aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorMarcelo <marcelo@xeon.cnet>2005-06-02 17:44:34 -0300
committerMarcelo Tosatti <marcelo.tosatti@cyclades.com>2005-06-02 17:44:34 -0300
commit284ce8b6e650e54a0348ebecb6c05d5acc899e56 (patch)
treea768647d90d95608d95fae564d387999e22aceb5 /fs
downloadlinux-2.4-284ce8b6e650e54a0348ebecb6c05d5acc899e56.tar.gz
initial v2.4 GIT import
Diffstat (limited to 'fs')
-rw-r--r--fs/ChangeLog159
-rw-r--r--fs/Config.in179
-rw-r--r--fs/Makefile84
-rw-r--r--fs/Makefile.lib2
-rw-r--r--fs/adfs/Makefile15
-rw-r--r--fs/adfs/adfs.h137
-rw-r--r--fs/adfs/dir.c295
-rw-r--r--fs/adfs/dir_f.c478
-rw-r--r--fs/adfs/dir_f.h65
-rw-r--r--fs/adfs/dir_fplus.c181
-rw-r--r--fs/adfs/dir_fplus.h45
-rw-r--r--fs/adfs/file.c41
-rw-r--r--fs/adfs/inode.c378
-rw-r--r--fs/adfs/map.c256
-rw-r--r--fs/adfs/super.c458
-rw-r--r--fs/affs/Changes338
-rw-r--r--fs/affs/Makefile17
-rw-r--r--fs/affs/amigaffs.c514
-rw-r--r--fs/affs/bitmap.c391
-rw-r--r--fs/affs/dir.c163
-rw-r--r--fs/affs/file.c932
-rw-r--r--fs/affs/inode.c417
-rw-r--r--fs/affs/namei.c470
-rw-r--r--fs/affs/super.c509
-rw-r--r--fs/affs/symlink.c87
-rw-r--r--fs/attr.c151
-rw-r--r--fs/autofs/Makefile12
-rw-r--r--fs/autofs/autofs_i.h159
-rw-r--r--fs/autofs/dir.c29
-rw-r--r--fs/autofs/dirhash.c249
-rw-r--r--fs/autofs/init.c41
-rw-r--r--fs/autofs/inode.c247
-rw-r--r--fs/autofs/root.c509
-rw-r--r--fs/autofs/symlink.c30
-rw-r--r--fs/autofs/waitq.c213
-rw-r--r--fs/autofs4/Makefile13
-rw-r--r--fs/autofs4/autofs_i.h160
-rw-r--r--fs/autofs4/expire.c269
-rw-r--r--fs/autofs4/init.c31
-rw-r--r--fs/autofs4/inode.c324
-rw-r--r--fs/autofs4/root.c532
-rw-r--r--fs/autofs4/symlink.c32
-rw-r--r--fs/autofs4/waitq.c250
-rw-r--r--fs/bad_inode.c108
-rw-r--r--fs/befs/ChangeLog417
-rw-r--r--fs/befs/Makefile15
-rw-r--r--fs/befs/TODO11
-rw-r--r--fs/befs/attribute.c116
-rw-r--r--fs/befs/befs.h152
-rw-r--r--fs/befs/befs_fs_types.h213
-rw-r--r--fs/befs/btree.c785
-rw-r--r--fs/befs/btree.h11
-rw-r--r--fs/befs/datastream.c528
-rw-r--r--fs/befs/datastream.h18
-rw-r--r--fs/befs/debug.c263
-rw-r--r--fs/befs/endian.h125
-rw-r--r--fs/befs/inode.c53
-rw-r--r--fs/befs/inode.h7
-rw-r--r--fs/befs/io.c99
-rw-r--r--fs/befs/io.h8
-rw-r--r--fs/befs/linuxvfs.c965
-rw-r--r--fs/befs/super.c112
-rw-r--r--fs/befs/super.h7
-rw-r--r--fs/bfs/Makefile15
-rw-r--r--fs/bfs/bfs_defs.h20
-rw-r--r--fs/bfs/dir.c339
-rw-r--r--fs/bfs/file.c168
-rw-r--r--fs/bfs/inode.c341
-rw-r--r--fs/binfmt_aout.c542
-rw-r--r--fs/binfmt_elf.c1408
-rw-r--r--fs/binfmt_em86.c113
-rw-r--r--fs/binfmt_misc.c717
-rw-r--r--fs/binfmt_script.c111
-rw-r--r--fs/binfmt_som.c329
-rw-r--r--fs/block_dev.c695
-rw-r--r--fs/buffer.c3146
-rw-r--r--fs/char_dev.c113
-rw-r--r--fs/coda/Makefile16
-rw-r--r--fs/coda/cache.c128
-rw-r--r--fs/coda/cnode.c206
-rw-r--r--fs/coda/coda_linux.c291
-rw-r--r--fs/coda/dir.c757
-rw-r--r--fs/coda/file.c292
-rw-r--r--fs/coda/inode.c271
-rw-r--r--fs/coda/pioctl.c103
-rw-r--r--fs/coda/psdev.c446
-rw-r--r--fs/coda/symlink.c55
-rw-r--r--fs/coda/sysctl.c523
-rw-r--r--fs/coda/upcall.c987
-rw-r--r--fs/cramfs/Makefile13
-rw-r--r--fs/cramfs/README168
-rw-r--r--fs/cramfs/inode.c465
-rw-r--r--fs/cramfs/uncompress.c77
-rw-r--r--fs/dcache.c1306
-rw-r--r--fs/devfs/Makefile42
-rw-r--r--fs/devfs/base.c3558
-rw-r--r--fs/devfs/util.c426
-rw-r--r--fs/devices.c216
-rw-r--r--fs/devpts/Makefile11
-rw-r--r--fs/devpts/devpts_i.h41
-rw-r--r--fs/devpts/inode.c256
-rw-r--r--fs/devpts/root.c135
-rw-r--r--fs/dnotify.c150
-rw-r--r--fs/dquot.c1536
-rw-r--r--fs/efs/Makefile15
-rw-r--r--fs/efs/dir.c107
-rw-r--r--fs/efs/file.c62
-rw-r--r--fs/efs/inode.c307
-rw-r--r--fs/efs/namei.c76
-rw-r--r--fs/efs/super.c227
-rw-r--r--fs/efs/symlink.c57
-rw-r--r--fs/exec.c1178
-rw-r--r--fs/ext2/CHANGES157
-rw-r--r--fs/ext2/Makefile16
-rw-r--r--fs/ext2/balloc.c785
-rw-r--r--fs/ext2/bitmap.c27
-rw-r--r--fs/ext2/dir.c600
-rw-r--r--fs/ext2/file.c54
-rw-r--r--fs/ext2/fsync.c55
-rw-r--r--fs/ext2/ialloc.c508
-rw-r--r--fs/ext2/inode.c1180
-rw-r--r--fs/ext2/ioctl.c76
-rw-r--r--fs/ext2/namei.c348
-rw-r--r--fs/ext2/super.c854
-rw-r--r--fs/ext2/symlink.c38
-rw-r--r--fs/ext3/Makefile16
-rw-r--r--fs/ext3/balloc.c1009
-rw-r--r--fs/ext3/bitmap.c26
-rw-r--r--fs/ext3/dir.c190
-rw-r--r--fs/ext3/file.c128
-rw-r--r--fs/ext3/fsync.c75
-rw-r--r--fs/ext3/ialloc.c663
-rw-r--r--fs/ext3/inode.c2737
-rw-r--r--fs/ext3/ioctl.c155
-rw-r--r--fs/ext3/namei.c1124
-rw-r--r--fs/ext3/super.c1854
-rw-r--r--fs/ext3/symlink.c39
-rw-r--r--fs/fat/Makefile17
-rw-r--r--fs/fat/buffer.c102
-rw-r--r--fs/fat/cache.c362
-rw-r--r--fs/fat/cvf.c177
-rw-r--r--fs/fat/dir.c798
-rw-r--r--fs/fat/fatfs_syms.c48
-rw-r--r--fs/fat/file.c136
-rw-r--r--fs/fat/inode.c1074
-rw-r--r--fs/fat/misc.c566
-rw-r--r--fs/fcntl.c541
-rw-r--r--fs/fifo.c157
-rw-r--r--fs/file.c234
-rw-r--r--fs/file_table.c205
-rw-r--r--fs/filesystems.c40
-rw-r--r--fs/freevxfs/Makefile11
-rw-r--r--fs/freevxfs/vxfs.h266
-rw-r--r--fs/freevxfs/vxfs_bmap.c277
-rw-r--r--fs/freevxfs/vxfs_dir.h94
-rw-r--r--fs/freevxfs/vxfs_extern.h78
-rw-r--r--fs/freevxfs/vxfs_fshead.c171
-rw-r--r--fs/freevxfs/vxfs_fshead.h69
-rw-r--r--fs/freevxfs/vxfs_immed.c138
-rw-r--r--fs/freevxfs/vxfs_inode.c349
-rw-r--r--fs/freevxfs/vxfs_inode.h182
-rw-r--r--fs/freevxfs/vxfs_kcompat.h21
-rw-r--r--fs/freevxfs/vxfs_lookup.c322
-rw-r--r--fs/freevxfs/vxfs_olt.c133
-rw-r--r--fs/freevxfs/vxfs_olt.h147
-rw-r--r--fs/freevxfs/vxfs_subr.c191
-rw-r--r--fs/freevxfs/vxfs_super.c251
-rw-r--r--fs/hfs/COPYING339
-rw-r--r--fs/hfs/ChangeLog2506
-rw-r--r--fs/hfs/FAQ.txt342
-rw-r--r--fs/hfs/HFS.txt1042
-rw-r--r--fs/hfs/INSTALL.txt126
-rw-r--r--fs/hfs/Makefile19
-rw-r--r--fs/hfs/TODO52
-rw-r--r--fs/hfs/balloc.c439
-rw-r--r--fs/hfs/bdelete.c488
-rw-r--r--fs/hfs/bfind.c322
-rw-r--r--fs/hfs/bins_del.c231
-rw-r--r--fs/hfs/binsert.c551
-rw-r--r--fs/hfs/bitmap.c412
-rw-r--r--fs/hfs/bitops.c124
-rw-r--r--fs/hfs/bnode.c544
-rw-r--r--fs/hfs/brec.c239
-rw-r--r--fs/hfs/btree.c324
-rw-r--r--fs/hfs/catalog.c1592
-rw-r--r--fs/hfs/dir.c394
-rw-r--r--fs/hfs/dir_cap.c327
-rw-r--r--fs/hfs/dir_dbl.c404
-rw-r--r--fs/hfs/dir_nat.c452
-rw-r--r--fs/hfs/extent.c809
-rw-r--r--fs/hfs/file.c534
-rw-r--r--fs/hfs/file_cap.c279
-rw-r--r--fs/hfs/file_hdr.c1031
-rw-r--r--fs/hfs/hfs.h550
-rw-r--r--fs/hfs/hfs_btree.h290
-rw-r--r--fs/hfs/inode.c503
-rw-r--r--fs/hfs/mdb.c320
-rw-r--r--fs/hfs/part_tbl.c244
-rw-r--r--fs/hfs/string.c144
-rw-r--r--fs/hfs/super.c529
-rw-r--r--fs/hfs/sysdep.c109
-rw-r--r--fs/hfs/trans.c556
-rw-r--r--fs/hfs/version.c10
-rw-r--r--fs/hfsplus/Makefile17
-rw-r--r--fs/hfsplus/README33
-rw-r--r--fs/hfsplus/bfind.c213
-rw-r--r--fs/hfsplus/bnode.c1031
-rw-r--r--fs/hfsplus/brec.c50
-rw-r--r--fs/hfsplus/btree.c317
-rw-r--r--fs/hfsplus/catalog.c333
-rw-r--r--fs/hfsplus/dir.c492
-rw-r--r--fs/hfsplus/extents.c512
-rw-r--r--fs/hfsplus/hfsplus_fs.h368
-rw-r--r--fs/hfsplus/hfsplus_raw.h321
-rw-r--r--fs/hfsplus/inode.c479
-rw-r--r--fs/hfsplus/options.c140
-rw-r--r--fs/hfsplus/super.c523
-rw-r--r--fs/hfsplus/tables.c408
-rw-r--r--fs/hfsplus/unicode.c126
-rw-r--r--fs/hfsplus/wrapper.c149
-rw-r--r--fs/hpfs/Makefile6
-rw-r--r--fs/hpfs/alloc.c452
-rw-r--r--fs/hpfs/anode.c490
-rw-r--r--fs/hpfs/buffer.c268
-rw-r--r--fs/hpfs/dentry.c60
-rw-r--r--fs/hpfs/dir.c281
-rw-r--r--fs/hpfs/dnode.c1070
-rw-r--r--fs/hpfs/ea.c364
-rw-r--r--fs/hpfs/file.c136
-rw-r--r--fs/hpfs/hpfs.h493
-rw-r--r--fs/hpfs/hpfs_fn.h315
-rw-r--r--fs/hpfs/inode.c328
-rw-r--r--fs/hpfs/map.c275
-rw-r--r--fs/hpfs/name.c144
-rw-r--r--fs/hpfs/namei.c534
-rw-r--r--fs/hpfs/super.c588
-rw-r--r--fs/inode.c1444
-rw-r--r--fs/intermezzo/Makefile15
-rw-r--r--fs/intermezzo/cache.c215
-rw-r--r--fs/intermezzo/dcache.c350
-rw-r--r--fs/intermezzo/dir.c1412
-rw-r--r--fs/intermezzo/ext_attr.c205
-rw-r--r--fs/intermezzo/file.c539
-rw-r--r--fs/intermezzo/fileset.c677
-rw-r--r--fs/intermezzo/inode.c187
-rw-r--r--fs/intermezzo/journal.c2453
-rw-r--r--fs/intermezzo/journal_ext2.c91
-rw-r--r--fs/intermezzo/journal_ext3.c285
-rw-r--r--fs/intermezzo/journal_obdfs.c194
-rw-r--r--fs/intermezzo/journal_reiserfs.c142
-rw-r--r--fs/intermezzo/journal_tmpfs.c109
-rw-r--r--fs/intermezzo/journal_xfs.c162
-rw-r--r--fs/intermezzo/kml_reint.c630
-rw-r--r--fs/intermezzo/kml_unpack.c708
-rw-r--r--fs/intermezzo/methods.c497
-rw-r--r--fs/intermezzo/presto.c740
-rw-r--r--fs/intermezzo/psdev.c649
-rw-r--r--fs/intermezzo/replicator.c288
-rw-r--r--fs/intermezzo/super.c406
-rw-r--r--fs/intermezzo/sysctl.c372
-rw-r--r--fs/intermezzo/upcall.c557
-rw-r--r--fs/intermezzo/vfs.c2461
-rw-r--r--fs/iobuf.c186
-rw-r--r--fs/ioctl.c126
-rw-r--r--fs/isofs/Makefile20
-rw-r--r--fs/isofs/compress.c360
-rw-r--r--fs/isofs/dir.c266
-rw-r--r--fs/isofs/inode.c1334
-rw-r--r--fs/isofs/joliet.c103
-rw-r--r--fs/isofs/namei.c182
-rw-r--r--fs/isofs/rock.c623
-rw-r--r--fs/isofs/rock.h119
-rw-r--r--fs/isofs/util.c82
-rw-r--r--fs/isofs/zisofs.h21
-rw-r--r--fs/jbd/Makefile15
-rw-r--r--fs/jbd/checkpoint.c606
-rw-r--r--fs/jbd/commit.c811
-rw-r--r--fs/jbd/journal.c2000
-rw-r--r--fs/jbd/recovery.c594
-rw-r--r--fs/jbd/revoke.c635
-rw-r--r--fs/jbd/transaction.c2182
-rw-r--r--fs/jffs/Makefile32
-rw-r--r--fs/jffs/inode-v23.c1770
-rw-r--r--fs/jffs/intrep.c3439
-rw-r--r--fs/jffs/intrep.h91
-rw-r--r--fs/jffs/jffs_fm.c791
-rw-r--r--fs/jffs/jffs_fm.h150
-rw-r--r--fs/jffs/jffs_proc.c269
-rw-r--r--fs/jffs/jffs_proc.h28
-rw-r--r--fs/jffs2/Makefile25
-rw-r--r--fs/jffs2/TODO20
-rw-r--r--fs/jffs2/background.c183
-rw-r--r--fs/jffs2/build.c287
-rw-r--r--fs/jffs2/compr.c151
-rw-r--r--fs/jffs2/compr_rtime.c128
-rw-r--r--fs/jffs2/compr_rubin.c349
-rw-r--r--fs/jffs2/compr_rubin.h28
-rw-r--r--fs/jffs2/compr_zlib.c177
-rw-r--r--fs/jffs2/comprtest.c307
-rw-r--r--fs/jffs2/dir.c1010
-rw-r--r--fs/jffs2/erase.c373
-rw-r--r--fs/jffs2/file.c557
-rw-r--r--fs/jffs2/gc.c705
-rw-r--r--fs/jffs2/histo.h3
-rw-r--r--fs/jffs2/histo_mips.h2
-rw-r--r--fs/jffs2/ioctl.c47
-rw-r--r--fs/jffs2/malloc.c220
-rw-r--r--fs/jffs2/nodelist.c354
-rw-r--r--fs/jffs2/nodelist.h354
-rw-r--r--fs/jffs2/nodemgmt.c392
-rw-r--r--fs/jffs2/pushpull.c71
-rw-r--r--fs/jffs2/pushpull.h66
-rw-r--r--fs/jffs2/read.c163
-rw-r--r--fs/jffs2/readinode.c508
-rw-r--r--fs/jffs2/scan.c756
-rw-r--r--fs/jffs2/super.c402
-rw-r--r--fs/jffs2/symlink.c110
-rw-r--r--fs/jffs2/write.c334
-rw-r--r--fs/jfs/Makefile15
-rw-r--r--fs/jfs/file.c115
-rw-r--r--fs/jfs/inode.c401
-rw-r--r--fs/jfs/jfs_btree.h172
-rw-r--r--fs/jfs/jfs_debug.c154
-rw-r--r--fs/jfs/jfs_debug.h122
-rw-r--r--fs/jfs/jfs_dinode.h151
-rw-r--r--fs/jfs/jfs_dmap.c4273
-rw-r--r--fs/jfs/jfs_dmap.h297
-rw-r--r--fs/jfs/jfs_dtree.c4739
-rw-r--r--fs/jfs/jfs_dtree.h279
-rw-r--r--fs/jfs/jfs_extent.c657
-rw-r--r--fs/jfs/jfs_extent.h31
-rw-r--r--fs/jfs/jfs_filsys.h280
-rw-r--r--fs/jfs/jfs_imap.c3279
-rw-r--r--fs/jfs/jfs_imap.h157
-rw-r--r--fs/jfs/jfs_incore.h186
-rw-r--r--fs/jfs/jfs_inode.c121
-rw-r--r--fs/jfs/jfs_inode.h23
-rw-r--r--fs/jfs/jfs_lock.h51
-rw-r--r--fs/jfs/jfs_logmgr.c2392
-rw-r--r--fs/jfs/jfs_logmgr.h517
-rw-r--r--fs/jfs/jfs_metapage.c637
-rw-r--r--fs/jfs/jfs_metapage.h115
-rw-r--r--fs/jfs/jfs_mount.c512
-rw-r--r--fs/jfs/jfs_superblock.h113
-rw-r--r--fs/jfs/jfs_txnmgr.c3080
-rw-r--r--fs/jfs/jfs_txnmgr.h314
-rw-r--r--fs/jfs/jfs_types.h190
-rw-r--r--fs/jfs/jfs_umount.c162
-rw-r--r--fs/jfs/jfs_unicode.c138
-rw-r--r--fs/jfs/jfs_unicode.h139
-rw-r--r--fs/jfs/jfs_uniupr.c134
-rw-r--r--fs/jfs/jfs_xattr.h62
-rw-r--r--fs/jfs/jfs_xtree.c4458
-rw-r--r--fs/jfs/jfs_xtree.h140
-rw-r--r--fs/jfs/namei.c1424
-rw-r--r--fs/jfs/resize.c539
-rw-r--r--fs/jfs/super.c597
-rw-r--r--fs/jfs/symlink.c43
-rw-r--r--fs/jfs/xattr.c947
-rw-r--r--fs/lockd/Makefile21
-rw-r--r--fs/lockd/clntlock.c254
-rw-r--r--fs/lockd/clntproc.c695
-rw-r--r--fs/lockd/host.c343
-rw-r--r--fs/lockd/lockd_syms.c38
-rw-r--r--fs/lockd/mon.c251
-rw-r--r--fs/lockd/svc.c398
-rw-r--r--fs/lockd/svc4proc.c588
-rw-r--r--fs/lockd/svclock.c685
-rw-r--r--fs/lockd/svcproc.c616
-rw-r--r--fs/lockd/svcshare.c111
-rw-r--r--fs/lockd/svcsubs.c311
-rw-r--r--fs/lockd/xdr.c674
-rw-r--r--fs/lockd/xdr4.c612
-rw-r--r--fs/locks.c2042
-rw-r--r--fs/minix/Makefile15
-rw-r--r--fs/minix/bitmap.c276
-rw-r--r--fs/minix/dir.c378
-rw-r--r--fs/minix/file.c44
-rw-r--r--fs/minix/inode.c539
-rw-r--r--fs/minix/itree_common.c347
-rw-r--r--fs/minix/itree_v1.c58
-rw-r--r--fs/minix/itree_v2.c63
-rw-r--r--fs/minix/namei.c315
-rw-r--r--fs/msdos/Makefile17
-rw-r--r--fs/msdos/msdosfs_syms.c43
-rw-r--r--fs/msdos/namei.c600
-rw-r--r--fs/namei.c2053
-rw-r--r--fs/namespace.c1073
-rw-r--r--fs/ncpfs/Config.in11
-rw-r--r--fs/ncpfs/Makefile21
-rw-r--r--fs/ncpfs/dir.c1152
-rw-r--r--fs/ncpfs/file.c296
-rw-r--r--fs/ncpfs/inode.c733
-rw-r--r--fs/ncpfs/ioctl.c644
-rw-r--r--fs/ncpfs/mmap.c123
-rw-r--r--fs/ncpfs/ncplib_kernel.c1129
-rw-r--r--fs/ncpfs/ncplib_kernel.h221
-rw-r--r--fs/ncpfs/ncpsign_kernel.c112
-rw-r--r--fs/ncpfs/ncpsign_kernel.h15
-rw-r--r--fs/ncpfs/sock.c610
-rw-r--r--fs/ncpfs/symlink.c163
-rw-r--r--fs/nfs/Makefile21
-rw-r--r--fs/nfs/dir.c1126
-rw-r--r--fs/nfs/direct.c382
-rw-r--r--fs/nfs/file.c373
-rw-r--r--fs/nfs/flushd.c205
-rw-r--r--fs/nfs/inode.c1179
-rw-r--r--fs/nfs/mount_clnt.c185
-rw-r--r--fs/nfs/nfs2xdr.c729
-rw-r--r--fs/nfs/nfs3proc.c532
-rw-r--r--fs/nfs/nfs3xdr.c1037
-rw-r--r--fs/nfs/nfsroot.c440
-rw-r--r--fs/nfs/pagelist.c500
-rw-r--r--fs/nfs/proc.c391
-rw-r--r--fs/nfs/read.c508
-rw-r--r--fs/nfs/symlink.c108
-rw-r--r--fs/nfs/unlink.c218
-rw-r--r--fs/nfs/write.c1323
-rw-r--r--fs/nfsd/Makefile20
-rw-r--r--fs/nfsd/auth.c61
-rw-r--r--fs/nfsd/export.c966
-rw-r--r--fs/nfsd/lockd.c93
-rw-r--r--fs/nfsd/nfs3proc.c692
-rw-r--r--fs/nfsd/nfs3xdr.c851
-rw-r--r--fs/nfsd/nfscache.c353
-rw-r--r--fs/nfsd/nfsctl.c349
-rw-r--r--fs/nfsd/nfsfh.c925
-rw-r--r--fs/nfsd/nfsproc.c609
-rw-r--r--fs/nfsd/nfssvc.c342
-rw-r--r--fs/nfsd/nfsxdr.c451
-rw-r--r--fs/nfsd/stats.c117
-rw-r--r--fs/nfsd/vfs.c1615
-rw-r--r--fs/nls/Config.in64
-rw-r--r--fs/nls/Makefile52
-rw-r--r--fs/nls/nls_base.c501
-rw-r--r--fs/nls/nls_big5.c63
-rw-r--r--fs/nls/nls_cp1250.c365
-rw-r--r--fs/nls/nls_cp1251.c318
-rw-r--r--fs/nls/nls_cp1255.c399
-rw-r--r--fs/nls/nls_cp437.c404
-rw-r--r--fs/nls/nls_cp737.c367
-rw-r--r--fs/nls/nls_cp775.c336
-rw-r--r--fs/nls/nls_cp850.c332
-rw-r--r--fs/nls/nls_cp852.c354
-rw-r--r--fs/nls/nls_cp855.c316
-rw-r--r--fs/nls/nls_cp857.c318
-rw-r--r--fs/nls/nls_cp860.c381
-rw-r--r--fs/nls/nls_cp861.c404
-rw-r--r--fs/nls/nls_cp862.c438
-rw-r--r--fs/nls/nls_cp863.c398
-rw-r--r--fs/nls/nls_cp864.c424
-rw-r--r--fs/nls/nls_cp865.c404
-rw-r--r--fs/nls/nls_cp866.c322
-rw-r--r--fs/nls/nls_cp869.c332
-rw-r--r--fs/nls/nls_cp874.c290
-rw-r--r--fs/nls/nls_cp932.c7925
-rw-r--r--fs/nls/nls_cp936.c11045
-rw-r--r--fs/nls/nls_cp949.c13962
-rw-r--r--fs/nls/nls_cp950.c9501
-rw-r--r--fs/nls/nls_euc-jp.c602
-rw-r--r--fs/nls/nls_euc-kr.c63
-rw-r--r--fs/nls/nls_gb2312.c63
-rw-r--r--fs/nls/nls_iso8859-1.c274
-rw-r--r--fs/nls/nls_iso8859-13.c302
-rw-r--r--fs/nls/nls_iso8859-14.c358
-rw-r--r--fs/nls/nls_iso8859-15.c324
-rw-r--r--fs/nls/nls_iso8859-2.c325
-rw-r--r--fs/nls/nls_iso8859-3.c325
-rw-r--r--fs/nls/nls_iso8859-4.c325
-rw-r--r--fs/nls/nls_iso8859-5.c289
-rw-r--r--fs/nls/nls_iso8859-6.c280
-rw-r--r--fs/nls/nls_iso8859-7.c334
-rw-r--r--fs/nls/nls_iso8859-8.c63
-rw-r--r--fs/nls/nls_iso8859-9.c289
-rw-r--r--fs/nls/nls_koi8-r.c340
-rw-r--r--fs/nls/nls_koi8-ru.c101
-rw-r--r--fs/nls/nls_koi8-u.c347
-rw-r--r--fs/nls/nls_sjis.c63
-rw-r--r--fs/nls/nls_tis-620.c63
-rw-r--r--fs/nls/nls_utf8.c61
-rw-r--r--fs/ntfs/Makefile11
-rw-r--r--fs/ntfs/attr.c872
-rw-r--r--fs/ntfs/attr.h38
-rw-r--r--fs/ntfs/dir.c1103
-rw-r--r--fs/ntfs/dir.h48
-rw-r--r--fs/ntfs/fs.c1167
-rw-r--r--fs/ntfs/inode.c2322
-rw-r--r--fs/ntfs/inode.h58
-rw-r--r--fs/ntfs/macros.h43
-rw-r--r--fs/ntfs/ntfsendian.h60
-rw-r--r--fs/ntfs/ntfstypes.h84
-rw-r--r--fs/ntfs/struct.h69
-rw-r--r--fs/ntfs/super.c1416
-rw-r--r--fs/ntfs/super.h32
-rw-r--r--fs/ntfs/support.c316
-rw-r--r--fs/ntfs/support.h89
-rw-r--r--fs/ntfs/sysctl.c55
-rw-r--r--fs/ntfs/sysctl.h17
-rw-r--r--fs/ntfs/unistr.c167
-rw-r--r--fs/ntfs/unistr.h44
-rw-r--r--fs/ntfs/util.c265
-rw-r--r--fs/ntfs/util.h56
-rw-r--r--fs/open.c918
-rw-r--r--fs/openpromfs/Makefile15
-rw-r--r--fs/openpromfs/inode.c1063
-rw-r--r--fs/partitions/Config.in71
-rw-r--r--fs/partitions/Makefile29
-rw-r--r--fs/partitions/acorn.c478
-rw-r--r--fs/partitions/acorn.h55
-rw-r--r--fs/partitions/amiga.c112
-rw-r--r--fs/partitions/amiga.h8
-rw-r--r--fs/partitions/atari.c162
-rw-r--r--fs/partitions/atari.h36
-rw-r--r--fs/partitions/check.c439
-rw-r--r--fs/partitions/check.h16
-rw-r--r--fs/partitions/efi.c738
-rw-r--r--fs/partitions/efi.h115
-rw-r--r--fs/partitions/ibm.c238
-rw-r--r--fs/partitions/ibm.h1
-rw-r--r--fs/partitions/ldm.c1545
-rw-r--r--fs/partitions/ldm.h221
-rw-r--r--fs/partitions/mac.c77
-rw-r--r--fs/partitions/mac.h52
-rw-r--r--fs/partitions/msdos.c641
-rw-r--r--fs/partitions/msdos.h9
-rw-r--r--fs/partitions/osf.c87
-rw-r--r--fs/partitions/osf.h9
-rw-r--r--fs/partitions/sgi.c85
-rw-r--r--fs/partitions/sgi.h9
-rw-r--r--fs/partitions/sun.c107
-rw-r--r--fs/partitions/sun.h9
-rw-r--r--fs/partitions/ultrix.c54
-rw-r--r--fs/partitions/ultrix.h7
-rw-r--r--fs/pipe.c650
-rw-r--r--fs/proc/Makefile21
-rw-r--r--fs/proc/array.c629
-rw-r--r--fs/proc/base.c1169
-rw-r--r--fs/proc/generic.c598
-rw-r--r--fs/proc/inode-alloc.txt13
-rw-r--r--fs/proc/inode.c211
-rw-r--r--fs/proc/kcore.c478
-rw-r--r--fs/proc/kmsg.c52
-rw-r--r--fs/proc/proc_devtree.c142
-rw-r--r--fs/proc/proc_misc.c673
-rw-r--r--fs/proc/proc_tty.c188
-rw-r--r--fs/proc/root.c150
-rw-r--r--fs/qnx4/Makefile15
-rw-r--r--fs/qnx4/README9
-rw-r--r--fs/qnx4/bitmap.c164
-rw-r--r--fs/qnx4/dir.c96
-rw-r--r--fs/qnx4/file.c43
-rw-r--r--fs/qnx4/fsync.c171
-rw-r--r--fs/qnx4/inode.c512
-rw-r--r--fs/qnx4/namei.c237
-rw-r--r--fs/qnx4/truncate.c38
-rw-r--r--fs/quota.c489
-rw-r--r--fs/quota_v1.c240
-rw-r--r--fs/quota_v2.c690
-rw-r--r--fs/ramfs/Makefile10
-rw-r--r--fs/ramfs/inode.c349
-rw-r--r--fs/read_write.c452
-rw-r--r--fs/readdir.c389
-rw-r--r--fs/reiserfs/Makefile30
-rw-r--r--fs/reiserfs/README161
-rw-r--r--fs/reiserfs/bitmap.c906
-rw-r--r--fs/reiserfs/buffer2.c76
-rw-r--r--fs/reiserfs/dir.c254
-rw-r--r--fs/reiserfs/do_balan.c1609
-rw-r--r--fs/reiserfs/file.c147
-rw-r--r--fs/reiserfs/fix_node.c2535
-rw-r--r--fs/reiserfs/hashes.c223
-rw-r--r--fs/reiserfs/ibalance.c1060
-rw-r--r--fs/reiserfs/inode.c2240
-rw-r--r--fs/reiserfs/ioctl.c152
-rw-r--r--fs/reiserfs/item_ops.c789
-rw-r--r--fs/reiserfs/journal.c3274
-rw-r--r--fs/reiserfs/lbalance.c1221
-rw-r--r--fs/reiserfs/namei.c1281
-rw-r--r--fs/reiserfs/objectid.c208
-rw-r--r--fs/reiserfs/prints.c733
-rw-r--r--fs/reiserfs/procfs.c743
-rw-r--r--fs/reiserfs/resize.c170
-rw-r--r--fs/reiserfs/stree.c1921
-rw-r--r--fs/reiserfs/super.c1390
-rw-r--r--fs/reiserfs/tail_conversion.c299
-rw-r--r--fs/reiserfs/version.c7
-rw-r--r--fs/romfs/Makefile15
-rw-r--r--fs/romfs/inode.c551
-rw-r--r--fs/select.c496
-rw-r--r--fs/seq_file.c381
-rw-r--r--fs/smbfs/ChangeLog150
-rw-r--r--fs/smbfs/Makefile41
-rw-r--r--fs/smbfs/cache.c208
-rw-r--r--fs/smbfs/dir.c661
-rw-r--r--fs/smbfs/file.c402
-rw-r--r--fs/smbfs/getopt.c63
-rw-r--r--fs/smbfs/getopt.h14
-rw-r--r--fs/smbfs/inode.c722
-rw-r--r--fs/smbfs/ioctl.c57
-rw-r--r--fs/smbfs/proc.c3366
-rw-r--r--fs/smbfs/proto.h71
-rw-r--r--fs/smbfs/smb_debug.h34
-rw-r--r--fs/smbfs/sock.c956
-rw-r--r--fs/smbfs/symlink.c90
-rw-r--r--fs/stat.c385
-rw-r--r--fs/super.c869
-rw-r--r--fs/sysv/CHANGES60
-rw-r--r--fs/sysv/ChangeLog29
-rw-r--r--fs/sysv/INTRO182
-rw-r--r--fs/sysv/Makefile16
-rw-r--r--fs/sysv/balloc.c235
-rw-r--r--fs/sysv/dir.c386
-rw-r--r--fs/sysv/file.c49
-rw-r--r--fs/sysv/ialloc.c232
-rw-r--r--fs/sysv/inode.c271
-rw-r--r--fs/sysv/itree.c439
-rw-r--r--fs/sysv/namei.c323
-rw-r--r--fs/sysv/super.c508
-rw-r--r--fs/sysv/symlink.c25
-rw-r--r--fs/udf/Makefile17
-rw-r--r--fs/udf/balloc.c948
-rw-r--r--fs/udf/crc.c178
-rw-r--r--fs/udf/dir.c257
-rw-r--r--fs/udf/directory.c327
-rw-r--r--fs/udf/ecma_167.h822
-rw-r--r--fs/udf/file.c375
-rw-r--r--fs/udf/fsync.c56
-rw-r--r--fs/udf/ialloc.c167
-rw-r--r--fs/udf/inode.c2217
-rw-r--r--fs/udf/lowlevel.c91
-rw-r--r--fs/udf/misc.c362
-rw-r--r--fs/udf/namei.c1300
-rw-r--r--fs/udf/osta_udf.h271
-rw-r--r--fs/udf/partition.c225
-rw-r--r--fs/udf/super.c1816
-rw-r--r--fs/udf/symlink.c129
-rw-r--r--fs/udf/truncate.c222
-rw-r--r--fs/udf/udf_i.h22
-rw-r--r--fs/udf/udf_sb.h119
-rw-r--r--fs/udf/udfdecl.h206
-rw-r--r--fs/udf/udfend.h81
-rw-r--r--fs/udf/udftime.c174
-rw-r--r--fs/udf/unicode.c534
-rw-r--r--fs/ufs/Makefile16
-rw-r--r--fs/ufs/balloc.c815
-rw-r--r--fs/ufs/cylinder.c207
-rw-r--r--fs/ufs/dir.c622
-rw-r--r--fs/ufs/file.c54
-rw-r--r--fs/ufs/ialloc.c290
-rw-r--r--fs/ufs/inode.c673
-rw-r--r--fs/ufs/namei.c337
-rw-r--r--fs/ufs/super.c1009
-rw-r--r--fs/ufs/swab.h133
-rw-r--r--fs/ufs/symlink.c45
-rw-r--r--fs/ufs/truncate.c466
-rw-r--r--fs/ufs/util.c196
-rw-r--r--fs/ufs/util.h523
-rw-r--r--fs/umsdos/Makefile24
-rw-r--r--fs/umsdos/README-WIP.txt114
-rw-r--r--fs/umsdos/dir.c809
-rw-r--r--fs/umsdos/emd.c660
-rw-r--r--fs/umsdos/inode.c478
-rw-r--r--fs/umsdos/ioctl.c439
-rw-r--r--fs/umsdos/mangle.c522
-rw-r--r--fs/umsdos/namei.c1130
-rw-r--r--fs/umsdos/notes17
-rw-r--r--fs/umsdos/rdir.c243
-rw-r--r--fs/umsdos/specs289
-rw-r--r--fs/vfat/Makefile17
-rw-r--r--fs/vfat/namei.c1288
-rw-r--r--fs/vfat/vfatfs_syms.c36
-rw-r--r--fs/xattr.c355
-rw-r--r--fs/xfs/Makefile134
-rw-r--r--fs/xfs/linux-2.4/Makefile77
-rw-r--r--fs/xfs/linux-2.4/kmem.c185
-rw-r--r--fs/xfs/linux-2.4/kmem.h152
-rw-r--r--fs/xfs/linux-2.4/mrlock.c274
-rw-r--r--fs/xfs/linux-2.4/mrlock.h87
-rw-r--r--fs/xfs/linux-2.4/mutex.h53
-rw-r--r--fs/xfs/linux-2.4/sema.h67
-rw-r--r--fs/xfs/linux-2.4/spin.h56
-rw-r--r--fs/xfs/linux-2.4/sv.h89
-rw-r--r--fs/xfs/linux-2.4/time.h55
-rw-r--r--fs/xfs/linux-2.4/xfs_aops.c1299
-rw-r--r--fs/xfs/linux-2.4/xfs_buf.c2368
-rw-r--r--fs/xfs/linux-2.4/xfs_buf.h634
-rw-r--r--fs/xfs/linux-2.4/xfs_cred.h50
-rw-r--r--fs/xfs/linux-2.4/xfs_file.c447
-rw-r--r--fs/xfs/linux-2.4/xfs_fs_subr.c126
-rw-r--r--fs/xfs/linux-2.4/xfs_fs_subr.h48
-rw-r--r--fs/xfs/linux-2.4/xfs_globals.c77
-rw-r--r--fs/xfs/linux-2.4/xfs_globals.h44
-rw-r--r--fs/xfs/linux-2.4/xfs_ioctl.c1254
-rw-r--r--fs/xfs/linux-2.4/xfs_iops.c690
-rw-r--r--fs/xfs/linux-2.4/xfs_iops.h51
-rw-r--r--fs/xfs/linux-2.4/xfs_linux.h388
-rw-r--r--fs/xfs/linux-2.4/xfs_lrw.c981
-rw-r--r--fs/xfs/linux-2.4/xfs_lrw.h111
-rw-r--r--fs/xfs/linux-2.4/xfs_stats.c117
-rw-r--r--fs/xfs/linux-2.4/xfs_stats.h160
-rw-r--r--fs/xfs/linux-2.4/xfs_super.c1066
-rw-r--r--fs/xfs/linux-2.4/xfs_super.h142
-rw-r--r--fs/xfs/linux-2.4/xfs_sysctl.c205
-rw-r--r--fs/xfs/linux-2.4/xfs_sysctl.h116
-rw-r--r--fs/xfs/linux-2.4/xfs_version.h44
-rw-r--r--fs/xfs/linux-2.4/xfs_vfs.c343
-rw-r--r--fs/xfs/linux-2.4/xfs_vfs.h236
-rw-r--r--fs/xfs/linux-2.4/xfs_vnode.c465
-rw-r--r--fs/xfs/linux-2.4/xfs_vnode.h668
-rw-r--r--fs/xfs/quota/Makefile57
-rw-r--r--fs/xfs/quota/xfs_dquot.c1650
-rw-r--r--fs/xfs/quota/xfs_dquot.h224
-rw-r--r--fs/xfs/quota/xfs_dquot_item.c715
-rw-r--r--fs/xfs/quota/xfs_dquot_item.h66
-rw-r--r--fs/xfs/quota/xfs_qm.c2848
-rw-r--r--fs/xfs/quota/xfs_qm.h236
-rw-r--r--fs/xfs/quota/xfs_qm_bhv.c410
-rw-r--r--fs/xfs/quota/xfs_qm_stats.c149
-rw-r--r--fs/xfs/quota/xfs_qm_stats.h68
-rw-r--r--fs/xfs/quota/xfs_qm_syscalls.c1460
-rw-r--r--fs/xfs/quota/xfs_quota_priv.h192
-rw-r--r--fs/xfs/quota/xfs_trans_dquot.c941
-rw-r--r--fs/xfs/support/Makefile51
-rw-r--r--fs/xfs/support/debug.c127
-rw-r--r--fs/xfs/support/debug.h72
-rw-r--r--fs/xfs/support/ktrace.c346
-rw-r--r--fs/xfs/support/ktrace.h101
-rw-r--r--fs/xfs/support/move.c66
-rw-r--r--fs/xfs/support/move.h84
-rw-r--r--fs/xfs/support/qsort.c155
-rw-r--r--fs/xfs/support/qsort.h41
-rw-r--r--fs/xfs/support/uuid.c151
-rw-r--r--fs/xfs/support/uuid.h48
-rw-r--r--fs/xfs/xfs.h40
-rw-r--r--fs/xfs/xfs_acl.c974
-rw-r--r--fs/xfs/xfs_acl.h116
-rw-r--r--fs/xfs/xfs_ag.h345
-rw-r--r--fs/xfs/xfs_alloc.c2623
-rw-r--r--fs/xfs/xfs_alloc.h203
-rw-r--r--fs/xfs/xfs_alloc_btree.c2204
-rw-r--r--fs/xfs/xfs_alloc_btree.h257
-rw-r--r--fs/xfs/xfs_arch.h276
-rw-r--r--fs/xfs/xfs_attr.c2660
-rw-r--r--fs/xfs/xfs_attr.h193
-rw-r--r--fs/xfs/xfs_attr_leaf.c3050
-rw-r--r--fs/xfs/xfs_attr_leaf.h308
-rw-r--r--fs/xfs/xfs_attr_sf.h149
-rw-r--r--fs/xfs/xfs_behavior.c218
-rw-r--r--fs/xfs/xfs_behavior.h204
-rw-r--r--fs/xfs/xfs_bit.c312
-rw-r--r--fs/xfs/xfs_bit.h85
-rw-r--r--fs/xfs/xfs_bmap.c6246
-rw-r--r--fs/xfs/xfs_bmap.h379
-rw-r--r--fs/xfs/xfs_bmap_btree.c2807
-rw-r--r--fs/xfs/xfs_bmap_btree.h701
-rw-r--r--fs/xfs/xfs_btree.c949
-rw-r--r--fs/xfs/xfs_btree.h592
-rw-r--r--fs/xfs/xfs_buf_item.c1221
-rw-r--r--fs/xfs/xfs_buf_item.h171
-rw-r--r--fs/xfs/xfs_cap.h84
-rw-r--r--fs/xfs/xfs_clnt.h106
-rw-r--r--fs/xfs/xfs_da_btree.c2650
-rw-r--r--fs/xfs/xfs_da_btree.h335
-rw-r--r--fs/xfs/xfs_dfrag.c387
-rw-r--r--fs/xfs/xfs_dfrag.h67
-rw-r--r--fs/xfs/xfs_dinode.h480
-rw-r--r--fs/xfs/xfs_dir.c1223
-rw-r--r--fs/xfs/xfs_dir.h154
-rw-r--r--fs/xfs/xfs_dir2.c859
-rw-r--r--fs/xfs/xfs_dir2.h109
-rw-r--r--fs/xfs/xfs_dir2_block.c1248
-rw-r--r--fs/xfs/xfs_dir2_block.h127
-rw-r--r--fs/xfs/xfs_dir2_data.c855
-rw-r--r--fs/xfs/xfs_dir2_data.h232
-rw-r--r--fs/xfs/xfs_dir2_leaf.c1896
-rw-r--r--fs/xfs/xfs_dir2_leaf.h360
-rw-r--r--fs/xfs/xfs_dir2_node.c2020
-rw-r--r--fs/xfs/xfs_dir2_node.h159
-rw-r--r--fs/xfs/xfs_dir2_sf.c1319
-rw-r--r--fs/xfs/xfs_dir2_sf.h255
-rw-r--r--fs/xfs/xfs_dir2_trace.c235
-rw-r--r--fs/xfs/xfs_dir2_trace.h86
-rw-r--r--fs/xfs/xfs_dir_leaf.c2231
-rw-r--r--fs/xfs/xfs_dir_leaf.h253
-rw-r--r--fs/xfs/xfs_dir_sf.h178
-rw-r--r--fs/xfs/xfs_dmapi.h212
-rw-r--r--fs/xfs/xfs_dmops.c52
-rw-r--r--fs/xfs/xfs_error.c327
-rw-r--r--fs/xfs/xfs_error.h196
-rw-r--r--fs/xfs/xfs_extfree_item.c668
-rw-r--r--fs/xfs/xfs_extfree_item.h123
-rw-r--r--fs/xfs/xfs_fs.h527
-rw-r--r--fs/xfs/xfs_fsops.c616
-rw-r--r--fs/xfs/xfs_fsops.h67
-rw-r--r--fs/xfs/xfs_ialloc.c1402
-rw-r--r--fs/xfs/xfs_ialloc.h184
-rw-r--r--fs/xfs/xfs_ialloc_btree.c2102
-rw-r--r--fs/xfs/xfs_ialloc_btree.h316
-rw-r--r--fs/xfs/xfs_iget.c1019
-rw-r--r--fs/xfs/xfs_imap.h54
-rw-r--r--fs/xfs/xfs_inode.c3875
-rw-r--r--fs/xfs/xfs_inode.h562
-rw-r--r--fs/xfs/xfs_inode_item.c1092
-rw-r--r--fs/xfs/xfs_inode_item.h197
-rw-r--r--fs/xfs/xfs_inum.h173
-rw-r--r--fs/xfs/xfs_iocore.c133
-rw-r--r--fs/xfs/xfs_iomap.c1000
-rw-r--r--fs/xfs/xfs_iomap.h107
-rw-r--r--fs/xfs/xfs_itable.c806
-rw-r--r--fs/xfs/xfs_itable.h106
-rw-r--r--fs/xfs/xfs_log.c3562
-rw-r--r--fs/xfs/xfs_log.h193
-rw-r--r--fs/xfs/xfs_log_priv.h561
-rw-r--r--fs/xfs/xfs_log_recover.c4098
-rw-r--r--fs/xfs/xfs_log_recover.h81
-rw-r--r--fs/xfs/xfs_mac.h120
-rw-r--r--fs/xfs/xfs_macros.c2245
-rw-r--r--fs/xfs/xfs_macros.h104
-rw-r--r--fs/xfs/xfs_mount.c1593
-rw-r--r--fs/xfs/xfs_mount.h572
-rw-r--r--fs/xfs/xfs_qmops.c71
-rw-r--r--fs/xfs/xfs_quota.h356
-rw-r--r--fs/xfs/xfs_refcache.c439
-rw-r--r--fs/xfs/xfs_refcache.h66
-rw-r--r--fs/xfs/xfs_rename.c673
-rw-r--r--fs/xfs/xfs_rtalloc.c2469
-rw-r--r--fs/xfs/xfs_rtalloc.h187
-rw-r--r--fs/xfs/xfs_rw.c356
-rw-r--r--fs/xfs/xfs_rw.h154
-rw-r--r--fs/xfs/xfs_sb.h583
-rw-r--r--fs/xfs/xfs_trans.c1315
-rw-r--r--fs/xfs/xfs_trans.h1042
-rw-r--r--fs/xfs/xfs_trans_ail.c596
-rw-r--r--fs/xfs/xfs_trans_buf.c1093
-rw-r--r--fs/xfs/xfs_trans_extfree.c156
-rw-r--r--fs/xfs/xfs_trans_inode.c342
-rw-r--r--fs/xfs/xfs_trans_item.c553
-rw-r--r--fs/xfs/xfs_trans_priv.h73
-rw-r--r--fs/xfs/xfs_trans_space.h105
-rw-r--r--fs/xfs/xfs_types.h182
-rw-r--r--fs/xfs/xfs_utils.c488
-rw-r--r--fs/xfs/xfs_utils.h52
-rw-r--r--fs/xfs/xfs_vfsops.c1928
-rw-r--r--fs/xfs/xfs_vnodeops.c4712
844 files changed, 445157 insertions, 0 deletions
diff --git a/fs/ChangeLog b/fs/ChangeLog
new file mode 100644
index 00000000000000..9d9b0cace370a6
--- /dev/null
+++ b/fs/ChangeLog
@@ -0,0 +1,159 @@
+Mon Oct 24 23:27:42 1994 Theodore Y. Ts'o (tytso@rt-11)
+
+ * fcntl.c (sys_fcntl): Liberalize security checks which Alan Cox
+ put in.
+
+Thu Oct 20 23:44:22 1994 Theodore Y. Ts'o (tytso@rt-11)
+
+ * fcntl.c (sys_fcntl): Add more of a security check to the
+ F_SETOWN fcntl().
+
+[Tons of changes missed, indeed. This list is worth restarting since
+at least some fixes WILL break third-party filesystems. Sorry, but
+there was no other way to fix rmdir/rename deadlock, for one.]
+
+Wed Dec 2 (Linus, fill the rest, please)
+
+ * namei.c (do_rmdir) and rmdir method in filesystems:
+ Locking of directory we remove was taken to VFS.
+ See comments in do_rmdir(). Unfixed filesystems
+ will bloody likely deadlock in rmdir().
+
+Thu Dec 3 17:25:31 1998 Al Viro (viro@math.psu.edu)
+
+ * namei.c (do_rmdir):
+ Reject non-directories here.
+ Two (probably) obsolete checks moved here too: we fail if
+ the directory we remove is the same as parent (BUG: we
+ serve mountpoints later) or if it lives on a different
+ device.
+ * sysv/namei.c (sysv_rmdir): See sysv/CHANGES
+
+Fri Dec 4 00:54:12 1998 AV
+
+ * namei.c (check_sticky): New function check_sticky(dir, inode).
+ If dir is sticky check whether we can unlink/rmdir/rename
+ the inode. Returns 1 if we can't. If dir isn't sticky -
+ return 0 (i.e. no objections). Some filesystems require
+ suser() here; some are fine with CAP_FOWNER. The later
+ seems more reasonable.
+ * namei.c (do_rmdir):
+ Moved the check for sticky bit here.
+ * affs/{inode,namei}.c:
+ All AFFS directories have sticky semantics (i.e. non-owner
+ having write permisssions on directory can unlink/rmdir/rename
+ only the files he owns), but AFFS didn't set S_ISVTX on them.
+ Fixed. NB: maybe this behaviour should be controlled by mount
+ option. Obvious values being 'sticky' (current behaviour),
+ 'nonsticky' (normal behaviour) and maybe some play on 'D'
+ permissions bit. FIXME.
+ * qnx4/namei.c (qnx4_rmdir):
+ Plugged inode leak.
+ * ufs/namei.c (ufs_rmdir):
+ Changed handling of busy directory to new scheme.
+
+Fri Dec 4 10:30:58 1998 AV
+
+ * namei.c (VFS_rmdir): New function. It gets inode of the parent and
+ dentry of the victim, does all checks and applies fs-specific
+ rmdir() method. It should be called with semaphores down
+ on both the victim and its parent and with bumped d_count on
+ victim (see comments in do_rmdir).
+ * include/linux/fs.h: Added VFS_rmdir
+ * kernel/ksyms.c: Added VFS_rmdir to export list (for NFSD).
+ * nfsd/vfs.c: Fixed rmdir handling.
+
+Tue Dec 8 05:55:08 1998 AV
+ * vfat/namei.c: Fixed the bug in vfat_rename() introduced in the
+ first round of rmdir fixes.
+
+Wed Dec 9 03:06:10 1998 AV
+ * namei.c (do_rename): part of fs-independent checks had been moved
+ here (sticky bit handling, type mismatches). Cases of
+ the source or target being append-only or immutable also went
+ here - if we check it for parent we could as well do it for
+ children.
+ * {affs,ext2,minix,sysv,ufs}/namei.c (do_*_rename):
+ Removed tests that went to VFS, it simplified the code big way.
+ Fixed a race in check for empty target - we should check for
+ extra owners _before_ checking for emptiness, not after it.
+ * {ext2,ufs}/namei.c (do_*_rename):
+ VERY nasty bug shot: if somebody mkdired /tmp/cca01234, went
+ there, rmdired '.', waited till somebody created a file with
+ the same name and said mv . /tmp/goodbye_sticky_bit... Well,
+ goodbye sticky bit. Down, not across!
+ * {minix,sysv}/namei.c (do_*_rename):
+ Incorrect check for other owners (i_count instead of d_count).
+ Fixed.
+ * vfat: Looks like the changes above fixed a bug in VFAT - this beast
+ used to allow renaming file over directory and vice versa.
+
+Wed Dec 9 08:00:27 1998 AV
+ * namei.c (VFS_rename): New function. It gets the same arguments as
+ ->rename() method, does all checks and applies fs-specific
+ rmdir() method. It should be called with semaphores down
+ on both parents.
+ * include/linux/fs.h: Added VFS_rename
+ * kernel/ksyms.c: Added VFS_rename to export list (for NFSD).
+ * nfsd/vfs.c: Changed rename handling (switched to VFS_rename).
+
+Wed Dec 9 18:16:27 1998 AV
+ * namei.c (do_unlink): handling of sticky bit went here.
+ * {affs,ext2,minix,qnx4,sysv,ufs}/namei.c (*_unlink):
+ removed handling of sticky bit.
+ * qnx4/namei.c (qnx4_unlink):
+ Yet another inode leak. Fixed.
+
+Thu Dec 10 04:55:26 1998 AV
+ * {ext2,minix,sysv,ufs}/namei.c (*_mknod):
+ removed meaningless code handling attempts to mknod symlinks
+ and directories. VFS protects us from _that_ and if this code
+ would ever be called we'ld get a filesystem corruption.
+
+Thu Dec 10 16:58:50 1998 AV
+ * namei.c (do_rename): Fixed dentry leak that had been introduced by
+ the first round of rmdir fixes.
+
+Fri Dec 11 14:57:17 1998 AV
+ * msdos/namei.c (msdos_rmdir): Fixed race in emptiness check.
+
+Sat Dec 12 19:59:57 1998 AV
+ * msdos/namei.c (msdos_mkdir): Fixed the evil breakage introduced by
+ the changes of rmdir locking scheme. We shouldn't call
+ msdos_rmdir from there.
+
+Sun Dec 13 02:05:16 1998 AV
+ * namei.c (do_unlink):
+ Added new function: vfs_unlink, with the same arguments as
+ ->unlink() method.
+ * kernel/ksyms.c: Made it exported.
+ * include/linux/fs.h: Added prototype.
+ * nfsd/vfs.c: Changed handling of unlink (switched to vfs_unlink)
+ * {ext2,ufs}/namei.c (*_unlink): moved handling of imm./append-only to
+ VFS.
+
+Wed Dec 16 06:10:04 1998 AV
+ * namei.c (may_create, may_delete): New inline functions.
+ They check whether creation/deletion is permitted.
+ Checks from other places of namei.c went there.
+ Looks like originally I misread permission-related stuff
+ both here and in nfsd. In particular, checks for
+ immutable are done in permission(). D'oh.
+ * unlink on directory should return -EISDIR, not -EPERM as it used to
+ do. Fixed.
+ * rmdir of immutable/append-only directory shouldn't be allowed. Fixed.
+
+Remains unfixed:
+ * rename's handling of races is, erm, not optimal. Looks like I know
+ what to do, but this thing needs some more cleanup - we can
+ take care of almost all races in VFS and be much more graceful
+ wrt locking. Moreover, it would give strong lookup atomicity.
+ But it's a lot of changes to lookup and dcache code, so it will
+ go after the fs drivers' cleanup.
+ * affs allows HARD links to directories. VFS is, to put it politely,
+ not too ready to cope with _that_. And I'm not sure it should
+ be - looks like they are pretty much similar to symlinks.
+ * truncate doesn't give a damn about IO errors and disk overflows (on
+ braindead filesystems). I've submitted a patch to Linus, but
+ looks like it wasn't applied.
+ * msdos: shouldn't we treat SYS as IMMUTABLE? Makes sense, IMHO.
diff --git a/fs/Config.in b/fs/Config.in
new file mode 100644
index 00000000000000..04d4d640168730
--- /dev/null
+++ b/fs/Config.in
@@ -0,0 +1,179 @@
+#
+# File system configuration
+#
+mainmenu_option next_comment
+comment 'File systems'
+
+bool 'Quota support' CONFIG_QUOTA
+dep_tristate ' VFS v0 quota format support' CONFIG_QFMT_V2 $CONFIG_QUOTA
+
+tristate 'Kernel automounter support' CONFIG_AUTOFS_FS
+tristate 'Kernel automounter version 4 support (also supports v3)' CONFIG_AUTOFS4_FS
+
+tristate 'Reiserfs support' CONFIG_REISERFS_FS
+dep_mbool ' Enable reiserfs debug mode' CONFIG_REISERFS_CHECK $CONFIG_REISERFS_FS
+dep_mbool ' Stats in /proc/fs/reiserfs' CONFIG_REISERFS_PROC_INFO $CONFIG_REISERFS_FS
+
+dep_tristate 'ADFS file system support (EXPERIMENTAL)' CONFIG_ADFS_FS $CONFIG_EXPERIMENTAL
+dep_mbool ' ADFS write support (DANGEROUS)' CONFIG_ADFS_FS_RW $CONFIG_ADFS_FS $CONFIG_EXPERIMENTAL
+
+dep_tristate 'Amiga FFS file system support (EXPERIMENTAL)' CONFIG_AFFS_FS $CONFIG_EXPERIMENTAL
+
+dep_tristate 'Apple HFS file system support (EXPERIMENTAL)' CONFIG_HFS_FS $CONFIG_EXPERIMENTAL
+
+dep_tristate 'Apple HFS+ (Extended HFS) file system support (EXPERIMENTAL)' CONFIG_HFSPLUS_FS $CONFIG_EXPERIMENTAL
+
+dep_tristate 'BeOS file systemv(BeFS) support (read only) (EXPERIMENTAL)' CONFIG_BEFS_FS $CONFIG_EXPERIMENTAL
+dep_mbool ' Debug Befs' CONFIG_BEFS_DEBUG $CONFIG_BEFS_FS
+
+dep_tristate 'BFS file system support (EXPERIMENTAL)' CONFIG_BFS_FS $CONFIG_EXPERIMENTAL
+
+tristate 'Ext3 journalling file system support' CONFIG_EXT3_FS
+# CONFIG_JBD could be its own option (even modular), but until there are
+# other users than ext3, we will simply make it be the same as CONFIG_EXT3_FS
+# dep_tristate ' Journal Block Device support (JBD for ext3)' CONFIG_JBD $CONFIG_EXT3_FS
+define_bool CONFIG_JBD $CONFIG_EXT3_FS
+dep_mbool ' JBD (ext3) debugging support' CONFIG_JBD_DEBUG $CONFIG_JBD
+
+# msdos file systems
+tristate 'DOS FAT fs support' CONFIG_FAT_FS
+dep_tristate ' MSDOS fs support' CONFIG_MSDOS_FS $CONFIG_FAT_FS
+dep_tristate ' UMSDOS: Unix-like file system on top of standard MSDOS fs' CONFIG_UMSDOS_FS $CONFIG_MSDOS_FS
+dep_tristate ' VFAT (Windows-95) fs support' CONFIG_VFAT_FS $CONFIG_FAT_FS
+dep_tristate 'EFS file system support (read only) (EXPERIMENTAL)' CONFIG_EFS_FS $CONFIG_EXPERIMENTAL
+dep_tristate 'Journalling Flash File System (JFFS) support' CONFIG_JFFS_FS $CONFIG_MTD
+if [ "$CONFIG_JFFS_FS" = "y" -o "$CONFIG_JFFS_FS" = "m" ] ; then
+ int 'JFFS debugging verbosity (0 = quiet, 3 = noisy)' CONFIG_JFFS_FS_VERBOSE 0
+ bool 'JFFS stats available in /proc filesystem' CONFIG_JFFS_PROC_FS
+fi
+dep_tristate 'Journalling Flash File System v2 (JFFS2) support' CONFIG_JFFS2_FS $CONFIG_MTD
+if [ "$CONFIG_JFFS2_FS" = "y" -o "$CONFIG_JFFS2_FS" = "m" ] ; then
+ int 'JFFS2 debugging verbosity (0 = quiet, 2 = noisy)' CONFIG_JFFS2_FS_DEBUG 0
+fi
+tristate 'Compressed ROM file system support' CONFIG_CRAMFS
+bool 'Virtual memory file system support (former shm fs)' CONFIG_TMPFS
+define_bool CONFIG_RAMFS y
+
+tristate 'ISO 9660 CDROM file system support' CONFIG_ISO9660_FS
+dep_mbool ' Microsoft Joliet CDROM extensions' CONFIG_JOLIET $CONFIG_ISO9660_FS
+dep_mbool ' Transparent decompression extension' CONFIG_ZISOFS $CONFIG_ISO9660_FS
+
+tristate 'JFS filesystem support' CONFIG_JFS_FS
+dep_mbool ' JFS debugging' CONFIG_JFS_DEBUG $CONFIG_JFS_FS
+dep_mbool ' JFS statistics' CONFIG_JFS_STATISTICS $CONFIG_JFS_FS
+
+tristate 'Minix fs support' CONFIG_MINIX_FS
+
+tristate 'FreeVxFS file system support (VERITAS VxFS(TM) compatible)' CONFIG_VXFS_FS
+tristate 'NTFS file system support (read only)' CONFIG_NTFS_FS
+dep_mbool ' NTFS write support (DANGEROUS)' CONFIG_NTFS_RW $CONFIG_NTFS_FS $CONFIG_EXPERIMENTAL
+
+tristate 'OS/2 HPFS file system support' CONFIG_HPFS_FS
+
+bool '/proc file system support' CONFIG_PROC_FS
+
+# For some reason devfs corrupts memory badly on x86-64. Disable it
+# for now.
+if [ "$CONFIG_X86_64" != "y" ] ; then
+dep_bool '/dev file system support (EXPERIMENTAL)' CONFIG_DEVFS_FS $CONFIG_EXPERIMENTAL
+dep_bool ' Automatically mount at boot' CONFIG_DEVFS_MOUNT $CONFIG_DEVFS_FS
+dep_bool ' Debug devfs' CONFIG_DEVFS_DEBUG $CONFIG_DEVFS_FS
+fi
+
+# It compiles as a module for testing only. It should not be used
+# as a module in general. If we make this "tristate", a bunch of people
+# who don't know what they are doing turn it on and complain when it
+# breaks.
+dep_bool '/dev/pts file system for Unix98 PTYs' CONFIG_DEVPTS_FS $CONFIG_UNIX98_PTYS
+
+tristate 'QNX4 file system support (read only)' CONFIG_QNX4FS_FS
+dep_mbool ' QNX4FS write support (DANGEROUS)' CONFIG_QNX4FS_RW $CONFIG_QNX4FS_FS $CONFIG_EXPERIMENTAL
+
+tristate 'ROM file system support' CONFIG_ROMFS_FS
+
+tristate 'Second extended fs support' CONFIG_EXT2_FS
+
+tristate 'System V/Xenix/V7/Coherent file system support' CONFIG_SYSV_FS
+
+tristate 'UDF file system support (read only)' CONFIG_UDF_FS
+dep_mbool ' UDF write support (DANGEROUS)' CONFIG_UDF_RW $CONFIG_UDF_FS $CONFIG_EXPERIMENTAL
+
+tristate 'UFS file system support (read only)' CONFIG_UFS_FS
+dep_mbool ' UFS file system write support (DANGEROUS)' CONFIG_UFS_FS_WRITE $CONFIG_UFS_FS $CONFIG_EXPERIMENTAL
+
+tristate 'XFS filesystem support' CONFIG_XFS_FS
+dep_mbool ' Quota support' CONFIG_XFS_QUOTA $CONFIG_XFS_FS
+dep_mbool ' Realtime support (EXPERIMENTAL)' CONFIG_XFS_RT $CONFIG_XFS_FS $CONFIG_EXPERIMENTAL
+dep_mbool ' Tracing support (EXPERIMENTAL)' CONFIG_XFS_TRACE $CONFIG_XFS_FS $CONFIG_EXPERIMENTAL
+dep_mbool ' Debugging support (EXPERIMENTAL)' CONFIG_XFS_DEBUG $CONFIG_XFS_FS $CONFIG_EXPERIMENTAL
+
+if [ "$CONFIG_NET" = "y" ]; then
+
+ mainmenu_option next_comment
+ comment 'Network File Systems'
+
+ dep_tristate 'Coda file system support (advanced network fs)' CONFIG_CODA_FS $CONFIG_INET
+ dep_tristate 'InterMezzo file system support (replicating fs) (EXPERIMENTAL)' CONFIG_INTERMEZZO_FS $CONFIG_INET $CONFIG_EXPERIMENTAL
+ dep_tristate 'NFS file system support' CONFIG_NFS_FS $CONFIG_INET
+ dep_mbool ' Provide NFSv3 client support' CONFIG_NFS_V3 $CONFIG_NFS_FS
+ dep_mbool ' Allow direct I/O on NFS files (EXPERIMENTAL)' CONFIG_NFS_DIRECTIO $CONFIG_NFS_FS $CONFIG_EXPERIMENTAL
+ dep_bool ' Root file system on NFS' CONFIG_ROOT_NFS $CONFIG_NFS_FS $CONFIG_IP_PNP
+
+ dep_tristate 'NFS server support' CONFIG_NFSD $CONFIG_INET
+ dep_mbool ' Provide NFSv3 server support' CONFIG_NFSD_V3 $CONFIG_NFSD
+ dep_mbool ' Provide NFS server over TCP support' CONFIG_NFSD_TCP $CONFIG_NFSD
+
+ if [ "$CONFIG_NFS_FS" = "y" -o "$CONFIG_NFSD" = "y" ]; then
+ define_tristate CONFIG_SUNRPC y
+ define_tristate CONFIG_LOCKD y
+ else
+ if [ "$CONFIG_NFS_FS" = "m" -o "$CONFIG_NFSD" = "m" ]; then
+ define_tristate CONFIG_SUNRPC m
+ define_tristate CONFIG_LOCKD m
+ else
+ define_tristate CONFIG_SUNRPC n
+ define_tristate CONFIG_LOCKD n
+ fi
+ fi
+ if [ "$CONFIG_NFSD_V3" = "y" -o "$CONFIG_NFS_V3" = "y" ]; then
+ define_bool CONFIG_LOCKD_V4 y
+ fi
+
+ dep_tristate 'SMB file system support (to mount Windows shares etc.)' CONFIG_SMB_FS $CONFIG_INET
+ if [ "$CONFIG_SMB_FS" != "n" ]; then
+ bool ' Use a default NLS' CONFIG_SMB_NLS_DEFAULT
+ if [ "$CONFIG_SMB_NLS_DEFAULT" = "y" ]; then
+ string ' Default Remote NLS Option' CONFIG_SMB_NLS_REMOTE "cp437"
+ fi
+ bool ' Enable Unix Extensions' CONFIG_SMB_UNIX
+ fi
+ if [ "$CONFIG_IPX" != "n" -o "$CONFIG_INET" != "n" ]; then
+ tristate 'NCP file system support (to mount NetWare volumes)' CONFIG_NCP_FS
+ source fs/ncpfs/Config.in
+ else
+ # for fs/nls/Config.in
+ define_bool CONFIG_NCPFS_NLS n
+ fi
+ endmenu
+
+else
+ # for fs/nls/Config.in
+ define_bool CONFIG_NCPFS_NLS n
+ define_bool CONFIG_SMB_FS n
+fi
+
+#
+# Do we need the compression support?
+#
+if [ "$CONFIG_ZISOFS" = "y" ]; then
+ define_tristate CONFIG_ZISOFS_FS $CONFIG_ISO9660_FS
+else
+ define_tristate CONFIG_ZISOFS_FS n
+fi
+
+mainmenu_option next_comment
+comment 'Partition Types'
+source fs/partitions/Config.in
+endmenu
+source fs/nls/Config.in
+endmenu
diff --git a/fs/Makefile b/fs/Makefile
new file mode 100644
index 00000000000000..85fdfded573616
--- /dev/null
+++ b/fs/Makefile
@@ -0,0 +1,84 @@
+#
+# Makefile for the Linux filesystems.
+#
+# 14 Sep 2000, Christoph Hellwig <hch@infradead.org>
+# Rewritten to use lists instead of if-statements.
+#
+
+O_TARGET := fs.o
+
+export-objs := filesystems.o open.o dcache.o buffer.o dquot.o
+mod-subdirs := nls
+
+obj-y := open.o read_write.o devices.o file_table.o buffer.o \
+ super.o block_dev.o char_dev.o stat.o exec.o pipe.o namei.o \
+ fcntl.o ioctl.o readdir.o select.o fifo.o locks.o \
+ dcache.o inode.o attr.o bad_inode.o file.o iobuf.o dnotify.o \
+ filesystems.o namespace.o seq_file.o xattr.o quota.o
+
+obj-$(CONFIG_QUOTA) += dquot.o quota_v1.o
+obj-$(CONFIG_QFMT_V2) += quota_v2.o
+
+subdir-$(CONFIG_PROC_FS) += proc
+subdir-y += partitions
+
+# Do not add any filesystems before this line
+subdir-$(CONFIG_EXT3_FS) += ext3 # Before ext2 so root fs can be ext3
+subdir-$(CONFIG_JBD) += jbd
+subdir-$(CONFIG_EXT2_FS) += ext2
+subdir-$(CONFIG_CRAMFS) += cramfs
+subdir-$(CONFIG_RAMFS) += ramfs
+subdir-$(CONFIG_CODA_FS) += coda
+subdir-$(CONFIG_INTERMEZZO_FS) += intermezzo
+subdir-$(CONFIG_MINIX_FS) += minix
+subdir-$(CONFIG_FAT_FS) += fat
+subdir-$(CONFIG_UMSDOS_FS) += umsdos
+subdir-$(CONFIG_MSDOS_FS) += msdos
+subdir-$(CONFIG_VFAT_FS) += vfat
+subdir-$(CONFIG_BFS_FS) += bfs
+subdir-$(CONFIG_ISO9660_FS) += isofs
+subdir-$(CONFIG_DEVFS_FS) += devfs
+subdir-$(CONFIG_HFSPLUS_FS) += hfsplus # Before hfs to find wrapped HFS+
+subdir-$(CONFIG_HFS_FS) += hfs
+subdir-$(CONFIG_VXFS_FS) += freevxfs
+subdir-$(CONFIG_NFS_FS) += nfs
+subdir-$(CONFIG_NFSD) += nfsd
+subdir-$(CONFIG_LOCKD) += lockd
+subdir-$(CONFIG_NLS) += nls
+subdir-$(CONFIG_SYSV_FS) += sysv
+subdir-$(CONFIG_SMB_FS) += smbfs
+subdir-$(CONFIG_NCP_FS) += ncpfs
+subdir-$(CONFIG_HPFS_FS) += hpfs
+subdir-$(CONFIG_NTFS_FS) += ntfs
+subdir-$(CONFIG_UFS_FS) += ufs
+subdir-$(CONFIG_EFS_FS) += efs
+subdir-$(CONFIG_JFFS_FS) += jffs
+subdir-$(CONFIG_JFFS2_FS) += jffs2
+subdir-$(CONFIG_AFFS_FS) += affs
+subdir-$(CONFIG_ROMFS_FS) += romfs
+subdir-$(CONFIG_QNX4FS_FS) += qnx4
+subdir-$(CONFIG_UDF_FS) += udf
+subdir-$(CONFIG_AUTOFS_FS) += autofs
+subdir-$(CONFIG_AUTOFS4_FS) += autofs4
+subdir-$(CONFIG_ADFS_FS) += adfs
+subdir-$(CONFIG_REISERFS_FS) += reiserfs
+subdir-$(CONFIG_DEVPTS_FS) += devpts
+subdir-$(CONFIG_SUN_OPENPROMFS) += openpromfs
+subdir-$(CONFIG_BEFS_FS) += befs
+subdir-$(CONFIG_JFS_FS) += jfs
+subdir-$(CONFIG_XFS_FS) += xfs
+
+obj-$(CONFIG_BINFMT_AOUT) += binfmt_aout.o
+obj-$(CONFIG_BINFMT_EM86) += binfmt_em86.o
+obj-$(CONFIG_BINFMT_MISC) += binfmt_misc.o
+
+# binfmt_script is always there
+obj-y += binfmt_script.o
+
+obj-$(CONFIG_BINFMT_ELF) += binfmt_elf.o
+
+# persistent filesystems
+obj-y += $(join $(subdir-y),$(subdir-y:%=/%.o))
+
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/Makefile.lib b/fs/Makefile.lib
new file mode 100644
index 00000000000000..bbdcfa520d193b
--- /dev/null
+++ b/fs/Makefile.lib
@@ -0,0 +1,2 @@
+obj-$(CONFIG_JFFS2_FS) += crc32.o
+obj-$(CONFIG_EFI_PARTITION) += crc32.o
diff --git a/fs/adfs/Makefile b/fs/adfs/Makefile
new file mode 100644
index 00000000000000..f1a279dde8831a
--- /dev/null
+++ b/fs/adfs/Makefile
@@ -0,0 +1,15 @@
+#
+# Makefile for the linux adfs filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile.
+
+O_TARGET := adfs.o
+
+obj-y := dir.o dir_f.o dir_fplus.o file.o inode.o map.o super.o
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/adfs/adfs.h b/fs/adfs/adfs.h
new file mode 100644
index 00000000000000..30c400e5bcc6e4
--- /dev/null
+++ b/fs/adfs/adfs.h
@@ -0,0 +1,137 @@
+/* Internal data structures for ADFS */
+
+#define ADFS_FREE_FRAG 0
+#define ADFS_BAD_FRAG 1
+#define ADFS_ROOT_FRAG 2
+
+#define ADFS_NDA_OWNER_READ (1 << 0)
+#define ADFS_NDA_OWNER_WRITE (1 << 1)
+#define ADFS_NDA_LOCKED (1 << 2)
+#define ADFS_NDA_DIRECTORY (1 << 3)
+#define ADFS_NDA_EXECUTE (1 << 4)
+#define ADFS_NDA_PUBLIC_READ (1 << 5)
+#define ADFS_NDA_PUBLIC_WRITE (1 << 6)
+
+#include "dir_f.h"
+
+/*
+ * Directory handling
+ */
+struct adfs_dir {
+ struct super_block *sb;
+
+ int nr_buffers;
+ struct buffer_head *bh[4];
+ unsigned int pos;
+ unsigned int parent_id;
+
+ struct adfs_dirheader dirhead;
+ union adfs_dirtail dirtail;
+};
+
+/*
+ * This is the overall maximum name length
+ */
+#define ADFS_MAX_NAME_LEN 256
+struct object_info {
+ __u32 parent_id; /* parent object id */
+ __u32 file_id; /* object id */
+ __u32 loadaddr; /* load address */
+ __u32 execaddr; /* execution address */
+ __u32 size; /* size */
+ __u8 attr; /* RISC OS attributes */
+ unsigned char name_len; /* name length */
+ char name[ADFS_MAX_NAME_LEN];/* file name */
+};
+
+struct adfs_dir_ops {
+ int (*read)(struct super_block *sb, unsigned int id, unsigned int sz, struct adfs_dir *dir);
+ int (*setpos)(struct adfs_dir *dir, unsigned int fpos);
+ int (*getnext)(struct adfs_dir *dir, struct object_info *obj);
+ int (*update)(struct adfs_dir *dir, struct object_info *obj);
+ int (*create)(struct adfs_dir *dir, struct object_info *obj);
+ int (*remove)(struct adfs_dir *dir, struct object_info *obj);
+ void (*free)(struct adfs_dir *dir);
+};
+
+struct adfs_discmap {
+ struct buffer_head *dm_bh;
+ __u32 dm_startblk;
+ unsigned int dm_startbit;
+ unsigned int dm_endbit;
+};
+
+/* dir stuff */
+
+
+/* Inode stuff */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+int adfs_get_block(struct inode *inode, long block,
+ struct buffer_head *bh, int create);
+#else
+int adfs_bmap(struct inode *inode, int block);
+#endif
+struct inode *adfs_iget(struct super_block *sb, struct object_info *obj);
+void adfs_read_inode(struct inode *inode);
+void adfs_write_inode(struct inode *inode,int unused);
+int adfs_notify_change(struct dentry *dentry, struct iattr *attr);
+
+/* map.c */
+extern int adfs_map_lookup(struct super_block *sb, int frag_id, int offset);
+extern unsigned int adfs_map_free(struct super_block *sb);
+
+/* Misc */
+void __adfs_error(struct super_block *sb, const char *function,
+ const char *fmt, ...);
+#define adfs_error(sb, fmt...) __adfs_error(sb, __FUNCTION__, fmt)
+
+/* namei.c */
+extern struct dentry *adfs_lookup(struct inode *dir, struct dentry *dentry);
+
+/* super.c */
+
+/*
+ * Inodes and file operations
+ */
+
+/* dir_*.c */
+extern struct inode_operations adfs_dir_inode_operations;
+extern struct file_operations adfs_dir_operations;
+extern struct dentry_operations adfs_dentry_operations;
+extern struct adfs_dir_ops adfs_f_dir_ops;
+extern struct adfs_dir_ops adfs_fplus_dir_ops;
+
+extern int adfs_dir_update(struct super_block *sb, struct object_info *obj);
+
+/* file.c */
+extern struct inode_operations adfs_file_inode_operations;
+extern struct file_operations adfs_file_operations;
+
+extern inline __u32 signed_asl(__u32 val, signed int shift)
+{
+ if (shift >= 0)
+ val <<= shift;
+ else
+ val >>= -shift;
+ return val;
+}
+
+/*
+ * Calculate the address of a block in an object given the block offset
+ * and the object identity.
+ *
+ * The root directory ID should always be looked up in the map [3.4]
+ */
+extern inline int
+__adfs_block_map(struct super_block *sb, unsigned int object_id,
+ unsigned int block)
+{
+ if (object_id & 255) {
+ unsigned int off;
+
+ off = (object_id & 255) - 1;
+ block += off << sb->u.adfs_sb.s_log2sharesize;
+ }
+
+ return adfs_map_lookup(sb, object_id >> 8, block);
+}
diff --git a/fs/adfs/dir.c b/fs/adfs/dir.c
new file mode 100644
index 00000000000000..51828c6057c4aa
--- /dev/null
+++ b/fs/adfs/dir.c
@@ -0,0 +1,295 @@
+/*
+ * linux/fs/adfs/dir.c
+ *
+ * Copyright (C) 1999-2000 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Common directory handling for ADFS
+ */
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/adfs_fs.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/spinlock.h>
+
+#include "adfs.h"
+
+/*
+ * For future. This should probably be per-directory.
+ */
+static rwlock_t adfs_dir_lock;
+
+static int
+adfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ struct super_block *sb = inode->i_sb;
+ struct adfs_dir_ops *ops = sb->u.adfs_sb.s_dir;
+ struct object_info obj;
+ struct adfs_dir dir;
+ int ret = 0;
+
+ if (filp->f_pos >> 32)
+ goto out;
+
+ ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
+ if (ret)
+ goto out;
+
+ switch ((unsigned long)filp->f_pos) {
+ case 0:
+ if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR) < 0)
+ goto free_out;
+ filp->f_pos += 1;
+
+ case 1:
+ if (filldir(dirent, "..", 2, 1, dir.parent_id, DT_DIR) < 0)
+ goto free_out;
+ filp->f_pos += 1;
+
+ default:
+ break;
+ }
+
+ read_lock(&adfs_dir_lock);
+
+ ret = ops->setpos(&dir, filp->f_pos - 2);
+ if (ret)
+ goto unlock_out;
+ while (ops->getnext(&dir, &obj) == 0) {
+ if (filldir(dirent, obj.name, obj.name_len,
+ filp->f_pos, obj.file_id, DT_UNKNOWN) < 0)
+ goto unlock_out;
+ filp->f_pos += 1;
+ }
+
+unlock_out:
+ read_unlock(&adfs_dir_lock);
+
+free_out:
+ ops->free(&dir);
+
+out:
+ return ret;
+}
+
+int
+adfs_dir_update(struct super_block *sb, struct object_info *obj)
+{
+ int ret = -EINVAL;
+#ifdef CONFIG_ADFS_FS_RW
+ struct adfs_dir_ops *ops = sb->u.adfs_sb.s_dir;
+ struct adfs_dir dir;
+
+ printk(KERN_INFO "adfs_dir_update: object %06X in dir %06X\n",
+ obj->file_id, obj->parent_id);
+
+ if (!ops->update) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = ops->read(sb, obj->parent_id, 0, &dir);
+ if (ret)
+ goto out;
+
+ write_lock(&adfs_dir_lock);
+ ret = ops->update(&dir, obj);
+ write_unlock(&adfs_dir_lock);
+
+ ops->free(&dir);
+out:
+#endif
+ return ret;
+}
+
+static int
+adfs_match(struct qstr *name, struct object_info *obj)
+{
+ int i;
+
+ if (name->len != obj->name_len)
+ return 0;
+
+ for (i = 0; i < name->len; i++) {
+ char c1, c2;
+
+ c1 = name->name[i];
+ c2 = obj->name[i];
+
+ if (c1 >= 'A' && c1 <= 'Z')
+ c1 += 'a' - 'A';
+ if (c2 >= 'A' && c2 <= 'Z')
+ c2 += 'a' - 'A';
+
+ if (c1 != c2)
+ return 0;
+ }
+ return 1;
+}
+
+static int
+adfs_dir_lookup_byname(struct inode *inode, struct qstr *name, struct object_info *obj)
+{
+ struct super_block *sb = inode->i_sb;
+ struct adfs_dir_ops *ops = sb->u.adfs_sb.s_dir;
+ struct adfs_dir dir;
+ int ret;
+
+ ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
+ if (ret)
+ goto out;
+
+ if (inode->u.adfs_i.parent_id != dir.parent_id) {
+ adfs_error(sb, "parent directory changed under me! (%lx but got %lx)\n",
+ inode->u.adfs_i.parent_id, dir.parent_id);
+ ret = -EIO;
+ goto free_out;
+ }
+
+ obj->parent_id = inode->i_ino;
+
+ /*
+ * '.' is handled by reserved_lookup() in fs/namei.c
+ */
+ if (name->len == 2 && name->name[0] == '.' && name->name[1] == '.') {
+ /*
+ * Currently unable to fill in the rest of 'obj',
+ * but this is better than nothing. We need to
+ * ascend one level to find it's parent.
+ */
+ obj->name_len = 0;
+ obj->file_id = obj->parent_id;
+ goto free_out;
+ }
+
+ read_lock(&adfs_dir_lock);
+
+ ret = ops->setpos(&dir, 0);
+ if (ret)
+ goto unlock_out;
+
+ ret = -ENOENT;
+ while (ops->getnext(&dir, obj) == 0) {
+ if (adfs_match(name, obj)) {
+ ret = 0;
+ break;
+ }
+ }
+
+unlock_out:
+ read_unlock(&adfs_dir_lock);
+
+free_out:
+ ops->free(&dir);
+out:
+ return ret;
+}
+
+struct file_operations adfs_dir_operations = {
+ read: generic_read_dir,
+ readdir: adfs_readdir,
+ fsync: file_fsync,
+};
+
+static int
+adfs_hash(struct dentry *parent, struct qstr *qstr)
+{
+ const unsigned int name_len = parent->d_sb->u.adfs_sb.s_namelen;
+ const unsigned char *name;
+ unsigned long hash;
+ int i;
+
+ if (qstr->len < name_len)
+ return 0;
+
+ /*
+ * Truncate the name in place, avoids
+ * having to define a compare function.
+ */
+ qstr->len = i = name_len;
+ name = qstr->name;
+ hash = init_name_hash();
+ while (i--) {
+ char c;
+
+ c = *name++;
+ if (c >= 'A' && c <= 'Z')
+ c += 'a' - 'A';
+
+ hash = partial_name_hash(c, hash);
+ }
+ qstr->hash = end_name_hash(hash);
+
+ return 0;
+}
+
+/*
+ * Compare two names, taking note of the name length
+ * requirements of the underlying filesystem.
+ */
+static int
+adfs_compare(struct dentry *parent, struct qstr *entry, struct qstr *name)
+{
+ int i;
+
+ if (entry->len != name->len)
+ return 1;
+
+ for (i = 0; i < name->len; i++) {
+ char a, b;
+
+ a = entry->name[i];
+ b = name->name[i];
+
+ if (a >= 'A' && a <= 'Z')
+ a += 'a' - 'A';
+ if (b >= 'A' && b <= 'Z')
+ b += 'a' - 'A';
+
+ if (a != b)
+ return 1;
+ }
+ return 0;
+}
+
+struct dentry_operations adfs_dentry_operations = {
+ d_hash: adfs_hash,
+ d_compare: adfs_compare,
+};
+
+struct dentry *adfs_lookup(struct inode *dir, struct dentry *dentry)
+{
+ struct inode *inode = NULL;
+ struct object_info obj;
+ int error;
+
+ dentry->d_op = &adfs_dentry_operations;
+ error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj);
+ if (error == 0) {
+ error = -EACCES;
+ /*
+ * This only returns NULL if get_empty_inode
+ * fails.
+ */
+ inode = adfs_iget(dir->i_sb, &obj);
+ if (inode)
+ error = 0;
+ }
+ d_add(dentry, inode);
+ return ERR_PTR(error);
+}
+
+/*
+ * directories can handle most operations...
+ */
+struct inode_operations adfs_dir_inode_operations = {
+ lookup: adfs_lookup,
+ setattr: adfs_notify_change,
+};
diff --git a/fs/adfs/dir_f.c b/fs/adfs/dir_f.c
new file mode 100644
index 00000000000000..66a0c36a74fbc3
--- /dev/null
+++ b/fs/adfs/dir_f.c
@@ -0,0 +1,478 @@
+/*
+ * linux/fs/adfs/dir_f.c
+ *
+ * Copyright (C) 1997-1999 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * E and F format directory handling
+ */
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/adfs_fs.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/spinlock.h>
+
+#include "adfs.h"
+#include "dir_f.h"
+
+static void adfs_f_free(struct adfs_dir *dir);
+
+/*
+ * Read an (unaligned) value of length 1..4 bytes
+ */
+static inline unsigned int adfs_readval(unsigned char *p, int len)
+{
+ unsigned int val = 0;
+
+ switch (len) {
+ case 4: val |= p[3] << 24;
+ case 3: val |= p[2] << 16;
+ case 2: val |= p[1] << 8;
+ default: val |= p[0];
+ }
+ return val;
+}
+
+static inline void adfs_writeval(unsigned char *p, int len, unsigned int val)
+{
+ switch (len) {
+ case 4: p[3] = val >> 24;
+ case 3: p[2] = val >> 16;
+ case 2: p[1] = val >> 8;
+ default: p[0] = val;
+ }
+}
+
+static inline int adfs_readname(char *buf, char *ptr, int maxlen)
+{
+ char *old_buf = buf;
+
+ while (*ptr >= ' ' && maxlen--) {
+ if (*ptr == '/')
+ *buf++ = '.';
+ else
+ *buf++ = *ptr;
+ ptr++;
+ }
+ *buf = '\0';
+
+ return buf - old_buf;
+}
+
+static inline void adfs_writename(char *to, char *from, int maxlen)
+{
+ int i;
+
+ for (i = 0; i < maxlen; i++) {
+ if (from[i] == '\0')
+ break;
+ if (from[i] == '.')
+ to[i] = '/';
+ else
+ to[i] = from[i];
+ }
+
+ for (; i < maxlen; i++)
+ to[i] = '\0';
+}
+
+#define ror13(v) ((v >> 13) | (v << 19))
+
+#define dir_u8(idx) \
+ ({ int _buf = idx >> blocksize_bits; \
+ int _off = idx - (_buf << blocksize_bits);\
+ *(u8 *)(bh[_buf]->b_data + _off); \
+ })
+
+#define dir_u32(idx) \
+ ({ int _buf = idx >> blocksize_bits; \
+ int _off = idx - (_buf << blocksize_bits);\
+ *(u32 *)(bh[_buf]->b_data + _off); \
+ })
+
+#define bufoff(_bh,_idx) \
+ ({ int _buf = _idx >> blocksize_bits; \
+ int _off = _idx - (_buf << blocksize_bits);\
+ (u8 *)(_bh[_buf]->b_data + _off); \
+ })
+
+/*
+ * There are some algorithms that are nice in
+ * assembler, but a bitch in C... This is one
+ * of them.
+ */
+static u8
+adfs_dir_checkbyte(const struct adfs_dir *dir)
+{
+ struct buffer_head * const *bh = dir->bh;
+ const int blocksize_bits = dir->sb->s_blocksize_bits;
+ union { u32 *ptr32; u8 *ptr8; } ptr, end;
+ u32 dircheck = 0;
+ int last = 5 - 26;
+ int i = 0;
+
+ /*
+ * Accumulate each word up to the last whole
+ * word of the last directory entry. This
+ * can spread across several buffer heads.
+ */
+ do {
+ last += 26;
+ do {
+ dircheck = cpu_to_le32(dir_u32(i)) ^ ror13(dircheck);
+
+ i += sizeof(u32);
+ } while (i < (last & ~3));
+ } while (dir_u8(last) != 0);
+
+ /*
+ * Accumulate the last few bytes. These
+ * bytes will be within the same bh.
+ */
+ if (i != last) {
+ ptr.ptr8 = bufoff(bh, i);
+ end.ptr8 = ptr.ptr8 + last - i;
+
+ do
+ dircheck = *ptr.ptr8++ ^ ror13(dircheck);
+ while (ptr.ptr8 < end.ptr8);
+ }
+
+ /*
+ * The directory tail is in the final bh
+ * Note that contary to the RISC OS PRMs,
+ * the first few bytes are NOT included
+ * in the check. All bytes are in the
+ * same bh.
+ */
+ ptr.ptr8 = bufoff(bh, 2008);
+ end.ptr8 = ptr.ptr8 + 36;
+
+ do {
+ unsigned int v = *ptr.ptr32++;
+ dircheck = cpu_to_le32(v) ^ ror13(dircheck);
+ } while (ptr.ptr32 < end.ptr32);
+
+ return (dircheck ^ (dircheck >> 8) ^ (dircheck >> 16) ^ (dircheck >> 24)) & 0xff;
+}
+
+/*
+ * Read and check that a directory is valid
+ */
+int
+adfs_dir_read(struct super_block *sb, unsigned long object_id,
+ unsigned int size, struct adfs_dir *dir)
+{
+ const unsigned int blocksize_bits = sb->s_blocksize_bits;
+ int blk = 0;
+
+ /*
+ * Directories which are not a multiple of 2048 bytes
+ * are considered bad v2 [3.6]
+ */
+ if (size & 2047)
+ goto bad_dir;
+
+ size >>= blocksize_bits;
+
+ dir->nr_buffers = 0;
+ dir->sb = sb;
+
+ for (blk = 0; blk < size; blk++) {
+ int phys;
+
+ phys = __adfs_block_map(sb, object_id, blk);
+ if (!phys) {
+ adfs_error(sb, "dir object %lX has a hole at offset %d",
+ object_id, blk);
+ goto release_buffers;
+ }
+
+ dir->bh[blk] = sb_bread(sb, phys);
+ if (!dir->bh[blk])
+ goto release_buffers;
+ }
+
+ memcpy(&dir->dirhead, bufoff(dir->bh, 0), sizeof(dir->dirhead));
+ memcpy(&dir->dirtail, bufoff(dir->bh, 2007), sizeof(dir->dirtail));
+
+ if (dir->dirhead.startmasseq != dir->dirtail.new.endmasseq ||
+ memcmp(&dir->dirhead.startname, &dir->dirtail.new.endname, 4))
+ goto bad_dir;
+
+ if (memcmp(&dir->dirhead.startname, "Nick", 4) &&
+ memcmp(&dir->dirhead.startname, "Hugo", 4))
+ goto bad_dir;
+
+ if (adfs_dir_checkbyte(dir) != dir->dirtail.new.dircheckbyte)
+ goto bad_dir;
+
+ dir->nr_buffers = blk;
+
+ return 0;
+
+bad_dir:
+ adfs_error(sb, "corrupted directory fragment %lX",
+ object_id);
+release_buffers:
+ for (blk -= 1; blk >= 0; blk -= 1)
+ brelse(dir->bh[blk]);
+
+ dir->sb = NULL;
+
+ return -EIO;
+}
+
+/*
+ * convert a disk-based directory entry to a Linux ADFS directory entry
+ */
+static inline void
+adfs_dir2obj(struct object_info *obj, struct adfs_direntry *de)
+{
+ obj->name_len = adfs_readname(obj->name, de->dirobname, ADFS_F_NAME_LEN);
+ obj->file_id = adfs_readval(de->dirinddiscadd, 3);
+ obj->loadaddr = adfs_readval(de->dirload, 4);
+ obj->execaddr = adfs_readval(de->direxec, 4);
+ obj->size = adfs_readval(de->dirlen, 4);
+ obj->attr = de->newdiratts;
+}
+
+/*
+ * convert a Linux ADFS directory entry to a disk-based directory entry
+ */
+static inline void
+adfs_obj2dir(struct adfs_direntry *de, struct object_info *obj)
+{
+ adfs_writeval(de->dirinddiscadd, 3, obj->file_id);
+ adfs_writeval(de->dirload, 4, obj->loadaddr);
+ adfs_writeval(de->direxec, 4, obj->execaddr);
+ adfs_writeval(de->dirlen, 4, obj->size);
+ de->newdiratts = obj->attr;
+}
+
+/*
+ * get a directory entry. Note that the caller is responsible
+ * for holding the relevent locks.
+ */
+int
+__adfs_dir_get(struct adfs_dir *dir, int pos, struct object_info *obj)
+{
+ struct super_block *sb = dir->sb;
+ struct adfs_direntry de;
+ int thissize, buffer, offset;
+
+ buffer = pos >> sb->s_blocksize_bits;
+
+ if (buffer > dir->nr_buffers)
+ return -EINVAL;
+
+ offset = pos & (sb->s_blocksize - 1);
+ thissize = sb->s_blocksize - offset;
+ if (thissize > 26)
+ thissize = 26;
+
+ memcpy(&de, dir->bh[buffer]->b_data + offset, thissize);
+ if (thissize != 26)
+ memcpy(((char *)&de) + thissize, dir->bh[buffer + 1]->b_data,
+ 26 - thissize);
+
+ if (!de.dirobname[0])
+ return -ENOENT;
+
+ adfs_dir2obj(obj, &de);
+
+ return 0;
+}
+
+int
+__adfs_dir_put(struct adfs_dir *dir, int pos, struct object_info *obj)
+{
+ struct super_block *sb = dir->sb;
+ struct adfs_direntry de;
+ int thissize, buffer, offset;
+
+ buffer = pos >> sb->s_blocksize_bits;
+
+ if (buffer > dir->nr_buffers)
+ return -EINVAL;
+
+ offset = pos & (sb->s_blocksize - 1);
+ thissize = sb->s_blocksize - offset;
+ if (thissize > 26)
+ thissize = 26;
+
+ /*
+ * Get the entry in total
+ */
+ memcpy(&de, dir->bh[buffer]->b_data + offset, thissize);
+ if (thissize != 26)
+ memcpy(((char *)&de) + thissize, dir->bh[buffer + 1]->b_data,
+ 26 - thissize);
+
+ /*
+ * update it
+ */
+ adfs_obj2dir(&de, obj);
+
+ /*
+ * Put the new entry back
+ */
+ memcpy(dir->bh[buffer]->b_data + offset, &de, thissize);
+ if (thissize != 26)
+ memcpy(dir->bh[buffer + 1]->b_data, ((char *)&de) + thissize,
+ 26 - thissize);
+
+ return 0;
+}
+
+/*
+ * the caller is responsible for holding the necessary
+ * locks.
+ */
+static int
+adfs_dir_find_entry(struct adfs_dir *dir, unsigned long object_id)
+{
+ int pos, ret;
+
+ ret = -ENOENT;
+
+ for (pos = 5; pos < ADFS_NUM_DIR_ENTRIES * 26 + 5; pos += 26) {
+ struct object_info obj;
+
+ if (!__adfs_dir_get(dir, pos, &obj))
+ break;
+
+ if (obj.file_id == object_id) {
+ ret = pos;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int
+adfs_f_read(struct super_block *sb, unsigned int id, unsigned int sz, struct adfs_dir *dir)
+{
+ int ret;
+
+ if (sz != ADFS_NEWDIR_SIZE)
+ return -EIO;
+
+ ret = adfs_dir_read(sb, id, sz, dir);
+ if (ret)
+ adfs_error(sb, "unable to read directory");
+ else
+ dir->parent_id = adfs_readval(dir->dirtail.new.dirparent, 3);
+
+ return ret;
+}
+
+static int
+adfs_f_setpos(struct adfs_dir *dir, unsigned int fpos)
+{
+ if (fpos >= ADFS_NUM_DIR_ENTRIES)
+ return -ENOENT;
+
+ dir->pos = 5 + fpos * 26;
+ return 0;
+}
+
+static int
+adfs_f_getnext(struct adfs_dir *dir, struct object_info *obj)
+{
+ unsigned int ret;
+
+ ret = __adfs_dir_get(dir, dir->pos, obj);
+ if (ret == 0)
+ dir->pos += 26;
+
+ return ret;
+}
+
+static int
+adfs_f_update(struct adfs_dir *dir, struct object_info *obj)
+{
+ struct super_block *sb = dir->sb;
+ int ret, i;
+
+ ret = adfs_dir_find_entry(dir, obj->file_id);
+ if (ret < 0) {
+ adfs_error(dir->sb, "unable to locate entry to update");
+ goto out;
+ }
+
+ __adfs_dir_put(dir, ret, obj);
+
+ /*
+ * Increment directory sequence number
+ */
+ dir->bh[0]->b_data[0] += 1;
+ dir->bh[dir->nr_buffers - 1]->b_data[sb->s_blocksize - 6] += 1;
+
+ ret = adfs_dir_checkbyte(dir);
+ /*
+ * Update directory check byte
+ */
+ dir->bh[dir->nr_buffers - 1]->b_data[sb->s_blocksize - 1] = ret;
+
+#if 1
+ {
+ const unsigned int blocksize_bits = sb->s_blocksize_bits;
+
+ memcpy(&dir->dirhead, bufoff(dir->bh, 0), sizeof(dir->dirhead));
+ memcpy(&dir->dirtail, bufoff(dir->bh, 2007), sizeof(dir->dirtail));
+
+ if (dir->dirhead.startmasseq != dir->dirtail.new.endmasseq ||
+ memcmp(&dir->dirhead.startname, &dir->dirtail.new.endname, 4))
+ goto bad_dir;
+
+ if (memcmp(&dir->dirhead.startname, "Nick", 4) &&
+ memcmp(&dir->dirhead.startname, "Hugo", 4))
+ goto bad_dir;
+
+ if (adfs_dir_checkbyte(dir) != dir->dirtail.new.dircheckbyte)
+ goto bad_dir;
+ }
+#endif
+ for (i = dir->nr_buffers - 1; i >= 0; i--)
+ mark_buffer_dirty(dir->bh[i]);
+
+ ret = 0;
+out:
+ return ret;
+#if 1
+bad_dir:
+ adfs_error(dir->sb, "whoops! I broke a directory!");
+ return -EIO;
+#endif
+}
+
+static void
+adfs_f_free(struct adfs_dir *dir)
+{
+ int i;
+
+ for (i = dir->nr_buffers - 1; i >= 0; i--) {
+ brelse(dir->bh[i]);
+ dir->bh[i] = NULL;
+ }
+
+ dir->nr_buffers = 0;
+ dir->sb = NULL;
+}
+
+struct adfs_dir_ops adfs_f_dir_ops = {
+ adfs_f_read,
+ adfs_f_setpos,
+ adfs_f_getnext,
+ adfs_f_update,
+ NULL,
+ NULL,
+ adfs_f_free
+};
diff --git a/fs/adfs/dir_f.h b/fs/adfs/dir_f.h
new file mode 100644
index 00000000000000..e4713404096cfb
--- /dev/null
+++ b/fs/adfs/dir_f.h
@@ -0,0 +1,65 @@
+/*
+ * linux/fs/adfs/dir_f.h
+ *
+ * Copyright (C) 1999 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Structures of directories on the F format disk
+ */
+#ifndef ADFS_DIR_F_H
+#define ADFS_DIR_F_H
+
+/*
+ * Directory header
+ */
+struct adfs_dirheader {
+ unsigned char startmasseq;
+ unsigned char startname[4];
+};
+
+#define ADFS_NEWDIR_SIZE 2048
+#define ADFS_NUM_DIR_ENTRIES 77
+
+/*
+ * Directory entries
+ */
+struct adfs_direntry {
+#define ADFS_F_NAME_LEN 10
+ char dirobname[ADFS_F_NAME_LEN];
+ __u8 dirload[4];
+ __u8 direxec[4];
+ __u8 dirlen[4];
+ __u8 dirinddiscadd[3];
+ __u8 newdiratts;
+};
+
+/*
+ * Directory tail
+ */
+union adfs_dirtail {
+ struct {
+ unsigned char dirlastmask;
+ char dirname[10];
+ unsigned char dirparent[3];
+ char dirtitle[19];
+ unsigned char reserved[14];
+ unsigned char endmasseq;
+ unsigned char endname[4];
+ unsigned char dircheckbyte;
+ } old;
+ struct {
+ unsigned char dirlastmask;
+ unsigned char reserved[2];
+ unsigned char dirparent[3];
+ char dirtitle[19];
+ char dirname[10];
+ unsigned char endmasseq;
+ unsigned char endname[4];
+ unsigned char dircheckbyte;
+ } new;
+};
+
+#endif
diff --git a/fs/adfs/dir_fplus.c b/fs/adfs/dir_fplus.c
new file mode 100644
index 00000000000000..71064bc55150cb
--- /dev/null
+++ b/fs/adfs/dir_fplus.c
@@ -0,0 +1,181 @@
+/*
+ * linux/fs/adfs/dir_fplus.c
+ *
+ * Copyright (C) 1997-1999 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/adfs_fs.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/spinlock.h>
+
+#include "adfs.h"
+#include "dir_fplus.h"
+
+static int
+adfs_fplus_read(struct super_block *sb, unsigned int id, unsigned int sz, struct adfs_dir *dir)
+{
+ struct adfs_bigdirheader *h;
+ struct adfs_bigdirtail *t;
+ unsigned long block;
+ unsigned int blk, size;
+ int i, ret = -EIO;
+
+ dir->nr_buffers = 0;
+
+ block = __adfs_block_map(sb, id, 0);
+ if (!block) {
+ adfs_error(sb, "dir object %X has a hole at offset 0", id);
+ goto out;
+ }
+
+ dir->bh[0] = sb_bread(sb, block);
+ if (!dir->bh[0])
+ goto out;
+ dir->nr_buffers += 1;
+
+ h = (struct adfs_bigdirheader *)dir->bh[0]->b_data;
+ size = le32_to_cpu(h->bigdirsize);
+ if (size != sz) {
+ printk(KERN_WARNING "adfs: adfs_fplus_read: directory header size\n"
+ " does not match directory size\n");
+ }
+
+ if (h->bigdirversion[0] != 0 || h->bigdirversion[1] != 0 ||
+ h->bigdirversion[2] != 0 || size & 2047 ||
+ h->bigdirstartname != cpu_to_le32(BIGDIRSTARTNAME))
+ goto out;
+
+ size >>= sb->s_blocksize_bits;
+ for (blk = 1; blk < size; blk++) {
+ block = __adfs_block_map(sb, id, blk);
+ if (!block) {
+ adfs_error(sb, "dir object %X has a hole at offset %d", id, blk);
+ goto out;
+ }
+
+ dir->bh[blk] = sb_bread(sb, block);
+ if (!dir->bh[blk])
+ goto out;
+ dir->nr_buffers = blk;
+ }
+
+ t = (struct adfs_bigdirtail *)(dir->bh[size - 1]->b_data + (sb->s_blocksize - 8));
+
+ if (t->bigdirendname != cpu_to_le32(BIGDIRENDNAME) ||
+ t->bigdirendmasseq != h->startmasseq ||
+ t->reserved[0] != 0 || t->reserved[1] != 0)
+ goto out;
+
+ dir->parent_id = le32_to_cpu(h->bigdirparent);
+ dir->sb = sb;
+ return 0;
+out:
+ for (i = 0; i < dir->nr_buffers; i++)
+ brelse(dir->bh[i]);
+ dir->sb = NULL;
+ return ret;
+}
+
+static int
+adfs_fplus_setpos(struct adfs_dir *dir, unsigned int fpos)
+{
+ struct adfs_bigdirheader *h = (struct adfs_bigdirheader *)dir->bh[0]->b_data;
+ int ret = -ENOENT;
+
+ if (fpos <= le32_to_cpu(h->bigdirentries)) {
+ dir->pos = fpos;
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static void
+dir_memcpy(struct adfs_dir *dir, unsigned int offset, void *to, int len)
+{
+ struct super_block *sb = dir->sb;
+ unsigned int buffer, partial, remainder;
+
+ buffer = offset >> sb->s_blocksize_bits;
+ offset &= sb->s_blocksize - 1;
+
+ partial = sb->s_blocksize - offset;
+
+ if (partial >= len)
+ memcpy(to, dir->bh[buffer]->b_data + offset, len);
+ else {
+ char *c = (char *)to;
+
+ remainder = len - partial;
+
+ memcpy(c, dir->bh[buffer]->b_data + offset, partial);
+ memcpy(c + partial, dir->bh[buffer + 1]->b_data, remainder);
+ }
+}
+
+static int
+adfs_fplus_getnext(struct adfs_dir *dir, struct object_info *obj)
+{
+ struct adfs_bigdirheader *h = (struct adfs_bigdirheader *)dir->bh[0]->b_data;
+ struct adfs_bigdirentry bde;
+ unsigned int offset;
+ int i, ret = -ENOENT;
+
+ if (dir->pos >= le32_to_cpu(h->bigdirentries))
+ goto out;
+
+ offset = offsetof(struct adfs_bigdirheader, bigdirname);
+ offset += ((le32_to_cpu(h->bigdirnamelen) + 4) & ~3);
+ offset += dir->pos * sizeof(struct adfs_bigdirentry);
+
+ dir_memcpy(dir, offset, &bde, sizeof(struct adfs_bigdirentry));
+
+ obj->loadaddr = le32_to_cpu(bde.bigdirload);
+ obj->execaddr = le32_to_cpu(bde.bigdirexec);
+ obj->size = le32_to_cpu(bde.bigdirlen);
+ obj->file_id = le32_to_cpu(bde.bigdirindaddr);
+ obj->attr = le32_to_cpu(bde.bigdirattr);
+ obj->name_len = le32_to_cpu(bde.bigdirobnamelen);
+
+ offset = offsetof(struct adfs_bigdirheader, bigdirname);
+ offset += ((le32_to_cpu(h->bigdirnamelen) + 4) & ~3);
+ offset += le32_to_cpu(h->bigdirentries) * sizeof(struct adfs_bigdirentry);
+ offset += le32_to_cpu(bde.bigdirobnameptr);
+
+ dir_memcpy(dir, offset, obj->name, obj->name_len);
+ for (i = 0; i < obj->name_len; i++)
+ if (obj->name[i] == '/')
+ obj->name[i] = '.';
+
+ dir->pos += 1;
+ ret = 0;
+out:
+ return ret;
+}
+
+static void
+adfs_fplus_free(struct adfs_dir *dir)
+{
+ int i;
+
+ for (i = 0; i < dir->nr_buffers; i++)
+ brelse(dir->bh[i]);
+ dir->sb = NULL;
+}
+
+struct adfs_dir_ops adfs_fplus_dir_ops = {
+ adfs_fplus_read,
+ adfs_fplus_setpos,
+ adfs_fplus_getnext,
+ NULL,
+ NULL,
+ NULL,
+ adfs_fplus_free
+};
diff --git a/fs/adfs/dir_fplus.h b/fs/adfs/dir_fplus.h
new file mode 100644
index 00000000000000..a65f65348d0c39
--- /dev/null
+++ b/fs/adfs/dir_fplus.h
@@ -0,0 +1,45 @@
+/*
+ * linux/fs/adfs/dir_fplus.h
+ *
+ * Copyright (C) 1999 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Structures of directories on the F+ format disk
+ */
+
+#define ADFS_FPLUS_NAME_LEN 255
+
+#define BIGDIRSTARTNAME ('S' | 'B' << 8 | 'P' << 16 | 'r' << 24)
+#define BIGDIRENDNAME ('o' | 'v' << 8 | 'e' << 16 | 'n' << 24)
+
+struct adfs_bigdirheader {
+ __u8 startmasseq;
+ __u8 bigdirversion[3];
+ __u32 bigdirstartname;
+ __u32 bigdirnamelen;
+ __u32 bigdirsize;
+ __u32 bigdirentries;
+ __u32 bigdirnamesize;
+ __u32 bigdirparent;
+ char bigdirname[1];
+};
+
+struct adfs_bigdirentry {
+ __u32 bigdirload;
+ __u32 bigdirexec;
+ __u32 bigdirlen;
+ __u32 bigdirindaddr;
+ __u32 bigdirattr;
+ __u32 bigdirobnamelen;
+ __u32 bigdirobnameptr;
+};
+
+struct adfs_bigdirtail {
+ __u32 bigdirendname;
+ __u8 bigdirendmasseq;
+ __u8 reserved[2];
+ __u8 bigdircheckbyte;
+};
diff --git a/fs/adfs/file.c b/fs/adfs/file.c
new file mode 100644
index 00000000000000..c00ebea8b14c50
--- /dev/null
+++ b/fs/adfs/file.c
@@ -0,0 +1,41 @@
+/*
+ * linux/fs/adfs/file.c
+ *
+ * Copyright (C) 1997-1999 Russell King
+ * from:
+ *
+ * linux/fs/ext2/file.c
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * from
+ *
+ * linux/fs/minix/file.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * adfs regular file handling primitives
+ */
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/fcntl.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+
+#include "adfs.h"
+
+struct file_operations adfs_file_operations = {
+ llseek: generic_file_llseek,
+ read: generic_file_read,
+ mmap: generic_file_mmap,
+ fsync: file_fsync,
+ write: generic_file_write,
+};
+
+struct inode_operations adfs_file_inode_operations = {
+ setattr: adfs_notify_change,
+};
diff --git a/fs/adfs/inode.c b/fs/adfs/inode.c
new file mode 100644
index 00000000000000..22d9bfd2504979
--- /dev/null
+++ b/fs/adfs/inode.c
@@ -0,0 +1,378 @@
+/*
+ * linux/fs/adfs/inode.c
+ *
+ * Copyright (C) 1997-1999 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/adfs_fs.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/mm.h>
+#include <linux/smp_lock.h>
+#include <linux/module.h>
+
+
+#include "adfs.h"
+
+/*
+ * Lookup/Create a block at offset 'block' into 'inode'. We currently do
+ * not support creation of new blocks, so we return -EIO for this case.
+ */
+int
+adfs_get_block(struct inode *inode, long block, struct buffer_head *bh, int create)
+{
+ if (block < 0)
+ goto abort_negative;
+
+ if (!create) {
+ if (block >= inode->i_blocks)
+ goto abort_toobig;
+
+ block = __adfs_block_map(inode->i_sb, inode->i_ino, block);
+ if (block) {
+ bh->b_dev = inode->i_dev;
+ bh->b_blocknr = block;
+ bh->b_state |= (1UL << BH_Mapped);
+ }
+ return 0;
+ }
+ /* don't support allocation of blocks yet */
+ return -EIO;
+
+abort_negative:
+ adfs_error(inode->i_sb, "block %d < 0", block);
+ return -EIO;
+
+abort_toobig:
+ return 0;
+}
+
+static int adfs_writepage(struct page *page)
+{
+ return block_write_full_page(page, adfs_get_block);
+}
+
+static int adfs_readpage(struct file *file, struct page *page)
+{
+ return block_read_full_page(page, adfs_get_block);
+}
+
+static int adfs_prepare_write(struct file *file, struct page *page, unsigned int from, unsigned int to)
+{
+ return cont_prepare_write(page, from, to, adfs_get_block,
+ &page->mapping->host->u.adfs_i.mmu_private);
+}
+
+static int _adfs_bmap(struct address_space *mapping, long block)
+{
+ return generic_block_bmap(mapping, block, adfs_get_block);
+}
+
+static struct address_space_operations adfs_aops = {
+ readpage: adfs_readpage,
+ writepage: adfs_writepage,
+ sync_page: block_sync_page,
+ prepare_write: adfs_prepare_write,
+ commit_write: generic_commit_write,
+ bmap: _adfs_bmap
+};
+
+static inline unsigned int
+adfs_filetype(struct inode *inode)
+{
+ unsigned int type;
+
+ if (inode->u.adfs_i.stamped)
+ type = (inode->u.adfs_i.loadaddr >> 8) & 0xfff;
+ else
+ type = (unsigned int) -1;
+
+ return type;
+}
+
+/*
+ * Convert ADFS attributes and filetype to Linux permission.
+ */
+static umode_t
+adfs_atts2mode(struct super_block *sb, struct inode *inode)
+{
+ unsigned int filetype, attr = inode->u.adfs_i.attr;
+ umode_t mode, rmask;
+
+ if (attr & ADFS_NDA_DIRECTORY) {
+ mode = S_IRUGO & sb->u.adfs_sb.s_owner_mask;
+ return S_IFDIR | S_IXUGO | mode;
+ }
+
+ filetype = adfs_filetype(inode);
+
+ switch (filetype) {
+ case 0xfc0: /* LinkFS */
+ return S_IFLNK|S_IRWXUGO;
+
+ case 0xfe6: /* UnixExec */
+ rmask = S_IRUGO | S_IXUGO;
+ break;
+
+ default:
+ rmask = S_IRUGO;
+ }
+
+ mode = S_IFREG;
+
+ if (attr & ADFS_NDA_OWNER_READ)
+ mode |= rmask & sb->u.adfs_sb.s_owner_mask;
+
+ if (attr & ADFS_NDA_OWNER_WRITE)
+ mode |= S_IWUGO & sb->u.adfs_sb.s_owner_mask;
+
+ if (attr & ADFS_NDA_PUBLIC_READ)
+ mode |= rmask & sb->u.adfs_sb.s_other_mask;
+
+ if (attr & ADFS_NDA_PUBLIC_WRITE)
+ mode |= S_IWUGO & sb->u.adfs_sb.s_other_mask;
+ return mode;
+}
+
+/*
+ * Convert Linux permission to ADFS attribute. We try to do the reverse
+ * of atts2mode, but there is not a 1:1 translation.
+ */
+static int
+adfs_mode2atts(struct super_block *sb, struct inode *inode)
+{
+ umode_t mode;
+ int attr;
+
+ /* FIXME: should we be able to alter a link? */
+ if (S_ISLNK(inode->i_mode))
+ return inode->u.adfs_i.attr;
+
+ if (S_ISDIR(inode->i_mode))
+ attr = ADFS_NDA_DIRECTORY;
+ else
+ attr = 0;
+
+ mode = inode->i_mode & sb->u.adfs_sb.s_owner_mask;
+ if (mode & S_IRUGO)
+ attr |= ADFS_NDA_OWNER_READ;
+ if (mode & S_IWUGO)
+ attr |= ADFS_NDA_OWNER_WRITE;
+
+ mode = inode->i_mode & sb->u.adfs_sb.s_other_mask;
+ mode &= ~sb->u.adfs_sb.s_owner_mask;
+ if (mode & S_IRUGO)
+ attr |= ADFS_NDA_PUBLIC_READ;
+ if (mode & S_IWUGO)
+ attr |= ADFS_NDA_PUBLIC_WRITE;
+
+ return attr;
+}
+
+/*
+ * Convert an ADFS time to Unix time. ADFS has a 40-bit centi-second time
+ * referenced to 1 Jan 1900 (til 2248)
+ */
+static unsigned int
+adfs_adfs2unix_time(struct inode *inode)
+{
+ unsigned int high, low;
+
+ if (inode->u.adfs_i.stamped == 0)
+ return CURRENT_TIME;
+
+ high = inode->u.adfs_i.loadaddr << 24;
+ low = inode->u.adfs_i.execaddr;
+
+ high |= low >> 8;
+ low &= 255;
+
+ /* Files dated pre 01 Jan 1970 00:00:00. */
+ if (high < 0x336e996a)
+ return 0;
+
+ /* Files dated post 18 Jan 2038 03:14:05. */
+ if (high >= 0x656e9969)
+ return 0x7ffffffd;
+
+ /* discard 2208988800 (0x336e996a00) seconds of time */
+ high -= 0x336e996a;
+
+ /* convert 40-bit centi-seconds to 32-bit seconds */
+ return (((high % 100) << 8) + low) / 100 + (high / 100 << 8);
+}
+
+/*
+ * Convert an Unix time to ADFS time. We only do this if the entry has a
+ * time/date stamp already.
+ */
+static void
+adfs_unix2adfs_time(struct inode *inode, unsigned int secs)
+{
+ unsigned int high, low;
+
+ if (inode->u.adfs_i.stamped) {
+ /* convert 32-bit seconds to 40-bit centi-seconds */
+ low = (secs & 255) * 100;
+ high = (secs / 256) * 100 + (low >> 8) + 0x336e996a;
+
+ inode->u.adfs_i.loadaddr = (high >> 24) |
+ (inode->u.adfs_i.loadaddr & ~0xff);
+ inode->u.adfs_i.execaddr = (low & 255) | (high << 8);
+ }
+}
+
+/*
+ * Fill in the inode information from the object information.
+ *
+ * Note that this is an inode-less filesystem, so we can't use the inode
+ * number to reference the metadata on the media. Instead, we use the
+ * inode number to hold the object ID, which in turn will tell us where
+ * the data is held. We also save the parent object ID, and with these
+ * two, we can locate the metadata.
+ *
+ * This does mean that we rely on an objects parent remaining the same at
+ * all times - we cannot cope with a cross-directory rename (yet).
+ */
+struct inode *
+adfs_iget(struct super_block *sb, struct object_info *obj)
+{
+ struct inode *inode;
+
+ inode = new_inode(sb);
+ if (!inode)
+ goto out;
+
+ inode->i_version = ++event;
+ inode->i_uid = sb->u.adfs_sb.s_uid;
+ inode->i_gid = sb->u.adfs_sb.s_gid;
+ inode->i_ino = obj->file_id;
+ inode->i_size = obj->size;
+ inode->i_nlink = 2;
+ inode->i_blksize = PAGE_SIZE;
+ inode->i_blocks = (inode->i_size + sb->s_blocksize - 1) >>
+ sb->s_blocksize_bits;
+
+ /*
+ * we need to save the parent directory ID so that
+ * write_inode can update the directory information
+ * for this file. This will need special handling
+ * for cross-directory renames.
+ */
+ inode->u.adfs_i.parent_id = obj->parent_id;
+ inode->u.adfs_i.loadaddr = obj->loadaddr;
+ inode->u.adfs_i.execaddr = obj->execaddr;
+ inode->u.adfs_i.attr = obj->attr;
+ inode->u.adfs_i.stamped = ((obj->loadaddr & 0xfff00000) == 0xfff00000);
+
+ inode->i_mode = adfs_atts2mode(sb, inode);
+ inode->i_mtime =
+ inode->i_atime =
+ inode->i_ctime = adfs_adfs2unix_time(inode);
+
+ if (S_ISDIR(inode->i_mode)) {
+ inode->i_op = &adfs_dir_inode_operations;
+ inode->i_fop = &adfs_dir_operations;
+ } else if (S_ISREG(inode->i_mode)) {
+ inode->i_op = &adfs_file_inode_operations;
+ inode->i_fop = &adfs_file_operations;
+ inode->i_mapping->a_ops = &adfs_aops;
+ inode->u.adfs_i.mmu_private = inode->i_size;
+ }
+
+ insert_inode_hash(inode);
+
+out:
+ return inode;
+}
+
+/*
+ * Validate and convert a changed access mode/time to their ADFS equivalents.
+ * adfs_write_inode will actually write the information back to the directory
+ * later.
+ */
+int
+adfs_notify_change(struct dentry *dentry, struct iattr *attr)
+{
+ struct inode *inode = dentry->d_inode;
+ struct super_block *sb = inode->i_sb;
+ unsigned int ia_valid = attr->ia_valid;
+ int error;
+
+ error = inode_change_ok(inode, attr);
+
+ /*
+ * we can't change the UID or GID of any file -
+ * we have a global UID/GID in the superblock
+ */
+ if ((ia_valid & ATTR_UID && attr->ia_uid != sb->u.adfs_sb.s_uid) ||
+ (ia_valid & ATTR_GID && attr->ia_gid != sb->u.adfs_sb.s_gid))
+ error = -EPERM;
+
+ if (error)
+ goto out;
+
+ if (ia_valid & ATTR_SIZE)
+ error = vmtruncate(inode, attr->ia_size);
+
+ if (error)
+ goto out;
+
+ if (ia_valid & ATTR_MTIME) {
+ inode->i_mtime = attr->ia_mtime;
+ adfs_unix2adfs_time(inode, attr->ia_mtime);
+ }
+ /*
+ * FIXME: should we make these == to i_mtime since we don't
+ * have the ability to represent them in our filesystem?
+ */
+ if (ia_valid & ATTR_ATIME)
+ inode->i_atime = attr->ia_atime;
+ if (ia_valid & ATTR_CTIME)
+ inode->i_ctime = attr->ia_ctime;
+ if (ia_valid & ATTR_MODE) {
+ inode->u.adfs_i.attr = adfs_mode2atts(sb, inode);
+ inode->i_mode = adfs_atts2mode(sb, inode);
+ }
+
+ /*
+ * FIXME: should we be marking this inode dirty even if
+ * we don't have any metadata to write back?
+ */
+ if (ia_valid & (ATTR_SIZE | ATTR_MTIME | ATTR_MODE))
+ mark_inode_dirty(inode);
+out:
+ return error;
+}
+
+/*
+ * write an existing inode back to the directory, and therefore the disk.
+ * The adfs-specific inode data has already been updated by
+ * adfs_notify_change()
+ */
+void adfs_write_inode(struct inode *inode, int unused)
+{
+ struct super_block *sb = inode->i_sb;
+ struct object_info obj;
+
+ lock_kernel();
+ obj.file_id = inode->i_ino;
+ obj.name_len = 0;
+ obj.parent_id = inode->u.adfs_i.parent_id;
+ obj.loadaddr = inode->u.adfs_i.loadaddr;
+ obj.execaddr = inode->u.adfs_i.execaddr;
+ obj.attr = inode->u.adfs_i.attr;
+ obj.size = inode->i_size;
+
+ adfs_dir_update(sb, &obj);
+ unlock_kernel();
+}
+MODULE_LICENSE("GPL");
diff --git a/fs/adfs/map.c b/fs/adfs/map.c
new file mode 100644
index 00000000000000..ce36fcd2512434
--- /dev/null
+++ b/fs/adfs/map.c
@@ -0,0 +1,256 @@
+/*
+ * linux/fs/adfs/map.c
+ *
+ * Copyright (C) 1997-1999 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/adfs_fs.h>
+#include <linux/spinlock.h>
+
+#include "adfs.h"
+
+/*
+ * For the future...
+ */
+static rwlock_t adfs_map_lock;
+
+#define GET_FRAG_ID(_map,_start,_idmask) \
+ ({ \
+ unsigned long _v2, _frag; \
+ unsigned int _tmp; \
+ _tmp = _start >> 5; \
+ _frag = le32_to_cpu(_map[_tmp]); \
+ _v2 = le32_to_cpu(_map[_tmp + 1]); \
+ _tmp = start & 31; \
+ _frag = (_frag >> _tmp) | (_v2 << (32 - _tmp)); \
+ _frag & _idmask; \
+ })
+
+/*
+ * return the map bit offset of the fragment frag_id in
+ * the zone dm.
+ * Note that the loop is optimised for best asm code -
+ * look at the output of:
+ * gcc -D__KERNEL__ -O2 -I../../include -o - -S map.c
+ */
+static int
+lookup_zone(const struct adfs_discmap *dm, const unsigned int idlen,
+ const unsigned int frag_id, unsigned int *offset)
+{
+ const unsigned int mapsize = dm->dm_endbit;
+ const unsigned int idmask = (1 << idlen) - 1;
+ unsigned long *map = ((unsigned long *)dm->dm_bh->b_data) + 1;
+ unsigned int start = dm->dm_startbit;
+ unsigned int mapptr;
+
+ do {
+ unsigned long frag;
+
+ frag = GET_FRAG_ID(map, start, idmask);
+ mapptr = start + idlen;
+
+ /*
+ * find end of fragment
+ */
+ {
+ unsigned long v2;
+
+ while ((v2 = map[mapptr >> 5] >> (mapptr & 31)) == 0) {
+ mapptr = (mapptr & ~31) + 32;
+ if (mapptr >= mapsize)
+ goto error;
+ }
+
+ mapptr += 1 + ffz(~v2);
+ }
+
+ if (frag == frag_id)
+ goto found;
+again:
+ start = mapptr;
+ } while (mapptr < mapsize);
+
+error:
+ return -1;
+
+found:
+ {
+ int length = mapptr - start;
+ if (*offset >= length) {
+ *offset -= length;
+ goto again;
+ }
+ }
+ return start + *offset;
+}
+
+/*
+ * Scan the free space map, for this zone, calculating the total
+ * number of map bits in each free space fragment.
+ *
+ * Note: idmask is limited to 15 bits [3.2]
+ */
+static unsigned int
+scan_free_map(struct adfs_sb_info *asb, struct adfs_discmap *dm)
+{
+ const unsigned int mapsize = dm->dm_endbit + 32;
+ const unsigned int idlen = asb->s_idlen;
+ const unsigned int frag_idlen = idlen <= 15 ? idlen : 15;
+ const unsigned int idmask = (1 << frag_idlen) - 1;
+ unsigned long *map = (unsigned long *)dm->dm_bh->b_data;
+ unsigned int start = 8, mapptr;
+ unsigned long frag;
+ unsigned long total = 0;
+
+ /*
+ * get fragment id
+ */
+ frag = GET_FRAG_ID(map, start, idmask);
+
+ /*
+ * If the freelink is null, then no free fragments
+ * exist in this zone.
+ */
+ if (frag == 0)
+ return 0;
+
+ do {
+ start += frag;
+
+ /*
+ * get fragment id
+ */
+ frag = GET_FRAG_ID(map, start, idmask);
+ mapptr = start + idlen;
+
+ /*
+ * find end of fragment
+ */
+ {
+ unsigned long v2;
+
+ while ((v2 = map[mapptr >> 5] >> (mapptr & 31)) == 0) {
+ mapptr = (mapptr & ~31) + 32;
+ if (mapptr >= mapsize)
+ goto error;
+ }
+
+ mapptr += 1 + ffz(~v2);
+ }
+
+ total += mapptr - start;
+ } while (frag >= idlen + 1);
+
+ if (frag != 0)
+ printk(KERN_ERR "adfs: undersized free fragment\n");
+
+ return total;
+error:
+ printk(KERN_ERR "adfs: oversized free fragment\n");
+ return 0;
+}
+
+static int
+scan_map(struct adfs_sb_info *asb, unsigned int zone,
+ const unsigned int frag_id, unsigned int mapoff)
+{
+ const unsigned int idlen = asb->s_idlen;
+ struct adfs_discmap *dm, *dm_end;
+ int result;
+
+ dm = asb->s_map + zone;
+ zone = asb->s_map_size;
+ dm_end = asb->s_map + zone;
+
+ do {
+ result = lookup_zone(dm, idlen, frag_id, &mapoff);
+
+ if (result != -1)
+ goto found;
+
+ dm ++;
+ if (dm == dm_end)
+ dm = asb->s_map;
+ } while (--zone > 0);
+
+ return -1;
+found:
+ result -= dm->dm_startbit;
+ result += dm->dm_startblk;
+
+ return result;
+}
+
+/*
+ * calculate the amount of free blocks in the map.
+ *
+ * n=1
+ * total_free = E(free_in_zone_n)
+ * nzones
+ */
+unsigned int
+adfs_map_free(struct super_block *sb)
+{
+ struct adfs_sb_info *asb = &sb->u.adfs_sb;
+ struct adfs_discmap *dm;
+ unsigned int total = 0;
+ unsigned int zone;
+
+ dm = asb->s_map;
+ zone = asb->s_map_size;
+
+ do {
+ total += scan_free_map(asb, dm++);
+ } while (--zone > 0);
+
+ return signed_asl(total, asb->s_map2blk);
+}
+
+int adfs_map_lookup (struct super_block *sb, int frag_id, int offset)
+{
+ struct adfs_sb_info *asb = &sb->u.adfs_sb;
+ unsigned int zone, mapoff;
+ int result;
+
+ /*
+ * map & root fragment is special - it starts in the center of the
+ * disk. The other fragments start at zone (frag / ids_per_zone)
+ */
+ if (frag_id == ADFS_ROOT_FRAG)
+ zone = asb->s_map_size >> 1;
+ else
+ zone = frag_id / asb->s_ids_per_zone;
+
+ if (zone >= asb->s_map_size)
+ goto bad_fragment;
+
+ /* Convert sector offset to map offset */
+ mapoff = signed_asl(offset, -asb->s_map2blk);
+
+ read_lock(&adfs_map_lock);
+ result = scan_map(asb, zone, frag_id, mapoff);
+ read_unlock(&adfs_map_lock);
+
+ if (result > 0) {
+ unsigned int secoff;
+
+ /* Calculate sector offset into map block */
+ secoff = offset - signed_asl(mapoff, asb->s_map2blk);
+ return secoff + signed_asl(result, asb->s_map2blk);
+ }
+
+ adfs_error(sb, "fragment %04X at offset %d not found in map",
+ frag_id, offset);
+ return 0;
+
+bad_fragment:
+ adfs_error(sb, "fragment %X is invalid (zone = %d, max = %d)",
+ frag_id, zone, asb->s_map_size);
+ return 0;
+}
diff --git a/fs/adfs/super.c b/fs/adfs/super.c
new file mode 100644
index 00000000000000..f1af56308a1ed7
--- /dev/null
+++ b/fs/adfs/super.c
@@ -0,0 +1,458 @@
+/*
+ * linux/fs/adfs/super.c
+ *
+ * Copyright (C) 1997-1999 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/adfs_fs.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/init.h>
+
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#include <stdarg.h>
+
+#include "adfs.h"
+#include "dir_f.h"
+#include "dir_fplus.h"
+
+void __adfs_error(struct super_block *sb, const char *function, const char *fmt, ...)
+{
+ char error_buf[128];
+ va_list args;
+
+ va_start(args, fmt);
+ vsprintf(error_buf, fmt, args);
+ va_end(args);
+
+ printk(KERN_CRIT "ADFS-fs error (device %s)%s%s: %s\n",
+ bdevname(sb->s_dev), function ? ": " : "",
+ function ? function : "", error_buf);
+}
+
+static int adfs_checkdiscrecord(struct adfs_discrecord *dr)
+{
+ int i;
+
+ /* sector size must be 256, 512 or 1024 bytes */
+ if (dr->log2secsize != 8 &&
+ dr->log2secsize != 9 &&
+ dr->log2secsize != 10)
+ return 1;
+
+ /* idlen must be at least log2secsize + 3 */
+ if (dr->idlen < dr->log2secsize + 3)
+ return 1;
+
+ /* we cannot have such a large disc that we
+ * are unable to represent sector offsets in
+ * 32 bits. This works out at 2.0 TB.
+ */
+ if (dr->disc_size_high >> dr->log2secsize)
+ return 1;
+
+ /*
+ * The following checks are not required for F+
+ * stage 1.
+ */
+#if 0
+ /* idlen must be smaller be no greater than 15 */
+ if (dr->idlen > 15)
+ return 1;
+
+ /* nzones must be less than 128 for the root
+ * directory to be addressable
+ */
+ if (dr->nzones >= 128 && dr->nzones_high == 0)
+ return 1;
+
+ /* root must be of the form 0x2.. */
+ if ((le32_to_cpu(dr->root) & 0xffffff00) != 0x00000200)
+ return 1;
+#else
+ /*
+ * Stage 2 F+ does not require the following check
+ */
+#if 0
+ /* idlen must be no greater than 16 v2 [1.0] */
+ if (dr->idlen > 16)
+ return 1;
+
+ /* we can't handle F+ discs yet */
+ if (dr->format_version || dr->root_size)
+ return 1;
+
+#else
+ /* idlen must be no greater than 19 v2 [1.0] */
+ if (dr->idlen > 19)
+ return 1;
+#endif
+#endif
+
+ /* reserved bytes should be zero */
+ for (i = 0; i < sizeof(dr->unused52); i++)
+ if (dr->unused52[i] != 0)
+ return 1;
+
+ return 0;
+}
+
+static unsigned char adfs_calczonecheck(struct super_block *sb, unsigned char *map)
+{
+ unsigned int v0, v1, v2, v3;
+ int i;
+
+ v0 = v1 = v2 = v3 = 0;
+ for (i = sb->s_blocksize - 4; i; i -= 4) {
+ v0 += map[i] + (v3 >> 8);
+ v3 &= 0xff;
+ v1 += map[i + 1] + (v0 >> 8);
+ v0 &= 0xff;
+ v2 += map[i + 2] + (v1 >> 8);
+ v1 &= 0xff;
+ v3 += map[i + 3] + (v2 >> 8);
+ v2 &= 0xff;
+ }
+ v0 += v3 >> 8;
+ v1 += map[1] + (v0 >> 8);
+ v2 += map[2] + (v1 >> 8);
+ v3 += map[3] + (v2 >> 8);
+
+ return v0 ^ v1 ^ v2 ^ v3;
+}
+
+static int adfs_checkmap(struct super_block *sb, struct adfs_discmap *dm)
+{
+ unsigned char crosscheck = 0, zonecheck = 1;
+ int i;
+
+ for (i = 0; i < sb->u.adfs_sb.s_map_size; i++) {
+ unsigned char *map;
+
+ map = dm[i].dm_bh->b_data;
+
+ if (adfs_calczonecheck(sb, map) != map[0]) {
+ adfs_error(sb, "zone %d fails zonecheck", i);
+ zonecheck = 0;
+ }
+ crosscheck ^= map[3];
+ }
+ if (crosscheck != 0xff)
+ adfs_error(sb, "crosscheck != 0xff");
+ return crosscheck == 0xff && zonecheck;
+}
+
+static void adfs_put_super(struct super_block *sb)
+{
+ int i;
+
+ for (i = 0; i < sb->u.adfs_sb.s_map_size; i++)
+ brelse(sb->u.adfs_sb.s_map[i].dm_bh);
+ kfree(sb->u.adfs_sb.s_map);
+}
+
+static int parse_options(struct super_block *sb, char *options)
+{
+ char *value, *opt;
+
+ if (!options)
+ return 0;
+
+ for (opt = strtok(options, ","); opt != NULL; opt = strtok(NULL, ",")) {
+ value = strchr(opt, '=');
+ if (value)
+ *value++ = '\0';
+
+ if (!strcmp(opt, "uid")) { /* owner of all files */
+ if (!value || !*value)
+ return -EINVAL;
+ sb->u.adfs_sb.s_uid = simple_strtoul(value, &value, 0);
+ if (*value)
+ return -EINVAL;
+ } else
+ if (!strcmp(opt, "gid")) { /* group owner of all files */
+ if (!value || !*value)
+ return -EINVAL;
+ sb->u.adfs_sb.s_gid = simple_strtoul(value, &value, 0);
+ if (*value)
+ return -EINVAL;
+ } else
+ if (!strcmp(opt, "ownmask")) { /* owner permission mask */
+ if (!value || !*value)
+ return -EINVAL;
+ sb->u.adfs_sb.s_owner_mask = simple_strtoul(value, &value, 8);
+ if (*value)
+ return -EINVAL;
+ } else
+ if (!strcmp(opt, "othmask")) { /* others permission mask */
+ if (!value || !*value)
+ return -EINVAL;
+ sb->u.adfs_sb.s_other_mask = simple_strtoul(value, &value, 8);
+ if (*value)
+ return -EINVAL;
+ } else { /* eh? say again. */
+ printk("ADFS-fs: unrecognised mount option %s\n", opt);
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+static int adfs_remount(struct super_block *sb, int *flags, char *data)
+{
+ return parse_options(sb, data);
+}
+
+static int adfs_statfs(struct super_block *sb, struct statfs *buf)
+{
+ struct adfs_sb_info *asb = &sb->u.adfs_sb;
+
+ buf->f_type = ADFS_SUPER_MAGIC;
+ buf->f_namelen = asb->s_namelen;
+ buf->f_bsize = sb->s_blocksize;
+ buf->f_blocks = asb->s_size;
+ buf->f_files = asb->s_ids_per_zone * asb->s_map_size;
+ buf->f_bavail =
+ buf->f_bfree = adfs_map_free(sb);
+ buf->f_ffree = buf->f_bfree * buf->f_files / buf->f_blocks;
+
+ return 0;
+}
+
+static struct super_operations adfs_sops = {
+ write_inode: adfs_write_inode,
+ put_super: adfs_put_super,
+ statfs: adfs_statfs,
+ remount_fs: adfs_remount,
+};
+
+static struct adfs_discmap *adfs_read_map(struct super_block *sb, struct adfs_discrecord *dr)
+{
+ struct adfs_discmap *dm;
+ unsigned int map_addr, zone_size, nzones;
+ int i, zone;
+
+ nzones = sb->u.adfs_sb.s_map_size;
+ zone_size = (8 << dr->log2secsize) - le16_to_cpu(dr->zone_spare);
+ map_addr = (nzones >> 1) * zone_size -
+ ((nzones > 1) ? ADFS_DR_SIZE_BITS : 0);
+ map_addr = signed_asl(map_addr, sb->u.adfs_sb.s_map2blk);
+
+ sb->u.adfs_sb.s_ids_per_zone = zone_size / (sb->u.adfs_sb.s_idlen + 1);
+
+ dm = kmalloc(nzones * sizeof(*dm), GFP_KERNEL);
+ if (dm == NULL) {
+ adfs_error(sb, "not enough memory");
+ return NULL;
+ }
+
+ for (zone = 0; zone < nzones; zone++, map_addr++) {
+ dm[zone].dm_startbit = 0;
+ dm[zone].dm_endbit = zone_size;
+ dm[zone].dm_startblk = zone * zone_size - ADFS_DR_SIZE_BITS;
+ dm[zone].dm_bh = sb_bread(sb, map_addr);
+
+ if (!dm[zone].dm_bh) {
+ adfs_error(sb, "unable to read map");
+ goto error_free;
+ }
+ }
+
+ /* adjust the limits for the first and last map zones */
+ i = zone - 1;
+ dm[0].dm_startblk = 0;
+ dm[0].dm_startbit = ADFS_DR_SIZE_BITS;
+ dm[i].dm_endbit = (dr->disc_size_high << (32 - dr->log2bpmb)) +
+ (dr->disc_size >> dr->log2bpmb) +
+ (ADFS_DR_SIZE_BITS - i * zone_size);
+
+ if (adfs_checkmap(sb, dm))
+ return dm;
+
+ adfs_error(sb, NULL, "map corrupted");
+
+error_free:
+ while (--zone >= 0)
+ brelse(dm[zone].dm_bh);
+
+ kfree(dm);
+ return NULL;
+}
+
+static inline unsigned long adfs_discsize(struct adfs_discrecord *dr, int block_bits)
+{
+ unsigned long discsize;
+
+ discsize = le32_to_cpu(dr->disc_size_high) << (32 - block_bits);
+ discsize |= le32_to_cpu(dr->disc_size) >> block_bits;
+
+ return discsize;
+}
+
+struct super_block *adfs_read_super(struct super_block *sb, void *data, int silent)
+{
+ struct adfs_discrecord *dr;
+ struct buffer_head *bh;
+ struct object_info root_obj;
+ unsigned char *b_data;
+ kdev_t dev = sb->s_dev;
+
+ /* set default options */
+ sb->u.adfs_sb.s_uid = 0;
+ sb->u.adfs_sb.s_gid = 0;
+ sb->u.adfs_sb.s_owner_mask = S_IRWXU;
+ sb->u.adfs_sb.s_other_mask = S_IRWXG | S_IRWXO;
+
+ if (parse_options(sb, data))
+ goto error;
+
+ sb->s_blocksize = BLOCK_SIZE;
+ set_blocksize(dev, BLOCK_SIZE);
+ if (!(bh = sb_bread(sb, ADFS_DISCRECORD / BLOCK_SIZE))) {
+ adfs_error(sb, "unable to read superblock");
+ goto error;
+ }
+
+ b_data = bh->b_data + (ADFS_DISCRECORD % BLOCK_SIZE);
+
+ if (adfs_checkbblk(b_data)) {
+ if (!silent)
+ printk("VFS: Can't find an adfs filesystem on dev "
+ "%s.\n", bdevname(dev));
+ goto error_free_bh;
+ }
+
+ dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET);
+
+ /*
+ * Do some sanity checks on the ADFS disc record
+ */
+ if (adfs_checkdiscrecord(dr)) {
+ if (!silent)
+ printk("VPS: Can't find an adfs filesystem on dev "
+ "%s.\n", bdevname(dev));
+ goto error_free_bh;
+ }
+
+ sb->s_blocksize_bits = dr->log2secsize;
+ sb->s_blocksize = 1 << sb->s_blocksize_bits;
+ if (sb->s_blocksize != BLOCK_SIZE &&
+ (sb->s_blocksize == 512 || sb->s_blocksize == 1024 ||
+ sb->s_blocksize == 2048 || sb->s_blocksize == 4096)) {
+
+ brelse(bh);
+ set_blocksize(dev, sb->s_blocksize);
+ bh = sb_bread(sb, ADFS_DISCRECORD / sb->s_blocksize);
+ if (!bh) {
+ adfs_error(sb, "couldn't read superblock on "
+ "2nd try.");
+ goto error;
+ }
+ b_data = bh->b_data + (ADFS_DISCRECORD % sb->s_blocksize);
+ if (adfs_checkbblk(b_data)) {
+ adfs_error(sb, "disc record mismatch, very weird!");
+ goto error_free_bh;
+ }
+ dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET);
+ }
+ if (sb->s_blocksize != bh->b_size) {
+ if (!silent)
+ printk(KERN_ERR "VFS: Unsupported blocksize on dev "
+ "%s.\n", bdevname(dev));
+ goto error_free_bh;
+ }
+
+ /*
+ * blocksize on this device should now be set to the ADFS log2secsize
+ */
+
+ sb->s_magic = ADFS_SUPER_MAGIC;
+ sb->u.adfs_sb.s_idlen = dr->idlen;
+ sb->u.adfs_sb.s_map_size = dr->nzones | (dr->nzones_high << 8);
+ sb->u.adfs_sb.s_map2blk = dr->log2bpmb - dr->log2secsize;
+ sb->u.adfs_sb.s_size = adfs_discsize(dr, sb->s_blocksize_bits);
+ sb->u.adfs_sb.s_version = dr->format_version;
+ sb->u.adfs_sb.s_log2sharesize = dr->log2sharesize;
+
+ sb->u.adfs_sb.s_map = adfs_read_map(sb, dr);
+ if (!sb->u.adfs_sb.s_map)
+ goto error_free_bh;
+
+ brelse(bh);
+
+ /*
+ * set up enough so that we can read an inode
+ */
+ sb->s_op = &adfs_sops;
+
+ dr = (struct adfs_discrecord *)(sb->u.adfs_sb.s_map[0].dm_bh->b_data + 4);
+
+ root_obj.parent_id = root_obj.file_id = le32_to_cpu(dr->root);
+ root_obj.name_len = 0;
+ root_obj.loadaddr = 0;
+ root_obj.execaddr = 0;
+ root_obj.size = ADFS_NEWDIR_SIZE;
+ root_obj.attr = ADFS_NDA_DIRECTORY | ADFS_NDA_OWNER_READ |
+ ADFS_NDA_OWNER_WRITE | ADFS_NDA_PUBLIC_READ;
+
+ /*
+ * If this is a F+ disk with variable length directories,
+ * get the root_size from the disc record.
+ */
+ if (sb->u.adfs_sb.s_version) {
+ root_obj.size = dr->root_size;
+ sb->u.adfs_sb.s_dir = &adfs_fplus_dir_ops;
+ sb->u.adfs_sb.s_namelen = ADFS_FPLUS_NAME_LEN;
+ } else {
+ sb->u.adfs_sb.s_dir = &adfs_f_dir_ops;
+ sb->u.adfs_sb.s_namelen = ADFS_F_NAME_LEN;
+ }
+
+ sb->s_root = d_alloc_root(adfs_iget(sb, &root_obj));
+ if (!sb->s_root) {
+ int i;
+
+ for (i = 0; i < sb->u.adfs_sb.s_map_size; i++)
+ brelse(sb->u.adfs_sb.s_map[i].dm_bh);
+ kfree(sb->u.adfs_sb.s_map);
+ adfs_error(sb, "get root inode failed\n");
+ goto error;
+ } else
+ sb->s_root->d_op = &adfs_dentry_operations;
+ return sb;
+
+error_free_bh:
+ brelse(bh);
+error:
+ return NULL;
+}
+
+static DECLARE_FSTYPE_DEV(adfs_fs_type, "adfs", adfs_read_super);
+
+static int __init init_adfs_fs(void)
+{
+ return register_filesystem(&adfs_fs_type);
+}
+
+static void __exit exit_adfs_fs(void)
+{
+ unregister_filesystem(&adfs_fs_type);
+}
+
+EXPORT_NO_SYMBOLS;
+
+module_init(init_adfs_fs)
+module_exit(exit_adfs_fs)
diff --git a/fs/affs/Changes b/fs/affs/Changes
new file mode 100644
index 00000000000000..ee63080031678c
--- /dev/null
+++ b/fs/affs/Changes
@@ -0,0 +1,338 @@
+(Note: I consider version numbers as cheap. That means
+that I do not like numbers like 0.1 and the like for
+things that can be used since quite some time. But
+then, 3.1 doesn't mean 'perfectly stable', too.)
+
+Known bugs:
+-----------
+
+- Doesn't work on the alpha. The only 64/32-bit
+ problem that I'm aware of (pointer/int conversion
+ in readdir()) gives compiler warnings but is
+ apparently not causing the failure, as directory
+ reads basically work (but all files are of size 0).
+ Alas, I've got no alpha to debug. :-(
+
+- The partition checker (drivers/block/genhd.c)
+ doesn't work with devices which have 256 byte
+ blocks (some very old SCSI drives).
+
+- The feature to automatically make the fs clean
+ might leave a trashed file system with the
+ bitmap flag set valid.
+
+- When a file is truncated to a size that is not
+ a multiple of the blocksize, the rest of the
+ last allocated block is not cleared. Well,
+ this fs never claimed to be Posix conformant.
+
+Please direct bug reports to: zippel@linux-m68k.org
+
+Version 3.19
+------------
+
+- sizeof changes from Kernel Janitor Project
+- several bug fixes found with fsx
+
+Version 3.18
+------------
+
+- change to global min macro + warning fixes
+- add module tags
+
+Version 3.17
+------------
+
+- locking fixes
+- wrong sign in __affs_hash_dentry
+- remove unnecessary check in affs_new_inode
+- enable international mode for dircache fs
+
+Version 3.16
+------------
+
+- use mark_buffer_dirty_inode instead of mark_buffer_dirty.
+- introduce affs_lock_{link|dir|ext}.
+
+Version 3.15
+------------
+
+- disable link to directories until we can properly support them.
+- locking fixes for link creation/removal.
+
+Version 3.14
+------------
+
+- correctly cut off long file names for compares
+- correctly initialize s_last_bmap
+
+Version 3.13
+------------
+
+Major cleanup for 2.4 [Roman Zippel]
+- new extended block handling
+- new bitmap allocation functions
+- locking should be safe for the future
+- cleanup of some interfaces
+
+Version 3.12
+------------
+
+more 2.4 fixes: [Roman Zippel]
+- s_lock changes
+- increased getblock mess
+- clear meta blocks
+
+Version 3.11
+------------
+
+- Converted to use 2.3.x page cache [Dave Jones <dave@powertweak.com>]
+- Corruption in truncate() bugfix [Ken Tyler <kent@werple.net.au>]
+
+Version 3.10
+------------
+
+- Changed partition checker to allow devices
+ with physical blocks != 512 bytes.
+
+- The partition checker now also ignores the
+ word at 0xd0 that Windows likes to write to.
+
+Version 3.9
+-----------
+
+- Moved cleanup from release_file() to put_inode().
+ This makes the first one obsolete.
+
+- truncate() zeroes the unused remainder of a
+ partially used last block when a file is truncated.
+ It also marks the inode dirty now (which is not
+ really necessary as notify_change() will do
+ it anyway).
+
+- Added a few comments, fixed some typos (and
+ introduced some new ones), made the debug messages
+ more consistent. Changed a bad example in the
+ doc file (affs.txt).
+
+- Sets the NOEXEC flag in read_super() for old file
+ systems, since you can't run programs on them.
+
+Version 3.8
+-----------
+Bill Hawes kindly reviewed the affs and sent me the
+patches he did. They're marked (BH). Thanks, Bill!
+
+- Cleanup of error handling in read_super().
+ Didn't release all resources in case of an
+ error. (BH)
+
+- put_inode() releases the ext cache only if it's
+ no longer needed. (BH)
+
+- One set of dentry callbacks is enough. (BH)
+
+- Cleanup of error handling in namei.c. (BH)
+
+- Cleanup of error handling in file.c. (BH)
+
+- The original blocksize of the device is
+ restored when the fs is unmounted. (BH)
+
+- getblock() did not invalidate the key cache
+ when it allocated a new block.
+
+- Removed some unnecessary locks as Bill
+ suggested.
+
+- Simplified match_name(), changed all hashing
+ and case insensitive name comparisons to use
+ uppercase. This makes the tolower() routines
+ obsolete.
+
+- Added mount option 'mufs' to force muFS
+ uid/gid interpretation.
+
+- File mode changes were not updated on disk.
+ This was fixed before, but somehow got lost.
+
+Version 3.7
+-----------
+
+- Added dentry callbacks to allow the dcache to
+ operate case insensitive and length ignorant
+ like the affs itself.
+
+- getblock() didn't update the lastblock field in the
+ inode if the fs was not an OFS. This bug only shows
+ up if a file was enlarged via truncate() and there
+ was not enough space.
+
+- Remove some more superfluous code left over from
+ the old link days ...
+
+- Fixed some oversights which were in patch 2.1.78.
+
+- Fixed a few typos.
+
+Version 3.6
+-----------
+
+- dentry changes. (Thanks to Jes Sorensen for his help.)
+
+- Fixed bug in balloc(): Superblock was not set dirty after
+ the bitmap was changed, so the bitmap wasn't sync'd.
+
+- Fixed nasty bug in find_new_zone(): If the current
+ zone number was zero, the loop didn't terminate,
+ causing a solid lock-up.
+
+- Removed support for old-style directory reads.
+
+- Fixed bug in add_entry(): When doing a sorted insert,
+ the pointer to the next entry in the hash chain wasn't
+ correctly byte-swapped. Since most of the users of the
+ affs use it on a 68k, they didn't notice. But why did
+ I not find this during my tests?
+
+- Fixed some oversights (version wasn't updated on some
+ directory changes).
+
+- Handling of hard links rewritten. To the VFS
+ they appear now as normal Unix links. They are
+ now resolved only once in lookup(). The backside
+ is that unlink(), rename() and rmdir() have to
+ be smart about them, but the result is worth the
+ effort. This also led to some code cleanup.
+
+- Changed name type to unsigned char; the test for
+ invalid filenames didn't work correctly.
+ (Thanks to Michael Krause for pointing at this.)
+
+- Changed mapping of executable flag.
+
+- Changed all network byte-order macros to the
+ recommended ones.
+
+- Added a remount function, so attempts to remount
+ a dircache filesystem or one with errors read/write
+ can be trapped. Previously, ro remounts didn't
+ flush the super block, and rw remounts didn't
+ create allocation zones ...
+
+- Call shrink_dcache_parent() in rmdir().
+ (Thanks to Bill Hawes.)
+
+- Permission checks in unlink().
+
+- Allow mounting of volumes with superfluous
+ bitmap pointers read only, also allows them
+ to be remounted read/write.
+
+- Owner/Group defaults now to the fs user (i.e.
+ the one that mounted it) instead of root. This
+ obsoletes the mount options uid and gid.
+
+- Argument to volume option could overflow the
+ name buffer. It is now silently truncated to
+ 30 characters. (Damn it! This kind of bug
+ is too embarrassing.)
+
+- Split inode.c into 2 files, the superblock
+ routines desperately wanted their own file.
+
+- truncate() didn't allocate an extension block
+ cache. If a file was extended by means of
+ truncate(), this led to an Oops.
+
+- fsuser is now checked last.
+
+- rename() will not ignore changes in filename
+ casing any more (though mv(1) still won't allow
+ you to do "mv oldname OldName").
+
+Version 3.5
+-----------
+
+- Extension block caches are now allocated on
+ demand instead of when a file is opened, as
+ files can be read and written without opening
+ them (e. g. the loopback device does this).
+
+- Removed an unused function.
+
+Version 3.4
+-----------
+
+- Hash chains are now sorted by block numbers.
+ (Thanks to Kars de Jong for finding this.)
+- Removed all unnecessary external symbols.
+
+Version 3.3
+-----------
+
+- Tried to make all types 'correct' and consistent.
+- Errors and warnings are now reported via a
+ function. They are all prefixed by a severity
+ and have the same appearance:
+ "AFFS: <function>: <error message>"
+ (There's one exception to this, as in that function
+ is no pointer to the super block available.)
+- The filesystem is remounted read-only after an
+ error.
+- The names of newly created filesystem objects are
+ now checked for validity.
+- Minor cleanups in comments.
+- Added this Changes file. At last!
+
+Version 3.2
+-----------
+
+- Extension block cache: Reading/writing of huge files
+ (several MB) is much faster (of course the added
+ overhead slows down opening, but this is hardly
+ noticeable).
+- The same get_block()-routine can now be used for
+ both OFS and FFS.
+- The super block is now searched in the block that
+ was calculated and in the one following. This
+ should remedy the round-off error introduced by
+ the 1-k blocks that Linux uses.
+- Minor changes to adhere to the new VFS interface.
+- The number of used blocks is now also calculated
+ if the filesystem is mounted read-only.
+- Prefixed some constants with AFFS_ to avoid name
+ clashes.
+- Removed 'EXPERIMENTAL' status.
+
+Version 3.1
+-----------
+
+- Fixed a nasty bug which didn't allow read-only
+ mounts.
+- Allow dir-cache filesystems to be mounted
+ read only.
+- OFS support.
+- Several other changes I just cannot remember
+ any more.
+
+Version 3.0
+-----------
+
+- Almost complete rewrite for the new VFS
+ interface in Linux 1.3.
+- Write support.
+- Support for hard and symbolic links.
+- Lots of things I remember even less ...
+
+Version 2.0
+-----------
+
+- Fixed a few things to get it compiled.
+- Automatic root block calculation.
+- Partition checker for genhd.c
+
+========================================
+
+Let's just call Ray Burr's original affs
+'Version 1.0'.
diff --git a/fs/affs/Makefile b/fs/affs/Makefile
new file mode 100644
index 00000000000000..5d485cdca28674
--- /dev/null
+++ b/fs/affs/Makefile
@@ -0,0 +1,17 @@
+#
+# Makefile for the Linux affs filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile.
+
+#EXTRA_CFLAGS=-DDEBUG=1
+
+O_TARGET := affs.o
+
+obj-y := super.o namei.o inode.o file.o dir.o amigaffs.o bitmap.o symlink.o
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/affs/amigaffs.c b/fs/affs/amigaffs.c
new file mode 100644
index 00000000000000..bef1add043562c
--- /dev/null
+++ b/fs/affs/amigaffs.c
@@ -0,0 +1,514 @@
+/*
+ * linux/fs/affs/amigaffs.c
+ *
+ * (c) 1996 Hans-Joachim Widmaier - Rewritten
+ *
+ * (C) 1993 Ray Burr - Amiga FFS filesystem.
+ *
+ * Please send bug reports to: hjw@zvw.de
+ */
+
+#include <stdarg.h>
+#include <linux/stat.h>
+#include <linux/sched.h>
+#include <linux/affs_fs.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/mm.h>
+#include <linux/amigaffs.h>
+
+extern struct timezone sys_tz;
+
+static char ErrorBuffer[256];
+
+/*
+ * Functions for accessing Amiga-FFS structures.
+ */
+
+
+/* Insert a header block bh into the directory dir
+ * caller must hold AFFS_DIR->i_hash_lock!
+ */
+
+int
+affs_insert_hash(struct inode *dir, struct buffer_head *bh)
+{
+ struct super_block *sb = dir->i_sb;
+ struct buffer_head *dir_bh;
+ u32 ino, hash_ino;
+ int offset;
+
+ ino = bh->b_blocknr;
+ offset = affs_hash_name(sb, AFFS_TAIL(sb, bh)->name + 1, AFFS_TAIL(sb, bh)->name[0]);
+
+ pr_debug("AFFS: insert_hash(dir=%u, ino=%d)\n", (u32)dir->i_ino, ino);
+
+ dir_bh = affs_bread(sb, dir->i_ino);
+ if (!dir_bh)
+ return -EIO;
+
+ hash_ino = be32_to_cpu(AFFS_HEAD(dir_bh)->table[offset]);
+ while (hash_ino) {
+ affs_brelse(dir_bh);
+ dir_bh = affs_bread(sb, hash_ino);
+ if (!dir_bh)
+ return -EIO;
+ hash_ino = be32_to_cpu(AFFS_TAIL(sb, dir_bh)->hash_chain);
+ }
+ AFFS_TAIL(sb, bh)->parent = cpu_to_be32(dir->i_ino);
+ AFFS_TAIL(sb, bh)->hash_chain = 0;
+ affs_fix_checksum(sb, bh);
+
+ if (dir->i_ino == dir_bh->b_blocknr)
+ AFFS_HEAD(dir_bh)->table[offset] = cpu_to_be32(ino);
+ else
+ AFFS_TAIL(sb, dir_bh)->hash_chain = cpu_to_be32(ino);
+
+ affs_adjust_checksum(dir_bh, ino);
+ mark_buffer_dirty_inode(dir_bh, dir);
+ affs_brelse(dir_bh);
+
+ dir->i_mtime = dir->i_ctime = CURRENT_TIME;
+ dir->i_version = ++event;
+ mark_inode_dirty(dir);
+
+ return 0;
+}
+
+/* Remove a header block from its directory.
+ * caller must hold AFFS_DIR->i_hash_lock!
+ */
+
+int
+affs_remove_hash(struct inode *dir, struct buffer_head *rem_bh)
+{
+ struct super_block *sb;
+ struct buffer_head *bh;
+ u32 rem_ino, hash_ino, ino;
+ int offset, retval;
+
+ sb = dir->i_sb;
+ rem_ino = rem_bh->b_blocknr;
+ offset = affs_hash_name(sb, AFFS_TAIL(sb, rem_bh)->name+1, AFFS_TAIL(sb, rem_bh)->name[0]);
+ pr_debug("AFFS: remove_hash(dir=%d, ino=%d, hashval=%d)\n", (u32)dir->i_ino, rem_ino, offset);
+
+ bh = affs_bread(sb, dir->i_ino);
+ if (!bh)
+ return -EIO;
+
+ retval = -ENOENT;
+ hash_ino = be32_to_cpu(AFFS_HEAD(bh)->table[offset]);
+ while (hash_ino) {
+ if (hash_ino == rem_ino) {
+ ino = AFFS_TAIL(sb, rem_bh)->hash_chain;
+ if (dir->i_ino == bh->b_blocknr)
+ AFFS_HEAD(bh)->table[offset] = ino;
+ else
+ AFFS_TAIL(sb, bh)->hash_chain = ino;
+ affs_adjust_checksum(bh, be32_to_cpu(ino) - hash_ino);
+ mark_buffer_dirty_inode(bh, dir);
+ AFFS_TAIL(sb, rem_bh)->parent = 0;
+ retval = 0;
+ break;
+ }
+ affs_brelse(bh);
+ bh = affs_bread(sb, hash_ino);
+ if (!bh)
+ return -EIO;
+ hash_ino = be32_to_cpu(AFFS_TAIL(sb, bh)->hash_chain);
+ }
+
+ affs_brelse(bh);
+
+ dir->i_mtime = dir->i_ctime = CURRENT_TIME;
+ dir->i_version = ++event;
+ mark_inode_dirty(dir);
+
+ return retval;
+}
+
+static void
+affs_fix_dcache(struct dentry *dentry, u32 entry_ino)
+{
+ struct inode *inode = dentry->d_inode;
+ void *data = dentry->d_fsdata;
+ struct list_head *head, *next;
+
+ spin_lock(&dcache_lock);
+ head = &inode->i_dentry;
+ next = head->next;
+ while (next != head) {
+ dentry = list_entry(next, struct dentry, d_alias);
+ if (entry_ino == (u32)(long)dentry->d_fsdata) {
+ dentry->d_fsdata = data;
+ break;
+ }
+ next = next->next;
+ }
+ spin_unlock(&dcache_lock);
+}
+
+
+/* Remove header from link chain */
+
+static int
+affs_remove_link(struct dentry *dentry)
+{
+ struct inode *dir, *inode = dentry->d_inode;
+ struct super_block *sb = inode->i_sb;
+ struct buffer_head *bh = NULL, *link_bh = NULL;
+ u32 link_ino, ino;
+ int retval;
+
+ pr_debug("AFFS: remove_link(key=%ld)\n", inode->i_ino);
+ retval = -EIO;
+ bh = affs_bread(sb, inode->i_ino);
+ if (!bh)
+ goto done;
+
+ link_ino = (u32)(long)dentry->d_fsdata;
+ if (inode->i_ino == link_ino) {
+ /* we can't remove the head of the link, as its blocknr is still used as ino,
+ * so we remove the block of the first link instead.
+ */
+ link_ino = be32_to_cpu(AFFS_TAIL(sb, bh)->link_chain);
+ link_bh = affs_bread(sb, link_ino);
+ if (!link_bh)
+ goto done;
+
+ dir = iget(sb, be32_to_cpu(AFFS_TAIL(sb, link_bh)->parent));
+ if (!dir)
+ goto done;
+
+ affs_lock_dir(dir);
+ affs_fix_dcache(dentry, link_ino);
+ retval = affs_remove_hash(dir, link_bh);
+ if (retval)
+ goto done;
+ mark_buffer_dirty_inode(link_bh, inode);
+
+ memcpy(AFFS_TAIL(sb, bh)->name, AFFS_TAIL(sb, link_bh)->name, 32);
+ retval = affs_insert_hash(dir, bh);
+ if (retval)
+ goto done;
+ mark_buffer_dirty_inode(bh, inode);
+
+ affs_unlock_dir(dir);
+ iput(dir);
+ } else {
+ link_bh = affs_bread(sb, link_ino);
+ if (!link_bh)
+ goto done;
+ }
+
+ while ((ino = be32_to_cpu(AFFS_TAIL(sb, bh)->link_chain))) {
+ if (ino == link_ino) {
+ ino = AFFS_TAIL(sb, link_bh)->link_chain;
+ AFFS_TAIL(sb, bh)->link_chain = ino;
+ affs_adjust_checksum(bh, be32_to_cpu(ino) - link_ino);
+ mark_buffer_dirty_inode(bh, inode);
+ retval = 0;
+ /* Fix the link count, if bh is a normal header block without links */
+ switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) {
+ case ST_LINKDIR:
+ case ST_LINKFILE:
+ break;
+ default:
+ if (!AFFS_TAIL(sb, bh)->link_chain)
+ inode->i_nlink = 1;
+ }
+ affs_free_block(sb, link_ino);
+ goto done;
+ }
+ affs_brelse(bh);
+ bh = affs_bread(sb, ino);
+ if (!bh)
+ goto done;
+ }
+ retval = -ENOENT;
+done:
+ affs_brelse(link_bh);
+ affs_brelse(bh);
+ return retval;
+}
+
+
+static int
+affs_empty_dir(struct inode *inode)
+{
+ struct super_block *sb = inode->i_sb;
+ struct buffer_head *bh;
+ int retval, size;
+
+ retval = -EIO;
+ bh = affs_bread(sb, inode->i_ino);
+ if (!bh)
+ goto done;
+
+ retval = -ENOTEMPTY;
+ for (size = AFFS_SB->s_hashsize - 1; size >= 0; size--)
+ if (AFFS_HEAD(bh)->table[size])
+ goto not_empty;
+ retval = 0;
+not_empty:
+ affs_brelse(bh);
+done:
+ return retval;
+}
+
+
+/* Remove a filesystem object. If the object to be removed has
+ * links to it, one of the links must be changed to inherit
+ * the file or directory. As above, any inode will do.
+ * The buffer will not be freed. If the header is a link, the
+ * block will be marked as free.
+ * This function returns a negative error number in case of
+ * an error, else 0 if the inode is to be deleted or 1 if not.
+ */
+
+int
+affs_remove_header(struct dentry *dentry)
+{
+ struct super_block *sb;
+ struct inode *inode, *dir;
+ struct buffer_head *bh = NULL;
+ int retval;
+
+ dir = dentry->d_parent->d_inode;
+ sb = dir->i_sb;
+
+ retval = -ENOENT;
+ inode = dentry->d_inode;
+ if (!inode)
+ goto done;
+
+ pr_debug("AFFS: remove_header(key=%ld)\n", inode->i_ino);
+ retval = -EIO;
+ bh = affs_bread(sb, (u32)(long)dentry->d_fsdata);
+ if (!bh)
+ goto done;
+
+ affs_lock_link(inode);
+ affs_lock_dir(dir);
+ switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) {
+ case ST_USERDIR:
+ /* if we ever want to support links to dirs
+ * i_hash_lock of the inode must only be
+ * taken after some checks
+ */
+ affs_lock_dir(inode);
+ retval = affs_empty_dir(inode);
+ affs_unlock_dir(inode);
+ if (retval)
+ goto done_unlock;
+ break;
+ default:
+ break;
+ }
+
+ retval = affs_remove_hash(dir, bh);
+ if (retval)
+ goto done_unlock;
+ mark_buffer_dirty_inode(bh, inode);
+
+ affs_unlock_dir(dir);
+
+ if (inode->i_nlink > 1)
+ retval = affs_remove_link(dentry);
+ else
+ inode->i_nlink = 0;
+ affs_unlock_link(inode);
+ inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+
+done:
+ affs_brelse(bh);
+ return retval;
+
+done_unlock:
+ affs_unlock_dir(dir);
+ affs_unlock_link(inode);
+ goto done;
+}
+
+/* Checksum a block, do various consistency checks and optionally return
+ the blocks type number. DATA points to the block. If their pointers
+ are non-null, *PTYPE and *STYPE are set to the primary and secondary
+ block types respectively, *HASHSIZE is set to the size of the hashtable
+ (which lets us calculate the block size).
+ Returns non-zero if the block is not consistent. */
+
+u32
+affs_checksum_block(struct super_block *sb, struct buffer_head *bh)
+{
+ u32 *ptr = (u32 *)bh->b_data;
+ u32 sum;
+ int bsize;
+
+ sum = 0;
+ for (bsize = sb->s_blocksize / sizeof(u32); bsize > 0; bsize--)
+ sum += be32_to_cpu(*ptr++);
+ return sum;
+}
+
+/*
+ * Calculate the checksum of a disk block and store it
+ * at the indicated position.
+ */
+
+void
+affs_fix_checksum(struct super_block *sb, struct buffer_head *bh)
+{
+ int cnt = sb->s_blocksize / sizeof(u32);
+ u32 *ptr = (u32 *)bh->b_data;
+ u32 checksum, *checksumptr;
+
+ checksumptr = ptr + 5;
+ *checksumptr = 0;
+ for (checksum = 0; cnt > 0; ptr++, cnt--)
+ checksum += be32_to_cpu(*ptr);
+ *checksumptr = cpu_to_be32(-checksum);
+}
+
+void
+secs_to_datestamp(time_t secs, struct affs_date *ds)
+{
+ u32 days;
+ u32 minute;
+
+ secs -= sys_tz.tz_minuteswest * 60 + ((8 * 365 + 2) * 24 * 60 * 60);
+ if (secs < 0)
+ secs = 0;
+ days = secs / 86400;
+ secs -= days * 86400;
+ minute = secs / 60;
+ secs -= minute * 60;
+
+ ds->days = be32_to_cpu(days);
+ ds->mins = be32_to_cpu(minute);
+ ds->ticks = be32_to_cpu(secs * 50);
+}
+
+mode_t
+prot_to_mode(u32 prot)
+{
+ int mode = 0;
+
+ if (!(prot & FIBF_NOWRITE))
+ mode |= S_IWUSR;
+ if (!(prot & FIBF_NOREAD))
+ mode |= S_IRUSR;
+ if (!(prot & FIBF_NOEXECUTE))
+ mode |= S_IXUSR;
+ if (prot & FIBF_GRP_WRITE)
+ mode |= S_IWGRP;
+ if (prot & FIBF_GRP_READ)
+ mode |= S_IRGRP;
+ if (prot & FIBF_GRP_EXECUTE)
+ mode |= S_IXGRP;
+ if (prot & FIBF_OTR_WRITE)
+ mode |= S_IWOTH;
+ if (prot & FIBF_OTR_READ)
+ mode |= S_IROTH;
+ if (prot & FIBF_OTR_EXECUTE)
+ mode |= S_IXOTH;
+
+ return mode;
+}
+
+void
+mode_to_prot(struct inode *inode)
+{
+ u32 prot = AFFS_INODE->i_protect;
+ mode_t mode = inode->i_mode;
+
+ if (!(mode & S_IXUSR))
+ prot |= FIBF_NOEXECUTE;
+ if (!(mode & S_IRUSR))
+ prot |= FIBF_NOREAD;
+ if (!(mode & S_IWUSR))
+ prot |= FIBF_NOWRITE;
+ if (mode & S_IXGRP)
+ prot |= FIBF_GRP_EXECUTE;
+ if (mode & S_IRGRP)
+ prot |= FIBF_GRP_READ;
+ if (mode & S_IWGRP)
+ prot |= FIBF_GRP_WRITE;
+ if (mode & S_IXOTH)
+ prot |= FIBF_OTR_EXECUTE;
+ if (mode & S_IROTH)
+ prot |= FIBF_OTR_READ;
+ if (mode & S_IWOTH)
+ prot |= FIBF_OTR_WRITE;
+
+ AFFS_INODE->i_protect = prot;
+}
+
+void
+affs_error(struct super_block *sb, const char *function, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args,fmt);
+ vsprintf(ErrorBuffer,fmt,args);
+ va_end(args);
+
+ printk(KERN_CRIT "AFFS error (device %s): %s(): %s\n", bdevname(sb->s_dev),
+ function,ErrorBuffer);
+ if (!(sb->s_flags & MS_RDONLY))
+ printk(KERN_WARNING "AFFS: Remounting filesystem read-only\n");
+ sb->s_flags |= MS_RDONLY;
+}
+
+void
+affs_warning(struct super_block *sb, const char *function, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args,fmt);
+ vsprintf(ErrorBuffer,fmt,args);
+ va_end(args);
+
+ printk(KERN_WARNING "AFFS warning (device %s): %s(): %s\n", bdevname(sb->s_dev),
+ function,ErrorBuffer);
+}
+
+/* Check if the name is valid for a affs object. */
+
+int
+affs_check_name(const unsigned char *name, int len)
+{
+ int i;
+
+ if (len > 30)
+#ifdef AFFS_NO_TRUNCATE
+ return -ENAMETOOLONG;
+#else
+ len = 30;
+#endif
+
+ for (i = 0; i < len; i++) {
+ if (name[i] < ' ' || name[i] == ':'
+ || (name[i] > 0x7e && name[i] < 0xa0))
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* This function copies name to bstr, with at most 30
+ * characters length. The bstr will be prepended by
+ * a length byte.
+ * NOTE: The name will must be already checked by
+ * affs_check_name()!
+ */
+
+int
+affs_copy_name(unsigned char *bstr, struct dentry *dentry)
+{
+ int len = min(dentry->d_name.len, 30u);
+
+ *bstr++ = len;
+ memcpy(bstr, dentry->d_name.name, len);
+ return len;
+}
diff --git a/fs/affs/bitmap.c b/fs/affs/bitmap.c
new file mode 100644
index 00000000000000..60b44cd30bfdf4
--- /dev/null
+++ b/fs/affs/bitmap.c
@@ -0,0 +1,391 @@
+/*
+ * linux/fs/affs/bitmap.c
+ *
+ * (c) 1996 Hans-Joachim Widmaier
+ *
+ * bitmap.c contains the code that handles all bitmap related stuff -
+ * block allocation, deallocation, calculation of free space.
+ */
+
+#include <linux/sched.h>
+#include <linux/affs_fs.h>
+#include <linux/stat.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/bitops.h>
+#include <linux/amigaffs.h>
+
+/* This is, of course, shamelessly stolen from fs/minix */
+
+static int nibblemap[] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 };
+
+u32
+affs_count_free_bits(u32 blocksize, const void *data)
+{
+ const u32 *map;
+ u32 free;
+ u32 tmp;
+
+ map = data;
+ free = 0;
+ for (blocksize /= 4; blocksize > 0; blocksize--) {
+ tmp = *map++;
+ while (tmp) {
+ free += nibblemap[tmp & 0xf];
+ tmp >>= 4;
+ }
+ }
+
+ return free;
+}
+
+u32
+affs_count_free_blocks(struct super_block *sb)
+{
+ struct affs_bm_info *bm;
+ u32 free;
+ int i;
+
+ pr_debug("AFFS: count_free_blocks()\n");
+
+ if (sb->s_flags & MS_RDONLY)
+ return 0;
+
+ down(&AFFS_SB->s_bmlock);
+
+ bm = AFFS_SB->s_bitmap;
+ free = 0;
+ for (i = AFFS_SB->s_bmap_count; i > 0; bm++, i--)
+ free += bm->bm_free;
+
+ up(&AFFS_SB->s_bmlock);
+
+ return free;
+}
+
+void
+affs_free_block(struct super_block *sb, u32 block)
+{
+ struct affs_bm_info *bm;
+ struct buffer_head *bh;
+ u32 blk, bmap, bit, mask, tmp;
+ u32 *data;
+
+ pr_debug("AFFS: free_block(%u)\n", block);
+
+ if (block > AFFS_SB->s_partition_size)
+ goto err_range;
+
+ blk = block - AFFS_SB->s_reserved;
+ bmap = blk / AFFS_SB->s_bmap_bits;
+ bit = blk % AFFS_SB->s_bmap_bits;
+ bm = &AFFS_SB->s_bitmap[bmap];
+
+ down(&AFFS_SB->s_bmlock);
+
+ bh = AFFS_SB->s_bmap_bh;
+ if (AFFS_SB->s_last_bmap != bmap) {
+ affs_brelse(bh);
+ bh = affs_bread(sb, bm->bm_key);
+ if (!bh)
+ goto err_bh_read;
+ AFFS_SB->s_bmap_bh = bh;
+ AFFS_SB->s_last_bmap = bmap;
+ }
+
+ mask = 1 << (bit & 31);
+ data = (u32 *)bh->b_data + bit / 32 + 1;
+
+ /* mark block free */
+ tmp = be32_to_cpu(*data);
+ if (tmp & mask)
+ goto err_free;
+ *data = cpu_to_be32(tmp | mask);
+
+ /* fix checksum */
+ tmp = be32_to_cpu(*(u32 *)bh->b_data);
+ *(u32 *)bh->b_data = cpu_to_be32(tmp - mask);
+
+ mark_buffer_dirty(bh);
+ sb->s_dirt = 1;
+ bm->bm_free++;
+
+ up(&AFFS_SB->s_bmlock);
+ return;
+
+err_free:
+ affs_warning(sb,"affs_free_block","Trying to free block %u which is already free", block);
+ up(&AFFS_SB->s_bmlock);
+ return;
+
+err_bh_read:
+ affs_error(sb,"affs_free_block","Cannot read bitmap block %u", bm->bm_key);
+ AFFS_SB->s_bmap_bh = NULL;
+ AFFS_SB->s_last_bmap = ~0;
+ up(&AFFS_SB->s_bmlock);
+ return;
+
+err_range:
+ affs_error(sb, "affs_free_block","Block %u outside partition", block);
+ return;
+}
+
+/*
+ * Allocate a block in the given allocation zone.
+ * Since we have to byte-swap the bitmap on little-endian
+ * machines, this is rather expensive. Therefor we will
+ * preallocate up to 16 blocks from the same word, if
+ * possible. We are not doing preallocations in the
+ * header zone, though.
+ */
+
+u32
+affs_alloc_block(struct inode *inode, u32 goal)
+{
+ struct super_block *sb;
+ struct affs_bm_info *bm;
+ struct buffer_head *bh;
+ u32 *data, *enddata;
+ u32 blk, bmap, bit, mask, mask2, tmp;
+ int i;
+
+ sb = inode->i_sb;
+
+ pr_debug("AFFS: balloc(inode=%lu,goal=%u): ", inode->i_ino, goal);
+
+ if (inode->u.affs_i.i_pa_cnt) {
+ pr_debug("%d\n", inode->u.affs_i.i_lastalloc+1);
+ inode->u.affs_i.i_pa_cnt--;
+ return ++inode->u.affs_i.i_lastalloc;
+ }
+
+ if (!goal || goal > AFFS_SB->s_partition_size) {
+ if (goal)
+ affs_warning(sb, "affs_balloc", "invalid goal %d", goal);
+ //if (!inode->u.affs_i.i_last_block)
+ // affs_warning(sb, "affs_balloc", "no last alloc block");
+ goal = AFFS_SB->s_reserved;
+ }
+
+ blk = goal - AFFS_SB->s_reserved;
+ bmap = blk / AFFS_SB->s_bmap_bits;
+ bm = &AFFS_SB->s_bitmap[bmap];
+
+ down(&AFFS_SB->s_bmlock);
+
+ if (bm->bm_free)
+ goto find_bmap_bit;
+
+find_bmap:
+ /* search for the next bmap buffer with free bits */
+ i = AFFS_SB->s_bmap_count;
+ do {
+ bmap++;
+ bm++;
+ if (bmap < AFFS_SB->s_bmap_count)
+ continue;
+ /* restart search at zero */
+ bmap = 0;
+ bm = AFFS_SB->s_bitmap;
+ if (--i <= 0)
+ goto err_full;
+ } while (!bm->bm_free);
+ blk = bmap * AFFS_SB->s_bmap_bits;
+
+find_bmap_bit:
+
+ bh = AFFS_SB->s_bmap_bh;
+ if (AFFS_SB->s_last_bmap != bmap) {
+ affs_brelse(bh);
+ bh = affs_bread(sb, bm->bm_key);
+ if (!bh)
+ goto err_bh_read;
+ AFFS_SB->s_bmap_bh = bh;
+ AFFS_SB->s_last_bmap = bmap;
+ }
+
+ /* find an unused block in this bitmap block */
+ bit = blk % AFFS_SB->s_bmap_bits;
+ data = (u32 *)bh->b_data + bit / 32 + 1;
+ enddata = (u32 *)((u8 *)bh->b_data + sb->s_blocksize);
+ mask = ~0UL << (bit & 31);
+ blk &= ~31UL;
+
+ tmp = be32_to_cpu(*data) & mask;
+ if (tmp)
+ goto find_bit;
+
+ /* scan the rest of the buffer */
+ do {
+ blk += 32;
+ if (++data >= enddata)
+ /* didn't find something, can only happen
+ * if scan didn't start at 0, try next bmap
+ */
+ goto find_bmap;
+ } while (!(tmp = *data));
+ tmp = be32_to_cpu(tmp);
+
+find_bit:
+ /* finally look for a free bit in the word */
+ bit = ffs(tmp) - 1;
+ blk += bit + AFFS_SB->s_reserved;
+ mask2 = mask = 1 << (bit & 31);
+ inode->u.affs_i.i_lastalloc = blk;
+
+ /* prealloc as much as possible within this word */
+ while ((mask2 <<= 1)) {
+ if (!(tmp & mask2))
+ break;
+ inode->u.affs_i.i_pa_cnt++;
+ mask |= mask2;
+ }
+ bm->bm_free -= inode->u.affs_i.i_pa_cnt + 1;
+
+ *data = cpu_to_be32(tmp & ~mask);
+
+ /* fix checksum */
+ tmp = be32_to_cpu(*(u32 *)bh->b_data);
+ *(u32 *)bh->b_data = cpu_to_be32(tmp + mask);
+
+ mark_buffer_dirty(bh);
+ sb->s_dirt = 1;
+
+ up(&AFFS_SB->s_bmlock);
+
+ pr_debug("%d\n", blk);
+ return blk;
+
+err_bh_read:
+ affs_error(sb,"affs_read_block","Cannot read bitmap block %u", bm->bm_key);
+ AFFS_SB->s_bmap_bh = NULL;
+ AFFS_SB->s_last_bmap = ~0;
+err_full:
+ pr_debug("failed\n");
+ up(&AFFS_SB->s_bmlock);
+ return 0;
+}
+
+int affs_init_bitmap(struct super_block *sb, int *flags)
+{
+ struct affs_bm_info *bm;
+ struct buffer_head *bmap_bh = NULL, *bh = NULL;
+ u32 *bmap_blk;
+ u32 size, blk, end, offset, mask;
+ int i, res = 0;
+
+ if (*flags & MS_RDONLY)
+ return 0;
+
+ if (!AFFS_ROOT_TAIL(sb, AFFS_SB->s_root_bh)->bm_flag) {
+ printk(KERN_NOTICE "AFFS: Bitmap invalid - mounting %s read only\n",
+ kdevname(sb->s_dev));
+ *flags |= MS_RDONLY;
+ return 0;
+ }
+
+ AFFS_SB->s_last_bmap = ~0;
+ AFFS_SB->s_bmap_bh = NULL;
+ AFFS_SB->s_bmap_bits = sb->s_blocksize * 8 - 32;
+ AFFS_SB->s_bmap_count = (AFFS_SB->s_partition_size - AFFS_SB->s_reserved +
+ AFFS_SB->s_bmap_bits - 1) / AFFS_SB->s_bmap_bits;
+ size = AFFS_SB->s_bmap_count * sizeof(*bm);
+ bm = AFFS_SB->s_bitmap = kmalloc(size, GFP_KERNEL);
+ if (!AFFS_SB->s_bitmap) {
+ printk(KERN_ERR "AFFS: Bitmap allocation failed\n");
+ return -ENOMEM;
+ }
+ memset(AFFS_SB->s_bitmap, 0, size);
+
+ bmap_blk = (u32 *)AFFS_SB->s_root_bh->b_data;
+ blk = sb->s_blocksize / 4 - 49;
+ end = blk + 25;
+
+ for (i = AFFS_SB->s_bmap_count; i > 0; bm++, i--) {
+ affs_brelse(bh);
+
+ bm->bm_key = be32_to_cpu(bmap_blk[blk]);
+ bh = affs_bread(sb, bm->bm_key);
+ if (!bh) {
+ printk(KERN_ERR "AFFS: Cannot read bitmap\n");
+ res = -EIO;
+ goto out;
+ }
+ if (affs_checksum_block(sb, bh)) {
+ printk(KERN_WARNING "AFFS: Bitmap %u invalid - mounting %s read only.\n",
+ bm->bm_key, kdevname(sb->s_dev));
+ *flags |= MS_RDONLY;
+ goto out;
+ }
+ pr_debug("AFFS: read bitmap block %d: %d\n", blk, bm->bm_key);
+ bm->bm_free = affs_count_free_bits(sb->s_blocksize - 4, bh->b_data + 4);
+
+ /* Don't try read the extension if this is the last block,
+ * but we also need the right bm pointer below
+ */
+ if (++blk < end || i == 1)
+ continue;
+ if (bmap_bh)
+ affs_brelse(bmap_bh);
+ bmap_bh = affs_bread(sb, be32_to_cpu(bmap_blk[blk]));
+ if (!bmap_bh) {
+ printk(KERN_ERR "AFFS: Cannot read bitmap extension\n");
+ res = -EIO;
+ goto out;
+ }
+ bmap_blk = (u32 *)bmap_bh->b_data;
+ blk = 0;
+ end = sb->s_blocksize / 4 - 1;
+ }
+
+ offset = (AFFS_SB->s_partition_size - AFFS_SB->s_reserved) % AFFS_SB->s_bmap_bits;
+ mask = ~(0xFFFFFFFFU << (offset & 31));
+ pr_debug("last word: %d %d %d\n", offset, offset / 32 + 1, mask);
+ offset = offset / 32 + 1;
+
+ if (mask) {
+ u32 old, new;
+
+ /* Mark unused bits in the last word as allocated */
+ old = be32_to_cpu(((u32 *)bh->b_data)[offset]);
+ new = old & mask;
+ //if (old != new) {
+ ((u32 *)bh->b_data)[offset] = cpu_to_be32(new);
+ /* fix checksum */
+ //new -= old;
+ //old = be32_to_cpu(*(u32 *)bh->b_data);
+ //*(u32 *)bh->b_data = cpu_to_be32(old - new);
+ //mark_buffer_dirty(bh);
+ //}
+ /* correct offset for the bitmap count below */
+ //offset++;
+ }
+ while (++offset < sb->s_blocksize / 4)
+ ((u32 *)bh->b_data)[offset] = 0;
+ ((u32 *)bh->b_data)[0] = 0;
+ ((u32 *)bh->b_data)[0] = cpu_to_be32(-affs_checksum_block(sb, bh));
+ mark_buffer_dirty(bh);
+
+ /* recalculate bitmap count for last block */
+ bm--;
+ bm->bm_free = affs_count_free_bits(sb->s_blocksize - 4, bh->b_data + 4);
+
+out:
+ affs_brelse(bh);
+ affs_brelse(bmap_bh);
+ return res;
+}
+
+void affs_free_bitmap(struct super_block *sb)
+{
+ if (!AFFS_SB->s_bitmap)
+ return;
+
+ affs_brelse(AFFS_SB->s_bmap_bh);
+ AFFS_SB->s_bmap_bh = NULL;
+ AFFS_SB->s_last_bmap = ~0;
+ kfree(AFFS_SB->s_bitmap);
+ AFFS_SB->s_bitmap = NULL;
+}
diff --git a/fs/affs/dir.c b/fs/affs/dir.c
new file mode 100644
index 00000000000000..c6339451b03f38
--- /dev/null
+++ b/fs/affs/dir.c
@@ -0,0 +1,163 @@
+/*
+ * linux/fs/affs/dir.c
+ *
+ * (c) 1996 Hans-Joachim Widmaier - Rewritten
+ *
+ * (C) 1993 Ray Burr - Modified for Amiga FFS filesystem.
+ *
+ * (C) 1992 Eric Youngdale Modified for ISO 9660 filesystem.
+ *
+ * (C) 1991 Linus Torvalds - minix filesystem
+ *
+ * affs directory handling functions
+ *
+ */
+
+#include <asm/uaccess.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/affs_fs.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/amigaffs.h>
+
+static int affs_readdir(struct file *, void *, filldir_t);
+
+struct file_operations affs_dir_operations = {
+ read: generic_read_dir,
+ readdir: affs_readdir,
+ fsync: file_fsync,
+};
+
+/*
+ * directories can handle most operations...
+ */
+struct inode_operations affs_dir_inode_operations = {
+ create: affs_create,
+ lookup: affs_lookup,
+ link: affs_link,
+ unlink: affs_unlink,
+ symlink: affs_symlink,
+ mkdir: affs_mkdir,
+ rmdir: affs_rmdir,
+ rename: affs_rename,
+ setattr: affs_notify_change,
+};
+
+static int
+affs_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ struct super_block *sb = inode->i_sb;
+ struct buffer_head *dir_bh;
+ struct buffer_head *fh_bh;
+ unsigned char *name;
+ int namelen;
+ u32 i;
+ int hash_pos;
+ int chain_pos;
+ u32 f_pos;
+ u32 ino;
+ int stored;
+ int res;
+
+ pr_debug("AFFS: readdir(ino=%lu,f_pos=%lx)\n",inode->i_ino,(unsigned long)filp->f_pos);
+
+ stored = 0;
+ res = -EIO;
+ dir_bh = NULL;
+ fh_bh = NULL;
+ f_pos = filp->f_pos;
+
+ if (f_pos == 0) {
+ filp->private_data = (void *)0;
+ if (filldir(dirent, ".", 1, f_pos, inode->i_ino, DT_DIR) < 0)
+ return 0;
+ filp->f_pos = f_pos = 1;
+ stored++;
+ }
+ if (f_pos == 1) {
+ if (filldir(dirent, "..", 2, f_pos, filp->f_dentry->d_parent->d_inode->i_ino, DT_DIR) < 0)
+ return stored;
+ filp->f_pos = f_pos = 2;
+ stored++;
+ }
+
+ affs_lock_dir(inode);
+ chain_pos = (f_pos - 2) & 0xffff;
+ hash_pos = (f_pos - 2) >> 16;
+ if (chain_pos == 0xffff) {
+ affs_warning(sb, "readdir", "More than 65535 entries in chain");
+ chain_pos = 0;
+ hash_pos++;
+ filp->f_pos = ((hash_pos << 16) | chain_pos) + 2;
+ }
+ dir_bh = affs_bread(sb, inode->i_ino);
+ if (!dir_bh)
+ goto readdir_out;
+
+ /* If the directory hasn't changed since the last call to readdir(),
+ * we can jump directly to where we left off.
+ */
+ ino = (u32)(long)filp->private_data;
+ if (ino && filp->f_version == inode->i_version) {
+ pr_debug("AFFS: readdir() left off=%d\n", ino);
+ goto inside;
+ }
+
+ ino = be32_to_cpu(AFFS_HEAD(dir_bh)->table[hash_pos]);
+ for (i = 0; ino && i < chain_pos; i++) {
+ fh_bh = affs_bread(sb, ino);
+ if (!fh_bh) {
+ affs_error(sb, "readdir","Cannot read block %d", i);
+ goto readdir_out;
+ }
+ ino = be32_to_cpu(AFFS_TAIL(sb, fh_bh)->hash_chain);
+ affs_brelse(fh_bh);
+ fh_bh = NULL;
+ }
+ if (ino)
+ goto inside;
+ hash_pos++;
+
+ for (; hash_pos < AFFS_SB->s_hashsize; hash_pos++) {
+ ino = be32_to_cpu(AFFS_HEAD(dir_bh)->table[hash_pos]);
+ if (!ino)
+ continue;
+ f_pos = (hash_pos << 16) + 2;
+inside:
+ do {
+ fh_bh = affs_bread(sb, ino);
+ if (!fh_bh) {
+ affs_error(sb, "readdir","Cannot read block %d", ino);
+ goto readdir_done;
+ }
+
+ namelen = min(AFFS_TAIL(sb, fh_bh)->name[0], (u8)30);
+ name = AFFS_TAIL(sb, fh_bh)->name + 1;
+ pr_debug("AFFS: readdir(): filldir(\"%.*s\", ino=%u), hash=%d, f_pos=%x\n",
+ namelen, name, ino, hash_pos, f_pos);
+ if (filldir(dirent, name, namelen, f_pos, ino, DT_UNKNOWN) < 0)
+ goto readdir_done;
+ stored++;
+ f_pos++;
+ ino = be32_to_cpu(AFFS_TAIL(sb, fh_bh)->hash_chain);
+ affs_brelse(fh_bh);
+ fh_bh = NULL;
+ } while (ino);
+ }
+readdir_done:
+ filp->f_pos = f_pos;
+ filp->f_version = inode->i_version;
+ filp->private_data = (void *)(long)ino;
+ res = stored;
+
+readdir_out:
+ affs_brelse(dir_bh);
+ affs_brelse(fh_bh);
+ affs_unlock_dir(inode);
+ pr_debug("AFFS: readdir()=%d\n", stored);
+ return res;
+}
diff --git a/fs/affs/file.c b/fs/affs/file.c
new file mode 100644
index 00000000000000..b648e923460c60
--- /dev/null
+++ b/fs/affs/file.c
@@ -0,0 +1,932 @@
+/*
+ * linux/fs/affs/file.c
+ *
+ * (c) 1996 Hans-Joachim Widmaier - Rewritten
+ *
+ * (C) 1993 Ray Burr - Modified for Amiga FFS filesystem.
+ *
+ * (C) 1992 Eric Youngdale Modified for ISO 9660 filesystem.
+ *
+ * (C) 1991 Linus Torvalds - minix filesystem
+ *
+ * affs regular file handling primitives
+ */
+
+#include <asm/div64.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/sched.h>
+#include <linux/affs_fs.h>
+#include <linux/fcntl.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/locks.h>
+#include <linux/smp_lock.h>
+#include <linux/dirent.h>
+#include <linux/fs.h>
+#include <linux/amigaffs.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+
+#if PAGE_SIZE < 4096
+#error PAGE_SIZE must be at least 4096
+#endif
+
+static int affs_grow_extcache(struct inode *inode, u32 lc_idx);
+static struct buffer_head *affs_alloc_extblock(struct inode *inode, struct buffer_head *bh, u32 ext);
+static inline struct buffer_head *affs_get_extblock(struct inode *inode, u32 ext);
+static struct buffer_head *affs_get_extblock_slow(struct inode *inode, u32 ext);
+static int affs_get_block(struct inode *inode, long block, struct buffer_head *bh_result, int create);
+
+static ssize_t affs_file_write(struct file *filp, const char *buf, size_t count, loff_t *ppos);
+static int affs_file_open(struct inode *inode, struct file *filp);
+static int affs_file_release(struct inode *inode, struct file *filp);
+
+struct file_operations affs_file_operations = {
+ llseek: generic_file_llseek,
+ read: generic_file_read,
+ write: affs_file_write,
+ mmap: generic_file_mmap,
+ open: affs_file_open,
+ release: affs_file_release,
+ fsync: file_fsync,
+};
+
+struct inode_operations affs_file_inode_operations = {
+ truncate: affs_truncate,
+ setattr: affs_notify_change,
+};
+
+static int
+affs_file_open(struct inode *inode, struct file *filp)
+{
+ if (atomic_read(&filp->f_count) != 1)
+ return 0;
+ pr_debug("AFFS: open(%d)\n", AFFS_INODE->i_opencnt);
+ AFFS_INODE->i_opencnt++;
+ return 0;
+}
+
+static int
+affs_file_release(struct inode *inode, struct file *filp)
+{
+ if (atomic_read(&filp->f_count) != 0)
+ return 0;
+ pr_debug("AFFS: release(%d)\n", AFFS_INODE->i_opencnt);
+ AFFS_INODE->i_opencnt--;
+ if (!AFFS_INODE->i_opencnt)
+ affs_free_prealloc(inode);
+
+ return 0;
+}
+
+static int
+affs_grow_extcache(struct inode *inode, u32 lc_idx)
+{
+ struct super_block *sb = inode->i_sb;
+ struct buffer_head *bh;
+ u32 lc_max;
+ int i, j, key;
+
+ if (!AFFS_INODE->i_lc) {
+ char *ptr = (char *)get_zeroed_page(GFP_NOFS);
+ if (!ptr)
+ return -ENOMEM;
+ AFFS_INODE->i_lc = (u32 *)ptr;
+ AFFS_INODE->i_ac = (struct affs_ext_key *)(ptr + AFFS_CACHE_SIZE / 2);
+ }
+
+ lc_max = AFFS_LC_SIZE << AFFS_INODE->i_lc_shift;
+
+ if (AFFS_INODE->i_extcnt > lc_max) {
+ u32 lc_shift, lc_mask, tmp, off;
+
+ /* need to recalculate linear cache, start from old size */
+ lc_shift = AFFS_INODE->i_lc_shift;
+ tmp = (AFFS_INODE->i_extcnt / AFFS_LC_SIZE) >> lc_shift;
+ for (; tmp; tmp >>= 1)
+ lc_shift++;
+ lc_mask = (1 << lc_shift) - 1;
+
+ /* fix idx and old size to new shift */
+ lc_idx >>= (lc_shift - AFFS_INODE->i_lc_shift);
+ AFFS_INODE->i_lc_size >>= (lc_shift - AFFS_INODE->i_lc_shift);
+
+ /* first shrink old cache to make more space */
+ off = 1 << (lc_shift - AFFS_INODE->i_lc_shift);
+ for (i = 1, j = off; j < AFFS_LC_SIZE; i++, j += off)
+ AFFS_INODE->i_ac[i] = AFFS_INODE->i_ac[j];
+
+ AFFS_INODE->i_lc_shift = lc_shift;
+ AFFS_INODE->i_lc_mask = lc_mask;
+ }
+
+ /* fill cache to the needed index */
+ i = AFFS_INODE->i_lc_size;
+ AFFS_INODE->i_lc_size = lc_idx + 1;
+ for (; i <= lc_idx; i++) {
+ if (!i) {
+ AFFS_INODE->i_lc[0] = inode->i_ino;
+ continue;
+ }
+ key = AFFS_INODE->i_lc[i - 1];
+ j = AFFS_INODE->i_lc_mask + 1;
+ // unlock cache
+ for (; j > 0; j--) {
+ bh = affs_bread(sb, key);
+ if (!bh)
+ goto err;
+ key = be32_to_cpu(AFFS_TAIL(sb, bh)->extension);
+ affs_brelse(bh);
+ }
+ // lock cache
+ AFFS_INODE->i_lc[i] = key;
+ }
+
+ return 0;
+
+err:
+ // lock cache
+ return -EIO;
+}
+
+static struct buffer_head *
+affs_alloc_extblock(struct inode *inode, struct buffer_head *bh, u32 ext)
+{
+ struct super_block *sb = inode->i_sb;
+ struct buffer_head *new_bh;
+ u32 blocknr, tmp;
+
+ blocknr = affs_alloc_block(inode, bh->b_blocknr);
+ if (!blocknr)
+ return ERR_PTR(-ENOSPC);
+
+ new_bh = affs_getzeroblk(sb, blocknr);
+ if (!new_bh) {
+ affs_free_block(sb, blocknr);
+ return ERR_PTR(-EIO);
+ }
+
+ AFFS_HEAD(new_bh)->ptype = cpu_to_be32(T_LIST);
+ AFFS_HEAD(new_bh)->key = cpu_to_be32(blocknr);
+ AFFS_TAIL(sb, new_bh)->stype = cpu_to_be32(ST_FILE);
+ AFFS_TAIL(sb, new_bh)->parent = cpu_to_be32(inode->i_ino);
+ affs_fix_checksum(sb, new_bh);
+
+ mark_buffer_dirty_inode(new_bh, inode);
+
+ tmp = be32_to_cpu(AFFS_TAIL(sb, bh)->extension);
+ if (tmp)
+ affs_warning(sb, "alloc_ext", "previous extension set (%x)", tmp);
+ AFFS_TAIL(sb, bh)->extension = cpu_to_be32(blocknr);
+ affs_adjust_checksum(bh, blocknr - tmp);
+ mark_buffer_dirty_inode(bh, inode);
+
+ AFFS_INODE->i_extcnt++;
+ mark_inode_dirty(inode);
+
+ return new_bh;
+}
+
+static inline struct buffer_head *
+affs_get_extblock(struct inode *inode, u32 ext)
+{
+ /* inline the simplest case: same extended block as last time */
+ struct buffer_head *bh = AFFS_INODE->i_ext_bh;
+ if (ext == AFFS_INODE->i_ext_last)
+ atomic_inc(&bh->b_count);
+ else
+ /* we have to do more (not inlined) */
+ bh = affs_get_extblock_slow(inode, ext);
+
+ return bh;
+}
+
+static struct buffer_head *
+affs_get_extblock_slow(struct inode *inode, u32 ext)
+{
+ struct super_block *sb = inode->i_sb;
+ struct buffer_head *bh;
+ u32 ext_key;
+ u32 lc_idx, lc_off, ac_idx;
+ u32 tmp, idx;
+
+ if (ext == AFFS_INODE->i_ext_last + 1) {
+ /* read the next extended block from the current one */
+ bh = AFFS_INODE->i_ext_bh;
+ ext_key = be32_to_cpu(AFFS_TAIL(sb, bh)->extension);
+ if (ext < AFFS_INODE->i_extcnt)
+ goto read_ext;
+ if (ext > AFFS_INODE->i_extcnt)
+ BUG();
+ bh = affs_alloc_extblock(inode, bh, ext);
+ if (IS_ERR(bh))
+ return bh;
+ goto store_ext;
+ }
+
+ if (ext == 0) {
+ /* we seek back to the file header block */
+ ext_key = inode->i_ino;
+ goto read_ext;
+ }
+
+ if (ext >= AFFS_INODE->i_extcnt) {
+ struct buffer_head *prev_bh;
+
+ /* allocate a new extended block */
+ if (ext > AFFS_INODE->i_extcnt)
+ BUG();
+
+ /* get previous extended block */
+ prev_bh = affs_get_extblock(inode, ext - 1);
+ if (IS_ERR(prev_bh))
+ return prev_bh;
+ bh = affs_alloc_extblock(inode, prev_bh, ext);
+ affs_brelse(prev_bh);
+ if (IS_ERR(bh))
+ return bh;
+ goto store_ext;
+ }
+
+again:
+ /* check if there is an extended cache and whether it's large enough */
+ lc_idx = ext >> AFFS_INODE->i_lc_shift;
+ lc_off = ext & AFFS_INODE->i_lc_mask;
+
+ if (lc_idx >= AFFS_INODE->i_lc_size) {
+ int err;
+
+ err = affs_grow_extcache(inode, lc_idx);
+ if (err)
+ return ERR_PTR(err);
+ goto again;
+ }
+
+ /* every n'th key we find in the linear cache */
+ if (!lc_off) {
+ ext_key = AFFS_INODE->i_lc[lc_idx];
+ goto read_ext;
+ }
+
+ /* maybe it's still in the associative cache */
+ ac_idx = (ext - lc_idx - 1) & AFFS_AC_MASK;
+ if (AFFS_INODE->i_ac[ac_idx].ext == ext) {
+ ext_key = AFFS_INODE->i_ac[ac_idx].key;
+ goto read_ext;
+ }
+
+ /* try to find one of the previous extended blocks */
+ tmp = ext;
+ idx = ac_idx;
+ while (--tmp, --lc_off > 0) {
+ idx = (idx - 1) & AFFS_AC_MASK;
+ if (AFFS_INODE->i_ac[idx].ext == tmp) {
+ ext_key = AFFS_INODE->i_ac[idx].key;
+ goto find_ext;
+ }
+ }
+
+ /* fall back to the linear cache */
+ ext_key = AFFS_INODE->i_lc[lc_idx];
+find_ext:
+ /* read all extended blocks until we find the one we need */
+ //unlock cache
+ do {
+ bh = affs_bread(sb, ext_key);
+ if (!bh)
+ goto err_bread;
+ ext_key = be32_to_cpu(AFFS_TAIL(sb, bh)->extension);
+ affs_brelse(bh);
+ tmp++;
+ } while (tmp < ext);
+ //lock cache
+
+ /* store it in the associative cache */
+ // recalculate ac_idx?
+ AFFS_INODE->i_ac[ac_idx].ext = ext;
+ AFFS_INODE->i_ac[ac_idx].key = ext_key;
+
+read_ext:
+ /* finally read the right extended block */
+ //unlock cache
+ bh = affs_bread(sb, ext_key);
+ if (!bh)
+ goto err_bread;
+ //lock cache
+
+store_ext:
+ /* release old cached extended block and store the new one */
+ affs_brelse(AFFS_INODE->i_ext_bh);
+ AFFS_INODE->i_ext_last = ext;
+ AFFS_INODE->i_ext_bh = bh;
+ atomic_inc(&bh->b_count);
+
+ return bh;
+
+err_bread:
+ affs_brelse(bh);
+ return ERR_PTR(-EIO);
+}
+
+static int
+affs_get_block(struct inode *inode, long block, struct buffer_head *bh_result, int create)
+{
+ struct super_block *sb = inode->i_sb;
+ struct buffer_head *ext_bh;
+ u32 ext;
+
+ pr_debug("AFFS: get_block(%u, %ld)\n", (u32)inode->i_ino, block);
+
+ if (block < 0)
+ goto err_small;
+
+ if (block >= AFFS_INODE->i_blkcnt) {
+ if (block > AFFS_INODE->i_blkcnt || !create)
+ goto err_big;
+ } else
+ create = 0;
+
+ //lock cache
+ affs_lock_ext(inode);
+
+ ext = block / AFFS_SB->s_hashsize;
+ block -= ext * AFFS_SB->s_hashsize;
+ ext_bh = affs_get_extblock(inode, ext);
+ if (IS_ERR(ext_bh))
+ goto err_ext;
+ bh_result->b_blocknr = be32_to_cpu(AFFS_BLOCK(sb, ext_bh, block));
+ bh_result->b_dev = inode->i_dev;
+ bh_result->b_state |= (1UL << BH_Mapped);
+
+ if (create) {
+ u32 blocknr = affs_alloc_block(inode, ext_bh->b_blocknr);
+ if (!blocknr)
+ goto err_alloc;
+ bh_result->b_state |= (1UL << BH_New);
+ AFFS_INODE->mmu_private += AFFS_SB->s_data_blksize;
+ AFFS_INODE->i_blkcnt++;
+
+ /* store new block */
+ if (bh_result->b_blocknr)
+ affs_warning(sb, "get_block", "block already set (%x)", bh_result->b_blocknr);
+ AFFS_BLOCK(sb, ext_bh, block) = cpu_to_be32(blocknr);
+ AFFS_HEAD(ext_bh)->block_count = cpu_to_be32(block + 1);
+ affs_adjust_checksum(ext_bh, blocknr - bh_result->b_blocknr + 1);
+ bh_result->b_blocknr = blocknr;
+
+ if (!block) {
+ /* insert first block into header block */
+ u32 tmp = be32_to_cpu(AFFS_HEAD(ext_bh)->first_data);
+ if (tmp)
+ affs_warning(sb, "get_block", "first block already set (%d)", tmp);
+ AFFS_HEAD(ext_bh)->first_data = cpu_to_be32(blocknr);
+ affs_adjust_checksum(ext_bh, blocknr - tmp);
+ }
+ }
+
+ affs_brelse(ext_bh);
+ //unlock cache
+ affs_unlock_ext(inode);
+ return 0;
+
+err_small:
+ affs_error(inode->i_sb,"get_block","Block < 0");
+ return -EIO;
+err_big:
+ affs_error(inode->i_sb,"get_block","strange block request %d", block);
+ return -EIO;
+err_ext:
+ // unlock cache
+ affs_unlock_ext(inode);
+ return PTR_ERR(ext_bh);
+err_alloc:
+ brelse(ext_bh);
+ bh_result->b_state &= ~(1UL << BH_Mapped);
+ // unlock cache
+ affs_unlock_ext(inode);
+ return -ENOSPC;
+}
+
+static int affs_writepage(struct page *page)
+{
+ return block_write_full_page(page, affs_get_block);
+}
+static int affs_readpage(struct file *file, struct page *page)
+{
+ return block_read_full_page(page, affs_get_block);
+}
+static int affs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
+{
+ return cont_prepare_write(page, from, to, affs_get_block,
+ &page->mapping->host->u.affs_i.mmu_private);
+}
+static int _affs_bmap(struct address_space *mapping, long block)
+{
+ return generic_block_bmap(mapping,block,affs_get_block);
+}
+struct address_space_operations affs_aops = {
+ readpage: affs_readpage,
+ writepage: affs_writepage,
+ sync_page: block_sync_page,
+ prepare_write: affs_prepare_write,
+ commit_write: generic_commit_write,
+ bmap: _affs_bmap
+};
+
+static inline struct buffer_head *
+affs_bread_ino(struct inode *inode, int block, int create)
+{
+ struct buffer_head *bh, tmp_bh;
+ int err;
+
+ tmp_bh.b_state = 0;
+ err = affs_get_block(inode, block, &tmp_bh, create);
+ if (!err) {
+ bh = affs_bread(inode->i_sb, tmp_bh.b_blocknr);
+ if (bh) {
+ bh->b_state |= tmp_bh.b_state;
+ return bh;
+ }
+ err = -EIO;
+ }
+ return ERR_PTR(err);
+}
+
+static inline struct buffer_head *
+affs_getzeroblk_ino(struct inode *inode, int block)
+{
+ struct buffer_head *bh, tmp_bh;
+ int err;
+
+ tmp_bh.b_state = 0;
+ err = affs_get_block(inode, block, &tmp_bh, 1);
+ if (!err) {
+ bh = affs_getzeroblk(inode->i_sb, tmp_bh.b_blocknr);
+ if (bh) {
+ bh->b_state |= tmp_bh.b_state;
+ return bh;
+ }
+ err = -EIO;
+ }
+ return ERR_PTR(err);
+}
+
+static inline struct buffer_head *
+affs_getemptyblk_ino(struct inode *inode, int block)
+{
+ struct buffer_head *bh, tmp_bh;
+ int err;
+
+ tmp_bh.b_state = 0;
+ err = affs_get_block(inode, block, &tmp_bh, 1);
+ if (!err) {
+ bh = affs_getemptyblk(inode->i_sb, tmp_bh.b_blocknr);
+ if (bh) {
+ bh->b_state |= tmp_bh.b_state;
+ return bh;
+ }
+ err = -EIO;
+ }
+ return ERR_PTR(err);
+}
+
+static ssize_t
+affs_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
+{
+ ssize_t retval;
+
+ retval = generic_file_write (file, buf, count, ppos);
+ if (retval >0) {
+ struct inode *inode = file->f_dentry->d_inode;
+ inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+ }
+ return retval;
+}
+
+static int
+affs_do_readpage_ofs(struct file *file, struct page *page, unsigned from, unsigned to)
+{
+ struct inode *inode = page->mapping->host;
+ struct super_block *sb = inode->i_sb;
+ struct buffer_head *bh;
+ char *data;
+ u32 bidx, boff, bsize;
+ u32 tmp;
+
+ pr_debug("AFFS: read_page(%u, %ld, %d, %d)\n", (u32)inode->i_ino, page->index, from, to);
+ if (from > to || to > PAGE_CACHE_SIZE)
+ BUG();
+ data = page_address(page);
+ bsize = AFFS_SB->s_data_blksize;
+ tmp = (page->index << PAGE_CACHE_SHIFT) + from;
+ bidx = tmp / bsize;
+ boff = tmp % bsize;
+
+ while (from < to) {
+ bh = affs_bread_ino(inode, bidx, 0);
+ if (IS_ERR(bh))
+ return PTR_ERR(bh);
+ tmp = min(bsize - boff, to - from);
+ if (from + tmp > to || tmp > bsize)
+ BUG();
+ memcpy(data + from, AFFS_DATA(bh) + boff, tmp);
+ affs_brelse(bh);
+ bidx++;
+ from += tmp;
+ boff = 0;
+ }
+ return 0;
+}
+
+static int
+affs_extent_file_ofs(struct inode *inode, u32 newsize)
+{
+ struct super_block *sb = inode->i_sb;
+ struct buffer_head *bh, *prev_bh;
+ u32 bidx, boff;
+ u32 size, bsize;
+ u32 tmp;
+
+ pr_debug("AFFS: extent_file(%u, %d)\n", (u32)inode->i_ino, newsize);
+ bsize = AFFS_SB->s_data_blksize;
+ bh = NULL;
+ size = AFFS_INODE->mmu_private;
+ bidx = size / bsize;
+ boff = size % bsize;
+ if (boff) {
+ bh = affs_bread_ino(inode, bidx, 0);
+ if (IS_ERR(bh))
+ return PTR_ERR(bh);
+ tmp = min(bsize - boff, newsize - size);
+ if (boff + tmp > bsize || tmp > bsize)
+ BUG();
+ memset(AFFS_DATA(bh) + boff, 0, tmp);
+ AFFS_DATA_HEAD(bh)->size = cpu_to_be32(be32_to_cpu(AFFS_DATA_HEAD(bh)->size) + tmp);
+ affs_fix_checksum(sb, bh);
+ mark_buffer_dirty_inode(bh, inode);
+ size += tmp;
+ bidx++;
+ } else if (bidx) {
+ bh = affs_bread_ino(inode, bidx - 1, 0);
+ if (IS_ERR(bh))
+ return PTR_ERR(bh);
+ }
+
+ while (size < newsize) {
+ prev_bh = bh;
+ bh = affs_getzeroblk_ino(inode, bidx);
+ if (IS_ERR(bh))
+ goto out;
+ tmp = min(bsize, newsize - size);
+ if (tmp > bsize)
+ BUG();
+ AFFS_DATA_HEAD(bh)->ptype = cpu_to_be32(T_DATA);
+ AFFS_DATA_HEAD(bh)->key = cpu_to_be32(inode->i_ino);
+ AFFS_DATA_HEAD(bh)->sequence = cpu_to_be32(bidx);
+ AFFS_DATA_HEAD(bh)->size = cpu_to_be32(tmp);
+ affs_fix_checksum(sb, bh);
+ bh->b_state &= ~(1UL << BH_New);
+ mark_buffer_dirty_inode(bh, inode);
+ if (prev_bh) {
+ u32 tmp = be32_to_cpu(AFFS_DATA_HEAD(prev_bh)->next);
+ if (tmp)
+ affs_warning(sb, "extent_file_ofs", "next block already set for %d (%d)", bidx, tmp);
+ AFFS_DATA_HEAD(prev_bh)->next = cpu_to_be32(bh->b_blocknr);
+ affs_adjust_checksum(prev_bh, bh->b_blocknr - tmp);
+ mark_buffer_dirty_inode(prev_bh, inode);
+ affs_brelse(prev_bh);
+ }
+ size += bsize;
+ bidx++;
+ }
+ affs_brelse(bh);
+ inode->i_size = AFFS_INODE->mmu_private = newsize;
+ return 0;
+
+out:
+ inode->i_size = AFFS_INODE->mmu_private = size;
+ return PTR_ERR(bh);
+}
+
+static int
+affs_readpage_ofs(struct file *file, struct page *page)
+{
+ struct inode *inode = page->mapping->host;
+ u32 to;
+ int err;
+
+ pr_debug("AFFS: read_page(%u, %ld)\n", (u32)inode->i_ino, page->index);
+ to = PAGE_CACHE_SIZE;
+ if (((page->index + 1) << PAGE_CACHE_SHIFT) > inode->i_size) {
+ to = inode->i_size & ~PAGE_CACHE_MASK;
+ memset(page_address(page) + to, 0, PAGE_CACHE_SIZE - to);
+ }
+
+ err = affs_do_readpage_ofs(file, page, 0, to);
+ if (!err)
+ SetPageUptodate(page);
+ UnlockPage(page);
+ return err;
+}
+
+static int affs_prepare_write_ofs(struct file *file, struct page *page, unsigned from, unsigned to)
+{
+ struct inode *inode = page->mapping->host;
+ u32 size, offset;
+ u32 tmp;
+ int err = 0;
+
+ pr_debug("AFFS: prepare_write(%u, %ld, %d, %d)\n", (u32)inode->i_ino, page->index, from, to);
+ offset = page->index << PAGE_CACHE_SHIFT;
+ if (offset + from > AFFS_INODE->mmu_private) {
+ err = affs_extent_file_ofs(inode, offset + from);
+ if (err)
+ return err;
+ }
+ size = inode->i_size;
+
+ if (Page_Uptodate(page))
+ return 0;
+
+ if (from) {
+ err = affs_do_readpage_ofs(file, page, 0, from);
+ if (err)
+ return err;
+ }
+ if (to < PAGE_CACHE_SIZE) {
+ memset(page_address(page) + to, 0, PAGE_CACHE_SIZE - to);
+ if (size > offset + to) {
+ if (size < offset + PAGE_CACHE_SIZE)
+ tmp = size & ~PAGE_CACHE_MASK;
+ else
+ tmp = PAGE_CACHE_SIZE;
+ err = affs_do_readpage_ofs(file, page, to, tmp);
+ }
+ }
+ return err;
+}
+
+static int affs_commit_write_ofs(struct file *file, struct page *page, unsigned from, unsigned to)
+{
+ struct inode *inode = page->mapping->host;
+ struct super_block *sb = inode->i_sb;
+ struct buffer_head *bh, *prev_bh;
+ char *data;
+ u32 bidx, boff, bsize;
+ u32 tmp;
+ int written;
+
+ pr_debug("AFFS: commit_write(%u, %ld, %d, %d)\n", (u32)inode->i_ino, page->index, from, to);
+ bsize = AFFS_SB->s_data_blksize;
+ data = page_address(page);
+
+ bh = NULL;
+ written = 0;
+ tmp = (page->index << PAGE_CACHE_SHIFT) + from;
+ bidx = tmp / bsize;
+ boff = tmp % bsize;
+ if (boff) {
+ bh = affs_bread_ino(inode, bidx, 0);
+ if (IS_ERR(bh))
+ return PTR_ERR(bh);
+ tmp = min(bsize - boff, to - from);
+ if (boff + tmp > bsize || tmp > bsize)
+ BUG();
+ memcpy(AFFS_DATA(bh) + boff, data + from, tmp);
+ AFFS_DATA_HEAD(bh)->size = cpu_to_be32(be32_to_cpu(AFFS_DATA_HEAD(bh)->size) + tmp);
+ affs_fix_checksum(sb, bh);
+ mark_buffer_dirty_inode(bh, inode);
+ written += tmp;
+ from += tmp;
+ bidx++;
+ } else if (bidx) {
+ bh = affs_bread_ino(inode, bidx - 1, 0);
+ if (IS_ERR(bh))
+ return PTR_ERR(bh);
+ }
+ while (from + bsize <= to) {
+ prev_bh = bh;
+ bh = affs_getemptyblk_ino(inode, bidx);
+ if (IS_ERR(bh))
+ goto out;
+ memcpy(AFFS_DATA(bh), data + from, bsize);
+ if (bh->b_state & (1UL << BH_New)) {
+ AFFS_DATA_HEAD(bh)->ptype = cpu_to_be32(T_DATA);
+ AFFS_DATA_HEAD(bh)->key = cpu_to_be32(inode->i_ino);
+ AFFS_DATA_HEAD(bh)->sequence = cpu_to_be32(bidx);
+ AFFS_DATA_HEAD(bh)->size = cpu_to_be32(bsize);
+ AFFS_DATA_HEAD(bh)->next = 0;
+ bh->b_state &= ~(1UL << BH_New);
+ if (prev_bh) {
+ u32 tmp = be32_to_cpu(AFFS_DATA_HEAD(prev_bh)->next);
+ if (tmp)
+ affs_warning(sb, "commit_write_ofs", "next block already set for %d (%d)", bidx, tmp);
+ AFFS_DATA_HEAD(prev_bh)->next = cpu_to_be32(bh->b_blocknr);
+ affs_adjust_checksum(prev_bh, bh->b_blocknr - tmp);
+ mark_buffer_dirty_inode(prev_bh, inode);
+ }
+ }
+ affs_brelse(prev_bh);
+ affs_fix_checksum(sb, bh);
+ mark_buffer_dirty_inode(bh, inode);
+ written += bsize;
+ from += bsize;
+ bidx++;
+ }
+ if (from < to) {
+ prev_bh = bh;
+ bh = affs_bread_ino(inode, bidx, 1);
+ if (IS_ERR(bh))
+ goto out;
+ tmp = min(bsize, to - from);
+ if (tmp > bsize)
+ BUG();
+ memcpy(AFFS_DATA(bh), data + from, tmp);
+ if (bh->b_state & (1UL << BH_New)) {
+ AFFS_DATA_HEAD(bh)->ptype = cpu_to_be32(T_DATA);
+ AFFS_DATA_HEAD(bh)->key = cpu_to_be32(inode->i_ino);
+ AFFS_DATA_HEAD(bh)->sequence = cpu_to_be32(bidx);
+ AFFS_DATA_HEAD(bh)->size = cpu_to_be32(tmp);
+ AFFS_DATA_HEAD(bh)->next = 0;
+ bh->b_state &= ~(1UL << BH_New);
+ if (prev_bh) {
+ u32 tmp = be32_to_cpu(AFFS_DATA_HEAD(prev_bh)->next);
+ if (tmp)
+ affs_warning(sb, "commit_write_ofs", "next block already set for %d (%d)", bidx, tmp);
+ AFFS_DATA_HEAD(prev_bh)->next = cpu_to_be32(bh->b_blocknr);
+ affs_adjust_checksum(prev_bh, bh->b_blocknr - tmp);
+ mark_buffer_dirty_inode(prev_bh, inode);
+ }
+ } else if (be32_to_cpu(AFFS_DATA_HEAD(bh)->size) < tmp)
+ AFFS_DATA_HEAD(bh)->size = cpu_to_be32(tmp);
+ affs_brelse(prev_bh);
+ affs_fix_checksum(sb, bh);
+ mark_buffer_dirty_inode(bh, inode);
+ written += tmp;
+ from += tmp;
+ bidx++;
+ }
+ SetPageUptodate(page);
+
+done:
+ affs_brelse(bh);
+ tmp = (page->index << PAGE_CACHE_SHIFT) + from;
+ if (tmp > inode->i_size)
+ inode->i_size = AFFS_INODE->mmu_private = tmp;
+
+ return written;
+
+out:
+ bh = prev_bh;
+ if (!written)
+ written = PTR_ERR(bh);
+ goto done;
+}
+
+struct address_space_operations affs_aops_ofs = {
+ readpage: affs_readpage_ofs,
+ //writepage: affs_writepage_ofs,
+ //sync_page: affs_sync_page_ofs,
+ prepare_write: affs_prepare_write_ofs,
+ commit_write: affs_commit_write_ofs
+};
+
+/* Free any preallocated blocks. */
+
+void
+affs_free_prealloc(struct inode *inode)
+{
+ struct super_block *sb = inode->i_sb;
+
+ pr_debug("AFFS: free_prealloc(ino=%lu)\n", inode->i_ino);
+
+ while (inode->u.affs_i.i_pa_cnt) {
+ inode->u.affs_i.i_pa_cnt--;
+ affs_free_block(sb, ++inode->u.affs_i.i_lastalloc);
+ }
+}
+
+/* Truncate (or enlarge) a file to the requested size. */
+
+void
+affs_truncate(struct inode *inode)
+{
+ struct super_block *sb = inode->i_sb;
+ u32 ext, ext_key;
+ u32 last_blk, blkcnt, blk;
+ u32 size;
+ struct buffer_head *ext_bh;
+ int i;
+
+ pr_debug("AFFS: truncate(inode=%d, oldsize=%u, newsize=%u)\n",
+ (u32)inode->i_ino, (u32)AFFS_INODE->mmu_private, (u32)inode->i_size);
+
+ last_blk = 0;
+ ext = 0;
+ if (inode->i_size) {
+ last_blk = ((u32)inode->i_size - 1) / AFFS_SB->s_data_blksize;
+ ext = last_blk / AFFS_SB->s_hashsize;
+ }
+
+ if (inode->i_size > AFFS_INODE->mmu_private) {
+ struct address_space *mapping = inode->i_mapping;
+ struct page *page;
+ u32 size = inode->i_size - 1;
+ int res;
+
+ page = grab_cache_page(mapping, size >> PAGE_CACHE_SHIFT);
+ if (!page)
+ return;
+ size = (size & (PAGE_CACHE_SIZE - 1)) + 1;
+ res = mapping->a_ops->prepare_write(NULL, page, size, size);
+ if (!res)
+ res = mapping->a_ops->commit_write(NULL, page, size, size);
+ UnlockPage(page);
+ page_cache_release(page);
+ mark_inode_dirty(inode);
+ return;
+ } else if (inode->i_size == AFFS_INODE->mmu_private)
+ return;
+
+ // lock cache
+ ext_bh = affs_get_extblock(inode, ext);
+ if (IS_ERR(ext_bh)) {
+ affs_warning(sb, "truncate", "unexpected read error for ext block %u (%d)",
+ ext, PTR_ERR(ext_bh));
+ return;
+ }
+ if (AFFS_INODE->i_lc) {
+ /* clear linear cache */
+ i = (ext + 1) >> AFFS_INODE->i_lc_shift;
+ if (AFFS_INODE->i_lc_size > i) {
+ AFFS_INODE->i_lc_size = i;
+ for (; i < AFFS_LC_SIZE; i++)
+ AFFS_INODE->i_lc[i] = 0;
+ }
+ /* clear associative cache */
+ for (i = 0; i < AFFS_AC_SIZE; i++)
+ if (AFFS_INODE->i_ac[i].ext >= ext)
+ AFFS_INODE->i_ac[i].ext = 0;
+ }
+ ext_key = be32_to_cpu(AFFS_TAIL(sb, ext_bh)->extension);
+
+ blkcnt = AFFS_INODE->i_blkcnt;
+ i = 0;
+ blk = last_blk;
+ if (inode->i_size) {
+ i = last_blk % AFFS_SB->s_hashsize + 1;
+ blk++;
+ } else
+ AFFS_HEAD(ext_bh)->first_data = 0;
+ size = AFFS_SB->s_hashsize;
+ if (size > blkcnt - blk + i)
+ size = blkcnt - blk + i;
+ for (; i < size; i++, blk++) {
+ affs_free_block(sb, be32_to_cpu(AFFS_BLOCK(sb, ext_bh, i)));
+ AFFS_BLOCK(sb, ext_bh, i) = 0;
+ }
+ AFFS_TAIL(sb, ext_bh)->extension = 0;
+ affs_fix_checksum(sb, ext_bh);
+ mark_buffer_dirty_inode(ext_bh, inode);
+ affs_brelse(ext_bh);
+
+ if (inode->i_size) {
+ AFFS_INODE->i_blkcnt = last_blk + 1;
+ AFFS_INODE->i_extcnt = ext + 1;
+ if (AFFS_SB->s_flags & SF_OFS) {
+ struct buffer_head *bh = affs_bread_ino(inode, last_blk, 0);
+ u32 tmp;
+ if (IS_ERR(bh)) {
+ affs_warning(sb, "truncate", "unexpected read error for last block %u (%d)",
+ last_blk, PTR_ERR(bh));
+ return;
+ }
+ tmp = be32_to_cpu(AFFS_DATA_HEAD(bh)->next);
+ AFFS_DATA_HEAD(bh)->next = 0;
+ affs_adjust_checksum(bh, -tmp);
+ affs_brelse(bh);
+ }
+ } else {
+ AFFS_INODE->i_blkcnt = 0;
+ AFFS_INODE->i_extcnt = 1;
+ }
+ AFFS_INODE->mmu_private = inode->i_size;
+ // unlock cache
+
+ while (ext_key) {
+ ext_bh = affs_bread(sb, ext_key);
+ size = AFFS_SB->s_hashsize;
+ if (size > blkcnt - blk)
+ size = blkcnt - blk;
+ for (i = 0; i < size; i++, blk++)
+ affs_free_block(sb, be32_to_cpu(AFFS_BLOCK(sb, ext_bh, i)));
+ affs_free_block(sb, ext_key);
+ ext_key = be32_to_cpu(AFFS_TAIL(sb, ext_bh)->extension);
+ affs_brelse(ext_bh);
+ }
+ affs_free_prealloc(inode);
+}
diff --git a/fs/affs/inode.c b/fs/affs/inode.c
new file mode 100644
index 00000000000000..b7f9365a972ff1
--- /dev/null
+++ b/fs/affs/inode.c
@@ -0,0 +1,417 @@
+/*
+ * linux/fs/affs/inode.c
+ *
+ * (c) 1996 Hans-Joachim Widmaier - Rewritten
+ *
+ * (C) 1993 Ray Burr - Modified for Amiga FFS filesystem.
+ *
+ * (C) 1992 Eric Youngdale Modified for ISO9660 filesystem.
+ *
+ * (C) 1991 Linus Torvalds - minix filesystem
+ */
+
+#include <asm/div64.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/sched.h>
+#include <linux/affs_fs.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/genhd.h>
+#include <linux/amigaffs.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <linux/module.h>
+
+extern struct inode_operations affs_symlink_inode_operations;
+extern struct timezone sys_tz;
+
+void
+affs_read_inode(struct inode *inode)
+{
+ struct super_block *sb = inode->i_sb;
+ struct buffer_head *bh;
+ struct affs_head *head;
+ struct affs_tail *tail;
+ u32 block;
+ u32 size;
+ u32 prot;
+ u16 id;
+
+ pr_debug("AFFS: read_inode(%lu)\n",inode->i_ino);
+
+ block = inode->i_ino;
+ bh = affs_bread(sb, block);
+ if (!bh) {
+ affs_warning(sb, "read_inode", "Cannot read block %d", block);
+ goto bad_inode;
+ }
+ if (affs_checksum_block(sb, bh) || be32_to_cpu(AFFS_HEAD(bh)->ptype) != T_SHORT) {
+ affs_warning(sb,"read_inode",
+ "Checksum or type (ptype=%d) error on inode %d",
+ AFFS_HEAD(bh)->ptype, block);
+ goto bad_inode;
+ }
+
+ head = AFFS_HEAD(bh);
+ tail = AFFS_TAIL(sb, bh);
+ prot = be32_to_cpu(tail->protect);
+
+ inode->i_size = 0;
+ inode->i_nlink = 1;
+ inode->i_mode = 0;
+ memset(AFFS_INODE, 0, sizeof(*AFFS_INODE));
+ init_MUTEX(&AFFS_INODE->i_link_lock);
+ init_MUTEX(&AFFS_INODE->i_ext_lock);
+ AFFS_INODE->i_extcnt = 1;
+ AFFS_INODE->i_ext_last = ~1;
+ AFFS_INODE->i_protect = prot;
+
+ if (AFFS_SB->s_flags & SF_SETMODE)
+ inode->i_mode = AFFS_SB->s_mode;
+ else
+ inode->i_mode = prot_to_mode(prot);
+
+ id = be16_to_cpu(tail->uid);
+ if (id == 0 || AFFS_SB->s_flags & SF_SETUID)
+ inode->i_uid = AFFS_SB->s_uid;
+ else if (id == 0xFFFF && AFFS_SB->s_flags & SF_MUFS)
+ inode->i_uid = 0;
+ else
+ inode->i_uid = id;
+
+ id = be16_to_cpu(tail->gid);
+ if (id == 0 || AFFS_SB->s_flags & SF_SETGID)
+ inode->i_gid = AFFS_SB->s_gid;
+ else if (id == 0xFFFF && AFFS_SB->s_flags & SF_MUFS)
+ inode->i_gid = 0;
+ else
+ inode->i_gid = id;
+
+ switch (be32_to_cpu(tail->stype)) {
+ case ST_ROOT:
+ inode->i_uid = AFFS_SB->s_uid;
+ inode->i_gid = AFFS_SB->s_gid;
+ /* fall through */
+ case ST_USERDIR:
+ if (be32_to_cpu(tail->stype) == ST_USERDIR ||
+ AFFS_SB->s_flags & SF_SETMODE) {
+ if (inode->i_mode & S_IRUSR)
+ inode->i_mode |= S_IXUSR;
+ if (inode->i_mode & S_IRGRP)
+ inode->i_mode |= S_IXGRP;
+ if (inode->i_mode & S_IROTH)
+ inode->i_mode |= S_IXOTH;
+ inode->i_mode |= S_IFDIR;
+ } else
+ inode->i_mode = S_IRUGO | S_IXUGO | S_IWUSR | S_IFDIR;
+ if (tail->link_chain)
+ inode->i_nlink = 2;
+ /* Maybe it should be controlled by mount parameter? */
+ //inode->i_mode |= S_ISVTX;
+ inode->i_op = &affs_dir_inode_operations;
+ inode->i_fop = &affs_dir_operations;
+ break;
+ case ST_LINKDIR:
+#if 0
+ affs_warning(sb, "read_inode", "inode is LINKDIR");
+ goto bad_inode;
+#else
+ inode->i_mode |= S_IFDIR;
+ inode->i_op = NULL;
+ inode->i_fop = NULL;
+ break;
+#endif
+ case ST_LINKFILE:
+ affs_warning(sb, "read_inode", "inode is LINKFILE");
+ goto bad_inode;
+ case ST_FILE:
+ size = be32_to_cpu(tail->size);
+ inode->i_mode |= S_IFREG;
+ AFFS_INODE->mmu_private = inode->i_size = size;
+ if (inode->i_size) {
+ AFFS_INODE->i_blkcnt = (size - 1) /
+ AFFS_SB->s_data_blksize + 1;
+ AFFS_INODE->i_extcnt = (AFFS_INODE->i_blkcnt - 1) /
+ AFFS_SB->s_hashsize + 1;
+ }
+ if (tail->link_chain)
+ inode->i_nlink = 2;
+ inode->i_mapping->a_ops = (AFFS_SB->s_flags & SF_OFS) ? &affs_aops_ofs : &affs_aops;
+ inode->i_op = &affs_file_inode_operations;
+ inode->i_fop = &affs_file_operations;
+ break;
+ case ST_SOFTLINK:
+ inode->i_mode |= S_IFLNK;
+ inode->i_op = &affs_symlink_inode_operations;
+ inode->i_data.a_ops = &affs_symlink_aops;
+ break;
+ }
+
+ inode->i_mtime = inode->i_atime = inode->i_ctime
+ = (be32_to_cpu(tail->change.days) * (24 * 60 * 60) +
+ be32_to_cpu(tail->change.mins) * 60 +
+ be32_to_cpu(tail->change.ticks) / 50 +
+ ((8 * 365 + 2) * 24 * 60 * 60)) +
+ sys_tz.tz_minuteswest * 60;
+ affs_brelse(bh);
+ return;
+
+bad_inode:
+ make_bad_inode(inode);
+ affs_brelse(bh);
+ return;
+}
+
+void
+affs_write_inode(struct inode *inode, int unused)
+{
+ struct super_block *sb = inode->i_sb;
+ struct buffer_head *bh;
+ struct affs_tail *tail;
+ uid_t uid;
+ gid_t gid;
+
+ pr_debug("AFFS: write_inode(%lu)\n",inode->i_ino);
+
+ if (!inode->i_nlink)
+ // possibly free block
+ return;
+ lock_kernel();
+ bh = affs_bread(sb, inode->i_ino);
+ if (!bh) {
+ affs_error(sb,"write_inode","Cannot read block %lu",inode->i_ino);
+ unlock_kernel();
+ return;
+ }
+ tail = AFFS_TAIL(sb, bh);
+ if (tail->stype == be32_to_cpu(ST_ROOT)) {
+ secs_to_datestamp(inode->i_mtime,&AFFS_ROOT_TAIL(sb, bh)->root_change);
+ } else {
+ tail->protect = cpu_to_be32(AFFS_INODE->i_protect);
+ tail->size = cpu_to_be32(inode->i_size);
+ secs_to_datestamp(inode->i_mtime,&tail->change);
+ if (!(inode->i_ino == AFFS_SB->s_root_block)) {
+ uid = inode->i_uid;
+ gid = inode->i_gid;
+ if (sb->u.affs_sb.s_flags & SF_MUFS) {
+ if (inode->i_uid == 0 || inode->i_uid == 0xFFFF)
+ uid = inode->i_uid ^ ~0;
+ if (inode->i_gid == 0 || inode->i_gid == 0xFFFF)
+ gid = inode->i_gid ^ ~0;
+ }
+ if (!(sb->u.affs_sb.s_flags & SF_SETUID))
+ tail->uid = cpu_to_be16(uid);
+ if (!(sb->u.affs_sb.s_flags & SF_SETGID))
+ tail->gid = cpu_to_be16(gid);
+ }
+ }
+ affs_fix_checksum(sb, bh);
+ mark_buffer_dirty_inode(bh, inode);
+ affs_brelse(bh);
+ unlock_kernel();
+}
+
+int
+affs_notify_change(struct dentry *dentry, struct iattr *attr)
+{
+ struct inode *inode = dentry->d_inode;
+ int error;
+
+ pr_debug("AFFS: notify_change(%lu,0x%x)\n",inode->i_ino,attr->ia_valid);
+
+ error = inode_change_ok(inode,attr);
+ if (error)
+ goto out;
+
+ if (((attr->ia_valid & ATTR_UID) && (inode->i_sb->u.affs_sb.s_flags & SF_SETUID)) ||
+ ((attr->ia_valid & ATTR_GID) && (inode->i_sb->u.affs_sb.s_flags & SF_SETGID)) ||
+ ((attr->ia_valid & ATTR_MODE) &&
+ (inode->i_sb->u.affs_sb.s_flags & (SF_SETMODE | SF_IMMUTABLE)))) {
+ if (!(inode->i_sb->u.affs_sb.s_flags & SF_QUIET))
+ error = -EPERM;
+ goto out;
+ }
+
+ inode_setattr(inode, attr);
+ if (!error && (attr->ia_valid & ATTR_MODE))
+ mode_to_prot(inode);
+out:
+ return error;
+}
+
+void
+affs_put_inode(struct inode *inode)
+{
+ pr_debug("AFFS: put_inode(ino=%lu, nlink=%u)\n", inode->i_ino, inode->i_nlink);
+ lock_kernel();
+ affs_free_prealloc(inode);
+ if (atomic_read(&inode->i_count) == 1) {
+ if (inode->i_size != AFFS_INODE->mmu_private)
+ affs_truncate(inode);
+ //if (inode->i_nlink)
+ // affs_clear_inode(inode);
+ }
+ unlock_kernel();
+}
+
+void
+affs_delete_inode(struct inode *inode)
+{
+ pr_debug("AFFS: delete_inode(ino=%lu, nlink=%u)\n", inode->i_ino, inode->i_nlink);
+ lock_kernel();
+ inode->i_size = 0;
+ if (S_ISREG(inode->i_mode))
+ affs_truncate(inode);
+ clear_inode(inode);
+ affs_free_block(inode->i_sb, inode->i_ino);
+ unlock_kernel();
+}
+
+void
+affs_clear_inode(struct inode *inode)
+{
+ unsigned long cache_page = (unsigned long) inode->u.affs_i.i_lc;
+
+ pr_debug("AFFS: clear_inode(ino=%lu, nlink=%u)\n", inode->i_ino, inode->i_nlink);
+ if (cache_page) {
+ pr_debug("AFFS: freeing ext cache\n");
+ inode->u.affs_i.i_lc = NULL;
+ inode->u.affs_i.i_ac = NULL;
+ free_page(cache_page);
+ }
+ affs_brelse(AFFS_INODE->i_ext_bh);
+ AFFS_INODE->i_ext_last = ~1;
+ AFFS_INODE->i_ext_bh = NULL;
+}
+
+struct inode *
+affs_new_inode(struct inode *dir)
+{
+ struct super_block *sb = dir->i_sb;
+ struct inode *inode;
+ u32 block;
+ struct buffer_head *bh;
+
+ if (!(inode = new_inode(sb)))
+ goto err_inode;
+
+ if (!(block = affs_alloc_block(dir, dir->i_ino)))
+ goto err_block;
+
+ bh = affs_getzeroblk(sb, block);
+ if (!bh)
+ goto err_bh;
+ mark_buffer_dirty_inode(bh, inode);
+ affs_brelse(bh);
+
+ inode->i_uid = current->fsuid;
+ inode->i_gid = current->fsgid;
+ inode->i_ino = block;
+ inode->i_nlink = 1;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+ memset(AFFS_INODE, 0, sizeof(*AFFS_INODE));
+ AFFS_INODE->i_extcnt = 1;
+ AFFS_INODE->i_ext_last = ~1;
+ init_MUTEX(&AFFS_INODE->i_link_lock);
+ init_MUTEX(&AFFS_INODE->i_ext_lock);
+
+ insert_inode_hash(inode);
+
+ return inode;
+
+err_bh:
+ affs_free_block(sb, block);
+err_block:
+ iput(inode);
+err_inode:
+ return NULL;
+}
+
+/*
+ * Add an entry to a directory. Create the header block
+ * and insert it into the hash table.
+ */
+
+int
+affs_add_entry(struct inode *dir, struct inode *inode, struct dentry *dentry, s32 type)
+{
+ struct super_block *sb = dir->i_sb;
+ struct buffer_head *inode_bh = NULL;
+ struct buffer_head *bh = NULL;
+ u32 block = 0;
+ int retval;
+
+ pr_debug("AFFS: add_entry(dir=%u, inode=%u, \"%*s\", type=%d)\n", (u32)dir->i_ino,
+ (u32)inode->i_ino, (int)dentry->d_name.len, dentry->d_name.name, type);
+
+ retval = -EIO;
+ bh = affs_bread(sb, inode->i_ino);
+ if (!bh)
+ goto done;
+
+ affs_lock_link(inode);
+ switch (type) {
+ case ST_LINKFILE:
+ case ST_LINKDIR:
+ inode_bh = bh;
+ retval = -ENOSPC;
+ block = affs_alloc_block(dir, dir->i_ino);
+ if (!block)
+ goto err;
+ retval = -EIO;
+ bh = affs_getzeroblk(sb, block);
+ if (!bh)
+ goto err;
+ break;
+ default:
+ break;
+ }
+
+ AFFS_HEAD(bh)->ptype = cpu_to_be32(T_SHORT);
+ AFFS_HEAD(bh)->key = cpu_to_be32(bh->b_blocknr);
+ affs_copy_name(AFFS_TAIL(sb, bh)->name, dentry);
+ AFFS_TAIL(sb, bh)->stype = cpu_to_be32(type);
+ AFFS_TAIL(sb, bh)->parent = cpu_to_be32(dir->i_ino);
+
+ if (inode_bh) {
+ u32 chain;
+ chain = AFFS_TAIL(sb, inode_bh)->link_chain;
+ AFFS_TAIL(sb, bh)->original = cpu_to_be32(inode->i_ino);
+ AFFS_TAIL(sb, bh)->link_chain = chain;
+ AFFS_TAIL(sb, inode_bh)->link_chain = cpu_to_be32(block);
+ affs_adjust_checksum(inode_bh, block - be32_to_cpu(chain));
+ mark_buffer_dirty_inode(inode_bh, inode);
+ inode->i_nlink = 2;
+ atomic_inc(&inode->i_count);
+ }
+ affs_fix_checksum(sb, bh);
+ mark_buffer_dirty_inode(bh, inode);
+ dentry->d_fsdata = (void *)bh->b_blocknr;
+
+ affs_lock_dir(dir);
+ retval = affs_insert_hash(dir, bh);
+ mark_buffer_dirty_inode(bh, inode);
+ affs_unlock_dir(dir);
+ affs_unlock_link(inode);
+
+ d_instantiate(dentry, inode);
+done:
+ affs_brelse(inode_bh);
+ affs_brelse(bh);
+ return retval;
+err:
+ if (block)
+ affs_free_block(sb, block);
+ affs_unlock_link(inode);
+ goto done;
+}
+MODULE_LICENSE("GPL");
diff --git a/fs/affs/namei.c b/fs/affs/namei.c
new file mode 100644
index 00000000000000..cd32c3b8150b2a
--- /dev/null
+++ b/fs/affs/namei.c
@@ -0,0 +1,470 @@
+/*
+ * linux/fs/affs/namei.c
+ *
+ * (c) 1996 Hans-Joachim Widmaier - Rewritten
+ *
+ * (C) 1993 Ray Burr - Modified for Amiga FFS filesystem.
+ *
+ * (C) 1991 Linus Torvalds - minix filesystem
+ */
+
+#include <linux/sched.h>
+#include <linux/affs_fs.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/locks.h>
+#include <linux/amigaffs.h>
+#include <asm/uaccess.h>
+
+#include <linux/errno.h>
+
+typedef int (*toupper_t)(int);
+
+extern struct inode_operations affs_symlink_inode_operations;
+
+static int affs_toupper(int ch);
+static int affs_hash_dentry(struct dentry *, struct qstr *);
+static int affs_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
+static int affs_intl_toupper(int ch);
+static int affs_intl_hash_dentry(struct dentry *, struct qstr *);
+static int affs_intl_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
+
+struct dentry_operations affs_dentry_operations = {
+ d_hash: affs_hash_dentry,
+ d_compare: affs_compare_dentry,
+};
+
+struct dentry_operations affs_intl_dentry_operations = {
+ d_hash: affs_intl_hash_dentry,
+ d_compare: affs_intl_compare_dentry,
+};
+
+
+/* Simple toupper() for DOS\1 */
+
+static int
+affs_toupper(int ch)
+{
+ return ch >= 'a' && ch <= 'z' ? ch -= ('a' - 'A') : ch;
+}
+
+/* International toupper() for DOS\3 ("international") */
+
+static int
+affs_intl_toupper(int ch)
+{
+ return (ch >= 'a' && ch <= 'z') || (ch >= 0xE0
+ && ch <= 0xFE && ch != 0xF7) ?
+ ch - ('a' - 'A') : ch;
+}
+
+static inline toupper_t
+affs_get_toupper(struct super_block *sb)
+{
+ return AFFS_SB->s_flags & SF_INTL ? affs_intl_toupper : affs_toupper;
+}
+
+/*
+ * Note: the dentry argument is the parent dentry.
+ */
+static inline int
+__affs_hash_dentry(struct dentry *dentry, struct qstr *qstr, toupper_t toupper)
+{
+ const u8 *name = qstr->name;
+ unsigned long hash;
+ int i;
+
+ i = affs_check_name(qstr->name,qstr->len);
+ if (i)
+ return i;
+
+ hash = init_name_hash();
+ i = min(qstr->len, 30u);
+ for (; i > 0; name++, i--)
+ hash = partial_name_hash(toupper(*name), hash);
+ qstr->hash = end_name_hash(hash);
+
+ return 0;
+}
+
+static int
+affs_hash_dentry(struct dentry *dentry, struct qstr *qstr)
+{
+ return __affs_hash_dentry(dentry, qstr, affs_toupper);
+}
+static int
+affs_intl_hash_dentry(struct dentry *dentry, struct qstr *qstr)
+{
+ return __affs_hash_dentry(dentry, qstr, affs_intl_toupper);
+}
+
+static inline int
+__affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b, toupper_t toupper)
+{
+ const u8 *aname = a->name;
+ const u8 *bname = b->name;
+ int len;
+
+ /* 'a' is the qstr of an already existing dentry, so the name
+ * must be valid. 'b' must be validated first.
+ */
+
+ if (affs_check_name(b->name,b->len))
+ return 1;
+
+ /* If the names are longer than the allowed 30 chars,
+ * the excess is ignored, so their length may differ.
+ */
+ len = a->len;
+ if (len >= 30) {
+ if (b->len < 30)
+ return 1;
+ len = 30;
+ } else if (len != b->len)
+ return 1;
+
+ for (; len > 0; len--)
+ if (toupper(*aname++) != toupper(*bname++))
+ return 1;
+
+ return 0;
+}
+
+static int
+affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
+{
+ return __affs_compare_dentry(dentry, a, b, affs_toupper);
+}
+static int
+affs_intl_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
+{
+ return __affs_compare_dentry(dentry, a, b, affs_intl_toupper);
+}
+
+/*
+ * NOTE! unlike strncmp, affs_match returns 1 for success, 0 for failure.
+ */
+
+static inline int
+affs_match(struct dentry *dentry, const u8 *name2, toupper_t toupper)
+{
+ const u8 *name = dentry->d_name.name;
+ int len = dentry->d_name.len;
+
+ if (len >= 30) {
+ if (*name2 < 30)
+ return 0;
+ len = 30;
+ } else if (len != *name2)
+ return 0;
+
+ for (name2++; len > 0; len--)
+ if (toupper(*name++) != toupper(*name2++))
+ return 0;
+ return 1;
+}
+
+int
+affs_hash_name(struct super_block *sb, const u8 *name, unsigned int len)
+{
+ toupper_t toupper = affs_get_toupper(sb);
+ int hash;
+
+ hash = len = min(len, 30u);
+ for (; len > 0; len--)
+ hash = (hash * 13 + toupper(*name++)) & 0x7ff;
+
+ return hash % AFFS_SB->s_hashsize;
+}
+
+static struct buffer_head *
+affs_find_entry(struct inode *dir, struct dentry *dentry)
+{
+ struct super_block *sb = dir->i_sb;
+ struct buffer_head *bh;
+ toupper_t toupper = affs_get_toupper(sb);
+ u32 key;
+
+ pr_debug("AFFS: find_entry(\"%.*s\")\n", (int)dentry->d_name.len, dentry->d_name.name);
+
+ bh = affs_bread(sb, dir->i_ino);
+ if (!bh)
+ return ERR_PTR(-EIO);
+
+ key = be32_to_cpu(AFFS_HEAD(bh)->table[affs_hash_name(sb, dentry->d_name.name, dentry->d_name.len)]);
+
+ for (;;) {
+ affs_brelse(bh);
+ if (key == 0)
+ return NULL;
+ bh = affs_bread(sb, key);
+ if (!bh)
+ return ERR_PTR(-EIO);
+ if (affs_match(dentry, AFFS_TAIL(sb, bh)->name, toupper))
+ return bh;
+ key = be32_to_cpu(AFFS_TAIL(sb, bh)->hash_chain);
+ }
+}
+
+struct dentry *
+affs_lookup(struct inode *dir, struct dentry *dentry)
+{
+ struct super_block *sb = dir->i_sb;
+ struct buffer_head *bh;
+ struct inode *inode = NULL;
+
+ pr_debug("AFFS: lookup(\"%.*s\")\n",(int)dentry->d_name.len,dentry->d_name.name);
+
+ affs_lock_dir(dir);
+ bh = affs_find_entry(dir, dentry);
+ affs_unlock_dir(dir);
+ if (IS_ERR(bh))
+ return ERR_PTR(PTR_ERR(bh));
+ if (bh) {
+ u32 ino = bh->b_blocknr;
+
+ /* store the real header ino in d_fsdata for faster lookups */
+ dentry->d_fsdata = (void *)(long)ino;
+ switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) {
+ //link to dirs disabled
+ //case ST_LINKDIR:
+ case ST_LINKFILE:
+ ino = be32_to_cpu(AFFS_TAIL(sb, bh)->original);
+ }
+ affs_brelse(bh);
+ inode = iget(sb, ino);
+ if (!inode)
+ return ERR_PTR(-EACCES);
+ }
+ dentry->d_op = AFFS_SB->s_flags & SF_INTL ? &affs_intl_dentry_operations : &affs_dentry_operations;
+ d_add(dentry, inode);
+ return NULL;
+}
+
+int
+affs_unlink(struct inode *dir, struct dentry *dentry)
+{
+ pr_debug("AFFS: unlink(dir=%d, \"%.*s\")\n", (u32)dir->i_ino,
+ (int)dentry->d_name.len, dentry->d_name.name);
+
+ if (!dentry->d_inode)
+ return -ENOENT;
+
+ return affs_remove_header(dentry);
+}
+
+int
+affs_create(struct inode *dir, struct dentry *dentry, int mode)
+{
+ struct super_block *sb = dir->i_sb;
+ struct inode *inode;
+ int error;
+
+ pr_debug("AFFS: create(%lu,\"%.*s\",0%o)\n",dir->i_ino,(int)dentry->d_name.len,
+ dentry->d_name.name,mode);
+
+ inode = affs_new_inode(dir);
+ if (!inode)
+ return -ENOSPC;
+
+ inode->i_mode = mode;
+ mode_to_prot(inode);
+ mark_inode_dirty(inode);
+
+ inode->i_op = &affs_file_inode_operations;
+ inode->i_fop = &affs_file_operations;
+ inode->i_mapping->a_ops = (AFFS_SB->s_flags & SF_OFS) ? &affs_aops_ofs : &affs_aops;
+ error = affs_add_entry(dir, inode, dentry, ST_FILE);
+ if (error) {
+ inode->i_nlink = 0;
+ iput(inode);
+ return error;
+ }
+ return 0;
+}
+
+int
+affs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+ struct inode *inode;
+ int error;
+
+ pr_debug("AFFS: mkdir(%lu,\"%.*s\",0%o)\n",dir->i_ino,
+ (int)dentry->d_name.len,dentry->d_name.name,mode);
+
+ inode = affs_new_inode(dir);
+ if (!inode)
+ return -ENOSPC;
+
+ inode->i_mode = S_IFDIR | mode;
+ mode_to_prot(inode);
+
+ inode->i_op = &affs_dir_inode_operations;
+ inode->i_fop = &affs_dir_operations;
+
+ error = affs_add_entry(dir, inode, dentry, ST_USERDIR);
+ if (error) {
+ inode->i_nlink = 0;
+ mark_inode_dirty(inode);
+ iput(inode);
+ return error;
+ }
+ return 0;
+}
+
+int
+affs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ pr_debug("AFFS: rmdir(dir=%u, \"%.*s\")\n", (u32)dir->i_ino,
+ (int)dentry->d_name.len, dentry->d_name.name);
+
+ if (!dentry->d_inode)
+ return -ENOENT;
+
+ return affs_remove_header(dentry);
+}
+
+int
+affs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
+{
+ struct super_block *sb = dir->i_sb;
+ struct buffer_head *bh;
+ struct inode *inode;
+ char *p;
+ int i, maxlen, error;
+ char c, lc;
+
+ pr_debug("AFFS: symlink(%lu,\"%.*s\" -> \"%s\")\n",dir->i_ino,
+ (int)dentry->d_name.len,dentry->d_name.name,symname);
+
+ maxlen = AFFS_SB->s_hashsize * sizeof(u32) - 1;
+ error = -ENOSPC;
+ inode = affs_new_inode(dir);
+ if (!inode)
+ return -ENOSPC;
+
+ inode->i_op = &affs_symlink_inode_operations;
+ inode->i_data.a_ops = &affs_symlink_aops;
+ inode->i_mode = S_IFLNK | 0777;
+ mode_to_prot(inode);
+
+ error = -EIO;
+ bh = affs_bread(sb, inode->i_ino);
+ if (!bh)
+ goto err;
+ i = 0;
+ p = (char *)AFFS_HEAD(bh)->table;
+ lc = '/';
+ if (*symname == '/') {
+ while (*symname == '/')
+ symname++;
+ while (AFFS_SB->s_volume[i]) /* Cannot overflow */
+ *p++ = AFFS_SB->s_volume[i++];
+ }
+ while (i < maxlen && (c = *symname++)) {
+ if (c == '.' && lc == '/' && *symname == '.' && symname[1] == '/') {
+ *p++ = '/';
+ i++;
+ symname += 2;
+ lc = '/';
+ } else if (c == '.' && lc == '/' && *symname == '/') {
+ symname++;
+ lc = '/';
+ } else {
+ *p++ = c;
+ lc = c;
+ i++;
+ }
+ if (lc == '/')
+ while (*symname == '/')
+ symname++;
+ }
+ *p = 0;
+ mark_buffer_dirty_inode(bh, inode);
+ affs_brelse(bh);
+ mark_inode_dirty(inode);
+
+ error = affs_add_entry(dir, inode, dentry, ST_SOFTLINK);
+ if (error)
+ goto err;
+
+ return 0;
+
+err:
+ inode->i_nlink = 0;
+ mark_inode_dirty(inode);
+ iput(inode);
+ return error;
+}
+
+int
+affs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
+{
+ struct inode *inode = old_dentry->d_inode;
+ int error;
+
+ pr_debug("AFFS: link(%u, %u, \"%.*s\")\n", (u32)inode->i_ino, (u32)dir->i_ino,
+ (int)dentry->d_name.len,dentry->d_name.name);
+
+ if (S_ISDIR(inode->i_mode))
+ return -EPERM;
+
+ error = affs_add_entry(dir, inode, dentry, S_ISDIR(inode->i_mode) ? ST_LINKDIR : ST_LINKFILE);
+ if (error) {
+ inode->i_nlink = 0;
+ mark_inode_dirty(inode);
+ iput(inode);
+ return error;
+ }
+ return 0;
+}
+
+int
+affs_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ struct super_block *sb = old_dir->i_sb;
+ struct buffer_head *bh = NULL;
+ int retval;
+
+ pr_debug("AFFS: rename(old=%u,\"%*s\" to new=%u,\"%*s\")\n",
+ (u32)old_dir->i_ino, (int)old_dentry->d_name.len, old_dentry->d_name.name,
+ (u32)new_dir->i_ino, (int)new_dentry->d_name.len, new_dentry->d_name.name);
+
+ if ((retval = affs_check_name(new_dentry->d_name.name,new_dentry->d_name.len)))
+ goto done;
+
+ /* Unlink destination if it already exists */
+ if (new_dentry->d_inode) {
+ retval = affs_remove_header(new_dentry);
+ if (retval)
+ return retval;
+ }
+
+ retval = -EIO;
+ bh = affs_bread(sb, old_dentry->d_inode->i_ino);
+ if (!bh)
+ goto done;
+
+ /* Remove header from its parent directory. */
+ affs_lock_dir(old_dir);
+ retval = affs_remove_hash(old_dir, bh);
+ affs_unlock_dir(old_dir);
+ if (retval)
+ goto done;
+
+ /* And insert it into the new directory with the new name. */
+ affs_copy_name(AFFS_TAIL(sb, bh)->name, new_dentry);
+ affs_fix_checksum(sb, bh);
+ affs_lock_dir(new_dir);
+ retval = affs_insert_hash(new_dir, bh);
+ affs_unlock_dir(new_dir);
+ /* TODO: move it back to old_dir, if error? */
+
+done:
+ mark_buffer_dirty_inode(bh, retval ? old_dir : new_dir);
+ affs_brelse(bh);
+ return retval;
+}
diff --git a/fs/affs/super.c b/fs/affs/super.c
new file mode 100644
index 00000000000000..7c3819530ce668
--- /dev/null
+++ b/fs/affs/super.c
@@ -0,0 +1,509 @@
+/*
+ * linux/fs/affs/inode.c
+ *
+ * (c) 1996 Hans-Joachim Widmaier - Rewritten
+ *
+ * (C) 1993 Ray Burr - Modified for Amiga FFS filesystem.
+ *
+ * (C) 1992 Eric Youngdale Modified for ISO 9660 filesystem.
+ *
+ * (C) 1991 Linus Torvalds - minix filesystem
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/sched.h>
+#include <linux/affs_fs.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/genhd.h>
+#include <linux/amigaffs.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/init.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+extern int *blk_size[];
+extern struct timezone sys_tz;
+
+static int affs_statfs(struct super_block *sb, struct statfs *buf);
+static int affs_remount (struct super_block *sb, int *flags, char *data);
+
+static void
+affs_put_super(struct super_block *sb)
+{
+ pr_debug("AFFS: put_super()\n");
+
+ if (!(sb->s_flags & MS_RDONLY)) {
+ AFFS_ROOT_TAIL(sb, AFFS_SB->s_root_bh)->bm_flag = be32_to_cpu(1);
+ secs_to_datestamp(CURRENT_TIME,
+ &AFFS_ROOT_TAIL(sb, AFFS_SB->s_root_bh)->disk_change);
+ affs_fix_checksum(sb, AFFS_SB->s_root_bh);
+ mark_buffer_dirty(AFFS_SB->s_root_bh);
+ }
+
+ if (AFFS_SB->s_prefix)
+ kfree(AFFS_SB->s_prefix);
+ affs_free_bitmap(sb);
+ affs_brelse(AFFS_SB->s_root_bh);
+
+ return;
+}
+
+static void
+affs_write_super(struct super_block *sb)
+{
+ int clean = 2;
+
+ if (!(sb->s_flags & MS_RDONLY)) {
+ // if (AFFS_SB->s_bitmap[i].bm_bh) {
+ // if (buffer_dirty(AFFS_SB->s_bitmap[i].bm_bh)) {
+ // clean = 0;
+ AFFS_ROOT_TAIL(sb, AFFS_SB->s_root_bh)->bm_flag = be32_to_cpu(clean);
+ secs_to_datestamp(CURRENT_TIME,
+ &AFFS_ROOT_TAIL(sb, AFFS_SB->s_root_bh)->disk_change);
+ affs_fix_checksum(sb, AFFS_SB->s_root_bh);
+ mark_buffer_dirty(AFFS_SB->s_root_bh);
+ sb->s_dirt = !clean; /* redo until bitmap synced */
+ } else
+ sb->s_dirt = 0;
+
+ pr_debug("AFFS: write_super() at %lu, clean=%d\n", CURRENT_TIME, clean);
+}
+
+static struct super_operations affs_sops = {
+ read_inode: affs_read_inode,
+ write_inode: affs_write_inode,
+ put_inode: affs_put_inode,
+ delete_inode: affs_delete_inode,
+ clear_inode: affs_clear_inode,
+ put_super: affs_put_super,
+ write_super: affs_write_super,
+ statfs: affs_statfs,
+ remount_fs: affs_remount,
+};
+
+static int
+parse_options(char *options, uid_t *uid, gid_t *gid, int *mode, int *reserved, s32 *root,
+ int *blocksize, char **prefix, char *volume, unsigned long *mount_opts)
+{
+ char *this_char, *value, *optn;
+ int f;
+
+ /* Fill in defaults */
+
+ *uid = current->uid;
+ *gid = current->gid;
+ *reserved = 2;
+ *root = -1;
+ *blocksize = -1;
+ volume[0] = ':';
+ volume[1] = 0;
+ *mount_opts = 0;
+ if (!options)
+ return 1;
+ for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) {
+ f = 0;
+ if ((value = strchr(this_char,'=')) != NULL)
+ *value++ = 0;
+ if ((optn = "protect") && !strcmp(this_char, optn)) {
+ if (value)
+ goto out_inv_arg;
+ *mount_opts |= SF_IMMUTABLE;
+ } else if ((optn = "verbose") && !strcmp(this_char, optn)) {
+ if (value)
+ goto out_inv_arg;
+ *mount_opts |= SF_VERBOSE;
+ } else if ((optn = "mufs") && !strcmp(this_char, optn)) {
+ if (value)
+ goto out_inv_arg;
+ *mount_opts |= SF_MUFS;
+ } else if ((f = !strcmp(this_char,"setuid")) || !strcmp(this_char,"setgid")) {
+ if (value) {
+ if (!*value) {
+ printk("AFFS: Argument for set[ug]id option missing\n");
+ return 0;
+ } else {
+ *(f ? uid : gid) = simple_strtoul(value,&value,0);
+ if (*value) {
+ printk("AFFS: Bad set[ug]id argument\n");
+ return 0;
+ }
+ *mount_opts |= f ? SF_SETUID : SF_SETGID;
+ }
+ }
+ } else if (!strcmp(this_char,"prefix")) {
+ optn = "prefix";
+ if (!value || !*value)
+ goto out_no_arg;
+ if (*prefix) { /* Free any previous prefix */
+ kfree(*prefix);
+ *prefix = NULL;
+ }
+ *prefix = kmalloc(strlen(value) + 1,GFP_KERNEL);
+ if (!*prefix)
+ return 0;
+ strcpy(*prefix,value);
+ *mount_opts |= SF_PREFIX;
+ } else if (!strcmp(this_char,"volume")) {
+ optn = "volume";
+ if (!value || !*value)
+ goto out_no_arg;
+ if (strlen(value) > 30)
+ value[30] = 0;
+ strncpy(volume,value,30);
+ } else if (!strcmp(this_char,"mode")) {
+ optn = "mode";
+ if (!value || !*value)
+ goto out_no_arg;
+ *mode = simple_strtoul(value,&value,8) & 0777;
+ if (*value)
+ return 0;
+ *mount_opts |= SF_SETMODE;
+ } else if (!strcmp(this_char,"reserved")) {
+ optn = "reserved";
+ if (!value || !*value)
+ goto out_no_arg;
+ *reserved = simple_strtoul(value,&value,0);
+ if (*value)
+ return 0;
+ } else if (!strcmp(this_char,"root")) {
+ optn = "root";
+ if (!value || !*value)
+ goto out_no_arg;
+ *root = simple_strtoul(value,&value,0);
+ if (*value)
+ return 0;
+ } else if (!strcmp(this_char,"bs")) {
+ optn = "bs";
+ if (!value || !*value)
+ goto out_no_arg;
+ *blocksize = simple_strtoul(value,&value,0);
+ if (*value)
+ return 0;
+ if (*blocksize != 512 && *blocksize != 1024 && *blocksize != 2048
+ && *blocksize != 4096) {
+ printk ("AFFS: Invalid blocksize (512, 1024, 2048, 4096 allowed)\n");
+ return 0;
+ }
+ } else if (!strcmp (this_char, "grpquota")
+ || !strcmp (this_char, "noquota")
+ || !strcmp (this_char, "quota")
+ || !strcmp (this_char, "usrquota"))
+ /* Silently ignore the quota options */
+ ;
+ else {
+ printk("AFFS: Unrecognized mount option %s\n", this_char);
+ return 0;
+ }
+ }
+ return 1;
+
+out_no_arg:
+ printk("AFFS: The %s option requires an argument\n", optn);
+ return 0;
+out_inv_arg:
+ printk("AFFS: Option %s does not take an argument\n", optn);
+ return 0;
+}
+
+/* This function definitely needs to be split up. Some fine day I'll
+ * hopefully have the guts to do so. Until then: sorry for the mess.
+ */
+
+static struct super_block *
+affs_read_super(struct super_block *sb, void *data, int silent)
+{
+ struct buffer_head *root_bh = NULL;
+ struct buffer_head *boot_bh;
+ struct inode *root_inode = NULL;
+ kdev_t dev = sb->s_dev;
+ s32 root_block;
+ int blocks, size, blocksize;
+ u32 chksum;
+ int num_bm;
+ int i, j;
+ s32 key;
+ uid_t uid;
+ gid_t gid;
+ int reserved;
+ unsigned long mount_flags;
+ int tmp_flags; /* fix remount prototype... */
+
+ pr_debug("AFFS: read_super(%s)\n",data ? (const char *)data : "no options");
+
+ sb->s_magic = AFFS_SUPER_MAGIC;
+ sb->s_op = &affs_sops;
+ memset(AFFS_SB, 0, sizeof(*AFFS_SB));
+ init_MUTEX(&AFFS_SB->s_bmlock);
+
+ if (!parse_options(data,&uid,&gid,&i,&reserved,&root_block,
+ &blocksize,&AFFS_SB->s_prefix,
+ AFFS_SB->s_volume, &mount_flags)) {
+ printk(KERN_ERR "AFFS: Error parsing options\n");
+ return NULL;
+ }
+ /* N.B. after this point s_prefix must be released */
+
+ AFFS_SB->s_flags = mount_flags;
+ AFFS_SB->s_mode = i;
+ AFFS_SB->s_uid = uid;
+ AFFS_SB->s_gid = gid;
+ AFFS_SB->s_reserved= reserved;
+
+ /* Get the size of the device in 512-byte blocks.
+ * If we later see that the partition uses bigger
+ * blocks, we will have to change it.
+ */
+
+ blocks = blk_size[MAJOR(dev)] ? blk_size[MAJOR(dev)][MINOR(dev)] : 0;
+ if (!blocks) {
+ printk(KERN_ERR "AFFS: Could not determine device size\n");
+ goto out_error;
+ }
+ size = (BLOCK_SIZE / 512) * blocks;
+ pr_debug("AFFS: initial blksize=%d, blocks=%d\n", 512, blocks);
+
+ affs_set_blocksize(sb, PAGE_SIZE);
+ /* Try to find root block. Its location depends on the block size. */
+
+ i = 512;
+ j = 4096;
+ if (blocksize > 0) {
+ i = j = blocksize;
+ size = size / (blocksize / 512);
+ }
+ for (blocksize = i, key = 0; blocksize <= j; blocksize <<= 1, size >>= 1) {
+ AFFS_SB->s_root_block = root_block;
+ if (root_block < 0)
+ AFFS_SB->s_root_block = (reserved + size - 1) / 2;
+ pr_debug("AFFS: setting blocksize to %d\n", blocksize);
+ affs_set_blocksize(sb, blocksize);
+ AFFS_SB->s_partition_size = size;
+
+ /* The root block location that was calculated above is not
+ * correct if the partition size is an odd number of 512-
+ * byte blocks, which will be rounded down to a number of
+ * 1024-byte blocks, and if there were an even number of
+ * reserved blocks. Ideally, all partition checkers should
+ * report the real number of blocks of the real blocksize,
+ * but since this just cannot be done, we have to try to
+ * find the root block anyways. In the above case, it is one
+ * block behind the calculated one. So we check this one, too.
+ */
+ for (num_bm = 0; num_bm < 2; num_bm++) {
+ pr_debug("AFFS: Dev %s, trying root=%u, bs=%d, "
+ "size=%d, reserved=%d\n",
+ kdevname(dev),
+ AFFS_SB->s_root_block + num_bm,
+ blocksize, size, reserved);
+ root_bh = affs_bread(sb, AFFS_SB->s_root_block + num_bm);
+ if (!root_bh)
+ continue;
+ if (!affs_checksum_block(sb, root_bh) &&
+ be32_to_cpu(AFFS_ROOT_HEAD(root_bh)->ptype) == T_SHORT &&
+ be32_to_cpu(AFFS_ROOT_TAIL(sb, root_bh)->stype) == ST_ROOT) {
+ AFFS_SB->s_hashsize = blocksize / 4 - 56;
+ AFFS_SB->s_root_block += num_bm;
+ key = 1;
+ goto got_root;
+ }
+ affs_brelse(root_bh);
+ root_bh = NULL;
+ }
+ }
+ if (!silent)
+ printk(KERN_ERR "AFFS: No valid root block on device %s\n",
+ kdevname(dev));
+ goto out_error;
+
+ /* N.B. after this point bh must be released */
+got_root:
+ root_block = AFFS_SB->s_root_block;
+
+ sb->s_blocksize_bits = blocksize == 512 ? 9 :
+ blocksize == 1024 ? 10 :
+ blocksize == 2048 ? 11 : 12;
+
+ /* Find out which kind of FS we have */
+ boot_bh = sb_bread(sb, 0);
+ if (!boot_bh) {
+ printk(KERN_ERR "AFFS: Cannot read boot block\n");
+ goto out_error;
+ }
+ chksum = be32_to_cpu(*(u32 *)boot_bh->b_data);
+ brelse(boot_bh);
+
+ /* Dircache filesystems are compatible with non-dircache ones
+ * when reading. As long as they aren't supported, writing is
+ * not recommended.
+ */
+ if ((chksum == FS_DCFFS || chksum == MUFS_DCFFS || chksum == FS_DCOFS
+ || chksum == MUFS_DCOFS) && !(sb->s_flags & MS_RDONLY)) {
+ printk(KERN_NOTICE "AFFS: Dircache FS - mounting %s read only\n",
+ kdevname(dev));
+ sb->s_flags |= MS_RDONLY;
+ }
+ switch (chksum) {
+ case MUFS_FS:
+ case MUFS_INTLFFS:
+ case MUFS_DCFFS:
+ AFFS_SB->s_flags |= SF_MUFS;
+ /* fall thru */
+ case FS_INTLFFS:
+ case FS_DCFFS:
+ AFFS_SB->s_flags |= SF_INTL;
+ break;
+ case MUFS_FFS:
+ AFFS_SB->s_flags |= SF_MUFS;
+ break;
+ case FS_FFS:
+ break;
+ case MUFS_OFS:
+ AFFS_SB->s_flags |= SF_MUFS;
+ /* fall thru */
+ case FS_OFS:
+ AFFS_SB->s_flags |= SF_OFS;
+ sb->s_flags |= MS_NOEXEC;
+ break;
+ case MUFS_DCOFS:
+ case MUFS_INTLOFS:
+ AFFS_SB->s_flags |= SF_MUFS;
+ case FS_DCOFS:
+ case FS_INTLOFS:
+ AFFS_SB->s_flags |= SF_INTL | SF_OFS;
+ sb->s_flags |= MS_NOEXEC;
+ break;
+ default:
+ printk(KERN_ERR "AFFS: Unknown filesystem on device %s: %08X\n",
+ kdevname(dev), chksum);
+ goto out_error;
+ }
+
+ if (mount_flags & SF_VERBOSE) {
+ chksum = cpu_to_be32(chksum);
+ printk(KERN_NOTICE "AFFS: Mounting volume \"%*s\": Type=%.3s\\%c, Blocksize=%d\n",
+ AFFS_ROOT_TAIL(sb, root_bh)->disk_name[0],
+ AFFS_ROOT_TAIL(sb, root_bh)->disk_name + 1,
+ (char *)&chksum,((char *)&chksum)[3] + '0',blocksize);
+ }
+
+ sb->s_flags |= MS_NODEV | MS_NOSUID;
+
+ AFFS_SB->s_data_blksize = sb->s_blocksize;
+ if (AFFS_SB->s_flags & SF_OFS)
+ AFFS_SB->s_data_blksize -= 24;
+
+ /* Keep super block in cache */
+ AFFS_SB->s_root_bh = root_bh;
+ /* N.B. after this point s_root_bh must be released */
+
+ tmp_flags = sb->s_flags;
+ if (affs_init_bitmap(sb, &tmp_flags))
+ goto out_error;
+ sb->s_flags = tmp_flags;
+
+ /* set up enough so that it can read an inode */
+
+ root_inode = iget(sb, root_block);
+ sb->s_root = d_alloc_root(root_inode);
+ if (!sb->s_root) {
+ printk(KERN_ERR "AFFS: Get root inode failed\n");
+ goto out_error;
+ }
+ sb->s_root->d_op = &affs_dentry_operations;
+
+ pr_debug("AFFS: s_flags=%lX\n",sb->s_flags);
+ return sb;
+
+ /*
+ * Begin the cascaded cleanup ...
+ */
+out_error:
+ if (root_inode)
+ iput(root_inode);
+ if (AFFS_SB->s_bitmap)
+ kfree(AFFS_SB->s_bitmap);
+ affs_brelse(root_bh);
+ if (AFFS_SB->s_prefix)
+ kfree(AFFS_SB->s_prefix);
+ return NULL;
+}
+
+static int
+affs_remount(struct super_block *sb, int *flags, char *data)
+{
+ int blocksize;
+ uid_t uid;
+ gid_t gid;
+ int mode;
+ int reserved;
+ int root_block;
+ unsigned long mount_flags;
+ int res = 0;
+
+ pr_debug("AFFS: remount(flags=0x%x,opts=\"%s\")\n",*flags,data);
+
+ if (!parse_options(data,&uid,&gid,&mode,&reserved,&root_block,
+ &blocksize,&AFFS_SB->s_prefix,AFFS_SB->s_volume,&mount_flags))
+ return -EINVAL;
+ AFFS_SB->s_flags = mount_flags;
+ AFFS_SB->s_mode = mode;
+ AFFS_SB->s_uid = uid;
+ AFFS_SB->s_gid = gid;
+
+ if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
+ return 0;
+ if (*flags & MS_RDONLY) {
+ sb->s_dirt = 1;
+ while (sb->s_dirt)
+ affs_write_super(sb);
+ affs_free_bitmap(sb);
+ } else
+ res = affs_init_bitmap(sb, flags);
+
+ return res;
+}
+
+static int
+affs_statfs(struct super_block *sb, struct statfs *buf)
+{
+ int free;
+
+ pr_debug("AFFS: statfs() partsize=%d, reserved=%d\n",AFFS_SB->s_partition_size,
+ AFFS_SB->s_reserved);
+
+ free = affs_count_free_blocks(sb);
+ buf->f_type = AFFS_SUPER_MAGIC;
+ buf->f_bsize = sb->s_blocksize;
+ buf->f_blocks = AFFS_SB->s_partition_size - AFFS_SB->s_reserved;
+ buf->f_bfree = free;
+ buf->f_bavail = free;
+ return 0;
+}
+
+static DECLARE_FSTYPE_DEV(affs_fs_type, "affs", affs_read_super);
+
+static int __init init_affs_fs(void)
+{
+ return register_filesystem(&affs_fs_type);
+}
+
+static void __exit exit_affs_fs(void)
+{
+ unregister_filesystem(&affs_fs_type);
+}
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_DESCRIPTION("Amiga filesystem support for Linux");
+MODULE_LICENSE("GPL");
+
+module_init(init_affs_fs)
+module_exit(exit_affs_fs)
diff --git a/fs/affs/symlink.c b/fs/affs/symlink.c
new file mode 100644
index 00000000000000..f8eedba6fdd393
--- /dev/null
+++ b/fs/affs/symlink.c
@@ -0,0 +1,87 @@
+/*
+ * linux/fs/affs/symlink.c
+ *
+ * 1995 Hans-Joachim Widmaier - Modified for affs.
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * affs symlink handling code
+ */
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/affs_fs.h>
+#include <linux/amigaffs.h>
+#include <linux/pagemap.h>
+#include <linux/smp_lock.h>
+
+static int affs_symlink_readpage(struct file *file, struct page *page)
+{
+ struct buffer_head *bh;
+ struct inode *inode = page->mapping->host;
+ char *link = kmap(page);
+ struct slink_front *lf;
+ int err;
+ int i, j;
+ char c;
+ char lc;
+ char *pf;
+
+ pr_debug("AFFS: follow_link(ino=%lu)\n",inode->i_ino);
+
+ err = -EIO;
+ lock_kernel();
+ bh = affs_bread(inode->i_sb, inode->i_ino);
+ unlock_kernel();
+ if (!bh)
+ goto fail;
+ i = 0;
+ j = 0;
+ lf = (struct slink_front *)bh->b_data;
+ lc = 0;
+ pf = inode->i_sb->u.affs_sb.s_prefix ? inode->i_sb->u.affs_sb.s_prefix : "/";
+
+ if (strchr(lf->symname,':')) { /* Handle assign or volume name */
+ while (i < 1023 && (c = pf[i]))
+ link[i++] = c;
+ while (i < 1023 && lf->symname[j] != ':')
+ link[i++] = lf->symname[j++];
+ if (i < 1023)
+ link[i++] = '/';
+ j++;
+ lc = '/';
+ }
+ while (i < 1023 && (c = lf->symname[j])) {
+ if (c == '/' && lc == '/' && i < 1020) { /* parent dir */
+ link[i++] = '.';
+ link[i++] = '.';
+ }
+ link[i++] = c;
+ lc = c;
+ j++;
+ }
+ link[i] = '\0';
+ lock_kernel();
+ affs_brelse(bh);
+ unlock_kernel();
+ SetPageUptodate(page);
+ kunmap(page);
+ UnlockPage(page);
+ return 0;
+fail:
+ SetPageError(page);
+ kunmap(page);
+ UnlockPage(page);
+ return err;
+}
+
+struct address_space_operations affs_symlink_aops = {
+ readpage: affs_symlink_readpage,
+};
+
+struct inode_operations affs_symlink_inode_operations = {
+ readlink: page_readlink,
+ follow_link: page_follow_link,
+ setattr: affs_notify_change,
+};
diff --git a/fs/attr.c b/fs/attr.c
new file mode 100644
index 00000000000000..2c99b510789836
--- /dev/null
+++ b/fs/attr.c
@@ -0,0 +1,151 @@
+/*
+ * linux/fs/attr.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ * changes by Thomas Schoebel-Theuer
+ */
+
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/smp_lock.h>
+#include <linux/dnotify.h>
+#include <linux/fcntl.h>
+#include <linux/quotaops.h>
+
+/* Taken over from the old code... */
+
+/* POSIX UID/GID verification for setting inode attributes. */
+int inode_change_ok(struct inode *inode, struct iattr *attr)
+{
+ int retval = -EPERM;
+ unsigned int ia_valid = attr->ia_valid;
+
+ /* If force is set do it anyway. */
+ if (ia_valid & ATTR_FORCE)
+ goto fine;
+
+ /* Make sure a caller can chown. */
+ if ((ia_valid & ATTR_UID) &&
+ (current->fsuid != inode->i_uid ||
+ attr->ia_uid != inode->i_uid) && !capable(CAP_CHOWN))
+ goto error;
+
+ /* Make sure caller can chgrp. */
+ if ((ia_valid & ATTR_GID) &&
+ (current->fsuid != inode->i_uid ||
+ (!in_group_p(attr->ia_gid) && attr->ia_gid != inode->i_gid)) &&
+ !capable(CAP_CHOWN))
+ goto error;
+
+ /* Make sure a caller can chmod. */
+ if (ia_valid & ATTR_MODE) {
+ if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
+ goto error;
+ /* Also check the setgid bit! */
+ if (!in_group_p((ia_valid & ATTR_GID) ? attr->ia_gid :
+ inode->i_gid) && !capable(CAP_FSETID))
+ attr->ia_mode &= ~S_ISGID;
+ }
+
+ /* Check for setting the inode time. */
+ if (ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET)) {
+ if (current->fsuid != inode->i_uid && !capable(CAP_FOWNER))
+ goto error;
+ }
+fine:
+ retval = 0;
+error:
+ return retval;
+}
+
+int inode_setattr(struct inode * inode, struct iattr * attr)
+{
+ unsigned int ia_valid = attr->ia_valid;
+ int error = 0;
+
+ if (ia_valid & ATTR_SIZE) {
+ error = vmtruncate(inode, attr->ia_size);
+ if (error)
+ goto out;
+ }
+
+ if (ia_valid & ATTR_UID)
+ inode->i_uid = attr->ia_uid;
+ if (ia_valid & ATTR_GID)
+ inode->i_gid = attr->ia_gid;
+ if (ia_valid & ATTR_ATIME)
+ inode->i_atime = attr->ia_atime;
+ if (ia_valid & ATTR_MTIME)
+ inode->i_mtime = attr->ia_mtime;
+ if (ia_valid & ATTR_CTIME)
+ inode->i_ctime = attr->ia_ctime;
+ if (ia_valid & ATTR_MODE) {
+ inode->i_mode = attr->ia_mode;
+ if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID))
+ inode->i_mode &= ~S_ISGID;
+ }
+ mark_inode_dirty(inode);
+out:
+ return error;
+}
+
+static int setattr_mask(unsigned int ia_valid)
+{
+ unsigned long dn_mask = 0;
+
+ if (ia_valid & ATTR_UID)
+ dn_mask |= DN_ATTRIB;
+ if (ia_valid & ATTR_GID)
+ dn_mask |= DN_ATTRIB;
+ if (ia_valid & ATTR_SIZE)
+ dn_mask |= DN_MODIFY;
+ /* both times implies a utime(s) call */
+ if ((ia_valid & (ATTR_ATIME|ATTR_MTIME)) == (ATTR_ATIME|ATTR_MTIME))
+ dn_mask |= DN_ATTRIB;
+ else if (ia_valid & ATTR_ATIME)
+ dn_mask |= DN_ACCESS;
+ else if (ia_valid & ATTR_MTIME)
+ dn_mask |= DN_MODIFY;
+ if (ia_valid & ATTR_MODE)
+ dn_mask |= DN_ATTRIB;
+ return dn_mask;
+}
+
+int notify_change(struct dentry * dentry, struct iattr * attr)
+{
+ struct inode *inode = dentry->d_inode;
+ int error;
+ time_t now = CURRENT_TIME;
+ unsigned int ia_valid = attr->ia_valid;
+
+ if (!inode)
+ BUG();
+
+ attr->ia_ctime = now;
+ if (!(ia_valid & ATTR_ATIME_SET))
+ attr->ia_atime = now;
+ if (!(ia_valid & ATTR_MTIME_SET))
+ attr->ia_mtime = now;
+
+ lock_kernel();
+ if (inode->i_op && inode->i_op->setattr)
+ error = inode->i_op->setattr(dentry, attr);
+ else {
+ error = inode_change_ok(inode, attr);
+ if (!error) {
+ if ((ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) ||
+ (ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid))
+ error = DQUOT_TRANSFER(inode, attr) ? -EDQUOT : 0;
+ if (!error)
+ error = inode_setattr(inode, attr);
+ }
+ }
+ unlock_kernel();
+ if (!error) {
+ unsigned long dn_mask = setattr_mask(ia_valid);
+ if (dn_mask)
+ dnotify_parent(dentry, dn_mask);
+ }
+ return error;
+}
diff --git a/fs/autofs/Makefile b/fs/autofs/Makefile
new file mode 100644
index 00000000000000..5c8dbcfe38a03f
--- /dev/null
+++ b/fs/autofs/Makefile
@@ -0,0 +1,12 @@
+#
+# Makefile for the linux autofs-filesystem routines.
+#
+# We can build this either out of the kernel tree or the autofs tools tree.
+#
+
+O_TARGET := autofs.o
+
+obj-y := dir.o dirhash.o init.o inode.o root.o symlink.o waitq.o
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/autofs/autofs_i.h b/fs/autofs/autofs_i.h
new file mode 100644
index 00000000000000..ccb38459e3530e
--- /dev/null
+++ b/fs/autofs/autofs_i.h
@@ -0,0 +1,159 @@
+/* -*- linux-c -*- ------------------------------------------------------- *
+ *
+ * linux/fs/autofs/autofs_i.h
+ *
+ * Copyright 1997-1998 Transmeta Corporation - All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/* Internal header file for autofs */
+
+#include <linux/auto_fs.h>
+
+/* This is the range of ioctl() numbers we claim as ours */
+#define AUTOFS_IOC_FIRST AUTOFS_IOC_READY
+#define AUTOFS_IOC_COUNT 32
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/wait.h>
+#include <asm/uaccess.h>
+
+#ifdef DEBUG
+#define DPRINTK(D) (printk D)
+#else
+#define DPRINTK(D) ((void)0)
+#endif
+
+#define AUTOFS_SUPER_MAGIC 0x0187
+
+/*
+ * If the daemon returns a negative response (AUTOFS_IOC_FAIL) then the
+ * kernel will keep the negative response cached for up to the time given
+ * here, although the time can be shorter if the kernel throws the dcache
+ * entry away. This probably should be settable from user space.
+ */
+#define AUTOFS_NEGATIVE_TIMEOUT (60*HZ) /* 1 minute */
+
+/* Structures associated with the root directory hash table */
+
+#define AUTOFS_HASH_SIZE 67
+
+struct autofs_dir_ent {
+ int hash;
+ char *name;
+ int len;
+ ino_t ino;
+ struct dentry *dentry;
+ /* Linked list of entries */
+ struct autofs_dir_ent *next;
+ struct autofs_dir_ent **back;
+ /* The following entries are for the expiry system */
+ unsigned long last_usage;
+ struct list_head exp;
+};
+
+struct autofs_dirhash {
+ struct autofs_dir_ent *h[AUTOFS_HASH_SIZE];
+ struct list_head expiry_head;
+};
+
+struct autofs_wait_queue {
+ wait_queue_head_t queue;
+ struct autofs_wait_queue *next;
+ autofs_wqt_t wait_queue_token;
+ /* We use the following to see what we are waiting for */
+ int hash;
+ int len;
+ char *name;
+ /* This is for status reporting upon return */
+ int status;
+ int wait_ctr;
+};
+
+struct autofs_symlink {
+ char *data;
+ int len;
+ time_t mtime;
+};
+
+#define AUTOFS_MAX_SYMLINKS 256
+
+#define AUTOFS_ROOT_INO 1
+#define AUTOFS_FIRST_SYMLINK 2
+#define AUTOFS_FIRST_DIR_INO (AUTOFS_FIRST_SYMLINK+AUTOFS_MAX_SYMLINKS)
+
+#define AUTOFS_SYMLINK_BITMAP_LEN \
+ ((AUTOFS_MAX_SYMLINKS+((sizeof(long)*1)-1))/(sizeof(long)*8))
+
+#define AUTOFS_SBI_MAGIC 0x6d4a556d
+
+struct autofs_sb_info {
+ u32 magic;
+ struct file *pipe;
+ pid_t oz_pgrp;
+ int catatonic;
+ unsigned long exp_timeout;
+ ino_t next_dir_ino;
+ struct autofs_wait_queue *queues; /* Wait queue pointer */
+ struct autofs_dirhash dirhash; /* Root directory hash */
+ struct autofs_symlink symlink[AUTOFS_MAX_SYMLINKS];
+ unsigned long symlink_bitmap[AUTOFS_SYMLINK_BITMAP_LEN];
+};
+
+static inline struct autofs_sb_info *autofs_sbi(struct super_block *sb)
+{
+ return (struct autofs_sb_info *)(sb->u.generic_sbp);
+}
+
+/* autofs_oz_mode(): do we see the man behind the curtain? (The
+ processes which do manipulations for us in user space sees the raw
+ filesystem without "magic".) */
+
+static inline int autofs_oz_mode(struct autofs_sb_info *sbi) {
+ return sbi->catatonic || current->pgrp == sbi->oz_pgrp;
+}
+
+/* Hash operations */
+
+void autofs_initialize_hash(struct autofs_dirhash *);
+struct autofs_dir_ent *autofs_hash_lookup(const struct autofs_dirhash *,struct qstr *);
+void autofs_hash_insert(struct autofs_dirhash *,struct autofs_dir_ent *);
+void autofs_hash_delete(struct autofs_dir_ent *);
+struct autofs_dir_ent *autofs_hash_enum(const struct autofs_dirhash *,off_t *,struct autofs_dir_ent *);
+void autofs_hash_dputall(struct autofs_dirhash *);
+void autofs_hash_nuke(struct autofs_dirhash *);
+
+/* Expiration-handling functions */
+
+void autofs_update_usage(struct autofs_dirhash *,struct autofs_dir_ent *);
+struct autofs_dir_ent *autofs_expire(struct super_block *,struct autofs_sb_info *, struct vfsmount *mnt);
+
+/* Operations structures */
+
+extern struct inode_operations autofs_root_inode_operations;
+extern struct inode_operations autofs_symlink_inode_operations;
+extern struct inode_operations autofs_dir_inode_operations;
+extern struct file_operations autofs_root_operations;
+
+/* Initializing function */
+
+struct super_block *autofs_read_super(struct super_block *, void *,int);
+
+/* Queue management functions */
+
+int autofs_wait(struct autofs_sb_info *,struct qstr *);
+int autofs_wait_release(struct autofs_sb_info *,autofs_wqt_t,int);
+void autofs_catatonic_mode(struct autofs_sb_info *);
+
+#ifdef DEBUG
+void autofs_say(const char *name, int len);
+#else
+#define autofs_say(n,l) ((void)0)
+#endif
diff --git a/fs/autofs/dir.c b/fs/autofs/dir.c
new file mode 100644
index 00000000000000..a70b71c6e02319
--- /dev/null
+++ b/fs/autofs/dir.c
@@ -0,0 +1,29 @@
+/* -*- linux-c -*- --------------------------------------------------------- *
+ *
+ * linux/fs/autofs/dir.c
+ *
+ * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ------------------------------------------------------------------------- */
+
+#include "autofs_i.h"
+
+/*
+ * No entries except for "." and "..", both of which are handled by the VFS
+ * layer. So all children are negative and dcache-based versions of operations
+ * are OK.
+ */
+static struct dentry *autofs_dir_lookup(struct inode *dir,struct dentry *dentry)
+{
+ d_add(dentry, NULL);
+ return NULL;
+}
+
+struct inode_operations autofs_dir_inode_operations = {
+ lookup: autofs_dir_lookup,
+};
+
diff --git a/fs/autofs/dirhash.c b/fs/autofs/dirhash.c
new file mode 100644
index 00000000000000..448143fd0796a3
--- /dev/null
+++ b/fs/autofs/dirhash.c
@@ -0,0 +1,249 @@
+/* -*- linux-c -*- --------------------------------------------------------- *
+ *
+ * linux/fs/autofs/dirhash.c
+ *
+ * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ------------------------------------------------------------------------- */
+
+#include "autofs_i.h"
+
+/* Functions for maintenance of expiry queue */
+
+static void autofs_init_usage(struct autofs_dirhash *dh,
+ struct autofs_dir_ent *ent)
+{
+ list_add_tail(&ent->exp, &dh->expiry_head);
+ ent->last_usage = jiffies;
+}
+
+static void autofs_delete_usage(struct autofs_dir_ent *ent)
+{
+ list_del(&ent->exp);
+}
+
+void autofs_update_usage(struct autofs_dirhash *dh,
+ struct autofs_dir_ent *ent)
+{
+ autofs_delete_usage(ent); /* Unlink from current position */
+ autofs_init_usage(dh,ent); /* Relink at queue tail */
+}
+
+struct autofs_dir_ent *autofs_expire(struct super_block *sb,
+ struct autofs_sb_info *sbi,
+ struct vfsmount *mnt)
+{
+ struct autofs_dirhash *dh = &sbi->dirhash;
+ struct autofs_dir_ent *ent;
+ struct dentry *dentry;
+ unsigned long timeout = sbi->exp_timeout;
+
+ while (1) {
+ if ( list_empty(&dh->expiry_head) || sbi->catatonic )
+ return NULL; /* No entries */
+ /* We keep the list sorted by last_usage and want old stuff */
+ ent = list_entry(dh->expiry_head.next, struct autofs_dir_ent, exp);
+ if (jiffies - ent->last_usage < timeout)
+ break;
+ /* Move to end of list in case expiry isn't desirable */
+ autofs_update_usage(dh, ent);
+
+ /* Check to see that entry is expirable */
+ if ( ent->ino < AUTOFS_FIRST_DIR_INO )
+ return ent; /* Symlinks are always expirable */
+
+ /* Get the dentry for the autofs subdirectory */
+ dentry = ent->dentry;
+
+ if ( !dentry ) {
+ /* Should only happen in catatonic mode */
+ printk("autofs: dentry == NULL but inode range is directory, entry %s\n", ent->name);
+ autofs_delete_usage(ent);
+ continue;
+ }
+
+ if ( !dentry->d_inode ) {
+ dput(dentry);
+ printk("autofs: negative dentry on expiry queue: %s\n",
+ ent->name);
+ autofs_delete_usage(ent);
+ continue;
+ }
+
+ /* Make sure entry is mounted and unused; note that dentry will
+ point to the mounted-on-top root. */
+ if (!S_ISDIR(dentry->d_inode->i_mode)||!d_mountpoint(dentry)) {
+ DPRINTK(("autofs: not expirable (not a mounted directory): %s\n", ent->name));
+ continue;
+ }
+ mntget(mnt);
+ dget(dentry);
+ if (!follow_down(&mnt, &dentry)) {
+ dput(dentry);
+ mntput(mnt);
+ DPRINTK(("autofs: not expirable (not a mounted directory): %s\n", ent->name));
+ continue;
+ }
+ while (d_mountpoint(dentry) && follow_down(&mnt, &dentry))
+ ;
+ dput(dentry);
+
+ if ( may_umount(mnt) == 0 ) {
+ mntput(mnt);
+ DPRINTK(("autofs: signaling expire on %s\n", ent->name));
+ return ent; /* Expirable! */
+ }
+ DPRINTK(("autofs: didn't expire due to may_umount: %s\n", ent->name));
+ mntput(mnt);
+ }
+ return NULL; /* No expirable entries */
+}
+
+void autofs_initialize_hash(struct autofs_dirhash *dh) {
+ memset(&dh->h, 0, AUTOFS_HASH_SIZE*sizeof(struct autofs_dir_ent *));
+ INIT_LIST_HEAD(&dh->expiry_head);
+}
+
+struct autofs_dir_ent *autofs_hash_lookup(const struct autofs_dirhash *dh, struct qstr *name)
+{
+ struct autofs_dir_ent *dhn;
+
+ DPRINTK(("autofs_hash_lookup: hash = 0x%08x, name = ", name->hash));
+ autofs_say(name->name,name->len);
+
+ for ( dhn = dh->h[(unsigned) name->hash % AUTOFS_HASH_SIZE] ; dhn ; dhn = dhn->next ) {
+ if ( name->hash == dhn->hash &&
+ name->len == dhn->len &&
+ !memcmp(name->name, dhn->name, name->len) )
+ break;
+ }
+
+ return dhn;
+}
+
+void autofs_hash_insert(struct autofs_dirhash *dh, struct autofs_dir_ent *ent)
+{
+ struct autofs_dir_ent **dhnp;
+
+ DPRINTK(("autofs_hash_insert: hash = 0x%08x, name = ", ent->hash));
+ autofs_say(ent->name,ent->len);
+
+ autofs_init_usage(dh,ent);
+ if (ent->dentry)
+ dget(ent->dentry);
+
+ dhnp = &dh->h[(unsigned) ent->hash % AUTOFS_HASH_SIZE];
+ ent->next = *dhnp;
+ ent->back = dhnp;
+ *dhnp = ent;
+ if ( ent->next )
+ ent->next->back = &(ent->next);
+}
+
+void autofs_hash_delete(struct autofs_dir_ent *ent)
+{
+ *(ent->back) = ent->next;
+ if ( ent->next )
+ ent->next->back = ent->back;
+
+ autofs_delete_usage(ent);
+
+ if ( ent->dentry )
+ dput(ent->dentry);
+ kfree(ent->name);
+ kfree(ent);
+}
+
+/*
+ * Used by readdir(). We must validate "ptr", so we can't simply make it
+ * a pointer. Values below 0xffff are reserved; calling with any value
+ * <= 0x10000 will return the first entry found.
+ *
+ * "last" can be NULL or the value returned by the last search *if* we
+ * want the next sequential entry.
+ */
+struct autofs_dir_ent *autofs_hash_enum(const struct autofs_dirhash *dh,
+ off_t *ptr, struct autofs_dir_ent *last)
+{
+ int bucket, ecount, i;
+ struct autofs_dir_ent *ent;
+
+ bucket = (*ptr >> 16) - 1;
+ ecount = *ptr & 0xffff;
+
+ if ( bucket < 0 ) {
+ bucket = ecount = 0;
+ }
+
+ DPRINTK(("autofs_hash_enum: bucket %d, entry %d\n", bucket, ecount));
+
+ ent = last ? last->next : NULL;
+
+ if ( ent ) {
+ ecount++;
+ } else {
+ while ( bucket < AUTOFS_HASH_SIZE ) {
+ ent = dh->h[bucket];
+ for ( i = ecount ; ent && i ; i-- )
+ ent = ent->next;
+
+ if (ent) {
+ ecount++; /* Point to *next* entry */
+ break;
+ }
+
+ bucket++; ecount = 0;
+ }
+ }
+
+#ifdef DEBUG
+ if ( !ent )
+ printk("autofs_hash_enum: nothing found\n");
+ else {
+ printk("autofs_hash_enum: found hash %08x, name", ent->hash);
+ autofs_say(ent->name,ent->len);
+ }
+#endif
+
+ *ptr = ((bucket+1) << 16) + ecount;
+ return ent;
+}
+
+/* Iterate over all the ents, and remove all dentry pointers. Used on
+ entering catatonic mode, in order to make the filesystem unmountable. */
+void autofs_hash_dputall(struct autofs_dirhash *dh)
+{
+ int i;
+ struct autofs_dir_ent *ent;
+
+ for ( i = 0 ; i < AUTOFS_HASH_SIZE ; i++ ) {
+ for ( ent = dh->h[i] ; ent ; ent = ent->next ) {
+ if ( ent->dentry ) {
+ dput(ent->dentry);
+ ent->dentry = NULL;
+ }
+ }
+ }
+}
+
+/* Delete everything. This is used on filesystem destruction, so we
+ make no attempt to keep the pointers valid */
+void autofs_hash_nuke(struct autofs_dirhash *dh)
+{
+ int i;
+ struct autofs_dir_ent *ent, *nent;
+
+ for ( i = 0 ; i < AUTOFS_HASH_SIZE ; i++ ) {
+ for ( ent = dh->h[i] ; ent ; ent = nent ) {
+ nent = ent->next;
+ if ( ent->dentry )
+ dput(ent->dentry);
+ kfree(ent->name);
+ kfree(ent);
+ }
+ }
+}
diff --git a/fs/autofs/init.c b/fs/autofs/init.c
new file mode 100644
index 00000000000000..0e7fff94f254c8
--- /dev/null
+++ b/fs/autofs/init.c
@@ -0,0 +1,41 @@
+/* -*- linux-c -*- --------------------------------------------------------- *
+ *
+ * linux/fs/autofs/init.c
+ *
+ * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ------------------------------------------------------------------------- */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include "autofs_i.h"
+
+static DECLARE_FSTYPE(autofs_fs_type, "autofs", autofs_read_super, 0);
+
+static int __init init_autofs_fs(void)
+{
+ return register_filesystem(&autofs_fs_type);
+}
+
+static void __exit exit_autofs_fs(void)
+{
+ unregister_filesystem(&autofs_fs_type);
+}
+
+module_init(init_autofs_fs);
+module_exit(exit_autofs_fs);
+
+#ifdef DEBUG
+void autofs_say(const char *name, int len)
+{
+ printk("(%d: ", len);
+ while ( len-- )
+ printk("%c", *name++);
+ printk(")\n");
+}
+#endif
+MODULE_LICENSE("GPL");
diff --git a/fs/autofs/inode.c b/fs/autofs/inode.c
new file mode 100644
index 00000000000000..64218926fe49c3
--- /dev/null
+++ b/fs/autofs/inode.c
@@ -0,0 +1,247 @@
+/* -*- linux-c -*- --------------------------------------------------------- *
+ *
+ * linux/fs/autofs/inode.c
+ *
+ * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ------------------------------------------------------------------------- */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/file.h>
+#include <linux/locks.h>
+#include <asm/bitops.h>
+#include "autofs_i.h"
+#define __NO_VERSION__
+#include <linux/module.h>
+
+static void autofs_put_super(struct super_block *sb)
+{
+ struct autofs_sb_info *sbi = autofs_sbi(sb);
+ unsigned int n;
+
+ if ( !sbi->catatonic )
+ autofs_catatonic_mode(sbi); /* Free wait queues, close pipe */
+
+ autofs_hash_nuke(&sbi->dirhash);
+ for ( n = 0 ; n < AUTOFS_MAX_SYMLINKS ; n++ ) {
+ if ( test_bit(n, sbi->symlink_bitmap) )
+ kfree(sbi->symlink[n].data);
+ }
+
+ kfree(sb->u.generic_sbp);
+
+ DPRINTK(("autofs: shutting down\n"));
+}
+
+static int autofs_statfs(struct super_block *sb, struct statfs *buf);
+static void autofs_read_inode(struct inode *inode);
+
+static struct super_operations autofs_sops = {
+ read_inode: autofs_read_inode,
+ put_super: autofs_put_super,
+ statfs: autofs_statfs,
+};
+
+static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid, pid_t *pgrp, int *minproto, int *maxproto)
+{
+ char *this_char, *value;
+
+ *uid = current->uid;
+ *gid = current->gid;
+ *pgrp = current->pgrp;
+
+ *minproto = *maxproto = AUTOFS_PROTO_VERSION;
+
+ *pipefd = -1;
+
+ if ( !options ) return 1;
+ for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) {
+ if ((value = strchr(this_char,'=')) != NULL)
+ *value++ = 0;
+ if (!strcmp(this_char,"fd")) {
+ if (!value || !*value)
+ return 1;
+ *pipefd = simple_strtoul(value,&value,0);
+ if (*value)
+ return 1;
+ }
+ else if (!strcmp(this_char,"uid")) {
+ if (!value || !*value)
+ return 1;
+ *uid = simple_strtoul(value,&value,0);
+ if (*value)
+ return 1;
+ }
+ else if (!strcmp(this_char,"gid")) {
+ if (!value || !*value)
+ return 1;
+ *gid = simple_strtoul(value,&value,0);
+ if (*value)
+ return 1;
+ }
+ else if (!strcmp(this_char,"pgrp")) {
+ if (!value || !*value)
+ return 1;
+ *pgrp = simple_strtoul(value,&value,0);
+ if (*value)
+ return 1;
+ }
+ else if (!strcmp(this_char,"minproto")) {
+ if (!value || !*value)
+ return 1;
+ *minproto = simple_strtoul(value,&value,0);
+ if (*value)
+ return 1;
+ }
+ else if (!strcmp(this_char,"maxproto")) {
+ if (!value || !*value)
+ return 1;
+ *maxproto = simple_strtoul(value,&value,0);
+ if (*value)
+ return 1;
+ }
+ else break;
+ }
+ return (*pipefd < 0);
+}
+
+struct super_block *autofs_read_super(struct super_block *s, void *data,
+ int silent)
+{
+ struct inode * root_inode;
+ struct dentry * root;
+ struct file * pipe;
+ int pipefd;
+ struct autofs_sb_info *sbi;
+ int minproto, maxproto;
+
+ sbi = (struct autofs_sb_info *) kmalloc(sizeof(struct autofs_sb_info), GFP_KERNEL);
+ if ( !sbi )
+ goto fail_unlock;
+ DPRINTK(("autofs: starting up, sbi = %p\n",sbi));
+
+ s->u.generic_sbp = sbi;
+ sbi->magic = AUTOFS_SBI_MAGIC;
+ sbi->catatonic = 0;
+ sbi->exp_timeout = 0;
+ sbi->oz_pgrp = current->pgrp;
+ autofs_initialize_hash(&sbi->dirhash);
+ sbi->queues = NULL;
+ memset(sbi->symlink_bitmap, 0, sizeof(long)*AUTOFS_SYMLINK_BITMAP_LEN);
+ sbi->next_dir_ino = AUTOFS_FIRST_DIR_INO;
+ s->s_blocksize = 1024;
+ s->s_blocksize_bits = 10;
+ s->s_magic = AUTOFS_SUPER_MAGIC;
+ s->s_op = &autofs_sops;
+
+ root_inode = iget(s, AUTOFS_ROOT_INO);
+ root = d_alloc_root(root_inode);
+ pipe = NULL;
+
+ if (!root)
+ goto fail_iput;
+
+ /* Can this call block? - WTF cares? s is locked. */
+ if ( parse_options(data,&pipefd,&root_inode->i_uid,&root_inode->i_gid,&sbi->oz_pgrp,&minproto,&maxproto) ) {
+ printk("autofs: called with bogus options\n");
+ goto fail_dput;
+ }
+
+ /* Couldn't this be tested earlier? */
+ if ( minproto > AUTOFS_PROTO_VERSION ||
+ maxproto < AUTOFS_PROTO_VERSION ) {
+ printk("autofs: kernel does not match daemon version\n");
+ goto fail_dput;
+ }
+
+ DPRINTK(("autofs: pipe fd = %d, pgrp = %u\n", pipefd, sbi->oz_pgrp));
+ pipe = fget(pipefd);
+
+ if ( !pipe ) {
+ printk("autofs: could not open pipe file descriptor\n");
+ goto fail_dput;
+ }
+ if ( !pipe->f_op || !pipe->f_op->write )
+ goto fail_fput;
+ sbi->pipe = pipe;
+
+ /*
+ * Success! Install the root dentry now to indicate completion.
+ */
+ s->s_root = root;
+ return s;
+
+fail_fput:
+ printk("autofs: pipe file descriptor does not contain proper ops\n");
+ fput(pipe);
+fail_dput:
+ dput(root);
+ goto fail_free;
+fail_iput:
+ printk("autofs: get root dentry failed\n");
+ iput(root_inode);
+fail_free:
+ kfree(sbi);
+fail_unlock:
+ return NULL;
+}
+
+static int autofs_statfs(struct super_block *sb, struct statfs *buf)
+{
+ buf->f_type = AUTOFS_SUPER_MAGIC;
+ buf->f_bsize = 1024;
+ buf->f_namelen = NAME_MAX;
+ return 0;
+}
+
+static void autofs_read_inode(struct inode *inode)
+{
+ ino_t ino = inode->i_ino;
+ unsigned int n;
+ struct autofs_sb_info *sbi = autofs_sbi(inode->i_sb);
+
+ /* Initialize to the default case (stub directory) */
+
+ inode->i_op = &autofs_dir_inode_operations;
+ inode->i_fop = &dcache_dir_ops;
+ inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
+ inode->i_nlink = 2;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+ inode->i_blocks = 0;
+ inode->i_blksize = 1024;
+
+ if ( ino == AUTOFS_ROOT_INO ) {
+ inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR;
+ inode->i_op = &autofs_root_inode_operations;
+ inode->i_fop = &autofs_root_operations;
+ inode->i_uid = inode->i_gid = 0; /* Changed in read_super */
+ return;
+ }
+
+ inode->i_uid = inode->i_sb->s_root->d_inode->i_uid;
+ inode->i_gid = inode->i_sb->s_root->d_inode->i_gid;
+
+ if ( ino >= AUTOFS_FIRST_SYMLINK && ino < AUTOFS_FIRST_DIR_INO ) {
+ /* Symlink inode - should be in symlink list */
+ struct autofs_symlink *sl;
+
+ n = ino - AUTOFS_FIRST_SYMLINK;
+ if ( n >= AUTOFS_MAX_SYMLINKS || !test_bit(n,sbi->symlink_bitmap)) {
+ printk("autofs: Looking for bad symlink inode %u\n", (unsigned int) ino);
+ return;
+ }
+
+ inode->i_op = &autofs_symlink_inode_operations;
+ sl = &sbi->symlink[n];
+ inode->u.generic_ip = sl;
+ inode->i_mode = S_IFLNK | S_IRWXUGO;
+ inode->i_mtime = inode->i_ctime = sl->mtime;
+ inode->i_size = sl->len;
+ inode->i_nlink = 1;
+ }
+}
diff --git a/fs/autofs/root.c b/fs/autofs/root.c
new file mode 100644
index 00000000000000..fe3db57a046e1a
--- /dev/null
+++ b/fs/autofs/root.c
@@ -0,0 +1,509 @@
+/* -*- linux-c -*- --------------------------------------------------------- *
+ *
+ * linux/fs/autofs/root.c
+ *
+ * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ------------------------------------------------------------------------- */
+
+#include <linux/errno.h>
+#include <linux/stat.h>
+#include <linux/param.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include "autofs_i.h"
+
+static int autofs_root_readdir(struct file *,void *,filldir_t);
+static struct dentry *autofs_root_lookup(struct inode *,struct dentry *);
+static int autofs_root_symlink(struct inode *,struct dentry *,const char *);
+static int autofs_root_unlink(struct inode *,struct dentry *);
+static int autofs_root_rmdir(struct inode *,struct dentry *);
+static int autofs_root_mkdir(struct inode *,struct dentry *,int);
+static int autofs_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long);
+
+struct file_operations autofs_root_operations = {
+ read: generic_read_dir,
+ readdir: autofs_root_readdir,
+ ioctl: autofs_root_ioctl,
+};
+
+struct inode_operations autofs_root_inode_operations = {
+ lookup: autofs_root_lookup,
+ unlink: autofs_root_unlink,
+ symlink: autofs_root_symlink,
+ mkdir: autofs_root_mkdir,
+ rmdir: autofs_root_rmdir,
+};
+
+static int autofs_root_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+ struct autofs_dir_ent *ent = NULL;
+ struct autofs_dirhash *dirhash;
+ struct autofs_sb_info *sbi;
+ struct inode * inode = filp->f_dentry->d_inode;
+ off_t onr, nr;
+
+ sbi = autofs_sbi(inode->i_sb);
+ dirhash = &sbi->dirhash;
+ nr = filp->f_pos;
+
+ switch(nr)
+ {
+ case 0:
+ if (filldir(dirent, ".", 1, nr, inode->i_ino, DT_DIR) < 0)
+ return 0;
+ filp->f_pos = ++nr;
+ /* fall through */
+ case 1:
+ if (filldir(dirent, "..", 2, nr, inode->i_ino, DT_DIR) < 0)
+ return 0;
+ filp->f_pos = ++nr;
+ /* fall through */
+ default:
+ while ( onr = nr, ent = autofs_hash_enum(dirhash,&nr,ent) ) {
+ if ( !ent->dentry || d_mountpoint(ent->dentry) ) {
+ if (filldir(dirent,ent->name,ent->len,onr,ent->ino,DT_UNKNOWN) < 0)
+ return 0;
+ filp->f_pos = nr;
+ }
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int try_to_fill_dentry(struct dentry *dentry, struct super_block *sb, struct autofs_sb_info *sbi)
+{
+ struct inode * inode;
+ struct autofs_dir_ent *ent;
+ int status = 0;
+
+ if ( !(ent = autofs_hash_lookup(&sbi->dirhash, &dentry->d_name)) ) {
+ do {
+ if ( status && dentry->d_inode ) {
+ if ( status != -ENOENT )
+ printk("autofs warning: lookup failure on positive dentry, status = %d, name = %s\n", status, dentry->d_name.name);
+ return 0; /* Try to get the kernel to invalidate this dentry */
+ }
+
+ /* Turn this into a real negative dentry? */
+ if (status == -ENOENT) {
+ dentry->d_time = jiffies + AUTOFS_NEGATIVE_TIMEOUT;
+ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING;
+ return 1;
+ } else if (status) {
+ /* Return a negative dentry, but leave it "pending" */
+ return 1;
+ }
+ status = autofs_wait(sbi, &dentry->d_name);
+ } while (!(ent = autofs_hash_lookup(&sbi->dirhash, &dentry->d_name)) );
+ }
+
+ /* Abuse this field as a pointer to the directory entry, used to
+ find the expire list pointers */
+ dentry->d_time = (unsigned long) ent;
+
+ if (!dentry->d_inode) {
+ inode = iget(sb, ent->ino);
+ if (!inode) {
+ /* Failed, but leave pending for next time */
+ return 1;
+ }
+ dentry->d_inode = inode;
+ }
+
+ /* If this is a directory that isn't a mount point, bitch at the
+ daemon and fix it in user space */
+ if ( S_ISDIR(dentry->d_inode->i_mode) && !d_mountpoint(dentry) ) {
+ return !autofs_wait(sbi, &dentry->d_name);
+ }
+
+ /* We don't update the usages for the autofs daemon itself, this
+ is necessary for recursive autofs mounts */
+ if ( !autofs_oz_mode(sbi) ) {
+ autofs_update_usage(&sbi->dirhash,ent);
+ }
+
+ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING;
+ return 1;
+}
+
+
+/*
+ * Revalidate is called on every cache lookup. Some of those
+ * cache lookups may actually happen while the dentry is not
+ * yet completely filled in, and revalidate has to delay such
+ * lookups..
+ */
+static int autofs_revalidate(struct dentry * dentry, int flags)
+{
+ struct inode * dir;
+ struct autofs_sb_info *sbi;
+ struct autofs_dir_ent *ent;
+ int res;
+
+ lock_kernel();
+ dir = dentry->d_parent->d_inode;
+ sbi = autofs_sbi(dir->i_sb);
+
+ /* Pending dentry */
+ if ( dentry->d_flags & DCACHE_AUTOFS_PENDING ) {
+ if (autofs_oz_mode(sbi))
+ res = 1;
+ else
+ res = try_to_fill_dentry(dentry, dir->i_sb, sbi);
+ unlock_kernel();
+ return res;
+ }
+
+ /* Negative dentry.. invalidate if "old" */
+ if (!dentry->d_inode) {
+ unlock_kernel();
+ return (dentry->d_time - jiffies <= AUTOFS_NEGATIVE_TIMEOUT);
+ }
+
+ /* Check for a non-mountpoint directory */
+ if ( S_ISDIR(dentry->d_inode->i_mode) && !d_mountpoint(dentry) ) {
+ if (autofs_oz_mode(sbi))
+ res = 1;
+ else
+ res = try_to_fill_dentry(dentry, dir->i_sb, sbi);
+ unlock_kernel();
+ return res;
+ }
+
+ /* Update the usage list */
+ if ( !autofs_oz_mode(sbi) ) {
+ ent = (struct autofs_dir_ent *) dentry->d_time;
+ if ( ent )
+ autofs_update_usage(&sbi->dirhash,ent);
+ }
+ unlock_kernel();
+ return 1;
+}
+
+static struct dentry_operations autofs_dentry_operations = {
+ d_revalidate: autofs_revalidate,
+};
+
+static struct dentry *autofs_root_lookup(struct inode *dir, struct dentry *dentry)
+{
+ struct autofs_sb_info *sbi;
+ int oz_mode;
+
+ DPRINTK(("autofs_root_lookup: name = "));
+ autofs_say(dentry->d_name.name,dentry->d_name.len);
+
+ if (dentry->d_name.len > NAME_MAX)
+ return ERR_PTR(-ENAMETOOLONG);/* File name too long to exist */
+
+ sbi = autofs_sbi(dir->i_sb);
+
+ oz_mode = autofs_oz_mode(sbi);
+ DPRINTK(("autofs_lookup: pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d\n",
+ current->pid, current->pgrp, sbi->catatonic, oz_mode));
+
+ /*
+ * Mark the dentry incomplete, but add it. This is needed so
+ * that the VFS layer knows about the dentry, and we can count
+ * on catching any lookups through the revalidate.
+ *
+ * Let all the hard work be done by the revalidate function that
+ * needs to be able to do this anyway..
+ *
+ * We need to do this before we release the directory semaphore.
+ */
+ dentry->d_op = &autofs_dentry_operations;
+ dentry->d_flags |= DCACHE_AUTOFS_PENDING;
+ d_add(dentry, NULL);
+
+ up(&dir->i_sem);
+ autofs_revalidate(dentry, 0);
+ down(&dir->i_sem);
+
+ /*
+ * If we are still pending, check if we had to handle
+ * a signal. If so we can force a restart..
+ */
+ if (dentry->d_flags & DCACHE_AUTOFS_PENDING) {
+ if (signal_pending(current))
+ return ERR_PTR(-ERESTARTNOINTR);
+ }
+
+ /*
+ * If this dentry is unhashed, then we shouldn't honour this
+ * lookup even if the dentry is positive. Returning ENOENT here
+ * doesn't do the right thing for all system calls, but it should
+ * be OK for the operations we permit from an autofs.
+ */
+ if ( dentry->d_inode && d_unhashed(dentry) )
+ return ERR_PTR(-ENOENT);
+
+ return NULL;
+}
+
+static int autofs_root_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
+{
+ struct autofs_sb_info *sbi = autofs_sbi(dir->i_sb);
+ struct autofs_dirhash *dh = &sbi->dirhash;
+ struct autofs_dir_ent *ent;
+ unsigned int n;
+ int slsize;
+ struct autofs_symlink *sl;
+
+ DPRINTK(("autofs_root_symlink: %s <- ", symname));
+ autofs_say(dentry->d_name.name,dentry->d_name.len);
+
+ if ( !autofs_oz_mode(sbi) )
+ return -EACCES;
+
+ if ( autofs_hash_lookup(dh, &dentry->d_name) )
+ return -EEXIST;
+
+ n = find_first_zero_bit(sbi->symlink_bitmap,AUTOFS_MAX_SYMLINKS);
+ if ( n >= AUTOFS_MAX_SYMLINKS )
+ return -ENOSPC;
+
+ set_bit(n,sbi->symlink_bitmap);
+ sl = &sbi->symlink[n];
+ sl->len = strlen(symname);
+ sl->data = kmalloc(slsize = sl->len+1, GFP_KERNEL);
+ if ( !sl->data ) {
+ clear_bit(n,sbi->symlink_bitmap);
+ return -ENOSPC;
+ }
+
+ ent = kmalloc(sizeof(struct autofs_dir_ent), GFP_KERNEL);
+ if ( !ent ) {
+ kfree(sl->data);
+ clear_bit(n,sbi->symlink_bitmap);
+ return -ENOSPC;
+ }
+
+ ent->name = kmalloc(dentry->d_name.len+1, GFP_KERNEL);
+ if ( !ent->name ) {
+ kfree(sl->data);
+ kfree(ent);
+ clear_bit(n,sbi->symlink_bitmap);
+ return -ENOSPC;
+ }
+
+ memcpy(sl->data,symname,slsize);
+ sl->mtime = CURRENT_TIME;
+
+ ent->ino = AUTOFS_FIRST_SYMLINK + n;
+ ent->hash = dentry->d_name.hash;
+ memcpy(ent->name, dentry->d_name.name, 1+(ent->len = dentry->d_name.len));
+ ent->dentry = NULL; /* We don't keep the dentry for symlinks */
+
+ autofs_hash_insert(dh,ent);
+ d_instantiate(dentry, iget(dir->i_sb,ent->ino));
+
+ return 0;
+}
+
+/*
+ * NOTE!
+ *
+ * Normal filesystems would do a "d_delete()" to tell the VFS dcache
+ * that the file no longer exists. However, doing that means that the
+ * VFS layer can turn the dentry into a negative dentry, which we
+ * obviously do not want (we're dropping the entry not because it
+ * doesn't exist, but because it has timed out).
+ *
+ * Also see autofs_root_rmdir()..
+ */
+static int autofs_root_unlink(struct inode *dir, struct dentry *dentry)
+{
+ struct autofs_sb_info *sbi = autofs_sbi(dir->i_sb);
+ struct autofs_dirhash *dh = &sbi->dirhash;
+ struct autofs_dir_ent *ent;
+ unsigned int n;
+
+ /* This allows root to remove symlinks */
+ if ( !autofs_oz_mode(sbi) && !capable(CAP_SYS_ADMIN) )
+ return -EACCES;
+
+ ent = autofs_hash_lookup(dh, &dentry->d_name);
+ if ( !ent )
+ return -ENOENT;
+
+ n = ent->ino - AUTOFS_FIRST_SYMLINK;
+ if ( n >= AUTOFS_MAX_SYMLINKS )
+ return -EISDIR; /* It's a directory, dummy */
+ if ( !test_bit(n,sbi->symlink_bitmap) )
+ return -EINVAL; /* Nonexistent symlink? Shouldn't happen */
+
+ dentry->d_time = (unsigned long)(struct autofs_dirhash *)NULL;
+ autofs_hash_delete(ent);
+ clear_bit(n,sbi->symlink_bitmap);
+ kfree(sbi->symlink[n].data);
+ d_drop(dentry);
+
+ return 0;
+}
+
+static int autofs_root_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ struct autofs_sb_info *sbi = autofs_sbi(dir->i_sb);
+ struct autofs_dirhash *dh = &sbi->dirhash;
+ struct autofs_dir_ent *ent;
+
+ if ( !autofs_oz_mode(sbi) )
+ return -EACCES;
+
+ ent = autofs_hash_lookup(dh, &dentry->d_name);
+ if ( !ent )
+ return -ENOENT;
+
+ if ( (unsigned int)ent->ino < AUTOFS_FIRST_DIR_INO )
+ return -ENOTDIR; /* Not a directory */
+
+ if ( ent->dentry != dentry ) {
+ printk("autofs_rmdir: odentry != dentry for entry %s\n", dentry->d_name.name);
+ }
+
+ dentry->d_time = (unsigned long)(struct autofs_dir_ent *)NULL;
+ autofs_hash_delete(ent);
+ dir->i_nlink--;
+ d_drop(dentry);
+
+ return 0;
+}
+
+static int autofs_root_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+ struct autofs_sb_info *sbi = autofs_sbi(dir->i_sb);
+ struct autofs_dirhash *dh = &sbi->dirhash;
+ struct autofs_dir_ent *ent;
+ ino_t ino;
+
+ if ( !autofs_oz_mode(sbi) )
+ return -EACCES;
+
+ ent = autofs_hash_lookup(dh, &dentry->d_name);
+ if ( ent )
+ return -EEXIST;
+
+ if ( sbi->next_dir_ino < AUTOFS_FIRST_DIR_INO ) {
+ printk("autofs: Out of inode numbers -- what the heck did you do??\n");
+ return -ENOSPC;
+ }
+ ino = sbi->next_dir_ino++;
+
+ ent = kmalloc(sizeof(struct autofs_dir_ent), GFP_KERNEL);
+ if ( !ent )
+ return -ENOSPC;
+
+ ent->name = kmalloc(dentry->d_name.len+1, GFP_KERNEL);
+ if ( !ent->name ) {
+ kfree(ent);
+ return -ENOSPC;
+ }
+
+ ent->hash = dentry->d_name.hash;
+ memcpy(ent->name, dentry->d_name.name, 1+(ent->len = dentry->d_name.len));
+ ent->ino = ino;
+ ent->dentry = dentry;
+ autofs_hash_insert(dh,ent);
+
+ dir->i_nlink++;
+ d_instantiate(dentry, iget(dir->i_sb,ino));
+
+ return 0;
+}
+
+/* Get/set timeout ioctl() operation */
+static inline int autofs_get_set_timeout(struct autofs_sb_info *sbi,
+ unsigned long *p)
+{
+ unsigned long ntimeout;
+
+ if (get_user(ntimeout, p) ||
+ put_user(sbi->exp_timeout / HZ, p))
+ return -EFAULT;
+
+ if ( ntimeout > ULONG_MAX/HZ )
+ sbi->exp_timeout = 0;
+ else
+ sbi->exp_timeout = ntimeout * HZ;
+
+ return 0;
+}
+
+/* Return protocol version */
+static inline int autofs_get_protover(int *p)
+{
+ return put_user(AUTOFS_PROTO_VERSION, p);
+}
+
+/* Perform an expiry operation */
+static inline int autofs_expire_run(struct super_block *sb,
+ struct autofs_sb_info *sbi,
+ struct vfsmount *mnt,
+ struct autofs_packet_expire *pkt_p)
+{
+ struct autofs_dir_ent *ent;
+ struct autofs_packet_expire pkt;
+
+ memset(&pkt,0,sizeof pkt);
+
+ pkt.hdr.proto_version = AUTOFS_PROTO_VERSION;
+ pkt.hdr.type = autofs_ptype_expire;
+
+ if ( !sbi->exp_timeout ||
+ !(ent = autofs_expire(sb,sbi,mnt)) )
+ return -EAGAIN;
+
+ pkt.len = ent->len;
+ memcpy(pkt.name, ent->name, pkt.len);
+ pkt.name[pkt.len] = '\0';
+
+ if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) )
+ return -EFAULT;
+
+ return 0;
+}
+
+/*
+ * ioctl()'s on the root directory is the chief method for the daemon to
+ * generate kernel reactions
+ */
+static int autofs_root_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct autofs_sb_info *sbi = autofs_sbi(inode->i_sb);
+
+ DPRINTK(("autofs_ioctl: cmd = 0x%08x, arg = 0x%08lx, sbi = %p, pgrp = %u\n",cmd,arg,sbi,current->pgrp));
+
+ if ( _IOC_TYPE(cmd) != _IOC_TYPE(AUTOFS_IOC_FIRST) ||
+ _IOC_NR(cmd) - _IOC_NR(AUTOFS_IOC_FIRST) >= AUTOFS_IOC_COUNT )
+ return -ENOTTY;
+
+ if ( !autofs_oz_mode(sbi) && !capable(CAP_SYS_ADMIN) )
+ return -EPERM;
+
+ switch(cmd) {
+ case AUTOFS_IOC_READY: /* Wait queue: go ahead and retry */
+ return autofs_wait_release(sbi,(autofs_wqt_t)arg,0);
+ case AUTOFS_IOC_FAIL: /* Wait queue: fail with ENOENT */
+ return autofs_wait_release(sbi,(autofs_wqt_t)arg,-ENOENT);
+ case AUTOFS_IOC_CATATONIC: /* Enter catatonic mode (daemon shutdown) */
+ autofs_catatonic_mode(sbi);
+ return 0;
+ case AUTOFS_IOC_PROTOVER: /* Get protocol version */
+ return autofs_get_protover((int *)arg);
+ case AUTOFS_IOC_SETTIMEOUT:
+ return autofs_get_set_timeout(sbi,(unsigned long *)arg);
+ case AUTOFS_IOC_EXPIRE:
+ return autofs_expire_run(inode->i_sb, sbi, filp->f_vfsmnt,
+ (struct autofs_packet_expire *)arg);
+ default:
+ return -ENOSYS;
+ }
+}
diff --git a/fs/autofs/symlink.c b/fs/autofs/symlink.c
new file mode 100644
index 00000000000000..3b90a077ea02a4
--- /dev/null
+++ b/fs/autofs/symlink.c
@@ -0,0 +1,30 @@
+/* -*- linux-c -*- --------------------------------------------------------- *
+ *
+ * linux/fs/autofs/symlink.c
+ *
+ * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ------------------------------------------------------------------------- */
+
+#include "autofs_i.h"
+
+static int autofs_readlink(struct dentry *dentry, char *buffer, int buflen)
+{
+ char *s=((struct autofs_symlink *)dentry->d_inode->u.generic_ip)->data;
+ return vfs_readlink(dentry, buffer, buflen, s);
+}
+
+static int autofs_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+ char *s=((struct autofs_symlink *)dentry->d_inode->u.generic_ip)->data;
+ return vfs_follow_link(nd, s);
+}
+
+struct inode_operations autofs_symlink_inode_operations = {
+ readlink: autofs_readlink,
+ follow_link: autofs_follow_link
+};
diff --git a/fs/autofs/waitq.c b/fs/autofs/waitq.c
new file mode 100644
index 00000000000000..c7da7f518f059a
--- /dev/null
+++ b/fs/autofs/waitq.c
@@ -0,0 +1,213 @@
+/* -*- linux-c -*- --------------------------------------------------------- *
+ *
+ * linux/fs/autofs/waitq.c
+ *
+ * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ------------------------------------------------------------------------- */
+
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/file.h>
+#include "autofs_i.h"
+
+/* We make this a static variable rather than a part of the superblock; it
+ is better if we don't reassign numbers easily even across filesystems */
+static autofs_wqt_t autofs_next_wait_queue = 1;
+
+/* These are the signals we allow interrupting a pending mount */
+#define SHUTDOWN_SIGS (sigmask(SIGKILL) | sigmask(SIGINT) | sigmask(SIGQUIT))
+
+void autofs_catatonic_mode(struct autofs_sb_info *sbi)
+{
+ struct autofs_wait_queue *wq, *nwq;
+
+ DPRINTK(("autofs: entering catatonic mode\n"));
+
+ sbi->catatonic = 1;
+ wq = sbi->queues;
+ sbi->queues = NULL; /* Erase all wait queues */
+ while ( wq ) {
+ nwq = wq->next;
+ wq->status = -ENOENT; /* Magic is gone - report failure */
+ kfree(wq->name);
+ wq->name = NULL;
+ wake_up(&wq->queue);
+ wq = nwq;
+ }
+ fput(sbi->pipe); /* Close the pipe */
+ autofs_hash_dputall(&sbi->dirhash); /* Remove all dentry pointers */
+}
+
+static int autofs_write(struct file *file, const void *addr, int bytes)
+{
+ unsigned long sigpipe, flags;
+ mm_segment_t fs;
+ const char *data = (const char *)addr;
+ ssize_t wr = 0;
+
+ /** WARNING: this is not safe for writing more than PIPE_BUF bytes! **/
+
+ sigpipe = sigismember(&current->pending.signal, SIGPIPE);
+
+ /* Save pointer to user space and point back to kernel space */
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ while (bytes &&
+ (wr = file->f_op->write(file,data,bytes,&file->f_pos)) > 0) {
+ data += wr;
+ bytes -= wr;
+ }
+
+ set_fs(fs);
+
+ /* Keep the currently executing process from receiving a
+ SIGPIPE unless it was already supposed to get one */
+ if (wr == -EPIPE && !sigpipe) {
+ spin_lock_irqsave(&current->sigmask_lock, flags);
+ sigdelset(&current->pending.signal, SIGPIPE);
+ recalc_sigpending(current);
+ spin_unlock_irqrestore(&current->sigmask_lock, flags);
+ }
+
+ return (bytes > 0);
+}
+
+static void autofs_notify_daemon(struct autofs_sb_info *sbi, struct autofs_wait_queue *wq)
+{
+ struct autofs_packet_missing pkt;
+
+ DPRINTK(("autofs_wait: wait id = 0x%08lx, name = ", wq->wait_queue_token));
+ autofs_say(wq->name,wq->len);
+
+ memset(&pkt,0,sizeof pkt); /* For security reasons */
+
+ pkt.hdr.proto_version = AUTOFS_PROTO_VERSION;
+ pkt.hdr.type = autofs_ptype_missing;
+ pkt.wait_queue_token = wq->wait_queue_token;
+ pkt.len = wq->len;
+ memcpy(pkt.name, wq->name, pkt.len);
+ pkt.name[pkt.len] = '\0';
+
+ if ( autofs_write(sbi->pipe,&pkt,sizeof(struct autofs_packet_missing)) )
+ autofs_catatonic_mode(sbi);
+}
+
+int autofs_wait(struct autofs_sb_info *sbi, struct qstr *name)
+{
+ struct autofs_wait_queue *wq;
+ int status;
+
+ /* In catatonic mode, we don't wait for nobody */
+ if ( sbi->catatonic )
+ return -ENOENT;
+
+ /* We shouldn't be able to get here, but just in case */
+ if ( name->len > NAME_MAX )
+ return -ENOENT;
+
+ for ( wq = sbi->queues ; wq ; wq = wq->next ) {
+ if ( wq->hash == name->hash &&
+ wq->len == name->len &&
+ wq->name && !memcmp(wq->name,name->name,name->len) )
+ break;
+ }
+
+ if ( !wq ) {
+ /* Create a new wait queue */
+ wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL);
+ if ( !wq )
+ return -ENOMEM;
+
+ wq->name = kmalloc(name->len,GFP_KERNEL);
+ if ( !wq->name ) {
+ kfree(wq);
+ return -ENOMEM;
+ }
+ wq->wait_queue_token = autofs_next_wait_queue++;
+ init_waitqueue_head(&wq->queue);
+ wq->hash = name->hash;
+ wq->len = name->len;
+ wq->status = -EINTR; /* Status return if interrupted */
+ memcpy(wq->name, name->name, name->len);
+ wq->next = sbi->queues;
+ sbi->queues = wq;
+
+ /* autofs_notify_daemon() may block */
+ wq->wait_ctr = 2;
+ autofs_notify_daemon(sbi,wq);
+ } else
+ wq->wait_ctr++;
+
+ /* wq->name is NULL if and only if the lock is already released */
+
+ if ( sbi->catatonic ) {
+ /* We might have slept, so check again for catatonic mode */
+ wq->status = -ENOENT;
+ if ( wq->name ) {
+ kfree(wq->name);
+ wq->name = NULL;
+ }
+ }
+
+ if ( wq->name ) {
+ /* Block all but "shutdown" signals while waiting */
+ sigset_t oldset;
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&current->sigmask_lock, irqflags);
+ oldset = current->blocked;
+ siginitsetinv(&current->blocked, SHUTDOWN_SIGS & ~oldset.sig[0]);
+ recalc_sigpending(current);
+ spin_unlock_irqrestore(&current->sigmask_lock, irqflags);
+
+ interruptible_sleep_on(&wq->queue);
+
+ spin_lock_irqsave(&current->sigmask_lock, irqflags);
+ current->blocked = oldset;
+ recalc_sigpending(current);
+ spin_unlock_irqrestore(&current->sigmask_lock, irqflags);
+ } else {
+ DPRINTK(("autofs_wait: skipped sleeping\n"));
+ }
+
+ status = wq->status;
+
+ if ( ! --wq->wait_ctr ) /* Are we the last process to need status? */
+ kfree(wq);
+
+ return status;
+}
+
+
+int autofs_wait_release(struct autofs_sb_info *sbi, autofs_wqt_t wait_queue_token, int status)
+{
+ struct autofs_wait_queue *wq, **wql;
+
+ for ( wql = &sbi->queues ; (wq = *wql) ; wql = &wq->next ) {
+ if ( wq->wait_queue_token == wait_queue_token )
+ break;
+ }
+ if ( !wq )
+ return -EINVAL;
+
+ *wql = wq->next; /* Unlink from chain */
+ kfree(wq->name);
+ wq->name = NULL; /* Do not wait on this queue */
+
+ wq->status = status;
+
+ if ( ! --wq->wait_ctr ) /* Is anyone still waiting for this guy? */
+ kfree(wq);
+ else
+ wake_up(&wq->queue);
+
+ return 0;
+}
+
diff --git a/fs/autofs4/Makefile b/fs/autofs4/Makefile
new file mode 100644
index 00000000000000..1eb09f0bf1b1a4
--- /dev/null
+++ b/fs/autofs4/Makefile
@@ -0,0 +1,13 @@
+#
+# Makefile for the linux autofs-filesystem routines.
+#
+# We can build this either out of the kernel tree or the autofs tools tree.
+#
+
+O_TARGET := autofs4.o
+
+obj-y := init.o inode.o root.o symlink.o waitq.o expire.o
+
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h
new file mode 100644
index 00000000000000..18335d427c6a6c
--- /dev/null
+++ b/fs/autofs4/autofs_i.h
@@ -0,0 +1,160 @@
+/* -*- c -*- ------------------------------------------------------------- *
+ *
+ * linux/fs/autofs/autofs_i.h
+ *
+ * Copyright 1997-1998 Transmeta Corporation - All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/* Internal header file for autofs */
+
+#include <linux/auto_fs4.h>
+#include <linux/list.h>
+
+/* This is the range of ioctl() numbers we claim as ours */
+#define AUTOFS_IOC_FIRST AUTOFS_IOC_READY
+#define AUTOFS_IOC_COUNT 32
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/wait.h>
+#include <asm/uaccess.h>
+
+/* #define DEBUG */
+
+#ifdef DEBUG
+#define DPRINTK(D) do{ printk("pid %d: ", current->pid); printk D; } while(0)
+#else
+#define DPRINTK(D) do {} while(0)
+#endif
+
+#define AUTOFS_SUPER_MAGIC 0x0187
+
+/*
+ * If the daemon returns a negative response (AUTOFS_IOC_FAIL) then the
+ * kernel will keep the negative response cached for up to the time given
+ * here, although the time can be shorter if the kernel throws the dcache
+ * entry away. This probably should be settable from user space.
+ */
+#define AUTOFS_NEGATIVE_TIMEOUT (60*HZ) /* 1 minute */
+
+/* Unified info structure. This is pointed to by both the dentry and
+ inode structures. Each file in the filesystem has an instance of this
+ structure. It holds a reference to the dentry, so dentries are never
+ flushed while the file exists. All name lookups are dealt with at the
+ dentry level, although the filesystem can interfere in the validation
+ process. Readdir is implemented by traversing the dentry lists. */
+struct autofs_info {
+ struct dentry *dentry;
+ struct inode *inode;
+
+ int flags;
+
+ struct autofs_sb_info *sbi;
+ unsigned long last_used;
+
+ mode_t mode;
+ size_t size;
+
+ void (*free)(struct autofs_info *);
+ union {
+ const char *symlink;
+ } u;
+};
+
+#define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */
+
+struct autofs_wait_queue {
+ wait_queue_head_t queue;
+ struct autofs_wait_queue *next;
+ autofs_wqt_t wait_queue_token;
+ /* We use the following to see what we are waiting for */
+ int hash;
+ int len;
+ char *name;
+ /* This is for status reporting upon return */
+ int status;
+ int wait_ctr;
+};
+
+#define AUTOFS_SBI_MAGIC 0x6d4a556d
+
+struct autofs_sb_info {
+ u32 magic;
+ struct file *pipe;
+ pid_t oz_pgrp;
+ int catatonic;
+ int version;
+ unsigned long exp_timeout;
+ struct super_block *sb;
+ struct autofs_wait_queue *queues; /* Wait queue pointer */
+};
+
+static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb)
+{
+ return (struct autofs_sb_info *)(sb->u.generic_sbp);
+}
+
+static inline struct autofs_info *autofs4_dentry_ino(struct dentry *dentry)
+{
+ return (struct autofs_info *)(dentry->d_fsdata);
+}
+
+/* autofs4_oz_mode(): do we see the man behind the curtain? (The
+ processes which do manipulations for us in user space sees the raw
+ filesystem without "magic".) */
+
+static inline int autofs4_oz_mode(struct autofs_sb_info *sbi) {
+ return sbi->catatonic || current->pgrp == sbi->oz_pgrp;
+}
+
+/* Does a dentry have some pending activity? */
+static inline int autofs4_ispending(struct dentry *dentry)
+{
+ struct autofs_info *inf = autofs4_dentry_ino(dentry);
+
+ return (dentry->d_flags & DCACHE_AUTOFS_PENDING) ||
+ (inf != NULL && inf->flags & AUTOFS_INF_EXPIRING);
+}
+
+struct inode *autofs4_get_inode(struct super_block *, struct autofs_info *);
+struct autofs_info *autofs4_init_inf(struct autofs_sb_info *, mode_t mode);
+void autofs4_free_ino(struct autofs_info *);
+
+/* Expiration */
+int is_autofs4_dentry(struct dentry *);
+int autofs4_expire_run(struct super_block *, struct vfsmount *,
+ struct autofs_sb_info *, struct autofs_packet_expire *);
+int autofs4_expire_multi(struct super_block *, struct vfsmount *,
+ struct autofs_sb_info *, int *);
+
+/* Operations structures */
+
+extern struct inode_operations autofs4_symlink_inode_operations;
+extern struct inode_operations autofs4_dir_inode_operations;
+extern struct inode_operations autofs4_root_inode_operations;
+extern struct file_operations autofs4_root_operations;
+
+/* Initializing function */
+
+struct super_block *autofs4_read_super(struct super_block *, void *,int);
+struct autofs_info *autofs4_init_ino(struct autofs_info *, struct autofs_sb_info *sbi, mode_t mode);
+
+/* Queue management functions */
+
+enum autofs_notify
+{
+ NFY_NONE,
+ NFY_MOUNT,
+ NFY_EXPIRE
+};
+
+int autofs4_wait(struct autofs_sb_info *,struct qstr *, enum autofs_notify);
+int autofs4_wait_release(struct autofs_sb_info *,autofs_wqt_t,int);
+void autofs4_catatonic_mode(struct autofs_sb_info *);
diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c
new file mode 100644
index 00000000000000..01f6ad59a5d175
--- /dev/null
+++ b/fs/autofs4/expire.c
@@ -0,0 +1,269 @@
+/* -*- c -*- --------------------------------------------------------------- *
+ *
+ * linux/fs/autofs/expire.c
+ *
+ * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
+ * Copyright 1999-2000 Jeremy Fitzhardinge <jeremy@goop.org>
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ------------------------------------------------------------------------- */
+
+#include "autofs_i.h"
+
+/*
+ * Determine if a subtree of the namespace is busy.
+ *
+ * mnt is the mount tree under the autofs mountpoint
+ */
+static inline int is_vfsmnt_tree_busy(struct vfsmount *mnt)
+{
+ struct vfsmount *this_parent = mnt;
+ struct list_head *next;
+ int count;
+
+ count = atomic_read(&mnt->mnt_count) - 1;
+
+repeat:
+ next = this_parent->mnt_mounts.next;
+ DPRINTK(("is_vfsmnt_tree_busy: mnt=%p, this_parent=%p, next=%p\n",
+ mnt, this_parent, next));
+resume:
+ for( ; next != &this_parent->mnt_mounts; next = next->next) {
+ struct vfsmount *p = list_entry(next, struct vfsmount,
+ mnt_child);
+
+ /* -1 for struct vfs_mount's normal count,
+ -1 to compensate for child's reference to parent */
+ count += atomic_read(&p->mnt_count) - 1 - 1;
+
+ DPRINTK(("is_vfsmnt_tree_busy: p=%p, count now %d\n",
+ p, count));
+
+ if (!list_empty(&p->mnt_mounts)) {
+ this_parent = p;
+ goto repeat;
+ }
+ /* root is busy if any leaf is busy */
+ if (atomic_read(&p->mnt_count) > 1)
+ return 1;
+ }
+
+ /* All done at this level ... ascend and resume the search. */
+ if (this_parent != mnt) {
+ next = this_parent->mnt_child.next;
+ this_parent = this_parent->mnt_parent;
+ goto resume;
+ }
+
+ DPRINTK(("is_vfsmnt_tree_busy: count=%d\n", count));
+ return count != 0; /* remaining users? */
+}
+
+/* Traverse a dentry's list of vfsmounts and return the number of
+ non-busy mounts */
+static int check_vfsmnt(struct vfsmount *mnt, struct dentry *dentry)
+{
+ int ret = dentry->d_mounted;
+ struct vfsmount *vfs = lookup_mnt(mnt, dentry);
+
+ if (vfs && is_vfsmnt_tree_busy(vfs))
+ ret--;
+ DPRINTK(("check_vfsmnt: ret=%d\n", ret));
+ return ret;
+}
+
+/* Check dentry tree for busyness. If a dentry appears to be busy
+ because it is a mountpoint, check to see if the mounted
+ filesystem is busy. */
+static int is_tree_busy(struct vfsmount *topmnt, struct dentry *top)
+{
+ struct dentry *this_parent;
+ struct list_head *next;
+ int count;
+
+ count = atomic_read(&top->d_count);
+
+ DPRINTK(("is_tree_busy: top=%p initial count=%d\n",
+ top, count));
+ this_parent = top;
+
+ if (is_autofs4_dentry(top)) {
+ count--;
+ DPRINTK(("is_tree_busy: autofs; count=%d\n", count));
+ }
+
+ if (d_mountpoint(top))
+ count -= check_vfsmnt(topmnt, top);
+
+ repeat:
+ next = this_parent->d_subdirs.next;
+ resume:
+ while (next != &this_parent->d_subdirs) {
+ int adj = 0;
+ struct dentry *dentry = list_entry(next, struct dentry,
+ d_child);
+ next = next->next;
+
+ count += atomic_read(&dentry->d_count) - 1;
+
+ if (d_mountpoint(dentry))
+ adj += check_vfsmnt(topmnt, dentry);
+
+ if (is_autofs4_dentry(dentry)) {
+ adj++;
+ DPRINTK(("is_tree_busy: autofs; adj=%d\n",
+ adj));
+ }
+
+ count -= adj;
+
+ if (!list_empty(&dentry->d_subdirs)) {
+ this_parent = dentry;
+ goto repeat;
+ }
+
+ if (atomic_read(&dentry->d_count) != adj) {
+ DPRINTK(("is_tree_busy: busy leaf (d_count=%d adj=%d)\n",
+ atomic_read(&dentry->d_count), adj));
+ return 1;
+ }
+ }
+
+ /* All done at this level ... ascend and resume the search. */
+ if (this_parent != top) {
+ next = this_parent->d_child.next;
+ this_parent = this_parent->d_parent;
+ goto resume;
+ }
+
+ DPRINTK(("is_tree_busy: count=%d\n", count));
+ return count != 0; /* remaining users? */
+}
+
+/*
+ * Find an eligible tree to time-out
+ * A tree is eligible if :-
+ * - it is unused by any user process
+ * - it has been unused for exp_timeout time
+ */
+static struct dentry *autofs4_expire(struct super_block *sb,
+ struct vfsmount *mnt,
+ struct autofs_sb_info *sbi,
+ int do_now)
+{
+ unsigned long now = jiffies;
+ unsigned long timeout;
+ struct dentry *root = sb->s_root;
+ struct list_head *tmp;
+
+ if (!sbi->exp_timeout || !root)
+ return NULL;
+
+ timeout = sbi->exp_timeout;
+
+ spin_lock(&dcache_lock);
+ for(tmp = root->d_subdirs.next;
+ tmp != &root->d_subdirs;
+ tmp = tmp->next) {
+ struct autofs_info *ino;
+ struct dentry *dentry = list_entry(tmp, struct dentry, d_child);
+
+ if (dentry->d_inode == NULL)
+ continue;
+
+ ino = autofs4_dentry_ino(dentry);
+
+ if (ino == NULL) {
+ /* dentry in the process of being deleted */
+ continue;
+ }
+
+ /* No point expiring a pending mount */
+ if (dentry->d_flags & DCACHE_AUTOFS_PENDING)
+ continue;
+
+ if (!do_now) {
+ /* Too young to die */
+ if (time_after(ino->last_used + timeout, now))
+ continue;
+
+ /* update last_used here :-
+ - obviously makes sense if it is in use now
+ - less obviously, prevents rapid-fire expire
+ attempts if expire fails the first time */
+ ino->last_used = now;
+ }
+ if (!is_tree_busy(mnt, dentry)) {
+ DPRINTK(("autofs_expire: returning %p %.*s\n",
+ dentry, (int)dentry->d_name.len, dentry->d_name.name));
+ /* Start from here next time */
+ list_del(&root->d_subdirs);
+ list_add(&root->d_subdirs, &dentry->d_child);
+ dget(dentry);
+ spin_unlock(&dcache_lock);
+
+ return dentry;
+ }
+ }
+ spin_unlock(&dcache_lock);
+
+ return NULL;
+}
+
+/* Perform an expiry operation */
+int autofs4_expire_run(struct super_block *sb,
+ struct vfsmount *mnt,
+ struct autofs_sb_info *sbi,
+ struct autofs_packet_expire *pkt_p)
+{
+ struct autofs_packet_expire pkt;
+ struct dentry *dentry;
+
+ memset(&pkt,0,sizeof pkt);
+
+ pkt.hdr.proto_version = sbi->version;
+ pkt.hdr.type = autofs_ptype_expire;
+
+ if ((dentry = autofs4_expire(sb, mnt, sbi, 0)) == NULL)
+ return -EAGAIN;
+
+ pkt.len = dentry->d_name.len;
+ memcpy(pkt.name, dentry->d_name.name, pkt.len);
+ pkt.name[pkt.len] = '\0';
+ dput(dentry);
+
+ if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) )
+ return -EFAULT;
+
+ return 0;
+}
+
+/* Call repeatedly until it returns -EAGAIN, meaning there's nothing
+ more to be done */
+int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt,
+ struct autofs_sb_info *sbi, int *arg)
+{
+ struct dentry *dentry;
+ int ret = -EAGAIN;
+ int do_now = 0;
+
+ if (arg && get_user(do_now, arg))
+ return -EFAULT;
+
+ if ((dentry = autofs4_expire(sb, mnt, sbi, do_now)) != NULL) {
+ struct autofs_info *de_info = autofs4_dentry_ino(dentry);
+
+ /* This is synchronous because it makes the daemon a
+ little easier */
+ de_info->flags |= AUTOFS_INF_EXPIRING;
+ ret = autofs4_wait(sbi, &dentry->d_name, NFY_EXPIRE);
+ de_info->flags &= ~AUTOFS_INF_EXPIRING;
+ dput(dentry);
+ }
+
+ return ret;
+}
+
diff --git a/fs/autofs4/init.c b/fs/autofs4/init.c
new file mode 100644
index 00000000000000..dbb3e5385ded0a
--- /dev/null
+++ b/fs/autofs4/init.c
@@ -0,0 +1,31 @@
+/* -*- c -*- --------------------------------------------------------------- *
+ *
+ * linux/fs/autofs/init.c
+ *
+ * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ------------------------------------------------------------------------- */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include "autofs_i.h"
+
+static DECLARE_FSTYPE(autofs_fs_type, "autofs", autofs4_read_super, 0);
+
+static int __init init_autofs4_fs(void)
+{
+ return register_filesystem(&autofs_fs_type);
+}
+
+static void __exit exit_autofs4_fs(void)
+{
+ unregister_filesystem(&autofs_fs_type);
+}
+
+module_init(init_autofs4_fs)
+module_exit(exit_autofs4_fs)
+MODULE_LICENSE("GPL");
diff --git a/fs/autofs4/inode.c b/fs/autofs4/inode.c
new file mode 100644
index 00000000000000..6a186f654be622
--- /dev/null
+++ b/fs/autofs4/inode.c
@@ -0,0 +1,324 @@
+/* -*- c -*- --------------------------------------------------------------- *
+ *
+ * linux/fs/autofs/inode.c
+ *
+ * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ------------------------------------------------------------------------- */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/file.h>
+#include <linux/locks.h>
+#include <asm/bitops.h>
+#include "autofs_i.h"
+#define __NO_VERSION__
+#include <linux/module.h>
+
+static void ino_lnkfree(struct autofs_info *ino)
+{
+ if (ino->u.symlink) {
+ kfree(ino->u.symlink);
+ ino->u.symlink = NULL;
+ }
+}
+
+struct autofs_info *autofs4_init_ino(struct autofs_info *ino,
+ struct autofs_sb_info *sbi, mode_t mode)
+{
+ int reinit = 1;
+
+ if (ino == NULL) {
+ reinit = 0;
+ ino = kmalloc(sizeof(*ino), GFP_KERNEL);
+ }
+
+ if (ino == NULL)
+ return NULL;
+
+ ino->flags = 0;
+ ino->mode = mode;
+ ino->inode = NULL;
+ ino->dentry = NULL;
+ ino->size = 0;
+
+ ino->last_used = jiffies;
+
+ ino->sbi = sbi;
+
+ if (reinit && ino->free)
+ (ino->free)(ino);
+
+ memset(&ino->u, 0, sizeof(ino->u));
+
+ ino->free = NULL;
+
+ if (S_ISLNK(mode))
+ ino->free = ino_lnkfree;
+
+ return ino;
+}
+
+void autofs4_free_ino(struct autofs_info *ino)
+{
+ if (ino->dentry) {
+ ino->dentry->d_fsdata = NULL;
+ if (ino->dentry->d_inode)
+ dput(ino->dentry);
+ ino->dentry = NULL;
+ }
+ if (ino->free)
+ (ino->free)(ino);
+ kfree(ino);
+}
+
+static void autofs4_put_super(struct super_block *sb)
+{
+ struct autofs_sb_info *sbi = autofs4_sbi(sb);
+
+ sb->u.generic_sbp = NULL;
+
+ if ( !sbi->catatonic )
+ autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */
+
+ kfree(sbi);
+
+ DPRINTK(("autofs: shutting down\n"));
+}
+
+static int autofs4_statfs(struct super_block *sb, struct statfs *buf);
+
+static struct super_operations autofs4_sops = {
+ put_super: autofs4_put_super,
+ statfs: autofs4_statfs,
+};
+
+static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid,
+ pid_t *pgrp, int *minproto, int *maxproto)
+{
+ char *this_char, *value;
+
+ *uid = current->uid;
+ *gid = current->gid;
+ *pgrp = current->pgrp;
+
+ *minproto = AUTOFS_MIN_PROTO_VERSION;
+ *maxproto = AUTOFS_MAX_PROTO_VERSION;
+
+ *pipefd = -1;
+
+ if ( !options ) return 1;
+ for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) {
+ if ((value = strchr(this_char,'=')) != NULL)
+ *value++ = 0;
+ if (!strcmp(this_char,"fd")) {
+ if (!value || !*value)
+ return 1;
+ *pipefd = simple_strtoul(value,&value,0);
+ if (*value)
+ return 1;
+ }
+ else if (!strcmp(this_char,"uid")) {
+ if (!value || !*value)
+ return 1;
+ *uid = simple_strtoul(value,&value,0);
+ if (*value)
+ return 1;
+ }
+ else if (!strcmp(this_char,"gid")) {
+ if (!value || !*value)
+ return 1;
+ *gid = simple_strtoul(value,&value,0);
+ if (*value)
+ return 1;
+ }
+ else if (!strcmp(this_char,"pgrp")) {
+ if (!value || !*value)
+ return 1;
+ *pgrp = simple_strtoul(value,&value,0);
+ if (*value)
+ return 1;
+ }
+ else if (!strcmp(this_char,"minproto")) {
+ if (!value || !*value)
+ return 1;
+ *minproto = simple_strtoul(value,&value,0);
+ if (*value)
+ return 1;
+ }
+ else if (!strcmp(this_char,"maxproto")) {
+ if (!value || !*value)
+ return 1;
+ *maxproto = simple_strtoul(value,&value,0);
+ if (*value)
+ return 1;
+ }
+ else break;
+ }
+ return (*pipefd < 0);
+}
+
+static struct autofs_info *autofs4_mkroot(struct autofs_sb_info *sbi)
+{
+ struct autofs_info *ino;
+
+ ino = autofs4_init_ino(NULL, sbi, S_IFDIR | 0755);
+ if (!ino)
+ return NULL;
+
+ return ino;
+}
+
+struct super_block *autofs4_read_super(struct super_block *s, void *data,
+ int silent)
+{
+ struct inode * root_inode;
+ struct dentry * root;
+ struct file * pipe;
+ int pipefd;
+ struct autofs_sb_info *sbi;
+ int minproto, maxproto;
+
+ sbi = (struct autofs_sb_info *) kmalloc(sizeof(*sbi), GFP_KERNEL);
+ if ( !sbi )
+ goto fail_unlock;
+ DPRINTK(("autofs: starting up, sbi = %p\n",sbi));
+
+ memset(sbi, 0, sizeof(*sbi));
+
+ s->u.generic_sbp = sbi;
+ sbi->magic = AUTOFS_SBI_MAGIC;
+ sbi->catatonic = 0;
+ sbi->exp_timeout = 0;
+ sbi->oz_pgrp = current->pgrp;
+ sbi->sb = s;
+ sbi->version = 0;
+ sbi->queues = NULL;
+ s->s_blocksize = 1024;
+ s->s_blocksize_bits = 10;
+ s->s_magic = AUTOFS_SUPER_MAGIC;
+ s->s_op = &autofs4_sops;
+
+ /*
+ * Get the root inode and dentry, but defer checking for errors.
+ */
+ root_inode = autofs4_get_inode(s, autofs4_mkroot(sbi));
+ root_inode->i_op = &autofs4_root_inode_operations;
+ root_inode->i_fop = &autofs4_root_operations;
+ root = d_alloc_root(root_inode);
+ pipe = NULL;
+
+ if (!root)
+ goto fail_iput;
+
+ /* Can this call block? */
+ if (parse_options(data, &pipefd,
+ &root_inode->i_uid, &root_inode->i_gid,
+ &sbi->oz_pgrp,
+ &minproto, &maxproto)) {
+ printk("autofs: called with bogus options\n");
+ goto fail_dput;
+ }
+
+ /* Couldn't this be tested earlier? */
+ if (maxproto < AUTOFS_MIN_PROTO_VERSION ||
+ minproto > AUTOFS_MAX_PROTO_VERSION) {
+ printk("autofs: kernel does not match daemon version "
+ "daemon (%d, %d) kernel (%d, %d)\n",
+ minproto, maxproto,
+ AUTOFS_MIN_PROTO_VERSION, AUTOFS_MAX_PROTO_VERSION);
+ goto fail_dput;
+ }
+
+ sbi->version = maxproto > AUTOFS_MAX_PROTO_VERSION ? AUTOFS_MAX_PROTO_VERSION : maxproto;
+
+ DPRINTK(("autofs: pipe fd = %d, pgrp = %u\n", pipefd, sbi->oz_pgrp));
+ pipe = fget(pipefd);
+
+ if ( !pipe ) {
+ printk("autofs: could not open pipe file descriptor\n");
+ goto fail_dput;
+ }
+ if ( !pipe->f_op || !pipe->f_op->write )
+ goto fail_fput;
+ sbi->pipe = pipe;
+
+ /*
+ * Success! Install the root dentry now to indicate completion.
+ */
+ s->s_root = root;
+ return s;
+
+ /*
+ * Failure ... clean up.
+ */
+fail_fput:
+ printk("autofs: pipe file descriptor does not contain proper ops\n");
+ /*
+ * fput() can block, so we clear the super block first.
+ */
+ fput(pipe);
+ /* fall through */
+fail_dput:
+ /*
+ * dput() can block, so we clear the super block first.
+ */
+ dput(root);
+ goto fail_free;
+fail_iput:
+ printk("autofs: get root dentry failed\n");
+ /*
+ * iput() can block, so we clear the super block first.
+ */
+ iput(root_inode);
+fail_free:
+ kfree(sbi);
+fail_unlock:
+ return NULL;
+}
+
+static int autofs4_statfs(struct super_block *sb, struct statfs *buf)
+{
+ buf->f_type = AUTOFS_SUPER_MAGIC;
+ buf->f_bsize = 1024;
+ buf->f_namelen = NAME_MAX;
+ return 0;
+}
+
+struct inode *autofs4_get_inode(struct super_block *sb,
+ struct autofs_info *inf)
+{
+ struct inode *inode = new_inode(sb);
+
+ if (inode == NULL)
+ return NULL;
+
+ inf->inode = inode;
+ inode->i_mode = inf->mode;
+ if (sb->s_root) {
+ inode->i_uid = sb->s_root->d_inode->i_uid;
+ inode->i_gid = sb->s_root->d_inode->i_gid;
+ } else {
+ inode->i_uid = 0;
+ inode->i_gid = 0;
+ }
+ inode->i_blksize = PAGE_CACHE_SIZE;
+ inode->i_blocks = 0;
+ inode->i_rdev = 0;
+ inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+
+ if (S_ISDIR(inf->mode)) {
+ inode->i_nlink = 2;
+ inode->i_op = &autofs4_dir_inode_operations;
+ inode->i_fop = &dcache_dir_ops;
+ } else if (S_ISLNK(inf->mode)) {
+ inode->i_size = inf->size;
+ inode->i_op = &autofs4_symlink_inode_operations;
+ }
+
+ return inode;
+}
diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c
new file mode 100644
index 00000000000000..0658ba68c0886b
--- /dev/null
+++ b/fs/autofs4/root.c
@@ -0,0 +1,532 @@
+/* -*- c -*- --------------------------------------------------------------- *
+ *
+ * linux/fs/autofs/root.c
+ *
+ * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
+ * Copyright 1999-2000 Jeremy Fitzhardinge <jeremy@goop.org>
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ------------------------------------------------------------------------- */
+
+#include <linux/errno.h>
+#include <linux/stat.h>
+#include <linux/param.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include "autofs_i.h"
+
+static struct dentry *autofs4_dir_lookup(struct inode *,struct dentry *);
+static int autofs4_dir_symlink(struct inode *,struct dentry *,const char *);
+static int autofs4_dir_unlink(struct inode *,struct dentry *);
+static int autofs4_dir_rmdir(struct inode *,struct dentry *);
+static int autofs4_dir_mkdir(struct inode *,struct dentry *,int);
+static int autofs4_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long);
+static struct dentry *autofs4_root_lookup(struct inode *,struct dentry *);
+
+struct file_operations autofs4_root_operations = {
+ open: dcache_dir_open,
+ release: dcache_dir_close,
+ llseek: dcache_dir_lseek,
+ read: generic_read_dir,
+ readdir: dcache_readdir,
+ fsync: dcache_dir_fsync,
+ ioctl: autofs4_root_ioctl,
+};
+
+struct inode_operations autofs4_root_inode_operations = {
+ lookup: autofs4_root_lookup,
+ unlink: autofs4_dir_unlink,
+ symlink: autofs4_dir_symlink,
+ mkdir: autofs4_dir_mkdir,
+ rmdir: autofs4_dir_rmdir,
+};
+
+struct inode_operations autofs4_dir_inode_operations = {
+ lookup: autofs4_dir_lookup,
+ unlink: autofs4_dir_unlink,
+ symlink: autofs4_dir_symlink,
+ mkdir: autofs4_dir_mkdir,
+ rmdir: autofs4_dir_rmdir,
+};
+
+/* Update usage from here to top of tree, so that scan of
+ top-level directories will give a useful result */
+static void autofs4_update_usage(struct dentry *dentry)
+{
+ struct dentry *top = dentry->d_sb->s_root;
+
+ for(; dentry != top; dentry = dentry->d_parent) {
+ struct autofs_info *ino = autofs4_dentry_ino(dentry);
+
+ if (ino) {
+ update_atime(dentry->d_inode);
+ ino->last_used = jiffies;
+ }
+ }
+}
+
+static int try_to_fill_dentry(struct dentry *dentry,
+ struct super_block *sb,
+ struct autofs_sb_info *sbi)
+{
+ struct autofs_info *de_info = autofs4_dentry_ino(dentry);
+ int status = 0;
+
+ /* Block on any pending expiry here; invalidate the dentry
+ when expiration is done to trigger mount request with a new
+ dentry */
+ if (de_info && (de_info->flags & AUTOFS_INF_EXPIRING)) {
+ DPRINTK(("try_to_fill_entry: waiting for expire %p name=%.*s, flags&PENDING=%s de_info=%p de_info->flags=%x\n",
+ dentry, dentry->d_name.len, dentry->d_name.name,
+ dentry->d_flags & DCACHE_AUTOFS_PENDING?"t":"f",
+ de_info, de_info?de_info->flags:0));
+ status = autofs4_wait(sbi, &dentry->d_name, NFY_NONE);
+
+ DPRINTK(("try_to_fill_entry: expire done status=%d\n", status));
+
+ return 0;
+ }
+
+ DPRINTK(("try_to_fill_entry: dentry=%p %.*s ino=%p\n",
+ dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode));
+
+ /* Wait for a pending mount, triggering one if there isn't one already */
+ while(dentry->d_inode == NULL) {
+ DPRINTK(("try_to_fill_entry: waiting for mount name=%.*s, de_info=%p de_info->flags=%x\n",
+ dentry->d_name.len, dentry->d_name.name,
+ de_info, de_info?de_info->flags:0));
+ status = autofs4_wait(sbi, &dentry->d_name, NFY_MOUNT);
+
+ DPRINTK(("try_to_fill_entry: mount done status=%d\n", status));
+
+ if (status && dentry->d_inode)
+ return 0; /* Try to get the kernel to invalidate this dentry */
+
+ /* Turn this into a real negative dentry? */
+ if (status == -ENOENT) {
+ dentry->d_time = jiffies + AUTOFS_NEGATIVE_TIMEOUT;
+ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING;
+ return 1;
+ } else if (status) {
+ /* Return a negative dentry, but leave it "pending" */
+ return 1;
+ }
+ }
+
+ /* If this is an unused directory that isn't a mount point,
+ bitch at the daemon and fix it in user space */
+ spin_lock(&dcache_lock);
+ if (S_ISDIR(dentry->d_inode->i_mode) &&
+ !d_mountpoint(dentry) &&
+ list_empty(&dentry->d_subdirs)) {
+ DPRINTK(("try_to_fill_entry: mounting existing dir\n"));
+ spin_unlock(&dcache_lock);
+ return autofs4_wait(sbi, &dentry->d_name, NFY_MOUNT) == 0;
+ }
+ spin_unlock(&dcache_lock);
+
+ /* We don't update the usages for the autofs daemon itself, this
+ is necessary for recursive autofs mounts */
+ if (!autofs4_oz_mode(sbi))
+ autofs4_update_usage(dentry);
+
+ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING;
+ return 1;
+}
+
+
+/*
+ * Revalidate is called on every cache lookup. Some of those
+ * cache lookups may actually happen while the dentry is not
+ * yet completely filled in, and revalidate has to delay such
+ * lookups..
+ */
+static int autofs4_root_revalidate(struct dentry * dentry, int flags)
+{
+ struct inode * dir = dentry->d_parent->d_inode;
+ struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb);
+ int oz_mode = autofs4_oz_mode(sbi);
+
+ /* Pending dentry */
+ if (autofs4_ispending(dentry)) {
+ if (autofs4_oz_mode(sbi))
+ return 1;
+ else
+ return try_to_fill_dentry(dentry, dir->i_sb, sbi);
+ }
+
+ /* Negative dentry.. invalidate if "old" */
+ if (dentry->d_inode == NULL)
+ return (dentry->d_time - jiffies <= AUTOFS_NEGATIVE_TIMEOUT);
+
+ /* Check for a non-mountpoint directory with no contents */
+ spin_lock(&dcache_lock);
+ if (S_ISDIR(dentry->d_inode->i_mode) &&
+ !d_mountpoint(dentry) &&
+ list_empty(&dentry->d_subdirs)) {
+ DPRINTK(("autofs_root_revalidate: dentry=%p %.*s, emptydir\n",
+ dentry, dentry->d_name.len, dentry->d_name.name));
+ spin_unlock(&dcache_lock);
+ if (oz_mode)
+ return 1;
+ else
+ return try_to_fill_dentry(dentry, dir->i_sb, sbi);
+ }
+ spin_unlock(&dcache_lock);
+
+ /* Update the usage list */
+ if (!oz_mode)
+ autofs4_update_usage(dentry);
+
+ return 1;
+}
+
+static int autofs4_revalidate(struct dentry *dentry, int flags)
+{
+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
+
+ if (!autofs4_oz_mode(sbi))
+ autofs4_update_usage(dentry);
+
+ return 1;
+}
+
+static void autofs4_dentry_release(struct dentry *de)
+{
+ struct autofs_info *inf;
+
+ lock_kernel();
+
+ DPRINTK(("autofs4_dentry_release: releasing %p\n", de));
+
+ inf = autofs4_dentry_ino(de);
+ de->d_fsdata = NULL;
+
+ if (inf) {
+ inf->dentry = NULL;
+ inf->inode = NULL;
+
+ autofs4_free_ino(inf);
+ }
+
+ unlock_kernel();
+}
+
+/* For dentries of directories in the root dir */
+static struct dentry_operations autofs4_root_dentry_operations = {
+ d_revalidate: autofs4_root_revalidate,
+ d_release: autofs4_dentry_release,
+};
+
+/* For other dentries */
+static struct dentry_operations autofs4_dentry_operations = {
+ d_revalidate: autofs4_revalidate,
+ d_release: autofs4_dentry_release,
+};
+
+/* Lookups in non-root dirs never find anything - if it's there, it's
+ already in the dcache */
+static struct dentry *autofs4_dir_lookup(struct inode *dir, struct dentry *dentry)
+{
+#if 0
+ DPRINTK(("autofs_dir_lookup: ignoring lookup of %.*s/%.*s\n",
+ dentry->d_parent->d_name.len, dentry->d_parent->d_name.name,
+ dentry->d_name.len, dentry->d_name.name));
+#endif
+
+ dentry->d_fsdata = NULL;
+ d_add(dentry, NULL);
+ return NULL;
+}
+
+/* Lookups in the root directory */
+static struct dentry *autofs4_root_lookup(struct inode *dir, struct dentry *dentry)
+{
+ struct autofs_sb_info *sbi;
+ int oz_mode;
+
+ DPRINTK(("autofs_root_lookup: name = %.*s\n",
+ dentry->d_name.len, dentry->d_name.name));
+
+ if (dentry->d_name.len > NAME_MAX)
+ return ERR_PTR(-ENAMETOOLONG);/* File name too long to exist */
+
+ sbi = autofs4_sbi(dir->i_sb);
+
+ oz_mode = autofs4_oz_mode(sbi);
+ DPRINTK(("autofs_lookup: pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d\n",
+ current->pid, current->pgrp, sbi->catatonic, oz_mode));
+
+ /*
+ * Mark the dentry incomplete, but add it. This is needed so
+ * that the VFS layer knows about the dentry, and we can count
+ * on catching any lookups through the revalidate.
+ *
+ * Let all the hard work be done by the revalidate function that
+ * needs to be able to do this anyway..
+ *
+ * We need to do this before we release the directory semaphore.
+ */
+ dentry->d_op = &autofs4_root_dentry_operations;
+
+ if (!oz_mode)
+ dentry->d_flags |= DCACHE_AUTOFS_PENDING;
+ dentry->d_fsdata = NULL;
+ d_add(dentry, NULL);
+
+ if (dentry->d_op && dentry->d_op->d_revalidate) {
+ up(&dir->i_sem);
+ (dentry->d_op->d_revalidate)(dentry, 0);
+ down(&dir->i_sem);
+ }
+
+ /*
+ * If we are still pending, check if we had to handle
+ * a signal. If so we can force a restart..
+ */
+ if (dentry->d_flags & DCACHE_AUTOFS_PENDING) {
+ if (signal_pending(current))
+ return ERR_PTR(-ERESTARTNOINTR);
+ }
+
+ /*
+ * If this dentry is unhashed, then we shouldn't honour this
+ * lookup even if the dentry is positive. Returning ENOENT here
+ * doesn't do the right thing for all system calls, but it should
+ * be OK for the operations we permit from an autofs.
+ */
+ if ( dentry->d_inode && d_unhashed(dentry) )
+ return ERR_PTR(-ENOENT);
+
+ return NULL;
+}
+
+static int autofs4_dir_symlink(struct inode *dir,
+ struct dentry *dentry,
+ const char *symname)
+{
+ struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb);
+ struct autofs_info *ino = autofs4_dentry_ino(dentry);
+ struct inode *inode;
+ char *cp;
+
+ DPRINTK(("autofs_dir_symlink: %s <- %.*s\n", symname,
+ dentry->d_name.len, dentry->d_name.name));
+
+ if (!autofs4_oz_mode(sbi))
+ return -EACCES;
+
+ ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555);
+ if (ino == NULL)
+ return -ENOSPC;
+
+ ino->size = strlen(symname);
+ ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL);
+
+ if (cp == NULL) {
+ kfree(ino);
+ return -ENOSPC;
+ }
+
+ strcpy(cp, symname);
+
+ inode = autofs4_get_inode(dir->i_sb, ino);
+ d_instantiate(dentry, inode);
+
+ if (dir == dir->i_sb->s_root->d_inode)
+ dentry->d_op = &autofs4_root_dentry_operations;
+ else
+ dentry->d_op = &autofs4_dentry_operations;
+
+ dentry->d_fsdata = ino;
+ ino->dentry = dget(dentry);
+ ino->inode = inode;
+
+ dir->i_mtime = CURRENT_TIME;
+
+ return 0;
+}
+
+/*
+ * NOTE!
+ *
+ * Normal filesystems would do a "d_delete()" to tell the VFS dcache
+ * that the file no longer exists. However, doing that means that the
+ * VFS layer can turn the dentry into a negative dentry. We don't want
+ * this, because since the unlink is probably the result of an expire.
+ * We simply d_drop it, which allows the dentry lookup to remount it
+ * if necessary.
+ *
+ * If a process is blocked on the dentry waiting for the expire to finish,
+ * it will invalidate the dentry and try to mount with a new one.
+ *
+ * Also see autofs_dir_rmdir()..
+ */
+static int autofs4_dir_unlink(struct inode *dir, struct dentry *dentry)
+{
+ struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb);
+ struct autofs_info *ino = autofs4_dentry_ino(dentry);
+
+ /* This allows root to remove symlinks */
+ if ( !autofs4_oz_mode(sbi) && !capable(CAP_SYS_ADMIN) )
+ return -EACCES;
+
+ dput(ino->dentry);
+
+ dentry->d_inode->i_size = 0;
+ dentry->d_inode->i_nlink = 0;
+
+ dir->i_mtime = CURRENT_TIME;
+
+ d_drop(dentry);
+
+ return 0;
+}
+
+static int autofs4_dir_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb);
+ struct autofs_info *ino = autofs4_dentry_ino(dentry);
+
+ if (!autofs4_oz_mode(sbi))
+ return -EACCES;
+
+ spin_lock(&dcache_lock);
+ if (!list_empty(&dentry->d_subdirs)) {
+ spin_unlock(&dcache_lock);
+ return -ENOTEMPTY;
+ }
+ list_del_init(&dentry->d_hash);
+ spin_unlock(&dcache_lock);
+
+ dput(ino->dentry);
+
+ dentry->d_inode->i_size = 0;
+ dentry->d_inode->i_nlink = 0;
+
+ if (dir->i_nlink)
+ dir->i_nlink--;
+
+ return 0;
+}
+
+
+
+static int autofs4_dir_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+ struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb);
+ struct autofs_info *ino = autofs4_dentry_ino(dentry);
+ struct inode *inode;
+
+ if ( !autofs4_oz_mode(sbi) )
+ return -EACCES;
+
+ DPRINTK(("autofs_dir_mkdir: dentry %p, creating %.*s\n",
+ dentry, dentry->d_name.len, dentry->d_name.name));
+
+ ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555);
+ if (ino == NULL)
+ return -ENOSPC;
+
+ inode = autofs4_get_inode(dir->i_sb, ino);
+ d_instantiate(dentry, inode);
+
+ if (dir == dir->i_sb->s_root->d_inode)
+ dentry->d_op = &autofs4_root_dentry_operations;
+ else
+ dentry->d_op = &autofs4_dentry_operations;
+
+ dentry->d_fsdata = ino;
+ ino->dentry = dget(dentry);
+ ino->inode = inode;
+ dir->i_nlink++;
+ dir->i_mtime = CURRENT_TIME;
+
+ return 0;
+}
+
+/* Get/set timeout ioctl() operation */
+static inline int autofs4_get_set_timeout(struct autofs_sb_info *sbi,
+ unsigned long *p)
+{
+ int rv;
+ unsigned long ntimeout;
+
+ if ( (rv = get_user(ntimeout, p)) ||
+ (rv = put_user(sbi->exp_timeout/HZ, p)) )
+ return rv;
+
+ if ( ntimeout > ULONG_MAX/HZ )
+ sbi->exp_timeout = 0;
+ else
+ sbi->exp_timeout = ntimeout * HZ;
+
+ return 0;
+}
+
+/* Return protocol version */
+static inline int autofs4_get_protover(struct autofs_sb_info *sbi, int *p)
+{
+ return put_user(sbi->version, p);
+}
+
+/* Identify autofs_dentries - this is so we can tell if there's
+ an extra dentry refcount or not. We only hold a refcount on the
+ dentry if its non-negative (ie, d_inode != NULL)
+*/
+int is_autofs4_dentry(struct dentry *dentry)
+{
+ return dentry && dentry->d_inode &&
+ (dentry->d_op == &autofs4_root_dentry_operations ||
+ dentry->d_op == &autofs4_dentry_operations) &&
+ dentry->d_fsdata != NULL;
+}
+
+/*
+ * ioctl()'s on the root directory is the chief method for the daemon to
+ * generate kernel reactions
+ */
+static int autofs4_root_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct autofs_sb_info *sbi = autofs4_sbi(inode->i_sb);
+
+ DPRINTK(("autofs_ioctl: cmd = 0x%08x, arg = 0x%08lx, sbi = %p, pgrp = %u\n",
+ cmd,arg,sbi,current->pgrp));
+
+ if ( _IOC_TYPE(cmd) != _IOC_TYPE(AUTOFS_IOC_FIRST) ||
+ _IOC_NR(cmd) - _IOC_NR(AUTOFS_IOC_FIRST) >= AUTOFS_IOC_COUNT )
+ return -ENOTTY;
+
+ if ( !autofs4_oz_mode(sbi) && !capable(CAP_SYS_ADMIN) )
+ return -EPERM;
+
+ switch(cmd) {
+ case AUTOFS_IOC_READY: /* Wait queue: go ahead and retry */
+ return autofs4_wait_release(sbi,(autofs_wqt_t)arg,0);
+ case AUTOFS_IOC_FAIL: /* Wait queue: fail with ENOENT */
+ return autofs4_wait_release(sbi,(autofs_wqt_t)arg,-ENOENT);
+ case AUTOFS_IOC_CATATONIC: /* Enter catatonic mode (daemon shutdown) */
+ autofs4_catatonic_mode(sbi);
+ return 0;
+ case AUTOFS_IOC_PROTOVER: /* Get protocol version */
+ return autofs4_get_protover(sbi, (int *)arg);
+ case AUTOFS_IOC_SETTIMEOUT:
+ return autofs4_get_set_timeout(sbi,(unsigned long *)arg);
+
+ /* return a single thing to expire */
+ case AUTOFS_IOC_EXPIRE:
+ return autofs4_expire_run(inode->i_sb,filp->f_vfsmnt,sbi,
+ (struct autofs_packet_expire *)arg);
+ /* same as above, but can send multiple expires through pipe */
+ case AUTOFS_IOC_EXPIRE_MULTI:
+ return autofs4_expire_multi(inode->i_sb,filp->f_vfsmnt,sbi,
+ (int *)arg);
+
+ default:
+ return -ENOSYS;
+ }
+}
diff --git a/fs/autofs4/symlink.c b/fs/autofs4/symlink.c
new file mode 100644
index 00000000000000..40f3e80b7a66d4
--- /dev/null
+++ b/fs/autofs4/symlink.c
@@ -0,0 +1,32 @@
+/* -*- c -*- --------------------------------------------------------------- *
+ *
+ * linux/fs/autofs/symlink.c
+ *
+ * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ------------------------------------------------------------------------- */
+
+#include "autofs_i.h"
+
+static int autofs4_readlink(struct dentry *dentry, char *buffer, int buflen)
+{
+ struct autofs_info *ino = autofs4_dentry_ino(dentry);
+
+ return vfs_readlink(dentry, buffer, buflen, ino->u.symlink);
+}
+
+static int autofs4_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+ struct autofs_info *ino = autofs4_dentry_ino(dentry);
+
+ return vfs_follow_link(nd, ino->u.symlink);
+}
+
+struct inode_operations autofs4_symlink_inode_operations = {
+ readlink: autofs4_readlink,
+ follow_link: autofs4_follow_link
+};
diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c
new file mode 100644
index 00000000000000..9e5fe900fa4c31
--- /dev/null
+++ b/fs/autofs4/waitq.c
@@ -0,0 +1,250 @@
+/* -*- c -*- --------------------------------------------------------------- *
+ *
+ * linux/fs/autofs/waitq.c
+ *
+ * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ------------------------------------------------------------------------- */
+
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/file.h>
+#include "autofs_i.h"
+
+/* We make this a static variable rather than a part of the superblock; it
+ is better if we don't reassign numbers easily even across filesystems */
+static autofs_wqt_t autofs4_next_wait_queue = 1;
+
+/* These are the signals we allow interrupting a pending mount */
+#define SHUTDOWN_SIGS (sigmask(SIGKILL) | sigmask(SIGINT) | sigmask(SIGQUIT))
+
+void autofs4_catatonic_mode(struct autofs_sb_info *sbi)
+{
+ struct autofs_wait_queue *wq, *nwq;
+
+ DPRINTK(("autofs: entering catatonic mode\n"));
+
+ sbi->catatonic = 1;
+ wq = sbi->queues;
+ sbi->queues = NULL; /* Erase all wait queues */
+ while ( wq ) {
+ nwq = wq->next;
+ wq->status = -ENOENT; /* Magic is gone - report failure */
+ kfree(wq->name);
+ wq->name = NULL;
+ wake_up(&wq->queue);
+ wq = nwq;
+ }
+ if (sbi->pipe) {
+ fput(sbi->pipe); /* Close the pipe */
+ sbi->pipe = NULL;
+ }
+
+ shrink_dcache_sb(sbi->sb);
+}
+
+static int autofs4_write(struct file *file, const void *addr, int bytes)
+{
+ unsigned long sigpipe, flags;
+ mm_segment_t fs;
+ const char *data = (const char *)addr;
+ ssize_t wr = 0;
+
+ /** WARNING: this is not safe for writing more than PIPE_BUF bytes! **/
+
+ sigpipe = sigismember(&current->pending.signal, SIGPIPE);
+
+ /* Save pointer to user space and point back to kernel space */
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ while (bytes &&
+ (wr = file->f_op->write(file,data,bytes,&file->f_pos)) > 0) {
+ data += wr;
+ bytes -= wr;
+ }
+
+ set_fs(fs);
+
+ /* Keep the currently executing process from receiving a
+ SIGPIPE unless it was already supposed to get one */
+ if (wr == -EPIPE && !sigpipe) {
+ spin_lock_irqsave(&current->sigmask_lock, flags);
+ sigdelset(&current->pending.signal, SIGPIPE);
+ recalc_sigpending(current);
+ spin_unlock_irqrestore(&current->sigmask_lock, flags);
+ }
+
+ return (bytes > 0);
+}
+
+static void autofs4_notify_daemon(struct autofs_sb_info *sbi,
+ struct autofs_wait_queue *wq,
+ int type)
+{
+ union autofs_packet_union pkt;
+ size_t pktsz;
+
+ DPRINTK(("autofs_notify: wait id = 0x%08lx, name = %.*s, type=%d\n",
+ wq->wait_queue_token, wq->len, wq->name, type));
+
+ memset(&pkt,0,sizeof pkt); /* For security reasons */
+
+ pkt.hdr.proto_version = sbi->version;
+ pkt.hdr.type = type;
+ if (type == autofs_ptype_missing) {
+ struct autofs_packet_missing *mp = &pkt.missing;
+
+ pktsz = sizeof(*mp);
+
+ mp->wait_queue_token = wq->wait_queue_token;
+ mp->len = wq->len;
+ memcpy(mp->name, wq->name, wq->len);
+ mp->name[wq->len] = '\0';
+ } else if (type == autofs_ptype_expire_multi) {
+ struct autofs_packet_expire_multi *ep = &pkt.expire_multi;
+
+ pktsz = sizeof(*ep);
+
+ ep->wait_queue_token = wq->wait_queue_token;
+ ep->len = wq->len;
+ memcpy(ep->name, wq->name, wq->len);
+ ep->name[wq->len] = '\0';
+ } else {
+ printk("autofs_notify_daemon: bad type %d!\n", type);
+ return;
+ }
+
+ if (autofs4_write(sbi->pipe, &pkt, pktsz))
+ autofs4_catatonic_mode(sbi);
+}
+
+int autofs4_wait(struct autofs_sb_info *sbi, struct qstr *name,
+ enum autofs_notify notify)
+{
+ struct autofs_wait_queue *wq;
+ int status;
+
+ /* In catatonic mode, we don't wait for nobody */
+ if ( sbi->catatonic )
+ return -ENOENT;
+
+ /* We shouldn't be able to get here, but just in case */
+ if ( name->len > NAME_MAX )
+ return -ENOENT;
+
+ for ( wq = sbi->queues ; wq ; wq = wq->next ) {
+ if ( wq->hash == name->hash &&
+ wq->len == name->len &&
+ wq->name && !memcmp(wq->name,name->name,name->len) )
+ break;
+ }
+
+ if ( !wq ) {
+ /* Create a new wait queue */
+ wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL);
+ if ( !wq )
+ return -ENOMEM;
+
+ wq->name = kmalloc(name->len,GFP_KERNEL);
+ if ( !wq->name ) {
+ kfree(wq);
+ return -ENOMEM;
+ }
+ wq->wait_queue_token = autofs4_next_wait_queue;
+ if (++autofs4_next_wait_queue == 0)
+ autofs4_next_wait_queue = 1;
+ init_waitqueue_head(&wq->queue);
+ wq->hash = name->hash;
+ wq->len = name->len;
+ wq->status = -EINTR; /* Status return if interrupted */
+ memcpy(wq->name, name->name, name->len);
+ wq->next = sbi->queues;
+ sbi->queues = wq;
+
+ DPRINTK(("autofs_wait: new wait id = 0x%08lx, name = %.*s, nfy=%d\n",
+ wq->wait_queue_token, wq->len, wq->name, notify));
+ /* autofs4_notify_daemon() may block */
+ wq->wait_ctr = 2;
+ if (notify != NFY_NONE) {
+ autofs4_notify_daemon(sbi,wq,
+ notify == NFY_MOUNT ? autofs_ptype_missing :
+ autofs_ptype_expire_multi);
+ }
+ } else {
+ wq->wait_ctr++;
+ DPRINTK(("autofs_wait: existing wait id = 0x%08lx, name = %.*s, nfy=%d\n",
+ wq->wait_queue_token, wq->len, wq->name, notify));
+ }
+
+ /* wq->name is NULL if and only if the lock is already released */
+
+ if ( sbi->catatonic ) {
+ /* We might have slept, so check again for catatonic mode */
+ wq->status = -ENOENT;
+ if ( wq->name ) {
+ kfree(wq->name);
+ wq->name = NULL;
+ }
+ }
+
+ if ( wq->name ) {
+ /* Block all but "shutdown" signals while waiting */
+ sigset_t oldset;
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&current->sigmask_lock, irqflags);
+ oldset = current->blocked;
+ siginitsetinv(&current->blocked, SHUTDOWN_SIGS & ~oldset.sig[0]);
+ recalc_sigpending(current);
+ spin_unlock_irqrestore(&current->sigmask_lock, irqflags);
+
+ interruptible_sleep_on(&wq->queue);
+
+ spin_lock_irqsave(&current->sigmask_lock, irqflags);
+ current->blocked = oldset;
+ recalc_sigpending(current);
+ spin_unlock_irqrestore(&current->sigmask_lock, irqflags);
+ } else {
+ DPRINTK(("autofs_wait: skipped sleeping\n"));
+ }
+
+ status = wq->status;
+
+ if (--wq->wait_ctr == 0) /* Are we the last process to need status? */
+ kfree(wq);
+
+ return status;
+}
+
+
+int autofs4_wait_release(struct autofs_sb_info *sbi, autofs_wqt_t wait_queue_token, int status)
+{
+ struct autofs_wait_queue *wq, **wql;
+
+ for ( wql = &sbi->queues ; (wq = *wql) ; wql = &wq->next ) {
+ if ( wq->wait_queue_token == wait_queue_token )
+ break;
+ }
+ if ( !wq )
+ return -EINVAL;
+
+ *wql = wq->next; /* Unlink from chain */
+ kfree(wq->name);
+ wq->name = NULL; /* Do not wait on this queue */
+
+ wq->status = status;
+
+ if (--wq->wait_ctr == 0) /* Is anyone still waiting for this guy? */
+ kfree(wq);
+ else
+ wake_up(&wq->queue);
+
+ return 0;
+}
+
diff --git a/fs/bad_inode.c b/fs/bad_inode.c
new file mode 100644
index 00000000000000..850ba5e9f551d4
--- /dev/null
+++ b/fs/bad_inode.c
@@ -0,0 +1,108 @@
+/*
+ * linux/fs/bad_inode.c
+ *
+ * Copyright (C) 1997, Stephen Tweedie
+ *
+ * Provide stub functions for unreadable inodes
+ */
+
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/sched.h>
+
+/*
+ * The follow_link operation is special: it must behave as a no-op
+ * so that a bad root inode can at least be unmounted. To do this
+ * we must dput() the base and return the dentry with a dget().
+ */
+static int bad_follow_link(struct dentry *dent, struct nameidata *nd)
+{
+ return vfs_follow_link(nd, ERR_PTR(-EIO));
+}
+
+static int return_EIO(void)
+{
+ return -EIO;
+}
+
+#define EIO_ERROR ((void *) (return_EIO))
+
+static struct file_operations bad_file_ops =
+{
+ llseek: EIO_ERROR,
+ read: EIO_ERROR,
+ write: EIO_ERROR,
+ readdir: EIO_ERROR,
+ poll: EIO_ERROR,
+ ioctl: EIO_ERROR,
+ mmap: EIO_ERROR,
+ open: EIO_ERROR,
+ flush: EIO_ERROR,
+ release: EIO_ERROR,
+ fsync: EIO_ERROR,
+ fasync: EIO_ERROR,
+ lock: EIO_ERROR,
+};
+
+struct inode_operations bad_inode_ops =
+{
+ create: EIO_ERROR,
+ lookup: EIO_ERROR,
+ link: EIO_ERROR,
+ unlink: EIO_ERROR,
+ symlink: EIO_ERROR,
+ mkdir: EIO_ERROR,
+ rmdir: EIO_ERROR,
+ mknod: EIO_ERROR,
+ rename: EIO_ERROR,
+ readlink: EIO_ERROR,
+ follow_link: bad_follow_link,
+ truncate: EIO_ERROR,
+ permission: EIO_ERROR,
+ revalidate: EIO_ERROR,
+};
+
+
+/*
+ * When a filesystem is unable to read an inode due to an I/O error in
+ * its read_inode() function, it can call make_bad_inode() to return a
+ * set of stubs which will return EIO errors as required.
+ *
+ * We only need to do limited initialisation: all other fields are
+ * preinitialised to zero automatically.
+ */
+
+/**
+ * make_bad_inode - mark an inode bad due to an I/O error
+ * @inode: Inode to mark bad
+ *
+ * When an inode cannot be read due to a media or remote network
+ * failure this function makes the inode "bad" and causes I/O operations
+ * on it to fail from this point on.
+ */
+
+void make_bad_inode(struct inode * inode)
+{
+ inode->i_mode = S_IFREG;
+ inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ inode->i_op = &bad_inode_ops;
+ inode->i_fop = &bad_file_ops;
+}
+
+/*
+ * This tests whether an inode has been flagged as bad. The test uses
+ * &bad_inode_ops to cover the case of invalidated inodes as well as
+ * those created by make_bad_inode() above.
+ */
+
+/**
+ * is_bad_inode - is an inode errored
+ * @inode: inode to test
+ *
+ * Returns true if the inode in question has been marked as bad.
+ */
+
+int is_bad_inode(struct inode * inode)
+{
+ return (inode->i_op == &bad_inode_ops);
+}
diff --git a/fs/befs/ChangeLog b/fs/befs/ChangeLog
new file mode 100644
index 00000000000000..8e09a0bd8ebb55
--- /dev/null
+++ b/fs/befs/ChangeLog
@@ -0,0 +1,417 @@
+Version 0.92 (2002-03-29)
+==========
+* Minor cleanup. Ran Lindent on the sources.
+
+Version 0.92 (2002-03-27)
+==========
+* Fixed module makefile problem. It was not compiling all the correct
+ source files!
+* Removed duplicated function definition
+* Fixed potential null pointer dereference when reporting an error
+
+Version 0.91 (2002-03-26)
+==========
+* Oy! Fixed stupid bug that would cause an unresolved symbol error.
+ Thanks to Laszlo Boszormenyi for pointing this out to me.
+
+Version 0.9 (2002-03-14)
+==========
+* Added Sergey S. Kostyliov's patch to eliminate memcpy() overhead
+ from b+tree operations. Changes the befs_read_datastream() interface.
+
+* Segregated the functions that interface directly with the linux vfs
+ interface into their own file called linuxvfs.c. [WD]
+
+Version 0.64 (2002-02-07)
+==========
+* Did the string comparision really right this time (btree.c) [WD]
+
+* Fixed up some places where I assumed that a long int could hold
+ a pointer value. (btree.c) [WD]
+
+* Andrew Farnham <andrewfarnham@uq.net.au> pointed out that the module
+ wouldn't work on older (<2.4.10) kernels due to an unresolved symbol.
+ This is bad, since 2.4.9 is still the current RedHat kernel. I added
+ a workaround for this problem (compatability.h) [WD]
+
+* Sergey S. Kostyliov made befs_find_key() use a binary search to find
+ keys within btree nodes, rather than the linear search we were using
+ before. (btree.c) [Sergey S. Kostyliov <rathamahata@php4.ru>]
+
+* Made a debian package of the source for use with kernel-package. [WD]
+
+
+Version 0.63 (2002-01-31)
+==========
+* Fixed bug in befs_find_brun_indirect() that would result in the wrong
+ block being read. It was introduced when adding byteswapping in
+ 0.61. (datastream.c) [WD]
+
+* Fixed a longstanding bug in befs_find_key() that would result in it
+ finding the first key that is a substring of the string it is searching
+ for. For example, this would cause files in the same directory with
+ names like file1 and file2 to mysteriously be duplicates of each other
+ (because they have the same inode number). Many thanks to Pavel Roskin
+ for reporting this serious bug!!!
+ (btree.c) [WD]
+
+* Added support for long symlinks, after Axel Dorfler explained up how
+ they work. I had forgotten all about them. (inode.c, symlink.c) [WD]
+
+* Documentation improvements in source. [WD]
+
+* Makefile fix for independant module when CONFIG_MODVERSION is set in
+ kernel config [Pavel Roskin <proski@gnu.org>]
+
+* Compile warning fix for namei.c. [Sergey S. Kostyliov <rathamahata@php4.ru>]
+
+
+Version 0.62
+==========
+* Fixed makefile for module install [WD]
+
+
+Version 0.61 (2002-01-20)
+==========
+* Made functions in endian.h to do the correct byteswapping, no matter
+ the arch. [WD]
+
+* Abbandoned silly checks for a NULL superblock pointer in debug.c. [WD]
+
+* Misc code cleanups. Also cleanup of this changelog file. [WD]
+
+* Added byteswapping to all metadata reads from disk.
+ Uses the functions from endian.h [WD]
+
+* Remove the typedef of struct super_block to vfs_sb, as it offended
+ certain peoples' aesthetic sense. [WD]
+
+* Ditto with the befs_read_block() interface. [WD]
+
+
+Version 0.6 (2001-12-15)
+==========
+* Cleanup of NLS functions (util.c) [WD]
+
+* Make directory lookup/read use the NLS if an iocharset is provided. [WD]
+
+* Fixed stupid bug where specifying the uid or gid mount options as '0'
+ would result in the filesystem using the on-disk uid and gid. [WD]
+
+* Added mount option to control debug printing.
+ The option is, simply enough, 'debug'.
+ (super.c, debug.c) [WD]
+
+* Removed notion of btree handle from btree.c. It was unessisary, as the
+ linux VFS doesn't allow us to keep any state between calls. Updated
+ dir.c, namei.c befs_fs.h to account for it. [WD]
+
+* Improved handleing of overflow nodes when listing directories.
+ Now works for overflow nodes hanging off of nodes other than the root
+ node. This is the cleaner solution to Brent Miszalaski's problem. [WD]
+
+* Added new debug/warning/error print functions in debug.c.
+ More flexible. Will soon be controllable at mount time
+ (see TODO). [WD]
+
+* Rewrote datastream positon lookups.
+ (datastream.c) [WD]
+
+* Moved the TODO list to it's own file.
+
+
+Version 0.50 (2001-11-13)
+==========
+* Added workaround for mis-understanding of the nature of the b+trees used
+ in directories. A cleaner solution will come after I've thought about it
+ for a while. Thanks to Brent Miszalaski for finding and reporting this bug.
+ (btree.c) [WD]
+
+* Minor cleanups
+
+* Added test for "impossible" condition of empty internal nodes in
+ seekleaf() in btree.c [WD]
+
+* Implemented the abstracted read_block() in io.c [WD]
+
+* Cleaned up the inode validation in inode.c [WD]
+
+* Anton Altaparmakov figured out (by asking Linus :) ) what was causing the
+ hanging disk io problem. It turns out you need to have the sync_pages
+ callback defined in your address_space_ops, even if it just uses the
+ default linux-supplied implementation. Fixed. Works now.
+ (file.c) [WD]
+
+* Anton Altaparmakov and Christoph Hellwig alerted me to the fact that
+ filesystem code should be using GFP_NOFS instead of GFP_KERNEL as the
+ priority parameter to kmalloc(). Fixed.
+ (datastream.c, btree.c super.c inode.c) [WD]
+
+* Anton also told me that the blocksize is not allowed to be larger than
+ the page size in linux, which is 4k i386. Oops. Added a test for
+ (blocksize > PAGE_SIZE), and refuse to mount in that case. What this
+ practicaly means is that 8k blocksize volumes won't work without a major
+ restructuring of the driver (or an alpha or other 64bit hardware). [WD]
+
+* Cleaned up the befs_count_blocks() function. Much smarter now.
+ And somewhat smaller too. [WD]
+
+* Made inode allocations use a slab cache
+ (super.c inode.c) [WD]
+
+* Moved the freeing of the private inode section from put_inode() to
+ clear_inode(). This fixes a potential free twice type bug. Put_inode()
+ can be called multiple times for each inode struct. [WD]
+
+* Converted all non vfs-callback functions to use befs_sb_info as the
+ superblock type, rather than struct super_block. This is for
+ portablity. [WD]
+
+* Fixed a couple of compile warnings due to use of malloc.h, when slab.h
+ is the new way. (inode.c, super.c) [WD]
+
+* Fixed erronous includes of linux/befs_fs_i.h and linux/befs_fs_sb.h
+ in inode.c [WD]
+
+Version 0.45 (2001-10-29)
+==========
+* Added functions to get the private superblock and inode structures from
+ their enclosing public structures. Switched all references to the
+ private portions to use them. (many files) [WD]
+
+* Made read_super and read_inode allocate the private portions of those
+ structures into the generic pointer fields of the public structures
+ with kmalloc(). put_super and put_inode free them. This allows us not
+ to have to touch the definitions of the public structures in
+ include/linux/fs.h. Also, befs_inode_info is huge (becuase of the
+ symlink string). (super.c, inode.c, befs_fs.h) [WD]
+
+* Fixed a thinko that was corrupting file reads after the first block_run
+ is done being read. (datastream.c) [WD]
+
+* Removed fsync() hooks, since a read-only filesystem doesn't need them.
+ [Christoph Hellwig].
+
+* Fixed befs_readlink() (symlink.c) [Christoph Hellwig].
+
+* Removed all the Read-Write stuff. I'll redo it when it is time to add
+ write support (various files) [WD].
+
+* Removed prototypes for functions who's definitions have been removed
+ (befs_fs.h) [WD].
+
+
+Version 0.4 (2001-10-28)
+==========
+* Made it an option to use the old non-pagecache befs_file_read() for
+ testing purposes. (fs/Config.in)
+
+* Fixed unused variable warnings when compiling without debugging.
+
+* Fixed a bug where the inode and super_block didn't get their blockbits
+ fields set (inode.c and super.c).
+
+* Release patch version 11. AKA befs-driver version 0.4.
+
+* Thats right. New versioning scheme.
+ I've done some serious testing on it now (on my box anyhow), and it
+ seems stable and not outragously slow. Existing features are more-or-less
+ correct (see TODO list). But it isn't 1.0 yet. I think 0.4 gives me some
+ headroom before the big 1.0.
+
+
+2001-10-26
+==========
+* Fixed date format in this file. Was I smoking crack?
+
+* Removed old datastream code from file.c, since it is nolonger used.
+
+* Generic_read_file() is now used to read regular file data.
+ It doesn't chew up the buffer cache (it does page io instead), and seems
+ to be about as fast (even though it has to look up each file block
+ indivdualy). And it knows about doing readahead, which is a major plus.
+ So it does i/o in much larger chunks. It is the correct linux way. It
+ uses befs_get_block() by way of befs_readpage() to find the disk offsets
+ of blocks, which in turn calls befs_fpos2brun() in datastream.c to do
+ the hard work of finding the disk block number.
+
+* Changed method of checking for a dirty filesystem in befs_read_super
+ (super.c). Now we check to see if log_start and log_end differ. If so,
+ the journal needs to be replayed, and the filesystem cannot be mounted.
+
+* Fixed an extra instance of MOD_DEC_USE_COUNT in super.c
+
+* Fixed a problem with reading the superblock on devices with large sector
+ sizes (such as cdroms) on linux 2.4.10 and up.
+
+2001-10-24
+==========
+* Fix nasty bug in converting block numbers to struct befs_inode_addr.
+ Subtle, because the old version was only sometimes wrong.
+ Probably responsible for lots of problems. (inode.c)
+
+* Fix bug with reading an empty directory. (btree.c and dir.c)
+
+* This one looks good. Release patch version 10
+
+2001-10-23
+==========
+* Added btree searching function.
+
+* Use befs_btree_find in befs_lookup (namei.c)
+
+* Additional comments in btree.c
+
+2001-10-22
+==========
+* Added B+tree reading functions (in btree.c).
+ Made befs_readdir() use them them instead of the cruft in index.c.
+
+2001-09-11
+==========
+* Converted befs_read_file() to use the new datastream code.
+
+* Finally updated the README file.
+
+* Added many comments.
+
+* Posted version 6
+
+* Removed byte-order conversion code.
+ I have no intention of supporting it, and it was very ugly.
+ Flow control with #ifdef (ugh). Maybe I'll redo it once
+ native byteorder works 100%.
+
+2001-09-10
+==========
+* Finished implementing read_datastream()
+
+* made befs_read_brun() more general
+ Supports an offset to start at and a max bytes to read
+ Added a wrapper function to give the old call
+
+2001-09-30
+==========
+* Discovered that the datastream handleing code in file.c is quite deficient
+ in several respects. For one thing, it doesn't deal with indirect blocks
+
+* Rewrote datastream handleing.
+
+* Created io.c, for io related functions.
+ Previously, the befs_bread() funtions lived in file.c
+ Created the befs_read_brun() function.
+
+
+2001-09-07
+==========
+* Made a function to actually count the number of fs blocks used by a file.
+ And helper functions.
+ (fs/befs/inode.c)
+
+2001-09-05
+==========
+* Fixed a misunderstanding of the inode fields.
+ This fixed the problmem with wrong file sizes from du and others.
+ The i_blocks field of the inode struct is not the nuber of blocks for the
+ inode, it is the number of blocks for the file. Also, i_blksize is not
+ nessisarily the size of the inode, although in practice it works out.
+ Changed to blocksize of filesystem.
+ (fs/befs/inode.c)
+
+* Permanently removed code that had been provisionally ifdefed out of befs_fs.h
+
+* Since we don't support access time, make that field zero, instead of
+ copying m_time.
+ (fs/befs/inode.c)
+
+* Added sanity check for inode reading
+ Make sure inode we got was the one we asked for.
+ (fs/befs/inode.c)
+
+* Code cleanup
+ Local pointers to commonly used structures in inode.c.
+ Got rid of abominations befs_iaddr2inode() and befs_inode2ino().
+ Replaced with single function iaddr2blockno().
+ (fs/befs/super.c) (fs/befs/inode.c)
+
+2001-09-01
+==========
+* Fixed the problem with statfs where it would always claim the disk was
+ half full, due to improper understanding of the statfs fields.
+ (fs/befs/super.c)
+
+* Posted verion 4 of the patch
+
+2001-09-01
+==========
+* Changed the macros in befs_fs.h to inline functions.
+ More readable. Typesafe. Better
+ (include/linux/befs_fs.h)
+
+* Moved type definitions from befs_fs.h to a new file, befs_fs_types.h
+ Because befs_fs_i.h and befs_fs_sb.h were including befs_fs.h for the
+ typedefs, and they are inlcuded in <linux/fs.h>, which has definitions
+ that I want the inline functions in befs_fs.h to be able to see. Nasty
+ circularity.
+ (include/linux/befs_fs.h)
+
+2001-08-30
+==========
+* Cleaned up some wording.
+
+* Added additional consitency checks on mount
+ Check block_size agrees with block_shift
+ Check flags == BEFS_CLEAN
+ (fs/befs/super.c)
+
+* Tell the kernel to only mount befs read-only.
+ By setting the MS_RDONLY flag in befs_read_super().
+ Not that it was possible to write before. But now the kernel won't even try.
+ (fs/befs/super.c)
+
+* Got rid of kernel warning on mount.
+ The kernel doesn't like it if you call set_blocksize() on a device when
+ you have some of its blocks open. Moved the second set_blocksize() to the
+ very end of befs_read_super(), after we are done with the disk superblock.
+ (fs/befs/super.c)
+
+* Fixed wrong number of args bug in befs_dump_inode
+ (fs/befs/debug.c)
+
+* Solved lots of type mismatches in kprint()s
+ (everwhere)
+
+2001-08-27
+==========
+* Cleaned up the fs/Config.in entries a bit, now slightly more descriptive.
+
+* BeFS depends on NLS, so I made activating BeFS enable the NLS questions
+ (fs/nls/Config.in)
+
+* Added Configure.help entries for CONFIG_BEFS_FS and CONFIG_DEBUG_BEFS
+ (Documentation/Configure.help)
+
+2001-08-??
+==========
+* Removed superblock locking calls in befs_read_super(). In 2.4, the VFS
+ hands us a super_block struct that is already locked.
+
+2001-08-13
+==========
+* Will Dyson <will_dyson@pobox.com> is now attempting to maintain this module
+ Makoto Kato <m_kato@ga2.so-net.ne.jp> is original author.Daniel Berlin
+ also did some work on it (fixing it up for the later 2.3.x kernels, IIRC).
+
+* Fixed compile errors on 2.4.1 kernel (WD)
+ Resolve rejected patches
+ Accomodate changed NLS interface (util.h)
+ Needed to include <linux/slab.h> in most files
+ Makefile changes
+ fs/Config.in changes
+
+* Tried to niceify the code using the ext2 fs as a guide
+ Declare befs_fs_type using the DECLARE_FSTYPE_DEV() macro
+
+* Made it a configure option to turn on debugging (fs/Config.in)
+
+* Compiles on 2.4.7
diff --git a/fs/befs/Makefile b/fs/befs/Makefile
new file mode 100644
index 00000000000000..cf20ad65f80a5c
--- /dev/null
+++ b/fs/befs/Makefile
@@ -0,0 +1,15 @@
+#
+# Makefile for the linux BeOS filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+O_TARGET := befs.o
+
+obj-y := datastream.o btree.o super.o inode.o debug.o io.o linuxvfs.o
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/befs/TODO b/fs/befs/TODO
new file mode 100644
index 00000000000000..dbbdd62affd3ca
--- /dev/null
+++ b/fs/befs/TODO
@@ -0,0 +1,11 @@
+TODO
+==========
+
+* Convert comments to the Kernel-Doc format.
+
+* See if Alexander Viro's option parser made it into the kernel tree.
+ Use that if we can. (include/linux/parser.h)
+
+* See if we really need separate types for on-disk and in-memory
+ representations of the superblock and inode.
+
diff --git a/fs/befs/attribute.c b/fs/befs/attribute.c
new file mode 100644
index 00000000000000..c00c1defb22383
--- /dev/null
+++ b/fs/befs/attribute.c
@@ -0,0 +1,116 @@
+/*
+ * linux/fs/befs/attribute.c
+ *
+ * Copyright (C) 2002 Will Dyson <will_dyson@pobox.com>
+ *
+ * Many thanks to Dominic Giampaolo, author of "Practical File System
+ * Design with the Be File System", for such a helpful book.
+ *
+ */
+
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+#include "befs.h"
+#include "endian.h"
+
+#define SD_DATA(sd)\
+ (void*)((char*)sd + sizeof(*sd) + (sd->name_size - sizeof(sd->name)))
+
+#define SD_NEXT(sd)\
+ (befs_small_data*)((char*)sd + sizeof(*sd) + (sd->name_size - \
+ sizeof(sd->name) + sd->data_size))
+
+int
+ list_small_data(struct super_block *sb, befs_inode * inode, filldir_t filldir);
+
+befs_small_data *find_small_data(struct super_block *sb, befs_inode * inode,
+ const char *name);
+int
+ read_small_data(struct super_block *sb, befs_inode * inode,
+ befs_small_data * sdata, void *buf, size_t bufsize);
+
+/**
+ *
+ *
+ *
+ *
+ *
+ */
+befs_small_data *
+find_small_data(struct super_block *sb, befs_inode * inode, const char *name)
+{
+ befs_small_data *sdata = inode->small_data;
+
+ while (sdata->type != 0) {
+ if (strcmp(name, sdata->name) != 0) {
+ return sdata;
+ }
+ sdata = SD_NEXT(sdata);
+ }
+ return NULL;
+}
+
+/**
+ *
+ *
+ *
+ *
+ *
+ */
+int
+read_small_data(struct super_block *sb, befs_inode * inode,
+ const char *name, void *buf, size_t bufsize)
+{
+ befs_small_data *sdata;
+
+ sdata = find_small_data(sb, inode, name);
+ if (sdata == NULL)
+ return BEFS_ERR;
+ else if (sdata->data_size > bufsize)
+ return BEFS_ERR;
+
+ memcpy(buf, SD_DATA(sdata), sdata->data_size);
+
+ return BEFS_OK;
+}
+
+/**
+ *
+ *
+ *
+ *
+ *
+ */
+int
+list_small_data(struct super_block *sb, befs_inode * inode)
+{
+
+}
+
+/**
+ *
+ *
+ *
+ *
+ *
+ */
+int
+list_attr(struct super_block *sb, befs_inode * inode)
+{
+
+}
+
+/**
+ *
+ *
+ *
+ *
+ *
+ */
+int
+read_attr(struct super_block *sb, befs_inode * inode)
+{
+
+}
diff --git a/fs/befs/befs.h b/fs/befs/befs.h
new file mode 100644
index 00000000000000..31c940b715e0f2
--- /dev/null
+++ b/fs/befs/befs.h
@@ -0,0 +1,152 @@
+/*
+ * befs_fs.h
+ *
+ * Copyright (C) 2001-2002 Will Dyson <will_dyson@pobox.com>
+ * Copyright (C) 1999 Makoto Kato (m_kato@ga2.so-net.ne.jp)
+ */
+
+#ifndef _LINUX_BEFS_H
+#define _LINUX_BEFS_H
+
+#include "befs_fs_types.h"
+
+/* used in debug.c */
+#define BEFS_VERSION "0.9.3"
+
+typedef u64 befs_blocknr_t;
+typedef u32 vfs_blocknr_t;
+
+/*
+ * BeFS in memory structures
+ */
+
+typedef struct befs_mount_options {
+ gid_t gid;
+ uid_t uid;
+ int use_gid;
+ int use_uid;
+ int debug;
+ char *iocharset;
+} befs_mount_options;
+
+typedef struct befs_sb_info {
+ u32 magic1;
+ u32 block_size;
+ u32 block_shift;
+ int byte_order;
+ befs_off_t num_blocks;
+ befs_off_t used_blocks;
+ u32 inode_size;
+ u32 magic2;
+
+ /* Allocation group information */
+ u32 blocks_per_ag;
+ u32 ag_shift;
+ u32 num_ags;
+
+ /* jornal log entry */
+ befs_block_run log_blocks;
+ befs_off_t log_start;
+ befs_off_t log_end;
+
+ befs_inode_addr root_dir;
+ befs_inode_addr indices;
+ u32 magic3;
+
+ befs_mount_options mount_opts;
+ struct nls_table *nls;
+
+} befs_sb_info;
+
+typedef struct befs_inode_info {
+ u32 i_flags;
+ u32 i_type;
+
+ befs_inode_addr i_inode_num;
+ befs_inode_addr i_parent;
+ befs_inode_addr i_attribute;
+
+ union {
+ befs_data_stream ds;
+ char symlink[BEFS_SYMLINK_LEN];
+ } i_data;
+
+} befs_inode_info;
+
+enum befs_err {
+ BEFS_OK,
+ BEFS_ERR,
+ BEFS_BAD_INODE,
+ BEFS_BT_END,
+ BEFS_BT_EMPTY,
+ BEFS_BT_MATCH,
+ BEFS_BT_PARMATCH,
+ BEFS_BT_NOT_FOUND
+};
+
+/****************************/
+/* debug.c */
+void befs_error(const struct super_block *sb, const char *fmt, ...);
+void befs_warning(const struct super_block *sb, const char *fmt, ...);
+void befs_debug(const struct super_block *sb, const char *fmt, ...);
+
+void befs_dump_super_block(const struct super_block *sb, befs_super_block *);
+void befs_dump_inode(const struct super_block *sb, befs_inode *);
+void befs_dump_index_entry(const struct super_block *sb, befs_btree_super *);
+void befs_dump_index_node(const struct super_block *sb, befs_btree_nodehead *);
+void befs_dump_inode_addr(const struct super_block *sb, befs_inode_addr);
+/****************************/
+
+/* Gets a pointer to the private portion of the super_block
+ * structure from the public part
+ */
+static inline befs_sb_info *
+BEFS_SB(const struct super_block *super)
+{
+ return (befs_sb_info *) super->u.generic_sbp;
+}
+
+static inline befs_inode_info *
+BEFS_I(const struct inode *inode)
+{
+ return (befs_inode_info *) inode->u.generic_ip;
+}
+
+static inline befs_blocknr_t
+iaddr2blockno(struct super_block *sb, befs_inode_addr * iaddr)
+{
+ return ((iaddr->allocation_group << BEFS_SB(sb)->ag_shift) +
+ iaddr->start);
+}
+
+static inline befs_inode_addr
+blockno2iaddr(struct super_block *sb, befs_blocknr_t blockno)
+{
+ befs_inode_addr iaddr;
+ iaddr.allocation_group = blockno >> BEFS_SB(sb)->ag_shift;
+ iaddr.start =
+ blockno - (iaddr.allocation_group << BEFS_SB(sb)->ag_shift);
+ iaddr.len = 1;
+
+ return iaddr;
+}
+
+static inline unsigned int
+befs_iaddrs_per_block(struct super_block *sb)
+{
+ return BEFS_SB(sb)->block_size / sizeof (befs_inode_addr);
+}
+
+static inline int
+befs_iaddr_is_empty(befs_inode_addr * iaddr)
+{
+ return (!iaddr->allocation_group) && (!iaddr->start) && (!iaddr->len);
+}
+
+static inline size_t
+befs_brun_size(struct super_block *sb, befs_block_run run)
+{
+ return BEFS_SB(sb)->block_size * run.len;
+}
+
+#endif /* _LINUX_BEFS_H */
diff --git a/fs/befs/befs_fs_types.h b/fs/befs/befs_fs_types.h
new file mode 100644
index 00000000000000..9095518e918d2e
--- /dev/null
+++ b/fs/befs/befs_fs_types.h
@@ -0,0 +1,213 @@
+/*
+ * include/linux/befs_fs_types.h
+ *
+ * Copyright (C) 2001 Will Dyson (will@cs.earlham.edu)
+ *
+ *
+ *
+ * from linux/include/linux/befs_fs.h
+ *
+ * Copyright (C) 1999 Makoto Kato (m_kato@ga2.so-net.ne.jp)
+ *
+ */
+
+#ifndef _LINUX_BEFS_FS_TYPES
+#define _LINUX_BEFS_FS_TYPES
+
+#ifdef __KERNEL__
+#include <linux/types.h>
+#endif /*__KERNEL__*/
+
+#define PACKED __attribute__ ((__packed__))
+
+/*
+ * Max name lengths of BFS
+ */
+
+#define BEFS_NAME_LEN 255
+
+#define BEFS_SYMLINK_LEN 144
+#define BEFS_NUM_DIRECT_BLOCKS 12
+#define B_OS_NAME_LENGTH 32
+
+/* The datastream blocks mapped by the double-indirect
+ * block are always 4 fs blocks long.
+ * This eliminates the need for linear searches among
+ * the potentially huge number of indirect blocks
+ *
+ * Err. Should that be 4 fs blocks or 4k???
+ * It matters on large blocksize volumes
+ */
+#define BEFS_DBLINDIR_BRUN_LEN 4
+
+/*
+ * Flags of superblock
+ */
+
+enum super_flags {
+ BEFS_BYTESEX_BE,
+ BEFS_BYTESEX_LE,
+ BEFS_CLEAN = 0x434c454e,
+ BEFS_DIRTY = 0x44495254,
+ BEFS_SUPER_MAGIC1 = 0x42465331, /* BFS1 */
+ BEFS_SUPER_MAGIC2 = 0xdd121031,
+ BEFS_SUPER_MAGIC3 = 0x15b6830e,
+};
+
+#define BEFS_BYTEORDER_NATIVE 0x42494745
+
+#define BEFS_SUPER_MAGIC BEFS_SUPER_MAGIC1
+
+/*
+ * Flags of inode
+ */
+
+#define BEFS_INODE_MAGIC1 0x3bbe0ad9
+
+enum inode_flags {
+ BEFS_INODE_IN_USE = 0x00000001,
+ BEFS_ATTR_INODE = 0x00000004,
+ BEFS_INODE_LOGGED = 0x00000008,
+ BEFS_INODE_DELETED = 0x00000010,
+ BEFS_LONG_SYMLINK = 0x00000040,
+ BEFS_PERMANENT_FLAG = 0x0000ffff,
+ BEFS_INODE_NO_CREATE = 0x00010000,
+ BEFS_INODE_WAS_WRITTEN = 0x00020000,
+ BEFS_NO_TRANSACTION = 0x00040000,
+};
+/*
+ * On-Disk datastructures of BeFS
+ */
+
+typedef u64 befs_off_t;
+typedef u64 befs_time_t;
+typedef void befs_binode_etc;
+
+/* Block runs */
+typedef struct {
+ u32 allocation_group;
+ u16 start;
+ u16 len;
+} PACKED befs_block_run;
+
+typedef befs_block_run befs_inode_addr;
+
+/*
+ * The Superblock Structure
+ */
+typedef struct {
+ char name[B_OS_NAME_LENGTH];
+ u32 magic1;
+ u32 fs_byte_order;
+
+ u32 block_size;
+ u32 block_shift;
+
+ befs_off_t num_blocks;
+ befs_off_t used_blocks;
+
+ u32 inode_size;
+
+ u32 magic2;
+ u32 blocks_per_ag;
+ u32 ag_shift;
+ u32 num_ags;
+
+ u32 flags;
+
+ befs_block_run log_blocks;
+ befs_off_t log_start;
+ befs_off_t log_end;
+
+ u32 magic3;
+ befs_inode_addr root_dir;
+ befs_inode_addr indices;
+
+} PACKED befs_super_block;
+
+/*
+ * Note: the indirect and dbl_indir block_runs may
+ * be longer than one block!
+ */
+typedef struct {
+ befs_block_run direct[BEFS_NUM_DIRECT_BLOCKS];
+ befs_off_t max_direct_range;
+ befs_block_run indirect;
+ befs_off_t max_indirect_range;
+ befs_block_run double_indirect;
+ befs_off_t max_double_indirect_range;
+ befs_off_t size;
+} PACKED befs_data_stream;
+
+/* Attribute */
+typedef struct {
+ u32 type;
+ u16 name_size;
+ u16 data_size;
+ char name[1];
+} PACKED befs_small_data;
+
+/* Inode structure */
+typedef struct {
+ u32 magic1;
+ befs_inode_addr inode_num;
+ u32 uid;
+ u32 gid;
+ u32 mode;
+ u32 flags;
+ befs_time_t create_time;
+ befs_time_t last_modified_time;
+ befs_inode_addr parent;
+ befs_inode_addr attributes;
+ u32 type;
+
+ u32 inode_size;
+ u32 etc; /* not use */
+
+ union {
+ befs_data_stream datastream;
+ char symlink[BEFS_SYMLINK_LEN];
+ } data;
+
+ u32 pad[4]; /* not use */
+ befs_small_data small_data[1];
+} PACKED befs_inode;
+
+/*
+ * B+tree superblock
+ */
+
+#define BEFS_BTREE_MAGIC 0x69f6c2e8
+
+enum btree_types {
+ BTREE_STRING_TYPE = 0,
+ BTREE_INT32_TYPE = 1,
+ BTREE_UINT32_TYPE = 2,
+ BTREE_INT64_TYPE = 3,
+ BTREE_UINT64_TYPE = 4,
+ BTREE_FLOAT_TYPE = 5,
+ BTREE_DOUBLE_TYPE = 6
+};
+
+typedef struct {
+ u32 magic;
+ u32 node_size;
+ u32 max_depth;
+ u32 data_type;
+ befs_off_t root_node_ptr;
+ befs_off_t free_node_ptr;
+ befs_off_t max_size;
+} PACKED befs_btree_super;
+
+/*
+ * Header stucture of each btree node
+ */
+typedef struct {
+ befs_off_t left;
+ befs_off_t right;
+ befs_off_t overflow;
+ u16 all_key_count;
+ u16 all_key_length;
+} PACKED befs_btree_nodehead;
+
+#endif /* _LINUX_BEFS_FS_TYPES */
diff --git a/fs/befs/btree.c b/fs/befs/btree.c
new file mode 100644
index 00000000000000..e71ade8a168501
--- /dev/null
+++ b/fs/befs/btree.c
@@ -0,0 +1,785 @@
+/*
+ * linux/fs/befs/btree.c
+ *
+ * Copyright (C) 2001-2002 Will Dyson <will_dyson@pobox.com>
+ *
+ * Licensed under the GNU GPL. See the file COPYING for details.
+ *
+ * 2002-02-05: Sergey S. Kostyliov added binary search withing
+ * btree nodes.
+ *
+ * Many thanks to:
+ *
+ * Dominic Giampaolo, author of "Practical File System
+ * Design with the Be File System", for such a helpful book.
+ *
+ * Marcus J. Ranum, author of the b+tree package in
+ * comp.sources.misc volume 10. This code is not copied from that
+ * work, but it is partially based on it.
+ *
+ * Makoto Kato, author of the original BeFS for linux filesystem
+ * driver.
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+
+#include "befs.h"
+#include "btree.h"
+#include "datastream.h"
+#include "endian.h"
+
+/*
+ * The btree functions in this file are built on top of the
+ * datastream.c interface, which is in turn built on top of the
+ * io.c interface.
+ */
+
+/* Befs B+tree structure:
+ *
+ * The first thing in the tree is the tree superblock. It tells you
+ * all kinds of usefull things about the tree, like where the rootnode
+ * is located, and the size of the nodes (always 1024 with current version
+ * of BeOS).
+ *
+ * The rest of the tree consists of a series of nodes. Nodes contain a header
+ * (struct befs_btree_nodehead), the packed key data, an array of shorts
+ * containing the ending offsets for each of the keys, and an array of
+ * befs_off_t values. In interior nodes, the keys are the ending keys for
+ * the childnode they point to, and the values are offsets into the
+ * datastream containing the tree.
+ */
+
+/* Note:
+ *
+ * The book states 2 confusing things about befs b+trees. First,
+ * it states that the overflow feild of node headers is used by internal nodes
+ * to point to another node that "effectivly continues this one". Here is what
+ * I belive that means. Each key in internal nodes points to another node that
+ * contains key values less than itself. Inspection reveals that the last key
+ * in the internal node is not the last key in the index. Keys that are
+ * greater than the last key in the internal node go into the overflow node.
+ * I imagine there is a performance reason for this.
+ *
+ * Second, it states that the header of a btree node is sufficient to
+ * distinguish internal nodes from leaf nodes. Without saying exactly how.
+ * After figuring out the first, it becomes obvious that internal nodes have
+ * overflow nodes and leafnodes do not.
+ */
+
+/*
+ * Currently, this code is only good for directory B+trees.
+ * In order to be used for other BFS indexes, it needs to be extended to handle
+ * duplicate keys and non-string keytypes (int32, int64, float, double).
+ */
+
+/*
+ * In memory structure of each btree node
+ */
+typedef struct {
+ befs_btree_nodehead head; /* head of node converted to cpu byteorder */
+ struct buffer_head *bh;
+ befs_btree_nodehead *od_node; /* on disk node */
+} befs_btree_node;
+
+/* local constants */
+const static befs_off_t befs_bt_inval = 0xffffffffffffffffULL;
+
+/* local functions */
+static int befs_btree_seekleaf(struct super_block *sb, befs_data_stream * ds,
+ befs_btree_super * bt_super,
+ befs_btree_node * this_node,
+ befs_off_t * node_off);
+
+static int befs_bt_read_super(struct super_block *sb, befs_data_stream * ds,
+ befs_btree_super * sup);
+
+static int befs_bt_read_node(struct super_block *sb, befs_data_stream * ds,
+ befs_btree_node * node, befs_off_t node_off);
+
+static int befs_leafnode(befs_btree_node * node);
+
+static u16 *befs_bt_keylen_index(befs_btree_node * node);
+
+static befs_off_t *befs_bt_valarray(befs_btree_node * node);
+
+static char *befs_bt_keydata(befs_btree_node * node);
+
+static int befs_find_key(struct super_block *sb, befs_btree_node * node,
+ const char *findkey, befs_off_t * value);
+
+static char *befs_bt_get_key(struct super_block *sb, befs_btree_node * node,
+ int index, u16 * keylen);
+
+static int befs_compare_strings(const void *key1, int keylen1,
+ const void *key2, int keylen2);
+
+/**
+ * befs_bt_read_super - read in btree superblock convert to cpu byteorder
+ * @sb: Filesystem superblock
+ * @ds: Datastream to read from
+ * @sup: Buffer in which to place the btree superblock
+ *
+ * Calls befs_read_datastream to read in the btree superblock and
+ * makes sure it is in cpu byteorder, byteswapping if nessisary.
+ *
+ * On success, returns BEFS_OK and *@sup contains the btree superblock,
+ * in cpu byte order.
+ *
+ * On failure, BEFS_ERR is returned.
+ */
+static int
+befs_bt_read_super(struct super_block *sb, befs_data_stream * ds,
+ befs_btree_super * sup)
+{
+ struct buffer_head *bh = NULL;
+ befs_btree_super *od_sup = NULL;
+
+ befs_debug(sb, "---> befs_btree_read_super()");
+
+ bh = befs_read_datastream(sb, ds, 0, NULL);
+
+ if (!bh) {
+ befs_error(sb, "Couldn't read index header.");
+ goto error;
+ }
+ od_sup = (befs_btree_super *) bh->b_data;
+ befs_dump_index_entry(sb, od_sup);
+
+ sup->magic = fs32_to_cpu(sb, od_sup->magic);
+ sup->node_size = fs32_to_cpu(sb, od_sup->node_size);
+ sup->max_depth = fs32_to_cpu(sb, od_sup->max_depth);
+ sup->data_type = fs32_to_cpu(sb, od_sup->data_type);
+ sup->root_node_ptr = fs64_to_cpu(sb, od_sup->root_node_ptr);
+ sup->free_node_ptr = fs64_to_cpu(sb, od_sup->free_node_ptr);
+ sup->max_size = fs64_to_cpu(sb, od_sup->max_size);
+
+ brelse(bh);
+ if (sup->magic != BEFS_BTREE_MAGIC) {
+ befs_error(sb, "Index header has bad magic.");
+ goto error;
+ }
+
+ befs_debug(sb, "<--- befs_btree_read_super()");
+ return BEFS_OK;
+
+ error:
+ befs_debug(sb, "<--- befs_btree_read_super() ERROR");
+ return BEFS_ERR;
+}
+
+/**
+ * befs_bt_read_node - read in btree node and convert to cpu byteorder
+ * @sb: Filesystem superblock
+ * @ds: Datastream to read from
+ * @node: Buffer in which to place the btree node
+ * @node_off: Starting offset (in bytes) of the node in @ds
+ *
+ * Calls befs_read_datastream to read in the indicated btree node and
+ * makes sure its header feilds are in cpu byteorder, byteswapping if
+ * nessisary.
+ * Note: node->bh must be NULL when this function called first
+ * time. Don't forget brelse(node->bh) after last call.
+ *
+ * On success, returns BEFS_OK and *@node contains the btree node that
+ * starts at @node_off, with the node->head fields in cpu byte order.
+ *
+ * On failure, BEFS_ERR is returned.
+ */
+
+static int
+befs_bt_read_node(struct super_block *sb, befs_data_stream * ds,
+ befs_btree_node * node, befs_off_t node_off)
+{
+ uint off = 0;
+
+ befs_debug(sb, "---> befs_bt_read_node()");
+
+ if (node->bh)
+ brelse(node->bh);
+
+ node->bh = befs_read_datastream(sb, ds, node_off, &off);
+ if (!node->bh) {
+ befs_error(sb, "befs_bt_read_node() failed to read "
+ "node at %Lu", node_off);
+ befs_debug(sb, "<--- befs_bt_read_node() ERROR");
+
+ return BEFS_ERR;
+ }
+ node->od_node =
+ (befs_btree_nodehead *) ((void *) node->bh->b_data + off);
+
+ befs_dump_index_node(sb, node->od_node);
+
+ node->head.left = fs64_to_cpu(sb, node->od_node->left);
+ node->head.right = fs64_to_cpu(sb, node->od_node->right);
+ node->head.overflow = fs64_to_cpu(sb, node->od_node->overflow);
+ node->head.all_key_count =
+ fs16_to_cpu(sb, node->od_node->all_key_count);
+ node->head.all_key_length =
+ fs16_to_cpu(sb, node->od_node->all_key_length);
+
+ befs_debug(sb, "<--- befs_btree_read_node()");
+ return BEFS_OK;
+}
+
+/**
+ * befs_btree_find - Find a key in a befs B+tree
+ * @sb: Filesystem superblock
+ * @ds: Datastream containing btree
+ * @key: Key string to lookup in btree
+ * @value: Value stored with @key
+ *
+ * On sucess, returns BEFS_OK and sets *@value to the value stored
+ * with @key (usually the disk block number of an inode).
+ *
+ * On failure, returns BEFS_ERR or BEFS_BT_NOT_FOUND.
+ *
+ * Algorithm:
+ * Read the superblock and rootnode of the b+tree.
+ * Drill down through the interior nodes using befs_find_key().
+ * Once at the correct leaf node, use befs_find_key() again to get the
+ * actuall value stored with the key.
+ */
+int
+befs_btree_find(struct super_block *sb, befs_data_stream * ds,
+ const char *key, befs_off_t * value)
+{
+ befs_btree_node *this_node = NULL;
+ befs_btree_super bt_super;
+ befs_off_t node_off;
+ int res;
+
+ befs_debug(sb, "---> befs_btree_find() Key: %s", key);
+
+ if (befs_bt_read_super(sb, ds, &bt_super) != BEFS_OK) {
+ befs_error(sb,
+ "befs_btree_find() failed to read index superblock");
+ goto error;
+ }
+
+ this_node = (befs_btree_node *) kmalloc(sizeof (befs_btree_node),
+ GFP_NOFS);
+ if (!this_node) {
+ befs_error(sb, "befs_btree_find() failed to allocate %u "
+ "bytes of memory", sizeof (befs_btree_node));
+ goto error;
+ }
+
+ this_node->bh = NULL;
+
+ /* read in root node */
+ node_off = bt_super.root_node_ptr;
+ if (befs_bt_read_node(sb, ds, this_node, node_off) != BEFS_OK) {
+ befs_error(sb, "befs_btree_find() failed to read "
+ "node at %Lu", node_off);
+ goto error_alloc;
+ }
+
+ while (!befs_leafnode(this_node)) {
+ res = befs_find_key(sb, this_node, key, &node_off);
+ if (res == BEFS_BT_NOT_FOUND)
+ node_off = this_node->head.overflow;
+ /* if no match, go to overflow node */
+ if (befs_bt_read_node(sb, ds, this_node, node_off) != BEFS_OK) {
+ befs_error(sb, "befs_btree_find() failed to read "
+ "node at %Lu", node_off);
+ goto error_alloc;
+ }
+ }
+
+ /* at the correct leaf node now */
+
+ res = befs_find_key(sb, this_node, key, value);
+
+ brelse(this_node->bh);
+ kfree(this_node);
+
+ if (res != BEFS_BT_MATCH) {
+ befs_debug(sb, "<--- befs_btree_find() Key %s not found", key);
+ *value = 0;
+ return BEFS_BT_NOT_FOUND;
+ }
+ befs_debug(sb, "<--- befs_btree_find() Found key %s, value %Lu",
+ key, *value);
+ return BEFS_OK;
+
+ error_alloc:
+ kfree(this_node);
+ error:
+ *value = 0;
+ befs_debug(sb, "<--- befs_btree_find() ERROR");
+ return BEFS_ERR;
+}
+
+/**
+ * befs_find_key - Search for a key within a node
+ * @sb: Filesystem superblock
+ * @node: Node to find the key within
+ * @key: Keystring to search for
+ * @value: If key is found, the value stored with the key is put here
+ *
+ * finds exact match if one exists, and returns BEFS_BT_MATCH
+ * If no exact match, finds first key in node that is greater
+ * (alpabeticly) than the search key and returns BEFS_BT_PARMATCH
+ * (for partial match, I guess). Can you think of something better to
+ * call it?
+ *
+ * If no key was a match or greater than the search key, return
+ * BEFS_BT_NOT_FOUND.
+ *
+ * Use binary search instead of a linear.
+ */
+static int
+befs_find_key(struct super_block *sb, befs_btree_node * node,
+ const char *findkey, befs_off_t * value)
+{
+ int first, last, mid;
+ int eq;
+ u16 keylen;
+ int findkey_len;
+ char *thiskey;
+ befs_off_t *valarray;
+
+ befs_debug(sb, "---> befs_find_key() %s", findkey);
+
+ *value = 0;
+
+ findkey_len = strlen(findkey);
+
+ /* if node can not contain key, just skeep this node */
+ last = node->head.all_key_count - 1;
+ thiskey = befs_bt_get_key(sb, node, last, &keylen);
+
+ eq = befs_compare_strings(thiskey, keylen, findkey, findkey_len);
+ if (eq < 0) {
+ befs_debug(sb, "<--- befs_find_key() %s not found", findkey);
+ return BEFS_BT_NOT_FOUND;
+ }
+
+ valarray = befs_bt_valarray(node);
+
+ /* simple binary search */
+ first = 0;
+ mid = 0;
+ while (last >= first) {
+ mid = (last + first) / 2;
+ befs_debug(sb, "first: %d, last: %d, mid: %d", first, last,
+ mid);
+ thiskey = befs_bt_get_key(sb, node, mid, &keylen);
+ eq = befs_compare_strings(thiskey, keylen, findkey,
+ findkey_len);
+ *value = fs64_to_cpu(sb, valarray[mid]);
+
+ if (eq == 0) {
+ befs_debug(sb, "<--- befs_find_key() found %s at %d",
+ thiskey, mid);
+
+ return BEFS_BT_MATCH;
+ }
+ if (eq > 0)
+ last = mid - 1;
+ else
+ first = mid + 1;
+ }
+ if (eq < 0)
+ *value = fs64_to_cpu(sb, valarray[mid + 1]);
+ befs_debug(sb, "<--- befs_find_key() found %s at %d", thiskey, mid);
+ return BEFS_BT_PARMATCH;
+}
+
+/**
+ * befs_btree_read - Traverse leafnodes of a btree
+ * @sb: Filesystem superblock
+ * @ds: Datastream containing btree
+ * @key_no: Key number (alphabetical order) of key to read
+ * @bufsize: Size of the buffer to return key in
+ * @keybuf: Pointer to a buffer to put the key in
+ * @keysize: Length of the returned key
+ * @value: Value stored with the returned key
+ *
+ * Heres how it works: Key_no is the index of the key/value pair to
+ * retun in keybuf/value.
+ * Bufsize is the size of keybuf (BEFS_NAME_LEN+1 is a good size). Keysize is
+ * the number of charecters in the key (just a convience).
+ *
+ * Algorithm:
+ * Get the first leafnode of the tree. See if the requested key is in that
+ * node. If not, follow the node->right link to the next leafnode. Repeat
+ * until the (key_no)th key is found or the tree is out of keys.
+ */
+int
+befs_btree_read(struct super_block *sb, befs_data_stream * ds,
+ loff_t key_no, size_t bufsize, char *keybuf, size_t * keysize,
+ befs_off_t * value)
+{
+ befs_btree_node *this_node;
+ befs_btree_super bt_super;
+ befs_off_t node_off = 0;
+ int cur_key;
+ befs_off_t *valarray;
+ char *keystart;
+ u16 keylen;
+ int res;
+
+ uint key_sum = 0;
+
+ befs_debug(sb, "---> befs_btree_read()");
+
+ if (befs_bt_read_super(sb, ds, &bt_super) != BEFS_OK) {
+ befs_error(sb,
+ "befs_btree_read() failed to read index superblock");
+ goto error;
+ }
+
+ if ((this_node = (befs_btree_node *)
+ kmalloc(sizeof (befs_btree_node), GFP_NOFS)) == NULL) {
+ befs_error(sb, "befs_btree_read() failed to allocate %u "
+ "bytes of memory", sizeof (befs_btree_node));
+ goto error;
+ }
+
+ node_off = bt_super.root_node_ptr;
+ this_node->bh = NULL;
+
+ /* seeks down to first leafnode, reads it into this_node */
+ res = befs_btree_seekleaf(sb, ds, &bt_super, this_node, &node_off);
+ if (res == BEFS_BT_EMPTY) {
+ brelse(this_node->bh);
+ kfree(this_node);
+ *value = 0;
+ *keysize = 0;
+ befs_debug(sb, "<--- befs_btree_read() Tree is EMPTY");
+ return BEFS_BT_EMPTY;
+ } else if (res == BEFS_ERR) {
+ goto error_alloc;
+ }
+
+ /* find the leaf node containing the key_no key */
+
+ while (key_sum + this_node->head.all_key_count <= key_no) {
+
+ /* no more nodes to look in: key_no is too large */
+ if (this_node->head.right == befs_bt_inval) {
+ *keysize = 0;
+ *value = 0;
+ befs_debug(sb,
+ "<--- befs_btree_read() END of keys at %Lu",
+ key_sum + this_node->head.all_key_count);
+ brelse(this_node->bh);
+ kfree(this_node);
+ return BEFS_BT_END;
+ }
+
+ key_sum += this_node->head.all_key_count;
+ node_off = this_node->head.right;
+
+ if (befs_bt_read_node(sb, ds, this_node, node_off) != BEFS_OK) {
+ befs_error(sb, "befs_btree_read() failed to read "
+ "node at %Lu", node_off);
+ goto error_alloc;
+ }
+ }
+
+ /* how many keys into this_node is key_no */
+ cur_key = key_no - key_sum;
+
+ /* get pointers to datastructures within the node body */
+ valarray = befs_bt_valarray(this_node);
+
+ keystart = befs_bt_get_key(sb, this_node, cur_key, &keylen);
+
+ befs_debug(sb, "Read [%Lu,%d]: keysize %d", node_off, cur_key, keylen);
+
+ if (bufsize < keylen + 1) {
+ befs_error(sb, "befs_btree_read() keybuf too small (%u) "
+ "for key of size %d", bufsize, keylen);
+ brelse(this_node->bh);
+ goto error_alloc;
+ };
+
+ strncpy(keybuf, keystart, keylen);
+ *value = fs64_to_cpu(sb, valarray[cur_key]);
+ *keysize = keylen;
+ keybuf[keylen] = '\0';
+
+ befs_debug(sb, "Read [%Lu,%d]: Key \"%.*s\", Value %Lu", node_off,
+ cur_key, keylen, keybuf, *value);
+
+ brelse(this_node->bh);
+ kfree(this_node);
+
+ befs_debug(sb, "<--- befs_btree_read()");
+
+ return BEFS_OK;
+
+ error_alloc:
+ kfree(this_node);
+
+ error:
+ *keysize = 0;
+ *value = 0;
+ befs_debug(sb, "<--- befs_btree_read() ERROR");
+ return BEFS_ERR;
+}
+
+/**
+ * befs_btree_seekleaf - Find the first leafnode in the btree
+ * @sb: Filesystem superblock
+ * @ds: Datastream containing btree
+ * @bt_super: Pointer to the uperblock of the btree
+ * @this_node: Buffer to return the leafnode in
+ * @node_off: Pointer to offset of current node within datastream. Modified
+ * by the function.
+ *
+ *
+ * Helper function for btree traverse. Moves the current position to the
+ * start of the first leaf node.
+ *
+ * Also checks for an empty tree. If there are no keys, returns BEFS_BT_EMPTY.
+ */
+static int
+befs_btree_seekleaf(struct super_block *sb, befs_data_stream * ds,
+ befs_btree_super * bt_super, befs_btree_node * this_node,
+ befs_off_t * node_off)
+{
+
+ befs_debug(sb, "---> befs_btree_seekleaf()");
+
+ if (befs_bt_read_node(sb, ds, this_node, *node_off) != BEFS_OK) {
+ befs_error(sb, "befs_btree_seekleaf() failed to read "
+ "node at %Lu", *node_off);
+ goto error;
+ }
+ befs_debug(sb, "Seekleaf to root node %Lu", *node_off);
+
+ if (this_node->head.all_key_count == 0 && befs_leafnode(this_node)) {
+ befs_debug(sb, "<--- befs_btree_seekleaf() Tree is EMPTY");
+ return BEFS_BT_EMPTY;
+ }
+
+ while (!befs_leafnode(this_node)) {
+
+ if (this_node->head.all_key_count == 0) {
+ befs_debug(sb, "befs_btree_seekleaf() encountered "
+ "an empty interior node: %Lu. Using Overflow "
+ "node: %Lu", *node_off,
+ this_node->head.overflow);
+ *node_off = this_node->head.overflow;
+ } else {
+ befs_off_t *valarray = befs_bt_valarray(this_node);
+ *node_off = fs64_to_cpu(sb, valarray[0]);
+ }
+ if (befs_bt_read_node(sb, ds, this_node, *node_off) != BEFS_OK) {
+ befs_error(sb, "befs_btree_seekleaf() failed to read "
+ "node at %Lu", *node_off);
+ goto error;
+ }
+
+ befs_debug(sb, "Seekleaf to child node %Lu", *node_off);
+ }
+ befs_debug(sb, "Node %Lu is a leaf node", *node_off);
+
+ return BEFS_OK;
+
+ error:
+ befs_debug(sb, "<--- befs_btree_seekleaf() ERROR");
+ return BEFS_ERR;
+}
+
+/**
+ * befs_leafnode - Determine if the btree node is a leaf node or an
+ * interior node
+ * @node: Pointer to node structure to test
+ *
+ * Return 1 if leaf, 0 if interior
+ */
+static int
+befs_leafnode(befs_btree_node * node)
+{
+ /* all interior nodes (and only interior nodes) have an overflow node */
+ if (node->head.overflow == befs_bt_inval)
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * befs_bt_keylen_index - Finds start of keylen index in a node
+ * @node: Pointer to the node structure to find the keylen index within
+ *
+ * Returns a pointer to the start of the key length index array
+ * of the B+tree node *@node
+ *
+ * "The length of all the keys in the node is added to the size of the
+ * header and then rounded up to a multiple of four to get the begining
+ * of the key length index" (p.88, practical filesystem design).
+ *
+ * Exept that rounding up to 8 works, and rounding up to 4 doesn't.
+ */
+static u16 *
+befs_bt_keylen_index(befs_btree_node * node)
+{
+ const int keylen_align = 8;
+ unsigned long int off =
+ (sizeof (befs_btree_nodehead) + node->head.all_key_length);
+ ulong tmp = off % keylen_align;
+
+ if (tmp)
+ off += keylen_align - tmp;
+
+ return (u16 *) ((void *) node->od_node + off);
+}
+
+/**
+ * befs_bt_valarray - Finds the start of value array in a node
+ * @node: Pointer to the node structure to find the value array within
+ *
+ * Returns a pointer to the start of the value array
+ * of the node pointed to by the node header
+ */
+static befs_off_t *
+befs_bt_valarray(befs_btree_node * node)
+{
+ void *keylen_index_start = (void *) befs_bt_keylen_index(node);
+ size_t keylen_index_size = node->head.all_key_count * sizeof (u16);
+
+ return (befs_off_t *) (keylen_index_start + keylen_index_size);
+}
+
+/**
+ * befs_bt_keydata - Finds start of keydata array in a node
+ * @node: Pointer to the node structure to find the keydata array within
+ *
+ * Returns a pointer to the start of the keydata array
+ * of the node pointed to by the node header
+ */
+static char *
+befs_bt_keydata(befs_btree_node * node)
+{
+ return (char *) ((void *) node->od_node + sizeof (befs_btree_nodehead));
+}
+
+/**
+ * befs_bt_get_key - returns a pointer to the start of a key
+ * @sb: filesystem superblock
+ * @node: node in which to look for the key
+ * @index: the index of the key to get
+ * @keylen: modified to be the length of the key at @index
+ *
+ * Returns a valid pointer into @node on success.
+ * Returns NULL on failure (bad input) and sets *@keylen = 0
+ */
+static char *
+befs_bt_get_key(struct super_block *sb, befs_btree_node * node,
+ int index, u16 * keylen)
+{
+ int prev_key_end;
+ char *keystart;
+ u16 *keylen_index;
+
+ if (index < 0 || index > node->head.all_key_count) {
+ *keylen = 0;
+ return NULL;
+ }
+
+ keystart = befs_bt_keydata(node);
+ keylen_index = befs_bt_keylen_index(node);
+
+ if (index == 0)
+ prev_key_end = 0;
+ else
+ prev_key_end = fs16_to_cpu(sb, keylen_index[index - 1]);
+
+ *keylen = fs16_to_cpu(sb, keylen_index[index]) - prev_key_end;
+
+ return keystart + prev_key_end;
+}
+
+/**
+ * befs_compare_strings - compare two strings
+ * @key1: pointer to the first key to be compared
+ * @keylen1: length in bytes of key1
+ * @key2: pointer to the second key to be compared
+ * @kelen2: lenght in bytes of key2
+ *
+ * Returns 0 if @key1 and @key2 are equal.
+ * Returns >0 if @key1 is greater.
+ * Returns <0 if @key2 is greater..
+ */
+static int
+befs_compare_strings(const void *key1, int keylen1,
+ const void *key2, int keylen2)
+{
+ int len = min_t(int, keylen1, keylen2);
+ int result = strncmp(key1, key2, len);
+ if (result == 0)
+ result = keylen1 - keylen2;
+ return result;
+}
+
+/* These will be used for non-string keyed btrees */
+#if 0
+static int
+btree_compare_int32(cont void *key1, int keylen1, const void *key2, int keylen2)
+{
+ return *(int32_t *) key1 - *(int32_t *) key2;
+}
+
+static int
+btree_compare_uint32(cont void *key1, int keylen1,
+ const void *key2, int keylen2)
+{
+ if (*(u_int32_t *) key1 == *(u_int32_t *) key2)
+ return 0;
+ else if (*(u_int32_t *) key1 > *(u_int32_t *) key2)
+ return 1;
+
+ return -1;
+}
+static int
+btree_compare_int64(cont void *key1, int keylen1, const void *key2, int keylen2)
+{
+ if (*(int64_t *) key1 == *(int64_t *) key2)
+ return 0;
+ else if (*(int64_t *) key1 > *(int64_t *) key2)
+ return 1;
+
+ return -1;
+}
+
+static int
+btree_compare_uint64(cont void *key1, int keylen1,
+ const void *key2, int keylen2)
+{
+ if (*(u_int64_t *) key1 == *(u_int64_t *) key2)
+ return 0;
+ else if (*(u_int64_t *) key1 > *(u_int64_t *) key2)
+ return 1;
+
+ return -1;
+}
+
+static int
+btree_compare_float(cont void *key1, int keylen1, const void *key2, int keylen2)
+{
+ float result = *(float *) key1 - *(float *) key2;
+ if (result == 0.0f)
+ return 0;
+
+ return (result < 0.0f) ? -1 : 1;
+}
+
+static int
+btree_compare_double(cont void *key1, int keylen1,
+ const void *key2, int keylen2)
+{
+ double result = *(double *) key1 - *(double *) key2;
+ if (result == 0.0)
+ return 0;
+
+ return (result < 0.0) ? -1 : 1;
+}
+#endif //0
diff --git a/fs/befs/btree.h b/fs/befs/btree.h
new file mode 100644
index 00000000000000..9aa43427ddc746
--- /dev/null
+++ b/fs/befs/btree.h
@@ -0,0 +1,11 @@
+/*
+ * btree.h
+ *
+ */
+
+int befs_btree_find(struct super_block *sb, befs_data_stream * ds,
+ const char *key, befs_off_t * value);
+
+int befs_btree_read(struct super_block *sb, befs_data_stream * ds,
+ loff_t key_no, size_t bufsize, char *keybuf,
+ size_t * keysize, befs_off_t * value);
diff --git a/fs/befs/datastream.c b/fs/befs/datastream.c
new file mode 100644
index 00000000000000..4467d22d5387a4
--- /dev/null
+++ b/fs/befs/datastream.c
@@ -0,0 +1,528 @@
+/*
+ * linux/fs/befs/datastream.c
+ *
+ * Copyright (C) 2001 Will Dyson <will_dyson@pobox.com>
+ *
+ * Based on portions of file.c by Makoto Kato <m_kato@ga2.so-net.ne.jp>
+ *
+ * Many thanks to Dominic Giampaolo, author of "Practical File System
+ * Design with the Be File System", for such a helpful book.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+
+#include "befs.h"
+#include "datastream.h"
+#include "io.h"
+#include "endian.h"
+
+const befs_inode_addr BAD_IADDR = { 0, 0, 0 };
+
+static int befs_find_brun_direct(struct super_block *sb,
+ befs_data_stream * data,
+ befs_blocknr_t blockno, befs_block_run * run);
+
+static int befs_find_brun_indirect(struct super_block *sb,
+ befs_data_stream * data,
+ befs_blocknr_t blockno,
+ befs_block_run * run);
+
+static int befs_find_brun_dblindirect(struct super_block *sb,
+ befs_data_stream * data,
+ befs_blocknr_t blockno,
+ befs_block_run * run);
+
+/**
+ * befs_read_datastream - get buffer_head containing data, starting from pos.
+ * @sb: Filesystem superblock
+ * @ds: datastrem to find data with
+ * @pos: start of data
+ * @off: offset of data in buffer_head->b_data
+ *
+ * Returns pointer to buffer_head containing data starting with offset @off,
+ * if you don't need to know offset just set @off = NULL.
+ */
+struct buffer_head *
+befs_read_datastream(struct super_block *sb, befs_data_stream * ds,
+ befs_off_t pos, uint * off)
+{
+ struct buffer_head *bh = NULL;
+ befs_block_run run;
+ befs_blocknr_t block; /* block coresponding to pos */
+
+ befs_debug(sb, "---> befs_read_datastream() %Lu", pos);
+ block = pos >> BEFS_SB(sb)->block_shift;
+ if (off)
+ *off = pos - (block << BEFS_SB(sb)->block_shift);
+
+ if (befs_fblock2brun(sb, ds, block, &run) != BEFS_OK) {
+ befs_error(sb, "BeFS: Error finding disk addr of block %lu",
+ block);
+ befs_debug(sb, "<--- befs_read_datastream() ERROR");
+ return NULL;
+ }
+ bh = befs_bread_iaddr(sb, run);
+ if (!bh) {
+ befs_error(sb, "BeFS: Error reading block %lu from datastream",
+ block);
+ return NULL;
+ }
+
+ befs_debug(sb, "<--- befs_read_datastream() read data, starting at %Lu",
+ pos);
+
+ return bh;
+}
+
+/*
+ * Takes a file position and gives back a brun who's starting block
+ * is block number fblock of the file.
+ *
+ * Returns BEFS_OK or BEFS_ERR.
+ *
+ * Calls specialized functions for each of the three possible
+ * datastream regions.
+ *
+ * 2001-11-15 Will Dyson
+ */
+int
+befs_fblock2brun(struct super_block *sb, befs_data_stream * data,
+ befs_blocknr_t fblock, befs_block_run * run)
+{
+ int err;
+ befs_off_t pos = fblock << BEFS_SB(sb)->block_shift;
+
+ if (pos < data->max_direct_range) {
+ err = befs_find_brun_direct(sb, data, fblock, run);
+
+ } else if (pos < data->max_indirect_range) {
+ err = befs_find_brun_indirect(sb, data, fblock, run);
+
+ } else if (pos < data->max_double_indirect_range) {
+ err = befs_find_brun_dblindirect(sb, data, fblock, run);
+
+ } else {
+ befs_error(sb,
+ "befs_fblock2brun() was asked to find block %lu, "
+ "which is not mapped by the datastream\n", fblock);
+ err = BEFS_ERR;
+ }
+ return err;
+}
+
+/**
+ * befs_read_lsmylink - read long symlink from datastream.
+ * @sb: Filesystem superblock
+ * @ds: Datastrem to read from
+ * @buf: Buffer in wich to place long symlink data
+ * @len: Length of the long symlink in bytes
+ *
+ * Returns the number of bytes read
+ */
+size_t
+befs_read_lsymlink(struct super_block * sb, befs_data_stream * ds, void *buff,
+ befs_off_t len)
+{
+ befs_off_t bytes_read = 0; /* bytes readed */
+ u16 plen;
+ struct buffer_head *bh = NULL;
+ befs_debug(sb, "---> befs_read_lsymlink() length: %Lu", len);
+
+ while (bytes_read < len) {
+ bh = befs_read_datastream(sb, ds, bytes_read, NULL);
+ if (!bh) {
+ befs_error(sb, "BeFS: Error reading datastream block "
+ "starting from %Lu", bytes_read);
+ befs_debug(sb, "<--- befs_read_lsymlink() ERROR");
+ return bytes_read;
+
+ }
+ plen = ((bytes_read + BEFS_SB(sb)->block_size) < len) ?
+ BEFS_SB(sb)->block_size : len - bytes_read;
+ memcpy(buff + bytes_read, bh->b_data, plen);
+ brelse(bh);
+ bytes_read += plen;
+ }
+
+ befs_debug(sb, "<--- befs_read_lsymlink() read %u bytes", bytes_read);
+ return bytes_read;
+}
+
+/**
+ * befs_count_blocks - blocks used by a file
+ * @sb: Filesystem superblock
+ * @ds: Datastream of the file
+ *
+ * Counts the number of fs blocks that the file represented by
+ * inode occupies on the filesystem, counting both regular file
+ * data and filesystem metadata (and eventually attribute data
+ * when we support attributes)
+*/
+
+befs_blocknr_t
+befs_count_blocks(struct super_block * sb, befs_data_stream * ds)
+{
+ befs_blocknr_t blocks;
+ befs_blocknr_t datablocks; /* File data blocks */
+ befs_blocknr_t metablocks; /* FS metadata blocks */
+ befs_sb_info *befs_sb = BEFS_SB(sb);
+
+ befs_debug(sb, "---> befs_count_blocks()");
+
+ datablocks = ds->size >> befs_sb->block_shift;
+ if (ds->size & (befs_sb->block_size - 1))
+ datablocks += 1;
+
+ metablocks = 1; /* Start with 1 block for inode */
+
+ /* Size of indirect block */
+ if (ds->size > ds->max_direct_range)
+ metablocks += ds->indirect.len;
+
+ /*
+ Double indir block, plus all the indirect blocks it mapps
+ In the double-indirect range, all block runs of data are
+ BEFS_DBLINDIR_BRUN_LEN blocks long. Therefore, we know
+ how many data block runs are in the double-indirect region,
+ and from that we know how many indirect blocks it takes to
+ map them. We assume that the indirect blocks are also
+ BEFS_DBLINDIR_BRUN_LEN blocks long.
+ */
+ if (ds->size > ds->max_indirect_range && ds->max_indirect_range != 0) {
+ uint dbl_bytes;
+ uint dbl_bruns;
+ uint indirblocks;
+
+ dbl_bytes =
+ ds->max_double_indirect_range - ds->max_indirect_range;
+ dbl_bruns =
+ dbl_bytes / (befs_sb->block_size * BEFS_DBLINDIR_BRUN_LEN);
+ indirblocks = dbl_bruns / befs_iaddrs_per_block(sb);
+
+ metablocks += ds->double_indirect.len;
+ metablocks += indirblocks;
+ }
+
+ blocks = datablocks + metablocks;
+ befs_debug(sb, "<--- befs_count_blocks() %u blocks", blocks);
+
+ return blocks;
+}
+
+/*
+ Finds the block run that starts at file block number blockno
+ in the file represented by the datastream data, if that
+ blockno is in the direct region of the datastream.
+
+ sb: the superblock
+ data: the datastream
+ blockno: the blocknumber to find
+ run: The found run is passed back through this pointer
+
+ Return value is BEFS_OK if the blockrun is found, BEFS_ERR
+ otherwise.
+
+ Algorithm:
+ Linear search. Checks each element of array[] to see if it
+ contains the blockno-th filesystem block. This is nessisary
+ because the block runs map variable amounts of data. Simply
+ keeps a count of the number of blocks searched so far (sum),
+ incrementing this by the length of each block run as we come
+ across it. Adds sum to *count before returning (this is so
+ you can search multiple arrays that are logicaly one array,
+ as in the indirect region code).
+
+ When/if blockno is found, if blockno is inside of a block
+ run as stored on disk, we offset the start and lenght members
+ of the block run, so that blockno is the start and len is
+ still valid (the run ends in the same place).
+
+ 2001-11-15 Will Dyson
+*/
+static int
+befs_find_brun_direct(struct super_block *sb, befs_data_stream * data,
+ befs_blocknr_t blockno, befs_block_run * run)
+{
+ int i;
+ befs_block_run *array = data->direct;
+ befs_blocknr_t sum;
+ befs_blocknr_t max_block =
+ data->max_direct_range >> BEFS_SB(sb)->block_shift;
+
+ befs_debug(sb, "---> befs_find_brun_direct(), find %lu", blockno);
+
+ if (blockno > max_block) {
+ befs_error(sb, "befs_find_brun_direct() passed block outside of"
+ "direct region");
+ return BEFS_ERR;
+ }
+
+ for (i = 0, sum = 0; i < BEFS_NUM_DIRECT_BLOCKS;
+ sum += array[i].len, i++) {
+ if (blockno >= sum && blockno < sum + (array[i].len)) {
+ int offset = blockno - sum;
+ run->allocation_group = array[i].allocation_group;
+ run->start = array[i].start + offset;
+ run->len = array[i].len - offset;
+
+ befs_debug(sb, "---> befs_find_brun_direct(), "
+ "found %lu at direct[%d]", blockno, i);
+ return BEFS_OK;
+ }
+ }
+
+ befs_debug(sb, "---> befs_find_brun_direct() ERROR");
+ return BEFS_ERR;
+}
+
+/*
+ Finds the block run that starts at file block number blockno
+ in the file represented by the datastream data, if that
+ blockno is in the indirect region of the datastream.
+
+ sb: the superblock
+ data: the datastream
+ blockno: the blocknumber to find
+ run: The found run is passed back through this pointer
+
+ Return value is BEFS_OK if the blockrun is found, BEFS_ERR
+ otherwise.
+
+ Algorithm:
+ For each block in the indirect run of the datastream, read
+ it in and search through it for search_blk.
+
+ XXX:
+ Really should check to make sure blockno is inside indirect
+ region.
+
+ 2001-11-15 Will Dyson
+*/
+static int
+befs_find_brun_indirect(struct super_block *sb,
+ befs_data_stream * data, befs_blocknr_t blockno,
+ befs_block_run * run)
+{
+ int i, j;
+ befs_blocknr_t sum = 0;
+ befs_blocknr_t indir_start_blk;
+ befs_blocknr_t search_blk;
+ struct buffer_head *indirblock;
+ befs_block_run *array;
+
+ befs_block_run indirect = data->indirect;
+ befs_blocknr_t indirblockno = iaddr2blockno(sb, &indirect);
+ int arraylen = befs_iaddrs_per_block(sb);
+
+ befs_debug(sb, "---> befs_find_brun_indirect(), find %lu", blockno);
+
+ indir_start_blk = data->max_direct_range >> BEFS_SB(sb)->block_shift;
+ search_blk = blockno - indir_start_blk;
+
+ /* Examine blocks of the indirect run one at a time */
+ for (i = 0; i < indirect.len; i++) {
+ indirblock = befs_bread(sb, indirblockno + i);
+ if (indirblock == NULL) {
+ befs_debug(sb,
+ "---> befs_find_brun_indirect() failed to "
+ "read disk block %lu from the indirect brun",
+ indirblockno + i);
+ return BEFS_ERR;
+ }
+
+ array = (befs_block_run *) indirblock->b_data;
+
+ for (j = 0; j < arraylen; ++j) {
+ int len = fs16_to_cpu(sb, array[j].len);
+
+ if (search_blk >= sum && search_blk < sum + len) {
+ int offset = search_blk - sum;
+ run->allocation_group =
+ fs32_to_cpu(sb, array[j].allocation_group);
+ run->start =
+ fs16_to_cpu(sb, array[j].start) + offset;
+ run->len =
+ fs16_to_cpu(sb, array[j].len) - offset;
+
+ brelse(indirblock);
+ befs_debug(sb,
+ "<--- befs_find_brun_indirect() found "
+ "file block %lu at indirect[%d]",
+ blockno, j + (i * arraylen));
+ return BEFS_OK;
+ }
+ sum += len;
+ }
+
+ brelse(indirblock);
+ }
+
+ /* Only fallthrough is an error */
+ befs_error(sb, "BeFS: befs_find_brun_indirect() failed to find "
+ "file block %lu", blockno);
+
+ befs_debug(sb, "<--- befs_find_brun_indirect() ERROR");
+ return BEFS_ERR;
+}
+
+/*
+ Finds the block run that starts at file block number blockno
+ in the file represented by the datastream data, if that
+ blockno is in the double-indirect region of the datastream.
+
+ sb: the superblock
+ data: the datastream
+ blockno: the blocknumber to find
+ run: The found run is passed back through this pointer
+
+ Return value is BEFS_OK if the blockrun is found, BEFS_ERR
+ otherwise.
+
+ Algorithm:
+ The block runs in the double-indirect region are different.
+ They are always allocated 4 fs blocks at a time, so each
+ block run maps a constant amount of file data. This means
+ that we can directly calculate how many block runs into the
+ double-indirect region we need to go to get to the one that
+ maps a particular filesystem block.
+
+ We do this in two stages. First we calculate which of the
+ inode addresses in the double-indirect block will point us
+ to the indirect block that contains the mapping for the data,
+ then we calculate which of the inode addresses in that
+ indirect block maps the data block we are after.
+
+ Oh, and once we've done that, we actually read in the blocks
+ that contain the inode addresses we calculated above. Even
+ though the double-indirect run may be several blocks long,
+ we can calculate which of those blocks will contain the index
+ we are after and only read that one. We then follow it to
+ the indirect block and perform a similar process to find
+ the actual block run that maps the data block we are interested
+ in.
+
+ Then we offset the run as in befs_find_brun_array() and we are
+ done.
+
+ 2001-11-15 Will Dyson
+*/
+static int
+befs_find_brun_dblindirect(struct super_block *sb,
+ befs_data_stream * data, befs_blocknr_t blockno,
+ befs_block_run * run)
+{
+ int dblindir_indx;
+ int indir_indx;
+ int offset;
+ int dbl_which_block;
+ int which_block;
+ int dbl_block_indx;
+ int block_indx;
+ off_t dblindir_leftover;
+ befs_blocknr_t blockno_at_run_start;
+ struct buffer_head *dbl_indir_block;
+ struct buffer_head *indir_block;
+ befs_block_run indir_run;
+ befs_inode_addr *iaddr_array = NULL;
+ befs_sb_info *befs_sb = BEFS_SB(sb);
+
+ befs_blocknr_t indir_start_blk =
+ data->max_indirect_range >> befs_sb->block_shift;
+
+ off_t dbl_indir_off = blockno - indir_start_blk;
+
+ /* number of data blocks mapped by each of the iaddrs in
+ * the indirect block pointed to by the double indirect block
+ */
+ size_t iblklen = BEFS_DBLINDIR_BRUN_LEN;
+
+ /* number of data blocks mapped by each of the iaddrs in
+ * the double indirect block
+ */
+ size_t diblklen = iblklen * befs_iaddrs_per_block(sb)
+ * BEFS_DBLINDIR_BRUN_LEN;
+
+ befs_debug(sb, "---> befs_find_brun_dblindirect() find %lu", blockno);
+
+ /* First, discover which of the double_indir->indir blocks
+ * contains pos. Then figure out how much of pos that
+ * accounted for. Then discover which of the iaddrs in
+ * the indirect block contains pos.
+ */
+
+ dblindir_indx = dbl_indir_off / diblklen;
+ dblindir_leftover = dbl_indir_off % diblklen;
+ indir_indx = dblindir_leftover / diblklen;
+
+ /* Read double indirect block */
+ dbl_which_block = dblindir_indx / befs_iaddrs_per_block(sb);
+ if (dbl_which_block > data->double_indirect.len) {
+ befs_error(sb, "The double-indirect index calculated by "
+ "befs_read_brun_dblindirect(), %d, is outside the range "
+ "of the double-indirect block", dblindir_indx);
+ return BEFS_ERR;
+ }
+
+ dbl_indir_block = befs_bread(sb,
+ iaddr2blockno(sb, &data->double_indirect) +
+ dbl_which_block);
+ if (dbl_indir_block == NULL) {
+ befs_error(sb, "befs_read_brun_dblindirect() couldn't read the "
+ "double-indirect block at blockno %lu",
+ iaddr2blockno(sb,
+ &data->double_indirect) +
+ dbl_which_block);
+ brelse(dbl_indir_block);
+ return BEFS_ERR;
+ }
+
+ dbl_block_indx =
+ dblindir_indx - (dbl_which_block * befs_iaddrs_per_block(sb));
+ iaddr_array = (befs_inode_addr *) dbl_indir_block->b_data;
+ indir_run = fsrun_to_cpu(sb, iaddr_array[dbl_block_indx]);
+ brelse(dbl_indir_block);
+ iaddr_array = NULL;
+
+ /* Read indirect block */
+ which_block = indir_indx / befs_iaddrs_per_block(sb);
+ if (which_block > indir_run.len) {
+ befs_error(sb, "The indirect index calculated by "
+ "befs_read_brun_dblindirect(), %d, is outside the range "
+ "of the indirect block", indir_indx);
+ return BEFS_ERR;
+ }
+
+ indir_block =
+ befs_bread(sb, iaddr2blockno(sb, &indir_run) + which_block);
+ if (indir_block == NULL) {
+ befs_error(sb, "befs_read_brun_dblindirect() couldn't read the "
+ "indirect block at blockno %lu",
+ iaddr2blockno(sb, &indir_run) + which_block);
+ brelse(indir_block);
+ return BEFS_ERR;
+ }
+
+ block_indx = indir_indx - (which_block * befs_iaddrs_per_block(sb));
+ iaddr_array = (befs_inode_addr *) indir_block->b_data;
+ *run = fsrun_to_cpu(sb, iaddr_array[block_indx]);
+ brelse(indir_block);
+ iaddr_array = NULL;
+
+ blockno_at_run_start = indir_start_blk;
+ blockno_at_run_start += diblklen * dblindir_indx;
+ blockno_at_run_start += iblklen * indir_indx;
+ offset = blockno - blockno_at_run_start;
+
+ run->start += offset;
+ run->len -= offset;
+
+ befs_debug(sb, "Found file block %lu in double_indirect[%d][%d],"
+ " double_indirect_leftover = %lu",
+ blockno, dblindir_indx, indir_indx, dblindir_leftover);
+
+ return BEFS_OK;
+}
diff --git a/fs/befs/datastream.h b/fs/befs/datastream.h
new file mode 100644
index 00000000000000..20612d04390609
--- /dev/null
+++ b/fs/befs/datastream.h
@@ -0,0 +1,18 @@
+/*
+ * datastream.h
+ *
+ */
+
+struct buffer_head *befs_read_datastream(struct super_block *sb,
+ befs_data_stream * ds, befs_off_t pos,
+ uint * off);
+
+int befs_fblock2brun(struct super_block *sb, befs_data_stream * data,
+ befs_blocknr_t fblock, befs_block_run * run);
+
+size_t befs_read_lsymlink(struct super_block *sb, befs_data_stream * data,
+ void *buff, befs_off_t len);
+
+befs_blocknr_t befs_count_blocks(struct super_block *sb, befs_data_stream * ds);
+
+extern const befs_inode_addr BAD_IADDR;
diff --git a/fs/befs/debug.c b/fs/befs/debug.c
new file mode 100644
index 00000000000000..e54bca818af240
--- /dev/null
+++ b/fs/befs/debug.c
@@ -0,0 +1,263 @@
+/*
+ * linux/fs/befs/debug.c
+ *
+ * Copyright (C) 2001 Will Dyson (will_dyson at pobox.com)
+ *
+ * With help from the ntfs-tng driver by Anton Altparmakov
+ *
+ * Copyright (C) 1999 Makoto Kato (m_kato@ga2.so-net.ne.jp)
+ *
+ * debug functions
+ */
+
+#ifdef __KERNEL__
+
+#include <stdarg.h>
+#include <linux/string.h>
+#include <linux/spinlock.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+
+#endif /* __KERNEL__ */
+
+#include "befs.h"
+#include "endian.h"
+
+#define ERRBUFSIZE 1024
+
+void
+befs_error(const struct super_block *sb, const char *fmt, ...)
+{
+ va_list args;
+ char err_buf[ERRBUFSIZE];
+
+ va_start(args, fmt);
+ vsnprintf(err_buf, ERRBUFSIZE, fmt, args);
+ va_end(args);
+
+ printk(KERN_ERR "BeFS(%s): %s\n", bdevname(sb->s_dev), err_buf);
+
+ befs_debug(sb, err_buf);
+}
+
+void
+befs_warning(const struct super_block *sb, const char *fmt, ...)
+{
+ va_list args;
+ char err_buf[ERRBUFSIZE];
+
+ va_start(args, fmt);
+ vsnprintf(err_buf, ERRBUFSIZE, fmt, args);
+ va_end(args);
+
+ printk(KERN_WARNING "BeFS(%s): %s\n", bdevname(sb->s_dev), err_buf);
+
+ befs_debug(sb, err_buf);
+}
+
+void
+befs_debug(const struct super_block *sb, const char *fmt, ...)
+{
+#ifdef CONFIG_BEFS_DEBUG
+
+ va_list args;
+ char err_buf[ERRBUFSIZE];
+
+ if (BEFS_SB(sb)->mount_opts.debug) {
+ va_start(args, fmt);
+ vsnprintf(err_buf, ERRBUFSIZE, fmt, args);
+ va_end(args);
+
+ printk(KERN_DEBUG "BeFS(%s): %s\n",
+ bdevname(sb->s_dev), err_buf);
+ }
+#endif //CONFIG_BEFS_DEBUG
+}
+
+void
+befs_dump_inode(const struct super_block *sb, befs_inode * inode)
+{
+#ifdef CONFIG_BEFS_DEBUG
+
+ befs_block_run tmp_run;
+
+ befs_debug(sb, "befs_inode infomation");
+
+ befs_debug(sb, " magic1 %08x", fs32_to_cpu(sb, inode->magic1));
+
+ tmp_run = fsrun_to_cpu(sb, inode->inode_num);
+ befs_debug(sb, " inode_num %u, %hu, %hu",
+ tmp_run.allocation_group, tmp_run.start, tmp_run.len);
+
+ befs_debug(sb, " uid %u", fs32_to_cpu(sb, inode->uid));
+ befs_debug(sb, " gid %u", fs32_to_cpu(sb, inode->gid));
+ befs_debug(sb, " mode %08x", fs32_to_cpu(sb, inode->mode));
+ befs_debug(sb, " flags %08x", fs32_to_cpu(sb, inode->flags));
+ befs_debug(sb, " create_time %Lu",
+ fs64_to_cpu(sb, inode->create_time));
+ befs_debug(sb, " last_modified_time %Lu",
+ fs64_to_cpu(sb, inode->last_modified_time));
+
+ tmp_run = fsrun_to_cpu(sb, inode->parent);
+ befs_debug(sb, " parent [%u, %hu, %hu]",
+ tmp_run.allocation_group, tmp_run.start, tmp_run.len);
+
+ tmp_run = fsrun_to_cpu(sb, inode->attributes);
+ befs_debug(sb, " attributes [%u, %hu, %hu]",
+ tmp_run.allocation_group, tmp_run.start, tmp_run.len);
+
+ befs_debug(sb, " type %08x", fs32_to_cpu(sb, inode->type));
+ befs_debug(sb, " inode_size %u", fs32_to_cpu(sb, inode->inode_size));
+
+ if (S_ISLNK(inode->mode)) {
+ befs_debug(sb, " Symbolic link [%s]", inode->data.symlink);
+ } else {
+ int i;
+
+ for (i = 0; i < BEFS_NUM_DIRECT_BLOCKS; i++) {
+ tmp_run =
+ fsrun_to_cpu(sb, inode->data.datastream.direct[i]);
+ befs_debug(sb, " direct %d [%u, %hu, %hu]", i,
+ tmp_run.allocation_group, tmp_run.start,
+ tmp_run.len);
+ }
+ befs_debug(sb, " max_direct_range %Lu",
+ fs64_to_cpu(sb,
+ inode->data.datastream.
+ max_direct_range));
+
+ tmp_run = fsrun_to_cpu(sb, inode->data.datastream.indirect);
+ befs_debug(sb, " indirect [%u, %hu, %hu]",
+ tmp_run.allocation_group,
+ tmp_run.start, tmp_run.len);
+
+ befs_debug(sb, " max_indirect_range %Lu",
+ fs64_to_cpu(sb,
+ inode->data.datastream.
+ max_indirect_range));
+
+ tmp_run =
+ fsrun_to_cpu(sb, inode->data.datastream.double_indirect);
+ befs_debug(sb, " double indirect [%u, %hu, %hu]",
+ tmp_run.allocation_group, tmp_run.start,
+ tmp_run.len);
+
+ befs_debug(sb, " max_double_indirect_range %Lu",
+ fs64_to_cpu(sb,
+ inode->data.datastream.
+ max_double_indirect_range));
+
+ befs_debug(sb, " size %Lu",
+ fs64_to_cpu(sb, inode->data.datastream.size));
+ }
+
+#endif //CONFIG_BEFS_DEBUG
+}
+
+/*
+ * Display super block structure for debug.
+ */
+
+void
+befs_dump_super_block(const struct super_block *sb, befs_super_block * sup)
+{
+#ifdef CONFIG_BEFS_DEBUG
+
+ befs_block_run tmp_run;
+
+ befs_debug(sb, "befs_super_block information");
+
+ befs_debug(sb, " name %s", sup->name);
+ befs_debug(sb, " magic1 %08x", fs32_to_cpu(sb, sup->magic1));
+ befs_debug(sb, " fs_byte_order %08x",
+ fs32_to_cpu(sb, sup->fs_byte_order));
+
+ befs_debug(sb, " block_size %u", fs32_to_cpu(sb, sup->block_size));
+ befs_debug(sb, " block_shift %u", fs32_to_cpu(sb, sup->block_shift));
+
+ befs_debug(sb, " num_blocks %Lu", fs64_to_cpu(sb, sup->num_blocks));
+ befs_debug(sb, " used_blocks %Lu", fs64_to_cpu(sb, sup->used_blocks));
+
+ befs_debug(sb, " magic2 %08x", fs32_to_cpu(sb, sup->magic2));
+ befs_debug(sb, " blocks_per_ag %u",
+ fs32_to_cpu(sb, sup->blocks_per_ag));
+ befs_debug(sb, " ag_shift %u", fs32_to_cpu(sb, sup->ag_shift));
+ befs_debug(sb, " num_ags %u", fs32_to_cpu(sb, sup->num_ags));
+
+ befs_debug(sb, " flags %08x", fs32_to_cpu(sb, sup->flags));
+
+ tmp_run = fsrun_to_cpu(sb, sup->log_blocks);
+ befs_debug(sb, " log_blocks %u, %hu, %hu",
+ tmp_run.allocation_group, tmp_run.start, tmp_run.len);
+
+ befs_debug(sb, " log_start %Ld", fs64_to_cpu(sb, sup->log_start));
+ befs_debug(sb, " log_end %Ld", fs64_to_cpu(sb, sup->log_end));
+
+ befs_debug(sb, " magic3 %08x", fs32_to_cpu(sb, sup->magic3));
+
+ tmp_run = fsrun_to_cpu(sb, sup->root_dir);
+ befs_debug(sb, " root_dir %u, %hu, %hu",
+ tmp_run.allocation_group, tmp_run.start, tmp_run.len);
+
+ tmp_run = fsrun_to_cpu(sb, sup->indices);
+ befs_debug(sb, " indices %u, %hu, %hu",
+ tmp_run.allocation_group, tmp_run.start, tmp_run.len);
+
+#endif //CONFIG_BEFS_DEBUG
+}
+
+void
+befs_dump_small_data(const struct super_block *sb, befs_small_data * sd)
+{
+}
+
+void
+befs_dump_run(const struct super_block *sb, befs_block_run run)
+{
+#ifdef CONFIG_BEFS_DEBUG
+
+ run = fsrun_to_cpu(sb, run);
+
+ befs_debug(sb, "[%u, %hu, %hu]",
+ run.allocation_group, run.start, run.len);
+
+#endif //CONFIG_BEFS_DEBUG
+}
+
+void
+befs_dump_index_entry(const struct super_block *sb, befs_btree_super * super)
+{
+#ifdef CONFIG_BEFS_DEBUG
+
+ befs_debug(sb, "Btree super structure");
+ befs_debug(sb, " magic %08x", fs32_to_cpu(sb, super->magic));
+ befs_debug(sb, " node_size %u", fs32_to_cpu(sb, super->node_size));
+ befs_debug(sb, " max_depth %08x", fs32_to_cpu(sb, super->max_depth));
+
+ befs_debug(sb, " data_type %08x", fs32_to_cpu(sb, super->data_type));
+ befs_debug(sb, " root_node_pointer %016LX",
+ fs64_to_cpu(sb, super->root_node_ptr));
+ befs_debug(sb, " free_node_pointer %016LX",
+ fs64_to_cpu(sb, super->free_node_ptr));
+ befs_debug(sb, " maximum size %016LX",
+ fs64_to_cpu(sb, super->max_size));
+
+#endif //CONFIG_BEFS_DEBUG
+}
+
+void
+befs_dump_index_node(const struct super_block *sb, befs_btree_nodehead * node)
+{
+#ifdef CONFIG_BEFS_DEBUG
+
+ befs_debug(sb, "Btree node structure");
+ befs_debug(sb, " left %016LX", fs64_to_cpu(sb, node->left));
+ befs_debug(sb, " right %016LX", fs64_to_cpu(sb, node->right));
+ befs_debug(sb, " overflow %016LX", fs64_to_cpu(sb, node->overflow));
+ befs_debug(sb, " all_key_count %hu",
+ fs16_to_cpu(sb, node->all_key_count));
+ befs_debug(sb, " all_key_length %hu",
+ fs16_to_cpu(sb, node->all_key_length));
+
+#endif //CONFIG_BEFS_DEBUG
+}
diff --git a/fs/befs/endian.h b/fs/befs/endian.h
new file mode 100644
index 00000000000000..d77a26e7bd971b
--- /dev/null
+++ b/fs/befs/endian.h
@@ -0,0 +1,125 @@
+/*
+ * linux/fs/befs/endian.h
+ *
+ * Copyright (C) 2001 Will Dyson <will_dyson@pobox.com>
+ *
+ * Partially based on similar funtions in the sysv driver.
+ */
+
+#ifndef LINUX_BEFS_ENDIAN
+#define LINUX_BEFS_ENDIAN
+
+#include <linux/byteorder/generic.h>
+
+static inline u64
+fs64_to_cpu(const struct super_block *sb, u64 n)
+{
+ if (BEFS_SB(sb)->byte_order == BEFS_BYTESEX_LE)
+ return le64_to_cpu(n);
+ else
+ return be64_to_cpu(n);
+}
+
+static inline u64
+cpu_to_fs64(const struct super_block *sb, u64 n)
+{
+ if (BEFS_SB(sb)->byte_order == BEFS_BYTESEX_LE)
+ return cpu_to_le64(n);
+ else
+ return cpu_to_be64(n);
+}
+
+static inline u32
+fs32_to_cpu(const struct super_block *sb, u32 n)
+{
+ if (BEFS_SB(sb)->byte_order == BEFS_BYTESEX_LE)
+ return le32_to_cpu(n);
+ else
+ return be32_to_cpu(n);
+}
+
+static inline u32
+cpu_to_fs32(const struct super_block *sb, u32 n)
+{
+ if (BEFS_SB(sb)->byte_order == BEFS_BYTESEX_LE)
+ return cpu_to_le32(n);
+ else
+ return cpu_to_be32(n);
+}
+
+static inline u16
+fs16_to_cpu(const struct super_block *sb, u16 n)
+{
+ if (BEFS_SB(sb)->byte_order == BEFS_BYTESEX_LE)
+ return le16_to_cpu(n);
+ else
+ return be16_to_cpu(n);
+}
+
+static inline u16
+cpu_to_fs16(const struct super_block *sb, u16 n)
+{
+ if (BEFS_SB(sb)->byte_order == BEFS_BYTESEX_LE)
+ return cpu_to_le16(n);
+ else
+ return cpu_to_be16(n);
+}
+
+/* Composite types below here */
+
+static inline befs_block_run
+fsrun_to_cpu(const struct super_block *sb, befs_block_run n)
+{
+ befs_block_run run;
+
+ if (BEFS_SB(sb)->byte_order == BEFS_BYTESEX_LE) {
+ run.allocation_group = le32_to_cpu(n.allocation_group);
+ run.start = le16_to_cpu(n.start);
+ run.len = le16_to_cpu(n.len);
+ } else {
+ run.allocation_group = be32_to_cpu(n.allocation_group);
+ run.start = be16_to_cpu(n.start);
+ run.len = be16_to_cpu(n.len);
+ }
+ return run;
+}
+
+static inline befs_block_run
+cpu_to_fsrun(const struct super_block *sb, befs_block_run n)
+{
+ befs_block_run run;
+
+ if (BEFS_SB(sb)->byte_order == BEFS_BYTESEX_LE) {
+ run.allocation_group = cpu_to_le32(n.allocation_group);
+ run.start = cpu_to_le16(n.start);
+ run.len = cpu_to_le16(n.len);
+ } else {
+ run.allocation_group = cpu_to_be32(n.allocation_group);
+ run.start = cpu_to_be16(n.start);
+ run.len = cpu_to_be16(n.len);
+ }
+ return run;
+}
+
+static inline befs_data_stream
+fsds_to_cpu(const struct super_block *sb, befs_data_stream n)
+{
+ befs_data_stream data;
+ int i;
+
+ for (i = 0; i < BEFS_NUM_DIRECT_BLOCKS; ++i)
+ data.direct[i] = fsrun_to_cpu(sb, n.direct[i]);
+
+ data.max_direct_range = fs64_to_cpu(sb, n.max_direct_range);
+ data.indirect = fsrun_to_cpu(sb, n.indirect);
+ data.max_indirect_range = fs64_to_cpu(sb, n.max_indirect_range);
+ data.double_indirect = fsrun_to_cpu(sb, n.double_indirect);
+ data.max_double_indirect_range = fs64_to_cpu(sb,
+ n.
+ max_double_indirect_range);
+ data.size = fs64_to_cpu(sb, n.size);
+
+ return data;
+}
+
+#endif //LINUX_BEFS_ENDIAN
diff --git a/fs/befs/inode.c b/fs/befs/inode.c
new file mode 100644
index 00000000000000..d41c9247ae8af6
--- /dev/null
+++ b/fs/befs/inode.c
@@ -0,0 +1,53 @@
+/*
+ * inode.c
+ *
+ * Copyright (C) 2001 Will Dyson <will_dyson@pobox.com>
+ */
+
+#include <linux/fs.h>
+
+#include "befs.h"
+#include "inode.h"
+#include "endian.h"
+
+/*
+ Validates the correctness of the befs inode
+ Returns BEFS_OK if the inode should be used, otherwise
+ returns BEFS_BAD_INODE
+*/
+int
+befs_check_inode(struct super_block *sb, befs_inode * raw_inode,
+ befs_blocknr_t inode)
+{
+ u32 magic1 = fs32_to_cpu(sb, raw_inode->magic1);
+ befs_inode_addr ino_num = fsrun_to_cpu(sb, raw_inode->inode_num);
+ u32 flags = fs32_to_cpu(sb, raw_inode->flags);
+
+ /* check magic header. */
+ if (magic1 != BEFS_INODE_MAGIC1) {
+ befs_error(sb,
+ "Inode has a bad magic header - inode = %lu", inode);
+ return BEFS_BAD_INODE;
+ }
+
+ /*
+ * Sanity check2: inodes store their own block address. Check it.
+ */
+ if (inode != iaddr2blockno(sb, &ino_num)) {
+ befs_error(sb, "inode blocknr field disagrees with vfs "
+ "VFS: %lu, Inode %lu",
+ inode, iaddr2blockno(sb, &ino_num));
+ return BEFS_BAD_INODE;
+ }
+
+ /*
+ * check flag
+ */
+
+ if (!(flags & BEFS_INODE_IN_USE)) {
+ befs_error(sb, "inode is not used - inode = %lu", inode);
+ return BEFS_BAD_INODE;
+ }
+
+ return BEFS_OK;
+}
diff --git a/fs/befs/inode.h b/fs/befs/inode.h
new file mode 100644
index 00000000000000..c3a7738dcbff1d
--- /dev/null
+++ b/fs/befs/inode.h
@@ -0,0 +1,7 @@
+/*
+ * inode.h
+ *
+ */
+
+int befs_check_inode(struct super_block *sb, befs_inode * raw_inode,
+ befs_blocknr_t inode);
diff --git a/fs/befs/io.c b/fs/befs/io.c
new file mode 100644
index 00000000000000..24dee9bb4d0492
--- /dev/null
+++ b/fs/befs/io.c
@@ -0,0 +1,99 @@
+/*
+ * linux/fs/befs/io.c
+ *
+ * Copyright (C) 2001 Will Dyson <will_dyson@pobox.com
+ *
+ * Based on portions of file.c and inode.c
+ * by Makoto Kato (m_kato@ga2.so-net.ne.jp)
+ *
+ * Many thanks to Dominic Giampaolo, author of Practical File System
+ * Design with the Be File System, for such a helpful book.
+ *
+ */
+
+#include <linux/fs.h>
+
+#include "befs.h"
+#include "io.h"
+
+/*
+ * Converts befs notion of disk addr to a disk offset and uses
+ * linux kernel function bread() to get the buffer containing
+ * the offset. -Will Dyson
+ *
+ */
+
+struct buffer_head *
+befs_bread_iaddr(struct super_block *sb, befs_inode_addr iaddr)
+{
+ struct buffer_head *bh = NULL;
+ befs_blocknr_t block = 0;
+ vfs_blocknr_t vfs_block = 0;
+ befs_sb_info *befs_sb = BEFS_SB(sb);
+
+ befs_debug(sb, "---> Enter befs_read_iaddr() "
+ "[%u, %hu, %hu]",
+ iaddr.allocation_group, iaddr.start, iaddr.len);
+
+ if (iaddr.allocation_group > befs_sb->num_ags) {
+ befs_error(sb, "BEFS: Invalid allocation group %u, max is %u",
+ iaddr.allocation_group, befs_sb->num_ags);
+ goto error;
+ }
+
+ block = iaddr2blockno(sb, &iaddr);
+ vfs_block = (vfs_blocknr_t) block;
+
+ if (vfs_block != block) {
+ befs_error(sb, "Error converting to host blocknr_t. %Lu "
+ "is larger than the host can use", block);
+ goto error;
+ }
+
+ befs_debug(sb, "befs_read_iaddr: offset = %lu", block);
+
+ bh = bread(sb->s_dev, vfs_block, befs_sb->block_size);
+
+ if (bh == NULL) {
+ befs_error(sb, "Failed to read block %lu", block);
+ goto error;
+ }
+
+ befs_debug(sb, "<--- befs_read_iaddr()");
+ return bh;
+
+ error:
+ befs_debug(sb, "<--- befs_read_iaddr() ERROR");
+ return NULL;
+}
+
+struct buffer_head *
+befs_bread(struct super_block *sb, befs_blocknr_t block)
+{
+ struct buffer_head *bh = NULL;
+ befs_sb_info *befs_sb = BEFS_SB(sb);
+ vfs_blocknr_t vfs_block = (vfs_blocknr_t) block;
+
+ befs_debug(sb, "---> Enter befs_read() %Lu", block);
+
+ if (vfs_block != block) {
+ befs_error(sb, "Error converting to host blocknr_t. %Lu "
+ "is larger than the host can use", block);
+ goto error;
+ }
+
+ bh = bread(sb->s_dev, vfs_block, befs_sb->block_size);
+
+ if (bh == NULL) {
+ befs_error(sb, "Failed to read block %lu", vfs_block);
+ goto error;
+ }
+
+ befs_debug(sb, "<--- befs_read()");
+
+ return bh;
+
+ error:
+ befs_debug(sb, "<--- befs_read() ERROR");
+ return NULL;
+}
diff --git a/fs/befs/io.h b/fs/befs/io.h
new file mode 100644
index 00000000000000..f06ce866a34045
--- /dev/null
+++ b/fs/befs/io.h
@@ -0,0 +1,8 @@
+/*
+ * io.h
+ */
+
+struct buffer_head *befs_bread_iaddr(struct super_block *sb,
+ befs_inode_addr iaddr);
+
+struct buffer_head *befs_bread(struct super_block *sb, befs_blocknr_t block);
diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c
new file mode 100644
index 00000000000000..96c13b269e95df
--- /dev/null
+++ b/fs/befs/linuxvfs.c
@@ -0,0 +1,965 @@
+/*
+ * linux/fs/befs/linuxvfs.c
+ *
+ * Copyright (C) 2001 Will Dyson <will_dyson@pobox.com
+ *
+ */
+
+#include <linux/blkdev.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+
+#include "befs.h"
+#include "btree.h"
+#include "inode.h"
+#include "datastream.h"
+#include "super.h"
+#include "io.h"
+#include "endian.h"
+
+EXPORT_NO_SYMBOLS;
+MODULE_DESCRIPTION("BeOS File System (BeFS) driver");
+MODULE_AUTHOR("Will Dyson");
+MODULE_LICENSE("GPL");
+
+/* The units the vfs expects inode->i_blocks to be in */
+#define VFS_BLOCK_SIZE 512
+
+static int befs_readdir(struct file *, void *, filldir_t);
+static int befs_get_block(struct inode *, long, struct buffer_head *, int);
+static int befs_readpage(struct file *file, struct page *page);
+static int befs_bmap(struct address_space *mapping, long block);
+static struct dentry *befs_lookup(struct inode *, struct dentry *);
+static void befs_read_inode(struct inode *ino);
+static void befs_clear_inode(struct inode *ino);
+static int befs_init_inodecache(void);
+static void befs_destroy_inodecache(void);
+
+static int befs_readlink(struct dentry *, char *, int);
+static int befs_follow_link(struct dentry *, struct nameidata *nd);
+
+static int befs_utf2nls(struct super_block *sb, const char *in, int in_len,
+ char **out, int *out_len);
+static int befs_nls2utf(struct super_block *sb, const char *in, int in_len,
+ char **out, int *out_len);
+
+static void befs_put_super(struct super_block *);
+static struct super_block *befs_read_super(struct super_block *, void *, int);
+static int befs_remount(struct super_block *, int *, char *);
+static int befs_statfs(struct super_block *, struct statfs *);
+static int parse_options(char *, befs_mount_options *);
+
+/* slab cache for befs_inode_info objects */
+static kmem_cache_t *befs_inode_cachep;
+
+static const struct super_operations befs_sops = {
+ read_inode:befs_read_inode, /* initialize & read inode */
+ clear_inode:befs_clear_inode, /* uninit inode */
+ put_super:befs_put_super, /* uninit super */
+ statfs:befs_statfs, /* statfs */
+ remount_fs:befs_remount,
+};
+
+struct file_operations befs_dir_operations = {
+ read:generic_read_dir,
+ readdir:befs_readdir,
+};
+
+struct inode_operations befs_dir_inode_operations = {
+ lookup:befs_lookup,
+};
+
+struct file_operations befs_file_operations = {
+ llseek:default_llseek,
+ read:generic_file_read,
+ mmap:generic_file_mmap,
+};
+
+struct inode_operations befs_file_inode_operations = {
+};
+
+struct address_space_operations befs_aops = {
+ readpage:befs_readpage,
+ sync_page:block_sync_page,
+ bmap:befs_bmap,
+};
+
+static struct inode_operations befs_symlink_inode_operations = {
+ readlink:befs_readlink,
+ follow_link:befs_follow_link,
+};
+
+/*
+ * Called by generic_file_read() to read a page of data
+ *
+ * In turn, simply calls a generic block read function and
+ * passes it the address of befs_get_block, for mapping file
+ * positions to disk blocks.
+ */
+static int
+befs_readpage(struct file *file, struct page *page)
+{
+ return block_read_full_page(page, befs_get_block);
+}
+
+static int
+befs_bmap(struct address_space *mapping, long block)
+{
+ return generic_block_bmap(mapping, block, befs_get_block);
+}
+
+/*
+ * Generic function to map a file position (block) to a
+ * disk offset (passed back in bh_result).
+ *
+ * Used by many higher level functions.
+ *
+ * Calls befs_fblock2brun() in datastream.c to do the real work.
+ *
+ * -WD 10-26-01
+ */
+
+static int
+befs_get_block(struct inode *inode, long block,
+ struct buffer_head *bh_result, int create)
+{
+ struct super_block *sb = inode->i_sb;
+ befs_data_stream *ds = &BEFS_I(inode)->i_data.ds;
+ befs_block_run run = BAD_IADDR;
+ int res = 0;
+ ulong disk_off;
+
+ befs_debug(sb, "---> befs_get_block() for inode %lu, block %ld",
+ inode->i_ino, block);
+
+ if (block < 0) {
+ befs_error(sb, "befs_get_block() was asked for a block "
+ "number less than zero: block %ld in inode %lu",
+ block, inode->i_ino);
+ return -EIO;
+ }
+
+ if (create) {
+ befs_error(sb, "befs_get_block() was asked to write to "
+ "block %ld in inode %lu", block, inode->i_ino);
+ return -EPERM;
+ }
+
+ res = befs_fblock2brun(sb, ds, block, &run);
+ if (res != BEFS_OK) {
+ befs_error(sb,
+ "<--- befs_get_block() for inode %lu, block "
+ "%ld ERROR", inode->i_ino, block);
+ return -EFBIG;
+ }
+
+ disk_off = (ulong) iaddr2blockno(sb, &run);
+
+ bh_result->b_dev = inode->i_dev;
+ bh_result->b_blocknr = disk_off;
+ bh_result->b_state |= (1UL << BH_Mapped);
+
+ befs_debug(sb, "<--- befs_get_block() for inode %lu, block %ld, "
+ "disk address %lu", inode->i_ino, block, disk_off);
+
+ return 0;
+}
+
+static struct dentry *
+befs_lookup(struct inode *dir, struct dentry *dentry)
+{
+ struct inode *inode = NULL;
+ struct super_block *sb = dir->i_sb;
+ befs_data_stream *ds = &BEFS_I(dir)->i_data.ds;
+ befs_off_t offset;
+ int ret;
+ int utfnamelen;
+ char *utfname;
+ const char *name = dentry->d_name.name;
+
+ befs_debug(sb, "---> befs_lookup() "
+ "name %s inode %ld", dentry->d_name.name, dir->i_ino);
+
+ /* Convert to UTF-8 */
+ if (BEFS_SB(sb)->nls) {
+ ret =
+ befs_nls2utf(sb, name, strlen(name), &utfname, &utfnamelen);
+ if (ret < 0) {
+ befs_debug(sb, "<--- befs_lookup() ERROR");
+ return ERR_PTR(ret);
+ }
+ ret = befs_btree_find(sb, ds, utfname, &offset);
+ kfree(utfname);
+
+ } else {
+ ret = befs_btree_find(sb, ds, dentry->d_name.name, &offset);
+ }
+
+ if (ret == BEFS_BT_NOT_FOUND) {
+ befs_debug(sb, "<--- befs_lookup() %s not found",
+ dentry->d_name.name);
+ return ERR_PTR(-ENOENT);
+
+ } else if (ret != BEFS_OK || offset == 0) {
+ befs_warning(sb, "<--- befs_lookup() Error");
+ return ERR_PTR(-ENODATA);
+ }
+
+ inode = iget(dir->i_sb, (ino_t) offset);
+ if (!inode)
+ return ERR_PTR(-EACCES);
+
+ d_add(dentry, inode);
+
+ befs_debug(sb, "<--- befs_lookup()");
+
+ return NULL;
+}
+
+static int
+befs_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ struct super_block *sb = inode->i_sb;
+ befs_data_stream *ds = &BEFS_I(inode)->i_data.ds;
+ befs_off_t value;
+ int result;
+ size_t keysize;
+ unsigned char d_type;
+ char keybuf[BEFS_NAME_LEN + 1];
+ char *nlsname;
+ int nlsnamelen;
+ const char *dirname = filp->f_dentry->d_name.name;
+
+ befs_debug(sb, "---> befs_readdir() "
+ "name %s, inode %ld, filp->f_pos %Ld",
+ dirname, inode->i_ino, filp->f_pos);
+
+ result = befs_btree_read(sb, ds, filp->f_pos, BEFS_NAME_LEN + 1,
+ keybuf, &keysize, &value);
+
+ if (result == BEFS_ERR) {
+ befs_debug(sb, "<--- befs_readdir() ERROR");
+ befs_error(sb, "IO error reading %s (inode %lu)",
+ dirname, inode->i_ino);
+ return -EIO;
+
+ } else if (result == BEFS_BT_END) {
+ befs_debug(sb, "<--- befs_readdir() END");
+ return 0;
+
+ } else if (result == BEFS_BT_EMPTY) {
+ befs_debug(sb, "<--- befs_readdir() Empty directory");
+ return 0;
+ }
+
+ d_type = DT_UNKNOWN;
+
+ /* Convert to NLS */
+ if (BEFS_SB(sb)->nls) {
+ result =
+ befs_utf2nls(sb, keybuf, keysize, &nlsname, &nlsnamelen);
+ if (result < 0) {
+ befs_debug(sb, "<--- befs_readdir() ERROR");
+ return result;
+ }
+ result = filldir(dirent, nlsname, nlsnamelen, filp->f_pos,
+ (ino_t) value, d_type);
+ kfree(nlsname);
+
+ } else {
+ result = filldir(dirent, keybuf, keysize, filp->f_pos,
+ (ino_t) value, d_type);
+ }
+
+ filp->f_pos++;
+
+ befs_debug(sb, "<--- befs_readdir() filp->f_pos %Ld", filp->f_pos);
+
+ return 0;
+}
+
+static void
+befs_clear_inode(struct inode *inode)
+{
+ befs_inode_info *b_ino = BEFS_I(inode);
+ inode->u.generic_ip = NULL;
+
+ if (b_ino) {
+ kmem_cache_free(befs_inode_cachep, b_ino);
+ }
+ return;
+}
+
+static void
+befs_read_inode(struct inode *inode)
+{
+ struct buffer_head *bh = NULL;
+ befs_inode *raw_inode = NULL;
+
+ struct super_block *sb = inode->i_sb;
+ befs_sb_info *befs_sb = BEFS_SB(sb);
+ befs_inode_info *befs_ino = NULL;
+
+ befs_debug(sb, "---> befs_read_inode() " "inode = %lu", inode->i_ino);
+
+ inode->u.generic_ip = kmem_cache_alloc(befs_inode_cachep, GFP_NOFS);
+ if (inode->u.generic_ip == NULL) {
+ befs_error(sb, "Unable to allocate memory for private "
+ "portion of inode %lu.", inode->i_ino);
+ goto unaquire_none;
+ }
+ befs_ino = BEFS_I(inode);
+
+ /* convert from vfs's inode number to befs's inode number */
+ befs_ino->i_inode_num = blockno2iaddr(sb, inode->i_ino);
+
+ befs_debug(sb, " real inode number [%u, %hu, %hu]",
+ befs_ino->i_inode_num.allocation_group,
+ befs_ino->i_inode_num.start, befs_ino->i_inode_num.len);
+
+ bh = befs_bread_iaddr(sb, befs_ino->i_inode_num);
+ if (!bh) {
+ befs_error(sb, "unable to read inode block - "
+ "inode = %lu", inode->i_ino);
+ goto unaquire_ino_info;
+ }
+
+ raw_inode = (befs_inode *) bh->b_data;
+
+ befs_dump_inode(sb, raw_inode);
+
+ if (befs_check_inode(sb, raw_inode, inode->i_ino) != BEFS_OK) {
+ befs_error(sb, "Bad inode: %lu", inode->i_ino);
+ goto unaquire_bh;
+ }
+
+ inode->i_mode = (umode_t) fs32_to_cpu(sb, raw_inode->mode);
+
+ /*
+ * set uid and gid. But since current BeOS is single user OS, so
+ * you can change by "uid" or "gid" options.
+ */
+
+ inode->i_uid = befs_sb->mount_opts.use_uid ?
+ befs_sb->mount_opts.uid : (uid_t) fs32_to_cpu(sb, raw_inode->uid);
+ inode->i_gid = befs_sb->mount_opts.use_gid ?
+ befs_sb->mount_opts.gid : (gid_t) fs32_to_cpu(sb, raw_inode->gid);
+
+ inode->i_nlink = 1;
+
+ /*
+ * BEFS's time is 64 bits, but current VFS is 32 bits...
+ * BEFS don't have access time. Nor inode change time. VFS
+ * doesn't have creation time.
+ */
+
+ inode->i_mtime =
+ (time_t) (fs64_to_cpu(sb, raw_inode->last_modified_time) >> 16);
+ inode->i_ctime = inode->i_mtime;
+ inode->i_atime = inode->i_mtime;
+ inode->i_blkbits = befs_sb->block_shift;
+ inode->i_blksize = befs_sb->block_size;
+
+ befs_ino->i_inode_num = fsrun_to_cpu(sb, raw_inode->inode_num);
+ befs_ino->i_parent = fsrun_to_cpu(sb, raw_inode->parent);
+ befs_ino->i_attribute = fsrun_to_cpu(sb, raw_inode->attributes);
+ befs_ino->i_flags = fs32_to_cpu(sb, raw_inode->flags);
+
+ if (S_ISLNK(inode->i_mode) && !(inode->i_flags & BEFS_LONG_SYMLINK)) {
+ inode->i_size = 0;
+ inode->i_blocks = befs_sb->block_size / VFS_BLOCK_SIZE;
+ strncpy(befs_ino->i_data.symlink, raw_inode->data.symlink,
+ BEFS_SYMLINK_LEN);
+ } else {
+ int num_blks;
+
+ befs_ino->i_data.ds =
+ fsds_to_cpu(sb, raw_inode->data.datastream);
+
+ num_blks = befs_count_blocks(sb, &befs_ino->i_data.ds);
+ inode->i_blocks =
+ num_blks * (befs_sb->block_size / VFS_BLOCK_SIZE);
+ inode->i_size = befs_ino->i_data.ds.size;
+ }
+
+ inode->i_mapping->a_ops = &befs_aops;
+
+ if (S_ISREG(inode->i_mode)) {
+ inode->i_fop = &befs_file_operations;
+ inode->i_op = &befs_file_inode_operations;
+ } else if (S_ISDIR(inode->i_mode)) {
+ inode->i_op = &befs_dir_inode_operations;
+ inode->i_fop = &befs_dir_operations;
+ } else if (S_ISLNK(inode->i_mode)) {
+ inode->i_op = &befs_symlink_inode_operations;
+ } else {
+ befs_error(sb, "Inode %lu is not a regular file, "
+ "directory or symlink. THAT IS WRONG! BeFS has no "
+ "on disk special files", inode->i_ino);
+ goto unaquire_bh;
+ }
+
+ brelse(bh);
+ befs_debug(sb, "<--- befs_read_inode()");
+ return;
+
+ unaquire_bh:
+ brelse(bh);
+
+ unaquire_ino_info:
+ kmem_cache_free(befs_inode_cachep, inode->u.generic_ip);
+
+ unaquire_none:
+ make_bad_inode(inode);
+ inode->u.generic_ip = NULL;
+ befs_debug(sb, "<--- befs_read_inode() - Bad inode");
+ return;
+}
+
+/* Initialize the inode cache. Called at fs setup.
+ *
+ * Taken from NFS implementation by Al Viro.
+ */
+static int
+befs_init_inodecache(void)
+{
+ befs_inode_cachep = kmem_cache_create("befs_inode_cache",
+ sizeof (struct befs_inode_info),
+ 0, SLAB_HWCACHE_ALIGN,
+ NULL, NULL);
+ if (befs_inode_cachep == NULL) {
+ printk(KERN_ERR "befs_init_inodecache: "
+ "Couldn't initalize inode slabcache\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+/* Called at fs teardown.
+ *
+ * Taken from NFS implementation by Al Viro.
+ */
+static void
+befs_destroy_inodecache(void)
+{
+ if (kmem_cache_destroy(befs_inode_cachep))
+ printk(KERN_ERR "befs_destroy_inodecache: "
+ "not all structures were freed\n");
+}
+
+/*
+ * The inode of symbolic link is different to data stream.
+ * The data stream become link name. Unless the LONG_SYMLINK
+ * flag is set.
+ */
+static int
+befs_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+ struct super_block *sb = dentry->d_sb;
+ befs_inode_info *befs_ino = BEFS_I(dentry->d_inode);
+ char *link;
+ int res;
+
+ if (befs_ino->i_flags & BEFS_LONG_SYMLINK) {
+ befs_data_stream *data = &befs_ino->i_data.ds;
+ befs_off_t linklen = data->size;
+
+ befs_debug(sb, "Follow long symlink");
+
+ link = kmalloc(linklen, GFP_NOFS);
+ if (link == NULL)
+ return -ENOMEM;
+
+ if (befs_read_lsymlink(sb, data, link, linklen) != linklen) {
+ kfree(link);
+ befs_error(sb, "Failed to read entire long symlink");
+ return -EIO;
+ }
+
+ res = vfs_follow_link(nd, link);
+
+ kfree(link);
+ } else {
+ link = befs_ino->i_data.symlink;
+ res = vfs_follow_link(nd, link);
+ }
+
+ return res;
+}
+
+static int
+befs_readlink(struct dentry *dentry, char *buffer, int buflen)
+{
+ struct super_block *sb = dentry->d_sb;
+ befs_inode_info *befs_ino = BEFS_I(dentry->d_inode);
+ char *link;
+ int res;
+
+ if (befs_ino->i_flags & BEFS_LONG_SYMLINK) {
+ befs_data_stream *data = &befs_ino->i_data.ds;
+ befs_off_t linklen = data->size;
+
+ befs_debug(sb, "Read long symlink");
+
+ link = kmalloc(linklen, GFP_NOFS);
+ if (link == NULL)
+ return -ENOMEM;
+
+ if (befs_read_lsymlink(sb, data, link, linklen) != linklen) {
+ kfree(link);
+ befs_error(sb, "Failed to read entire long symlink");
+ return -EIO;
+ }
+
+ res = vfs_readlink(dentry, buffer, buflen, link);
+
+ kfree(link);
+ } else {
+ link = befs_ino->i_data.symlink;
+ res = vfs_readlink(dentry, buffer, buflen, link);
+ }
+
+ return res;
+}
+
+/*
+ * UTF-8 to NLS charset convert routine
+ *
+ * Changed 8/10/01 by Will Dyson. Now use uni2char() / char2uni() rather than
+ * the nls tables directly
+ */
+
+static int
+befs_utf2nls(struct super_block *sb, const char *in,
+ int in_len, char **out, int *out_len)
+{
+ struct nls_table *nls = BEFS_SB(sb)->nls;
+ int i, o;
+ wchar_t uni;
+ int unilen, utflen;
+ char *result;
+ int maxlen = in_len; /* The utf8->nls conversion cant make more chars */
+
+ befs_debug(sb, "---> utf2nls()");
+
+ if (!nls) {
+ befs_error(sb, "befs_utf2nls called with no NLS table loaded");
+ return -EINVAL;
+ }
+
+ *out = result = kmalloc(maxlen, GFP_NOFS);
+ if (!*out) {
+ befs_error(sb, "befs_utf2nls() cannot allocate memory");
+ *out_len = 0;
+ return -ENOMEM;
+ }
+
+ for (i = o = 0; i < in_len; i += utflen, o += unilen) {
+
+ /* convert from UTF-8 to Unicode */
+ utflen = utf8_mbtowc(&uni, &in[i], in_len - i);
+ if (utflen < 0) {
+ goto conv_err;
+ }
+
+ /* convert from Unicode to nls */
+ unilen = nls->uni2char(uni, &result[o], 1);
+ if (unilen < 0) {
+ goto conv_err;
+ }
+ }
+ result[o] = '\0';
+ *out_len = o;
+
+ befs_debug(sb, "<--- utf2nls()");
+
+ return o;
+
+ conv_err:
+ befs_error(sb, "Name using charecter set %s contains a charecter that "
+ "cannot be converted to unicode.", nls->charset);
+ befs_debug(sb, "<--- utf2nls()");
+ kfree(result);
+ return -EILSEQ;
+}
+
+/**
+ * befs_nls2utf - Convert NLS string to utf8 encodeing
+ * @sb: Superblock
+ * @src: Input string buffer in NLS format
+ * @srclen: Length of input string in bytes
+ * @dest: The output string in UTF8 format
+ * @destlen: Length of the output buffer
+ *
+ * Converts input string @src, which is in the format of the loaded NLS map,
+ * into a utf8 string.
+ *
+ * The destination string @dest is allocated by this function and the caller is
+ * responsible for freeing it with kfree()
+ *
+ * On return, *@destlen is the length of @dest in bytes.
+ *
+ * On success, the return value is the number of utf8 charecters written to
+ * the ouput buffer @dest.
+ *
+ * On Failure, a negative number coresponding to the error code is returned.
+ */
+
+static int
+befs_nls2utf(struct super_block *sb, const char *in,
+ int in_len, char **out, int *out_len)
+{
+ struct nls_table *nls = BEFS_SB(sb)->nls;
+ int i, o;
+ wchar_t uni;
+ int unilen, utflen;
+ char *result;
+ int maxlen = 3 * in_len;
+
+ befs_debug(sb, "---> nls2utf()\n");
+
+ if (!nls) {
+ befs_error(sb, "befs_nls2utf called with no NLS table loaded.");
+ return -EINVAL;
+ }
+
+ *out = result = kmalloc(maxlen, GFP_NOFS);
+ if (!*out) {
+ befs_error(sb, "befs_nls2utf() cannot allocate memory");
+ *out_len = 0;
+ return -ENOMEM;
+ }
+
+ for (i = o = 0; i < in_len; i += unilen, o += utflen) {
+
+ /* convert from nls to unicode */
+ unilen = nls->char2uni(&in[i], in_len - i, &uni);
+ if (unilen < 0) {
+ goto conv_err;
+ }
+
+ /* convert from unicode to UTF-8 */
+ utflen = utf8_wctomb(&result[o], uni, 3);
+ if (utflen <= 0) {
+ goto conv_err;
+ }
+ }
+
+ result[o] = '\0';
+ *out_len = o;
+
+ befs_debug(sb, "<--- nls2utf()");
+
+ return i;
+
+ conv_err:
+ befs_error(sb, "Name using charecter set %s contains a charecter that "
+ "cannot be converted to unicode.", nls->charset);
+ befs_debug(sb, "<--- nls2utf()");
+ kfree(result);
+ return -EILSEQ;
+}
+
+/****Superblock****/
+
+static int
+parse_options(char *options, befs_mount_options * opts)
+{
+ char *this_char;
+ char *value;
+ int ret = 1;
+
+ /* Initialize options */
+ opts->uid = 0;
+ opts->gid = 0;
+ opts->use_uid = 0;
+ opts->use_gid = 0;
+ opts->iocharset = NULL;
+ opts->debug = 0;
+
+ if (!options)
+ return ret;
+
+ for (this_char = strtok(options, ","); this_char != NULL;
+ this_char = strtok(NULL, ",")) {
+
+ if ((value = strchr(this_char, '=')) != NULL)
+ *value++ = 0;
+
+ if (!strcmp(this_char, "uid")) {
+ if (!value || !*value) {
+ ret = 0;
+ } else {
+ opts->uid = simple_strtoul(value, &value, 0);
+ opts->use_uid = 1;
+ if (*value) {
+ printk(KERN_ERR "BEFS: Invalid uid "
+ "option: %s\n", value);
+ ret = 0;
+ }
+ }
+ } else if (!strcmp(this_char, "gid")) {
+ if (!value || !*value)
+ ret = 0;
+ else {
+ opts->gid = simple_strtoul(value, &value, 0);
+ opts->use_gid = 1;
+ if (*value) {
+ printk(KERN_ERR
+ "BEFS: Invalid gid option: "
+ "%s\n", value);
+ ret = 0;
+ }
+ }
+ } else if (!strcmp(this_char, "iocharset") && value) {
+ char *p = value;
+ int len;
+
+ while (*value && *value != ',')
+ value++;
+ len = value - p;
+ if (len) {
+ char *buffer = kmalloc(len + 1, GFP_NOFS);
+ if (buffer) {
+ opts->iocharset = buffer;
+ memcpy(buffer, p, len);
+ buffer[len] = 0;
+
+ } else {
+ printk(KERN_ERR "BEFS: "
+ "cannot allocate memory\n");
+ ret = 0;
+ }
+ }
+ } else if (!strcmp(this_char, "debug")) {
+ opts->debug = 1;
+ }
+ }
+
+ return ret;
+}
+
+/* This function has the responsibiltiy of getting the
+ * filesystem ready for unmounting.
+ * Basicly, we free everything that we allocated in
+ * befs_read_inode
+ */
+static void
+befs_put_super(struct super_block *sb)
+{
+ if (BEFS_SB(sb)->mount_opts.iocharset) {
+ kfree(BEFS_SB(sb)->mount_opts.iocharset);
+ BEFS_SB(sb)->mount_opts.iocharset = NULL;
+ }
+
+ if (BEFS_SB(sb)->nls) {
+ unload_nls(BEFS_SB(sb)->nls);
+ BEFS_SB(sb)->nls = NULL;
+ }
+
+ if (sb->u.generic_sbp) {
+ kfree(sb->u.generic_sbp);
+ sb->u.generic_sbp = NULL;
+ }
+ return;
+}
+
+/* Allocate private field of the superblock, fill it.
+ *
+ * Finish filling the public superblock fields
+ * Make the root directory
+ * Load a set of NLS translations if needed.
+ */
+static struct super_block *
+befs_read_super(struct super_block *sb, void *data, int silent)
+{
+ struct buffer_head *bh;
+ befs_sb_info *befs_sb;
+ befs_super_block *disk_sb;
+ int blocksize;
+
+ const unsigned long sb_block = 0;
+ const off_t x86_sb_off = 512;
+
+ sb->u.generic_sbp = kmalloc(sizeof (struct befs_sb_info), GFP_NOFS);
+ if (sb->u.generic_sbp == NULL) {
+ printk(KERN_ERR
+ "BeFS(%s): Unable to allocate memory for private "
+ "portion of superblock. Bailing.\n",
+ kdevname(sb->s_dev));
+ goto unaquire_none;
+ }
+ befs_sb = BEFS_SB(sb);
+
+ if (!parse_options((char *) data, &befs_sb->mount_opts)) {
+ befs_error(sb, "cannot parse mount options");
+ goto unaquire_priv_sbp;
+ }
+
+ befs_debug(sb, "---> befs_read_super()");
+
+#ifndef CONFIG_BEFS_RW
+ if (!(sb->s_flags & MS_RDONLY)) {
+ befs_warning(sb,
+ "No write support. Marking filesystem read-only");
+ sb->s_flags |= MS_RDONLY;
+ }
+#endif /* CONFIG_BEFS_RW */
+
+ /*
+ * Set dummy blocksize to read super block.
+ * Will be set to real fs blocksize later.
+ *
+ * Linux 2.4.10 and later refuse to read blocks smaller than
+ * the hardsect size for the device. But we also need to read at
+ * least 1k to get the second 512 bytes of the volume.
+ * -WD 10-26-01
+ */
+ blocksize = max_t(int, get_hardsect_size(sb->s_dev), 1024);
+ set_blocksize(sb->s_dev, blocksize);
+
+ if (!(bh = bread(sb->s_dev, sb_block, blocksize))) {
+ befs_error(sb, "unable to read superblock");
+ goto unaquire_priv_sbp;
+ }
+
+ /* account for offset of super block on x86 */
+ disk_sb = (befs_super_block *) bh->b_data;
+ if ((le32_to_cpu(disk_sb->magic1) == BEFS_SUPER_MAGIC1) ||
+ (be32_to_cpu(disk_sb->magic1) == BEFS_SUPER_MAGIC1)) {
+ befs_debug(sb, "Using PPC superblock location");
+ } else {
+ befs_debug(sb, "Using x86 superblock location");
+ disk_sb =
+ (befs_super_block *) ((void *) bh->b_data + x86_sb_off);
+ }
+
+ if (befs_load_sb(sb, disk_sb) != BEFS_OK)
+ goto unaquire_bh;
+
+ befs_dump_super_block(sb, disk_sb);
+
+ brelse(bh);
+
+ if (befs_check_sb(sb) != BEFS_OK)
+ goto unaquire_priv_sbp;
+
+ /*
+ * set up enough so that it can read an inode
+ * Fill in kernel superblock fields from private sb
+ */
+ sb->s_magic = BEFS_SUPER_MAGIC;
+ sb->s_blocksize = (ulong) befs_sb->block_size;
+ sb->s_blocksize_bits = (unsigned char) befs_sb->block_shift;
+ sb->s_op = (struct super_operations *) &befs_sops;
+ sb->s_root =
+ d_alloc_root(iget(sb, iaddr2blockno(sb, &(befs_sb->root_dir))));
+ if (!sb->s_root) {
+ befs_error(sb, "get root inode failed");
+ goto unaquire_priv_sbp;
+ }
+
+ /* load nls library */
+ if (befs_sb->mount_opts.iocharset) {
+ befs_debug(sb, "Loading nls: %s",
+ befs_sb->mount_opts.iocharset);
+ befs_sb->nls = load_nls(befs_sb->mount_opts.iocharset);
+ if (!befs_sb->nls) {
+ befs_warning(sb, "Cannot load nls %s"
+ "loding default nls",
+ befs_sb->mount_opts.iocharset);
+ befs_sb->nls = load_nls_default();
+ }
+ }
+
+ /* Set real blocksize of fs */
+ set_blocksize(sb->s_dev, (int) befs_sb->block_size);
+
+ return sb;
+/*****************/
+ unaquire_bh:
+ brelse(bh);
+
+ unaquire_priv_sbp:
+ kfree(sb->u.generic_sbp);
+
+ unaquire_none:
+ sb->s_dev = 0;
+ sb->u.generic_sbp = NULL;
+ return NULL;
+}
+
+static int
+befs_remount(struct super_block *sb, int *flags, char *data)
+{
+ if (!(*flags & MS_RDONLY))
+ return -EINVAL;
+ return 0;
+}
+
+static int
+befs_statfs(struct super_block *sb, struct statfs *buf)
+{
+
+ befs_debug(sb, "---> befs_statfs()");
+
+ buf->f_type = BEFS_SUPER_MAGIC;
+ buf->f_bsize = sb->s_blocksize;
+ buf->f_blocks = BEFS_SB(sb)->num_blocks;
+ buf->f_bfree = BEFS_SB(sb)->num_blocks - BEFS_SB(sb)->used_blocks;
+ buf->f_bavail = buf->f_bfree;
+ buf->f_files = 0; /* UNKNOWN */
+ buf->f_ffree = 0; /* UNKNOWN */
+ buf->f_namelen = BEFS_NAME_LEN;
+
+ befs_debug(sb, "<--- befs_statfs()");
+
+ return 0;
+}
+
+/*
+ Makes a variable of type file_system_type,
+ named befs_fs_tipe, identified by the "befs" string,
+ and containing a reference to the befs_read_super function
+
+ Macro declared in <linux/fs.h>
+*/
+static DECLARE_FSTYPE_DEV(befs_fs_type, "befs", befs_read_super);
+
+static int __init
+init_befs_fs(void)
+{
+ int err;
+
+ printk(KERN_INFO "BeFS version: %s\n", BEFS_VERSION);
+
+ err = befs_init_inodecache();
+ if (err)
+ return err;
+
+ return register_filesystem(&befs_fs_type);
+}
+
+static void __exit
+exit_befs_fs(void)
+{
+ befs_destroy_inodecache();
+
+ unregister_filesystem(&befs_fs_type);
+}
+
+/*
+Macros that typecheck the init and exit functions,
+ensures that they are called at init and cleanup,
+and eliminates warnings about unused functions.
+*/
+module_init(init_befs_fs)
+ module_exit(exit_befs_fs)
diff --git a/fs/befs/super.c b/fs/befs/super.c
new file mode 100644
index 00000000000000..57ec2514bb315d
--- /dev/null
+++ b/fs/befs/super.c
@@ -0,0 +1,112 @@
+/*
+ * super.c
+ *
+ * Copyright (C) 2001-2002 Will Dyson <will_dyson@pobox.com>
+ *
+ * Licensed under the GNU GPL. See the file COPYING for details.
+ *
+ */
+
+#include <linux/fs.h>
+
+#include "befs.h"
+#include "super.h"
+#include "endian.h"
+
+/**
+ * load_befs_sb -- Read from disk and properly byteswap all the fields
+ * of the befs superblock
+ *
+ *
+ *
+ *
+ */
+int
+befs_load_sb(struct super_block *sb, befs_super_block * disk_sb)
+{
+ befs_sb_info *befs_sb = BEFS_SB(sb);
+
+ /* Check the byte order of the filesystem */
+ if (le32_to_cpu(disk_sb->fs_byte_order) == BEFS_BYTEORDER_NATIVE)
+ befs_sb->byte_order = BEFS_BYTESEX_LE;
+ else if (be32_to_cpu(disk_sb->fs_byte_order) == BEFS_BYTEORDER_NATIVE)
+ befs_sb->byte_order = BEFS_BYTESEX_BE;
+
+ befs_sb->magic1 = fs32_to_cpu(sb, disk_sb->magic1);
+ befs_sb->magic2 = fs32_to_cpu(sb, disk_sb->magic2);
+ befs_sb->magic3 = fs32_to_cpu(sb, disk_sb->magic3);
+ befs_sb->block_size = fs32_to_cpu(sb, disk_sb->block_size);
+ befs_sb->block_shift = fs32_to_cpu(sb, disk_sb->block_shift);
+ befs_sb->num_blocks = fs64_to_cpu(sb, disk_sb->num_blocks);
+ befs_sb->used_blocks = fs64_to_cpu(sb, disk_sb->used_blocks);
+ befs_sb->inode_size = fs32_to_cpu(sb, disk_sb->inode_size);
+
+ befs_sb->blocks_per_ag = fs32_to_cpu(sb, disk_sb->blocks_per_ag);
+ befs_sb->ag_shift = fs32_to_cpu(sb, disk_sb->ag_shift);
+ befs_sb->num_ags = fs32_to_cpu(sb, disk_sb->num_ags);
+
+ befs_sb->log_blocks = fsrun_to_cpu(sb, disk_sb->log_blocks);
+ befs_sb->log_start = fs64_to_cpu(sb, disk_sb->log_start);
+ befs_sb->log_end = fs64_to_cpu(sb, disk_sb->log_end);
+
+ befs_sb->root_dir = fsrun_to_cpu(sb, disk_sb->root_dir);
+ befs_sb->indices = fsrun_to_cpu(sb, disk_sb->indices);
+ befs_sb->nls = NULL;
+
+ return BEFS_OK;
+}
+
+int
+befs_check_sb(struct super_block *sb)
+{
+ befs_sb_info *befs_sb = BEFS_SB(sb);
+
+ /* Check magic headers of super block */
+ if ((befs_sb->magic1 != BEFS_SUPER_MAGIC1)
+ || (befs_sb->magic2 != BEFS_SUPER_MAGIC2)
+ || (befs_sb->magic3 != BEFS_SUPER_MAGIC3)) {
+ befs_error(sb, "invalid magic header");
+ return BEFS_ERR;
+ }
+
+ /*
+ * Check blocksize of BEFS.
+ *
+ * Blocksize of BEFS is 1024, 2048, 4096 or 8192.
+ */
+
+ if ((befs_sb->block_size != 1024)
+ && (befs_sb->block_size != 2048)
+ && (befs_sb->block_size != 4096)
+ && (befs_sb->block_size != 8192)) {
+ befs_error(sb, "invalid blocksize: %u", befs_sb->block_size);
+ return BEFS_ERR;
+ }
+
+ if (befs_sb->block_size > PAGE_SIZE) {
+ befs_error(sb, "blocksize(%u) cannot be larger"
+ "than system pagesize(%lu)", befs_sb->block_size,
+ PAGE_SIZE);
+ return BEFS_ERR;
+ }
+
+ /*
+ * block_shift and block_size encode the same information
+ * in different ways as a consistency check.
+ */
+
+ if ((1 << befs_sb->block_shift) != befs_sb->block_size) {
+ befs_error(sb, "block_shift disagrees with block_size. "
+ "Corruption likely.");
+ return BEFS_ERR;
+ }
+
+ if (befs_sb->log_start != befs_sb->log_end) {
+ befs_error(sb, "Filesystem not clean! There are blocks in the "
+ "journal. You must boot into BeOS and mount this volume "
+ "to make it clean.");
+ return BEFS_ERR;
+ }
+
+ return BEFS_OK;
+}
diff --git a/fs/befs/super.h b/fs/befs/super.h
new file mode 100644
index 00000000000000..d33d1ca74e698b
--- /dev/null
+++ b/fs/befs/super.h
@@ -0,0 +1,7 @@
+/*
+ * super.h
+ */
+
+int befs_load_sb(struct super_block *sb, befs_super_block * disk_sb);
+
+int befs_check_sb(struct super_block *sb);
diff --git a/fs/bfs/Makefile b/fs/bfs/Makefile
new file mode 100644
index 00000000000000..ff4d375dd53985
--- /dev/null
+++ b/fs/bfs/Makefile
@@ -0,0 +1,15 @@
+#
+# Makefile for BFS filesystem.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main Makefile...
+
+O_TARGET := bfs.o
+
+obj-y := inode.o file.o dir.o
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/bfs/bfs_defs.h b/fs/bfs/bfs_defs.h
new file mode 100644
index 00000000000000..672419cd09574e
--- /dev/null
+++ b/fs/bfs/bfs_defs.h
@@ -0,0 +1,20 @@
+#define su_lasti u.bfs_sb.si_lasti
+#define su_blocks u.bfs_sb.si_blocks
+#define su_freeb u.bfs_sb.si_freeb
+#define su_freei u.bfs_sb.si_freei
+#define su_lf_ioff u.bfs_sb.si_lf_ioff
+#define su_lf_sblk u.bfs_sb.si_lf_sblk
+#define su_lf_eblk u.bfs_sb.si_lf_eblk
+#define su_imap u.bfs_sb.si_imap
+#define su_sbh u.bfs_sb.si_sbh
+#define su_bfs_sb u.bfs_sb.si_bfs_sb
+
+#define iu_dsk_ino u.bfs_i.i_dsk_ino
+#define iu_sblock u.bfs_i.i_sblock
+#define iu_eblock u.bfs_i.i_eblock
+
+#define printf(format, args...) \
+do { \
+ printk(KERN_ERR "BFS-fs: %s(): ", __FUNCTION__); \
+ printk(format, args); \
+} while (0)
diff --git a/fs/bfs/dir.c b/fs/bfs/dir.c
new file mode 100644
index 00000000000000..9b6c86195a6825
--- /dev/null
+++ b/fs/bfs/dir.c
@@ -0,0 +1,339 @@
+/*
+ * fs/bfs/dir.c
+ * BFS directory operations.
+ * Copyright (C) 1999,2000 Tigran Aivazian <tigran@veritas.com>
+ */
+
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/bfs_fs.h>
+#include <linux/locks.h>
+
+#include "bfs_defs.h"
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define dprintf(x...) printf(x)
+#else
+#define dprintf(x...)
+#endif
+
+static int bfs_add_entry(struct inode * dir, const char * name, int namelen, int ino);
+static struct buffer_head * bfs_find_entry(struct inode * dir,
+ const char * name, int namelen, struct bfs_dirent ** res_dir);
+
+static int bfs_readdir(struct file * f, void * dirent, filldir_t filldir)
+{
+ struct inode * dir = f->f_dentry->d_inode;
+ struct buffer_head * bh;
+ struct bfs_dirent * de;
+ kdev_t dev = dir->i_dev;
+ unsigned int offset;
+ int block;
+
+ if (f->f_pos & (BFS_DIRENT_SIZE-1)) {
+ printf("Bad f_pos=%08lx for %s:%08lx\n", (unsigned long)f->f_pos,
+ bdevname(dev), dir->i_ino);
+ return -EBADF;
+ }
+
+ while (f->f_pos < dir->i_size) {
+ offset = f->f_pos & (BFS_BSIZE-1);
+ block = dir->iu_sblock + (f->f_pos >> BFS_BSIZE_BITS);
+ bh = sb_bread(dir->i_sb, block);
+ if (!bh) {
+ f->f_pos += BFS_BSIZE - offset;
+ continue;
+ }
+ do {
+ de = (struct bfs_dirent *)(bh->b_data + offset);
+ if (de->ino) {
+ int size = strnlen(de->name, BFS_NAMELEN);
+ if (filldir(dirent, de->name, size, f->f_pos, de->ino, DT_UNKNOWN) < 0) {
+ brelse(bh);
+ return 0;
+ }
+ }
+ offset += BFS_DIRENT_SIZE;
+ f->f_pos += BFS_DIRENT_SIZE;
+ } while (offset < BFS_BSIZE && f->f_pos < dir->i_size);
+ brelse(bh);
+ }
+
+ UPDATE_ATIME(dir);
+ return 0;
+}
+
+struct file_operations bfs_dir_operations = {
+ read: generic_read_dir,
+ readdir: bfs_readdir,
+ fsync: file_fsync,
+};
+
+extern void dump_imap(const char *, struct super_block *);
+
+static int bfs_create(struct inode * dir, struct dentry * dentry, int mode)
+{
+ int err;
+ struct inode * inode;
+ struct super_block * s = dir->i_sb;
+ unsigned long ino;
+
+ inode = new_inode(s);
+ if (!inode)
+ return -ENOSPC;
+ ino = find_first_zero_bit(s->su_imap, s->su_lasti);
+ if (ino > s->su_lasti) {
+ iput(inode);
+ return -ENOSPC;
+ }
+ set_bit(ino, s->su_imap);
+ s->su_freei--;
+ inode->i_uid = current->fsuid;
+ inode->i_gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+ inode->i_blocks = inode->i_blksize = 0;
+ inode->i_op = &bfs_file_inops;
+ inode->i_fop = &bfs_file_operations;
+ inode->i_mapping->a_ops = &bfs_aops;
+ inode->i_mode = mode;
+ inode->i_ino = inode->iu_dsk_ino = ino;
+ inode->iu_sblock = inode->iu_eblock = 0;
+ insert_inode_hash(inode);
+ mark_inode_dirty(inode);
+ dump_imap("create",s);
+
+ err = bfs_add_entry(dir, dentry->d_name.name, dentry->d_name.len, inode->i_ino);
+ if (err) {
+ inode->i_nlink--;
+ mark_inode_dirty(inode);
+ iput(inode);
+ return err;
+ }
+ d_instantiate(dentry, inode);
+ return 0;
+}
+
+static struct dentry * bfs_lookup(struct inode * dir, struct dentry * dentry)
+{
+ struct inode * inode = NULL;
+ struct buffer_head * bh;
+ struct bfs_dirent * de;
+
+ if (dentry->d_name.len > BFS_NAMELEN)
+ return ERR_PTR(-ENAMETOOLONG);
+
+ bh = bfs_find_entry(dir, dentry->d_name.name, dentry->d_name.len, &de);
+ if (bh) {
+ unsigned long ino = le32_to_cpu(de->ino);
+ brelse(bh);
+ inode = iget(dir->i_sb, ino);
+ if (!inode)
+ return ERR_PTR(-EACCES);
+ }
+ d_add(dentry, inode);
+ return NULL;
+}
+
+static int bfs_link(struct dentry * old, struct inode * dir, struct dentry * new)
+{
+ struct inode * inode = old->d_inode;
+ int err;
+
+ if (S_ISDIR(inode->i_mode))
+ return -EPERM;
+
+ err = bfs_add_entry(dir, new->d_name.name, new->d_name.len, inode->i_ino);
+ if (err)
+ return err;
+ inode->i_nlink++;
+ inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+ atomic_inc(&inode->i_count);
+ d_instantiate(new, inode);
+ return 0;
+}
+
+
+static int bfs_unlink(struct inode * dir, struct dentry * dentry)
+{
+ int error = -ENOENT;
+ struct inode * inode;
+ struct buffer_head * bh;
+ struct bfs_dirent * de;
+
+ inode = dentry->d_inode;
+ bh = bfs_find_entry(dir, dentry->d_name.name, dentry->d_name.len, &de);
+ if (!bh || de->ino != inode->i_ino)
+ goto out_brelse;
+
+ if (!inode->i_nlink) {
+ printf("unlinking non-existent file %s:%lu (nlink=%d)\n", bdevname(inode->i_dev),
+ inode->i_ino, inode->i_nlink);
+ inode->i_nlink = 1;
+ }
+ de->ino = 0;
+ dir->i_version = ++event;
+ mark_buffer_dirty(bh);
+ dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ mark_inode_dirty(dir);
+ inode->i_nlink--;
+ inode->i_ctime = dir->i_ctime;
+ mark_inode_dirty(inode);
+ error = 0;
+
+out_brelse:
+ brelse(bh);
+ return error;
+}
+
+static int bfs_rename(struct inode * old_dir, struct dentry * old_dentry,
+ struct inode * new_dir, struct dentry * new_dentry)
+{
+ struct inode * old_inode, * new_inode;
+ struct buffer_head * old_bh, * new_bh;
+ struct bfs_dirent * old_de, * new_de;
+ int error = -ENOENT;
+
+ old_bh = new_bh = NULL;
+ old_inode = old_dentry->d_inode;
+ if (S_ISDIR(old_inode->i_mode))
+ return -EINVAL;
+
+ old_bh = bfs_find_entry(old_dir,
+ old_dentry->d_name.name,
+ old_dentry->d_name.len, &old_de);
+
+ if (!old_bh || old_de->ino != old_inode->i_ino)
+ goto end_rename;
+
+ error = -EPERM;
+ new_inode = new_dentry->d_inode;
+ new_bh = bfs_find_entry(new_dir,
+ new_dentry->d_name.name,
+ new_dentry->d_name.len, &new_de);
+
+ if (new_bh && !new_inode) {
+ brelse(new_bh);
+ new_bh = NULL;
+ }
+ if (!new_bh) {
+ error = bfs_add_entry(new_dir,
+ new_dentry->d_name.name,
+ new_dentry->d_name.len, old_inode->i_ino);
+ if (error)
+ goto end_rename;
+ }
+ old_de->ino = 0;
+ old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
+ old_dir->i_version = ++event;
+ mark_inode_dirty(old_dir);
+ if (new_inode) {
+ new_inode->i_nlink--;
+ new_inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(new_inode);
+ }
+ mark_buffer_dirty(old_bh);
+ error = 0;
+
+end_rename:
+ brelse(old_bh);
+ brelse(new_bh);
+ return error;
+}
+
+struct inode_operations bfs_dir_inops = {
+ create: bfs_create,
+ lookup: bfs_lookup,
+ link: bfs_link,
+ unlink: bfs_unlink,
+ rename: bfs_rename,
+};
+
+static int bfs_add_entry(struct inode * dir, const char * name, int namelen, int ino)
+{
+ struct buffer_head * bh;
+ struct bfs_dirent * de;
+ int block, sblock, eblock, off;
+ int i;
+
+ dprintf("name=%s, namelen=%d\n", name, namelen);
+
+ if (!namelen)
+ return -ENOENT;
+ if (namelen > BFS_NAMELEN)
+ return -ENAMETOOLONG;
+
+ sblock = dir->iu_sblock;
+ eblock = dir->iu_eblock;
+ for (block=sblock; block<=eblock; block++) {
+ bh = sb_bread(dir->i_sb, block);
+ if(!bh)
+ return -ENOSPC;
+ for (off=0; off<BFS_BSIZE; off+=BFS_DIRENT_SIZE) {
+ de = (struct bfs_dirent *)(bh->b_data + off);
+ if (!de->ino) {
+ if ((block-sblock)*BFS_BSIZE + off >= dir->i_size) {
+ dir->i_size += BFS_DIRENT_SIZE;
+ dir->i_ctime = CURRENT_TIME;
+ }
+ dir->i_mtime = CURRENT_TIME;
+ mark_inode_dirty(dir);
+ dir->i_version = ++event;
+ de->ino = ino;
+ for (i=0; i<BFS_NAMELEN; i++)
+ de->name[i] = (i < namelen) ? name[i] : 0;
+ mark_buffer_dirty(bh);
+ brelse(bh);
+ return 0;
+ }
+ }
+ brelse(bh);
+ }
+ return -ENOSPC;
+}
+
+static inline int bfs_namecmp(int len, const char * name, const char * buffer)
+{
+ if (len < BFS_NAMELEN && buffer[len])
+ return 0;
+ return !memcmp(name, buffer, len);
+}
+
+static struct buffer_head * bfs_find_entry(struct inode * dir,
+ const char * name, int namelen, struct bfs_dirent ** res_dir)
+{
+ unsigned long block, offset;
+ struct buffer_head * bh;
+ struct bfs_dirent * de;
+
+ *res_dir = NULL;
+ if (namelen > BFS_NAMELEN)
+ return NULL;
+ bh = NULL;
+ block = offset = 0;
+ while (block * BFS_BSIZE + offset < dir->i_size) {
+ if (!bh) {
+ bh = sb_bread(dir->i_sb, dir->iu_sblock + block);
+ if (!bh) {
+ block++;
+ continue;
+ }
+ }
+ de = (struct bfs_dirent *)(bh->b_data + offset);
+ offset += BFS_DIRENT_SIZE;
+ if (de->ino && bfs_namecmp(namelen, name, de->name)) {
+ *res_dir = de;
+ return bh;
+ }
+ if (offset < bh->b_size)
+ continue;
+ brelse(bh);
+ bh = NULL;
+ offset = 0;
+ block++;
+ }
+ brelse(bh);
+ return NULL;
+}
diff --git a/fs/bfs/file.c b/fs/bfs/file.c
new file mode 100644
index 00000000000000..d7a284392225be
--- /dev/null
+++ b/fs/bfs/file.c
@@ -0,0 +1,168 @@
+/*
+ * fs/bfs/file.c
+ * BFS file operations.
+ * Copyright (C) 1999,2000 Tigran Aivazian <tigran@veritas.com>
+ */
+
+#include <linux/fs.h>
+#include <linux/locks.h>
+#include <linux/bfs_fs.h>
+#include <linux/smp_lock.h>
+#include "bfs_defs.h"
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define dprintf(x...) printf(x)
+#else
+#define dprintf(x...)
+#endif
+
+struct file_operations bfs_file_operations = {
+ llseek: generic_file_llseek,
+ read: generic_file_read,
+ write: generic_file_write,
+ mmap: generic_file_mmap,
+};
+
+static int bfs_move_block(unsigned long from, unsigned long to, kdev_t dev)
+{
+ struct buffer_head *bh, *new;
+
+ bh = bread(dev, from, BFS_BSIZE);
+ if (!bh)
+ return -EIO;
+ new = getblk(dev, to, BFS_BSIZE);
+ memcpy(new->b_data, bh->b_data, bh->b_size);
+ mark_buffer_dirty(new);
+ bforget(bh);
+ brelse(new);
+ return 0;
+}
+
+static int bfs_move_blocks(kdev_t dev, unsigned long start, unsigned long end,
+ unsigned long where)
+{
+ unsigned long i;
+
+ dprintf("%08lx-%08lx->%08lx\n", start, end, where);
+ for (i = start; i <= end; i++)
+ if(bfs_move_block(i, where + i, dev)) {
+ dprintf("failed to move block %08lx -> %08lx\n", i, where + i);
+ return -EIO;
+ }
+ return 0;
+}
+
+static int bfs_get_block(struct inode * inode, long block,
+ struct buffer_head * bh_result, int create)
+{
+ long phys;
+ int err;
+ struct super_block *sb = inode->i_sb;
+ struct buffer_head *sbh = sb->su_sbh;
+
+ if (block < 0 || block > sb->su_blocks)
+ return -EIO;
+
+ phys = inode->iu_sblock + block;
+ if (!create) {
+ if (phys <= inode->iu_eblock) {
+ dprintf("c=%d, b=%08lx, phys=%08lx (granted)\n", create, block, phys);
+ bh_result->b_dev = inode->i_dev;
+ bh_result->b_blocknr = phys;
+ bh_result->b_state |= (1UL << BH_Mapped);
+ }
+ return 0;
+ }
+
+ /* if the file is not empty and the requested block is within the range
+ of blocks allocated for this file, we can grant it */
+ if (inode->i_size && phys <= inode->iu_eblock) {
+ dprintf("c=%d, b=%08lx, phys=%08lx (interim block granted)\n",
+ create, block, phys);
+ bh_result->b_dev = inode->i_dev;
+ bh_result->b_blocknr = phys;
+ bh_result->b_state |= (1UL << BH_Mapped);
+ return 0;
+ }
+
+ /* the rest has to be protected against itself */
+ lock_kernel();
+
+ /* if the last data block for this file is the last allocated block, we can
+ extend the file trivially, without moving it anywhere */
+ if (inode->iu_eblock == sb->su_lf_eblk) {
+ dprintf("c=%d, b=%08lx, phys=%08lx (simple extension)\n",
+ create, block, phys);
+ bh_result->b_dev = inode->i_dev;
+ bh_result->b_blocknr = phys;
+ bh_result->b_state |= (1UL << BH_Mapped);
+ sb->su_freeb -= phys - inode->iu_eblock;
+ sb->su_lf_eblk = inode->iu_eblock = phys;
+ mark_inode_dirty(inode);
+ mark_buffer_dirty(sbh);
+ err = 0;
+ goto out;
+ }
+
+ /* Ok, we have to move this entire file to the next free block */
+ phys = sb->su_lf_eblk + 1;
+ if (inode->iu_sblock) { /* if data starts on block 0 then there is no data */
+ err = bfs_move_blocks(inode->i_dev, inode->iu_sblock,
+ inode->iu_eblock, phys);
+ if (err) {
+ dprintf("failed to move ino=%08lx -> fs corruption\n", inode->i_ino);
+ goto out;
+ }
+ } else
+ err = 0;
+
+ dprintf("c=%d, b=%08lx, phys=%08lx (moved)\n", create, block, phys);
+ inode->iu_sblock = phys;
+ phys += block;
+ sb->su_lf_eblk = inode->iu_eblock = phys;
+
+ /* this assumes nothing can write the inode back while we are here
+ * and thus update inode->i_blocks! (XXX)*/
+ sb->su_freeb -= inode->iu_eblock - inode->iu_sblock + 1 - inode->i_blocks;
+ mark_inode_dirty(inode);
+ mark_buffer_dirty(sbh);
+ bh_result->b_dev = inode->i_dev;
+ bh_result->b_blocknr = phys;
+ bh_result->b_state |= (1UL << BH_Mapped);
+out:
+ unlock_kernel();
+ return err;
+}
+
+static int bfs_writepage(struct page *page)
+{
+ return block_write_full_page(page, bfs_get_block);
+}
+
+static int bfs_readpage(struct file *file, struct page *page)
+{
+ return block_read_full_page(page, bfs_get_block);
+}
+
+static int bfs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
+{
+ return block_prepare_write(page, from, to, bfs_get_block);
+}
+
+static int bfs_bmap(struct address_space *mapping, long block)
+{
+ return generic_block_bmap(mapping, block, bfs_get_block);
+}
+
+struct address_space_operations bfs_aops = {
+ readpage: bfs_readpage,
+ writepage: bfs_writepage,
+ sync_page: block_sync_page,
+ prepare_write: bfs_prepare_write,
+ commit_write: generic_commit_write,
+ bmap: bfs_bmap,
+};
+
+struct inode_operations bfs_file_inops;
diff --git a/fs/bfs/inode.c b/fs/bfs/inode.c
new file mode 100644
index 00000000000000..f83f13f562007a
--- /dev/null
+++ b/fs/bfs/inode.c
@@ -0,0 +1,341 @@
+/*
+ * fs/bfs/inode.c
+ * BFS superblock and inode operations.
+ * Copyright (C) 1999,2000 Tigran Aivazian <tigran@veritas.com>
+ * From fs/minix, Copyright (C) 1991, 1992 Linus Torvalds.
+ */
+
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/locks.h>
+#include <linux/bfs_fs.h>
+#include <linux/smp_lock.h>
+
+#include <asm/uaccess.h>
+
+#include "bfs_defs.h"
+
+MODULE_AUTHOR("Tigran A. Aivazian <tigran@veritas.com>");
+MODULE_DESCRIPTION("SCO UnixWare BFS filesystem for Linux");
+MODULE_LICENSE("GPL");
+EXPORT_NO_SYMBOLS;
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define dprintf(x...) printf(x)
+#else
+#define dprintf(x...)
+#endif
+
+void dump_imap(const char *prefix, struct super_block * s);
+
+static void bfs_read_inode(struct inode * inode)
+{
+ unsigned long ino = inode->i_ino;
+ kdev_t dev = inode->i_dev;
+ struct bfs_inode * di;
+ struct buffer_head * bh;
+ int block, off;
+
+ if (ino < BFS_ROOT_INO || ino > inode->i_sb->su_lasti) {
+ printf("Bad inode number %s:%08lx\n", bdevname(dev), ino);
+ make_bad_inode(inode);
+ return;
+ }
+
+ block = (ino - BFS_ROOT_INO)/BFS_INODES_PER_BLOCK + 1;
+ bh = sb_bread(inode->i_sb, block);
+ if (!bh) {
+ printf("Unable to read inode %s:%08lx\n", bdevname(dev), ino);
+ make_bad_inode(inode);
+ return;
+ }
+
+ off = (ino - BFS_ROOT_INO) % BFS_INODES_PER_BLOCK;
+ di = (struct bfs_inode *)bh->b_data + off;
+
+ inode->i_mode = 0x0000FFFF & di->i_mode;
+ if (di->i_vtype == BFS_VDIR) {
+ inode->i_mode |= S_IFDIR;
+ inode->i_op = &bfs_dir_inops;
+ inode->i_fop = &bfs_dir_operations;
+ } else if (di->i_vtype == BFS_VREG) {
+ inode->i_mode |= S_IFREG;
+ inode->i_op = &bfs_file_inops;
+ inode->i_fop = &bfs_file_operations;
+ inode->i_mapping->a_ops = &bfs_aops;
+ }
+
+ inode->i_uid = di->i_uid;
+ inode->i_gid = di->i_gid;
+ inode->i_nlink = di->i_nlink;
+ inode->i_size = BFS_FILESIZE(di);
+ inode->i_blocks = BFS_FILEBLOCKS(di);
+ inode->i_blksize = PAGE_SIZE;
+ inode->i_atime = di->i_atime;
+ inode->i_mtime = di->i_mtime;
+ inode->i_ctime = di->i_ctime;
+ inode->iu_dsk_ino = di->i_ino; /* can be 0 so we store a copy */
+ inode->iu_sblock = di->i_sblock;
+ inode->iu_eblock = di->i_eblock;
+
+ brelse(bh);
+}
+
+static void bfs_write_inode(struct inode * inode, int unused)
+{
+ unsigned long ino = inode->i_ino;
+ kdev_t dev = inode->i_dev;
+ struct bfs_inode * di;
+ struct buffer_head * bh;
+ int block, off;
+
+ if (ino < BFS_ROOT_INO || ino > inode->i_sb->su_lasti) {
+ printf("Bad inode number %s:%08lx\n", bdevname(dev), ino);
+ return;
+ }
+
+ lock_kernel();
+ block = (ino - BFS_ROOT_INO)/BFS_INODES_PER_BLOCK + 1;
+ bh = sb_bread(inode->i_sb, block);
+ if (!bh) {
+ printf("Unable to read inode %s:%08lx\n", bdevname(dev), ino);
+ unlock_kernel();
+ return;
+ }
+
+ off = (ino - BFS_ROOT_INO)%BFS_INODES_PER_BLOCK;
+ di = (struct bfs_inode *)bh->b_data + off;
+
+ if (inode->i_ino == BFS_ROOT_INO)
+ di->i_vtype = BFS_VDIR;
+ else
+ di->i_vtype = BFS_VREG;
+
+ di->i_ino = inode->i_ino;
+ di->i_mode = inode->i_mode;
+ di->i_uid = inode->i_uid;
+ di->i_gid = inode->i_gid;
+ di->i_nlink = inode->i_nlink;
+ di->i_atime = inode->i_atime;
+ di->i_mtime = inode->i_mtime;
+ di->i_ctime = inode->i_ctime;
+ di->i_sblock = inode->iu_sblock;
+ di->i_eblock = inode->iu_eblock;
+ di->i_eoffset = di->i_sblock * BFS_BSIZE + inode->i_size - 1;
+
+ mark_buffer_dirty(bh);
+ brelse(bh);
+ unlock_kernel();
+}
+
+static void bfs_delete_inode(struct inode * inode)
+{
+ unsigned long ino = inode->i_ino;
+ kdev_t dev = inode->i_dev;
+ struct bfs_inode * di;
+ struct buffer_head * bh;
+ int block, off;
+ struct super_block * s = inode->i_sb;
+
+ dprintf("ino=%08lx\n", inode->i_ino);
+
+ if (inode->i_ino < BFS_ROOT_INO || inode->i_ino > inode->i_sb->su_lasti) {
+ printf("invalid ino=%08lx\n", inode->i_ino);
+ return;
+ }
+
+ inode->i_size = 0;
+ inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ lock_kernel();
+ mark_inode_dirty(inode);
+ block = (ino - BFS_ROOT_INO)/BFS_INODES_PER_BLOCK + 1;
+ bh = sb_bread(s, block);
+ if (!bh) {
+ printf("Unable to read inode %s:%08lx\n", bdevname(dev), ino);
+ unlock_kernel();
+ return;
+ }
+ off = (ino - BFS_ROOT_INO)%BFS_INODES_PER_BLOCK;
+ di = (struct bfs_inode *)bh->b_data + off;
+ if (di->i_ino) {
+ s->su_freeb += BFS_FILEBLOCKS(di);
+ s->su_freei++;
+ clear_bit(di->i_ino, s->su_imap);
+ dump_imap("delete_inode", s);
+ }
+ di->i_ino = 0;
+ di->i_sblock = 0;
+ mark_buffer_dirty(bh);
+ brelse(bh);
+
+ /* if this was the last file, make the previous
+ block "last files last block" even if there is no real file there,
+ saves us 1 gap */
+ if (s->su_lf_eblk == inode->iu_eblock) {
+ s->su_lf_eblk = inode->iu_sblock - 1;
+ mark_buffer_dirty(s->su_sbh);
+ }
+ unlock_kernel();
+ clear_inode(inode);
+}
+
+static void bfs_put_super(struct super_block *s)
+{
+ brelse(s->su_sbh);
+ kfree(s->su_imap);
+}
+
+static int bfs_statfs(struct super_block *s, struct statfs *buf)
+{
+ buf->f_type = BFS_MAGIC;
+ buf->f_bsize = s->s_blocksize;
+ buf->f_blocks = s->su_blocks;
+ buf->f_bfree = buf->f_bavail = s->su_freeb;
+ buf->f_files = s->su_lasti + 1 - BFS_ROOT_INO;
+ buf->f_ffree = s->su_freei;
+ buf->f_fsid.val[0] = kdev_t_to_nr(s->s_dev);
+ buf->f_namelen = BFS_NAMELEN;
+ return 0;
+}
+
+static void bfs_write_super(struct super_block *s)
+{
+ if (!(s->s_flags & MS_RDONLY))
+ mark_buffer_dirty(s->su_sbh);
+ s->s_dirt = 0;
+}
+
+static struct super_operations bfs_sops = {
+ read_inode: bfs_read_inode,
+ write_inode: bfs_write_inode,
+ delete_inode: bfs_delete_inode,
+ put_super: bfs_put_super,
+ write_super: bfs_write_super,
+ statfs: bfs_statfs,
+};
+
+void dump_imap(const char *prefix, struct super_block * s)
+{
+#if 0
+ int i;
+ char *tmpbuf = (char *)get_free_page(GFP_KERNEL);
+
+ if (!tmpbuf)
+ return;
+ for (i=s->su_lasti; i>=0; i--) {
+ if (i>PAGE_SIZE-100) break;
+ if (test_bit(i, s->su_imap))
+ strcat(tmpbuf, "1");
+ else
+ strcat(tmpbuf, "0");
+ }
+ printk(KERN_ERR "BFS-fs: %s: lasti=%08lx <%s>\n", prefix, s->su_lasti, tmpbuf);
+ free_page((unsigned long)tmpbuf);
+#endif
+}
+
+static struct super_block * bfs_read_super(struct super_block * s,
+ void * data, int silent)
+{
+ kdev_t dev;
+ struct buffer_head * bh;
+ struct bfs_super_block * bfs_sb;
+ struct inode * inode;
+ int i, imap_len;
+
+ dev = s->s_dev;
+ set_blocksize(dev, BFS_BSIZE);
+ s->s_blocksize = BFS_BSIZE;
+ s->s_blocksize_bits = BFS_BSIZE_BITS;
+
+ bh = sb_bread(s, 0);
+ if(!bh)
+ goto out;
+ bfs_sb = (struct bfs_super_block *)bh->b_data;
+ if (bfs_sb->s_magic != BFS_MAGIC) {
+ if (!silent)
+ printf("No BFS filesystem on %s (magic=%08x)\n",
+ bdevname(dev), bfs_sb->s_magic);
+ goto out;
+ }
+ if (BFS_UNCLEAN(bfs_sb, s) && !silent)
+ printf("%s is unclean, continuing\n", bdevname(dev));
+
+ s->s_magic = BFS_MAGIC;
+ s->su_bfs_sb = bfs_sb;
+ s->su_sbh = bh;
+ s->su_lasti = (bfs_sb->s_start - BFS_BSIZE)/sizeof(struct bfs_inode)
+ + BFS_ROOT_INO - 1;
+
+ imap_len = s->su_lasti/8 + 1;
+ s->su_imap = kmalloc(imap_len, GFP_KERNEL);
+ if (!s->su_imap)
+ goto out;
+ memset(s->su_imap, 0, imap_len);
+ for (i=0; i<BFS_ROOT_INO; i++)
+ set_bit(i, s->su_imap);
+
+ s->s_op = &bfs_sops;
+ inode = iget(s, BFS_ROOT_INO);
+ if (!inode) {
+ kfree(s->su_imap);
+ goto out;
+ }
+ s->s_root = d_alloc_root(inode);
+ if (!s->s_root) {
+ iput(inode);
+ kfree(s->su_imap);
+ goto out;
+ }
+
+ s->su_blocks = (bfs_sb->s_end + 1)>>BFS_BSIZE_BITS; /* for statfs(2) */
+ s->su_freeb = (bfs_sb->s_end + 1 - bfs_sb->s_start)>>BFS_BSIZE_BITS;
+ s->su_freei = 0;
+ s->su_lf_eblk = 0;
+ s->su_lf_sblk = 0;
+ s->su_lf_ioff = 0;
+ for (i=BFS_ROOT_INO; i<=s->su_lasti; i++) {
+ inode = iget(s,i);
+ if (inode->iu_dsk_ino == 0)
+ s->su_freei++;
+ else {
+ set_bit(i, s->su_imap);
+ s->su_freeb -= inode->i_blocks;
+ if (inode->iu_eblock > s->su_lf_eblk) {
+ s->su_lf_eblk = inode->iu_eblock;
+ s->su_lf_sblk = inode->iu_sblock;
+ s->su_lf_ioff = BFS_INO2OFF(i);
+ }
+ }
+ iput(inode);
+ }
+ if (!(s->s_flags & MS_RDONLY)) {
+ mark_buffer_dirty(bh);
+ s->s_dirt = 1;
+ }
+ dump_imap("read_super", s);
+ return s;
+
+out:
+ brelse(bh);
+ return NULL;
+}
+
+static DECLARE_FSTYPE_DEV(bfs_fs_type, "bfs", bfs_read_super);
+
+static int __init init_bfs_fs(void)
+{
+ return register_filesystem(&bfs_fs_type);
+}
+
+static void __exit exit_bfs_fs(void)
+{
+ unregister_filesystem(&bfs_fs_type);
+}
+
+module_init(init_bfs_fs)
+module_exit(exit_bfs_fs)
diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c
new file mode 100644
index 00000000000000..b46d9d832e962e
--- /dev/null
+++ b/fs/binfmt_aout.c
@@ -0,0 +1,542 @@
+/*
+ * linux/fs/binfmt_aout.c
+ *
+ * Copyright (C) 1991, 1992, 1996 Linus Torvalds
+ */
+
+#include <linux/module.h>
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/a.out.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/user.h>
+#include <linux/slab.h>
+#include <linux/binfmts.h>
+#include <linux/personality.h>
+#include <linux/init.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/pgalloc.h>
+
+static int load_aout_binary(struct linux_binprm *, struct pt_regs * regs);
+static int load_aout_library(struct file*);
+static int aout_core_dump(long signr, struct pt_regs * regs, struct file *file);
+
+extern void dump_thread(struct pt_regs *, struct user *);
+
+static struct linux_binfmt aout_format = {
+ NULL, THIS_MODULE, load_aout_binary, load_aout_library, aout_core_dump, PAGE_SIZE
+};
+
+#define BAD_ADDR(x) ((unsigned long)(x) >= TASK_SIZE)
+
+static int set_brk(unsigned long start, unsigned long end)
+{
+ start = PAGE_ALIGN(start);
+ end = PAGE_ALIGN(end);
+ if (end > start) {
+ unsigned long addr;
+ down_write(&current->mm->mmap_sem);
+ addr = do_brk(start, end - start);
+ up_write(&current->mm->mmap_sem);
+ if (BAD_ADDR(addr))
+ return addr;
+ }
+ return 0;
+}
+
+/*
+ * These are the only things you should do on a core-file: use only these
+ * macros to write out all the necessary info.
+ */
+
+static int dump_write(struct file *file, const void *addr, int nr)
+{
+ return file->f_op->write(file, addr, nr, &file->f_pos) == nr;
+}
+
+#define DUMP_WRITE(addr, nr) \
+ if (!dump_write(file, (void *)(addr), (nr))) \
+ goto end_coredump;
+
+#define DUMP_SEEK(offset) \
+if (file->f_op->llseek) { \
+ if (file->f_op->llseek(file,(offset),0) != (offset)) \
+ goto end_coredump; \
+} else file->f_pos = (offset)
+
+/*
+ * Routine writes a core dump image in the current directory.
+ * Currently only a stub-function.
+ *
+ * Note that setuid/setgid files won't make a core-dump if the uid/gid
+ * changed due to the set[u|g]id. It's enforced by the "current->mm->dumpable"
+ * field, which also makes sure the core-dumps won't be recursive if the
+ * dumping of the process results in another error..
+ */
+
+static int aout_core_dump(long signr, struct pt_regs * regs, struct file *file)
+{
+ mm_segment_t fs;
+ int has_dumped = 0;
+ unsigned long dump_start, dump_size;
+ struct user dump;
+#if defined(__alpha__)
+# define START_DATA(u) (u.start_data)
+#elif defined(__arm__)
+# define START_DATA(u) ((u.u_tsize << PAGE_SHIFT) + u.start_code)
+#elif defined(__sparc__)
+# define START_DATA(u) (u.u_tsize)
+#elif defined(__i386__) || defined(__mc68000__) || defined(__arch_um__)
+# define START_DATA(u) (u.u_tsize << PAGE_SHIFT)
+#endif
+#ifdef __sparc__
+# define START_STACK(u) ((regs->u_regs[UREG_FP]) & ~(PAGE_SIZE - 1))
+#else
+# define START_STACK(u) (u.start_stack)
+#endif
+
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+ has_dumped = 1;
+ current->flags |= PF_DUMPCORE;
+ strncpy(dump.u_comm, current->comm, sizeof(current->comm));
+#ifndef __sparc__
+ dump.u_ar0 = (void *)(((unsigned long)(&dump.regs)) - ((unsigned long)(&dump)));
+#endif
+ dump.signal = signr;
+ dump_thread(regs, &dump);
+
+/* If the size of the dump file exceeds the rlimit, then see what would happen
+ if we wrote the stack, but not the data area. */
+#ifdef __sparc__
+ if ((dump.u_dsize+dump.u_ssize) >
+ current->rlim[RLIMIT_CORE].rlim_cur)
+ dump.u_dsize = 0;
+#else
+ if ((dump.u_dsize+dump.u_ssize+1) * PAGE_SIZE >
+ current->rlim[RLIMIT_CORE].rlim_cur)
+ dump.u_dsize = 0;
+#endif
+
+/* Make sure we have enough room to write the stack and data areas. */
+#ifdef __sparc__
+ if ((dump.u_ssize) >
+ current->rlim[RLIMIT_CORE].rlim_cur)
+ dump.u_ssize = 0;
+#else
+ if ((dump.u_ssize+1) * PAGE_SIZE >
+ current->rlim[RLIMIT_CORE].rlim_cur)
+ dump.u_ssize = 0;
+#endif
+
+/* make sure we actually have a data and stack area to dump */
+ set_fs(USER_DS);
+#ifdef __sparc__
+ if (verify_area(VERIFY_READ, (void *) START_DATA(dump), dump.u_dsize))
+ dump.u_dsize = 0;
+ if (verify_area(VERIFY_READ, (void *) START_STACK(dump), dump.u_ssize))
+ dump.u_ssize = 0;
+#else
+ if (verify_area(VERIFY_READ, (void *) START_DATA(dump), dump.u_dsize << PAGE_SHIFT))
+ dump.u_dsize = 0;
+ if (verify_area(VERIFY_READ, (void *) START_STACK(dump), dump.u_ssize << PAGE_SHIFT))
+ dump.u_ssize = 0;
+#endif
+
+ set_fs(KERNEL_DS);
+/* struct user */
+ DUMP_WRITE(&dump,sizeof(dump));
+/* Now dump all of the user data. Include malloced stuff as well */
+#ifndef __sparc__
+ DUMP_SEEK(PAGE_SIZE);
+#endif
+/* now we start writing out the user space info */
+ set_fs(USER_DS);
+/* Dump the data area */
+ if (dump.u_dsize != 0) {
+ dump_start = START_DATA(dump);
+#ifdef __sparc__
+ dump_size = dump.u_dsize;
+#else
+ dump_size = dump.u_dsize << PAGE_SHIFT;
+#endif
+ DUMP_WRITE(dump_start,dump_size);
+ }
+/* Now prepare to dump the stack area */
+ if (dump.u_ssize != 0) {
+ dump_start = START_STACK(dump);
+#ifdef __sparc__
+ dump_size = dump.u_ssize;
+#else
+ dump_size = dump.u_ssize << PAGE_SHIFT;
+#endif
+ DUMP_WRITE(dump_start,dump_size);
+ }
+/* Finally dump the task struct. Not be used by gdb, but could be useful */
+ set_fs(KERNEL_DS);
+ DUMP_WRITE(current,sizeof(*current));
+end_coredump:
+ set_fs(fs);
+ return has_dumped;
+}
+
+/*
+ * create_aout_tables() parses the env- and arg-strings in new user
+ * memory and creates the pointer tables from them, and puts their
+ * addresses on the "stack", returning the new stack pointer value.
+ */
+static unsigned long * create_aout_tables(char * p, struct linux_binprm * bprm)
+{
+ char **argv, **envp;
+ unsigned long * sp;
+ int argc = bprm->argc;
+ int envc = bprm->envc;
+
+ sp = (unsigned long *) ((-(unsigned long)sizeof(char *)) & (unsigned long) p);
+#ifdef __sparc__
+ /* This imposes the proper stack alignment for a new process. */
+ sp = (unsigned long *) (((unsigned long) sp) & ~7);
+ if ((envc+argc+3)&1) --sp;
+#endif
+#ifdef __alpha__
+/* whee.. test-programs are so much fun. */
+ put_user(0, --sp);
+ put_user(0, --sp);
+ if (bprm->loader) {
+ put_user(0, --sp);
+ put_user(0x3eb, --sp);
+ put_user(bprm->loader, --sp);
+ put_user(0x3ea, --sp);
+ }
+ put_user(bprm->exec, --sp);
+ put_user(0x3e9, --sp);
+#endif
+ sp -= envc+1;
+ envp = (char **) sp;
+ sp -= argc+1;
+ argv = (char **) sp;
+#if defined(__i386__) || defined(__mc68000__) || defined(__arm__) || defined(__arch_um__)
+ put_user((unsigned long) envp,--sp);
+ put_user((unsigned long) argv,--sp);
+#endif
+ put_user(argc,--sp);
+ current->mm->arg_start = (unsigned long) p;
+ while (argc-->0) {
+ char c;
+ put_user(p,argv++);
+ do {
+ get_user(c,p++);
+ } while (c);
+ }
+ put_user(NULL,argv);
+ current->mm->arg_end = current->mm->env_start = (unsigned long) p;
+ while (envc-->0) {
+ char c;
+ put_user(p,envp++);
+ do {
+ get_user(c,p++);
+ } while (c);
+ }
+ put_user(NULL,envp);
+ current->mm->env_end = (unsigned long) p;
+ return sp;
+}
+
+/*
+ * These are the functions used to load a.out style executables and shared
+ * libraries. There is no binary dependent code anywhere else.
+ */
+
+static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs)
+{
+ struct exec ex;
+ unsigned long error;
+ unsigned long fd_offset;
+ unsigned long rlim;
+ int retval;
+
+ ex = *((struct exec *) bprm->buf); /* exec-header */
+ if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC &&
+ N_MAGIC(ex) != QMAGIC && N_MAGIC(ex) != NMAGIC) ||
+ N_TRSIZE(ex) || N_DRSIZE(ex) ||
+ bprm->file->f_dentry->d_inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) {
+ return -ENOEXEC;
+ }
+
+ fd_offset = N_TXTOFF(ex);
+
+ /* Check initial limits. This avoids letting people circumvent
+ * size limits imposed on them by creating programs with large
+ * arrays in the data or bss.
+ */
+ rlim = current->rlim[RLIMIT_DATA].rlim_cur;
+ if (rlim >= RLIM_INFINITY)
+ rlim = ~0;
+ if (ex.a_data + ex.a_bss > rlim)
+ return -ENOMEM;
+
+ /* Flush all traces of the currently running executable */
+ retval = flush_old_exec(bprm);
+ if (retval)
+ return retval;
+
+ /* OK, This is the point of no return */
+#if defined(__alpha__)
+ SET_AOUT_PERSONALITY(bprm, ex);
+#elif defined(__sparc__)
+ set_personality(PER_SUNOS);
+#if !defined(__sparc_v9__)
+ memcpy(&current->thread.core_exec, &ex, sizeof(struct exec));
+#endif
+#else
+ set_personality(PER_LINUX);
+#endif
+
+ current->mm->end_code = ex.a_text +
+ (current->mm->start_code = N_TXTADDR(ex));
+ current->mm->end_data = ex.a_data +
+ (current->mm->start_data = N_DATADDR(ex));
+ current->mm->brk = ex.a_bss +
+ (current->mm->start_brk = N_BSSADDR(ex));
+
+ current->mm->rss = 0;
+ current->mm->mmap = NULL;
+ compute_creds(bprm);
+ current->flags &= ~PF_FORKNOEXEC;
+#ifdef __sparc__
+ if (N_MAGIC(ex) == NMAGIC) {
+ loff_t pos = fd_offset;
+ /* Fuck me plenty... */
+ /* <AOL></AOL> */
+ down_write(&current->mm->mmap_sem);
+ error = do_brk(N_TXTADDR(ex), ex.a_text);
+ up_write(&current->mm->mmap_sem);
+ bprm->file->f_op->read(bprm->file, (char *) N_TXTADDR(ex),
+ ex.a_text, &pos);
+ down_write(&current->mm->mmap_sem);
+ error = do_brk(N_DATADDR(ex), ex.a_data);
+ up_write(&current->mm->mmap_sem);
+ bprm->file->f_op->read(bprm->file, (char *) N_DATADDR(ex),
+ ex.a_data, &pos);
+ goto beyond_if;
+ }
+#endif
+
+ if (N_MAGIC(ex) == OMAGIC) {
+ unsigned long text_addr, map_size;
+ loff_t pos;
+
+ text_addr = N_TXTADDR(ex);
+
+#if defined(__alpha__) || defined(__sparc__)
+ pos = fd_offset;
+ map_size = ex.a_text+ex.a_data + PAGE_SIZE - 1;
+#else
+ pos = 32;
+ map_size = ex.a_text+ex.a_data;
+#endif
+ down_write(&current->mm->mmap_sem);
+ error = do_brk(text_addr & PAGE_MASK, map_size);
+ up_write(&current->mm->mmap_sem);
+ if (error != (text_addr & PAGE_MASK)) {
+ send_sig(SIGKILL, current, 0);
+ return error;
+ }
+
+ error = bprm->file->f_op->read(bprm->file, (char *)text_addr,
+ ex.a_text+ex.a_data, &pos);
+ if ((signed long)error < 0) {
+ send_sig(SIGKILL, current, 0);
+ return error;
+ }
+
+ flush_icache_range(text_addr, text_addr+ex.a_text+ex.a_data);
+ } else {
+ static unsigned long error_time, error_time2;
+ if ((ex.a_text & 0xfff || ex.a_data & 0xfff) &&
+ (N_MAGIC(ex) != NMAGIC) && (jiffies-error_time2) > 5*HZ)
+ {
+ printk(KERN_NOTICE "executable not page aligned\n");
+ error_time2 = jiffies;
+ }
+
+ if ((fd_offset & ~PAGE_MASK) != 0 &&
+ (jiffies-error_time) > 5*HZ)
+ {
+ printk(KERN_WARNING
+ "fd_offset is not page aligned. Please convert program: %s\n",
+ bprm->file->f_dentry->d_name.name);
+ error_time = jiffies;
+ }
+
+ if (!bprm->file->f_op->mmap||((fd_offset & ~PAGE_MASK) != 0)) {
+ loff_t pos = fd_offset;
+ down_write(&current->mm->mmap_sem);
+ do_brk(N_TXTADDR(ex), ex.a_text+ex.a_data);
+ up_write(&current->mm->mmap_sem);
+ bprm->file->f_op->read(bprm->file,(char *)N_TXTADDR(ex),
+ ex.a_text+ex.a_data, &pos);
+ flush_icache_range((unsigned long) N_TXTADDR(ex),
+ (unsigned long) N_TXTADDR(ex) +
+ ex.a_text+ex.a_data);
+ goto beyond_if;
+ }
+
+ down_write(&current->mm->mmap_sem);
+ error = do_mmap(bprm->file, N_TXTADDR(ex), ex.a_text,
+ PROT_READ | PROT_EXEC,
+ MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE,
+ fd_offset);
+ up_write(&current->mm->mmap_sem);
+
+ if (error != N_TXTADDR(ex)) {
+ send_sig(SIGKILL, current, 0);
+ return error;
+ }
+
+ down_write(&current->mm->mmap_sem);
+ error = do_mmap(bprm->file, N_DATADDR(ex), ex.a_data,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE,
+ fd_offset + ex.a_text);
+ up_write(&current->mm->mmap_sem);
+ if (error != N_DATADDR(ex)) {
+ send_sig(SIGKILL, current, 0);
+ return error;
+ }
+ }
+beyond_if:
+ set_binfmt(&aout_format);
+
+ retval = set_brk(current->mm->start_brk, current->mm->brk);
+ if (retval < 0) {
+ send_sig(SIGKILL, current, 0);
+ return retval;
+ }
+
+ retval = setup_arg_pages(bprm);
+ if (retval < 0) {
+ /* Someone check-me: is this error path enough? */
+ send_sig(SIGKILL, current, 0);
+ return retval;
+ }
+
+ current->mm->start_stack =
+ (unsigned long) create_aout_tables((char *) bprm->p, bprm);
+#ifdef __alpha__
+ regs->gp = ex.a_gpvalue;
+#endif
+ start_thread(regs, ex.a_entry, current->mm->start_stack);
+ if (current->ptrace & PT_PTRACED)
+ send_sig(SIGTRAP, current, 0);
+ return 0;
+}
+
+static int load_aout_library(struct file *file)
+{
+ struct inode * inode;
+ unsigned long bss, start_addr, len;
+ unsigned long error;
+ int retval;
+ struct exec ex;
+
+ inode = file->f_dentry->d_inode;
+
+ retval = -ENOEXEC;
+ error = kernel_read(file, 0, (char *) &ex, sizeof(ex));
+ if (error != sizeof(ex))
+ goto out;
+
+ /* We come in here for the regular a.out style of shared libraries */
+ if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != QMAGIC) || N_TRSIZE(ex) ||
+ N_DRSIZE(ex) || ((ex.a_entry & 0xfff) && N_MAGIC(ex) == ZMAGIC) ||
+ inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) {
+ goto out;
+ }
+
+ if (N_FLAGS(ex))
+ goto out;
+
+ /* For QMAGIC, the starting address is 0x20 into the page. We mask
+ this off to get the starting address for the page */
+
+ start_addr = ex.a_entry & 0xfffff000;
+
+ if ((N_TXTOFF(ex) & ~PAGE_MASK) != 0) {
+ static unsigned long error_time;
+ loff_t pos = N_TXTOFF(ex);
+
+ if ((jiffies-error_time) > 5*HZ)
+ {
+ printk(KERN_WARNING
+ "N_TXTOFF is not page aligned. Please convert library: %s\n",
+ file->f_dentry->d_name.name);
+ error_time = jiffies;
+ }
+
+ down_write(&current->mm->mmap_sem);
+ do_brk(start_addr, ex.a_text + ex.a_data + ex.a_bss);
+ up_write(&current->mm->mmap_sem);
+
+ file->f_op->read(file, (char *)start_addr,
+ ex.a_text + ex.a_data, &pos);
+ flush_icache_range((unsigned long) start_addr,
+ (unsigned long) start_addr + ex.a_text + ex.a_data);
+
+ retval = 0;
+ goto out;
+ }
+ /* Now use mmap to map the library into memory. */
+ down_write(&current->mm->mmap_sem);
+ error = do_mmap(file, start_addr, ex.a_text + ex.a_data,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE,
+ N_TXTOFF(ex));
+ up_write(&current->mm->mmap_sem);
+ retval = error;
+ if (error != start_addr)
+ goto out;
+
+ len = PAGE_ALIGN(ex.a_text + ex.a_data);
+ bss = ex.a_text + ex.a_data + ex.a_bss;
+ if (bss > len) {
+ down_write(&current->mm->mmap_sem);
+ error = do_brk(start_addr + len, bss - len);
+ up_write(&current->mm->mmap_sem);
+
+ retval = error;
+ if (error != start_addr + len)
+ goto out;
+ }
+ retval = 0;
+out:
+ return retval;
+}
+
+static int __init init_aout_binfmt(void)
+{
+ return register_binfmt(&aout_format);
+}
+
+static void __exit exit_aout_binfmt(void)
+{
+ unregister_binfmt(&aout_format);
+}
+
+EXPORT_NO_SYMBOLS;
+
+module_init(init_aout_binfmt);
+module_exit(exit_aout_binfmt);
+MODULE_LICENSE("GPL");
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
new file mode 100644
index 00000000000000..c01dd6712e2229
--- /dev/null
+++ b/fs/binfmt_elf.c
@@ -0,0 +1,1408 @@
+/*
+ * linux/fs/binfmt_elf.c
+ *
+ * These are the functions used to load ELF format executables as used
+ * on SVr4 machines. Information on the format may be found in the book
+ * "UNIX SYSTEM V RELEASE 4 Programmers Guide: Ansi C and Programming Support
+ * Tools".
+ *
+ * Copyright 1993, 1994: Eric Youngdale (ericy@cais.com).
+ */
+
+#include <linux/module.h>
+
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/a.out.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/binfmts.h>
+#include <linux/string.h>
+#include <linux/file.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/shm.h>
+#include <linux/personality.h>
+#include <linux/elfcore.h>
+#include <linux/init.h>
+#include <linux/highuid.h>
+#include <linux/smp_lock.h>
+#include <linux/compiler.h>
+#include <linux/highmem.h>
+
+#include <asm/uaccess.h>
+#include <asm/param.h>
+#include <asm/pgalloc.h>
+
+#define DLINFO_ITEMS 13
+
+#include <linux/elf.h>
+
+static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs);
+static int load_elf_library(struct file*);
+static unsigned long elf_map (struct file *, unsigned long, struct elf_phdr *, int, int);
+extern int dump_fpu (struct pt_regs *, elf_fpregset_t *);
+extern void dump_thread(struct pt_regs *, struct user *);
+
+#ifndef elf_addr_t
+#define elf_addr_t unsigned long
+#define elf_caddr_t char *
+#endif
+
+/*
+ * If we don't support core dumping, then supply a NULL so we
+ * don't even try.
+ */
+#ifdef USE_ELF_CORE_DUMP
+static int elf_core_dump(long signr, struct pt_regs * regs, struct file * file);
+#else
+#define elf_core_dump NULL
+#endif
+
+#if ELF_EXEC_PAGESIZE > PAGE_SIZE
+# define ELF_MIN_ALIGN ELF_EXEC_PAGESIZE
+#else
+# define ELF_MIN_ALIGN PAGE_SIZE
+#endif
+
+#define ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(ELF_MIN_ALIGN-1))
+#define ELF_PAGEOFFSET(_v) ((_v) & (ELF_MIN_ALIGN-1))
+#define ELF_PAGEALIGN(_v) (((_v) + ELF_MIN_ALIGN - 1) & ~(ELF_MIN_ALIGN - 1))
+
+static struct linux_binfmt elf_format = {
+ NULL, THIS_MODULE, load_elf_binary, load_elf_library, elf_core_dump, ELF_EXEC_PAGESIZE
+};
+
+#define BAD_ADDR(x) ((unsigned long)(x) > TASK_SIZE)
+
+static int set_brk(unsigned long start, unsigned long end)
+{
+ start = ELF_PAGEALIGN(start);
+ end = ELF_PAGEALIGN(end);
+ if (end > start) {
+ unsigned long addr;
+ down_write(&current->mm->mmap_sem);
+ addr = do_brk(start, end - start);
+ up_write(&current->mm->mmap_sem);
+ if (BAD_ADDR(addr))
+ return addr;
+ }
+ current->mm->start_brk = current->mm->brk = end;
+ return 0;
+}
+
+
+/* We need to explicitly zero any fractional pages
+ after the data section (i.e. bss). This would
+ contain the junk from the file that should not
+ be in memory */
+
+
+static void padzero(unsigned long elf_bss)
+{
+ unsigned long nbyte;
+
+ nbyte = ELF_PAGEOFFSET(elf_bss);
+ if (nbyte) {
+ nbyte = ELF_MIN_ALIGN - nbyte;
+ clear_user((void *) elf_bss, nbyte);
+ }
+}
+
+static elf_addr_t *
+create_elf_tables(char *p, int argc, int envc,
+ struct elfhdr * exec,
+ unsigned long load_addr,
+ unsigned long load_bias,
+ unsigned long interp_load_addr, int ibcs)
+{
+ elf_caddr_t *argv;
+ elf_caddr_t *envp;
+ elf_addr_t *sp, *csp;
+ char *k_platform, *u_platform;
+ long hwcap;
+ size_t platform_len = 0;
+ size_t len;
+
+ /*
+ * Get hold of platform and hardware capabilities masks for
+ * the machine we are running on. In some cases (Sparc),
+ * this info is impossible to get, in others (i386) it is
+ * merely difficult.
+ */
+
+ hwcap = ELF_HWCAP;
+ k_platform = ELF_PLATFORM;
+
+ if (k_platform) {
+ platform_len = strlen(k_platform) + 1;
+ u_platform = p - platform_len;
+ __copy_to_user(u_platform, k_platform, platform_len);
+ } else
+ u_platform = p;
+
+#if defined(__i386__) && defined(CONFIG_SMP)
+ /*
+ * In some cases (e.g. Hyper-Threading), we want to avoid L1 evictions
+ * by the processes running on the same package. One thing we can do
+ * is to shuffle the initial stack for them.
+ *
+ * The conditionals here are unneeded, but kept in to make the
+ * code behaviour the same as pre change unless we have hyperthreaded
+ * processors. This keeps Mr Marcelo Person happier but should be
+ * removed for 2.5
+ */
+
+ if(smp_num_siblings > 1)
+ u_platform = u_platform - ((current->pid % 64) << 7);
+#endif
+
+ /*
+ * Force 16 byte _final_ alignment here for generality.
+ */
+ sp = (elf_addr_t *)(~15UL & (unsigned long)(u_platform));
+ csp = sp;
+ csp -= (1+DLINFO_ITEMS)*2 + (k_platform ? 2 : 0);
+#ifdef DLINFO_ARCH_ITEMS
+ csp -= DLINFO_ARCH_ITEMS*2;
+#endif
+ csp -= envc+1;
+ csp -= argc+1;
+ csp -= (!ibcs ? 3 : 1); /* argc itself */
+ if ((unsigned long)csp & 15UL)
+ sp -= ((unsigned long)csp & 15UL) / sizeof(*sp);
+
+ /*
+ * Put the ELF interpreter info on the stack
+ */
+#define NEW_AUX_ENT(nr, id, val) \
+ __put_user ((id), sp+(nr*2)); \
+ __put_user ((val), sp+(nr*2+1)); \
+
+ sp -= 2;
+ NEW_AUX_ENT(0, AT_NULL, 0);
+ if (k_platform) {
+ sp -= 2;
+ NEW_AUX_ENT(0, AT_PLATFORM, (elf_addr_t)(unsigned long) u_platform);
+ }
+ sp -= DLINFO_ITEMS*2;
+ NEW_AUX_ENT( 0, AT_HWCAP, hwcap);
+ NEW_AUX_ENT( 1, AT_PAGESZ, ELF_EXEC_PAGESIZE);
+ NEW_AUX_ENT( 2, AT_CLKTCK, CLOCKS_PER_SEC);
+ NEW_AUX_ENT( 3, AT_PHDR, load_addr + exec->e_phoff);
+ NEW_AUX_ENT( 4, AT_PHENT, sizeof (struct elf_phdr));
+ NEW_AUX_ENT( 5, AT_PHNUM, exec->e_phnum);
+ NEW_AUX_ENT( 6, AT_BASE, interp_load_addr);
+ NEW_AUX_ENT( 7, AT_FLAGS, 0);
+ NEW_AUX_ENT( 8, AT_ENTRY, load_bias + exec->e_entry);
+ NEW_AUX_ENT( 9, AT_UID, (elf_addr_t) current->uid);
+ NEW_AUX_ENT(10, AT_EUID, (elf_addr_t) current->euid);
+ NEW_AUX_ENT(11, AT_GID, (elf_addr_t) current->gid);
+ NEW_AUX_ENT(12, AT_EGID, (elf_addr_t) current->egid);
+#ifdef ARCH_DLINFO
+ /*
+ * ARCH_DLINFO must come last so platform specific code can enforce
+ * special alignment requirements on the AUXV if necessary (eg. PPC).
+ */
+ ARCH_DLINFO;
+#endif
+#undef NEW_AUX_ENT
+
+ sp -= envc+1;
+ envp = (elf_caddr_t *) sp;
+ sp -= argc+1;
+ argv = (elf_caddr_t *) sp;
+ if (!ibcs) {
+ __put_user((elf_addr_t)(unsigned long) envp,--sp);
+ __put_user((elf_addr_t)(unsigned long) argv,--sp);
+ }
+
+ __put_user((elf_addr_t)argc,--sp);
+ current->mm->arg_start = current->mm->arg_end = (unsigned long) p;
+ while (argc-->0) {
+ __put_user((elf_caddr_t)(unsigned long)p,argv++);
+ len = strnlen_user(p, PAGE_SIZE*MAX_ARG_PAGES);
+ if (!len || len > PAGE_SIZE*MAX_ARG_PAGES)
+ return NULL;
+ p += len;
+ }
+ __put_user(NULL, argv);
+ current->mm->arg_end = current->mm->env_start = (unsigned long) p;
+ while (envc-->0) {
+ __put_user((elf_caddr_t)(unsigned long)p,envp++);
+ len = strnlen_user(p, PAGE_SIZE*MAX_ARG_PAGES);
+ if (!len || len > PAGE_SIZE*MAX_ARG_PAGES)
+ return NULL;
+ p += len;
+ }
+ __put_user(NULL, envp);
+ current->mm->env_end = (unsigned long) p;
+ return sp;
+}
+
+#ifndef elf_map
+
+static inline unsigned long
+elf_map (struct file *filep, unsigned long addr, struct elf_phdr *eppnt, int prot, int type)
+{
+ unsigned long map_addr;
+
+ down_write(&current->mm->mmap_sem);
+ map_addr = do_mmap(filep, ELF_PAGESTART(addr),
+ eppnt->p_filesz + ELF_PAGEOFFSET(eppnt->p_vaddr), prot, type,
+ eppnt->p_offset - ELF_PAGEOFFSET(eppnt->p_vaddr));
+ up_write(&current->mm->mmap_sem);
+ return(map_addr);
+}
+
+#endif /* !elf_map */
+
+/* This is much more generalized than the library routine read function,
+ so we keep this separate. Technically the library read function
+ is only provided so that we can read a.out libraries that have
+ an ELF header */
+
+static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex,
+ struct file * interpreter,
+ unsigned long *interp_load_addr)
+{
+ struct elf_phdr *elf_phdata;
+ struct elf_phdr *eppnt;
+ unsigned long load_addr = 0;
+ int load_addr_set = 0;
+ unsigned long last_bss = 0, elf_bss = 0;
+ unsigned long error = ~0UL;
+ int retval, i, size;
+
+ /* First of all, some simple consistency checks */
+ if (interp_elf_ex->e_type != ET_EXEC &&
+ interp_elf_ex->e_type != ET_DYN)
+ goto out;
+ if (!elf_check_arch(interp_elf_ex))
+ goto out;
+ if (!interpreter->f_op || !interpreter->f_op->mmap)
+ goto out;
+
+ /*
+ * If the size of this structure has changed, then punt, since
+ * we will be doing the wrong thing.
+ */
+ if (interp_elf_ex->e_phentsize != sizeof(struct elf_phdr))
+ goto out;
+
+ if (interp_elf_ex->e_phnum < 1 ||
+ interp_elf_ex->e_phnum > 65536U / sizeof(struct elf_phdr))
+ goto out;
+
+ /* Now read in all of the header information */
+
+ size = sizeof(struct elf_phdr) * interp_elf_ex->e_phnum;
+ if (size > ELF_MIN_ALIGN)
+ goto out;
+ elf_phdata = (struct elf_phdr *) kmalloc(size, GFP_KERNEL);
+ if (!elf_phdata)
+ goto out;
+
+ retval = kernel_read(interpreter,interp_elf_ex->e_phoff,(char *)elf_phdata,size);
+ error = -EIO;
+ if (retval != size) {
+ if (retval < 0)
+ error = retval;
+ goto out_close;
+ }
+
+ eppnt = elf_phdata;
+ for (i=0; i<interp_elf_ex->e_phnum; i++, eppnt++) {
+ if (eppnt->p_type == PT_LOAD) {
+ int elf_type = MAP_PRIVATE | MAP_DENYWRITE;
+ int elf_prot = 0;
+ unsigned long vaddr = 0;
+ unsigned long k, map_addr;
+
+ if (eppnt->p_flags & PF_R) elf_prot = PROT_READ;
+ if (eppnt->p_flags & PF_W) elf_prot |= PROT_WRITE;
+ if (eppnt->p_flags & PF_X) elf_prot |= PROT_EXEC;
+ vaddr = eppnt->p_vaddr;
+ if (interp_elf_ex->e_type == ET_EXEC || load_addr_set)
+ elf_type |= MAP_FIXED;
+
+ map_addr = elf_map(interpreter, load_addr + vaddr, eppnt, elf_prot, elf_type);
+ if (BAD_ADDR(map_addr))
+ goto out_close;
+
+ if (!load_addr_set && interp_elf_ex->e_type == ET_DYN) {
+ load_addr = map_addr - ELF_PAGESTART(vaddr);
+ load_addr_set = 1;
+ }
+
+ /*
+ * Check to see if the section's size will overflow the
+ * allowed task size. Note that p_filesz must always be
+ * <= p_memsize so it is only necessary to check p_memsz.
+ */
+ k = load_addr + eppnt->p_vaddr;
+ if (k > TASK_SIZE || eppnt->p_filesz > eppnt->p_memsz ||
+ eppnt->p_memsz > TASK_SIZE || TASK_SIZE - eppnt->p_memsz < k) {
+ error = -ENOMEM;
+ goto out_close;
+ }
+
+ /*
+ * Find the end of the file mapping for this phdr, and keep
+ * track of the largest address we see for this.
+ */
+ k = load_addr + eppnt->p_vaddr + eppnt->p_filesz;
+ if (k > elf_bss)
+ elf_bss = k;
+
+ /*
+ * Do the same thing for the memory mapping - between
+ * elf_bss and last_bss is the bss section.
+ */
+ k = load_addr + eppnt->p_memsz + eppnt->p_vaddr;
+ if (k > last_bss)
+ last_bss = k;
+ }
+ }
+
+ /* Now use mmap to map the library into memory. */
+
+ /*
+ * Now fill out the bss section. First pad the last page up
+ * to the page boundary, and then perform a mmap to make sure
+ * that there are zero-mapped pages up to and including the
+ * last bss page.
+ */
+ padzero(elf_bss);
+ elf_bss = ELF_PAGESTART(elf_bss + ELF_MIN_ALIGN - 1); /* What we have mapped so far */
+
+ /* Map the last of the bss segment */
+ if (last_bss > elf_bss) {
+ down_write(&current->mm->mmap_sem);
+ error = do_brk(elf_bss, last_bss - elf_bss);
+ up_write(&current->mm->mmap_sem);
+ if (BAD_ADDR(error))
+ goto out_close;
+ }
+
+ *interp_load_addr = load_addr;
+ /*
+ * XXX: is everything deallocated properly if this happens
+ * to be ~0UL (that is, we succeeded, but the header is broken
+ * and thus the caller will think that we failed)? We'd better
+ * switch to out-of-band error reporting.
+ */
+ error = ((unsigned long) interp_elf_ex->e_entry) + load_addr;
+
+out_close:
+ kfree(elf_phdata);
+out:
+ return error;
+}
+
+static unsigned long load_aout_interp(struct exec * interp_ex,
+ struct file * interpreter)
+{
+ unsigned long text_data, elf_entry = ~0UL;
+ char * addr;
+ loff_t offset;
+
+ current->mm->end_code = interp_ex->a_text;
+ text_data = interp_ex->a_text + interp_ex->a_data;
+ current->mm->end_data = text_data;
+ current->mm->brk = interp_ex->a_bss + text_data;
+
+ switch (N_MAGIC(*interp_ex)) {
+ case OMAGIC:
+ offset = 32;
+ addr = (char *) 0;
+ break;
+ case ZMAGIC:
+ case QMAGIC:
+ offset = N_TXTOFF(*interp_ex);
+ addr = (char *) N_TXTADDR(*interp_ex);
+ break;
+ default:
+ goto out;
+ }
+
+ down_write(&current->mm->mmap_sem);
+ do_brk(0, text_data);
+ up_write(&current->mm->mmap_sem);
+
+ if (!interpreter->f_op || !interpreter->f_op->read)
+ goto out;
+ if (interpreter->f_op->read(interpreter, addr, text_data, &offset) < 0)
+ goto out;
+ flush_icache_range((unsigned long)addr,
+ (unsigned long)addr + text_data);
+
+ down_write(&current->mm->mmap_sem);
+ do_brk(ELF_PAGESTART(text_data + ELF_MIN_ALIGN - 1),
+ interp_ex->a_bss);
+ up_write(&current->mm->mmap_sem);
+
+ elf_entry = interp_ex->a_entry;
+
+out:
+ return elf_entry;
+}
+
+/*
+ * These are the functions used to load ELF style executables and shared
+ * libraries. There is no binary dependent code anywhere else.
+ */
+
+#define INTERPRETER_NONE 0
+#define INTERPRETER_AOUT 1
+#define INTERPRETER_ELF 2
+
+
+static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
+{
+ struct file *interpreter = NULL; /* to shut gcc up */
+ unsigned long load_addr = 0, load_bias = 0;
+ int load_addr_set = 0;
+ char * elf_interpreter = NULL;
+ unsigned int interpreter_type = INTERPRETER_NONE;
+ unsigned char ibcs2_interpreter = 0;
+ unsigned long error;
+ struct elf_phdr * elf_ppnt, *elf_phdata;
+ unsigned long elf_bss, k, elf_brk;
+ int elf_exec_fileno;
+ int retval, i;
+ unsigned int size;
+ unsigned long elf_entry, interp_load_addr = 0;
+ unsigned long start_code, end_code, start_data, end_data;
+ unsigned long reloc_func_desc = 0;
+ struct elfhdr elf_ex;
+ struct elfhdr interp_elf_ex;
+ struct exec interp_ex;
+ char passed_fileno[6];
+ struct files_struct *files;
+
+ /* Get the exec-header */
+ elf_ex = *((struct elfhdr *) bprm->buf);
+
+ retval = -ENOEXEC;
+ /* First of all, some simple consistency checks */
+ if (memcmp(elf_ex.e_ident, ELFMAG, SELFMAG) != 0)
+ goto out;
+
+ if (elf_ex.e_type != ET_EXEC && elf_ex.e_type != ET_DYN)
+ goto out;
+ if (!elf_check_arch(&elf_ex))
+ goto out;
+ if (!bprm->file->f_op||!bprm->file->f_op->mmap)
+ goto out;
+
+ /* Now read in all of the header information */
+
+ if (elf_ex.e_phentsize != sizeof(struct elf_phdr))
+ goto out;
+ if (elf_ex.e_phnum < 1 ||
+ elf_ex.e_phnum > 65536U / sizeof(struct elf_phdr))
+ goto out;
+ size = elf_ex.e_phnum * sizeof(struct elf_phdr);
+ retval = -ENOMEM;
+ elf_phdata = (struct elf_phdr *) kmalloc(size, GFP_KERNEL);
+ if (!elf_phdata)
+ goto out;
+
+ retval = kernel_read(bprm->file, elf_ex.e_phoff, (char *) elf_phdata, size);
+ if (retval != size) {
+ if (retval >= 0)
+ retval = -EIO;
+ goto out_free_ph;
+ }
+
+ files = current->files; /* Refcounted so ok */
+ retval = unshare_files();
+ if (retval < 0)
+ goto out_free_ph;
+ if (files == current->files) {
+ put_files_struct(files);
+ files = NULL;
+ }
+
+ /* exec will make our files private anyway, but for the a.out
+ loader stuff we need to do it earlier */
+
+ retval = get_unused_fd();
+ if (retval < 0)
+ goto out_free_fh;
+ get_file(bprm->file);
+ fd_install(elf_exec_fileno = retval, bprm->file);
+
+ elf_ppnt = elf_phdata;
+ elf_bss = 0;
+ elf_brk = 0;
+
+ start_code = ~0UL;
+ end_code = 0;
+ start_data = 0;
+ end_data = 0;
+
+ for (i = 0; i < elf_ex.e_phnum; i++) {
+ if (elf_ppnt->p_type == PT_INTERP) {
+ /* This is the program interpreter used for
+ * shared libraries - for now assume that this
+ * is an a.out format binary
+ */
+
+ retval = -ENOEXEC;
+ if (elf_ppnt->p_filesz > PATH_MAX ||
+ elf_ppnt->p_filesz < 2)
+ goto out_free_file;
+
+ retval = -ENOMEM;
+ elf_interpreter = (char *) kmalloc(elf_ppnt->p_filesz,
+ GFP_KERNEL);
+ if (!elf_interpreter)
+ goto out_free_file;
+
+ retval = kernel_read(bprm->file, elf_ppnt->p_offset,
+ elf_interpreter,
+ elf_ppnt->p_filesz);
+ if (retval != elf_ppnt->p_filesz) {
+ if (retval >= 0)
+ retval = -EIO;
+ goto out_free_interp;
+ }
+ /* make sure path is NULL terminated */
+ retval = -ENOEXEC;
+ if (elf_interpreter[elf_ppnt->p_filesz - 1] != '\0')
+ goto out_free_interp;
+
+ /* If the program interpreter is one of these two,
+ * then assume an iBCS2 image. Otherwise assume
+ * a native linux image.
+ */
+ if (strcmp(elf_interpreter,"/usr/lib/libc.so.1") == 0 ||
+ strcmp(elf_interpreter,"/usr/lib/ld.so.1") == 0)
+ ibcs2_interpreter = 1;
+#if 0
+ printk("Using ELF interpreter %s\n", elf_interpreter);
+#endif
+
+ SET_PERSONALITY(elf_ex, ibcs2_interpreter);
+
+ interpreter = open_exec(elf_interpreter);
+ retval = PTR_ERR(interpreter);
+ if (IS_ERR(interpreter))
+ goto out_free_interp;
+ retval = kernel_read(interpreter, 0, bprm->buf, BINPRM_BUF_SIZE);
+ if (retval != BINPRM_BUF_SIZE) {
+ if (retval >= 0)
+ retval = -EIO;
+ goto out_free_dentry;
+ }
+
+ /* Get the exec headers */
+ interp_ex = *((struct exec *) bprm->buf);
+ interp_elf_ex = *((struct elfhdr *) bprm->buf);
+ break;
+ }
+ elf_ppnt++;
+ }
+
+ /* Some simple consistency checks for the interpreter */
+ if (elf_interpreter) {
+ interpreter_type = INTERPRETER_ELF | INTERPRETER_AOUT;
+
+ /* Now figure out which format our binary is */
+ if ((N_MAGIC(interp_ex) != OMAGIC) &&
+ (N_MAGIC(interp_ex) != ZMAGIC) &&
+ (N_MAGIC(interp_ex) != QMAGIC))
+ interpreter_type = INTERPRETER_ELF;
+
+ if (memcmp(interp_elf_ex.e_ident, ELFMAG, SELFMAG) != 0)
+ interpreter_type &= ~INTERPRETER_ELF;
+
+ retval = -ELIBBAD;
+ if (!interpreter_type)
+ goto out_free_dentry;
+
+ /* Make sure only one type was selected */
+ if ((interpreter_type & INTERPRETER_ELF) &&
+ interpreter_type != INTERPRETER_ELF) {
+ // FIXME - ratelimit this before re-enabling
+ // printk(KERN_WARNING "ELF: Ambiguous type, using ELF\n");
+ interpreter_type = INTERPRETER_ELF;
+ }
+ /* Verify the interpreter has a valid arch */
+ if ((interpreter_type == INTERPRETER_ELF) &&
+ !elf_check_arch(&interp_elf_ex))
+ goto out_free_dentry;
+ } else {
+ /* Executables without an interpreter also need a personality */
+ SET_PERSONALITY(elf_ex, ibcs2_interpreter);
+ }
+
+ /* OK, we are done with that, now set up the arg stuff,
+ and then start this sucker up */
+
+ if (!bprm->sh_bang) {
+ char * passed_p;
+
+ if (interpreter_type == INTERPRETER_AOUT) {
+ sprintf(passed_fileno, "%d", elf_exec_fileno);
+ passed_p = passed_fileno;
+
+ if (elf_interpreter) {
+ retval = copy_strings_kernel(1,&passed_p,bprm);
+ if (retval)
+ goto out_free_dentry;
+ bprm->argc++;
+ }
+ }
+ }
+
+ /* Flush all traces of the currently running executable */
+ retval = flush_old_exec(bprm);
+ if (retval)
+ goto out_free_dentry;
+
+ /* Discard our unneeded old files struct */
+ if (files) {
+ steal_locks(files);
+ put_files_struct(files);
+ files = NULL;
+ }
+
+ /* OK, This is the point of no return */
+ current->mm->start_data = 0;
+ current->mm->end_data = 0;
+ current->mm->end_code = 0;
+ current->mm->mmap = NULL;
+ current->flags &= ~PF_FORKNOEXEC;
+ elf_entry = (unsigned long) elf_ex.e_entry;
+
+ /* Do this so that we can load the interpreter, if need be. We will
+ change some of these later */
+ current->mm->rss = 0;
+ retval = setup_arg_pages(bprm);
+ if (retval < 0) {
+ send_sig(SIGKILL, current, 0);
+ return retval;
+ }
+
+ current->mm->start_stack = bprm->p;
+
+ /* Now we do a little grungy work by mmaping the ELF image into
+ the correct location in memory. At this point, we assume that
+ the image should be loaded at fixed address, not at a variable
+ address. */
+
+ for(i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum; i++, elf_ppnt++) {
+ int elf_prot = 0, elf_flags;
+ unsigned long vaddr;
+
+ if (elf_ppnt->p_type != PT_LOAD)
+ continue;
+
+ if (unlikely (elf_brk > elf_bss)) {
+ unsigned long nbyte;
+
+ /* There was a PT_LOAD segment with p_memsz > p_filesz
+ before this one. Map anonymous pages, if needed,
+ and clear the area. */
+ retval = set_brk (elf_bss + load_bias,
+ elf_brk + load_bias);
+ if (retval) {
+ send_sig(SIGKILL, current, 0);
+ goto out_free_dentry;
+ }
+ nbyte = ELF_PAGEOFFSET(elf_bss);
+ if (nbyte) {
+ nbyte = ELF_MIN_ALIGN - nbyte;
+ if (nbyte > elf_brk - elf_bss)
+ nbyte = elf_brk - elf_bss;
+ clear_user((void *) elf_bss + load_bias, nbyte);
+ }
+ }
+
+ if (elf_ppnt->p_flags & PF_R) elf_prot |= PROT_READ;
+ if (elf_ppnt->p_flags & PF_W) elf_prot |= PROT_WRITE;
+ if (elf_ppnt->p_flags & PF_X) elf_prot |= PROT_EXEC;
+
+ elf_flags = MAP_PRIVATE|MAP_DENYWRITE|MAP_EXECUTABLE;
+
+ vaddr = elf_ppnt->p_vaddr;
+ if (elf_ex.e_type == ET_EXEC || load_addr_set) {
+ elf_flags |= MAP_FIXED;
+ } else if (elf_ex.e_type == ET_DYN) {
+ /* Try and get dynamic programs out of the way of the default mmap
+ base, as well as whatever program they might try to exec. This
+ is because the brk will follow the loader, and is not movable. */
+ load_bias = ELF_PAGESTART(ELF_ET_DYN_BASE - vaddr);
+ }
+
+ error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt, elf_prot, elf_flags);
+ if (BAD_ADDR(error)) {
+ send_sig(SIGKILL, current, 0);
+ goto out_free_dentry;
+ }
+
+ if (!load_addr_set) {
+ load_addr_set = 1;
+ load_addr = (elf_ppnt->p_vaddr - elf_ppnt->p_offset);
+ if (elf_ex.e_type == ET_DYN) {
+ load_bias += error -
+ ELF_PAGESTART(load_bias + vaddr);
+ load_addr += load_bias;
+ reloc_func_desc = load_addr;
+ }
+ }
+ k = elf_ppnt->p_vaddr;
+ if (k < start_code) start_code = k;
+ if (start_data < k) start_data = k;
+
+ /*
+ * Check to see if the section's size will overflow the
+ * allowed task size. Note that p_filesz must always be
+ * <= p_memsz so it is only necessary to check p_memsz.
+ */
+ if (k > TASK_SIZE || elf_ppnt->p_filesz > elf_ppnt->p_memsz ||
+ elf_ppnt->p_memsz > TASK_SIZE ||
+ TASK_SIZE - elf_ppnt->p_memsz < k) {
+ /* set_brk can never work. Avoid overflows. */
+ send_sig(SIGKILL, current, 0);
+ goto out_free_dentry;
+ }
+
+ k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz;
+
+ if (k > elf_bss)
+ elf_bss = k;
+ if ((elf_ppnt->p_flags & PF_X) && end_code < k)
+ end_code = k;
+ if (end_data < k)
+ end_data = k;
+ k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz;
+ if (k > elf_brk)
+ elf_brk = k;
+ }
+
+ elf_entry += load_bias;
+ elf_bss += load_bias;
+ elf_brk += load_bias;
+ start_code += load_bias;
+ end_code += load_bias;
+ start_data += load_bias;
+ end_data += load_bias;
+
+ /* Calling set_brk effectively mmaps the pages that we need
+ * for the bss and break sections. We must do this before
+ * mapping in the interpreter, to make sure it doesn't wind
+ * up getting placed where the bss needs to go.
+ */
+ retval = set_brk(elf_bss, elf_brk);
+ if (retval) {
+ send_sig(SIGKILL, current, 0);
+ goto out_free_dentry;
+ }
+ padzero(elf_bss);
+
+ if (elf_interpreter) {
+ if (interpreter_type == INTERPRETER_AOUT)
+ elf_entry = load_aout_interp(&interp_ex,
+ interpreter);
+ else
+ elf_entry = load_elf_interp(&interp_elf_ex,
+ interpreter,
+ &interp_load_addr);
+ if (BAD_ADDR(elf_entry)) {
+ printk(KERN_ERR "Unable to load interpreter %.128s\n",
+ elf_interpreter);
+ force_sig(SIGSEGV, current);
+ retval = -ENOEXEC; /* Nobody gets to see this, but.. */
+ goto out_free_dentry;
+ }
+ reloc_func_desc = interp_load_addr;
+
+ allow_write_access(interpreter);
+ fput(interpreter);
+ kfree(elf_interpreter);
+ }
+
+ kfree(elf_phdata);
+
+ if (interpreter_type != INTERPRETER_AOUT)
+ sys_close(elf_exec_fileno);
+
+ set_binfmt(&elf_format);
+
+ compute_creds(bprm);
+ current->flags &= ~PF_FORKNOEXEC;
+ bprm->p = (unsigned long)
+ create_elf_tables((char *)bprm->p,
+ bprm->argc,
+ bprm->envc,
+ &elf_ex,
+ load_addr, load_bias,
+ interp_load_addr,
+ (interpreter_type == INTERPRETER_AOUT ? 0 : 1));
+ /* N.B. passed_fileno might not be initialized? */
+ if (interpreter_type == INTERPRETER_AOUT)
+ current->mm->arg_start += strlen(passed_fileno) + 1;
+ current->mm->start_brk = current->mm->brk = elf_brk;
+ current->mm->end_code = end_code;
+ current->mm->start_code = start_code;
+ current->mm->start_data = start_data;
+ current->mm->end_data = end_data;
+ current->mm->start_stack = bprm->p;
+
+#if 0
+ printk("(start_brk) %lx\n" , (long) current->mm->start_brk);
+ printk("(end_code) %lx\n" , (long) current->mm->end_code);
+ printk("(start_code) %lx\n" , (long) current->mm->start_code);
+ printk("(start_data) %lx\n" , (long) current->mm->start_data);
+ printk("(end_data) %lx\n" , (long) current->mm->end_data);
+ printk("(start_stack) %lx\n" , (long) current->mm->start_stack);
+ printk("(brk) %lx\n" , (long) current->mm->brk);
+#endif
+
+ if (current->personality & MMAP_PAGE_ZERO) {
+ /* Why this, you ask??? Well SVr4 maps page 0 as read-only,
+ and some applications "depend" upon this behavior.
+ Since we do not have the power to recompile these, we
+ emulate the SVr4 behavior. Sigh. */
+ /* N.B. Shouldn't the size here be PAGE_SIZE?? */
+ down_write(&current->mm->mmap_sem);
+ error = do_mmap(NULL, 0, 4096, PROT_READ | PROT_EXEC,
+ MAP_FIXED | MAP_PRIVATE, 0);
+ up_write(&current->mm->mmap_sem);
+ }
+
+#ifdef ELF_PLAT_INIT
+ /*
+ * The ABI may specify that certain registers be set up in special
+ * ways (on i386 %edx is the address of a DT_FINI function, for
+ * example. In addition, it may also specify (eg, PowerPC64 ELF)
+ * that the e_entry field is the address of the function descriptor
+ * for the startup routine, rather than the address of the startup
+ * routine itself. This macro performs whatever initialization to
+ * the regs structure is required as well as any relocations to the
+ * function descriptor entries when executing dynamically linked apps.
+ */
+ ELF_PLAT_INIT(regs, reloc_func_desc);
+#endif
+
+ start_thread(regs, elf_entry, bprm->p);
+ if (current->ptrace & PT_PTRACED)
+ send_sig(SIGTRAP, current, 0);
+ retval = 0;
+out:
+ return retval;
+
+ /* error cleanup */
+out_free_dentry:
+ allow_write_access(interpreter);
+ if (interpreter)
+ fput(interpreter);
+out_free_interp:
+ if (elf_interpreter)
+ kfree(elf_interpreter);
+out_free_file:
+ sys_close(elf_exec_fileno);
+out_free_fh:
+ if (files) {
+ put_files_struct(current->files);
+ current->files = files;
+ }
+out_free_ph:
+ kfree(elf_phdata);
+ goto out;
+}
+
+/* This is really simpleminded and specialized - we are loading an
+ a.out library that is given an ELF header. */
+
+static int load_elf_library(struct file *file)
+{
+ struct elf_phdr *elf_phdata;
+ struct elf_phdr *eppnt;
+ unsigned long elf_bss, bss, len;
+ int retval, error, i, j;
+ struct elfhdr elf_ex;
+
+ error = -ENOEXEC;
+ retval = kernel_read(file, 0, (char *) &elf_ex, sizeof(elf_ex));
+ if (retval != sizeof(elf_ex))
+ goto out;
+
+ if (memcmp(elf_ex.e_ident, ELFMAG, SELFMAG) != 0)
+ goto out;
+
+ /* First of all, some simple consistency checks */
+ if (elf_ex.e_type != ET_EXEC || elf_ex.e_phnum > 2 ||
+ !elf_check_arch(&elf_ex) || !file->f_op || !file->f_op->mmap)
+ goto out;
+
+ /* Now read in all of the header information */
+
+ j = sizeof(struct elf_phdr) * elf_ex.e_phnum;
+ /* j < ELF_MIN_ALIGN because elf_ex.e_phnum <= 2 */
+
+ error = -ENOMEM;
+ elf_phdata = kmalloc(j, GFP_KERNEL);
+ if (!elf_phdata)
+ goto out;
+
+ eppnt = elf_phdata;
+ error = -ENOEXEC;
+ retval = kernel_read(file, elf_ex.e_phoff, (char *)eppnt, j);
+ if (retval != j)
+ goto out_free_ph;
+
+ for (j = 0, i = 0; i<elf_ex.e_phnum; i++)
+ if ((eppnt + i)->p_type == PT_LOAD) j++;
+ if (j != 1)
+ goto out_free_ph;
+
+ while (eppnt->p_type != PT_LOAD)
+ eppnt++;
+
+ /* Now use mmap to map the library into memory. */
+ down_write(&current->mm->mmap_sem);
+ error = do_mmap(file,
+ ELF_PAGESTART(eppnt->p_vaddr),
+ (eppnt->p_filesz +
+ ELF_PAGEOFFSET(eppnt->p_vaddr)),
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE,
+ (eppnt->p_offset -
+ ELF_PAGEOFFSET(eppnt->p_vaddr)));
+ up_write(&current->mm->mmap_sem);
+ if (error != ELF_PAGESTART(eppnt->p_vaddr))
+ goto out_free_ph;
+
+ elf_bss = eppnt->p_vaddr + eppnt->p_filesz;
+ padzero(elf_bss);
+
+ len = ELF_PAGESTART(eppnt->p_filesz + eppnt->p_vaddr + ELF_MIN_ALIGN - 1);
+ bss = eppnt->p_memsz + eppnt->p_vaddr;
+ if (bss > len) {
+ down_write(&current->mm->mmap_sem);
+ do_brk(len, bss - len);
+ up_write(&current->mm->mmap_sem);
+ }
+ error = 0;
+
+out_free_ph:
+ kfree(elf_phdata);
+out:
+ return error;
+}
+
+/*
+ * Note that some platforms still use traditional core dumps and not
+ * the ELF core dump. Each platform can select it as appropriate.
+ */
+#ifdef USE_ELF_CORE_DUMP
+
+/*
+ * ELF core dumper
+ *
+ * Modelled on fs/exec.c:aout_core_dump()
+ * Jeremy Fitzhardinge <jeremy@sw.oz.au>
+ */
+/*
+ * These are the only things you should do on a core-file: use only these
+ * functions to write out all the necessary info.
+ */
+static int dump_write(struct file *file, const void *addr, int nr)
+{
+ return file->f_op->write(file, addr, nr, &file->f_pos) == nr;
+}
+
+static int dump_seek(struct file *file, off_t off)
+{
+ if (file->f_op->llseek) {
+ if (file->f_op->llseek(file, off, 0) != off)
+ return 0;
+ } else
+ file->f_pos = off;
+ return 1;
+}
+
+/*
+ * Decide whether a segment is worth dumping; default is yes to be
+ * sure (missing info is worse than too much; etc).
+ * Personally I'd include everything, and use the coredump limit...
+ *
+ * I think we should skip something. But I am not sure how. H.J.
+ */
+static inline int maydump(struct vm_area_struct *vma)
+{
+ /*
+ * If we may not read the contents, don't allow us to dump
+ * them either. "dump_write()" can't handle it anyway.
+ */
+ if (!(vma->vm_flags & VM_READ))
+ return 0;
+
+ /* Do not dump I/O mapped devices! -DaveM */
+ if (vma->vm_flags & VM_IO)
+ return 0;
+#if 1
+ if (vma->vm_flags & (VM_WRITE|VM_GROWSUP|VM_GROWSDOWN))
+ return 1;
+ if (vma->vm_flags & (VM_READ|VM_EXEC|VM_EXECUTABLE|VM_SHARED))
+ return 0;
+#endif
+ return 1;
+}
+
+#define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
+
+/* An ELF note in memory */
+struct memelfnote
+{
+ const char *name;
+ int type;
+ unsigned int datasz;
+ void *data;
+};
+
+static int notesize(struct memelfnote *en)
+{
+ int sz;
+
+ sz = sizeof(struct elf_note);
+ sz += roundup(strlen(en->name), 4);
+ sz += roundup(en->datasz, 4);
+
+ return sz;
+}
+
+/* #define DEBUG */
+
+#ifdef DEBUG
+static void dump_regs(const char *str, elf_greg_t *r)
+{
+ int i;
+ static const char *regs[] = { "ebx", "ecx", "edx", "esi", "edi", "ebp",
+ "eax", "ds", "es", "fs", "gs",
+ "orig_eax", "eip", "cs",
+ "efl", "uesp", "ss"};
+ printk("Registers: %s\n", str);
+
+ for(i = 0; i < ELF_NGREG; i++)
+ {
+ unsigned long val = r[i];
+ printk(" %-2d %-5s=%08lx %lu\n", i, regs[i], val, val);
+ }
+}
+#endif
+
+#define DUMP_WRITE(addr, nr) \
+ do { if (!dump_write(file, (addr), (nr))) return 0; } while(0)
+#define DUMP_SEEK(off) \
+ do { if (!dump_seek(file, (off))) return 0; } while(0)
+
+static int writenote(struct memelfnote *men, struct file *file)
+{
+ struct elf_note en;
+
+ en.n_namesz = strlen(men->name);
+ en.n_descsz = men->datasz;
+ en.n_type = men->type;
+
+ DUMP_WRITE(&en, sizeof(en));
+ DUMP_WRITE(men->name, en.n_namesz);
+ /* XXX - cast from long long to long to avoid need for libgcc.a */
+ DUMP_SEEK(roundup((unsigned long)file->f_pos, 4)); /* XXX */
+ DUMP_WRITE(men->data, men->datasz);
+ DUMP_SEEK(roundup((unsigned long)file->f_pos, 4)); /* XXX */
+
+ return 1;
+}
+#undef DUMP_WRITE
+#undef DUMP_SEEK
+
+#define DUMP_WRITE(addr, nr) \
+ if ((size += (nr)) > limit || !dump_write(file, (addr), (nr))) \
+ goto end_coredump;
+#define DUMP_SEEK(off) \
+ if (!dump_seek(file, (off))) \
+ goto end_coredump;
+/*
+ * Actual dumper
+ *
+ * This is a two-pass process; first we find the offsets of the bits,
+ * and then they are actually written out. If we run out of core limit
+ * we just truncate.
+ */
+static int elf_core_dump(long signr, struct pt_regs * regs, struct file * file)
+{
+ int has_dumped = 0;
+ mm_segment_t fs;
+ int segs;
+ size_t size = 0;
+ int i;
+ struct vm_area_struct *vma;
+ struct elfhdr elf;
+ off_t offset = 0, dataoff;
+ unsigned long limit = current->rlim[RLIMIT_CORE].rlim_cur;
+ int numnote = 4;
+ struct memelfnote notes[4];
+ struct elf_prstatus prstatus; /* NT_PRSTATUS */
+ elf_fpregset_t fpu; /* NT_PRFPREG */
+ struct elf_prpsinfo psinfo; /* NT_PRPSINFO */
+
+ /* first copy the parameters from user space */
+ memset(&psinfo, 0, sizeof(psinfo));
+ {
+ unsigned int i, len;
+
+ len = current->mm->arg_end - current->mm->arg_start;
+ if (len >= ELF_PRARGSZ)
+ len = ELF_PRARGSZ-1;
+ copy_from_user(&psinfo.pr_psargs,
+ (const char *)current->mm->arg_start, len);
+ for(i = 0; i < len; i++)
+ if (psinfo.pr_psargs[i] == 0)
+ psinfo.pr_psargs[i] = ' ';
+ psinfo.pr_psargs[len] = 0;
+
+ }
+
+ memset(&prstatus, 0, sizeof(prstatus));
+ /*
+ * This transfers the registers from regs into the standard
+ * coredump arrangement, whatever that is.
+ */
+#ifdef ELF_CORE_COPY_REGS
+ ELF_CORE_COPY_REGS(prstatus.pr_reg, regs)
+#else
+ if (sizeof(elf_gregset_t) != sizeof(struct pt_regs))
+ {
+ printk("sizeof(elf_gregset_t) (%ld) != sizeof(struct pt_regs) (%ld)\n",
+ (long)sizeof(elf_gregset_t), (long)sizeof(struct pt_regs));
+ }
+ else
+ *(struct pt_regs *)&prstatus.pr_reg = *regs;
+#endif
+
+ /* now stop all vm operations */
+ down_write(&current->mm->mmap_sem);
+ segs = current->mm->map_count;
+
+#ifdef DEBUG
+ printk("elf_core_dump: %d segs %lu limit\n", segs, limit);
+#endif
+
+ /* Set up header */
+ memcpy(elf.e_ident, ELFMAG, SELFMAG);
+ elf.e_ident[EI_CLASS] = ELF_CLASS;
+ elf.e_ident[EI_DATA] = ELF_DATA;
+ elf.e_ident[EI_VERSION] = EV_CURRENT;
+ memset(elf.e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD);
+
+ elf.e_type = ET_CORE;
+ elf.e_machine = ELF_ARCH;
+ elf.e_version = EV_CURRENT;
+ elf.e_entry = 0;
+ elf.e_phoff = sizeof(elf);
+ elf.e_shoff = 0;
+ elf.e_flags = 0;
+ elf.e_ehsize = sizeof(elf);
+ elf.e_phentsize = sizeof(struct elf_phdr);
+ elf.e_phnum = segs+1; /* Include notes */
+ elf.e_shentsize = 0;
+ elf.e_shnum = 0;
+ elf.e_shstrndx = 0;
+
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ has_dumped = 1;
+ current->flags |= PF_DUMPCORE;
+
+ DUMP_WRITE(&elf, sizeof(elf));
+ offset += sizeof(elf); /* Elf header */
+ offset += (segs+1) * sizeof(struct elf_phdr); /* Program headers */
+
+ /*
+ * Set up the notes in similar form to SVR4 core dumps made
+ * with info from their /proc.
+ */
+
+ notes[0].name = "CORE";
+ notes[0].type = NT_PRSTATUS;
+ notes[0].datasz = sizeof(prstatus);
+ notes[0].data = &prstatus;
+ prstatus.pr_info.si_signo = prstatus.pr_cursig = signr;
+ prstatus.pr_sigpend = current->pending.signal.sig[0];
+ prstatus.pr_sighold = current->blocked.sig[0];
+ psinfo.pr_pid = prstatus.pr_pid = current->pid;
+ psinfo.pr_ppid = prstatus.pr_ppid = current->p_pptr->pid;
+ psinfo.pr_pgrp = prstatus.pr_pgrp = current->pgrp;
+ psinfo.pr_sid = prstatus.pr_sid = current->session;
+ prstatus.pr_utime.tv_sec = CT_TO_SECS(current->times.tms_utime);
+ prstatus.pr_utime.tv_usec = CT_TO_USECS(current->times.tms_utime);
+ prstatus.pr_stime.tv_sec = CT_TO_SECS(current->times.tms_stime);
+ prstatus.pr_stime.tv_usec = CT_TO_USECS(current->times.tms_stime);
+ prstatus.pr_cutime.tv_sec = CT_TO_SECS(current->times.tms_cutime);
+ prstatus.pr_cutime.tv_usec = CT_TO_USECS(current->times.tms_cutime);
+ prstatus.pr_cstime.tv_sec = CT_TO_SECS(current->times.tms_cstime);
+ prstatus.pr_cstime.tv_usec = CT_TO_USECS(current->times.tms_cstime);
+
+#ifdef DEBUG
+ dump_regs("Passed in regs", (elf_greg_t *)regs);
+ dump_regs("prstatus regs", (elf_greg_t *)&prstatus.pr_reg);
+#endif
+
+ notes[1].name = "CORE";
+ notes[1].type = NT_PRPSINFO;
+ notes[1].datasz = sizeof(psinfo);
+ notes[1].data = &psinfo;
+ i = current->state ? ffz(~current->state) + 1 : 0;
+ psinfo.pr_state = i;
+ psinfo.pr_sname = (i < 0 || i > 5) ? '.' : "RSDZTD"[i];
+ psinfo.pr_zomb = psinfo.pr_sname == 'Z';
+ psinfo.pr_nice = current->nice;
+ psinfo.pr_flag = current->flags;
+ psinfo.pr_uid = NEW_TO_OLD_UID(current->uid);
+ psinfo.pr_gid = NEW_TO_OLD_GID(current->gid);
+ strncpy(psinfo.pr_fname, current->comm, sizeof(psinfo.pr_fname));
+
+ notes[2].name = "CORE";
+ notes[2].type = NT_TASKSTRUCT;
+ notes[2].datasz = sizeof(*current);
+ notes[2].data = current;
+
+ /* Try to dump the FPU. */
+ prstatus.pr_fpvalid = dump_fpu (regs, &fpu);
+ if (!prstatus.pr_fpvalid)
+ {
+ numnote--;
+ }
+ else
+ {
+ notes[3].name = "CORE";
+ notes[3].type = NT_PRFPREG;
+ notes[3].datasz = sizeof(fpu);
+ notes[3].data = &fpu;
+ }
+
+ /* Write notes phdr entry */
+ {
+ struct elf_phdr phdr;
+ int sz = 0;
+
+ for(i = 0; i < numnote; i++)
+ sz += notesize(&notes[i]);
+
+ phdr.p_type = PT_NOTE;
+ phdr.p_offset = offset;
+ phdr.p_vaddr = 0;
+ phdr.p_paddr = 0;
+ phdr.p_filesz = sz;
+ phdr.p_memsz = 0;
+ phdr.p_flags = 0;
+ phdr.p_align = 0;
+
+ offset += phdr.p_filesz;
+ DUMP_WRITE(&phdr, sizeof(phdr));
+ }
+
+ /* Page-align dumped data */
+ dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE);
+
+ /* Write program headers for segments dump */
+ for(vma = current->mm->mmap; vma != NULL; vma = vma->vm_next) {
+ struct elf_phdr phdr;
+ size_t sz;
+
+ sz = vma->vm_end - vma->vm_start;
+
+ phdr.p_type = PT_LOAD;
+ phdr.p_offset = offset;
+ phdr.p_vaddr = vma->vm_start;
+ phdr.p_paddr = 0;
+ phdr.p_filesz = maydump(vma) ? sz : 0;
+ phdr.p_memsz = sz;
+ offset += phdr.p_filesz;
+ phdr.p_flags = vma->vm_flags & VM_READ ? PF_R : 0;
+ if (vma->vm_flags & VM_WRITE) phdr.p_flags |= PF_W;
+ if (vma->vm_flags & VM_EXEC) phdr.p_flags |= PF_X;
+ phdr.p_align = ELF_EXEC_PAGESIZE;
+
+ DUMP_WRITE(&phdr, sizeof(phdr));
+ }
+
+ for(i = 0; i < numnote; i++)
+ if (!writenote(&notes[i], file))
+ goto end_coredump;
+
+ DUMP_SEEK(dataoff);
+
+ for(vma = current->mm->mmap; vma != NULL; vma = vma->vm_next) {
+ unsigned long addr;
+
+ if (!maydump(vma))
+ continue;
+
+#ifdef DEBUG
+ printk("elf_core_dump: writing %08lx-%08lx\n", vma->vm_start, vma->vm_end);
+#endif
+
+ for (addr = vma->vm_start;
+ addr < vma->vm_end;
+ addr += PAGE_SIZE) {
+ struct page* page;
+ struct vm_area_struct *vma;
+
+ if (get_user_pages(current, current->mm, addr, 1, 0, 1,
+ &page, &vma) <= 0) {
+ DUMP_SEEK (file->f_pos + PAGE_SIZE);
+ } else {
+ if (page == ZERO_PAGE(addr)) {
+ DUMP_SEEK (file->f_pos + PAGE_SIZE);
+ } else {
+ void *kaddr;
+ flush_cache_page(vma, addr);
+ kaddr = kmap(page);
+ DUMP_WRITE(kaddr, PAGE_SIZE);
+ flush_page_to_ram(page);
+ kunmap(page);
+ }
+ put_page(page);
+ }
+ }
+ }
+
+ if ((off_t) file->f_pos != offset) {
+ /* Sanity check */
+ printk("elf_core_dump: file->f_pos (%ld) != offset (%ld)\n",
+ (off_t) file->f_pos, offset);
+ }
+
+ end_coredump:
+ set_fs(fs);
+ up_write(&current->mm->mmap_sem);
+ return has_dumped;
+}
+#endif /* USE_ELF_CORE_DUMP */
+
+static int __init init_elf_binfmt(void)
+{
+ return register_binfmt(&elf_format);
+}
+
+static void __exit exit_elf_binfmt(void)
+{
+ /* Remove the COFF and ELF loaders. */
+ unregister_binfmt(&elf_format);
+}
+
+module_init(init_elf_binfmt)
+module_exit(exit_elf_binfmt)
+MODULE_LICENSE("GPL");
diff --git a/fs/binfmt_em86.c b/fs/binfmt_em86.c
new file mode 100644
index 00000000000000..3e637421853e41
--- /dev/null
+++ b/fs/binfmt_em86.c
@@ -0,0 +1,113 @@
+/*
+ * linux/fs/binfmt_em86.c
+ *
+ * Based on linux/fs/binfmt_script.c
+ * Copyright (C) 1996 Martin von Löwis
+ * original #!-checking implemented by tytso.
+ *
+ * em86 changes Copyright (C) 1997 Jim Paradis
+ */
+
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/slab.h>
+#include <linux/locks.h>
+#include <linux/smp_lock.h>
+#include <linux/binfmts.h>
+#include <linux/elf.h>
+#include <linux/init.h>
+#include <linux/file.h>
+
+
+#define EM86_INTERP "/usr/bin/em86"
+#define EM86_I_NAME "em86"
+
+static int load_em86(struct linux_binprm *bprm,struct pt_regs *regs)
+{
+ char *interp, *i_name, *i_arg;
+ struct file * file;
+ int retval;
+ struct elfhdr elf_ex;
+
+ /* Make sure this is a Linux/Intel ELF executable... */
+ elf_ex = *((struct elfhdr *)bprm->buf);
+
+ if (memcmp(elf_ex.e_ident, ELFMAG, SELFMAG) != 0)
+ return -ENOEXEC;
+
+ /* First of all, some simple consistency checks */
+ if ((elf_ex.e_type != ET_EXEC && elf_ex.e_type != ET_DYN) ||
+ (!((elf_ex.e_machine == EM_386) || (elf_ex.e_machine == EM_486))) ||
+ (!bprm->file->f_op || !bprm->file->f_op->mmap)) {
+ return -ENOEXEC;
+ }
+
+ bprm->sh_bang++; /* Well, the bang-shell is implicit... */
+ allow_write_access(bprm->file);
+ fput(bprm->file);
+ bprm->file = NULL;
+
+ /* Unlike in the script case, we don't have to do any hairy
+ * parsing to find our interpreter... it's hardcoded!
+ */
+ interp = EM86_INTERP;
+ i_name = EM86_I_NAME;
+ i_arg = NULL; /* We reserve the right to add an arg later */
+
+ /*
+ * Splice in (1) the interpreter's name for argv[0]
+ * (2) (optional) argument to interpreter
+ * (3) filename of emulated file (replace argv[0])
+ *
+ * This is done in reverse order, because of how the
+ * user environment and arguments are stored.
+ */
+ remove_arg_zero(bprm);
+ retval = copy_strings_kernel(1, &bprm->filename, bprm);
+ if (retval < 0) return retval;
+ bprm->argc++;
+ if (i_arg) {
+ retval = copy_strings_kernel(1, &i_arg, bprm);
+ if (retval < 0) return retval;
+ bprm->argc++;
+ }
+ retval = copy_strings_kernel(1, &i_name, bprm);
+ if (retval < 0) return retval;
+ bprm->argc++;
+
+ /*
+ * OK, now restart the process with the interpreter's inode.
+ * Note that we use open_exec() as the name is now in kernel
+ * space, and we don't need to copy it.
+ */
+ file = open_exec(interp);
+ if (IS_ERR(file))
+ return PTR_ERR(file);
+
+ bprm->file = file;
+
+ retval = prepare_binprm(bprm);
+ if (retval < 0)
+ return retval;
+
+ return search_binary_handler(bprm, regs);
+}
+
+struct linux_binfmt em86_format = {
+ NULL, THIS_MODULE, load_em86, NULL, NULL, 0
+};
+
+static int __init init_em86_binfmt(void)
+{
+ return register_binfmt(&em86_format);
+}
+
+static void __exit exit_em86_binfmt(void)
+{
+ unregister_binfmt(&em86_format);
+}
+
+module_init(init_em86_binfmt)
+module_exit(exit_em86_binfmt)
+MODULE_LICENSE("GPL");
diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c
new file mode 100644
index 00000000000000..b3f8a0417fa87d
--- /dev/null
+++ b/fs/binfmt_misc.c
@@ -0,0 +1,717 @@
+/*
+ * binfmt_misc.c
+ *
+ * Copyright (C) 1997 Richard Günther
+ *
+ * binfmt_misc detects binaries via a magic or filename extension and invokes
+ * a specified wrapper. This should obsolete binfmt_java, binfmt_em86 and
+ * binfmt_mz.
+ *
+ * 1997-04-25 first version
+ * [...]
+ * 1997-05-19 cleanup
+ * 1997-06-26 hpa: pass the real filename rather than argv[0]
+ * 1997-06-30 minor cleanup
+ * 1997-08-09 removed extension stripping, locking cleanup
+ * 2001-02-28 AV: rewritten into something that resembles C. Original didn't.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <linux/binfmts.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/file.h>
+#include <linux/pagemap.h>
+
+#include <asm/uaccess.h>
+
+enum {
+ VERBOSE_STATUS = 1 /* make it zero to save 400 bytes kernel memory */
+};
+
+static LIST_HEAD(entries);
+static int enabled = 1;
+
+enum {Enabled, Magic};
+
+typedef struct {
+ struct list_head list;
+ unsigned long flags; /* type, status, etc. */
+ int offset; /* offset of magic */
+ int size; /* size of magic/mask */
+ char *magic; /* magic or filename extension */
+ char *mask; /* mask, NULL for exact match */
+ char *interpreter; /* filename of interpreter */
+ char *name;
+ struct dentry *dentry;
+} Node;
+
+static rwlock_t entries_lock __attribute__((unused)) = RW_LOCK_UNLOCKED;
+
+/*
+ * Check if we support the binfmt
+ * if we do, return the node, else NULL
+ * locking is done in load_misc_binary
+ */
+static Node *check_file(struct linux_binprm *bprm)
+{
+ char *p = strrchr(bprm->filename, '.');
+ struct list_head *l;
+
+ for (l = entries.next; l != &entries; l = l->next) {
+ Node *e = list_entry(l, Node, list);
+ char *s;
+ int j;
+
+ if (!test_bit(Enabled, &e->flags))
+ continue;
+
+ if (!test_bit(Magic, &e->flags)) {
+ if (p && !strcmp(e->magic, p + 1))
+ return e;
+ continue;
+ }
+
+ s = bprm->buf + e->offset;
+ if (e->mask) {
+ for (j = 0; j < e->size; j++)
+ if ((*s++ ^ e->magic[j]) & e->mask[j])
+ break;
+ } else {
+ for (j = 0; j < e->size; j++)
+ if ((*s++ ^ e->magic[j]))
+ break;
+ }
+ if (j == e->size)
+ return e;
+ }
+ return NULL;
+}
+
+/*
+ * the loader itself
+ */
+static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs)
+{
+ Node *fmt;
+ struct file * file;
+ char iname[BINPRM_BUF_SIZE];
+ char *iname_addr = iname;
+ int retval;
+
+ retval = -ENOEXEC;
+ if (!enabled)
+ goto _ret;
+
+ /* to keep locking time low, we copy the interpreter string */
+ read_lock(&entries_lock);
+ fmt = check_file(bprm);
+ if (fmt) {
+ strncpy(iname, fmt->interpreter, BINPRM_BUF_SIZE - 1);
+ iname[BINPRM_BUF_SIZE - 1] = '\0';
+ }
+ read_unlock(&entries_lock);
+ if (!fmt)
+ goto _ret;
+
+ allow_write_access(bprm->file);
+ fput(bprm->file);
+ bprm->file = NULL;
+
+ /* Build args for interpreter */
+ remove_arg_zero(bprm);
+ retval = copy_strings_kernel(1, &bprm->filename, bprm);
+ if (retval < 0) goto _ret;
+ bprm->argc++;
+ retval = copy_strings_kernel(1, &iname_addr, bprm);
+ if (retval < 0) goto _ret;
+ bprm->argc++;
+ bprm->filename = iname; /* for binfmt_script */
+
+ file = open_exec(iname);
+ retval = PTR_ERR(file);
+ if (IS_ERR(file))
+ goto _ret;
+ bprm->file = file;
+
+ retval = prepare_binprm(bprm);
+ if (retval >= 0)
+ retval = search_binary_handler(bprm, regs);
+_ret:
+ return retval;
+}
+
+/* Command parsers */
+
+/*
+ * parses and copies one argument enclosed in del from *sp to *dp,
+ * recognising the \x special.
+ * returns pointer to the copied argument or NULL in case of an
+ * error (and sets err) or null argument length.
+ */
+static char *scanarg(char *s, char del)
+{
+ char c;
+
+ while ((c = *s++) != del) {
+ if (c == '\\' && *s == 'x') {
+ s++;
+ if (!isxdigit(*s++))
+ return NULL;
+ if (!isxdigit(*s++))
+ return NULL;
+ }
+ }
+ return s;
+}
+
+static int unquote(char *from)
+{
+ char c = 0, *s = from, *p = from;
+
+ while ((c = *s++) != '\0') {
+ if (c == '\\' && *s == 'x') {
+ s++;
+ c = toupper(*s++);
+ *p = (c - (isdigit(c) ? '0' : 'A' - 10)) << 4;
+ c = toupper(*s++);
+ *p++ |= c - (isdigit(c) ? '0' : 'A' - 10);
+ continue;
+ }
+ *p++ = c;
+ }
+ return p - from;
+}
+
+/*
+ * This registers a new binary format, it recognises the syntax
+ * ':name:type:offset:magic:mask:interpreter:'
+ * where the ':' is the IFS, that can be chosen with the first char
+ */
+static Node *create_entry(const char *buffer, size_t count)
+{
+ Node *e;
+ int memsize, err;
+ char *buf, *p;
+ char del;
+
+ /* some sanity checks */
+ err = -EINVAL;
+ if ((count < 11) || (count > 256))
+ goto out;
+
+ err = -ENOMEM;
+ memsize = sizeof(Node) + count + 8;
+ e = (Node *) kmalloc(memsize, GFP_USER);
+ if (!e)
+ goto out;
+
+ p = buf = (char *)e + sizeof(Node);
+
+ memset(e, 0, sizeof(Node));
+ if (copy_from_user(buf, buffer, count))
+ goto Efault;
+
+ del = *p++; /* delimeter */
+
+ memset(buf+count, del, 8);
+
+ e->name = p;
+ p = strchr(p, del);
+ if (!p)
+ goto Einval;
+ *p++ = '\0';
+ if (!e->name[0] ||
+ !strcmp(e->name, ".") ||
+ !strcmp(e->name, "..") ||
+ strchr(e->name, '/'))
+ goto Einval;
+ switch (*p++) {
+ case 'E': e->flags = 1<<Enabled; break;
+ case 'M': e->flags = (1<<Enabled) | (1<<Magic); break;
+ default: goto Einval;
+ }
+ if (*p++ != del)
+ goto Einval;
+ if (test_bit(Magic, &e->flags)) {
+ char *s = strchr(p, del);
+ if (!s)
+ goto Einval;
+ *s++ = '\0';
+ e->offset = simple_strtoul(p, &p, 10);
+ if (*p++)
+ goto Einval;
+ e->magic = p;
+ p = scanarg(p, del);
+ if (!p)
+ goto Einval;
+ p[-1] = '\0';
+ if (!e->magic[0])
+ goto Einval;
+ e->mask = p;
+ p = scanarg(p, del);
+ if (!p)
+ goto Einval;
+ p[-1] = '\0';
+ if (!e->mask[0])
+ e->mask = NULL;
+ e->size = unquote(e->magic);
+ if (e->mask && unquote(e->mask) != e->size)
+ goto Einval;
+ if (e->size + e->offset > BINPRM_BUF_SIZE)
+ goto Einval;
+ } else {
+ p = strchr(p, del);
+ if (!p)
+ goto Einval;
+ *p++ = '\0';
+ e->magic = p;
+ p = strchr(p, del);
+ if (!p)
+ goto Einval;
+ *p++ = '\0';
+ if (!e->magic[0] || strchr(e->magic, '/'))
+ goto Einval;
+ p = strchr(p, del);
+ if (!p)
+ goto Einval;
+ *p++ = '\0';
+ }
+ e->interpreter = p;
+ p = strchr(p, del);
+ if (!p)
+ goto Einval;
+ *p++ = '\0';
+ if (!e->interpreter[0])
+ goto Einval;
+
+ if (*p == '\n')
+ p++;
+ if (p != buf + count)
+ goto Einval;
+ return e;
+
+out:
+ return ERR_PTR(err);
+
+Efault:
+ kfree(e);
+ return ERR_PTR(-EFAULT);
+Einval:
+ kfree(e);
+ return ERR_PTR(-EINVAL);
+}
+
+/*
+ * Set status of entry/binfmt_misc:
+ * '1' enables, '0' disables and '-1' clears entry/binfmt_misc
+ */
+static int parse_command(const char *buffer, size_t count)
+{
+ char s[4];
+
+ if (!count)
+ return 0;
+ if (count > 3)
+ return -EINVAL;
+ if (copy_from_user(s, buffer, count))
+ return -EFAULT;
+ if (s[count-1] == '\n')
+ count--;
+ if (count == 1 && s[0] == '0')
+ return 1;
+ if (count == 1 && s[0] == '1')
+ return 2;
+ if (count == 2 && s[0] == '-' && s[1] == '1')
+ return 3;
+ return -EINVAL;
+}
+
+/* generic stuff */
+
+static void entry_status(Node *e, char *page)
+{
+ char *dp;
+ char *status = "disabled";
+
+ if (test_bit(Enabled, &e->flags))
+ status = "enabled";
+
+ if (!VERBOSE_STATUS) {
+ sprintf(page, "%s\n", status);
+ return;
+ }
+
+ sprintf(page, "%s\ninterpreter %s\n", status, e->interpreter);
+ dp = page + strlen(page);
+ if (!test_bit(Magic, &e->flags)) {
+ sprintf(dp, "extension .%s\n", e->magic);
+ } else {
+ int i;
+
+ sprintf(dp, "offset %i\nmagic ", e->offset);
+ dp = page + strlen(page);
+ for (i = 0; i < e->size; i++) {
+ sprintf(dp, "%02x", 0xff & (int) (e->magic[i]));
+ dp += 2;
+ }
+ if (e->mask) {
+ sprintf(dp, "\nmask ");
+ dp += 6;
+ for (i = 0; i < e->size; i++) {
+ sprintf(dp, "%02x", 0xff & (int) (e->mask[i]));
+ dp += 2;
+ }
+ }
+ *dp++ = '\n';
+ *dp = '\0';
+ }
+}
+
+static struct inode *bm_get_inode(struct super_block *sb, int mode)
+{
+ struct inode * inode = new_inode(sb);
+
+ if (inode) {
+ inode->i_mode = mode;
+ inode->i_uid = 0;
+ inode->i_gid = 0;
+ inode->i_blksize = PAGE_CACHE_SIZE;
+ inode->i_blocks = 0;
+ inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ }
+ return inode;
+}
+
+static void bm_clear_inode(struct inode *inode)
+{
+ Node *e = inode->u.generic_ip;
+
+ if (e) {
+ write_lock(&entries_lock);
+ list_del(&e->list);
+ write_unlock(&entries_lock);
+ kfree(e);
+ }
+}
+
+static void kill_node(Node *e)
+{
+ struct dentry *dentry;
+
+ write_lock(&entries_lock);
+ dentry = e->dentry;
+ if (dentry) {
+ list_del(&e->list);
+ INIT_LIST_HEAD(&e->list);
+ e->dentry = NULL;
+ }
+ write_unlock(&entries_lock);
+
+ if (dentry) {
+ dentry->d_inode->i_nlink--;
+ d_drop(dentry);
+ dput(dentry);
+ }
+}
+
+/* /<entry> */
+
+static ssize_t
+bm_entry_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos)
+{
+ Node *e = file->f_dentry->d_inode->u.generic_ip;
+ loff_t pos = *ppos;
+ ssize_t res;
+ char *page;
+ int len;
+
+ if (!(page = (char*) __get_free_page(GFP_KERNEL)))
+ return -ENOMEM;
+
+ entry_status(e, page);
+ len = strlen(page);
+
+ res = -EINVAL;
+ if (pos < 0)
+ goto out;
+ res = 0;
+ if (pos >= len)
+ goto out;
+ if (len < pos + nbytes)
+ nbytes = len - pos;
+ res = -EFAULT;
+ if (copy_to_user(buf, page + pos, nbytes))
+ goto out;
+ *ppos = pos + nbytes;
+ res = nbytes;
+out:
+ free_page((unsigned long) page);
+ return res;
+}
+
+static ssize_t bm_entry_write(struct file *file, const char *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct dentry *root;
+ Node *e = file->f_dentry->d_inode->u.generic_ip;
+ int res = parse_command(buffer, count);
+
+ switch (res) {
+ case 1: clear_bit(Enabled, &e->flags);
+ break;
+ case 2: set_bit(Enabled, &e->flags);
+ break;
+ case 3: root = dget(file->f_vfsmnt->mnt_sb->s_root);
+ down(&root->d_inode->i_sem);
+ down(&root->d_inode->i_zombie);
+
+ kill_node(e);
+
+ up(&root->d_inode->i_zombie);
+ up(&root->d_inode->i_sem);
+ dput(root);
+ break;
+ default: return res;
+ }
+ return count;
+}
+
+static struct file_operations bm_entry_operations = {
+ read: bm_entry_read,
+ write: bm_entry_write,
+};
+
+/* /register */
+
+static ssize_t bm_register_write(struct file *file, const char *buffer,
+ size_t count, loff_t *ppos)
+{
+ Node *e;
+ struct dentry *root, *dentry;
+ struct super_block *sb = file->f_vfsmnt->mnt_sb;
+ int err = 0;
+
+ e = create_entry(buffer, count);
+
+ if (IS_ERR(e))
+ return PTR_ERR(e);
+
+ root = dget(sb->s_root);
+ down(&root->d_inode->i_sem);
+ dentry = lookup_one_len(e->name, root, strlen(e->name));
+ err = PTR_ERR(dentry);
+ if (!IS_ERR(dentry)) {
+ down(&root->d_inode->i_zombie);
+ if (dentry->d_inode) {
+ err = -EEXIST;
+ } else {
+ struct inode * inode = bm_get_inode(sb, S_IFREG | 0644);
+ err = -ENOMEM;
+
+ if (inode) {
+ write_lock(&entries_lock);
+
+ e->dentry = dget(dentry);
+ inode->u.generic_ip = e;
+ inode->i_fop = &bm_entry_operations;
+ d_instantiate(dentry, inode);
+
+ list_add(&e->list, &entries);
+ write_unlock(&entries_lock);
+
+ err = 0;
+ }
+ }
+ up(&root->d_inode->i_zombie);
+ dput(dentry);
+ }
+ up(&root->d_inode->i_sem);
+ dput(root);
+
+ if (err) {
+ kfree(e);
+ return -EINVAL;
+ }
+ return count;
+}
+
+static struct file_operations bm_register_operations = {
+ write: bm_register_write,
+};
+
+/* /status */
+
+static ssize_t
+bm_status_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos)
+{
+ char *s = enabled ? "enabled" : "disabled";
+ int len = strlen(s);
+ loff_t pos = *ppos;
+
+ if (pos < 0)
+ return -EINVAL;
+ if (pos >= len)
+ return 0;
+ if (len < pos + nbytes)
+ nbytes = len - pos;
+ if (copy_to_user(buf, s + pos, nbytes))
+ return -EFAULT;
+ *ppos = pos + nbytes;
+ return nbytes;
+}
+
+static ssize_t bm_status_write(struct file * file, const char * buffer,
+ size_t count, loff_t *ppos)
+{
+ int res = parse_command(buffer, count);
+ struct dentry *root;
+
+ switch (res) {
+ case 1: enabled = 0; break;
+ case 2: enabled = 1; break;
+ case 3: root = dget(file->f_vfsmnt->mnt_sb->s_root);
+ down(&root->d_inode->i_sem);
+ down(&root->d_inode->i_zombie);
+
+ while (!list_empty(&entries))
+ kill_node(list_entry(entries.next, Node, list));
+
+ up(&root->d_inode->i_zombie);
+ up(&root->d_inode->i_sem);
+ dput(root);
+ default: return res;
+ }
+ return count;
+}
+
+static struct file_operations bm_status_operations = {
+ read: bm_status_read,
+ write: bm_status_write,
+};
+
+/* / */
+
+static struct dentry * bm_lookup(struct inode *dir, struct dentry *dentry)
+{
+ d_add(dentry, NULL);
+ return NULL;
+}
+
+static struct inode_operations bm_dir_inode_operations = {
+ lookup: bm_lookup,
+};
+
+/* Superblock handling */
+
+static int bm_statfs(struct super_block *sb, struct statfs *buf)
+{
+ buf->f_type = sb->s_magic;
+ buf->f_bsize = PAGE_CACHE_SIZE;
+ buf->f_namelen = 255;
+ return 0;
+}
+
+static struct super_operations s_ops = {
+ statfs: bm_statfs,
+ put_inode: force_delete,
+ clear_inode: bm_clear_inode,
+};
+
+static struct super_block *bm_read_super(struct super_block * sb, void * data, int silent)
+{
+ struct qstr names[2] = {{name:"status"}, {name:"register"}};
+ struct inode * inode;
+ struct dentry * dentry[3];
+ int i;
+
+ for (i=0; i<sizeof(names)/sizeof(names[0]); i++) {
+ names[i].len = strlen(names[i].name);
+ names[i].hash = full_name_hash(names[i].name, names[i].len);
+ }
+
+ sb->s_blocksize = PAGE_CACHE_SIZE;
+ sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+ sb->s_magic = 0x42494e4d;
+ sb->s_op = &s_ops;
+
+ inode = bm_get_inode(sb, S_IFDIR | 0755);
+ if (!inode)
+ return NULL;
+ inode->i_op = &bm_dir_inode_operations;
+ inode->i_fop = &dcache_dir_ops;
+ dentry[0] = d_alloc_root(inode);
+ if (!dentry[0]) {
+ iput(inode);
+ return NULL;
+ }
+ dentry[1] = d_alloc(dentry[0], &names[0]);
+ if (!dentry[1])
+ goto out1;
+ dentry[2] = d_alloc(dentry[0], &names[1]);
+ if (!dentry[2])
+ goto out2;
+ inode = bm_get_inode(sb, S_IFREG | 0644);
+ if (!inode)
+ goto out3;
+ inode->i_fop = &bm_status_operations;
+ d_add(dentry[1], inode);
+ inode = bm_get_inode(sb, S_IFREG | 0400);
+ if (!inode)
+ goto out3;
+ inode->i_fop = &bm_register_operations;
+ d_add(dentry[2], inode);
+
+ sb->s_root = dentry[0];
+ return sb;
+
+out3:
+ dput(dentry[2]);
+out2:
+ dput(dentry[1]);
+out1:
+ dput(dentry[0]);
+ return NULL;
+}
+
+static struct linux_binfmt misc_format = {
+ NULL, THIS_MODULE, load_misc_binary, NULL, NULL, 0
+};
+
+static DECLARE_FSTYPE(bm_fs_type, "binfmt_misc", bm_read_super, FS_SINGLE|FS_LITTER);
+
+static struct vfsmount *bm_mnt;
+
+static int __init init_misc_binfmt(void)
+{
+ int err = register_filesystem(&bm_fs_type);
+ if (!err) {
+ bm_mnt = kern_mount(&bm_fs_type);
+ err = PTR_ERR(bm_mnt);
+ if (IS_ERR(bm_mnt))
+ unregister_filesystem(&bm_fs_type);
+ else {
+ err = register_binfmt(&misc_format);
+ if (err) {
+ unregister_filesystem(&bm_fs_type);
+ kern_umount(bm_mnt);
+ }
+ }
+ }
+ return err;
+}
+
+static void __exit exit_misc_binfmt(void)
+{
+ unregister_binfmt(&misc_format);
+ unregister_filesystem(&bm_fs_type);
+ kern_umount(bm_mnt);
+}
+
+EXPORT_NO_SYMBOLS;
+
+module_init(init_misc_binfmt);
+module_exit(exit_misc_binfmt);
+MODULE_LICENSE("GPL");
diff --git a/fs/binfmt_script.c b/fs/binfmt_script.c
new file mode 100644
index 00000000000000..85f31b81fdffb2
--- /dev/null
+++ b/fs/binfmt_script.c
@@ -0,0 +1,111 @@
+/*
+ * linux/fs/binfmt_script.c
+ *
+ * Copyright (C) 1996 Martin von Löwis
+ * original #!-checking implemented by tytso.
+ */
+
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/slab.h>
+#include <linux/binfmts.h>
+#include <linux/init.h>
+#include <linux/file.h>
+#include <linux/smp_lock.h>
+
+static int load_script(struct linux_binprm *bprm,struct pt_regs *regs)
+{
+ char *cp, *i_name, *i_arg;
+ struct file *file;
+ char interp[BINPRM_BUF_SIZE];
+ int retval;
+
+ if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!') || (bprm->sh_bang))
+ return -ENOEXEC;
+ /*
+ * This section does the #! interpretation.
+ * Sorta complicated, but hopefully it will work. -TYT
+ */
+
+ bprm->sh_bang++;
+ allow_write_access(bprm->file);
+ fput(bprm->file);
+ bprm->file = NULL;
+
+ bprm->buf[BINPRM_BUF_SIZE - 1] = '\0';
+ if ((cp = strchr(bprm->buf, '\n')) == NULL)
+ cp = bprm->buf+BINPRM_BUF_SIZE-1;
+ *cp = '\0';
+ while (cp > bprm->buf) {
+ cp--;
+ if ((*cp == ' ') || (*cp == '\t'))
+ *cp = '\0';
+ else
+ break;
+ }
+ for (cp = bprm->buf+2; (*cp == ' ') || (*cp == '\t'); cp++);
+ if (*cp == '\0')
+ return -ENOEXEC; /* No interpreter name found */
+ i_name = cp;
+ i_arg = 0;
+ for ( ; *cp && (*cp != ' ') && (*cp != '\t'); cp++)
+ /* nothing */ ;
+ while ((*cp == ' ') || (*cp == '\t'))
+ *cp++ = '\0';
+ if (*cp)
+ i_arg = cp;
+ strcpy (interp, i_name);
+ /*
+ * OK, we've parsed out the interpreter name and
+ * (optional) argument.
+ * Splice in (1) the interpreter's name for argv[0]
+ * (2) (optional) argument to interpreter
+ * (3) filename of shell script (replace argv[0])
+ *
+ * This is done in reverse order, because of how the
+ * user environment and arguments are stored.
+ */
+ remove_arg_zero(bprm);
+ retval = copy_strings_kernel(1, &bprm->filename, bprm);
+ if (retval < 0) return retval;
+ bprm->argc++;
+ if (i_arg) {
+ retval = copy_strings_kernel(1, &i_arg, bprm);
+ if (retval < 0) return retval;
+ bprm->argc++;
+ }
+ retval = copy_strings_kernel(1, &i_name, bprm);
+ if (retval) return retval;
+ bprm->argc++;
+ /*
+ * OK, now restart the process with the interpreter's dentry.
+ */
+ file = open_exec(interp);
+ if (IS_ERR(file))
+ return PTR_ERR(file);
+
+ bprm->file = file;
+ retval = prepare_binprm(bprm);
+ if (retval < 0)
+ return retval;
+ return search_binary_handler(bprm,regs);
+}
+
+struct linux_binfmt script_format = {
+ NULL, THIS_MODULE, load_script, NULL, NULL, 0
+};
+
+static int __init init_script_binfmt(void)
+{
+ return register_binfmt(&script_format);
+}
+
+static void __exit exit_script_binfmt(void)
+{
+ unregister_binfmt(&script_format);
+}
+
+module_init(init_script_binfmt)
+module_exit(exit_script_binfmt)
+MODULE_LICENSE("GPL");
diff --git a/fs/binfmt_som.c b/fs/binfmt_som.c
new file mode 100644
index 00000000000000..4eec0aaf584bbb
--- /dev/null
+++ b/fs/binfmt_som.c
@@ -0,0 +1,329 @@
+/*
+ * linux/fs/binfmt_som.c
+ *
+ * These are the functions used to load SOM format executables as used
+ * by HP-UX.
+ *
+ * Copyright 1999 Matthew Wilcox <willy@bofh.ai>
+ * based on binfmt_elf which is
+ * Copyright 1993, 1994: Eric Youngdale (ericy@cais.com).
+ */
+
+#include <linux/module.h>
+
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/binfmts.h>
+#include <linux/som.h>
+#include <linux/string.h>
+#include <linux/file.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/shm.h>
+#include <linux/personality.h>
+#include <linux/init.h>
+
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+
+#include <linux/config.h>
+
+#include <linux/elf.h>
+
+static int load_som_binary(struct linux_binprm * bprm, struct pt_regs * regs);
+static int load_som_library(struct file *);
+
+/*
+ * If we don't support core dumping, then supply a NULL so we
+ * don't even try.
+ */
+#if 0
+static int som_core_dump(long signr, struct pt_regs * regs);
+#else
+#define som_core_dump NULL
+#endif
+
+#define SOM_PAGESTART(_v) ((_v) & ~(unsigned long)(SOM_PAGESIZE-1))
+#define SOM_PAGEOFFSET(_v) ((_v) & (SOM_PAGESIZE-1))
+#define SOM_PAGEALIGN(_v) (((_v) + SOM_PAGESIZE - 1) & ~(SOM_PAGESIZE - 1))
+
+static struct linux_binfmt som_format = {
+ NULL, THIS_MODULE, load_som_binary, load_som_library, som_core_dump, SOM_PAGESIZE
+};
+
+/*
+ * create_som_tables() parses the env- and arg-strings in new user
+ * memory and creates the pointer tables from them, and puts their
+ * addresses on the "stack", returning the new stack pointer value.
+ */
+static void create_som_tables(struct linux_binprm *bprm)
+{
+ char **argv, **envp;
+ int argc = bprm->argc;
+ int envc = bprm->envc;
+ unsigned long p;
+ unsigned long *sp;
+
+ /* Word-align the stack pointer */
+ sp = (unsigned long *)((bprm->p + 3) & ~3);
+
+ envp = (char **) sp;
+ sp += envc + 1;
+ argv = (char **) sp;
+ sp += argc + 1;
+
+ __put_user((unsigned long) envp,++sp);
+ __put_user((unsigned long) argv,++sp);
+
+ __put_user(argc, ++sp);
+
+ bprm->p = (unsigned long) sp;
+
+ p = current->mm->arg_start;
+ while (argc-- > 0) {
+ __put_user((char *)p,argv++);
+ p += strlen_user((char *)p);
+ }
+ __put_user(NULL, argv);
+ current->mm->arg_end = current->mm->env_start = p;
+ while (envc-- > 0) {
+ __put_user((char *)p,envp++);
+ p += strlen_user((char *)p);
+ }
+ __put_user(NULL, envp);
+ current->mm->env_end = p;
+}
+
+static int check_som_header(struct som_hdr *som_ex)
+{
+ int *buf = (int *)som_ex;
+ int i, ck;
+
+ if (som_ex->system_id != SOM_SID_PARISC_1_0 &&
+ som_ex->system_id != SOM_SID_PARISC_1_1 &&
+ som_ex->system_id != SOM_SID_PARISC_2_0)
+ return -ENOEXEC;
+
+ if (som_ex->a_magic != SOM_EXEC_NONSHARE &&
+ som_ex->a_magic != SOM_EXEC_SHARE &&
+ som_ex->a_magic != SOM_EXEC_DEMAND)
+ return -ENOEXEC;
+
+ if (som_ex->version_id != SOM_ID_OLD &&
+ som_ex->version_id != SOM_ID_NEW)
+ return -ENOEXEC;
+
+ ck = 0;
+ for (i=0; i<32; i++)
+ ck ^= buf[i];
+ if (ck != 0)
+ return -ENOEXEC;
+
+ return 0;
+}
+
+static int map_som_binary(struct file *file,
+ const struct som_exec_auxhdr *hpuxhdr)
+{
+ unsigned long code_start, code_size, data_start, data_size;
+ unsigned long bss_start, som_brk;
+ int retval;
+ int prot = PROT_READ | PROT_EXEC;
+ int flags = MAP_FIXED|MAP_PRIVATE|MAP_DENYWRITE|MAP_EXECUTABLE;
+
+ mm_segment_t old_fs = get_fs();
+ set_fs(get_ds());
+
+ code_start = SOM_PAGESTART(hpuxhdr->exec_tmem);
+ code_size = SOM_PAGEALIGN(hpuxhdr->exec_tsize);
+ current->mm->start_code = code_start;
+ current->mm->end_code = code_start + code_size;
+ down_write(&current->mm->mmap_sem);
+ retval = do_mmap(file, code_start, code_size, prot,
+ flags, SOM_PAGESTART(hpuxhdr->exec_tfile));
+ up_write(&current->mm->mmap_sem);
+ if (retval < 0 && retval > -1024)
+ goto out;
+
+ data_start = SOM_PAGESTART(hpuxhdr->exec_dmem);
+ data_size = SOM_PAGEALIGN(hpuxhdr->exec_dsize);
+ current->mm->start_data = data_start;
+ current->mm->end_data = bss_start = data_start + data_size;
+ down_write(&current->mm->mmap_sem);
+ retval = do_mmap(file, data_start, data_size,
+ prot | PROT_WRITE, flags,
+ SOM_PAGESTART(hpuxhdr->exec_dfile));
+ up_write(&current->mm->mmap_sem);
+ if (retval < 0 && retval > -1024)
+ goto out;
+
+ som_brk = bss_start + SOM_PAGEALIGN(hpuxhdr->exec_bsize);
+ current->mm->start_brk = current->mm->brk = som_brk;
+ down_write(&current->mm->mmap_sem);
+ retval = do_mmap(NULL, bss_start, som_brk - bss_start,
+ prot | PROT_WRITE, MAP_FIXED | MAP_PRIVATE, 0);
+ up_write(&current->mm->mmap_sem);
+ if (retval > 0 || retval < -1024)
+ retval = 0;
+out:
+ set_fs(old_fs);
+ return retval;
+}
+
+
+/*
+ * These are the functions used to load SOM executables and shared
+ * libraries. There is no binary dependent code anywhere else.
+ */
+
+static inline int
+do_load_som_binary(struct linux_binprm * bprm, struct pt_regs * regs)
+{
+ int som_exec_fileno;
+ int retval;
+ unsigned int size;
+ unsigned long som_entry;
+ struct som_hdr *som_ex;
+ struct som_exec_auxhdr *hpuxhdr;
+
+ /* Get the exec-header */
+ som_ex = (struct som_hdr *) bprm->buf;
+
+ retval = check_som_header(som_ex);
+ if (retval != 0)
+ goto out;
+
+ /* Now read in the auxiliary header information */
+
+ retval = -ENOMEM;
+ size = som_ex->aux_header_size;
+ if (size > SOM_PAGESIZE)
+ goto out;
+ hpuxhdr = (struct som_exec_auxhdr *) kmalloc(size, GFP_KERNEL);
+ if (!hpuxhdr)
+ goto out;
+
+ retval = kernel_read(bprm->file, som_ex->aux_header_location,
+ (char *) hpuxhdr, size);
+ if (retval < 0)
+ goto out_free;
+#error "Fix security hole before enabling me"
+ retval = get_unused_fd();
+ if (retval < 0)
+ goto out_free;
+ get_file(bprm->file);
+ fd_install(som_exec_fileno = retval, bprm->file);
+
+ /* Flush all traces of the currently running executable */
+ retval = flush_old_exec(bprm);
+ if (retval)
+ goto out_free;
+
+ /* OK, This is the point of no return */
+ current->flags &= ~PF_FORKNOEXEC;
+ current->personality = PER_HPUX;
+
+ /* Set the task size for HP-UX processes such that
+ * the gateway page is outside the address space.
+ * This can be fixed later, but for now, this is much
+ * easier.
+ */
+
+ current->thread.task_size = 0xc0000000;
+
+ /* Set map base to allow enough room for hp-ux heap growth */
+
+ current->thread.map_base = 0x80000000;
+
+ retval = map_som_binary(bprm->file, hpuxhdr);
+ if (retval < 0)
+ goto out_free;
+
+ som_entry = hpuxhdr->exec_entry;
+ kfree(hpuxhdr);
+
+ set_binfmt(&som_format);
+ compute_creds(bprm);
+ setup_arg_pages(bprm);
+
+ create_som_tables(bprm);
+
+ current->mm->start_stack = bprm->p;
+ current->mm->rss = 0;
+
+#if 0
+ printk("(start_brk) %08lx\n" , (unsigned long) current->mm->start_brk);
+ printk("(end_code) %08lx\n" , (unsigned long) current->mm->end_code);
+ printk("(start_code) %08lx\n" , (unsigned long) current->mm->start_code);
+ printk("(end_data) %08lx\n" , (unsigned long) current->mm->end_data);
+ printk("(start_stack) %08lx\n" , (unsigned long) current->mm->start_stack);
+ printk("(brk) %08lx\n" , (unsigned long) current->mm->brk);
+#endif
+
+ map_hpux_gateway_page(current,current->mm);
+
+ start_thread_som(regs, som_entry, bprm->p);
+ if (current->ptrace & PT_PTRACED)
+ send_sig(SIGTRAP, current, 0);
+ return 0;
+
+ /* error cleanup */
+out_free:
+ kfree(hpuxhdr);
+out:
+ return retval;
+}
+
+static int
+load_som_binary(struct linux_binprm * bprm, struct pt_regs * regs)
+{
+ int retval;
+
+ MOD_INC_USE_COUNT;
+ retval = do_load_som_binary(bprm, regs);
+ MOD_DEC_USE_COUNT;
+ return retval;
+}
+
+static inline int
+do_load_som_library(struct file *f)
+{
+/* No lib support in SOM yet. gizza chance.. */
+ return -ENOEXEC;
+}
+
+static int load_som_library(struct file *f)
+{
+ int retval;
+
+ MOD_INC_USE_COUNT;
+ retval = do_load_som_library(f);
+ MOD_DEC_USE_COUNT;
+ return retval;
+}
+
+ /* Install the SOM loader.
+ * N.B. We *rely* on the table being the right size with the
+ * right number of free slots...
+ */
+
+static int __init init_som_binfmt(void)
+{
+ return register_binfmt(&som_format);
+}
+
+static void __exit exit_som_binfmt(void)
+{
+ /* Remove the SOM loader. */
+ unregister_binfmt(&som_format);
+}
+
+module_init(init_som_binfmt);
+module_exit(exit_som_binfmt);
+
diff --git a/fs/block_dev.c b/fs/block_dev.c
new file mode 100644
index 00000000000000..64cdc20c3e39ff
--- /dev/null
+++ b/fs/block_dev.c
@@ -0,0 +1,695 @@
+/*
+ * linux/fs/block_dev.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ * Copyright (C) 2001 Andrea Arcangeli <andrea@suse.de> SuSE
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/locks.h>
+#include <linux/fcntl.h>
+#include <linux/slab.h>
+#include <linux/kmod.h>
+#include <linux/major.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/smp_lock.h>
+#include <linux/iobuf.h>
+#include <linux/highmem.h>
+#include <linux/blkdev.h>
+#include <linux/module.h>
+
+#include <asm/uaccess.h>
+
+static unsigned long max_block(kdev_t dev)
+{
+ unsigned int retval = ~0U;
+ int major = MAJOR(dev);
+
+ if (blk_size[major]) {
+ int minor = MINOR(dev);
+ unsigned int blocks = blk_size[major][minor];
+ if (blocks) {
+ unsigned int size = block_size(dev);
+ unsigned int sizebits = blksize_bits(size);
+ blocks += (size-1) >> BLOCK_SIZE_BITS;
+ retval = blocks << (BLOCK_SIZE_BITS - sizebits);
+ if (sizebits > BLOCK_SIZE_BITS)
+ retval = blocks >> (sizebits - BLOCK_SIZE_BITS);
+ }
+ }
+ return retval;
+}
+
+static loff_t blkdev_size(kdev_t dev)
+{
+ unsigned int blocks = ~0U;
+ int major = MAJOR(dev);
+
+ if (blk_size[major]) {
+ int minor = MINOR(dev);
+ blocks = blk_size[major][minor];
+ }
+ return (loff_t) blocks << BLOCK_SIZE_BITS;
+}
+
+/* Kill _all_ buffers, dirty or not.. */
+static void kill_bdev(struct block_device *bdev)
+{
+ invalidate_bdev(bdev, 1);
+ truncate_inode_pages(bdev->bd_inode->i_mapping, 0);
+}
+
+int set_blocksize(kdev_t dev, int size)
+{
+ int oldsize;
+ struct block_device *bdev;
+
+ /* Size must be a power of two, and between 512 and PAGE_SIZE */
+ if (size > PAGE_SIZE || size < 512 || (size & (size-1)))
+ return -EINVAL;
+
+ /* Size cannot be smaller than the size supported by the device */
+ if (size < get_hardsect_size(dev))
+ return -EINVAL;
+
+ /* No blocksize array? Implies hardcoded BLOCK_SIZE */
+ if (!blksize_size[MAJOR(dev)]) {
+ if (size == BLOCK_SIZE)
+ return 0;
+ return -EINVAL;
+ }
+
+ oldsize = blksize_size[MAJOR(dev)][MINOR(dev)];
+ if (oldsize == size)
+ return 0;
+
+ if (!oldsize && size == BLOCK_SIZE) {
+ blksize_size[MAJOR(dev)][MINOR(dev)] = size;
+ return 0;
+ }
+
+ /* Ok, we're actually changing the blocksize.. */
+ bdev = bdget(dev);
+ sync_buffers(dev, 2);
+ blksize_size[MAJOR(dev)][MINOR(dev)] = size;
+ bdev->bd_inode->i_blkbits = blksize_bits(size);
+ kill_bdev(bdev);
+ bdput(bdev);
+ return 0;
+}
+
+int sb_set_blocksize(struct super_block *sb, int size)
+{
+ int bits;
+ if (set_blocksize(sb->s_dev, size) < 0)
+ return 0;
+ sb->s_blocksize = size;
+ for (bits = 9, size >>= 9; size >>= 1; bits++)
+ ;
+ sb->s_blocksize_bits = bits;
+ return sb->s_blocksize;
+}
+
+int sb_min_blocksize(struct super_block *sb, int size)
+{
+ int minsize = get_hardsect_size(sb->s_dev);
+ if (size < minsize)
+ size = minsize;
+ return sb_set_blocksize(sb, size);
+}
+
+static int blkdev_get_block(struct inode * inode, long iblock, struct buffer_head * bh, int create)
+{
+ if (iblock >= max_block(inode->i_rdev))
+ return -EIO;
+
+ bh->b_dev = inode->i_rdev;
+ bh->b_blocknr = iblock;
+ bh->b_state |= 1UL << BH_Mapped;
+ return 0;
+}
+
+static int blkdev_direct_IO(int rw, struct inode * inode, struct kiobuf * iobuf, unsigned long blocknr, int blocksize)
+{
+ return generic_direct_IO(rw, inode, iobuf, blocknr, blocksize, blkdev_get_block);
+}
+
+static int blkdev_writepage(struct page * page)
+{
+ return block_write_full_page(page, blkdev_get_block);
+}
+
+static int blkdev_readpage(struct file * file, struct page * page)
+{
+ return block_read_full_page(page, blkdev_get_block);
+}
+
+static int blkdev_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
+{
+ return block_prepare_write(page, from, to, blkdev_get_block);
+}
+
+static int blkdev_commit_write(struct file *file, struct page *page, unsigned from, unsigned to)
+{
+ return block_commit_write(page, from, to);
+}
+
+/*
+ * private llseek:
+ * for a block special file file->f_dentry->d_inode->i_size is zero
+ * so we compute the size by hand (just as in block_read/write above)
+ */
+static loff_t block_llseek(struct file *file, loff_t offset, int origin)
+{
+ /* ewww */
+ loff_t size = file->f_dentry->d_inode->i_bdev->bd_inode->i_size;
+ loff_t retval;
+
+ switch (origin) {
+ case 2:
+ offset += size;
+ break;
+ case 1:
+ offset += file->f_pos;
+ }
+ retval = -EINVAL;
+ if (offset >= 0 && offset <= size) {
+ if (offset != file->f_pos) {
+ file->f_pos = offset;
+ file->f_reada = 0;
+ file->f_version = ++event;
+ }
+ retval = offset;
+ }
+ return retval;
+}
+
+
+static int __block_fsync(struct inode * inode)
+{
+ int ret, err;
+
+ ret = filemap_fdatasync(inode->i_mapping);
+ err = sync_buffers(inode->i_rdev, 1);
+ if (err && !ret)
+ ret = err;
+ err = filemap_fdatawait(inode->i_mapping);
+ if (err && !ret)
+ ret = err;
+
+ return ret;
+}
+
+/*
+ * Filp may be NULL when we are called by an msync of a vma
+ * since the vma has no handle.
+ */
+
+static int block_fsync(struct file *filp, struct dentry *dentry, int datasync)
+{
+ struct inode * inode = dentry->d_inode;
+
+ return __block_fsync(inode);
+}
+
+/*
+ * pseudo-fs
+ */
+
+static struct super_block *bd_read_super(struct super_block *sb, void *data, int silent)
+{
+ static struct super_operations sops = {};
+ struct inode *root = new_inode(sb);
+ if (!root)
+ return NULL;
+ root->i_mode = S_IFDIR | S_IRUSR | S_IWUSR;
+ root->i_uid = root->i_gid = 0;
+ root->i_atime = root->i_mtime = root->i_ctime = CURRENT_TIME;
+ sb->s_maxbytes = ~0ULL;
+ sb->s_blocksize = 1024;
+ sb->s_blocksize_bits = 10;
+ sb->s_magic = 0x62646576;
+ sb->s_op = &sops;
+ sb->s_root = d_alloc(NULL, &(const struct qstr) { "bdev:", 5, 0 });
+ if (!sb->s_root) {
+ iput(root);
+ return NULL;
+ }
+ sb->s_root->d_sb = sb;
+ sb->s_root->d_parent = sb->s_root;
+ d_instantiate(sb->s_root, root);
+ return sb;
+}
+
+static DECLARE_FSTYPE(bd_type, "bdev", bd_read_super, FS_NOMOUNT);
+
+static struct vfsmount *bd_mnt;
+
+/*
+ * bdev cache handling - shamelessly stolen from inode.c
+ * We use smaller hashtable, though.
+ */
+
+#define HASH_BITS 6
+#define HASH_SIZE (1UL << HASH_BITS)
+#define HASH_MASK (HASH_SIZE-1)
+static struct list_head bdev_hashtable[HASH_SIZE];
+static spinlock_t bdev_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
+static kmem_cache_t * bdev_cachep;
+
+#define alloc_bdev() \
+ ((struct block_device *) kmem_cache_alloc(bdev_cachep, SLAB_KERNEL))
+#define destroy_bdev(bdev) kmem_cache_free(bdev_cachep, (bdev))
+
+static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
+{
+ struct block_device * bdev = (struct block_device *) foo;
+
+ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+ SLAB_CTOR_CONSTRUCTOR)
+ {
+ memset(bdev, 0, sizeof(*bdev));
+ sema_init(&bdev->bd_sem, 1);
+ INIT_LIST_HEAD(&bdev->bd_inodes);
+ }
+}
+
+void __init bdev_cache_init(void)
+{
+ int i, err;
+ struct list_head *head = bdev_hashtable;
+
+ i = HASH_SIZE;
+ do {
+ INIT_LIST_HEAD(head);
+ head++;
+ i--;
+ } while (i);
+
+ bdev_cachep = kmem_cache_create("bdev_cache",
+ sizeof(struct block_device),
+ 0, SLAB_HWCACHE_ALIGN, init_once,
+ NULL);
+ if (!bdev_cachep)
+ panic("Cannot create bdev_cache SLAB cache");
+ err = register_filesystem(&bd_type);
+ if (err)
+ panic("Cannot register bdev pseudo-fs");
+ bd_mnt = kern_mount(&bd_type);
+ err = PTR_ERR(bd_mnt);
+ if (IS_ERR(bd_mnt))
+ panic("Cannot create bdev pseudo-fs");
+}
+
+/*
+ * Most likely _very_ bad one - but then it's hardly critical for small
+ * /dev and can be fixed when somebody will need really large one.
+ */
+static inline unsigned long hash(dev_t dev)
+{
+ unsigned long tmp = dev;
+ tmp = tmp + (tmp >> HASH_BITS) + (tmp >> HASH_BITS*2);
+ return tmp & HASH_MASK;
+}
+
+static struct block_device *bdfind(dev_t dev, struct list_head *head)
+{
+ struct list_head *p;
+ struct block_device *bdev;
+ for (p=head->next; p!=head; p=p->next) {
+ bdev = list_entry(p, struct block_device, bd_hash);
+ if (bdev->bd_dev != dev)
+ continue;
+ atomic_inc(&bdev->bd_count);
+ return bdev;
+ }
+ return NULL;
+}
+
+struct block_device *bdget(dev_t dev)
+{
+ struct list_head * head = bdev_hashtable + hash(dev);
+ struct block_device *bdev, *new_bdev;
+ spin_lock(&bdev_lock);
+ bdev = bdfind(dev, head);
+ spin_unlock(&bdev_lock);
+ if (bdev)
+ return bdev;
+ new_bdev = alloc_bdev();
+ if (new_bdev) {
+ struct inode *inode = new_inode(bd_mnt->mnt_sb);
+ if (inode) {
+ kdev_t kdev = to_kdev_t(dev);
+ atomic_set(&new_bdev->bd_count,1);
+ new_bdev->bd_dev = dev;
+ new_bdev->bd_op = NULL;
+ new_bdev->bd_inode = inode;
+ inode->i_rdev = kdev;
+ inode->i_dev = kdev;
+ inode->i_bdev = new_bdev;
+ inode->i_data.a_ops = &def_blk_aops;
+ inode->i_data.gfp_mask = GFP_USER;
+ inode->i_mode = S_IFBLK;
+ spin_lock(&bdev_lock);
+ bdev = bdfind(dev, head);
+ if (!bdev) {
+ list_add(&new_bdev->bd_hash, head);
+ spin_unlock(&bdev_lock);
+ return new_bdev;
+ }
+ spin_unlock(&bdev_lock);
+ iput(new_bdev->bd_inode);
+ }
+ destroy_bdev(new_bdev);
+ }
+ return bdev;
+}
+
+static inline void __bd_forget(struct inode *inode)
+{
+ list_del_init(&inode->i_devices);
+ inode->i_bdev = NULL;
+ inode->i_mapping = &inode->i_data;
+}
+
+void bdput(struct block_device *bdev)
+{
+ if (atomic_dec_and_lock(&bdev->bd_count, &bdev_lock)) {
+ struct list_head *p;
+ if (bdev->bd_openers)
+ BUG();
+ list_del(&bdev->bd_hash);
+ while ( (p = bdev->bd_inodes.next) != &bdev->bd_inodes ) {
+ __bd_forget(list_entry(p, struct inode, i_devices));
+ }
+ spin_unlock(&bdev_lock);
+ iput(bdev->bd_inode);
+ destroy_bdev(bdev);
+ }
+}
+
+int bd_acquire(struct inode *inode)
+{
+ struct block_device *bdev;
+ spin_lock(&bdev_lock);
+ if (inode->i_bdev) {
+ atomic_inc(&inode->i_bdev->bd_count);
+ spin_unlock(&bdev_lock);
+ return 0;
+ }
+ spin_unlock(&bdev_lock);
+ bdev = bdget(kdev_t_to_nr(inode->i_rdev));
+ if (!bdev)
+ return -ENOMEM;
+ spin_lock(&bdev_lock);
+ if (!inode->i_bdev) {
+ inode->i_bdev = bdev;
+ inode->i_mapping = bdev->bd_inode->i_mapping;
+ list_add(&inode->i_devices, &bdev->bd_inodes);
+ } else if (inode->i_bdev != bdev)
+ BUG();
+ spin_unlock(&bdev_lock);
+ return 0;
+}
+
+/* Call when you free inode */
+
+void bd_forget(struct inode *inode)
+{
+ spin_lock(&bdev_lock);
+ if (inode->i_bdev)
+ __bd_forget(inode);
+ spin_unlock(&bdev_lock);
+}
+
+static struct {
+ const char *name;
+ struct block_device_operations *bdops;
+} blkdevs[MAX_BLKDEV];
+
+int get_blkdev_list(char * p)
+{
+ int i;
+ int len;
+
+ len = sprintf(p, "\nBlock devices:\n");
+ for (i = 0; i < MAX_BLKDEV ; i++) {
+ if (blkdevs[i].bdops) {
+ len += sprintf(p+len, "%3d %s\n", i, blkdevs[i].name);
+ }
+ }
+ return len;
+}
+
+/*
+ Return the function table of a device.
+ Load the driver if needed.
+*/
+const struct block_device_operations * get_blkfops(unsigned int major)
+{
+ const struct block_device_operations *ret = NULL;
+
+ /* major 0 is used for non-device mounts */
+ if (major && major < MAX_BLKDEV) {
+#ifdef CONFIG_KMOD
+ if (!blkdevs[major].bdops) {
+ char name[20];
+ sprintf(name, "block-major-%d", major);
+ request_module(name);
+ }
+#endif
+ ret = blkdevs[major].bdops;
+ }
+ return ret;
+}
+
+int register_blkdev(unsigned int major, const char * name, struct block_device_operations *bdops)
+{
+ if (major == 0) {
+ for (major = MAX_BLKDEV-1; major > 0; major--) {
+ if (blkdevs[major].bdops == NULL) {
+ blkdevs[major].name = name;
+ blkdevs[major].bdops = bdops;
+ return major;
+ }
+ }
+ return -EBUSY;
+ }
+ if (major >= MAX_BLKDEV)
+ return -EINVAL;
+ if (blkdevs[major].bdops && blkdevs[major].bdops != bdops)
+ return -EBUSY;
+ blkdevs[major].name = name;
+ blkdevs[major].bdops = bdops;
+ return 0;
+}
+
+int unregister_blkdev(unsigned int major, const char * name)
+{
+ if (major >= MAX_BLKDEV)
+ return -EINVAL;
+ if (!blkdevs[major].bdops)
+ return -EINVAL;
+ if (strcmp(blkdevs[major].name, name))
+ return -EINVAL;
+ blkdevs[major].name = NULL;
+ blkdevs[major].bdops = NULL;
+ return 0;
+}
+
+/*
+ * This routine checks whether a removable media has been changed,
+ * and invalidates all buffer-cache-entries in that case. This
+ * is a relatively slow routine, so we have to try to minimize using
+ * it. Thus it is called only upon a 'mount' or 'open'. This
+ * is the best way of combining speed and utility, I think.
+ * People changing diskettes in the middle of an operation deserve
+ * to lose :-)
+ */
+int check_disk_change(kdev_t dev)
+{
+ int i;
+ const struct block_device_operations * bdops = NULL;
+
+ i = MAJOR(dev);
+ if (i < MAX_BLKDEV)
+ bdops = blkdevs[i].bdops;
+ if (bdops == NULL) {
+ devfs_handle_t de;
+
+ de = devfs_find_handle (NULL, NULL, i, MINOR (dev),
+ DEVFS_SPECIAL_BLK, 0);
+ if (de) {
+ bdops = devfs_get_ops (de);
+ devfs_put_ops (de); /* We're running in owner module */
+ }
+ }
+ if (bdops == NULL)
+ return 0;
+ if (bdops->check_media_change == NULL)
+ return 0;
+ if (!bdops->check_media_change(dev))
+ return 0;
+
+ if (invalidate_device(dev, 0))
+ printk("VFS: busy inodes on changed media.\n");
+
+ if (bdops->revalidate)
+ bdops->revalidate(dev);
+ return 1;
+}
+
+int ioctl_by_bdev(struct block_device *bdev, unsigned cmd, unsigned long arg)
+{
+ int res;
+ mm_segment_t old_fs = get_fs();
+
+ if (!bdev->bd_op->ioctl)
+ return -EINVAL;
+ set_fs(KERNEL_DS);
+ res = bdev->bd_op->ioctl(bdev->bd_inode, NULL, cmd, arg);
+ set_fs(old_fs);
+ return res;
+}
+
+static int do_open(struct block_device *bdev, struct inode *inode, struct file *file)
+{
+ int ret = -ENXIO;
+ kdev_t dev = to_kdev_t(bdev->bd_dev);
+
+ down(&bdev->bd_sem);
+ lock_kernel();
+ if (!bdev->bd_op)
+ bdev->bd_op = get_blkfops(MAJOR(dev));
+ if (bdev->bd_op) {
+ ret = 0;
+ if (bdev->bd_op->owner)
+ __MOD_INC_USE_COUNT(bdev->bd_op->owner);
+ if (bdev->bd_op->open)
+ ret = bdev->bd_op->open(inode, file);
+ if (!ret) {
+ bdev->bd_openers++;
+ bdev->bd_inode->i_size = blkdev_size(dev);
+ bdev->bd_inode->i_blkbits = blksize_bits(block_size(dev));
+ } else {
+ if (bdev->bd_op->owner)
+ __MOD_DEC_USE_COUNT(bdev->bd_op->owner);
+ if (!bdev->bd_openers)
+ bdev->bd_op = NULL;
+ }
+ }
+ unlock_kernel();
+ up(&bdev->bd_sem);
+ if (ret)
+ bdput(bdev);
+ return ret;
+}
+
+int blkdev_get(struct block_device *bdev, mode_t mode, unsigned flags, int kind)
+{
+ /*
+ * This crockload is due to bad choice of ->open() type.
+ * It will go away.
+ * For now, block device ->open() routine must _not_
+ * examine anything in 'inode' argument except ->i_rdev.
+ */
+ struct file fake_file = {};
+ struct dentry fake_dentry = {};
+ fake_file.f_mode = mode;
+ fake_file.f_flags = flags;
+ fake_file.f_dentry = &fake_dentry;
+ fake_dentry.d_inode = bdev->bd_inode;
+
+ return do_open(bdev, bdev->bd_inode, &fake_file);
+}
+
+int blkdev_open(struct inode * inode, struct file * filp)
+{
+ struct block_device *bdev;
+
+ /*
+ * Preserve backwards compatibility and allow large file access
+ * even if userspace doesn't ask for it explicitly. Some mkfs
+ * binary needs it. We might want to drop this workaround
+ * during an unstable branch.
+ */
+ filp->f_flags |= O_LARGEFILE;
+
+ bd_acquire(inode);
+ bdev = inode->i_bdev;
+
+ return do_open(bdev, inode, filp);
+}
+
+int blkdev_put(struct block_device *bdev, int kind)
+{
+ int ret = 0;
+ kdev_t rdev = to_kdev_t(bdev->bd_dev); /* this should become bdev */
+ struct inode *bd_inode = bdev->bd_inode;
+
+ down(&bdev->bd_sem);
+ lock_kernel();
+ if (kind == BDEV_FILE)
+ __block_fsync(bd_inode);
+ else if (kind == BDEV_FS)
+ fsync_no_super(rdev);
+ if (!--bdev->bd_openers)
+ kill_bdev(bdev);
+ if (bdev->bd_op->release)
+ ret = bdev->bd_op->release(bd_inode, NULL);
+ if (bdev->bd_op->owner)
+ __MOD_DEC_USE_COUNT(bdev->bd_op->owner);
+ if (!bdev->bd_openers)
+ bdev->bd_op = NULL;
+ unlock_kernel();
+ up(&bdev->bd_sem);
+ bdput(bdev);
+ return ret;
+}
+
+int blkdev_close(struct inode * inode, struct file * filp)
+{
+ return blkdev_put(inode->i_bdev, BDEV_FILE);
+}
+
+static int blkdev_ioctl(struct inode *inode, struct file *file, unsigned cmd,
+ unsigned long arg)
+{
+ if (inode->i_bdev->bd_op->ioctl)
+ return inode->i_bdev->bd_op->ioctl(inode, file, cmd, arg);
+ return -EINVAL;
+}
+
+struct address_space_operations def_blk_aops = {
+ readpage: blkdev_readpage,
+ writepage: blkdev_writepage,
+ sync_page: block_sync_page,
+ prepare_write: blkdev_prepare_write,
+ commit_write: blkdev_commit_write,
+ direct_IO: blkdev_direct_IO,
+};
+
+struct file_operations def_blk_fops = {
+ open: blkdev_open,
+ release: blkdev_close,
+ llseek: block_llseek,
+ read: generic_file_read,
+ write: generic_file_write,
+ mmap: generic_file_mmap,
+ fsync: block_fsync,
+ ioctl: blkdev_ioctl,
+};
+
+const char * bdevname(kdev_t dev)
+{
+ static char buffer[32];
+ const char * name = blkdevs[MAJOR(dev)].name;
+
+ if (!name)
+ name = "unknown-block";
+
+ sprintf(buffer, "%s(%d,%d)", name, MAJOR(dev), MINOR(dev));
+ return buffer;
+}
diff --git a/fs/buffer.c b/fs/buffer.c
new file mode 100644
index 00000000000000..0271d77b0135ad
--- /dev/null
+++ b/fs/buffer.c
@@ -0,0 +1,3146 @@
+/*
+ * linux/fs/buffer.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+/*
+ * 'buffer.c' implements the buffer-cache functions. Race-conditions have
+ * been avoided by NEVER letting an interrupt change a buffer (except for the
+ * data, of course), but instead letting the caller do it.
+ */
+
+/* Start bdflush() with kernel_thread not syscall - Paul Gortmaker, 12/95 */
+
+/* Removed a lot of unnecessary code and simplified things now that
+ * the buffer cache isn't our primary cache - Andrew Tridgell 12/96
+ */
+
+/* Speed up hash, lru, and free list operations. Use gfp() for allocating
+ * hash table, use SLAB cache for buffer heads. -DaveM
+ */
+
+/* Added 32k buffer block sizes - these are required older ARM systems.
+ * - RMK
+ */
+
+/* Thread it... -DaveM */
+
+/* async buffer flushing, 1999 Andrea Arcangeli <andrea@suse.de> */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/locks.h>
+#include <linux/errno.h>
+#include <linux/swap.h>
+#include <linux/swapctl.h>
+#include <linux/smp_lock.h>
+#include <linux/vmalloc.h>
+#include <linux/blkdev.h>
+#include <linux/sysrq.h>
+#include <linux/file.h>
+#include <linux/init.h>
+#include <linux/quotaops.h>
+#include <linux/iobuf.h>
+#include <linux/highmem.h>
+#include <linux/module.h>
+#include <linux/completion.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/bitops.h>
+#include <asm/mmu_context.h>
+
+#define NR_RESERVED (10*MAX_BUF_PER_PAGE)
+#define MAX_UNUSED_BUFFERS NR_RESERVED+20 /* don't ever have more than this
+ number of unused buffer heads */
+
+/* Anti-deadlock ordering:
+ * lru_list_lock > hash_table_lock > unused_list_lock
+ */
+
+#define BH_ENTRY(list) list_entry((list), struct buffer_head, b_inode_buffers)
+
+/*
+ * Hash table gook..
+ */
+static unsigned int bh_hash_mask;
+static unsigned int bh_hash_shift;
+static struct buffer_head **hash_table;
+static rwlock_t hash_table_lock = RW_LOCK_UNLOCKED;
+
+static struct buffer_head *lru_list[NR_LIST];
+
+static spinlock_cacheline_t lru_list_lock_cacheline = {SPIN_LOCK_UNLOCKED};
+#define lru_list_lock lru_list_lock_cacheline.lock
+
+static int nr_buffers_type[NR_LIST];
+static unsigned long size_buffers_type[NR_LIST];
+
+static struct buffer_head * unused_list;
+static int nr_unused_buffer_heads;
+static spinlock_t unused_list_lock = SPIN_LOCK_UNLOCKED;
+static DECLARE_WAIT_QUEUE_HEAD(buffer_wait);
+
+static int grow_buffers(kdev_t dev, unsigned long block, int size);
+static int osync_buffers_list(struct list_head *);
+static void __refile_buffer(struct buffer_head *);
+
+/*
+ * A global sysctl-controlled flag which puts the machine into "laptop mode"
+ */
+int laptop_mode;
+
+static DECLARE_WAIT_QUEUE_HEAD(kupdate_wait);
+
+/* This is used by some architectures to estimate available memory. */
+atomic_t buffermem_pages = ATOMIC_INIT(0);
+
+/* Here is the parameter block for the bdflush process. If you add or
+ * remove any of the parameters, make sure to update kernel/sysctl.c
+ * and the documentation at linux/Documentation/sysctl/vm.txt.
+ */
+
+#define N_PARAM 9
+
+/* The dummy values in this structure are left in there for compatibility
+ * with old programs that play with the /proc entries.
+ */
+union bdflush_param {
+ struct {
+ int nfract; /* Percentage of buffer cache dirty to
+ activate bdflush */
+ int ndirty; /* Maximum number of dirty blocks to write out per
+ wake-cycle */
+ int dummy2; /* old "nrefill" */
+ int dummy3; /* unused */
+ int interval; /* jiffies delay between kupdate flushes */
+ int age_buffer; /* Time for normal buffer to age before we flush it */
+ int nfract_sync;/* Percentage of buffer cache dirty to
+ activate bdflush synchronously */
+ int nfract_stop_bdflush; /* Percetange of buffer cache dirty to stop bdflush */
+ int dummy5; /* unused */
+ } b_un;
+ unsigned int data[N_PARAM];
+} bdf_prm = {{30, 500, 0, 0, 5*HZ, 30*HZ, 60, 20, 0}};
+
+/* These are the min and max parameter values that we will allow to be assigned */
+int bdflush_min[N_PARAM] = { 0, 1, 0, 0, 0, 1*HZ, 0, 0, 0};
+int bdflush_max[N_PARAM] = {100,50000, 20000, 20000,10000*HZ, 10000*HZ, 100, 100, 0};
+
+static inline int write_buffer_delay(struct buffer_head *bh)
+{
+ struct page *page = bh->b_page;
+
+ if (!TryLockPage(page)) {
+ spin_unlock(&lru_list_lock);
+ unlock_buffer(bh);
+ page->mapping->a_ops->writepage(page);
+ return 1;
+ }
+
+ return 0;
+}
+
+static inline void write_buffer(struct buffer_head *bh)
+{
+ if (buffer_delay(bh)) {
+ struct page *page = bh->b_page;
+
+ lock_page(page);
+ if (buffer_delay(bh)) {
+ page->mapping->a_ops->writepage(page);
+ return;
+ }
+ unlock_page(page);
+ }
+
+ ll_rw_block(WRITE, 1, &bh);
+}
+
+void fastcall unlock_buffer(struct buffer_head *bh)
+{
+ clear_bit(BH_Wait_IO, &bh->b_state);
+ clear_bit(BH_Launder, &bh->b_state);
+ /*
+ * When a locked buffer is visible to the I/O layer BH_Launder
+ * is set. This means before unlocking we must clear BH_Launder,
+ * mb() on alpha and then clear BH_Lock, so no reader can see
+ * BH_Launder set on an unlocked buffer and then risk to deadlock.
+ */
+ smp_mb__after_clear_bit();
+ clear_bit(BH_Lock, &bh->b_state);
+ smp_mb__after_clear_bit();
+ if (waitqueue_active(&bh->b_wait))
+ wake_up(&bh->b_wait);
+}
+
+/*
+ * Note that the real wait_on_buffer() is an inline function that checks
+ * that the buffer is locked before calling this, so that unnecessary disk
+ * unplugging does not occur.
+ */
+void __wait_on_buffer(struct buffer_head * bh)
+{
+ struct task_struct *tsk = current;
+ DECLARE_WAITQUEUE(wait, tsk);
+
+ get_bh(bh);
+ add_wait_queue(&bh->b_wait, &wait);
+ do {
+ set_task_state(tsk, TASK_UNINTERRUPTIBLE);
+ if (!buffer_locked(bh))
+ break;
+ /*
+ * We must read tq_disk in TQ_ACTIVE after the
+ * add_wait_queue effect is visible to other cpus.
+ * We could unplug some line above it wouldn't matter
+ * but we can't do that right after add_wait_queue
+ * without an smp_mb() in between because spin_unlock
+ * has inclusive semantics.
+ * Doing it here is the most efficient place so we
+ * don't do a suprious unplug if we get a racy
+ * wakeup that make buffer_locked to return 0, and
+ * doing it here avoids an explicit smp_mb() we
+ * rely on the implicit one in set_task_state.
+ */
+ run_task_queue(&tq_disk);
+ schedule();
+ } while (buffer_locked(bh));
+ tsk->state = TASK_RUNNING;
+ remove_wait_queue(&bh->b_wait, &wait);
+ put_bh(bh);
+}
+
+/*
+ * Default synchronous end-of-IO handler.. Just mark it up-to-date and
+ * unlock the buffer. This is what ll_rw_block uses too.
+ */
+void end_buffer_io_sync(struct buffer_head *bh, int uptodate)
+{
+ mark_buffer_uptodate(bh, uptodate);
+ unlock_buffer(bh);
+ put_bh(bh);
+}
+
+/*
+ * The buffers have been marked clean and locked. Just submit the dang
+ * things..
+ */
+static void write_locked_buffers(struct buffer_head **array, unsigned int count)
+{
+ do {
+ struct buffer_head * bh = *array++;
+ bh->b_end_io = end_buffer_io_sync;
+ submit_bh(WRITE, bh);
+ } while (--count);
+}
+
+/*
+ * Write some buffers from the head of the dirty queue.
+ *
+ * This must be called with the LRU lock held, and will
+ * return without it!
+ */
+#define NRSYNC (32)
+static int write_some_buffers(kdev_t dev)
+{
+ struct buffer_head *next;
+ struct buffer_head *array[NRSYNC];
+ unsigned int count;
+ int nr;
+
+ next = lru_list[BUF_DIRTY];
+ nr = nr_buffers_type[BUF_DIRTY];
+ count = 0;
+ while (next && --nr >= 0) {
+ struct buffer_head * bh = next;
+ next = bh->b_next_free;
+
+ if (dev != NODEV && bh->b_dev != dev)
+ continue;
+ if (test_and_set_bit(BH_Lock, &bh->b_state))
+ continue;
+ if (buffer_delay(bh)) {
+ if (write_buffer_delay(bh)) {
+ if (count)
+ write_locked_buffers(array, count);
+ return -EAGAIN;
+ }
+ } else if (atomic_set_buffer_clean(bh)) {
+ __refile_buffer(bh);
+ get_bh(bh);
+ array[count++] = bh;
+ if (count < NRSYNC)
+ continue;
+
+ spin_unlock(&lru_list_lock);
+ write_locked_buffers(array, count);
+ return -EAGAIN;
+ }
+ unlock_buffer(bh);
+ __refile_buffer(bh);
+ }
+ spin_unlock(&lru_list_lock);
+
+ if (count)
+ write_locked_buffers(array, count);
+ return 0;
+}
+
+/*
+ * Write out all buffers on the dirty list.
+ */
+static void write_unlocked_buffers(kdev_t dev)
+{
+ do
+ spin_lock(&lru_list_lock);
+ while (write_some_buffers(dev));
+}
+
+/*
+ * Wait for a buffer on the proper list.
+ *
+ * This must be called with the LRU lock held, and
+ * will return with it released.
+ */
+static int wait_for_buffers(kdev_t dev, int index, int refile)
+{
+ struct buffer_head * next;
+ int nr;
+
+ next = lru_list[index];
+ nr = nr_buffers_type[index];
+ while (next && --nr >= 0) {
+ struct buffer_head *bh = next;
+ next = bh->b_next_free;
+
+ if (!buffer_locked(bh)) {
+ if (refile)
+ __refile_buffer(bh);
+ continue;
+ }
+ if (dev != NODEV && bh->b_dev != dev)
+ continue;
+
+ get_bh(bh);
+ spin_unlock(&lru_list_lock);
+ wait_on_buffer (bh);
+ put_bh(bh);
+ return -EAGAIN;
+ }
+ spin_unlock(&lru_list_lock);
+ return 0;
+}
+
+static int wait_for_locked_buffers(kdev_t dev, int index, int refile)
+{
+ do {
+ spin_lock(&lru_list_lock);
+ } while (wait_for_buffers(dev, index, refile));
+ return 0;
+}
+
+/* Call sync_buffers with wait!=0 to ensure that the call does not
+ * return until all buffer writes have completed. Sync() may return
+ * before the writes have finished; fsync() may not.
+ */
+
+/* Godamity-damn. Some buffers (bitmaps for filesystems)
+ * spontaneously dirty themselves without ever brelse being called.
+ * We will ultimately want to put these in a separate list, but for
+ * now we search all of the lists for dirty buffers.
+ */
+int sync_buffers(kdev_t dev, int wait)
+{
+ int err = 0;
+
+ /* One pass for no-wait, three for wait:
+ * 0) write out all dirty, unlocked buffers;
+ * 1) wait for all dirty locked buffers;
+ * 2) write out all dirty, unlocked buffers;
+ * 2) wait for completion by waiting for all buffers to unlock.
+ */
+ write_unlocked_buffers(dev);
+ if (wait) {
+ err = wait_for_locked_buffers(dev, BUF_DIRTY, 0);
+ write_unlocked_buffers(dev);
+ err |= wait_for_locked_buffers(dev, BUF_LOCKED, 1);
+ }
+ return err;
+}
+EXPORT_SYMBOL(sync_buffers);
+
+int fsync_super(struct super_block *sb)
+{
+ kdev_t dev = sb->s_dev;
+ sync_buffers(dev, 0);
+
+ lock_kernel();
+ sync_inodes_sb(sb);
+ DQUOT_SYNC_SB(sb);
+ lock_super(sb);
+ if (sb->s_dirt && sb->s_op && sb->s_op->write_super)
+ sb->s_op->write_super(sb);
+ unlock_super(sb);
+ if (sb->s_op && sb->s_op->sync_fs)
+ sb->s_op->sync_fs(sb);
+ unlock_kernel();
+
+ return sync_buffers(dev, 1);
+}
+
+int fsync_no_super(kdev_t dev)
+{
+ sync_buffers(dev, 0);
+ return sync_buffers(dev, 1);
+}
+
+int fsync_dev(kdev_t dev)
+{
+ sync_buffers(dev, 0);
+
+ lock_kernel();
+ sync_inodes(dev);
+ DQUOT_SYNC_DEV(dev);
+ sync_supers(dev, 1);
+ unlock_kernel();
+
+ return sync_buffers(dev, 1);
+}
+
+/*
+ * There's no real reason to pretend we should
+ * ever do anything differently
+ */
+void sync_dev(kdev_t dev)
+{
+ fsync_dev(dev);
+}
+
+asmlinkage long sys_sync(void)
+{
+ fsync_dev(0);
+ return 0;
+}
+
+/*
+ * filp may be NULL if called via the msync of a vma.
+ */
+
+int file_fsync(struct file *filp, struct dentry *dentry, int datasync)
+{
+ struct inode * inode = dentry->d_inode;
+ struct super_block * sb;
+ kdev_t dev;
+ int ret;
+
+ lock_kernel();
+ /* sync the inode to buffers */
+ write_inode_now(inode, 0);
+
+ /* sync the superblock to buffers */
+ sb = inode->i_sb;
+ lock_super(sb);
+ if (sb->s_op && sb->s_op->write_super)
+ sb->s_op->write_super(sb);
+ unlock_super(sb);
+
+ /* .. finally sync the buffers to disk */
+ dev = inode->i_dev;
+ ret = sync_buffers(dev, 1);
+ unlock_kernel();
+ return ret;
+}
+
+asmlinkage long sys_fsync(unsigned int fd)
+{
+ struct file * file;
+ struct dentry * dentry;
+ struct inode * inode;
+ int ret, err;
+
+ ret = -EBADF;
+ file = fget(fd);
+ if (!file)
+ goto out;
+
+ dentry = file->f_dentry;
+ inode = dentry->d_inode;
+
+ ret = -EINVAL;
+ if (!file->f_op || !file->f_op->fsync) {
+ /* Why? We can still call filemap_fdatasync */
+ goto out_putf;
+ }
+
+ /* We need to protect against concurrent writers.. */
+ down(&inode->i_sem);
+ ret = filemap_fdatasync(inode->i_mapping);
+ err = file->f_op->fsync(file, dentry, 0);
+ if (err && !ret)
+ ret = err;
+ err = filemap_fdatawait(inode->i_mapping);
+ if (err && !ret)
+ ret = err;
+ up(&inode->i_sem);
+
+out_putf:
+ fput(file);
+out:
+ return ret;
+}
+
+int do_fdatasync(struct file *file)
+{
+ int ret, err;
+ struct dentry *dentry;
+ struct inode *inode;
+
+ if (unlikely(!file->f_op || !file->f_op->fsync))
+ return -EINVAL;
+
+ dentry = file->f_dentry;
+ inode = dentry->d_inode;
+
+ ret = filemap_fdatasync(inode->i_mapping);
+ err = file->f_op->fsync(file, dentry, 1);
+ if (err && !ret)
+ ret = err;
+ err = filemap_fdatawait(inode->i_mapping);
+ if (err && !ret)
+ ret = err;
+ return ret;
+}
+
+asmlinkage long sys_fdatasync(unsigned int fd)
+{
+ struct file * file;
+ struct inode *inode;
+ int ret;
+
+ ret = -EBADF;
+ file = fget(fd);
+ if (!file)
+ goto out;
+
+ inode = file->f_dentry->d_inode;
+ down(&inode->i_sem);
+ ret = do_fdatasync(file);
+ up(&inode->i_sem);
+
+ fput(file);
+out:
+ return ret;
+}
+
+/* After several hours of tedious analysis, the following hash
+ * function won. Do not mess with it... -DaveM
+ */
+#define _hashfn(dev,block) \
+ ((((dev)<<(bh_hash_shift - 6)) ^ ((dev)<<(bh_hash_shift - 9))) ^ \
+ (((block)<<(bh_hash_shift - 6)) ^ ((block) >> 13) ^ \
+ ((block) << (bh_hash_shift - 12))))
+#define hash(dev,block) hash_table[(_hashfn(HASHDEV(dev),block) & bh_hash_mask)]
+
+static inline void __insert_into_hash_list(struct buffer_head *bh)
+{
+ struct buffer_head **head = &hash(bh->b_dev, bh->b_blocknr);
+ struct buffer_head *next = *head;
+
+ *head = bh;
+ bh->b_pprev = head;
+ bh->b_next = next;
+ if (next != NULL)
+ next->b_pprev = &bh->b_next;
+}
+
+static __inline__ void __hash_unlink(struct buffer_head *bh)
+{
+ struct buffer_head **pprev = bh->b_pprev;
+ if (pprev) {
+ struct buffer_head *next = bh->b_next;
+ if (next)
+ next->b_pprev = pprev;
+ *pprev = next;
+ bh->b_pprev = NULL;
+ }
+}
+
+static void __insert_into_lru_list(struct buffer_head * bh, int blist)
+{
+ struct buffer_head **bhp = &lru_list[blist];
+
+ if (bh->b_prev_free || bh->b_next_free) BUG();
+
+ if(!*bhp) {
+ *bhp = bh;
+ bh->b_prev_free = bh;
+ }
+ bh->b_next_free = *bhp;
+ bh->b_prev_free = (*bhp)->b_prev_free;
+ (*bhp)->b_prev_free->b_next_free = bh;
+ (*bhp)->b_prev_free = bh;
+ nr_buffers_type[blist]++;
+ size_buffers_type[blist] += bh->b_size;
+}
+
+static void __remove_from_lru_list(struct buffer_head * bh)
+{
+ struct buffer_head *next = bh->b_next_free;
+ if (next) {
+ struct buffer_head *prev = bh->b_prev_free;
+ int blist = bh->b_list;
+
+ prev->b_next_free = next;
+ next->b_prev_free = prev;
+ if (lru_list[blist] == bh) {
+ if (next == bh)
+ next = NULL;
+ lru_list[blist] = next;
+ }
+ bh->b_next_free = NULL;
+ bh->b_prev_free = NULL;
+ nr_buffers_type[blist]--;
+ size_buffers_type[blist] -= bh->b_size;
+ }
+}
+
+/* must be called with both the hash_table_lock and the lru_list_lock
+ held */
+static void __remove_from_queues(struct buffer_head *bh)
+{
+ __hash_unlink(bh);
+ __remove_from_lru_list(bh);
+}
+
+static void remove_from_queues(struct buffer_head *bh)
+{
+ spin_lock(&lru_list_lock);
+ write_lock(&hash_table_lock);
+ __remove_from_queues(bh);
+ write_unlock(&hash_table_lock);
+ spin_unlock(&lru_list_lock);
+}
+
+struct buffer_head * get_hash_table(kdev_t dev, int block, int size)
+{
+ struct buffer_head *bh, **p = &hash(dev, block);
+
+ read_lock(&hash_table_lock);
+
+ for (;;) {
+ bh = *p;
+ if (!bh)
+ break;
+ p = &bh->b_next;
+ if (bh->b_blocknr != block)
+ continue;
+ if (bh->b_size != size)
+ continue;
+ if (bh->b_dev != dev)
+ continue;
+ get_bh(bh);
+ break;
+ }
+
+ read_unlock(&hash_table_lock);
+ return bh;
+}
+
+void fastcall buffer_insert_list(struct buffer_head *bh, struct list_head *list)
+{
+ spin_lock(&lru_list_lock);
+ if (buffer_attached(bh))
+ list_del(&bh->b_inode_buffers);
+ set_buffer_attached(bh);
+ list_add_tail(&bh->b_inode_buffers, list);
+ spin_unlock(&lru_list_lock);
+}
+
+/*
+ * The caller must have the lru_list lock before calling the
+ * remove_inode_queue functions.
+ */
+static void __remove_inode_queue(struct buffer_head *bh)
+{
+ list_del(&bh->b_inode_buffers);
+ clear_buffer_attached(bh);
+}
+
+static inline void remove_inode_queue(struct buffer_head *bh)
+{
+ if (buffer_attached(bh))
+ __remove_inode_queue(bh);
+}
+
+int inode_has_buffers(struct inode *inode)
+{
+ int ret;
+
+ spin_lock(&lru_list_lock);
+ ret = !list_empty(&inode->i_dirty_buffers) || !list_empty(&inode->i_dirty_data_buffers);
+ spin_unlock(&lru_list_lock);
+
+ return ret;
+}
+
+/* If invalidate_buffers() will trash dirty buffers, it means some kind
+ of fs corruption is going on. Trashing dirty data always imply losing
+ information that was supposed to be just stored on the physical layer
+ by the user.
+
+ Thus invalidate_buffers in general usage is not allwowed to trash
+ dirty buffers. For example ioctl(FLSBLKBUF) expects dirty data to
+ be preserved. These buffers are simply skipped.
+
+ We also skip buffers which are still in use. For example this can
+ happen if a userspace program is reading the block device.
+
+ NOTE: In the case where the user removed a removable-media-disk even if
+ there's still dirty data not synced on disk (due a bug in the device driver
+ or due an error of the user), by not destroying the dirty buffers we could
+ generate corruption also on the next media inserted, thus a parameter is
+ necessary to handle this case in the most safe way possible (trying
+ to not corrupt also the new disk inserted with the data belonging to
+ the old now corrupted disk). Also for the ramdisk the natural thing
+ to do in order to release the ramdisk memory is to destroy dirty buffers.
+
+ These are two special cases. Normal usage imply the device driver
+ to issue a sync on the device (without waiting I/O completion) and
+ then an invalidate_buffers call that doesn't trash dirty buffers.
+
+ For handling cache coherency with the blkdev pagecache the 'update' case
+ is been introduced. It is needed to re-read from disk any pinned
+ buffer. NOTE: re-reading from disk is destructive so we can do it only
+ when we assume nobody is changing the buffercache under our I/O and when
+ we think the disk contains more recent information than the buffercache.
+ The update == 1 pass marks the buffers we need to update, the update == 2
+ pass does the actual I/O. */
+void invalidate_bdev(struct block_device *bdev, int destroy_dirty_buffers)
+{
+ int i, nlist, slept;
+ struct buffer_head * bh, * bh_next;
+ kdev_t dev = to_kdev_t(bdev->bd_dev); /* will become bdev */
+
+ retry:
+ slept = 0;
+ spin_lock(&lru_list_lock);
+ for(nlist = 0; nlist < NR_LIST; nlist++) {
+ bh = lru_list[nlist];
+ if (!bh)
+ continue;
+ for (i = nr_buffers_type[nlist]; i > 0 ; bh = bh_next, i--) {
+ bh_next = bh->b_next_free;
+
+ /* Another device? */
+ if (bh->b_dev != dev)
+ continue;
+ /* Not hashed? */
+ if (!bh->b_pprev)
+ continue;
+ if (buffer_locked(bh)) {
+ get_bh(bh);
+ spin_unlock(&lru_list_lock);
+ wait_on_buffer(bh);
+ slept = 1;
+ spin_lock(&lru_list_lock);
+ put_bh(bh);
+ }
+
+ write_lock(&hash_table_lock);
+ /* All buffers in the lru lists are mapped */
+ if (!buffer_mapped(bh))
+ BUG();
+ if (buffer_dirty(bh) && destroy_dirty_buffers)
+ printk("invalidate: dirty buffer\n");
+ if (!atomic_read(&bh->b_count)) {
+ if (destroy_dirty_buffers || !buffer_dirty(bh)) {
+ remove_inode_queue(bh);
+ }
+ } else if (!bdev->bd_openers)
+ printk("invalidate: busy buffer\n");
+
+ write_unlock(&hash_table_lock);
+ if (slept)
+ goto out;
+ }
+ }
+out:
+ spin_unlock(&lru_list_lock);
+ if (slept)
+ goto retry;
+
+ /* Get rid of the page cache */
+ invalidate_inode_pages(bdev->bd_inode);
+}
+
+void __invalidate_buffers(kdev_t dev, int destroy_dirty_buffers)
+{
+ struct block_device *bdev = bdget(dev);
+ if (bdev) {
+ invalidate_bdev(bdev, destroy_dirty_buffers);
+ bdput(bdev);
+ }
+}
+
+static void free_more_memory(void)
+{
+ balance_dirty();
+ wakeup_bdflush();
+ try_to_free_pages(GFP_NOIO);
+ run_task_queue(&tq_disk);
+ yield();
+}
+
+void init_buffer(struct buffer_head *bh, bh_end_io_t *handler, void *private)
+{
+ bh->b_list = BUF_CLEAN;
+ bh->b_end_io = handler;
+ bh->b_private = private;
+}
+
+void end_buffer_io_async(struct buffer_head * bh, int uptodate)
+{
+ static spinlock_t page_uptodate_lock = SPIN_LOCK_UNLOCKED;
+ unsigned long flags;
+ struct buffer_head *tmp;
+ struct page *page;
+ int fullup = 1;
+
+ mark_buffer_uptodate(bh, uptodate);
+
+ /* This is a temporary buffer used for page I/O. */
+ page = bh->b_page;
+
+ if (!uptodate)
+ SetPageError(page);
+
+ /*
+ * Be _very_ careful from here on. Bad things can happen if
+ * two buffer heads end IO at almost the same time and both
+ * decide that the page is now completely done.
+ *
+ * Async buffer_heads are here only as labels for IO, and get
+ * thrown away once the IO for this page is complete. IO is
+ * deemed complete once all buffers have been visited
+ * (b_count==0) and are now unlocked. We must make sure that
+ * only the _last_ buffer that decrements its count is the one
+ * that unlock the page..
+ */
+ spin_lock_irqsave(&page_uptodate_lock, flags);
+ mark_buffer_async(bh, 0);
+ unlock_buffer(bh);
+ tmp = bh->b_this_page;
+ while (tmp != bh) {
+ if (buffer_locked(tmp)) {
+ if (buffer_async(tmp))
+ goto still_busy;
+ } else if (!buffer_uptodate(tmp))
+ fullup = 0;
+ tmp = tmp->b_this_page;
+ }
+
+ /* OK, the async IO on this page is complete. */
+ spin_unlock_irqrestore(&page_uptodate_lock, flags);
+
+ /*
+ * If none of the buffers had errors and all were uptodate
+ * then we can set the page uptodate:
+ */
+ if (fullup && !PageError(page))
+ SetPageUptodate(page);
+
+ UnlockPage(page);
+
+ return;
+
+still_busy:
+ spin_unlock_irqrestore(&page_uptodate_lock, flags);
+ return;
+}
+
+inline void set_buffer_async_io(struct buffer_head *bh)
+{
+ bh->b_end_io = end_buffer_io_async;
+ mark_buffer_async(bh, 1);
+}
+
+/*
+ * Synchronise all the inode's dirty buffers to the disk.
+ *
+ * We have conflicting pressures: we want to make sure that all
+ * initially dirty buffers get waited on, but that any subsequently
+ * dirtied buffers don't. After all, we don't want fsync to last
+ * forever if somebody is actively writing to the file.
+ *
+ * Do this in two main stages: first we copy dirty buffers to a
+ * temporary inode list, queueing the writes as we go. Then we clean
+ * up, waiting for those writes to complete.
+ *
+ * During this second stage, any subsequent updates to the file may end
+ * up refiling the buffer on the original inode's dirty list again, so
+ * there is a chance we will end up with a buffer queued for write but
+ * not yet completed on that list. So, as a final cleanup we go through
+ * the osync code to catch these locked, dirty buffers without requeuing
+ * any newly dirty buffers for write.
+ */
+int fsync_buffers_list(struct list_head *list)
+{
+ struct buffer_head *bh;
+ struct list_head tmp;
+ int err = 0, err2;
+
+ INIT_LIST_HEAD(&tmp);
+
+ spin_lock(&lru_list_lock);
+
+ while (!list_empty(list)) {
+ bh = BH_ENTRY(list->next);
+ list_del(&bh->b_inode_buffers);
+ if (!buffer_dirty(bh) && !buffer_locked(bh))
+ clear_buffer_attached(bh);
+ else {
+ set_buffer_attached(bh);
+ list_add(&bh->b_inode_buffers, &tmp);
+ if (buffer_dirty(bh)) {
+ get_bh(bh);
+ spin_unlock(&lru_list_lock);
+ /*
+ * Wait I/O completion before submitting
+ * the buffer, to be sure the write will
+ * be effective on the latest data in
+ * the buffer. (otherwise - if there's old
+ * I/O in flight - write_buffer would become
+ * a noop)
+ */
+ wait_on_buffer(bh);
+ write_buffer(bh);
+ brelse(bh);
+ spin_lock(&lru_list_lock);
+ }
+ }
+ }
+
+ while (!list_empty(&tmp)) {
+ bh = BH_ENTRY(tmp.prev);
+ remove_inode_queue(bh);
+ get_bh(bh);
+ spin_unlock(&lru_list_lock);
+ wait_on_buffer(bh);
+ if (!buffer_uptodate(bh))
+ err = -EIO;
+ brelse(bh);
+ spin_lock(&lru_list_lock);
+ }
+
+ spin_unlock(&lru_list_lock);
+ err2 = osync_buffers_list(list);
+
+ if (err)
+ return err;
+ else
+ return err2;
+}
+
+/*
+ * osync is designed to support O_SYNC io. It waits synchronously for
+ * all already-submitted IO to complete, but does not queue any new
+ * writes to the disk.
+ *
+ * To do O_SYNC writes, just queue the buffer writes with ll_rw_block as
+ * you dirty the buffers, and then use osync_buffers_list to wait for
+ * completion. Any other dirty buffers which are not yet queued for
+ * write will not be flushed to disk by the osync.
+ */
+static int osync_buffers_list(struct list_head *list)
+{
+ struct buffer_head *bh;
+ struct list_head *p;
+ int err = 0;
+
+ spin_lock(&lru_list_lock);
+
+ repeat:
+ list_for_each_prev(p, list) {
+ bh = BH_ENTRY(p);
+ if (buffer_locked(bh)) {
+ get_bh(bh);
+ spin_unlock(&lru_list_lock);
+ wait_on_buffer(bh);
+ if (!buffer_uptodate(bh))
+ err = -EIO;
+ brelse(bh);
+ spin_lock(&lru_list_lock);
+ goto repeat;
+ }
+ }
+
+ spin_unlock(&lru_list_lock);
+ return err;
+}
+
+/*
+ * Invalidate any and all dirty buffers on a given inode. We are
+ * probably unmounting the fs, but that doesn't mean we have already
+ * done a sync(). Just drop the buffers from the inode list.
+ */
+void invalidate_inode_buffers(struct inode *inode)
+{
+ struct list_head * entry;
+
+ spin_lock(&lru_list_lock);
+ while ((entry = inode->i_dirty_buffers.next) != &inode->i_dirty_buffers)
+ remove_inode_queue(BH_ENTRY(entry));
+ while ((entry = inode->i_dirty_data_buffers.next) != &inode->i_dirty_data_buffers)
+ remove_inode_queue(BH_ENTRY(entry));
+ spin_unlock(&lru_list_lock);
+}
+
+
+/*
+ * Ok, this is getblk, and it isn't very clear, again to hinder
+ * race-conditions. Most of the code is seldom used, (ie repeating),
+ * so it should be much more efficient than it looks.
+ *
+ * The algorithm is changed: hopefully better, and an elusive bug removed.
+ *
+ * 14.02.92: changed it to sync dirty buffers a bit: better performance
+ * when the filesystem starts to get full of dirty blocks (I hope).
+ */
+struct buffer_head * getblk(kdev_t dev, int block, int size)
+{
+ for (;;) {
+ struct buffer_head * bh;
+
+ bh = get_hash_table(dev, block, size);
+ if (bh) {
+ touch_buffer(bh);
+ return bh;
+ }
+
+ if (!grow_buffers(dev, block, size))
+ free_more_memory();
+ }
+}
+
+/* -1 -> no need to flush
+ 0 -> async flush
+ 1 -> sync flush (wait for I/O completion) */
+static int balance_dirty_state(void)
+{
+ unsigned long dirty, tot, hard_dirty_limit, soft_dirty_limit;
+
+ dirty = size_buffers_type[BUF_DIRTY] >> PAGE_SHIFT;
+ tot = nr_free_buffer_pages();
+
+ dirty *= 100;
+ soft_dirty_limit = tot * bdf_prm.b_un.nfract;
+ hard_dirty_limit = tot * bdf_prm.b_un.nfract_sync;
+
+ /* First, check for the "real" dirty limit. */
+ if (dirty > soft_dirty_limit) {
+ if (dirty > hard_dirty_limit && !(current->flags & PF_NOIO))
+ return 1;
+ return 0;
+ }
+
+ return -1;
+}
+
+static int bdflush_stop(void)
+{
+ unsigned long dirty, tot, dirty_limit;
+
+ dirty = size_buffers_type[BUF_DIRTY] >> PAGE_SHIFT;
+ tot = nr_free_buffer_pages();
+
+ dirty *= 100;
+ dirty_limit = tot * bdf_prm.b_un.nfract_stop_bdflush;
+
+ if (!laptop_mode && dirty > dirty_limit)
+ return 0;
+ return 1;
+}
+
+/*
+ * if a new dirty buffer is created we need to balance bdflush.
+ *
+ * in the future we might want to make bdflush aware of different
+ * pressures on different devices - thus the (currently unused)
+ * 'dev' parameter.
+ */
+void balance_dirty(void)
+{
+ int state = balance_dirty_state();
+
+ if (state < 0)
+ return;
+
+ wakeup_bdflush();
+
+ /*
+ * And if we're _really_ out of balance, wait for
+ * some of the dirty/locked buffers ourselves.
+ * This will throttle heavy writers.
+ */
+ if (state > 0) {
+ spin_lock(&lru_list_lock);
+ write_some_buffers(NODEV);
+ }
+}
+EXPORT_SYMBOL(balance_dirty);
+
+inline void fastcall __mark_dirty(struct buffer_head *bh)
+{
+ bh->b_flushtime = jiffies + bdf_prm.b_un.age_buffer;
+ refile_buffer(bh);
+}
+
+/* atomic version, the user must call balance_dirty() by hand
+ as soon as it become possible to block */
+void fastcall __mark_buffer_dirty(struct buffer_head *bh)
+{
+ if (!atomic_set_buffer_dirty(bh))
+ __mark_dirty(bh);
+}
+
+void fastcall mark_buffer_dirty(struct buffer_head *bh)
+{
+ if (!atomic_set_buffer_dirty(bh)) {
+ if (block_dump)
+ printk("%s: dirtied buffer\n", current->comm);
+ __mark_dirty(bh);
+ balance_dirty();
+ }
+}
+
+void set_buffer_flushtime(struct buffer_head *bh)
+{
+ bh->b_flushtime = jiffies + bdf_prm.b_un.age_buffer;
+}
+EXPORT_SYMBOL(set_buffer_flushtime);
+
+int get_buffer_flushtime(void)
+{
+ return bdf_prm.b_un.interval;
+}
+EXPORT_SYMBOL(get_buffer_flushtime);
+
+/*
+ * A buffer may need to be moved from one buffer list to another
+ * (e.g. in case it is not shared any more). Handle this.
+ */
+static void __refile_buffer(struct buffer_head *bh)
+{
+ int dispose = BUF_CLEAN;
+ if (buffer_locked(bh))
+ dispose = BUF_LOCKED;
+ if (buffer_dirty(bh))
+ dispose = BUF_DIRTY;
+ if (dispose != bh->b_list) {
+ __remove_from_lru_list(bh);
+ bh->b_list = dispose;
+ if (dispose == BUF_CLEAN)
+ remove_inode_queue(bh);
+ __insert_into_lru_list(bh, dispose);
+ }
+}
+
+void refile_buffer(struct buffer_head *bh)
+{
+ spin_lock(&lru_list_lock);
+ __refile_buffer(bh);
+ spin_unlock(&lru_list_lock);
+}
+
+/*
+ * Release a buffer head
+ */
+void __brelse(struct buffer_head * buf)
+{
+ if (atomic_read(&buf->b_count)) {
+ put_bh(buf);
+ return;
+ }
+ printk(KERN_ERR "VFS: brelse: Trying to free free buffer\n");
+}
+
+/*
+ * bforget() is like brelse(), except it discards any
+ * potentially dirty data.
+ */
+void __bforget(struct buffer_head * buf)
+{
+ mark_buffer_clean(buf);
+ __brelse(buf);
+}
+
+/**
+ * bread() - reads a specified block and returns the bh
+ * @block: number of block
+ * @size: size (in bytes) to read
+ *
+ * Reads a specified block, and returns buffer head that
+ * contains it. It returns NULL if the block was unreadable.
+ */
+struct buffer_head * bread(kdev_t dev, int block, int size)
+{
+ struct buffer_head * bh;
+
+ bh = getblk(dev, block, size);
+ if (buffer_uptodate(bh))
+ return bh;
+ set_bit(BH_Sync, &bh->b_state);
+ ll_rw_block(READ, 1, &bh);
+ wait_on_buffer(bh);
+ if (buffer_uptodate(bh))
+ return bh;
+ brelse(bh);
+ return NULL;
+}
+
+/*
+ * Note: the caller should wake up the buffer_wait list if needed.
+ */
+static void __put_unused_buffer_head(struct buffer_head * bh)
+{
+ if (unlikely(buffer_attached(bh)))
+ BUG();
+ if (nr_unused_buffer_heads >= MAX_UNUSED_BUFFERS) {
+ kmem_cache_free(bh_cachep, bh);
+ } else {
+ bh->b_dev = B_FREE;
+ bh->b_blocknr = -1;
+ bh->b_this_page = NULL;
+
+ nr_unused_buffer_heads++;
+ bh->b_next_free = unused_list;
+ unused_list = bh;
+ }
+}
+
+void put_unused_buffer_head(struct buffer_head *bh)
+{
+ spin_lock(&unused_list_lock);
+ __put_unused_buffer_head(bh);
+ spin_unlock(&unused_list_lock);
+}
+EXPORT_SYMBOL(put_unused_buffer_head);
+
+/*
+ * Reserve NR_RESERVED buffer heads for async IO requests to avoid
+ * no-buffer-head deadlock. Return NULL on failure; waiting for
+ * buffer heads is now handled in create_buffers().
+ */
+struct buffer_head * get_unused_buffer_head(int async)
+{
+ struct buffer_head * bh;
+
+ spin_lock(&unused_list_lock);
+ if (nr_unused_buffer_heads > NR_RESERVED) {
+ bh = unused_list;
+ unused_list = bh->b_next_free;
+ nr_unused_buffer_heads--;
+ spin_unlock(&unused_list_lock);
+ return bh;
+ }
+ spin_unlock(&unused_list_lock);
+
+ /* This is critical. We can't call out to the FS
+ * to get more buffer heads, because the FS may need
+ * more buffer-heads itself. Thus SLAB_NOFS.
+ */
+ if((bh = kmem_cache_alloc(bh_cachep, SLAB_NOFS)) != NULL) {
+ bh->b_blocknr = -1;
+ bh->b_this_page = NULL;
+ return bh;
+ }
+
+ /*
+ * If we need an async buffer, use the reserved buffer heads.
+ * Non-PF_MEMALLOC tasks can just loop in create_buffers().
+ */
+ if (async && (current->flags & PF_MEMALLOC)) {
+ spin_lock(&unused_list_lock);
+ if (unused_list) {
+ bh = unused_list;
+ unused_list = bh->b_next_free;
+ nr_unused_buffer_heads--;
+ spin_unlock(&unused_list_lock);
+ return bh;
+ }
+ spin_unlock(&unused_list_lock);
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL(get_unused_buffer_head);
+
+void set_bh_page (struct buffer_head *bh, struct page *page, unsigned long offset)
+{
+ if (offset >= PAGE_SIZE)
+ BUG();
+
+ if (PageHighMem(page)) {
+ bh->b_data = (char *)offset;
+ } else {
+ bh->b_data = page_address(page) + offset;
+ }
+ bh->b_page = page;
+}
+EXPORT_SYMBOL(set_bh_page);
+
+/*
+ * Create the appropriate buffers when given a page for data area and
+ * the size of each buffer.. Use the bh->b_this_page linked list to
+ * follow the buffers created. Return NULL if unable to create more
+ * buffers.
+ * The async flag is used to differentiate async IO (paging, swapping)
+ * from ordinary buffer allocations, and only async requests are allowed
+ * to sleep waiting for buffer heads.
+ */
+static struct buffer_head * create_buffers(struct page * page, unsigned long size, int async)
+{
+ struct buffer_head *bh, *head;
+ long offset;
+
+try_again:
+ head = NULL;
+ offset = PAGE_SIZE;
+ while ((offset -= size) >= 0) {
+ bh = get_unused_buffer_head(async);
+ if (!bh)
+ goto no_grow;
+
+ bh->b_dev = NODEV;
+ bh->b_this_page = head;
+ head = bh;
+
+ bh->b_state = 0;
+ bh->b_next_free = NULL;
+ bh->b_pprev = NULL;
+ atomic_set(&bh->b_count, 0);
+ bh->b_size = size;
+
+ set_bh_page(bh, page, offset);
+
+ bh->b_list = BUF_CLEAN;
+ bh->b_end_io = NULL;
+ }
+ return head;
+/*
+ * In case anything failed, we just free everything we got.
+ */
+no_grow:
+ if (head) {
+ spin_lock(&unused_list_lock);
+ do {
+ bh = head;
+ head = head->b_this_page;
+ __put_unused_buffer_head(bh);
+ } while (head);
+ spin_unlock(&unused_list_lock);
+
+ /* Wake up any waiters ... */
+ wake_up(&buffer_wait);
+ }
+
+ /*
+ * Return failure for non-async IO requests. Async IO requests
+ * are not allowed to fail, so we have to wait until buffer heads
+ * become available. But we don't want tasks sleeping with
+ * partially complete buffers, so all were released above.
+ */
+ if (!async)
+ return NULL;
+
+ /* We're _really_ low on memory. Now we just
+ * wait for old buffer heads to become free due to
+ * finishing IO. Since this is an async request and
+ * the reserve list is empty, we're sure there are
+ * async buffer heads in use.
+ */
+ run_task_queue(&tq_disk);
+
+ free_more_memory();
+ goto try_again;
+}
+
+/*
+ * Called when truncating a buffer on a page completely.
+ */
+static void discard_buffer(struct buffer_head * bh)
+{
+ if (buffer_mapped(bh) || buffer_delay(bh)) {
+ mark_buffer_clean(bh);
+ lock_buffer(bh);
+ clear_bit(BH_Uptodate, &bh->b_state);
+ clear_bit(BH_Mapped, &bh->b_state);
+ clear_bit(BH_Req, &bh->b_state);
+ clear_bit(BH_New, &bh->b_state);
+ clear_bit(BH_Delay, &bh->b_state);
+ remove_from_queues(bh);
+ unlock_buffer(bh);
+ }
+}
+
+/**
+ * try_to_release_page - release old fs-specific metadata on a page
+ *
+ */
+
+int try_to_release_page(struct page * page, int gfp_mask)
+{
+ if (!PageLocked(page))
+ BUG();
+
+ if (!page->mapping)
+ goto try_to_free;
+ if (!page->mapping->a_ops->releasepage)
+ goto try_to_free;
+ if (page->mapping->a_ops->releasepage(page, gfp_mask))
+ goto try_to_free;
+ /*
+ * We couldn't release buffer metadata; don't even bother trying
+ * to release buffers.
+ */
+ return 0;
+try_to_free:
+ return try_to_free_buffers(page, gfp_mask);
+}
+
+/*
+ * We don't have to release all buffers here, but
+ * we have to be sure that no dirty buffer is left
+ * and no IO is going on (no buffer is locked), because
+ * we have truncated the file and are going to free the
+ * blocks on-disk..
+ */
+int discard_bh_page(struct page *page, unsigned long offset, int drop_pagecache)
+{
+ struct buffer_head *head, *bh, *next;
+ unsigned int curr_off = 0;
+
+ if (!PageLocked(page))
+ BUG();
+ if (!page->buffers)
+ return 1;
+
+ head = page->buffers;
+ bh = head;
+ do {
+ unsigned int next_off = curr_off + bh->b_size;
+ next = bh->b_this_page;
+
+ /*
+ * is this block fully flushed?
+ */
+ if (offset <= curr_off)
+ discard_buffer(bh);
+ curr_off = next_off;
+ bh = next;
+ } while (bh != head);
+
+ /*
+ * subtle. We release buffer-heads only if this is
+ * the 'final' flushpage. We have invalidated the get_block
+ * cached value unconditionally, so real IO is not
+ * possible anymore.
+ *
+ * If the free doesn't work out, the buffers can be
+ * left around - they just turn into anonymous buffers
+ * instead.
+ */
+ if (!offset) {
+ if (!try_to_release_page(page, 0))
+ return 0;
+ }
+
+ return 1;
+}
+
+void create_empty_buffers(struct page *page, kdev_t dev, unsigned long blocksize)
+{
+ struct buffer_head *bh, *head, *tail;
+
+ /* FIXME: create_buffers should fail if there's no enough memory */
+ head = create_buffers(page, blocksize, 1);
+ if (page->buffers)
+ BUG();
+
+ bh = head;
+ do {
+ bh->b_dev = dev;
+ bh->b_blocknr = 0;
+ bh->b_end_io = NULL;
+ tail = bh;
+ bh = bh->b_this_page;
+ } while (bh);
+ tail->b_this_page = head;
+ page->buffers = head;
+ page_cache_get(page);
+}
+EXPORT_SYMBOL(create_empty_buffers);
+
+/*
+ * We are taking a block for data and we don't want any output from any
+ * buffer-cache aliases starting from return from that function and
+ * until the moment when something will explicitly mark the buffer
+ * dirty (hopefully that will not happen until we will free that block ;-)
+ * We don't even need to mark it not-uptodate - nobody can expect
+ * anything from a newly allocated buffer anyway. We used to used
+ * unmap_buffer() for such invalidation, but that was wrong. We definitely
+ * don't want to mark the alias unmapped, for example - it would confuse
+ * anyone who might pick it with bread() afterwards...
+ */
+
+static void unmap_underlying_metadata(struct buffer_head * bh)
+{
+ struct buffer_head *old_bh;
+
+ old_bh = get_hash_table(bh->b_dev, bh->b_blocknr, bh->b_size);
+ if (old_bh) {
+ mark_buffer_clean(old_bh);
+ wait_on_buffer(old_bh);
+ clear_bit(BH_Req, &old_bh->b_state);
+ __brelse(old_bh);
+ }
+}
+
+/*
+ * NOTE! All mapped/uptodate combinations are valid:
+ *
+ * Mapped Uptodate Meaning
+ *
+ * No No "unknown" - must do get_block()
+ * No Yes "hole" - zero-filled
+ * Yes No "allocated" - allocated on disk, not read in
+ * Yes Yes "valid" - allocated and up-to-date in memory.
+ *
+ * "Dirty" is valid only with the last case (mapped+uptodate).
+ */
+
+/*
+ * block_write_full_page() is SMP threaded - the kernel lock is not held.
+ */
+static int __block_write_full_page(struct inode *inode, struct page *page, get_block_t *get_block)
+{
+ int err, i;
+ unsigned long block;
+ struct buffer_head *bh, *head;
+ int need_unlock;
+
+ if (!PageLocked(page))
+ BUG();
+
+ if (!page->buffers)
+ create_empty_buffers(page, inode->i_dev, 1 << inode->i_blkbits);
+ head = page->buffers;
+
+ block = page->index << (PAGE_CACHE_SHIFT - inode->i_blkbits);
+
+ bh = head;
+ i = 0;
+
+ /* Stage 1: make sure we have all the buffers mapped! */
+ do {
+ /*
+ * If the buffer isn't up-to-date, we can't be sure
+ * that the buffer has been initialized with the proper
+ * block number information etc..
+ *
+ * Leave it to the low-level FS to make all those
+ * decisions (block #0 may actually be a valid block)
+ */
+ if (!buffer_mapped(bh)) {
+ err = get_block(inode, block, bh, 1);
+ if (err)
+ goto out;
+ if (buffer_new(bh))
+ unmap_underlying_metadata(bh);
+ }
+ bh = bh->b_this_page;
+ block++;
+ } while (bh != head);
+
+ /* Stage 2: lock the buffers, mark them clean */
+ do {
+ lock_buffer(bh);
+ set_buffer_async_io(bh);
+ set_bit(BH_Uptodate, &bh->b_state);
+ clear_bit(BH_Dirty, &bh->b_state);
+ bh = bh->b_this_page;
+ } while (bh != head);
+
+ /* Stage 3: submit the IO */
+ do {
+ struct buffer_head *next = bh->b_this_page;
+ submit_bh(WRITE, bh);
+ bh = next;
+ } while (bh != head);
+
+ /* Done - end_buffer_io_async will unlock */
+ SetPageUptodate(page);
+
+ wakeup_page_waiters(page);
+
+ return 0;
+
+out:
+ /*
+ * ENOSPC, or some other error. We may already have added some
+ * blocks to the file, so we need to write these out to avoid
+ * exposing stale data.
+ */
+ ClearPageUptodate(page);
+ bh = head;
+ need_unlock = 1;
+ /* Recovery: lock and submit the mapped buffers */
+ do {
+ if (buffer_mapped(bh)) {
+ lock_buffer(bh);
+ set_buffer_async_io(bh);
+ need_unlock = 0;
+ }
+ bh = bh->b_this_page;
+ } while (bh != head);
+ do {
+ struct buffer_head *next = bh->b_this_page;
+ if (buffer_mapped(bh)) {
+ set_bit(BH_Uptodate, &bh->b_state);
+ clear_bit(BH_Dirty, &bh->b_state);
+ submit_bh(WRITE, bh);
+ }
+ bh = next;
+ } while (bh != head);
+ if (need_unlock)
+ UnlockPage(page);
+ wakeup_page_waiters(page);
+ return err;
+}
+
+static int __block_prepare_write(struct inode *inode, struct page *page,
+ unsigned from, unsigned to, get_block_t *get_block)
+{
+ unsigned block_start, block_end;
+ unsigned long block;
+ int err = 0;
+ unsigned blocksize, bbits;
+ struct buffer_head *bh, *head, *wait[2], **wait_bh=wait;
+ char *kaddr = kmap(page);
+
+ blocksize = 1 << inode->i_blkbits;
+ if (!page->buffers)
+ create_empty_buffers(page, inode->i_dev, blocksize);
+ head = page->buffers;
+
+ bbits = inode->i_blkbits;
+ block = page->index << (PAGE_CACHE_SHIFT - bbits);
+
+ for(bh = head, block_start = 0; bh != head || !block_start;
+ block++, block_start=block_end, bh = bh->b_this_page) {
+ if (!bh)
+ BUG();
+ block_end = block_start+blocksize;
+ if (block_end <= from)
+ continue;
+ if (block_start >= to)
+ break;
+ clear_bit(BH_New, &bh->b_state);
+ if (!buffer_mapped(bh)) {
+ err = get_block(inode, block, bh, 1);
+ if (err)
+ goto out;
+ if (buffer_new(bh)) {
+ unmap_underlying_metadata(bh);
+ if (Page_Uptodate(page)) {
+ set_bit(BH_Uptodate, &bh->b_state);
+ continue;
+ }
+ if (block_end > to)
+ memset(kaddr+to, 0, block_end-to);
+ if (block_start < from)
+ memset(kaddr+block_start, 0, from-block_start);
+ if (block_end > to || block_start < from)
+ flush_dcache_page(page);
+ continue;
+ }
+ }
+ if (Page_Uptodate(page)) {
+ set_bit(BH_Uptodate, &bh->b_state);
+ continue;
+ }
+ if (!buffer_uptodate(bh) && !buffer_delay(bh) &&
+ (block_start < from || block_end > to)) {
+ ll_rw_block(READ, 1, &bh);
+ *wait_bh++=bh;
+ }
+ }
+ /*
+ * If we issued read requests - let them complete.
+ */
+ while(wait_bh > wait) {
+ wait_on_buffer(*--wait_bh);
+ if (!buffer_uptodate(*wait_bh))
+ return -EIO;
+ }
+ return 0;
+out:
+ /*
+ * Zero out any newly allocated blocks to avoid exposing stale
+ * data. If BH_New is set, we know that the block was newly
+ * allocated in the above loop.
+ *
+ * Details the buffer can be new and uptodate because:
+ * 1) hole in uptodate page, get_block(create) allocate the block,
+ * so the buffer is new and additionally we also mark it uptodate
+ * 2) The buffer is not mapped and uptodate due a previous partial read.
+ *
+ * We can always ignore uptodate buffers here, if you mark a buffer
+ * uptodate you must make sure it contains the right data first.
+ *
+ * We must stop the "undo/clear" fixup pass not at the caller "to"
+ * but at the last block that we successfully arrived in the main loop.
+ */
+ bh = head;
+ to = block_start; /* stop at the last successfully handled block */
+ block_start = 0;
+ do {
+ block_end = block_start+blocksize;
+ if (block_end <= from)
+ goto next_bh;
+ if (block_start >= to)
+ break;
+ if (buffer_new(bh) && !buffer_uptodate(bh)) {
+ memset(kaddr+block_start, 0, bh->b_size);
+ flush_dcache_page(page);
+ set_bit(BH_Uptodate, &bh->b_state);
+ mark_buffer_dirty(bh);
+ }
+next_bh:
+ block_start = block_end;
+ bh = bh->b_this_page;
+ } while (bh != head);
+ return err;
+}
+
+static int __block_commit_write(struct inode *inode, struct page *page,
+ unsigned from, unsigned to)
+{
+ unsigned block_start, block_end;
+ int partial = 0, need_balance_dirty = 0;
+ unsigned blocksize;
+ struct buffer_head *bh, *head;
+
+ blocksize = 1 << inode->i_blkbits;
+
+ for(bh = head = page->buffers, block_start = 0;
+ bh != head || !block_start;
+ block_start=block_end, bh = bh->b_this_page) {
+ block_end = block_start + blocksize;
+ if (block_end <= from || block_start >= to) {
+ if (!buffer_uptodate(bh))
+ partial = 1;
+ } else {
+ set_bit(BH_Uptodate, &bh->b_state);
+ if (!atomic_set_buffer_dirty(bh)) {
+ __mark_dirty(bh);
+ buffer_insert_inode_data_queue(bh, inode);
+ need_balance_dirty = 1;
+ }
+ }
+ }
+
+ if (need_balance_dirty)
+ balance_dirty();
+ /*
+ * is this a partial write that happened to make all buffers
+ * uptodate then we can optimize away a bogus readpage() for
+ * the next read(). Here we 'discover' wether the page went
+ * uptodate as a result of this (potentially partial) write.
+ */
+ if (!partial)
+ SetPageUptodate(page);
+ return 0;
+}
+
+/*
+ * Generic "read page" function for block devices that have the normal
+ * get_block functionality. This is most of the block device filesystems.
+ * Reads the page asynchronously --- the unlock_buffer() and
+ * mark_buffer_uptodate() functions propagate buffer state into the
+ * page struct once IO has completed.
+ */
+int block_read_full_page(struct page *page, get_block_t *get_block)
+{
+ struct inode *inode = page->mapping->host;
+ unsigned long iblock, lblock;
+ struct buffer_head *bh, *head, *arr[MAX_BUF_PER_PAGE];
+ unsigned int blocksize, blocks;
+ int nr, i;
+
+ if (!PageLocked(page))
+ PAGE_BUG(page);
+ blocksize = 1 << inode->i_blkbits;
+ if (!page->buffers)
+ create_empty_buffers(page, inode->i_dev, blocksize);
+ head = page->buffers;
+
+ blocks = PAGE_CACHE_SIZE >> inode->i_blkbits;
+ iblock = page->index << (PAGE_CACHE_SHIFT - inode->i_blkbits);
+ lblock = (inode->i_size+blocksize-1) >> inode->i_blkbits;
+ bh = head;
+ nr = 0;
+ i = 0;
+
+ do {
+ if (buffer_uptodate(bh))
+ continue;
+
+ if (!buffer_mapped(bh)) {
+ if (iblock < lblock) {
+ if (get_block(inode, iblock, bh, 0))
+ SetPageError(page);
+ }
+ if (!buffer_mapped(bh)) {
+ memset(kmap(page) + i*blocksize, 0, blocksize);
+ flush_dcache_page(page);
+ kunmap(page);
+ set_bit(BH_Uptodate, &bh->b_state);
+ continue;
+ }
+ /* get_block() might have updated the buffer synchronously */
+ if (buffer_uptodate(bh))
+ continue;
+ }
+
+ arr[nr] = bh;
+ nr++;
+ } while (i++, iblock++, (bh = bh->b_this_page) != head);
+
+ if (!nr) {
+ /*
+ * All buffers are uptodate - we can set the page uptodate
+ * as well. But not if get_block() returned an error.
+ */
+ if (!PageError(page))
+ SetPageUptodate(page);
+ UnlockPage(page);
+ return 0;
+ }
+
+ /* Stage two: lock the buffers */
+ for (i = 0; i < nr; i++) {
+ struct buffer_head * bh = arr[i];
+ lock_buffer(bh);
+ set_buffer_async_io(bh);
+ }
+
+ /* Stage 3: start the IO */
+ for (i = 0; i < nr; i++) {
+ struct buffer_head * bh = arr[i];
+ if (buffer_uptodate(bh))
+ end_buffer_io_async(bh, 1);
+ else
+ submit_bh(READ, bh);
+ }
+
+ wakeup_page_waiters(page);
+
+ return 0;
+}
+
+/* utility function for filesystems that need to do work on expanding
+ * truncates. Uses prepare/commit_write to allow the filesystem to
+ * deal with the hole.
+ */
+int generic_cont_expand(struct inode *inode, loff_t size)
+{
+ struct address_space *mapping = inode->i_mapping;
+ struct page *page;
+ unsigned long index, offset, limit;
+ int err;
+
+ err = -EFBIG;
+ limit = current->rlim[RLIMIT_FSIZE].rlim_cur;
+ if (limit != RLIM_INFINITY && size > (loff_t)limit) {
+ send_sig(SIGXFSZ, current, 0);
+ goto out;
+ }
+ if (size > inode->i_sb->s_maxbytes)
+ goto out;
+
+ offset = (size & (PAGE_CACHE_SIZE-1)); /* Within page */
+
+ /* ugh. in prepare/commit_write, if from==to==start of block, we
+ ** skip the prepare. make sure we never send an offset for the start
+ ** of a block
+ */
+ if ((offset & (inode->i_sb->s_blocksize - 1)) == 0) {
+ offset++;
+ }
+ index = size >> PAGE_CACHE_SHIFT;
+ err = -ENOMEM;
+ page = grab_cache_page(mapping, index);
+ if (!page)
+ goto out;
+ err = mapping->a_ops->prepare_write(NULL, page, offset, offset);
+ if (!err) {
+ err = mapping->a_ops->commit_write(NULL, page, offset, offset);
+ }
+ UnlockPage(page);
+ page_cache_release(page);
+ if (err > 0)
+ err = 0;
+out:
+ return err;
+}
+
+/*
+ * For moronic filesystems that do not allow holes in file.
+ * We may have to extend the file.
+ */
+
+int cont_prepare_write(struct page *page, unsigned offset, unsigned to, get_block_t *get_block, unsigned long *bytes)
+{
+ struct address_space *mapping = page->mapping;
+ struct inode *inode = mapping->host;
+ struct page *new_page;
+ unsigned long pgpos;
+ long status;
+ unsigned zerofrom;
+ unsigned blocksize = 1 << inode->i_blkbits;
+ char *kaddr;
+
+ while(page->index > (pgpos = *bytes>>PAGE_CACHE_SHIFT)) {
+ status = -ENOMEM;
+ new_page = grab_cache_page(mapping, pgpos);
+ if (!new_page)
+ goto out;
+ /* we might sleep */
+ if (*bytes>>PAGE_CACHE_SHIFT != pgpos) {
+ UnlockPage(new_page);
+ page_cache_release(new_page);
+ continue;
+ }
+ zerofrom = *bytes & ~PAGE_CACHE_MASK;
+ if (zerofrom & (blocksize-1)) {
+ *bytes |= (blocksize-1);
+ (*bytes)++;
+ }
+ status = __block_prepare_write(inode, new_page, zerofrom,
+ PAGE_CACHE_SIZE, get_block);
+ if (status)
+ goto out_unmap;
+ kaddr = page_address(new_page);
+ memset(kaddr+zerofrom, 0, PAGE_CACHE_SIZE-zerofrom);
+ flush_dcache_page(new_page);
+ __block_commit_write(inode, new_page, zerofrom, PAGE_CACHE_SIZE);
+ kunmap(new_page);
+ UnlockPage(new_page);
+ page_cache_release(new_page);
+ }
+
+ if (page->index < pgpos) {
+ /* completely inside the area */
+ zerofrom = offset;
+ } else {
+ /* page covers the boundary, find the boundary offset */
+ zerofrom = *bytes & ~PAGE_CACHE_MASK;
+
+ /* if we will expand the thing last block will be filled */
+ if (to > zerofrom && (zerofrom & (blocksize-1))) {
+ *bytes |= (blocksize-1);
+ (*bytes)++;
+ }
+
+ /* starting below the boundary? Nothing to zero out */
+ if (offset <= zerofrom)
+ zerofrom = offset;
+ }
+ status = __block_prepare_write(inode, page, zerofrom, to, get_block);
+ if (status)
+ goto out1;
+ kaddr = page_address(page);
+ if (zerofrom < offset) {
+ memset(kaddr+zerofrom, 0, offset-zerofrom);
+ flush_dcache_page(page);
+ __block_commit_write(inode, page, zerofrom, offset);
+ }
+ return 0;
+out1:
+ ClearPageUptodate(page);
+ kunmap(page);
+ return status;
+
+out_unmap:
+ ClearPageUptodate(new_page);
+ kunmap(new_page);
+ UnlockPage(new_page);
+ page_cache_release(new_page);
+out:
+ return status;
+}
+
+int block_prepare_write(struct page *page, unsigned from, unsigned to,
+ get_block_t *get_block)
+{
+ struct inode *inode = page->mapping->host;
+ int err = __block_prepare_write(inode, page, from, to, get_block);
+ if (err) {
+ ClearPageUptodate(page);
+ kunmap(page);
+ }
+ return err;
+}
+
+int block_commit_write(struct page *page, unsigned from, unsigned to)
+{
+ struct inode *inode = page->mapping->host;
+ __block_commit_write(inode,page,from,to);
+ kunmap(page);
+ return 0;
+}
+
+int generic_commit_write(struct file *file, struct page *page,
+ unsigned from, unsigned to)
+{
+ struct inode *inode = page->mapping->host;
+ loff_t pos = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to;
+ __block_commit_write(inode,page,from,to);
+ kunmap(page);
+ if (pos > inode->i_size) {
+ inode->i_size = pos;
+ mark_inode_dirty(inode);
+ }
+ return 0;
+}
+
+int block_truncate_page(struct address_space *mapping, loff_t from, get_block_t *get_block)
+{
+ unsigned long index = from >> PAGE_CACHE_SHIFT;
+ unsigned offset = from & (PAGE_CACHE_SIZE-1);
+ unsigned blocksize, iblock, length, pos;
+ struct inode *inode = mapping->host;
+ struct page *page;
+ struct buffer_head *bh;
+ int err;
+
+ blocksize = 1 << inode->i_blkbits;
+ length = offset & (blocksize - 1);
+
+ /* Block boundary? Nothing to do */
+ if (!length)
+ return 0;
+
+ length = blocksize - length;
+ iblock = index << (PAGE_CACHE_SHIFT - inode->i_blkbits);
+
+ page = grab_cache_page(mapping, index);
+ err = -ENOMEM;
+ if (!page)
+ goto out;
+
+ if (!page->buffers)
+ create_empty_buffers(page, inode->i_dev, blocksize);
+
+ /* Find the buffer that contains "offset" */
+ bh = page->buffers;
+ pos = blocksize;
+ while (offset >= pos) {
+ bh = bh->b_this_page;
+ iblock++;
+ pos += blocksize;
+ }
+
+ err = 0;
+ if (!buffer_mapped(bh)) {
+ /* Hole? Nothing to do */
+ if (buffer_uptodate(bh))
+ goto unlock;
+ get_block(inode, iblock, bh, 0);
+ /* Still unmapped? Nothing to do */
+ if (!buffer_mapped(bh))
+ goto unlock;
+ }
+
+ /* Ok, it's mapped. Make sure it's up-to-date */
+ if (Page_Uptodate(page))
+ set_bit(BH_Uptodate, &bh->b_state);
+
+ if (!buffer_uptodate(bh) && !buffer_delay(bh)) {
+ err = -EIO;
+ ll_rw_block(READ, 1, &bh);
+ wait_on_buffer(bh);
+ /* Uhhuh. Read error. Complain and punt. */
+ if (!buffer_uptodate(bh))
+ goto unlock;
+ }
+
+ memset(kmap(page) + offset, 0, length);
+ flush_dcache_page(page);
+ kunmap(page);
+
+ if (!atomic_set_buffer_dirty(bh)) {
+ __mark_dirty(bh);
+ buffer_insert_inode_data_queue(bh, inode);
+ balance_dirty();
+ }
+
+ err = 0;
+
+unlock:
+ UnlockPage(page);
+ page_cache_release(page);
+out:
+ return err;
+}
+
+int block_write_full_page(struct page *page, get_block_t *get_block)
+{
+ struct inode *inode = page->mapping->host;
+ unsigned long end_index = inode->i_size >> PAGE_CACHE_SHIFT;
+ unsigned offset;
+ int err;
+
+ /* easy case */
+ if (page->index < end_index)
+ return __block_write_full_page(inode, page, get_block);
+
+ /* things got complicated... */
+ offset = inode->i_size & (PAGE_CACHE_SIZE-1);
+ /* OK, are we completely out? */
+ if (page->index >= end_index+1 || !offset) {
+ UnlockPage(page);
+ return -EIO;
+ }
+
+ /* Sigh... will have to work, then... */
+ err = __block_prepare_write(inode, page, 0, offset, get_block);
+ if (!err) {
+ memset(page_address(page) + offset, 0, PAGE_CACHE_SIZE - offset);
+ flush_dcache_page(page);
+ __block_commit_write(inode,page,0,offset);
+done:
+ kunmap(page);
+ UnlockPage(page);
+ return err;
+ }
+ ClearPageUptodate(page);
+ goto done;
+}
+
+/*
+ * Commence writeout of all the buffers against a page. The
+ * page must be locked. Returns zero on success or a negative
+ * errno.
+ */
+int writeout_one_page(struct page *page)
+{
+ struct buffer_head *bh, *head = page->buffers;
+
+ if (!PageLocked(page))
+ BUG();
+ bh = head;
+ do {
+ if (buffer_locked(bh) || !buffer_dirty(bh) || !buffer_uptodate(bh))
+ continue;
+
+ bh->b_flushtime = jiffies;
+ ll_rw_block(WRITE, 1, &bh);
+ } while ((bh = bh->b_this_page) != head);
+ return 0;
+}
+EXPORT_SYMBOL(writeout_one_page);
+
+/*
+ * Wait for completion of I/O of all buffers against a page. The page
+ * must be locked. Returns zero on success or a negative errno.
+ */
+int waitfor_one_page(struct page *page)
+{
+ int error = 0;
+ struct buffer_head *bh, *head = page->buffers;
+
+ bh = head;
+ do {
+ wait_on_buffer(bh);
+ if (buffer_req(bh) && !buffer_uptodate(bh))
+ error = -EIO;
+ } while ((bh = bh->b_this_page) != head);
+ return error;
+}
+EXPORT_SYMBOL(waitfor_one_page);
+
+int generic_block_bmap(struct address_space *mapping, long block, get_block_t *get_block)
+{
+ struct buffer_head tmp;
+ struct inode *inode = mapping->host;
+ tmp.b_state = 0;
+ tmp.b_blocknr = 0;
+ get_block(inode, block, &tmp, 0);
+ return tmp.b_blocknr;
+}
+
+int generic_direct_IO(int rw, struct inode * inode, struct kiobuf * iobuf, unsigned long blocknr, int blocksize, get_block_t * get_block)
+{
+ int i, nr_blocks, retval;
+ unsigned long * blocks = iobuf->blocks;
+ int length;
+ int beyond_eof = 0;
+
+ length = iobuf->length;
+ nr_blocks = length / blocksize;
+ /* build the blocklist */
+ for (i = 0; i < nr_blocks; i++, blocknr++) {
+ struct buffer_head bh;
+
+ bh.b_state = 0;
+ bh.b_dev = inode->i_dev;
+ bh.b_size = blocksize;
+ bh.b_page = NULL;
+
+ if (((loff_t) blocknr) * blocksize >= inode->i_size)
+ beyond_eof = 1;
+
+ /* Only allow get_block to create new blocks if we are safely
+ beyond EOF. O_DIRECT is unsafe inside sparse files. */
+ retval = get_block(inode, blocknr, &bh,
+ ((rw != READ) && beyond_eof));
+
+ if (retval) {
+ if (!i)
+ /* report error to userspace */
+ goto out;
+ else
+ /* do short I/O until 'i' */
+ break;
+ }
+
+ if (rw == READ) {
+ if (buffer_new(&bh))
+ BUG();
+ if (!buffer_mapped(&bh)) {
+ /* there was an hole in the filesystem */
+ blocks[i] = -1UL;
+ continue;
+ }
+ } else {
+ if (buffer_new(&bh))
+ unmap_underlying_metadata(&bh);
+ if (!buffer_mapped(&bh))
+ /* upper layers need to pass the error on or
+ * fall back to buffered IO. */
+ return -ENOTBLK;
+ }
+ blocks[i] = bh.b_blocknr;
+ }
+
+ /* patch length to handle short I/O */
+ iobuf->length = i * blocksize;
+ if (!beyond_eof)
+ up(&inode->i_sem);
+ retval = brw_kiovec(rw, 1, &iobuf, inode->i_dev, iobuf->blocks, blocksize);
+ if (!beyond_eof)
+ down(&inode->i_sem);
+ /* restore orig length */
+ iobuf->length = length;
+ out:
+
+ return retval;
+}
+
+/*
+ * IO completion routine for a buffer_head being used for kiobuf IO: we
+ * can't dispatch the kiobuf callback until io_count reaches 0.
+ */
+
+static void end_buffer_io_kiobuf(struct buffer_head *bh, int uptodate)
+{
+ struct kiobuf *kiobuf;
+
+ mark_buffer_uptodate(bh, uptodate);
+
+ kiobuf = bh->b_private;
+ end_kio_request(kiobuf, uptodate);
+ unlock_buffer(bh);
+}
+
+/*
+ * For brw_kiovec: submit a set of buffer_head temporary IOs and wait
+ * for them to complete. Clean up the buffer_heads afterwards.
+ */
+
+static int wait_kio(int rw, int nr, struct buffer_head *bh[], int size)
+{
+ int iosize, err;
+ int i;
+ struct buffer_head *tmp;
+
+ iosize = 0;
+ err = 0;
+
+ for (i = nr; --i >= 0; ) {
+ iosize += size;
+ tmp = bh[i];
+ wait_on_buffer(tmp);
+
+ if (!buffer_uptodate(tmp)) {
+ /* We are traversing bh'es in reverse order so
+ clearing iosize on error calculates the
+ amount of IO before the first error. */
+ iosize = 0;
+ err = -EIO;
+ }
+ }
+
+ if (iosize)
+ return iosize;
+ return err;
+}
+
+/*
+ * Start I/O on a physical range of kernel memory, defined by a vector
+ * of kiobuf structs (much like a user-space iovec list).
+ *
+ * The kiobuf must already be locked for IO. IO is submitted
+ * asynchronously: you need to check page->locked and page->uptodate.
+ *
+ * It is up to the caller to make sure that there are enough blocks
+ * passed in to completely map the iobufs to disk.
+ */
+
+int brw_kiovec(int rw, int nr, struct kiobuf *iovec[],
+ kdev_t dev, unsigned long b[], int size)
+{
+ int err;
+ int length;
+ int transferred;
+ int i;
+ int bufind;
+ int pageind;
+ int bhind;
+ int offset;
+ unsigned long blocknr;
+ struct kiobuf * iobuf = NULL;
+ struct page * map;
+ struct buffer_head *tmp, **bhs = NULL;
+
+ if (!nr)
+ return 0;
+
+ /*
+ * First, do some alignment and validity checks
+ */
+ for (i = 0; i < nr; i++) {
+ iobuf = iovec[i];
+ if ((iobuf->offset & (size-1)) ||
+ (iobuf->length & (size-1)))
+ return -EINVAL;
+ if (!iobuf->nr_pages)
+ panic("brw_kiovec: iobuf not initialised");
+ }
+
+ /*
+ * OK to walk down the iovec doing page IO on each page we find.
+ */
+ bufind = bhind = transferred = err = 0;
+ for (i = 0; i < nr; i++) {
+ iobuf = iovec[i];
+ offset = iobuf->offset;
+ length = iobuf->length;
+ iobuf->errno = 0;
+ if (!bhs)
+ bhs = iobuf->bh;
+
+ for (pageind = 0; pageind < iobuf->nr_pages; pageind++) {
+ map = iobuf->maplist[pageind];
+ if (!map) {
+ err = -EFAULT;
+ goto finished;
+ }
+
+ while (length > 0) {
+ blocknr = b[bufind++];
+ if (blocknr == -1UL) {
+ if (rw == READ) {
+ /* there was an hole in the filesystem */
+ memset(kmap(map) + offset, 0, size);
+ flush_dcache_page(map);
+ kunmap(map);
+
+ transferred += size;
+ goto skip_block;
+ } else
+ BUG();
+ }
+ tmp = bhs[bhind++];
+
+ tmp->b_size = size;
+ set_bh_page(tmp, map, offset);
+ tmp->b_this_page = tmp;
+
+ init_buffer(tmp, end_buffer_io_kiobuf, iobuf);
+ tmp->b_dev = dev;
+ tmp->b_blocknr = blocknr;
+ tmp->b_state = (1 << BH_Mapped) | (1 << BH_Lock) | (1 << BH_Req);
+
+ if (rw == WRITE) {
+ set_bit(BH_Uptodate, &tmp->b_state);
+ clear_bit(BH_Dirty, &tmp->b_state);
+ } else
+ set_bit(BH_Uptodate, &tmp->b_state);
+
+ atomic_inc(&iobuf->io_count);
+ submit_bh(rw, tmp);
+ /*
+ * Wait for IO if we have got too much
+ */
+ if (bhind >= KIO_MAX_SECTORS) {
+ kiobuf_wait_for_io(iobuf); /* wake-one */
+ err = wait_kio(rw, bhind, bhs, size);
+ if (err >= 0)
+ transferred += err;
+ else
+ goto finished;
+ bhind = 0;
+ }
+
+ skip_block:
+ length -= size;
+ offset += size;
+
+ if (offset >= PAGE_SIZE) {
+ offset = 0;
+ break;
+ }
+ } /* End of block loop */
+ } /* End of page loop */
+ } /* End of iovec loop */
+
+ /* Is there any IO still left to submit? */
+ if (bhind) {
+ kiobuf_wait_for_io(iobuf); /* wake-one */
+ err = wait_kio(rw, bhind, bhs, size);
+ if (err >= 0)
+ transferred += err;
+ else
+ goto finished;
+ }
+
+ finished:
+ if (transferred)
+ return transferred;
+ return err;
+}
+
+/*
+ * Start I/O on a page.
+ * This function expects the page to be locked and may return
+ * before I/O is complete. You then have to check page->locked
+ * and page->uptodate.
+ *
+ * brw_page() is SMP-safe, although it's being called with the
+ * kernel lock held - but the code is ready.
+ *
+ * FIXME: we need a swapper_inode->get_block function to remove
+ * some of the bmap kludges and interface ugliness here.
+ */
+int brw_page(int rw, struct page *page, kdev_t dev, int b[], int size)
+{
+ struct buffer_head *head, *bh;
+
+ if (!PageLocked(page))
+ panic("brw_page: page not locked for I/O");
+
+ if (!page->buffers)
+ create_empty_buffers(page, dev, size);
+ head = bh = page->buffers;
+
+ /* Stage 1: lock all the buffers */
+ do {
+ lock_buffer(bh);
+ bh->b_blocknr = *(b++);
+ set_bit(BH_Mapped, &bh->b_state);
+ set_buffer_async_io(bh);
+ bh = bh->b_this_page;
+ } while (bh != head);
+
+ /* Stage 2: start the IO */
+ do {
+ struct buffer_head *next = bh->b_this_page;
+ submit_bh(rw, bh);
+ bh = next;
+ } while (bh != head);
+ wakeup_page_waiters(page);
+ return 0;
+}
+
+int block_symlink(struct inode *inode, const char *symname, int len)
+{
+ struct address_space *mapping = inode->i_mapping;
+ struct page *page = grab_cache_page(mapping, 0);
+ int err = -ENOMEM;
+ char *kaddr;
+
+ if (!page)
+ goto fail;
+ err = mapping->a_ops->prepare_write(NULL, page, 0, len-1);
+ if (err)
+ goto fail_map;
+ kaddr = page_address(page);
+ memcpy(kaddr, symname, len-1);
+ mapping->a_ops->commit_write(NULL, page, 0, len-1);
+ /*
+ * Notice that we are _not_ going to block here - end of page is
+ * unmapped, so this will only try to map the rest of page, see
+ * that it is unmapped (typically even will not look into inode -
+ * ->i_size will be enough for everything) and zero it out.
+ * OTOH it's obviously correct and should make the page up-to-date.
+ */
+ err = mapping->a_ops->readpage(NULL, page);
+ wait_on_page(page);
+ page_cache_release(page);
+ if (err < 0)
+ goto fail;
+ mark_inode_dirty(inode);
+ return 0;
+fail_map:
+ UnlockPage(page);
+ page_cache_release(page);
+fail:
+ return err;
+}
+
+static inline void link_dev_buffers(struct page * page, struct buffer_head *head)
+{
+ struct buffer_head *bh, *tail;
+
+ bh = head;
+ do {
+ tail = bh;
+ bh = bh->b_this_page;
+ } while (bh);
+ tail->b_this_page = head;
+ page->buffers = head;
+ page_cache_get(page);
+}
+
+/*
+ * Create the page-cache page that contains the requested block
+ */
+static struct page * grow_dev_page(struct block_device *bdev, unsigned long index, int size)
+{
+ struct page * page;
+ struct buffer_head *bh;
+
+ page = find_or_create_page(bdev->bd_inode->i_mapping, index, GFP_NOFS);
+ if (!page)
+ return NULL;
+
+ if (!PageLocked(page))
+ BUG();
+
+ bh = page->buffers;
+ if (bh) {
+ if (bh->b_size == size)
+ return page;
+ if (!try_to_free_buffers(page, GFP_NOFS))
+ goto failed;
+ }
+
+ bh = create_buffers(page, size, 0);
+ if (!bh)
+ goto failed;
+ link_dev_buffers(page, bh);
+ return page;
+
+failed:
+ UnlockPage(page);
+ page_cache_release(page);
+ return NULL;
+}
+
+static void hash_page_buffers(struct page *page, kdev_t dev, int block, int size)
+{
+ struct buffer_head *head = page->buffers;
+ struct buffer_head *bh = head;
+ unsigned int uptodate;
+
+ uptodate = 1 << BH_Mapped;
+ if (Page_Uptodate(page))
+ uptodate |= 1 << BH_Uptodate;
+
+ write_lock(&hash_table_lock);
+ do {
+ if (!(bh->b_state & (1 << BH_Mapped))) {
+ init_buffer(bh, NULL, NULL);
+ bh->b_dev = dev;
+ bh->b_blocknr = block;
+ bh->b_state = uptodate;
+ }
+
+ /* Insert the buffer into the hash lists if necessary */
+ if (!bh->b_pprev)
+ __insert_into_hash_list(bh);
+
+ block++;
+ bh = bh->b_this_page;
+ } while (bh != head);
+ write_unlock(&hash_table_lock);
+}
+
+/*
+ * Try to increase the number of buffers available: the size argument
+ * is used to determine what kind of buffers we want.
+ */
+static int grow_buffers(kdev_t dev, unsigned long block, int size)
+{
+ struct page * page;
+ struct block_device *bdev;
+ unsigned long index;
+ int sizebits;
+
+ /* Size must be multiple of hard sectorsize */
+ if (size & (get_hardsect_size(dev)-1))
+ BUG();
+ /* Size must be within 512 bytes and PAGE_SIZE */
+ if (size < 512 || size > PAGE_SIZE)
+ BUG();
+
+ sizebits = -1;
+ do {
+ sizebits++;
+ } while ((size << sizebits) < PAGE_SIZE);
+
+ index = block >> sizebits;
+ block = index << sizebits;
+
+ bdev = bdget(kdev_t_to_nr(dev));
+ if (!bdev) {
+ printk("No block device for %s\n", kdevname(dev));
+ BUG();
+ }
+
+ /* Create a page with the proper size buffers.. */
+ page = grow_dev_page(bdev, index, size);
+
+ /* This is "wrong" - talk to Al Viro */
+ atomic_dec(&bdev->bd_count);
+ if (!page)
+ return 0;
+
+ /* Hash in the buffers on the hash list */
+ hash_page_buffers(page, dev, block, size);
+ UnlockPage(page);
+ page_cache_release(page);
+
+ /* We hashed up this page, so increment buffermem */
+ atomic_inc(&buffermem_pages);
+ return 1;
+}
+
+/*
+ * The first time the VM inspects a page which has locked buffers, it
+ * will just mark it as needing waiting upon on the scan of the page LRU.
+ * BH_Wait_IO is used for this.
+ *
+ * The second time the VM visits the page, if it still has locked
+ * buffers, it is time to start writing them out. (BH_Wait_IO was set).
+ *
+ * The third time the VM visits the page, if the I/O hasn't completed
+ * then it's time to wait upon writeout. BH_Lock and BH_Launder are
+ * used for this.
+ *
+ * There is also the case of buffers which were locked by someone else
+ * - write(2) callers, bdflush, etc. There can be a huge number of these
+ * and we don't want to just skip them all and fail the page allocation.
+ * We want to be able to wait on these buffers as well.
+ *
+ * The BH_Launder bit is set in submit_bh() to indicate that I/O is
+ * underway against the buffer, doesn't matter who started it - we know
+ * that the buffer will eventually come unlocked, and so it's safe to
+ * wait on it.
+ *
+ * The caller holds the page lock and the caller will free this page
+ * into current->local_page, so by waiting on the page's buffers the
+ * caller is guaranteed to obtain this page.
+ *
+ * sync_page_buffers() will sort-of return true if all the buffers
+ * against this page are freeable, so try_to_free_buffers() should
+ * try to free the page's buffers a second time. This is a bit
+ * broken for blocksize < PAGE_CACHE_SIZE, but not very importantly.
+ */
+static int sync_page_buffers(struct buffer_head *head)
+{
+ struct buffer_head * bh = head;
+ int tryagain = 1;
+
+ do {
+ if (!buffer_dirty(bh) && !buffer_locked(bh))
+ continue;
+
+ /* Don't start IO first time around.. */
+ if (!test_and_set_bit(BH_Wait_IO, &bh->b_state)) {
+ tryagain = 0;
+ continue;
+ }
+
+ /* Second time through we start actively writing out.. */
+ if (test_and_set_bit(BH_Lock, &bh->b_state)) {
+ if (unlikely(!buffer_launder(bh))) {
+ tryagain = 0;
+ continue;
+ }
+ wait_on_buffer(bh);
+ tryagain = 1;
+ continue;
+ }
+
+ if (!atomic_set_buffer_clean(bh)) {
+ unlock_buffer(bh);
+ continue;
+ }
+
+ __mark_buffer_clean(bh);
+ get_bh(bh);
+ bh->b_end_io = end_buffer_io_sync;
+ submit_bh(WRITE, bh);
+ tryagain = 0;
+ } while ((bh = bh->b_this_page) != head);
+
+ return tryagain;
+}
+
+/*
+ * Can the buffer be thrown out?
+ */
+#define BUFFER_BUSY_BITS ((1<<BH_Dirty) | (1<<BH_Lock))
+#define buffer_busy(bh) (atomic_read(&(bh)->b_count) | ((bh)->b_state & BUFFER_BUSY_BITS))
+
+/*
+ * try_to_free_buffers() checks if all the buffers on this particular page
+ * are unused, and free's the page if so.
+ *
+ * Wake up bdflush() if this fails - if we're running low on memory due
+ * to dirty buffers, we need to flush them out as quickly as possible.
+ *
+ * NOTE: There are quite a number of ways that threads of control can
+ * obtain a reference to a buffer head within a page. So we must
+ * lock out all of these paths to cleanly toss the page.
+ */
+int fastcall try_to_free_buffers(struct page * page, unsigned int gfp_mask)
+{
+ struct buffer_head * tmp, * bh = page->buffers;
+
+cleaned_buffers_try_again:
+ spin_lock(&lru_list_lock);
+ write_lock(&hash_table_lock);
+ tmp = bh;
+ do {
+ if (buffer_busy(tmp))
+ goto busy_buffer_page;
+ tmp = tmp->b_this_page;
+ } while (tmp != bh);
+
+ spin_lock(&unused_list_lock);
+ tmp = bh;
+
+ /* if this buffer was hashed, this page counts as buffermem */
+ if (bh->b_pprev)
+ atomic_dec(&buffermem_pages);
+ do {
+ struct buffer_head * p = tmp;
+ tmp = tmp->b_this_page;
+
+ if (p->b_dev == B_FREE) BUG();
+
+ remove_inode_queue(p);
+ __remove_from_queues(p);
+ __put_unused_buffer_head(p);
+ } while (tmp != bh);
+ spin_unlock(&unused_list_lock);
+
+ /* Wake up anyone waiting for buffer heads */
+ wake_up(&buffer_wait);
+
+ /* And free the page */
+ page->buffers = NULL;
+ page_cache_release(page);
+ write_unlock(&hash_table_lock);
+ spin_unlock(&lru_list_lock);
+ return 1;
+
+busy_buffer_page:
+ /* Uhhuh, start writeback so that we don't end up with all dirty pages */
+ write_unlock(&hash_table_lock);
+ spin_unlock(&lru_list_lock);
+ gfp_mask = pf_gfp_mask(gfp_mask);
+ if (gfp_mask & __GFP_IO) {
+ if ((gfp_mask & __GFP_HIGHIO) || !PageHighMem(page)) {
+ if (sync_page_buffers(bh)) {
+ /* no IO or waiting next time */
+ gfp_mask = 0;
+ goto cleaned_buffers_try_again;
+ }
+ }
+ }
+ if (balance_dirty_state() >= 0)
+ wakeup_bdflush();
+ return 0;
+}
+EXPORT_SYMBOL(try_to_free_buffers);
+
+/* ================== Debugging =================== */
+
+void show_buffers(void)
+{
+#ifdef CONFIG_SMP
+ struct buffer_head * bh;
+ int delalloc = 0, found = 0, locked = 0, dirty = 0, used = 0, lastused = 0;
+ int nlist;
+ static char *buf_types[NR_LIST] = { "CLEAN", "LOCKED", "DIRTY", };
+#endif
+
+ printk("Buffer memory: %6dkB\n",
+ atomic_read(&buffermem_pages) << (PAGE_SHIFT-10));
+
+ printk("Cache memory: %6ldkB\n",
+ (page_cache_size - atomic_read(&buffermem_pages)) << (PAGE_SHIFT-10));
+
+#ifdef CONFIG_SMP /* trylock does nothing on UP and so we could deadlock */
+ if (!spin_trylock(&lru_list_lock))
+ return;
+ for(nlist = 0; nlist < NR_LIST; nlist++) {
+ delalloc = found = locked = dirty = used = lastused = 0;
+ bh = lru_list[nlist];
+ if(!bh) continue;
+
+ do {
+ found++;
+ if (buffer_locked(bh))
+ locked++;
+ if (buffer_dirty(bh))
+ dirty++;
+ if (buffer_delay(bh))
+ delalloc++;
+ if (atomic_read(&bh->b_count))
+ used++, lastused = found;
+ bh = bh->b_next_free;
+ } while (bh != lru_list[nlist]);
+ {
+ int tmp = nr_buffers_type[nlist];
+ if (found != tmp)
+ printk("%9s: BUG -> found %d, reported %d\n",
+ buf_types[nlist], found, tmp);
+ }
+ printk("%9s: %d buffers, %lu kbyte, %d used (last=%d), "
+ "%d locked, %d dirty, %d delay\n",
+ buf_types[nlist], found, size_buffers_type[nlist]>>10,
+ used, lastused, locked, dirty, delalloc);
+ }
+ spin_unlock(&lru_list_lock);
+#endif
+}
+
+/* ===================== Init ======================= */
+
+/*
+ * allocate the hash table and init the free list
+ * Use gfp() for the hash table to decrease TLB misses, use
+ * SLAB cache for buffer heads.
+ */
+void __init buffer_init(unsigned long mempages)
+{
+ int order, i;
+ unsigned int nr_hash;
+
+ /* The buffer cache hash table is less important these days,
+ * trim it a bit.
+ */
+ mempages >>= 14;
+
+ mempages *= sizeof(struct buffer_head *);
+
+ for (order = 0; (1 << order) < mempages; order++)
+ ;
+
+ /* try to allocate something until we get it or we're asking
+ for something that is really too small */
+
+ do {
+ unsigned long tmp;
+
+ nr_hash = (PAGE_SIZE << order) / sizeof(struct buffer_head *);
+ bh_hash_mask = (nr_hash - 1);
+
+ tmp = nr_hash;
+ bh_hash_shift = 0;
+ while((tmp >>= 1UL) != 0UL)
+ bh_hash_shift++;
+
+ hash_table = (struct buffer_head **)
+ __get_free_pages(GFP_ATOMIC, order);
+ } while (hash_table == NULL && --order > 0);
+ printk(KERN_INFO "Buffer cache hash table entries: %d (order: %d, %ld bytes)\n",
+ nr_hash, order, (PAGE_SIZE << order));
+
+ if (!hash_table)
+ panic("Failed to allocate buffer hash table\n");
+
+ /* Setup hash chains. */
+ for(i = 0; i < nr_hash; i++)
+ hash_table[i] = NULL;
+
+ /* Setup lru lists. */
+ for(i = 0; i < NR_LIST; i++)
+ lru_list[i] = NULL;
+
+}
+
+
+/* ====================== bdflush support =================== */
+
+/* This is a simple kernel daemon, whose job it is to provide a dynamic
+ * response to dirty buffers. Once this process is activated, we write back
+ * a limited number of buffers to the disks and then go back to sleep again.
+ */
+
+DECLARE_WAIT_QUEUE_HEAD(bdflush_wait);
+
+void wakeup_bdflush(void)
+{
+ wake_up_interruptible(&bdflush_wait);
+}
+
+void wakeup_kupdate(void)
+{
+ if (waitqueue_active(&kupdate_wait))
+ wake_up(&kupdate_wait);
+}
+
+/*
+ * Here we attempt to write back old buffers. We also try to flush inodes
+ * and supers as well, since this function is essentially "update", and
+ * otherwise there would be no way of ensuring that these quantities ever
+ * get written back. Ideally, we would have a timestamp on the inodes
+ * and superblocks so that we could write back only the old ones as well
+ */
+
+static int sync_old_buffers(void)
+{
+ lock_kernel();
+ sync_unlocked_inodes();
+ sync_supers(0, 0);
+ unlock_kernel();
+
+ for (;;) {
+ struct buffer_head *bh;
+
+ spin_lock(&lru_list_lock);
+ bh = lru_list[BUF_DIRTY];
+ if (!bh)
+ break;
+ if (time_before(jiffies, bh->b_flushtime) && !laptop_mode)
+ break;
+ if (write_some_buffers(NODEV))
+ continue;
+ return 0;
+ }
+ spin_unlock(&lru_list_lock);
+ return 0;
+}
+
+int block_sync_page(struct page *page)
+{
+ run_task_queue(&tq_disk);
+ return 0;
+}
+
+/* This is the interface to bdflush. As we get more sophisticated, we can
+ * pass tuning parameters to this "process", to adjust how it behaves.
+ * We would want to verify each parameter, however, to make sure that it
+ * is reasonable. */
+
+asmlinkage long sys_bdflush(int func, long data)
+{
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (func == 1) {
+ /* do_exit directly and let kupdate to do its work alone. */
+ do_exit(0);
+#if 0 /* left here as it's the only example of lazy-mm-stuff used from
+ a syscall that doesn't care about the current mm context. */
+ int error;
+ struct mm_struct *user_mm;
+
+ /*
+ * bdflush will spend all of it's time in kernel-space,
+ * without touching user-space, so we can switch it into
+ * 'lazy TLB mode' to reduce the cost of context-switches
+ * to and from bdflush.
+ */
+ user_mm = start_lazy_tlb();
+ error = sync_old_buffers();
+ end_lazy_tlb(user_mm);
+ return error;
+#endif
+ }
+
+ /* Basically func 1 means read param 1, 2 means write param 1, etc */
+ if (func >= 2) {
+ int i = (func-2) >> 1;
+ if (i >= 0 && i < N_PARAM) {
+ if ((func & 1) == 0)
+ return put_user(bdf_prm.data[i], (int*)data);
+
+ if (data >= bdflush_min[i] && data <= bdflush_max[i]) {
+ bdf_prm.data[i] = data;
+ return 0;
+ }
+ }
+ return -EINVAL;
+ }
+
+ /* Having func 0 used to launch the actual bdflush and then never
+ * return (unless explicitly killed). We return zero here to
+ * remain semi-compatible with present update(8) programs.
+ */
+ return 0;
+}
+
+/*
+ * This is the actual bdflush daemon itself. It used to be started from
+ * the syscall above, but now we launch it ourselves internally with
+ * kernel_thread(...) directly after the first thread in init/main.c
+ */
+int bdflush(void *startup)
+{
+ struct task_struct *tsk = current;
+
+ /*
+ * We have a bare-bones task_struct, and really should fill
+ * in a few more things so "top" and /proc/2/{exe,root,cwd}
+ * display semi-sane things. Not real crucial though...
+ */
+
+ tsk->session = 1;
+ tsk->pgrp = 1;
+ strcpy(tsk->comm, "bdflush");
+
+ /* avoid getting signals */
+ spin_lock_irq(&tsk->sigmask_lock);
+ flush_signals(tsk);
+ sigfillset(&tsk->blocked);
+ recalc_sigpending(tsk);
+ spin_unlock_irq(&tsk->sigmask_lock);
+
+ complete((struct completion *)startup);
+
+ /*
+ * FIXME: The ndirty logic here is wrong. It's supposed to
+ * send bdflush back to sleep after writing ndirty buffers.
+ * In fact, the test is wrong so bdflush will in fact
+ * sleep when bdflush_stop() returns true.
+ *
+ * FIXME: If it proves useful to implement ndirty properly,
+ * then perhaps the value of ndirty should be scaled by the
+ * amount of memory in the machine.
+ */
+ for (;;) {
+ int ndirty = bdf_prm.b_un.ndirty;
+
+ CHECK_EMERGENCY_SYNC
+
+ while (ndirty > 0) {
+ spin_lock(&lru_list_lock);
+ if (!write_some_buffers(NODEV))
+ break;
+ ndirty -= NRSYNC;
+ }
+ if (ndirty > 0 || bdflush_stop())
+ interruptible_sleep_on(&bdflush_wait);
+ }
+}
+
+/*
+ * This is the kernel update daemon. It was used to live in userspace
+ * but since it's need to run safely we want it unkillable by mistake.
+ * You don't need to change your userspace configuration since
+ * the userspace `update` will do_exit(0) at the first sys_bdflush().
+ */
+int kupdate(void *startup)
+{
+ struct task_struct * tsk = current;
+ int interval;
+
+ tsk->session = 1;
+ tsk->pgrp = 1;
+ strcpy(tsk->comm, "kupdated");
+
+ /* sigstop and sigcont will stop and wakeup kupdate */
+ spin_lock_irq(&tsk->sigmask_lock);
+ sigfillset(&tsk->blocked);
+ siginitsetinv(&current->blocked, sigmask(SIGCONT) | sigmask(SIGSTOP));
+ recalc_sigpending(tsk);
+ spin_unlock_irq(&tsk->sigmask_lock);
+
+ complete((struct completion *)startup);
+
+ for (;;) {
+ DECLARE_WAITQUEUE(wait, tsk);
+
+ add_wait_queue(&kupdate_wait, &wait);
+
+ /* update interval */
+ interval = bdf_prm.b_un.interval;
+ if (interval) {
+ tsk->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(interval);
+ } else {
+ tsk->state = TASK_STOPPED;
+ schedule(); /* wait for SIGCONT */
+ }
+ remove_wait_queue(&kupdate_wait, &wait);
+ /* check for sigstop */
+ if (signal_pending(tsk)) {
+ int sig, stopped = 0;
+ struct siginfo info;
+
+ spin_lock_irq(&tsk->sigmask_lock);
+ sig = dequeue_signal(&current->blocked, &info);
+ if (sig == SIGSTOP)
+ stopped = 1;
+ spin_unlock_irq(&tsk->sigmask_lock);
+ if (stopped) {
+ tsk->state = TASK_STOPPED;
+ schedule(); /* wait for SIGCONT */
+ }
+ }
+#ifdef DEBUG
+ printk(KERN_DEBUG "kupdate() activated...\n");
+#endif
+ sync_old_buffers();
+ if (laptop_mode)
+ fsync_dev(NODEV);
+ run_task_queue(&tq_disk);
+ }
+}
+
+static int __init bdflush_init(void)
+{
+ static struct completion startup __initdata = COMPLETION_INITIALIZER(startup);
+
+ kernel_thread(bdflush, &startup, CLONE_FS | CLONE_FILES | CLONE_SIGNAL);
+ wait_for_completion(&startup);
+ kernel_thread(kupdate, &startup, CLONE_FS | CLONE_FILES | CLONE_SIGNAL);
+ wait_for_completion(&startup);
+ return 0;
+}
+
+module_init(bdflush_init)
+
diff --git a/fs/char_dev.c b/fs/char_dev.c
new file mode 100644
index 00000000000000..de15221d0d5ea5
--- /dev/null
+++ b/fs/char_dev.c
@@ -0,0 +1,113 @@
+/*
+ * linux/fs/block_dev.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+
+#define HASH_BITS 6
+#define HASH_SIZE (1UL << HASH_BITS)
+#define HASH_MASK (HASH_SIZE-1)
+static struct list_head cdev_hashtable[HASH_SIZE];
+static spinlock_t cdev_lock = SPIN_LOCK_UNLOCKED;
+static kmem_cache_t * cdev_cachep;
+
+#define alloc_cdev() \
+ ((struct char_device *) kmem_cache_alloc(cdev_cachep, SLAB_KERNEL))
+#define destroy_cdev(cdev) kmem_cache_free(cdev_cachep, (cdev))
+
+static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
+{
+ struct char_device * cdev = (struct char_device *) foo;
+
+ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+ SLAB_CTOR_CONSTRUCTOR)
+ {
+ memset(cdev, 0, sizeof(*cdev));
+ sema_init(&cdev->sem, 1);
+ }
+}
+
+void __init cdev_cache_init(void)
+{
+ int i;
+ struct list_head *head = cdev_hashtable;
+
+ i = HASH_SIZE;
+ do {
+ INIT_LIST_HEAD(head);
+ head++;
+ i--;
+ } while (i);
+
+ cdev_cachep = kmem_cache_create("cdev_cache",
+ sizeof(struct char_device),
+ 0, SLAB_HWCACHE_ALIGN, init_once,
+ NULL);
+ if (!cdev_cachep)
+ panic("Cannot create cdev_cache SLAB cache");
+}
+
+/*
+ * Most likely _very_ bad one - but then it's hardly critical for small
+ * /dev and can be fixed when somebody will need really large one.
+ */
+static inline unsigned long hash(dev_t dev)
+{
+ unsigned long tmp = dev;
+ tmp = tmp + (tmp >> HASH_BITS) + (tmp >> HASH_BITS*2);
+ return tmp & HASH_MASK;
+}
+
+static struct char_device *cdfind(dev_t dev, struct list_head *head)
+{
+ struct list_head *p;
+ struct char_device *cdev;
+ for (p=head->next; p!=head; p=p->next) {
+ cdev = list_entry(p, struct char_device, hash);
+ if (cdev->dev != dev)
+ continue;
+ atomic_inc(&cdev->count);
+ return cdev;
+ }
+ return NULL;
+}
+
+struct char_device *cdget(dev_t dev)
+{
+ struct list_head * head = cdev_hashtable + hash(dev);
+ struct char_device *cdev, *new_cdev;
+ spin_lock(&cdev_lock);
+ cdev = cdfind(dev, head);
+ spin_unlock(&cdev_lock);
+ if (cdev)
+ return cdev;
+ new_cdev = alloc_cdev();
+ if (!new_cdev)
+ return NULL;
+ atomic_set(&new_cdev->count,1);
+ new_cdev->dev = dev;
+ spin_lock(&cdev_lock);
+ cdev = cdfind(dev, head);
+ if (!cdev) {
+ list_add(&new_cdev->hash, head);
+ spin_unlock(&cdev_lock);
+ return new_cdev;
+ }
+ spin_unlock(&cdev_lock);
+ destroy_cdev(new_cdev);
+ return cdev;
+}
+
+void cdput(struct char_device *cdev)
+{
+ if (atomic_dec_and_lock(&cdev->count, &cdev_lock)) {
+ list_del(&cdev->hash);
+ spin_unlock(&cdev_lock);
+ destroy_cdev(cdev);
+ }
+}
+
diff --git a/fs/coda/Makefile b/fs/coda/Makefile
new file mode 100644
index 00000000000000..e169867eeaf13f
--- /dev/null
+++ b/fs/coda/Makefile
@@ -0,0 +1,16 @@
+#
+# Makefile for the Linux Coda filesystem routines.
+#
+
+O_TARGET := coda.o
+
+obj-y := psdev.o cache.o cnode.o inode.o dir.o file.o upcall.o coda_linux.o\
+ symlink.o pioctl.o sysctl.o
+obj-m := $(O_TARGET)
+
+# If you want debugging output, please uncomment the following line.
+
+# EXTRA_CFLAGS += -DDEBUG -DDEBUG_SMB_MALLOC=1
+
+include $(TOPDIR)/Rules.make
+
diff --git a/fs/coda/cache.c b/fs/coda/cache.c
new file mode 100644
index 00000000000000..03ab04df4477ed
--- /dev/null
+++ b/fs/coda/cache.c
@@ -0,0 +1,128 @@
+/*
+ * Cache operations for Coda.
+ * For Linux 2.1: (C) 1997 Carnegie Mellon University
+ * For Linux 2.3: (C) 2000 Carnegie Mellon University
+ *
+ * Carnegie Mellon encourages users of this code to contribute improvements
+ * to the Coda project http://www.coda.cs.cmu.edu/ <coda@cs.cmu.edu>.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <asm/uaccess.h>
+#include <linux/string.h>
+#include <linux/list.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_psdev.h>
+#include <linux/coda_fs_i.h>
+#include <linux/coda_cache.h>
+
+/* replace or extend an acl cache hit */
+void coda_cache_enter(struct inode *inode, int mask)
+{
+ struct coda_inode_info *cii = ITOC(inode);
+
+ if ( !coda_cred_ok(&cii->c_cached_cred) ) {
+ coda_load_creds(&cii->c_cached_cred);
+ cii->c_cached_perm = mask;
+ } else
+ cii->c_cached_perm |= mask;
+}
+
+/* remove cached acl from an inode */
+void coda_cache_clear_inode(struct inode *inode)
+{
+ struct coda_inode_info *cii = ITOC(inode);
+ cii->c_cached_perm = 0;
+}
+
+/* remove all acl caches for a principal (or all principals when cred == NULL)*/
+void coda_cache_clear_all(struct super_block *sb, struct coda_cred *cred)
+{
+ struct coda_sb_info *sbi;
+ struct coda_inode_info *cii;
+ struct list_head *tmp;
+
+ sbi = coda_sbp(sb);
+ if (!sbi) BUG();
+
+ list_for_each(tmp, &sbi->sbi_cihead)
+ {
+ cii = list_entry(tmp, struct coda_inode_info, c_cilist);
+ if (!cred || coda_cred_eq(cred, &cii->c_cached_cred))
+ cii->c_cached_perm = 0;
+ }
+}
+
+
+/* check if the mask has been matched against the acl already */
+int coda_cache_check(struct inode *inode, int mask)
+{
+ struct coda_inode_info *cii = ITOC(inode);
+ int hit;
+
+ hit = ((mask & cii->c_cached_perm) == mask) &&
+ coda_cred_ok(&cii->c_cached_cred);
+
+ CDEBUG(D_CACHE, "%s for ino %ld\n", hit ? "HIT" : "MISS", inode->i_ino);
+ return hit;
+}
+
+
+/* Purging dentries and children */
+/* The following routines drop dentries which are not
+ in use and flag dentries which are in use to be
+ zapped later.
+
+ The flags are detected by:
+ - coda_dentry_revalidate (for lookups) if the flag is C_PURGE
+ - coda_dentry_delete: to remove dentry from the cache when d_count
+ falls to zero
+ - an inode method coda_revalidate (for attributes) if the
+ flag is C_VATTR
+*/
+
+/* this won't do any harm: just flag all children */
+static void coda_flag_children(struct dentry *parent, int flag)
+{
+ struct list_head *child;
+ struct dentry *de;
+
+ spin_lock(&dcache_lock);
+ list_for_each(child, &parent->d_subdirs)
+ {
+ de = list_entry(child, struct dentry, d_child);
+ /* don't know what to do with negative dentries */
+ if ( ! de->d_inode )
+ continue;
+ CDEBUG(D_DOWNCALL, "%d for %*s/%*s\n", flag,
+ de->d_name.len, de->d_name.name,
+ de->d_parent->d_name.len, de->d_parent->d_name.name);
+ coda_flag_inode(de->d_inode, flag);
+ }
+ spin_unlock(&dcache_lock);
+ return;
+}
+
+void coda_flag_inode_children(struct inode *inode, int flag)
+{
+ struct dentry *alias_de;
+
+ if ( !inode || !S_ISDIR(inode->i_mode))
+ return;
+
+ alias_de = d_find_alias(inode);
+ if (!alias_de)
+ return;
+ coda_flag_children(alias_de, flag);
+ shrink_dcache_parent(alias_de);
+ dput(alias_de);
+}
+
diff --git a/fs/coda/cnode.c b/fs/coda/cnode.c
new file mode 100644
index 00000000000000..8827cb653a718a
--- /dev/null
+++ b/fs/coda/cnode.c
@@ -0,0 +1,206 @@
+/* cnode related routines for the coda kernel code
+ (C) 1996 Peter Braam
+ */
+
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/time.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_fs_i.h>
+#include <linux/coda_psdev.h>
+
+extern int coda_debug;
+
+inline int coda_fideq(ViceFid *fid1, ViceFid *fid2)
+{
+ if (fid1->Vnode != fid2->Vnode) return 0;
+ if (fid1->Volume != fid2->Volume) return 0;
+ if (fid1->Unique != fid2->Unique) return 0;
+ return 1;
+}
+
+inline int coda_isnullfid(ViceFid *fid)
+{
+ if (fid->Vnode || fid->Volume || fid->Unique) return 0;
+ return 1;
+}
+
+static int coda_inocmp(struct inode *inode, unsigned long ino, void *opaque)
+{
+ return (coda_fideq((ViceFid *)opaque, &(ITOC(inode)->c_fid)));
+}
+
+static struct inode_operations coda_symlink_inode_operations = {
+ readlink: page_readlink,
+ follow_link: page_follow_link,
+ setattr: coda_notify_change,
+};
+
+/* cnode.c */
+static void coda_fill_inode(struct inode *inode, struct coda_vattr *attr)
+{
+ CDEBUG(D_SUPER, "ino: %ld\n", inode->i_ino);
+
+ if (coda_debug & D_SUPER )
+ print_vattr(attr);
+
+ coda_vattr_to_iattr(inode, attr);
+
+ if (S_ISREG(inode->i_mode)) {
+ inode->i_op = &coda_file_inode_operations;
+ inode->i_fop = &coda_file_operations;
+ } else if (S_ISDIR(inode->i_mode)) {
+ inode->i_op = &coda_dir_inode_operations;
+ inode->i_fop = &coda_dir_operations;
+ } else if (S_ISLNK(inode->i_mode)) {
+ inode->i_op = &coda_symlink_inode_operations;
+ inode->i_data.a_ops = &coda_symlink_aops;
+ inode->i_mapping = &inode->i_data;
+ } else
+ init_special_inode(inode, inode->i_mode, attr->va_rdev);
+}
+
+struct inode * coda_iget(struct super_block * sb, ViceFid * fid,
+ struct coda_vattr * attr)
+{
+ struct inode *inode;
+ struct coda_inode_info *cii;
+ ino_t ino = coda_f2i(fid);
+ struct coda_sb_info *sbi = coda_sbp(sb);
+
+ down(&sbi->sbi_iget4_mutex);
+ inode = iget4(sb, ino, coda_inocmp, fid);
+
+ if ( !inode ) {
+ CDEBUG(D_CNODE, "coda_iget: no inode\n");
+ up(&sbi->sbi_iget4_mutex);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* check if the inode is already initialized */
+ cii = ITOC(inode);
+ if (coda_isnullfid(&cii->c_fid))
+ /* new, empty inode found... initializing */
+ cii->c_fid = *fid;
+ up(&sbi->sbi_iget4_mutex);
+
+ /* always replace the attributes, type might have changed */
+ coda_fill_inode(inode, attr);
+ return inode;
+}
+
+/* this is effectively coda_iget:
+ - get attributes (might be cached)
+ - get the inode for the fid using vfs iget
+ - link the two up if this is needed
+ - fill in the attributes
+*/
+int coda_cnode_make(struct inode **inode, ViceFid *fid, struct super_block *sb)
+{
+ struct coda_vattr attr;
+ int error;
+
+ /* We get inode numbers from Venus -- see venus source */
+ error = venus_getattr(sb, fid, &attr);
+ if ( error ) {
+ CDEBUG(D_CNODE,
+ "coda_cnode_make: coda_getvattr returned %d for %s.\n",
+ error, coda_f2s(fid));
+ *inode = NULL;
+ return error;
+ }
+
+ *inode = coda_iget(sb, fid, &attr);
+ if ( IS_ERR(*inode) ) {
+ printk("coda_cnode_make: coda_iget failed\n");
+ return PTR_ERR(*inode);
+ }
+
+ CDEBUG(D_DOWNCALL, "Done making inode: ino %ld, count %d with %s\n",
+ (*inode)->i_ino, atomic_read(&(*inode)->i_count),
+ coda_f2s(&ITOC(*inode)->c_fid));
+ return 0;
+}
+
+
+void coda_replace_fid(struct inode *inode, struct ViceFid *oldfid,
+ struct ViceFid *newfid)
+{
+ struct coda_inode_info *cii;
+
+ cii = ITOC(inode);
+
+ if (!coda_fideq(&cii->c_fid, oldfid))
+ BUG();
+
+ /* replace fid and rehash inode */
+ /* XXX we probably need to hold some lock here! */
+ remove_inode_hash(inode);
+ cii->c_fid = *newfid;
+ inode->i_ino = coda_f2i(newfid);
+ insert_inode_hash(inode);
+}
+
+/* convert a fid to an inode. */
+struct inode *coda_fid_to_inode(ViceFid *fid, struct super_block *sb)
+{
+ ino_t nr;
+ struct inode *inode;
+ struct coda_inode_info *cii;
+ struct coda_sb_info *sbi;
+
+ if ( !sb ) {
+ printk("coda_fid_to_inode: no sb!\n");
+ return NULL;
+ }
+
+ CDEBUG(D_INODE, "%s\n", coda_f2s(fid));
+
+ sbi = coda_sbp(sb);
+ nr = coda_f2i(fid);
+ down(&sbi->sbi_iget4_mutex);
+ inode = iget4(sb, nr, coda_inocmp, fid);
+ if ( !inode ) {
+ printk("coda_fid_to_inode: null from iget, sb %p, nr %ld.\n",
+ sb, (long)nr);
+ goto out_unlock;
+ }
+
+ cii = ITOC(inode);
+
+ /* The inode could already be purged due to memory pressure */
+ if (coda_isnullfid(&cii->c_fid)) {
+ inode->i_nlink = 0;
+ iput(inode);
+ goto out_unlock;
+ }
+
+ CDEBUG(D_INODE, "found %ld\n", inode->i_ino);
+ up(&sbi->sbi_iget4_mutex);
+ return inode;
+
+out_unlock:
+ up(&sbi->sbi_iget4_mutex);
+ return NULL;
+}
+
+/* the CONTROL inode is made without asking attributes from Venus */
+int coda_cnode_makectl(struct inode **inode, struct super_block *sb)
+{
+ int error = 0;
+
+ *inode = iget(sb, CTL_INO);
+ if ( *inode ) {
+ (*inode)->i_op = &coda_ioctl_inode_operations;
+ (*inode)->i_fop = &coda_ioctl_operations;
+ (*inode)->i_mode = 0444;
+ error = 0;
+ } else {
+ error = -ENOMEM;
+ }
+
+ return error;
+}
+
diff --git a/fs/coda/coda_linux.c b/fs/coda/coda_linux.c
new file mode 100644
index 00000000000000..c9b34577d1c1fd
--- /dev/null
+++ b/fs/coda/coda_linux.c
@@ -0,0 +1,291 @@
+/*
+ * Inode operations for Coda filesystem
+ * Original version: (C) 1996 P. Braam and M. Callahan
+ * Rewritten for Linux 2.1. (C) 1997 Carnegie Mellon University
+ *
+ * Carnegie Mellon encourages users to contribute improvements to
+ * the Coda project. Contact Peter Braam (coda@cs.cmu.edu).
+ */
+
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <asm/uaccess.h>
+#include <linux/string.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_psdev.h>
+#include <linux/coda_fs_i.h>
+
+/* initialize the debugging variables */
+int coda_debug;
+int coda_access_cache = 1;
+int coda_fake_statfs;
+
+/* print a fid */
+char * coda_f2s(ViceFid *f)
+{
+ static char s[60];
+ sprintf(s, "(%-#lx.%-#lx.%-#lx)", f->Volume, f->Vnode, f->Unique);
+ return s;
+}
+
+/* recognize special .CONTROL name */
+int coda_iscontrol(const char *name, size_t length)
+{
+ return ((CODA_CONTROLLEN == length) &&
+ (strncmp(name, CODA_CONTROL, CODA_CONTROLLEN) == 0));
+}
+
+/* recognize /coda inode */
+int coda_isroot(struct inode *i)
+{
+ return ( i->i_sb->s_root->d_inode == i );
+}
+
+/* put the current process credentials in the cred */
+void coda_load_creds(struct coda_cred *cred)
+{
+ cred->cr_uid = (vuid_t) current->uid;
+ cred->cr_euid = (vuid_t) current->euid;
+ cred->cr_suid = (vuid_t) current->suid;
+ cred->cr_fsuid = (vuid_t) current->fsuid;
+
+ cred->cr_groupid = (vgid_t) current->gid;
+ cred->cr_egid = (vgid_t) current->egid;
+ cred->cr_sgid = (vgid_t) current->sgid;
+ cred->cr_fsgid = (vgid_t) current->fsgid;
+}
+
+int coda_cred_ok(struct coda_cred *cred)
+{
+ return(current->fsuid == cred->cr_fsuid);
+}
+
+int coda_cred_eq(struct coda_cred *cred1, struct coda_cred *cred2)
+{
+ return (cred1->cr_fsuid == cred2->cr_fsuid);
+}
+
+unsigned short coda_flags_to_cflags(unsigned short flags)
+{
+ unsigned short coda_flags = 0;
+
+ if ( (flags & O_ACCMODE) == O_RDONLY ){
+ CDEBUG(D_FILE, "--> C_O_READ added\n");
+ coda_flags |= C_O_READ;
+ }
+
+ if ( (flags & O_ACCMODE) == O_RDWR ) {
+ CDEBUG(D_FILE, "--> C_O_READ | C_O_WRITE added\n");
+ coda_flags |= C_O_READ | C_O_WRITE;
+ }
+
+ if ( (flags & O_ACCMODE) == O_WRONLY ){
+ CDEBUG(D_FILE, "--> C_O_WRITE added\n");
+ coda_flags |= C_O_WRITE;
+ }
+
+ if ( flags & O_TRUNC ) {
+ CDEBUG(D_FILE, "--> C_O_TRUNC added\n");
+ coda_flags |= C_O_TRUNC;
+ }
+
+ if ( flags & O_CREAT ) {
+ CDEBUG(D_FILE, "--> C_O_CREAT added\n");
+ coda_flags |= C_O_CREAT;
+ }
+
+ if ( flags & O_EXCL ) {
+ coda_flags |= C_O_EXCL;
+ CDEBUG(D_FILE, "--> C_O_EXCL added\n");
+ }
+
+ return coda_flags;
+}
+
+
+/* utility functions below */
+void coda_vattr_to_iattr(struct inode *inode, struct coda_vattr *attr)
+{
+ int inode_type;
+ /* inode's i_dev, i_flags, i_ino are set by iget
+ XXX: is this all we need ??
+ */
+ switch (attr->va_type) {
+ case C_VNON:
+ inode_type = 0;
+ break;
+ case C_VREG:
+ inode_type = S_IFREG;
+ break;
+ case C_VDIR:
+ inode_type = S_IFDIR;
+ break;
+ case C_VLNK:
+ inode_type = S_IFLNK;
+ break;
+ default:
+ inode_type = 0;
+ }
+ inode->i_mode |= inode_type;
+
+ if (attr->va_mode != (u_short) -1)
+ inode->i_mode = attr->va_mode | inode_type;
+ if (attr->va_uid != -1)
+ inode->i_uid = (uid_t) attr->va_uid;
+ if (attr->va_gid != -1)
+ inode->i_gid = (gid_t) attr->va_gid;
+ if (attr->va_nlink != -1)
+ inode->i_nlink = attr->va_nlink;
+ if (attr->va_size != -1)
+ inode->i_size = attr->va_size;
+ if (attr->va_blocksize != -1)
+ inode->i_blksize = attr->va_blocksize;
+ if (attr->va_size != -1)
+ inode->i_blocks = (attr->va_size + 511) >> 9;
+ if (attr->va_atime.tv_sec != -1)
+ inode->i_atime = attr->va_atime.tv_sec;
+ if (attr->va_mtime.tv_sec != -1)
+ inode->i_mtime = attr->va_mtime.tv_sec;
+ if (attr->va_ctime.tv_sec != -1)
+ inode->i_ctime = attr->va_ctime.tv_sec;
+}
+
+
+/*
+ * BSD sets attributes that need not be modified to -1.
+ * Linux uses the valid field to indicate what should be
+ * looked at. The BSD type field needs to be deduced from linux
+ * mode.
+ * So we have to do some translations here.
+ */
+
+void coda_iattr_to_vattr(struct iattr *iattr, struct coda_vattr *vattr)
+{
+ unsigned int valid;
+
+ /* clean out */
+ vattr->va_mode = (umode_t) -1;
+ vattr->va_uid = (vuid_t) -1;
+ vattr->va_gid = (vgid_t) -1;
+ vattr->va_size = (off_t) -1;
+ vattr->va_atime.tv_sec = (time_t) -1;
+ vattr->va_mtime.tv_sec = (time_t) -1;
+ vattr->va_ctime.tv_sec = (time_t) -1;
+ vattr->va_atime.tv_nsec = (time_t) -1;
+ vattr->va_mtime.tv_nsec = (time_t) -1;
+ vattr->va_ctime.tv_nsec = (time_t) -1;
+ vattr->va_type = C_VNON;
+ vattr->va_fileid = -1;
+ vattr->va_gen = -1;
+ vattr->va_bytes = -1;
+ vattr->va_nlink = -1;
+ vattr->va_blocksize = -1;
+ vattr->va_rdev = -1;
+ vattr->va_flags = 0;
+
+ /* determine the type */
+#if 0
+ mode = iattr->ia_mode;
+ if ( S_ISDIR(mode) ) {
+ vattr->va_type = C_VDIR;
+ } else if ( S_ISREG(mode) ) {
+ vattr->va_type = C_VREG;
+ } else if ( S_ISLNK(mode) ) {
+ vattr->va_type = C_VLNK;
+ } else {
+ /* don't do others */
+ vattr->va_type = C_VNON;
+ }
+#endif
+
+ /* set those vattrs that need change */
+ valid = iattr->ia_valid;
+ if ( valid & ATTR_MODE ) {
+ vattr->va_mode = iattr->ia_mode;
+ }
+ if ( valid & ATTR_UID ) {
+ vattr->va_uid = (vuid_t) iattr->ia_uid;
+ }
+ if ( valid & ATTR_GID ) {
+ vattr->va_gid = (vgid_t) iattr->ia_gid;
+ }
+ if ( valid & ATTR_SIZE ) {
+ vattr->va_size = iattr->ia_size;
+ }
+ if ( valid & ATTR_ATIME ) {
+ vattr->va_atime.tv_sec = iattr->ia_atime;
+ vattr->va_atime.tv_nsec = 0;
+ }
+ if ( valid & ATTR_MTIME ) {
+ vattr->va_mtime.tv_sec = iattr->ia_mtime;
+ vattr->va_mtime.tv_nsec = 0;
+ }
+ if ( valid & ATTR_CTIME ) {
+ vattr->va_ctime.tv_sec = iattr->ia_ctime;
+ vattr->va_ctime.tv_nsec = 0;
+ }
+}
+
+void print_vattr(struct coda_vattr *attr)
+{
+ char *typestr;
+
+ switch (attr->va_type) {
+ case C_VNON:
+ typestr = "C_VNON";
+ break;
+ case C_VREG:
+ typestr = "C_VREG";
+ break;
+ case C_VDIR:
+ typestr = "C_VDIR";
+ break;
+ case C_VBLK:
+ typestr = "C_VBLK";
+ break;
+ case C_VCHR:
+ typestr = "C_VCHR";
+ break;
+ case C_VLNK:
+ typestr = "C_VLNK";
+ break;
+ case C_VSOCK:
+ typestr = "C_VSCK";
+ break;
+ case C_VFIFO:
+ typestr = "C_VFFO";
+ break;
+ case C_VBAD:
+ typestr = "C_VBAD";
+ break;
+ default:
+ typestr = "????";
+ break;
+ }
+
+
+ printk("attr: type %s (%o) mode %o uid %d gid %d rdev %d\n",
+ typestr, (int)attr->va_type, (int)attr->va_mode,
+ (int)attr->va_uid, (int)attr->va_gid, (int)attr->va_rdev);
+
+ printk(" fileid %d nlink %d size %d blocksize %d bytes %d\n",
+ (int)attr->va_fileid, (int)attr->va_nlink,
+ (int)attr->va_size,
+ (int)attr->va_blocksize,(int)attr->va_bytes);
+ printk(" gen %ld flags %ld\n",
+ attr->va_gen, attr->va_flags);
+ printk(" atime sec %d nsec %d\n",
+ (int)attr->va_atime.tv_sec, (int)attr->va_atime.tv_nsec);
+ printk(" mtime sec %d nsec %d\n",
+ (int)attr->va_mtime.tv_sec, (int)attr->va_mtime.tv_nsec);
+ printk(" ctime sec %d nsec %d\n",
+ (int)attr->va_ctime.tv_sec, (int)attr->va_ctime.tv_nsec);
+}
diff --git a/fs/coda/dir.c b/fs/coda/dir.c
new file mode 100644
index 00000000000000..86bf619333c5f5
--- /dev/null
+++ b/fs/coda/dir.c
@@ -0,0 +1,757 @@
+
+/*
+ * Directory operations for Coda filesystem
+ * Original version: (C) 1996 P. Braam and M. Callahan
+ * Rewritten for Linux 2.1. (C) 1997 Carnegie Mellon University
+ *
+ * Carnegie Mellon encourages users to contribute improvements to
+ * the Coda project. Contact Peter Braam (coda@cs.cmu.edu).
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <linux/string.h>
+#include <linux/smp_lock.h>
+
+#include <asm/uaccess.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_psdev.h>
+#include <linux/coda_fs_i.h>
+#include <linux/coda_cache.h>
+#include <linux/coda_proc.h>
+
+/* dir inode-ops */
+static int coda_create(struct inode *dir, struct dentry *new, int mode);
+static int coda_mknod(struct inode *dir, struct dentry *new, int mode, int rdev);
+static struct dentry *coda_lookup(struct inode *dir, struct dentry *target);
+static int coda_link(struct dentry *old_dentry, struct inode *dir_inode,
+ struct dentry *entry);
+static int coda_unlink(struct inode *dir_inode, struct dentry *entry);
+static int coda_symlink(struct inode *dir_inode, struct dentry *entry,
+ const char *symname);
+static int coda_mkdir(struct inode *dir_inode, struct dentry *entry, int mode);
+static int coda_rmdir(struct inode *dir_inode, struct dentry *entry);
+static int coda_rename(struct inode *old_inode, struct dentry *old_dentry,
+ struct inode *new_inode, struct dentry *new_dentry);
+
+/* dir file-ops */
+static int coda_readdir(struct file *file, void *dirent, filldir_t filldir);
+
+/* dentry ops */
+static int coda_dentry_revalidate(struct dentry *de, int);
+static int coda_dentry_delete(struct dentry *);
+
+/* support routines */
+static int coda_venus_readdir(struct file *filp, filldir_t filldir,
+ void *dirent, struct dentry *dir);
+int coda_fsync(struct file *, struct dentry *dentry, int datasync);
+
+int coda_hasmknod;
+
+struct dentry_operations coda_dentry_operations =
+{
+ d_revalidate: coda_dentry_revalidate,
+ d_delete: coda_dentry_delete,
+};
+
+struct inode_operations coda_dir_inode_operations =
+{
+ create: coda_create,
+ lookup: coda_lookup,
+ link: coda_link,
+ unlink: coda_unlink,
+ symlink: coda_symlink,
+ mkdir: coda_mkdir,
+ rmdir: coda_rmdir,
+ mknod: coda_mknod,
+ rename: coda_rename,
+ permission: coda_permission,
+ revalidate: coda_revalidate_inode,
+ setattr: coda_notify_change,
+};
+
+struct file_operations coda_dir_operations = {
+ llseek: generic_file_llseek,
+ read: generic_read_dir,
+ readdir: coda_readdir,
+ open: coda_open,
+ flush: coda_flush,
+ release: coda_release,
+ fsync: coda_fsync,
+};
+
+
+/* inode operations for directories */
+/* access routines: lookup, readlink, permission */
+static struct dentry *coda_lookup(struct inode *dir, struct dentry *entry)
+{
+ struct inode *res_inode = NULL;
+ struct ViceFid resfid = {0,0,0};
+ int dropme = 0; /* to indicate entry should not be cached */
+ int type = 0;
+ int error = 0;
+ const char *name = entry->d_name.name;
+ size_t length = entry->d_name.len;
+
+ if ( length > CODA_MAXNAMLEN ) {
+ printk("name too long: lookup, %s (%*s)\n",
+ coda_i2s(dir), (int)length, name);
+ return ERR_PTR(-ENAMETOOLONG);
+ }
+
+ CDEBUG(D_INODE, "name %s, len %ld in ino %ld, fid %s\n",
+ name, (long)length, dir->i_ino, coda_i2s(dir));
+
+ /* control object, create inode on the fly */
+ if (coda_isroot(dir) && coda_iscontrol(name, length)) {
+ error = coda_cnode_makectl(&res_inode, dir->i_sb);
+ CDEBUG(D_SPECIAL,
+ "Lookup on CTL object; dir ino %ld, count %d\n",
+ dir->i_ino, atomic_read(&dir->i_count));
+ dropme = 1;
+ goto exit;
+ }
+
+ error = venus_lookup(dir->i_sb, coda_i2f(dir),
+ (const char *)name, length, &type, &resfid);
+
+ res_inode = NULL;
+ if (!error) {
+ if (type & CODA_NOCACHE) {
+ type &= (~CODA_NOCACHE);
+ CDEBUG(D_INODE, "dropme set for %s\n",
+ coda_f2s(&resfid));
+ dropme = 1;
+ }
+
+ error = coda_cnode_make(&res_inode, &resfid, dir->i_sb);
+ if (error) return ERR_PTR(error);
+ } else if (error != -ENOENT) {
+ CDEBUG(D_INODE, "error for %s(%*s)%d\n",
+ coda_i2s(dir), (int)length, name, error);
+ return ERR_PTR(error);
+ }
+ CDEBUG(D_INODE, "lookup: %s is (%s), type %d result %d, dropme %d\n",
+ name, coda_f2s(&resfid), type, error, dropme);
+
+exit:
+ entry->d_time = 0;
+ entry->d_op = &coda_dentry_operations;
+ d_add(entry, res_inode);
+ if ( dropme ) {
+ d_drop(entry);
+ coda_flag_inode(res_inode, C_VATTR);
+ }
+ return NULL;
+}
+
+
+int coda_permission(struct inode *inode, int mask)
+{
+ int error;
+
+ coda_vfs_stat.permission++;
+
+ if ( mask == 0 )
+ return 0;
+
+ if ( coda_access_cache ) {
+ coda_permission_stat.count++;
+
+ if ( coda_cache_check(inode, mask) ) {
+ coda_permission_stat.hit_count++;
+ return 0;
+ }
+ }
+
+ CDEBUG(D_INODE, "mask is %o\n", mask);
+ error = venus_access(inode->i_sb, coda_i2f(inode), mask);
+
+ CDEBUG(D_INODE, "fid: %s, ino: %ld (mask: %o) error: %d\n",
+ coda_i2s(inode), inode->i_ino, mask, error);
+
+ if (!error)
+ coda_cache_enter(inode, mask);
+
+ return error;
+}
+
+
+static inline void coda_dir_changed(struct inode *dir, int link)
+{
+#ifdef REQUERY_VENUS_FOR_MTIME
+ /* invalidate the directory cnode's attributes so we refetch the
+ * attributes from venus next time the inode is referenced */
+ coda_flag_inode(dir, C_VATTR);
+#else
+ /* optimistically we can also act as if our nose bleeds. The
+ * granularity of the mtime is coarse anyways so we might actually be
+ * right most of the time. Note: we only do this for directories. */
+ dir->i_mtime = dir->i_ctime = CURRENT_TIME;
+#endif
+ if (link)
+ dir->i_nlink += link;
+}
+
+/* creation routines: create, mknod, mkdir, link, symlink */
+static int coda_create(struct inode *dir, struct dentry *de, int mode)
+{
+ int error=0;
+ const char *name=de->d_name.name;
+ int length=de->d_name.len;
+ struct inode *inode;
+ struct ViceFid newfid;
+ struct coda_vattr attrs;
+
+ coda_vfs_stat.create++;
+
+ CDEBUG(D_INODE, "name: %s, length %d, mode %o\n", name, length, mode);
+
+ if (coda_isroot(dir) && coda_iscontrol(name, length))
+ return -EPERM;
+
+ error = venus_create(dir->i_sb, coda_i2f(dir), name, length,
+ 0, mode, 0, &newfid, &attrs);
+
+ if ( error ) {
+ CDEBUG(D_INODE, "create: %s, result %d\n",
+ coda_f2s(&newfid), error);
+ d_drop(de);
+ return error;
+ }
+
+ inode = coda_iget(dir->i_sb, &newfid, &attrs);
+ if ( IS_ERR(inode) ) {
+ d_drop(de);
+ return PTR_ERR(inode);
+ }
+
+ /* invalidate the directory cnode's attributes */
+ coda_dir_changed(dir, 0);
+ d_instantiate(de, inode);
+ return 0;
+}
+
+static int coda_mknod(struct inode *dir, struct dentry *de, int mode, int rdev)
+{
+ int error=0;
+ const char *name=de->d_name.name;
+ int length=de->d_name.len;
+ struct inode *inode;
+ struct ViceFid newfid;
+ struct coda_vattr attrs;
+
+ if ( coda_hasmknod == 0 )
+ return -EIO;
+
+ coda_vfs_stat.create++;
+
+ CDEBUG(D_INODE, "name: %s, length %d, mode %o, rdev %x\n",
+ name, length, mode, rdev);
+
+ if (coda_isroot(dir) && coda_iscontrol(name, length))
+ return -EPERM;
+
+ error = venus_create(dir->i_sb, coda_i2f(dir), name, length,
+ 0, mode, rdev, &newfid, &attrs);
+
+ if ( error ) {
+ CDEBUG(D_INODE, "mknod: %s, result %d\n",
+ coda_f2s(&newfid), error);
+ d_drop(de);
+ return error;
+ }
+
+ inode = coda_iget(dir->i_sb, &newfid, &attrs);
+ if ( IS_ERR(inode) ) {
+ d_drop(de);
+ return PTR_ERR(inode);
+ }
+
+ /* invalidate the directory cnode's attributes */
+ coda_dir_changed(dir, 0);
+ d_instantiate(de, inode);
+ return 0;
+}
+
+static int coda_mkdir(struct inode *dir, struct dentry *de, int mode)
+{
+ struct inode *inode;
+ struct coda_vattr attrs;
+ const char *name = de->d_name.name;
+ int len = de->d_name.len;
+ int error;
+ struct ViceFid newfid;
+
+ coda_vfs_stat.mkdir++;
+
+ if (coda_isroot(dir) && coda_iscontrol(name, len))
+ return -EPERM;
+
+ CDEBUG(D_INODE, "mkdir %s (len %d) in %s, mode %o.\n",
+ name, len, coda_i2s(dir), mode);
+
+ attrs.va_mode = mode;
+ error = venus_mkdir(dir->i_sb, coda_i2f(dir),
+ name, len, &newfid, &attrs);
+
+ if ( error ) {
+ CDEBUG(D_INODE, "mkdir error: %s result %d\n",
+ coda_f2s(&newfid), error);
+ d_drop(de);
+ return error;
+ }
+
+ CDEBUG(D_INODE, "mkdir: new dir has fid %s.\n",
+ coda_f2s(&newfid));
+
+ inode = coda_iget(dir->i_sb, &newfid, &attrs);
+ if ( IS_ERR(inode) ) {
+ d_drop(de);
+ return PTR_ERR(inode);
+ }
+
+ /* invalidate the directory cnode's attributes */
+ coda_dir_changed(dir, 1);
+ d_instantiate(de, inode);
+ return 0;
+}
+
+/* try to make de an entry in dir_inodde linked to source_de */
+static int coda_link(struct dentry *source_de, struct inode *dir_inode,
+ struct dentry *de)
+{
+ struct inode *inode = source_de->d_inode;
+ const char * name = de->d_name.name;
+ int len = de->d_name.len;
+ int error;
+
+ coda_vfs_stat.link++;
+
+ if (coda_isroot(dir_inode) && coda_iscontrol(name, len))
+ return -EPERM;
+
+ CDEBUG(D_INODE, "old: fid: %s\n", coda_i2s(inode));
+ CDEBUG(D_INODE, "directory: %s\n", coda_i2s(dir_inode));
+
+ error = venus_link(dir_inode->i_sb, coda_i2f(inode),
+ coda_i2f(dir_inode), (const char *)name, len);
+
+ if (error) {
+ d_drop(de);
+ goto out;
+ }
+
+ coda_dir_changed(dir_inode, 0);
+ atomic_inc(&inode->i_count);
+ d_instantiate(de, inode);
+ inode->i_nlink++;
+
+out:
+ CDEBUG(D_INODE, "link result %d\n",error);
+ return(error);
+}
+
+
+static int coda_symlink(struct inode *dir_inode, struct dentry *de,
+ const char *symname)
+{
+ const char *name = de->d_name.name;
+ int len = de->d_name.len;
+ int symlen;
+ int error=0;
+
+ coda_vfs_stat.symlink++;
+
+ if (coda_isroot(dir_inode) && coda_iscontrol(name, len))
+ return -EPERM;
+
+ symlen = strlen(symname);
+ if ( symlen > CODA_MAXPATHLEN )
+ return -ENAMETOOLONG;
+
+ CDEBUG(D_INODE, "symname: %s, length: %d\n", symname, symlen);
+
+ /*
+ * This entry is now negative. Since we do not create
+ * an inode for the entry we have to drop it.
+ */
+ d_drop(de);
+ error = venus_symlink(dir_inode->i_sb, coda_i2f(dir_inode), name, len,
+ symname, symlen);
+
+ /* mtime is no good anymore */
+ if ( !error )
+ coda_dir_changed(dir_inode, 0);
+
+ CDEBUG(D_INODE, "in symlink result %d\n",error);
+ return error;
+}
+
+/* destruction routines: unlink, rmdir */
+int coda_unlink(struct inode *dir, struct dentry *de)
+{
+ int error;
+ const char *name = de->d_name.name;
+ int len = de->d_name.len;
+
+ coda_vfs_stat.unlink++;
+
+ CDEBUG(D_INODE, " %s in %s, dirino %ld\n", name ,
+ coda_i2s(dir), dir->i_ino);
+
+ error = venus_remove(dir->i_sb, coda_i2f(dir), name, len);
+ if ( error ) {
+ CDEBUG(D_INODE, "upc returned error %d\n", error);
+ return error;
+ }
+
+ coda_dir_changed(dir, 0);
+ de->d_inode->i_nlink--;
+
+ return 0;
+}
+
+int coda_rmdir(struct inode *dir, struct dentry *de)
+{
+ const char *name = de->d_name.name;
+ int len = de->d_name.len;
+ int error;
+
+ coda_vfs_stat.rmdir++;
+
+ if (!d_unhashed(de))
+ return -EBUSY;
+ error = venus_rmdir(dir->i_sb, coda_i2f(dir), name, len);
+
+ if ( error ) {
+ CDEBUG(D_INODE, "upc returned error %d\n", error);
+ return error;
+ }
+
+ coda_dir_changed(dir, -1);
+ de->d_inode->i_nlink--;
+ d_delete(de);
+
+ return 0;
+}
+
+/* rename */
+static int coda_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ const char *old_name = old_dentry->d_name.name;
+ const char *new_name = new_dentry->d_name.name;
+ int old_length = old_dentry->d_name.len;
+ int new_length = new_dentry->d_name.len;
+ int link_adjust = 0;
+ int error;
+
+ coda_vfs_stat.rename++;
+
+ CDEBUG(D_INODE, "old: %s, (%d length), new: %s"
+ "(%d length). old:d_count: %d, new:d_count: %d\n",
+ old_name, old_length, new_name, new_length,
+ atomic_read(&old_dentry->d_count), atomic_read(&new_dentry->d_count));
+
+ error = venus_rename(old_dir->i_sb, coda_i2f(old_dir),
+ coda_i2f(new_dir), old_length, new_length,
+ (const char *) old_name, (const char *)new_name);
+
+ if ( !error ) {
+ if ( new_dentry->d_inode ) {
+ if ( S_ISDIR(new_dentry->d_inode->i_mode) )
+ link_adjust = 1;
+
+ coda_dir_changed(old_dir, -link_adjust);
+ coda_dir_changed(new_dir, link_adjust);
+ coda_flag_inode(new_dentry->d_inode, C_VATTR);
+ } else {
+ coda_flag_inode(old_dir, C_VATTR);
+ coda_flag_inode(new_dir, C_VATTR);
+ }
+ }
+
+ CDEBUG(D_INODE, "result %d\n", error);
+
+ return error;
+}
+
+
+/* file operations for directories */
+int coda_readdir(struct file *coda_file, void *dirent, filldir_t filldir)
+{
+ struct dentry *coda_dentry = coda_file->f_dentry;
+ struct coda_file_info *cfi;
+ struct file *host_file;
+ int ret;
+
+ cfi = CODA_FTOC(coda_file);
+ if (!cfi || cfi->cfi_magic != CODA_MAGIC) BUG();
+ host_file = cfi->cfi_container;
+
+ coda_vfs_stat.readdir++;
+
+ down(&host_file->f_dentry->d_inode->i_sem);
+ host_file->f_pos = coda_file->f_pos;
+
+ if ( !host_file->f_op->readdir ) {
+ /* Venus: we must read Venus dirents from the file */
+ ret = coda_venus_readdir(host_file, filldir, dirent, coda_dentry);
+ } else {
+ /* potemkin case: we were handed a directory inode */
+ /* We can't call vfs_readdir because we are already holding
+ * the inode semaphore. */
+ ret = -ENOENT;
+ if (!IS_DEADDIR(host_file->f_dentry->d_inode))
+ ret = host_file->f_op->readdir(host_file, filldir, dirent);
+ }
+
+ coda_file->f_pos = host_file->f_pos;
+ up(&host_file->f_dentry->d_inode->i_sem);
+
+ return ret;
+}
+
+static inline unsigned int CDT2DT(unsigned char cdt)
+{
+ unsigned int dt;
+
+ switch(cdt) {
+ case CDT_UNKNOWN: dt = DT_UNKNOWN; break;
+ case CDT_FIFO: dt = DT_FIFO; break;
+ case CDT_CHR: dt = DT_CHR; break;
+ case CDT_DIR: dt = DT_DIR; break;
+ case CDT_BLK: dt = DT_BLK; break;
+ case CDT_REG: dt = DT_REG; break;
+ case CDT_LNK: dt = DT_LNK; break;
+ case CDT_SOCK: dt = DT_SOCK; break;
+ case CDT_WHT: dt = DT_WHT; break;
+ default: dt = DT_UNKNOWN; break;
+ }
+ return dt;
+}
+
+/* support routines */
+static int coda_venus_readdir(struct file *filp, filldir_t filldir,
+ void *dirent, struct dentry *dir)
+{
+ int result = 0; /* # of entries returned */
+ struct venus_dirent *vdir;
+ unsigned long vdir_size =
+ (unsigned long)(&((struct venus_dirent *)0)->d_name);
+ unsigned int type;
+ struct qstr name;
+ ino_t ino;
+ int ret, i;
+
+ vdir = (struct venus_dirent *)kmalloc(sizeof(*vdir), GFP_KERNEL);
+ if (!vdir) return -ENOMEM;
+
+ i = filp->f_pos;
+ switch(i) {
+ case 0:
+ ret = filldir(dirent, ".", 1, 0, dir->d_inode->i_ino, DT_DIR);
+ if (ret < 0) break;
+ result++;
+ filp->f_pos++;
+ /* fallthrough */
+ case 1:
+ ret = filldir(dirent, "..", 2, 1, dir->d_parent->d_inode->i_ino, DT_DIR);
+ if (ret < 0) break;
+ result++;
+ filp->f_pos++;
+ /* fallthrough */
+ default:
+ while (1) {
+ /* read entries from the directory file */
+ ret = kernel_read(filp, filp->f_pos - 2, (char *)vdir,
+ sizeof(*vdir));
+ if (ret < 0) {
+ printk("coda_venus_readdir: read dir failed %d\n", ret);
+ break;
+ }
+ if (ret == 0) break; /* end of directory file reached */
+
+ /* catch truncated reads */
+ if (ret < vdir_size || ret < vdir_size + vdir->d_namlen) {
+ printk("coda_venus_readdir: short read: %ld\n",
+ filp->f_dentry->d_inode->i_ino);
+ ret = -EBADF;
+ break;
+ }
+ /* validate whether the directory file actually makes sense */
+ if (vdir->d_reclen < vdir_size + vdir->d_namlen ||
+ vdir->d_namlen > CODA_MAXNAMLEN) {
+ printk("coda_venus_readdir: Invalid dir: %ld\n",
+ filp->f_dentry->d_inode->i_ino);
+ ret = -EBADF;
+ break;
+ }
+
+ name.len = vdir->d_namlen;
+ name.name = vdir->d_name;
+
+ /* Make sure we skip '.' and '..', we already got those */
+ if (name.name[0] == '.' && (name.len == 1 ||
+ (vdir->d_name[1] == '.' && name.len == 2)))
+ vdir->d_fileno = name.len = 0;
+
+ /* skip null entries */
+ if (vdir->d_fileno && name.len) {
+ /* try to look up this entry in the dcache, that way
+ * userspace doesn't have to worry about breaking
+ * getcwd by having mismatched inode numbers for
+ * internal volume mountpoints. */
+ ino = find_inode_number(dir, &name);
+ if (!ino) ino = vdir->d_fileno;
+
+ type = CDT2DT(vdir->d_type);
+ ret = filldir(dirent, name.name, name.len, filp->f_pos,
+ ino, type);
+ /* failure means no space for filling in this round */
+ if (ret < 0) break;
+ result++;
+ }
+ /* we'll always have progress because d_reclen is unsigned and
+ * we've already established it is non-zero. */
+ filp->f_pos += vdir->d_reclen;
+ }
+ }
+ kfree(vdir);
+ return result ? result : ret;
+}
+
+/* called when a cache lookup succeeds */
+static int coda_dentry_revalidate(struct dentry *de, int flags)
+{
+ struct inode *inode = de->d_inode;
+ struct coda_inode_info *cii;
+
+ if (!inode)
+ return 1;
+ lock_kernel();
+ if (coda_isroot(inode))
+ goto out;
+ if (is_bad_inode(inode))
+ goto bad;
+
+ cii = ITOC(de->d_inode);
+ if (!(cii->c_flags & (C_PURGE | C_FLUSH)))
+ goto out;
+
+ shrink_dcache_parent(de);
+
+ /* propagate for a flush */
+ if (cii->c_flags & C_FLUSH)
+ coda_flag_inode_children(inode, C_FLUSH);
+
+ if (atomic_read(&de->d_count) > 1) {
+ /* pretend it's valid, but don't change the flags */
+ CDEBUG(D_DOWNCALL, "BOOM for: ino %ld, %s\n",
+ de->d_inode->i_ino, coda_f2s(&cii->c_fid));
+ goto out;
+ }
+
+ /* clear the flags. */
+ cii->c_flags &= ~(C_VATTR | C_PURGE | C_FLUSH);
+
+bad:
+ unlock_kernel();
+ return 0;
+out:
+ unlock_kernel();
+ return 1;
+}
+
+/*
+ * This is the callback from dput() when d_count is going to 0.
+ * We use this to unhash dentries with bad inodes.
+ */
+static int coda_dentry_delete(struct dentry * dentry)
+{
+ int flags;
+
+ if (!dentry->d_inode)
+ return 0;
+
+ flags = (ITOC(dentry->d_inode)->c_flags) & C_PURGE;
+ if (is_bad_inode(dentry->d_inode) || flags) {
+ CDEBUG(D_DOWNCALL, "bad inode, unhashing %s/%s, %ld\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ dentry->d_inode->i_ino);
+ return 1;
+ }
+ return 0;
+}
+
+
+
+/*
+ * This is called when we want to check if the inode has
+ * changed on the server. Coda makes this easy since the
+ * cache manager Venus issues a downcall to the kernel when this
+ * happens
+ */
+int coda_revalidate_inode(struct dentry *dentry)
+{
+ struct coda_vattr attr;
+ int error = 0;
+ int old_mode;
+ ino_t old_ino;
+ struct inode *inode = dentry->d_inode;
+ struct coda_inode_info *cii = ITOC(inode);
+
+ CDEBUG(D_INODE, "revalidating: %*s/%*s\n",
+ dentry->d_name.len, dentry->d_name.name,
+ dentry->d_parent->d_name.len, dentry->d_parent->d_name.name);
+
+ lock_kernel();
+ if ( !cii->c_flags )
+ goto ok;
+
+ if (cii->c_flags & (C_VATTR | C_PURGE | C_FLUSH)) {
+ error = venus_getattr(inode->i_sb, &(cii->c_fid), &attr);
+ if ( error )
+ goto return_bad_inode;
+
+ /* this inode may be lost if:
+ - it's ino changed
+ - type changes must be permitted for repair and
+ missing mount points.
+ */
+ old_mode = inode->i_mode;
+ old_ino = inode->i_ino;
+ coda_vattr_to_iattr(inode, &attr);
+
+ if ((old_mode & S_IFMT) != (inode->i_mode & S_IFMT)) {
+ printk("Coda: inode %ld, fid %s changed type!\n",
+ inode->i_ino, coda_f2s(&(cii->c_fid)));
+ }
+
+ /* the following can happen when a local fid is replaced
+ with a global one, here we lose and declare the inode bad */
+ if (inode->i_ino != old_ino)
+ goto return_bad_inode;
+
+ coda_flag_inode_children(inode, C_FLUSH);
+ cii->c_flags &= ~(C_VATTR | C_PURGE | C_FLUSH);
+ }
+
+ok:
+ unlock_kernel();
+ return 0;
+
+return_bad_inode:
+ unlock_kernel();
+ return -EIO;
+}
+
diff --git a/fs/coda/file.c b/fs/coda/file.c
new file mode 100644
index 00000000000000..164f3170b7c126
--- /dev/null
+++ b/fs/coda/file.c
@@ -0,0 +1,292 @@
+/*
+ * File operations for Coda.
+ * Original version: (C) 1996 Peter Braam
+ * Rewritten for Linux 2.1: (C) 1997 Carnegie Mellon University
+ *
+ * Carnegie Mellon encourages users of this code to contribute improvements
+ * to the Coda project. Contact Peter Braam <coda@cs.cmu.edu>.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <linux/smp_lock.h>
+#include <linux/string.h>
+#include <asm/uaccess.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_fs_i.h>
+#include <linux/coda_psdev.h>
+#include <linux/coda_proc.h>
+
+/* if CODA_STORE fails with EOPNOTSUPP, venus clearly doesn't support
+ * CODA_STORE/CODA_RELEASE and we fall back on using the CODA_CLOSE upcall */
+int use_coda_close;
+
+static ssize_t
+coda_file_read(struct file *coda_file, char *buf, size_t count, loff_t *ppos)
+{
+ struct coda_file_info *cfi;
+ struct file *host_file;
+
+ cfi = CODA_FTOC(coda_file);
+ if (!cfi || cfi->cfi_magic != CODA_MAGIC) BUG();
+ host_file = cfi->cfi_container;
+
+ if (!host_file->f_op || !host_file->f_op->read)
+ return -EINVAL;
+
+ return host_file->f_op->read(host_file, buf, count, ppos);
+}
+
+static ssize_t
+coda_file_write(struct file *coda_file, const char *buf, size_t count, loff_t *ppos)
+{
+ struct inode *coda_inode = coda_file->f_dentry->d_inode;
+ struct coda_file_info *cfi;
+ struct file *host_file;
+ ssize_t ret;
+
+ cfi = CODA_FTOC(coda_file);
+ if (!cfi || cfi->cfi_magic != CODA_MAGIC) BUG();
+ host_file = cfi->cfi_container;
+
+ if (!host_file->f_op || !host_file->f_op->write)
+ return -EINVAL;
+
+ down(&coda_inode->i_sem);
+
+ ret = host_file->f_op->write(host_file, buf, count, ppos);
+
+ coda_inode->i_size = host_file->f_dentry->d_inode->i_size;
+ coda_inode->i_blocks = (coda_inode->i_size + 511) >> 9;
+ coda_inode->i_mtime = coda_inode->i_ctime = CURRENT_TIME;
+ up(&coda_inode->i_sem);
+
+ return ret;
+}
+
+static int
+coda_file_mmap(struct file *coda_file, struct vm_area_struct *vma)
+{
+ struct coda_file_info *cfi;
+ struct coda_inode_info *cii;
+ struct file *host_file;
+ struct inode *coda_inode, *host_inode;
+
+ cfi = CODA_FTOC(coda_file);
+ if (!cfi || cfi->cfi_magic != CODA_MAGIC) BUG();
+ host_file = cfi->cfi_container;
+
+ if (!host_file->f_op || !host_file->f_op->mmap)
+ return -ENODEV;
+
+ coda_inode = coda_file->f_dentry->d_inode;
+ host_inode = host_file->f_dentry->d_inode;
+ if (coda_inode->i_mapping == &coda_inode->i_data)
+ coda_inode->i_mapping = host_inode->i_mapping;
+
+ /* only allow additional mmaps as long as userspace isn't changing
+ * the container file on us! */
+ else if (coda_inode->i_mapping != host_inode->i_mapping)
+ return -EBUSY;
+
+ /* keep track of how often the coda_inode/host_file has been mmapped */
+ cii = ITOC(coda_inode);
+ cii->c_mapcount++;
+ cfi->cfi_mapcount++;
+
+ return host_file->f_op->mmap(host_file, vma);
+}
+
+int coda_open(struct inode *coda_inode, struct file *coda_file)
+{
+ struct file *host_file = NULL;
+ int error;
+ unsigned short flags = coda_file->f_flags & (~O_EXCL);
+ unsigned short coda_flags = coda_flags_to_cflags(flags);
+ struct coda_file_info *cfi;
+ struct inode *host_inode;
+
+ coda_vfs_stat.open++;
+
+ cfi = kmalloc(sizeof(struct coda_file_info), GFP_KERNEL);
+ if (!cfi)
+ return -ENOMEM;
+
+ lock_kernel();
+
+ error = venus_open(coda_inode->i_sb, coda_i2f(coda_inode), coda_flags,
+ &host_file);
+
+ if (!error && !host_file)
+ error = EBADF;
+
+ if (error) {
+ kfree(cfi);
+ unlock_kernel();
+ return error;
+ }
+
+ host_file->f_flags |= coda_file->f_flags & (O_APPEND | O_SYNC);
+
+ cfi->cfi_magic = CODA_MAGIC;
+ cfi->cfi_mapcount = 0;
+ cfi->cfi_container = host_file;
+ coda_load_creds(&cfi->cfi_cred);
+
+ host_inode = host_file->f_dentry->d_inode;
+ if (coda_inode->i_mapping == &coda_inode->i_data)
+ coda_inode->i_mapping = host_inode->i_mapping;
+
+ else if (coda_inode->i_mapping != host_inode->i_mapping) {
+ /* This is not a good thing, it doesn't happen with 'venus'
+ * Coda's own userspace daemon, but others might not provide
+ * the same guarantees. Still looking for the real fix. */
+ printk("coda_open: changed mapping\n");
+ coda_inode->i_mapping = host_inode->i_mapping;
+ }
+
+ if (coda_file->private_data != NULL) BUG();
+ coda_file->private_data = cfi;
+
+ unlock_kernel();
+ return 0;
+}
+
+int coda_flush(struct file *coda_file)
+{
+ unsigned short flags = coda_file->f_flags & ~O_EXCL;
+ unsigned short coda_flags = coda_flags_to_cflags(flags);
+ struct coda_file_info *cfi;
+ struct inode *coda_inode;
+ int err = 0, fcnt;
+
+ coda_vfs_stat.flush++;
+
+ /* No need to make an upcall when we have not made any modifications
+ * to the file */
+ if ((coda_file->f_flags & O_ACCMODE) == O_RDONLY)
+ return 0;
+
+ if (use_coda_close)
+ return 0;
+
+ fcnt = file_count(coda_file);
+ if (fcnt > 1) return 0;
+
+ coda_inode = coda_file->f_dentry->d_inode;
+
+ cfi = CODA_FTOC(coda_file);
+ if (!cfi || cfi->cfi_magic != CODA_MAGIC) BUG();
+
+ err = venus_store(coda_inode->i_sb, coda_i2f(coda_inode), coda_flags,
+ &cfi->cfi_cred);
+
+ if (err == -EOPNOTSUPP) {
+ use_coda_close = 1;
+ err = 0;
+ }
+
+ return err;
+}
+
+int coda_release(struct inode *coda_inode, struct file *coda_file)
+{
+ unsigned short flags = (coda_file->f_flags) & (~O_EXCL);
+ unsigned short coda_flags = coda_flags_to_cflags(flags);
+ struct coda_file_info *cfi;
+ struct coda_inode_info *cii;
+ struct inode *host_inode;
+ int err = 0;
+
+ lock_kernel();
+ coda_vfs_stat.release++;
+
+ if (!use_coda_close) {
+ err = venus_release(coda_inode->i_sb, coda_i2f(coda_inode),
+ coda_flags);
+ if (err == -EOPNOTSUPP) {
+ use_coda_close = 1;
+ err = 0;
+ }
+ }
+
+ cfi = CODA_FTOC(coda_file);
+ if (!cfi || cfi->cfi_magic != CODA_MAGIC) BUG();
+
+ if (use_coda_close)
+ err = venus_close(coda_inode->i_sb, coda_i2f(coda_inode),
+ coda_flags, &cfi->cfi_cred);
+
+ host_inode = cfi->cfi_container->f_dentry->d_inode;
+ cii = ITOC(coda_inode);
+
+ /* did we mmap this file? */
+ if (coda_inode->i_mapping == &host_inode->i_data) {
+ cii->c_mapcount -= cfi->cfi_mapcount;
+ if (!cii->c_mapcount)
+ coda_inode->i_mapping = &coda_inode->i_data;
+ }
+
+ fput(cfi->cfi_container);
+
+ kfree(coda_file->private_data);
+ coda_file->private_data = NULL;
+
+ unlock_kernel();
+ return err;
+}
+
+int coda_fsync(struct file *coda_file, struct dentry *coda_dentry, int datasync)
+{
+ struct file *host_file;
+ struct dentry *host_dentry;
+ struct inode *host_inode, *coda_inode = coda_dentry->d_inode;
+ struct coda_file_info *cfi;
+ int err = 0;
+
+ if (!(S_ISREG(coda_inode->i_mode) || S_ISDIR(coda_inode->i_mode) ||
+ S_ISLNK(coda_inode->i_mode)))
+ return -EINVAL;
+
+ cfi = CODA_FTOC(coda_file);
+ if (!cfi || cfi->cfi_magic != CODA_MAGIC) BUG();
+ host_file = cfi->cfi_container;
+
+ coda_vfs_stat.fsync++;
+
+ if (host_file->f_op && host_file->f_op->fsync) {
+ host_dentry = host_file->f_dentry;
+ host_inode = host_dentry->d_inode;
+ down(&host_inode->i_sem);
+ err = host_file->f_op->fsync(host_file, host_dentry, datasync);
+ up(&host_inode->i_sem);
+ }
+
+ if ( !err && !datasync ) {
+ lock_kernel();
+ err = venus_fsync(coda_inode->i_sb, coda_i2f(coda_inode));
+ unlock_kernel();
+ }
+
+ return err;
+}
+
+struct file_operations coda_file_operations = {
+ llseek: generic_file_llseek,
+ read: coda_file_read,
+ write: coda_file_write,
+ mmap: coda_file_mmap,
+ open: coda_open,
+ flush: coda_flush,
+ release: coda_release,
+ fsync: coda_fsync,
+};
+
diff --git a/fs/coda/inode.c b/fs/coda/inode.c
new file mode 100644
index 00000000000000..a20ec117f3ba6b
--- /dev/null
+++ b/fs/coda/inode.c
@@ -0,0 +1,271 @@
+/*
+ * Super block/filesystem wide operations
+ *
+ * Copyright (C) 1996 Peter J. Braam <braam@maths.ox.ac.uk> and
+ * Michael Callahan <callahan@maths.ox.ac.uk>
+ *
+ * Rewritten for Linux 2.1. Peter Braam <braam@cs.cmu.edu>
+ * Copyright (C) Carnegie Mellon University
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <linux/unistd.h>
+#include <linux/smp_lock.h>
+#include <linux/file.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include <linux/fs.h>
+#include <linux/vmalloc.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_psdev.h>
+#include <linux/coda_fs_i.h>
+#include <linux/coda_cache.h>
+
+/* VFS super_block ops */
+static struct super_block *coda_read_super(struct super_block *, void *, int);
+static void coda_read_inode(struct inode *);
+static void coda_clear_inode(struct inode *);
+static void coda_put_super(struct super_block *);
+static int coda_statfs(struct super_block *sb, struct statfs *buf);
+
+/* exported operations */
+struct super_operations coda_super_operations =
+{
+ read_inode: coda_read_inode,
+ clear_inode: coda_clear_inode,
+ put_super: coda_put_super,
+ statfs: coda_statfs,
+};
+
+static int get_device_index(struct coda_mount_data *data)
+{
+ struct file *file;
+ struct inode *inode;
+ int idx;
+
+ if(data == NULL) {
+ printk("coda_read_super: Bad mount data\n");
+ return -1;
+ }
+
+ if(data->version != CODA_MOUNT_VERSION) {
+ printk("coda_read_super: Bad mount version\n");
+ return -1;
+ }
+
+ file = fget(data->fd);
+ inode = NULL;
+ if(file)
+ inode = file->f_dentry->d_inode;
+
+ if(!inode || !S_ISCHR(inode->i_mode) ||
+ MAJOR(inode->i_rdev) != CODA_PSDEV_MAJOR) {
+ if(file)
+ fput(file);
+
+ printk("coda_read_super: Bad file\n");
+ return -1;
+ }
+
+ idx = MINOR(inode->i_rdev);
+ fput(file);
+
+ if(idx < 0 || idx >= MAX_CODADEVS) {
+ printk("coda_read_super: Bad minor number\n");
+ return -1;
+ }
+
+ return idx;
+}
+
+static struct super_block * coda_read_super(struct super_block *sb,
+ void *data, int silent)
+{
+ struct inode *root = 0;
+ struct coda_sb_info *sbi = NULL;
+ struct venus_comm *vc = NULL;
+ ViceFid fid;
+ int error;
+ int idx;
+
+ idx = get_device_index((struct coda_mount_data *) data);
+
+ /* Ignore errors in data, for backward compatibility */
+ if(idx == -1)
+ idx = 0;
+
+ printk(KERN_INFO "coda_read_super: device index: %i\n", idx);
+
+ vc = &coda_comms[idx];
+ if (!vc->vc_inuse) {
+ printk("coda_read_super: No pseudo device\n");
+ return NULL;
+ }
+
+ if ( vc->vc_sb ) {
+ printk("coda_read_super: Device already mounted\n");
+ return NULL;
+ }
+
+ sbi = kmalloc(sizeof(struct coda_sb_info), GFP_KERNEL);
+ if(!sbi) {
+ return NULL;
+ }
+
+ vc->vc_sb = sb;
+
+ sbi->sbi_sb = sb;
+ sbi->sbi_vcomm = vc;
+ INIT_LIST_HEAD(&sbi->sbi_cihead);
+ init_MUTEX(&sbi->sbi_iget4_mutex);
+
+ sb->u.generic_sbp = sbi;
+ sb->s_blocksize = 1024; /* XXXXX what do we put here?? */
+ sb->s_blocksize_bits = 10;
+ sb->s_magic = CODA_SUPER_MAGIC;
+ sb->s_op = &coda_super_operations;
+
+ /* get root fid from Venus: this needs the root inode */
+ error = venus_rootfid(sb, &fid);
+ if ( error ) {
+ printk("coda_read_super: coda_get_rootfid failed with %d\n",
+ error);
+ goto error;
+ }
+ printk("coda_read_super: rootfid is %s\n", coda_f2s(&fid));
+
+ /* make root inode */
+ error = coda_cnode_make(&root, &fid, sb);
+ if ( error || !root ) {
+ printk("Failure of coda_cnode_make for root: error %d\n", error);
+ goto error;
+ }
+
+ printk("coda_read_super: rootinode is %ld dev %d\n",
+ root->i_ino, root->i_dev);
+ sb->s_root = d_alloc_root(root);
+ return sb;
+
+ error:
+ if (sbi) {
+ kfree(sbi);
+ if(vc)
+ vc->vc_sb = NULL;
+ }
+ if (root)
+ iput(root);
+
+ return NULL;
+}
+
+static void coda_put_super(struct super_block *sb)
+{
+ struct coda_sb_info *sbi;
+
+ sbi = coda_sbp(sb);
+ sbi->sbi_vcomm->vc_sb = NULL;
+ list_del_init(&sbi->sbi_cihead);
+
+ printk("Coda: Bye bye.\n");
+ kfree(sbi);
+}
+
+/* all filling in of inodes postponed until lookup */
+static void coda_read_inode(struct inode *inode)
+{
+ struct coda_sb_info *sbi = coda_sbp(inode->i_sb);
+ struct coda_inode_info *cii;
+
+ if (!sbi) BUG();
+
+ cii = ITOC(inode);
+ if (!coda_isnullfid(&cii->c_fid)) {
+ printk("coda_read_inode: initialized inode");
+ return;
+ }
+
+ cii->c_mapcount = 0;
+ list_add(&cii->c_cilist, &sbi->sbi_cihead);
+}
+
+static void coda_clear_inode(struct inode *inode)
+{
+ struct coda_inode_info *cii = ITOC(inode);
+
+ CDEBUG(D_SUPER, " inode->ino: %ld, count: %d\n",
+ inode->i_ino, atomic_read(&inode->i_count));
+ CDEBUG(D_DOWNCALL, "clearing inode: %ld, %x\n", inode->i_ino, cii->c_flags);
+
+ list_del_init(&cii->c_cilist);
+ coda_cache_clear_inode(inode);
+}
+
+int coda_notify_change(struct dentry *de, struct iattr *iattr)
+{
+ struct inode *inode = de->d_inode;
+ struct coda_vattr vattr;
+ int error;
+
+ memset(&vattr, 0, sizeof(vattr));
+
+ inode->i_ctime = CURRENT_TIME;
+ coda_iattr_to_vattr(iattr, &vattr);
+ vattr.va_type = C_VNON; /* cannot set type */
+ CDEBUG(D_SUPER, "vattr.va_mode %o\n", vattr.va_mode);
+
+ /* Venus is responsible for truncating the container-file!!! */
+ error = venus_setattr(inode->i_sb, coda_i2f(inode), &vattr);
+
+ if ( !error ) {
+ coda_vattr_to_iattr(inode, &vattr);
+ coda_cache_clear_inode(inode);
+ }
+ CDEBUG(D_SUPER, "inode.i_mode %o, error %d\n", inode->i_mode, error);
+
+ return error;
+}
+
+struct inode_operations coda_file_inode_operations = {
+ permission: coda_permission,
+ revalidate: coda_revalidate_inode,
+ setattr: coda_notify_change,
+};
+
+static int coda_statfs(struct super_block *sb, struct statfs *buf)
+{
+ int error;
+
+ error = venus_statfs(sb, buf);
+
+ if (error) {
+ /* fake something like AFS does */
+ buf->f_blocks = 9000000;
+ buf->f_bfree = 9000000;
+ buf->f_bavail = 9000000;
+ buf->f_files = 9000000;
+ buf->f_ffree = 9000000;
+ }
+
+ /* and fill in the rest */
+ buf->f_type = CODA_SUPER_MAGIC;
+ buf->f_bsize = 1024;
+ buf->f_namelen = CODA_MAXNAMLEN;
+
+ return 0;
+}
+
+/* init_coda: used by filesystems.c to register coda */
+
+DECLARE_FSTYPE( coda_fs_type, "coda", coda_read_super, 0);
+
diff --git a/fs/coda/pioctl.c b/fs/coda/pioctl.c
new file mode 100644
index 00000000000000..92ce22a393dfd0
--- /dev/null
+++ b/fs/coda/pioctl.c
@@ -0,0 +1,103 @@
+/*
+ * Pioctl operations for Coda.
+ * Original version: (C) 1996 Peter Braam
+ * Rewritten for Linux 2.1: (C) 1997 Carnegie Mellon University
+ *
+ * Carnegie Mellon encourages users of this code to contribute improvements
+ * to the Coda project. Contact Peter Braam <coda@cs.cmu.edu>.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <linux/string.h>
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <asm/uaccess.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_fs_i.h>
+#include <linux/coda_psdev.h>
+
+/* pioctl ops */
+static int coda_ioctl_permission(struct inode *inode, int mask);
+static int coda_pioctl(struct inode * inode, struct file * filp,
+ unsigned int cmd, unsigned long user_data);
+
+/* exported from this file */
+struct inode_operations coda_ioctl_inode_operations =
+{
+ permission: coda_ioctl_permission,
+ setattr: coda_notify_change,
+};
+
+struct file_operations coda_ioctl_operations = {
+ owner: THIS_MODULE,
+ ioctl: coda_pioctl,
+};
+
+/* the coda pioctl inode ops */
+static int coda_ioctl_permission(struct inode *inode, int mask)
+{
+ return 0;
+}
+
+static int coda_pioctl(struct inode * inode, struct file * filp,
+ unsigned int cmd, unsigned long user_data)
+{
+ struct nameidata nd;
+ int error;
+ struct PioctlData data;
+ struct inode *target_inode = NULL;
+ struct coda_inode_info *cnp;
+
+ /* get the Pioctl data arguments from user space */
+ if (copy_from_user(&data, (int *)user_data, sizeof(data))) {
+ return -EINVAL;
+ }
+
+ /*
+ * Look up the pathname. Note that the pathname is in
+ * user memory, and namei takes care of this
+ */
+ CDEBUG(D_PIOCTL, "namei, data.follow = %d\n",
+ data.follow);
+ if ( data.follow ) {
+ error = user_path_walk(data.path, &nd);
+ } else {
+ error = user_path_walk_link(data.path, &nd);
+ }
+
+ if ( error ) {
+ CDEBUG(D_PIOCTL, "error: lookup fails.\n");
+ return error;
+ } else {
+ target_inode = nd.dentry->d_inode;
+ }
+
+ CDEBUG(D_PIOCTL, "target ino: 0x%ld, dev: 0x%x\n",
+ target_inode->i_ino, target_inode->i_dev);
+
+ /* return if it is not a Coda inode */
+ if ( target_inode->i_sb != inode->i_sb ) {
+ path_release(&nd);
+ return -EINVAL;
+ }
+
+ /* now proceed to make the upcall */
+ cnp = ITOC(target_inode);
+
+ error = venus_pioctl(inode->i_sb, &(cnp->c_fid), cmd, &data);
+
+ CDEBUG(D_PIOCTL, "ioctl on inode %ld\n", target_inode->i_ino);
+ CDEBUG(D_DOWNCALL, "dput on ino: %ld, icount %d, dcount %d\n", target_inode->i_ino,
+ atomic_read(&target_inode->i_count), atomic_read(&nd.dentry->d_count));
+ path_release(&nd);
+ return error;
+}
+
diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c
new file mode 100644
index 00000000000000..4e4921a0f481d8
--- /dev/null
+++ b/fs/coda/psdev.c
@@ -0,0 +1,446 @@
+/*
+ * An implementation of a loadable kernel mode driver providing
+ * multiple kernel/user space bidirectional communications links.
+ *
+ * Author: Alan Cox <alan@redhat.com>
+ *
+ * This program 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 of the License, or (at your option) any later version.
+ *
+ * Adapted to become the Linux 2.0 Coda pseudo device
+ * Peter Braam <braam@maths.ox.ac.uk>
+ * Michael Callahan <mjc@emmy.smith.edu>
+ *
+ * Changes for Linux 2.1
+ * Copyright (c) 1997 Carnegie-Mellon University
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/lp.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+#include <linux/delay.h>
+#include <linux/skbuff.h>
+#include <linux/proc_fs.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/smp_lock.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/poll.h>
+#include <asm/uaccess.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_fs_i.h>
+#include <linux/coda_psdev.h>
+#include <linux/coda_proc.h>
+
+#define upc_free(r) kfree(r)
+
+/*
+ * Coda stuff
+ */
+extern struct file_system_type coda_fs_type;
+
+/* statistics */
+int coda_hard; /* allows signals during upcalls */
+unsigned long coda_timeout = 30; /* .. secs, then signals will dequeue */
+
+
+struct venus_comm coda_comms[MAX_CODADEVS];
+kmem_cache_t *cii_cache, *cred_cache, *upc_cache;
+
+/*
+ * Device operations
+ */
+
+static unsigned int coda_psdev_poll(struct file *file, poll_table * wait)
+{
+ struct venus_comm *vcp = (struct venus_comm *) file->private_data;
+ unsigned int mask = POLLOUT | POLLWRNORM;
+
+ poll_wait(file, &vcp->vc_waitq, wait);
+ if (!list_empty(&vcp->vc_pending))
+ mask |= POLLIN | POLLRDNORM;
+
+ return mask;
+}
+
+static int coda_psdev_ioctl(struct inode * inode, struct file * filp,
+ unsigned int cmd, unsigned long arg)
+{
+ unsigned int data;
+
+ switch(cmd) {
+ case CIOC_KERNEL_VERSION:
+ data = CODA_KERNEL_VERSION;
+ return put_user(data, (int *) arg);
+ default:
+ return -ENOTTY;
+ }
+
+ return 0;
+}
+
+/*
+ * Receive a message written by Venus to the psdev
+ */
+
+static ssize_t coda_psdev_write(struct file *file, const char *buf,
+ size_t nbytes, loff_t *off)
+{
+ struct venus_comm *vcp = (struct venus_comm *) file->private_data;
+ struct upc_req *req = NULL;
+ struct upc_req *tmp;
+ struct list_head *lh;
+ struct coda_in_hdr hdr;
+ ssize_t retval = 0, count = 0;
+ int error;
+
+ /* Peek at the opcode, uniquefier */
+ if (copy_from_user(&hdr, buf, 2 * sizeof(u_long)))
+ return -EFAULT;
+
+ CDEBUG(D_PSDEV, "(process,opc,uniq)=(%d,%ld,%ld), nbytes %ld\n",
+ current->pid, hdr.opcode, hdr.unique, (long)nbytes);
+
+ if (DOWNCALL(hdr.opcode)) {
+ struct super_block *sb = NULL;
+ union outputArgs *dcbuf;
+ int size = sizeof(*dcbuf);
+
+ sb = vcp->vc_sb;
+ if ( !sb ) {
+ CDEBUG(D_PSDEV, "coda_psdev_write: downcall, no SB!\n");
+ count = nbytes;
+ goto out;
+ }
+ CDEBUG(D_PSDEV, "handling downcall\n");
+
+ if ( nbytes < sizeof(struct coda_out_hdr) ) {
+ printk("coda_downcall opc %ld uniq %ld, not enough!\n",
+ hdr.opcode, hdr.unique);
+ count = nbytes;
+ goto out;
+ }
+ if ( nbytes > size ) {
+ printk("Coda: downcall opc %ld, uniq %ld, too much!",
+ hdr.opcode, hdr.unique);
+ nbytes = size;
+ }
+ CODA_ALLOC(dcbuf, union outputArgs *, nbytes);
+ if (copy_from_user(dcbuf, buf, nbytes)) {
+ CODA_FREE(dcbuf, nbytes);
+ retval = -EFAULT;
+ goto out;
+ }
+
+ /* what downcall errors does Venus handle ? */
+ lock_kernel();
+ error = coda_downcall(hdr.opcode, dcbuf, sb);
+ unlock_kernel();
+
+ CODA_FREE(dcbuf, nbytes);
+ if (error) {
+ printk("psdev_write: coda_downcall error: %d\n", error);
+ retval = error;
+ goto out;
+ }
+ count = nbytes;
+ goto out;
+ }
+
+ /* Look for the message on the processing queue. */
+ lock_kernel();
+ list_for_each(lh, &vcp->vc_processing) {
+ tmp = list_entry(lh, struct upc_req , uc_chain);
+ if (tmp->uc_unique == hdr.unique) {
+ req = tmp;
+ list_del(&req->uc_chain);
+ break;
+ }
+ }
+ unlock_kernel();
+
+ if (!req) {
+ printk("psdev_write: msg (%ld, %ld) not found\n",
+ hdr.opcode, hdr.unique);
+ retval = -ESRCH;
+ goto out;
+ }
+
+ CDEBUG(D_PSDEV,"Eureka: uniq %ld on queue!\n", hdr.unique);
+
+ /* move data into response buffer. */
+ if (req->uc_outSize < nbytes) {
+ printk("psdev_write: too much cnt: %d, cnt: %ld, opc: %ld, uniq: %ld.\n",
+ req->uc_outSize, (long)nbytes, hdr.opcode, hdr.unique);
+ nbytes = req->uc_outSize; /* don't have more space! */
+ }
+ if (copy_from_user(req->uc_data, buf, nbytes)) {
+ req->uc_flags |= REQ_ABORT;
+ wake_up(&req->uc_sleep);
+ retval = -EFAULT;
+ goto out;
+ }
+
+ /* adjust outsize. is this useful ?? */
+ req->uc_outSize = nbytes;
+ req->uc_flags |= REQ_WRITE;
+ count = nbytes;
+
+ /* Convert filedescriptor into a file handle */
+ if (req->uc_opcode == CODA_OPEN_BY_FD) {
+ struct coda_open_by_fd_out *outp =
+ (struct coda_open_by_fd_out *)req->uc_data;
+ outp->fh = fget(outp->fd);
+ }
+
+ CDEBUG(D_PSDEV,
+ "Found! Count %ld for (opc,uniq)=(%ld,%ld), upc_req at %p\n",
+ (long)count, hdr.opcode, hdr.unique, &req);
+
+ wake_up(&req->uc_sleep);
+out:
+ return(count ? count : retval);
+}
+
+/*
+ * Read a message from the kernel to Venus
+ */
+
+static ssize_t coda_psdev_read(struct file * file, char * buf,
+ size_t nbytes, loff_t *off)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ struct venus_comm *vcp = (struct venus_comm *) file->private_data;
+ struct upc_req *req;
+ ssize_t retval = 0, count = 0;
+
+ if (nbytes == 0)
+ return 0;
+
+ lock_kernel();
+
+ add_wait_queue(&vcp->vc_waitq, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ while (list_empty(&vcp->vc_pending)) {
+ if (file->f_flags & O_NONBLOCK) {
+ retval = -EAGAIN;
+ break;
+ }
+ if (signal_pending(current)) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+ schedule();
+ }
+
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&vcp->vc_waitq, &wait);
+
+ if (retval)
+ goto out;
+
+ req = list_entry(vcp->vc_pending.next, struct upc_req,uc_chain);
+ list_del(&req->uc_chain);
+
+ /* Move the input args into userspace */
+ count = req->uc_inSize;
+ if (nbytes < req->uc_inSize) {
+ printk ("psdev_read: Venus read %ld bytes of %d in message\n",
+ (long)nbytes, req->uc_inSize);
+ count = nbytes;
+ }
+
+ if (copy_to_user(buf, req->uc_data, count))
+ retval = -EFAULT;
+
+ /* If request was not a signal, enqueue and don't free */
+ if (!(req->uc_flags & REQ_ASYNC)) {
+ req->uc_flags |= REQ_READ;
+ list_add(&(req->uc_chain), vcp->vc_processing.prev);
+ goto out;
+ }
+
+ CDEBUG(D_PSDEV, "vcread: signal msg (%d, %d)\n",
+ req->uc_opcode, req->uc_unique);
+
+ CODA_FREE(req->uc_data, sizeof(struct coda_in_hdr));
+ upc_free(req);
+out:
+ unlock_kernel();
+ return (count ? count : retval);
+}
+
+static int coda_psdev_open(struct inode * inode, struct file * file)
+{
+ struct venus_comm *vcp;
+ int idx;
+
+ lock_kernel();
+ idx = MINOR(inode->i_rdev);
+ if(idx >= MAX_CODADEVS) {
+ unlock_kernel();
+ return -ENODEV;
+ }
+
+ vcp = &coda_comms[idx];
+ if(vcp->vc_inuse) {
+ unlock_kernel();
+ return -EBUSY;
+ }
+
+ if (!vcp->vc_inuse++) {
+ INIT_LIST_HEAD(&vcp->vc_pending);
+ INIT_LIST_HEAD(&vcp->vc_processing);
+ init_waitqueue_head(&vcp->vc_waitq);
+ vcp->vc_sb = 0;
+ vcp->vc_seq = 0;
+ }
+
+ file->private_data = vcp;
+
+ CDEBUG(D_PSDEV, "device %i - inuse: %d\n", idx, vcp->vc_inuse);
+
+ unlock_kernel();
+ return 0;
+}
+
+
+static int coda_psdev_release(struct inode * inode, struct file * file)
+{
+ struct venus_comm *vcp = (struct venus_comm *) file->private_data;
+ struct upc_req *req;
+ struct list_head *lh, *next;
+
+ lock_kernel();
+ if ( !vcp->vc_inuse ) {
+ unlock_kernel();
+ printk("psdev_release: Not open.\n");
+ return -1;
+ }
+
+ CDEBUG(D_PSDEV, "psdev_release: inuse %d\n", vcp->vc_inuse);
+ if (--vcp->vc_inuse) {
+ unlock_kernel();
+ return 0;
+ }
+
+ /* Wakeup clients so they can return. */
+ CDEBUG(D_PSDEV, "wake up pending clients\n");
+ lh = vcp->vc_pending.next;
+ next = lh;
+ while ( (lh = next) != &vcp->vc_pending) {
+ next = lh->next;
+ req = list_entry(lh, struct upc_req, uc_chain);
+ /* Async requests need to be freed here */
+ if (req->uc_flags & REQ_ASYNC) {
+ CODA_FREE(req->uc_data, sizeof(struct coda_in_hdr));
+ upc_free(req);
+ continue;
+ }
+ req->uc_flags |= REQ_ABORT;
+ wake_up(&req->uc_sleep);
+ }
+
+ lh = &vcp->vc_processing;
+ CDEBUG(D_PSDEV, "wake up processing clients\n");
+ while ( (lh = lh->next) != &vcp->vc_processing) {
+ req = list_entry(lh, struct upc_req, uc_chain);
+ req->uc_flags |= REQ_ABORT;
+ wake_up(&req->uc_sleep);
+ }
+ CDEBUG(D_PSDEV, "Done.\n");
+
+ unlock_kernel();
+ return 0;
+}
+
+
+static struct file_operations coda_psdev_fops = {
+ owner: THIS_MODULE,
+ read: coda_psdev_read,
+ write: coda_psdev_write,
+ poll: coda_psdev_poll,
+ ioctl: coda_psdev_ioctl,
+ open: coda_psdev_open,
+ release: coda_psdev_release,
+};
+
+static devfs_handle_t devfs_handle;
+
+static int init_coda_psdev(void)
+{
+ if(devfs_register_chrdev(CODA_PSDEV_MAJOR,"coda_psdev",
+ &coda_psdev_fops)) {
+ printk(KERN_ERR "coda_psdev: unable to get major %d\n",
+ CODA_PSDEV_MAJOR);
+ return -EIO;
+ }
+ devfs_handle = devfs_mk_dir (NULL, "coda", NULL);
+ devfs_register_series (devfs_handle, "%u", MAX_CODADEVS, DEVFS_FL_NONE,
+ CODA_PSDEV_MAJOR, 0,
+ S_IFCHR | S_IRUSR | S_IWUSR,
+ &coda_psdev_fops, NULL);
+
+ coda_sysctl_init();
+
+ return 0;
+}
+
+
+MODULE_AUTHOR("Peter J. Braam <braam@cs.cmu.edu>");
+MODULE_LICENSE("GPL");
+
+static int __init init_coda(void)
+{
+ int status;
+ printk(KERN_INFO "Coda Kernel/Venus communications, v5.3.18, coda@cs.cmu.edu\n");
+
+ status = init_coda_psdev();
+ if ( status ) {
+ printk("Problem (%d) in init_coda_psdev\n", status);
+ return status;
+ }
+
+ status = register_filesystem(&coda_fs_type);
+ if (status) {
+ printk("coda: failed to register filesystem!\n");
+ devfs_unregister(devfs_handle);
+ devfs_unregister_chrdev(CODA_PSDEV_MAJOR,"coda_psdev");
+ coda_sysctl_clean();
+ }
+ return status;
+}
+
+static void __exit exit_coda(void)
+{
+ int err;
+
+ err = unregister_filesystem(&coda_fs_type);
+ if ( err != 0 ) {
+ printk("coda: failed to unregister filesystem\n");
+ }
+ devfs_unregister(devfs_handle);
+ devfs_unregister_chrdev(CODA_PSDEV_MAJOR, "coda_psdev");
+ coda_sysctl_clean();
+}
+
+module_init(init_coda);
+module_exit(exit_coda);
+
diff --git a/fs/coda/symlink.c b/fs/coda/symlink.c
new file mode 100644
index 00000000000000..f3318b82bd0ea8
--- /dev/null
+++ b/fs/coda/symlink.c
@@ -0,0 +1,55 @@
+/*
+ * Symlink inode operations for Coda filesystem
+ * Original version: (C) 1996 P. Braam and M. Callahan
+ * Rewritten for Linux 2.1. (C) 1997 Carnegie Mellon University
+ *
+ * Carnegie Mellon encourages users to contribute improvements to
+ * the Coda project. Contact Peter Braam (coda@cs.cmu.edu).
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <linux/smp_lock.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_psdev.h>
+#include <linux/coda_fs_i.h>
+#include <linux/coda_proc.h>
+
+static int coda_symlink_filler(struct file *file, struct page *page)
+{
+ struct inode *inode = page->mapping->host;
+ int error;
+ struct coda_inode_info *cii;
+ unsigned int len = PAGE_SIZE;
+ char *p = kmap(page);
+
+ lock_kernel();
+ cii = ITOC(inode);
+ coda_vfs_stat.follow_link++;
+
+ error = venus_readlink(inode->i_sb, &cii->c_fid, p, &len);
+ unlock_kernel();
+ if (error)
+ goto fail;
+ SetPageUptodate(page);
+ kunmap(page);
+ UnlockPage(page);
+ return 0;
+
+fail:
+ SetPageError(page);
+ kunmap(page);
+ UnlockPage(page);
+ return error;
+}
+
+struct address_space_operations coda_symlink_aops = {
+ readpage: coda_symlink_filler
+};
diff --git a/fs/coda/sysctl.c b/fs/coda/sysctl.c
new file mode 100644
index 00000000000000..61ec1264ef01f7
--- /dev/null
+++ b/fs/coda/sysctl.c
@@ -0,0 +1,523 @@
+/*
+ * Sysctl operations for Coda filesystem
+ * Original version: (C) 1996 P. Braam and M. Callahan
+ * Rewritten for Linux 2.1. (C) 1997 Carnegie Mellon University
+ *
+ * Carnegie Mellon encourages users to contribute improvements to
+ * the Coda project. Contact Peter Braam (coda@cs.cmu.edu).
+ *
+ * CODA operation statistics
+ * (c) March, 1998 Zhanyong Wan <zhanyong.wan@yale.edu>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/sysctl.h>
+#include <linux/swapctl.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/ctype.h>
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+#include <linux/utsname.h>
+#define __NO_VERSION__
+#include <linux/module.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_fs_i.h>
+#include <linux/coda_psdev.h>
+#include <linux/coda_cache.h>
+#include <linux/coda_proc.h>
+
+static struct ctl_table_header *fs_table_header;
+
+#define FS_CODA 1 /* Coda file system */
+
+#define CODA_DEBUG 1 /* control debugging */
+#define CODA_ENTRY 2 /* control enter/leave pattern */
+#define CODA_TIMEOUT 3 /* timeout on upcalls to become intrble */
+#define CODA_MC 4 /* use/do not use the access cache */
+#define CODA_HARD 5 /* mount type "hard" or "soft" */
+#define CODA_VFS 6 /* vfs statistics */
+#define CODA_UPCALL 7 /* upcall statistics */
+#define CODA_PERMISSION 8 /* permission statistics */
+#define CODA_CACHE_INV 9 /* cache invalidation statistics */
+#define CODA_FAKE_STATFS 10 /* don't query venus for actual cache usage */
+
+static ctl_table coda_table[] = {
+ {CODA_DEBUG, "debug", &coda_debug, sizeof(int), 0644, NULL, &proc_dointvec},
+ {CODA_MC, "accesscache", &coda_access_cache, sizeof(int), 0644, NULL, &proc_dointvec},
+ {CODA_TIMEOUT, "timeout", &coda_timeout, sizeof(int), 0644, NULL, &proc_dointvec},
+ {CODA_HARD, "hard", &coda_hard, sizeof(int), 0644, NULL, &proc_dointvec},
+ {CODA_VFS, "vfs_stats", NULL, 0, 0644, NULL, &do_reset_coda_vfs_stats},
+ {CODA_UPCALL, "upcall_stats", NULL, 0, 0644, NULL, &do_reset_coda_upcall_stats},
+ {CODA_PERMISSION, "permission_stats", NULL, 0, 0644, NULL, &do_reset_coda_permission_stats},
+ {CODA_CACHE_INV, "cache_inv_stats", NULL, 0, 0644, NULL, &do_reset_coda_cache_inv_stats},
+ {CODA_FAKE_STATFS, "fake_statfs", &coda_fake_statfs, sizeof(int), 0600, NULL, &proc_dointvec},
+ { 0 }
+};
+
+static ctl_table fs_table[] = {
+ {FS_CODA, "coda", NULL, 0, 0555, coda_table},
+ {0}
+};
+
+struct coda_vfs_stats coda_vfs_stat;
+struct coda_permission_stats coda_permission_stat;
+struct coda_cache_inv_stats coda_cache_inv_stat;
+struct coda_upcall_stats_entry coda_upcall_stat[CODA_NCALLS];
+struct coda_upcallstats coda_callstats;
+int coda_upcall_timestamping = 0;
+
+/* keep this in sync with coda.h! */
+char *coda_upcall_names[] = {
+ "totals ", /* 0 */
+ "- ", /* 1 */
+ "root ", /* 2 */
+ "open_by_fd ", /* 3 */
+ "open ", /* 4 */
+ "close ", /* 5 */
+ "ioctl ", /* 6 */
+ "getattr ", /* 7 */
+ "setattr ", /* 8 */
+ "access ", /* 9 */
+ "lookup ", /* 10 */
+ "create ", /* 11 */
+ "remove ", /* 12 */
+ "link ", /* 13 */
+ "rename ", /* 14 */
+ "mkdir ", /* 15 */
+ "rmdir ", /* 16 */
+ "readdir ", /* 17 */
+ "symlink ", /* 18 */
+ "readlink ", /* 19 */
+ "fsync ", /* 20 */
+ "- ", /* 21 */
+ "vget ", /* 22 */
+ "signal ", /* 23 */
+ "replace ", /* 24 */
+ "flush ", /* 25 */
+ "purgeuser ", /* 26 */
+ "zapfile ", /* 27 */
+ "zapdir ", /* 28 */
+ "- ", /* 29 */
+ "purgefid ", /* 30 */
+ "open_by_path", /* 31 */
+ "resolve ", /* 32 */
+ "reintegrate ", /* 33 */
+ "statfs ", /* 34 */
+ "store ", /* 35 */
+ "release " /* 36 */
+};
+
+
+void reset_coda_vfs_stats( void )
+{
+ memset( &coda_vfs_stat, 0, sizeof( coda_vfs_stat ) );
+}
+
+void reset_coda_upcall_stats( void )
+{
+ memset( &coda_upcall_stat, 0, sizeof( coda_upcall_stat ) );
+}
+
+void reset_coda_permission_stats( void )
+{
+ memset( &coda_permission_stat, 0, sizeof( coda_permission_stat ) );
+}
+
+void reset_coda_cache_inv_stats( void )
+{
+ memset( &coda_cache_inv_stat, 0, sizeof( coda_cache_inv_stat ) );
+}
+
+
+void do_time_stats( struct coda_upcall_stats_entry * pentry,
+ unsigned long runtime )
+{
+ unsigned long time = runtime; /* time in us */
+ CDEBUG(D_SPECIAL, "time: %ld\n", time);
+
+ if ( pentry->count == 0 ) {
+ pentry->time_sum = pentry->time_squared_sum = 0;
+ }
+
+ pentry->count++;
+ pentry->time_sum += time;
+ pentry->time_squared_sum += time*time;
+}
+
+
+
+void coda_upcall_stats(int opcode, long unsigned runtime)
+{
+ struct coda_upcall_stats_entry * pentry;
+
+ if ( opcode < 0 || opcode > CODA_NCALLS - 1) {
+ printk("Nasty opcode %d passed to coda_upcall_stats\n",
+ opcode);
+ return;
+ }
+
+ pentry = &coda_upcall_stat[opcode];
+ do_time_stats(pentry, runtime);
+
+ /* fill in the totals */
+ pentry = &coda_upcall_stat[0];
+ do_time_stats(pentry, runtime);
+
+}
+
+unsigned long get_time_average( const struct coda_upcall_stats_entry * pentry )
+{
+ return ( pentry->count == 0 ) ? 0 : pentry->time_sum / pentry->count;
+}
+
+static inline unsigned long absolute( unsigned long x )
+{
+ return x >= 0 ? x : -x;
+}
+
+static unsigned long sqr_root( unsigned long x )
+{
+ unsigned long y = x, r;
+ int n_bit = 0;
+
+ if ( x == 0 )
+ return 0;
+ if ( x < 0)
+ x = -x;
+
+ while ( y ) {
+ y >>= 1;
+ n_bit++;
+ }
+
+ r = 1 << (n_bit/2);
+
+ while ( 1 ) {
+ r = (r + x/r)/2;
+ if ( r*r <= x && x < (r+1)*(r+1) )
+ break;
+ }
+
+ return r;
+}
+
+unsigned long get_time_std_deviation( const struct coda_upcall_stats_entry * pentry )
+{
+ unsigned long time_avg;
+
+ if ( pentry->count <= 1 )
+ return 0;
+
+ time_avg = get_time_average( pentry );
+
+ return sqr_root( (pentry->time_squared_sum / pentry->count) -
+ time_avg * time_avg );
+}
+
+int do_reset_coda_vfs_stats( ctl_table * table, int write, struct file * filp,
+ void * buffer, size_t * lenp )
+{
+ if ( write ) {
+ reset_coda_vfs_stats();
+
+ filp->f_pos += *lenp;
+ } else {
+ *lenp = 0;
+ }
+
+ return 0;
+}
+
+int do_reset_coda_upcall_stats( ctl_table * table, int write,
+ struct file * filp, void * buffer,
+ size_t * lenp )
+{
+ if ( write ) {
+ if (*lenp > 0) {
+ char c;
+ if (get_user(c, (char *)buffer))
+ return -EFAULT;
+ coda_upcall_timestamping = (c == '1');
+ }
+ reset_coda_upcall_stats();
+
+ filp->f_pos += *lenp;
+ } else {
+ *lenp = 0;
+ }
+
+ return 0;
+}
+
+int do_reset_coda_permission_stats( ctl_table * table, int write,
+ struct file * filp, void * buffer,
+ size_t * lenp )
+{
+ if ( write ) {
+ reset_coda_permission_stats();
+
+ filp->f_pos += *lenp;
+ } else {
+ *lenp = 0;
+ }
+
+ return 0;
+}
+
+int do_reset_coda_cache_inv_stats( ctl_table * table, int write,
+ struct file * filp, void * buffer,
+ size_t * lenp )
+{
+ if ( write ) {
+ reset_coda_cache_inv_stats();
+
+ filp->f_pos += *lenp;
+ } else {
+ *lenp = 0;
+ }
+
+ return 0;
+}
+
+int coda_vfs_stats_get_info( char * buffer, char ** start, off_t offset,
+ int length)
+{
+ int len=0;
+ off_t begin;
+ struct coda_vfs_stats * ps = & coda_vfs_stat;
+
+ /* this works as long as we are below 1024 characters! */
+ len += sprintf( buffer,
+ "Coda VFS statistics\n"
+ "===================\n\n"
+ "File Operations:\n"
+ "\topen\t\t%9d\n"
+ "\tflush\t\t%9d\n"
+ "\trelease\t\t%9d\n"
+ "\tfsync\t\t%9d\n\n"
+ "Dir Operations:\n"
+ "\treaddir\t\t%9d\n\n"
+ "Inode Operations\n"
+ "\tcreate\t\t%9d\n"
+ "\tlookup\t\t%9d\n"
+ "\tlink\t\t%9d\n"
+ "\tunlink\t\t%9d\n"
+ "\tsymlink\t\t%9d\n"
+ "\tmkdir\t\t%9d\n"
+ "\trmdir\t\t%9d\n"
+ "\trename\t\t%9d\n"
+ "\tpermission\t%9d\n",
+
+ /* file operations */
+ ps->open,
+ ps->flush,
+ ps->release,
+ ps->fsync,
+
+ /* dir operations */
+ ps->readdir,
+
+ /* inode operations */
+ ps->create,
+ ps->lookup,
+ ps->link,
+ ps->unlink,
+ ps->symlink,
+ ps->mkdir,
+ ps->rmdir,
+ ps->rename,
+ ps->permission);
+
+ begin = offset;
+ *start = buffer + begin;
+ len -= begin;
+
+ if ( len > length )
+ len = length;
+ if ( len < 0 )
+ len = 0;
+
+ return len;
+}
+
+int coda_upcall_stats_get_info( char * buffer, char ** start, off_t offset,
+ int length)
+{
+ int len=0;
+ int i;
+ off_t begin;
+ off_t pos = 0;
+ char tmpbuf[80];
+ int tmplen = 0;
+
+ /* this works as long as we are below 1024 characters! */
+ if ( offset < 80 )
+ len += sprintf( buffer,"%-79s\n", "Coda upcall statistics");
+ if ( offset < 160)
+ len += sprintf( buffer + len,"%-79s\n", "======================");
+ if ( offset < 240)
+ len += sprintf( buffer + len,"%-79s\n", "upcall count avg time(us) std deviation(us)");
+ if ( offset < 320)
+ len += sprintf( buffer + len,"%-79s\n", "------ ----- ------------ -----------------");
+ pos = 320;
+ for ( i = 0 ; i < CODA_NCALLS ; i++ ) {
+ tmplen += sprintf(tmpbuf,"%s %9d %10ld %10ld",
+ coda_upcall_names[i],
+ coda_upcall_stat[i].count,
+ get_time_average(&coda_upcall_stat[i]),
+ coda_upcall_stat[i].time_squared_sum);
+ pos += 80;
+ if ( pos < offset )
+ continue;
+ len += sprintf(buffer + len, "%-79s\n", tmpbuf);
+ if ( len >= length )
+ break;
+ }
+
+ begin = len- (pos - offset);
+ *start = buffer + begin;
+ len -= begin;
+
+ if ( len > length )
+ len = length;
+ if ( len < 0 )
+ len = 0;
+
+ return len;
+}
+
+int coda_permission_stats_get_info( char * buffer, char ** start, off_t offset,
+ int length)
+{
+ int len=0;
+ off_t begin;
+ struct coda_permission_stats * ps = & coda_permission_stat;
+
+ /* this works as long as we are below 1024 characters! */
+ len += sprintf( buffer,
+ "Coda permission statistics\n"
+ "==========================\n\n"
+ "count\t\t%9d\n"
+ "hit count\t%9d\n",
+
+ ps->count,
+ ps->hit_count );
+
+ begin = offset;
+ *start = buffer + begin;
+ len -= begin;
+
+ if ( len > length )
+ len = length;
+ if ( len < 0 )
+ len = 0;
+
+ return len;
+}
+
+int coda_cache_inv_stats_get_info( char * buffer, char ** start, off_t offset,
+ int length)
+{
+ int len=0;
+ off_t begin;
+ struct coda_cache_inv_stats * ps = & coda_cache_inv_stat;
+
+ /* this works as long as we are below 1024 characters! */
+ len += sprintf( buffer,
+ "Coda cache invalidation statistics\n"
+ "==================================\n\n"
+ "flush\t\t%9d\n"
+ "purge user\t%9d\n"
+ "zap_dir\t\t%9d\n"
+ "zap_file\t%9d\n"
+ "zap_vnode\t%9d\n"
+ "purge_fid\t%9d\n"
+ "replace\t\t%9d\n",
+ ps->flush,
+ ps->purge_user,
+ ps->zap_dir,
+ ps->zap_file,
+ ps->zap_vnode,
+ ps->purge_fid,
+ ps->replace );
+
+ begin = offset;
+ *start = buffer + begin;
+ len -= begin;
+
+ if ( len > length )
+ len = length;
+ if ( len < 0 )
+ len = 0;
+
+ return len;
+}
+
+
+#ifdef CONFIG_PROC_FS
+
+/*
+ target directory structure:
+ /proc/fs (see linux/fs/proc/root.c)
+ /proc/fs/coda
+ /proc/fs/coda/{vfs_stats,
+
+*/
+
+struct proc_dir_entry* proc_fs_coda;
+
+#endif
+
+#define coda_proc_create(name,get_info) \
+ create_proc_info_entry(name, 0, proc_fs_coda, get_info)
+
+void coda_sysctl_init()
+{
+ memset(&coda_callstats, 0, sizeof(coda_callstats));
+ reset_coda_vfs_stats();
+ reset_coda_upcall_stats();
+ reset_coda_permission_stats();
+ reset_coda_cache_inv_stats();
+
+#ifdef CONFIG_PROC_FS
+ proc_fs_coda = proc_mkdir("coda", proc_root_fs);
+ if (proc_fs_coda) {
+ proc_fs_coda->owner = THIS_MODULE;
+ coda_proc_create("vfs_stats", coda_vfs_stats_get_info);
+ coda_proc_create("upcall_stats", coda_upcall_stats_get_info);
+ coda_proc_create("permission_stats", coda_permission_stats_get_info);
+ coda_proc_create("cache_inv_stats", coda_cache_inv_stats_get_info);
+ }
+#endif
+
+#ifdef CONFIG_SYSCTL
+ if ( !fs_table_header )
+ fs_table_header = register_sysctl_table(fs_table, 0);
+#endif
+}
+
+void coda_sysctl_clean()
+{
+
+#ifdef CONFIG_SYSCTL
+ if ( fs_table_header ) {
+ unregister_sysctl_table(fs_table_header);
+ fs_table_header = 0;
+ }
+#endif
+
+#if CONFIG_PROC_FS
+ remove_proc_entry("cache_inv_stats", proc_fs_coda);
+ remove_proc_entry("permission_stats", proc_fs_coda);
+ remove_proc_entry("upcall_stats", proc_fs_coda);
+ remove_proc_entry("vfs_stats", proc_fs_coda);
+ remove_proc_entry("coda", proc_root_fs);
+#endif
+}
diff --git a/fs/coda/upcall.c b/fs/coda/upcall.c
new file mode 100644
index 00000000000000..611a4036be2154
--- /dev/null
+++ b/fs/coda/upcall.c
@@ -0,0 +1,987 @@
+/*
+ * Mostly platform independent upcall operations to Venus:
+ * -- upcalls
+ * -- upcall routines
+ *
+ * Linux 2.0 version
+ * Copyright (C) 1996 Peter J. Braam <braam@maths.ox.ac.uk>,
+ * Michael Callahan <callahan@maths.ox.ac.uk>
+ *
+ * Redone for Linux 2.1
+ * Copyright (C) 1997 Carnegie Mellon University
+ *
+ * Carnegie Mellon University encourages users of this code to contribute
+ * improvements to the Coda project. Contact Peter Braam <coda@cs.cmu.edu>.
+ */
+
+#include <asm/system.h>
+#include <asm/signal.h>
+#include <linux/signal.h>
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <linux/string.h>
+#include <asm/uaccess.h>
+#include <linux/vmalloc.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_psdev.h>
+#include <linux/coda_fs_i.h>
+#include <linux/coda_cache.h>
+#include <linux/coda_proc.h>
+
+#define upc_alloc() kmalloc(sizeof(struct upc_req), GFP_KERNEL)
+#define upc_free(r) kfree(r)
+
+static int coda_upcall(struct coda_sb_info *mntinfo, int inSize, int *outSize,
+ union inputArgs *buffer);
+
+static void *alloc_upcall(int opcode, int size)
+{
+ union inputArgs *inp;
+
+ CODA_ALLOC(inp, union inputArgs *, size);
+ if (!inp)
+ return ERR_PTR(-ENOMEM);
+
+ inp->ih.opcode = opcode;
+ inp->ih.pid = current->pid;
+ inp->ih.pgid = current->pgrp;
+ coda_load_creds(&(inp->ih.cred));
+
+ return (void*)inp;
+}
+
+#define UPARG(op)\
+do {\
+ inp = (union inputArgs *)alloc_upcall(op, insize); \
+ if (IS_ERR(inp)) { return PTR_ERR(inp); }\
+ outp = (union outputArgs *)(inp); \
+ outsize = insize; \
+} while (0)
+
+#define INSIZE(tag) sizeof(struct coda_ ## tag ## _in)
+#define OUTSIZE(tag) sizeof(struct coda_ ## tag ## _out)
+#define SIZE(tag) max_t(unsigned int, INSIZE(tag), OUTSIZE(tag))
+
+
+/* the upcalls */
+int venus_rootfid(struct super_block *sb, ViceFid *fidp)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+
+ insize = SIZE(root);
+ UPARG(CODA_ROOT);
+
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+ if (error) {
+ printk("coda_get_rootfid: error %d\n", error);
+ } else {
+ *fidp = (ViceFid) outp->coda_root.VFid;
+ CDEBUG(D_SUPER, "VolumeId: %lx, VnodeId: %lx.\n",
+ fidp->Volume, fidp->Vnode);
+ }
+
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+int venus_getattr(struct super_block *sb, struct ViceFid *fid,
+ struct coda_vattr *attr)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+
+ insize = SIZE(getattr);
+ UPARG(CODA_GETATTR);
+ inp->coda_getattr.VFid = *fid;
+
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+ *attr = outp->coda_getattr.attr;
+
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+int venus_setattr(struct super_block *sb, struct ViceFid *fid,
+ struct coda_vattr *vattr)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+
+ insize = SIZE(setattr);
+ UPARG(CODA_SETATTR);
+
+ inp->coda_setattr.VFid = *fid;
+ inp->coda_setattr.attr = *vattr;
+
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+ CDEBUG(D_SUPER, " result %d\n", error);
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+int venus_lookup(struct super_block *sb, struct ViceFid *fid,
+ const char *name, int length, int * type,
+ struct ViceFid *resfid)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+ int offset;
+
+ offset = INSIZE(lookup);
+ insize = max_t(unsigned int, offset + length +1, OUTSIZE(lookup));
+ UPARG(CODA_LOOKUP);
+
+ inp->coda_lookup.VFid = *fid;
+ inp->coda_lookup.name = offset;
+ inp->coda_lookup.flags = CLU_CASE_SENSITIVE;
+ /* send Venus a null terminated string */
+ memcpy((char *)(inp) + offset, name, length);
+ *((char *)inp + offset + length) = '\0';
+
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+ *resfid = outp->coda_lookup.VFid;
+ *type = outp->coda_lookup.vtype;
+
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+int venus_store(struct super_block *sb, struct ViceFid *fid, int flags,
+ struct coda_cred *cred)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+
+ insize = SIZE(store);
+ UPARG(CODA_STORE);
+
+ memcpy(&(inp->ih.cred), cred, sizeof(*cred));
+
+ inp->coda_store.VFid = *fid;
+ inp->coda_store.flags = flags;
+
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+int venus_release(struct super_block *sb, struct ViceFid *fid, int flags)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+
+ insize = SIZE(release);
+ UPARG(CODA_RELEASE);
+
+ inp->coda_release.VFid = *fid;
+ inp->coda_release.flags = flags;
+
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+int venus_close(struct super_block *sb, struct ViceFid *fid, int flags,
+ struct coda_cred *cred)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+
+ insize = SIZE(release);
+ UPARG(CODA_CLOSE);
+
+ memcpy(&(inp->ih.cred), cred, sizeof(*cred));
+
+ inp->coda_close.VFid = *fid;
+ inp->coda_close.flags = flags;
+
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+int venus_open(struct super_block *sb, struct ViceFid *fid,
+ int flags, struct file **fh)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+
+ insize = SIZE(open_by_fd);
+ UPARG(CODA_OPEN_BY_FD);
+
+ inp->coda_open.VFid = *fid;
+ inp->coda_open.flags = flags;
+
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+ *fh = outp->coda_open_by_fd.fh;
+
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+int venus_mkdir(struct super_block *sb, struct ViceFid *dirfid,
+ const char *name, int length,
+ struct ViceFid *newfid, struct coda_vattr *attrs)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+ int offset;
+
+ offset = INSIZE(mkdir);
+ insize = max_t(unsigned int, offset + length + 1, OUTSIZE(mkdir));
+ UPARG(CODA_MKDIR);
+
+ inp->coda_mkdir.VFid = *dirfid;
+ inp->coda_mkdir.attr = *attrs;
+ inp->coda_mkdir.name = offset;
+ /* Venus must get null terminated string */
+ memcpy((char *)(inp) + offset, name, length);
+ *((char *)inp + offset + length) = '\0';
+
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+ *attrs = outp->coda_mkdir.attr;
+ *newfid = outp->coda_mkdir.VFid;
+
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+
+int venus_rename(struct super_block *sb, struct ViceFid *old_fid,
+ struct ViceFid *new_fid, size_t old_length,
+ size_t new_length, const char *old_name,
+ const char *new_name)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+ int offset, s;
+
+ offset = INSIZE(rename);
+ insize = max_t(unsigned int, offset + new_length + old_length + 8,
+ OUTSIZE(rename));
+ UPARG(CODA_RENAME);
+
+ inp->coda_rename.sourceFid = *old_fid;
+ inp->coda_rename.destFid = *new_fid;
+ inp->coda_rename.srcname = offset;
+
+ /* Venus must receive an null terminated string */
+ s = ( old_length & ~0x3) +4; /* round up to word boundary */
+ memcpy((char *)(inp) + offset, old_name, old_length);
+ *((char *)inp + offset + old_length) = '\0';
+
+ /* another null terminated string for Venus */
+ offset += s;
+ inp->coda_rename.destname = offset;
+ s = ( new_length & ~0x3) +4; /* round up to word boundary */
+ memcpy((char *)(inp) + offset, new_name, new_length);
+ *((char *)inp + offset + new_length) = '\0';
+
+ CDEBUG(D_INODE, "destname in packet: %s\n",
+ (char *)inp + (int) inp->coda_rename.destname);
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+int venus_create(struct super_block *sb, struct ViceFid *dirfid,
+ const char *name, int length, int excl, int mode, int rdev,
+ struct ViceFid *newfid, struct coda_vattr *attrs)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+ int offset;
+
+ offset = INSIZE(create);
+ insize = max_t(unsigned int, offset + length + 1, OUTSIZE(create));
+ UPARG(CODA_CREATE);
+
+ inp->coda_create.VFid = *dirfid;
+ inp->coda_create.attr.va_mode = mode;
+ inp->coda_create.attr.va_rdev = rdev;
+ inp->coda_create.excl = excl;
+ inp->coda_create.mode = mode;
+ inp->coda_create.name = offset;
+
+ /* Venus must get null terminated string */
+ memcpy((char *)(inp) + offset, name, length);
+ *((char *)inp + offset + length) = '\0';
+
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+ *attrs = outp->coda_create.attr;
+ *newfid = outp->coda_create.VFid;
+
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+int venus_rmdir(struct super_block *sb, struct ViceFid *dirfid,
+ const char *name, int length)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+ int offset;
+
+ offset = INSIZE(rmdir);
+ insize = max_t(unsigned int, offset + length + 1, OUTSIZE(rmdir));
+ UPARG(CODA_RMDIR);
+
+ inp->coda_rmdir.VFid = *dirfid;
+ inp->coda_rmdir.name = offset;
+ memcpy((char *)(inp) + offset, name, length);
+ *((char *)inp + offset + length) = '\0';
+
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+int venus_remove(struct super_block *sb, struct ViceFid *dirfid,
+ const char *name, int length)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int error=0, insize, outsize, offset;
+
+ offset = INSIZE(remove);
+ insize = max_t(unsigned int, offset + length + 1, OUTSIZE(remove));
+ UPARG(CODA_REMOVE);
+
+ inp->coda_remove.VFid = *dirfid;
+ inp->coda_remove.name = offset;
+ memcpy((char *)(inp) + offset, name, length);
+ *((char *)inp + offset + length) = '\0';
+
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+int venus_readlink(struct super_block *sb, struct ViceFid *fid,
+ char *buffer, int *length)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+ int retlen;
+ char *result;
+
+ insize = max_t(unsigned int,
+ INSIZE(readlink), OUTSIZE(readlink)+ *length + 1);
+ UPARG(CODA_READLINK);
+
+ inp->coda_readlink.VFid = *fid;
+
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+ if (! error) {
+ retlen = outp->coda_readlink.count;
+ if ( retlen > *length )
+ retlen = *length;
+ *length = retlen;
+ result = (char *)outp + (long)outp->coda_readlink.data;
+ memcpy(buffer, result, retlen);
+ *(buffer + retlen) = '\0';
+ }
+
+ CDEBUG(D_INODE, " result %d\n",error);
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+
+
+int venus_link(struct super_block *sb, struct ViceFid *fid,
+ struct ViceFid *dirfid, const char *name, int len )
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+ int offset;
+
+ offset = INSIZE(link);
+ insize = max_t(unsigned int, offset + len + 1, OUTSIZE(link));
+ UPARG(CODA_LINK);
+
+ inp->coda_link.sourceFid = *fid;
+ inp->coda_link.destFid = *dirfid;
+ inp->coda_link.tname = offset;
+
+ /* make sure strings are null terminated */
+ memcpy((char *)(inp) + offset, name, len);
+ *((char *)inp + offset + len) = '\0';
+
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+ CDEBUG(D_INODE, " result %d\n",error);
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+int venus_symlink(struct super_block *sb, struct ViceFid *fid,
+ const char *name, int len,
+ const char *symname, int symlen)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+ int offset, s;
+
+ offset = INSIZE(symlink);
+ insize = max_t(unsigned int, offset + len + symlen + 8, OUTSIZE(symlink));
+ UPARG(CODA_SYMLINK);
+
+ /* inp->coda_symlink.attr = *tva; XXXXXX */
+ inp->coda_symlink.VFid = *fid;
+
+ /* Round up to word boundary and null terminate */
+ inp->coda_symlink.srcname = offset;
+ s = ( symlen & ~0x3 ) + 4;
+ memcpy((char *)(inp) + offset, symname, symlen);
+ *((char *)inp + offset + symlen) = '\0';
+
+ /* Round up to word boundary and null terminate */
+ offset += s;
+ inp->coda_symlink.tname = offset;
+ s = (len & ~0x3) + 4;
+ memcpy((char *)(inp) + offset, name, len);
+ *((char *)inp + offset + len) = '\0';
+
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+ CDEBUG(D_INODE, " result %d\n",error);
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+int venus_fsync(struct super_block *sb, struct ViceFid *fid)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+
+ insize=SIZE(fsync);
+ UPARG(CODA_FSYNC);
+
+ inp->coda_fsync.VFid = *fid;
+ error = coda_upcall(coda_sbp(sb), sizeof(union inputArgs),
+ &outsize, inp);
+
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+int venus_access(struct super_block *sb, struct ViceFid *fid, int mask)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+
+ insize = SIZE(access);
+ UPARG(CODA_ACCESS);
+
+ inp->coda_access.VFid = *fid;
+ inp->coda_access.flags = mask;
+
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+
+int venus_pioctl(struct super_block *sb, struct ViceFid *fid,
+ unsigned int cmd, struct PioctlData *data)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+ int iocsize;
+
+ insize = VC_MAXMSGSIZE;
+ UPARG(CODA_IOCTL);
+
+ /* build packet for Venus */
+ if (data->vi.in_size > VC_MAXDATASIZE) {
+ error = -EINVAL;
+ goto exit;
+ }
+
+ if (data->vi.out_size > VC_MAXDATASIZE) {
+ error = -EINVAL;
+ goto exit;
+ }
+
+ inp->coda_ioctl.VFid = *fid;
+
+ /* the cmd field was mutated by increasing its size field to
+ * reflect the path and follow args. We need to subtract that
+ * out before sending the command to Venus. */
+ inp->coda_ioctl.cmd = (cmd & ~(PIOCPARM_MASK << 16));
+ iocsize = ((cmd >> 16) & PIOCPARM_MASK) - sizeof(char *) - sizeof(int);
+ inp->coda_ioctl.cmd |= (iocsize & PIOCPARM_MASK) << 16;
+
+ /* in->coda_ioctl.rwflag = flag; */
+ inp->coda_ioctl.len = data->vi.in_size;
+ inp->coda_ioctl.data = (char *)(INSIZE(ioctl));
+
+ /* get the data out of user space */
+ if ( copy_from_user((char*)inp + (long)inp->coda_ioctl.data,
+ data->vi.in, data->vi.in_size) ) {
+ error = -EINVAL;
+ goto exit;
+ }
+
+ error = coda_upcall(coda_sbp(sb), SIZE(ioctl) + data->vi.in_size,
+ &outsize, inp);
+
+ if (error) {
+ printk("coda_pioctl: Venus returns: %d for %s\n",
+ error, coda_f2s(fid));
+ goto exit;
+ }
+
+ if (outsize < (long)outp->coda_ioctl.data + outp->coda_ioctl.len) {
+ CDEBUG(D_FILE, "reply size %d < reply len %ld\n", outsize,
+ (long)outp->coda_ioctl.data + outp->coda_ioctl.len);
+ error = -EINVAL;
+ goto exit;
+ }
+
+ if (outp->coda_ioctl.len > data->vi.out_size) {
+ CDEBUG(D_FILE, "return len %d > request len %d\n",
+ outp->coda_ioctl.len, data->vi.out_size);
+ error = -EINVAL;
+ goto exit;
+ }
+
+ /* Copy out the OUT buffer. */
+ error = verify_area(VERIFY_WRITE, data->vi.out, outp->coda_ioctl.len);
+ if ( error ) goto exit;
+
+ if (copy_to_user(data->vi.out,
+ (char *)outp + (long)outp->coda_ioctl.data,
+ outp->coda_ioctl.len)) {
+ error = -EINVAL;
+ }
+ exit:
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+int venus_statfs(struct super_block *sb, struct statfs *sfs)
+{
+ union inputArgs *inp;
+ union outputArgs *outp;
+ int insize, outsize, error;
+
+ insize = max_t(unsigned int, INSIZE(statfs), OUTSIZE(statfs));
+ UPARG(CODA_STATFS);
+
+ error = coda_upcall(coda_sbp(sb), insize, &outsize, inp);
+
+ if (!error) {
+ sfs->f_blocks = outp->coda_statfs.stat.f_blocks;
+ sfs->f_bfree = outp->coda_statfs.stat.f_bfree;
+ sfs->f_bavail = outp->coda_statfs.stat.f_bavail;
+ sfs->f_files = outp->coda_statfs.stat.f_files;
+ sfs->f_ffree = outp->coda_statfs.stat.f_ffree;
+ } else {
+ printk("coda_statfs: Venus returns: %d\n", error);
+ }
+
+ CDEBUG(D_INODE, " result %d\n",error);
+ CODA_FREE(inp, insize);
+ return error;
+}
+
+/*
+ * coda_upcall and coda_downcall routines.
+ *
+ */
+
+static inline unsigned long coda_waitfor_upcall(struct upc_req *vmp,
+ struct venus_comm *vcommp)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ struct timeval begin = { 0, 0 }, end = { 0, 0 };
+
+ vmp->uc_posttime = jiffies;
+
+ if (coda_upcall_timestamping)
+ do_gettimeofday(&begin);
+
+ add_wait_queue(&vmp->uc_sleep, &wait);
+ for (;;) {
+ if ( !coda_hard && vmp->uc_opcode != CODA_CLOSE )
+ set_current_state(TASK_INTERRUPTIBLE);
+ else
+ set_current_state(TASK_UNINTERRUPTIBLE);
+
+ /* venus died */
+ if ( !vcommp->vc_inuse )
+ break;
+
+ /* got a reply */
+ if ( vmp->uc_flags & ( REQ_WRITE | REQ_ABORT ) )
+ break;
+
+ if ( !coda_hard && vmp->uc_opcode != CODA_CLOSE && signal_pending(current) ) {
+ /* if this process really wants to die, let it go */
+ if ( sigismember(&(current->pending.signal), SIGKILL) ||
+ sigismember(&(current->pending.signal), SIGINT) )
+ break;
+ /* signal is present: after timeout always return
+ really smart idea, probably useless ... */
+ if ( jiffies - vmp->uc_posttime > coda_timeout * HZ )
+ break;
+ }
+ schedule();
+ }
+ remove_wait_queue(&vmp->uc_sleep, &wait);
+ set_current_state(TASK_RUNNING);
+
+ if (coda_upcall_timestamping && begin.tv_sec != 0) {
+ do_gettimeofday(&end);
+
+ if (end.tv_usec < begin.tv_usec) {
+ end.tv_usec += 1000000; end.tv_sec--;
+ }
+ end.tv_sec -= begin.tv_sec;
+ end.tv_usec -= begin.tv_usec;
+ }
+
+ CDEBUG(D_SPECIAL, "begin: %ld.%06ld, elapsed: %ld.%06ld\n",
+ begin.tv_sec, (unsigned long)begin.tv_usec,
+ end.tv_sec, (unsigned long)end.tv_usec);
+
+ return ((end.tv_sec * 1000000) + end.tv_usec);
+}
+
+
+/*
+ * coda_upcall will return an error in the case of
+ * failed communication with Venus _or_ will peek at Venus
+ * reply and return Venus' error.
+ *
+ * As venus has 2 types of errors, normal errors (positive) and internal
+ * errors (negative), normal errors are negated, while internal errors
+ * are all mapped to -EINTR, while showing a nice warning message. (jh)
+ *
+ */
+static int coda_upcall(struct coda_sb_info *sbi,
+ int inSize, int *outSize,
+ union inputArgs *buffer)
+{
+ unsigned long runtime;
+ struct venus_comm *vcommp;
+ union outputArgs *out;
+ struct upc_req *req;
+ int error = 0;
+
+ vcommp = sbi->sbi_vcomm;
+ if ( !vcommp->vc_inuse ) {
+ printk("No pseudo device in upcall comms at %p\n", vcommp);
+ return -ENXIO;
+ }
+
+ /* Format the request message. */
+ req = upc_alloc();
+ if (!req) {
+ printk("Failed to allocate upc_req structure\n");
+ return -ENOMEM;
+ }
+ req->uc_data = (void *)buffer;
+ req->uc_flags = 0;
+ req->uc_inSize = inSize;
+ req->uc_outSize = *outSize ? *outSize : inSize;
+ req->uc_opcode = ((union inputArgs *)buffer)->ih.opcode;
+ req->uc_unique = ++vcommp->vc_seq;
+ init_waitqueue_head(&req->uc_sleep);
+
+ /* Fill in the common input args. */
+ ((union inputArgs *)buffer)->ih.unique = req->uc_unique;
+
+ /* Append msg to pending queue and poke Venus. */
+ list_add(&(req->uc_chain), vcommp->vc_pending.prev);
+
+ CDEBUG(D_UPCALL,
+ "Proc %d wake Venus for(opc,uniq) =(%d,%d) msg at %p.zzz.\n",
+ current->pid, req->uc_opcode, req->uc_unique, req);
+
+ wake_up_interruptible(&vcommp->vc_waitq);
+ /* We can be interrupted while we wait for Venus to process
+ * our request. If the interrupt occurs before Venus has read
+ * the request, we dequeue and return. If it occurs after the
+ * read but before the reply, we dequeue, send a signal
+ * message, and return. If it occurs after the reply we ignore
+ * it. In no case do we want to restart the syscall. If it
+ * was interrupted by a venus shutdown (psdev_close), return
+ * ENODEV. */
+
+ /* Go to sleep. Wake up on signals only after the timeout. */
+ runtime = coda_waitfor_upcall(req, vcommp);
+ coda_upcall_stats(((union inputArgs *)buffer)->ih.opcode, runtime);
+
+ CDEBUG(D_TIMING, "opc: %d time: %ld uniq: %d size: %d\n",
+ req->uc_opcode, jiffies - req->uc_posttime,
+ req->uc_unique, req->uc_outSize);
+ CDEBUG(D_UPCALL,
+ "..process %d woken up by Venus for req at %p, data at %p\n",
+ current->pid, req, req->uc_data);
+ if (vcommp->vc_inuse) { /* i.e. Venus is still alive */
+ /* Op went through, interrupt or not... */
+ if (req->uc_flags & REQ_WRITE) {
+ out = (union outputArgs *)req->uc_data;
+ /* here we map positive Venus errors to kernel errors */
+ error = -out->oh.result;
+ CDEBUG(D_UPCALL,
+ "upcall: (u,o,r) (%ld, %ld, %ld) out at %p\n",
+ out->oh.unique, out->oh.opcode, out->oh.result, out);
+ *outSize = req->uc_outSize;
+ goto exit;
+ }
+ if ( !(req->uc_flags & REQ_READ) && signal_pending(current)) {
+ /* Interrupted before venus read it. */
+ CDEBUG(D_UPCALL,
+ "Interrupted before read:(op,un) (%d.%d), flags = %x\n",
+ req->uc_opcode, req->uc_unique, req->uc_flags);
+ list_del(&(req->uc_chain));
+ /* perhaps the best way to convince the app to
+ give up? */
+ error = -EINTR;
+ goto exit;
+ }
+ if ( (req->uc_flags & REQ_READ) && signal_pending(current) ) {
+ /* interrupted after Venus did its read, send signal */
+ union inputArgs *sig_inputArgs;
+ struct upc_req *sig_req;
+
+ CDEBUG(D_UPCALL,
+ "Sending Venus a signal: op = %d.%d, flags = %x\n",
+ req->uc_opcode, req->uc_unique, req->uc_flags);
+
+ list_del(&(req->uc_chain));
+ error = -ENOMEM;
+ sig_req = upc_alloc();
+ if (!sig_req) goto exit;
+
+ CODA_ALLOC((sig_req->uc_data), char *, sizeof(struct coda_in_hdr));
+ if (!sig_req->uc_data) {
+ upc_free(sig_req);
+ goto exit;
+ }
+
+ error = -EINTR;
+ sig_inputArgs = (union inputArgs *)sig_req->uc_data;
+ sig_inputArgs->ih.opcode = CODA_SIGNAL;
+ sig_inputArgs->ih.unique = req->uc_unique;
+
+ sig_req->uc_flags = REQ_ASYNC;
+ sig_req->uc_opcode = sig_inputArgs->ih.opcode;
+ sig_req->uc_unique = sig_inputArgs->ih.unique;
+ sig_req->uc_inSize = sizeof(struct coda_in_hdr);
+ sig_req->uc_outSize = sizeof(struct coda_in_hdr);
+ CDEBUG(D_UPCALL,
+ "coda_upcall: enqueing signal msg (%d, %d)\n",
+ sig_req->uc_opcode, sig_req->uc_unique);
+
+ /* insert at head of queue! */
+ list_add(&(sig_req->uc_chain), &vcommp->vc_pending);
+ wake_up_interruptible(&vcommp->vc_waitq);
+ } else {
+ printk("Coda: Strange interruption..\n");
+ error = -EINTR;
+ }
+ } else { /* If venus died i.e. !VC_OPEN(vcommp) */
+ printk("coda_upcall: Venus dead on (op,un) (%d.%d) flags %d\n",
+ req->uc_opcode, req->uc_unique, req->uc_flags);
+ error = -ENODEV;
+ }
+
+ exit:
+ upc_free(req);
+ if (error)
+ badclstats();
+ return error;
+}
+
+/*
+ The statements below are part of the Coda opportunistic
+ programming -- taken from the Mach/BSD kernel code for Coda.
+ You don't get correct semantics by stating what needs to be
+ done without guaranteeing the invariants needed for it to happen.
+ When will be have time to find out what exactly is going on? (pjb)
+*/
+
+
+/*
+ * There are 7 cases where cache invalidations occur. The semantics
+ * of each is listed here:
+ *
+ * CODA_FLUSH -- flush all entries from the name cache and the cnode cache.
+ * CODA_PURGEUSER -- flush all entries from the name cache for a specific user
+ * This call is a result of token expiration.
+ *
+ * The next arise as the result of callbacks on a file or directory.
+ * CODA_ZAPFILE -- flush the cached attributes for a file.
+
+ * CODA_ZAPDIR -- flush the attributes for the dir and
+ * force a new lookup for all the children
+ of this dir.
+
+ *
+ * The next is a result of Venus detecting an inconsistent file.
+ * CODA_PURGEFID -- flush the attribute for the file
+ * purge it and its children from the dcache
+ *
+ * The last allows Venus to replace local fids with global ones
+ * during reintegration.
+ *
+ * CODA_REPLACE -- replace one ViceFid with another throughout the name cache */
+
+int coda_downcall(int opcode, union outputArgs * out, struct super_block *sb)
+{
+ /* Handle invalidation requests. */
+ if ( !sb || !sb->s_root || !sb->s_root->d_inode) {
+ CDEBUG(D_DOWNCALL, "coda_downcall: opcode %d, no sb!\n", opcode);
+ return 0;
+ }
+
+ switch (opcode) {
+
+ case CODA_FLUSH : {
+ clstats(CODA_FLUSH);
+ CDEBUG(D_DOWNCALL, "CODA_FLUSH\n");
+ coda_cache_clear_all(sb, NULL);
+ shrink_dcache_sb(sb);
+ coda_flag_inode(sb->s_root->d_inode, C_FLUSH);
+ return(0);
+ }
+
+ case CODA_PURGEUSER : {
+ struct coda_cred *cred = &out->coda_purgeuser.cred;
+ CDEBUG(D_DOWNCALL, "CODA_PURGEUSER\n");
+ if ( !cred ) {
+ printk("PURGEUSER: null cred!\n");
+ return 0;
+ }
+ clstats(CODA_PURGEUSER);
+ coda_cache_clear_all(sb, cred);
+ return(0);
+ }
+
+ case CODA_ZAPDIR : {
+ struct inode *inode;
+ ViceFid *fid = &out->coda_zapdir.CodaFid;
+ CDEBUG(D_DOWNCALL, "zapdir: fid = %s...\n", coda_f2s(fid));
+ clstats(CODA_ZAPDIR);
+
+ inode = coda_fid_to_inode(fid, sb);
+ if (inode) {
+ CDEBUG(D_DOWNCALL, "zapdir: inode = %ld children flagged\n",
+ inode->i_ino);
+ coda_flag_inode_children(inode, C_PURGE);
+ CDEBUG(D_DOWNCALL, "zapdir: inode = %ld cache cleared\n", inode->i_ino);
+ coda_flag_inode(inode, C_VATTR);
+ iput(inode);
+ } else
+ CDEBUG(D_DOWNCALL, "zapdir: no inode\n");
+
+ return(0);
+ }
+
+ case CODA_ZAPFILE : {
+ struct inode *inode;
+ struct ViceFid *fid = &out->coda_zapfile.CodaFid;
+ clstats(CODA_ZAPFILE);
+ CDEBUG(D_DOWNCALL, "zapfile: fid = %s\n", coda_f2s(fid));
+ inode = coda_fid_to_inode(fid, sb);
+ if ( inode ) {
+ CDEBUG(D_DOWNCALL, "zapfile: inode = %ld\n",
+ inode->i_ino);
+ coda_flag_inode(inode, C_VATTR);
+ iput(inode);
+ } else
+ CDEBUG(D_DOWNCALL, "zapfile: no inode\n");
+ return 0;
+ }
+
+ case CODA_PURGEFID : {
+ struct inode *inode;
+ ViceFid *fid = &out->coda_purgefid.CodaFid;
+ CDEBUG(D_DOWNCALL, "purgefid: fid = %s\n", coda_f2s(fid));
+ clstats(CODA_PURGEFID);
+ inode = coda_fid_to_inode(fid, sb);
+ if ( inode ) {
+ CDEBUG(D_DOWNCALL, "purgefid: inode = %ld\n",
+ inode->i_ino);
+ coda_flag_inode_children(inode, C_PURGE);
+
+ /* catch the dentries later if some are still busy */
+ coda_flag_inode(inode, C_PURGE);
+ d_prune_aliases(inode);
+
+ iput(inode);
+ } else
+ CDEBUG(D_DOWNCALL, "purgefid: no inode\n");
+ return 0;
+ }
+
+ case CODA_REPLACE : {
+ struct inode *inode;
+ ViceFid *oldfid = &out->coda_replace.OldFid;
+ ViceFid *newfid = &out->coda_replace.NewFid;
+ clstats(CODA_REPLACE);
+ CDEBUG(D_DOWNCALL, "CODA_REPLACE\n");
+ inode = coda_fid_to_inode(oldfid, sb);
+ if ( inode ) {
+ CDEBUG(D_DOWNCALL, "replacefid: inode = %ld\n",
+ inode->i_ino);
+ coda_replace_fid(inode, oldfid, newfid);
+ iput(inode);
+ }else
+ CDEBUG(D_DOWNCALL, "purgefid: no inode\n");
+
+ return 0;
+ }
+ }
+ return 0;
+}
+
diff --git a/fs/cramfs/Makefile b/fs/cramfs/Makefile
new file mode 100644
index 00000000000000..dc8352324da0d5
--- /dev/null
+++ b/fs/cramfs/Makefile
@@ -0,0 +1,13 @@
+#
+# Makefile for the linux cramfs routines.
+#
+
+O_TARGET := cramfs.o
+
+obj-y := inode.o uncompress.o
+
+obj-m := $(O_TARGET)
+
+CFLAGS_uncompress.o := -I $(TOPDIR)/fs/inflate_fs
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/cramfs/README b/fs/cramfs/README
new file mode 100644
index 00000000000000..445d1c2d764675
--- /dev/null
+++ b/fs/cramfs/README
@@ -0,0 +1,168 @@
+Notes on Filesystem Layout
+--------------------------
+
+These notes describe what mkcramfs generates. Kernel requirements are
+a bit looser, e.g. it doesn't care if the <file_data> items are
+swapped around (though it does care that directory entries (inodes) in
+a given directory are contiguous, as this is used by readdir).
+
+All data is currently in host-endian format; neither mkcramfs nor the
+kernel ever do swabbing. (See section `Block Size' below.)
+
+<filesystem>:
+ <superblock>
+ <directory_structure>
+ <data>
+
+<superblock>: struct cramfs_super (see cramfs_fs.h).
+
+<directory_structure>:
+ For each file:
+ struct cramfs_inode (see cramfs_fs.h).
+ Filename. Not generally null-terminated, but it is
+ null-padded to a multiple of 4 bytes.
+
+The order of inode traversal is described as "width-first" (not to be
+confused with breadth-first); i.e. like depth-first but listing all of
+a directory's entries before recursing down its subdirectories: the
+same order as `ls -AUR' (but without the /^\..*:$/ directory header
+lines); put another way, the same order as `find -type d -exec
+ls -AU1 {} \;'.
+
+Beginning in 2.4.7, directory entries are sorted. This optimization
+allows cramfs_lookup to return more quickly when a filename does not
+exist, speeds up user-space directory sorts, etc.
+
+<data>:
+ One <file_data> for each file that's either a symlink or a
+ regular file of non-zero st_size.
+
+<file_data>:
+ nblocks * <block_pointer>
+ (where nblocks = (st_size - 1) / blksize + 1)
+ nblocks * <block>
+ padding to multiple of 4 bytes
+
+The i'th <block_pointer> for a file stores the byte offset of the
+*end* of the i'th <block> (i.e. one past the last byte, which is the
+same as the start of the (i+1)'th <block> if there is one). The first
+<block> immediately follows the last <block_pointer> for the file.
+<block_pointer>s are each 32 bits long.
+
+The order of <file_data>'s is a depth-first descent of the directory
+tree, i.e. the same order as `find -size +0 \( -type f -o -type l \)
+-print'.
+
+
+<block>: The i'th <block> is the output of zlib's compress function
+applied to the i'th blksize-sized chunk of the input data.
+(For the last <block> of the file, the input may of course be smaller.)
+Each <block> may be a different size. (See <block_pointer> above.)
+<block>s are merely byte-aligned, not generally u32-aligned.
+
+
+Holes
+-----
+
+This kernel supports cramfs holes (i.e. [efficient representation of]
+blocks in uncompressed data consisting entirely of NUL bytes), but by
+default mkcramfs doesn't test for & create holes, since cramfs in
+kernels up to at least 2.3.39 didn't support holes. Run mkcramfs
+with -z if you want it to create files that can have holes in them.
+
+
+Tools
+-----
+
+The cramfs user-space tools, including mkcramfs and cramfsck, are
+located at <http://sourceforge.net/projects/cramfs/>.
+
+
+Future Development
+==================
+
+Block Size
+----------
+
+(Block size in cramfs refers to the size of input data that is
+compressed at a time. It's intended to be somewhere around
+PAGE_CACHE_SIZE for cramfs_readpage's convenience.)
+
+The superblock ought to indicate the block size that the fs was
+written for, since comments in <linux/pagemap.h> indicate that
+PAGE_CACHE_SIZE may grow in future (if I interpret the comment
+correctly).
+
+Currently, mkcramfs #define's PAGE_CACHE_SIZE as 4096 and uses that
+for blksize, whereas Linux-2.3.39 uses its PAGE_CACHE_SIZE, which in
+turn is defined as PAGE_SIZE (which can be as large as 32KB on arm).
+This discrepancy is a bug, though it's not clear which should be
+changed.
+
+One option is to change mkcramfs to take its PAGE_CACHE_SIZE from
+<asm/page.h>. Personally I don't like this option, but it does
+require the least amount of change: just change `#define
+PAGE_CACHE_SIZE (4096)' to `#include <asm/page.h>'. The disadvantage
+is that the generated cramfs cannot always be shared between different
+kernels, not even necessarily kernels of the same architecture if
+PAGE_CACHE_SIZE is subject to change between kernel versions
+(currently possible with arm and ia64).
+
+The remaining options try to make cramfs more sharable.
+
+One part of that is addressing endianness. The two options here are
+`always use little-endian' (like ext2fs) or `writer chooses
+endianness; kernel adapts at runtime'. Little-endian wins because of
+code simplicity and little CPU overhead even on big-endian machines.
+
+The cost of swabbing is changing the code to use the le32_to_cpu
+etc. macros as used by ext2fs. We don't need to swab the compressed
+data, only the superblock, inodes and block pointers.
+
+
+The other part of making cramfs more sharable is choosing a block
+size. The options are:
+
+ 1. Always 4096 bytes.
+
+ 2. Writer chooses blocksize; kernel adapts but rejects blocksize >
+ PAGE_CACHE_SIZE.
+
+ 3. Writer chooses blocksize; kernel adapts even to blocksize >
+ PAGE_CACHE_SIZE.
+
+It's easy enough to change the kernel to use a smaller value than
+PAGE_CACHE_SIZE: just make cramfs_readpage read multiple blocks.
+
+The cost of option 1 is that kernels with a larger PAGE_CACHE_SIZE
+value don't get as good compression as they can.
+
+The cost of option 2 relative to option 1 is that the code uses
+variables instead of #define'd constants. The gain is that people
+with kernels having larger PAGE_CACHE_SIZE can make use of that if
+they don't mind their cramfs being inaccessible to kernels with
+smaller PAGE_CACHE_SIZE values.
+
+Option 3 is easy to implement if we don't mind being CPU-inefficient:
+e.g. get readpage to decompress to a buffer of size MAX_BLKSIZE (which
+must be no larger than 32KB) and discard what it doesn't need.
+Getting readpage to read into all the covered pages is harder.
+
+The main advantage of option 3 over 1, 2, is better compression. The
+cost is greater complexity. Probably not worth it, but I hope someone
+will disagree. (If it is implemented, then I'll re-use that code in
+e2compr.)
+
+
+Another cost of 2 and 3 over 1 is making mkcramfs use a different
+block size, but that just means adding and parsing a -b option.
+
+
+Inode Size
+----------
+
+Given that cramfs will probably be used for CDs etc. as well as just
+silicon ROMs, it might make sense to expand the inode a little from
+its current 12 bytes. Inodes other than the root inode are followed
+by filename, so the expansion doesn't even have to be a multiple of 4
+bytes.
diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c
new file mode 100644
index 00000000000000..8fb1e70611447f
--- /dev/null
+++ b/fs/cramfs/inode.c
@@ -0,0 +1,465 @@
+/*
+ * Compressed rom filesystem for Linux.
+ *
+ * Copyright (C) 1999 Linus Torvalds.
+ *
+ * This file is released under the GPL.
+ */
+
+/*
+ * These are the VFS interfaces to the compressed rom filesystem.
+ * The actual compression is based on zlib, see the other files.
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/pagemap.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/blkdev.h>
+#include <linux/cramfs_fs.h>
+#include <asm/semaphore.h>
+
+#include <asm/uaccess.h>
+
+#define CRAMFS_SB_MAGIC u.cramfs_sb.magic
+#define CRAMFS_SB_SIZE u.cramfs_sb.size
+#define CRAMFS_SB_BLOCKS u.cramfs_sb.blocks
+#define CRAMFS_SB_FILES u.cramfs_sb.files
+#define CRAMFS_SB_FLAGS u.cramfs_sb.flags
+
+static struct super_operations cramfs_ops;
+static struct inode_operations cramfs_dir_inode_operations;
+static struct file_operations cramfs_directory_operations;
+static struct address_space_operations cramfs_aops;
+
+static DECLARE_MUTEX(read_mutex);
+
+
+/* These two macros may change in future, to provide better st_ino
+ semantics. */
+#define CRAMINO(x) ((x)->offset?(x)->offset<<2:1)
+#define OFFSET(x) ((x)->i_ino)
+
+static struct inode *get_cramfs_inode(struct super_block *sb, struct cramfs_inode * cramfs_inode)
+{
+ struct inode * inode = new_inode(sb);
+
+ if (inode) {
+ inode->i_mode = cramfs_inode->mode;
+ inode->i_uid = cramfs_inode->uid;
+ inode->i_size = cramfs_inode->size;
+ inode->i_blocks = (cramfs_inode->size - 1) / 512 + 1;
+ inode->i_blksize = PAGE_CACHE_SIZE;
+ inode->i_gid = cramfs_inode->gid;
+ inode->i_ino = CRAMINO(cramfs_inode);
+ /* inode->i_nlink is left 1 - arguably wrong for directories,
+ but it's the best we can do without reading the directory
+ contents. 1 yields the right result in GNU find, even
+ without -noleaf option. */
+ insert_inode_hash(inode);
+ if (S_ISREG(inode->i_mode)) {
+ inode->i_fop = &generic_ro_fops;
+ inode->i_data.a_ops = &cramfs_aops;
+ } else if (S_ISDIR(inode->i_mode)) {
+ inode->i_op = &cramfs_dir_inode_operations;
+ inode->i_fop = &cramfs_directory_operations;
+ } else if (S_ISLNK(inode->i_mode)) {
+ inode->i_op = &page_symlink_inode_operations;
+ inode->i_data.a_ops = &cramfs_aops;
+ } else {
+ inode->i_size = 0;
+ init_special_inode(inode, inode->i_mode, cramfs_inode->size);
+ }
+ }
+ return inode;
+}
+
+/*
+ * We have our own block cache: don't fill up the buffer cache
+ * with the rom-image, because the way the filesystem is set
+ * up the accesses should be fairly regular and cached in the
+ * page cache and dentry tree anyway..
+ *
+ * This also acts as a way to guarantee contiguous areas of up to
+ * BLKS_PER_BUF*PAGE_CACHE_SIZE, so that the caller doesn't need to
+ * worry about end-of-buffer issues even when decompressing a full
+ * page cache.
+ */
+#define READ_BUFFERS (2)
+/* NEXT_BUFFER(): Loop over [0..(READ_BUFFERS-1)]. */
+#define NEXT_BUFFER(_ix) ((_ix) ^ 1)
+
+/*
+ * BLKS_PER_BUF_SHIFT should be at least 2 to allow for "compressed"
+ * data that takes up more space than the original and with unlucky
+ * alignment.
+ */
+#define BLKS_PER_BUF_SHIFT (2)
+#define BLKS_PER_BUF (1 << BLKS_PER_BUF_SHIFT)
+#define BUFFER_SIZE (BLKS_PER_BUF*PAGE_CACHE_SIZE)
+
+static unsigned char read_buffers[READ_BUFFERS][BUFFER_SIZE];
+static unsigned buffer_blocknr[READ_BUFFERS];
+static struct super_block * buffer_dev[READ_BUFFERS];
+static int next_buffer;
+
+/*
+ * Returns a pointer to a buffer containing at least LEN bytes of
+ * filesystem starting at byte offset OFFSET into the filesystem.
+ */
+static void *cramfs_read(struct super_block *sb, unsigned int offset, unsigned int len)
+{
+ struct address_space *mapping = sb->s_bdev->bd_inode->i_mapping;
+ struct page *pages[BLKS_PER_BUF];
+ unsigned i, blocknr, buffer, unread;
+ unsigned long devsize;
+ int major, minor;
+
+ char *data;
+
+ if (!len)
+ return NULL;
+ blocknr = offset >> PAGE_CACHE_SHIFT;
+ offset &= PAGE_CACHE_SIZE - 1;
+
+ /* Check if an existing buffer already has the data.. */
+ for (i = 0; i < READ_BUFFERS; i++) {
+ unsigned int blk_offset;
+
+ if (buffer_dev[i] != sb)
+ continue;
+ if (blocknr < buffer_blocknr[i])
+ continue;
+ blk_offset = (blocknr - buffer_blocknr[i]) << PAGE_CACHE_SHIFT;
+ blk_offset += offset;
+ if (blk_offset + len > BUFFER_SIZE)
+ continue;
+ return read_buffers[i] + blk_offset;
+ }
+
+ devsize = mapping->host->i_size >> PAGE_CACHE_SHIFT;
+ major = MAJOR(sb->s_dev);
+ minor = MINOR(sb->s_dev);
+
+ if (blk_size[major])
+ devsize = blk_size[major][minor] >> 2;
+
+ /* Ok, read in BLKS_PER_BUF pages completely first. */
+ unread = 0;
+ for (i = 0; i < BLKS_PER_BUF; i++) {
+ struct page *page = NULL;
+
+ if (blocknr + i < devsize) {
+ page = read_cache_page(mapping, blocknr + i,
+ (filler_t *)mapping->a_ops->readpage,
+ NULL);
+ /* synchronous error? */
+ if (IS_ERR(page))
+ page = NULL;
+ }
+ pages[i] = page;
+ }
+
+ for (i = 0; i < BLKS_PER_BUF; i++) {
+ struct page *page = pages[i];
+ if (page) {
+ wait_on_page(page);
+ if (!Page_Uptodate(page)) {
+ /* asynchronous error */
+ page_cache_release(page);
+ pages[i] = NULL;
+ }
+ }
+ }
+
+ buffer = next_buffer;
+ next_buffer = NEXT_BUFFER(buffer);
+ buffer_blocknr[buffer] = blocknr;
+ buffer_dev[buffer] = sb;
+
+ data = read_buffers[buffer];
+ for (i = 0; i < BLKS_PER_BUF; i++) {
+ struct page *page = pages[i];
+ if (page) {
+ memcpy(data, kmap(page), PAGE_CACHE_SIZE);
+ kunmap(page);
+ page_cache_release(page);
+ } else
+ memset(data, 0, PAGE_CACHE_SIZE);
+ data += PAGE_CACHE_SIZE;
+ }
+ return read_buffers[buffer] + offset;
+}
+
+
+static struct super_block * cramfs_read_super(struct super_block *sb, void *data, int silent)
+{
+ int i;
+ struct cramfs_super super;
+ unsigned long root_offset;
+ struct super_block * retval = NULL;
+
+ /* Invalidate the read buffers on mount: think disk change.. */
+ for (i = 0; i < READ_BUFFERS; i++)
+ buffer_blocknr[i] = -1;
+
+ down(&read_mutex);
+ /* Read the first block and get the superblock from it */
+ memcpy(&super, cramfs_read(sb, 0, sizeof(super)), sizeof(super));
+ up(&read_mutex);
+
+ /* Do sanity checks on the superblock */
+ if (super.magic != CRAMFS_MAGIC) {
+ /* check at 512 byte offset */
+ memcpy(&super, cramfs_read(sb, 512, sizeof(super)), sizeof(super));
+ if (super.magic != CRAMFS_MAGIC) {
+ printk(KERN_ERR "cramfs: wrong magic\n");
+ goto out;
+ }
+ }
+
+ /* get feature flags first */
+ if (super.flags & ~CRAMFS_SUPPORTED_FLAGS) {
+ printk(KERN_ERR "cramfs: unsupported filesystem features\n");
+ goto out;
+ }
+
+ /* Check that the root inode is in a sane state */
+ if (!S_ISDIR(super.root.mode)) {
+ printk(KERN_ERR "cramfs: root is not a directory\n");
+ goto out;
+ }
+ root_offset = super.root.offset << 2;
+ if (super.flags & CRAMFS_FLAG_FSID_VERSION_2) {
+ sb->CRAMFS_SB_SIZE=super.size;
+ sb->CRAMFS_SB_BLOCKS=super.fsid.blocks;
+ sb->CRAMFS_SB_FILES=super.fsid.files;
+ } else {
+ sb->CRAMFS_SB_SIZE=1<<28;
+ sb->CRAMFS_SB_BLOCKS=0;
+ sb->CRAMFS_SB_FILES=0;
+ }
+ sb->CRAMFS_SB_MAGIC=super.magic;
+ sb->CRAMFS_SB_FLAGS=super.flags;
+ if (root_offset == 0)
+ printk(KERN_INFO "cramfs: empty filesystem");
+ else if (!(super.flags & CRAMFS_FLAG_SHIFTED_ROOT_OFFSET) &&
+ ((root_offset != sizeof(struct cramfs_super)) &&
+ (root_offset != 512 + sizeof(struct cramfs_super))))
+ {
+ printk(KERN_ERR "cramfs: bad root offset %lu\n", root_offset);
+ goto out;
+ }
+
+ /* Set it all up.. */
+ sb->s_op = &cramfs_ops;
+ sb->s_root = d_alloc_root(get_cramfs_inode(sb, &super.root));
+ retval = sb;
+out:
+ return retval;
+}
+
+static int cramfs_statfs(struct super_block *sb, struct statfs *buf)
+{
+ buf->f_type = CRAMFS_MAGIC;
+ buf->f_bsize = PAGE_CACHE_SIZE;
+ buf->f_blocks = sb->CRAMFS_SB_BLOCKS;
+ buf->f_bfree = 0;
+ buf->f_bavail = 0;
+ buf->f_files = sb->CRAMFS_SB_FILES;
+ buf->f_ffree = 0;
+ buf->f_namelen = CRAMFS_MAXPATHLEN;
+ return 0;
+}
+
+/*
+ * Read a cramfs directory entry.
+ */
+static int cramfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ struct super_block *sb = inode->i_sb;
+ unsigned int offset;
+ int copied;
+
+ /* Offset within the thing. */
+ offset = filp->f_pos;
+ if (offset >= inode->i_size)
+ return 0;
+ /* Directory entries are always 4-byte aligned */
+ if (offset & 3)
+ return -EINVAL;
+
+ copied = 0;
+ while (offset < inode->i_size) {
+ struct cramfs_inode *de;
+ unsigned long nextoffset;
+ char *name;
+ int namelen, error;
+
+ down(&read_mutex);
+ de = cramfs_read(sb, OFFSET(inode) + offset, sizeof(*de)+256);
+ up(&read_mutex);
+ name = (char *)(de+1);
+
+ /*
+ * Namelengths on disk are shifted by two
+ * and the name padded out to 4-byte boundaries
+ * with zeroes.
+ */
+ namelen = de->namelen << 2;
+ nextoffset = offset + sizeof(*de) + namelen;
+ for (;;) {
+ if (!namelen)
+ return -EIO;
+ if (name[namelen-1])
+ break;
+ namelen--;
+ }
+ error = filldir(dirent, name, namelen, offset, CRAMINO(de), de->mode >> 12);
+ if (error)
+ break;
+
+ offset = nextoffset;
+ filp->f_pos = offset;
+ copied++;
+ }
+ return 0;
+}
+
+/*
+ * Lookup and fill in the inode data..
+ */
+static struct dentry * cramfs_lookup(struct inode *dir, struct dentry *dentry)
+{
+ unsigned int offset = 0;
+ int sorted = dir->i_sb->CRAMFS_SB_FLAGS & CRAMFS_FLAG_SORTED_DIRS;
+
+ while (offset < dir->i_size) {
+ struct cramfs_inode *de;
+ char *name;
+ int namelen, retval;
+
+ down(&read_mutex);
+ de = cramfs_read(dir->i_sb, OFFSET(dir) + offset, sizeof(*de)+256);
+ up(&read_mutex);
+ name = (char *)(de+1);
+
+ /* Try to take advantage of sorted directories */
+ if (sorted && (dentry->d_name.name[0] < name[0]))
+ break;
+
+ namelen = de->namelen << 2;
+ offset += sizeof(*de) + namelen;
+
+ /* Quick check that the name is roughly the right length */
+ if (((dentry->d_name.len + 3) & ~3) != namelen)
+ continue;
+
+ for (;;) {
+ if (!namelen)
+ return ERR_PTR(-EIO);
+ if (name[namelen-1])
+ break;
+ namelen--;
+ }
+ if (namelen != dentry->d_name.len)
+ continue;
+ retval = memcmp(dentry->d_name.name, name, namelen);
+ if (retval > 0)
+ continue;
+ if (!retval) {
+ d_add(dentry, get_cramfs_inode(dir->i_sb, de));
+ return NULL;
+ }
+ /* else (retval < 0) */
+ if (sorted)
+ break;
+ }
+ d_add(dentry, NULL);
+ return NULL;
+}
+
+static int cramfs_readpage(struct file *file, struct page * page)
+{
+ struct inode *inode = page->mapping->host;
+ u32 maxblock, bytes_filled;
+ void *pgdata;
+
+ maxblock = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+ bytes_filled = 0;
+ if (page->index < maxblock) {
+ struct super_block *sb = inode->i_sb;
+ u32 blkptr_offset = OFFSET(inode) + page->index*4;
+ u32 start_offset, compr_len;
+
+ start_offset = OFFSET(inode) + maxblock*4;
+ down(&read_mutex);
+ if (page->index)
+ start_offset = *(u32 *) cramfs_read(sb, blkptr_offset-4, 4);
+ compr_len = (*(u32 *) cramfs_read(sb, blkptr_offset, 4) - start_offset);
+ up(&read_mutex);
+ pgdata = kmap(page);
+ if (compr_len == 0)
+ ; /* hole */
+ else {
+ down(&read_mutex);
+ bytes_filled = cramfs_uncompress_block(pgdata,
+ PAGE_CACHE_SIZE,
+ cramfs_read(sb, start_offset, compr_len),
+ compr_len);
+ up(&read_mutex);
+ }
+ } else
+ pgdata = kmap(page);
+ memset(pgdata + bytes_filled, 0, PAGE_CACHE_SIZE - bytes_filled);
+ kunmap(page);
+ flush_dcache_page(page);
+ SetPageUptodate(page);
+ UnlockPage(page);
+ return 0;
+}
+
+static struct address_space_operations cramfs_aops = {
+ readpage: cramfs_readpage
+};
+
+/*
+ * Our operations:
+ */
+
+/*
+ * A directory can only readdir
+ */
+static struct file_operations cramfs_directory_operations = {
+ read: generic_read_dir,
+ readdir: cramfs_readdir,
+};
+
+static struct inode_operations cramfs_dir_inode_operations = {
+ lookup: cramfs_lookup,
+};
+
+static struct super_operations cramfs_ops = {
+ statfs: cramfs_statfs,
+};
+
+static DECLARE_FSTYPE_DEV(cramfs_fs_type, "cramfs", cramfs_read_super);
+
+static int __init init_cramfs_fs(void)
+{
+ cramfs_uncompress_init();
+ return register_filesystem(&cramfs_fs_type);
+}
+
+static void __exit exit_cramfs_fs(void)
+{
+ cramfs_uncompress_exit();
+ unregister_filesystem(&cramfs_fs_type);
+}
+
+module_init(init_cramfs_fs)
+module_exit(exit_cramfs_fs)
+MODULE_LICENSE("GPL");
diff --git a/fs/cramfs/uncompress.c b/fs/cramfs/uncompress.c
new file mode 100644
index 00000000000000..5034365b06a862
--- /dev/null
+++ b/fs/cramfs/uncompress.c
@@ -0,0 +1,77 @@
+/*
+ * uncompress.c
+ *
+ * (C) Copyright 1999 Linus Torvalds
+ *
+ * cramfs interfaces to the uncompression library. There's really just
+ * three entrypoints:
+ *
+ * - cramfs_uncompress_init() - called to initialize the thing.
+ * - cramfs_uncompress_exit() - tell me when you're done
+ * - cramfs_uncompress_block() - uncompress a block.
+ *
+ * NOTE NOTE NOTE! The uncompression is entirely single-threaded. We
+ * only have one stream, and we'll initialize it only once even if it
+ * then is used by multiple filesystems.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/vmalloc.h>
+#include <linux/zlib.h>
+
+static z_stream stream;
+static int initialized;
+
+/* Returns length of decompressed data. */
+int cramfs_uncompress_block(void *dst, int dstlen, void *src, int srclen)
+{
+ int err;
+
+ stream.next_in = src;
+ stream.avail_in = srclen;
+
+ stream.next_out = dst;
+ stream.avail_out = dstlen;
+
+ err = zlib_inflateReset(&stream);
+ if (err != Z_OK) {
+ printk("zlib_inflateReset error %d\n", err);
+ zlib_inflateEnd(&stream);
+ zlib_inflateInit(&stream);
+ }
+
+ err = zlib_inflate(&stream, Z_FINISH);
+ if (err != Z_STREAM_END)
+ goto err;
+ return stream.total_out;
+
+err:
+ printk("Error %d while decompressing!\n", err);
+ printk("%p(%d)->%p(%d)\n", src, srclen, dst, dstlen);
+ return 0;
+}
+
+int cramfs_uncompress_init(void)
+{
+ if (!initialized++) {
+ stream.workspace = vmalloc(zlib_inflate_workspacesize());
+ if ( !stream.workspace ) {
+ initialized = 0;
+ return -ENOMEM;
+ }
+ stream.next_in = NULL;
+ stream.avail_in = 0;
+ zlib_inflateInit(&stream);
+ }
+ return 0;
+}
+
+int cramfs_uncompress_exit(void)
+{
+ if (!--initialized) {
+ zlib_inflateEnd(&stream);
+ vfree(stream.workspace);
+ }
+ return 0;
+}
diff --git a/fs/dcache.c b/fs/dcache.c
new file mode 100644
index 00000000000000..d120eb523ef7b3
--- /dev/null
+++ b/fs/dcache.c
@@ -0,0 +1,1306 @@
+/*
+ * fs/dcache.c
+ *
+ * Complete reimplementation
+ * (C) 1997 Thomas Schoebel-Theuer,
+ * with heavy changes by Linus Torvalds
+ */
+
+/*
+ * Notes on the allocation strategy:
+ *
+ * The dcache is a master of the icache - whenever a dcache entry
+ * exists, the inode will always exist. "iput()" is done either when
+ * the dcache entry is deleted or garbage collected.
+ */
+
+#include <linux/config.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+#include <linux/cache.h>
+#include <linux/module.h>
+
+#include <asm/uaccess.h>
+
+#define DCACHE_PARANOIA 1
+/* #define DCACHE_DEBUG 1 */
+
+spinlock_t dcache_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
+
+/* Right now the dcache depends on the kernel lock */
+#define check_lock() if (!kernel_locked()) BUG()
+
+static kmem_cache_t *dentry_cache;
+
+/*
+ * This is the single most critical data structure when it comes
+ * to the dcache: the hashtable for lookups. Somebody should try
+ * to make this good - I've just made it work.
+ *
+ * This hash-function tries to avoid losing too many bits of hash
+ * information, yet avoid using a prime hash-size or similar.
+ */
+#define D_HASHBITS d_hash_shift
+#define D_HASHMASK d_hash_mask
+
+static unsigned int d_hash_mask;
+static unsigned int d_hash_shift;
+static struct list_head *dentry_hashtable;
+static LIST_HEAD(dentry_unused);
+
+/* Statistics gathering. */
+struct dentry_stat_t dentry_stat = {0, 0, 45, 0,};
+
+/*
+ * no dcache_lock, please. The caller must decrement dentry_stat.nr_dentry
+ * inside dcache_lock.
+ */
+static inline void d_free(struct dentry *dentry)
+{
+ if (dentry->d_op && dentry->d_op->d_release)
+ dentry->d_op->d_release(dentry);
+ if (dname_external(dentry))
+ kfree(dentry->d_name.name);
+ kmem_cache_free(dentry_cache, dentry);
+}
+
+/*
+ * Release the dentry's inode, using the filesystem
+ * d_iput() operation if defined.
+ * Called with dcache_lock held, drops it.
+ */
+static inline void dentry_iput(struct dentry * dentry)
+{
+ struct inode *inode = dentry->d_inode;
+ if (inode) {
+ dentry->d_inode = NULL;
+ list_del_init(&dentry->d_alias);
+ spin_unlock(&dcache_lock);
+ if (dentry->d_op && dentry->d_op->d_iput)
+ dentry->d_op->d_iput(dentry, inode);
+ else
+ iput(inode);
+ } else
+ spin_unlock(&dcache_lock);
+}
+
+/*
+ * This is dput
+ *
+ * This is complicated by the fact that we do not want to put
+ * dentries that are no longer on any hash chain on the unused
+ * list: we'd much rather just get rid of them immediately.
+ *
+ * However, that implies that we have to traverse the dentry
+ * tree upwards to the parents which might _also_ now be
+ * scheduled for deletion (it may have been only waiting for
+ * its last child to go away).
+ *
+ * This tail recursion is done by hand as we don't want to depend
+ * on the compiler to always get this right (gcc generally doesn't).
+ * Real recursion would eat up our stack space.
+ */
+
+/*
+ * dput - release a dentry
+ * @dentry: dentry to release
+ *
+ * Release a dentry. This will drop the usage count and if appropriate
+ * call the dentry unlink method as well as removing it from the queues and
+ * releasing its resources. If the parent dentries were scheduled for release
+ * they too may now get deleted.
+ *
+ * no dcache lock, please.
+ */
+
+void dput(struct dentry *dentry)
+{
+ if (!dentry)
+ return;
+
+repeat:
+ if (!atomic_dec_and_lock(&dentry->d_count, &dcache_lock))
+ return;
+
+ /* dput on a free dentry? */
+ if (!list_empty(&dentry->d_lru))
+ BUG();
+ /*
+ * AV: ->d_delete() is _NOT_ allowed to block now.
+ */
+ if (dentry->d_op && dentry->d_op->d_delete) {
+ if (dentry->d_op->d_delete(dentry))
+ goto unhash_it;
+ }
+ /* Unreachable? Get rid of it */
+ if (list_empty(&dentry->d_hash))
+ goto kill_it;
+ list_add(&dentry->d_lru, &dentry_unused);
+ dentry_stat.nr_unused++;
+ spin_unlock(&dcache_lock);
+ return;
+
+unhash_it:
+ list_del_init(&dentry->d_hash);
+
+kill_it: {
+ struct dentry *parent;
+ list_del(&dentry->d_child);
+ dentry_stat.nr_dentry--; /* For d_free, below */
+ /* drops the lock, at that point nobody can reach this dentry */
+ dentry_iput(dentry);
+ parent = dentry->d_parent;
+ d_free(dentry);
+ if (dentry == parent)
+ return;
+ dentry = parent;
+ goto repeat;
+ }
+}
+
+/**
+ * d_invalidate - invalidate a dentry
+ * @dentry: dentry to invalidate
+ *
+ * Try to invalidate the dentry if it turns out to be
+ * possible. If there are other dentries that can be
+ * reached through this one we can't delete it and we
+ * return -EBUSY. On success we return 0.
+ *
+ * no dcache lock.
+ */
+
+int d_invalidate(struct dentry * dentry)
+{
+ /*
+ * If it's already been dropped, return OK.
+ */
+ spin_lock(&dcache_lock);
+ if (list_empty(&dentry->d_hash)) {
+ spin_unlock(&dcache_lock);
+ return 0;
+ }
+ /*
+ * Check whether to do a partial shrink_dcache
+ * to get rid of unused child entries.
+ */
+ if (!list_empty(&dentry->d_subdirs)) {
+ spin_unlock(&dcache_lock);
+ shrink_dcache_parent(dentry);
+ spin_lock(&dcache_lock);
+ }
+
+ /*
+ * Somebody else still using it?
+ *
+ * If it's a directory, we can't drop it
+ * for fear of somebody re-populating it
+ * with children (even though dropping it
+ * would make it unreachable from the root,
+ * we might still populate it if it was a
+ * working directory or similar).
+ */
+ if (atomic_read(&dentry->d_count) > 1) {
+ if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) {
+ spin_unlock(&dcache_lock);
+ return -EBUSY;
+ }
+ }
+
+ list_del_init(&dentry->d_hash);
+ spin_unlock(&dcache_lock);
+ return 0;
+}
+
+/* This should be called _only_ with dcache_lock held */
+
+static inline struct dentry * __dget_locked(struct dentry *dentry)
+{
+ atomic_inc(&dentry->d_count);
+ if (atomic_read(&dentry->d_count) == 1) {
+ dentry_stat.nr_unused--;
+ list_del_init(&dentry->d_lru);
+ }
+ return dentry;
+}
+
+struct dentry * dget_locked(struct dentry *dentry)
+{
+ return __dget_locked(dentry);
+}
+
+/**
+ * d_find_alias - grab a hashed alias of inode
+ * @inode: inode in question
+ *
+ * If inode has a hashed alias - acquire the reference to alias and
+ * return it. Otherwise return NULL. Notice that if inode is a directory
+ * there can be only one alias and it can be unhashed only if it has
+ * no children.
+ */
+
+struct dentry * d_find_alias(struct inode *inode)
+{
+ struct list_head *head, *next, *tmp;
+ struct dentry *alias;
+
+ spin_lock(&dcache_lock);
+ head = &inode->i_dentry;
+ next = inode->i_dentry.next;
+ while (next != head) {
+ tmp = next;
+ next = tmp->next;
+ alias = list_entry(tmp, struct dentry, d_alias);
+ if (!list_empty(&alias->d_hash)) {
+ __dget_locked(alias);
+ spin_unlock(&dcache_lock);
+ return alias;
+ }
+ }
+ spin_unlock(&dcache_lock);
+ return NULL;
+}
+
+/*
+ * Try to kill dentries associated with this inode.
+ * WARNING: you must own a reference to inode.
+ */
+void d_prune_aliases(struct inode *inode)
+{
+ struct list_head *tmp, *head = &inode->i_dentry;
+restart:
+ spin_lock(&dcache_lock);
+ tmp = head;
+ while ((tmp = tmp->next) != head) {
+ struct dentry *dentry = list_entry(tmp, struct dentry, d_alias);
+ if (!atomic_read(&dentry->d_count)) {
+ __dget_locked(dentry);
+ spin_unlock(&dcache_lock);
+ d_drop(dentry);
+ dput(dentry);
+ goto restart;
+ }
+ }
+ spin_unlock(&dcache_lock);
+}
+
+/*
+ * Throw away a dentry - free the inode, dput the parent.
+ * This requires that the LRU list has already been
+ * removed.
+ * Called with dcache_lock, drops it and then regains.
+ */
+static inline void prune_one_dentry(struct dentry * dentry)
+{
+ struct dentry * parent;
+
+ list_del_init(&dentry->d_hash);
+ list_del(&dentry->d_child);
+ dentry_stat.nr_dentry--; /* For d_free, below */
+ dentry_iput(dentry);
+ parent = dentry->d_parent;
+ d_free(dentry);
+ if (parent != dentry)
+ dput(parent);
+ spin_lock(&dcache_lock);
+}
+
+/**
+ * prune_dcache - shrink the dcache
+ * @count: number of entries to try and free
+ *
+ * Shrink the dcache. This is done when we need
+ * more memory, or simply when we need to unmount
+ * something (at which point we need to unuse
+ * all dentries).
+ *
+ * This function may fail to free any resources if
+ * all the dentries are in use.
+ */
+
+void prune_dcache(int count)
+{
+ spin_lock(&dcache_lock);
+ for (;;) {
+ struct dentry *dentry;
+ struct list_head *tmp;
+
+ tmp = dentry_unused.prev;
+
+ if (tmp == &dentry_unused)
+ break;
+ list_del_init(tmp);
+ dentry = list_entry(tmp, struct dentry, d_lru);
+
+ /* If the dentry was recently referenced, don't free it. */
+ if (dentry->d_vfs_flags & DCACHE_REFERENCED) {
+ dentry->d_vfs_flags &= ~DCACHE_REFERENCED;
+ list_add(&dentry->d_lru, &dentry_unused);
+ continue;
+ }
+ dentry_stat.nr_unused--;
+
+ /* Unused dentry with a count? */
+ if (atomic_read(&dentry->d_count))
+ BUG();
+
+ prune_one_dentry(dentry);
+ if (!--count)
+ break;
+ }
+ spin_unlock(&dcache_lock);
+}
+
+/*
+ * Shrink the dcache for the specified super block.
+ * This allows us to unmount a device without disturbing
+ * the dcache for the other devices.
+ *
+ * This implementation makes just two traversals of the
+ * unused list. On the first pass we move the selected
+ * dentries to the most recent end, and on the second
+ * pass we free them. The second pass must restart after
+ * each dput(), but since the target dentries are all at
+ * the end, it's really just a single traversal.
+ */
+
+/**
+ * shrink_dcache_sb - shrink dcache for a superblock
+ * @sb: superblock
+ *
+ * Shrink the dcache for the specified super block. This
+ * is used to free the dcache before unmounting a file
+ * system
+ */
+
+void shrink_dcache_sb(struct super_block * sb)
+{
+ struct list_head *tmp, *next;
+ struct dentry *dentry;
+
+ /*
+ * Pass one ... move the dentries for the specified
+ * superblock to the most recent end of the unused list.
+ */
+ spin_lock(&dcache_lock);
+ next = dentry_unused.next;
+ while (next != &dentry_unused) {
+ tmp = next;
+ next = tmp->next;
+ dentry = list_entry(tmp, struct dentry, d_lru);
+ if (dentry->d_sb != sb)
+ continue;
+ list_del(tmp);
+ list_add(tmp, &dentry_unused);
+ }
+
+ /*
+ * Pass two ... free the dentries for this superblock.
+ */
+repeat:
+ next = dentry_unused.next;
+ while (next != &dentry_unused) {
+ tmp = next;
+ next = tmp->next;
+ dentry = list_entry(tmp, struct dentry, d_lru);
+ if (dentry->d_sb != sb)
+ continue;
+ if (atomic_read(&dentry->d_count))
+ continue;
+ dentry_stat.nr_unused--;
+ list_del_init(tmp);
+ prune_one_dentry(dentry);
+ goto repeat;
+ }
+ spin_unlock(&dcache_lock);
+}
+
+/*
+ * Search for at least 1 mount point in the dentry's subdirs.
+ * We descend to the next level whenever the d_subdirs
+ * list is non-empty and continue searching.
+ */
+
+/**
+ * have_submounts - check for mounts over a dentry
+ * @parent: dentry to check.
+ *
+ * Return true if the parent or its subdirectories contain
+ * a mount point
+ */
+
+int have_submounts(struct dentry *parent)
+{
+ struct dentry *this_parent = parent;
+ struct list_head *next;
+
+ spin_lock(&dcache_lock);
+ if (d_mountpoint(parent))
+ goto positive;
+repeat:
+ next = this_parent->d_subdirs.next;
+resume:
+ while (next != &this_parent->d_subdirs) {
+ struct list_head *tmp = next;
+ struct dentry *dentry = list_entry(tmp, struct dentry, d_child);
+ next = tmp->next;
+ /* Have we found a mount point ? */
+ if (d_mountpoint(dentry))
+ goto positive;
+ if (!list_empty(&dentry->d_subdirs)) {
+ this_parent = dentry;
+ goto repeat;
+ }
+ }
+ /*
+ * All done at this level ... ascend and resume the search.
+ */
+ if (this_parent != parent) {
+ next = this_parent->d_child.next;
+ this_parent = this_parent->d_parent;
+ goto resume;
+ }
+ spin_unlock(&dcache_lock);
+ return 0; /* No mount points found in tree */
+positive:
+ spin_unlock(&dcache_lock);
+ return 1;
+}
+
+/*
+ * Search the dentry child list for the specified parent,
+ * and move any unused dentries to the end of the unused
+ * list for prune_dcache(). We descend to the next level
+ * whenever the d_subdirs list is non-empty and continue
+ * searching.
+ */
+static int select_parent(struct dentry * parent)
+{
+ struct dentry *this_parent = parent;
+ struct list_head *next;
+ int found = 0;
+
+ spin_lock(&dcache_lock);
+repeat:
+ next = this_parent->d_subdirs.next;
+resume:
+ while (next != &this_parent->d_subdirs) {
+ struct list_head *tmp = next;
+ struct dentry *dentry = list_entry(tmp, struct dentry, d_child);
+ next = tmp->next;
+ if (!atomic_read(&dentry->d_count)) {
+ list_del(&dentry->d_lru);
+ list_add(&dentry->d_lru, dentry_unused.prev);
+ found++;
+ }
+ /*
+ * Descend a level if the d_subdirs list is non-empty.
+ */
+ if (!list_empty(&dentry->d_subdirs)) {
+ this_parent = dentry;
+#ifdef DCACHE_DEBUG
+printk(KERN_DEBUG "select_parent: descending to %s/%s, found=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, found);
+#endif
+ goto repeat;
+ }
+ }
+ /*
+ * All done at this level ... ascend and resume the search.
+ */
+ if (this_parent != parent) {
+ next = this_parent->d_child.next;
+ this_parent = this_parent->d_parent;
+#ifdef DCACHE_DEBUG
+printk(KERN_DEBUG "select_parent: ascending to %s/%s, found=%d\n",
+this_parent->d_parent->d_name.name, this_parent->d_name.name, found);
+#endif
+ goto resume;
+ }
+ spin_unlock(&dcache_lock);
+ return found;
+}
+
+/**
+ * shrink_dcache_parent - prune dcache
+ * @parent: parent of entries to prune
+ *
+ * Prune the dcache to remove unused children of the parent dentry.
+ */
+
+void shrink_dcache_parent(struct dentry * parent)
+{
+ int found;
+
+ while ((found = select_parent(parent)) != 0)
+ prune_dcache(found);
+}
+
+/*
+ * This is called from kswapd when we think we need some
+ * more memory, but aren't really sure how much. So we
+ * carefully try to free a _bit_ of our dcache, but not
+ * too much.
+ *
+ * Priority:
+ * 0 - very urgent: shrink everything
+ * ...
+ * 6 - base-level: try to shrink a bit.
+ */
+int shrink_dcache_memory(int priority, unsigned int gfp_mask)
+{
+ int count = 0;
+
+ /*
+ * Nasty deadlock avoidance.
+ *
+ * ext2_new_block->getblk->GFP->shrink_dcache_memory->prune_dcache->
+ * prune_one_dentry->dput->dentry_iput->iput->inode->i_sb->s_op->
+ * put_inode->ext2_discard_prealloc->ext2_free_blocks->lock_super->
+ * DEADLOCK.
+ *
+ * We should make sure we don't hold the superblock lock over
+ * block allocations, but for now:
+ */
+ if (!(gfp_mask & __GFP_FS))
+ return 0;
+
+ count = dentry_stat.nr_unused / priority;
+
+ prune_dcache(count);
+ return kmem_cache_shrink(dentry_cache);
+}
+
+#define NAME_ALLOC_LEN(len) ((len+16) & ~15)
+
+/**
+ * d_alloc - allocate a dcache entry
+ * @parent: parent of entry to allocate
+ * @name: qstr of the name
+ *
+ * Allocates a dentry. It returns %NULL if there is insufficient memory
+ * available. On a success the dentry is returned. The name passed in is
+ * copied and the copy passed in may be reused after this call.
+ */
+
+struct dentry * d_alloc(struct dentry * parent, const struct qstr *name)
+{
+ char * str;
+ struct dentry *dentry;
+
+ dentry = kmem_cache_alloc(dentry_cache, GFP_KERNEL);
+ if (!dentry)
+ return NULL;
+
+ if (name->len > DNAME_INLINE_LEN-1) {
+ str = kmalloc(NAME_ALLOC_LEN(name->len), GFP_KERNEL);
+ if (!str) {
+ kmem_cache_free(dentry_cache, dentry);
+ return NULL;
+ }
+ } else
+ str = dentry->d_iname;
+
+ memcpy(str, name->name, name->len);
+ str[name->len] = 0;
+
+ atomic_set(&dentry->d_count, 1);
+ dentry->d_vfs_flags = 0;
+ dentry->d_flags = 0;
+ dentry->d_inode = NULL;
+ dentry->d_parent = NULL;
+ dentry->d_sb = NULL;
+ dentry->d_name.name = str;
+ dentry->d_name.len = name->len;
+ dentry->d_name.hash = name->hash;
+ dentry->d_op = NULL;
+ dentry->d_fsdata = NULL;
+ dentry->d_mounted = 0;
+ INIT_LIST_HEAD(&dentry->d_hash);
+ INIT_LIST_HEAD(&dentry->d_lru);
+ INIT_LIST_HEAD(&dentry->d_subdirs);
+ INIT_LIST_HEAD(&dentry->d_alias);
+ if (parent) {
+ dentry->d_parent = dget(parent);
+ dentry->d_sb = parent->d_sb;
+ } else
+ INIT_LIST_HEAD(&dentry->d_child);
+
+ spin_lock(&dcache_lock);
+ if (parent)
+ list_add(&dentry->d_child, &parent->d_subdirs);
+ dentry_stat.nr_dentry++;
+ spin_unlock(&dcache_lock);
+
+ return dentry;
+}
+
+/**
+ * d_instantiate - fill in inode information for a dentry
+ * @entry: dentry to complete
+ * @inode: inode to attach to this dentry
+ *
+ * Fill in inode information in the entry.
+ *
+ * This turns negative dentries into productive full members
+ * of society.
+ *
+ * NOTE! This assumes that the inode count has been incremented
+ * (or otherwise set) by the caller to indicate that it is now
+ * in use by the dcache.
+ */
+
+void d_instantiate(struct dentry *entry, struct inode * inode)
+{
+ if (!list_empty(&entry->d_alias)) BUG();
+ spin_lock(&dcache_lock);
+ if (inode)
+ list_add(&entry->d_alias, &inode->i_dentry);
+ entry->d_inode = inode;
+ spin_unlock(&dcache_lock);
+}
+
+/**
+ * d_alloc_root - allocate root dentry
+ * @root_inode: inode to allocate the root for
+ *
+ * Allocate a root ("/") dentry for the inode given. The inode is
+ * instantiated and returned. %NULL is returned if there is insufficient
+ * memory or the inode passed is %NULL.
+ */
+
+struct dentry * d_alloc_root(struct inode * root_inode)
+{
+ struct dentry *res = NULL;
+
+ if (root_inode) {
+ res = d_alloc(NULL, &(const struct qstr) { "/", 1, 0 });
+ if (res) {
+ res->d_sb = root_inode->i_sb;
+ res->d_parent = res;
+ d_instantiate(res, root_inode);
+ }
+ }
+ return res;
+}
+
+static inline struct list_head * d_hash(struct dentry * parent, unsigned long hash)
+{
+ hash += (unsigned long) parent / L1_CACHE_BYTES;
+ hash = hash ^ (hash >> D_HASHBITS);
+ return dentry_hashtable + (hash & D_HASHMASK);
+}
+
+/**
+ * d_lookup - search for a dentry
+ * @parent: parent dentry
+ * @name: qstr of name we wish to find
+ *
+ * Searches the children of the parent dentry for the name in question. If
+ * the dentry is found its reference count is incremented and the dentry
+ * is returned. The caller must use d_put to free the entry when it has
+ * finished using it. %NULL is returned on failure.
+ */
+
+struct dentry * d_lookup(struct dentry * parent, struct qstr * name)
+{
+ unsigned int len = name->len;
+ unsigned int hash = name->hash;
+ const unsigned char *str = name->name;
+ struct list_head *head = d_hash(parent,hash);
+ struct list_head *tmp;
+
+ spin_lock(&dcache_lock);
+ tmp = head->next;
+ for (;;) {
+ struct dentry * dentry = list_entry(tmp, struct dentry, d_hash);
+ if (tmp == head)
+ break;
+ tmp = tmp->next;
+ if (dentry->d_name.hash != hash)
+ continue;
+ if (dentry->d_parent != parent)
+ continue;
+ if (parent->d_op && parent->d_op->d_compare) {
+ if (parent->d_op->d_compare(parent, &dentry->d_name, name))
+ continue;
+ } else {
+ if (dentry->d_name.len != len)
+ continue;
+ if (memcmp(dentry->d_name.name, str, len))
+ continue;
+ }
+ __dget_locked(dentry);
+ dentry->d_vfs_flags |= DCACHE_REFERENCED;
+ spin_unlock(&dcache_lock);
+ return dentry;
+ }
+ spin_unlock(&dcache_lock);
+ return NULL;
+}
+
+/**
+ * d_validate - verify dentry provided from insecure source
+ * @dentry: The dentry alleged to be valid child of @dparent
+ * @dparent: The parent dentry (known to be valid)
+ * @hash: Hash of the dentry
+ * @len: Length of the name
+ *
+ * An insecure source has sent us a dentry, here we verify it and dget() it.
+ * This is used by ncpfs in its readdir implementation.
+ * Zero is returned in the dentry is invalid.
+ */
+
+int d_validate(struct dentry *dentry, struct dentry *dparent)
+{
+ unsigned long dent_addr = (unsigned long) dentry;
+ unsigned long min_addr = PAGE_OFFSET;
+ unsigned long align_mask = 0x0F;
+ struct list_head *base, *lhp;
+
+ if (dent_addr < min_addr)
+ goto out;
+ if (dent_addr > (unsigned long)high_memory - sizeof(struct dentry))
+ goto out;
+ if (dent_addr & align_mask)
+ goto out;
+ if ((!kern_addr_valid(dent_addr)) || (!kern_addr_valid(dent_addr -1 +
+ sizeof(struct dentry))))
+ goto out;
+
+ if (dentry->d_parent != dparent)
+ goto out;
+
+ spin_lock(&dcache_lock);
+ lhp = base = d_hash(dparent, dentry->d_name.hash);
+ while ((lhp = lhp->next) != base) {
+ if (dentry == list_entry(lhp, struct dentry, d_hash)) {
+ __dget_locked(dentry);
+ spin_unlock(&dcache_lock);
+ return 1;
+ }
+ }
+ spin_unlock(&dcache_lock);
+out:
+ return 0;
+}
+
+/*
+ * When a file is deleted, we have two options:
+ * - turn this dentry into a negative dentry
+ * - unhash this dentry and free it.
+ *
+ * Usually, we want to just turn this into
+ * a negative dentry, but if anybody else is
+ * currently using the dentry or the inode
+ * we can't do that and we fall back on removing
+ * it from the hash queues and waiting for
+ * it to be deleted later when it has no users
+ */
+
+/**
+ * d_delete - delete a dentry
+ * @dentry: The dentry to delete
+ *
+ * Turn the dentry into a negative dentry if possible, otherwise
+ * remove it from the hash queues so it can be deleted later
+ */
+
+void d_delete(struct dentry * dentry)
+{
+ /*
+ * Are we the only user?
+ */
+ spin_lock(&dcache_lock);
+ if (atomic_read(&dentry->d_count) == 1) {
+ dentry_iput(dentry);
+ return;
+ }
+ spin_unlock(&dcache_lock);
+
+ /*
+ * If not, just drop the dentry and let dput
+ * pick up the tab..
+ */
+ d_drop(dentry);
+}
+
+/**
+ * d_rehash - add an entry back to the hash
+ * @entry: dentry to add to the hash
+ *
+ * Adds a dentry to the hash according to its name.
+ */
+
+void d_rehash(struct dentry * entry)
+{
+ struct list_head *list = d_hash(entry->d_parent, entry->d_name.hash);
+ if (!list_empty(&entry->d_hash)) BUG();
+ spin_lock(&dcache_lock);
+ list_add(&entry->d_hash, list);
+ spin_unlock(&dcache_lock);
+}
+
+#define do_switch(x,y) do { \
+ __typeof__ (x) __tmp = x; \
+ x = y; y = __tmp; } while (0)
+
+/*
+ * When switching names, the actual string doesn't strictly have to
+ * be preserved in the target - because we're dropping the target
+ * anyway. As such, we can just do a simple memcpy() to copy over
+ * the new name before we switch.
+ *
+ * Note that we have to be a lot more careful about getting the hash
+ * switched - we have to switch the hash value properly even if it
+ * then no longer matches the actual (corrupted) string of the target.
+ * The hash value has to match the hash queue that the dentry is on..
+ */
+static inline void switch_names(struct dentry * dentry, struct dentry * target)
+{
+ const unsigned char *old_name, *new_name;
+
+ check_lock();
+ memcpy(dentry->d_iname, target->d_iname, DNAME_INLINE_LEN);
+ old_name = target->d_name.name;
+ new_name = dentry->d_name.name;
+ if (old_name == target->d_iname)
+ old_name = dentry->d_iname;
+ if (new_name == dentry->d_iname)
+ new_name = target->d_iname;
+ target->d_name.name = new_name;
+ dentry->d_name.name = old_name;
+}
+
+/*
+ * We cannibalize "target" when moving dentry on top of it,
+ * because it's going to be thrown away anyway. We could be more
+ * polite about it, though.
+ *
+ * This forceful removal will result in ugly /proc output if
+ * somebody holds a file open that got deleted due to a rename.
+ * We could be nicer about the deleted file, and let it show
+ * up under the name it got deleted rather than the name that
+ * deleted it.
+ *
+ * Careful with the hash switch. The hash switch depends on
+ * the fact that any list-entry can be a head of the list.
+ * Think about it.
+ */
+
+/**
+ * d_move - move a dentry
+ * @dentry: entry to move
+ * @target: new dentry
+ *
+ * Update the dcache to reflect the move of a file name. Negative
+ * dcache entries should not be moved in this way.
+ */
+
+void d_move(struct dentry * dentry, struct dentry * target)
+{
+ check_lock();
+
+ if (!dentry->d_inode)
+ printk(KERN_WARNING "VFS: moving negative dcache entry\n");
+
+ spin_lock(&dcache_lock);
+ /* Move the dentry to the target hash queue */
+ list_del(&dentry->d_hash);
+ list_add(&dentry->d_hash, &target->d_hash);
+
+ /* Unhash the target: dput() will then get rid of it */
+ list_del_init(&target->d_hash);
+
+ list_del(&dentry->d_child);
+ list_del(&target->d_child);
+
+ /* Switch the parents and the names.. */
+ switch_names(dentry, target);
+ do_switch(dentry->d_parent, target->d_parent);
+ do_switch(dentry->d_name.len, target->d_name.len);
+ do_switch(dentry->d_name.hash, target->d_name.hash);
+
+ /* And add them back to the (new) parent lists */
+ list_add(&target->d_child, &target->d_parent->d_subdirs);
+ list_add(&dentry->d_child, &dentry->d_parent->d_subdirs);
+ spin_unlock(&dcache_lock);
+}
+
+/**
+ * d_path - return the path of a dentry
+ * @dentry: dentry to report
+ * @vfsmnt: vfsmnt to which the dentry belongs
+ * @root: root dentry
+ * @rootmnt: vfsmnt to which the root dentry belongs
+ * @buffer: buffer to return value in
+ * @buflen: buffer length
+ *
+ * Convert a dentry into an ASCII path name. If the entry has been deleted
+ * the string " (deleted)" is appended. Note that this is ambiguous. Returns
+ * the buffer.
+ *
+ * "buflen" should be %PAGE_SIZE or more. Caller holds the dcache_lock.
+ */
+char * __d_path(struct dentry *dentry, struct vfsmount *vfsmnt,
+ struct dentry *root, struct vfsmount *rootmnt,
+ char *buffer, int buflen)
+{
+ char * end = buffer+buflen;
+ char * retval;
+ int namelen;
+
+ *--end = '\0';
+ buflen--;
+ if (!IS_ROOT(dentry) && list_empty(&dentry->d_hash)) {
+ buflen -= 10;
+ end -= 10;
+ memcpy(end, " (deleted)", 10);
+ }
+
+ /* Get '/' right */
+ retval = end-1;
+ *retval = '/';
+
+ for (;;) {
+ struct dentry * parent;
+
+ if (dentry == root && vfsmnt == rootmnt)
+ break;
+ if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
+ /* Global root? */
+ if (vfsmnt->mnt_parent == vfsmnt)
+ goto global_root;
+ dentry = vfsmnt->mnt_mountpoint;
+ vfsmnt = vfsmnt->mnt_parent;
+ continue;
+ }
+ parent = dentry->d_parent;
+ namelen = dentry->d_name.len;
+ buflen -= namelen + 1;
+ if (buflen < 0)
+ return ERR_PTR(-ENAMETOOLONG);
+ end -= namelen;
+ memcpy(end, dentry->d_name.name, namelen);
+ *--end = '/';
+ retval = end;
+ dentry = parent;
+ }
+
+ return retval;
+
+global_root:
+ namelen = dentry->d_name.len;
+ buflen -= namelen;
+ if (buflen >= 0) {
+ retval -= namelen-1; /* hit the slash */
+ memcpy(retval, dentry->d_name.name, namelen);
+ } else
+ retval = ERR_PTR(-ENAMETOOLONG);
+ return retval;
+}
+
+/*
+ * NOTE! The user-level library version returns a
+ * character pointer. The kernel system call just
+ * returns the length of the buffer filled (which
+ * includes the ending '\0' character), or a negative
+ * error value. So libc would do something like
+ *
+ * char *getcwd(char * buf, size_t size)
+ * {
+ * int retval;
+ *
+ * retval = sys_getcwd(buf, size);
+ * if (retval >= 0)
+ * return buf;
+ * errno = -retval;
+ * return NULL;
+ * }
+ */
+asmlinkage long sys_getcwd(char *buf, unsigned long size)
+{
+ int error;
+ struct vfsmount *pwdmnt, *rootmnt;
+ struct dentry *pwd, *root;
+ char *page = (char *) __get_free_page(GFP_USER);
+
+ if (!page)
+ return -ENOMEM;
+
+ read_lock(&current->fs->lock);
+ pwdmnt = mntget(current->fs->pwdmnt);
+ pwd = dget(current->fs->pwd);
+ rootmnt = mntget(current->fs->rootmnt);
+ root = dget(current->fs->root);
+ read_unlock(&current->fs->lock);
+
+ error = -ENOENT;
+ /* Has the current directory has been unlinked? */
+ spin_lock(&dcache_lock);
+ if (pwd->d_parent == pwd || !list_empty(&pwd->d_hash)) {
+ unsigned long len;
+ char * cwd;
+
+ cwd = __d_path(pwd, pwdmnt, root, rootmnt, page, PAGE_SIZE);
+ spin_unlock(&dcache_lock);
+
+ error = PTR_ERR(cwd);
+ if (IS_ERR(cwd))
+ goto out;
+
+ error = -ERANGE;
+ len = PAGE_SIZE + page - cwd;
+ if (len <= size) {
+ error = len;
+ if (copy_to_user(buf, cwd, len))
+ error = -EFAULT;
+ }
+ } else
+ spin_unlock(&dcache_lock);
+
+out:
+ dput(pwd);
+ mntput(pwdmnt);
+ dput(root);
+ mntput(rootmnt);
+ free_page((unsigned long) page);
+ return error;
+}
+
+/*
+ * Test whether new_dentry is a subdirectory of old_dentry.
+ *
+ * Trivially implemented using the dcache structure
+ */
+
+/**
+ * is_subdir - is new dentry a subdirectory of old_dentry
+ * @new_dentry: new dentry
+ * @old_dentry: old dentry
+ *
+ * Returns 1 if new_dentry is a subdirectory of the parent (at any depth).
+ * Returns 0 otherwise.
+ */
+
+int is_subdir(struct dentry * new_dentry, struct dentry * old_dentry)
+{
+ int result;
+
+ result = 0;
+ for (;;) {
+ if (new_dentry != old_dentry) {
+ struct dentry * parent = new_dentry->d_parent;
+ if (parent == new_dentry)
+ break;
+ new_dentry = parent;
+ continue;
+ }
+ result = 1;
+ break;
+ }
+ return result;
+}
+
+void d_genocide(struct dentry *root)
+{
+ struct dentry *this_parent = root;
+ struct list_head *next;
+
+ spin_lock(&dcache_lock);
+repeat:
+ next = this_parent->d_subdirs.next;
+resume:
+ while (next != &this_parent->d_subdirs) {
+ struct list_head *tmp = next;
+ struct dentry *dentry = list_entry(tmp, struct dentry, d_child);
+ next = tmp->next;
+ if (d_unhashed(dentry)||!dentry->d_inode)
+ continue;
+ if (!list_empty(&dentry->d_subdirs)) {
+ this_parent = dentry;
+ goto repeat;
+ }
+ atomic_dec(&dentry->d_count);
+ }
+ if (this_parent != root) {
+ next = this_parent->d_child.next;
+ atomic_dec(&this_parent->d_count);
+ this_parent = this_parent->d_parent;
+ goto resume;
+ }
+ spin_unlock(&dcache_lock);
+}
+
+/**
+ * find_inode_number - check for dentry with name
+ * @dir: directory to check
+ * @name: Name to find.
+ *
+ * Check whether a dentry already exists for the given name,
+ * and return the inode number if it has an inode. Otherwise
+ * 0 is returned.
+ *
+ * This routine is used to post-process directory listings for
+ * filesystems using synthetic inode numbers, and is necessary
+ * to keep getcwd() working.
+ */
+
+ino_t find_inode_number(struct dentry *dir, struct qstr *name)
+{
+ struct dentry * dentry;
+ ino_t ino = 0;
+
+ /*
+ * Check for a fs-specific hash function. Note that we must
+ * calculate the standard hash first, as the d_op->d_hash()
+ * routine may choose to leave the hash value unchanged.
+ */
+ name->hash = full_name_hash(name->name, name->len);
+ if (dir->d_op && dir->d_op->d_hash)
+ {
+ if (dir->d_op->d_hash(dir, name) != 0)
+ goto out;
+ }
+
+ dentry = d_lookup(dir, name);
+ if (dentry)
+ {
+ if (dentry->d_inode)
+ ino = dentry->d_inode->i_ino;
+ dput(dentry);
+ }
+out:
+ return ino;
+}
+
+static void __init dcache_init(unsigned long mempages)
+{
+ struct list_head *d;
+ unsigned long order;
+ unsigned int nr_hash;
+ int i;
+
+ /*
+ * A constructor could be added for stable state like the lists,
+ * but it is probably not worth it because of the cache nature
+ * of the dcache.
+ * If fragmentation is too bad then the SLAB_HWCACHE_ALIGN
+ * flag could be removed here, to hint to the allocator that
+ * it should not try to get multiple page regions.
+ */
+ dentry_cache = kmem_cache_create("dentry_cache",
+ sizeof(struct dentry),
+ 0,
+ SLAB_HWCACHE_ALIGN,
+ NULL, NULL);
+ if (!dentry_cache)
+ panic("Cannot create dentry cache");
+
+#if PAGE_SHIFT < 13
+ mempages >>= (13 - PAGE_SHIFT);
+#endif
+ mempages *= sizeof(struct list_head);
+ for (order = 0; ((1UL << order) << PAGE_SHIFT) < mempages; order++)
+ ;
+
+ do {
+ unsigned long tmp;
+
+ nr_hash = (1UL << order) * PAGE_SIZE /
+ sizeof(struct list_head);
+ d_hash_mask = (nr_hash - 1);
+
+ tmp = nr_hash;
+ d_hash_shift = 0;
+ while ((tmp >>= 1UL) != 0UL)
+ d_hash_shift++;
+
+ dentry_hashtable = (struct list_head *)
+ __get_free_pages(GFP_ATOMIC, order);
+ } while (dentry_hashtable == NULL && --order >= 0);
+
+ printk(KERN_INFO "Dentry cache hash table entries: %d (order: %ld, %ld bytes)\n",
+ nr_hash, order, (PAGE_SIZE << order));
+
+ if (!dentry_hashtable)
+ panic("Failed to allocate dcache hash table\n");
+
+ d = dentry_hashtable;
+ i = nr_hash;
+ do {
+ INIT_LIST_HEAD(d);
+ d++;
+ i--;
+ } while (i);
+}
+
+static void init_buffer_head(void * foo, kmem_cache_t * cachep, unsigned long flags)
+{
+ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+ SLAB_CTOR_CONSTRUCTOR)
+ {
+ struct buffer_head * bh = (struct buffer_head *) foo;
+
+ memset(bh, 0, sizeof(*bh));
+ init_waitqueue_head(&bh->b_wait);
+ }
+}
+
+/* SLAB cache for __getname() consumers */
+kmem_cache_t *names_cachep;
+
+/* SLAB cache for file structures */
+kmem_cache_t *filp_cachep;
+
+/* SLAB cache for dquot structures */
+kmem_cache_t *dquot_cachep;
+
+/* SLAB cache for buffer_head structures */
+kmem_cache_t *bh_cachep;
+EXPORT_SYMBOL(bh_cachep);
+
+extern void bdev_cache_init(void);
+extern void cdev_cache_init(void);
+extern void iobuf_cache_init(void);
+
+void __init vfs_caches_init(unsigned long mempages)
+{
+ bh_cachep = kmem_cache_create("buffer_head",
+ sizeof(struct buffer_head), 0,
+ SLAB_HWCACHE_ALIGN, init_buffer_head, NULL);
+ if(!bh_cachep)
+ panic("Cannot create buffer head SLAB cache");
+
+ names_cachep = kmem_cache_create("names_cache",
+ PATH_MAX, 0,
+ SLAB_HWCACHE_ALIGN, NULL, NULL);
+ if (!names_cachep)
+ panic("Cannot create names SLAB cache");
+
+ filp_cachep = kmem_cache_create("filp",
+ sizeof(struct file), 0,
+ SLAB_HWCACHE_ALIGN, NULL, NULL);
+ if(!filp_cachep)
+ panic("Cannot create filp SLAB cache");
+
+#if defined (CONFIG_QUOTA)
+ dquot_cachep = kmem_cache_create("dquot",
+ sizeof(struct dquot), sizeof(unsigned long) * 4,
+ SLAB_HWCACHE_ALIGN, NULL, NULL);
+ if (!dquot_cachep)
+ panic("Cannot create dquot SLAB cache");
+#endif
+
+ dcache_init(mempages);
+ inode_init(mempages);
+ files_init(mempages);
+ mnt_init(mempages);
+ bdev_cache_init();
+ cdev_cache_init();
+ iobuf_cache_init();
+}
diff --git a/fs/devfs/Makefile b/fs/devfs/Makefile
new file mode 100644
index 00000000000000..b551ffd093b056
--- /dev/null
+++ b/fs/devfs/Makefile
@@ -0,0 +1,42 @@
+#
+# Makefile for the linux devfs-filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+O_TARGET := devfs.o
+
+export-objs := base.o util.o
+
+obj-y := base.o util.o
+
+# Special case to support building documentation
+ifndef TOPDIR
+TOPDIR = ../..
+endif
+
+include $(TOPDIR)/Rules.make
+
+
+# Rule to build documentation
+doc: base.c util.c
+ @echo '$$PACKAGE devfs' > devfs.doc
+ @echo '$$NAME Linux Kernel' >> devfs.doc
+ @echo '$$SUMMARY devfs (Device FileSystem) functions' >> devfs.doc
+ @echo '$$SYNOPSIS "#include <linux/devfs_fs.h>"' >> devfs.doc
+ @echo '$$END' >> devfs.doc
+ c2doc base.c util.c >> devfs.doc
+ karma_doc2man -section 9 devfs.doc .
+ rm devfs.doc
+ gzip --best *.9
+ mv *.9.gz /usr/man/man9
+
+
+# Rule for test compiling
+test:
+ gcc -o /tmp/base.o -D__KERNEL__ -I../../include -Wall \
+ -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe \
+ -fno-strength-reduce -DEXPORT_SYMTAB -c base.c
diff --git a/fs/devfs/base.c b/fs/devfs/base.c
new file mode 100644
index 00000000000000..8e9bd93ade9930
--- /dev/null
+++ b/fs/devfs/base.c
@@ -0,0 +1,3558 @@
+/* devfs (Device FileSystem) driver.
+
+ Copyright (C) 1998-2002 Richard Gooch
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Richard Gooch may be reached by email at rgooch@atnf.csiro.au
+ The postal address is:
+ Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia.
+
+ ChangeLog
+
+ 19980110 Richard Gooch <rgooch@atnf.csiro.au>
+ Original version.
+ v0.1
+ 19980111 Richard Gooch <rgooch@atnf.csiro.au>
+ Created per-fs inode table rather than using inode->u.generic_ip
+ v0.2
+ 19980111 Richard Gooch <rgooch@atnf.csiro.au>
+ Created .epoch inode which has a ctime of 0.
+ Fixed loss of named pipes when dentries lost.
+ Fixed loss of inode data when devfs_register() follows mknod().
+ v0.3
+ 19980111 Richard Gooch <rgooch@atnf.csiro.au>
+ Fix for when compiling with CONFIG_KERNELD.
+ 19980112 Richard Gooch <rgooch@atnf.csiro.au>
+ Fix for readdir() which sometimes didn't show entries.
+ Added <<tolerant>> option to <devfs_register>.
+ v0.4
+ 19980113 Richard Gooch <rgooch@atnf.csiro.au>
+ Created <devfs_fill_file> function.
+ v0.5
+ 19980115 Richard Gooch <rgooch@atnf.csiro.au>
+ Added subdirectory support. Major restructuring.
+ 19980116 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixed <find_by_dev> to not search major=0,minor=0.
+ Added symlink support.
+ v0.6
+ 19980120 Richard Gooch <rgooch@atnf.csiro.au>
+ Created <devfs_mk_dir> function and support directory unregister
+ 19980120 Richard Gooch <rgooch@atnf.csiro.au>
+ Auto-ownership uses real uid/gid rather than effective uid/gid.
+ v0.7
+ 19980121 Richard Gooch <rgooch@atnf.csiro.au>
+ Supported creation of sockets.
+ v0.8
+ 19980122 Richard Gooch <rgooch@atnf.csiro.au>
+ Added DEVFS_FL_HIDE_UNREG flag.
+ Interface change to <devfs_mk_symlink>.
+ Created <devfs_symlink> to support symlink(2).
+ v0.9
+ 19980123 Richard Gooch <rgooch@atnf.csiro.au>
+ Added check to <devfs_fill_file> to check inode is in devfs.
+ Added optional traversal of symlinks.
+ v0.10
+ 19980124 Richard Gooch <rgooch@atnf.csiro.au>
+ Created <devfs_get_flags> and <devfs_set_flags>.
+ v0.11
+ 19980125 C. Scott Ananian <cananian@alumni.princeton.edu>
+ Created <devfs_find_handle>.
+ 19980125 Richard Gooch <rgooch@atnf.csiro.au>
+ Allow removal of symlinks.
+ v0.12
+ 19980125 Richard Gooch <rgooch@atnf.csiro.au>
+ Created <devfs_set_symlink_destination>.
+ 19980126 Richard Gooch <rgooch@atnf.csiro.au>
+ Moved DEVFS_SUPER_MAGIC into header file.
+ Added DEVFS_FL_HIDE flag.
+ Created <devfs_get_maj_min>.
+ Created <devfs_get_handle_from_inode>.
+ Fixed minor bug in <find_by_dev>.
+ 19980127 Richard Gooch <rgooch@atnf.csiro.au>
+ Changed interface to <find_by_dev>, <find_entry>,
+ <devfs_unregister>, <devfs_fill_file> and <devfs_find_handle>.
+ Fixed inode times when symlink created with symlink(2).
+ v0.13
+ 19980129 C. Scott Ananian <cananian@alumni.princeton.edu>
+ Exported <devfs_set_symlink_destination>, <devfs_get_maj_min>
+ and <devfs_get_handle_from_inode>.
+ 19980129 Richard Gooch <rgooch@atnf.csiro.au>
+ Created <devfs_unlink> to support unlink(2).
+ v0.14
+ 19980129 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixed kerneld support for entries in devfs subdirectories.
+ 19980130 Richard Gooch <rgooch@atnf.csiro.au>
+ Bugfixes in <call_kerneld>.
+ v0.15
+ 19980207 Richard Gooch <rgooch@atnf.csiro.au>
+ Call kerneld when looking up unregistered entries.
+ v0.16
+ 19980326 Richard Gooch <rgooch@atnf.csiro.au>
+ Modified interface to <devfs_find_handle> for symlink traversal.
+ v0.17
+ 19980331 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixed persistence bug with device numbers for manually created
+ device files.
+ Fixed problem with recreating symlinks with different content.
+ v0.18
+ 19980401 Richard Gooch <rgooch@atnf.csiro.au>
+ Changed to CONFIG_KMOD.
+ Hide entries which are manually unlinked.
+ Always invalidate devfs dentry cache when registering entries.
+ Created <devfs_rmdir> to support rmdir(2).
+ Ensure directories created by <devfs_mk_dir> are visible.
+ v0.19
+ 19980402 Richard Gooch <rgooch@atnf.csiro.au>
+ Invalidate devfs dentry cache when making directories.
+ Invalidate devfs dentry cache when removing entries.
+ Fixed persistence bug with fifos.
+ v0.20
+ 19980421 Richard Gooch <rgooch@atnf.csiro.au>
+ Print process command when debugging kerneld/kmod.
+ Added debugging for register/unregister/change operations.
+ 19980422 Richard Gooch <rgooch@atnf.csiro.au>
+ Added "devfs=" boot options.
+ v0.21
+ 19980426 Richard Gooch <rgooch@atnf.csiro.au>
+ No longer lock/unlock superblock in <devfs_put_super>.
+ Drop negative dentries when they are released.
+ Manage dcache more efficiently.
+ v0.22
+ 19980427 Richard Gooch <rgooch@atnf.csiro.au>
+ Added DEVFS_FL_AUTO_DEVNUM flag.
+ v0.23
+ 19980430 Richard Gooch <rgooch@atnf.csiro.au>
+ No longer set unnecessary methods.
+ v0.24
+ 19980504 Richard Gooch <rgooch@atnf.csiro.au>
+ Added PID display to <call_kerneld> debugging message.
+ Added "after" debugging message to <call_kerneld>.
+ 19980519 Richard Gooch <rgooch@atnf.csiro.au>
+ Added "diread" and "diwrite" boot options.
+ 19980520 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixed persistence problem with permissions.
+ v0.25
+ 19980602 Richard Gooch <rgooch@atnf.csiro.au>
+ Support legacy device nodes.
+ Fixed bug where recreated inodes were hidden.
+ v0.26
+ 19980602 Richard Gooch <rgooch@atnf.csiro.au>
+ Improved debugging in <get_vfs_inode>.
+ 19980607 Richard Gooch <rgooch@atnf.csiro.au>
+ No longer free old dentries in <devfs_mk_dir>.
+ Free all dentries for a given entry when deleting inodes.
+ v0.27
+ 19980627 Richard Gooch <rgooch@atnf.csiro.au>
+ Limit auto-device numbering to majors 128 to 239.
+ v0.28
+ 19980629 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixed inode times persistence problem.
+ v0.29
+ 19980704 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixed spelling in <devfs_readlink> debug.
+ Fixed bug in <devfs_setup> parsing "dilookup".
+ v0.30
+ 19980705 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixed devfs inode leak when manually recreating inodes.
+ Fixed permission persistence problem when recreating inodes.
+ v0.31
+ 19980727 Richard Gooch <rgooch@atnf.csiro.au>
+ Removed harmless "unused variable" compiler warning.
+ Fixed modes for manually recreated device nodes.
+ v0.32
+ 19980728 Richard Gooch <rgooch@atnf.csiro.au>
+ Added NULL devfs inode warning in <devfs_read_inode>.
+ Force all inode nlink values to 1.
+ v0.33
+ 19980730 Richard Gooch <rgooch@atnf.csiro.au>
+ Added "dimknod" boot option.
+ Set inode nlink to 0 when freeing dentries.
+ Fixed modes for manually recreated symlinks.
+ v0.34
+ 19980802 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixed bugs in recreated directories and symlinks.
+ v0.35
+ 19980806 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixed bugs in recreated device nodes.
+ 19980807 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixed bug in currently unused <devfs_get_handle_from_inode>.
+ Defined new <devfs_handle_t> type.
+ Improved debugging when getting entries.
+ Fixed bug where directories could be emptied.
+ v0.36
+ 19980809 Richard Gooch <rgooch@atnf.csiro.au>
+ Replaced dummy .epoch inode with .devfsd character device.
+ 19980810 Richard Gooch <rgooch@atnf.csiro.au>
+ Implemented devfsd protocol revision 0.
+ v0.37
+ 19980819 Richard Gooch <rgooch@atnf.csiro.au>
+ Added soothing message to warning in <devfs_d_iput>.
+ v0.38
+ 19980829 Richard Gooch <rgooch@atnf.csiro.au>
+ Use GCC extensions for structure initialisations.
+ Implemented async open notification.
+ Incremented devfsd protocol revision to 1.
+ v0.39
+ 19980908 Richard Gooch <rgooch@atnf.csiro.au>
+ Moved async open notification to end of <devfs_open>.
+ v0.40
+ 19980910 Richard Gooch <rgooch@atnf.csiro.au>
+ Prepended "/dev/" to module load request.
+ Renamed <call_kerneld> to <call_kmod>.
+ v0.41
+ 19980910 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixed typo "AYSNC" -> "ASYNC".
+ v0.42
+ 19980910 Richard Gooch <rgooch@atnf.csiro.au>
+ Added open flag for files.
+ v0.43
+ 19980927 Richard Gooch <rgooch@atnf.csiro.au>
+ Set i_blocks=0 and i_blksize=1024 in <devfs_read_inode>.
+ v0.44
+ 19981005 Richard Gooch <rgooch@atnf.csiro.au>
+ Added test for empty <<name>> in <devfs_find_handle>.
+ Renamed <generate_path> to <devfs_generate_path> and published.
+ v0.45
+ 19981006 Richard Gooch <rgooch@atnf.csiro.au>
+ Created <devfs_get_fops>.
+ v0.46
+ 19981007 Richard Gooch <rgooch@atnf.csiro.au>
+ Limit auto-device numbering to majors 144 to 239.
+ v0.47
+ 19981010 Richard Gooch <rgooch@atnf.csiro.au>
+ Updated <devfs_follow_link> for VFS change in 2.1.125.
+ v0.48
+ 19981022 Richard Gooch <rgooch@atnf.csiro.au>
+ Created DEVFS_ FL_COMPAT flag.
+ v0.49
+ 19981023 Richard Gooch <rgooch@atnf.csiro.au>
+ Created "nocompat" boot option.
+ v0.50
+ 19981025 Richard Gooch <rgooch@atnf.csiro.au>
+ Replaced "mount" boot option with "nomount".
+ v0.51
+ 19981110 Richard Gooch <rgooch@atnf.csiro.au>
+ Created "only" boot option.
+ v0.52
+ 19981112 Richard Gooch <rgooch@atnf.csiro.au>
+ Added DEVFS_FL_REMOVABLE flag.
+ v0.53
+ 19981114 Richard Gooch <rgooch@atnf.csiro.au>
+ Only call <scan_dir_for_removable> on first call to
+ <devfs_readdir>.
+ v0.54
+ 19981205 Richard Gooch <rgooch@atnf.csiro.au>
+ Updated <devfs_rmdir> for VFS change in 2.1.131.
+ v0.55
+ 19981218 Richard Gooch <rgooch@atnf.csiro.au>
+ Created <devfs_mk_compat>.
+ 19981220 Richard Gooch <rgooch@atnf.csiro.au>
+ Check for partitions on removable media in <devfs_lookup>.
+ v0.56
+ 19990118 Richard Gooch <rgooch@atnf.csiro.au>
+ Added support for registering regular files.
+ Created <devfs_set_file_size>.
+ Update devfs inodes from entries if not changed through FS.
+ v0.57
+ 19990124 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixed <devfs_fill_file> to only initialise temporary inodes.
+ Trap for NULL fops in <devfs_register>.
+ Return -ENODEV in <devfs_fill_file> for non-driver inodes.
+ v0.58
+ 19990126 Richard Gooch <rgooch@atnf.csiro.au>
+ Switched from PATH_MAX to DEVFS_PATHLEN.
+ v0.59
+ 19990127 Richard Gooch <rgooch@atnf.csiro.au>
+ Created "nottycompat" boot option.
+ v0.60
+ 19990318 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixed <devfsd_read> to not overrun event buffer.
+ v0.61
+ 19990329 Richard Gooch <rgooch@atnf.csiro.au>
+ Created <devfs_auto_unregister>.
+ v0.62
+ 19990330 Richard Gooch <rgooch@atnf.csiro.au>
+ Don't return unregistred entries in <devfs_find_handle>.
+ Panic in <devfs_unregister> if entry unregistered.
+ 19990401 Richard Gooch <rgooch@atnf.csiro.au>
+ Don't panic in <devfs_auto_unregister> for duplicates.
+ v0.63
+ 19990402 Richard Gooch <rgooch@atnf.csiro.au>
+ Don't unregister already unregistered entries in <unregister>.
+ v0.64
+ 19990510 Richard Gooch <rgooch@atnf.csiro.au>
+ Disable warning messages when unable to read partition table for
+ removable media.
+ v0.65
+ 19990512 Richard Gooch <rgooch@atnf.csiro.au>
+ Updated <devfs_lookup> for VFS change in 2.3.1-pre1.
+ Created "oops-on-panic" boot option.
+ Improved debugging in <devfs_register> and <devfs_unregister>.
+ v0.66
+ 19990519 Richard Gooch <rgooch@atnf.csiro.au>
+ Added documentation for some functions.
+ 19990525 Richard Gooch <rgooch@atnf.csiro.au>
+ Removed "oops-on-panic" boot option: now always Oops.
+ v0.67
+ 19990531 Richard Gooch <rgooch@atnf.csiro.au>
+ Improved debugging in <devfs_register>.
+ v0.68
+ 19990604 Richard Gooch <rgooch@atnf.csiro.au>
+ Added "diunlink" and "nokmod" boot options.
+ Removed superfluous warning message in <devfs_d_iput>.
+ v0.69
+ 19990611 Richard Gooch <rgooch@atnf.csiro.au>
+ Took account of change to <d_alloc_root>.
+ v0.70
+ 19990614 Richard Gooch <rgooch@atnf.csiro.au>
+ Created separate event queue for each mounted devfs.
+ Removed <devfs_invalidate_dcache>.
+ Created new ioctl()s.
+ Incremented devfsd protocol revision to 3.
+ Fixed bug when re-creating directories: contents were lost.
+ Block access to inodes until devfsd updates permissions.
+ 19990615 Richard Gooch <rgooch@atnf.csiro.au>
+ Support 2.2.x kernels.
+ v0.71
+ 19990623 Richard Gooch <rgooch@atnf.csiro.au>
+ Switched to sending process uid/gid to devfsd.
+ Renamed <call_kmod> to <try_modload>.
+ Added DEVFSD_NOTIFY_LOOKUP event.
+ 19990624 Richard Gooch <rgooch@atnf.csiro.au>
+ Added DEVFSD_NOTIFY_CHANGE event.
+ Incremented devfsd protocol revision to 4.
+ v0.72
+ 19990713 Richard Gooch <rgooch@atnf.csiro.au>
+ Return EISDIR rather than EINVAL for read(2) on directories.
+ v0.73
+ 19990809 Richard Gooch <rgooch@atnf.csiro.au>
+ Changed <devfs_setup> to new __init scheme.
+ v0.74
+ 19990901 Richard Gooch <rgooch@atnf.csiro.au>
+ Changed remaining function declarations to new __init scheme.
+ v0.75
+ 19991013 Richard Gooch <rgooch@atnf.csiro.au>
+ Created <devfs_get_info>, <devfs_set_info>,
+ <devfs_get_first_child> and <devfs_get_next_sibling>.
+ Added <<dir>> parameter to <devfs_register>, <devfs_mk_compat>,
+ <devfs_mk_dir> and <devfs_find_handle>.
+ Work sponsored by SGI.
+ v0.76
+ 19991017 Richard Gooch <rgooch@atnf.csiro.au>
+ Allow multiple unregistrations.
+ Work sponsored by SGI.
+ v0.77
+ 19991026 Richard Gooch <rgooch@atnf.csiro.au>
+ Added major and minor number to devfsd protocol.
+ Incremented devfsd protocol revision to 5.
+ Work sponsored by SGI.
+ v0.78
+ 19991030 Richard Gooch <rgooch@atnf.csiro.au>
+ Support info pointer for all devfs entry types.
+ Added <<info>> parameter to <devfs_mk_dir> and
+ <devfs_mk_symlink>.
+ Work sponsored by SGI.
+ v0.79
+ 19991031 Richard Gooch <rgooch@atnf.csiro.au>
+ Support "../" when searching devfs namespace.
+ Work sponsored by SGI.
+ v0.80
+ 19991101 Richard Gooch <rgooch@atnf.csiro.au>
+ Created <devfs_get_unregister_slave>.
+ Work sponsored by SGI.
+ v0.81
+ 19991103 Richard Gooch <rgooch@atnf.csiro.au>
+ Exported <devfs_get_parent>.
+ Work sponsored by SGI.
+ v0.82
+ 19991104 Richard Gooch <rgooch@atnf.csiro.au>
+ Removed unused <devfs_set_symlink_destination>.
+ 19991105 Richard Gooch <rgooch@atnf.csiro.au>
+ Do not hide entries from devfsd or children.
+ Removed DEVFS_ FL_TTY_COMPAT flag.
+ Removed "nottycompat" boot option.
+ Removed <devfs_mk_compat>.
+ Work sponsored by SGI.
+ v0.83
+ 19991107 Richard Gooch <rgooch@atnf.csiro.au>
+ Added DEVFS_FL_WAIT flag.
+ Work sponsored by SGI.
+ v0.84
+ 19991107 Richard Gooch <rgooch@atnf.csiro.au>
+ Support new "disc" naming scheme in <get_removable_partition>.
+ Allow NULL fops in <devfs_register>.
+ Work sponsored by SGI.
+ v0.85
+ 19991110 Richard Gooch <rgooch@atnf.csiro.au>
+ Fall back to major table if NULL fops given to <devfs_register>.
+ Work sponsored by SGI.
+ v0.86
+ 19991204 Richard Gooch <rgooch@atnf.csiro.au>
+ Support fifos when unregistering.
+ Work sponsored by SGI.
+ v0.87
+ 19991209 Richard Gooch <rgooch@atnf.csiro.au>
+ Removed obsolete DEVFS_ FL_COMPAT and DEVFS_ FL_TOLERANT flags.
+ Work sponsored by SGI.
+ v0.88
+ 19991214 Richard Gooch <rgooch@atnf.csiro.au>
+ Removed kmod support.
+ Work sponsored by SGI.
+ v0.89
+ 19991216 Richard Gooch <rgooch@atnf.csiro.au>
+ Improved debugging in <get_vfs_inode>.
+ Ensure dentries created by devfsd will be cleaned up.
+ Work sponsored by SGI.
+ v0.90
+ 19991223 Richard Gooch <rgooch@atnf.csiro.au>
+ Created <devfs_get_name>.
+ Work sponsored by SGI.
+ v0.91
+ 20000203 Richard Gooch <rgooch@atnf.csiro.au>
+ Ported to kernel 2.3.42.
+ Removed <devfs_fill_file>.
+ Work sponsored by SGI.
+ v0.92
+ 20000306 Richard Gooch <rgooch@atnf.csiro.au>
+ Added DEVFS_ FL_NO_PERSISTENCE flag.
+ Removed unnecessary call to <update_devfs_inode_from_entry> in
+ <devfs_readdir>.
+ Work sponsored by SGI.
+ v0.93
+ 20000413 Richard Gooch <rgooch@atnf.csiro.au>
+ Set inode->i_size to correct size for symlinks.
+ 20000414 Richard Gooch <rgooch@atnf.csiro.au>
+ Only give lookup() method to directories to comply with new VFS
+ assumptions.
+ Work sponsored by SGI.
+ 20000415 Richard Gooch <rgooch@atnf.csiro.au>
+ Remove unnecessary tests in symlink methods.
+ Don't kill existing block ops in <devfs_read_inode>.
+ Work sponsored by SGI.
+ v0.94
+ 20000424 Richard Gooch <rgooch@atnf.csiro.au>
+ Don't create missing directories in <devfs_find_handle>.
+ Work sponsored by SGI.
+ v0.95
+ 20000430 Richard Gooch <rgooch@atnf.csiro.au>
+ Added CONFIG_DEVFS_MOUNT.
+ Work sponsored by SGI.
+ v0.96
+ 20000608 Richard Gooch <rgooch@atnf.csiro.au>
+ Disabled multi-mount capability (use VFS bindings instead).
+ Work sponsored by SGI.
+ v0.97
+ 20000610 Richard Gooch <rgooch@atnf.csiro.au>
+ Switched to FS_SINGLE to disable multi-mounts.
+ 20000612 Richard Gooch <rgooch@atnf.csiro.au>
+ Removed module support.
+ Removed multi-mount code.
+ Removed compatibility macros: VFS has changed too much.
+ Work sponsored by SGI.
+ v0.98
+ 20000614 Richard Gooch <rgooch@atnf.csiro.au>
+ Merged devfs inode into devfs entry.
+ Work sponsored by SGI.
+ v0.99
+ 20000619 Richard Gooch <rgooch@atnf.csiro.au>
+ Removed dead code in <devfs_register> which used to call
+ <free_dentries>.
+ Work sponsored by SGI.
+ v0.100
+ 20000621 Richard Gooch <rgooch@atnf.csiro.au>
+ Changed interface to <devfs_register>.
+ Work sponsored by SGI.
+ v0.101
+ 20000622 Richard Gooch <rgooch@atnf.csiro.au>
+ Simplified interface to <devfs_mk_symlink> and <devfs_mk_dir>.
+ Simplified interface to <devfs_find_handle>.
+ Work sponsored by SGI.
+ v0.102
+ 20010519 Richard Gooch <rgooch@atnf.csiro.au>
+ Ensure <devfs_generate_path> terminates string for root entry.
+ Exported <devfs_get_name> to modules.
+ 20010520 Richard Gooch <rgooch@atnf.csiro.au>
+ Make <devfs_mk_symlink> send events to devfsd.
+ Cleaned up option processing in <devfs_setup>.
+ 20010521 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixed bugs in handling symlinks: could leak or cause Oops.
+ 20010522 Richard Gooch <rgooch@atnf.csiro.au>
+ Cleaned up directory handling by separating fops.
+ v0.103
+ 20010601 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixed handling of inverted options in <devfs_setup>.
+ v0.104
+ 20010604 Richard Gooch <rgooch@atnf.csiro.au>
+ Adjusted <try_modload> to account for <devfs_generate_path> fix.
+ v0.105
+ 20010617 Richard Gooch <rgooch@atnf.csiro.au>
+ Answered question posed by Al Viro and removed his comments.
+ Moved setting of registered flag after other fields are changed.
+ Fixed race between <devfsd_close> and <devfsd_notify_one>.
+ Global VFS changes added bogus BKL to <devfsd_close>: removed.
+ Widened locking in <devfs_readlink> and <devfs_follow_link>.
+ Replaced <devfsd_read> stack usage with <devfsd_ioctl> kmalloc.
+ Simplified locking in <devfsd_ioctl> and fixed memory leak.
+ v0.106
+ 20010709 Richard Gooch <rgooch@atnf.csiro.au>
+ Removed broken devnum allocation and use <devfs_alloc_devnum>.
+ Fixed old devnum leak by calling new <devfs_dealloc_devnum>.
+ v0.107
+ 20010712 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixed bug in <devfs_setup> which could hang boot process.
+ v0.108
+ 20010730 Richard Gooch <rgooch@atnf.csiro.au>
+ Added DEVFSD_NOTIFY_DELETE event.
+ 20010801 Richard Gooch <rgooch@atnf.csiro.au>
+ Removed #include <asm/segment.h>.
+ v0.109
+ 20010807 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixed inode table races by removing it and using
+ inode->u.generic_ip instead.
+ Moved <devfs_read_inode> into <get_vfs_inode>.
+ Moved <devfs_write_inode> into <devfs_notify_change>.
+ v0.110
+ 20010808 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixed race in <devfs_do_symlink> for uni-processor.
+ v0.111
+ 20010818 Richard Gooch <rgooch@atnf.csiro.au>
+ Removed remnant of multi-mount support in <devfs_mknod>.
+ Removed unused DEVFS_FL_SHOW_UNREG flag.
+ v0.112
+ 20010820 Richard Gooch <rgooch@atnf.csiro.au>
+ Removed nlink field from struct devfs_inode.
+ v0.113
+ 20010823 Richard Gooch <rgooch@atnf.csiro.au>
+ Replaced BKL with global rwsem to protect symlink data (quick
+ and dirty hack).
+ v0.114
+ 20010827 Richard Gooch <rgooch@atnf.csiro.au>
+ Replaced global rwsem for symlink with per-link refcount.
+ v0.115
+ 20010919 Richard Gooch <rgooch@atnf.csiro.au>
+ Set inode->i_mapping->a_ops for block nodes in <get_vfs_inode>.
+ v0.116
+ 20011008 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixed overrun in <devfs_link> by removing function (not needed).
+ 20011009 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixed buffer underrun in <try_modload>.
+ 20011029 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixed race in <devfsd_ioctl> when setting event mask.
+ 20011114 Richard Gooch <rgooch@atnf.csiro.au>
+ First release of new locking code.
+ v1.0
+ 20011117 Richard Gooch <rgooch@atnf.csiro.au>
+ Discard temporary buffer, now use "%s" for dentry names.
+ 20011118 Richard Gooch <rgooch@atnf.csiro.au>
+ Don't generate path in <try_modload>: use fake entry instead.
+ Use "existing" directory in <_devfs_make_parent_for_leaf>.
+ 20011122 Richard Gooch <rgooch@atnf.csiro.au>
+ Use slab cache rather than fixed buffer for devfsd events.
+ v1.1
+ 20011125 Richard Gooch <rgooch@atnf.csiro.au>
+ Send DEVFSD_NOTIFY_REGISTERED events in <devfs_mk_dir>.
+ 20011127 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixed locking bug in <devfs_d_revalidate_wait> due to typo.
+ Do not send CREATE, CHANGE, ASYNC_OPEN or DELETE events from
+ devfsd or children.
+ v1.2
+ 20011202 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixed bug in <devfsd_read>: was dereferencing freed pointer.
+ v1.3
+ 20011203 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixed bug in <devfsd_close>: was dereferencing freed pointer.
+ Added process group check for devfsd privileges.
+ v1.4
+ 20011204 Richard Gooch <rgooch@atnf.csiro.au>
+ Use SLAB_ATOMIC in <devfsd_notify_de> from <devfs_d_delete>.
+ v1.5
+ 20011211 Richard Gooch <rgooch@atnf.csiro.au>
+ Return old entry in <devfs_mk_dir> for 2.4.x kernels.
+ 20011212 Richard Gooch <rgooch@atnf.csiro.au>
+ Increment refcount on module in <check_disc_changed>.
+ 20011215 Richard Gooch <rgooch@atnf.csiro.au>
+ Created <devfs_get_handle> and exported <devfs_put>.
+ Increment refcount on module in <devfs_get_ops>.
+ Created <devfs_put_ops>.
+ v1.6
+ 20011216 Richard Gooch <rgooch@atnf.csiro.au>
+ Added poisoning to <devfs_put>.
+ Improved debugging messages.
+ v1.7
+ 20011221 Richard Gooch <rgooch@atnf.csiro.au>
+ Corrected (made useful) debugging message in <unregister>.
+ Moved <kmem_cache_create> in <mount_devfs_fs> to <init_devfs_fs>
+ 20011224 Richard Gooch <rgooch@atnf.csiro.au>
+ Added magic number to guard against scribbling drivers.
+ 20011226 Richard Gooch <rgooch@atnf.csiro.au>
+ Only return old entry in <devfs_mk_dir> if a directory.
+ Defined macros for error and debug messages.
+ v1.8
+ 20020113 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixed (rare, old) race in <devfs_lookup>.
+ v1.9
+ 20020120 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixed deadlock bug in <devfs_d_revalidate_wait>.
+ Tag VFS deletable in <devfs_mk_symlink> if handle ignored.
+ v1.10
+ 20020129 Richard Gooch <rgooch@atnf.csiro.au>
+ Added KERN_* to remaining messages.
+ Cleaned up declaration of <stat_read>.
+ v1.11
+ 20020219 Richard Gooch <rgooch@atnf.csiro.au>
+ Changed <devfs_rmdir> to allow later additions if not yet empty.
+ v1.12
+ 20020514 Richard Gooch <rgooch@atnf.csiro.au>
+ Added BKL to <devfs_open> because drivers still need it.
+ Protected <scan_dir_for_removable> and <get_removable_partition>
+ from changing directory contents.
+ v1.12a
+ 20020721 Richard Gooch <rgooch@atnf.csiro.au>
+ Switched to ISO C structure field initialisers.
+ Switch to set_current_state() and move before add_wait_queue().
+ 20020722 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixed devfs entry leak in <devfs_readdir> when *readdir fails.
+ v1.12b
+ 20020818 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixed module unload race in <devfs_open>.
+ v1.12c
+*/
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/tty.h>
+#include <linux/timer.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/wait.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/devfs_fs.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/smp_lock.h>
+#include <linux/smp.h>
+#include <linux/version.h>
+#include <linux/rwsem.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/processor.h>
+#include <asm/system.h>
+#include <asm/pgtable.h>
+#include <asm/bitops.h>
+#include <asm/atomic.h>
+
+#define DEVFS_VERSION "1.12c (20020818)"
+
+#define DEVFS_NAME "devfs"
+
+#define FIRST_INODE 1
+
+#define STRING_LENGTH 256
+#define FAKE_BLOCK_SIZE 1024
+#define POISON_PTR ( *(void **) poison_array )
+#define MAGIC_VALUE 0x327db823
+
+#ifndef TRUE
+# define TRUE 1
+# define FALSE 0
+#endif
+
+#define MODE_DIR (S_IFDIR | S_IWUSR | S_IRUGO | S_IXUGO)
+
+#define IS_HIDDEN(de) ( (de)->hide && !is_devfsd_or_child(fs_info) )
+
+#define DEBUG_NONE 0x0000000
+#define DEBUG_MODULE_LOAD 0x0000001
+#define DEBUG_REGISTER 0x0000002
+#define DEBUG_UNREGISTER 0x0000004
+#define DEBUG_FREE 0x0000008
+#define DEBUG_SET_FLAGS 0x0000010
+#define DEBUG_S_READ 0x0000100 /* Break */
+#define DEBUG_I_LOOKUP 0x0001000 /* Break */
+#define DEBUG_I_CREATE 0x0002000
+#define DEBUG_I_GET 0x0004000
+#define DEBUG_I_CHANGE 0x0008000
+#define DEBUG_I_UNLINK 0x0010000
+#define DEBUG_I_RLINK 0x0020000
+#define DEBUG_I_FLINK 0x0040000
+#define DEBUG_I_MKNOD 0x0080000
+#define DEBUG_F_READDIR 0x0100000 /* Break */
+#define DEBUG_D_DELETE 0x1000000 /* Break */
+#define DEBUG_D_RELEASE 0x2000000
+#define DEBUG_D_IPUT 0x4000000
+#define DEBUG_ALL 0xfffffff
+#define DEBUG_DISABLED DEBUG_NONE
+
+#define OPTION_NONE 0x00
+#define OPTION_MOUNT 0x01
+#define OPTION_ONLY 0x02
+
+#define PRINTK(format, args...) \
+ {printk (KERN_ERR "%s" format, __FUNCTION__ , ## args);}
+
+#define OOPS(format, args...) \
+ {printk (KERN_CRIT "%s" format, __FUNCTION__ , ## args); \
+ printk ("Forcing Oops\n"); \
+ BUG();}
+
+#ifdef CONFIG_DEVFS_DEBUG
+# define VERIFY_ENTRY(de) \
+ {if ((de) && (de)->magic_number != MAGIC_VALUE) \
+ OOPS ("(%p): bad magic value: %x\n", (de), (de)->magic_number);}
+# define WRITE_ENTRY_MAGIC(de,magic) (de)->magic_number = (magic)
+# define DPRINTK(flag, format, args...) \
+ {if (devfs_debug & flag) \
+ printk (KERN_INFO "%s" format, __FUNCTION__ , ## args);}
+#else
+# define VERIFY_ENTRY(de)
+# define WRITE_ENTRY_MAGIC(de,magic)
+# define DPRINTK(flag, format, args...)
+#endif
+
+
+struct directory_type
+{
+ rwlock_t lock; /* Lock for searching(R)/updating(W) */
+ struct devfs_entry *first;
+ struct devfs_entry *last;
+ unsigned short num_removable; /* Lock for writing but not reading */
+ unsigned char no_more_additions:1;
+};
+
+struct file_type
+{
+ unsigned long size;
+};
+
+struct device_type
+{
+ unsigned short major;
+ unsigned short minor;
+};
+
+struct fcb_type /* File, char, block type */
+{
+ void *ops;
+ union
+ {
+ struct file_type file;
+ struct device_type device;
+ }
+ u;
+ unsigned char auto_owner:1;
+ unsigned char aopen_notify:1;
+ unsigned char removable:1; /* Belongs in device_type, but save space */
+ unsigned char open:1; /* Not entirely correct */
+ unsigned char autogen:1; /* Belongs in device_type, but save space */
+};
+
+struct symlink_type
+{
+ unsigned int length; /* Not including the NULL-termimator */
+ char *linkname; /* This is NULL-terminated */
+};
+
+struct devfs_inode /* This structure is for "persistent" inode storage */
+{
+ struct dentry *dentry;
+ time_t atime;
+ time_t mtime;
+ time_t ctime;
+ unsigned int ino; /* Inode number as seen in the VFS */
+ uid_t uid;
+ gid_t gid;
+};
+
+struct devfs_entry
+{
+#ifdef CONFIG_DEVFS_DEBUG
+ unsigned int magic_number;
+#endif
+ void *info;
+ atomic_t refcount; /* When this drops to zero, it's unused */
+ union
+ {
+ struct directory_type dir;
+ struct fcb_type fcb;
+ struct symlink_type symlink;
+ const char *name; /* Only used for (mode == 0) */
+ }
+ u;
+ struct devfs_entry *prev; /* Previous entry in the parent directory */
+ struct devfs_entry *next; /* Next entry in the parent directory */
+ struct devfs_entry *parent; /* The parent directory */
+ struct devfs_entry *slave; /* Another entry to unregister */
+ struct devfs_inode inode;
+ umode_t mode;
+ unsigned short namelen; /* I think 64k+ filenames are a way off... */
+ unsigned char hide:1;
+ unsigned char vfs_deletable:1;/* Whether the VFS may delete the entry */
+ char name[1]; /* This is just a dummy: the allocated array
+ is bigger. This is NULL-terminated */
+};
+
+/* The root of the device tree */
+static struct devfs_entry *root_entry;
+
+struct devfsd_buf_entry
+{
+ struct devfs_entry *de; /* The name is generated with this */
+ unsigned short type; /* The type of event */
+ umode_t mode;
+ uid_t uid;
+ gid_t gid;
+ struct devfsd_buf_entry *next;
+};
+
+struct fs_info /* This structure is for the mounted devfs */
+{
+ struct super_block *sb;
+ spinlock_t devfsd_buffer_lock; /* Lock when inserting/deleting events */
+ struct devfsd_buf_entry *devfsd_first_event;
+ struct devfsd_buf_entry *devfsd_last_event;
+ volatile int devfsd_sleeping;
+ volatile struct task_struct *devfsd_task;
+ volatile pid_t devfsd_pgrp;
+ volatile struct file *devfsd_file;
+ struct devfsd_notify_struct *devfsd_info;
+ volatile unsigned long devfsd_event_mask;
+ atomic_t devfsd_overrun_count;
+ wait_queue_head_t devfsd_wait_queue; /* Wake devfsd on input */
+ wait_queue_head_t revalidate_wait_queue; /* Wake when devfsd sleeps */
+};
+
+static struct fs_info fs_info = {devfsd_buffer_lock: SPIN_LOCK_UNLOCKED};
+static kmem_cache_t *devfsd_buf_cache;
+#ifdef CONFIG_DEVFS_DEBUG
+static unsigned int devfs_debug_init __initdata = DEBUG_NONE;
+static unsigned int devfs_debug = DEBUG_NONE;
+static spinlock_t stat_lock = SPIN_LOCK_UNLOCKED;
+static unsigned int stat_num_entries;
+static unsigned int stat_num_bytes;
+#endif
+static unsigned char poison_array[8] =
+ {0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a};
+
+#ifdef CONFIG_DEVFS_MOUNT
+static unsigned int boot_options = OPTION_MOUNT;
+#else
+static unsigned int boot_options = OPTION_NONE;
+#endif
+
+/* Forward function declarations */
+static devfs_handle_t _devfs_walk_path (struct devfs_entry *dir,
+ const char *name, int namelen,
+ int traverse_symlink);
+static ssize_t devfsd_read (struct file *file, char *buf, size_t len,
+ loff_t *ppos);
+static int devfsd_ioctl (struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg);
+static int devfsd_close (struct inode *inode, struct file *file);
+#ifdef CONFIG_DEVFS_DEBUG
+static ssize_t stat_read (struct file *file, char *buf, size_t len,
+ loff_t *ppos);
+static struct file_operations stat_fops =
+{
+ .read = stat_read,
+};
+#endif
+
+
+/* Devfs daemon file operations */
+static struct file_operations devfsd_fops =
+{
+ .read = devfsd_read,
+ .ioctl = devfsd_ioctl,
+ .release = devfsd_close,
+};
+
+
+/* Support functions follow */
+
+
+/**
+ * devfs_get - Get a reference to a devfs entry.
+ * @de: The devfs entry.
+ */
+
+static struct devfs_entry *devfs_get (struct devfs_entry *de)
+{
+ VERIFY_ENTRY (de);
+ if (de) atomic_inc (&de->refcount);
+ return de;
+} /* End Function devfs_get */
+
+/**
+ * devfs_put - Put (release) a reference to a devfs entry.
+ * @de: The handle to the devfs entry.
+ */
+
+void devfs_put (devfs_handle_t de)
+{
+ if (!de) return;
+ VERIFY_ENTRY (de);
+ if (de->info == POISON_PTR) OOPS ("(%p): poisoned pointer\n", de);
+ if ( !atomic_dec_and_test (&de->refcount) ) return;
+ if (de == root_entry) OOPS ("(%p): root entry being freed\n", de);
+ DPRINTK (DEBUG_FREE, "(%s): de: %p, parent: %p \"%s\"\n",
+ de->name, de, de->parent,
+ de->parent ? de->parent->name : "no parent");
+ if ( S_ISLNK (de->mode) ) kfree (de->u.symlink.linkname);
+ if ( ( S_ISCHR (de->mode) || S_ISBLK (de->mode) ) && de->u.fcb.autogen )
+ {
+ devfs_dealloc_devnum ( S_ISCHR (de->mode) ? DEVFS_SPECIAL_CHR :
+ DEVFS_SPECIAL_BLK,
+ mk_kdev (de->u.fcb.u.device.major,
+ de->u.fcb.u.device.minor) );
+ }
+ WRITE_ENTRY_MAGIC (de, 0);
+#ifdef CONFIG_DEVFS_DEBUG
+ spin_lock (&stat_lock);
+ --stat_num_entries;
+ stat_num_bytes -= sizeof *de + de->namelen;
+ if ( S_ISLNK (de->mode) ) stat_num_bytes -= de->u.symlink.length + 1;
+ spin_unlock (&stat_lock);
+#endif
+ de->info = POISON_PTR;
+ kfree (de);
+} /* End Function devfs_put */
+
+/**
+ * _devfs_search_dir - Search for a devfs entry in a directory.
+ * @dir: The directory to search.
+ * @name: The name of the entry to search for.
+ * @namelen: The number of characters in @name.
+ *
+ * Search for a devfs entry in a directory and returns a pointer to the entry
+ * on success, else %NULL. The directory must be locked already.
+ * An implicit devfs_get() is performed on the returned entry.
+ */
+
+static struct devfs_entry *_devfs_search_dir (struct devfs_entry *dir,
+ const char *name,
+ unsigned int namelen)
+{
+ struct devfs_entry *curr;
+
+ if ( !S_ISDIR (dir->mode) )
+ {
+ PRINTK ("(%s): not a directory\n", dir->name);
+ return NULL;
+ }
+ for (curr = dir->u.dir.first; curr != NULL; curr = curr->next)
+ {
+ if (curr->namelen != namelen) continue;
+ if (memcmp (curr->name, name, namelen) == 0) break;
+ /* Not found: try the next one */
+ }
+ return devfs_get (curr);
+} /* End Function _devfs_search_dir */
+
+
+/**
+ * _devfs_alloc_entry - Allocate a devfs entry.
+ * @name: The name of the entry.
+ * @namelen: The number of characters in @name.
+ *
+ * Allocate a devfs entry and returns a pointer to the entry on success, else
+ * %NULL.
+ */
+
+static struct devfs_entry *_devfs_alloc_entry (const char *name,
+ unsigned int namelen,
+ umode_t mode)
+{
+ struct devfs_entry *new;
+ static unsigned long inode_counter = FIRST_INODE;
+ static spinlock_t counter_lock = SPIN_LOCK_UNLOCKED;
+
+ if ( name && (namelen < 1) ) namelen = strlen (name);
+ if ( ( new = kmalloc (sizeof *new + namelen, GFP_KERNEL) ) == NULL )
+ return NULL;
+ memset (new, 0, sizeof *new + namelen); /* Will set '\0' on name */
+ new->mode = mode;
+ if ( S_ISDIR (mode) ) rwlock_init (&new->u.dir.lock);
+ atomic_set (&new->refcount, 1);
+ spin_lock (&counter_lock);
+ new->inode.ino = inode_counter++;
+ spin_unlock (&counter_lock);
+ if (name) memcpy (new->name, name, namelen);
+ new->namelen = namelen;
+ WRITE_ENTRY_MAGIC (new, MAGIC_VALUE);
+#ifdef CONFIG_DEVFS_DEBUG
+ spin_lock (&stat_lock);
+ ++stat_num_entries;
+ stat_num_bytes += sizeof *new + namelen;
+ spin_unlock (&stat_lock);
+#endif
+ return new;
+} /* End Function _devfs_alloc_entry */
+
+
+/**
+ * _devfs_append_entry - Append a devfs entry to a directory's child list.
+ * @dir: The directory to add to.
+ * @de: The devfs entry to append.
+ * @removable: If TRUE, increment the count of removable devices for %dir.
+ * @old_de: If an existing entry exists, it will be written here. This may
+ * be %NULL. An implicit devfs_get() is performed on this entry.
+ *
+ * Append a devfs entry to a directory's list of children, checking first to
+ * see if an entry of the same name exists. The directory will be locked.
+ * The value 0 is returned on success, else a negative error code.
+ * On failure, an implicit devfs_put() is performed on %de.
+ */
+
+static int _devfs_append_entry (devfs_handle_t dir, devfs_handle_t de,
+ int removable, devfs_handle_t *old_de)
+{
+ int retval;
+
+ if (old_de) *old_de = NULL;
+ if ( !S_ISDIR (dir->mode) )
+ {
+ PRINTK ("(%s): dir: \"%s\" is not a directory\n", de->name, dir->name);
+ devfs_put (de);
+ return -ENOTDIR;
+ }
+ write_lock (&dir->u.dir.lock);
+ if (dir->u.dir.no_more_additions) retval = -ENOENT;
+ else
+ {
+ struct devfs_entry *old;
+
+ old = _devfs_search_dir (dir, de->name, de->namelen);
+ if (old_de) *old_de = old;
+ else devfs_put (old);
+ if (old == NULL)
+ {
+ de->parent = dir;
+ de->prev = dir->u.dir.last;
+ /* Append to the directory's list of children */
+ if (dir->u.dir.first == NULL) dir->u.dir.first = de;
+ else dir->u.dir.last->next = de;
+ dir->u.dir.last = de;
+ if (removable) ++dir->u.dir.num_removable;
+ retval = 0;
+ }
+ else retval = -EEXIST;
+ }
+ write_unlock (&dir->u.dir.lock);
+ if (retval) devfs_put (de);
+ return retval;
+} /* End Function _devfs_append_entry */
+
+
+/**
+ * _devfs_get_root_entry - Get the root devfs entry.
+ *
+ * Returns the root devfs entry on success, else %NULL.
+ */
+
+static struct devfs_entry *_devfs_get_root_entry (void)
+{
+ kdev_t devnum;
+ struct devfs_entry *new;
+ static spinlock_t root_lock = SPIN_LOCK_UNLOCKED;
+
+ /* Always ensure the root is created */
+ if (root_entry) return root_entry;
+ if ( ( new = _devfs_alloc_entry (NULL, 0,MODE_DIR) ) == NULL ) return NULL;
+ spin_lock (&root_lock);
+ if (root_entry)
+ {
+ spin_unlock (&root_lock);
+ devfs_put (new);
+ return (root_entry);
+ }
+ root_entry = new;
+ spin_unlock (&root_lock);
+ /* And create the entry for ".devfsd" */
+ if ( ( new = _devfs_alloc_entry (".devfsd", 0, S_IFCHR |S_IRUSR |S_IWUSR) )
+ == NULL ) return NULL;
+ devnum = devfs_alloc_devnum (DEVFS_SPECIAL_CHR);
+ new->u.fcb.u.device.major = major (devnum);
+ new->u.fcb.u.device.minor = minor (devnum);
+ new->u.fcb.ops = &devfsd_fops;
+ _devfs_append_entry (root_entry, new, FALSE, NULL);
+#ifdef CONFIG_DEVFS_DEBUG
+ if ( ( new = _devfs_alloc_entry (".stat", 0, S_IFCHR | S_IRUGO | S_IWUGO) )
+ == NULL ) return NULL;
+ devnum = devfs_alloc_devnum (DEVFS_SPECIAL_CHR);
+ new->u.fcb.u.device.major = major (devnum);
+ new->u.fcb.u.device.minor = minor (devnum);
+ new->u.fcb.ops = &stat_fops;
+ _devfs_append_entry (root_entry, new, FALSE, NULL);
+#endif
+ return root_entry;
+} /* End Function _devfs_get_root_entry */
+
+
+/**
+ * _devfs_descend - Descend down a tree using the next component name.
+ * @dir: The directory to search.
+ * @name: The component name to search for.
+ * @namelen: The length of %name.
+ * @next_pos: The position of the next '/' or '\0' is written here.
+ *
+ * Descend into a directory, searching for a component. This function forms
+ * the core of a tree-walking algorithm. The directory will be locked.
+ * The devfs entry corresponding to the component is returned. If there is
+ * no matching entry, %NULL is returned.
+ * An implicit devfs_get() is performed on the returned entry.
+ */
+
+static struct devfs_entry *_devfs_descend (struct devfs_entry *dir,
+ const char *name, int namelen,
+ int *next_pos)
+{
+ const char *stop, *ptr;
+ struct devfs_entry *entry;
+
+ if ( (namelen >= 3) && (strncmp (name, "../", 3) == 0) )
+ { /* Special-case going to parent directory */
+ *next_pos = 3;
+ return devfs_get (dir->parent);
+ }
+ stop = name + namelen;
+ /* Search for a possible '/' */
+ for (ptr = name; (ptr < stop) && (*ptr != '/'); ++ptr);
+ *next_pos = ptr - name;
+ read_lock (&dir->u.dir.lock);
+ entry = _devfs_search_dir (dir, name, *next_pos);
+ read_unlock (&dir->u.dir.lock);
+ return entry;
+} /* End Function _devfs_descend */
+
+
+static devfs_handle_t _devfs_make_parent_for_leaf (struct devfs_entry *dir,
+ const char *name,
+ int namelen, int *leaf_pos)
+{
+ int next_pos = 0;
+
+ if (dir == NULL) dir = _devfs_get_root_entry ();
+ if (dir == NULL) return NULL;
+ devfs_get (dir);
+ /* Search for possible trailing component and ignore it */
+ for (--namelen; (namelen > 0) && (name[namelen] != '/'); --namelen);
+ *leaf_pos = (name[namelen] == '/') ? (namelen + 1) : 0;
+ for (; namelen > 0; name += next_pos, namelen -= next_pos)
+ {
+ struct devfs_entry *de, *old;
+
+ if ( ( de = _devfs_descend (dir, name, namelen, &next_pos) ) == NULL )
+ {
+ de = _devfs_alloc_entry (name, next_pos, MODE_DIR);
+ devfs_get (de);
+ if ( !de || _devfs_append_entry (dir, de, FALSE, &old) )
+ {
+ devfs_put (de);
+ if ( !old || !S_ISDIR (old->mode) )
+ {
+ devfs_put (old);
+ devfs_put (dir);
+ return NULL;
+ }
+ de = old; /* Use the existing directory */
+ }
+ }
+ if (de == dir->parent)
+ {
+ devfs_put (dir);
+ devfs_put (de);
+ return NULL;
+ }
+ devfs_put (dir);
+ dir = de;
+ if (name[next_pos] == '/') ++next_pos;
+ }
+ return dir;
+} /* End Function _devfs_make_parent_for_leaf */
+
+
+static devfs_handle_t _devfs_prepare_leaf (devfs_handle_t *dir,
+ const char *name, umode_t mode)
+{
+ int namelen, leaf_pos;
+ struct devfs_entry *de;
+
+ namelen = strlen (name);
+ if ( ( *dir = _devfs_make_parent_for_leaf (*dir, name, namelen,
+ &leaf_pos) ) == NULL )
+ {
+ PRINTK ("(%s): could not create parent path\n", name);
+ return NULL;
+ }
+ if ( ( de = _devfs_alloc_entry (name + leaf_pos, namelen - leaf_pos,mode) )
+ == NULL )
+ {
+ PRINTK ("(%s): could not allocate entry\n", name);
+ devfs_put (*dir);
+ return NULL;
+ }
+ return de;
+} /* End Function _devfs_prepare_leaf */
+
+
+static devfs_handle_t _devfs_walk_path (struct devfs_entry *dir,
+ const char *name, int namelen,
+ int traverse_symlink)
+{
+ int next_pos = 0;
+
+ if (dir == NULL) dir = _devfs_get_root_entry ();
+ if (dir == NULL) return NULL;
+ devfs_get (dir);
+ for (; namelen > 0; name += next_pos, namelen -= next_pos)
+ {
+ struct devfs_entry *de, *link;
+
+ if ( ( de = _devfs_descend (dir, name, namelen, &next_pos) ) == NULL )
+ {
+ devfs_put (dir);
+ return NULL;
+ }
+ if (S_ISLNK (de->mode) && traverse_symlink)
+ { /* Need to follow the link: this is a stack chomper */
+ link = _devfs_walk_path (dir, de->u.symlink.linkname,
+ de->u.symlink.length, TRUE);
+ devfs_put (de);
+ if (!link)
+ {
+ devfs_put (dir);
+ return NULL;
+ }
+ de = link;
+ }
+ devfs_put (dir);
+ dir = de;
+ if (name[next_pos] == '/') ++next_pos;
+ }
+ return dir;
+} /* End Function _devfs_walk_path */
+
+
+/**
+ * _devfs_find_by_dev - Find a devfs entry in a directory.
+ * @dir: The directory where to search
+ * @major: The major number to search for.
+ * @minor: The minor number to search for.
+ * @type: The type of special file to search for. This may be either
+ * %DEVFS_SPECIAL_CHR or %DEVFS_SPECIAL_BLK.
+ *
+ * Returns the devfs_entry pointer on success, else %NULL. An implicit
+ * devfs_get() is performed.
+ */
+
+static struct devfs_entry *_devfs_find_by_dev (struct devfs_entry *dir,
+ unsigned int major,
+ unsigned int minor, char type)
+{
+ struct devfs_entry *entry, *de;
+
+ devfs_get (dir);
+ if (dir == NULL) return NULL;
+ if ( !S_ISDIR (dir->mode) )
+ {
+ PRINTK ("(%p): not a directory\n", dir);
+ devfs_put (dir);
+ return NULL;
+ }
+ /* First search files in this directory */
+ read_lock (&dir->u.dir.lock);
+ for (entry = dir->u.dir.first; entry != NULL; entry = entry->next)
+ {
+ if ( !S_ISCHR (entry->mode) && !S_ISBLK (entry->mode) ) continue;
+ if ( S_ISCHR (entry->mode) && (type != DEVFS_SPECIAL_CHR) ) continue;
+ if ( S_ISBLK (entry->mode) && (type != DEVFS_SPECIAL_BLK) ) continue;
+ if ( (entry->u.fcb.u.device.major == major) &&
+ (entry->u.fcb.u.device.minor == minor) )
+ {
+ devfs_get (entry);
+ read_unlock (&dir->u.dir.lock);
+ devfs_put (dir);
+ return entry;
+ }
+ /* Not found: try the next one */
+ }
+ /* Now recursively search the subdirectories: this is a stack chomper */
+ for (entry = dir->u.dir.first; entry != NULL; entry = entry->next)
+ {
+ if ( !S_ISDIR (entry->mode) ) continue;
+ de = _devfs_find_by_dev (entry, major, minor, type);
+ if (de)
+ {
+ read_unlock (&dir->u.dir.lock);
+ devfs_put (dir);
+ return de;
+ }
+ }
+ read_unlock (&dir->u.dir.lock);
+ devfs_put (dir);
+ return NULL;
+} /* End Function _devfs_find_by_dev */
+
+
+/**
+ * _devfs_find_entry - Find a devfs entry.
+ * @dir: The handle to the parent devfs directory entry. If this is %NULL the
+ * name is relative to the root of the devfs.
+ * @name: The name of the entry. This may be %NULL.
+ * @major: The major number. This is used if lookup by @name fails.
+ * @minor: The minor number. This is used if lookup by @name fails.
+ * NOTE: If @major and @minor are both 0, searching by major and minor
+ * numbers is disabled.
+ * @type: The type of special file to search for. This may be either
+ * %DEVFS_SPECIAL_CHR or %DEVFS_SPECIAL_BLK.
+ * @traverse_symlink: If %TRUE then symbolic links are traversed.
+ *
+ * Returns the devfs_entry pointer on success, else %NULL. An implicit
+ * devfs_get() is performed.
+ */
+
+static struct devfs_entry *_devfs_find_entry (devfs_handle_t dir,
+ const char *name,
+ unsigned int major,
+ unsigned int minor,
+ char type, int traverse_symlink)
+{
+ struct devfs_entry *entry;
+
+ if (name != NULL)
+ {
+ unsigned int namelen = strlen (name);
+
+ if (name[0] == '/')
+ {
+ /* Skip leading pathname component */
+ if (namelen < 2)
+ {
+ PRINTK ("(%s): too short\n", name);
+ return NULL;
+ }
+ for (++name, --namelen; (*name != '/') && (namelen > 0);
+ ++name, --namelen);
+ if (namelen < 2)
+ {
+ PRINTK ("(%s): too short\n", name);
+ return NULL;
+ }
+ ++name;
+ --namelen;
+ }
+ entry = _devfs_walk_path (dir, name, namelen, traverse_symlink);
+ if (entry != NULL) return entry;
+ }
+ /* Have to search by major and minor: slow */
+ if ( (major == 0) && (minor == 0) ) return NULL;
+ return _devfs_find_by_dev (root_entry, major, minor, type);
+} /* End Function _devfs_find_entry */
+
+static struct devfs_entry *get_devfs_entry_from_vfs_inode (struct inode *inode)
+{
+ if (inode == NULL) return NULL;
+ VERIFY_ENTRY ( (struct devfs_entry *) inode->u.generic_ip );
+ return inode->u.generic_ip;
+} /* End Function get_devfs_entry_from_vfs_inode */
+
+
+/**
+ * free_dentry - Free the dentry for a device entry and invalidate inode.
+ * @de: The entry.
+ *
+ * This must only be called after the entry has been unhooked from it's
+ * parent directory.
+ */
+
+static void free_dentry (struct devfs_entry *de)
+{
+ struct dentry *dentry = de->inode.dentry;
+
+ if (!dentry) return;
+ spin_lock (&dcache_lock);
+ dget_locked (dentry);
+ spin_unlock (&dcache_lock);
+ /* Forcefully remove the inode */
+ if (dentry->d_inode != NULL) dentry->d_inode->i_nlink = 0;
+ d_drop (dentry);
+ dput (dentry);
+} /* End Function free_dentry */
+
+
+/**
+ * is_devfsd_or_child - Test if the current process is devfsd or one of its children.
+ * @fs_info: The filesystem information.
+ *
+ * Returns %TRUE if devfsd or child, else %FALSE.
+ */
+
+static int is_devfsd_or_child (struct fs_info *fs_info)
+{
+ struct task_struct *p;
+
+ if (current == fs_info->devfsd_task) return (TRUE);
+ if (current->pgrp == fs_info->devfsd_pgrp) return (TRUE);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,1)
+ for (p = current->p_opptr; p != &init_task; p = p->p_opptr)
+ {
+ if (p == fs_info->devfsd_task) return (TRUE);
+ }
+#endif
+ return (FALSE);
+} /* End Function is_devfsd_or_child */
+
+
+/**
+ * devfsd_queue_empty - Test if devfsd has work pending in its event queue.
+ * @fs_info: The filesystem information.
+ *
+ * Returns %TRUE if the queue is empty, else %FALSE.
+ */
+
+static inline int devfsd_queue_empty (struct fs_info *fs_info)
+{
+ return (fs_info->devfsd_last_event) ? FALSE : TRUE;
+} /* End Function devfsd_queue_empty */
+
+
+/**
+ * wait_for_devfsd_finished - Wait for devfsd to finish processing its event queue.
+ * @fs_info: The filesystem information.
+ *
+ * Returns %TRUE if no more waiting will be required, else %FALSE.
+ */
+
+static int wait_for_devfsd_finished (struct fs_info *fs_info)
+{
+ DECLARE_WAITQUEUE (wait, current);
+
+ if (fs_info->devfsd_task == NULL) return (TRUE);
+ if (devfsd_queue_empty (fs_info) && fs_info->devfsd_sleeping) return TRUE;
+ if ( is_devfsd_or_child (fs_info) ) return (FALSE);
+ set_current_state (TASK_UNINTERRUPTIBLE);
+ add_wait_queue (&fs_info->revalidate_wait_queue, &wait);
+ if (!devfsd_queue_empty (fs_info) || !fs_info->devfsd_sleeping)
+ if (fs_info->devfsd_task) schedule ();
+ remove_wait_queue (&fs_info->revalidate_wait_queue, &wait);
+ __set_current_state (TASK_RUNNING);
+ return (TRUE);
+} /* End Function wait_for_devfsd_finished */
+
+
+/**
+ * devfsd_notify_de - Notify the devfsd daemon of a change.
+ * @de: The devfs entry that has changed. This and all parent entries will
+ * have their reference counts incremented if the event was queued.
+ * @type: The type of change.
+ * @mode: The mode of the entry.
+ * @uid: The user ID.
+ * @gid: The group ID.
+ * @fs_info: The filesystem info.
+ *
+ * Returns %TRUE if an event was queued and devfsd woken up, else %FALSE.
+ */
+
+static int devfsd_notify_de (struct devfs_entry *de,
+ unsigned short type, umode_t mode,
+ uid_t uid, gid_t gid, struct fs_info *fs_info,
+ int atomic)
+{
+ struct devfsd_buf_entry *entry;
+ struct devfs_entry *curr;
+
+ if ( !( fs_info->devfsd_event_mask & (1 << type) ) ) return (FALSE);
+ if ( ( entry = kmem_cache_alloc (devfsd_buf_cache,
+ atomic ? SLAB_ATOMIC : SLAB_KERNEL) )
+ == NULL )
+ {
+ atomic_inc (&fs_info->devfsd_overrun_count);
+ return (FALSE);
+ }
+ for (curr = de; curr != NULL; curr = curr->parent) devfs_get (curr);
+ entry->de = de;
+ entry->type = type;
+ entry->mode = mode;
+ entry->uid = uid;
+ entry->gid = gid;
+ entry->next = NULL;
+ spin_lock (&fs_info->devfsd_buffer_lock);
+ if (!fs_info->devfsd_first_event) fs_info->devfsd_first_event = entry;
+ if (fs_info->devfsd_last_event) fs_info->devfsd_last_event->next = entry;
+ fs_info->devfsd_last_event = entry;
+ spin_unlock (&fs_info->devfsd_buffer_lock);
+ wake_up_interruptible (&fs_info->devfsd_wait_queue);
+ return (TRUE);
+} /* End Function devfsd_notify_de */
+
+
+/**
+ * devfsd_notify - Notify the devfsd daemon of a change.
+ * @de: The devfs entry that has changed.
+ * @type: The type of change event.
+ * @wait: If TRUE, the function waits for the daemon to finish processing
+ * the event.
+ */
+
+static void devfsd_notify (struct devfs_entry *de,unsigned short type,int wait)
+{
+ if (devfsd_notify_de (de, type, de->mode, current->euid,
+ current->egid, &fs_info, 0) && wait)
+ wait_for_devfsd_finished (&fs_info);
+} /* End Function devfsd_notify */
+
+
+/**
+ * devfs_register - Register a device entry.
+ * @dir: The handle to the parent devfs directory entry. If this is %NULL the
+ * new name is relative to the root of the devfs.
+ * @name: The name of the entry.
+ * @flags: A set of bitwise-ORed flags (DEVFS_FL_*).
+ * @major: The major number. Not needed for regular files.
+ * @minor: The minor number. Not needed for regular files.
+ * @mode: The default file mode.
+ * @ops: The &file_operations or &block_device_operations structure.
+ * This must not be externally deallocated.
+ * @info: An arbitrary pointer which will be written to the @private_data
+ * field of the &file structure passed to the device driver. You can set
+ * this to whatever you like, and change it once the file is opened (the next
+ * file opened will not see this change).
+ *
+ * Returns a handle which may later be used in a call to devfs_unregister().
+ * On failure %NULL is returned.
+ */
+
+devfs_handle_t devfs_register (devfs_handle_t dir, const char *name,
+ unsigned int flags,
+ unsigned int major, unsigned int minor,
+ umode_t mode, void *ops, void *info)
+{
+ char devtype = S_ISCHR (mode) ? DEVFS_SPECIAL_CHR : DEVFS_SPECIAL_BLK;
+ int err;
+ kdev_t devnum = NODEV;
+ struct devfs_entry *de;
+
+ if (name == NULL)
+ {
+ PRINTK ("(): NULL name pointer\n");
+ return NULL;
+ }
+ if (ops == NULL)
+ {
+ if ( S_ISBLK (mode) ) ops = (void *) get_blkfops (major);
+ if (ops == NULL)
+ {
+ PRINTK ("(%s): NULL ops pointer\n", name);
+ return NULL;
+ }
+ PRINTK ("(%s): NULL ops, got %p from major table\n", name, ops);
+ }
+ if ( S_ISDIR (mode) )
+ {
+ PRINTK ("(%s): creating directories is not allowed\n", name);
+ return NULL;
+ }
+ if ( S_ISLNK (mode) )
+ {
+ PRINTK ("(%s): creating symlinks is not allowed\n", name);
+ return NULL;
+ }
+ if ( ( S_ISCHR (mode) || S_ISBLK (mode) ) &&
+ (flags & DEVFS_FL_AUTO_DEVNUM) )
+ {
+ if ( kdev_none ( devnum = devfs_alloc_devnum (devtype) ) )
+ {
+ PRINTK ("(%s): exhausted %s device numbers\n",
+ name, S_ISCHR (mode) ? "char" : "block");
+ return NULL;
+ }
+ major = major (devnum);
+ minor = minor (devnum);
+ }
+ if ( ( de = _devfs_prepare_leaf (&dir, name, mode) ) == NULL )
+ {
+ PRINTK ("(%s): could not prepare leaf\n", name);
+ if ( !kdev_none (devnum) ) devfs_dealloc_devnum (devtype, devnum);
+ return NULL;
+ }
+ if ( S_ISCHR (mode) || S_ISBLK (mode) )
+ {
+ de->u.fcb.u.device.major = major;
+ de->u.fcb.u.device.minor = minor;
+ de->u.fcb.autogen = kdev_none (devnum) ? FALSE : TRUE;
+ }
+ else if ( !S_ISREG (mode) )
+ {
+ PRINTK ("(%s): illegal mode: %x\n", name, mode);
+ devfs_put (de);
+ devfs_put (dir);
+ return (NULL);
+ }
+ de->info = info;
+ if (flags & DEVFS_FL_CURRENT_OWNER)
+ {
+ de->inode.uid = current->uid;
+ de->inode.gid = current->gid;
+ }
+ else
+ {
+ de->inode.uid = 0;
+ de->inode.gid = 0;
+ }
+ de->u.fcb.ops = ops;
+ de->u.fcb.auto_owner = (flags & DEVFS_FL_AUTO_OWNER) ? TRUE : FALSE;
+ de->u.fcb.aopen_notify = (flags & DEVFS_FL_AOPEN_NOTIFY) ? TRUE : FALSE;
+ de->hide = (flags & DEVFS_FL_HIDE) ? TRUE : FALSE;
+ if (flags & DEVFS_FL_REMOVABLE) de->u.fcb.removable = TRUE;
+ if ( ( err = _devfs_append_entry (dir, de, de->u.fcb.removable, NULL) )
+ != 0 )
+ {
+ PRINTK ("(%s): could not append to parent, err: %d\n", name, err);
+ devfs_put (dir);
+ if ( !kdev_none (devnum) ) devfs_dealloc_devnum (devtype, devnum);
+ return NULL;
+ }
+ DPRINTK (DEBUG_REGISTER, "(%s): de: %p dir: %p \"%s\" pp: %p\n",
+ name, de, dir, dir->name, dir->parent);
+ devfsd_notify (de, DEVFSD_NOTIFY_REGISTERED, flags & DEVFS_FL_WAIT);
+ devfs_put (dir);
+ return de;
+} /* End Function devfs_register */
+
+
+/**
+ * _devfs_unhook - Unhook a device entry from its parents list
+ * @de: The entry to unhook.
+ *
+ * Returns %TRUE if the entry was unhooked, else %FALSE if it was
+ * previously unhooked.
+ * The caller must have a write lock on the parent directory.
+ */
+
+static int _devfs_unhook (struct devfs_entry *de)
+{
+ struct devfs_entry *parent;
+
+ if ( !de || (de->prev == de) ) return FALSE;
+ parent = de->parent;
+ if (de->prev == NULL) parent->u.dir.first = de->next;
+ else de->prev->next = de->next;
+ if (de->next == NULL) parent->u.dir.last = de->prev;
+ else de->next->prev = de->prev;
+ de->prev = de; /* Indicate we're unhooked */
+ de->next = NULL; /* Force early termination for <devfs_readdir> */
+ if ( ( S_ISREG (de->mode) || S_ISCHR (de->mode) || S_ISBLK (de->mode) ) &&
+ de->u.fcb.removable )
+ --parent->u.dir.num_removable;
+ return TRUE;
+} /* End Function _devfs_unhook */
+
+
+/**
+ * _devfs_unregister - Unregister a device entry from it's parent.
+ * @dir: The parent directory.
+ * @de: The entry to unregister.
+ *
+ * The caller must have a write lock on the parent directory, which is
+ * unlocked by this function.
+ */
+
+static void _devfs_unregister (struct devfs_entry *dir, struct devfs_entry *de)
+{
+ int unhooked = _devfs_unhook (de);
+
+ write_unlock (&dir->u.dir.lock);
+ if (!unhooked) return;
+ devfs_get (dir);
+ devfs_unregister (de->slave); /* Let it handle the locking */
+ devfsd_notify (de, DEVFSD_NOTIFY_UNREGISTERED, 0);
+ free_dentry (de);
+ devfs_put (dir);
+ if ( !S_ISDIR (de->mode) ) return;
+ while (TRUE) /* Recursively unregister: this is a stack chomper */
+ {
+ struct devfs_entry *child;
+
+ write_lock (&de->u.dir.lock);
+ de->u.dir.no_more_additions = TRUE;
+ child = de->u.dir.first;
+ VERIFY_ENTRY (child);
+ _devfs_unregister (de, child);
+ if (!child) break;
+ DPRINTK (DEBUG_UNREGISTER, "(%s): child: %p refcount: %d\n",
+ child->name, child, atomic_read (&child->refcount) );
+ devfs_put (child);
+ }
+} /* End Function _devfs_unregister */
+
+
+/**
+ * devfs_unregister - Unregister a device entry.
+ * @de: A handle previously created by devfs_register() or returned from
+ * devfs_get_handle(). If this is %NULL the routine does nothing.
+ */
+
+void devfs_unregister (devfs_handle_t de)
+{
+ VERIFY_ENTRY (de);
+ if ( (de == NULL) || (de->parent == NULL) ) return;
+ DPRINTK (DEBUG_UNREGISTER, "(%s): de: %p refcount: %d\n",
+ de->name, de, atomic_read (&de->refcount) );
+ write_lock (&de->parent->u.dir.lock);
+ _devfs_unregister (de->parent, de);
+ devfs_put (de);
+} /* End Function devfs_unregister */
+
+static int devfs_do_symlink (devfs_handle_t dir, const char *name,
+ unsigned int flags, const char *link,
+ devfs_handle_t *handle, void *info)
+{
+ int err;
+ unsigned int linklength;
+ char *newlink;
+ struct devfs_entry *de;
+
+ if (handle != NULL) *handle = NULL;
+ if (name == NULL)
+ {
+ PRINTK ("(): NULL name pointer\n");
+ return -EINVAL;
+ }
+ if (link == NULL)
+ {
+ PRINTK ("(%s): NULL link pointer\n", name);
+ return -EINVAL;
+ }
+ linklength = strlen (link);
+ if ( ( newlink = kmalloc (linklength + 1, GFP_KERNEL) ) == NULL )
+ return -ENOMEM;
+ memcpy (newlink, link, linklength);
+ newlink[linklength] = '\0';
+ if ( ( de = _devfs_prepare_leaf (&dir, name, S_IFLNK | S_IRUGO | S_IXUGO) )
+ == NULL )
+ {
+ PRINTK ("(%s): could not prepare leaf\n", name);
+ kfree (newlink);
+ return -ENOTDIR;
+ }
+ de->info = info;
+ de->hide = (flags & DEVFS_FL_HIDE) ? TRUE : FALSE;
+ de->u.symlink.linkname = newlink;
+ de->u.symlink.length = linklength;
+ if ( ( err = _devfs_append_entry (dir, de, FALSE, NULL) ) != 0 )
+ {
+ PRINTK ("(%s): could not append to parent, err: %d\n", name, err);
+ devfs_put (dir);
+ return err;
+ }
+ devfs_put (dir);
+#ifdef CONFIG_DEVFS_DEBUG
+ spin_lock (&stat_lock);
+ stat_num_bytes += linklength + 1;
+ spin_unlock (&stat_lock);
+#endif
+ if (handle != NULL) *handle = de;
+ return 0;
+} /* End Function devfs_do_symlink */
+
+
+/**
+ * devfs_mk_symlink Create a symbolic link in the devfs namespace.
+ * @dir: The handle to the parent devfs directory entry. If this is %NULL the
+ * new name is relative to the root of the devfs.
+ * @name: The name of the entry.
+ * @flags: A set of bitwise-ORed flags (DEVFS_FL_*).
+ * @link: The destination name.
+ * @handle: The handle to the symlink entry is written here. This may be %NULL.
+ * @info: An arbitrary pointer which will be associated with the entry.
+ *
+ * Returns 0 on success, else a negative error code is returned.
+ */
+
+int devfs_mk_symlink (devfs_handle_t dir, const char *name, unsigned int flags,
+ const char *link, devfs_handle_t *handle, void *info)
+{
+ int err;
+ devfs_handle_t de;
+
+ if (handle != NULL) *handle = NULL;
+ DPRINTK (DEBUG_REGISTER, "(%s)\n", name);
+ err = devfs_do_symlink (dir, name, flags, link, &de, info);
+ if (err) return err;
+ if (handle == NULL) de->vfs_deletable = TRUE;
+ else *handle = de;
+ devfsd_notify (de, DEVFSD_NOTIFY_REGISTERED, flags & DEVFS_FL_WAIT);
+ return 0;
+} /* End Function devfs_mk_symlink */
+
+
+/**
+ * devfs_mk_dir - Create a directory in the devfs namespace.
+ * @dir: The handle to the parent devfs directory entry. If this is %NULL the
+ * new name is relative to the root of the devfs.
+ * @name: The name of the entry.
+ * @info: An arbitrary pointer which will be associated with the entry.
+ *
+ * Use of this function is optional. The devfs_register() function
+ * will automatically create intermediate directories as needed. This function
+ * is provided for efficiency reasons, as it provides a handle to a directory.
+ * Returns a handle which may later be used in a call to devfs_unregister().
+ * On failure %NULL is returned.
+ */
+
+devfs_handle_t devfs_mk_dir (devfs_handle_t dir, const char *name, void *info)
+{
+ int err;
+ struct devfs_entry *de, *old;
+
+ if (name == NULL)
+ {
+ PRINTK ("(): NULL name pointer\n");
+ return NULL;
+ }
+ if ( ( de = _devfs_prepare_leaf (&dir, name, MODE_DIR) ) == NULL )
+ {
+ PRINTK ("(%s): could not prepare leaf\n", name);
+ return NULL;
+ }
+ de->info = info;
+ if ( ( err = _devfs_append_entry (dir, de, FALSE, &old) ) != 0 )
+ {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,1)
+ if ( old && S_ISDIR (old->mode) )
+ {
+ PRINTK ("(%s): using old entry in dir: %p \"%s\"\n",
+ name, dir, dir->name);
+ old->vfs_deletable = FALSE;
+ devfs_put (dir);
+ return old;
+ }
+#endif
+ PRINTK ("(%s): could not append to dir: %p \"%s\", err: %d\n",
+ name, dir, dir->name, err);
+ devfs_put (old);
+ devfs_put (dir);
+ return NULL;
+ }
+ DPRINTK (DEBUG_REGISTER, "(%s): de: %p dir: %p \"%s\"\n",
+ name, de, dir, dir->name);
+ devfsd_notify (de, DEVFSD_NOTIFY_REGISTERED, 0);
+ devfs_put (dir);
+ return de;
+} /* End Function devfs_mk_dir */
+
+
+/**
+ * devfs_get_handle - Find the handle of a devfs entry.
+ * @dir: The handle to the parent devfs directory entry. If this is %NULL the
+ * name is relative to the root of the devfs.
+ * @name: The name of the entry.
+ * @major: The major number. This is used if @name is %NULL.
+ * @minor: The minor number. This is used if @name is %NULL.
+ * @type: The type of special file to search for. This may be either
+ * %DEVFS_SPECIAL_CHR or %DEVFS_SPECIAL_BLK.
+ * @traverse_symlinks: If %TRUE then symlink entries in the devfs namespace are
+ * traversed. Symlinks pointing out of the devfs namespace will cause a
+ * failure. Symlink traversal consumes stack space.
+ *
+ * Returns a handle which may later be used in a call to
+ * devfs_unregister(), devfs_get_flags(), or devfs_set_flags(). A
+ * subsequent devfs_put() is required to decrement the refcount.
+ * On failure %NULL is returned.
+ */
+
+devfs_handle_t devfs_get_handle (devfs_handle_t dir, const char *name,
+ unsigned int major, unsigned int minor,
+ char type, int traverse_symlinks)
+{
+ if ( (name != NULL) && (name[0] == '\0') ) name = NULL;
+ return _devfs_find_entry (dir, name, major, minor, type,traverse_symlinks);
+} /* End Function devfs_get_handle */
+
+
+/* Compatibility function. Will be removed in sometime in 2.5 */
+
+devfs_handle_t devfs_find_handle (devfs_handle_t dir, const char *name,
+ unsigned int major, unsigned int minor,
+ char type, int traverse_symlinks)
+{
+ devfs_handle_t de;
+
+ de = devfs_get_handle (dir, name, major, minor, type, traverse_symlinks);
+ devfs_put (de);
+ return de;
+} /* End Function devfs_find_handle */
+
+
+/**
+ * devfs_get_flags - Get the flags for a devfs entry.
+ * @de: The handle to the device entry.
+ * @flags: The flags are written here.
+ *
+ * Returns 0 on success, else a negative error code.
+ */
+
+int devfs_get_flags (devfs_handle_t de, unsigned int *flags)
+{
+ unsigned int fl = 0;
+
+ if (de == NULL) return -EINVAL;
+ VERIFY_ENTRY (de);
+ if (de->hide) fl |= DEVFS_FL_HIDE;
+ if ( S_ISCHR (de->mode) || S_ISBLK (de->mode) || S_ISREG (de->mode) )
+ {
+ if (de->u.fcb.auto_owner) fl |= DEVFS_FL_AUTO_OWNER;
+ if (de->u.fcb.aopen_notify) fl |= DEVFS_FL_AOPEN_NOTIFY;
+ if (de->u.fcb.removable) fl |= DEVFS_FL_REMOVABLE;
+ }
+ *flags = fl;
+ return 0;
+} /* End Function devfs_get_flags */
+
+
+/*
+ * devfs_set_flags - Set the flags for a devfs entry.
+ * @de: The handle to the device entry.
+ * @flags: The flags to set. Unset flags are cleared.
+ *
+ * Returns 0 on success, else a negative error code.
+ */
+
+int devfs_set_flags (devfs_handle_t de, unsigned int flags)
+{
+ if (de == NULL) return -EINVAL;
+ VERIFY_ENTRY (de);
+ DPRINTK (DEBUG_SET_FLAGS, "(%s): flags: %x\n", de->name, flags);
+ de->hide = (flags & DEVFS_FL_HIDE) ? TRUE : FALSE;
+ if ( S_ISCHR (de->mode) || S_ISBLK (de->mode) || S_ISREG (de->mode) )
+ {
+ de->u.fcb.auto_owner = (flags & DEVFS_FL_AUTO_OWNER) ? TRUE : FALSE;
+ de->u.fcb.aopen_notify = (flags & DEVFS_FL_AOPEN_NOTIFY) ? TRUE:FALSE;
+ }
+ return 0;
+} /* End Function devfs_set_flags */
+
+
+/**
+ * devfs_get_maj_min - Get the major and minor numbers for a devfs entry.
+ * @de: The handle to the device entry.
+ * @major: The major number is written here. This may be %NULL.
+ * @minor: The minor number is written here. This may be %NULL.
+ *
+ * Returns 0 on success, else a negative error code.
+ */
+
+int devfs_get_maj_min (devfs_handle_t de, unsigned int *major,
+ unsigned int *minor)
+{
+ if (de == NULL) return -EINVAL;
+ VERIFY_ENTRY (de);
+ if ( S_ISDIR (de->mode) ) return -EISDIR;
+ if ( !S_ISCHR (de->mode) && !S_ISBLK (de->mode) ) return -EINVAL;
+ if (major != NULL) *major = de->u.fcb.u.device.major;
+ if (minor != NULL) *minor = de->u.fcb.u.device.minor;
+ return 0;
+} /* End Function devfs_get_maj_min */
+
+
+/**
+ * devfs_get_handle_from_inode - Get the devfs handle for a VFS inode.
+ * @inode: The VFS inode.
+ *
+ * Returns the devfs handle on success, else %NULL.
+ */
+
+devfs_handle_t devfs_get_handle_from_inode (struct inode *inode)
+{
+ if (!inode || !inode->i_sb) return NULL;
+ if (inode->i_sb->s_magic != DEVFS_SUPER_MAGIC) return NULL;
+ return get_devfs_entry_from_vfs_inode (inode);
+} /* End Function devfs_get_handle_from_inode */
+
+
+/**
+ * devfs_generate_path - Generate a pathname for an entry, relative to the devfs root.
+ * @de: The devfs entry.
+ * @path: The buffer to write the pathname to. The pathname and '\0'
+ * terminator will be written at the end of the buffer.
+ * @buflen: The length of the buffer.
+ *
+ * Returns the offset in the buffer where the pathname starts on success,
+ * else a negative error code.
+ */
+
+int devfs_generate_path (devfs_handle_t de, char *path, int buflen)
+{
+ int pos;
+#define NAMEOF(de) ( (de)->mode ? (de)->name : (de)->u.name )
+
+ if (de == NULL) return -EINVAL;
+ VERIFY_ENTRY (de);
+ if (de->namelen >= buflen) return -ENAMETOOLONG; /* Must be first */
+ path[buflen - 1] = '\0';
+ if (de->parent == NULL) return buflen - 1; /* Don't prepend root */
+ pos = buflen - de->namelen - 1;
+ memcpy (path + pos, NAMEOF (de), de->namelen);
+ for (de = de->parent; de->parent != NULL; de = de->parent)
+ {
+ if (pos - de->namelen - 1 < 0) return -ENAMETOOLONG;
+ path[--pos] = '/';
+ pos -= de->namelen;
+ memcpy (path + pos, NAMEOF (de), de->namelen);
+ }
+ return pos;
+} /* End Function devfs_generate_path */
+
+
+/**
+ * devfs_get_ops - Get the device operations for a devfs entry.
+ * @de: The handle to the device entry.
+ *
+ * Returns a pointer to the device operations on success, else NULL.
+ * The use count for the module owning the operations will be incremented.
+ */
+
+void *devfs_get_ops (devfs_handle_t de)
+{
+ struct module *owner;
+
+ if (de == NULL) return NULL;
+ VERIFY_ENTRY (de);
+ if ( !S_ISCHR (de->mode) && !S_ISBLK (de->mode) && !S_ISREG (de->mode) )
+ return NULL;
+ if (de->u.fcb.ops == NULL) return NULL;
+ read_lock (&de->parent->u.dir.lock); /* Prevent module from unloading */
+ if (de->next == de) owner = NULL; /* Ops pointer is already stale */
+ else if ( S_ISCHR (de->mode) || S_ISREG (de->mode) )
+ owner = ( (struct file_operations *) de->u.fcb.ops )->owner;
+ else owner = ( (struct block_device_operations *) de->u.fcb.ops )->owner;
+ if ( (de->next == de) || !try_inc_mod_count (owner) )
+ { /* Entry is already unhooked or module is unloading */
+ read_unlock (&de->parent->u.dir.lock);
+ return NULL;
+ }
+ read_unlock (&de->parent->u.dir.lock); /* Module can continue unloading*/
+ return de->u.fcb.ops;
+} /* End Function devfs_get_ops */
+
+
+/**
+ * devfs_put_ops - Put the device operations for a devfs entry.
+ * @de: The handle to the device entry.
+ *
+ * The use count for the module owning the operations will be decremented.
+ */
+
+void devfs_put_ops (devfs_handle_t de)
+{
+ struct module *owner;
+
+ if (de == NULL) return;
+ VERIFY_ENTRY (de);
+ if ( !S_ISCHR (de->mode) && !S_ISBLK (de->mode) && !S_ISREG (de->mode) )
+ return;
+ if (de->u.fcb.ops == NULL) return;
+ if ( S_ISCHR (de->mode) || S_ISREG (de->mode) )
+ owner = ( (struct file_operations *) de->u.fcb.ops )->owner;
+ else owner = ( (struct block_device_operations *) de->u.fcb.ops )->owner;
+ if (owner) __MOD_DEC_USE_COUNT (owner);
+} /* End Function devfs_put_ops */
+
+
+/**
+ * devfs_set_file_size - Set the file size for a devfs regular file.
+ * @de: The handle to the device entry.
+ * @size: The new file size.
+ *
+ * Returns 0 on success, else a negative error code.
+ */
+
+int devfs_set_file_size (devfs_handle_t de, unsigned long size)
+{
+ if (de == NULL) return -EINVAL;
+ VERIFY_ENTRY (de);
+ if ( !S_ISREG (de->mode) ) return -EINVAL;
+ if (de->u.fcb.u.file.size == size) return 0;
+ de->u.fcb.u.file.size = size;
+ if (de->inode.dentry == NULL) return 0;
+ if (de->inode.dentry->d_inode == NULL) return 0;
+ de->inode.dentry->d_inode->i_size = size;
+ return 0;
+} /* End Function devfs_set_file_size */
+
+
+/**
+ * devfs_get_info - Get the info pointer written to private_data of @de upon open.
+ * @de: The handle to the device entry.
+ *
+ * Returns the info pointer.
+ */
+void *devfs_get_info (devfs_handle_t de)
+{
+ if (de == NULL) return NULL;
+ VERIFY_ENTRY (de);
+ return de->info;
+} /* End Function devfs_get_info */
+
+
+/**
+ * devfs_set_info - Set the info pointer written to private_data upon open.
+ * @de: The handle to the device entry.
+ * @info: pointer to the data
+ *
+ * Returns 0 on success, else a negative error code.
+ */
+int devfs_set_info (devfs_handle_t de, void *info)
+{
+ if (de == NULL) return -EINVAL;
+ VERIFY_ENTRY (de);
+ de->info = info;
+ return 0;
+} /* End Function devfs_set_info */
+
+
+/**
+ * devfs_get_parent - Get the parent device entry.
+ * @de: The handle to the device entry.
+ *
+ * Returns the parent device entry if it exists, else %NULL.
+ */
+devfs_handle_t devfs_get_parent (devfs_handle_t de)
+{
+ if (de == NULL) return NULL;
+ VERIFY_ENTRY (de);
+ return de->parent;
+} /* End Function devfs_get_parent */
+
+
+/**
+ * devfs_get_first_child - Get the first leaf node in a directory.
+ * @de: The handle to the device entry.
+ *
+ * Returns the leaf node device entry if it exists, else %NULL.
+ */
+
+devfs_handle_t devfs_get_first_child (devfs_handle_t de)
+{
+ if (de == NULL) return NULL;
+ VERIFY_ENTRY (de);
+ if ( !S_ISDIR (de->mode) ) return NULL;
+ return de->u.dir.first;
+} /* End Function devfs_get_first_child */
+
+
+/**
+ * devfs_get_next_sibling - Get the next sibling leaf node. for a device entry.
+ * @de: The handle to the device entry.
+ *
+ * Returns the leaf node device entry if it exists, else %NULL.
+ */
+
+devfs_handle_t devfs_get_next_sibling (devfs_handle_t de)
+{
+ if (de == NULL) return NULL;
+ VERIFY_ENTRY (de);
+ return de->next;
+} /* End Function devfs_get_next_sibling */
+
+
+/**
+ * devfs_auto_unregister - Configure a devfs entry to be automatically unregistered.
+ * @master: The master devfs entry. Only one slave may be registered.
+ * @slave: The devfs entry which will be automatically unregistered when the
+ * master entry is unregistered. It is illegal to call devfs_unregister()
+ * on this entry.
+ */
+
+void devfs_auto_unregister (devfs_handle_t master, devfs_handle_t slave)
+{
+ if (master == NULL) return;
+ VERIFY_ENTRY (master);
+ VERIFY_ENTRY (slave);
+ if (master->slave != NULL)
+ {
+ /* Because of the dumbness of the layers above, ignore duplicates */
+ if (master->slave == slave) return;
+ PRINTK ("(%s): only one slave allowed\n", master->name);
+ OOPS ("(): old slave: \"%s\" new slave: \"%s\"\n",
+ master->slave->name, slave->name);
+ }
+ master->slave = slave;
+} /* End Function devfs_auto_unregister */
+
+
+/**
+ * devfs_get_unregister_slave - Get the slave entry which will be automatically unregistered.
+ * @master: The master devfs entry.
+ *
+ * Returns the slave which will be unregistered when @master is unregistered.
+ */
+
+devfs_handle_t devfs_get_unregister_slave (devfs_handle_t master)
+{
+ if (master == NULL) return NULL;
+ VERIFY_ENTRY (master);
+ return master->slave;
+} /* End Function devfs_get_unregister_slave */
+
+
+/**
+ * devfs_get_name - Get the name for a device entry in its parent directory.
+ * @de: The handle to the device entry.
+ * @namelen: The length of the name is written here. This may be %NULL.
+ *
+ * Returns the name on success, else %NULL.
+ */
+
+const char *devfs_get_name (devfs_handle_t de, unsigned int *namelen)
+{
+ if (de == NULL) return NULL;
+ VERIFY_ENTRY (de);
+ if (namelen != NULL) *namelen = de->namelen;
+ return de->name;
+} /* End Function devfs_get_name */
+
+
+/**
+ * devfs_register_chrdev - Optionally register a conventional character driver.
+ * @major: The major number for the driver.
+ * @name: The name of the driver (as seen in /proc/devices).
+ * @fops: The &file_operations structure pointer.
+ *
+ * This function will register a character driver provided the "devfs=only"
+ * option was not provided at boot time.
+ * Returns 0 on success, else a negative error code on failure.
+ */
+
+int devfs_register_chrdev (unsigned int major, const char *name,
+ struct file_operations *fops)
+{
+ if (boot_options & OPTION_ONLY) return 0;
+ return register_chrdev (major, name, fops);
+} /* End Function devfs_register_chrdev */
+
+
+/**
+ * devfs_register_blkdev - Optionally register a conventional block driver.
+ * @major: The major number for the driver.
+ * @name: The name of the driver (as seen in /proc/devices).
+ * @bdops: The &block_device_operations structure pointer.
+ *
+ * This function will register a block driver provided the "devfs=only"
+ * option was not provided at boot time.
+ * Returns 0 on success, else a negative error code on failure.
+ */
+
+int devfs_register_blkdev (unsigned int major, const char *name,
+ struct block_device_operations *bdops)
+{
+ if (boot_options & OPTION_ONLY) return 0;
+ return register_blkdev (major, name, bdops);
+} /* End Function devfs_register_blkdev */
+
+
+/**
+ * devfs_unregister_chrdev - Optionally unregister a conventional character driver.
+ * @major: The major number for the driver.
+ * @name: The name of the driver (as seen in /proc/devices).
+ *
+ * This function will unregister a character driver provided the "devfs=only"
+ * option was not provided at boot time.
+ * Returns 0 on success, else a negative error code on failure.
+ */
+
+int devfs_unregister_chrdev (unsigned int major, const char *name)
+{
+ if (boot_options & OPTION_ONLY) return 0;
+ return unregister_chrdev (major, name);
+} /* End Function devfs_unregister_chrdev */
+
+
+/**
+ * devfs_unregister_blkdev - Optionally unregister a conventional block driver.
+ * @major: The major number for the driver.
+ * @name: The name of the driver (as seen in /proc/devices).
+ *
+ * This function will unregister a block driver provided the "devfs=only"
+ * option was not provided at boot time.
+ * Returns 0 on success, else a negative error code on failure.
+ */
+
+int devfs_unregister_blkdev (unsigned int major, const char *name)
+{
+ if (boot_options & OPTION_ONLY) return 0;
+ return unregister_blkdev (major, name);
+} /* End Function devfs_unregister_blkdev */
+
+/**
+ * devfs_setup - Process kernel boot options.
+ * @str: The boot options after the "devfs=".
+ */
+
+static int __init devfs_setup (char *str)
+{
+ static struct
+ {
+ char *name;
+ unsigned int mask;
+ unsigned int *opt;
+ } devfs_options_tab[] __initdata =
+ {
+#ifdef CONFIG_DEVFS_DEBUG
+ {"dall", DEBUG_ALL, &devfs_debug_init},
+ {"dmod", DEBUG_MODULE_LOAD, &devfs_debug_init},
+ {"dreg", DEBUG_REGISTER, &devfs_debug_init},
+ {"dunreg", DEBUG_UNREGISTER, &devfs_debug_init},
+ {"dfree", DEBUG_FREE, &devfs_debug_init},
+ {"diget", DEBUG_I_GET, &devfs_debug_init},
+ {"dchange", DEBUG_SET_FLAGS, &devfs_debug_init},
+ {"dsread", DEBUG_S_READ, &devfs_debug_init},
+ {"dichange", DEBUG_I_CHANGE, &devfs_debug_init},
+ {"dimknod", DEBUG_I_MKNOD, &devfs_debug_init},
+ {"dilookup", DEBUG_I_LOOKUP, &devfs_debug_init},
+ {"diunlink", DEBUG_I_UNLINK, &devfs_debug_init},
+#endif /* CONFIG_DEVFS_DEBUG */
+ {"only", OPTION_ONLY, &boot_options},
+ {"mount", OPTION_MOUNT, &boot_options},
+ {NULL, 0, NULL}
+ };
+
+ while ( (*str != '\0') && !isspace (*str) )
+ {
+ int i, found = 0, invert = 0;
+
+ if (strncmp (str, "no", 2) == 0)
+ {
+ invert = 1;
+ str += 2;
+ }
+ for (i = 0; devfs_options_tab[i].name != NULL; i++)
+ {
+ int len = strlen (devfs_options_tab[i].name);
+
+ if (strncmp (str, devfs_options_tab[i].name, len) == 0)
+ {
+ if (invert)
+ *devfs_options_tab[i].opt &= ~devfs_options_tab[i].mask;
+ else
+ *devfs_options_tab[i].opt |= devfs_options_tab[i].mask;
+ str += len;
+ found = 1;
+ break;
+ }
+ }
+ if (!found) return 0; /* No match */
+ if (*str != ',') return 0; /* No more options */
+ ++str;
+ }
+ return 1;
+} /* End Function devfs_setup */
+
+__setup("devfs=", devfs_setup);
+
+EXPORT_SYMBOL(devfs_put);
+EXPORT_SYMBOL(devfs_register);
+EXPORT_SYMBOL(devfs_unregister);
+EXPORT_SYMBOL(devfs_mk_symlink);
+EXPORT_SYMBOL(devfs_mk_dir);
+EXPORT_SYMBOL(devfs_get_handle);
+EXPORT_SYMBOL(devfs_find_handle);
+EXPORT_SYMBOL(devfs_get_flags);
+EXPORT_SYMBOL(devfs_set_flags);
+EXPORT_SYMBOL(devfs_get_maj_min);
+EXPORT_SYMBOL(devfs_get_handle_from_inode);
+EXPORT_SYMBOL(devfs_generate_path);
+EXPORT_SYMBOL(devfs_get_ops);
+EXPORT_SYMBOL(devfs_set_file_size);
+EXPORT_SYMBOL(devfs_get_info);
+EXPORT_SYMBOL(devfs_set_info);
+EXPORT_SYMBOL(devfs_get_parent);
+EXPORT_SYMBOL(devfs_get_first_child);
+EXPORT_SYMBOL(devfs_get_next_sibling);
+EXPORT_SYMBOL(devfs_auto_unregister);
+EXPORT_SYMBOL(devfs_get_unregister_slave);
+EXPORT_SYMBOL(devfs_get_name);
+EXPORT_SYMBOL(devfs_register_chrdev);
+EXPORT_SYMBOL(devfs_register_blkdev);
+EXPORT_SYMBOL(devfs_unregister_chrdev);
+EXPORT_SYMBOL(devfs_unregister_blkdev);
+
+
+/**
+ * try_modload - Notify devfsd of an inode lookup by a non-devfsd process.
+ * @parent: The parent devfs entry.
+ * @fs_info: The filesystem info.
+ * @name: The device name.
+ * @namelen: The number of characters in @name.
+ * @buf: A working area that will be used. This must not go out of scope
+ * until devfsd is idle again.
+ *
+ * Returns 0 on success (event was queued), else a negative error code.
+ */
+
+static int try_modload (struct devfs_entry *parent, struct fs_info *fs_info,
+ const char *name, unsigned namelen,
+ struct devfs_entry *buf)
+{
+ if ( !( fs_info->devfsd_event_mask & (1 << DEVFSD_NOTIFY_LOOKUP) ) )
+ return -ENOENT;
+ if ( is_devfsd_or_child (fs_info) ) return -ENOENT;
+ memset (buf, 0, sizeof *buf);
+ atomic_set (&buf->refcount, 1);
+ buf->parent = parent;
+ buf->namelen = namelen;
+ buf->u.name = name;
+ WRITE_ENTRY_MAGIC (buf, MAGIC_VALUE);
+ if ( !devfsd_notify_de (buf, DEVFSD_NOTIFY_LOOKUP, 0,
+ current->euid, current->egid, fs_info, 0) )
+ return -ENOENT;
+ /* Possible success: event has been queued */
+ return 0;
+} /* End Function try_modload */
+
+
+/**
+ * check_disc_changed - Check if a removable disc was changed.
+ * @de: The device.
+ *
+ * Returns 1 if the media was changed, else 0.
+ *
+ * This function may block, and may indirectly cause the parent directory
+ * contents to be changed due to partition re-reading.
+ */
+
+static int check_disc_changed (struct devfs_entry *de)
+{
+ int tmp;
+ int retval = 0;
+ kdev_t dev = mk_kdev (de->u.fcb.u.device.major, de->u.fcb.u.device.minor);
+ struct block_device_operations *bdops;
+ extern int warn_no_part;
+
+ if ( !S_ISBLK (de->mode) ) return 0;
+ bdops = devfs_get_ops (de);
+ if (!bdops) return 0;
+ if (bdops->check_media_change == NULL) goto out;
+ if ( !bdops->check_media_change (dev) ) goto out;
+ retval = 1;
+ printk (KERN_DEBUG "VFS: Disk change detected on device %s\n",
+ kdevname (dev) );
+ if ( invalidate_device (dev, 0) )
+ printk (KERN_WARNING "VFS: busy inodes on changed media..\n");
+ /* Ugly hack to disable messages about unable to read partition table */
+ tmp = warn_no_part;
+ warn_no_part = 0;
+ if (bdops->revalidate) bdops->revalidate (dev);
+ warn_no_part = tmp;
+out:
+ devfs_put_ops (de);
+ return retval;
+} /* End Function check_disc_changed */
+
+
+/**
+ * scan_dir_for_removable - Scan a directory for removable media devices and check media.
+ * @dir: The directory.
+ *
+ * This function may block, and may indirectly cause the directory
+ * contents to be changed due to partition re-reading. The directory will
+ * be locked for reading.
+ */
+
+static void scan_dir_for_removable (struct devfs_entry *dir)
+{
+ struct devfs_entry *de;
+
+ read_lock (&dir->u.dir.lock);
+ if (dir->u.dir.num_removable < 1) de = NULL;
+ else
+ {
+ for (de = dir->u.dir.first; de != NULL; de = de->next)
+ {
+ if (S_ISBLK (de->mode) && de->u.fcb.removable) break;
+ }
+ devfs_get (de);
+ }
+ read_unlock (&dir->u.dir.lock);
+ if (de) check_disc_changed (de);
+ devfs_put (de);
+} /* End Function scan_dir_for_removable */
+
+/**
+ * get_removable_partition - Get removable media partition.
+ * @dir: The parent directory.
+ * @name: The name of the entry.
+ * @namelen: The number of characters in <<name>>.
+ *
+ * Returns 1 if the media was changed, else 0.
+ *
+ * This function may block, and may indirectly cause the directory
+ * contents to be changed due to partition re-reading. The directory must
+ * be locked for reading upon entry, and will be unlocked upon exit.
+ */
+
+static int get_removable_partition (struct devfs_entry *dir, const char *name,
+ unsigned int namelen)
+{
+ int retval;
+ struct devfs_entry *de;
+
+ if (dir->u.dir.num_removable < 1)
+ {
+ read_unlock (&dir->u.dir.lock);
+ return 0;
+ }
+ for (de = dir->u.dir.first; de != NULL; de = de->next)
+ {
+ if (!S_ISBLK (de->mode) || !de->u.fcb.removable) continue;
+ if (strcmp (de->name, "disc") == 0) break;
+ /* Support for names where the partition is appended to the disc name
+ */
+ if (de->namelen >= namelen) continue;
+ if (strncmp (de->name, name, de->namelen) == 0) break;
+ }
+ devfs_get (de);
+ read_unlock (&dir->u.dir.lock);
+ retval = de ? check_disc_changed (de) : 0;
+ devfs_put (de);
+ return retval;
+} /* End Function get_removable_partition */
+
+
+/* Superblock operations follow */
+
+static struct inode_operations devfs_iops;
+static struct inode_operations devfs_dir_iops;
+static struct file_operations devfs_fops;
+static struct file_operations devfs_dir_fops;
+static struct inode_operations devfs_symlink_iops;
+
+static int devfs_notify_change (struct dentry *dentry, struct iattr *iattr)
+{
+ int retval;
+ struct devfs_entry *de;
+ struct inode *inode = dentry->d_inode;
+ struct fs_info *fs_info = inode->i_sb->u.generic_sbp;
+
+ de = get_devfs_entry_from_vfs_inode (inode);
+ if (de == NULL) return -ENODEV;
+ retval = inode_change_ok (inode, iattr);
+ if (retval != 0) return retval;
+ retval = inode_setattr (inode, iattr);
+ if (retval != 0) return retval;
+ DPRINTK (DEBUG_I_CHANGE, "(%d): VFS inode: %p devfs_entry: %p\n",
+ (int) inode->i_ino, inode, de);
+ DPRINTK (DEBUG_I_CHANGE, "(): mode: 0%o uid: %d gid: %d\n",
+ (int) inode->i_mode, (int) inode->i_uid, (int) inode->i_gid);
+ /* Inode is not on hash chains, thus must save permissions here rather
+ than in a write_inode() method */
+ if ( ( !S_ISREG (inode->i_mode) && !S_ISCHR (inode->i_mode) &&
+ !S_ISBLK (inode->i_mode) ) || !de->u.fcb.auto_owner )
+ {
+ de->mode = inode->i_mode;
+ de->inode.uid = inode->i_uid;
+ de->inode.gid = inode->i_gid;
+ }
+ de->inode.atime = inode->i_atime;
+ de->inode.mtime = inode->i_mtime;
+ de->inode.ctime = inode->i_ctime;
+ if ( ( iattr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID) ) &&
+ !is_devfsd_or_child (fs_info) )
+ devfsd_notify_de (de, DEVFSD_NOTIFY_CHANGE, inode->i_mode,
+ inode->i_uid, inode->i_gid, fs_info, 0);
+ return 0;
+} /* End Function devfs_notify_change */
+
+static int devfs_statfs (struct super_block *sb, struct statfs *buf)
+{
+ buf->f_type = DEVFS_SUPER_MAGIC;
+ buf->f_bsize = FAKE_BLOCK_SIZE;
+ buf->f_bfree = 0;
+ buf->f_bavail = 0;
+ buf->f_ffree = 0;
+ buf->f_namelen = NAME_MAX;
+ return 0;
+} /* End Function devfs_statfs */
+
+static void devfs_clear_inode (struct inode *inode)
+{
+ if ( S_ISBLK (inode->i_mode) ) bdput (inode->i_bdev);
+} /* End Function devfs_clear_inode */
+
+static struct super_operations devfs_sops =
+{
+ .put_inode = force_delete,
+ .clear_inode = devfs_clear_inode,
+ .statfs = devfs_statfs,
+};
+
+
+/**
+ * _devfs_get_vfs_inode - Get a VFS inode.
+ * @sb: The super block.
+ * @de: The devfs inode.
+ * @dentry: The dentry to register with the devfs inode.
+ *
+ * Returns the inode on success, else %NULL. An implicit devfs_get() is
+ * performed if the inode is created.
+ */
+
+static struct inode *_devfs_get_vfs_inode (struct super_block *sb,
+ struct devfs_entry *de,
+ struct dentry *dentry)
+{
+ int is_fcb = FALSE;
+ struct inode *inode;
+
+ if (de->prev == de) return NULL; /* Quick check to see if unhooked */
+ if ( ( inode = new_inode (sb) ) == NULL )
+ {
+ PRINTK ("(%s): new_inode() failed, de: %p\n", de->name, de);
+ return NULL;
+ }
+ if (de->parent)
+ {
+ read_lock (&de->parent->u.dir.lock);
+ if (de->prev != de) de->inode.dentry = dentry; /* Not unhooked */
+ read_unlock (&de->parent->u.dir.lock);
+ }
+ else de->inode.dentry = dentry; /* Root: no locking needed */
+ if (de->inode.dentry != dentry)
+ { /* Must have been unhooked */
+ iput (inode);
+ return NULL;
+ }
+ inode->u.generic_ip = devfs_get (de);
+ inode->i_ino = de->inode.ino;
+ DPRINTK (DEBUG_I_GET, "(%d): VFS inode: %p devfs_entry: %p\n",
+ (int) inode->i_ino, inode, de);
+ inode->i_blocks = 0;
+ inode->i_blksize = FAKE_BLOCK_SIZE;
+ inode->i_op = &devfs_iops;
+ inode->i_fop = &devfs_fops;
+ inode->i_rdev = NODEV;
+ if ( S_ISCHR (de->mode) )
+ {
+ inode->i_rdev = mk_kdev (de->u.fcb.u.device.major,
+ de->u.fcb.u.device.minor);
+ inode->i_cdev = cdget ( kdev_t_to_nr (inode->i_rdev) );
+ is_fcb = TRUE;
+ }
+ else if ( S_ISBLK (de->mode) )
+ {
+ inode->i_rdev = mk_kdev (de->u.fcb.u.device.major,
+ de->u.fcb.u.device.minor);
+ if (bd_acquire (inode) == 0)
+ {
+ if (!inode->i_bdev->bd_op && de->u.fcb.ops)
+ inode->i_bdev->bd_op = de->u.fcb.ops;
+ }
+ else PRINTK ("(%d): no block device from bdget()\n",(int)inode->i_ino);
+ is_fcb = TRUE;
+ }
+ else if ( S_ISFIFO (de->mode) ) inode->i_fop = &def_fifo_fops;
+ else if ( S_ISREG (de->mode) )
+ {
+ inode->i_size = de->u.fcb.u.file.size;
+ is_fcb = TRUE;
+ }
+ else if ( S_ISDIR (de->mode) )
+ {
+ inode->i_op = &devfs_dir_iops;
+ inode->i_fop = &devfs_dir_fops;
+ }
+ else if ( S_ISLNK (de->mode) )
+ {
+ inode->i_op = &devfs_symlink_iops;
+ inode->i_size = de->u.symlink.length;
+ }
+ if (is_fcb && de->u.fcb.auto_owner)
+ inode->i_mode = (de->mode & S_IFMT) | S_IRUGO | S_IWUGO;
+ else inode->i_mode = de->mode;
+ inode->i_uid = de->inode.uid;
+ inode->i_gid = de->inode.gid;
+ inode->i_atime = de->inode.atime;
+ inode->i_mtime = de->inode.mtime;
+ inode->i_ctime = de->inode.ctime;
+ DPRINTK (DEBUG_I_GET, "(): mode: 0%o uid: %d gid: %d\n",
+ (int) inode->i_mode, (int) inode->i_uid, (int) inode->i_gid);
+ return inode;
+} /* End Function _devfs_get_vfs_inode */
+
+
+/* File operations for device entries follow */
+
+static int devfs_readdir (struct file *file, void *dirent, filldir_t filldir)
+{
+ int err, count;
+ int stored = 0;
+ struct fs_info *fs_info;
+ struct devfs_entry *parent, *de, *next = NULL;
+ struct inode *inode = file->f_dentry->d_inode;
+
+ fs_info = inode->i_sb->u.generic_sbp;
+ parent = get_devfs_entry_from_vfs_inode (file->f_dentry->d_inode);
+ if ( (long) file->f_pos < 0 ) return -EINVAL;
+ DPRINTK (DEBUG_F_READDIR, "(%s): fs_info: %p pos: %ld\n",
+ parent->name, fs_info, (long) file->f_pos);
+ switch ( (long) file->f_pos )
+ {
+ case 0:
+ scan_dir_for_removable (parent);
+ err = (*filldir) (dirent, "..", 2, file->f_pos,
+ file->f_dentry->d_parent->d_inode->i_ino, DT_DIR);
+ if (err == -EINVAL) break;
+ if (err < 0) return err;
+ file->f_pos++;
+ ++stored;
+ /* Fall through */
+ case 1:
+ err = (*filldir) (dirent, ".", 1, file->f_pos, inode->i_ino, DT_DIR);
+ if (err == -EINVAL) break;
+ if (err < 0) return err;
+ file->f_pos++;
+ ++stored;
+ /* Fall through */
+ default:
+ /* Skip entries */
+ count = file->f_pos - 2;
+ read_lock (&parent->u.dir.lock);
+ for (de = parent->u.dir.first; de && (count > 0); de = de->next)
+ if ( !IS_HIDDEN (de) ) --count;
+ devfs_get (de);
+ read_unlock (&parent->u.dir.lock);
+ /* Now add all remaining entries */
+ while (de)
+ {
+ if ( IS_HIDDEN (de) ) err = 0;
+ else
+ {
+ err = (*filldir) (dirent, de->name, de->namelen,
+ file->f_pos, de->inode.ino, de->mode >> 12);
+ if (err < 0) devfs_put (de);
+ else
+ {
+ file->f_pos++;
+ ++stored;
+ }
+ }
+ if (err == -EINVAL) break;
+ if (err < 0) return err;
+ read_lock (&parent->u.dir.lock);
+ next = devfs_get (de->next);
+ read_unlock (&parent->u.dir.lock);
+ devfs_put (de);
+ de = next;
+ }
+ break;
+ }
+ return stored;
+} /* End Function devfs_readdir */
+
+static int devfs_open (struct inode *inode, struct file *file)
+{
+ int err;
+ struct fcb_type *df;
+ struct devfs_entry *de;
+ struct fs_info *fs_info = inode->i_sb->u.generic_sbp;
+ void *ops;
+
+ de = get_devfs_entry_from_vfs_inode (inode);
+ if (de == NULL) return -ENODEV;
+ if ( S_ISDIR (de->mode) ) return 0;
+ df = &de->u.fcb;
+ file->private_data = de->info;
+ ops = devfs_get_ops (de); /* Now have module refcount */
+ if ( S_ISBLK (inode->i_mode) )
+ {
+ file->f_op = &def_blk_fops;
+ if (ops) inode->i_bdev->bd_op = ops;
+ err = def_blk_fops.open (inode, file); /* This bumps module refcount */
+ devfs_put_ops (de); /* So drop my refcount */
+ }
+ else
+ {
+ file->f_op = ops;
+ if (file->f_op)
+ {
+ lock_kernel ();
+ err = file->f_op->open ? (*file->f_op->open) (inode, file) : 0;
+ unlock_kernel ();
+ }
+ else
+ { /* Fallback to legacy scheme (I don't have a module refcount) */
+ if ( S_ISCHR (inode->i_mode) ) err = chrdev_open (inode, file);
+ else err = -ENODEV;
+ }
+ }
+ if (err < 0) return err;
+ /* Open was successful */
+ if (df->open) return 0;
+ df->open = TRUE; /* This is the first open */
+ if (df->auto_owner)
+ {
+ /* Change the ownership/protection to what driver specified */
+ inode->i_mode = de->mode;
+ inode->i_uid = current->euid;
+ inode->i_gid = current->egid;
+ }
+ if ( df->aopen_notify && !is_devfsd_or_child (fs_info) )
+ devfsd_notify_de (de, DEVFSD_NOTIFY_ASYNC_OPEN, inode->i_mode,
+ current->euid, current->egid, fs_info, 0);
+ return 0;
+} /* End Function devfs_open */
+
+static struct file_operations devfs_fops =
+{
+ .open = devfs_open,
+};
+
+static struct file_operations devfs_dir_fops =
+{
+ .read = generic_read_dir,
+ .readdir = devfs_readdir,
+ .open = devfs_open,
+};
+
+
+/* Dentry operations for device entries follow */
+
+
+/**
+ * devfs_d_release - Callback for when a dentry is freed.
+ * @dentry: The dentry.
+ */
+
+static void devfs_d_release (struct dentry *dentry)
+{
+ DPRINTK (DEBUG_D_RELEASE, "(%p): inode: %p\n", dentry, dentry->d_inode);
+} /* End Function devfs_d_release */
+
+/**
+ * devfs_d_iput - Callback for when a dentry loses its inode.
+ * @dentry: The dentry.
+ * @inode: The inode.
+ */
+
+static void devfs_d_iput (struct dentry *dentry, struct inode *inode)
+{
+ struct devfs_entry *de;
+
+ de = get_devfs_entry_from_vfs_inode (inode);
+ DPRINTK (DEBUG_D_IPUT,"(%s): dentry: %p inode: %p de: %p de->dentry: %p\n",
+ de->name, dentry, inode, de, de->inode.dentry);
+ if ( de->inode.dentry && (de->inode.dentry != dentry) )
+ OOPS ("(%s): de: %p dentry: %p de->dentry: %p\n",
+ de->name, de, dentry, de->inode.dentry);
+ de->inode.dentry = NULL;
+ iput (inode);
+ devfs_put (de);
+} /* End Function devfs_d_iput */
+
+static int devfs_d_delete (struct dentry *dentry);
+
+static struct dentry_operations devfs_dops =
+{
+ .d_delete = devfs_d_delete,
+ .d_release = devfs_d_release,
+ .d_iput = devfs_d_iput,
+};
+
+static int devfs_d_revalidate_wait (struct dentry *dentry, int flags);
+
+static struct dentry_operations devfs_wait_dops =
+{
+ .d_delete = devfs_d_delete,
+ .d_release = devfs_d_release,
+ .d_iput = devfs_d_iput,
+ .d_revalidate = devfs_d_revalidate_wait,
+};
+
+/**
+ * devfs_d_delete - Callback for when all files for a dentry are closed.
+ * @dentry: The dentry.
+ */
+
+static int devfs_d_delete (struct dentry *dentry)
+{
+ struct inode *inode = dentry->d_inode;
+ struct devfs_entry *de;
+ struct fs_info *fs_info;
+
+ if (dentry->d_op == &devfs_wait_dops) dentry->d_op = &devfs_dops;
+ /* Unhash dentry if negative (has no inode) */
+ if (inode == NULL)
+ {
+ DPRINTK (DEBUG_D_DELETE, "(%p): dropping negative dentry\n", dentry);
+ return 1;
+ }
+ fs_info = inode->i_sb->u.generic_sbp;
+ de = get_devfs_entry_from_vfs_inode (inode);
+ DPRINTK (DEBUG_D_DELETE, "(%p): inode: %p devfs_entry: %p\n",
+ dentry, inode, de);
+ if (de == NULL) return 0;
+ if ( !S_ISCHR (de->mode) && !S_ISBLK (de->mode) && !S_ISREG (de->mode) )
+ return 0;
+ if (!de->u.fcb.open) return 0;
+ de->u.fcb.open = FALSE;
+ if (de->u.fcb.aopen_notify)
+ devfsd_notify_de (de, DEVFSD_NOTIFY_CLOSE, inode->i_mode,
+ current->euid, current->egid, fs_info, 1);
+ if (!de->u.fcb.auto_owner) return 0;
+ /* Change the ownership/protection back */
+ inode->i_mode = (de->mode & S_IFMT) | S_IRUGO | S_IWUGO;
+ inode->i_uid = de->inode.uid;
+ inode->i_gid = de->inode.gid;
+ return 0;
+} /* End Function devfs_d_delete */
+
+struct devfs_lookup_struct
+{
+ devfs_handle_t de;
+ wait_queue_head_t wait_queue;
+};
+
+static int devfs_d_revalidate_wait (struct dentry *dentry, int flags)
+{
+ struct inode *dir = dentry->d_parent->d_inode;
+ struct fs_info *fs_info = dir->i_sb->u.generic_sbp;
+ devfs_handle_t parent = get_devfs_entry_from_vfs_inode (dir);
+ struct devfs_lookup_struct *lookup_info = dentry->d_fsdata;
+ DECLARE_WAITQUEUE (wait, current);
+
+ if ( is_devfsd_or_child (fs_info) )
+ {
+ devfs_handle_t de = lookup_info->de;
+ struct inode *inode;
+
+ DPRINTK (DEBUG_I_LOOKUP,
+ "(%s): dentry: %p inode: %p de: %p by: \"%s\"\n",
+ dentry->d_name.name, dentry, dentry->d_inode, de,
+ current->comm);
+ if (dentry->d_inode) return 1;
+ if (de == NULL)
+ {
+ read_lock (&parent->u.dir.lock);
+ de = _devfs_search_dir (parent, dentry->d_name.name,
+ dentry->d_name.len);
+ read_unlock (&parent->u.dir.lock);
+ if (de == NULL) return 1;
+ lookup_info->de = de;
+ }
+ /* Create an inode, now that the driver information is available */
+ inode = _devfs_get_vfs_inode (dir->i_sb, de, dentry);
+ if (!inode) return 1;
+ DPRINTK (DEBUG_I_LOOKUP,
+ "(%s): new VFS inode(%u): %p de: %p by: \"%s\"\n",
+ de->name, de->inode.ino, inode, de, current->comm);
+ d_instantiate (dentry, inode);
+ return 1;
+ }
+ if (lookup_info == NULL) return 1; /* Early termination */
+ read_lock (&parent->u.dir.lock);
+ if (dentry->d_fsdata)
+ {
+ set_current_state (TASK_UNINTERRUPTIBLE);
+ add_wait_queue (&lookup_info->wait_queue, &wait);
+ read_unlock (&parent->u.dir.lock);
+ schedule ();
+ }
+ else read_unlock (&parent->u.dir.lock);
+ return 1;
+} /* End Function devfs_d_revalidate_wait */
+
+
+/* Inode operations for device entries follow */
+
+static struct dentry *devfs_lookup (struct inode *dir, struct dentry *dentry)
+{
+ struct devfs_entry tmp; /* Must stay in scope until devfsd idle again */
+ struct devfs_lookup_struct lookup_info;
+ struct fs_info *fs_info = dir->i_sb->u.generic_sbp;
+ struct devfs_entry *parent, *de;
+ struct inode *inode;
+ struct dentry *retval = NULL;
+
+ /* Set up the dentry operations before anything else, to ensure cleaning
+ up on any error */
+ dentry->d_op = &devfs_dops;
+ /* First try to get the devfs entry for this directory */
+ parent = get_devfs_entry_from_vfs_inode (dir);
+ DPRINTK (DEBUG_I_LOOKUP, "(%s): dentry: %p parent: %p by: \"%s\"\n",
+ dentry->d_name.name, dentry, parent, current->comm);
+ if (parent == NULL) return ERR_PTR (-ENOENT);
+ read_lock (&parent->u.dir.lock);
+ de = _devfs_search_dir (parent, dentry->d_name.name, dentry->d_name.len);
+ if (de) read_unlock (&parent->u.dir.lock);
+ else
+ { /* Try re-reading the partition (media may have changed) */
+ if ( get_removable_partition (parent, dentry->d_name.name,
+ dentry->d_name.len) ) /* Unlocks */
+ { /* Media did change */
+ read_lock (&parent->u.dir.lock);
+ de = _devfs_search_dir (parent, dentry->d_name.name,
+ dentry->d_name.len);
+ read_unlock (&parent->u.dir.lock);
+ }
+ }
+ lookup_info.de = de;
+ init_waitqueue_head (&lookup_info.wait_queue);
+ dentry->d_fsdata = &lookup_info;
+ if (de == NULL)
+ { /* Try with devfsd. For any kind of failure, leave a negative dentry
+ so someone else can deal with it (in the case where the sysadmin
+ does a mknod()). It's important to do this before hashing the
+ dentry, so that the devfsd queue is filled before revalidates
+ can start */
+ if (try_modload (parent, fs_info,
+ dentry->d_name.name, dentry->d_name.len, &tmp) < 0)
+ { /* Lookup event was not queued to devfsd */
+ d_add (dentry, NULL);
+ return NULL;
+ }
+ }
+ dentry->d_op = &devfs_wait_dops;
+ d_add (dentry, NULL); /* Open the floodgates */
+ /* Unlock directory semaphore, which will release any waiters. They
+ will get the hashed dentry, and may be forced to wait for
+ revalidation */
+ up (&dir->i_sem);
+ wait_for_devfsd_finished (fs_info); /* If I'm not devfsd, must wait */
+ down (&dir->i_sem); /* Grab it again because them's the rules */
+ de = lookup_info.de;
+ /* If someone else has been so kind as to make the inode, we go home
+ early */
+ if (dentry->d_inode) goto out;
+ if (de == NULL)
+ {
+ read_lock (&parent->u.dir.lock);
+ de = _devfs_search_dir (parent, dentry->d_name.name,
+ dentry->d_name.len);
+ read_unlock (&parent->u.dir.lock);
+ if (de == NULL) goto out;
+ /* OK, there's an entry now, but no VFS inode yet */
+ }
+ /* Create an inode, now that the driver information is available */
+ inode = _devfs_get_vfs_inode (dir->i_sb, de, dentry);
+ if (!inode)
+ {
+ retval = ERR_PTR (-ENOMEM);
+ goto out;
+ }
+ DPRINTK (DEBUG_I_LOOKUP, "(%s): new VFS inode(%u): %p de: %p by: \"%s\"\n",
+ de->name, de->inode.ino, inode, de, current->comm);
+ d_instantiate (dentry, inode);
+out:
+ dentry->d_op = &devfs_dops;
+ dentry->d_fsdata = NULL;
+ write_lock (&parent->u.dir.lock);
+ wake_up (&lookup_info.wait_queue);
+ write_unlock (&parent->u.dir.lock);
+ devfs_put (de);
+ return retval;
+} /* End Function devfs_lookup */
+
+static int devfs_unlink (struct inode *dir, struct dentry *dentry)
+{
+ int unhooked;
+ struct devfs_entry *de;
+ struct inode *inode = dentry->d_inode;
+ struct fs_info *fs_info = dir->i_sb->u.generic_sbp;
+
+ de = get_devfs_entry_from_vfs_inode (inode);
+ DPRINTK (DEBUG_I_UNLINK, "(%s): de: %p\n", dentry->d_name.name, de);
+ if (de == NULL) return -ENOENT;
+ if (!de->vfs_deletable) return -EPERM;
+ write_lock (&de->parent->u.dir.lock);
+ unhooked = _devfs_unhook (de);
+ write_unlock (&de->parent->u.dir.lock);
+ if (!unhooked) return -ENOENT;
+ if ( !is_devfsd_or_child (fs_info) )
+ devfsd_notify_de (de, DEVFSD_NOTIFY_DELETE, inode->i_mode,
+ inode->i_uid, inode->i_gid, fs_info, 0);
+ free_dentry (de);
+ devfs_put (de);
+ return 0;
+} /* End Function devfs_unlink */
+
+static int devfs_symlink (struct inode *dir, struct dentry *dentry,
+ const char *symname)
+{
+ int err;
+ struct fs_info *fs_info = dir->i_sb->u.generic_sbp;
+ struct devfs_entry *parent, *de;
+ struct inode *inode;
+
+ /* First try to get the devfs entry for this directory */
+ parent = get_devfs_entry_from_vfs_inode (dir);
+ if (parent == NULL) return -ENOENT;
+ err = devfs_do_symlink (parent, dentry->d_name.name, DEVFS_FL_NONE,
+ symname, &de, NULL);
+ DPRINTK (DEBUG_DISABLED, "(%s): errcode from <devfs_do_symlink>: %d\n",
+ dentry->d_name.name, err);
+ if (err < 0) return err;
+ de->vfs_deletable = TRUE;
+ de->inode.uid = current->euid;
+ de->inode.gid = current->egid;
+ de->inode.atime = CURRENT_TIME;
+ de->inode.mtime = CURRENT_TIME;
+ de->inode.ctime = CURRENT_TIME;
+ if ( ( inode = _devfs_get_vfs_inode (dir->i_sb, de, dentry) ) == NULL )
+ return -ENOMEM;
+ DPRINTK (DEBUG_DISABLED, "(%s): new VFS inode(%u): %p dentry: %p\n",
+ dentry->d_name.name, de->inode.ino, inode, dentry);
+ d_instantiate (dentry, inode);
+ if ( !is_devfsd_or_child (fs_info) )
+ devfsd_notify_de (de, DEVFSD_NOTIFY_CREATE, inode->i_mode,
+ inode->i_uid, inode->i_gid, fs_info, 0);
+ return 0;
+} /* End Function devfs_symlink */
+
+static int devfs_mkdir (struct inode *dir, struct dentry *dentry, int mode)
+{
+ int err;
+ struct fs_info *fs_info = dir->i_sb->u.generic_sbp;
+ struct devfs_entry *parent, *de;
+ struct inode *inode;
+
+ mode = (mode & ~S_IFMT) | S_IFDIR; /* VFS doesn't pass S_IFMT part */
+ parent = get_devfs_entry_from_vfs_inode (dir);
+ if (parent == NULL) return -ENOENT;
+ de = _devfs_alloc_entry (dentry->d_name.name, dentry->d_name.len, mode);
+ if (!de) return -ENOMEM;
+ de->vfs_deletable = TRUE;
+ if ( ( err = _devfs_append_entry (parent, de, FALSE, NULL) ) != 0 )
+ return err;
+ de->inode.uid = current->euid;
+ de->inode.gid = current->egid;
+ de->inode.atime = CURRENT_TIME;
+ de->inode.mtime = CURRENT_TIME;
+ de->inode.ctime = CURRENT_TIME;
+ if ( ( inode = _devfs_get_vfs_inode (dir->i_sb, de, dentry) ) == NULL )
+ return -ENOMEM;
+ DPRINTK (DEBUG_DISABLED, "(%s): new VFS inode(%u): %p dentry: %p\n",
+ dentry->d_name.name, de->inode.ino, inode, dentry);
+ d_instantiate (dentry, inode);
+ if ( !is_devfsd_or_child (fs_info) )
+ devfsd_notify_de (de, DEVFSD_NOTIFY_CREATE, inode->i_mode,
+ inode->i_uid, inode->i_gid, fs_info, 0);
+ return 0;
+} /* End Function devfs_mkdir */
+
+static int devfs_rmdir (struct inode *dir, struct dentry *dentry)
+{
+ int err = 0;
+ struct devfs_entry *de;
+ struct fs_info *fs_info = dir->i_sb->u.generic_sbp;
+ struct inode *inode = dentry->d_inode;
+
+ if (dir->i_sb->u.generic_sbp != inode->i_sb->u.generic_sbp) return -EINVAL;
+ de = get_devfs_entry_from_vfs_inode (inode);
+ if (de == NULL) return -ENOENT;
+ if ( !S_ISDIR (de->mode) ) return -ENOTDIR;
+ if (!de->vfs_deletable) return -EPERM;
+ /* First ensure the directory is empty and will stay that way */
+ write_lock (&de->u.dir.lock);
+ if (de->u.dir.first) err = -ENOTEMPTY;
+ else de->u.dir.no_more_additions = TRUE;
+ write_unlock (&de->u.dir.lock);
+ if (err) return err;
+ /* Now unhook the directory from it's parent */
+ write_lock (&de->parent->u.dir.lock);
+ if ( !_devfs_unhook (de) ) err = -ENOENT;
+ write_unlock (&de->parent->u.dir.lock);
+ if (err) return err;
+ if ( !is_devfsd_or_child (fs_info) )
+ devfsd_notify_de (de, DEVFSD_NOTIFY_DELETE, inode->i_mode,
+ inode->i_uid, inode->i_gid, fs_info, 0);
+ free_dentry (de);
+ devfs_put (de);
+ return 0;
+} /* End Function devfs_rmdir */
+
+static int devfs_mknod (struct inode *dir, struct dentry *dentry, int mode,
+ int rdev)
+{
+ int err;
+ struct fs_info *fs_info = dir->i_sb->u.generic_sbp;
+ struct devfs_entry *parent, *de;
+ struct inode *inode;
+
+ DPRINTK (DEBUG_I_MKNOD, "(%s): mode: 0%o dev: %d\n",
+ dentry->d_name.name, mode, rdev);
+ parent = get_devfs_entry_from_vfs_inode (dir);
+ if (parent == NULL) return -ENOENT;
+ de = _devfs_alloc_entry (dentry->d_name.name, dentry->d_name.len, mode);
+ if (!de) return -ENOMEM;
+ de->vfs_deletable = TRUE;
+ if ( S_ISBLK (mode) || S_ISCHR (mode) )
+ {
+ de->u.fcb.u.device.major = MAJOR (rdev);
+ de->u.fcb.u.device.minor = MINOR (rdev);
+ }
+ if ( ( err = _devfs_append_entry (parent, de, FALSE, NULL) ) != 0 )
+ return err;
+ de->inode.uid = current->euid;
+ de->inode.gid = current->egid;
+ de->inode.atime = CURRENT_TIME;
+ de->inode.mtime = CURRENT_TIME;
+ de->inode.ctime = CURRENT_TIME;
+ if ( ( inode = _devfs_get_vfs_inode (dir->i_sb, de, dentry) ) == NULL )
+ return -ENOMEM;
+ DPRINTK (DEBUG_I_MKNOD, ": new VFS inode(%u): %p dentry: %p\n",
+ de->inode.ino, inode, dentry);
+ d_instantiate (dentry, inode);
+ if ( !is_devfsd_or_child (fs_info) )
+ devfsd_notify_de (de, DEVFSD_NOTIFY_CREATE, inode->i_mode,
+ inode->i_uid, inode->i_gid, fs_info, 0);
+ return 0;
+} /* End Function devfs_mknod */
+
+static int devfs_readlink (struct dentry *dentry, char *buffer, int buflen)
+{
+ int err;
+ struct devfs_entry *de;
+
+ de = get_devfs_entry_from_vfs_inode (dentry->d_inode);
+ if (!de) return -ENODEV;
+ err = vfs_readlink (dentry, buffer, buflen, de->u.symlink.linkname);
+ return err;
+} /* End Function devfs_readlink */
+
+static int devfs_follow_link (struct dentry *dentry, struct nameidata *nd)
+{
+ int err;
+ struct devfs_entry *de;
+
+ de = get_devfs_entry_from_vfs_inode (dentry->d_inode);
+ if (!de) return -ENODEV;
+ err = vfs_follow_link (nd, de->u.symlink.linkname);
+ return err;
+} /* End Function devfs_follow_link */
+
+static struct inode_operations devfs_iops =
+{
+ .setattr = devfs_notify_change,
+};
+
+static struct inode_operations devfs_dir_iops =
+{
+ .lookup = devfs_lookup,
+ .unlink = devfs_unlink,
+ .symlink = devfs_symlink,
+ .mkdir = devfs_mkdir,
+ .rmdir = devfs_rmdir,
+ .mknod = devfs_mknod,
+ .setattr = devfs_notify_change,
+};
+
+static struct inode_operations devfs_symlink_iops =
+{
+ .readlink = devfs_readlink,
+ .follow_link = devfs_follow_link,
+ .setattr = devfs_notify_change,
+};
+
+static struct super_block *devfs_read_super (struct super_block *sb,
+ void *data, int silent)
+{
+ struct inode *root_inode = NULL;
+
+ if (_devfs_get_root_entry () == NULL) goto out_no_root;
+ atomic_set (&fs_info.devfsd_overrun_count, 0);
+ init_waitqueue_head (&fs_info.devfsd_wait_queue);
+ init_waitqueue_head (&fs_info.revalidate_wait_queue);
+ fs_info.sb = sb;
+ sb->u.generic_sbp = &fs_info;
+ sb->s_blocksize = 1024;
+ sb->s_blocksize_bits = 10;
+ sb->s_magic = DEVFS_SUPER_MAGIC;
+ sb->s_op = &devfs_sops;
+ if ( ( root_inode = _devfs_get_vfs_inode (sb, root_entry, NULL) ) == NULL )
+ goto out_no_root;
+ sb->s_root = d_alloc_root (root_inode);
+ if (!sb->s_root) goto out_no_root;
+ DPRINTK (DEBUG_S_READ, "(): made devfs ptr: %p\n", sb->u.generic_sbp);
+ return sb;
+
+out_no_root:
+ PRINTK ("(): get root inode failed\n");
+ if (root_inode) iput (root_inode);
+ return NULL;
+} /* End Function devfs_read_super */
+
+
+static DECLARE_FSTYPE (devfs_fs_type, DEVFS_NAME, devfs_read_super, FS_SINGLE);
+
+
+/* File operations for devfsd follow */
+
+static ssize_t devfsd_read (struct file *file, char *buf, size_t len,
+ loff_t *ppos)
+{
+ int done = FALSE;
+ int ival;
+ loff_t pos, devname_offset, tlen, rpos, old_pos;
+ devfs_handle_t de;
+ struct devfsd_buf_entry *entry;
+ struct fs_info *fs_info = file->f_dentry->d_inode->i_sb->u.generic_sbp;
+ struct devfsd_notify_struct *info = fs_info->devfsd_info;
+ DECLARE_WAITQUEUE (wait, current);
+
+ /* Can't seek (pread) on this device */
+ if (ppos != &file->f_pos) return -ESPIPE;
+ /* Verify the task has grabbed the queue */
+ if (fs_info->devfsd_task != current) return -EPERM;
+ info->major = 0;
+ info->minor = 0;
+ /* Block for a new entry */
+ set_current_state (TASK_INTERRUPTIBLE);
+ add_wait_queue (&fs_info->devfsd_wait_queue, &wait);
+ while ( devfsd_queue_empty (fs_info) )
+ {
+ fs_info->devfsd_sleeping = TRUE;
+ wake_up (&fs_info->revalidate_wait_queue);
+ schedule ();
+ fs_info->devfsd_sleeping = FALSE;
+ if ( signal_pending (current) )
+ {
+ remove_wait_queue (&fs_info->devfsd_wait_queue, &wait);
+ __set_current_state (TASK_RUNNING);
+ return -EINTR;
+ }
+ set_current_state (TASK_INTERRUPTIBLE);
+ }
+ remove_wait_queue (&fs_info->devfsd_wait_queue, &wait);
+ __set_current_state (TASK_RUNNING);
+ /* Now play with the data */
+ ival = atomic_read (&fs_info->devfsd_overrun_count);
+ info->overrun_count = ival;
+ entry = fs_info->devfsd_first_event;
+ info->type = entry->type;
+ info->mode = entry->mode;
+ info->uid = entry->uid;
+ info->gid = entry->gid;
+ de = entry->de;
+ if ( S_ISCHR (de->mode) || S_ISBLK (de->mode) )
+ {
+ info->major = de->u.fcb.u.device.major;
+ info->minor = de->u.fcb.u.device.minor;
+ }
+ pos = devfs_generate_path (de, info->devname, DEVFS_PATHLEN);
+ if (pos < 0) return pos;
+ info->namelen = DEVFS_PATHLEN - pos - 1;
+ if (info->mode == 0) info->mode = de->mode;
+ devname_offset = info->devname - (char *) info;
+ old_pos = rpos = *ppos;
+ if (rpos >= 0 && rpos < devname_offset)
+ {
+ /* Copy parts of the header */
+ tlen = devname_offset - rpos;
+ if (tlen > len) tlen = len;
+ if ( copy_to_user (buf, (char *) info + rpos, tlen) )
+ {
+ return -EFAULT;
+ }
+ rpos += tlen;
+ buf += tlen;
+ len -= tlen;
+ }
+ if ( (rpos >= devname_offset) && (len > 0) )
+ {
+ /* Copy the name */
+ tlen = info->namelen + 1;
+ if (tlen > len) tlen = len;
+ else done = TRUE;
+ if ( copy_to_user (buf, info->devname + pos + rpos - devname_offset,
+ tlen) )
+ {
+ return -EFAULT;
+ }
+ rpos += tlen;
+ }
+ tlen = rpos - old_pos;
+ if (done)
+ {
+ devfs_handle_t parent;
+
+ spin_lock (&fs_info->devfsd_buffer_lock);
+ fs_info->devfsd_first_event = entry->next;
+ if (entry->next == NULL) fs_info->devfsd_last_event = NULL;
+ spin_unlock (&fs_info->devfsd_buffer_lock);
+ for (; de != NULL; de = parent)
+ {
+ parent = de->parent;
+ devfs_put (de);
+ }
+ kmem_cache_free (devfsd_buf_cache, entry);
+ if (ival > 0) atomic_sub (ival, &fs_info->devfsd_overrun_count);
+ *ppos = 0;
+ }
+ else *ppos = rpos;
+ return tlen;
+} /* End Function devfsd_read */
+
+static int devfsd_ioctl (struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int ival;
+ struct fs_info *fs_info = inode->i_sb->u.generic_sbp;
+
+ switch (cmd)
+ {
+ case DEVFSDIOC_GET_PROTO_REV:
+ ival = DEVFSD_PROTOCOL_REVISION_KERNEL;
+ if ( copy_to_user ( (void *)arg, &ival, sizeof ival ) ) return -EFAULT;
+ break;
+ case DEVFSDIOC_SET_EVENT_MASK:
+ /* Ensure only one reader has access to the queue. This scheme will
+ work even if the global kernel lock were to be removed, because it
+ doesn't matter who gets in first, as long as only one gets it */
+ if (fs_info->devfsd_task == NULL)
+ {
+ static spinlock_t lock = SPIN_LOCK_UNLOCKED;
+
+ if ( !spin_trylock (&lock) ) return -EBUSY;
+ if (fs_info->devfsd_task != NULL)
+ { /* We lost the race... */
+ spin_unlock (&lock);
+ return -EBUSY;
+ }
+ fs_info->devfsd_task = current;
+ spin_unlock (&lock);
+ fs_info->devfsd_pgrp = (current->pgrp == current->pid) ?
+ current->pgrp : 0;
+ fs_info->devfsd_file = file;
+ fs_info->devfsd_info = kmalloc (sizeof *fs_info->devfsd_info,
+ GFP_KERNEL);
+ if (!fs_info->devfsd_info)
+ {
+ devfsd_close (inode, file);
+ return -ENOMEM;
+ }
+ }
+ else if (fs_info->devfsd_task != current) return -EBUSY;
+ fs_info->devfsd_event_mask = arg; /* Let the masses come forth */
+ break;
+ case DEVFSDIOC_RELEASE_EVENT_QUEUE:
+ if (fs_info->devfsd_file != file) return -EPERM;
+ return devfsd_close (inode, file);
+ /*break;*/
+#ifdef CONFIG_DEVFS_DEBUG
+ case DEVFSDIOC_SET_DEBUG_MASK:
+ if ( copy_from_user (&ival, (void *) arg, sizeof ival) )return -EFAULT;
+ devfs_debug = ival;
+ break;
+#endif
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+} /* End Function devfsd_ioctl */
+
+static int devfsd_close (struct inode *inode, struct file *file)
+{
+ struct devfsd_buf_entry *entry, *next;
+ struct fs_info *fs_info = inode->i_sb->u.generic_sbp;
+
+ if (fs_info->devfsd_file != file) return 0;
+ fs_info->devfsd_event_mask = 0;
+ fs_info->devfsd_file = NULL;
+ spin_lock (&fs_info->devfsd_buffer_lock);
+ entry = fs_info->devfsd_first_event;
+ fs_info->devfsd_first_event = NULL;
+ fs_info->devfsd_last_event = NULL;
+ if (fs_info->devfsd_info)
+ {
+ kfree (fs_info->devfsd_info);
+ fs_info->devfsd_info = NULL;
+ }
+ spin_unlock (&fs_info->devfsd_buffer_lock);
+ fs_info->devfsd_pgrp = 0;
+ fs_info->devfsd_task = NULL;
+ wake_up (&fs_info->revalidate_wait_queue);
+ for (; entry; entry = next)
+ {
+ next = entry->next;
+ kmem_cache_free (devfsd_buf_cache, entry);
+ }
+ return 0;
+} /* End Function devfsd_close */
+
+#ifdef CONFIG_DEVFS_DEBUG
+static ssize_t stat_read (struct file *file, char *buf, size_t len,
+ loff_t *ppos)
+{
+ ssize_t num;
+ loff_t n = *ppos;
+ char txt[80];
+
+ num = sprintf (txt, "Number of entries: %u number of bytes: %u\n",
+ stat_num_entries, stat_num_bytes) + 1;
+ /* Can't seek (pread) on this device */
+ if (ppos != &file->f_pos) return -ESPIPE;
+ if (n != (unsigned)n || n >= num) return 0;
+ if (len > num - n) len = num - n;
+ if ( copy_to_user (buf, txt + n, len) ) return -EFAULT;
+ *ppos = n + len;
+ return len;
+} /* End Function stat_read */
+#endif
+
+
+static int __init init_devfs_fs (void)
+{
+ int err;
+
+ printk (KERN_INFO "%s: v%s Richard Gooch (rgooch@atnf.csiro.au)\n",
+ DEVFS_NAME, DEVFS_VERSION);
+ devfsd_buf_cache = kmem_cache_create ("devfsd_event",
+ sizeof (struct devfsd_buf_entry),
+ 0, 0, NULL, NULL);
+ if (!devfsd_buf_cache) OOPS ("(): unable to allocate event slab\n");
+#ifdef CONFIG_DEVFS_DEBUG
+ devfs_debug = devfs_debug_init;
+ printk (KERN_INFO "%s: devfs_debug: 0x%0x\n", DEVFS_NAME, devfs_debug);
+#endif
+ printk (KERN_INFO "%s: boot_options: 0x%0x\n", DEVFS_NAME, boot_options);
+ err = register_filesystem (&devfs_fs_type);
+ if (!err)
+ {
+ struct vfsmount *devfs_mnt = kern_mount (&devfs_fs_type);
+ err = PTR_ERR (devfs_mnt);
+ if ( !IS_ERR (devfs_mnt) ) err = 0;
+ }
+ return err;
+} /* End Function init_devfs_fs */
+
+void __init mount_devfs_fs (void)
+{
+ int err;
+
+ if ( !(boot_options & OPTION_MOUNT) ) return;
+ err = do_mount ("none", "/dev", "devfs", 0, NULL);
+ if (err == 0) printk (KERN_INFO "Mounted devfs on /dev\n");
+ else PRINTK ("(): unable to mount devfs, err: %d\n", err);
+} /* End Function mount_devfs_fs */
+
+module_init(init_devfs_fs)
diff --git a/fs/devfs/util.c b/fs/devfs/util.c
new file mode 100644
index 00000000000000..9431e7d903b2b2
--- /dev/null
+++ b/fs/devfs/util.c
@@ -0,0 +1,426 @@
+/* devfs (Device FileSystem) utilities.
+
+ Copyright (C) 1999-2002 Richard Gooch
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ Richard Gooch may be reached by email at rgooch@atnf.csiro.au
+ The postal address is:
+ Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia.
+
+ ChangeLog
+
+ 19991031 Richard Gooch <rgooch@atnf.csiro.au>
+ Created.
+ 19991103 Richard Gooch <rgooch@atnf.csiro.au>
+ Created <_devfs_convert_name> and supported SCSI and IDE CD-ROMs
+ 20000203 Richard Gooch <rgooch@atnf.csiro.au>
+ Changed operations pointer type to void *.
+ 20000621 Richard Gooch <rgooch@atnf.csiro.au>
+ Changed interface to <devfs_register_series>.
+ 20000622 Richard Gooch <rgooch@atnf.csiro.au>
+ Took account of interface change to <devfs_mk_symlink>.
+ Took account of interface change to <devfs_mk_dir>.
+ 20010519 Richard Gooch <rgooch@atnf.csiro.au>
+ Documentation cleanup.
+ 20010709 Richard Gooch <rgooch@atnf.csiro.au>
+ Created <devfs_*alloc_major> and <devfs_*alloc_devnum>.
+ 20010710 Richard Gooch <rgooch@atnf.csiro.au>
+ Created <devfs_*alloc_unique_number>.
+ 20010730 Richard Gooch <rgooch@atnf.csiro.au>
+ Documentation typo fix.
+ 20010806 Richard Gooch <rgooch@atnf.csiro.au>
+ Made <block_semaphore> and <char_semaphore> private.
+ 20010813 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixed bug in <devfs_alloc_unique_number>: limited to 128 numbers
+ 20010818 Richard Gooch <rgooch@atnf.csiro.au>
+ Updated major masks up to Linus' "no new majors" proclamation.
+ Block: were 126 now 122 free, char: were 26 now 19 free.
+ 20020324 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixed bug in <devfs_alloc_unique_number>: was clearing beyond
+ bitfield.
+ 20020326 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixed bitfield data type for <devfs_*alloc_devnum>.
+ Made major bitfield type and initialiser 64 bit safe.
+ 20020413 Richard Gooch <rgooch@atnf.csiro.au>
+ Fixed shift warning on 64 bit machines.
+ 20020428 Richard Gooch <rgooch@atnf.csiro.au>
+ Copied and used macro for error messages from fs/devfs/base.c
+*/
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#include <asm/bitops.h>
+
+#define PRINTK(format, args...) \
+ {printk (KERN_ERR "%s" format, __FUNCTION__ , ## args);}
+
+
+/* Private functions follow */
+
+/**
+ * devfs_register_tape - Register a tape device in the "/dev/tapes" hierarchy.
+ * @de: Any tape device entry in the device directory.
+ */
+
+void devfs_register_tape (devfs_handle_t de)
+{
+ int pos;
+ devfs_handle_t parent, slave;
+ char name[16], dest[64];
+ static unsigned int tape_counter;
+ static devfs_handle_t tape_dir;
+
+ if (tape_dir == NULL) tape_dir = devfs_mk_dir (NULL, "tapes", NULL);
+ parent = devfs_get_parent (de);
+ pos = devfs_generate_path (parent, dest + 3, sizeof dest - 3);
+ if (pos < 0) return;
+ strncpy (dest + pos, "../", 3);
+ sprintf (name, "tape%u", tape_counter++);
+ devfs_mk_symlink (tape_dir, name, DEVFS_FL_DEFAULT, dest + pos,
+ &slave, NULL);
+ devfs_auto_unregister (de, slave);
+} /* End Function devfs_register_tape */
+EXPORT_SYMBOL(devfs_register_tape);
+
+
+/**
+ * devfs_register_series - Register a sequence of device entries.
+ * @dir: The handle to the parent devfs directory entry. If this is %NULL
+ * the new names are relative to the root of the devfs.
+ * @format: The printf-style format string. A single "\%u" is allowed.
+ * @num_entries: The number of entries to register.
+ * @flags: A set of bitwise-ORed flags (DEVFS_FL_*).
+ * @major: The major number. Not needed for regular files.
+ * @minor_start: The starting minor number. Not needed for regular files.
+ * @mode: The default file mode.
+ * @ops: The &file_operations or &block_device_operations structure.
+ * This must not be externally deallocated.
+ * @info: An arbitrary pointer which will be written to the private_data
+ * field of the &file structure passed to the device driver. You
+ * can set this to whatever you like, and change it once the file
+ * is opened (the next file opened will not see this change).
+ */
+
+void devfs_register_series (devfs_handle_t dir, const char *format,
+ unsigned int num_entries, unsigned int flags,
+ unsigned int major, unsigned int minor_start,
+ umode_t mode, void *ops, void *info)
+{
+ unsigned int count;
+ char devname[128];
+
+ for (count = 0; count < num_entries; ++count)
+ {
+ sprintf (devname, format, count);
+ devfs_register (dir, devname, flags, major, minor_start + count,
+ mode, ops, info);
+ }
+} /* End Function devfs_register_series */
+EXPORT_SYMBOL(devfs_register_series);
+
+
+struct major_list
+{
+ spinlock_t lock;
+ unsigned long bits[256 / BITS_PER_LONG];
+};
+#if BITS_PER_LONG == 32
+# define INITIALISER64(low,high) (low), (high)
+#else
+# define INITIALISER64(low,high) ( (unsigned long) (high) << 32 | (low) )
+#endif
+
+/* Block majors already assigned:
+ 0-3, 7-9, 11-63, 65-99, 101-113, 120-127, 199, 201, 240-255
+ Total free: 122
+*/
+static struct major_list block_major_list =
+{SPIN_LOCK_UNLOCKED,
+ {INITIALISER64 (0xfffffb8f, 0xffffffff), /* Majors 0-31, 32-63 */
+ INITIALISER64 (0xfffffffe, 0xff03ffef), /* Majors 64-95, 96-127 */
+ INITIALISER64 (0x00000000, 0x00000000), /* Majors 128-159, 160-191 */
+ INITIALISER64 (0x00000280, 0xffff0000), /* Majors 192-223, 224-255 */
+ }
+};
+
+/* Char majors already assigned:
+ 0-7, 9-151, 154-158, 160-211, 216-221, 224-230, 240-255
+ Total free: 19
+*/
+static struct major_list char_major_list =
+{SPIN_LOCK_UNLOCKED,
+ {INITIALISER64 (0xfffffeff, 0xffffffff), /* Majors 0-31, 32-63 */
+ INITIALISER64 (0xffffffff, 0xffffffff), /* Majors 64-95, 96-127 */
+ INITIALISER64 (0x7cffffff, 0xffffffff), /* Majors 128-159, 160-191 */
+ INITIALISER64 (0x3f0fffff, 0xffff007f), /* Majors 192-223, 224-255 */
+ }
+};
+
+
+/**
+ * devfs_alloc_major - Allocate a major number.
+ * @type: The type of the major (DEVFS_SPECIAL_CHR or DEVFS_SPECIAL_BLK)
+
+ * Returns the allocated major, else -1 if none are available.
+ * This routine is thread safe and does not block.
+ */
+
+int devfs_alloc_major (char type)
+{
+ int major;
+ struct major_list *list;
+
+ list = (type == DEVFS_SPECIAL_CHR) ? &char_major_list : &block_major_list;
+ spin_lock (&list->lock);
+ major = find_first_zero_bit (list->bits, 256);
+ if (major < 256) __set_bit (major, list->bits);
+ else major = -1;
+ spin_unlock (&list->lock);
+ return major;
+} /* End Function devfs_alloc_major */
+EXPORT_SYMBOL(devfs_alloc_major);
+
+
+/**
+ * devfs_dealloc_major - Deallocate a major number.
+ * @type: The type of the major (DEVFS_SPECIAL_CHR or DEVFS_SPECIAL_BLK)
+ * @major: The major number.
+ * This routine is thread safe and does not block.
+ */
+
+void devfs_dealloc_major (char type, int major)
+{
+ int was_set;
+ struct major_list *list;
+
+ if (major < 0) return;
+ list = (type == DEVFS_SPECIAL_CHR) ? &char_major_list : &block_major_list;
+ spin_lock (&list->lock);
+ was_set = __test_and_clear_bit (major, list->bits);
+ spin_unlock (&list->lock);
+ if (!was_set) PRINTK ("(): major %d was already free\n", major);
+} /* End Function devfs_dealloc_major */
+EXPORT_SYMBOL(devfs_dealloc_major);
+
+
+struct minor_list
+{
+ int major;
+ unsigned long bits[256 / BITS_PER_LONG];
+ struct minor_list *next;
+};
+
+struct device_list
+{
+ struct minor_list *first, *last;
+ int none_free;
+};
+
+static DECLARE_MUTEX (block_semaphore);
+static struct device_list block_list;
+
+static DECLARE_MUTEX (char_semaphore);
+static struct device_list char_list;
+
+
+/**
+ * devfs_alloc_devnum - Allocate a device number.
+ * @type: The type (DEVFS_SPECIAL_CHR or DEVFS_SPECIAL_BLK).
+ *
+ * Returns the allocated device number, else NODEV if none are available.
+ * This routine is thread safe and may block.
+ */
+
+kdev_t devfs_alloc_devnum (char type)
+{
+ int minor;
+ struct semaphore *semaphore;
+ struct device_list *list;
+ struct minor_list *entry;
+
+ if (type == DEVFS_SPECIAL_CHR)
+ {
+ semaphore = &char_semaphore;
+ list = &char_list;
+ }
+ else
+ {
+ semaphore = &block_semaphore;
+ list = &block_list;
+ }
+ if (list->none_free) return NODEV; /* Fast test */
+ down (semaphore);
+ if (list->none_free)
+ {
+ up (semaphore);
+ return NODEV;
+ }
+ for (entry = list->first; entry != NULL; entry = entry->next)
+ {
+ minor = find_first_zero_bit (entry->bits, 256);
+ if (minor >= 256) continue;
+ __set_bit (minor, entry->bits);
+ up (semaphore);
+ return mk_kdev (entry->major, minor);
+ }
+ /* Need to allocate a new major */
+ if ( ( entry = kmalloc (sizeof *entry, GFP_KERNEL) ) == NULL )
+ {
+ list->none_free = 1;
+ up (semaphore);
+ return NODEV;
+ }
+ memset (entry, 0, sizeof *entry);
+ if ( ( entry->major = devfs_alloc_major (type) ) < 0 )
+ {
+ list->none_free = 1;
+ up (semaphore);
+ kfree (entry);
+ return NODEV;
+ }
+ __set_bit (0, entry->bits);
+ if (list->first == NULL) list->first = entry;
+ else list->last->next = entry;
+ list->last = entry;
+ up (semaphore);
+ return mk_kdev (entry->major, 0);
+} /* End Function devfs_alloc_devnum */
+EXPORT_SYMBOL(devfs_alloc_devnum);
+
+
+/**
+ * devfs_dealloc_devnum - Dellocate a device number.
+ * @type: The type (DEVFS_SPECIAL_CHR or DEVFS_SPECIAL_BLK).
+ * @devnum: The device number.
+ *
+ * This routine is thread safe and does not block.
+ */
+
+void devfs_dealloc_devnum (char type, kdev_t devnum)
+{
+ int major, minor;
+ struct semaphore *semaphore;
+ struct device_list *list;
+ struct minor_list *entry;
+
+ if ( kdev_none (devnum) ) return;
+ if (type == DEVFS_SPECIAL_CHR)
+ {
+ semaphore = &char_semaphore;
+ list = &char_list;
+ }
+ else
+ {
+ semaphore = &block_semaphore;
+ list = &block_list;
+ }
+ major = major (devnum);
+ minor = minor (devnum);
+ down (semaphore);
+ for (entry = list->first; entry != NULL; entry = entry->next)
+ {
+ int was_set;
+
+ if (entry->major != major) continue;
+ was_set = __test_and_clear_bit (minor, entry->bits);
+ if (was_set) list->none_free = 0;
+ up (semaphore);
+ if (!was_set)
+ PRINTK ( "(): device %s was already free\n", kdevname (devnum) );
+ return;
+ }
+ up (semaphore);
+ PRINTK ( "(): major for %s not previously allocated\n",
+ kdevname (devnum) );
+} /* End Function devfs_dealloc_devnum */
+EXPORT_SYMBOL(devfs_dealloc_devnum);
+
+
+/**
+ * devfs_alloc_unique_number - Allocate a unique (positive) number.
+ * @space: The number space to allocate from.
+ *
+ * Returns the allocated unique number, else a negative error code.
+ * This routine is thread safe and may block.
+ */
+
+int devfs_alloc_unique_number (struct unique_numspace *space)
+{
+ int number;
+ unsigned int length;
+
+ /* Get around stupid lack of semaphore initialiser */
+ spin_lock (&space->init_lock);
+ if (!space->sem_initialised)
+ {
+ sema_init (&space->semaphore, 1);
+ space->sem_initialised = 1;
+ }
+ spin_unlock (&space->init_lock);
+ down (&space->semaphore);
+ if (space->num_free < 1)
+ {
+ void *bits;
+
+ if (space->length < 16) length = 16;
+ else length = space->length << 1;
+ if ( ( bits = vmalloc (length) ) == NULL )
+ {
+ up (&space->semaphore);
+ return -ENOMEM;
+ }
+ if (space->bits != NULL)
+ {
+ memcpy (bits, space->bits, space->length);
+ vfree (space->bits);
+ }
+ space->num_free = (length - space->length) << 3;
+ space->bits = bits;
+ memset (bits + space->length, 0, length - space->length);
+ space->length = length;
+ }
+ number = find_first_zero_bit (space->bits, space->length << 3);
+ --space->num_free;
+ __set_bit (number, space->bits);
+ up (&space->semaphore);
+ return number;
+} /* End Function devfs_alloc_unique_number */
+EXPORT_SYMBOL(devfs_alloc_unique_number);
+
+
+/**
+ * devfs_dealloc_unique_number - Deallocate a unique (positive) number.
+ * @space: The number space to deallocate from.
+ * @number: The number to deallocate.
+ *
+ * This routine is thread safe and may block.
+ */
+
+void devfs_dealloc_unique_number (struct unique_numspace *space, int number)
+{
+ int was_set;
+
+ if (number < 0) return;
+ down (&space->semaphore);
+ was_set = __test_and_clear_bit (number, space->bits);
+ if (was_set) ++space->num_free;
+ up (&space->semaphore);
+ if (!was_set) PRINTK ("(): number %d was already free\n", number);
+} /* End Function devfs_dealloc_unique_number */
+EXPORT_SYMBOL(devfs_dealloc_unique_number);
diff --git a/fs/devices.c b/fs/devices.c
new file mode 100644
index 00000000000000..3b4448e8d0e816
--- /dev/null
+++ b/fs/devices.c
@@ -0,0 +1,216 @@
+/*
+ * linux/fs/devices.c
+ *
+ * (C) 1993 Matthias Urlichs -- collected common code and tables.
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * Added kerneld support: Jacques Gelinas and Bjorn Ekwall
+ * (changed to kmod)
+ */
+
+#include <linux/config.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/smp_lock.h>
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+
+#include <linux/tty.h>
+
+/* serial module kmod load support */
+struct tty_driver *get_tty_driver(kdev_t device);
+#define isa_tty_dev(ma) (ma == TTY_MAJOR || ma == TTYAUX_MAJOR)
+#define need_serial(ma,mi) (get_tty_driver(MKDEV(ma,mi)) == NULL)
+#endif
+
+struct device_struct {
+ const char * name;
+ struct file_operations * fops;
+};
+
+static rwlock_t chrdevs_lock = RW_LOCK_UNLOCKED;
+static struct device_struct chrdevs[MAX_CHRDEV];
+
+extern int get_blkdev_list(char *);
+
+int get_device_list(char * page)
+{
+ int i;
+ int len;
+
+ len = sprintf(page, "Character devices:\n");
+ read_lock(&chrdevs_lock);
+ for (i = 0; i < MAX_CHRDEV ; i++) {
+ if (chrdevs[i].fops) {
+ len += sprintf(page+len, "%3d %s\n", i, chrdevs[i].name);
+ }
+ }
+ read_unlock(&chrdevs_lock);
+ len += get_blkdev_list(page+len);
+ return len;
+}
+
+/*
+ Return the function table of a device.
+ Load the driver if needed.
+ Increment the reference count of module in question.
+*/
+static struct file_operations * get_chrfops(unsigned int major, unsigned int minor)
+{
+ struct file_operations *ret = NULL;
+
+ if (!major || major >= MAX_CHRDEV)
+ return NULL;
+
+ read_lock(&chrdevs_lock);
+ ret = fops_get(chrdevs[major].fops);
+ read_unlock(&chrdevs_lock);
+#ifdef CONFIG_KMOD
+ if (ret && isa_tty_dev(major)) {
+ lock_kernel();
+ if (need_serial(major,minor)) {
+ /* Force request_module anyway, but what for? */
+ fops_put(ret);
+ ret = NULL;
+ }
+ unlock_kernel();
+ }
+ if (!ret) {
+ char name[20];
+ sprintf(name, "char-major-%d", major);
+ request_module(name);
+
+ read_lock(&chrdevs_lock);
+ ret = fops_get(chrdevs[major].fops);
+ read_unlock(&chrdevs_lock);
+ }
+#endif
+ return ret;
+}
+
+int register_chrdev(unsigned int major, const char * name, struct file_operations *fops)
+{
+ if (major == 0) {
+ write_lock(&chrdevs_lock);
+ for (major = MAX_CHRDEV-1; major > 0; major--) {
+ if (chrdevs[major].fops == NULL) {
+ chrdevs[major].name = name;
+ chrdevs[major].fops = fops;
+ write_unlock(&chrdevs_lock);
+ return major;
+ }
+ }
+ write_unlock(&chrdevs_lock);
+ return -EBUSY;
+ }
+ if (major >= MAX_CHRDEV)
+ return -EINVAL;
+ write_lock(&chrdevs_lock);
+ if (chrdevs[major].fops && chrdevs[major].fops != fops) {
+ write_unlock(&chrdevs_lock);
+ return -EBUSY;
+ }
+ chrdevs[major].name = name;
+ chrdevs[major].fops = fops;
+ write_unlock(&chrdevs_lock);
+ return 0;
+}
+
+int unregister_chrdev(unsigned int major, const char * name)
+{
+ if (major >= MAX_CHRDEV)
+ return -EINVAL;
+ write_lock(&chrdevs_lock);
+ if (!chrdevs[major].fops || strcmp(chrdevs[major].name, name)) {
+ write_unlock(&chrdevs_lock);
+ return -EINVAL;
+ }
+ chrdevs[major].name = NULL;
+ chrdevs[major].fops = NULL;
+ write_unlock(&chrdevs_lock);
+ return 0;
+}
+
+/*
+ * Called every time a character special file is opened
+ */
+int chrdev_open(struct inode * inode, struct file * filp)
+{
+ int ret = -ENODEV;
+
+ filp->f_op = get_chrfops(MAJOR(inode->i_rdev), MINOR(inode->i_rdev));
+ if (filp->f_op) {
+ ret = 0;
+ if (filp->f_op->open != NULL) {
+ lock_kernel();
+ ret = filp->f_op->open(inode,filp);
+ unlock_kernel();
+ }
+ }
+ return ret;
+}
+
+/*
+ * Dummy default file-operations: the only thing this does
+ * is contain the open that then fills in the correct operations
+ * depending on the special file...
+ */
+static struct file_operations def_chr_fops = {
+ open: chrdev_open,
+};
+
+/*
+ * Print device name (in decimal, hexadecimal or symbolic)
+ * Note: returns pointer to static data!
+ */
+const char * kdevname(kdev_t dev)
+{
+ static char buffer[32];
+ sprintf(buffer, "%02x:%02x", MAJOR(dev), MINOR(dev));
+ return buffer;
+}
+
+const char * cdevname(kdev_t dev)
+{
+ static char buffer[32];
+ const char * name = chrdevs[MAJOR(dev)].name;
+
+ if (!name)
+ name = "unknown-char";
+ sprintf(buffer, "%s(%d,%d)", name, MAJOR(dev), MINOR(dev));
+ return buffer;
+}
+
+static int sock_no_open(struct inode *irrelevant, struct file *dontcare)
+{
+ return -ENXIO;
+}
+
+static struct file_operations bad_sock_fops = {
+ open: sock_no_open
+};
+
+void init_special_inode(struct inode *inode, umode_t mode, int rdev)
+{
+ inode->i_mode = mode;
+ if (S_ISCHR(mode)) {
+ inode->i_fop = &def_chr_fops;
+ inode->i_rdev = to_kdev_t(rdev);
+ inode->i_cdev = cdget(rdev);
+ } else if (S_ISBLK(mode)) {
+ inode->i_fop = &def_blk_fops;
+ inode->i_rdev = to_kdev_t(rdev);
+ } else if (S_ISFIFO(mode))
+ inode->i_fop = &def_fifo_fops;
+ else if (S_ISSOCK(mode))
+ inode->i_fop = &bad_sock_fops;
+ else
+ printk(KERN_DEBUG "init_special_inode: bogus imode (%o)\n", mode);
+}
diff --git a/fs/devpts/Makefile b/fs/devpts/Makefile
new file mode 100644
index 00000000000000..8ab293007dc89d
--- /dev/null
+++ b/fs/devpts/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for the Linux /dev/pts virtual filesystem.
+#
+
+O_TARGET := devpts.o
+
+obj-y := root.o inode.o
+
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/devpts/devpts_i.h b/fs/devpts/devpts_i.h
new file mode 100644
index 00000000000000..da387ea1a93218
--- /dev/null
+++ b/fs/devpts/devpts_i.h
@@ -0,0 +1,41 @@
+/* -*- linux-c -*- --------------------------------------------------------- *
+ *
+ * linux/fs/devpts/devpts_i.h
+ *
+ * Copyright 1998 H. Peter Anvin -- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ------------------------------------------------------------------------- */
+
+#include <linux/fs.h>
+#include <linux/tty.h>
+#include <linux/types.h>
+
+#define BUILDING_DEVPTS 1
+#include <linux/devpts_fs.h>
+
+struct devpts_sb_info {
+ u32 magic;
+ int setuid;
+ int setgid;
+ uid_t uid;
+ gid_t gid;
+ umode_t mode;
+
+ unsigned int max_ptys;
+ struct inode **inodes;
+};
+
+#define DEVPTS_SUPER_MAGIC 0x1cd1
+#define DEVPTS_SBI_MAGIC 0x01da1d02
+
+extern inline struct devpts_sb_info *SBI(struct super_block *sb)
+{
+ return (struct devpts_sb_info *)(sb->u.generic_sbp);
+}
+
+extern struct inode_operations devpts_root_inode_operations;
+extern struct file_operations devpts_root_operations;
diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c
new file mode 100644
index 00000000000000..9ad01667a1799e
--- /dev/null
+++ b/fs/devpts/inode.c
@@ -0,0 +1,256 @@
+/* -*- linux-c -*- --------------------------------------------------------- *
+ *
+ * linux/fs/devpts/inode.c
+ *
+ * Copyright 1998 H. Peter Anvin -- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ------------------------------------------------------------------------- */
+
+#include <linux/module.h>
+
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kdev_t.h>
+#include <linux/kernel.h>
+#include <linux/locks.h>
+#include <linux/major.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/tty.h>
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+
+#include "devpts_i.h"
+
+static struct vfsmount *devpts_mnt;
+
+static void devpts_put_super(struct super_block *sb)
+{
+ struct devpts_sb_info *sbi = SBI(sb);
+ struct inode *inode;
+ int i;
+
+ for ( i = 0 ; i < sbi->max_ptys ; i++ ) {
+ if ( (inode = sbi->inodes[i]) ) {
+ if ( atomic_read(&inode->i_count) != 1 )
+ printk("devpts_put_super: badness: entry %d count %d\n",
+ i, atomic_read(&inode->i_count));
+ inode->i_nlink--;
+ iput(inode);
+ }
+ }
+ kfree(sbi->inodes);
+ kfree(sbi);
+}
+
+static int devpts_statfs(struct super_block *sb, struct statfs *buf);
+static int devpts_remount (struct super_block * sb, int * flags, char * data);
+
+static struct super_operations devpts_sops = {
+ put_super: devpts_put_super,
+ statfs: devpts_statfs,
+ remount_fs: devpts_remount,
+};
+
+static int devpts_parse_options(char *options, struct devpts_sb_info *sbi)
+{
+ int setuid = 0;
+ int setgid = 0;
+ uid_t uid = 0;
+ gid_t gid = 0;
+ umode_t mode = 0600;
+ char *this_char, *value;
+
+ this_char = NULL;
+ if ( options )
+ this_char = strtok(options,",");
+ for ( ; this_char; this_char = strtok(NULL,",")) {
+ if ((value = strchr(this_char,'=')) != NULL)
+ *value++ = 0;
+ if (!strcmp(this_char,"uid")) {
+ if (!value || !*value)
+ return 1;
+ uid = simple_strtoul(value,&value,0);
+ if (*value)
+ return 1;
+ setuid = 1;
+ }
+ else if (!strcmp(this_char,"gid")) {
+ if (!value || !*value)
+ return 1;
+ gid = simple_strtoul(value,&value,0);
+ if (*value)
+ return 1;
+ setgid = 1;
+ }
+ else if (!strcmp(this_char,"mode")) {
+ if (!value || !*value)
+ return 1;
+ mode = simple_strtoul(value,&value,8);
+ if (*value)
+ return 1;
+ }
+ else
+ return 1;
+ }
+ sbi->setuid = setuid;
+ sbi->setgid = setgid;
+ sbi->uid = uid;
+ sbi->gid = gid;
+ sbi->mode = mode & ~S_IFMT;
+
+ return 0;
+}
+
+static int devpts_remount(struct super_block * sb, int * flags, char * data)
+{
+ struct devpts_sb_info *sbi = sb->u.generic_sbp;
+ int res = devpts_parse_options(data,sbi);
+ if (res) {
+ printk("devpts: called with bogus options\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+struct super_block *devpts_read_super(struct super_block *s, void *data,
+ int silent)
+{
+ struct inode * inode;
+ struct devpts_sb_info *sbi;
+
+ sbi = (struct devpts_sb_info *) kmalloc(sizeof(struct devpts_sb_info), GFP_KERNEL);
+ if ( !sbi )
+ goto fail;
+
+ sbi->magic = DEVPTS_SBI_MAGIC;
+ sbi->max_ptys = unix98_max_ptys;
+ sbi->inodes = kmalloc(sizeof(struct inode *) * sbi->max_ptys, GFP_KERNEL);
+ if ( !sbi->inodes )
+ goto fail_free;
+ memset(sbi->inodes, 0, sizeof(struct inode *) * sbi->max_ptys);
+
+ if ( devpts_parse_options(data,sbi) && !silent) {
+ printk("devpts: called with bogus options\n");
+ goto fail_inode;
+ }
+
+ inode = new_inode(s);
+ if (!inode)
+ goto fail_inode;
+ inode->i_ino = 1;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+ inode->i_blocks = 0;
+ inode->i_blksize = 1024;
+ inode->i_uid = inode->i_gid = 0;
+ inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR;
+ inode->i_op = &devpts_root_inode_operations;
+ inode->i_fop = &devpts_root_operations;
+ inode->i_nlink = 2;
+
+ s->u.generic_sbp = (void *) sbi;
+ s->s_blocksize = 1024;
+ s->s_blocksize_bits = 10;
+ s->s_magic = DEVPTS_SUPER_MAGIC;
+ s->s_op = &devpts_sops;
+ s->s_root = d_alloc_root(inode);
+ if (s->s_root)
+ return s;
+
+ printk("devpts: get root dentry failed\n");
+ iput(inode);
+fail_inode:
+ kfree(sbi->inodes);
+fail_free:
+ kfree(sbi);
+fail:
+ return NULL;
+}
+
+static int devpts_statfs(struct super_block *sb, struct statfs *buf)
+{
+ buf->f_type = DEVPTS_SUPER_MAGIC;
+ buf->f_bsize = 1024;
+ buf->f_namelen = NAME_MAX;
+ return 0;
+}
+
+static DECLARE_FSTYPE(devpts_fs_type, "devpts", devpts_read_super, FS_SINGLE);
+
+void devpts_pty_new(int number, kdev_t device)
+{
+ struct super_block *sb = devpts_mnt->mnt_sb;
+ struct devpts_sb_info *sbi = SBI(sb);
+ struct inode *inode;
+
+ if ( sbi->inodes[number] )
+ return; /* Already registered, this does happen */
+
+ inode = new_inode(sb);
+ if (!inode)
+ return;
+ inode->i_ino = number+2;
+ inode->i_blocks = 0;
+ inode->i_blksize = 1024;
+ inode->i_uid = sbi->setuid ? sbi->uid : current->fsuid;
+ inode->i_gid = sbi->setgid ? sbi->gid : current->fsgid;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+ init_special_inode(inode, S_IFCHR|sbi->mode, kdev_t_to_nr(device));
+
+ if ( sbi->inodes[number] ) {
+ iput(inode);
+ return;
+ }
+ sbi->inodes[number] = inode;
+}
+
+void devpts_pty_kill(int number)
+{
+ struct super_block *sb = devpts_mnt->mnt_sb;
+ struct devpts_sb_info *sbi = SBI(sb);
+ struct inode *inode = sbi->inodes[number];
+
+ if ( inode ) {
+ sbi->inodes[number] = NULL;
+ inode->i_nlink--;
+ iput(inode);
+ }
+}
+
+static int __init init_devpts_fs(void)
+{
+ int err = register_filesystem(&devpts_fs_type);
+ if (!err) {
+ devpts_mnt = kern_mount(&devpts_fs_type);
+ err = PTR_ERR(devpts_mnt);
+ if (!IS_ERR(devpts_mnt))
+ err = 0;
+#ifdef MODULE
+ if ( !err ) {
+ devpts_upcall_new = devpts_pty_new;
+ devpts_upcall_kill = devpts_pty_kill;
+ }
+#endif
+ }
+ return err;
+}
+
+static void __exit exit_devpts_fs(void)
+{
+#ifdef MODULE
+ devpts_upcall_new = NULL;
+ devpts_upcall_kill = NULL;
+#endif
+ unregister_filesystem(&devpts_fs_type);
+ kern_umount(devpts_mnt);
+}
+
+module_init(init_devpts_fs)
+module_exit(exit_devpts_fs)
+MODULE_LICENSE("GPL");
+
diff --git a/fs/devpts/root.c b/fs/devpts/root.c
new file mode 100644
index 00000000000000..e93c3354c5b7f2
--- /dev/null
+++ b/fs/devpts/root.c
@@ -0,0 +1,135 @@
+/* -*- linux-c -*- --------------------------------------------------------- *
+ *
+ * linux/fs/devpts/root.c
+ *
+ * Copyright 1998 H. Peter Anvin -- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ------------------------------------------------------------------------- */
+
+#include <linux/errno.h>
+#include <linux/stat.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include "devpts_i.h"
+
+static int devpts_root_readdir(struct file *,void *,filldir_t);
+static struct dentry *devpts_root_lookup(struct inode *,struct dentry *);
+static int devpts_revalidate(struct dentry *, int);
+
+struct file_operations devpts_root_operations = {
+ read: generic_read_dir,
+ readdir: devpts_root_readdir,
+};
+
+struct inode_operations devpts_root_inode_operations = {
+ lookup: devpts_root_lookup,
+};
+
+static struct dentry_operations devpts_dentry_operations = {
+ d_revalidate: devpts_revalidate,
+};
+
+/*
+ * The normal naming convention is simply /dev/pts/<number>; this conforms
+ * to the System V naming convention
+ */
+
+#define genptsname(buf,num) sprintf(buf, "%d", num)
+
+static int devpts_root_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+ struct inode * inode = filp->f_dentry->d_inode;
+ struct devpts_sb_info * sbi = SBI(filp->f_dentry->d_inode->i_sb);
+ off_t nr;
+ char numbuf[16];
+
+ nr = filp->f_pos;
+
+ switch(nr)
+ {
+ case 0:
+ if (filldir(dirent, ".", 1, nr, inode->i_ino, DT_DIR) < 0)
+ return 0;
+ filp->f_pos = ++nr;
+ /* fall through */
+ case 1:
+ if (filldir(dirent, "..", 2, nr, inode->i_ino, DT_DIR) < 0)
+ return 0;
+ filp->f_pos = ++nr;
+ /* fall through */
+ default:
+ while ( nr - 2 < sbi->max_ptys ) {
+ int ptynr = nr - 2;
+ if ( sbi->inodes[ptynr] ) {
+ genptsname(numbuf, ptynr);
+ if ( filldir(dirent, numbuf, strlen(numbuf), nr, nr, DT_CHR) < 0 )
+ return 0;
+ }
+ filp->f_pos = ++nr;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * Revalidate is called on every cache lookup. We use it to check that
+ * the pty really does still exist. Never revalidate negative dentries;
+ * for simplicity (fix later?)
+ */
+static int devpts_revalidate(struct dentry * dentry, int flags)
+{
+ struct devpts_sb_info *sbi;
+
+ if ( !dentry->d_inode )
+ return 0;
+
+ sbi = SBI(dentry->d_inode->i_sb);
+
+ return ( sbi->inodes[dentry->d_inode->i_ino - 2] == dentry->d_inode );
+}
+
+static struct dentry *devpts_root_lookup(struct inode * dir, struct dentry * dentry)
+{
+ struct devpts_sb_info *sbi = SBI(dir->i_sb);
+ unsigned int entry;
+ int i;
+ const char *p;
+
+ dentry->d_op = &devpts_dentry_operations;
+
+ if ( dentry->d_name.len == 1 && dentry->d_name.name[0] == '0' ) {
+ entry = 0;
+ } else if ( dentry->d_name.len < 1 ) {
+ return NULL;
+ } else {
+ p = dentry->d_name.name;
+ if ( *p < '1' || *p > '9' )
+ return NULL;
+ entry = *p++ - '0';
+
+ for ( i = dentry->d_name.len-1 ; i ; i-- ) {
+ unsigned int nentry = *p++ - '0';
+ if ( nentry > 9 )
+ return NULL;
+ if ( entry >= ~0U/10 )
+ return NULL;
+ entry = nentry + entry * 10;
+ }
+ }
+
+ if ( entry >= sbi->max_ptys )
+ return NULL;
+
+ if ( sbi->inodes[entry] )
+ atomic_inc(&sbi->inodes[entry]->i_count);
+
+ d_add(dentry, sbi->inodes[entry]);
+
+ return NULL;
+}
diff --git a/fs/dnotify.c b/fs/dnotify.c
new file mode 100644
index 00000000000000..7ccdb3187d1c4b
--- /dev/null
+++ b/fs/dnotify.c
@@ -0,0 +1,150 @@
+/*
+ * Directory notifications for Linux.
+ *
+ * Copyright (C) 2000,2001,2002 Stephen Rothwell
+ *
+ * This program 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.
+ *
+ * This program 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.
+ */
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/dnotify.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+
+extern void send_sigio(struct fown_struct *fown, int fd, int band);
+
+int dir_notify_enable = 1;
+
+static rwlock_t dn_lock = RW_LOCK_UNLOCKED;
+static kmem_cache_t *dn_cache;
+
+static void redo_inode_mask(struct inode *inode)
+{
+ unsigned long new_mask;
+ struct dnotify_struct *dn;
+
+ new_mask = 0;
+ for (dn = inode->i_dnotify; dn != NULL; dn = dn->dn_next)
+ new_mask |= dn->dn_mask & ~DN_MULTISHOT;
+ inode->i_dnotify_mask = new_mask;
+}
+
+void dnotify_flush(struct file *filp, fl_owner_t id)
+{
+ struct dnotify_struct *dn;
+ struct dnotify_struct **prev;
+ struct inode *inode;
+
+ inode = filp->f_dentry->d_inode;
+ if (!S_ISDIR(inode->i_mode))
+ return;
+ write_lock(&dn_lock);
+ prev = &inode->i_dnotify;
+ while ((dn = *prev) != NULL) {
+ if ((dn->dn_owner == id) && (dn->dn_filp == filp)) {
+ *prev = dn->dn_next;
+ redo_inode_mask(inode);
+ kmem_cache_free(dn_cache, dn);
+ break;
+ }
+ prev = &dn->dn_next;
+ }
+ write_unlock(&dn_lock);
+}
+
+int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg)
+{
+ struct dnotify_struct *dn;
+ struct dnotify_struct *odn;
+ struct dnotify_struct **prev;
+ struct inode *inode;
+ fl_owner_t id = current->files;
+
+ if ((arg & ~DN_MULTISHOT) == 0) {
+ dnotify_flush(filp, id);
+ return 0;
+ }
+ if (!dir_notify_enable)
+ return -EINVAL;
+ inode = filp->f_dentry->d_inode;
+ if (!S_ISDIR(inode->i_mode))
+ return -ENOTDIR;
+ dn = kmem_cache_alloc(dn_cache, SLAB_KERNEL);
+ if (dn == NULL)
+ return -ENOMEM;
+ write_lock(&dn_lock);
+ prev = &inode->i_dnotify;
+ while ((odn = *prev) != NULL) {
+ if ((odn->dn_owner == id) && (odn->dn_filp == filp)) {
+ odn->dn_fd = fd;
+ odn->dn_mask |= arg;
+ inode->i_dnotify_mask |= arg & ~DN_MULTISHOT;
+ kmem_cache_free(dn_cache, dn);
+ goto out;
+ }
+ prev = &odn->dn_next;
+ }
+ filp->f_owner.pid = current->pid;
+ filp->f_owner.uid = current->uid;
+ filp->f_owner.euid = current->euid;
+ dn->dn_mask = arg;
+ dn->dn_fd = fd;
+ dn->dn_filp = filp;
+ dn->dn_owner = id;
+ inode->i_dnotify_mask |= arg & ~DN_MULTISHOT;
+ dn->dn_next = inode->i_dnotify;
+ inode->i_dnotify = dn;
+out:
+ write_unlock(&dn_lock);
+ return 0;
+}
+
+void __inode_dir_notify(struct inode *inode, unsigned long event)
+{
+ struct dnotify_struct * dn;
+ struct dnotify_struct **prev;
+ struct fown_struct * fown;
+ int changed = 0;
+
+ write_lock(&dn_lock);
+ prev = &inode->i_dnotify;
+ while ((dn = *prev) != NULL) {
+ if ((dn->dn_mask & event) == 0) {
+ prev = &dn->dn_next;
+ continue;
+ }
+ fown = &dn->dn_filp->f_owner;
+ if (fown->pid)
+ send_sigio(fown, dn->dn_fd, POLL_MSG);
+ if (dn->dn_mask & DN_MULTISHOT)
+ prev = &dn->dn_next;
+ else {
+ *prev = dn->dn_next;
+ changed = 1;
+ kmem_cache_free(dn_cache, dn);
+ }
+ }
+ if (changed)
+ redo_inode_mask(inode);
+ write_unlock(&dn_lock);
+}
+
+static int __init dnotify_init(void)
+{
+ dn_cache = kmem_cache_create("dnotify_cache",
+ sizeof(struct dnotify_struct), 0, 0, NULL, NULL);
+ if (!dn_cache)
+ panic("cannot create dnotify slab cache");
+ return 0;
+}
+
+module_init(dnotify_init)
diff --git a/fs/dquot.c b/fs/dquot.c
new file mode 100644
index 00000000000000..ddaeccb0ce3f90
--- /dev/null
+++ b/fs/dquot.c
@@ -0,0 +1,1536 @@
+/*
+ * Implementation of the diskquota system for the LINUX operating
+ * system. QUOTA is implemented using the BSD system call interface as
+ * the means of communication with the user level. Currently only the
+ * ext2 filesystem has support for disk quotas. Other filesystems may
+ * be added in the future. This file contains the generic routines
+ * called by the different filesystems on allocation of an inode or
+ * block. These routines take care of the administration needed to
+ * have a consistent diskquota tracking system. The ideas of both
+ * user and group quotas are based on the Melbourne quota system as
+ * used on BSD derived systems. The internal implementation is
+ * based on one of the several variants of the LINUX inode-subsystem
+ * with added complexity of the diskquota system.
+ *
+ * Version: $Id: dquot.c,v 6.3 1996/11/17 18:35:34 mvw Exp mvw $
+ *
+ * Author: Marco van Wieringen <mvw@planets.elm.net>
+ *
+ * Fixes: Dmitry Gorodchanin <pgmdsg@ibi.com>, 11 Feb 96
+ *
+ * Revised list management to avoid races
+ * -- Bill Hawes, <whawes@star.net>, 9/98
+ *
+ * Fixed races in dquot_transfer(), dqget() and dquot_alloc_...().
+ * As the consequence the locking was moved from dquot_decr_...(),
+ * dquot_incr_...() to calling functions.
+ * invalidate_dquots() now writes modified dquots.
+ * Serialized quota_off() and quota_on() for mount point.
+ * Fixed a few bugs in grow_dquots().
+ * Fixed deadlock in write_dquot() - we no longer account quotas on
+ * quota files
+ * remove_dquot_ref() moved to inode.c - it now traverses through inodes
+ * add_dquot_ref() restarts after blocking
+ * Added check for bogus uid and fixed check for group in quotactl.
+ * Jan Kara, <jack@suse.cz>, sponsored by SuSE CR, 10-11/99
+ *
+ * Used struct list_head instead of own list struct
+ * Invalidation of referenced dquots is no longer possible
+ * Improved free_dquots list management
+ * Quota and i_blocks are now updated in one place to avoid races
+ * Warnings are now delayed so we won't block in critical section
+ * Write updated not to require dquot lock
+ * Jan Kara, <jack@suse.cz>, 9/2000
+ *
+ * Added dynamic quota structure allocation
+ * Jan Kara <jack@suse.cz> 12/2000
+ *
+ * Rewritten quota interface. Implemented new quota format and
+ * formats registering.
+ * Jan Kara, <jack@suse.cz>, 2001,2002
+ *
+ * (C) Copyright 1994 - 1997 Marco van Wieringen
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/tty.h>
+#include <linux/file.h>
+#include <linux/slab.h>
+#include <linux/sysctl.h>
+#include <linux/smp_lock.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+
+#include <asm/uaccess.h>
+
+static char *quotatypes[] = INITQFNAMES;
+static struct quota_format_type *quota_formats; /* List of registered formats */
+
+int register_quota_format(struct quota_format_type *fmt)
+{
+ lock_kernel();
+ fmt->qf_next = quota_formats;
+ quota_formats = fmt;
+ unlock_kernel();
+ return 0;
+}
+
+void unregister_quota_format(struct quota_format_type *fmt)
+{
+ struct quota_format_type **actqf;
+
+ lock_kernel();
+ for (actqf = &quota_formats; *actqf && *actqf != fmt; actqf = &(*actqf)->qf_next);
+ if (*actqf)
+ *actqf = (*actqf)->qf_next;
+ unlock_kernel();
+}
+
+static struct quota_format_type *find_quota_format(int id)
+{
+ struct quota_format_type *actqf;
+
+ lock_kernel();
+ for (actqf = quota_formats; actqf && actqf->qf_fmt_id != id; actqf = actqf->qf_next);
+ if (actqf && !try_inc_mod_count(actqf->qf_owner))
+ actqf = NULL;
+ unlock_kernel();
+ return actqf;
+}
+
+static void put_quota_format(struct quota_format_type *fmt)
+{
+ if (fmt->qf_owner)
+ __MOD_DEC_USE_COUNT(fmt->qf_owner);
+}
+
+/*
+ * Dquot List Management:
+ * The quota code uses three lists for dquot management: the inuse_list,
+ * free_dquots, and dquot_hash[] array. A single dquot structure may be
+ * on all three lists, depending on its current state.
+ *
+ * All dquots are placed to the end of inuse_list when first created, and this
+ * list is used for the sync and invalidate operations, which must look
+ * at every dquot.
+ *
+ * Unused dquots (dq_count == 0) are added to the free_dquots list when freed,
+ * and this list is searched whenever we need an available dquot. Dquots are
+ * removed from the list as soon as they are used again, and
+ * dqstats.free_dquots gives the number of dquots on the list. When
+ * dquot is invalidated it's completely released from memory.
+ *
+ * Dquots with a specific identity (device, type and id) are placed on
+ * one of the dquot_hash[] hash chains. The provides an efficient search
+ * mechanism to locate a specific dquot.
+ */
+
+/*
+ * Note that any operation which operates on dquot data (ie. dq_dqb) mustn't
+ * block while it's updating/reading it. Otherwise races would occur.
+ *
+ * Locked dquots might not be referenced in inodes - operations like
+ * add_dquot_space() does dqduplicate() and would complain. Currently
+ * dquot it locked only once in its existence - when it's being read
+ * to memory on first dqget() and at that time it can't be referenced
+ * from inode. Write operations on dquots don't hold dquot lock as they
+ * copy data to internal buffers before writing anyway and copying as well
+ * as any data update should be atomic. Also nobody can change used
+ * entries in dquot structure as this is done only when quota is destroyed
+ * and invalidate_dquots() waits for dquot to have dq_count == 0.
+ */
+
+static LIST_HEAD(inuse_list);
+static LIST_HEAD(free_dquots);
+static struct list_head dquot_hash[NR_DQHASH];
+
+static void dqput(struct dquot *);
+static struct dquot *dqduplicate(struct dquot *);
+
+static inline void get_dquot_ref(struct dquot *dquot)
+{
+ dquot->dq_count++;
+}
+
+static inline void put_dquot_ref(struct dquot *dquot)
+{
+ dquot->dq_count--;
+}
+
+static inline void get_dquot_dup_ref(struct dquot *dquot)
+{
+ dquot->dq_dup_ref++;
+}
+
+static inline void put_dquot_dup_ref(struct dquot *dquot)
+{
+ dquot->dq_dup_ref--;
+}
+
+static inline int const hashfn(struct super_block *sb, unsigned int id, int type)
+{
+ return((HASHDEV(sb->s_dev) ^ id) * (MAXQUOTAS - type)) % NR_DQHASH;
+}
+
+static inline void insert_dquot_hash(struct dquot *dquot)
+{
+ struct list_head *head = dquot_hash + hashfn(dquot->dq_sb, dquot->dq_id, dquot->dq_type);
+ list_add(&dquot->dq_hash, head);
+}
+
+static inline void remove_dquot_hash(struct dquot *dquot)
+{
+ list_del(&dquot->dq_hash);
+ INIT_LIST_HEAD(&dquot->dq_hash);
+}
+
+static inline struct dquot *find_dquot(unsigned int hashent, struct super_block *sb, unsigned int id, int type)
+{
+ struct list_head *head;
+ struct dquot *dquot;
+
+ for (head = dquot_hash[hashent].next; head != dquot_hash+hashent; head = head->next) {
+ dquot = list_entry(head, struct dquot, dq_hash);
+ if (dquot->dq_sb == sb && dquot->dq_id == id && dquot->dq_type == type)
+ return dquot;
+ }
+ return NODQUOT;
+}
+
+/* Add a dquot to the head of the free list */
+static inline void put_dquot_head(struct dquot *dquot)
+{
+ list_add(&dquot->dq_free, &free_dquots);
+ dqstats.free_dquots++;
+}
+
+/* Add a dquot to the tail of the free list */
+static inline void put_dquot_last(struct dquot *dquot)
+{
+ list_add(&dquot->dq_free, free_dquots.prev);
+ dqstats.free_dquots++;
+}
+
+/* Move dquot to the head of free list (it must be already on it) */
+static inline void move_dquot_head(struct dquot *dquot)
+{
+ list_del(&dquot->dq_free);
+ list_add(&dquot->dq_free, &free_dquots);
+}
+
+static inline void remove_free_dquot(struct dquot *dquot)
+{
+ if (list_empty(&dquot->dq_free))
+ return;
+ list_del(&dquot->dq_free);
+ INIT_LIST_HEAD(&dquot->dq_free);
+ dqstats.free_dquots--;
+}
+
+static inline void put_inuse(struct dquot *dquot)
+{
+ /* We add to the back of inuse list so we don't have to restart
+ * when traversing this list and we block */
+ list_add(&dquot->dq_inuse, inuse_list.prev);
+ dqstats.allocated_dquots++;
+}
+
+static inline void remove_inuse(struct dquot *dquot)
+{
+ dqstats.allocated_dquots--;
+ list_del(&dquot->dq_inuse);
+}
+
+static void __wait_on_dquot(struct dquot *dquot)
+{
+ DECLARE_WAITQUEUE(wait, current);
+
+ add_wait_queue(&dquot->dq_wait_lock, &wait);
+repeat:
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ if (dquot->dq_flags & DQ_LOCKED) {
+ schedule();
+ goto repeat;
+ }
+ remove_wait_queue(&dquot->dq_wait_lock, &wait);
+ current->state = TASK_RUNNING;
+}
+
+static inline void wait_on_dquot(struct dquot *dquot)
+{
+ if (dquot->dq_flags & DQ_LOCKED)
+ __wait_on_dquot(dquot);
+}
+
+static inline void lock_dquot(struct dquot *dquot)
+{
+ wait_on_dquot(dquot);
+ dquot->dq_flags |= DQ_LOCKED;
+}
+
+static inline void unlock_dquot(struct dquot *dquot)
+{
+ dquot->dq_flags &= ~DQ_LOCKED;
+ wake_up(&dquot->dq_wait_lock);
+}
+
+/* Wait for dquot to be unused */
+static void __wait_dquot_unused(struct dquot *dquot)
+{
+ DECLARE_WAITQUEUE(wait, current);
+
+ add_wait_queue(&dquot->dq_wait_free, &wait);
+repeat:
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ if (dquot->dq_count) {
+ schedule();
+ goto repeat;
+ }
+ remove_wait_queue(&dquot->dq_wait_free, &wait);
+ current->state = TASK_RUNNING;
+}
+
+/* Wait for all duplicated dquot references to be dropped */
+static void __wait_dup_drop(struct dquot *dquot)
+{
+ DECLARE_WAITQUEUE(wait, current);
+
+ add_wait_queue(&dquot->dq_wait_free, &wait);
+repeat:
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ if (dquot->dq_dup_ref) {
+ schedule();
+ goto repeat;
+ }
+ remove_wait_queue(&dquot->dq_wait_free, &wait);
+ current->state = TASK_RUNNING;
+}
+
+static int read_dqblk(struct dquot *dquot)
+{
+ int ret;
+ struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
+
+ lock_dquot(dquot);
+ down(&dqopt->dqio_sem);
+ ret = dqopt->ops[dquot->dq_type]->read_dqblk(dquot);
+ up(&dqopt->dqio_sem);
+ unlock_dquot(dquot);
+ return ret;
+}
+
+static int commit_dqblk(struct dquot *dquot)
+{
+ int ret;
+ struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
+
+ down(&dqopt->dqio_sem);
+ ret = dqopt->ops[dquot->dq_type]->commit_dqblk(dquot);
+ up(&dqopt->dqio_sem);
+ return ret;
+}
+
+/* Invalidate all dquots on the list, wait for all users. Note that this function is called
+ * after quota is disabled so no new quota might be created. As we only insert to the end of
+ * inuse list, we don't have to restart searching... */
+static void invalidate_dquots(struct super_block *sb, int type)
+{
+ struct dquot *dquot;
+ struct list_head *head;
+
+restart:
+ list_for_each(head, &inuse_list) {
+ dquot = list_entry(head, struct dquot, dq_inuse);
+ if (dquot->dq_sb != sb)
+ continue;
+ if (dquot->dq_type != type)
+ continue;
+ dquot->dq_flags |= DQ_INVAL;
+ if (dquot->dq_count)
+ /*
+ * Wait for any users of quota. As we have already cleared the flags in
+ * superblock and cleared all pointers from inodes we are assured
+ * that there will be no new users of this quota.
+ */
+ __wait_dquot_unused(dquot);
+ /* Quota now have no users and it has been written on last dqput() */
+ remove_dquot_hash(dquot);
+ remove_free_dquot(dquot);
+ remove_inuse(dquot);
+ kmem_cache_free(dquot_cachep, dquot);
+ goto restart;
+ }
+}
+
+static int vfs_quota_sync(struct super_block *sb, int type)
+{
+ struct list_head *head;
+ struct dquot *dquot;
+ struct quota_info *dqopt = sb_dqopt(sb);
+ int cnt;
+
+restart:
+ list_for_each(head, &inuse_list) {
+ dquot = list_entry(head, struct dquot, dq_inuse);
+ if (sb && dquot->dq_sb != sb)
+ continue;
+ if (type != -1 && dquot->dq_type != type)
+ continue;
+ if (!dquot->dq_sb) /* Invalidated? */
+ continue;
+ if (!dquot_dirty(dquot) && !(dquot->dq_flags & DQ_LOCKED))
+ continue;
+ /* Get reference to quota so it won't be invalidated. get_dquot_ref()
+ * is enough since if dquot is locked/modified it can't be
+ * on the free list */
+ get_dquot_ref(dquot);
+ dqstats.lookups++;
+ if (dquot->dq_flags & DQ_LOCKED)
+ wait_on_dquot(dquot);
+ if (dquot_dirty(dquot))
+ sb->dq_op->write_dquot(dquot);
+ /* Move the inuse_list head pointer to just after the
+ * current dquot, so that we'll restart the list walk
+ * after this point on the next pass. */
+ list_move(&inuse_list, &dquot->dq_inuse);
+ dqput(dquot);
+ goto restart;
+ }
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++)
+ if ((cnt == type || type == -1) && sb_has_quota_enabled(sb, cnt))
+ dqopt->info[cnt].dqi_flags &= ~DQF_ANY_DQUOT_DIRTY;
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++)
+ if ((cnt == type || type == -1) && sb_has_quota_enabled(sb, cnt) && info_dirty(&dqopt->info[cnt]))
+ dqopt->ops[cnt]->write_file_info(sb, cnt);
+ dqstats.syncs++;
+
+ return 0;
+}
+
+static struct super_block *get_super_to_sync(int type)
+{
+ struct list_head *head;
+ int cnt, dirty;
+
+restart:
+ spin_lock(&sb_lock);
+ list_for_each(head, &super_blocks) {
+ struct super_block *sb = list_entry(head, struct super_block, s_list);
+
+ for (cnt = 0, dirty = 0; cnt < MAXQUOTAS; cnt++)
+ if ((type == cnt || type == -1) && sb_has_quota_enabled(sb, cnt)
+ && sb_dqopt(sb)->info[cnt].dqi_flags & DQF_ANY_DQUOT_DIRTY)
+ dirty = 1;
+ if (!dirty)
+ continue;
+ sb->s_count++;
+ spin_unlock(&sb_lock);
+ down_read(&sb->s_umount);
+ if (!sb->s_root) {
+ drop_super(sb);
+ goto restart;
+ }
+ return sb;
+ }
+ spin_unlock(&sb_lock);
+ return NULL;
+}
+
+void sync_dquots_dev(kdev_t dev, int type)
+{
+ struct super_block *sb;
+
+ if (dev) {
+ if ((sb = get_super(dev))) {
+ lock_kernel();
+ if (sb->s_qcop->quota_sync)
+ sb->s_qcop->quota_sync(sb, type);
+ unlock_kernel();
+ drop_super(sb);
+ }
+ }
+ else {
+ while ((sb = get_super_to_sync(type))) {
+ lock_kernel();
+ if (sb->s_qcop->quota_sync)
+ sb->s_qcop->quota_sync(sb, type);
+ unlock_kernel();
+ drop_super(sb);
+ }
+ }
+}
+
+void sync_dquots_sb(struct super_block *sb, int type)
+{
+ lock_kernel();
+ if (sb->s_qcop->quota_sync)
+ sb->s_qcop->quota_sync(sb, type);
+ unlock_kernel();
+}
+
+/* Free unused dquots from cache */
+static void prune_dqcache(int count)
+{
+ struct list_head *head;
+ struct dquot *dquot;
+
+ head = free_dquots.prev;
+ while (head != &free_dquots && count) {
+ dquot = list_entry(head, struct dquot, dq_free);
+ remove_dquot_hash(dquot);
+ remove_free_dquot(dquot);
+ remove_inuse(dquot);
+ kmem_cache_free(dquot_cachep, dquot);
+ count--;
+ head = free_dquots.prev;
+ }
+}
+
+/*
+ * This is called from kswapd when we think we need some
+ * more memory, but aren't really sure how much. So we
+ * carefully try to free a _bit_ of our dqcache, but not
+ * too much.
+ *
+ * Priority:
+ * 1 - very urgent: shrink everything
+ * ...
+ * 6 - base-level: try to shrink a bit.
+ */
+
+int shrink_dqcache_memory(int priority, unsigned int gfp_mask)
+{
+ int count = 0;
+
+ lock_kernel();
+ count = dqstats.free_dquots / priority;
+ prune_dqcache(count);
+ unlock_kernel();
+ return kmem_cache_shrink(dquot_cachep);
+}
+
+/*
+ * Put reference to dquot
+ * NOTE: If you change this function please check whether dqput_blocks() works right...
+ */
+static void dqput(struct dquot *dquot)
+{
+ if (!dquot)
+ return;
+#ifdef __DQUOT_PARANOIA
+ if (!dquot->dq_count) {
+ printk("VFS: dqput: trying to free free dquot\n");
+ printk("VFS: device %s, dquot of %s %d\n",
+ kdevname(dquot->dq_dev), quotatypes[dquot->dq_type],
+ dquot->dq_id);
+ return;
+ }
+#endif
+
+ dqstats.drops++;
+we_slept:
+ if (dquot->dq_dup_ref && dquot->dq_count - dquot->dq_dup_ref <= 1) { /* Last unduplicated reference? */
+ __wait_dup_drop(dquot);
+ goto we_slept;
+ }
+ if (dquot->dq_count > 1) {
+ /* We have more than one user... We can simply decrement use count */
+ put_dquot_ref(dquot);
+ return;
+ }
+ if (dquot_dirty(dquot)) {
+ dquot->dq_sb->dq_op->write_dquot(dquot);
+ goto we_slept;
+ }
+
+ /* sanity check */
+ if (!list_empty(&dquot->dq_free)) {
+ printk(KERN_ERR "dqput: dquot already on free list??\n");
+ put_dquot_ref(dquot);
+ return;
+ }
+ put_dquot_ref(dquot);
+ /* If dquot is going to be invalidated invalidate_dquots() is going to free it so */
+ if (!(dquot->dq_flags & DQ_INVAL))
+ put_dquot_last(dquot); /* Place at end of LRU free queue */
+ wake_up(&dquot->dq_wait_free);
+}
+
+static struct dquot *get_empty_dquot(struct super_block *sb, int type)
+{
+ struct dquot *dquot;
+
+ dquot = kmem_cache_alloc(dquot_cachep, SLAB_KERNEL);
+ if(!dquot)
+ return NODQUOT;
+
+ memset((caddr_t)dquot, 0, sizeof(struct dquot));
+ init_waitqueue_head(&dquot->dq_wait_free);
+ init_waitqueue_head(&dquot->dq_wait_lock);
+ INIT_LIST_HEAD(&dquot->dq_free);
+ INIT_LIST_HEAD(&dquot->dq_inuse);
+ INIT_LIST_HEAD(&dquot->dq_hash);
+ dquot->dq_sb = sb;
+ dquot->dq_dev = sb->s_dev;
+ dquot->dq_type = type;
+ dquot->dq_count = 1;
+ /* all dquots go on the inuse_list */
+ put_inuse(dquot);
+
+ return dquot;
+}
+
+static struct dquot *dqget(struct super_block *sb, unsigned int id, int type)
+{
+ unsigned int hashent = hashfn(sb, id, type);
+ struct dquot *dquot, *empty = NODQUOT;
+ struct quota_info *dqopt = sb_dqopt(sb);
+
+we_slept:
+ if (!is_enabled(dqopt, type)) {
+ if (empty)
+ dqput(empty);
+ return NODQUOT;
+ }
+
+ if ((dquot = find_dquot(hashent, sb, id, type)) == NODQUOT) {
+ if (empty == NODQUOT) {
+ if ((empty = get_empty_dquot(sb, type)) == NODQUOT)
+ schedule(); /* Try to wait for a moment... */
+ goto we_slept;
+ }
+ dquot = empty;
+ dquot->dq_id = id;
+ /* hash it first so it can be found */
+ insert_dquot_hash(dquot);
+ read_dqblk(dquot);
+ } else {
+ if (!dquot->dq_count)
+ remove_free_dquot(dquot);
+ get_dquot_ref(dquot);
+ dqstats.cache_hits++;
+ wait_on_dquot(dquot);
+ if (empty)
+ dqput(empty);
+ }
+
+ if (!dquot->dq_sb) { /* Has somebody invalidated entry under us? */
+ printk(KERN_ERR "VFS: dqget(): Quota invalidated in dqget()!\n");
+ dqput(dquot);
+ return NODQUOT;
+ }
+ dqstats.lookups++;
+
+ return dquot;
+}
+
+/* Duplicate reference to dquot got from inode */
+static struct dquot *dqduplicate(struct dquot *dquot)
+{
+ if (dquot == NODQUOT)
+ return NODQUOT;
+ get_dquot_ref(dquot);
+ if (!dquot->dq_sb) {
+ printk(KERN_ERR "VFS: dqduplicate(): Invalidated quota to be duplicated!\n");
+ put_dquot_ref(dquot);
+ return NODQUOT;
+ }
+ if (dquot->dq_flags & DQ_LOCKED)
+ printk(KERN_ERR "VFS: dqduplicate(): Locked quota to be duplicated!\n");
+ get_dquot_dup_ref(dquot);
+ dqstats.lookups++;
+
+ return dquot;
+}
+
+/* Put duplicated reference */
+static void dqputduplicate(struct dquot *dquot)
+{
+ if (!dquot->dq_dup_ref) {
+ printk(KERN_ERR "VFS: dqputduplicate(): Duplicated dquot put without duplicate reference.\n");
+ return;
+ }
+ put_dquot_dup_ref(dquot);
+ if (!dquot->dq_dup_ref)
+ wake_up(&dquot->dq_wait_free);
+ put_dquot_ref(dquot);
+ dqstats.drops++;
+}
+
+static int dqinit_needed(struct inode *inode, int type)
+{
+ int cnt;
+
+ if (IS_NOQUOTA(inode))
+ return 0;
+ if (type != -1)
+ return inode->i_dquot[type] == NODQUOT;
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++)
+ if (inode->i_dquot[cnt] == NODQUOT)
+ return 1;
+ return 0;
+}
+
+static void add_dquot_ref(struct super_block *sb, int type)
+{
+ struct list_head *p;
+
+restart:
+ file_list_lock();
+ list_for_each(p, &sb->s_files) {
+ struct file *filp = list_entry(p, struct file, f_list);
+ struct inode *inode = filp->f_dentry->d_inode;
+ if (filp->f_mode & FMODE_WRITE && dqinit_needed(inode, type)) {
+ struct vfsmount *mnt = mntget(filp->f_vfsmnt);
+ struct dentry *dentry = dget(filp->f_dentry);
+ file_list_unlock();
+ sb->dq_op->initialize(inode, type);
+ dput(dentry);
+ mntput(mnt);
+ /* As we may have blocked we had better restart... */
+ goto restart;
+ }
+ }
+ file_list_unlock();
+}
+
+/* Return 0 if dqput() won't block (note that 1 doesn't necessarily mean blocking) */
+static inline int dqput_blocks(struct dquot *dquot)
+{
+ if (dquot->dq_dup_ref && dquot->dq_count - dquot->dq_dup_ref <= 1)
+ return 1;
+ if (dquot->dq_count <= 1 && dquot->dq_flags & DQ_MOD)
+ return 1;
+ return 0;
+}
+
+/* Remove references to dquots from inode - add dquot to list for freeing if needed */
+int remove_inode_dquot_ref(struct inode *inode, int type, struct list_head *tofree_head)
+{
+ struct dquot *dquot = inode->i_dquot[type];
+ int cnt;
+
+ inode->i_dquot[type] = NODQUOT;
+ /* any other quota in use? */
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ if (inode->i_dquot[cnt] != NODQUOT)
+ goto put_it;
+ }
+ inode->i_flags &= ~S_QUOTA;
+put_it:
+ if (dquot != NODQUOT) {
+ if (dqput_blocks(dquot)) {
+ if (dquot->dq_count != 1)
+ printk(KERN_WARNING "VFS: Adding dquot with dq_count %d to dispose list.\n", dquot->dq_count);
+ list_add(&dquot->dq_free, tofree_head); /* As dquot must have currently users it can't be on the free list... */
+ return 1;
+ }
+ else
+ dqput(dquot); /* We have guaranteed we won't block */
+ }
+ return 0;
+}
+
+/* Free list of dquots - called from inode.c */
+void put_dquot_list(struct list_head *tofree_head)
+{
+ struct list_head *act_head;
+ struct dquot *dquot;
+
+ lock_kernel();
+ act_head = tofree_head->next;
+ /* So now we have dquots on the list... Just free them */
+ while (act_head != tofree_head) {
+ dquot = list_entry(act_head, struct dquot, dq_free);
+ act_head = act_head->next;
+ list_del(&dquot->dq_free); /* Remove dquot from the list so we won't have problems... */
+ INIT_LIST_HEAD(&dquot->dq_free);
+ dqput(dquot);
+ }
+ unlock_kernel();
+}
+
+static inline void dquot_incr_inodes(struct dquot *dquot, unsigned long number)
+{
+ dquot->dq_dqb.dqb_curinodes += number;
+ mark_dquot_dirty(dquot);
+}
+
+static inline void dquot_incr_space(struct dquot *dquot, qsize_t number)
+{
+ dquot->dq_dqb.dqb_curspace += number;
+ mark_dquot_dirty(dquot);
+}
+
+static inline void dquot_decr_inodes(struct dquot *dquot, unsigned long number)
+{
+ if (dquot->dq_dqb.dqb_curinodes > number)
+ dquot->dq_dqb.dqb_curinodes -= number;
+ else
+ dquot->dq_dqb.dqb_curinodes = 0;
+ if (dquot->dq_dqb.dqb_curinodes < dquot->dq_dqb.dqb_isoftlimit)
+ dquot->dq_dqb.dqb_itime = (time_t) 0;
+ dquot->dq_flags &= ~DQ_INODES;
+ mark_dquot_dirty(dquot);
+}
+
+static inline void dquot_decr_space(struct dquot *dquot, qsize_t number)
+{
+ if (dquot->dq_dqb.dqb_curspace > number)
+ dquot->dq_dqb.dqb_curspace -= number;
+ else
+ dquot->dq_dqb.dqb_curspace = 0;
+ if (toqb(dquot->dq_dqb.dqb_curspace) < dquot->dq_dqb.dqb_bsoftlimit)
+ dquot->dq_dqb.dqb_btime = (time_t) 0;
+ dquot->dq_flags &= ~DQ_BLKS;
+ mark_dquot_dirty(dquot);
+}
+
+static int flag_print_warnings = 1;
+
+static inline int need_print_warning(struct dquot *dquot, int flag)
+{
+ if (!flag_print_warnings)
+ return 0;
+
+ switch (dquot->dq_type) {
+ case USRQUOTA:
+ return current->fsuid == dquot->dq_id && !(dquot->dq_flags & flag);
+ case GRPQUOTA:
+ return in_group_p(dquot->dq_id) && !(dquot->dq_flags & flag);
+ }
+ return 0;
+}
+
+/* Values of warnings */
+#define NOWARN 0
+#define IHARDWARN 1
+#define ISOFTLONGWARN 2
+#define ISOFTWARN 3
+#define BHARDWARN 4
+#define BSOFTLONGWARN 5
+#define BSOFTWARN 6
+
+/* Print warning to user which exceeded quota */
+static void print_warning(struct dquot *dquot, const char warntype)
+{
+ char *msg = NULL;
+ int flag = (warntype == BHARDWARN || warntype == BSOFTLONGWARN) ? DQ_BLKS :
+ ((warntype == IHARDWARN || warntype == ISOFTLONGWARN) ? DQ_INODES : 0);
+
+ if (!need_print_warning(dquot, flag))
+ return;
+ dquot->dq_flags |= flag;
+ tty_write_message(current->tty, (char *)bdevname(dquot->dq_sb->s_dev));
+ if (warntype == ISOFTWARN || warntype == BSOFTWARN)
+ tty_write_message(current->tty, ": warning, ");
+ else
+ tty_write_message(current->tty, ": write failed, ");
+ tty_write_message(current->tty, quotatypes[dquot->dq_type]);
+ switch (warntype) {
+ case IHARDWARN:
+ msg = " file limit reached.\r\n";
+ break;
+ case ISOFTLONGWARN:
+ msg = " file quota exceeded too long.\r\n";
+ break;
+ case ISOFTWARN:
+ msg = " file quota exceeded.\r\n";
+ break;
+ case BHARDWARN:
+ msg = " block limit reached.\r\n";
+ break;
+ case BSOFTLONGWARN:
+ msg = " block quota exceeded too long.\r\n";
+ break;
+ case BSOFTWARN:
+ msg = " block quota exceeded.\r\n";
+ break;
+ }
+ tty_write_message(current->tty, msg);
+}
+
+static inline void flush_warnings(struct dquot **dquots, char *warntype)
+{
+ int i;
+
+ for (i = 0; i < MAXQUOTAS; i++)
+ if (dquots[i] != NODQUOT && warntype[i] != NOWARN)
+ print_warning(dquots[i], warntype[i]);
+}
+
+static inline char ignore_hardlimit(struct dquot *dquot)
+{
+ struct mem_dqinfo *info = &sb_dqopt(dquot->dq_sb)->info[dquot->dq_type];
+
+ return capable(CAP_SYS_RESOURCE) &&
+ (info->dqi_format->qf_fmt_id != QFMT_VFS_OLD || !(info->dqi_flags & V1_DQF_RSQUASH));
+}
+
+static int check_idq(struct dquot *dquot, ulong inodes, char *warntype)
+{
+ *warntype = NOWARN;
+ if (inodes <= 0 || dquot->dq_flags & DQ_FAKE)
+ return QUOTA_OK;
+
+ if (dquot->dq_dqb.dqb_ihardlimit &&
+ (dquot->dq_dqb.dqb_curinodes + inodes) > dquot->dq_dqb.dqb_ihardlimit &&
+ !ignore_hardlimit(dquot)) {
+ *warntype = IHARDWARN;
+ return NO_QUOTA;
+ }
+
+ if (dquot->dq_dqb.dqb_isoftlimit &&
+ (dquot->dq_dqb.dqb_curinodes + inodes) > dquot->dq_dqb.dqb_isoftlimit &&
+ dquot->dq_dqb.dqb_itime && CURRENT_TIME >= dquot->dq_dqb.dqb_itime &&
+ !ignore_hardlimit(dquot)) {
+ *warntype = ISOFTLONGWARN;
+ return NO_QUOTA;
+ }
+
+ if (dquot->dq_dqb.dqb_isoftlimit &&
+ (dquot->dq_dqb.dqb_curinodes + inodes) > dquot->dq_dqb.dqb_isoftlimit &&
+ dquot->dq_dqb.dqb_itime == 0) {
+ *warntype = ISOFTWARN;
+ dquot->dq_dqb.dqb_itime = CURRENT_TIME + sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_igrace;
+ }
+
+ return QUOTA_OK;
+}
+
+static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *warntype)
+{
+ *warntype = 0;
+ if (space <= 0 || dquot->dq_flags & DQ_FAKE)
+ return QUOTA_OK;
+
+ if (dquot->dq_dqb.dqb_bhardlimit &&
+ toqb(dquot->dq_dqb.dqb_curspace + space) > dquot->dq_dqb.dqb_bhardlimit &&
+ !ignore_hardlimit(dquot)) {
+ if (!prealloc)
+ *warntype = BHARDWARN;
+ return NO_QUOTA;
+ }
+
+ if (dquot->dq_dqb.dqb_bsoftlimit &&
+ toqb(dquot->dq_dqb.dqb_curspace + space) > dquot->dq_dqb.dqb_bsoftlimit &&
+ dquot->dq_dqb.dqb_btime && CURRENT_TIME >= dquot->dq_dqb.dqb_btime &&
+ !ignore_hardlimit(dquot)) {
+ if (!prealloc)
+ *warntype = BSOFTLONGWARN;
+ return NO_QUOTA;
+ }
+
+ if (dquot->dq_dqb.dqb_bsoftlimit &&
+ toqb(dquot->dq_dqb.dqb_curspace + space) > dquot->dq_dqb.dqb_bsoftlimit &&
+ dquot->dq_dqb.dqb_btime == 0) {
+ if (!prealloc) {
+ *warntype = BSOFTWARN;
+ dquot->dq_dqb.dqb_btime = CURRENT_TIME + sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_bgrace;
+ }
+ else
+ /*
+ * We don't allow preallocation to exceed softlimit so exceeding will
+ * be always printed
+ */
+ return NO_QUOTA;
+ }
+
+ return QUOTA_OK;
+}
+
+/*
+ * Externally referenced functions through dquot_operations in inode.
+ *
+ * Note: this is a blocking operation.
+ */
+void dquot_initialize(struct inode *inode, int type)
+{
+ struct dquot *dquot[MAXQUOTAS];
+ unsigned int id = 0;
+ int cnt;
+
+ if (IS_NOQUOTA(inode))
+ return;
+ /* Build list of quotas to initialize... We can block here */
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ dquot[cnt] = NODQUOT;
+ if (type != -1 && cnt != type)
+ continue;
+ if (!sb_has_quota_enabled(inode->i_sb, cnt))
+ continue;
+ if (inode->i_dquot[cnt] == NODQUOT) {
+ switch (cnt) {
+ case USRQUOTA:
+ id = inode->i_uid;
+ break;
+ case GRPQUOTA:
+ id = inode->i_gid;
+ break;
+ }
+ dquot[cnt] = dqget(inode->i_sb, id, cnt);
+ }
+ }
+ /* NOBLOCK START: Here we shouldn't block */
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ if (dquot[cnt] == NODQUOT || !sb_has_quota_enabled(inode->i_sb, cnt) || inode->i_dquot[cnt] != NODQUOT)
+ continue;
+ inode->i_dquot[cnt] = dquot[cnt];
+ dquot[cnt] = NODQUOT;
+ inode->i_flags |= S_QUOTA;
+ }
+ /* NOBLOCK END */
+ /* Put quotas which we didn't use */
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++)
+ if (dquot[cnt] != NODQUOT)
+ dqput(dquot[cnt]);
+}
+
+/*
+ * Release all quota for the specified inode.
+ *
+ * Note: this is a blocking operation.
+ */
+void dquot_drop(struct inode *inode)
+{
+ struct dquot *dquot;
+ int cnt;
+
+ inode->i_flags &= ~S_QUOTA;
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ if (inode->i_dquot[cnt] == NODQUOT)
+ continue;
+ dquot = inode->i_dquot[cnt];
+ inode->i_dquot[cnt] = NODQUOT;
+ dqput(dquot);
+ }
+}
+
+/*
+ * This operation can block, but only after everything is updated
+ */
+int dquot_alloc_space(struct inode *inode, qsize_t number, int warn)
+{
+ int cnt, ret = NO_QUOTA;
+ struct dquot *dquot[MAXQUOTAS];
+ char warntype[MAXQUOTAS];
+
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ dquot[cnt] = NODQUOT;
+ warntype[cnt] = NOWARN;
+ }
+ /* NOBLOCK Start */
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ dquot[cnt] = dqduplicate(inode->i_dquot[cnt]);
+ if (dquot[cnt] == NODQUOT)
+ continue;
+ if (check_bdq(dquot[cnt], number, warn, warntype+cnt) == NO_QUOTA)
+ goto warn_put_all;
+ }
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ if (dquot[cnt] == NODQUOT)
+ continue;
+ dquot_incr_space(dquot[cnt], number);
+ }
+ inode_add_bytes(inode, number);
+ /* NOBLOCK End */
+ ret = QUOTA_OK;
+warn_put_all:
+ flush_warnings(dquot, warntype);
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++)
+ if (dquot[cnt] != NODQUOT)
+ dqputduplicate(dquot[cnt]);
+ return ret;
+}
+
+/*
+ * This operation can block, but only after everything is updated
+ */
+int dquot_alloc_inode(const struct inode *inode, unsigned long number)
+{
+ int cnt, ret = NO_QUOTA;
+ struct dquot *dquot[MAXQUOTAS];
+ char warntype[MAXQUOTAS];
+
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ dquot[cnt] = NODQUOT;
+ warntype[cnt] = NOWARN;
+ }
+ /* NOBLOCK Start */
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ dquot[cnt] = dqduplicate(inode -> i_dquot[cnt]);
+ if (dquot[cnt] == NODQUOT)
+ continue;
+ if (check_idq(dquot[cnt], number, warntype+cnt) == NO_QUOTA)
+ goto warn_put_all;
+ }
+
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ if (dquot[cnt] == NODQUOT)
+ continue;
+ dquot_incr_inodes(dquot[cnt], number);
+ }
+ /* NOBLOCK End */
+ ret = QUOTA_OK;
+warn_put_all:
+ flush_warnings(dquot, warntype);
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++)
+ if (dquot[cnt] != NODQUOT)
+ dqputduplicate(dquot[cnt]);
+ return ret;
+}
+
+/*
+ * This is a non-blocking operation.
+ */
+void dquot_free_space(struct inode *inode, qsize_t number)
+{
+ unsigned int cnt;
+ struct dquot *dquot;
+
+ /* NOBLOCK Start */
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ dquot = dqduplicate(inode->i_dquot[cnt]);
+ if (dquot == NODQUOT)
+ continue;
+ dquot_decr_space(dquot, number);
+ dqputduplicate(dquot);
+ }
+ inode_sub_bytes(inode, number);
+ /* NOBLOCK End */
+}
+
+/*
+ * This is a non-blocking operation.
+ */
+void dquot_free_inode(const struct inode *inode, unsigned long number)
+{
+ unsigned int cnt;
+ struct dquot *dquot;
+
+ /* NOBLOCK Start */
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ dquot = dqduplicate(inode->i_dquot[cnt]);
+ if (dquot == NODQUOT)
+ continue;
+ dquot_decr_inodes(dquot, number);
+ dqputduplicate(dquot);
+ }
+ /* NOBLOCK End */
+}
+
+/*
+ * Transfer the number of inode and blocks from one diskquota to an other.
+ *
+ * This operation can block, but only after everything is updated
+ */
+int dquot_transfer(struct inode *inode, struct iattr *iattr)
+{
+ qsize_t space;
+ struct dquot *transfer_from[MAXQUOTAS];
+ struct dquot *transfer_to[MAXQUOTAS];
+ int cnt, ret = NO_QUOTA, chuid = (iattr->ia_valid & ATTR_UID) && inode->i_uid != iattr->ia_uid,
+ chgid = (iattr->ia_valid & ATTR_GID) && inode->i_gid != iattr->ia_gid;
+ char warntype[MAXQUOTAS];
+
+ /* Clear the arrays */
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ transfer_to[cnt] = transfer_from[cnt] = NODQUOT;
+ warntype[cnt] = NOWARN;
+ }
+ /* First build the transfer_to list - here we can block on reading of dquots... */
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ if (!sb_has_quota_enabled(inode->i_sb, cnt))
+ continue;
+ switch (cnt) {
+ case USRQUOTA:
+ if (!chuid)
+ continue;
+ transfer_to[cnt] = dqget(inode->i_sb, iattr->ia_uid, cnt);
+ break;
+ case GRPQUOTA:
+ if (!chgid)
+ continue;
+ transfer_to[cnt] = dqget(inode->i_sb, iattr->ia_gid, cnt);
+ break;
+ }
+ }
+ /* NOBLOCK START: From now on we shouldn't block */
+ space = inode_get_bytes(inode);
+ /* Build the transfer_from list and check the limits */
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ /* The second test can fail when quotaoff is in progress... */
+ if (transfer_to[cnt] == NODQUOT || !sb_has_quota_enabled(inode->i_sb, cnt))
+ continue;
+ transfer_from[cnt] = dqduplicate(inode->i_dquot[cnt]);
+ if (transfer_from[cnt] == NODQUOT) /* Can happen on quotafiles (quota isn't initialized on them)... */
+ continue;
+ if (check_idq(transfer_to[cnt], 1, warntype+cnt) == NO_QUOTA ||
+ check_bdq(transfer_to[cnt], space, 0, warntype+cnt) == NO_QUOTA)
+ goto warn_put_all;
+ }
+
+ /*
+ * Finally perform the needed transfer from transfer_from to transfer_to
+ */
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ /*
+ * Skip changes for same uid or gid or for non-existing quota-type.
+ */
+ if (transfer_from[cnt] == NODQUOT || transfer_to[cnt] == NODQUOT)
+ continue;
+
+ dquot_decr_inodes(transfer_from[cnt], 1);
+ dquot_decr_space(transfer_from[cnt], space);
+
+ dquot_incr_inodes(transfer_to[cnt], 1);
+ dquot_incr_space(transfer_to[cnt], space);
+
+ if (inode->i_dquot[cnt] == NODQUOT)
+ BUG();
+ inode->i_dquot[cnt] = transfer_to[cnt];
+ /*
+ * We've got to release transfer_from[] twice - once for dquot_transfer() and
+ * once for inode. We don't want to release transfer_to[] as it's now placed in inode
+ */
+ transfer_to[cnt] = transfer_from[cnt];
+ }
+ /* NOBLOCK END. From now on we can block as we wish */
+ ret = QUOTA_OK;
+warn_put_all:
+ flush_warnings(transfer_to, warntype);
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ /* First we must put duplicate - otherwise we might deadlock */
+ if (transfer_from[cnt] != NODQUOT)
+ dqputduplicate(transfer_from[cnt]);
+ if (transfer_to[cnt] != NODQUOT)
+ dqput(transfer_to[cnt]);
+ }
+ return ret;
+}
+
+/*
+ * Definitions of diskquota operations.
+ */
+struct dquot_operations dquot_operations = {
+ initialize: dquot_initialize, /* mandatory */
+ drop: dquot_drop, /* mandatory */
+ alloc_space: dquot_alloc_space,
+ alloc_inode: dquot_alloc_inode,
+ free_space: dquot_free_space,
+ free_inode: dquot_free_inode,
+ transfer: dquot_transfer,
+ write_dquot: commit_dqblk
+};
+
+/* Function used by filesystems for initializing the dquot_operations structure */
+void init_dquot_operations(struct dquot_operations *fsdqops)
+{
+ memcpy(fsdqops, &dquot_operations, sizeof(dquot_operations));
+}
+
+static inline void set_enable_flags(struct quota_info *dqopt, int type)
+{
+ switch (type) {
+ case USRQUOTA:
+ dqopt->flags |= DQUOT_USR_ENABLED;
+ break;
+ case GRPQUOTA:
+ dqopt->flags |= DQUOT_GRP_ENABLED;
+ break;
+ }
+}
+
+static inline void reset_enable_flags(struct quota_info *dqopt, int type)
+{
+ switch (type) {
+ case USRQUOTA:
+ dqopt->flags &= ~DQUOT_USR_ENABLED;
+ break;
+ case GRPQUOTA:
+ dqopt->flags &= ~DQUOT_GRP_ENABLED;
+ break;
+ }
+}
+
+/* Function in inode.c - remove pointers to dquots in icache */
+extern void remove_dquot_ref(struct super_block *, int);
+
+/*
+ * Turn quota off on a device. type == -1 ==> quotaoff for all types (umount)
+ */
+int vfs_quota_off(struct super_block *sb, int type)
+{
+ int cnt;
+ struct quota_info *dqopt = sb_dqopt(sb);
+
+ lock_kernel();
+ if (!sb)
+ goto out;
+
+ /* We need to serialize quota_off() for device */
+ down(&dqopt->dqoff_sem);
+ for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ if (type != -1 && cnt != type)
+ continue;
+ if (!is_enabled(dqopt, cnt))
+ continue;
+ reset_enable_flags(dqopt, cnt);
+
+ /* Note: these are blocking operations */
+ remove_dquot_ref(sb, cnt);
+ invalidate_dquots(sb, cnt);
+ if (info_dirty(&dqopt->info[cnt]))
+ dqopt->ops[cnt]->write_file_info(sb, cnt);
+ if (dqopt->ops[cnt]->free_file_info)
+ dqopt->ops[cnt]->free_file_info(sb, cnt);
+ put_quota_format(dqopt->info[cnt].dqi_format);
+
+ fput(dqopt->files[cnt]);
+ dqopt->files[cnt] = (struct file *)NULL;
+ dqopt->info[cnt].dqi_flags = 0;
+ dqopt->info[cnt].dqi_igrace = 0;
+ dqopt->info[cnt].dqi_bgrace = 0;
+ dqopt->ops[cnt] = NULL;
+ }
+ up(&dqopt->dqoff_sem);
+out:
+ unlock_kernel();
+ return 0;
+}
+
+int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path)
+{
+ struct file *f = NULL;
+ struct inode *inode;
+ struct quota_info *dqopt = sb_dqopt(sb);
+ struct quota_format_type *fmt = find_quota_format(format_id);
+ int error;
+
+ if (!fmt)
+ return -ESRCH;
+ if (is_enabled(dqopt, type)) {
+ error = -EBUSY;
+ goto out_fmt;
+ }
+
+ down(&dqopt->dqoff_sem);
+
+ f = filp_open(path, O_RDWR, 0600);
+
+ error = PTR_ERR(f);
+ if (IS_ERR(f))
+ goto out_lock;
+ dqopt->files[type] = f;
+ error = -EIO;
+ if (!f->f_op || !f->f_op->read || !f->f_op->write)
+ goto out_f;
+ inode = f->f_dentry->d_inode;
+ error = -EACCES;
+ if (!S_ISREG(inode->i_mode))
+ goto out_f;
+ error = -EINVAL;
+ if (!fmt->qf_ops->check_quota_file(sb, type))
+ goto out_f;
+ /* We don't want quota and atime on quota files */
+ dquot_drop(inode);
+ inode->i_flags |= S_NOQUOTA | S_NOATIME;
+
+ dqopt->ops[type] = fmt->qf_ops;
+ dqopt->info[type].dqi_format = fmt;
+ if ((error = dqopt->ops[type]->read_file_info(sb, type)) < 0)
+ goto out_f;
+ set_enable_flags(dqopt, type);
+
+ add_dquot_ref(sb, type);
+
+ up(&dqopt->dqoff_sem);
+ return 0;
+
+out_f:
+ if (f)
+ filp_close(f, NULL);
+ dqopt->files[type] = NULL;
+out_lock:
+ up(&dqopt->dqoff_sem);
+out_fmt:
+ put_quota_format(fmt);
+
+ return error;
+}
+
+/* Generic routine for getting common part of quota structure */
+static void do_get_dqblk(struct dquot *dquot, struct if_dqblk *di)
+{
+ struct mem_dqblk *dm = &dquot->dq_dqb;
+
+ di->dqb_bhardlimit = dm->dqb_bhardlimit;
+ di->dqb_bsoftlimit = dm->dqb_bsoftlimit;
+ di->dqb_curspace = dm->dqb_curspace;
+ di->dqb_ihardlimit = dm->dqb_ihardlimit;
+ di->dqb_isoftlimit = dm->dqb_isoftlimit;
+ di->dqb_curinodes = dm->dqb_curinodes;
+ di->dqb_btime = dm->dqb_btime;
+ di->dqb_itime = dm->dqb_itime;
+ di->dqb_valid = QIF_ALL;
+}
+
+int vfs_get_dqblk(struct super_block *sb, int type, qid_t id, struct if_dqblk *di)
+{
+ struct dquot *dquot = dqget(sb, id, type);
+
+ if (!dquot)
+ return -EINVAL;
+ do_get_dqblk(dquot, di);
+ dqput(dquot);
+ return 0;
+}
+
+/* Generic routine for setting common part of quota structure */
+static void do_set_dqblk(struct dquot *dquot, struct if_dqblk *di)
+{
+ struct mem_dqblk *dm = &dquot->dq_dqb;
+ int check_blim = 0, check_ilim = 0;
+
+ if (di->dqb_valid & QIF_SPACE) {
+ dm->dqb_curspace = di->dqb_curspace;
+ check_blim = 1;
+ }
+ if (di->dqb_valid & QIF_BLIMITS) {
+ dm->dqb_bsoftlimit = di->dqb_bsoftlimit;
+ dm->dqb_bhardlimit = di->dqb_bhardlimit;
+ check_blim = 1;
+ }
+ if (di->dqb_valid & QIF_INODES) {
+ dm->dqb_curinodes = di->dqb_curinodes;
+ check_ilim = 1;
+ }
+ if (di->dqb_valid & QIF_ILIMITS) {
+ dm->dqb_isoftlimit = di->dqb_isoftlimit;
+ dm->dqb_ihardlimit = di->dqb_ihardlimit;
+ check_ilim = 1;
+ }
+ if (di->dqb_valid & QIF_BTIME)
+ dm->dqb_btime = di->dqb_btime;
+ if (di->dqb_valid & QIF_ITIME)
+ dm->dqb_itime = di->dqb_itime;
+
+ if (check_blim) {
+ if (!dm->dqb_bsoftlimit || toqb(dm->dqb_curspace) < dm->dqb_bsoftlimit) {
+ dm->dqb_btime = 0;
+ dquot->dq_flags &= ~DQ_BLKS;
+ }
+ else if (!(di->dqb_valid & QIF_BTIME)) /* Set grace only if user hasn't provided his own... */
+ dm->dqb_btime = CURRENT_TIME + sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_bgrace;
+ }
+ if (check_ilim) {
+ if (!dm->dqb_isoftlimit || dm->dqb_curinodes < dm->dqb_isoftlimit) {
+ dm->dqb_itime = 0;
+ dquot->dq_flags &= ~DQ_INODES;
+ }
+ else if (!(di->dqb_valid & QIF_ITIME)) /* Set grace only if user hasn't provided his own... */
+ dm->dqb_itime = CURRENT_TIME + sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_igrace;
+ }
+ if (dm->dqb_bhardlimit || dm->dqb_bsoftlimit || dm->dqb_ihardlimit || dm->dqb_isoftlimit)
+ dquot->dq_flags &= ~DQ_FAKE;
+ else
+ dquot->dq_flags |= DQ_FAKE;
+ dquot->dq_flags |= DQ_MOD;
+}
+
+int vfs_set_dqblk(struct super_block *sb, int type, qid_t id, struct if_dqblk *di)
+{
+ struct dquot *dquot = dqget(sb, id, type);
+
+ if (!dquot)
+ return -EINVAL;
+ do_set_dqblk(dquot, di);
+ dqput(dquot);
+ return 0;
+}
+
+/* Generic routine for getting common part of quota file information */
+int vfs_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii)
+{
+ struct mem_dqinfo *mi = sb_dqopt(sb)->info + type;
+
+ ii->dqi_bgrace = mi->dqi_bgrace;
+ ii->dqi_igrace = mi->dqi_igrace;
+ ii->dqi_flags = mi->dqi_flags & DQF_MASK;
+ ii->dqi_valid = IIF_ALL;
+ return 0;
+}
+
+/* Generic routine for setting common part of quota file information */
+int vfs_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii)
+{
+ struct mem_dqinfo *mi = sb_dqopt(sb)->info + type;
+
+ if (ii->dqi_valid & IIF_BGRACE)
+ mi->dqi_bgrace = ii->dqi_bgrace;
+ if (ii->dqi_valid & IIF_IGRACE)
+ mi->dqi_igrace = ii->dqi_igrace;
+ if (ii->dqi_valid & IIF_FLAGS)
+ mi->dqi_flags = (mi->dqi_flags & ~DQF_MASK) | (ii->dqi_flags & DQF_MASK);
+ mark_info_dirty(mi);
+ return 0;
+}
+
+struct quotactl_ops vfs_quotactl_ops = {
+ quota_on: vfs_quota_on,
+ quota_off: vfs_quota_off,
+ quota_sync: vfs_quota_sync,
+ get_info: vfs_get_dqinfo,
+ set_info: vfs_set_dqinfo,
+ get_dqblk: vfs_get_dqblk,
+ set_dqblk: vfs_set_dqblk
+};
+
+static ctl_table fs_dqstats_table[] = {
+ {FS_DQ_LOOKUPS, "lookups", &dqstats.lookups, sizeof(int), 0444, NULL, &proc_dointvec},
+ {FS_DQ_DROPS, "drops", &dqstats.drops, sizeof(int), 0444, NULL, &proc_dointvec},
+ {FS_DQ_READS, "reads", &dqstats.reads, sizeof(int), 0444, NULL, &proc_dointvec},
+ {FS_DQ_WRITES, "writes", &dqstats.writes, sizeof(int), 0444, NULL, &proc_dointvec},
+ {FS_DQ_CACHE_HITS, "cache_hits", &dqstats.cache_hits, sizeof(int), 0444, NULL, &proc_dointvec},
+ {FS_DQ_ALLOCATED, "allocated_dquots", &dqstats.allocated_dquots, sizeof(int), 0444, NULL, &proc_dointvec},
+ {FS_DQ_FREE, "free_dquots", &dqstats.free_dquots, sizeof(int), 0444, NULL, &proc_dointvec},
+ {FS_DQ_SYNCS, "syncs", &dqstats.syncs, sizeof(int), 0444, NULL, &proc_dointvec},
+ {FS_DQ_WARNINGS, "warnings", &flag_print_warnings, sizeof(int), 0644, NULL, &proc_dointvec},
+ {},
+};
+
+static ctl_table fs_table[] = {
+ {FS_DQSTATS, "quota", NULL, 0, 0555, fs_dqstats_table},
+ {},
+};
+
+static ctl_table sys_table[] = {
+ {CTL_FS, "fs", NULL, 0, 0555, fs_table},
+ {},
+};
+
+static int __init dquot_init(void)
+{
+ int i;
+
+ register_sysctl_table(sys_table, 0);
+ for (i = 0; i < NR_DQHASH; i++)
+ INIT_LIST_HEAD(dquot_hash + i);
+ printk(KERN_NOTICE "VFS: Disk quotas v%s\n", __DQUOT_VERSION__);
+
+ return 0;
+}
+__initcall(dquot_init);
+
+EXPORT_SYMBOL(register_quota_format);
+EXPORT_SYMBOL(unregister_quota_format);
+EXPORT_SYMBOL(dqstats);
+EXPORT_SYMBOL(init_dquot_operations);
diff --git a/fs/efs/Makefile b/fs/efs/Makefile
new file mode 100644
index 00000000000000..9352c722b925f5
--- /dev/null
+++ b/fs/efs/Makefile
@@ -0,0 +1,15 @@
+#
+# Makefile for the linux efs-filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+O_TARGET := efs.o
+
+obj-y := super.o inode.o namei.o dir.o file.o symlink.o
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/efs/dir.c b/fs/efs/dir.c
new file mode 100644
index 00000000000000..cc7df77a034adf
--- /dev/null
+++ b/fs/efs/dir.c
@@ -0,0 +1,107 @@
+/*
+ * dir.c
+ *
+ * Copyright (c) 1999 Al Smith
+ */
+
+#include <linux/efs_fs.h>
+
+static int efs_readdir(struct file *, void *, filldir_t);
+
+struct file_operations efs_dir_operations = {
+ read: generic_read_dir,
+ readdir: efs_readdir,
+};
+
+struct inode_operations efs_dir_inode_operations = {
+ lookup: efs_lookup,
+};
+
+static int efs_readdir(struct file *filp, void *dirent, filldir_t filldir) {
+ struct inode *inode = filp->f_dentry->d_inode;
+ struct buffer_head *bh;
+
+ struct efs_dir *dirblock;
+ struct efs_dentry *dirslot;
+ efs_ino_t inodenum;
+ efs_block_t block;
+ int slot, namelen;
+ char *nameptr;
+
+ if (inode->i_size & (EFS_DIRBSIZE-1))
+ printk(KERN_WARNING "EFS: WARNING: readdir(): directory size not a multiple of EFS_DIRBSIZE\n");
+
+ /* work out where this entry can be found */
+ block = filp->f_pos >> EFS_DIRBSIZE_BITS;
+
+ /* each block contains at most 256 slots */
+ slot = filp->f_pos & 0xff;
+
+ /* look at all blocks */
+ while (block < inode->i_blocks) {
+ /* read the dir block */
+ bh = sb_bread(inode->i_sb, efs_bmap(inode, block));
+
+ if (!bh) {
+ printk(KERN_ERR "EFS: readdir(): failed to read dir block %d\n", block);
+ break;
+ }
+
+ dirblock = (struct efs_dir *) bh->b_data;
+
+ if (be16_to_cpu(dirblock->magic) != EFS_DIRBLK_MAGIC) {
+ printk(KERN_ERR "EFS: readdir(): invalid directory block\n");
+ brelse(bh);
+ break;
+ }
+
+ while (slot < dirblock->slots) {
+ if (dirblock->space[slot] == 0) {
+ slot++;
+ continue;
+ }
+
+ dirslot = (struct efs_dentry *) (((char *) bh->b_data) + EFS_SLOTAT(dirblock, slot));
+
+ inodenum = be32_to_cpu(dirslot->inode);
+ namelen = dirslot->namelen;
+ nameptr = dirslot->name;
+
+#ifdef DEBUG
+ printk(KERN_DEBUG "EFS: readdir(): block %d slot %d/%d: inode %u, name \"%s\", namelen %u\n", block, slot, dirblock->slots-1, inodenum, nameptr, namelen);
+#endif
+ if (namelen > 0) {
+ /* found the next entry */
+ filp->f_pos = (block << EFS_DIRBSIZE_BITS) | slot;
+
+ /* copy filename and data in dirslot */
+ filldir(dirent, nameptr, namelen, filp->f_pos, inodenum, DT_UNKNOWN);
+
+ /* sanity check */
+ if (nameptr - (char *) dirblock + namelen > EFS_DIRBSIZE) {
+ printk(KERN_WARNING "EFS: directory entry %d exceeds directory block\n", slot);
+ slot++;
+ continue;
+ }
+
+ /* store position of next slot */
+ if (++slot == dirblock->slots) {
+ slot = 0;
+ block++;
+ }
+ brelse(bh);
+ filp->f_pos = (block << EFS_DIRBSIZE_BITS) | slot;
+ return 0;
+ }
+ slot++;
+ }
+ brelse(bh);
+
+ slot = 0;
+ block++;
+ }
+
+ filp->f_pos = (block << EFS_DIRBSIZE_BITS) | slot;
+ return 0;
+}
+
diff --git a/fs/efs/file.c b/fs/efs/file.c
new file mode 100644
index 00000000000000..67f58987ead05b
--- /dev/null
+++ b/fs/efs/file.c
@@ -0,0 +1,62 @@
+/*
+ * file.c
+ *
+ * Copyright (c) 1999 Al Smith
+ *
+ * Portions derived from work (c) 1995,1996 Christian Vogelgsang.
+ */
+
+#include <linux/efs_fs.h>
+
+int efs_get_block(struct inode *inode, long iblock,
+ struct buffer_head *bh_result, int create)
+{
+ int error = -EROFS;
+ long phys;
+
+ if (create)
+ return error;
+ if (iblock >= inode->i_blocks) {
+#ifdef DEBUG
+ /*
+ * i have no idea why this happens as often as it does
+ */
+ printk(KERN_WARNING "EFS: bmap(): block %d >= %ld (filesize %ld)\n",
+ block,
+ inode->i_blocks,
+ inode->i_size);
+#endif
+ return 0;
+ }
+ phys = efs_map_block(inode, iblock);
+ if (phys) {
+ bh_result->b_dev = inode->i_dev;
+ bh_result->b_blocknr = phys;
+ bh_result->b_state |= (1UL << BH_Mapped);
+ }
+ return 0;
+}
+
+int efs_bmap(struct inode *inode, efs_block_t block) {
+
+ if (block < 0) {
+ printk(KERN_WARNING "EFS: bmap(): block < 0\n");
+ return 0;
+ }
+
+ /* are we about to read past the end of a file ? */
+ if (!(block < inode->i_blocks)) {
+#ifdef DEBUG
+ /*
+ * i have no idea why this happens as often as it does
+ */
+ printk(KERN_WARNING "EFS: bmap(): block %d >= %ld (filesize %ld)\n",
+ block,
+ inode->i_blocks,
+ inode->i_size);
+#endif
+ return 0;
+ }
+
+ return efs_map_block(inode, block);
+}
diff --git a/fs/efs/inode.c b/fs/efs/inode.c
new file mode 100644
index 00000000000000..ba03990abe4619
--- /dev/null
+++ b/fs/efs/inode.c
@@ -0,0 +1,307 @@
+/*
+ * inode.c
+ *
+ * Copyright (c) 1999 Al Smith
+ *
+ * Portions derived from work (c) 1995,1996 Christian Vogelgsang,
+ * and from work (c) 1998 Mike Shaver.
+ */
+
+#include <linux/efs_fs.h>
+#include <linux/efs_fs_sb.h>
+#include <linux/module.h>
+
+
+extern int efs_get_block(struct inode *, long, struct buffer_head *, int);
+static int efs_readpage(struct file *file, struct page *page)
+{
+ return block_read_full_page(page,efs_get_block);
+}
+static int _efs_bmap(struct address_space *mapping, long block)
+{
+ return generic_block_bmap(mapping,block,efs_get_block);
+}
+struct address_space_operations efs_aops = {
+ readpage: efs_readpage,
+ sync_page: block_sync_page,
+ bmap: _efs_bmap
+};
+
+static inline void extent_copy(efs_extent *src, efs_extent *dst) {
+ /*
+ * this is slightly evil. it doesn't just copy
+ * efs_extent from src to dst, it also mangles
+ * the bits so that dst ends up in cpu byte-order.
+ */
+
+ dst->cooked.ex_magic = (unsigned int) src->raw[0];
+ dst->cooked.ex_bn = ((unsigned int) src->raw[1] << 16) |
+ ((unsigned int) src->raw[2] << 8) |
+ ((unsigned int) src->raw[3] << 0);
+ dst->cooked.ex_length = (unsigned int) src->raw[4];
+ dst->cooked.ex_offset = ((unsigned int) src->raw[5] << 16) |
+ ((unsigned int) src->raw[6] << 8) |
+ ((unsigned int) src->raw[7] << 0);
+ return;
+}
+
+void efs_read_inode(struct inode *inode) {
+ int i, inode_index;
+ dev_t device;
+ struct buffer_head *bh;
+ struct efs_sb_info *sb = SUPER_INFO(inode->i_sb);
+ struct efs_inode_info *in = INODE_INFO(inode);
+ efs_block_t block, offset;
+ struct efs_dinode *efs_inode;
+
+ /*
+ ** EFS layout:
+ **
+ ** | cylinder group | cylinder group | cylinder group ..etc
+ ** |inodes|data |inodes|data |inodes|data ..etc
+ **
+ ** work out the inode block index, (considering initially that the
+ ** inodes are stored as consecutive blocks). then work out the block
+ ** number of that inode given the above layout, and finally the
+ ** offset of the inode within that block.
+ */
+
+ inode_index = inode->i_ino /
+ (EFS_BLOCKSIZE / sizeof(struct efs_dinode));
+
+ block = sb->fs_start + sb->first_block +
+ (sb->group_size * (inode_index / sb->inode_blocks)) +
+ (inode_index % sb->inode_blocks);
+
+ offset = (inode->i_ino %
+ (EFS_BLOCKSIZE / sizeof(struct efs_dinode))) *
+ sizeof(struct efs_dinode);
+
+ bh = sb_bread(inode->i_sb, block);
+ if (!bh) {
+ printk(KERN_WARNING "EFS: bread() failed at block %d\n", block);
+ goto read_inode_error;
+ }
+
+ efs_inode = (struct efs_dinode *) (bh->b_data + offset);
+
+ inode->i_mode = be16_to_cpu(efs_inode->di_mode);
+ inode->i_nlink = be16_to_cpu(efs_inode->di_nlink);
+ inode->i_uid = (uid_t)be16_to_cpu(efs_inode->di_uid);
+ inode->i_gid = (gid_t)be16_to_cpu(efs_inode->di_gid);
+ inode->i_size = be32_to_cpu(efs_inode->di_size);
+ inode->i_atime = be32_to_cpu(efs_inode->di_atime);
+ inode->i_mtime = be32_to_cpu(efs_inode->di_mtime);
+ inode->i_ctime = be32_to_cpu(efs_inode->di_ctime);
+
+ /* this is the number of blocks in the file */
+ if (inode->i_size == 0) {
+ inode->i_blocks = 0;
+ } else {
+ inode->i_blocks = ((inode->i_size - 1) >> EFS_BLOCKSIZE_BITS) + 1;
+ }
+
+ /*
+ * BUG: irix dev_t is 32-bits. linux dev_t is only 16-bits.
+ *
+ * apparently linux will change to 32-bit dev_t sometime during
+ * linux 2.3.
+ *
+ * as is, this code maps devices that can't be represented in
+ * 16-bits (ie major > 255 or minor > 255) to major = minor = 255.
+ *
+ * during 2.3 when 32-bit dev_t become available, we should test
+ * to see whether odev contains 65535. if this is the case then we
+ * should then do device = be32_to_cpu(efs_inode->di_u.di_dev.ndev).
+ */
+ device = be16_to_cpu(efs_inode->di_u.di_dev.odev);
+
+ /* get the number of extents for this object */
+ in->numextents = be16_to_cpu(efs_inode->di_numextents);
+ in->lastextent = 0;
+
+ /* copy the extents contained within the inode to memory */
+ for(i = 0; i < EFS_DIRECTEXTENTS; i++) {
+ extent_copy(&(efs_inode->di_u.di_extents[i]), &(in->extents[i]));
+ if (i < in->numextents && in->extents[i].cooked.ex_magic != 0) {
+ printk(KERN_WARNING "EFS: extent %d has bad magic number in inode %lu\n", i, inode->i_ino);
+ brelse(bh);
+ goto read_inode_error;
+ }
+ }
+
+ brelse(bh);
+
+#ifdef DEBUG
+ printk(KERN_DEBUG "EFS: read_inode(): inode %lu, extents %d, mode %o\n",
+ inode->i_ino, in->numextents, inode->i_mode);
+#endif
+
+ switch (inode->i_mode & S_IFMT) {
+ case S_IFDIR:
+ inode->i_op = &efs_dir_inode_operations;
+ inode->i_fop = &efs_dir_operations;
+ break;
+ case S_IFREG:
+ inode->i_fop = &generic_ro_fops;
+ inode->i_data.a_ops = &efs_aops;
+ break;
+ case S_IFLNK:
+ inode->i_op = &page_symlink_inode_operations;
+ inode->i_data.a_ops = &efs_symlink_aops;
+ break;
+ case S_IFCHR:
+ case S_IFBLK:
+ case S_IFIFO:
+ init_special_inode(inode, inode->i_mode, device);
+ break;
+ default:
+ printk(KERN_WARNING "EFS: unsupported inode mode %o\n", inode->i_mode);
+ goto read_inode_error;
+ break;
+ }
+
+ return;
+
+read_inode_error:
+ printk(KERN_WARNING "EFS: failed to read inode %lu\n", inode->i_ino);
+ make_bad_inode(inode);
+
+ return;
+}
+
+static inline efs_block_t
+efs_extent_check(efs_extent *ptr, efs_block_t block, struct efs_sb_info *sb) {
+ efs_block_t start;
+ efs_block_t length;
+ efs_block_t offset;
+
+ /*
+ * given an extent and a logical block within a file,
+ * can this block be found within this extent ?
+ */
+ start = ptr->cooked.ex_bn;
+ length = ptr->cooked.ex_length;
+ offset = ptr->cooked.ex_offset;
+
+ if ((block >= offset) && (block < offset+length)) {
+ return(sb->fs_start + start + block - offset);
+ } else {
+ return 0;
+ }
+}
+
+efs_block_t efs_map_block(struct inode *inode, efs_block_t block) {
+ struct efs_sb_info *sb = SUPER_INFO(inode->i_sb);
+ struct efs_inode_info *in = INODE_INFO(inode);
+ struct buffer_head *bh = NULL;
+
+ int cur, last, first = 1;
+ int ibase, ioffset, dirext, direxts, indext, indexts;
+ efs_block_t iblock, result = 0, lastblock = 0;
+ efs_extent ext, *exts;
+
+ last = in->lastextent;
+
+ if (in->numextents <= EFS_DIRECTEXTENTS) {
+ /* first check the last extent we returned */
+ if ((result = efs_extent_check(&in->extents[last], block, sb)))
+ return result;
+
+ /* if we only have one extent then nothing can be found */
+ if (in->numextents == 1) {
+ printk(KERN_ERR "EFS: map_block() failed to map (1 extent)\n");
+ return 0;
+ }
+
+ direxts = in->numextents;
+
+ /*
+ * check the stored extents in the inode
+ * start with next extent and check forwards
+ */
+ for(dirext = 1; dirext < direxts; dirext++) {
+ cur = (last + dirext) % in->numextents;
+ if ((result = efs_extent_check(&in->extents[cur], block, sb))) {
+ in->lastextent = cur;
+ return result;
+ }
+ }
+
+ printk(KERN_ERR "EFS: map_block() failed to map block %u (dir)\n", block);
+ return 0;
+ }
+
+#ifdef DEBUG
+ printk(KERN_DEBUG "EFS: map_block(): indirect search for logical block %u\n", block);
+#endif
+ direxts = in->extents[0].cooked.ex_offset;
+ indexts = in->numextents;
+
+ for(indext = 0; indext < indexts; indext++) {
+ cur = (last + indext) % indexts;
+
+ /*
+ * work out which direct extent contains `cur'.
+ *
+ * also compute ibase: i.e. the number of the first
+ * indirect extent contained within direct extent `cur'.
+ *
+ */
+ ibase = 0;
+ for(dirext = 0; cur < ibase && dirext < direxts; dirext++) {
+ ibase += in->extents[dirext].cooked.ex_length *
+ (EFS_BLOCKSIZE / sizeof(efs_extent));
+ }
+
+ if (dirext == direxts) {
+ /* should never happen */
+ printk(KERN_ERR "EFS: couldn't find direct extent for indirect extent %d (block %u)\n", cur, block);
+ if (bh) brelse(bh);
+ return 0;
+ }
+
+ /* work out block number and offset of this indirect extent */
+ iblock = sb->fs_start + in->extents[dirext].cooked.ex_bn +
+ (cur - ibase) /
+ (EFS_BLOCKSIZE / sizeof(efs_extent));
+ ioffset = (cur - ibase) %
+ (EFS_BLOCKSIZE / sizeof(efs_extent));
+
+ if (first || lastblock != iblock) {
+ if (bh) brelse(bh);
+
+ bh = sb_bread(inode->i_sb, iblock);
+ if (!bh) {
+ printk(KERN_ERR "EFS: bread() failed at block %d\n", iblock);
+ return 0;
+ }
+#ifdef DEBUG
+ printk(KERN_DEBUG "EFS: map_block(): read indirect extent block %d\n", iblock);
+#endif
+ first = 0;
+ lastblock = iblock;
+ }
+
+ exts = (efs_extent *) bh->b_data;
+
+ extent_copy(&(exts[ioffset]), &ext);
+
+ if (ext.cooked.ex_magic != 0) {
+ printk(KERN_ERR "EFS: extent %d has bad magic number in block %d\n", cur, iblock);
+ if (bh) brelse(bh);
+ return 0;
+ }
+
+ if ((result = efs_extent_check(&ext, block, sb))) {
+ if (bh) brelse(bh);
+ in->lastextent = cur;
+ return result;
+ }
+ }
+ if (bh) brelse(bh);
+ printk(KERN_ERR "EFS: map_block() failed to map block %u (indir)\n", block);
+ return 0;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/fs/efs/namei.c b/fs/efs/namei.c
new file mode 100644
index 00000000000000..cc06bc8cbab025
--- /dev/null
+++ b/fs/efs/namei.c
@@ -0,0 +1,76 @@
+/*
+ * namei.c
+ *
+ * Copyright (c) 1999 Al Smith
+ *
+ * Portions derived from work (c) 1995,1996 Christian Vogelgsang.
+ */
+
+#include <linux/string.h>
+#include <linux/efs_fs.h>
+
+static efs_ino_t efs_find_entry(struct inode *inode, const char *name, int len) {
+ struct buffer_head *bh;
+
+ int slot, namelen;
+ char *nameptr;
+ struct efs_dir *dirblock;
+ struct efs_dentry *dirslot;
+ efs_ino_t inodenum;
+ efs_block_t block;
+
+ if (inode->i_size & (EFS_DIRBSIZE-1))
+ printk(KERN_WARNING "EFS: WARNING: find_entry(): directory size not a multiple of EFS_DIRBSIZE\n");
+
+ for(block = 0; block < inode->i_blocks; block++) {
+
+ bh = sb_bread(inode->i_sb, efs_bmap(inode, block));
+ if (!bh) {
+ printk(KERN_ERR "EFS: find_entry(): failed to read dir block %d\n", block);
+ return 0;
+ }
+
+ dirblock = (struct efs_dir *) bh->b_data;
+
+ if (be16_to_cpu(dirblock->magic) != EFS_DIRBLK_MAGIC) {
+ printk(KERN_ERR "EFS: find_entry(): invalid directory block\n");
+ brelse(bh);
+ return(0);
+ }
+
+ for(slot = 0; slot < dirblock->slots; slot++) {
+ dirslot = (struct efs_dentry *) (((char *) bh->b_data) + EFS_SLOTAT(dirblock, slot));
+
+ namelen = dirslot->namelen;
+ nameptr = dirslot->name;
+
+ if ((namelen == len) && (!memcmp(name, nameptr, len))) {
+ inodenum = be32_to_cpu(dirslot->inode);
+ brelse(bh);
+ return(inodenum);
+ }
+ }
+ brelse(bh);
+ }
+ return(0);
+}
+
+struct dentry *efs_lookup(struct inode *dir, struct dentry *dentry) {
+ efs_ino_t inodenum;
+ struct inode * inode;
+
+ if (!dir || !S_ISDIR(dir->i_mode))
+ return ERR_PTR(-ENOENT);
+
+ inode = NULL;
+
+ inodenum = efs_find_entry(dir, dentry->d_name.name, dentry->d_name.len);
+ if (inodenum) {
+ if (!(inode = iget(dir->i_sb, inodenum)))
+ return ERR_PTR(-EACCES);
+ }
+
+ d_add(dentry, inode);
+ return NULL;
+}
+
diff --git a/fs/efs/super.c b/fs/efs/super.c
new file mode 100644
index 00000000000000..f82d8c21ef5baf
--- /dev/null
+++ b/fs/efs/super.c
@@ -0,0 +1,227 @@
+/*
+ * super.c
+ *
+ * Copyright (c) 1999 Al Smith
+ *
+ * Portions derived from work (c) 1995,1996 Christian Vogelgsang.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/locks.h>
+#include <linux/efs_fs.h>
+#include <linux/efs_vh.h>
+#include <linux/efs_fs_sb.h>
+
+static DECLARE_FSTYPE_DEV(efs_fs_type, "efs", efs_read_super);
+
+static struct super_operations efs_superblock_operations = {
+ read_inode: efs_read_inode,
+ statfs: efs_statfs,
+};
+
+static int __init init_efs_fs(void) {
+ printk("EFS: "EFS_VERSION" - http://aeschi.ch.eu.org/efs/\n");
+ return register_filesystem(&efs_fs_type);
+}
+
+static void __exit exit_efs_fs(void) {
+ unregister_filesystem(&efs_fs_type);
+}
+
+EXPORT_NO_SYMBOLS;
+
+module_init(init_efs_fs)
+module_exit(exit_efs_fs)
+
+static efs_block_t efs_validate_vh(struct volume_header *vh) {
+ int i;
+ unsigned int cs, csum, *ui;
+ efs_block_t sblock = 0; /* shuts up gcc */
+ struct pt_types *pt_entry;
+ int pt_type, slice = -1;
+
+ if (be32_to_cpu(vh->vh_magic) != VHMAGIC) {
+ /*
+ * assume that we're dealing with a partition and allow
+ * read_super() to try and detect a valid superblock
+ * on the next block.
+ */
+ return 0;
+ }
+
+ ui = ((unsigned int *) (vh + 1)) - 1;
+ for(csum = 0; ui >= ((unsigned int *) vh);) {
+ cs = *ui--;
+ csum += be32_to_cpu(cs);
+ }
+ if (csum) {
+ printk(KERN_INFO "EFS: SGI disklabel: checksum bad, label corrupted\n");
+ return 0;
+ }
+
+#ifdef DEBUG
+ printk(KERN_DEBUG "EFS: bf: \"%16s\"\n", vh->vh_bootfile);
+
+ for(i = 0; i < NVDIR; i++) {
+ int j;
+ char name[VDNAMESIZE+1];
+
+ for(j = 0; j < VDNAMESIZE; j++) {
+ name[j] = vh->vh_vd[i].vd_name[j];
+ }
+ name[j] = (char) 0;
+
+ if (name[0]) {
+ printk(KERN_DEBUG "EFS: vh: %8s block: 0x%08x size: 0x%08x\n",
+ name,
+ (int) be32_to_cpu(vh->vh_vd[i].vd_lbn),
+ (int) be32_to_cpu(vh->vh_vd[i].vd_nbytes));
+ }
+ }
+#endif
+
+ for(i = 0; i < NPARTAB; i++) {
+ pt_type = (int) be32_to_cpu(vh->vh_pt[i].pt_type);
+ for(pt_entry = sgi_pt_types; pt_entry->pt_name; pt_entry++) {
+ if (pt_type == pt_entry->pt_type) break;
+ }
+#ifdef DEBUG
+ if (be32_to_cpu(vh->vh_pt[i].pt_nblks)) {
+ printk(KERN_DEBUG "EFS: pt %2d: start: %08d size: %08d type: 0x%02x (%s)\n",
+ i,
+ (int) be32_to_cpu(vh->vh_pt[i].pt_firstlbn),
+ (int) be32_to_cpu(vh->vh_pt[i].pt_nblks),
+ pt_type,
+ (pt_entry->pt_name) ? pt_entry->pt_name : "unknown");
+ }
+#endif
+ if (IS_EFS(pt_type)) {
+ sblock = be32_to_cpu(vh->vh_pt[i].pt_firstlbn);
+ slice = i;
+ }
+ }
+
+ if (slice == -1) {
+ printk(KERN_NOTICE "EFS: partition table contained no EFS partitions\n");
+#ifdef DEBUG
+ } else {
+ printk(KERN_INFO "EFS: using slice %d (type %s, offset 0x%x)\n",
+ slice,
+ (pt_entry->pt_name) ? pt_entry->pt_name : "unknown",
+ sblock);
+#endif
+ }
+ return(sblock);
+}
+
+static int efs_validate_super(struct efs_sb_info *sb, struct efs_super *super) {
+
+ if (!IS_EFS_MAGIC(be32_to_cpu(super->fs_magic))) return -1;
+
+ sb->fs_magic = be32_to_cpu(super->fs_magic);
+ sb->total_blocks = be32_to_cpu(super->fs_size);
+ sb->first_block = be32_to_cpu(super->fs_firstcg);
+ sb->group_size = be32_to_cpu(super->fs_cgfsize);
+ sb->data_free = be32_to_cpu(super->fs_tfree);
+ sb->inode_free = be32_to_cpu(super->fs_tinode);
+ sb->inode_blocks = be16_to_cpu(super->fs_cgisize);
+ sb->total_groups = be16_to_cpu(super->fs_ncg);
+
+ return 0;
+}
+
+struct super_block *efs_read_super(struct super_block *s, void *d, int silent) {
+ kdev_t dev = s->s_dev;
+ struct efs_sb_info *sb;
+ struct buffer_head *bh;
+
+ sb = SUPER_INFO(s);
+
+ s->s_magic = EFS_SUPER_MAGIC;
+ s->s_blocksize = EFS_BLOCKSIZE;
+ s->s_blocksize_bits = EFS_BLOCKSIZE_BITS;
+
+ if( set_blocksize(dev, EFS_BLOCKSIZE) < 0)
+ {
+ printk(KERN_ERR "EFS: device does not support %d byte blocks\n",
+ EFS_BLOCKSIZE);
+ goto out_no_fs_ul;
+ }
+
+ /* read the vh (volume header) block */
+ bh = sb_bread(s, 0);
+
+ if (!bh) {
+ printk(KERN_ERR "EFS: cannot read volume header\n");
+ goto out_no_fs_ul;
+ }
+
+ /*
+ * if this returns zero then we didn't find any partition table.
+ * this isn't (yet) an error - just assume for the moment that
+ * the device is valid and go on to search for a superblock.
+ */
+ sb->fs_start = efs_validate_vh((struct volume_header *) bh->b_data);
+ brelse(bh);
+
+ if (sb->fs_start == -1) {
+ goto out_no_fs_ul;
+ }
+
+ bh = sb_bread(s, sb->fs_start + EFS_SUPER);
+ if (!bh) {
+ printk(KERN_ERR "EFS: cannot read superblock\n");
+ goto out_no_fs_ul;
+ }
+
+ if (efs_validate_super(sb, (struct efs_super *) bh->b_data)) {
+#ifdef DEBUG
+ printk(KERN_WARNING "EFS: invalid superblock at block %u\n", sb->fs_start + EFS_SUPER);
+#endif
+ brelse(bh);
+ goto out_no_fs_ul;
+ }
+ brelse(bh);
+
+ if (!(s->s_flags & MS_RDONLY)) {
+#ifdef DEBUG
+ printk(KERN_INFO "EFS: forcing read-only mode\n");
+#endif
+ s->s_flags |= MS_RDONLY;
+ }
+ s->s_op = &efs_superblock_operations;
+ s->s_root = d_alloc_root(iget(s, EFS_ROOTINODE));
+
+ if (!(s->s_root)) {
+ printk(KERN_ERR "EFS: get root inode failed\n");
+ goto out_no_fs;
+ }
+
+ return(s);
+
+out_no_fs_ul:
+out_no_fs:
+ return(NULL);
+}
+
+int efs_statfs(struct super_block *s, struct statfs *buf) {
+ struct efs_sb_info *sb = SUPER_INFO(s);
+
+ buf->f_type = EFS_SUPER_MAGIC; /* efs magic number */
+ buf->f_bsize = EFS_BLOCKSIZE; /* blocksize */
+ buf->f_blocks = sb->total_groups * /* total data blocks */
+ (sb->group_size - sb->inode_blocks);
+ buf->f_bfree = sb->data_free; /* free data blocks */
+ buf->f_bavail = sb->data_free; /* free blocks for non-root */
+ buf->f_files = sb->total_groups * /* total inodes */
+ sb->inode_blocks *
+ (EFS_BLOCKSIZE / sizeof(struct efs_dinode));
+ buf->f_ffree = sb->inode_free; /* free inodes */
+ buf->f_fsid.val[0] = (sb->fs_magic >> 16) & 0xffff; /* fs ID */
+ buf->f_fsid.val[1] = sb->fs_magic & 0xffff; /* fs ID */
+ buf->f_namelen = EFS_MAXNAMELEN; /* max filename length */
+
+ return 0;
+}
+
diff --git a/fs/efs/symlink.c b/fs/efs/symlink.c
new file mode 100644
index 00000000000000..5dd10f50edb04e
--- /dev/null
+++ b/fs/efs/symlink.c
@@ -0,0 +1,57 @@
+/*
+ * symlink.c
+ *
+ * Copyright (c) 1999 Al Smith
+ *
+ * Portions derived from work (c) 1995,1996 Christian Vogelgsang.
+ */
+
+#include <linux/string.h>
+#include <linux/efs_fs.h>
+#include <linux/pagemap.h>
+#include <linux/smp_lock.h>
+
+static int efs_symlink_readpage(struct file *file, struct page *page)
+{
+ char *link = kmap(page);
+ struct buffer_head * bh;
+ struct inode * inode = page->mapping->host;
+ efs_block_t size = inode->i_size;
+ int err;
+
+ err = -ENAMETOOLONG;
+ if (size > 2 * EFS_BLOCKSIZE)
+ goto fail;
+
+ lock_kernel();
+ /* read first 512 bytes of link target */
+ err = -EIO;
+ bh = sb_bread(inode->i_sb, efs_bmap(inode, 0));
+ if (!bh)
+ goto fail;
+ memcpy(link, bh->b_data, (size > EFS_BLOCKSIZE) ? EFS_BLOCKSIZE : size);
+ brelse(bh);
+ if (size > EFS_BLOCKSIZE) {
+ bh = sb_bread(inode->i_sb, efs_bmap(inode, 1));
+ if (!bh)
+ goto fail;
+ memcpy(link + EFS_BLOCKSIZE, bh->b_data, size - EFS_BLOCKSIZE);
+ brelse(bh);
+ }
+ link[size] = '\0';
+ unlock_kernel();
+ SetPageUptodate(page);
+ kunmap(page);
+ UnlockPage(page);
+ return 0;
+fail:
+ unlock_kernel();
+ SetPageError(page);
+ kunmap(page);
+ UnlockPage(page);
+ return err;
+}
+
+struct address_space_operations efs_symlink_aops = {
+ readpage: efs_symlink_readpage
+};
diff --git a/fs/exec.c b/fs/exec.c
new file mode 100644
index 00000000000000..f196e7e69965de
--- /dev/null
+++ b/fs/exec.c
@@ -0,0 +1,1178 @@
+/*
+ * linux/fs/exec.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+/*
+ * #!-checking implemented by tytso.
+ */
+/*
+ * Demand-loading implemented 01.12.91 - no need to read anything but
+ * the header into memory. The inode of the executable is put into
+ * "current->executable", and page faults do the actual loading. Clean.
+ *
+ * Once more I can proudly say that linux stood up to being changed: it
+ * was less than 2 hours work to get demand-loading completely implemented.
+ *
+ * Demand loading changed July 1993 by Eric Youngdale. Use mmap instead,
+ * current->executable is only used by the procfs. This allows a dispatch
+ * table to check for several different types of binary formats. We keep
+ * trying until we recognize the file or we run out of supported binary
+ * formats.
+ */
+
+#include <linux/config.h>
+#include <linux/slab.h>
+#include <linux/file.h>
+#include <linux/mman.h>
+#include <linux/a.out.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/smp_lock.h>
+#include <linux/init.h>
+#include <linux/pagemap.h>
+#include <linux/highmem.h>
+#include <linux/spinlock.h>
+#include <linux/personality.h>
+#include <linux/swap.h>
+#include <linux/utsname.h>
+#define __NO_VERSION__
+#include <linux/module.h>
+
+#include <asm/uaccess.h>
+#include <asm/pgalloc.h>
+#include <asm/mmu_context.h>
+
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+
+int core_uses_pid;
+char core_pattern[65] = "core";
+int core_setuid_ok = 0;
+/* The maximal length of core_pattern is also specified in sysctl.c */
+
+static struct linux_binfmt *formats;
+static rwlock_t binfmt_lock = RW_LOCK_UNLOCKED;
+
+int register_binfmt(struct linux_binfmt * fmt)
+{
+ struct linux_binfmt ** tmp = &formats;
+
+ if (!fmt)
+ return -EINVAL;
+ if (fmt->next)
+ return -EBUSY;
+ write_lock(&binfmt_lock);
+ while (*tmp) {
+ if (fmt == *tmp) {
+ write_unlock(&binfmt_lock);
+ return -EBUSY;
+ }
+ tmp = &(*tmp)->next;
+ }
+ fmt->next = formats;
+ formats = fmt;
+ write_unlock(&binfmt_lock);
+ return 0;
+}
+
+int unregister_binfmt(struct linux_binfmt * fmt)
+{
+ struct linux_binfmt ** tmp = &formats;
+
+ write_lock(&binfmt_lock);
+ while (*tmp) {
+ if (fmt == *tmp) {
+ *tmp = fmt->next;
+ write_unlock(&binfmt_lock);
+ return 0;
+ }
+ tmp = &(*tmp)->next;
+ }
+ write_unlock(&binfmt_lock);
+ return -EINVAL;
+}
+
+static inline void put_binfmt(struct linux_binfmt * fmt)
+{
+ if (fmt->module)
+ __MOD_DEC_USE_COUNT(fmt->module);
+}
+
+/*
+ * Note that a shared library must be both readable and executable due to
+ * security reasons.
+ *
+ * Also note that we take the address to load from from the file itself.
+ */
+asmlinkage long sys_uselib(const char * library)
+{
+ struct file * file;
+ struct nameidata nd;
+ int error;
+
+ error = user_path_walk(library, &nd);
+ if (error)
+ goto out;
+
+ error = -EINVAL;
+ if (!S_ISREG(nd.dentry->d_inode->i_mode))
+ goto exit;
+
+ error = permission(nd.dentry->d_inode, MAY_READ | MAY_EXEC);
+ if (error)
+ goto exit;
+
+ file = dentry_open(nd.dentry, nd.mnt, O_RDONLY);
+ error = PTR_ERR(file);
+ if (IS_ERR(file))
+ goto out;
+
+ error = -ENOEXEC;
+ if(file->f_op && file->f_op->read) {
+ struct linux_binfmt * fmt;
+
+ read_lock(&binfmt_lock);
+ for (fmt = formats ; fmt ; fmt = fmt->next) {
+ if (!fmt->load_shlib)
+ continue;
+ if (!try_inc_mod_count(fmt->module))
+ continue;
+ read_unlock(&binfmt_lock);
+ error = fmt->load_shlib(file);
+ read_lock(&binfmt_lock);
+ put_binfmt(fmt);
+ if (error != -ENOEXEC)
+ break;
+ }
+ read_unlock(&binfmt_lock);
+ }
+ fput(file);
+out:
+ return error;
+exit:
+ path_release(&nd);
+ goto out;
+}
+
+/*
+ * count() counts the number of arguments/envelopes
+ */
+static int count(char ** argv, int max)
+{
+ int i = 0;
+
+ if (argv != NULL) {
+ for (;;) {
+ char * p;
+
+ if (get_user(p, argv))
+ return -EFAULT;
+ if (!p)
+ break;
+ argv++;
+ if(++i > max)
+ return -E2BIG;
+ }
+ }
+ return i;
+}
+
+/*
+ * 'copy_strings()' copies argument/envelope strings from user
+ * memory to free pages in kernel mem. These are in a format ready
+ * to be put directly into the top of new user memory.
+ */
+int copy_strings(int argc,char ** argv, struct linux_binprm *bprm)
+{
+ struct page *kmapped_page = NULL;
+ char *kaddr = NULL;
+ int ret;
+
+ while (argc-- > 0) {
+ char *str;
+ int len;
+ unsigned long pos;
+
+ if (get_user(str, argv+argc) ||
+ !(len = strnlen_user(str, bprm->p))) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ if (bprm->p < len) {
+ ret = -E2BIG;
+ goto out;
+ }
+
+ bprm->p -= len;
+ /* XXX: add architecture specific overflow check here. */
+ pos = bprm->p;
+
+ while (len > 0) {
+ int i, new, err;
+ int offset, bytes_to_copy;
+ struct page *page;
+
+ offset = pos % PAGE_SIZE;
+ i = pos/PAGE_SIZE;
+ page = bprm->page[i];
+ new = 0;
+ if (!page) {
+ page = alloc_page(GFP_HIGHUSER);
+ bprm->page[i] = page;
+ if (!page) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ new = 1;
+ }
+
+ if (page != kmapped_page) {
+ if (kmapped_page)
+ kunmap(kmapped_page);
+ kmapped_page = page;
+ kaddr = kmap(kmapped_page);
+ }
+ if (new && offset)
+ memset(kaddr, 0, offset);
+ bytes_to_copy = PAGE_SIZE - offset;
+ if (bytes_to_copy > len) {
+ bytes_to_copy = len;
+ if (new)
+ memset(kaddr+offset+len, 0,
+ PAGE_SIZE-offset-len);
+ }
+ err = copy_from_user(kaddr+offset, str, bytes_to_copy);
+ if (err) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ pos += bytes_to_copy;
+ str += bytes_to_copy;
+ len -= bytes_to_copy;
+ }
+ }
+ ret = 0;
+out:
+ if (kmapped_page)
+ kunmap(kmapped_page);
+ return ret;
+}
+
+/*
+ * Like copy_strings, but get argv and its values from kernel memory.
+ */
+int copy_strings_kernel(int argc,char ** argv, struct linux_binprm *bprm)
+{
+ int r;
+ mm_segment_t oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ r = copy_strings(argc, argv, bprm);
+ set_fs(oldfs);
+ return r;
+}
+
+/*
+ * This routine is used to map in a page into an address space: needed by
+ * execve() for the initial stack and environment pages.
+ *
+ * tsk->mmap_sem is held for writing.
+ */
+void put_dirty_page(struct task_struct * tsk, struct page *page, unsigned long address)
+{
+ pgd_t * pgd;
+ pmd_t * pmd;
+ pte_t * pte;
+ struct vm_area_struct *vma;
+ pgprot_t prot = PAGE_COPY;
+
+ if (page_count(page) != 1)
+ printk(KERN_ERR "mem_map disagrees with %p at %08lx\n", page, address);
+ pgd = pgd_offset(tsk->mm, address);
+
+ spin_lock(&tsk->mm->page_table_lock);
+ pmd = pmd_alloc(tsk->mm, pgd, address);
+ if (!pmd)
+ goto out;
+ pte = pte_alloc(tsk->mm, pmd, address);
+ if (!pte)
+ goto out;
+ if (!pte_none(*pte))
+ goto out;
+ lru_cache_add(page);
+ flush_dcache_page(page);
+ flush_page_to_ram(page);
+ /* lookup is cheap because there is only a single entry in the list */
+ vma = find_vma(tsk->mm, address);
+ if (vma)
+ prot = vma->vm_page_prot;
+ set_pte(pte, pte_mkdirty(pte_mkwrite(mk_pte(page, prot))));
+ tsk->mm->rss++;
+ spin_unlock(&tsk->mm->page_table_lock);
+
+ /* no need for flush_tlb */
+ return;
+out:
+ spin_unlock(&tsk->mm->page_table_lock);
+ __free_page(page);
+ force_sig(SIGKILL, tsk);
+ return;
+}
+
+int setup_arg_pages(struct linux_binprm *bprm)
+{
+ unsigned long stack_base;
+ struct vm_area_struct *mpnt;
+ int i, ret;
+
+ stack_base = STACK_TOP - MAX_ARG_PAGES*PAGE_SIZE;
+
+ bprm->p += stack_base;
+ if (bprm->loader)
+ bprm->loader += stack_base;
+ bprm->exec += stack_base;
+
+ mpnt = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+ if (!mpnt)
+ return -ENOMEM;
+
+ down_write(&current->mm->mmap_sem);
+ {
+ mpnt->vm_mm = current->mm;
+ mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p;
+ mpnt->vm_end = STACK_TOP;
+ mpnt->vm_flags = VM_STACK_FLAGS;
+ mpnt->vm_page_prot = protection_map[VM_STACK_FLAGS & 0x7];
+ mpnt->vm_ops = NULL;
+ mpnt->vm_pgoff = 0;
+ mpnt->vm_file = NULL;
+ mpnt->vm_private_data = (void *) 0;
+ if ((ret = insert_vm_struct(current->mm, mpnt))) {
+ up_write(&current->mm->mmap_sem);
+ kmem_cache_free(vm_area_cachep, mpnt);
+ return ret;
+ }
+ current->mm->total_vm = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT;
+ }
+
+ for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
+ struct page *page = bprm->page[i];
+ if (page) {
+ bprm->page[i] = NULL;
+ put_dirty_page(current,page,stack_base);
+ }
+ stack_base += PAGE_SIZE;
+ }
+ up_write(&current->mm->mmap_sem);
+
+ return 0;
+}
+
+struct file *open_exec(const char *name)
+{
+ struct nameidata nd;
+ struct inode *inode;
+ struct file *file;
+ int err = 0;
+
+ err = path_lookup(name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, &nd);
+ file = ERR_PTR(err);
+ if (!err) {
+ inode = nd.dentry->d_inode;
+ file = ERR_PTR(-EACCES);
+ if (!(nd.mnt->mnt_flags & MNT_NOEXEC) &&
+ S_ISREG(inode->i_mode)) {
+ int err = permission(inode, MAY_EXEC);
+ if (!err && !(inode->i_mode & 0111))
+ err = -EACCES;
+ file = ERR_PTR(err);
+ if (!err) {
+ file = dentry_open(nd.dentry, nd.mnt, O_RDONLY);
+ if (!IS_ERR(file)) {
+ err = deny_write_access(file);
+ if (err) {
+ fput(file);
+ file = ERR_PTR(err);
+ }
+ }
+out:
+ return file;
+ }
+ }
+ path_release(&nd);
+ }
+ goto out;
+}
+
+int kernel_read(struct file *file, unsigned long offset,
+ char * addr, unsigned long count)
+{
+ mm_segment_t old_fs;
+ loff_t pos = offset;
+ int result = -ENOSYS;
+
+ if (!file->f_op->read)
+ goto fail;
+ old_fs = get_fs();
+ set_fs(get_ds());
+ result = file->f_op->read(file, addr, count, &pos);
+ set_fs(old_fs);
+fail:
+ return result;
+}
+
+static int exec_mmap(void)
+{
+ struct mm_struct * mm, * old_mm;
+
+ old_mm = current->mm;
+
+ if (old_mm && atomic_read(&old_mm->mm_users) == 1) {
+ mm_release();
+ down_write(&old_mm->mmap_sem);
+ exit_mmap(old_mm);
+ up_write(&old_mm->mmap_sem);
+ return 0;
+ }
+
+
+ mm = mm_alloc();
+ if (mm) {
+ struct mm_struct *active_mm;
+
+ if (init_new_context(current, mm)) {
+ mmdrop(mm);
+ return -ENOMEM;
+ }
+
+ /* Add it to the list of mm's */
+ spin_lock(&mmlist_lock);
+ list_add(&mm->mmlist, &init_mm.mmlist);
+ mmlist_nr++;
+ spin_unlock(&mmlist_lock);
+
+ task_lock(current);
+ active_mm = current->active_mm;
+ current->mm = mm;
+ current->active_mm = mm;
+ task_unlock(current);
+ activate_mm(active_mm, mm);
+ mm_release();
+ if (old_mm) {
+ if (active_mm != old_mm) BUG();
+ mmput(old_mm);
+ return 0;
+ }
+ mmdrop(active_mm);
+ return 0;
+ }
+ return -ENOMEM;
+}
+
+/*
+ * This function makes sure the current process has its own signal table,
+ * so that flush_signal_handlers can later reset the handlers without
+ * disturbing other processes. (Other processes might share the signal
+ * table via the CLONE_SIGNAL option to clone().)
+ */
+
+static inline int make_private_signals(void)
+{
+ struct signal_struct * newsig;
+
+ if (atomic_read(&current->sig->count) <= 1)
+ return 0;
+ newsig = kmem_cache_alloc(sigact_cachep, GFP_KERNEL);
+ if (newsig == NULL)
+ return -ENOMEM;
+ spin_lock_init(&newsig->siglock);
+ atomic_set(&newsig->count, 1);
+ memcpy(newsig->action, current->sig->action, sizeof(newsig->action));
+ spin_lock_irq(&current->sigmask_lock);
+ current->sig = newsig;
+ spin_unlock_irq(&current->sigmask_lock);
+ return 0;
+}
+
+/*
+ * If make_private_signals() made a copy of the signal table, decrement the
+ * refcount of the original table, and free it if necessary.
+ * We don't do that in make_private_signals() so that we can back off
+ * in flush_old_exec() if an error occurs after calling make_private_signals().
+ */
+
+static inline void release_old_signals(struct signal_struct * oldsig)
+{
+ if (current->sig == oldsig)
+ return;
+ if (atomic_dec_and_test(&oldsig->count))
+ kmem_cache_free(sigact_cachep, oldsig);
+}
+
+/*
+ * These functions flushes out all traces of the currently running executable
+ * so that a new one can be started
+ */
+
+static inline void flush_old_files(struct files_struct * files)
+{
+ long j = -1;
+
+ write_lock(&files->file_lock);
+ for (;;) {
+ unsigned long set, i;
+
+ j++;
+ i = j * __NFDBITS;
+ if (i >= files->max_fds || i >= files->max_fdset)
+ break;
+ set = files->close_on_exec->fds_bits[j];
+ if (!set)
+ continue;
+ files->close_on_exec->fds_bits[j] = 0;
+ write_unlock(&files->file_lock);
+ for ( ; set ; i++,set >>= 1) {
+ if (set & 1) {
+ sys_close(i);
+ }
+ }
+ write_lock(&files->file_lock);
+
+ }
+ write_unlock(&files->file_lock);
+}
+
+/*
+ * An execve() will automatically "de-thread" the process.
+ * Note: we don't have to hold the tasklist_lock to test
+ * whether we migth need to do this. If we're not part of
+ * a thread group, there is no way we can become one
+ * dynamically. And if we are, we only need to protect the
+ * unlink - even if we race with the last other thread exit,
+ * at worst the list_del_init() might end up being a no-op.
+ */
+static inline void de_thread(struct task_struct *tsk)
+{
+ if (!list_empty(&tsk->thread_group)) {
+ write_lock_irq(&tasklist_lock);
+ list_del_init(&tsk->thread_group);
+ write_unlock_irq(&tasklist_lock);
+ }
+
+ /* Minor oddity: this might stay the same. */
+ tsk->tgid = tsk->pid;
+}
+
+void get_task_comm(char *buf, struct task_struct *tsk)
+{
+ /* buf must be at least sizeof(tsk->comm) in size */
+ task_lock(tsk);
+ memcpy(buf, tsk->comm, sizeof(tsk->comm));
+ task_unlock(tsk);
+}
+
+void set_task_comm(struct task_struct *tsk, char *buf)
+{
+ task_lock(tsk);
+ strncpy(tsk->comm, buf, sizeof(tsk->comm));
+ tsk->comm[sizeof(tsk->comm)-1]='\0';
+ task_unlock(tsk);
+}
+
+int flush_old_exec(struct linux_binprm * bprm)
+{
+ char * name;
+ int i, ch, retval;
+ struct signal_struct * oldsig;
+ struct files_struct * files;
+ char tcomm[sizeof(current->comm)];
+
+ /*
+ * Make sure we have a private signal table
+ */
+ oldsig = current->sig;
+ retval = make_private_signals();
+ if (retval) goto flush_failed;
+
+ /*
+ * Make sure we have private file handles. Ask the
+ * fork helper to do the work for us and the exit
+ * helper to do the cleanup of the old one.
+ */
+
+ files = current->files; /* refcounted so safe to hold */
+ retval = unshare_files();
+ if(retval)
+ goto flush_failed;
+
+ /*
+ * Release all of the old mmap stuff
+ */
+ retval = exec_mmap();
+ if (retval) goto mmap_failed;
+
+ /* This is the point of no return */
+ steal_locks(files);
+ put_files_struct(files);
+ release_old_signals(oldsig);
+
+ current->sas_ss_sp = current->sas_ss_size = 0;
+
+ if (current->euid == current->uid && current->egid == current->gid) {
+ current->mm->dumpable = 1;
+ current->task_dumpable = 1;
+ }
+ name = bprm->filename;
+ for (i=0; (ch = *(name++)) != '\0';) {
+ if (ch == '/')
+ i = 0;
+ else
+ if (i < (sizeof(tcomm) - 1))
+ tcomm[i++] = ch;
+ }
+ tcomm[i] = '\0';
+ set_task_comm(current, tcomm);
+
+ flush_thread();
+
+ de_thread(current);
+
+ if (bprm->e_uid != current->euid || bprm->e_gid != current->egid ||
+ permission(bprm->file->f_dentry->d_inode,MAY_READ))
+ current->mm->dumpable = 0;
+
+ /* An exec changes our domain. We are no longer part of the thread
+ group */
+
+ current->self_exec_id++;
+
+ flush_signal_handlers(current);
+ flush_old_files(current->files);
+
+ return 0;
+
+mmap_failed:
+ put_files_struct(current->files);
+ current->files = files;
+flush_failed:
+ spin_lock_irq(&current->sigmask_lock);
+ if (current->sig != oldsig) {
+ kmem_cache_free(sigact_cachep, current->sig);
+ current->sig = oldsig;
+ }
+ spin_unlock_irq(&current->sigmask_lock);
+ return retval;
+}
+
+/*
+ * We mustn't allow tracing of suid binaries, unless
+ * the tracer has the capability to trace anything..
+ */
+static inline int must_not_trace_exec(struct task_struct * p)
+{
+ return (p->ptrace & PT_PTRACED) && !(p->ptrace & PT_PTRACE_CAP);
+}
+
+/*
+ * Fill the binprm structure from the inode.
+ * Check permissions, then read the first 128 (BINPRM_BUF_SIZE) bytes
+ */
+int prepare_binprm(struct linux_binprm *bprm)
+{
+ int mode;
+ struct inode * inode = bprm->file->f_dentry->d_inode;
+
+ mode = inode->i_mode;
+ /*
+ * Check execute perms again - if the caller has CAP_DAC_OVERRIDE,
+ * vfs_permission lets a non-executable through
+ */
+ if (!(mode & 0111)) /* with at least _one_ execute bit set */
+ return -EACCES;
+ if (bprm->file->f_op == NULL)
+ return -EACCES;
+
+ bprm->e_uid = current->euid;
+ bprm->e_gid = current->egid;
+
+ if(!(bprm->file->f_vfsmnt->mnt_flags & MNT_NOSUID)) {
+ /* Set-uid? */
+ if (mode & S_ISUID)
+ bprm->e_uid = inode->i_uid;
+
+ /* Set-gid? */
+ /*
+ * If setgid is set but no group execute bit then this
+ * is a candidate for mandatory locking, not a setgid
+ * executable.
+ */
+ if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))
+ bprm->e_gid = inode->i_gid;
+ }
+
+ /* We don't have VFS support for capabilities yet */
+ cap_clear(bprm->cap_inheritable);
+ cap_clear(bprm->cap_permitted);
+ cap_clear(bprm->cap_effective);
+
+ /* To support inheritance of root-permissions and suid-root
+ * executables under compatibility mode, we raise all three
+ * capability sets for the file.
+ *
+ * If only the real uid is 0, we only raise the inheritable
+ * and permitted sets of the executable file.
+ */
+
+ if (!issecure(SECURE_NOROOT)) {
+ if (bprm->e_uid == 0 || current->uid == 0) {
+ cap_set_full(bprm->cap_inheritable);
+ cap_set_full(bprm->cap_permitted);
+ }
+ if (bprm->e_uid == 0)
+ cap_set_full(bprm->cap_effective);
+ }
+
+ memset(bprm->buf,0,BINPRM_BUF_SIZE);
+ return kernel_read(bprm->file,0,bprm->buf,BINPRM_BUF_SIZE);
+}
+
+/*
+ * This function is used to produce the new IDs and capabilities
+ * from the old ones and the file's capabilities.
+ *
+ * The formula used for evolving capabilities is:
+ *
+ * pI' = pI
+ * (***) pP' = (fP & X) | (fI & pI)
+ * pE' = pP' & fE [NB. fE is 0 or ~0]
+ *
+ * I=Inheritable, P=Permitted, E=Effective // p=process, f=file
+ * ' indicates post-exec(), and X is the global 'cap_bset'.
+ *
+ */
+
+void compute_creds(struct linux_binprm *bprm)
+{
+ kernel_cap_t new_permitted, working;
+ int do_unlock = 0;
+
+ new_permitted = cap_intersect(bprm->cap_permitted, cap_bset);
+ working = cap_intersect(bprm->cap_inheritable,
+ current->cap_inheritable);
+ new_permitted = cap_combine(new_permitted, working);
+
+ if (bprm->e_uid != current->uid || bprm->e_gid != current->gid ||
+ !cap_issubset(new_permitted, current->cap_permitted)) {
+ current->mm->dumpable = 0;
+
+ lock_kernel();
+ if (must_not_trace_exec(current)
+ || atomic_read(&current->fs->count) > 1
+ || atomic_read(&current->files->count) > 1
+ || atomic_read(&current->sig->count) > 1) {
+ if(!capable(CAP_SETUID)) {
+ bprm->e_uid = current->uid;
+ bprm->e_gid = current->gid;
+ }
+ if(!capable(CAP_SETPCAP)) {
+ new_permitted = cap_intersect(new_permitted,
+ current->cap_permitted);
+ }
+ }
+ do_unlock = 1;
+ }
+
+
+ /* For init, we want to retain the capabilities set
+ * in the init_task struct. Thus we skip the usual
+ * capability rules */
+ if (current->pid != 1) {
+ current->cap_permitted = new_permitted;
+ current->cap_effective =
+ cap_intersect(new_permitted, bprm->cap_effective);
+ }
+
+ /* AUD: Audit candidate if current->cap_effective is set */
+
+ current->suid = current->euid = current->fsuid = bprm->e_uid;
+ current->sgid = current->egid = current->fsgid = bprm->e_gid;
+
+ if(do_unlock)
+ unlock_kernel();
+ current->keep_capabilities = 0;
+}
+
+
+void remove_arg_zero(struct linux_binprm *bprm)
+{
+ if (bprm->argc) {
+ unsigned long offset;
+ char * kaddr;
+ struct page *page;
+
+ offset = bprm->p % PAGE_SIZE;
+ goto inside;
+
+ while (bprm->p++, *(kaddr+offset++)) {
+ if (offset != PAGE_SIZE)
+ continue;
+ offset = 0;
+ kunmap(page);
+inside:
+ page = bprm->page[bprm->p/PAGE_SIZE];
+ kaddr = kmap(page);
+ }
+ kunmap(page);
+ bprm->argc--;
+ }
+}
+
+/*
+ * cycle the list of binary formats handler, until one recognizes the image
+ */
+int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs)
+{
+ int try,retval=0;
+ struct linux_binfmt *fmt;
+#ifdef __alpha__
+ /* handle /sbin/loader.. */
+ {
+ struct exec * eh = (struct exec *) bprm->buf;
+
+ if (!bprm->loader && eh->fh.f_magic == 0x183 &&
+ (eh->fh.f_flags & 0x3000) == 0x3000)
+ {
+ struct file * file;
+ unsigned long loader;
+
+ allow_write_access(bprm->file);
+ fput(bprm->file);
+ bprm->file = NULL;
+
+ loader = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *);
+
+ file = open_exec("/sbin/loader");
+ retval = PTR_ERR(file);
+ if (IS_ERR(file))
+ return retval;
+
+ /* Remember if the application is TASO. */
+ bprm->sh_bang = eh->ah.entry < 0x100000000;
+
+ bprm->file = file;
+ bprm->loader = loader;
+ retval = prepare_binprm(bprm);
+ if (retval<0)
+ return retval;
+ /* should call search_binary_handler recursively here,
+ but it does not matter */
+ }
+ }
+#endif
+ /* kernel module loader fixup */
+ /* so we don't try to load run modprobe in kernel space. */
+ set_fs(USER_DS);
+ for (try=0; try<2; try++) {
+ read_lock(&binfmt_lock);
+ for (fmt = formats ; fmt ; fmt = fmt->next) {
+ int (*fn)(struct linux_binprm *, struct pt_regs *) = fmt->load_binary;
+ if (!fn)
+ continue;
+ if (!try_inc_mod_count(fmt->module))
+ continue;
+ read_unlock(&binfmt_lock);
+ retval = fn(bprm, regs);
+ if (retval >= 0) {
+ put_binfmt(fmt);
+ allow_write_access(bprm->file);
+ if (bprm->file)
+ fput(bprm->file);
+ bprm->file = NULL;
+ current->did_exec = 1;
+ return retval;
+ }
+ read_lock(&binfmt_lock);
+ put_binfmt(fmt);
+ if (retval != -ENOEXEC)
+ break;
+ if (!bprm->file) {
+ read_unlock(&binfmt_lock);
+ return retval;
+ }
+ }
+ read_unlock(&binfmt_lock);
+ if (retval != -ENOEXEC) {
+ break;
+#ifdef CONFIG_KMOD
+ }else{
+#define printable(c) (((c)=='\t') || ((c)=='\n') || (0x20<=(c) && (c)<=0x7e))
+ char modname[20];
+ if (printable(bprm->buf[0]) &&
+ printable(bprm->buf[1]) &&
+ printable(bprm->buf[2]) &&
+ printable(bprm->buf[3]))
+ break; /* -ENOEXEC */
+ sprintf(modname, "binfmt-%04x", *(unsigned short *)(&bprm->buf[2]));
+ request_module(modname);
+#endif
+ }
+ }
+ return retval;
+}
+
+
+/*
+ * sys_execve() executes a new program.
+ */
+int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs)
+{
+ struct linux_binprm bprm;
+ struct file *file;
+ int retval;
+ int i;
+
+ file = open_exec(filename);
+
+ retval = PTR_ERR(file);
+ if (IS_ERR(file))
+ return retval;
+
+ bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *);
+ memset(bprm.page, 0, MAX_ARG_PAGES*sizeof(bprm.page[0]));
+
+ bprm.file = file;
+ bprm.filename = filename;
+ bprm.sh_bang = 0;
+ bprm.loader = 0;
+ bprm.exec = 0;
+ if ((bprm.argc = count(argv, bprm.p / sizeof(void *))) < 0) {
+ allow_write_access(file);
+ fput(file);
+ return bprm.argc;
+ }
+
+ if ((bprm.envc = count(envp, bprm.p / sizeof(void *))) < 0) {
+ allow_write_access(file);
+ fput(file);
+ return bprm.envc;
+ }
+
+ retval = prepare_binprm(&bprm);
+ if (retval < 0)
+ goto out;
+
+ retval = copy_strings_kernel(1, &bprm.filename, &bprm);
+ if (retval < 0)
+ goto out;
+
+ bprm.exec = bprm.p;
+ retval = copy_strings(bprm.envc, envp, &bprm);
+ if (retval < 0)
+ goto out;
+
+ retval = copy_strings(bprm.argc, argv, &bprm);
+ if (retval < 0)
+ goto out;
+
+ retval = search_binary_handler(&bprm,regs);
+ if (retval >= 0)
+ /* execve success */
+ return retval;
+
+out:
+ /* Something went wrong, return the inode and free the argument pages*/
+ allow_write_access(bprm.file);
+ if (bprm.file)
+ fput(bprm.file);
+
+ for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
+ struct page * page = bprm.page[i];
+ if (page)
+ __free_page(page);
+ }
+
+ return retval;
+}
+
+void set_binfmt(struct linux_binfmt *new)
+{
+ struct linux_binfmt *old = current->binfmt;
+ if (new && new->module)
+ __MOD_INC_USE_COUNT(new->module);
+ current->binfmt = new;
+ if (old && old->module)
+ __MOD_DEC_USE_COUNT(old->module);
+}
+
+#define CORENAME_MAX_SIZE 64
+
+/* format_corename will inspect the pattern parameter, and output a
+ * name into corename, which must have space for at least
+ * CORENAME_MAX_SIZE bytes plus one byte for the zero terminator.
+ */
+void format_corename(char *corename, const char *pattern, long signr)
+{
+ const char *pat_ptr = pattern;
+ char *out_ptr = corename;
+ char *const out_end = corename + CORENAME_MAX_SIZE;
+ int rc;
+ int pid_in_pattern = 0;
+
+ /* Repeat as long as we have more pattern to process and more output
+ space */
+ while (*pat_ptr) {
+ if (*pat_ptr != '%') {
+ if (out_ptr == out_end)
+ goto out;
+ *out_ptr++ = *pat_ptr++;
+ } else {
+ switch (*++pat_ptr) {
+ case 0:
+ goto out;
+ /* Double percent, output one percent */
+ case '%':
+ if (out_ptr == out_end)
+ goto out;
+ *out_ptr++ = '%';
+ break;
+ /* pid */
+ case 'p':
+ pid_in_pattern = 1;
+ rc = snprintf(out_ptr, out_end - out_ptr,
+ "%d", current->pid);
+ if (rc > out_end - out_ptr)
+ goto out;
+ out_ptr += rc;
+ break;
+ /* uid */
+ case 'u':
+ rc = snprintf(out_ptr, out_end - out_ptr,
+ "%d", current->uid);
+ if (rc > out_end - out_ptr)
+ goto out;
+ out_ptr += rc;
+ break;
+ /* gid */
+ case 'g':
+ rc = snprintf(out_ptr, out_end - out_ptr,
+ "%d", current->gid);
+ if (rc > out_end - out_ptr)
+ goto out;
+ out_ptr += rc;
+ break;
+ /* signal that caused the coredump */
+ case 's':
+ rc = snprintf(out_ptr, out_end - out_ptr,
+ "%ld", signr);
+ if (rc > out_end - out_ptr)
+ goto out;
+ out_ptr += rc;
+ break;
+ /* UNIX time of coredump */
+ case 't': {
+ struct timeval tv;
+ do_gettimeofday(&tv);
+ rc = snprintf(out_ptr, out_end - out_ptr,
+ "%ld", tv.tv_sec);
+ if (rc > out_end - out_ptr)
+ goto out;
+ out_ptr += rc;
+ break;
+ }
+ /* hostname */
+ case 'h':
+ down_read(&uts_sem);
+ rc = snprintf(out_ptr, out_end - out_ptr,
+ "%s", system_utsname.nodename);
+ up_read(&uts_sem);
+ if (rc > out_end - out_ptr)
+ goto out;
+ out_ptr += rc;
+ break;
+ /* executable */
+ case 'e':
+ rc = snprintf(out_ptr, out_end - out_ptr,
+ "%s", current->comm);
+ if (rc > out_end - out_ptr)
+ goto out;
+ out_ptr += rc;
+ break;
+ default:
+ break;
+ }
+ ++pat_ptr;
+ }
+ }
+ /* Backward compatibility with core_uses_pid:
+ *
+ * If core_pattern does not include a %p (as is the default)
+ * and core_uses_pid is set, then .%pid will be appended to
+ * the filename */
+ if (!pid_in_pattern
+ && (core_uses_pid || atomic_read(&current->mm->mm_users) != 1)) {
+ rc = snprintf(out_ptr, out_end - out_ptr,
+ ".%d", current->pid);
+ if (rc > out_end - out_ptr)
+ goto out;
+ out_ptr += rc;
+ }
+ out:
+ *out_ptr = 0;
+}
+
+int do_coredump(long signr, struct pt_regs * regs)
+{
+ struct linux_binfmt * binfmt;
+ char corename[CORENAME_MAX_SIZE + 1];
+ struct file * file;
+ struct inode * inode;
+ int retval = 0;
+ int fsuid = current->fsuid;
+
+ lock_kernel();
+ binfmt = current->binfmt;
+ if (!binfmt || !binfmt->core_dump)
+ goto fail;
+ if (!is_dumpable(current))
+ {
+ if(!core_setuid_ok || !current->task_dumpable)
+ goto fail;
+ current->fsuid = 0;
+ }
+ current->mm->dumpable = 0;
+ if (current->rlim[RLIMIT_CORE].rlim_cur < binfmt->min_coredump)
+ goto fail;
+
+ format_corename(corename, core_pattern, signr);
+ file = filp_open(corename, O_CREAT | 2 | O_NOFOLLOW, 0600);
+ if (IS_ERR(file))
+ goto fail;
+ inode = file->f_dentry->d_inode;
+ if (inode->i_nlink > 1)
+ goto close_fail; /* multiple links - don't dump */
+ if (d_unhashed(file->f_dentry))
+ goto close_fail;
+
+ if (!S_ISREG(inode->i_mode))
+ goto close_fail;
+ if (!file->f_op)
+ goto close_fail;
+ if (!file->f_op->write)
+ goto close_fail;
+ if (do_truncate(file->f_dentry, 0) != 0)
+ goto close_fail;
+
+ retval = binfmt->core_dump(signr, regs, file);
+
+close_fail:
+ filp_close(file, NULL);
+fail:
+ if (fsuid != current->fsuid)
+ current->fsuid = fsuid;
+ unlock_kernel();
+ return retval;
+}
diff --git a/fs/ext2/CHANGES b/fs/ext2/CHANGES
new file mode 100644
index 00000000000000..aa5aaf0e5911c4
--- /dev/null
+++ b/fs/ext2/CHANGES
@@ -0,0 +1,157 @@
+Changes from version 0.5a to version 0.5b
+=========================================
+ - Now that we have sysctl(), the immutable flag cannot be changed when
+ the system is running at security level > 0.
+ - Some cleanups in the code.
+ - More consistency checks on directories.
+ - The ext2.diff patch from Tom May <ftom@netcom.com> has been
+ integrated. This patch replaces expensive "/" and "%" with
+ cheap ">>" and "&" where possible.
+
+Changes from version 0.5 to version 0.5a
+========================================
+ - Zero the partial block following the end of the file when a file
+ is truncated.
+ - Dates updated in the copyright.
+ - More checks when the filesystem is mounted: the count of blocks,
+ fragments, and inodes per group is checked against the block size.
+ - The buffers used by the error routines are now static variables, to
+ avoid using space on the kernel stack, as requested by Linus.
+ - Some cleanups in the error messages (some versions of syslog contain
+ a bug which truncates an error message if it contains '\n').
+ - Check that no data can be written to a file past the 2GB limit.
+ - The famous readdir() bug has been fixed by Stephen Tweedie.
+ - Added a revision level in the superblock.
+ - Full support for O_SYNC flag of the open system call.
+ - New mount options: `resuid=#uid' and `resgid=#gid'. `resuid' causes
+ ext2fs to consider user #uid like root for the reserved blocks.
+ `resgid' acts the same way with group #gid. New fields in the
+ superblock contain default values for resuid and resgid and can
+ be modified by tune2fs.
+ Idea comes from Rene Cougnenc <cougnenc@renux.frmug.fr.net>.
+ - New mount options: `bsddf' and `minixdf'. `bsddf' causes ext2fs
+ to remove the blocks used for FS structures from the total block
+ count in statfs. With `minixdf', ext2fs mimics Minix behavior
+ in statfs (i.e. it returns the total number of blocks on the
+ partition). This is intended to make bde happy :-)
+ - New file attributes:
+ - Immutable files cannot be modified. Data cannot be written to
+ these files. They cannot be removed, renamed and new links cannot
+ be created. Even root cannot modify the files. He has to remove
+ the immutable attribute first.
+ - Append-only files: can only be written in append-mode when writing.
+ They cannot be removed, renamed and new links cannot be created.
+ Note: files may only be added to an append-only directory.
+ - No-dump files: the attribute is not used by the kernel. My port
+ of dump uses it to avoid backing up files which are not important.
+ - New check in ext2_check_dir_entry: the inode number is checked.
+ - Support for big file systems: the copy of the FS descriptor is now
+ dynamically allocated (previous versions used a fixed size array).
+ This allows to mount 2GB+ FS.
+ - Reorganization of the ext2_inode structure to allow other operating
+ systems to create specific fields if they use ext2fs as their native
+ file system. Currently, ext2fs is only implemented in Linux but
+ will soon be part of Gnu Hurd and of Masix.
+
+Changes from version 0.4b to version 0.5
+========================================
+ - New superblock fields: s_lastcheck and s_checkinterval added
+ by Uwe Ohse <uwe@tirka.gun.de> to implement timedependent checks
+ of the file system
+ - Real random numbers for secure rm added by Pierre del Perugia
+ <delperug@gla.ecoledoc.ibp.fr>
+ - The mount warnings related to the state of a fs are not printed
+ if the fs is mounted read-only, idea by Nick Holloway
+ <alfie@dcs.warwick.ac.uk>
+
+Changes from version 0.4a to version 0.4b
+=========================================
+ - Copyrights changed to include the name of my laboratory.
+ - Clean up of balloc.c and ialloc.c.
+ - More consistency checks.
+ - Block preallocation added by Stephen Tweedie.
+ - Direct reads of directories disallowed.
+ - Readahead implemented in readdir by Stephen Tweedie.
+ - Bugs in block and inodes allocation fixed.
+ - Readahead implemented in ext2_find_entry by Chip Salzenberg.
+ - New mount options:
+ `check=none|normal|strict'
+ `debug'
+ `errors=continue|remount-ro|panic'
+ `grpid', `bsdgroups'
+ `nocheck'
+ `nogrpid', `sysvgroups'
+ - truncate() now tries to deallocate contiguous blocks in a single call
+ to ext2_free_blocks().
+ - lots of cosmetic changes.
+
+Changes from version 0.4 to version 0.4a
+========================================
+ - the `sync' option support is now complete. Version 0.4 was not
+ supporting it when truncating a file. I have tested the synchronous
+ writes and they work but they make the system very slow :-( I have
+ to work again on this to make it faster.
+ - when detecting an error on a mounted filesystem, version 0.4 used
+ to try to write a flag in the super block even if the filesystem had
+ been mounted read-only. This is fixed.
+ - the `sb=#' option now causes the kernel code to use the filesystem
+ descriptors located at block #+1. Version 0.4 used the superblock
+ backup located at block # but used the main copy of the descriptors.
+ - a new file attribute `S' is supported. This attribute causes
+ synchronous writes but is applied to a file not to the entire file
+ system (thanks to Michael Kraehe <kraehe@bakunin.north.de> for
+ suggesting it).
+ - the directory cache is inhibited by default. The cache management
+ code seems to be buggy and I have to look at it carefully before
+ using it again.
+ - deleting a file with the `s' attribute (secure deletion) causes its
+ blocks to be overwritten with random values not with zeros (thanks to
+ Michael A. Griffith <grif@cs.ucr.edu> for suggesting it).
+ - lots of cosmetic changes have been made.
+
+Changes from version 0.3 to version 0.4
+=======================================
+ - Three new mount options are supported: `check', `sync' and `sb=#'.
+ `check' tells the kernel code to make more consistency checks
+ when the file system is mounted. Currently, the kernel code checks
+ that the blocks and inodes bitmaps are consistent with the free
+ blocks and inodes counts. More checks will be added in future
+ releases.
+ `sync' tells the kernel code to use synchronous writes when updating
+ an inode, a bitmap, a directory entry or an indirect block. This
+ can make the file system much slower but can be a big win for files
+ recovery in case of a crash (and we can now say to the BSD folks
+ that Linux also supports synchronous updates :-).
+ `sb=#' tells the kernel code to use an alternate super block instead
+ of its master copy. `#' is the number of the block (counted in
+ 1024 bytes blocks) which contains the alternate super block.
+ An ext2 file system typically contains backups of the super block
+ at blocks 8193, 16385, and so on.
+ - I have change the meaning of the valid flag used by e2fsck. it
+ now contains the state of the file system. If the kernel code
+ detects an inconsistency while the file system is mounted, it flags
+ it as erroneous and e2fsck will detect that on next run.
+ - The super block now contains a mount counter. This counter is
+ incremented each time the file system is mounted read/write. When
+ this counter becomes bigger than a maximal mount counts (also stored
+ in the super block), e2fsck checks the file system, even if it had
+ been unmounted cleanly, and resets this counter to 0.
+ - File attributes are now supported. One can associate a set of
+ attributes to a file. Three attributes are defined:
+ `c': the file is marked for automatic compression,
+ `s': the file is marked for secure deletion: when the file is
+ deleted, its blocks are zeroed and written back to the disk,
+ `u': the file is marked for undeletion: when the file is deleted,
+ its contents are saved to allow a future undeletion.
+ Currently, only the `s' attribute is implemented in the kernel
+ code. Support for the other attributes will be added in a future
+ release.
+ - a few bugs related to times updates have been fixed by Bruce
+ Evans and me.
+ - a bug related to the links count of deleted inodes has been fixed.
+ Previous versions used to keep the links count set to 1 when a file
+ was deleted. The new version now sets links_count to 0 when deleting
+ the last link.
+ - a race condition when deallocating an inode has been fixed by
+ Stephen Tweedie.
+
diff --git a/fs/ext2/Makefile b/fs/ext2/Makefile
new file mode 100644
index 00000000000000..60e07beee0837e
--- /dev/null
+++ b/fs/ext2/Makefile
@@ -0,0 +1,16 @@
+#
+# Makefile for the linux ext2-filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+O_TARGET := ext2.o
+
+obj-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o \
+ ioctl.o namei.o super.o symlink.o
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c
new file mode 100644
index 00000000000000..2cd14eb3dba528
--- /dev/null
+++ b/fs/ext2/balloc.c
@@ -0,0 +1,785 @@
+/*
+ * linux/fs/ext2/balloc.c
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * Enhanced block allocation by Stephen Tweedie (sct@dcs.ed.ac.uk), 1993
+ * Big-endian to little-endian byte-swapping/bitmaps by
+ * David S. Miller (davem@caip.rutgers.edu), 1995
+ */
+
+#include <linux/config.h>
+#include <linux/fs.h>
+#include <linux/ext2_fs.h>
+#include <linux/locks.h>
+#include <linux/quotaops.h>
+
+/*
+ * balloc.c contains the blocks allocation and deallocation routines
+ */
+
+/*
+ * The free blocks are managed by bitmaps. A file system contains several
+ * blocks groups. Each group contains 1 bitmap block for blocks, 1 bitmap
+ * block for inodes, N blocks for the inode table and data blocks.
+ *
+ * The file system contains group descriptors which are located after the
+ * super block. Each descriptor contains the number of the bitmap block and
+ * the free blocks count in the block. The descriptors are loaded in memory
+ * when a file system is mounted (see ext2_read_super).
+ */
+
+
+#define in_range(b, first, len) ((b) >= (first) && (b) <= (first) + (len) - 1)
+
+struct ext2_group_desc * ext2_get_group_desc(struct super_block * sb,
+ unsigned int block_group,
+ struct buffer_head ** bh)
+{
+ unsigned long group_desc;
+ unsigned long desc;
+ struct ext2_group_desc * gdp;
+
+ if (block_group >= sb->u.ext2_sb.s_groups_count) {
+ ext2_error (sb, "ext2_get_group_desc",
+ "block_group >= groups_count - "
+ "block_group = %d, groups_count = %lu",
+ block_group, sb->u.ext2_sb.s_groups_count);
+
+ return NULL;
+ }
+
+ group_desc = block_group / EXT2_DESC_PER_BLOCK(sb);
+ desc = block_group % EXT2_DESC_PER_BLOCK(sb);
+ if (!sb->u.ext2_sb.s_group_desc[group_desc]) {
+ ext2_error (sb, "ext2_get_group_desc",
+ "Group descriptor not loaded - "
+ "block_group = %d, group_desc = %lu, desc = %lu",
+ block_group, group_desc, desc);
+ return NULL;
+ }
+
+ gdp = (struct ext2_group_desc *)
+ sb->u.ext2_sb.s_group_desc[group_desc]->b_data;
+ if (bh)
+ *bh = sb->u.ext2_sb.s_group_desc[group_desc];
+ return gdp + desc;
+}
+
+/*
+ * Read the bitmap for a given block_group, reading into the specified
+ * slot in the superblock's bitmap cache.
+ *
+ * Return >=0 on success or a -ve error code.
+ */
+
+static int read_block_bitmap (struct super_block * sb,
+ unsigned int block_group,
+ unsigned long bitmap_nr)
+{
+ struct ext2_group_desc * gdp;
+ struct buffer_head * bh = NULL;
+ int retval = -EIO;
+
+ gdp = ext2_get_group_desc (sb, block_group, NULL);
+ if (!gdp)
+ goto error_out;
+ retval = 0;
+ bh = sb_bread(sb, le32_to_cpu(gdp->bg_block_bitmap));
+ if (!bh) {
+ ext2_error (sb, "read_block_bitmap",
+ "Cannot read block bitmap - "
+ "block_group = %d, block_bitmap = %lu",
+ block_group, (unsigned long) gdp->bg_block_bitmap);
+ retval = -EIO;
+ }
+ /*
+ * On IO error, just leave a zero in the superblock's block pointer for
+ * this group. The IO will be retried next time.
+ */
+error_out:
+ sb->u.ext2_sb.s_block_bitmap_number[bitmap_nr] = block_group;
+ sb->u.ext2_sb.s_block_bitmap[bitmap_nr] = bh;
+ return retval;
+}
+
+/*
+ * load_block_bitmap loads the block bitmap for a blocks group
+ *
+ * It maintains a cache for the last bitmaps loaded. This cache is managed
+ * with a LRU algorithm.
+ *
+ * Notes:
+ * 1/ There is one cache per mounted file system.
+ * 2/ If the file system contains less than EXT2_MAX_GROUP_LOADED groups,
+ * this function reads the bitmap without maintaining a LRU cache.
+ *
+ * Return the slot used to store the bitmap, or a -ve error code.
+ */
+static int __load_block_bitmap (struct super_block * sb,
+ unsigned int block_group)
+{
+ int i, j, retval = 0;
+ unsigned long block_bitmap_number;
+ struct buffer_head * block_bitmap;
+
+ if (block_group >= sb->u.ext2_sb.s_groups_count)
+ ext2_panic (sb, "load_block_bitmap",
+ "block_group >= groups_count - "
+ "block_group = %d, groups_count = %lu",
+ block_group, sb->u.ext2_sb.s_groups_count);
+
+ if (sb->u.ext2_sb.s_groups_count <= EXT2_MAX_GROUP_LOADED) {
+ if (sb->u.ext2_sb.s_block_bitmap[block_group]) {
+ if (sb->u.ext2_sb.s_block_bitmap_number[block_group] ==
+ block_group)
+ return block_group;
+ ext2_error (sb, "__load_block_bitmap",
+ "block_group != block_bitmap_number");
+ }
+ retval = read_block_bitmap (sb, block_group, block_group);
+ if (retval < 0)
+ return retval;
+ return block_group;
+ }
+
+ for (i = 0; i < sb->u.ext2_sb.s_loaded_block_bitmaps &&
+ sb->u.ext2_sb.s_block_bitmap_number[i] != block_group; i++)
+ ;
+ if (i < sb->u.ext2_sb.s_loaded_block_bitmaps &&
+ sb->u.ext2_sb.s_block_bitmap_number[i] == block_group) {
+ block_bitmap_number = sb->u.ext2_sb.s_block_bitmap_number[i];
+ block_bitmap = sb->u.ext2_sb.s_block_bitmap[i];
+ for (j = i; j > 0; j--) {
+ sb->u.ext2_sb.s_block_bitmap_number[j] =
+ sb->u.ext2_sb.s_block_bitmap_number[j - 1];
+ sb->u.ext2_sb.s_block_bitmap[j] =
+ sb->u.ext2_sb.s_block_bitmap[j - 1];
+ }
+ sb->u.ext2_sb.s_block_bitmap_number[0] = block_bitmap_number;
+ sb->u.ext2_sb.s_block_bitmap[0] = block_bitmap;
+
+ /*
+ * There's still one special case here --- if block_bitmap == 0
+ * then our last attempt to read the bitmap failed and we have
+ * just ended up caching that failure. Try again to read it.
+ */
+ if (!block_bitmap)
+ retval = read_block_bitmap (sb, block_group, 0);
+ } else {
+ if (sb->u.ext2_sb.s_loaded_block_bitmaps < EXT2_MAX_GROUP_LOADED)
+ sb->u.ext2_sb.s_loaded_block_bitmaps++;
+ else
+ brelse (sb->u.ext2_sb.s_block_bitmap[EXT2_MAX_GROUP_LOADED - 1]);
+ for (j = sb->u.ext2_sb.s_loaded_block_bitmaps - 1; j > 0; j--) {
+ sb->u.ext2_sb.s_block_bitmap_number[j] =
+ sb->u.ext2_sb.s_block_bitmap_number[j - 1];
+ sb->u.ext2_sb.s_block_bitmap[j] =
+ sb->u.ext2_sb.s_block_bitmap[j - 1];
+ }
+ retval = read_block_bitmap (sb, block_group, 0);
+ }
+ return retval;
+}
+
+/*
+ * Load the block bitmap for a given block group. First of all do a couple
+ * of fast lookups for common cases and then pass the request onto the guts
+ * of the bitmap loader.
+ *
+ * Return the slot number of the group in the superblock bitmap cache's on
+ * success, or a -ve error code.
+ *
+ * There is still one inconsistency here --- if the number of groups in this
+ * filesystems is <= EXT2_MAX_GROUP_LOADED, then we have no way of
+ * differentiating between a group for which we have never performed a bitmap
+ * IO request, and a group for which the last bitmap read request failed.
+ */
+static inline int load_block_bitmap (struct super_block * sb,
+ unsigned int block_group)
+{
+ int slot;
+
+ /*
+ * Do the lookup for the slot. First of all, check if we're asking
+ * for the same slot as last time, and did we succeed that last time?
+ */
+ if (sb->u.ext2_sb.s_loaded_block_bitmaps > 0 &&
+ sb->u.ext2_sb.s_block_bitmap_number[0] == block_group &&
+ sb->u.ext2_sb.s_block_bitmap[0]) {
+ return 0;
+ }
+ /*
+ * Or can we do a fast lookup based on a loaded group on a filesystem
+ * small enough to be mapped directly into the superblock?
+ */
+ else if (sb->u.ext2_sb.s_groups_count <= EXT2_MAX_GROUP_LOADED &&
+ sb->u.ext2_sb.s_block_bitmap_number[block_group] == block_group &&
+ sb->u.ext2_sb.s_block_bitmap[block_group]) {
+ slot = block_group;
+ }
+ /*
+ * If not, then do a full lookup for this block group.
+ */
+ else {
+ slot = __load_block_bitmap (sb, block_group);
+ }
+
+ /*
+ * <0 means we just got an error
+ */
+ if (slot < 0)
+ return slot;
+
+ /*
+ * If it's a valid slot, we may still have cached a previous IO error,
+ * in which case the bh in the superblock cache will be zero.
+ */
+ if (!sb->u.ext2_sb.s_block_bitmap[slot])
+ return -EIO;
+
+ /*
+ * Must have been read in OK to get this far.
+ */
+ return slot;
+}
+
+/* Free given blocks, update quota and i_blocks field */
+void ext2_free_blocks (struct inode * inode, unsigned long block,
+ unsigned long count)
+{
+ struct buffer_head * bh;
+ struct buffer_head * bh2;
+ unsigned long block_group;
+ unsigned long bit;
+ unsigned long i;
+ int bitmap_nr;
+ unsigned long overflow;
+ struct super_block * sb;
+ struct ext2_group_desc * gdp;
+ struct ext2_super_block * es;
+
+ sb = inode->i_sb;
+ if (!sb) {
+ printk ("ext2_free_blocks: nonexistent device");
+ return;
+ }
+ lock_super (sb);
+ es = sb->u.ext2_sb.s_es;
+ if (block < le32_to_cpu(es->s_first_data_block) ||
+ block + count < block ||
+ (block + count) > le32_to_cpu(es->s_blocks_count)) {
+ ext2_error (sb, "ext2_free_blocks",
+ "Freeing blocks not in datazone - "
+ "block = %lu, count = %lu", block, count);
+ goto error_return;
+ }
+
+ ext2_debug ("freeing block(s) %lu-%lu\n", block, block + count - 1);
+
+do_more:
+ overflow = 0;
+ block_group = (block - le32_to_cpu(es->s_first_data_block)) /
+ EXT2_BLOCKS_PER_GROUP(sb);
+ bit = (block - le32_to_cpu(es->s_first_data_block)) %
+ EXT2_BLOCKS_PER_GROUP(sb);
+ /*
+ * Check to see if we are freeing blocks across a group
+ * boundary.
+ */
+ if (bit + count > EXT2_BLOCKS_PER_GROUP(sb)) {
+ overflow = bit + count - EXT2_BLOCKS_PER_GROUP(sb);
+ count -= overflow;
+ }
+ bitmap_nr = load_block_bitmap (sb, block_group);
+ if (bitmap_nr < 0)
+ goto error_return;
+
+ bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr];
+ gdp = ext2_get_group_desc (sb, block_group, &bh2);
+ if (!gdp)
+ goto error_return;
+
+ for (i = 0; i < count; i++, block++) {
+ if (block == le32_to_cpu(gdp->bg_block_bitmap) ||
+ block == le32_to_cpu(gdp->bg_inode_bitmap) ||
+ in_range(block, le32_to_cpu(gdp->bg_inode_table),
+ EXT2_SB(sb)->s_itb_per_group)) {
+ ext2_error(sb, __FUNCTION__,
+ "Freeing block in system zone - block = %lu",
+ block);
+ continue;
+ }
+
+ if (!ext2_clear_bit (bit + i, bh->b_data))
+ ext2_error(sb, __FUNCTION__,
+ "bit already cleared for block %lu", block);
+ else {
+ DQUOT_FREE_BLOCK(inode, 1);
+ gdp->bg_free_blocks_count =
+ cpu_to_le16(le16_to_cpu(gdp->bg_free_blocks_count)+1);
+ es->s_free_blocks_count =
+ cpu_to_le32(le32_to_cpu(es->s_free_blocks_count)+1);
+ }
+ }
+
+ mark_buffer_dirty(bh2);
+ mark_buffer_dirty(sb->u.ext2_sb.s_sbh);
+
+ mark_buffer_dirty(bh);
+ if (sb->s_flags & MS_SYNCHRONOUS) {
+ ll_rw_block (WRITE, 1, &bh);
+ wait_on_buffer (bh);
+ }
+ if (overflow) {
+ count = overflow;
+ goto do_more;
+ }
+ sb->s_dirt = 1;
+error_return:
+ unlock_super (sb);
+ return;
+}
+
+/*
+ * 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
+ * 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.
+ * This function also updates quota and i_blocks field.
+ */
+int ext2_new_block (struct inode * inode, unsigned long goal,
+ u32 * prealloc_count, u32 * prealloc_block, int * err)
+{
+ struct buffer_head * bh;
+ struct buffer_head * bh2;
+ char * p, * r;
+ int i, j, k, tmp;
+ int bitmap_nr;
+ struct super_block * sb;
+ struct ext2_group_desc * gdp;
+ struct ext2_super_block * es;
+#ifdef EXT2FS_DEBUG
+ static int goal_hits = 0, goal_attempts = 0;
+#endif
+ *err = -ENOSPC;
+ sb = inode->i_sb;
+ if (!sb) {
+ printk ("ext2_new_block: nonexistent device");
+ return 0;
+ }
+
+ lock_super (sb);
+ es = sb->u.ext2_sb.s_es;
+ if (le32_to_cpu(es->s_free_blocks_count) <= le32_to_cpu(es->s_r_blocks_count) &&
+ ((sb->u.ext2_sb.s_resuid != current->fsuid) &&
+ (sb->u.ext2_sb.s_resgid == 0 ||
+ !in_group_p (sb->u.ext2_sb.s_resgid)) &&
+ !capable(CAP_SYS_RESOURCE)))
+ goto out;
+
+ ext2_debug ("goal=%lu.\n", goal);
+
+repeat:
+ /*
+ * First, test whether the goal block is free.
+ */
+ if (goal < le32_to_cpu(es->s_first_data_block) ||
+ goal >= le32_to_cpu(es->s_blocks_count))
+ goal = le32_to_cpu(es->s_first_data_block);
+ i = (goal - le32_to_cpu(es->s_first_data_block)) / EXT2_BLOCKS_PER_GROUP(sb);
+ gdp = ext2_get_group_desc (sb, i, &bh2);
+ if (!gdp)
+ goto io_error;
+
+ if (le16_to_cpu(gdp->bg_free_blocks_count) > 0) {
+ j = ((goal - le32_to_cpu(es->s_first_data_block)) % EXT2_BLOCKS_PER_GROUP(sb));
+#ifdef EXT2FS_DEBUG
+ if (j)
+ goal_attempts++;
+#endif
+ bitmap_nr = load_block_bitmap (sb, i);
+ if (bitmap_nr < 0)
+ goto io_error;
+
+ bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr];
+
+ ext2_debug ("goal is at %d:%d.\n", i, j);
+
+ if (!ext2_test_bit(j, bh->b_data)) {
+ ext2_debug("goal bit allocated, %d hits\n",++goal_hits);
+ goto got_block;
+ }
+ if (j) {
+ /*
+ * The goal was occupied; search forward for a free
+ * block within the next XX blocks.
+ *
+ * end_goal is more or less random, but it has to be
+ * less than EXT2_BLOCKS_PER_GROUP. Aligning up to the
+ * next 64-bit boundary is simple..
+ */
+ int end_goal = (j + 63) & ~63;
+ j = ext2_find_next_zero_bit(bh->b_data, end_goal, j);
+ if (j < end_goal)
+ goto got_block;
+ }
+
+ ext2_debug ("Bit not found near goal\n");
+
+ /*
+ * There has been no free block found in the near vicinity
+ * 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.
+ */
+ p = ((char *) bh->b_data) + (j >> 3);
+ r = memscan(p, 0, (EXT2_BLOCKS_PER_GROUP(sb) - j + 7) >> 3);
+ k = (r - ((char *) bh->b_data)) << 3;
+ if (k < EXT2_BLOCKS_PER_GROUP(sb)) {
+ j = k;
+ goto search_back;
+ }
+
+ k = ext2_find_next_zero_bit ((unsigned long *) bh->b_data,
+ EXT2_BLOCKS_PER_GROUP(sb),
+ j);
+ if (k < EXT2_BLOCKS_PER_GROUP(sb)) {
+ j = k;
+ goto got_block;
+ }
+ }
+
+ ext2_debug ("Bit not found in block group %d.\n", i);
+
+ /*
+ * Now search the rest of the groups. We assume that
+ * i and gdp correctly point to the last group visited.
+ */
+ for (k = 0; k < sb->u.ext2_sb.s_groups_count; k++) {
+ i++;
+ if (i >= sb->u.ext2_sb.s_groups_count)
+ i = 0;
+ gdp = ext2_get_group_desc (sb, i, &bh2);
+ if (!gdp)
+ goto io_error;
+ if (le16_to_cpu(gdp->bg_free_blocks_count) > 0)
+ break;
+ }
+ if (k >= sb->u.ext2_sb.s_groups_count)
+ goto out;
+ bitmap_nr = load_block_bitmap (sb, i);
+ if (bitmap_nr < 0)
+ goto io_error;
+
+ bh = sb->u.ext2_sb.s_block_bitmap[bitmap_nr];
+ r = memscan(bh->b_data, 0, EXT2_BLOCKS_PER_GROUP(sb) >> 3);
+ j = (r - bh->b_data) << 3;
+ if (j < EXT2_BLOCKS_PER_GROUP(sb))
+ goto search_back;
+ else
+ j = ext2_find_first_zero_bit ((unsigned long *) bh->b_data,
+ EXT2_BLOCKS_PER_GROUP(sb));
+ if (j >= EXT2_BLOCKS_PER_GROUP(sb)) {
+ ext2_error (sb, "ext2_new_block",
+ "Free blocks count corrupted for block group %d", i);
+ goto out;
+ }
+
+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.
+ */
+ for (k = 0; k < 7 && j > 0 && !ext2_test_bit (j - 1, bh->b_data); k++, j--);
+
+got_block:
+
+ ext2_debug ("using block group %d(%d)\n", i, gdp->bg_free_blocks_count);
+
+ /*
+ * Check quota for allocation of this block.
+ */
+ if(DQUOT_ALLOC_BLOCK(inode, 1)) {
+ *err = -EDQUOT;
+ goto out;
+ }
+
+ tmp = j + i * EXT2_BLOCKS_PER_GROUP(sb) + le32_to_cpu(es->s_first_data_block);
+
+ if (tmp == le32_to_cpu(gdp->bg_block_bitmap) ||
+ tmp == le32_to_cpu(gdp->bg_inode_bitmap) ||
+ in_range (tmp, le32_to_cpu(gdp->bg_inode_table),
+ EXT2_SB(sb)->s_itb_per_group)) {
+ ext2_error (sb, "ext2_new_block",
+ "Allocating block in system zone - block = %u",
+ tmp);
+ ext2_set_bit(j, bh->b_data);
+ DQUOT_FREE_BLOCK(inode, 1);
+ goto repeat;
+ }
+
+ if (ext2_set_bit (j, bh->b_data)) {
+ ext2_warning (sb, "ext2_new_block",
+ "bit already set for block %d", j);
+ DQUOT_FREE_BLOCK(inode, 1);
+ goto repeat;
+ }
+
+ ext2_debug ("found bit %d\n", j);
+
+ /*
+ * Do block preallocation now if required.
+ */
+#ifdef EXT2_PREALLOCATE
+ /* Writer: ->i_prealloc* */
+ if (prealloc_count && !*prealloc_count) {
+ int prealloc_goal;
+ unsigned long next_block = tmp + 1;
+
+ prealloc_goal = es->s_prealloc_blocks ?
+ es->s_prealloc_blocks : EXT2_DEFAULT_PREALLOC_BLOCKS;
+
+ *prealloc_block = next_block;
+ /* Writer: end */
+ for (k = 1;
+ k < prealloc_goal && (j + k) < EXT2_BLOCKS_PER_GROUP(sb);
+ k++, next_block++) {
+ if (DQUOT_PREALLOC_BLOCK(inode, 1))
+ break;
+ /* Writer: ->i_prealloc* */
+ if (*prealloc_block + *prealloc_count != next_block ||
+ ext2_set_bit (j + k, bh->b_data)) {
+ /* Writer: end */
+ DQUOT_FREE_BLOCK(inode, 1);
+ break;
+ }
+ (*prealloc_count)++;
+ /* Writer: end */
+ }
+ /*
+ * As soon as we go for per-group spinlocks we'll need these
+ * done inside the loop above.
+ */
+ gdp->bg_free_blocks_count =
+ cpu_to_le16(le16_to_cpu(gdp->bg_free_blocks_count) -
+ (k - 1));
+ es->s_free_blocks_count =
+ cpu_to_le32(le32_to_cpu(es->s_free_blocks_count) -
+ (k - 1));
+ ext2_debug ("Preallocated a further %lu bits.\n",
+ (k - 1));
+ }
+#endif
+
+ j = tmp;
+
+ mark_buffer_dirty(bh);
+ if (sb->s_flags & MS_SYNCHRONOUS) {
+ ll_rw_block (WRITE, 1, &bh);
+ wait_on_buffer (bh);
+ }
+
+ if (j >= le32_to_cpu(es->s_blocks_count)) {
+ ext2_error (sb, "ext2_new_block",
+ "block(%d) >= blocks count(%d) - "
+ "block_group = %d, es == %p ",j,
+ le32_to_cpu(es->s_blocks_count), i, es);
+ goto out;
+ }
+
+ ext2_debug ("allocating block %d. "
+ "Goal hits %d of %d.\n", j, goal_hits, goal_attempts);
+
+ gdp->bg_free_blocks_count = cpu_to_le16(le16_to_cpu(gdp->bg_free_blocks_count) - 1);
+ mark_buffer_dirty(bh2);
+ es->s_free_blocks_count = cpu_to_le32(le32_to_cpu(es->s_free_blocks_count) - 1);
+ mark_buffer_dirty(sb->u.ext2_sb.s_sbh);
+ sb->s_dirt = 1;
+ unlock_super (sb);
+ *err = 0;
+ return j;
+
+io_error:
+ *err = -EIO;
+out:
+ unlock_super (sb);
+ return 0;
+
+}
+
+unsigned long ext2_count_free_blocks (struct super_block * sb)
+{
+#ifdef EXT2FS_DEBUG
+ struct ext2_super_block * es;
+ unsigned long desc_count, bitmap_count, x;
+ int bitmap_nr;
+ struct ext2_group_desc * gdp;
+ int i;
+
+ lock_super (sb);
+ es = sb->u.ext2_sb.s_es;
+ desc_count = 0;
+ bitmap_count = 0;
+ gdp = NULL;
+ for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) {
+ gdp = ext2_get_group_desc (sb, i, NULL);
+ if (!gdp)
+ continue;
+ desc_count += le16_to_cpu(gdp->bg_free_blocks_count);
+ bitmap_nr = load_block_bitmap (sb, i);
+ if (bitmap_nr < 0)
+ continue;
+
+ x = ext2_count_free (sb->u.ext2_sb.s_block_bitmap[bitmap_nr],
+ sb->s_blocksize);
+ printk ("group %d: stored = %d, counted = %lu\n",
+ i, le16_to_cpu(gdp->bg_free_blocks_count), x);
+ bitmap_count += x;
+ }
+ printk("ext2_count_free_blocks: stored = %lu, computed = %lu, %lu\n",
+ le32_to_cpu(es->s_free_blocks_count), desc_count, bitmap_count);
+ unlock_super (sb);
+ return bitmap_count;
+#else
+ return le32_to_cpu(sb->u.ext2_sb.s_es->s_free_blocks_count);
+#endif
+}
+
+static inline int block_in_use (unsigned long block,
+ struct super_block * sb,
+ unsigned char * map)
+{
+ return ext2_test_bit ((block - le32_to_cpu(sb->u.ext2_sb.s_es->s_first_data_block)) %
+ EXT2_BLOCKS_PER_GROUP(sb), map);
+}
+
+static 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;
+ }
+}
+
+int ext2_group_sparse(int group)
+{
+ return (test_root(group, 3) || test_root(group, 5) ||
+ test_root(group, 7));
+}
+
+/**
+ * ext2_bg_has_super - number of blocks used by the superblock in group
+ * @sb: superblock for filesystem
+ * @group: group number to check
+ *
+ * Return the number of blocks used by the superblock (primary or backup)
+ * in this group. Currently this will be only 0 or 1.
+ */
+int ext2_bg_has_super(struct super_block *sb, int group)
+{
+ if (EXT2_HAS_RO_COMPAT_FEATURE(sb,EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)&&
+ !ext2_group_sparse(group))
+ return 0;
+ return 1;
+}
+
+/**
+ * ext2_bg_num_gdb - number of blocks used by the group table in group
+ * @sb: superblock for filesystem
+ * @group: group number to check
+ *
+ * Return the number of blocks used by the group descriptor table
+ * (primary or backup) in this group. In the future there may be a
+ * different number of descriptor blocks in each group.
+ */
+unsigned long ext2_bg_num_gdb(struct super_block *sb, int group)
+{
+ if (EXT2_HAS_RO_COMPAT_FEATURE(sb,EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)&&
+ !ext2_group_sparse(group))
+ return 0;
+ return EXT2_SB(sb)->s_gdb_count;
+}
+
+#ifdef CONFIG_EXT2_CHECK
+/* Called at mount-time, super-block is locked */
+void ext2_check_blocks_bitmap (struct super_block * sb)
+{
+ struct buffer_head * bh;
+ struct ext2_super_block * es;
+ unsigned long desc_count, bitmap_count, x, j;
+ unsigned long desc_blocks;
+ int bitmap_nr;
+ struct ext2_group_desc * gdp;
+ int i;
+
+ es = sb->u.ext2_sb.s_es;
+ desc_count = 0;
+ bitmap_count = 0;
+ gdp = NULL;
+ for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) {
+ gdp = ext2_get_group_desc (sb, i, NULL);
+ if (!gdp)
+ continue;
+ desc_count += le16_to_cpu(gdp->bg_free_blocks_count);
+ bitmap_nr = load_block_bitmap (sb, i);
+ if (bitmap_nr < 0)
+ continue;
+
+ bh = EXT2_SB(sb)->s_block_bitmap[bitmap_nr];
+
+ if (ext2_bg_has_super(sb, i) && !ext2_test_bit(0, bh->b_data))
+ ext2_error(sb, __FUNCTION__,
+ "Superblock in group %d is marked free", i);
+
+ desc_blocks = ext2_bg_num_gdb(sb, i);
+ for (j = 0; j < desc_blocks; j++)
+ if (!ext2_test_bit(j + 1, bh->b_data))
+ ext2_error(sb, __FUNCTION__,
+ "Descriptor block #%ld in group "
+ "%d is marked free", j, i);
+
+ if (!block_in_use (le32_to_cpu(gdp->bg_block_bitmap), sb, bh->b_data))
+ ext2_error (sb, "ext2_check_blocks_bitmap",
+ "Block bitmap for group %d is marked free",
+ i);
+
+ if (!block_in_use (le32_to_cpu(gdp->bg_inode_bitmap), sb, bh->b_data))
+ ext2_error (sb, "ext2_check_blocks_bitmap",
+ "Inode bitmap for group %d is marked free",
+ i);
+
+ for (j = 0; j < sb->u.ext2_sb.s_itb_per_group; j++)
+ if (!block_in_use (le32_to_cpu(gdp->bg_inode_table) + j, sb, bh->b_data))
+ ext2_error (sb, "ext2_check_blocks_bitmap",
+ "Block #%ld of the inode table in "
+ "group %d is marked free", j, i);
+
+ x = ext2_count_free (bh, sb->s_blocksize);
+ if (le16_to_cpu(gdp->bg_free_blocks_count) != x)
+ ext2_error (sb, "ext2_check_blocks_bitmap",
+ "Wrong free blocks count for group %d, "
+ "stored = %d, counted = %lu", i,
+ le16_to_cpu(gdp->bg_free_blocks_count), x);
+ bitmap_count += x;
+ }
+ if (le32_to_cpu(es->s_free_blocks_count) != bitmap_count)
+ ext2_error (sb, "ext2_check_blocks_bitmap",
+ "Wrong free blocks count in super block, "
+ "stored = %lu, counted = %lu",
+ (unsigned long) le32_to_cpu(es->s_free_blocks_count), bitmap_count);
+}
+#endif
diff --git a/fs/ext2/bitmap.c b/fs/ext2/bitmap.c
new file mode 100644
index 00000000000000..c30a3b075bf88f
--- /dev/null
+++ b/fs/ext2/bitmap.c
@@ -0,0 +1,27 @@
+/*
+ * linux/fs/ext2/bitmap.c
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ */
+
+#include <linux/fs.h>
+#include <linux/ext2_fs.h>
+
+
+static int nibblemap[] = {4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0};
+
+unsigned long ext2_count_free (struct buffer_head * map, unsigned int numchars)
+{
+ unsigned int i;
+ unsigned long sum = 0;
+
+ if (!map)
+ return (0);
+ for (i = 0; i < numchars; i++)
+ sum += nibblemap[map->b_data[i] & 0xf] +
+ nibblemap[(map->b_data[i] >> 4) & 0xf];
+ return (sum);
+}
diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c
new file mode 100644
index 00000000000000..58b76dd0cd29fa
--- /dev/null
+++ b/fs/ext2/dir.c
@@ -0,0 +1,600 @@
+/*
+ * linux/fs/ext2/dir.c
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * from
+ *
+ * linux/fs/minix/dir.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * ext2 directory handling functions
+ *
+ * Big-endian to little-endian byte-swapping/bitmaps by
+ * David S. Miller (davem@caip.rutgers.edu), 1995
+ *
+ * All code that works with directory layout had been switched to pagecache
+ * and moved here. AV
+ */
+
+#include <linux/fs.h>
+#include <linux/ext2_fs.h>
+#include <linux/pagemap.h>
+
+typedef struct ext2_dir_entry_2 ext2_dirent;
+
+/*
+ * ext2 uses block-sized chunks. Arguably, sector-sized ones would be
+ * more robust, but we have what we have
+ */
+static inline unsigned ext2_chunk_size(struct inode *inode)
+{
+ return inode->i_sb->s_blocksize;
+}
+
+static inline void ext2_put_page(struct page *page)
+{
+ kunmap(page);
+ page_cache_release(page);
+}
+
+static inline unsigned long dir_pages(struct inode *inode)
+{
+ return (inode->i_size+PAGE_CACHE_SIZE-1)>>PAGE_CACHE_SHIFT;
+}
+
+static int ext2_commit_chunk(struct page *page, unsigned from, unsigned to)
+{
+ struct inode *dir = page->mapping->host;
+ int err = 0;
+ dir->i_version = ++event;
+ page->mapping->a_ops->commit_write(NULL, page, from, to);
+ if (IS_SYNC(dir)) {
+ int err2;
+ err = writeout_one_page(page);
+ err2 = waitfor_one_page(page);
+ if (err == 0)
+ err = err2;
+ }
+ return err;
+}
+
+static void ext2_check_page(struct page *page)
+{
+ struct inode *dir = page->mapping->host;
+ struct super_block *sb = dir->i_sb;
+ unsigned chunk_size = ext2_chunk_size(dir);
+ char *kaddr = page_address(page);
+ u32 max_inumber = le32_to_cpu(sb->u.ext2_sb.s_es->s_inodes_count);
+ unsigned offs, rec_len;
+ unsigned limit = PAGE_CACHE_SIZE;
+ ext2_dirent *p;
+ char *error;
+
+ if ((dir->i_size >> PAGE_CACHE_SHIFT) == page->index) {
+ limit = dir->i_size & ~PAGE_CACHE_MASK;
+ if (limit & (chunk_size - 1))
+ goto Ebadsize;
+ for (offs = limit; offs<PAGE_CACHE_SIZE; offs += chunk_size) {
+ ext2_dirent *p = (ext2_dirent*)(kaddr + offs);
+ p->rec_len = cpu_to_le16(chunk_size);
+ }
+ if (!limit)
+ goto out;
+ }
+ for (offs = 0; offs <= limit - EXT2_DIR_REC_LEN(1); offs += rec_len) {
+ p = (ext2_dirent *)(kaddr + offs);
+ rec_len = le16_to_cpu(p->rec_len);
+
+ if (rec_len < EXT2_DIR_REC_LEN(1))
+ goto Eshort;
+ if (rec_len & 3)
+ goto Ealign;
+ if (rec_len < EXT2_DIR_REC_LEN(p->name_len))
+ goto Enamelen;
+ if (((offs + rec_len - 1) ^ offs) & ~(chunk_size-1))
+ goto Espan;
+ if (le32_to_cpu(p->inode) > max_inumber)
+ goto Einumber;
+ }
+ if (offs != limit)
+ goto Eend;
+out:
+ SetPageChecked(page);
+ return;
+
+ /* Too bad, we had an error */
+
+Ebadsize:
+ ext2_error(sb, "ext2_check_page",
+ "size of directory #%lu is not a multiple of chunk size",
+ dir->i_ino
+ );
+ goto fail;
+Eshort:
+ error = "rec_len is smaller than minimal";
+ goto bad_entry;
+Ealign:
+ error = "unaligned directory entry";
+ goto bad_entry;
+Enamelen:
+ error = "rec_len is too small for name_len";
+ goto bad_entry;
+Espan:
+ error = "directory entry across blocks";
+ goto bad_entry;
+Einumber:
+ error = "inode out of bounds";
+bad_entry:
+ ext2_error (sb, "ext2_check_page", "bad entry in directory #%lu: %s - "
+ "offset=%lu, inode=%lu, rec_len=%d, name_len=%d",
+ dir->i_ino, error, (page->index<<PAGE_CACHE_SHIFT)+offs,
+ (unsigned long) le32_to_cpu(p->inode),
+ rec_len, p->name_len);
+ goto fail;
+Eend:
+ p = (ext2_dirent *)(kaddr + offs);
+ ext2_error (sb, "ext2_check_page",
+ "entry in directory #%lu spans the page boundary"
+ "offset=%lu, inode=%lu",
+ dir->i_ino, (page->index<<PAGE_CACHE_SHIFT)+offs,
+ (unsigned long) le32_to_cpu(p->inode));
+fail:
+ SetPageChecked(page);
+ SetPageError(page);
+}
+
+static struct page * ext2_get_page(struct inode *dir, unsigned long n)
+{
+ struct address_space *mapping = dir->i_mapping;
+ struct page *page = read_cache_page(mapping, n,
+ (filler_t*)mapping->a_ops->readpage, NULL);
+ if (!IS_ERR(page)) {
+ wait_on_page(page);
+ kmap(page);
+ if (!Page_Uptodate(page))
+ goto fail;
+ if (!PageChecked(page))
+ ext2_check_page(page);
+ if (PageError(page))
+ goto fail;
+ }
+ return page;
+
+fail:
+ ext2_put_page(page);
+ return ERR_PTR(-EIO);
+}
+
+/*
+ * NOTE! unlike strncmp, ext2_match returns 1 for success, 0 for failure.
+ *
+ * len <= EXT2_NAME_LEN and de != NULL are guaranteed by caller.
+ */
+static inline int ext2_match (int len, const char * const name,
+ struct ext2_dir_entry_2 * de)
+{
+ if (len != de->name_len)
+ return 0;
+ if (!de->inode)
+ return 0;
+ return !memcmp(name, de->name, len);
+}
+
+/*
+ * p is at least 6 bytes before the end of page
+ */
+static inline ext2_dirent *ext2_next_entry(ext2_dirent *p)
+{
+ return (ext2_dirent *)((char*)p + le16_to_cpu(p->rec_len));
+}
+
+static inline unsigned
+ext2_validate_entry(char *base, unsigned offset, unsigned mask)
+{
+ ext2_dirent *de = (ext2_dirent*)(base + offset);
+ ext2_dirent *p = (ext2_dirent*)(base + (offset&mask));
+ while ((char*)p < (char*)de)
+ p = ext2_next_entry(p);
+ return (char *)p - base;
+}
+
+static unsigned char ext2_filetype_table[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,
+};
+
+#define S_SHIFT 12
+static unsigned char ext2_type_by_mode[S_IFMT >> S_SHIFT] = {
+ [S_IFREG >> S_SHIFT] EXT2_FT_REG_FILE,
+ [S_IFDIR >> S_SHIFT] EXT2_FT_DIR,
+ [S_IFCHR >> S_SHIFT] EXT2_FT_CHRDEV,
+ [S_IFBLK >> S_SHIFT] EXT2_FT_BLKDEV,
+ [S_IFIFO >> S_SHIFT] EXT2_FT_FIFO,
+ [S_IFSOCK >> S_SHIFT] EXT2_FT_SOCK,
+ [S_IFLNK >> S_SHIFT] EXT2_FT_SYMLINK,
+};
+
+static inline void ext2_set_de_type(ext2_dirent *de, struct inode *inode)
+{
+ mode_t mode = inode->i_mode;
+ if (EXT2_HAS_INCOMPAT_FEATURE(inode->i_sb, EXT2_FEATURE_INCOMPAT_FILETYPE))
+ de->file_type = ext2_type_by_mode[(mode & S_IFMT)>>S_SHIFT];
+ else
+ de->file_type = 0;
+}
+
+static int
+ext2_readdir (struct file * filp, void * dirent, filldir_t filldir)
+{
+ loff_t pos = filp->f_pos;
+ struct inode *inode = filp->f_dentry->d_inode;
+ struct super_block *sb = inode->i_sb;
+ unsigned offset = pos & ~PAGE_CACHE_MASK;
+ unsigned long n = pos >> PAGE_CACHE_SHIFT;
+ unsigned long npages = dir_pages(inode);
+ unsigned chunk_mask = ~(ext2_chunk_size(inode)-1);
+ unsigned char *types = NULL;
+ int need_revalidate = (filp->f_version != inode->i_version);
+
+ if (pos > inode->i_size - EXT2_DIR_REC_LEN(1))
+ goto done;
+
+ if (EXT2_HAS_INCOMPAT_FEATURE(sb, EXT2_FEATURE_INCOMPAT_FILETYPE))
+ types = ext2_filetype_table;
+
+ for ( ; n < npages; n++, offset = 0) {
+ char *kaddr, *limit;
+ ext2_dirent *de;
+ struct page *page = ext2_get_page(inode, n);
+
+ if (IS_ERR(page))
+ continue;
+ kaddr = page_address(page);
+ if (need_revalidate) {
+ offset = ext2_validate_entry(kaddr, offset, chunk_mask);
+ need_revalidate = 0;
+ }
+ de = (ext2_dirent *)(kaddr+offset);
+ limit = kaddr + PAGE_CACHE_SIZE - EXT2_DIR_REC_LEN(1);
+ for ( ;(char*)de <= limit; de = ext2_next_entry(de))
+ if (de->inode) {
+ int over;
+ unsigned char d_type = DT_UNKNOWN;
+
+ if (types && de->file_type < EXT2_FT_MAX)
+ d_type = types[de->file_type];
+
+ offset = (char *)de - kaddr;
+ over = filldir(dirent, de->name, de->name_len,
+ (n<<PAGE_CACHE_SHIFT) | offset,
+ le32_to_cpu(de->inode), d_type);
+ if (over) {
+ ext2_put_page(page);
+ goto done;
+ }
+ }
+ ext2_put_page(page);
+ }
+
+done:
+ filp->f_pos = (n << PAGE_CACHE_SHIFT) | offset;
+ filp->f_version = inode->i_version;
+ UPDATE_ATIME(inode);
+ return 0;
+}
+
+/*
+ * ext2_find_entry()
+ *
+ * finds an entry in the specified directory with the wanted name. It
+ * returns the page in which the entry was found, and the entry itself
+ * (as a parameter - res_dir). Page is returned mapped and unlocked.
+ * Entry is guaranteed to be valid.
+ */
+struct ext2_dir_entry_2 * ext2_find_entry (struct inode * dir,
+ struct dentry *dentry, struct page ** res_page)
+{
+ const char *name = dentry->d_name.name;
+ int namelen = dentry->d_name.len;
+ unsigned reclen = EXT2_DIR_REC_LEN(namelen);
+ unsigned long start, n;
+ unsigned long npages = dir_pages(dir);
+ struct page *page = NULL;
+ ext2_dirent * de;
+
+ /* OFFSET_CACHE */
+ *res_page = NULL;
+
+ start = dir->u.ext2_i.i_dir_start_lookup;
+ if (start >= npages)
+ start = 0;
+ n = start;
+ do {
+ char *kaddr;
+ page = ext2_get_page(dir, n);
+ if (!IS_ERR(page)) {
+ kaddr = page_address(page);
+ de = (ext2_dirent *) kaddr;
+ kaddr += PAGE_CACHE_SIZE - reclen;
+ while ((char *) de <= kaddr) {
+ if (ext2_match (namelen, name, de))
+ goto found;
+ de = ext2_next_entry(de);
+ }
+ ext2_put_page(page);
+ }
+ if (++n >= npages)
+ n = 0;
+ } while (n != start);
+ return NULL;
+
+found:
+ *res_page = page;
+ dir->u.ext2_i.i_dir_start_lookup = n;
+ return de;
+}
+
+struct ext2_dir_entry_2 * ext2_dotdot (struct inode *dir, struct page **p)
+{
+ struct page *page = ext2_get_page(dir, 0);
+ ext2_dirent *de = NULL;
+
+ if (!IS_ERR(page)) {
+ de = ext2_next_entry((ext2_dirent *) page_address(page));
+ *p = page;
+ }
+ return de;
+}
+
+ino_t ext2_inode_by_name(struct inode * dir, struct dentry *dentry)
+{
+ ino_t res = 0;
+ struct ext2_dir_entry_2 * de;
+ struct page *page;
+
+ de = ext2_find_entry (dir, dentry, &page);
+ if (de) {
+ res = le32_to_cpu(de->inode);
+ kunmap(page);
+ page_cache_release(page);
+ }
+ return res;
+}
+
+/* Releases the page */
+void ext2_set_link(struct inode *dir, struct ext2_dir_entry_2 *de,
+ struct page *page, struct inode *inode)
+{
+ unsigned from = (char *) de - (char *) page_address(page);
+ unsigned to = from + le16_to_cpu(de->rec_len);
+ int err;
+
+ lock_page(page);
+ err = page->mapping->a_ops->prepare_write(NULL, page, from, to);
+ if (err)
+ BUG();
+ de->inode = cpu_to_le32(inode->i_ino);
+ ext2_set_de_type (de, inode);
+ err = ext2_commit_chunk(page, from, to);
+ UnlockPage(page);
+ ext2_put_page(page);
+ dir->i_mtime = dir->i_ctime = CURRENT_TIME;
+ dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL;
+ mark_inode_dirty(dir);
+}
+
+/*
+ * Parent is locked.
+ */
+int ext2_add_link (struct dentry *dentry, struct inode *inode)
+{
+ struct inode *dir = dentry->d_parent->d_inode;
+ const char *name = dentry->d_name.name;
+ int namelen = dentry->d_name.len;
+ unsigned reclen = EXT2_DIR_REC_LEN(namelen);
+ unsigned short rec_len, name_len;
+ struct page *page = NULL;
+ ext2_dirent * de;
+ unsigned long npages = dir_pages(dir);
+ unsigned long n;
+ char *kaddr;
+ unsigned from, to;
+ int err;
+
+ /* We take care of directory expansion in the same loop */
+ for (n = 0; n <= npages; n++) {
+ page = ext2_get_page(dir, n);
+ err = PTR_ERR(page);
+ if (IS_ERR(page))
+ goto out;
+ kaddr = page_address(page);
+ de = (ext2_dirent *)kaddr;
+ kaddr += PAGE_CACHE_SIZE - reclen;
+ while ((char *)de <= kaddr) {
+ err = -EEXIST;
+ if (ext2_match (namelen, name, de))
+ goto out_page;
+ name_len = EXT2_DIR_REC_LEN(de->name_len);
+ rec_len = le16_to_cpu(de->rec_len);
+ if (!de->inode && rec_len >= reclen)
+ goto got_it;
+ if (rec_len >= name_len + reclen)
+ goto got_it;
+ de = (ext2_dirent *) ((char *) de + rec_len);
+ }
+ ext2_put_page(page);
+ }
+ BUG();
+ return -EINVAL;
+
+got_it:
+ from = (char*)de - (char*)page_address(page);
+ to = from + rec_len;
+ lock_page(page);
+ err = page->mapping->a_ops->prepare_write(NULL, page, from, to);
+ if (err)
+ goto out_unlock;
+ if (de->inode) {
+ ext2_dirent *de1 = (ext2_dirent *) ((char *) de + name_len);
+ de1->rec_len = cpu_to_le16(rec_len - name_len);
+ de->rec_len = cpu_to_le16(name_len);
+ de = de1;
+ }
+ de->name_len = namelen;
+ memcpy (de->name, name, namelen);
+ de->inode = cpu_to_le32(inode->i_ino);
+ ext2_set_de_type (de, inode);
+ err = ext2_commit_chunk(page, from, to);
+ dir->i_mtime = dir->i_ctime = CURRENT_TIME;
+ dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL;
+ mark_inode_dirty(dir);
+ /* OFFSET_CACHE */
+out_unlock:
+ UnlockPage(page);
+out_page:
+ ext2_put_page(page);
+out:
+ return err;
+}
+
+/*
+ * ext2_delete_entry deletes a directory entry by merging it with the
+ * previous entry. Page is up-to-date. Releases the page.
+ */
+int ext2_delete_entry (struct ext2_dir_entry_2 * dir, struct page * page )
+{
+ struct address_space *mapping = page->mapping;
+ struct inode *inode = mapping->host;
+ char *kaddr = page_address(page);
+ unsigned from = ((char*)dir - kaddr) & ~(ext2_chunk_size(inode)-1);
+ unsigned to = ((char*)dir - kaddr) + le16_to_cpu(dir->rec_len);
+ ext2_dirent * pde = NULL;
+ ext2_dirent * de = (ext2_dirent *) (kaddr + from);
+ int err;
+
+ while ((char*)de < (char*)dir) {
+ pde = de;
+ de = ext2_next_entry(de);
+ }
+ if (pde)
+ from = (char*)pde - (char*)page_address(page);
+ lock_page(page);
+ err = mapping->a_ops->prepare_write(NULL, page, from, to);
+ if (err)
+ BUG();
+ if (pde)
+ pde->rec_len = cpu_to_le16(to-from);
+ dir->inode = 0;
+ err = ext2_commit_chunk(page, from, to);
+ UnlockPage(page);
+ ext2_put_page(page);
+ inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+ inode->u.ext2_i.i_flags &= ~EXT2_BTREE_FL;
+ mark_inode_dirty(inode);
+ return err;
+}
+
+/*
+ * Set the first fragment of directory.
+ */
+int ext2_make_empty(struct inode *inode, struct inode *parent)
+{
+ struct address_space *mapping = inode->i_mapping;
+ struct page *page = grab_cache_page(mapping, 0);
+ unsigned chunk_size = ext2_chunk_size(inode);
+ struct ext2_dir_entry_2 * de;
+ char *base;
+ int err;
+
+ if (!page)
+ return -ENOMEM;
+ err = mapping->a_ops->prepare_write(NULL, page, 0, chunk_size);
+ if (err)
+ goto fail;
+
+ base = page_address(page);
+
+ memset(base, 0, chunk_size);
+ de = (struct ext2_dir_entry_2 *) base;
+ de->name_len = 1;
+ de->rec_len = cpu_to_le16(EXT2_DIR_REC_LEN(1));
+ memcpy (de->name, ".\0\0", 4);
+ de->inode = cpu_to_le32(inode->i_ino);
+ ext2_set_de_type (de, inode);
+
+ de = (struct ext2_dir_entry_2 *) (base + EXT2_DIR_REC_LEN(1));
+ de->name_len = 2;
+ de->rec_len = cpu_to_le16(chunk_size - EXT2_DIR_REC_LEN(1));
+ de->inode = cpu_to_le32(parent->i_ino);
+ memcpy (de->name, "..\0", 4);
+ ext2_set_de_type (de, inode);
+
+ err = ext2_commit_chunk(page, 0, chunk_size);
+fail:
+ UnlockPage(page);
+ page_cache_release(page);
+ return err;
+}
+
+/*
+ * routine to check that the specified directory is empty (for rmdir)
+ */
+int ext2_empty_dir (struct inode * inode)
+{
+ struct page *page = NULL;
+ unsigned long i, npages = dir_pages(inode);
+
+ for (i = 0; i < npages; i++) {
+ char *kaddr;
+ ext2_dirent * de;
+ page = ext2_get_page(inode, i);
+
+ if (IS_ERR(page))
+ continue;
+
+ kaddr = page_address(page);
+ de = (ext2_dirent *)kaddr;
+ kaddr += PAGE_CACHE_SIZE-EXT2_DIR_REC_LEN(1);
+
+ while ((char *)de <= kaddr) {
+ if (de->inode != 0) {
+ /* check for . and .. */
+ if (de->name[0] != '.')
+ goto not_empty;
+ if (de->name_len > 2)
+ goto not_empty;
+ if (de->name_len < 2) {
+ if (de->inode !=
+ cpu_to_le32(inode->i_ino))
+ goto not_empty;
+ } else if (de->name[1] != '.')
+ goto not_empty;
+ }
+ de = ext2_next_entry(de);
+ }
+ ext2_put_page(page);
+ }
+ return 1;
+
+not_empty:
+ ext2_put_page(page);
+ return 0;
+}
+
+struct file_operations ext2_dir_operations = {
+ read: generic_read_dir,
+ readdir: ext2_readdir,
+ ioctl: ext2_ioctl,
+ fsync: ext2_sync_file,
+};
diff --git a/fs/ext2/file.c b/fs/ext2/file.c
new file mode 100644
index 00000000000000..8d978bdf66bee0
--- /dev/null
+++ b/fs/ext2/file.c
@@ -0,0 +1,54 @@
+/*
+ * linux/fs/ext2/file.c
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * from
+ *
+ * linux/fs/minix/file.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * ext2 fs regular file handling primitives
+ *
+ * 64-bit file support on 64-bit platforms by Jakub Jelinek
+ * (jj@sunsite.ms.mff.cuni.cz)
+ */
+
+#include <linux/fs.h>
+#include <linux/ext2_fs.h>
+#include <linux/sched.h>
+
+/*
+ * Called when an inode is released. Note that this is different
+ * from ext2_open_file: open gets called at every open, but release
+ * gets called only when /all/ the files are closed.
+ */
+static int ext2_release_file (struct inode * inode, struct file * filp)
+{
+ if (filp->f_mode & FMODE_WRITE)
+ ext2_discard_prealloc (inode);
+ return 0;
+}
+
+/*
+ * We have mostly NULL's here: the current defaults are ok for
+ * the ext2 filesystem.
+ */
+struct file_operations ext2_file_operations = {
+ llseek: generic_file_llseek,
+ read: generic_file_read,
+ write: generic_file_write,
+ ioctl: ext2_ioctl,
+ mmap: generic_file_mmap,
+ open: generic_file_open,
+ release: ext2_release_file,
+ fsync: ext2_sync_file,
+};
+
+struct inode_operations ext2_file_inode_operations = {
+ truncate: ext2_truncate,
+};
diff --git a/fs/ext2/fsync.c b/fs/ext2/fsync.c
new file mode 100644
index 00000000000000..211e52c7fc5b47
--- /dev/null
+++ b/fs/ext2/fsync.c
@@ -0,0 +1,55 @@
+/*
+ * linux/fs/ext2/fsync.c
+ *
+ * Copyright (C) 1993 Stephen Tweedie (sct@dcs.ed.ac.uk)
+ * from
+ * Copyright (C) 1992 Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ * from
+ * linux/fs/minix/truncate.c Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * ext2fs fsync primitive
+ *
+ * Big-endian to little-endian byte-swapping/bitmaps by
+ * David S. Miller (davem@caip.rutgers.edu), 1995
+ *
+ * Removed unnecessary code duplication for little endian machines
+ * and excessive __inline__s.
+ * Andi Kleen, 1997
+ *
+ * Major simplications and cleanup - we only need to do the metadata, because
+ * we can depend on generic_block_fdatasync() to sync the data blocks.
+ */
+
+#include <linux/fs.h>
+#include <linux/ext2_fs.h>
+#include <linux/locks.h>
+#include <linux/smp_lock.h>
+
+
+/*
+ * File may be NULL when we are called. Perhaps we shouldn't
+ * even pass file to fsync ?
+ */
+
+int ext2_sync_file(struct file * file, struct dentry *dentry, int datasync)
+{
+ struct inode *inode = dentry->d_inode;
+ return ext2_fsync_inode(inode, datasync);
+}
+
+int ext2_fsync_inode(struct inode *inode, int datasync)
+{
+ int err;
+
+ err = fsync_inode_buffers(inode);
+ err |= fsync_inode_data_buffers(inode);
+ if (!(inode->i_state & I_DIRTY))
+ return err;
+ if (datasync && !(inode->i_state & I_DIRTY_DATASYNC))
+ return err;
+
+ err |= ext2_sync_inode(inode);
+ return err ? -EIO : 0;
+}
diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c
new file mode 100644
index 00000000000000..ed9a314e7630fa
--- /dev/null
+++ b/fs/ext2/ialloc.c
@@ -0,0 +1,508 @@
+/*
+ * linux/fs/ext2/ialloc.c
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * BSD ufs-inspired inode and directory allocation by
+ * Stephen Tweedie (sct@dcs.ed.ac.uk), 1993
+ * Big-endian to little-endian byte-swapping/bitmaps by
+ * David S. Miller (davem@caip.rutgers.edu), 1995
+ */
+
+#include <linux/config.h>
+#include <linux/fs.h>
+#include <linux/ext2_fs.h>
+#include <linux/locks.h>
+#include <linux/quotaops.h>
+
+
+/*
+ * ialloc.c contains the inodes allocation and deallocation routines
+ */
+
+/*
+ * The free inodes are managed by bitmaps. A file system contains several
+ * blocks groups. Each group contains 1 bitmap block for blocks, 1 bitmap
+ * block for inodes, N blocks for the inode table and data blocks.
+ *
+ * The file system contains group descriptors which are located after the
+ * super block. Each descriptor contains the number of the bitmap block and
+ * the free blocks count in the block. The descriptors are loaded in memory
+ * when a file system is mounted (see ext2_read_super).
+ */
+
+
+/*
+ * Read the inode allocation bitmap for a given block_group, reading
+ * into the specified slot in the superblock's bitmap cache.
+ *
+ * Return buffer_head of bitmap on success or NULL.
+ */
+static struct buffer_head *read_inode_bitmap (struct super_block * sb,
+ unsigned long block_group)
+{
+ struct ext2_group_desc *desc;
+ struct buffer_head *bh = NULL;
+
+ desc = ext2_get_group_desc(sb, block_group, NULL);
+ if (!desc)
+ goto error_out;
+
+ bh = sb_bread(sb, le32_to_cpu(desc->bg_inode_bitmap));
+ if (!bh)
+ ext2_error (sb, "read_inode_bitmap",
+ "Cannot read inode bitmap - "
+ "block_group = %lu, inode_bitmap = %lu",
+ block_group, (unsigned long) desc->bg_inode_bitmap);
+error_out:
+ return bh;
+}
+
+/*
+ * load_inode_bitmap loads the inode bitmap for a blocks group
+ *
+ * It maintains a cache for the last bitmaps loaded. This cache is managed
+ * with a LRU algorithm.
+ *
+ * Notes:
+ * 1/ There is one cache per mounted file system.
+ * 2/ If the file system contains less than EXT2_MAX_GROUP_LOADED groups,
+ * this function reads the bitmap without maintaining a LRU cache.
+ *
+ * Return the buffer_head of the bitmap or the ERR_PTR(error)
+ */
+static struct buffer_head *load_inode_bitmap (struct super_block * sb,
+ unsigned int block_group)
+{
+ int i, slot = 0;
+ struct ext2_sb_info *sbi = &sb->u.ext2_sb;
+ struct buffer_head *bh = sbi->s_inode_bitmap[0];
+
+ if (block_group >= sbi->s_groups_count)
+ ext2_panic (sb, "load_inode_bitmap",
+ "block_group >= groups_count - "
+ "block_group = %d, groups_count = %lu",
+ block_group, sbi->s_groups_count);
+
+ if (sbi->s_loaded_inode_bitmaps > 0 &&
+ sbi->s_inode_bitmap_number[0] == block_group && bh)
+ goto found;
+
+ if (sbi->s_groups_count <= EXT2_MAX_GROUP_LOADED) {
+ slot = block_group;
+ bh = sbi->s_inode_bitmap[slot];
+ if (!bh)
+ goto read_it;
+ if (sbi->s_inode_bitmap_number[slot] == slot)
+ goto found;
+ ext2_panic (sb, "load_inode_bitmap",
+ "block_group != inode_bitmap_number");
+ }
+
+ bh = NULL;
+ for (i = 0; i < sbi->s_loaded_inode_bitmaps &&
+ sbi->s_inode_bitmap_number[i] != block_group;
+ i++)
+ ;
+ if (i < sbi->s_loaded_inode_bitmaps)
+ bh = sbi->s_inode_bitmap[i];
+ else if (sbi->s_loaded_inode_bitmaps < EXT2_MAX_GROUP_LOADED)
+ sbi->s_loaded_inode_bitmaps++;
+ else
+ brelse (sbi->s_inode_bitmap[--i]);
+
+ while (i--) {
+ sbi->s_inode_bitmap_number[i+1] = sbi->s_inode_bitmap_number[i];
+ sbi->s_inode_bitmap[i+1] = sbi->s_inode_bitmap[i];
+ }
+
+read_it:
+ if (!bh)
+ bh = read_inode_bitmap (sb, block_group);
+ sbi->s_inode_bitmap_number[slot] = block_group;
+ sbi->s_inode_bitmap[slot] = bh;
+ if (!bh)
+ return ERR_PTR(-EIO);
+found:
+ return bh;
+}
+
+/*
+ * NOTE! When we get the inode, we're the only people
+ * that have access to it, and as such there are no
+ * race conditions we have to worry about. The inode
+ * is not on the hash-lists, and it cannot be reached
+ * through the filesystem because the directory entry
+ * has been deleted earlier.
+ *
+ * HOWEVER: we must make sure that we get no aliases,
+ * which means that we have to call "clear_inode()"
+ * _before_ we mark the inode not in use in the inode
+ * bitmaps. Otherwise a newly created file might use
+ * the same inode number (not actually the same pointer
+ * though), and then we'd have two inodes sharing the
+ * same inode number and space on the harddisk.
+ */
+void ext2_free_inode (struct inode * inode)
+{
+ struct super_block * sb = inode->i_sb;
+ int is_directory;
+ unsigned long ino;
+ struct buffer_head * bh;
+ struct buffer_head * bh2;
+ unsigned long block_group;
+ unsigned long bit;
+ struct ext2_group_desc * desc;
+ struct ext2_super_block * es;
+
+ ino = inode->i_ino;
+ ext2_debug ("freeing inode %lu\n", ino);
+
+ /*
+ * Note: we must free any quota before locking the superblock,
+ * as writing the quota to disk may need the lock as well.
+ */
+ if (!is_bad_inode(inode)) {
+ /* Quota is already initialized in iput() */
+ DQUOT_FREE_INODE(inode);
+ DQUOT_DROP(inode);
+ }
+
+ lock_super (sb);
+ es = sb->u.ext2_sb.s_es;
+ is_directory = S_ISDIR(inode->i_mode);
+
+ /* Do this BEFORE marking the inode not in use or returning an error */
+ clear_inode (inode);
+
+ if (ino < EXT2_FIRST_INO(sb) ||
+ ino > le32_to_cpu(es->s_inodes_count)) {
+ ext2_error (sb, "ext2_free_inode",
+ "reserved or nonexistent inode %lu", ino);
+ goto error_return;
+ }
+ block_group = (ino - 1) / EXT2_INODES_PER_GROUP(sb);
+ bit = (ino - 1) % EXT2_INODES_PER_GROUP(sb);
+ bh = load_inode_bitmap (sb, block_group);
+ if (IS_ERR(bh))
+ goto error_return;
+
+ /* Ok, now we can actually update the inode bitmaps.. */
+ if (!ext2_clear_bit (bit, bh->b_data))
+ ext2_error (sb, "ext2_free_inode",
+ "bit already cleared for inode %lu", ino);
+ else {
+ desc = ext2_get_group_desc (sb, block_group, &bh2);
+ if (desc) {
+ desc->bg_free_inodes_count =
+ cpu_to_le16(le16_to_cpu(desc->bg_free_inodes_count) + 1);
+ if (is_directory)
+ desc->bg_used_dirs_count =
+ cpu_to_le16(le16_to_cpu(desc->bg_used_dirs_count) - 1);
+ }
+ mark_buffer_dirty(bh2);
+ es->s_free_inodes_count =
+ cpu_to_le32(le32_to_cpu(es->s_free_inodes_count) + 1);
+ mark_buffer_dirty(sb->u.ext2_sb.s_sbh);
+ }
+ mark_buffer_dirty(bh);
+ if (sb->s_flags & MS_SYNCHRONOUS) {
+ ll_rw_block (WRITE, 1, &bh);
+ wait_on_buffer (bh);
+ }
+ sb->s_dirt = 1;
+error_return:
+ unlock_super (sb);
+}
+
+/*
+ * There are two policies for allocating an inode. If the new inode is
+ * a directory, then a forward search is made for a block group with both
+ * free space and a low directory-to-inode ratio; if that fails, then of
+ * the groups with above-average free space, that group with the fewest
+ * directories already is chosen.
+ *
+ * For other inodes, search forward from the parent directory\'s block
+ * group to find a free inode.
+ */
+
+static int find_group_dir(struct super_block *sb, int parent_group)
+{
+ struct ext2_super_block * es = sb->u.ext2_sb.s_es;
+ int ngroups = sb->u.ext2_sb.s_groups_count;
+ int avefreei = le32_to_cpu(es->s_free_inodes_count) / ngroups;
+ struct ext2_group_desc *desc, *best_desc = NULL;
+ struct buffer_head *bh, *best_bh = NULL;
+ int group, best_group = -1;
+
+ for (group = 0; group < ngroups; group++) {
+ desc = ext2_get_group_desc (sb, group, &bh);
+ if (!desc || !desc->bg_free_inodes_count)
+ continue;
+ if (le16_to_cpu(desc->bg_free_inodes_count) < avefreei)
+ continue;
+ if (!best_desc ||
+ (le16_to_cpu(desc->bg_free_blocks_count) >
+ le16_to_cpu(best_desc->bg_free_blocks_count))) {
+ best_group = group;
+ best_desc = desc;
+ best_bh = bh;
+ }
+ }
+ if (!best_desc)
+ return -1;
+ best_desc->bg_free_inodes_count =
+ cpu_to_le16(le16_to_cpu(best_desc->bg_free_inodes_count) - 1);
+ best_desc->bg_used_dirs_count =
+ cpu_to_le16(le16_to_cpu(best_desc->bg_used_dirs_count) + 1);
+ mark_buffer_dirty(best_bh);
+ return best_group;
+}
+
+static int find_group_other(struct super_block *sb, int parent_group)
+{
+ int ngroups = sb->u.ext2_sb.s_groups_count;
+ struct ext2_group_desc *desc;
+ struct buffer_head *bh;
+ int group, i;
+
+ /*
+ * Try to place the inode in its parent directory
+ */
+ group = parent_group;
+ desc = ext2_get_group_desc (sb, group, &bh);
+ if (desc && le16_to_cpu(desc->bg_free_inodes_count))
+ goto found;
+
+ /*
+ * Use a quadratic hash to find a group with a
+ * free inode
+ */
+ for (i = 1; i < ngroups; i <<= 1) {
+ group += i;
+ if (group >= ngroups)
+ group -= ngroups;
+ desc = ext2_get_group_desc (sb, group, &bh);
+ if (desc && le16_to_cpu(desc->bg_free_inodes_count))
+ goto found;
+ }
+
+ /*
+ * That failed: try linear search for a free inode
+ */
+ group = parent_group + 1;
+ for (i = 2; i < ngroups; i++) {
+ if (++group >= ngroups)
+ group = 0;
+ desc = ext2_get_group_desc (sb, group, &bh);
+ if (desc && le16_to_cpu(desc->bg_free_inodes_count))
+ goto found;
+ }
+
+ return -1;
+
+found:
+ desc->bg_free_inodes_count =
+ cpu_to_le16(le16_to_cpu(desc->bg_free_inodes_count) - 1);
+ mark_buffer_dirty(bh);
+ return group;
+}
+
+struct inode * ext2_new_inode (const struct inode * dir, int mode)
+{
+ struct super_block * sb;
+ struct buffer_head * bh;
+ struct buffer_head * bh2;
+ int group, i;
+ ino_t ino;
+ struct inode * inode;
+ struct ext2_group_desc * desc;
+ struct ext2_super_block * es;
+ int err;
+
+ sb = dir->i_sb;
+ inode = new_inode(sb);
+ if (!inode)
+ return ERR_PTR(-ENOMEM);
+
+ lock_super (sb);
+ es = sb->u.ext2_sb.s_es;
+repeat:
+ if (S_ISDIR(mode))
+ group = find_group_dir(sb, dir->u.ext2_i.i_block_group);
+ else
+ group = find_group_other(sb, dir->u.ext2_i.i_block_group);
+
+ err = -ENOSPC;
+ if (group == -1)
+ goto fail;
+
+ err = -EIO;
+ bh = load_inode_bitmap (sb, group);
+ if (IS_ERR(bh))
+ goto fail2;
+
+ i = ext2_find_first_zero_bit ((unsigned long *) bh->b_data,
+ EXT2_INODES_PER_GROUP(sb));
+ if (i >= EXT2_INODES_PER_GROUP(sb))
+ goto bad_count;
+ ext2_set_bit (i, bh->b_data);
+
+ mark_buffer_dirty(bh);
+ if (sb->s_flags & MS_SYNCHRONOUS) {
+ ll_rw_block (WRITE, 1, &bh);
+ wait_on_buffer (bh);
+ }
+
+ ino = group * EXT2_INODES_PER_GROUP(sb) + i + 1;
+ if (ino < EXT2_FIRST_INO(sb) || ino > le32_to_cpu(es->s_inodes_count)) {
+ ext2_error (sb, "ext2_new_inode",
+ "reserved inode or inode > inodes count - "
+ "block_group = %d,inode=%ld", group, ino);
+ err = -EIO;
+ goto fail2;
+ }
+
+ es->s_free_inodes_count =
+ cpu_to_le32(le32_to_cpu(es->s_free_inodes_count) - 1);
+ mark_buffer_dirty(sb->u.ext2_sb.s_sbh);
+ sb->s_dirt = 1;
+ inode->i_uid = current->fsuid;
+ if (test_opt (sb, GRPID))
+ inode->i_gid = dir->i_gid;
+ else if (dir->i_mode & S_ISGID) {
+ inode->i_gid = dir->i_gid;
+ if (S_ISDIR(mode))
+ mode |= S_ISGID;
+ } else
+ inode->i_gid = current->fsgid;
+ inode->i_mode = mode;
+
+ inode->i_ino = ino;
+ inode->i_blksize = PAGE_SIZE; /* This is the optimal IO size (for stat), not the fs block size */
+ inode->i_blocks = 0;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+ inode->u.ext2_i.i_state = EXT2_STATE_NEW;
+ inode->u.ext2_i.i_flags = dir->u.ext2_i.i_flags & ~EXT2_BTREE_FL;
+ if (S_ISLNK(mode))
+ inode->u.ext2_i.i_flags &= ~(EXT2_IMMUTABLE_FL|EXT2_APPEND_FL);
+ inode->u.ext2_i.i_block_group = group;
+ ext2_set_inode_flags(inode);
+ insert_inode_hash(inode);
+ inode->i_generation = event++;
+ mark_inode_dirty(inode);
+
+ unlock_super (sb);
+ if(DQUOT_ALLOC_INODE(inode)) {
+ DQUOT_DROP(inode);
+ inode->i_flags |= S_NOQUOTA;
+ inode->i_nlink = 0;
+ iput(inode);
+ return ERR_PTR(-EDQUOT);
+ }
+ ext2_debug ("allocating inode %lu\n", inode->i_ino);
+ return inode;
+
+fail2:
+ desc = ext2_get_group_desc (sb, group, &bh2);
+ desc->bg_free_inodes_count =
+ cpu_to_le16(le16_to_cpu(desc->bg_free_inodes_count) + 1);
+ if (S_ISDIR(mode))
+ desc->bg_used_dirs_count =
+ cpu_to_le16(le16_to_cpu(desc->bg_used_dirs_count) - 1);
+ mark_buffer_dirty(bh2);
+fail:
+ unlock_super(sb);
+ make_bad_inode(inode);
+ iput(inode);
+ return ERR_PTR(err);
+
+bad_count:
+ ext2_error (sb, "ext2_new_inode",
+ "Free inodes count corrupted in group %d",
+ group);
+ /* Is it really ENOSPC? */
+ err = -ENOSPC;
+ if (sb->s_flags & MS_RDONLY)
+ goto fail;
+
+ desc = ext2_get_group_desc (sb, group, &bh2);
+ desc->bg_free_inodes_count = 0;
+ mark_buffer_dirty(bh2);
+ goto repeat;
+}
+
+unsigned long ext2_count_free_inodes (struct super_block * sb)
+{
+#ifdef EXT2FS_DEBUG
+ struct ext2_super_block * es;
+ unsigned long desc_count = 0, bitmap_count = 0;
+ int i;
+
+ lock_super (sb);
+ es = sb->u.ext2_sb.s_es;
+ for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) {
+ struct ext2_group_desc *desc = ext2_get_group_desc (sb, i, NULL);
+ struct buffer_head *bh;
+ unsigned x;
+
+ if (!desc)
+ continue;
+ desc_count += le16_to_cpu(desc->bg_free_inodes_count);
+ bh = load_inode_bitmap (sb, i);
+ if (IS_ERR(bh))
+ continue;
+
+ x = ext2_count_free (bh, EXT2_INODES_PER_GROUP(sb) / 8);
+ printk ("group %d: stored = %d, counted = %lu\n",
+ i, le16_to_cpu(desc->bg_free_inodes_count), x);
+ bitmap_count += x;
+ }
+ printk("ext2_count_free_inodes: stored = %lu, computed = %lu, %lu\n",
+ le32_to_cpu(es->s_free_inodes_count), desc_count, bitmap_count);
+ unlock_super (sb);
+ return desc_count;
+#else
+ return le32_to_cpu(sb->u.ext2_sb.s_es->s_free_inodes_count);
+#endif
+}
+
+#ifdef CONFIG_EXT2_CHECK
+/* Called at mount-time, super-block is locked */
+void ext2_check_inodes_bitmap (struct super_block * sb)
+{
+ struct ext2_super_block * es = sb->u.ext2_sb.s_es;
+ unsigned long desc_count = 0, bitmap_count = 0;
+ int i;
+
+ for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++) {
+ struct ext2_group_desc *desc = ext2_get_group_desc(sb, i, NULL);
+ struct buffer_head *bh;
+ unsigned x;
+
+ if (!desc)
+ continue;
+ desc_count += le16_to_cpu(desc->bg_free_inodes_count);
+ bh = load_inode_bitmap (sb, i);
+ if (IS_ERR(bh))
+ continue;
+
+ x = ext2_count_free (bh, EXT2_INODES_PER_GROUP(sb) / 8);
+ if (le16_to_cpu(desc->bg_free_inodes_count) != x)
+ ext2_error (sb, "ext2_check_inodes_bitmap",
+ "Wrong free inodes count in group %d, "
+ "stored = %d, counted = %lu", i,
+ le16_to_cpu(desc->bg_free_inodes_count), x);
+ bitmap_count += x;
+ }
+ if (le32_to_cpu(es->s_free_inodes_count) != bitmap_count)
+ ext2_error (sb, "ext2_check_inodes_bitmap",
+ "Wrong free inodes count in super block, "
+ "stored = %lu, counted = %lu",
+ (unsigned long)le32_to_cpu(es->s_free_inodes_count),
+ bitmap_count);
+}
+#endif
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
new file mode 100644
index 00000000000000..c9756a5251e9cf
--- /dev/null
+++ b/fs/ext2/inode.c
@@ -0,0 +1,1180 @@
+/*
+ * linux/fs/ext2/inode.c
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * from
+ *
+ * linux/fs/minix/inode.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * Goal-directed block allocation by Stephen Tweedie
+ * (sct@dcs.ed.ac.uk), 1993, 1998
+ * Big-endian to little-endian byte-swapping/bitmaps by
+ * David S. Miller (davem@caip.rutgers.edu), 1995
+ * 64-bit file support on 64-bit platforms by Jakub Jelinek
+ * (jj@sunsite.ms.mff.cuni.cz)
+ *
+ * Assorted race fixes, rewrite of ext2_get_block() by Al Viro, 2000
+ */
+
+#include <linux/fs.h>
+#include <linux/ext2_fs.h>
+#include <linux/locks.h>
+#include <linux/smp_lock.h>
+#include <linux/sched.h>
+#include <linux/highuid.h>
+#include <linux/quotaops.h>
+#include <linux/module.h>
+
+MODULE_AUTHOR("Remy Card and others");
+MODULE_DESCRIPTION("Second Extended Filesystem");
+MODULE_LICENSE("GPL");
+
+/*
+ * Test whether an inode is a fast symlink.
+ */
+static inline int ext2_inode_is_fast_symlink(struct inode *inode)
+{
+ int ea_blocks = inode->u.ext2_i.i_file_acl ?
+ (inode->i_sb->s_blocksize >> 9) : 0;
+
+ return (S_ISLNK(inode->i_mode) &&
+ inode->i_blocks - ea_blocks == 0);
+}
+
+static int ext2_update_inode(struct inode * inode, int do_sync);
+
+/*
+ * Called at each iput()
+ */
+void ext2_put_inode (struct inode * inode)
+{
+ ext2_discard_prealloc (inode);
+}
+
+/*
+ * Called at the last iput() if i_nlink is zero.
+ */
+void ext2_delete_inode (struct inode * inode)
+{
+ lock_kernel();
+
+ if (is_bad_inode(inode) ||
+ inode->i_ino == EXT2_ACL_IDX_INO ||
+ inode->i_ino == EXT2_ACL_DATA_INO)
+ goto no_delete;
+ inode->u.ext2_i.i_dtime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+ ext2_update_inode(inode, IS_SYNC(inode));
+ inode->i_size = 0;
+ if (inode->i_blocks)
+ ext2_truncate (inode);
+ ext2_free_inode (inode);
+
+ unlock_kernel();
+ return;
+no_delete:
+ unlock_kernel();
+ clear_inode(inode); /* We must guarantee clearing of inode... */
+}
+
+void ext2_discard_prealloc (struct inode * inode)
+{
+#ifdef EXT2_PREALLOCATE
+ lock_kernel();
+ /* Writer: ->i_prealloc* */
+ if (inode->u.ext2_i.i_prealloc_count) {
+ unsigned short total = inode->u.ext2_i.i_prealloc_count;
+ unsigned long block = inode->u.ext2_i.i_prealloc_block;
+ inode->u.ext2_i.i_prealloc_count = 0;
+ inode->u.ext2_i.i_prealloc_block = 0;
+ /* Writer: end */
+ ext2_free_blocks (inode, block, total);
+ }
+ unlock_kernel();
+#endif
+}
+
+static int ext2_alloc_block (struct inode * inode, unsigned long goal, int *err)
+{
+#ifdef EXT2FS_DEBUG
+ static unsigned long alloc_hits = 0, alloc_attempts = 0;
+#endif
+ unsigned long result;
+
+
+#ifdef EXT2_PREALLOCATE
+ /* Writer: ->i_prealloc* */
+ if (inode->u.ext2_i.i_prealloc_count &&
+ (goal == inode->u.ext2_i.i_prealloc_block ||
+ goal + 1 == inode->u.ext2_i.i_prealloc_block))
+ {
+ result = inode->u.ext2_i.i_prealloc_block++;
+ inode->u.ext2_i.i_prealloc_count--;
+ /* Writer: end */
+ ext2_debug ("preallocation hit (%lu/%lu).\n",
+ ++alloc_hits, ++alloc_attempts);
+ } else {
+ ext2_discard_prealloc (inode);
+ ext2_debug ("preallocation miss (%lu/%lu).\n",
+ alloc_hits, ++alloc_attempts);
+ if (S_ISREG(inode->i_mode))
+ result = ext2_new_block (inode, goal,
+ &inode->u.ext2_i.i_prealloc_count,
+ &inode->u.ext2_i.i_prealloc_block, err);
+ else
+ result = ext2_new_block (inode, goal, 0, 0, err);
+ }
+#else
+ result = ext2_new_block (inode, goal, 0, 0, err);
+#endif
+ return result;
+}
+
+typedef struct {
+ u32 *p;
+ u32 key;
+ struct buffer_head *bh;
+} Indirect;
+
+static inline void add_chain(Indirect *p, struct buffer_head *bh, u32 *v)
+{
+ p->key = *(p->p = v);
+ p->bh = bh;
+}
+
+static inline int verify_chain(Indirect *from, Indirect *to)
+{
+ while (from <= to && from->key == *from->p)
+ from++;
+ return (from > to);
+}
+
+/**
+ * ext2_block_to_path - parse the block number into array of offsets
+ * @inode: inode in question (we are only interested in its superblock)
+ * @i_block: block number to be parsed
+ * @offsets: array to store the offsets in
+ *
+ * To store the locations of file's data ext2 uses a data structure common
+ * for UNIX filesystems - tree of pointers anchored in the inode, with
+ * data blocks at leaves and indirect blocks in intermediate nodes.
+ * This function translates the block number into path in that tree -
+ * return value is the path length and @offsets[n] is the offset of
+ * pointer to (n+1)th node in the nth one. If @block is out of range
+ * (negative or too large) warning is printed and zero returned.
+ *
+ * Note: function doesn't find node addresses, so no IO is needed. All
+ * we need to know is the capacity of indirect blocks (taken from the
+ * inode->i_sb).
+ */
+
+/*
+ * Portability note: the last comparison (check that we fit into triple
+ * indirect block) is spelled differently, because otherwise on an
+ * architecture with 32-bit longs and 8Kb pages we might get into trouble
+ * if our filesystem had 8Kb blocks. We might use long long, but that would
+ * kill us on x86. Oh, well, at least the sign propagation does not matter -
+ * i_block would have to be negative in the very beginning, so we would not
+ * get there at all.
+ */
+
+static int ext2_block_to_path(struct inode *inode, long i_block, int offsets[4])
+{
+ int ptrs = EXT2_ADDR_PER_BLOCK(inode->i_sb);
+ int ptrs_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb);
+ const long direct_blocks = EXT2_NDIR_BLOCKS,
+ indirect_blocks = ptrs,
+ double_blocks = (1 << (ptrs_bits * 2));
+ int n = 0;
+
+ if (i_block < 0) {
+ ext2_warning (inode->i_sb, "ext2_block_to_path", "block < 0");
+ } else if (i_block < direct_blocks) {
+ offsets[n++] = i_block;
+ } else if ( (i_block -= direct_blocks) < indirect_blocks) {
+ offsets[n++] = EXT2_IND_BLOCK;
+ offsets[n++] = i_block;
+ } else if ((i_block -= indirect_blocks) < double_blocks) {
+ offsets[n++] = EXT2_DIND_BLOCK;
+ offsets[n++] = i_block >> ptrs_bits;
+ offsets[n++] = i_block & (ptrs - 1);
+ } else if (((i_block -= double_blocks) >> (ptrs_bits * 2)) < ptrs) {
+ offsets[n++] = EXT2_TIND_BLOCK;
+ offsets[n++] = i_block >> (ptrs_bits * 2);
+ offsets[n++] = (i_block >> ptrs_bits) & (ptrs - 1);
+ offsets[n++] = i_block & (ptrs - 1);
+ } else {
+ ext2_warning (inode->i_sb, "ext2_block_to_path", "block > big");
+ }
+ return n;
+}
+
+/**
+ * ext2_get_branch - read the chain of indirect blocks leading to data
+ * @inode: inode in question
+ * @depth: depth of the chain (1 - direct pointer, etc.)
+ * @offsets: offsets of pointers in inode/indirect blocks
+ * @chain: place to store the result
+ * @err: here we store the error value
+ *
+ * Function fills the array of triples <key, p, bh> and returns %NULL
+ * if everything went OK or the pointer to the last filled triple
+ * (incomplete one) otherwise. Upon the return chain[i].key contains
+ * the number of (i+1)-th block in the chain (as it is stored in memory,
+ * i.e. little-endian 32-bit), chain[i].p contains the address of that
+ * number (it points into struct inode for i==0 and into the bh->b_data
+ * for i>0) and chain[i].bh points to the buffer_head of i-th indirect
+ * block for i>0 and NULL for i==0. In other words, it holds the block
+ * numbers of the chain, addresses they were taken from (and where we can
+ * verify that chain did not change) and buffer_heads hosting these
+ * numbers.
+ *
+ * Function stops when it stumbles upon zero pointer (absent block)
+ * (pointer to last triple returned, *@err == 0)
+ * or when it gets an IO error reading an indirect block
+ * (ditto, *@err == -EIO)
+ * or when it notices that chain had been changed while it was reading
+ * (ditto, *@err == -EAGAIN)
+ * or when it reads all @depth-1 indirect blocks successfully and finds
+ * the whole chain, all way to the data (returns %NULL, *err == 0).
+ */
+static Indirect *ext2_get_branch(struct inode *inode,
+ int depth,
+ int *offsets,
+ Indirect chain[4],
+ int *err)
+{
+ struct super_block *sb = inode->i_sb;
+ Indirect *p = chain;
+ struct buffer_head *bh;
+
+ *err = 0;
+ /* i_data is not going away, no lock needed */
+ add_chain (chain, NULL, inode->u.ext2_i.i_data + *offsets);
+ if (!p->key)
+ goto no_block;
+ while (--depth) {
+ bh = sb_bread(sb, le32_to_cpu(p->key));
+ if (!bh)
+ goto failure;
+ /* Reader: pointers */
+ if (!verify_chain(chain, p))
+ goto changed;
+ add_chain(++p, bh, (u32*)bh->b_data + *++offsets);
+ /* Reader: end */
+ if (!p->key)
+ goto no_block;
+ }
+ return NULL;
+
+changed:
+ *err = -EAGAIN;
+ goto no_block;
+failure:
+ *err = -EIO;
+no_block:
+ return p;
+}
+
+/**
+ * ext2_find_near - find a place for allocation with sufficient locality
+ * @inode: owner
+ * @ind: descriptor of indirect block.
+ *
+ * This function returns the prefered place for block allocation.
+ * It is used when heuristic for sequential allocation fails.
+ * Rules are:
+ * + if there is a block to the left of our position - allocate near it.
+ * + if pointer will live in indirect block - allocate near that block.
+ * + if pointer will live in inode - allocate in the same cylinder group.
+ * Caller must make sure that @ind is valid and will stay that way.
+ */
+
+static inline unsigned long ext2_find_near(struct inode *inode, Indirect *ind)
+{
+ u32 *start = ind->bh ? (u32*) ind->bh->b_data : inode->u.ext2_i.i_data;
+ u32 *p;
+
+ /* Try to find previous block */
+ for (p = ind->p - 1; p >= start; p--)
+ if (*p)
+ return le32_to_cpu(*p);
+
+ /* No such thing, so let's try location of indirect block */
+ if (ind->bh)
+ return ind->bh->b_blocknr;
+
+ /*
+ * It is going to be refered from inode itself? OK, just put it into
+ * the same cylinder group then.
+ */
+ return (inode->u.ext2_i.i_block_group *
+ EXT2_BLOCKS_PER_GROUP(inode->i_sb)) +
+ le32_to_cpu(inode->i_sb->u.ext2_sb.s_es->s_first_data_block);
+}
+
+/**
+ * ext2_find_goal - find a prefered place for allocation.
+ * @inode: owner
+ * @block: block we want
+ * @chain: chain of indirect blocks
+ * @partial: pointer to the last triple within a chain
+ * @goal: place to store the result.
+ *
+ * Normally this function find the prefered place for block allocation,
+ * stores it in *@goal and returns zero. If the branch had been changed
+ * under us we return -EAGAIN.
+ */
+
+static inline int ext2_find_goal(struct inode *inode,
+ long block,
+ Indirect chain[4],
+ Indirect *partial,
+ unsigned long *goal)
+{
+ /* Writer: ->i_next_alloc* */
+ if (block == inode->u.ext2_i.i_next_alloc_block + 1) {
+ inode->u.ext2_i.i_next_alloc_block++;
+ inode->u.ext2_i.i_next_alloc_goal++;
+ }
+ /* Writer: end */
+ /* Reader: pointers, ->i_next_alloc* */
+ if (verify_chain(chain, partial)) {
+ /*
+ * try the heuristic for sequential allocation,
+ * failing that at least try to get decent locality.
+ */
+ if (block == inode->u.ext2_i.i_next_alloc_block)
+ *goal = inode->u.ext2_i.i_next_alloc_goal;
+ if (!*goal)
+ *goal = ext2_find_near(inode, partial);
+ return 0;
+ }
+ /* Reader: end */
+ return -EAGAIN;
+}
+
+/**
+ * ext2_alloc_branch - allocate and set up a chain of blocks.
+ * @inode: owner
+ * @num: depth of the chain (number of blocks to allocate)
+ * @offsets: offsets (in the blocks) to store the pointers to next.
+ * @branch: place to store the chain in.
+ *
+ * This function allocates @num blocks, zeroes out all but the last one,
+ * links them into chain and (if we are synchronous) writes them to disk.
+ * In other words, it prepares a branch that can be spliced onto the
+ * inode. It stores the information about that chain in the branch[], in
+ * the same format as ext2_get_branch() would do. We are calling it after
+ * we had read the existing part of chain and partial points to the last
+ * triple of that (one with zero ->key). Upon the exit we have the same
+ * picture as after the successful ext2_get_block(), excpet that in one
+ * place chain is disconnected - *branch->p is still zero (we did not
+ * set the last link), but branch->key contains the number that should
+ * be placed into *branch->p to fill that gap.
+ *
+ * If allocation fails we free all blocks we've allocated (and forget
+ * their buffer_heads) and return the error value the from failed
+ * ext2_alloc_block() (normally -ENOSPC). Otherwise we set the chain
+ * as described above and return 0.
+ */
+
+static int ext2_alloc_branch(struct inode *inode,
+ int num,
+ unsigned long goal,
+ int *offsets,
+ Indirect *branch)
+{
+ int blocksize = inode->i_sb->s_blocksize;
+ int n = 0;
+ int err;
+ int i;
+ int parent = ext2_alloc_block(inode, goal, &err);
+
+ branch[0].key = cpu_to_le32(parent);
+ if (parent) for (n = 1; n < num; n++) {
+ struct buffer_head *bh;
+ /* Allocate the next block */
+ int nr = ext2_alloc_block(inode, parent, &err);
+ if (!nr)
+ break;
+ branch[n].key = cpu_to_le32(nr);
+ /*
+ * Get buffer_head for parent block, zero it out and set
+ * the pointer to new one, then send parent to disk.
+ */
+ bh = sb_getblk(inode->i_sb, parent);
+ lock_buffer(bh);
+ memset(bh->b_data, 0, blocksize);
+ branch[n].bh = bh;
+ branch[n].p = (u32*) bh->b_data + offsets[n];
+ *branch[n].p = branch[n].key;
+ mark_buffer_uptodate(bh, 1);
+ unlock_buffer(bh);
+ mark_buffer_dirty_inode(bh, inode);
+ /* We used to sync bh here if IS_SYNC(inode).
+ * But for S_ISREG files we now rely upon generic_osync_inode()
+ * and b_inode_buffers
+ */
+ if (S_ISDIR(inode->i_mode) && IS_SYNC(inode)) {
+ ll_rw_block (WRITE, 1, &bh);
+ wait_on_buffer (bh);
+ }
+ parent = nr;
+ }
+ if (n == num)
+ return 0;
+
+ /* Allocation failed, free what we already allocated */
+ for (i = 1; i < n; i++)
+ bforget(branch[i].bh);
+ for (i = 0; i < n; i++)
+ ext2_free_blocks(inode, le32_to_cpu(branch[i].key), 1);
+ return err;
+}
+
+/**
+ * ext2_splice_branch - splice the allocated branch onto inode.
+ * @inode: owner
+ * @block: (logical) number of block we are adding
+ * @chain: chain of indirect blocks (with a missing link - see
+ * ext2_alloc_branch)
+ * @where: location of missing link
+ * @num: number of blocks we are adding
+ *
+ * This function verifies that chain (up to the missing link) had not
+ * changed, fills the missing link and does all housekeeping needed in
+ * inode (->i_blocks, etc.). In case of success we end up with the full
+ * chain to new block and return 0. Otherwise (== chain had been changed)
+ * we free the new blocks (forgetting their buffer_heads, indeed) and
+ * return -EAGAIN.
+ */
+
+static inline int ext2_splice_branch(struct inode *inode,
+ long block,
+ Indirect chain[4],
+ Indirect *where,
+ int num)
+{
+ int i;
+
+ /* Verify that place we are splicing to is still there and vacant */
+
+ /* Writer: pointers, ->i_next_alloc* */
+ if (!verify_chain(chain, where-1) || *where->p)
+ /* Writer: end */
+ goto changed;
+
+ /* That's it */
+
+ *where->p = where->key;
+ inode->u.ext2_i.i_next_alloc_block = block;
+ inode->u.ext2_i.i_next_alloc_goal = le32_to_cpu(where[num-1].key);
+
+ /* Writer: end */
+
+ /* We are done with atomic stuff, now do the rest of housekeeping */
+
+ inode->i_ctime = CURRENT_TIME;
+
+ /* had we spliced it onto indirect block? */
+ if (where->bh) {
+ mark_buffer_dirty_inode(where->bh, inode);
+ if (S_ISDIR(inode->i_mode) && IS_SYNC(inode)) {
+ ll_rw_block(WRITE, 1, &where->bh);
+ wait_on_buffer(where->bh);
+ }
+ }
+
+ mark_inode_dirty(inode);
+ return 0;
+
+changed:
+ for (i = 1; i < num; i++)
+ bforget(where[i].bh);
+ for (i = 0; i < num; i++)
+ ext2_free_blocks(inode, le32_to_cpu(where[i].key), 1);
+ return -EAGAIN;
+}
+
+/*
+ * Allocation strategy is simple: if we have to allocate something, we will
+ * have to go the whole way to leaf. So let's do it before attaching anything
+ * to tree, set linkage between the newborn blocks, write them if sync is
+ * required, recheck the path, free and repeat if check fails, otherwise
+ * set the last missing link (that will protect us from any truncate-generated
+ * removals - all blocks on the path are immune now) and possibly force the
+ * write on the parent block.
+ * That has a nice additional property: no special recovery from the failed
+ * allocations is needed - we simply release blocks and do not touch anything
+ * reachable from inode.
+ */
+
+static int ext2_get_block(struct inode *inode, long iblock, struct buffer_head *bh_result, int create)
+{
+ int err = -EIO;
+ int offsets[4];
+ Indirect chain[4];
+ Indirect *partial;
+ unsigned long goal;
+ int left;
+ int depth = ext2_block_to_path(inode, iblock, offsets);
+
+ if (depth == 0)
+ goto out;
+
+ lock_kernel();
+reread:
+ partial = ext2_get_branch(inode, depth, offsets, chain, &err);
+
+ /* Simplest case - block found, no allocation needed */
+ if (!partial) {
+got_it:
+ bh_result->b_dev = inode->i_dev;
+ bh_result->b_blocknr = le32_to_cpu(chain[depth-1].key);
+ bh_result->b_state |= (1UL << BH_Mapped);
+ /* Clean up and exit */
+ partial = chain+depth-1; /* the whole chain */
+ goto cleanup;
+ }
+
+ /* Next simple case - plain lookup or failed read of indirect block */
+ if (!create || err == -EIO) {
+cleanup:
+ while (partial > chain) {
+ brelse(partial->bh);
+ partial--;
+ }
+ unlock_kernel();
+out:
+ return err;
+ }
+
+ /*
+ * Indirect block might be removed by truncate while we were
+ * reading it. Handling of that case (forget what we've got and
+ * reread) is taken out of the main path.
+ */
+ if (err == -EAGAIN)
+ goto changed;
+
+ goal = 0;
+ if (ext2_find_goal(inode, iblock, chain, partial, &goal) < 0)
+ goto changed;
+
+ left = (chain + depth) - partial;
+ err = ext2_alloc_branch(inode, left, goal,
+ offsets+(partial-chain), partial);
+ if (err)
+ goto cleanup;
+
+ if (ext2_splice_branch(inode, iblock, chain, partial, left) < 0)
+ goto changed;
+
+ bh_result->b_state |= (1UL << BH_New);
+ goto got_it;
+
+changed:
+ while (partial > chain) {
+ brelse(partial->bh);
+ partial--;
+ }
+ goto reread;
+}
+
+static int ext2_writepage(struct page *page)
+{
+ return block_write_full_page(page,ext2_get_block);
+}
+static int ext2_readpage(struct file *file, struct page *page)
+{
+ return block_read_full_page(page,ext2_get_block);
+}
+static int ext2_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
+{
+ return block_prepare_write(page,from,to,ext2_get_block);
+}
+static int ext2_bmap(struct address_space *mapping, long block)
+{
+ return generic_block_bmap(mapping,block,ext2_get_block);
+}
+static int ext2_direct_IO(int rw, struct inode * inode, struct kiobuf * iobuf, unsigned long blocknr, int blocksize)
+{
+ return generic_direct_IO(rw, inode, iobuf, blocknr, blocksize, ext2_get_block);
+}
+struct address_space_operations ext2_aops = {
+ readpage: ext2_readpage,
+ writepage: ext2_writepage,
+ sync_page: block_sync_page,
+ prepare_write: ext2_prepare_write,
+ commit_write: generic_commit_write,
+ bmap: ext2_bmap,
+ direct_IO: ext2_direct_IO,
+};
+
+/*
+ * Probably it should be a library function... search for first non-zero word
+ * or memcmp with zero_page, whatever is better for particular architecture.
+ * Linus?
+ */
+static inline int all_zeroes(u32 *p, u32 *q)
+{
+ while (p < q)
+ if (*p++)
+ return 0;
+ return 1;
+}
+
+/**
+ * ext2_find_shared - find the indirect blocks for partial truncation.
+ * @inode: inode in question
+ * @depth: depth of the affected branch
+ * @offsets: offsets of pointers in that branch (see ext2_block_to_path)
+ * @chain: place to store the pointers to partial indirect blocks
+ * @top: place to the (detached) top of branch
+ *
+ * This is a helper function used by ext2_truncate().
+ *
+ * When we do truncate() we may have to clean the ends of several indirect
+ * blocks but leave the blocks themselves alive. Block is partially
+ * truncated if some data below the new i_size is refered from it (and
+ * it is on the path to the first completely truncated data block, indeed).
+ * We have to free the top of that path along with everything to the right
+ * of the path. Since no allocation past the truncation point is possible
+ * until ext2_truncate() finishes, we may safely do the latter, but top
+ * of branch may require special attention - pageout below the truncation
+ * point might try to populate it.
+ *
+ * We atomically detach the top of branch from the tree, store the block
+ * number of its root in *@top, pointers to buffer_heads of partially
+ * truncated blocks - in @chain[].bh and pointers to their last elements
+ * that should not be removed - in @chain[].p. Return value is the pointer
+ * to last filled element of @chain.
+ *
+ * The work left to caller to do the actual freeing of subtrees:
+ * a) free the subtree starting from *@top
+ * b) free the subtrees whose roots are stored in
+ * (@chain[i].p+1 .. end of @chain[i].bh->b_data)
+ * c) free the subtrees growing from the inode past the @chain[0].p
+ * (no partially truncated stuff there).
+ */
+
+static Indirect *ext2_find_shared(struct inode *inode,
+ int depth,
+ int offsets[4],
+ Indirect chain[4],
+ u32 *top)
+{
+ Indirect *partial, *p;
+ int k, err;
+
+ *top = 0;
+ for (k = depth; k > 1 && !offsets[k-1]; k--)
+ ;
+ partial = ext2_get_branch(inode, k, offsets, chain, &err);
+ /* Writer: pointers */
+ if (!partial)
+ partial = chain + k-1;
+ /*
+ * If the branch acquired continuation since we've looked at it -
+ * fine, it should all survive and (new) top doesn't belong to us.
+ */
+ if (!partial->key && *partial->p)
+ /* Writer: end */
+ goto no_top;
+ for (p=partial; p>chain && all_zeroes((u32*)p->bh->b_data,p->p); p--)
+ ;
+ /*
+ * OK, we've found the last block that must survive. The rest of our
+ * branch should be detached before unlocking. However, if that rest
+ * of branch is all ours and does not grow immediately from the inode
+ * it's easier to cheat and just decrement partial->p.
+ */
+ if (p == chain + k - 1 && p > chain) {
+ p->p--;
+ } else {
+ *top = *p->p;
+ *p->p = 0;
+ }
+ /* Writer: end */
+
+ while(partial > p)
+ {
+ brelse(partial->bh);
+ partial--;
+ }
+no_top:
+ return partial;
+}
+
+/**
+ * ext2_free_data - free a list of data blocks
+ * @inode: inode we are dealing with
+ * @p: array of block numbers
+ * @q: points immediately past the end of array
+ *
+ * We are freeing all blocks refered from that array (numbers are
+ * stored as little-endian 32-bit) and updating @inode->i_blocks
+ * appropriately.
+ */
+static inline void ext2_free_data(struct inode *inode, u32 *p, u32 *q)
+{
+ unsigned long block_to_free = 0, count = 0;
+ unsigned long nr;
+
+ for ( ; p < q ; p++) {
+ nr = le32_to_cpu(*p);
+ if (nr) {
+ *p = 0;
+ /* accumulate blocks to free if they're contiguous */
+ if (count == 0)
+ goto free_this;
+ else if (block_to_free == nr - count)
+ count++;
+ else {
+ mark_inode_dirty(inode);
+ ext2_free_blocks (inode, block_to_free, count);
+ free_this:
+ block_to_free = nr;
+ count = 1;
+ }
+ }
+ }
+ if (count > 0) {
+ mark_inode_dirty(inode);
+ ext2_free_blocks (inode, block_to_free, count);
+ }
+}
+
+/**
+ * ext2_free_branches - free an array of branches
+ * @inode: inode we are dealing with
+ * @p: array of block numbers
+ * @q: pointer immediately past the end of array
+ * @depth: depth of the branches to free
+ *
+ * We are freeing all blocks refered from these branches (numbers are
+ * stored as little-endian 32-bit) and updating @inode->i_blocks
+ * appropriately.
+ */
+static void ext2_free_branches(struct inode *inode, u32 *p, u32 *q, int depth)
+{
+ struct buffer_head * bh;
+ unsigned long nr;
+
+ if (depth--) {
+ int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
+ for ( ; p < q ; p++) {
+ nr = le32_to_cpu(*p);
+ if (!nr)
+ continue;
+ *p = 0;
+ bh = sb_bread(inode->i_sb, nr);
+ /*
+ * A read failure? Report error and clear slot
+ * (should be rare).
+ */
+ if (!bh) {
+ ext2_error(inode->i_sb, "ext2_free_branches",
+ "Read failure, inode=%ld, block=%ld",
+ inode->i_ino, nr);
+ continue;
+ }
+ ext2_free_branches(inode,
+ (u32*)bh->b_data,
+ (u32*)bh->b_data + addr_per_block,
+ depth);
+ bforget(bh);
+ ext2_free_blocks(inode, nr, 1);
+ mark_inode_dirty(inode);
+ }
+ } else
+ ext2_free_data(inode, p, q);
+}
+
+void ext2_truncate (struct inode * inode)
+{
+ u32 *i_data = inode->u.ext2_i.i_data;
+ int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
+ int offsets[4];
+ Indirect chain[4];
+ Indirect *partial;
+ int nr = 0;
+ int n;
+ long iblock;
+ unsigned blocksize;
+
+ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+ S_ISLNK(inode->i_mode)))
+ return;
+ if (ext2_inode_is_fast_symlink(inode))
+ return;
+ if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
+ return;
+
+ ext2_discard_prealloc(inode);
+
+ blocksize = inode->i_sb->s_blocksize;
+ iblock = (inode->i_size + blocksize-1)
+ >> EXT2_BLOCK_SIZE_BITS(inode->i_sb);
+
+ block_truncate_page(inode->i_mapping, inode->i_size, ext2_get_block);
+
+ n = ext2_block_to_path(inode, iblock, offsets);
+ if (n == 0)
+ return;
+
+ if (n == 1) {
+ ext2_free_data(inode, i_data+offsets[0],
+ i_data + EXT2_NDIR_BLOCKS);
+ goto do_indirects;
+ }
+
+ partial = ext2_find_shared(inode, n, offsets, chain, &nr);
+ /* Kill the top of shared branch (already detached) */
+ if (nr) {
+ if (partial == chain)
+ mark_inode_dirty(inode);
+ else
+ mark_buffer_dirty_inode(partial->bh, inode);
+ ext2_free_branches(inode, &nr, &nr+1, (chain+n-1) - partial);
+ }
+ /* Clear the ends of indirect blocks on the shared branch */
+ while (partial > chain) {
+ ext2_free_branches(inode,
+ partial->p + 1,
+ (u32*)partial->bh->b_data + addr_per_block,
+ (chain+n-1) - partial);
+ mark_buffer_dirty_inode(partial->bh, inode);
+ brelse (partial->bh);
+ partial--;
+ }
+do_indirects:
+ /* Kill the remaining (whole) subtrees */
+ switch (offsets[0]) {
+ default:
+ nr = i_data[EXT2_IND_BLOCK];
+ if (nr) {
+ i_data[EXT2_IND_BLOCK] = 0;
+ mark_inode_dirty(inode);
+ ext2_free_branches(inode, &nr, &nr+1, 1);
+ }
+ case EXT2_IND_BLOCK:
+ nr = i_data[EXT2_DIND_BLOCK];
+ if (nr) {
+ i_data[EXT2_DIND_BLOCK] = 0;
+ mark_inode_dirty(inode);
+ ext2_free_branches(inode, &nr, &nr+1, 2);
+ }
+ case EXT2_DIND_BLOCK:
+ nr = i_data[EXT2_TIND_BLOCK];
+ if (nr) {
+ i_data[EXT2_TIND_BLOCK] = 0;
+ mark_inode_dirty(inode);
+ ext2_free_branches(inode, &nr, &nr+1, 3);
+ }
+ case EXT2_TIND_BLOCK:
+ ;
+ }
+ inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ if (IS_SYNC(inode)) {
+ fsync_inode_buffers(inode);
+ ext2_sync_inode (inode);
+ } else {
+ mark_inode_dirty(inode);
+ }
+}
+
+void ext2_set_inode_flags(struct inode *inode)
+{
+ unsigned int flags = inode->u.ext2_i.i_flags;
+
+ inode->i_flags &= ~(S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME);
+ if (flags & EXT2_SYNC_FL)
+ inode->i_flags |= S_SYNC;
+ if (flags & EXT2_APPEND_FL)
+ inode->i_flags |= S_APPEND;
+ if (flags & EXT2_IMMUTABLE_FL)
+ inode->i_flags |= S_IMMUTABLE;
+ if (flags & EXT2_NOATIME_FL)
+ inode->i_flags |= S_NOATIME;
+}
+
+void ext2_read_inode (struct inode * inode)
+{
+ struct buffer_head * bh;
+ struct ext2_inode * raw_inode;
+ unsigned long block_group;
+ unsigned long group_desc;
+ unsigned long desc;
+ unsigned long block;
+ unsigned long offset;
+ struct ext2_group_desc * gdp;
+
+ if ((inode->i_ino != EXT2_ROOT_INO && inode->i_ino != EXT2_ACL_IDX_INO &&
+ inode->i_ino != EXT2_ACL_DATA_INO &&
+ inode->i_ino < EXT2_FIRST_INO(inode->i_sb)) ||
+ inode->i_ino > le32_to_cpu(inode->i_sb->u.ext2_sb.s_es->s_inodes_count)) {
+ ext2_error (inode->i_sb, "ext2_read_inode",
+ "bad inode number: %lu", inode->i_ino);
+ goto bad_inode;
+ }
+ block_group = (inode->i_ino - 1) / EXT2_INODES_PER_GROUP(inode->i_sb);
+ if (block_group >= inode->i_sb->u.ext2_sb.s_groups_count) {
+ ext2_error (inode->i_sb, "ext2_read_inode",
+ "group >= groups count");
+ goto bad_inode;
+ }
+ group_desc = block_group >> EXT2_DESC_PER_BLOCK_BITS(inode->i_sb);
+ desc = block_group & (EXT2_DESC_PER_BLOCK(inode->i_sb) - 1);
+ bh = inode->i_sb->u.ext2_sb.s_group_desc[group_desc];
+ if (!bh) {
+ ext2_error (inode->i_sb, "ext2_read_inode",
+ "Descriptor not loaded");
+ goto bad_inode;
+ }
+
+ gdp = (struct ext2_group_desc *) bh->b_data;
+ /*
+ * Figure out the offset within the block group inode table
+ */
+ offset = ((inode->i_ino - 1) % EXT2_INODES_PER_GROUP(inode->i_sb)) *
+ EXT2_INODE_SIZE(inode->i_sb);
+ block = le32_to_cpu(gdp[desc].bg_inode_table) +
+ (offset >> EXT2_BLOCK_SIZE_BITS(inode->i_sb));
+ if (!(bh = sb_bread(inode->i_sb, block))) {
+ ext2_error (inode->i_sb, "ext2_read_inode",
+ "unable to read inode block - "
+ "inode=%lu, block=%lu", inode->i_ino, block);
+ goto bad_inode;
+ }
+ offset &= (EXT2_BLOCK_SIZE(inode->i_sb) - 1);
+ raw_inode = (struct ext2_inode *) (bh->b_data + offset);
+
+ inode->i_mode = le16_to_cpu(raw_inode->i_mode);
+ inode->i_uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low);
+ inode->i_gid = (gid_t)le16_to_cpu(raw_inode->i_gid_low);
+ if(!(test_opt (inode->i_sb, NO_UID32))) {
+ inode->i_uid |= le16_to_cpu(raw_inode->i_uid_high) << 16;
+ inode->i_gid |= le16_to_cpu(raw_inode->i_gid_high) << 16;
+ }
+ inode->i_nlink = le16_to_cpu(raw_inode->i_links_count);
+ inode->i_size = le32_to_cpu(raw_inode->i_size);
+ inode->i_atime = le32_to_cpu(raw_inode->i_atime);
+ inode->i_ctime = le32_to_cpu(raw_inode->i_ctime);
+ inode->i_mtime = le32_to_cpu(raw_inode->i_mtime);
+ inode->u.ext2_i.i_dtime = le32_to_cpu(raw_inode->i_dtime);
+ /* We now have enough fields to check if the inode was active or not.
+ * This is needed because nfsd might try to access dead inodes
+ * the test is that same one that e2fsck uses
+ * NeilBrown 1999oct15
+ */
+ if (inode->i_nlink == 0 && (inode->i_mode == 0 || inode->u.ext2_i.i_dtime)) {
+ /* this inode is deleted */
+ brelse (bh);
+ goto bad_inode;
+ }
+ inode->i_blksize = PAGE_SIZE; /* This is the optimal IO size (for stat), not the fs block size */
+ inode->i_blocks = le32_to_cpu(raw_inode->i_blocks);
+ inode->i_version = ++event;
+ inode->u.ext2_i.i_flags = le32_to_cpu(raw_inode->i_flags);
+ inode->u.ext2_i.i_faddr = le32_to_cpu(raw_inode->i_faddr);
+ inode->u.ext2_i.i_frag_no = raw_inode->i_frag;
+ inode->u.ext2_i.i_frag_size = raw_inode->i_fsize;
+ inode->u.ext2_i.i_file_acl = le32_to_cpu(raw_inode->i_file_acl);
+ if (S_ISREG(inode->i_mode))
+ inode->i_size |= ((__u64)le32_to_cpu(raw_inode->i_size_high)) << 32;
+ else
+ inode->u.ext2_i.i_dir_acl = le32_to_cpu(raw_inode->i_dir_acl);
+ inode->i_generation = le32_to_cpu(raw_inode->i_generation);
+ inode->u.ext2_i.i_state = 0;
+ inode->u.ext2_i.i_prealloc_count = 0;
+ inode->u.ext2_i.i_block_group = block_group;
+
+ /*
+ * NOTE! The in-memory inode i_data array is in little-endian order
+ * even on big-endian machines: we do NOT byteswap the block numbers!
+ */
+ for (block = 0; block < EXT2_N_BLOCKS; block++)
+ inode->u.ext2_i.i_data[block] = raw_inode->i_block[block];
+
+ if (inode->i_ino == EXT2_ACL_IDX_INO ||
+ inode->i_ino == EXT2_ACL_DATA_INO)
+ /* Nothing to do */ ;
+ else if (S_ISREG(inode->i_mode)) {
+ inode->i_op = &ext2_file_inode_operations;
+ inode->i_fop = &ext2_file_operations;
+ inode->i_mapping->a_ops = &ext2_aops;
+ } else if (S_ISDIR(inode->i_mode)) {
+ inode->i_op = &ext2_dir_inode_operations;
+ inode->i_fop = &ext2_dir_operations;
+ inode->i_mapping->a_ops = &ext2_aops;
+ } else if (S_ISLNK(inode->i_mode)) {
+ if (ext2_inode_is_fast_symlink(inode))
+ inode->i_op = &ext2_fast_symlink_inode_operations;
+ else {
+ inode->i_op = &page_symlink_inode_operations;
+ inode->i_mapping->a_ops = &ext2_aops;
+ }
+ } else
+ init_special_inode(inode, inode->i_mode,
+ le32_to_cpu(raw_inode->i_block[0]));
+ brelse (bh);
+ inode->i_attr_flags = 0;
+ ext2_set_inode_flags(inode);
+ return;
+
+bad_inode:
+ make_bad_inode(inode);
+ return;
+}
+
+static int ext2_update_inode(struct inode * inode, int do_sync)
+{
+ struct buffer_head * bh;
+ struct ext2_inode * raw_inode;
+ unsigned long block_group;
+ unsigned long group_desc;
+ unsigned long desc;
+ unsigned long block;
+ unsigned long offset;
+ int err = 0;
+ struct ext2_group_desc * gdp;
+
+ if ((inode->i_ino != EXT2_ROOT_INO &&
+ inode->i_ino < EXT2_FIRST_INO(inode->i_sb)) ||
+ inode->i_ino > le32_to_cpu(inode->i_sb->u.ext2_sb.s_es->s_inodes_count)) {
+ ext2_error (inode->i_sb, "ext2_write_inode",
+ "bad inode number: %lu", inode->i_ino);
+ return -EIO;
+ }
+ block_group = (inode->i_ino - 1) / EXT2_INODES_PER_GROUP(inode->i_sb);
+ if (block_group >= inode->i_sb->u.ext2_sb.s_groups_count) {
+ ext2_error (inode->i_sb, "ext2_write_inode",
+ "group >= groups count");
+ return -EIO;
+ }
+ group_desc = block_group >> EXT2_DESC_PER_BLOCK_BITS(inode->i_sb);
+ desc = block_group & (EXT2_DESC_PER_BLOCK(inode->i_sb) - 1);
+ bh = inode->i_sb->u.ext2_sb.s_group_desc[group_desc];
+ if (!bh) {
+ ext2_error (inode->i_sb, "ext2_write_inode",
+ "Descriptor not loaded");
+ return -EIO;
+ }
+ gdp = (struct ext2_group_desc *) bh->b_data;
+ /*
+ * Figure out the offset within the block group inode table
+ */
+ offset = ((inode->i_ino - 1) % EXT2_INODES_PER_GROUP(inode->i_sb)) *
+ EXT2_INODE_SIZE(inode->i_sb);
+ block = le32_to_cpu(gdp[desc].bg_inode_table) +
+ (offset >> EXT2_BLOCK_SIZE_BITS(inode->i_sb));
+ if (!(bh = sb_bread(inode->i_sb, block))) {
+ ext2_error (inode->i_sb, "ext2_write_inode",
+ "unable to read inode block - "
+ "inode=%lu, block=%lu", inode->i_ino, block);
+ return -EIO;
+ }
+ offset &= EXT2_BLOCK_SIZE(inode->i_sb) - 1;
+ raw_inode = (struct ext2_inode *) (bh->b_data + offset);
+
+ /* For fields not tracked in the in-memory inode,
+ * initialise them to zero for new inodes. */
+ if (inode->u.ext2_i.i_state & EXT2_STATE_NEW)
+ memset(raw_inode, 0, EXT2_SB(inode->i_sb)->s_inode_size);
+
+ raw_inode->i_mode = cpu_to_le16(inode->i_mode);
+ if(!(test_opt(inode->i_sb, NO_UID32))) {
+ raw_inode->i_uid_low = cpu_to_le16(low_16_bits(inode->i_uid));
+ raw_inode->i_gid_low = cpu_to_le16(low_16_bits(inode->i_gid));
+/*
+ * Fix up interoperability with old kernels. Otherwise, old inodes get
+ * re-used with the upper 16 bits of the uid/gid intact
+ */
+ if(!inode->u.ext2_i.i_dtime) {
+ raw_inode->i_uid_high = cpu_to_le16(high_16_bits(inode->i_uid));
+ raw_inode->i_gid_high = cpu_to_le16(high_16_bits(inode->i_gid));
+ } else {
+ raw_inode->i_uid_high = 0;
+ raw_inode->i_gid_high = 0;
+ }
+ } else {
+ raw_inode->i_uid_low = cpu_to_le16(fs_high2lowuid(inode->i_uid));
+ raw_inode->i_gid_low = cpu_to_le16(fs_high2lowgid(inode->i_gid));
+ raw_inode->i_uid_high = 0;
+ raw_inode->i_gid_high = 0;
+ }
+ raw_inode->i_links_count = cpu_to_le16(inode->i_nlink);
+ raw_inode->i_size = cpu_to_le32(inode->i_size);
+ raw_inode->i_atime = cpu_to_le32(inode->i_atime);
+ raw_inode->i_ctime = cpu_to_le32(inode->i_ctime);
+ raw_inode->i_mtime = cpu_to_le32(inode->i_mtime);
+ raw_inode->i_blocks = cpu_to_le32(inode->i_blocks);
+ raw_inode->i_dtime = cpu_to_le32(inode->u.ext2_i.i_dtime);
+ raw_inode->i_flags = cpu_to_le32(inode->u.ext2_i.i_flags);
+ raw_inode->i_faddr = cpu_to_le32(inode->u.ext2_i.i_faddr);
+ raw_inode->i_frag = inode->u.ext2_i.i_frag_no;
+ raw_inode->i_fsize = inode->u.ext2_i.i_frag_size;
+ raw_inode->i_file_acl = cpu_to_le32(inode->u.ext2_i.i_file_acl);
+ if (!S_ISREG(inode->i_mode))
+ raw_inode->i_dir_acl = cpu_to_le32(inode->u.ext2_i.i_dir_acl);
+ else {
+ raw_inode->i_size_high = cpu_to_le32(inode->i_size >> 32);
+ if (inode->i_size > 0x7fffffffULL) {
+ struct super_block *sb = inode->i_sb;
+ if (!EXT2_HAS_RO_COMPAT_FEATURE(sb,
+ EXT2_FEATURE_RO_COMPAT_LARGE_FILE) ||
+ EXT2_SB(sb)->s_es->s_rev_level ==
+ cpu_to_le32(EXT2_GOOD_OLD_REV)) {
+ /* If this is the first large file
+ * created, add a flag to the superblock.
+ */
+ lock_kernel();
+ ext2_update_dynamic_rev(sb);
+ EXT2_SET_RO_COMPAT_FEATURE(sb,
+ EXT2_FEATURE_RO_COMPAT_LARGE_FILE);
+ unlock_kernel();
+ ext2_write_super(sb);
+ }
+ }
+ }
+
+ raw_inode->i_generation = cpu_to_le32(inode->i_generation);
+ if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+ raw_inode->i_block[0] = cpu_to_le32(kdev_t_to_nr(inode->i_rdev));
+ else for (block = 0; block < EXT2_N_BLOCKS; block++)
+ raw_inode->i_block[block] = inode->u.ext2_i.i_data[block];
+ mark_buffer_dirty(bh);
+ if (do_sync) {
+ ll_rw_block (WRITE, 1, &bh);
+ wait_on_buffer (bh);
+ if (buffer_req(bh) && !buffer_uptodate(bh)) {
+ printk ("IO error syncing ext2 inode ["
+ "%s:%08lx]\n",
+ bdevname(inode->i_dev), inode->i_ino);
+ err = -EIO;
+ }
+ }
+ inode->u.ext2_i.i_state &= ~EXT2_STATE_NEW;
+ brelse (bh);
+ return err;
+}
+
+void ext2_write_inode (struct inode * inode, int wait)
+{
+ lock_kernel();
+ ext2_update_inode (inode, wait);
+ unlock_kernel();
+}
+
+int ext2_sync_inode (struct inode *inode)
+{
+ return ext2_update_inode (inode, 1);
+}
diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c
new file mode 100644
index 00000000000000..0b4e80a3bc0760
--- /dev/null
+++ b/fs/ext2/ioctl.c
@@ -0,0 +1,76 @@
+/*
+ * linux/fs/ext2/ioctl.c
+ *
+ * Copyright (C) 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ */
+
+#include <linux/fs.h>
+#include <linux/ext2_fs.h>
+#include <linux/sched.h>
+#include <asm/uaccess.h>
+
+
+int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
+ unsigned long arg)
+{
+ unsigned int flags;
+
+ ext2_debug ("cmd = %u, arg = %lu\n", cmd, arg);
+
+ switch (cmd) {
+ case EXT2_IOC_GETFLAGS:
+ flags = inode->u.ext2_i.i_flags & EXT2_FL_USER_VISIBLE;
+ return put_user(flags, (int *) arg);
+ case EXT2_IOC_SETFLAGS: {
+ unsigned int oldflags;
+
+ if (IS_RDONLY(inode))
+ return -EROFS;
+
+ if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
+ return -EACCES;
+
+ if (get_user(flags, (int *) arg))
+ return -EFAULT;
+
+ oldflags = inode->u.ext2_i.i_flags;
+
+ /*
+ * The IMMUTABLE and APPEND_ONLY flags can only be changed by
+ * the relevant capability.
+ *
+ * This test looks nicer. Thanks to Pauline Middelink
+ */
+ if ((flags ^ oldflags) & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL)) {
+ if (!capable(CAP_LINUX_IMMUTABLE))
+ return -EPERM;
+ }
+
+ flags = flags & EXT2_FL_USER_MODIFIABLE;
+ flags |= oldflags & ~EXT2_FL_USER_MODIFIABLE;
+ inode->u.ext2_i.i_flags = flags;
+
+ ext2_set_inode_flags(inode);
+ inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+ return 0;
+ }
+ case EXT2_IOC_GETVERSION:
+ return put_user(inode->i_generation, (int *) arg);
+ case EXT2_IOC_SETVERSION:
+ if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
+ return -EPERM;
+ if (IS_RDONLY(inode))
+ return -EROFS;
+ if (get_user(inode->i_generation, (int *) arg))
+ return -EFAULT;
+ inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+ return 0;
+ default:
+ return -ENOTTY;
+ }
+}
diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c
new file mode 100644
index 00000000000000..364deca4f89028
--- /dev/null
+++ b/fs/ext2/namei.c
@@ -0,0 +1,348 @@
+/*
+ * linux/fs/ext2/namei.c
+ *
+ * Rewrite to pagecache. Almost all code had been changed, so blame me
+ * if the things go wrong. Please, send bug reports to viro@math.psu.edu
+ *
+ * Stuff here is basically a glue between the VFS and generic UNIXish
+ * filesystem that keeps everything in pagecache. All knowledge of the
+ * directory layout is in fs/ext2/dir.c - it turned out to be easily separatable
+ * and it's easier to debug that way. In principle we might want to
+ * generalize that a bit and turn it into a library. Or not.
+ *
+ * The only non-static object here is ext2_dir_inode_operations.
+ *
+ * TODO: get rid of kmap() use, add readahead.
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * from
+ *
+ * linux/fs/minix/namei.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * Big-endian to little-endian byte-swapping/bitmaps by
+ * David S. Miller (davem@caip.rutgers.edu), 1995
+ */
+
+#include <linux/fs.h>
+#include <linux/ext2_fs.h>
+#include <linux/pagemap.h>
+
+/*
+ * Couple of helper functions - make the code slightly cleaner.
+ */
+
+static inline void ext2_inc_count(struct inode *inode)
+{
+ inode->i_nlink++;
+ mark_inode_dirty(inode);
+}
+
+static inline void ext2_dec_count(struct inode *inode)
+{
+ inode->i_nlink--;
+ mark_inode_dirty(inode);
+}
+
+static inline int ext2_add_nondir(struct dentry *dentry, struct inode *inode)
+{
+ int err = ext2_add_link(dentry, inode);
+ if (!err) {
+ d_instantiate(dentry, inode);
+ return 0;
+ }
+ ext2_dec_count(inode);
+ iput(inode);
+ return err;
+}
+
+/*
+ * Methods themselves.
+ */
+
+static struct dentry *ext2_lookup(struct inode * dir, struct dentry *dentry)
+{
+ struct inode * inode;
+ ino_t ino;
+
+ if (dentry->d_name.len > EXT2_NAME_LEN)
+ return ERR_PTR(-ENAMETOOLONG);
+
+ ino = ext2_inode_by_name(dir, dentry);
+ inode = NULL;
+ if (ino) {
+ inode = iget(dir->i_sb, ino);
+ if (!inode)
+ return ERR_PTR(-EACCES);
+ }
+ d_add(dentry, inode);
+ return NULL;
+}
+
+/*
+ * By the time this is called, we already have created
+ * the directory cache entry for the new file, but it
+ * is so far negative - it has no inode.
+ *
+ * If the create succeeds, we fill in the inode information
+ * with d_instantiate().
+ */
+static int ext2_create (struct inode * dir, struct dentry * dentry, int mode)
+{
+ struct inode * inode = ext2_new_inode (dir, mode);
+ int err = PTR_ERR(inode);
+ if (!IS_ERR(inode)) {
+ inode->i_op = &ext2_file_inode_operations;
+ inode->i_fop = &ext2_file_operations;
+ inode->i_mapping->a_ops = &ext2_aops;
+ mark_inode_dirty(inode);
+ err = ext2_add_nondir(dentry, inode);
+ }
+ return err;
+}
+
+static int ext2_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev)
+{
+ struct inode * inode = ext2_new_inode (dir, mode);
+ int err = PTR_ERR(inode);
+ if (!IS_ERR(inode)) {
+ init_special_inode(inode, mode, rdev);
+ mark_inode_dirty(inode);
+ err = ext2_add_nondir(dentry, inode);
+ }
+ return err;
+}
+
+static int ext2_symlink (struct inode * dir, struct dentry * dentry,
+ const char * symname)
+{
+ struct super_block * sb = dir->i_sb;
+ int err = -ENAMETOOLONG;
+ unsigned l = strlen(symname)+1;
+ struct inode * inode;
+
+ if (l > sb->s_blocksize)
+ goto out;
+
+ inode = ext2_new_inode (dir, S_IFLNK | S_IRWXUGO);
+ err = PTR_ERR(inode);
+ if (IS_ERR(inode))
+ goto out;
+
+ if (l > sizeof (inode->u.ext2_i.i_data)) {
+ /* slow symlink */
+ inode->i_op = &page_symlink_inode_operations;
+ inode->i_mapping->a_ops = &ext2_aops;
+ err = block_symlink(inode, symname, l);
+ if (err)
+ goto out_fail;
+ } else {
+ /* fast symlink */
+ inode->i_op = &ext2_fast_symlink_inode_operations;
+ memcpy((char*)&inode->u.ext2_i.i_data,symname,l);
+ inode->i_size = l-1;
+ }
+ mark_inode_dirty(inode);
+
+ err = ext2_add_nondir(dentry, inode);
+out:
+ return err;
+
+out_fail:
+ ext2_dec_count(inode);
+ iput (inode);
+ goto out;
+}
+
+static int ext2_link (struct dentry * old_dentry, struct inode * dir,
+ struct dentry *dentry)
+{
+ struct inode *inode = old_dentry->d_inode;
+
+ if (S_ISDIR(inode->i_mode))
+ return -EPERM;
+
+ if (inode->i_nlink >= EXT2_LINK_MAX)
+ return -EMLINK;
+
+ inode->i_ctime = CURRENT_TIME;
+ ext2_inc_count(inode);
+ atomic_inc(&inode->i_count);
+
+ return ext2_add_nondir(dentry, inode);
+}
+
+static int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode)
+{
+ struct inode * inode;
+ int err = -EMLINK;
+
+ if (dir->i_nlink >= EXT2_LINK_MAX)
+ goto out;
+
+ ext2_inc_count(dir);
+
+ inode = ext2_new_inode (dir, S_IFDIR | mode);
+ err = PTR_ERR(inode);
+ if (IS_ERR(inode))
+ goto out_dir;
+
+ inode->i_op = &ext2_dir_inode_operations;
+ inode->i_fop = &ext2_dir_operations;
+ inode->i_mapping->a_ops = &ext2_aops;
+
+ ext2_inc_count(inode);
+
+ err = ext2_make_empty(inode, dir);
+ if (err)
+ goto out_fail;
+
+ err = ext2_add_link(dentry, inode);
+ if (err)
+ goto out_fail;
+
+ d_instantiate(dentry, inode);
+out:
+ return err;
+
+out_fail:
+ ext2_dec_count(inode);
+ ext2_dec_count(inode);
+ iput(inode);
+out_dir:
+ ext2_dec_count(dir);
+ goto out;
+}
+
+static int ext2_unlink(struct inode * dir, struct dentry *dentry)
+{
+ struct inode * inode = dentry->d_inode;
+ struct ext2_dir_entry_2 * de;
+ struct page * page;
+ int err = -ENOENT;
+
+ de = ext2_find_entry (dir, dentry, &page);
+ if (!de)
+ goto out;
+
+ err = ext2_delete_entry (de, page);
+ if (err)
+ goto out;
+
+ inode->i_ctime = dir->i_ctime;
+ ext2_dec_count(inode);
+ err = 0;
+out:
+ return err;
+}
+
+static int ext2_rmdir (struct inode * dir, struct dentry *dentry)
+{
+ struct inode * inode = dentry->d_inode;
+ int err = -ENOTEMPTY;
+
+ if (ext2_empty_dir(inode)) {
+ err = ext2_unlink(dir, dentry);
+ if (!err) {
+ inode->i_size = 0;
+ ext2_dec_count(inode);
+ ext2_dec_count(dir);
+ }
+ }
+ return err;
+}
+
+static int ext2_rename (struct inode * old_dir, struct dentry * old_dentry,
+ struct inode * new_dir, struct dentry * new_dentry )
+{
+ struct inode * old_inode = old_dentry->d_inode;
+ struct inode * new_inode = new_dentry->d_inode;
+ struct page * dir_page = NULL;
+ struct ext2_dir_entry_2 * dir_de = NULL;
+ struct page * old_page;
+ struct ext2_dir_entry_2 * old_de;
+ int err = -ENOENT;
+
+ old_de = ext2_find_entry (old_dir, old_dentry, &old_page);
+ if (!old_de)
+ goto out;
+
+ if (S_ISDIR(old_inode->i_mode)) {
+ err = -EIO;
+ dir_de = ext2_dotdot(old_inode, &dir_page);
+ if (!dir_de)
+ goto out_old;
+ }
+
+ if (new_inode) {
+ struct page *new_page;
+ struct ext2_dir_entry_2 *new_de;
+
+ err = -ENOTEMPTY;
+ if (dir_de && !ext2_empty_dir (new_inode))
+ goto out_dir;
+
+ err = -ENOENT;
+ new_de = ext2_find_entry (new_dir, new_dentry, &new_page);
+ if (!new_de)
+ goto out_dir;
+ ext2_inc_count(old_inode);
+ ext2_set_link(new_dir, new_de, new_page, old_inode);
+ new_inode->i_ctime = CURRENT_TIME;
+ if (dir_de)
+ new_inode->i_nlink--;
+ ext2_dec_count(new_inode);
+ } else {
+ if (dir_de) {
+ err = -EMLINK;
+ if (new_dir->i_nlink >= EXT2_LINK_MAX)
+ goto out_dir;
+ }
+ ext2_inc_count(old_inode);
+ err = ext2_add_link(new_dentry, old_inode);
+ if (err) {
+ ext2_dec_count(old_inode);
+ goto out_dir;
+ }
+ if (dir_de)
+ ext2_inc_count(new_dir);
+ }
+
+ ext2_delete_entry (old_de, old_page);
+ ext2_dec_count(old_inode);
+
+ if (dir_de) {
+ ext2_set_link(old_inode, dir_de, dir_page, new_dir);
+ ext2_dec_count(old_dir);
+ }
+ return 0;
+
+
+out_dir:
+ if (dir_de) {
+ kunmap(dir_page);
+ page_cache_release(dir_page);
+ }
+out_old:
+ kunmap(old_page);
+ page_cache_release(old_page);
+out:
+ return err;
+}
+
+struct inode_operations ext2_dir_inode_operations = {
+ create: ext2_create,
+ lookup: ext2_lookup,
+ link: ext2_link,
+ unlink: ext2_unlink,
+ symlink: ext2_symlink,
+ mkdir: ext2_mkdir,
+ rmdir: ext2_rmdir,
+ mknod: ext2_mknod,
+ rename: ext2_rename,
+};
diff --git a/fs/ext2/super.c b/fs/ext2/super.c
new file mode 100644
index 00000000000000..d510cc8d7de3aa
--- /dev/null
+++ b/fs/ext2/super.c
@@ -0,0 +1,854 @@
+/*
+ * linux/fs/ext2/super.c
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * from
+ *
+ * linux/fs/minix/inode.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * Big-endian to little-endian byte-swapping/bitmaps by
+ * David S. Miller (davem@caip.rutgers.edu), 1995
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/ext2_fs.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/locks.h>
+#include <linux/blkdev.h>
+#include <asm/uaccess.h>
+
+
+static void ext2_sync_super(struct super_block *sb,
+ struct ext2_super_block *es);
+
+static char error_buf[1024];
+
+void ext2_error (struct super_block * sb, const char * function,
+ const char * fmt, ...)
+{
+ va_list args;
+ struct ext2_super_block *es = EXT2_SB(sb)->s_es;
+
+ if (!(sb->s_flags & MS_RDONLY)) {
+ sb->u.ext2_sb.s_mount_state |= EXT2_ERROR_FS;
+ es->s_state =
+ cpu_to_le16(le16_to_cpu(es->s_state) | EXT2_ERROR_FS);
+ ext2_sync_super(sb, es);
+ }
+ va_start (args, fmt);
+ vsprintf (error_buf, fmt, args);
+ va_end (args);
+ if (test_opt (sb, ERRORS_PANIC) ||
+ (le16_to_cpu(sb->u.ext2_sb.s_es->s_errors) == EXT2_ERRORS_PANIC &&
+ !test_opt (sb, ERRORS_CONT) && !test_opt (sb, ERRORS_RO)))
+ panic ("EXT2-fs panic (device %s): %s: %s\n",
+ bdevname(sb->s_dev), function, error_buf);
+ printk (KERN_CRIT "EXT2-fs error (device %s): %s: %s\n",
+ bdevname(sb->s_dev), function, error_buf);
+ if (test_opt (sb, ERRORS_RO) ||
+ (le16_to_cpu(sb->u.ext2_sb.s_es->s_errors) == EXT2_ERRORS_RO &&
+ !test_opt (sb, ERRORS_CONT) && !test_opt (sb, ERRORS_PANIC))) {
+ printk ("Remounting filesystem read-only\n");
+ sb->s_flags |= MS_RDONLY;
+ }
+}
+
+NORET_TYPE void ext2_panic (struct super_block * sb, const char * function,
+ const char * fmt, ...)
+{
+ va_list args;
+
+ if (!(sb->s_flags & MS_RDONLY)) {
+ sb->u.ext2_sb.s_mount_state |= EXT2_ERROR_FS;
+ sb->u.ext2_sb.s_es->s_state =
+ cpu_to_le16(le16_to_cpu(sb->u.ext2_sb.s_es->s_state) | EXT2_ERROR_FS);
+ mark_buffer_dirty(sb->u.ext2_sb.s_sbh);
+ sb->s_dirt = 1;
+ }
+ va_start (args, fmt);
+ vsprintf (error_buf, fmt, args);
+ va_end (args);
+ sb->s_flags |= MS_RDONLY;
+ panic ("EXT2-fs panic (device %s): %s: %s\n",
+ bdevname(sb->s_dev), function, error_buf);
+}
+
+void ext2_warning (struct super_block * sb, const char * function,
+ const char * fmt, ...)
+{
+ va_list args;
+
+ va_start (args, fmt);
+ vsprintf (error_buf, fmt, args);
+ va_end (args);
+ printk (KERN_WARNING "EXT2-fs warning (device %s): %s: %s\n",
+ bdevname(sb->s_dev), function, error_buf);
+}
+
+void ext2_update_dynamic_rev(struct super_block *sb)
+{
+ struct ext2_super_block *es = EXT2_SB(sb)->s_es;
+
+ if (le32_to_cpu(es->s_rev_level) > EXT2_GOOD_OLD_REV)
+ return;
+
+ ext2_warning(sb, __FUNCTION__,
+ "updating to rev %d because of new feature flag, "
+ "running e2fsck is recommended",
+ EXT2_DYNAMIC_REV);
+
+ es->s_first_ino = cpu_to_le32(EXT2_GOOD_OLD_FIRST_INO);
+ es->s_inode_size = cpu_to_le16(EXT2_GOOD_OLD_INODE_SIZE);
+ es->s_rev_level = cpu_to_le32(EXT2_DYNAMIC_REV);
+ /* leave es->s_feature_*compat flags alone */
+ /* es->s_uuid will be set by e2fsck if empty */
+
+ /*
+ * The rest of the superblock fields should be zero, and if not it
+ * means they are likely already in use, so leave them alone. We
+ * can leave it up to e2fsck to clean up any inconsistencies there.
+ */
+}
+
+void ext2_put_super (struct super_block * sb)
+{
+ int db_count;
+ int i;
+
+ if (!(sb->s_flags & MS_RDONLY)) {
+ struct ext2_super_block *es = EXT2_SB(sb)->s_es;
+
+ es->s_state = le16_to_cpu(EXT2_SB(sb)->s_mount_state);
+ ext2_sync_super(sb, es);
+ }
+ db_count = EXT2_SB(sb)->s_gdb_count;
+ for (i = 0; i < db_count; i++)
+ if (sb->u.ext2_sb.s_group_desc[i])
+ brelse (sb->u.ext2_sb.s_group_desc[i]);
+ kfree(sb->u.ext2_sb.s_group_desc);
+ for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++)
+ if (sb->u.ext2_sb.s_inode_bitmap[i])
+ brelse (sb->u.ext2_sb.s_inode_bitmap[i]);
+ for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++)
+ if (sb->u.ext2_sb.s_block_bitmap[i])
+ brelse (sb->u.ext2_sb.s_block_bitmap[i]);
+ brelse (sb->u.ext2_sb.s_sbh);
+
+ return;
+}
+
+static struct super_operations ext2_sops = {
+ read_inode: ext2_read_inode,
+ write_inode: ext2_write_inode,
+ put_inode: ext2_put_inode,
+ delete_inode: ext2_delete_inode,
+ put_super: ext2_put_super,
+ write_super: ext2_write_super,
+ statfs: ext2_statfs,
+ remount_fs: ext2_remount,
+};
+
+/*
+ * This function has been shamelessly adapted from the msdos fs
+ */
+static int parse_options (char * options, unsigned long * sb_block,
+ unsigned short *resuid, unsigned short * resgid,
+ unsigned long * mount_options)
+{
+ char * this_char;
+ char * value;
+
+ if (!options)
+ return 1;
+ for (this_char = strtok (options, ",");
+ this_char != NULL;
+ this_char = strtok (NULL, ",")) {
+ if ((value = strchr (this_char, '=')) != NULL)
+ *value++ = 0;
+ if (!strcmp (this_char, "bsddf"))
+ clear_opt (*mount_options, MINIX_DF);
+ else if (!strcmp (this_char, "nouid32")) {
+ set_opt (*mount_options, NO_UID32);
+ }
+ else if (!strcmp (this_char, "check")) {
+ if (!value || !*value || !strcmp (value, "none"))
+ clear_opt (*mount_options, CHECK);
+ else
+#ifdef CONFIG_EXT2_CHECK
+ set_opt (*mount_options, CHECK);
+#else
+ printk("EXT2 Check option not supported\n");
+#endif
+ }
+ else if (!strcmp (this_char, "debug"))
+ set_opt (*mount_options, DEBUG);
+ else if (!strcmp (this_char, "errors")) {
+ if (!value || !*value) {
+ printk ("EXT2-fs: the errors option requires "
+ "an argument\n");
+ return 0;
+ }
+ if (!strcmp (value, "continue")) {
+ clear_opt (*mount_options, ERRORS_RO);
+ clear_opt (*mount_options, ERRORS_PANIC);
+ set_opt (*mount_options, ERRORS_CONT);
+ }
+ else if (!strcmp (value, "remount-ro")) {
+ clear_opt (*mount_options, ERRORS_CONT);
+ clear_opt (*mount_options, ERRORS_PANIC);
+ set_opt (*mount_options, ERRORS_RO);
+ }
+ else if (!strcmp (value, "panic")) {
+ clear_opt (*mount_options, ERRORS_CONT);
+ clear_opt (*mount_options, ERRORS_RO);
+ set_opt (*mount_options, ERRORS_PANIC);
+ }
+ else {
+ printk ("EXT2-fs: Invalid errors option: %s\n",
+ value);
+ return 0;
+ }
+ }
+ else if (!strcmp (this_char, "grpid") ||
+ !strcmp (this_char, "bsdgroups"))
+ set_opt (*mount_options, GRPID);
+ else if (!strcmp (this_char, "minixdf"))
+ set_opt (*mount_options, MINIX_DF);
+ else if (!strcmp (this_char, "nocheck"))
+ clear_opt (*mount_options, CHECK);
+ else if (!strcmp (this_char, "nogrpid") ||
+ !strcmp (this_char, "sysvgroups"))
+ clear_opt (*mount_options, GRPID);
+ else if (!strcmp (this_char, "resgid")) {
+ if (!value || !*value) {
+ printk ("EXT2-fs: the resgid option requires "
+ "an argument\n");
+ return 0;
+ }
+ *resgid = simple_strtoul (value, &value, 0);
+ if (*value) {
+ printk ("EXT2-fs: Invalid resgid option: %s\n",
+ value);
+ return 0;
+ }
+ }
+ else if (!strcmp (this_char, "resuid")) {
+ if (!value || !*value) {
+ printk ("EXT2-fs: the resuid option requires "
+ "an argument");
+ return 0;
+ }
+ *resuid = simple_strtoul (value, &value, 0);
+ if (*value) {
+ printk ("EXT2-fs: Invalid resuid option: %s\n",
+ value);
+ return 0;
+ }
+ }
+ else if (!strcmp (this_char, "sb")) {
+ if (!value || !*value) {
+ printk ("EXT2-fs: the sb option requires "
+ "an argument");
+ return 0;
+ }
+ *sb_block = simple_strtoul (value, &value, 0);
+ if (*value) {
+ printk ("EXT2-fs: Invalid sb option: %s\n",
+ value);
+ return 0;
+ }
+ }
+ /* Silently ignore the quota options */
+ else if (!strcmp (this_char, "grpquota")
+ || !strcmp (this_char, "noquota")
+ || !strcmp (this_char, "quota")
+ || !strcmp (this_char, "usrquota"))
+ /* Don't do anything ;-) */ ;
+ else {
+ printk ("EXT2-fs: Unrecognized mount option %s\n", this_char);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int ext2_setup_super (struct super_block * sb,
+ struct ext2_super_block * es,
+ int read_only)
+{
+ int res = 0;
+ if (le32_to_cpu(es->s_rev_level) > EXT2_MAX_SUPP_REV) {
+ printk ("EXT2-fs warning: revision level too high, "
+ "forcing read-only mode\n");
+ res = MS_RDONLY;
+ }
+ if (read_only)
+ return res;
+ if (!(sb->u.ext2_sb.s_mount_state & EXT2_VALID_FS))
+ printk ("EXT2-fs warning: mounting unchecked fs, "
+ "running e2fsck is recommended\n");
+ else if ((sb->u.ext2_sb.s_mount_state & EXT2_ERROR_FS))
+ printk ("EXT2-fs warning: mounting fs with errors, "
+ "running e2fsck is recommended\n");
+ else if ((__s16) le16_to_cpu(es->s_max_mnt_count) >= 0 &&
+ le16_to_cpu(es->s_mnt_count) >=
+ (unsigned short) (__s16) le16_to_cpu(es->s_max_mnt_count))
+ printk ("EXT2-fs warning: maximal mount count reached, "
+ "running e2fsck is recommended\n");
+ else if (le32_to_cpu(es->s_checkinterval) &&
+ (le32_to_cpu(es->s_lastcheck) + le32_to_cpu(es->s_checkinterval) <= CURRENT_TIME))
+ printk ("EXT2-fs warning: checktime reached, "
+ "running e2fsck is recommended\n");
+ if (!(__s16) le16_to_cpu(es->s_max_mnt_count))
+ es->s_max_mnt_count = (__s16) cpu_to_le16(EXT2_DFL_MAX_MNT_COUNT);
+ es->s_mnt_count=cpu_to_le16(le16_to_cpu(es->s_mnt_count) + 1);
+ ext2_write_super(sb);
+ if (test_opt (sb, DEBUG))
+ printk ("[EXT II FS %s, %s, bs=%lu, fs=%lu, gc=%lu, "
+ "bpg=%lu, ipg=%lu, mo=%04lx]\n",
+ EXT2FS_VERSION, EXT2FS_DATE, sb->s_blocksize,
+ sb->u.ext2_sb.s_frag_size,
+ sb->u.ext2_sb.s_groups_count,
+ EXT2_BLOCKS_PER_GROUP(sb),
+ EXT2_INODES_PER_GROUP(sb),
+ sb->u.ext2_sb.s_mount_opt);
+#ifdef CONFIG_EXT2_CHECK
+ if (test_opt (sb, CHECK)) {
+ ext2_check_blocks_bitmap (sb);
+ ext2_check_inodes_bitmap (sb);
+ }
+#endif
+ return res;
+}
+
+static int ext2_check_descriptors (struct super_block * sb)
+{
+ int i;
+ int desc_block = 0;
+ unsigned long block = le32_to_cpu(sb->u.ext2_sb.s_es->s_first_data_block);
+ struct ext2_group_desc * gdp = NULL;
+
+ ext2_debug ("Checking group descriptors");
+
+ for (i = 0; i < sb->u.ext2_sb.s_groups_count; i++)
+ {
+ if ((i % EXT2_DESC_PER_BLOCK(sb)) == 0)
+ gdp = (struct ext2_group_desc *) sb->u.ext2_sb.s_group_desc[desc_block++]->b_data;
+ if (le32_to_cpu(gdp->bg_block_bitmap) < block ||
+ le32_to_cpu(gdp->bg_block_bitmap) >= block + EXT2_BLOCKS_PER_GROUP(sb))
+ {
+ ext2_error (sb, "ext2_check_descriptors",
+ "Block bitmap for group %d"
+ " not in group (block %lu)!",
+ i, (unsigned long) le32_to_cpu(gdp->bg_block_bitmap));
+ return 0;
+ }
+ if (le32_to_cpu(gdp->bg_inode_bitmap) < block ||
+ le32_to_cpu(gdp->bg_inode_bitmap) >= block + EXT2_BLOCKS_PER_GROUP(sb))
+ {
+ ext2_error (sb, "ext2_check_descriptors",
+ "Inode bitmap for group %d"
+ " not in group (block %lu)!",
+ i, (unsigned long) le32_to_cpu(gdp->bg_inode_bitmap));
+ return 0;
+ }
+ if (le32_to_cpu(gdp->bg_inode_table) < block ||
+ le32_to_cpu(gdp->bg_inode_table) + sb->u.ext2_sb.s_itb_per_group >=
+ block + EXT2_BLOCKS_PER_GROUP(sb))
+ {
+ ext2_error (sb, "ext2_check_descriptors",
+ "Inode table for group %d"
+ " not in group (block %lu)!",
+ i, (unsigned long) le32_to_cpu(gdp->bg_inode_table));
+ return 0;
+ }
+ block += EXT2_BLOCKS_PER_GROUP(sb);
+ gdp++;
+ }
+ return 1;
+}
+
+#define log2(n) ffz(~(n))
+
+/*
+ * Maximal file size. There is a direct, and {,double-,triple-}indirect
+ * block limit, and also a limit of (2^32 - 1) 512-byte sectors in i_blocks.
+ * We need to be 1 filesystem block less than the 2^32 sector limit.
+ */
+static loff_t ext2_max_size(int bits)
+{
+ loff_t res = EXT2_NDIR_BLOCKS;
+ res += 1LL << (bits-2);
+ res += 1LL << (2*(bits-2));
+ res += 1LL << (3*(bits-2));
+ res <<= bits;
+ if (res > (512LL << 32) - (1 << bits))
+ res = (512LL << 32) - (1 << bits);
+ return res;
+}
+
+static unsigned long descriptor_loc(struct super_block *sb,
+ unsigned long logic_sb_block,
+ int nr)
+{
+ struct ext2_sb_info *sbi = EXT2_SB(sb);
+ unsigned long bg, first_data_block, first_meta_bg;
+ int has_super = 0;
+
+ first_data_block = le32_to_cpu(sbi->s_es->s_first_data_block);
+ first_meta_bg = le32_to_cpu(sbi->s_es->s_first_meta_bg);
+
+ if (!EXT2_HAS_INCOMPAT_FEATURE(sb, EXT2_FEATURE_INCOMPAT_META_BG) ||
+ nr < first_meta_bg)
+ return (logic_sb_block + nr + 1);
+ bg = sbi->s_desc_per_block * nr;
+ if (ext2_bg_has_super(sb, bg))
+ has_super = 1;
+ return (first_data_block + has_super + (bg * sbi->s_blocks_per_group));
+}
+
+struct super_block * ext2_read_super (struct super_block * sb, void * data,
+ int silent)
+{
+ struct buffer_head * bh;
+ struct ext2_sb_info * sbi = EXT2_SB(sb);
+ struct ext2_super_block * es;
+ unsigned long sb_block = 1;
+ unsigned short resuid = EXT2_DEF_RESUID;
+ unsigned short resgid = EXT2_DEF_RESGID;
+ unsigned long block;
+ unsigned long logic_sb_block;
+ unsigned long offset = 0;
+ kdev_t dev = sb->s_dev;
+ int blocksize = BLOCK_SIZE;
+ int db_count;
+ int i, j;
+
+ /*
+ * See what the current blocksize for the device is, and
+ * use that as the blocksize. Otherwise (or if the blocksize
+ * is smaller than the default) use the default.
+ * This is important for devices that have a hardware
+ * sectorsize that is larger than the default.
+ */
+ blocksize = get_hardsect_size(dev);
+ if(blocksize < BLOCK_SIZE )
+ blocksize = BLOCK_SIZE;
+
+ sb->u.ext2_sb.s_mount_opt = 0;
+ if (!parse_options ((char *) data, &sb_block, &resuid, &resgid,
+ &sb->u.ext2_sb.s_mount_opt)) {
+ return NULL;
+ }
+
+ if (set_blocksize(dev, blocksize) < 0) {
+ printk ("EXT2-fs: unable to set blocksize %d\n", blocksize);
+ return NULL;
+ }
+ sb->s_blocksize = blocksize;
+
+ /*
+ * If the superblock doesn't start on a sector boundary,
+ * calculate the offset. FIXME(eric) this doesn't make sense
+ * that we would have to do this.
+ */
+ if (blocksize != BLOCK_SIZE) {
+ logic_sb_block = (sb_block*BLOCK_SIZE) / blocksize;
+ offset = (sb_block*BLOCK_SIZE) % blocksize;
+ } else {
+ logic_sb_block = sb_block;
+ }
+
+ if (!(bh = sb_bread(sb, logic_sb_block))) {
+ printk ("EXT2-fs: unable to read superblock\n");
+ return NULL;
+ }
+ /*
+ * Note: s_es must be initialized as soon as possible because
+ * some ext2 macro-instructions depend on its value
+ */
+ es = (struct ext2_super_block *) (((char *)bh->b_data) + offset);
+ sb->u.ext2_sb.s_es = es;
+ sb->s_magic = le16_to_cpu(es->s_magic);
+ if (sb->s_magic != EXT2_SUPER_MAGIC) {
+ if (!silent)
+ printk ("VFS: Can't find ext2 filesystem on dev %s.\n",
+ bdevname(dev));
+ goto failed_mount;
+ }
+ if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV &&
+ (EXT2_HAS_COMPAT_FEATURE(sb, ~0U) ||
+ EXT2_HAS_RO_COMPAT_FEATURE(sb, ~0U) ||
+ EXT2_HAS_INCOMPAT_FEATURE(sb, ~0U)))
+ printk("EXT2-fs warning: feature flags set on rev 0 fs, "
+ "running e2fsck is recommended\n");
+ /*
+ * Check feature flags regardless of the revision level, since we
+ * previously didn't change the revision level when setting the flags,
+ * so there is a chance incompat flags are set on a rev 0 filesystem.
+ */
+ if ((i = EXT2_HAS_INCOMPAT_FEATURE(sb, ~EXT2_FEATURE_INCOMPAT_SUPP))) {
+ printk("EXT2-fs: %s: couldn't mount because of "
+ "unsupported optional features (%x).\n",
+ bdevname(dev), i);
+ goto failed_mount;
+ }
+ if (!(sb->s_flags & MS_RDONLY) &&
+ (i = EXT2_HAS_RO_COMPAT_FEATURE(sb, ~EXT2_FEATURE_RO_COMPAT_SUPP))){
+ printk("EXT2-fs: %s: couldn't mount RDWR because of "
+ "unsupported optional features (%x).\n",
+ bdevname(dev), i);
+ goto failed_mount;
+ }
+ if (EXT2_HAS_COMPAT_FEATURE(sb, EXT3_FEATURE_COMPAT_HAS_JOURNAL))
+ ext2_warning(sb, __FUNCTION__,
+ "mounting ext3 filesystem as ext2\n");
+ sb->s_blocksize_bits =
+ le32_to_cpu(EXT2_SB(sb)->s_es->s_log_block_size) + 10;
+ sb->s_blocksize = 1 << sb->s_blocksize_bits;
+
+ sb->s_maxbytes = ext2_max_size(sb->s_blocksize_bits);
+
+ /* If the blocksize doesn't match, re-read the thing.. */
+ if (sb->s_blocksize != blocksize) {
+ blocksize = sb->s_blocksize;
+ brelse(bh);
+
+ if (set_blocksize(dev, blocksize) < 0) {
+ printk(KERN_ERR "EXT2-fs: blocksize too small for device.\n");
+ return NULL;
+ }
+
+ logic_sb_block = (sb_block*BLOCK_SIZE) / blocksize;
+ offset = (sb_block*BLOCK_SIZE) % blocksize;
+ bh = sb_bread(sb, logic_sb_block);
+ if(!bh) {
+ printk("EXT2-fs: Couldn't read superblock on "
+ "2nd try.\n");
+ goto failed_mount;
+ }
+ es = (struct ext2_super_block *) (((char *)bh->b_data) + offset);
+ sb->u.ext2_sb.s_es = es;
+ if (es->s_magic != le16_to_cpu(EXT2_SUPER_MAGIC)) {
+ printk ("EXT2-fs: Magic mismatch, very weird !\n");
+ goto failed_mount;
+ }
+ }
+
+ if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV) {
+ sbi->s_inode_size = EXT2_GOOD_OLD_INODE_SIZE;
+ sbi->s_first_ino = EXT2_GOOD_OLD_FIRST_INO;
+ } else {
+ sbi->s_inode_size = le16_to_cpu(es->s_inode_size);
+ sbi->s_first_ino = le32_to_cpu(es->s_first_ino);
+ if ((sbi->s_inode_size < EXT2_GOOD_OLD_INODE_SIZE) ||
+ (sbi->s_inode_size & (sbi->s_inode_size - 1)) ||
+ (sbi->s_inode_size > blocksize)) {
+ printk ("EXT2-fs: unsupported inode size: %d\n",
+ sbi->s_inode_size);
+ goto failed_mount;
+ }
+ }
+ sb->u.ext2_sb.s_frag_size = EXT2_MIN_FRAG_SIZE <<
+ le32_to_cpu(es->s_log_frag_size);
+ if (sb->u.ext2_sb.s_frag_size)
+ sb->u.ext2_sb.s_frags_per_block = sb->s_blocksize /
+ sb->u.ext2_sb.s_frag_size;
+ else
+ sb->s_magic = 0;
+ sb->u.ext2_sb.s_blocks_per_group = le32_to_cpu(es->s_blocks_per_group);
+ sb->u.ext2_sb.s_frags_per_group = le32_to_cpu(es->s_frags_per_group);
+ sb->u.ext2_sb.s_inodes_per_group = le32_to_cpu(es->s_inodes_per_group);
+ sb->u.ext2_sb.s_inodes_per_block = sb->s_blocksize /
+ EXT2_INODE_SIZE(sb);
+ sb->u.ext2_sb.s_itb_per_group = sb->u.ext2_sb.s_inodes_per_group /
+ sb->u.ext2_sb.s_inodes_per_block;
+ sb->u.ext2_sb.s_desc_per_block = sb->s_blocksize /
+ sizeof (struct ext2_group_desc);
+ sb->u.ext2_sb.s_sbh = bh;
+ if (resuid != EXT2_DEF_RESUID)
+ sb->u.ext2_sb.s_resuid = resuid;
+ else
+ sb->u.ext2_sb.s_resuid = le16_to_cpu(es->s_def_resuid);
+ if (resgid != EXT2_DEF_RESGID)
+ sb->u.ext2_sb.s_resgid = resgid;
+ else
+ sb->u.ext2_sb.s_resgid = le16_to_cpu(es->s_def_resgid);
+ sb->u.ext2_sb.s_mount_state = le16_to_cpu(es->s_state);
+ sb->u.ext2_sb.s_addr_per_block_bits =
+ log2 (EXT2_ADDR_PER_BLOCK(sb));
+ sb->u.ext2_sb.s_desc_per_block_bits =
+ log2 (EXT2_DESC_PER_BLOCK(sb));
+ if (sb->s_magic != EXT2_SUPER_MAGIC) {
+ if (!silent)
+ printk ("VFS: Can't find an ext2 filesystem on dev "
+ "%s.\n",
+ bdevname(dev));
+ goto failed_mount;
+ }
+ if (sb->s_blocksize != bh->b_size) {
+ if (!silent)
+ printk ("VFS: Unsupported blocksize on dev "
+ "%s.\n", bdevname(dev));
+ goto failed_mount;
+ }
+
+ if (sb->s_blocksize != sb->u.ext2_sb.s_frag_size) {
+ printk ("EXT2-fs: fragsize %lu != blocksize %lu (not supported yet)\n",
+ sb->u.ext2_sb.s_frag_size, sb->s_blocksize);
+ goto failed_mount;
+ }
+
+ if (sb->u.ext2_sb.s_blocks_per_group > sb->s_blocksize * 8) {
+ printk ("EXT2-fs: #blocks per group too big: %lu\n",
+ sb->u.ext2_sb.s_blocks_per_group);
+ goto failed_mount;
+ }
+ if (sb->u.ext2_sb.s_frags_per_group > sb->s_blocksize * 8) {
+ printk ("EXT2-fs: #fragments per group too big: %lu\n",
+ sb->u.ext2_sb.s_frags_per_group);
+ goto failed_mount;
+ }
+ if (sb->u.ext2_sb.s_inodes_per_group > sb->s_blocksize * 8) {
+ printk ("EXT2-fs: #inodes per group too big: %lu\n",
+ sb->u.ext2_sb.s_inodes_per_group);
+ goto failed_mount;
+ }
+
+ sb->u.ext2_sb.s_groups_count = (le32_to_cpu(es->s_blocks_count) -
+ le32_to_cpu(es->s_first_data_block) +
+ EXT2_BLOCKS_PER_GROUP(sb) - 1) /
+ EXT2_BLOCKS_PER_GROUP(sb);
+ db_count = (sb->u.ext2_sb.s_groups_count + EXT2_DESC_PER_BLOCK(sb) - 1) /
+ EXT2_DESC_PER_BLOCK(sb);
+ sb->u.ext2_sb.s_group_desc = kmalloc (db_count * sizeof (struct buffer_head *), GFP_KERNEL);
+ if (sb->u.ext2_sb.s_group_desc == NULL) {
+ printk ("EXT2-fs: not enough memory\n");
+ goto failed_mount;
+ }
+ for (i = 0; i < db_count; i++) {
+ block = descriptor_loc(sb, logic_sb_block, i);
+ sbi->s_group_desc[i] = sb_bread(sb, block);
+ if (!sbi->s_group_desc[i]) {
+ for (j = 0; j < i; j++)
+ brelse (sbi->s_group_desc[j]);
+ kfree(sbi->s_group_desc);
+ printk ("EXT2-fs: unable to read group descriptors\n");
+ goto failed_mount;
+ }
+ }
+ if (!ext2_check_descriptors (sb)) {
+ printk ("EXT2-fs: group descriptors corrupted!\n");
+ db_count = i;
+ goto failed_mount2;
+ }
+ for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++) {
+ sb->u.ext2_sb.s_inode_bitmap_number[i] = 0;
+ sb->u.ext2_sb.s_inode_bitmap[i] = NULL;
+ sb->u.ext2_sb.s_block_bitmap_number[i] = 0;
+ sb->u.ext2_sb.s_block_bitmap[i] = NULL;
+ }
+ sb->u.ext2_sb.s_loaded_inode_bitmaps = 0;
+ sb->u.ext2_sb.s_loaded_block_bitmaps = 0;
+ sb->u.ext2_sb.s_gdb_count = db_count;
+ /*
+ * set up enough so that it can read an inode
+ */
+ sb->s_op = &ext2_sops;
+ sb->s_root = d_alloc_root(iget(sb, EXT2_ROOT_INO));
+ if (!sb->s_root || !S_ISDIR(sb->s_root->d_inode->i_mode) ||
+ !sb->s_root->d_inode->i_blocks || !sb->s_root->d_inode->i_size) {
+ if (sb->s_root) {
+ dput(sb->s_root);
+ sb->s_root = NULL;
+ printk(KERN_ERR "EXT2-fs: corrupt root inode, run e2fsck\n");
+ } else
+ printk(KERN_ERR "EXT2-fs: get root inode failed\n");
+ goto failed_mount2;
+ }
+ ext2_setup_super (sb, es, sb->s_flags & MS_RDONLY);
+ return sb;
+failed_mount2:
+ for (i = 0; i < db_count; i++)
+ brelse(sb->u.ext2_sb.s_group_desc[i]);
+ kfree(sb->u.ext2_sb.s_group_desc);
+failed_mount:
+ brelse(bh);
+ return NULL;
+}
+
+static void ext2_commit_super (struct super_block * sb,
+ struct ext2_super_block * es)
+{
+ es->s_wtime = cpu_to_le32(CURRENT_TIME);
+ mark_buffer_dirty(sb->u.ext2_sb.s_sbh);
+ sb->s_dirt = 0;
+}
+
+static void ext2_sync_super(struct super_block *sb, struct ext2_super_block *es)
+{
+ es->s_wtime = cpu_to_le32(CURRENT_TIME);
+ mark_buffer_dirty(EXT2_SB(sb)->s_sbh);
+ ll_rw_block(WRITE, 1, &EXT2_SB(sb)->s_sbh);
+ wait_on_buffer(EXT2_SB(sb)->s_sbh);
+ sb->s_dirt = 0;
+}
+
+/*
+ * In the second extended file system, it is not necessary to
+ * write the super block since we use a mapping of the
+ * disk super block in a buffer.
+ *
+ * However, this function is still used to set the fs valid
+ * flags to 0. We need to set this flag to 0 since the fs
+ * may have been checked while mounted and e2fsck may have
+ * set s_state to EXT2_VALID_FS after some corrections.
+ */
+
+void ext2_write_super (struct super_block * sb)
+{
+ struct ext2_super_block * es;
+
+ if (!(sb->s_flags & MS_RDONLY)) {
+ es = sb->u.ext2_sb.s_es;
+
+ if (le16_to_cpu(es->s_state) & EXT2_VALID_FS) {
+ ext2_debug ("setting valid to 0\n");
+ es->s_state = cpu_to_le16(le16_to_cpu(es->s_state) &
+ ~EXT2_VALID_FS);
+ es->s_mtime = cpu_to_le32(CURRENT_TIME);
+ ext2_sync_super(sb, es);
+ } else
+ ext2_commit_super (sb, es);
+ }
+ sb->s_dirt = 0;
+}
+
+int ext2_remount (struct super_block * sb, int * flags, char * data)
+{
+ struct ext2_super_block * es;
+ unsigned short resuid = sb->u.ext2_sb.s_resuid;
+ unsigned short resgid = sb->u.ext2_sb.s_resgid;
+ unsigned long new_mount_opt;
+ unsigned long tmp;
+
+ /*
+ * Allow the "check" option to be passed as a remount option.
+ */
+ new_mount_opt = sb->u.ext2_sb.s_mount_opt;
+ if (!parse_options (data, &tmp, &resuid, &resgid,
+ &new_mount_opt))
+ return -EINVAL;
+
+ sb->u.ext2_sb.s_mount_opt = new_mount_opt;
+ sb->u.ext2_sb.s_resuid = resuid;
+ sb->u.ext2_sb.s_resgid = resgid;
+ es = sb->u.ext2_sb.s_es;
+ if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
+ return 0;
+ if (*flags & MS_RDONLY) {
+ if (le16_to_cpu(es->s_state) & EXT2_VALID_FS ||
+ !(sb->u.ext2_sb.s_mount_state & EXT2_VALID_FS))
+ return 0;
+ /*
+ * OK, we are remounting a valid rw partition rdonly, so set
+ * the rdonly flag and then mark the partition as valid again.
+ */
+ es->s_state = cpu_to_le16(sb->u.ext2_sb.s_mount_state);
+ es->s_mtime = cpu_to_le32(CURRENT_TIME);
+ } else {
+ int ret;
+ if ((ret = EXT2_HAS_RO_COMPAT_FEATURE(sb,
+ ~EXT2_FEATURE_RO_COMPAT_SUPP))) {
+ printk("EXT2-fs: %s: couldn't remount RDWR because of "
+ "unsupported optional features (%x).\n",
+ bdevname(sb->s_dev), ret);
+ return -EROFS;
+ }
+ /*
+ * Mounting a RDONLY partition read-write, so reread and
+ * store the current valid flag. (It may have been changed
+ * by e2fsck since we originally mounted the partition.)
+ */
+ sb->u.ext2_sb.s_mount_state = le16_to_cpu(es->s_state);
+ if (!ext2_setup_super (sb, es, 0))
+ sb->s_flags &= ~MS_RDONLY;
+ }
+ ext2_sync_super(sb, es);
+ return 0;
+}
+
+int ext2_statfs (struct super_block * sb, struct statfs * buf)
+{
+ unsigned long overhead;
+ int i;
+
+ if (test_opt (sb, MINIX_DF))
+ overhead = 0;
+ else {
+ /*
+ * Compute the overhead (FS structures)
+ */
+
+ /*
+ * All of the blocks before first_data_block are
+ * overhead
+ */
+ overhead = le32_to_cpu(sb->u.ext2_sb.s_es->s_first_data_block);
+
+ /*
+ * Add the overhead attributed to the superblock and
+ * block group descriptors. If the sparse superblocks
+ * feature is turned on, then not all groups have this.
+ */
+ for (i = 0; i < EXT2_SB(sb)->s_groups_count; i++)
+ overhead += ext2_bg_has_super(sb, i) +
+ ext2_bg_num_gdb(sb, i);
+
+ /*
+ * Every block group has an inode bitmap, a block
+ * bitmap, and an inode table.
+ */
+ overhead += (sb->u.ext2_sb.s_groups_count *
+ (2 + sb->u.ext2_sb.s_itb_per_group));
+ }
+
+ buf->f_type = EXT2_SUPER_MAGIC;
+ buf->f_bsize = sb->s_blocksize;
+ buf->f_blocks = le32_to_cpu(sb->u.ext2_sb.s_es->s_blocks_count) - overhead;
+ buf->f_bfree = ext2_count_free_blocks (sb);
+ buf->f_bavail = buf->f_bfree - le32_to_cpu(sb->u.ext2_sb.s_es->s_r_blocks_count);
+ if (buf->f_bfree < le32_to_cpu(sb->u.ext2_sb.s_es->s_r_blocks_count))
+ buf->f_bavail = 0;
+ buf->f_files = le32_to_cpu(sb->u.ext2_sb.s_es->s_inodes_count);
+ buf->f_ffree = ext2_count_free_inodes (sb);
+ buf->f_namelen = EXT2_NAME_LEN;
+ return 0;
+}
+
+static DECLARE_FSTYPE_DEV(ext2_fs_type, "ext2", ext2_read_super);
+
+static int __init init_ext2_fs(void)
+{
+ return register_filesystem(&ext2_fs_type);
+}
+
+static void __exit exit_ext2_fs(void)
+{
+ unregister_filesystem(&ext2_fs_type);
+}
+
+EXPORT_NO_SYMBOLS;
+
+module_init(init_ext2_fs)
+module_exit(exit_ext2_fs)
diff --git a/fs/ext2/symlink.c b/fs/ext2/symlink.c
new file mode 100644
index 00000000000000..05a4e585a8eea0
--- /dev/null
+++ b/fs/ext2/symlink.c
@@ -0,0 +1,38 @@
+/*
+ * linux/fs/ext2/symlink.c
+ *
+ * Only fast symlinks left here - the rest is done by generic code. AV, 1999
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * from
+ *
+ * linux/fs/minix/symlink.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * ext2 symlink handling code
+ */
+
+#include <linux/fs.h>
+#include <linux/ext2_fs.h>
+
+static int ext2_readlink(struct dentry *dentry, char *buffer, int buflen)
+{
+ char *s = (char *)dentry->d_inode->u.ext2_i.i_data;
+ return vfs_readlink(dentry, buffer, buflen, s);
+}
+
+static int ext2_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+ char *s = (char *)dentry->d_inode->u.ext2_i.i_data;
+ return vfs_follow_link(nd, s);
+}
+
+struct inode_operations ext2_fast_symlink_inode_operations = {
+ readlink: ext2_readlink,
+ follow_link: ext2_follow_link,
+};
diff --git a/fs/ext3/Makefile b/fs/ext3/Makefile
new file mode 100644
index 00000000000000..68cb4861815cf0
--- /dev/null
+++ b/fs/ext3/Makefile
@@ -0,0 +1,16 @@
+#
+# Makefile for the linux ext2-filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+O_TARGET := ext3.o
+
+obj-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o \
+ ioctl.o namei.o super.o symlink.o
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/ext3/balloc.c b/fs/ext3/balloc.c
new file mode 100644
index 00000000000000..b9b03ab4c9edcc
--- /dev/null
+++ b/fs/ext3/balloc.c
@@ -0,0 +1,1009 @@
+/*
+ * linux/fs/ext3/balloc.c
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * Enhanced block allocation by Stephen Tweedie (sct@redhat.com), 1993
+ * Big-endian to little-endian byte-swapping/bitmaps by
+ * David S. Miller (davem@caip.rutgers.edu), 1995
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/jbd.h>
+#include <linux/ext3_fs.h>
+#include <linux/ext3_jbd.h>
+#include <linux/locks.h>
+#include <linux/quotaops.h>
+
+/*
+ * balloc.c contains the blocks allocation and deallocation routines
+ */
+
+/*
+ * The free blocks are managed by bitmaps. A file system contains several
+ * blocks groups. Each group contains 1 bitmap block for blocks, 1 bitmap
+ * block for inodes, N blocks for the inode table and data blocks.
+ *
+ * The file system contains group descriptors which are located after the
+ * super block. Each descriptor contains the number of the bitmap block and
+ * the free blocks count in the block. The descriptors are loaded in memory
+ * when a file system is mounted (see ext3_read_super).
+ */
+
+
+#define in_range(b, first, len) ((b) >= (first) && (b) <= (first) + (len) - 1)
+
+struct ext3_group_desc * ext3_get_group_desc(struct super_block * sb,
+ unsigned int block_group,
+ struct buffer_head ** bh)
+{
+ unsigned long group_desc;
+ unsigned long desc;
+ struct ext3_group_desc * gdp;
+
+ if (block_group >= sb->u.ext3_sb.s_groups_count) {
+ ext3_error (sb, "ext3_get_group_desc",
+ "block_group >= groups_count - "
+ "block_group = %d, groups_count = %lu",
+ block_group, sb->u.ext3_sb.s_groups_count);
+
+ return NULL;
+ }
+
+ group_desc = block_group / EXT3_DESC_PER_BLOCK(sb);
+ desc = block_group % EXT3_DESC_PER_BLOCK(sb);
+ if (!sb->u.ext3_sb.s_group_desc[group_desc]) {
+ ext3_error (sb, "ext3_get_group_desc",
+ "Group descriptor not loaded - "
+ "block_group = %d, group_desc = %lu, desc = %lu",
+ block_group, group_desc, desc);
+ return NULL;
+ }
+
+ gdp = (struct ext3_group_desc *)
+ sb->u.ext3_sb.s_group_desc[group_desc]->b_data;
+ if (bh)
+ *bh = sb->u.ext3_sb.s_group_desc[group_desc];
+ return gdp + desc;
+}
+
+/*
+ * Read the bitmap for a given block_group, reading into the specified
+ * slot in the superblock's bitmap cache.
+ *
+ * Return >=0 on success or a -ve error code.
+ */
+
+static int read_block_bitmap (struct super_block * sb,
+ unsigned int block_group,
+ unsigned long bitmap_nr)
+{
+ struct ext3_group_desc * gdp;
+ struct buffer_head * bh = NULL;
+ int retval = -EIO;
+
+ gdp = ext3_get_group_desc (sb, block_group, NULL);
+ if (!gdp)
+ goto error_out;
+ retval = 0;
+ bh = sb_bread(sb, le32_to_cpu(gdp->bg_block_bitmap));
+ if (!bh) {
+ ext3_error (sb, "read_block_bitmap",
+ "Cannot read block bitmap - "
+ "block_group = %d, block_bitmap = %lu",
+ block_group, (unsigned long) gdp->bg_block_bitmap);
+ retval = -EIO;
+ }
+ /*
+ * On IO error, just leave a zero in the superblock's block pointer for
+ * this group. The IO will be retried next time.
+ */
+error_out:
+ sb->u.ext3_sb.s_block_bitmap_number[bitmap_nr] = block_group;
+ sb->u.ext3_sb.s_block_bitmap[bitmap_nr] = bh;
+ return retval;
+}
+
+/*
+ * load_block_bitmap loads the block bitmap for a blocks group
+ *
+ * It maintains a cache for the last bitmaps loaded. This cache is managed
+ * with a LRU algorithm.
+ *
+ * Notes:
+ * 1/ There is one cache per mounted file system.
+ * 2/ If the file system contains less than EXT3_MAX_GROUP_LOADED groups,
+ * this function reads the bitmap without maintaining a LRU cache.
+ *
+ * Return the slot used to store the bitmap, or a -ve error code.
+ */
+static int __load_block_bitmap (struct super_block * sb,
+ unsigned int block_group)
+{
+ int i, j, retval = 0;
+ unsigned long block_bitmap_number;
+ struct buffer_head * block_bitmap;
+
+ if (block_group >= sb->u.ext3_sb.s_groups_count)
+ ext3_panic (sb, "load_block_bitmap",
+ "block_group >= groups_count - "
+ "block_group = %d, groups_count = %lu",
+ block_group, sb->u.ext3_sb.s_groups_count);
+
+ if (sb->u.ext3_sb.s_groups_count <= EXT3_MAX_GROUP_LOADED) {
+ if (sb->u.ext3_sb.s_block_bitmap[block_group]) {
+ if (sb->u.ext3_sb.s_block_bitmap_number[block_group] ==
+ block_group)
+ return block_group;
+ ext3_error (sb, "__load_block_bitmap",
+ "block_group != block_bitmap_number");
+ }
+ retval = read_block_bitmap (sb, block_group, block_group);
+ if (retval < 0)
+ return retval;
+ return block_group;
+ }
+
+ for (i = 0; i < sb->u.ext3_sb.s_loaded_block_bitmaps &&
+ sb->u.ext3_sb.s_block_bitmap_number[i] != block_group; i++)
+ ;
+ if (i < sb->u.ext3_sb.s_loaded_block_bitmaps &&
+ sb->u.ext3_sb.s_block_bitmap_number[i] == block_group) {
+ block_bitmap_number = sb->u.ext3_sb.s_block_bitmap_number[i];
+ block_bitmap = sb->u.ext3_sb.s_block_bitmap[i];
+ for (j = i; j > 0; j--) {
+ sb->u.ext3_sb.s_block_bitmap_number[j] =
+ sb->u.ext3_sb.s_block_bitmap_number[j - 1];
+ sb->u.ext3_sb.s_block_bitmap[j] =
+ sb->u.ext3_sb.s_block_bitmap[j - 1];
+ }
+ sb->u.ext3_sb.s_block_bitmap_number[0] = block_bitmap_number;
+ sb->u.ext3_sb.s_block_bitmap[0] = block_bitmap;
+
+ /*
+ * There's still one special case here --- if block_bitmap == 0
+ * then our last attempt to read the bitmap failed and we have
+ * just ended up caching that failure. Try again to read it.
+ */
+ if (!block_bitmap)
+ retval = read_block_bitmap (sb, block_group, 0);
+ } else {
+ if (sb->u.ext3_sb.s_loaded_block_bitmaps<EXT3_MAX_GROUP_LOADED)
+ sb->u.ext3_sb.s_loaded_block_bitmaps++;
+ else
+ brelse (sb->u.ext3_sb.s_block_bitmap
+ [EXT3_MAX_GROUP_LOADED - 1]);
+ for (j = sb->u.ext3_sb.s_loaded_block_bitmaps - 1;
+ j > 0; j--) {
+ sb->u.ext3_sb.s_block_bitmap_number[j] =
+ sb->u.ext3_sb.s_block_bitmap_number[j - 1];
+ sb->u.ext3_sb.s_block_bitmap[j] =
+ sb->u.ext3_sb.s_block_bitmap[j - 1];
+ }
+ retval = read_block_bitmap (sb, block_group, 0);
+ }
+ return retval;
+}
+
+/*
+ * Load the block bitmap for a given block group. First of all do a couple
+ * of fast lookups for common cases and then pass the request onto the guts
+ * of the bitmap loader.
+ *
+ * Return the slot number of the group in the superblock bitmap cache's on
+ * success, or a -ve error code.
+ *
+ * There is still one inconsistency here --- if the number of groups in this
+ * filesystems is <= EXT3_MAX_GROUP_LOADED, then we have no way of
+ * differentiating between a group for which we have never performed a bitmap
+ * IO request, and a group for which the last bitmap read request failed.
+ */
+static inline int load_block_bitmap (struct super_block * sb,
+ unsigned int block_group)
+{
+ int slot;
+
+ /*
+ * Do the lookup for the slot. First of all, check if we're asking
+ * for the same slot as last time, and did we succeed that last time?
+ */
+ if (sb->u.ext3_sb.s_loaded_block_bitmaps > 0 &&
+ sb->u.ext3_sb.s_block_bitmap_number[0] == block_group &&
+ sb->u.ext3_sb.s_block_bitmap[0]) {
+ return 0;
+ }
+ /*
+ * Or can we do a fast lookup based on a loaded group on a filesystem
+ * small enough to be mapped directly into the superblock?
+ */
+ else if (sb->u.ext3_sb.s_groups_count <= EXT3_MAX_GROUP_LOADED &&
+ sb->u.ext3_sb.s_block_bitmap_number[block_group]==block_group
+ && sb->u.ext3_sb.s_block_bitmap[block_group]) {
+ slot = block_group;
+ }
+ /*
+ * If not, then do a full lookup for this block group.
+ */
+ else {
+ slot = __load_block_bitmap (sb, block_group);
+ }
+
+ /*
+ * <0 means we just got an error
+ */
+ if (slot < 0)
+ return slot;
+
+ /*
+ * If it's a valid slot, we may still have cached a previous IO error,
+ * in which case the bh in the superblock cache will be zero.
+ */
+ if (!sb->u.ext3_sb.s_block_bitmap[slot])
+ return -EIO;
+
+ /*
+ * Must have been read in OK to get this far.
+ */
+ return slot;
+}
+
+/* Free given blocks, update quota and i_blocks field */
+void ext3_free_blocks (handle_t *handle, struct inode * inode,
+ unsigned long block, unsigned long count)
+{
+ struct buffer_head *bitmap_bh;
+ struct buffer_head *gd_bh;
+ unsigned long block_group;
+ unsigned long bit;
+ unsigned long i;
+ int bitmap_nr;
+ unsigned long overflow;
+ struct super_block * sb;
+ struct ext3_group_desc * gdp;
+ struct ext3_super_block * es;
+ int err = 0, ret;
+ int dquot_freed_blocks = 0;
+
+ sb = inode->i_sb;
+ if (!sb) {
+ printk ("ext3_free_blocks: nonexistent device");
+ return;
+ }
+ lock_super (sb);
+ es = sb->u.ext3_sb.s_es;
+ if (block < le32_to_cpu(es->s_first_data_block) ||
+ block + count < block ||
+ (block + count) > le32_to_cpu(es->s_blocks_count)) {
+ ext3_error (sb, "ext3_free_blocks",
+ "Freeing blocks not in datazone - "
+ "block = %lu, count = %lu", block, count);
+ goto error_return;
+ }
+
+ ext3_debug ("freeing block %lu\n", block);
+
+do_more:
+ overflow = 0;
+ block_group = (block - le32_to_cpu(es->s_first_data_block)) /
+ EXT3_BLOCKS_PER_GROUP(sb);
+ bit = (block - le32_to_cpu(es->s_first_data_block)) %
+ EXT3_BLOCKS_PER_GROUP(sb);
+ /*
+ * Check to see if we are freeing blocks across a group
+ * boundary.
+ */
+ if (bit + count > EXT3_BLOCKS_PER_GROUP(sb)) {
+ overflow = bit + count - EXT3_BLOCKS_PER_GROUP(sb);
+ count -= overflow;
+ }
+ bitmap_nr = load_block_bitmap (sb, block_group);
+ if (bitmap_nr < 0)
+ goto error_return;
+
+ bitmap_bh = sb->u.ext3_sb.s_block_bitmap[bitmap_nr];
+ gdp = ext3_get_group_desc (sb, block_group, &gd_bh);
+ if (!gdp)
+ goto error_return;
+
+ /*
+ * We are about to start releasing blocks in the bitmap,
+ * so we need undo access.
+ */
+ /* @@@ check errors */
+ BUFFER_TRACE(bitmap_bh, "getting undo access");
+ err = ext3_journal_get_undo_access(handle, bitmap_bh);
+ if (err)
+ goto error_return;
+
+ /*
+ * We are about to modify some metadata. Call the journal APIs
+ * to unshare ->b_data if a currently-committing transaction is
+ * using it
+ */
+ BUFFER_TRACE(gd_bh, "get_write_access");
+ err = ext3_journal_get_write_access(handle, gd_bh);
+ if (err)
+ goto error_return;
+
+ BUFFER_TRACE(sb->u.ext3_sb.s_sbh, "get_write_access");
+ err = ext3_journal_get_write_access(handle, sb->u.ext3_sb.s_sbh);
+ if (err)
+ goto error_return;
+
+ for (i = 0; i < count; i++, block++) {
+ if (block == le32_to_cpu(gdp->bg_block_bitmap) ||
+ block == le32_to_cpu(gdp->bg_inode_bitmap) ||
+ in_range(block, le32_to_cpu(gdp->bg_inode_table),
+ EXT3_SB(sb)->s_itb_per_group)) {
+ ext3_error(sb, __FUNCTION__,
+ "Freeing block in system zone - block = %lu",
+ block);
+ continue;
+ }
+
+ /*
+ * An HJ special. This is expensive...
+ */
+#ifdef CONFIG_JBD_DEBUG
+ {
+ struct buffer_head *debug_bh;
+ debug_bh = sb_get_hash_table(sb, block);
+ if (debug_bh) {
+ BUFFER_TRACE(debug_bh, "Deleted!");
+ if (!bh2jh(bitmap_bh)->b_committed_data)
+ BUFFER_TRACE(debug_bh,
+ "No commited data in bitmap");
+ BUFFER_TRACE2(debug_bh, bitmap_bh, "bitmap");
+ __brelse(debug_bh);
+ }
+ }
+#endif
+ BUFFER_TRACE(bitmap_bh, "clear bit");
+ if (!ext3_clear_bit (bit + i, bitmap_bh->b_data)) {
+ ext3_error(sb, __FUNCTION__,
+ "bit already cleared for block %lu", block);
+ BUFFER_TRACE(bitmap_bh, "bit already cleared");
+ } else {
+ dquot_freed_blocks++;
+ gdp->bg_free_blocks_count =
+ cpu_to_le16(le16_to_cpu(gdp->bg_free_blocks_count)+1);
+ es->s_free_blocks_count =
+ cpu_to_le32(le32_to_cpu(es->s_free_blocks_count)+1);
+ }
+ /* @@@ This prevents newly-allocated data from being
+ * freed and then reallocated within the same
+ * transaction.
+ *
+ * Ideally we would want to allow that to happen, but to
+ * do so requires making journal_forget() capable of
+ * revoking the queued write of a data block, which
+ * implies blocking on the journal lock. *forget()
+ * cannot block due to truncate races.
+ *
+ * Eventually we can fix this by making journal_forget()
+ * return a status indicating whether or not it was able
+ * to revoke the buffer. On successful revoke, it is
+ * safe not to set the allocation bit in the committed
+ * bitmap, because we know that there is no outstanding
+ * activity on the buffer any more and so it is safe to
+ * reallocate it.
+ */
+ BUFFER_TRACE(bitmap_bh, "clear in b_committed_data");
+ J_ASSERT_BH(bitmap_bh,
+ bh2jh(bitmap_bh)->b_committed_data != NULL);
+ ext3_set_bit(bit + i, bh2jh(bitmap_bh)->b_committed_data);
+ }
+
+ /* We dirtied the bitmap block */
+ BUFFER_TRACE(bitmap_bh, "dirtied bitmap block");
+ err = ext3_journal_dirty_metadata(handle, bitmap_bh);
+
+ /* And the group descriptor block */
+ BUFFER_TRACE(gd_bh, "dirtied group descriptor block");
+ ret = ext3_journal_dirty_metadata(handle, gd_bh);
+ if (!err) err = ret;
+
+ /* And the superblock */
+ BUFFER_TRACE(sb->u.ext3_sb.s_sbh, "dirtied superblock");
+ ret = ext3_journal_dirty_metadata(handle, sb->u.ext3_sb.s_sbh);
+ if (!err) err = ret;
+
+ if (overflow && !err) {
+ count = overflow;
+ goto do_more;
+ }
+ sb->s_dirt = 1;
+error_return:
+ ext3_std_error(sb, err);
+ unlock_super(sb);
+ if (dquot_freed_blocks)
+ DQUOT_FREE_BLOCK(inode, dquot_freed_blocks);
+ return;
+}
+
+/* For ext3 allocations, we must not reuse any blocks which are
+ * allocated in the bitmap buffer's "last committed data" copy. This
+ * prevents deletes from freeing up the page for reuse until we have
+ * committed the delete transaction.
+ *
+ * If we didn't do this, then deleting something and reallocating it as
+ * data would allow the old block to be overwritten before the
+ * transaction committed (because we force data to disk before commit).
+ * This would lead to corruption if we crashed between overwriting the
+ * data and committing the delete.
+ *
+ * @@@ We may want to make this allocation behaviour conditional on
+ * data-writes at some point, and disable it for metadata allocations or
+ * sync-data inodes.
+ */
+static int ext3_test_allocatable(int nr, struct buffer_head *bh)
+{
+ if (ext3_test_bit(nr, bh->b_data))
+ return 0;
+ if (!buffer_jbd(bh) || !bh2jh(bh)->b_committed_data)
+ return 1;
+ return !ext3_test_bit(nr, bh2jh(bh)->b_committed_data);
+}
+
+/*
+ * Find an allocatable block in a bitmap. We honour both the bitmap and
+ * its last-committed copy (if that exists), and perform the "most
+ * appropriate allocation" algorithm of looking for a free block near
+ * the initial goal; then for a free byte somewhere in the bitmap; then
+ * for any free bit in the bitmap.
+ */
+static int find_next_usable_block(int start,
+ struct buffer_head *bh, int maxblocks)
+{
+ int here, next;
+ char *p, *r;
+
+ if (start > 0) {
+ /*
+ * The goal was occupied; search forward for a free
+ * block within the next XX blocks.
+ *
+ * end_goal is more or less random, but it has to be
+ * less than EXT3_BLOCKS_PER_GROUP. Aligning up to the
+ * next 64-bit boundary is simple..
+ */
+ int end_goal = (start + 63) & ~63;
+ here = ext3_find_next_zero_bit(bh->b_data, end_goal, start);
+ if (here < end_goal && ext3_test_allocatable(here, bh))
+ return here;
+
+ ext3_debug ("Bit not found near goal\n");
+
+ }
+
+ here = start;
+ if (here < 0)
+ here = 0;
+
+ /*
+ * There has been no free block found in the near vicinity 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
+ */
+ p = ((char *) bh->b_data) + (here >> 3);
+ r = memscan(p, 0, (maxblocks - here + 7) >> 3);
+ next = (r - ((char *) bh->b_data)) << 3;
+
+ if (next < maxblocks && ext3_test_allocatable(next, bh))
+ return next;
+
+ /* The bitmap search --- search forward alternately
+ * through the actual bitmap and the last-committed copy
+ * until we find a bit free in both. */
+
+ while (here < maxblocks) {
+ next = ext3_find_next_zero_bit ((unsigned long *) bh->b_data,
+ maxblocks, here);
+ if (next >= maxblocks)
+ return -1;
+ if (ext3_test_allocatable(next, bh))
+ return next;
+
+ J_ASSERT_BH(bh, bh2jh(bh)->b_committed_data);
+ here = ext3_find_next_zero_bit
+ ((unsigned long *) bh2jh(bh)->b_committed_data,
+ maxblocks, next);
+ }
+ return -1;
+}
+
+/*
+ * ext3_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
+ * 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.
+ * This function also updates quota and i_blocks field.
+ */
+int ext3_new_block (handle_t *handle, struct inode * inode,
+ unsigned long goal, u32 * prealloc_count,
+ u32 * prealloc_block, int * errp)
+{
+ struct buffer_head * bh, *bhtmp;
+ struct buffer_head * bh2;
+#if 0
+ char * p, * r;
+#endif
+ int i, j, k, tmp, alloctmp;
+ int bitmap_nr;
+ int fatal = 0, err;
+ int performed_allocation = 0;
+ struct super_block * sb;
+ struct ext3_group_desc * gdp;
+ struct ext3_super_block * es;
+#ifdef EXT3FS_DEBUG
+ static int goal_hits = 0, goal_attempts = 0;
+#endif
+ *errp = -ENOSPC;
+ sb = inode->i_sb;
+ if (!sb) {
+ printk ("ext3_new_block: nonexistent device");
+ return 0;
+ }
+
+ /*
+ * Check quota for allocation of this block.
+ */
+ if (DQUOT_ALLOC_BLOCK(inode, 1)) {
+ *errp = -EDQUOT;
+ return 0;
+ }
+
+ lock_super (sb);
+ es = sb->u.ext3_sb.s_es;
+ if (le32_to_cpu(es->s_free_blocks_count) <=
+ le32_to_cpu(es->s_r_blocks_count) &&
+ ((sb->u.ext3_sb.s_resuid != current->fsuid) &&
+ (sb->u.ext3_sb.s_resgid == 0 ||
+ !in_group_p (sb->u.ext3_sb.s_resgid)) &&
+ !capable(CAP_SYS_RESOURCE)))
+ goto out;
+
+ ext3_debug ("goal=%lu.\n", goal);
+
+repeat:
+ /*
+ * First, test whether the goal block is free.
+ */
+ if (goal < le32_to_cpu(es->s_first_data_block) ||
+ goal >= le32_to_cpu(es->s_blocks_count))
+ goal = le32_to_cpu(es->s_first_data_block);
+ i = (goal - le32_to_cpu(es->s_first_data_block)) /
+ EXT3_BLOCKS_PER_GROUP(sb);
+ gdp = ext3_get_group_desc (sb, i, &bh2);
+ if (!gdp)
+ goto io_error;
+
+ if (le16_to_cpu(gdp->bg_free_blocks_count) > 0) {
+ j = ((goal - le32_to_cpu(es->s_first_data_block)) %
+ EXT3_BLOCKS_PER_GROUP(sb));
+#ifdef EXT3FS_DEBUG
+ if (j)
+ goal_attempts++;
+#endif
+ bitmap_nr = load_block_bitmap (sb, i);
+ if (bitmap_nr < 0)
+ goto io_error;
+
+ bh = sb->u.ext3_sb.s_block_bitmap[bitmap_nr];
+
+ ext3_debug ("goal is at %d:%d.\n", i, j);
+
+ if (ext3_test_allocatable(j, bh)) {
+#ifdef EXT3FS_DEBUG
+ goal_hits++;
+ ext3_debug ("goal bit allocated.\n");
+#endif
+ goto got_block;
+ }
+
+ j = find_next_usable_block(j, bh, EXT3_BLOCKS_PER_GROUP(sb));
+ if (j >= 0)
+ goto search_back;
+ }
+
+ ext3_debug ("Bit not found in block group %d.\n", i);
+
+ /*
+ * Now search the rest of the groups. We assume that
+ * i and gdp correctly point to the last group visited.
+ */
+ for (k = 0; k < sb->u.ext3_sb.s_groups_count; k++) {
+ i++;
+ if (i >= sb->u.ext3_sb.s_groups_count)
+ i = 0;
+ gdp = ext3_get_group_desc (sb, i, &bh2);
+ if (!gdp) {
+ *errp = -EIO;
+ goto out;
+ }
+ if (le16_to_cpu(gdp->bg_free_blocks_count) > 0) {
+ bitmap_nr = load_block_bitmap (sb, i);
+ if (bitmap_nr < 0)
+ goto io_error;
+
+ bh = sb->u.ext3_sb.s_block_bitmap[bitmap_nr];
+ j = find_next_usable_block(-1, bh,
+ EXT3_BLOCKS_PER_GROUP(sb));
+ if (j >= 0)
+ goto search_back;
+ }
+ }
+
+ /* No space left on the device */
+ goto out;
+
+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.
+ */
+ for ( k = 0;
+ k < 7 && j > 0 && ext3_test_allocatable(j - 1, bh);
+ k++, j--)
+ ;
+
+got_block:
+
+ ext3_debug ("using block group %d(%d)\n", i, gdp->bg_free_blocks_count);
+
+ /* Make sure we use undo access for the bitmap, because it is
+ critical that we do the frozen_data COW on bitmap buffers in
+ all cases even if the buffer is in BJ_Forget state in the
+ committing transaction. */
+ BUFFER_TRACE(bh, "get undo access for marking new block");
+ fatal = ext3_journal_get_undo_access(handle, bh);
+ if (fatal) goto out;
+
+ BUFFER_TRACE(bh2, "get_write_access");
+ fatal = ext3_journal_get_write_access(handle, bh2);
+ if (fatal) goto out;
+
+ BUFFER_TRACE(sb->u.ext3_sb.s_sbh, "get_write_access");
+ fatal = ext3_journal_get_write_access(handle, sb->u.ext3_sb.s_sbh);
+ if (fatal) goto out;
+
+ tmp = j + i * EXT3_BLOCKS_PER_GROUP(sb)
+ + le32_to_cpu(es->s_first_data_block);
+
+ if (tmp == le32_to_cpu(gdp->bg_block_bitmap) ||
+ tmp == le32_to_cpu(gdp->bg_inode_bitmap) ||
+ in_range (tmp, le32_to_cpu(gdp->bg_inode_table),
+ EXT3_SB(sb)->s_itb_per_group)) {
+ ext3_error(sb, __FUNCTION__,
+ "Allocating block in system zone - block = %u", tmp);
+
+ /* Note: This will potentially use up one of the handle's
+ * buffer credits. Normally we have way too many credits,
+ * so that is OK. In _very_ rare cases it might not be OK.
+ * We will trigger an assertion if we run out of credits,
+ * and we will have to do a full fsck of the filesystem -
+ * better than randomly corrupting filesystem metadata.
+ */
+ ext3_set_bit(j, bh->b_data);
+ goto repeat;
+ }
+
+
+ /* The superblock lock should guard against anybody else beating
+ * us to this point! */
+ J_ASSERT_BH(bh, !ext3_test_bit(j, bh->b_data));
+ BUFFER_TRACE(bh, "setting bitmap bit");
+ ext3_set_bit(j, bh->b_data);
+ performed_allocation = 1;
+
+#ifdef CONFIG_JBD_DEBUG
+ {
+ struct buffer_head *debug_bh;
+
+ /* Record bitmap buffer state in the newly allocated block */
+ debug_bh = sb_get_hash_table(sb, tmp);
+ if (debug_bh) {
+ BUFFER_TRACE(debug_bh, "state when allocated");
+ BUFFER_TRACE2(debug_bh, bh, "bitmap state");
+ brelse(debug_bh);
+ }
+ }
+#endif
+ if (buffer_jbd(bh) && bh2jh(bh)->b_committed_data)
+ J_ASSERT_BH(bh, !ext3_test_bit(j, bh2jh(bh)->b_committed_data));
+ bhtmp = bh;
+ alloctmp = j;
+
+ ext3_debug ("found bit %d\n", j);
+
+ /*
+ * Do block preallocation now if required.
+ */
+#ifdef EXT3_PREALLOCATE
+ /*
+ * akpm: this is not enabled for ext3. Need to use
+ * ext3_test_allocatable()
+ */
+ /* Writer: ->i_prealloc* */
+ if (prealloc_count && !*prealloc_count) {
+ int prealloc_goal;
+ unsigned long next_block = tmp + 1;
+
+ prealloc_goal = es->s_prealloc_blocks ?
+ es->s_prealloc_blocks : EXT3_DEFAULT_PREALLOC_BLOCKS;
+
+ *prealloc_block = next_block;
+ /* Writer: end */
+ for (k = 1;
+ k < prealloc_goal && (j + k) < EXT3_BLOCKS_PER_GROUP(sb);
+ k++, next_block++) {
+ if (DQUOT_PREALLOC_BLOCK(inode, 1))
+ break;
+ /* Writer: ->i_prealloc* */
+ if (*prealloc_block + *prealloc_count != next_block ||
+ ext3_set_bit (j + k, bh->b_data)) {
+ /* Writer: end */
+ DQUOT_FREE_BLOCK(inode, 1);
+ break;
+ }
+ (*prealloc_count)++;
+ /* Writer: end */
+ }
+ /*
+ * As soon as we go for per-group spinlocks we'll need these
+ * done inside the loop above.
+ */
+ gdp->bg_free_blocks_count =
+ cpu_to_le16(le16_to_cpu(gdp->bg_free_blocks_count) -
+ (k - 1));
+ es->s_free_blocks_count =
+ cpu_to_le32(le32_to_cpu(es->s_free_blocks_count) -
+ (k - 1));
+ ext3_debug ("Preallocated a further %lu bits.\n",
+ (k - 1));
+ }
+#endif
+
+ j = tmp;
+
+ BUFFER_TRACE(bh, "journal_dirty_metadata for bitmap block");
+ err = ext3_journal_dirty_metadata(handle, bh);
+ if (!fatal) fatal = err;
+
+ if (j >= le32_to_cpu(es->s_blocks_count)) {
+ ext3_error (sb, "ext3_new_block",
+ "block(%d) >= blocks count(%d) - "
+ "block_group = %d, es == %p ",j,
+ le32_to_cpu(es->s_blocks_count), i, es);
+ goto out;
+ }
+
+ /*
+ * It is up to the caller to add the new buffer to a journal
+ * list of some description. We don't know in advance whether
+ * the caller wants to use it as metadata or data.
+ */
+
+ ext3_debug ("allocating block %d. "
+ "Goal hits %d of %d.\n", j, goal_hits, goal_attempts);
+
+ gdp->bg_free_blocks_count =
+ cpu_to_le16(le16_to_cpu(gdp->bg_free_blocks_count) - 1);
+ es->s_free_blocks_count =
+ cpu_to_le32(le32_to_cpu(es->s_free_blocks_count) - 1);
+
+ BUFFER_TRACE(bh2, "journal_dirty_metadata for group descriptor");
+ err = ext3_journal_dirty_metadata(handle, bh2);
+ if (!fatal) fatal = err;
+
+ BUFFER_TRACE(bh, "journal_dirty_metadata for superblock");
+ err = ext3_journal_dirty_metadata(handle, sb->u.ext3_sb.s_sbh);
+ if (!fatal) fatal = err;
+
+ sb->s_dirt = 1;
+ if (fatal)
+ goto out;
+
+ unlock_super (sb);
+ *errp = 0;
+ return j;
+
+io_error:
+ *errp = -EIO;
+out:
+ if (fatal) {
+ *errp = fatal;
+ ext3_std_error(sb, fatal);
+ }
+ unlock_super (sb);
+ /*
+ * Undo the block allocation
+ */
+ if (!performed_allocation)
+ DQUOT_FREE_BLOCK(inode, 1);
+ return 0;
+
+}
+
+unsigned long ext3_count_free_blocks (struct super_block * sb)
+{
+#ifdef EXT3FS_DEBUG
+ struct ext3_super_block * es;
+ unsigned long desc_count, bitmap_count, x;
+ int bitmap_nr;
+ struct ext3_group_desc * gdp;
+ int i;
+
+ lock_super (sb);
+ es = sb->u.ext3_sb.s_es;
+ desc_count = 0;
+ bitmap_count = 0;
+ gdp = NULL;
+ for (i = 0; i < sb->u.ext3_sb.s_groups_count; i++) {
+ gdp = ext3_get_group_desc (sb, i, NULL);
+ if (!gdp)
+ continue;
+ desc_count += le16_to_cpu(gdp->bg_free_blocks_count);
+ bitmap_nr = load_block_bitmap (sb, i);
+ if (bitmap_nr < 0)
+ continue;
+
+ x = ext3_count_free (sb->u.ext3_sb.s_block_bitmap[bitmap_nr],
+ sb->s_blocksize);
+ printk ("group %d: stored = %d, counted = %lu\n",
+ i, le16_to_cpu(gdp->bg_free_blocks_count), x);
+ bitmap_count += x;
+ }
+ printk("ext3_count_free_blocks: stored = %lu, computed = %lu, %lu\n",
+ le32_to_cpu(es->s_free_blocks_count), desc_count, bitmap_count);
+ unlock_super (sb);
+ return bitmap_count;
+#else
+ return le32_to_cpu(sb->u.ext3_sb.s_es->s_free_blocks_count);
+#endif
+}
+
+static inline int block_in_use (unsigned long block,
+ struct super_block * sb,
+ unsigned char * map)
+{
+ return ext3_test_bit ((block -
+ le32_to_cpu(sb->u.ext3_sb.s_es->s_first_data_block)) %
+ EXT3_BLOCKS_PER_GROUP(sb), map);
+}
+
+static 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;
+ }
+}
+
+int ext3_group_sparse(int group)
+{
+ return (test_root(group, 3) || test_root(group, 5) ||
+ test_root(group, 7));
+}
+
+/**
+ * ext3_bg_has_super - number of blocks used by the superblock in group
+ * @sb: superblock for filesystem
+ * @group: group number to check
+ *
+ * Return the number of blocks used by the superblock (primary or backup)
+ * in this group. Currently this will be only 0 or 1.
+ */
+int ext3_bg_has_super(struct super_block *sb, int group)
+{
+ if (EXT3_HAS_RO_COMPAT_FEATURE(sb,EXT3_FEATURE_RO_COMPAT_SPARSE_SUPER)&&
+ !ext3_group_sparse(group))
+ return 0;
+ return 1;
+}
+
+/**
+ * ext3_bg_num_gdb - number of blocks used by the group table in group
+ * @sb: superblock for filesystem
+ * @group: group number to check
+ *
+ * Return the number of blocks used by the group descriptor table
+ * (primary or backup) in this group. In the future there may be a
+ * different number of descriptor blocks in each group.
+ */
+unsigned long ext3_bg_num_gdb(struct super_block *sb, int group)
+{
+ if (EXT3_HAS_RO_COMPAT_FEATURE(sb,EXT3_FEATURE_RO_COMPAT_SPARSE_SUPER)&&
+ !ext3_group_sparse(group))
+ return 0;
+ return EXT3_SB(sb)->s_gdb_count;
+}
+
+#ifdef CONFIG_EXT3_CHECK
+/* Called at mount-time, super-block is locked */
+void ext3_check_blocks_bitmap (struct super_block * sb)
+{
+ struct buffer_head * bh;
+ struct ext3_super_block * es;
+ unsigned long desc_count, bitmap_count, x, j;
+ unsigned long desc_blocks;
+ int bitmap_nr;
+ struct ext3_group_desc * gdp;
+ int i;
+
+ es = sb->u.ext3_sb.s_es;
+ desc_count = 0;
+ bitmap_count = 0;
+ gdp = NULL;
+ for (i = 0; i < sb->u.ext3_sb.s_groups_count; i++) {
+ gdp = ext3_get_group_desc (sb, i, NULL);
+ if (!gdp)
+ continue;
+ desc_count += le16_to_cpu(gdp->bg_free_blocks_count);
+ bitmap_nr = load_block_bitmap (sb, i);
+ if (bitmap_nr < 0)
+ continue;
+
+ bh = EXT3_SB(sb)->s_block_bitmap[bitmap_nr];
+
+ if (ext3_bg_has_super(sb, i) && !ext3_test_bit(0, bh->b_data))
+ ext3_error(sb, __FUNCTION__,
+ "Superblock in group %d is marked free", i);
+
+ desc_blocks = ext3_bg_num_gdb(sb, i);
+ for (j = 0; j < desc_blocks; j++)
+ if (!ext3_test_bit(j + 1, bh->b_data))
+ ext3_error(sb, __FUNCTION__,
+ "Descriptor block #%ld in group "
+ "%d is marked free", j, i);
+
+ if (!block_in_use (le32_to_cpu(gdp->bg_block_bitmap),
+ sb, bh->b_data))
+ ext3_error (sb, "ext3_check_blocks_bitmap",
+ "Block bitmap for group %d is marked free",
+ i);
+
+ if (!block_in_use (le32_to_cpu(gdp->bg_inode_bitmap),
+ sb, bh->b_data))
+ ext3_error (sb, "ext3_check_blocks_bitmap",
+ "Inode bitmap for group %d is marked free",
+ i);
+
+ for (j = 0; j < sb->u.ext3_sb.s_itb_per_group; j++)
+ if (!block_in_use (le32_to_cpu(gdp->bg_inode_table) + j,
+ sb, bh->b_data))
+ ext3_error (sb, "ext3_check_blocks_bitmap",
+ "Block #%d of the inode table in "
+ "group %d is marked free", j, i);
+
+ x = ext3_count_free (bh, sb->s_blocksize);
+ if (le16_to_cpu(gdp->bg_free_blocks_count) != x)
+ ext3_error (sb, "ext3_check_blocks_bitmap",
+ "Wrong free blocks count for group %d, "
+ "stored = %d, counted = %lu", i,
+ le16_to_cpu(gdp->bg_free_blocks_count), x);
+ bitmap_count += x;
+ }
+ if (le32_to_cpu(es->s_free_blocks_count) != bitmap_count)
+ ext3_error (sb, "ext3_check_blocks_bitmap",
+ "Wrong free blocks count in super block, "
+ "stored = %lu, counted = %lu",
+ (unsigned long)le32_to_cpu(es->s_free_blocks_count),
+ bitmap_count);
+}
+#endif
diff --git a/fs/ext3/bitmap.c b/fs/ext3/bitmap.c
new file mode 100644
index 00000000000000..4247a500d46930
--- /dev/null
+++ b/fs/ext3/bitmap.c
@@ -0,0 +1,26 @@
+/*
+ * linux/fs/ext3/bitmap.c
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ */
+
+#include <linux/fs.h>
+
+
+static int nibblemap[] = {4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0};
+
+unsigned long ext3_count_free (struct buffer_head * map, unsigned int numchars)
+{
+ unsigned int i;
+ unsigned long sum = 0;
+
+ if (!map)
+ return (0);
+ for (i = 0; i < numchars; i++)
+ sum += nibblemap[map->b_data[i] & 0xf] +
+ nibblemap[(map->b_data[i] >> 4) & 0xf];
+ return (sum);
+}
diff --git a/fs/ext3/dir.c b/fs/ext3/dir.c
new file mode 100644
index 00000000000000..20841818f819c9
--- /dev/null
+++ b/fs/ext3/dir.c
@@ -0,0 +1,190 @@
+/*
+ * linux/fs/ext3/dir.c
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * from
+ *
+ * linux/fs/minix/dir.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * ext3 directory handling functions
+ *
+ * Big-endian to little-endian byte-swapping/bitmaps by
+ * David S. Miller (davem@caip.rutgers.edu), 1995
+ */
+
+#include <linux/fs.h>
+#include <linux/jbd.h>
+#include <linux/ext3_fs.h>
+
+static unsigned char ext3_filetype_table[] = {
+ DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK, DT_FIFO, DT_SOCK, DT_LNK
+};
+
+static int ext3_readdir(struct file *, void *, filldir_t);
+
+struct file_operations ext3_dir_operations = {
+ read: generic_read_dir,
+ readdir: ext3_readdir, /* BKL held */
+ ioctl: ext3_ioctl, /* BKL held */
+ fsync: ext3_sync_file, /* BKL held */
+};
+
+int ext3_check_dir_entry (const char * function, struct inode * dir,
+ struct ext3_dir_entry_2 * de,
+ struct buffer_head * bh,
+ unsigned long offset)
+{
+ const char * error_msg = NULL;
+ const int rlen = le16_to_cpu(de->rec_len);
+
+ if (rlen < EXT3_DIR_REC_LEN(1))
+ error_msg = "rec_len is smaller than minimal";
+ else if (rlen % 4 != 0)
+ error_msg = "rec_len % 4 != 0";
+ else if (rlen < EXT3_DIR_REC_LEN(de->name_len))
+ error_msg = "rec_len is too small for name_len";
+ else if (((char *) de - bh->b_data) + rlen > dir->i_sb->s_blocksize)
+ error_msg = "directory entry across blocks";
+ else if (le32_to_cpu(de->inode) >
+ le32_to_cpu(dir->i_sb->u.ext3_sb.s_es->s_inodes_count))
+ error_msg = "inode out of bounds";
+
+ if (error_msg != NULL)
+ ext3_error (dir->i_sb, function,
+ "bad entry in directory #%lu: %s - "
+ "offset=%lu, inode=%lu, rec_len=%d, name_len=%d",
+ dir->i_ino, error_msg, offset,
+ (unsigned long) le32_to_cpu(de->inode),
+ rlen, de->name_len);
+ return error_msg == NULL ? 1 : 0;
+}
+
+static int ext3_readdir(struct file * filp,
+ void * dirent, filldir_t filldir)
+{
+ int error = 0;
+ unsigned long offset, blk;
+ int i, num, stored;
+ struct buffer_head * bh, * tmp, * bha[16];
+ struct ext3_dir_entry_2 * de;
+ struct super_block * sb;
+ int err;
+ struct inode *inode = filp->f_dentry->d_inode;
+
+ sb = inode->i_sb;
+
+ stored = 0;
+ bh = NULL;
+ offset = filp->f_pos & (sb->s_blocksize - 1);
+
+ while (!error && !stored && filp->f_pos < inode->i_size) {
+ blk = (filp->f_pos) >> EXT3_BLOCK_SIZE_BITS(sb);
+ bh = ext3_bread (0, inode, blk, 0, &err);
+ if (!bh) {
+ ext3_error (sb, "ext3_readdir",
+ "directory #%lu contains a hole at offset %lu",
+ inode->i_ino, (unsigned long)filp->f_pos);
+ filp->f_pos += sb->s_blocksize - offset;
+ continue;
+ }
+
+ /*
+ * Do the readahead
+ */
+ if (!offset) {
+ for (i = 16 >> (EXT3_BLOCK_SIZE_BITS(sb) - 9), num = 0;
+ i > 0; i--) {
+ tmp = ext3_getblk (NULL, inode, ++blk, 0, &err);
+ if (tmp && !buffer_uptodate(tmp) &&
+ !buffer_locked(tmp))
+ bha[num++] = tmp;
+ else
+ brelse (tmp);
+ }
+ if (num) {
+ ll_rw_block (READA, num, bha);
+ for (i = 0; i < num; i++)
+ brelse (bha[i]);
+ }
+ }
+
+revalidate:
+ /* If the dir block has changed since the last call to
+ * readdir(2), then we might be pointing to an invalid
+ * dirent right now. Scan from the start of the block
+ * to make sure. */
+ if (filp->f_version != inode->i_version) {
+ for (i = 0; i < sb->s_blocksize && i < offset; ) {
+ de = (struct ext3_dir_entry_2 *)
+ (bh->b_data + i);
+ /* It's too expensive to do a full
+ * dirent test each time round this
+ * loop, but we do have to test at
+ * least that it is non-zero. A
+ * failure will be detected in the
+ * dirent test below. */
+ if (le16_to_cpu(de->rec_len) <
+ EXT3_DIR_REC_LEN(1))
+ break;
+ i += le16_to_cpu(de->rec_len);
+ }
+ offset = i;
+ filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1))
+ | offset;
+ filp->f_version = inode->i_version;
+ }
+
+ while (!error && filp->f_pos < inode->i_size
+ && offset < sb->s_blocksize) {
+ de = (struct ext3_dir_entry_2 *) (bh->b_data + offset);
+ if (!ext3_check_dir_entry ("ext3_readdir", inode, de,
+ bh, offset)) {
+ /* On error, skip the f_pos to the
+ next block. */
+ filp->f_pos = (filp->f_pos |
+ (sb->s_blocksize - 1)) + 1;
+ brelse (bh);
+ return stored;
+ }
+ offset += le16_to_cpu(de->rec_len);
+ if (le32_to_cpu(de->inode)) {
+ /* We might block in the next section
+ * if the data destination is
+ * currently swapped out. So, use a
+ * version stamp to detect whether or
+ * not the directory has been modified
+ * during the copy operation.
+ */
+ unsigned long version = filp->f_version;
+ unsigned char d_type = DT_UNKNOWN;
+
+ if (EXT3_HAS_INCOMPAT_FEATURE(sb,
+ EXT3_FEATURE_INCOMPAT_FILETYPE)
+ && de->file_type < EXT3_FT_MAX)
+ d_type =
+ ext3_filetype_table[de->file_type];
+ error = filldir(dirent, de->name,
+ de->name_len,
+ filp->f_pos,
+ le32_to_cpu(de->inode),
+ d_type);
+ if (error)
+ break;
+ if (version != filp->f_version)
+ goto revalidate;
+ stored ++;
+ }
+ filp->f_pos += le16_to_cpu(de->rec_len);
+ }
+ offset = 0;
+ brelse (bh);
+ }
+ UPDATE_ATIME(inode);
+ return 0;
+}
diff --git a/fs/ext3/file.c b/fs/ext3/file.c
new file mode 100644
index 00000000000000..e480751d02fc48
--- /dev/null
+++ b/fs/ext3/file.c
@@ -0,0 +1,128 @@
+/*
+ * linux/fs/ext3/file.c
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * from
+ *
+ * linux/fs/minix/file.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * ext3 fs regular file handling primitives
+ *
+ * 64-bit file support on 64-bit platforms by Jakub Jelinek
+ * (jj@sunsite.ms.mff.cuni.cz)
+ */
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/locks.h>
+#include <linux/jbd.h>
+#include <linux/ext3_fs.h>
+#include <linux/ext3_jbd.h>
+#include <linux/smp_lock.h>
+
+/*
+ * Called when an inode is released. Note that this is different
+ * from ext3_file_open: open gets called at every open, but release
+ * gets called only when /all/ the files are closed.
+ */
+static int ext3_release_file (struct inode * inode, struct file * filp)
+{
+ if (filp->f_mode & FMODE_WRITE)
+ ext3_discard_prealloc (inode);
+ return 0;
+}
+
+/*
+ * Called when an inode is about to be opened.
+ * We use this to disallow opening RW large files on 32bit systems if
+ * the caller didn't specify O_LARGEFILE. On 64bit systems we force
+ * on this flag in sys_open.
+ */
+static int ext3_open_file (struct inode * inode, struct file * filp)
+{
+ if (!(filp->f_flags & O_LARGEFILE) &&
+ inode->i_size > 0x7FFFFFFFLL)
+ return -EFBIG;
+ return 0;
+}
+
+/*
+ * ext3_file_write().
+ *
+ * Most things are done in ext3_prepare_write() and ext3_commit_write().
+ */
+
+static ssize_t
+ext3_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
+{
+ ssize_t ret;
+ int err;
+ struct inode *inode = file->f_dentry->d_inode;
+
+ ret = generic_file_write(file, buf, count, ppos);
+
+ /* Skip file flushing code if there was an error, or if nothing
+ was written. */
+ if (ret <= 0)
+ return ret;
+
+ /* If the inode is IS_SYNC, or is O_SYNC and we are doing
+ data-journaling, then we need to make sure that we force the
+ transaction to disk to keep all metadata uptodate
+ synchronously. */
+
+ if (file->f_flags & O_SYNC) {
+ /* If we are non-data-journaled, then the dirty data has
+ already been flushed to backing store by
+ generic_osync_inode, and the inode has been flushed
+ too if there have been any modifications other than
+ mere timestamp updates.
+
+ Open question --- do we care about flushing
+ timestamps too if the inode is IS_SYNC? */
+ if (!ext3_should_journal_data(inode))
+ return ret;
+
+ goto force_commit;
+ }
+
+ /* So we know that there has been no forced data flush. If the
+ inode is marked IS_SYNC, we need to force one ourselves. */
+ if (!IS_SYNC(inode))
+ return ret;
+
+ /* Open question #2 --- should we force data to disk here too?
+ If we don't, the only impact is that data=writeback
+ filesystems won't flush data to disk automatically on
+ IS_SYNC, only metadata (but historically, that is what ext2
+ has done.) */
+
+force_commit:
+ err = ext3_force_commit(inode->i_sb);
+ if (err)
+ return err;
+ return ret;
+}
+
+struct file_operations ext3_file_operations = {
+ llseek: generic_file_llseek, /* BKL held */
+ read: generic_file_read, /* BKL not held. Don't need */
+ write: ext3_file_write, /* BKL not held. Don't need */
+ ioctl: ext3_ioctl, /* BKL held */
+ mmap: generic_file_mmap,
+ open: ext3_open_file, /* BKL not held. Don't need */
+ release: ext3_release_file, /* BKL not held. Don't need */
+ fsync: ext3_sync_file, /* BKL held */
+};
+
+struct inode_operations ext3_file_inode_operations = {
+ truncate: ext3_truncate, /* BKL held */
+ setattr: ext3_setattr, /* BKL held */
+};
+
diff --git a/fs/ext3/fsync.c b/fs/ext3/fsync.c
new file mode 100644
index 00000000000000..1a52049bb5f7b8
--- /dev/null
+++ b/fs/ext3/fsync.c
@@ -0,0 +1,75 @@
+/*
+ * linux/fs/ext3/fsync.c
+ *
+ * Copyright (C) 1993 Stephen Tweedie (sct@redhat.com)
+ * from
+ * Copyright (C) 1992 Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ * from
+ * linux/fs/minix/truncate.c Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * ext3fs fsync primitive
+ *
+ * Big-endian to little-endian byte-swapping/bitmaps by
+ * David S. Miller (davem@caip.rutgers.edu), 1995
+ *
+ * Removed unnecessary code duplication for little endian machines
+ * and excessive __inline__s.
+ * Andi Kleen, 1997
+ *
+ * Major simplications and cleanup - we only need to do the metadata, because
+ * we can depend on generic_block_fdatasync() to sync the data blocks.
+ */
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/jbd.h>
+#include <linux/ext3_fs.h>
+#include <linux/ext3_jbd.h>
+#include <linux/jbd.h>
+#include <linux/smp_lock.h>
+
+/*
+ * akpm: A new design for ext3_sync_file().
+ *
+ * This is only called from sys_fsync(), sys_fdatasync() and sys_msync().
+ * There cannot be a transaction open by this task. (AKPM: quotas?)
+ * Another task could have dirtied this inode. Its data can be in any
+ * state in the journalling system.
+ *
+ * What we do is just kick off a commit and wait on it. This will snapshot the
+ * inode to disk.
+ *
+ * Note that there is a serious optimisation we can make here: if the current
+ * inode is not part of j_running_transaction or j_committing_transaction
+ * then we have nothing to do. That would require implementation of t_ilist,
+ * which isn't too hard.
+ */
+
+int ext3_sync_file(struct file * file, struct dentry *dentry, int datasync)
+{
+ struct inode *inode = dentry->d_inode;
+ int ret;
+
+ J_ASSERT(ext3_journal_current_handle() == 0);
+
+ /*
+ * fsync_inode_buffers() just walks i_dirty_buffers and waits
+ * on them. It's a no-op for full data journalling because
+ * i_dirty_buffers will be ampty.
+ * Really, we only need to start I/O on the dirty buffers -
+ * we'll end up waiting on them in commit.
+ */
+ ret = fsync_inode_buffers(inode);
+
+ /* In writeback mode, we need to force out data buffers too. In
+ * the other modes, ext3_force_commit takes care of forcing out
+ * just the right data blocks. */
+ if (test_opt(inode->i_sb, DATA_FLAGS) == EXT3_MOUNT_WRITEBACK_DATA)
+ ret |= fsync_inode_data_buffers(inode);
+
+ ext3_force_commit(inode->i_sb);
+
+ return ret;
+}
diff --git a/fs/ext3/ialloc.c b/fs/ext3/ialloc.c
new file mode 100644
index 00000000000000..fbb5dc3fe9e011
--- /dev/null
+++ b/fs/ext3/ialloc.c
@@ -0,0 +1,663 @@
+/*
+ * linux/fs/ext3/ialloc.c
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * BSD ufs-inspired inode and directory allocation by
+ * Stephen Tweedie (sct@redhat.com), 1993
+ * Big-endian to little-endian byte-swapping/bitmaps by
+ * David S. Miller (davem@caip.rutgers.edu), 1995
+ */
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/jbd.h>
+#include <linux/ext3_fs.h>
+#include <linux/ext3_jbd.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/quotaops.h>
+
+#include <asm/bitops.h>
+#include <asm/byteorder.h>
+
+/*
+ * ialloc.c contains the inodes allocation and deallocation routines
+ */
+
+/*
+ * The free inodes are managed by bitmaps. A file system contains several
+ * blocks groups. Each group contains 1 bitmap block for blocks, 1 bitmap
+ * block for inodes, N blocks for the inode table and data blocks.
+ *
+ * The file system contains group descriptors which are located after the
+ * super block. Each descriptor contains the number of the bitmap block and
+ * the free blocks count in the block. The descriptors are loaded in memory
+ * when a file system is mounted (see ext3_read_super).
+ */
+
+
+/*
+ * Read the inode allocation bitmap for a given block_group, reading
+ * into the specified slot in the superblock's bitmap cache.
+ *
+ * Return >=0 on success or a -ve error code.
+ */
+static int read_inode_bitmap (struct super_block * sb,
+ unsigned long block_group,
+ unsigned int bitmap_nr)
+{
+ struct ext3_group_desc * gdp;
+ struct buffer_head * bh = NULL;
+ int retval = 0;
+
+ gdp = ext3_get_group_desc (sb, block_group, NULL);
+ if (!gdp) {
+ retval = -EIO;
+ goto error_out;
+ }
+ bh = sb_bread(sb, le32_to_cpu(gdp->bg_inode_bitmap));
+ if (!bh) {
+ ext3_error (sb, "read_inode_bitmap",
+ "Cannot read inode bitmap - "
+ "block_group = %lu, inode_bitmap = %u",
+ block_group, gdp->bg_inode_bitmap);
+ retval = -EIO;
+ }
+ /*
+ * On IO error, just leave a zero in the superblock's block pointer for
+ * this group. The IO will be retried next time.
+ */
+error_out:
+ sb->u.ext3_sb.s_inode_bitmap_number[bitmap_nr] = block_group;
+ sb->u.ext3_sb.s_inode_bitmap[bitmap_nr] = bh;
+ return retval;
+}
+
+/*
+ * load_inode_bitmap loads the inode bitmap for a blocks group
+ *
+ * It maintains a cache for the last bitmaps loaded. This cache is managed
+ * with a LRU algorithm.
+ *
+ * Notes:
+ * 1/ There is one cache per mounted file system.
+ * 2/ If the file system contains less than EXT3_MAX_GROUP_LOADED groups,
+ * this function reads the bitmap without maintaining a LRU cache.
+ *
+ * Return the slot used to store the bitmap, or a -ve error code.
+ */
+static int load_inode_bitmap (struct super_block * sb,
+ unsigned int block_group)
+{
+ struct ext3_sb_info *sbi = EXT3_SB(sb);
+ unsigned long inode_bitmap_number;
+ struct buffer_head * inode_bitmap;
+ int i, j, retval = 0;
+
+ if (block_group >= sbi->s_groups_count)
+ ext3_panic (sb, "load_inode_bitmap",
+ "block_group >= groups_count - "
+ "block_group = %d, groups_count = %lu",
+ block_group, sbi->s_groups_count);
+ if (sbi->s_loaded_inode_bitmaps > 0 &&
+ sbi->s_inode_bitmap_number[0] == block_group &&
+ sbi->s_inode_bitmap[0] != NULL)
+ return 0;
+ if (sbi->s_groups_count <= EXT3_MAX_GROUP_LOADED) {
+ if (sbi->s_inode_bitmap[block_group]) {
+ if (sbi->s_inode_bitmap_number[block_group] !=
+ block_group)
+ ext3_panic(sb, "load_inode_bitmap",
+ "block_group != inode_bitmap_number");
+ return block_group;
+ }
+ retval = read_inode_bitmap(sb, block_group, block_group);
+ if (retval < 0)
+ return retval;
+ return block_group;
+ }
+
+ for (i = 0; i < sbi->s_loaded_inode_bitmaps &&
+ sbi->s_inode_bitmap_number[i] != block_group; i++)
+ /* do nothing */;
+ if (i < sbi->s_loaded_inode_bitmaps &&
+ sbi->s_inode_bitmap_number[i] == block_group) {
+ inode_bitmap_number = sbi->s_inode_bitmap_number[i];
+ inode_bitmap = sbi->s_inode_bitmap[i];
+ for (j = i; j > 0; j--) {
+ sbi->s_inode_bitmap_number[j] =
+ sbi->s_inode_bitmap_number[j - 1];
+ sbi->s_inode_bitmap[j] = sbi->s_inode_bitmap[j - 1];
+ }
+ sbi->s_inode_bitmap_number[0] = inode_bitmap_number;
+ sbi->s_inode_bitmap[0] = inode_bitmap;
+
+ /*
+ * There's still one special case here --- if inode_bitmap == 0
+ * then our last attempt to read the bitmap failed and we have
+ * just ended up caching that failure. Try again to read it.
+ */
+ if (!inode_bitmap)
+ retval = read_inode_bitmap (sb, block_group, 0);
+ } else {
+ if (sbi->s_loaded_inode_bitmaps < EXT3_MAX_GROUP_LOADED)
+ sbi->s_loaded_inode_bitmaps++;
+ else
+ brelse(sbi->s_inode_bitmap[EXT3_MAX_GROUP_LOADED - 1]);
+ for (j = sbi->s_loaded_inode_bitmaps - 1; j > 0; j--) {
+ sbi->s_inode_bitmap_number[j] =
+ sbi->s_inode_bitmap_number[j - 1];
+ sbi->s_inode_bitmap[j] = sbi->s_inode_bitmap[j - 1];
+ }
+ retval = read_inode_bitmap (sb, block_group, 0);
+ }
+ return retval;
+}
+
+/*
+ * NOTE! When we get the inode, we're the only people
+ * that have access to it, and as such there are no
+ * race conditions we have to worry about. The inode
+ * is not on the hash-lists, and it cannot be reached
+ * through the filesystem because the directory entry
+ * has been deleted earlier.
+ *
+ * HOWEVER: we must make sure that we get no aliases,
+ * which means that we have to call "clear_inode()"
+ * _before_ we mark the inode not in use in the inode
+ * bitmaps. Otherwise a newly created file might use
+ * the same inode number (not actually the same pointer
+ * though), and then we'd have two inodes sharing the
+ * same inode number and space on the harddisk.
+ */
+void ext3_free_inode (handle_t *handle, struct inode * inode)
+{
+ struct super_block * sb = inode->i_sb;
+ int is_directory;
+ unsigned long ino;
+ struct buffer_head * bh;
+ struct buffer_head * bh2;
+ unsigned long block_group;
+ unsigned long bit;
+ int bitmap_nr;
+ struct ext3_group_desc * gdp;
+ struct ext3_super_block * es;
+ int fatal = 0, err;
+
+ if (!inode->i_dev) {
+ printk ("ext3_free_inode: inode has no device\n");
+ return;
+ }
+ if (atomic_read(&inode->i_count) > 1) {
+ printk ("ext3_free_inode: inode has count=%d\n",
+ atomic_read(&inode->i_count));
+ return;
+ }
+ if (inode->i_nlink) {
+ printk ("ext3_free_inode: inode has nlink=%d\n",
+ inode->i_nlink);
+ return;
+ }
+ if (!sb) {
+ printk("ext3_free_inode: inode on nonexistent device\n");
+ return;
+ }
+
+ ino = inode->i_ino;
+ ext3_debug ("freeing inode %lu\n", ino);
+
+ /*
+ * Note: we must free any quota before locking the superblock,
+ * as writing the quota to disk may need the lock as well.
+ */
+ DQUOT_INIT(inode);
+ DQUOT_FREE_INODE(inode);
+ DQUOT_DROP(inode);
+
+ is_directory = S_ISDIR(inode->i_mode);
+
+ /* Do this BEFORE marking the inode not in use or returning an error */
+ clear_inode (inode);
+
+ lock_super (sb);
+ es = sb->u.ext3_sb.s_es;
+ if (ino < EXT3_FIRST_INO(sb) || ino > le32_to_cpu(es->s_inodes_count)) {
+ ext3_error (sb, "ext3_free_inode",
+ "reserved or nonexistent inode %lu", ino);
+ goto error_return;
+ }
+ block_group = (ino - 1) / EXT3_INODES_PER_GROUP(sb);
+ bit = (ino - 1) % EXT3_INODES_PER_GROUP(sb);
+ bitmap_nr = load_inode_bitmap (sb, block_group);
+ if (bitmap_nr < 0)
+ goto error_return;
+
+ bh = sb->u.ext3_sb.s_inode_bitmap[bitmap_nr];
+
+ BUFFER_TRACE(bh, "get_write_access");
+ fatal = ext3_journal_get_write_access(handle, bh);
+ if (fatal)
+ goto error_return;
+
+ /* Ok, now we can actually update the inode bitmaps.. */
+ if (!ext3_clear_bit (bit, bh->b_data))
+ ext3_error (sb, "ext3_free_inode",
+ "bit already cleared for inode %lu", ino);
+ else {
+ gdp = ext3_get_group_desc (sb, block_group, &bh2);
+
+ BUFFER_TRACE(bh2, "get_write_access");
+ fatal = ext3_journal_get_write_access(handle, bh2);
+ if (fatal) goto error_return;
+
+ BUFFER_TRACE(sb->u.ext3_sb.s_sbh, "get write access");
+ fatal = ext3_journal_get_write_access(handle, sb->u.ext3_sb.s_sbh);
+ if (fatal) goto error_return;
+
+ if (gdp) {
+ gdp->bg_free_inodes_count = cpu_to_le16(
+ le16_to_cpu(gdp->bg_free_inodes_count) + 1);
+ if (is_directory)
+ gdp->bg_used_dirs_count = cpu_to_le16(
+ le16_to_cpu(gdp->bg_used_dirs_count) - 1);
+ }
+ BUFFER_TRACE(bh2, "call ext3_journal_dirty_metadata");
+ err = ext3_journal_dirty_metadata(handle, bh2);
+ if (!fatal) fatal = err;
+ es->s_free_inodes_count =
+ cpu_to_le32(le32_to_cpu(es->s_free_inodes_count) + 1);
+ BUFFER_TRACE(sb->u.ext3_sb.s_sbh,
+ "call ext3_journal_dirty_metadata");
+ err = ext3_journal_dirty_metadata(handle, sb->u.ext3_sb.s_sbh);
+ if (!fatal) fatal = err;
+ }
+ BUFFER_TRACE(bh, "call ext3_journal_dirty_metadata");
+ err = ext3_journal_dirty_metadata(handle, bh);
+ if (!fatal)
+ fatal = err;
+ sb->s_dirt = 1;
+error_return:
+ ext3_std_error(sb, fatal);
+ unlock_super(sb);
+}
+
+/*
+ * There are two policies for allocating an inode. If the new inode is
+ * a directory, then a forward search is made for a block group with both
+ * free space and a low directory-to-inode ratio; if that fails, then of
+ * the groups with above-average free space, that group with the fewest
+ * directories already is chosen.
+ *
+ * For other inodes, search forward from the parent directory's block
+ * group to find a free inode.
+ */
+struct inode * ext3_new_inode (handle_t *handle,
+ const struct inode * dir, int mode)
+{
+ struct super_block * sb;
+ struct buffer_head * bh;
+ struct buffer_head * bh2;
+ int i, j, avefreei;
+ struct inode * inode;
+ int bitmap_nr;
+ struct ext3_group_desc * gdp;
+ struct ext3_group_desc * tmp;
+ struct ext3_super_block * es;
+ int err = 0;
+
+ /* Cannot create files in a deleted directory */
+ if (!dir || !dir->i_nlink)
+ return ERR_PTR(-EPERM);
+
+ sb = dir->i_sb;
+ inode = new_inode(sb);
+ if (!inode)
+ return ERR_PTR(-ENOMEM);
+ init_rwsem(&inode->u.ext3_i.truncate_sem);
+
+ lock_super (sb);
+ es = sb->u.ext3_sb.s_es;
+repeat:
+ gdp = NULL;
+ i = 0;
+
+ if (S_ISDIR(mode)) {
+ avefreei = le32_to_cpu(es->s_free_inodes_count) /
+ sb->u.ext3_sb.s_groups_count;
+ if (!gdp) {
+ for (j = 0; j < sb->u.ext3_sb.s_groups_count; j++) {
+ struct buffer_head *temp_buffer;
+ tmp = ext3_get_group_desc (sb, j, &temp_buffer);
+ if (tmp &&
+ le16_to_cpu(tmp->bg_free_inodes_count) &&
+ le16_to_cpu(tmp->bg_free_inodes_count) >=
+ avefreei) {
+ if (!gdp || (le16_to_cpu(tmp->bg_free_blocks_count) >
+ le16_to_cpu(gdp->bg_free_blocks_count))) {
+ i = j;
+ gdp = tmp;
+ bh2 = temp_buffer;
+ }
+ }
+ }
+ }
+ } else {
+ /*
+ * Try to place the inode in its parent directory
+ */
+ i = dir->u.ext3_i.i_block_group;
+ tmp = ext3_get_group_desc (sb, i, &bh2);
+ if (tmp && le16_to_cpu(tmp->bg_free_inodes_count))
+ gdp = tmp;
+ else
+ {
+ /*
+ * Use a quadratic hash to find a group with a
+ * free inode
+ */
+ for (j = 1; j < sb->u.ext3_sb.s_groups_count; j <<= 1) {
+ i += j;
+ if (i >= sb->u.ext3_sb.s_groups_count)
+ i -= sb->u.ext3_sb.s_groups_count;
+ tmp = ext3_get_group_desc (sb, i, &bh2);
+ if (tmp &&
+ le16_to_cpu(tmp->bg_free_inodes_count)) {
+ gdp = tmp;
+ break;
+ }
+ }
+ }
+ if (!gdp) {
+ /*
+ * That failed: try linear search for a free inode
+ */
+ i = dir->u.ext3_i.i_block_group + 1;
+ for (j = 2; j < sb->u.ext3_sb.s_groups_count; j++) {
+ if (++i >= sb->u.ext3_sb.s_groups_count)
+ i = 0;
+ tmp = ext3_get_group_desc (sb, i, &bh2);
+ if (tmp &&
+ le16_to_cpu(tmp->bg_free_inodes_count)) {
+ gdp = tmp;
+ break;
+ }
+ }
+ }
+ }
+
+ err = -ENOSPC;
+ if (!gdp)
+ goto out;
+
+ err = -EIO;
+ bitmap_nr = load_inode_bitmap (sb, i);
+ if (bitmap_nr < 0)
+ goto fail;
+
+ bh = sb->u.ext3_sb.s_inode_bitmap[bitmap_nr];
+
+ if ((j = ext3_find_first_zero_bit ((unsigned long *) bh->b_data,
+ EXT3_INODES_PER_GROUP(sb))) <
+ EXT3_INODES_PER_GROUP(sb)) {
+ BUFFER_TRACE(bh, "get_write_access");
+ err = ext3_journal_get_write_access(handle, bh);
+ if (err) goto fail;
+
+ if (ext3_set_bit (j, bh->b_data)) {
+ ext3_error (sb, "ext3_new_inode",
+ "bit already set for inode %d", j);
+ goto repeat;
+ }
+ BUFFER_TRACE(bh, "call ext3_journal_dirty_metadata");
+ err = ext3_journal_dirty_metadata(handle, bh);
+ if (err) goto fail;
+ } else {
+ if (le16_to_cpu(gdp->bg_free_inodes_count) != 0) {
+ ext3_error (sb, "ext3_new_inode",
+ "Free inodes count corrupted in group %d",
+ i);
+ /* Is it really ENOSPC? */
+ err = -ENOSPC;
+ if (sb->s_flags & MS_RDONLY)
+ goto fail;
+
+ BUFFER_TRACE(bh2, "get_write_access");
+ err = ext3_journal_get_write_access(handle, bh2);
+ if (err) goto fail;
+ gdp->bg_free_inodes_count = 0;
+ BUFFER_TRACE(bh2, "call ext3_journal_dirty_metadata");
+ err = ext3_journal_dirty_metadata(handle, bh2);
+ if (err) goto fail;
+ }
+ goto repeat;
+ }
+ j += i * EXT3_INODES_PER_GROUP(sb) + 1;
+ if (j < EXT3_FIRST_INO(sb) || j > le32_to_cpu(es->s_inodes_count)) {
+ ext3_error (sb, "ext3_new_inode",
+ "reserved inode or inode > inodes count - "
+ "block_group = %d,inode=%d", i, j);
+ err = -EIO;
+ goto fail;
+ }
+
+ BUFFER_TRACE(bh2, "get_write_access");
+ err = ext3_journal_get_write_access(handle, bh2);
+ if (err) goto fail;
+ gdp->bg_free_inodes_count =
+ cpu_to_le16(le16_to_cpu(gdp->bg_free_inodes_count) - 1);
+ if (S_ISDIR(mode))
+ gdp->bg_used_dirs_count =
+ cpu_to_le16(le16_to_cpu(gdp->bg_used_dirs_count) + 1);
+ BUFFER_TRACE(bh2, "call ext3_journal_dirty_metadata");
+ err = ext3_journal_dirty_metadata(handle, bh2);
+ if (err) goto fail;
+
+ BUFFER_TRACE(sb->u.ext3_sb.s_sbh, "get_write_access");
+ err = ext3_journal_get_write_access(handle, sb->u.ext3_sb.s_sbh);
+ if (err) goto fail;
+ es->s_free_inodes_count =
+ cpu_to_le32(le32_to_cpu(es->s_free_inodes_count) - 1);
+ BUFFER_TRACE(sb->u.ext3_sb.s_sbh, "call ext3_journal_dirty_metadata");
+ err = ext3_journal_dirty_metadata(handle, sb->u.ext3_sb.s_sbh);
+ sb->s_dirt = 1;
+ if (err) goto fail;
+
+ inode->i_uid = current->fsuid;
+ if (test_opt (sb, GRPID))
+ inode->i_gid = dir->i_gid;
+ else if (dir->i_mode & S_ISGID) {
+ inode->i_gid = dir->i_gid;
+ if (S_ISDIR(mode))
+ mode |= S_ISGID;
+ } else
+ inode->i_gid = current->fsgid;
+ inode->i_mode = mode;
+
+ inode->i_ino = j;
+ /* This is the optimal IO size (for stat), not the fs block size */
+ inode->i_blksize = PAGE_SIZE;
+ inode->i_blocks = 0;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+ inode->u.ext3_i.i_flags = dir->u.ext3_i.i_flags & ~EXT3_INDEX_FL;
+ if (S_ISLNK(mode))
+ inode->u.ext3_i.i_flags &= ~(EXT3_IMMUTABLE_FL|EXT3_APPEND_FL);
+#ifdef EXT3_FRAGMENTS
+ inode->u.ext3_i.i_faddr = 0;
+ inode->u.ext3_i.i_frag_no = 0;
+ inode->u.ext3_i.i_frag_size = 0;
+#endif
+ inode->u.ext3_i.i_file_acl = 0;
+ inode->u.ext3_i.i_dir_acl = 0;
+ inode->u.ext3_i.i_dtime = 0;
+ INIT_LIST_HEAD(&inode->u.ext3_i.i_orphan);
+#ifdef EXT3_PREALLOCATE
+ inode->u.ext3_i.i_prealloc_count = 0;
+#endif
+ inode->u.ext3_i.i_block_group = i;
+
+ ext3_set_inode_flags(inode);
+ if (IS_SYNC(inode))
+ handle->h_sync = 1;
+ insert_inode_hash(inode);
+ inode->i_generation = sb->u.ext3_sb.s_next_generation++;
+
+ inode->u.ext3_i.i_state = EXT3_STATE_NEW;
+ err = ext3_mark_inode_dirty(handle, inode);
+ if (err) goto fail;
+
+ unlock_super (sb);
+ if(DQUOT_ALLOC_INODE(inode)) {
+ DQUOT_DROP(inode);
+ inode->i_flags |= S_NOQUOTA;
+ inode->i_nlink = 0;
+ iput(inode);
+ return ERR_PTR(-EDQUOT);
+ }
+ ext3_debug ("allocating inode %lu\n", inode->i_ino);
+ return inode;
+
+fail:
+ ext3_std_error(sb, err);
+out:
+ unlock_super(sb);
+ iput(inode);
+ return ERR_PTR(err);
+}
+
+/* Verify that we are loading a valid orphan from disk */
+struct inode *ext3_orphan_get(struct super_block *sb, unsigned long ino)
+{
+ unsigned long max_ino = le32_to_cpu(EXT3_SB(sb)->s_es->s_inodes_count);
+ unsigned long block_group;
+ int bit;
+ int bitmap_nr;
+ struct buffer_head *bh;
+ struct inode *inode = NULL;
+
+ /* Error cases - e2fsck has already cleaned up for us */
+ if (ino > max_ino) {
+ ext3_warning(sb, __FUNCTION__,
+ "bad orphan ino %lu! e2fsck was run?\n", ino);
+ return NULL;
+ }
+
+ block_group = (ino - 1) / EXT3_INODES_PER_GROUP(sb);
+ bit = (ino - 1) % EXT3_INODES_PER_GROUP(sb);
+ if ((bitmap_nr = load_inode_bitmap(sb, block_group)) < 0 ||
+ !(bh = EXT3_SB(sb)->s_inode_bitmap[bitmap_nr])) {
+ ext3_warning(sb, __FUNCTION__,
+ "inode bitmap error for orphan %lu\n", ino);
+ return NULL;
+ }
+
+ /* Having the inode bit set should be a 100% indicator that this
+ * is a valid orphan (no e2fsck run on fs). Orphans also include
+ * inodes that were being truncated, so we can't check i_nlink==0.
+ */
+ if (!ext3_test_bit(bit, bh->b_data) || !(inode = iget(sb, ino)) ||
+ is_bad_inode(inode) || NEXT_ORPHAN(inode) > max_ino) {
+ ext3_warning(sb, __FUNCTION__,
+ "bad orphan inode %lu! e2fsck was run?\n", ino);
+ printk(KERN_NOTICE "ext3_test_bit(bit=%d, block=%ld) = %d\n",
+ bit, bh->b_blocknr, ext3_test_bit(bit, bh->b_data));
+ printk(KERN_NOTICE "inode=%p\n", inode);
+ if (inode) {
+ printk(KERN_NOTICE "is_bad_inode(inode)=%d\n",
+ is_bad_inode(inode));
+ printk(KERN_NOTICE "NEXT_ORPHAN(inode)=%u\n",
+ NEXT_ORPHAN(inode));
+ printk(KERN_NOTICE "max_ino=%lu\n", max_ino);
+ }
+ /* Avoid freeing blocks if we got a bad deleted inode */
+ if (inode && inode->i_nlink == 0)
+ inode->i_blocks = 0;
+ iput(inode);
+ return NULL;
+ }
+
+ return inode;
+}
+
+unsigned long ext3_count_free_inodes (struct super_block * sb)
+{
+#ifdef EXT3FS_DEBUG
+ struct ext3_super_block * es;
+ unsigned long desc_count, bitmap_count, x;
+ int bitmap_nr;
+ struct ext3_group_desc * gdp;
+ int i;
+
+ lock_super (sb);
+ es = sb->u.ext3_sb.s_es;
+ desc_count = 0;
+ bitmap_count = 0;
+ gdp = NULL;
+ for (i = 0; i < sb->u.ext3_sb.s_groups_count; i++) {
+ gdp = ext3_get_group_desc (sb, i, NULL);
+ if (!gdp)
+ continue;
+ desc_count += le16_to_cpu(gdp->bg_free_inodes_count);
+ bitmap_nr = load_inode_bitmap (sb, i);
+ if (bitmap_nr < 0)
+ continue;
+
+ x = ext3_count_free (sb->u.ext3_sb.s_inode_bitmap[bitmap_nr],
+ EXT3_INODES_PER_GROUP(sb) / 8);
+ printk ("group %d: stored = %d, counted = %lu\n",
+ i, le16_to_cpu(gdp->bg_free_inodes_count), x);
+ bitmap_count += x;
+ }
+ printk("ext3_count_free_inodes: stored = %lu, computed = %lu, %lu\n",
+ le32_to_cpu(es->s_free_inodes_count), desc_count, bitmap_count);
+ unlock_super (sb);
+ return desc_count;
+#else
+ return le32_to_cpu(sb->u.ext3_sb.s_es->s_free_inodes_count);
+#endif
+}
+
+#ifdef CONFIG_EXT3_CHECK
+/* Called at mount-time, super-block is locked */
+void ext3_check_inodes_bitmap (struct super_block * sb)
+{
+ struct ext3_super_block * es;
+ unsigned long desc_count, bitmap_count, x;
+ int bitmap_nr;
+ struct ext3_group_desc * gdp;
+ int i;
+
+ es = sb->u.ext3_sb.s_es;
+ desc_count = 0;
+ bitmap_count = 0;
+ gdp = NULL;
+ for (i = 0; i < sb->u.ext3_sb.s_groups_count; i++) {
+ gdp = ext3_get_group_desc (sb, i, NULL);
+ if (!gdp)
+ continue;
+ desc_count += le16_to_cpu(gdp->bg_free_inodes_count);
+ bitmap_nr = load_inode_bitmap (sb, i);
+ if (bitmap_nr < 0)
+ continue;
+
+ x = ext3_count_free (sb->u.ext3_sb.s_inode_bitmap[bitmap_nr],
+ EXT3_INODES_PER_GROUP(sb) / 8);
+ if (le16_to_cpu(gdp->bg_free_inodes_count) != x)
+ ext3_error (sb, "ext3_check_inodes_bitmap",
+ "Wrong free inodes count in group %d, "
+ "stored = %d, counted = %lu", i,
+ le16_to_cpu(gdp->bg_free_inodes_count), x);
+ bitmap_count += x;
+ }
+ if (le32_to_cpu(es->s_free_inodes_count) != bitmap_count)
+ ext3_error (sb, "ext3_check_inodes_bitmap",
+ "Wrong free inodes count in super block, "
+ "stored = %lu, counted = %lu",
+ (unsigned long)le32_to_cpu(es->s_free_inodes_count),
+ bitmap_count);
+}
+#endif
diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c
new file mode 100644
index 00000000000000..bcd86f6d805147
--- /dev/null
+++ b/fs/ext3/inode.c
@@ -0,0 +1,2737 @@
+/*
+ * linux/fs/ext3/inode.c
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * from
+ *
+ * linux/fs/minix/inode.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * Goal-directed block allocation by Stephen Tweedie
+ * (sct@redhat.com), 1993, 1998
+ * Big-endian to little-endian byte-swapping/bitmaps by
+ * David S. Miller (davem@caip.rutgers.edu), 1995
+ * 64-bit file support on 64-bit platforms by Jakub Jelinek
+ * (jj@sunsite.ms.mff.cuni.cz)
+ *
+ * Assorted race fixes, rewrite of ext3_get_block() by Al Viro, 2000
+ */
+
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/ext3_jbd.h>
+#include <linux/jbd.h>
+#include <linux/locks.h>
+#include <linux/smp_lock.h>
+#include <linux/highuid.h>
+#include <linux/quotaops.h>
+#include <linux/module.h>
+
+/*
+ * SEARCH_FROM_ZERO forces each block allocation to search from the start
+ * of the filesystem. This is to force rapid reallocation of recently-freed
+ * blocks. The file fragmentation is horrendous.
+ */
+#undef SEARCH_FROM_ZERO
+
+/*
+ * Test whether an inode is a fast symlink.
+ */
+static inline int ext3_inode_is_fast_symlink(struct inode *inode)
+{
+ int ea_blocks = EXT3_I(inode)->i_file_acl ?
+ (inode->i_sb->s_blocksize >> 9) : 0;
+
+ return (S_ISLNK(inode->i_mode) &&
+ inode->i_blocks - ea_blocks == 0);
+}
+
+/* The ext3 forget function must perform a revoke if we are freeing data
+ * which has been journaled. Metadata (eg. indirect blocks) must be
+ * revoked in all cases.
+ *
+ * "bh" may be NULL: a metadata block may have been freed from memory
+ * but there may still be a record of it in the journal, and that record
+ * still needs to be revoked.
+ */
+
+static int ext3_forget(handle_t *handle, int is_metadata,
+ struct inode *inode, struct buffer_head *bh,
+ int blocknr)
+{
+ int err;
+
+ BUFFER_TRACE(bh, "enter");
+
+ jbd_debug(4, "forgetting bh %p: is_metadata = %d, mode %o, "
+ "data mode %lx\n",
+ bh, is_metadata, inode->i_mode,
+ test_opt(inode->i_sb, DATA_FLAGS));
+
+ /* Never use the revoke function if we are doing full data
+ * journaling: there is no need to, and a V1 superblock won't
+ * support it. Otherwise, only skip the revoke on un-journaled
+ * data blocks. */
+
+ if (test_opt(inode->i_sb, DATA_FLAGS) == EXT3_MOUNT_JOURNAL_DATA ||
+ (!is_metadata && !ext3_should_journal_data(inode))) {
+ if (bh) {
+ BUFFER_TRACE(bh, "call journal_forget");
+ ext3_journal_forget(handle, bh);
+ }
+ return 0;
+ }
+
+ /*
+ * data!=journal && (is_metadata || should_journal_data(inode))
+ */
+ BUFFER_TRACE(bh, "call ext3_journal_revoke");
+ err = ext3_journal_revoke(handle, blocknr, bh);
+ if (err)
+ ext3_abort(inode->i_sb, __FUNCTION__,
+ "error %d when attempting revoke", err);
+ BUFFER_TRACE(bh, "exit");
+ return err;
+}
+
+/*
+ * Work out how many blocks we need to progress with the next chunk of a
+ * truncate transaction.
+ */
+
+static unsigned long blocks_for_truncate(struct inode *inode)
+{
+ unsigned long needed;
+
+ needed = inode->i_blocks >> (inode->i_sb->s_blocksize_bits - 9);
+
+ /* Give ourselves just enough room to cope with inodes in which
+ * i_blocks is corrupt: we've seen disk corruptions in the past
+ * which resulted in random data in an inode which looked enough
+ * like a regular file for ext3 to try to delete it. Things
+ * will go a bit crazy if that happens, but at least we should
+ * try not to panic the whole kernel. */
+ if (needed < 2)
+ needed = 2;
+
+ /* But we need to bound the transaction so we don't overflow the
+ * journal. */
+ if (needed > EXT3_MAX_TRANS_DATA)
+ needed = EXT3_MAX_TRANS_DATA;
+
+ return EXT3_DATA_TRANS_BLOCKS + needed;
+}
+
+/*
+ * Truncate transactions can be complex and absolutely huge. So we need to
+ * be able to restart the transaction at a conventient checkpoint to make
+ * sure we don't overflow the journal.
+ *
+ * start_transaction gets us a new handle for a truncate transaction,
+ * and extend_transaction tries to extend the existing one a bit. If
+ * extend fails, we need to propagate the failure up and restart the
+ * transaction in the top-level truncate loop. --sct
+ */
+
+static handle_t *start_transaction(struct inode *inode)
+{
+ handle_t *result;
+
+ result = ext3_journal_start(inode, blocks_for_truncate(inode));
+ if (!IS_ERR(result))
+ return result;
+
+ ext3_std_error(inode->i_sb, PTR_ERR(result));
+ return result;
+}
+
+/*
+ * Try to extend this transaction for the purposes of truncation.
+ *
+ * Returns 0 if we managed to create more room. If we can't create more
+ * room, and the transaction must be restarted we return 1.
+ */
+static int try_to_extend_transaction(handle_t *handle, struct inode *inode)
+{
+ if (handle->h_buffer_credits > EXT3_RESERVE_TRANS_BLOCKS)
+ return 0;
+ if (!ext3_journal_extend(handle, blocks_for_truncate(inode)))
+ return 0;
+ return 1;
+}
+
+/*
+ * Restart the transaction associated with *handle. This does a commit,
+ * so before we call here everything must be consistently dirtied against
+ * this transaction.
+ */
+static int ext3_journal_test_restart(handle_t *handle, struct inode *inode)
+{
+ jbd_debug(2, "restarting handle %p\n", handle);
+ return ext3_journal_restart(handle, blocks_for_truncate(inode));
+}
+
+/*
+ * Called at each iput()
+ */
+void ext3_put_inode (struct inode * inode)
+{
+ ext3_discard_prealloc (inode);
+}
+
+/*
+ * Called at the last iput() if i_nlink is zero.
+ */
+void ext3_delete_inode (struct inode * inode)
+{
+ handle_t *handle;
+
+ if (is_bad_inode(inode) ||
+ inode->i_ino == EXT3_ACL_IDX_INO ||
+ inode->i_ino == EXT3_ACL_DATA_INO)
+ goto no_delete;
+
+ lock_kernel();
+ handle = start_transaction(inode);
+ if (IS_ERR(handle)) {
+ /* If we're going to skip the normal cleanup, we still
+ * need to make sure that the in-core orphan linked list
+ * is properly cleaned up. */
+ ext3_orphan_del(NULL, inode);
+
+ ext3_std_error(inode->i_sb, PTR_ERR(handle));
+ unlock_kernel();
+ goto no_delete;
+ }
+
+ if (IS_SYNC(inode))
+ handle->h_sync = 1;
+ inode->i_size = 0;
+ if (inode->i_blocks)
+ ext3_truncate(inode);
+ /*
+ * Kill off the orphan record which ext3_truncate created.
+ * AKPM: I think this can be inside the above `if'.
+ * Note that ext3_orphan_del() has to be able to cope with the
+ * deletion of a non-existent orphan - this is because we don't
+ * know if ext3_truncate() actually created an orphan record.
+ * (Well, we could do this if we need to, but heck - it works)
+ */
+ ext3_orphan_del(handle, inode);
+ inode->u.ext3_i.i_dtime = CURRENT_TIME;
+
+ /*
+ * One subtle ordering requirement: if anything has gone wrong
+ * (transaction abort, IO errors, whatever), then we can still
+ * do these next steps (the fs will already have been marked as
+ * having errors), but we can't free the inode if the mark_dirty
+ * fails.
+ */
+ if (ext3_mark_inode_dirty(handle, inode))
+ /* If that failed, just do the required in-core inode clear. */
+ clear_inode(inode);
+ else
+ ext3_free_inode(handle, inode);
+ ext3_journal_stop(handle, inode);
+ unlock_kernel();
+ return;
+no_delete:
+ clear_inode(inode); /* We must guarantee clearing of inode... */
+}
+
+void ext3_discard_prealloc (struct inode * inode)
+{
+#ifdef EXT3_PREALLOCATE
+ lock_kernel();
+ /* Writer: ->i_prealloc* */
+ if (inode->u.ext3_i.i_prealloc_count) {
+ unsigned short total = inode->u.ext3_i.i_prealloc_count;
+ unsigned long block = inode->u.ext3_i.i_prealloc_block;
+ inode->u.ext3_i.i_prealloc_count = 0;
+ inode->u.ext3_i.i_prealloc_block = 0;
+ /* Writer: end */
+ ext3_free_blocks (inode, block, total);
+ }
+ unlock_kernel();
+#endif
+}
+
+static int ext3_alloc_block (handle_t *handle,
+ struct inode * inode, unsigned long goal, int *err)
+{
+#ifdef EXT3FS_DEBUG
+ static unsigned long alloc_hits = 0, alloc_attempts = 0;
+#endif
+ unsigned long result;
+
+#ifdef EXT3_PREALLOCATE
+ /* Writer: ->i_prealloc* */
+ if (inode->u.ext3_i.i_prealloc_count &&
+ (goal == inode->u.ext3_i.i_prealloc_block ||
+ goal + 1 == inode->u.ext3_i.i_prealloc_block))
+ {
+ result = inode->u.ext3_i.i_prealloc_block++;
+ inode->u.ext3_i.i_prealloc_count--;
+ /* Writer: end */
+ ext3_debug ("preallocation hit (%lu/%lu).\n",
+ ++alloc_hits, ++alloc_attempts);
+ } else {
+ ext3_discard_prealloc (inode);
+ ext3_debug ("preallocation miss (%lu/%lu).\n",
+ alloc_hits, ++alloc_attempts);
+ if (S_ISREG(inode->i_mode))
+ result = ext3_new_block (inode, goal,
+ &inode->u.ext3_i.i_prealloc_count,
+ &inode->u.ext3_i.i_prealloc_block, err);
+ else
+ result = ext3_new_block (inode, goal, 0, 0, err);
+ /*
+ * AKPM: this is somewhat sticky. I'm not surprised it was
+ * disabled in 2.2's ext3. Need to integrate b_committed_data
+ * guarding with preallocation, if indeed preallocation is
+ * effective.
+ */
+ }
+#else
+ result = ext3_new_block (handle, inode, goal, 0, 0, err);
+#endif
+ return result;
+}
+
+
+typedef struct {
+ u32 *p;
+ u32 key;
+ struct buffer_head *bh;
+} Indirect;
+
+static inline void add_chain(Indirect *p, struct buffer_head *bh, u32 *v)
+{
+ p->key = *(p->p = v);
+ p->bh = bh;
+}
+
+static inline int verify_chain(Indirect *from, Indirect *to)
+{
+ while (from <= to && from->key == *from->p)
+ from++;
+ return (from > to);
+}
+
+/**
+ * ext3_block_to_path - parse the block number into array of offsets
+ * @inode: inode in question (we are only interested in its superblock)
+ * @i_block: block number to be parsed
+ * @offsets: array to store the offsets in
+ *
+ * To store the locations of file's data ext3 uses a data structure common
+ * for UNIX filesystems - tree of pointers anchored in the inode, with
+ * data blocks at leaves and indirect blocks in intermediate nodes.
+ * This function translates the block number into path in that tree -
+ * return value is the path length and @offsets[n] is the offset of
+ * pointer to (n+1)th node in the nth one. If @block is out of range
+ * (negative or too large) warning is printed and zero returned.
+ *
+ * Note: function doesn't find node addresses, so no IO is needed. All
+ * we need to know is the capacity of indirect blocks (taken from the
+ * inode->i_sb).
+ */
+
+/*
+ * Portability note: the last comparison (check that we fit into triple
+ * indirect block) is spelled differently, because otherwise on an
+ * architecture with 32-bit longs and 8Kb pages we might get into trouble
+ * if our filesystem had 8Kb blocks. We might use long long, but that would
+ * kill us on x86. Oh, well, at least the sign propagation does not matter -
+ * i_block would have to be negative in the very beginning, so we would not
+ * get there at all.
+ */
+
+static int ext3_block_to_path(struct inode *inode, long i_block, int offsets[4])
+{
+ int ptrs = EXT3_ADDR_PER_BLOCK(inode->i_sb);
+ int ptrs_bits = EXT3_ADDR_PER_BLOCK_BITS(inode->i_sb);
+ const long direct_blocks = EXT3_NDIR_BLOCKS,
+ indirect_blocks = ptrs,
+ double_blocks = (1 << (ptrs_bits * 2));
+ int n = 0;
+
+ if (i_block < 0) {
+ ext3_warning (inode->i_sb, "ext3_block_to_path", "block < 0");
+ } else if (i_block < direct_blocks) {
+ offsets[n++] = i_block;
+ } else if ( (i_block -= direct_blocks) < indirect_blocks) {
+ offsets[n++] = EXT3_IND_BLOCK;
+ offsets[n++] = i_block;
+ } else if ((i_block -= indirect_blocks) < double_blocks) {
+ offsets[n++] = EXT3_DIND_BLOCK;
+ offsets[n++] = i_block >> ptrs_bits;
+ offsets[n++] = i_block & (ptrs - 1);
+ } else if (((i_block -= double_blocks) >> (ptrs_bits * 2)) < ptrs) {
+ offsets[n++] = EXT3_TIND_BLOCK;
+ offsets[n++] = i_block >> (ptrs_bits * 2);
+ offsets[n++] = (i_block >> ptrs_bits) & (ptrs - 1);
+ offsets[n++] = i_block & (ptrs - 1);
+ } else {
+ ext3_warning (inode->i_sb, "ext3_block_to_path", "block > big");
+ }
+ return n;
+}
+
+/**
+ * ext3_get_branch - read the chain of indirect blocks leading to data
+ * @inode: inode in question
+ * @depth: depth of the chain (1 - direct pointer, etc.)
+ * @offsets: offsets of pointers in inode/indirect blocks
+ * @chain: place to store the result
+ * @err: here we store the error value
+ *
+ * Function fills the array of triples <key, p, bh> and returns %NULL
+ * if everything went OK or the pointer to the last filled triple
+ * (incomplete one) otherwise. Upon the return chain[i].key contains
+ * the number of (i+1)-th block in the chain (as it is stored in memory,
+ * i.e. little-endian 32-bit), chain[i].p contains the address of that
+ * number (it points into struct inode for i==0 and into the bh->b_data
+ * for i>0) and chain[i].bh points to the buffer_head of i-th indirect
+ * block for i>0 and NULL for i==0. In other words, it holds the block
+ * numbers of the chain, addresses they were taken from (and where we can
+ * verify that chain did not change) and buffer_heads hosting these
+ * numbers.
+ *
+ * Function stops when it stumbles upon zero pointer (absent block)
+ * (pointer to last triple returned, *@err == 0)
+ * or when it gets an IO error reading an indirect block
+ * (ditto, *@err == -EIO)
+ * or when it notices that chain had been changed while it was reading
+ * (ditto, *@err == -EAGAIN)
+ * or when it reads all @depth-1 indirect blocks successfully and finds
+ * the whole chain, all way to the data (returns %NULL, *err == 0).
+ */
+static Indirect *ext3_get_branch(struct inode *inode, int depth, int *offsets,
+ Indirect chain[4], int *err)
+{
+ struct super_block *sb = inode->i_sb;
+ Indirect *p = chain;
+ struct buffer_head *bh;
+
+ *err = 0;
+ /* i_data is not going away, no lock needed */
+ add_chain (chain, NULL, inode->u.ext3_i.i_data + *offsets);
+ if (!p->key)
+ goto no_block;
+ while (--depth) {
+ bh = sb_bread(sb, le32_to_cpu(p->key));
+ if (!bh)
+ goto failure;
+ /* Reader: pointers */
+ if (!verify_chain(chain, p))
+ goto changed;
+ add_chain(++p, bh, (u32*)bh->b_data + *++offsets);
+ /* Reader: end */
+ if (!p->key)
+ goto no_block;
+ }
+ return NULL;
+
+changed:
+ brelse(bh);
+ *err = -EAGAIN;
+ goto no_block;
+failure:
+ *err = -EIO;
+no_block:
+ return p;
+}
+
+/**
+ * ext3_find_near - find a place for allocation with sufficient locality
+ * @inode: owner
+ * @ind: descriptor of indirect block.
+ *
+ * This function returns the prefered place for block allocation.
+ * It is used when heuristic for sequential allocation fails.
+ * Rules are:
+ * + if there is a block to the left of our position - allocate near it.
+ * + if pointer will live in indirect block - allocate near that block.
+ * + if pointer will live in inode - allocate in the same
+ * cylinder group.
+ * Caller must make sure that @ind is valid and will stay that way.
+ */
+
+static inline unsigned long ext3_find_near(struct inode *inode, Indirect *ind)
+{
+ u32 *start = ind->bh ? (u32*) ind->bh->b_data : inode->u.ext3_i.i_data;
+ u32 *p;
+
+ /* Try to find previous block */
+ for (p = ind->p - 1; p >= start; p--)
+ if (*p)
+ return le32_to_cpu(*p);
+
+ /* No such thing, so let's try location of indirect block */
+ if (ind->bh)
+ return ind->bh->b_blocknr;
+
+ /*
+ * It is going to be refered from inode itself? OK, just put it into
+ * the same cylinder group then.
+ */
+ return (inode->u.ext3_i.i_block_group *
+ EXT3_BLOCKS_PER_GROUP(inode->i_sb)) +
+ le32_to_cpu(inode->i_sb->u.ext3_sb.s_es->s_first_data_block);
+}
+
+/**
+ * ext3_find_goal - find a prefered place for allocation.
+ * @inode: owner
+ * @block: block we want
+ * @chain: chain of indirect blocks
+ * @partial: pointer to the last triple within a chain
+ * @goal: place to store the result.
+ *
+ * Normally this function find the prefered place for block allocation,
+ * stores it in *@goal and returns zero. If the branch had been changed
+ * under us we return -EAGAIN.
+ */
+
+static int ext3_find_goal(struct inode *inode, long block, Indirect chain[4],
+ Indirect *partial, unsigned long *goal)
+{
+ /* Writer: ->i_next_alloc* */
+ if (block == inode->u.ext3_i.i_next_alloc_block + 1) {
+ inode->u.ext3_i.i_next_alloc_block++;
+ inode->u.ext3_i.i_next_alloc_goal++;
+ }
+#ifdef SEARCH_FROM_ZERO
+ inode->u.ext3_i.i_next_alloc_block = 0;
+ inode->u.ext3_i.i_next_alloc_goal = 0;
+#endif
+ /* Writer: end */
+ /* Reader: pointers, ->i_next_alloc* */
+ if (verify_chain(chain, partial)) {
+ /*
+ * try the heuristic for sequential allocation,
+ * failing that at least try to get decent locality.
+ */
+ if (block == inode->u.ext3_i.i_next_alloc_block)
+ *goal = inode->u.ext3_i.i_next_alloc_goal;
+ if (!*goal)
+ *goal = ext3_find_near(inode, partial);
+#ifdef SEARCH_FROM_ZERO
+ *goal = 0;
+#endif
+ return 0;
+ }
+ /* Reader: end */
+ return -EAGAIN;
+}
+
+/**
+ * ext3_alloc_branch - allocate and set up a chain of blocks.
+ * @inode: owner
+ * @num: depth of the chain (number of blocks to allocate)
+ * @offsets: offsets (in the blocks) to store the pointers to next.
+ * @branch: place to store the chain in.
+ *
+ * This function allocates @num blocks, zeroes out all but the last one,
+ * links them into chain and (if we are synchronous) writes them to disk.
+ * In other words, it prepares a branch that can be spliced onto the
+ * inode. It stores the information about that chain in the branch[], in
+ * the same format as ext3_get_branch() would do. We are calling it after
+ * we had read the existing part of chain and partial points to the last
+ * triple of that (one with zero ->key). Upon the exit we have the same
+ * picture as after the successful ext3_get_block(), excpet that in one
+ * place chain is disconnected - *branch->p is still zero (we did not
+ * set the last link), but branch->key contains the number that should
+ * be placed into *branch->p to fill that gap.
+ *
+ * If allocation fails we free all blocks we've allocated (and forget
+ * their buffer_heads) and return the error value the from failed
+ * ext3_alloc_block() (normally -ENOSPC). Otherwise we set the chain
+ * as described above and return 0.
+ */
+
+static int ext3_alloc_branch(handle_t *handle, struct inode *inode,
+ int num,
+ unsigned long goal,
+ int *offsets,
+ Indirect *branch)
+{
+ int blocksize = inode->i_sb->s_blocksize;
+ int n = 0, keys = 0;
+ int err = 0;
+ int i;
+ int parent = ext3_alloc_block(handle, inode, goal, &err);
+
+ branch[0].key = cpu_to_le32(parent);
+ if (parent) {
+ for (n = 1; n < num; n++) {
+ struct buffer_head *bh;
+ /* Allocate the next block */
+ int nr = ext3_alloc_block(handle, inode, parent, &err);
+ if (!nr)
+ break;
+ branch[n].key = cpu_to_le32(nr);
+ keys = n+1;
+
+ /*
+ * Get buffer_head for parent block, zero it out
+ * and set the pointer to new one, then send
+ * parent to disk.
+ */
+ bh = sb_getblk(inode->i_sb, parent);
+ branch[n].bh = bh;
+ lock_buffer(bh);
+ BUFFER_TRACE(bh, "call get_create_access");
+ err = ext3_journal_get_create_access(handle, bh);
+ if (err) {
+ unlock_buffer(bh);
+ brelse(bh);
+ break;
+ }
+
+ memset(bh->b_data, 0, blocksize);
+ branch[n].p = (u32*) bh->b_data + offsets[n];
+ *branch[n].p = branch[n].key;
+ BUFFER_TRACE(bh, "marking uptodate");
+ mark_buffer_uptodate(bh, 1);
+ unlock_buffer(bh);
+
+ BUFFER_TRACE(bh, "call ext3_journal_dirty_metadata");
+ err = ext3_journal_dirty_metadata(handle, bh);
+ if (err)
+ break;
+
+ parent = nr;
+ }
+ }
+ if (n == num)
+ return 0;
+
+ /* Allocation failed, free what we already allocated */
+ for (i = 1; i < keys; i++) {
+ BUFFER_TRACE(branch[i].bh, "call journal_forget");
+ ext3_journal_forget(handle, branch[i].bh);
+ }
+ for (i = 0; i < keys; i++)
+ ext3_free_blocks(handle, inode, le32_to_cpu(branch[i].key), 1);
+ return err;
+}
+
+/**
+ * ext3_splice_branch - splice the allocated branch onto inode.
+ * @inode: owner
+ * @block: (logical) number of block we are adding
+ * @chain: chain of indirect blocks (with a missing link - see
+ * ext3_alloc_branch)
+ * @where: location of missing link
+ * @num: number of blocks we are adding
+ *
+ * This function verifies that chain (up to the missing link) had not
+ * changed, fills the missing link and does all housekeeping needed in
+ * inode (->i_blocks, etc.). In case of success we end up with the full
+ * chain to new block and return 0. Otherwise (== chain had been changed)
+ * we free the new blocks (forgetting their buffer_heads, indeed) and
+ * return -EAGAIN.
+ */
+
+static int ext3_splice_branch(handle_t *handle, struct inode *inode, long block,
+ Indirect chain[4], Indirect *where, int num)
+{
+ int i;
+ int err = 0;
+
+ /*
+ * If we're splicing into a [td]indirect block (as opposed to the
+ * inode) then we need to get write access to the [td]indirect block
+ * before the splice.
+ */
+ if (where->bh) {
+ BUFFER_TRACE(where->bh, "get_write_access");
+ err = ext3_journal_get_write_access(handle, where->bh);
+ if (err)
+ goto err_out;
+ }
+ /* Verify that place we are splicing to is still there and vacant */
+
+ /* Writer: pointers, ->i_next_alloc* */
+ if (!verify_chain(chain, where-1) || *where->p)
+ /* Writer: end */
+ goto changed;
+
+ /* That's it */
+
+ *where->p = where->key;
+ inode->u.ext3_i.i_next_alloc_block = block;
+ inode->u.ext3_i.i_next_alloc_goal = le32_to_cpu(where[num-1].key);
+#ifdef SEARCH_FROM_ZERO
+ inode->u.ext3_i.i_next_alloc_block = 0;
+ inode->u.ext3_i.i_next_alloc_goal = 0;
+#endif
+ /* Writer: end */
+
+ /* We are done with atomic stuff, now do the rest of housekeeping */
+
+ inode->i_ctime = CURRENT_TIME;
+ ext3_mark_inode_dirty(handle, inode);
+
+ /* had we spliced it onto indirect block? */
+ if (where->bh) {
+ /*
+ * akpm: If we spliced it onto an indirect block, we haven't
+ * altered the inode. Note however that if it is being spliced
+ * onto an indirect block at the very end of the file (the
+ * file is growing) then we *will* alter the inode to reflect
+ * the new i_size. But that is not done here - it is done in
+ * generic_commit_write->__mark_inode_dirty->ext3_dirty_inode.
+ */
+ jbd_debug(5, "splicing indirect only\n");
+ BUFFER_TRACE(where->bh, "call ext3_journal_dirty_metadata");
+ err = ext3_journal_dirty_metadata(handle, where->bh);
+ if (err)
+ goto err_out;
+ } else {
+ /*
+ * OK, we spliced it into the inode itself on a direct block.
+ * Inode was dirtied above.
+ */
+ jbd_debug(5, "splicing direct\n");
+ }
+ return err;
+
+changed:
+ /*
+ * AKPM: if where[i].bh isn't part of the current updating
+ * transaction then we explode nastily. Test this code path.
+ */
+ jbd_debug(1, "the chain changed: try again\n");
+ err = -EAGAIN;
+
+err_out:
+ for (i = 1; i < num; i++) {
+ BUFFER_TRACE(where[i].bh, "call journal_forget");
+ ext3_journal_forget(handle, where[i].bh);
+ }
+ /* For the normal collision cleanup case, we free up the blocks.
+ * On genuine filesystem errors we don't even think about doing
+ * that. */
+ if (err == -EAGAIN)
+ for (i = 0; i < num; i++)
+ ext3_free_blocks(handle, inode,
+ le32_to_cpu(where[i].key), 1);
+ return err;
+}
+
+/*
+ * Allocation strategy is simple: if we have to allocate something, we will
+ * have to go the whole way to leaf. So let's do it before attaching anything
+ * to tree, set linkage between the newborn blocks, write them if sync is
+ * required, recheck the path, free and repeat if check fails, otherwise
+ * set the last missing link (that will protect us from any truncate-generated
+ * removals - all blocks on the path are immune now) and possibly force the
+ * write on the parent block.
+ * That has a nice additional property: no special recovery from the failed
+ * allocations is needed - we simply release blocks and do not touch anything
+ * reachable from inode.
+ *
+ * akpm: `handle' can be NULL if create == 0.
+ *
+ * The BKL may not be held on entry here. Be sure to take it early.
+ */
+
+static int ext3_get_block_handle(handle_t *handle, struct inode *inode,
+ long iblock,
+ struct buffer_head *bh_result, int create)
+{
+ int err = -EIO;
+ int offsets[4];
+ Indirect chain[4];
+ Indirect *partial;
+ unsigned long goal;
+ int left;
+ int depth = ext3_block_to_path(inode, iblock, offsets);
+ loff_t new_size;
+
+ J_ASSERT(handle != NULL || create == 0);
+
+ if (depth == 0)
+ goto out;
+
+ lock_kernel();
+reread:
+ partial = ext3_get_branch(inode, depth, offsets, chain, &err);
+
+ /* Simplest case - block found, no allocation needed */
+ if (!partial) {
+ bh_result->b_state &= ~(1UL << BH_New);
+got_it:
+ bh_result->b_dev = inode->i_dev;
+ bh_result->b_blocknr = le32_to_cpu(chain[depth-1].key);
+ bh_result->b_state |= (1UL << BH_Mapped);
+ /* Clean up and exit */
+ partial = chain+depth-1; /* the whole chain */
+ goto cleanup;
+ }
+
+ /* Next simple case - plain lookup or failed read of indirect block */
+ if (!create || err == -EIO) {
+cleanup:
+ while (partial > chain) {
+ BUFFER_TRACE(partial->bh, "call brelse");
+ brelse(partial->bh);
+ partial--;
+ }
+ BUFFER_TRACE(bh_result, "returned");
+ unlock_kernel();
+out:
+ return err;
+ }
+
+ /*
+ * Indirect block might be removed by truncate while we were
+ * reading it. Handling of that case (forget what we've got and
+ * reread) is taken out of the main path.
+ */
+ if (err == -EAGAIN)
+ goto changed;
+
+ goal = 0;
+ if (ext3_find_goal(inode, iblock, chain, partial, &goal) < 0)
+ goto changed;
+
+ left = (chain + depth) - partial;
+
+ /*
+ * Block out ext3_truncate while we alter the tree
+ */
+ down_read(&inode->u.ext3_i.truncate_sem);
+ err = ext3_alloc_branch(handle, inode, left, goal,
+ offsets+(partial-chain), partial);
+
+ /* The ext3_splice_branch call will free and forget any buffers
+ * on the new chain if there is a failure, but that risks using
+ * up transaction credits, especially for bitmaps where the
+ * credits cannot be returned. Can we handle this somehow? We
+ * may need to return -EAGAIN upwards in the worst case. --sct */
+ if (!err)
+ err = ext3_splice_branch(handle, inode, iblock, chain,
+ partial, left);
+ up_read(&inode->u.ext3_i.truncate_sem);
+ if (err == -EAGAIN)
+ goto changed;
+ if (err)
+ goto cleanup;
+
+ new_size = inode->i_size;
+ /*
+ * This is not racy against ext3_truncate's modification of i_disksize
+ * because VM/VFS ensures that the file cannot be extended while
+ * truncate is in progress. It is racy between multiple parallel
+ * instances of get_block, but we have the BKL.
+ */
+ if (new_size > inode->u.ext3_i.i_disksize)
+ inode->u.ext3_i.i_disksize = new_size;
+
+ bh_result->b_state |= (1UL << BH_New);
+ goto got_it;
+
+changed:
+ while (partial > chain) {
+ jbd_debug(1, "buffer chain changed, retrying\n");
+ BUFFER_TRACE(partial->bh, "brelsing");
+ brelse(partial->bh);
+ partial--;
+ }
+ goto reread;
+}
+
+/*
+ * The BKL is not held on entry here.
+ */
+static int ext3_get_block(struct inode *inode, long iblock,
+ struct buffer_head *bh_result, int create)
+{
+ handle_t *handle = 0;
+ int ret;
+
+ if (create) {
+ handle = ext3_journal_current_handle();
+ J_ASSERT(handle != 0);
+ }
+ ret = ext3_get_block_handle(handle, inode, iblock, bh_result, create);
+ return ret;
+}
+
+/*
+ * `handle' can be NULL if create is zero
+ */
+struct buffer_head *ext3_getblk(handle_t *handle, struct inode * inode,
+ long block, int create, int * errp)
+{
+ struct buffer_head dummy;
+ int fatal = 0, err;
+
+ J_ASSERT(handle != NULL || create == 0);
+
+ dummy.b_state = 0;
+ dummy.b_blocknr = -1000;
+ buffer_trace_init(&dummy.b_history);
+ *errp = ext3_get_block_handle(handle, inode, block, &dummy, create);
+ if (!*errp && buffer_mapped(&dummy)) {
+ struct buffer_head *bh;
+ bh = sb_getblk(inode->i_sb, dummy.b_blocknr);
+ if (buffer_new(&dummy)) {
+ J_ASSERT(create != 0);
+ J_ASSERT(handle != 0);
+
+ /* Now that we do not always journal data, we
+ should keep in mind whether this should
+ always journal the new buffer as metadata.
+ For now, regular file writes use
+ ext3_get_block instead, so it's not a
+ problem. */
+ lock_kernel();
+ lock_buffer(bh);
+ BUFFER_TRACE(bh, "call get_create_access");
+ fatal = ext3_journal_get_create_access(handle, bh);
+ if (!fatal) {
+ memset(bh->b_data, 0,
+ inode->i_sb->s_blocksize);
+ mark_buffer_uptodate(bh, 1);
+ }
+ unlock_buffer(bh);
+ BUFFER_TRACE(bh, "call ext3_journal_dirty_metadata");
+ err = ext3_journal_dirty_metadata(handle, bh);
+ if (!fatal) fatal = err;
+ unlock_kernel();
+ } else {
+ BUFFER_TRACE(bh, "not a new buffer");
+ }
+ if (fatal) {
+ *errp = fatal;
+ brelse(bh);
+ bh = NULL;
+ }
+ return bh;
+ }
+ return NULL;
+}
+
+struct buffer_head *ext3_bread(handle_t *handle, struct inode * inode,
+ int block, int create, int *err)
+{
+ struct buffer_head * bh;
+ int prev_blocks;
+
+ prev_blocks = inode->i_blocks;
+
+ bh = ext3_getblk (handle, inode, block, create, err);
+ if (!bh)
+ return bh;
+#ifdef EXT3_PREALLOCATE
+ /*
+ * If the inode has grown, and this is a directory, then use a few
+ * more of the preallocated blocks to keep directory fragmentation
+ * down. The preallocated blocks are guaranteed to be contiguous.
+ */
+ if (create &&
+ S_ISDIR(inode->i_mode) &&
+ inode->i_blocks > prev_blocks &&
+ EXT3_HAS_COMPAT_FEATURE(inode->i_sb,
+ EXT3_FEATURE_COMPAT_DIR_PREALLOC)) {
+ int i;
+ struct buffer_head *tmp_bh;
+
+ for (i = 1;
+ inode->u.ext3_i.i_prealloc_count &&
+ i < EXT3_SB(inode->i_sb)->s_es->s_prealloc_dir_blocks;
+ i++) {
+ /*
+ * ext3_getblk will zero out the contents of the
+ * directory for us
+ */
+ tmp_bh = ext3_getblk(handle, inode,
+ block+i, create, err);
+ if (!tmp_bh) {
+ brelse (bh);
+ return 0;
+ }
+ brelse (tmp_bh);
+ }
+ }
+#endif
+ if (buffer_uptodate(bh))
+ return bh;
+ ll_rw_block (READ, 1, &bh);
+ wait_on_buffer (bh);
+ if (buffer_uptodate(bh))
+ return bh;
+ brelse (bh);
+ *err = -EIO;
+ return NULL;
+}
+
+static int walk_page_buffers( handle_t *handle,
+ struct inode *inode,
+ struct buffer_head *head,
+ unsigned from,
+ unsigned to,
+ int *partial,
+ int (*fn)( handle_t *handle,
+ struct inode *inode,
+ struct buffer_head *bh))
+{
+ struct buffer_head *bh;
+ unsigned block_start, block_end;
+ unsigned blocksize = head->b_size;
+ int err, ret = 0;
+
+ for ( bh = head, block_start = 0;
+ ret == 0 && (bh != head || !block_start);
+ block_start = block_end, bh = bh->b_this_page)
+ {
+ block_end = block_start + blocksize;
+ if (block_end <= from || block_start >= to) {
+ if (partial && !buffer_uptodate(bh))
+ *partial = 1;
+ continue;
+ }
+ err = (*fn)(handle, inode, bh);
+ if (!ret)
+ ret = err;
+ }
+ return ret;
+}
+
+/*
+ * To preserve ordering, it is essential that the hole instantiation and
+ * the data write be encapsulated in a single transaction. We cannot
+ * close off a transaction and start a new one between the ext3_get_block()
+ * and the commit_write(). So doing the journal_start at the start of
+ * prepare_write() is the right place.
+ *
+ * Also, this function can nest inside ext3_writepage() ->
+ * block_write_full_page(). In that case, we *know* that ext3_writepage()
+ * has generated enough buffer credits to do the whole page. So we won't
+ * block on the journal in that case, which is good, because the caller may
+ * be PF_MEMALLOC.
+ *
+ * By accident, ext3 can be reentered when a transaction is open via
+ * quota file writes. If we were to commit the transaction while thus
+ * reentered, there can be a deadlock - we would be holding a quota
+ * lock, and the commit would never complete if another thread had a
+ * transaction open and was blocking on the quota lock - a ranking
+ * violation.
+ *
+ * So what we do is to rely on the fact that journal_stop/journal_start
+ * will _not_ run commit under these circumstances because handle->h_ref
+ * is elevated. We'll still have enough credits for the tiny quotafile
+ * write.
+ */
+
+static int do_journal_get_write_access(handle_t *handle, struct inode *inode,
+ struct buffer_head *bh)
+{
+ return ext3_journal_get_write_access(handle, bh);
+}
+
+static int ext3_prepare_write(struct file *file, struct page *page,
+ unsigned from, unsigned to)
+{
+ struct inode *inode = page->mapping->host;
+ int ret, needed_blocks = ext3_writepage_trans_blocks(inode);
+ handle_t *handle;
+
+ lock_kernel();
+ handle = ext3_journal_start(inode, needed_blocks);
+ if (IS_ERR(handle)) {
+ ret = PTR_ERR(handle);
+ goto out;
+ }
+ unlock_kernel();
+ ret = block_prepare_write(page, from, to, ext3_get_block);
+ lock_kernel();
+ if (ret != 0)
+ goto prepare_write_failed;
+
+ if (ext3_should_journal_data(inode)) {
+ ret = walk_page_buffers(handle, inode, page->buffers,
+ from, to, NULL, do_journal_get_write_access);
+ if (ret) {
+ /*
+ * We're going to fail this prepare_write(),
+ * so commit_write() will not be called.
+ * We need to undo block_prepare_write()'s kmap().
+ * AKPM: Do we need to clear PageUptodate? I don't
+ * think so.
+ */
+ kunmap(page);
+ }
+ }
+prepare_write_failed:
+ if (ret)
+ ext3_journal_stop(handle, inode);
+out:
+ unlock_kernel();
+ return ret;
+}
+
+static int journal_dirty_sync_data(handle_t *handle, struct inode *inode,
+ struct buffer_head *bh)
+{
+ int ret = ext3_journal_dirty_data(handle, bh, 0);
+ buffer_insert_inode_data_queue(bh, inode);
+ return ret;
+}
+
+/*
+ * For ext3_writepage(). We also brelse() the buffer to account for
+ * the bget() which ext3_writepage() performs.
+ */
+static int journal_dirty_async_data(handle_t *handle, struct inode *inode,
+ struct buffer_head *bh)
+{
+ int ret = ext3_journal_dirty_data(handle, bh, 1);
+ buffer_insert_inode_data_queue(bh, inode);
+ __brelse(bh);
+ return ret;
+}
+
+/* For commit_write() in data=journal mode */
+static int commit_write_fn(handle_t *handle, struct inode *inode,
+ struct buffer_head *bh)
+{
+ set_bit(BH_Uptodate, &bh->b_state);
+ return ext3_journal_dirty_metadata(handle, bh);
+}
+
+/*
+ * We need to pick up the new inode size which generic_commit_write gave us
+ * `file' can be NULL - eg, when called from block_symlink().
+ *
+ * ext3 inode->i_dirty_buffers policy: If we're journalling data we
+ * definitely don't want them to appear on the inode at all - instead
+ * we need to manage them at the JBD layer and we need to intercept
+ * the relevant sync operations and translate them into journal operations.
+ *
+ * If we're not journalling data then we can just leave the buffers
+ * on ->i_dirty_buffers. If someone writes them out for us then thanks.
+ * Otherwise we'll do it in commit, if we're using ordered data.
+ */
+
+static int ext3_commit_write(struct file *file, struct page *page,
+ unsigned from, unsigned to)
+{
+ handle_t *handle = ext3_journal_current_handle();
+ struct inode *inode = page->mapping->host;
+ int ret = 0, ret2;
+
+ lock_kernel();
+ if (ext3_should_journal_data(inode)) {
+ /*
+ * Here we duplicate the generic_commit_write() functionality
+ */
+ int partial = 0;
+ loff_t pos = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to;
+
+ ret = walk_page_buffers(handle, inode, page->buffers,
+ from, to, &partial, commit_write_fn);
+ if (!partial)
+ SetPageUptodate(page);
+ kunmap(page);
+ if (pos > inode->i_size)
+ inode->i_size = pos;
+ EXT3_I(inode)->i_state |= EXT3_STATE_JDATA;
+ } else {
+ if (ext3_should_order_data(inode)) {
+ ret = walk_page_buffers(handle, inode, page->buffers,
+ from, to, NULL, journal_dirty_sync_data);
+ }
+ /* Be careful here if generic_commit_write becomes a
+ * required invocation after block_prepare_write. */
+ if (ret == 0) {
+ ret = generic_commit_write(file, page, from, to);
+ } else {
+ /*
+ * block_prepare_write() was called, but we're not
+ * going to call generic_commit_write(). So we
+ * need to perform generic_commit_write()'s kunmap
+ * by hand.
+ */
+ kunmap(page);
+ }
+ }
+ if (inode->i_size > inode->u.ext3_i.i_disksize) {
+ inode->u.ext3_i.i_disksize = inode->i_size;
+ ret2 = ext3_mark_inode_dirty(handle, inode);
+ if (!ret)
+ ret = ret2;
+ }
+ ret2 = ext3_journal_stop(handle, inode);
+ unlock_kernel();
+ if (!ret)
+ ret = ret2;
+ return ret;
+}
+
+/*
+ * bmap() is special. It gets used by applications such as lilo and by
+ * the swapper to find the on-disk block of a specific piece of data.
+ *
+ * Naturally, this is dangerous if the block concerned is still in the
+ * journal. If somebody makes a swapfile on an ext3 data-journaling
+ * filesystem and enables swap, then they may get a nasty shock when the
+ * data getting swapped to that swapfile suddenly gets overwritten by
+ * the original zero's written out previously to the journal and
+ * awaiting writeback in the kernel's buffer cache.
+ *
+ * So, if we see any bmap calls here on a modified, data-journaled file,
+ * take extra steps to flush any blocks which might be in the cache.
+ */
+static int ext3_bmap(struct address_space *mapping, long block)
+{
+ struct inode *inode = mapping->host;
+ journal_t *journal;
+ int err;
+
+ if (EXT3_I(inode)->i_state & EXT3_STATE_JDATA) {
+ /*
+ * This is a REALLY heavyweight approach, but the use of
+ * bmap on dirty files is expected to be extremely rare:
+ * only if we run lilo or swapon on a freshly made file
+ * do we expect this to happen.
+ *
+ * (bmap requires CAP_SYS_RAWIO so this does not
+ * represent an unprivileged user DOS attack --- we'd be
+ * in trouble if mortal users could trigger this path at
+ * will.)
+ *
+ * NB. EXT3_STATE_JDATA is not set on files other than
+ * regular files. If somebody wants to bmap a directory
+ * or symlink and gets confused because the buffer
+ * hasn't yet been flushed to disk, they deserve
+ * everything they get.
+ */
+
+ EXT3_I(inode)->i_state &= ~EXT3_STATE_JDATA;
+ journal = EXT3_JOURNAL(inode);
+ journal_lock_updates(journal);
+ err = journal_flush(journal);
+ journal_unlock_updates(journal);
+
+ if (err)
+ return 0;
+ }
+
+ return generic_block_bmap(mapping,block,ext3_get_block);
+}
+
+static int bget_one(handle_t *handle, struct inode *inode,
+ struct buffer_head *bh)
+{
+ atomic_inc(&bh->b_count);
+ return 0;
+}
+
+/*
+ * Note that we always start a transaction even if we're not journalling
+ * data. This is to preserve ordering: any hole instantiation within
+ * __block_write_full_page -> ext3_get_block() should be journalled
+ * along with the data so we don't crash and then get metadata which
+ * refers to old data.
+ *
+ * In all journalling modes block_write_full_page() will start the I/O.
+ *
+ * Problem:
+ *
+ * ext3_writepage() -> kmalloc() -> __alloc_pages() -> page_launder() ->
+ * ext3_writepage()
+ *
+ * Similar for:
+ *
+ * ext3_file_write() -> generic_file_write() -> __alloc_pages() -> ...
+ *
+ * Same applies to ext3_get_block(). We will deadlock on various things like
+ * lock_journal and i_truncate_sem.
+ *
+ * Setting PF_MEMALLOC here doesn't work - too many internal memory
+ * allocations fail.
+ *
+ * 16May01: If we're reentered then journal_current_handle() will be
+ * non-zero. We simply *return*.
+ *
+ * 1 July 2001: @@@ FIXME:
+ * In journalled data mode, a data buffer may be metadata against the
+ * current transaction. But the same file is part of a shared mapping
+ * and someone does a writepage() on it.
+ *
+ * We will move the buffer onto the async_data list, but *after* it has
+ * been dirtied. So there's a small window where we have dirty data on
+ * BJ_Metadata.
+ *
+ * Note that this only applies to the last partial page in the file. The
+ * bit which block_write_full_page() uses prepare/commit for. (That's
+ * broken code anyway: it's wrong for msync()).
+ *
+ * It's a rare case: affects the final partial page, for journalled data
+ * where the file is subject to bith write() and writepage() in the same
+ * transction. To fix it we'll need a custom block_write_full_page().
+ * We'll probably need that anyway for journalling writepage() output.
+ *
+ * We don't honour synchronous mounts for writepage(). That would be
+ * disastrous. Any write() or metadata operation will sync the fs for
+ * us.
+ */
+static int ext3_writepage(struct page *page)
+{
+ struct inode *inode = page->mapping->host;
+ struct buffer_head *page_buffers;
+ handle_t *handle = NULL;
+ int ret = 0, err;
+ int needed;
+ int order_data;
+
+ J_ASSERT(PageLocked(page));
+
+ /*
+ * We give up here if we're reentered, because it might be
+ * for a different filesystem. One *could* look for a
+ * nested transaction opportunity.
+ */
+ lock_kernel();
+ if (ext3_journal_current_handle())
+ goto out_fail;
+
+ needed = ext3_writepage_trans_blocks(inode);
+ if (current->flags & PF_MEMALLOC)
+ handle = ext3_journal_try_start(inode, needed);
+ else
+ handle = ext3_journal_start(inode, needed);
+
+ if (IS_ERR(handle)) {
+ ret = PTR_ERR(handle);
+ goto out_fail;
+ }
+
+ order_data = ext3_should_order_data(inode) ||
+ ext3_should_journal_data(inode);
+
+ unlock_kernel();
+
+ page_buffers = NULL; /* Purely to prevent compiler warning */
+
+ /* bget() all the buffers */
+ if (order_data) {
+ if (!page->buffers)
+ create_empty_buffers(page,
+ inode->i_dev, inode->i_sb->s_blocksize);
+ page_buffers = page->buffers;
+ walk_page_buffers(handle, inode, page_buffers, 0,
+ PAGE_CACHE_SIZE, NULL, bget_one);
+ }
+
+ ret = block_write_full_page(page, ext3_get_block);
+
+ /*
+ * The page can become unlocked at any point now, and
+ * truncate can then come in and change things. So we
+ * can't touch *page from now on. But *page_buffers is
+ * safe due to elevated refcount.
+ */
+
+ handle = ext3_journal_current_handle();
+ lock_kernel();
+
+ /* And attach them to the current transaction */
+ if (order_data) {
+ err = walk_page_buffers(handle, inode, page_buffers,
+ 0, PAGE_CACHE_SIZE, NULL, journal_dirty_async_data);
+ if (!ret)
+ ret = err;
+ }
+
+ err = ext3_journal_stop(handle, inode);
+ if (!ret)
+ ret = err;
+ unlock_kernel();
+ return ret;
+
+out_fail:
+
+ unlock_kernel();
+ SetPageDirty(page);
+ UnlockPage(page);
+ return ret;
+}
+
+static int ext3_readpage(struct file *file, struct page *page)
+{
+ return block_read_full_page(page,ext3_get_block);
+}
+
+
+static int ext3_flushpage(struct page *page, unsigned long offset)
+{
+ journal_t *journal = EXT3_JOURNAL(page->mapping->host);
+ return journal_flushpage(journal, page, offset);
+}
+
+static int ext3_releasepage(struct page *page, int wait)
+{
+ journal_t *journal = EXT3_JOURNAL(page->mapping->host);
+ return journal_try_to_free_buffers(journal, page, wait);
+}
+
+
+struct address_space_operations ext3_aops = {
+ readpage: ext3_readpage, /* BKL not held. Don't need */
+ writepage: ext3_writepage, /* BKL not held. We take it */
+ sync_page: block_sync_page,
+ prepare_write: ext3_prepare_write, /* BKL not held. We take it */
+ commit_write: ext3_commit_write, /* BKL not held. We take it */
+ bmap: ext3_bmap, /* BKL held */
+ flushpage: ext3_flushpage, /* BKL not held. Don't need */
+ releasepage: ext3_releasepage, /* BKL not held. Don't need */
+};
+
+/*
+ * ext3_block_truncate_page() zeroes out a mapping from file offset `from'
+ * up to the end of the block which corresponds to `from'.
+ * This required during truncate. We need to physically zero the tail end
+ * of that block so it doesn't yield old data if the file is later grown.
+ */
+static int ext3_block_truncate_page(handle_t *handle,
+ struct address_space *mapping, loff_t from)
+{
+ unsigned long index = from >> PAGE_CACHE_SHIFT;
+ unsigned offset = from & (PAGE_CACHE_SIZE-1);
+ unsigned blocksize, iblock, length, pos;
+ struct inode *inode = mapping->host;
+ struct page *page;
+ struct buffer_head *bh;
+ int err;
+
+ blocksize = inode->i_sb->s_blocksize;
+ length = offset & (blocksize - 1);
+
+ /* Block boundary? Nothing to do */
+ if (!length)
+ return 0;
+
+ length = blocksize - length;
+ iblock = index << (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits);
+
+ page = find_or_create_page(mapping, index, GFP_NOFS);
+ err = -ENOMEM;
+ if (!page)
+ goto out;
+
+ if (!page->buffers)
+ create_empty_buffers(page, inode->i_dev, blocksize);
+
+ /* Find the buffer that contains "offset" */
+ bh = page->buffers;
+ pos = blocksize;
+ while (offset >= pos) {
+ bh = bh->b_this_page;
+ iblock++;
+ pos += blocksize;
+ }
+
+ err = 0;
+ if (!buffer_mapped(bh)) {
+ /* Hole? Nothing to do */
+ if (buffer_uptodate(bh))
+ goto unlock;
+ ext3_get_block(inode, iblock, bh, 0);
+ /* Still unmapped? Nothing to do */
+ if (!buffer_mapped(bh))
+ goto unlock;
+ }
+
+ /* Ok, it's mapped. Make sure it's up-to-date */
+ if (Page_Uptodate(page))
+ set_bit(BH_Uptodate, &bh->b_state);
+
+ if (!buffer_uptodate(bh)) {
+ err = -EIO;
+ ll_rw_block(READ, 1, &bh);
+ wait_on_buffer(bh);
+ /* Uhhuh. Read error. Complain and punt. */
+ if (!buffer_uptodate(bh))
+ goto unlock;
+ }
+
+ if (ext3_should_journal_data(inode)) {
+ BUFFER_TRACE(bh, "get write access");
+ err = ext3_journal_get_write_access(handle, bh);
+ if (err)
+ goto unlock;
+ }
+
+ memset(kmap(page) + offset, 0, length);
+ flush_dcache_page(page);
+ kunmap(page);
+
+ BUFFER_TRACE(bh, "zeroed end of block");
+
+ err = 0;
+ if (ext3_should_journal_data(inode)) {
+ err = ext3_journal_dirty_metadata(handle, bh);
+ } else {
+ if (ext3_should_order_data(inode))
+ err = ext3_journal_dirty_data(handle, bh, 0);
+ __mark_buffer_dirty(bh);
+ }
+
+unlock:
+ UnlockPage(page);
+ page_cache_release(page);
+out:
+ return err;
+}
+
+/*
+ * Probably it should be a library function... search for first non-zero word
+ * or memcmp with zero_page, whatever is better for particular architecture.
+ * Linus?
+ */
+static inline int all_zeroes(u32 *p, u32 *q)
+{
+ while (p < q)
+ if (*p++)
+ return 0;
+ return 1;
+}
+
+/**
+ * ext3_find_shared - find the indirect blocks for partial truncation.
+ * @inode: inode in question
+ * @depth: depth of the affected branch
+ * @offsets: offsets of pointers in that branch (see ext3_block_to_path)
+ * @chain: place to store the pointers to partial indirect blocks
+ * @top: place to the (detached) top of branch
+ *
+ * This is a helper function used by ext3_truncate().
+ *
+ * When we do truncate() we may have to clean the ends of several
+ * indirect blocks but leave the blocks themselves alive. Block is
+ * partially truncated if some data below the new i_size is refered
+ * from it (and it is on the path to the first completely truncated
+ * data block, indeed). We have to free the top of that path along
+ * with everything to the right of the path. Since no allocation
+ * past the truncation point is possible until ext3_truncate()
+ * finishes, we may safely do the latter, but top of branch may
+ * require special attention - pageout below the truncation point
+ * might try to populate it.
+ *
+ * We atomically detach the top of branch from the tree, store the
+ * block number of its root in *@top, pointers to buffer_heads of
+ * partially truncated blocks - in @chain[].bh and pointers to
+ * their last elements that should not be removed - in
+ * @chain[].p. Return value is the pointer to last filled element
+ * of @chain.
+ *
+ * The work left to caller to do the actual freeing of subtrees:
+ * a) free the subtree starting from *@top
+ * b) free the subtrees whose roots are stored in
+ * (@chain[i].p+1 .. end of @chain[i].bh->b_data)
+ * c) free the subtrees growing from the inode past the @chain[0].
+ * (no partially truncated stuff there). */
+
+static Indirect *ext3_find_shared(struct inode *inode,
+ int depth,
+ int offsets[4],
+ Indirect chain[4],
+ u32 *top)
+{
+ Indirect *partial, *p;
+ int k, err;
+
+ *top = 0;
+ /* Make k index the deepest non-null offest + 1 */
+ for (k = depth; k > 1 && !offsets[k-1]; k--)
+ ;
+ partial = ext3_get_branch(inode, k, offsets, chain, &err);
+ /* Writer: pointers */
+ if (!partial)
+ partial = chain + k-1;
+ /*
+ * If the branch acquired continuation since we've looked at it -
+ * fine, it should all survive and (new) top doesn't belong to us.
+ */
+ if (!partial->key && *partial->p)
+ /* Writer: end */
+ goto no_top;
+ for (p=partial; p>chain && all_zeroes((u32*)p->bh->b_data,p->p); p--)
+ ;
+ /*
+ * OK, we've found the last block that must survive. The rest of our
+ * branch should be detached before unlocking. However, if that rest
+ * of branch is all ours and does not grow immediately from the inode
+ * it's easier to cheat and just decrement partial->p.
+ */
+ if (p == chain + k - 1 && p > chain) {
+ p->p--;
+ } else {
+ *top = *p->p;
+ /* Nope, don't do this in ext3. Must leave the tree intact */
+#if 0
+ *p->p = 0;
+#endif
+ }
+ /* Writer: end */
+
+ while(partial > p)
+ {
+ brelse(partial->bh);
+ partial--;
+ }
+no_top:
+ return partial;
+}
+
+/*
+ * Zero a number of block pointers in either an inode or an indirect block.
+ * If we restart the transaction we must again get write access to the
+ * indirect block for further modification.
+ *
+ * We release `count' blocks on disk, but (last - first) may be greater
+ * than `count' because there can be holes in there.
+ */
+static void
+ext3_clear_blocks(handle_t *handle, struct inode *inode, struct buffer_head *bh,
+ unsigned long block_to_free, unsigned long count,
+ u32 *first, u32 *last)
+{
+ u32 *p;
+ if (try_to_extend_transaction(handle, inode)) {
+ if (bh) {
+ BUFFER_TRACE(bh, "call ext3_journal_dirty_metadata");
+ ext3_journal_dirty_metadata(handle, bh);
+ }
+ ext3_mark_inode_dirty(handle, inode);
+ ext3_journal_test_restart(handle, inode);
+ if (bh) {
+ BUFFER_TRACE(bh, "retaking write access");
+ ext3_journal_get_write_access(handle, bh);
+ }
+ }
+
+ /*
+ * Any buffers which are on the journal will be in memory. We find
+ * them on the hash table so journal_revoke() will run journal_forget()
+ * on them. We've already detached each block from the file, so
+ * bforget() in journal_forget() should be safe.
+ *
+ * AKPM: turn on bforget in journal_forget()!!!
+ */
+ for (p = first; p < last; p++) {
+ u32 nr = le32_to_cpu(*p);
+ if (nr) {
+ struct buffer_head *bh;
+
+ *p = 0;
+ bh = sb_get_hash_table(inode->i_sb, nr);
+ ext3_forget(handle, 0, inode, bh, nr);
+ }
+ }
+
+ ext3_free_blocks(handle, inode, block_to_free, count);
+}
+
+/**
+ * ext3_free_data - free a list of data blocks
+ * @handle: handle for this transaction
+ * @inode: inode we are dealing with
+ * @this_bh: indirect buffer_head which contains *@first and *@last
+ * @first: array of block numbers
+ * @last: points immediately past the end of array
+ *
+ * We are freeing all blocks refered from that array (numbers are stored as
+ * little-endian 32-bit) and updating @inode->i_blocks appropriately.
+ *
+ * We accumulate contiguous runs of blocks to free. Conveniently, if these
+ * blocks are contiguous then releasing them at one time will only affect one
+ * or two bitmap blocks (+ group descriptor(s) and superblock) and we won't
+ * actually use a lot of journal space.
+ *
+ * @this_bh will be %NULL if @first and @last point into the inode's direct
+ * block pointers.
+ */
+static void ext3_free_data(handle_t *handle, struct inode *inode,
+ struct buffer_head *this_bh, u32 *first, u32 *last)
+{
+ unsigned long block_to_free = 0; /* Starting block # of a run */
+ unsigned long count = 0; /* Number of blocks in the run */
+ u32 *block_to_free_p = NULL; /* Pointer into inode/ind
+ corresponding to
+ block_to_free */
+ unsigned long nr; /* Current block # */
+ u32 *p; /* Pointer into inode/ind
+ for current block */
+ int err;
+
+ if (this_bh) { /* For indirect block */
+ BUFFER_TRACE(this_bh, "get_write_access");
+ err = ext3_journal_get_write_access(handle, this_bh);
+ /* Important: if we can't update the indirect pointers
+ * to the blocks, we can't free them. */
+ if (err)
+ return;
+ }
+
+ for (p = first; p < last; p++) {
+ nr = le32_to_cpu(*p);
+ if (nr) {
+ /* accumulate blocks to free if they're contiguous */
+ if (count == 0) {
+ block_to_free = nr;
+ block_to_free_p = p;
+ count = 1;
+ } else if (nr == block_to_free + count) {
+ count++;
+ } else {
+ ext3_clear_blocks(handle, inode, this_bh,
+ block_to_free,
+ count, block_to_free_p, p);
+ block_to_free = nr;
+ block_to_free_p = p;
+ count = 1;
+ }
+ }
+ }
+
+ if (count > 0)
+ ext3_clear_blocks(handle, inode, this_bh, block_to_free,
+ count, block_to_free_p, p);
+
+ if (this_bh) {
+ BUFFER_TRACE(this_bh, "call ext3_journal_dirty_metadata");
+ ext3_journal_dirty_metadata(handle, this_bh);
+ }
+}
+
+/**
+ * ext3_free_branches - free an array of branches
+ * @handle: JBD handle for this transaction
+ * @inode: inode we are dealing with
+ * @parent_bh: the buffer_head which contains *@first and *@last
+ * @first: array of block numbers
+ * @last: pointer immediately past the end of array
+ * @depth: depth of the branches to free
+ *
+ * We are freeing all blocks refered from these branches (numbers are
+ * stored as little-endian 32-bit) and updating @inode->i_blocks
+ * appropriately.
+ */
+static void ext3_free_branches(handle_t *handle, struct inode *inode,
+ struct buffer_head *parent_bh,
+ u32 *first, u32 *last, int depth)
+{
+ unsigned long nr;
+ u32 *p;
+
+ if (is_handle_aborted(handle))
+ return;
+
+ if (depth--) {
+ struct buffer_head *bh;
+ int addr_per_block = EXT3_ADDR_PER_BLOCK(inode->i_sb);
+ p = last;
+ while (--p >= first) {
+ nr = le32_to_cpu(*p);
+ if (!nr)
+ continue; /* A hole */
+
+ /* Go read the buffer for the next level down */
+ bh = sb_bread(inode->i_sb, nr);
+
+ /*
+ * A read failure? Report error and clear slot
+ * (should be rare).
+ */
+ if (!bh) {
+ ext3_error(inode->i_sb, "ext3_free_branches",
+ "Read failure, inode=%ld, block=%ld",
+ inode->i_ino, nr);
+ continue;
+ }
+
+ /* This zaps the entire block. Bottom up. */
+ BUFFER_TRACE(bh, "free child branches");
+ ext3_free_branches(handle, inode, bh, (u32*)bh->b_data,
+ (u32*)bh->b_data + addr_per_block,
+ depth);
+
+ /*
+ * We've probably journalled the indirect block several
+ * times during the truncate. But it's no longer
+ * needed and we now drop it from the transaction via
+ * journal_revoke().
+ *
+ * That's easy if it's exclusively part of this
+ * transaction. But if it's part of the committing
+ * transaction then journal_forget() will simply
+ * brelse() it. That means that if the underlying
+ * block is reallocated in ext3_get_block(),
+ * unmap_underlying_metadata() will find this block
+ * and will try to get rid of it. damn, damn.
+ *
+ * If this block has already been committed to the
+ * journal, a revoke record will be written. And
+ * revoke records must be emitted *before* clearing
+ * this block's bit in the bitmaps.
+ */
+ ext3_forget(handle, 1, inode, bh, bh->b_blocknr);
+
+ /*
+ * Everything below this this pointer has been
+ * released. Now let this top-of-subtree go.
+ *
+ * We want the freeing of this indirect block to be
+ * atomic in the journal with the updating of the
+ * bitmap block which owns it. So make some room in
+ * the journal.
+ *
+ * We zero the parent pointer *after* freeing its
+ * pointee in the bitmaps, so if extend_transaction()
+ * for some reason fails to put the bitmap changes and
+ * the release into the same transaction, recovery
+ * will merely complain about releasing a free block,
+ * rather than leaking blocks.
+ */
+ if (is_handle_aborted(handle))
+ return;
+ if (try_to_extend_transaction(handle, inode)) {
+ ext3_mark_inode_dirty(handle, inode);
+ ext3_journal_test_restart(handle, inode);
+ }
+
+ ext3_free_blocks(handle, inode, nr, 1);
+
+ if (parent_bh) {
+ /*
+ * The block which we have just freed is
+ * pointed to by an indirect block: journal it
+ */
+ BUFFER_TRACE(parent_bh, "get_write_access");
+ if (!ext3_journal_get_write_access(handle,
+ parent_bh)){
+ *p = 0;
+ BUFFER_TRACE(parent_bh,
+ "call ext3_journal_dirty_metadata");
+ ext3_journal_dirty_metadata(handle,
+ parent_bh);
+ }
+ }
+ }
+ } else {
+ /* We have reached the bottom of the tree. */
+ BUFFER_TRACE(parent_bh, "free data blocks");
+ ext3_free_data(handle, inode, parent_bh, first, last);
+ }
+}
+
+/*
+ * ext3_truncate()
+ *
+ * We block out ext3_get_block() block instantiations across the entire
+ * transaction, and VFS/VM ensures that ext3_truncate() cannot run
+ * simultaneously on behalf of the same inode.
+ *
+ * As we work through the truncate and commmit bits of it to the journal there
+ * is one core, guiding principle: the file's tree must always be consistent on
+ * disk. We must be able to restart the truncate after a crash.
+ *
+ * The file's tree may be transiently inconsistent in memory (although it
+ * probably isn't), but whenever we close off and commit a journal transaction,
+ * the contents of (the filesystem + the journal) must be consistent and
+ * restartable. It's pretty simple, really: bottom up, right to left (although
+ * left-to-right works OK too).
+ *
+ * Note that at recovery time, journal replay occurs *before* the restart of
+ * truncate against the orphan inode list.
+ *
+ * The committed inode has the new, desired i_size (which is the same as
+ * i_disksize in this case). After a crash, ext3_orphan_cleanup() will see
+ * that this inode's truncate did not complete and it will again call
+ * ext3_truncate() to have another go. So there will be instantiated blocks
+ * to the right of the truncation point in a crashed ext3 filesystem. But
+ * that's fine - as long as they are linked from the inode, the post-crash
+ * ext3_truncate() run will find them and release them.
+ */
+
+void ext3_truncate(struct inode * inode)
+{
+ handle_t *handle;
+ u32 *i_data = inode->u.ext3_i.i_data;
+ int addr_per_block = EXT3_ADDR_PER_BLOCK(inode->i_sb);
+ int offsets[4];
+ Indirect chain[4];
+ Indirect *partial;
+ int nr = 0;
+ int n;
+ long last_block;
+ unsigned blocksize;
+
+ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+ S_ISLNK(inode->i_mode)))
+ return;
+ if (ext3_inode_is_fast_symlink(inode))
+ return;
+ if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
+ return;
+
+ ext3_discard_prealloc(inode);
+
+ handle = start_transaction(inode);
+ if (IS_ERR(handle))
+ return; /* AKPM: return what? */
+
+ blocksize = inode->i_sb->s_blocksize;
+ last_block = (inode->i_size + blocksize-1)
+ >> EXT3_BLOCK_SIZE_BITS(inode->i_sb);
+
+ ext3_block_truncate_page(handle, inode->i_mapping, inode->i_size);
+
+
+ n = ext3_block_to_path(inode, last_block, offsets);
+ if (n == 0)
+ goto out_stop; /* error */
+
+ /*
+ * OK. This truncate is going to happen. We add the inode to the
+ * orphan list, so that if this truncate spans multiple transactions,
+ * and we crash, we will resume the truncate when the filesystem
+ * recovers. It also marks the inode dirty, to catch the new size.
+ *
+ * Implication: the file must always be in a sane, consistent
+ * truncatable state while each transaction commits.
+ */
+ if (ext3_orphan_add(handle, inode))
+ goto out_stop;
+
+ /*
+ * The orphan list entry will now protect us from any crash which
+ * occurs before the truncate completes, so it is now safe to propagate
+ * the new, shorter inode size (held for now in i_size) into the
+ * on-disk inode. We do this via i_disksize, which is the value which
+ * ext3 *really* writes onto the disk inode.
+ */
+ inode->u.ext3_i.i_disksize = inode->i_size;
+
+ /*
+ * From here we block out all ext3_get_block() callers who want to
+ * modify the block allocation tree.
+ */
+ down_write(&inode->u.ext3_i.truncate_sem);
+
+ if (n == 1) { /* direct blocks */
+ ext3_free_data(handle, inode, NULL, i_data+offsets[0],
+ i_data + EXT3_NDIR_BLOCKS);
+ goto do_indirects;
+ }
+
+ partial = ext3_find_shared(inode, n, offsets, chain, &nr);
+ /* Kill the top of shared branch (not detached) */
+ if (nr) {
+ if (partial == chain) {
+ /* Shared branch grows from the inode */
+ ext3_free_branches(handle, inode, NULL,
+ &nr, &nr+1, (chain+n-1) - partial);
+ *partial->p = 0;
+ /*
+ * We mark the inode dirty prior to restart,
+ * and prior to stop. No need for it here.
+ */
+ } else {
+ /* Shared branch grows from an indirect block */
+ BUFFER_TRACE(partial->bh, "get_write_access");
+ ext3_free_branches(handle, inode, partial->bh,
+ partial->p,
+ partial->p+1, (chain+n-1) - partial);
+ }
+ }
+ /* Clear the ends of indirect blocks on the shared branch */
+ while (partial > chain) {
+ ext3_free_branches(handle, inode, partial->bh, partial->p + 1,
+ (u32*)partial->bh->b_data + addr_per_block,
+ (chain+n-1) - partial);
+ BUFFER_TRACE(partial->bh, "call brelse");
+ brelse (partial->bh);
+ partial--;
+ }
+do_indirects:
+ /* Kill the remaining (whole) subtrees */
+ switch (offsets[0]) {
+ default:
+ nr = i_data[EXT3_IND_BLOCK];
+ if (nr) {
+ ext3_free_branches(handle, inode, NULL,
+ &nr, &nr+1, 1);
+ i_data[EXT3_IND_BLOCK] = 0;
+ }
+ case EXT3_IND_BLOCK:
+ nr = i_data[EXT3_DIND_BLOCK];
+ if (nr) {
+ ext3_free_branches(handle, inode, NULL,
+ &nr, &nr+1, 2);
+ i_data[EXT3_DIND_BLOCK] = 0;
+ }
+ case EXT3_DIND_BLOCK:
+ nr = i_data[EXT3_TIND_BLOCK];
+ if (nr) {
+ ext3_free_branches(handle, inode, NULL,
+ &nr, &nr+1, 3);
+ i_data[EXT3_TIND_BLOCK] = 0;
+ }
+ case EXT3_TIND_BLOCK:
+ ;
+ }
+ up_write(&inode->u.ext3_i.truncate_sem);
+ inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ ext3_mark_inode_dirty(handle, inode);
+
+ /* In a multi-transaction truncate, we only make the final
+ * transaction synchronous */
+ if (IS_SYNC(inode))
+ handle->h_sync = 1;
+out_stop:
+ /*
+ * If this was a simple ftruncate(), and the file will remain alive
+ * then we need to clear up the orphan record which we created above.
+ * However, if this was a real unlink then we were called by
+ * ext3_delete_inode(), and we allow that function to clean up the
+ * orphan info for us.
+ */
+ if (inode->i_nlink)
+ ext3_orphan_del(handle, inode);
+
+ ext3_journal_stop(handle, inode);
+}
+
+/*
+ * ext3_get_inode_loc returns with an extra refcount against the
+ * inode's underlying buffer_head on success.
+ */
+
+int ext3_get_inode_loc (struct inode *inode, struct ext3_iloc *iloc)
+{
+ struct buffer_head *bh = 0;
+ unsigned long block;
+ unsigned long block_group;
+ unsigned long group_desc;
+ unsigned long desc;
+ unsigned long offset;
+ struct ext3_group_desc * gdp;
+
+ if ((inode->i_ino != EXT3_ROOT_INO &&
+ inode->i_ino != EXT3_ACL_IDX_INO &&
+ inode->i_ino != EXT3_ACL_DATA_INO &&
+ inode->i_ino != EXT3_JOURNAL_INO &&
+ inode->i_ino < EXT3_FIRST_INO(inode->i_sb)) ||
+ inode->i_ino > le32_to_cpu(
+ inode->i_sb->u.ext3_sb.s_es->s_inodes_count)) {
+ ext3_error (inode->i_sb, "ext3_get_inode_loc",
+ "bad inode number: %lu", inode->i_ino);
+ goto bad_inode;
+ }
+ block_group = (inode->i_ino - 1) / EXT3_INODES_PER_GROUP(inode->i_sb);
+ if (block_group >= inode->i_sb->u.ext3_sb.s_groups_count) {
+ ext3_error (inode->i_sb, "ext3_get_inode_loc",
+ "group >= groups count");
+ goto bad_inode;
+ }
+ group_desc = block_group >> EXT3_DESC_PER_BLOCK_BITS(inode->i_sb);
+ desc = block_group & (EXT3_DESC_PER_BLOCK(inode->i_sb) - 1);
+ bh = inode->i_sb->u.ext3_sb.s_group_desc[group_desc];
+ if (!bh) {
+ ext3_error (inode->i_sb, "ext3_get_inode_loc",
+ "Descriptor not loaded");
+ goto bad_inode;
+ }
+
+ gdp = (struct ext3_group_desc *) bh->b_data;
+ /*
+ * Figure out the offset within the block group inode table
+ */
+ offset = ((inode->i_ino - 1) % EXT3_INODES_PER_GROUP(inode->i_sb)) *
+ EXT3_INODE_SIZE(inode->i_sb);
+ block = le32_to_cpu(gdp[desc].bg_inode_table) +
+ (offset >> EXT3_BLOCK_SIZE_BITS(inode->i_sb));
+ if (!(bh = sb_bread(inode->i_sb, block))) {
+ ext3_error (inode->i_sb, "ext3_get_inode_loc",
+ "unable to read inode block - "
+ "inode=%lu, block=%lu", inode->i_ino, block);
+ goto bad_inode;
+ }
+ offset &= (EXT3_BLOCK_SIZE(inode->i_sb) - 1);
+
+ iloc->bh = bh;
+ iloc->raw_inode = (struct ext3_inode *) (bh->b_data + offset);
+ iloc->block_group = block_group;
+
+ return 0;
+
+ bad_inode:
+ return -EIO;
+}
+
+void ext3_set_inode_flags(struct inode *inode)
+{
+ unsigned int flags = inode->u.ext3_i.i_flags;
+
+ inode->i_flags &= ~(S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME);
+ if (flags & EXT3_SYNC_FL)
+ inode->i_flags |= S_SYNC;
+ if (flags & EXT3_APPEND_FL)
+ inode->i_flags |= S_APPEND;
+ if (flags & EXT3_IMMUTABLE_FL)
+ inode->i_flags |= S_IMMUTABLE;
+ if (flags & EXT3_NOATIME_FL)
+ inode->i_flags |= S_NOATIME;
+}
+
+
+void ext3_read_inode(struct inode * inode)
+{
+ struct ext3_iloc iloc;
+ struct ext3_inode *raw_inode;
+ struct buffer_head *bh;
+ int block;
+
+ if(ext3_get_inode_loc(inode, &iloc))
+ goto bad_inode;
+ bh = iloc.bh;
+ raw_inode = iloc.raw_inode;
+ init_rwsem(&inode->u.ext3_i.truncate_sem);
+ inode->i_mode = le16_to_cpu(raw_inode->i_mode);
+ inode->i_uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low);
+ inode->i_gid = (gid_t)le16_to_cpu(raw_inode->i_gid_low);
+ if(!(test_opt (inode->i_sb, NO_UID32))) {
+ inode->i_uid |= le16_to_cpu(raw_inode->i_uid_high) << 16;
+ inode->i_gid |= le16_to_cpu(raw_inode->i_gid_high) << 16;
+ }
+ inode->i_nlink = le16_to_cpu(raw_inode->i_links_count);
+ inode->i_size = le32_to_cpu(raw_inode->i_size);
+ inode->i_atime = le32_to_cpu(raw_inode->i_atime);
+ inode->i_ctime = le32_to_cpu(raw_inode->i_ctime);
+ inode->i_mtime = le32_to_cpu(raw_inode->i_mtime);
+ inode->u.ext3_i.i_dtime = le32_to_cpu(raw_inode->i_dtime);
+ /* We now have enough fields to check if the inode was active or not.
+ * This is needed because nfsd might try to access dead inodes
+ * the test is that same one that e2fsck uses
+ * NeilBrown 1999oct15
+ */
+ if (inode->i_nlink == 0) {
+ if (inode->i_mode == 0 ||
+ !(inode->i_sb->u.ext3_sb.s_mount_state & EXT3_ORPHAN_FS)) {
+ /* this inode is deleted */
+ brelse (bh);
+ goto bad_inode;
+ }
+ /* The only unlinked inodes we let through here have
+ * valid i_mode and are being read by the orphan
+ * recovery code: that's fine, we're about to complete
+ * the process of deleting those. */
+ }
+ inode->i_blksize = PAGE_SIZE; /* This is the optimal IO size
+ * (for stat), not the fs block
+ * size */
+ inode->i_blocks = le32_to_cpu(raw_inode->i_blocks);
+ inode->i_version = ++event;
+ inode->u.ext3_i.i_flags = le32_to_cpu(raw_inode->i_flags);
+#ifdef EXT3_FRAGMENTS
+ inode->u.ext3_i.i_faddr = le32_to_cpu(raw_inode->i_faddr);
+ inode->u.ext3_i.i_frag_no = raw_inode->i_frag;
+ inode->u.ext3_i.i_frag_size = raw_inode->i_fsize;
+#endif
+ inode->u.ext3_i.i_file_acl = le32_to_cpu(raw_inode->i_file_acl);
+ if (!S_ISREG(inode->i_mode)) {
+ inode->u.ext3_i.i_dir_acl = le32_to_cpu(raw_inode->i_dir_acl);
+ } else {
+ inode->i_size |=
+ ((__u64)le32_to_cpu(raw_inode->i_size_high)) << 32;
+ }
+ inode->u.ext3_i.i_disksize = inode->i_size;
+ inode->i_generation = le32_to_cpu(raw_inode->i_generation);
+#ifdef EXT3_PREALLOCATE
+ inode->u.ext3_i.i_prealloc_count = 0;
+#endif
+ inode->u.ext3_i.i_block_group = iloc.block_group;
+
+ /*
+ * NOTE! The in-memory inode i_data array is in little-endian order
+ * even on big-endian machines: we do NOT byteswap the block numbers!
+ */
+ for (block = 0; block < EXT3_N_BLOCKS; block++)
+ inode->u.ext3_i.i_data[block] = iloc.raw_inode->i_block[block];
+ INIT_LIST_HEAD(&inode->u.ext3_i.i_orphan);
+
+ if (inode->i_ino == EXT3_ACL_IDX_INO ||
+ inode->i_ino == EXT3_ACL_DATA_INO)
+ /* Nothing to do */ ;
+ else if (S_ISREG(inode->i_mode)) {
+ inode->i_op = &ext3_file_inode_operations;
+ inode->i_fop = &ext3_file_operations;
+ inode->i_mapping->a_ops = &ext3_aops;
+ } else if (S_ISDIR(inode->i_mode)) {
+ inode->i_op = &ext3_dir_inode_operations;
+ inode->i_fop = &ext3_dir_operations;
+ } else if (S_ISLNK(inode->i_mode)) {
+ if (ext3_inode_is_fast_symlink(inode))
+ inode->i_op = &ext3_fast_symlink_inode_operations;
+ else {
+ inode->i_op = &page_symlink_inode_operations;
+ inode->i_mapping->a_ops = &ext3_aops;
+ }
+ } else
+ init_special_inode(inode, inode->i_mode,
+ le32_to_cpu(iloc.raw_inode->i_block[0]));
+ brelse(iloc.bh);
+ ext3_set_inode_flags(inode);
+ return;
+
+bad_inode:
+ make_bad_inode(inode);
+ return;
+}
+
+/*
+ * Post the struct inode info into an on-disk inode location in the
+ * buffer-cache. This gobbles the caller's reference to the
+ * buffer_head in the inode location struct.
+ */
+
+static int ext3_do_update_inode(handle_t *handle,
+ struct inode *inode,
+ struct ext3_iloc *iloc)
+{
+ struct ext3_inode *raw_inode = iloc->raw_inode;
+ struct buffer_head *bh = iloc->bh;
+ int err = 0, rc, block;
+
+ if (handle) {
+ BUFFER_TRACE(bh, "get_write_access");
+ err = ext3_journal_get_write_access(handle, bh);
+ if (err)
+ goto out_brelse;
+ }
+ /* For fields not not tracking in the in-memory inode,
+ * initialise them to zero for new inodes. */
+ if (EXT3_I(inode)->i_state & EXT3_STATE_NEW)
+ memset(raw_inode, 0, EXT3_SB(inode->i_sb)->s_inode_size);
+
+ raw_inode->i_mode = cpu_to_le16(inode->i_mode);
+ if(!(test_opt(inode->i_sb, NO_UID32))) {
+ raw_inode->i_uid_low = cpu_to_le16(low_16_bits(inode->i_uid));
+ raw_inode->i_gid_low = cpu_to_le16(low_16_bits(inode->i_gid));
+/*
+ * Fix up interoperability with old kernels. Otherwise, old inodes get
+ * re-used with the upper 16 bits of the uid/gid intact
+ */
+ if(!inode->u.ext3_i.i_dtime) {
+ raw_inode->i_uid_high =
+ cpu_to_le16(high_16_bits(inode->i_uid));
+ raw_inode->i_gid_high =
+ cpu_to_le16(high_16_bits(inode->i_gid));
+ } else {
+ raw_inode->i_uid_high = 0;
+ raw_inode->i_gid_high = 0;
+ }
+ } else {
+ raw_inode->i_uid_low =
+ cpu_to_le16(fs_high2lowuid(inode->i_uid));
+ raw_inode->i_gid_low =
+ cpu_to_le16(fs_high2lowgid(inode->i_gid));
+ raw_inode->i_uid_high = 0;
+ raw_inode->i_gid_high = 0;
+ }
+ raw_inode->i_links_count = cpu_to_le16(inode->i_nlink);
+ raw_inode->i_size = cpu_to_le32(inode->u.ext3_i.i_disksize);
+ raw_inode->i_atime = cpu_to_le32(inode->i_atime);
+ raw_inode->i_ctime = cpu_to_le32(inode->i_ctime);
+ raw_inode->i_mtime = cpu_to_le32(inode->i_mtime);
+ raw_inode->i_blocks = cpu_to_le32(inode->i_blocks);
+ raw_inode->i_dtime = cpu_to_le32(inode->u.ext3_i.i_dtime);
+ raw_inode->i_flags = cpu_to_le32(inode->u.ext3_i.i_flags);
+#ifdef EXT3_FRAGMENTS
+ raw_inode->i_faddr = cpu_to_le32(inode->u.ext3_i.i_faddr);
+ raw_inode->i_frag = inode->u.ext3_i.i_frag_no;
+ raw_inode->i_fsize = inode->u.ext3_i.i_frag_size;
+#endif
+ raw_inode->i_file_acl = cpu_to_le32(inode->u.ext3_i.i_file_acl);
+ if (!S_ISREG(inode->i_mode)) {
+ raw_inode->i_dir_acl = cpu_to_le32(inode->u.ext3_i.i_dir_acl);
+ } else {
+ raw_inode->i_size_high =
+ cpu_to_le32(inode->u.ext3_i.i_disksize >> 32);
+ if (inode->u.ext3_i.i_disksize > 0x7fffffffULL) {
+ struct super_block *sb = inode->i_sb;
+ if (!EXT3_HAS_RO_COMPAT_FEATURE(sb,
+ EXT3_FEATURE_RO_COMPAT_LARGE_FILE) ||
+ EXT3_SB(sb)->s_es->s_rev_level ==
+ cpu_to_le32(EXT3_GOOD_OLD_REV)) {
+ /* If this is the first large file
+ * created, add a flag to the superblock.
+ */
+ err = ext3_journal_get_write_access(handle,
+ sb->u.ext3_sb.s_sbh);
+ if (err)
+ goto out_brelse;
+ ext3_update_dynamic_rev(sb);
+ EXT3_SET_RO_COMPAT_FEATURE(sb,
+ EXT3_FEATURE_RO_COMPAT_LARGE_FILE);
+ sb->s_dirt = 1;
+ handle->h_sync = 1;
+ err = ext3_journal_dirty_metadata(handle,
+ sb->u.ext3_sb.s_sbh);
+ }
+ }
+ }
+ raw_inode->i_generation = cpu_to_le32(inode->i_generation);
+ if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+ raw_inode->i_block[0] =
+ cpu_to_le32(kdev_t_to_nr(inode->i_rdev));
+ else for (block = 0; block < EXT3_N_BLOCKS; block++)
+ raw_inode->i_block[block] = inode->u.ext3_i.i_data[block];
+
+ BUFFER_TRACE(bh, "call ext3_journal_dirty_metadata");
+ rc = ext3_journal_dirty_metadata(handle, bh);
+ if (!err)
+ err = rc;
+ EXT3_I(inode)->i_state &= ~EXT3_STATE_NEW;
+
+out_brelse:
+ brelse (bh);
+ ext3_std_error(inode->i_sb, err);
+ return err;
+}
+
+/*
+ * ext3_write_inode()
+ *
+ * We are called from a few places:
+ *
+ * - Within generic_file_write() for O_SYNC files.
+ * Here, there will be no transaction running. We wait for any running
+ * trasnaction to commit.
+ *
+ * - Within sys_sync(), kupdate and such.
+ * We wait on commit, if tol to.
+ *
+ * - Within prune_icache() (PF_MEMALLOC == true)
+ * Here we simply return. We can't afford to block kswapd on the
+ * journal commit.
+ *
+ * In all cases it is actually safe for us to return without doing anything,
+ * because the inode has been copied into a raw inode buffer in
+ * ext3_mark_inode_dirty(). This is a correctness thing for O_SYNC and for
+ * knfsd.
+ *
+ * Note that we are absolutely dependent upon all inode dirtiers doing the
+ * right thing: they *must* call mark_inode_dirty() after dirtying info in
+ * which we are interested.
+ *
+ * It would be a bug for them to not do this. The code:
+ *
+ * mark_inode_dirty(inode)
+ * stuff();
+ * inode->i_size = expr;
+ *
+ * is in error because a kswapd-driven write_inode() could occur while
+ * `stuff()' is running, and the new i_size will be lost. Plus the inode
+ * will no longer be on the superblock's dirty inode list.
+ */
+void ext3_write_inode(struct inode *inode, int wait)
+{
+ if (current->flags & PF_MEMALLOC)
+ return;
+
+ if (ext3_journal_current_handle()) {
+ jbd_debug(0, "called recursively, non-PF_MEMALLOC!\n");
+ return;
+ }
+
+ if (!wait)
+ return;
+
+ ext3_force_commit(inode->i_sb);
+}
+
+/*
+ * ext3_setattr()
+ *
+ * Called from notify_change.
+ *
+ * We want to trap VFS attempts to truncate the file as soon as
+ * possible. In particular, we want to make sure that when the VFS
+ * shrinks i_size, we put the inode on the orphan list and modify
+ * i_disksize immediately, so that during the subsequent flushing of
+ * dirty pages and freeing of disk blocks, we can guarantee that any
+ * commit will leave the blocks being flushed in an unused state on
+ * disk. (On recovery, the inode will get truncated and the blocks will
+ * be freed, so we have a strong guarantee that no future commit will
+ * leave these blocks visible to the user.)
+ *
+ * This is only needed for regular files. rmdir() has its own path, and
+ * we can never truncate a direcory except on final unlink (at which
+ * point i_nlink is zero so recovery is easy.)
+ *
+ * Called with the BKL.
+ */
+
+int ext3_setattr(struct dentry *dentry, struct iattr *attr)
+{
+ struct inode *inode = dentry->d_inode;
+ int error, rc = 0;
+ const unsigned int ia_valid = attr->ia_valid;
+
+ error = inode_change_ok(inode, attr);
+ if (error)
+ return error;
+
+ if ((ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) ||
+ (ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) {
+ error = DQUOT_TRANSFER(inode, attr) ? -EDQUOT : 0;
+ if (error)
+ return error;
+ }
+
+ if (attr->ia_valid & ATTR_SIZE && attr->ia_size < inode->i_size) {
+ handle_t *handle;
+
+ handle = ext3_journal_start(inode, 3);
+ if (IS_ERR(handle)) {
+ error = PTR_ERR(handle);
+ goto err_out;
+ }
+
+ error = ext3_orphan_add(handle, inode);
+ inode->u.ext3_i.i_disksize = attr->ia_size;
+ rc = ext3_mark_inode_dirty(handle, inode);
+ if (!error)
+ error = rc;
+ ext3_journal_stop(handle, inode);
+ }
+
+ rc = inode_setattr(inode, attr);
+
+ /* If inode_setattr's call to ext3_truncate failed to get a
+ * transaction handle at all, we need to clean up the in-core
+ * orphan list manually. */
+ if (inode->i_nlink)
+ ext3_orphan_del(NULL, inode);
+
+err_out:
+ ext3_std_error(inode->i_sb, error);
+ if (!error)
+ error = rc;
+ return error;
+}
+
+
+/*
+ * akpm: how many blocks doth make a writepage()?
+ *
+ * With N blocks per page, it may be:
+ * N data blocks
+ * 2 indirect block
+ * 2 dindirect
+ * 1 tindirect
+ * N+5 bitmap blocks (from the above)
+ * N+5 group descriptor summary blocks
+ * 1 inode block
+ * 1 superblock.
+ * 2 * EXT3_SINGLEDATA_TRANS_BLOCKS for the quote files
+ *
+ * 3 * (N + 5) + 2 + 2 * EXT3_SINGLEDATA_TRANS_BLOCKS
+ *
+ * With ordered or writeback data it's the same, less the N data blocks.
+ *
+ * If the inode's direct blocks can hold an integral number of pages then a
+ * page cannot straddle two indirect blocks, and we can only touch one indirect
+ * and dindirect block, and the "5" above becomes "3".
+ *
+ * This still overestimates under most circumstances. If we were to pass the
+ * start and end offsets in here as well we could do block_to_path() on each
+ * block and work out the exact number of indirects which are touched. Pah.
+ */
+
+int ext3_writepage_trans_blocks(struct inode *inode)
+{
+ int bpp = ext3_journal_blocks_per_page(inode);
+ int indirects = (EXT3_NDIR_BLOCKS % bpp) ? 5 : 3;
+ int ret;
+
+ if (ext3_should_journal_data(inode))
+ ret = 3 * (bpp + indirects) + 2;
+ else
+ ret = 2 * (bpp + indirects) + 2;
+
+#ifdef CONFIG_QUOTA
+ ret += 2 * EXT3_SINGLEDATA_TRANS_BLOCKS;
+#endif
+
+ return ret;
+}
+
+int
+ext3_mark_iloc_dirty(handle_t *handle,
+ struct inode *inode,
+ struct ext3_iloc *iloc)
+{
+ int err = 0;
+
+ if (handle) {
+ /* the do_update_inode consumes one bh->b_count */
+ atomic_inc(&iloc->bh->b_count);
+ err = ext3_do_update_inode(handle, inode, iloc);
+ /* ext3_do_update_inode() does journal_dirty_metadata */
+ brelse(iloc->bh);
+ } else {
+ printk(KERN_EMERG "%s: called with no handle!\n", __FUNCTION__);
+ }
+ return err;
+}
+
+/*
+ * On success, We end up with an outstanding reference count against
+ * iloc->bh. This _must_ be cleaned up later.
+ */
+
+int
+ext3_reserve_inode_write(handle_t *handle, struct inode *inode,
+ struct ext3_iloc *iloc)
+{
+ int err = 0;
+ if (handle) {
+ err = ext3_get_inode_loc(inode, iloc);
+ if (!err) {
+ BUFFER_TRACE(iloc->bh, "get_write_access");
+ err = ext3_journal_get_write_access(handle, iloc->bh);
+ if (err) {
+ brelse(iloc->bh);
+ iloc->bh = NULL;
+ }
+ }
+ }
+ ext3_std_error(inode->i_sb, err);
+ return err;
+}
+
+/*
+ * akpm: What we do here is to mark the in-core inode as clean
+ * with respect to inode dirtiness (it may still be data-dirty).
+ * This means that the in-core inode may be reaped by prune_icache
+ * without having to perform any I/O. This is a very good thing,
+ * because *any* task may call prune_icache - even ones which
+ * have a transaction open against a different journal.
+ *
+ * Is this cheating? Not really. Sure, we haven't written the
+ * inode out, but prune_icache isn't a user-visible syncing function.
+ * Whenever the user wants stuff synced (sys_sync, sys_msync, sys_fsync)
+ * we start and wait on commits.
+ *
+ * Is this efficient/effective? Well, we're being nice to the system
+ * by cleaning up our inodes proactively so they can be reaped
+ * without I/O. But we are potentially leaving up to five seconds'
+ * worth of inodes floating about which prune_icache wants us to
+ * write out. One way to fix that would be to get prune_icache()
+ * to do a write_super() to free up some memory. It has the desired
+ * effect.
+ */
+int ext3_mark_inode_dirty(handle_t *handle, struct inode *inode)
+{
+ struct ext3_iloc iloc;
+ int err;
+
+ err = ext3_reserve_inode_write(handle, inode, &iloc);
+ if (!err)
+ err = ext3_mark_iloc_dirty(handle, inode, &iloc);
+ return err;
+}
+
+/*
+ * akpm: ext3_dirty_inode() is called from __mark_inode_dirty()
+ *
+ * We're really interested in the case where a file is being extended.
+ * i_size has been changed by generic_commit_write() and we thus need
+ * to include the updated inode in the current transaction.
+ *
+ * Also, DQUOT_ALLOC_SPACE() will always dirty the inode when blocks
+ * are allocated to the file.
+ *
+ * If the inode is marked synchronous, we don't honour that here - doing
+ * so would cause a commit on atime updates, which we don't bother doing.
+ * We handle synchronous inodes at the highest possible level.
+ */
+void ext3_dirty_inode(struct inode *inode)
+{
+ handle_t *current_handle = ext3_journal_current_handle();
+ handle_t *handle;
+
+ lock_kernel();
+ handle = ext3_journal_start(inode, 2);
+ if (IS_ERR(handle))
+ goto out;
+ if (current_handle &&
+ current_handle->h_transaction != handle->h_transaction) {
+ /* This task has a transaction open against a different fs */
+ printk(KERN_EMERG "%s: transactions do not match!\n",
+ __FUNCTION__);
+ } else {
+ jbd_debug(5, "marking dirty. outer handle=%p\n",
+ current_handle);
+ ext3_mark_inode_dirty(handle, inode);
+ }
+ ext3_journal_stop(handle, inode);
+out:
+ unlock_kernel();
+}
+
+#ifdef AKPM
+/*
+ * Bind an inode's backing buffer_head into this transaction, to prevent
+ * it from being flushed to disk early. Unlike
+ * ext3_reserve_inode_write, this leaves behind no bh reference and
+ * returns no iloc structure, so the caller needs to repeat the iloc
+ * lookup to mark the inode dirty later.
+ */
+static inline int
+ext3_pin_inode(handle_t *handle, struct inode *inode)
+{
+ struct ext3_iloc iloc;
+
+ int err = 0;
+ if (handle) {
+ err = ext3_get_inode_loc(inode, &iloc);
+ if (!err) {
+ BUFFER_TRACE(iloc.bh, "get_write_access");
+ err = journal_get_write_access(handle, iloc.bh);
+ if (!err)
+ err = ext3_journal_dirty_metadata(handle,
+ iloc.bh);
+ brelse(iloc.bh);
+ }
+ }
+ ext3_std_error(inode->i_sb, err);
+ return err;
+}
+#endif
+
+int ext3_change_inode_journal_flag(struct inode *inode, int val)
+{
+ journal_t *journal;
+ handle_t *handle;
+ int err;
+
+ /*
+ * We have to be very careful here: changing a data block's
+ * journaling status dynamically is dangerous. If we write a
+ * data block to the journal, change the status and then delete
+ * that block, we risk forgetting to revoke the old log record
+ * from the journal and so a subsequent replay can corrupt data.
+ * So, first we make sure that the journal is empty and that
+ * nobody is changing anything.
+ */
+
+ journal = EXT3_JOURNAL(inode);
+ if (is_journal_aborted(journal) || IS_RDONLY(inode))
+ return -EROFS;
+
+ journal_lock_updates(journal);
+ journal_flush(journal);
+
+ /*
+ * OK, there are no updates running now, and all cached data is
+ * synced to disk. We are now in a completely consistent state
+ * which doesn't have anything in the journal, and we know that
+ * no filesystem updates are running, so it is safe to modify
+ * the inode's in-core data-journaling state flag now.
+ */
+
+ if (val)
+ inode->u.ext3_i.i_flags |= EXT3_JOURNAL_DATA_FL;
+ else
+ inode->u.ext3_i.i_flags &= ~EXT3_JOURNAL_DATA_FL;
+
+ journal_unlock_updates(journal);
+
+ /* Finally we can mark the inode as dirty. */
+
+ handle = ext3_journal_start(inode, 1);
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+
+ err = ext3_mark_inode_dirty(handle, inode);
+ handle->h_sync = 1;
+ ext3_journal_stop(handle, inode);
+ ext3_std_error(inode->i_sb, err);
+
+ return err;
+}
+
+
+/*
+ * ext3_aops_journal_start().
+ *
+ * <This function died, but the comment lives on>
+ *
+ * We need to take the inode semaphore *outside* the
+ * journal_start/journal_stop. Otherwise, a different task could do a
+ * wait_for_commit() while holding ->i_sem, which deadlocks. The rule
+ * is: transaction open/closes are considered to be a locking operation
+ * and they nest *inside* ->i_sem.
+ * ----------------------------------------------------------------------------
+ * Possible problem:
+ * ext3_file_write()
+ * -> generic_file_write()
+ * -> __alloc_pages()
+ * -> page_launder()
+ * -> ext3_writepage()
+ *
+ * And the writepage can be on a different fs while we have a
+ * transaction open against this one! Bad.
+ *
+ * I tried making the task PF_MEMALLOC here, but that simply results in
+ * 0-order allocation failures passed back to generic_file_write().
+ * Instead, we rely on the reentrancy protection in ext3_writepage().
+ * ----------------------------------------------------------------------------
+ * When we do the journal_start() here we don't really need to reserve
+ * any blocks - we won't need any until we hit ext3_prepare_write(),
+ * which does all the needed journal extending. However! There is a
+ * problem with quotas:
+ *
+ * Thread 1:
+ * sys_sync
+ * ->sync_dquots
+ * ->commit_dquot
+ * ->lock_dquot
+ * ->write_dquot
+ * ->ext3_file_write
+ * ->journal_start
+ * ->ext3_prepare_write
+ * ->journal_extend
+ * ->journal_start
+ * Thread 2:
+ * ext3_create (for example)
+ * ->ext3_new_inode
+ * ->dquot_initialize
+ * ->lock_dquot
+ *
+ * Deadlock. Thread 1's journal_start blocks because thread 2 has a
+ * transaction open. Thread 2's transaction will never close because
+ * thread 2 is stuck waiting for the dquot lock.
+ *
+ * So. We must ensure that thread 1 *never* needs to extend the journal
+ * for quota writes. We do that by reserving enough journal blocks
+ * here, in ext3_aops_journal_start() to ensure that the forthcoming "see if we
+ * need to extend" test in ext3_prepare_write() succeeds.
+ */
diff --git a/fs/ext3/ioctl.c b/fs/ext3/ioctl.c
new file mode 100644
index 00000000000000..601525ac80aa0a
--- /dev/null
+++ b/fs/ext3/ioctl.c
@@ -0,0 +1,155 @@
+/*
+ * linux/fs/ext3/ioctl.c
+ *
+ * Copyright (C) 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ */
+
+#include <linux/fs.h>
+#include <linux/jbd.h>
+#include <linux/ext3_fs.h>
+#include <linux/ext3_jbd.h>
+#include <linux/sched.h>
+#include <asm/uaccess.h>
+
+
+int ext3_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
+ unsigned long arg)
+{
+ unsigned int flags;
+
+ ext3_debug ("cmd = %u, arg = %lu\n", cmd, arg);
+
+ switch (cmd) {
+ case EXT3_IOC_GETFLAGS:
+ flags = inode->u.ext3_i.i_flags & EXT3_FL_USER_VISIBLE;
+ return put_user(flags, (int *) arg);
+ case EXT3_IOC_SETFLAGS: {
+ handle_t *handle = NULL;
+ int err;
+ struct ext3_iloc iloc;
+ unsigned int oldflags;
+ unsigned int jflag;
+
+ if (IS_RDONLY(inode))
+ return -EROFS;
+
+ if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
+ return -EACCES;
+
+ if (get_user(flags, (int *) arg))
+ return -EFAULT;
+
+ oldflags = inode->u.ext3_i.i_flags;
+
+ /* The JOURNAL_DATA flag is modifiable only by root */
+ jflag = flags & EXT3_JOURNAL_DATA_FL;
+
+ /*
+ * The IMMUTABLE and APPEND_ONLY flags can only be changed by
+ * the relevant capability.
+ *
+ * This test looks nicer. Thanks to Pauline Middelink
+ */
+ if ((flags ^ oldflags) & (EXT3_APPEND_FL | EXT3_IMMUTABLE_FL)) {
+ if (!capable(CAP_LINUX_IMMUTABLE))
+ return -EPERM;
+ }
+
+ /*
+ * The JOURNAL_DATA flag can only be changed by
+ * the relevant capability.
+ */
+ if ((jflag ^ oldflags) & (EXT3_JOURNAL_DATA_FL)) {
+ if (!capable(CAP_SYS_RESOURCE))
+ return -EPERM;
+ }
+
+
+ handle = ext3_journal_start(inode, 1);
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ if (IS_SYNC(inode))
+ handle->h_sync = 1;
+ err = ext3_reserve_inode_write(handle, inode, &iloc);
+ if (err)
+ goto flags_err;
+
+ flags = flags & EXT3_FL_USER_MODIFIABLE;
+ flags |= oldflags & ~EXT3_FL_USER_MODIFIABLE;
+ inode->u.ext3_i.i_flags = flags;
+
+ ext3_set_inode_flags(inode);
+ inode->i_ctime = CURRENT_TIME;
+
+ err = ext3_mark_iloc_dirty(handle, inode, &iloc);
+flags_err:
+ ext3_journal_stop(handle, inode);
+ if (err)
+ return err;
+
+ if ((jflag ^ oldflags) & (EXT3_JOURNAL_DATA_FL))
+ err = ext3_change_inode_journal_flag(inode, jflag);
+ return err;
+ }
+ case EXT3_IOC_GETVERSION:
+ case EXT3_IOC_GETVERSION_OLD:
+ return put_user(inode->i_generation, (int *) arg);
+ case EXT3_IOC_SETVERSION:
+ case EXT3_IOC_SETVERSION_OLD: {
+ handle_t *handle;
+ struct ext3_iloc iloc;
+ __u32 generation;
+ int err;
+
+ if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
+ return -EPERM;
+ if (IS_RDONLY(inode))
+ return -EROFS;
+ if (get_user(generation, (int *) arg))
+ return -EFAULT;
+
+ handle = ext3_journal_start(inode, 1);
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+ err = ext3_reserve_inode_write(handle, inode, &iloc);
+ if (err)
+ return err;
+
+ inode->i_ctime = CURRENT_TIME;
+ inode->i_generation = generation;
+
+ err = ext3_mark_iloc_dirty(handle, inode, &iloc);
+ ext3_journal_stop(handle, inode);
+ return err;
+ }
+#ifdef CONFIG_JBD_DEBUG
+ case EXT3_IOC_WAIT_FOR_READONLY:
+ /*
+ * This is racy - by the time we're woken up and running,
+ * the superblock could be released. And the module could
+ * have been unloaded. So sue me.
+ *
+ * Returns 1 if it slept, else zero.
+ */
+ {
+ struct super_block *sb = inode->i_sb;
+ DECLARE_WAITQUEUE(wait, current);
+ int ret = 0;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ add_wait_queue(&sb->u.ext3_sb.ro_wait_queue, &wait);
+ if (timer_pending(&sb->u.ext3_sb.turn_ro_timer)) {
+ schedule();
+ ret = 1;
+ }
+ remove_wait_queue(&sb->u.ext3_sb.ro_wait_queue, &wait);
+ return ret;
+ }
+#endif
+ default:
+ return -ENOTTY;
+ }
+}
diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c
new file mode 100644
index 00000000000000..20950ced5e0d1a
--- /dev/null
+++ b/fs/ext3/namei.c
@@ -0,0 +1,1124 @@
+/*
+ * linux/fs/ext3/namei.c
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * from
+ *
+ * linux/fs/minix/namei.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * Big-endian to little-endian byte-swapping/bitmaps by
+ * David S. Miller (davem@caip.rutgers.edu), 1995
+ * Directory entry file type support and forward compatibility hooks
+ * for B-tree directories by Theodore Ts'o (tytso@mit.edu), 1998
+ */
+
+#include <linux/fs.h>
+#include <linux/jbd.h>
+#include <linux/sched.h>
+#include <linux/ext3_fs.h>
+#include <linux/ext3_jbd.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/quotaops.h>
+
+
+/*
+ * define how far ahead to read directories while searching them.
+ */
+#define NAMEI_RA_CHUNKS 2
+#define NAMEI_RA_BLOCKS 4
+#define NAMEI_RA_SIZE (NAMEI_RA_CHUNKS * NAMEI_RA_BLOCKS)
+#define NAMEI_RA_INDEX(c,b) (((c) * NAMEI_RA_BLOCKS) + (b))
+
+/*
+ * NOTE! unlike strncmp, ext3_match returns 1 for success, 0 for failure.
+ *
+ * `len <= EXT3_NAME_LEN' is guaranteed by caller.
+ * `de != NULL' is guaranteed by caller.
+ */
+static inline int ext3_match (int len, const char * const name,
+ struct ext3_dir_entry_2 * de)
+{
+ if (len != de->name_len)
+ return 0;
+ if (!de->inode)
+ return 0;
+ return !memcmp(name, de->name, len);
+}
+
+/*
+ * Returns 0 if not found, -1 on failure, and 1 on success
+ */
+static int inline search_dirblock(struct buffer_head * bh,
+ struct inode *dir,
+ struct dentry *dentry,
+ unsigned long offset,
+ struct ext3_dir_entry_2 ** res_dir)
+{
+ struct ext3_dir_entry_2 * de;
+ char * dlimit;
+ int de_len;
+ const char *name = dentry->d_name.name;
+ int namelen = dentry->d_name.len;
+
+ de = (struct ext3_dir_entry_2 *) bh->b_data;
+ dlimit = bh->b_data + dir->i_sb->s_blocksize;
+ while ((char *) de < dlimit) {
+ /* this code is executed quadratically often */
+ /* do minimal checking `by hand' */
+
+ if ((char *) de + namelen <= dlimit &&
+ ext3_match (namelen, name, de)) {
+ /* found a match - just to be sure, do a full check */
+ if (!ext3_check_dir_entry("ext3_find_entry",
+ dir, de, bh, offset))
+ return -1;
+ *res_dir = de;
+ return 1;
+ }
+ /* prevent looping on a bad block */
+ de_len = le16_to_cpu(de->rec_len);
+ if (de_len <= 0)
+ return -1;
+ offset += de_len;
+ de = (struct ext3_dir_entry_2 *) ((char *) de + de_len);
+ }
+ return 0;
+}
+
+/*
+ * ext3_find_entry()
+ *
+ * finds an entry in the specified directory with the wanted name. It
+ * returns the cache buffer in which the entry was found, and the entry
+ * itself (as a parameter - res_dir). It does NOT read the inode of the
+ * entry - you'll have to do that yourself if you want to.
+ *
+ * The returned buffer_head has ->b_count elevated. The caller is expected
+ * to brelse() it when appropriate.
+ */
+static struct buffer_head * ext3_find_entry (struct dentry *dentry,
+ struct ext3_dir_entry_2 ** res_dir)
+{
+ struct super_block * sb;
+ struct buffer_head * bh_use[NAMEI_RA_SIZE];
+ struct buffer_head * bh, *ret = NULL;
+ unsigned long start, block, b;
+ int ra_max = 0; /* Number of bh's in the readahead
+ buffer, bh_use[] */
+ int ra_ptr = 0; /* Current index into readahead
+ buffer */
+ int num = 0;
+ int nblocks, i, err;
+ struct inode *dir = dentry->d_parent->d_inode;
+
+ *res_dir = NULL;
+ sb = dir->i_sb;
+
+ nblocks = dir->i_size >> EXT3_BLOCK_SIZE_BITS(sb);
+ start = dir->u.ext3_i.i_dir_start_lookup;
+ if (start >= nblocks)
+ start = 0;
+ block = start;
+restart:
+ do {
+ /*
+ * We deal with the read-ahead logic here.
+ */
+ if (ra_ptr >= ra_max) {
+ /* Refill the readahead buffer */
+ ra_ptr = 0;
+ b = block;
+ for (ra_max = 0; ra_max < NAMEI_RA_SIZE; ra_max++) {
+ /*
+ * Terminate if we reach the end of the
+ * directory and must wrap, or if our
+ * search has finished at this block.
+ */
+ if (b >= nblocks || (num && block == start)) {
+ bh_use[ra_max] = NULL;
+ break;
+ }
+ num++;
+ bh = ext3_getblk(NULL, dir, b++, 0, &err);
+ bh_use[ra_max] = bh;
+ if (bh)
+ ll_rw_block(READ, 1, &bh);
+ }
+ }
+ if ((bh = bh_use[ra_ptr++]) == NULL)
+ goto next;
+ wait_on_buffer(bh);
+ if (!buffer_uptodate(bh)) {
+ /* read error, skip block & hope for the best */
+ brelse(bh);
+ goto next;
+ }
+ i = search_dirblock(bh, dir, dentry,
+ block << EXT3_BLOCK_SIZE_BITS(sb), res_dir);
+ if (i == 1) {
+ dir->u.ext3_i.i_dir_start_lookup = block;
+ ret = bh;
+ goto cleanup_and_exit;
+ } else {
+ brelse(bh);
+ if (i < 0)
+ goto cleanup_and_exit;
+ }
+ next:
+ if (++block >= nblocks)
+ block = 0;
+ } while (block != start);
+
+ /*
+ * If the directory has grown while we were searching, then
+ * search the last part of the directory before giving up.
+ */
+ block = nblocks;
+ nblocks = dir->i_size >> EXT3_BLOCK_SIZE_BITS(sb);
+ if (block < nblocks) {
+ start = 0;
+ goto restart;
+ }
+
+cleanup_and_exit:
+ /* Clean up the read-ahead blocks */
+ for (; ra_ptr < ra_max; ra_ptr++)
+ brelse (bh_use[ra_ptr]);
+ return ret;
+}
+
+static struct dentry *ext3_lookup(struct inode * dir, struct dentry *dentry)
+{
+ struct inode * inode;
+ struct ext3_dir_entry_2 * de;
+ struct buffer_head * bh;
+
+ if (dentry->d_name.len > EXT3_NAME_LEN)
+ return ERR_PTR(-ENAMETOOLONG);
+
+ bh = ext3_find_entry(dentry, &de);
+ inode = NULL;
+ if (bh) {
+ unsigned long ino = le32_to_cpu(de->inode);
+ brelse (bh);
+ inode = iget(dir->i_sb, ino);
+
+ if (!inode)
+ return ERR_PTR(-EACCES);
+ }
+ d_add(dentry, inode);
+ return NULL;
+}
+
+#define S_SHIFT 12
+static unsigned char ext3_type_by_mode[S_IFMT >> S_SHIFT] = {
+ [S_IFREG >> S_SHIFT] EXT3_FT_REG_FILE,
+ [S_IFDIR >> S_SHIFT] EXT3_FT_DIR,
+ [S_IFCHR >> S_SHIFT] EXT3_FT_CHRDEV,
+ [S_IFBLK >> S_SHIFT] EXT3_FT_BLKDEV,
+ [S_IFIFO >> S_SHIFT] EXT3_FT_FIFO,
+ [S_IFSOCK >> S_SHIFT] EXT3_FT_SOCK,
+ [S_IFLNK >> S_SHIFT] EXT3_FT_SYMLINK,
+};
+
+static inline void ext3_set_de_type(struct super_block *sb,
+ struct ext3_dir_entry_2 *de,
+ umode_t mode) {
+ if (EXT3_HAS_INCOMPAT_FEATURE(sb, EXT3_FEATURE_INCOMPAT_FILETYPE))
+ de->file_type = ext3_type_by_mode[(mode & S_IFMT)>>S_SHIFT];
+}
+
+/*
+ * ext3_add_entry()
+ *
+ * adds a file entry to the specified directory, using the same
+ * semantics as ext3_find_entry(). It returns NULL if it failed.
+ *
+ * NOTE!! The inode part of 'de' is left at 0 - which means you
+ * may not sleep between calling this and putting something into
+ * the entry, as someone else might have used it while you slept.
+ */
+
+/*
+ * AKPM: the journalling code here looks wrong on the error paths
+ */
+static int ext3_add_entry (handle_t *handle, struct dentry *dentry,
+ struct inode *inode)
+{
+ struct inode *dir = dentry->d_parent->d_inode;
+ const char *name = dentry->d_name.name;
+ int namelen = dentry->d_name.len;
+ unsigned long offset;
+ unsigned short rec_len;
+ struct buffer_head * bh;
+ struct ext3_dir_entry_2 * de, * de1;
+ struct super_block * sb;
+ int retval;
+
+ sb = dir->i_sb;
+
+ if (!namelen)
+ return -EINVAL;
+ bh = ext3_bread (handle, dir, 0, 0, &retval);
+ if (!bh)
+ return retval;
+ rec_len = EXT3_DIR_REC_LEN(namelen);
+ offset = 0;
+ de = (struct ext3_dir_entry_2 *) bh->b_data;
+ while (1) {
+ if ((char *)de >= sb->s_blocksize + bh->b_data) {
+ brelse (bh);
+ bh = NULL;
+ bh = ext3_bread (handle, dir,
+ offset >> EXT3_BLOCK_SIZE_BITS(sb), 1, &retval);
+ if (!bh)
+ return retval;
+ if (dir->i_size <= offset) {
+ if (dir->i_size == 0) {
+ brelse(bh);
+ return -ENOENT;
+ }
+
+ ext3_debug ("creating next block\n");
+
+ BUFFER_TRACE(bh, "get_write_access");
+ ext3_journal_get_write_access(handle, bh);
+ de = (struct ext3_dir_entry_2 *) bh->b_data;
+ de->inode = 0;
+ de->rec_len = le16_to_cpu(sb->s_blocksize);
+ dir->u.ext3_i.i_disksize =
+ dir->i_size = offset + sb->s_blocksize;
+ dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL;
+ ext3_mark_inode_dirty(handle, dir);
+ } else {
+
+ ext3_debug ("skipping to next block\n");
+
+ de = (struct ext3_dir_entry_2 *) bh->b_data;
+ }
+ }
+ if (!ext3_check_dir_entry ("ext3_add_entry", dir, de, bh,
+ offset)) {
+ brelse (bh);
+ return -ENOENT;
+ }
+ if (ext3_match (namelen, name, de)) {
+ brelse (bh);
+ return -EEXIST;
+ }
+ if ((le32_to_cpu(de->inode) == 0 &&
+ le16_to_cpu(de->rec_len) >= rec_len) ||
+ (le16_to_cpu(de->rec_len) >=
+ EXT3_DIR_REC_LEN(de->name_len) + rec_len)) {
+ BUFFER_TRACE(bh, "get_write_access");
+ ext3_journal_get_write_access(handle, bh);
+ /* By now the buffer is marked for journaling */
+ offset += le16_to_cpu(de->rec_len);
+ if (le32_to_cpu(de->inode)) {
+ de1 = (struct ext3_dir_entry_2 *) ((char *) de +
+ EXT3_DIR_REC_LEN(de->name_len));
+ de1->rec_len =
+ cpu_to_le16(le16_to_cpu(de->rec_len) -
+ EXT3_DIR_REC_LEN(de->name_len));
+ de->rec_len = cpu_to_le16(
+ EXT3_DIR_REC_LEN(de->name_len));
+ de = de1;
+ }
+ de->file_type = EXT3_FT_UNKNOWN;
+ if (inode) {
+ de->inode = cpu_to_le32(inode->i_ino);
+ ext3_set_de_type(dir->i_sb, de, inode->i_mode);
+ } else
+ de->inode = 0;
+ de->name_len = namelen;
+ memcpy (de->name, name, namelen);
+ /*
+ * XXX shouldn't update any times until successful
+ * completion of syscall, but too many callers depend
+ * on this.
+ *
+ * XXX similarly, too many callers depend on
+ * ext3_new_inode() setting the times, but error
+ * recovery deletes the inode, so the worst that can
+ * happen is that the times are slightly out of date
+ * and/or different from the directory change time.
+ */
+ dir->i_mtime = dir->i_ctime = CURRENT_TIME;
+ dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL;
+ dir->i_version = ++event;
+ ext3_mark_inode_dirty(handle, dir);
+ BUFFER_TRACE(bh, "call ext3_journal_dirty_metadata");
+ ext3_journal_dirty_metadata(handle, bh);
+ brelse(bh);
+ return 0;
+ }
+ offset += le16_to_cpu(de->rec_len);
+ de = (struct ext3_dir_entry_2 *)
+ ((char *) de + le16_to_cpu(de->rec_len));
+ }
+ brelse (bh);
+ return -ENOSPC;
+}
+
+/*
+ * ext3_delete_entry deletes a directory entry by merging it with the
+ * previous entry
+ */
+static int ext3_delete_entry (handle_t *handle,
+ struct inode * dir,
+ struct ext3_dir_entry_2 * de_del,
+ struct buffer_head * bh)
+{
+ struct ext3_dir_entry_2 * de, * pde;
+ int i;
+
+ i = 0;
+ pde = NULL;
+ de = (struct ext3_dir_entry_2 *) bh->b_data;
+ while (i < bh->b_size) {
+ if (!ext3_check_dir_entry("ext3_delete_entry", dir, de, bh, i))
+ return -EIO;
+ if (de == de_del) {
+ BUFFER_TRACE(bh, "get_write_access");
+ ext3_journal_get_write_access(handle, bh);
+ if (pde)
+ pde->rec_len =
+ cpu_to_le16(le16_to_cpu(pde->rec_len) +
+ le16_to_cpu(de->rec_len));
+ else
+ de->inode = 0;
+ dir->i_version = ++event;
+ BUFFER_TRACE(bh, "call ext3_journal_dirty_metadata");
+ ext3_journal_dirty_metadata(handle, bh);
+ return 0;
+ }
+ i += le16_to_cpu(de->rec_len);
+ pde = de;
+ de = (struct ext3_dir_entry_2 *)
+ ((char *) de + le16_to_cpu(de->rec_len));
+ }
+ return -ENOENT;
+}
+
+/*
+ * ext3_mark_inode_dirty is somewhat expensive, so unlike ext2 we
+ * do not perform it in these functions. We perform it at the call site,
+ * if it is needed.
+ */
+static inline void ext3_inc_count(handle_t *handle, struct inode *inode)
+{
+ inode->i_nlink++;
+}
+
+static inline void ext3_dec_count(handle_t *handle, struct inode *inode)
+{
+ inode->i_nlink--;
+}
+
+static int ext3_add_nondir(handle_t *handle,
+ struct dentry *dentry, struct inode *inode)
+{
+ int err = ext3_add_entry(handle, dentry, inode);
+ if (!err) {
+ err = ext3_mark_inode_dirty(handle, inode);
+ if (err == 0) {
+ d_instantiate(dentry, inode);
+ return 0;
+ }
+ }
+ ext3_dec_count(handle, inode);
+ iput(inode);
+ return err;
+}
+
+/*
+ * By the time this is called, we already have created
+ * the directory cache entry for the new file, but it
+ * is so far negative - it has no inode.
+ *
+ * If the create succeeds, we fill in the inode information
+ * with d_instantiate().
+ */
+static int ext3_create (struct inode * dir, struct dentry * dentry, int mode)
+{
+ handle_t *handle;
+ struct inode * inode;
+ int err;
+
+ handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + 3);
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+
+ if (IS_SYNC(dir))
+ handle->h_sync = 1;
+
+ inode = ext3_new_inode (handle, dir, mode);
+ err = PTR_ERR(inode);
+ if (!IS_ERR(inode)) {
+ inode->i_op = &ext3_file_inode_operations;
+ inode->i_fop = &ext3_file_operations;
+ inode->i_mapping->a_ops = &ext3_aops;
+ err = ext3_add_nondir(handle, dentry, inode);
+ }
+ ext3_journal_stop(handle, dir);
+ return err;
+}
+
+static int ext3_mknod (struct inode * dir, struct dentry *dentry,
+ int mode, int rdev)
+{
+ handle_t *handle;
+ struct inode *inode;
+ int err;
+
+ handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + 3);
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+
+ if (IS_SYNC(dir))
+ handle->h_sync = 1;
+
+ inode = ext3_new_inode (handle, dir, mode);
+ err = PTR_ERR(inode);
+ if (!IS_ERR(inode)) {
+ init_special_inode(inode, mode, rdev);
+ err = ext3_add_nondir(handle, dentry, inode);
+ }
+ ext3_journal_stop(handle, dir);
+ return err;
+}
+
+static int ext3_mkdir(struct inode * dir, struct dentry * dentry, int mode)
+{
+ handle_t *handle;
+ struct inode * inode;
+ struct buffer_head * dir_block;
+ struct ext3_dir_entry_2 * de;
+ int err;
+
+ if (dir->i_nlink >= EXT3_LINK_MAX)
+ return -EMLINK;
+
+ handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + 3);
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+
+ if (IS_SYNC(dir))
+ handle->h_sync = 1;
+
+ inode = ext3_new_inode (handle, dir, S_IFDIR);
+ err = PTR_ERR(inode);
+ if (IS_ERR(inode))
+ goto out_stop;
+
+ inode->i_op = &ext3_dir_inode_operations;
+ inode->i_fop = &ext3_dir_operations;
+ inode->i_size = inode->u.ext3_i.i_disksize = inode->i_sb->s_blocksize;
+ inode->i_blocks = 0;
+ dir_block = ext3_bread (handle, inode, 0, 1, &err);
+ if (!dir_block) {
+ inode->i_nlink--; /* is this nlink == 0? */
+ ext3_mark_inode_dirty(handle, inode);
+ iput (inode);
+ goto out_stop;
+ }
+ BUFFER_TRACE(dir_block, "get_write_access");
+ ext3_journal_get_write_access(handle, dir_block);
+ de = (struct ext3_dir_entry_2 *) dir_block->b_data;
+ de->inode = cpu_to_le32(inode->i_ino);
+ de->name_len = 1;
+ de->rec_len = cpu_to_le16(EXT3_DIR_REC_LEN(de->name_len));
+ strcpy (de->name, ".");
+ ext3_set_de_type(dir->i_sb, de, S_IFDIR);
+ de = (struct ext3_dir_entry_2 *)
+ ((char *) de + le16_to_cpu(de->rec_len));
+ de->inode = cpu_to_le32(dir->i_ino);
+ de->rec_len = cpu_to_le16(inode->i_sb->s_blocksize-EXT3_DIR_REC_LEN(1));
+ de->name_len = 2;
+ strcpy (de->name, "..");
+ ext3_set_de_type(dir->i_sb, de, S_IFDIR);
+ inode->i_nlink = 2;
+ BUFFER_TRACE(dir_block, "call ext3_journal_dirty_metadata");
+ ext3_journal_dirty_metadata(handle, dir_block);
+ brelse (dir_block);
+ inode->i_mode = S_IFDIR | mode;
+ if (dir->i_mode & S_ISGID)
+ inode->i_mode |= S_ISGID;
+ ext3_mark_inode_dirty(handle, inode);
+ err = ext3_add_entry (handle, dentry, inode);
+ if (err)
+ goto out_no_entry;
+ dir->i_nlink++;
+ dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL;
+ ext3_mark_inode_dirty(handle, dir);
+ d_instantiate(dentry, inode);
+out_stop:
+ ext3_journal_stop(handle, dir);
+ return err;
+
+out_no_entry:
+ inode->i_nlink = 0;
+ ext3_mark_inode_dirty(handle, inode);
+ iput (inode);
+ goto out_stop;
+}
+
+/*
+ * routine to check that the specified directory is empty (for rmdir)
+ */
+static int empty_dir (struct inode * inode)
+{
+ unsigned long offset;
+ struct buffer_head * bh;
+ struct ext3_dir_entry_2 * de, * de1;
+ struct super_block * sb;
+ int err;
+
+ sb = inode->i_sb;
+ if (inode->i_size < EXT3_DIR_REC_LEN(1) + EXT3_DIR_REC_LEN(2) ||
+ !(bh = ext3_bread (NULL, inode, 0, 0, &err))) {
+ ext3_warning (inode->i_sb, "empty_dir",
+ "bad directory (dir #%lu) - no data block",
+ inode->i_ino);
+ return 1;
+ }
+ de = (struct ext3_dir_entry_2 *) bh->b_data;
+ de1 = (struct ext3_dir_entry_2 *)
+ ((char *) de + le16_to_cpu(de->rec_len));
+ if (le32_to_cpu(de->inode) != inode->i_ino ||
+ !le32_to_cpu(de1->inode) ||
+ strcmp (".", de->name) ||
+ strcmp ("..", de1->name)) {
+ ext3_warning (inode->i_sb, "empty_dir",
+ "bad directory (dir #%lu) - no `.' or `..'",
+ inode->i_ino);
+ brelse (bh);
+ return 1;
+ }
+ offset = le16_to_cpu(de->rec_len) + le16_to_cpu(de1->rec_len);
+ de = (struct ext3_dir_entry_2 *)
+ ((char *) de1 + le16_to_cpu(de1->rec_len));
+ while (offset < inode->i_size ) {
+ if (!bh ||
+ (void *) de >= (void *) (bh->b_data+sb->s_blocksize)) {
+ brelse (bh);
+ bh = ext3_bread (NULL, inode,
+ offset >> EXT3_BLOCK_SIZE_BITS(sb), 0, &err);
+ if (!bh) {
+#if 0
+ ext3_error (sb, "empty_dir",
+ "directory #%lu contains a hole at offset %lu",
+ inode->i_ino, offset);
+#endif
+ offset += sb->s_blocksize;
+ continue;
+ }
+ de = (struct ext3_dir_entry_2 *) bh->b_data;
+ }
+ if (!ext3_check_dir_entry ("empty_dir", inode, de, bh,
+ offset)) {
+ brelse (bh);
+ return 1;
+ }
+ if (le32_to_cpu(de->inode)) {
+ brelse (bh);
+ return 0;
+ }
+ offset += le16_to_cpu(de->rec_len);
+ de = (struct ext3_dir_entry_2 *)
+ ((char *) de + le16_to_cpu(de->rec_len));
+ }
+ brelse (bh);
+ return 1;
+}
+
+/* ext3_orphan_add() links an unlinked or truncated inode into a list of
+ * such inodes, starting at the superblock, in case we crash before the
+ * file is closed/deleted, or in case the inode truncate spans multiple
+ * transactions and the last transaction is not recovered after a crash.
+ *
+ * At filesystem recovery time, we walk this list deleting unlinked
+ * inodes and truncating linked inodes in ext3_orphan_cleanup().
+ */
+int ext3_orphan_add(handle_t *handle, struct inode *inode)
+{
+ struct super_block *sb = inode->i_sb;
+ struct ext3_iloc iloc;
+ int err = 0, rc;
+
+ lock_super(sb);
+ if (!list_empty(&inode->u.ext3_i.i_orphan))
+ goto out_unlock;
+
+ /* Orphan handling is only valid for files with data blocks
+ * being truncated, or files being unlinked. */
+
+ /* @@@ FIXME: Observation from aviro:
+ * I think I can trigger J_ASSERT in ext3_orphan_add(). We block
+ * here (on lock_super()), so race with ext3_link() which might bump
+ * ->i_nlink. For, say it, character device. Not a regular file,
+ * not a directory, not a symlink and ->i_nlink > 0.
+ */
+ J_ASSERT ((S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+ S_ISLNK(inode->i_mode)) || inode->i_nlink == 0);
+
+ BUFFER_TRACE(sb->u.ext3_sb.s_sbh, "get_write_access");
+ err = ext3_journal_get_write_access(handle, sb->u.ext3_sb.s_sbh);
+ if (err)
+ goto out_unlock;
+
+ err = ext3_reserve_inode_write(handle, inode, &iloc);
+ if (err)
+ goto out_unlock;
+
+ /* Insert this inode at the head of the on-disk orphan list... */
+ NEXT_ORPHAN(inode) = le32_to_cpu(EXT3_SB(sb)->s_es->s_last_orphan);
+ EXT3_SB(sb)->s_es->s_last_orphan = cpu_to_le32(inode->i_ino);
+ err = ext3_journal_dirty_metadata(handle, sb->u.ext3_sb.s_sbh);
+ rc = ext3_mark_iloc_dirty(handle, inode, &iloc);
+ if (!err)
+ err = rc;
+
+ /* Only add to the head of the in-memory list if all the
+ * previous operations succeeded. If the orphan_add is going to
+ * fail (possibly taking the journal offline), we can't risk
+ * leaving the inode on the orphan list: stray orphan-list
+ * entries can cause panics at unmount time.
+ *
+ * This is safe: on error we're going to ignore the orphan list
+ * anyway on the next recovery. */
+ if (!err)
+ list_add(&inode->u.ext3_i.i_orphan, &EXT3_SB(sb)->s_orphan);
+
+ jbd_debug(4, "superblock will point to %ld\n", inode->i_ino);
+ jbd_debug(4, "orphan inode %ld will point to %d\n",
+ inode->i_ino, NEXT_ORPHAN(inode));
+out_unlock:
+ unlock_super(sb);
+ ext3_std_error(inode->i_sb, err);
+ return err;
+}
+
+/*
+ * ext3_orphan_del() removes an unlinked or truncated inode from the list
+ * of such inodes stored on disk, because it is finally being cleaned up.
+ */
+int ext3_orphan_del(handle_t *handle, struct inode *inode)
+{
+ struct list_head *prev;
+ struct ext3_sb_info *sbi;
+ unsigned long ino_next;
+ struct ext3_iloc iloc;
+ int err = 0;
+
+ lock_super(inode->i_sb);
+ if (list_empty(&inode->u.ext3_i.i_orphan)) {
+ unlock_super(inode->i_sb);
+ return 0;
+ }
+
+ ino_next = NEXT_ORPHAN(inode);
+ prev = inode->u.ext3_i.i_orphan.prev;
+ sbi = EXT3_SB(inode->i_sb);
+
+ jbd_debug(4, "remove inode %lu from orphan list\n", inode->i_ino);
+
+ list_del(&inode->u.ext3_i.i_orphan);
+ INIT_LIST_HEAD(&inode->u.ext3_i.i_orphan);
+
+ /* If we're on an error path, we may not have a valid
+ * transaction handle with which to update the orphan list on
+ * disk, but we still need to remove the inode from the linked
+ * list in memory. */
+ if (!handle)
+ goto out;
+
+ err = ext3_reserve_inode_write(handle, inode, &iloc);
+ if (err)
+ goto out_err;
+
+ if (prev == &sbi->s_orphan) {
+ jbd_debug(4, "superblock will point to %lu\n", ino_next);
+ BUFFER_TRACE(sbi->s_sbh, "get_write_access");
+ err = ext3_journal_get_write_access(handle, sbi->s_sbh);
+ if (err)
+ goto out_brelse;
+ sbi->s_es->s_last_orphan = cpu_to_le32(ino_next);
+ err = ext3_journal_dirty_metadata(handle, sbi->s_sbh);
+ } else {
+ struct ext3_iloc iloc2;
+ struct inode *i_prev =
+ list_entry(prev, struct inode, u.ext3_i.i_orphan);
+
+ jbd_debug(4, "orphan inode %lu will point to %lu\n",
+ i_prev->i_ino, ino_next);
+ err = ext3_reserve_inode_write(handle, i_prev, &iloc2);
+ if (err)
+ goto out_brelse;
+ NEXT_ORPHAN(i_prev) = ino_next;
+ err = ext3_mark_iloc_dirty(handle, i_prev, &iloc2);
+ }
+ if (err)
+ goto out_brelse;
+ NEXT_ORPHAN(inode) = 0;
+ err = ext3_mark_iloc_dirty(handle, inode, &iloc);
+ if (err)
+ goto out_brelse;
+
+out_err:
+ ext3_std_error(inode->i_sb, err);
+out:
+ unlock_super(inode->i_sb);
+ return err;
+
+out_brelse:
+ brelse(iloc.bh);
+ goto out_err;
+}
+
+static int ext3_rmdir (struct inode * dir, struct dentry *dentry)
+{
+ int retval;
+ struct inode * inode;
+ struct buffer_head * bh;
+ struct ext3_dir_entry_2 * de;
+ handle_t *handle;
+
+ handle = ext3_journal_start(dir, EXT3_DELETE_TRANS_BLOCKS);
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+
+ retval = -ENOENT;
+ bh = ext3_find_entry (dentry, &de);
+ if (!bh)
+ goto end_rmdir;
+
+ if (IS_SYNC(dir))
+ handle->h_sync = 1;
+
+ inode = dentry->d_inode;
+ DQUOT_INIT(inode);
+
+ retval = -EIO;
+ if (le32_to_cpu(de->inode) != inode->i_ino)
+ goto end_rmdir;
+
+ retval = -ENOTEMPTY;
+ if (!empty_dir (inode))
+ goto end_rmdir;
+
+ retval = ext3_delete_entry(handle, dir, de, bh);
+ if (retval)
+ goto end_rmdir;
+ if (inode->i_nlink != 2)
+ ext3_warning (inode->i_sb, "ext3_rmdir",
+ "empty directory has nlink!=2 (%d)",
+ inode->i_nlink);
+ inode->i_version = ++event;
+ inode->i_nlink = 0;
+ /* There's no need to set i_disksize: the fact that i_nlink is
+ * zero will ensure that the right thing happens during any
+ * recovery. */
+ inode->i_size = 0;
+ ext3_orphan_add(handle, inode);
+ dir->i_nlink--;
+ inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ ext3_mark_inode_dirty(handle, inode);
+ dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL;
+ ext3_mark_inode_dirty(handle, dir);
+
+end_rmdir:
+ ext3_journal_stop(handle, dir);
+ brelse (bh);
+ return retval;
+}
+
+static int ext3_unlink(struct inode * dir, struct dentry *dentry)
+{
+ int retval;
+ struct inode * inode;
+ struct buffer_head * bh;
+ struct ext3_dir_entry_2 * de;
+ handle_t *handle;
+
+ handle = ext3_journal_start(dir, EXT3_DELETE_TRANS_BLOCKS);
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+
+ if (IS_SYNC(dir))
+ handle->h_sync = 1;
+
+ retval = -ENOENT;
+ bh = ext3_find_entry (dentry, &de);
+ if (!bh)
+ goto end_unlink;
+
+ inode = dentry->d_inode;
+ DQUOT_INIT(inode);
+
+ retval = -EIO;
+ if (le32_to_cpu(de->inode) != inode->i_ino)
+ goto end_unlink;
+
+ if (!inode->i_nlink) {
+ ext3_warning (inode->i_sb, "ext3_unlink",
+ "Deleting nonexistent file (%lu), %d",
+ inode->i_ino, inode->i_nlink);
+ inode->i_nlink = 1;
+ }
+ retval = ext3_delete_entry(handle, dir, de, bh);
+ if (retval)
+ goto end_unlink;
+ dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL;
+ ext3_mark_inode_dirty(handle, dir);
+ inode->i_nlink--;
+ if (!inode->i_nlink)
+ ext3_orphan_add(handle, inode);
+ inode->i_ctime = dir->i_ctime;
+ ext3_mark_inode_dirty(handle, inode);
+ retval = 0;
+
+end_unlink:
+ ext3_journal_stop(handle, dir);
+ brelse (bh);
+ return retval;
+}
+
+static int ext3_symlink (struct inode * dir,
+ struct dentry *dentry, const char * symname)
+{
+ handle_t *handle;
+ struct inode * inode;
+ int l, err;
+
+ l = strlen(symname)+1;
+ if (l > dir->i_sb->s_blocksize)
+ return -ENAMETOOLONG;
+
+ handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + 5);
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+
+ if (IS_SYNC(dir))
+ handle->h_sync = 1;
+
+ inode = ext3_new_inode (handle, dir, S_IFLNK|S_IRWXUGO);
+ err = PTR_ERR(inode);
+ if (IS_ERR(inode))
+ goto out_stop;
+
+ if (l > sizeof (inode->u.ext3_i.i_data)) {
+ inode->i_op = &page_symlink_inode_operations;
+ inode->i_mapping->a_ops = &ext3_aops;
+ /*
+ * block_symlink() calls back into ext3_prepare/commit_write.
+ * We have a transaction open. All is sweetness. It also sets
+ * i_size in generic_commit_write().
+ */
+ err = block_symlink(inode, symname, l);
+ if (err)
+ goto out_no_entry;
+ } else {
+ inode->i_op = &ext3_fast_symlink_inode_operations;
+ memcpy((char*)&inode->u.ext3_i.i_data,symname,l);
+ inode->i_size = l-1;
+ }
+ inode->u.ext3_i.i_disksize = inode->i_size;
+ err = ext3_add_nondir(handle, dentry, inode);
+out_stop:
+ ext3_journal_stop(handle, dir);
+ return err;
+
+out_no_entry:
+ ext3_dec_count(handle, inode);
+ ext3_mark_inode_dirty(handle, inode);
+ iput (inode);
+ goto out_stop;
+}
+
+static int ext3_link (struct dentry * old_dentry,
+ struct inode * dir, struct dentry *dentry)
+{
+ handle_t *handle;
+ struct inode *inode = old_dentry->d_inode;
+ int err;
+
+ if (S_ISDIR(inode->i_mode))
+ return -EPERM;
+
+ if (inode->i_nlink >= EXT3_LINK_MAX)
+ return -EMLINK;
+
+ handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS);
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+
+ if (IS_SYNC(dir))
+ handle->h_sync = 1;
+
+ inode->i_ctime = CURRENT_TIME;
+ ext3_inc_count(handle, inode);
+ atomic_inc(&inode->i_count);
+
+ err = ext3_add_nondir(handle, dentry, inode);
+ ext3_journal_stop(handle, dir);
+ return err;
+}
+
+#define PARENT_INO(buffer) \
+ ((struct ext3_dir_entry_2 *) ((char *) buffer + \
+ le16_to_cpu(((struct ext3_dir_entry_2 *) buffer)->rec_len)))->inode
+
+/*
+ * Anybody can rename anything with this: the permission checks are left to the
+ * higher-level routines.
+ */
+static int ext3_rename (struct inode * old_dir, struct dentry *old_dentry,
+ struct inode * new_dir,struct dentry *new_dentry)
+{
+ handle_t *handle;
+ struct inode * old_inode, * new_inode;
+ struct buffer_head * old_bh, * new_bh, * dir_bh;
+ struct ext3_dir_entry_2 * old_de, * new_de;
+ int retval;
+
+ old_bh = new_bh = dir_bh = NULL;
+
+ handle = ext3_journal_start(old_dir, 2 * EXT3_DATA_TRANS_BLOCKS + 2);
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+
+ if (IS_SYNC(old_dir) || IS_SYNC(new_dir))
+ handle->h_sync = 1;
+
+ old_bh = ext3_find_entry (old_dentry, &old_de);
+ /*
+ * Check for inode number is _not_ due to possible IO errors.
+ * We might rmdir the source, keep it as pwd of some process
+ * and merrily kill the link to whatever was created under the
+ * same name. Goodbye sticky bit ;-<
+ */
+ old_inode = old_dentry->d_inode;
+ retval = -ENOENT;
+ if (!old_bh || le32_to_cpu(old_de->inode) != old_inode->i_ino)
+ goto end_rename;
+
+ new_inode = new_dentry->d_inode;
+ new_bh = ext3_find_entry (new_dentry, &new_de);
+ if (new_bh) {
+ if (!new_inode) {
+ brelse (new_bh);
+ new_bh = NULL;
+ } else {
+ DQUOT_INIT(new_inode);
+ }
+ }
+ if (S_ISDIR(old_inode->i_mode)) {
+ if (new_inode) {
+ retval = -ENOTEMPTY;
+ if (!empty_dir (new_inode))
+ goto end_rename;
+ }
+ retval = -EIO;
+ dir_bh = ext3_bread (handle, old_inode, 0, 0, &retval);
+ if (!dir_bh)
+ goto end_rename;
+ if (le32_to_cpu(PARENT_INO(dir_bh->b_data)) != old_dir->i_ino)
+ goto end_rename;
+ retval = -EMLINK;
+ if (!new_inode && new_dir!=old_dir &&
+ new_dir->i_nlink >= EXT3_LINK_MAX)
+ goto end_rename;
+ }
+ if (!new_bh) {
+ retval = ext3_add_entry (handle, new_dentry, old_inode);
+ if (retval)
+ goto end_rename;
+ } else {
+ BUFFER_TRACE(new_bh, "get write access");
+ BUFFER_TRACE(new_bh, "get_write_access");
+ ext3_journal_get_write_access(handle, new_bh);
+ new_de->inode = le32_to_cpu(old_inode->i_ino);
+ if (EXT3_HAS_INCOMPAT_FEATURE(new_dir->i_sb,
+ EXT3_FEATURE_INCOMPAT_FILETYPE))
+ new_de->file_type = old_de->file_type;
+ new_dir->i_version = ++event;
+ BUFFER_TRACE(new_bh, "call ext3_journal_dirty_metadata");
+ ext3_journal_dirty_metadata(handle, new_bh);
+ brelse(new_bh);
+ new_bh = NULL;
+ }
+
+ /*
+ * Like most other Unix systems, set the ctime for inodes on a
+ * rename.
+ */
+ old_inode->i_ctime = CURRENT_TIME;
+ ext3_mark_inode_dirty(handle, old_inode);
+
+ /*
+ * ok, that's it
+ */
+ ext3_delete_entry(handle, old_dir, old_de, old_bh);
+
+ if (new_inode) {
+ new_inode->i_nlink--;
+ new_inode->i_ctime = CURRENT_TIME;
+ }
+ old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
+ old_dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL;
+ if (dir_bh) {
+ BUFFER_TRACE(dir_bh, "get_write_access");
+ ext3_journal_get_write_access(handle, dir_bh);
+ PARENT_INO(dir_bh->b_data) = le32_to_cpu(new_dir->i_ino);
+ BUFFER_TRACE(dir_bh, "call ext3_journal_dirty_metadata");
+ ext3_journal_dirty_metadata(handle, dir_bh);
+ old_dir->i_nlink--;
+ if (new_inode) {
+ new_inode->i_nlink--;
+ } else {
+ new_dir->i_nlink++;
+ new_dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL;
+ ext3_mark_inode_dirty(handle, new_dir);
+ }
+ }
+ ext3_mark_inode_dirty(handle, old_dir);
+ if (new_inode) {
+ ext3_mark_inode_dirty(handle, new_inode);
+ if (!new_inode->i_nlink)
+ ext3_orphan_add(handle, new_inode);
+ }
+ retval = 0;
+
+end_rename:
+ brelse (dir_bh);
+ brelse (old_bh);
+ brelse (new_bh);
+ ext3_journal_stop(handle, old_dir);
+ return retval;
+}
+
+/*
+ * directories can handle most operations...
+ */
+struct inode_operations ext3_dir_inode_operations = {
+ create: ext3_create, /* BKL held */
+ lookup: ext3_lookup, /* BKL held */
+ link: ext3_link, /* BKL held */
+ unlink: ext3_unlink, /* BKL held */
+ symlink: ext3_symlink, /* BKL held */
+ mkdir: ext3_mkdir, /* BKL held */
+ rmdir: ext3_rmdir, /* BKL held */
+ mknod: ext3_mknod, /* BKL held */
+ rename: ext3_rename, /* BKL held */
+};
diff --git a/fs/ext3/super.c b/fs/ext3/super.c
new file mode 100644
index 00000000000000..33e2f9726cb458
--- /dev/null
+++ b/fs/ext3/super.c
@@ -0,0 +1,1854 @@
+/*
+ * linux/fs/ext3/super.c
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * from
+ *
+ * linux/fs/minix/inode.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * Big-endian to little-endian byte-swapping/bitmaps by
+ * David S. Miller (davem@caip.rutgers.edu), 1995
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/jbd.h>
+#include <linux/ext3_fs.h>
+#include <linux/ext3_jbd.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/locks.h>
+#include <linux/blkdev.h>
+#include <linux/smp_lock.h>
+#include <linux/random.h>
+#include <asm/uaccess.h>
+
+#ifdef CONFIG_JBD_DEBUG
+static int ext3_ro_after; /* Make fs read-only after this many jiffies */
+#endif
+
+static int ext3_load_journal(struct super_block *, struct ext3_super_block *);
+static int ext3_create_journal(struct super_block *, struct ext3_super_block *,
+ int);
+static void ext3_commit_super (struct super_block * sb,
+ struct ext3_super_block * es,
+ int sync);
+static void ext3_mark_recovery_complete(struct super_block * sb,
+ struct ext3_super_block * es);
+static void ext3_clear_journal_err(struct super_block * sb,
+ struct ext3_super_block * es);
+
+static int ext3_sync_fs(struct super_block * sb);
+
+#ifdef CONFIG_JBD_DEBUG
+int journal_no_write[2];
+
+/*
+ * Debug code for turning filesystems "read-only" after a specified
+ * amount of time. This is for crash/recovery testing.
+ */
+
+static void make_rdonly(kdev_t dev, int *no_write)
+{
+ if (dev) {
+ printk(KERN_WARNING "Turning device %s read-only\n",
+ bdevname(dev));
+ *no_write = 0xdead0000 + dev;
+ }
+}
+
+static void turn_fs_readonly(unsigned long arg)
+{
+ struct super_block *sb = (struct super_block *)arg;
+
+ make_rdonly(sb->s_dev, &journal_no_write[0]);
+ make_rdonly(EXT3_SB(sb)->s_journal->j_dev, &journal_no_write[1]);
+ wake_up(&EXT3_SB(sb)->ro_wait_queue);
+}
+
+static void setup_ro_after(struct super_block *sb)
+{
+ struct ext3_sb_info *sbi = EXT3_SB(sb);
+ init_timer(&sbi->turn_ro_timer);
+ if (ext3_ro_after) {
+ printk(KERN_DEBUG "fs will go read-only in %d jiffies\n",
+ ext3_ro_after);
+ init_waitqueue_head(&sbi->ro_wait_queue);
+ journal_no_write[0] = 0;
+ journal_no_write[1] = 0;
+ sbi->turn_ro_timer.function = turn_fs_readonly;
+ sbi->turn_ro_timer.data = (unsigned long)sb;
+ sbi->turn_ro_timer.expires = jiffies + ext3_ro_after;
+ ext3_ro_after = 0;
+ add_timer(&sbi->turn_ro_timer);
+ }
+}
+
+static void clear_ro_after(struct super_block *sb)
+{
+ del_timer_sync(&EXT3_SB(sb)->turn_ro_timer);
+ journal_no_write[0] = 0;
+ journal_no_write[1] = 0;
+ ext3_ro_after = 0;
+}
+#else
+#define setup_ro_after(sb) do {} while (0)
+#define clear_ro_after(sb) do {} while (0)
+#endif
+
+
+static char error_buf[1024];
+
+/* Determine the appropriate response to ext3_error on a given filesystem */
+
+static int ext3_error_behaviour(struct super_block *sb)
+{
+ /* First check for mount-time options */
+ if (test_opt (sb, ERRORS_PANIC))
+ return EXT3_ERRORS_PANIC;
+ if (test_opt (sb, ERRORS_RO))
+ return EXT3_ERRORS_RO;
+ if (test_opt (sb, ERRORS_CONT))
+ return EXT3_ERRORS_CONTINUE;
+
+ /* If no overrides were specified on the mount, then fall back
+ * to the default behaviour set in the filesystem's superblock
+ * on disk. */
+ switch (le16_to_cpu(sb->u.ext3_sb.s_es->s_errors)) {
+ case EXT3_ERRORS_PANIC:
+ return EXT3_ERRORS_PANIC;
+ case EXT3_ERRORS_RO:
+ return EXT3_ERRORS_RO;
+ default:
+ break;
+ }
+ return EXT3_ERRORS_CONTINUE;
+}
+
+/* Deal with the reporting of failure conditions on a filesystem such as
+ * inconsistencies detected or read IO failures.
+ *
+ * On ext2, we can store the error state of the filesystem in the
+ * superblock. That is not possible on ext3, because we may have other
+ * write ordering constraints on the superblock which prevent us from
+ * writing it out straight away; and given that the journal is about to
+ * be aborted, we can't rely on the current, or future, transactions to
+ * write out the superblock safely.
+ *
+ * We'll just use the journal_abort() error code to record an error in
+ * the journal instead. On recovery, the journal will compain about
+ * that error until we've noted it down and cleared it.
+ */
+
+static void ext3_handle_error(struct super_block *sb)
+{
+ struct ext3_super_block *es = EXT3_SB(sb)->s_es;
+
+ EXT3_SB(sb)->s_mount_state |= EXT3_ERROR_FS;
+ es->s_state |= cpu_to_le32(EXT3_ERROR_FS);
+
+ if (sb->s_flags & MS_RDONLY)
+ return;
+
+ if (ext3_error_behaviour(sb) != EXT3_ERRORS_CONTINUE) {
+ EXT3_SB(sb)->s_mount_opt |= EXT3_MOUNT_ABORT;
+ journal_abort(EXT3_SB(sb)->s_journal, -EIO);
+ }
+
+ if (ext3_error_behaviour(sb) == EXT3_ERRORS_PANIC)
+ panic ("EXT3-fs (device %s): panic forced after error\n",
+ bdevname(sb->s_dev));
+
+ if (ext3_error_behaviour(sb) == EXT3_ERRORS_RO) {
+ printk (KERN_CRIT "Remounting filesystem read-only\n");
+ sb->s_flags |= MS_RDONLY;
+ }
+
+ ext3_commit_super(sb, es, 1);
+}
+
+void ext3_error (struct super_block * sb, const char * function,
+ const char * fmt, ...)
+{
+ va_list args;
+
+ va_start (args, fmt);
+ vsprintf (error_buf, fmt, args);
+ va_end (args);
+
+ printk (KERN_CRIT "EXT3-fs error (device %s): %s: %s\n",
+ bdevname(sb->s_dev), function, error_buf);
+
+ ext3_handle_error(sb);
+}
+
+const char *ext3_decode_error(struct super_block * sb, int errno, char nbuf[16])
+{
+ char *errstr = NULL;
+
+ switch (errno) {
+ case -EIO:
+ errstr = "IO failure";
+ break;
+ case -ENOMEM:
+ errstr = "Out of memory";
+ break;
+ case -EROFS:
+ if (!sb || EXT3_SB(sb)->s_journal->j_flags & JFS_ABORT)
+ errstr = "Journal has aborted";
+ else
+ errstr = "Readonly filesystem";
+ break;
+ default:
+ /* If the caller passed in an extra buffer for unknown
+ * errors, textualise them now. Else we just return
+ * NULL. */
+ if (nbuf) {
+ /* Check for truncated error codes... */
+ if (snprintf(nbuf, 16, "error %d", -errno) >= 0)
+ errstr = nbuf;
+ }
+
+ break;
+ }
+
+ return errstr;
+}
+
+/* __ext3_std_error decodes expected errors from journaling functions
+ * automatically and invokes the appropriate error response. */
+
+void __ext3_std_error (struct super_block * sb, const char * function,
+ int errno)
+{
+ char nbuf[16];
+ const char *errstr = ext3_decode_error(sb, errno, nbuf);
+
+ printk (KERN_CRIT "EXT3-fs error (device %s) in %s: %s\n",
+ bdevname(sb->s_dev), function, errstr);
+
+ ext3_handle_error(sb);
+}
+
+/*
+ * ext3_abort is a much stronger failure handler than ext3_error. The
+ * abort function may be used to deal with unrecoverable failures such
+ * as journal IO errors or ENOMEM at a critical moment in log management.
+ *
+ * We unconditionally force the filesystem into an ABORT|READONLY state,
+ * unless the error response on the fs has been set to panic in which
+ * case we take the easy way out and panic immediately.
+ */
+
+void ext3_abort (struct super_block * sb, const char * function,
+ const char * fmt, ...)
+{
+ va_list args;
+
+ printk (KERN_CRIT "ext3_abort called.\n");
+
+ va_start (args, fmt);
+ vsprintf (error_buf, fmt, args);
+ va_end (args);
+
+ if (ext3_error_behaviour(sb) == EXT3_ERRORS_PANIC)
+ panic ("EXT3-fs panic (device %s): %s: %s\n",
+ bdevname(sb->s_dev), function, error_buf);
+
+ printk (KERN_CRIT "EXT3-fs abort (device %s): %s: %s\n",
+ bdevname(sb->s_dev), function, error_buf);
+
+ if (sb->s_flags & MS_RDONLY)
+ return;
+
+ printk (KERN_CRIT "Remounting filesystem read-only\n");
+ sb->u.ext3_sb.s_mount_state |= EXT3_ERROR_FS;
+ sb->s_flags |= MS_RDONLY;
+ sb->u.ext3_sb.s_mount_opt |= EXT3_MOUNT_ABORT;
+ journal_abort(EXT3_SB(sb)->s_journal, -EIO);
+}
+
+/* Deal with the reporting of failure conditions while running, such as
+ * inconsistencies in operation or invalid system states.
+ *
+ * Use ext3_error() for cases of invalid filesystem states, as that will
+ * record an error on disk and force a filesystem check on the next boot.
+ */
+NORET_TYPE void ext3_panic (struct super_block * sb, const char * function,
+ const char * fmt, ...)
+{
+ va_list args;
+
+ va_start (args, fmt);
+ vsprintf (error_buf, fmt, args);
+ va_end (args);
+
+ /* this is to prevent panic from syncing this filesystem */
+ /* AKPM: is this sufficient? */
+ sb->s_flags |= MS_RDONLY;
+ panic ("EXT3-fs panic (device %s): %s: %s\n",
+ bdevname(sb->s_dev), function, error_buf);
+}
+
+void ext3_warning (struct super_block * sb, const char * function,
+ const char * fmt, ...)
+{
+ va_list args;
+
+ va_start (args, fmt);
+ vsprintf (error_buf, fmt, args);
+ va_end (args);
+ printk (KERN_WARNING "EXT3-fs warning (device %s): %s: %s\n",
+ bdevname(sb->s_dev), function, error_buf);
+}
+
+void ext3_update_dynamic_rev(struct super_block *sb)
+{
+ struct ext3_super_block *es = EXT3_SB(sb)->s_es;
+
+ if (le32_to_cpu(es->s_rev_level) > EXT3_GOOD_OLD_REV)
+ return;
+
+ ext3_warning(sb, __FUNCTION__,
+ "updating to rev %d because of new feature flag, "
+ "running e2fsck is recommended",
+ EXT3_DYNAMIC_REV);
+
+ es->s_first_ino = cpu_to_le32(EXT3_GOOD_OLD_FIRST_INO);
+ es->s_inode_size = cpu_to_le16(EXT3_GOOD_OLD_INODE_SIZE);
+ es->s_rev_level = cpu_to_le32(EXT3_DYNAMIC_REV);
+ /* leave es->s_feature_*compat flags alone */
+ /* es->s_uuid will be set by e2fsck if empty */
+
+ /*
+ * The rest of the superblock fields should be zero, and if not it
+ * means they are likely already in use, so leave them alone. We
+ * can leave it up to e2fsck to clean up any inconsistencies there.
+ */
+}
+
+/*
+ * Open the external journal device
+ */
+static struct block_device *ext3_blkdev_get(kdev_t dev)
+{
+ struct block_device *bdev;
+ int err = -ENODEV;
+
+ bdev = bdget(kdev_t_to_nr(dev));
+ if (bdev == NULL)
+ goto fail;
+ err = blkdev_get(bdev, FMODE_READ|FMODE_WRITE, 0, BDEV_FS);
+ if (err < 0)
+ goto fail;
+ return bdev;
+
+fail:
+ printk(KERN_ERR "EXT3: failed to open journal device %s: %d\n",
+ bdevname(dev), err);
+ return NULL;
+}
+
+/*
+ * Release the journal device
+ */
+static int ext3_blkdev_put(struct block_device *bdev)
+{
+ return blkdev_put(bdev, BDEV_FS);
+}
+
+static int ext3_blkdev_remove(struct ext3_sb_info *sbi)
+{
+ struct block_device *bdev;
+ int ret = -ENODEV;
+
+ bdev = sbi->journal_bdev;
+ if (bdev) {
+ ret = ext3_blkdev_put(bdev);
+ sbi->journal_bdev = 0;
+ }
+ return ret;
+}
+
+#define orphan_list_entry(l) list_entry((l), struct inode, u.ext3_i.i_orphan)
+
+static void dump_orphan_list(struct super_block *sb, struct ext3_sb_info *sbi)
+{
+ struct list_head *l;
+
+ printk(KERN_ERR "sb orphan head is %d\n",
+ le32_to_cpu(sbi->s_es->s_last_orphan));
+
+ printk(KERN_ERR "sb_info orphan list:\n");
+ list_for_each(l, &sbi->s_orphan) {
+ struct inode *inode = orphan_list_entry(l);
+ printk(KERN_ERR " "
+ "inode 0x%04x:%ld at %p: mode %o, nlink %d, next %d\n",
+ inode->i_dev, inode->i_ino, inode,
+ inode->i_mode, inode->i_nlink,
+ le32_to_cpu(NEXT_ORPHAN(inode)));
+ }
+}
+
+void ext3_put_super (struct super_block * sb)
+{
+ struct ext3_sb_info *sbi = EXT3_SB(sb);
+ struct ext3_super_block *es = sbi->s_es;
+ kdev_t j_dev = sbi->s_journal->j_dev;
+ int i;
+
+ journal_destroy(sbi->s_journal);
+ if (!(sb->s_flags & MS_RDONLY)) {
+ EXT3_CLEAR_INCOMPAT_FEATURE(sb, EXT3_FEATURE_INCOMPAT_RECOVER);
+ es->s_state = le16_to_cpu(sbi->s_mount_state);
+ BUFFER_TRACE(sbi->s_sbh, "marking dirty");
+ mark_buffer_dirty(sbi->s_sbh);
+ ext3_commit_super(sb, es, 1);
+ }
+
+ for (i = 0; i < sbi->s_gdb_count; i++)
+ brelse(sbi->s_group_desc[i]);
+ kfree(sbi->s_group_desc);
+ for (i = 0; i < EXT3_MAX_GROUP_LOADED; i++)
+ brelse(sbi->s_inode_bitmap[i]);
+ for (i = 0; i < EXT3_MAX_GROUP_LOADED; i++)
+ brelse(sbi->s_block_bitmap[i]);
+ brelse(sbi->s_sbh);
+
+ /* Debugging code just in case the in-memory inode orphan list
+ * isn't empty. The on-disk one can be non-empty if we've
+ * detected an error and taken the fs readonly, but the
+ * in-memory list had better be clean by this point. */
+ if (!list_empty(&sbi->s_orphan))
+ dump_orphan_list(sb, sbi);
+ J_ASSERT(list_empty(&sbi->s_orphan));
+
+ invalidate_buffers(sb->s_dev);
+ if (j_dev != sb->s_dev) {
+ /*
+ * Invalidate the journal device's buffers. We don't want them
+ * floating about in memory - the physical journal device may
+ * hotswapped, and it breaks the `ro-after' testing code.
+ */
+ fsync_no_super(j_dev);
+ invalidate_buffers(j_dev);
+ ext3_blkdev_remove(sbi);
+ }
+ clear_ro_after(sb);
+
+ return;
+}
+
+static struct dquot_operations ext3_qops;
+
+static struct super_operations ext3_sops = {
+ read_inode: ext3_read_inode, /* BKL held */
+ write_inode: ext3_write_inode, /* BKL not held. Don't need */
+ dirty_inode: ext3_dirty_inode, /* BKL not held. We take it */
+ put_inode: ext3_put_inode, /* BKL not held. Don't need */
+ delete_inode: ext3_delete_inode, /* BKL not held. We take it */
+ put_super: ext3_put_super, /* BKL held */
+ write_super: ext3_write_super, /* BKL held */
+ sync_fs: ext3_sync_fs,
+ write_super_lockfs: ext3_write_super_lockfs, /* BKL not held. Take it */
+ unlockfs: ext3_unlockfs, /* BKL not held. We take it */
+ statfs: ext3_statfs, /* BKL held */
+ remount_fs: ext3_remount, /* BKL held */
+};
+
+static int want_value(char *value, char *option)
+{
+ if (!value || !*value) {
+ printk(KERN_NOTICE "EXT3-fs: the %s option needs an argument\n",
+ option);
+ return -1;
+ }
+ return 0;
+}
+
+static int want_null_value(char *value, char *option)
+{
+ if (*value) {
+ printk(KERN_NOTICE "EXT3-fs: Invalid %s argument: %s\n",
+ option, value);
+ return -1;
+ }
+ return 0;
+}
+
+static int want_numeric(char *value, char *option, unsigned long *number)
+{
+ if (want_value(value, option))
+ return -1;
+ *number = simple_strtoul(value, &value, 0);
+ if (want_null_value(value, option))
+ return -1;
+ return 0;
+}
+
+/*
+ * This function has been shamelessly adapted from the msdos fs
+ */
+static int parse_options (char * options, unsigned long * sb_block,
+ struct ext3_sb_info *sbi,
+ unsigned long * inum,
+ int is_remount)
+{
+ unsigned long *mount_options = &sbi->s_mount_opt;
+ uid_t *resuid = &sbi->s_resuid;
+ gid_t *resgid = &sbi->s_resgid;
+ char * this_char;
+ char * value;
+
+ if (!options)
+ return 1;
+ for (this_char = strtok (options, ",");
+ this_char != NULL;
+ this_char = strtok (NULL, ",")) {
+ if ((value = strchr (this_char, '=')) != NULL)
+ *value++ = 0;
+ if (!strcmp (this_char, "bsddf"))
+ clear_opt (*mount_options, MINIX_DF);
+ else if (!strcmp (this_char, "nouid32")) {
+ set_opt (*mount_options, NO_UID32);
+ }
+ else if (!strcmp (this_char, "abort"))
+ set_opt (*mount_options, ABORT);
+ else if (!strcmp (this_char, "check")) {
+ if (!value || !*value || !strcmp (value, "none"))
+ clear_opt (*mount_options, CHECK);
+ else
+#ifdef CONFIG_EXT3_CHECK
+ set_opt (*mount_options, CHECK);
+#else
+ printk(KERN_ERR
+ "EXT3 Check option not supported\n");
+#endif
+ }
+ else if (!strcmp (this_char, "debug"))
+ set_opt (*mount_options, DEBUG);
+ else if (!strcmp (this_char, "errors")) {
+ if (want_value(value, "errors"))
+ return 0;
+ if (!strcmp (value, "continue")) {
+ clear_opt (*mount_options, ERRORS_RO);
+ clear_opt (*mount_options, ERRORS_PANIC);
+ set_opt (*mount_options, ERRORS_CONT);
+ }
+ else if (!strcmp (value, "remount-ro")) {
+ clear_opt (*mount_options, ERRORS_CONT);
+ clear_opt (*mount_options, ERRORS_PANIC);
+ set_opt (*mount_options, ERRORS_RO);
+ }
+ else if (!strcmp (value, "panic")) {
+ clear_opt (*mount_options, ERRORS_CONT);
+ clear_opt (*mount_options, ERRORS_RO);
+ set_opt (*mount_options, ERRORS_PANIC);
+ }
+ else {
+ printk (KERN_ERR
+ "EXT3-fs: Invalid errors option: %s\n",
+ value);
+ return 0;
+ }
+ }
+ else if (!strcmp (this_char, "grpid") ||
+ !strcmp (this_char, "bsdgroups"))
+ set_opt (*mount_options, GRPID);
+ else if (!strcmp (this_char, "minixdf"))
+ set_opt (*mount_options, MINIX_DF);
+ else if (!strcmp (this_char, "nocheck"))
+ clear_opt (*mount_options, CHECK);
+ else if (!strcmp (this_char, "nogrpid") ||
+ !strcmp (this_char, "sysvgroups"))
+ clear_opt (*mount_options, GRPID);
+ else if (!strcmp (this_char, "resgid")) {
+ unsigned long v;
+ if (want_numeric(value, "resgid", &v))
+ return 0;
+ *resgid = v;
+ }
+ else if (!strcmp (this_char, "resuid")) {
+ unsigned long v;
+ if (want_numeric(value, "resuid", &v))
+ return 0;
+ *resuid = v;
+ }
+ else if (!strcmp (this_char, "sb")) {
+ if (want_numeric(value, "sb", sb_block))
+ return 0;
+ }
+#ifdef CONFIG_JBD_DEBUG
+ else if (!strcmp (this_char, "ro-after")) {
+ unsigned long v;
+ if (want_numeric(value, "ro-after", &v))
+ return 0;
+ ext3_ro_after = v;
+ }
+#endif
+ /* Silently ignore the quota options */
+ else if (!strcmp (this_char, "grpquota")
+ || !strcmp (this_char, "noquota")
+ || !strcmp (this_char, "quota")
+ || !strcmp (this_char, "usrquota"))
+ /* Don't do anything ;-) */ ;
+ else if (!strcmp (this_char, "journal")) {
+ /* @@@ FIXME */
+ /* Eventually we will want to be able to create
+ a journal file here. For now, only allow the
+ user to specify an existing inode to be the
+ journal file. */
+ if (is_remount) {
+ printk(KERN_ERR "EXT3-fs: cannot specify "
+ "journal on remount\n");
+ return 0;
+ }
+
+ if (want_value(value, "journal"))
+ return 0;
+ if (!strcmp (value, "update"))
+ set_opt (*mount_options, UPDATE_JOURNAL);
+ else if (want_numeric(value, "journal", inum))
+ return 0;
+ }
+ else if (!strcmp (this_char, "noload"))
+ set_opt (*mount_options, NOLOAD);
+ else if (!strcmp (this_char, "data")) {
+ int data_opt = 0;
+
+ if (want_value(value, "data"))
+ return 0;
+ if (!strcmp (value, "journal"))
+ data_opt = EXT3_MOUNT_JOURNAL_DATA;
+ else if (!strcmp (value, "ordered"))
+ data_opt = EXT3_MOUNT_ORDERED_DATA;
+ else if (!strcmp (value, "writeback"))
+ data_opt = EXT3_MOUNT_WRITEBACK_DATA;
+ else {
+ printk (KERN_ERR
+ "EXT3-fs: Invalid data option: %s\n",
+ value);
+ return 0;
+ }
+ if (is_remount) {
+ if ((*mount_options & EXT3_MOUNT_DATA_FLAGS) !=
+ data_opt) {
+ printk(KERN_ERR
+ "EXT3-fs: cannot change data "
+ "mode on remount\n");
+ return 0;
+ }
+ } else {
+ *mount_options &= ~EXT3_MOUNT_DATA_FLAGS;
+ *mount_options |= data_opt;
+ }
+ } else if (!strcmp (this_char, "commit")) {
+ unsigned long v;
+ if (want_numeric(value, "commit", &v))
+ return 0;
+ sbi->s_commit_interval = (HZ * v);
+ } else {
+ printk (KERN_ERR
+ "EXT3-fs: Unrecognized mount option %s\n",
+ this_char);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int ext3_setup_super(struct super_block *sb, struct ext3_super_block *es,
+ int read_only)
+{
+ struct ext3_sb_info *sbi = EXT3_SB(sb);
+ int res = 0;
+
+ if (le32_to_cpu(es->s_rev_level) > EXT3_MAX_SUPP_REV) {
+ printk (KERN_ERR "EXT3-fs warning: revision level too high, "
+ "forcing read-only mode\n");
+ res = MS_RDONLY;
+ }
+ if (read_only)
+ return res;
+ if (!(sbi->s_mount_state & EXT3_VALID_FS))
+ printk (KERN_WARNING "EXT3-fs warning: mounting unchecked fs, "
+ "running e2fsck is recommended\n");
+ else if ((sbi->s_mount_state & EXT3_ERROR_FS))
+ printk (KERN_WARNING
+ "EXT3-fs warning: mounting fs with errors, "
+ "running e2fsck is recommended\n");
+ else if ((__s16) le16_to_cpu(es->s_max_mnt_count) >= 0 &&
+ le16_to_cpu(es->s_mnt_count) >=
+ (unsigned short) (__s16) le16_to_cpu(es->s_max_mnt_count))
+ printk (KERN_WARNING
+ "EXT3-fs warning: maximal mount count reached, "
+ "running e2fsck is recommended\n");
+ else if (le32_to_cpu(es->s_checkinterval) &&
+ (le32_to_cpu(es->s_lastcheck) +
+ le32_to_cpu(es->s_checkinterval) <= CURRENT_TIME))
+ printk (KERN_WARNING
+ "EXT3-fs warning: checktime reached, "
+ "running e2fsck is recommended\n");
+#if 0
+ /* @@@ We _will_ want to clear the valid bit if we find
+ inconsistencies, to force a fsck at reboot. But for
+ a plain journaled filesystem we can keep it set as
+ valid forever! :) */
+ es->s_state = cpu_to_le16(le16_to_cpu(es->s_state) & ~EXT3_VALID_FS);
+#endif
+ if (!(__s16) le16_to_cpu(es->s_max_mnt_count))
+ es->s_max_mnt_count =
+ (__s16) cpu_to_le16(EXT3_DFL_MAX_MNT_COUNT);
+ es->s_mnt_count=cpu_to_le16(le16_to_cpu(es->s_mnt_count) + 1);
+ es->s_mtime = cpu_to_le32(CURRENT_TIME);
+ ext3_update_dynamic_rev(sb);
+ EXT3_SET_INCOMPAT_FEATURE(sb, EXT3_FEATURE_INCOMPAT_RECOVER);
+ ext3_commit_super (sb, es, 1);
+ if (test_opt (sb, DEBUG))
+ printk (KERN_INFO
+ "[EXT3 FS %s, %s, bs=%lu, gc=%lu, "
+ "bpg=%lu, ipg=%lu, mo=%04lx]\n",
+ EXT3FS_VERSION, EXT3FS_DATE, sb->s_blocksize,
+ sbi->s_groups_count,
+ EXT3_BLOCKS_PER_GROUP(sb),
+ EXT3_INODES_PER_GROUP(sb),
+ sbi->s_mount_opt);
+ printk(KERN_INFO "EXT3 FS " EXT3FS_VERSION ", " EXT3FS_DATE " on %s, ",
+ bdevname(sb->s_dev));
+ if (EXT3_SB(sb)->s_journal->j_inode == NULL) {
+ printk("external journal on %s\n",
+ bdevname(EXT3_SB(sb)->s_journal->j_dev));
+ } else {
+ printk("internal journal\n");
+ }
+#ifdef CONFIG_EXT3_CHECK
+ if (test_opt (sb, CHECK)) {
+ ext3_check_blocks_bitmap (sb);
+ ext3_check_inodes_bitmap (sb);
+ }
+#endif
+ setup_ro_after(sb);
+ return res;
+}
+
+static int ext3_check_descriptors (struct super_block * sb)
+{
+ struct ext3_sb_info *sbi = EXT3_SB(sb);
+ unsigned long block = le32_to_cpu(sbi->s_es->s_first_data_block);
+ struct ext3_group_desc * gdp = NULL;
+ int desc_block = 0;
+ int i;
+
+ ext3_debug ("Checking group descriptors");
+
+ for (i = 0; i < sbi->s_groups_count; i++)
+ {
+ if ((i % EXT3_DESC_PER_BLOCK(sb)) == 0)
+ gdp = (struct ext3_group_desc *)
+ sbi->s_group_desc[desc_block++]->b_data;
+ if (le32_to_cpu(gdp->bg_block_bitmap) < block ||
+ le32_to_cpu(gdp->bg_block_bitmap) >=
+ block + EXT3_BLOCKS_PER_GROUP(sb))
+ {
+ ext3_error (sb, "ext3_check_descriptors",
+ "Block bitmap for group %d"
+ " not in group (block %lu)!",
+ i, (unsigned long)
+ le32_to_cpu(gdp->bg_block_bitmap));
+ return 0;
+ }
+ if (le32_to_cpu(gdp->bg_inode_bitmap) < block ||
+ le32_to_cpu(gdp->bg_inode_bitmap) >=
+ block + EXT3_BLOCKS_PER_GROUP(sb))
+ {
+ ext3_error (sb, "ext3_check_descriptors",
+ "Inode bitmap for group %d"
+ " not in group (block %lu)!",
+ i, (unsigned long)
+ le32_to_cpu(gdp->bg_inode_bitmap));
+ return 0;
+ }
+ if (le32_to_cpu(gdp->bg_inode_table) < block ||
+ le32_to_cpu(gdp->bg_inode_table) + sbi->s_itb_per_group >=
+ block + EXT3_BLOCKS_PER_GROUP(sb))
+ {
+ ext3_error (sb, "ext3_check_descriptors",
+ "Inode table for group %d"
+ " not in group (block %lu)!",
+ i, (unsigned long)
+ le32_to_cpu(gdp->bg_inode_table));
+ return 0;
+ }
+ block += EXT3_BLOCKS_PER_GROUP(sb);
+ gdp++;
+ }
+ return 1;
+}
+
+static unsigned long descriptor_loc(struct super_block *sb,
+ unsigned long logic_sb_block,
+ int nr)
+{
+ struct ext3_sb_info *sbi = EXT3_SB(sb);
+ unsigned long bg, first_data_block, first_meta_bg;
+ int has_super = 0;
+
+ first_data_block = le32_to_cpu(sbi->s_es->s_first_data_block);
+ first_meta_bg = le32_to_cpu(sbi->s_es->s_first_meta_bg);
+
+ if (!EXT3_HAS_INCOMPAT_FEATURE(sb, EXT3_FEATURE_INCOMPAT_META_BG) ||
+ nr < first_meta_bg)
+ return (logic_sb_block + nr + 1);
+ bg = sbi->s_desc_per_block * nr;
+ if (ext3_bg_has_super(sb, bg))
+ has_super = 1;
+ return (first_data_block + has_super + (bg * sbi->s_blocks_per_group));
+}
+
+
+/* ext3_orphan_cleanup() walks a singly-linked list of inodes (starting at
+ * the superblock) which were deleted from all directories, but held open by
+ * a process at the time of a crash. We walk the list and try to delete these
+ * inodes at recovery time (only with a read-write filesystem).
+ *
+ * In order to keep the orphan inode chain consistent during traversal (in
+ * case of crash during recovery), we link each inode into the superblock
+ * orphan list_head and handle it the same way as an inode deletion during
+ * normal operation (which journals the operations for us).
+ *
+ * We only do an iget() and an iput() on each inode, which is very safe if we
+ * accidentally point at an in-use or already deleted inode. The worst that
+ * can happen in this case is that we get a "bit already cleared" message from
+ * ext3_free_inode(). The only reason we would point at a wrong inode is if
+ * e2fsck was run on this filesystem, and it must have already done the orphan
+ * inode cleanup for us, so we can safely abort without any further action.
+ */
+static void ext3_orphan_cleanup (struct super_block * sb,
+ struct ext3_super_block * es)
+{
+ unsigned int s_flags = sb->s_flags;
+ int nr_orphans = 0, nr_truncates = 0;
+ if (!es->s_last_orphan) {
+ jbd_debug(4, "no orphan inodes to clean up\n");
+ return;
+ }
+
+ if (sb->u.ext3_sb.s_mount_state & EXT3_ERROR_FS) {
+ if (es->s_last_orphan)
+ jbd_debug(1, "Errors on filesystem, "
+ "clearing orphan list.\n");
+ es->s_last_orphan = 0;
+ jbd_debug(1, "Skipping orphan recovery on fs with errors.\n");
+ return;
+ }
+
+ if (s_flags & MS_RDONLY) {
+ printk(KERN_INFO "EXT3-fs: %s: orphan cleanup on readonly fs\n",
+ bdevname(sb->s_dev));
+ sb->s_flags &= ~MS_RDONLY;
+ }
+
+ while (es->s_last_orphan) {
+ struct inode *inode;
+
+ if (!(inode =
+ ext3_orphan_get(sb, le32_to_cpu(es->s_last_orphan)))) {
+ es->s_last_orphan = 0;
+ break;
+ }
+
+ list_add(&EXT3_I(inode)->i_orphan, &EXT3_SB(sb)->s_orphan);
+ if (inode->i_nlink) {
+ printk(KERN_DEBUG "%s: truncating inode %ld to %Ld "
+ "bytes\n", __FUNCTION__, inode->i_ino,
+ inode->i_size);
+ jbd_debug(2, "truncating inode %ld to %Ld bytes\n",
+ inode->i_ino, inode->i_size);
+ ext3_truncate(inode);
+ nr_truncates++;
+ } else {
+ printk(KERN_DEBUG "%s: deleting unreferenced "
+ "inode %ld\n", __FUNCTION__, inode->i_ino);
+ jbd_debug(2, "deleting unreferenced inode %ld\n",
+ inode->i_ino);
+ nr_orphans++;
+ }
+ iput(inode); /* The delete magic happens here! */
+ }
+
+#define PLURAL(x) (x), ((x)==1) ? "" : "s"
+
+ if (nr_orphans)
+ printk(KERN_INFO "EXT3-fs: %s: %d orphan inode%s deleted\n",
+ bdevname(sb->s_dev), PLURAL(nr_orphans));
+ if (nr_truncates)
+ printk(KERN_INFO "EXT3-fs: %s: %d truncate%s cleaned up\n",
+ bdevname(sb->s_dev), PLURAL(nr_truncates));
+ sb->s_flags = s_flags; /* Restore MS_RDONLY status */
+}
+
+#define log2(n) ffz(~(n))
+
+/*
+ * Maximal file size. There is a direct, and {,double-,triple-}indirect
+ * block limit, and also a limit of (2^32 - 1) 512-byte sectors in i_blocks.
+ * We need to be 1 filesystem block less than the 2^32 sector limit.
+ */
+static loff_t ext3_max_size(int bits)
+{
+ loff_t res = EXT3_NDIR_BLOCKS;
+ res += 1LL << (bits-2);
+ res += 1LL << (2*(bits-2));
+ res += 1LL << (3*(bits-2));
+ res <<= bits;
+ if (res > (512LL << 32) - (1 << bits))
+ res = (512LL << 32) - (1 << bits);
+ return res;
+}
+
+struct super_block * ext3_read_super (struct super_block * sb, void * data,
+ int silent)
+{
+ struct buffer_head * bh;
+ struct ext3_super_block *es = 0;
+ struct ext3_sb_info *sbi = EXT3_SB(sb);
+ unsigned long block;
+ unsigned long sb_block = 1;
+ unsigned long logic_sb_block = 1;
+ unsigned long offset = 0;
+ unsigned long journal_inum = 0;
+ kdev_t dev = sb->s_dev;
+ int blocksize;
+ int hblock;
+ int db_count;
+ int i;
+ int needs_recovery;
+
+#ifdef CONFIG_JBD_DEBUG
+ ext3_ro_after = 0;
+#endif
+ /*
+ * See what the current blocksize for the device is, and
+ * use that as the blocksize. Otherwise (or if the blocksize
+ * is smaller than the default) use the default.
+ * This is important for devices that have a hardware
+ * sectorsize that is larger than the default.
+ */
+ blocksize = EXT3_MIN_BLOCK_SIZE;
+ hblock = get_hardsect_size(dev);
+ if (blocksize < hblock)
+ blocksize = hblock;
+
+ sbi->s_mount_opt = 0;
+ sbi->s_resuid = EXT3_DEF_RESUID;
+ sbi->s_resgid = EXT3_DEF_RESGID;
+ if (!parse_options ((char *) data, &sb_block, sbi, &journal_inum, 0)) {
+ sb->s_dev = 0;
+ goto out_fail;
+ }
+
+ sb->s_blocksize = blocksize;
+ set_blocksize (dev, blocksize);
+
+ /*
+ * The ext3 superblock will not be buffer aligned for other than 1kB
+ * block sizes. We need to calculate the offset from buffer start.
+ */
+ if (blocksize != EXT3_MIN_BLOCK_SIZE) {
+ logic_sb_block = (sb_block * EXT3_MIN_BLOCK_SIZE) / blocksize;
+ offset = (sb_block * EXT3_MIN_BLOCK_SIZE) % blocksize;
+ }
+
+ if (!(bh = sb_bread(sb, logic_sb_block))) {
+ printk (KERN_ERR "EXT3-fs: unable to read superblock\n");
+ goto out_fail;
+ }
+ /*
+ * Note: s_es must be initialized as soon as possible because
+ * some ext3 macro-instructions depend on its value
+ */
+ es = (struct ext3_super_block *) (((char *)bh->b_data) + offset);
+ sbi->s_es = es;
+ sb->s_magic = le16_to_cpu(es->s_magic);
+ if (sb->s_magic != EXT3_SUPER_MAGIC) {
+ if (!silent)
+ printk(KERN_ERR
+ "VFS: Can't find ext3 filesystem on dev %s.\n",
+ bdevname(dev));
+ goto failed_mount;
+ }
+ if (le32_to_cpu(es->s_rev_level) == EXT3_GOOD_OLD_REV &&
+ (EXT3_HAS_COMPAT_FEATURE(sb, ~0U) ||
+ EXT3_HAS_RO_COMPAT_FEATURE(sb, ~0U) ||
+ EXT3_HAS_INCOMPAT_FEATURE(sb, ~0U)))
+ printk(KERN_WARNING
+ "EXT3-fs warning: feature flags set on rev 0 fs, "
+ "running e2fsck is recommended\n");
+ /*
+ * Check feature flags regardless of the revision level, since we
+ * previously didn't change the revision level when setting the flags,
+ * so there is a chance incompat flags are set on a rev 0 filesystem.
+ */
+ if ((i = EXT3_HAS_INCOMPAT_FEATURE(sb, ~EXT3_FEATURE_INCOMPAT_SUPP))) {
+ printk(KERN_ERR "EXT3-fs: %s: couldn't mount because of "
+ "unsupported optional features (%x).\n",
+ bdevname(dev), i);
+ goto failed_mount;
+ }
+ if (!(sb->s_flags & MS_RDONLY) &&
+ (i = EXT3_HAS_RO_COMPAT_FEATURE(sb, ~EXT3_FEATURE_RO_COMPAT_SUPP))){
+ printk(KERN_ERR "EXT3-fs: %s: couldn't mount RDWR because of "
+ "unsupported optional features (%x).\n",
+ bdevname(dev), i);
+ goto failed_mount;
+ }
+ sb->s_blocksize_bits = le32_to_cpu(es->s_log_block_size) + 10;
+ sb->s_blocksize = 1 << sb->s_blocksize_bits;
+
+ if (sb->s_blocksize < EXT3_MIN_BLOCK_SIZE ||
+ sb->s_blocksize > EXT3_MAX_BLOCK_SIZE) {
+ printk(KERN_ERR
+ "EXT3-fs: Unsupported filesystem blocksize %d on %s.\n",
+ blocksize, bdevname(dev));
+ goto failed_mount;
+ }
+
+ sb->s_maxbytes = ext3_max_size(sb->s_blocksize_bits);
+
+ if (sb->s_blocksize != blocksize) {
+ blocksize = sb->s_blocksize;
+
+ /*
+ * Make sure the blocksize for the filesystem is larger
+ * than the hardware sectorsize for the machine.
+ */
+ if (sb->s_blocksize < hblock) {
+ printk(KERN_ERR "EXT3-fs: blocksize %d too small for "
+ "device blocksize %d.\n", blocksize, hblock);
+ goto failed_mount;
+ }
+
+ brelse (bh);
+ set_blocksize (dev, sb->s_blocksize);
+ logic_sb_block = (sb_block * EXT3_MIN_BLOCK_SIZE) / blocksize;
+ offset = (sb_block * EXT3_MIN_BLOCK_SIZE) % blocksize;
+ bh = sb_bread(sb, logic_sb_block);
+ if (!bh) {
+ printk(KERN_ERR
+ "EXT3-fs: Can't read superblock on 2nd try.\n");
+ return NULL;
+ }
+ es = (struct ext3_super_block *)(((char *)bh->b_data) + offset);
+ sbi->s_es = es;
+ if (es->s_magic != le16_to_cpu(EXT3_SUPER_MAGIC)) {
+ printk (KERN_ERR
+ "EXT3-fs: Magic mismatch, very weird !\n");
+ goto failed_mount;
+ }
+ }
+
+ if (le32_to_cpu(es->s_rev_level) == EXT3_GOOD_OLD_REV) {
+ sbi->s_inode_size = EXT3_GOOD_OLD_INODE_SIZE;
+ sbi->s_first_ino = EXT3_GOOD_OLD_FIRST_INO;
+ } else {
+ sbi->s_inode_size = le16_to_cpu(es->s_inode_size);
+ sbi->s_first_ino = le32_to_cpu(es->s_first_ino);
+ if ((sbi->s_inode_size < EXT3_GOOD_OLD_INODE_SIZE) ||
+ (sbi->s_inode_size & (sbi->s_inode_size - 1)) ||
+ (sbi->s_inode_size > blocksize)) {
+ printk (KERN_ERR
+ "EXT3-fs: unsupported inode size: %d\n",
+ sbi->s_inode_size);
+ goto failed_mount;
+ }
+ }
+ sbi->s_frag_size = EXT3_MIN_FRAG_SIZE <<
+ le32_to_cpu(es->s_log_frag_size);
+ if (blocksize != sbi->s_frag_size) {
+ printk(KERN_ERR
+ "EXT3-fs: fragsize %lu != blocksize %u (unsupported)\n",
+ sbi->s_frag_size, blocksize);
+ goto failed_mount;
+ }
+ sbi->s_frags_per_block = 1;
+ sbi->s_blocks_per_group = le32_to_cpu(es->s_blocks_per_group);
+ sbi->s_frags_per_group = le32_to_cpu(es->s_frags_per_group);
+ sbi->s_inodes_per_group = le32_to_cpu(es->s_inodes_per_group);
+ sbi->s_inodes_per_block = blocksize / EXT3_INODE_SIZE(sb);
+ sbi->s_itb_per_group = sbi->s_inodes_per_group /sbi->s_inodes_per_block;
+ sbi->s_desc_per_block = blocksize / sizeof(struct ext3_group_desc);
+ sbi->s_sbh = bh;
+ if (sbi->s_resuid == EXT3_DEF_RESUID)
+ sbi->s_resuid = le16_to_cpu(es->s_def_resuid);
+ if (sbi->s_resgid == EXT3_DEF_RESGID)
+ sbi->s_resgid = le16_to_cpu(es->s_def_resgid);
+ sbi->s_mount_state = le16_to_cpu(es->s_state);
+ sbi->s_addr_per_block_bits = log2(EXT3_ADDR_PER_BLOCK(sb));
+ sbi->s_desc_per_block_bits = log2(EXT3_DESC_PER_BLOCK(sb));
+
+ if (sbi->s_blocks_per_group > blocksize * 8) {
+ printk (KERN_ERR
+ "EXT3-fs: #blocks per group too big: %lu\n",
+ sbi->s_blocks_per_group);
+ goto failed_mount;
+ }
+ if (sbi->s_frags_per_group > blocksize * 8) {
+ printk (KERN_ERR
+ "EXT3-fs: #fragments per group too big: %lu\n",
+ sbi->s_frags_per_group);
+ goto failed_mount;
+ }
+ if (sbi->s_inodes_per_group > blocksize * 8) {
+ printk (KERN_ERR
+ "EXT3-fs: #inodes per group too big: %lu\n",
+ sbi->s_inodes_per_group);
+ goto failed_mount;
+ }
+
+ sbi->s_groups_count = (le32_to_cpu(es->s_blocks_count) -
+ le32_to_cpu(es->s_first_data_block) +
+ EXT3_BLOCKS_PER_GROUP(sb) - 1) /
+ EXT3_BLOCKS_PER_GROUP(sb);
+ db_count = (sbi->s_groups_count + EXT3_DESC_PER_BLOCK(sb) - 1) /
+ EXT3_DESC_PER_BLOCK(sb);
+ sbi->s_group_desc = kmalloc(db_count * sizeof (struct buffer_head *),
+ GFP_KERNEL);
+ if (sbi->s_group_desc == NULL) {
+ printk (KERN_ERR "EXT3-fs: not enough memory\n");
+ goto failed_mount;
+ }
+ for (i = 0; i < db_count; i++) {
+ block = descriptor_loc(sb, logic_sb_block, i);
+ sbi->s_group_desc[i] = sb_bread(sb, block);
+ if (!sbi->s_group_desc[i]) {
+ printk (KERN_ERR "EXT3-fs: "
+ "can't read group descriptor %d\n", i);
+ db_count = i;
+ goto failed_mount2;
+ }
+ }
+ if (!ext3_check_descriptors (sb)) {
+ printk (KERN_ERR "EXT3-fs: group descriptors corrupted !\n");
+ goto failed_mount2;
+ }
+ for (i = 0; i < EXT3_MAX_GROUP_LOADED; i++) {
+ sbi->s_inode_bitmap_number[i] = 0;
+ sbi->s_inode_bitmap[i] = NULL;
+ sbi->s_block_bitmap_number[i] = 0;
+ sbi->s_block_bitmap[i] = NULL;
+ }
+ sbi->s_loaded_inode_bitmaps = 0;
+ sbi->s_loaded_block_bitmaps = 0;
+ sbi->s_gdb_count = db_count;
+ get_random_bytes(&sbi->s_next_generation, sizeof(u32));
+ /*
+ * set up enough so that it can read an inode
+ */
+ sb->s_op = &ext3_sops;
+ sb->dq_op = &ext3_qops;
+ INIT_LIST_HEAD(&sbi->s_orphan); /* unlinked but open files */
+
+ sb->s_root = 0;
+
+ needs_recovery = (es->s_last_orphan != 0 ||
+ EXT3_HAS_INCOMPAT_FEATURE(sb,
+ EXT3_FEATURE_INCOMPAT_RECOVER));
+
+ /*
+ * The first inode we look at is the journal inode. Don't try
+ * root first: it may be modified in the journal!
+ */
+ if (!test_opt(sb, NOLOAD) &&
+ EXT3_HAS_COMPAT_FEATURE(sb, EXT3_FEATURE_COMPAT_HAS_JOURNAL)) {
+ if (ext3_load_journal(sb, es))
+ goto failed_mount2;
+ } else if (journal_inum) {
+ if (ext3_create_journal(sb, es, journal_inum))
+ goto failed_mount2;
+ } else {
+ if (!silent)
+ printk (KERN_ERR
+ "ext3: No journal on filesystem on %s\n",
+ bdevname(dev));
+ goto failed_mount2;
+ }
+
+ /* We have now updated the journal if required, so we can
+ * validate the data journaling mode. */
+ switch (test_opt(sb, DATA_FLAGS)) {
+ case 0:
+ /* No mode set, assume a default based on the journal
+ capabilities: ORDERED_DATA if the journal can
+ cope, else JOURNAL_DATA */
+ if (journal_check_available_features
+ (sbi->s_journal, 0, 0, JFS_FEATURE_INCOMPAT_REVOKE))
+ set_opt(sbi->s_mount_opt, ORDERED_DATA);
+ else
+ set_opt(sbi->s_mount_opt, JOURNAL_DATA);
+ break;
+
+ case EXT3_MOUNT_ORDERED_DATA:
+ case EXT3_MOUNT_WRITEBACK_DATA:
+ if (!journal_check_available_features
+ (sbi->s_journal, 0, 0, JFS_FEATURE_INCOMPAT_REVOKE)) {
+ printk(KERN_ERR "EXT3-fs: Journal does not support "
+ "requested data journaling mode\n");
+ goto failed_mount3;
+ }
+ default:
+ break;
+ }
+
+ /*
+ * The journal_load will have done any necessary log recovery,
+ * so we can safely mount the rest of the filesystem now.
+ */
+
+ sb->s_root = d_alloc_root(iget(sb, EXT3_ROOT_INO));
+ if (!sb->s_root || !S_ISDIR(sb->s_root->d_inode->i_mode) ||
+ !sb->s_root->d_inode->i_blocks || !sb->s_root->d_inode->i_size) {
+ if (sb->s_root) {
+ dput(sb->s_root);
+ sb->s_root = NULL;
+ printk(KERN_ERR
+ "EXT3-fs: corrupt root inode, run e2fsck\n");
+ } else
+ printk(KERN_ERR "EXT3-fs: get root inode failed\n");
+ goto failed_mount3;
+ }
+
+ ext3_setup_super (sb, es, sb->s_flags & MS_RDONLY);
+ EXT3_SB(sb)->s_mount_state |= EXT3_ORPHAN_FS;
+ ext3_orphan_cleanup(sb, es);
+ EXT3_SB(sb)->s_mount_state &= ~EXT3_ORPHAN_FS;
+ if (needs_recovery)
+ printk (KERN_INFO "EXT3-fs: recovery complete.\n");
+ ext3_mark_recovery_complete(sb, es);
+ printk (KERN_INFO "EXT3-fs: mounted filesystem with %s data mode.\n",
+ test_opt(sb,DATA_FLAGS) == EXT3_MOUNT_JOURNAL_DATA ? "journal":
+ test_opt(sb,DATA_FLAGS) == EXT3_MOUNT_ORDERED_DATA ? "ordered":
+ "writeback");
+
+ return sb;
+
+failed_mount3:
+ journal_destroy(sbi->s_journal);
+failed_mount2:
+ for (i = 0; i < db_count; i++)
+ brelse(sbi->s_group_desc[i]);
+ kfree(sbi->s_group_desc);
+failed_mount:
+ ext3_blkdev_remove(sbi);
+ brelse(bh);
+out_fail:
+ return NULL;
+}
+
+/*
+ * Setup any per-fs journal parameters now. We'll do this both on
+ * initial mount, once the journal has been initialised but before we've
+ * done any recovery; and again on any subsequent remount.
+ */
+static void ext3_init_journal_params(struct ext3_sb_info *sbi,
+ journal_t *journal)
+{
+ if (sbi->s_commit_interval)
+ journal->j_commit_interval = sbi->s_commit_interval;
+ /* We could also set up an ext3-specific default for the commit
+ * interval here, but for now we'll just fall back to the jbd
+ * default. */
+}
+
+
+static journal_t *ext3_get_journal(struct super_block *sb, int journal_inum)
+{
+ struct inode *journal_inode;
+ journal_t *journal;
+
+ /* First, test for the existence of a valid inode on disk. Bad
+ * things happen if we iget() an unused inode, as the subsequent
+ * iput() will try to delete it. */
+
+ journal_inode = iget(sb, journal_inum);
+ if (!journal_inode) {
+ printk(KERN_ERR "EXT3-fs: no journal found.\n");
+ return NULL;
+ }
+ if (!journal_inode->i_nlink) {
+ make_bad_inode(journal_inode);
+ iput(journal_inode);
+ printk(KERN_ERR "EXT3-fs: journal inode is deleted.\n");
+ return NULL;
+ }
+
+ jbd_debug(2, "Journal inode found at %p: %Ld bytes\n",
+ journal_inode, journal_inode->i_size);
+ if (is_bad_inode(journal_inode) || !S_ISREG(journal_inode->i_mode)) {
+ printk(KERN_ERR "EXT3-fs: invalid journal inode.\n");
+ iput(journal_inode);
+ return NULL;
+ }
+
+ journal = journal_init_inode(journal_inode);
+ if (!journal) {
+ printk(KERN_ERR "EXT3-fs: Could not load journal inode\n");
+ iput(journal_inode);
+ return NULL;
+ }
+ ext3_init_journal_params(EXT3_SB(sb), journal);
+ return journal;
+}
+
+static journal_t *ext3_get_dev_journal(struct super_block *sb,
+ int dev)
+{
+ struct buffer_head * bh;
+ journal_t *journal;
+ int start;
+ int len;
+ int hblock, blocksize;
+ unsigned long sb_block;
+ unsigned long offset;
+ kdev_t journal_dev = to_kdev_t(dev);
+ struct ext3_super_block * es;
+ struct block_device *bdev;
+
+ bdev = ext3_blkdev_get(journal_dev);
+ if (bdev == NULL)
+ return NULL;
+
+ blocksize = sb->s_blocksize;
+ hblock = get_hardsect_size(journal_dev);
+ if (blocksize < hblock) {
+ printk(KERN_ERR
+ "EXT3-fs: blocksize too small for journal device.\n");
+ goto out_bdev;
+ }
+
+ sb_block = EXT3_MIN_BLOCK_SIZE / blocksize;
+ offset = EXT3_MIN_BLOCK_SIZE % blocksize;
+ set_blocksize(dev, blocksize);
+ if (!(bh = bread(dev, sb_block, blocksize))) {
+ printk(KERN_ERR "EXT3-fs: couldn't read superblock of "
+ "external journal\n");
+ goto out_bdev;
+ }
+
+ es = (struct ext3_super_block *) (((char *)bh->b_data) + offset);
+ if ((le16_to_cpu(es->s_magic) != EXT3_SUPER_MAGIC) ||
+ !(le32_to_cpu(es->s_feature_incompat) &
+ EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) {
+ printk(KERN_ERR "EXT3-fs: external journal has "
+ "bad superblock\n");
+ brelse(bh);
+ goto out_bdev;
+ }
+
+ if (memcmp(EXT3_SB(sb)->s_es->s_journal_uuid, es->s_uuid, 16)) {
+ printk(KERN_ERR "EXT3-fs: journal UUID does not match\n");
+ brelse(bh);
+ goto out_bdev;
+ }
+
+ len = le32_to_cpu(es->s_blocks_count);
+ start = sb_block + 1;
+ brelse(bh); /* we're done with the superblock */
+
+ journal = journal_init_dev(journal_dev, sb->s_dev,
+ start, len, blocksize);
+ if (!journal) {
+ printk(KERN_ERR "EXT3-fs: failed to create device journal\n");
+ goto out_bdev;
+ }
+ ll_rw_block(READ, 1, &journal->j_sb_buffer);
+ wait_on_buffer(journal->j_sb_buffer);
+ if (!buffer_uptodate(journal->j_sb_buffer)) {
+ printk(KERN_ERR "EXT3-fs: I/O error on journal device\n");
+ goto out_journal;
+ }
+ if (ntohl(journal->j_superblock->s_nr_users) != 1) {
+ printk(KERN_ERR "EXT3-fs: External journal has more than one "
+ "user (unsupported) - %d\n",
+ ntohl(journal->j_superblock->s_nr_users));
+ goto out_journal;
+ }
+ EXT3_SB(sb)->journal_bdev = bdev;
+ ext3_init_journal_params(EXT3_SB(sb), journal);
+ return journal;
+out_journal:
+ journal_destroy(journal);
+out_bdev:
+ ext3_blkdev_put(bdev);
+ return NULL;
+}
+
+static int ext3_load_journal(struct super_block * sb,
+ struct ext3_super_block * es)
+{
+ journal_t *journal;
+ int journal_inum = le32_to_cpu(es->s_journal_inum);
+ int journal_dev = le32_to_cpu(es->s_journal_dev);
+ int err = 0;
+ int really_read_only;
+
+ really_read_only = is_read_only(sb->s_dev);
+
+ /*
+ * Are we loading a blank journal or performing recovery after a
+ * crash? For recovery, we need to check in advance whether we
+ * can get read-write access to the device.
+ */
+
+ if (EXT3_HAS_INCOMPAT_FEATURE(sb, EXT3_FEATURE_INCOMPAT_RECOVER)) {
+ if (sb->s_flags & MS_RDONLY) {
+ printk(KERN_INFO "EXT3-fs: INFO: recovery "
+ "required on readonly filesystem.\n");
+ if (really_read_only) {
+ printk(KERN_ERR "EXT3-fs: write access "
+ "unavailable, cannot proceed.\n");
+ return -EROFS;
+ }
+ printk (KERN_INFO "EXT3-fs: write access will "
+ "be enabled during recovery.\n");
+ }
+ }
+
+ if (journal_inum && journal_dev) {
+ printk(KERN_ERR "EXT3-fs: filesystem has both journal "
+ "and inode journals!\n");
+ return -EINVAL;
+ }
+
+ if (journal_inum) {
+ if (!(journal = ext3_get_journal(sb, journal_inum)))
+ return -EINVAL;
+ } else {
+ if (!(journal = ext3_get_dev_journal(sb, journal_dev)))
+ return -EINVAL;
+ }
+
+
+ if (!really_read_only && test_opt(sb, UPDATE_JOURNAL)) {
+ err = journal_update_format(journal);
+ if (err) {
+ printk(KERN_ERR "EXT3-fs: error updating journal.\n");
+ journal_destroy(journal);
+ return err;
+ }
+ }
+
+ if (!EXT3_HAS_INCOMPAT_FEATURE(sb, EXT3_FEATURE_INCOMPAT_RECOVER))
+ err = journal_wipe(journal, !really_read_only);
+ if (!err)
+ err = journal_load(journal);
+
+ if (err) {
+ printk(KERN_ERR "EXT3-fs: error loading journal.\n");
+ journal_destroy(journal);
+ return err;
+ }
+
+ EXT3_SB(sb)->s_journal = journal;
+ ext3_clear_journal_err(sb, es);
+ return 0;
+}
+
+static int ext3_create_journal(struct super_block * sb,
+ struct ext3_super_block * es,
+ int journal_inum)
+{
+ journal_t *journal;
+
+ if (sb->s_flags & MS_RDONLY) {
+ printk(KERN_ERR "EXT3-fs: readonly filesystem when trying to "
+ "create journal.\n");
+ return -EROFS;
+ }
+
+ if (!(journal = ext3_get_journal(sb, journal_inum)))
+ return -EINVAL;
+
+ printk(KERN_INFO "EXT3-fs: creating new journal on inode %d\n",
+ journal_inum);
+
+ if (journal_create(journal)) {
+ printk(KERN_ERR "EXT3-fs: error creating journal.\n");
+ journal_destroy(journal);
+ return -EIO;
+ }
+
+ EXT3_SB(sb)->s_journal = journal;
+
+ ext3_update_dynamic_rev(sb);
+ EXT3_SET_INCOMPAT_FEATURE(sb, EXT3_FEATURE_INCOMPAT_RECOVER);
+ EXT3_SET_COMPAT_FEATURE(sb, EXT3_FEATURE_COMPAT_HAS_JOURNAL);
+
+ es->s_journal_inum = cpu_to_le32(journal_inum);
+ sb->s_dirt = 1;
+
+ /* Make sure we flush the recovery flag to disk. */
+ ext3_commit_super(sb, es, 1);
+
+ return 0;
+}
+
+static void ext3_commit_super (struct super_block * sb,
+ struct ext3_super_block * es,
+ int sync)
+{
+ es->s_wtime = cpu_to_le32(CURRENT_TIME);
+ BUFFER_TRACE(sb->u.ext3_sb.s_sbh, "marking dirty");
+ mark_buffer_dirty(sb->u.ext3_sb.s_sbh);
+ if (sync) {
+ ll_rw_block(WRITE, 1, &sb->u.ext3_sb.s_sbh);
+ wait_on_buffer(sb->u.ext3_sb.s_sbh);
+ }
+}
+
+
+/*
+ * Have we just finished recovery? If so, and if we are mounting (or
+ * remounting) the filesystem readonly, then we will end up with a
+ * consistent fs on disk. Record that fact.
+ */
+static void ext3_mark_recovery_complete(struct super_block * sb,
+ struct ext3_super_block * es)
+{
+ journal_flush(EXT3_SB(sb)->s_journal);
+ if (EXT3_HAS_INCOMPAT_FEATURE(sb, EXT3_FEATURE_INCOMPAT_RECOVER) &&
+ sb->s_flags & MS_RDONLY) {
+ EXT3_CLEAR_INCOMPAT_FEATURE(sb, EXT3_FEATURE_INCOMPAT_RECOVER);
+ sb->s_dirt = 0;
+ ext3_commit_super(sb, es, 1);
+ }
+}
+
+/*
+ * If we are mounting (or read-write remounting) a filesystem whose journal
+ * has recorded an error from a previous lifetime, move that error to the
+ * main filesystem now.
+ */
+static void ext3_clear_journal_err(struct super_block * sb,
+ struct ext3_super_block * es)
+{
+ journal_t *journal;
+ int j_errno;
+ const char *errstr;
+
+ journal = EXT3_SB(sb)->s_journal;
+
+ /*
+ * Now check for any error status which may have been recorded in the
+ * journal by a prior ext3_error() or ext3_abort()
+ */
+
+ j_errno = journal_errno(journal);
+ if (j_errno) {
+ char nbuf[16];
+
+ errstr = ext3_decode_error(sb, j_errno, nbuf);
+ ext3_warning(sb, __FUNCTION__, "Filesystem error recorded "
+ "from previous mount: %s", errstr);
+ ext3_warning(sb, __FUNCTION__, "Marking fs in need of "
+ "filesystem check.");
+
+ sb->u.ext3_sb.s_mount_state |= EXT3_ERROR_FS;
+ es->s_state |= cpu_to_le16(EXT3_ERROR_FS);
+ ext3_commit_super (sb, es, 1);
+
+ journal_clear_err(journal);
+ }
+}
+
+/*
+ * Force the running and committing transactions to commit,
+ * and wait on the commit.
+ */
+int ext3_force_commit(struct super_block *sb)
+{
+ journal_t *journal;
+ int ret;
+
+ if (sb->s_flags & MS_RDONLY)
+ return 0;
+
+ journal = EXT3_SB(sb)->s_journal;
+ sb->s_dirt = 0;
+ lock_kernel(); /* important: lock down j_running_transaction */
+ ret = ext3_journal_force_commit(journal);
+ unlock_kernel();
+ return ret;
+}
+
+/*
+ * Ext3 always journals updates to the superblock itself, so we don't
+ * have to propagate any other updates to the superblock on disk at this
+ * point. Just start an async writeback to get the buffers on their way
+ * to the disk.
+ *
+ * This implicitly triggers the writebehind on sync().
+ */
+
+void ext3_write_super (struct super_block * sb)
+{
+ if (down_trylock(&sb->s_lock) == 0)
+ BUG();
+ sb->s_dirt = 0;
+ log_start_commit(EXT3_SB(sb)->s_journal, NULL);
+}
+
+static int ext3_sync_fs(struct super_block *sb)
+{
+ tid_t target;
+
+ sb->s_dirt = 0;
+ target = log_start_commit(EXT3_SB(sb)->s_journal, NULL);
+ log_wait_commit(EXT3_SB(sb)->s_journal, target);
+ return 0;
+}
+
+/*
+ * LVM calls this function before a (read-only) snapshot is created. This
+ * gives us a chance to flush the journal completely and mark the fs clean.
+ */
+void ext3_write_super_lockfs(struct super_block *sb)
+{
+ sb->s_dirt = 0;
+
+ lock_kernel(); /* 2.4.5 forgot to do this for us */
+ if (!(sb->s_flags & MS_RDONLY)) {
+ journal_t *journal = EXT3_SB(sb)->s_journal;
+
+ /* Now we set up the journal barrier. */
+ unlock_super(sb);
+ journal_lock_updates(journal);
+ journal_flush(journal);
+ lock_super(sb);
+
+ /* Journal blocked and flushed, clear needs_recovery flag. */
+ EXT3_CLEAR_INCOMPAT_FEATURE(sb, EXT3_FEATURE_INCOMPAT_RECOVER);
+ ext3_commit_super(sb, EXT3_SB(sb)->s_es, 1);
+ }
+ unlock_kernel();
+}
+
+/*
+ * Called by LVM after the snapshot is done. We need to reset the RECOVER
+ * flag here, even though the filesystem is not technically dirty yet.
+ */
+void ext3_unlockfs(struct super_block *sb)
+{
+ if (!(sb->s_flags & MS_RDONLY)) {
+ lock_kernel();
+ lock_super(sb);
+ /* Reser the needs_recovery flag before the fs is unlocked. */
+ EXT3_SET_INCOMPAT_FEATURE(sb, EXT3_FEATURE_INCOMPAT_RECOVER);
+ ext3_commit_super(sb, EXT3_SB(sb)->s_es, 1);
+ unlock_super(sb);
+ journal_unlock_updates(EXT3_SB(sb)->s_journal);
+ unlock_kernel();
+ }
+}
+
+int ext3_remount (struct super_block * sb, int * flags, char * data)
+{
+ struct ext3_super_block * es;
+ struct ext3_sb_info *sbi = EXT3_SB(sb);
+ unsigned long tmp;
+
+ clear_ro_after(sb);
+
+ /*
+ * Allow the "check" option to be passed as a remount option.
+ */
+ if (!parse_options(data, &tmp, sbi, &tmp, 1))
+ return -EINVAL;
+
+ if (sbi->s_mount_opt & EXT3_MOUNT_ABORT)
+ ext3_abort(sb, __FUNCTION__, "Abort forced by user");
+
+ es = sbi->s_es;
+
+ ext3_init_journal_params(sbi, sbi->s_journal);
+
+ if ((*flags & MS_RDONLY) != (sb->s_flags & MS_RDONLY)) {
+ if (sbi->s_mount_opt & EXT3_MOUNT_ABORT)
+ return -EROFS;
+
+ if (*flags & MS_RDONLY) {
+ /*
+ * First of all, the unconditional stuff we have to do
+ * to disable replay of the journal when we next remount
+ */
+ sb->s_flags |= MS_RDONLY;
+
+ /*
+ * OK, test if we are remounting a valid rw partition
+ * readonly, and if so set the rdonly flag and then
+ * mark the partition as valid again.
+ */
+ if (!(es->s_state & cpu_to_le16(EXT3_VALID_FS)) &&
+ (sbi->s_mount_state & EXT3_VALID_FS))
+ es->s_state = cpu_to_le16(sbi->s_mount_state);
+
+ ext3_mark_recovery_complete(sb, es);
+ } else {
+ int ret;
+ if ((ret = EXT3_HAS_RO_COMPAT_FEATURE(sb,
+ ~EXT3_FEATURE_RO_COMPAT_SUPP))) {
+ printk(KERN_WARNING "EXT3-fs: %s: couldn't "
+ "remount RDWR because of unsupported "
+ "optional features (%x).\n",
+ bdevname(sb->s_dev), ret);
+ return -EROFS;
+ }
+ /*
+ * Mounting a RDONLY partition read-write, so reread
+ * and store the current valid flag. (It may have
+ * been changed by e2fsck since we originally mounted
+ * the partition.)
+ */
+ ext3_clear_journal_err(sb, es);
+ sbi->s_mount_state = le16_to_cpu(es->s_state);
+ if (!ext3_setup_super (sb, es, 0))
+ sb->s_flags &= ~MS_RDONLY;
+ }
+ }
+ setup_ro_after(sb);
+ return 0;
+}
+
+int ext3_statfs (struct super_block * sb, struct statfs * buf)
+{
+ struct ext3_super_block *es = EXT3_SB(sb)->s_es;
+ unsigned long overhead;
+ int i;
+
+ if (test_opt (sb, MINIX_DF))
+ overhead = 0;
+ else {
+ /*
+ * Compute the overhead (FS structures)
+ */
+
+ /*
+ * All of the blocks before first_data_block are
+ * overhead
+ */
+ overhead = le32_to_cpu(es->s_first_data_block);
+
+ /*
+ * Add the overhead attributed to the superblock and
+ * block group descriptors. If the sparse superblocks
+ * feature is turned on, then not all groups have this.
+ */
+ for (i = 0; i < EXT3_SB(sb)->s_groups_count; i++)
+ overhead += ext3_bg_has_super(sb, i) +
+ ext3_bg_num_gdb(sb, i);
+
+ /*
+ * Every block group has an inode bitmap, a block
+ * bitmap, and an inode table.
+ */
+ overhead += (EXT3_SB(sb)->s_groups_count *
+ (2 + EXT3_SB(sb)->s_itb_per_group));
+ }
+
+ buf->f_type = EXT3_SUPER_MAGIC;
+ buf->f_bsize = sb->s_blocksize;
+ buf->f_blocks = le32_to_cpu(es->s_blocks_count) - overhead;
+ buf->f_bfree = ext3_count_free_blocks (sb);
+ buf->f_bavail = buf->f_bfree - le32_to_cpu(es->s_r_blocks_count);
+ if (buf->f_bfree < le32_to_cpu(es->s_r_blocks_count))
+ buf->f_bavail = 0;
+ buf->f_files = le32_to_cpu(es->s_inodes_count);
+ buf->f_ffree = ext3_count_free_inodes (sb);
+ buf->f_namelen = EXT3_NAME_LEN;
+ return 0;
+}
+
+/* Helper function for writing quotas on sync - we need to start transaction before quota file
+ * is locked for write. Otherwise the are possible deadlocks:
+ * Process 1 Process 2
+ * ext3_create() quota_sync()
+ * journal_start() write_dquot()
+ * DQUOT_INIT() down(dqio_sem)
+ * down(dqio_sem) journal_start()
+ *
+ */
+
+#ifdef CONFIG_QUOTA
+
+static int (*old_write_dquot)(struct dquot *dquot);
+
+/* Blocks: (2 data blocks) * (3 indirect + 1 descriptor + 1 bitmap) + superblock */
+#define EXT3_OLD_QFMT_BLOCKS 11
+/* Blocks: quota info + (4 pointer blocks + 1 entry block) * (3 indirect + 1 descriptor + 1 bitmap) + superblock */
+#define EXT3_V0_QFMT_BLOCKS 27
+
+static int ext3_write_dquot(struct dquot *dquot)
+{
+ int nblocks, ret;
+ handle_t *handle;
+ struct quota_info *dqops = sb_dqopt(dquot->dq_sb);
+ struct inode *qinode;
+
+ switch (dqops->info[dquot->dq_type].dqi_format->qf_fmt_id) {
+ case QFMT_VFS_OLD:
+ nblocks = EXT3_OLD_QFMT_BLOCKS;
+ break;
+ case QFMT_VFS_V0:
+ nblocks = EXT3_V0_QFMT_BLOCKS;
+ break;
+ default:
+ nblocks = EXT3_MAX_TRANS_DATA;
+ }
+ lock_kernel();
+ qinode = dqops->files[dquot->dq_type]->f_dentry->d_inode;
+ handle = ext3_journal_start(qinode, nblocks);
+ if (IS_ERR(handle)) {
+ unlock_kernel();
+ return PTR_ERR(handle);
+ }
+ unlock_kernel();
+ ret = old_write_dquot(dquot);
+ lock_kernel();
+ ret = ext3_journal_stop(handle, qinode);
+ unlock_kernel();
+ return ret;
+}
+#endif
+
+static DECLARE_FSTYPE_DEV(ext3_fs_type, "ext3", ext3_read_super);
+
+static int __init init_ext3_fs(void)
+{
+#ifdef CONFIG_QUOTA
+ init_dquot_operations(&ext3_qops);
+ old_write_dquot = ext3_qops.write_dquot;
+ ext3_qops.write_dquot = ext3_write_dquot;
+#endif
+ return register_filesystem(&ext3_fs_type);
+}
+
+static void __exit exit_ext3_fs(void)
+{
+ unregister_filesystem(&ext3_fs_type);
+}
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Remy Card, Stephen Tweedie, Andrew Morton, Andreas Dilger, Theodore Ts'o and others");
+MODULE_DESCRIPTION("Second Extended Filesystem with journaling extensions");
+MODULE_LICENSE("GPL");
+module_init(init_ext3_fs)
+module_exit(exit_ext3_fs)
diff --git a/fs/ext3/symlink.c b/fs/ext3/symlink.c
new file mode 100644
index 00000000000000..87f91adbd2acbd
--- /dev/null
+++ b/fs/ext3/symlink.c
@@ -0,0 +1,39 @@
+/*
+ * linux/fs/ext3/symlink.c
+ *
+ * Only fast symlinks left here - the rest is done by generic code. AV, 1999
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * from
+ *
+ * linux/fs/minix/symlink.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * ext3 symlink handling code
+ */
+
+#include <linux/fs.h>
+#include <linux/jbd.h>
+#include <linux/ext3_fs.h>
+
+static int ext3_readlink(struct dentry *dentry, char *buffer, int buflen)
+{
+ char *s = (char *)dentry->d_inode->u.ext3_i.i_data;
+ return vfs_readlink(dentry, buffer, buflen, s);
+}
+
+static int ext3_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+ char *s = (char *)dentry->d_inode->u.ext3_i.i_data;
+ return vfs_follow_link(nd, s);
+}
+
+struct inode_operations ext3_fast_symlink_inode_operations = {
+ readlink: ext3_readlink, /* BKL not held. Don't need */
+ follow_link: ext3_follow_link, /* BKL not held. Don't need */
+};
diff --git a/fs/fat/Makefile b/fs/fat/Makefile
new file mode 100644
index 00000000000000..d1e503117bafcb
--- /dev/null
+++ b/fs/fat/Makefile
@@ -0,0 +1,17 @@
+#
+# Makefile for the Linux fat filesystem support.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile.
+
+O_TARGET := fat.o
+
+export-objs := fatfs_syms.o
+
+obj-y := buffer.o cache.o dir.o file.o inode.o misc.o cvf.o fatfs_syms.o
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/fat/buffer.c b/fs/fat/buffer.c
new file mode 100644
index 00000000000000..117d85b33ae7e7
--- /dev/null
+++ b/fs/fat/buffer.c
@@ -0,0 +1,102 @@
+/*
+ * linux/fs/fat/buffer.c
+ *
+ *
+ */
+
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/blkdev.h>
+#include <linux/msdos_fs.h>
+#include <linux/fat_cvf.h>
+
+#if 0
+# define PRINTK(x) printk x
+#else
+# define PRINTK(x)
+#endif
+
+struct buffer_head *fat_bread(struct super_block *sb, int block)
+{
+ return MSDOS_SB(sb)->cvf_format->cvf_bread(sb,block);
+}
+struct buffer_head *fat_getblk(struct super_block *sb, int block)
+{
+ return MSDOS_SB(sb)->cvf_format->cvf_getblk(sb,block);
+}
+void fat_brelse (struct super_block *sb, struct buffer_head *bh)
+{
+ if (bh)
+ MSDOS_SB(sb)->cvf_format->cvf_brelse(sb,bh);
+}
+void fat_mark_buffer_dirty (
+ struct super_block *sb,
+ struct buffer_head *bh)
+{
+ MSDOS_SB(sb)->cvf_format->cvf_mark_buffer_dirty(sb,bh);
+}
+void fat_set_uptodate (
+ struct super_block *sb,
+ struct buffer_head *bh,
+ int val)
+{
+ MSDOS_SB(sb)->cvf_format->cvf_set_uptodate(sb,bh,val);
+}
+int fat_is_uptodate(struct super_block *sb, struct buffer_head *bh)
+{
+ return MSDOS_SB(sb)->cvf_format->cvf_is_uptodate(sb,bh);
+}
+void fat_ll_rw_block (
+ struct super_block *sb,
+ int opr,
+ int nbreq,
+ struct buffer_head *bh[32])
+{
+ MSDOS_SB(sb)->cvf_format->cvf_ll_rw_block(sb,opr,nbreq,bh);
+}
+
+struct buffer_head *default_fat_bread(struct super_block *sb, int block)
+{
+ return sb_bread(sb, block);
+}
+
+struct buffer_head *default_fat_getblk(struct super_block *sb, int block)
+{
+ return sb_getblk(sb, block);
+}
+
+void default_fat_brelse(struct super_block *sb, struct buffer_head *bh)
+{
+ brelse (bh);
+}
+
+void default_fat_mark_buffer_dirty (
+ struct super_block *sb,
+ struct buffer_head *bh)
+{
+ mark_buffer_dirty (bh);
+}
+
+void default_fat_set_uptodate (
+ struct super_block *sb,
+ struct buffer_head *bh,
+ int val)
+{
+ mark_buffer_uptodate(bh, val);
+}
+
+int default_fat_is_uptodate (struct super_block *sb, struct buffer_head *bh)
+{
+ return buffer_uptodate(bh);
+}
+
+void default_fat_ll_rw_block (
+ struct super_block *sb,
+ int opr,
+ int nbreq,
+ struct buffer_head *bh[32])
+{
+ ll_rw_block(opr,nbreq,bh);
+}
diff --git a/fs/fat/cache.c b/fs/fat/cache.c
new file mode 100644
index 00000000000000..7240bc8a2e52e1
--- /dev/null
+++ b/fs/fat/cache.c
@@ -0,0 +1,362 @@
+/*
+ * linux/fs/fat/cache.c
+ *
+ * Written 1992,1993 by Werner Almesberger
+ *
+ * Mar 1999. AV. Changed cache, so that it uses the starting cluster instead
+ * of inode number.
+ * May 1999. AV. Fixed the bogosity with FAT32 (read "FAT28"). Fscking lusers.
+ */
+
+#include <linux/msdos_fs.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/fat_cvf.h>
+
+#if 0
+# define PRINTK(x) printk x
+#else
+# define PRINTK(x)
+#endif
+
+static struct fat_cache *fat_cache,cache[FAT_CACHE];
+static spinlock_t fat_cache_lock = SPIN_LOCK_UNLOCKED;
+
+/* Returns the this'th FAT entry, -1 if it is an end-of-file entry. If
+ new_value is != -1, that FAT entry is replaced by it. */
+
+int fat_access(struct super_block *sb,int nr,int new_value)
+{
+ return MSDOS_SB(sb)->cvf_format->fat_access(sb,nr,new_value);
+}
+
+int fat_bmap(struct inode *inode,int sector)
+{
+ return MSDOS_SB(inode->i_sb)->cvf_format->cvf_bmap(inode,sector);
+}
+
+int default_fat_access(struct super_block *sb,int nr,int new_value)
+{
+ struct buffer_head *bh, *bh2, *c_bh, *c_bh2;
+ unsigned char *p_first, *p_last;
+ int copy, first, last, next, b;
+
+ if ((unsigned) (nr-2) >= MSDOS_SB(sb)->clusters)
+ return 0;
+ if (MSDOS_SB(sb)->fat_bits == 32) {
+ first = last = nr*4;
+ } else if (MSDOS_SB(sb)->fat_bits == 16) {
+ first = last = nr*2;
+ } else {
+ first = nr*3/2;
+ last = first+1;
+ }
+ b = MSDOS_SB(sb)->fat_start + (first >> sb->s_blocksize_bits);
+ if (!(bh = fat_bread(sb, b))) {
+ printk("bread in fat_access failed\n");
+ return 0;
+ }
+ if ((first >> sb->s_blocksize_bits) == (last >> sb->s_blocksize_bits)) {
+ bh2 = bh;
+ } else {
+ if (!(bh2 = fat_bread(sb, b+1))) {
+ fat_brelse(sb, bh);
+ printk("2nd bread in fat_access failed\n");
+ return 0;
+ }
+ }
+ if (MSDOS_SB(sb)->fat_bits == 32) {
+ p_first = p_last = NULL; /* GCC needs that stuff */
+ next = CF_LE_L(((__u32 *) bh->b_data)[(first &
+ (sb->s_blocksize - 1)) >> 2]);
+ /* Fscking Microsoft marketing department. Their "32" is 28. */
+ next &= 0xfffffff;
+ if (next >= 0xffffff7) next = -1;
+ PRINTK(("fat_bread: 0x%x, nr=0x%x, first=0x%x, next=0x%x\n", b, nr, first, next));
+
+ } else if (MSDOS_SB(sb)->fat_bits == 16) {
+ p_first = p_last = NULL; /* GCC needs that stuff */
+ next = CF_LE_W(((__u16 *) bh->b_data)[(first &
+ (sb->s_blocksize - 1)) >> 1]);
+ if (next >= 0xfff7) next = -1;
+ } else {
+ p_first = &((__u8 *)bh->b_data)[first & (sb->s_blocksize - 1)];
+ p_last = &((__u8 *)bh2->b_data)[(first + 1) & (sb->s_blocksize - 1)];
+ if (nr & 1) next = ((*p_first >> 4) | (*p_last << 4)) & 0xfff;
+ else next = (*p_first+(*p_last << 8)) & 0xfff;
+ if (next >= 0xff7) next = -1;
+ }
+ if (new_value != -1) {
+ if (MSDOS_SB(sb)->fat_bits == 32) {
+ ((__u32 *)bh->b_data)[(first & (sb->s_blocksize - 1)) >> 2]
+ = CT_LE_L(new_value);
+ } else if (MSDOS_SB(sb)->fat_bits == 16) {
+ ((__u16 *)bh->b_data)[(first & (sb->s_blocksize - 1)) >> 1]
+ = CT_LE_W(new_value);
+ } else {
+ if (nr & 1) {
+ *p_first = (*p_first & 0xf) | (new_value << 4);
+ *p_last = new_value >> 4;
+ }
+ else {
+ *p_first = new_value & 0xff;
+ *p_last = (*p_last & 0xf0) | (new_value >> 8);
+ }
+ fat_mark_buffer_dirty(sb, bh2);
+ }
+ fat_mark_buffer_dirty(sb, bh);
+ for (copy = 1; copy < MSDOS_SB(sb)->fats; copy++) {
+ b = MSDOS_SB(sb)->fat_start + (first >> sb->s_blocksize_bits)
+ + MSDOS_SB(sb)->fat_length * copy;
+ if (!(c_bh = fat_bread(sb, b)))
+ break;
+ if (bh != bh2) {
+ if (!(c_bh2 = fat_bread(sb, b+1))) {
+ fat_brelse(sb, c_bh);
+ break;
+ }
+ memcpy(c_bh2->b_data, bh2->b_data, sb->s_blocksize);
+ fat_mark_buffer_dirty(sb, c_bh2);
+ fat_brelse(sb, c_bh2);
+ }
+ memcpy(c_bh->b_data, bh->b_data, sb->s_blocksize);
+ fat_mark_buffer_dirty(sb, c_bh);
+ fat_brelse(sb, c_bh);
+ }
+ }
+ fat_brelse(sb, bh);
+ if (bh != bh2)
+ fat_brelse(sb, bh2);
+ return next;
+}
+
+void fat_cache_init(void)
+{
+ static int initialized = 0;
+ int count;
+
+ spin_lock(&fat_cache_lock);
+ if (initialized) {
+ spin_unlock(&fat_cache_lock);
+ return;
+ }
+ fat_cache = &cache[0];
+ for (count = 0; count < FAT_CACHE; count++) {
+ cache[count].device = 0;
+ cache[count].next = count == FAT_CACHE-1 ? NULL :
+ &cache[count+1];
+ }
+ initialized = 1;
+ spin_unlock(&fat_cache_lock);
+}
+
+
+void fat_cache_lookup(struct inode *inode,int cluster,int *f_clu,int *d_clu)
+{
+ struct fat_cache *walk;
+ int first = MSDOS_I(inode)->i_start;
+
+ if (!first)
+ return;
+ spin_lock(&fat_cache_lock);
+ for (walk = fat_cache; walk; walk = walk->next)
+ if (inode->i_dev == walk->device
+ && walk->start_cluster == first
+ && walk->file_cluster <= cluster
+ && walk->file_cluster > *f_clu) {
+ *d_clu = walk->disk_cluster;
+#ifdef DEBUG
+printk("cache hit: %d (%d)\n",walk->file_cluster,*d_clu);
+#endif
+ if ((*f_clu = walk->file_cluster) == cluster) {
+ spin_unlock(&fat_cache_lock);
+ return;
+ }
+ }
+ spin_unlock(&fat_cache_lock);
+#ifdef DEBUG
+printk("cache miss\n");
+#endif
+}
+
+
+#ifdef DEBUG
+static void list_cache(void)
+{
+ struct fat_cache *walk;
+
+ for (walk = fat_cache; walk; walk = walk->next) {
+ if (walk->device)
+ printk("<%s,%d>(%d,%d) ", kdevname(walk->device),
+ walk->start_cluster, walk->file_cluster,
+ walk->disk_cluster);
+ else printk("-- ");
+ }
+ printk("\n");
+}
+#endif
+
+
+void fat_cache_add(struct inode *inode,int f_clu,int d_clu)
+{
+ struct fat_cache *walk,*last;
+ int first = MSDOS_I(inode)->i_start;
+
+ last = NULL;
+ spin_lock(&fat_cache_lock);
+ for (walk = fat_cache; walk->next; walk = (last = walk)->next)
+ if (inode->i_dev == walk->device
+ && walk->start_cluster == first
+ && walk->file_cluster == f_clu) {
+ if (walk->disk_cluster != d_clu) {
+ printk("FAT cache corruption inode=%ld\n",
+ inode->i_ino);
+ spin_unlock(&fat_cache_lock);
+ fat_cache_inval_inode(inode);
+ return;
+ }
+ /* update LRU */
+ if (last == NULL) {
+ spin_unlock(&fat_cache_lock);
+ return;
+ }
+ last->next = walk->next;
+ walk->next = fat_cache;
+ fat_cache = walk;
+#ifdef DEBUG
+list_cache();
+#endif
+ spin_unlock(&fat_cache_lock);
+ return;
+ }
+ walk->device = inode->i_dev;
+ walk->start_cluster = first;
+ walk->file_cluster = f_clu;
+ walk->disk_cluster = d_clu;
+ last->next = NULL;
+ walk->next = fat_cache;
+ fat_cache = walk;
+ spin_unlock(&fat_cache_lock);
+#ifdef DEBUG
+list_cache();
+#endif
+}
+
+
+/* Cache invalidation occurs rarely, thus the LRU chain is not updated. It
+ fixes itself after a while. */
+
+void fat_cache_inval_inode(struct inode *inode)
+{
+ struct fat_cache *walk;
+ int first = MSDOS_I(inode)->i_start;
+
+ spin_lock(&fat_cache_lock);
+ for (walk = fat_cache; walk; walk = walk->next)
+ if (walk->device == inode->i_dev
+ && walk->start_cluster == first)
+ walk->device = 0;
+ spin_unlock(&fat_cache_lock);
+}
+
+
+void fat_cache_inval_dev(kdev_t device)
+{
+ struct fat_cache *walk;
+
+ spin_lock(&fat_cache_lock);
+ for (walk = fat_cache; walk; walk = walk->next)
+ if (walk->device == device)
+ walk->device = 0;
+ spin_unlock(&fat_cache_lock);
+}
+
+
+int fat_get_cluster(struct inode *inode,int cluster)
+{
+ int nr,count;
+
+ if (!(nr = MSDOS_I(inode)->i_start)) return 0;
+ if (!cluster) return nr;
+ count = 0;
+ for (fat_cache_lookup(inode,cluster,&count,&nr); count < cluster;
+ count++) {
+ if ((nr = fat_access(inode->i_sb,nr,-1)) == -1) return 0;
+ if (!nr) return 0;
+ }
+ fat_cache_add(inode,cluster,nr);
+ return nr;
+}
+
+int default_fat_bmap(struct inode *inode,int sector)
+{
+ struct super_block *sb = inode->i_sb;
+ struct msdos_sb_info *sbi = MSDOS_SB(sb);
+ int cluster, offset, last_block;
+
+ if ((sbi->fat_bits != 32) &&
+ (inode->i_ino == MSDOS_ROOT_INO || (S_ISDIR(inode->i_mode) &&
+ !MSDOS_I(inode)->i_start))) {
+ if (sector >= sbi->dir_entries >> sbi->dir_per_block_bits)
+ return 0;
+ return sector + sbi->dir_start;
+ }
+ last_block = (MSDOS_I(inode)->mmu_private + (sb->s_blocksize - 1))
+ >> sb->s_blocksize_bits;
+ if (sector >= last_block)
+ return 0;
+
+ cluster = sector / sbi->cluster_size;
+ offset = sector % sbi->cluster_size;
+ if (!(cluster = fat_get_cluster(inode, cluster)))
+ return 0;
+
+ return (cluster - 2) * sbi->cluster_size + sbi->data_start + offset;
+}
+
+
+/* Free all clusters after the skip'th cluster. Doesn't use the cache,
+ because this way we get an additional sanity check. */
+
+int fat_free(struct inode *inode,int skip)
+{
+ int nr,last;
+
+ if (!(nr = MSDOS_I(inode)->i_start)) return 0;
+ last = 0;
+ while (skip--) {
+ last = nr;
+ if ((nr = fat_access(inode->i_sb,nr,-1)) == -1) return 0;
+ if (!nr) {
+ printk("fat_free: skipped EOF\n");
+ return -EIO;
+ }
+ }
+ if (last) {
+ fat_access(inode->i_sb,last,EOF_FAT(inode->i_sb));
+ fat_cache_inval_inode(inode);
+ } else {
+ fat_cache_inval_inode(inode);
+ MSDOS_I(inode)->i_start = 0;
+ MSDOS_I(inode)->i_logstart = 0;
+ mark_inode_dirty(inode);
+ }
+ lock_fat(inode->i_sb);
+ while (nr != -1) {
+ if (!(nr = fat_access(inode->i_sb,nr,0))) {
+ fat_fs_panic(inode->i_sb,"fat_free: deleting beyond EOF");
+ break;
+ }
+ if (MSDOS_SB(inode->i_sb)->free_clusters != -1) {
+ MSDOS_SB(inode->i_sb)->free_clusters++;
+ if (MSDOS_SB(inode->i_sb)->fat_bits == 32) {
+ fat_clusters_flush(inode->i_sb);
+ }
+ }
+ inode->i_blocks -= (1 << MSDOS_SB(inode->i_sb)->cluster_bits) / 512;
+ }
+ unlock_fat(inode->i_sb);
+ return 0;
+}
diff --git a/fs/fat/cvf.c b/fs/fat/cvf.c
new file mode 100644
index 00000000000000..37dd78c579431b
--- /dev/null
+++ b/fs/fat/cvf.c
@@ -0,0 +1,177 @@
+/*
+ * CVF extensions for fat-based filesystems
+ *
+ * written 1997,1998 by Frank Gockel <gockel@sent13.uni-duisburg.de>
+ *
+ * please do not remove the next line, dmsdos needs it for verifying patches
+ * CVF-FAT-VERSION-ID: 1.2.0
+ *
+ */
+
+#include<linux/sched.h>
+#include<linux/fs.h>
+#include<linux/msdos_fs.h>
+#include<linux/msdos_fs_sb.h>
+#include<linux/string.h>
+#include<linux/fat_cvf.h>
+#include<linux/config.h>
+#ifdef CONFIG_KMOD
+#include<linux/kmod.h>
+#endif
+
+#define MAX_CVF_FORMATS 3
+
+struct buffer_head *default_fat_bread(struct super_block *,int);
+struct buffer_head *default_fat_getblk(struct super_block *, int);
+void default_fat_brelse(struct super_block *, struct buffer_head *);
+void default_fat_mark_buffer_dirty (struct super_block *, struct buffer_head *);
+void default_fat_set_uptodate (struct super_block *, struct buffer_head *,int);
+int default_fat_is_uptodate(struct super_block *, struct buffer_head *);
+int default_fat_access(struct super_block *sb,int nr,int new_value);
+void default_fat_ll_rw_block (struct super_block *sb, int opr, int nbreq,
+ struct buffer_head *bh[32]);
+int default_fat_bmap(struct inode *inode,int block);
+ssize_t default_fat_file_write(struct file *filp, const char *buf,
+ size_t count, loff_t *ppos);
+
+struct cvf_format default_cvf = {
+ cvf_version: 0, /* version - who cares? */
+ cvf_version_text: "plain",
+ flags: 0, /* flags - who cares? */
+ cvf_bread: default_fat_bread,
+ cvf_getblk: default_fat_getblk,
+ cvf_brelse: default_fat_brelse,
+ cvf_mark_buffer_dirty: default_fat_mark_buffer_dirty,
+ cvf_set_uptodate: default_fat_set_uptodate,
+ cvf_is_uptodate: default_fat_is_uptodate,
+ cvf_ll_rw_block: default_fat_ll_rw_block,
+ fat_access: default_fat_access,
+ cvf_bmap: default_fat_bmap,
+ cvf_file_read: generic_file_read,
+ cvf_file_write: default_fat_file_write,
+};
+
+struct cvf_format *cvf_formats[MAX_CVF_FORMATS];
+int cvf_format_use_count[MAX_CVF_FORMATS];
+
+int register_cvf_format(struct cvf_format*cvf_format)
+{ int i,j;
+
+ for(i=0;i<MAX_CVF_FORMATS;++i)
+ { if(cvf_formats[i]==NULL)
+ { /* free slot found, now check version */
+ for(j=0;j<MAX_CVF_FORMATS;++j)
+ { if(cvf_formats[j])
+ { if(cvf_formats[j]->cvf_version==cvf_format->cvf_version)
+ { printk("register_cvf_format: version %d already registered\n",
+ cvf_format->cvf_version);
+ return -1;
+ }
+ }
+ }
+ cvf_formats[i]=cvf_format;
+ cvf_format_use_count[i]=0;
+ printk("CVF format %s (version id %d) successfully registered.\n",
+ cvf_format->cvf_version_text,cvf_format->cvf_version);
+ return 0;
+ }
+ }
+
+ printk("register_cvf_format: too many formats\n");
+ return -1;
+}
+
+int unregister_cvf_format(struct cvf_format*cvf_format)
+{ int i;
+
+ for(i=0;i<MAX_CVF_FORMATS;++i)
+ { if(cvf_formats[i])
+ { if(cvf_formats[i]->cvf_version==cvf_format->cvf_version)
+ { if(cvf_format_use_count[i])
+ { printk("unregister_cvf_format: format %d in use, cannot remove!\n",
+ cvf_formats[i]->cvf_version);
+ return -1;
+ }
+
+ printk("CVF format %s (version id %d) successfully unregistered.\n",
+ cvf_formats[i]->cvf_version_text,cvf_formats[i]->cvf_version);
+ cvf_formats[i]=NULL;
+ return 0;
+ }
+ }
+ }
+
+ printk("unregister_cvf_format: format %d is not registered\n",
+ cvf_format->cvf_version);
+ return -1;
+}
+
+void dec_cvf_format_use_count_by_version(int version)
+{ int i;
+
+ for(i=0;i<MAX_CVF_FORMATS;++i)
+ { if(cvf_formats[i])
+ { if(cvf_formats[i]->cvf_version==version)
+ { --cvf_format_use_count[i];
+ if(cvf_format_use_count[i]<0)
+ { cvf_format_use_count[i]=0;
+ printk(KERN_EMERG "FAT FS/CVF: This is a bug in cvf_version_use_count\n");
+ }
+ return;
+ }
+ }
+ }
+
+ printk("dec_cvf_format_use_count_by_version: version %d not found ???\n",
+ version);
+}
+
+int detect_cvf(struct super_block*sb,char*force)
+{ int i;
+ int found=0;
+ int found_i=-1;
+
+ if(force)
+ if(strcmp(force,"autoload")==0)
+ {
+#ifdef CONFIG_KMOD
+ request_module("cvf_autoload");
+ force=NULL;
+#else
+ printk("cannot autoload CVF modules: kmod support is not compiled into kernel\n");
+ return -1;
+#endif
+ }
+
+#ifdef CONFIG_KMOD
+ if(force)
+ if(*force)
+ request_module(force);
+#endif
+
+ if(force)
+ { if(*force)
+ { for(i=0;i<MAX_CVF_FORMATS;++i)
+ { if(cvf_formats[i])
+ { if(!strcmp(cvf_formats[i]->cvf_version_text,force))
+ return i;
+ }
+ }
+ printk("CVF format %s unknown (module not loaded?)\n",force);
+ return -1;
+ }
+ }
+
+ for(i=0;i<MAX_CVF_FORMATS;++i)
+ { if(cvf_formats[i])
+ { if(cvf_formats[i]->detect_cvf(sb))
+ { ++found;
+ found_i=i;
+ }
+ }
+ }
+
+ if(found==1)return found_i;
+ if(found>1)printk("CVF detection ambiguous, please use cvf_format=xxx option\n");
+ return -1;
+}
diff --git a/fs/fat/dir.c b/fs/fat/dir.c
new file mode 100644
index 00000000000000..08422f5a3227b5
--- /dev/null
+++ b/fs/fat/dir.c
@@ -0,0 +1,798 @@
+/*
+ * linux/fs/fat/dir.c
+ *
+ * directory handling functions for fat-based filesystems
+ *
+ * Written 1992,1993 by Werner Almesberger
+ *
+ * Hidden files 1995 by Albert Cahalan <albert@ccs.neu.edu> <adc@coe.neu.edu>
+ *
+ * VFAT extensions by Gordon Chaffee <chaffee@plateau.cs.berkeley.edu>
+ * Merged with msdos fs by Henrik Storner <storner@osiris.ping.dk>
+ * Rewritten for constant inumbers. Plugged buffer overrun in readdir(). AV
+ * Short name translation 1999, 2001 by Wolfram Pienkoss <wp@bszh.de>
+ */
+
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/nls.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/ioctl.h>
+#include <linux/dirent.h>
+#include <linux/mm.h>
+#include <linux/ctype.h>
+
+#include <asm/uaccess.h>
+
+#define PRINTK(X)
+
+struct file_operations fat_dir_operations = {
+ read: generic_read_dir,
+ readdir: fat_readdir,
+ ioctl: fat_dir_ioctl,
+ fsync: file_fsync,
+};
+
+/*
+ * Convert Unicode 16 to UTF8, translated Unicode, or ASCII.
+ * If uni_xlate is enabled and we can't get a 1:1 conversion, use a
+ * colon as an escape character since it is normally invalid on the vfat
+ * filesystem. The following four characters are the hexadecimal digits
+ * of Unicode value. This lets us do a full dump and restore of Unicode
+ * filenames. We could get into some trouble with long Unicode names,
+ * but ignore that right now.
+ * Ahem... Stack smashing in ring 0 isn't fun. Fixed.
+ */
+static int
+uni16_to_x8(unsigned char *ascii, wchar_t *uni, int uni_xlate,
+ struct nls_table *nls)
+{
+ wchar_t *ip, ec;
+ unsigned char *op, nc;
+ int charlen;
+ int k;
+
+ ip = uni;
+ op = ascii;
+
+ while (*ip) {
+ ec = *ip++;
+ if ( (charlen = nls->uni2char(ec, op, NLS_MAX_CHARSET_SIZE)) > 0) {
+ op += charlen;
+ } else {
+ if (uni_xlate == 1) {
+ *op = ':';
+ for (k = 4; k > 0; k--) {
+ nc = ec & 0xF;
+ op[k] = nc > 9 ? nc + ('a' - 10)
+ : nc + '0';
+ ec >>= 4;
+ }
+ op += 5;
+ } else {
+ *op++ = '?';
+ }
+ }
+ /* We have some slack there, so it's OK */
+ if (op>ascii+256) {
+ op = ascii + 256;
+ break;
+ }
+ }
+ *op = 0;
+ return (op - ascii);
+}
+
+#if 0
+static void dump_de(struct msdos_dir_entry *de)
+{
+ int i;
+ unsigned char *p = (unsigned char *) de;
+ printk("[");
+
+ for (i = 0; i < 32; i++, p++) {
+ printk("%02x ", *p);
+ }
+ printk("]\n");
+}
+#endif
+
+static inline unsigned char
+fat_tolower(struct nls_table *t, unsigned char c)
+{
+ unsigned char nc = t->charset2lower[c];
+
+ return nc ? nc : c;
+}
+
+static inline int
+fat_short2uni(struct nls_table *t, unsigned char *c, int clen, wchar_t *uni)
+{
+ int charlen;
+
+ charlen = t->char2uni(c, clen, uni);
+ if (charlen < 0) {
+ *uni = 0x003f; /* a question mark */
+ charlen = 1;
+ }
+ return charlen;
+}
+
+static inline int
+fat_short2lower_uni(struct nls_table *t, unsigned char *c, int clen, wchar_t *uni)
+{
+ int charlen;
+ wchar_t wc;
+
+ charlen = t->char2uni(c, clen, &wc);
+ if (charlen < 0) {
+ *uni = 0x003f; /* a question mark */
+ charlen = 1;
+ } else if (charlen <= 1) {
+ unsigned char nc = t->charset2lower[*c];
+
+ if (!nc)
+ nc = *c;
+
+ if ( (charlen = t->char2uni(&nc, 1, uni)) < 0) {
+ *uni = 0x003f; /* a question mark */
+ charlen = 1;
+ }
+ } else
+ *uni = wc;
+
+ return charlen;
+}
+
+static int
+fat_strnicmp(struct nls_table *t, const unsigned char *s1,
+ const unsigned char *s2, int len)
+{
+ while(len--)
+ if (fat_tolower(t, *s1++) != fat_tolower(t, *s2++))
+ return 1;
+
+ return 0;
+}
+
+static inline int
+fat_shortname2uni(struct nls_table *nls, char *buf, int buf_size,
+ wchar_t *uni_buf, unsigned short opt, int lower)
+{
+ int len = 0;
+
+ if (opt & VFAT_SFN_DISPLAY_LOWER)
+ len = fat_short2lower_uni(nls, buf, buf_size, uni_buf);
+ else if (opt & VFAT_SFN_DISPLAY_WIN95)
+ len = fat_short2uni(nls, buf, buf_size, uni_buf);
+ else if (opt & VFAT_SFN_DISPLAY_WINNT) {
+ if (lower)
+ len = fat_short2lower_uni(nls, buf, buf_size, uni_buf);
+ else
+ len = fat_short2uni(nls, buf, buf_size, uni_buf);
+ } else
+ len = fat_short2uni(nls, buf, buf_size, uni_buf);
+
+ return len;
+}
+
+/*
+ * Return values: negative -> error, 0 -> not found, positive -> found,
+ * value is the total amount of slots, including the shortname entry.
+ */
+int fat_search_long(struct inode *inode, const char *name, int name_len,
+ int anycase, loff_t *spos, loff_t *lpos)
+{
+ struct super_block *sb = inode->i_sb;
+ struct buffer_head *bh = NULL;
+ struct msdos_dir_entry *de;
+ struct nls_table *nls_io = MSDOS_SB(sb)->nls_io;
+ struct nls_table *nls_disk = MSDOS_SB(sb)->nls_disk;
+ wchar_t bufuname[14];
+ unsigned char xlate_len, long_slots;
+ wchar_t *unicode = NULL;
+ char work[8], bufname[260]; /* 256 + 4 */
+ int uni_xlate = MSDOS_SB(sb)->options.unicode_xlate;
+ int utf8 = MSDOS_SB(sb)->options.utf8;
+ unsigned short opt_shortname = MSDOS_SB(sb)->options.shortname;
+ int chl, i, j, last_u, res = 0;
+ loff_t i_pos, cpos = 0;
+
+ while(1) {
+ if (fat_get_entry(inode,&cpos,&bh,&de,&i_pos) == -1)
+ goto EODir;
+parse_record:
+ long_slots = 0;
+ if (de->name[0] == (__s8) DELETED_FLAG)
+ continue;
+ if (de->attr != ATTR_EXT && (de->attr & ATTR_VOLUME))
+ continue;
+ if (de->attr != ATTR_EXT && IS_FREE(de->name))
+ continue;
+ if (de->attr == ATTR_EXT) {
+ struct msdos_dir_slot *ds;
+ unsigned char id;
+ unsigned char slot;
+ unsigned char slots;
+ unsigned char sum;
+ unsigned char alias_checksum;
+
+ if (!unicode) {
+ unicode = (wchar_t *)
+ __get_free_page(GFP_KERNEL);
+ if (!unicode) {
+ fat_brelse(sb, bh);
+ return -ENOMEM;
+ }
+ }
+parse_long:
+ slots = 0;
+ ds = (struct msdos_dir_slot *) de;
+ id = ds->id;
+ if (!(id & 0x40))
+ continue;
+ slots = id & ~0x40;
+ if (slots > 20 || !slots) /* ceil(256 * 2 / 26) */
+ continue;
+ long_slots = slots;
+ alias_checksum = ds->alias_checksum;
+
+ slot = slots;
+ while (1) {
+ int offset;
+
+ slot--;
+ offset = slot * 13;
+ fat16_towchar(unicode + offset, ds->name0_4, 5);
+ fat16_towchar(unicode + offset + 5, ds->name5_10, 6);
+ fat16_towchar(unicode + offset + 11, ds->name11_12, 2);
+
+ if (ds->id & 0x40) {
+ unicode[offset + 13] = 0;
+ }
+ if (fat_get_entry(inode,&cpos,&bh,&de,&i_pos)<0)
+ goto EODir;
+ if (slot == 0)
+ break;
+ ds = (struct msdos_dir_slot *) de;
+ if (ds->attr != ATTR_EXT)
+ goto parse_record;
+ if ((ds->id & ~0x40) != slot)
+ goto parse_long;
+ if (ds->alias_checksum != alias_checksum)
+ goto parse_long;
+ }
+ if (de->name[0] == (__s8) DELETED_FLAG)
+ continue;
+ if (de->attr == ATTR_EXT)
+ goto parse_long;
+ if (IS_FREE(de->name) || (de->attr & ATTR_VOLUME))
+ continue;
+ for (sum = 0, i = 0; i < 11; i++)
+ sum = (((sum&1)<<7)|((sum&0xfe)>>1)) + de->name[i];
+ if (sum != alias_checksum)
+ long_slots = 0;
+ }
+
+ for (i = 0; i < 8; i++) {
+ /* see namei.c, msdos_format_name */
+ if (de->name[i] == 0x05)
+ work[i] = 0xE5;
+ else
+ work[i] = de->name[i];
+ }
+ for (i = 0, j = 0, last_u = 0; i < 8;) {
+ if (!work[i]) break;
+ chl = fat_shortname2uni(nls_disk, &work[i], 8 - i,
+ &bufuname[j++], opt_shortname,
+ de->lcase & CASE_LOWER_BASE);
+ if (chl <= 1) {
+ if (work[i] != ' ')
+ last_u = j;
+ } else {
+ last_u = j;
+ }
+ i += chl;
+ }
+ j = last_u;
+ fat_short2uni(nls_disk, ".", 1, &bufuname[j++]);
+ for (i = 0; i < 3;) {
+ if (!de->ext[i]) break;
+ chl = fat_shortname2uni(nls_disk, &de->ext[i], 3 - i,
+ &bufuname[j++], opt_shortname,
+ de->lcase & CASE_LOWER_EXT);
+ if (chl <= 1) {
+ if (de->ext[i] != ' ')
+ last_u = j;
+ } else {
+ last_u = j;
+ }
+ i += chl;
+ }
+ if (!last_u)
+ continue;
+
+ bufuname[last_u] = 0x0000;
+ xlate_len = utf8
+ ?utf8_wcstombs(bufname, bufuname, sizeof(bufname))
+ :uni16_to_x8(bufname, bufuname, uni_xlate, nls_io);
+ if (xlate_len == name_len)
+ if ((!anycase && !memcmp(name, bufname, xlate_len)) ||
+ (anycase && !fat_strnicmp(nls_io, name, bufname,
+ xlate_len)))
+ goto Found;
+
+ if (long_slots) {
+ xlate_len = utf8
+ ?utf8_wcstombs(bufname, unicode, sizeof(bufname))
+ :uni16_to_x8(bufname, unicode, uni_xlate, nls_io);
+ if (xlate_len != name_len)
+ continue;
+ if ((!anycase && !memcmp(name, bufname, xlate_len)) ||
+ (anycase && !fat_strnicmp(nls_io, name, bufname,
+ xlate_len)))
+ goto Found;
+ }
+ }
+
+Found:
+ res = long_slots + 1;
+ *spos = cpos - sizeof(struct msdos_dir_entry);
+ *lpos = cpos - res*sizeof(struct msdos_dir_entry);
+EODir:
+ fat_brelse(sb, bh);
+ if (unicode) {
+ free_page((unsigned long) unicode);
+ }
+ return res;
+}
+
+static int fat_readdirx(struct inode *inode, struct file *filp, void *dirent,
+ filldir_t filldir, int shortnames, int both)
+{
+ struct super_block *sb = inode->i_sb;
+ struct buffer_head *bh;
+ struct msdos_dir_entry *de;
+ struct nls_table *nls_io = MSDOS_SB(sb)->nls_io;
+ struct nls_table *nls_disk = MSDOS_SB(sb)->nls_disk;
+ wchar_t bufuname[14];
+ unsigned char long_slots;
+ wchar_t *unicode = NULL;
+ char c, work[8], bufname[56], *ptname = bufname;
+ unsigned long lpos, dummy, *furrfu = &lpos;
+ int uni_xlate = MSDOS_SB(sb)->options.unicode_xlate;
+ int isvfat = MSDOS_SB(sb)->options.isvfat;
+ int utf8 = MSDOS_SB(sb)->options.utf8;
+ int nocase = MSDOS_SB(sb)->options.nocase;
+ unsigned short opt_shortname = MSDOS_SB(sb)->options.shortname;
+ unsigned long inum;
+ int chi, chl, i, i2, j, last, last_u, dotoffset = 0;
+ loff_t i_pos, cpos;
+
+ cpos = filp->f_pos;
+/* Fake . and .. for the root directory. */
+ if (inode->i_ino == MSDOS_ROOT_INO) {
+ while (cpos < 2) {
+ if (filldir(dirent, "..", cpos+1, cpos, MSDOS_ROOT_INO, DT_DIR) < 0)
+ return 0;
+ cpos++;
+ filp->f_pos++;
+ }
+ if (cpos == 2) {
+ dummy = 2;
+ furrfu = &dummy;
+ cpos = 0;
+ }
+ }
+ if (cpos & (sizeof(struct msdos_dir_entry)-1))
+ return -ENOENT;
+
+ bh = NULL;
+GetNew:
+ long_slots = 0;
+ if (fat_get_entry(inode,&cpos,&bh,&de,&i_pos) == -1)
+ goto EODir;
+ /* Check for long filename entry */
+ if (isvfat) {
+ if (de->name[0] == (__s8) DELETED_FLAG)
+ goto RecEnd;
+ if (de->attr != ATTR_EXT && (de->attr & ATTR_VOLUME))
+ goto RecEnd;
+ if (de->attr != ATTR_EXT && IS_FREE(de->name))
+ goto RecEnd;
+ } else {
+ if ((de->attr & ATTR_VOLUME) || IS_FREE(de->name))
+ goto RecEnd;
+ }
+
+ if (isvfat && de->attr == ATTR_EXT) {
+ struct msdos_dir_slot *ds;
+ unsigned char id;
+ unsigned char slot;
+ unsigned char slots;
+ unsigned char sum;
+ unsigned char alias_checksum;
+
+ if (!unicode) {
+ unicode = (wchar_t *)
+ __get_free_page(GFP_KERNEL);
+ if (!unicode) {
+ filp->f_pos = cpos;
+ fat_brelse(sb, bh);
+ return -ENOMEM;
+ }
+ }
+ParseLong:
+ slots = 0;
+ ds = (struct msdos_dir_slot *) de;
+ id = ds->id;
+ if (!(id & 0x40))
+ goto RecEnd;
+ slots = id & ~0x40;
+ if (slots > 20 || !slots) /* ceil(256 * 2 / 26) */
+ goto RecEnd;
+ long_slots = slots;
+ alias_checksum = ds->alias_checksum;
+
+ slot = slots;
+ while (1) {
+ int offset;
+
+ slot--;
+ offset = slot * 13;
+ fat16_towchar(unicode + offset, ds->name0_4, 5);
+ fat16_towchar(unicode + offset + 5, ds->name5_10, 6);
+ fat16_towchar(unicode + offset + 11, ds->name11_12, 2);
+
+ if (ds->id & 0x40) {
+ unicode[offset + 13] = 0;
+ }
+ if (fat_get_entry(inode,&cpos,&bh,&de,&i_pos) == -1)
+ goto EODir;
+ if (slot == 0)
+ break;
+ ds = (struct msdos_dir_slot *) de;
+ if (ds->attr != ATTR_EXT)
+ goto RecEnd; /* XXX */
+ if ((ds->id & ~0x40) != slot)
+ goto ParseLong;
+ if (ds->alias_checksum != alias_checksum)
+ goto ParseLong;
+ }
+ if (de->name[0] == (__s8) DELETED_FLAG)
+ goto RecEnd;
+ if (de->attr == ATTR_EXT)
+ goto ParseLong;
+ if (IS_FREE(de->name) || (de->attr & ATTR_VOLUME))
+ goto RecEnd;
+ for (sum = 0, i = 0; i < 11; i++)
+ sum = (((sum&1)<<7)|((sum&0xfe)>>1)) + de->name[i];
+ if (sum != alias_checksum)
+ long_slots = 0;
+ }
+
+ if ((de->attr & ATTR_HIDDEN) && MSDOS_SB(sb)->options.dotsOK) {
+ *ptname++ = '.';
+ dotoffset = 1;
+ }
+
+ for (i = 0; i < 8; i++) {
+ /* see namei.c, msdos_format_name */
+ if (de->name[i] == 0x05)
+ work[i] = 0xE5;
+ else
+ work[i] = de->name[i];
+ }
+ for (i = 0, j = 0, last = 0, last_u = 0; i < 8;) {
+ if (!(c = work[i])) break;
+ chl = fat_shortname2uni(nls_disk, &work[i], 8 - i,
+ &bufuname[j++], opt_shortname,
+ de->lcase & CASE_LOWER_BASE);
+ if (chl <= 1) {
+ ptname[i++] = (!nocase && c>='A' && c<='Z') ? c+32 : c;
+ if (c != ' ') {
+ last = i;
+ last_u = j;
+ }
+ } else {
+ last_u = j;
+ for (chi = 0; chi < chl && i < 8; chi++) {
+ ptname[i] = work[i];
+ i++; last = i;
+ }
+ }
+ }
+ i = last;
+ j = last_u;
+ fat_short2uni(nls_disk, ".", 1, &bufuname[j++]);
+ ptname[i++] = '.';
+ for (i2 = 0; i2 < 3;) {
+ if (!(c = de->ext[i2])) break;
+ chl = fat_shortname2uni(nls_disk, &de->ext[i2], 3 - i2,
+ &bufuname[j++], opt_shortname,
+ de->lcase & CASE_LOWER_EXT);
+ if (chl <= 1) {
+ i2++;
+ ptname[i++] = (!nocase && c>='A' && c<='Z') ? c+32 : c;
+ if (c != ' ') {
+ last = i;
+ last_u = j;
+ }
+ } else {
+ last_u = j;
+ for (chi = 0; chi < chl && i2 < 3; chi++) {
+ ptname[i++] = de->ext[i2++];
+ last = i;
+ }
+ }
+ }
+ if (!last)
+ goto RecEnd;
+
+ i = last + dotoffset;
+ j = last_u;
+
+ lpos = cpos - (long_slots+1)*sizeof(struct msdos_dir_entry);
+ if (!memcmp(de->name,MSDOS_DOT,11))
+ inum = inode->i_ino;
+ else if (!memcmp(de->name,MSDOS_DOTDOT,11)) {
+/* inum = fat_parent_ino(inode,0); */
+ inum = filp->f_dentry->d_parent->d_inode->i_ino;
+ } else {
+ struct inode *tmp = fat_iget(sb, i_pos);
+ if (tmp) {
+ inum = tmp->i_ino;
+ iput(tmp);
+ } else
+ inum = iunique(sb, MSDOS_ROOT_INO);
+ }
+
+ if (isvfat) {
+ bufuname[j] = 0x0000;
+ i = utf8 ? utf8_wcstombs(bufname, bufuname, sizeof(bufname))
+ : uni16_to_x8(bufname, bufuname, uni_xlate, nls_io);
+ }
+
+ if (!long_slots||shortnames) {
+ if (both)
+ bufname[i] = '\0';
+ if (filldir(dirent, bufname, i, *furrfu, inum,
+ (de->attr & ATTR_DIR) ? DT_DIR : DT_REG) < 0)
+ goto FillFailed;
+ } else {
+ char longname[275];
+ int long_len = utf8
+ ? utf8_wcstombs(longname, unicode, sizeof(longname))
+ : uni16_to_x8(longname, unicode, uni_xlate,
+ nls_io);
+ if (both) {
+ memcpy(&longname[long_len+1], bufname, i);
+ long_len += i;
+ }
+ if (filldir(dirent, longname, long_len, *furrfu, inum,
+ (de->attr & ATTR_DIR) ? DT_DIR : DT_REG) < 0)
+ goto FillFailed;
+ }
+
+RecEnd:
+ furrfu = &lpos;
+ filp->f_pos = cpos;
+ goto GetNew;
+EODir:
+ filp->f_pos = cpos;
+FillFailed:
+ if (bh)
+ fat_brelse(sb, bh);
+ if (unicode) {
+ free_page((unsigned long) unicode);
+ }
+ return 0;
+}
+
+int fat_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ return fat_readdirx(inode, filp, dirent, filldir, 0, 0);
+}
+
+static int vfat_ioctl_fill(
+ void * buf,
+ const char * name,
+ int name_len,
+ loff_t offset,
+ ino_t ino,
+ unsigned int d_type)
+{
+ struct dirent *d1 = (struct dirent *)buf;
+ struct dirent *d2 = d1 + 1;
+ int len, slen;
+ int dotdir;
+
+ get_user(len, &d1->d_reclen);
+ if (len != 0) {
+ return -1;
+ }
+
+ if ((name_len == 1 && name[0] == '.') ||
+ (name_len == 2 && name[0] == '.' && name[1] == '.')) {
+ dotdir = 1;
+ len = name_len;
+ } else {
+ dotdir = 0;
+ len = strlen(name);
+ }
+ if (len != name_len) {
+ copy_to_user(d2->d_name, name, len);
+ put_user(0, d2->d_name + len);
+ put_user(len, &d2->d_reclen);
+ put_user(ino, &d2->d_ino);
+ put_user(offset, &d2->d_off);
+ slen = name_len - len;
+ copy_to_user(d1->d_name, name+len+1, slen);
+ put_user(0, d1->d_name+slen);
+ put_user(slen, &d1->d_reclen);
+ } else {
+ put_user(0, d2->d_name);
+ put_user(0, &d2->d_reclen);
+ copy_to_user(d1->d_name, name, len);
+ put_user(0, d1->d_name+len);
+ put_user(len, &d1->d_reclen);
+ }
+ PRINTK(("FAT d1=%p d2=%p len=%d, name_len=%d\n",
+ d1, d2, len, name_len));
+
+ return 0;
+}
+
+int fat_dir_ioctl(struct inode * inode, struct file * filp,
+ unsigned int cmd, unsigned long arg)
+{
+ int err;
+ /*
+ * We want to provide an interface for Samba to be able
+ * to get the short filename for a given long filename.
+ * Samba should use this ioctl instead of readdir() to
+ * get the information it needs.
+ */
+ switch (cmd) {
+ case VFAT_IOCTL_READDIR_BOTH: {
+ struct dirent *d1 = (struct dirent *)arg;
+ err = verify_area(VERIFY_WRITE, d1, sizeof(struct dirent[2]));
+ if (err)
+ return err;
+ put_user(0, &d1->d_reclen);
+ return fat_readdirx(inode,filp,(void *)arg,
+ vfat_ioctl_fill, 0, 1);
+ }
+ case VFAT_IOCTL_READDIR_SHORT: {
+ struct dirent *d1 = (struct dirent *)arg;
+ put_user(0, &d1->d_reclen);
+ err = verify_area(VERIFY_WRITE, d1, sizeof(struct dirent[2]));
+ if (err)
+ return err;
+ return fat_readdirx(inode,filp,(void *)arg,
+ vfat_ioctl_fill, 1, 1);
+ }
+ default:
+ /* forward ioctl to CVF extension */
+ if (MSDOS_SB(inode->i_sb)->cvf_format &&
+ MSDOS_SB(inode->i_sb)->cvf_format->cvf_dir_ioctl)
+ return MSDOS_SB(inode->i_sb)->cvf_format
+ ->cvf_dir_ioctl(inode,filp,cmd,arg);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/***** See if directory is empty */
+int fat_dir_empty(struct inode *dir)
+{
+ loff_t pos, i_pos;
+ struct buffer_head *bh;
+ struct msdos_dir_entry *de;
+ int result = 0;
+
+ pos = 0;
+ bh = NULL;
+ while (fat_get_entry(dir,&pos,&bh,&de,&i_pos) > -1) {
+ /* Ignore vfat longname entries */
+ if (de->attr == ATTR_EXT)
+ continue;
+ if (!IS_FREE(de->name) &&
+ strncmp(de->name,MSDOS_DOT , MSDOS_NAME) &&
+ strncmp(de->name,MSDOS_DOTDOT, MSDOS_NAME)) {
+ result = -ENOTEMPTY;
+ break;
+ }
+ }
+ if (bh)
+ fat_brelse(dir->i_sb, bh);
+
+ return result;
+}
+
+/* This assumes that size of cluster is above the 32*slots */
+
+int fat_add_entries(struct inode *dir,int slots, struct buffer_head **bh,
+ struct msdos_dir_entry **de, loff_t *i_pos)
+{
+ struct super_block *sb = dir->i_sb;
+ loff_t offset, curr;
+ int row;
+ struct buffer_head *new_bh;
+
+ offset = curr = 0;
+ *bh = NULL;
+ row = 0;
+ while (fat_get_entry(dir,&curr,bh,de,i_pos) > -1) {
+ if (IS_FREE((*de)->name)) {
+ if (++row == slots)
+ return offset;
+ } else {
+ row = 0;
+ offset = curr;
+ }
+ }
+ if ((dir->i_ino == MSDOS_ROOT_INO) && (MSDOS_SB(sb)->fat_bits != 32))
+ return -ENOSPC;
+ new_bh = fat_extend_dir(dir);
+ if (!new_bh)
+ return -ENOSPC;
+ fat_brelse(sb, new_bh);
+ do fat_get_entry(dir,&curr,bh,de,i_pos); while (++row<slots);
+ return offset;
+}
+
+int fat_new_dir(struct inode *dir, struct inode *parent, int is_vfat)
+{
+ struct super_block *sb = dir->i_sb;
+ struct buffer_head *bh;
+ struct msdos_dir_entry *de;
+ __u16 date, time;
+
+ if ((bh = fat_extend_dir(dir)) == NULL) return -ENOSPC;
+ /* zeroed out, so... */
+ fat_date_unix2dos(dir->i_mtime,&time,&date);
+ de = (struct msdos_dir_entry*)&bh->b_data[0];
+ memcpy(de[0].name,MSDOS_DOT,MSDOS_NAME);
+ memcpy(de[1].name,MSDOS_DOTDOT,MSDOS_NAME);
+ de[0].attr = de[1].attr = ATTR_DIR;
+ de[0].time = de[1].time = CT_LE_W(time);
+ de[0].date = de[1].date = CT_LE_W(date);
+ if (is_vfat) { /* extra timestamps */
+ de[0].ctime = de[1].ctime = CT_LE_W(time);
+ de[0].adate = de[0].cdate =
+ de[1].adate = de[1].cdate = CT_LE_W(date);
+ }
+ de[0].start = CT_LE_W(MSDOS_I(dir)->i_logstart);
+ de[0].starthi = CT_LE_W(MSDOS_I(dir)->i_logstart>>16);
+ de[1].start = CT_LE_W(MSDOS_I(parent)->i_logstart);
+ de[1].starthi = CT_LE_W(MSDOS_I(parent)->i_logstart>>16);
+ fat_mark_buffer_dirty(sb, bh);
+ fat_brelse(sb, bh);
+ dir->i_atime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ mark_inode_dirty(dir);
+
+ return 0;
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/fs/fat/fatfs_syms.c b/fs/fat/fatfs_syms.c
new file mode 100644
index 00000000000000..343727a4228a44
--- /dev/null
+++ b/fs/fat/fatfs_syms.c
@@ -0,0 +1,48 @@
+/*
+ * linux/fs/fat/fatfs_syms.c
+ *
+ * Exported kernel symbols for the low-level FAT-based fs support.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <linux/mm.h>
+#include <linux/msdos_fs.h>
+#include <linux/fat_cvf.h>
+
+EXPORT_SYMBOL(fat_new_dir);
+EXPORT_SYMBOL(fat_get_block);
+EXPORT_SYMBOL(fat_clear_inode);
+EXPORT_SYMBOL(fat_date_unix2dos);
+EXPORT_SYMBOL(fat_delete_inode);
+EXPORT_SYMBOL(fat__get_entry);
+EXPORT_SYMBOL(fat_mark_buffer_dirty);
+EXPORT_SYMBOL(fat_notify_change);
+EXPORT_SYMBOL(fat_put_super);
+EXPORT_SYMBOL(fat_attach);
+EXPORT_SYMBOL(fat_detach);
+EXPORT_SYMBOL(fat_build_inode);
+EXPORT_SYMBOL(fat_read_super);
+EXPORT_SYMBOL(fat_search_long);
+EXPORT_SYMBOL(fat_readdir);
+EXPORT_SYMBOL(fat_scan);
+EXPORT_SYMBOL(fat_statfs);
+EXPORT_SYMBOL(fat_write_inode);
+EXPORT_SYMBOL(register_cvf_format);
+EXPORT_SYMBOL(unregister_cvf_format);
+EXPORT_SYMBOL(fat_get_cluster);
+EXPORT_SYMBOL(fat_dir_ioctl);
+EXPORT_SYMBOL(fat_add_entries);
+EXPORT_SYMBOL(fat_dir_empty);
+EXPORT_SYMBOL(fat_truncate);
+EXPORT_SYMBOL(fat_brelse);
+
+static int __init init_fat_fs(void)
+{
+ fat_hash_init();
+ return 0;
+}
+
+module_init(init_fat_fs)
diff --git a/fs/fat/file.c b/fs/fat/file.c
new file mode 100644
index 00000000000000..ade1a71bf50ae7
--- /dev/null
+++ b/fs/fat/file.c
@@ -0,0 +1,136 @@
+/*
+ * linux/fs/fat/file.c
+ *
+ * Written 1992,1993 by Werner Almesberger
+ *
+ * regular file handling primitives for fat-based filesystems
+ */
+
+#include <linux/sched.h>
+#include <linux/locks.h>
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/pagemap.h>
+#include <linux/fat_cvf.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#define PRINTK(x)
+#define Printk(x) printk x
+
+struct file_operations fat_file_operations = {
+ llseek: generic_file_llseek,
+ read: fat_file_read,
+ write: fat_file_write,
+ mmap: generic_file_mmap,
+ fsync: file_fsync,
+};
+
+struct inode_operations fat_file_inode_operations = {
+ truncate: fat_truncate,
+ setattr: fat_notify_change,
+};
+
+ssize_t fat_file_read(
+ struct file *filp,
+ char *buf,
+ size_t count,
+ loff_t *ppos)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ return MSDOS_SB(inode->i_sb)->cvf_format
+ ->cvf_file_read(filp,buf,count,ppos);
+}
+
+
+int fat_get_block(struct inode *inode, long iblock, struct buffer_head *bh_result, int create)
+{
+ struct super_block *sb = inode->i_sb;
+ unsigned long phys;
+
+ phys = fat_bmap(inode, iblock);
+ if (phys) {
+ bh_result->b_dev = inode->i_dev;
+ bh_result->b_blocknr = phys;
+ bh_result->b_state |= (1UL << BH_Mapped);
+ return 0;
+ }
+ if (!create)
+ return 0;
+ if (iblock << sb->s_blocksize_bits != MSDOS_I(inode)->mmu_private) {
+ BUG();
+ return -EIO;
+ }
+ if (!(iblock % MSDOS_SB(inode->i_sb)->cluster_size)) {
+ if (fat_add_cluster(inode) < 0)
+ return -ENOSPC;
+ }
+ MSDOS_I(inode)->mmu_private += sb->s_blocksize;
+ phys = fat_bmap(inode, iblock);
+ if (!phys)
+ BUG();
+ bh_result->b_dev = inode->i_dev;
+ bh_result->b_blocknr = phys;
+ bh_result->b_state |= (1UL << BH_Mapped);
+ bh_result->b_state |= (1UL << BH_New);
+ return 0;
+}
+
+ssize_t fat_file_write(
+ struct file *filp,
+ const char *buf,
+ size_t count,
+ loff_t *ppos)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ struct super_block *sb = inode->i_sb;
+ return MSDOS_SB(sb)->cvf_format
+ ->cvf_file_write(filp,buf,count,ppos);
+}
+
+ssize_t default_fat_file_write(
+ struct file *filp,
+ const char *buf,
+ size_t count,
+ loff_t *ppos)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ int retval;
+
+ retval = generic_file_write(filp, buf, count, ppos);
+ if (retval > 0) {
+ inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ MSDOS_I(inode)->i_attrs |= ATTR_ARCH;
+ mark_inode_dirty(inode);
+ }
+ return retval;
+}
+
+void fat_truncate(struct inode *inode)
+{
+ struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
+ int cluster;
+
+ /* Why no return value? Surely the disk could fail... */
+ if (IS_RDONLY (inode))
+ return /* -EPERM */;
+ if (IS_IMMUTABLE(inode))
+ return /* -EPERM */;
+ cluster = 1 << sbi->cluster_bits;
+ /*
+ * This protects against truncating a file bigger than it was then
+ * trying to write into the hole.
+ */
+ if (MSDOS_I(inode)->mmu_private > inode->i_size)
+ MSDOS_I(inode)->mmu_private = inode->i_size;
+
+ fat_free(inode, (inode->i_size + (cluster - 1)) >> sbi->cluster_bits);
+ MSDOS_I(inode)->i_attrs |= ATTR_ARCH;
+ inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+}
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
new file mode 100644
index 00000000000000..f3b6639ba8b94d
--- /dev/null
+++ b/fs/fat/inode.c
@@ -0,0 +1,1074 @@
+/*
+ * linux/fs/fat/inode.c
+ *
+ * Written 1992,1993 by Werner Almesberger
+ * VFAT extensions by Gordon Chaffee, merged with msdos fs by Henrik Storner
+ * Rewritten for the constant inumbers support by Al Viro
+ *
+ * Fixes:
+ *
+ * Max Cohan: Fixed invalid FSINFO offset when info_sector is 0
+ */
+
+#include <linux/module.h>
+#include <linux/msdos_fs.h>
+#include <linux/nls.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/bitops.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/locks.h>
+#include <linux/fat_cvf.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+
+#include <asm/uaccess.h>
+#include <asm/unaligned.h>
+
+extern struct cvf_format default_cvf;
+
+/* #define FAT_PARANOIA 1 */
+#define DEBUG_LEVEL 0
+#ifdef FAT_DEBUG
+# define PRINTK(x) printk x
+#else
+# define PRINTK(x)
+#endif
+#if (DEBUG_LEVEL >= 1)
+# define PRINTK1(x) printk x
+#else
+# define PRINTK1(x)
+#endif
+
+/*
+ * New FAT inode stuff. We do the following:
+ * a) i_ino is constant and has nothing with on-disk location.
+ * b) FAT manages its own cache of directory entries.
+ * c) *This* cache is indexed by on-disk location.
+ * d) inode has an associated directory entry, all right, but
+ * it may be unhashed.
+ * e) currently entries are stored within struct inode. That should
+ * change.
+ * f) we deal with races in the following way:
+ * 1. readdir() and lookup() do FAT-dir-cache lookup.
+ * 2. rename() unhashes the F-d-c entry and rehashes it in
+ * a new place.
+ * 3. unlink() and rmdir() unhash F-d-c entry.
+ * 4. fat_write_inode() checks whether the thing is unhashed.
+ * If it is we silently return. If it isn't we do bread(),
+ * check if the location is still valid and retry if it
+ * isn't. Otherwise we do changes.
+ * 5. Spinlock is used to protect hash/unhash/location check/lookup
+ * 6. fat_clear_inode() unhashes the F-d-c entry.
+ * 7. lookup() and readdir() do igrab() if they find a F-d-c entry
+ * and consider negative result as cache miss.
+ */
+
+#define FAT_HASH_BITS 8
+#define FAT_HASH_SIZE (1UL << FAT_HASH_BITS)
+#define FAT_HASH_MASK (FAT_HASH_SIZE-1)
+static struct list_head fat_inode_hashtable[FAT_HASH_SIZE];
+spinlock_t fat_inode_lock = SPIN_LOCK_UNLOCKED;
+
+void fat_hash_init(void)
+{
+ int i;
+ for(i = 0; i < FAT_HASH_SIZE; i++) {
+ INIT_LIST_HEAD(&fat_inode_hashtable[i]);
+ }
+}
+
+static inline unsigned long fat_hash(struct super_block *sb, loff_t i_pos)
+{
+ unsigned long tmp = (unsigned long)i_pos | (unsigned long) sb;
+ tmp = tmp + (tmp >> FAT_HASH_BITS) + (tmp >> FAT_HASH_BITS * 2);
+ return tmp & FAT_HASH_MASK;
+}
+
+void fat_attach(struct inode *inode, loff_t i_pos)
+{
+ spin_lock(&fat_inode_lock);
+ MSDOS_I(inode)->i_pos = i_pos;
+ list_add(&MSDOS_I(inode)->i_fat_hash,
+ fat_inode_hashtable + fat_hash(inode->i_sb, i_pos));
+ spin_unlock(&fat_inode_lock);
+}
+
+void fat_detach(struct inode *inode)
+{
+ spin_lock(&fat_inode_lock);
+ MSDOS_I(inode)->i_pos = 0;
+ list_del(&MSDOS_I(inode)->i_fat_hash);
+ INIT_LIST_HEAD(&MSDOS_I(inode)->i_fat_hash);
+ spin_unlock(&fat_inode_lock);
+}
+
+struct inode *fat_iget(struct super_block *sb, loff_t i_pos)
+{
+ struct list_head *p = fat_inode_hashtable + fat_hash(sb, i_pos);
+ struct list_head *walk;
+ struct msdos_inode_info *i;
+ struct inode *inode = NULL;
+
+ spin_lock(&fat_inode_lock);
+ list_for_each(walk, p) {
+ i = list_entry(walk, struct msdos_inode_info, i_fat_hash);
+ if (i->i_fat_inode->i_sb != sb)
+ continue;
+ if (i->i_pos != i_pos)
+ continue;
+ inode = igrab(i->i_fat_inode);
+ if (inode)
+ break;
+ }
+ spin_unlock(&fat_inode_lock);
+ return inode;
+}
+
+static void fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de);
+
+struct inode *fat_build_inode(struct super_block *sb,
+ struct msdos_dir_entry *de, loff_t i_pos, int *res)
+{
+ struct inode *inode;
+ *res = 0;
+ inode = fat_iget(sb, i_pos);
+ if (inode)
+ goto out;
+ inode = new_inode(sb);
+ *res = -ENOMEM;
+ if (!inode)
+ goto out;
+ *res = 0;
+ inode->i_ino = iunique(sb, MSDOS_ROOT_INO);
+ fat_fill_inode(inode, de);
+ fat_attach(inode, i_pos);
+ insert_inode_hash(inode);
+out:
+ return inode;
+}
+
+void fat_delete_inode(struct inode *inode)
+{
+ if (!is_bad_inode(inode)) {
+ lock_kernel();
+ inode->i_size = 0;
+ fat_truncate(inode);
+ unlock_kernel();
+ }
+ clear_inode(inode);
+}
+
+void fat_clear_inode(struct inode *inode)
+{
+ if (is_bad_inode(inode))
+ return;
+ lock_kernel();
+ spin_lock(&fat_inode_lock);
+ fat_cache_inval_inode(inode);
+ list_del(&MSDOS_I(inode)->i_fat_hash);
+ spin_unlock(&fat_inode_lock);
+ unlock_kernel();
+}
+
+void fat_put_super(struct super_block *sb)
+{
+ if (MSDOS_SB(sb)->cvf_format->cvf_version) {
+ dec_cvf_format_use_count_by_version(MSDOS_SB(sb)->cvf_format->cvf_version);
+ MSDOS_SB(sb)->cvf_format->unmount_cvf(sb);
+ }
+ if (MSDOS_SB(sb)->fat_bits == 32) {
+ fat_clusters_flush(sb);
+ }
+ fat_cache_inval_dev(sb->s_dev);
+ set_blocksize (sb->s_dev,BLOCK_SIZE);
+ if (MSDOS_SB(sb)->nls_disk) {
+ unload_nls(MSDOS_SB(sb)->nls_disk);
+ MSDOS_SB(sb)->nls_disk = NULL;
+ MSDOS_SB(sb)->options.codepage = 0;
+ }
+ if (MSDOS_SB(sb)->nls_io) {
+ unload_nls(MSDOS_SB(sb)->nls_io);
+ MSDOS_SB(sb)->nls_io = NULL;
+ }
+ /*
+ * Note: the iocharset option might have been specified
+ * without enabling nls_io, so check for it here.
+ */
+ if (MSDOS_SB(sb)->options.iocharset) {
+ kfree(MSDOS_SB(sb)->options.iocharset);
+ MSDOS_SB(sb)->options.iocharset = NULL;
+ }
+}
+
+
+static int parse_options(char *options,int *fat, int *debug,
+ struct fat_mount_options *opts,
+ char *cvf_format, char *cvf_options)
+{
+ char *this_char,*value,save,*savep;
+ char *p;
+ int ret = 1, len;
+
+ opts->name_check = 'n';
+ opts->conversion = 'b';
+ opts->fs_uid = current->uid;
+ opts->fs_gid = current->gid;
+ opts->fs_umask = current->fs->umask;
+ opts->quiet = opts->sys_immutable = opts->dotsOK = opts->showexec = 0;
+ opts->codepage = 0;
+ opts->nocase = 0;
+ opts->shortname = 0;
+ opts->utf8 = 0;
+ opts->iocharset = NULL;
+ *debug = *fat = 0;
+
+ if (!options)
+ goto out;
+ save = 0;
+ savep = NULL;
+ for (this_char = strtok(options,","); this_char;
+ this_char = strtok(NULL,",")) {
+ if ((value = strchr(this_char,'=')) != NULL) {
+ save = *value;
+ savep = value;
+ *value++ = 0;
+ }
+ if (!strcmp(this_char,"check") && value) {
+ if (value[0] && !value[1] && strchr("rns",*value))
+ opts->name_check = *value;
+ else if (!strcmp(value,"relaxed"))
+ opts->name_check = 'r';
+ else if (!strcmp(value,"normal"))
+ opts->name_check = 'n';
+ else if (!strcmp(value,"strict"))
+ opts->name_check = 's';
+ else ret = 0;
+ }
+ else if (!strcmp(this_char,"conv") && value) {
+ if (value[0] && !value[1] && strchr("bta",*value))
+ opts->conversion = *value;
+ else if (!strcmp(value,"binary"))
+ opts->conversion = 'b';
+ else if (!strcmp(value,"text"))
+ opts->conversion = 't';
+ else if (!strcmp(value,"auto"))
+ opts->conversion = 'a';
+ else ret = 0;
+ }
+ else if (!strcmp(this_char,"dots")) {
+ opts->dotsOK = 1;
+ }
+ else if (!strcmp(this_char,"nocase")) {
+ opts->nocase = 1;
+ }
+ else if (!strcmp(this_char,"nodots")) {
+ opts->dotsOK = 0;
+ }
+ else if (!strcmp(this_char,"showexec")) {
+ opts->showexec = 1;
+ }
+ else if (!strcmp(this_char,"dotsOK") && value) {
+ if (!strcmp(value,"yes")) opts->dotsOK = 1;
+ else if (!strcmp(value,"no")) opts->dotsOK = 0;
+ else ret = 0;
+ }
+ else if (!strcmp(this_char,"uid")) {
+ if (!value || !*value) ret = 0;
+ else {
+ opts->fs_uid = simple_strtoul(value,&value,0);
+ if (*value) ret = 0;
+ }
+ }
+ else if (!strcmp(this_char,"gid")) {
+ if (!value || !*value) ret= 0;
+ else {
+ opts->fs_gid = simple_strtoul(value,&value,0);
+ if (*value) ret = 0;
+ }
+ }
+ else if (!strcmp(this_char,"umask")) {
+ if (!value || !*value) ret = 0;
+ else {
+ opts->fs_umask = simple_strtoul(value,&value,8);
+ if (*value) ret = 0;
+ }
+ }
+ else if (!strcmp(this_char,"debug")) {
+ if (value) ret = 0;
+ else *debug = 1;
+ }
+ else if (!strcmp(this_char,"fat")) {
+ if (!value || !*value) ret = 0;
+ else {
+ *fat = simple_strtoul(value,&value,0);
+ if (*value || (*fat != 12 && *fat != 16 &&
+ *fat != 32))
+ ret = 0;
+ }
+ }
+ else if (!strcmp(this_char,"quiet")) {
+ if (value) ret = 0;
+ else opts->quiet = 1;
+ }
+ else if (!strcmp(this_char,"blocksize")) {
+ printk("FAT: blocksize option is obsolete, "
+ "not supported now\n");
+ }
+ else if (!strcmp(this_char,"sys_immutable")) {
+ if (value) ret = 0;
+ else opts->sys_immutable = 1;
+ }
+ else if (!strcmp(this_char,"codepage") && value) {
+ opts->codepage = simple_strtoul(value,&value,0);
+ if (*value) ret = 0;
+ else printk ("MSDOS FS: Using codepage %d\n",
+ opts->codepage);
+ }
+ else if (!strcmp(this_char,"iocharset") && value) {
+ p = value;
+ while (*value && *value != ',')
+ value++;
+ len = value - p;
+ if (len) {
+ char *buffer;
+
+ if (opts->iocharset != NULL) {
+ kfree(opts->iocharset);
+ opts->iocharset = NULL;
+ }
+ buffer = kmalloc(len + 1, GFP_KERNEL);
+ if (buffer != NULL) {
+ opts->iocharset = buffer;
+ memcpy(buffer, p, len);
+ buffer[len] = 0;
+ printk("MSDOS FS: IO charset %s\n", buffer);
+ } else
+ ret = 0;
+ }
+ }
+ else if (!strcmp(this_char,"cvf_format")) {
+ if (!value)
+ return 0;
+ strncpy(cvf_format,value,20);
+ }
+ else if (!strcmp(this_char,"cvf_options")) {
+ if (!value)
+ return 0;
+ strncpy(cvf_options,value,100);
+ }
+
+ if (this_char != options) *(this_char-1) = ',';
+ if (value) *savep = save;
+ if (ret == 0)
+ break;
+ }
+out:
+ return ret;
+}
+
+static void fat_read_root(struct inode *inode)
+{
+ struct super_block *sb = inode->i_sb;
+ struct msdos_sb_info *sbi = MSDOS_SB(sb);
+ int nr;
+
+ INIT_LIST_HEAD(&MSDOS_I(inode)->i_fat_hash);
+ MSDOS_I(inode)->i_pos = 0;
+ MSDOS_I(inode)->i_fat_inode = inode;
+ inode->i_uid = sbi->options.fs_uid;
+ inode->i_gid = sbi->options.fs_gid;
+ inode->i_version = ++event;
+ inode->i_generation = 0;
+ inode->i_mode = (S_IRWXUGO & ~sbi->options.fs_umask) | S_IFDIR;
+ inode->i_op = sbi->dir_ops;
+ inode->i_fop = &fat_dir_operations;
+ if (sbi->fat_bits == 32) {
+ MSDOS_I(inode)->i_start = sbi->root_cluster;
+ if ((nr = MSDOS_I(inode)->i_start) != 0) {
+ while (nr != -1) {
+ inode->i_size += 1 << sbi->cluster_bits;
+ if (!(nr = fat_access(sb, nr, -1))) {
+ printk("Directory %ld: bad FAT\n",
+ inode->i_ino);
+ break;
+ }
+ }
+ }
+ } else {
+ MSDOS_I(inode)->i_start = 0;
+ inode->i_size = sbi->dir_entries * sizeof(struct msdos_dir_entry);
+ }
+ inode->i_blksize = 1 << sbi->cluster_bits;
+ inode->i_blocks = ((inode->i_size + inode->i_blksize - 1)
+ & ~(inode->i_blksize - 1)) >> 9;
+ MSDOS_I(inode)->i_logstart = 0;
+ MSDOS_I(inode)->mmu_private = inode->i_size;
+
+ MSDOS_I(inode)->i_attrs = 0;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = 0;
+ MSDOS_I(inode)->i_ctime_ms = 0;
+ inode->i_nlink = fat_subdirs(inode)+2;
+}
+
+/*
+ * a FAT file handle with fhtype 3 is
+ * 0/ i_ino - for fast, reliable lookup if still in the cache
+ * 1/ i_generation - to see if i_ino is still valid
+ * bit 0 == 0 iff directory
+ * 2/ i_pos(8-39) - if ino has changed, but still in cache
+ * 3/ i_pos(4-7)|i_logstart - to semi-verify inode found at i_pos
+ * 4/ i_pos(0-3)|parent->i_logstart - maybe used to hunt for the file on disc
+ *
+ * Hack for NFSv2: Maximum FAT entry number is 28bits and maximum
+ * i_pos is 40bits (blocknr(32) + dir offset(8)), so two 4bits
+ * of i_logstart is used to store the directory entry offset.
+ */
+struct dentry *fat_fh_to_dentry(struct super_block *sb, __u32 *fh,
+ int len, int fhtype, int parent)
+{
+ struct inode *inode = NULL;
+ struct list_head *lp;
+ struct dentry *result;
+
+ if (fhtype != 3)
+ return ERR_PTR(-ESTALE);
+ if (len < 5)
+ return ERR_PTR(-ESTALE);
+ /* We cannot find the parent,
+ It better just *be* there */
+ if (parent)
+ return ERR_PTR(-ESTALE);
+
+ inode = iget(sb, fh[0]);
+ if (!inode || is_bad_inode(inode) || inode->i_generation != fh[1]) {
+ if (inode)
+ iput(inode);
+ inode = NULL;
+ }
+ if (!inode) {
+ loff_t i_pos;
+ int i_logstart = fh[3] & 0x0fffffff;
+
+ i_pos = (loff_t)fh[2] << 8;
+ i_pos |= ((fh[3] >> 24) & 0xf0) | (fh[4] >> 28);
+
+ /* try 2 - see if i_pos is in F-d-c
+ * require i_logstart to be the same
+ * Will fail if you truncate and then re-write
+ */
+
+ inode = fat_iget(sb, i_pos);
+ if (inode && MSDOS_I(inode)->i_logstart != i_logstart) {
+ iput(inode);
+ inode = NULL;
+ }
+ }
+ if (!inode) {
+ /* For now, do nothing
+ * What we could do is:
+ * follow the file starting at fh[4], and record
+ * the ".." entry, and the name of the fh[2] entry.
+ * The follow the ".." file finding the next step up.
+ * This way we build a path to the root of
+ * the tree. If this works, we lookup the path and so
+ * get this inode into the cache.
+ * Finally try the fat_iget lookup again
+ * If that fails, then weare totally out of luck
+ * But all that is for another day
+ */
+ }
+ if (!inode)
+ return ERR_PTR(-ESTALE);
+
+
+ /* now to find a dentry.
+ * If possible, get a well-connected one
+ *
+ * Given the way that we found the inode, it *MUST* be
+ * well-connected, but it is easiest to just copy the
+ * code.
+ */
+ spin_lock(&dcache_lock);
+ for (lp = inode->i_dentry.next; lp != &inode->i_dentry ; lp=lp->next) {
+ result = list_entry(lp,struct dentry, d_alias);
+ if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED)) {
+ dget_locked(result);
+ result->d_vfs_flags |= DCACHE_REFERENCED;
+ spin_unlock(&dcache_lock);
+ iput(inode);
+ return result;
+ }
+ }
+ spin_unlock(&dcache_lock);
+ result = d_alloc_root(inode);
+ if (result == NULL) {
+ iput(inode);
+ return ERR_PTR(-ENOMEM);
+ }
+ result->d_op = sb->s_root->d_op;
+ result->d_flags |= DCACHE_NFSD_DISCONNECTED;
+ return result;
+}
+
+int fat_dentry_to_fh(struct dentry *de, __u32 *fh, int *lenp, int needparent)
+{
+ int len = *lenp;
+ struct inode *inode = de->d_inode;
+ u32 ipos_h, ipos_m, ipos_l;
+
+ if (len < 5)
+ return 255; /* no room */
+
+ ipos_h = MSDOS_I(inode)->i_pos >> 8;
+ ipos_m = (MSDOS_I(inode)->i_pos & 0xf0) << 24;
+ ipos_l = (MSDOS_I(inode)->i_pos & 0x0f) << 28;
+ *lenp = 5;
+ fh[0] = inode->i_ino;
+ fh[1] = inode->i_generation;
+ fh[2] = ipos_h;
+ fh[3] = ipos_m | MSDOS_I(inode)->i_logstart;
+ fh[4] = ipos_l | MSDOS_I(de->d_parent->d_inode)->i_logstart;
+ return 3;
+}
+
+static struct super_operations fat_sops = {
+ write_inode: fat_write_inode,
+ delete_inode: fat_delete_inode,
+ put_super: fat_put_super,
+ statfs: fat_statfs,
+ clear_inode: fat_clear_inode,
+
+ read_inode: make_bad_inode,
+ fh_to_dentry: fat_fh_to_dentry,
+ dentry_to_fh: fat_dentry_to_fh,
+};
+
+/*
+ * Read the super block of an MS-DOS FS.
+ *
+ * Note that this may be called from vfat_read_super
+ * with some fields already initialized.
+ */
+struct super_block *
+fat_read_super(struct super_block *sb, void *data, int silent,
+ struct inode_operations *fs_dir_inode_ops)
+{
+ struct inode *root_inode;
+ struct buffer_head *bh;
+ struct fat_boot_sector *b;
+ struct msdos_sb_info *sbi = MSDOS_SB(sb);
+ char *p;
+ int logical_sector_size, hard_blksize, fat_clusters = 0;
+ unsigned int total_sectors, rootdir_sectors;
+ int fat32, debug, error, fat, cp;
+ struct fat_mount_options opts;
+ char buf[50];
+ int i;
+ char cvf_format[21];
+ char cvf_options[101];
+
+ cvf_format[0] = '\0';
+ cvf_options[0] = '\0';
+ sbi->cvf_format = NULL;
+ sbi->private_data = NULL;
+
+ sbi->dir_ops = fs_dir_inode_ops;
+
+ sb->s_maxbytes = MAX_NON_LFS;
+ sb->s_op = &fat_sops;
+
+ hard_blksize = get_hardsect_size(sb->s_dev);
+ if (!hard_blksize)
+ hard_blksize = 512;
+
+ opts.isvfat = sbi->options.isvfat;
+ if (!parse_options((char *) data, &fat, &debug, &opts,
+ cvf_format, cvf_options))
+ goto out_fail;
+ /* N.B. we should parse directly into the sb structure */
+ memcpy(&(sbi->options), &opts, sizeof(struct fat_mount_options));
+
+ fat_cache_init();
+
+ sb->s_blocksize = hard_blksize;
+ set_blocksize(sb->s_dev, hard_blksize);
+ bh = sb_bread(sb, 0);
+ if (bh == NULL) {
+ printk("FAT: unable to read boot sector\n");
+ goto out_fail;
+ }
+
+/*
+ * The DOS3 partition size limit is *not* 32M as many people think.
+ * Instead, it is 64K sectors (with the usual sector size being
+ * 512 bytes, leading to a 32M limit).
+ *
+ * DOS 3 partition managers got around this problem by faking a
+ * larger sector size, ie treating multiple physical sectors as
+ * a single logical sector.
+ *
+ * We can accommodate this scheme by adjusting our cluster size,
+ * fat_start, and data_start by an appropriate value.
+ *
+ * (by Drew Eckhardt)
+ */
+
+
+ b = (struct fat_boot_sector *) bh->b_data;
+ logical_sector_size =
+ CF_LE_W(get_unaligned((unsigned short *) &b->sector_size));
+ if (!logical_sector_size
+ || (logical_sector_size & (logical_sector_size - 1))) {
+ printk("FAT: bogus logical sector size %d\n",
+ logical_sector_size);
+ brelse(bh);
+ goto out_invalid;
+ }
+
+ sbi->cluster_size = b->cluster_size;
+ if (!sbi->cluster_size
+ || (sbi->cluster_size & (sbi->cluster_size - 1))) {
+ printk("FAT: bogus cluster size %d\n", sbi->cluster_size);
+ brelse(bh);
+ goto out_invalid;
+ }
+
+ if (logical_sector_size < hard_blksize) {
+ printk("FAT: logical sector size too small for device"
+ " (logical sector size = %d)\n", logical_sector_size);
+ brelse(bh);
+ goto out_invalid;
+ }
+
+ sbi->cluster_bits = ffs(logical_sector_size * sbi->cluster_size) - 1;
+ sbi->fats = b->fats;
+ sbi->fat_start = CF_LE_W(b->reserved);
+ sbi->prev_free = 0;
+ if (!b->fat_length && b->fat32_length) {
+ struct fat_boot_fsinfo *fsinfo;
+ struct buffer_head *fsinfo_bh;
+ int fsinfo_block, fsinfo_offset;
+
+ /* Must be FAT32 */
+ fat32 = 1;
+ sbi->fat_length = CF_LE_L(b->fat32_length);
+ sbi->root_cluster = CF_LE_L(b->root_cluster);
+
+ sbi->fsinfo_sector = CF_LE_W(b->info_sector);
+ /* MC - if info_sector is 0, don't multiply by 0 */
+ if (sbi->fsinfo_sector == 0)
+ sbi->fsinfo_sector = 1;
+
+ fsinfo_block =
+ (sbi->fsinfo_sector * logical_sector_size) / hard_blksize;
+ fsinfo_offset =
+ (sbi->fsinfo_sector * logical_sector_size) % hard_blksize;
+ fsinfo_bh = bh;
+ if (fsinfo_block != 0) {
+ fsinfo_bh = sb_bread(sb, fsinfo_block);
+ if (fsinfo_bh == NULL) {
+ printk("FAT: bread failed, FSINFO block"
+ " (blocknr = %d)\n", fsinfo_block);
+ brelse(bh);
+ goto out_invalid;
+ }
+ }
+ fsinfo = (struct fat_boot_fsinfo *)&fsinfo_bh->b_data[fsinfo_offset];
+ if (!IS_FSINFO(fsinfo)) {
+ printk("FAT: Did not find valid FSINFO signature.\n"
+ "Found signature1 0x%x signature2 0x%x sector=%ld.\n",
+ CF_LE_L(fsinfo->signature1),
+ CF_LE_L(fsinfo->signature2),
+ sbi->fsinfo_sector);
+ } else {
+ sbi->free_clusters = CF_LE_L(fsinfo->free_clusters);
+ sbi->prev_free = CF_LE_L(fsinfo->next_cluster);
+ }
+
+ if (fsinfo_block != 0)
+ brelse(fsinfo_bh);
+ } else {
+ fat32 = 0;
+ sbi->fat_length = CF_LE_W(b->fat_length);
+ sbi->root_cluster = 0;
+ sbi->free_clusters = -1; /* Don't know yet */
+ }
+
+ sbi->dir_per_block = logical_sector_size / sizeof(struct msdos_dir_entry);
+ sbi->dir_per_block_bits = ffs(sbi->dir_per_block) - 1;
+
+ sbi->dir_start = sbi->fat_start + sbi->fats * sbi->fat_length;
+ sbi->dir_entries =
+ CF_LE_W(get_unaligned((unsigned short *)&b->dir_entries));
+ rootdir_sectors = sbi->dir_entries
+ * sizeof(struct msdos_dir_entry) / logical_sector_size;
+ sbi->data_start = sbi->dir_start + rootdir_sectors;
+ total_sectors = CF_LE_W(get_unaligned((unsigned short *)&b->sectors));
+ if (total_sectors == 0)
+ total_sectors = CF_LE_L(b->total_sect);
+ sbi->clusters = (total_sectors - sbi->data_start) / sbi->cluster_size;
+
+ error = 0;
+ if (!error) {
+ sbi->fat_bits = fat32 ? 32 :
+ (fat ? fat :
+ (sbi->clusters > MSDOS_FAT12 ? 16 : 12));
+ fat_clusters =
+ sbi->fat_length * logical_sector_size * 8 / sbi->fat_bits;
+ error = !sbi->fats || (sbi->dir_entries & (sbi->dir_per_block - 1))
+ || sbi->clusters + 2 > fat_clusters + MSDOS_MAX_EXTRA
+ || logical_sector_size < 512
+ || PAGE_CACHE_SIZE < logical_sector_size
+ || !b->secs_track || !b->heads;
+ }
+ brelse(bh);
+
+ if (error)
+ goto out_invalid;
+
+ sb->s_blocksize = logical_sector_size;
+ sb->s_blocksize_bits = ffs(logical_sector_size) - 1;
+ set_blocksize(sb->s_dev, sb->s_blocksize);
+ sbi->cvf_format = &default_cvf;
+ if (!strcmp(cvf_format, "none"))
+ i = -1;
+ else
+ i = detect_cvf(sb,cvf_format);
+ if (i >= 0)
+ error = cvf_formats[i]->mount_cvf(sb, cvf_options);
+ if (error || debug) {
+ /* The MSDOS_CAN_BMAP is obsolete, but left just to remember */
+ printk("[MS-DOS FS Rel. 12,FAT %d,check=%c,conv=%c,"
+ "uid=%d,gid=%d,umask=%03o%s]\n",
+ sbi->fat_bits,opts.name_check,
+ opts.conversion,opts.fs_uid,opts.fs_gid,opts.fs_umask,
+ MSDOS_CAN_BMAP(sbi) ? ",bmap" : "");
+ printk("[me=0x%x,cs=%d,#f=%d,fs=%d,fl=%ld,ds=%ld,de=%d,data=%ld,"
+ "se=%u,ts=%u,ls=%d,rc=%ld,fc=%u]\n",
+ b->media, sbi->cluster_size, sbi->fats,
+ sbi->fat_start, sbi->fat_length, sbi->dir_start,
+ sbi->dir_entries, sbi->data_start,
+ CF_LE_W(get_unaligned((unsigned short *)&b->sectors)),
+ CF_LE_L(b->total_sect), logical_sector_size,
+ sbi->root_cluster, sbi->free_clusters);
+ printk ("hard sector size = %d\n", hard_blksize);
+ }
+ if (i < 0)
+ if (sbi->clusters + 2 > fat_clusters)
+ sbi->clusters = fat_clusters - 2;
+ if (error)
+ goto out_invalid;
+
+ sb->s_magic = MSDOS_SUPER_MAGIC;
+ /* set up enough so that it can read an inode */
+ init_MUTEX(&sbi->fat_lock);
+
+ cp = opts.codepage ? opts.codepage : 437;
+ sprintf(buf, "cp%d", cp);
+ sbi->nls_disk = load_nls(buf);
+ if (! sbi->nls_disk) {
+ /* Fail only if explicit charset specified */
+ if (opts.codepage != 0)
+ goto out_fail;
+ sbi->options.codepage = 0; /* already 0?? */
+ sbi->nls_disk = load_nls_default();
+ }
+
+ sbi->nls_io = NULL;
+ if (sbi->options.isvfat && !opts.utf8) {
+ p = opts.iocharset ? opts.iocharset : CONFIG_NLS_DEFAULT;
+ sbi->nls_io = load_nls(p);
+ if (! sbi->nls_io)
+ /* Fail only if explicit charset specified */
+ if (opts.iocharset)
+ goto out_unload_nls;
+ }
+ if (! sbi->nls_io)
+ sbi->nls_io = load_nls_default();
+
+ root_inode = new_inode(sb);
+ if (!root_inode)
+ goto out_unload_nls;
+ root_inode->i_ino = MSDOS_ROOT_INO;
+ fat_read_root(root_inode);
+ insert_inode_hash(root_inode);
+ sb->s_root = d_alloc_root(root_inode);
+ if (!sb->s_root)
+ goto out_no_root;
+ if(i >= 0) {
+ sbi->cvf_format = cvf_formats[i];
+ ++cvf_format_use_count[i];
+ }
+ return sb;
+
+out_no_root:
+ printk("FAT: get root inode failed\n");
+ iput(root_inode);
+ unload_nls(sbi->nls_io);
+out_unload_nls:
+ unload_nls(sbi->nls_disk);
+ goto out_fail;
+out_invalid:
+ if (!silent) {
+ printk("VFS: Can't find a valid FAT filesystem on dev %s.\n",
+ kdevname(sb->s_dev));
+ }
+out_fail:
+ if (opts.iocharset) {
+ printk("FAT: freeing iocharset=%s\n", opts.iocharset);
+ kfree(opts.iocharset);
+ }
+ if(sbi->private_data)
+ kfree(sbi->private_data);
+ sbi->private_data = NULL;
+
+ return NULL;
+}
+
+int fat_statfs(struct super_block *sb,struct statfs *buf)
+{
+ int free,nr;
+
+ if (MSDOS_SB(sb)->cvf_format &&
+ MSDOS_SB(sb)->cvf_format->cvf_statfs)
+ return MSDOS_SB(sb)->cvf_format->cvf_statfs(sb,buf,
+ sizeof(struct statfs));
+
+ lock_fat(sb);
+ if (MSDOS_SB(sb)->free_clusters != -1)
+ free = MSDOS_SB(sb)->free_clusters;
+ else {
+ free = 0;
+ for (nr = 2; nr < MSDOS_SB(sb)->clusters+2; nr++)
+ if (!fat_access(sb,nr,-1)) free++;
+ MSDOS_SB(sb)->free_clusters = free;
+ }
+ unlock_fat(sb);
+ buf->f_type = sb->s_magic;
+ buf->f_bsize = 1 << MSDOS_SB(sb)->cluster_bits;
+ buf->f_blocks = MSDOS_SB(sb)->clusters;
+ buf->f_bfree = free;
+ buf->f_bavail = free;
+ buf->f_namelen = MSDOS_SB(sb)->options.isvfat ? 260 : 12;
+ return 0;
+}
+
+static int is_exec(char *extension)
+{
+ char *exe_extensions = "EXECOMBAT", *walk;
+
+ for (walk = exe_extensions; *walk; walk += 3)
+ if (!strncmp(extension, walk, 3))
+ return 1;
+ return 0;
+}
+
+static int fat_writepage(struct page *page)
+{
+ return block_write_full_page(page,fat_get_block);
+}
+static int fat_readpage(struct file *file, struct page *page)
+{
+ return block_read_full_page(page,fat_get_block);
+}
+static int fat_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
+{
+ return cont_prepare_write(page,from,to,fat_get_block,
+ &MSDOS_I(page->mapping->host)->mmu_private);
+}
+static int _fat_bmap(struct address_space *mapping, long block)
+{
+ return generic_block_bmap(mapping,block,fat_get_block);
+}
+static struct address_space_operations fat_aops = {
+ readpage: fat_readpage,
+ writepage: fat_writepage,
+ sync_page: block_sync_page,
+ prepare_write: fat_prepare_write,
+ commit_write: generic_commit_write,
+ bmap: _fat_bmap
+};
+
+/* doesn't deal with root inode */
+static void fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de)
+{
+ struct super_block *sb = inode->i_sb;
+ struct msdos_sb_info *sbi = MSDOS_SB(sb);
+ int nr;
+
+ INIT_LIST_HEAD(&MSDOS_I(inode)->i_fat_hash);
+ MSDOS_I(inode)->i_pos = 0;
+ MSDOS_I(inode)->i_fat_inode = inode;
+ inode->i_uid = sbi->options.fs_uid;
+ inode->i_gid = sbi->options.fs_gid;
+ inode->i_version = ++event;
+ inode->i_generation = CURRENT_TIME;
+
+ if ((de->attr & ATTR_DIR) && !IS_FREE(de->name)) {
+ inode->i_generation &= ~1;
+ inode->i_mode = MSDOS_MKMODE(de->attr,S_IRWXUGO &
+ ~sbi->options.fs_umask) | S_IFDIR;
+ inode->i_op = sbi->dir_ops;
+ inode->i_fop = &fat_dir_operations;
+
+ MSDOS_I(inode)->i_start = CF_LE_W(de->start);
+ if (sbi->fat_bits == 32)
+ MSDOS_I(inode)->i_start |= (CF_LE_W(de->starthi) << 16);
+
+ MSDOS_I(inode)->i_logstart = MSDOS_I(inode)->i_start;
+ inode->i_nlink = fat_subdirs(inode);
+ /* includes .., compensating for "self" */
+#ifdef DEBUG
+ if (!inode->i_nlink) {
+ printk("directory %d: i_nlink == 0\n",inode->i_ino);
+ inode->i_nlink = 1;
+ }
+#endif
+ if ((nr = MSDOS_I(inode)->i_start) != 0)
+ while (nr != -1) {
+ inode->i_size += 1 << sbi->cluster_bits;
+ if (!(nr = fat_access(sb, nr, -1))) {
+ printk("Directory %ld: bad FAT\n",
+ inode->i_ino);
+ break;
+ }
+ }
+ MSDOS_I(inode)->mmu_private = inode->i_size;
+ } else { /* not a directory */
+ inode->i_generation |= 1;
+ inode->i_mode = MSDOS_MKMODE(de->attr,
+ ((sbi->options.showexec &&
+ !is_exec(de->ext))
+ ? S_IRUGO|S_IWUGO : S_IRWXUGO)
+ & ~sbi->options.fs_umask) | S_IFREG;
+ MSDOS_I(inode)->i_start = CF_LE_W(de->start);
+ if (sbi->fat_bits == 32)
+ MSDOS_I(inode)->i_start |= (CF_LE_W(de->starthi) << 16);
+
+ MSDOS_I(inode)->i_logstart = MSDOS_I(inode)->i_start;
+ inode->i_size = CF_LE_L(de->size);
+ inode->i_op = &fat_file_inode_operations;
+ inode->i_fop = &fat_file_operations;
+ inode->i_mapping->a_ops = &fat_aops;
+ MSDOS_I(inode)->mmu_private = inode->i_size;
+ }
+ if(de->attr & ATTR_SYS)
+ if (sbi->options.sys_immutable)
+ inode->i_flags |= S_IMMUTABLE;
+ MSDOS_I(inode)->i_attrs = de->attr & ATTR_UNUSED;
+ /* this is as close to the truth as we can get ... */
+ inode->i_blksize = 1 << sbi->cluster_bits;
+ inode->i_blocks = ((inode->i_size + inode->i_blksize - 1)
+ & ~(inode->i_blksize - 1)) >> 9;
+ inode->i_mtime = inode->i_atime =
+ date_dos2unix(CF_LE_W(de->time),CF_LE_W(de->date));
+ inode->i_ctime =
+ MSDOS_SB(sb)->options.isvfat
+ ? date_dos2unix(CF_LE_W(de->ctime),CF_LE_W(de->cdate))
+ : inode->i_mtime;
+ MSDOS_I(inode)->i_ctime_ms = de->ctime_ms;
+}
+
+void fat_write_inode(struct inode *inode, int wait)
+{
+ struct super_block *sb = inode->i_sb;
+ struct buffer_head *bh;
+ struct msdos_dir_entry *raw_entry;
+ loff_t i_pos;
+
+retry:
+ i_pos = MSDOS_I(inode)->i_pos;
+ if (inode->i_ino == MSDOS_ROOT_INO || !i_pos) {
+ return;
+ }
+ lock_kernel();
+ if (!(bh = fat_bread(sb, i_pos >> MSDOS_SB(sb)->dir_per_block_bits))) {
+ printk("dev = %s, i_pos = %llu\n", kdevname(inode->i_dev), i_pos);
+ fat_fs_panic(sb, "msdos_write_inode: unable to read i-node block");
+ unlock_kernel();
+ return;
+ }
+ spin_lock(&fat_inode_lock);
+ if (i_pos != MSDOS_I(inode)->i_pos) {
+ spin_unlock(&fat_inode_lock);
+ fat_brelse(sb, bh);
+ unlock_kernel();
+ goto retry;
+ }
+
+ raw_entry = &((struct msdos_dir_entry *) (bh->b_data))
+ [i_pos & (MSDOS_SB(sb)->dir_per_block - 1)];
+ if (S_ISDIR(inode->i_mode)) {
+ raw_entry->attr = ATTR_DIR;
+ raw_entry->size = 0;
+ }
+ else {
+ raw_entry->attr = ATTR_NONE;
+ raw_entry->size = CT_LE_L(inode->i_size);
+ }
+ raw_entry->attr |= MSDOS_MKATTR(inode->i_mode) |
+ MSDOS_I(inode)->i_attrs;
+ raw_entry->start = CT_LE_W(MSDOS_I(inode)->i_logstart);
+ raw_entry->starthi = CT_LE_W(MSDOS_I(inode)->i_logstart >> 16);
+ fat_date_unix2dos(inode->i_mtime,&raw_entry->time,&raw_entry->date);
+ raw_entry->time = CT_LE_W(raw_entry->time);
+ raw_entry->date = CT_LE_W(raw_entry->date);
+ if (MSDOS_SB(sb)->options.isvfat) {
+ fat_date_unix2dos(inode->i_ctime,&raw_entry->ctime,&raw_entry->cdate);
+ raw_entry->ctime_ms = MSDOS_I(inode)->i_ctime_ms;
+ raw_entry->ctime = CT_LE_W(raw_entry->ctime);
+ raw_entry->cdate = CT_LE_W(raw_entry->cdate);
+ }
+ spin_unlock(&fat_inode_lock);
+ fat_mark_buffer_dirty(sb, bh);
+ fat_brelse(sb, bh);
+ unlock_kernel();
+}
+
+
+int fat_notify_change(struct dentry * dentry, struct iattr * attr)
+{
+ struct super_block *sb = dentry->d_sb;
+ struct inode *inode = dentry->d_inode;
+ int error;
+
+ /* FAT cannot truncate to a longer file */
+ if (attr->ia_valid & ATTR_SIZE) {
+ if (attr->ia_size > inode->i_size)
+ return -EPERM;
+ }
+
+ error = inode_change_ok(inode, attr);
+ if (error)
+ return MSDOS_SB(sb)->options.quiet ? 0 : error;
+
+ if (((attr->ia_valid & ATTR_UID) &&
+ (attr->ia_uid != MSDOS_SB(sb)->options.fs_uid)) ||
+ ((attr->ia_valid & ATTR_GID) &&
+ (attr->ia_gid != MSDOS_SB(sb)->options.fs_gid)) ||
+ ((attr->ia_valid & ATTR_MODE) &&
+ (attr->ia_mode & ~MSDOS_VALID_MODE)))
+ error = -EPERM;
+
+ if (error)
+ return MSDOS_SB(sb)->options.quiet ? 0 : error;
+
+ error = inode_setattr(inode, attr);
+ if (error)
+ return error;
+
+ if (S_ISDIR(inode->i_mode))
+ inode->i_mode |= S_IXUGO;
+
+ inode->i_mode = ((inode->i_mode & S_IFMT) | ((((inode->i_mode & S_IRWXU
+ & ~MSDOS_SB(sb)->options.fs_umask) | S_IRUSR) >> 6)*S_IXUGO)) &
+ ~MSDOS_SB(sb)->options.fs_umask;
+ return 0;
+}
+MODULE_LICENSE("GPL");
diff --git a/fs/fat/misc.c b/fs/fat/misc.c
new file mode 100644
index 00000000000000..9c01afc8b897cd
--- /dev/null
+++ b/fs/fat/misc.c
@@ -0,0 +1,566 @@
+/*
+ * linux/fs/fat/misc.c
+ *
+ * Written 1992,1993 by Werner Almesberger
+ * 22/11/2000 - Fixed fat_date_unix2dos for dates earlier than 01/01/1980
+ * and date_dos2unix for date==0 by Igor Zhbanov(bsg@uniyar.ac.ru)
+ */
+
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+
+#if 0
+# define PRINTK(x) printk x
+#else
+# define PRINTK(x)
+#endif
+#define Printk(x) printk x
+
+/* Well-known binary file extensions - of course there are many more */
+
+static char ascii_extensions[] =
+ "TXT" "ME " "HTM" "1ST" "LOG" " " /* text files */
+ "C " "H " "CPP" "LIS" "PAS" "FOR" /* programming languages */
+ "F " "MAK" "INC" "BAS" /* programming languages */
+ "BAT" "SH " /* program code :) */
+ "INI" /* config files */
+ "PBM" "PGM" "DXF" /* graphics */
+ "TEX"; /* TeX */
+
+
+/*
+ * fat_fs_panic reports a severe file system problem and sets the file system
+ * read-only. The file system can be made writable again by remounting it.
+ */
+
+void fat_fs_panic(struct super_block *s,const char *msg)
+{
+ int not_ro;
+
+ not_ro = !(s->s_flags & MS_RDONLY);
+ if (not_ro) s->s_flags |= MS_RDONLY;
+ printk("Filesystem panic (dev %s).\n %s\n", kdevname(s->s_dev), msg);
+ if (not_ro)
+ printk(" File system has been set read-only\n");
+}
+
+
+/*
+ * fat_is_binary selects optional text conversion based on the conversion mode
+ * and the extension part of the file name.
+ */
+
+int fat_is_binary(char conversion,char *extension)
+{
+ char *walk;
+
+ switch (conversion) {
+ case 'b':
+ return 1;
+ case 't':
+ return 0;
+ case 'a':
+ for (walk = ascii_extensions; *walk; walk += 3)
+ if (!strncmp(extension,walk,3)) return 0;
+ return 1; /* default binary conversion */
+ default:
+ printk("Invalid conversion mode - defaulting to "
+ "binary.\n");
+ return 1;
+ }
+}
+
+void lock_fat(struct super_block *sb)
+{
+ down(&(MSDOS_SB(sb)->fat_lock));
+}
+
+void unlock_fat(struct super_block *sb)
+{
+ up(&(MSDOS_SB(sb)->fat_lock));
+}
+
+/* Flushes the number of free clusters on FAT32 */
+/* XXX: Need to write one per FSINFO block. Currently only writes 1 */
+void fat_clusters_flush(struct super_block *sb)
+{
+ struct buffer_head *bh;
+ struct fat_boot_fsinfo *fsinfo;
+
+ bh = fat_bread(sb, MSDOS_SB(sb)->fsinfo_sector);
+ if (bh == NULL) {
+ printk("FAT bread failed in fat_clusters_flush\n");
+ return;
+ }
+
+ fsinfo = (struct fat_boot_fsinfo *)bh->b_data;
+ /* Sanity check */
+ if (!IS_FSINFO(fsinfo)) {
+ printk("FAT: Did not find valid FSINFO signature.\n"
+ "Found signature1 0x%x signature2 0x%x sector=%ld.\n",
+ CF_LE_L(fsinfo->signature1), CF_LE_L(fsinfo->signature2),
+ MSDOS_SB(sb)->fsinfo_sector);
+ return;
+ }
+ fsinfo->free_clusters = CF_LE_L(MSDOS_SB(sb)->free_clusters);
+ fsinfo->next_cluster = CF_LE_L(MSDOS_SB(sb)->prev_free);
+ fat_mark_buffer_dirty(sb, bh);
+ fat_brelse(sb, bh);
+}
+
+/*
+ * fat_add_cluster tries to allocate a new cluster and adds it to the
+ * file represented by inode.
+ */
+int fat_add_cluster(struct inode *inode)
+{
+ struct super_block *sb = inode->i_sb;
+ int count, nr, limit, last, curr, file_cluster;
+ int cluster_size = MSDOS_SB(sb)->cluster_size;
+ int res = -ENOSPC;
+
+ lock_fat(sb);
+
+ if (MSDOS_SB(sb)->free_clusters == 0) {
+ unlock_fat(sb);
+ return res;
+ }
+ limit = MSDOS_SB(sb)->clusters;
+ nr = limit; /* to keep GCC happy */
+ for (count = 0; count < limit; count++) {
+ nr = ((count + MSDOS_SB(sb)->prev_free) % limit) + 2;
+ if (fat_access(sb, nr, -1) == 0)
+ break;
+ }
+ if (count >= limit) {
+ MSDOS_SB(sb)->free_clusters = 0;
+ unlock_fat(sb);
+ return res;
+ }
+
+ MSDOS_SB(sb)->prev_free = (count + MSDOS_SB(sb)->prev_free + 1) % limit;
+ fat_access(sb, nr, EOF_FAT(sb));
+ if (MSDOS_SB(sb)->free_clusters != -1)
+ MSDOS_SB(sb)->free_clusters--;
+ if (MSDOS_SB(sb)->fat_bits == 32)
+ fat_clusters_flush(sb);
+
+ unlock_fat(sb);
+
+ /* We must locate the last cluster of the file to add this
+ new one (nr) to the end of the link list (the FAT).
+
+ Here file_cluster will be the number of the last cluster of the
+ file (before we add nr).
+
+ last is the corresponding cluster number on the disk. We will
+ use last to plug the nr cluster. We will use file_cluster to
+ update the cache.
+ */
+ last = file_cluster = 0;
+ if ((curr = MSDOS_I(inode)->i_start) != 0) {
+ fat_cache_lookup(inode, INT_MAX, &last, &curr);
+ file_cluster = last;
+ while (curr && curr != -1){
+ file_cluster++;
+ if (!(curr = fat_access(sb, last = curr,-1))) {
+ fat_fs_panic(sb, "File without EOF");
+ return res;
+ }
+ }
+ }
+ if (last) {
+ fat_access(sb, last, nr);
+ fat_cache_add(inode, file_cluster, nr);
+ } else {
+ MSDOS_I(inode)->i_start = nr;
+ MSDOS_I(inode)->i_logstart = nr;
+ mark_inode_dirty(inode);
+ }
+ if (file_cluster
+ != inode->i_blocks / cluster_size / (sb->s_blocksize / 512)) {
+ printk ("file_cluster badly computed!!! %d <> %ld\n",
+ file_cluster,
+ inode->i_blocks / cluster_size / (sb->s_blocksize / 512));
+ fat_cache_inval_inode(inode);
+ }
+ inode->i_blocks += (1 << MSDOS_SB(sb)->cluster_bits) / 512;
+
+ return nr;
+}
+
+struct buffer_head *fat_extend_dir(struct inode *inode)
+{
+ struct super_block *sb = inode->i_sb;
+ int nr, sector, last_sector;
+ struct buffer_head *bh, *res = NULL;
+ int cluster_size = MSDOS_SB(sb)->cluster_size;
+
+ if (MSDOS_SB(sb)->fat_bits != 32) {
+ if (inode->i_ino == MSDOS_ROOT_INO)
+ return res;
+ }
+
+ nr = fat_add_cluster(inode);
+ if (nr < 0)
+ return res;
+
+ sector = MSDOS_SB(sb)->data_start + (nr - 2) * cluster_size;
+ last_sector = sector + cluster_size;
+ if (MSDOS_SB(sb)->cvf_format && MSDOS_SB(sb)->cvf_format->zero_out_cluster)
+ MSDOS_SB(sb)->cvf_format->zero_out_cluster(inode, nr);
+ else {
+ for ( ; sector < last_sector; sector++) {
+#ifdef DEBUG
+ printk("zeroing sector %d\n", sector);
+#endif
+ if (!(bh = fat_getblk(sb, sector)))
+ printk("getblk failed\n");
+ else {
+ memset(bh->b_data, 0, sb->s_blocksize);
+ fat_set_uptodate(sb, bh, 1);
+ fat_mark_buffer_dirty(sb, bh);
+ if (!res)
+ res = bh;
+ else
+ fat_brelse(sb, bh);
+ }
+ }
+ }
+ if (inode->i_size & (sb->s_blocksize - 1)) {
+ fat_fs_panic(sb, "Odd directory size");
+ inode->i_size = (inode->i_size + sb->s_blocksize)
+ & ~(sb->s_blocksize - 1);
+ }
+ inode->i_size += 1 << MSDOS_SB(sb)->cluster_bits;
+ MSDOS_I(inode)->mmu_private += 1 << MSDOS_SB(sb)->cluster_bits;
+ mark_inode_dirty(inode);
+
+ return res;
+}
+
+/* Linear day numbers of the respective 1sts in non-leap years. */
+
+static int day_n[] = { 0,31,59,90,120,151,181,212,243,273,304,334,0,0,0,0 };
+ /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */
+
+
+extern struct timezone sys_tz;
+
+
+/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
+
+int date_dos2unix(unsigned short time,unsigned short date)
+{
+ int month,year,secs;
+
+ /* first subtract and mask after that... Otherwise, if
+ date == 0, bad things happen */
+ month = ((date >> 5) - 1) & 15;
+ year = date >> 9;
+ secs = (time & 31)*2+60*((time >> 5) & 63)+(time >> 11)*3600+86400*
+ ((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 &&
+ month < 2 ? 1 : 0)+3653);
+ /* days since 1.1.70 plus 80's leap day */
+ secs += sys_tz.tz_minuteswest*60;
+ return secs;
+}
+
+
+/* Convert linear UNIX date to a MS-DOS time/date pair. */
+
+void fat_date_unix2dos(int unix_date,unsigned short *time,
+ unsigned short *date)
+{
+ int day,year,nl_day,month;
+
+ unix_date -= sys_tz.tz_minuteswest*60;
+
+ /* Jan 1 GMT 00:00:00 1980. But what about another time zone? */
+ if (unix_date < 315532800)
+ unix_date = 315532800;
+
+ *time = (unix_date % 60)/2+(((unix_date/60) % 60) << 5)+
+ (((unix_date/3600) % 24) << 11);
+ day = unix_date/86400-3652;
+ year = day/365;
+ if ((year+3)/4+365*year > day) year--;
+ day -= (year+3)/4+365*year;
+ if (day == 59 && !(year & 3)) {
+ nl_day = day;
+ month = 2;
+ }
+ else {
+ nl_day = (year & 3) || day <= 59 ? day : day-1;
+ for (month = 0; month < 12; month++)
+ if (day_n[month] > nl_day) break;
+ }
+ *date = nl_day-day_n[month-1]+1+(month << 5)+(year << 9);
+}
+
+
+/* Returns the inode number of the directory entry at offset pos. If bh is
+ non-NULL, it is brelse'd before. Pos is incremented. The buffer header is
+ returned in bh.
+ AV. Most often we do it item-by-item. Makes sense to optimize.
+ AV. OK, there we go: if both bh and de are non-NULL we assume that we just
+ AV. want the next entry (took one explicit de=NULL in vfat/namei.c).
+ AV. It's done in fat_get_entry() (inlined), here the slow case lives.
+ AV. Additionally, when we return -1 (i.e. reached the end of directory)
+ AV. we make bh NULL.
+ */
+
+int fat__get_entry(struct inode *dir, loff_t *pos,struct buffer_head **bh,
+ struct msdos_dir_entry **de, loff_t *i_pos)
+{
+ struct super_block *sb = dir->i_sb;
+ struct msdos_sb_info *sbi = MSDOS_SB(sb);
+ int sector;
+ loff_t offset;
+
+ while (1) {
+ offset = *pos;
+ PRINTK (("get_entry offset %d\n",offset));
+ if (*bh)
+ fat_brelse(sb, *bh);
+ *bh = NULL;
+ if ((sector = fat_bmap(dir,offset >> sb->s_blocksize_bits)) == -1)
+ return -1;
+ PRINTK (("get_entry sector %d %p\n",sector,*bh));
+ PRINTK (("get_entry sector apres brelse\n"));
+ if (!sector)
+ return -1; /* beyond EOF */
+ *pos += sizeof(struct msdos_dir_entry);
+ if (!(*bh = fat_bread(sb, sector))) {
+ printk("Directory sread (sector 0x%x) failed\n",sector);
+ continue;
+ }
+ PRINTK (("get_entry apres sread\n"));
+
+ offset &= sb->s_blocksize - 1;
+ *de = (struct msdos_dir_entry *) ((*bh)->b_data + offset);
+ *i_pos = ((loff_t)sector << sbi->dir_per_block_bits) + (offset >> MSDOS_DIR_BITS);
+
+ return 0;
+ }
+}
+
+
+/*
+ * Now an ugly part: this set of directory scan routines works on clusters
+ * rather than on inodes and sectors. They are necessary to locate the '..'
+ * directory "inode". raw_scan_sector operates in four modes:
+ *
+ * name number ino action
+ * -------- -------- -------- -------------------------------------------------
+ * non-NULL - X Find an entry with that name
+ * NULL non-NULL non-NULL Find an entry whose data starts at *number
+ * NULL non-NULL NULL Count subdirectories in *number. (*)
+ * NULL NULL non-NULL Find an empty entry
+ *
+ * (*) The return code should be ignored. It DOES NOT indicate success or
+ * failure. *number has to be initialized to zero.
+ *
+ * - = not used, X = a value is returned unless NULL
+ *
+ * If res_bh is non-NULL, the buffer is not deallocated but returned to the
+ * caller on success. res_de is set accordingly.
+ *
+ * If cont is non-zero, raw_found continues with the entry after the one
+ * res_bh/res_de point to.
+ */
+
+
+#define RSS_NAME /* search for name */ \
+ done = !strncmp(data[entry].name,name,MSDOS_NAME) && \
+ !(data[entry].attr & ATTR_VOLUME);
+
+#define RSS_START /* search for start cluster */ \
+ done = !IS_FREE(data[entry].name) \
+ && ( \
+ ( \
+ (sbi->fat_bits != 32) ? 0 : (CF_LE_W(data[entry].starthi) << 16) \
+ ) \
+ | CF_LE_W(data[entry].start) \
+ ) == *number;
+
+#define RSS_FREE /* search for free entry */ \
+ { \
+ done = IS_FREE(data[entry].name); \
+ }
+
+#define RSS_COUNT /* count subdirectories */ \
+ { \
+ done = 0; \
+ if (!IS_FREE(data[entry].name) && (data[entry].attr & ATTR_DIR)) \
+ (*number)++; \
+ }
+
+static int raw_scan_sector(struct super_block *sb, int sector,
+ const char *name, int *number, loff_t *i_pos,
+ struct buffer_head **res_bh,
+ struct msdos_dir_entry **res_de)
+{
+ struct msdos_sb_info *sbi = MSDOS_SB(sb);
+ struct buffer_head *bh;
+ struct msdos_dir_entry *data;
+ int entry,start,done;
+
+ if (!(bh = fat_bread(sb, sector)))
+ return -EIO;
+ data = (struct msdos_dir_entry *) bh->b_data;
+ for (entry = 0; entry < sbi->dir_per_block; entry++) {
+/* RSS_COUNT: if (data[entry].name == name) done=true else done=false. */
+ if (name) {
+ RSS_NAME
+ } else {
+ if (!i_pos) RSS_COUNT
+ else {
+ if (number) RSS_START
+ else RSS_FREE
+ }
+ }
+ if (done) {
+ if (i_pos) {
+ *i_pos = ((loff_t)sector << sbi->dir_per_block_bits) + entry;
+ }
+ start = CF_LE_W(data[entry].start);
+ if (sbi->fat_bits == 32)
+ start |= (CF_LE_W(data[entry].starthi) << 16);
+
+ if (!res_bh)
+ fat_brelse(sb, bh);
+ else {
+ *res_bh = bh;
+ *res_de = &data[entry];
+ }
+ return start;
+ }
+ }
+ fat_brelse(sb, bh);
+ return -ENOENT;
+}
+
+
+/*
+ * raw_scan_root performs raw_scan_sector on the root directory until the
+ * requested entry is found or the end of the directory is reached.
+ */
+
+static int raw_scan_root(struct super_block *sb, const char *name,
+ int *number, loff_t *i_pos,
+ struct buffer_head **res_bh,
+ struct msdos_dir_entry **res_de)
+{
+ int count,cluster;
+
+ for (count = 0;
+ count < MSDOS_SB(sb)->dir_entries / MSDOS_SB(sb)->dir_per_block;
+ count++) {
+ cluster = raw_scan_sector(sb, MSDOS_SB(sb)->dir_start + count,
+ name, number, i_pos, res_bh, res_de);
+ if (cluster >= 0)
+ return cluster;
+ }
+ return -ENOENT;
+}
+
+
+/*
+ * raw_scan_nonroot performs raw_scan_sector on a non-root directory until the
+ * requested entry is found or the end of the directory is reached.
+ */
+
+static int raw_scan_nonroot(struct super_block *sb, int start, const char *name,
+ int *number, loff_t *i_pos,
+ struct buffer_head **res_bh,
+ struct msdos_dir_entry **res_de)
+{
+ struct msdos_sb_info *sbi = MSDOS_SB(sb);
+ int count, cluster, sector;
+
+#ifdef DEBUG
+ printk("raw_scan_nonroot: start=%d\n",start);
+#endif
+ do {
+ for (count = 0; count < sbi->cluster_size; count++) {
+ sector = (start - 2) * sbi->cluster_size
+ + count + sbi->data_start;
+ cluster = raw_scan_sector(sb, sector, name, number,
+ i_pos, res_bh, res_de);
+ if (cluster >= 0)
+ return cluster;
+ }
+ if (!(start = fat_access(sb,start,-1))) {
+ fat_fs_panic(sb,"FAT error");
+ break;
+ }
+#ifdef DEBUG
+ printk("next start: %d\n",start);
+#endif
+ }
+ while (start != -1);
+ return -ENOENT;
+}
+
+
+/*
+ * raw_scan performs raw_scan_sector on any sector.
+ *
+ * NOTE: raw_scan must not be used on a directory that is is the process of
+ * being created.
+ */
+
+static int raw_scan(struct super_block *sb, int start, const char *name,
+ loff_t *i_pos, struct buffer_head **res_bh,
+ struct msdos_dir_entry **res_de)
+{
+ if (start)
+ return raw_scan_nonroot(sb,start,name,NULL,i_pos,res_bh,res_de);
+ else
+ return raw_scan_root(sb,name,NULL,i_pos,res_bh,res_de);
+}
+
+/*
+ * fat_subdirs counts the number of sub-directories of dir. It can be run
+ * on directories being created.
+ */
+int fat_subdirs(struct inode *dir)
+{
+ struct msdos_sb_info *sbi = MSDOS_SB(dir->i_sb);
+ int number;
+
+ number = 0;
+ if ((dir->i_ino == MSDOS_ROOT_INO) && (sbi->fat_bits != 32))
+ raw_scan_root(dir->i_sb, NULL, &number, NULL, NULL, NULL);
+ else {
+ if ((dir->i_ino != MSDOS_ROOT_INO) && !MSDOS_I(dir)->i_start)
+ return 0; /* in mkdir */
+ else {
+ raw_scan_nonroot(dir->i_sb, MSDOS_I(dir)->i_start,
+ NULL, &number, NULL, NULL, NULL);
+ }
+ }
+ return number;
+}
+
+
+/*
+ * Scans a directory for a given file (name points to its formatted name) or
+ * for an empty directory slot (name is NULL). Returns an error code or zero.
+ */
+
+int fat_scan(struct inode *dir, const char *name, struct buffer_head **res_bh,
+ struct msdos_dir_entry **res_de, loff_t *i_pos)
+{
+ int res;
+
+ res = raw_scan(dir->i_sb, MSDOS_I(dir)->i_start, name, i_pos,
+ res_bh, res_de);
+ return (res < 0) ? res : 0;
+}
diff --git a/fs/fcntl.c b/fs/fcntl.c
new file mode 100644
index 00000000000000..9447229f219533
--- /dev/null
+++ b/fs/fcntl.c
@@ -0,0 +1,541 @@
+/*
+ * linux/fs/fcntl.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/file.h>
+#include <linux/dnotify.h>
+#include <linux/smp_lock.h>
+#include <linux/slab.h>
+#include <linux/iobuf.h>
+#include <linux/ptrace.h>
+
+#include <asm/poll.h>
+#include <asm/siginfo.h>
+#include <asm/uaccess.h>
+
+extern int sock_fcntl (struct file *, unsigned int cmd, unsigned long arg);
+extern int fcntl_setlease(unsigned int fd, struct file *filp, long arg);
+extern int fcntl_getlease(struct file *filp);
+
+/* Expand files. Return <0 on error; 0 nothing done; 1 files expanded,
+ * we may have blocked.
+ *
+ * Should be called with the files->file_lock spinlock held for write.
+ */
+static int expand_files(struct files_struct *files, int nr)
+{
+ int err, expand = 0;
+#ifdef FDSET_DEBUG
+ printk (KERN_ERR __FUNCTION__ " %d: nr = %d\n", current->pid, nr);
+#endif
+
+ if (nr >= files->max_fdset) {
+ expand = 1;
+ if ((err = expand_fdset(files, nr)))
+ goto out;
+ }
+ if (nr >= files->max_fds) {
+ expand = 1;
+ if ((err = expand_fd_array(files, nr)))
+ goto out;
+ }
+ err = expand;
+ out:
+#ifdef FDSET_DEBUG
+ if (err)
+ printk (KERN_ERR __FUNCTION__ " %d: return %d\n", current->pid, err);
+#endif
+ return err;
+}
+
+/*
+ * locate_fd finds a free file descriptor in the open_fds fdset,
+ * expanding the fd arrays if necessary. The files write lock will be
+ * held on exit to ensure that the fd can be entered atomically.
+ */
+
+static int locate_fd(struct files_struct *files,
+ struct file *file, int orig_start)
+{
+ unsigned int newfd;
+ int error;
+ int start;
+
+ write_lock(&files->file_lock);
+
+ error = -EINVAL;
+ if (orig_start >= current->rlim[RLIMIT_NOFILE].rlim_cur)
+ goto out;
+
+repeat:
+ /*
+ * Someone might have closed fd's in the range
+ * orig_start..files->next_fd
+ */
+ start = orig_start;
+ if (start < files->next_fd)
+ start = files->next_fd;
+
+ newfd = start;
+ if (start < files->max_fdset) {
+ newfd = find_next_zero_bit(files->open_fds->fds_bits,
+ files->max_fdset, start);
+ }
+
+ error = -EMFILE;
+ if (newfd >= current->rlim[RLIMIT_NOFILE].rlim_cur)
+ goto out;
+
+ error = expand_files(files, newfd);
+ if (error < 0)
+ goto out;
+
+ /*
+ * If we needed to expand the fs array we
+ * might have blocked - try again.
+ */
+ if (error)
+ goto repeat;
+
+ if (start <= files->next_fd)
+ files->next_fd = newfd + 1;
+
+ error = newfd;
+
+out:
+ return error;
+}
+
+static inline void allocate_fd(struct files_struct *files,
+ struct file *file, int fd)
+{
+ FD_SET(fd, files->open_fds);
+ FD_CLR(fd, files->close_on_exec);
+ write_unlock(&files->file_lock);
+ fd_install(fd, file);
+}
+
+static int dupfd(struct file *file, int start)
+{
+ struct files_struct * files = current->files;
+ int ret;
+
+ ret = locate_fd(files, file, start);
+ if (ret < 0)
+ goto out_putf;
+ allocate_fd(files, file, ret);
+ return ret;
+
+out_putf:
+ write_unlock(&files->file_lock);
+ fput(file);
+ return ret;
+}
+
+asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd)
+{
+ int err = -EBADF;
+ struct file * file, *tofree;
+ struct files_struct * files = current->files;
+
+ write_lock(&files->file_lock);
+ if (!(file = fcheck(oldfd)))
+ goto out_unlock;
+ err = newfd;
+ if (newfd == oldfd)
+ goto out_unlock;
+ err = -EBADF;
+ if (newfd >= current->rlim[RLIMIT_NOFILE].rlim_cur)
+ goto out_unlock;
+ get_file(file); /* We are now finished with oldfd */
+
+ err = expand_files(files, newfd);
+ if (err < 0)
+ goto out_fput;
+
+ /* To avoid races with open() and dup(), we will mark the fd as
+ * in-use in the open-file bitmap throughout the entire dup2()
+ * process. This is quite safe: do_close() uses the fd array
+ * entry, not the bitmap, to decide what work needs to be
+ * done. --sct */
+ /* Doesn't work. open() might be there first. --AV */
+
+ /* Yes. It's a race. In user space. Nothing sane to do */
+ err = -EBUSY;
+ tofree = files->fd[newfd];
+ if (!tofree && FD_ISSET(newfd, files->open_fds))
+ goto out_fput;
+
+ files->fd[newfd] = file;
+ FD_SET(newfd, files->open_fds);
+ FD_CLR(newfd, files->close_on_exec);
+ write_unlock(&files->file_lock);
+
+ if (tofree)
+ filp_close(tofree, files);
+ err = newfd;
+out:
+ return err;
+out_unlock:
+ write_unlock(&files->file_lock);
+ goto out;
+
+out_fput:
+ write_unlock(&files->file_lock);
+ fput(file);
+ goto out;
+}
+
+asmlinkage long sys_dup(unsigned int fildes)
+{
+ int ret = -EBADF;
+ struct file * file = fget(fildes);
+
+ if (file)
+ ret = dupfd(file, 0);
+ return ret;
+}
+
+#define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | FASYNC | O_DIRECT)
+
+static int setfl(int fd, struct file * filp, unsigned long arg)
+{
+ struct inode * inode = filp->f_dentry->d_inode;
+ int error;
+
+ /*
+ * In the case of an append-only file, O_APPEND
+ * cannot be cleared
+ */
+ if (!(arg & O_APPEND) && IS_APPEND(inode))
+ return -EPERM;
+
+ /* Did FASYNC state change? */
+ if ((arg ^ filp->f_flags) & FASYNC) {
+ if (filp->f_op && filp->f_op->fasync) {
+ error = filp->f_op->fasync(fd, filp, (arg & FASYNC) != 0);
+ if (error < 0)
+ return error;
+ }
+ }
+
+ if (arg & O_DIRECT) {
+ /*
+ * alloc_kiovec() can sleep and we are only serialized by
+ * the big kernel lock here, so abuse the i_sem to serialize
+ * this case too. We of course wouldn't need to go deep down
+ * to the inode layer, we could stay at the file layer, but
+ * we don't want to pay for the memory of a semaphore in each
+ * file structure too and we use the inode semaphore that we just
+ * pay for anyways.
+ */
+ error = 0;
+ down(&inode->i_sem);
+ if (!filp->f_iobuf)
+ error = alloc_kiovec(1, &filp->f_iobuf);
+ up(&inode->i_sem);
+ if (error < 0)
+ return error;
+ }
+
+ /* required for strict SunOS emulation */
+ if (O_NONBLOCK != O_NDELAY)
+ if (arg & O_NDELAY)
+ arg |= O_NONBLOCK;
+
+ filp->f_flags = (arg & SETFL_MASK) | (filp->f_flags & ~SETFL_MASK);
+ return 0;
+}
+
+static long do_fcntl(unsigned int fd, unsigned int cmd,
+ unsigned long arg, struct file * filp)
+{
+ long err = -EINVAL;
+
+ switch (cmd) {
+ case F_DUPFD:
+ if (arg < NR_OPEN) {
+ get_file(filp);
+ err = dupfd(filp, arg);
+ }
+ break;
+ case F_GETFD:
+ err = get_close_on_exec(fd);
+ break;
+ case F_SETFD:
+ err = 0;
+ set_close_on_exec(fd, arg&1);
+ break;
+ case F_GETFL:
+ err = filp->f_flags;
+ break;
+ case F_SETFL:
+ lock_kernel();
+ err = setfl(fd, filp, arg);
+ unlock_kernel();
+ break;
+ case F_GETLK:
+ err = fcntl_getlk(fd, (struct flock *) arg);
+ break;
+ case F_SETLK:
+ case F_SETLKW:
+ err = fcntl_setlk(fd, cmd, (struct flock *) arg);
+ break;
+ case F_GETOWN:
+ /*
+ * XXX If f_owner is a process group, the
+ * negative return value will get converted
+ * into an error. Oops. If we keep the
+ * current syscall conventions, the only way
+ * to fix this will be in libc.
+ */
+ err = filp->f_owner.pid;
+ force_successful_syscall_return();
+ break;
+ case F_SETOWN:
+ lock_kernel();
+ filp->f_owner.pid = arg;
+ filp->f_owner.uid = current->uid;
+ filp->f_owner.euid = current->euid;
+ err = 0;
+ if (S_ISSOCK (filp->f_dentry->d_inode->i_mode))
+ err = sock_fcntl (filp, F_SETOWN, arg);
+ unlock_kernel();
+ break;
+ case F_GETSIG:
+ err = filp->f_owner.signum;
+ break;
+ case F_SETSIG:
+ /* arg == 0 restores default behaviour. */
+ if (arg < 0 || arg > _NSIG) {
+ break;
+ }
+ err = 0;
+ filp->f_owner.signum = arg;
+ break;
+ case F_GETLEASE:
+ err = fcntl_getlease(filp);
+ break;
+ case F_SETLEASE:
+ err = fcntl_setlease(fd, filp, arg);
+ break;
+ case F_NOTIFY:
+ err = fcntl_dirnotify(fd, filp, arg);
+ break;
+ default:
+ /* sockets need a few special fcntls. */
+ err = -EINVAL;
+ if (S_ISSOCK (filp->f_dentry->d_inode->i_mode))
+ err = sock_fcntl (filp, cmd, arg);
+ break;
+ }
+
+ return err;
+}
+
+asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg)
+{
+ struct file * filp;
+ long err = -EBADF;
+
+ filp = fget(fd);
+ if (!filp)
+ goto out;
+
+ err = do_fcntl(fd, cmd, arg, filp);
+
+ fput(filp);
+out:
+ return err;
+}
+
+#if BITS_PER_LONG == 32
+asmlinkage long sys_fcntl64(unsigned int fd, unsigned int cmd, unsigned long arg)
+{
+ struct file * filp;
+ long err;
+
+ err = -EBADF;
+ filp = fget(fd);
+ if (!filp)
+ goto out;
+
+ switch (cmd) {
+ case F_GETLK64:
+ err = fcntl_getlk64(fd, (struct flock64 *) arg);
+ break;
+ case F_SETLK64:
+ err = fcntl_setlk64(fd, cmd, (struct flock64 *) arg);
+ break;
+ case F_SETLKW64:
+ err = fcntl_setlk64(fd, cmd, (struct flock64 *) arg);
+ break;
+ default:
+ err = do_fcntl(fd, cmd, arg, filp);
+ break;
+ }
+ fput(filp);
+out:
+ return err;
+}
+#endif
+
+/* Table to convert sigio signal codes into poll band bitmaps */
+
+static long band_table[NSIGPOLL] = {
+ POLLIN | POLLRDNORM, /* POLL_IN */
+ POLLOUT | POLLWRNORM | POLLWRBAND, /* POLL_OUT */
+ POLLIN | POLLRDNORM | POLLMSG, /* POLL_MSG */
+ POLLERR, /* POLL_ERR */
+ POLLPRI | POLLRDBAND, /* POLL_PRI */
+ POLLHUP | POLLERR /* POLL_HUP */
+};
+
+static void send_sigio_to_task(struct task_struct *p,
+ struct fown_struct *fown,
+ int fd,
+ int reason)
+{
+ if ((fown->euid != 0) &&
+ (fown->euid ^ p->suid) && (fown->euid ^ p->uid) &&
+ (fown->uid ^ p->suid) && (fown->uid ^ p->uid))
+ return;
+ switch (fown->signum) {
+ siginfo_t si;
+ default:
+ /* Queue a rt signal with the appropriate fd as its
+ value. We use SI_SIGIO as the source, not
+ SI_KERNEL, since kernel signals always get
+ delivered even if we can't queue. Failure to
+ queue in this case _should_ be reported; we fall
+ back to SIGIO in that case. --sct */
+ si.si_signo = fown->signum;
+ si.si_errno = 0;
+ si.si_code = reason;
+ /* Make sure we are called with one of the POLL_*
+ reasons, otherwise we could leak kernel stack into
+ userspace. */
+ if ((reason & __SI_MASK) != __SI_POLL)
+ BUG();
+ if (reason - POLL_IN >= NSIGPOLL)
+ si.si_band = ~0L;
+ else
+ si.si_band = band_table[reason - POLL_IN];
+ si.si_fd = fd;
+ if (!send_sig_info(fown->signum, &si, p))
+ break;
+ /* fall-through: fall back on the old plain SIGIO signal */
+ case 0:
+ send_sig(SIGIO, p, 1);
+ }
+}
+
+void send_sigio(struct fown_struct *fown, int fd, int band)
+{
+ struct task_struct * p;
+ int pid = fown->pid;
+
+ read_lock(&tasklist_lock);
+ if ( (pid > 0) && (p = find_task_by_pid(pid)) ) {
+ send_sigio_to_task(p, fown, fd, band);
+ goto out;
+ }
+ for_each_task(p) {
+ int match = p->pid;
+ if (pid < 0)
+ match = -p->pgrp;
+ if (pid != match)
+ continue;
+ send_sigio_to_task(p, fown, fd, band);
+ }
+out:
+ read_unlock(&tasklist_lock);
+}
+
+static rwlock_t fasync_lock = RW_LOCK_UNLOCKED;
+static kmem_cache_t *fasync_cache;
+
+/*
+ * fasync_helper() is used by some character device drivers (mainly mice)
+ * to set up the fasync queue. It returns negative on error, 0 if it did
+ * no changes and positive if it added/deleted the entry.
+ */
+int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
+{
+ struct fasync_struct *fa, **fp;
+ struct fasync_struct *new = NULL;
+ int result = 0;
+
+ if (on) {
+ new = kmem_cache_alloc(fasync_cache, SLAB_KERNEL);
+ if (!new)
+ return -ENOMEM;
+ }
+ write_lock_irq(&fasync_lock);
+ for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) {
+ if (fa->fa_file == filp) {
+ if(on) {
+ fa->fa_fd = fd;
+ kmem_cache_free(fasync_cache, new);
+ } else {
+ *fp = fa->fa_next;
+ kmem_cache_free(fasync_cache, fa);
+ result = 1;
+ }
+ goto out;
+ }
+ }
+
+ if (on) {
+ new->magic = FASYNC_MAGIC;
+ new->fa_file = filp;
+ new->fa_fd = fd;
+ new->fa_next = *fapp;
+ *fapp = new;
+ result = 1;
+ }
+out:
+ write_unlock_irq(&fasync_lock);
+ return result;
+}
+
+void __kill_fasync(struct fasync_struct *fa, int sig, int band)
+{
+ while (fa) {
+ struct fown_struct * fown;
+ if (fa->magic != FASYNC_MAGIC) {
+ printk(KERN_ERR "kill_fasync: bad magic number in "
+ "fasync_struct!\n");
+ return;
+ }
+ fown = &fa->fa_file->f_owner;
+ /* Don't send SIGURG to processes which have not set a
+ queued signum: SIGURG has its own default signalling
+ mechanism. */
+ if (fown->pid && !(sig == SIGURG && fown->signum == 0))
+ send_sigio(fown, fa->fa_fd, band);
+ fa = fa->fa_next;
+ }
+}
+
+void kill_fasync(struct fasync_struct **fp, int sig, int band)
+{
+ read_lock(&fasync_lock);
+ __kill_fasync(*fp, sig, band);
+ read_unlock(&fasync_lock);
+}
+
+static int __init fasync_init(void)
+{
+ fasync_cache = kmem_cache_create("fasync_cache",
+ sizeof(struct fasync_struct), 0, 0, NULL, NULL);
+ if (!fasync_cache)
+ panic("cannot create fasync slab cache");
+ return 0;
+}
+
+module_init(fasync_init)
diff --git a/fs/fifo.c b/fs/fifo.c
new file mode 100644
index 00000000000000..f5738c126bce40
--- /dev/null
+++ b/fs/fifo.c
@@ -0,0 +1,157 @@
+/*
+ * linux/fs/fifo.c
+ *
+ * written by Paul H. Hargrove
+ *
+ * Fixes:
+ * 10-06-1999, AV: fixed OOM handling in fifo_open(), moved
+ * initialization there, switched to external
+ * allocation of pipe_inode_info.
+ */
+
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+
+static void wait_for_partner(struct inode* inode, unsigned int* cnt)
+{
+ int cur = *cnt;
+ while(cur == *cnt) {
+ pipe_wait(inode);
+ if(signal_pending(current))
+ break;
+ }
+}
+
+static void wake_up_partner(struct inode* inode)
+{
+ wake_up_interruptible(PIPE_WAIT(*inode));
+}
+
+static int fifo_open(struct inode *inode, struct file *filp)
+{
+ int ret;
+
+ ret = -ERESTARTSYS;
+ lock_kernel();
+ if (down_interruptible(PIPE_SEM(*inode)))
+ goto err_nolock_nocleanup;
+
+ if (!inode->i_pipe) {
+ ret = -ENOMEM;
+ if(!pipe_new(inode))
+ goto err_nocleanup;
+ }
+ filp->f_version = 0;
+
+ switch (filp->f_mode) {
+ case 1:
+ /*
+ * O_RDONLY
+ * POSIX.1 says that O_NONBLOCK means return with the FIFO
+ * opened, even when there is no process writing the FIFO.
+ */
+ filp->f_op = &read_fifo_fops;
+ PIPE_RCOUNTER(*inode)++;
+ if (PIPE_READERS(*inode)++ == 0)
+ wake_up_partner(inode);
+
+ if (!PIPE_WRITERS(*inode)) {
+ if ((filp->f_flags & O_NONBLOCK)) {
+ /* suppress POLLHUP until we have
+ * seen a writer */
+ filp->f_version = PIPE_WCOUNTER(*inode);
+ } else
+ {
+ wait_for_partner(inode, &PIPE_WCOUNTER(*inode));
+ if(signal_pending(current))
+ goto err_rd;
+ }
+ }
+ break;
+
+ case 2:
+ /*
+ * O_WRONLY
+ * POSIX.1 says that O_NONBLOCK means return -1 with
+ * errno=ENXIO when there is no process reading the FIFO.
+ */
+ ret = -ENXIO;
+ if ((filp->f_flags & O_NONBLOCK) && !PIPE_READERS(*inode))
+ goto err;
+
+ filp->f_op = &write_fifo_fops;
+ PIPE_WCOUNTER(*inode)++;
+ if (!PIPE_WRITERS(*inode)++)
+ wake_up_partner(inode);
+
+ if (!PIPE_READERS(*inode)) {
+ wait_for_partner(inode, &PIPE_RCOUNTER(*inode));
+ if (signal_pending(current))
+ goto err_wr;
+ }
+ break;
+
+ case 3:
+ /*
+ * O_RDWR
+ * POSIX.1 leaves this case "undefined" when O_NONBLOCK is set.
+ * This implementation will NEVER block on a O_RDWR open, since
+ * the process can at least talk to itself.
+ */
+ filp->f_op = &rdwr_fifo_fops;
+
+ PIPE_READERS(*inode)++;
+ PIPE_WRITERS(*inode)++;
+ PIPE_RCOUNTER(*inode)++;
+ PIPE_WCOUNTER(*inode)++;
+ if (PIPE_READERS(*inode) == 1 || PIPE_WRITERS(*inode) == 1)
+ wake_up_partner(inode);
+ break;
+
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* Ok! */
+ up(PIPE_SEM(*inode));
+ unlock_kernel();
+ return 0;
+
+err_rd:
+ if (!--PIPE_READERS(*inode))
+ wake_up_interruptible(PIPE_WAIT(*inode));
+ ret = -ERESTARTSYS;
+ goto err;
+
+err_wr:
+ if (!--PIPE_WRITERS(*inode))
+ wake_up_interruptible(PIPE_WAIT(*inode));
+ ret = -ERESTARTSYS;
+ goto err;
+
+err:
+ if (!PIPE_READERS(*inode) && !PIPE_WRITERS(*inode)) {
+ struct pipe_inode_info *info = inode->i_pipe;
+ inode->i_pipe = NULL;
+ free_page((unsigned long)info->base);
+ kfree(info);
+ }
+
+err_nocleanup:
+ up(PIPE_SEM(*inode));
+
+err_nolock_nocleanup:
+ unlock_kernel();
+ return ret;
+}
+
+/*
+ * Dummy default file-operations: the only thing this does
+ * is contain the open that then fills in the correct operations
+ * depending on the access mode of the file...
+ */
+struct file_operations def_fifo_fops = {
+ open: fifo_open, /* will set read or write pipe_fops */
+};
diff --git a/fs/file.c b/fs/file.c
new file mode 100644
index 00000000000000..d3e4d3a3f212f1
--- /dev/null
+++ b/fs/file.c
@@ -0,0 +1,234 @@
+/*
+ * linux/fs/file.c
+ *
+ * Copyright (C) 1998-1999, Stephen Tweedie and Bill Hawes
+ *
+ * Manage the dynamic fd arrays in the process files_struct.
+ */
+
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#include <asm/bitops.h>
+
+
+/*
+ * Allocate an fd array, using kmalloc or vmalloc.
+ * Note: the array isn't cleared at allocation time.
+ */
+struct file ** alloc_fd_array(int num)
+{
+ struct file **new_fds;
+ int size = num * sizeof(struct file *);
+
+ if (size <= PAGE_SIZE)
+ new_fds = (struct file **) kmalloc(size, GFP_KERNEL);
+ else
+ new_fds = (struct file **) vmalloc(size);
+ return new_fds;
+}
+
+void free_fd_array(struct file **array, int num)
+{
+ int size = num * sizeof(struct file *);
+
+ if (!array) {
+ printk(KERN_ERR "%s array = 0 (num = %d)\n",
+ __FUNCTION__, num);
+ return;
+ }
+
+ if (num <= NR_OPEN_DEFAULT) /* Don't free the embedded fd array! */
+ return;
+ else if (size <= PAGE_SIZE)
+ kfree(array);
+ else
+ vfree(array);
+}
+
+/*
+ * Expand the fd array in the files_struct. Called with the files
+ * spinlock held for write.
+ */
+
+int expand_fd_array(struct files_struct *files, int nr)
+{
+ struct file **new_fds;
+ int error, nfds;
+
+
+ error = -EMFILE;
+ if (files->max_fds >= NR_OPEN || nr >= NR_OPEN)
+ goto out;
+
+ nfds = files->max_fds;
+ write_unlock(&files->file_lock);
+
+ /*
+ * Expand to the max in easy steps, and keep expanding it until
+ * we have enough for the requested fd array size.
+ */
+
+ do {
+#if NR_OPEN_DEFAULT < 256
+ if (nfds < 256)
+ nfds = 256;
+ else
+#endif
+ if (nfds < (PAGE_SIZE / sizeof(struct file *)))
+ nfds = PAGE_SIZE / sizeof(struct file *);
+ else {
+ nfds = nfds * 2;
+ if (nfds > NR_OPEN)
+ nfds = NR_OPEN;
+ }
+ } while (nfds <= nr);
+
+ error = -ENOMEM;
+ new_fds = alloc_fd_array(nfds);
+ write_lock(&files->file_lock);
+ if (!new_fds)
+ goto out;
+
+ /* Copy the existing array and install the new pointer */
+
+ if (nfds > files->max_fds) {
+ struct file **old_fds;
+ int i;
+
+ old_fds = xchg(&files->fd, new_fds);
+ i = xchg(&files->max_fds, nfds);
+
+ /* Don't copy/clear the array if we are creating a new
+ fd array for fork() */
+ if (i) {
+ memcpy(new_fds, old_fds, i * sizeof(struct file *));
+ /* clear the remainder of the array */
+ memset(&new_fds[i], 0,
+ (nfds-i) * sizeof(struct file *));
+
+ write_unlock(&files->file_lock);
+ free_fd_array(old_fds, i);
+ write_lock(&files->file_lock);
+ }
+ } else {
+ /* Somebody expanded the array while we slept ... */
+ write_unlock(&files->file_lock);
+ free_fd_array(new_fds, nfds);
+ write_lock(&files->file_lock);
+ }
+ error = 0;
+out:
+ return error;
+}
+
+/*
+ * Allocate an fdset array, using kmalloc or vmalloc.
+ * Note: the array isn't cleared at allocation time.
+ */
+fd_set * alloc_fdset(int num)
+{
+ fd_set *new_fdset;
+ int size = num / 8;
+
+ if (size <= PAGE_SIZE)
+ new_fdset = (fd_set *) kmalloc(size, GFP_KERNEL);
+ else
+ new_fdset = (fd_set *) vmalloc(size);
+ return new_fdset;
+}
+
+void free_fdset(fd_set *array, int num)
+{
+ int size = num / 8;
+
+ if (!array) {
+ printk(KERN_ERR "%s array = 0 (num = %d)\n",
+ __FUNCTION__, num);
+ return;
+ }
+
+ if (num <= __FD_SETSIZE) /* Don't free an embedded fdset */
+ return;
+ else if (size <= PAGE_SIZE)
+ kfree(array);
+ else
+ vfree(array);
+}
+
+/*
+ * Expand the fdset in the files_struct. Called with the files spinlock
+ * held for write.
+ */
+int expand_fdset(struct files_struct *files, int nr)
+{
+ fd_set *new_openset = 0, *new_execset = 0;
+ int error, nfds = 0;
+
+ error = -EMFILE;
+ if (files->max_fdset >= NR_OPEN || nr >= NR_OPEN)
+ goto out;
+
+ nfds = files->max_fdset;
+ write_unlock(&files->file_lock);
+
+ /* Expand to the max in easy steps */
+ do {
+ if (nfds < (PAGE_SIZE * 8))
+ nfds = PAGE_SIZE * 8;
+ else {
+ nfds = nfds * 2;
+ if (nfds > NR_OPEN)
+ nfds = NR_OPEN;
+ }
+ } while (nfds <= nr);
+
+ error = -ENOMEM;
+ new_openset = alloc_fdset(nfds);
+ new_execset = alloc_fdset(nfds);
+ write_lock(&files->file_lock);
+ if (!new_openset || !new_execset)
+ goto out;
+
+ error = 0;
+
+ /* Copy the existing tables and install the new pointers */
+ if (nfds > files->max_fdset) {
+ int i = files->max_fdset / (sizeof(unsigned long) * 8);
+ int count = (nfds - files->max_fdset) / 8;
+
+ /*
+ * Don't copy the entire array if the current fdset is
+ * not yet initialised.
+ */
+ if (i) {
+ memcpy (new_openset, files->open_fds, files->max_fdset/8);
+ memcpy (new_execset, files->close_on_exec, files->max_fdset/8);
+ memset (&new_openset->fds_bits[i], 0, count);
+ memset (&new_execset->fds_bits[i], 0, count);
+ }
+
+ nfds = xchg(&files->max_fdset, nfds);
+ new_openset = xchg(&files->open_fds, new_openset);
+ new_execset = xchg(&files->close_on_exec, new_execset);
+ write_unlock(&files->file_lock);
+ free_fdset (new_openset, nfds);
+ free_fdset (new_execset, nfds);
+ write_lock(&files->file_lock);
+ return 0;
+ }
+ /* Somebody expanded the array while we slept ... */
+
+out:
+ write_unlock(&files->file_lock);
+ if (new_openset)
+ free_fdset(new_openset, nfds);
+ if (new_execset)
+ free_fdset(new_execset, nfds);
+ write_lock(&files->file_lock);
+ return error;
+}
+
diff --git a/fs/file_table.c b/fs/file_table.c
new file mode 100644
index 00000000000000..ee0e4635d93ef6
--- /dev/null
+++ b/fs/file_table.c
@@ -0,0 +1,205 @@
+/*
+ * linux/fs/file_table.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/file.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/smp_lock.h>
+#include <linux/iobuf.h>
+
+/* sysctl tunables... */
+struct files_stat_struct files_stat = {0, 0, NR_FILE};
+
+/* Here the new files go */
+static LIST_HEAD(anon_list);
+/* And here the free ones sit */
+static LIST_HEAD(free_list);
+/* public *and* exported. Not pretty! */
+spinlock_t files_lock = SPIN_LOCK_UNLOCKED;
+
+/* Find an unused file structure and return a pointer to it.
+ * Returns NULL, if there are no more free file structures or
+ * we run out of memory.
+ *
+ * SMP-safe.
+ */
+struct file * get_empty_filp(void)
+{
+ static int old_max = 0;
+ struct file * f;
+
+ file_list_lock();
+ if (files_stat.nr_free_files > NR_RESERVED_FILES) {
+ used_one:
+ f = list_entry(free_list.next, struct file, f_list);
+ list_del(&f->f_list);
+ files_stat.nr_free_files--;
+ new_one:
+ memset(f, 0, sizeof(*f));
+ atomic_set(&f->f_count,1);
+ f->f_version = ++event;
+ f->f_uid = current->fsuid;
+ f->f_gid = current->fsgid;
+ f->f_maxcount = INT_MAX;
+ list_add(&f->f_list, &anon_list);
+ file_list_unlock();
+ return f;
+ }
+ /*
+ * Use a reserved one if we're the superuser
+ */
+ if (files_stat.nr_free_files && !current->euid)
+ goto used_one;
+ /*
+ * Allocate a new one if we're below the limit.
+ */
+ if (files_stat.nr_files < files_stat.max_files) {
+ file_list_unlock();
+ f = kmem_cache_alloc(filp_cachep, SLAB_KERNEL);
+ file_list_lock();
+ if (f) {
+ files_stat.nr_files++;
+ goto new_one;
+ }
+ /* Big problems... */
+ printk(KERN_WARNING "VFS: filp allocation failed\n");
+
+ } else if (files_stat.max_files > old_max) {
+ printk(KERN_INFO "VFS: file-max limit %d reached\n", files_stat.max_files);
+ old_max = files_stat.max_files;
+ }
+ file_list_unlock();
+ return NULL;
+}
+
+/*
+ * Clear and initialize a (private) struct file for the given dentry,
+ * and call the open function (if any). The caller must verify that
+ * inode->i_fop is not NULL.
+ */
+int init_private_file(struct file *filp, struct dentry *dentry, int mode)
+{
+ memset(filp, 0, sizeof(*filp));
+ filp->f_mode = mode;
+ atomic_set(&filp->f_count, 1);
+ filp->f_dentry = dentry;
+ filp->f_uid = current->fsuid;
+ filp->f_gid = current->fsgid;
+ filp->f_op = dentry->d_inode->i_fop;
+ filp->f_maxcount = INT_MAX;
+
+ if (filp->f_op->open)
+ return filp->f_op->open(dentry->d_inode, filp);
+ else
+ return 0;
+}
+
+void fastcall fput(struct file * file)
+{
+ struct dentry * dentry = file->f_dentry;
+ struct vfsmount * mnt = file->f_vfsmnt;
+ struct inode * inode = dentry->d_inode;
+
+ if (atomic_dec_and_test(&file->f_count)) {
+ locks_remove_flock(file);
+
+ if (file->f_iobuf)
+ free_kiovec(1, &file->f_iobuf);
+
+ if (file->f_op && file->f_op->release)
+ file->f_op->release(inode, file);
+ fops_put(file->f_op);
+ if (file->f_mode & FMODE_WRITE)
+ put_write_access(inode);
+ file_list_lock();
+ file->f_dentry = NULL;
+ file->f_vfsmnt = NULL;
+ list_del(&file->f_list);
+ list_add(&file->f_list, &free_list);
+ files_stat.nr_free_files++;
+ file_list_unlock();
+ dput(dentry);
+ mntput(mnt);
+ }
+}
+
+struct file fastcall *fget(unsigned int fd)
+{
+ struct file * file;
+ struct files_struct *files = current->files;
+
+ read_lock(&files->file_lock);
+ file = fcheck(fd);
+ if (file)
+ get_file(file);
+ read_unlock(&files->file_lock);
+ return file;
+}
+
+/* Here. put_filp() is SMP-safe now. */
+
+void put_filp(struct file *file)
+{
+ if(atomic_dec_and_test(&file->f_count)) {
+ file_list_lock();
+ list_del(&file->f_list);
+ list_add(&file->f_list, &free_list);
+ files_stat.nr_free_files++;
+ file_list_unlock();
+ }
+}
+
+void file_move(struct file *file, struct list_head *list)
+{
+ if (!list)
+ return;
+ file_list_lock();
+ list_del(&file->f_list);
+ list_add(&file->f_list, list);
+ file_list_unlock();
+}
+
+int fs_may_remount_ro(struct super_block *sb)
+{
+ struct list_head *p;
+
+ /* Check that no files are currently opened for writing. */
+ file_list_lock();
+ for (p = sb->s_files.next; p != &sb->s_files; p = p->next) {
+ struct file *file = list_entry(p, struct file, f_list);
+ struct inode *inode = file->f_dentry->d_inode;
+
+ /* File with pending delete? */
+ if (inode->i_nlink == 0)
+ goto too_bad;
+
+ /* Writable file? */
+ if (S_ISREG(inode->i_mode) && (file->f_mode & FMODE_WRITE))
+ goto too_bad;
+ }
+ file_list_unlock();
+ return 1; /* Tis' cool bro. */
+too_bad:
+ file_list_unlock();
+ return 0;
+}
+
+void __init files_init(unsigned long mempages)
+{
+ int n;
+ /* One file with associated inode and dcache is very roughly 1K.
+ * Per default don't use more than 10% of our memory for files.
+ */
+
+ n = (mempages * (PAGE_SIZE / 1024)) / 10;
+ files_stat.max_files = n;
+ if (files_stat.max_files < NR_FILE)
+ files_stat.max_files = NR_FILE;
+}
+
diff --git a/fs/filesystems.c b/fs/filesystems.c
new file mode 100644
index 00000000000000..87774532f489b7
--- /dev/null
+++ b/fs/filesystems.c
@@ -0,0 +1,40 @@
+/*
+ * linux/fs/filesystems.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * nfsservctl system-call when nfsd is not compiled in.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/kmod.h>
+#include <linux/nfsd/interface.h>
+
+#if ! defined(CONFIG_NFSD)
+struct nfsd_linkage *nfsd_linkage;
+
+long
+asmlinkage sys_nfsservctl(int cmd, void *argp, void *resp)
+{
+ int ret = -ENOSYS;
+
+#if defined(CONFIG_MODULES)
+ lock_kernel();
+
+ if (nfsd_linkage ||
+ (request_module ("nfsd") == 0 && nfsd_linkage)) {
+ __MOD_INC_USE_COUNT(nfsd_linkage->owner);
+ unlock_kernel();
+ ret = nfsd_linkage->do_nfsservctl(cmd, argp, resp);
+ __MOD_DEC_USE_COUNT(nfsd_linkage->owner);
+ } else
+ unlock_kernel();
+#endif
+ return ret;
+}
+EXPORT_SYMBOL(nfsd_linkage);
+
+#endif /* CONFIG_NFSD */
diff --git a/fs/freevxfs/Makefile b/fs/freevxfs/Makefile
new file mode 100644
index 00000000000000..593cb7be23aac0
--- /dev/null
+++ b/fs/freevxfs/Makefile
@@ -0,0 +1,11 @@
+#
+# VxFS Makefile
+#
+
+O_TARGET := freevxfs.o
+
+obj-y := vxfs_bmap.o vxfs_fshead.o vxfs_immed.o vxfs_inode.o \
+ vxfs_lookup.o vxfs_olt.o vxfs_subr.o vxfs_super.o
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/freevxfs/vxfs.h b/fs/freevxfs/vxfs.h
new file mode 100644
index 00000000000000..3811dc5e5099b9
--- /dev/null
+++ b/fs/freevxfs/vxfs.h
@@ -0,0 +1,266 @@
+/*
+ * Copyright (c) 2000-2001 Christoph Hellwig.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#ifndef _VXFS_SUPER_H_
+#define _VXFS_SUPER_H_
+
+#ident "$Id: vxfs.h 1.12 2001/12/28 19:48:03 hch Exp $"
+
+/*
+ * Veritas filesystem driver - superblock structure.
+ *
+ * This file contains the definition of the disk and core
+ * superblocks of the Veritas Filesystem.
+ */
+#include <linux/types.h>
+#include "vxfs_kcompat.h"
+
+
+/*
+ * Data types for use with the VxFS ondisk format.
+ */
+typedef int32_t vx_daddr_t;
+typedef int32_t vx_ino_t;
+
+/*
+ * Superblock magic number (vxfs_super->vs_magic).
+ */
+#define VXFS_SUPER_MAGIC 0xa501FCF5
+
+/*
+ * The root inode.
+ */
+#define VXFS_ROOT_INO 2
+
+/*
+ * Num of entries in free extent array
+ */
+#define VXFS_NEFREE 32
+
+
+/*
+ * VxFS superblock (disk).
+ */
+struct vxfs_sb {
+ /*
+ * Readonly fields for the version 1 superblock.
+ *
+ * Lots of this fields are no more used by version 2
+ * and never filesystems.
+ */
+ u_int32_t vs_magic; /* Magic number */
+ int32_t vs_version; /* VxFS version */
+ u_int32_t vs_ctime; /* create time - secs */
+ u_int32_t vs_cutime; /* create time - usecs */
+ int32_t __unused1; /* unused */
+ int32_t __unused2; /* unused */
+ vx_daddr_t vs_old_logstart; /* obsolete */
+ vx_daddr_t vs_old_logend; /* obsolete */
+ int32_t vs_bsize; /* block size */
+ int32_t vs_size; /* number of blocks */
+ int32_t vs_dsize; /* number of data blocks */
+ u_int32_t vs_old_ninode; /* obsolete */
+ int32_t vs_old_nau; /* obsolete */
+ int32_t __unused3; /* unused */
+ int32_t vs_old_defiextsize; /* obsolete */
+ int32_t vs_old_ilbsize; /* obsolete */
+ int32_t vs_immedlen; /* size of immediate data area */
+ int32_t vs_ndaddr; /* number of direct extentes */
+ vx_daddr_t vs_firstau; /* address of first AU */
+ vx_daddr_t vs_emap; /* offset of extent map in AU */
+ vx_daddr_t vs_imap; /* offset of inode map in AU */
+ vx_daddr_t vs_iextop; /* offset of ExtOp. map in AU */
+ vx_daddr_t vs_istart; /* offset of inode list in AU */
+ vx_daddr_t vs_bstart; /* offset of fdblock in AU */
+ vx_daddr_t vs_femap; /* aufirst + emap */
+ vx_daddr_t vs_fimap; /* aufirst + imap */
+ vx_daddr_t vs_fiextop; /* aufirst + iextop */
+ vx_daddr_t vs_fistart; /* aufirst + istart */
+ vx_daddr_t vs_fbstart; /* aufirst + bstart */
+ int32_t vs_nindir; /* number of entries in indir */
+ int32_t vs_aulen; /* length of AU in blocks */
+ int32_t vs_auimlen; /* length of imap in blocks */
+ int32_t vs_auemlen; /* length of emap in blocks */
+ int32_t vs_auilen; /* length of ilist in blocks */
+ int32_t vs_aupad; /* length of pad in blocks */
+ int32_t vs_aublocks; /* data blocks in AU */
+ int32_t vs_maxtier; /* log base 2 of aublocks */
+ int32_t vs_inopb; /* number of inodes per blk */
+ int32_t vs_old_inopau; /* obsolete */
+ int32_t vs_old_inopilb; /* obsolete */
+ int32_t vs_old_ndiripau; /* obsolete */
+ int32_t vs_iaddrlen; /* size of indirect addr ext. */
+ int32_t vs_bshift; /* log base 2 of bsize */
+ int32_t vs_inoshift; /* log base 2 of inobp */
+ int32_t vs_bmask; /* ~( bsize - 1 ) */
+ int32_t vs_boffmask; /* bsize - 1 */
+ int32_t vs_old_inomask; /* old_inopilb - 1 */
+ int32_t vs_checksum; /* checksum of V1 data */
+
+ /*
+ * Version 1, writable
+ */
+ int32_t vs_free; /* number of free blocks */
+ int32_t vs_ifree; /* number of free inodes */
+ int32_t vs_efree[VXFS_NEFREE]; /* number of free extents by size */
+ int32_t vs_flags; /* flags ?!? */
+ u_int8_t vs_mod; /* filesystem has been changed */
+ u_int8_t vs_clean; /* clean FS */
+ u_int16_t __unused4; /* unused */
+ u_int32_t vs_firstlogid; /* mount time log ID */
+ u_int32_t vs_wtime; /* last time written - sec */
+ u_int32_t vs_wutime; /* last time written - usec */
+ u_int8_t vs_fname[6]; /* FS name */
+ u_int8_t vs_fpack[6]; /* FS pack name */
+ int32_t vs_logversion; /* log format version */
+ int32_t __unused5; /* unused */
+
+ /*
+ * Version 2, Read-only
+ */
+ vx_daddr_t vs_oltext[2]; /* OLT extent and replica */
+ int32_t vs_oltsize; /* OLT extent size */
+ int32_t vs_iauimlen; /* size of inode map */
+ int32_t vs_iausize; /* size of IAU in blocks */
+ int32_t vs_dinosize; /* size of inode in bytes */
+ int32_t vs_old_dniaddr; /* indir levels per inode */
+ int32_t vs_checksum2; /* checksum of V2 RO */
+
+ /*
+ * Actually much more...
+ */
+};
+
+
+/*
+ * In core superblock filesystem private data for VxFS.
+ */
+struct vxfs_sb_info {
+ struct vxfs_sb *vsi_raw; /* raw (on disk) supeblock */
+ struct buffer_head *vsi_bp; /* buffer for raw superblock*/
+ struct inode *vsi_fship; /* fileset header inode */
+ struct inode *vsi_ilist; /* inode list inode */
+ struct inode *vsi_stilist; /* structual inode list inode */
+ u_long vsi_iext; /* initial inode list */
+ ino_t vsi_fshino; /* fileset header inode */
+ daddr_t vsi_oltext; /* OLT extent */
+ daddr_t vsi_oltsize; /* OLT size */
+};
+
+
+/*
+ * File modes. File types above 0xf000 are vxfs internal only, they should
+ * not be passed back to higher levels of the system. vxfs file types must
+ * never have one of the regular file type bits set.
+ */
+enum vxfs_mode {
+ VXFS_ISUID = 0x00000800, /* setuid */
+ VXFS_ISGID = 0x00000400, /* setgid */
+ VXFS_ISVTX = 0x00000200, /* sticky bit */
+ VXFS_IREAD = 0x00000100, /* read */
+ VXFS_IWRITE = 0x00000080, /* write */
+ VXFS_IEXEC = 0x00000040, /* exec */
+
+ VXFS_IFIFO = 0x00001000, /* Named pipe */
+ VXFS_IFCHR = 0x00002000, /* Character device */
+ VXFS_IFDIR = 0x00004000, /* Directory */
+ VXFS_IFNAM = 0x00005000, /* Xenix device ?? */
+ VXFS_IFBLK = 0x00006000, /* Block device */
+ VXFS_IFREG = 0x00008000, /* Regular file */
+ VXFS_IFCMP = 0x00009000, /* Compressed file ?!? */
+ VXFS_IFLNK = 0x0000a000, /* Symlink */
+ VXFS_IFSOC = 0x0000c000, /* Socket */
+
+ /* VxFS internal */
+ VXFS_IFFSH = 0x10000000, /* Fileset header */
+ VXFS_IFILT = 0x20000000, /* Inode list */
+ VXFS_IFIAU = 0x30000000, /* Inode allocation unit */
+ VXFS_IFCUT = 0x40000000, /* Current usage table */
+ VXFS_IFATT = 0x50000000, /* Attr. inode */
+ VXFS_IFLCT = 0x60000000, /* Link count table */
+ VXFS_IFIAT = 0x70000000, /* Indirect attribute file */
+ VXFS_IFEMR = 0x80000000, /* Extent map reorg file */
+ VXFS_IFQUO = 0x90000000, /* BSD quota file */
+ VXFS_IFPTI = 0xa0000000, /* "Pass through" inode */
+ VXFS_IFLAB = 0x11000000, /* Device label file */
+ VXFS_IFOLT = 0x12000000, /* OLT file */
+ VXFS_IFLOG = 0x13000000, /* Log file */
+ VXFS_IFEMP = 0x14000000, /* Extent map file */
+ VXFS_IFEAU = 0x15000000, /* Extent AU file */
+ VXFS_IFAUS = 0x16000000, /* Extent AU summary file */
+ VXFS_IFDEV = 0x17000000, /* Device config file */
+
+};
+
+#define VXFS_TYPE_MASK 0xfffff000
+
+#define VXFS_IS_TYPE(ip,type) (((ip)->vii_mode & VXFS_TYPE_MASK) == (type))
+#define VXFS_ISFIFO(x) VXFS_IS_TYPE((x),VXFS_IFIFO)
+#define VXFS_ISCHR(x) VXFS_IS_TYPE((x),VXFS_IFCHR)
+#define VXFS_ISDIR(x) VXFS_IS_TYPE((x),VXFS_IFDIR)
+#define VXFS_ISNAM(x) VXFS_IS_TYPE((x),VXFS_IFNAM)
+#define VXFS_ISBLK(x) VXFS_IS_TYPE((x),VXFS_IFBLK)
+#define VXFS_ISLNK(x) VXFS_IS_TYPE((x),VXFS_IFLNK)
+#define VXFS_ISREG(x) VXFS_IS_TYPE((x),VXFS_IFREG)
+#define VXFS_ISCMP(x) VXFS_IS_TYPE((x),VXFS_IFCMP)
+#define VXFS_ISSOC(x) VXFS_IS_TYPE((x),VXFS_IFSOC)
+
+#define VXFS_ISFSH(x) VXFS_IS_TYPE((x),VXFS_IFFSH)
+#define VXFS_ISILT(x) VXFS_IS_TYPE((x),VXFS_IFILT)
+
+/*
+ * Inmode organisation types.
+ */
+enum {
+ VXFS_ORG_NONE = 0, /* Inode has *no* format ?!? */
+ VXFS_ORG_EXT4 = 1, /* Ext4 */
+ VXFS_ORG_IMMED = 2, /* All data stored in inode */
+ VXFS_ORG_TYPED = 3, /* Typed extents */
+};
+
+#define VXFS_IS_ORG(ip,org) ((ip)->vii_orgtype == (org))
+#define VXFS_ISNONE(ip) VXFS_IS_ORG((ip), VXFS_ORG_NONE)
+#define VXFS_ISEXT4(ip) VXFS_IS_ORG((ip), VXFS_ORG_EXT4)
+#define VXFS_ISIMMED(ip) VXFS_IS_ORG((ip), VXFS_ORG_IMMED)
+#define VXFS_ISTYPED(ip) VXFS_IS_ORG((ip), VXFS_ORG_TYPED)
+
+
+/*
+ * Get filesystem private data from VFS inode.
+ */
+#define VXFS_INO(ip) \
+ ((struct vxfs_inode_info *)(ip)->u.generic_ip)
+
+/*
+ * Get filesystem private data from VFS superblock.
+ */
+#define VXFS_SBI(sbp) \
+ ((struct vxfs_sb_info *)(sbp)->u.generic_sbp)
+
+#endif /* _VXFS_SUPER_H_ */
diff --git a/fs/freevxfs/vxfs_bmap.c b/fs/freevxfs/vxfs_bmap.c
new file mode 100644
index 00000000000000..ed31e0a06843c7
--- /dev/null
+++ b/fs/freevxfs/vxfs_bmap.c
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 2000-2001 Christoph Hellwig.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ident "$Id: vxfs_bmap.c,v 1.25 2002/01/02 23:36:55 hch Exp hch $"
+
+/*
+ * Veritas filesystem driver - filesystem to disk block mapping.
+ */
+#include <linux/fs.h>
+#include <linux/kernel.h>
+
+#include "vxfs.h"
+#include "vxfs_inode.h"
+
+
+#ifdef DIAGNOSTIC
+static void
+vxfs_typdump(struct vxfs_typed *typ)
+{
+ printk(KERN_DEBUG "type=%Lu ", typ->vt_hdr >> VXFS_TYPED_TYPESHIFT);
+ printk("offset=%Lx ", typ->vt_hdr & VXFS_TYPED_OFFSETMASK);
+ printk("block=%x ", typ->vt_block);
+ printk("size=%x\n", typ->vt_size);
+}
+#endif
+
+/**
+ * vxfs_bmap_ext4 - do bmap for ext4 extents
+ * @ip: pointer to the inode we do bmap for
+ * @iblock: logical block.
+ *
+ * Description:
+ * vxfs_bmap_ext4 performs the bmap operation for inodes with
+ * ext4-style extents (which are much like the traditional UNIX
+ * inode organisation).
+ *
+ * Returns:
+ * The physical block number on success, else Zero.
+ */
+static daddr_t
+vxfs_bmap_ext4(struct inode *ip, long bn)
+{
+ struct super_block *sb = ip->i_sb;
+ struct vxfs_inode_info *vip = VXFS_INO(ip);
+ unsigned long bsize = sb->s_blocksize;
+ u32 indsize = vip->vii_ext4.ve4_indsize;
+ int i;
+
+ if (indsize > sb->s_blocksize)
+ goto fail_size;
+
+ for (i = 0; i < VXFS_NDADDR; i++) {
+ struct direct *d = vip->vii_ext4.ve4_direct + i;
+ if (bn >= 0 && bn < d->size)
+ return (bn + d->extent);
+ bn -= d->size;
+ }
+
+ if ((bn / (indsize * indsize * bsize / 4)) == 0) {
+ struct buffer_head *buf;
+ daddr_t bno;
+ u32 *indir;
+
+ buf = sb_bread(sb, vip->vii_ext4.ve4_indir[0]);
+ if (!buf || !buffer_mapped(buf))
+ goto fail_buf;
+
+ indir = (u32 *)buf->b_data;
+ bno = indir[(bn/indsize) % (indsize*bn)] + (bn%indsize);
+
+ brelse(buf);
+ return bno;
+ } else
+ printk(KERN_WARNING "no matching indir?");
+
+ return 0;
+
+fail_size:
+ printk("vxfs: indirect extent to big!\n");
+fail_buf:
+ return 0;
+}
+
+/**
+ * vxfs_bmap_indir - recursion for vxfs_bmap_typed
+ * @ip: pointer to the inode we do bmap for
+ * @indir: indirect block we start reading at
+ * @size: size of the typed area to search
+ * @block: partially result from further searches
+ *
+ * Description:
+ * vxfs_bmap_indir reads a &struct vxfs_typed at @indir
+ * and performs the type-defined action.
+ *
+ * Return Value:
+ * The physical block number on success, else Zero.
+ *
+ * Note:
+ * Kernelstack is rare. Unrecurse?
+ */
+static daddr_t
+vxfs_bmap_indir(struct inode *ip, long indir, int size, long block)
+{
+ struct buffer_head *bp = NULL;
+ daddr_t pblock = 0;
+ int i;
+
+ for (i = 0; i < size * VXFS_TYPED_PER_BLOCK(ip->i_sb); i++) {
+ struct vxfs_typed *typ;
+ int64_t off;
+
+ bp = sb_bread(ip->i_sb,
+ indir + (i / VXFS_TYPED_PER_BLOCK(ip->i_sb)));
+ if (!buffer_mapped(bp))
+ return 0;
+
+ typ = ((struct vxfs_typed *)bp->b_data) +
+ (i % VXFS_TYPED_PER_BLOCK(ip->i_sb));
+ off = (typ->vt_hdr & VXFS_TYPED_OFFSETMASK);
+
+ if (block < off) {
+ brelse(bp);
+ continue;
+ }
+
+ switch ((u_int32_t)(typ->vt_hdr >> VXFS_TYPED_TYPESHIFT)) {
+ case VXFS_TYPED_INDIRECT:
+ pblock = vxfs_bmap_indir(ip, typ->vt_block,
+ typ->vt_size, block - off);
+ if (pblock == -2)
+ break;
+ goto out;
+ case VXFS_TYPED_DATA:
+ if ((block - off) >= typ->vt_size)
+ break;
+ pblock = (typ->vt_block + block - off);
+ goto out;
+ case VXFS_TYPED_INDIRECT_DEV4:
+ case VXFS_TYPED_DATA_DEV4: {
+ struct vxfs_typed_dev4 *typ4 =
+ (struct vxfs_typed_dev4 *)typ;
+
+ printk(KERN_INFO "\n\nTYPED_DEV4 detected!\n");
+ printk(KERN_INFO "block: %Lu\tsize: %Ld\tdev: %d\n",
+ typ4->vd4_block, typ4->vd4_size, typ4->vd4_dev);
+ goto fail;
+ }
+ default:
+ BUG();
+ }
+ brelse(bp);
+ }
+
+fail:
+ pblock = 0;
+out:
+ brelse(bp);
+ return (pblock);
+}
+
+/**
+ * vxfs_bmap_typed - bmap for typed extents
+ * @ip: pointer to the inode we do bmap for
+ * @iblock: logical block
+ *
+ * Description:
+ * Performs the bmap operation for typed extents.
+ *
+ * Return Value:
+ * The physical block number on success, else Zero.
+ */
+static daddr_t
+vxfs_bmap_typed(struct inode *ip, long iblock)
+{
+ struct vxfs_inode_info *vip = VXFS_INO(ip);
+ daddr_t pblock = 0;
+ int i;
+
+ for (i = 0; i < VXFS_NTYPED; i++) {
+ struct vxfs_typed *typ = vip->vii_org.typed + i;
+ int64_t off = (typ->vt_hdr & VXFS_TYPED_OFFSETMASK);
+
+#ifdef DIAGNOSTIC
+ vxfs_typdump(typ);
+#endif
+ if (iblock < off)
+ continue;
+ switch ((u_int32_t)(typ->vt_hdr >> VXFS_TYPED_TYPESHIFT)) {
+ case VXFS_TYPED_INDIRECT:
+ pblock = vxfs_bmap_indir(ip, typ->vt_block,
+ typ->vt_size, iblock - off);
+ if (pblock == -2)
+ break;
+ return (pblock);
+ case VXFS_TYPED_DATA:
+ if ((iblock - off) < typ->vt_size)
+ return (typ->vt_block + iblock - off);
+ break;
+ case VXFS_TYPED_INDIRECT_DEV4:
+ case VXFS_TYPED_DATA_DEV4: {
+ struct vxfs_typed_dev4 *typ4 =
+ (struct vxfs_typed_dev4 *)typ;
+
+ printk(KERN_INFO "\n\nTYPED_DEV4 detected!\n");
+ printk(KERN_INFO "block: %Lu\tsize: %Ld\tdev: %d\n",
+ typ4->vd4_block, typ4->vd4_size, typ4->vd4_dev);
+ return 0;
+ }
+ default:
+ BUG();
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * vxfs_bmap1 - vxfs-internal bmap operation
+ * @ip: pointer to the inode we do bmap for
+ * @iblock: logical block
+ *
+ * Description:
+ * vxfs_bmap1 perfoms a logical to physical block mapping
+ * for vxfs-internal purposes.
+ *
+ * Return Value:
+ * The physical block number on success, else Zero.
+ */
+daddr_t
+vxfs_bmap1(struct inode *ip, long iblock)
+{
+ struct vxfs_inode_info *vip = VXFS_INO(ip);
+
+ if (VXFS_ISEXT4(vip))
+ return vxfs_bmap_ext4(ip, iblock);
+ if (VXFS_ISTYPED(vip))
+ return vxfs_bmap_typed(ip, iblock);
+ if (VXFS_ISNONE(vip))
+ goto unsupp;
+ if (VXFS_ISIMMED(vip))
+ goto unsupp;
+
+ printk(KERN_WARNING "vxfs: inode %ld has no valid orgtype (%x)\n",
+ ip->i_ino, vip->vii_orgtype);
+ BUG();
+
+unsupp:
+ printk(KERN_WARNING "vxfs: inode %ld has an unsupported orgtype (%x)\n",
+ ip->i_ino, vip->vii_orgtype);
+ return 0;
+}
diff --git a/fs/freevxfs/vxfs_dir.h b/fs/freevxfs/vxfs_dir.h
new file mode 100644
index 00000000000000..b4271e244b1327
--- /dev/null
+++ b/fs/freevxfs/vxfs_dir.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2000-2001 Christoph Hellwig.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#ifndef _VXFS_DIR_H_
+#define _VXFS_DIR_H_
+
+#ident "$Id: vxfs_dir.h,v 1.7 2001/05/21 15:48:26 hch Exp hch $"
+
+/*
+ * Veritas filesystem driver - directory structure.
+ *
+ * This file contains the definition of the vxfs directory format.
+ */
+
+
+/*
+ * VxFS directory block header.
+ *
+ * This entry is the head of every filesystem block in a directory.
+ * It is used for free space managment and additionally includes
+ * a hash for speeding up directory search (lookup).
+ *
+ * The hash may be empty and in fact we do not use it all in the
+ * Linux driver for now.
+ */
+struct vxfs_dirblk {
+ u_int16_t d_free; /* free space in dirblock */
+ u_int16_t d_nhash; /* no of hash chains */
+ u_int16_t d_hash[1]; /* hash chain */
+};
+
+/*
+ * VXFS_NAMELEN is the maximum length of the d_name field
+ * of an VxFS directory entry.
+ */
+#define VXFS_NAMELEN 256
+
+/*
+ * VxFS directory entry.
+ */
+struct vxfs_direct {
+ vx_ino_t d_ino; /* inode number */
+ u_int16_t d_reclen; /* record length */
+ u_int16_t d_namelen; /* d_name length */
+ u_int16_t d_hashnext; /* next hash entry */
+ char d_name[VXFS_NAMELEN]; /* name */
+};
+
+/*
+ * VXFS_DIRPAD defines the directory entry boundaries, is _must_ be
+ * a multiple of four.
+ * VXFS_NAMEMIN is the length of a directory entry with a NULL d_name.
+ * VXFS_DIRROUND is an internal macros that rounds a length to a value
+ * usable for directory sizes.
+ * VXFS_DIRLEN calculates the directory entry size for an entry with
+ * a d_name with size len.
+ */
+#define VXFS_DIRPAD 4
+#define VXFS_NAMEMIN ((int)((struct vxfs_direct *)0)->d_name)
+#define VXFS_DIRROUND(len) ((VXFS_DIRPAD + (len) - 1) & ~(VXFS_DIRPAD -1))
+#define VXFS_DIRLEN(len) (VXFS_DIRROUND(VXFS_NAMEMIN + (len)))
+
+/*
+ * VXFS_DIRBLKOV is the overhead of a specific dirblock.
+ */
+#define VXFS_DIRBLKOV(dbp) ((sizeof(short) * dbp->d_nhash) + 4)
+
+#endif /* _VXFS_DIR_H_ */
diff --git a/fs/freevxfs/vxfs_extern.h b/fs/freevxfs/vxfs_extern.h
new file mode 100644
index 00000000000000..f09eced5128252
--- /dev/null
+++ b/fs/freevxfs/vxfs_extern.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2000-2001 Christoph Hellwig.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#ifndef _VXFS_EXTERN_H_
+#define _VXFS_EXTERN_H_
+
+#ident "$Id: vxfs_extern.h,v 1.22 2001/12/28 20:50:47 hch Exp hch $"
+
+/*
+ * Veritas filesystem driver - external prototypes.
+ *
+ * This file contains prototypes for all vxfs functions used
+ * outside their respective source files.
+ */
+
+
+struct kmem_cache_s;
+struct super_block;
+struct vxfs_inode_info;
+struct inode;
+
+
+/* vxfs_bmap.c */
+extern daddr_t vxfs_bmap1(struct inode *, long);
+
+/* vxfs_fshead.c */
+extern int vxfs_read_fshead(struct super_block *);
+
+/* vxfs_inode.c */
+extern struct kmem_cache_s *vxfs_inode_cachep;
+extern void vxfs_dumpi(struct vxfs_inode_info *, ino_t);
+extern struct inode * vxfs_get_fake_inode(struct super_block *,
+ struct vxfs_inode_info *);
+extern void vxfs_put_fake_inode(struct inode *);
+extern struct vxfs_inode_info * vxfs_blkiget(struct super_block *, u_long, ino_t);
+extern struct vxfs_inode_info * vxfs_stiget(struct super_block *, ino_t);
+extern void vxfs_read_inode(struct inode *);
+extern void vxfs_put_inode(struct inode *);
+
+/* vxfs_lookup.c */
+extern struct inode_operations vxfs_dir_inode_ops;
+extern struct file_operations vxfs_dir_operations;
+
+/* vxfs_olt.c */
+extern int vxfs_read_olt(struct super_block *, u_long);
+
+/* vxfs_subr.c */
+extern struct page * vxfs_get_page(struct address_space *, u_long);
+extern void vxfs_put_page(struct page *);
+extern struct buffer_head * vxfs_bread(struct inode *, int);
+
+#endif /* _VXFS_EXTERN_H_ */
diff --git a/fs/freevxfs/vxfs_fshead.c b/fs/freevxfs/vxfs_fshead.c
new file mode 100644
index 00000000000000..faf0069126465d
--- /dev/null
+++ b/fs/freevxfs/vxfs_fshead.c
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2000-2001 Christoph Hellwig.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ident "$Id: vxfs_fshead.c,v 1.20 2002/01/02 22:02:12 hch Exp hch $"
+
+/*
+ * Veritas filesystem driver - fileset header routines.
+ */
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+
+#include "vxfs.h"
+#include "vxfs_inode.h"
+#include "vxfs_extern.h"
+#include "vxfs_fshead.h"
+
+
+#ifdef DIAGNOSTIC
+static void
+vxfs_dumpfsh(struct vxfs_fsh *fhp)
+{
+ printk("\n\ndumping fileset header:\n");
+ printk("----------------------------\n");
+ printk("version: %u\n", fhp->fsh_version);
+ printk("fsindex: %u\n", fhp->fsh_fsindex);
+ printk("iauino: %u\tninodes:%u\n",
+ fhp->fsh_iauino, fhp->fsh_ninodes);
+ printk("maxinode: %u\tlctino: %u\n",
+ fhp->fsh_maxinode, fhp->fsh_lctino);
+ printk("nau: %u\n", fhp->fsh_nau);
+ printk("ilistino[0]: %u\tilistino[1]: %u\n",
+ fhp->fsh_ilistino[0], fhp->fsh_ilistino[1]);
+}
+#endif
+
+/**
+ * vxfs_getfsh - read fileset header into memory
+ * @ip: the (fake) fileset header inode
+ * @which: 0 for the structural, 1 for the primary fsh.
+ *
+ * Description:
+ * vxfs_getfsh reads either the structural or primary fileset header
+ * described by @ip into memory.
+ *
+ * Returns:
+ * The fileset header structure on success, else Zero.
+ */
+static struct vxfs_fsh *
+vxfs_getfsh(struct inode *ip, int which)
+{
+ struct buffer_head *bp;
+
+ bp = vxfs_bread(ip, which);
+ if (buffer_mapped(bp)) {
+ struct vxfs_fsh *fhp;
+
+ if (!(fhp = kmalloc(sizeof(*fhp), SLAB_KERNEL)))
+ return NULL;
+ memcpy(fhp, bp->b_data, sizeof(*fhp));
+
+ brelse(bp);
+ return (fhp);
+ }
+
+ return NULL;
+}
+
+/**
+ * vxfs_read_fshead - read the fileset headers
+ * @sbp: superblock to which the fileset belongs
+ *
+ * Description:
+ * vxfs_read_fshead will fill the inode and structural inode list in @sb.
+ *
+ * Returns:
+ * Zero on success, else a negative error code (-EINVAL).
+ */
+int
+vxfs_read_fshead(struct super_block *sbp)
+{
+ struct vxfs_sb_info *infp = VXFS_SBI(sbp);
+ struct vxfs_fsh *pfp, *sfp;
+ struct vxfs_inode_info *vip, *tip;
+
+ if (!(vip = vxfs_blkiget(sbp, infp->vsi_iext, infp->vsi_fshino))) {
+ printk(KERN_ERR "vxfs: unabled to read fsh inode\n");
+ return -EINVAL;
+ } else if (!VXFS_ISFSH(vip)) {
+ printk(KERN_ERR "vxfs: fsh list inode is of wrong type (%x)\n",
+ vip->vii_mode & VXFS_TYPE_MASK);
+ return -EINVAL;
+ }
+
+
+#ifdef DIAGNOSTIC
+ printk("vxfs: fsh inode dump:\n");
+ vxfs_dumpi(vip, infp->vsi_fshino);
+#endif
+
+ if (!(infp->vsi_fship = vxfs_get_fake_inode(sbp, vip))) {
+ printk(KERN_ERR "vxfs: unabled to get fsh inode\n");
+ return -EINVAL;
+ }
+
+ if (!(sfp = vxfs_getfsh(infp->vsi_fship, 0))) {
+ printk(KERN_ERR "vxfs: unabled to get structural fsh\n");
+ return -EINVAL;
+ }
+
+#ifdef DIAGNOSTIC
+ vxfs_dumpfsh(sfp);
+#endif
+
+ if (!(pfp = vxfs_getfsh(infp->vsi_fship, 1))) {
+ printk(KERN_ERR "vxfs: unabled to get primary fsh\n");
+ return -EINVAL;
+ }
+
+#ifdef DIAGNOSTIC
+ vxfs_dumpfsh(pfp);
+#endif
+
+ tip = vxfs_blkiget(sbp, infp->vsi_iext, sfp->fsh_ilistino[0]);
+ if (!tip || ((infp->vsi_stilist = vxfs_get_fake_inode(sbp, tip)) == NULL)) {
+ printk(KERN_ERR "vxfs: unabled to get structual list inode\n");
+ return -EINVAL;
+ } else if (!VXFS_ISILT(VXFS_INO(infp->vsi_stilist))) {
+ printk(KERN_ERR "vxfs: structual list inode is of wrong type (%x)\n",
+ VXFS_INO(infp->vsi_stilist)->vii_mode & VXFS_TYPE_MASK);
+ return -EINVAL;
+ }
+
+ tip = vxfs_stiget(sbp, pfp->fsh_ilistino[0]);
+ if (!tip || ((infp->vsi_ilist = vxfs_get_fake_inode(sbp, tip)) == NULL)) {
+ printk(KERN_ERR "vxfs: unabled to get inode list inode\n");
+ return -EINVAL;
+ } else if (!VXFS_ISILT(VXFS_INO(infp->vsi_ilist))) {
+ printk(KERN_ERR "vxfs: inode list inode is of wrong type (%x)\n",
+ VXFS_INO(infp->vsi_ilist)->vii_mode & VXFS_TYPE_MASK);
+ return -EINVAL;
+ }
+
+ return 0;
+}
diff --git a/fs/freevxfs/vxfs_fshead.h b/fs/freevxfs/vxfs_fshead.h
new file mode 100644
index 00000000000000..28fc5c6a586c74
--- /dev/null
+++ b/fs/freevxfs/vxfs_fshead.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2000-2001 Christoph Hellwig.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#ifndef _VXFS_FSHEAD_H_
+#define _VXFS_FSHEAD_H_
+
+#ident "$Id: vxfs_fshead.h,v 1.7 2001/05/23 17:27:39 hch Exp hch $"
+
+/*
+ * Veritas filesystem driver - fileset header structures.
+ *
+ * This file contains the physical structure of the VxFS
+ * fileset header.
+ */
+
+
+/*
+ * Fileset header
+ */
+struct vxfs_fsh {
+ u_int32_t fsh_version; /* fileset header version */
+ u_int32_t fsh_fsindex; /* fileset index */
+ u_int32_t fsh_time; /* modification time - sec */
+ u_int32_t fsh_utime; /* modification time - usec */
+ u_int32_t fsh_extop; /* extop flags */
+ vx_ino_t fsh_ninodes; /* allocated inodes */
+ u_int32_t fsh_nau; /* number of IAUs */
+ u_int32_t fsh_old_ilesize; /* old size of ilist */
+ u_int32_t fsh_dflags; /* flags */
+ u_int32_t fsh_quota; /* quota limit */
+ vx_ino_t fsh_maxinode; /* maximum inode number */
+ vx_ino_t fsh_iauino; /* IAU inode */
+ vx_ino_t fsh_ilistino[2]; /* ilist inodes */
+ vx_ino_t fsh_lctino; /* link count table inode */
+
+ /*
+ * Slightly more fields follow, but they
+ * a) are not of any interest for us, and
+ * b) differ a lot in different vxfs versions/ports
+ */
+};
+
+#endif /* _VXFS_FSHEAD_H_ */
diff --git a/fs/freevxfs/vxfs_immed.c b/fs/freevxfs/vxfs_immed.c
new file mode 100644
index 00000000000000..a13e2605b6c40f
--- /dev/null
+++ b/fs/freevxfs/vxfs_immed.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2000-2001 Christoph Hellwig.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ident "$Id: vxfs_immed.c,v 1.10 2001/04/25 18:11:23 hch Exp hch $"
+
+/*
+ * Veritas filesystem driver - support for 'immed' inodes.
+ */
+#include <linux/fs.h>
+#include <linux/pagemap.h>
+
+#include "vxfs.h"
+#include "vxfs_inode.h"
+
+
+static int vxfs_immed_readlink(struct dentry *, char *, int);
+static int vxfs_immed_follow_link(struct dentry *, struct nameidata *);
+
+static int vxfs_immed_readpage(struct file *, struct page *);
+
+/*
+ * Inode operations for immed symlinks.
+ *
+ * Unliked all other operations we do not go through the pagecache,
+ * but do all work directly on the inode.
+ */
+struct inode_operations vxfs_immed_symlink_iops = {
+ .readlink = vxfs_immed_readlink,
+ .follow_link = vxfs_immed_follow_link,
+};
+
+/*
+ * Adress space operations for immed files and directories.
+ */
+struct address_space_operations vxfs_immed_aops = {
+ .readpage = vxfs_immed_readpage,
+};
+
+
+/**
+ * vxfs_immed_readlink - read immed symlink
+ * @dp: dentry for the link
+ * @bp: output buffer
+ * @buflen: length of @bp
+ *
+ * Description:
+ * vxfs_immed_readlink calls vfs_readlink to read the link
+ * described by @dp into userspace.
+ *
+ * Returns:
+ * Number of bytes successfully copied to userspace.
+ */
+static int
+vxfs_immed_readlink(struct dentry *dp, char *bp, int buflen)
+{
+ struct vxfs_inode_info *vip = VXFS_INO(dp->d_inode);
+
+ return (vfs_readlink(dp, bp, buflen, vip->vii_immed.vi_immed));
+}
+
+/**
+ * vxfs_immed_follow_link - follow immed symlink
+ * @dp: dentry for the link
+ * @np: pathname lookup data for the current path walk
+ *
+ * Description:
+ * vxfs_immed_follow_link restarts the pathname lookup with
+ * the data obtained from @dp.
+ *
+ * Returns:
+ * Zero on success, else a negative error code.
+ */
+static int
+vxfs_immed_follow_link(struct dentry *dp, struct nameidata *np)
+{
+ struct vxfs_inode_info *vip = VXFS_INO(dp->d_inode);
+
+ return (vfs_follow_link(np, vip->vii_immed.vi_immed));
+}
+
+/**
+ * vxfs_immed_readpage - read part of an immed inode into pagecache
+ * @file: file context (unused)
+ * @page: page frame to fill in.
+ *
+ * Description:
+ * vxfs_immed_readpage reads a part of the immed area of the
+ * file that hosts @pp into the pagecache.
+ *
+ * Returns:
+ * Zero on success, else a negative error code.
+ *
+ * Locking status:
+ * @page is locked and will be unlocked.
+ */
+static int
+vxfs_immed_readpage(struct file *fp, struct page *pp)
+{
+ struct vxfs_inode_info *vip = VXFS_INO(pp->mapping->host);
+ u_int64_t offset = pp->index << PAGE_CACHE_SHIFT;
+ caddr_t kaddr;
+
+ kaddr = kmap(pp);
+ memcpy(kaddr, vip->vii_immed.vi_immed + offset, PAGE_CACHE_SIZE);
+ kunmap(pp);
+
+ flush_dcache_page(pp);
+ SetPageUptodate(pp);
+ UnlockPage(pp);
+
+ return 0;
+}
diff --git a/fs/freevxfs/vxfs_inode.c b/fs/freevxfs/vxfs_inode.c
new file mode 100644
index 00000000000000..98060cfd518927
--- /dev/null
+++ b/fs/freevxfs/vxfs_inode.c
@@ -0,0 +1,349 @@
+/*
+ * Copyright (c) 2000-2001 Christoph Hellwig.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ident "$Id: vxfs_inode.c,v 1.42 2002/01/02 23:51:36 hch Exp hch $"
+
+/*
+ * Veritas filesystem driver - inode routines.
+ */
+#include <linux/fs.h>
+#include <linux/pagemap.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+
+#include "vxfs.h"
+#include "vxfs_inode.h"
+#include "vxfs_extern.h"
+
+
+extern struct address_space_operations vxfs_aops;
+extern struct address_space_operations vxfs_immed_aops;
+
+extern struct inode_operations vxfs_immed_symlink_iops;
+
+static struct file_operations vxfs_file_operations = {
+ .open = generic_file_open,
+ .llseek = generic_file_llseek,
+ .read = generic_file_read,
+ .mmap = generic_file_mmap,
+};
+
+
+kmem_cache_t *vxfs_inode_cachep;
+
+
+#ifdef DIAGNOSTIC
+/*
+ * Dump inode contents (partially).
+ */
+void
+vxfs_dumpi(struct vxfs_inode_info *vip, ino_t ino)
+{
+ printk(KERN_DEBUG "\n\n");
+ if (ino)
+ printk(KERN_DEBUG "dumping vxfs inode %ld\n", ino);
+ else
+ printk(KERN_DEBUG "dumping unknown vxfs inode\n");
+
+ printk(KERN_DEBUG "---------------------------\n");
+ printk(KERN_DEBUG "mode is %x\n", vip->vii_mode);
+ printk(KERN_DEBUG "nlink:%u, uid:%u, gid:%u\n",
+ vip->vii_nlink, vip->vii_uid, vip->vii_gid);
+ printk(KERN_DEBUG "size:%Lx, blocks:%u\n",
+ vip->vii_size, vip->vii_blocks);
+ printk(KERN_DEBUG "orgtype:%u\n", vip->vii_orgtype);
+}
+#endif
+
+
+/**
+ * vxfs_blkiget - find inode based on extent #
+ * @sbp: superblock of the filesystem we search in
+ * @extent: number of the extent to search
+ * @ino: inode number to search
+ *
+ * Description:
+ * vxfs_blkiget searches inode @ino in the filesystem described by
+ * @sbp in the extent @extent.
+ * Returns the matching VxFS inode on success, else a NULL pointer.
+ *
+ * NOTE:
+ * While __vxfs_iget uses the pagecache vxfs_blkiget uses the
+ * buffercache. This function should not be used outside the
+ * read_super() method, otherwise the data may be incoherent.
+ */
+struct vxfs_inode_info *
+vxfs_blkiget(struct super_block *sbp, u_long extent, ino_t ino)
+{
+ struct buffer_head *bp;
+ u_long block, offset;
+
+ block = extent + ((ino * VXFS_ISIZE) / sbp->s_blocksize);
+ offset = ((ino % (sbp->s_blocksize / VXFS_ISIZE)) * VXFS_ISIZE);
+ bp = sb_bread(sbp, block);
+
+ if (buffer_mapped(bp)) {
+ struct vxfs_inode_info *vip;
+ struct vxfs_dinode *dip;
+
+ if (!(vip = kmem_cache_alloc(vxfs_inode_cachep, SLAB_KERNEL)))
+ goto fail;
+ dip = (struct vxfs_dinode *)(bp->b_data + offset);
+ memcpy(vip, dip, sizeof(*vip));
+#ifdef DIAGNOSTIC
+ vxfs_dumpi(vip, ino);
+#endif
+ brelse(bp);
+ return (vip);
+ }
+
+fail:
+ printk(KERN_WARNING "vxfs: unable to read block %ld\n", block);
+ brelse(bp);
+ return NULL;
+}
+
+/**
+ * __vxfs_iget - generic find inode facility
+ * @sbp: VFS superblock
+ * @ino: inode number
+ * @ilistp: inode list
+ *
+ * Description:
+ * Search the for inode number @ino in the filesystem
+ * described by @sbp. Use the specified inode table (@ilistp).
+ * Returns the matching VxFS inode on success, else a NULL pointer.
+ */
+static struct vxfs_inode_info *
+__vxfs_iget(ino_t ino, struct inode *ilistp)
+{
+ struct page *pp;
+ u_long offset;
+
+ offset = (ino % (PAGE_SIZE / VXFS_ISIZE)) * VXFS_ISIZE;
+ pp = vxfs_get_page(ilistp->i_mapping, ino * VXFS_ISIZE / PAGE_SIZE);
+
+ if (!IS_ERR(pp)) {
+ struct vxfs_inode_info *vip;
+ struct vxfs_dinode *dip;
+ caddr_t kaddr = (char *)page_address(pp);
+
+ if (!(vip = kmem_cache_alloc(vxfs_inode_cachep, SLAB_KERNEL)))
+ goto fail;
+ dip = (struct vxfs_dinode *)(kaddr + offset);
+ memcpy(vip, dip, sizeof(*vip));
+#ifdef DIAGNOSTIC
+ vxfs_dumpi(vip, ino);
+#endif
+ vxfs_put_page(pp);
+ return (vip);
+ }
+
+ printk(KERN_WARNING "vxfs: error on page %p\n", pp);
+ return NULL;
+
+fail:
+ printk(KERN_WARNING "vxfs: unable to read inode %ld\n", ino);
+ vxfs_put_page(pp);
+ return NULL;
+}
+
+/**
+ * vxfs_stiget - find inode using the structural inode list
+ * @sbp: VFS superblock
+ * @ino: inode #
+ *
+ * Description:
+ * Find inode @ino in the filesystem described by @sbp using
+ * the structural inode list.
+ * Returns the matching VxFS inode on success, else a NULL pointer.
+ */
+struct vxfs_inode_info *
+vxfs_stiget(struct super_block *sbp, ino_t ino)
+{
+ return __vxfs_iget(ino, VXFS_SBI(sbp)->vsi_stilist);
+}
+
+/**
+ * vxfs_transmod - mode for a VxFS inode
+ * @vip: VxFS inode
+ *
+ * Description:
+ * vxfs_transmod returns a Linux mode_t for a given
+ * VxFS inode structure.
+ */
+static __inline__ mode_t
+vxfs_transmod(struct vxfs_inode_info *vip)
+{
+ mode_t ret = vip->vii_mode & ~VXFS_TYPE_MASK;
+
+ if (VXFS_ISFIFO(vip))
+ ret |= S_IFIFO;
+ if (VXFS_ISCHR(vip))
+ ret |= S_IFCHR;
+ if (VXFS_ISDIR(vip))
+ ret |= S_IFDIR;
+ if (VXFS_ISBLK(vip))
+ ret |= S_IFBLK;
+ if (VXFS_ISLNK(vip))
+ ret |= S_IFLNK;
+ if (VXFS_ISREG(vip))
+ ret |= S_IFREG;
+ if (VXFS_ISSOC(vip))
+ ret |= S_IFSOCK;
+
+ return (ret);
+}
+
+/**
+ * vxfs_iinit- helper to fill inode fields
+ * @ip: VFS inode
+ * @vip: VxFS inode
+ *
+ * Description:
+ * vxfs_instino is a helper function to fill in all relevant
+ * fields in @ip from @vip.
+ */
+static void
+vxfs_iinit(struct inode *ip, struct vxfs_inode_info *vip)
+{
+
+ ip->i_mode = vxfs_transmod(vip);
+ ip->i_uid = (uid_t)vip->vii_uid;
+ ip->i_gid = (gid_t)vip->vii_gid;
+
+ ip->i_nlink = vip->vii_nlink;
+ ip->i_size = vip->vii_size;
+
+ ip->i_atime = vip->vii_atime;
+ ip->i_ctime = vip->vii_ctime;
+ ip->i_mtime = vip->vii_mtime;
+
+ ip->i_blksize = PAGE_SIZE;
+ ip->i_blocks = vip->vii_blocks;
+ ip->i_generation = vip->vii_gen;
+
+ ip->u.generic_ip = (void *)vip;
+
+}
+
+/**
+ * vxfs_get_fake_inode - get fake inode structure
+ * @sbp: filesystem superblock
+ * @vip: fspriv inode
+ *
+ * Description:
+ * vxfs_fake_inode gets a fake inode (not in the inode hash) for a
+ * superblock, vxfs_inode pair.
+ * Returns the filled VFS inode.
+ */
+struct inode *
+vxfs_get_fake_inode(struct super_block *sbp, struct vxfs_inode_info *vip)
+{
+ struct inode *ip = NULL;
+
+ if ((ip = new_inode(sbp))) {
+ vxfs_iinit(ip, vip);
+ ip->i_mapping->a_ops = &vxfs_aops;
+ }
+ return (ip);
+}
+
+/**
+ * vxfs_put_fake_inode - free faked inode
+ * *ip: VFS inode
+ *
+ * Description:
+ * vxfs_put_fake_inode frees all data asssociated with @ip.
+ */
+void
+vxfs_put_fake_inode(struct inode *ip)
+{
+ iput(ip);
+}
+
+/**
+ * vxfs_read_inode - fill in inode information
+ * @ip: inode pointer to fill
+ *
+ * Description:
+ * vxfs_read_inode reads the disk inode for @ip and fills
+ * in all relevant fields in @ip.
+ */
+void
+vxfs_read_inode(struct inode *ip)
+{
+ struct super_block *sbp = ip->i_sb;
+ struct vxfs_inode_info *vip;
+ struct address_space_operations *aops;
+ ino_t ino = ip->i_ino;
+
+ if (!(vip = __vxfs_iget(ino, VXFS_SBI(sbp)->vsi_ilist)))
+ return;
+
+ vxfs_iinit(ip, vip);
+
+ if (VXFS_ISIMMED(vip))
+ aops = &vxfs_immed_aops;
+ else
+ aops = &vxfs_aops;
+
+ if (S_ISREG(ip->i_mode)) {
+ ip->i_fop = &vxfs_file_operations;
+ ip->i_mapping->a_ops = aops;
+ } else if (S_ISDIR(ip->i_mode)) {
+ ip->i_op = &vxfs_dir_inode_ops;
+ ip->i_fop = &vxfs_dir_operations;
+ ip->i_mapping->a_ops = aops;
+ } else if (S_ISLNK(ip->i_mode)) {
+ if (!VXFS_ISIMMED(vip)) {
+ ip->i_op = &page_symlink_inode_operations;
+ ip->i_mapping->a_ops = &vxfs_aops;
+ } else
+ ip->i_op = &vxfs_immed_symlink_iops;
+ } else
+ init_special_inode(ip, ip->i_mode, vip->vii_rdev);
+
+ return;
+}
+
+/**
+ * vxfs_put_inode - remove inode from main memory
+ * @ip: inode to discard.
+ *
+ * Description:
+ * vxfs_put_inode() is called on each iput. If we are the last
+ * link in memory, free the fspriv inode area.
+ */
+void
+vxfs_put_inode(struct inode *ip)
+{
+ if (atomic_read(&ip->i_count) == 1)
+ kmem_cache_free(vxfs_inode_cachep, ip->u.generic_ip);
+}
diff --git a/fs/freevxfs/vxfs_inode.h b/fs/freevxfs/vxfs_inode.h
new file mode 100644
index 00000000000000..e7d2fc114c2c4f
--- /dev/null
+++ b/fs/freevxfs/vxfs_inode.h
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2000-2001 Christoph Hellwig.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#ifndef _VXFS_INODE_H_
+#define _VXFS_INODE_H_
+
+#ident "$Id: vxfs_inode.h,v 1.15 2001/05/26 22:41:23 hch Exp hch $"
+
+/*
+ * Veritas filesystem driver - inode structure.
+ *
+ * This file contains the definition of the disk and core
+ * inodes of the Veritas Filesystem.
+ */
+
+
+#define VXFS_ISIZE 0x100 /* Inode size */
+
+#define VXFS_NDADDR 10 /* Number of direct addrs in inode */
+#define VXFS_NIADDR 2 /* Number of indirect addrs in inode */
+#define VXFS_NIMMED 96 /* Size of immediate data in inode */
+#define VXFS_NTYPED 6 /* Num of typed extents */
+
+#define VXFS_TYPED_OFFSETMASK (0x00FFFFFFFFFFFFFFULL)
+#define VXFS_TYPED_TYPEMASK (0xFF00000000000000ULL)
+#define VXFS_TYPED_TYPESHIFT 56
+
+#define VXFS_TYPED_PER_BLOCK(sbp) \
+ ((sbp)->s_blocksize / sizeof(struct vxfs_typed))
+
+/*
+ * Possible extent descriptor types for %VXFS_ORG_TYPED extents.
+ */
+enum {
+ VXFS_TYPED_INDIRECT = 1,
+ VXFS_TYPED_DATA = 2,
+ VXFS_TYPED_INDIRECT_DEV4 = 3,
+ VXFS_TYPED_DATA_DEV4 = 4,
+};
+
+/*
+ * Data stored immediately in the inode.
+ */
+struct vxfs_immed {
+ u_int8_t vi_immed[VXFS_NIMMED];
+};
+
+struct vxfs_ext4 {
+ u_int32_t ve4_spare; /* ?? */
+ u_int32_t ve4_indsize; /* Indirect extent size */
+ vx_daddr_t ve4_indir[VXFS_NIADDR]; /* Indirect extents */
+ struct direct { /* Direct extents */
+ vx_daddr_t extent; /* Extent number */
+ int32_t size; /* Size of extent */
+ } ve4_direct[VXFS_NDADDR];
+};
+
+struct vxfs_typed {
+ u_int64_t vt_hdr; /* Header, 0xTTOOOOOOOOOOOOOO; T=type,O=offs */
+ vx_daddr_t vt_block; /* Extent block */
+ int32_t vt_size; /* Size in blocks */
+};
+
+struct vxfs_typed_dev4 {
+ u_int64_t vd4_hdr; /* Header, 0xTTOOOOOOOOOOOOOO; T=type,O=offs */
+ u_int64_t vd4_block; /* Extent block */
+ u_int64_t vd4_size; /* Size in blocks */
+ int32_t vd4_dev; /* Device ID */
+ u_int32_t __pad1;
+};
+
+/*
+ * The inode as contained on the physical device.
+ */
+struct vxfs_dinode {
+ int32_t vdi_mode;
+ u_int32_t vdi_nlink; /* Link count */
+ u_int32_t vdi_uid; /* UID */
+ u_int32_t vdi_gid; /* GID */
+ u_int64_t vdi_size; /* Inode size in bytes */
+ u_int32_t vdi_atime; /* Last time accessed - sec */
+ u_int32_t vdi_autime; /* Last time accessed - usec */
+ u_int32_t vdi_mtime; /* Last modify time - sec */
+ u_int32_t vdi_mutime; /* Last modify time - usec */
+ u_int32_t vdi_ctime; /* Create time - sec */
+ u_int32_t vdi_cutime; /* Create time - usec */
+ u_int8_t vdi_aflags; /* Allocation flags */
+ u_int8_t vdi_orgtype; /* Organisation type */
+ u_int16_t vdi_eopflags;
+ u_int32_t vdi_eopdata;
+ union {
+ u_int32_t rdev;
+ u_int32_t dotdot;
+ struct {
+ u_int32_t reserved;
+ u_int32_t fixextsize;
+ } i_regular;
+ struct {
+ u_int32_t matchino;
+ u_int32_t fsetindex;
+ } i_vxspec;
+ u_int64_t align;
+ } vdi_ftarea;
+ u_int32_t vdi_blocks; /* How much blocks does inode occupy */
+ u_int32_t vdi_gen; /* Inode generation */
+ u_int64_t vdi_version; /* Version */
+ union {
+ struct vxfs_immed immed;
+ struct vxfs_ext4 ext4;
+ struct vxfs_typed typed[VXFS_NTYPED];
+ } vdi_org;
+ u_int32_t vdi_iattrino;
+};
+
+#define vdi_rdev vdi_ftarea.rdev
+#define vdi_dotdot vdi_ftarea.dotdot
+#define vdi_fixextsize vdi_ftarea.regular.fixextsize
+#define vdi_matchino vdi_ftarea.vxspec.matchino
+#define vdi_fsetindex vdi_ftarea.vxspec.fsetindex
+
+#define vdi_immed vdi_org.immed
+#define vdi_ext4 vdi_org.ext4
+#define vdi_typed vdi_org.typed
+
+
+/*
+ * The inode as represented in the main memory.
+ *
+ * TBD: This should become a separate structure...
+ */
+#define vxfs_inode_info vxfs_dinode
+
+#define vii_mode vdi_mode
+#define vii_uid vdi_uid
+#define vii_gid vdi_gid
+#define vii_nlink vdi_nlink
+#define vii_size vdi_size
+#define vii_atime vdi_atime
+#define vii_ctime vdi_ctime
+#define vii_mtime vdi_mtime
+#define vii_blocks vdi_blocks
+#define vii_org vdi_org
+#define vii_orgtype vdi_orgtype
+#define vii_gen vdi_gen
+
+#define vii_rdev vdi_ftarea.rdev
+#define vii_dotdot vdi_ftarea.dotdot
+#define vii_fixextsize vdi_ftarea.regular.fixextsize
+#define vii_matchino vdi_ftarea.vxspec.matchino
+#define vii_fsetindex vdi_ftarea.vxspec.fsetindex
+
+#define vii_immed vdi_org.immed
+#define vii_ext4 vdi_org.ext4
+#define vii_typed vdi_org.typed
+
+#endif /* _VXFS_INODE_H_ */
diff --git a/fs/freevxfs/vxfs_kcompat.h b/fs/freevxfs/vxfs_kcompat.h
new file mode 100644
index 00000000000000..3a2e7dac53d779
--- /dev/null
+++ b/fs/freevxfs/vxfs_kcompat.h
@@ -0,0 +1,21 @@
+#ifndef _VXFS_KCOMPAT_H
+#define _VXFS_KCOMPAT_H
+
+#include <linux/version.h>
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
+
+#include <linux/blkdev.h>
+
+typedef long sector_t;
+
+/* Dito. */
+static inline void map_bh(struct buffer_head *bh, struct super_block *sb, int block)
+{
+ bh->b_state |= 1 << BH_Mapped;
+ bh->b_dev = sb->s_dev;
+ bh->b_blocknr = block;
+}
+
+#endif /* Kernel 2.4 */
+#endif /* _VXFS_KCOMPAT_H */
diff --git a/fs/freevxfs/vxfs_lookup.c b/fs/freevxfs/vxfs_lookup.c
new file mode 100644
index 00000000000000..2942766f27f456
--- /dev/null
+++ b/fs/freevxfs/vxfs_lookup.c
@@ -0,0 +1,322 @@
+/*
+ * Copyright (c) 2000-2001 Christoph Hellwig.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ident "$Id: vxfs_lookup.c,v 1.21 2002/01/02 22:00:13 hch Exp hch $"
+
+/*
+ * Veritas filesystem driver - lookup and other directory related code.
+ */
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/highmem.h>
+#include <linux/kernel.h>
+#include <linux/pagemap.h>
+
+#include "vxfs.h"
+#include "vxfs_dir.h"
+#include "vxfs_inode.h"
+#include "vxfs_extern.h"
+
+/*
+ * Number of VxFS blocks per page.
+ */
+#define VXFS_BLOCK_PER_PAGE(sbp) ((PAGE_CACHE_SIZE / (sbp)->s_blocksize))
+
+
+static struct dentry * vxfs_lookup(struct inode *, struct dentry *);
+static int vxfs_readdir(struct file *, void *, filldir_t);
+
+struct inode_operations vxfs_dir_inode_ops = {
+ .lookup = vxfs_lookup,
+};
+
+struct file_operations vxfs_dir_operations = {
+ .readdir = vxfs_readdir,
+};
+
+
+static __inline__ u_long
+dir_pages(struct inode *inode)
+{
+ return (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+}
+
+static __inline__ u_long
+dir_blocks(struct inode *ip)
+{
+ u_long bsize = ip->i_sb->s_blocksize;
+ return (ip->i_size + bsize - 1) & ~(bsize - 1);
+}
+
+/*
+ * NOTE! unlike strncmp, vxfs_match returns 1 for success, 0 for failure.
+ *
+ * len <= VXFS_NAMELEN and de != NULL are guaranteed by caller.
+ */
+static __inline__ int
+vxfs_match(int len, const char * const name, struct vxfs_direct *de)
+{
+ if (len != de->d_namelen)
+ return 0;
+ if (!de->d_ino)
+ return 0;
+ return !memcmp(name, de->d_name, len);
+}
+
+static __inline__ struct vxfs_direct *
+vxfs_next_entry(struct vxfs_direct *de)
+{
+ return ((struct vxfs_direct *)((char*)de + de->d_reclen));
+}
+
+/**
+ * vxfs_find_entry - find a mathing directory entry for a dentry
+ * @ip: directory inode
+ * @dp: dentry for which we want to find a direct
+ * @ppp: gets filled with the page the return value sits in
+ *
+ * Description:
+ * vxfs_find_entry finds a &struct vxfs_direct for the VFS directory
+ * cache entry @dp. @ppp will be filled with the page the return
+ * value resides in.
+ *
+ * Returns:
+ * The wanted direct on success, else a NULL pointer.
+ */
+static struct vxfs_direct *
+vxfs_find_entry(struct inode *ip, struct dentry *dp, struct page **ppp)
+{
+ u_long npages, page, nblocks, pblocks, block;
+ u_long bsize = ip->i_sb->s_blocksize;
+ const char *name = dp->d_name.name;
+ int namelen = dp->d_name.len;
+
+ npages = dir_pages(ip);
+ nblocks = dir_blocks(ip);
+ pblocks = VXFS_BLOCK_PER_PAGE(ip->i_sb);
+
+ for (page = 0; page < npages; page++) {
+ caddr_t kaddr;
+ struct page *pp;
+
+ pp = vxfs_get_page(ip->i_mapping, page);
+ if (IS_ERR(pp))
+ continue;
+ kaddr = (caddr_t)page_address(pp);
+
+ for (block = 0; block <= nblocks && block <= pblocks; block++) {
+ caddr_t baddr, limit;
+ struct vxfs_dirblk *dbp;
+ struct vxfs_direct *de;
+
+ baddr = kaddr + (block * bsize);
+ limit = baddr + bsize - VXFS_DIRLEN(1);
+
+ dbp = (struct vxfs_dirblk *)baddr;
+ de = (struct vxfs_direct *)(baddr + VXFS_DIRBLKOV(dbp));
+
+ for (; (caddr_t)de <= limit; de = vxfs_next_entry(de)) {
+ if (!de->d_reclen)
+ break;
+ if (!de->d_ino)
+ continue;
+ if (vxfs_match(namelen, name, de)) {
+ *ppp = pp;
+ return (de);
+ }
+ }
+ }
+ vxfs_put_page(pp);
+ }
+
+ return NULL;
+}
+
+/**
+ * vxfs_inode_by_name - find inode number for dentry
+ * @dip: directory to search in
+ * @dp: dentry we seach for
+ *
+ * Description:
+ * vxfs_inode_by_name finds out the inode number of
+ * the path component described by @dp in @dip.
+ *
+ * Returns:
+ * The wanted inode number on success, else Zero.
+ */
+static ino_t
+vxfs_inode_by_name(struct inode *dip, struct dentry *dp)
+{
+ struct vxfs_direct *de;
+ struct page *pp;
+ ino_t ino = 0;
+
+ de = vxfs_find_entry(dip, dp, &pp);
+ if (de) {
+ ino = de->d_ino;
+ kunmap(pp);
+ page_cache_release(pp);
+ }
+
+ return (ino);
+}
+
+/**
+ * vxfs_lookup - lookup pathname component
+ * @dip: dir in which we lookup
+ * @dp: dentry we lookup
+ *
+ * Description:
+ * vxfs_lookup tries to lookup the pathname component described
+ * by @dp in @dip.
+ *
+ * Returns:
+ * A NULL-pointer on success, else an negative error code encoded
+ * in the return pointer.
+ */
+static struct dentry *
+vxfs_lookup(struct inode *dip, struct dentry *dp)
+{
+ struct inode *ip = NULL;
+ ino_t ino;
+
+ if (dp->d_name.len > VXFS_NAMELEN)
+ return ERR_PTR(-ENAMETOOLONG);
+
+ ino = vxfs_inode_by_name(dip, dp);
+ if (ino == 0)
+ return NULL;
+
+ ip = iget(dip->i_sb, ino);
+ if (!ip)
+ return ERR_PTR(-EACCES);
+ d_add(dp, ip);
+ return NULL;
+}
+
+/**
+ * vxfs_readdir - read a directory
+ * @fp: the directory to read
+ * @retp: return buffer
+ * @filler: filldir callback
+ *
+ * Description:
+ * vxfs_readdir fills @retp with directory entries from @fp
+ * using the VFS supplied callback @filler.
+ *
+ * Returns:
+ * Zero.
+ */
+static int
+vxfs_readdir(struct file *fp, void *retp, filldir_t filler)
+{
+ struct inode *ip = fp->f_dentry->d_inode;
+ struct super_block *sbp = ip->i_sb;
+ u_long bsize = sbp->s_blocksize;
+ u_long page, npages, block, pblocks, nblocks, offset;
+ loff_t pos;
+
+ switch ((long)fp->f_pos) {
+ case 0:
+ if (filler(retp, ".", 1, fp->f_pos, ip->i_ino, DT_DIR) < 0)
+ goto out;
+ fp->f_pos++;
+ /* fallthrough */
+ case 1:
+ if (filler(retp, "..", 2, fp->f_pos, VXFS_INO(ip)->vii_dotdot, DT_DIR) < 0)
+ goto out;
+ fp->f_pos++;
+ /* fallthrough */
+ }
+
+ pos = fp->f_pos - 2;
+
+ if (pos > VXFS_DIRROUND(ip->i_size))
+ return 0;
+
+ npages = dir_pages(ip);
+ nblocks = dir_blocks(ip);
+ pblocks = VXFS_BLOCK_PER_PAGE(sbp);
+
+ page = pos >> PAGE_CACHE_SHIFT;
+ offset = pos & ~PAGE_CACHE_MASK;
+ block = (u_long)(pos >> sbp->s_blocksize_bits) % pblocks;
+
+ for (; page < npages; page++, block = 0) {
+ caddr_t kaddr;
+ struct page *pp;
+
+ pp = vxfs_get_page(ip->i_mapping, page);
+ if (IS_ERR(pp))
+ continue;
+ kaddr = (caddr_t)page_address(pp);
+
+ for (; block <= nblocks && block <= pblocks; block++) {
+ caddr_t baddr, limit;
+ struct vxfs_dirblk *dbp;
+ struct vxfs_direct *de;
+
+ baddr = kaddr + (block * bsize);
+ limit = baddr + bsize - VXFS_DIRLEN(1);
+
+ dbp = (struct vxfs_dirblk *)baddr;
+ de = (struct vxfs_direct *)
+ (offset ?
+ (kaddr + offset) :
+ (baddr + VXFS_DIRBLKOV(dbp)));
+
+ for (; (caddr_t)de <= limit; de = vxfs_next_entry(de)) {
+ int over;
+
+ if (!de->d_reclen)
+ break;
+ if (!de->d_ino)
+ continue;
+
+ offset = (caddr_t)de - kaddr;
+ over = filler(retp, de->d_name, de->d_namelen,
+ ((page << PAGE_CACHE_SHIFT) | offset) + 2,
+ de->d_ino, DT_UNKNOWN);
+ if (over) {
+ vxfs_put_page(pp);
+ goto done;
+ }
+ }
+ offset = 0;
+ }
+ vxfs_put_page(pp);
+ offset = 0;
+ }
+
+done:
+ fp->f_pos = ((page << PAGE_CACHE_SHIFT) | offset) + 2;
+out:
+ return 0;
+}
diff --git a/fs/freevxfs/vxfs_olt.c b/fs/freevxfs/vxfs_olt.c
new file mode 100644
index 00000000000000..6b45a5a5c03bd8
--- /dev/null
+++ b/fs/freevxfs/vxfs_olt.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2000-2001 Christoph Hellwig.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ident "$Id: vxfs_olt.c,v 1.10 2002/01/02 23:03:58 hch Exp hch $"
+
+/*
+ * Veritas filesystem driver - object location table support.
+ */
+#include <linux/fs.h>
+#include <linux/kernel.h>
+
+#include "vxfs.h"
+#include "vxfs_olt.h"
+
+
+static __inline__ void
+vxfs_get_fshead(struct vxfs_oltfshead *fshp, struct vxfs_sb_info *infp)
+{
+ if (infp->vsi_fshino)
+ BUG();
+ infp->vsi_fshino = fshp->olt_fsino[0];
+}
+
+static __inline__ void
+vxfs_get_ilist(struct vxfs_oltilist *ilistp, struct vxfs_sb_info *infp)
+{
+ if (infp->vsi_iext)
+ BUG();
+ infp->vsi_iext = ilistp->olt_iext[0];
+}
+
+static __inline__ u_long
+vxfs_oblock(struct super_block *sbp, daddr_t block, u_long bsize)
+{
+ if (sbp->s_blocksize % bsize)
+ BUG();
+ return (block * (sbp->s_blocksize / bsize));
+}
+
+
+/**
+ * vxfs_read_olt - read olt
+ * @sbp: superblock of the filesystem
+ * @bsize: blocksize of the filesystem
+ *
+ * Description:
+ * vxfs_read_olt reads the olt of the filesystem described by @sbp
+ * into main memory and does some basic setup.
+ *
+ * Returns:
+ * Zero on success, else a negative error code.
+ */
+int
+vxfs_read_olt(struct super_block *sbp, u_long bsize)
+{
+ struct vxfs_sb_info *infp = VXFS_SBI(sbp);
+ struct buffer_head *bp;
+ struct vxfs_olt *op;
+ char *oaddr, *eaddr;
+
+
+ bp = sb_bread(sbp, vxfs_oblock(sbp, infp->vsi_oltext, bsize));
+ if (!bp || !bp->b_data)
+ goto fail;
+
+ op = (struct vxfs_olt *)bp->b_data;
+ if (op->olt_magic != VXFS_OLT_MAGIC) {
+ printk(KERN_NOTICE "vxfs: ivalid olt magic number\n");
+ goto fail;
+ }
+
+ /*
+ * It is in theory possible that vsi_oltsize is > 1.
+ * I've not seen any such filesystem yet and I'm lazy.. --hch
+ */
+ if (infp->vsi_oltsize > 1) {
+ printk(KERN_NOTICE "vxfs: oltsize > 1 detected.\n");
+ printk(KERN_NOTICE "vxfs: please notify hch@caldera.de\n");
+ goto fail;
+ }
+
+ oaddr = (char *)bp->b_data + op->olt_size;
+ eaddr = (char *)bp->b_data + (infp->vsi_oltsize * sbp->s_blocksize);
+
+ while (oaddr < eaddr) {
+ struct vxfs_oltcommon *ocp =
+ (struct vxfs_oltcommon *)oaddr;
+
+ switch (ocp->olt_type) {
+ case VXFS_OLT_FSHEAD:
+ vxfs_get_fshead((struct vxfs_oltfshead *)oaddr, infp);
+ break;
+ case VXFS_OLT_ILIST:
+ vxfs_get_ilist((struct vxfs_oltilist *)oaddr, infp);
+ break;
+ }
+
+ oaddr += ocp->olt_size;
+ }
+
+ brelse(bp);
+ return 0;
+
+fail:
+ brelse(bp);
+ return -EINVAL;
+}
diff --git a/fs/freevxfs/vxfs_olt.h b/fs/freevxfs/vxfs_olt.h
new file mode 100644
index 00000000000000..e24f949f8bdc93
--- /dev/null
+++ b/fs/freevxfs/vxfs_olt.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2000-2001 Christoph Hellwig.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#ifndef _VXFS_OLT_H_
+#define _VXFS_OLT_H_
+
+#ident "$Id: vxfs_olt.h,v 1.5 2001/04/25 18:11:23 hch Exp hch $"
+
+/*
+ * Veritas filesystem driver - Object Location Table data structures.
+ *
+ * This file contains definitions for the Object Location Table used
+ * by the Veritas Filesystem version 2 and newer.
+ */
+
+
+/*
+ * OLT magic number (vxfs_olt->olt_magic).
+ */
+#define VXFS_OLT_MAGIC 0xa504FCF5
+
+/*
+ * VxFS OLT entry types.
+ */
+enum {
+ VXFS_OLT_FREE = 1,
+ VXFS_OLT_FSHEAD = 2,
+ VXFS_OLT_CUT = 3,
+ VXFS_OLT_ILIST = 4,
+ VXFS_OLT_DEV = 5,
+ VXFS_OLT_SB = 6
+};
+
+/*
+ * VxFS OLT header.
+ *
+ * The Object Location Table header is placed at the beginning of each
+ * OLT extent. It is used to fing certain filesystem-wide metadata, e.g.
+ * the inital inode list, the fileset header or the device configuration.
+ */
+struct vxfs_olt {
+ u_int32_t olt_magic; /* magic number */
+ u_int32_t olt_size; /* size of this entry */
+ u_int32_t olt_checksum; /* checksum of extent */
+ u_int32_t __unused1; /* ??? */
+ u_int32_t olt_mtime; /* time of last mod. (sec) */
+ u_int32_t olt_mutime; /* time of last mod. (usec) */
+ u_int32_t olt_totfree; /* free space in OLT extent */
+ vx_daddr_t olt_extents[2]; /* addr of this extent, replica */
+ u_int32_t olt_esize; /* size of this extent */
+ vx_daddr_t olt_next[2]; /* addr of next extent, replica */
+ u_int32_t olt_nsize; /* size of next extent */
+ u_int32_t __unused2; /* align to 8 byte boundary */
+};
+
+/*
+ * VxFS common OLT entry (on disk).
+ */
+struct vxfs_oltcommon {
+ u_int32_t olt_type; /* type of this record */
+ u_int32_t olt_size; /* size of this record */
+};
+
+/*
+ * VxFS free OLT entry (on disk).
+ */
+struct vxfs_oltfree {
+ u_int32_t olt_type; /* type of this record */
+ u_int32_t olt_fsize; /* size of this free record */
+};
+
+/*
+ * VxFS initial-inode list (on disk).
+ */
+struct vxfs_oltilist {
+ u_int32_t olt_type; /* type of this record */
+ u_int32_t olt_size; /* size of this record */
+ vx_ino_t olt_iext[2]; /* initial inode list, replica */
+};
+
+/*
+ * Current Usage Table
+ */
+struct vxfs_oltcut {
+ u_int32_t olt_type; /* type of this record */
+ u_int32_t olt_size; /* size of this record */
+ vx_ino_t olt_cutino; /* inode of current usage table */
+ u_int32_t __pad; /* unused, 8 byte align */
+};
+
+/*
+ * Inodes containing Superblock, Intent log and OLTs
+ */
+struct vxfs_oltsb {
+ u_int32_t olt_type; /* type of this record */
+ u_int32_t olt_size; /* size of this record */
+ vx_ino_t olt_sbino; /* inode of superblock file */
+ u_int32_t __unused1; /* ??? */
+ vx_ino_t olt_logino[2]; /* inode of log file,replica */
+ vx_ino_t olt_oltino[2]; /* inode of OLT, replica */
+};
+
+/*
+ * Inode containing device configuration + it's replica
+ */
+struct vxfs_oltdev {
+ u_int32_t olt_type; /* type of this record */
+ u_int32_t olt_size; /* size of this record */
+ vx_ino_t olt_devino[2]; /* inode of device config files */
+};
+
+/*
+ * Fileset header
+ */
+struct vxfs_oltfshead {
+ u_int32_t olt_type; /* type number */
+ u_int32_t olt_size; /* size of this record */
+ vx_ino_t olt_fsino[2]; /* inodes of fileset header */
+};
+
+#endif /* _VXFS_OLT_H_ */
diff --git a/fs/freevxfs/vxfs_subr.c b/fs/freevxfs/vxfs_subr.c
new file mode 100644
index 00000000000000..51739281101ac6
--- /dev/null
+++ b/fs/freevxfs/vxfs_subr.c
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2000-2001 Christoph Hellwig.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ident "$Id: vxfs_subr.c,v 1.8 2001/12/28 20:50:47 hch Exp hch $"
+
+/*
+ * Veritas filesystem driver - shared subroutines.
+ */
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+
+#include "vxfs_kcompat.h"
+#include "vxfs_extern.h"
+
+
+static int vxfs_readpage(struct file *, struct page *);
+static int vxfs_bmap(struct address_space *, long);
+
+struct address_space_operations vxfs_aops = {
+ .readpage = vxfs_readpage,
+ .bmap = vxfs_bmap,
+ .sync_page = block_sync_page,
+};
+
+__inline__ void
+vxfs_put_page(struct page *pp)
+{
+ kunmap(pp);
+ page_cache_release(pp);
+}
+
+/**
+ * vxfs_get_page - read a page into memory.
+ * @ip: inode to read from
+ * @n: page number
+ *
+ * Description:
+ * vxfs_get_page reads the @n th page of @ip into the pagecache.
+ *
+ * Returns:
+ * The wanted page on success, else a NULL pointer.
+ */
+struct page *
+vxfs_get_page(struct address_space *mapping, u_long n)
+{
+ struct page * pp;
+
+ pp = read_cache_page(mapping, n,
+ (filler_t*)mapping->a_ops->readpage, NULL);
+
+ if (!IS_ERR(pp)) {
+ wait_on_page(pp);
+ kmap(pp);
+ if (!Page_Uptodate(pp))
+ goto fail;
+ /** if (!PageChecked(pp)) **/
+ /** vxfs_check_page(pp); **/
+ if (PageError(pp))
+ goto fail;
+ }
+
+ return (pp);
+
+fail:
+ vxfs_put_page(pp);
+ return ERR_PTR(-EIO);
+}
+
+/**
+ * vxfs_bread - read buffer for a give inode,block tuple
+ * @ip: inode
+ * @block: logical block
+ *
+ * Description:
+ * The vxfs_bread function reads block no @block of
+ * @ip into the buffercache.
+ *
+ * Returns:
+ * The resulting &struct buffer_head.
+ */
+struct buffer_head *
+vxfs_bread(struct inode *ip, int block)
+{
+ struct buffer_head *bp;
+ daddr_t pblock;
+
+ pblock = vxfs_bmap1(ip, block);
+ bp = sb_bread(ip->i_sb, pblock);
+
+ return (bp);
+}
+
+/**
+ * vxfs_get_block - locate buffer for given inode,block tuple
+ * @ip: inode
+ * @iblock: logical block
+ * @bp: buffer skeleton
+ * @create: %TRUE if blocks may be newly allocated.
+ *
+ * Description:
+ * The vxfs_get_block function fills @bp with the right physical
+ * block and device number to perform a lowlevel read/write on
+ * it.
+ *
+ * Returns:
+ * Zero on success, else a negativ error code (-EIO).
+ */
+static int
+vxfs_getblk(struct inode *ip, sector_t iblock,
+ struct buffer_head *bp, int create)
+{
+ daddr_t pblock;
+
+ pblock = vxfs_bmap1(ip, iblock);
+ if (pblock != 0) {
+ map_bh(bp, ip->i_sb, pblock);
+ return 0;
+ }
+
+ return -EIO;
+}
+
+/**
+ * vxfs_readpage - read one page synchronously into the pagecache
+ * @file: file context (unused)
+ * @page: page frame to fill in.
+ *
+ * Description:
+ * The vxfs_readpage routine reads @page synchronously into the
+ * pagecache.
+ *
+ * Returns:
+ * Zero on success, else a negative error code.
+ *
+ * Locking status:
+ * @page is locked and will be unlocked.
+ */
+static int
+vxfs_readpage(struct file *file, struct page *page)
+{
+ return block_read_full_page(page, vxfs_getblk);
+}
+
+/**
+ * vxfs_bmap - perform logical to physical block mapping
+ * @mapping: logical to physical mapping to use
+ * @block: logical block (relative to @mapping).
+ *
+ * Description:
+ * Vxfs_bmap find out the corresponding phsical block to the
+ * @mapping, @block pair.
+ *
+ * Returns:
+ * Physical block number on success, else Zero.
+ *
+ * Locking status:
+ * We are under the bkl.
+ */
+static int
+vxfs_bmap(struct address_space *mapping, long block)
+{
+ return generic_block_bmap(mapping, block, vxfs_getblk);
+}
diff --git a/fs/freevxfs/vxfs_super.c b/fs/freevxfs/vxfs_super.c
new file mode 100644
index 00000000000000..768e91bb371192
--- /dev/null
+++ b/fs/freevxfs/vxfs_super.c
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2000-2001 Christoph Hellwig.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ident "$Id: vxfs_super.c,v 1.29 2002/01/02 22:02:12 hch Exp hch $"
+
+/*
+ * Veritas filesystem driver - superblock related routines.
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include <linux/blkdev.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+
+#include "vxfs.h"
+#include "vxfs_extern.h"
+#include "vxfs_dir.h"
+#include "vxfs_inode.h"
+
+
+MODULE_AUTHOR("Christoph Hellwig");
+MODULE_DESCRIPTION("Veritas Filesystem (VxFS) driver");
+MODULE_LICENSE("Dual BSD/GPL");
+
+
+static void vxfs_put_super(struct super_block *);
+static int vxfs_statfs(struct super_block *, struct statfs *);
+
+static struct super_operations vxfs_super_ops = {
+ .read_inode = vxfs_read_inode,
+ .put_inode = vxfs_put_inode,
+ .put_super = vxfs_put_super,
+ .statfs = vxfs_statfs,
+};
+
+/**
+ * vxfs_put_super - free superblock resources
+ * @sbp: VFS superblock.
+ *
+ * Description:
+ * vxfs_put_super frees all resources allocated for @sbp
+ * after the last instance of the filesystem is unmounted.
+ */
+
+static void
+vxfs_put_super(struct super_block *sbp)
+{
+ struct vxfs_sb_info *infp = VXFS_SBI(sbp);
+
+ vxfs_put_fake_inode(infp->vsi_fship);
+ vxfs_put_fake_inode(infp->vsi_ilist);
+ vxfs_put_fake_inode(infp->vsi_stilist);
+
+ brelse(infp->vsi_bp);
+ kfree(infp);
+}
+
+/**
+ * vxfs_statfs - get filesystem information
+ * @sbp: VFS superblock
+ * @bufp: output buffer
+ *
+ * Description:
+ * vxfs_statfs fills the statfs buffer @bufp with information
+ * about the filesystem described by @sbp.
+ *
+ * Returns:
+ * Zero.
+ *
+ * Locking:
+ * We are under bkl and @sbp->s_lock.
+ *
+ * Notes:
+ * This is everything but complete...
+ */
+static int
+vxfs_statfs(struct super_block *sbp, struct statfs *bufp)
+{
+ struct vxfs_sb_info *infp = VXFS_SBI(sbp);
+
+ bufp->f_type = VXFS_SUPER_MAGIC;
+ bufp->f_bsize = sbp->s_blocksize;
+ bufp->f_blocks = infp->vsi_raw->vs_dsize;
+ bufp->f_bfree = infp->vsi_raw->vs_free;
+ bufp->f_bavail = 0;
+ bufp->f_files = 0;
+ bufp->f_ffree = infp->vsi_raw->vs_ifree;
+ bufp->f_namelen = VXFS_NAMELEN;
+
+ return 0;
+}
+
+/**
+ * vxfs_read_super - read superblock into memory and initalize filesystem
+ * @sbp: VFS superblock (to fill)
+ * @dp: fs private mount data
+ * @silent: do not complain loudly when sth is wrong
+ *
+ * Description:
+ * We are called on the first mount of a filesystem to read the
+ * superblock into memory and do some basic setup.
+ *
+ * Returns:
+ * The superblock on success, else %NULL.
+ *
+ * Locking:
+ * We are under the bkl and @sbp->s_lock.
+ */
+static struct super_block *
+vxfs_read_super(struct super_block *sbp, void *dp, int silent)
+{
+ struct vxfs_sb_info *infp;
+ struct vxfs_sb *rsbp;
+ struct buffer_head *bp = NULL;
+ u_long bsize;
+
+ infp = kmalloc(sizeof(*infp), GFP_KERNEL);
+ if (!infp) {
+ printk(KERN_WARNING "vxfs: unable to allocate incore superblock\n");
+ return NULL;
+ }
+ memset(infp, 0, sizeof(*infp));
+
+ bsize = sb_min_blocksize(sbp, BLOCK_SIZE);
+ if (!bsize) {
+ printk(KERN_WARNING "vxfs: unable to set blocksize\n");
+ goto out;
+ }
+
+ bp = sb_bread(sbp, 1);
+ if (!bp || !buffer_mapped(bp)) {
+ if (!silent) {
+ printk(KERN_WARNING
+ "vxfs: unable to read disk superblock\n");
+ }
+ goto out;
+ }
+
+ rsbp = (struct vxfs_sb *)bp->b_data;
+ if (rsbp->vs_magic != VXFS_SUPER_MAGIC) {
+ if (!silent)
+ printk(KERN_NOTICE "vxfs: WRONG superblock magic\n");
+ goto out;
+ }
+
+ if ((rsbp->vs_version < 2 || rsbp->vs_version > 4) && !silent) {
+ printk(KERN_NOTICE "vxfs: unsupported VxFS version (%d)\n",
+ rsbp->vs_version);
+ goto out;
+ }
+
+#ifdef DIAGNOSTIC
+ printk(KERN_DEBUG "vxfs: supported VxFS version (%d)\n", rsbp->vs_version);
+ printk(KERN_DEBUG "vxfs: blocksize: %d\n", rsbp->vs_bsize);
+#endif
+
+ sbp->s_magic = rsbp->vs_magic;
+ sbp->u.generic_sbp = (void *)infp;
+
+ infp->vsi_raw = rsbp;
+ infp->vsi_bp = bp;
+ infp->vsi_oltext = rsbp->vs_oltext[0];
+ infp->vsi_oltsize = rsbp->vs_oltsize;
+
+ if (!sb_set_blocksize(sbp, rsbp->vs_bsize)) {
+ printk(KERN_WARNING "vxfs: unable to set final block size\n");
+ goto out;
+ }
+
+ if (vxfs_read_olt(sbp, bsize)) {
+ printk(KERN_WARNING "vxfs: unable to read olt\n");
+ goto out;
+ }
+
+ if (vxfs_read_fshead(sbp)) {
+ printk(KERN_WARNING "vxfs: unable to read fshead\n");
+ goto out;
+ }
+
+ sbp->s_op = &vxfs_super_ops;
+ sbp->s_root = d_alloc_root(iget(sbp, VXFS_ROOT_INO));
+ if (!sbp->s_root) {
+ printk(KERN_WARNING "vxfs: unable to get root dentry.\n");
+ goto out_free_ilist;
+ }
+
+ return (sbp);
+
+out_free_ilist:
+ vxfs_put_fake_inode(infp->vsi_fship);
+ vxfs_put_fake_inode(infp->vsi_ilist);
+ vxfs_put_fake_inode(infp->vsi_stilist);
+out:
+ brelse(bp);
+ kfree(infp);
+ return NULL;
+}
+
+/*
+ * The usual module blurb.
+ */
+static DECLARE_FSTYPE_DEV(vxfs_fs_type, "vxfs", vxfs_read_super);
+
+static int __init
+vxfs_init(void)
+{
+ vxfs_inode_cachep = kmem_cache_create("vxfs_inode",
+ sizeof(struct vxfs_inode_info), 0, 0, NULL, NULL);
+ if (vxfs_inode_cachep)
+ return (register_filesystem(&vxfs_fs_type));
+ return -ENOMEM;
+}
+
+static void __exit
+vxfs_cleanup(void)
+{
+ unregister_filesystem(&vxfs_fs_type);
+ kmem_cache_destroy(vxfs_inode_cachep);
+}
+
+module_init(vxfs_init);
+module_exit(vxfs_cleanup);
diff --git a/fs/hfs/COPYING b/fs/hfs/COPYING
new file mode 100644
index 00000000000000..e77696ae8ddfea
--- /dev/null
+++ b/fs/hfs/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program 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 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/fs/hfs/ChangeLog b/fs/hfs/ChangeLog
new file mode 100644
index 00000000000000..723f2232d8fad4
--- /dev/null
+++ b/fs/hfs/ChangeLog
@@ -0,0 +1,2506 @@
+2000-01-02 a sun <asun@asun.cobalt.com>
+
+ * file.c (hfs_get_block): added hfs_get_block for regular files.
+
+1999-04-12 a sun <asun@hecate.darksunrising.blah>
+
+ * file_hdr.c (hdr_read): added rootinfo behaviour for DID header.
+
+1999-04-11 a sun <asun@hecate.darksunrising.blah>
+
+ * super.c (parse_options): added s_version so that we can select
+ between different versions of the same layout.
+
+1999-04-05 a sun <asun@hecate.darksunrising.blah>
+
+ * linux/hfs_fs.h: unified netatalk and appledouble header format.
+ added in all of the AFP attribute bits.
+
+ * file_hdr.c: added netatalk appledouble v2 compatible headers.
+
+1999-01-30 a sun <asun@hecate.darksunrising.blah>
+
+ * catalog.c (hfs_cat_move): fixed corruption problem with
+ renames.
+
+1999-01-27 a sun <asun@hecate.darksunrising.blah>
+
+ * file_hdr.c (get/set_dates): got rid of broken afpd times. NOTE:
+ you must use netatalk-1.4b2+asun2.1.2 or newer for this.
+
+1998-12-20 a sun <asun@hecate.darksunrising.blah>
+
+ * bdelete.c (del_root): assign bthLNode and bthFNode only if the
+ root node becomes a leaf node. Disk First Aid no longer
+ complains. Norton Utilities, of course, has decided that it
+ doesn't like the root node number. bleah. i think that it might be
+ due to Norton Utilities not expecting the root node to have moved.
+
+1998-12-16 a sun <asun@hecate.darksunrising.blah>
+
+ * sysdep.c (hfs_revalidate_dentry): fix inode dates when there's a
+ timezone change.
+
+1998-12-15 root <root@hecate.darksunrising.blah>
+
+ * extent.c (new_extent): expand block size variables to handle
+ u32.
+
+ * mdb.c (hfs_mdb_get): AlBlkSiz shouldn't be capped at 65535. we
+ should be able to handle much larger volumes now.
+
+1998-11-21 a sun <asun@hecate.darksunrising.blah>
+
+ * hfs_sysdep.h, hfs_fs.h: added hfs_from_utc/to_utc to deal with
+ date differences on hfs formatted media.
+
+ NOTE: hfs extended keeps everything in utc, so we'll need to deal
+ with that when appropriate.
+
+1998-11-12 a sun <asun@hecate.darksunrising.blah>
+
+ * extent.c (shrink_fork): added some lock_bitmap/unlock_bitmap's
+ to protect hfs_clear_vbm_bits. we should no longer have problems
+ with free_ablocks wrapping around.
+
+1998-11-02 a sun <asun@hecate.darksunrising.blah>
+
+ * mdb.c (hfs_mdb_get): plugged up an mdb failed initialization
+ leak.
+
+1998-10-31 a sun <asun@hecate.darksunrising.blah>
+
+ * version.c (hfs_version): bumped to version 0.96.
+
+ * mdb.c (hfs_mdb_commit): you only write out the alternate MDB
+ when the catalog or extents overflow files grow. that just leaves
+ the btree corruption problems. bleah (whilst deleting a bunch of
+ files, more of the btree can get pruned away than desired).
+
+1998-10-30 a sun <asun@hecate.darksunrising.blah>
+
+ * dir.c: fixed a bunch of silliness with deletions. make sure to
+ zero out stuff and set mark_inode_dirty().
+
+1998-10-29 a sun <asun@hecate.darksunrising.blah>
+
+ * string.c (hfs_strcmp, hfs_streq, hfs_strhash): converted them to
+ take name/len arguments instead of hfs_name to reduce copying.
+
+ * dir.c, dir_nat.c, dir_cap.c, dir_dbl.c, sysdep.c: modified
+ relevant areas to reflect string.c changes.
+
+1998-10-28 a sun <asun@hecate.darksunrising.blah>
+
+ * hfs.h (hfs_lookup_dentry): oh my. more silliness. make sure to
+ have the d_lookup use the same hash value as the one generated by
+ hfs_hash_dentry. i also changed the argument order.
+ (hfs_drop_special): change the argument order to be more in line
+ with what the dcache stuff looks like.
+
+ * sysdep.c (hfs_compare_dentry): the compare was returning the
+ wrong value for correct matches and causing all sorts of
+ mischief. this fixes both directory counts and mounting on top of
+ hfs volumes.
+
+ * file.c, file_cap.c, file_hdr.c: added mark_inode_dirty()'s in
+ the relevant places.
+
+1998-10-11 root <asun@hecate.darksunrising.blah>
+
+ * mdb.c (hfs_mdb_get): moved initialization of mdb->entry_dirty
+ list to here to deal with trying to read a bad hfs volume.
+
+1998-10-10 a sun <asun@zoology.washington.edu>
+
+ * inode.c, catalog.c, dir_*.c, sysdep.c: parts of the dcache
+ conversion didn't get done properly. specifically, i forgot to
+ move the hfs_cat_puts into the right place. that's fixed now.
+
+1998-09-11 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * mdb.c: altered mdb struct to reflect hfs plus usage.
+
+1998-08-27 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * file.c, file_hdr.c, file_cap.c: dealt with the remaining
+ copy_to/from_user() error cases.
+
+1998-08-26 a sun <asun@purgatorius.zoology.washington.edu>
+
+ * super.c (hfs_read_super): fixed to deal with cdroms. why doesn't
+ the cdrom layer call the partition table code?
+
+Wed Jan 21 14:04:26 1998 a sun <asun@zoology.washington.edu>
+
+ * inode.c, sysdep.c
+ use d_iput to uncache dentry from catalog entry instead of relying
+ on put_inode. no more NULL pointer dereferences!
+
+ * catalog.c
+ cleaned up hfs_cat_put a little.
+
+ ISSUES (non-fatal): mv dir dir2 while creating files in dir screws
+ up directory counts.
+
+ deletion using netatalk screws up directory
+ counts.
+
+Thu Jan 15 19:14:28 1998 a sun <asun@zoology.washington.edu>
+
+ * catalog.c
+ make deletion happen when requested instead of waiting until
+ an hfs_cat_put as the dcache can hold onto entries for quite
+ some time.
+
+Wed Jan 14 14:43:16 1998 a sun <asun@zoology.washington.edu>
+
+ * catalog.c
+ the current catalog allocation scheme allocates
+ PAGE_SIZE/sizeof(struct hfs_cat_entry) entries at a time and keeps
+ a pool of free entries up to this allocation unit * 8.
+
+ * inode.c
+ make sure to always hfs_cat_put if hfs_iget is going to return
+ NULL.
+
+ * string.c, catalog.c
+ use linux' hashing method to generate hashes. the old hashing was
+ getting collisions. catalog.c also has a larger hash table to
+ prevent collisions.
+
+Tue Jan 13 13:06:01 1998 a sun <asun@zoology.washington.edu>
+
+ * version.c
+ bumped to 0.95+asun3
+
+ * catalog.c
+ re-wrote to dynamically allocate/delete catalog entries. on a 486,
+ entries fit into the size-256 slab.
+
+Wed Jan 7 19:33:33 1998 a sun <asun@zoology.washington.edu>
+
+ * inode.c
+ don't hfs_cat_put gratuitously in hfs_iget. that's a bad
+ idea and results in screwed up entry counts.
+
+Tue Jan 6 14:38:24 1998 a sun <asun@zoology.washington.edu>
+
+ * version.c
+ changed it to 0.95+asun2
+
+ * sysdep.c
+ altered catalog entry pruning to make sure that an iput
+ gets done. for some reason, shrink_dcache_parent wasn't
+ doing it.
+
+ * catalog.c
+ added a global dirty list to check for pruning.
+
+Tue Jan 6 12:29:52 1998 a sun <asun@zoology.washington.edu>
+
+ * catalog.c
+ re-wrote it to be similar to 2.1.x inode.c. this should
+ at least make catalog.c SMP safe.
+
+ * hfs.h, linux/hfs_fs.h
+ moved dentry operations into hfs.h. these probably should
+ be moved somewhere else.
+
+ * super.c, dir_cap.c, dir_nat.c, dir_dbl.c, sysdep.c
+ added dentry ops to hash everything to lowercase.
+
+Sun Dec 28 22:48:53 1997 a sun <asun@zoology.washington.edu>
+
+ * sysdep.c, catalog.c, hfs.h
+ as a temporary workaround until catalog.c gets re-written,
+ i flush the dcache if we need more entries.
+
+Fri Dec 19 15:11:21 1997 a sun <asun@zoology.washington.edu>
+
+ * dir_dbl.c
+ statically allocate tmp_name instead of doing it dynamically.
+
+ NOTE: well, those pesky hfs_cat_put messages still aren't gone. in
+ addition, catalog.c needs to be modified to free up some entries
+ when the cache gets filled up.
+
+Sun Dec 14 11:51:11 1997 a sun <asun@zoology.washington.edu>
+
+ * linux/hfs_fs.h
+ moved the dentry stuff into within the #ifdef __KERNEL__
+ part of hfs_fs.h and cleaned up a little.
+
+Sun Dec 14 11:24:54 1997 a sun <asun@zoology.washington.edu>
+
+ * dir.c
+ changed hfs_rename to delete all old dentries. hfs_cat_put
+ messages on umount should be a thing of the past now.
+
+Sun Dec 14 01:12:58 1997 a sun <asun@zoology.washington.edu>
+
+ * dir.c
+ changed mark_inodes_deleted to dget/d_delete/dput the dentry
+ instead of just dropping it. the bytes available should now
+ be updated properly upon deletion.
+
+Wed Dec 10 00:01:25 1997 a sun <asun@zoology.washington.edu>
+
+ * dir.c
+ changed mark_inodes_deleted to drop the dentry instead of
+ just deleting it.
+
+ TODO: bytes available aren't being properly updated when a
+ resource fork gets deleted.
+
+Mon Dec 8 23:22:40 1997 a sun <asun@zoology.washington.edu>
+
+ * dir_cap.c, dir_nat.c, dir_dbl.c, dir.c
+ * hfs.h, linux/hfs_sysdep.h, linux/hfs_fs_i.h
+ Added code to drop ({dbl,cap,nat}_drop_dentry) invalid
+ dentries when creating or moving a file.
+
+ * inode.c
+ Added code to delete cached dentries when a file gets deleted.
+
+ * current yuckiness: there's an extra hfs_cat_put somewhere. it's
+ harmless but bothersome.
+
+Thu Dec 4 00:14:03 1997 a sun <asun@zoology.washington.edu>
+
+ * dir.c, dir_cap.c, dir_nat.c, file.c, file_hdr.c, inode.c,
+ * linux/{hfs_sysdep.h, hfs_fs.h}, version.c:
+ Completed first code dentrification sweep. It mounts! It copies!
+ It dcaches!
+
+Mon Apr 28 06:58:44 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * version.c, INSTALL.sgml, HFS.sgml:
+ Bump version to 0.95 (Woohoo! We're beta!)
+
+ * linux/hfs_fs.h:
+ Modify HFS_SB() and HFS_I() when compiled into the kernel.
+
+ * FAQ.sgml:
+ Add a new question (and its answer):
+ Why does my Macintosh show generic application and document icons?
+
+ * HFS.sgml:
+ Add some URLs and remove the (now empty) FAQ section.
+
+Sun Apr 27 22:17:01 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * HFS.sgml:
+ Don't call the version 1 headers "slightly modified".
+
+ * file_hdr.c, dir_nat.c:
+ Comment some AFPD compatibility stuff.
+
+ * FAQ.sgml:
+ Update for version 0.95.
+
+ * BUG_INFO:
+ Remove the BIG_INFO script since we no longer mention it.
+
+ * README.sgml, INSTALL.sgml, HFS.sgml, Makefile:
+ Split README.sgml into HFS.sgml and INSTALL.sgml.
+ Stop including the document sources in snapshots.
+
+ * file_hdr.c:
+ Fix hdr_truncate() not to truncate the data fork.
+
+Wed Apr 16 23:56:25 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * FAQ.sgml:
+ Bump version to 0.8.4 and add two answers:
+ How to fsck an HFS filesystem.
+ How to generate linux/version.h.
+
+ * version.c, README.sgml:
+ Bump version to 0.8.4.
+
+ * README.sgml, FAQ.sgml, Makefile:
+ Separate the FAQ from the README.
+
+ * linux/hfs_fs.h:
+ Add (struct hfs_fork) to the forward declarations.
+
+Thu Apr 10 05:47:16 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * linux/hfs_sysdep.h:
+ Work around the non-const declaration of test_bit()'s second argument.
+
+ * Makefile:
+ Use .config from the kernel source to check for MODVERSIONS.
+
+Wed Apr 9 07:57:17 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * bnode.c:
+ Check the record table in each bnode as we read it from disk.
+
+ * super.c, mdb.c, hfs.h:
+ Deal with the ATTRIB_CLEAN bit of the MDB properly (in mdb.c).
+
+ * super.c, hfs.h, mdb.c:
+ Search for the alt-MDB rather than using the device size to find it.
+
+Wed Apr 9 03:39:05 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * version.c, README.sgml:
+ Bump version to 0.8.3.
+
+Mon Apr 7 20:09:56 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * part_tbl.c:
+ Fix to allow bootable CDROMs (which have blocksize != 512) to mount.
+
+ * super.c:
+ Check that blk_size[MAJOR(dev)] is non-NULL before dereferencing.
+
+Sat Apr 5 10:44:42 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * hfs_btree.h, binsert.c, brec.c, bfind.c, bins_del.c, bdelete.c:
+ Make btree operations less likely to do
+ nasty things if the tree is corrupted.
+
+ * part_tbl.c, README.sgml:
+ Count partitions from 0 rather than from 1.
+
+Wed Apr 2 23:26:51 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * bdelete.c:
+ Don't bother checking for oversized keys in hfs_bdelete().
+
+ * bdelete.c, bfind.c, binsert.c:
+ Verify key lengths against the maximum given for the tree.
+
+ * Makefile:
+ Check that /usr/include/linux/modversions.h exists before including it.
+ This allows compilation without CONFIG_MODVERSIONS enabled.
+
+Sat Mar 29 13:17:53 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * linux/hfs_fs.h, super.c, file_hdr.c, hfs.h, extent.c, file_cap.c,
+ dir_dbl.c, dir_nat.c, dir.c, dir_cap.c, binsert.c, catalog.c,
+ bfind.c:
+ Make (struct hfs_bkey) and (struct hfs_brec) more "abstract".
+
+ * binsert.c:
+ Remove redundant test in hfs_binsert().
+
+Sat Mar 29 05:24:23 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * version.c, README.sgml:
+ Fix formatting problems in README.sgml and bump version to 0.8.2.
+
+ * extent.c:
+ Fix bug that caused serious headaches with fragmented files.
+
+Fri Mar 28 00:23:18 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * version.c, README.sgml:
+ Bump version to 0.8.1.
+
+ * btree.c, balloc.c:
+ Commit map nodes to buffers when new map nodes are added.
+
+Thu Mar 27 22:41:07 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * Makefile:
+ Include linux/modversions.h from the gcc command line.
+
+ * mdb.c:
+ Was updating modified date twice in hfs_mdb_commit().
+
+ * linux/hfs_sysdep.h, linux/hfs_fs.h, linux/hfs_fs_i.h,
+ linux/hfs_fs_sb.h, sysdep.c, trans.c, super.c, hfs_sysdep.h, inode.c,
+ hfs_fs_i.h, hfs_fs_sb.h, hfs_fs.h, hfs.h, file_cap.c, file_hdr.c,
+ file.c, dir_nat.c, dir_cap.c, dir_dbl.c, Makefile, dir.c:
+ Rearrange headers in preparation for inclusion in the kernel.
+
+ * hfs_fs_sb.h, hfs_fs.h:
+ Add forward declarations so other code can include these headers.
+
+ * hfs_sysdep.h:
+ Include __constant_hton[ls]() for little-endian machines.
+
+ * hfs_fs.h, hfs_sysdep.h, hfs.h:
+ Move typedefs of hfs_{byte,word,lword}_t from hfs.h to hfs_sysdep.h.
+ Include hfs_sysdep.h from hfs_fs.h.
+
+ * trans.c, super.c, part_tbl.c, string.c, inode.c, mdb.c, hfs_fs_sb.h,
+ hfs_sysdep.h, hfs_fs.h, hfs.h, hfs_btree.h, file_cap.c, file_hdr.c,
+ file.c, dir_nat.c, extent.c, dir_dbl.c, dir.c, dir_cap.c, catalog.c,
+ btree.c, bnode.c, brec.c, bitmap.c, bitops.c, bins_del.c, binsert.c,
+ bdelete.c, bfind.c, balloc.c:
+ Big type system changes in preparation for kernel inclusion:
+ '[US](8|16|32)' -> 'hfs_[us](8|16|32)' (avoids name space pollution)
+ 'hfs_name_t' -> 'struct hfs_name' (allows forward declaration)
+
+ * super.c, hfs_fs.h:
+ Add init_hfs_fs() to super.c for non-module compilation.
+
+Wed Mar 26 07:53:59 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * version.c, README.sgml:
+ Bump version to 0.8.
+
+ * README.sgml:
+ Special compilation note for DEC Alpha.
+
+ * README.sgml:
+ Note status on non-Intel processors.
+
+ * hfs_fs.h:
+ Use long's for read() and write() on the Alpha.
+
+ * README.sgml:
+ Document the afpd mount option.
+
+ * inode.c:
+ Make files always writable for owner in afpd mode.
+
+Tue Mar 25 23:21:39 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * part_tbl.c:
+ Clean up the error checking code a bit.
+
+Sat Mar 22 19:43:40 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * part_tbl.c:
+ Fixed uninitialized variable in old-style partition code.
+
+ * bins_del.c, bdelete.c:
+ Fix extraneous "bad argument to shift_{left,right}" messages.
+
+ * bitops.c:
+ Note that these routines are now tested on Intel, PPC and Alpha.
+
+ * Makefile:
+ Add -fno-builtin to the CFLAGS.
+
+Fri Feb 14 10:50:14 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * hfs_sysdep.h:
+ Don't include <asm/*.h> until after <linux/types.h>.
+
+ * catalog.c:
+ Use volume create date in hashfn() rather than casting pointer to int.
+
+ * hfs.h, mdb.c:
+ Maintaing volume create, modify and backup dates in struct hfs_mdb.
+
+ * hfs_fs.h:
+ Include the header for put_user BEFORE using it!
+
+ * string.c, hfs.h:
+ Make hfs_strhash() return an unsigned int.
+
+ * trans.c, version.c, super.c, mdb.c, part_tbl.c, string.c, inode.c,
+ hfs_sysdep.h, hfs_fs.h, hfs_fs_sb.h, hfs_btree.h, hfs.h, file_cap.c,
+ file_hdr.c, extent.c, dir_dbl.c, dir_nat.c, dir_cap.c, dir.c,
+ catalog.c, btree.c, bnode.c, brec.c, bitmap.c, binsert.c,
+ bins_del.c, bdelete.c, balloc.c, README.sgml, Makefile:
+ Updated copyright notices.
+
+ * trans.c, part_tbl.c, string.c, super.c, inode.c, mdb.c, hfs_fs.h,
+ hfs_fs_sb.h, hfs_sysdep.h, hfs_btree.h, hfs.h, file_cap.c,
+ file_hdr.c, dir_nat.c, extent.c, dir_cap.c, dir_dbl.c, catalog.c,
+ dir.c, brec.c, btree.c, bitmap.c, bnode.c, bdelete.c, bins_del.c,
+ binsert.c, Makefile, TODO, balloc.c:
+ First shot at portability to the DEC Alpha and non-gcc compilers.
+ This involved a significant overhaul of the type system.
+
+Tue Feb 4 04:26:54 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * version.c, README.sgml:
+ Bump version to "pre-0.8-4".
+
+ * dir_nat.c:
+ Allow creat() in Netatalk .AppleDouble directories.
+
+ * dir_dbl.c:
+ Make local functions static.
+
+ * dir_dbl.c:
+ Removed unnecessary 'extern' qualifiers from forward declarations.
+
+ * file_hdr.c, TODO:
+ Fixed the 30-year time warp with afpd.
+
+ * TODO, trans.c:
+ Don't mangle the name .AppleDesktop under fork=netatalk.
+
+Mon Feb 3 23:18:45 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * inode.c:
+ Make header files always writable when the afpd mount option is given.
+ Otherwise it is impossible to unlock a locked file.
+
+ * TODO, inode.c:
+ Let afpd think chmod() always succeeds, so "New Folder" works right.
+
+ * super.c:
+ The 'afpd' mount option now makes 'fork=n,names=n' the default.
+
+ * TODO:
+ List the current known afpd-compatibility problems as bugs.
+
+ * file_hdr.c:
+ Make certain date changes through header files get written to disk.
+
+Sat Feb 1 02:24:12 1997 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * mdb.c:
+ Work around for Linux rounding device sizes to 1k increments.
+
+ * README.sgml:
+ Fixed a typo: "the a".
+
+Sat Dec 28 20:41:01 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * TODO:
+ Add ioctl() interface as a "missing feature."
+
+ * dir_nat.c:
+ Finish implementing the afpd-compatibility
+ mode using the new 'afpd' mount option.
+
+ * hfs_fs_sb.h, super.c:
+ Add new 'afpd' mount option.
+
+ * file_cap.c:
+ Spelling fix.
+
+Wed Dec 11 23:16:08 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * TODO, README.sgml:
+ Optimistically document the hybrid CD problem as fixed.
+
+ * part_tbl.c:
+ Fix the partition code so at least some of the hybrid
+ CDROMs that were previously rejected are now accepted.
+
+ * hfs.h:
+ Make fs_start a 32-bit integer rather than 16-bits.
+ The 16-bit value would overflow if a partition started
+ beyond the 32M mark (e.g. the Executor 2 Beta 1 CDROM).
+
+ * extent.c:
+ Fixed a typo in an error message.
+
+Tue Dec 10 14:43:46 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * dir_nat.c:
+ Merge in the (still dormant) afpd-compatibility changes.
+
+ * inode.c:
+ Make the .AppleDouble directory writable (again).
+
+ * version.c, README.sgml:
+ Bump version up to "pre-0.8-3".
+
+ * hfs_fs.h, file_cap.c, file_hdr.c:
+ Move AFP constants to hfs_fs.h and prefix them with "HFS_".
+
+ * dir_nat.c, inode.c:
+ Back-out changes that allowed writing to the .AppleDouble directory.
+
+ * Makefile:
+ Update rules for linuxdoc-sgml v1.5.
+
+ * extent.c:
+ Fixed serious bug in decode_extent() with handling of empty extents.
+
+ * file.c:
+ Rewrote hfs_getblk().
+ It will no longer hang if hfs_extent_map() is buggy.
+ Also halves the worst-case number of calls to hfs_extent_map().
+
+ * extent.c:
+ Fixed serious bug in decode_extent() with handling of empty extents.
+
+ * hfs_fs.h:
+ Small change so the PPC (and maybe other architectures?)
+ pick up the prototypes for the user-space access functions.
+
+ * super.c, file_cap.c, file_hdr.c, hfs_fs.h, file.c:
+ Updated for new user-space memory interface.
+
+Sun Dec 8 11:49:36 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * dir_nat.c:
+ Add special code for unlink(), and rename() in the .AppleDouble
+ directory and rmdir() of the .AppleDouble directory.
+
+ * inode.c:
+ Make the .AppleDouble directory writable.
+
+ * file_hdr.c:
+ Use AFP flags in version 1 headers (for Netatalk compatibility).
+
+ * trans.c:
+ Fixed bug with long names causing kernel Oops.
+
+Mon Oct 7 06:05:01 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * hfs_fs.h, file_cap.c, file_hdr.c, hfs.h, extent.c, file.c, dir.c:
+ Fix types for various read/write/truncate computations.
+ Also allows compilation with 2.1.x kernels.
+
+Thu Sep 19 10:28:43 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * README.sgml, version.c:
+ Bump version up to "pre-0.8-2".
+
+ * TODO:
+ Reformat the To Do list introducing prioritized categories.
+
+ * file_hdr.c, file.c:
+ Move comments about mmap() for headers from file.c to file_hdr.c.
+ Also revise the reasoning for not yet having it implemented.
+
+ * dir_nat.c, dir_cap.c, dir_dbl.c:
+ Remove 'hfs_' prefix from names of some purely local functions.
+
+ * dir_dbl.c, TODO:
+ Under AppleDouble make create(), mkdir(), mknod(), unlink(), rename()
+ and rename() check against header files when arguments start with '%'.
+
+ * super.c, hfs_fs_sb.h, hfs_fs.h, dir_dbl.c, dir_nat.c, dir_cap.c,
+ dir.c, README.sgml:
+ Fix problem that prevented creating %RootInfo or .rootinfo in all
+ directories in addition to preventing deletion from the root directory.
+
+ * TODO:
+ Remove writable header files from the To Do list.
+
+ * README.sgml:
+ Add extensive discussion of writing to HFS filesystems and
+ the format of the special files.
+
+ * file_hdr.c:
+ Generate the 'homefs' field for version 1 header files.
+
+Wed Sep 18 23:07:45 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * hfs_fs.h, file_cap.c:
+ Comment the definition of (struct hfs_cap_info).
+
+ * version.c, README.sgml:
+ Bump version up to "pre-0.8-1" and update the "How can I write?" FAQ.
+
+ * file_hdr.c:
+ Implement hdr_write() and hdr_truncate()!!
+
+ * hfs_fs_i.h, inode.c:
+ Make hdr_layout per-inode (not per-file) so hdr_truncate() will work.
+
+ * file.c, hfs.h, catalog.c, extent.c, balloc.c:
+ hfs_extent_adj() now uses fork->lsize to determine the target file size.
+
+Sun Sep 15 07:55:24 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * README.sgml, trans.c:
+ Prevent creation of files & directories with '\0' or ':' in their names.
+
+ * string.c, hfs_fs.h, hfs.h, dir_dbl.c, dir_nat.c, dir_cap.c:
+ With case=lower could have run off end of string.
+
+Tue Sep 10 12:05:47 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * inode.c:
+ Small clean up of HFS_FIL_LOCK handling.
+
+ * inode.c:
+ Fix notify_change() not to accidentally make metadata executable.
+
+ * hfs_fs.h:
+ AppleSingle files should have HFS_ITYPE_NORM.
+
+ * inode.c:
+ Return to old behavior where MdDat = i_mtime.
+
+ * dir_dbl.c:
+ Fix serious bug in hfs_dbl_readdir() that would lock-up access to a
+ directory if one tried to write to a directory they had previously read.
+
+ * file.c:
+ Fix hfs_do_write() to adjust the fork's 'lsize' if it changed.
+
+ * inode.c, file_cap.c:
+ Allow truncate() to be called even on metadata.
+ Any size changes will last only until the next iput() of the inode.
+ Truncating a header file doesn't yet truncate the resource fork.
+
+ * inode.c:
+ Allow chmod() on a directory if it doesn't actually change i_mode.
+
+ * hfs_fs.h, trans.c, super.c:
+ Rename hfs_cap2mac() to hfs_colon2mac().
+ Rename hfs_apl2mac() to hfs_prcnt2mac().
+
+ * file_hdr.c:
+ Move header construction out of hdr_read() to create hdr_build_meta().
+
+ * hfs.h:
+ Add byte-order independent conversions: U32->U16, U32->U8 and U16->U8.
+
+ * file.c, file_cap.c, hfs_fs.h:
+ Rename fix_perms() to hfs_file_fix_mode() and
+ move it from file_cap.c to file.c.
+
+ * README.sgml, super.c:
+ Make the default for the names mount option vary with the fork option.
+
+ * file_cap.c:
+ The umask was applied incorrectly in fix_perms().
+
+Mon Sep 9 13:11:28 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * README.sgml:
+ Note that it compiles on m68k machines, but needs more testing.
+
+ * hfs_sysdep.h, Makefile:
+ Changes to compile unmodified on m68k (and possibly other machines).
+
+ * dir_cap.c:
+ hfs_cap_readdir() was mistakenly producing .rootinfo entries for
+ the .finderinfo and .resource subdirectories of the root directory.
+
+ * inode.c:
+ A directory's i_size was too small by 1 under CAP, so hfs_cap_readdir()
+ would omit the last directory entry. i_nlink was also too large by 1.
+
+Sun Sep 8 12:56:06 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * file_hdr.c:
+ Rewrite hdr_read() to be more efficient and to deal correctly with
+ descriptors having lengths that differ from the actual size of the data.
+
+ * file_cap.c:
+ Add write support for CAP finderinfo files!!
+
+ * super.c, inode.c, hfs_fs.h, hfs_fs_i.h, hfs_fs_sb.h, file_dbl.c,
+ file_nat.c, file_hdr.c, file.c, file_cap.c, Makefile, dir.c:
+ Generate metadata (header files and CAP finderinfo files) on-the-fly.
+ The files file_{dbl,nat}.c are merged into file_hdr.c as a result.
+
+Sat Sep 7 08:09:24 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * README.sgml:
+ Fix silly grammatical error.
+
+Fri Sep 6 09:17:12 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * hfs_fs_sb.h, super.c:
+ No need to cast type of s_reserved.
+
+ * file_dbl.c, file_nat.c, dir_dbl.c, dir_nat.c, file_cap.c, dir_cap.c:
+ Add the missing NULL readpage and writepage entries to the inode_ops.
+
+ * file_dbl.c, file_nat.c, file.c, file_cap.c:
+ Cleanup error checking for read() and write().
+
+Thu Sep 5 05:29:53 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * version.c, README.sgml:
+ Bump version up to "0.7.2".
+ User-visible changes from 0.7.0:
+ + Corrected CAP finderinfo file format.
+ + Support for more features of CAP finderinfo files.
+ + No longer requires gcc 2.7.0 or newer.
+ + Now implements mknod() system call.
+
+ * hfs_fs.h, dir_nat.c, file_cap.c, file_nat.c, README.sgml, dir_cap.c:
+ Include the CAP and Netatalk copyright notices.
+
+ * hfs_fs.h, file_cap.c:
+ Repair and improve CAP support.
+
+ * catalog.c:
+ Oops! The BkDat for new files and directories was in 1972 when
+ it should have been in 1904 (not that it matters that much).
+
+ * inode.c:
+ The HFS MdDat should be the larger of the i_mtime and i_ctime.
+
+ * README.sgml:
+ Change 'm_time' to 'i_mtime'.
+
+Wed Sep 4 13:27:35 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * version.c, README.sgml:
+ Bump version up to "0.7.1".
+ User-visible changes from 0.7.0:
+ + Minor bug in CAP finderinfo file format fixed.
+ + No longer requires gcc 2.7.0 or newer.
+ + Now implements mknod() system call.
+
+ * README.sgml:
+ Removed note about needing gcc 2.7.0 or newer.
+
+ * file.c:
+ Optimize hfs_do_read() based on the fact that HFS has no holes in files.
+ Minor code formatting changes.
+
+ * hfs.h, hfs_sysdep.h, mdb.c, extent.c, file.c, btree.c, catalog.c,
+ balloc.c, bnode.c:
+ Reorganize memory management routines.
+ hfs_malloc() and hfs_free() are the main routines.
+ The macros FREE() and MALLOC() are gone.
+ HFS_NEW() and HFS_DELETE() are new 'shorthand' macros.
+
+ * btree.c:
+ Fix broken debugging code.
+
+ * super.c, hfs.h, mdb.c, part_tbl.c, Makefile:
+ Separate partition table handling into its own file.
+
+ * dir.c:
+ Spelling fixes.
+
+ * sysdep.c:
+ Oops! Error check got sense reversed while editing.
+
+ * mdb.c, sysdep.c, hfs.h, hfs_btree.h, hfs_sysdep.h, btree.c, extent.c,
+ bfind.c, bnode.c, balloc.c:
+ Make hfs_buffer a pointer to a buffer_head, rather than a buffer_head.
+
+ * hfs_fs.h, dir_cap.c, dir_dbl.c, dir_nat.c, dir.c:
+ Add a mknod() entry to the inode_operations for normal directories.
+ All it is good for is letting root create regular files.
+
+ * file_dbl.c, file_nat.c, file.c, file_cap.c, dir_cap.c, dir_dbl.c,
+ dir_nat.c:
+ Add the missing NULL entries to the end of the file_operations.
+
+ * super.c, hfs_btree.h, hfs_fs.h, mdb.c, extent.c, hfs.h, catalog.c:
+ Make the remainder of the (untested) changes
+ to allow compilation with gcc 2.6.3.
+
+ * hfs_fs.h:
+ Fix hfs_fs.h to work with gcc 2.6.3.
+
+ * hfs_fs.h:
+ (struct hfs_cap_info) should never have been 'packed'.
+
+ * BUG_INFO:
+ Use -V for getting version of module utilities.
+
+ * super.c, sysdep.c, trans.c, hfs_fs_sb.h, inode.c, hfs_fs.h,
+ hfs_fs_i.h, file_cap.c, file_dbl.c, file_nat.c, dir_dbl.c,
+ dir_nat.c, file.c, dir.c, dir_cap.c:
+ Fix up hfs_fs{,_i,_sb}.h in preparation for inclusion in kernel.
+
+Tue Sep 3 23:58:03 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * hfs.h:
+ Change eventual destination to linux/fs/hfs rather than include/linux.
+
+ * super.c, inode.c, mdb.c, hfs_btree.h, hfs_fs.h, hfs_sysdep.h,
+ file_dbl.c, file_nat.c, hfs.h, dir_nat.c, extent.c, dir_dbl.c,
+ catalog.c, dir_cap.c, brec.c, btree.c, binsert.c, bnode.c, bdelete.c,
+ bfind.c, bins_del.c, balloc.c:
+ Replace all the swap{16,32}() stuff w/ ntohl() and friends.
+
+Fri Aug 30 09:51:23 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * version.c, README.sgml:
+ Rewrite installation instructions and bump version up to "0.7.0".
+
+ * Makefile:
+ Remove the INCDIR variable; we now rely on the
+ user to have the correct links in /usr/include.
+
+Mon Aug 26 12:25:41 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * version.c, README.sgml:
+ Reformat the documentation and bump version up to "pre-0.7-9".
+ Hopefully this will become version 0.7 in a few days.
+
+Thu Aug 22 08:00:44 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * README.sgml, version.c:
+ Bump version up to "pre-0.7-8".
+
+ * file_nat.c, file_dbl.c:
+ AppleDouble headers had resource fork size in wrong byte order.
+
+Wed Aug 21 05:22:28 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * version.c, README.sgml:
+ Bump version up to "pre-0.7-7".
+
+ * bnode.c:
+ Fixed a long-standing bug in hfs_bnode_lock().
+ This bug occasionally caused lock-up under heavy load.
+
+Tue Aug 20 09:15:10 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * README.sgml, version.c:
+ Bump version up to "pre-0.7-6".
+
+ * catalog.c:
+ Fix a deadlock problem in catalog readers/writers locking.
+
+ * bins_del.c:
+ hfs_bnode_update_key() was still corrupting the header node sometimes.
+
+ * catalog.c, dir.c:
+ Fix problem with extending the catalog B-tree hanging hfs_cat_commit().
+ Fix a race that could delete a non-empty directory.
+
+Sun Aug 18 23:16:43 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * version.c, README.sgml:
+ Bump version to "pre-0.7-5" for test release.
+
+ * dir_cap.c, README.sgml:
+ Change ".:rootinfo:" to ".rootinfo".
+
+ * hfs_fs.h, dir_cap.c, dir_dbl.c, dir_nat.c:
+ Mangle the names as first step in hfs_{cap,dbl,nat}_lookup().
+ Use the new hfs_streq() to catch mixed case matches to the special
+ files and directories in hfs_{cap,dbl,nat}_lookup().
+ Store reserved names only once.
+
+ * dir.c, hfs.h, string.c:
+ Implement hfs_streq() which tests for string equality more
+ rapidly than hfs_strcmp() by checking for equal length first,
+ and use it when checking for reserved names.
+
+ * inode.c, TODO, dir_cap.c, dir_dbl.c, README.sgml:
+ Provide the metadata for the root directory for the CAP and AppleDouble
+ schemes in the files ".:rootinfo:" and "%RootInfo", respectively.
+
+ * TODO, super.c:
+ Add (untested) support for the old Mac Plus style of partition map.
+
+ * bdelete.c, TODO:
+ Note the possibility of bdelete() to hanging on a corrupted B-tree.
+
+ * TODO:
+ Add items corresponding to some of the 'XXX' comments in the sources.
+
+ * dir_dbl.c, dir_cap.c:
+ Update comments, removing ref. to a comment that once existed in inode.c
+
+ * catalog.c:
+ Remove some redundant locking and error checks
+ that had been previously marked as questionable.
+
+Sat Aug 17 08:06:56 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * binsert.c, bfind.c, bins_del.c, balloc.c, bdelete.c:
+ Edited some comments for correctness.
+
+ * README.sgml, version.c:
+ Bump version up to "pre-0.7-4" in preparation for snapshot release.
+
+ * Makefile:
+ Have 'make dep' delete the *.o and *.s files.
+
+ * catalog.c, hfs.h, TODO, bfind.c:
+ Move looping from hfs_cat_next() into hfs_bsucc(),
+ where it can be done an entire node at a time.
+
+Fri Aug 16 05:02:59 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * TODO:
+ Add AppleShare support to the list of goals.
+
+ * trans.c, super.c, hfs_fs.h, README.sgml:
+ Add a "names=netatalk" mount option, since
+ Netatalk quotes initial periods and CAP doesn't.
+
+ * Makefile:
+ Oops! Had removed the 'include .depend' from Makefile.
+
+ * inode.c, hfs_fs.h, file_nat.c, file_dbl.c, file.c, dir_nat.c,
+ dir_dbl.c, dir_cap.c, dir.c, README.sgml:
+ Update for 2.0.1 and newer kernels.
+
+ * Makefile:
+ Get rid of ifeq stuff and use a .tmpdepend file to make sure
+ a failed 'make depend' doesn't allow a 'make hfs.o'.
+
+Wed Aug 14 01:03:01 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * version.c, README.sgml:
+ Bump version up to "pre-0.7-3" in preparation for snapshot release.
+
+ * btree.c, extent.c, bnode.c:
+ Fix up some debugging code.
+
+Tue Aug 13 12:42:12 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * version.c, README.sgml:
+ Bump revision to "pre-0.7-2".
+
+ * super.c, sysdep.c, mdb.c, file_nat.c, inode.c, file_cap.c,
+ file_dbl.c, file.c, extent.c, dir.c, catalog.c, btree.c, bnode.c,
+ balloc.c:
+ Added the remaining missing function comments.
+
+ * Makefile, README.sgml:
+ Simplify the default make rule to build the dependency file AND hfs.o.
+ Change the installation instructions to reflect the change.
+
+ * hfs.h:
+ Added missing structure comments.
+
+ * bdelete.c:
+ Merge bdelete_brec() back into hfs_bdelete().
+ Add missing function comments.
+
+
+ * extent.c:
+ Insignificant code change removing an unneeded indirection.
+
+ * btree.c, hfs_btree.h, balloc.c, bnode.c:
+ Add a 'sys_mdb' field to (struct hfs_btree).
+
+ * extent.c, hfs_sysdep.h, sysdep.c, bnode.c, balloc.c, bfind.c,
+ Makefile:
+ Move hfs_buffer_read() from hfs_sysdep.h to sysdep.c so it can use
+ the symbol HFS_SECTOR_SIZE rather than the manifest constant 512.
+ Have hfs_buffer_read() print an error message,
+ and remove redundant errors from the callers.
+
+ * hfs_sysdep.h, mdb.c, super.c, file.c, hfs.h, hfs_btree.h, catalog.c,
+ extent.c, btree.c, balloc.c, bfind.c, bnode.c:
+ Get rid of the type hfs_device and the fields of that type,
+ using the type hfs_sysmdb and the 'sys_mdb' field in its place.
+
+ * Makefile:
+ Fix definition of HDRS variable.
+
+ * README.sgml, version.c:
+ Bump version up to "pre-0.7-1".
+
+ * Makefile:
+ Separate sources and headers into three groups:
+ B-tree code, HFS code and Linux code.
+
+ * bitmap.c, bitops.c, hfs.h, hfs_sysdep.h, balloc.c:
+ Implemented portable set of bit operations in hfs_sysdep.h
+
+ * mdb.c, hfs_sysdep.h, hfs_btree.h, extent.c, btree.c, bitmap.c,
+ bnode.c, balloc.c:
+ Implement a portable set of buffer operations in hfs_sysdep.h
+
+ * TODO:
+ Remove note about separating header files into two parts.
+
+ * catalog.c:
+ Remove call to hfs_mdb_dirty(), since the hfs_brec_relse() does it.
+
+ * hfs.h, extent.c, file.c:
+ Move hfs_getblk() from extent.c to file.c, since that is now the
+ only file that actually uses it.
+
+ * balloc.c:
+ Replace use of hfs_getblk() in balloc.c with a local function
+ (get_new_node()) that doesn't retry, since B-trees can't shrink.
+
+ * hfs.h, hfs_btree.h, hfs_sysdep.h, mdb.c, extent.c:
+ Make hfs_buffer a typedef.
+
+ * inode.c, hfs.h, hfs_sysdep.h, dir.c:
+ Change hfs_sysentry to a typedef.
+ Rename 'sysentry' field of (struct hfs_cat_entry) to 'sys_entry'.
+
+ * super.c, mdb.c, catalog.c:
+ Rename hfs_cat_sync() to hfs_cat_commit() and call it
+ from hfs_mdb_commit() rather than from hfs_write_super().
+
+ * catalog.c, file.c:
+ Minimize the calls to hfs_mdb_dirty(). Now called when:
+ 1) A buffer holding a volume bitmap block is dirtied.
+ 2) A dirty B-tree node is written back to the buffers.
+ 3) A dirty catalog entry is written back to the buffers.
+
+ * hfs_sysdep.h, hfs.h:
+ Make hfs_sysmdb a typedef.
+
+Sun Aug 11 08:46:10 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * hfs_sysdep.h, extent.c, hfs.h:
+ Replace hfs_mdb_{lock,unlock} with more portable
+ scheme using a wait queue in the MDB.
+
+ * hfs.h, hfs_btree.h, hfs_sysdep.h, bnode.c, catalog.c, binsert.c:
+ Make hfs_wait_queue a typedef'd pointer to a (struct wait_queue).
+ Rename hfs_wait_on() to hfs_sleep_on().
+
+ * catalog.c, hfs_sysdep.h, super.c, bfind.c, bnode.c, balloc.c:
+ Implemented hfs_dev_name() in hfs_sysdep.h
+ as a portable call to produce a device name.
+
+ * super.c, hfs.h, mdb.c:
+ Rename hfs_mdb_read() to hfs_mdb_get(), and don't take a
+ 'sys_mdb' argument. That's the callers responsibility.
+
+ * sysdep.c, Makefile:
+ Remove the pointless file sysdep.c
+
+ * README.sgml:
+ Clean up the "System Requirements" section.
+
+Sat Aug 10 22:41:24 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * sysdep.h, sysdep.c, super.c, hfs_sysdep.h, mdb.c, string.c,
+ hfs_fs.h, hfs_fs_i.h, hfs_fs_sb.h, hfs_btree_private.h, hfs_btree.h,
+ file_cap.c, file_dbl.c, file_nat.c, hfs.h, file.c, dir_nat.c,
+ extent.c, dir.c, dir_cap.c, dir_dbl.c, catalog.c, bnode.c, brec.c,
+ btree.c, binsert.c, bitmap.c, bitops.c, bfind.c, bins_del.c,
+ Makefile, balloc.c, bdelete.c:
+ Includes the hfs.h that was missing from the previous check in.
+ MAJOR include-file cleanup:
+ hfs_btree.h merged into hfs.h
+ hfs_btree_private.h renamed hfs_btree.h
+ sysdep.h renamed hfs_sysdep.h
+ Fixed some minor portability fixes shown up by the header split.
+
+ * README.sgml:
+ Add instructions for a dealing with a missing linux/version.h
+
+ * hfs_fs.h, mdb.c, string.c, catalog.c, extent.c, btree.c, bitmap.c,
+ bitops.c, bnode.c, brec.c, bins_del.c, binsert.c, bdelete.c, bfind.c,
+ balloc.c:
+ Major split of hfs_fs.h into Linux-specific
+ part (hfs_fs.h) and HFS-specific part (hfs.h).
+
+ * file.c, extent.c:
+ Move hfs_getblk() from file.c to extent.c
+
+ * sysdep.h, super.c, mdb.c, hfs_fs_sb.h, hfs_fs.h, file.c, extent.c,
+ catalog.c, bnode.c, bitmap.c:
+ Make the field 's_mdb' in (struct hfs_sb_info) a pointer to
+ the MDB, rather than the actual MDB. This allowed the definition
+ of (struct hfs_mdb) to be moved from hfs_fs_sb.h to hfs_fs.h.
+
+ * ccache.c, hfs_fs.h, Makefile, catalog.c:
+ Merged ccache.c and catalog.c into the latter.
+ Moved definition of (struct hfs_cat_rec) into catalog.c
+
+ * extent.c:
+ Oops! Last set of changes didn't compile but they're OK now.
+
+ * hfs_btree.h, hfs_fs.h, mdb.c, ccache.c, extent.c, btree.c:
+ Move the definition of (struct hfs_raw_extent) inside
+ extent.c and treat it as simple array of U16's elsewhere.
+
+ * hfs_fs.h, dir_dbl.c, dir_nat.c, ccache.c, catalog.c, dir_cap.c:
+ Make hfs_cat_next() return the CNID and cdrType of the entry.
+ Now catalog.c and ccache.c are the only files which
+ depend on the structure of a catalog record on disk.
+
+ * dir.c, hfs_fs.h, catalog.c:
+ Replace hfs_cat_new_{file,dir}() with hfs_cat_{create,mkdir}()
+ which are wrappers for what used to be hfs_cat_create().
+
+ * hfs_fs.h, mdb.c, super.c, Makefile:
+ Split super.c into super.c (Linux stuff) and mdb.c (MDB stuff).
+
+ * super.c, hfs_fs_sb.h:
+ Add the MDB field 'drAtrb' to (struct hfs_mdb) as the field 'attrib'.
+
+ * hfs_fs_sb.h, super.c:
+ Split hfs_read_super() into hfs_read_super() and hfs_mdb_read().
+
+ * super.c, hfs_fs_sb.h:
+ Remove the unneeded 'hs' field from (struct hfs_mdb).
+
+ * TODO:
+ Remove item about hfs_notify_change() needing to update metadata.
+
+ * inode.c, hfs_fs.h, hfs_fs_sb.h, file_cap.c, file_dbl.c, file_nat.c,
+ file.c, dir.c:
+ Add a flags argument to hfs_{cap,dbl,nat}_buildmeta() so that
+ it only builds the parts that are currently out-of-date.
+ Call hfs_{cap,dbl,nat}_buildmeta() through hfs_update_meta()
+ in hfs_notify_change() and hfs_rename() to update the metadata.
+
+ * dir.c:
+ Make test for normal dir in update_dirs_{plus,minus}() more explicit.
+
+ * inode.c, file_cap.c, file_dbl.c, file_nat.c, dir_dbl.c, dir_nat.c,
+ file.c, README.sgml, dir_cap.c:
+ Resolve the "meta-data" vs. "metadata" rivalry in favor of the latter.
+
+ * btree.c:
+ Simplify some debugging code.
+
+ * hfs_btree_private.h, bnode.c, btree.c, balloc.c:
+ Put the in-core copy of the header node IN the
+ B-tree structure rather than just a pointer to it.
+
+ * hfs_btree_private.h, btree.c, bnode.c:
+ Have hfs_btree_commit() call hfs_bnode_commit()
+ to commit the header and root nodes.
+
+ * hfs_fs.h, super.c, hfs_btree_private.h, btree.c, hfs_btree.h,
+ balloc.c:
+ Change hfs_commit_mdb() to hfs_mdb_commit().
+ Make hfs_mdb_commit() call hfs_btree_commit().
+ Move code to update B-tree size and extent
+ from hfs_btree_extend() to hfs_btree_commit().
+ Make hfs_btree_extend() call hfs_mdb_commit().
+
+ * super.c:
+ Change hfs_commit_super() to hfs_commit_mdb().
+
+ * btree.c, bnode.c, bfind.c:
+ Fixed up broken debugging code and error messages.
+
+ * super.c, hfs_btree_private.h, btree.c, hfs_btree.h, bdelete.c,
+ binsert.c, balloc.c:
+ Now use write-back caching of B-tree header fields.
+
+ * hfs_fs.h:
+ Get rid of the add{16,32}() inlines as they are no longer used.
+
+ * hfs_btree_private.h, binsert.c, btree.c, bdelete.c, bfind.c, balloc.c:
+ All the needed fields of the B-tree header are
+ now cached for reading, but not yet writing.
+
+ * TODO:
+ Remove "Implement write count" from TODO list.
+
+ * file.c, super.c, bnode.c:
+ Implement write count.
+
+ * catalog.c:
+ Fix directory entry counting in hfs_cat_move().
+
+ * balloc.c:
+ Simplify hfs_btree_extend(), since the allocation
+ request will get rounded up to the clumpsize.
+
+ * extent.c:
+ Honor clumpsize when allocating blocks to files.
+
+ * file_cap.c, file_dbl.c, file_nat.c, super.c, dir.c, file.c,
+ ccache.c, catalog.c, balloc.c:
+ Mark 44 functions in need of commenting.
+
+ * hfs_fs_sb.h, super.c, extent.c, hfs_fs.h, ccache.c, btree.c, balloc.c:
+ Record clumpsize in allocation blocks rather than 512-byte blocks.
+
+ * sysdep.h, super.c, TODO, balloc.c, hfs_fs_sb.h:
+ Now updates the backup MDB when a B-tree grows.
+
+ * extent.c:
+ hfs_extent_free() had test against NULL backward.
+ The result is that access to a file with extents in the extents
+ B-tree would result in an infinite loop in hfs_cat_put().
+
+ * hfs_fs_sb.h, super.c, hfs_fs.h:
+ Reorganize partition map code to get size of partition
+ in preparation for dealing with the alternate MDB.
+
+Fri Aug 9 03:25:13 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * Makefile:
+ Add make rules for README.{ps,info}
+
+ * README, README.sgml, DOC, FAQ, Makefile, .cvsignore,
+ Merge CHANGES into ChangeLog.
+ Merge DOC, FAQ and README into README.sgml.
+ Add make rules for building README.{txt,dvi}
+
+ * BUG_INFO, Makefile:
+ Added a BUG_INFO script which attempts to collect some useful
+ information which I'd like to see in every bug report I receive.
+
+ * Makefile, version.c:
+ Added version.c which contains a version string.
+
+Thu Aug 8 21:48:24 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * trans.c:
+ Fix Latin-1 -> Macintosh filename mapping to change colons to pipes.
+
+ * trans.c:
+ Fixed Mac->Latin-1 translation to behave as documented for the
+ extended 8-bit characters without corresponding Latin-1 characters.
+
+ * inode.c, super.c, file.c, hfs_fs_i.h, hfs_fs_sb.h, DOC:
+ Added a conv={binary,text,auto} mount option similar to that of the
+ msdos, hpfs and iso9660 filesystems, but applying only to data forks.
+ As compared to those filesystems, HFS has the advantage that only a
+ single CR need be converted to a NL, rather than a CR/NL sequence, so
+ it is quite safe to seek in the file.
+ Additionally the 'Type' field is far more reliable indicator of text
+ files than a file extension.
+
+ * super.c:
+ Simplified parsing of mount options.
+
+ * super.c:
+ Oops! The part=<n> mount option was being parsed in octal!
+
+ * TODO:
+ Remove "case=lower" from the list of goals.
+
+ * super.c, hfs_fs.h, hfs_fs_sb.h, string.c, dir_dbl.c, dir_nat.c,
+ dir_cap.c, DOC:
+ Resurrect the case={asis,lower} mount option.
+
+ * dir.c:
+ Simpler test for "normal" directory in update_dirs_{plus,minus}().
+
+ * hfs_fs_sb.h, super.c, dir.c, hfs_fs.h, catalog.c, DOC:
+ Add mount options to specify what Type and Creator will be used for
+ new files and change the default from NULLs to "????".
+
+Wed Aug 7 11:32:22 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * catalog.c:
+ In hfs_cat_next() use entry->cnid rather than the key of the initial
+ brec to identify the end of the directory.
+
+ * README:
+ Update for pre-0.7 version.
+
+ * hfs_fs.h:
+ Create versioned module if CONFIG_MODVERSIONS is set in linux/config.h
+
+ * TODO:
+ Note need for special steps for unaligned accesses on some machines.
+
+ * FAQ:
+ Added Q0: What is HFS?
+ Added Q7: Does hfs_fs work w/ 400k and 800k diskettes?
+ Brought Q6 (about writability) up to date.
+ Made a few other answers more verbose.
+
+Tue Aug 6 00:58:46 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * Makefile:
+ Changed 'snapshot' rule to include cvs tag command.
+
+ * hfs_fs.h, dir_cap.c, dir_dbl.c, dir_nat.c, catalog.c, ccache.c:
+ Implemented readers half of dir locking scheme so readdir() should
+ produce consistent results and count_dir_entries() is not race prone.
+
+ * catalog.c:
+ hfs_cat_move() was calling hfs_cat_decache() after changing
+ the key rather than before, corrupting the hash lists.
+
+Mon Aug 5 14:03:46 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * hfs_fs.h, catalog.c:
+ Implemented the writers half of a locking scheme for directories.
+
+ * inode.c:
+ Fixed a serious bug in hfs_notify_change() that would allow a chmod()
+ on directory meta-data and would cause the directory inode (if it was
+ in memory at the time) to change into a file inode.
+
+ * inode.c:
+ Fixed a problem with write permissions on directory meta-data.
+
+ * dir_dbl.c, dir_nat.c, dir_cap.c:
+ hfs_{cap,dbl,nat}_readdir() now return the correct value in the 'd_ino'
+ field of the dirent for all cases, something I think has always been
+ done incorrectly until now.
+
+ * dir_nat.c, inode.c, dir_cap.c:
+ In hfs_{cap,nat}_lookup() take advantage of the
+ 'file_type' field of (struct hfs_inode_info).
+
+ * TODO:
+ Removed two accomplished goals (rename() and improved readdir()).
+
+ * inode.c, dir_dbl.c, dir_nat.c, hfs_fs_i.h, dir.c, dir_cap.c:
+ Rewrite hfs_{cap,dbl,nat}_readdir() to take advantage of hfs_cat_next().
+ They now use a uniform 'i_size' for all inodes for a given directory.
+ This simplifies update_dirs_{plus,minus}() and eliminates the need for
+ the 'file_size' and 'dir_link' fields of (struct hfs_inode_info).
+ For the CAP and Netatalk schemes the meta-data directories are now the
+ last entries rather than coming just after '.' and '..'. This is in
+ preparation for the day when we can write to the files in those
+ directories, and ensures that when using 'tar' to copy HFS filesystems
+ the file or directory will be created before the meta-data is written.
+ Otherwise we could be stuck writing meta-data and not knowing if it is
+ for a file or a directory!
+
+ * ccache.c:
+ Updated count_dir_entries() for new hfs_cat_next().
+
+ * hfs_fs.h, catalog.c:
+ hfs_cat{nth,next}() no longer take a 'types' argument,
+ so they now return all entries.
+ hfs_cat_next() now uses the ParID of the key to detect
+ the end of the directory.
+ hfs_cat_nth() now accepts n=0 as a valid input, requesting the thread.
+
+ * trans.c, string.c, super.c, dir_nat.c, hfs_fs.h, dir.c, dir_cap.c,
+ dir_dbl.c, catalog.c:
+ Rename (struct hfs_cname) to the more appropriate (struct hfs_pstr).
+
+ * hfs_fs.h, hfs_btree.h:
+ Move some constants from hfs_fs.h to hfs_btree.h
+
+ * bdelete.c, hfs_btree.h:
+ Remove hfs_bdelete_brec() from public B-tree interface.
+
+ * hfs_btree_private.h, hfs_fs.h, btree.c, hfs_btree.h, bnode.c, brec.c,
+ bfind.c, bins_del.c, binsert.c, balloc.c, bdelete.c, Makefile:
+ Split B-tree stuff into public and private parts:
+ brec.c split into bfind.c and brec.c
+ hfs_btree.h split into hfs_btree.h and hfs_btree_private.c
+
+ * inode.c:
+ The tests and sets of the HFS_FIL_LOCK bit where all reversed!
+
+ * hfs_fs.h, ccache.c:
+ Redo some ccache stuff, removing the 'error' field from
+ (struct hfs_cat_entry) and ensuring that hfs_cat_put()
+ will not sleep on an uninitialized entry.
+
+Sun Aug 4 23:43:28 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * sysdep.h:
+ Change swap{16,32}() back to macros since hton[ls]() are functions.
+
+ * hfs_fs.h, ccache.c:
+ Use only lowest order byte of parent CNID in hashing a catalog key.
+
+ * bdelete.c:
+ The "improved" bdelete() was TOO paranoid looking for missing parents.
+
+ * ccache.c:
+ Get rid of pointless swap16const(0).
+
+ * hfs_fs.h, inode.c, extent.c, ccache.c, dir_cap.c, dir_nat.c,
+ binsert.c, catalog.c:
+ Store cnid and associated constants in big-endian byte order.
+ This reduces the number of byte-order swaps required.
+
+ * sysdep.h:
+ Make swap32() and swap16() inline functions.
+
+ * dir_nat.c, dir_cap.c, dir_dbl.c:
+ Added hfs_rename() to the inode_operations for normal directories.
+
+ * dir.c, hfs_fs.h:
+ Added hfs_rename() and cleaned up hfs_{create,mkdir,unlink,rmdir}().
+
+ * catalog.c:
+ Added the missing check for moving a directory into itself.
+
+ * catalog.c, ccache.c, hfs_fs.h:
+ Implement a nearly ideal hfs_cat_move().
+ It still needs to prevent moving a directory into itself.
+ The functions hfs_cat_{create,delete,move}() still need work with
+ respect to their atomicity (especially vs. readdir).
+
+ * bdelete.c:
+ Fixed a serious bug in hfs_bdelete_brec() that would yield a corrupted
+ b-tree when the first record in a bnode was deleted.
+ Made bdelete() more aggressive when checking for missing parents.
+
+Sat Aug 3 06:11:50 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * btree.c, super.c:
+ Fixed a problem that caused a kernel oops when no HFS filesystem
+ is found.
+
+Wed Jul 24 13:06:12 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * catalog.c:
+ Remove race in hfs_cat_create() that could overflow directory valence.
+
+ * catalog.c:
+ Fix hfs_cat_create() so the parent directory doesn't get deleted
+ out from under it. Otherwise we could have created files and
+ directories in deleted directories.
+
+ * hfs_fs.h, dir_cap.c, dir_dbl.c, dir_nat.c, catalog.c, ccache.c:
+ Redo hfs_cat_{next,nth}() in terms of which entry types to
+ allow, rather than which to skip.
+
+ * catalog.c:
+ The function hfs_cat_create() would fail to hfs_cat_put(entry) if
+ the 'record' argument was invalid or if the 'result' argument was NULL.
+
+ * dir.c:
+ The functions hfs_{create,mkdir,unlink,rmdir} all failed to
+ call iput() when their arguments conflicted with a reserved name.
+
+ * catalog.c, hfs_fs_sb.h:
+ Start over on rename(). Still unfinished.
+ Fix silly bug in hfs_cat_create() that made it always fail.
+
+ * ccache.c:
+ Fix byte-order bug in write_entry().
+
+Tue Jul 23 12:12:58 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * dir_dbl.c, dir_nat.c, hfs_fs.h, dir.c, dir_cap.c:
+ Remove the macros KEY() and PARENT() since the key is now easy
+ to access through the catalog entry.
+ Replace the macros NAME{IN,OUT}() with inline functions
+ hfs_name{in,out}() to gain type checking of arguments.
+
+ * catalog.c:
+ Remove the macro TYPE().
+
+ * inode.c, file_dbl.c, file_nat.c, file.c, file_cap.c:
+ Remove the #define's of the unused macro KEY().
+
+ * hfs_fs.h, dir_cap.c, dir_dbl.c, dir_nat.c, catalog.c, dir.c:
+ Replace hfs_lookup_parent() in dir.c with hfs_cat_parent() in catalog.c.
+ This new function performs locking to protect against rename() changing
+ the parent during I/O.
+ It is also intended for use with files as well as directories.
+ Change hfs_{cap,dbl,nat}_lookup() to use the new function.
+
+ * dir.c, hfs_fs.h, catalog.c:
+ Remerge hfs_cat_{create,mkdir}() into hfs_cat_create() and resurrect
+ hfs_cat_new_{file,dir}().
+ Fix hfs_cat_{create,delete} to use the improved catalog cache for
+ locking in place of directory-level create/delete locks.
+ Fix hfs_{create,mkdir}() to use the new hfs_cat_create().
+
+ * hfs_fs.h, ccache.c:
+ Rewrite parts to remove need for specialized create/delete locking.
+ Use new case-independent hash function.
+ Fix bug in hfs_cat_get() that would read an entry w/o locking it.
+ Call hfs_relinquish() before retrying a deleted entry in hfs_cat_get.
+ If there is a read error, then don't retry in hfs_cat_get().
+ Remove unused 'version' field from (struct hfs_cat_entry).
+
+ * sysdep.h:
+ Add hfs_relinquish(), a system-independent alias for schedule().
+
+ * hfs_fs.h, string.c:
+ Add hfs_strhash(), a simplistic case-independent hash function.
+
+ * hfs_fs.h, inode.c:
+ Make hfs_iget() an inline function.
+
+ * TODO:
+ Add a few goals and removed those that have been achieved.
+
+ * Makefile:
+ Add ccache.c to list of source files.
+ Add rule for *.s files and include them in the 'clean' rule.
+
+Wed Jul 17 17:22:45 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * sysdep.h, trans.c, string.c, super.c, hfs_fs_i.h, hfs_fs_sb.h,
+ inode.c, hfs_btree.h, hfs_fs.h, file_dbl.c, file_nat.c, extent.c,
+ file.c, file_cap.c, dir_dbl.c, dir_nat.c, ccache.c, dir.c,
+ dir_cap.c, btree.c, catalog.c, bnode.c, brec.c, balloc.c:
+ Total rewrite of the inode-handling stuff to be centered around
+ a catalog entry cache (ccache.c). This results not only in a far
+ more sensible way of doing things, but also removed many race
+ conditions. (The source and object code both got smaller too!)
+ Many small "undocumented features" were also fixed.
+ Replace HFS_CNAME with (struct hfs_cname).
+ rename() has been temporarily abandoned.
+
+Thu Jul 11 01:14:38 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * dir.c:
+ As written hfs_lookup_parent() had two overlapping read requests
+ in the catalog tree. This could have led to deadlock.
+
+Wed Jul 10 09:27:00 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * catalog.c, hfs_fs.h, bdelete.c:
+ More work on getting rename() fleshed out. Still not done.
+ Before I can finish it looks like I'll need to build a
+ mechanism for exclusive access to the catalog tree. There
+ just doesn't seem to be any other way to get proper POSIX
+ semantics without a bunch of race conditions elsewhere.
+
+ * hfs_fs.h, inode.c, dir_cap.c, dir_dbl.c, dir_nat.c, catalog.c:
+ More work on the still incomplete rename() code.
+ Merge hfs_cat_add_{dir,file}() into hfs_cat_create().
+ Add file-thread support to hfs_cat_{create,delete,rename}.
+
+Tue Jul 9 09:43:15 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * inode.c, dir_dbl.c, dir_nat.c, extent.c, dir_cap.c:
+ The indirect (struct hfs_file) was causing blocks not to be freed
+ when files where deleted, and an omission in hfs_put_inode() was
+ preventing the inode from getting freed. Both are now fixed.
+
+ * hfs_fs.h, dir_dbl.c, dir_nat.c, hfs_btree.h, catalog.c, dir_cap.c,
+ bdelete.c:
+ Made unlink() and rmdir() more race resistant and did some more
+ work on the still incomplete code for rename().
+
+ * btree.c, bnode.c:
+ There was a serious race condition in the bnode cache, so
+ hfs_bnode_find() is now modeled after Linus's inode cache.
+
+Mon Jul 8 10:33:38 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * hfs_fs_i.h, inode.c, file_cap.c, file_dbl.c, file_nat.c, dir_dbl.c,
+ dir_nat.c, file.c, dir.c, dir_cap.c:
+ More changes to layout of (struct hfs_inode_info).
+
+ * super.c, inode_cap.c, inode_dbl.c, inode_nat.c, inode.c, hfs_fs_i.h,
+ hfs_fs_sb.h, file_nat.c, hfs_fs.h, file.c, file_cap.c, file_dbl.c,
+ Makefile, catalog.c:
+ Implemented new layout for (struct hfs_inode_info) resulting in the
+ elimination of lots of duplicated code for hfs_*_write_inode(),
+ hfs_*_put_inode() and *_open() functions.
+ Merged inode_*.c files back into inode.c.
+ Not fully tested.
+
+ * TODO:
+ Add a few more of my goals to the list.
+
+ * README:
+ Documentation updates.
+
+ * inode_nat.c, inode_cap.c, inode_dbl.c, inode.c, hfs_fs.h, hfs_fs_i.h,
+ file.c, file_cap.c, file_dbl.c, file_nat.c, catalog.c:
+ (struct hfs_file) and metadata are read when file is opened or
+ truncated and are released by iput().
+
+Sun Jul 7 23:55:43 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * inode_nat.c, inode_cap.c, inode_dbl.c, inode.c, dir_nat.c, hfs_fs.h,
+ hfs_fs_i.h, dir_cap.c, dir_dbl.c, catalog.c, dir.c:
+ (struct hfs_dir) is now inside (struct hfs_inode_info) once again.
+
+ * inode_nat.c, super.c, inode_cap.c, inode_dbl.c, inode.c, file_nat.c,
+ hfs_btree.h, hfs_fs.h, extent.c, file_cap.c, file_dbl.c, dir_nat.c,
+ dir_cap.c, dir_dbl.c, btree.c, catalog.c, dir.c, bpath.c, brec.c,
+ bins_del.c, binsert.c, bnode.c, bfind.c, balloc.c, bdelete.c,
+ Makefile:
+ Remerged (struct hfs_bpath) and (struct hfs_brec), merging the
+ files bfind.c and bpath.c as a resurrected brec.c.
+
+Sat Jul 6 21:47:05 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * inode_cap.c, inode_dbl.c, inode_nat.c, inode.c, hfs_fs.h, hfs_fs_i.h,
+ file_cap.c, file_dbl.c, file_nat.c, hfs_btree.h, dir_nat.c, extent.c,
+ dir.c, dir_cap.c, dir_dbl.c, btree.c, catalog.c, bfind.c, bpath.c,
+ binsert.c, bdelete.c:
+ Renamed (struct hfs_brec_key) to (struct hfs_bkey).
+
+Tue May 28 07:53:24 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * inode_cap.c, catalog.c:
+ Spelling fixes.
+
+ * inode_nat.c, super.c, inode_cap.c, inode_dbl.c, inode.c, hfs_fs.h,
+ hfs_fs_i.h, hfs_fs_sb.h, file.c, file_dbl.c, file_nat.c, dir_dbl.c,
+ dir_nat.c, extent.c, dir.c, dir_cap.c, catalog.c:
+ Structures got too big, so I had to add a layer of indirection
+ to (struct hfs_inode_info).
+ This means we must clear_inode() in inode_put().
+
+Mon May 27 01:32:42 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * catalog.c, file_cap.c:
+ Some sizeof() stuff now uses variable not type.
+
+ * hfs_fs.h:
+ Make HFS_I() and HFS_SB() inline to gain type checking.
+
+Sun May 26 13:34:17 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * dir_nat.c:
+ Oops. Had left some debugging printk()s in place.
+
+ * file_dbl.c, file_nat.c, file_cap.c:
+ Cleaned up variable names for consistency.
+
+ * hfs_fs_sb.h:
+ Add a couple 'const's to function typedefs.
+
+ * hfs_fs.h:
+ Add and update function prototypes.
+ Cleaned up type names.
+ Fix debugging malloc code.
+ Add hfs_iget_by_name() as an inline function.
+
+ * sysdep.h:
+ Remove extra semicolon from macro definitions.
+
+ * super.c:
+ Use new hfs_iget_by_name() to get root inode.
+
+ * extent.c:
+ Cleaned up some variable naming for consistency.
+
+ * catalog.c:
+ Added (untested) code for hfs_cat_move_file().
+
+ * catalog.c:
+ Fix one missed call to hfs_cat_build_key().
+ Make hfs_cat_add_{file,dir}() take a cat_entry as an argument.
+ Add hfs_cat_new_{file,dir}() to generate new cat_entry's.
+
+ * dir_dbl.c, dir_nat.c, dir.c, dir_cap.c:
+ Cleaned up type and variable names.
+ Updated calls to hfs_cat_build_key() and NAMEOUT()
+ Use new hfs_iget_by_*() calls.
+
+ * inode_cap.c, inode_dbl.c, inode_nat.c:
+ Cleaned up type and variable names.
+
+ * inode.c:
+ Update calls to hfs_cat_build_key().
+ Cleaned up type and variable names.
+ Implemented a hierarchy of hfs_iget_by*() calls.
+
+ * catalog.c:
+ Change hfs_cat_build_key() to take a HFS_CNAME as input.
+
+ * btree.c:
+ Initialize lsize and psize fields of file.
+
+ * trans.c:
+ Now passes type HFS_CNAME and has name/len in "normal" order.
+
+Tue May 21 07:02:34 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * bnode.c:
+ Attempt to read invalid bnode would have led to an infinite loop under
+ certain circumstances. One way to cause this was with an invalid
+ partition table which points beyond the end of the device.
+
+Sat May 11 12:38:42 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * sysdep.h, sysdep.c, inode_dbl.c, inode_nat.c, super.c, inode_cap.c,
+ inode.c, hfs_fs.h, hfs_fs_i.h, hfs_fs_sb.h, file_dbl.c, file_nat.c,
+ hfs_btree.h, extent.c, file.c, file_cap.c, dir_nat.c, dir.c,
+ dir_cap.c, dir_dbl.c, btree.c, catalog.c, bitmap.c, bitops.c,
+ bnode.c, bfind.c, bins_del.c, binsert.c, balloc.c, bdelete.c:
+ Another big wave of portability-oriented changes.
+
+Tue May 7 11:28:35 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * super.c, sysdep.c, sysdep.h, inode_cap.c, inode_dbl.c, inode_nat.c,
+ hfs_fs_i.h, inode.c, file_nat.c, hfs_btree.h, hfs_fs.h, file.c,
+ file_cap.c, file_dbl.c, dir_nat.c, extent.c, dir_cap.c, dir_dbl.c,
+ btree.c, catalog.c, dir.c, bnode.c, bpath.c, binsert.c, bitmap.c,
+ bitops.c, bdelete.c, bfind.c, bins_del.c, Makefile, balloc.c:
+ Start a big move to abstract all the Linux-specific stuff
+ out of the lower levels. Created sysdep.[ch] to hold it.
+
+ * FAQ, TODO:
+ Bring some documentation up-to-date.
+
+Fri May 3 20:15:29 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * super.c, inode_dbl.c, inode_nat.c, inode.c, inode_cap.c, extent.c,
+ hfs_fs.h, hfs_fs_i.h, dir_dbl.c, dir_nat.c, catalog.c, dir.c,
+ dir_cap.c, bpath.c, btree.c, binsert.c, bnode.c:
+ "FID reform": 'fid' became 'cnid' (Catalog Node ID), and is now
+ a field in (struct hfs_file). The new name is more consistent
+ with Apple's documentation. The presence of 'cnid' in (struct
+ hfs_file) help move more of the code toward OS-independence.
+
+ * inode_nat.c, super.c, trans.c, inode.c, inode_cap.c, inode_dbl.c,
+ hfs_fs.h, file_cap.c, file_dbl.c, file_nat.c, dir_nat.c, extent.c,
+ file.c, dir.c, dir_cap.c, dir_dbl.c, btree.c, catalog.c, bnode.c,
+ bpath.c, bins_del.c, binsert.c, bitmap.c, bitops.c, bdelete.c,
+ bfind.c, balloc.c:
+ A lot of changes in what headers are included and in what order.
+
+Sat Apr 27 12:28:54 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * FAQ:
+ Updated for current writability status.
+
+ * .cvsignore:
+ Added ChangeLog.
+
+ * file_dbl.c, file_nat.c, file_cap.c, file.c, dir_dbl.c, dir_nat.c,
+ dir_cap.c:
+ Added the default fsync() to all file_operations structures.
+
+ * dir_nat.c, hfs_fs.h, dir.c, dir_cap.c, dir_dbl.c:
+ Add rmdir() for normal directories.
+
+ * binsert.c:
+ I had messed up insertion so that is would sometime fail to
+ split the root, but its OK now.
+
+ * dir.c:
+ hfs_do_unlink() decremented directory counts rather than file counts.
+
+Wed Apr 24 13:20:08 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * hfs_fs.h, bnode.c, hfs_btree.h:
+ Fixed a couple more type size assumptions.
+
+ * hfs_fs.h, balloc.c, bitmap.c, bitops.c:
+ "Portable" bitmap handling was wrong for just about everything but
+ the i386 and the "inverse big-endian" bit ordering that I thought
+ the m68k port was using. It seems the m68k port is now using standard
+ big-endian bit-numbering conventions.
+ This code is now correct for the standard big- and little-endian bit
+ orderings. (which should cover all Linux systems?)
+ Also no longer assumes sizeof(long) == 4, though that might still be
+ a problem in other parts of the code.
+
+Tue Apr 23 19:19:27 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * FAQ:
+ Bring uptodate for this snapshot.
+
+ * Makefile:
+ Add FAQ to $(MISC)
+
+ * README, TODO:
+ Documentation updates.
+
+ * bdelete.c:
+ Spelling fixes.
+
+ * dir_cap.c:
+ In unlink() don't force metadata into memory if not present.
+
+ * bdelete.c:
+ Some function comments and some clean up.
+
+ * bins_del.c:
+ Added missing function comment for hfs_bnode_update_key().
+
+ * binsert.c, bitmap.c:
+ Spelling and grammar corrections to comments.
+
+ * hfs_btree.h, hfs_fs.h, bins_del.c, binsert.c, Makefile, bdelete.c:
+ Clean up of hfs_bdelete(), splitting bins_del.c into three files:
+ bins_del.c, binsert.c and bdelete.c
+
+ * bpath.c, bins_del.c:
+ hfs_bdelete() is now working "correctly", but needs some cleaning up.
+
+Mon Apr 22 05:35:41 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * hfs_fs.h, bpath.c, hfs_btree.h, bins_del.c, bnode.c, balloc.c,
+ bfind.c:
+ Rewrite bnode handling, heading toward a more write-behind approach.
+ Have done away with HFS_LOCK_BLIND.
+
+ * inode_dbl.c, inode_nat.c, extent.c, hfs_fs_i.h, inode_cap.c:
+ Was trying to truncate resource fork of directories!
+
+Sun Apr 21 08:15:43 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * balloc.c:
+ Updated to use truncate() to grow full trees.
+
+ * extent.c, hfs_fs.h, file.c, inode.c:
+ Added truncate() for normal files.
+
+ * bins_del.c:
+ hfs_bdelete() fixes for handling removal of root.
+
+ * inode_cap.c, inode_dbl.c, inode_nat.c:
+ Release storage for deleted files in hfs_*_put_inode().
+
+ * bitmap.c:
+ Make len=0 valid for hfs_{set,clear}_vbm_bits().
+
+ * super.c, inode.c, hfs_fs_i.h, hfs_fs_sb.h, btree.c, balloc.c:
+ Changed from clumpsize to clumpblks.
+
+ * inode_nat.c, hfs_fs.h, inode_cap.c, inode_dbl.c, btree.c, extent.c,
+ balloc.c:
+ Some extent-related changes in preparation for truncate() support.
+
+Sat Apr 20 10:59:13 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * inode_nat.c, hfs_fs_i.h, inode.c, inode_cap.c, inode_dbl.c,
+ dir_nat.c, hfs_fs.h, dir.c, dir_cap.c, dir_dbl.c:
+ Removed dir.valence from hfs inode.
+ Added unlink(), but still need truncate() and some more support
+ in hfs_*_put_inode() to free the disk space used by deleted files.
+
+ * bnode.c:
+ Check for NULL bnode in hfs_bnode_relse().
+
+ * bins_del.c:
+ Fixed a byte-order problem in bdelete_nonempty().
+
+ * hfs_fs.h, bnode.c, bpath.c, hfs_btree.h, balloc.c, bins_del.c:
+ First attempt at hfs_bdelete().
+
+ * dir.c:
+ The Finder would display strange things if it couldn't set frView.
+ Therefore initialize frView field for new directories.
+
+ * file_cap.c, file_dbl.c, file_nat.c, hfs_fs.h:
+ Define User/Finder info fields of catalog entry in more detail.
+
+ * hfs_fs.h:
+ HFS_BFIND_DELETE should require exact match.
+
+ * dir.c:
+ Set "record in use" bit of filFlags for new files.
+
+ * inode.c:
+ Was doing the wrong thing with i_ctime.
+
+ * dir_nat.c, dir_cap.c, dir_dbl.c:
+ Added some missing updates to the inode in hfs_*_{create,mkdir}().
+
+Sun Apr 14 00:10:52 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * hfs_fs.h, file_dbl.c, file_nat.c, file.c:
+ Work around the ever-changing type of f_reada.
+
+Sat Apr 13 00:43:41 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * bpath.c, bfind.c:
+ Spelling corrections in comments.
+
+ * bins_del.c:
+ ifdef out shift_left() until it is actually used.
+
+ * hfs_btree.h, hfs_fs.h, bins_del.c, bpath.c, bfind.c:
+ Cleaned up code related to 'flags' argument to hfs_bpath_find().
+
+Fri Apr 12 23:30:01 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * bpath.c:
+ Updated comments.
+ Rewrote hfs_bpath_init() and hfs_bpath_next().
+
+ * hfs_btree.h:
+ Updated prototype for hfs_bpath_init().
+
+ * bins_del.c:
+ Updated call to hfs_bpath_init().
+
+ * inode.c, inode_cap.c, inode_dbl.c, inode_nat.c, extent.c, file_cap.c,
+ file_dbl.c, file_nat.c, dir_cap.c, dir_dbl.c, dir_nat.c, catalog.c,
+ dir.c:
+ Renamed hfs_brec_relse() to hfs_brelse().
+
+ * hfs_fs.h, hfs_btree.h:
+ Updated prototypes to reflect new names in bpath.c
+
+ * bins_del.c:
+ Updated calls to functions in bpath.c
+ Updated comments.
+
+ * Makefile:
+ Renamed brec.c to bpath.c
+
+ * bfind.c:
+ Updated calls to functions in bpath.c
+ Added hfs_brelse() which was previously hfs_brec_relse() in brec.c
+
+ * bpath.c:
+ brec.c renamed to bpath.c
+ Functions renamed to reflect their current actions.
+ Comments are still out of date.
+ hfs_brec_relse() renamed to hfs_brelse() and moved to bfind.c
+
+ * brec.c:
+ brec.c renamed to bpath.c
+
+Wed Apr 10 07:20:28 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * hfs_fs.h, extent.c, hfs_btree.h, brec.c, dir.c, bfind.c,
+ bins_del.c:
+ Backed-out changes to hfs_binsert() that added the ability to
+ return the new record, since it will probably not ever be needed.
+
+ * extent.c:
+ Since 1.3.45 truncate() has locked the file, so there is no need
+ for all the things I've been doing to hfs_file_extend() & new_extent().
+ Those two functions have been cleaned up a bit (similar to older forms).
+
+ * extent.c:
+ hfs_file_extend() now more "robust", but new_extent() is still
+ not fully "concurrency safe."
+
+Tue Apr 9 09:01:18 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * bins_del.c:
+ Made split() inline.
+
+ * inode.c, dir_nat.c, hfs_fs.h, dir_cap.c:
+ Added hfs_itry() to get in-core inodes.
+
+ * inode_dbl.c, inode_nat.c, hfs_fs.h, inode.c, inode_cap.c, file_dbl.c,
+ file_nat.c, hfs_btree.h, extent.c, file_cap.c, dir_cap.c, dir_dbl.c,
+ dir_nat.c, brec.c, catalog.c, dir.c, bins_del.c, bnode.c,
+ bfind.c:
+ Rewrite of all the (struct hfs_brec) stuff.
+
+Mon Apr 8 21:50:01 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * btree.c, extent.c, bnode.c:
+ Fixed format strings in a few debugging printk()'s.
+
+ * brec.c, hfs_fs.h:
+ Removed hfs_brec_relse_one().
+
+ * hfs_fs.h, bnode.c, brec.c, hfs_btree.h, bfind.c, bins_del.c, balloc.c:
+ (struct hfs_bnode_ref)s are now returned by value rather than reference
+ and they are in (struct hfs_brec) rather than pointed to. Cuts down on
+ a lot of kmalloc() and kfree() traffic.
+
+ * hfs_fs.h, dir.c, extent.c, bins_del.c:
+ Modified hfs_binsert() to be able to return the new record.
+
+ * bins_del.c, hfs_btree.h:
+ Added shift_left(), still untested.
+
+ * bins_del.c:
+ new_root() was missing its comment.
+
+ * super.c, trans.c, hfs_fs_i.h, inode.c, inode_dbl.c, inode_nat.c,
+ file_nat.c, hfs_btree.h, hfs_fs.h, file.c, file_dbl.c, dir_dbl.c,
+ dir_nat.c, extent.c, dir.c, dir_cap.c, bitops.c, bnode.c, brec.c,
+ bfind.c, bins_del.c, bitmap.c, balloc.c:
+ Fixed lines over 80 characters and tabified files.
+
+ * bins_del.c:
+ Fixed line(s) over 80 columns.
+
+ * trans.c, inode_nat.c, string.c, super.c, inode.c, inode_cap.c,
+ inode_dbl.c, hfs_fs_i.h, hfs_fs_sb.h, hfs_btree.h, hfs_fs.h, file.c,
+ file_cap.c, file_dbl.c, file_nat.c, dir_dbl.c, extent.c, btree.c,
+ dir_cap.c, bitops.c, bnode.c, brec.c, bfind.c, bins_del.c, bitmap.c,
+ DOC, README, TODO, balloc.c, CHANGES:
+ About 150 spelling corrections.
+
+Sun Apr 7 23:14:28 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * dir_cap.c, dir_dbl.c, dir_nat.c, dir.c:
+ Cleaned-up check for special names in mkdir().
+
+ * extent.c:
+ More verbose error message.
+
+ * inode_dbl.c, inode_nat.c, hfs_fs_i.h, inode.c, inode_cap.c, dir.c,
+ hfs_fs.h:
+ Limit directories to 32767 entries, since Mac uses 16-bit integer.
+
+Fri Apr 5 07:27:57 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * FAQ:
+ Initial version.
+
+ * dir_dbl.c, dir_nat.c, bins_del.c, dir.c, dir_cap.c:
+ Added missing function comments.
+
+Wed Apr 3 06:38:36 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * brec.c:
+ Cleaned-up code for brec->flags.
+
+ * extent.c:
+ Added function comments.
+
+ * bins_del.c:
+ Added function comments.
+ hfs_binsert() was incrementing record count even on failure.
+
+Mon Apr 1 08:35:51 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * extent.c:
+ Rewrote find_ext() and new_extent() for new hfs_btree_extend().
+ Moved hfs_btree_extend() to balloc.c
+ Fixed potential kernel OOPS in new_extent().
+
+ * brec.c:
+ Fixed potential kernel OOPS in hfs_brec_get_root().
+ Removed hfs_brec_find_first().
+ Fixed return value of hfs_brec_find().
+
+ * bins_del.c:
+ Updated call to hfs_btree_extend().
+
+ * balloc.c:
+ Merged hfs_bnode_add() and hfs_btree_extend() into the later.
+ Commented init_mapnode().
+
+ * bfind.c:
+ Removed hfs_bfind_first().
+
+ * hfs_fs.h, hfs_btree.h:
+ Updated prototypes.
+
+Sat Mar 30 22:56:47 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * CHANGES, README, TODO:
+ Updated documentation in preparation for 0.6 release.
+
+ * inode.c, hfs_fs.h:
+ Got rid of HFS_FAKE_EXEC in favor of noexec mount option.
+
+ * inode.c, super.c, DOC, hfs_fs_sb.h:
+ Added "quiet" mount option, like the fat filesystem.
+
+ * inode.c, dir_cap.c, dir_nat.c:
+ Pseudo-directories are read-only (at least for now).
+
+ * hfs_fs.h, dir_dbl.c, dir_nat.c, dir.c, dir_cap.c:
+ mkdir() updated to check against reserved names, but the
+ AppleDouble scheme still has problems with names starting with '%'.
+
+ * dir_dbl.c, dir_nat.c, hfs_fs.h, dir.c, dir_cap.c:
+ Added mkdir(). (It only took 2 tries to get it right!!)
+ Only works in "normal" directories and doesn't yet stop
+ one from creating dirs with the reserved names.
+
+ * brec.c, extent.c, bins_del.c:
+ Now have a way to get an EEXIST back from hfs_binsert().
+
+ * btree.c, inode.c, hfs_fs_i.h, file.c, bfind.c, bnode.c, balloc.c:
+ Added 'dev' field to struct hfs_file.
+
+ * hfs_fs_i.h, inode.c, btree.c, extent.c, file.c, bnode.c, brec.c,
+ balloc.c:
+ Removed duplicated fields from struct hfs_file since
+ even B*-trees now have that information in the inode.
+
+ * extent.c:
+ zero_blocks() neglected allocation block size in computing start.
+
+Fri Mar 29 16:04:37 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * super.c:
+ hfs_statfs(): f_files and f_ffree fields are now -1, which is
+ documented as the value for "undefined" fields in struct statfs.
+
+ * trans.c, inode_nat.c, string.c, super.c, inode_dbl.c, inode_cap.c,
+ inode.c, file_nat.c, file_dbl.c, file_cap.c, file.c, dir_dbl.c,
+ extent.c, dir_cap.c, catalog.c, btree.c, brec.c, bnode.c, bitops.c,
+ bitmap.c, bins_del.c, balloc.c:
+ Stylistic editing: {} for all 'for', 'while' and 'if' blocks.
+ I hope I didn't screw-up anything.
+
+ * hfs_fs.h, dir.c, dir_cap.c, dir_dbl.c, dir_nat.c:
+ Added creation of normal files to all three fork schemes!
+ Strange things may happen when trying to create "non-normal" files.
+
+ * brec.c:
+ Cleaned up some debugging code.
+
+ * hfs_fs_i.h:
+ File and directory counts could have overflown 16-bit integer.
+
+ * hfs_btree.h:
+ Added HFS_BREC_RIGHT to help fix insertion problem.
+
+ * extent.c:
+ Various fixes to hfs_{file,btree}_extend().
+
+ * catalog.c:
+ Made hfs_build_cat_key() more "correct".
+
+ * btree.c:
+ Added and fixed debugging code.
+
+ * brec.c:
+ Fixed overflow detection.
+ Added some debugging code.
+
+ * bnode.c:
+ Dirtied some buffers in places that might have been missed.
+ Fixed some debugging code that had broken.
+
+ * bitops.c:
+ hfs_count_free_bits() was running off end of bitmap.
+
+ * bins_del.c:
+ Fixed various bugs, mostly related to variable-length keys.
+
+ * balloc.c:
+ Had forgotten to set a bit in new mapnodes.
+ Node counts were overflowing 16-bit integers.
+
+ * bitmap.c:
+ Oops! clear/set did opposite operation on full words.
+
+Wed Mar 27 10:59:07 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * hfs_fs_i.h:
+ Updated struct hfs_extent for concurrent access.
+ Also caused a slight modification to struct hfs_file.
+
+ * hfs_fs.h, hfs_btree.h:
+ Added/updated prototypes.
+
+ * balloc.c:
+ hfs_bnode_alloc() finished but still untested.
+
+ * bins_del.c:
+ Fixed up deadlock avoidance in hfs_binsert() again.
+ Perhaps I even got it right this time.
+
+ * extent.c:
+ hfs_file_extend() now safe under concurrent operations?
+
+ * file.c:
+ hfs_getblk() now safe under concurrent operations?
+
+Tue Mar 26 23:26:35 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * btree.c:
+ Added call to hfs_extent_trim() to fix memory leak.
+
+ * extent.c:
+ Oops, had left a "#define static" in from debugging.
+
+ * bins_del.c:
+ hfs_binsert() rewritten to avoid deadlock when extending
+ the extents B*-tree.
+
+ * btree.c:
+ Moved hfs_btree_extend() to extent.c
+
+ * inode_nat.c, inode_cap.c, inode_dbl.c:
+ hfs_*_put_inode() rewritten to call hfs_extent_trim().
+
+ * extent.c:
+ Big rewrite for new struct hfs_extent:
+ Now keep linked list of extents.
+ Cache is now a pointer to a list element.
+ Now have 'end' field to aid decode_extent().
+ New functions:
+ hfs_extent_trim(): frees linked list.
+ hfs_btree_extend(): for extending B*-trees.
+ Improved debugging output.
+
+ * balloc.c:
+ Added hfs_bnode_add() (incomplete and uncommented).
+
+ * btree.c:
+ Moved some work from hfs_btree_extend() to hfs_bnode_add().
+
+ * bfind.c:
+ Added hfs_bfind_first() as wrapper for hfs_brec_find_first().
+
+ * brec.c:
+ Added hfs_brec_find_first() to search first leaf node.
+
+ * bins_del.c:
+ Added error returns to hfs_binsert() and binsert().
+
+ * bins_del.c:
+ Check to see that we really need ancestors before starting.
+ Check that hfs_btree_alloc() gave us enough nodes.
+ binsert() uses info precomputed by hfs_binsert().
+
+Mon Mar 25 11:33:53 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * bnode.c:
+ Collected together the error returns in hfs_bnode_lock().
+
+ * Makefile:
+ Added ChangeLog to $(MISC).
+
+Wed Mar 20 19:41:45 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * super.c, hfs_fs.h, file.c, dir_dbl.c, dir_nat.c, dir.c, dir_cap.c:
+ Removed support for kernels older than about 1.3.70
+ Most of that support had been broken recently anyway.
+
+ * super.c:
+ Fixed so DEBUG_MEM works w/o DEBUG_ALL.
+ Updated call to hfs_btree_init().
+
+ * hfs_fs.h:
+ Updated/added prototypes.
+
+ * hfs_btree.h:
+ HFS_BFIND_CHAIN removed.
+ struct hfs_brec gets new 'flags' field with bits:
+ HFS_BREC_{FIRST,OVERFLOW,UNDERFLOW,UNINITIALIZED}
+ Removed bitmap size constants.
+ Changes to struct hfs_btree:
+ 'file' and 'cache' now structs rather than pointers.
+ Added 'reserved' field (used during insertion).
+ Added pointers to size and extent in MDB.
+
+ * file.c:
+ Made hfs_getblk() public.
+ Removed (fil->inode == NULL) special cases.
+
+ * extent.c:
+ {find,update}_ext() are no longer inline.
+ new_extent() fails when called for the extents tree;
+ previously it would hanging calling hfs_binsert().
+ extend_file():
+ renamed to hfs_file_extend() and made public.
+ fixed to work for B*-trees.
+ zeros-out blocks as they are allocated.
+ fixed bugs for (allocation block) != (physical block).
+
+ * btree.c:
+ hfs_btree_{init,free}() modified for changes to struct:
+ 'file' and 'cache' moved back into structure
+ file.inode initialized to reduce special cases
+ hfs_btree_init() gets pointer to size in MDB instead of size.
+ Added hfs_btree_extend() (incomplete and uncommented).
+
+ * bnode.c:
+ hfs_bnode_{alloc,free}() moved to separate file.
+ Removed 'const' from some function arguments
+ due to change in struct hfs_btree.
+ hfs_bnode_lock(): added WRITE/RESRV->READ transition.
+
+ * brec.c:
+ hfs_brec_get_{root,child}() now take a 'keep_mask' argument
+ indicating when to keep ancestor nodes, and store
+ information about why ancestors were kept.
+ HFS_BFIND_CHAIN eliminated in favor of HFS_BFIND_{INSERT,DELETE}
+ which are now implemented using 'keep_mask'.
+ Added hfs_brec_relse_one() that doesn't release ancestors.
+
+ * bins_del.c:
+ Lots of rewrites to cleanup insertion.
+ Now tries to extend tree before insertion starts.
+ binsert() iterative rather than recursive.
+ No point in keeping track as it is still not "stable".
+
+ * balloc.c:
+ New file: started with hfs_bnode_{free,alloc}()
+ Added hfs_bnode_init() to initialize a newly allocated bnode.
+ hfs_bnode_free():
+ Renamed hfs_bnode_bitop().
+ Can set or clear a specified bit.
+ Gets bitmap sizes from nodes directly.
+ hfs_bnode_alloc():
+ Returns actual node, calling hfs_bnode_init().
+ Gets bitmap sizes from nodes directly.
+
+ * bfind.c:
+ Removed obsolete comment from hfs_bsucc()
+ Removed 'const' from tree arg of hfs_bfind()
+ due to changes in struct hfs_btree.
+
+ * Makefile:
+ Added new file: balloc.c
+
+Sat Mar 9 22:03:53 1996 Paul H. Hargrove <hargrove@sccm.stanford.edu>
+
+ * Start of detailed CVS logging.
+
+Mar 09, 1996: snapshot-09Mar96 hargrove@sccm.stanford.edu (Paul H. Hargrove)
+ NOT AN OFFICIAL RELEASE
+ Fixed up debugging code that was broken by split of btree.c
+ Added debugging kmalloc/kfree
+ Fixed memory leak in hfs_bnode_relse()
+
+Mar 08, 1996: snapshot-08Mar96 hargrove@sccm.stanford.edu (Paul H. Hargrove)
+ NOT AN OFFICIAL RELEASE
+ now reset blocksize on device when done.
+ hfs_binsert done (except for the full tree case).
+ btree.c split up into manageable pieces (need to sort out hfs_btree.h)
+
+Feb 26, 1996: snapshot-26Feb96 hargrove@sccm.stanford.edu (Paul H. Hargrove)
+ NOT AN OFFICIAL RELEASE
+ Some writability.
+ Bug with multiple opens of meta data fixed.
+ Netatalk support no longer considered experimental.
+
+Virtually everything has changed, so I've lost track here.
+
+Nov 16, 1995: snapshot-16Nov95 hargrove@sccm.stanford.edu (Paul H. Hargrove)
+ NOT AN OFFICIAL RELEASE
+ Still more comments.
+ btree.c back to 80 columns. will do same to other files soon.
+ Starting with btree.c have begun to put file contents into some
+ sort of standard order.
+ Moved metadata reading to VFS open() routine and now free it in
+ the VFS release() routine. Much cleaner than the old way.
+ Unified hfs_iget by shifting scheme-dependent code into a function
+ pointer in the superblock. This could/should be shifted to
+ a VFS read_inode() routine if that can be done cleanly.
+ Probably lots of other changes; I've lost track.
+
+Nov 05, 1995: version 0.5.3 hargrove@sccm.stanford.edu (Paul H. Hargrove)
+ NOT AN OFFICIAL RELEASE
+ 1.2.x compatibility removed
+ Added lots of comments to btree.c and cleanup some code. The result
+ is that the source file doubled in size while the object
+ file dropped in size by 20%.
+ Added some comments to super.c and dir.c as well.
+ Cleaned up some stuff in dir.c adding some additional error checking
+ and moving closer to using a unified hfs_iget by migrating
+ common code into lookup_parent().
+ Changed btree.c to use a separate bnode cache per filesystem.
+ Renamed a bunch of the bnode functions in btree.c
+
+Jun 29, 1995: version 0.5.2 hargrove@sccm.stanford.edu (Paul H. Hargrove)
+ BUG FIX and 1.3.x-compatibility release.
+ Will compile under 1.2.x or 1.3.x by changing one line in Makefile.
+ Started adding magic numbers to structures for "safety".
+ Don't strip internal symbols when linking or loading, as this made
+ good bug reports rather difficult.
+ Fixed a bug that could cause the fs to lock-up after trying to open
+ a non-existent file.
+ Fixed a bug that allowed files to appear truncated, when in fact it
+ is still not possible to truncate a file.
+ Added more/better comments to header files.
+ Deal with volume and b-tree bitmaps in preparation for writing.
+ Fixed readdir() to deal properly with the case where the directory
+ changes while writing to user-space. (which can't yet
+ actually happen, until directories are writable).
+
+Jun 23, 1995: version 0.5.1 hargrove@sccm.stanford.edu (Paul H. Hargrove)
+ BUG FIX RELEASE
+ Removed two debugging messages that didn't belong.
+ Fixed a typo that prevented modified inodes from being written to disk.
+ Added a missing line which prevented rmmod'ing sometimes.
+ Added a missing line which caused errors when modifying .finderinfo or
+ .resource under the CAP system.
+ Added a notify_change() to keep mode bits sensible, and to cause
+ changes to an inode to affect the data fork and resource fork
+ of a file together.
+
+Jun 22, 1995: version 0.5 hargrove@sccm.stanford.edu (Paul H. Hargrove)
+ Fixed a bug that was giving wrong values for i_blocks
+ Partly writable (can only 'touch' existing files, so far)
+ Removed case= mount option. It will be back eventually.
+ Can now deal with CDROMs (and hard disks?), many thanks to
+ Holger Schemel for this work.
+ Latin-1 filename conversion also due to Holger Schemel.
+ Rewritten btree operations.
+
+Feb 28, 1995: version 0.4 hargrove@sccm.stanford.edu (Paul H. Hargrove)
+ Requires Linux >= 1.1.94: depends on changes made to asm/byteorder.h
+ Now using string comparison code donated by ARDI (see string.c)
+ Code reorganized to use data structures more like ARDI's.
+ More code reorganization to abstract the btree operations.
+ Added the fork= mount option.
+ Added AppleDouble support. Executor, from ARDI, can now run programs
+ from HFS filesystems mounted with the HFS module.
+
+Jan 28, 1995: version 0.3 hargrove@sccm.stanford.edu (Paul H. Hargrove)
+ Major code reorganization.
+ Known for certain to work ONLY on floppies.
+ Started caching extents, so got faster on long file reads.
+ Now compiles separate from kernel tree.
+ Supports 5 filename conversion methods.
+ Supports forks, using the method from CAP.
+ All external symbols now start with HFS_ or hfs_
+
+Jan 12, 1995: version 0.2 hargrove@sccm.stanford.edu (Paul H. Hargrove)
+ Should now work on all HFS volumes, but still only tested on floppies.
+ Got smaller and faster with some code reorganization.
+ Since Linus moved htons() and friends to an asm file, should now be
+ truly endian-independent, but still only tested on Intel machines.
+ Requires Linux >= 1.1.77, since Linus moved htons().
+
+Jan 05, 1995: version 0.1 hargrove@sccm.stanford.edu (Paul H. Hargrove)
+ First release.
+ 1.44Mb floppies only
+ no resource forks
+ trivial name mangling only
+ read only
+ for Linux >= 1.1.75
diff --git a/fs/hfs/FAQ.txt b/fs/hfs/FAQ.txt
new file mode 100644
index 00000000000000..de76e8d2380785
--- /dev/null
+++ b/fs/hfs/FAQ.txt
@@ -0,0 +1,342 @@
+ Frequently Asked Questions about the HFS filesystem for
+ Linux
+ Paul H. Hargrove, hargrove@sccm.Stanford.EDU
+ version 1.0.3, 27 Apr 1997
+
+ This document provides answers to some of the most frequently asked
+ questions about the HFS filesystem for Linux. It is currently pretty
+ rough and totally unorganized. Corrections, additions and clarifica-
+ tions are appreciated. The most current version of this document is
+ kept on The HFS for Linux Page <http://www-sccm.Stanford.EDU/~har-
+ grove/HFS/>.
+ ______________________________________________________________________
+
+ Table of Contents:
+
+ 1. What is this FAQ about?
+
+ 2. What is HFS?
+
+ 3. How I mount AppleShare volumes?
+
+ 4. What is the current version of the HFS filesystem.
+
+ 5. How stable is the current version?
+
+ 6. Is there a mailing list for discussion of the HFS filesystem?
+
+ 7. What version of Linux do I need to be running?
+
+ 8. Will it run on my (your processor type here)?
+
+ 9. Will it run under (your non-Linux operating system here)?
+
+ 10. Why can I mount some HFS CDROMs but not others?
+
+ 11. What does ``only 1024-char blocks implemented (512)'' mean?
+
+ 12. Why do I get a message about a bad or unknown partition table?
+
+ 13. Can I mount multiple HFS partitions from the same Macintosh
+ disk?
+
+ 14. In what ways can I write to HFS filesystems?
+
+ 15. Does the HFS filesystem work with 400 kB or 800 kB Macintosh
+ diskettes?
+
+ 16. How can I format an HFS filesystem?
+
+ 17. How can I fsck an HFS filesystem?
+
+ 18. Why do I get ``error -50'' messages from my Mac when using
+ netatalk?
+
+ 19. Why does my Macintosh show generic application and document
+ icons?
+
+ 20. How owns all the copyrights and trademarks? ;-)
+
+ 20.1. This Document
+
+ 20.2. The Software
+
+ 20.3. Trademarks
+ ______________________________________________________________________
+
+ 11.. WWhhaatt iiss tthhiiss FFAAQQ aabboouutt??
+
+ This FAQ is about the HFS filesystem for Linux, which is available in
+ two forms. The stand-alone version (called hfs_fs) is a Linux kernel
+ loadable module implementing the Macintosh HFS filesystem. The HFS
+ filesystem is also included in some distributions of the Linux kernel
+ source (in the directory linux/fs/hfs). This version can be compiled
+ as a loadable module or compiled into the kernel.
+
+ Either version allows a machine running Linux to read and write disks
+ from a Macintosh (almost) as though they were native Linux disks.
+
+ 22.. WWhhaatt iiss HHFFSS??
+
+ HFS stands for ``Hierarchical File System'' and is the filesystem used
+ by the Mac Plus and all later Macintosh models. Earlier Macintosh
+ models used MFS (``Macintosh File System''), which is not supported.
+
+ 33.. HHooww II mmoouunntt AApppplleeSShhaarree vvoolluummeess??
+
+ The HFS filesystem is for mounting local filesystems only. There is
+ an experimental afpfs by Ben Hekster heksterb@acm.org available from
+ http://www.odyssey.co.il/~heksterb/Software/afpfs/.
+
+ 44.. WWhhaatt iiss tthhee ccuurrrreenntt vveerrssiioonn ooff tthhee HHFFSS ffiilleessyysstteemm..
+
+ As of version 1.0.3 of this FAQ, version 0.95 is the most recent. You
+ can always find the most recent version on The HFS for Linux Page
+ <http://www-sccm.Stanford.EDU/~hargrove/HFS/>. Announcements of new
+ versions are made to the comp.os.linux.announce newsgroup.
+
+ 55.. HHooww ssttaabbllee iiss tthhee ccuurrrreenntt vveerrssiioonn??
+
+ Version 0.95 is considered to be ``beta'' software, so I recommend
+ making backups of anything important before you start playing. It is
+ relatively free of bugs due to lots of testing of the previous
+ releases.
+
+ After a suitable period without new bugs the I will consider the
+ software to be ``stable'' and the version number will jump to 1.0.
+
+ 66.. IIss tthheerree aa mmaaiilliinngg lliisstt ffoorr ddiissccuussssiioonn ooff tthhee HHFFSS ffiilleessyysstteemm??
+
+ There is no mailing list devoted exclusively to the HFS filesystem.
+ However, announcements of new versions are posted to the ``linux-
+ atalk'' and ``hfs-interest'' lists. I will see bug reports sent to
+ those lists but e-mail is more reliable (hargrove@sccm.Stanford.EDU).
+
+ To subscribe to hfs-interest send e-mail with a body of ``subscribe
+ hfs-interest (your e-mail address)'' to majordomo@ccs.neu.edu.
+
+ To subscribe to linux-atalk send e-mail with a body of ``SUBSCRIBE
+ LINUX-ATALK (Your full name)'' to listserv@netspace.org.
+
+ 77.. WWhhaatt vveerrssiioonn ooff LLiinnuuxx ddoo II nneeeedd ttoo bbee rruunnnniinngg??
+
+ To compile and use the stand-alone distribution of the HFS filesystem
+ you will need Linux kernel version 2.0.1 or newer compiled with
+ modules enabled (CONFIG_MODULES). To compile you will need the kernel
+ headers which match the kernel you are running. This is covered in
+ more detail in the installation instructions in INSTALL.txt.
+
+ If your kernel came with HFS in the kernel source tree then HFS should
+ work with your Linux version. There may be small problems with a few
+ of the development kernel releases. For these releases check the HFS
+ for Linux Page <http://www-sccm.Stanford.EDU/~hargrove/HFS/> for
+ patches.
+
+ 88.. WWiillll iitt rruunn oonn mmyy ((yyoouurr pprroocceessssoorr ttyyppee hheerree))??
+
+ The code is carefully written to be independent of your processor's
+ word size and byte-order, so if your machine runs Linux it can run the
+ HFS filesystem. However some younger ports don't yet have support for
+ loadable modules.
+
+ Note that HFS is tested most extensively on Intel platforms. So there
+ could be subtle compilation problems on other platforms. If you
+ encounter any that are not addressed by the documentation then please
+ let me know.
+
+ 99.. WWiillll iitt rruunn uunnddeerr ((yyoouurr nnoonn--LLiinnuuxx ooppeerraattiinngg ssyysstteemm hheerree))??
+
+ No. There is a port in progress to NetBSD. I know of no other active
+ porting attempts. If you are interested in porting the HFS filesystem
+ to another Unix-like operating system, I am interested in providing
+ what guidance I can.
+
+ 1100.. WWhhyy ccaann II mmoouunntt ssoommee HHFFSS CCDDRROOMMss bbuutt nnoott ootthheerrss??
+
+ In the past there was a known incompatibility with some ``hybrid''
+ CDROMs that appear as HFS disks on Macs and as ISO9660 disks on other
+ systems. I think I have fixed the problem. So, if you encounter this
+ particular problem or have problems with specific non-hybrid CDROMs
+ please e-mail me with the title and manufacturer of the CD.
+
+ 1111.. WWhhaatt ddooeess ````oonnllyy 11002244--cchhaarr bblloocckkss iimmpplleemmeenntteedd ((551122))'''' mmeeaann??
+
+ This message comes from the kernel and indicates that an attempt was
+ made to read a 512-byte block from a device that doesn't support
+ 512-byte blocks. The HFS filesystem only works with 512-byte blocks,
+ and therefore doesn't function with these devices. Eventually it may
+ be able to use 1024-byte (or even 2048-byte) blocks when necessary.
+ Ideally the device driver should be enhanced to support 512-byte
+ blocks so that the various filesystems which need 512-byte blocks
+ don't each need to work around it.
+
+ 1122.. WWhhyy ddoo II ggeett aa mmeessssaaggee aabboouutt aa bbaadd oorr uunnkknnoowwnn ppaarrttiittiioonn ttaabbllee??
+
+ If your Linux kernel doesn't understand Macintosh partition tables it
+ gives this warning when it can't find a partition table it recognizes.
+ To support partitioned media with such kernels, decoding of Mac
+ partition tables is done by the HFS filesystem so you should still be
+ able to mount the disk. However, to do so you will need to mount the
+ raw device (such as /dev/sdb instead of /dev/sdb4) and use the part
+ mount option to indicate which partition you want.
+
+ 1133.. CCaann II mmoouunntt mmuullttiippllee HHFFSS ppaarrttiittiioonnss ffrroomm tthhee ssaammee MMaacciinnttoosshh ddiisskk??
+
+ Only if your kernel understands Macintosh partition tables. It the
+ kernel doesn't understand the Macintosh partition table, the HFS
+ filesystem must access the raw device. Therefore, the kernel thinks
+ the entire drive is in use and prevents additional mounts on it.
+
+ 1144.. IInn wwhhaatt wwaayyss ccaann II wwrriittee ttoo HHFFSS ffiilleessyysstteemmss??
+
+ The HFS filesystem is as capable as the MS-DOS or VFAT filesystems,
+ except that certain things can only be done with a file's data fork.
+
+ You ccaann:
+
+ +o Create, delete and rename directories and data forks of files with
+ the caveat that names are case insensitive (so foo and Foo are the
+ same file or directory).
+
+ +o Run Linux executables or shared libraries on an HFS disk if they
+ are stored in the data fork of a file.
+
+ +o Read, write and truncate both forks of files and the Finder's
+ metadata of files and directories.
+
+ +o Mmap data forks of files (and the resource fork if the filesystem
+ is mounted with the fork=cap option).
+
+ +o Toggle the 'w' permission bits (as a group) of data forks.
+
+ +o Change the i_mtime of files and directories.
+
+ You ccaannnnoott:
+
+ +o Create, delete or rename resource forks of files or the Finder's
+ metadata. Note, however, that they are created (with defaults
+ values), deleted and renamed along with the corresponding data fork
+ or directory.
+
+ +o Run Linux executables or shared libraries on an HFS disk if they
+ are stored in the resource fork of a file.
+
+ +o Mmap the Finder's metadata (when fork=cap) or AppleDouble header
+ files (when fork=double or fork=netatalk).
+
+ +o Change permissions on directories.
+
+ +o Change the uid or gid of files or directories.
+
+ +o Set the set-uid, set-gid or sticky permission bits.
+
+ +o Create multiple links to files.
+
+ +o Create symlinks, device files, sockets or FIFOs.
+
+ 1155.. DDooeess tthhee HHFFSS ffiilleessyysstteemm wwoorrkk wwiitthh 440000kk oorr 880000kk MMaacciinnttoosshh
+ ddiisskkeetttteess??
+
+ Yes and no. The software is fully capable of dealing with HFS disks
+ of any size. However, the 400k and 800k diskettes are written in a
+ physical format that is incompatible with most non-Macintosh floppy
+ drives. Note also that almost all 400k Macintosh diskettes are MFS,
+ not HFS.
+
+ 1166.. HHooww ccaann II ffoorrmmaatt aann HHFFSS ffiilleessyysstteemm??
+
+ Robert Leslie (rob@mars.org) has written a package for working with
+ HFS filesystems (like mtools plus a graphical interface). One program
+ in the package is hformat which can format HFS filesystems. The
+ latest version can be found on the HFS Utilities home page
+ <http://www.mars.org/home/rob/proj/hfs/>.
+
+ 1177.. HHooww ccaann II ffsscckk aann HHFFSS ffiilleessyysstteemm??
+
+ Right now you'll have to use a Macintosh to do this. However, Rob
+ Leslie is working on an fsck for HFS filesystems.
+
+ 1188.. WWhhyy ddoo II ggeett ````eerrrroorr --5500'''' mmeessssaaggeess ffrroomm mmyy MMaacc wwhheenn uussiinngg
+ nneettaattaallkk??
+
+ To be compatible with netatalk's afpd you will need to use netatalk
+ version 1.4b1 or newer and mount the HFS filesystem with the ``afpd''
+ mount option. More information is provided in the ``afpd'' subsection
+ of the ``Mount Options'' section of the HFS documentation (HFS.txt if
+ you have the stand-alone HFS distribution or
+ linux/Documentation/filesystems/hfs.txt if HFS is in your kernel
+ source tree.)
+
+ 1199.. WWhhyy ddooeess mmyy MMaacciinnttoosshh sshhooww ggeenneerriicc aapppplliiccaattiioonn aanndd ddooccuummeenntt
+ iiccoonnss??
+
+ When using the ``afpd'' mount option the Desktop database on the disk
+ is not made available to Netatalk's afpd. Because of this mounting an
+ HFS filesystem across the network to a Macintosh may result in the
+ Finder showing generic application and document icons. Additionally
+ double clicking on a document will fail to start the correct
+ application.
+
+ If the disk is writable you can make Netatalk build a new Desktop
+ database in its own format by holding down the Option key while
+ selecting the volume in the Chooser. If the disk is not writable then
+ these problems can be worked around by copying the application to a
+ local disk on the Macintosh.
+
+ 2200.. HHooww oowwnnss aallll tthhee ccooppyyrriigghhttss aanndd ttrraaddeemmaarrkkss?? ;;--))
+
+ 2200..11.. TThhiiss DDooccuummeenntt
+
+ This document is Copyright (c) 1996, 1997 by Paul H. Hargrove.
+
+ Permission is granted to make and distribute verbatim copies of this
+ document provided the copyright notice and this permission notice are
+ preserved on all copies.
+
+ Permission is granted to copy and distribute modified versions of this
+ document under the conditions for verbatim copies above, provided a
+ notice clearly stating that the document is a modified version is also
+ included in the modified document.
+
+ Permission is granted to copy and distribute translations of this
+ document into another language, under the conditions specified above
+ for modified versions.
+
+ Permission is granted to convert this document into another media
+ under the conditions specified above for modified versions provided
+ the requirement to acknowledge the source document is fulfilled by
+ inclusion of an obvious reference to the source document in the new
+ media. Where there is any doubt as to what defines ``obvious'' the
+ copyright owner reserves the right to decide.
+
+ 2200..22.. TThhee SSooffttwwaarree
+
+ The HFS filesystem software is Copyright (c) 1994-1997 by Paul H.
+ Hargrove.
+
+ The software 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 software 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 software in the file ``COPYING''; if not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
+ USA.
+
+ 2200..33.. TTrraaddeemmaarrkkss
+
+ +o ``Finder'' is a trademark of Apple Computer, Inc.
+
+ +o ``Apple'', ``AppleShare'', and ``Macintosh'' are registered
+ trademarks of Apple Computer, Inc.
+
+ +o ``MS-DOS'' is a registered trademarks of Microsoft Corporation.
+
+ +o All other trademarks are the property of their respective owners.
+
diff --git a/fs/hfs/HFS.txt b/fs/hfs/HFS.txt
new file mode 100644
index 00000000000000..5c1ae5fcce01c4
--- /dev/null
+++ b/fs/hfs/HFS.txt
@@ -0,0 +1,1042 @@
+ Macintosh HFS Filesystem for Linux
+ Paul H. Hargrove, hargrove@sccm.Stanford.EDU
+ version 0.95, 28 Apr 1997
+
+ This document describes version 0.95 of the Macintosh HFS filesystem
+ for Linux. The most current versions of this document and the
+ software are kept at The HFS for Linux Page
+ <http://www-sccm.Stanford.EDU/~hargrove/HFS/>.
+ ______________________________________________________________________
+
+ Table of Contents:
+
+ 1. Introduction
+
+ 2. Mounting HFS Filesystems
+
+ 2.1. afpd
+
+ 2.2. case={asis, lower}
+
+ 2.3. conv={auto, binary, text}
+
+ 2.4. creator=cccc
+
+ 2.5. fork={cap, double, netatalk}
+
+ 2.6. gid=n
+
+ 2.7. names={7bit, 8bit, alpha, cap, latin, netatalk, trivial}
+
+ 2.8. part=n
+
+ 2.9. quiet
+
+ 2.10. type=cccc
+
+ 2.11. uid=n
+
+ 2.12. umask=n
+
+ 3. Writing to HFS Filesystems
+
+ 3.1. Writing with fork=cap
+
+ 3.2. Writing with fork=double
+
+ 3.3. Writing with fork=netatalk
+
+ 4. A Guide to Special File Formats
+
+ 4.1. CAP .finderinfo Files
+
+ 4.2. AppleDouble Header Files
+
+ 5. Reporting Bugs
+
+ 5.1. What Goes in a Bug Report
+
+ 5.2. How to Report a Kernel Oops or GPF
+
+ 6. Legal Notices
+
+ 6.1. This Document
+
+ 6.2. The Software
+
+ 6.2.1. The Columbia AppleTalk Package for UNIX
+
+ 6.2.2. Netatalk
+
+ 6.3. Trademarks
+ ______________________________________________________________________
+
+ 11.. IInnttrroodduuccttiioonn
+
+ This software implements the Macintosh HFS filesystem under Linux. It
+ allows you to read and write HFS filesystems on floppy disks, CDROMs,
+ hard drives, ZIP drives, etc. It is _n_o_t an AppleShare client.
+
+ If you use this software, please send me a note telling of your
+ success or failure with it. Your feedback lets me know that this
+ project is not a waste of my time.
+
+ This code is still experimental, so backup anything important before
+ you start playing. I'd like you to know that I've never lost any
+ files while using this software, or I would not release it. However,
+ a ``better safe than sorry'' attitude is probably best.
+
+ If, for instance, the buffer cache were to become corrupted you could
+ start losing things on other disks. Because of this, if you get a
+ General Protection Fault, or a kernel Oops, I _s_t_r_o_n_g_l_y recommend that
+ you reboot before writing any files.
+
+ 22.. MMoouunnttiinngg HHFFSS FFiilleessyysstteemmss
+
+ Once you have the HFS filesystem compiled into the kernel or installed
+ as a loadable module, you will be able to use hfs as a filesystem type
+ option to mount. For instance, to mount a Macintosh floppy disk on
+ the directory /mnt using the default mount options you would execute
+ ``mount -t hfs /dev/fd0 /mnt''.
+
+ The remainder of this section describes the several mount options
+ available to control how the HFS filesystem is mapped onto a Linux
+ filesystem structure. The values for the multiple-choice options
+ (case, conv, fork and names) can be abbreviated by their first
+ character.
+
+ 22..11.. aaffppdd
+
+ If included in the options, then the behavior of the filesystem is
+ changed to make it fully read-write compatible with Netatalk's afpd.
+ In this mode you should not use normal user-level tools to modify the
+ filesystem, though reading from it is acceptable. This is because the
+ return codes from some system calls are changed to fool afpd. These
+ changes will confuse many user-level tools. In particular ``rm -r''
+ will loop forever.
+
+ This option implies fork=netatalk, which in turn implies
+ names=netatalk. If either of these options are explicitly set to
+ something else they will take precedence and will confuse afpd. The
+ quiet option has no effect. The case= option functions normally, but
+ afpd usually does the same thing for you. The conv= and part= options
+ also function normally.
+
+ You will probably want to use the uid=, gid= and umask= mount options.
+ Note that because all the files on an HFS filesystem belong to a
+ single user and group and have a single umask, the full AppleShare
+ permission scheme will not work through Netatalk.
+
+ One additional limitation is that the Desktop database on the disk is
+ stored in afpd's format and is separate from any existing database
+ maintained by the Finder when the volume is used on a Macintosh.
+ Because of this mounting an HFS CDROM across the network to a
+ Macintosh may result in applications and documents showing up with
+ default application and document icons. Additionally double clicking
+ on a document will fail to start the correct application. Both of
+ these problems can be worked around by copying the application to a
+ local disk on the Macintosh.
+
+ This mode is known to be compatible with afpd from Netatalk versions
+ 1.4b1 and 1.4b2, and known to be incompatible with the afpd from
+ version 1.3.3. As of this writing Netatalk version 1.4 has not yet
+ been released. However, it is expected that this mode will be
+ compatible with afpd from Netatalk version 1.4 when it is released.
+
+ 22..22.. ccaassee=={{aassiiss,, lloowweerr}}
+
+ default value: asis
+
+ This option determines if Macintosh filenames are presented in their
+ original case or in all lowercase. Filename lookup is always case
+ insensitive, so either way foo and Foo refer to the same file but ls
+ will list Foo with case=asis, and foo with case=lower. (Same as for
+ the HPFS filesystem.)
+
+ aassiiss
+ Filenames are reported in the case they were created with.
+
+ lloowweerr
+ Filenames are reported in lowercase.
+
+ 22..33.. ccoonnvv=={{aauuttoo,, bbiinnaarryy,, tteexxtt}}
+
+ default value: binary
+
+ This option controls CR<->NL conversion of Macintosh _d_a_t_a _f_o_r_k_s. Any
+ translation takes place only for files accessed with the read() and
+ write() system calls (either directly or through the stdio functions).
+ Access through mmap() is unaffected. (Similar to the conv= option for
+ the MS-DOS filesystem.)
+
+ aauuttoo
+ If the Finder's type for a file is TEXT or ttro, then CR
+ characters are converted to NL characters when read, and NL
+ characters are converted to CR characters when written.
+
+ Be warned that some Macintosh applications create files with
+ type TEXT even though the contents is clearly binary.
+
+ bbiinnaarryy
+ No CR<->NL conversion is done.
+
+ tteexxtt
+ In all data forks, regardless of the Finder's type for the file,
+ CR characters are converted to NL characters when read, and NL
+ characters are converted to CR characters when written.
+
+ 22..44.. ccrreeaattoorr==cccccccc
+
+ default value: ``????''
+
+ Specifies the 4-character string specifying the Finder's Creator for
+ new files.
+
+ 22..55.. ffoorrkk=={{ccaapp,, ddoouubbllee,, nneettaattaallkk}}
+
+ default value: cap
+
+ This option determines how resource forks and the Finder's metadata
+ are represented within the structure of the Linux filesystem.
+
+ ccaapp
+ The scheme used by the Columbia AppleTalk Package's AUFS.
+
+ Associated with each directory are two special directories and a
+ metadata file. The directory ./bar is represented by:
+
+ ..//bbaarr
+ The directory itself, containing subdirectories, the data
+ forks of files, and the following two special directories.
+
+ ..//bbaarr//..rreessoouurrccee
+ A special directory holding resource forks of the files in
+ ./bar.
+
+ ..//bbaarr//..ffiinnddeerriinnffoo
+ A special directory holding metadata files for the files and
+ subdirectories in ./bar.
+
+ ..//..ffiinnddeerriinnffoo//bbaarr
+ The metadata file for the directory ./bar.
+
+ The files in a directory are represented as three files:
+
+ ..//ffoooo
+ The data fork of the file ./foo.
+
+ ..//..rreessoouurrccee//ffoooo
+ The resource fork of the file ./foo.
+
+ ..//..ffiinnddeerriinnffoo//ffoooo
+ The metadata file for the file ./foo.
+
+ Additionally, the file .rootinfo in the root directory of the
+ HFS filesystem is a metadata file for the root directory.
+
+ Brief documentation on the format of file containing the
+ Finder's metadata is included in the section ``A Guide to
+ Special File Formats'' in this document. More detailed
+ information is available in the Columbia AppleTalk Package.
+
+ ddoouubbllee
+ The ``AppleDouble'' format recommended by Apple. (Apple's other
+ recommended format, ``AppleSingle'', is not yet implemented.)
+
+ Associated with each directory is an AppleDouble ``header
+ file''. The directory ./bar is represented by:
+
+ ..//bbaarr
+ The directory itself, containing subdirectories, the data
+ forks for files, and the header files for files and
+ subdirectories.
+
+ ..//%%bbaarr
+ The header file for the directory ./bar, containing the
+ Finder's metadata for the directory.
+
+ The files in a directory are represented as two files:
+
+ ..//ffoooo
+ The data fork of the file ./foo.
+
+ ..//%%ffoooo
+ The header file for the file ./foo, containing the resource
+ fork and the Finder's metadata for the file.
+
+ Additionally, the file %RootInfo in the root directory of the
+ HFS filesystem is a header file for the root directory. This is
+ not quite the %RootInfo file referred to in the AppleDouble
+ specification.
+
+ The header files used in this scheme are version 2 AppleDouble
+ header files. Their format is described briefly in the section
+ ``A Guide to Special File Formats'' in this document. They are
+ documented in detail in ``AppleSingle/AppleDouble Formats:
+ Developer's Note (9/94)'', available from Apple's Developer
+ Services Page <http://devworld.apple.com>.
+
+ Note that the naming convention for the header file can cause
+ name conflicts. For instance, using Apple's 7-bit ASCII name
+ conversion (see the names mount option) the name %Desktop could
+ be interpreted either as the header file for the file Desktop or
+ as the file with 0xDE as the hexadecimal representation of its
+ first character, and "sktop" as the remaining 5 characters. The
+ problem arises when both files exist, since only one will be
+ accessible. The behavior of the HFS filesystem in the case of
+ such a conflict is undefined, and may change in future releases.
+ (If this causes problems for you, please don't report it as a
+ bug; I didn't design this ``standard'', Apple did.)
+
+ nneettaattaallkk
+ The scheme used by the Netatalk afpd.
+
+ Associated with each directory is a special directory and a
+ metadata file. The directory ./bar is represented by:
+
+ ..//bbaarr
+ The directory itself, containing subdirectories, the data
+ forks of files, and the following special directory.
+
+ ..//bbaarr//..AApppplleeDDoouubbllee
+ A special directory holding AppleDouble header files for
+ ./bar and the files it contains, but not for the
+ subdirectories it contains.
+
+ ..//bbaarr//..AApppplleeDDoouubbllee//..PPaarreenntt
+ The header file for the directory ./bar, containing the
+ Finder's metadata for the directory.
+
+ The files in a directory are represented as two files:
+
+ ..//ffoooo
+ The data fork of the file ./foo.
+
+ ..//..AApppplleeDDoouubbllee//ffoooo
+ The header file for file ./foo, containing the resource fork
+ and the Finder's metadata.
+
+ The header files used in this scheme are version 1 AppleDouble
+ header files. They are described briefly in the section ``A
+ Guide to Special File Formats'' in this document. The format is
+ documented in detail in the ``Apple II File Type Notes'' under
+ the type ``$E0.0002/$E0.0003-AppleDouble'', and in Appendix B of
+ the ``A/UX Toolbox: Macintosh ROM Interface'' manual.
+
+ 22..66.. ggiidd==nn
+
+ default value: gid of the mounting process
+
+ Specifies the group that owns all files and directories on the
+ filesystem. (Same as for the MS-DOS and HPFS filesystems.)
+
+ 22..77.. nnaammeess=={{77bbiitt,, 88bbiitt,, aallpphhaa,, ccaapp,, llaattiinn,, nneettaattaallkk,, ttrriivviiaall}}
+
+ default value: varies as follows
+
+ +o If the fork option is set to double, then names defaults to alpha.
+
+ +o If the fork option is set to netatalk, then names defaults to
+ netatalk.
+
+ +o If the fork option is set to cap (or has taken that value by
+ default), then names defaults to cap.
+
+ This option determines how to convert between valid Macintosh
+ filenames and valid Linux filenames. The 7bit, 8bit and alpha options
+ correspond to Apple's recommended conventions named ``7-bit ASCII'',
+ ``8-bit'' and ``7-bit alphanumeric''.
+
+ 77bbiitt
+ When converting from Macintosh filenames to Linux filenames the
+ NULL (0x00), slash (/) and percent (%) characters and the
+ extended 8-bit characters (hexadecimal codes 0x80-0xff) are
+ replaced by a percent character (%) followed by the two-digit
+ hexadecimal code for the character.
+
+ When converting from Linux filenames to Macintosh filenames the
+ string "%YZ" is replaced by the character with hexadecimal code
+ 0xYZ. If 0xYZ is not a valid hexadecimal number or is the code
+ for NULL or colon (:) then the string "%YZ" is unchanged. A
+ colon (:) is replaced by a pipe character (|).
+
+ 88bbiitt
+ When converting from Macintosh filenames to Linux filenames the
+ NULL (0x00), slash (/) and percent (%) characters are replaced
+ by a percent character (%) followed by the two-digit hexadecimal
+ code for the character.
+
+ When converting from Linux filenames to Macintosh filenames the
+ string "%YZ" is replaced by the character with hexadecimal code
+ 0xYZ. If 0xYZ is not a valid hexadecimal number or is the code
+ for NULL or colon (:) then the string "%YZ" is unchanged. A
+ colon (:) is replaced by a pipe character (|).
+
+ aallpphhaa
+ When converting from Macintosh filenames to Linux filenames only
+ the alphanumeric characters (a-z, A-Z and 0-9), the underscore
+ (_) and the last period (.) in the filename are unchanged. The
+ remaining characters are replaced by a percent character (%)
+ followed by the two-digit hexadecimal code for the character.
+
+ When converting from Linux filenames to Macintosh filenames the
+ string "%YZ" is replaced by the character with hexadecimal code
+ 0xYZ. If 0xYZ is not a valid hexadecimal number or is the code
+ for NULL or colon (:) then the string "%YZ" is unchanged. A
+ colon (:) is replaced by a pipe character (|).
+
+ ccaapp
+ The convention used by the Columbia AppleTalk Package's AUFS.
+
+ When converting from Macintosh filenames to Linux filenames the
+ characters from space ( ) through tilde (~) (ASCII 32-126) are
+ unchanged, with the exception of slash (/). The slash (/) and
+ all characters outside the range 32-126 are replaced by a colon
+ (:) followed by the two-digit hexadecimal code for the
+ character.
+
+ When converting from Linux filenames to Macintosh filenames the
+ string ":YZ" is replaced by the character with hexadecimal code
+ 0xYZ. If 0xYZ is not a valid hexadecimal number or is the code
+ for NULL or colon (:) then the colon is replaced by a pipe
+ character (|).
+
+ llaattiinn
+ When converting from Macintosh filenames to Linux filenames the
+ characters from space ( ) through tilde (~) (ASCII 32-126) are
+ unchanged, with the exception of slash (/) and percent (%). The
+ extended 8-bit Macintosh characters with equivalents in the
+ Latin-1 character set are replaced by those equivalents. The
+ remaining characters are replaced by a percent character (%)
+ followed by the two-digit hexadecimal code for the character.
+
+ When converting from Linux filenames to Macintosh filenames the
+ string "%YZ" is replaced by the character with hexadecimal code
+ 0xYZ. If 0xYZ is not a valid hexadecimal number or is the code
+ for NULL or colon (:) then the string "%YZ" is unchanged. The
+ Latin-1 characters with equivalents in the extended 8-bit
+ Macintosh character set are replaced by those equivalents. A
+ colon (:) is replaced by a pipe character (|).
+
+ Thanks to Holger Schemel (aeglos@valinor.owl.de) for
+ contributing this conversion mode.
+
+ nneettaattaallkk
+ The convention used by the Netatalk afpd.
+
+ When converting from Macintosh filenames to Linux filenames the
+ characters from space ( ) through tilde (~) (ASCII 32-126) are
+ unchanged, with the exception of slash (/) and any initial
+ period (.). The slash (/) and any initial period (.) and all
+ characters outside the range 32-126 are replaced by a colon (:)
+ followed by the two-digit hexadecimal code for the character.
+
+ When converting from Linux filenames to Macintosh filenames the
+ string ":YZ" is replaced by the character with hexadecimal code
+ 0xYZ. If 0xYZ is not a valid hexadecimal number or is the code
+ for NULL or colon (:) then the colon is replaced by a pipe
+ character (|).
+
+ ttrriivviiaall
+ When converting from Macintosh filenames to Linux filenames a
+ slash character (/) is replaced by a colon (:).
+
+ When converting from Linux filenames to Macintosh filenames a
+ colon (:) is replaced by a slash character (/).
+
+ 22..88.. ppaarrtt==nn
+
+ default value: 0
+
+ Specifies which HFS partition to mount from a Macintosh CDROM or hard
+ drive. Partitions are numbered from 0 and count only those identified
+ in the partition table as containing HFS filesystems. This option is
+ only useful when the Linux platform doesn't fully support Macintosh
+ partition tables. In particular on MkLinux and Linux-Pmac this option
+ is useless.
+
+ Note that in versions before 0.8.3 partitions were numbered from 1.
+
+ 22..99.. qquuiieett
+
+ If included in the options, then chown and chmod operations will not
+ return errors, but will instead fail silently. (Same as for the MS-
+ DOS and HPFS filesystems.)
+
+ 22..1100.. ttyyppee==cccccccc
+
+ default value: ``????''
+
+ Specifies the 4-character string specifying the Finder's Type for new
+ files.
+
+ 22..1111.. uuiidd==nn
+
+ default value: uid of the mounting process
+
+ Specifies the user that owns all files and directories on the
+ filesystem. (Same as for the MS-DOS and HPFS filesystems.)
+
+ 22..1122.. uummaasskk==nn
+
+ default value: umask of the mounting process
+
+ Specifies (in octal) the umask used for all files and directories.
+ (Same as for the MS-DOS and HPFS filesystems.)
+
+ 33.. WWrriittiinngg ttoo HHFFSS FFiilleessyysstteemmss
+
+ Each of the values of the fork mount option yields a different
+ representation of the Macintosh-specific parts of a file within the
+ structure of the Linux filesystem. There are, therefore, slightly
+ different steps involved in copying files if you want to preserve the
+ resource forks and the Finder's metadata.
+
+ It is important to remember not to use normal user-level tools to
+ modify a filesystem mounted with the afpd mount option.
+
+ Regardless of the value of the fork mount option you can do virtually
+ everything to the data fork of a file that you can to a file on any
+ other filesystem. The limitations are essentially the same as those
+ imposed by the MS-DOS filesystem:
+
+ +o You can't change the uid or gid of files.
+
+ +o You can't set the set-uid, set-gid or sticky permission bits.
+
+ +o You can't clear the execute permission bits.
+
+ Likewise you can do virtually everything to a directory that you can
+ to a directory on another file system with the following exceptions:
+
+ +o You can't create, delete or rename resource forks of files or the
+ Finder's metadata. Note, however, that they are created (with
+ defaults values), deleted and renamed along with the corresponding
+ data fork or directory.
+
+ +o You can't change permissions on directories.
+
+ +o You can't change the uid or gid of directories.
+
+ +o You can't create multiple links to files.
+
+ +o You can't create symlinks, device files, sockets or FIFOs.
+
+ 33..11.. WWrriittiinngg wwiitthh ffoorrkk==ccaapp
+
+ Unlike the other schemes for representing forked files, the CAP scheme
+ presents the resource fork as an independent file; the resource fork
+ of ./foo is ./.resource/foo. Therefore, you can treat it as a normal
+ file. You can do anything to a resource fork that you can do to a
+ data fork, except that you cannot enable execute permissions on a
+ resource fork. Therefore, resource forks are not suitable for holding
+ Linux executables or shared libraries.
+
+ If you plan to use the resource fork on a Macintosh then you must obey
+ the format of a valid resource fork. This format is documented in
+ Chapter 1 of Apple's _I_n_s_i_d_e _M_a_c_i_n_t_o_s_h_: _M_o_r_e _M_a_c_i_n_t_o_s_h _T_o_o_l_b_o_x. The
+ filesystem knows nothing about this format and so does nothing to
+ enforce it.
+
+ The current support for reading and writing is sufficient to allow
+ copying of entire directories with tar, as long as both the source and
+ destination are mounted with fork=cap. tar may complain about being
+ unable to change the uid, gid or mode of files. This is normal and is
+ an unavoidable side effect of the having a single uid, gid and umask
+ for the entire filesystem.
+
+ It is impossible to create a resource fork or a Finder metadata file.
+ However, they are created automatically when the data fork is created.
+ Therefore, if you wish to copy a single file including both forks and
+ the Finder's metadata then you must create the data fork first. Then
+ you can copy the resource fork and the Finder's metadata. For
+ instance to copy the file foo to dir/bar you should do the following:
+
+ 1. cp foo dir/bar
+
+ 2. cp .resource/foo dir/.resource/bar
+
+ 3. cp .finderinfo/foo dir/.finderinfo/bar
+
+ You may get ``Operation not permitted'' errors from cp when it tries
+ to change the permissions on files. These errors can safely be
+ ignored. This method will work even if the file dir/bar exists.
+
+ If you wish to move foo to dir/bar and foo and dir are on the same
+ filesystem then you only need to execute ``mv foo dir/bar'' and the
+ resource fork and the Finder's metadata will move too. However, if
+ foo and dir are on different filesystem then this will lose the
+ resource fork and metadata. Therefore, it is safest to always move
+ files as follows:
+
+ 1. cp foo dir/bar
+
+ 2. cp .resource/foo dir/.resource/bar
+
+ 3. cp .finderinfo/foo dir/.finderinfo/bar
+
+ 4. rm foo
+
+ You may get ``Operation not permitted'' errors from cp when it tries
+ to change the permissions on files. These errors can safely be
+ ignored. This method will work even if the file dir/bar exists.
+
+ Directories have no resource fork but you may wish to create a
+ directory which has the same location and view on the Finder's screen
+ as an existing one. This can be done by copying the Finder metadata
+ file. To give the directory bar the same location, layout, creation
+ date and modify date as foo you simply execute ``cp .finderinfo/foo
+ .finderinfo/bar''.
+
+ When copying an entire directory with ``cp -R'' you may also wish to
+ copy the metadata for the directory:
+
+ 1. cp -R foo bar
+
+ 2. cp .finderinfo/foo .finderinfo/bar
+
+ You may get ``Operation not permitted'' errors from cp when it tries
+ to change the permissions on files. These errors can safely be
+ ignored.
+
+ 33..22.. WWrriittiinngg wwiitthh ffoorrkk==ddoouubbllee
+
+ The current support for reading and writing header files is sufficient
+ to allow copying of entire directories with tar, as long as both the
+ source and destination are mounted with fork=double. tar may complain
+ about being unable to change the uid, gid or mode of files. This is
+ normal and is an unavoidable side effect of the having a single uid,
+ gid and umask for the entire filesystem.
+
+ It is impossible to create a header file. However, they are created
+ automatically when the data fork is created. Therefore, if you wish
+ to copy a single file including both forks and the Finder's metadata
+ then you must create the data fork first. Then you can copy the
+ header file. instance to copy the file foo to dir/bar you should do
+ the following:
+
+ 1. cp foo dir/bar
+
+ 2. cp %foo dir/%bar
+
+ You may get ``Operation not permitted'' errors from cp when it tries
+ to change the permissions on files. These errors can safely be
+ ignored. This method will work even if the file dir/bar exists.
+
+ If you wish to move foo to dir/bar and foo and dir are on the same
+ filesystem then you only need to execute ``mv foo dir/bar'' and the
+ header file will move too. However, if foo and dir are on different
+ filesystem then this will lose the header file. Therefore, it is
+ safest to always move files as follows:
+
+ 1. cp foo dir/bar
+
+ 2. cp %foo dir/%bar
+
+ 3. rm foo
+
+ You may get ``Operation not permitted'' errors from cp when it tries
+ to change the permissions on files. These errors can safely be
+ ignored. This method will work even if the file dir/bar exists.
+
+ Directories have no resource fork but you may wish to create a
+ directory which has the same location and view on the Finder's screen
+ as an existing one. This can be done by copying the corresponding
+ header file. To give the directory bar the same location, layout,
+ creation date and modify date as foo simply execute ``cp %foo %bar''.
+
+ When copying an entire directory with ``cp -R'' you may also wish to
+ copy the header file for the directory as well:
+
+ 1. cp -R foo bar
+
+ 2. cp %foo %bar
+
+ You may get ``Operation not permitted'' errors from cp when it tries
+ to change the permissions on files. These errors can safely be
+ ignored.
+
+ 33..33.. WWrriittiinngg wwiitthh ffoorrkk==nneettaattaallkk
+
+ The current support for reading and writing header files is sufficient
+ to allow copying of entire directories with tar, as long as both the
+ source and destination are mounted fork=netatalk. tar may complain
+ about being unable to change the uid, gid or mode of files. This is
+ normal and is an unavoidable side effect of the having a single uid,
+ gid and umask for the entire filesystem.
+
+ It is impossible to create a header file. However, they are created
+ automatically when the data fork is created. Therefore, if you wish
+ to copy a single file including both forks and the Finder's metadata
+ then you must create the data fork first. Then you can copy the
+ header file. instance to copy the file foo to dir/bar you should do
+ the following:
+
+ 1. cp foo dir/bar
+
+ 2. cp .AppleDouble/foo dir/.AppleDouble/bar
+
+ You may get ``Operation not permitted'' errors from cp when it tries
+ to change the permissions on files. These errors can safely be
+ ignored. This method will work even if the file dir/bar exists.
+
+ If you wish to move foo to dir/bar and foo and dir are on the same
+ filesystem then you only need to execute ``mv foo dir/bar'' and the
+ header file will move too. However, if foo and dir are on different
+ filesystem then this will lose the header file. Therefore, it is
+ safest to always move files as follows:
+
+ 1. cp foo dir/bar
+
+ 2. cp .AppleDouble/foo dir/.AppleDouble/bar
+
+ 3. rm foo
+
+ You may get ``Operation not permitted'' errors from cp when it tries
+ to change the permissions on files. These errors can safely be
+ ignored. This method will work even if the file dir/bar exists.
+
+ Directories have no resource fork but you may wish to create a
+ directory which has the same location and view on the Finder's screen
+ as an existing one. This can be done by copying the corresponding
+ header file. To give the directory bar the same location, layout,
+ creation date and modify date as foo you simply execute ``cp
+ foo/.AppleDouble/.Parent bar/.AppleDouble/.Parent''.
+
+ Because the fork=netatalk scheme holds the header file for a directory
+ within that directory, directories can safely be copied with ``cp -R
+ foo bar'' with no loss of information. However, you may get
+ ``Operation not permitted'' errors from cp when it tries to change the
+ permissions on files. These errors can safely be ignored.
+
+ 44.. AA GGuuiiddee ttoo SSppeecciiaall FFiillee FFoorrmmaattss
+
+ Each of the values of the fork mount option yields different special
+ files to represent the Macintosh-specific parts of a file within the
+ structure of the Linux filesystem. You can write to these special
+ files to change things such as the Creator and Type of a file.
+ However, to do so safely you must follow certain rules to avoid
+ corrupting the data. Additionally, there are certain fields in the
+ special files that you can't change (writes to them will fail
+ silently).
+
+ 44..11.. CCAAPP ..ffiinnddeerriinnffoo FFiilleess
+
+ The Finder's metadata for the file ./foo in held in the file
+ ./.finderinfo/foo. The file has a fixed format defined in hfs_fs.h as
+ follows:
+
+ ______________________________________________________________________
+ struct hfs_cap_info {
+ __u8 fi_fndr[32]; /* Finder's info */
+ __u16 fi_attr; /* AFP attributes */
+ __u8 fi_magic1; /* Magic number: */
+ #define HFS_CAP_MAGIC1 0xFF
+ __u8 fi_version; /* Version of this structure: */
+ #define HFS_CAP_VERSION 0x10
+ __u8 fi_magic; /* Another magic number: */
+ #define HFS_CAP_MAGIC 0xDA
+ __u8 fi_bitmap; /* Bitmap of which names are valid: */
+ #define HFS_CAP_SHORTNAME 0x01
+ #define HFS_CAP_LONGNAME 0x02
+ __u8 fi_shortfilename[12+1]; /* "short name" (unused) */
+ __u8 fi_macfilename[32+1]; /* Original (Macintosh) name */
+ __u8 fi_comln; /* Length of comment (always 0) */
+ __u8 fi_comnt[200]; /* Finder comment (unused) */
+ /* optional: used by aufs only if compiled with USE_MAC_DATES */
+ __u8 fi_datemagic; /* Magic number for dates extension: */
+ #define HFS_CAP_DMAGIC 0xDA
+ __u8 fi_datevalid; /* Bitmap of which dates are valid: */
+ #define HFS_CAP_MDATE 0x01
+ #define HFS_CAP_CDATE 0x02
+ __u8 fi_ctime[4]; /* Creation date (in AFP format) */
+ __u8 fi_mtime[4]; /* Modify date (in AFP format) */
+ __u8 fi_utime[4]; /* Un*x time of last mtime change */
+ };
+ ______________________________________________________________________
+
+ The type __u8 is an unsigned character, and __u16 is an unsigned
+ 16-bit integer.
+
+ Currently only the fields fi_fndr, fi_attr, fi_ctime and fi_mtime can
+ be changed. Writes to the other fields are silently ignored.
+ However, you shouldn't write random bytes to the other fields, since
+ they may be writable in the future.
+
+ The fi_fndr field is the ``Finder info'' and ``Extended Finder info''
+ for a file or directory. These structures are described in various
+ books on Macintosh programming. The portion of the most interest is
+ probably the first 8 bytes which, for a file, give the 4-byte Type
+ followed by the 4-byte Creator.
+
+ The fi_attr field is the AFP attributes of the file or directory.
+ While you can write any value to this field, only the ``write-
+ inhibit'' bit is significant. Setting or clearing this bit will clear
+ or set the write bits in the file's permissions. When you read from
+ this field anything you may have written is lost. If the file has
+ write permissions enabled then you will read zero from this field.
+ With write permission disabled you will read back 0x01 0xA0, which
+ corresponds to setting the ``write-inhibit'', ``rename-inhibit'' and
+ ``delete-inhibit'' bits.
+
+ The fi_ctime and fi_mtime are the Macintosh created and modified time
+ for the file or directory, and are 32-bit signed integers in network
+ byteorder giving seconds from 00:00 GMT Jan. 1, 2000.
+
+ 44..22.. AApppplleeDDoouubbllee HHeeaaddeerr FFiilleess
+
+ Both the fork=double and fork=netatalk schemes for representing forked
+ files use AppleDouble header files to contain the resource fork and
+ the Finder's metadata together in a single file.
+
+ The AppleDouble format specifies a fixed-format header which describes
+ which fields are contained in the remainder of the file, where they
+ are located in the file and how long they are. A full description of
+ the version 1 format used when fork=netatalk is available from ??????.
+ The version 2 format used when fork=double is documented in ??????.
+ The discussion that follows assumes you have read and understood these
+ documents, which may be difficult until I've replaced the ``??????''s
+ above with something more informative :-).
+
+ Due to the variable structure of an AppleDouble header file you must
+ not use buffered I/O when reading or writing them; you should only use
+ the read() and write() system calls. It is also important that you
+ make some effort to coordinate processes that are reading and writing
+ the same header file, since a reader will receive the wrong data if
+ the location of a given entry has changed since it read the descriptor
+ for the entry. If a process tries to read the descriptor table while
+ it is changing then it is possible to read totally meaningless data.
+
+ When a header file is opened it is initially presented with a default
+ header layout. You may write to the header to change the layout, but
+ when all file descriptors for the file or directory have been closed
+ the change in format is lost and subsequent opens will yield the
+ default layout. Changes to supported entries are made directly to the
+ filesystem and are thus preserved when the file is closed and
+ reopened.
+
+ The HFS filesystem currently uses a fixed-size table to hold the
+ descriptors. Therefore you are limited to HFS_HDR_MAX (currently 10)
+ descriptors. In the unlikely event that you try to write a header
+ with more descriptors, a warning will be issued by the kernel, and
+ extra descriptors will be ignored. This should be considered a bug
+ and will hopefully change sooner rather than later.
+
+ The results of specifying overlapping entries is undefined and should
+ not be relied upon to remain unchanged from one version of the HFS
+ filesystem to the next. There is no valid reason to define
+ overlapping entries, so just don't do it!
+
+ Changes to the magic number and version fields are preserved until all
+ file descriptors are closed, however the only significance given to
+ them internally is that the 16 bytes following the version changes
+ meaning according to the version. For version 1 header files these 16
+ bytes contain the string ``Macintosh'' followed by 7 spaces. For any
+ other value of the version field these 16 bytes are all zeros. In
+ either case writes to these 16 bytes are silently ignored.
+
+ Since the magic number and version are given no other significance
+ internally, you are free to do many things that violate the official
+ formats. For instance you can create an entry for the data fork in a
+ header file with an AppleDouble magic number or create ``File Info''
+ (id=7) entries in version 2 header files and ``File Dates Info''
+ (id=8) entries in version 1 header files. However, future versions of
+ the filesystem may enforce the format more strictly.
+
+ Entry id 1 (``Data Fork'') is read-only. You should use the data file
+ to modify the data fork. The data fork is, of course, not supported
+ for directories.
+
+ Entry ids 2, 7, 8, 9 and 10 (``Resource Fork'', ``File Info'', ``File
+ Dates Info'', ``Finder Info'' and ``Macintosh File Info'') are fully
+ supported, meaning that their contents may be read and written and
+ that data written is preserved when the file is closed and reopened.
+ The resource fork is, of course, not supported for directories.
+
+ Entry id 7 specifies some of the same data given by ids 8 and 10. If
+ you create a header file with an entry for id 7 and for ids 8 or 10,
+ then the behavior with respect to their interaction is undefined. A
+ header that contains an entry for id 7 and for ids 8 or 10 is not
+ valid as either a version 1 or a version 2 header file, so there is no
+ reason to do this and future versions may prevent it.
+
+ Entry id 3 (``Real Name'') is read-only, since it will change
+ automatically when a file is renamed. Writes to the corresponding
+ entry are silently ignored.
+
+ All other entry ids are ignored. You may create descriptors for them;
+ in fact the default header layout when fork=netatalk includes a
+ descriptor for id 4 (``Comment''). However writes to the entries
+ corresponding to the ignored ids fail silently and reads from the
+ entries always return zeros. However, you shouldn't write random
+ bytes to unsupported entries, since they may be supported in the
+ future.
+
+ All of the supported entry types except the data and resource forks
+ have a fixed length. If you give them a smaller length in the
+ descriptor then you are unable to access part of the corresponding
+ entry. If you give them a larger length in the descriptor, then the
+ corresponding entry is padded with zeros and writes to the extra space
+ are silently ignored.
+
+ Writes to the length field of descriptors for the data and resource
+ forks will cause the corresponding fork to grow (with zero padding) or
+ shrink to the indicated length.
+
+ If you have an entry for the data fork then the descriptor's length
+ field does not change automatically to reflect any modification of the
+ data fork directly (the data does change however). If the data fork
+ is longer than the descriptor indicates, then a portion of it is
+ inaccessible. If the data fork is shorter than the descriptor
+ indicates then reads will be padded with zeros.
+
+ Writes beyond the end of the resource fork that extend into empty
+ space between entries or beyond the end of the file will extend the
+ fork, automatically changing the length field of the corresponding
+ descriptor. Writes to any other space between entries are silently
+ ignored and read of such spaces always return zeros.
+
+ Calling truncate() on a header file can change the length of the
+ resource fork and such a change will automatically be reflected in the
+ length field of the corresponding descriptor. If truncate() shortens
+ the file so that the entry for the resource fork would extend beyond
+ the new end of the file then the fork is shortened to fit in the space
+ that remains, or to zero bytes if the entry is now entirely beyond the
+ end of the file. If the last entry in a header file is the resource
+ fork then a call to truncate() that extends the header file will
+ extend the fork with zeros. Note that this happens even if there was
+ previously space between the end of the fork and the end of the file.
+
+ 55.. RReeppoorrttiinngg BBuuggss
+
+ If you'd like any problems you encounter fixed, you'll need to provide
+ a detailed bug report. However, you should check the FAQ (available
+ from the HFS for Linux Page <http://www-sccm.Stanford.EDU/~hargrove/HFS/>)
+ first to be certain that your problem is not a known limitation of the
+ filesystem. If your bug doesn't appear in the FAQ then you should e-mail
+ me at hargrove@sccm.Stanford.EDU.
+
+ 55..11.. WWhhaatt GGooeess iinn aa BBuugg RReeppoorrtt
+
+ When writing your bug report, include any facts you think might be
+ relevant; I'd much rather have a bunch of extra facts than need to
+ e-mail you to get the information. At a minimum the following
+ information should be included:
+
+ +o The version of the HFS filesystem you are using (see
+ linux/fs/hfs/version.h).
+
+ +o The kernel version you are using.
+
+ +o Any unofficial kernel patches or loadable modules you are using.
+
+ +o If you are loading the HFS filesystem as a module, then version of
+ the module utilities used to load hfs.o.
+
+ +o The type of media you are working with (floppy, CDROM, ZIP Drive,
+ etc.).
+
+ +o The steps required to reproduce the bug, including mount options
+ used. (If you can't reproduce the bug tell me everything you did
+ the one time it did occur, but be warned that non-reproducible bugs
+ can only rarely be fixed.)
+
+ 55..22.. HHooww ttoo RReeppoorrtt aa KKeerrnneell OOooppss oorr GGPPFF
+
+ If you encounter a bug that causes a kernel Oops or a General
+ Protection Fault then you'll need to collect some additional
+ information for the bug report. If you are loading the HFS filesystem
+ as a module, then is important that you do this before rebooting,
+ since the module is unlikely to be loaded at the same address after
+ the reboot.
+
+ You should include all the information that the kernel prints to the
+ console or to the system logs. However, the EIP and Stack Trace are
+ addresses in _y_o_u_r kernel and mean nothing to me without more
+ information. Using your System.map file (or either ksymoops or klogd)
+ determine which functions the EIP and Stack Trace are in. If you do
+ this by hand using your System.map file then the correct symbol is the
+ one of type t or T with the largest address less than or equal to the
+ one you are resolving.
+
+ If you are loading the HFS filesystem as a module and the Oops or GPF
+ was in the HFS code then the EIP and the top levels of the Stack Trace
+ will be in a loadable module, rather than in the kernel proper. So,
+ their symbols will not be in the file System.map. Therefore, you will
+ need to use /proc/ksyms, or a loadmap produced by passing the -m
+ option to insmod, to locate those symbols.
+
+ 66.. LLeeggaall NNoottiicceess
+
+ 66..11.. TThhiiss DDooccuummeenntt
+
+ This document is Copyright (c) 1996, 1997 by Paul H. Hargrove.
+
+ Permission is granted to make and distribute verbatim copies of this
+ document provided the copyright notice and this permission notice are
+ preserved on all copies.
+
+ Permission is granted to copy and distribute modified versions of this
+ document under the conditions for verbatim copies above, provided a
+ notice clearly stating that the document is a modified version is also
+ included in the modified document.
+
+ Permission is granted to copy and distribute translations of this
+ document into another language, under the conditions specified above
+ for modified versions.
+
+ Permission is granted to convert this document into another media
+ under the conditions specified above for modified versions provided
+ the requirement to acknowledge the source document is fulfilled by
+ inclusion of an obvious reference to the source document in the new
+ media. Where there is any doubt as to what defines ``obvious'' the
+ copyright owner reserves the right to decide.
+
+ 66..22.. TThhee SSooffttwwaarree
+
+ The HFS filesystem for Linux is Copyright (c) 1994-1997 by Paul H.
+ Hargrove.
+
+ This software 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.
+
+ This software 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 this software in the file ``COPYING''; if not, write to the
+ Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
+ USA.
+
+ 66..22..11.. TThhee CCoolluummbbiiaa AApppplleeTTaallkk PPaacckkaaggee ffoorr UUNNIIXX
+
+ The source code distribution of the Columbia AppleTalk Package for
+ UNIX, version 6.0, (CAP) was used as a _s_p_e_c_i_f_i_c_a_t_i_o_n of the location
+ and format of files used by CAP's Aufs. No code from CAP appears in
+ the HFS filesystem. The HFS filesystem is not a work ``derived'' from
+ CAP in the sense of intellectual property law.
+
+ 66..22..22.. NNeettaattaallkk
+
+ The source code distributions of Netatalk, versions 1.3.3b2 and 1.4b2,
+ were used as a _s_p_e_c_i_f_i_c_a_t_i_o_n of the location and format of files used
+ by Netatalk's afpd. No code from Netatalk appears in the HFS
+ filesystem. The HFS filesystem is not a work ``derived'' from
+ Netatalk in the sense of intellectual property law.
+
+ 66..33.. TTrraaddeemmaarrkkss
+
+ +o ``Finder'' is a trademarks of Apple Computer, Inc.
+
+ +o ``Apple'', ``AppleShare'', ``AppleTalk'' and ``Macintosh'' are
+ registered trademarks of Apple Computer, Inc.
+
+ +o ``Microsoft'' and ``MS-DOS'' are registered trademarks of Microsoft
+ Corporation.
+
+ +o All other trademarks are the property of their respective owners.
+
diff --git a/fs/hfs/INSTALL.txt b/fs/hfs/INSTALL.txt
new file mode 100644
index 00000000000000..8ea44c0ec7a9f2
--- /dev/null
+++ b/fs/hfs/INSTALL.txt
@@ -0,0 +1,126 @@
+ Installation instructions for the HFS Filesystem for Linux
+ Paul H. Hargrove, hargrove@sccm.Stanford.EDU
+ version 0.95 28 Apr 1997
+
+ This document explains how to compile and install version 0.95 of
+ hfs_fs, the HFS filesystem for Linux.
+
+ 11.. SSyysstteemm RReeqquuiirreemmeennttss
+
+ You will need the following to compile and use this release of hfs_fs:
+
+ +o Kernel version 2.0.1 or newer compiled with modules enabled
+ (CONFIG_MODULES).
+
+ +o The kernel sources (or at least the header files) available online.
+
+ +o The module utilities package current for your kernel version and an
+ understanding of how to use it. (The file
+ Documentation/modules.txt in the kernel source directory provides a
+ brief introduction.)
+
+ 22.. IInnssttaallllaattiioonn
+
+ This release of the HFS filesystem is not part of the official kernel
+ distribution. Therefore, it is compiled as a module and then loaded
+ into the kernel using the module utilities. Therefore, your kernel
+ must be compiled with CONFIG_MODULES enabled.
+
+ 22..11.. CCoommppiilliinngg tthhee llooaaddaabbllee mmoodduullee
+
+ To compile hfs.o you should only need to execute ``make'' in the
+ hfs_fs source directory.
+
+ If gcc complains about not finding a large number of header files with
+ names beginning with ``linux/'' then you probably don't have the
+ kernel header files installed correctly. Either /usr/include/linux,
+ /usr/include/asm and /usr/include/scsi should be symbolic links to
+ include/linux, include/asm and include/scsi in the kernel source tree
+ for the kernel you wish to use hfs_fs with, or else they should be
+ directories containing the header files for the kernel you wish to use
+ hfs_fs with.
+
+ If gcc complains about not finding linux/version.h, then you will need
+ to run ``make dep'' in the kernel source directory to build it. Under
+ MkLinux, run ``make include/linux/version.h'' instead.
+
+ If gcc complains about not finding the files linux/config.h or
+ linux/autoconf.h, then you will need to run ``make config'' and ``make
+ dep'' in the kernel source directory to build these two files.
+
+ If you are compiling on a DEC Alpha and receive messages saying
+ assignment from incompatible pointer type when compiling files dir_*.c
+ and file_*.c, then you need to change a single line in the file
+ linux/hfs_fs.h. Remove the text ``&& !defined(__alpha__)'' from the
+ end of line 217.
+
+ 22..22.. IInnssttaalllliinngg tthhee mmoodduullee iinn tthhee mmoodduulleess ddiirreeccttoorryy ((ooppttiioonnaall))
+
+ If you plan to use kerneld to automatically load the module or if you
+ wish to use modprobe or insmod without supplying a complete path to
+ hfs.o, then you will need to copy hfs.o into a directory where the
+ module utilities expect to find it.
+
+ The proper directory may depend slightly on your configuration.
+ However, /lib/modules/default/fs/ is a common one for filesystem
+ modules. Once hfs.o is in the proper directory you should run depmod
+ -a to update the dependency list used by kerneld and modprobe.
+
+ 22..33.. LLooaaddiinngg tthhee mmoodduullee iinnttoo tthhee rruunnnniinngg kkeerrnneell
+
+ There are three ways to accomplish this:
+
+ 1. If you are running kerneld and have installed hfs.o in the modules
+ directory then you don't need to issue any commands; the module
+ will be loaded when you attempt to mount an HFS filesystem.
+
+ 2. If you are _n_o_t running kerneld then you can load hfs.o manually by
+ running modprobe hfs.o. If you have not installed hfs.o in one of
+ the standard module directories, then you will need provide a full
+ path to the file hfs.o.
+
+ 3. If you have been experiencing kernel crashes with hfs_fs, then you
+ should file a bug report including the names of the functions which
+ the EIP and Stack Trace point into. To help with this you can ask
+ for relocation map for the module when you load it. To do this
+ load the module with ``insmod -m hfs.o >loadmap''. Again, you may
+ need a full path to the file hfs.o if you have not placed it in one
+ of the standard module directories.
+
+ 22..44.. UUssiinngg tthhee mmoodduullee wwiitthh vveerrssiioonneedd ssyymmbboollss
+
+ All the interface between the module and the kernel take place through
+ very stable (since the mid-1.3.x kernels) parts of the kernel. If you
+ enabled versioned symbols (CONFIG_MODVERSIONS) when you compiled your
+ kernel you should often be able to compile this module once and then
+ use it with many kernels newer than the one you compiled it for.
+
+ In any case, it is unlikely that this module will need changes with
+ each new kernel patch; simple recompilation should usually suffice.
+
+ 33.. LLeeggaall NNoottiicceess
+
+ 33..11.. TThhiiss DDooccuummeenntt
+
+ This document is Copyright (c) 1996, 1997 by Paul H. Hargrove.
+
+ Permission is granted to make and distribute verbatim copies of this
+ document provided the copyright notice and this permission notice are
+ preserved on all copies.
+
+ Permission is granted to copy and distribute modified versions of this
+ document under the conditions for verbatim copies above, provided a
+ notice clearly stating that the document is a modified version is also
+ included in the modified document.
+
+ Permission is granted to copy and distribute translations of this
+ document into another language, under the conditions specified above
+ for modified versions.
+
+ Permission is granted to convert this document into another media
+ under the conditions specified above for modified versions provided
+ the requirement to acknowledge the source document is fulfilled by
+ inclusion of an obvious reference to the source document in the new
+ media. Where there is any doubt as to what defines ``obvious'' the
+ copyright owner reserves the right to decide.
+
diff --git a/fs/hfs/Makefile b/fs/hfs/Makefile
new file mode 100644
index 00000000000000..fd5a7589c6aeb0
--- /dev/null
+++ b/fs/hfs/Makefile
@@ -0,0 +1,19 @@
+#
+# Makefile for the Linux hfs filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile.
+
+O_TARGET := hfs.o
+
+obj-y := balloc.o bdelete.o bfind.o bins_del.o binsert.o bitmap.o bitops.o \
+ bnode.o brec.o btree.o catalog.o dir.o dir_cap.o dir_dbl.o \
+ dir_nat.o extent.o file.o file_cap.o file_hdr.o inode.o mdb.o \
+ part_tbl.o string.o super.o sysdep.o trans.o version.o
+
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/hfs/TODO b/fs/hfs/TODO
new file mode 100644
index 00000000000000..fbbc6e16c37941
--- /dev/null
+++ b/fs/hfs/TODO
@@ -0,0 +1,52 @@
+The hfs_fs "to do" list.
+------------------------
+Items are broken down into groups and the groups are listed in order
+from most important to least important. The items within each group
+are not placed in any particular order. The order in which items are
+listed probably doesn't correlate well with the order they will be
+addressed.
+
+Genuine bugs:
+1. Header files have compiled-in limit (currently 10) on descriptors.
+
+Missing features:
+1. 1k block support is needed for some devices.
+2. An ioctl()-based interface is needed to provide a consistent way
+ to do things under all of the representations of forked files.
+
+Possible additional "fork" mount options:
+1. AppleSingle.
+2. The scheme MacOS uses on FAT disks (PC Exchange).
+3. "Flat" (no resource forks or metadata).
+
+Performance issues:
+1. Use drAllocPtr to speed block allocations.
+2. Keep a real cache of bnodes, rather than just a hash table of
+ the ones that are currently in use.
+3. Keep a real cache of extent records, rather than just a linked
+ list of the ones that are currently in use and the one most
+ recently used. This is particularly needed to get acceptable
+ performance with multiple readers on a file. Perhaps simply
+ keep them in memory once they've been read until the file is
+ closed.
+
+Implementation details:
+1. Allocation scheme could/should be closer to that used by Apple.
+2. B*-tree insertion could/should be closer to that used by Apple.
+3. Magic-number checks on data structures are rarely done.
+4. Error recovery is needed for failed binsert(), bdelete() and rename().
+5. Deadlock detection is needed to make insert_empty_bnode() and
+ bdelete() less likely to hang on a corrupted B-tree.
+6. Metadata for covered directories shouldn't appear in the filesystem.
+ Under CAP and AppleDouble it currently does. However, the obvious
+ solution is a real performance killer and is not worth implementing.
+
+Fantasy features:
+1. Access Desktop file/database for comment and icon.
+2. Implement mmap() for AppleDouble header files and CAP info files.
+3. Implement AppleShare client support.
+
+Suggestions/comments/questions are welcome.
+Code addressing any of the issues listed above is especially welcome.
+Paul H. Hargrove
+hargrove@sccm.Stanford.EDU
diff --git a/fs/hfs/balloc.c b/fs/hfs/balloc.c
new file mode 100644
index 00000000000000..58d28178473481
--- /dev/null
+++ b/fs/hfs/balloc.c
@@ -0,0 +1,439 @@
+/*
+ * linux/fs/hfs/balloc.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU General Public License.
+ *
+ * hfs_bnode_alloc() and hfs_bnode_bitop() are based on GPLed code
+ * Copyright (C) 1995 Michael Dreher
+ *
+ * This file contains the code to create and destroy nodes
+ * in the B-tree structure.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ *
+ * The code in this file initializes some structures which contain
+ * pointers by calling memset(&foo, 0, sizeof(foo)).
+ * This produces the desired behavior only due to the non-ANSI
+ * assumption that the machine representation of NULL is all zeros.
+ */
+
+#include "hfs_btree.h"
+
+/*================ File-local functions ================*/
+
+/*
+ * get_new_node()
+ *
+ * Get a buffer for a new node with out reading it from disk.
+ */
+static hfs_buffer get_new_node(struct hfs_btree *tree, hfs_u32 node)
+{
+ int tmp;
+ hfs_buffer retval = HFS_BAD_BUFFER;
+
+ tmp = hfs_extent_map(&tree->entry.u.file.data_fork, node, 0);
+ if (tmp) {
+ retval = hfs_buffer_get(tree->sys_mdb, tmp, 0);
+ }
+ return retval;
+}
+
+/*
+ * hfs_bnode_init()
+ *
+ * Description:
+ * Initialize a newly allocated bnode.
+ * Input Variable(s):
+ * struct hfs_btree *tree: Pointer to a B-tree
+ * hfs_u32 node: the node number to allocate
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * struct hfs_bnode_ref for the new node
+ * Preconditions:
+ * 'tree' points to a "valid" (struct hfs_btree)
+ * 'node' exists and has been allocated in the bitmap of bnodes.
+ * Postconditions:
+ * On success:
+ * The node is not read from disk, nor added to the bnode cache.
+ * The 'sticky' and locking-related fields are all zero/NULL.
+ * The bnode's nd{[FB]Link, Type, NHeight} fields are uninitialized.
+ * The bnode's ndNRecs field and offsets table indicate an empty bnode.
+ * On failure:
+ * The node is deallocated.
+ */
+static struct hfs_bnode_ref hfs_bnode_init(struct hfs_btree * tree,
+ hfs_u32 node)
+{
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+ extern int bnode_count;
+#endif
+ struct hfs_bnode_ref retval;
+
+ retval.lock_type = HFS_LOCK_NONE;
+ if (!HFS_NEW(retval.bn)) {
+ hfs_warn("hfs_bnode_init: out of memory.\n");
+ goto bail2;
+ }
+
+ /* Partially initialize the in-core structure */
+ memset(retval.bn, 0, sizeof(*retval.bn));
+ retval.bn->magic = HFS_BNODE_MAGIC;
+ retval.bn->tree = tree;
+ retval.bn->node = node;
+ hfs_init_waitqueue(&retval.bn->wqueue);
+ hfs_init_waitqueue(&retval.bn->rqueue);
+ hfs_bnode_lock(&retval, HFS_LOCK_WRITE);
+
+ retval.bn->buf = get_new_node(tree, node);
+ if (!hfs_buffer_ok(retval.bn->buf)) {
+ goto bail1;
+ }
+
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+ ++bnode_count;
+#endif
+
+ /* Partially initialize the on-disk structure */
+ memset(hfs_buffer_data(retval.bn->buf), 0, HFS_SECTOR_SIZE);
+ hfs_put_hs(sizeof(struct NodeDescriptor), RECTBL(retval.bn, 1));
+
+ return retval;
+
+bail1:
+ HFS_DELETE(retval.bn);
+bail2:
+ /* clear the bit in the bitmap */
+ hfs_bnode_bitop(tree, node, 0);
+ return retval;
+}
+
+/*
+ * init_mapnode()
+ *
+ * Description:
+ * Initializes a given node as a mapnode in the given tree.
+ * Input Variable(s):
+ * struct hfs_bnode *bn: the node to add the mapnode after.
+ * hfs_u32: the node to use as a mapnode.
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * struct hfs_bnode *: the new mapnode or NULL
+ * Preconditions:
+ * 'tree' is a valid (struct hfs_btree).
+ * 'node' is the number of the first node in 'tree' that is not
+ * represented by a bit in the existing mapnodes.
+ * Postconditions:
+ * On failure 'tree' is unchanged and NULL is returned.
+ * On success the node given by 'node' has been added to the linked
+ * list of mapnodes attached to 'tree', and has been initialized as
+ * a valid mapnode with its first bit set to indicate itself as
+ * allocated.
+ */
+static struct hfs_bnode *init_mapnode(struct hfs_bnode *bn, hfs_u32 node)
+{
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+ extern int bnode_count;
+#endif
+ struct hfs_bnode *retval;
+
+ if (!HFS_NEW(retval)) {
+ hfs_warn("hfs_bnode_add: out of memory.\n");
+ return NULL;
+ }
+
+ memset(retval, 0, sizeof(*retval));
+ retval->magic = HFS_BNODE_MAGIC;
+ retval->tree = bn->tree;
+ retval->node = node;
+ retval->sticky = HFS_STICKY;
+ retval->buf = get_new_node(bn->tree, node);
+ if (!hfs_buffer_ok(retval->buf)) {
+ HFS_DELETE(retval);
+ return NULL;
+ }
+
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+ ++bnode_count;
+#endif
+
+ /* Initialize the bnode data structure */
+ memset(hfs_buffer_data(retval->buf), 0, HFS_SECTOR_SIZE);
+ retval->ndFLink = 0;
+ retval->ndBLink = bn->node;
+ retval->ndType = ndMapNode;
+ retval->ndNHeight = 0;
+ retval->ndNRecs = 1;
+ hfs_put_hs(sizeof(struct NodeDescriptor), RECTBL(retval, 1));
+ hfs_put_hs(0x1fa, RECTBL(retval, 2));
+ *((hfs_u8 *)bnode_key(retval, 1)) = 0x80; /* set first bit of bitmap */
+ retval->prev = bn;
+ hfs_bnode_commit(retval);
+
+ bn->ndFLink = node;
+ bn->next = retval;
+ hfs_bnode_commit(bn);
+
+ return retval;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_bnode_bitop()
+ *
+ * Description:
+ * Allocate/free the requested node of a B-tree of the hfs filesystem
+ * by setting/clearing the corresponding bit in the B-tree bitmap.
+ * The size of the B-tree will not be changed.
+ * Input Variable(s):
+ * struct hfs_btree *tree: Pointer to a B-tree
+ * hfs_u32 bitnr: The node number to free
+ * int set: 0 to clear the bit, non-zero to set it.
+ * Output Variable(s):
+ * None
+ * Returns:
+ * 0: no error
+ * -1: The node was already allocated/free, nothing has been done.
+ * -2: The node is out of range of the B-tree.
+ * -4: not enough map nodes to hold all the bits
+ * Preconditions:
+ * 'tree' points to a "valid" (struct hfs_btree)
+ * 'bitnr' is a node number within the range of the btree, which is
+ * currently free/allocated.
+ * Postconditions:
+ * The bit number 'bitnr' of the node bitmap is set/cleared and the
+ * number of free nodes in the btree is decremented/incremented by one.
+ */
+int hfs_bnode_bitop(struct hfs_btree *tree, hfs_u32 bitnr, int set)
+{
+ struct hfs_bnode *bn; /* the current bnode */
+ hfs_u16 start; /* the start (in bits) of the bitmap in node */
+ hfs_u16 len; /* the len (in bits) of the bitmap in node */
+ hfs_u32 *u32; /* address of the u32 containing the bit */
+
+ if (bitnr >= tree->bthNNodes) {
+ hfs_warn("hfs_bnode_bitop: node number out of range.\n");
+ return -2;
+ }
+
+ bn = &tree->head;
+ for (;;) {
+ start = bnode_offset(bn, bn->ndNRecs) << 3;
+ len = (bnode_offset(bn, bn->ndNRecs + 1) << 3) - start;
+
+ if (bitnr < len) {
+ break;
+ }
+
+ /* continue on to next map node if available */
+ if (!(bn = bn->next)) {
+ hfs_warn("hfs_bnode_bitop: too few map nodes.\n");
+ return -4;
+ }
+ bitnr -= len;
+ }
+
+ /* Change the correct bit */
+ bitnr += start;
+ u32 = (hfs_u32 *)hfs_buffer_data(bn->buf) + (bitnr >> 5);
+ bitnr %= 32;
+ if ((set && hfs_set_bit(bitnr, u32)) ||
+ (!set && !hfs_clear_bit(bitnr, u32))) {
+ hfs_warn("hfs_bnode_bitop: bitmap corruption.\n");
+ return -1;
+ }
+ hfs_buffer_dirty(bn->buf);
+
+ /* adjust the free count */
+ tree->bthFree += (set ? -1 : 1);
+ tree->dirt = 1;
+
+ return 0;
+}
+
+/*
+ * hfs_bnode_alloc()
+ *
+ * Description:
+ * Find a cleared bit in the B-tree node bitmap of the hfs filesystem,
+ * set it and return the corresponding bnode, with its contents zeroed.
+ * When there is no free bnode in the tree, an error is returned, no
+ * new nodes will be added by this function!
+ * Input Variable(s):
+ * struct hfs_btree *tree: Pointer to a B-tree
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * struct hfs_bnode_ref for the new bnode
+ * Preconditions:
+ * 'tree' points to a "valid" (struct hfs_btree)
+ * There is at least one free bnode.
+ * Postconditions:
+ * On success:
+ * The corresponding bit in the btree bitmap is set.
+ * The number of free nodes in the btree is decremented by one.
+ * The node is not read from disk, nor added to the bnode cache.
+ * The 'sticky' field is uninitialized.
+ */
+struct hfs_bnode_ref hfs_bnode_alloc(struct hfs_btree *tree)
+{
+ struct hfs_bnode *bn; /* the current bnode */
+ hfs_u32 bitnr = 0; /* which bit are we examining */
+ hfs_u16 first; /* the first clear bit in this bnode */
+ hfs_u16 start; /* the start (in bits) of the bitmap in node */
+ hfs_u16 end; /* the end (in bits) of the bitmap in node */
+ hfs_u32 *data; /* address of the data in this bnode */
+
+ bn = &tree->head;
+ for (;;) {
+ start = bnode_offset(bn, bn->ndNRecs) << 3;
+ end = bnode_offset(bn, bn->ndNRecs + 1) << 3;
+ data = (hfs_u32 *)hfs_buffer_data(bn->buf);
+
+ /* search the current node */
+ first = hfs_find_zero_bit(data, end, start);
+ if (first < end) {
+ break;
+ }
+
+ /* continue search in next map node */
+ bn = bn->next;
+
+ if (!bn) {
+ hfs_warn("hfs_bnode_alloc: too few map nodes.\n");
+ goto bail;
+ }
+ bitnr += (end - start);
+ }
+
+ if ((bitnr += (first - start)) >= tree->bthNNodes) {
+ hfs_warn("hfs_bnode_alloc: no free nodes found, "
+ "count wrong?\n");
+ goto bail;
+ }
+
+ if (hfs_set_bit(first % 32, data + (first>>5))) {
+ hfs_warn("hfs_bnode_alloc: bitmap corruption.\n");
+ goto bail;
+ }
+ hfs_buffer_dirty(bn->buf);
+
+ /* decrement the free count */
+ --tree->bthFree;
+ tree->dirt = 1;
+
+ return hfs_bnode_init(tree, bitnr);
+
+bail:
+ return (struct hfs_bnode_ref){NULL, HFS_LOCK_NONE};
+}
+
+/*
+ * hfs_btree_extend()
+ *
+ * Description:
+ * Adds nodes to a B*-tree if possible.
+ * Input Variable(s):
+ * struct hfs_btree *tree: the btree to add nodes to.
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * void
+ * Preconditions:
+ * 'tree' is a valid (struct hfs_btree *).
+ * Postconditions:
+ * If possible the number of nodes indicated by the tree's clumpsize
+ * have been added to the tree, updating all in-core and on-disk
+ * allocation information.
+ * If insufficient disk-space was available then fewer nodes may have
+ * been added than would be expected based on the clumpsize.
+ * In the case of the extents B*-tree this function will add fewer
+ * nodes than expected if adding more would result in an extent
+ * record for the extents tree being added to the extents tree.
+ * The situation could be dealt with, but doing so confuses Macs.
+ */
+void hfs_btree_extend(struct hfs_btree *tree)
+{
+ struct hfs_bnode_ref head;
+ struct hfs_bnode *bn, *tmp;
+ struct hfs_cat_entry *entry = &tree->entry;
+ struct hfs_mdb *mdb = entry->mdb;
+ hfs_u32 old_nodes, new_nodes, total_nodes, new_mapnodes, seen;
+
+ old_nodes = entry->u.file.data_fork.psize;
+
+ entry->u.file.data_fork.lsize += 1; /* rounded up to clumpsize */
+ hfs_extent_adj(&entry->u.file.data_fork);
+
+ total_nodes = entry->u.file.data_fork.psize;
+ entry->u.file.data_fork.lsize = total_nodes << HFS_SECTOR_SIZE_BITS;
+ new_nodes = total_nodes - old_nodes;
+ if (!new_nodes) {
+ return;
+ }
+
+ head = hfs_bnode_find(tree, 0, HFS_LOCK_WRITE);
+ if (!(bn = head.bn)) {
+ hfs_warn("hfs_btree_extend: header node not found.\n");
+ return;
+ }
+
+ seen = 0;
+ new_mapnodes = 0;
+ for (;;) {
+ seen += bnode_rsize(bn, bn->ndNRecs) << 3;
+
+ if (seen >= total_nodes) {
+ break;
+ }
+
+ if (!bn->next) {
+ tmp = init_mapnode(bn, seen);
+ if (!tmp) {
+ hfs_warn("hfs_btree_extend: "
+ "can't build mapnode.\n");
+ hfs_bnode_relse(&head);
+ return;
+ }
+ ++new_mapnodes;
+ }
+ bn = bn->next;
+ }
+ hfs_bnode_relse(&head);
+
+ tree->bthNNodes = total_nodes;
+ tree->bthFree += (new_nodes - new_mapnodes);
+ tree->dirt = 1;
+
+ /* write the backup MDB, not returning until it is written */
+ hfs_mdb_commit(mdb, 1);
+
+ return;
+}
+
+/*
+ * hfs_bnode_free()
+ *
+ * Remove a node from the cache and mark it free in the bitmap.
+ */
+int hfs_bnode_free(struct hfs_bnode_ref *bnr)
+{
+ hfs_u32 node = bnr->bn->node;
+ struct hfs_btree *tree = bnr->bn->tree;
+
+ if (bnr->bn->count != 1) {
+ hfs_warn("hfs_bnode_free: count != 1.\n");
+ return -EIO;
+ }
+
+ hfs_bnode_relse(bnr);
+ hfs_bnode_bitop(tree, node, 0);
+ return 0;
+}
diff --git a/fs/hfs/bdelete.c b/fs/hfs/bdelete.c
new file mode 100644
index 00000000000000..aec139476d6f31
--- /dev/null
+++ b/fs/hfs/bdelete.c
@@ -0,0 +1,488 @@
+/*
+ * linux/fs/hfs/bdelete.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU General Public License.
+ *
+ * This file contains the code to delete records in a B-tree.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs_btree.h"
+
+/*================ Variable-like macros ================*/
+
+#define FULL (HFS_SECTOR_SIZE - sizeof(struct NodeDescriptor))
+#define NO_SPACE (HFS_SECTOR_SIZE+1)
+
+/*================ File-local functions ================*/
+
+/*
+ * bdelete_nonempty()
+ *
+ * Description:
+ * Deletes a record from a given bnode without regard to it becoming empty.
+ * Input Variable(s):
+ * struct hfs_brec* brec: pointer to the brec for the deletion
+ * struct hfs_belem* belem: which node in 'brec' to delete from
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * void
+ * Preconditions:
+ * 'brec' points to a valid (struct hfs_brec).
+ * 'belem' points to a valid (struct hfs_belem) in 'brec'.
+ * Postconditions:
+ * The record has been inserted in the position indicated by 'brec'.
+ */
+static void bdelete_nonempty(struct hfs_brec *brec, struct hfs_belem *belem)
+{
+ int i, rec, nrecs, tomove;
+ hfs_u16 size;
+ hfs_u8 *start;
+ struct hfs_bnode *bnode = belem->bnr.bn;
+
+ rec = belem->record;
+ nrecs = bnode->ndNRecs;
+ size = bnode_rsize(bnode, rec);
+ tomove = bnode_offset(bnode, nrecs+1) - bnode_offset(bnode, rec+1);
+
+ /* adjust the record table */
+ for (i = rec+1; i <= nrecs; ++i) {
+ hfs_put_hs(bnode_offset(bnode,i+1) - size, RECTBL(bnode,i));
+ }
+
+ /* move it down */
+ start = bnode_key(bnode, rec);
+ memmove(start, start + size, tomove);
+
+ /* update record count */
+ --bnode->ndNRecs;
+}
+
+/*
+ * del_root()
+ *
+ * Description:
+ * Delete the current root bnode.
+ * Input Variable(s):
+ * struct hfs_bnode_ref *root: reference to the root bnode
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * int: 0 on success, error code on failure
+ * Preconditions:
+ * 'root' refers to the root bnode with HFS_LOCK_WRITE access.
+ * None of 'root's children are held with HFS_LOCK_WRITE access.
+ * Postconditions:
+ * The current 'root' node is removed from the tree and the depth
+ * of the tree is reduced by one.
+ * If 'root' is an index node with exactly one child, then that
+ * child becomes the new root of the tree.
+ * If 'root' is an empty leaf node the tree becomes empty.
+ * Upon return access to 'root' is relinquished.
+ */
+static int del_root(struct hfs_bnode_ref *root)
+{
+ struct hfs_btree *tree = root->bn->tree;
+ struct hfs_bnode_ref child;
+ hfs_u32 node;
+
+ if (root->bn->ndNRecs > 1) {
+ return 0;
+ } else if (root->bn->ndNRecs == 0) {
+ /* tree is empty */
+ tree->bthRoot = 0;
+ tree->root = NULL;
+ tree->bthRoot = 0;
+ tree->bthFNode = 0;
+ tree->bthLNode = 0;
+ --tree->bthDepth;
+ tree->dirt = 1;
+ if (tree->bthDepth) {
+ hfs_warn("hfs_bdelete: empty tree with bthDepth=%d\n",
+ tree->bthDepth);
+ goto bail;
+ }
+ return hfs_bnode_free(root);
+ } else if (root->bn->ndType == ndIndxNode) {
+ /* tree is non-empty */
+ node = hfs_get_hl(bkey_record(bnode_datastart(root->bn)));
+
+ child = hfs_bnode_find(tree, node, HFS_LOCK_READ);
+ if (!child.bn) {
+ hfs_warn("hfs_bdelete: can't read child node.\n");
+ goto bail;
+ }
+
+ child.bn->sticky = HFS_STICKY;
+ if (child.bn->next) {
+ child.bn->next->prev = child.bn->prev;
+ }
+ if (child.bn->prev) {
+ child.bn->prev->next = child.bn->next;
+ }
+ if (bhash(tree, child.bn->node) == child.bn) {
+ bhash(tree, child.bn->node) = child.bn->next;
+ }
+ child.bn->next = NULL;
+ child.bn->prev = NULL;
+
+ tree->bthRoot = child.bn->node;
+ tree->root = child.bn;
+
+ /* re-assign bthFNode and bthLNode if the new root is
+ a leaf node. */
+ if (child.bn->ndType == ndLeafNode) {
+ tree->bthFNode = node;
+ tree->bthLNode = node;
+ }
+ hfs_bnode_relse(&child);
+
+ tree->bthRoot = node;
+ --tree->bthDepth;
+ tree->dirt = 1;
+ if (!tree->bthDepth) {
+ hfs_warn("hfs_bdelete: non-empty tree with "
+ "bthDepth == 0\n");
+ goto bail;
+ }
+ return hfs_bnode_free(root); /* marks tree dirty */
+ }
+ hfs_bnode_relse(root);
+ return 0;
+
+bail:
+ hfs_bnode_relse(root);
+ return -EIO;
+}
+
+
+/*
+ * delete_empty_bnode()
+ *
+ * Description:
+ * Removes an empty non-root bnode from between 'left' and 'right'
+ * Input Variable(s):
+ * hfs_u32 left_node: node number of 'left' or zero if 'left' is invalid
+ * struct hfs_bnode_ref *left: reference to the left neighbor of the
+ * bnode to remove, or invalid if no such neighbor exists.
+ * struct hfs_bnode_ref *center: reference to the bnode to remove
+ * hfs_u32 right_node: node number of 'right' or zero if 'right' is invalid
+ * struct hfs_bnode_ref *right: reference to the right neighbor of the
+ * bnode to remove, or invalid if no such neighbor exists.
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * void
+ * Preconditions:
+ * 'left_node' is as described above.
+ * 'left' points to a valid (struct hfs_bnode_ref) having HFS_LOCK_WRITE
+ * access and referring to the left neighbor of 'center' if such a
+ * neighbor exists, or invalid if no such neighbor exists.
+ * 'center' points to a valid (struct hfs_bnode_ref) having HFS_LOCK_WRITE
+ * access and referring to the bnode to delete.
+ * 'right_node' is as described above.
+ * 'right' points to a valid (struct hfs_bnode_ref) having HFS_LOCK_WRITE
+ * access and referring to the right neighbor of 'center' if such a
+ * neighbor exists, or invalid if no such neighbor exists.
+ * Postconditions:
+ * If 'left' is valid its 'ndFLink' field becomes 'right_node'.
+ * If 'right' is valid its 'ndBLink' field becomes 'left_node'.
+ * If 'center' was the first leaf node then the tree's 'bthFNode'
+ * field becomes 'right_node'
+ * If 'center' was the last leaf node then the tree's 'bthLNode'
+ * field becomes 'left_node'
+ * 'center' is NOT freed and access to the nodes is NOT relinquished.
+ */
+static void delete_empty_bnode(hfs_u32 left_node, struct hfs_bnode_ref *left,
+ struct hfs_bnode_ref *center,
+ hfs_u32 right_node, struct hfs_bnode_ref *right)
+{
+ struct hfs_bnode *bnode = center->bn;
+
+ if (left_node) {
+ left->bn->ndFLink = right_node;
+ } else if (bnode->ndType == ndLeafNode) {
+ bnode->tree->bthFNode = right_node;
+ bnode->tree->dirt = 1;
+ }
+
+ if (right_node) {
+ right->bn->ndBLink = left_node;
+ } else if (bnode->ndType == ndLeafNode) {
+ bnode->tree->bthLNode = left_node;
+ bnode->tree->dirt = 1;
+ }
+}
+
+/*
+ * balance()
+ *
+ * Description:
+ * Attempt to equalize space usage in neighboring bnodes.
+ * Input Variable(s):
+ * struct hfs_bnode *left: the left bnode.
+ * struct hfs_bnode *right: the right bnode.
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * void
+ * Preconditions:
+ * 'left' and 'right' point to valid (struct hfs_bnode)s obtained
+ * with HFS_LOCK_WRITE access, and are neighbors.
+ * Postconditions:
+ * Records are shifted either left or right to make the space usage
+ * nearly equal. When exact equality is not possible the break
+ * point is chosen to reduce data movement.
+ * The key corresponding to 'right' in its parent is NOT updated.
+ */
+static void balance(struct hfs_bnode *left, struct hfs_bnode *right)
+{
+ int index, left_free, right_free, half;
+
+ left_free = bnode_freespace(left);
+ right_free = bnode_freespace(right);
+ half = (left_free + right_free)/2;
+
+ if (left_free < right_free) {
+ /* shift right to balance */
+ index = left->ndNRecs + 1;
+ while (right_free >= half) {
+ --index;
+ right_free -= bnode_rsize(left,index)+sizeof(hfs_u16);
+ }
+ if (index < left->ndNRecs) {
+#if defined(DEBUG_ALL) || defined(DEBUG_BALANCE)
+ hfs_warn("shifting %d of %d recs right to balance: ",
+ left->ndNRecs - index, left->ndNRecs);
+#endif
+ hfs_bnode_shift_right(left, right, index+1);
+#if defined(DEBUG_ALL) || defined(DEBUG_BALANCE)
+ hfs_warn("%d,%d\n", left->ndNRecs, right->ndNRecs);
+#endif
+ }
+ } else {
+ /* shift left to balance */
+ index = 0;
+ while (left_free >= half) {
+ ++index;
+ left_free -= bnode_rsize(right,index)+sizeof(hfs_u16);
+ }
+ if (index > 1) {
+#if defined(DEBUG_ALL) || defined(DEBUG_BALANCE)
+ hfs_warn("shifting %d of %d recs left to balance: ",
+ index-1, right->ndNRecs);
+#endif
+ hfs_bnode_shift_left(left, right, index-1);
+#if defined(DEBUG_ALL) || defined(DEBUG_BALANCE)
+ hfs_warn("%d,%d\n", left->ndNRecs, right->ndNRecs);
+#endif
+ }
+ }
+}
+
+/*
+ * bdelete()
+ *
+ * Delete the given record from a B-tree.
+ */
+static int bdelete(struct hfs_brec *brec)
+{
+ struct hfs_btree *tree = brec->tree;
+ struct hfs_belem *belem = brec->bottom;
+ struct hfs_belem *parent = (belem-1);
+ struct hfs_bnode *bnode;
+ hfs_u32 left_node, right_node;
+ struct hfs_bnode_ref left, right;
+ int left_space, right_space, min_space;
+ int fix_right_key;
+ int fix_key;
+
+ while ((belem > brec->top) &&
+ (belem->flags & (HFS_BPATH_UNDERFLOW | HFS_BPATH_FIRST))) {
+ bnode = belem->bnr.bn;
+ fix_key = belem->flags & HFS_BPATH_FIRST;
+ fix_right_key = 0;
+
+ bdelete_nonempty(brec, belem);
+
+ if (bnode->node == tree->root->node) {
+ del_root(&belem->bnr);
+ --brec->bottom;
+ goto done;
+ }
+
+ /* check for btree corruption which could lead to deadlock */
+ left_node = bnode->ndBLink;
+ right_node = bnode->ndFLink;
+ if ((left_node && hfs_bnode_in_brec(left_node, brec)) ||
+ (right_node && hfs_bnode_in_brec(right_node, brec)) ||
+ (left_node == right_node)) {
+ hfs_warn("hfs_bdelete: corrupt btree\n");
+ hfs_brec_relse(brec, NULL);
+ return -EIO;
+ }
+
+ /* grab the left neighbor if it exists */
+ if (left_node) {
+ hfs_bnode_lock(&belem->bnr, HFS_LOCK_RESRV);
+ left = hfs_bnode_find(tree,left_node,HFS_LOCK_WRITE);
+ if (!left.bn) {
+ hfs_warn("hfs_bdelete: unable to read left "
+ "neighbor.\n");
+ hfs_brec_relse(brec, NULL);
+ return -EIO;
+ }
+ hfs_bnode_lock(&belem->bnr, HFS_LOCK_WRITE);
+ if (parent->record != 1) {
+ left_space = bnode_freespace(left.bn);
+ } else {
+ left_space = NO_SPACE;
+ }
+ } else {
+ left.bn = NULL;
+ left_space = NO_SPACE;
+ }
+
+ /* grab the right neighbor if it exists */
+ if (right_node) {
+ right = hfs_bnode_find(tree,right_node,HFS_LOCK_WRITE);
+ if (!right.bn) {
+ hfs_warn("hfs_bdelete: unable to read right "
+ "neighbor.\n");
+ hfs_bnode_relse(&left);
+ hfs_brec_relse(brec, NULL);
+ return -EIO;
+ }
+ if (parent->record < parent->bnr.bn->ndNRecs) {
+ right_space = bnode_freespace(right.bn);
+ } else {
+ right_space = NO_SPACE;
+ }
+ } else {
+ right.bn = NULL;
+ right_space = NO_SPACE;
+ }
+
+ if (left_space < right_space) {
+ min_space = left_space;
+ } else {
+ min_space = right_space;
+ }
+
+ if (min_space == NO_SPACE) {
+ hfs_warn("hfs_bdelete: no siblings?\n");
+ hfs_brec_relse(brec, NULL);
+ return -EIO;
+ }
+
+ if (bnode->ndNRecs == 0) {
+ delete_empty_bnode(left_node, &left, &belem->bnr,
+ right_node, &right);
+ } else if (min_space + bnode_freespace(bnode) >= FULL) {
+ if ((right_space == NO_SPACE) ||
+ ((right_space == min_space) &&
+ (left_space != NO_SPACE))) {
+ hfs_bnode_shift_left(left.bn, bnode,
+ bnode->ndNRecs);
+ } else {
+ hfs_bnode_shift_right(bnode, right.bn, 1);
+ fix_right_key = 1;
+ }
+ delete_empty_bnode(left_node, &left, &belem->bnr,
+ right_node, &right);
+ } else if (min_space == right_space) {
+ balance(bnode, right.bn);
+ fix_right_key = 1;
+ } else {
+ balance(left.bn, bnode);
+ fix_key = 1;
+ }
+
+ if (fix_right_key) {
+ hfs_bnode_update_key(brec, belem, right.bn, 1);
+ }
+
+ hfs_bnode_relse(&left);
+ hfs_bnode_relse(&right);
+
+ if (bnode->ndNRecs) {
+ if (fix_key) {
+ hfs_bnode_update_key(brec, belem, bnode, 0);
+ }
+ goto done;
+ }
+
+ hfs_bnode_free(&belem->bnr);
+ --brec->bottom;
+ belem = parent;
+ --parent;
+ }
+
+ if (belem < brec->top) {
+ hfs_warn("hfs_bdelete: Missing parent.\n");
+ hfs_brec_relse(brec, NULL);
+ return -EIO;
+ }
+
+ bdelete_nonempty(brec, belem);
+
+done:
+ hfs_brec_relse(brec, NULL);
+ return 0;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_bdelete()
+ *
+ * Delete the requested record from a B-tree.
+ */
+int hfs_bdelete(struct hfs_btree *tree, const struct hfs_bkey *key)
+{
+ struct hfs_belem *belem;
+ struct hfs_bnode *bnode;
+ struct hfs_brec brec;
+ int retval;
+
+ if (!tree || (tree->magic != HFS_BTREE_MAGIC) || !key) {
+ hfs_warn("hfs_bdelete: invalid arguments.\n");
+ return -EINVAL;
+ }
+
+ retval = hfs_bfind(&brec, tree, key, HFS_BFIND_DELETE);
+ if (!retval) {
+ belem = brec.bottom;
+ bnode = belem->bnr.bn;
+
+ belem->flags = 0;
+ if ((bnode->ndNRecs * sizeof(hfs_u16) + bnode_end(bnode) -
+ bnode_rsize(bnode, belem->record)) < FULL/2) {
+ belem->flags |= HFS_BPATH_UNDERFLOW;
+ }
+ if (belem->record == 1) {
+ belem->flags |= HFS_BPATH_FIRST;
+ }
+
+ if (!belem->flags) {
+ hfs_brec_lock(&brec, brec.bottom);
+ } else {
+ hfs_brec_lock(&brec, NULL);
+ }
+
+ retval = bdelete(&brec);
+ if (!retval) {
+ --brec.tree->bthNRecs;
+ brec.tree->dirt = 1;
+ }
+ hfs_brec_relse(&brec, NULL);
+ }
+ return retval;
+}
diff --git a/fs/hfs/bfind.c b/fs/hfs/bfind.c
new file mode 100644
index 00000000000000..8e84133d20bd9a
--- /dev/null
+++ b/fs/hfs/bfind.c
@@ -0,0 +1,322 @@
+/*
+ * linux/fs/hfs/bfind.c
+ *
+ * Copyright (C) 1995, 1996 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU General Public License.
+ *
+ * This file contains the code to access records in a btree.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs_btree.h"
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_brec_relse()
+ *
+ * Description:
+ * This function releases some of the nodes associated with a brec.
+ * Input Variable(s):
+ * struct hfs_brec *brec: pointer to the brec to release some nodes from.
+ * struct hfs_belem *elem: the last node to release or NULL for all
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * void
+ * Preconditions:
+ * 'brec' points to a "valid" (struct hfs_brec)
+ * Postconditions:
+ * All nodes between the indicated node and the beginning of the path
+ * are released.
+ */
+void hfs_brec_relse(struct hfs_brec *brec, struct hfs_belem *elem)
+{
+ if (!elem) {
+ elem = brec->bottom;
+ }
+
+ while (brec->top <= elem) {
+ hfs_bnode_relse(&brec->top->bnr);
+ ++brec->top;
+ }
+}
+
+/*
+ * hfs_bfind()
+ *
+ * Description:
+ * This function has sole responsibility for locating existing
+ * records in a B-tree. Given a B-tree and a key it locates the
+ * "greatest" record "less than or equal to" the given key. The
+ * exact behavior is determined by the bits of the flags variable as
+ * follows:
+ * ('flags' & HFS_LOCK_MASK):
+ * The lock_type argument to be used when calling hfs_bnode_find().
+ * HFS_BFIND_EXACT: only accept an exact match, otherwise take the
+ * "largest" record less than 'target' as a "match"
+ * HFS_BFIND_LOCK: request HFS_LOCK_WRITE access to the node containing
+ * the "matching" record when it is located
+ * HFS_BPATH_FIRST: keep access to internal nodes when accessing their
+ * first child.
+ * HFS_BPATH_OVERFLOW: keep access to internal nodes when the accessed
+ * child is too full to insert another pointer record.
+ * HFS_BPATH_UNDERFLOW: keep access to internal nodes when the accessed
+ * child is would be less than half full upon removing a pointer record.
+ * Input Variable(s):
+ * struct hfs_brec *brec: pointer to the (struct hfs_brec) to hold
+ * the search results.
+ * struct hfs_bkey *target: pointer to the (struct hfs_bkey)
+ * to search for
+ * int flags: bitwise OR of flags which determine the function's behavior
+ * Output Variable(s):
+ * 'brec' contains the results of the search on success or is invalid
+ * on failure.
+ * Returns:
+ * int: 0 or 1 on success or an error code on failure:
+ * -EINVAL: one of the input variables was NULL.
+ * -ENOENT: tree is valid but empty or no "matching" record was located.
+ * If the HFS_BFIND_EXACT bit of 'flags' is not set then the case of no
+ * matching record will give a 'brec' with a 'record' field of zero
+ * rather than returning this error.
+ * -EIO: an I/O operation or an assertion about the structure of a
+ * valid B-tree failed indicating corruption of either the B-tree
+ * structure on the disk or one of the in-core structures representing
+ * the B-tree.
+ * (This could also be returned if a kmalloc() call failed in a
+ * subordinate routine that is intended to get the data from the
+ * disk or the buffer cache.)
+ * Preconditions:
+ * 'brec' is NULL or points to a (struct hfs_brec) with a 'tree' field
+ * which points to a valid (struct hfs_btree).
+ * 'target' is NULL or points to a "valid" (struct hfs_bkey)
+ * Postconditions:
+ * If 'brec', 'brec->tree' or 'target' is NULL then -EINVAL is returned.
+ * If 'brec', 'brec->tree' and 'target' are non-NULL but the tree
+ * is empty then -ENOENT is returned.
+ * If 'brec', 'brec->tree' and 'target' are non-NULL but the call to
+ * hfs_brec_init() fails then '*brec' is NULL and -EIO is returned.
+ * If 'brec', 'brec->tree' and 'target' are non-NULL and the tree is
+ * non-empty then the tree is searched as follows:
+ * If any call to hfs_brec_next() fails or returns a node that is
+ * neither an index node nor a leaf node then -EIO is returned to
+ * indicate that the B-tree or buffer-cache are corrupted.
+ * If every record in the tree is "greater than" the given key
+ * and the HFS_BFIND_EXACT bit of 'flags' is set then -ENOENT is returned.
+ * If every record in the tree is "greater than" the given key
+ * and the HFS_BFIND_EXACT bit of 'flags' is clear then 'brec' refers
+ * to the first leaf node in the tree and has a 'record' field of
+ * zero, and 1 is returned.
+ * If a "matching" record is located with key "equal to" 'target'
+ * then the return value is 0 and 'brec' indicates the record.
+ * If a "matching" record is located with key "greater than" 'target'
+ * then the behavior is determined as follows:
+ * If the HFS_BFIND_EXACT bit of 'flags' is not set then 1 is returned
+ * and 'brec' refers to the "matching" record.
+ * If the HFS_BFIND_EXACT bit of 'flags' is set then -ENOENT is returned.
+ * If the return value is non-negative and the HFS_BFIND_LOCK bit of
+ * 'flags' is set then hfs_brec_lock() is called on the bottom element
+ * of 'brec' before returning.
+ */
+int hfs_bfind(struct hfs_brec *brec, struct hfs_btree *tree,
+ const struct hfs_bkey *target, int flags)
+{
+ struct hfs_belem *curr;
+ struct hfs_bkey *key;
+ struct hfs_bnode *bn;
+ int result, ntype;
+
+ /* check for invalid arguments */
+ if (!brec || (tree->magic != HFS_BTREE_MAGIC) || !target) {
+ return -EINVAL;
+ }
+
+ /* check for empty tree */
+ if (!tree->root || !tree->bthNRecs) {
+ return -ENOENT;
+ }
+
+ /* start search at root of tree */
+ if (!(curr = hfs_brec_init(brec, tree, flags))) {
+ return -EIO;
+ }
+
+ /* traverse the tree */
+ do {
+ bn = curr->bnr.bn;
+
+ if (!curr->record) {
+ hfs_warn("hfs_bfind: empty bnode\n");
+ hfs_brec_relse(brec, NULL);
+ return -EIO;
+ }
+
+ /* reverse linear search yielding largest key "less
+ than or equal to" 'target'.
+ It is questionable whether a binary search would be
+ significantly faster */
+ do {
+ key = belem_key(curr);
+ if (!key->KeyLen) {
+ hfs_warn("hfs_bfind: empty key\n");
+ hfs_brec_relse(brec, NULL);
+ return -EIO;
+ }
+ result = (tree->compare)(target, key);
+ } while ((result<0) && (--curr->record));
+
+ ntype = bn->ndType;
+
+ /* see if all keys > target */
+ if (!curr->record) {
+ if (bn->ndBLink) {
+ /* at a node other than the left-most at a
+ given level it means the parent had an
+ incorrect key for this child */
+ hfs_brec_relse(brec, NULL);
+ hfs_warn("hfs_bfind: corrupted b-tree %d.\n",
+ (int)ntohl(tree->entry.cnid));
+ return -EIO;
+ }
+ if (flags & HFS_BFIND_EXACT) {
+ /* we're not going to find it */
+ hfs_brec_relse(brec, NULL);
+ return -ENOENT;
+ }
+ if (ntype == ndIndxNode) {
+ /* since we are at the left-most node at
+ the current level and looking for the
+ predecessor of 'target' keep going down */
+ curr->record = 1;
+ } else {
+ /* we're at first leaf so fall through */
+ }
+ }
+
+ /* get next node if necessary */
+ if ((ntype == ndIndxNode) && !(curr = hfs_brec_next(brec))) {
+ return -EIO;
+ }
+ } while (ntype == ndIndxNode);
+
+ if (key->KeyLen > tree->bthKeyLen) {
+ hfs_warn("hfs_bfind: oversized key\n");
+ hfs_brec_relse(brec, NULL);
+ return -EIO;
+ }
+
+ if (ntype != ndLeafNode) {
+ hfs_warn("hfs_bfind: invalid node type %02x in node %d of "
+ "btree %d\n", bn->ndType, bn->node,
+ (int)ntohl(tree->entry.cnid));
+ hfs_brec_relse(brec, NULL);
+ return -EIO;
+ }
+
+ if ((flags & HFS_BFIND_EXACT) && result) {
+ hfs_brec_relse(brec, NULL);
+ return -ENOENT;
+ }
+
+ if (!(flags & HFS_BPATH_MASK)) {
+ hfs_brec_relse(brec, brec->bottom-1);
+ }
+
+ if (flags & HFS_BFIND_LOCK) {
+ hfs_brec_lock(brec, brec->bottom);
+ }
+
+ brec->key = brec_key(brec);
+ brec->data = bkey_record(brec->key);
+
+ return result ? 1 : 0;
+}
+
+/*
+ * hfs_bsucc()
+ *
+ * Description:
+ * This function overwrites '*brec' with its successor in the B-tree,
+ * obtaining the same type of access.
+ * Input Variable(s):
+ * struct hfs_brec *brec: address of the (struct hfs_brec) to overwrite
+ * with its successor
+ * Output Variable(s):
+ * struct hfs_brec *brec: address of the successor of the original
+ * '*brec' or to invalid data
+ * Returns:
+ * int: 0 on success, or one of -EINVAL, -EIO, or -EINVAL on failure
+ * Preconditions:
+ * 'brec' pointers to a "valid" (struct hfs_brec)
+ * Postconditions:
+ * If the given '*brec' is not "valid" -EINVAL is returned and
+ * '*brec' is unchanged.
+ * If the given 'brec' is "valid" but has no successor then -ENOENT
+ * is returned and '*brec' is invalid.
+ * If a call to hfs_bnode_find() is necessary to find the successor,
+ * but fails then -EIO is returned and '*brec' is invalid.
+ * If none of the three previous conditions prevents finding the
+ * successor of '*brec', then 0 is returned, and '*brec' is overwritten
+ * with the (struct hfs_brec) for its successor.
+ * In the cases when '*brec' is invalid, the old records is freed.
+ */
+int hfs_bsucc(struct hfs_brec *brec, int count)
+{
+ struct hfs_belem *belem;
+ struct hfs_bnode *bn;
+
+ if (!brec || !(belem = brec->bottom) || (belem != brec->top) ||
+ !(bn = belem->bnr.bn) || (bn->magic != HFS_BNODE_MAGIC) ||
+ !bn->tree || (bn->tree->magic != HFS_BTREE_MAGIC) ||
+ !hfs_buffer_ok(bn->buf)) {
+ hfs_warn("hfs_bsucc: invalid/corrupt arguments.\n");
+ return -EINVAL;
+ }
+
+ while (count) {
+ int left = bn->ndNRecs - belem->record;
+
+ if (left < count) {
+ struct hfs_bnode_ref old;
+ hfs_u32 node;
+
+ /* Advance to next node */
+ if (!(node = bn->ndFLink)) {
+ hfs_brec_relse(brec, belem);
+ return -ENOENT;
+ }
+ if (node == bn->node) {
+ hfs_warn("hfs_bsucc: corrupt btree\n");
+ hfs_brec_relse(brec, belem);
+ return -EIO;
+ }
+ old = belem->bnr;
+ belem->bnr = hfs_bnode_find(brec->tree, node,
+ belem->bnr.lock_type);
+ hfs_bnode_relse(&old);
+ if (!(bn = belem->bnr.bn)) {
+ return -EIO;
+ }
+ belem->record = 1;
+ count -= (left + 1);
+ } else {
+ belem->record += count;
+ break;
+ }
+ }
+ brec->key = belem_key(belem);
+ brec->data = bkey_record(brec->key);
+
+ if (brec->key->KeyLen > brec->tree->bthKeyLen) {
+ hfs_warn("hfs_bsucc: oversized key\n");
+ hfs_brec_relse(brec, NULL);
+ return -EIO;
+ }
+
+ return 0;
+}
diff --git a/fs/hfs/bins_del.c b/fs/hfs/bins_del.c
new file mode 100644
index 00000000000000..a03b959e3f126a
--- /dev/null
+++ b/fs/hfs/bins_del.c
@@ -0,0 +1,231 @@
+/*
+ * linux/fs/hfs/bins_del.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU General Public License.
+ *
+ * This file contains the code common to inserting and deleting records
+ * in a B-tree.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs_btree.h"
+
+/*================ File-local functions ================*/
+
+/*
+ * hfs_bnode_update_key()
+ *
+ * Description:
+ * Updates the key for a bnode in its parent.
+ * The key change is propagated up the tree as necessary.
+ * Input Variable(s):
+ * struct hfs_brec *brec: the search path to update keys in
+ * struct hfs_belem *belem: the search path element with the changed key
+ * struct hfs_bnode *bnode: the bnode with the changed key
+ * int offset: the "distance" from 'belem->bn' to 'bnode':
+ * 0 if the change is in 'belem->bn',
+ * 1 if the change is in its right sibling, etc.
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * void
+ * Preconditions:
+ * 'brec' points to a valid (struct hfs_brec)
+ * 'belem' points to a valid (struct hfs_belem) in 'brec'.
+ * 'bnode' points to a valid (struct hfs_bnode) which is non-empty
+ * and is 'belem->bn' or one of its siblings.
+ * 'offset' is as described above.
+ * Postconditions:
+ * The key change is propagated up the tree as necessary.
+ */
+void hfs_bnode_update_key(struct hfs_brec *brec, struct hfs_belem *belem,
+ struct hfs_bnode *bnode, int offset)
+{
+ int record = (--belem)->record + offset;
+ void *key = bnode_datastart(bnode) + 1;
+ int keysize = brec->tree->bthKeyLen;
+ struct hfs_belem *limit;
+
+ memcpy(1+bnode_key(belem->bnr.bn, record), key, keysize);
+
+ /* don't trash the header */
+ if (brec->top > &brec->elem[1]) {
+ limit = brec->top;
+ } else {
+ limit = &brec->elem[1];
+ }
+
+ while ((belem > limit) && (record == 1)) {
+ record = (--belem)->record;
+ memcpy(1+belem_key(belem), key, keysize);
+ }
+}
+
+/*
+ * hfs_bnode_shift_right()
+ *
+ * Description:
+ * Shifts some records from a node to its right neighbor.
+ * Input Variable(s):
+ * struct hfs_bnode* left: the node to shift records from
+ * struct hfs_bnode* right: the node to shift records to
+ * hfs_u16 first: the number of the first record in 'left' to move to 'right'
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * void
+ * Preconditions:
+ * 'left' and 'right' point to valid (struct hfs_bnode)s.
+ * 'left' contains at least 'first' records.
+ * 'right' has enough free space to hold the records to be moved from 'left'
+ * Postconditions:
+ * The record numbered 'first' and all records after it in 'left' are
+ * placed at the beginning of 'right'.
+ * The key corresponding to 'right' in its parent is NOT updated.
+ */
+void hfs_bnode_shift_right(struct hfs_bnode *left, struct hfs_bnode *right,
+ int first)
+{
+ int i, adjust, nrecs;
+ unsigned size;
+ hfs_u16 *to, *from;
+
+ if ((first <= 0) || (first > left->ndNRecs)) {
+ hfs_warn("bad argument to shift_right: first=%d, nrecs=%d\n",
+ first, left->ndNRecs);
+ return;
+ }
+
+ /* initialize variables */
+ nrecs = left->ndNRecs + 1 - first;
+ size = bnode_end(left) - bnode_offset(left, first);
+
+ /* move (possibly empty) contents of right node forward */
+ memmove(bnode_datastart(right) + size,
+ bnode_datastart(right),
+ bnode_end(right) - sizeof(struct NodeDescriptor));
+
+ /* copy in new records */
+ memcpy(bnode_datastart(right), bnode_key(left,first), size);
+
+ /* fix up offsets in right node */
+ i = right->ndNRecs + 1;
+ from = RECTBL(right, i);
+ to = from - nrecs;
+ while (i--) {
+ hfs_put_hs(hfs_get_hs(from++) + size, to++);
+ }
+ adjust = sizeof(struct NodeDescriptor) - bnode_offset(left, first);
+ i = nrecs-1;
+ from = RECTBL(left, first+i);
+ while (i--) {
+ hfs_put_hs(hfs_get_hs(from++) + adjust, to++);
+ }
+
+ /* fix record counts */
+ left->ndNRecs -= nrecs;
+ right->ndNRecs += nrecs;
+}
+
+/*
+ * hfs_bnode_shift_left()
+ *
+ * Description:
+ * Shifts some records from a node to its left neighbor.
+ * Input Variable(s):
+ * struct hfs_bnode* left: the node to shift records to
+ * struct hfs_bnode* right: the node to shift records from
+ * hfs_u16 last: the number of the last record in 'right' to move to 'left'
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * void
+ * Preconditions:
+ * 'left' and 'right' point to valid (struct hfs_bnode)s.
+ * 'right' contains at least 'last' records.
+ * 'left' has enough free space to hold the records to be moved from 'right'
+ * Postconditions:
+ * The record numbered 'last' and all records before it in 'right' are
+ * placed at the end of 'left'.
+ * The key corresponding to 'right' in its parent is NOT updated.
+ */
+void hfs_bnode_shift_left(struct hfs_bnode *left, struct hfs_bnode *right,
+ int last)
+{
+ int i, adjust, nrecs;
+ unsigned size;
+ hfs_u16 *to, *from;
+
+ if ((last <= 0) || (last > right->ndNRecs)) {
+ hfs_warn("bad argument to shift_left: last=%d, nrecs=%d\n",
+ last, right->ndNRecs);
+ return;
+ }
+
+ /* initialize variables */
+ size = bnode_offset(right, last + 1) - sizeof(struct NodeDescriptor);
+
+ /* copy records to left node */
+ memcpy(bnode_dataend(left), bnode_datastart(right), size);
+
+ /* move (possibly empty) remainder of right node backward */
+ memmove(bnode_datastart(right), bnode_datastart(right) + size,
+ bnode_end(right) - bnode_offset(right, last + 1));
+
+ /* fix up offsets */
+ nrecs = left->ndNRecs;
+ i = last;
+ from = RECTBL(right, 2);
+ to = RECTBL(left, nrecs + 2);
+ adjust = bnode_offset(left, nrecs + 1) - sizeof(struct NodeDescriptor);
+ while (i--) {
+ hfs_put_hs(hfs_get_hs(from--) + adjust, to--);
+ }
+ i = right->ndNRecs + 1 - last;
+ ++from;
+ to = RECTBL(right, 1);
+ while (i--) {
+ hfs_put_hs(hfs_get_hs(from--) - size, to--);
+ }
+
+ /* fix record counts */
+ left->ndNRecs += last;
+ right->ndNRecs -= last;
+}
+
+/*
+ * hfs_bnode_in_brec()
+ *
+ * Description:
+ * Determines whethet a given bnode is part of a given brec.
+ * This is used to avoid deadlock in the case of a corrupted b-tree.
+ * Input Variable(s):
+ * hfs_u32 node: the number of the node to check for.
+ * struct hfs_brec* brec: the brec to check in.
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * int: 1 it found, 0 if not
+ * Preconditions:
+ * 'brec' points to a valid struct hfs_brec.
+ * Postconditions:
+ * 'brec' is unchanged.
+ */
+int hfs_bnode_in_brec(hfs_u32 node, const struct hfs_brec *brec)
+{
+ const struct hfs_belem *belem = brec->bottom;
+
+ while (belem && (belem >= brec->top)) {
+ if (belem->bnr.bn && (belem->bnr.bn->node == node)) {
+ return 1;
+ }
+ --belem;
+ }
+ return 0;
+}
diff --git a/fs/hfs/binsert.c b/fs/hfs/binsert.c
new file mode 100644
index 00000000000000..6f0853892dcb55
--- /dev/null
+++ b/fs/hfs/binsert.c
@@ -0,0 +1,551 @@
+/*
+ * linux/fs/hfs/binsert.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU General Public License.
+ *
+ * This file contains the code to insert records in a B-tree.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs_btree.h"
+
+/*================ File-local functions ================*/
+
+/* btree locking functions */
+static inline void hfs_btree_lock(struct hfs_btree *tree)
+{
+ while (tree->lock)
+ hfs_sleep_on(&tree->wait);
+ tree->lock = 1;
+}
+
+static inline void hfs_btree_unlock(struct hfs_btree *tree)
+{
+ tree->lock = 0;
+ hfs_wake_up(&tree->wait);
+}
+
+/*
+ * binsert_nonfull()
+ *
+ * Description:
+ * Inserts a record in a given bnode known to have sufficient space.
+ * Input Variable(s):
+ * struct hfs_brec* brec: pointer to the brec for the insertion
+ * struct hfs_belem* belem: the element in the search path to insert in
+ * struct hfs_bkey* key: pointer to the key for the record to insert
+ * void* data: pointer to the record to insert
+ * hfs_u16 keysize: size of the key to insert
+ * hfs_u16 datasize: size of the record to insert
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * NONE
+ * Preconditions:
+ * 'brec' points to a valid (struct hfs_brec).
+ * 'belem' points to a valid (struct hfs_belem) in 'brec', the node
+ * of which has enough free space to insert 'key' and 'data'.
+ * 'key' is a pointer to a valid (struct hfs_bkey) of length 'keysize'
+ * which, in sorted order, belongs at the location indicated by 'brec'.
+ * 'data' is non-NULL an points to appropriate data of length 'datasize'
+ * Postconditions:
+ * The record has been inserted in the position indicated by 'brec'.
+ */
+static void binsert_nonfull(struct hfs_brec *brec, struct hfs_belem *belem,
+ const struct hfs_bkey *key, const void *data,
+ hfs_u8 keysize, hfs_u16 datasize)
+{
+ int i, rec, nrecs, size, tomove;
+ hfs_u8 *start;
+ struct hfs_bnode *bnode = belem->bnr.bn;
+
+ rec = ++(belem->record);
+ size = ROUND(keysize+1) + datasize;
+ nrecs = bnode->ndNRecs + 1;
+ tomove = bnode_offset(bnode, nrecs) - bnode_offset(bnode, rec);
+
+ /* adjust the record table */
+ for (i = nrecs; i >= rec; --i) {
+ hfs_put_hs(bnode_offset(bnode,i) + size, RECTBL(bnode,i+1));
+ }
+
+ /* make room */
+ start = bnode_key(bnode, rec);
+ memmove(start + size, start, tomove);
+
+ /* copy in the key and the data*/
+ *start = keysize;
+ keysize = ROUND(keysize+1);
+ memcpy(start + 1, (hfs_u8 *)key + 1, keysize-1);
+ memcpy(start + keysize, data, datasize);
+
+ /* update record count */
+ ++bnode->ndNRecs;
+}
+
+/*
+ * add_root()
+ *
+ * Description:
+ * Adds a new root to a B*-tree, increasing its height.
+ * Input Variable(s):
+ * struct hfs_btree *tree: the tree to add a new root to
+ * struct hfs_bnode *left: the new root's first child or NULL
+ * struct hfs_bnode *right: the new root's second child or NULL
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * void
+ * Preconditions:
+ * 'tree' points to a valid (struct hfs_btree).
+ * 'left' and 'right' point to valid (struct hfs_bnode)s, which
+ * resulted from splitting the old root node, or are both NULL
+ * if there was no root node before.
+ * Postconditions:
+ * Upon success a new root node is added to 'tree' with either
+ * two children ('left' and 'right') or none.
+ */
+static void add_root(struct hfs_btree *tree,
+ struct hfs_bnode *left,
+ struct hfs_bnode *right)
+{
+ struct hfs_bnode_ref bnr;
+ struct hfs_bnode *root;
+ struct hfs_bkey *key;
+ int keylen = tree->bthKeyLen;
+
+ if (left && !right) {
+ hfs_warn("add_root: LEFT but no RIGHT\n");
+ return;
+ }
+
+ bnr = hfs_bnode_alloc(tree);
+ if (!(root = bnr.bn)) {
+ return;
+ }
+
+ root->sticky = HFS_STICKY;
+ tree->root = root;
+ tree->bthRoot = root->node;
+ ++tree->bthDepth;
+
+ root->ndNHeight = tree->bthDepth;
+ root->ndFLink = 0;
+ root->ndBLink = 0;
+
+ if (!left) {
+ /* tree was empty */
+ root->ndType = ndLeafNode;
+ root->ndNRecs = 0;
+
+ tree->bthFNode = root->node;
+ tree->bthLNode = root->node;
+ } else {
+ root->ndType = ndIndxNode;
+ root->ndNRecs = 2;
+
+ hfs_put_hs(sizeof(struct NodeDescriptor) + ROUND(1+keylen) +
+ sizeof(hfs_u32), RECTBL(root, 2));
+ key = bnode_key(root, 1);
+ key->KeyLen = keylen;
+ memcpy(key->value,
+ ((struct hfs_bkey *)bnode_key(left, 1))->value, keylen);
+ hfs_put_hl(left->node, bkey_record(key));
+
+ hfs_put_hs(sizeof(struct NodeDescriptor) + 2*ROUND(1+keylen) +
+ 2*sizeof(hfs_u32), RECTBL(root, 3));
+ key = bnode_key(root, 2);
+ key->KeyLen = keylen;
+ memcpy(key->value,
+ ((struct hfs_bkey *)bnode_key(right, 1))->value, keylen);
+ hfs_put_hl(right->node, bkey_record(key));
+
+ /* the former root (left) is now just a normal node */
+ left->sticky = HFS_NOT_STICKY;
+ if ((left->next = bhash(tree, left->node))) {
+ left->next->prev = left;
+ }
+ bhash(tree, left->node) = left;
+ }
+ hfs_bnode_relse(&bnr);
+ tree->dirt = 1;
+}
+
+/*
+ * insert_empty_bnode()
+ *
+ * Description:
+ * Adds an empty node to the right of 'left'.
+ * Input Variable(s):
+ * struct hfs_btree *tree: the tree to add a node to
+ * struct hfs_bnode *left: the node to add a node after
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * struct hfs_bnode_ref *: reference to the new bnode.
+ * Preconditions:
+ * 'tree' points to a valid (struct hfs_btree) with at least 1 free node.
+ * 'left' points to a valid (struct hfs_bnode) belonging to 'tree'.
+ * Postconditions:
+ * If NULL is returned then 'tree' and 'left' are unchanged.
+ * Otherwise a node with 0 records is inserted in the tree to the right
+ * of the node 'left'. The 'ndFLink' of 'left' and the 'ndBLink' of
+ * the former right-neighbor of 'left' (if one existed) point to the
+ * new node. If 'left' had no right neighbor and is a leaf node the
+ * the 'bthLNode' of 'tree' points to the new node. The free-count and
+ * bitmap for 'tree' are kept current by hfs_bnode_alloc() which supplies
+ * the required node.
+ */
+static struct hfs_bnode_ref insert_empty_bnode(struct hfs_btree *tree,
+ struct hfs_bnode *left)
+{
+ struct hfs_bnode_ref retval;
+ struct hfs_bnode_ref right;
+
+ retval = hfs_bnode_alloc(tree);
+ if (!retval.bn) {
+ hfs_warn("hfs_binsert: out of bnodes?.\n");
+ goto done;
+ }
+ retval.bn->sticky = HFS_NOT_STICKY;
+ if ((retval.bn->next = bhash(tree, retval.bn->node))) {
+ retval.bn->next->prev = retval.bn;
+ }
+ bhash(tree, retval.bn->node) = retval.bn;
+
+ if (left->ndFLink) {
+ right = hfs_bnode_find(tree, left->ndFLink, HFS_LOCK_WRITE);
+ if (!right.bn) {
+ hfs_warn("hfs_binsert: corrupt btree.\n");
+ hfs_bnode_bitop(tree, retval.bn->node, 0);
+ hfs_bnode_relse(&retval);
+ goto done;
+ }
+ right.bn->ndBLink = retval.bn->node;
+ hfs_bnode_relse(&right);
+ } else if (left->ndType == ndLeafNode) {
+ tree->bthLNode = retval.bn->node;
+ tree->dirt = 1;
+ }
+
+ retval.bn->ndFLink = left->ndFLink;
+ retval.bn->ndBLink = left->node;
+ retval.bn->ndType = left->ndType;
+ retval.bn->ndNHeight = left->ndNHeight;
+ retval.bn->ndNRecs = 0;
+
+ left->ndFLink = retval.bn->node;
+
+ done:
+ return retval;
+}
+
+/*
+ * split()
+ *
+ * Description:
+ * Splits an over full node during insertion.
+ * Picks the split point that results in the most-nearly equal
+ * space usage in the new and old nodes.
+ * Input Variable(s):
+ * struct hfs_belem *elem: the over full node.
+ * int size: the number of bytes to be used by the new record and its key.
+ * Output Variable(s):
+ * struct hfs_belem *elem: changed to indicate where the new record
+ * should be inserted.
+ * Returns:
+ * struct hfs_bnode_ref: reference to the new bnode.
+ * Preconditions:
+ * 'elem' points to a valid path element corresponding to the over full node.
+ * 'size' is positive.
+ * Postconditions:
+ * The records in the node corresponding to 'elem' are redistributed across
+ * the old and new nodes so that after inserting the new record, the space
+ * usage in these two nodes is as equal as possible.
+ * 'elem' is updated so that a call to binsert_nonfull() will insert the
+ * new record in the correct location.
+ */
+static inline struct hfs_bnode_ref split(struct hfs_belem *elem, int size)
+{
+ struct hfs_bnode *bnode = elem->bnr.bn;
+ int nrecs, cutoff, index, tmp, used, in_right;
+ struct hfs_bnode_ref right;
+
+ right = insert_empty_bnode(bnode->tree, bnode);
+ if (right.bn) {
+ nrecs = bnode->ndNRecs;
+ cutoff = (size + bnode_end(bnode) -
+ sizeof(struct NodeDescriptor) +
+ (nrecs+1)*sizeof(hfs_u16))/2;
+ used = 0;
+ in_right = 1;
+ /* note that this only works because records sizes are even */
+ for (index=1; index <= elem->record; ++index) {
+ tmp = (sizeof(hfs_u16) + bnode_rsize(bnode, index))/2;
+ used += tmp;
+ if (used > cutoff) {
+ goto found;
+ }
+ used += tmp;
+ }
+ tmp = (size + sizeof(hfs_u16))/2;
+ used += tmp;
+ if (used > cutoff) {
+ goto found;
+ }
+ in_right = 0;
+ used += tmp;
+ for (; index <= nrecs; ++index) {
+ tmp = (sizeof(hfs_u16) + bnode_rsize(bnode, index))/2;
+ used += tmp;
+ if (used > cutoff) {
+ goto found;
+ }
+ used += tmp;
+ }
+ /* couldn't find the split point! */
+ hfs_bnode_relse(&right);
+ }
+ return right;
+
+found:
+ if (in_right) {
+ elem->bnr = right;
+ elem->record -= index-1;
+ }
+ hfs_bnode_shift_right(bnode, right.bn, index);
+
+ return right;
+}
+
+/*
+ * binsert()
+ *
+ * Description:
+ * Inserts a record in a tree known to have enough room, even if the
+ * insertion requires the splitting of nodes.
+ * Input Variable(s):
+ * struct hfs_brec *brec: partial path to the node to insert in
+ * const struct hfs_bkey *key: key for the new record
+ * const void *data: data for the new record
+ * hfs_u8 keysize: size of the key
+ * hfs_u16 datasize: size of the data
+ * int reserve: number of nodes reserved in case of splits
+ * Output Variable(s):
+ * *brec = NULL
+ * Returns:
+ * int: 0 on success, error code on failure
+ * Preconditions:
+ * 'brec' points to a valid (struct hfs_brec) corresponding to a
+ * record in a leaf node, after which a record is to be inserted,
+ * or to "record 0" of the leaf node if the record is to be inserted
+ * before all existing records in the node. The (struct hfs_brec)
+ * includes all ancestors of the leaf node that are needed to
+ * complete the insertion including the parents of any nodes that
+ * will be split.
+ * 'key' points to a valid (struct hfs_bkey) which is appropriate
+ * to this tree, and which belongs at the insertion point.
+ * 'data' points data appropriate for the indicated node.
+ * 'keysize' gives the size in bytes of the key.
+ * 'datasize' gives the size in bytes of the data.
+ * 'reserve' gives the number of nodes that have been reserved in the
+ * tree to allow for splitting of nodes.
+ * Postconditions:
+ * All 'reserve'd nodes have been either used or released.
+ * *brec = NULL
+ * On success the key and data have been inserted at the indicated
+ * location in the tree, all appropriate fields of the in-core data
+ * structures have been changed and updated versions of the on-disk
+ * data structures have been scheduled for write-back to disk.
+ * On failure the B*-tree is probably invalid both on disk and in-core.
+ *
+ * XXX: Some attempt at repair might be made in the event of failure,
+ * or the fs should be remounted read-only so things don't get worse.
+ */
+static int binsert(struct hfs_brec *brec, const struct hfs_bkey *key,
+ const void *data, hfs_u8 keysize, hfs_u16 datasize,
+ int reserve)
+{
+ struct hfs_bnode_ref left, right, other;
+ struct hfs_btree *tree = brec->tree;
+ struct hfs_belem *belem = brec->bottom;
+ int tmpsize = 1 + tree->bthKeyLen;
+ struct hfs_bkey *tmpkey = hfs_malloc(tmpsize);
+ hfs_u32 node;
+
+ while ((belem >= brec->top) && (belem->flags & HFS_BPATH_OVERFLOW)) {
+ left = belem->bnr;
+ if (left.bn->ndFLink &&
+ hfs_bnode_in_brec(left.bn->ndFLink, brec)) {
+ hfs_warn("hfs_binsert: corrupt btree\n");
+ tree->reserved -= reserve;
+ hfs_free(tmpkey, tmpsize);
+ return -EIO;
+ }
+
+ right = split(belem, ROUND(keysize+1) + ROUND(datasize));
+ --reserve;
+ --tree->reserved;
+ if (!right.bn) {
+ hfs_warn("hfs_binsert: unable to split node!\n");
+ tree->reserved -= reserve;
+ hfs_free(tmpkey, tmpsize);
+ return -ENOSPC;
+ }
+ binsert_nonfull(brec, belem, key, data, keysize, datasize);
+
+ if (belem->bnr.bn == left.bn) {
+ other = right;
+ if (belem->record == 1) {
+ hfs_bnode_update_key(brec, belem, left.bn, 0);
+ }
+ } else {
+ other = left;
+ }
+
+ if (left.bn->node == tree->root->node) {
+ add_root(tree, left.bn, right.bn);
+ hfs_bnode_relse(&other);
+ goto done;
+ }
+
+ data = &node;
+ datasize = sizeof(node);
+ node = htonl(right.bn->node);
+ key = tmpkey;
+ keysize = tree->bthKeyLen;
+ memcpy(tmpkey, bnode_key(right.bn, 1), keysize+1);
+ hfs_bnode_relse(&other);
+
+ --belem;
+ }
+
+ if (belem < brec->top) {
+ hfs_warn("hfs_binsert: Missing parent.\n");
+ tree->reserved -= reserve;
+ hfs_free(tmpkey, tmpsize);
+ return -EIO;
+ }
+
+ binsert_nonfull(brec, belem, key, data, keysize, datasize);
+
+done:
+ tree->reserved -= reserve;
+ hfs_free(tmpkey, tmpsize);
+ return 0;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_binsert()
+ *
+ * Description:
+ * This function inserts a new record into a b-tree.
+ * Input Variable(s):
+ * struct hfs_btree *tree: pointer to the (struct hfs_btree) to insert in
+ * struct hfs_bkey *key: pointer to the (struct hfs_bkey) to insert
+ * void *data: pointer to the data to associate with 'key' in the b-tree
+ * unsigned int datasize: the size of the data
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * int: 0 on success, error code on failure
+ * Preconditions:
+ * 'tree' points to a valid (struct hfs_btree)
+ * 'key' points to a valid (struct hfs_bkey)
+ * 'data' points to valid memory of length 'datasize'
+ * Postconditions:
+ * If zero is returned then the record has been inserted in the
+ * indicated location updating all in-core data structures and
+ * scheduling all on-disk data structures for write-back.
+ */
+int hfs_binsert(struct hfs_btree *tree, const struct hfs_bkey *key,
+ const void *data, hfs_u16 datasize)
+{
+ struct hfs_brec brec;
+ struct hfs_belem *belem;
+ int err, reserve, retval;
+ hfs_u8 keysize;
+
+ if (!tree || (tree->magic != HFS_BTREE_MAGIC) || !key || !data) {
+ hfs_warn("hfs_binsert: invalid arguments.\n");
+ return -EINVAL;
+ }
+
+ if (key->KeyLen > tree->bthKeyLen) {
+ hfs_warn("hfs_binsert: oversized key\n");
+ return -EINVAL;
+ }
+
+restart:
+ if (!tree->bthNRecs) {
+ /* create the root bnode */
+ add_root(tree, NULL, NULL);
+ if (!hfs_brec_init(&brec, tree, HFS_BFIND_INSERT)) {
+ hfs_warn("hfs_binsert: failed to create root.\n");
+ return -ENOSPC;
+ }
+ } else {
+ err = hfs_bfind(&brec, tree, key, HFS_BFIND_INSERT);
+ if (err < 0) {
+ hfs_warn("hfs_binsert: hfs_brec_find failed.\n");
+ return err;
+ } else if (err == 0) {
+ hfs_brec_relse(&brec, NULL);
+ return -EEXIST;
+ }
+ }
+
+ keysize = key->KeyLen;
+ datasize = ROUND(datasize);
+ belem = brec.bottom;
+ belem->flags = 0;
+ if (bnode_freespace(belem->bnr.bn) <
+ (sizeof(hfs_u16) + ROUND(keysize+1) + datasize)) {
+ belem->flags |= HFS_BPATH_OVERFLOW;
+ }
+ if (belem->record == 0) {
+ belem->flags |= HFS_BPATH_FIRST;
+ }
+
+ if (!belem->flags) {
+ hfs_brec_lock(&brec, brec.bottom);
+ reserve = 0;
+ } else {
+ reserve = brec.bottom - brec.top;
+ if (brec.top == 0) {
+ ++reserve;
+ }
+ /* make certain we have enough nodes to proceed */
+ if ((tree->bthFree - tree->reserved) < reserve) {
+ hfs_brec_relse(&brec, NULL);
+ hfs_btree_lock(tree);
+ if ((tree->bthFree - tree->reserved) < reserve) {
+ hfs_btree_extend(tree);
+ }
+ hfs_btree_unlock(tree);
+ if ((tree->bthFree - tree->reserved) < reserve) {
+ return -ENOSPC;
+ } else {
+ goto restart;
+ }
+ }
+ tree->reserved += reserve;
+ hfs_brec_lock(&brec, NULL);
+ }
+
+ retval = binsert(&brec, key, data, keysize, datasize, reserve);
+ hfs_brec_relse(&brec, NULL);
+ if (!retval) {
+ ++tree->bthNRecs;
+ tree->dirt = 1;
+ }
+ return retval;
+}
diff --git a/fs/hfs/bitmap.c b/fs/hfs/bitmap.c
new file mode 100644
index 00000000000000..c9bb850d3d9ac9
--- /dev/null
+++ b/fs/hfs/bitmap.c
@@ -0,0 +1,412 @@
+/*
+ * linux/fs/hfs/bitmap.c
+ *
+ * Copyright (C) 1996-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU General Public License.
+ *
+ * Based on GPLed code Copyright (C) 1995 Michael Dreher
+ *
+ * This file contains the code to modify the volume bitmap:
+ * search/set/clear bits.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs.h"
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_vbm_count_free()
+ *
+ * Description:
+ * Count the number of consecutive cleared bits in the bitmap blocks of
+ * the hfs MDB starting at bit number 'start'. 'mdb' had better
+ * be locked or the indicated number of blocks may be no longer free,
+ * when this functions returns!
+ * Input Variable(s):
+ * struct hfs_mdb *mdb: Pointer to the hfs MDB
+ * hfs_u16 start: bit number to start at
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * The number of consecutive cleared bits starting at bit 'start'
+ * Preconditions:
+ * 'mdb' points to a "valid" (struct hfs_mdb).
+ * Postconditions:
+ * NONE
+ */
+hfs_u16 hfs_vbm_count_free(const struct hfs_mdb *mdb, hfs_u16 start)
+{
+ hfs_u16 block_nr; /* index of the current bitmap block */
+ hfs_u16 bit_nr; /* index of the current bit in block */
+ hfs_u16 count; /* number of bits found so far */
+ hfs_u16 len; /* number of bits found in this block */
+ hfs_u16 max_block; /* index of last bitmap block */
+ hfs_u16 max_bits; /* index of last bit in block */
+
+ /* is this a valid HFS MDB? */
+ if (!mdb) {
+ return 0;
+ }
+
+ block_nr = start / HFS_BM_BPB;
+ bit_nr = start % HFS_BM_BPB;
+ max_block = (mdb->fs_ablocks + HFS_BM_BPB - 1) / HFS_BM_BPB - 1;
+
+ count = 0;
+ while (block_nr <= max_block) {
+ if (block_nr != max_block) {
+ max_bits = HFS_BM_BPB;
+ } else {
+ max_bits = mdb->fs_ablocks % HFS_BM_BPB;
+ }
+
+ len=hfs_count_zero_bits(hfs_buffer_data(mdb->bitmap[block_nr]),
+ max_bits, bit_nr);
+ count += len;
+
+ /* see if we fell short of the end of this block */
+ if ((len + bit_nr) < max_bits) {
+ break;
+ }
+
+ ++block_nr;
+ bit_nr = 0;
+ }
+ return count;
+}
+
+/*
+ * hfs_vbm_search_free()
+ *
+ * Description:
+ * Search for 'num_bits' consecutive cleared bits in the bitmap blocks of
+ * the hfs MDB. 'mdb' had better be locked or the returned range
+ * may be no longer free, when this functions returns!
+ * XXX Currently the search starts from bit 0, but it should start with
+ * the bit number stored in 's_alloc_ptr' of the MDB.
+ * Input Variable(s):
+ * struct hfs_mdb *mdb: Pointer to the hfs MDB
+ * hfs_u16 *num_bits: Pointer to the number of cleared bits
+ * to search for
+ * Output Variable(s):
+ * hfs_u16 *num_bits: The number of consecutive clear bits of the
+ * returned range. If the bitmap is fragmented, this will be less than
+ * requested and it will be zero, when the disk is full.
+ * Returns:
+ * The number of the first bit of the range of cleared bits which has been
+ * found. When 'num_bits' is zero, this is invalid!
+ * Preconditions:
+ * 'mdb' points to a "valid" (struct hfs_mdb).
+ * 'num_bits' points to a variable of type (hfs_u16), which contains
+ * the number of cleared bits to find.
+ * Postconditions:
+ * 'num_bits' is set to the length of the found sequence.
+ */
+hfs_u16 hfs_vbm_search_free(const struct hfs_mdb *mdb, hfs_u16 *num_bits)
+{
+ hfs_u16 block_nr; /* index of the current bitmap block */
+
+ /* position and length of current portion of a run */
+ hfs_u16 cur_pos, cur_len;
+
+ /* position and length of current complete run */
+ hfs_u16 pos=0, len=0;
+
+ /* position and length of longest complete run */
+ hfs_u16 longest_pos=0, longest_len=0;
+
+ void *bitmap; /* contents of the current bitmap block */
+ hfs_u16 max_block; /* upper limit of outer loop */
+ hfs_u16 max_bits; /* upper limit of inner loop */
+
+ /* is this a valid HFS MDB? */
+ if (!mdb) {
+ *num_bits = 0;
+ hfs_warn("hfs_vbm_search_free: not a valid MDB\n");
+ return 0;
+ }
+
+ /* make sure we have actual work to perform */
+ if (!(*num_bits)) {
+ return 0;
+ }
+
+ max_block = (mdb->fs_ablocks+HFS_BM_BPB-1) / HFS_BM_BPB - 1;
+
+ /* search all bitmap blocks */
+ for (block_nr = 0; block_nr <= max_block; block_nr++) {
+ bitmap = hfs_buffer_data(mdb->bitmap[block_nr]);
+
+ if (block_nr != max_block) {
+ max_bits = HFS_BM_BPB;
+ } else {
+ max_bits = mdb->fs_ablocks % HFS_BM_BPB;
+ }
+
+ cur_pos = 0;
+ do {
+ cur_len = hfs_count_zero_bits(bitmap, max_bits,
+ cur_pos);
+ len += cur_len;
+ if (len > longest_len) {
+ longest_pos = pos;
+ longest_len = len;
+ if (len >= *num_bits) {
+ goto search_end;
+ }
+ }
+ if ((cur_pos + cur_len) == max_bits) {
+ break; /* zeros may continue into next block */
+ }
+
+ /* find start of next run of zeros */
+ cur_pos = hfs_find_zero_bit(bitmap, max_bits,
+ cur_pos + cur_len);
+ pos = cur_pos + HFS_BM_BPB*block_nr;
+ len = 0;
+ } while (cur_pos < max_bits);
+ }
+
+search_end:
+ *num_bits = longest_len;
+ return longest_pos;
+}
+
+
+/*
+ * hfs_set_vbm_bits()
+ *
+ * Description:
+ * Set the requested bits in the volume bitmap of the hfs filesystem
+ * Input Variable(s):
+ * struct hfs_mdb *mdb: Pointer to the hfs MDB
+ * hfs_u16 start: The offset of the first bit
+ * hfs_u16 count: The number of bits
+ * Output Variable(s):
+ * None
+ * Returns:
+ * 0: no error
+ * -1: One of the bits was already set. This is a strange
+ * error and when it happens, the filesystem must be repaired!
+ * -2: One or more of the bits are out of range of the bitmap.
+ * -3: The 's_magic' field of the MDB does not match
+ * Preconditions:
+ * 'mdb' points to a "valid" (struct hfs_mdb).
+ * Postconditions:
+ * Starting with bit number 'start', 'count' bits in the volume bitmap
+ * are set. The affected bitmap blocks are marked "dirty", the free
+ * block count of the MDB is updated and the MDB is marked dirty.
+ */
+int hfs_set_vbm_bits(struct hfs_mdb *mdb, hfs_u16 start, hfs_u16 count)
+{
+ hfs_u16 block_nr; /* index of the current bitmap block */
+ hfs_u16 u32_nr; /* index of the current hfs_u32 in block */
+ hfs_u16 bit_nr; /* index of the current bit in hfs_u32 */
+ hfs_u16 left = count; /* number of bits left to be set */
+ hfs_u32 *bitmap; /* the current bitmap block's contents */
+
+ /* is this a valid HFS MDB? */
+ if (!mdb) {
+ return -3;
+ }
+
+ /* is there any actual work to be done? */
+ if (!count) {
+ return 0;
+ }
+
+ /* are all of the bits in range? */
+ if ((start + count) > mdb->fs_ablocks) {
+ return -2;
+ }
+
+ block_nr = start / HFS_BM_BPB;
+ u32_nr = (start % HFS_BM_BPB) / 32;
+ bit_nr = start % 32;
+
+ /* bitmap is always on a 32-bit boundary */
+ bitmap = (hfs_u32 *)hfs_buffer_data(mdb->bitmap[block_nr]);
+
+ /* do any partial hfs_u32 at the start */
+ if (bit_nr != 0) {
+ while ((bit_nr < 32) && left) {
+ if (hfs_set_bit(bit_nr, bitmap + u32_nr)) {
+ hfs_buffer_dirty(mdb->bitmap[block_nr]);
+ return -1;
+ }
+ ++bit_nr;
+ --left;
+ }
+ bit_nr=0;
+
+ /* advance u32_nr and check for end of this block */
+ if (++u32_nr > 127) {
+ u32_nr = 0;
+ hfs_buffer_dirty(mdb->bitmap[block_nr]);
+ ++block_nr;
+ /* bitmap is always on a 32-bit boundary */
+ bitmap = (hfs_u32 *)
+ hfs_buffer_data(mdb->bitmap[block_nr]);
+ }
+ }
+
+ /* do full hfs_u32s */
+ while (left > 31) {
+ if (bitmap[u32_nr] != ((hfs_u32)0)) {
+ hfs_buffer_dirty(mdb->bitmap[block_nr]);
+ return -1;
+ }
+ bitmap[u32_nr] = ~((hfs_u32)0);
+ left -= 32;
+
+ /* advance u32_nr and check for end of this block */
+ if (++u32_nr > 127) {
+ u32_nr = 0;
+ hfs_buffer_dirty(mdb->bitmap[block_nr]);
+ ++block_nr;
+ /* bitmap is always on a 32-bit boundary */
+ bitmap = (hfs_u32 *)
+ hfs_buffer_data(mdb->bitmap[block_nr]);
+ }
+ }
+
+
+ /* do any partial hfs_u32 at end */
+ while (left) {
+ if (hfs_set_bit(bit_nr, bitmap + u32_nr)) {
+ hfs_buffer_dirty(mdb->bitmap[block_nr]);
+ return -1;
+ }
+ ++bit_nr;
+ --left;
+ }
+
+ hfs_buffer_dirty(mdb->bitmap[block_nr]);
+ mdb->free_ablocks -= count;
+
+ /* successful completion */
+ hfs_mdb_dirty(mdb->sys_mdb);
+ return 0;
+}
+
+/*
+ * hfs_clear_vbm_bits()
+ *
+ * Description:
+ * Clear the requested bits in the volume bitmap of the hfs filesystem
+ * Input Variable(s):
+ * struct hfs_mdb *mdb: Pointer to the hfs MDB
+ * hfs_u16 start: The offset of the first bit
+ * hfs_u16 count: The number of bits
+ * Output Variable(s):
+ * None
+ * Returns:
+ * 0: no error
+ * -1: One of the bits was already clear. This is a strange
+ * error and when it happens, the filesystem must be repaired!
+ * -2: One or more of the bits are out of range of the bitmap.
+ * -3: The 's_magic' field of the MDB does not match
+ * Preconditions:
+ * 'mdb' points to a "valid" (struct hfs_mdb).
+ * Postconditions:
+ * Starting with bit number 'start', 'count' bits in the volume bitmap
+ * are cleared. The affected bitmap blocks are marked "dirty", the free
+ * block count of the MDB is updated and the MDB is marked dirty.
+ */
+int hfs_clear_vbm_bits(struct hfs_mdb *mdb, hfs_u16 start, hfs_u16 count)
+{
+ hfs_u16 block_nr; /* index of the current bitmap block */
+ hfs_u16 u32_nr; /* index of the current hfs_u32 in block */
+ hfs_u16 bit_nr; /* index of the current bit in hfs_u32 */
+ hfs_u16 left = count; /* number of bits left to be set */
+ hfs_u32 *bitmap; /* the current bitmap block's contents */
+
+ /* is this a valid HFS MDB? */
+ if (!mdb) {
+ return -3;
+ }
+
+ /* is there any actual work to be done? */
+ if (!count) {
+ return 0;
+ }
+
+ /* are all of the bits in range? */
+ if ((start + count) > mdb->fs_ablocks) {
+ return -2;
+ }
+
+ block_nr = start / HFS_BM_BPB;
+ u32_nr = (start % HFS_BM_BPB) / 32;
+ bit_nr = start % 32;
+
+ /* bitmap is always on a 32-bit boundary */
+ bitmap = (hfs_u32 *)hfs_buffer_data(mdb->bitmap[block_nr]);
+
+ /* do any partial hfs_u32 at the start */
+ if (bit_nr != 0) {
+ while ((bit_nr < 32) && left) {
+ if (!hfs_clear_bit(bit_nr, bitmap + u32_nr)) {
+ hfs_buffer_dirty(mdb->bitmap[block_nr]);
+ return -1;
+ }
+ ++bit_nr;
+ --left;
+ }
+ bit_nr=0;
+
+ /* advance u32_nr and check for end of this block */
+ if (++u32_nr > 127) {
+ u32_nr = 0;
+ hfs_buffer_dirty(mdb->bitmap[block_nr]);
+ ++block_nr;
+ /* bitmap is always on a 32-bit boundary */
+ bitmap = (hfs_u32 *)
+ hfs_buffer_data(mdb->bitmap[block_nr]);
+ }
+ }
+
+ /* do full hfs_u32s */
+ while (left > 31) {
+ if (bitmap[u32_nr] != ~((hfs_u32)0)) {
+ hfs_buffer_dirty(mdb->bitmap[block_nr]);
+ return -1;
+ }
+ bitmap[u32_nr] = ((hfs_u32)0);
+ left -= 32;
+
+ /* advance u32_nr and check for end of this block */
+ if (++u32_nr > 127) {
+ u32_nr = 0;
+ hfs_buffer_dirty(mdb->bitmap[block_nr]);
+ ++block_nr;
+ /* bitmap is always on a 32-bit boundary */
+ bitmap = (hfs_u32 *)
+ hfs_buffer_data(mdb->bitmap[block_nr]);
+ }
+ }
+
+
+ /* do any partial hfs_u32 at end */
+ while (left) {
+ if (!hfs_clear_bit(bit_nr, bitmap + u32_nr)) {
+ hfs_buffer_dirty(mdb->bitmap[block_nr]);
+ return -1;
+ }
+ ++bit_nr;
+ --left;
+ }
+
+ hfs_buffer_dirty(mdb->bitmap[block_nr]);
+ mdb->free_ablocks += count;
+
+ /* successful completion */
+ hfs_mdb_dirty(mdb->sys_mdb);
+ return 0;
+}
diff --git a/fs/hfs/bitops.c b/fs/hfs/bitops.c
new file mode 100644
index 00000000000000..e2cb38877e76c2
--- /dev/null
+++ b/fs/hfs/bitops.c
@@ -0,0 +1,124 @@
+/*
+ * linux/fs/hfs/bitops.c
+ *
+ * Copyright (C) 1996 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU General Public License.
+ *
+ * This file contains functions to handle bitmaps in "left-to-right"
+ * bit-order such that the MSB of a 32-bit big-endian word is bit 0.
+ * (This corresponds to bit 7 of a 32-bit little-endian word.)
+ *
+ * I have tested and confirmed that the results are identical on the
+ * Intel x86, PowerPC and DEC Alpha processors.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ */
+
+#include "hfs.h"
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_find_zero_bit()
+ *
+ * Description:
+ * Given a block of memory, its length in bits, and a starting bit number,
+ * determine the number of the first zero bits (in left-to-right ordering)
+ * in that range.
+ *
+ * Returns >= 'size' if no zero bits are found in the range.
+ *
+ * Accesses memory in 32-bit aligned chunks of 32-bits and thus
+ * may read beyond the 'size'th bit.
+ */
+hfs_u32 hfs_find_zero_bit(const hfs_u32 *start, hfs_u32 size, hfs_u32 offset)
+{
+ const hfs_u32 *end = start + ((size + 31) >> 5);
+ const hfs_u32 *curr = start + (offset >> 5);
+ int bit = offset % 32;
+
+ if (offset < size) {
+ /* scan the first partial hfs_u32 for zero bits */
+ if (bit != 0) {
+ do {
+ if (!hfs_test_bit(bit, curr)) {
+ goto done;
+ }
+ ++bit;
+ } while (bit < 32);
+ bit = 0;
+ ++curr;
+ }
+
+ /* scan complete hfs_u32s for the first zero bit */
+ while (curr < end) {
+ if (*curr == ~((hfs_u32)0)) {
+ ++curr;
+ } else {
+ while (hfs_test_bit(bit, curr)) {
+ ++bit;
+ }
+ break;
+ }
+ }
+
+done:
+ bit |= (curr - start) << 5;
+ return bit;
+ } else {
+ return size;
+ }
+}
+
+/*
+ * hfs_count_zero_bits()
+ *
+ * Description:
+ * Given a block of memory, its length in bits, and a starting bit number,
+ * determine the number of consecutive zero bits (in left-to-right ordering)
+ * in that range.
+ *
+ * Accesses memory in 32-bit aligned chunks of 32-bits and thus
+ * may read beyond the 'size'th bit.
+ */
+hfs_u32 hfs_count_zero_bits(const hfs_u32 *start, hfs_u32 size, hfs_u32 offset)
+{
+ const hfs_u32 *end = start + ((size + 31) >> 5);
+ const hfs_u32 *curr = start + (offset >> 5);
+ int bit = offset % 32;
+
+ if (offset < size) {
+ /* scan the first partial hfs_u32 for one bits */
+ if (bit != 0) {
+ do {
+ if (hfs_test_bit(bit, curr)) {
+ goto done;
+ }
+ ++bit;
+ } while (bit < 32);
+ bit = 0;
+ ++curr;
+ }
+
+ /* scan complete hfs_u32s for the first one bit */
+ while (curr < end) {
+ if (*curr == ((hfs_u32)0)) {
+ ++curr;
+ } else {
+ while (!hfs_test_bit(bit, curr)) {
+ ++bit;
+ }
+ break;
+ }
+ }
+
+done:
+ bit |= (curr - start) << 5;
+ if (bit > size) {
+ bit = size;
+ }
+ return bit - offset;
+ } else {
+ return 0;
+ }
+}
diff --git a/fs/hfs/bnode.c b/fs/hfs/bnode.c
new file mode 100644
index 00000000000000..d7f0566e3cc06e
--- /dev/null
+++ b/fs/hfs/bnode.c
@@ -0,0 +1,544 @@
+/*
+ * linux/fs/hfs/bnode.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU General Public License.
+ *
+ * This file contains the code to access nodes in the B-tree structure.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ *
+ * The code in this file initializes some structures which contain
+ * pointers by calling memset(&foo, 0, sizeof(foo)).
+ * This produces the desired behavior only due to the non-ANSI
+ * assumption that the machine representation of NULL is all zeros.
+ */
+
+#include "hfs_btree.h"
+
+/*================ File-local variables ================*/
+
+/* debugging statistics */
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+int bnode_count = 0;
+#endif
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_bnode_delete()
+ *
+ * Description:
+ * This function is called to remove a bnode from the cache and
+ * release its resources.
+ * Input Variable(s):
+ * struct hfs_bnode *bn: Pointer to the (struct hfs_bnode) to be
+ * removed from the cache.
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * void
+ * Preconditions:
+ * 'bn' points to a "valid" (struct hfs_bnode).
+ * Postconditions:
+ * The node 'bn' is removed from the cache, its memory freed and its
+ * buffer (if any) released.
+ */
+void hfs_bnode_delete(struct hfs_bnode *bn)
+{
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+ --bnode_count;
+#endif
+ /* join neighbors */
+ if (bn->next) {
+ bn->next->prev = bn->prev;
+ }
+ if (bn->prev) {
+ bn->prev->next = bn->next;
+ }
+ /* fix cache slot if necessary */
+ if (bhash(bn->tree, bn->node) == bn) {
+ bhash(bn->tree, bn->node) = bn->next;
+ }
+ /* release resources */
+ hfs_buffer_put(bn->buf); /* safe: checks for NULL argument */
+ HFS_DELETE(bn);
+}
+
+
+/*
+ * hfs_bnode_read()
+ *
+ * Description:
+ * This function creates a (struct hfs_bnode) and, if appropriate,
+ * inserts it in the cache.
+ * Input Variable(s):
+ * struct hfs_bnode *bnode: pointer to the new bnode.
+ * struct hfs_btree *tree: pointer to the (struct hfs_btree)
+ * containing the desired node
+ * hfs_u32 node: the number of the desired node.
+ * int sticky: the value to assign to the 'sticky' field.
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * (struct hfs_bnode *) pointing to the newly created bnode or NULL.
+ * Preconditions:
+ * 'bnode' points to a "valid" (struct hfs_bnode).
+ * 'tree' points to a "valid" (struct hfs_btree).
+ * 'node' is an existing node number in the B-tree.
+ * Postconditions:
+ * The following are true of 'bnode' upon return:
+ * The 'magic' field is set to indicate a valid (struct hfs_bnode).
+ * The 'sticky', 'tree' and 'node' fields are initialized to the
+ * values of the of the corresponding arguments.
+ * If the 'sticky' argument is zero then the fields 'prev' and
+ * 'next' are initialized by inserting the (struct hfs_bnode) in the
+ * linked list of the appropriate cache slot; otherwise they are
+ * initialized to NULL.
+ * The data is read from disk (or buffer cache) and the 'buf' field
+ * points to the buffer for that data.
+ * If no other processes tried to access this node while this
+ * process was waiting on disk I/O (if necessary) then the
+ * remaining fields are zero ('count', 'resrv', 'lock') or NULL
+ * ('wqueue', 'rqueue') corresponding to no accesses.
+ * If there were access attempts during I/O then they were blocked
+ * until the I/O was complete, and the fields 'count', 'resrv',
+ * 'lock', 'wqueue' and 'rqueue' reflect the results of unblocking
+ * those processes when the I/O was completed.
+ */
+void hfs_bnode_read(struct hfs_bnode *bnode, struct hfs_btree *tree,
+ hfs_u32 node, int sticky)
+{
+ struct NodeDescriptor *nd;
+ int block, lcv;
+ hfs_u16 curr, prev, limit;
+
+ /* Initialize the structure */
+ memset(bnode, 0, sizeof(*bnode));
+ bnode->magic = HFS_BNODE_MAGIC;
+ bnode->tree = tree;
+ bnode->node = node;
+ bnode->sticky = sticky;
+ hfs_init_waitqueue(&bnode->rqueue);
+ hfs_init_waitqueue(&bnode->wqueue);
+
+ if (sticky == HFS_NOT_STICKY) {
+ /* Insert it in the cache if appropriate */
+ if ((bnode->next = bhash(tree, node))) {
+ bnode->next->prev = bnode;
+ }
+ bhash(tree, node) = bnode;
+ }
+
+ /* Make the bnode look like it is being
+ modified so other processes will wait for
+ the I/O to complete */
+ bnode->count = bnode->resrv = bnode->lock = 1;
+
+ /* Read in the node, possibly causing a schedule()
+ call. If the I/O fails then emit a warning. Each
+ process that was waiting on the bnode (including
+ the current one) will notice the failure and
+ hfs_bnode_relse() the node. The last hfs_bnode_relse()
+ will call hfs_bnode_delete() and discard the bnode. */
+
+ block = hfs_extent_map(&tree->entry.u.file.data_fork, node, 0);
+ if (!block) {
+ hfs_warn("hfs_bnode_read: bad node number 0x%08x\n", node);
+ } else if (hfs_buffer_ok(bnode->buf =
+ hfs_buffer_get(tree->sys_mdb, block, 1))) {
+ /* read in the NodeDescriptor */
+ nd = (struct NodeDescriptor *)hfs_buffer_data(bnode->buf);
+ bnode->ndFLink = hfs_get_hl(nd->ndFLink);
+ bnode->ndBLink = hfs_get_hl(nd->ndBLink);
+ bnode->ndType = nd->ndType;
+ bnode->ndNHeight = nd->ndNHeight;
+ bnode->ndNRecs = hfs_get_hs(nd->ndNRecs);
+
+ /* verify the integrity of the node */
+ prev = sizeof(struct NodeDescriptor);
+ limit = HFS_SECTOR_SIZE - sizeof(hfs_u16)*(bnode->ndNRecs + 1);
+ for (lcv=1; lcv <= (bnode->ndNRecs + 1); ++lcv) {
+ curr = hfs_get_hs(RECTBL(bnode, lcv));
+ if ((curr < prev) || (curr > limit)) {
+ hfs_warn("hfs_bnode_read: corrupt node "
+ "number 0x%08x\n", node);
+ hfs_buffer_put(bnode->buf);
+ bnode->buf = NULL;
+ break;
+ }
+ prev = curr;
+ }
+ }
+
+ /* Undo our fakery with the lock state and
+ hfs_wake_up() anyone who we managed to trick */
+ --bnode->count;
+ bnode->resrv = bnode->lock = 0;
+ hfs_wake_up(&bnode->rqueue);
+}
+
+/*
+ * hfs_bnode_lock()
+ *
+ * Description:
+ * This function does the locking of a bnode.
+ * Input Variable(s):
+ * struct hfs_bnode *bn: pointer to the (struct hfs_bnode) to lock
+ * int lock_type: the type of lock desired
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * void
+ * Preconditions:
+ * 'bn' points to a "valid" (struct hfs_bnode).
+ * 'lock_type' is a valid hfs_lock_t
+ * Postconditions:
+ * The 'count' field of 'bn' is incremented by one. If 'lock_type'
+ * is HFS_LOCK_RESRV the 'resrv' field is also incremented.
+ */
+void hfs_bnode_lock(struct hfs_bnode_ref *bnr, int lock_type)
+{
+ struct hfs_bnode *bn = bnr->bn;
+
+ if ((lock_type == bnr->lock_type) || !bn) {
+ return;
+ }
+
+ if (bnr->lock_type == HFS_LOCK_WRITE) {
+ hfs_bnode_commit(bnr->bn);
+ }
+
+ switch (lock_type) {
+ default:
+ goto bail;
+ break;
+
+ case HFS_LOCK_READ:
+ /* We may not obtain read access if any process is
+ currently modifying or waiting to modify this node.
+ If we can't obtain access we wait on the rqueue
+ wait queue to be woken up by the modifying process
+ when it relinquishes its lock. */
+ switch (bnr->lock_type) {
+ default:
+ goto bail;
+ break;
+
+ case HFS_LOCK_NONE:
+ while (bn->lock || waitqueue_active(&bn->wqueue)) {
+ hfs_sleep_on(&bn->rqueue);
+ }
+ ++bn->count;
+ break;
+ }
+ break;
+
+ case HFS_LOCK_RESRV:
+ /* We may not obtain a reservation (read access with
+ an option to write later), if any process currently
+ holds a reservation on this node. That includes
+ any process which is currently modifying this node.
+ If we can't obtain access, then we wait on the
+ rqueue wait queue to e woken up by the
+ reservation-holder when it calls hfs_bnode_relse. */
+ switch (bnr->lock_type) {
+ default:
+ goto bail;
+ break;
+
+ case HFS_LOCK_NONE:
+ while (bn->resrv) {
+ hfs_sleep_on(&bn->rqueue);
+ }
+ bn->resrv = 1;
+ ++bn->count;
+ break;
+
+ case HFS_LOCK_WRITE:
+ bn->lock = 0;
+ hfs_wake_up(&bn->rqueue);
+ break;
+ }
+ break;
+
+ case HFS_LOCK_WRITE:
+ switch (bnr->lock_type) {
+ default:
+ goto bail;
+ break;
+
+ case HFS_LOCK_NONE:
+ while (bn->resrv) {
+ hfs_sleep_on(&bn->rqueue);
+ }
+ bn->resrv = 1;
+ ++bn->count;
+ case HFS_LOCK_RESRV:
+ while (bn->count > 1) {
+ hfs_sleep_on(&bn->wqueue);
+ }
+ bn->lock = 1;
+ break;
+ }
+ break;
+
+ case HFS_LOCK_NONE:
+ switch (bnr->lock_type) {
+ default:
+ goto bail;
+ break;
+
+ case HFS_LOCK_READ:
+ /* This process was reading this node. If
+ there is now exactly one other process using
+ the node then hfs_wake_up() a (potentially
+ nonexistent) waiting process. Note that I
+ refer to "a" process since the reservation
+ system ensures that only one process can
+ get itself on the wait queue. */
+ if (bn->count == 2) {
+ hfs_wake_up(&bn->wqueue);
+ }
+ break;
+
+ case HFS_LOCK_WRITE:
+ /* This process was modifying this node.
+ Unlock the node and fall-through to the
+ HFS_LOCK_RESRV case, since a 'reservation'
+ is a prerequisite for HFS_LOCK_WRITE. */
+ bn->lock = 0;
+ case HFS_LOCK_RESRV:
+ /* This process had placed a 'reservation' on
+ this node, indicating an intention to
+ possibly modify the node. We can get to
+ this spot directly (if the 'reservation'
+ not converted to a HFS_LOCK_WRITE), or by
+ falling through from the above case if the
+ reservation was converted.
+ Since HFS_LOCK_RESRV and HFS_LOCK_WRITE
+ both block processes that want access
+ (HFS_LOCK_RESRV blocks other processes that
+ want reservations but allow HFS_LOCK_READ
+ accesses, while HFS_LOCK_WRITE must have
+ exclusive access and thus blocks both
+ types) we hfs_wake_up() any processes that
+ might be waiting for access. If multiple
+ processes are waiting for a reservation
+ then the magic of process scheduling will
+ settle the dispute. */
+ bn->resrv = 0;
+ hfs_wake_up(&bn->rqueue);
+ break;
+ }
+ --bn->count;
+ break;
+ }
+ bnr->lock_type = lock_type;
+ return;
+
+bail:
+ hfs_warn("hfs_bnode_lock: invalid lock change: %d->%d.\n",
+ bnr->lock_type, lock_type);
+ return;
+}
+
+/*
+ * hfs_bnode_relse()
+ *
+ * Description:
+ * This function is called when a process is done using a bnode. If
+ * the proper conditions are met then we call hfs_bnode_delete() to remove
+ * it from the cache. If it is not deleted then we update its state
+ * to reflect one less process using it.
+ * Input Variable(s):
+ * struct hfs_bnode *bn: pointer to the (struct hfs_bnode) to release.
+ * int lock_type: The type of lock held by the process releasing this node.
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * void
+ * Preconditions:
+ * 'bn' is NULL or points to a "valid" (struct hfs_bnode).
+ * Postconditions:
+ * If 'bn' meets the appropriate conditions (see below) then it is
+ * kept in the cache and all fields are set to consistent values
+ * which reflect one less process using the node than upon entry.
+ * If 'bn' does not meet the conditions then it is deleted (see
+ * hfs_bnode_delete() for postconditions).
+ * In either case, if 'lock_type' is HFS_LOCK_WRITE
+ * then the corresponding buffer is dirtied.
+ */
+void hfs_bnode_relse(struct hfs_bnode_ref *bnr)
+{
+ struct hfs_bnode *bn;
+
+ if (!bnr || !(bn = bnr->bn)) {
+ return;
+ }
+
+ /* We update the lock state of the node if it is still in use
+ or if it is "sticky" (such as the B-tree head and root).
+ Otherwise we just delete it. */
+ if ((bn->count > 1) || (waitqueue_active(&bn->rqueue)) || (bn->sticky != HFS_NOT_STICKY)) {
+ hfs_bnode_lock(bnr, HFS_LOCK_NONE);
+ } else {
+ /* dirty buffer if we (might) have modified it */
+ if (bnr->lock_type == HFS_LOCK_WRITE) {
+ hfs_bnode_commit(bn);
+ }
+ hfs_bnode_delete(bn);
+ bnr->lock_type = HFS_LOCK_NONE;
+ }
+ bnr->bn = NULL;
+}
+
+/*
+ * hfs_bnode_find()
+ *
+ * Description:
+ * This function is called to obtain a bnode. The cache is
+ * searched for the node. If it not found there it is added to
+ * the cache by hfs_bnode_read(). There are two special cases node=0
+ * (the header node) and node='tree'->bthRoot (the root node), in
+ * which the nodes are obtained from fields of 'tree' without
+ * consulting or modifying the cache.
+ * Input Variable(s):
+ * struct hfs_tree *tree: pointer to the (struct hfs_btree) from
+ * which to get a node.
+ * int node: the node number to get from 'tree'.
+ * int lock_type: The kind of access (HFS_LOCK_READ, or
+ * HFS_LOCK_RESRV) to obtain to the node
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * (struct hfs_bnode_ref) Reference to the requested node.
+ * Preconditions:
+ * 'tree' points to a "valid" (struct hfs_btree).
+ * Postconditions:
+ * If 'node' refers to a valid node in 'tree' and 'lock_type' has
+ * one of the values listed above and no I/O errors occur then the
+ * value returned refers to a valid (struct hfs_bnode) corresponding
+ * to the requested node with the requested access type. The node
+ * is also added to the cache if not previously present and not the
+ * root or header.
+ * If the conditions given above are not met, the bnode in the
+ * returned reference is NULL.
+ */
+struct hfs_bnode_ref hfs_bnode_find(struct hfs_btree *tree,
+ hfs_u32 node, int lock_type)
+{
+ struct hfs_bnode *bn;
+ struct hfs_bnode *empty = NULL;
+ struct hfs_bnode_ref bnr;
+
+ bnr.lock_type = HFS_LOCK_NONE;
+ bnr.bn = NULL;
+
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+ hfs_warn("hfs_bnode_find: %c %d:%d\n",
+ lock_type==HFS_LOCK_READ?'R':
+ (lock_type==HFS_LOCK_RESRV?'V':'W'),
+ (int)ntohl(tree->entry.cnid), node);
+#endif
+
+ /* check special cases */
+ if (!node) {
+ bn = &tree->head;
+ goto return_it;
+ } else if (node == tree->bthRoot) {
+ bn = tree->root;
+ goto return_it;
+ }
+
+restart:
+ /* look for the node in the cache. */
+ bn = bhash(tree, node);
+ while (bn && (bn->magic == HFS_BNODE_MAGIC)) {
+ if (bn->node == node) {
+ goto found_it;
+ }
+ bn = bn->next;
+ }
+
+ if (!empty) {
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+ ++bnode_count;
+#endif
+ if (HFS_NEW(empty)) {
+ goto restart;
+ }
+ return bnr;
+ }
+ bn = empty;
+ hfs_bnode_read(bn, tree, node, HFS_NOT_STICKY);
+ goto return_it;
+
+found_it:
+ /* check validity */
+ if (bn->magic != HFS_BNODE_MAGIC) {
+ /* If we find a corrupt bnode then we return
+ NULL. However, we don't try to remove it
+ from the cache or release its resources
+ since we have no idea what kind of trouble
+ we could get into that way. */
+ hfs_warn("hfs_bnode_find: bnode cache is corrupt.\n");
+ return bnr;
+ }
+ if (empty) {
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+ --bnode_count;
+#endif
+ HFS_DELETE(empty);
+ }
+
+return_it:
+ /* Wait our turn */
+ bnr.bn = bn;
+ hfs_bnode_lock(&bnr, lock_type);
+
+ /* Check for failure to read the node from disk */
+ if (!hfs_buffer_ok(bn->buf)) {
+ hfs_bnode_relse(&bnr);
+ }
+
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+ if (!bnr.bn) {
+ hfs_warn("hfs_bnode_find: failed\n");
+ } else {
+ hfs_warn("hfs_bnode_find: use %d(%d) lvl %d [%d]\n", bn->count,
+ bn->buf->b_count, bn->ndNHeight, bnode_count);
+ hfs_warn("hfs_bnode_find: blnk %u flnk %u recs %u\n",
+ bn->ndBLink, bn->ndFLink, bn->ndNRecs);
+ }
+#endif
+
+ return bnr;
+}
+
+/*
+ * hfs_bnode_commit()
+ *
+ * Called to write a possibly dirty bnode back to disk.
+ */
+void hfs_bnode_commit(struct hfs_bnode *bn)
+{
+ if (hfs_buffer_ok(bn->buf)) {
+ struct NodeDescriptor *nd;
+ nd = (struct NodeDescriptor *)hfs_buffer_data(bn->buf);
+
+ hfs_put_hl(bn->ndFLink, nd->ndFLink);
+ hfs_put_hl(bn->ndBLink, nd->ndBLink);
+ nd->ndType = bn->ndType;
+ nd->ndNHeight = bn->ndNHeight;
+ hfs_put_hs(bn->ndNRecs, nd->ndNRecs);
+ hfs_buffer_dirty(bn->buf);
+
+ /* increment write count */
+ hfs_mdb_dirty(bn->tree->sys_mdb);
+ }
+}
diff --git a/fs/hfs/brec.c b/fs/hfs/brec.c
new file mode 100644
index 00000000000000..4db76fc4f62cb7
--- /dev/null
+++ b/fs/hfs/brec.c
@@ -0,0 +1,239 @@
+/*
+ * linux/fs/hfs/brec.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU General Public License.
+ *
+ * This file contains the code to access records in a btree.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs_btree.h"
+
+/*================ File-local functions ================*/
+
+/*
+ * first()
+ *
+ * returns HFS_BPATH_FIRST if elem->record == 1, 0 otherwise
+ */
+static inline int first(const struct hfs_belem *elem)
+{
+ return (elem->record == 1) ? HFS_BPATH_FIRST : 0;
+}
+
+/*
+ * overflow()
+ *
+ * return HFS_BPATH_OVERFLOW if the node has no room for an
+ * additional pointer record, 0 otherwise.
+ */
+static inline int overflow(const struct hfs_btree *tree,
+ const struct hfs_bnode *bnode)
+{
+ /* there is some algebra involved in getting this form */
+ return ((HFS_SECTOR_SIZE - sizeof(hfs_u32)) <
+ (bnode_end(bnode) + (2+bnode->ndNRecs)*sizeof(hfs_u16) +
+ ROUND(tree->bthKeyLen+1))) ? HFS_BPATH_OVERFLOW : 0;
+}
+
+/*
+ * underflow()
+ *
+ * return HFS_BPATH_UNDERFLOW if the node will be less that 1/2 full
+ * upon removal of a pointer record, 0 otherwise.
+ */
+static inline int underflow(const struct hfs_btree *tree,
+ const struct hfs_bnode *bnode)
+{
+ return ((bnode->ndNRecs * sizeof(hfs_u16) +
+ bnode_offset(bnode, bnode->ndNRecs)) <
+ (HFS_SECTOR_SIZE - sizeof(struct NodeDescriptor))/2) ?
+ HFS_BPATH_UNDERFLOW : 0;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_brec_next()
+ *
+ * Description:
+ * Obtain access to a child of an internal node in a B-tree.
+ * Input Variable(s):
+ * struct hfs_brec *brec: pointer to the (struct hfs_brec) to
+ * add an element to.
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * struct hfs_belem *: pointer to the new path element or NULL
+ * Preconditions:
+ * 'brec' points to a "valid" (struct hfs_brec), the last element of
+ * which corresponds to a record in a bnode of type ndIndxNode and the
+ * 'record' field indicates the index record for the desired child.
+ * Postconditions:
+ * If the call to hfs_bnode_find() fails then 'brec' is released
+ * and a NULL is returned.
+ * Otherwise:
+ * Any ancestors in 'brec' that are not needed (as determined by the
+ * 'keep_flags' field of 'brec) are released from 'brec'.
+ * A new element is added to 'brec' corresponding to the desired
+ * child.
+ * The child is obtained with the same 'lock_type' field as its
+ * parent.
+ * The 'record' field is initialized to the last record.
+ * A pointer to the new path element is returned.
+ */
+struct hfs_belem *hfs_brec_next(struct hfs_brec *brec)
+{
+ struct hfs_belem *elem = brec->bottom;
+ hfs_u32 node;
+ int lock_type;
+
+ /* release unneeded ancestors */
+ elem->flags = first(elem) |
+ overflow(brec->tree, elem->bnr.bn) |
+ underflow(brec->tree, elem->bnr.bn);
+ if (!(brec->keep_flags & elem->flags)) {
+ hfs_brec_relse(brec, brec->bottom-1);
+ } else if ((brec->bottom-2 >= brec->top) &&
+ !(elem->flags & (elem-1)->flags)) {
+ hfs_brec_relse(brec, brec->bottom-2);
+ }
+
+ node = hfs_get_hl(belem_record(elem));
+ lock_type = elem->bnr.lock_type;
+
+ if (!node || hfs_bnode_in_brec(node, brec)) {
+ hfs_warn("hfs_bfind: corrupt btree\n");
+ hfs_brec_relse(brec, NULL);
+ return NULL;
+ }
+
+ ++elem;
+ ++brec->bottom;
+
+ elem->bnr = hfs_bnode_find(brec->tree, node, lock_type);
+ if (!elem->bnr.bn) {
+ hfs_brec_relse(brec, NULL);
+ return NULL;
+ }
+ elem->record = elem->bnr.bn->ndNRecs;
+
+ return elem;
+}
+
+/*
+ * hfs_brec_lock()
+ *
+ * Description:
+ * This function obtains HFS_LOCK_WRITE access to the bnode
+ * containing this hfs_brec. All descendents in the path from this
+ * record to the leaf are given HFS_LOCK_WRITE access and all
+ * ancestors in the path from the root to here are released.
+ * Input Variable(s):
+ * struct hfs_brec *brec: pointer to the brec to obtain
+ * HFS_LOCK_WRITE access to some of the nodes of.
+ * struct hfs_belem *elem: the first node to lock or NULL for all
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * void
+ * Preconditions:
+ * 'brec' points to a "valid" (struct hfs_brec)
+ * Postconditions:
+ * All nodes between the indicated node and the beginning of the path
+ * are released. hfs_bnode_lock() is called in turn on each node
+ * from the indicated node to the leaf node of the path, with a
+ * lock_type argument of HFS_LOCK_WRITE. If one of those calls
+ * results in deadlock, then this function will never return.
+ */
+void hfs_brec_lock(struct hfs_brec *brec, struct hfs_belem *elem)
+{
+ if (!elem) {
+ elem = brec->top;
+ } else if (elem > brec->top) {
+ hfs_brec_relse(brec, elem-1);
+ }
+
+ while (elem <= brec->bottom) {
+ hfs_bnode_lock(&elem->bnr, HFS_LOCK_WRITE);
+ ++elem;
+ }
+}
+
+/*
+ * hfs_brec_init()
+ *
+ * Description:
+ * Obtain access to the root node of a B-tree.
+ * Note that this first must obtain access to the header node.
+ * Input Variable(s):
+ * struct hfs_brec *brec: pointer to the (struct hfs_brec) to
+ * initialize
+ * struct hfs_btree *btree: pointer to the (struct hfs_btree)
+ * int lock_type: the type of access to get to the nodes.
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * struct hfs_belem *: pointer to the root path element or NULL
+ * Preconditions:
+ * 'brec' points to a (struct hfs_brec).
+ * 'tree' points to a valid (struct hfs_btree).
+ * Postconditions:
+ * If the two calls to brec_bnode_find() succeed then the return value
+ * points to a (struct hfs_belem) which corresponds to the root node
+ * of 'brec->tree'.
+ * Both the root and header nodes are obtained with the type of lock
+ * given by (flags & HFS_LOCK_MASK).
+ * The fields 'record' field of the root is set to its last record.
+ * If the header node is not needed to complete the appropriate
+ * operation (as determined by the 'keep_flags' field of 'brec') then
+ * it is released before this function returns.
+ * If either call to brec_bnode_find() fails, NULL is returned and the
+ * (struct hfs_brec) pointed to by 'brec' is invalid.
+ */
+struct hfs_belem *hfs_brec_init(struct hfs_brec *brec, struct hfs_btree *tree,
+ int flags)
+{
+ struct hfs_belem *head = &brec->elem[0];
+ struct hfs_belem *root = &brec->elem[1];
+ int lock_type = flags & HFS_LOCK_MASK;
+
+ brec->tree = tree;
+
+ head->bnr = hfs_bnode_find(tree, 0, lock_type);
+ if (!head->bnr.bn) {
+ return NULL;
+ }
+
+ root->bnr = hfs_bnode_find(tree, tree->bthRoot, lock_type);
+ if (!root->bnr.bn) {
+ hfs_bnode_relse(&head->bnr);
+ return NULL;
+ }
+
+ root->record = root->bnr.bn->ndNRecs;
+
+ brec->top = head;
+ brec->bottom = root;
+
+ brec->keep_flags = flags & HFS_BPATH_MASK;
+
+ /* HFS_BPATH_FIRST not applicable for root */
+ /* and HFS_BPATH_UNDERFLOW is different */
+ root->flags = overflow(tree, root->bnr.bn);
+ if (root->record < 3) {
+ root->flags |= HFS_BPATH_UNDERFLOW;
+ }
+
+ if (!(root->flags & brec->keep_flags)) {
+ hfs_brec_relse(brec, head);
+ }
+
+ return root;
+}
diff --git a/fs/hfs/btree.c b/fs/hfs/btree.c
new file mode 100644
index 00000000000000..bc23f94c26de52
--- /dev/null
+++ b/fs/hfs/btree.c
@@ -0,0 +1,324 @@
+/*
+ * linux/fs/hfs/btree.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU General Public License.
+ *
+ * This file contains the code to manipulate the B-tree structure.
+ * The catalog and extents files are both B-trees.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ *
+ * The code in this file initializes some structures which contain
+ * pointers by calling memset(&foo, 0, sizeof(foo)).
+ * This produces the desired behavior only due to the non-ANSI
+ * assumption that the machine representation of NULL is all zeros.
+ */
+
+#include "hfs_btree.h"
+
+/*================ File-local functions ================*/
+
+/*
+ * hfs_bnode_ditch()
+ *
+ * Description:
+ * This function deletes an entire linked list of bnodes, so it
+ * does not need to keep the linked list consistent as
+ * hfs_bnode_delete() does.
+ * Called by hfs_btree_init() for error cleanup and by hfs_btree_free().
+ * Input Variable(s):
+ * struct hfs_bnode *bn: pointer to the first (struct hfs_bnode) in
+ * the linked list to be deleted.
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * void
+ * Preconditions:
+ * 'bn' is NULL or points to a "valid" (struct hfs_bnode) with a 'prev'
+ * field of NULL.
+ * Postconditions:
+ * 'bn' and all (struct hfs_bnode)s in the chain of 'next' pointers
+ * are deleted, freeing the associated memory and hfs_buffer_put()ing
+ * the associated buffer.
+ */
+static void hfs_bnode_ditch(struct hfs_bnode *bn) {
+ struct hfs_bnode *tmp;
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+ extern int bnode_count;
+#endif
+
+ while (bn != NULL) {
+ tmp = bn->next;
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+ hfs_warn("deleting node %d from tree %d with count %d\n",
+ bn->node, (int)ntohl(bn->tree->entry.cnid), bn->count);
+ --bnode_count;
+#endif
+ hfs_buffer_put(bn->buf); /* safe: checks for NULL argument */
+
+ /* free all but the header */
+ if (bn->node) {
+ HFS_DELETE(bn);
+ }
+ bn = tmp;
+ }
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_btree_free()
+ *
+ * Description:
+ * This function frees a (struct hfs_btree) obtained from hfs_btree_init().
+ * Called by hfs_put_super().
+ * Input Variable(s):
+ * struct hfs_btree *bt: pointer to the (struct hfs_btree) to free
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * void
+ * Preconditions:
+ * 'bt' is NULL or points to a "valid" (struct hfs_btree)
+ * Postconditions:
+ * If 'bt' points to a "valid" (struct hfs_btree) then all (struct
+ * hfs_bnode)s associated with 'bt' are freed by calling
+ * hfs_bnode_ditch() and the memory associated with the (struct
+ * hfs_btree) is freed.
+ * If 'bt' is NULL or not "valid" an error is printed and nothing
+ * is changed.
+ */
+void hfs_btree_free(struct hfs_btree *bt)
+{
+ int lcv;
+
+ if (bt && (bt->magic == HFS_BTREE_MAGIC)) {
+ hfs_extent_free(&bt->entry.u.file.data_fork);
+
+ for (lcv=0; lcv<HFS_CACHELEN; ++lcv) {
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+ hfs_warn("deleting nodes from bucket %d:\n", lcv);
+#endif
+ hfs_bnode_ditch(bt->cache[lcv]);
+ }
+
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+ hfs_warn("deleting header and bitmap nodes\n");
+#endif
+ hfs_bnode_ditch(&bt->head);
+
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+ hfs_warn("deleting root node\n");
+#endif
+ hfs_bnode_ditch(bt->root);
+
+ HFS_DELETE(bt);
+ } else if (bt) {
+ hfs_warn("hfs_btree_free: corrupted hfs_btree.\n");
+ }
+}
+
+/*
+ * hfs_btree_init()
+ *
+ * Description:
+ * Given some vital information from the MDB (HFS superblock),
+ * initializes the fields of a (struct hfs_btree).
+ * Input Variable(s):
+ * struct hfs_mdb *mdb: pointer to the MDB
+ * ino_t cnid: the CNID (HFS_CAT_CNID or HFS_EXT_CNID) of the B-tree
+ * hfs_u32 tsize: the size, in bytes, of the B-tree
+ * hfs_u32 csize: the size, in bytes, of the clump size for the B-tree
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * (struct hfs_btree *): pointer to the initialized hfs_btree on success,
+ * or NULL on failure
+ * Preconditions:
+ * 'mdb' points to a "valid" (struct hfs_mdb)
+ * Postconditions:
+ * Assuming the inputs are what they claim to be, no errors occur
+ * reading from disk, and no inconsistencies are noticed in the data
+ * read from disk, the return value is a pointer to a "valid"
+ * (struct hfs_btree). If there are errors reading from disk or
+ * inconsistencies are noticed in the data read from disk, then and
+ * all resources that were allocated are released and NULL is
+ * returned. If the inputs are not what they claim to be or if they
+ * are unnoticed inconsistencies in the data read from disk then the
+ * returned hfs_btree is probably going to lead to errors when it is
+ * used in a non-trivial way.
+ */
+struct hfs_btree * hfs_btree_init(struct hfs_mdb *mdb, ino_t cnid,
+ hfs_byte_t ext[12],
+ hfs_u32 tsize, hfs_u32 csize)
+{
+ struct hfs_btree * bt;
+ struct BTHdrRec * th;
+ struct hfs_bnode * tmp;
+ unsigned int next;
+#if defined(DEBUG_HEADER) || defined(DEBUG_ALL)
+ unsigned char *p, *q;
+#endif
+
+ if (!mdb || !ext || !HFS_NEW(bt)) {
+ goto bail3;
+ }
+
+ bt->magic = HFS_BTREE_MAGIC;
+ bt->sys_mdb = mdb->sys_mdb;
+ bt->reserved = 0;
+ bt->lock = 0;
+ hfs_init_waitqueue(&bt->wait);
+ bt->dirt = 0;
+ memset(bt->cache, 0, sizeof(bt->cache));
+
+#if 0 /* this is a fake entry. so we don't need to initialize it. */
+ memset(&bt->entry, 0, sizeof(bt->entry));
+ hfs_init_waitqueue(&bt->entry.wait);
+ INIT_LIST_HEAD(&bt->entry.hash);
+ INIT_LIST_HEAD(&bt->entry.list);
+#endif
+
+ bt->entry.mdb = mdb;
+ bt->entry.cnid = cnid;
+ bt->entry.type = HFS_CDR_FIL;
+ bt->entry.u.file.magic = HFS_FILE_MAGIC;
+ bt->entry.u.file.clumpablks = (csize / mdb->alloc_blksz)
+ >> HFS_SECTOR_SIZE_BITS;
+ bt->entry.u.file.data_fork.entry = &bt->entry;
+ bt->entry.u.file.data_fork.lsize = tsize;
+ bt->entry.u.file.data_fork.psize = tsize >> HFS_SECTOR_SIZE_BITS;
+ bt->entry.u.file.data_fork.fork = HFS_FK_DATA;
+ hfs_extent_in(&bt->entry.u.file.data_fork, ext);
+
+ hfs_bnode_read(&bt->head, bt, 0, HFS_STICKY);
+ if (!hfs_buffer_ok(bt->head.buf)) {
+ goto bail2;
+ }
+ th = (struct BTHdrRec *)((char *)hfs_buffer_data(bt->head.buf) +
+ sizeof(struct NodeDescriptor));
+
+ /* read in the bitmap nodes (if any) */
+ tmp = &bt->head;
+ while ((next = tmp->ndFLink)) {
+ if (!HFS_NEW(tmp->next)) {
+ goto bail2;
+ }
+ hfs_bnode_read(tmp->next, bt, next, HFS_STICKY);
+ if (!hfs_buffer_ok(tmp->next->buf)) {
+ goto bail2;
+ }
+ tmp->next->prev = tmp;
+ tmp = tmp->next;
+ }
+
+ if (hfs_get_ns(th->bthNodeSize) != htons(HFS_SECTOR_SIZE)) {
+ hfs_warn("hfs_btree_init: bthNodeSize!=512 not supported\n");
+ goto bail2;
+ }
+
+ if (cnid == htonl(HFS_CAT_CNID)) {
+ bt->compare = (hfs_cmpfn)hfs_cat_compare;
+ } else if (cnid == htonl(HFS_EXT_CNID)) {
+ bt->compare = (hfs_cmpfn)hfs_ext_compare;
+ } else {
+ goto bail2;
+ }
+ bt->bthDepth = hfs_get_hs(th->bthDepth);
+ bt->bthRoot = hfs_get_hl(th->bthRoot);
+ bt->bthNRecs = hfs_get_hl(th->bthNRecs);
+ bt->bthFNode = hfs_get_hl(th->bthFNode);
+ bt->bthLNode = hfs_get_hl(th->bthLNode);
+ bt->bthNNodes = hfs_get_hl(th->bthNNodes);
+ bt->bthFree = hfs_get_hl(th->bthFree);
+ bt->bthKeyLen = hfs_get_hs(th->bthKeyLen);
+
+#if defined(DEBUG_HEADER) || defined(DEBUG_ALL)
+ hfs_warn("bthDepth %d\n", bt->bthDepth);
+ hfs_warn("bthRoot %d\n", bt->bthRoot);
+ hfs_warn("bthNRecs %d\n", bt->bthNRecs);
+ hfs_warn("bthFNode %d\n", bt->bthFNode);
+ hfs_warn("bthLNode %d\n", bt->bthLNode);
+ hfs_warn("bthKeyLen %d\n", bt->bthKeyLen);
+ hfs_warn("bthNNodes %d\n", bt->bthNNodes);
+ hfs_warn("bthFree %d\n", bt->bthFree);
+ p = (unsigned char *)hfs_buffer_data(bt->head.buf);
+ q = p + HFS_SECTOR_SIZE;
+ while (p < q) {
+ hfs_warn("%02x %02x %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x %02x %02x\n",
+ *p++, *p++, *p++, *p++, *p++, *p++, *p++, *p++,
+ *p++, *p++, *p++, *p++, *p++, *p++, *p++, *p++);
+ }
+#endif
+
+ /* Read in the root if it exists.
+ The header always exists, but the root exists only if the
+ tree is non-empty */
+ if (bt->bthDepth && bt->bthRoot) {
+ if (!HFS_NEW(bt->root)) {
+ goto bail2;
+ }
+ hfs_bnode_read(bt->root, bt, bt->bthRoot, HFS_STICKY);
+ if (!hfs_buffer_ok(bt->root->buf)) {
+ goto bail1;
+ }
+ } else {
+ bt->root = NULL;
+ }
+
+ return bt;
+
+ bail1:
+ hfs_bnode_ditch(bt->root);
+ bail2:
+ hfs_bnode_ditch(&bt->head);
+ HFS_DELETE(bt);
+ bail3:
+ return NULL;
+}
+
+/*
+ * hfs_btree_commit()
+ *
+ * Called to write a possibly dirty btree back to disk.
+ */
+void hfs_btree_commit(struct hfs_btree *bt, hfs_byte_t ext[12], hfs_lword_t size)
+{
+ if (bt->dirt) {
+ struct BTHdrRec *th;
+ th = (struct BTHdrRec *)((char *)hfs_buffer_data(bt->head.buf) +
+ sizeof(struct NodeDescriptor));
+
+ hfs_put_hs(bt->bthDepth, th->bthDepth);
+ hfs_put_hl(bt->bthRoot, th->bthRoot);
+ hfs_put_hl(bt->bthNRecs, th->bthNRecs);
+ hfs_put_hl(bt->bthFNode, th->bthFNode);
+ hfs_put_hl(bt->bthLNode, th->bthLNode);
+ hfs_put_hl(bt->bthNNodes, th->bthNNodes);
+ hfs_put_hl(bt->bthFree, th->bthFree);
+ hfs_buffer_dirty(bt->head.buf);
+
+ /*
+ * Commit the bnodes which are not cached.
+ * The map nodes don't need to be committed here because
+ * they are committed every time they are changed.
+ */
+ hfs_bnode_commit(&bt->head);
+ if (bt->root) {
+ hfs_bnode_commit(bt->root);
+ }
+
+
+ hfs_put_hl(bt->bthNNodes << HFS_SECTOR_SIZE_BITS, size);
+ hfs_extent_out(&bt->entry.u.file.data_fork, ext);
+ /* hfs_buffer_dirty(mdb->buf); (Done by caller) */
+
+ bt->dirt = 0;
+ }
+}
diff --git a/fs/hfs/catalog.c b/fs/hfs/catalog.c
new file mode 100644
index 00000000000000..3bcdaae4e024e8
--- /dev/null
+++ b/fs/hfs/catalog.c
@@ -0,0 +1,1592 @@
+/*
+ * linux/fs/hfs/catalog.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU General Public License.
+ *
+ * This file contains the functions related to the catalog B-tree.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * Cache code shamelessly stolen from
+ * linux/fs/inode.c Copyright (C) 1991, 1992 Linus Torvalds
+ * re-shamelessly stolen Copyright (C) 1997 Linus Torvalds
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ *
+ * The code in this file initializes some structures by calling
+ * memset(&foo, 0, sizeof(foo)). This produces the desired behavior
+ * only due to the non-ANSI assumption that the machine representation
+ */
+
+#include "hfs.h"
+
+/*================ Variable-like macros ================*/
+
+/* Number of hash table slots */
+#define C_HASHBITS 10
+#define C_HASHSIZE (1UL << C_HASHBITS)
+#define C_HASHMASK (C_HASHSIZE - 1)
+
+/* Number of entries to fit in a single page on an i386.
+ * Actually, now it's used to increment the free entry pool. */
+#define CCACHE_INC (PAGE_SIZE/sizeof(struct hfs_cat_entry))
+#define CCACHE_MAX (CCACHE_INC * 8)
+
+/*================ File-local data types ================*/
+
+/* The catalog record for a file */
+typedef struct {
+ hfs_byte_t Flags; /* Flags such as read-only */
+ hfs_byte_t Typ; /* file version number = 0 */
+ hfs_finfo_t UsrWds; /* data used by the Finder */
+ hfs_lword_t FlNum; /* The CNID */
+ hfs_word_t StBlk; /* obsolete */
+ hfs_lword_t LgLen; /* The logical EOF of the data fork*/
+ hfs_lword_t PyLen; /* The physical EOF of the data fork */
+ hfs_word_t RStBlk; /* obsolete */
+ hfs_lword_t RLgLen; /* The logical EOF of the rsrc fork */
+ hfs_lword_t RPyLen; /* The physical EOF of the rsrc fork */
+ hfs_lword_t CrDat; /* The creation date */
+ hfs_lword_t MdDat; /* The modified date */
+ hfs_lword_t BkDat; /* The last backup date */
+ hfs_fxinfo_t FndrInfo; /* more data for the Finder */
+ hfs_word_t ClpSize; /* number of bytes to allocate
+ when extending files */
+ hfs_byte_t ExtRec[12]; /* first extent record
+ for the data fork */
+ hfs_byte_t RExtRec[12]; /* first extent record
+ for the resource fork */
+ hfs_lword_t Resrv; /* reserved by Apple */
+} __attribute__((packed)) FIL_REC;
+
+/* the catalog record for a directory */
+typedef struct {
+ hfs_word_t Flags; /* flags */
+ hfs_word_t Val; /* Valence: number of files and
+ dirs in the directory */
+ hfs_lword_t DirID; /* The CNID */
+ hfs_lword_t CrDat; /* The creation date */
+ hfs_lword_t MdDat; /* The modification date */
+ hfs_lword_t BkDat; /* The last backup date */
+ hfs_dinfo_t UsrInfo; /* data used by the Finder */
+ hfs_dxinfo_t FndrInfo; /* more data used by Finder */
+ hfs_byte_t Resrv[16]; /* reserved by Apple */
+} __attribute__((packed)) DIR_REC;
+
+/* the catalog record for a thread */
+typedef struct {
+ hfs_byte_t Reserv[8]; /* reserved by Apple */
+ hfs_lword_t ParID; /* CNID of parent directory */
+ struct hfs_name CName; /* The name of this entry */
+} __attribute__((packed)) THD_REC;
+
+/* A catalog tree record */
+struct hfs_cat_rec {
+ hfs_byte_t cdrType; /* The type of entry */
+ hfs_byte_t cdrResrv2; /* padding */
+ union {
+ FIL_REC fil;
+ DIR_REC dir;
+ THD_REC thd;
+ } u;
+} __attribute__((packed));
+
+/*================ File-local variables ================*/
+
+static LIST_HEAD(entry_in_use);
+static LIST_HEAD(entry_unused);
+static struct list_head hash_table[C_HASHSIZE];
+
+static spinlock_t entry_lock = SPIN_LOCK_UNLOCKED;
+
+static struct {
+ int nr_entries;
+ int nr_free_entries;
+} entries_stat;
+
+/*================ File-local functions ================*/
+
+/*
+ * brec_to_id
+ *
+ * Get the CNID from a brec
+ */
+static inline hfs_u32 brec_to_id(struct hfs_brec *brec)
+{
+ struct hfs_cat_rec *rec = brec->data;
+
+ return hfs_get_nl((rec->cdrType==HFS_CDR_FIL) ?
+ rec->u.fil.FlNum : rec->u.dir.DirID);
+}
+
+/*
+ * hashfn()
+ *
+ * hash an (struct mdb *) and a (struct hfs_cat_key *) to an integer.
+ */
+static inline unsigned int hashfn(const struct hfs_mdb *mdb,
+ const struct hfs_cat_key *key)
+{
+ unsigned int hash;
+
+ hash = (unsigned long) mdb | (unsigned long) key->ParID[3] |
+ hfs_strhash(key->CName.Name, key->CName.Len);
+ hash = hash ^ (hash >> C_HASHBITS) ^ (hash >> C_HASHBITS*2);
+ return hash & C_HASHMASK;
+}
+
+/*
+ * hash()
+ *
+ * hash an (struct mdb *) and a (struct hfs_cat_key *)
+ * to a pointer to a slot in the hash table.
+ */
+static inline struct list_head *hash(struct hfs_mdb *mdb,
+ const struct hfs_cat_key *key)
+{
+ return hash_table + hashfn(mdb, key);
+}
+
+static inline void insert_hash(struct hfs_cat_entry *entry)
+{
+ struct list_head *head = hash(entry->mdb, &entry->key);
+ list_add(&entry->hash, head);
+}
+
+static inline void remove_hash(struct hfs_cat_entry *entry)
+{
+ list_del(&entry->hash);
+ INIT_LIST_HEAD(&entry->hash);
+}
+
+/*
+ * wait_on_entry()
+ *
+ * Sleep until a locked entry is unlocked.
+ */
+static inline void wait_on_entry(struct hfs_cat_entry * entry)
+{
+ while ((entry->state & HFS_LOCK)) {
+ hfs_sleep_on(&entry->wait);
+ }
+}
+
+/*
+ * lock_entry()
+ *
+ * Obtain an exclusive lock on an entry.
+ */
+static void lock_entry(struct hfs_cat_entry * entry)
+{
+ wait_on_entry(entry);
+ spin_lock(&entry_lock);
+ entry->state |= HFS_LOCK;
+ spin_unlock(&entry_lock);
+}
+
+/*
+ * lock_entry()
+ *
+ * Relinquish an exclusive lock on an entry.
+ */
+static void unlock_entry(struct hfs_cat_entry * entry)
+{
+ spin_lock(&entry_lock);
+ entry->state &= ~HFS_LOCK;
+ spin_unlock(&entry_lock);
+ hfs_wake_up(&entry->wait);
+}
+
+/* put entry on mdb dirty list. */
+void hfs_cat_mark_dirty(struct hfs_cat_entry *entry)
+{
+ struct hfs_mdb *mdb = entry->mdb;
+
+ spin_lock(&entry_lock);
+ if (!(entry->state & HFS_DIRTY)) {
+ entry->state |= HFS_DIRTY;
+
+ /* Only add valid (ie hashed) entries to the dirty list. */
+ if (!list_empty(&entry->hash)) {
+ list_del(&entry->list);
+ list_add(&entry->list, &mdb->entry_dirty);
+ }
+ }
+ spin_unlock(&entry_lock);
+}
+
+/* delete an entry and remove it from the hash table. */
+static void delete_entry(struct hfs_cat_entry *entry)
+{
+ if (!(entry->state & HFS_DELETED)) {
+ entry->state |= HFS_DELETED;
+ list_del(&entry->hash);
+ INIT_LIST_HEAD(&entry->hash);
+
+ if (entry->type == HFS_CDR_FIL) {
+ /* free all extents */
+ entry->u.file.data_fork.lsize = 0;
+ hfs_extent_adj(&entry->u.file.data_fork);
+ entry->u.file.rsrc_fork.lsize = 0;
+ hfs_extent_adj(&entry->u.file.rsrc_fork);
+ }
+ }
+}
+
+
+static inline void init_entry(struct hfs_cat_entry *entry)
+{
+ memset(entry, 0, sizeof(*entry));
+ hfs_init_waitqueue(&entry->wait);
+ INIT_LIST_HEAD(&entry->hash);
+ INIT_LIST_HEAD(&entry->list);
+}
+
+/*
+ * hfs_cat_alloc()
+ *
+ * Try to allocate another entry.
+ */
+static inline struct hfs_cat_entry *hfs_cat_alloc(void)
+{
+ struct hfs_cat_entry *entry;
+
+ if (!HFS_NEW(entry))
+ return NULL;
+
+ init_entry(entry);
+ return entry;
+}
+
+/* this gets called with the spinlock held. */
+static int grow_entries(void)
+{
+ struct hfs_cat_entry *entry;
+ int i;
+
+ for (i = 0; i < CCACHE_INC; i++) {
+ if (!(entry = hfs_cat_alloc()))
+ break;
+ list_add(&entry->list, &entry_unused);
+ }
+
+ entries_stat.nr_entries += i;
+ entries_stat.nr_free_entries += i;
+
+ return i;
+}
+
+/*
+ * __read_entry()
+ *
+ * Convert a (struct hfs_cat_rec) to a (struct hfs_cat_entry).
+ */
+static void __read_entry(struct hfs_cat_entry *entry,
+ const struct hfs_cat_rec *cat)
+{
+ entry->type = cat->cdrType;
+
+ if (cat->cdrType == HFS_CDR_DIR) {
+ struct hfs_dir *dir = &entry->u.dir;
+
+ entry->cnid = hfs_get_nl(cat->u.dir.DirID);
+
+ dir->magic = HFS_DIR_MAGIC;
+ dir->flags = hfs_get_ns(cat->u.dir.Flags);
+ memcpy(&entry->info.dir.dinfo, &cat->u.dir.UsrInfo, 16);
+ memcpy(&entry->info.dir.dxinfo, &cat->u.dir.FndrInfo, 16);
+ entry->create_date = hfs_get_nl(cat->u.dir.CrDat);
+ entry->modify_date = hfs_get_nl(cat->u.dir.MdDat);
+ entry->backup_date = hfs_get_nl(cat->u.dir.BkDat);
+ dir->dirs = dir->files = 0;
+ hfs_init_waitqueue(&dir->read_wait);
+ hfs_init_waitqueue(&dir->write_wait);
+ } else if (cat->cdrType == HFS_CDR_FIL) {
+ struct hfs_file *fil = &entry->u.file;
+
+ entry->cnid = hfs_get_nl(cat->u.fil.FlNum);
+
+ fil->magic = HFS_FILE_MAGIC;
+
+ fil->data_fork.fork = HFS_FK_DATA;
+ fil->data_fork.entry = entry;
+ fil->data_fork.lsize = hfs_get_hl(cat->u.fil.LgLen);
+ fil->data_fork.psize = hfs_get_hl(cat->u.fil.PyLen) >>
+ HFS_SECTOR_SIZE_BITS;
+ hfs_extent_in(&fil->data_fork, cat->u.fil.ExtRec);
+
+ fil->rsrc_fork.fork = HFS_FK_RSRC;
+ fil->rsrc_fork.entry = entry;
+ fil->rsrc_fork.lsize = hfs_get_hl(cat->u.fil.RLgLen);
+ fil->rsrc_fork.psize = hfs_get_hl(cat->u.fil.RPyLen) >>
+ HFS_SECTOR_SIZE_BITS;
+ hfs_extent_in(&fil->rsrc_fork, cat->u.fil.RExtRec);
+
+ memcpy(&entry->info.file.finfo, &cat->u.fil.UsrWds, 16);
+ memcpy(&entry->info.file.fxinfo, &cat->u.fil.FndrInfo, 16);
+
+ entry->create_date = hfs_get_nl(cat->u.fil.CrDat);
+ entry->modify_date = hfs_get_nl(cat->u.fil.MdDat);
+ entry->backup_date = hfs_get_nl(cat->u.fil.BkDat);
+ fil->clumpablks = (hfs_get_hs(cat->u.fil.ClpSize)
+ / entry->mdb->alloc_blksz)
+ >> HFS_SECTOR_SIZE_BITS;
+ fil->flags = cat->u.fil.Flags;
+ } else {
+ hfs_warn("hfs_fs: entry is neither file nor directory!\n");
+ }
+}
+
+/*
+ * count_dir_entries()
+ *
+ * Count the number of files and directories in a given directory.
+ */
+static inline void count_dir_entries(struct hfs_cat_entry *entry,
+ struct hfs_brec *brec)
+{
+ int error = 0;
+ hfs_u32 cnid;
+ hfs_u8 type;
+
+ if (!hfs_cat_open(entry, brec)) {
+ while (!(error = hfs_cat_next(entry, brec, 1, &cnid, &type))) {
+ if (type == HFS_CDR_FIL) {
+ ++entry->u.dir.files;
+ } else if (type == HFS_CDR_DIR) {
+ ++entry->u.dir.dirs;
+ }
+ } /* -ENOENT is normal termination */
+ }
+ if (error != -ENOENT) {
+ entry->cnid = 0;
+ }
+}
+
+/*
+ * read_entry()
+ *
+ * Convert a (struct hfs_brec) to a (struct hfs_cat_entry).
+ */
+static inline void read_entry(struct hfs_cat_entry *entry,
+ struct hfs_brec *brec)
+{
+ int need_count;
+ struct hfs_cat_rec *rec = brec->data;
+
+ __read_entry(entry, rec);
+
+ need_count = (rec->cdrType == HFS_CDR_DIR) && rec->u.dir.Val;
+
+ hfs_brec_relse(brec, NULL);
+
+ if (need_count) {
+ count_dir_entries(entry, brec);
+ }
+}
+
+/*
+ * __write_entry()
+ *
+ * Convert a (struct hfs_cat_entry) to a (struct hfs_cat_rec).
+ */
+static void __write_entry(const struct hfs_cat_entry *entry,
+ struct hfs_cat_rec *cat)
+{
+ if (entry->type == HFS_CDR_DIR) {
+ const struct hfs_dir *dir = &entry->u.dir;
+
+ hfs_put_ns(dir->flags, cat->u.dir.Flags);
+ hfs_put_hs(dir->dirs + dir->files, cat->u.dir.Val);
+ hfs_put_nl(entry->cnid, cat->u.dir.DirID);
+ hfs_put_nl(entry->create_date, cat->u.dir.CrDat);
+ hfs_put_nl(entry->modify_date, cat->u.dir.MdDat);
+ hfs_put_nl(entry->backup_date, cat->u.dir.BkDat);
+ memcpy(&cat->u.dir.UsrInfo, &entry->info.dir.dinfo, 16);
+ memcpy(&cat->u.dir.FndrInfo, &entry->info.dir.dxinfo, 16);
+ } else if (entry->type == HFS_CDR_FIL) {
+ const struct hfs_file *fil = &entry->u.file;
+
+ cat->u.fil.Flags = fil->flags;
+ hfs_put_nl(entry->cnid, cat->u.fil.FlNum);
+ memcpy(&cat->u.fil.UsrWds, &entry->info.file.finfo, 16);
+ hfs_put_hl(fil->data_fork.lsize, cat->u.fil.LgLen);
+ hfs_put_hl(fil->data_fork.psize << HFS_SECTOR_SIZE_BITS,
+ cat->u.fil.PyLen);
+ hfs_put_hl(fil->rsrc_fork.lsize, cat->u.fil.RLgLen);
+ hfs_put_hl(fil->rsrc_fork.psize << HFS_SECTOR_SIZE_BITS,
+ cat->u.fil.RPyLen);
+ hfs_put_nl(entry->create_date, cat->u.fil.CrDat);
+ hfs_put_nl(entry->modify_date, cat->u.fil.MdDat);
+ hfs_put_nl(entry->backup_date, cat->u.fil.BkDat);
+ memcpy(&cat->u.fil.FndrInfo, &entry->info.file.fxinfo, 16);
+ hfs_put_hs((fil->clumpablks * entry->mdb->alloc_blksz)
+ << HFS_SECTOR_SIZE_BITS, cat->u.fil.ClpSize);
+ hfs_extent_out(&fil->data_fork, cat->u.fil.ExtRec);
+ hfs_extent_out(&fil->rsrc_fork, cat->u.fil.RExtRec);
+ } else {
+ hfs_warn("__write_entry: invalid entry\n");
+ }
+}
+
+/*
+ * write_entry()
+ *
+ * Write a modified entry back to the catalog B-tree. this gets called
+ * with the entry locked.
+ */
+static void write_entry(struct hfs_cat_entry * entry)
+{
+ struct hfs_brec brec;
+ int error;
+
+ if (!(entry->state & HFS_DELETED)) {
+ error = hfs_bfind(&brec, entry->mdb->cat_tree,
+ HFS_BKEY(&entry->key), HFS_BFIND_WRITE);
+ if (!error) {
+ if ((entry->state & HFS_KEYDIRTY)) {
+ /* key may have changed case due to a rename */
+ entry->state &= ~HFS_KEYDIRTY;
+ if (brec.key->KeyLen != entry->key.KeyLen) {
+ hfs_warn("hfs_write_entry: key length "
+ "changed!\n");
+ error = 1;
+ } else {
+ memcpy(brec.key, &entry->key,
+ entry->key.KeyLen);
+ }
+ } else if (entry->cnid != brec_to_id(&brec)) {
+ hfs_warn("hfs_write_entry: CNID "
+ "changed unexpectedly!\n");
+ error = 1;
+ }
+ if (!error) {
+ __write_entry(entry, brec.data);
+ }
+ hfs_brec_relse(&brec, NULL);
+ }
+ if (error) {
+ hfs_warn("hfs_write_entry: unable to write "
+ "entry %08x\n", entry->cnid);
+ }
+ }
+}
+
+
+/* this gets called with the spinlock held. */
+static struct hfs_cat_entry *find_entry(struct hfs_mdb *mdb,
+ const struct hfs_cat_key *key)
+{
+ struct list_head *tmp, *head = hash(mdb, key);
+ struct hfs_cat_entry * entry;
+
+ tmp = head;
+ for (;;) {
+ tmp = tmp->next;
+ entry = NULL;
+ if (tmp == head)
+ break;
+ entry = list_entry(tmp, struct hfs_cat_entry, hash);
+ if (entry->mdb != mdb)
+ continue;
+ if (hfs_cat_compare(&entry->key, key)) {
+ continue;
+ }
+ entry->count++;
+ break;
+ }
+
+ return entry;
+}
+
+
+/* be careful. this gets called with the spinlock held. */
+static struct hfs_cat_entry *get_new_entry(struct hfs_mdb *mdb,
+ const struct hfs_cat_key *key,
+ const int read)
+{
+ struct hfs_cat_entry *entry;
+ struct list_head *head = hash(mdb, key);
+ struct list_head *tmp;
+
+add_new_entry:
+ tmp = entry_unused.next;
+ if ((tmp != &entry_unused) ) {
+ list_del(tmp);
+ entries_stat.nr_free_entries--;
+ entry = list_entry(tmp, struct hfs_cat_entry, list);
+ list_add(&entry->list, &entry_in_use);
+ list_add(&entry->hash, head);
+ entry->mdb = mdb;
+ entry->count = 1;
+ memcpy(&entry->key, key, sizeof(*key));
+ entry->state = HFS_LOCK;
+ spin_unlock(&entry_lock);
+
+ if (read) {
+ struct hfs_brec brec;
+
+ if (hfs_bfind(&brec, mdb->cat_tree,
+ HFS_BKEY(key), HFS_BFIND_READ_EQ)) {
+ /* uh oh. we failed to read the record.
+ * the entry doesn't actually exist. */
+ goto read_fail;
+ }
+
+ read_entry(entry, &brec);
+
+ /* error */
+ if (!entry->cnid) {
+ goto read_fail;
+ }
+
+ /* we don't have to acquire a spinlock here or
+ * below for the unlocking bits as we're the first
+ * user of this entry. */
+ entry->state &= ~HFS_LOCK;
+ hfs_wake_up(&entry->wait);
+ }
+
+ return entry;
+ }
+
+
+ /* try to allocate more entries. grow_entries() doesn't release
+ * the spinlock. */
+ if (grow_entries())
+ goto add_new_entry;
+
+ spin_unlock(&entry_lock);
+ return NULL;
+
+read_fail:
+ /* short-cut hfs_cat_put by doing everything here. */
+ spin_lock(&entry_lock);
+ list_del(&entry->hash);
+ list_del(&entry->list);
+ init_entry(entry);
+ list_add(&entry->list, &entry_unused);
+ entries_stat.nr_free_entries++;
+ spin_unlock(&entry_lock);
+ return NULL;
+}
+
+/*
+ * get_entry()
+ *
+ * Try to return an entry for the indicated file or directory.
+ * If ('read' == 0) then no attempt will be made to read it from disk
+ * and a locked, but uninitialized, entry is returned.
+ */
+static struct hfs_cat_entry *get_entry(struct hfs_mdb *mdb,
+ const struct hfs_cat_key *key,
+ const int read)
+{
+ struct hfs_cat_entry * entry;
+
+#if defined(DEBUG_CATALOG) || defined(DEBUG_ALL)
+ hfs_warn("hfs_get_entry: mdb=%p key=%s read=%d\n",
+ mdb, key->CName.Name, read);
+#endif
+
+ spin_lock(&entry_lock);
+ entry = find_entry(mdb, key);
+ if (!entry) {
+ return get_new_entry(mdb, key, read);
+ }
+ spin_unlock(&entry_lock);
+ wait_on_entry(entry);
+ return entry;
+}
+
+/*
+ * new_cnid()
+ *
+ * Allocate a CNID to use for a new file or directory.
+ */
+static inline hfs_u32 new_cnid(struct hfs_mdb *mdb)
+{
+ /* If the create succeeds then the mdb will get dirtied */
+ return htonl(mdb->next_id++);
+}
+
+/*
+ * update_dir()
+ *
+ * Update counts, times and dirt on a changed directory
+ */
+static void update_dir(struct hfs_mdb *mdb, struct hfs_cat_entry *dir,
+ int is_dir, int count)
+{
+ /* update counts */
+ if (is_dir) {
+ mdb->dir_count += count;
+ dir->u.dir.dirs += count;
+ if (dir->cnid == htonl(HFS_ROOT_CNID)) {
+ mdb->root_dirs += count;
+ }
+ } else {
+ mdb->file_count += count;
+ dir->u.dir.files += count;
+ if (dir->cnid == htonl(HFS_ROOT_CNID)) {
+ mdb->root_files += count;
+ }
+ }
+
+ /* update times and dirt */
+ dir->modify_date = hfs_time();
+ hfs_cat_mark_dirty(dir);
+}
+
+/*
+ * Add a writer to dir, excluding readers.
+ *
+ * XXX: this is wrong. it allows a move to occur when a directory
+ * is being written to.
+ */
+static inline void start_write(struct hfs_cat_entry *dir)
+{
+ if (dir->u.dir.readers || waitqueue_active(&dir->u.dir.read_wait)) {
+ hfs_sleep_on(&dir->u.dir.write_wait);
+ }
+ ++dir->u.dir.writers;
+}
+
+/*
+ * Add a reader to dir, excluding writers.
+ */
+static inline void start_read(struct hfs_cat_entry *dir)
+{
+ if (dir->u.dir.writers || waitqueue_active(&dir->u.dir.write_wait)) {
+ hfs_sleep_on(&dir->u.dir.read_wait);
+ }
+ ++dir->u.dir.readers;
+}
+
+/*
+ * Remove a writer from dir, possibly admitting readers.
+ */
+static inline void end_write(struct hfs_cat_entry *dir)
+{
+ if (!(--dir->u.dir.writers)) {
+ hfs_wake_up(&dir->u.dir.read_wait);
+ }
+}
+
+/*
+ * Remove a reader from dir, possibly admitting writers.
+ */
+static inline void end_read(struct hfs_cat_entry *dir)
+{
+ if (!(--dir->u.dir.readers)) {
+ hfs_wake_up(&dir->u.dir.write_wait);
+ }
+}
+
+/*
+ * create_entry()
+ *
+ * Add a new file or directory to the catalog B-tree and
+ * return a (struct hfs_cat_entry) for it in '*result'.
+ */
+static int create_entry(struct hfs_cat_entry *parent, struct hfs_cat_key *key,
+ const struct hfs_cat_rec *record, int is_dir,
+ hfs_u32 cnid, struct hfs_cat_entry **result)
+{
+ struct hfs_mdb *mdb = parent->mdb;
+ struct hfs_cat_entry *entry;
+ struct hfs_cat_key thd_key;
+ struct hfs_cat_rec thd_rec;
+ int error, has_thread;
+
+ if (result) {
+ *result = NULL;
+ }
+
+ /* keep readers from getting confused by changing dir size */
+ start_write(parent);
+
+ /* create a locked entry in the cache */
+ entry = get_entry(mdb, key, 0);
+ if (!entry) {
+ /* The entry exists but can't be read */
+ error = -EIO;
+ goto done;
+ }
+
+ if (entry->cnid) {
+ /* The (unlocked) entry exists in the cache */
+ error = -EEXIST;
+ goto bail2;
+ }
+
+ /* limit directory valence to signed 16-bit integer */
+ if ((parent->u.dir.dirs + parent->u.dir.files) >= HFS_MAX_VALENCE) {
+ error = -ENOSPC;
+ goto bail1;
+ }
+
+ has_thread = is_dir || (record->u.fil.Flags & HFS_FIL_THD);
+
+ if (has_thread) {
+ /* init some fields for the thread record */
+ memset(&thd_rec, 0, sizeof(thd_rec));
+ thd_rec.cdrType = is_dir ? HFS_CDR_THD : HFS_CDR_FTH;
+ memcpy(&thd_rec.u.thd.ParID, &key->ParID,
+ sizeof(hfs_u32) + sizeof(struct hfs_name));
+
+ /* insert the thread record */
+ hfs_cat_build_key(cnid, NULL, &thd_key);
+ error = hfs_binsert(mdb->cat_tree, HFS_BKEY(&thd_key),
+ &thd_rec, 2 + sizeof(THD_REC));
+ if (error) {
+ goto bail1;
+ }
+ }
+
+ /* insert the record */
+ error = hfs_binsert(mdb->cat_tree, HFS_BKEY(key), record,
+ is_dir ? 2 + sizeof(DIR_REC) :
+ 2 + sizeof(FIL_REC));
+ if (error) {
+ if (has_thread && (error != -EIO)) {
+ /* at least TRY to remove the thread record */
+ (void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(&thd_key));
+ }
+ goto bail1;
+ }
+
+ /* update the parent directory */
+ update_dir(mdb, parent, is_dir, 1);
+
+ /* complete the cache entry and return success */
+ __read_entry(entry, record);
+ unlock_entry(entry);
+
+ if (result) {
+ *result = entry;
+ } else {
+ hfs_cat_put(entry);
+ }
+ goto done;
+
+bail1:
+ /* entry really didn't exist, so we don't need to really delete it.
+ * we do need to remove it from the hash, though. */
+ entry->state |= HFS_DELETED;
+ remove_hash(entry);
+ unlock_entry(entry);
+bail2:
+ hfs_cat_put(entry);
+done:
+ end_write(parent);
+ return error;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_cat_put()
+ *
+ * Release an entry we aren't using anymore.
+ *
+ * nothing in hfs_cat_put goes to sleep now except on the initial entry.
+ */
+void hfs_cat_put(struct hfs_cat_entry * entry)
+{
+ if (entry) {
+ wait_on_entry(entry);
+
+ /* just in case. this should never happen. */
+ if (!entry->count) {
+ hfs_warn("hfs_cat_put: trying to free free entry: %p\n",
+ entry);
+ return;
+ }
+
+#if defined(DEBUG_CATALOG) || defined(DEBUG_ALL)
+ hfs_warn("hfs_cat_put: %p(%u) type=%d state=%lu\n",
+ entry, entry->count, entry->type, entry->state);
+#endif
+ spin_lock(&entry_lock);
+ if (!--entry->count) {
+ if ((entry->state & HFS_DELETED))
+ goto entry_deleted;
+
+ if ((entry->type == HFS_CDR_FIL)) {
+ /* clear out any cached extents */
+ if (entry->u.file.data_fork.first.next) {
+ hfs_extent_free(&entry->u.file.data_fork);
+ }
+ if (entry->u.file.rsrc_fork.first.next) {
+ hfs_extent_free(&entry->u.file.rsrc_fork);
+ }
+ }
+
+ /* if we put a dirty entry, write it out. */
+ if ((entry->state & HFS_DIRTY)) {
+ entry->state ^= HFS_DIRTY | HFS_LOCK;
+ write_entry(entry);
+ entry->state &= ~HFS_LOCK;
+ }
+
+ list_del(&entry->hash);
+entry_deleted: /* deleted entries have already been removed
+ * from the hash list. */
+ list_del(&entry->list);
+ if (entries_stat.nr_free_entries > CCACHE_MAX) {
+ HFS_DELETE(entry);
+ entries_stat.nr_entries--;
+ } else {
+ init_entry(entry);
+ list_add(&entry->list, &entry_unused);
+ entries_stat.nr_free_entries++;
+ }
+ }
+ spin_unlock(&entry_lock);
+ }
+}
+
+/*
+ * hfs_cat_get()
+ *
+ * Wrapper for get_entry() which always calls with ('read'==1).
+ * Used for access to get_entry() from outside this file.
+ */
+struct hfs_cat_entry *hfs_cat_get(struct hfs_mdb *mdb,
+ const struct hfs_cat_key *key)
+{
+ return get_entry(mdb, key, 1);
+}
+
+/* invalidate all entries for a device */
+static void invalidate_list(struct list_head *head, struct hfs_mdb *mdb,
+ struct list_head *dispose)
+{
+ struct list_head *next;
+
+ next = head->next;
+ for (;;) {
+ struct list_head *tmp = next;
+ struct hfs_cat_entry * entry;
+
+ next = next->next;
+ if (tmp == head)
+ break;
+ entry = list_entry(tmp, struct hfs_cat_entry, list);
+ if (entry->mdb != mdb) {
+ continue;
+ }
+
+ if (!entry->count) {
+ list_del(&entry->hash);
+ INIT_LIST_HEAD(&entry->hash);
+ list_del(&entry->list);
+ list_add(&entry->list, dispose);
+ continue;
+ }
+
+ hfs_warn("hfs_fs: entry %p(%u) busy on removed device %s.\n",
+ entry, entry->count,
+ hfs_mdb_name(entry->mdb->sys_mdb));
+ }
+}
+
+/* delete entries from a list */
+static void delete_list(struct list_head *head)
+{
+ struct list_head *next = head->next;
+ struct hfs_cat_entry *entry;
+
+ for (;;) {
+ struct list_head * tmp = next;
+
+ next = next->next;
+ if (tmp == head) {
+ break;
+ }
+ entry = list_entry(tmp, struct hfs_cat_entry, list);
+ HFS_DELETE(entry);
+ }
+}
+
+/*
+ * hfs_cat_invalidate()
+ *
+ * Called by hfs_mdb_put() to remove all the entries
+ * in the cache that are associated with a given MDB.
+ */
+void hfs_cat_invalidate(struct hfs_mdb *mdb)
+{
+ LIST_HEAD(throw_away);
+
+ spin_lock(&entry_lock);
+ invalidate_list(&entry_in_use, mdb, &throw_away);
+ invalidate_list(&mdb->entry_dirty, mdb, &throw_away);
+ spin_unlock(&entry_lock);
+
+ delete_list(&throw_away);
+#if defined(DEBUG_CATALOG) || defined(DEBUG_ALL)
+ hfs_warn("hfs_cat_invalidate: free=%d total=%d\n",
+ entries_stat.nr_free_entries,
+ entries_stat.nr_entries);
+#endif
+}
+
+/*
+ * hfs_cat_commit()
+ *
+ * Called by hfs_mdb_commit() to write dirty entries to the disk buffers.
+ */
+void hfs_cat_commit(struct hfs_mdb *mdb)
+{
+ struct list_head *tmp, *head = &mdb->entry_dirty;
+ struct hfs_cat_entry *entry;
+
+ spin_lock(&entry_lock);
+ while ((tmp = head->prev) != head) {
+ entry = list_entry(tmp, struct hfs_cat_entry, list);
+
+ if ((entry->state & HFS_LOCK)) {
+ spin_unlock(&entry_lock);
+ wait_on_entry(entry);
+ spin_lock(&entry_lock);
+ } else {
+ struct list_head *insert = &entry_in_use;
+
+ if (!entry->count)
+ insert = entry_in_use.prev;
+
+ /* add to in_use list */
+ list_del(&entry->list);
+ list_add(&entry->list, insert);
+
+ /* reset DIRTY, set LOCK */
+ entry->state ^= HFS_DIRTY | HFS_LOCK;
+ spin_unlock(&entry_lock);
+ write_entry(entry);
+ spin_lock(&entry_lock);
+ entry->state &= ~HFS_LOCK;
+ hfs_wake_up(&entry->wait);
+ }
+ }
+ spin_unlock(&entry_lock);
+}
+
+/*
+ * hfs_cat_free()
+ *
+ * Releases all the memory allocated in grow_entries().
+ * Must call hfs_cat_invalidate() on all MDBs before calling this.
+ * This only gets rid of the unused pool of entries. all the other
+ * entry references should have either been freed by cat_invalidate
+ * or moved onto the unused list.
+ */
+void hfs_cat_free(void)
+{
+ delete_list(&entry_unused);
+}
+
+/*
+ * hfs_cat_compare()
+ *
+ * Description:
+ * This is the comparison function used for the catalog B-tree. In
+ * comparing catalog B-tree entries, the parent id is the most
+ * significant field (compared as unsigned ints). The name field is
+ * the least significant (compared in "Macintosh lexical order",
+ * see hfs_strcmp() in string.c)
+ * Input Variable(s):
+ * struct hfs_cat_key *key1: pointer to the first key to compare
+ * struct hfs_cat_key *key2: pointer to the second key to compare
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * int: negative if key1<key2, positive if key1>key2, and 0 if key1==key2
+ * Preconditions:
+ * key1 and key2 point to "valid" (struct hfs_cat_key)s.
+ * Postconditions:
+ * This function has no side-effects
+ */
+int hfs_cat_compare(const struct hfs_cat_key *key1,
+ const struct hfs_cat_key *key2)
+{
+ unsigned int parents;
+ int retval;
+
+ parents = hfs_get_hl(key1->ParID) - hfs_get_hl(key2->ParID);
+ if (parents != 0) {
+ retval = (int)parents;
+ } else {
+ retval = hfs_strcmp(key1->CName.Name, key1->CName.Len,
+ key2->CName.Name, key2->CName.Len);
+ }
+ return retval;
+}
+
+/*
+ * hfs_cat_build_key()
+ *
+ * Given the ID of the parent and the name build a search key.
+ */
+void hfs_cat_build_key(hfs_u32 parent, const struct hfs_name *cname,
+ struct hfs_cat_key *key)
+{
+ hfs_put_nl(parent, key->ParID);
+
+ if (cname) {
+ key->KeyLen = 6 + cname->Len;
+ memcpy(&key->CName, cname, sizeof(*cname));
+ } else {
+ key->KeyLen = 6;
+ memset(&key->CName, 0, sizeof(*cname));
+ }
+}
+
+/*
+ * hfs_cat_open()
+ *
+ * Given a directory on an HFS filesystem get its thread and
+ * lock the directory against insertions and deletions.
+ * Return 0 on success or an error code on failure.
+ */
+int hfs_cat_open(struct hfs_cat_entry *dir, struct hfs_brec *brec)
+{
+ struct hfs_cat_key key;
+ int error;
+
+ if (dir->type != HFS_CDR_DIR) {
+ return -EINVAL;
+ }
+
+ /* Block writers */
+ start_read(dir);
+
+ /* Find the directory */
+ hfs_cat_build_key(dir->cnid, NULL, &key);
+ error = hfs_bfind(brec, dir->mdb->cat_tree,
+ HFS_BKEY(&key), HFS_BFIND_READ_EQ);
+
+ if (error) {
+ end_read(dir);
+ }
+
+ return error;
+}
+
+/*
+ * hfs_cat_next()
+ *
+ * Given a catalog brec structure, replace it with the count'th next brec
+ * in the same directory.
+ * Return an error code if there is a problem, 0 if OK.
+ * Note that an error code of -ENOENT means there are no more entries
+ * in this directory.
+ * The directory is "closed" on an error.
+ */
+int hfs_cat_next(struct hfs_cat_entry *dir, struct hfs_brec *brec,
+ hfs_u16 count, hfs_u32 *cnid, hfs_u8 *type)
+{
+ int error;
+
+ if (!dir || !brec) {
+ return -EINVAL;
+ }
+
+ /* Get the count'th next catalog tree entry */
+ error = hfs_bsucc(brec, count);
+ if (!error) {
+ struct hfs_cat_key *key = (struct hfs_cat_key *)brec->key;
+ if (hfs_get_nl(key->ParID) != dir->cnid) {
+ hfs_brec_relse(brec, NULL);
+ error = -ENOENT;
+ }
+ }
+ if (!error) {
+ *type = ((struct hfs_cat_rec *)brec->data)->cdrType;
+ *cnid = brec_to_id(brec);
+ } else {
+ end_read(dir);
+ }
+ return error;
+}
+
+/*
+ * hfs_cat_close()
+ *
+ * Given a catalog brec structure, replace it with the count'th next brec
+ * in the same directory.
+ * Return an error code if there is a problem, 0 if OK.
+ * Note that an error code of -ENOENT means there are no more entries
+ * in this directory.
+ */
+void hfs_cat_close(struct hfs_cat_entry *dir, struct hfs_brec *brec)
+{
+ if (dir && brec) {
+ hfs_brec_relse(brec, NULL);
+ end_read(dir);
+ }
+}
+
+/*
+ * hfs_cat_parent()
+ *
+ * Given a catalog entry, return the entry for its parent.
+ * Uses catalog key for the entry to get its parent's ID
+ * and then uses the parent's thread record to locate the
+ * parent's actual catalog entry.
+ */
+struct hfs_cat_entry *hfs_cat_parent(struct hfs_cat_entry *entry)
+{
+ struct hfs_cat_entry *retval = NULL;
+ struct hfs_mdb *mdb = entry->mdb;
+ struct hfs_brec brec;
+ struct hfs_cat_key key;
+ int error;
+
+ lock_entry(entry);
+ if (!(entry->state & HFS_DELETED)) {
+ hfs_cat_build_key(hfs_get_nl(entry->key.ParID), NULL, &key);
+ error = hfs_bfind(&brec, mdb->cat_tree,
+ HFS_BKEY(&key), HFS_BFIND_READ_EQ);
+ if (!error) {
+ /* convert thread record to key */
+ struct hfs_cat_rec *rec = brec.data;
+ key.KeyLen = 6 + rec->u.thd.CName.Len;
+ memcpy(&key.ParID, &rec->u.thd.ParID,
+ sizeof(hfs_u32) + sizeof(struct hfs_name));
+
+ hfs_brec_relse(&brec, NULL);
+
+ retval = hfs_cat_get(mdb, &key);
+ }
+ }
+ unlock_entry(entry);
+ return retval;
+}
+
+/*
+ * hfs_cat_create()
+ *
+ * Create a new file with the indicated name in the indicated directory.
+ * The file will have the indicated flags, type and creator.
+ * If successful an (struct hfs_cat_entry) is returned in '*result'.
+ */
+int hfs_cat_create(struct hfs_cat_entry *parent, struct hfs_cat_key *key,
+ hfs_u8 flags, hfs_u32 type, hfs_u32 creator,
+ struct hfs_cat_entry **result)
+{
+ struct hfs_cat_rec record;
+ hfs_u32 id = new_cnid(parent->mdb);
+ hfs_u32 mtime = hfs_time();
+
+#if defined(DEBUG_CATALOG) || defined(DEBUG_ALL)
+ hfs_warn("hfs_cat_create: %p/%s flags=%d res=%p\n",
+ parent, key->CName.Name, flags, result);
+#endif
+ /* init some fields for the file record */
+ memset(&record, 0, sizeof(record));
+ record.cdrType = HFS_CDR_FIL;
+ record.u.fil.Flags = flags | HFS_FIL_USED;
+ hfs_put_nl(id, record.u.fil.FlNum);
+ hfs_put_nl(mtime, record.u.fil.CrDat);
+ hfs_put_nl(mtime, record.u.fil.MdDat);
+ hfs_put_nl(0, record.u.fil.BkDat);
+ hfs_put_nl(type, record.u.fil.UsrWds.fdType);
+ hfs_put_nl(creator, record.u.fil.UsrWds.fdCreator);
+
+ return create_entry(parent, key, &record, 0, id, result);
+}
+
+/*
+ * hfs_cat_mkdir()
+ *
+ * Create a new directory with the indicated name in the indicated directory.
+ * If successful an (struct hfs_cat_entry) is returned in '*result'.
+ */
+int hfs_cat_mkdir(struct hfs_cat_entry *parent, struct hfs_cat_key *key,
+ struct hfs_cat_entry **result)
+{
+ struct hfs_cat_rec record;
+ hfs_u32 id = new_cnid(parent->mdb);
+ hfs_u32 mtime = hfs_time();
+
+#if defined(DEBUG_CATALOG) || defined(DEBUG_ALL)
+ hfs_warn("hfs_cat_mkdir: %p/%s res=%p\n", parent, key->CName.Name,
+ result);
+#endif
+
+ /* init some fields for the directory record */
+ memset(&record, 0, sizeof(record));
+ record.cdrType = HFS_CDR_DIR;
+ hfs_put_nl(id, record.u.dir.DirID);
+ hfs_put_nl(mtime, record.u.dir.CrDat);
+ hfs_put_nl(mtime, record.u.dir.MdDat);
+ hfs_put_nl(0, record.u.dir.BkDat);
+ hfs_put_hs(0xff, record.u.dir.UsrInfo.frView);
+
+ return create_entry(parent, key, &record, 1, id, result);
+}
+
+/*
+ * hfs_cat_delete()
+ *
+ * Delete the indicated file or directory.
+ * The associated thread is also removed unless ('with_thread'==0).
+ */
+int hfs_cat_delete(struct hfs_cat_entry *parent, struct hfs_cat_entry *entry,
+ int with_thread)
+{
+ struct hfs_cat_key key;
+ struct hfs_mdb *mdb = parent->mdb;
+ int is_dir, error = 0;
+
+#if defined(DEBUG_CATALOG) || defined(DEBUG_ALL)
+ hfs_warn("hfs_cat_delete: %p/%p type=%d state=%lu, thread=%d\n",
+ parent, entry, entry->type, entry->state, with_thread);
+#endif
+ if (parent->mdb != entry->mdb) {
+ return -EINVAL;
+ }
+
+ if (entry->type == HFS_CDR_FIL) {
+ with_thread = (entry->u.file.flags&HFS_FIL_THD) && with_thread;
+ is_dir = 0;
+ } else {
+ is_dir = 1;
+ }
+
+ /* keep readers from getting confused by changing dir size */
+ start_write(parent);
+
+ /* don't delete a busy directory */
+ if (entry->type == HFS_CDR_DIR) {
+ start_read(entry);
+
+ error = -ENOTEMPTY;
+ if (entry->u.dir.files || entry->u.dir.dirs)
+ goto hfs_delete_end;
+ }
+
+ /* try to delete the file or directory */
+ lock_entry(entry);
+ error = -ENOENT;
+ if ((entry->state & HFS_DELETED)) {
+ /* somebody beat us to it. */
+ goto hfs_delete_unlock;
+ }
+
+ /* delete the catalog record */
+ if ((error = hfs_bdelete(mdb->cat_tree, HFS_BKEY(&entry->key)))) {
+ goto hfs_delete_unlock;
+ }
+
+ /* Mark the entry deleted and remove it from the cache */
+ delete_entry(entry);
+
+ /* try to delete the thread entry if it exists */
+ if (with_thread) {
+ hfs_cat_build_key(entry->cnid, NULL, &key);
+ (void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(&key));
+ }
+
+ update_dir(mdb, parent, is_dir, -1);
+
+hfs_delete_unlock:
+ unlock_entry(entry);
+
+hfs_delete_end:
+ if (entry->type == HFS_CDR_DIR) {
+ end_read(entry);
+ }
+ end_write(parent);
+ return error;
+}
+
+/*
+ * hfs_cat_move()
+ *
+ * Rename a file or directory, possibly to a new directory.
+ * If the destination exists it is removed and a
+ * (struct hfs_cat_entry) for it is returned in '*result'.
+ */
+int hfs_cat_move(struct hfs_cat_entry *old_dir, struct hfs_cat_entry *new_dir,
+ struct hfs_cat_entry *entry, struct hfs_cat_key *new_key,
+ struct hfs_cat_entry **removed)
+{
+ struct hfs_cat_entry *dest;
+ struct hfs_mdb *mdb;
+ int error = 0;
+ int is_dir, has_thread;
+
+ if (removed) {
+ *removed = NULL;
+ }
+
+ /* sanity checks */
+ if (!old_dir || !new_dir) {
+ return -EINVAL;
+ }
+ mdb = old_dir->mdb;
+ if (mdb != new_dir->mdb) {
+ return -EXDEV;
+ }
+
+ /* precompute a few things */
+ if (entry->type == HFS_CDR_DIR) {
+ is_dir = 1;
+ has_thread = 1;
+ } else if (entry->type == HFS_CDR_FIL) {
+ is_dir = 0;
+ has_thread = entry->u.file.flags & HFS_FIL_THD;
+ } else {
+ return -EINVAL;
+ }
+
+ while (mdb->rename_lock) {
+ hfs_sleep_on(&mdb->rename_wait);
+ }
+ spin_lock(&entry_lock);
+ mdb->rename_lock = 1; /* XXX: should be atomic_inc */
+ spin_unlock(&entry_lock);
+
+ /* keep readers from getting confused by changing dir size */
+ start_write(new_dir);
+ if (old_dir != new_dir) {
+ start_write(old_dir);
+ }
+
+ /* Don't move a directory inside itself */
+ if (is_dir) {
+ struct hfs_cat_key thd_key;
+ struct hfs_brec brec;
+
+ hfs_u32 id = new_dir->cnid;
+ while (id != htonl(HFS_ROOT_CNID)) {
+ if (id == entry->cnid) {
+ error = -EINVAL;
+ } else {
+ hfs_cat_build_key(id, NULL, &thd_key);
+ error = hfs_bfind(&brec, mdb->cat_tree,
+ HFS_BKEY(&thd_key),
+ HFS_BFIND_READ_EQ);
+ }
+ if (error) {
+ goto done;
+ } else {
+ struct hfs_cat_rec *rec = brec.data;
+ id = hfs_get_nl(rec->u.thd.ParID);
+ hfs_brec_relse(&brec, NULL);
+ }
+ }
+ }
+
+restart:
+ /* see if the destination exists, getting it if it does */
+ dest = hfs_cat_get(mdb, new_key);
+ if (!dest) {
+ /* destination doesn't exist, so create it */
+ struct hfs_cat_rec new_record;
+
+ /* create a locked entry in the cache */
+ dest = get_entry(mdb, new_key, 0);
+ if (!dest) {
+ error = -EIO;
+ goto done;
+ }
+ if (dest->cnid) {
+ /* The (unlocked) entry exists in the cache */
+ goto have_distinct;
+ }
+
+ /* limit directory valence to signed 16-bit integer */
+ if ((new_dir->u.dir.dirs + new_dir->u.dir.files) >=
+ HFS_MAX_VALENCE) {
+ error = -ENOSPC;
+ goto bail3;
+ }
+
+ /* build the new record. make sure to zero out the
+ record. */
+ memset(&new_record, 0, sizeof(new_record));
+ new_record.cdrType = entry->type;
+ __write_entry(entry, &new_record);
+
+ /* insert the new record */
+ error = hfs_binsert(mdb->cat_tree, HFS_BKEY(new_key),
+ &new_record, is_dir ? 2 + sizeof(DIR_REC) :
+ 2 + sizeof(FIL_REC));
+ if (error == -EEXIST) {
+ delete_entry(dest);
+ unlock_entry(dest);
+ hfs_cat_put(dest);
+ goto restart;
+ } else if (error) {
+ goto bail3;
+ }
+
+ /* update the destination directory */
+ update_dir(mdb, new_dir, is_dir, 1);
+ } else if (entry != dest) {
+have_distinct:
+ /* The destination exists and is not same as source */
+ lock_entry(dest);
+ if ((dest->state & HFS_DELETED)) {
+ unlock_entry(dest);
+ hfs_cat_put(dest);
+ goto restart;
+ }
+ if (dest->type != entry->type) {
+ /* can't move a file on top
+ of a dir nor vice versa. */
+ error = is_dir ? -ENOTDIR : -EISDIR;
+ } else if (is_dir && (dest->u.dir.dirs || dest->u.dir.files)) {
+ /* directory to replace is not empty */
+ error = -ENOTEMPTY;
+ }
+
+ if (error) {
+ goto bail2;
+ }
+ } else {
+ /* The destination exists but is same as source */
+ --entry->count;
+ dest = NULL;
+ }
+
+ /* lock the entry */
+ lock_entry(entry);
+ if ((entry->state & HFS_DELETED)) {
+ error = -ENOENT;
+ goto bail1;
+ }
+
+ if (dest) {
+ /* remove the old entry */
+ error = hfs_bdelete(mdb->cat_tree, HFS_BKEY(&entry->key));
+
+ if (error) {
+ /* We couldn't remove the entry for the
+ original file, so nothing has changed. */
+ goto bail1;
+ }
+ update_dir(mdb, old_dir, is_dir, -1);
+ }
+
+ /* update the thread of the dir/file we're moving */
+ if (has_thread) {
+ struct hfs_cat_key thd_key;
+ struct hfs_brec brec;
+
+ hfs_cat_build_key(entry->cnid, NULL, &thd_key);
+ error = hfs_bfind(&brec, mdb->cat_tree,
+ HFS_BKEY(&thd_key), HFS_BFIND_WRITE);
+ if (error == -ENOENT) {
+ if (is_dir) {
+ /* directory w/o a thread! */
+ error = -EIO;
+ } else {
+ /* We were lied to! */
+ entry->u.file.flags &= ~HFS_FIL_THD;
+ hfs_cat_mark_dirty(entry);
+ }
+ }
+ if (!error) {
+ struct hfs_cat_rec *rec = brec.data;
+ memcpy(&rec->u.thd.ParID, &new_key->ParID,
+ sizeof(hfs_u32) + sizeof(struct hfs_name));
+ hfs_brec_relse(&brec, NULL);
+ } else if (error == -ENOENT) {
+ error = 0;
+ } else if (!dest) {
+ /* Nothing was changed */
+ unlock_entry(entry);
+ goto done;
+ } else {
+ /* Something went seriously wrong.
+ The dir/file has been deleted. */
+ /* XXX try some recovery? */
+ delete_entry(entry);
+ goto bail1;
+ }
+ }
+
+ /* TRY to remove the thread for the pre-existing entry */
+ if (dest && dest->cnid &&
+ (is_dir || (dest->u.file.flags & HFS_FIL_THD))) {
+ struct hfs_cat_key thd_key;
+
+ hfs_cat_build_key(dest->cnid, NULL, &thd_key);
+ (void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(&thd_key));
+ }
+
+ /* update directories */
+ new_dir->modify_date = hfs_time();
+ hfs_cat_mark_dirty(new_dir);
+
+ /* update key */
+ remove_hash(entry);
+ memcpy(&entry->key, new_key, sizeof(*new_key));
+ /* KEYDIRTY as case might differ */
+ entry->state |= HFS_KEYDIRTY;
+ insert_hash(entry);
+ hfs_cat_mark_dirty(entry);
+ unlock_entry(entry);
+
+ /* delete any pre-existing or place-holder entry */
+ if (dest) {
+ delete_entry(dest);
+ unlock_entry(dest);
+ if (removed && dest->cnid) {
+ *removed = dest;
+ } else {
+ hfs_cat_put(dest);
+ }
+ }
+ goto done;
+
+bail1:
+ unlock_entry(entry);
+bail2:
+ if (dest) {
+ if (!dest->cnid) {
+ /* TRY to remove the new entry */
+ (void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(new_key));
+ update_dir(mdb, new_dir, is_dir, -1);
+bail3:
+ delete_entry(dest);
+ }
+ unlock_entry(dest);
+ hfs_cat_put(dest);
+ }
+done:
+ if (new_dir != old_dir) {
+ end_write(old_dir);
+ }
+ end_write(new_dir);
+ spin_lock(&entry_lock);
+ mdb->rename_lock = 0; /* XXX: should use atomic_dec */
+ hfs_wake_up(&mdb->rename_wait);
+ spin_unlock(&entry_lock);
+
+ return error;
+}
+
+/*
+ * Initialize the hash tables
+ */
+void hfs_cat_init(void)
+{
+ int i;
+ struct list_head *head = hash_table;
+
+ i = C_HASHSIZE;
+ do {
+ INIT_LIST_HEAD(head);
+ head++;
+ i--;
+ } while (i);
+}
diff --git a/fs/hfs/dir.c b/fs/hfs/dir.c
new file mode 100644
index 00000000000000..4f35c2760d73b6
--- /dev/null
+++ b/fs/hfs/dir.c
@@ -0,0 +1,394 @@
+/*
+ * linux/fs/hfs/dir.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU General Public License.
+ *
+ * This file contains directory-related functions independent of which
+ * scheme is being used to represent forks.
+ *
+ * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs.h"
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+/*================ File-local functions ================*/
+
+/*
+ * build_key()
+ *
+ * Build a key for a file by the given name in the given directory.
+ * If the name matches one of the reserved names returns 1 otherwise 0.
+ */
+static int build_key(struct hfs_cat_key *key, struct inode *dir,
+ const char *name, int len)
+{
+ struct hfs_name cname;
+ const struct hfs_name *reserved;
+
+ /* mangle the name */
+ hfs_nameout(dir, &cname, name, len);
+
+ /* check against reserved names */
+ reserved = HFS_SB(dir->i_sb)->s_reserved1;
+ while (reserved->Len) {
+ if (hfs_streq(reserved->Name, reserved->Len,
+ cname.Name, cname.Len)) {
+ return 1;
+ }
+ ++reserved;
+ }
+
+ /* check against the names reserved only in the root directory */
+ if (HFS_I(dir)->entry->cnid == htonl(HFS_ROOT_CNID)) {
+ reserved = HFS_SB(dir->i_sb)->s_reserved2;
+ while (reserved->Len) {
+ if (hfs_streq(reserved->Name, reserved->Len,
+ cname.Name, cname.Len)) {
+ return 1;
+ }
+ ++reserved;
+ }
+ }
+
+ /* build the key */
+ hfs_cat_build_key(HFS_I(dir)->entry->cnid, &cname, key);
+
+ return 0;
+}
+
+/*
+ * update_dirs_plus()
+ *
+ * Update the fields 'i_size', 'i_nlink', 'i_ctime', 'i_mtime' and
+ * 'i_version' of the inodes associated with a directory that has
+ * had a file ('is_dir'==0) or directory ('is_dir'!=0) added to it.
+ */
+static inline void update_dirs_plus(struct hfs_cat_entry *dir, int is_dir)
+{
+ int i;
+
+ for (i = 0; i < 4; ++i) {
+ struct dentry *de = dir->sys_entry[i];
+ if (de) {
+ struct inode *tmp = de->d_inode;
+ if (S_ISDIR(tmp->i_mode)) {
+ if (is_dir &&
+ (i == HFS_ITYPE_TO_INT(HFS_ITYPE_NORM))) {
+ /* In "normal" directory only */
+ ++(tmp->i_nlink);
+ }
+ tmp->i_size += HFS_I(tmp)->dir_size;
+ tmp->i_version = ++event;
+ }
+ tmp->i_ctime = tmp->i_mtime = CURRENT_TIME;
+ mark_inode_dirty(tmp);
+ }
+ }
+}
+
+/*
+ * update_dirs_minus()
+ *
+ * Update the fields 'i_size', 'i_nlink', 'i_ctime', 'i_mtime' and
+ * 'i_version' of the inodes associated with a directory that has
+ * had a file ('is_dir'==0) or directory ('is_dir'!=0) removed.
+ */
+static inline void update_dirs_minus(struct hfs_cat_entry *dir, int is_dir)
+{
+ int i;
+
+ for (i = 0; i < 4; ++i) {
+ struct dentry *de = dir->sys_entry[i];
+ if (de) {
+ struct inode *tmp = de->d_inode;
+ if (S_ISDIR(tmp->i_mode)) {
+ if (is_dir &&
+ (i == HFS_ITYPE_TO_INT(HFS_ITYPE_NORM))) {
+ /* In "normal" directory only */
+ --(tmp->i_nlink);
+ }
+ tmp->i_size -= HFS_I(tmp)->dir_size;
+ tmp->i_version = ++event;
+ }
+ tmp->i_ctime = tmp->i_mtime = CURRENT_TIME;
+ mark_inode_dirty(tmp);
+ }
+ }
+}
+
+/*
+ * mark_inodes_deleted()
+ *
+ * Update inodes associated with a deleted entry to reflect its deletion.
+ * Well, we really just drop the dentry.
+ *
+ * XXX: we should be using delete_inode for some of this stuff.
+ */
+static inline void mark_inodes_deleted(struct hfs_cat_entry *entry,
+ struct dentry *dentry)
+{
+ struct dentry *de;
+ struct inode *tmp;
+ int i;
+
+ for (i = 0; i < 4; ++i) {
+ if ((de = entry->sys_entry[i]) && (dentry != de)) {
+ dget(de);
+ tmp = de->d_inode;
+ tmp->i_nlink = 0;
+ tmp->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(tmp);
+ d_delete(de);
+ dput(de);
+ }
+ }
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_create()
+ *
+ * This is the create() entry in the inode_operations structure for
+ * regular HFS directories. The purpose is to create a new file in
+ * a directory and return a corresponding inode, given the inode for
+ * the directory and the name (and its length) of the new file.
+ */
+int hfs_create(struct inode * dir, struct dentry *dentry, int mode)
+{
+ struct hfs_cat_entry *entry = HFS_I(dir)->entry;
+ struct hfs_cat_entry *new;
+ struct hfs_cat_key key;
+ struct inode *inode;
+ int error;
+
+ /* build the key, checking against reserved names */
+ if (build_key(&key, dir, dentry->d_name.name, dentry->d_name.len))
+ return -EEXIST;
+
+ if ((error = hfs_cat_create(entry, &key,
+ (mode & S_IWUSR) ? 0 : HFS_FIL_LOCK,
+ HFS_SB(dir->i_sb)->s_type,
+ HFS_SB(dir->i_sb)->s_creator, &new)))
+ return error;
+
+ /* create an inode for the new file. back out if we run
+ * into trouble. */
+ new->count++; /* hfs_iget() eats one */
+ if (!(inode = hfs_iget(new, HFS_I(dir)->file_type, dentry))) {
+ hfs_cat_delete(entry, new, 1);
+ hfs_cat_put(new);
+ return -EIO;
+ }
+
+ hfs_cat_put(new);
+ update_dirs_plus(entry, 0);
+ /* toss any relevant negative dentries */
+ if (HFS_I(dir)->d_drop_op)
+ HFS_I(dir)->d_drop_op(dentry, HFS_I(dir)->file_type);
+ mark_inode_dirty(inode);
+ d_instantiate(dentry, inode);
+ return 0;
+}
+
+/*
+ * hfs_mkdir()
+ *
+ * This is the mkdir() entry in the inode_operations structure for
+ * regular HFS directories. The purpose is to create a new directory
+ * in a directory, given the inode for the parent directory and the
+ * name (and its length) of the new directory.
+ */
+int hfs_mkdir(struct inode * parent, struct dentry *dentry, int mode)
+{
+ struct hfs_cat_entry *entry = HFS_I(parent)->entry;
+ struct hfs_cat_entry *new;
+ struct hfs_cat_key key;
+ struct inode *inode;
+ int error;
+
+ /* build the key, checking against reserved names */
+ if (build_key(&key, parent, dentry->d_name.name,
+ dentry->d_name.len))
+ return -EEXIST;
+
+ /* try to create the directory */
+ if ((error = hfs_cat_mkdir(entry, &key, &new)))
+ return error;
+
+ /* back out if we run into trouble */
+ new->count++; /* hfs_iget eats one */
+ if (!(inode = hfs_iget(new, HFS_I(parent)->file_type, dentry))) {
+ hfs_cat_delete(entry, new, 1);
+ hfs_cat_put(new);
+ return -EIO;
+ }
+
+ hfs_cat_put(new);
+ update_dirs_plus(entry, 1);
+ mark_inode_dirty(inode);
+ d_instantiate(dentry, inode);
+ return 0;
+}
+
+/*
+ * hfs_unlink()
+ *
+ * This is the unlink() entry in the inode_operations structure for
+ * regular HFS directories. The purpose is to delete an existing
+ * file, given the inode for the parent directory and the name
+ * (and its length) of the existing file.
+ */
+int hfs_unlink(struct inode * dir, struct dentry *dentry)
+{
+ struct hfs_cat_entry *entry = HFS_I(dir)->entry;
+ struct hfs_cat_entry *victim = NULL;
+ struct hfs_cat_key key;
+ int error;
+
+ if (build_key(&key, dir, dentry->d_name.name,
+ dentry->d_name.len))
+ return -EPERM;
+
+ if (!(victim = hfs_cat_get(entry->mdb, &key)))
+ return -ENOENT;
+
+ error = -EPERM;
+ if (victim->type != HFS_CDR_FIL)
+ goto hfs_unlink_put;
+
+ if (!(error = hfs_cat_delete(entry, victim, 1))) {
+ struct inode *inode = dentry->d_inode;
+
+ mark_inodes_deleted(victim, dentry);
+ inode->i_nlink--;
+ inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+ update_dirs_minus(entry, 0);
+ }
+
+hfs_unlink_put:
+ hfs_cat_put(victim); /* Note that hfs_cat_put(NULL) is safe. */
+ return error;
+}
+
+/*
+ * hfs_rmdir()
+ *
+ * This is the rmdir() entry in the inode_operations structure for
+ * regular HFS directories. The purpose is to delete an existing
+ * directory, given the inode for the parent directory and the name
+ * (and its length) of the existing directory.
+ */
+int hfs_rmdir(struct inode * parent, struct dentry *dentry)
+{
+ struct hfs_cat_entry *entry = HFS_I(parent)->entry;
+ struct hfs_cat_entry *victim = NULL;
+ struct inode *inode = dentry->d_inode;
+ struct hfs_cat_key key;
+ int error;
+
+ if (build_key(&key, parent, dentry->d_name.name,
+ dentry->d_name.len))
+ return -EPERM;
+
+ if (!(victim = hfs_cat_get(entry->mdb, &key)))
+ return -ENOENT;
+
+ error = -ENOTDIR;
+ if (victim->type != HFS_CDR_DIR)
+ goto hfs_rmdir_put;
+
+ error = -EBUSY;
+ if (!d_unhashed(dentry))
+ goto hfs_rmdir_put;
+
+ /* we only have to worry about 2 and 3 for mount points */
+ if (victim->sys_entry[2] && d_mountpoint(victim->sys_entry[2]))
+ goto hfs_rmdir_put;
+ if (victim->sys_entry[3] && d_mountpoint(victim->sys_entry[3]))
+ goto hfs_rmdir_put;
+
+
+ if ((error = hfs_cat_delete(entry, victim, 1)))
+ goto hfs_rmdir_put;
+
+ mark_inodes_deleted(victim, dentry);
+ inode->i_nlink = 0;
+ inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+ update_dirs_minus(entry, 1);
+
+hfs_rmdir_put:
+ hfs_cat_put(victim); /* Note that hfs_cat_put(NULL) is safe. */
+ return error;
+}
+
+/*
+ * hfs_rename()
+ *
+ * This is the rename() entry in the inode_operations structure for
+ * regular HFS directories. The purpose is to rename an existing
+ * file or directory, given the inode for the current directory and
+ * the name (and its length) of the existing file/directory and the
+ * inode for the new directory and the name (and its length) of the
+ * new file/directory.
+ * XXX: how do you handle must_be dir?
+ */
+int hfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ struct hfs_cat_entry *old_parent = HFS_I(old_dir)->entry;
+ struct hfs_cat_entry *new_parent = HFS_I(new_dir)->entry;
+ struct hfs_cat_entry *victim = NULL;
+ struct hfs_cat_entry *deleted;
+ struct hfs_cat_key key;
+ int error;
+
+ if (build_key(&key, old_dir, old_dentry->d_name.name,
+ old_dentry->d_name.len) ||
+ (HFS_ITYPE(old_dir->i_ino) != HFS_ITYPE(new_dir->i_ino)))
+ return -EPERM;
+
+ if (!(victim = hfs_cat_get(old_parent->mdb, &key)))
+ return -ENOENT;
+
+ error = -EPERM;
+ if (build_key(&key, new_dir, new_dentry->d_name.name,
+ new_dentry->d_name.len))
+ goto hfs_rename_put;
+
+ if (!(error = hfs_cat_move(old_parent, new_parent,
+ victim, &key, &deleted))) {
+ int is_dir = (victim->type == HFS_CDR_DIR);
+
+ /* drop the old dentries */
+ mark_inodes_deleted(victim, old_dentry);
+ update_dirs_minus(old_parent, is_dir);
+ if (deleted) {
+ mark_inodes_deleted(deleted, new_dentry);
+ hfs_cat_put(deleted);
+ } else {
+ /* no existing inodes. just drop negative dentries */
+ if (HFS_I(new_dir)->d_drop_op)
+ HFS_I(new_dir)->d_drop_op(new_dentry,
+ HFS_I(new_dir)->file_type);
+ update_dirs_plus(new_parent, is_dir);
+ }
+
+ }
+
+hfs_rename_put:
+ hfs_cat_put(victim); /* Note that hfs_cat_put(NULL) is safe. */
+ return error;
+}
diff --git a/fs/hfs/dir_cap.c b/fs/hfs/dir_cap.c
new file mode 100644
index 00000000000000..3c000cdd53f9cb
--- /dev/null
+++ b/fs/hfs/dir_cap.c
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU General Public License.
+ *
+ * This file contains the inode_operations and file_operations
+ * structures for HFS directories under the CAP scheme.
+ *
+ * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds
+ *
+ * The source code distribution of the Columbia AppleTalk Package for
+ * UNIX, version 6.0, (CAP) was used as a specification of the
+ * location and format of files used by CAP's Aufs. No code from CAP
+ * appears in hfs_fs. hfs_fs is not a work ``derived'' from CAP in
+ * the sense of intellectual property law.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs.h"
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+/*================ Forward declarations ================*/
+
+static struct dentry *cap_lookup(struct inode *, struct dentry *);
+static int cap_readdir(struct file *, void *, filldir_t);
+
+/*================ Global variables ================*/
+
+#define DOT_LEN 1
+#define DOT_DOT_LEN 2
+#define DOT_RESOURCE_LEN 9
+#define DOT_FINDERINFO_LEN 11
+#define DOT_ROOTINFO_LEN 9
+
+const struct hfs_name hfs_cap_reserved1[] = {
+ {DOT_LEN, "."},
+ {DOT_DOT_LEN, ".."},
+ {DOT_RESOURCE_LEN, ".resource"},
+ {DOT_FINDERINFO_LEN, ".finderinfo"},
+ {0, ""},
+};
+
+const struct hfs_name hfs_cap_reserved2[] = {
+ {DOT_ROOTINFO_LEN, ".rootinfo"},
+ {0, ""},
+};
+
+#define DOT (&hfs_cap_reserved1[0])
+#define DOT_DOT (&hfs_cap_reserved1[1])
+#define DOT_RESOURCE (&hfs_cap_reserved1[2])
+#define DOT_FINDERINFO (&hfs_cap_reserved1[3])
+#define DOT_ROOTINFO (&hfs_cap_reserved2[0])
+
+struct file_operations hfs_cap_dir_operations = {
+ read: generic_read_dir,
+ readdir: cap_readdir,
+ fsync: file_fsync,
+};
+
+struct inode_operations hfs_cap_ndir_inode_operations = {
+ create: hfs_create,
+ lookup: cap_lookup,
+ unlink: hfs_unlink,
+ mkdir: hfs_mkdir,
+ rmdir: hfs_rmdir,
+ rename: hfs_rename,
+ setattr: hfs_notify_change,
+};
+
+struct inode_operations hfs_cap_fdir_inode_operations = {
+ lookup: cap_lookup,
+ setattr: hfs_notify_change,
+};
+
+struct inode_operations hfs_cap_rdir_inode_operations = {
+ create: hfs_create,
+ lookup: cap_lookup,
+ setattr: hfs_notify_change,
+};
+
+/*================ File-local functions ================*/
+
+/*
+ * cap_lookup()
+ *
+ * This is the lookup() entry in the inode_operations structure for
+ * HFS directories in the CAP scheme. The purpose is to generate the
+ * inode corresponding to an entry in a directory, given the inode for
+ * the directory and the name (and its length) of the entry.
+ */
+static struct dentry *cap_lookup(struct inode * dir, struct dentry *dentry)
+{
+ ino_t dtype;
+ struct hfs_name cname;
+ struct hfs_cat_entry *entry;
+ struct hfs_cat_key key;
+ struct inode *inode = NULL;
+
+ dentry->d_op = &hfs_dentry_operations;
+ entry = HFS_I(dir)->entry;
+ dtype = HFS_ITYPE(dir->i_ino);
+
+ /* Perform name-mangling */
+ hfs_nameout(dir, &cname, dentry->d_name.name,
+ dentry->d_name.len);
+
+ /* no need to check for "." or ".." */
+
+ /* Check for special directories if in a normal directory.
+ Note that cap_dupdir() does an iput(dir). */
+ if (dtype==HFS_CAP_NDIR) {
+ /* Check for ".resource", ".finderinfo" and ".rootinfo" */
+ if (hfs_streq(cname.Name, cname.Len,
+ DOT_RESOURCE->Name, DOT_RESOURCE_LEN)) {
+ ++entry->count; /* __hfs_iget() eats one */
+ inode = hfs_iget(entry, HFS_CAP_RDIR, dentry);
+ goto done;
+ } else if (hfs_streq(cname.Name, cname.Len,
+ DOT_FINDERINFO->Name,
+ DOT_FINDERINFO_LEN)) {
+ ++entry->count; /* __hfs_iget() eats one */
+ inode = hfs_iget(entry, HFS_CAP_FDIR, dentry);
+ goto done;
+ } else if ((entry->cnid == htonl(HFS_ROOT_CNID)) &&
+ hfs_streq(cname.Name, cname.Len,
+ DOT_ROOTINFO->Name, DOT_ROOTINFO_LEN)) {
+ ++entry->count; /* __hfs_iget() eats one */
+ inode = hfs_iget(entry, HFS_CAP_FNDR, dentry);
+ goto done;
+ }
+ }
+
+ /* Do an hfs_iget() on the mangled name. */
+ hfs_cat_build_key(entry->cnid, &cname, &key);
+ inode = hfs_iget(hfs_cat_get(entry->mdb, &key),
+ HFS_I(dir)->file_type, dentry);
+
+ /* Don't return a resource fork for a directory */
+ if (inode && (dtype == HFS_CAP_RDIR) &&
+ (HFS_I(inode)->entry->type == HFS_CDR_DIR)) {
+ iput(inode); /* this does an hfs_cat_put */
+ inode = NULL;
+ }
+
+done:
+ d_add(dentry, inode);
+ return NULL;
+}
+
+/*
+ * cap_readdir()
+ *
+ * This is the readdir() entry in the file_operations structure for
+ * HFS directories in the CAP scheme. The purpose is to enumerate the
+ * entries in a directory, given the inode of the directory and a
+ * (struct file *), the 'f_pos' field of which indicates the location
+ * in the directory. The (struct file *) is updated so that the next
+ * call with the same 'dir' and 'filp' arguments will produce the next
+ * directory entry. The entries are returned in 'dirent', which is
+ * "filled-in" by calling filldir(). This allows the same readdir()
+ * function be used for different dirent formats. We try to read in
+ * as many entries as we can before filldir() refuses to take any more.
+ *
+ * XXX: In the future it may be a good idea to consider not generating
+ * metadata files for covered directories since the data doesn't
+ * correspond to the mounted directory. However this requires an
+ * iget() for every directory which could be considered an excessive
+ * amount of overhead. Since the inode for a mount point is always
+ * in-core this is another argument for a call to get an inode if it
+ * is in-core or NULL if it is not.
+ */
+static int cap_readdir(struct file * filp,
+ void * dirent, filldir_t filldir)
+{
+ ino_t type;
+ int skip_dirs;
+ struct hfs_brec brec;
+ struct hfs_cat_entry *entry;
+ struct inode *dir = filp->f_dentry->d_inode;
+
+ entry = HFS_I(dir)->entry;
+ type = HFS_ITYPE(dir->i_ino);
+ skip_dirs = (type == HFS_CAP_RDIR);
+
+ if (filp->f_pos == 0) {
+ /* Entry 0 is for "." */
+ if (filldir(dirent, DOT->Name, DOT_LEN, 0, dir->i_ino, DT_DIR)) {
+ return 0;
+ }
+ filp->f_pos = 1;
+ }
+
+ if (filp->f_pos == 1) {
+ /* Entry 1 is for ".." */
+ hfs_u32 cnid;
+
+ if (type == HFS_CAP_NDIR) {
+ cnid = hfs_get_nl(entry->key.ParID);
+ } else {
+ cnid = entry->cnid;
+ }
+
+ if (filldir(dirent, DOT_DOT->Name,
+ DOT_DOT_LEN, 1, ntohl(cnid), DT_DIR)) {
+ return 0;
+ }
+ filp->f_pos = 2;
+ }
+
+ if (filp->f_pos < (dir->i_size - 3)) {
+ hfs_u32 cnid;
+ hfs_u8 type;
+
+ if (hfs_cat_open(entry, &brec) ||
+ hfs_cat_next(entry, &brec, filp->f_pos - 2, &cnid, &type)) {
+ return 0;
+ }
+ while (filp->f_pos < (dir->i_size - 3)) {
+ if (hfs_cat_next(entry, &brec, 1, &cnid, &type)) {
+ return 0;
+ }
+ if (!skip_dirs || (type != HFS_CDR_DIR)) {
+ ino_t ino;
+ unsigned int len;
+ unsigned char tmp_name[HFS_NAMEMAX];
+
+ ino = ntohl(cnid) | HFS_I(dir)->file_type;
+ len = hfs_namein(dir, tmp_name,
+ &((struct hfs_cat_key *)brec.key)->CName);
+ if (filldir(dirent, tmp_name, len,
+ filp->f_pos, ino, DT_UNKNOWN)) {
+ hfs_cat_close(entry, &brec);
+ return 0;
+ }
+ }
+ ++filp->f_pos;
+ }
+ hfs_cat_close(entry, &brec);
+ }
+
+ if (filp->f_pos == (dir->i_size - 3)) {
+ if ((entry->cnid == htonl(HFS_ROOT_CNID)) &&
+ (type == HFS_CAP_NDIR)) {
+ /* In root dir last-2 entry is for ".rootinfo" */
+ if (filldir(dirent, DOT_ROOTINFO->Name,
+ DOT_ROOTINFO_LEN, filp->f_pos,
+ ntohl(entry->cnid) | HFS_CAP_FNDR,
+ DT_UNKNOWN)) {
+ return 0;
+ }
+ }
+ ++filp->f_pos;
+ }
+
+ if (filp->f_pos == (dir->i_size - 2)) {
+ if (type == HFS_CAP_NDIR) {
+ /* In normal dirs last-1 entry is for ".finderinfo" */
+ if (filldir(dirent, DOT_FINDERINFO->Name,
+ DOT_FINDERINFO_LEN, filp->f_pos,
+ ntohl(entry->cnid) | HFS_CAP_FDIR,
+ DT_UNKNOWN)) {
+ return 0;
+ }
+ }
+ ++filp->f_pos;
+ }
+
+ if (filp->f_pos == (dir->i_size - 1)) {
+ if (type == HFS_CAP_NDIR) {
+ /* In normal dirs last entry is for ".resource" */
+ if (filldir(dirent, DOT_RESOURCE->Name,
+ DOT_RESOURCE_LEN, filp->f_pos,
+ ntohl(entry->cnid) | HFS_CAP_RDIR,
+ DT_UNKNOWN)) {
+ return 0;
+ }
+ }
+ ++filp->f_pos;
+ }
+
+ return 0;
+}
+
+
+/* due to the dcache caching negative dentries for non-existent files,
+ * we need to drop those entries when a file silently gets created.
+ * as far as i can tell, the calls that need to do this are the file
+ * related calls (create, rename, and mknod). the directory calls
+ * should be immune. the relevant calls in dir.c call drop_dentry
+ * upon successful completion. */
+void hfs_cap_drop_dentry(struct dentry *dentry, const ino_t type)
+{
+ if (type == HFS_CAP_DATA) { /* given name */
+ hfs_drop_special(dentry->d_parent, DOT_FINDERINFO, dentry);
+ hfs_drop_special(dentry->d_parent, DOT_RESOURCE, dentry);
+ } else {
+ struct dentry *de;
+
+ /* given {.resource,.finderinfo}/name, look for name */
+ if ((de = hfs_lookup_dentry(dentry->d_parent->d_parent,
+ dentry->d_name.name, dentry->d_name.len))) {
+ if (!de->d_inode)
+ d_drop(de);
+ dput(de);
+ }
+
+ switch (type) {
+ case HFS_CAP_RSRC: /* given .resource/name */
+ /* look for .finderinfo/name */
+ hfs_drop_special(dentry->d_parent->d_parent, DOT_FINDERINFO,
+ dentry);
+ break;
+ case HFS_CAP_FNDR: /* given .finderinfo/name. i don't this
+ * happens. */
+ /* look for .resource/name */
+ hfs_drop_special(dentry->d_parent->d_parent, DOT_RESOURCE,
+ dentry);
+ break;
+ }
+ }
+}
diff --git a/fs/hfs/dir_dbl.c b/fs/hfs/dir_dbl.c
new file mode 100644
index 00000000000000..8b7f876c312a6a
--- /dev/null
+++ b/fs/hfs/dir_dbl.c
@@ -0,0 +1,404 @@
+/*
+ * linux/fs/hfs/dir_dbl.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU General Public License.
+ *
+ * This file contains the inode_operations and file_operations
+ * structures for HFS directories.
+ *
+ * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs.h"
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+/*================ Forward declarations ================*/
+
+static struct dentry *dbl_lookup(struct inode *, struct dentry *);
+static int dbl_readdir(struct file *, void *, filldir_t);
+static int dbl_create(struct inode *, struct dentry *, int);
+static int dbl_mkdir(struct inode *, struct dentry *, int);
+static int dbl_unlink(struct inode *, struct dentry *);
+static int dbl_rmdir(struct inode *, struct dentry *);
+static int dbl_rename(struct inode *, struct dentry *,
+ struct inode *, struct dentry *);
+
+/*================ Global variables ================*/
+
+#define DOT_LEN 1
+#define DOT_DOT_LEN 2
+#define ROOTINFO_LEN 8
+#define PCNT_ROOTINFO_LEN 9
+
+const struct hfs_name hfs_dbl_reserved1[] = {
+ {DOT_LEN, "."},
+ {DOT_DOT_LEN, ".."},
+ {0, ""},
+};
+
+const struct hfs_name hfs_dbl_reserved2[] = {
+ {ROOTINFO_LEN, "RootInfo"},
+ {PCNT_ROOTINFO_LEN, "%RootInfo"},
+ {0, ""},
+};
+
+#define DOT (&hfs_dbl_reserved1[0])
+#define DOT_DOT (&hfs_dbl_reserved1[1])
+#define ROOTINFO (&hfs_dbl_reserved2[0])
+#define PCNT_ROOTINFO (&hfs_dbl_reserved2[1])
+
+struct file_operations hfs_dbl_dir_operations = {
+ read: generic_read_dir,
+ readdir: dbl_readdir,
+ fsync: file_fsync,
+};
+
+struct inode_operations hfs_dbl_dir_inode_operations = {
+ create: dbl_create,
+ lookup: dbl_lookup,
+ unlink: dbl_unlink,
+ mkdir: dbl_mkdir,
+ rmdir: dbl_rmdir,
+ rename: dbl_rename,
+ setattr: hfs_notify_change,
+};
+
+
+/*================ File-local functions ================*/
+
+/*
+ * is_hdr()
+ */
+static int is_hdr(struct inode *dir, const char *name, int len)
+{
+ int retval = 0;
+
+ if (name[0] == '%') {
+ struct hfs_cat_entry *entry = HFS_I(dir)->entry;
+ struct hfs_cat_entry *victim;
+ struct hfs_name cname;
+ struct hfs_cat_key key;
+
+ hfs_nameout(dir, &cname, name+1, len-1);
+ hfs_cat_build_key(entry->cnid, &cname, &key);
+ if ((victim = hfs_cat_get(entry->mdb, &key))) {
+ hfs_cat_put(victim);
+ retval = 1;
+ }
+ }
+ return retval;
+}
+
+/*
+ * dbl_lookup()
+ *
+ * This is the lookup() entry in the inode_operations structure for
+ * HFS directories in the AppleDouble scheme. The purpose is to
+ * generate the inode corresponding to an entry in a directory, given
+ * the inode for the directory and the name (and its length) of the
+ * entry.
+ */
+static struct dentry *dbl_lookup(struct inode * dir, struct dentry *dentry)
+{
+ struct hfs_name cname;
+ struct hfs_cat_entry *entry;
+ struct hfs_cat_key key;
+ struct inode *inode = NULL;
+
+ dentry->d_op = &hfs_dentry_operations;
+ entry = HFS_I(dir)->entry;
+
+ /* Perform name-mangling */
+ hfs_nameout(dir, &cname, dentry->d_name.name, dentry->d_name.len);
+
+ /* no need to check for "." or ".." */
+
+ /* Check for "%RootInfo" if in the root directory. */
+ if ((entry->cnid == htonl(HFS_ROOT_CNID)) &&
+ hfs_streq(cname.Name, cname.Len,
+ PCNT_ROOTINFO->Name, PCNT_ROOTINFO_LEN)) {
+ ++entry->count; /* __hfs_iget() eats one */
+ inode = hfs_iget(entry, HFS_DBL_HDR, dentry);
+ goto done;
+ }
+
+ /* Do an hfs_iget() on the mangled name. */
+ hfs_cat_build_key(entry->cnid, &cname, &key);
+ inode = hfs_iget(hfs_cat_get(entry->mdb, &key), HFS_DBL_NORM, dentry);
+
+ /* Try as a header if not found and first character is '%' */
+ if (!inode && (dentry->d_name.name[0] == '%')) {
+ hfs_nameout(dir, &cname, dentry->d_name.name+1,
+ dentry->d_name.len-1);
+ hfs_cat_build_key(entry->cnid, &cname, &key);
+ inode = hfs_iget(hfs_cat_get(entry->mdb, &key),
+ HFS_DBL_HDR, dentry);
+ }
+
+done:
+ d_add(dentry, inode);
+ return NULL;
+}
+
+/*
+ * dbl_readdir()
+ *
+ * This is the readdir() entry in the file_operations structure for
+ * HFS directories in the AppleDouble scheme. The purpose is to
+ * enumerate the entries in a directory, given the inode of the
+ * directory and a (struct file *), the 'f_pos' field of which
+ * indicates the location in the directory. The (struct file *) is
+ * updated so that the next call with the same 'dir' and 'filp'
+ * arguments will produce the next directory entry. The entries are
+ * returned in 'dirent', which is "filled-in" by calling filldir().
+ * This allows the same readdir() function be used for different
+ * formats. We try to read in as many entries as we can before
+ * filldir() refuses to take any more.
+ *
+ * XXX: In the future it may be a good idea to consider not generating
+ * metadata files for covered directories since the data doesn't
+ * correspond to the mounted directory. However this requires an
+ * iget() for every directory which could be considered an excessive
+ * amount of overhead. Since the inode for a mount point is always
+ * in-core this is another argument for a call to get an inode if it
+ * is in-core or NULL if it is not.
+ */
+static int dbl_readdir(struct file * filp,
+ void * dirent, filldir_t filldir)
+{
+ struct hfs_brec brec;
+ struct hfs_cat_entry *entry;
+ struct inode *dir = filp->f_dentry->d_inode;
+
+ entry = HFS_I(dir)->entry;
+
+ if (filp->f_pos == 0) {
+ /* Entry 0 is for "." */
+ if (filldir(dirent, DOT->Name, DOT_LEN, 0, dir->i_ino,
+ DT_DIR)) {
+ return 0;
+ }
+ filp->f_pos = 1;
+ }
+
+ if (filp->f_pos == 1) {
+ /* Entry 1 is for ".." */
+ if (filldir(dirent, DOT_DOT->Name, DOT_DOT_LEN, 1,
+ hfs_get_hl(entry->key.ParID), DT_DIR)) {
+ return 0;
+ }
+ filp->f_pos = 2;
+ }
+
+ if (filp->f_pos < (dir->i_size - 1)) {
+ hfs_u32 cnid;
+ hfs_u8 type;
+
+ if (hfs_cat_open(entry, &brec) ||
+ hfs_cat_next(entry, &brec, (filp->f_pos - 1) >> 1,
+ &cnid, &type)) {
+ return 0;
+ }
+
+ while (filp->f_pos < (dir->i_size - 1)) {
+ unsigned char tmp_name[HFS_NAMEMAX + 1];
+ ino_t ino;
+ int is_hdr = (filp->f_pos & 1);
+ unsigned int len;
+
+ if (is_hdr) {
+ ino = ntohl(cnid) | HFS_DBL_HDR;
+ tmp_name[0] = '%';
+ len = 1 + hfs_namein(dir, tmp_name + 1,
+ &((struct hfs_cat_key *)brec.key)->CName);
+ } else {
+ if (hfs_cat_next(entry, &brec, 1,
+ &cnid, &type)) {
+ return 0;
+ }
+ ino = ntohl(cnid);
+ len = hfs_namein(dir, tmp_name,
+ &((struct hfs_cat_key *)brec.key)->CName);
+ }
+
+ if (filldir(dirent, tmp_name, len, filp->f_pos, ino,
+ DT_UNKNOWN)) {
+ hfs_cat_close(entry, &brec);
+ return 0;
+ }
+ ++filp->f_pos;
+ }
+ hfs_cat_close(entry, &brec);
+ }
+
+ if (filp->f_pos == (dir->i_size - 1)) {
+ if (entry->cnid == htonl(HFS_ROOT_CNID)) {
+ /* In root dir last entry is for "%RootInfo" */
+ if (filldir(dirent, PCNT_ROOTINFO->Name,
+ PCNT_ROOTINFO_LEN, filp->f_pos,
+ ntohl(entry->cnid) | HFS_DBL_HDR,
+ DT_UNKNOWN)) {
+ return 0;
+ }
+ }
+ ++filp->f_pos;
+ }
+
+ return 0;
+}
+
+/*
+ * dbl_create()
+ *
+ * This is the create() entry in the inode_operations structure for
+ * AppleDouble directories. The purpose is to create a new file in
+ * a directory and return a corresponding inode, given the inode for
+ * the directory and the name (and its length) of the new file.
+ */
+static int dbl_create(struct inode * dir, struct dentry *dentry,
+ int mode)
+{
+ int error;
+
+ if (is_hdr(dir, dentry->d_name.name, dentry->d_name.len)) {
+ error = -EEXIST;
+ } else {
+ error = hfs_create(dir, dentry, mode);
+ }
+ return error;
+}
+
+/*
+ * dbl_mkdir()
+ *
+ * This is the mkdir() entry in the inode_operations structure for
+ * AppleDouble directories. The purpose is to create a new directory
+ * in a directory, given the inode for the parent directory and the
+ * name (and its length) of the new directory.
+ */
+static int dbl_mkdir(struct inode * parent, struct dentry *dentry,
+ int mode)
+{
+ int error;
+
+ if (is_hdr(parent, dentry->d_name.name, dentry->d_name.len)) {
+ error = -EEXIST;
+ } else {
+ error = hfs_mkdir(parent, dentry, mode);
+ }
+ return error;
+}
+
+/*
+ * dbl_unlink()
+ *
+ * This is the unlink() entry in the inode_operations structure for
+ * AppleDouble directories. The purpose is to delete an existing
+ * file, given the inode for the parent directory and the name
+ * (and its length) of the existing file.
+ */
+static int dbl_unlink(struct inode * dir, struct dentry *dentry)
+{
+ int error;
+
+ error = hfs_unlink(dir, dentry);
+ if ((error == -ENOENT) && is_hdr(dir, dentry->d_name.name,
+ dentry->d_name.len)) {
+ error = -EPERM;
+ }
+ return error;
+}
+
+/*
+ * dbl_rmdir()
+ *
+ * This is the rmdir() entry in the inode_operations structure for
+ * AppleDouble directories. The purpose is to delete an existing
+ * directory, given the inode for the parent directory and the name
+ * (and its length) of the existing directory.
+ */
+static int dbl_rmdir(struct inode * parent, struct dentry *dentry)
+{
+ int error;
+
+ error = hfs_rmdir(parent, dentry);
+ if ((error == -ENOENT) && is_hdr(parent, dentry->d_name.name,
+ dentry->d_name.len)) {
+ error = -ENOTDIR;
+ }
+ return error;
+}
+
+/*
+ * dbl_rename()
+ *
+ * This is the rename() entry in the inode_operations structure for
+ * AppleDouble directories. The purpose is to rename an existing
+ * file or directory, given the inode for the current directory and
+ * the name (and its length) of the existing file/directory and the
+ * inode for the new directory and the name (and its length) of the
+ * new file/directory.
+ *
+ * XXX: how do we handle must_be_dir?
+ */
+static int dbl_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ int error;
+
+ if (is_hdr(new_dir, new_dentry->d_name.name,
+ new_dentry->d_name.len)) {
+ error = -EPERM;
+ } else {
+ error = hfs_rename(old_dir, old_dentry,
+ new_dir, new_dentry);
+ if ((error == -ENOENT) /*&& !must_be_dir*/ &&
+ is_hdr(old_dir, old_dentry->d_name.name,
+ old_dentry->d_name.len)) {
+ error = -EPERM;
+ }
+ }
+ return error;
+}
+
+
+/* due to the dcache caching negative dentries for non-existent files,
+ * we need to drop those entries when a file silently gets created.
+ * as far as i can tell, the calls that need to do this are the file
+ * related calls (create, rename, and mknod). the directory calls
+ * should be immune. the relevant calls in dir.c call drop_dentry
+ * upon successful completion. */
+void hfs_dbl_drop_dentry(struct dentry *dentry, const ino_t type)
+{
+ unsigned char tmp_name[HFS_NAMEMAX + 1];
+ struct dentry *de = NULL;
+
+ switch (type) {
+ case HFS_DBL_HDR:
+ /* given %name, look for name. i don't think this happens. */
+ de = hfs_lookup_dentry(dentry->d_parent,
+ dentry->d_name.name + 1, dentry->d_name.len - 1);
+ break;
+ case HFS_DBL_DATA:
+ /* given name, look for %name */
+ tmp_name[0] = '%';
+ strncpy(tmp_name + 1, dentry->d_name.name, HFS_NAMELEN - 1);
+ de = hfs_lookup_dentry(dentry->d_parent,
+ tmp_name, dentry->d_name.len + 1);
+ }
+
+ if (de) {
+ if (!de->d_inode)
+ d_drop(de);
+ dput(de);
+ }
+}
diff --git a/fs/hfs/dir_nat.c b/fs/hfs/dir_nat.c
new file mode 100644
index 00000000000000..d378aa3a0f61a1
--- /dev/null
+++ b/fs/hfs/dir_nat.c
@@ -0,0 +1,452 @@
+/*
+ * linux/fs/hfs/dir_nat.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU General Public License.
+ *
+ * This file contains the inode_operations and file_operations
+ * structures for HFS directories.
+ *
+ * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds
+ *
+ * The source code distributions of Netatalk, versions 1.3.3b2 and
+ * 1.4b2, were used as a specification of the location and format of
+ * files used by Netatalk's afpd. No code from Netatalk appears in
+ * hfs_fs. hfs_fs is not a work ``derived'' from Netatalk in the
+ * sense of intellectual property law.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs.h"
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+/*================ Forward declarations ================*/
+
+static struct dentry *nat_lookup(struct inode *, struct dentry *);
+static int nat_readdir(struct file *, void *, filldir_t);
+static int nat_rmdir(struct inode *, struct dentry *);
+static int nat_hdr_unlink(struct inode *, struct dentry *);
+static int nat_hdr_rename(struct inode *, struct dentry *,
+ struct inode *, struct dentry *);
+
+/*================ Global variables ================*/
+
+#define DOT_LEN 1
+#define DOT_DOT_LEN 2
+#define DOT_APPLEDOUBLE_LEN 12
+#define DOT_PARENT_LEN 7
+#define ROOTINFO_LEN 8
+
+const struct hfs_name hfs_nat_reserved1[] = {
+ {DOT_LEN, "."},
+ {DOT_DOT_LEN, ".."},
+ {DOT_APPLEDOUBLE_LEN, ".AppleDouble"},
+ {DOT_PARENT_LEN, ".Parent"},
+ {0, ""},
+};
+
+const struct hfs_name hfs_nat_reserved2[] = {
+ {ROOTINFO_LEN, "RootInfo"},
+};
+
+#define DOT (&hfs_nat_reserved1[0])
+#define DOT_DOT (&hfs_nat_reserved1[1])
+#define DOT_APPLEDOUBLE (&hfs_nat_reserved1[2])
+#define DOT_PARENT (&hfs_nat_reserved1[3])
+#define ROOTINFO (&hfs_nat_reserved2[0])
+
+struct file_operations hfs_nat_dir_operations = {
+ read: generic_read_dir,
+ readdir: nat_readdir,
+ fsync: file_fsync,
+};
+
+struct inode_operations hfs_nat_ndir_inode_operations = {
+ create: hfs_create,
+ lookup: nat_lookup,
+ unlink: hfs_unlink,
+ mkdir: hfs_mkdir,
+ rmdir: nat_rmdir,
+ rename: hfs_rename,
+ setattr: hfs_notify_change,
+};
+
+struct inode_operations hfs_nat_hdir_inode_operations = {
+ create: hfs_create,
+ lookup: nat_lookup,
+ unlink: nat_hdr_unlink,
+ rename: nat_hdr_rename,
+ setattr: hfs_notify_change,
+};
+
+/*================ File-local functions ================*/
+
+/*
+ * nat_lookup()
+ *
+ * This is the lookup() entry in the inode_operations structure for
+ * HFS directories in the Netatalk scheme. The purpose is to generate
+ * the inode corresponding to an entry in a directory, given the inode
+ * for the directory and the name (and its length) of the entry.
+ */
+static struct dentry *nat_lookup(struct inode * dir, struct dentry *dentry)
+{
+ ino_t dtype;
+ struct hfs_name cname;
+ struct hfs_cat_entry *entry;
+ struct hfs_cat_key key;
+ struct inode *inode = NULL;
+
+ dentry->d_op = &hfs_dentry_operations;
+ entry = HFS_I(dir)->entry;
+ dtype = HFS_ITYPE(dir->i_ino);
+
+ /* Perform name-mangling */
+ hfs_nameout(dir, &cname, dentry->d_name.name, dentry->d_name.len);
+
+ /* no need to check for "." or ".." */
+
+ /* Check for ".AppleDouble" if in a normal directory,
+ and for ".Parent" in ".AppleDouble". */
+ if (dtype==HFS_NAT_NDIR) {
+ /* Check for ".AppleDouble" */
+ if (hfs_streq(cname.Name, cname.Len,
+ DOT_APPLEDOUBLE->Name, DOT_APPLEDOUBLE_LEN)) {
+ ++entry->count; /* __hfs_iget() eats one */
+ inode = hfs_iget(entry, HFS_NAT_HDIR, dentry);
+ goto done;
+ }
+ } else if (dtype==HFS_NAT_HDIR) {
+ if (hfs_streq(cname.Name, cname.Len,
+ DOT_PARENT->Name, DOT_PARENT_LEN)) {
+ ++entry->count; /* __hfs_iget() eats one */
+ inode = hfs_iget(entry, HFS_NAT_HDR, dentry);
+ goto done;
+ }
+
+ if ((entry->cnid == htonl(HFS_ROOT_CNID)) &&
+ hfs_streq(cname.Name, cname.Len,
+ ROOTINFO->Name, ROOTINFO_LEN)) {
+ ++entry->count; /* __hfs_iget() eats one */
+ inode = hfs_iget(entry, HFS_NAT_HDR, dentry);
+ goto done;
+ }
+ }
+
+ /* Do an hfs_iget() on the mangled name. */
+ hfs_cat_build_key(entry->cnid, &cname, &key);
+ inode = hfs_iget(hfs_cat_get(entry->mdb, &key),
+ HFS_I(dir)->file_type, dentry);
+
+ /* Don't return a header file for a directory other than .Parent */
+ if (inode && (dtype == HFS_NAT_HDIR) &&
+ (HFS_I(inode)->entry != entry) &&
+ (HFS_I(inode)->entry->type == HFS_CDR_DIR)) {
+ iput(inode); /* this does an hfs_cat_put */
+ inode = NULL;
+ }
+
+done:
+ d_add(dentry, inode);
+ return NULL;
+}
+
+/*
+ * nat_readdir()
+ *
+ * This is the readdir() entry in the file_operations structure for
+ * HFS directories in the netatalk scheme. The purpose is to
+ * enumerate the entries in a directory, given the inode of the
+ * directory and a struct file which indicates the location in the
+ * directory. The struct file is updated so that the next call with
+ * the same dir and filp will produce the next directory entry. The
+ * entries are returned in dirent, which is "filled-in" by calling
+ * filldir(). This allows the same readdir() function be used for
+ * different dirent formats. We try to read in as many entries as we
+ * can before filldir() refuses to take any more.
+ *
+ * Note that the Netatalk format doesn't have the problem with
+ * metadata for covered directories that exists in the other formats,
+ * since the metadata is contained within the directory.
+ */
+static int nat_readdir(struct file * filp,
+ void * dirent, filldir_t filldir)
+{
+ ino_t type;
+ int skip_dirs;
+ struct hfs_brec brec;
+ struct hfs_cat_entry *entry;
+ struct inode *dir = filp->f_dentry->d_inode;
+
+ entry = HFS_I(dir)->entry;
+ type = HFS_ITYPE(dir->i_ino);
+ skip_dirs = (type == HFS_NAT_HDIR);
+
+ if (filp->f_pos == 0) {
+ /* Entry 0 is for "." */
+ if (filldir(dirent, DOT->Name, DOT_LEN, 0, dir->i_ino,
+ DT_DIR)) {
+ return 0;
+ }
+ filp->f_pos = 1;
+ }
+
+ if (filp->f_pos == 1) {
+ /* Entry 1 is for ".." */
+ hfs_u32 cnid;
+
+ if (type == HFS_NAT_NDIR) {
+ cnid = hfs_get_nl(entry->key.ParID);
+ } else {
+ cnid = entry->cnid;
+ }
+
+ if (filldir(dirent, DOT_DOT->Name,
+ DOT_DOT_LEN, 1, ntohl(cnid), DT_DIR)) {
+ return 0;
+ }
+ filp->f_pos = 2;
+ }
+
+ if (filp->f_pos < (dir->i_size - 2)) {
+ hfs_u32 cnid;
+ hfs_u8 type;
+
+ if (hfs_cat_open(entry, &brec) ||
+ hfs_cat_next(entry, &brec, filp->f_pos - 2, &cnid, &type)) {
+ return 0;
+ }
+ while (filp->f_pos < (dir->i_size - 2)) {
+ if (hfs_cat_next(entry, &brec, 1, &cnid, &type)) {
+ return 0;
+ }
+ if (!skip_dirs || (type != HFS_CDR_DIR)) {
+ ino_t ino;
+ unsigned int len;
+ unsigned char tmp_name[HFS_NAMEMAX];
+
+ ino = ntohl(cnid) | HFS_I(dir)->file_type;
+ len = hfs_namein(dir, tmp_name,
+ &((struct hfs_cat_key *)brec.key)->CName);
+ if (filldir(dirent, tmp_name, len,
+ filp->f_pos, ino, DT_UNKNOWN)) {
+ hfs_cat_close(entry, &brec);
+ return 0;
+ }
+ }
+ ++filp->f_pos;
+ }
+ hfs_cat_close(entry, &brec);
+ }
+
+ if (filp->f_pos == (dir->i_size - 2)) {
+ if (type == HFS_NAT_NDIR) {
+ /* In normal dirs entry 2 is for ".AppleDouble" */
+ if (filldir(dirent, DOT_APPLEDOUBLE->Name,
+ DOT_APPLEDOUBLE_LEN, filp->f_pos,
+ ntohl(entry->cnid) | HFS_NAT_HDIR,
+ DT_UNKNOWN)) {
+ return 0;
+ }
+ } else if (type == HFS_NAT_HDIR) {
+ /* In .AppleDouble entry 2 is for ".Parent" */
+ if (filldir(dirent, DOT_PARENT->Name,
+ DOT_PARENT_LEN, filp->f_pos,
+ ntohl(entry->cnid) | HFS_NAT_HDR,
+ DT_UNKNOWN)) {
+ return 0;
+ }
+ }
+ ++filp->f_pos;
+ }
+
+ if (filp->f_pos == (dir->i_size - 1)) {
+ /* handle ROOT/.AppleDouble/RootInfo as the last entry. */
+ if ((entry->cnid == htonl(HFS_ROOT_CNID)) &&
+ (type == HFS_NAT_HDIR)) {
+ if (filldir(dirent, ROOTINFO->Name,
+ ROOTINFO_LEN, filp->f_pos,
+ ntohl(entry->cnid) | HFS_NAT_HDR,
+ DT_UNKNOWN)) {
+ return 0;
+ }
+ }
+ ++filp->f_pos;
+ }
+
+ return 0;
+}
+
+/* due to the dcache caching negative dentries for non-existent files,
+ * we need to drop those entries when a file silently gets created.
+ * as far as i can tell, the calls that need to do this are the file
+ * related calls (create, rename, and mknod). the directory calls
+ * should be immune. the relevant calls in dir.c call drop_dentry
+ * upon successful completion. */
+void hfs_nat_drop_dentry(struct dentry *dentry, const ino_t type)
+{
+ struct dentry *de;
+
+ switch (type) {
+ case HFS_NAT_HDR: /* given .AppleDouble/name */
+ /* look for name */
+ de = hfs_lookup_dentry(dentry->d_parent->d_parent,
+ dentry->d_name.name, dentry->d_name.len);
+
+ if (de) {
+ if (!de->d_inode)
+ d_drop(de);
+ dput(de);
+ }
+ break;
+ case HFS_NAT_DATA: /* given name */
+ /* look for .AppleDouble/name */
+ hfs_drop_special(dentry->d_parent, DOT_APPLEDOUBLE, dentry);
+ break;
+ }
+
+}
+
+/*
+ * nat_rmdir()
+ *
+ * This is the rmdir() entry in the inode_operations structure for
+ * Netatalk directories. The purpose is to delete an existing
+ * directory, given the inode for the parent directory and the name
+ * (and its length) of the existing directory.
+ *
+ * We handle .AppleDouble and call hfs_rmdir() for all other cases.
+ */
+static int nat_rmdir(struct inode *parent, struct dentry *dentry)
+{
+ struct hfs_cat_entry *entry = HFS_I(parent)->entry;
+ struct hfs_name cname;
+ int error;
+
+ hfs_nameout(parent, &cname, dentry->d_name.name, dentry->d_name.len);
+ if (hfs_streq(cname.Name, cname.Len,
+ DOT_APPLEDOUBLE->Name, DOT_APPLEDOUBLE_LEN)) {
+ if (!HFS_SB(parent->i_sb)->s_afpd) {
+ /* Not in AFPD compatibility mode */
+ error = -EPERM;
+ } else if (entry->u.dir.files || entry->u.dir.dirs) {
+ /* AFPD compatible, but the directory is not empty */
+ error = -ENOTEMPTY;
+ } else {
+ /* AFPD compatible, so pretend to succeed */
+ error = 0;
+ }
+ } else {
+ error = hfs_rmdir(parent, dentry);
+ }
+ return error;
+}
+
+/*
+ * nat_hdr_unlink()
+ *
+ * This is the unlink() entry in the inode_operations structure for
+ * Netatalk .AppleDouble directories. The purpose is to delete an
+ * existing file, given the inode for the parent directory and the name
+ * (and its length) of the existing file.
+ *
+ * WE DON'T ACTUALLY DELETE HEADER THE FILE.
+ * In non-afpd-compatible mode:
+ * We return -EPERM.
+ * In afpd-compatible mode:
+ * We return success if the file exists or is .Parent.
+ * Otherwise we return -ENOENT.
+ */
+static int nat_hdr_unlink(struct inode *dir, struct dentry *dentry)
+{
+ struct hfs_cat_entry *entry = HFS_I(dir)->entry;
+ int error = 0;
+
+ if (!HFS_SB(dir->i_sb)->s_afpd) {
+ /* Not in AFPD compatibility mode */
+ error = -EPERM;
+ } else {
+ struct hfs_name cname;
+
+ hfs_nameout(dir, &cname, dentry->d_name.name,
+ dentry->d_name.len);
+ if (!hfs_streq(cname.Name, cname.Len,
+ DOT_PARENT->Name, DOT_PARENT_LEN)) {
+ struct hfs_cat_entry *victim;
+ struct hfs_cat_key key;
+
+ hfs_cat_build_key(entry->cnid, &cname, &key);
+ victim = hfs_cat_get(entry->mdb, &key);
+
+ if (victim) {
+ /* pretend to succeed */
+ hfs_cat_put(victim);
+ } else {
+ error = -ENOENT;
+ }
+ }
+ }
+ return error;
+}
+
+/*
+ * nat_hdr_rename()
+ *
+ * This is the rename() entry in the inode_operations structure for
+ * Netatalk header directories. The purpose is to rename an existing
+ * file given the inode for the current directory and the name
+ * (and its length) of the existing file and the inode for the new
+ * directory and the name (and its length) of the new file/directory.
+ *
+ * WE NEVER MOVE ANYTHING.
+ * In non-afpd-compatible mode:
+ * We return -EPERM.
+ * In afpd-compatible mode:
+ * If the source header doesn't exist, we return -ENOENT.
+ * If the destination is not a header directory we return -EPERM.
+ * We return success if the destination is also a header directory
+ * and the header exists or is ".Parent".
+ */
+static int nat_hdr_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ struct hfs_cat_entry *entry = HFS_I(old_dir)->entry;
+ int error = 0;
+
+ if (!HFS_SB(old_dir->i_sb)->s_afpd) {
+ /* Not in AFPD compatibility mode */
+ error = -EPERM;
+ } else {
+ struct hfs_name cname;
+
+ hfs_nameout(old_dir, &cname, old_dentry->d_name.name,
+ old_dentry->d_name.len);
+ if (!hfs_streq(cname.Name, cname.Len,
+ DOT_PARENT->Name, DOT_PARENT_LEN)) {
+ struct hfs_cat_entry *victim;
+ struct hfs_cat_key key;
+
+ hfs_cat_build_key(entry->cnid, &cname, &key);
+ victim = hfs_cat_get(entry->mdb, &key);
+
+ if (victim) {
+ /* pretend to succeed */
+ hfs_cat_put(victim);
+ } else {
+ error = -ENOENT;
+ }
+ }
+
+ if (!error && (HFS_ITYPE(new_dir->i_ino) != HFS_NAT_HDIR)) {
+ error = -EPERM;
+ }
+ }
+ return error;
+}
diff --git a/fs/hfs/extent.c b/fs/hfs/extent.c
new file mode 100644
index 00000000000000..5df23375add4de
--- /dev/null
+++ b/fs/hfs/extent.c
@@ -0,0 +1,809 @@
+/*
+ * linux/fs/hfs/extent.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU General Public License.
+ *
+ * This file contains the functions related to the extents B-tree.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs.h"
+
+/*================ File-local data type ================*/
+
+/* An extent record on disk*/
+struct hfs_raw_extent {
+ hfs_word_t block1;
+ hfs_word_t length1;
+ hfs_word_t block2;
+ hfs_word_t length2;
+ hfs_word_t block3;
+ hfs_word_t length3;
+};
+
+/*================ File-local functions ================*/
+
+/*
+ * build_key
+ */
+static inline void build_key(struct hfs_ext_key *key,
+ const struct hfs_fork *fork, hfs_u16 block)
+{
+ key->KeyLen = 7;
+ key->FkType = fork->fork;
+ hfs_put_nl(fork->entry->cnid, key->FNum);
+ hfs_put_hs(block, key->FABN);
+}
+
+
+/*
+ * lock_bitmap()
+ *
+ * Get an exclusive lock on the B-tree bitmap.
+ */
+static inline void lock_bitmap(struct hfs_mdb *mdb) {
+ while (mdb->bitmap_lock) {
+ hfs_sleep_on(&mdb->bitmap_wait);
+ }
+ mdb->bitmap_lock = 1;
+}
+
+/*
+ * unlock_bitmap()
+ *
+ * Relinquish an exclusive lock on the B-tree bitmap.
+ */
+static inline void unlock_bitmap(struct hfs_mdb *mdb) {
+ mdb->bitmap_lock = 0;
+ hfs_wake_up(&mdb->bitmap_wait);
+}
+
+/*
+ * dump_ext()
+ *
+ * prints the content of a extent for debugging purposes.
+ */
+#if defined(DEBUG_EXTENTS) || defined(DEBUG_ALL)
+static void dump_ext(const char *msg, const struct hfs_extent *e) {
+ if (e) {
+ hfs_warn("%s (%d-%d) (%d-%d) (%d-%d)\n", msg,
+ e->start,
+ e->start + e->length[0] - 1,
+ e->start + e->length[0],
+ e->start + e->length[0] + e->length[1] - 1,
+ e->start + e->length[0] + e->length[1],
+ e->end);
+ } else {
+ hfs_warn("%s NULL\n", msg);
+ }
+}
+#else
+#define dump_ext(A,B) {}
+#endif
+
+/*
+ * read_extent()
+ *
+ * Initializes a (struct hfs_extent) from a (struct hfs_raw_extent) and
+ * the number of the starting block for the extent.
+ *
+ * Note that the callers must check that to,from != NULL
+ */
+static void read_extent(struct hfs_extent *to,
+ const struct hfs_raw_extent *from,
+ hfs_u16 start)
+{
+ to->start = start;
+ to->block[0] = hfs_get_hs(from->block1);
+ to->length[0] = hfs_get_hs(from->length1);
+ to->block[1] = hfs_get_hs(from->block2);
+ to->length[1] = hfs_get_hs(from->length2);
+ to->block[2] = hfs_get_hs(from->block3);
+ to->length[2] = hfs_get_hs(from->length3);
+ to->end = start + to->length[0] + to->length[1] + to->length[2] - 1;
+ to->next = to->prev = NULL;
+ to->count = 0;
+}
+
+/*
+ * write_extent()
+ *
+ * Initializes a (struct hfs_raw_extent) from a (struct hfs_extent).
+ *
+ * Note that the callers must check that to,from != NULL
+ */
+static void write_extent(struct hfs_raw_extent *to,
+ const struct hfs_extent *from)
+{
+ hfs_put_hs(from->block[0], to->block1);
+ hfs_put_hs(from->length[0], to->length1);
+ hfs_put_hs(from->block[1], to->block2);
+ hfs_put_hs(from->length[1], to->length2);
+ hfs_put_hs(from->block[2], to->block3);
+ hfs_put_hs(from->length[2], to->length3);
+}
+
+/*
+ * decode_extent()
+ *
+ * Given an extent record and allocation block offset into the file,
+ * return the number of the corresponding allocation block on disk,
+ * or -1 if the desired block is not mapped by the given extent.
+ *
+ * Note that callers must check that extent != NULL
+ */
+static int decode_extent(const struct hfs_extent * extent, int block)
+{
+ if (!extent || (block < extent->start) || (block > extent->end) ||
+ (extent->end == (hfs_u16)(extent->start - 1))) {
+ return -1;
+ }
+ block -= extent->start;
+ if (block < extent->length[0]) {
+ return block + extent->block[0];
+ }
+ block -= extent->length[0];
+ if (block < extent->length[1]) {
+ return block + extent->block[1];
+ }
+ return block + extent->block[2] - extent->length[1];
+}
+
+/*
+ * relse_ext()
+ *
+ * Reduce the reference count of an in-core extent record by one,
+ * removing it from memory if the count falls to zero.
+ */
+static void relse_ext(struct hfs_extent *ext)
+{
+ if (--ext->count || !ext->start) {
+ return;
+ }
+ ext->prev->next = ext->next;
+ if (ext->next) {
+ ext->next->prev = ext->prev;
+ }
+ HFS_DELETE(ext);
+}
+
+/*
+ * set_cache()
+ *
+ * Changes the 'cache' field of the fork.
+ */
+static inline void set_cache(struct hfs_fork *fork, struct hfs_extent *ext)
+{
+ struct hfs_extent *tmp = fork->cache;
+
+ ++ext->count;
+ fork->cache = ext;
+ relse_ext(tmp);
+}
+
+/*
+ * find_ext()
+ *
+ * Given a pointer to a (struct hfs_file) and an allocation block
+ * number in the file, find the extent record containing that block.
+ * Returns a pointer to the extent record on success or NULL on failure.
+ * The 'cache' field of 'fil' also points to the extent so it has a
+ * reference count of at least 2.
+ *
+ * Callers must check that fil != NULL
+ */
+static struct hfs_extent * find_ext(struct hfs_fork *fork, int alloc_block)
+{
+ struct hfs_cat_entry *entry = fork->entry;
+ struct hfs_btree *tr= entry->mdb->ext_tree;
+ struct hfs_ext_key target, *key;
+ struct hfs_brec brec;
+ struct hfs_extent *ext, *ptr;
+ int tmp;
+
+ if (alloc_block < 0) {
+ ext = &fork->first;
+ goto found;
+ }
+
+ ext = fork->cache;
+ if (!ext || (alloc_block < ext->start)) {
+ ext = &fork->first;
+ }
+ while (ext->next && (alloc_block > ext->end)) {
+ ext = ext->next;
+ }
+ if ((alloc_block <= ext->end) && (alloc_block >= ext->start)) {
+ goto found;
+ }
+
+ /* time to read more extents */
+ if (!HFS_NEW(ext)) {
+ goto bail3;
+ }
+
+ build_key(&target, fork, alloc_block);
+
+ tmp = hfs_bfind(&brec, tr, HFS_BKEY(&target), HFS_BFIND_READ_LE);
+ if (tmp < 0) {
+ goto bail2;
+ }
+
+ key = (struct hfs_ext_key *)brec.key;
+ if ((hfs_get_nl(key->FNum) != hfs_get_nl(target.FNum)) ||
+ (key->FkType != fork->fork)) {
+ goto bail1;
+ }
+
+ read_extent(ext, brec.data, hfs_get_hs(key->FABN));
+ hfs_brec_relse(&brec, NULL);
+
+ if ((alloc_block > ext->end) && (alloc_block < ext->start)) {
+ /* something strange happened */
+ goto bail2;
+ }
+
+ ptr = fork->cache;
+ if (!ptr || (alloc_block < ptr->start)) {
+ ptr = &fork->first;
+ }
+ while (ptr->next && (alloc_block > ptr->end)) {
+ ptr = ptr->next;
+ }
+ if (ext->start == ptr->start) {
+ /* somebody beat us to it. */
+ HFS_DELETE(ext);
+ ext = ptr;
+ } else if (ext->start < ptr->start) {
+ /* insert just before ptr */
+ ptr->prev->next = ext;
+ ext->prev = ptr->prev;
+ ext->next = ptr;
+ ptr->prev = ext;
+ } else {
+ /* insert at end */
+ ptr->next = ext;
+ ext->prev = ptr;
+ }
+ found:
+ ++ext->count; /* for return value */
+ set_cache(fork, ext);
+ return ext;
+
+ bail1:
+ hfs_brec_relse(&brec, NULL);
+ bail2:
+ HFS_DELETE(ext);
+ bail3:
+ return NULL;
+}
+
+/*
+ * delete_extent()
+ *
+ * Description:
+ * Deletes an extent record from a fork, reducing its physical length.
+ * Input Variable(s):
+ * struct hfs_fork *fork: the fork
+ * struct hfs_extent *ext: the current last extent for 'fork'
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * void
+ * Preconditions:
+ * 'fork' points to a valid (struct hfs_fork)
+ * 'ext' point to a valid (struct hfs_extent) which is the last in 'fork'
+ * and which is not also the first extent in 'fork'.
+ * Postconditions:
+ * The extent record has been removed if possible, and a warning has been
+ * printed otherwise.
+ */
+static void delete_extent(struct hfs_fork *fork, struct hfs_extent *ext)
+{
+ struct hfs_mdb *mdb = fork->entry->mdb;
+ struct hfs_ext_key key;
+ int error;
+
+ if (fork->cache == ext) {
+ set_cache(fork, ext->prev);
+ }
+ ext->prev->next = NULL;
+ if (ext->count != 1) {
+ hfs_warn("hfs_truncate: extent has count %d.\n", ext->count);
+ }
+
+ lock_bitmap(mdb);
+ error = hfs_clear_vbm_bits(mdb, ext->block[2], ext->length[2]);
+ if (error) {
+ hfs_warn("hfs_truncate: error %d freeing blocks.\n", error);
+ }
+ error = hfs_clear_vbm_bits(mdb, ext->block[1], ext->length[1]);
+ if (error) {
+ hfs_warn("hfs_truncate: error %d freeing blocks.\n", error);
+ }
+ error = hfs_clear_vbm_bits(mdb, ext->block[0], ext->length[0]);
+ if (error) {
+ hfs_warn("hfs_truncate: error %d freeing blocks.\n", error);
+ }
+ unlock_bitmap(mdb);
+
+ build_key(&key, fork, ext->start);
+
+ error = hfs_bdelete(mdb->ext_tree, HFS_BKEY(&key));
+ if (error) {
+ hfs_warn("hfs_truncate: error %d deleting an extent.\n", error);
+ }
+
+ HFS_DELETE(ext);
+}
+
+/*
+ * new_extent()
+ *
+ * Description:
+ * Adds a new extent record to a fork, extending its physical length.
+ * Input Variable(s):
+ * struct hfs_fork *fork: the fork to extend
+ * struct hfs_extent *ext: the current last extent for 'fork'
+ * hfs_u16 ablock: the number of allocation blocks in 'fork'.
+ * hfs_u16 start: first allocation block to add to 'fork'.
+ * hfs_u16 len: the number of allocation blocks to add to 'fork'.
+ * hfs_u32 ablksz: number of sectors in an allocation block.
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * (struct hfs_extent *) the new extent or NULL
+ * Preconditions:
+ * 'fork' points to a valid (struct hfs_fork)
+ * 'ext' point to a valid (struct hfs_extent) which is the last in 'fork'
+ * 'ablock', 'start', 'len' and 'ablksz' are what they claim to be.
+ * Postconditions:
+ * If NULL is returned then no changes have been made to 'fork'.
+ * If the return value is non-NULL that it is the extent that has been
+ * added to 'fork' both in memory and on disk. The 'psize' field of
+ * 'fork' has been updated to reflect the new physical size.
+ */
+static struct hfs_extent *new_extent(struct hfs_fork *fork,
+ struct hfs_extent *ext,
+ hfs_u16 ablock, hfs_u16 start,
+ hfs_u16 len, hfs_u16 ablksz)
+{
+ struct hfs_raw_extent raw;
+ struct hfs_ext_key key;
+ int error;
+
+ if (fork->entry->cnid == htonl(HFS_EXT_CNID)) {
+ /* Limit extents tree to the record in the MDB */
+ return NULL;
+ }
+
+ if (!HFS_NEW(ext->next)) {
+ return NULL;
+ }
+ ext->next->prev = ext;
+ ext->next->next = NULL;
+ ext = ext->next;
+ relse_ext(ext->prev);
+
+ ext->start = ablock;
+ ext->block[0] = start;
+ ext->length[0] = len;
+ ext->block[1] = 0;
+ ext->length[1] = 0;
+ ext->block[2] = 0;
+ ext->length[2] = 0;
+ ext->end = ablock + len - 1;
+ ext->count = 1;
+
+ write_extent(&raw, ext);
+
+ build_key(&key, fork, ablock);
+
+ error = hfs_binsert(fork->entry->mdb->ext_tree,
+ HFS_BKEY(&key), &raw, sizeof(raw));
+ if (error) {
+ ext->prev->next = NULL;
+ HFS_DELETE(ext);
+ return NULL;
+ }
+ set_cache(fork, ext);
+ return ext;
+}
+
+/*
+ * update_ext()
+ *
+ * Given a (struct hfs_fork) write an extent record back to disk.
+ */
+static void update_ext(struct hfs_fork *fork, struct hfs_extent *ext)
+{
+ struct hfs_ext_key target;
+ struct hfs_brec brec;
+
+ if (ext->start) {
+ build_key(&target, fork, ext->start);
+
+ if (!hfs_bfind(&brec, fork->entry->mdb->ext_tree,
+ HFS_BKEY(&target), HFS_BFIND_WRITE)) {
+ write_extent(brec.data, ext);
+ hfs_brec_relse(&brec, NULL);
+ }
+ }
+}
+
+/*
+ * zero_blocks()
+ *
+ * Zeros-out 'num' allocation blocks beginning with 'start'.
+ */
+static int zero_blocks(struct hfs_mdb *mdb, int start, int num) {
+ hfs_buffer buf;
+ int end;
+ int j;
+
+ start = mdb->fs_start + start * mdb->alloc_blksz;
+ end = start + num * mdb->alloc_blksz;
+
+ for (j=start; j<end; ++j) {
+ if (hfs_buffer_ok(buf = hfs_buffer_get(mdb->sys_mdb, j, 0))) {
+ memset(hfs_buffer_data(buf), 0, HFS_SECTOR_SIZE);
+ hfs_buffer_dirty(buf);
+ hfs_buffer_put(buf);
+ }
+ }
+ return 0;
+}
+
+/*
+ * shrink_fork()
+ *
+ * Try to remove enough allocation blocks from 'fork'
+ * so that it is 'ablocks' allocation blocks long.
+ */
+static void shrink_fork(struct hfs_fork *fork, int ablocks)
+{
+ struct hfs_mdb *mdb = fork->entry->mdb;
+ struct hfs_extent *ext;
+ int i, error, next, count;
+ hfs_u32 ablksz = mdb->alloc_blksz;
+
+ next = (fork->psize / ablksz) - 1;
+ ext = find_ext(fork, next);
+ while (ext && ext->start && (ext->start >= ablocks)) {
+ next = ext->start - 1;
+ delete_extent(fork, ext);
+ ext = find_ext(fork, next);
+ }
+ if (!ext) {
+ fork->psize = (next + 1) * ablksz;
+ return;
+ }
+
+ if ((count = next + 1 - ablocks) > 0) {
+ for (i=2; (i>=0) && !ext->length[i]; --i) {};
+ lock_bitmap(mdb);
+ while (count && (ext->length[i] <= count)) {
+ ext->end -= ext->length[i];
+ count -= ext->length[i];
+ error = hfs_clear_vbm_bits(mdb, ext->block[i],
+ ext->length[i]);
+ if (error) {
+ hfs_warn("hfs_truncate: error %d freeing "
+ "blocks.\n", error);
+ }
+ ext->block[i] = ext->length[i] = 0;
+ --i;
+ }
+ if (count) {
+ ext->end -= count;
+ ext->length[i] -= count;
+ error = hfs_clear_vbm_bits(mdb, ext->block[i] +
+ ext->length[i], count);
+ if (error) {
+ hfs_warn("hfs_truncate: error %d freeing "
+ "blocks.\n", error);
+ }
+ }
+ unlock_bitmap(mdb);
+ update_ext(fork, ext);
+ }
+
+ fork->psize = ablocks * ablksz;
+}
+
+/*
+ * grow_fork()
+ *
+ * Try to add enough allocation blocks to 'fork'
+ * so that it is 'ablock' allocation blocks long.
+ */
+static int grow_fork(struct hfs_fork *fork, int ablocks)
+{
+ struct hfs_cat_entry *entry = fork->entry;
+ struct hfs_mdb *mdb = entry->mdb;
+ struct hfs_extent *ext;
+ int i, start, err;
+ hfs_u16 need, len=0;
+ hfs_u32 ablksz = mdb->alloc_blksz;
+ hfs_u32 blocks, clumpablks;
+
+ blocks = fork->psize;
+ need = ablocks - blocks/ablksz;
+ if (need < 1) { /* no need to grow the fork */
+ return 0;
+ }
+
+ /* round up to clumpsize */
+ if (entry->u.file.clumpablks) {
+ clumpablks = entry->u.file.clumpablks;
+ } else {
+ clumpablks = mdb->clumpablks;
+ }
+ need = ((need + clumpablks - 1) / clumpablks) * clumpablks;
+
+ /* find last extent record and try to extend it */
+ if (!(ext = find_ext(fork, blocks/ablksz - 1))) {
+ /* somehow we couldn't find the end of the file! */
+ return -1;
+ }
+
+ /* determine which is the last used extent in the record */
+ /* then try to allocate the blocks immediately following it */
+ for (i=2; (i>=0) && !ext->length[i]; --i) {};
+ if (i>=0) {
+ /* try to extend the last extent */
+ start = ext->block[i] + ext->length[i];
+
+ err = 0;
+ lock_bitmap(mdb);
+ len = hfs_vbm_count_free(mdb, start);
+ if (!len) {
+ unlock_bitmap(mdb);
+ goto more_extents;
+ }
+ if (need < len) {
+ len = need;
+ }
+ err = hfs_set_vbm_bits(mdb, start, len);
+ unlock_bitmap(mdb);
+ if (err) {
+ relse_ext(ext);
+ return -1;
+ }
+
+ zero_blocks(mdb, start, len);
+
+ ext->length[i] += len;
+ ext->end += len;
+ blocks = (fork->psize += len * ablksz);
+ need -= len;
+ update_ext(fork, ext);
+ }
+
+more_extents:
+ /* add some more extents */
+ while (need) {
+ len = need;
+ err = 0;
+ lock_bitmap(mdb);
+ start = hfs_vbm_search_free(mdb, &len);
+ if (need < len) {
+ len = need;
+ }
+ err = hfs_set_vbm_bits(mdb, start, len);
+ unlock_bitmap(mdb);
+ if (!len || err) {
+ relse_ext(ext);
+ return -1;
+ }
+ zero_blocks(mdb, start, len);
+
+ /* determine which is the first free extent in the record */
+ for (i=0; (i<3) && ext->length[i]; ++i) {};
+ if (i < 3) {
+ ext->block[i] = start;
+ ext->length[i] = len;
+ ext->end += len;
+ update_ext(fork, ext);
+ } else {
+ if (!(ext = new_extent(fork, ext, blocks/ablksz,
+ start, len, ablksz))) {
+ lock_bitmap(mdb);
+ hfs_clear_vbm_bits(mdb, start, len);
+ unlock_bitmap(mdb);
+ return -1;
+ }
+ }
+ blocks = (fork->psize += len * ablksz);
+ need -= len;
+ }
+ set_cache(fork, ext);
+ relse_ext(ext);
+ return 0;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_ext_compare()
+ *
+ * Description:
+ * This is the comparison function used for the extents B-tree. In
+ * comparing extent B-tree entries, the file id is the most
+ * significant field (compared as unsigned ints); the fork type is
+ * the second most significant field (compared as unsigned chars);
+ * and the allocation block number field is the least significant
+ * (compared as unsigned ints).
+ * Input Variable(s):
+ * struct hfs_ext_key *key1: pointer to the first key to compare
+ * struct hfs_ext_key *key2: pointer to the second key to compare
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * int: negative if key1<key2, positive if key1>key2, and 0 if key1==key2
+ * Preconditions:
+ * key1 and key2 point to "valid" (struct hfs_ext_key)s.
+ * Postconditions:
+ * This function has no side-effects */
+int hfs_ext_compare(const struct hfs_ext_key *key1,
+ const struct hfs_ext_key *key2)
+{
+ unsigned int tmp;
+ int retval;
+
+ tmp = hfs_get_hl(key1->FNum) - hfs_get_hl(key2->FNum);
+ if (tmp != 0) {
+ retval = (int)tmp;
+ } else {
+ tmp = (unsigned char)key1->FkType - (unsigned char)key2->FkType;
+ if (tmp != 0) {
+ retval = (int)tmp;
+ } else {
+ retval = (int)(hfs_get_hs(key1->FABN)
+ - hfs_get_hs(key2->FABN));
+ }
+ }
+ return retval;
+}
+
+/*
+ * hfs_extent_adj()
+ *
+ * Given an hfs_fork shrink or grow the fork to hold the
+ * forks logical size.
+ */
+void hfs_extent_adj(struct hfs_fork *fork)
+{
+ if (fork) {
+ hfs_u32 blks, ablocks, ablksz;
+
+ if (fork->lsize > HFS_FORK_MAX) {
+ fork->lsize = HFS_FORK_MAX;
+ }
+
+ blks = (fork->lsize+HFS_SECTOR_SIZE-1) >> HFS_SECTOR_SIZE_BITS;
+ ablksz = fork->entry->mdb->alloc_blksz;
+ ablocks = (blks + ablksz - 1) / ablksz;
+
+ if (blks > fork->psize) {
+ grow_fork(fork, ablocks);
+ if (blks > fork->psize) {
+ fork->lsize =
+ fork->psize >> HFS_SECTOR_SIZE_BITS;
+ }
+ } else if (blks < fork->psize) {
+ shrink_fork(fork, ablocks);
+ }
+ }
+}
+
+/*
+ * hfs_extent_map()
+ *
+ * Given an hfs_fork and a block number within the fork, return the
+ * number of the corresponding physical block on disk, or zero on
+ * error.
+ */
+int hfs_extent_map(struct hfs_fork *fork, int block, int create)
+{
+ int ablksz, ablock, offset, tmp;
+ struct hfs_extent *ext;
+
+ if (!fork || !fork->entry || !fork->entry->mdb) {
+ return 0;
+ }
+
+#if defined(DEBUG_EXTENTS) || defined(DEBUG_ALL)
+ hfs_warn("hfs_extent_map: ablock %d of file %d, fork %d\n",
+ block, fork->entry->cnid, fork->fork);
+#endif
+
+ if (block < 0) {
+ hfs_warn("hfs_extent_map: block < 0\n");
+ return 0;
+ }
+ if (block > (HFS_FORK_MAX >> HFS_SECTOR_SIZE_BITS)) {
+ hfs_warn("hfs_extent_map: block(0x%08x) > big; cnid=%d "
+ "fork=%d\n", block, fork->entry->cnid, fork->fork);
+ return 0;
+ }
+ ablksz = fork->entry->mdb->alloc_blksz;
+ offset = fork->entry->mdb->fs_start + (block % ablksz);
+ ablock = block / ablksz;
+
+ if (block >= fork->psize) {
+ if (!create || (grow_fork(fork, ablock + 1) < 0))
+ return 0;
+ }
+
+#if defined(DEBUG_EXTENTS) || defined(DEBUG_ALL)
+ hfs_warn("(lblock %d offset %d)\n", ablock, offset);
+#endif
+
+ if ((ext = find_ext(fork, ablock))) {
+ dump_ext("trying new: ", ext);
+ tmp = decode_extent(ext, ablock);
+ relse_ext(ext);
+ if (tmp >= 0) {
+ return tmp*ablksz + offset;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * hfs_extent_out()
+ *
+ * Copy the first extent record from a (struct hfs_fork) to a (struct
+ * raw_extent), record (normally the one in the catalog entry).
+ */
+void hfs_extent_out(const struct hfs_fork *fork, hfs_byte_t dummy[12])
+{
+ struct hfs_raw_extent *ext = (struct hfs_raw_extent *)dummy;
+
+ if (fork && ext) {
+ write_extent(ext, &fork->first);
+ dump_ext("extent out: ", &fork->first);
+ }
+}
+
+/*
+ * hfs_extent_in()
+ *
+ * Copy an raw_extent to the 'first' and 'cache' fields of an hfs_fork.
+ */
+void hfs_extent_in(struct hfs_fork *fork, const hfs_byte_t dummy[12])
+{
+ const struct hfs_raw_extent *ext =
+ (const struct hfs_raw_extent *)dummy;
+
+ if (fork && ext) {
+ read_extent(&fork->first, ext, 0);
+ fork->cache = &fork->first;
+ fork->first.count = 2;
+ dump_ext("extent in: ", &fork->first);
+ }
+}
+
+/*
+ * hfs_extent_free()
+ *
+ * Removes from memory all extents associated with 'fil'.
+ */
+void hfs_extent_free(struct hfs_fork *fork)
+{
+ if (fork) {
+ set_cache(fork, &fork->first);
+
+ if (fork->first.next) {
+ hfs_warn("hfs_extent_free: extents in use!\n");
+ }
+ }
+}
diff --git a/fs/hfs/file.c b/fs/hfs/file.c
new file mode 100644
index 00000000000000..bdb8f796c411c1
--- /dev/null
+++ b/fs/hfs/file.c
@@ -0,0 +1,534 @@
+/*
+ * linux/fs/hfs/file.c
+ *
+ * Copyright (C) 1995, 1996 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU General Public License.
+ *
+ * This file contains the file-related functions which are independent of
+ * which scheme is being used to represent forks.
+ *
+ * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs.h"
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+/*================ Forward declarations ================*/
+
+static hfs_rwret_t hfs_file_read(struct file *, char *, hfs_rwarg_t,
+ loff_t *);
+static hfs_rwret_t hfs_file_write(struct file *, const char *, hfs_rwarg_t,
+ loff_t *);
+static void hfs_file_truncate(struct inode *);
+
+/*================ Global variables ================*/
+
+struct file_operations hfs_file_operations = {
+ llseek: generic_file_llseek,
+ read: hfs_file_read,
+ write: hfs_file_write,
+ mmap: generic_file_mmap,
+ fsync: file_fsync,
+};
+
+struct inode_operations hfs_file_inode_operations = {
+ truncate: hfs_file_truncate,
+ setattr: hfs_notify_change,
+};
+
+/*================ Variable-like macros ================*/
+
+/* maximum number of blocks to try to read in at once */
+#define NBUF 32
+
+/*================ File-local functions ================*/
+
+/*
+ * hfs_getblk()
+ *
+ * Given an hfs_fork and a block number return the buffer_head for
+ * that block from the fork. If 'create' is non-zero then allocate
+ * the necessary block(s) to the fork.
+ */
+struct buffer_head *hfs_getblk(struct hfs_fork *fork, int block, int create)
+{
+ int tmp;
+ struct super_block *sb = fork->entry->mdb->sys_mdb;
+
+ tmp = hfs_extent_map(fork, block, create);
+
+ if (create) {
+ /* If writing the block, then we have exclusive access
+ to the file until we return, so it can't have moved.
+ */
+ if (tmp) {
+ hfs_cat_mark_dirty(fork->entry);
+ return sb_getblk(sb, tmp);
+ }
+ return NULL;
+ } else {
+ /* If reading the block, then retry since the
+ location on disk could have changed while
+ we waited on the I/O in getblk to complete.
+ */
+ do {
+ struct buffer_head *bh = sb_getblk(sb, tmp);
+ int tmp2 = hfs_extent_map(fork, block, 0);
+
+ if (tmp2 == tmp) {
+ return bh;
+ } else {
+ /* The block moved or no longer exists. */
+ brelse(bh);
+ tmp = tmp2;
+ }
+ } while (tmp != 0);
+
+ /* The block no longer exists. */
+ return NULL;
+ }
+}
+
+/*
+ * hfs_get_block
+ *
+ * This is the hfs_get_block() field in the inode_operations structure for
+ * "regular" (non-header) files. The purpose is to translate an inode
+ * and a block number within the corresponding file into a physical
+ * block number. This function just calls hfs_extent_map() to do the
+ * real work and then stuffs the appropriate info into the buffer_head.
+ */
+int hfs_get_block(struct inode *inode, long iblock, struct buffer_head *bh_result, int create)
+{
+ unsigned long phys;
+
+ phys = hfs_extent_map(HFS_I(inode)->fork, iblock, create);
+ if (phys) {
+ bh_result->b_dev = inode->i_dev;
+ bh_result->b_blocknr = phys;
+ bh_result->b_state |= (1UL << BH_Mapped);
+ if (create)
+ bh_result->b_state |= (1UL << BH_New);
+ return 0;
+ }
+
+ if (!create)
+ return 0;
+
+ /* we tried to add stuff, but we couldn't. send back an out-of-space
+ * error. */
+ return -ENOSPC;
+}
+
+
+/*
+ * hfs_file_read()
+ *
+ * This is the read field in the inode_operations structure for
+ * "regular" (non-header) files. The purpose is to transfer up to
+ * 'count' bytes from the file corresponding to 'inode', beginning at
+ * 'filp->offset' bytes into the file. The data is transferred to
+ * user-space at the address 'buf'. Returns the number of bytes
+ * successfully transferred. This function checks the arguments, does
+ * some setup and then calls hfs_do_read() to do the actual transfer. */
+static hfs_rwret_t hfs_file_read(struct file * filp, char * buf,
+ hfs_rwarg_t count, loff_t *ppos)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ hfs_s32 read, left, pos, size;
+
+ if (!S_ISREG(inode->i_mode)) {
+ hfs_warn("hfs_file_read: mode = %07o\n",inode->i_mode);
+ return -EINVAL;
+ }
+ pos = *ppos;
+ if (pos < 0 || pos >= HFS_FORK_MAX) {
+ return 0;
+ }
+ size = inode->i_size;
+ if (pos > size) {
+ left = 0;
+ } else {
+ left = size - pos;
+ }
+ if (left > count) {
+ left = count;
+ }
+ if (left <= 0) {
+ return 0;
+ }
+ if ((read = hfs_do_read(inode, HFS_I(inode)->fork, pos,
+ buf, left, filp->f_reada != 0)) > 0) {
+ *ppos = pos + read;
+ filp->f_reada = 1;
+ }
+
+ return read;
+}
+
+/*
+ * hfs_file_write()
+ *
+ * This is the write() entry in the file_operations structure for
+ * "regular" files. The purpose is to transfer up to 'count' bytes
+ * to the file corresponding to 'inode' beginning at offset
+ * 'file->f_pos' from user-space at the address 'buf'. The return
+ * value is the number of bytes actually transferred.
+ */
+static hfs_rwret_t hfs_file_write(struct file * filp, const char * buf,
+ hfs_rwarg_t count, loff_t *ppos)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ struct hfs_fork *fork = HFS_I(inode)->fork;
+ hfs_s32 written, pos;
+
+ if (!S_ISREG(inode->i_mode)) {
+ hfs_warn("hfs_file_write: mode = %07o\n", inode->i_mode);
+ return -EINVAL;
+ }
+
+ pos = (filp->f_flags & O_APPEND) ? inode->i_size : *ppos;
+
+ if (pos < 0 || pos >= HFS_FORK_MAX) {
+ return 0;
+ }
+ if (count > HFS_FORK_MAX) {
+ count = HFS_FORK_MAX;
+ }
+ if ((written = hfs_do_write(inode, fork, pos, buf, count)) > 0)
+ pos += written;
+
+ *ppos = pos;
+ if (pos > inode->i_size) {
+ inode->i_size = pos;
+ mark_inode_dirty(inode);
+ }
+
+ return written;
+}
+
+/*
+ * hfs_file_truncate()
+ *
+ * This is the truncate() entry in the file_operations structure for
+ * "regular" files. The purpose is to change the length of the file
+ * corresponding to the given inode. Changes can either lengthen or
+ * shorten the file.
+ */
+static void hfs_file_truncate(struct inode * inode)
+{
+ struct hfs_fork *fork = HFS_I(inode)->fork;
+
+ fork->lsize = inode->i_size;
+ hfs_extent_adj(fork);
+ hfs_cat_mark_dirty(HFS_I(inode)->entry);
+
+ inode->i_size = fork->lsize;
+ inode->i_blocks = fork->psize;
+ mark_inode_dirty(inode);
+}
+
+/*
+ * xlate_to_user()
+ *
+ * Like copy_to_user() while translating CR->NL.
+ */
+static inline void xlate_to_user(char *buf, const char *data, int count)
+{
+ char ch;
+
+ while (count--) {
+ ch = *(data++);
+ put_user((ch == '\r') ? '\n' : ch, buf++);
+ }
+}
+
+/*
+ * xlate_from_user()
+ *
+ * Like copy_from_user() while translating NL->CR;
+ */
+static inline int xlate_from_user(char *data, const char *buf, int count)
+{
+ int i;
+
+ i = copy_from_user(data, buf, count);
+ count -= i;
+ while (count--) {
+ if (*data == '\n') {
+ *data = '\r';
+ }
+ ++data;
+ }
+ return i;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_do_read()
+ *
+ * This function transfers actual data from disk to user-space memory,
+ * returning the number of bytes successfully transferred. 'fork' tells
+ * which file on the disk to read from. 'pos' gives the offset into
+ * the Linux file at which to begin the transfer. Note that this will
+ * differ from 'filp->offset' in the case of an AppleDouble header file
+ * due to the block of metadata at the beginning of the file, which has
+ * no corresponding place in the HFS file. 'count' tells how many
+ * bytes to transfer. 'buf' gives an address in user-space to transfer
+ * the data to.
+ *
+ * This is based on Linus's minix_file_read().
+ * It has been changed to take into account that HFS files have no holes.
+ */
+hfs_s32 hfs_do_read(struct inode *inode, struct hfs_fork * fork, hfs_u32 pos,
+ char * buf, hfs_u32 count, int reada)
+{
+ kdev_t dev = inode->i_dev;
+ hfs_s32 size, chars, offset, block, blocks, read = 0;
+ int bhrequest, uptodate;
+ int convert = HFS_I(inode)->convert;
+ struct buffer_head ** bhb, ** bhe;
+ struct buffer_head * bhreq[NBUF];
+ struct buffer_head * buflist[NBUF];
+
+ /* split 'pos' in to block and (byte) offset components */
+ block = pos >> HFS_SECTOR_SIZE_BITS;
+ offset = pos & (HFS_SECTOR_SIZE-1);
+
+ /* compute the logical size of the fork in blocks */
+ size = (fork->lsize + (HFS_SECTOR_SIZE-1)) >> HFS_SECTOR_SIZE_BITS;
+
+ /* compute the number of physical blocks to be transferred */
+ blocks = (count+offset+HFS_SECTOR_SIZE-1) >> HFS_SECTOR_SIZE_BITS;
+
+ bhb = bhe = buflist;
+ if (reada) {
+ if (blocks < read_ahead[MAJOR(dev)] / (HFS_SECTOR_SIZE>>9)) {
+ blocks = read_ahead[MAJOR(dev)] / (HFS_SECTOR_SIZE>>9);
+ }
+ if (block + blocks > size) {
+ blocks = size - block;
+ }
+ }
+
+ /* We do this in a two stage process. We first try and
+ request as many blocks as we can, then we wait for the
+ first one to complete, and then we try and wrap up as many
+ as are actually done.
+
+ This routine is optimized to make maximum use of the
+ various buffers and caches. */
+
+ do {
+ bhrequest = 0;
+ uptodate = 1;
+ while (blocks) {
+ --blocks;
+ *bhb = hfs_getblk(fork, block++, 0);
+
+ if (!(*bhb)) {
+ /* Since there are no holes in HFS files
+ we must have encountered an error.
+ So, stop adding blocks to the queue. */
+ blocks = 0;
+ break;
+ }
+
+ if (!buffer_uptodate(*bhb)) {
+ uptodate = 0;
+ bhreq[bhrequest++] = *bhb;
+ }
+
+ if (++bhb == &buflist[NBUF]) {
+ bhb = buflist;
+ }
+
+ /* If the block we have on hand is uptodate,
+ go ahead and complete processing. */
+ if (uptodate) {
+ break;
+ }
+ if (bhb == bhe) {
+ break;
+ }
+ }
+
+ /* If the only block in the queue is bad then quit */
+ if (!(*bhe)) {
+ break;
+ }
+
+ /* Now request them all */
+ if (bhrequest) {
+ ll_rw_block(READ, bhrequest, bhreq);
+ }
+
+ do { /* Finish off all I/O that has actually completed */
+ char *p;
+
+ wait_on_buffer(*bhe);
+
+ if (!buffer_uptodate(*bhe)) {
+ /* read error? */
+ brelse(*bhe);
+ if (++bhe == &buflist[NBUF]) {
+ bhe = buflist;
+ }
+ count = 0;
+ break;
+ }
+
+ if (count < HFS_SECTOR_SIZE - offset) {
+ chars = count;
+ } else {
+ chars = HFS_SECTOR_SIZE - offset;
+ }
+ p = (*bhe)->b_data + offset;
+ if (convert) {
+ xlate_to_user(buf, p, chars);
+ } else {
+ chars -= copy_to_user(buf, p, chars);
+ if (!chars) {
+ brelse(*bhe);
+ count = 0;
+ if (!read)
+ read = -EFAULT;
+ break;
+ }
+ }
+ brelse(*bhe);
+ count -= chars;
+ buf += chars;
+ read += chars;
+ offset = 0;
+ if (++bhe == &buflist[NBUF]) {
+ bhe = buflist;
+ }
+ } while (count && (bhe != bhb) && !buffer_locked(*bhe));
+ } while (count);
+
+ /* Release the read-ahead blocks */
+ while (bhe != bhb) {
+ brelse(*bhe);
+ if (++bhe == &buflist[NBUF]) {
+ bhe = buflist;
+ }
+ }
+ if (!read) {
+ return -EIO;
+ }
+ return read;
+}
+
+/*
+ * hfs_do_write()
+ *
+ * This function transfers actual data from user-space memory to disk,
+ * returning the number of bytes successfully transferred. 'fork' tells
+ * which file on the disk to write to. 'pos' gives the offset into
+ * the Linux file at which to begin the transfer. Note that this will
+ * differ from 'filp->offset' in the case of an AppleDouble header file
+ * due to the block of metadata at the beginning of the file, which has
+ * no corresponding place in the HFS file. 'count' tells how many
+ * bytes to transfer. 'buf' gives an address in user-space to transfer
+ * the data from.
+ *
+ * This is just a minor edit of Linus's minix_file_write().
+ */
+hfs_s32 hfs_do_write(struct inode *inode, struct hfs_fork * fork, hfs_u32 pos,
+ const char * buf, hfs_u32 count)
+{
+ hfs_s32 written, c;
+ struct buffer_head * bh;
+ char * p;
+ int convert = HFS_I(inode)->convert;
+
+ written = 0;
+ while (written < count) {
+ bh = hfs_getblk(fork, pos/HFS_SECTOR_SIZE, 1);
+ if (!bh) {
+ if (!written) {
+ written = -ENOSPC;
+ }
+ break;
+ }
+ c = HFS_SECTOR_SIZE - (pos % HFS_SECTOR_SIZE);
+ if (c > count - written) {
+ c = count - written;
+ }
+ if (c != HFS_SECTOR_SIZE && !buffer_uptodate(bh)) {
+ ll_rw_block(READ, 1, &bh);
+ wait_on_buffer(bh);
+ if (!buffer_uptodate(bh)) {
+ brelse(bh);
+ if (!written) {
+ written = -EIO;
+ }
+ break;
+ }
+ }
+ p = (pos % HFS_SECTOR_SIZE) + bh->b_data;
+ c -= convert ? xlate_from_user(p, buf, c) :
+ copy_from_user(p, buf, c);
+ if (!c) {
+ brelse(bh);
+ if (!written)
+ written = -EFAULT;
+ break;
+ }
+ pos += c;
+ written += c;
+ buf += c;
+ mark_buffer_uptodate(bh, 1);
+ mark_buffer_dirty(bh);
+ brelse(bh);
+ }
+ if (written > 0) {
+ struct hfs_cat_entry *entry = fork->entry;
+
+ inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ if (pos > fork->lsize) {
+ fork->lsize = pos;
+ }
+ entry->modify_date = hfs_u_to_mtime(CURRENT_TIME);
+ hfs_cat_mark_dirty(entry);
+ }
+ return written;
+}
+
+/*
+ * hfs_file_fix_mode()
+ *
+ * Fixes up the permissions on a file after changing the write-inhibit bit.
+ */
+void hfs_file_fix_mode(struct hfs_cat_entry *entry)
+{
+ struct dentry **de = entry->sys_entry;
+ int i;
+
+ if (entry->u.file.flags & HFS_FIL_LOCK) {
+ for (i = 0; i < 4; ++i) {
+ if (de[i]) {
+ de[i]->d_inode->i_mode &= ~S_IWUGO;
+ }
+ }
+ } else {
+ for (i = 0; i < 4; ++i) {
+ if (de[i]) {
+ struct inode *inode = de[i]->d_inode;
+ inode->i_mode |= S_IWUGO;
+ inode->i_mode &=
+ ~HFS_SB(inode->i_sb)->s_umask;
+ }
+ }
+ }
+}
diff --git a/fs/hfs/file_cap.c b/fs/hfs/file_cap.c
new file mode 100644
index 00000000000000..f3481f57eb7e66
--- /dev/null
+++ b/fs/hfs/file_cap.c
@@ -0,0 +1,279 @@
+/*
+ * linux/fs/hfs/file_cap.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU General Public License.
+ *
+ * This file contains the file_ops and inode_ops for the metadata
+ * files under the CAP representation.
+ *
+ * The source code distribution of the Columbia AppleTalk Package for
+ * UNIX, version 6.0, (CAP) was used as a specification of the
+ * location and format of files used by CAP's Aufs. No code from CAP
+ * appears in hfs_fs. hfs_fs is not a work ``derived'' from CAP in
+ * the sense of intellectual property law.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs.h"
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+/*================ Forward declarations ================*/
+static loff_t cap_info_llseek(struct file *, loff_t,
+ int);
+static hfs_rwret_t cap_info_read(struct file *, char *,
+ hfs_rwarg_t, loff_t *);
+static hfs_rwret_t cap_info_write(struct file *, const char *,
+ hfs_rwarg_t, loff_t *);
+/*================ Function-like macros ================*/
+
+/*
+ * OVERLAPS()
+ *
+ * Determines if a given range overlaps the specified structure member
+ */
+#define OVERLAPS(START, END, TYPE, MEMB) \
+ ((END > offsetof(TYPE, MEMB)) && \
+ (START < offsetof(TYPE, MEMB) + sizeof(((TYPE *)0)->MEMB)))
+
+/*================ Global variables ================*/
+
+struct file_operations hfs_cap_info_operations = {
+ llseek: cap_info_llseek,
+ read: cap_info_read,
+ write: cap_info_write,
+ fsync: file_fsync,
+};
+
+struct inode_operations hfs_cap_info_inode_operations = {
+ setattr: hfs_notify_change_cap,
+};
+
+/*================ File-local functions ================*/
+
+/*
+ * cap_build_meta()
+ *
+ * Build the metadata structure.
+ */
+static void cap_build_meta(struct hfs_cap_info *meta,
+ struct hfs_cat_entry *entry)
+{
+ memset(meta, 0, sizeof(*meta));
+ memcpy(meta->fi_fndr, &entry->info, 32);
+ if ((entry->type == HFS_CDR_FIL) &&
+ (entry->u.file.flags & HFS_FIL_LOCK)) {
+ /* Couple the locked bit of the file to the
+ AFP {write,rename,delete} inhibit bits. */
+ hfs_put_hs(HFS_AFP_RDONLY, meta->fi_attr);
+ }
+ meta->fi_magic1 = HFS_CAP_MAGIC1;
+ meta->fi_version = HFS_CAP_VERSION;
+ meta->fi_magic = HFS_CAP_MAGIC;
+ meta->fi_bitmap = HFS_CAP_LONGNAME;
+ memcpy(meta->fi_macfilename, entry->key.CName.Name,
+ entry->key.CName.Len);
+ meta->fi_datemagic = HFS_CAP_DMAGIC;
+ meta->fi_datevalid = HFS_CAP_MDATE | HFS_CAP_CDATE;
+ hfs_put_nl(hfs_m_to_htime(entry->create_date), meta->fi_ctime);
+ hfs_put_nl(hfs_m_to_htime(entry->modify_date), meta->fi_mtime);
+ hfs_put_nl(CURRENT_TIME, meta->fi_utime);
+}
+
+static loff_t cap_info_llseek(struct file *file, loff_t offset, int origin)
+{
+ long long retval;
+
+ switch (origin) {
+ case 2:
+ offset += file->f_dentry->d_inode->i_size;
+ break;
+ case 1:
+ offset += file->f_pos;
+ }
+ retval = -EINVAL;
+ if (offset>=0 && offset<=HFS_FORK_MAX) {
+ if (offset != file->f_pos) {
+ file->f_pos = offset;
+ file->f_reada = 0;
+ file->f_version = ++event;
+ }
+ retval = offset;
+ }
+ return retval;
+}
+
+/*
+ * cap_info_read()
+ *
+ * This is the read() entry in the file_operations structure for CAP
+ * metadata files. The purpose is to transfer up to 'count' bytes
+ * from the file corresponding to 'inode' beginning at offset
+ * 'file->f_pos' to user-space at the address 'buf'. The return value
+ * is the number of bytes actually transferred.
+ */
+static hfs_rwret_t cap_info_read(struct file *filp, char *buf,
+ hfs_rwarg_t count, loff_t *ppos)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+ hfs_s32 left, size, read = 0;
+ hfs_u32 pos;
+
+ if (!S_ISREG(inode->i_mode)) {
+ hfs_warn("hfs_cap_info_read: mode = %07o\n", inode->i_mode);
+ return -EINVAL;
+ }
+
+ pos = *ppos;
+ if (pos > HFS_FORK_MAX) {
+ return 0;
+ }
+ size = inode->i_size;
+ if (pos > size) {
+ left = 0;
+ } else {
+ left = size - pos;
+ }
+ if (left > count) {
+ left = count;
+ }
+ if (left <= 0) {
+ return 0;
+ }
+
+ if (pos < sizeof(struct hfs_cap_info)) {
+ int memcount = sizeof(struct hfs_cap_info) - pos;
+ struct hfs_cap_info meta;
+
+ if (memcount > left) {
+ memcount = left;
+ }
+ cap_build_meta(&meta, entry);
+ memcount -= copy_to_user(buf, ((char *)&meta) + pos, memcount);
+ left -= memcount;
+ read += memcount;
+ pos += memcount;
+ buf += memcount;
+ }
+
+ if (left > 0) {
+ clear_user(buf, left);
+ pos += left;
+ }
+
+ if (read) {
+ inode->i_atime = CURRENT_TIME;
+ *ppos = pos;
+ mark_inode_dirty(inode);
+ }
+
+ return read;
+}
+
+/*
+ * cap_info_write()
+ *
+ * This is the write() entry in the file_operations structure for CAP
+ * metadata files. The purpose is to transfer up to 'count' bytes
+ * to the file corresponding to 'inode' beginning at offset
+ * '*ppos' from user-space at the address 'buf'.
+ * The return value is the number of bytes actually transferred.
+ */
+static hfs_rwret_t cap_info_write(struct file *filp, const char *buf,
+ hfs_rwarg_t count, loff_t *ppos)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ hfs_u32 pos, last;
+
+ if (!S_ISREG(inode->i_mode)) {
+ hfs_warn("hfs_file_write: mode = %07o\n", inode->i_mode);
+ return -EINVAL;
+ }
+ if (count <= 0) {
+ return 0;
+ }
+
+ pos = (filp->f_flags & O_APPEND) ? inode->i_size : *ppos;
+
+ if (pos > HFS_FORK_MAX) {
+ return 0;
+ }
+
+ last = pos + count;
+ if (last > HFS_FORK_MAX) {
+ last = HFS_FORK_MAX;
+ count = HFS_FORK_MAX - pos;
+ }
+
+ if (last > inode->i_size)
+ inode->i_size = last;
+
+ /* Only deal with the part we store in memory */
+ if (pos < sizeof(struct hfs_cap_info)) {
+ int end, mem_count;
+ struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+ struct hfs_cap_info meta;
+
+ mem_count = sizeof(struct hfs_cap_info) - pos;
+ if (mem_count > count) {
+ mem_count = count;
+ }
+ end = pos + mem_count;
+
+ cap_build_meta(&meta, entry);
+ mem_count -= copy_from_user(((char *)&meta) + pos, buf, mem_count);
+
+ /* Update finder attributes if changed */
+ if (OVERLAPS(pos, end, struct hfs_cap_info, fi_fndr)) {
+ memcpy(&entry->info, meta.fi_fndr, 32);
+ hfs_cat_mark_dirty(entry);
+ }
+
+ /* Update file flags if changed */
+ if (OVERLAPS(pos, end, struct hfs_cap_info, fi_attr) &&
+ (entry->type == HFS_CDR_FIL)) {
+ int locked = hfs_get_ns(&meta.fi_attr) &
+ htons(HFS_AFP_WRI);
+ hfs_u8 new_flags;
+
+ if (locked) {
+ new_flags = entry->u.file.flags | HFS_FIL_LOCK;
+ } else {
+ new_flags = entry->u.file.flags & ~HFS_FIL_LOCK;
+ }
+
+ if (new_flags != entry->u.file.flags) {
+ entry->u.file.flags = new_flags;
+ hfs_cat_mark_dirty(entry);
+ hfs_file_fix_mode(entry);
+ }
+ }
+
+ /* Update CrDat if changed */
+ if (OVERLAPS(pos, end, struct hfs_cap_info, fi_ctime)) {
+ entry->create_date =
+ hfs_h_to_mtime(hfs_get_nl(meta.fi_ctime));
+ hfs_cat_mark_dirty(entry);
+ }
+
+ /* Update MdDat if changed */
+ if (OVERLAPS(pos, end, struct hfs_cap_info, fi_mtime)) {
+ entry->modify_date =
+ hfs_h_to_mtime(hfs_get_nl(meta.fi_mtime));
+ hfs_cat_mark_dirty(entry);
+ }
+ }
+
+ *ppos = last;
+ inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+ return count;
+}
diff --git a/fs/hfs/file_hdr.c b/fs/hfs/file_hdr.c
new file mode 100644
index 00000000000000..790c052e9e53b7
--- /dev/null
+++ b/fs/hfs/file_hdr.c
@@ -0,0 +1,1031 @@
+/*
+ * linux/fs/hfs/file_hdr.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU General Public License.
+ *
+ * This file contains the file_ops and inode_ops for the metadata
+ * files under the AppleDouble and Netatalk representations.
+ *
+ * The source code distributions of Netatalk, versions 1.3.3b2 and
+ * 1.4b2, were used as a specification of the location and format of
+ * files used by Netatalk's afpd. No code from Netatalk appears in
+ * hfs_fs. hfs_fs is not a work ``derived'' from Netatalk in the
+ * sense of intellectual property law.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ *
+ * XXX: Note the reason that there is not bmap() for AppleDouble
+ * header files is that dynamic nature of their structure make it
+ * very difficult to safely mmap them. Maybe in the distant future
+ * I'll get bored enough to implement it.
+ */
+
+#include "hfs.h"
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+/* prodos types */
+#define PRODOSI_FTYPE_DIR 0x0F
+#define PRODOSI_FTYPE_TEXT 0x04
+#define PRODOSI_FTYPE_8BIT 0xFF
+#define PRODOSI_FTYPE_16BIT 0xB3
+
+#define PRODOSI_AUXTYPE_DIR 0x0200
+
+/*================ Forward declarations ================*/
+static loff_t hdr_llseek(struct file *, loff_t, int);
+static hfs_rwret_t hdr_read(struct file *, char *, hfs_rwarg_t, loff_t *);
+static hfs_rwret_t hdr_write(struct file *, const char *,
+ hfs_rwarg_t, loff_t *);
+/*================ Global variables ================*/
+
+struct file_operations hfs_hdr_operations = {
+ llseek: hdr_llseek,
+ read: hdr_read,
+ write: hdr_write,
+ fsync: file_fsync,
+};
+
+struct inode_operations hfs_hdr_inode_operations = {
+ setattr: hfs_notify_change_hdr,
+};
+
+const struct hfs_hdr_layout hfs_dbl_fil_hdr_layout = {
+ __constant_htonl(HFS_DBL_MAGIC), /* magic */
+ __constant_htonl(HFS_HDR_VERSION_2), /* version */
+ 6, /* entries */
+ { /* descr[] */
+ {HFS_HDR_FNAME, offsetof(struct hfs_dbl_hdr, real_name), ~0},
+ {HFS_HDR_DATES, offsetof(struct hfs_dbl_hdr, create_time), 16},
+ {HFS_HDR_FINFO, offsetof(struct hfs_dbl_hdr, finderinfo), 32},
+ {HFS_HDR_MACI, offsetof(struct hfs_dbl_hdr, fileinfo), 4},
+ {HFS_HDR_DID, offsetof(struct hfs_dbl_hdr, cnid), 4},
+ {HFS_HDR_RSRC, HFS_DBL_HDR_LEN, ~0}
+ },
+ { /* order[] */
+ (struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[0],
+ (struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[1],
+ (struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[2],
+ (struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[3],
+ (struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[4],
+ (struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[5]
+ }
+};
+
+const struct hfs_hdr_layout hfs_dbl_dir_hdr_layout = {
+ __constant_htonl(HFS_DBL_MAGIC), /* magic */
+ __constant_htonl(HFS_HDR_VERSION_2), /* version */
+ 5, /* entries */
+ { /* descr[] */
+ {HFS_HDR_FNAME, offsetof(struct hfs_dbl_hdr, real_name), ~0},
+ {HFS_HDR_DATES, offsetof(struct hfs_dbl_hdr, create_time), 16},
+ {HFS_HDR_FINFO, offsetof(struct hfs_dbl_hdr, finderinfo), 32},
+ {HFS_HDR_MACI, offsetof(struct hfs_dbl_hdr, fileinfo), 4},
+ {HFS_HDR_DID, offsetof(struct hfs_dbl_hdr, cnid), 4}
+ },
+ { /* order[] */
+ (struct hfs_hdr_descr *)&hfs_dbl_dir_hdr_layout.descr[0],
+ (struct hfs_hdr_descr *)&hfs_dbl_dir_hdr_layout.descr[1],
+ (struct hfs_hdr_descr *)&hfs_dbl_dir_hdr_layout.descr[2],
+ (struct hfs_hdr_descr *)&hfs_dbl_dir_hdr_layout.descr[3],
+ (struct hfs_hdr_descr *)&hfs_dbl_dir_hdr_layout.descr[4]
+ }
+};
+
+const struct hfs_hdr_layout hfs_nat2_hdr_layout = {
+ __constant_htonl(HFS_DBL_MAGIC), /* magic */
+ __constant_htonl(HFS_HDR_VERSION_2), /* version */
+ 9, /* entries */
+ { /* descr[] */
+ {HFS_HDR_FNAME, offsetof(struct hfs_dbl_hdr, real_name), ~0},
+ {HFS_HDR_COMNT, offsetof(struct hfs_dbl_hdr, comment), 0},
+ {HFS_HDR_DATES, offsetof(struct hfs_dbl_hdr, create_time), 16},
+ {HFS_HDR_FINFO, offsetof(struct hfs_dbl_hdr, finderinfo), 32},
+ {HFS_HDR_AFPI, offsetof(struct hfs_dbl_hdr, fileinfo), 4},
+ {HFS_HDR_DID, offsetof(struct hfs_dbl_hdr, cnid), 4},
+ {HFS_HDR_SNAME, offsetof(struct hfs_dbl_hdr, short_name), ~0},
+ {HFS_HDR_PRODOSI, offsetof(struct hfs_dbl_hdr, prodosi), 8},
+ {HFS_HDR_RSRC, HFS_NAT_HDR_LEN, ~0}
+ },
+ { /* order[] */
+ (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[0],
+ (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[1],
+ (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[2],
+ (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[3],
+ (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[4],
+ (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[5],
+ (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[6],
+ (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[7],
+ (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[8]
+ }
+};
+
+const struct hfs_hdr_layout hfs_nat_hdr_layout = {
+ __constant_htonl(HFS_DBL_MAGIC), /* magic */
+ __constant_htonl(HFS_HDR_VERSION_1), /* version */
+ 5, /* entries */
+ { /* descr[] */
+ {HFS_HDR_FNAME, offsetof(struct hfs_dbl_hdr, real_name), ~0},
+ {HFS_HDR_COMNT, offsetof(struct hfs_dbl_hdr, comment), 0},
+ {HFS_HDR_OLDI, offsetof(struct hfs_dbl_hdr, create_time), 16},
+ {HFS_HDR_FINFO, offsetof(struct hfs_dbl_hdr, finderinfo), 32},
+ {HFS_HDR_RSRC, HFS_NAT_HDR_LEN, ~0},
+ },
+ { /* order[] */
+ (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[0],
+ (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[1],
+ (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[2],
+ (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[3],
+ (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[4]
+ }
+};
+
+/*================ File-local variables ================*/
+
+static const char fstype[16] =
+ {'M','a','c','i','n','t','o','s','h',' ',' ',' ',' ',' ',' ',' '};
+
+/*================ File-local data types ================*/
+
+struct hdr_hdr {
+ hfs_lword_t magic;
+ hfs_lword_t version;
+ hfs_byte_t filler[16];
+ hfs_word_t entries;
+ hfs_byte_t descrs[12*HFS_HDR_MAX];
+} __attribute__((packed));
+
+/*================ File-local functions ================*/
+
+/*
+ * dlength()
+ */
+static int dlength(const struct hfs_hdr_descr *descr,
+ const struct hfs_cat_entry *entry)
+{
+ hfs_u32 length = descr->length;
+
+ /* handle auto-sized entries */
+ if (length == ~0) {
+ switch (descr->id) {
+ case HFS_HDR_DATA:
+ if (entry->type == HFS_CDR_FIL) {
+ length = entry->u.file.data_fork.lsize;
+ } else {
+ length = 0;
+ }
+ break;
+
+ case HFS_HDR_RSRC:
+ if (entry->type == HFS_CDR_FIL) {
+ length = entry->u.file.rsrc_fork.lsize;
+ } else {
+ length = 0;
+ }
+ break;
+
+ case HFS_HDR_FNAME:
+ length = entry->key.CName.Len;
+ break;
+
+ case HFS_HDR_SNAME:
+ default:
+ length = 0;
+ }
+ }
+ return length;
+}
+
+/*
+ * hdr_build_meta()
+ */
+static void hdr_build_meta(struct hdr_hdr *meta,
+ const struct hfs_hdr_layout *layout,
+ const struct hfs_cat_entry *entry)
+{
+ const struct hfs_hdr_descr *descr;
+ hfs_byte_t *ptr;
+ int lcv;
+
+ hfs_put_nl(layout->magic, meta->magic);
+ hfs_put_nl(layout->version, meta->version);
+ if (layout->version == htonl(HFS_HDR_VERSION_1)) {
+ memcpy(meta->filler, fstype, 16);
+ } else {
+ memset(meta->filler, 0, 16);
+ }
+ hfs_put_hs(layout->entries, meta->entries);
+ memset(meta->descrs, 0, sizeof(meta->descrs));
+ for (lcv = 0, descr = layout->descr, ptr = meta->descrs;
+ lcv < layout->entries; ++lcv, ++descr, ptr += 12) {
+ hfs_put_hl(descr->id, ptr);
+ hfs_put_hl(descr->offset, ptr + 4);
+ hfs_put_hl(dlength(descr, entry), ptr + 8);
+ }
+}
+
+/*
+ * dup_layout ()
+ */
+static struct hfs_hdr_layout *dup_layout(const struct hfs_hdr_layout *old)
+{
+ struct hfs_hdr_layout *new;
+ int lcv;
+
+ if (HFS_NEW(new)) {
+ memcpy(new, old, sizeof(*new));
+ for (lcv = 0; lcv < new->entries; ++lcv) {
+ new->order[lcv] = (struct hfs_hdr_descr *)
+ ((char *)(new->order[lcv]) +
+ ((char *)new - (char *)old));
+ }
+ }
+ return new;
+}
+
+/*
+ * init_layout()
+ */
+static inline void init_layout(struct hfs_hdr_layout *layout,
+ const hfs_byte_t *descrs)
+{
+ struct hfs_hdr_descr **base, **p, **q, *tmp;
+ int lcv, entries = layout->entries;
+
+ for (lcv = 0; lcv < entries; ++lcv, descrs += 12) {
+ layout->order[lcv] = &layout->descr[lcv];
+ layout->descr[lcv].id = hfs_get_hl(descrs);
+ layout->descr[lcv].offset = hfs_get_hl(descrs + 4);
+ layout->descr[lcv].length = hfs_get_hl(descrs + 8);
+ }
+ for (lcv = layout->entries; lcv < HFS_HDR_MAX; ++lcv) {
+ layout->order[lcv] = NULL;
+ layout->descr[lcv].id = 0;
+ layout->descr[lcv].offset = 0;
+ layout->descr[lcv].length = 0;
+ }
+
+ /* Sort the 'order' array using an insertion sort */
+ base = &layout->order[0];
+ for (p = (base+1); p < (base+entries); ++p) {
+ q=p;
+ while ((*q)->offset < (*(q-1))->offset) {
+ tmp = *q;
+ *q = *(q-1);
+ *(--q) = tmp;
+ if (q == base) break;
+ }
+ }
+}
+
+/*
+ * adjust_forks()
+ */
+static inline void adjust_forks(struct hfs_cat_entry *entry,
+ const struct hfs_hdr_layout *layout)
+{
+ int lcv;
+
+ for (lcv = 0; lcv < layout->entries; ++lcv) {
+ const struct hfs_hdr_descr *descr = &layout->descr[lcv];
+
+ if ((descr->id == HFS_HDR_DATA) &&
+ (descr->length != entry->u.file.data_fork.lsize)) {
+ entry->u.file.data_fork.lsize = descr->length;
+ hfs_extent_adj(&entry->u.file.data_fork);
+ } else if ((descr->id == HFS_HDR_RSRC) &&
+ (descr->length != entry->u.file.rsrc_fork.lsize)) {
+ entry->u.file.rsrc_fork.lsize = descr->length;
+ hfs_extent_adj(&entry->u.file.rsrc_fork);
+ }
+ }
+}
+
+/*
+ * get_dates()
+ */
+static void get_dates(const struct hfs_cat_entry *entry,
+ const struct inode *inode, hfs_u32 dates[3])
+{
+ dates[0] = hfs_m_to_htime(entry->create_date);
+ dates[1] = hfs_m_to_htime(entry->modify_date);
+ dates[2] = hfs_m_to_htime(entry->backup_date);
+}
+
+/*
+ * set_dates()
+ */
+static void set_dates(struct hfs_cat_entry *entry, struct inode *inode,
+ const hfs_u32 *dates)
+{
+ hfs_u32 tmp;
+
+ tmp = hfs_h_to_mtime(dates[0]);
+ if (entry->create_date != tmp) {
+ entry->create_date = tmp;
+ hfs_cat_mark_dirty(entry);
+ }
+ tmp = hfs_h_to_mtime(dates[1]);
+ if (entry->modify_date != tmp) {
+ entry->modify_date = tmp;
+ inode->i_ctime = inode->i_atime = inode->i_mtime =
+ hfs_h_to_utime(dates[1]);
+ hfs_cat_mark_dirty(entry);
+ }
+ tmp = hfs_h_to_mtime(dates[2]);
+ if (entry->backup_date != tmp) {
+ entry->backup_date = tmp;
+ hfs_cat_mark_dirty(entry);
+ }
+}
+
+loff_t hdr_llseek(struct file *file, loff_t offset, int origin)
+{
+ long long retval;
+
+ switch (origin) {
+ case 2:
+ offset += file->f_dentry->d_inode->i_size;
+ break;
+ case 1:
+ offset += file->f_pos;
+ }
+ retval = -EINVAL;
+ if (offset>=0 && offset<file->f_dentry->d_inode->i_size) {
+ if (offset != file->f_pos) {
+ file->f_pos = offset;
+ file->f_reada = 0;
+ file->f_version = ++event;
+ }
+ retval = offset;
+ }
+ return retval;
+}
+
+/*
+ * hdr_read()
+ *
+ * This is the read field in the inode_operations structure for
+ * header files. The purpose is to transfer up to 'count' bytes
+ * from the file corresponding to 'inode', beginning at
+ * 'filp->offset' bytes into the file. The data is transferred to
+ * user-space at the address 'buf'. Returns the number of bytes
+ * successfully transferred.
+ */
+/* XXX: what about the entry count changing on us? */
+static hfs_rwret_t hdr_read(struct file * filp, char * buf,
+ hfs_rwarg_t count, loff_t *ppos)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+ const struct hfs_hdr_layout *layout;
+ off_t start, length, offset;
+ loff_t pos = *ppos;
+ int left, lcv, read = 0;
+
+ if (!S_ISREG(inode->i_mode)) {
+ hfs_warn("hfs_hdr_read: mode = %07o\n",inode->i_mode);
+ return -EINVAL;
+ }
+
+ if (HFS_I(inode)->layout) {
+ layout = HFS_I(inode)->layout;
+ } else {
+ layout = HFS_I(inode)->default_layout;
+ }
+
+ /* Adjust count to fit within the bounds of the file */
+ if (pos != (unsigned)pos || pos >= inode->i_size || count <= 0) {
+ return 0;
+ } else if (count > inode->i_size - pos) {
+ count = inode->i_size - pos;
+ }
+
+ /* Handle the fixed-location portion */
+ length = sizeof(hfs_u32) + sizeof(hfs_u32) + 16 +
+ sizeof(hfs_u16) + layout->entries * (3 * sizeof(hfs_u32));
+ if (pos < length) {
+ struct hdr_hdr meta;
+
+ left = length - pos;
+ if (left > count) {
+ left = count;
+ }
+
+ hdr_build_meta(&meta, layout, entry);
+ left -= copy_to_user(buf, ((char *)&meta) + pos, left);
+ count -= left;
+ read += left;
+ pos += left;
+ buf += left;
+ }
+ if (!count) {
+ goto done;
+ }
+
+ /* Handle the actual data */
+ for (lcv = 0; count && (lcv < layout->entries); ++lcv) {
+ const struct hfs_hdr_descr *descr = layout->order[lcv];
+ struct hfs_fork *fork;
+ char tmp[16], *p;
+ off_t limit;
+
+ /* stop reading if we run out of descriptors early */
+ if (!descr) {
+ break;
+ }
+
+ /* find start and length of this entry */
+ start = descr->offset;
+ length = dlength(descr, entry);
+
+ /* Skip to next entry if this one is empty or isn't needed */
+ if (!length || (pos >= start + length)) {
+ continue;
+ }
+
+ /* Pad with zeros to the start of this entry if needed */
+ if (pos < start) {
+ left = start - pos;
+ if (left > count) {
+ left = count;
+ }
+ clear_user(buf, left);
+ count -= left;
+ read += left;
+ pos += left;
+ buf += left;
+ }
+ if (!count) {
+ goto done;
+ }
+
+ /* locate and/or construct the data for this entry */
+ fork = NULL;
+ p = NULL;
+ switch (descr->id) {
+ case HFS_HDR_DATA:
+ fork = &entry->u.file.data_fork;
+ limit = fork->lsize;
+ break;
+
+ case HFS_HDR_RSRC:
+ fork = &entry->u.file.rsrc_fork;
+ limit = fork->lsize;
+ break;
+
+ case HFS_HDR_FNAME:
+ p = entry->key.CName.Name;
+ limit = entry->key.CName.Len;
+ break;
+
+ case HFS_HDR_OLDI:
+ case HFS_HDR_DATES:
+ get_dates(entry, inode, (hfs_u32 *)tmp);
+ if (descr->id == HFS_HDR_DATES) {
+ /* XXX: access date. hfsplus actually
+ has this. */
+ memcpy(tmp + 12, tmp + 4, 4);
+ } else if ((entry->type == HFS_CDR_FIL) &&
+ (entry->u.file.flags & HFS_FIL_LOCK)) {
+ hfs_put_hl(HFS_AFP_RDONLY, tmp + 12);
+ } else {
+ hfs_put_nl(0, tmp + 12);
+ }
+ p = tmp;
+ limit = 16;
+ break;
+
+ case HFS_HDR_FINFO:
+ p = (char *)&entry->info;
+ limit = 32;
+ break;
+
+ case HFS_HDR_AFPI:
+ /* XXX: this needs to do more mac->afp mappings */
+ hfs_put_ns(0, tmp);
+ if ((entry->type == HFS_CDR_FIL) &&
+ (entry->u.file.flags & HFS_FIL_LOCK)) {
+ hfs_put_hs(HFS_AFP_RDONLY, tmp + 2);
+ } else {
+ hfs_put_ns(0, tmp + 2);
+ }
+ p = tmp;
+ limit = 4;
+ break;
+
+ case HFS_HDR_PRODOSI:
+ /* XXX: this needs to do mac->prodos translations */
+ memset(tmp, 0, 8);
+#if 0
+ hfs_put_ns(0, tmp); /* access */
+ hfs_put_ns(0, tmp); /* type */
+ hfs_put_nl(0, tmp); /* aux type */
+#endif
+ p = tmp;
+ limit = 8;
+ break;
+
+ case HFS_HDR_MACI:
+ hfs_put_ns(0, tmp);
+ if (entry->type == HFS_CDR_FIL) {
+ hfs_put_hs(entry->u.file.flags, tmp + 2);
+ } else {
+ hfs_put_ns(entry->u.dir.flags, tmp + 2);
+ }
+ p = tmp;
+ limit = 4;
+ break;
+
+ case HFS_HDR_DID:
+ /* if it's rootinfo, stick the next available did in
+ * the did slot. */
+ limit = 4;
+ if (entry->cnid == htonl(HFS_ROOT_CNID)) {
+ struct hfs_mdb *mdb = entry->mdb;
+ const struct hfs_name *reserved =
+ HFS_SB(mdb->sys_mdb)->s_reserved2;
+
+ while (reserved->Len) {
+ if (hfs_streq(reserved->Name,
+ reserved->Len,
+ entry->key.CName.Name,
+ entry->key.CName.Len)) {
+ hfs_put_hl(mdb->next_id, tmp);
+ p = tmp;
+ goto hfs_did_done;
+ }
+ reserved++;
+ }
+ }
+ p = (char *) &entry->cnid;
+hfs_did_done:
+ break;
+
+ case HFS_HDR_SNAME:
+ default:
+ limit = 0;
+ }
+
+ /* limit the transfer to the available data
+ of to the stated length of the entry. */
+ if (length > limit) {
+ length = limit;
+ }
+ offset = pos - start;
+ left = length - offset;
+ if (left > count) {
+ left = count;
+ }
+ if (left <= 0) {
+ continue;
+ }
+
+ /* transfer the data */
+ if (p) {
+ left -= copy_to_user(buf, p + offset, left);
+ } else if (fork) {
+ left = hfs_do_read(inode, fork, offset, buf, left,
+ filp->f_reada != 0);
+ if (left > 0) {
+ filp->f_reada = 1;
+ } else if (!read) {
+ return left;
+ } else {
+ goto done;
+ }
+ }
+ count -= left;
+ read += left;
+ pos += left;
+ buf += left;
+ }
+
+ /* Pad the file out with zeros */
+ if (count) {
+ clear_user(buf, count);
+ read += count;
+ pos += count;
+ }
+
+done:
+ if (read) {
+ inode->i_atime = CURRENT_TIME;
+ *ppos = pos;
+ mark_inode_dirty(inode);
+ }
+ return read;
+}
+
+/*
+ * hdr_write()
+ *
+ * This is the write() entry in the file_operations structure for
+ * header files. The purpose is to transfer up to 'count' bytes
+ * to the file corresponding to 'inode' beginning at offset
+ * '*ppos' from user-space at the address 'buf'.
+ * The return value is the number of bytes actually transferred.
+ */
+static hfs_rwret_t hdr_write(struct file *filp, const char *buf,
+ hfs_rwarg_t count, loff_t *ppos)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+ struct hfs_hdr_layout *layout;
+ off_t start, length, offset;
+ int left, lcv, written = 0;
+ struct hdr_hdr meta;
+ int built_meta = 0;
+ loff_t pos;
+
+ if (!S_ISREG(inode->i_mode)) {
+ hfs_warn("hfs_hdr_write: mode = %07o\n", inode->i_mode);
+ return -EINVAL;
+ }
+
+ pos = (filp->f_flags & O_APPEND) ? inode->i_size : *ppos;
+
+ if (count <= 0 || pos != (unsigned)pos) {
+ return 0;
+ }
+
+ if (!HFS_I(inode)->layout) {
+ HFS_I(inode)->layout = dup_layout(HFS_I(inode)->default_layout);
+ }
+ layout = HFS_I(inode)->layout;
+
+ /* Handle the 'magic', 'version', 'filler' and 'entries' fields */
+ length = sizeof(hfs_u32) + sizeof(hfs_u32) + 16 + sizeof(hfs_u16);
+ if (pos < length) {
+ hdr_build_meta(&meta, layout, entry);
+ built_meta = 1;
+
+ left = length - pos;
+ if (left > count) {
+ left = count;
+ }
+
+ left -= copy_from_user(((char *)&meta) + pos, buf, left);
+ layout->magic = hfs_get_nl(meta.magic);
+ layout->version = hfs_get_nl(meta.version);
+ layout->entries = hfs_get_hs(meta.entries);
+ if (layout->entries > HFS_HDR_MAX) {
+ /* XXX: should allocate slots dynamically */
+ hfs_warn("hfs_hdr_write: TRUNCATING TO %d "
+ "DESCRIPTORS\n", HFS_HDR_MAX);
+ layout->entries = HFS_HDR_MAX;
+ }
+
+ count -= left;
+ written += left;
+ pos += left;
+ buf += left;
+ }
+ if (!count) {
+ goto done;
+ }
+
+ /* We know for certain how many entries we have, so process them */
+ length += layout->entries * 3 * sizeof(hfs_u32);
+ if (pos < length) {
+ if (!built_meta) {
+ hdr_build_meta(&meta, layout, entry);
+ }
+
+ left = length - pos;
+ if (left > count) {
+ left = count;
+ }
+
+ left -= copy_from_user(((char *)&meta) + pos, buf, left);
+ init_layout(layout, meta.descrs);
+
+ count -= left;
+ written += left;
+ pos += left;
+ buf += left;
+
+ /* Handle possible size changes for the forks */
+ if (entry->type == HFS_CDR_FIL) {
+ adjust_forks(entry, layout);
+ hfs_cat_mark_dirty(entry);
+ }
+ }
+
+ /* Handle the actual data */
+ for (lcv = 0; count && (lcv < layout->entries); ++lcv) {
+ struct hfs_hdr_descr *descr = layout->order[lcv];
+ struct hfs_fork *fork;
+ char tmp[16], *p;
+ off_t limit;
+
+ /* stop writing if we run out of descriptors early */
+ if (!descr) {
+ break;
+ }
+
+ /* find start and length of this entry */
+ start = descr->offset;
+ if ((descr->id == HFS_HDR_DATA) ||
+ (descr->id == HFS_HDR_RSRC)) {
+ if (entry->type == HFS_CDR_FIL) {
+ length = 0x7fffffff - start;
+ } else {
+ continue;
+ }
+ } else {
+ length = dlength(descr, entry);
+ }
+
+ /* Trim length to avoid overlap with the next entry */
+ if (layout->order[lcv+1] &&
+ ((start + length) > layout->order[lcv+1]->offset)) {
+ length = layout->order[lcv+1]->offset - start;
+ }
+
+ /* Skip to next entry if this one is empty or isn't needed */
+ if (!length || (pos >= start + length)) {
+ continue;
+ }
+
+ /* Skip any padding that may exist between entries */
+ if (pos < start) {
+ left = start - pos;
+ if (left > count) {
+ left = count;
+ }
+ count -= left;
+ written += left;
+ pos += left;
+ buf += left;
+ }
+ if (!count) {
+ goto done;
+ }
+
+ /* locate and/or construct the data for this entry */
+ fork = NULL;
+ p = NULL;
+ switch (descr->id) {
+ case HFS_HDR_DATA:
+#if 0
+/* Can't yet write to the data fork via a header file, since there is the
+ * possibility to write via the data file, and the only locking is at the
+ * inode level.
+ */
+ fork = &entry->u.file.data_fork;
+ limit = length;
+#else
+ limit = 0;
+#endif
+ break;
+
+ case HFS_HDR_RSRC:
+ fork = &entry->u.file.rsrc_fork;
+ limit = length;
+ break;
+
+ case HFS_HDR_OLDI:
+ case HFS_HDR_DATES:
+ get_dates(entry, inode, (hfs_u32 *)tmp);
+ if (descr->id == HFS_HDR_DATES) {
+ memcpy(tmp + 12, tmp + 4, 4);
+ } else if ((entry->type == HFS_CDR_FIL) &&
+ (entry->u.file.flags & HFS_FIL_LOCK)) {
+ hfs_put_hl(HFS_AFP_RDONLY, tmp + 12);
+ } else {
+ hfs_put_nl(0, tmp + 12);
+ }
+ p = tmp;
+ limit = 16;
+ break;
+
+ case HFS_HDR_FINFO:
+ p = (char *)&entry->info;
+ limit = 32;
+ break;
+
+ case HFS_HDR_AFPI:
+ hfs_put_ns(0, tmp);
+ if ((entry->type == HFS_CDR_FIL) &&
+ (entry->u.file.flags & HFS_FIL_LOCK)) {
+ hfs_put_hs(HFS_AFP_RDONLY, tmp + 2);
+ } else {
+ hfs_put_ns(0, tmp + 2);
+ }
+ p = tmp;
+ limit = 4;
+ break;
+
+ case HFS_HDR_PRODOSI:
+ /* XXX: this needs to do mac->prodos translations */
+ memset(tmp, 0, 8);
+#if 0
+ hfs_put_ns(0, tmp); /* access */
+ hfs_put_ns(0, tmp); /* type */
+ hfs_put_nl(0, tmp); /* aux type */
+#endif
+ p = tmp;
+ limit = 8;
+ break;
+
+ case HFS_HDR_MACI:
+ hfs_put_ns(0, tmp);
+ if (entry->type == HFS_CDR_FIL) {
+ hfs_put_hs(entry->u.file.flags, tmp + 2);
+ } else {
+ hfs_put_ns(entry->u.dir.flags, tmp + 2);
+ }
+ p = tmp;
+ limit = 4;
+ break;
+
+ case HFS_HDR_FNAME: /* Can't rename a file this way */
+ case HFS_HDR_DID: /* can't specify a did this way */
+ default:
+ limit = 0;
+ }
+
+ /* limit the transfer to the available data
+ of to the stated length of the entry. */
+ if (length > limit) {
+ length = limit;
+ }
+ offset = pos - start;
+ left = length - offset;
+ if (left > count) {
+ left = count;
+ }
+ if (left <= 0) {
+ continue;
+ }
+
+ /* transfer the data from user space */
+ if (p) {
+ left -= copy_from_user(p + offset, buf, left);
+ } else if (fork) {
+ left = hfs_do_write(inode, fork, offset, buf, left);
+ }
+
+ /* process the data */
+ switch (descr->id) {
+ case HFS_HDR_OLDI:
+ set_dates(entry, inode, (hfs_u32 *)tmp);
+ if (entry->type == HFS_CDR_FIL) {
+ hfs_u8 new_flags = entry->u.file.flags;
+
+ if (hfs_get_nl(tmp+12) & htonl(HFS_AFP_WRI)) {
+ new_flags |= HFS_FIL_LOCK;
+ } else {
+ new_flags &= ~HFS_FIL_LOCK;
+ }
+
+ if (new_flags != entry->u.file.flags) {
+ entry->u.file.flags = new_flags;
+ hfs_cat_mark_dirty(entry);
+ hfs_file_fix_mode(entry);
+ }
+ }
+ break;
+
+ case HFS_HDR_DATES:
+ set_dates(entry, inode, (hfs_u32 *)tmp);
+ break;
+
+ case HFS_HDR_FINFO:
+ hfs_cat_mark_dirty(entry);
+ break;
+
+ case HFS_HDR_MACI:
+ if (entry->type == HFS_CDR_DIR) {
+ hfs_u16 new_flags = hfs_get_ns(tmp + 2);
+
+ if (entry->u.dir.flags != new_flags) {
+ entry->u.dir.flags = new_flags;
+ hfs_cat_mark_dirty(entry);
+ }
+ } else {
+ hfs_u8 new_flags = tmp[3];
+ hfs_u8 changed = entry->u.file.flags^new_flags;
+
+ if (changed) {
+ entry->u.file.flags = new_flags;
+ hfs_cat_mark_dirty(entry);
+ if (changed & HFS_FIL_LOCK) {
+ hfs_file_fix_mode(entry);
+ }
+ }
+ }
+ break;
+
+ case HFS_HDR_DATA:
+ case HFS_HDR_RSRC:
+ if (left <= 0) {
+ if (!written) {
+ return left;
+ } else {
+ goto done;
+ }
+ } else if (fork->lsize > descr->length) {
+ descr->length = fork->lsize;
+ }
+ break;
+
+ case HFS_HDR_FNAME: /* Can't rename a file this way */
+ case HFS_HDR_DID: /* Can't specify a did this way */
+ case HFS_HDR_PRODOSI: /* not implemented yet */
+ case HFS_HDR_AFPI: /* ditto */
+ default:
+ break;
+ }
+
+ count -= left;
+ written += left;
+ pos += left;
+ buf += left;
+ }
+
+ /* Skip any padding at the end */
+ if (count) {
+ written += count;
+ pos += count;
+ }
+
+done:
+ *ppos = pos;
+ if (written > 0) {
+ if (pos > inode->i_size)
+ inode->i_size = pos;
+ inode->i_mtime = inode->i_atime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+ }
+ return written;
+}
+
+/*
+ * hdr_truncate()
+ *
+ * This is the truncate field in the inode_operations structure for
+ * header files. The purpose is to allocate or release blocks as needed
+ * to satisfy a change in file length.
+ */
+void hdr_truncate(struct inode *inode, size_t size)
+{
+ struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+ struct hfs_hdr_layout *layout;
+ int lcv, last;
+
+ inode->i_size = size;
+ if (!HFS_I(inode)->layout) {
+ HFS_I(inode)->layout = dup_layout(HFS_I(inode)->default_layout);
+ }
+ layout = HFS_I(inode)->layout;
+
+ last = layout->entries - 1;
+ for (lcv = 0; lcv <= last; ++lcv) {
+ struct hfs_hdr_descr *descr = layout->order[lcv];
+ struct hfs_fork *fork;
+ hfs_u32 offset;
+
+ if (!descr) {
+ break;
+ }
+
+ if (descr->id == HFS_HDR_RSRC) {
+ fork = &entry->u.file.rsrc_fork;
+#if 0
+/* Can't yet truncate the data fork via a header file, since there is the
+ * possibility to truncate via the data file, and the only locking is at
+ * the inode level.
+ */
+ } else if (descr->id == HFS_HDR_DATA) {
+ fork = &entry->u.file.data_fork;
+#endif
+ } else {
+ continue;
+ }
+
+ offset = descr->offset;
+
+ if ((lcv != last) && ((offset + descr->length) <= size)) {
+ continue;
+ }
+
+ if (offset < size) {
+ descr->length = size - offset;
+ } else {
+ descr->length = 0;
+ }
+ if (fork->lsize != descr->length) {
+ fork->lsize = descr->length;
+ hfs_extent_adj(fork);
+ hfs_cat_mark_dirty(entry);
+ }
+ }
+}
diff --git a/fs/hfs/hfs.h b/fs/hfs/hfs.h
new file mode 100644
index 00000000000000..f266bc0c421357
--- /dev/null
+++ b/fs/hfs/hfs.h
@@ -0,0 +1,550 @@
+/*
+ * linux/fs/hfs/hfs.h
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU General Public License.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ */
+
+#ifndef _HFS_H
+#define _HFS_H
+
+#include <linux/hfs_sysdep.h>
+
+#define HFS_NEW(X) ((X) = hfs_malloc(sizeof(*(X))))
+#define HFS_DELETE(X) do { hfs_free((X), sizeof(*(X))); (X) = NULL; } \
+ while (0)
+
+/* offsets to various blocks */
+#define HFS_DD_BLK 0 /* Driver Descriptor block */
+#define HFS_PMAP_BLK 1 /* First block of partition map */
+#define HFS_MDB_BLK 2 /* Block (w/i partition) of MDB */
+
+/* magic numbers for various disk blocks */
+#define HFS_DRVR_DESC_MAGIC 0x4552 /* "ER": driver descriptor map */
+#define HFS_OLD_PMAP_MAGIC 0x5453 /* "TS": old-type partition map */
+#define HFS_NEW_PMAP_MAGIC 0x504D /* "PM": new-type partition map */
+#define HFS_SUPER_MAGIC 0x4244 /* "BD": HFS MDB (super block) */
+#define HFS_MFS_SUPER_MAGIC 0xD2D7 /* MFS MDB (super block) */
+
+/* magic numbers for various internal structures */
+#define HFS_FILE_MAGIC 0x4801
+#define HFS_DIR_MAGIC 0x4802
+#define HFS_MDB_MAGIC 0x4803
+#define HFS_EXT_MAGIC 0x4804 /* XXX currently unused */
+#define HFS_BREC_MAGIC 0x4811 /* XXX currently unused */
+#define HFS_BTREE_MAGIC 0x4812
+#define HFS_BNODE_MAGIC 0x4813
+
+/* various FIXED size parameters */
+#define HFS_SECTOR_SIZE 512 /* size of an HFS sector */
+#define HFS_SECTOR_SIZE_BITS 9 /* log_2(HFS_SECTOR_SIZE) */
+#define HFS_NAMELEN 31 /* maximum length of an HFS filename */
+#define HFS_NAMEMAX (3*31) /* max size of ENCODED filename */
+#define HFS_BM_MAXBLOCKS (16) /* max number of bitmap blocks */
+#define HFS_BM_BPB (8*HFS_SECTOR_SIZE) /* number of bits per bitmap block */
+#define HFS_MAX_VALENCE 32767U
+#define HFS_FORK_MAX (0x7FFFFFFF)
+
+/* Meanings of the drAtrb field of the MDB,
+ * Reference: _Inside Macintosh: Files_ p. 2-61
+ */
+#define HFS_SB_ATTRIB_HLOCK 0x0080
+#define HFS_SB_ATTRIB_CLEAN 0x0100
+#define HFS_SB_ATTRIB_SPARED 0x0200
+#define HFS_SB_ATTRIB_SLOCK 0x8000
+
+/* 2**16 - 1 */
+#define HFS_USHRT_MAX 65535
+
+/* Some special File ID numbers */
+#define HFS_POR_CNID 1 /* Parent Of the Root */
+#define HFS_ROOT_CNID 2 /* ROOT directory */
+#define HFS_EXT_CNID 3 /* EXTents B-tree */
+#define HFS_CAT_CNID 4 /* CATalog B-tree */
+#define HFS_BAD_CNID 5 /* BAD blocks file */
+#define HFS_ALLOC_CNID 6 /* ALLOCation file (HFS+) */
+#define HFS_START_CNID 7 /* STARTup file (HFS+) */
+#define HFS_ATTR_CNID 8 /* ATTRibutes file (HFS+) */
+#define HFS_EXCH_CNID 15 /* ExchangeFiles temp id */
+
+/* values for hfs_cat_rec.cdrType */
+#define HFS_CDR_DIR 0x01 /* folder (directory) */
+#define HFS_CDR_FIL 0x02 /* file */
+#define HFS_CDR_THD 0x03 /* folder (directory) thread */
+#define HFS_CDR_FTH 0x04 /* file thread */
+
+/* legal values for hfs_ext_key.FkType and hfs_file.fork */
+#define HFS_FK_DATA 0x00
+#define HFS_FK_RSRC 0xFF
+
+/* bits in hfs_fil_entry.Flags */
+#define HFS_FIL_LOCK 0x01 /* locked */
+#define HFS_FIL_THD 0x02 /* file thread */
+#define HFS_FIL_DOPEN 0x04 /* data fork open */
+#define HFS_FIL_ROPEN 0x08 /* resource fork open */
+#define HFS_FIL_DIR 0x10 /* directory (always clear) */
+#define HFS_FIL_RSRV1 0x20 /* reserved */
+#define HFS_FIL_NOCOPY 0x40 /* copy-protected file */
+#define HFS_FIL_USED 0x80 /* open */
+
+/* bits in hfs_dir_entry.Flags. dirflags is 16 bits. */
+#define HFS_DIR_LOCK 0x01 /* locked */
+#define HFS_DIR_THD 0x02 /* directory thread */
+#define HFS_DIR_INEXPFOLDER 0x04 /* in a shared area */
+#define HFS_DIR_MOUNTED 0x08 /* mounted */
+#define HFS_DIR_DIR 0x10 /* directory (always set) */
+#define HFS_DIR_EXPFOLDER 0x20 /* share point */
+#define HFS_DIR_RSRV1 0x40 /* reserved */
+#define HFS_DIR_RSRV2 0x80 /* reserved */
+
+/* Access types used when requesting access to a B-node */
+#define HFS_LOCK_NONE 0x0000 /* Illegal */
+#define HFS_LOCK_READ 0x0001 /* read-only access */
+#define HFS_LOCK_RESRV 0x0002 /* might potentially modify */
+#define HFS_LOCK_WRITE 0x0003 /* will modify now (exclusive access) */
+#define HFS_LOCK_MASK 0x000f
+
+/* Flags field of the hfs_path_elem */
+#define HFS_BPATH_FIRST 0x0100
+#define HFS_BPATH_OVERFLOW 0x0200
+#define HFS_BPATH_UNDERFLOW 0x0400
+#define HFS_BPATH_MASK 0x0f00
+
+/* Flags for hfs_bfind() */
+#define HFS_BFIND_EXACT 0x0010
+#define HFS_BFIND_LOCK 0x0020
+
+/* Modes for hfs_bfind() */
+#define HFS_BFIND_WRITE (HFS_LOCK_RESRV|HFS_BFIND_EXACT|HFS_BFIND_LOCK)
+#define HFS_BFIND_READ_EQ (HFS_LOCK_READ|HFS_BFIND_EXACT)
+#define HFS_BFIND_READ_LE (HFS_LOCK_READ)
+#define HFS_BFIND_INSERT (HFS_LOCK_RESRV|HFS_BPATH_FIRST|HFS_BPATH_OVERFLOW)
+#define HFS_BFIND_DELETE \
+ (HFS_LOCK_RESRV|HFS_BFIND_EXACT|HFS_BPATH_FIRST|HFS_BPATH_UNDERFLOW)
+
+/*======== HFS structures as they appear on the disk ========*/
+
+/* Pascal-style string of up to 31 characters */
+struct hfs_name {
+ hfs_byte_t Len;
+ hfs_byte_t Name[31];
+} __attribute__((packed));
+
+typedef struct {
+ hfs_word_t v;
+ hfs_word_t h;
+} hfs_point_t;
+
+typedef struct {
+ hfs_word_t top;
+ hfs_word_t left;
+ hfs_word_t bottom;
+ hfs_word_t right;
+} hfs_rect_t;
+
+typedef struct {
+ hfs_lword_t fdType;
+ hfs_lword_t fdCreator;
+ hfs_word_t fdFlags;
+ hfs_point_t fdLocation;
+ hfs_word_t fdFldr;
+} __attribute__((packed)) hfs_finfo_t;
+
+typedef struct {
+ hfs_word_t fdIconID;
+ hfs_byte_t fdUnused[8];
+ hfs_word_t fdComment;
+ hfs_lword_t fdPutAway;
+} __attribute__((packed)) hfs_fxinfo_t;
+
+typedef struct {
+ hfs_rect_t frRect;
+ hfs_word_t frFlags;
+ hfs_point_t frLocation;
+ hfs_word_t frView;
+} __attribute__((packed)) hfs_dinfo_t;
+
+typedef struct {
+ hfs_point_t frScroll;
+ hfs_lword_t frOpenChain;
+ hfs_word_t frUnused;
+ hfs_word_t frComment;
+ hfs_lword_t frPutAway;
+} __attribute__((packed)) hfs_dxinfo_t;
+
+union hfs_finder_info {
+ struct {
+ hfs_finfo_t finfo;
+ hfs_fxinfo_t fxinfo;
+ } file;
+ struct {
+ hfs_dinfo_t dinfo;
+ hfs_dxinfo_t dxinfo;
+ } dir;
+};
+
+/* A btree record key on disk */
+struct hfs_bkey {
+ hfs_byte_t KeyLen; /* number of bytes in the key */
+ hfs_byte_t value[1]; /* (KeyLen) bytes of key */
+} __attribute__((packed));
+
+/* Cast to a pointer to a generic bkey */
+#define HFS_BKEY(X) (((void)((X)->KeyLen)), ((struct hfs_bkey *)(X)))
+
+/* The key used in the catalog b-tree: */
+struct hfs_cat_key {
+ hfs_byte_t KeyLen; /* number of bytes in the key */
+ hfs_byte_t Resrv1; /* padding */
+ hfs_lword_t ParID; /* CNID of the parent dir */
+ struct hfs_name CName; /* The filename of the entry */
+} __attribute__((packed));
+
+/* The key used in the extents b-tree: */
+struct hfs_ext_key {
+ hfs_byte_t KeyLen; /* number of bytes in the key */
+ hfs_byte_t FkType; /* HFS_FK_{DATA,RSRC} */
+ hfs_lword_t FNum; /* The File ID of the file */
+ hfs_word_t FABN; /* allocation blocks number*/
+} __attribute__((packed));
+
+/*======== Data structures kept in memory ========*/
+
+/*
+ * struct hfs_mdb
+ *
+ * The fields from the MDB of an HFS filesystem
+ */
+struct hfs_mdb {
+ int magic; /* A magic number */
+ unsigned char vname[28]; /* The volume name */
+ hfs_sysmdb sys_mdb; /* superblock */
+ hfs_buffer buf; /* The hfs_buffer
+ holding the real
+ superblock (aka VIB
+ or MDB) */
+ hfs_buffer alt_buf; /* The hfs_buffer holding
+ the alternate superblock */
+ hfs_buffer bitmap[16]; /* The hfs_buffer holding the
+ allocation bitmap */
+ struct hfs_btree * ext_tree; /* Information about
+ the extents b-tree */
+ struct hfs_btree * cat_tree; /* Information about
+ the catalog b-tree */
+ hfs_u32 file_count; /* The number of
+ regular files in
+ the filesystem */
+ hfs_u32 dir_count; /* The number of
+ directories in the
+ filesystem */
+ hfs_u32 next_id; /* The next available
+ file id number */
+ hfs_u32 clumpablks; /* The number of allocation
+ blocks to try to add when
+ extending a file */
+ hfs_u32 write_count; /* The number of MDB
+ writes (a sort of
+ version number) */
+ hfs_u32 fs_start; /* The first 512-byte
+ block represented
+ in the bitmap */
+ hfs_u32 create_date; /* In network byte-order */
+ hfs_u32 modify_date; /* In network byte-order */
+ hfs_u32 backup_date; /* In network byte-order */
+ hfs_u16 root_files; /* The number of
+ regular
+ (non-directory)
+ files in the root
+ directory */
+ hfs_u16 root_dirs; /* The number of
+ directories in the
+ root directory */
+ hfs_u16 fs_ablocks; /* The number of
+ allocation blocks
+ in the filesystem */
+ hfs_u16 free_ablocks; /* The number of unused
+ allocation blocks
+ in the filesystem */
+ hfs_u32 alloc_blksz; /* The number of
+ 512-byte blocks per
+ "allocation block" */
+ hfs_u16 attrib; /* Attribute word */
+ hfs_wait_queue rename_wait;
+ int rename_lock;
+ hfs_wait_queue bitmap_wait;
+ int bitmap_lock;
+ struct list_head entry_dirty;
+};
+
+/*
+ * struct hfs_extent
+ *
+ * The offset to allocation block mapping for a given file is
+ * contained in a series of these structures. Each (struct
+ * hfs_extent) records up to three runs of contiguous allocation
+ * blocks. An allocation block is a contiguous group of physical
+ * blocks.
+ */
+struct hfs_extent {
+ int magic; /* A magic number */
+ unsigned short start; /* Where in the file this record
+ begins (in allocation blocks) */
+ unsigned short end; /* Where in the file this record
+ ends (in allocation blocks) */
+ unsigned short block[3]; /* The allocation block on disk which
+ begins this extent */
+ unsigned short length[3]; /* The number of allocation blocks
+ in this extent */
+ struct hfs_extent *next; /* Next extent record for this file */
+ struct hfs_extent *prev; /* Previous extent record for this file */
+ int count; /* Number of times it is used */
+};
+
+/*
+ * struct hfs_dir
+ *
+ * This structure holds information specific
+ * to a directory in an HFS filesystem.
+ */
+struct hfs_dir {
+ int magic; /* A magic number */
+ hfs_u16 flags;
+ hfs_u16 dirs; /* Number of directories in this one */
+ hfs_u16 files; /* Number of files in this directory */
+ int readers;
+ hfs_wait_queue read_wait;
+ int writers;
+ hfs_wait_queue write_wait;
+};
+
+/*
+ * struct hfs_fork
+ *
+ * This structure holds the information
+ * specific to a single fork of a file.
+ */
+struct hfs_fork {
+ struct hfs_cat_entry *entry; /* The file this fork is part of */
+ struct hfs_extent first; /* The first extent record for
+ this fork */
+ struct hfs_extent *cache; /* The most-recently accessed
+ extent record for this fork */
+ hfs_u32 lsize; /* The logical size in bytes */
+ hfs_u32 psize; /* The phys size (512-byte blocks) */
+ hfs_u8 fork; /* Which fork is this? */
+};
+
+/*
+ * struct hfs_file
+ *
+ * This structure holds information specific
+ * to a file in an HFS filesystem.
+ */
+struct hfs_file {
+ int magic;
+ struct hfs_fork data_fork;
+ struct hfs_fork rsrc_fork;
+ hfs_u16 clumpablks;
+ hfs_u8 flags;
+};
+
+/*
+ * struct hfs_file
+ *
+ * This structure holds information about a
+ * file or directory in an HFS filesystem.
+ *
+ * 'wait' must remain 1st and 'hash' 2nd since we do some pointer arithmetic.
+ */
+struct hfs_cat_entry {
+ hfs_wait_queue wait;
+ struct list_head hash;
+ struct list_head list;
+ struct hfs_mdb *mdb;
+ hfs_sysentry sys_entry;
+ struct hfs_cat_key key;
+ union hfs_finder_info info;
+ hfs_u32 cnid; /* In network byte-order */
+ hfs_u32 create_date; /* In network byte-order */
+ hfs_u32 modify_date; /* In network byte-order */
+ hfs_u32 backup_date; /* In network byte-order */
+ unsigned short count;
+ unsigned long state;
+ hfs_u8 type;
+ union {
+ struct hfs_dir dir;
+ struct hfs_file file;
+ } u;
+};
+
+/* hfs entry state bits */
+#define HFS_DIRTY 1
+#define HFS_KEYDIRTY 2
+#define HFS_LOCK 4
+#define HFS_DELETED 8
+
+/*
+ * struct hfs_bnode_ref
+ *
+ * A pointer to a (struct hfs_bnode) and the type of lock held on it.
+ */
+struct hfs_bnode_ref {
+ struct hfs_bnode *bn;
+ int lock_type;
+};
+
+/*
+ * struct hfs_belem
+ *
+ * An element of the path from the root of a B-tree to a leaf.
+ * Includes the reference to a (struct hfs_bnode), the index of
+ * the appropriate record in that node, and some flags.
+ */
+struct hfs_belem {
+ struct hfs_bnode_ref bnr;
+ int record;
+ int flags;
+};
+
+/*
+ * struct hfs_brec
+ *
+ * The structure returned by hfs_bfind() to describe the requested record.
+ */
+struct hfs_brec {
+ int keep_flags;
+ struct hfs_btree *tree;
+ struct hfs_belem *top;
+ struct hfs_belem *bottom;
+ struct hfs_belem elem[9];
+ struct hfs_bkey *key;
+ void *data; /* The actual data */
+};
+
+/*================ Function prototypes ================*/
+
+/* bdelete.c */
+extern int hfs_bdelete(struct hfs_btree *, const struct hfs_bkey *);
+
+/* bfind.c */
+extern void hfs_brec_relse(struct hfs_brec *, struct hfs_belem *);
+extern int hfs_bsucc(struct hfs_brec *, int);
+extern int hfs_bfind(struct hfs_brec *, struct hfs_btree *,
+ const struct hfs_bkey *, int);
+
+/* binsert.c */
+extern int hfs_binsert(struct hfs_btree *, const struct hfs_bkey *,
+ const void *, hfs_u16);
+
+/* bitmap.c */
+extern hfs_u16 hfs_vbm_count_free(const struct hfs_mdb *, hfs_u16);
+extern hfs_u16 hfs_vbm_search_free(const struct hfs_mdb *, hfs_u16 *);
+extern int hfs_set_vbm_bits(struct hfs_mdb *, hfs_u16, hfs_u16);
+extern int hfs_clear_vbm_bits(struct hfs_mdb *, hfs_u16, hfs_u16);
+
+/* bitops.c */
+extern hfs_u32 hfs_find_zero_bit(const hfs_u32 *, hfs_u32, hfs_u32);
+extern hfs_u32 hfs_count_zero_bits(const hfs_u32 *, hfs_u32, hfs_u32);
+
+/* btree.c */
+extern struct hfs_btree *hfs_btree_init(struct hfs_mdb *, ino_t,
+ hfs_byte_t *, hfs_u32, hfs_u32);
+extern void hfs_btree_free(struct hfs_btree *);
+extern void hfs_btree_commit(struct hfs_btree *, hfs_byte_t *, hfs_lword_t);
+
+/* catalog.c */
+extern void hfs_cat_init(void);
+extern void hfs_cat_put(struct hfs_cat_entry *);
+extern void hfs_cat_mark_dirty(struct hfs_cat_entry *);
+extern struct hfs_cat_entry *hfs_cat_get(struct hfs_mdb *,
+ const struct hfs_cat_key *);
+
+extern void hfs_cat_invalidate(struct hfs_mdb *);
+extern void hfs_cat_commit(struct hfs_mdb *);
+extern void hfs_cat_free(void);
+
+extern int hfs_cat_compare(const struct hfs_cat_key *,
+ const struct hfs_cat_key *);
+extern void hfs_cat_build_key(hfs_u32, const struct hfs_name *,
+ struct hfs_cat_key *);
+extern struct hfs_cat_entry *hfs_cat_parent(struct hfs_cat_entry *);
+
+extern int hfs_cat_open(struct hfs_cat_entry *, struct hfs_brec *);
+extern int hfs_cat_next(struct hfs_cat_entry *, struct hfs_brec *,
+ hfs_u16, hfs_u32 *, hfs_u8 *);
+extern void hfs_cat_close(struct hfs_cat_entry *, struct hfs_brec *);
+
+extern int hfs_cat_create(struct hfs_cat_entry *, struct hfs_cat_key *,
+ hfs_u8, hfs_u32, hfs_u32, struct hfs_cat_entry **);
+extern int hfs_cat_mkdir(struct hfs_cat_entry *, struct hfs_cat_key *,
+ struct hfs_cat_entry **);
+extern int hfs_cat_delete(struct hfs_cat_entry *, struct hfs_cat_entry *, int);
+extern int hfs_cat_move(struct hfs_cat_entry *, struct hfs_cat_entry *,
+ struct hfs_cat_entry *, struct hfs_cat_key *,
+ struct hfs_cat_entry **);
+
+/* extent.c */
+extern int hfs_ext_compare(const struct hfs_ext_key *,
+ const struct hfs_ext_key *);
+extern void hfs_extent_in(struct hfs_fork *, const hfs_byte_t *);
+extern void hfs_extent_out(const struct hfs_fork *, hfs_byte_t *);
+extern int hfs_extent_map(struct hfs_fork *, int, int);
+extern void hfs_extent_adj(struct hfs_fork *);
+extern void hfs_extent_free(struct hfs_fork *);
+
+/* file.c */
+extern int hfs_get_block(struct inode *, long, struct buffer_head *, int);
+
+/* mdb.c */
+extern struct hfs_mdb *hfs_mdb_get(hfs_sysmdb, int, hfs_s32);
+extern void hfs_mdb_commit(struct hfs_mdb *, int);
+extern void hfs_mdb_put(struct hfs_mdb *, int);
+
+/* part_tbl.c */
+extern int hfs_part_find(hfs_sysmdb, int, int, hfs_s32 *, hfs_s32 *);
+
+/* string.c */
+extern unsigned int hfs_strhash(const unsigned char *, unsigned int);
+extern int hfs_strcmp(const unsigned char *, unsigned int,
+ const unsigned char *, unsigned int);
+extern int hfs_streq(const unsigned char *, unsigned int,
+ const unsigned char *, unsigned int);
+extern void hfs_tolower(unsigned char *, int);
+
+static __inline__ struct dentry
+*hfs_lookup_dentry(struct dentry *base, const char *name, const int len)
+{
+ struct qstr this;
+
+ this.name = name;
+ this.len = len;
+ this.hash = hfs_strhash(name, len);
+
+ return d_lookup(base, &this);
+}
+
+/* drop a dentry for one of the special directories.
+ * it's in the form of base/name/dentry. */
+static __inline__ void hfs_drop_special(struct dentry *base,
+ const struct hfs_name *name,
+ struct dentry *dentry)
+{
+ struct dentry *dparent, *de;
+
+ dparent = hfs_lookup_dentry(base, name->Name, name->Len);
+ if (dparent) {
+ de = hfs_lookup_dentry(dparent, dentry->d_name.name,
+ dentry->d_name.len);
+ if (de) {
+ if (!de->d_inode)
+ d_drop(de);
+ dput(de);
+ }
+ dput(dparent);
+ }
+}
+
+extern struct dentry_operations hfs_dentry_operations;
+#endif
diff --git a/fs/hfs/hfs_btree.h b/fs/hfs/hfs_btree.h
new file mode 100644
index 00000000000000..c3371bfa883dd1
--- /dev/null
+++ b/fs/hfs/hfs_btree.h
@@ -0,0 +1,290 @@
+/*
+ * linux/fs/hfs/hfs_btree.h
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU General Public License.
+ *
+ * This file contains the declarations of the private B-tree
+ * structures and functions.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ */
+
+#ifndef _HFS_BTREE_H
+#define _HFS_BTREE_H
+
+#include "hfs.h"
+
+/*================ Variable-like macros ================*/
+
+/* The stickiness of a (struct hfs_bnode) */
+#define HFS_NOT_STICKY 0
+#define HFS_STICKY 1
+
+/* The number of hash buckets in a B-tree's bnode cache */
+#define HFS_CACHELEN 17 /* primes are best? */
+
+/*
+ * Legal values for the 'ndType' field of a (struct NodeDescriptor)
+ *
+ * Reference: _Inside Macintosh: Files_ p. 2-65
+ */
+#define ndIndxNode 0x00 /* An internal (index) node */
+#define ndHdrNode 0x01 /* The tree header node (node 0) */
+#define ndMapNode 0x02 /* Holds part of the bitmap of used nodes */
+#define ndLeafNode 0xFF /* A leaf (ndNHeight==1) node */
+
+/*
+ * Legal values for the bthAtrb field of a (struct BTHdrRec)
+ *
+ * Reference: TN 1150
+ */
+#define bthBadClose 0x00000001 /* b-tree not closed properly. not
+ used by hfsplus. */
+#define bthBigKeys 0x00000002 /* key length is u16 instead of u8.
+ used by hfsplus. */
+#define bthVarIndxKeys 0x00000004 /* variable key length instead of
+ max key length. use din catalog
+ b-tree but not in extents
+ b-tree (hfsplus). */
+
+/*================ Function-like macros ================*/
+
+/* Access the cache slot which should contain the desired node */
+#define bhash(tree, node) ((tree)->cache[(node) % HFS_CACHELEN])
+
+/* round up to multiple of sizeof(hfs_u16) */
+#define ROUND(X) ((X + sizeof(hfs_u16) - 1) & ~(sizeof(hfs_u16)-1))
+
+/* Refer to the (base-1) array of offsets in a bnode */
+#define RECTBL(X,N) \
+ (((hfs_u16 *)(hfs_buffer_data((X)->buf)+HFS_SECTOR_SIZE))-(N))
+
+/*================ Private data types ================*/
+
+/*
+ * struct BTHdrRec
+ *
+ * The B-tree header record
+ *
+ * This data structure is stored in the first node (512-byte block) of
+ * each B-tree file. It contains important information about the
+ * B-tree. Most fields vary over the life of the tree and are
+ * indicated by a 'V' in the comments. The other fields are fixed for
+ * the life of the tree and are indicated by a 'F'.
+ *
+ * Reference: _Inside Macintosh: Files_ pp. 2-68 through 2-69 */
+struct BTHdrRec {
+ hfs_word_t bthDepth; /* (V) The number of levels in this B-tree */
+ hfs_lword_t bthRoot; /* (V) The node number of the root node */
+ hfs_lword_t bthNRecs; /* (V) The number of leaf records */
+ hfs_lword_t bthFNode; /* (V) The number of the first leaf node */
+ hfs_lword_t bthLNode; /* (V) The number of the last leaf node */
+ hfs_word_t bthNodeSize; /* (F) The number of bytes in a node (=512) */
+ hfs_word_t bthKeyLen; /* (F) The length of a key in an index node */
+ hfs_lword_t bthNNodes; /* (V) The total number of nodes */
+ hfs_lword_t bthFree; /* (V) The number of unused nodes */
+ hfs_word_t bthResv1; /* reserved */
+ hfs_lword_t bthClpSiz; /* (F) clump size. not usually used. */
+ hfs_byte_t bthType; /* (F) BTree type */
+ hfs_byte_t bthResv2; /* reserved */
+ hfs_lword_t bthAtrb; /* (F) attributes */
+ hfs_lword_t bthResv3[16]; /* Reserved */
+} __attribute__((packed));
+
+/*
+ * struct NodeDescriptor
+ *
+ * The B-tree node descriptor.
+ *
+ * This structure begins each node in the B-tree file. It contains
+ * important information about the node's contents. 'V' and 'F' in
+ * the comments indicate fields that are variable or fixed over the
+ * life of a node, where the 'life' of a node is defined as the period
+ * between leaving and reentering the free pool.
+ *
+ * Reference: _Inside Macintosh: Files_ p. 2-64
+ */
+struct NodeDescriptor {
+ hfs_lword_t ndFLink; /* (V) Number of the next node at this level */
+ hfs_lword_t ndBLink; /* (V) Number of the prev node at this level */
+ hfs_byte_t ndType; /* (F) The type of node */
+ hfs_byte_t ndNHeight; /* (F) The level of this node (leaves=1) */
+ hfs_word_t ndNRecs; /* (V) The number of records in this node */
+ hfs_word_t ndResv2; /* Reserved */
+} __attribute__((packed));
+
+/*
+ * typedef hfs_cmpfn
+ *
+ * The type 'hfs_cmpfn' is a comparison function taking 2 keys and
+ * returning a positive, negative or zero integer according to the
+ * ordering of the two keys (just like strcmp() does for strings).
+ */
+typedef int (*hfs_cmpfn)(const void *, const void *);
+
+/*
+ * struct hfs_bnode
+ *
+ * An in-core B-tree node
+ *
+ * This structure holds information from the NodeDescriptor in native
+ * byte-order, a pointer to the buffer which contains the actual
+ * node and fields necessary for locking access to the node during
+ * updates. The use of the locking fields is explained with the
+ * locking functions.
+ */
+struct hfs_bnode {
+ int magic; /* Magic number to guard against
+ wild pointers */
+ hfs_buffer buf; /* The buffer containing the
+ actual node */
+ struct hfs_btree *tree; /* The tree to which this node
+ belongs */
+ struct hfs_bnode *prev; /* Next node in this hash bucket */
+ struct hfs_bnode *next; /* Previous node in this hash
+ bucket */
+ int sticky; /* Boolean: non-zero means keep
+ this node in-core (set for
+ root and head) */
+ hfs_u32 node; /* Node number */
+ hfs_u16 nodeSize; /* node size */
+ hfs_u16 keyLen; /* key length */
+ /* locking related fields: */
+ hfs_wait_queue wqueue; /* Wait queue for write access */
+ hfs_wait_queue rqueue; /* Wait queue for read or reserve
+ access */
+ int count; /* Number of processes accessing
+ this node */
+ int resrv; /* Boolean, true means a process
+ had placed a 'reservation' on
+ this node */
+ int lock; /* Boolean, true means some
+ process has exclusive access,
+ so KEEP OUT */
+ /* fields from the NodeDescriptor in native byte-order: */
+ hfs_u32 ndFLink;
+ hfs_u32 ndBLink;
+ hfs_u16 ndNRecs;
+ hfs_u8 ndType;
+ hfs_u8 ndNHeight;
+};
+
+/*
+ * struct hfs_btree
+ *
+ * An in-core B-tree.
+ *
+ * This structure holds information from the BTHdrRec, MDB
+ * (superblock) and other information needed to work with the B-tree.
+ */
+struct hfs_btree {
+ int magic; /* Magic number to
+ guard against wild
+ pointers */
+ hfs_cmpfn compare; /* Comparison function
+ for this tree */
+ struct hfs_bnode head; /* in-core copy of node 0 */
+ struct hfs_bnode *root; /* Pointer to the in-core
+ copy of the root node */
+ hfs_sysmdb sys_mdb; /* The "device" holding
+ the filesystem */
+ int reserved; /* bnodes claimed but
+ not yet used */
+ struct hfs_bnode /* The bnode cache */
+ *cache[HFS_CACHELEN];
+ struct hfs_cat_entry entry; /* Fake catalog entry */
+ int lock;
+ hfs_wait_queue wait;
+ int dirt;
+ int keySize;
+ /* Fields from the BTHdrRec in native byte-order: */
+ hfs_u32 bthRoot;
+ hfs_u32 bthNRecs;
+ hfs_u32 bthFNode;
+ hfs_u32 bthLNode;
+ hfs_u32 bthNNodes;
+ hfs_u32 bthFree;
+ hfs_u16 bthKeyLen;
+ hfs_u16 bthDepth;
+};
+
+/*================ Global functions ================*/
+
+/* Convert a (struct hfs_bnode *) and an index to the value of the
+ n-th offset in the bnode (N >= 1) to the offset */
+extern inline hfs_u16 bnode_offset(const struct hfs_bnode *bnode, int n)
+{ return hfs_get_hs(RECTBL(bnode,n)); }
+
+/* Convert a (struct hfs_bnode *) and an index to the size of the
+ n-th record in the bnode (N >= 1) */
+extern inline hfs_u16 bnode_rsize(const struct hfs_bnode *bnode, int n)
+{ return bnode_offset(bnode, n+1) - bnode_offset(bnode, n); }
+
+/* Convert a (struct hfs_bnode *) to the offset of the empty part */
+extern inline hfs_u16 bnode_end(const struct hfs_bnode *bnode)
+{ return bnode_offset(bnode, bnode->ndNRecs + 1); }
+
+/* Convert a (struct hfs_bnode *) to the number of free bytes it contains */
+extern inline hfs_u16 bnode_freespace(const struct hfs_bnode *bnode)
+{ return HFS_SECTOR_SIZE - bnode_end(bnode)
+ - (bnode->ndNRecs + 1)*sizeof(hfs_u16); }
+
+/* Convert a (struct hfs_bnode *) X and an index N to
+ the address of the record N in the bnode (N >= 1) */
+extern inline void *bnode_datastart(const struct hfs_bnode *bnode)
+{ return (void *)(hfs_buffer_data(bnode->buf)+sizeof(struct NodeDescriptor)); }
+
+/* Convert a (struct hfs_bnode *) to the address of the empty part */
+extern inline void *bnode_dataend(const struct hfs_bnode *bnode)
+{ return (void *)(hfs_buffer_data(bnode->buf) + bnode_end(bnode)); }
+
+/* Convert various pointers to address of record's key */
+extern inline void *bnode_key(const struct hfs_bnode *bnode, int n)
+{ return (void *)(hfs_buffer_data(bnode->buf) + bnode_offset(bnode, n)); }
+extern inline void *belem_key(const struct hfs_belem *elem)
+{ return bnode_key(elem->bnr.bn, elem->record); }
+extern inline void *brec_key(const struct hfs_brec *brec)
+{ return belem_key(brec->bottom); }
+
+/* Convert various pointers to the address of a record */
+extern inline void *bkey_record(const struct hfs_bkey *key)
+{ return (void *)key + ROUND(key->KeyLen + 1); }
+extern inline void *bnode_record(const struct hfs_bnode *bnode, int n)
+{ return bkey_record(bnode_key(bnode, n)); }
+extern inline void *belem_record(const struct hfs_belem *elem)
+{ return bkey_record(belem_key(elem)); }
+extern inline void *brec_record(const struct hfs_brec *brec)
+{ return bkey_record(brec_key(brec)); }
+
+/*================ Function Prototypes ================*/
+
+/* balloc.c */
+extern int hfs_bnode_bitop(struct hfs_btree *, hfs_u32, int);
+extern struct hfs_bnode_ref hfs_bnode_alloc(struct hfs_btree *);
+extern int hfs_bnode_free(struct hfs_bnode_ref *);
+extern void hfs_btree_extend(struct hfs_btree *);
+
+/* bins_del.c */
+extern void hfs_bnode_update_key(struct hfs_brec *, struct hfs_belem *,
+ struct hfs_bnode *, int);
+extern void hfs_bnode_shift_right(struct hfs_bnode *, struct hfs_bnode *, int);
+extern void hfs_bnode_shift_left(struct hfs_bnode *, struct hfs_bnode *, int);
+extern int hfs_bnode_in_brec(hfs_u32 node, const struct hfs_brec *brec);
+
+/* bnode.c */
+extern void hfs_bnode_read(struct hfs_bnode *, struct hfs_btree *,
+ hfs_u32, int);
+extern void hfs_bnode_relse(struct hfs_bnode_ref *);
+extern struct hfs_bnode_ref hfs_bnode_find(struct hfs_btree *, hfs_u32, int);
+extern void hfs_bnode_lock(struct hfs_bnode_ref *, int);
+extern void hfs_bnode_delete(struct hfs_bnode *);
+extern void hfs_bnode_commit(struct hfs_bnode *);
+
+/* brec.c */
+extern void hfs_brec_lock(struct hfs_brec *, struct hfs_belem *);
+extern struct hfs_belem *hfs_brec_init(struct hfs_brec *, struct hfs_btree *,
+ int);
+extern struct hfs_belem *hfs_brec_next(struct hfs_brec *);
+
+#endif
diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c
new file mode 100644
index 00000000000000..034e9fca67fda0
--- /dev/null
+++ b/fs/hfs/inode.c
@@ -0,0 +1,503 @@
+/*
+ * linux/fs/hfs/inode.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU General Public License.
+ *
+ * This file contains inode-related functions which do not depend on
+ * which scheme is being used to represent forks.
+ *
+ * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs.h"
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+#include <linux/smp_lock.h>
+
+/*================ Variable-like macros ================*/
+
+#define HFS_VALID_MODE_BITS (S_IFREG | S_IFDIR | S_IRWXUGO)
+
+/*================ File-local functions ================*/
+
+/*
+ * init_file_inode()
+ *
+ * Given an HFS catalog entry initialize an inode for a file.
+ */
+static void init_file_inode(struct inode *inode, hfs_u8 fork)
+{
+ struct hfs_fork *fk;
+ struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+
+ if (fork == HFS_FK_DATA) {
+ inode->i_mode = S_IRWXUGO | S_IFREG;
+ } else {
+ inode->i_mode = S_IRUGO | S_IWUGO | S_IFREG;
+ }
+
+ if (fork == HFS_FK_DATA) {
+#if 0 /* XXX: disable crlf translations for now */
+ hfs_u32 type = hfs_get_nl(entry->info.file.finfo.fdType);
+
+ HFS_I(inode)->convert =
+ ((HFS_SB(inode->i_sb)->s_conv == 't') ||
+ ((HFS_SB(inode->i_sb)->s_conv == 'a') &&
+ ((type == htonl(0x54455854)) || /* "TEXT" */
+ (type == htonl(0x7474726f))))); /* "ttro" */
+#else
+ HFS_I(inode)->convert = 0;
+#endif
+ fk = &entry->u.file.data_fork;
+ } else {
+ fk = &entry->u.file.rsrc_fork;
+ HFS_I(inode)->convert = 0;
+ }
+ HFS_I(inode)->fork = fk;
+ inode->i_size = fk->lsize;
+ inode->i_blocks = fk->psize;
+ inode->i_nlink = 1;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_put_inode()
+ *
+ * This is the put_inode() entry in the super_operations for HFS
+ * filesystems. The purpose is to perform any filesystem-dependent
+ * cleanup necessary when the use-count of an inode falls to zero.
+ */
+void hfs_put_inode(struct inode * inode)
+{
+ struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+
+ lock_kernel();
+ hfs_cat_put(entry);
+ if (atomic_read(&inode->i_count) == 1) {
+ struct hfs_hdr_layout *tmp = HFS_I(inode)->layout;
+
+ if (tmp) {
+ HFS_I(inode)->layout = NULL;
+ HFS_DELETE(tmp);
+ }
+ }
+ unlock_kernel();
+}
+
+/*
+ * hfs_notify_change()
+ *
+ * Based very closely on fs/msdos/inode.c by Werner Almesberger
+ *
+ * This is the notify_change() field in the super_operations structure
+ * for HFS file systems. The purpose is to take that changes made to
+ * an inode and apply then in a filesystem-dependent manner. In this
+ * case the process has a few of tasks to do:
+ * 1) prevent changes to the i_uid and i_gid fields.
+ * 2) map file permissions to the closest allowable permissions
+ * 3) Since multiple Linux files can share the same on-disk inode under
+ * HFS (for instance the data and resource forks of a file) a change
+ * to permissions must be applied to all other in-core inodes which
+ * correspond to the same HFS file.
+ */
+enum {HFS_NORM, HFS_HDR, HFS_CAP};
+
+static int __hfs_notify_change(struct dentry *dentry, struct iattr * attr, int kind)
+{
+ struct inode *inode = dentry->d_inode;
+ struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+ struct dentry **de = entry->sys_entry;
+ struct hfs_sb_info *hsb = HFS_SB(inode->i_sb);
+ int error, i;
+
+ error = inode_change_ok(inode, attr); /* basic permission checks */
+ if (error) {
+ /* Let netatalk's afpd think chmod() always succeeds */
+ if (hsb->s_afpd &&
+ (attr->ia_valid == (ATTR_MODE | ATTR_CTIME))) {
+ return 0;
+ } else {
+ return error;
+ }
+ }
+
+ /* no uig/gid changes and limit which mode bits can be set */
+ if (((attr->ia_valid & ATTR_UID) &&
+ (attr->ia_uid != hsb->s_uid)) ||
+ ((attr->ia_valid & ATTR_GID) &&
+ (attr->ia_gid != hsb->s_gid)) ||
+ ((attr->ia_valid & ATTR_MODE) &&
+ (((entry->type == HFS_CDR_DIR) &&
+ (attr->ia_mode != inode->i_mode))||
+ (attr->ia_mode & ~HFS_VALID_MODE_BITS)))) {
+ return hsb->s_quiet ? 0 : error;
+ }
+
+ if (entry->type == HFS_CDR_DIR) {
+ attr->ia_valid &= ~ATTR_MODE;
+ } else if (attr->ia_valid & ATTR_MODE) {
+ /* Only the 'w' bits can ever change and only all together. */
+ if (attr->ia_mode & S_IWUSR) {
+ attr->ia_mode = inode->i_mode | S_IWUGO;
+ } else {
+ attr->ia_mode = inode->i_mode & ~S_IWUGO;
+ }
+ attr->ia_mode &= ~hsb->s_umask;
+ }
+ /*
+ * Normal files handle size change in normal way.
+ * Oddballs are served here.
+ */
+ if (attr->ia_valid & ATTR_SIZE) {
+ if (kind == HFS_CAP) {
+ inode->i_size = attr->ia_size;
+ if (inode->i_size > HFS_FORK_MAX)
+ inode->i_size = HFS_FORK_MAX;
+ mark_inode_dirty(inode);
+ attr->ia_valid &= ~ATTR_SIZE;
+ } else if (kind == HFS_HDR) {
+ hdr_truncate(inode, attr->ia_size);
+ attr->ia_valid &= ~ATTR_SIZE;
+ }
+ }
+ error = inode_setattr(inode, attr);
+ if (error)
+ return error;
+
+ /* We wouldn't want to mess with the sizes of the other fork */
+ attr->ia_valid &= ~ATTR_SIZE;
+
+ /* We must change all in-core inodes corresponding to this file. */
+ for (i = 0; i < 4; ++i) {
+ if (de[i] && (de[i] != dentry)) {
+ inode_setattr(de[i]->d_inode, attr);
+ }
+ }
+
+ /* Change the catalog entry if needed */
+ if (attr->ia_valid & ATTR_MTIME) {
+ entry->modify_date = hfs_u_to_mtime(inode->i_mtime);
+ hfs_cat_mark_dirty(entry);
+ }
+ if (attr->ia_valid & ATTR_MODE) {
+ hfs_u8 new_flags;
+
+ if (inode->i_mode & S_IWUSR) {
+ new_flags = entry->u.file.flags & ~HFS_FIL_LOCK;
+ } else {
+ new_flags = entry->u.file.flags | HFS_FIL_LOCK;
+ }
+
+ if (new_flags != entry->u.file.flags) {
+ entry->u.file.flags = new_flags;
+ hfs_cat_mark_dirty(entry);
+ }
+ }
+ /* size changes handled in hfs_extent_adj() */
+
+ return 0;
+}
+
+int hfs_notify_change(struct dentry *dentry, struct iattr * attr)
+{
+ return __hfs_notify_change(dentry, attr, HFS_NORM);
+}
+
+int hfs_notify_change_cap(struct dentry *dentry, struct iattr * attr)
+{
+ return __hfs_notify_change(dentry, attr, HFS_CAP);
+}
+
+int hfs_notify_change_hdr(struct dentry *dentry, struct iattr * attr)
+{
+ return __hfs_notify_change(dentry, attr, HFS_HDR);
+}
+
+static int hfs_writepage(struct page *page)
+{
+ return block_write_full_page(page,hfs_get_block);
+}
+static int hfs_readpage(struct file *file, struct page *page)
+{
+ return block_read_full_page(page,hfs_get_block);
+}
+static int hfs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
+{
+ return cont_prepare_write(page,from,to,hfs_get_block,
+ &page->mapping->host->u.hfs_i.mmu_private);
+}
+static int hfs_bmap(struct address_space *mapping, long block)
+{
+ return generic_block_bmap(mapping,block,hfs_get_block);
+}
+struct address_space_operations hfs_aops = {
+ readpage: hfs_readpage,
+ writepage: hfs_writepage,
+ sync_page: block_sync_page,
+ prepare_write: hfs_prepare_write,
+ commit_write: generic_commit_write,
+ bmap: hfs_bmap
+};
+
+/*
+ * __hfs_iget()
+ *
+ * Given the MDB for a HFS filesystem, a 'key' and an 'entry' in
+ * the catalog B-tree and the 'type' of the desired file return the
+ * inode for that file/directory or NULL. Note that 'type' indicates
+ * whether we want the actual file or directory, or the corresponding
+ * metadata (AppleDouble header file or CAP metadata file).
+ *
+ * In an ideal world we could call iget() and would not need this
+ * function. However, since there is no way to even know the inode
+ * number until we've found the file/directory in the catalog B-tree
+ * that simply won't happen.
+ *
+ * The main idea here is to look in the catalog B-tree to get the
+ * vital info about the file or directory (including the file id which
+ * becomes the inode number) and then to call iget() and return the
+ * inode if it is complete. If it is not then we use the catalog
+ * entry to fill in the missing info, by calling the appropriate
+ * 'fillin' function. Note that these fillin functions are
+ * essentially hfs_*_read_inode() functions, but since there is no way
+ * to pass the catalog entry through iget() to such a read_inode()
+ * function, we have to call them after iget() returns an incomplete
+ * inode to us. This is pretty much the same problem faced in the NFS
+ * code, and pretty much the same solution. The SMB filesystem deals
+ * with this in a different way: by using the address of the
+ * kmalloc()'d space which holds the data as the inode number.
+ *
+ * XXX: Both this function and NFS's corresponding nfs_fhget() would
+ * benefit from a way to pass an additional (void *) through iget() to
+ * the VFS read_inode() function.
+ *
+ * this will hfs_cat_put() the entry if it fails.
+ */
+struct inode *hfs_iget(struct hfs_cat_entry *entry, ino_t type,
+ struct dentry *dentry)
+{
+ struct dentry **sys_entry;
+ struct super_block *sb;
+ struct inode *inode;
+
+ if (!entry) {
+ return NULL;
+ }
+
+ /* If there are several processes all calling __iget() for
+ the same inode then they will all get the same one back.
+ The first one to return from __iget() will notice that the
+ i_mode field of the inode is blank and KNOW that it is
+ the first to return. Therefore, it will set the appropriate
+ 'sys_entry' field in the entry and initialize the inode.
+ All the initialization must be done without sleeping,
+ or else other processes could end up using a partially
+ initialized inode. */
+
+ sb = entry->mdb->sys_mdb;
+ sys_entry = &entry->sys_entry[HFS_ITYPE_TO_INT(type)];
+
+ if (!(inode = iget(sb, ntohl(entry->cnid) | type))) {
+ hfs_cat_put(entry);
+ return NULL;
+ }
+
+ if (inode->i_dev != sb->s_dev) {
+ iput(inode); /* automatically does an hfs_cat_put */
+ inode = NULL;
+ } else if (!inode->i_mode || (*sys_entry == NULL)) {
+ /* Initialize the inode */
+ struct hfs_sb_info *hsb = HFS_SB(sb);
+
+ inode->i_rdev = 0;
+ inode->i_ctime = inode->i_atime = inode->i_mtime =
+ hfs_m_to_utime(entry->modify_date);
+ inode->i_blksize = HFS_SECTOR_SIZE;
+ inode->i_uid = hsb->s_uid;
+ inode->i_gid = hsb->s_gid;
+
+ memset(HFS_I(inode), 0, sizeof(struct hfs_inode_info));
+ HFS_I(inode)->magic = HFS_INO_MAGIC;
+ HFS_I(inode)->entry = entry;
+ HFS_I(inode)->tz_secondswest = hfs_to_utc(0);
+
+ hsb->s_ifill(inode, type, hsb->s_version);
+ if (!hsb->s_afpd && (entry->type == HFS_CDR_FIL) &&
+ (entry->u.file.flags & HFS_FIL_LOCK)) {
+ inode->i_mode &= ~S_IWUGO;
+ }
+ inode->i_mode &= ~hsb->s_umask;
+
+ if (!inode->i_mode) {
+ iput(inode); /* does an hfs_cat_put */
+ inode = NULL;
+ } else
+ *sys_entry = dentry; /* cache dentry */
+
+ }
+
+ return inode;
+}
+
+/*================ Scheme-specific functions ================*/
+
+/*
+ * hfs_cap_ifill()
+ *
+ * This function serves the same purpose as a read_inode() function does
+ * in other filesystems. It is called by __hfs_iget() to fill in
+ * the missing fields of an uninitialized inode under the CAP scheme.
+ */
+void hfs_cap_ifill(struct inode * inode, ino_t type, const int version)
+{
+ struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+
+ HFS_I(inode)->d_drop_op = hfs_cap_drop_dentry;
+ if (type == HFS_CAP_FNDR) {
+ inode->i_size = sizeof(struct hfs_cap_info);
+ inode->i_blocks = 0;
+ inode->i_nlink = 1;
+ inode->i_mode = S_IRUGO | S_IWUGO | S_IFREG;
+ inode->i_op = &hfs_cap_info_inode_operations;
+ inode->i_fop = &hfs_cap_info_operations;
+ } else if (entry->type == HFS_CDR_FIL) {
+ init_file_inode(inode, (type == HFS_CAP_DATA) ?
+ HFS_FK_DATA : HFS_FK_RSRC);
+ inode->i_op = &hfs_file_inode_operations;
+ inode->i_fop = &hfs_file_operations;
+ inode->i_mapping->a_ops = &hfs_aops;
+ inode->u.hfs_i.mmu_private = inode->i_size;
+ } else { /* Directory */
+ struct hfs_dir *hdir = &entry->u.dir;
+
+ inode->i_blocks = 0;
+ inode->i_size = hdir->files + hdir->dirs + 5;
+ HFS_I(inode)->dir_size = 1;
+ if (type == HFS_CAP_NDIR) {
+ inode->i_mode = S_IRWXUGO | S_IFDIR;
+ inode->i_nlink = hdir->dirs + 4;
+ inode->i_op = &hfs_cap_ndir_inode_operations;
+ inode->i_fop = &hfs_cap_dir_operations;
+ HFS_I(inode)->file_type = HFS_CAP_NORM;
+ } else if (type == HFS_CAP_FDIR) {
+ inode->i_mode = S_IRUGO | S_IXUGO | S_IFDIR;
+ inode->i_nlink = 2;
+ inode->i_op = &hfs_cap_fdir_inode_operations;
+ inode->i_fop = &hfs_cap_dir_operations;
+ HFS_I(inode)->file_type = HFS_CAP_FNDR;
+ } else if (type == HFS_CAP_RDIR) {
+ inode->i_mode = S_IRUGO | S_IXUGO | S_IFDIR;
+ inode->i_nlink = 2;
+ inode->i_op = &hfs_cap_rdir_inode_operations;
+ inode->i_fop = &hfs_cap_dir_operations;
+ HFS_I(inode)->file_type = HFS_CAP_RSRC;
+ }
+ }
+}
+
+/*
+ * hfs_dbl_ifill()
+ *
+ * This function serves the same purpose as a read_inode() function does
+ * in other filesystems. It is called by __hfs_iget() to fill in
+ * the missing fields of an uninitialized inode under the AppleDouble
+ * scheme.
+ */
+void hfs_dbl_ifill(struct inode * inode, ino_t type, const int version)
+{
+ struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+
+ HFS_I(inode)->d_drop_op = hfs_dbl_drop_dentry;
+ if (type == HFS_DBL_HDR) {
+ if (entry->type == HFS_CDR_FIL) {
+ init_file_inode(inode, HFS_FK_RSRC);
+ inode->i_size += HFS_DBL_HDR_LEN;
+ HFS_I(inode)->default_layout = &hfs_dbl_fil_hdr_layout;
+ } else {
+ inode->i_size = HFS_DBL_HDR_LEN;
+ inode->i_mode = S_IRUGO | S_IWUGO | S_IFREG;
+ inode->i_nlink = 1;
+ HFS_I(inode)->default_layout = &hfs_dbl_dir_hdr_layout;
+ }
+ inode->i_op = &hfs_hdr_inode_operations;
+ inode->i_fop = &hfs_hdr_operations;
+ } else if (entry->type == HFS_CDR_FIL) {
+ init_file_inode(inode, HFS_FK_DATA);
+ inode->i_op = &hfs_file_inode_operations;
+ inode->i_fop = &hfs_file_operations;
+ inode->i_mapping->a_ops = &hfs_aops;
+ inode->u.hfs_i.mmu_private = inode->i_size;
+ } else { /* Directory */
+ struct hfs_dir *hdir = &entry->u.dir;
+
+ inode->i_blocks = 0;
+ inode->i_nlink = hdir->dirs + 2;
+ inode->i_size = 3 + 2 * (hdir->dirs + hdir->files);
+ inode->i_mode = S_IRWXUGO | S_IFDIR;
+ inode->i_op = &hfs_dbl_dir_inode_operations;
+ inode->i_fop = &hfs_dbl_dir_operations;
+ HFS_I(inode)->file_type = HFS_DBL_NORM;
+ HFS_I(inode)->dir_size = 2;
+ }
+}
+
+/*
+ * hfs_nat_ifill()
+ *
+ * This function serves the same purpose as a read_inode() function does
+ * in other filesystems. It is called by __hfs_iget() to fill in
+ * the missing fields of an uninitialized inode under the Netatalk
+ * scheme.
+ */
+void hfs_nat_ifill(struct inode * inode, ino_t type, const int version)
+{
+ struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+
+ HFS_I(inode)->d_drop_op = hfs_nat_drop_dentry;
+ if (type == HFS_NAT_HDR) {
+ if (entry->type == HFS_CDR_FIL) {
+ init_file_inode(inode, HFS_FK_RSRC);
+ inode->i_size += HFS_NAT_HDR_LEN;
+ } else {
+ inode->i_size = HFS_NAT_HDR_LEN;
+ inode->i_mode = S_IRUGO | S_IWUGO | S_IFREG;
+ inode->i_nlink = 1;
+ }
+ inode->i_op = &hfs_hdr_inode_operations;
+ inode->i_fop = &hfs_hdr_operations;
+ HFS_I(inode)->default_layout = (version == 2) ?
+ &hfs_nat2_hdr_layout : &hfs_nat_hdr_layout;
+ } else if (entry->type == HFS_CDR_FIL) {
+ init_file_inode(inode, HFS_FK_DATA);
+ inode->i_op = &hfs_file_inode_operations;
+ inode->i_fop = &hfs_file_operations;
+ inode->i_mapping->a_ops = &hfs_aops;
+ inode->u.hfs_i.mmu_private = inode->i_size;
+ } else { /* Directory */
+ struct hfs_dir *hdir = &entry->u.dir;
+
+ inode->i_blocks = 0;
+ inode->i_size = hdir->files + hdir->dirs + 4;
+ inode->i_mode = S_IRWXUGO | S_IFDIR;
+ HFS_I(inode)->dir_size = 1;
+ if (type == HFS_NAT_NDIR) {
+ inode->i_nlink = hdir->dirs + 3;
+ inode->i_op = &hfs_nat_ndir_inode_operations;
+ HFS_I(inode)->file_type = HFS_NAT_NORM;
+ } else if (type == HFS_NAT_HDIR) {
+ inode->i_nlink = 2;
+ inode->i_op = &hfs_nat_hdir_inode_operations;
+ HFS_I(inode)->file_type = HFS_NAT_HDR;
+ }
+ inode->i_fop = &hfs_nat_dir_operations;
+ }
+}
diff --git a/fs/hfs/mdb.c b/fs/hfs/mdb.c
new file mode 100644
index 00000000000000..679a03a1355703
--- /dev/null
+++ b/fs/hfs/mdb.c
@@ -0,0 +1,320 @@
+/*
+ * linux/fs/hfs/mdb.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU General Public License.
+ *
+ * This file contains functions for reading/writing the MDB.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ *
+ * The code in this file initializes some structures which contain
+ * pointers by calling memset(&foo, 0, sizeof(foo)).
+ * This produces the desired behavior only due to the non-ANSI
+ * assumption that the machine representation of NULL is all zeros.
+ */
+
+#include "hfs.h"
+
+/*================ File-local data types ================*/
+
+/*
+ * The HFS Master Directory Block (MDB).
+ *
+ * Also known as the Volume Information Block (VIB), this structure is
+ * the HFS equivalent of a superblock.
+ *
+ * Reference: _Inside Macintosh: Files_ pages 2-59 through 2-62
+ *
+ * modified for HFS Extended
+ */
+struct raw_mdb {
+ hfs_word_t drSigWord; /* Signature word indicating fs type */
+ hfs_lword_t drCrDate; /* fs creation date/time */
+ hfs_lword_t drLsMod; /* fs modification date/time */
+ hfs_word_t drAtrb; /* fs attributes */
+ hfs_word_t drNmFls; /* number of files in root directory */
+ hfs_word_t drVBMSt; /* location (in 512-byte blocks)
+ of the volume bitmap */
+ hfs_word_t drAllocPtr; /* location (in allocation blocks)
+ to begin next allocation search */
+ hfs_word_t drNmAlBlks; /* number of allocation blocks */
+ hfs_lword_t drAlBlkSiz; /* bytes in an allocation block */
+ hfs_lword_t drClpSiz; /* clumpsize, the number of bytes to
+ allocate when extending a file */
+ hfs_word_t drAlBlSt; /* location (in 512-byte blocks)
+ of the first allocation block */
+ hfs_lword_t drNxtCNID; /* CNID to assign to the next
+ file or directory created */
+ hfs_word_t drFreeBks; /* number of free allocation blocks */
+ hfs_byte_t drVN[28]; /* the volume label */
+ hfs_lword_t drVolBkUp; /* fs backup date/time */
+ hfs_word_t drVSeqNum; /* backup sequence number */
+ hfs_lword_t drWrCnt; /* fs write count */
+ hfs_lword_t drXTClpSiz; /* clumpsize for the extents B-tree */
+ hfs_lword_t drCTClpSiz; /* clumpsize for the catalog B-tree */
+ hfs_word_t drNmRtDirs; /* number of directories in
+ the root directory */
+ hfs_lword_t drFilCnt; /* number of files in the fs */
+ hfs_lword_t drDirCnt; /* number of directories in the fs */
+ hfs_byte_t drFndrInfo[32]; /* data used by the Finder */
+ hfs_word_t drEmbedSigWord; /* embedded volume signature */
+ hfs_lword_t drEmbedExtent; /* starting block number (xdrStABN)
+ and number of allocation blocks
+ (xdrNumABlks) occupied by embedded
+ volume */
+ hfs_lword_t drXTFlSize; /* bytes in the extents B-tree */
+ hfs_byte_t drXTExtRec[12]; /* extents B-tree's first 3 extents */
+ hfs_lword_t drCTFlSize; /* bytes in the catalog B-tree */
+ hfs_byte_t drCTExtRec[12]; /* catalog B-tree's first 3 extents */
+} __attribute__((packed));
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_mdb_get()
+ *
+ * Build the in-core MDB for a filesystem, including
+ * the B-trees and the volume bitmap.
+ */
+struct hfs_mdb *hfs_mdb_get(hfs_sysmdb sys_mdb, int readonly,
+ hfs_s32 part_start)
+{
+ struct hfs_mdb *mdb;
+ hfs_buffer buf;
+ struct raw_mdb *raw;
+ unsigned int bs, block;
+ int lcv, limit;
+ hfs_buffer *bmbuf;
+
+ if (!HFS_NEW(mdb)) {
+ hfs_warn("hfs_fs: out of memory\n");
+ return NULL;
+ }
+
+ memset(mdb, 0, sizeof(*mdb));
+ mdb->magic = HFS_MDB_MAGIC;
+ mdb->sys_mdb = sys_mdb;
+ INIT_LIST_HEAD(&mdb->entry_dirty);
+ hfs_init_waitqueue(&mdb->rename_wait);
+ hfs_init_waitqueue(&mdb->bitmap_wait);
+
+ /* See if this is an HFS filesystem */
+ buf = hfs_buffer_get(sys_mdb, part_start + HFS_MDB_BLK, 1);
+ if (!hfs_buffer_ok(buf)) {
+ hfs_warn("hfs_fs: Unable to read superblock\n");
+ HFS_DELETE(mdb);
+ goto bail2;
+ }
+
+ raw = (struct raw_mdb *)hfs_buffer_data(buf);
+ if (hfs_get_ns(raw->drSigWord) != htons(HFS_SUPER_MAGIC)) {
+ hfs_buffer_put(buf);
+ HFS_DELETE(mdb);
+ goto bail2;
+ }
+ mdb->buf = buf;
+
+ bs = hfs_get_hl(raw->drAlBlkSiz);
+ if (!bs || (bs & (HFS_SECTOR_SIZE-1))) {
+ hfs_warn("hfs_fs: bad allocation block size %d != 512\n", bs);
+ hfs_buffer_put(buf);
+ HFS_DELETE(mdb);
+ goto bail2;
+ }
+ mdb->alloc_blksz = bs >> HFS_SECTOR_SIZE_BITS;
+
+ /* These parameters are read from the MDB, and never written */
+ mdb->create_date = hfs_get_hl(raw->drCrDate);
+ mdb->fs_ablocks = hfs_get_hs(raw->drNmAlBlks);
+ mdb->fs_start = hfs_get_hs(raw->drAlBlSt) + part_start;
+ mdb->backup_date = hfs_get_hl(raw->drVolBkUp);
+ mdb->clumpablks = (hfs_get_hl(raw->drClpSiz) / mdb->alloc_blksz)
+ >> HFS_SECTOR_SIZE_BITS;
+ memcpy(mdb->vname, raw->drVN, sizeof(raw->drVN));
+
+ /* These parameters are read from and written to the MDB */
+ mdb->modify_date = hfs_get_nl(raw->drLsMod);
+ mdb->attrib = hfs_get_ns(raw->drAtrb);
+ mdb->free_ablocks = hfs_get_hs(raw->drFreeBks);
+ mdb->next_id = hfs_get_hl(raw->drNxtCNID);
+ mdb->write_count = hfs_get_hl(raw->drWrCnt);
+ mdb->root_files = hfs_get_hs(raw->drNmFls);
+ mdb->root_dirs = hfs_get_hs(raw->drNmRtDirs);
+ mdb->file_count = hfs_get_hl(raw->drFilCnt);
+ mdb->dir_count = hfs_get_hl(raw->drDirCnt);
+
+ /* TRY to get the alternate (backup) MDB. */
+ lcv = mdb->fs_start + mdb->fs_ablocks * mdb->alloc_blksz;
+ limit = lcv + mdb->alloc_blksz;
+ for (; lcv < limit; ++lcv) {
+ buf = hfs_buffer_get(sys_mdb, lcv, 1);
+ if (hfs_buffer_ok(buf)) {
+ struct raw_mdb *tmp =
+ (struct raw_mdb *)hfs_buffer_data(buf);
+
+ if (hfs_get_ns(tmp->drSigWord) ==
+ htons(HFS_SUPER_MAGIC)) {
+ mdb->alt_buf = buf;
+ break;
+ }
+ }
+ hfs_buffer_put(buf);
+ }
+
+ if (mdb->alt_buf == NULL) {
+ hfs_warn("hfs_fs: unable to locate alternate MDB\n");
+ hfs_warn("hfs_fs: continuing without an alternate MDB\n");
+ }
+
+ /* read in the bitmap */
+ block = hfs_get_hs(raw->drVBMSt) + part_start;
+ bmbuf = mdb->bitmap;
+ lcv = (mdb->fs_ablocks + 4095) / 4096;
+ for ( ; lcv; --lcv, ++bmbuf, ++block) {
+ if (!hfs_buffer_ok(*bmbuf =
+ hfs_buffer_get(sys_mdb, block, 1))) {
+ hfs_warn("hfs_fs: unable to read volume bitmap\n");
+ goto bail1;
+ }
+ }
+
+ if (!(mdb->ext_tree = hfs_btree_init(mdb, htonl(HFS_EXT_CNID),
+ raw->drXTExtRec,
+ hfs_get_hl(raw->drXTFlSize),
+ hfs_get_hl(raw->drXTClpSiz))) ||
+ !(mdb->cat_tree = hfs_btree_init(mdb, htonl(HFS_CAT_CNID),
+ raw->drCTExtRec,
+ hfs_get_hl(raw->drCTFlSize),
+ hfs_get_hl(raw->drCTClpSiz)))) {
+ hfs_warn("hfs_fs: unable to initialize data structures\n");
+ goto bail1;
+ }
+
+ if (!(mdb->attrib & htons(HFS_SB_ATTRIB_CLEAN))) {
+ hfs_warn("hfs_fs: WARNING: mounting unclean filesystem.\n");
+ } else if (!readonly && !(mdb->attrib & (HFS_SB_ATTRIB_HLOCK | HFS_SB_ATTRIB_SLOCK))) {
+ /* Mark the volume uncleanly unmounted in case we crash */
+ hfs_put_ns(mdb->attrib & htons(~HFS_SB_ATTRIB_CLEAN),
+ raw->drAtrb);
+ hfs_buffer_dirty(mdb->buf);
+ hfs_buffer_sync(mdb->buf);
+ }
+
+ return mdb;
+
+bail1:
+ hfs_mdb_put(mdb, readonly);
+bail2:
+ return NULL;
+}
+
+/*
+ * hfs_mdb_commit()
+ *
+ * Description:
+ * This updates the MDB on disk (look also at hfs_write_super()).
+ * It does not check, if the superblock has been modified, or
+ * if the filesystem has been mounted read-only. It is mainly
+ * called by hfs_write_super() and hfs_btree_extend().
+ * Input Variable(s):
+ * struct hfs_mdb *mdb: Pointer to the hfs MDB
+ * int backup;
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * void
+ * Preconditions:
+ * 'mdb' points to a "valid" (struct hfs_mdb).
+ * Postconditions:
+ * The HFS MDB and on disk will be updated, by copying the possibly
+ * modified fields from the in memory MDB (in native byte order) to
+ * the disk block buffer.
+ * If 'backup' is non-zero then the alternate MDB is also written
+ * and the function doesn't return until it is actually on disk.
+ */
+void hfs_mdb_commit(struct hfs_mdb *mdb, int backup)
+{
+ struct raw_mdb *raw = (struct raw_mdb *)hfs_buffer_data(mdb->buf);
+
+ /* Commit catalog entries to buffers */
+ hfs_cat_commit(mdb);
+
+ /* Commit B-tree data to buffers */
+ hfs_btree_commit(mdb->cat_tree, raw->drCTExtRec, raw->drCTFlSize);
+ hfs_btree_commit(mdb->ext_tree, raw->drXTExtRec, raw->drXTFlSize);
+
+ /* Update write_count and modify_date */
+ ++mdb->write_count;
+ mdb->modify_date = hfs_time();
+
+ /* These parameters may have been modified, so write them back */
+ hfs_put_nl(mdb->modify_date, raw->drLsMod);
+ hfs_put_hs(mdb->free_ablocks, raw->drFreeBks);
+ hfs_put_hl(mdb->next_id, raw->drNxtCNID);
+ hfs_put_hl(mdb->write_count, raw->drWrCnt);
+ hfs_put_hs(mdb->root_files, raw->drNmFls);
+ hfs_put_hs(mdb->root_dirs, raw->drNmRtDirs);
+ hfs_put_hl(mdb->file_count, raw->drFilCnt);
+ hfs_put_hl(mdb->dir_count, raw->drDirCnt);
+
+ /* write MDB to disk */
+ hfs_buffer_dirty(mdb->buf);
+
+ /* write the backup MDB, not returning until it is written.
+ * we only do this when either the catalog or extents overflow
+ * files grow. */
+ if (backup && hfs_buffer_ok(mdb->alt_buf)) {
+ struct raw_mdb *tmp = (struct raw_mdb *)
+ hfs_buffer_data(mdb->alt_buf);
+
+ if ((hfs_get_hl(tmp->drCTFlSize) <
+ hfs_get_hl(raw->drCTFlSize)) ||
+ (hfs_get_hl(tmp->drXTFlSize) <
+ hfs_get_hl(raw->drXTFlSize))) {
+ memcpy(hfs_buffer_data(mdb->alt_buf),
+ hfs_buffer_data(mdb->buf), HFS_SECTOR_SIZE);
+ hfs_buffer_dirty(mdb->alt_buf);
+ hfs_buffer_sync(mdb->alt_buf);
+ }
+ }
+}
+
+/*
+ * hfs_mdb_put()
+ *
+ * Release the resources associated with the in-core MDB. */
+void hfs_mdb_put(struct hfs_mdb *mdb, int readonly) {
+ int lcv;
+
+ /* invalidate cached catalog entries */
+ hfs_cat_invalidate(mdb);
+
+ /* free the B-trees */
+ hfs_btree_free(mdb->ext_tree);
+ hfs_btree_free(mdb->cat_tree);
+
+ /* free the volume bitmap */
+ for (lcv = 0; lcv < HFS_BM_MAXBLOCKS; ++lcv) {
+ hfs_buffer_put(mdb->bitmap[lcv]);
+ }
+
+ /* update volume attributes */
+ if (!readonly) {
+ struct raw_mdb *raw =
+ (struct raw_mdb *)hfs_buffer_data(mdb->buf);
+ hfs_put_ns(mdb->attrib, raw->drAtrb);
+ hfs_buffer_dirty(mdb->buf);
+ }
+
+ /* free the buffers holding the primary and alternate MDBs */
+ hfs_buffer_put(mdb->buf);
+ hfs_buffer_put(mdb->alt_buf);
+
+ /* free the MDB */
+ HFS_DELETE(mdb);
+}
diff --git a/fs/hfs/part_tbl.c b/fs/hfs/part_tbl.c
new file mode 100644
index 00000000000000..2211572262bded
--- /dev/null
+++ b/fs/hfs/part_tbl.c
@@ -0,0 +1,244 @@
+/*
+ * linux/fs/hfs/part_tbl.c
+ *
+ * Copyright (C) 1996-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU General Public License.
+ *
+ * Original code to handle the new style Mac partition table based on
+ * a patch contributed by Holger Schemel (aeglos@valinor.owl.de).
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ *
+ * The code in this file initializes some structures which contain
+ * pointers by calling memset(&foo, 0, sizeof(foo)).
+ * This produces the desired behavior only due to the non-ANSI
+ * assumption that the machine representation of NULL is all zeros.
+ */
+
+#include "hfs.h"
+
+/*================ File-local data types ================*/
+
+/*
+ * The Macintosh Driver Descriptor Block
+ *
+ * On partitioned Macintosh media this is block 0.
+ * We really only need the "magic number" to check for partitioned media.
+ */
+struct hfs_drvr_desc {
+ hfs_word_t ddSig; /* The signature word */
+ /* a bunch more stuff we don't need */
+};
+
+/*
+ * The new style Mac partition map
+ *
+ * For each partition on the media there is a physical block (512-byte
+ * block) containing one of these structures. These blocks are
+ * contiguous starting at block 1.
+ */
+struct new_pmap {
+ hfs_word_t pmSig; /* Signature bytes to verify
+ that this is a partition
+ map block */
+ hfs_word_t reSigPad; /* padding */
+ hfs_lword_t pmMapBlkCnt; /* (At least in block 1) this
+ is the number of partition
+ map blocks */
+ hfs_lword_t pmPyPartStart; /* The physical block number
+ of the first block in this
+ partition */
+ hfs_lword_t pmPartBlkCnt; /* The number of physical
+ blocks in this partition */
+ hfs_byte_t pmPartName[32]; /* (null terminated?) string
+ giving the name of this
+ partition */
+ hfs_byte_t pmPartType[32]; /* (null terminated?) string
+ giving the type of this
+ partition */
+ /* a bunch more stuff we don't need */
+};
+
+/*
+ * The old style Mac partition map
+ *
+ * The partition map consists for a 2-byte signature followed by an
+ * array of these structures. The map is terminated with an all-zero
+ * one of these.
+ */
+struct old_pmap {
+ hfs_word_t pdSig; /* Signature bytes */
+ struct old_pmap_entry {
+ hfs_lword_t pdStart;
+ hfs_lword_t pdSize;
+ hfs_lword_t pdFSID;
+ } pdEntry[42];
+} __attribute__((packed));
+
+/*================ File-local functions ================*/
+
+/*
+ * parse_new_part_table()
+ *
+ * Parse a new style partition map looking for the
+ * start and length of the 'part'th HFS partition.
+ */
+static int parse_new_part_table(hfs_sysmdb sys_mdb, hfs_buffer buf,
+ int part, hfs_s32 *size, hfs_s32 *start)
+{
+ struct new_pmap *pm = (struct new_pmap *)hfs_buffer_data(buf);
+ hfs_u32 pmap_entries = hfs_get_hl(pm->pmMapBlkCnt);
+ int hfs_part = 0;
+ int entry;
+
+ for (entry = 0; (entry < pmap_entries) && !(*start); ++entry) {
+ if (entry) {
+ /* read the next partition map entry */
+ buf = hfs_buffer_get(sys_mdb, HFS_PMAP_BLK + entry, 1);
+ if (!hfs_buffer_ok(buf)) {
+ hfs_warn("hfs_fs: unable to "
+ "read partition map.\n");
+ goto bail;
+ }
+ pm = (struct new_pmap *)hfs_buffer_data(buf);
+ if (hfs_get_ns(pm->pmSig) !=
+ htons(HFS_NEW_PMAP_MAGIC)) {
+ hfs_warn("hfs_fs: invalid "
+ "entry in partition map\n");
+ hfs_buffer_put(buf);
+ goto bail;
+ }
+ }
+
+ /* look for an HFS partition */
+ if (!memcmp(pm->pmPartType,"Apple_HFS",9) &&
+ ((hfs_part++) == part)) {
+ /* Found it! */
+ *start = hfs_get_hl(pm->pmPyPartStart);
+ *size = hfs_get_hl(pm->pmPartBlkCnt);
+ }
+
+ hfs_buffer_put(buf);
+ }
+
+ return 0;
+
+bail:
+ return 1;
+}
+
+/*
+ * parse_old_part_table()
+ *
+ * Parse a old style partition map looking for the
+ * start and length of the 'part'th HFS partition.
+ */
+static int parse_old_part_table(hfs_sysmdb sys_mdb, hfs_buffer buf,
+ int part, hfs_s32 *size, hfs_s32 *start)
+{
+ struct old_pmap *pm = (struct old_pmap *)hfs_buffer_data(buf);
+ struct old_pmap_entry *p = &pm->pdEntry[0];
+ int hfs_part = 0;
+
+ while ((p->pdStart || p->pdSize || p->pdFSID) && !(*start)) {
+ /* look for an HFS partition */
+ if ((hfs_get_nl(p->pdFSID) == htonl(0x54465331)/*"TFS1"*/) &&
+ ((hfs_part++) == part)) {
+ /* Found it! */
+ *start = hfs_get_hl(p->pdStart);
+ *size = hfs_get_hl(p->pdSize);
+ }
+ ++p;
+ }
+ hfs_buffer_put(buf);
+
+ return 0;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_part_find()
+ *
+ * Parse the partition map looking for the
+ * start and length of the 'part'th HFS partition.
+ */
+int hfs_part_find(hfs_sysmdb sys_mdb, int part, int silent,
+ hfs_s32 *size, hfs_s32 *start)
+{
+ hfs_buffer buf;
+ hfs_u16 sig;
+ int dd_found = 0;
+ int retval = 1;
+
+ /* Read block 0 to see if this media is partitioned */
+ buf = hfs_buffer_get(sys_mdb, HFS_DD_BLK, 1);
+ if (!hfs_buffer_ok(buf)) {
+ hfs_warn("hfs_fs: Unable to read block 0.\n");
+ goto done;
+ }
+ sig = hfs_get_ns(((struct hfs_drvr_desc *)hfs_buffer_data(buf))->ddSig);
+ hfs_buffer_put(buf);
+
+ if (sig == htons(HFS_DRVR_DESC_MAGIC)) {
+ /* We are definitely on partitioned media. */
+ dd_found = 1;
+ }
+
+ buf = hfs_buffer_get(sys_mdb, HFS_PMAP_BLK, 1);
+ if (!hfs_buffer_ok(buf)) {
+ hfs_warn("hfs_fs: Unable to read block 1.\n");
+ goto done;
+ }
+
+ *size = *start = 0;
+
+ switch (hfs_get_ns(hfs_buffer_data(buf))) {
+ case __constant_htons(HFS_OLD_PMAP_MAGIC):
+ retval = parse_old_part_table(sys_mdb, buf, part, size, start);
+ break;
+
+ case __constant_htons(HFS_NEW_PMAP_MAGIC):
+ retval = parse_new_part_table(sys_mdb, buf, part, size, start);
+ break;
+
+ default:
+ if (dd_found) {
+ /* The media claimed to have a partition map */
+ if (!silent) {
+ hfs_warn("hfs_fs: This disk has an "
+ "unrecognized partition map type.\n");
+ }
+ } else {
+ /* Conclude that the media is not partitioned */
+ retval = 0;
+ }
+ goto done;
+ }
+
+ if (!retval) {
+ if (*start == 0) {
+ if (part) {
+ hfs_warn("hfs_fs: unable to locate "
+ "HFS partition number %d.\n", part);
+ } else {
+ hfs_warn("hfs_fs: unable to locate any "
+ "HFS partitions.\n");
+ }
+ retval = 1;
+ } else if (*size < 0) {
+ hfs_warn("hfs_fs: Partition size > 1 Terabyte.\n");
+ retval = 1;
+ } else if (*start < 0) {
+ hfs_warn("hfs_fs: Partition begins beyond 1 "
+ "Terabyte.\n");
+ retval = 1;
+ }
+ }
+done:
+ return retval;
+}
diff --git a/fs/hfs/string.c b/fs/hfs/string.c
new file mode 100644
index 00000000000000..f499b9a805bbde
--- /dev/null
+++ b/fs/hfs/string.c
@@ -0,0 +1,144 @@
+/*
+ * linux/fs/hfs/string.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU General Public License.
+ *
+ * This file contains the string comparison function for the
+ * Macintosh character set.
+ *
+ * The code in this file is derived from code which is copyright
+ * 1986, 1989, 1990 by Abacus Research and Development, Inc. (ARDI)
+ * It is used here by the permission of ARDI's president Cliff Matthews.
+ *
+ * If you discover bugs in this code please notify both the author of the
+ * Linux HFS file system: hargrove@sccm.stanford.edu (Paul H. Hargrove)
+ * and the author of ARDI's HFS code: ctm@ardi.com (Clifford T. Matthews)
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ */
+
+#include "hfs.h"
+
+/*================ File-local variables ================*/
+
+/*
+ * unsigned char caseorder[]
+ *
+ * Defines the lexical ordering of characters on the Macintosh
+ *
+ * Composition of the 'casefold' and 'order' tables from ARDI's code
+ * with the entry for 0x20 changed to match that for 0xCA to remove
+ * special case for those two characters.
+ */
+static unsigned char caseorder[256] = {
+0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,
+0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,
+0x20,0x22,0x23,0x28,0x29,0x2A,0x2B,0x2C,0x2F,0x30,0x31,0x32,0x33,0x34,0x35,0x36,
+0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,0x40,0x41,0x42,0x43,0x44,0x45,0x46,
+0x47,0x48,0x57,0x59,0x5D,0x5F,0x66,0x68,0x6A,0x6C,0x72,0x74,0x76,0x78,0x7A,0x7E,
+0x8C,0x8E,0x90,0x92,0x95,0x97,0x9E,0xA0,0xA2,0xA4,0xA7,0xA9,0xAA,0xAB,0xAC,0xAD,
+0x4E,0x48,0x57,0x59,0x5D,0x5F,0x66,0x68,0x6A,0x6C,0x72,0x74,0x76,0x78,0x7A,0x7E,
+0x8C,0x8E,0x90,0x92,0x95,0x97,0x9E,0xA0,0xA2,0xA4,0xA7,0xAF,0xB0,0xB1,0xB2,0xB3,
+0x4A,0x4C,0x5A,0x60,0x7B,0x7F,0x98,0x4F,0x49,0x51,0x4A,0x4B,0x4C,0x5A,0x60,0x63,
+0x64,0x65,0x6E,0x6F,0x70,0x71,0x7B,0x84,0x85,0x86,0x7F,0x80,0x9A,0x9B,0x9C,0x98,
+0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0x94,0xBB,0xBC,0xBD,0xBE,0xBF,0xC0,0x4D,0x81,
+0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0x55,0x8A,0xCC,0x4D,0x81,
+0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0x26,0x27,0xD4,0x20,0x49,0x4B,0x80,0x82,0x82,
+0xD5,0xD6,0x24,0x25,0x2D,0x2E,0xD7,0xD8,0xA6,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF,
+0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,
+0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF
+};
+
+/*
+ * unsigned char casefold[]
+ *
+ * Defines the mapping to lowercase characters on the Macintosh
+ *
+ * "Inverse" of the 'casefold' from ARDI's code.
+ */
+static unsigned char casefold[256] = {
+0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,
+0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,
+0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,
+0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,
+0x40,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,
+0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x5B,0x5C,0x5D,0x5E,0x5F,
+0x41,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,
+0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F,
+0x8A,0x8C,0x8D,0x8E,0x96,0x9A,0x9F,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,
+0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,
+0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xBE,0xBF,
+0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF,
+0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0x88,0x8B,0x9B,0xCF,0xCF,
+0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF,
+0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,
+0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF
+};
+
+/*================ Global functions ================*/
+
+/*
+ * Hash a string to an integer in a case-independent way
+ */
+unsigned int hfs_strhash(const unsigned char *name, unsigned int len)
+{
+ unsigned long hash = init_name_hash();
+
+ while (len--)
+ hash = partial_name_hash(caseorder[*name++],
+ hash);
+ return end_name_hash(hash);
+}
+
+/*
+ * Compare two strings in the HFS filename character ordering
+ * Returns positive, negative, or zero, not just 0 or (+/-)1
+ *
+ * Equivalent to ARDI's call:
+ * ROMlib_RelString(s1+1, s2+1, true, false, (s1[0]<<16) | s2[0])
+ */
+int hfs_strcmp(const unsigned char *s1, unsigned int len1,
+ const unsigned char *s2, unsigned int len2)
+{
+ int len, tmp;
+
+ len = (len1 > len2) ? len2 : len1;
+
+ while (len--) {
+ if ((tmp = (int)caseorder[*(s1++)] -
+ (int)caseorder[*(s2++)])) {
+ return tmp;
+ }
+ }
+ return len1 - len2;
+}
+
+/*
+ * Test for equality of two strings in the HFS filename character ordering.
+ */
+int hfs_streq(const unsigned char *s1, unsigned int len1,
+ const unsigned char *s2, unsigned int len2)
+{
+ if (len1 != len2) {
+ return 0;
+ }
+
+ while (len1--) {
+ if (caseorder[*(s1++)] != caseorder[*(s2++)]) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/*
+ * Convert a string to the Macintosh version of lower case.
+ */
+void hfs_tolower(unsigned char *p, int len)
+{
+ while (len--) {
+ *p = casefold[*p];
+ ++p;
+ }
+}
diff --git a/fs/hfs/super.c b/fs/hfs/super.c
new file mode 100644
index 00000000000000..d90d7bd43d425b
--- /dev/null
+++ b/fs/hfs/super.c
@@ -0,0 +1,529 @@
+/*
+ * linux/fs/hfs/super.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU General Public License.
+ *
+ * This file contains hfs_read_super(), some of the super_ops and
+ * init_module() and cleanup_module(). The remaining super_ops are in
+ * inode.c since they deal with inodes.
+ *
+ * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ *
+ * The code in this file initializes some structures which contain
+ * pointers by calling memset(&foo, 0, sizeof(foo)).
+ * This produces the desired behavior only due to the non-ANSI
+ * assumption that the machine representation of NULL is all zeros.
+ */
+
+#include "hfs.h"
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+#include <linux/config.h> /* for CONFIG_MAC_PARTITION */
+#include <linux/blkdev.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+MODULE_LICENSE("GPL");
+
+/*================ Forward declarations ================*/
+
+static void hfs_read_inode(struct inode *);
+static void hfs_put_super(struct super_block *);
+static int hfs_statfs(struct super_block *, struct statfs *);
+static void hfs_write_super(struct super_block *);
+
+/*================ Global variables ================*/
+
+static struct super_operations hfs_super_operations = {
+ read_inode: hfs_read_inode,
+ put_inode: hfs_put_inode,
+ put_super: hfs_put_super,
+ write_super: hfs_write_super,
+ statfs: hfs_statfs,
+ remount_fs: hfs_remount,
+};
+
+/*================ File-local variables ================*/
+
+static DECLARE_FSTYPE_DEV(hfs_fs, "hfs", hfs_read_super);
+
+/*================ File-local functions ================*/
+
+/*
+ * hfs_read_inode()
+ *
+ * this doesn't actually do much. hfs_iget actually fills in the
+ * necessary inode information.
+ */
+static void hfs_read_inode(struct inode *inode)
+{
+ inode->i_mode = 0;
+}
+
+/*
+ * hfs_write_super()
+ *
+ * Description:
+ * This function is called by the VFS only. When the filesystem
+ * is mounted r/w it updates the MDB on disk.
+ * Input Variable(s):
+ * struct super_block *sb: Pointer to the hfs superblock
+ * Output Variable(s):
+ * NONE
+ * Returns:
+ * void
+ * Preconditions:
+ * 'sb' points to a "valid" (struct super_block).
+ * Postconditions:
+ * The MDB is marked 'unsuccessfully unmounted' by clearing bit 8 of drAtrb
+ * (hfs_put_super() must set this flag!). Some MDB fields are updated
+ * and the MDB buffer is written to disk by calling hfs_mdb_commit().
+ */
+static void hfs_write_super(struct super_block *sb)
+{
+ struct hfs_mdb *mdb = HFS_SB(sb)->s_mdb;
+
+ /* is this a valid hfs superblock? */
+ if (!sb || sb->s_magic != HFS_SUPER_MAGIC) {
+ return;
+ }
+
+ if (!(sb->s_flags & MS_RDONLY)) {
+ /* sync everything to the buffers */
+ hfs_mdb_commit(mdb, 0);
+ }
+ sb->s_dirt = 0;
+}
+
+/*
+ * hfs_put_super()
+ *
+ * This is the put_super() entry in the super_operations structure for
+ * HFS filesystems. The purpose is to release the resources
+ * associated with the superblock sb.
+ */
+static void hfs_put_super(struct super_block *sb)
+{
+ struct hfs_mdb *mdb = HFS_SB(sb)->s_mdb;
+
+ if (!(sb->s_flags & MS_RDONLY)) {
+ hfs_mdb_commit(mdb, 0);
+ sb->s_dirt = 0;
+ }
+
+ /* release the MDB's resources */
+ hfs_mdb_put(mdb, sb->s_flags & MS_RDONLY);
+
+ /* restore default blocksize for the device */
+ set_blocksize(sb->s_dev, BLOCK_SIZE);
+}
+
+/*
+ * hfs_statfs()
+ *
+ * This is the statfs() entry in the super_operations structure for
+ * HFS filesystems. The purpose is to return various data about the
+ * filesystem.
+ *
+ * changed f_files/f_ffree to reflect the fs_ablock/free_ablocks.
+ */
+static int hfs_statfs(struct super_block *sb, struct statfs *buf)
+{
+ struct hfs_mdb *mdb = HFS_SB(sb)->s_mdb;
+
+ buf->f_type = HFS_SUPER_MAGIC;
+ buf->f_bsize = HFS_SECTOR_SIZE;
+ buf->f_blocks = mdb->alloc_blksz * mdb->fs_ablocks;
+ buf->f_bfree = mdb->alloc_blksz * mdb->free_ablocks;
+ buf->f_bavail = buf->f_bfree;
+ buf->f_files = mdb->fs_ablocks;
+ buf->f_ffree = mdb->free_ablocks;
+ buf->f_namelen = HFS_NAMELEN;
+
+ return 0;
+}
+
+/*
+ * parse_options()
+ *
+ * adapted from linux/fs/msdos/inode.c written 1992,93 by Werner Almesberger
+ * This function is called by hfs_read_super() to parse the mount options.
+ */
+static int parse_options(char *options, struct hfs_sb_info *hsb, int *part)
+{
+ char *this_char, *value;
+ char names, fork;
+
+ if (hsb->magic != HFS_SB_MAGIC) {
+ /* initialize the sb with defaults */
+ hsb->magic = HFS_SB_MAGIC;
+ hsb->s_uid = current->uid;
+ hsb->s_gid = current->gid;
+ hsb->s_umask = current->fs->umask;
+ hsb->s_type = 0x3f3f3f3f; /* == '????' */
+ hsb->s_creator = 0x3f3f3f3f; /* == '????' */
+ hsb->s_lowercase = 0;
+ hsb->s_quiet = 0;
+ hsb->s_afpd = 0;
+ /* default version. 0 just selects the defaults */
+ hsb->s_version = 0;
+ hsb->s_conv = 'b';
+ names = '?';
+ fork = '?';
+ *part = 0;
+ }
+
+ if (!options) {
+ goto done;
+ }
+ for (this_char = strtok(options,","); this_char;
+ this_char = strtok(NULL,",")) {
+ if ((value = strchr(this_char,'=')) != NULL) {
+ *value++ = 0;
+ }
+ /* Numeric-valued options */
+ if (!strcmp(this_char, "version")) {
+ if (!value || !*value) {
+ return 0;
+ }
+ hsb->s_version = simple_strtoul(value,&value,0);
+ if (*value) {
+ return 0;
+ }
+ } else if (!strcmp(this_char,"uid")) {
+ if (!value || !*value) {
+ return 0;
+ }
+ hsb->s_uid = simple_strtoul(value,&value,0);
+ if (*value) {
+ return 0;
+ }
+ } else if (!strcmp(this_char,"gid")) {
+ if (!value || !*value) {
+ return 0;
+ }
+ hsb->s_gid = simple_strtoul(value,&value,0);
+ if (*value) {
+ return 0;
+ }
+ } else if (!strcmp(this_char,"umask")) {
+ if (!value || !*value) {
+ return 0;
+ }
+ hsb->s_umask = simple_strtoul(value,&value,8);
+ if (*value) {
+ return 0;
+ }
+ } else if (!strcmp(this_char,"part")) {
+ if (!value || !*value) {
+ return 0;
+ }
+ *part = simple_strtoul(value,&value,0);
+ if (*value) {
+ return 0;
+ }
+ /* String-valued options */
+ } else if (!strcmp(this_char,"type") && value) {
+ if (strlen(value) != 4) {
+ return 0;
+ }
+ hsb->s_type = hfs_get_nl(value);
+ } else if (!strcmp(this_char,"creator") && value) {
+ if (strlen(value) != 4) {
+ return 0;
+ }
+ hsb->s_creator = hfs_get_nl(value);
+ /* Boolean-valued options */
+ } else if (!strcmp(this_char,"quiet")) {
+ if (value) {
+ return 0;
+ }
+ hsb->s_quiet = 1;
+ } else if (!strcmp(this_char,"afpd")) {
+ if (value) {
+ return 0;
+ }
+ hsb->s_afpd = 1;
+ /* Multiple choice options */
+ } else if (!strcmp(this_char,"names") && value) {
+ if ((*value && !value[1] && strchr("ntal78c",*value)) ||
+ !strcmp(value,"netatalk") ||
+ !strcmp(value,"trivial") ||
+ !strcmp(value,"alpha") ||
+ !strcmp(value,"latin") ||
+ !strcmp(value,"7bit") ||
+ !strcmp(value,"8bit") ||
+ !strcmp(value,"cap")) {
+ names = *value;
+ } else {
+ return 0;
+ }
+ } else if (!strcmp(this_char,"fork") && value) {
+ if ((*value && !value[1] && strchr("nsdc",*value)) ||
+ !strcmp(value,"netatalk") ||
+ !strcmp(value,"single") ||
+ !strcmp(value,"double") ||
+ !strcmp(value,"cap")) {
+ fork = *value;
+ } else {
+ return 0;
+ }
+ } else if (!strcmp(this_char,"case") && value) {
+ if ((*value && !value[1] && strchr("la",*value)) ||
+ !strcmp(value,"lower") ||
+ !strcmp(value,"asis")) {
+ hsb->s_lowercase = (*value == 'l');
+ } else {
+ return 0;
+ }
+ } else if (!strcmp(this_char,"conv") && value) {
+ if ((*value && !value[1] && strchr("bta",*value)) ||
+ !strcmp(value,"binary") ||
+ !strcmp(value,"text") ||
+ !strcmp(value,"auto")) {
+ hsb->s_conv = *value;
+ } else {
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+ }
+
+done:
+ /* Parse the "fork" and "names" options */
+ if (fork == '?') {
+ fork = hsb->s_afpd ? 'n' : 'c';
+ }
+ switch (fork) {
+ default:
+ case 'c':
+ hsb->s_ifill = hfs_cap_ifill;
+ hsb->s_reserved1 = hfs_cap_reserved1;
+ hsb->s_reserved2 = hfs_cap_reserved2;
+ break;
+
+ case 's':
+ hfs_warn("hfs_fs: AppleSingle not yet implemented.\n");
+ return 0;
+ /* break; */
+
+ case 'd':
+ hsb->s_ifill = hfs_dbl_ifill;
+ hsb->s_reserved1 = hfs_dbl_reserved1;
+ hsb->s_reserved2 = hfs_dbl_reserved2;
+ break;
+
+ case 'n':
+ hsb->s_ifill = hfs_nat_ifill;
+ hsb->s_reserved1 = hfs_nat_reserved1;
+ hsb->s_reserved2 = hfs_nat_reserved2;
+ break;
+ }
+
+ if (names == '?') {
+ names = fork;
+ }
+ switch (names) {
+ default:
+ case 'n':
+ hsb->s_nameout = hfs_colon2mac;
+ hsb->s_namein = hfs_mac2nat;
+ break;
+
+ case 'c':
+ hsb->s_nameout = hfs_colon2mac;
+ hsb->s_namein = hfs_mac2cap;
+ break;
+
+ case 't':
+ hsb->s_nameout = hfs_triv2mac;
+ hsb->s_namein = hfs_mac2triv;
+ break;
+
+ case '7':
+ hsb->s_nameout = hfs_prcnt2mac;
+ hsb->s_namein = hfs_mac2seven;
+ break;
+
+ case '8':
+ hsb->s_nameout = hfs_prcnt2mac;
+ hsb->s_namein = hfs_mac2eight;
+ break;
+
+ case 'l':
+ hsb->s_nameout = hfs_latin2mac;
+ hsb->s_namein = hfs_mac2latin;
+ break;
+
+ case 'a': /* 's' and 'd' are unadvertised aliases for 'alpha', */
+ case 's': /* since 'alpha' is the default if fork=s or fork=d. */
+ case 'd': /* (It is also helpful for poor typists!) */
+ hsb->s_nameout = hfs_prcnt2mac;
+ hsb->s_namein = hfs_mac2alpha;
+ break;
+ }
+
+ return 1;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_read_super()
+ *
+ * This is the function that is responsible for mounting an HFS
+ * filesystem. It performs all the tasks necessary to get enough data
+ * from the disk to read the root inode. This includes parsing the
+ * mount options, dealing with Macintosh partitions, reading the
+ * superblock and the allocation bitmap blocks, calling
+ * hfs_btree_init() to get the necessary data about the extents and
+ * catalog B-trees and, finally, reading the root inode into memory.
+ */
+struct super_block *hfs_read_super(struct super_block *s, void *data,
+ int silent)
+{
+ struct hfs_mdb *mdb;
+ struct hfs_cat_key key;
+ kdev_t dev = s->s_dev;
+ int dev_blocksize;
+ hfs_s32 part_size, part_start;
+ struct inode *root_inode;
+ int part;
+
+ memset(HFS_SB(s), 0, sizeof(*(HFS_SB(s))));
+ if (!parse_options((char *)data, HFS_SB(s), &part)) {
+ hfs_warn("hfs_fs: unable to parse mount options.\n");
+ goto bail3;
+ }
+
+ /* set the device driver to 512-byte blocks */
+ if (set_blocksize(dev, HFS_SECTOR_SIZE) < 0) {
+ dev_blocksize = get_hardsect_size(dev);
+ hfs_warn("hfs_fs: unsupported device block size: %d\n",
+ dev_blocksize);
+ goto bail3;
+ }
+ s->s_blocksize_bits = HFS_SECTOR_SIZE_BITS;
+ s->s_blocksize = HFS_SECTOR_SIZE;
+
+#ifdef CONFIG_MAC_PARTITION
+ /* check to see if we're in a partition */
+ mdb = hfs_mdb_get(s, s->s_flags & MS_RDONLY, 0);
+
+ /* erk. try parsing the partition table ourselves */
+ if (!mdb) {
+ if (hfs_part_find(s, part, silent, &part_size, &part_start)) {
+ goto bail2;
+ }
+ mdb = hfs_mdb_get(s, s->s_flags & MS_RDONLY, part_start);
+ }
+#else
+ if (hfs_part_find(s, part, silent, &part_size, &part_start)) {
+ goto bail2;
+ }
+
+ mdb = hfs_mdb_get(s, s->s_flags & MS_RDONLY, part_start);
+#endif
+
+ if (!mdb) {
+ if (!silent) {
+ hfs_warn("VFS: Can't find a HFS filesystem on dev %s.\n",
+ kdevname(dev));
+ }
+ goto bail2;
+ }
+
+ if (mdb->attrib & (HFS_SB_ATTRIB_HLOCK | HFS_SB_ATTRIB_SLOCK)) {
+ if (!silent)
+ hfs_warn("hfs_fs: Filesystem is marked locked, mounting read-only.\n");
+ s->s_flags |= MS_RDONLY;
+ }
+
+ HFS_SB(s)->s_mdb = mdb;
+ if (HFS_ITYPE(mdb->next_id) != 0) {
+ hfs_warn("hfs_fs: too many files.\n");
+ goto bail1;
+ }
+
+ s->s_magic = HFS_SUPER_MAGIC;
+ s->s_op = &hfs_super_operations;
+
+ /* try to get the root inode */
+ hfs_cat_build_key(htonl(HFS_POR_CNID),
+ (struct hfs_name *)(mdb->vname), &key);
+
+ root_inode = hfs_iget(hfs_cat_get(mdb, &key), HFS_ITYPE_NORM, NULL);
+ if (!root_inode)
+ goto bail_no_root;
+
+ s->s_root = d_alloc_root(root_inode);
+ if (!s->s_root)
+ goto bail_no_root;
+
+ /* fix up pointers. */
+ HFS_I(root_inode)->entry->sys_entry[HFS_ITYPE_TO_INT(HFS_ITYPE_NORM)] =
+ s->s_root;
+ s->s_root->d_op = &hfs_dentry_operations;
+
+ /* everything's okay */
+ return s;
+
+bail_no_root:
+ hfs_warn("hfs_fs: get root inode failed.\n");
+ iput(root_inode);
+bail1:
+ hfs_mdb_put(mdb, s->s_flags & MS_RDONLY);
+bail2:
+ set_blocksize(dev, BLOCK_SIZE);
+bail3:
+ return NULL;
+}
+
+int hfs_remount(struct super_block *s, int *flags, char *data)
+{
+ int part; /* ignored */
+
+ if (!parse_options(data, HFS_SB(s), &part)) {
+ hfs_warn("hfs_fs: unable to parse mount options.\n");
+ return -EINVAL;
+ }
+
+ if ((*flags & MS_RDONLY) == (s->s_flags & MS_RDONLY))
+ return 0;
+ if (!(*flags & MS_RDONLY)) {
+ if (HFS_SB(s)->s_mdb->attrib & (HFS_SB_ATTRIB_HLOCK | HFS_SB_ATTRIB_SLOCK)) {
+ hfs_warn("hfs_fs: Filesystem is marked locked, leaving it read-only.\n");
+ s->s_flags |= MS_RDONLY;
+ *flags |= MS_RDONLY;
+ }
+ }
+ return 0;
+}
+
+static int __init init_hfs_fs(void)
+{
+ hfs_cat_init();
+ return register_filesystem(&hfs_fs);
+}
+
+static void __exit exit_hfs_fs(void) {
+ hfs_cat_free();
+ unregister_filesystem(&hfs_fs);
+}
+
+module_init(init_hfs_fs)
+module_exit(exit_hfs_fs)
+
+#if defined(DEBUG_ALL) || defined(DEBUG_MEM)
+long int hfs_alloc = 0;
+#endif
diff --git a/fs/hfs/sysdep.c b/fs/hfs/sysdep.c
new file mode 100644
index 00000000000000..c96107d5fcab21
--- /dev/null
+++ b/fs/hfs/sysdep.c
@@ -0,0 +1,109 @@
+/*
+ * linux/fs/hfs/sysdep.c
+ *
+ * Copyright (C) 1996 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU General Public License.
+ *
+ * This file contains the code to do various system dependent things.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs.h"
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+#include <linux/smp_lock.h>
+
+static int hfs_revalidate_dentry(struct dentry *, int);
+static int hfs_hash_dentry(struct dentry *, struct qstr *);
+static int hfs_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
+static void hfs_dentry_iput(struct dentry *, struct inode *);
+struct dentry_operations hfs_dentry_operations =
+{
+ d_revalidate: hfs_revalidate_dentry,
+ d_hash: hfs_hash_dentry,
+ d_compare: hfs_compare_dentry,
+ d_iput: hfs_dentry_iput,
+};
+
+/*
+ * hfs_buffer_get()
+ *
+ * Return a buffer for the 'block'th block of the media.
+ * If ('read'==0) then the buffer is not read from disk.
+ */
+hfs_buffer hfs_buffer_get(hfs_sysmdb sys_mdb, int block, int read) {
+ hfs_buffer tmp = HFS_BAD_BUFFER;
+
+ if (read) {
+ tmp = sb_bread(sys_mdb, block);
+ } else {
+ tmp = sb_getblk(sys_mdb, block);
+ if (tmp) {
+ mark_buffer_uptodate(tmp, 1);
+ }
+ }
+ if (!tmp) {
+ hfs_error("hfs_fs: unable to read block 0x%08x from dev %s\n",
+ block, hfs_mdb_name(sys_mdb));
+ }
+
+ return tmp;
+}
+
+/* dentry case-handling: just lowercase everything */
+
+/* hfs_strhash now uses the same hashing function as the dcache. */
+static int hfs_hash_dentry(struct dentry *dentry, struct qstr *this)
+{
+ if (this->len > HFS_NAMELEN)
+ return 0;
+
+ this->hash = hfs_strhash(this->name, this->len);
+ return 0;
+}
+
+/* return 1 on failure and 0 on success */
+static int hfs_compare_dentry(struct dentry *dentry, struct qstr *a,
+ struct qstr *b)
+{
+ if (a->len != b->len) return 1;
+
+ if (a->len > HFS_NAMELEN)
+ return 1;
+
+ return !hfs_streq(a->name, a->len, b->name, b->len);
+}
+
+static void hfs_dentry_iput(struct dentry *dentry, struct inode *inode)
+{
+ struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+
+ lock_kernel();
+ entry->sys_entry[HFS_ITYPE_TO_INT(HFS_ITYPE(inode->i_ino))] = NULL;
+ unlock_kernel();
+ iput(inode);
+}
+
+static int hfs_revalidate_dentry(struct dentry *dentry, int flags)
+{
+ struct inode *inode = dentry->d_inode;
+ int diff;
+
+ /* fix up inode on a timezone change */
+ lock_kernel();
+ if (inode &&
+ (diff = (hfs_to_utc(0) - HFS_I(inode)->tz_secondswest))) {
+ inode->i_ctime += diff;
+ inode->i_atime += diff;
+ inode->i_mtime += diff;
+ HFS_I(inode)->tz_secondswest += diff;
+ }
+ unlock_kernel();
+ return 1;
+}
diff --git a/fs/hfs/trans.c b/fs/hfs/trans.c
new file mode 100644
index 00000000000000..64adf73fc5c675
--- /dev/null
+++ b/fs/hfs/trans.c
@@ -0,0 +1,556 @@
+/*
+ * linux/fs/hfs/trans.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU General Public License.
+ *
+ * This file contains routines for converting between the Macintosh
+ * character set and various other encodings. This includes dealing
+ * with ':' vs. '/' as the path-element separator.
+ *
+ * Latin-1 translation based on code contributed by Holger Schemel
+ * (aeglos@valinor.owl.de).
+ *
+ * The '8-bit', '7-bit ASCII' and '7-bit alphanumeric' encodings are
+ * implementations of the three encodings recommended by Apple in the
+ * document "AppleSingle/AppleDouble Formats: Developer's Note
+ * (9/94)". This document is available from Apple's Technical
+ * Information Library from the World Wide Web server
+ * www.info.apple.com.
+ *
+ * The 'CAP' encoding is an implementation of the naming scheme used
+ * by the Columbia AppleTalk Package, available for anonymous FTP from
+ * ????.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include "hfs.h"
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+/*================ File-local variables ================*/
+
+/* int->ASCII map for a single hex digit */
+static char hex[16] = {'0','1','2','3','4','5','6','7',
+ '8','9','a','b','c','d','e','f'};
+/*
+ * Latin-1 to Mac character set map
+ *
+ * For the sake of consistency this map is generated from the Mac to
+ * Latin-1 map the first time it is needed. This means there is just
+ * one map to maintain.
+ */
+static unsigned char latin2mac_map[128]; /* initially all zero */
+
+/*
+ * Mac to Latin-1 map for the upper 128 characters (both have ASCII in
+ * the lower 128 positions)
+ */
+static unsigned char mac2latin_map[128] = {
+ 0xC4, 0xC5, 0xC7, 0xC9, 0xD1, 0xD6, 0xDC, 0xE1,
+ 0xE0, 0xE2, 0xE4, 0xE3, 0xE5, 0xE7, 0xE9, 0xE8,
+ 0xEA, 0xEB, 0xED, 0xEC, 0xEE, 0xEF, 0xF1, 0xF3,
+ 0xF2, 0xF4, 0xF6, 0xF5, 0xFA, 0xF9, 0xFB, 0xFC,
+ 0x00, 0xB0, 0xA2, 0xA3, 0xA7, 0xB7, 0xB6, 0xDF,
+ 0xAE, 0xA9, 0x00, 0xB4, 0xA8, 0x00, 0xC6, 0xD8,
+ 0x00, 0xB1, 0x00, 0x00, 0xA5, 0xB5, 0xF0, 0x00,
+ 0x00, 0x00, 0x00, 0xAA, 0xBA, 0x00, 0xE6, 0xF8,
+ 0xBF, 0xA1, 0xAC, 0x00, 0x00, 0x00, 0x00, 0xAB,
+ 0xBB, 0x00, 0xA0, 0xC0, 0xC3, 0xD5, 0x00, 0x00,
+ 0xAD, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF7, 0x00,
+ 0xFF, 0x00, 0x00, 0xA4, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xB8, 0x00, 0x00, 0xC2, 0xCA, 0xC1,
+ 0xCB, 0xC8, 0xCD, 0xCE, 0xCF, 0xCC, 0xD3, 0xD4,
+ 0x00, 0xD2, 0xDA, 0xDB, 0xD9, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/*================ File-local functions ================*/
+
+/*
+ * dehex()
+ *
+ * Given a hexadecimal digit in ASCII, return the integer representation.
+ */
+static inline const unsigned char dehex(char c) {
+ if ((c>='0')&&(c<='9')) {
+ return c-'0';
+ }
+ if ((c>='a')&&(c<='f')) {
+ return c-'a'+10;
+ }
+ if ((c>='A')&&(c<='F')) {
+ return c-'A'+10;
+ }
+ return 0xff;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_mac2nat()
+ *
+ * Given a 'Pascal String' (a string preceded by a length byte) in
+ * the Macintosh character set produce the corresponding filename using
+ * the Netatalk name-mangling scheme, returning the length of the
+ * mangled filename. Note that the output string is not NULL terminated.
+ *
+ * The name-mangling works as follows:
+ * Characters 32-126 (' '-'~') except '/' and any initial '.' are passed
+ * unchanged from input to output. The remaining characters are replaced
+ * by three characters: ':xx' where xx is the hexadecimal representation
+ * of the character, using lowercase 'a' through 'f'.
+ */
+int hfs_mac2nat(char *out, const struct hfs_name *in) {
+ unsigned char c;
+ const unsigned char *p = in->Name;
+ int len = in->Len;
+ int count = 0;
+
+ /* Special case for .AppleDesktop which in the
+ distant future may be a pseudodirectory. */
+ if (strncmp(".AppleDesktop", p, len) == 0) {
+ strncpy(out, p, 13);
+ return 13;
+ }
+
+ while (len--) {
+ c = *p++;
+ if ((c<32) || (c=='/') || (c>126) || (!count && (c=='.'))) {
+ *out++ = ':';
+ *out++ = hex[(c>>4) & 0xf];
+ *out++ = hex[c & 0xf];
+ count += 3;
+ } else {
+ *out++ = c;
+ count++;
+ }
+ }
+ return count;
+}
+
+/*
+ * hfs_mac2cap()
+ *
+ * Given a 'Pascal String' (a string preceded by a length byte) in
+ * the Macintosh character set produce the corresponding filename using
+ * the CAP name-mangling scheme, returning the length of the mangled
+ * filename. Note that the output string is not NULL terminated.
+ *
+ * The name-mangling works as follows:
+ * Characters 32-126 (' '-'~') except '/' are passed unchanged from
+ * input to output. The remaining characters are replaced by three
+ * characters: ':xx' where xx is the hexadecimal representation of the
+ * character, using lowercase 'a' through 'f'.
+ */
+int hfs_mac2cap(char *out, const struct hfs_name *in) {
+ unsigned char c;
+ const unsigned char *p = in->Name;
+ int len = in->Len;
+ int count = 0;
+
+ while (len--) {
+ c = *p++;
+ if ((c<32) || (c=='/') || (c>126)) {
+ *out++ = ':';
+ *out++ = hex[(c>>4) & 0xf];
+ *out++ = hex[c & 0xf];
+ count += 3;
+ } else {
+ *out++ = c;
+ count++;
+ }
+ }
+ return count;
+}
+
+/*
+ * hfs_mac2eight()
+ *
+ * Given a 'Pascal String' (a string preceded by a length byte) in
+ * the Macintosh character set produce the corresponding filename using
+ * the '8-bit' name-mangling scheme, returning the length of the
+ * mangled filename. Note that the output string is not NULL
+ * terminated.
+ *
+ * This is one of the three recommended naming conventions described
+ * in Apple's document "AppleSingle/AppleDouble Formats: Developer's
+ * Note (9/94)"
+ *
+ * The name-mangling works as follows:
+ * Characters 0, '%' and '/' are replaced by three characters: '%xx'
+ * where xx is the hexadecimal representation of the character, using
+ * lowercase 'a' through 'f'. All other characters are passed
+ * unchanged from input to output. Note that this format is mainly
+ * implemented for completeness and is rather hard to read.
+ */
+int hfs_mac2eight(char *out, const struct hfs_name *in) {
+ unsigned char c;
+ const unsigned char *p = in->Name;
+ int len = in->Len;
+ int count = 0;
+
+ while (len--) {
+ c = *p++;
+ if (!c || (c=='/') || (c=='%')) {
+ *out++ = '%';
+ *out++ = hex[(c>>4) & 0xf];
+ *out++ = hex[c & 0xf];
+ count += 3;
+ } else {
+ *out++ = c;
+ count++;
+ }
+ }
+ return count;
+}
+
+/*
+ * hfs_mac2seven()
+ *
+ * Given a 'Pascal String' (a string preceded by a length byte) in
+ * the Macintosh character set produce the corresponding filename using
+ * the '7-bit ASCII' name-mangling scheme, returning the length of the
+ * mangled filename. Note that the output string is not NULL
+ * terminated.
+ *
+ * This is one of the three recommended naming conventions described
+ * in Apple's document "AppleSingle/AppleDouble Formats: Developer's
+ * Note (9/94)"
+ *
+ * The name-mangling works as follows:
+ * Characters 0, '%', '/' and 128-255 are replaced by three
+ * characters: '%xx' where xx is the hexadecimal representation of the
+ * character, using lowercase 'a' through 'f'. All other characters
+ * are passed unchanged from input to output. Note that control
+ * characters (including newline) and space are unchanged make reading
+ * these filenames difficult.
+ */
+int hfs_mac2seven(char *out, const struct hfs_name *in) {
+ unsigned char c;
+ const unsigned char *p = in->Name;
+ int len = in->Len;
+ int count = 0;
+
+ while (len--) {
+ c = *p++;
+ if (!c || (c=='/') || (c=='%') || (c&0x80)) {
+ *out++ = '%';
+ *out++ = hex[(c>>4) & 0xf];
+ *out++ = hex[c & 0xf];
+ count += 3;
+ } else {
+ *out++ = c;
+ count++;
+ }
+ }
+ return count;
+}
+
+/*
+ * hfs_mac2alpha()
+ *
+ * Given a 'Pascal String' (a string preceded by a length byte) in
+ * the Macintosh character set produce the corresponding filename using
+ * the '7-bit alphanumeric' name-mangling scheme, returning the length
+ * of the mangled filename. Note that the output string is not NULL
+ * terminated.
+ *
+ * This is one of the three recommended naming conventions described
+ * in Apple's document "AppleSingle/AppleDouble Formats: Developer's
+ * Note (9/94)"
+ *
+ * The name-mangling works as follows:
+ * The characters 'a'-'z', 'A'-'Z', '0'-'9', '_' and the last '.' in
+ * the filename are passed unchanged from input to output. All
+ * remaining characters (including any '.'s other than the last) are
+ * replaced by three characters: '%xx' where xx is the hexadecimal
+ * representation of the character, using lowercase 'a' through 'f'.
+ */
+int hfs_mac2alpha(char *out, const struct hfs_name *in) {
+ unsigned char c;
+ const unsigned char *p = in->Name;
+ int len = in->Len;
+ int count = 0;
+ const unsigned char *lp; /* last period */
+
+ /* strrchr() would be good here, but 'in' is not null-terminated */
+ for (lp=p+len-1; (lp>=p)&&(*lp!='.'); --lp) {}
+ ++lp;
+
+ while (len--) {
+ c = *p++;
+ if ((p==lp) || ((c>='0')&&(c<='9')) || ((c>='A')&&(c<='Z')) ||
+ ((c>='a')&&(c<='z')) || (c=='_')) {
+ *out++ = c;
+ count++;
+ } else {
+ *out++ = '%';
+ *out++ = hex[(c>>4) & 0xf];
+ *out++ = hex[c & 0xf];
+ count += 3;
+ }
+ }
+ return count;
+}
+
+/*
+ * hfs_mac2triv()
+ *
+ * Given a 'Pascal String' (a string preceded by a length byte) in
+ * the Macintosh character set produce the corresponding filename using
+ * the 'trivial' name-mangling scheme, returning the length of the
+ * mangled filename. Note that the output string is not NULL
+ * terminated.
+ *
+ * The name-mangling works as follows:
+ * The character '/', which is illegal in Linux filenames is replaced
+ * by ':' which never appears in HFS filenames. All other characters
+ * are passed unchanged from input to output.
+ */
+int hfs_mac2triv(char *out, const struct hfs_name *in) {
+ unsigned char c;
+ const unsigned char *p = in->Name;
+ int len = in->Len;
+ int count = 0;
+
+ while (len--) {
+ c = *p++;
+ if (c=='/') {
+ *out++ = ':';
+ } else {
+ *out++ = c;
+ }
+ count++;
+ }
+ return count;
+}
+
+/*
+ * hfs_mac2latin()
+ *
+ * Given a 'Pascal String' (a string preceded by a length byte) in
+ * the Macintosh character set produce the corresponding filename using
+ * the 'Latin-1' name-mangling scheme, returning the length of the
+ * mangled filename. Note that the output string is not NULL
+ * terminated.
+ *
+ * The Macintosh character set and Latin-1 are both extensions of the
+ * ASCII character set. Some, but certainly not all, of the characters
+ * in the Macintosh character set are also in Latin-1 but not with the
+ * same encoding. This name-mangling scheme replaces the characters in
+ * the Macintosh character set that have Latin-1 equivalents by those
+ * equivalents; the characters 32-126, excluding '/' and '%', are
+ * passed unchanged from input to output. The remaining characters
+ * are replaced by three characters: '%xx' where xx is the hexadecimal
+ * representation of the character, using lowercase 'a' through 'f'.
+ *
+ * The array mac2latin_map[] indicates the correspondence between the
+ * two character sets. The byte in element x-128 gives the Latin-1
+ * encoding of the character with encoding x in the Macintosh
+ * character set. A value of zero indicates Latin-1 has no
+ * corresponding character.
+ */
+int hfs_mac2latin(char *out, const struct hfs_name *in) {
+ unsigned char c;
+ const unsigned char *p = in->Name;
+ int len = in->Len;
+ int count = 0;
+
+ while (len--) {
+ c = *p++;
+
+ if ((c & 0x80) && mac2latin_map[c & 0x7f]) {
+ *out++ = mac2latin_map[c & 0x7f];
+ count++;
+ } else if ((c>=32) && (c<=126) && (c!='/') && (c!='%')) {
+ *out++ = c;
+ count++;
+ } else {
+ *out++ = '%';
+ *out++ = hex[(c>>4) & 0xf];
+ *out++ = hex[c & 0xf];
+ count += 3;
+ }
+ }
+ return count;
+}
+
+/*
+ * hfs_colon2mac()
+ *
+ * Given an ASCII string (not null-terminated) and its length,
+ * generate the corresponding filename in the Macintosh character set
+ * using the 'CAP' name-mangling scheme, returning the length of the
+ * mangled filename. Note that the output string is not NULL
+ * terminated.
+ *
+ * This routine is a inverse to hfs_mac2cap() and hfs_mac2nat().
+ * A ':' not followed by a 2-digit hexadecimal number (or followed
+ * by the codes for NULL or ':') is replaced by a '|'.
+ */
+void hfs_colon2mac(struct hfs_name *out, const char *in, int len) {
+ int hi, lo;
+ unsigned char code, c, *count;
+ unsigned char *p = out->Name;
+
+ out->Len = 0;
+ count = &out->Len;
+ while (len-- && (*count < HFS_NAMELEN)) {
+ c = *in++;
+ (*count)++;
+ if (c!=':') {
+ *p++ = c;
+ } else if ((len<2) ||
+ ((hi=dehex(in[0])) & 0xf0) ||
+ ((lo=dehex(in[1])) & 0xf0) ||
+ !(code = (hi << 4) | lo) ||
+ (code == ':')) {
+ *p++ = '|';
+ } else {
+ *p++ = code;
+ len -= 2;
+ in += 2;
+ }
+ }
+}
+
+/*
+ * hfs_prcnt2mac()
+ *
+ * Given an ASCII string (not null-terminated) and its length,
+ * generate the corresponding filename in the Macintosh character set
+ * using Apple's three recommended name-mangling schemes, returning
+ * the length of the mangled filename. Note that the output string is
+ * not NULL terminated.
+ *
+ * This routine is a inverse to hfs_mac2alpha(), hfs_mac2seven() and
+ * hfs_mac2eight().
+ * A '%' not followed by a 2-digit hexadecimal number (or followed
+ * by the code for NULL or ':') is unchanged.
+ * A ':' is replaced by a '|'.
+ */
+void hfs_prcnt2mac(struct hfs_name *out, const char *in, int len) {
+ int hi, lo;
+ unsigned char code, c, *count;
+ unsigned char *p = out->Name;
+
+ out->Len = 0;
+ count = &out->Len;
+ while (len-- && (*count < HFS_NAMELEN)) {
+ c = *in++;
+ (*count)++;
+ if (c==':') {
+ *p++ = '|';
+ } else if (c!='%') {
+ *p++ = c;
+ } else if ((len<2) ||
+ ((hi=dehex(in[0])) & 0xf0) ||
+ ((lo=dehex(in[1])) & 0xf0) ||
+ !(code = (hi << 4) | lo) ||
+ (code == ':')) {
+ *p++ = '%';
+ } else {
+ *p++ = code;
+ len -= 2;
+ in += 2;
+ }
+ }
+}
+
+/*
+ * hfs_triv2mac()
+ *
+ * Given an ASCII string (not null-terminated) and its length,
+ * generate the corresponding filename in the Macintosh character set
+ * using the 'trivial' name-mangling scheme, returning the length of
+ * the mangled filename. Note that the output string is not NULL
+ * terminated.
+ *
+ * This routine is a inverse to hfs_mac2triv().
+ * A ':' is replaced by a '/'.
+ */
+void hfs_triv2mac(struct hfs_name *out, const char *in, int len) {
+ unsigned char c, *count;
+ unsigned char *p = out->Name;
+
+ out->Len = 0;
+ count = &out->Len;
+ while (len-- && (*count < HFS_NAMELEN)) {
+ c = *in++;
+ (*count)++;
+ if (c==':') {
+ *p++ = '/';
+ } else {
+ *p++ = c;
+ }
+ }
+}
+
+/*
+ * hfs_latin2mac()
+ *
+ * Given an Latin-1 string (not null-terminated) and its length,
+ * generate the corresponding filename in the Macintosh character set
+ * using the 'Latin-1' name-mangling scheme, returning the length of
+ * the mangled filename. Note that the output string is not NULL
+ * terminated.
+ *
+ * This routine is a inverse to hfs_latin2cap().
+ * A '%' not followed by a 2-digit hexadecimal number (or followed
+ * by the code for NULL or ':') is unchanged.
+ * A ':' is replaced by a '|'.
+ *
+ * Note that the character map is built the first time it is needed.
+ */
+void hfs_latin2mac(struct hfs_name *out, const char *in, int len)
+{
+ int hi, lo;
+ unsigned char code, c, *count;
+ unsigned char *p = out->Name;
+ static int map_initialized;
+
+ if (!map_initialized) {
+ int i;
+
+ /* build the inverse mapping at run time */
+ for (i = 0; i < 128; i++) {
+ if ((c = mac2latin_map[i])) {
+ latin2mac_map[(int)c - 128] = i + 128;
+ }
+ }
+ map_initialized = 1;
+ }
+
+ out->Len = 0;
+ count = &out->Len;
+ while (len-- && (*count < HFS_NAMELEN)) {
+ c = *in++;
+ (*count)++;
+
+ if (c==':') {
+ *p++ = '|';
+ } else if (c!='%') {
+ if (c<128 || !(*p = latin2mac_map[c-128])) {
+ *p = c;
+ }
+ p++;
+ } else if ((len<2) ||
+ ((hi=dehex(in[0])) & 0xf0) ||
+ ((lo=dehex(in[1])) & 0xf0) ||
+ !(code = (hi << 4) | lo) ||
+ (code == ':')) {
+ *p++ = '%';
+ } else {
+ *p++ = code;
+ len -= 2;
+ in += 2;
+ }
+ }
+}
diff --git a/fs/hfs/version.c b/fs/hfs/version.c
new file mode 100644
index 00000000000000..ef0283ec0b6b16
--- /dev/null
+++ b/fs/hfs/version.c
@@ -0,0 +1,10 @@
+/*
+ * linux/fs/hfs/version.c
+ *
+ * Copyright (C) 1995-1997 Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU General Public License.
+ *
+ * This file contains the version string for this release.
+ */
+
+const char hfs_version[]="0.96";
diff --git a/fs/hfsplus/Makefile b/fs/hfsplus/Makefile
new file mode 100644
index 00000000000000..2b23d1cec4c50e
--- /dev/null
+++ b/fs/hfsplus/Makefile
@@ -0,0 +1,17 @@
+#
+# Makefile for the linux hfsplus filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+O_TARGET := hfsplus.o
+
+obj-y := super.o options.o inode.o extents.o catalog.o dir.o btree.o \
+ bnode.o brec.o bfind.o tables.o unicode.o wrapper.o
+
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/hfsplus/README b/fs/hfsplus/README
new file mode 100644
index 00000000000000..f6e02fba45b619
--- /dev/null
+++ b/fs/hfsplus/README
@@ -0,0 +1,33 @@
+ Copyright (C) 2001 Brad Boyer
+ Copyright (C) 2003 Ardis Technologies bv
+
+ This program 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 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+HFS+ is the standard volume format for Mac OS X.
+
+A basic overview and also a detailed description of the HFS+ volume format
+is provided by Apple's technical note 1150.
+
+The new driver provides full access to HFS+ plus volumes under Linux. It
+bases on the driver by Brad Boyer at
+http://sourceforge.net/projects/linux-hfsplus.
+
+This driver now supports full read and write access and has a better
+perfomance. It also supports hard links and the resource fork is accessible
+via /rsrc.
+
+This is a beta release, it was intensively tested, but use at your own risk.
+
diff --git a/fs/hfsplus/bfind.c b/fs/hfsplus/bfind.c
new file mode 100644
index 00000000000000..6dcceec90e5cc2
--- /dev/null
+++ b/fs/hfsplus/bfind.c
@@ -0,0 +1,213 @@
+/*
+ * linux/fs/hfsplus/bfind.c
+ *
+ * Copyright (C) 2001
+ * Brad Boyer (flar@allandria.com)
+ * (C) 2003 Ardis Technologies <roman@ardistech.com>
+ *
+ * Search routines for btrees
+ */
+
+#include <linux/slab.h>
+#include "hfsplus_fs.h"
+
+/* Find the record in bnode that best matches key (not greater than...)*/
+void hfsplus_find_rec(hfsplus_bnode *bnode, struct hfsplus_find_data *fd)
+{
+ int cmpval;
+ u16 off, len, keylen;
+ int rec;
+ int b, e;
+
+ b = 0;
+ e = bnode->num_recs - 1;
+ do {
+ rec = (e + b) / 2;
+ len = hfsplus_brec_lenoff(bnode, rec, &off);
+ keylen = hfsplus_brec_keylen(bnode, rec);
+ hfsplus_bnode_readbytes(bnode, fd->key, off, keylen);
+ cmpval = bnode->tree->keycmp(fd->key, fd->search_key);
+ if (!cmpval) {
+ fd->exact = 1;
+ e = rec;
+ break;
+ }
+ if (cmpval < 0)
+ b = rec + 1;
+ else
+ e = rec - 1;
+ } while (b <= e);
+ //printk("%d: %d,%d,%d\n", bnode->this, b, e, rec);
+ if (rec != e && e >= 0) {
+ len = hfsplus_brec_lenoff(bnode, e, &off);
+ keylen = hfsplus_brec_keylen(bnode, e);
+ hfsplus_bnode_readbytes(bnode, fd->key, off, keylen);
+ }
+ fd->record = e;
+ fd->keyoffset = off;
+ fd->keylength = keylen;
+ fd->entryoffset = off + keylen;
+ fd->entrylength = len - keylen;
+}
+
+/* Traverse a B*Tree from the root to a leaf finding best fit to key */
+/* Return allocated copy of node found, set recnum to best record */
+int hfsplus_btree_find(struct hfsplus_find_data *fd)
+{
+ hfsplus_btree *tree;
+ hfsplus_bnode *bnode;
+ u32 data, nidx, parent;
+ int height, err;
+
+ tree = fd->tree;
+ if (fd->bnode)
+ hfsplus_put_bnode(fd->bnode);
+ fd->bnode = NULL;
+ fd->exact = 0;
+ nidx = tree->root;
+ if (!nidx)
+ return -ENOENT;
+ height = tree->depth;
+ err = 0;
+ parent = 0;
+ for (;;) {
+ bnode = hfsplus_find_bnode(tree, nidx);
+ if (!bnode) {
+ err = -EIO;
+ break;
+ }
+ if (bnode->height != height)
+ goto invalid;
+ if (bnode->kind != (--height ? HFSPLUS_NODE_NDX : HFSPLUS_NODE_LEAF))
+ goto invalid;
+ bnode->parent = parent;
+
+ hfsplus_find_rec(bnode, fd);
+ if (fd->record < 0) {
+ err = -ENOENT;
+ goto release;
+ }
+ if (!height) {
+ if (!fd->exact)
+ err = -ENOENT;
+ break;
+ }
+
+ parent = nidx;
+ hfsplus_bnode_readbytes(bnode, &data, fd->entryoffset, 4);
+ nidx = be32_to_cpu(data);
+ hfsplus_put_bnode(bnode);
+ }
+ fd->bnode = bnode;
+ return err;
+
+invalid:
+ printk("HFS+-fs: inconsistency in B*Tree\n");
+ err = -EIO;
+release:
+ hfsplus_put_bnode(bnode);
+ return err;
+}
+
+int hfsplus_btree_find_entry(struct hfsplus_find_data *fd,
+ void *entry, int entry_len)
+{
+ int res;
+
+ res = hfsplus_btree_find(fd);
+ if (res)
+ return res;
+ if (fd->entrylength > entry_len)
+ return -EINVAL;
+ hfsplus_bnode_readbytes(fd->bnode, entry, fd->entryoffset, fd->entrylength);
+ return 0;
+}
+
+int hfsplus_btree_move(struct hfsplus_find_data *fd, int cnt)
+{
+ struct hfsplus_btree *tree;
+ hfsplus_bnode *bnode;
+ int idx, res = 0;
+ u16 off, len, keylen;
+
+ bnode = fd->bnode;
+ tree = bnode->tree;
+
+ if (cnt < -0xFFFF || cnt > 0xFFFF)
+ return -EINVAL;
+
+ if (cnt < 0) {
+ cnt = -cnt;
+ while (cnt > fd->record) {
+ cnt -= fd->record + 1;
+ fd->record = bnode->num_recs - 1;
+ idx = bnode->prev;
+ if (!idx) {
+ res = -ENOENT;
+ goto out;
+ }
+ hfsplus_put_bnode(bnode);
+ bnode = hfsplus_find_bnode(tree, idx);
+ if (!bnode) {
+ res = -EIO;
+ goto out;
+ }
+ }
+ fd->record -= cnt;
+ } else {
+ while (cnt >= bnode->num_recs - fd->record) {
+ cnt -= bnode->num_recs - fd->record;
+ fd->record = 0;
+ idx = bnode->next;
+ if (!idx) {
+ res = -ENOENT;
+ goto out;
+ }
+ hfsplus_put_bnode(bnode);
+ bnode = hfsplus_find_bnode(tree, idx);
+ if (!bnode) {
+ res = -EIO;
+ goto out;
+ }
+ }
+ fd->record += cnt;
+ }
+
+ len = hfsplus_brec_lenoff(bnode, fd->record, &off);
+ keylen = hfsplus_brec_keylen(bnode, fd->record);
+ fd->keyoffset = off;
+ fd->keylength = keylen;
+ fd->entryoffset = off + keylen;
+ fd->entrylength = len - keylen;
+ hfsplus_bnode_readbytes(bnode, fd->key, off, keylen);
+out:
+ fd->bnode = bnode;
+ return res;
+}
+
+int hfsplus_find_init(hfsplus_btree *tree, struct hfsplus_find_data *fd)
+{
+ fd->tree = tree;
+ fd->bnode = NULL;
+ fd->search_key = kmalloc(tree->max_key_len + 2, GFP_KERNEL);
+ if (!fd->search_key)
+ return -ENOMEM;
+ fd->key = kmalloc(tree->max_key_len + 2, GFP_KERNEL);
+ if (!fd->key) {
+ kfree(fd->search_key);
+ return -ENOMEM;
+ }
+ dprint(DBG_BNODE_REFS, "find_init: %d (%p)\n", tree->cnid, __builtin_return_address(0));
+ down(&tree->tree_lock);
+ return 0;
+}
+
+void hfsplus_find_exit(struct hfsplus_find_data *fd)
+{
+ hfsplus_put_bnode(fd->bnode);
+ kfree(fd->search_key);
+ kfree(fd->key);
+ dprint(DBG_BNODE_REFS, "find_exit: %d (%p)\n", fd->tree->cnid, __builtin_return_address(0));
+ up(&fd->tree->tree_lock);
+ fd->tree = NULL;
+}
diff --git a/fs/hfsplus/bnode.c b/fs/hfsplus/bnode.c
new file mode 100644
index 00000000000000..12a77afb57a24b
--- /dev/null
+++ b/fs/hfsplus/bnode.c
@@ -0,0 +1,1031 @@
+/*
+ * linux/fs/hfsplus/bnode.c
+ *
+ * Copyright (C) 2001
+ * Brad Boyer (flar@allandria.com)
+ * (C) 2003 Ardis Technologies <roman@ardistech.com>
+ *
+ * Handle basic btree node operations
+ */
+
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/fs.h>
+#include <linux/swap.h>
+#include <linux/version.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+#include <linux/buffer_head.h>
+#endif
+
+#include "hfsplus_fs.h"
+#include "hfsplus_raw.h"
+
+#define REF_PAGES 0
+
+int hfsplus_update_idx_rec(struct hfsplus_find_data *fd);
+
+/* Get the given block associated with the tree owning node */
+struct buffer_head *hfsplus_getblk(struct inode *inode, unsigned long n)
+{
+ struct super_block *sb;
+ struct buffer_head tmp_bh;
+
+ sb = inode->i_sb;
+ if (hfsplus_get_block(inode, n, &tmp_bh, 1)) {
+ printk("HFS+-fs: Failed to find block for B*Tree data\n");
+ return NULL;
+ }
+ return sb_bread(sb, tmp_bh.b_blocknr);
+}
+
+/* Copy a specified range of bytes from the raw data of a node */
+void hfsplus_bnode_readbytes(hfsplus_bnode *node, void *buf,
+ unsigned long off, unsigned long len)
+{
+ unsigned long l;
+ struct page **pagep;
+
+ off += node->page_offset;
+ pagep = node->page + (off >> PAGE_CACHE_SHIFT);
+ off &= ~PAGE_CACHE_MASK;
+
+ l = min(len, PAGE_CACHE_SIZE - off);
+ memcpy(buf, hfsplus_kmap(*pagep) + off, l);
+ hfsplus_kunmap(*pagep++);
+
+ while ((len -= l)) {
+ buf += l;
+ l = min(len, PAGE_CACHE_SIZE);
+ memcpy(buf, hfsplus_kmap(*pagep), l);
+ hfsplus_kunmap(*pagep++);
+ }
+}
+
+u16 hfsplus_bnode_read_u16(hfsplus_bnode *node, unsigned long off)
+{
+ u16 data;
+ // optimize later...
+ hfsplus_bnode_readbytes(node, &data, off, 2);
+ return be16_to_cpu(data);
+}
+
+void hfsplus_bnode_read_key(hfsplus_bnode *node, void *key, unsigned long off)
+{
+ hfsplus_btree *tree;
+ unsigned long key_len;
+
+ tree = node->tree;
+ if (node->kind == HFSPLUS_NODE_LEAF ||
+ tree->attributes & HFSPLUS_TREE_VAR_NDXKEY_SIZE)
+ key_len = hfsplus_bnode_read_u16(node, off) + 2;
+ else
+ key_len = tree->max_key_len + 2;
+
+ hfsplus_bnode_readbytes(node, key, off, key_len);
+}
+
+void hfsplus_bnode_writebytes(hfsplus_bnode *node, void *buf,
+ unsigned long off, unsigned long len)
+{
+ unsigned long l;
+ struct page **pagep;
+
+ off += node->page_offset;
+ pagep = node->page + (off >> PAGE_CACHE_SHIFT);
+ off &= ~PAGE_CACHE_MASK;
+
+ l = min(len, PAGE_CACHE_SIZE - off);
+ memcpy(hfsplus_kmap(*pagep) + off, buf, l);
+ set_page_dirty(*pagep);
+ hfsplus_kunmap(*pagep++);
+
+ while ((len -= l)) {
+ buf += l;
+ l = min(len, PAGE_CACHE_SIZE);
+ memcpy(hfsplus_kmap(*pagep), buf, l);
+ set_page_dirty(*pagep);
+ hfsplus_kunmap(*pagep++);
+ }
+}
+
+void hfsplus_bnode_write_u16(hfsplus_bnode *node, unsigned long off, u16 data)
+{
+ data = cpu_to_be16(data);
+ // optimize later...
+ hfsplus_bnode_writebytes(node, &data, off, 2);
+}
+
+void hfsplus_bnode_copybytes(hfsplus_bnode *dst_node, unsigned long dst,
+ hfsplus_bnode *src_node, unsigned long src, unsigned long len)
+{
+ struct hfsplus_btree *tree;
+ struct page **src_page, **dst_page;
+ unsigned long l;
+
+ dprint(DBG_BNODE_MOD, "copybytes: %lu,%lu,%lu\n", dst, src, len);
+ if (!len)
+ return;
+ tree = src_node->tree;
+ src += src_node->page_offset;
+ dst += dst_node->page_offset;
+ src_page = src_node->page + (src >> PAGE_CACHE_SHIFT);
+ src &= ~PAGE_CACHE_MASK;
+ dst_page = dst_node->page + (dst >> PAGE_CACHE_SHIFT);
+ dst &= ~PAGE_CACHE_MASK;
+
+ if (src == dst) {
+ l = min(len, PAGE_CACHE_SIZE - src);
+ memcpy(hfsplus_kmap(*dst_page) + src, hfsplus_kmap(*src_page) + src, l);
+ hfsplus_kunmap(*src_page++);
+ set_page_dirty(*dst_page);
+ hfsplus_kunmap(*dst_page++);
+
+ while ((len -= l)) {
+ l = min(len, PAGE_CACHE_SIZE);
+ memcpy(hfsplus_kmap(*dst_page), hfsplus_kmap(*src_page), l);
+ hfsplus_kunmap(*src_page++);
+ set_page_dirty(*dst_page);
+ hfsplus_kunmap(*dst_page++);
+ }
+ } else {
+ void *src_ptr, *dst_ptr;
+
+ do {
+ src_ptr = hfsplus_kmap(*src_page) + src;
+ dst_ptr = hfsplus_kmap(*dst_page) + dst;
+ if (PAGE_CACHE_SIZE - src < PAGE_CACHE_SIZE - dst) {
+ l = PAGE_CACHE_SIZE - src;
+ src = 0;
+ dst += l;
+ } else {
+ l = PAGE_CACHE_SIZE - dst;
+ src += l;
+ dst = 0;
+ }
+ l = min(len, l);
+ memcpy(dst_ptr, src_ptr, l);
+ hfsplus_kunmap(*src_page);
+ set_page_dirty(*dst_page);
+ hfsplus_kunmap(*dst_page);
+ if (!dst)
+ dst_page++;
+ else
+ src_page++;
+ } while ((len -= l));
+ }
+}
+
+void hfsplus_bnode_movebytes(hfsplus_bnode *node, unsigned long dst,
+ unsigned long src, unsigned long len)
+{
+ struct page **src_page, **dst_page;
+ unsigned long l;
+
+ dprint(DBG_BNODE_MOD, "movebytes: %lu,%lu,%lu\n", dst, src, len);
+ if (!len)
+ return;
+ src += node->page_offset;
+ dst += node->page_offset;
+ if (dst > src) {
+ src += len - 1;
+ src_page = node->page + (src >> PAGE_CACHE_SHIFT);
+ src = (src & ~PAGE_CACHE_MASK) + 1;
+ dst += len - 1;
+ dst_page = node->page + (dst >> PAGE_CACHE_SHIFT);
+ dst = (dst & ~PAGE_CACHE_MASK) + 1;
+
+ if (src == dst) {
+ while (src < len) {
+ memmove(hfsplus_kmap(*dst_page), hfsplus_kmap(*src_page), src);
+ hfsplus_kunmap(*src_page--);
+ set_page_dirty(*dst_page);
+ hfsplus_kunmap(*dst_page--);
+ len -= src;
+ src = PAGE_CACHE_SIZE;
+ }
+ src -= len;
+ memmove(hfsplus_kmap(*dst_page) + src, hfsplus_kmap(*src_page) + src, len);
+ hfsplus_kunmap(*src_page);
+ set_page_dirty(*dst_page);
+ hfsplus_kunmap(*dst_page);
+ } else {
+ void *src_ptr, *dst_ptr;
+
+ do {
+ src_ptr = hfsplus_kmap(*src_page) + src;
+ dst_ptr = hfsplus_kmap(*dst_page) + dst;
+ if (src < dst) {
+ l = src;
+ src = PAGE_CACHE_SIZE;
+ dst -= l;
+ } else {
+ l = dst;
+ src -= l;
+ dst = PAGE_CACHE_SIZE;
+ }
+ l = min(len, l);
+ memmove(dst_ptr - l, src_ptr - l, l);
+ hfsplus_kunmap(*src_page);
+ set_page_dirty(*dst_page);
+ hfsplus_kunmap(*dst_page);
+ if (dst == PAGE_CACHE_SIZE)
+ dst_page--;
+ else
+ src_page--;
+ } while ((len -= l));
+ }
+ } else {
+ src_page = node->page + (src >> PAGE_CACHE_SHIFT);
+ src &= ~PAGE_CACHE_MASK;
+ dst_page = node->page + (dst >> PAGE_CACHE_SHIFT);
+ dst &= ~PAGE_CACHE_MASK;
+
+ if (src == dst) {
+ l = min(len, PAGE_CACHE_SIZE - src);
+ memmove(hfsplus_kmap(*dst_page) + src, hfsplus_kmap(*src_page) + src, l);
+ hfsplus_kunmap(*src_page++);
+ set_page_dirty(*dst_page);
+ hfsplus_kunmap(*dst_page++);
+
+ while ((len -= l)) {
+ l = min(len, PAGE_CACHE_SIZE);
+ memmove(hfsplus_kmap(*dst_page), hfsplus_kmap(*src_page), l);
+ hfsplus_kunmap(*src_page++);
+ set_page_dirty(*dst_page);
+ hfsplus_kunmap(*dst_page++);
+ }
+ } else {
+ void *src_ptr, *dst_ptr;
+
+ do {
+ src_ptr = hfsplus_kmap(*src_page) + src;
+ dst_ptr = hfsplus_kmap(*dst_page) + dst;
+ if (PAGE_CACHE_SIZE - src < PAGE_CACHE_SIZE - dst) {
+ l = PAGE_CACHE_SIZE - src;
+ src = 0;
+ dst += l;
+ } else {
+ l = PAGE_CACHE_SIZE - dst;
+ src += l;
+ dst = 0;
+ }
+ l = min(len, l);
+ memmove(dst_ptr, src_ptr, l);
+ hfsplus_kunmap(*src_page);
+ set_page_dirty(*dst_page);
+ hfsplus_kunmap(*dst_page);
+ if (!dst)
+ dst_page++;
+ else
+ src_page++;
+ } while ((len -= l));
+ }
+ }
+}
+
+void hfsplus_bnode_dump(hfsplus_bnode *node)
+{
+ hfsplus_btree_node_desc desc;
+ u32 cnid;
+ int i, off, key_off;
+
+ dprint(DBG_BNODE_MOD, "bnode: %d\n", node->this);
+ hfsplus_bnode_readbytes(node, &desc, 0, sizeof(desc));
+ dprint(DBG_BNODE_MOD, "%d, %d, %d, %d, %d\n",
+ be32_to_cpu(desc.next), be32_to_cpu(desc.prev),
+ desc.kind, desc.height, be16_to_cpu(desc.num_rec));
+
+ off = node->tree->node_size - 2;
+ for (i = be16_to_cpu(desc.num_rec); i >= 0; off -= 2, i--) {
+ key_off = hfsplus_bnode_read_u16(node, off);
+ dprint(DBG_BNODE_MOD, " %d", key_off);
+ if (i && node->kind == HFSPLUS_NODE_NDX) {
+ int tmp;
+
+ tmp = hfsplus_bnode_read_u16(node, key_off);
+ dprint(DBG_BNODE_MOD, " (%d", tmp);
+ hfsplus_bnode_readbytes(node, &cnid, key_off + 2 + tmp, 4);
+ dprint(DBG_BNODE_MOD, ",%d)", be32_to_cpu(cnid));
+ }
+ }
+ dprint(DBG_BNODE_MOD, "\n");
+}
+
+int hfsplus_btree_add_level(hfsplus_btree *tree)
+{
+ hfsplus_bnode *node, *new_node;
+ hfsplus_btree_node_desc node_desc;
+ int key_size, rec;
+ u32 cnid;
+
+ node = NULL;
+ if (tree->root)
+ node = hfsplus_find_bnode(tree, tree->root);
+ new_node = hfsplus_btree_alloc_node(tree);
+ if (IS_ERR(new_node))
+ return PTR_ERR(new_node);
+
+ tree->root = new_node->this;
+ if (!tree->depth) {
+ tree->leaf_head = tree->leaf_tail = new_node->this;
+ new_node->kind = HFSPLUS_NODE_LEAF;
+ new_node->num_recs = 0;
+ } else {
+ new_node->kind = HFSPLUS_NODE_NDX;
+ new_node->num_recs = 1;
+ }
+ new_node->parent = 0;
+ new_node->next = 0;
+ new_node->prev = 0;
+ new_node->height = ++tree->depth;
+
+ node_desc.next = cpu_to_be32(new_node->next);
+ node_desc.prev = cpu_to_be32(new_node->prev);
+ node_desc.kind = new_node->kind;
+ node_desc.height = new_node->height;
+ node_desc.num_rec = cpu_to_be16(new_node->num_recs);
+ node_desc.reserved = 0;
+ hfsplus_bnode_writebytes(new_node, &node_desc, 0, sizeof(node_desc));
+
+ rec = tree->node_size - 2;
+ hfsplus_bnode_write_u16(new_node, rec, 14);
+
+ if (node) {
+ /* insert old root idx into new root */
+ node->parent = tree->root;
+ key_size = hfsplus_bnode_read_u16(node, 14) + 2;
+ // key_size if index node
+ hfsplus_bnode_copybytes(new_node, 14, node, 14, key_size);
+ cnid = cpu_to_be32(node->this);
+ hfsplus_bnode_writebytes(new_node, &cnid, 14 + key_size, 4);
+
+ rec -= 2;
+ hfsplus_bnode_write_u16(new_node, rec, 14 + key_size + 4);
+
+ hfsplus_put_bnode(node);
+ }
+ hfsplus_put_bnode(new_node);
+ mark_inode_dirty(tree->inode);
+
+ return 0;
+}
+
+hfsplus_bnode *hfsplus_bnode_split(struct hfsplus_find_data *fd)
+{
+ hfsplus_btree *tree;
+ hfsplus_bnode *node, *new_node;
+ hfsplus_btree_node_desc node_desc;
+ int num_recs, new_rec_off, new_off, old_rec_off;
+ int data_start, data_end, size;
+
+ tree = fd->tree;
+ node = fd->bnode;
+ new_node = hfsplus_btree_alloc_node(tree);
+ if (IS_ERR(new_node))
+ return new_node;
+ hfsplus_get_bnode(node);
+ dprint(DBG_BNODE_MOD, "split_nodes: %d - %d - %d\n",
+ node->this, new_node->this, node->next);
+ new_node->next = node->next;
+ new_node->prev = node->this;
+ new_node->parent = node->parent;
+ new_node->kind = node->kind;
+ new_node->height = node->height;
+
+ size = tree->node_size / 2;
+ old_rec_off = tree->node_size - 4;
+ num_recs = 1;
+ for (;;) {
+ data_start = hfsplus_bnode_read_u16(node, old_rec_off);
+ if (data_start > size)
+ break;
+ old_rec_off -= 2;
+ num_recs++;
+ if (num_recs < node->num_recs)
+ continue;
+ /* panic? */
+ hfsplus_put_bnode(node);
+ hfsplus_put_bnode(new_node);
+ return NULL;
+ }
+
+ if (fd->record + 1 < num_recs) {
+ /* new record is in the lower half,
+ * so leave some more space there
+ */
+ old_rec_off += 2;
+ num_recs--;
+ data_start = hfsplus_bnode_read_u16(node, old_rec_off);
+ } else {
+ hfsplus_put_bnode(node);
+ hfsplus_get_bnode(new_node);
+ fd->bnode = new_node;
+ fd->record -= num_recs;
+ fd->keyoffset -= data_start;
+ fd->entryoffset -= data_start;
+ }
+ new_node->num_recs = node->num_recs - num_recs;
+ node->num_recs = num_recs;
+
+ new_rec_off = tree->node_size - 2;
+ new_off = 14;
+ size = data_start - new_off;
+ num_recs = new_node->num_recs;
+ data_end = data_start;
+ while (num_recs) {
+ hfsplus_bnode_write_u16(new_node, new_rec_off, new_off);
+ old_rec_off -= 2;
+ new_rec_off -= 2;
+ data_end = hfsplus_bnode_read_u16(node, old_rec_off);
+ new_off = data_end - size;
+ num_recs--;
+ }
+ hfsplus_bnode_write_u16(new_node, new_rec_off, new_off);
+ hfsplus_bnode_copybytes(new_node, 14, node, data_start, data_end - data_start);
+
+ /* update new bnode header */
+ node_desc.next = cpu_to_be32(new_node->next);
+ node_desc.prev = cpu_to_be32(new_node->prev);
+ node_desc.kind = new_node->kind;
+ node_desc.height = new_node->height;
+ node_desc.num_rec = cpu_to_be16(new_node->num_recs);
+ node_desc.reserved = 0;
+ hfsplus_bnode_writebytes(new_node, &node_desc, 0, sizeof(node_desc));
+
+ /* update previous bnode header */
+ node->next = new_node->this;
+ hfsplus_bnode_readbytes(node, &node_desc, 0, sizeof(node_desc));
+ node_desc.next = cpu_to_be32(node->next);
+ node_desc.num_rec = cpu_to_be16(node->num_recs);
+ hfsplus_bnode_writebytes(node, &node_desc, 0, sizeof(node_desc));
+
+ /* update next bnode header */
+ if (new_node->next) {
+ hfsplus_bnode *next_node = hfsplus_find_bnode(tree, new_node->next);
+ next_node->prev = new_node->this;
+ hfsplus_bnode_readbytes(next_node, &node_desc, 0, sizeof(node_desc));
+ node_desc.prev = cpu_to_be32(next_node->prev);
+ hfsplus_bnode_writebytes(next_node, &node_desc, 0, sizeof(node_desc));
+ hfsplus_put_bnode(next_node);
+ } else if (node->this == tree->leaf_tail) {
+ /* if there is no next node, this might be the new tail */
+ tree->leaf_tail = new_node->this;
+ mark_inode_dirty(tree->inode);
+ }
+
+ hfsplus_bnode_dump(node);
+ hfsplus_bnode_dump(new_node);
+ hfsplus_put_bnode(node);
+
+ return new_node;
+}
+
+int hfsplus_bnode_insert_rec(struct hfsplus_find_data *fd, void *entry, int entry_len)
+{
+ hfsplus_btree *tree;
+ hfsplus_bnode *node, *new_node;
+ int size, key_len, rec;
+ int data_off, end_off;
+ int idx_rec_off, data_rec_off, end_rec_off;
+ u32 cnid;
+
+ tree = fd->tree;
+ if (!fd->bnode) {
+ if (!tree->root)
+ hfsplus_btree_add_level(tree);
+ fd->bnode = hfsplus_find_bnode(tree, tree->leaf_head);
+ fd->record = -1;
+ }
+ new_node = NULL;
+again:
+ /* new record idx and complete record size */
+ rec = fd->record + 1;
+ key_len = be16_to_cpu(fd->search_key->key_len) + 2;
+ size = key_len + entry_len;
+
+ node = fd->bnode;
+ hfsplus_bnode_dump(node);
+ /* get last offset */
+ end_rec_off = tree->node_size - (node->num_recs + 1) * 2;
+ end_off = hfsplus_bnode_read_u16(node, end_rec_off);
+ end_rec_off -= 2;
+ dprint(DBG_BNODE_MOD, "insert_rec: %d, %d, %d, %d\n", rec, size, end_off, end_rec_off);
+ if (size > end_rec_off - end_off) {
+ if (new_node)
+ panic("not enough room!\n");
+ new_node = hfsplus_bnode_split(fd);
+ if (IS_ERR(new_node))
+ return PTR_ERR(new_node);
+ goto again;
+ }
+ if (node->kind == HFSPLUS_NODE_LEAF) {
+ tree->leaf_count++;
+ mark_inode_dirty(tree->inode);
+ }
+ node->num_recs++;
+ /* write new last offset */
+ hfsplus_bnode_write_u16(node, offsetof(hfsplus_btree_node_desc, num_rec), node->num_recs);
+ hfsplus_bnode_write_u16(node, end_rec_off, end_off + size);
+ data_off = end_off;
+ data_rec_off = end_rec_off + 2;
+ idx_rec_off = tree->node_size - (rec + 1) * 2;
+ if (idx_rec_off == data_rec_off)
+ goto skip;
+ /* move all following entries */
+ do {
+ data_off = hfsplus_bnode_read_u16(node, data_rec_off + 2);
+ hfsplus_bnode_write_u16(node, data_rec_off, data_off + size);
+ data_rec_off += 2;
+ } while (data_rec_off < idx_rec_off);
+
+ /* move data away */
+ hfsplus_bnode_movebytes(node, data_off + size, data_off,
+ end_off - data_off);
+
+skip:
+ hfsplus_bnode_writebytes(node, fd->search_key, data_off, key_len);
+ hfsplus_bnode_writebytes(node, entry, data_off + key_len, entry_len);
+ hfsplus_bnode_dump(node);
+
+ if (new_node) {
+ if (!rec && new_node != node)
+ hfsplus_update_idx_rec(fd);
+
+ hfsplus_put_bnode(fd->bnode);
+ if (!new_node->parent) {
+ hfsplus_btree_add_level(tree);
+ new_node->parent = tree->root;
+ }
+ fd->bnode = hfsplus_find_bnode(tree, new_node->parent);
+
+ /* create index data entry */
+ cnid = cpu_to_be32(new_node->this);
+ entry = &cnid;
+ entry_len = sizeof(cnid);
+
+ /* get index key */
+ hfsplus_bnode_read_key(new_node, fd->search_key, 14);
+ hfsplus_find_rec(fd->bnode, fd);
+
+ hfsplus_put_bnode(new_node);
+ new_node = NULL;
+ goto again;
+ }
+
+ if (!rec)
+ hfsplus_update_idx_rec(fd);
+
+ return 0;
+}
+
+int hfsplus_update_idx_rec(struct hfsplus_find_data *fd)
+{
+ hfsplus_btree *tree;
+ hfsplus_bnode *node, *new_node, *parent;
+ int newkeylen, diff;
+ int rec, rec_off, end_rec_off;
+ int start_off, end_off;
+
+ tree = fd->tree;
+ node = fd->bnode;
+ new_node = NULL;
+ if (!node->parent)
+ return 0;
+
+again:
+ parent = hfsplus_find_bnode(tree, node->parent);
+ hfsplus_find_rec(parent, fd);
+ hfsplus_bnode_dump(parent);
+ rec = fd->record;
+
+ /* size difference between old and new key */
+ newkeylen = hfsplus_bnode_read_u16(node, 14) + 2;
+ dprint(DBG_BNODE_MOD, "update_rec: %d, %d, %d\n", rec, fd->keylength, newkeylen);
+
+ rec_off = tree->node_size - (rec + 2) * 2;
+ end_rec_off = tree->node_size - (parent->num_recs + 1) * 2;
+ diff = newkeylen - fd->keylength;
+ if (!diff)
+ goto skip;
+ if (diff > 0) {
+ end_off = hfsplus_bnode_read_u16(parent, end_rec_off);
+ if (end_rec_off - end_off < diff) {
+
+ printk("splitting index node...\n");
+ fd->bnode = parent;
+ new_node = hfsplus_bnode_split(fd);
+ if (IS_ERR(new_node))
+ return PTR_ERR(new_node);
+ parent = fd->bnode;
+ rec = fd->record;
+ rec_off = tree->node_size - (rec + 2) * 2;
+ end_rec_off = tree->node_size - (parent->num_recs + 1) * 2;
+ }
+ }
+
+ end_off = start_off = hfsplus_bnode_read_u16(parent, rec_off);
+ hfsplus_bnode_write_u16(parent, rec_off, start_off + diff);
+ start_off -= 4; /* move previous cnid too */
+
+ while (rec_off > end_rec_off) {
+ rec_off -= 2;
+ end_off = hfsplus_bnode_read_u16(parent, rec_off);
+ hfsplus_bnode_write_u16(parent, rec_off, end_off + diff);
+ }
+ hfsplus_bnode_movebytes(parent, start_off + diff, start_off,
+ end_off - start_off);
+skip:
+ hfsplus_bnode_copybytes(parent, fd->keyoffset, node, 14, newkeylen);
+ hfsplus_bnode_dump(parent);
+
+ hfsplus_put_bnode(node);
+ node = parent;
+
+ if (new_node) {
+ u32 cnid;
+
+ fd->bnode = hfsplus_find_bnode(tree, new_node->parent);
+ /* create index key and entry */
+ hfsplus_bnode_read_key(new_node, fd->search_key, 14);
+ cnid = cpu_to_be32(new_node->this);
+
+ hfsplus_find_rec(fd->bnode, fd);
+ hfsplus_bnode_insert_rec(fd, &cnid, sizeof(cnid));
+ hfsplus_put_bnode(fd->bnode);
+ hfsplus_put_bnode(new_node);
+
+ if (!rec) {
+ if (new_node == node)
+ goto out;
+ /* restore search_key */
+ hfsplus_bnode_read_key(node, fd->search_key, 14);
+ }
+ }
+
+ if (!rec && node->parent)
+ goto again;
+out:
+ fd->bnode = node;
+ return 0;
+}
+
+int hfsplus_bnode_remove_rec(struct hfsplus_find_data *fd)
+{
+ hfsplus_btree *tree;
+ hfsplus_bnode *node, *parent;
+ int end_off, rec_off, data_off, size;
+
+ tree = fd->tree;
+ node = fd->bnode;
+again:
+ rec_off = tree->node_size - (fd->record + 2) * 2;
+ end_off = tree->node_size - (node->num_recs + 1) * 2;
+
+ if (node->kind == HFSPLUS_NODE_LEAF) {
+ tree->leaf_count--;
+ mark_inode_dirty(tree->inode);
+ }
+ hfsplus_bnode_dump(node);
+ dprint(DBG_BNODE_MOD, "remove_rec: %d, %d\n", fd->record, fd->keylength + fd->entrylength);
+ if (!--node->num_recs) {
+ hfsplus_btree_remove_node(node);
+ if (!node->parent)
+ return 0;
+ parent = hfsplus_find_bnode(tree, node->parent);
+ if (!parent)
+ return -EIO;
+ hfsplus_put_bnode(node);
+ node = fd->bnode = parent;
+
+ hfsplus_find_rec(node, fd);
+ goto again;
+ }
+ hfsplus_bnode_write_u16(node, offsetof(hfsplus_btree_node_desc, num_rec), node->num_recs);
+
+ if (rec_off == end_off)
+ goto skip;
+ size = fd->keylength + fd->entrylength;
+
+ do {
+ data_off = hfsplus_bnode_read_u16(node, rec_off);
+ hfsplus_bnode_write_u16(node, rec_off + 2, data_off - size);
+ rec_off -= 2;
+ } while (rec_off >= end_off);
+
+ /* fill hole */
+ hfsplus_bnode_movebytes(node, fd->keyoffset, fd->keyoffset + size,
+ data_off - fd->keyoffset - size);
+skip:
+ hfsplus_bnode_dump(node);
+ if (!fd->record)
+ hfsplus_update_idx_rec(fd);
+ return 0;
+}
+
+/* Check for valid kind/height pairs , return 0 for bad pairings */
+static int hfsplus_check_kh(hfsplus_btree *tree, u8 kind, u8 height)
+{
+ if ((kind == HFSPLUS_NODE_HEAD) || (kind == HFSPLUS_NODE_MAP)) {
+ if (height != 0)
+ goto hk_error;
+ } else if (kind == HFSPLUS_NODE_LEAF) {
+ if (height != 1)
+ goto hk_error;
+ } else if (kind == HFSPLUS_NODE_NDX) {
+ if ((height <= 1) || (height > tree->depth))
+ goto hk_error;
+ } else {
+ printk("HFS+-fs: unknown node type in B*Tree\n");
+ return 0;
+ }
+ return 1;
+ hk_error:
+ printk("HFS+-fs: corrupt node height in B*Tree\n");
+ return 0;
+}
+
+static inline int hfsplus_bnode_hash(u32 num)
+{
+ num = (num >> 16) + num;
+ num += num >> 8;
+ return num & (NODE_HASH_SIZE - 1);
+}
+
+hfsplus_bnode *__hfsplus_find_bnode(hfsplus_btree *tree, u32 cnid)
+{
+ hfsplus_bnode *node;
+
+ if (cnid >= tree->node_count) {
+ printk("HFS+-fs: request for non-existent node %d in B*Tree\n", cnid);
+ return NULL;
+ }
+
+ for (node = tree->node_hash[hfsplus_bnode_hash(cnid)];
+ node; node = node->next_hash) {
+ if (node->this == cnid) {
+ return node;
+ }
+ }
+ return NULL;
+}
+
+hfsplus_bnode *__hfsplus_create_bnode(hfsplus_btree *tree, u32 cnid)
+{
+ struct super_block *sb;
+ hfsplus_bnode *node, *node2;
+ struct address_space *mapping;
+ struct page *page;
+ int size, block, i, hash;
+ loff_t off;
+
+ if (cnid >= tree->node_count) {
+ printk("HFS+-fs: request for non-existent node %d in B*Tree\n", cnid);
+ return NULL;
+ }
+
+ sb = tree->inode->i_sb;
+ size = sizeof(hfsplus_bnode) + tree->pages_per_bnode *
+ sizeof(struct page *);
+ node = kmalloc(size, GFP_KERNEL);
+ if (!node)
+ return NULL;
+ memset(node, 0, size);
+ node->tree = tree;
+ node->this = cnid;
+ set_bit(HFSPLUS_BNODE_NEW, &node->flags);
+ atomic_set(&node->refcnt, 1);
+ dprint(DBG_BNODE_REFS, "new_node(%d:%d): 1\n",
+ node->tree->cnid, node->this);
+ init_waitqueue_head(&node->lock_wq);
+ spin_lock(&tree->hash_lock);
+ node2 = __hfsplus_find_bnode(tree, cnid);
+ if (!node2) {
+ hash = hfsplus_bnode_hash(cnid);
+ node->next_hash = tree->node_hash[hash];
+ tree->node_hash[hash] = node;
+ tree->node_hash_cnt++;
+ } else {
+ spin_unlock(&tree->hash_lock);
+ kfree(node);
+ wait_event(node2->lock_wq, !test_bit(HFSPLUS_BNODE_NEW, &node2->flags));
+ return node2;
+ }
+ spin_unlock(&tree->hash_lock);
+
+ mapping = tree->inode->i_mapping;
+ off = (loff_t)cnid * tree->node_size;
+ block = off >> PAGE_CACHE_SHIFT;
+ node->page_offset = off & ~PAGE_CACHE_MASK;
+ for (i = 0; i < tree->pages_per_bnode; i++) {
+ page = grab_cache_page(mapping, block++);
+ if (!page)
+ goto fail;
+ if (!PageUptodate(page)) {
+ if (mapping->a_ops->readpage(NULL, page))
+ goto fail;
+ wait_on_page_locked(page);
+ if (!PageUptodate(page))
+ goto fail;
+ } else
+ unlock_page(page);
+#if !REF_PAGES
+ page_cache_release(page);
+#endif
+ node->page[i] = page;
+ }
+
+ return node;
+fail:
+ if (page)
+ page_cache_release(page);
+ set_bit(HFSPLUS_BNODE_ERROR, &node->flags);
+ return node;
+}
+
+void __hfsplus_bnode_remove(hfsplus_bnode *node)
+{
+ hfsplus_bnode **p;
+
+ dprint(DBG_BNODE_REFS, "remove_node(%d:%d): %d\n",
+ node->tree->cnid, node->this, atomic_read(&node->refcnt));
+ for (p = &node->tree->node_hash[hfsplus_bnode_hash(node->this)];
+ *p && *p != node; p = &(*p)->next_hash)
+ ;
+ if (!*p)
+ BUG();
+ *p = node->next_hash;
+ node->tree->node_hash_cnt--;
+}
+
+/* Load a particular node out of a tree */
+hfsplus_bnode *hfsplus_find_bnode(hfsplus_btree *tree, u32 num)
+{
+ hfsplus_bnode *node;
+ hfsplus_btree_node_desc *desc;
+ int i, rec_off, off, next_off;
+ int entry_size, key_size;
+
+ spin_lock(&tree->hash_lock);
+ node = __hfsplus_find_bnode(tree, num);
+ if (node) {
+ hfsplus_get_bnode(node);
+ spin_unlock(&tree->hash_lock);
+ wait_event(node->lock_wq, !test_bit(HFSPLUS_BNODE_NEW, &node->flags));
+ return node;
+ }
+ spin_unlock(&tree->hash_lock);
+ node = __hfsplus_create_bnode(tree, num);
+ if (!node)
+ return ERR_PTR(-ENOMEM);
+ if (!test_bit(HFSPLUS_BNODE_NEW, &node->flags))
+ return node;
+
+ desc = (hfsplus_btree_node_desc *)(hfsplus_kmap(node->page[0]) + node->page_offset);
+ node->prev = be32_to_cpu(desc->prev);
+ node->next = be32_to_cpu(desc->next);
+ node->num_recs = be16_to_cpu(desc->num_rec);
+ node->kind = desc->kind;
+ node->height = desc->height;
+
+ if (!hfsplus_check_kh(tree, desc->kind, desc->height)) {
+ hfsplus_kunmap(node->page[0]);
+ goto node_error;
+ }
+ hfsplus_kunmap(node->page[0]);
+
+ rec_off = tree->node_size - 2;
+ off = hfsplus_bnode_read_u16(node, rec_off);
+ if (off != sizeof(hfsplus_btree_node_desc))
+ goto node_error;
+ for (i = 1; i <= node->num_recs; off = next_off, i++) {
+ rec_off -= 2;
+ next_off = hfsplus_bnode_read_u16(node, rec_off);
+ if (next_off <= off ||
+ next_off > tree->node_size ||
+ next_off & 1)
+ goto node_error;
+ entry_size = next_off - off;
+ if (node->kind != HFSPLUS_NODE_NDX &&
+ node->kind != HFSPLUS_NODE_LEAF)
+ continue;
+ key_size = hfsplus_bnode_read_u16(node, off) + 2;
+ if (key_size >= entry_size || key_size & 1)
+ goto node_error;
+ }
+ clear_bit(HFSPLUS_BNODE_NEW, &node->flags);
+ wake_up(&node->lock_wq);
+ return node;
+
+node_error:
+ set_bit(HFSPLUS_BNODE_ERROR, &node->flags);
+ clear_bit(HFSPLUS_BNODE_NEW, &node->flags);
+ wake_up(&node->lock_wq);
+ hfsplus_put_bnode(node);
+ return ERR_PTR(-EIO);
+}
+
+void hfsplus_bnode_free(hfsplus_bnode *node)
+{
+ //int i;
+
+ //for (i = 0; i < node->tree->pages_per_bnode; i++)
+ // if (node->page[i])
+ // page_cache_release(node->page[i]);
+ kfree(node);
+}
+
+hfsplus_bnode *hfsplus_create_bnode(hfsplus_btree *tree, u32 num)
+{
+ hfsplus_bnode *node;
+ struct page **pagep;
+ int i;
+
+ spin_lock(&tree->hash_lock);
+ node = __hfsplus_find_bnode(tree, num);
+ spin_unlock(&tree->hash_lock);
+ if (node)
+ BUG();
+ node = __hfsplus_create_bnode(tree, num);
+ if (!node)
+ return ERR_PTR(-ENOMEM);
+
+ pagep = node->page;
+ memset(hfsplus_kmap(*pagep) + node->page_offset, 0,
+ min((int)PAGE_CACHE_SIZE, (int)tree->node_size));
+ set_page_dirty(*pagep);
+ hfsplus_kunmap(*pagep++);
+ for (i = 1; i < tree->pages_per_bnode; i++) {
+ memset(hfsplus_kmap(*pagep), 0, PAGE_CACHE_SIZE);
+ set_page_dirty(*pagep);
+ hfsplus_kunmap(*pagep++);
+ }
+ clear_bit(HFSPLUS_BNODE_NEW, &node->flags);
+ wake_up(&node->lock_wq);
+
+ return node;
+}
+
+void hfsplus_get_bnode(hfsplus_bnode *node)
+{
+ if (node) {
+ atomic_inc(&node->refcnt);
+#if REF_PAGES
+ {
+ int i;
+ for (i = 0; i < node->tree->pages_per_bnode; i++)
+ get_page(node->page[i]);
+ }
+#endif
+ dprint(DBG_BNODE_REFS, "get_node(%d:%d): %d\n",
+ node->tree->cnid, node->this, atomic_read(&node->refcnt));
+ }
+}
+
+/* Dispose of resources used by a node */
+void hfsplus_put_bnode(hfsplus_bnode *node)
+{
+ if (node) {
+ struct hfsplus_btree *tree = node->tree;
+ int i;
+
+ dprint(DBG_BNODE_REFS, "put_node(%d:%d): %d\n",
+ node->tree->cnid, node->this, atomic_read(&node->refcnt));
+ if (!atomic_read(&node->refcnt))
+ BUG();
+ if (!atomic_dec_and_lock(&node->refcnt, &tree->hash_lock)) {
+#if REF_PAGES
+ for (i = 0; i < tree->pages_per_bnode; i++)
+ put_page(node->page[i]);
+#endif
+ return;
+ }
+ for (i = 0; i < tree->pages_per_bnode; i++) {
+ mark_page_accessed(node->page[i]);
+#if REF_PAGES
+ put_page(node->page[i]);
+#endif
+ }
+
+ if (test_bit(HFSPLUS_BNODE_DELETED, &node->flags)) {
+ __hfsplus_bnode_remove(node);
+ spin_unlock(&tree->hash_lock);
+ hfsplus_btree_free_node(node);
+ hfsplus_bnode_free(node);
+ return;
+ }
+ spin_unlock(&tree->hash_lock);
+ }
+}
+
+void hfsplus_lock_bnode(hfsplus_bnode *node)
+{
+ wait_event(node->lock_wq, !test_and_set_bit(HFSPLUS_BNODE_LOCK, &node->flags));
+}
+
+void hfsplus_unlock_bnode(hfsplus_bnode *node)
+{
+ clear_bit(HFSPLUS_BNODE_LOCK, &node->flags);
+ if (waitqueue_active(&node->lock_wq))
+ wake_up(&node->lock_wq);
+}
diff --git a/fs/hfsplus/brec.c b/fs/hfsplus/brec.c
new file mode 100644
index 00000000000000..aaf4a383ccc063
--- /dev/null
+++ b/fs/hfsplus/brec.c
@@ -0,0 +1,50 @@
+/*
+ * linux/fs/hfsplus/brec.c
+ *
+ * Copyright (C) 2001
+ * Brad Boyer (flar@allandria.com)
+ * (C) 2003 Ardis Technologies <roman@ardistech.com>
+ *
+ * Handle individual btree records
+ */
+
+#include "hfsplus_fs.h"
+#include "hfsplus_raw.h"
+
+/* Get the length and offset of the given record in the given node */
+u16 hfsplus_brec_lenoff(hfsplus_bnode *node, u16 rec, u16 *off)
+{
+ u16 retval[2];
+ u16 dataoff;
+
+ dataoff = node->tree->node_size - (rec + 2) * 2;
+ hfsplus_bnode_readbytes(node, retval, dataoff, 4);
+ *off = be16_to_cpu(retval[1]);
+ return be16_to_cpu(retval[0]) - *off;
+}
+
+/* Get the length of the key from a keyed record */
+u16 hfsplus_brec_keylen(hfsplus_bnode *node, u16 rec)
+{
+ u16 klsz, retval, recoff;
+ unsigned char buf[2];
+
+ if ((node->kind != HFSPLUS_NODE_NDX)&&(node->kind != HFSPLUS_NODE_LEAF))
+ return 0;
+
+ klsz = (node->tree->attributes & HFSPLUS_TREE_BIGKEYS) ? 2 : 1;
+ if ((node->kind == HFSPLUS_NODE_NDX) &&
+ !(node->tree->attributes & HFSPLUS_TREE_VAR_NDXKEY_SIZE)) {
+ retval = node->tree->max_key_len;
+ } else {
+ recoff = hfsplus_bnode_read_u16(node, node->tree->node_size - (rec + 1) * 2);
+ if (!recoff)
+ return 0;
+ hfsplus_bnode_readbytes(node, buf, recoff, klsz);
+ if (klsz == 1)
+ retval = buf[0];
+ else
+ retval = be16_to_cpu(*(u16 *)buf);
+ }
+ return (retval + klsz + 1) & 0xFFFE;
+}
diff --git a/fs/hfsplus/btree.c b/fs/hfsplus/btree.c
new file mode 100644
index 00000000000000..551d4d55f85292
--- /dev/null
+++ b/fs/hfsplus/btree.c
@@ -0,0 +1,317 @@
+/*
+ * linux/fs/hfsplus/btree.c
+ *
+ * Copyright (C) 2001
+ * Brad Boyer (flar@allandria.com)
+ * (C) 2003 Ardis Technologies <roman@ardistech.com>
+ *
+ * Handle opening/closing btree
+ */
+
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <asm/div64.h>
+
+#include "hfsplus_fs.h"
+#include "hfsplus_raw.h"
+
+/* Release resources used by a btree */
+void hfsplus_close_btree(struct hfsplus_btree *tree)
+{
+ hfsplus_bnode *node;
+ int i;
+
+ if (!tree)
+ return;
+
+ for (i = 0; i < NODE_HASH_SIZE; i++) {
+ while ((node = tree->node_hash[i])) {
+ tree->node_hash[i] = node->next_hash;
+ if (atomic_read(&node->refcnt))
+ printk("HFS+: node %d:%d still has %d user(s)!\n",
+ node->tree->cnid, node->this, atomic_read(&node->refcnt));
+ hfsplus_bnode_free(node);
+ tree->node_hash_cnt--;
+ }
+ }
+ iput(tree->inode);
+ kfree(tree);
+}
+
+/* Fill in extra data in tree structure from header node */
+static void hfsplus_read_treeinfo(hfsplus_btree *tree, hfsplus_btree_head *hdr)
+{
+ unsigned int shift, size;
+
+ if (!tree || !hdr)
+ return;
+
+ tree->root = be32_to_cpu(hdr->root);
+ tree->leaf_count = be32_to_cpu(hdr->leaf_count);
+ tree->leaf_head = be32_to_cpu(hdr->leaf_head);
+ tree->leaf_tail = be32_to_cpu(hdr->leaf_tail);
+ tree->node_count = be32_to_cpu(hdr->node_count);
+ tree->free_nodes = be32_to_cpu(hdr->free_nodes);
+ tree->attributes = be32_to_cpu(hdr->attributes);
+ tree->node_size = be16_to_cpu(hdr->node_size);
+ tree->max_key_len = be16_to_cpu(hdr->max_key_len);
+ tree->depth = be16_to_cpu(hdr->depth);
+
+ size = tree->node_size;
+ if (size & (size - 1))
+ /* panic */;
+ for (shift = 0; size >>= 1; shift += 1)
+ ;
+ tree->node_size_shift = shift;
+
+ tree->pages_per_bnode = (tree->node_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+}
+
+static void hfsplus_write_treeinfo(hfsplus_btree *tree, hfsplus_btree_head *hdr)
+{
+ hdr->root = cpu_to_be32(tree->root);
+ hdr->leaf_count = cpu_to_be32(tree->leaf_count);
+ hdr->leaf_head = cpu_to_be32(tree->leaf_head);
+ hdr->leaf_tail = cpu_to_be32(tree->leaf_tail);
+ hdr->node_count = cpu_to_be32(tree->node_count);
+ hdr->free_nodes = cpu_to_be32(tree->free_nodes);
+ hdr->attributes = cpu_to_be32(tree->attributes);
+ hdr->depth = cpu_to_be16(tree->depth);
+}
+
+/* Get a reference to a B*Tree and do some initial checks */
+hfsplus_btree *hfsplus_open_btree(struct super_block *sb, u32 id)
+{
+ hfsplus_btree *tree;
+ hfsplus_btree_head *head;
+ struct address_space *mapping;
+ struct page *page;
+
+ tree = kmalloc(sizeof(struct hfsplus_btree), GFP_KERNEL);
+ if (!tree)
+ return NULL;
+ memset(tree, 0, sizeof(struct hfsplus_btree));
+
+ init_MUTEX(&tree->tree_lock);
+ spin_lock_init(&tree->hash_lock);
+ /* Set the correct compare function */
+ tree->sb = sb;
+ tree->cnid = id;
+ if (id == HFSPLUS_EXT_CNID) {
+ tree->keycmp = hfsplus_cmp_ext_key;
+ } else if (id == HFSPLUS_CAT_CNID) {
+ tree->keycmp = hfsplus_cmp_cat_key;
+ } else {
+ printk("HFS+-fs: unknown B*Tree requested\n");
+ goto free_tree;
+ }
+ tree->inode = iget(sb, id);
+ if (!tree->inode)
+ goto free_tree;
+
+ mapping = tree->inode->i_mapping;
+ page = grab_cache_page(mapping, 0);
+ if (!page)
+ goto free_tree;
+ if (!PageUptodate(page)) {
+ if (mapping->a_ops->readpage(NULL, page))
+ goto fail_page;
+ wait_on_page_locked(page);
+ if (!PageUptodate(page))
+ goto fail_page;
+ } else
+ unlock_page(page);
+
+ /* Load the header */
+ head = (hfsplus_btree_head *)(kmap(page) + sizeof(hfsplus_btree_node_desc));
+ hfsplus_read_treeinfo(tree, head);
+ kunmap(page);
+ page_cache_release(page);
+ return tree;
+
+ fail_page:
+ page_cache_release(page);
+ free_tree:
+ iput(tree->inode);
+ kfree(tree);
+ return NULL;
+}
+
+void hfsplus_write_btree(struct hfsplus_btree *tree)
+{
+ hfsplus_btree_head *head;
+ hfsplus_bnode *node;
+ struct page *page;
+
+ node = hfsplus_find_bnode(tree, 0);
+ if (!node)
+ /* panic? */
+ return;
+ /* Load the header */
+ page = node->page[0];
+ head = (hfsplus_btree_head *)(kmap(page) + sizeof(hfsplus_btree_node_desc));
+ hfsplus_write_treeinfo(tree, head);
+ kunmap(page);
+ set_page_dirty(page);
+ hfsplus_put_bnode(node);
+}
+
+hfsplus_bnode *hfsplus_btree_alloc_node(hfsplus_btree *tree)
+{
+ hfsplus_bnode *node;
+ struct page **pagep;
+ u32 nidx;
+ u16 idx, off, len;
+ u8 *data, byte, m;
+ int i;
+
+ while (!tree->free_nodes) {
+ loff_t size;
+ int res;
+
+ res = hfsplus_extend_file(tree->inode);
+ if (res)
+ return ERR_PTR(res);
+ HFSPLUS_I(tree->inode).total_blocks = HFSPLUS_I(tree->inode).alloc_blocks;
+ size = HFSPLUS_I(tree->inode).total_blocks;
+ size <<= tree->sb->s_blocksize_bits;
+ tree->inode->i_size = size;
+ do_div(size, (u32)tree->node_size);
+ tree->free_nodes = (u32)size - tree->node_count;
+ tree->node_count = size;
+ }
+
+ nidx = 0;
+ node = hfsplus_find_bnode(tree, nidx);
+ len = hfsplus_brec_lenoff(node, 2, &off);
+
+ off += node->page_offset;
+ pagep = node->page + (off >> PAGE_CACHE_SHIFT);
+ data = hfsplus_kmap(*pagep);
+ off &= ~PAGE_CACHE_MASK;
+ idx = 0;
+
+ for (;;) {
+ while (len) {
+ byte = data[off];
+ if (byte != 0xff) {
+ for (m = 0x80, i = 0; i < 8; m >>= 1, i++) {
+ if (!(byte & m)) {
+ idx += i;
+ data[off] |= m;
+ set_page_dirty(*pagep);
+ hfsplus_kunmap(*pagep);
+ tree->free_nodes--;
+ mark_inode_dirty(tree->inode);
+ hfsplus_put_bnode(node);
+ return hfsplus_create_bnode(tree, idx);
+ }
+ }
+ }
+ if (++off >= PAGE_CACHE_SIZE) {
+ hfsplus_kunmap(*pagep++);
+ data = hfsplus_kmap(*pagep);
+ off = 0;
+ }
+ idx += 8;
+ len--;
+ }
+ nidx = node->next;
+ hfsplus_put_bnode(node);
+ if (!nidx) {
+ printk("need new bmap node...\n");
+ hfsplus_kunmap(*pagep);
+ return ERR_PTR(-ENOSPC);
+ }
+ node = hfsplus_find_bnode(tree, nidx);
+ len = hfsplus_brec_lenoff(node, 0, &off);
+
+ off += node->page_offset;
+ pagep = node->page + (off >> PAGE_CACHE_SHIFT);
+ data = hfsplus_kmap(*pagep);
+ off &= ~PAGE_CACHE_MASK;
+ }
+}
+
+void hfsplus_btree_remove_node(hfsplus_bnode *node)
+{
+ hfsplus_btree *tree;
+ hfsplus_bnode *tmp;
+ u32 cnid;
+
+ tree = node->tree;
+ if (node->prev) {
+ tmp = hfsplus_find_bnode(tree, node->prev);
+ tmp->next = node->next;
+ cnid = cpu_to_be32(tmp->next);
+ hfsplus_bnode_writebytes(tmp, &cnid, offsetof(hfsplus_btree_node_desc, next), 4);
+ hfsplus_put_bnode(tmp);
+ } else if (node->kind == HFSPLUS_NODE_LEAF)
+ tree->leaf_head = node->next;
+
+ if (node->next) {
+ tmp = hfsplus_find_bnode(tree, node->next);
+ tmp->prev = node->prev;
+ cnid = cpu_to_be32(tmp->prev);
+ hfsplus_bnode_writebytes(tmp, &cnid, offsetof(hfsplus_btree_node_desc, prev), 4);
+ hfsplus_put_bnode(tmp);
+ } else if (node->kind == HFSPLUS_NODE_LEAF)
+ tree->leaf_tail = node->prev;
+
+ // move down?
+ if (!node->prev && !node->next) {
+ printk("hfsplus_btree_del_level\n");
+ }
+ if (!node->parent) {
+ tree->root = 0;
+ tree->depth = 0;
+ }
+ set_bit(HFSPLUS_BNODE_DELETED, &node->flags);
+}
+
+void hfsplus_btree_free_node(hfsplus_bnode *node)
+{
+ hfsplus_btree *tree;
+ struct page *page;
+ u16 off, len;
+ u32 nidx;
+ u8 *data, byte, m;
+
+ dprint(DBG_BNODE_MOD, "btree_free_node: %u\n", node->this);
+ tree = node->tree;
+ nidx = node->this;
+ node = hfsplus_find_bnode(tree, 0);
+ len = hfsplus_brec_lenoff(node, 2, &off);
+ while (nidx >= len * 8) {
+ u32 i;
+
+ nidx -= len * 8;
+ i = node->next;
+ hfsplus_put_bnode(node);
+ if (!nidx)
+ /* panic */;
+ node = hfsplus_find_bnode(tree, nidx);
+ if (node->kind != HFSPLUS_NODE_MAP)
+ /* panic */;
+ len = hfsplus_brec_lenoff(node, 0, &off);
+ }
+ off += node->page_offset + nidx / 8;
+ page = node->page[off >> PAGE_CACHE_SHIFT];
+ data = hfsplus_kmap(page);
+ off &= ~PAGE_CACHE_MASK;
+ m = 1 << (~nidx & 7);
+ byte = data[off];
+ if (!(byte & m)) {
+ BUG();
+ /* panic */
+ hfsplus_kunmap(page);
+ hfsplus_put_bnode(node);
+ return;
+ }
+ data[off] = byte & ~m;
+ set_page_dirty(page);
+ hfsplus_kunmap(page);
+ hfsplus_put_bnode(node);
+ tree->free_nodes++;
+ mark_inode_dirty(tree->inode);
+}
diff --git a/fs/hfsplus/catalog.c b/fs/hfsplus/catalog.c
new file mode 100644
index 00000000000000..a8710372f4da23
--- /dev/null
+++ b/fs/hfsplus/catalog.c
@@ -0,0 +1,333 @@
+/*
+ * linux/fs/hfsplus/catalog.c
+ *
+ * Copyright (C) 2001
+ * Brad Boyer (flar@allandria.com)
+ * (C) 2003 Ardis Technologies <roman@ardistech.com>
+ *
+ * Handling of catalog records
+ */
+
+#include <linux/sched.h>
+
+#include "hfsplus_fs.h"
+#include "hfsplus_raw.h"
+
+int hfsplus_cmp_cat_key(hfsplus_btree_key *k1, hfsplus_btree_key *k2)
+{
+ u32 k1p, k2p;
+
+ k1p = k1->cat.parent;
+ k2p = k2->cat.parent;
+ if (k1p != k2p)
+ return be32_to_cpu(k1p) < be32_to_cpu(k2p) ? -1 : 1;
+
+ return hfsplus_unistrcmp(&k1->cat.name, &k2->cat.name);
+}
+
+void hfsplus_fill_cat_key(hfsplus_btree_key *key, u32 parent,
+ struct qstr *str)
+{
+ int len;
+
+ key->cat.parent = cpu_to_be32(parent);
+ if (str) {
+ hfsplus_asc2uni(&key->cat.name, str->name, str->len);
+ len = be16_to_cpu(key->cat.name.length);
+ } else
+ len = key->cat.name.length = 0;
+ key->key_len = cpu_to_be16(6 + 2 * len);
+}
+
+static void hfsplus_fill_cat_key_uni(hfsplus_btree_key *key, u32 parent,
+ hfsplus_unistr *name)
+{
+ int ustrlen;
+
+ ustrlen = be16_to_cpu(name->length);
+ key->cat.parent = cpu_to_be32(parent);
+ key->cat.name.length = cpu_to_be16(ustrlen);
+ ustrlen *= 2;
+ memcpy(key->cat.name.unicode, name->unicode, ustrlen);
+ key->key_len = cpu_to_be16(6 + ustrlen);
+}
+
+static void hfsplus_set_perms(struct inode *inode, hfsplus_perm *perms)
+{
+ perms->mode = cpu_to_be32(inode->i_mode);
+ perms->owner = cpu_to_be32(inode->i_uid);
+ perms->group = cpu_to_be32(inode->i_gid);
+}
+
+static int hfsplus_fill_cat_entry(hfsplus_cat_entry *entry, u32 cnid, struct inode *inode)
+{
+ if (S_ISDIR(inode->i_mode)) {
+ hfsplus_cat_folder *folder;
+
+ folder = &entry->folder;
+ memset(folder, 0, sizeof(*folder));
+ folder->type = cpu_to_be16(HFSPLUS_FOLDER);
+ folder->id = cpu_to_be32(inode->i_ino);
+ folder->create_date = folder->content_mod_date =
+ folder->attribute_mod_date = folder->access_date =
+ hfsp_now2mt();
+ hfsplus_set_perms(inode, &folder->permissions);
+ if (inode == HFSPLUS_SB(inode->i_sb).hidden_dir)
+ /* invisible and namelocked */
+ folder->user_info.frFlags = cpu_to_be16(0x5000);
+ return sizeof(*folder);
+ } else {
+ hfsplus_cat_file *file;
+
+ file = &entry->file;
+ memset(file, 0, sizeof(*file));
+ file->type = cpu_to_be16(HFSPLUS_FILE);
+ file->id = cpu_to_be32(cnid);
+ file->create_date = file->content_mod_date =
+ file->attribute_mod_date = file->access_date =
+ hfsp_now2mt();
+ if (cnid == inode->i_ino) {
+ hfsplus_set_perms(inode, &file->permissions);
+ file->user_info.fdType = cpu_to_be32(HFSPLUS_SB(inode->i_sb).type);
+ file->user_info.fdCreator = cpu_to_be32(HFSPLUS_SB(inode->i_sb).creator);
+ } else {
+ file->user_info.fdType = cpu_to_be32(HFSP_HARDLINK_TYPE);
+ file->user_info.fdCreator = cpu_to_be32(HFSP_HFSPLUS_CREATOR);
+ file->user_info.fdFlags = cpu_to_be16(0x100);
+ file->permissions.dev = cpu_to_be32(HFSPLUS_I(inode).dev);
+ }
+ return sizeof(*file);
+ }
+}
+
+static int hfsplus_fill_cat_thread(hfsplus_cat_entry *entry, int type,
+ u32 parentid, struct qstr *str)
+{
+ entry->type = cpu_to_be16(type);
+ entry->thread.reserved = 0;
+ entry->thread.parentID = cpu_to_be32(parentid);
+ hfsplus_asc2uni(&entry->thread.nodeName, str->name, str->len);
+ return 10 + be16_to_cpu(entry->thread.nodeName.length) * 2;
+}
+
+/* Try to get a catalog entry for given catalog id */
+int hfsplus_find_cat(struct super_block *sb, unsigned long cnid,
+ struct hfsplus_find_data *fd)
+{
+ hfsplus_cat_entry tmp;
+ int err;
+ u16 type;
+
+ hfsplus_fill_cat_key(fd->search_key, cnid, NULL);
+ err = hfsplus_btree_find_entry(fd, &tmp, sizeof(hfsplus_cat_entry));
+ if (err)
+ return err;
+
+ type = be16_to_cpu(tmp.type);
+ if (type != HFSPLUS_FOLDER_THREAD && type != HFSPLUS_FILE_THREAD) {
+ printk("HFS+-fs: Found bad thread record in catalog\n");
+ return -EIO;
+ }
+
+ hfsplus_fill_cat_key_uni(fd->search_key, be32_to_cpu(tmp.thread.parentID),
+ &tmp.thread.nodeName);
+ return hfsplus_btree_find(fd);
+}
+
+int hfsplus_create_cat(u32 cnid, struct inode *dir, struct qstr *str, struct inode *inode)
+{
+ struct hfsplus_find_data fd;
+ struct super_block *sb;
+ hfsplus_cat_entry entry;
+ int entry_size;
+ int err;
+
+ dprint(DBG_CAT_MOD, "create_cat: %s,%u(%d)\n", str->name, cnid, inode->i_nlink);
+ sb = dir->i_sb;
+ hfsplus_find_init(HFSPLUS_SB(sb).cat_tree, &fd);
+
+ hfsplus_fill_cat_key(fd.search_key, cnid, NULL);
+ entry_size = hfsplus_fill_cat_thread(&entry, S_ISDIR(inode->i_mode) ?
+ HFSPLUS_FOLDER_THREAD : HFSPLUS_FILE_THREAD,
+ dir->i_ino, str);
+ err = hfsplus_btree_find(&fd);
+ if (err != -ENOENT) {
+ if (!err)
+ err = -EEXIST;
+ goto out;
+ }
+ err = hfsplus_bnode_insert_rec(&fd, &entry, entry_size);
+ if (err)
+ goto out;
+
+ hfsplus_fill_cat_key(fd.search_key, dir->i_ino, str);
+ entry_size = hfsplus_fill_cat_entry(&entry, cnid, inode);
+ err = hfsplus_btree_find(&fd);
+ if (err != -ENOENT) {
+ /* panic? */
+ if (!err)
+ err = -EEXIST;
+ goto out;
+ }
+ err = hfsplus_bnode_insert_rec(&fd, &entry, entry_size);
+ if (!err) {
+ dir->i_size++;
+ mark_inode_dirty(dir);
+ }
+out:
+ hfsplus_find_exit(&fd);
+
+ return err;
+}
+
+int hfsplus_delete_cat(u32 cnid, struct inode *dir, struct qstr *str)
+{
+ struct super_block *sb;
+ struct hfsplus_find_data fd;
+ hfsplus_fork_raw fork;
+ struct list_head *pos;
+ int err, off;
+ u16 type;
+
+ dprint(DBG_CAT_MOD, "delete_cat: %s,%u\n", str ? str->name : NULL, cnid);
+ sb = dir->i_sb;
+ hfsplus_find_init(HFSPLUS_SB(sb).cat_tree, &fd);
+
+ if (!str) {
+ int len;
+
+ hfsplus_fill_cat_key(fd.search_key, cnid, NULL);
+ err = hfsplus_btree_find(&fd);
+ if (err)
+ goto out;
+
+ off = fd.entryoffset + offsetof(hfsplus_cat_thread, nodeName);
+ fd.search_key->cat.parent = cpu_to_be32(dir->i_ino);
+ hfsplus_bnode_readbytes(fd.bnode, &fd.search_key->cat.name.length, off, 2);
+ len = be16_to_cpu(fd.search_key->cat.name.length) * 2;
+ hfsplus_bnode_readbytes(fd.bnode, &fd.search_key->cat.name.unicode, off + 2, len);
+ fd.search_key->key_len = cpu_to_be16(6 + len);
+ } else
+ hfsplus_fill_cat_key(fd.search_key, dir->i_ino, str);
+
+ err = hfsplus_btree_find(&fd);
+ if (err)
+ goto out;
+
+ type = hfsplus_bnode_read_u16(fd.bnode, fd.entryoffset);
+ if (type == HFSPLUS_FILE) {
+#if 0
+ off = fd.entryoffset + offsetof(hfsplus_cat_file, data_fork);
+ hfsplus_bnode_readbytes(fd.bnode, &fork, off, sizeof(fork));
+ hfsplus_free_fork(sb, cnid, &fork, HFSPLUS_TYPE_DATA);
+#endif
+
+ off = fd.entryoffset + offsetof(hfsplus_cat_file, rsrc_fork);
+ hfsplus_bnode_readbytes(fd.bnode, &fork, off, sizeof(fork));
+ hfsplus_free_fork(sb, cnid, &fork, HFSPLUS_TYPE_RSRC);
+ }
+
+ list_for_each(pos, &HFSPLUS_I(dir).open_dir_list) {
+ struct hfsplus_readdir_data *rd =
+ list_entry(pos, struct hfsplus_readdir_data, list);
+ if (fd.tree->keycmp(fd.search_key, (void *)&rd->key) < 0)
+ rd->file->f_pos--;
+ }
+
+ err = hfsplus_bnode_remove_rec(&fd);
+ if (err)
+ goto out;
+
+ hfsplus_fill_cat_key(fd.search_key, cnid, NULL);
+ err = hfsplus_btree_find(&fd);
+ if (err)
+ goto out;
+
+ err = hfsplus_bnode_remove_rec(&fd);
+ if (err)
+ goto out;
+
+ dir->i_size--;
+ mark_inode_dirty(dir);
+out:
+ hfsplus_find_exit(&fd);
+
+ return err;
+}
+
+int hfsplus_rename_cat(u32 cnid,
+ struct inode *src_dir, struct qstr *src_name,
+ struct inode *dst_dir, struct qstr *dst_name)
+{
+ struct super_block *sb;
+ struct hfsplus_find_data src_fd, dst_fd;
+ hfsplus_cat_entry entry;
+ int entry_size, type;
+ int err = 0;
+
+ dprint(DBG_CAT_MOD, "rename_cat: %u - %lu,%s - %lu,%s\n", cnid, src_dir->i_ino, src_name->name,
+ dst_dir->i_ino, dst_name->name);
+ sb = src_dir->i_sb;
+ hfsplus_find_init(HFSPLUS_SB(sb).cat_tree, &src_fd);
+ dst_fd = src_fd;
+
+ /* find the old dir entry and read the data */
+ hfsplus_fill_cat_key(src_fd.search_key, src_dir->i_ino, src_name);
+ err = hfsplus_btree_find(&src_fd);
+ if (err)
+ goto out;
+
+ hfsplus_bnode_readbytes(src_fd.bnode, &entry, src_fd.entryoffset,
+ src_fd.entrylength);
+
+ /* create new dir entry with the data from the old entry */
+ hfsplus_fill_cat_key(dst_fd.search_key, dst_dir->i_ino, dst_name);
+ err = hfsplus_btree_find(&dst_fd);
+ if (err != -ENOENT) {
+ if (!err)
+ err = -EEXIST;
+ goto out;
+ }
+
+ err = hfsplus_bnode_insert_rec(&dst_fd, &entry, src_fd.entrylength);
+ if (err)
+ goto out;
+ dst_dir->i_size++;
+ mark_inode_dirty(dst_dir);
+
+ /* finally remove the old entry */
+ hfsplus_fill_cat_key(src_fd.search_key, src_dir->i_ino, src_name);
+ err = hfsplus_btree_find(&src_fd);
+ if (err)
+ goto out;
+ err = hfsplus_bnode_remove_rec(&src_fd);
+ if (err)
+ goto out;
+ src_dir->i_size--;
+ mark_inode_dirty(src_dir);
+
+ /* remove old thread entry */
+ hfsplus_fill_cat_key(src_fd.search_key, cnid, NULL);
+ err = hfsplus_btree_find(&src_fd);
+ if (err)
+ goto out;
+ type = hfsplus_bnode_read_u16(src_fd.bnode, src_fd.entryoffset);
+ err = hfsplus_bnode_remove_rec(&src_fd);
+ if (err)
+ goto out;
+
+ /* create new thread entry */
+ hfsplus_fill_cat_key(dst_fd.search_key, cnid, NULL);
+ entry_size = hfsplus_fill_cat_thread(&entry, type, dst_dir->i_ino, dst_name);
+ err = hfsplus_btree_find(&dst_fd);
+ if (err != -ENOENT) {
+ if (!err)
+ err = -EEXIST;
+ goto out;
+ }
+ err = hfsplus_bnode_insert_rec(&dst_fd, &entry, entry_size);
+out:
+ hfsplus_put_bnode(dst_fd.bnode);
+ hfsplus_find_exit(&src_fd);
+ return err;
+}
diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c
new file mode 100644
index 00000000000000..09337d80a79987
--- /dev/null
+++ b/fs/hfsplus/dir.c
@@ -0,0 +1,492 @@
+/*
+ * linux/fs/hfsplus/dir.c
+ *
+ * Copyright (C) 2001
+ * Brad Boyer (flar@allandria.com)
+ * (C) 2003 Ardis Technologies <roman@ardistech.com>
+ *
+ * Handling of directories
+ */
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/version.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+#include <linux/buffer_head.h>
+#endif
+
+#include "hfsplus_fs.h"
+#include "hfsplus_raw.h"
+
+/* Find the entry inside dir named dentry->d_name */
+static struct dentry *hfsplus_lookup(struct inode *dir, struct dentry *dentry)
+{
+ struct inode *inode = NULL;
+ struct hfsplus_find_data fd;
+ struct super_block *sb;
+ hfsplus_cat_entry entry;
+ int err;
+ u32 cnid, linkid = 0;
+ u16 type;
+
+ sb = dir->i_sb;
+ dentry->d_fsdata = NULL;
+ hfsplus_find_init(HFSPLUS_SB(sb).cat_tree, &fd);
+ hfsplus_fill_cat_key(fd.search_key, dir->i_ino, &dentry->d_name);
+again:
+ err = hfsplus_btree_find_entry(&fd, &entry, sizeof(entry));
+ if (err) {
+ if (err == -ENOENT) {
+ hfsplus_find_exit(&fd);
+ /* No such entry */
+ inode = NULL;
+ goto out;
+ }
+ goto fail;
+ }
+ type = be16_to_cpu(entry.type);
+ if (type == HFSPLUS_FOLDER) {
+ if (fd.entrylength < sizeof(hfsplus_cat_folder)) {
+ err = -EIO;
+ goto fail;
+ }
+ cnid = be32_to_cpu(entry.folder.id);
+ } else if (type == HFSPLUS_FILE) {
+ if (fd.entrylength < sizeof(hfsplus_cat_file)) {
+ err = -EIO;
+ goto fail;
+ }
+ cnid = be32_to_cpu(entry.file.id);
+ if (entry.file.user_info.fdType == cpu_to_be32(HFSP_HARDLINK_TYPE) &&
+ entry.file.user_info.fdCreator == cpu_to_be32(HFSP_HFSPLUS_CREATOR)) {
+ struct qstr str;
+ char name[32];
+
+ if (dentry->d_fsdata) {
+ err = -ENOENT;
+ inode = NULL;
+ goto out;
+ }
+ dentry->d_fsdata = (void *)(unsigned long)cnid;
+ linkid = be32_to_cpu(entry.file.permissions.dev);
+ str.len = sprintf(name, "iNode%d", linkid);
+ str.name = name;
+ hfsplus_fill_cat_key(fd.search_key, HFSPLUS_SB(sb).hidden_dir->i_ino, &str);
+ goto again;
+ } else if (!dentry->d_fsdata)
+ dentry->d_fsdata = (void *)(unsigned long)cnid;
+ } else {
+ printk("HFS+-fs: Illegal catalog entry type in lookup\n");
+ err = -EIO;
+ goto fail;
+ }
+ hfsplus_find_exit(&fd);
+ inode = iget(dir->i_sb, cnid);
+ if (!inode)
+ return ERR_PTR(-EACCES);
+ if (S_ISREG(inode->i_mode))
+ HFSPLUS_I(inode).dev = linkid;
+out:
+ d_add(dentry, inode);
+ return NULL;
+fail:
+ hfsplus_find_exit(&fd);
+ return ERR_PTR(err);
+}
+
+static int hfsplus_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ struct super_block *sb = inode->i_sb;
+ int len, err;
+ char strbuf[HFSPLUS_MAX_STRLEN + 1];
+ hfsplus_cat_entry entry;
+ struct hfsplus_find_data fd;
+ struct hfsplus_readdir_data *rd;
+ u16 type;
+
+ if (filp->f_pos >= inode->i_size)
+ return 0;
+
+ hfsplus_find_init(HFSPLUS_SB(sb).cat_tree, &fd);
+ hfsplus_fill_cat_key(fd.search_key, inode->i_ino, NULL);
+ err = hfsplus_btree_find(&fd);
+ if (err)
+ goto out;
+
+ switch ((u32)filp->f_pos) {
+ case 0:
+ /* This is completely artificial... */
+ if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR))
+ goto out;
+ filp->f_pos++;
+ /* fall through */
+ case 1:
+ hfsplus_bnode_readbytes(fd.bnode, &entry, fd.entryoffset, fd.entrylength);
+ if (be16_to_cpu(entry.type) != HFSPLUS_FOLDER_THREAD) {
+ printk("HFS+-fs: bad catalog folder thread\n");
+ err = -EIO;
+ goto out;
+ }
+ if (fd.entrylength < HFSPLUS_MIN_THREAD_SZ) {
+ printk("HFS+-fs: truncated catalog thread\n");
+ err = -EIO;
+ goto out;
+ }
+ if (filldir(dirent, "..", 2, 1,
+ be32_to_cpu(entry.thread.parentID), DT_DIR))
+ goto out;
+ filp->f_pos++;
+ /* fall through */
+ default:
+ if (filp->f_pos >= inode->i_size)
+ goto out;
+ err = hfsplus_btree_move(&fd, filp->f_pos - 1);
+ if (err)
+ goto out;
+ }
+
+ for (;;) {
+ if (be32_to_cpu(fd.key->cat.parent) != inode->i_ino) {
+ printk("HFS+-fs: walked past end of dir\n");
+ err = -EIO;
+ goto out;
+ }
+ hfsplus_bnode_readbytes(fd.bnode, &entry, fd.entryoffset, fd.entrylength);
+ type = be16_to_cpu(entry.type);
+ len = HFSPLUS_MAX_STRLEN;
+ err = hfsplus_uni2asc(&fd.key->cat.name, strbuf, &len);
+ if (err)
+ goto out;
+ if (type == HFSPLUS_FOLDER) {
+ if (fd.entrylength < sizeof(hfsplus_cat_folder)) {
+ printk("HFS+-fs: small dir entry\n");
+ err = -EIO;
+ goto out;
+ }
+ if (HFSPLUS_SB(sb).hidden_dir &&
+ HFSPLUS_SB(sb).hidden_dir->i_ino == be32_to_cpu(entry.folder.id))
+ goto next;
+ if (filldir(dirent, strbuf, len, filp->f_pos,
+ be32_to_cpu(entry.folder.id), DT_DIR))
+ break;
+ } else if (type == HFSPLUS_FILE) {
+ if (fd.entrylength < sizeof(hfsplus_cat_file)) {
+ printk("HFS+-fs: small file entry\n");
+ err = -EIO;
+ goto out;
+ }
+ if (filldir(dirent, strbuf, len, filp->f_pos,
+ be32_to_cpu(entry.file.id), DT_REG))
+ break;
+ } else {
+ printk("HFS+-fs: bad catalog entry type\n");
+ err = -EIO;
+ goto out;
+ }
+ next:
+ filp->f_pos++;
+ if (filp->f_pos >= inode->i_size)
+ goto out;
+ err = hfsplus_btree_move(&fd, 1);
+ if (err)
+ goto out;
+ }
+ rd = filp->private_data;
+ if (!filp->private_data) {
+ rd = kmalloc(sizeof(struct hfsplus_readdir_data), GFP_KERNEL);
+ if (!rd) {
+ err = -ENOMEM;
+ goto out;
+ }
+ filp->private_data = rd;
+ rd->file = filp;
+ list_add(&rd->list, &HFSPLUS_I(inode).open_dir_list);
+ }
+ memcpy(&rd->key, fd.key, sizeof(hfsplus_cat_key));
+out:
+ hfsplus_find_exit(&fd);
+ return err;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+static loff_t hfsplus_seek_dir(struct file *file, loff_t offset, int origin)
+{
+ loff_t res;
+
+ down(&file->f_dentry->d_inode->i_sem);
+ res = default_llseek(file, offset, origin);
+ up(&file->f_dentry->d_inode->i_sem);
+
+ return res;
+}
+#endif
+
+static int hfsplus_dir_release(struct inode *inode, struct file *file)
+{
+ struct hfsplus_readdir_data *rd = file->private_data;
+ if (rd) {
+ list_del(&rd->list);
+ kfree(rd);
+ }
+ return 0;
+}
+
+int hfsplus_create(struct inode *dir, struct dentry *dentry, int mode)
+{
+ struct inode *inode;
+ int res;
+
+ inode = hfsplus_new_inode(dir->i_sb, mode);
+ if (!inode)
+ return -ENOSPC;
+
+ res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
+ if (res) {
+ inode->i_nlink = 0;
+ iput(inode);
+ return res;
+ }
+ dentry->d_fsdata = (void *)inode->i_ino;
+ d_instantiate(dentry, inode);
+ mark_inode_dirty(inode);
+ return 0;
+}
+
+int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir, struct dentry *dst_dentry)
+{
+ struct super_block *sb = dst_dir->i_sb;
+ struct inode *inode = src_dentry->d_inode;
+ struct inode *src_dir = src_dentry->d_parent->d_inode;
+ struct qstr str;
+ char name[32];
+ u32 cnid, id;
+ int res;
+
+ if (HFSPLUS_IS_RSRC(inode))
+ return -EPERM;
+
+ if (inode->i_ino == (u32)(unsigned long)src_dentry->d_fsdata) {
+ for (;;) {
+ get_random_bytes(&id, sizeof(cnid));
+ id &= 0x3fffffff;
+ str.name = name;
+ str.len = sprintf(name, "iNode%d", id);
+ res = hfsplus_rename_cat(inode->i_ino,
+ src_dir, &src_dentry->d_name,
+ HFSPLUS_SB(sb).hidden_dir, &str);
+ if (!res)
+ break;
+ if (res != -EEXIST)
+ return res;
+ }
+ HFSPLUS_I(inode).dev = id;
+ cnid = HFSPLUS_SB(sb).next_cnid++;
+ src_dentry->d_fsdata = (void *)(unsigned long)cnid;
+ res = hfsplus_create_cat(cnid, src_dir, &src_dentry->d_name, inode);
+ if (res)
+ /* panic? */
+ return res;
+ HFSPLUS_SB(sb).file_count++;
+ }
+ cnid = HFSPLUS_SB(sb).next_cnid++;
+ res = hfsplus_create_cat(cnid, dst_dir, &dst_dentry->d_name, inode);
+ if (res)
+ return res;
+
+ inode->i_nlink++;
+ dst_dentry->d_fsdata = (void *)(unsigned long)cnid;
+ d_instantiate(dst_dentry, inode);
+ atomic_inc(&inode->i_count);
+ inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+ HFSPLUS_SB(sb).file_count++;
+ sb->s_dirt = 1;
+
+ return 0;
+}
+
+int hfsplus_unlink(struct inode *dir, struct dentry *dentry)
+{
+ struct super_block *sb = dir->i_sb;
+ struct inode *inode = dentry->d_inode;
+ struct qstr str;
+ char name[32];
+ u32 cnid;
+ int res;
+
+ if (HFSPLUS_IS_RSRC(inode))
+ return -EPERM;
+
+ cnid = (u32)(unsigned long)dentry->d_fsdata;
+ if (inode->i_ino == cnid &&
+ atomic_read(&HFSPLUS_I(inode).opencnt)) {
+ str.name = name;
+ str.len = sprintf(name, "temp%lu", inode->i_ino);
+ res = hfsplus_rename_cat(inode->i_ino,
+ dir, &dentry->d_name,
+ HFSPLUS_SB(sb).hidden_dir, &str);
+ if (!res)
+ inode->i_flags |= S_DEAD;
+ return res;
+ }
+ res = hfsplus_delete_cat(cnid, dir, &dentry->d_name);
+ if (res)
+ return res;
+
+ inode->i_nlink--;
+ hfsplus_delete_inode(inode);
+ if (inode->i_ino != cnid && !inode->i_nlink) {
+ if (!atomic_read(&HFSPLUS_I(inode).opencnt)) {
+ res = hfsplus_delete_cat(inode->i_ino, HFSPLUS_SB(sb).hidden_dir, NULL);
+ if (!res)
+ hfsplus_delete_inode(inode);
+ } else
+ inode->i_flags |= S_DEAD;
+ }
+ inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+
+ return res;
+}
+
+int hfsplus_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+ struct inode *inode;
+ int res;
+
+ inode = hfsplus_new_inode(dir->i_sb, S_IFDIR | mode);
+ if (!inode)
+ return -ENOSPC;
+
+ res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
+ if (res) {
+ inode->i_nlink = 0;
+ iput(inode);
+ return res;
+ }
+ d_instantiate(dentry, inode);
+ mark_inode_dirty(inode);
+ return 0;
+}
+
+int hfsplus_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ struct inode *inode;
+ int res;
+
+ inode = dentry->d_inode;
+ if (inode->i_size != 2)
+ return -ENOTEMPTY;
+ res = hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
+ if (res)
+ return res;
+ inode->i_nlink = 0;
+ inode->i_ctime = CURRENT_TIME;
+ hfsplus_delete_inode(inode);
+ mark_inode_dirty(inode);
+ return 0;
+}
+
+int hfsplus_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
+{
+ struct super_block *sb;
+ struct inode *inode;
+ int res;
+
+ sb = dir->i_sb;
+ inode = hfsplus_new_inode(sb, S_IFLNK | S_IRWXUGO);
+ if (!inode)
+ return -ENOSPC;
+
+ res = page_symlink(inode, symname, strlen(symname) + 1);
+ if (res) {
+ inode->i_nlink = 0;
+ iput (inode);
+ return res;
+ }
+
+ mark_inode_dirty(inode);
+ res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
+
+ if (!res) {
+ d_instantiate(dentry, inode);
+ mark_inode_dirty(inode);
+ }
+
+ return res;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+int hfsplus_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev)
+#else
+int hfsplus_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev)
+#endif
+{
+ struct super_block *sb;
+ struct inode *inode;
+ int res;
+
+ sb = dir->i_sb;
+ inode = hfsplus_new_inode(sb, mode);
+ if (!inode)
+ return -ENOSPC;
+
+ res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
+ if (res) {
+ inode->i_nlink = 0;
+ iput(inode);
+ return res;
+ }
+ init_special_inode(inode, mode, rdev);
+ d_instantiate(dentry, inode);
+ mark_inode_dirty(inode);
+
+ return 0;
+}
+
+int hfsplus_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ int res;
+
+ /* Unlink destination if it already exists */
+ if (new_dentry->d_inode) {
+ res = hfsplus_unlink(new_dir, new_dentry);
+ if (res)
+ return res;
+ }
+
+ res = hfsplus_rename_cat((u32)(unsigned long)old_dentry->d_fsdata,
+ old_dir, &old_dentry->d_name,
+ new_dir, &new_dentry->d_name);
+ if (!res)
+ new_dentry->d_fsdata = old_dentry->d_fsdata;
+ return res;
+}
+
+struct inode_operations hfsplus_dir_inode_operations = {
+ .lookup = hfsplus_lookup,
+ .create = hfsplus_create,
+ .link = hfsplus_link,
+ .unlink = hfsplus_unlink,
+ .mkdir = hfsplus_mkdir,
+ .rmdir = hfsplus_rmdir,
+ .symlink = hfsplus_symlink,
+ .mknod = hfsplus_mknod,
+ .rename = hfsplus_rename,
+};
+
+struct file_operations hfsplus_dir_operations = {
+ .read = generic_read_dir,
+ .readdir = hfsplus_readdir,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ .llseek = hfsplus_seek_dir,
+#else
+ .llseek = generic_file_llseek,
+#endif
+ .release = hfsplus_dir_release,
+};
diff --git a/fs/hfsplus/extents.c b/fs/hfsplus/extents.c
new file mode 100644
index 00000000000000..0238f9b9be1ff5
--- /dev/null
+++ b/fs/hfsplus/extents.c
@@ -0,0 +1,512 @@
+/*
+ * linux/fs/hfsplus/extents.c
+ *
+ * Copyright (C) 2001
+ * Brad Boyer (flar@allandria.com)
+ * (C) 2003 Ardis Technologies <roman@ardistech.com>
+ *
+ * Handling of Extents both in catalog and extents overflow trees
+ */
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/pagemap.h>
+#include <linux/version.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+#include <linux/buffer_head.h>
+#endif
+
+#include "hfsplus_fs.h"
+#include "hfsplus_raw.h"
+
+/* Compare two extents keys, returns 0 on same, pos/neg for difference */
+int hfsplus_cmp_ext_key(hfsplus_btree_key *k1, hfsplus_btree_key *k2)
+{
+ u32 k1id, k2id;
+ u32 k1s, k2s;
+
+ k1id = k1->ext.cnid;
+ k2id = k2->ext.cnid;
+ if (k1id != k2id)
+ return be32_to_cpu(k1id) < be32_to_cpu(k2id) ? -1 : 1;
+
+ if (k1->ext.fork_type != k2->ext.fork_type)
+ return k1->ext.fork_type < k2->ext.fork_type ? -1 : 1;
+
+ k1s = k1->ext.start_block;
+ k2s = k2->ext.start_block;
+ if (k1s == k2s)
+ return 0;
+ return be32_to_cpu(k1s) < be32_to_cpu(k2s) ? -1 : 1;
+}
+
+void hfsplus_fill_ext_key(hfsplus_btree_key *key, u32 cnid,
+ u32 block, u8 type)
+{
+ key->key_len = cpu_to_be16(HFSPLUS_EXT_KEYLEN - 2);
+ key->ext.cnid = cpu_to_be32(cnid);
+ key->ext.start_block = cpu_to_be32(block);
+ key->ext.fork_type = type;
+ key->ext.pad = 0;
+}
+
+static u32 hfsplus_find_extent(hfsplus_extent *extent, u32 off)
+{
+ int i;
+ u32 count;
+
+ for (i = 0; i < 8; extent++, i++) {
+ count = be32_to_cpu(extent->block_count);
+ if (off < count)
+ return be32_to_cpu(extent->start_block) + off;
+ off -= count;
+ }
+ /* panic? */
+ return 0;
+}
+
+static int hfsplus_find_extentry(struct hfsplus_find_data *fd,
+ hfsplus_extent *extent)
+{
+ int res;
+
+ fd->key->ext.cnid = 0;
+ res = hfsplus_btree_find(fd);
+ if (res && res != -ENOENT)
+ return res;
+ if (fd->key->ext.cnid != fd->search_key->ext.cnid ||
+ fd->key->ext.fork_type != fd->search_key->ext.fork_type)
+ return -ENOENT;
+ //if (fd->entrylength != ext_key_size)...
+ hfsplus_bnode_readbytes(fd->bnode, extent, fd->entryoffset, fd->entrylength);
+ return 0;
+}
+
+/* Get a block at iblock for inode, possibly allocating if create */
+int hfsplus_get_block(struct inode *inode, sector_t iblock,
+ struct buffer_head *bh_result, int create)
+{
+ struct super_block *sb;
+ hfsplus_extent_rec ext_entry;
+ struct hfsplus_find_data fd;
+ int err = -EIO;
+ u32 ablock, dblock = 0;
+
+ sb = inode->i_sb;
+
+ /* Convert inode block to disk allocation block */
+ ablock = iblock;
+
+ if (ablock >= HFSPLUS_I(inode).total_blocks) {
+ if (ablock > HFSPLUS_I(inode).total_blocks || !create)
+ return -EIO;
+ if (ablock >= HFSPLUS_I(inode).alloc_blocks) {
+ err = hfsplus_extend_file(inode);
+ if (err)
+ return err;
+ }
+ HFSPLUS_I(inode).mmu_private += sb->s_blocksize;
+ HFSPLUS_I(inode).total_blocks++;
+ mark_inode_dirty(inode);
+ } else
+ create = 0;
+
+ if (ablock < HFSPLUS_I(inode).extent_blocks) {
+ dblock = hfsplus_find_extent(HFSPLUS_I(inode).extents, ablock);
+ } else {
+ hfsplus_find_init(HFSPLUS_SB(sb).ext_tree, &fd);
+ hfsplus_fill_ext_key(fd.search_key, inode->i_ino, ablock, HFSPLUS_IS_RSRC(inode) ?
+ HFSPLUS_TYPE_RSRC : HFSPLUS_TYPE_DATA);
+ err = hfsplus_find_extentry(&fd, ext_entry);
+ if (!err)
+ dblock = hfsplus_find_extent(ext_entry, ablock -
+ be32_to_cpu(fd.key->ext.start_block));
+ hfsplus_find_exit(&fd);
+ if (err)
+ return err;
+ }
+
+ if (!dblock)
+ return -EIO;
+ dprint(DBG_EXTENT, "get_block(%lu): %lu - %u\n", inode->i_ino, iblock, dblock);
+
+ map_bh(bh_result, sb, dblock + HFSPLUS_SB(sb).blockoffset);
+ if (create)
+ set_buffer_new(bh_result);
+ return 0;
+}
+
+static void hfsplus_dump_extent(hfsplus_extent *extent)
+{
+ int i;
+
+ dprint(DBG_EXTENT, " ");
+ for (i = 0; i < 8; i++)
+ dprint(DBG_EXTENT, " %u:%u", be32_to_cpu(extent[i].start_block),
+ be32_to_cpu(extent[i].block_count));
+ dprint(DBG_EXTENT, "\n");
+}
+
+static int hfsplus_add_extent(hfsplus_extent *extent, u32 offset,
+ u32 alloc_block, u32 block_count)
+{
+ u32 count, start;
+ int i;
+
+ hfsplus_dump_extent(extent);
+ for (i = 0; i < 8; extent++, i++) {
+ count = be32_to_cpu(extent->block_count);
+ if (offset == count) {
+ start = be32_to_cpu(extent->start_block);
+ if (alloc_block != start + count) {
+ if (++i >= 8)
+ return -ENOSPC;
+ extent++;
+ extent->start_block = cpu_to_be32(alloc_block);
+ } else
+ block_count += count;
+ extent->block_count = cpu_to_be32(block_count);
+ return 0;
+ } else if (offset < count)
+ break;
+ offset -= count;
+ }
+ /* panic? */
+ return -EIO;
+}
+
+void hfsplus_free_blocks(struct super_block *sb, u32 start, u32 count)
+{
+ struct inode *anode;
+ struct buffer_head *bh;
+ int size, blk, off;
+ unsigned long *data, word, m;
+
+ anode = HFSPLUS_SB(sb).alloc_file;
+ size = sb->s_blocksize / sizeof(unsigned long);
+ blk = (start >> sb->s_blocksize_bits) / 8;
+ off = (start & (sb->s_blocksize * 8 - 1)) / 8 / sizeof(unsigned long);
+ m = 1 << (~start & (8 * sizeof(unsigned long) - 1));
+
+ HFSPLUS_SB(sb).free_blocks += count;
+ sb->s_dirt = 1;
+
+ while (count) {
+ bh = hfsplus_getblk(anode, blk);
+ data = (unsigned long *)bh->b_data;
+ do {
+ word = ~be32_to_cpu(data[off]);
+ for (;;) {
+ if (word & m)
+ printk("freeing free block %u\n", start);
+ word |= m;
+ start++;
+ if (!--count) {
+ data[off] = cpu_to_be32(~word);
+ goto done;
+ }
+ if (!(m >>= 1))
+ break;
+ }
+ data[off] = cpu_to_be32(~word);
+ m = 1UL << (8 * sizeof(unsigned long) - 1);
+ } while (++off < size);
+ done:
+ mark_buffer_dirty_inode(bh, anode);
+ brelse(bh);
+ if (++blk >= anode->i_blocks)
+ break;
+ off = 0;
+ }
+ if (count)
+ printk("%u block left to free\n", count);
+}
+
+int hfsplus_free_extents(struct super_block *sb, hfsplus_extent *extent,
+ u32 offset, u32 block_nr)
+{
+ u32 count, start;
+ int i;
+
+ hfsplus_dump_extent(extent);
+ for (i = 0; i < 8; extent++, i++) {
+ count = be32_to_cpu(extent->block_count);
+ if (offset == count)
+ goto found;
+ else if (offset < count)
+ break;
+ offset -= count;
+ }
+ /* panic? */
+ return -EIO;
+found:
+ for (;;) {
+ start = be32_to_cpu(extent->start_block);
+ if (count <= block_nr) {
+ hfsplus_free_blocks(sb, start, count);
+ extent->block_count = 0;
+ extent->start_block = 0;
+ block_nr -= count;
+ } else {
+ count -= block_nr;
+ hfsplus_free_blocks(sb, start + count, block_nr);
+ extent->block_count = cpu_to_be32(count);
+ block_nr = 0;
+ }
+ if (!block_nr || !i)
+ return 0;
+ i--;
+ extent--;
+ count = be32_to_cpu(extent->block_count);
+ }
+}
+
+int hfsplus_free_fork(struct super_block *sb, u32 cnid, hfsplus_fork_raw *fork, int type)
+{
+ struct hfsplus_find_data fd;
+ hfsplus_extent_rec ext_entry;
+ u32 total_blocks, blocks, start;
+ int res, i;
+
+ total_blocks = be32_to_cpu(fork->total_blocks);
+ if (!total_blocks)
+ return 0;
+
+ blocks = 0;
+ for (i = 0; i < 8; i++)
+ blocks += be32_to_cpu(fork->extents[i].block_count);
+
+ res = hfsplus_free_extents(sb, fork->extents, blocks, blocks);
+ if (res)
+ return res;
+ if (total_blocks == blocks)
+ return 0;
+
+ hfsplus_find_init(HFSPLUS_SB(sb).ext_tree, &fd);
+ do {
+ hfsplus_fill_ext_key(fd.search_key, cnid,
+ total_blocks, type);
+ res = hfsplus_find_extentry(&fd, ext_entry);
+ if (res)
+ break;
+ start = be32_to_cpu(fd.key->ext.start_block);
+ hfsplus_free_extents(sb, ext_entry,
+ total_blocks - start,
+ total_blocks);
+ hfsplus_bnode_remove_rec(&fd);
+ total_blocks = start;
+ } while (total_blocks > blocks);
+ hfsplus_find_exit(&fd);
+
+ return res;
+}
+
+int hfsplus_extend_file(struct inode *inode)
+{
+ struct super_block *sb = inode->i_sb;
+ struct inode *anode = HFSPLUS_SB(sb).alloc_file;
+ struct hfsplus_find_data fd;
+ hfsplus_extent_rec ext_entry;
+ struct buffer_head *bh;
+ u32 blk, blk1, ablock, block_count;
+ unsigned long *data, word, m;
+ int res, size, off, i;
+
+ if (anode->i_size * 8 < HFSPLUS_SB(sb).total_blocks - HFSPLUS_SB(sb).free_blocks + 8) {
+ // extend alloc file
+ BUG();
+ }
+ blk = blk1 = (HFSPLUS_SB(sb).next_alloc >> sb->s_blocksize_bits) / 8;
+ off = (HFSPLUS_SB(sb).next_alloc & (sb->s_blocksize * 8 - 1)) / sizeof(unsigned long) / 8;
+
+ for (;;) {
+ // ignore first off for now...
+ off = 0;
+ bh = hfsplus_getblk(anode, blk);
+ if (blk == anode->i_blocks - 1)
+ size = (HFSPLUS_SB(sb).total_blocks &
+ (sb->s_blocksize * 8 - 1)) /
+ 8 / sizeof(unsigned long);
+ else
+ size = sb->s_blocksize / sizeof(unsigned long);
+ data = (unsigned long *)bh->b_data;
+ do {
+ word = be32_to_cpu(data[off]);
+ if (!~word)
+ continue;
+ m = 1UL << (sizeof(unsigned long) * 8 - 1);
+ for (i = 0; m; i++, m >>= 1) {
+ if (word & m)
+ continue;
+ ablock = (blk << sb->s_blocksize_bits) * 8 +
+ off * sizeof(unsigned long) * 8 + i;
+ block_count = 1;
+ word |= m;
+#if 0
+ while ((m >>= 1) && !(word & m)) {
+ block_count++;
+ word |= m;
+ }
+#endif
+ data[off] = cpu_to_be32(word);
+ mark_buffer_dirty_inode(bh, anode);
+ brelse(bh);
+ goto found;
+ }
+ } while (++off < size);
+ brelse(bh);
+ if (++blk >= anode->i_blocks)
+ blk = 0;
+ if (blk == blk1)
+ return -ENOSPC;
+ }
+
+found:
+ dprint(DBG_EXTENT, "extend %lu: %u,%u\n", inode->i_ino, ablock, block_count);
+ if (HFSPLUS_I(inode).alloc_blocks <= HFSPLUS_I(inode).extent_blocks) {
+ if (!HFSPLUS_I(inode).extent_blocks) {
+ dprint(DBG_EXTENT, "first extents\n");
+ /* no extents yet */
+ HFSPLUS_I(inode).extents[0].start_block = cpu_to_be32(ablock);
+ HFSPLUS_I(inode).extents[0].block_count = cpu_to_be32(block_count);
+ res = 0;
+ } else
+ /* try to append to extents in inode */
+ res = hfsplus_add_extent(HFSPLUS_I(inode).extents,
+ HFSPLUS_I(inode).alloc_blocks,
+ ablock, block_count);
+ if (!res) {
+ hfsplus_dump_extent(HFSPLUS_I(inode).extents);
+ HFSPLUS_I(inode).extent_blocks += block_count;
+ } else if (res == -ENOSPC) {
+ /* create new extent, so find place to insert it */
+ hfsplus_find_init(HFSPLUS_SB(sb).ext_tree, &fd);
+ hfsplus_fill_ext_key(fd.search_key, inode->i_ino,
+ HFSPLUS_I(inode).alloc_blocks,
+ HFSPLUS_IS_RSRC(inode) ?
+ HFSPLUS_TYPE_RSRC : HFSPLUS_TYPE_DATA);
+ res = hfsplus_find_extentry(&fd, ext_entry);
+ if (res && res != -ENOENT) {
+ hfsplus_find_exit(&fd);
+ goto out;
+ }
+ goto insert_extent;
+ }
+ } else {
+ hfsplus_find_init(HFSPLUS_SB(sb).ext_tree, &fd);
+ hfsplus_fill_ext_key(fd.search_key, inode->i_ino,
+ HFSPLUS_I(inode).alloc_blocks,
+ HFSPLUS_IS_RSRC(inode) ?
+ HFSPLUS_TYPE_RSRC : HFSPLUS_TYPE_DATA);
+ res = hfsplus_find_extentry(&fd, ext_entry);
+ if (res) {
+ hfsplus_find_exit(&fd);
+ goto out;
+ }
+
+ res = hfsplus_add_extent(ext_entry, HFSPLUS_I(inode).alloc_blocks -
+ be32_to_cpu(fd.key->ext.start_block),
+ ablock, block_count);
+ if (!res) {
+ hfsplus_dump_extent(ext_entry);
+ hfsplus_bnode_writebytes(fd.bnode, &ext_entry,
+ fd.entryoffset,
+ sizeof(ext_entry));
+ } else if (res == -ENOSPC)
+ goto insert_extent;
+ hfsplus_find_exit(&fd);
+ }
+out:
+ if (!res) {
+ HFSPLUS_I(inode).alloc_blocks += block_count;
+ mark_inode_dirty(inode);
+ HFSPLUS_SB(sb).free_blocks -= block_count;
+ sb->s_dirt = 1;
+ }
+ return res;
+
+insert_extent:
+ dprint(DBG_EXTENT, "insert new extent\n");
+ memset(ext_entry, 0, sizeof(ext_entry));
+ ext_entry[0].start_block = cpu_to_be32(ablock);
+ ext_entry[0].block_count = cpu_to_be32(block_count);
+
+ hfsplus_bnode_insert_rec(&fd, ext_entry, sizeof(ext_entry));
+ hfsplus_find_exit(&fd);
+ res = 0;
+ goto out;
+}
+
+void hfsplus_truncate(struct inode *inode)
+{
+ struct super_block *sb = inode->i_sb;
+ struct hfsplus_find_data fd;
+ hfsplus_extent_rec ext_entry;
+ u32 blk_cnt, start;
+ int res;
+
+ dprint(DBG_INODE, "truncate: %lu, %Lu -> %Lu\n", inode->i_ino,
+ (long long)HFSPLUS_I(inode).mmu_private, inode->i_size);
+ if (inode->i_size == HFSPLUS_I(inode).mmu_private)
+ return;
+ if (inode->i_size > HFSPLUS_I(inode).mmu_private) {
+ struct address_space *mapping = inode->i_mapping;
+ struct page *page;
+ u32 size = inode->i_size - 1;
+ int res;
+
+ page = grab_cache_page(mapping, size >> PAGE_CACHE_SHIFT);
+ if (!page)
+ return;
+ size &= PAGE_CACHE_SIZE - 1;
+ size++;
+ res = mapping->a_ops->prepare_write(NULL, page, size, size);
+ if (!res)
+ res = mapping->a_ops->commit_write(NULL, page, size, size);
+ if (res)
+ inode->i_size = HFSPLUS_I(inode).mmu_private;
+ unlock_page(page);
+ page_cache_release(page);
+ mark_inode_dirty(inode);
+ return;
+ }
+ blk_cnt = (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits;
+
+ while (1) {
+ if (HFSPLUS_I(inode).alloc_blocks <= HFSPLUS_I(inode).extent_blocks) {
+ hfsplus_free_extents(sb, HFSPLUS_I(inode).extents,
+ HFSPLUS_I(inode).alloc_blocks,
+ HFSPLUS_I(inode).alloc_blocks - blk_cnt);
+ hfsplus_dump_extent(HFSPLUS_I(inode).extents);
+ HFSPLUS_I(inode).extent_blocks = blk_cnt;
+ break;
+ }
+ hfsplus_find_init(HFSPLUS_SB(sb).ext_tree, &fd);
+ hfsplus_fill_ext_key(fd.search_key, inode->i_ino,
+ HFSPLUS_I(inode).alloc_blocks,
+ HFSPLUS_IS_RSRC(inode) ?
+ HFSPLUS_TYPE_RSRC : HFSPLUS_TYPE_DATA);
+ res = hfsplus_find_extentry(&fd, ext_entry);
+ if (res) {
+ hfsplus_find_exit(&fd);
+ break;
+ }
+ start = be32_to_cpu(fd.key->ext.start_block);
+ hfsplus_free_extents(sb, ext_entry,
+ HFSPLUS_I(inode).alloc_blocks - start,
+ HFSPLUS_I(inode).alloc_blocks - blk_cnt);
+ hfsplus_dump_extent(ext_entry);
+ if (blk_cnt > start) {
+ hfsplus_bnode_writebytes(fd.bnode, &ext_entry,
+ fd.entryoffset,
+ sizeof(ext_entry));
+ hfsplus_find_exit(&fd);
+ break;
+ }
+ HFSPLUS_I(inode).alloc_blocks = start;
+ hfsplus_bnode_remove_rec(&fd);
+ hfsplus_find_exit(&fd);
+ }
+ HFSPLUS_I(inode).mmu_private = inode->i_size;
+ HFSPLUS_I(inode).alloc_blocks = HFSPLUS_I(inode).total_blocks = blk_cnt;
+}
diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h
new file mode 100644
index 00000000000000..225441977f369a
--- /dev/null
+++ b/fs/hfsplus/hfsplus_fs.h
@@ -0,0 +1,368 @@
+/*
+ * linux/include/linux/hfsplus_fs.h
+ *
+ * Copyright (C) 1999
+ * Brad Boyer (flar@pants.nu)
+ * (C) 2003 Ardis Technologies <roman@ardistech.com>
+ *
+ */
+
+#ifndef _LINUX_HFSPLUS_FS_H
+#define _LINUX_HFSPLUS_FS_H
+
+#include <linux/fs.h>
+#include <linux/version.h>
+#include "hfsplus_raw.h"
+
+#define DBG_BNODE_REFS 0x00000001
+#define DBG_BNODE_MOD 0x00000002
+#define DBG_CAT_MOD 0x00000004
+#define DBG_INODE 0x00000008
+#define DBG_SUPER 0x00000010
+#define DBG_EXTENT 0x00000020
+
+//#define DBG_MASK (DBG_EXTENT|DBG_INODE|DBG_BNODE_MOD)
+//#define DBG_MASK (DBG_BNODE_MOD|DBG_CAT_MOD)
+//#define DBG_MASK (DBG_BNODE_REFS|DBG_INODE|DBG_BNODE_MOD)
+#define DBG_MASK (0)
+
+#define dprint(flg, fmt, args...) \
+ if (flg & DBG_MASK) printk(fmt , ## args)
+
+/* Runtime config options */
+#define HFSPLUS_CASE_ASIS 0
+#define HFSPLUS_CASE_LOWER 1
+
+#define HFSPLUS_FORK_RAW 0
+#define HFSPLUS_FORK_CAP 1
+#define HFSPLUS_FORK_DOUBLE 2
+#define HFSPLUS_FORK_NETATALK 3
+
+#define HFSPLUS_NAMES_TRIVIAL 0
+#define HFSPLUS_NAMES_CAP 1
+#define HFSPLUS_NAMES_NETATALK 2
+#define HFSPLUS_NAMES_7BIT 3
+
+#define HFSPLUS_DEF_CR_TYPE 0x3F3F3F3F /* '????' */
+
+#define HFSPLUS_TYPE_DATA 0x00
+#define HFSPLUS_TYPE_RSRC 0xFF
+
+typedef int (*btree_keycmp)(hfsplus_btree_key *, hfsplus_btree_key *);
+
+#define NODE_HASH_SIZE 256
+
+/* An HFS+ BTree held in memory */
+typedef struct hfsplus_btree {
+ struct super_block *sb;
+ struct inode *inode;
+ btree_keycmp keycmp;
+
+ u32 cnid;
+ u32 root;
+ u32 leaf_count;
+ u32 leaf_head;
+ u32 leaf_tail;
+ u32 node_count;
+ u32 free_nodes;
+ u32 attributes;
+
+ unsigned int node_size;
+ unsigned int node_size_shift;
+ unsigned int max_key_len;
+ unsigned int depth;
+
+ //unsigned int map1_size, map_size;
+ struct semaphore tree_lock;
+
+ unsigned int pages_per_bnode;
+ spinlock_t hash_lock;
+ struct hfsplus_bnode *node_hash[NODE_HASH_SIZE];
+ int node_hash_cnt;
+} hfsplus_btree;
+
+struct page;
+
+/* An HFS+ BTree node in memory */
+typedef struct hfsplus_bnode {
+ struct hfsplus_btree *tree;
+
+ u32 prev;
+ u32 this;
+ u32 next;
+ u32 parent;
+
+ u16 num_recs;
+ u8 kind;
+ u8 height;
+
+ struct hfsplus_bnode *next_hash;
+ unsigned long flags;
+ wait_queue_head_t lock_wq;
+ atomic_t refcnt;
+ unsigned int page_offset;
+ struct page *page[0];
+} hfsplus_bnode;
+
+#define HFSPLUS_BNODE_LOCK 0
+#define HFSPLUS_BNODE_ERROR 1
+#define HFSPLUS_BNODE_NEW 2
+#define HFSPLUS_BNODE_DIRTY 3
+#define HFSPLUS_BNODE_DELETED 4
+
+/*
+ * HFS+ superblock info (built from Volume Header on disk)
+ */
+
+struct hfsplus_vh;
+struct hfsplus_btree;
+
+struct hfsplus_sb_info {
+ struct buffer_head *s_vhbh;
+ struct hfsplus_vh *s_vhdr;
+ struct hfsplus_btree *ext_tree;
+ struct hfsplus_btree *cat_tree;
+ struct hfsplus_btree *attr_tree;
+ struct inode *alloc_file;
+ struct inode *hidden_dir;
+
+ /* Runtime variables */
+ u32 blockoffset;
+ u32 sect_count;
+ //int a2b_shift;
+
+ /* Stuff in host order from Vol Header */
+ u32 total_blocks;
+ u32 free_blocks;
+ u32 next_alloc;
+ u32 next_cnid;
+ u32 file_count;
+ u32 folder_count;
+
+ /* Config options */
+ u32 creator;
+ u32 type;
+
+ int charcase;
+ int fork;
+ int namemap;
+
+ umode_t umask;
+ uid_t uid;
+ gid_t gid;
+
+ unsigned long flags;
+
+ atomic_t inode_cnt;
+ u32 last_inode_cnt;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ struct list_head rsrc_inodes;
+#else
+ struct hlist_head rsrc_inodes;
+#endif
+};
+
+#define HFSPLUS_SB_WRITEBACKUP 0x0001
+
+
+struct hfsplus_inode_info {
+ /* Device number in hfsplus_permissions in catalog */
+ u32 dev;
+ /* Allocation extents from catlog record or volume header */
+ hfsplus_extent_rec extents;
+ u32 total_blocks, extent_blocks, alloc_blocks;
+ atomic_t opencnt;
+
+ struct inode *rsrc_inode;
+ unsigned long flags;
+
+ struct list_head open_dir_list;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ unsigned long mmu_private;
+#else
+ loff_t mmu_private;
+ struct inode vfs_inode;
+#endif
+};
+
+#define HFSPLUS_FLG_RSRC 0x0001
+#define HFSPLUS_FLG_DIRTYMODE 0x0002
+
+#define HFSPLUS_IS_DATA(inode) (!(HFSPLUS_I(inode).flags & HFSPLUS_FLG_RSRC))
+#define HFSPLUS_IS_RSRC(inode) (HFSPLUS_I(inode).flags & HFSPLUS_FLG_RSRC)
+
+struct hfsplus_find_data {
+ /* filled by caller */
+ hfsplus_btree_key *search_key;
+ hfsplus_btree_key *key;
+ /* filled by find */
+ hfsplus_btree *tree;
+ hfsplus_bnode *bnode;
+ /* filled by findrec */
+ int record, exact;
+ int keyoffset, keylength;
+ int entryoffset, entrylength;
+};
+
+struct hfsplus_readdir_data {
+ struct list_head list;
+ struct file *file;
+ hfsplus_cat_key key;
+};
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+typedef long sector_t;
+#endif
+
+/*
+ * Functions in any *.c used in other files
+ */
+
+/* bfind.c */
+void hfsplus_find_rec(hfsplus_bnode *, struct hfsplus_find_data *);
+int hfsplus_btree_find(struct hfsplus_find_data *);
+int hfsplus_btree_find_entry(struct hfsplus_find_data *,
+ void *, int);
+int hfsplus_btree_move(struct hfsplus_find_data *, int);
+int hfsplus_find_init(hfsplus_btree *, struct hfsplus_find_data *);
+void hfsplus_find_exit(struct hfsplus_find_data *);
+
+/* bnode.c */
+struct buffer_head *hfsplus_getblk(struct inode *, unsigned long);
+hfsplus_bnode *__hfsplus_find_bnode(hfsplus_btree *, u32);
+void __hfsplus_bnode_remove(hfsplus_bnode *);
+hfsplus_bnode *hfsplus_create_bnode(hfsplus_btree *, u32);
+hfsplus_bnode *hfsplus_find_bnode(hfsplus_btree *, u32);
+void hfsplus_get_bnode(hfsplus_bnode *);
+void hfsplus_put_bnode(hfsplus_bnode *);
+void hfsplus_bnode_free(hfsplus_bnode *);
+void hfsplus_bnode_readbytes(hfsplus_bnode *, void *, unsigned long, unsigned long);
+u16 hfsplus_bnode_read_u16(hfsplus_bnode *, unsigned long);
+void hfsplus_bnode_writebytes(hfsplus_bnode *, void *, unsigned long, unsigned long);
+void hfsplus_bnode_write_u16(hfsplus_bnode *, unsigned long, u16);
+void hfsplus_bnode_copybytes(hfsplus_bnode *, unsigned long,
+ hfsplus_bnode *, unsigned long, unsigned long);
+void hfsplus_bnode_movebytes(hfsplus_bnode *, unsigned long, unsigned long, unsigned long);
+int hfsplus_bnode_insert_rec(struct hfsplus_find_data *, void *, int);
+int hfsplus_bnode_remove_rec(struct hfsplus_find_data *);
+
+/* brec.c */
+u16 hfsplus_brec_lenoff(hfsplus_bnode *, u16, u16 *);
+u16 hfsplus_brec_keylen(hfsplus_bnode *, u16);
+
+/* btree.c */
+hfsplus_btree *hfsplus_open_btree(struct super_block *, u32);
+void hfsplus_close_btree(struct hfsplus_btree *);
+void hfsplus_write_btree(struct hfsplus_btree *);
+hfsplus_bnode *hfsplus_btree_alloc_node(hfsplus_btree *);
+void hfsplus_btree_remove_node(hfsplus_bnode *);
+void hfsplus_btree_free_node(hfsplus_bnode *);
+
+/* catalog.c */
+int hfsplus_cmp_cat_key(hfsplus_btree_key *, hfsplus_btree_key *);
+void hfsplus_fill_cat_key(hfsplus_btree_key *, u32, struct qstr *);
+int hfsplus_find_cat(struct super_block *, unsigned long, struct hfsplus_find_data *);
+int hfsplus_create_cat(u32, struct inode *, struct qstr *, struct inode *);
+int hfsplus_delete_cat(u32, struct inode *, struct qstr *);
+int hfsplus_rename_cat(u32, struct inode *, struct qstr *,
+ struct inode *, struct qstr *);
+
+/* extents.c */
+int hfsplus_cmp_ext_key(hfsplus_btree_key *, hfsplus_btree_key *);
+void hfsplus_fill_ext_key(hfsplus_btree_key *, u32, u32, u8);
+int hfsplus_get_block(struct inode *, sector_t, struct buffer_head *, int);
+int hfsplus_free_fork(struct super_block *, u32, hfsplus_fork_raw *, int);
+int hfsplus_extend_file(struct inode *);
+void hfsplus_truncate(struct inode *);
+
+/* inode.c */
+void hfsplus_inode_read_fork(struct inode *, hfsplus_fork_raw *);
+void hfsplus_inode_write_fork(struct inode *, hfsplus_fork_raw *);
+int hfsplus_cat_read_inode(struct inode *, struct hfsplus_find_data *);
+void hfsplus_cat_write_inode(struct inode *);
+struct inode *hfsplus_new_inode(struct super_block *, int);
+void hfsplus_delete_inode(struct inode *);
+
+extern struct address_space_operations hfsplus_btree_aops;
+
+/* options.c */
+int parse_options(char *, struct hfsplus_sb_info *);
+void fill_defaults(struct hfsplus_sb_info *);
+void fill_current(struct hfsplus_sb_info *, struct hfsplus_sb_info *);
+
+/* tables.c */
+extern u16 case_fold_table[];
+
+/* unicode.c */
+int hfsplus_unistrcmp(const hfsplus_unistr *, const hfsplus_unistr *);
+int hfsplus_uni2asc(const hfsplus_unistr *, char *, int *);
+int hfsplus_asc2uni(hfsplus_unistr *, const char *, int);
+
+/* wrapper.c */
+int hfsplus_read_wrapper(struct super_block *);
+
+/* access macros */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+#define HFSPLUS_SB(super) (*(struct hfsplus_sb_info *)&(super)->u)
+#define HFSPLUS_I(inode) (*(struct hfsplus_inode_info *)&(inode)->u)
+#else
+/*
+static inline struct hfsplus_sb_info *HFSPLUS_SB(struct super_block *sb)
+{
+ return sb->s_fs_info;
+}
+static inline struct hfsplus_inode_info *HFSPLUS_I(struct inode *inode)
+{
+ return list_entry(inode, struct hfsplus_inode_info, vfs_inode);
+}
+*/
+#define HFSPLUS_SB(super) (*(struct hfsplus_sb_info *)(super)->s_fs_info)
+#define HFSPLUS_I(inode) (*list_entry(inode, struct hfsplus_inode_info, vfs_inode))
+#endif
+
+#if 1
+#define hfsplus_kmap(p) ({ struct page *__p = (p); kmap(__p); })
+#define hfsplus_kunmap(p) ({ struct page *__p = (p); kunmap(__p); __p; })
+#else
+#define hfsplus_kmap(p) kmap(p)
+#define hfsplus_kunmap(p) kunmap(p)
+#endif
+
+/* time macros */
+#define __hfsp_mt2ut(t) (be32_to_cpu(t) - 2082844800U)
+#define __hfsp_ut2mt(t) (cpu_to_be32(t + 2082844800U))
+
+/* compatibility */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+#define PageUptodate(page) Page_Uptodate(page)
+#define wait_on_page_locked(page) wait_on_page(page)
+#define get_seconds() CURRENT_TIME
+#define page_symlink(i,n,l) block_symlink(i,n,l)
+#define map_bh(bh, sb, block) ({ \
+ bh->b_dev = kdev_t_to_nr(sb->s_dev); \
+ bh->b_blocknr = block; \
+ bh->b_state |= (1UL << BH_Mapped); \
+})
+#define set_buffer_new(bh) (bh->b_state |= (1UL << BH_New))
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,21)
+#define new_inode(sb) ({ \
+ struct inode *inode = get_empty_inode(); \
+ if (inode) { \
+ inode->i_sb = sb; \
+ inode->i_dev = sb->s_dev; \
+ inode->i_blkbits = sb->s_blocksize_bits; \
+ } \
+ inode; \
+})
+#endif
+#define hfsp_mt2ut(t) __hfsp_mt2ut(t)
+#define hfsp_ut2mt(t) __hfsp_ut2mt(t)
+#define hfsp_now2mt() __hfsp_ut2mt(CURRENT_TIME)
+#else
+#define hfsp_mt2ut(t) (struct timespec){ .tv_sec = __hfsp_mt2ut(t) }
+#define hfsp_ut2mt(t) __hfsp_ut2mt((t).tv_sec)
+#define hfsp_now2mt() __hfsp_ut2mt(get_seconds())
+#endif
+
+#endif
diff --git a/fs/hfsplus/hfsplus_raw.h b/fs/hfsplus/hfsplus_raw.h
new file mode 100644
index 00000000000000..ef49f5376e78dd
--- /dev/null
+++ b/fs/hfsplus/hfsplus_raw.h
@@ -0,0 +1,321 @@
+/*
+ * linux/include/linux/hfsplus_raw.h
+ *
+ * Copyright (C) 1999
+ * Brad Boyer (flar@pants.nu)
+ * (C) 2003 Ardis Technologies <roman@ardistech.com>
+ *
+ * Format of structures on disk
+ * Information taken from Apple Technote #1150 (HFS Plus Volume Format)
+ *
+ */
+
+#ifndef _LINUX_HFSPLUS_RAW_H
+#define _LINUX_HFSPLUS_RAW_H
+
+#include <linux/types.h>
+
+#define __packed __attribute__ ((packed))
+
+/* Some constants */
+#define HFSPLUS_SECTOR_SIZE 512
+#define HFSPLUS_VOLHEAD_SECTOR 2
+#define HFSPLUS_VOLHEAD_SIG 0x482b
+#define HFSPLUS_SUPER_MAGIC 0x482b
+#define HFSPLUS_CURRENT_VERSION 4
+
+#define HFSP_WRAP_MAGIC 0x4244
+#define HFSP_WRAP_ATTRIB_SLOCK 0x8000
+#define HFSP_WRAP_ATTRIB_SPARED 0x0200
+
+#define HFSP_WRAPOFF_SIG 0x00
+#define HFSP_WRAPOFF_ATTRIB 0x0A
+#define HFSP_WRAPOFF_ABLKSIZE 0x14
+#define HFSP_WRAPOFF_ABLKSTART 0x1C
+#define HFSP_WRAPOFF_EMBEDSIG 0x7C
+#define HFSP_WRAPOFF_EMBEDEXT 0x7E
+
+#define HFSP_HIDDENDIR_NAME "\xe2\x90\x80\xe2\x90\x80\xe2\x90\x80\xe2\x90\x80HFS+ Private Data"
+
+#define HFSP_HARDLINK_TYPE 0x686c6e6b /* 'hlnk' */
+#define HFSP_HFSPLUS_CREATOR 0x6866732b /* 'hfs+' */
+
+#define HFSP_MOUNT_VERSION 0x482b4c78 /* 'H+Lx' */
+
+/* Structures used on disk */
+
+typedef u32 hfsplus_cnid;
+typedef u16 hfsplus_unichr;
+
+/* A "string" as used in filenames, etc. */
+typedef struct {
+ u16 length;
+ hfsplus_unichr unicode[255];
+} __packed hfsplus_unistr;
+
+#define HFSPLUS_MAX_STRLEN 255
+
+/* POSIX permissions */
+typedef struct {
+ u32 owner;
+ u32 group;
+ u32 mode;
+ u32 dev;
+} __packed hfsplus_perm;
+
+/* A single contiguous area of a file */
+typedef struct {
+ u32 start_block;
+ u32 block_count;
+} __packed hfsplus_extent;
+typedef hfsplus_extent hfsplus_extent_rec[8];
+
+/* Information for a "Fork" in a file */
+typedef struct {
+ u64 total_size;
+ u32 clump_size;
+ u32 total_blocks;
+ hfsplus_extent_rec extents;
+} __packed hfsplus_fork_raw;
+
+/* HFS+ Volume Header */
+typedef struct hfsplus_vh {
+ u16 signature;
+ u16 version;
+ u32 attributes;
+ u32 last_mount_vers;
+ u32 reserved;
+
+ u32 create_date;
+ u32 modify_date;
+ u32 backup_date;
+ u32 checked_date;
+
+ u32 file_count;
+ u32 folder_count;
+
+ u32 blocksize;
+ u32 total_blocks;
+ u32 free_blocks;
+
+ u32 next_alloc;
+ u32 rsrc_clump_sz;
+ u32 data_clump_sz;
+ hfsplus_cnid next_cnid;
+
+ u32 write_count;
+ u64 encodings_bmp;
+
+ u8 finder_info[32];
+
+ hfsplus_fork_raw alloc_file;
+ hfsplus_fork_raw ext_file;
+ hfsplus_fork_raw cat_file;
+ hfsplus_fork_raw attr_file;
+ hfsplus_fork_raw start_file;
+} __packed hfsplus_vh;
+
+/* HFS+ volume attributes */
+#define HFSPLUS_VOL_UNMNT (1 << 8)
+#define HFSPLUS_VOL_SPARE_BLK (1 << 9)
+#define HFSPLUS_VOL_NOCACHE (1 << 10)
+#define HFSPLUS_VOL_INCNSTNT (1 << 11)
+#define HFSPLUS_VOL_SOFTLOCK (1 << 15)
+
+/* HFS+ BTree node descriptor */
+typedef struct {
+ u32 next;
+ u32 prev;
+ s8 kind;
+ u8 height;
+ u16 num_rec;
+ u16 reserved;
+} __packed hfsplus_btree_node_desc;
+
+/* HFS+ BTree node types */
+#define HFSPLUS_NODE_NDX 0x00
+#define HFSPLUS_NODE_HEAD 0x01
+#define HFSPLUS_NODE_MAP 0x02
+#define HFSPLUS_NODE_LEAF 0xFF
+
+/* HFS+ BTree header */
+typedef struct {
+ u16 depth;
+ u32 root;
+ u32 leaf_count;
+ u32 leaf_head;
+ u32 leaf_tail;
+ u16 node_size;
+ u16 max_key_len;
+ u32 node_count;
+ u32 free_nodes;
+ u16 reserved1;
+ u32 clump_size;
+ u8 btree_type;
+ u8 reserved2;
+ u32 attributes;
+ u32 reserved3[16];
+} __packed hfsplus_btree_head;
+
+/* BTree attributes */
+#define HFSPLUS_TREE_BIGKEYS 2
+#define HFSPLUS_TREE_VAR_NDXKEY_SIZE 4
+
+/* HFS+ BTree misc info */
+#define HFSPLUS_TREE_HEAD 0
+#define HFSPLUS_NODE_MXSZ 32768
+
+/* Some special File ID numbers (stolen from hfs.h) */
+#define HFSPLUS_POR_CNID 1 /* Parent Of the Root */
+#define HFSPLUS_ROOT_CNID 2 /* ROOT directory */
+#define HFSPLUS_EXT_CNID 3 /* EXTents B-tree */
+#define HFSPLUS_CAT_CNID 4 /* CATalog B-tree */
+#define HFSPLUS_BAD_CNID 5 /* BAD blocks file */
+#define HFSPLUS_ALLOC_CNID 6 /* ALLOCation file */
+#define HFSPLUS_START_CNID 7 /* STARTup file */
+#define HFSPLUS_ATTR_CNID 8 /* ATTRibutes file */
+#define HFSPLUS_EXCH_CNID 15 /* ExchangeFiles temp id */
+#define HFSPLUS_FIRSTUSER_CNID 16 /* first available user id */
+
+/* HFS+ catalog entry key */
+typedef struct {
+ u16 key_len;
+ hfsplus_cnid parent;
+ hfsplus_unistr name;
+} __packed hfsplus_cat_key;
+
+
+/* Structs from hfs.h */
+typedef struct {
+ u16 v;
+ u16 h;
+} __packed hfsp_point;
+
+typedef struct {
+ u16 top;
+ u16 left;
+ u16 bottom;
+ u16 right;
+} __packed hfsp_rect;
+
+
+/* HFS directory info (stolen from hfs.h */
+typedef struct {
+ hfsp_rect frRect;
+ u16 frFlags;
+ hfsp_point frLocation;
+ u16 frView;
+} __packed DInfo;
+
+typedef struct {
+ hfsp_point frScroll;
+ u32 frOpenChain;
+ u16 frUnused;
+ u16 frComment;
+ u32 frPutAway;
+} __packed DXInfo;
+
+/* HFS+ folder data (part of an hfsplus_cat_entry) */
+typedef struct {
+ s16 type;
+ u16 flags;
+ u32 valence;
+ hfsplus_cnid id;
+ u32 create_date;
+ u32 content_mod_date;
+ u32 attribute_mod_date;
+ u32 access_date;
+ u32 backup_date;
+ hfsplus_perm permissions;
+ DInfo user_info;
+ DXInfo finder_info;
+ u32 text_encoding;
+ u32 reserved;
+} __packed hfsplus_cat_folder;
+
+/* HFS file info (stolen from hfs.h) */
+typedef struct {
+ u32 fdType;
+ u32 fdCreator;
+ u16 fdFlags;
+ hfsp_point fdLocation;
+ u16 fdFldr;
+} __packed FInfo;
+
+typedef struct {
+ u16 fdIconID;
+ u8 fdUnused[8];
+ u16 fdComment;
+ u32 fdPutAway;
+} __packed FXInfo;
+
+/* HFS+ file data (part of a cat_entry) */
+typedef struct {
+ s16 type;
+ u16 flags;
+ u32 reserved1;
+ hfsplus_cnid id;
+ u32 create_date;
+ u32 content_mod_date;
+ u32 attribute_mod_date;
+ u32 access_date;
+ u32 backup_date;
+ hfsplus_perm permissions;
+ FInfo user_info;
+ FXInfo finder_info;
+ u32 text_encoding;
+ u32 reserved2;
+
+ hfsplus_fork_raw data_fork;
+ hfsplus_fork_raw rsrc_fork;
+} __packed hfsplus_cat_file;
+
+/* File attribute bits */
+#define kHFSFileLockedBit 0x0000
+#define kHFSFileLockedMask 0x0001
+#define kHFSThreadExistsBit 0x0001
+#define kHFSThreadExistsMask 0x0002
+
+/* HFS+ catalog thread (part of a cat_entry) */
+typedef struct {
+ s16 type;
+ s16 reserved;
+ hfsplus_cnid parentID;
+ hfsplus_unistr nodeName;
+} __packed hfsplus_cat_thread;
+
+#define HFSPLUS_MIN_THREAD_SZ 10
+
+/* A data record in the catalog tree */
+typedef union {
+ s16 type;
+ hfsplus_cat_folder folder;
+ hfsplus_cat_file file;
+ hfsplus_cat_thread thread;
+} __packed hfsplus_cat_entry;
+
+/* HFS+ catalog entry type */
+#define HFSPLUS_FOLDER 0x0001
+#define HFSPLUS_FILE 0x0002
+#define HFSPLUS_FOLDER_THREAD 0x0003
+#define HFSPLUS_FILE_THREAD 0x0004
+
+/* HFS+ extents tree key */
+typedef struct {
+ u16 key_len;
+ u8 fork_type;
+ u8 pad;
+ hfsplus_cnid cnid;
+ u32 start_block;
+} __packed hfsplus_ext_key;
+
+#define HFSPLUS_EXT_KEYLEN 12
+
+/* HFS+ generic BTree key */
+typedef union {
+ u16 key_len;
+ hfsplus_cat_key cat;
+ hfsplus_ext_key ext;
+} __packed hfsplus_btree_key;
+
+#endif
diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c
new file mode 100644
index 00000000000000..14d5d0380e6391
--- /dev/null
+++ b/fs/hfsplus/inode.c
@@ -0,0 +1,479 @@
+/*
+ * linux/fs/hfsplus/inode.c
+ *
+ * Copyright (C) 2001
+ * Brad Boyer (flar@allandria.com)
+ * (C) 2003 Ardis Technologies <roman@ardistech.com>
+ *
+ * Inode handling routines
+ */
+
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/pagemap.h>
+#include <linux/version.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+#include <linux/buffer_head.h>
+#endif
+
+#include "hfsplus_fs.h"
+#include "hfsplus_raw.h"
+
+static int hfsplus_readpage(struct file *file, struct page *page)
+{
+ //printk("readpage: %lu\n", page->index);
+ return block_read_full_page(page, hfsplus_get_block);
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+static int hfsplus_writepage(struct page *page, struct writeback_control *wbc)
+{
+ //printk("writepage: %lu\n", page->index);
+ return block_write_full_page(page, hfsplus_get_block, wbc);
+}
+#else
+static int hfsplus_writepage(struct page *page)
+{
+ //printk("writepage: %lu\n", page->index);
+ return block_write_full_page(page, hfsplus_get_block);
+}
+#endif
+
+static int hfsplus_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
+{
+ return cont_prepare_write(page, from, to, hfsplus_get_block,
+ &HFSPLUS_I(page->mapping->host).mmu_private);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+static int hfsplus_bmap(struct address_space *mapping, long block)
+#else
+static sector_t hfsplus_bmap(struct address_space *mapping, sector_t block)
+#endif
+{
+ return generic_block_bmap(mapping, block, hfsplus_get_block);
+}
+
+int hfsplus_releasepage(struct page *page, int mask)
+{
+ struct inode *inode = page->mapping->host;
+ struct super_block *sb = inode->i_sb;
+ struct hfsplus_btree *tree;
+ hfsplus_bnode *node;
+ u32 nidx;
+ int i, res = 1;
+
+ switch (inode->i_ino) {
+ case HFSPLUS_EXT_CNID:
+ tree = HFSPLUS_SB(sb).ext_tree;
+ break;
+ case HFSPLUS_CAT_CNID:
+ tree = HFSPLUS_SB(sb).cat_tree;
+ break;
+ case HFSPLUS_ATTR_CNID:
+ tree = HFSPLUS_SB(sb).attr_tree;
+ break;
+ default:
+ BUG();
+ return 0;
+ }
+ if (tree->node_size >= PAGE_CACHE_SIZE) {
+ nidx = page->index >> (tree->node_size_shift - PAGE_CACHE_SHIFT);
+ spin_lock(&tree->hash_lock);
+ node = __hfsplus_find_bnode(tree, nidx);
+ if (!node)
+ ;
+ else if (atomic_read(&node->refcnt))
+ res = 0;
+ else for (i = 0; i < tree->pages_per_bnode; i++) {
+ if (PageActive(node->page[i])) {
+ res = 0;
+ break;
+ }
+ }
+ if (res && node) {
+ __hfsplus_bnode_remove(node);
+ hfsplus_bnode_free(node);
+ }
+ spin_unlock(&tree->hash_lock);
+ } else {
+ nidx = page->index >> (PAGE_CACHE_SHIFT - tree->node_size_shift);
+ i = 1 << (PAGE_CACHE_SHIFT - tree->node_size_shift);
+ spin_lock(&tree->hash_lock);
+ do {
+ node = __hfsplus_find_bnode(tree, nidx++);
+ if (!node)
+ continue;
+ if (atomic_read(&node->refcnt)) {
+ res = 0;
+ break;
+ }
+ __hfsplus_bnode_remove(node);
+ hfsplus_bnode_free(node);
+ } while (--i);
+ spin_unlock(&tree->hash_lock);
+ }
+ //printk("releasepage: %lu,%x = %d\n", page->index, mask, res);
+ return res;
+}
+
+struct address_space_operations hfsplus_btree_aops = {
+ .readpage = hfsplus_readpage,
+ .writepage = hfsplus_writepage,
+ .sync_page = block_sync_page,
+ .prepare_write = hfsplus_prepare_write,
+ .commit_write = generic_commit_write,
+ .bmap = hfsplus_bmap,
+ .releasepage = hfsplus_releasepage,
+};
+
+struct address_space_operations hfsplus_aops = {
+ .readpage = hfsplus_readpage,
+ .writepage = hfsplus_writepage,
+ .sync_page = block_sync_page,
+ .prepare_write = hfsplus_prepare_write,
+ .commit_write = generic_commit_write,
+ .bmap = hfsplus_bmap,
+};
+
+static struct dentry *hfsplus_file_lookup(struct inode *dir, struct dentry *dentry)
+{
+ struct hfsplus_find_data fd;
+ struct super_block *sb = dir->i_sb;
+ struct inode *inode = NULL;
+ int err;
+
+ if (HFSPLUS_IS_RSRC(dir) || strcmp(dentry->d_name.name, "rsrc"))
+ goto out;
+
+ inode = HFSPLUS_I(dir).rsrc_inode;
+ if (inode)
+ goto out;
+
+ inode = new_inode(sb);
+ if (!inode)
+ return ERR_PTR(-ENOMEM);
+
+ inode->i_ino = dir->i_ino;
+ HFSPLUS_I(inode).flags = HFSPLUS_FLG_RSRC;
+
+ hfsplus_find_init(HFSPLUS_SB(sb).cat_tree, &fd);
+ err = hfsplus_find_cat(sb, dir->i_ino, &fd);
+ if (!err)
+ err = hfsplus_cat_read_inode(inode, &fd);
+ hfsplus_find_exit(&fd);
+ if (err) {
+ iput(inode);
+ return ERR_PTR(err);
+ }
+ HFSPLUS_I(inode).rsrc_inode = dir;
+ HFSPLUS_I(dir).rsrc_inode = inode;
+ igrab(dir);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ list_add(&inode->i_hash, &HFSPLUS_SB(sb).rsrc_inodes);
+#else
+ hlist_add_head(&inode->i_hash, &HFSPLUS_SB(sb).rsrc_inodes);
+#endif
+ mark_inode_dirty(inode);
+ {
+ void hfsplus_inode_check(struct super_block *sb);
+ atomic_inc(&HFSPLUS_SB(sb).inode_cnt);
+ hfsplus_inode_check(sb);
+ }
+out:
+ d_add(dentry, inode);
+ return NULL;
+}
+
+static void hfsplus_get_perms(struct inode *inode, hfsplus_perm *perms, int dir)
+{
+ struct super_block *sb = inode->i_sb;
+ int mode;
+
+ mode = be32_to_cpu(perms->mode) & 0xffff;
+
+ inode->i_uid = be32_to_cpu(perms->owner);
+ if (!inode->i_uid && !mode)
+ inode->i_uid = HFSPLUS_SB(sb).uid;
+
+ inode->i_gid = be32_to_cpu(perms->group);
+ if (!inode->i_gid && !mode)
+ inode->i_gid = HFSPLUS_SB(sb).gid;
+
+ if (dir) {
+ mode = mode ? (mode & S_IALLUGO) :
+ (S_IRWXUGO & ~(HFSPLUS_SB(sb).umask));
+ mode |= S_IFDIR;
+ } else if (!mode)
+ mode = S_IFREG | ((S_IRUGO|S_IWUGO) &
+ ~(HFSPLUS_SB(sb).umask));
+ inode->i_mode = mode;
+}
+
+static void hfsplus_set_perms(struct inode *inode, hfsplus_perm *perms)
+{
+ perms->mode = cpu_to_be32(inode->i_mode);
+ perms->owner = cpu_to_be32(inode->i_uid);
+ perms->group = cpu_to_be32(inode->i_gid);
+ perms->dev = cpu_to_be32(HFSPLUS_I(inode).dev);
+}
+
+static int hfsplus_file_open(struct inode *inode, struct file *file)
+{
+ if (HFSPLUS_IS_RSRC(inode))
+ inode = HFSPLUS_I(inode).rsrc_inode;
+ if (atomic_read(&file->f_count) != 1)
+ return 0;
+ atomic_inc(&HFSPLUS_I(inode).opencnt);
+ return 0;
+}
+
+static int hfsplus_file_release(struct inode *inode, struct file *file)
+{
+ struct super_block *sb = inode->i_sb;
+
+ if (HFSPLUS_IS_RSRC(inode))
+ inode = HFSPLUS_I(inode).rsrc_inode;
+ if (atomic_read(&file->f_count) != 0)
+ return 0;
+ if (atomic_dec_and_test(&HFSPLUS_I(inode).opencnt)) {
+ down(&inode->i_sem);
+ if (inode->i_flags & S_DEAD) {
+ hfsplus_delete_cat(inode->i_ino, HFSPLUS_SB(sb).hidden_dir, NULL);
+ hfsplus_delete_inode(inode);
+ }
+ up(&inode->i_sem);
+ }
+ return 0;
+}
+
+extern struct inode_operations hfsplus_dir_inode_operations;
+extern struct file_operations hfsplus_dir_operations;
+
+struct inode_operations hfsplus_file_inode_operations = {
+ .lookup = hfsplus_file_lookup,
+ .truncate = hfsplus_truncate,
+};
+
+struct file_operations hfsplus_file_operations = {
+ .llseek = generic_file_llseek,
+ .read = generic_file_read,
+ //.write = hfsplus_file_write,
+ .write = generic_file_write,
+ .mmap = generic_file_mmap,
+ .fsync = file_fsync,
+ .open = hfsplus_file_open,
+ .release = hfsplus_file_release,
+};
+
+struct inode *hfsplus_new_inode(struct super_block *sb, int mode)
+{
+ struct inode *inode = new_inode(sb);
+ if (!inode)
+ return NULL;
+
+ {
+ void hfsplus_inode_check(struct super_block *sb);
+ atomic_inc(&HFSPLUS_SB(sb).inode_cnt);
+ hfsplus_inode_check(sb);
+ }
+ inode->i_ino = HFSPLUS_SB(sb).next_cnid++;
+ inode->i_mode = mode;
+ inode->i_uid = current->fsuid;
+ inode->i_gid = current->fsgid;
+ inode->i_nlink = 1;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+ if (S_ISDIR(inode->i_mode)) {
+ inode->i_size = 2;
+ HFSPLUS_SB(sb).folder_count++;
+ inode->i_op = &hfsplus_dir_inode_operations;
+ inode->i_fop = &hfsplus_dir_operations;
+ INIT_LIST_HEAD(&HFSPLUS_I(inode).open_dir_list);
+ } else if (S_ISREG(inode->i_mode)) {
+ HFSPLUS_SB(sb).file_count++;
+ inode->i_op = &hfsplus_file_inode_operations;
+ inode->i_fop = &hfsplus_file_operations;
+ inode->i_mapping->a_ops = &hfsplus_aops;
+ } else if (S_ISLNK(inode->i_mode)) {
+ HFSPLUS_SB(sb).file_count++;
+ inode->i_op = &page_symlink_inode_operations;
+ inode->i_mapping->a_ops = &hfsplus_aops;
+ } else
+ HFSPLUS_SB(sb).file_count++;
+ insert_inode_hash(inode);
+ mark_inode_dirty(inode);
+ sb->s_dirt = 1;
+
+ return inode;
+}
+
+void hfsplus_delete_inode(struct inode *inode)
+{
+ struct super_block *sb = inode->i_sb;
+
+ if (S_ISDIR(inode->i_mode)) {
+ HFSPLUS_SB(sb).folder_count--;
+ sb->s_dirt = 1;
+ return;
+ }
+ HFSPLUS_SB(sb).file_count--;
+ if (S_ISREG(inode->i_mode)) {
+ if (!inode->i_nlink) {
+ inode->i_size = 0;
+ hfsplus_truncate(inode);
+ }
+ } else if (S_ISLNK(inode->i_mode)) {
+ inode->i_size = 0;
+ hfsplus_truncate(inode);
+ }
+ sb->s_dirt = 1;
+}
+
+void hfsplus_inode_read_fork(struct inode *inode, hfsplus_fork_raw *fork)
+{
+ u32 count;
+ int i;
+
+ memcpy(&HFSPLUS_I(inode).extents, &fork->extents,
+ sizeof(hfsplus_extent_rec));
+ for (count = 0, i = 0; i < 8; i++)
+ count += be32_to_cpu(fork->extents[i].block_count);
+ HFSPLUS_I(inode).extent_blocks = count;
+ HFSPLUS_I(inode).total_blocks = HFSPLUS_I(inode).alloc_blocks =
+ be32_to_cpu(fork->total_blocks);
+ inode->i_size = HFSPLUS_I(inode).mmu_private = be64_to_cpu(fork->total_size);
+ inode->i_blocks = be32_to_cpu(fork->total_blocks);
+}
+
+void hfsplus_inode_write_fork(struct inode *inode, hfsplus_fork_raw *fork)
+{
+ memcpy(&fork->extents, &HFSPLUS_I(inode).extents,
+ sizeof(hfsplus_extent_rec));
+ fork->total_size = cpu_to_be64(inode->i_size);
+ fork->total_blocks = cpu_to_be32(HFSPLUS_I(inode).alloc_blocks);
+}
+
+int hfsplus_cat_read_inode(struct inode *inode, struct hfsplus_find_data *fd)
+{
+ hfsplus_cat_entry entry;
+ int res = 0;
+ u16 type;
+
+ type = hfsplus_bnode_read_u16(fd->bnode, fd->entryoffset);
+
+ HFSPLUS_I(inode).dev = 0;
+ inode->i_blksize = PAGE_SIZE; /* Doesn't seem to be useful... */
+ if (type == HFSPLUS_FOLDER) {
+ hfsplus_cat_folder *folder = &entry.folder;
+
+ if (fd->entrylength < sizeof(hfsplus_cat_folder))
+ /* panic? */;
+ hfsplus_bnode_readbytes(fd->bnode, &entry, fd->entryoffset,
+ sizeof(hfsplus_cat_folder));
+ memset(&HFSPLUS_I(inode).extents, 0,
+ sizeof(hfsplus_extent_rec));
+ hfsplus_get_perms(inode, &folder->permissions, 1);
+ inode->i_nlink = 1;
+ inode->i_size = 2 + be32_to_cpu(folder->valence);
+ inode->i_atime = hfsp_mt2ut(folder->access_date);
+ inode->i_mtime = hfsp_mt2ut(folder->content_mod_date);
+ inode->i_ctime = inode->i_mtime;
+ inode->i_blocks = 0;
+ inode->i_op = &hfsplus_dir_inode_operations;
+ inode->i_fop = &hfsplus_dir_operations;
+ INIT_LIST_HEAD(&HFSPLUS_I(inode).open_dir_list);
+ } else if (type == HFSPLUS_FILE) {
+ hfsplus_cat_file *file = &entry.file;
+
+ if (fd->entrylength < sizeof(hfsplus_cat_file))
+ /* panic? */;
+ hfsplus_bnode_readbytes(fd->bnode, &entry, fd->entryoffset,
+ sizeof(hfsplus_cat_file));
+
+ hfsplus_inode_read_fork(inode, HFSPLUS_IS_DATA(inode) ?
+ &file->data_fork : &file->rsrc_fork);
+ hfsplus_get_perms(inode, &file->permissions, 0);
+ inode->i_nlink = 1;
+ if (S_ISREG(inode->i_mode)) {
+ if (file->permissions.dev)
+ inode->i_nlink = be32_to_cpu(file->permissions.dev);
+ inode->i_op = &hfsplus_file_inode_operations;
+ inode->i_fop = &hfsplus_file_operations;
+ inode->i_mapping->a_ops = &hfsplus_aops;
+ } else if (S_ISLNK(inode->i_mode)) {
+ inode->i_op = &page_symlink_inode_operations;
+ inode->i_mapping->a_ops = &hfsplus_aops;
+ } else {
+ init_special_inode(inode, inode->i_mode,
+ be32_to_cpu(file->permissions.dev));
+ }
+ inode->i_atime = hfsp_mt2ut(file->access_date);
+ inode->i_mtime = hfsp_mt2ut(file->content_mod_date);
+ inode->i_ctime = inode->i_mtime;
+ } else {
+ printk("HFS+-fs: bad catalog entry used to create inode\n");
+ res = -EIO;
+ }
+ return res;
+}
+
+void hfsplus_cat_write_inode(struct inode *inode)
+{
+ struct hfsplus_find_data fd;
+ hfsplus_cat_entry entry;
+
+ if (HFSPLUS_IS_RSRC(inode)) {
+ mark_inode_dirty(HFSPLUS_I(inode).rsrc_inode);
+ return;
+ }
+
+ if (!inode->i_nlink)
+ return;
+
+ if (hfsplus_find_init(HFSPLUS_SB(inode->i_sb).cat_tree, &fd))
+ /* panic? */
+ return;
+
+ if (hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd))
+ /* panic? */
+ goto out;
+
+ if (S_ISDIR(inode->i_mode)) {
+ hfsplus_cat_folder *folder = &entry.folder;
+
+ if (fd.entrylength < sizeof(hfsplus_cat_folder))
+ /* panic? */;
+ hfsplus_bnode_readbytes(fd.bnode, &entry, fd.entryoffset,
+ sizeof(hfsplus_cat_folder));
+ /* simple node checks? */
+ hfsplus_set_perms(inode, &folder->permissions);
+ folder->access_date = hfsp_ut2mt(inode->i_atime);
+ folder->content_mod_date = hfsp_ut2mt(inode->i_mtime);
+ folder->attribute_mod_date = hfsp_ut2mt(inode->i_ctime);
+ folder->valence = cpu_to_be32(inode->i_size - 2);
+ hfsplus_bnode_writebytes(fd.bnode, &entry, fd.entryoffset,
+ sizeof(hfsplus_cat_folder));
+ } else {
+ hfsplus_cat_file *file = &entry.file;
+
+ if (fd.entrylength < sizeof(hfsplus_cat_file))
+ /* panic? */;
+ hfsplus_bnode_readbytes(fd.bnode, &entry, fd.entryoffset,
+ sizeof(hfsplus_cat_file));
+ hfsplus_inode_write_fork(inode, &file->data_fork);
+ if (HFSPLUS_I(inode).rsrc_inode)
+ hfsplus_inode_write_fork(HFSPLUS_I(inode).rsrc_inode, &file->rsrc_fork);
+ if (S_ISREG(inode->i_mode))
+ HFSPLUS_I(inode).dev = inode->i_nlink;
+ if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+ HFSPLUS_I(inode).dev = kdev_t_to_nr(inode->i_rdev);
+ hfsplus_set_perms(inode, &file->permissions);
+ file->access_date = hfsp_ut2mt(inode->i_atime);
+ file->content_mod_date = hfsp_ut2mt(inode->i_mtime);
+ file->attribute_mod_date = hfsp_ut2mt(inode->i_ctime);
+ hfsplus_bnode_writebytes(fd.bnode, &entry, fd.entryoffset,
+ sizeof(hfsplus_cat_file));
+ }
+out:
+ hfsplus_find_exit(&fd);
+}
diff --git a/fs/hfsplus/options.c b/fs/hfsplus/options.c
new file mode 100644
index 00000000000000..7d5fbb1f0e7a06
--- /dev/null
+++ b/fs/hfsplus/options.c
@@ -0,0 +1,140 @@
+/*
+ * linux/fs/hfsplus/options.c
+ *
+ * Copyright (C) 2001
+ * Brad Boyer (flar@allandria.com)
+ * (C) 2003 Ardis Technologies <roman@ardistech.com>
+ *
+ * Option parsing
+ */
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include "hfsplus_fs.h"
+
+/* Initialize an options object to reasonable defaults */
+void fill_defaults(struct hfsplus_sb_info *opts)
+{
+ if (!opts)
+ return;
+
+ opts->creator = HFSPLUS_DEF_CR_TYPE;
+ opts->type = HFSPLUS_DEF_CR_TYPE;
+ opts->charcase = HFSPLUS_CASE_ASIS;
+ opts->fork = HFSPLUS_FORK_RAW;
+ opts->namemap = HFSPLUS_NAMES_TRIVIAL;
+ opts->umask = current->fs->umask;
+ opts->uid = current->uid;
+ opts->gid = current->gid;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+/* Copy settings from one hfsplus_sb_info object to another */
+void fill_current(struct hfsplus_sb_info *curopts,
+ struct hfsplus_sb_info *opts)
+{
+ if (!curopts || !opts)
+ return;
+
+ opts->creator = curopts->creator;
+ opts->type = curopts->type;
+ opts->charcase = curopts->charcase;
+ opts->fork = curopts->fork;
+ opts->namemap = curopts->namemap;
+ opts->umask = curopts->umask;
+ opts->uid = curopts->uid;
+ opts->gid = curopts->gid;
+}
+#endif
+
+/* convert a "four byte character" to a 32 bit int with error checks */
+static int fill_fourchar(u32 *result, char *input)
+{
+ u32 out;
+ int i;
+
+ if (!result || !input || !*input || (strlen(input) != 4))
+ return 0;
+
+ for (out = 0, i = 0; i < 4; i++) {
+ out <<= 8;
+ out |= ((int)(input[i])) & 0xFF;
+ }
+ *result = out;
+ return 1;
+}
+
+/* convert a string to int with error checks */
+static int fill_int(int *result, char *input, int base)
+{
+ char *tmp = input;
+ int intval;
+
+ if (!result || !input || !*input)
+ return 0;
+
+ intval = simple_strtoul(tmp, &tmp, base);
+ if (*tmp)
+ return 0;
+
+ *result = intval;
+ return 1;
+}
+
+/* Parse options from mount. Returns 0 on failure */
+/* input is the options passed to mount() as a string */
+int parse_options(char *input, struct hfsplus_sb_info *results)
+{
+ char *curropt, *value;
+ int tmp;
+
+ if (!input)
+ return 1;
+
+ while ((curropt = strsep(&input,",")) != NULL) {
+ if (!*curropt)
+ continue;
+
+ if ((value = strchr(curropt, '=')) != NULL)
+ *value++ = '\0';
+
+ if (!strcmp(curropt, "creator")) {
+ if (!fill_fourchar(&(results->creator), value)) {
+ printk("HFS+-fs: creator requires a 4 character value\n");
+ return 0;
+ }
+ } else if (!strcmp(curropt, "type")) {
+ if (!fill_fourchar(&(results->type), value)) {
+ printk("HFS+-fs: type requires a 4 character value\n");
+ return 0;
+ }
+ } else if (!strcmp(curropt, "case")) {
+ } else if (!strcmp(curropt, "fork")) {
+ } else if (!strcmp(curropt, "names")) {
+ } else if (!strcmp(curropt, "umask")) {
+ if (!fill_int(&tmp, value, 8)) {
+ printk("HFS+-fs: umask requires a value\n");
+ return 0;
+ }
+ results->umask = (umode_t)tmp;
+ } else if (!strcmp(curropt, "uid")) {
+ if (!fill_int(&tmp, value, 0)) {
+ printk("HFS+-fs: uid requires an argument\n");
+ return 0;
+ }
+ results->uid = (uid_t)tmp;
+ } else if (!strcmp(curropt, "gid")) {
+ if (!fill_int(&tmp, value, 0)) {
+ printk("HFS+-fs: gid requires an argument\n");
+ return 0;
+ }
+ results->gid = (gid_t)tmp;
+ } else {
+ printk("HFS+-fs: unknown option %s\n", curropt);
+ return 0;
+ }
+ }
+
+ return 1;
+}
diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c
new file mode 100644
index 00000000000000..66ca634ee12788
--- /dev/null
+++ b/fs/hfsplus/super.c
@@ -0,0 +1,523 @@
+/*
+ * linux/fs/hfsplus/super.c
+ *
+ * Copyright (C) 2001
+ * Brad Boyer (flar@allandria.com)
+ * (C) 2003 Ardis Technologies <roman@ardistech.com>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+#include <linux/locks.h>
+#else
+#include <linux/buffer_head.h>
+#include <linux/vfs.h>
+
+static struct inode *hfsplus_alloc_inode(struct super_block *sb);
+static void hfsplus_destroy_inode(struct inode *inode);
+#endif
+
+#include "hfsplus_fs.h"
+
+void hfsplus_inode_check(struct super_block *sb)
+{
+#if 0
+ u32 cnt = atomic_read(&HFSPLUS_SB(sb).inode_cnt);
+ u32 last_cnt = HFSPLUS_SB(sb).last_inode_cnt;
+
+ if (cnt <= (last_cnt / 2) ||
+ cnt >= (last_cnt * 2)) {
+ HFSPLUS_SB(sb).last_inode_cnt = cnt;
+ printk("inode_check: %u,%u,%u\n", cnt, last_cnt,
+ HFSPLUS_SB(sb).cat_tree ? HFSPLUS_SB(sb).cat_tree->node_hash_cnt : 0);
+ }
+#endif
+}
+
+static void hfsplus_read_inode(struct inode *inode)
+{
+ struct hfsplus_find_data fd;
+ struct hfsplus_vh *vhdr;
+ int err;
+
+ atomic_inc(&HFSPLUS_SB(inode->i_sb).inode_cnt);
+ hfsplus_inode_check(inode->i_sb);
+ if (inode->i_ino >= HFSPLUS_FIRSTUSER_CNID) {
+ read_inode:
+ HFSPLUS_I(inode).flags = 0;
+ hfsplus_find_init(HFSPLUS_SB(inode->i_sb).cat_tree, &fd);
+ err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
+ if (!err)
+ err = hfsplus_cat_read_inode(inode, &fd);
+ hfsplus_find_exit(&fd);
+ if (err)
+ goto bad_inode;
+ return;
+ }
+ vhdr = HFSPLUS_SB(inode->i_sb).s_vhdr;
+ switch(inode->i_ino) {
+ case HFSPLUS_ROOT_CNID:
+ goto read_inode;
+ case HFSPLUS_EXT_CNID:
+ hfsplus_inode_read_fork(inode, &vhdr->ext_file);
+ inode->i_mapping->a_ops = &hfsplus_btree_aops;
+ break;
+ case HFSPLUS_CAT_CNID:
+ hfsplus_inode_read_fork(inode, &vhdr->cat_file);
+ inode->i_mapping->a_ops = &hfsplus_btree_aops;
+ break;
+ case HFSPLUS_ALLOC_CNID:
+ hfsplus_inode_read_fork(inode, &vhdr->alloc_file);
+ break;
+ case HFSPLUS_START_CNID:
+ hfsplus_inode_read_fork(inode, &vhdr->start_file);
+ break;
+ case HFSPLUS_ATTR_CNID:
+ hfsplus_inode_read_fork(inode, &vhdr->attr_file);
+ inode->i_mapping->a_ops = &hfsplus_btree_aops;
+ break;
+ default:
+ goto bad_inode;
+ }
+
+ return;
+
+ bad_inode:
+ make_bad_inode(inode);
+}
+
+void hfsplus_write_inode(struct inode *inode, int unused)
+{
+ struct hfsplus_vh *vhdr;
+
+ dprint(DBG_INODE, "hfsplus_write_inode: %lu\n", inode->i_ino);
+ if (inode->i_ino >= HFSPLUS_FIRSTUSER_CNID) {
+ hfsplus_cat_write_inode(inode);
+ return;
+ }
+ vhdr = HFSPLUS_SB(inode->i_sb).s_vhdr;
+ switch (inode->i_ino) {
+ case HFSPLUS_ROOT_CNID:
+ hfsplus_cat_write_inode(inode);
+ break;
+ case HFSPLUS_EXT_CNID:
+ if (vhdr->ext_file.total_size != cpu_to_be64(inode->i_size)) {
+ HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP;
+ inode->i_sb->s_dirt = 1;
+ }
+ hfsplus_inode_write_fork(inode, &vhdr->ext_file);
+ hfsplus_write_btree(HFSPLUS_SB(inode->i_sb).ext_tree);
+ break;
+ case HFSPLUS_CAT_CNID:
+ if (vhdr->cat_file.total_size != cpu_to_be64(inode->i_size)) {
+ HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP;
+ inode->i_sb->s_dirt = 1;
+ }
+ hfsplus_inode_write_fork(inode, &vhdr->cat_file);
+ hfsplus_write_btree(HFSPLUS_SB(inode->i_sb).cat_tree);
+ break;
+ case HFSPLUS_ALLOC_CNID:
+ if (vhdr->alloc_file.total_size != cpu_to_be64(inode->i_size)) {
+ HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP;
+ inode->i_sb->s_dirt = 1;
+ }
+ hfsplus_inode_write_fork(inode, &vhdr->alloc_file);
+ break;
+ case HFSPLUS_START_CNID:
+ if (vhdr->start_file.total_size != cpu_to_be64(inode->i_size)) {
+ HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP;
+ inode->i_sb->s_dirt = 1;
+ }
+ hfsplus_inode_write_fork(inode, &vhdr->start_file);
+ break;
+ case HFSPLUS_ATTR_CNID:
+ if (vhdr->attr_file.total_size != cpu_to_be64(inode->i_size)) {
+ HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP;
+ inode->i_sb->s_dirt = 1;
+ }
+ hfsplus_inode_write_fork(inode, &vhdr->attr_file);
+ hfsplus_write_btree(HFSPLUS_SB(inode->i_sb).attr_tree);
+ break;
+ }
+}
+
+static void hfsplus_clear_inode(struct inode *inode)
+{
+ atomic_dec(&HFSPLUS_SB(inode->i_sb).inode_cnt);
+ if (HFSPLUS_IS_RSRC(inode)) {
+ HFSPLUS_I(HFSPLUS_I(inode).rsrc_inode).rsrc_inode = NULL;
+ iput(HFSPLUS_I(inode).rsrc_inode);
+ }
+ hfsplus_inode_check(inode->i_sb);
+}
+
+static void hfsplus_write_super(struct super_block *sb)
+{
+ struct hfsplus_vh *vhdr = HFSPLUS_SB(sb).s_vhdr;
+
+ dprint(DBG_SUPER, "hfsplus_write_super\n");
+ sb->s_dirt = 0;
+ if (sb->s_flags & MS_RDONLY)
+ /* warn? */
+ return;
+
+ vhdr->free_blocks = cpu_to_be32(HFSPLUS_SB(sb).free_blocks);
+ vhdr->next_alloc = cpu_to_be32(HFSPLUS_SB(sb).next_alloc);
+ vhdr->next_cnid = cpu_to_be32(HFSPLUS_SB(sb).next_cnid);
+ vhdr->folder_count = cpu_to_be32(HFSPLUS_SB(sb).folder_count);
+ vhdr->file_count = cpu_to_be32(HFSPLUS_SB(sb).file_count);
+
+ mark_buffer_dirty(HFSPLUS_SB(sb).s_vhbh);
+ if (HFSPLUS_SB(sb).flags & HFSPLUS_SB_WRITEBACKUP) {
+ if (HFSPLUS_SB(sb).sect_count) {
+ struct buffer_head *bh;
+ u32 block, offset;
+
+ block = HFSPLUS_SB(sb).blockoffset;
+ block += (HFSPLUS_SB(sb).sect_count - 2) >> (sb->s_blocksize_bits - 9);
+ offset = ((HFSPLUS_SB(sb).sect_count - 2) << 9) & (sb->s_blocksize - 1);
+ //printk("backup: %u,%u,%u,%u\n", HFSPLUS_SB(sb).blockoffset,
+ // HFSPLUS_SB(sb).sect_count, block, offset);
+ bh = sb_bread(sb, block);
+ vhdr = (struct hfsplus_vh *)(bh->b_data + offset);
+ if (be16_to_cpu(vhdr->signature) == HFSPLUS_VOLHEAD_SIG) {
+ memcpy(vhdr, HFSPLUS_SB(sb).s_vhdr, sizeof(*vhdr));
+ mark_buffer_dirty(bh);
+ brelse(bh);
+ } else
+ printk("backup not found!\n");
+ }
+ HFSPLUS_SB(sb).flags &= ~HFSPLUS_SB_WRITEBACKUP;
+ }
+}
+
+static void hfsplus_put_super(struct super_block *sb)
+{
+ dprint(DBG_SUPER, "hfsplus_put_super\n");
+ if (!(sb->s_flags & MS_RDONLY)) {
+ struct hfsplus_vh *vhdr = HFSPLUS_SB(sb).s_vhdr;
+
+ vhdr->modify_date = hfsp_now2mt();
+ vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_UNMNT);
+ vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_INCNSTNT);
+ mark_buffer_dirty(HFSPLUS_SB(sb).s_vhbh);
+ ll_rw_block(WRITE, 1, &HFSPLUS_SB(sb).s_vhbh);
+ wait_on_buffer(HFSPLUS_SB(sb).s_vhbh);
+ }
+
+ hfsplus_close_btree(HFSPLUS_SB(sb).cat_tree);
+ hfsplus_close_btree(HFSPLUS_SB(sb).ext_tree);
+ iput(HFSPLUS_SB(sb).alloc_file);
+ iput(HFSPLUS_SB(sb).hidden_dir);
+ brelse(HFSPLUS_SB(sb).s_vhbh);
+}
+
+static int hfsplus_statfs(struct super_block *sb, struct statfs *buf)
+{
+ buf->f_type = HFSPLUS_SUPER_MAGIC;
+ buf->f_bsize = sb->s_blocksize;
+ buf->f_blocks = be32_to_cpu(HFSPLUS_SB(sb).s_vhdr->total_blocks);
+ buf->f_bfree = HFSPLUS_SB(sb).free_blocks;
+ buf->f_bavail = buf->f_bfree;
+ buf->f_files = 0xFFFFFFFF;
+ buf->f_ffree = 0xFFFFFFFF - HFSPLUS_SB(sb).next_cnid;
+ buf->f_namelen = HFSPLUS_MAX_STRLEN;
+
+ return 0;
+}
+
+int hfsplus_remount(struct super_block *sb, int *flags, char *data)
+{
+ if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
+ return 0;
+ if (!(*flags & MS_RDONLY)) {
+ struct hfsplus_vh *vhdr = HFSPLUS_SB(sb).s_vhdr;
+
+ if ((vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_INCNSTNT)) ||
+ !(vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_UNMNT))) {
+ printk("HFS+-fs warning: Filesystem was not cleanly unmounted, "
+ "running fsck.hfsplus is recommended. leaving read-only.\n");
+ sb->s_flags |= MS_RDONLY;
+ *flags |= MS_RDONLY;
+ } else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) {
+ printk("HFS+-fs: Filesystem is marked locked, leaving read-only.\n");
+ sb->s_flags |= MS_RDONLY;
+ *flags |= MS_RDONLY;
+ }
+ }
+ return 0;
+}
+
+static struct super_operations hfsplus_sops = {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+ .alloc_inode = hfsplus_alloc_inode,
+ .destroy_inode = hfsplus_destroy_inode,
+#endif
+ .read_inode = hfsplus_read_inode,
+ .write_inode = hfsplus_write_inode,
+ .clear_inode = hfsplus_clear_inode,
+ .put_super = hfsplus_put_super,
+ .write_super = hfsplus_write_super,
+ .statfs = hfsplus_statfs,
+ .remount_fs = hfsplus_remount,
+};
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+struct super_block *hfsplus_read_super(struct super_block *sb, void *data,
+ int silent)
+#else
+static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
+#endif
+{
+ struct hfsplus_vh *vhdr;
+ struct hfsplus_sb_info *sbi;
+ hfsplus_cat_entry entry;
+ struct hfsplus_find_data fd;
+ struct qstr str;
+ int err = -EINVAL;
+
+ sbi = kmalloc(sizeof(struct hfsplus_sb_info), GFP_KERNEL);
+ if (!sbi) {
+ err = -ENOMEM;
+ goto out2;
+ }
+ memset(sbi, 0, sizeof(HFSPLUS_SB(sb)));
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ if (sizeof(struct inode) - offsetof(struct inode, u) < sizeof(struct hfsplus_inode_info)) {
+ extern void hfsplus_inode_info_exceeded_space_in_inode_error(void);
+ hfsplus_inode_info_exceeded_space_in_inode_error();
+ }
+
+ if (sizeof(struct super_block) - offsetof(struct super_block, u) < sizeof(struct hfsplus_sb_info)) {
+ extern void hfsplus_sb_info_exceeded_space_in_super_block_error(void);
+ hfsplus_sb_info_exceeded_space_in_super_block_error();
+ }
+
+ INIT_LIST_HEAD(&HFSPLUS_SB(sb).rsrc_inodes);
+#else
+ sb->s_fs_info = sbi;
+ INIT_HLIST_HEAD(&sbi->rsrc_inodes);
+#endif
+ fill_defaults(sbi);
+ if (!parse_options(data, sbi)) {
+ if (!silent)
+ printk("HFS+-fs: unable to parse mount options\n");
+ err = -EINVAL;
+ goto out2;
+ }
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ fill_current(sbi, &HFSPLUS_SB(sb));
+ kfree(sbi);
+#endif
+
+ /* Grab the volume header */
+ if (hfsplus_read_wrapper(sb)) {
+ if (!silent)
+ printk("HFS+-fs: unable to find HFS+ superblock\n");
+ err = -EINVAL;
+ goto out2;
+ }
+ vhdr = HFSPLUS_SB(sb).s_vhdr;
+
+ /* Copy parts of the volume header into the superblock */
+ sb->s_magic = be16_to_cpu(vhdr->signature);
+ if (be16_to_cpu(vhdr->version) != HFSPLUS_CURRENT_VERSION) {
+ if (!silent)
+ printk("HFS+-fs: wrong filesystem version\n");
+ goto cleanup;
+ }
+ HFSPLUS_SB(sb).total_blocks = be32_to_cpu(vhdr->total_blocks);
+ HFSPLUS_SB(sb).free_blocks = be32_to_cpu(vhdr->free_blocks);
+ HFSPLUS_SB(sb).next_alloc = be32_to_cpu(vhdr->next_alloc);
+ HFSPLUS_SB(sb).next_cnid = be32_to_cpu(vhdr->next_cnid);
+ HFSPLUS_SB(sb).file_count = be32_to_cpu(vhdr->file_count);
+ HFSPLUS_SB(sb).folder_count = be32_to_cpu(vhdr->folder_count);
+
+ /* Set up operations so we can load metadata */
+ sb->s_op = &hfsplus_sops;
+
+ if ((vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_INCNSTNT)) ||
+ !(vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_UNMNT))) {
+ if (!silent)
+ printk("HFS+-fs warning: Filesystem was not cleanly unmounted, "
+ "running fsck.hfsplus is recommended. mounting read-only.\n");
+ sb->s_flags |= MS_RDONLY;
+ } else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) {
+ if (!silent)
+ printk("HFS+-fs: Filesystem is marked locked, mounting read-only.\n");
+ sb->s_flags |= MS_RDONLY;
+ }
+
+ /* Load metadata objects (B*Trees) */
+ HFSPLUS_SB(sb).ext_tree = hfsplus_open_btree(sb, HFSPLUS_EXT_CNID);
+ if (!HFSPLUS_SB(sb).ext_tree) {
+ if (!silent)
+ printk("HFS+-fs: failed to load extents file\n");
+ goto cleanup;
+ }
+ HFSPLUS_SB(sb).cat_tree = hfsplus_open_btree(sb, HFSPLUS_CAT_CNID);
+ if (!HFSPLUS_SB(sb).cat_tree) {
+ if (!silent)
+ printk("HFS+-fs: failed to load catalog file\n");
+ goto cleanup;
+ }
+
+ HFSPLUS_SB(sb).alloc_file = iget(sb, HFSPLUS_ALLOC_CNID);
+ if (!HFSPLUS_SB(sb).alloc_file) {
+ if (!silent)
+ printk("HFS+-fs: failed to load allocation file\n");
+ goto cleanup;
+ }
+
+ /* Load the root directory */
+ sb->s_root = d_alloc_root(iget(sb, HFSPLUS_ROOT_CNID));
+ if (!sb->s_root) {
+ if (!silent)
+ printk("HFS+-fs: failed to load root directory\n");
+ goto cleanup;
+ }
+
+ str.len = sizeof(HFSP_HIDDENDIR_NAME) - 1;
+ str.name = HFSP_HIDDENDIR_NAME;
+ hfsplus_find_init(HFSPLUS_SB(sb).cat_tree, &fd);
+ hfsplus_fill_cat_key(fd.search_key, HFSPLUS_ROOT_CNID, &str);
+ if (!hfsplus_btree_find_entry(&fd, &entry, sizeof(entry))) {
+ hfsplus_find_exit(&fd);
+ if (entry.type != cpu_to_be16(HFSPLUS_FOLDER))
+ goto cleanup;
+ HFSPLUS_SB(sb).hidden_dir = iget(sb, be32_to_cpu(entry.folder.id));
+ if (!HFSPLUS_SB(sb).hidden_dir)
+ goto cleanup;
+ } else
+ hfsplus_find_exit(&fd);
+
+ if (sb->s_flags & MS_RDONLY)
+ goto out;
+
+ /* H+LX == hfsplusutils, H+Lx == this driver, H+lx is unused
+ * all three are registered with Apple for our use
+ */
+ vhdr->last_mount_vers = cpu_to_be32(HFSP_MOUNT_VERSION);
+ vhdr->modify_date = hfsp_now2mt();
+ vhdr->write_count = cpu_to_be32(be32_to_cpu(vhdr->write_count) + 1);
+ vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_UNMNT);
+ vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_INCNSTNT);
+ mark_buffer_dirty(HFSPLUS_SB(sb).s_vhbh);
+ ll_rw_block(WRITE, 1, &HFSPLUS_SB(sb).s_vhbh);
+ wait_on_buffer(HFSPLUS_SB(sb).s_vhbh);
+
+ if (!HFSPLUS_SB(sb).hidden_dir) {
+ printk("HFS+: create hidden dir...\n");
+ HFSPLUS_SB(sb).hidden_dir = hfsplus_new_inode(sb, S_IFDIR);
+ hfsplus_create_cat(HFSPLUS_SB(sb).hidden_dir->i_ino, sb->s_root->d_inode,
+ &str, HFSPLUS_SB(sb).hidden_dir);
+ mark_inode_dirty(HFSPLUS_SB(sb).hidden_dir);
+ }
+out:
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ return sb;
+#else
+ return 0;
+#endif
+
+cleanup:
+ hfsplus_put_super(sb);
+out2:
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+ return NULL;
+#else
+ return err;
+#endif
+}
+
+MODULE_AUTHOR("Brad Boyer");
+MODULE_DESCRIPTION("Extended Macintosh Filesystem");
+MODULE_LICENSE("GPL");
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+
+static DECLARE_FSTYPE_DEV(hfsplus_fs_type, "hfsplus", hfsplus_read_super);
+
+static int __init init_hfsplus_fs(void)
+{
+ return register_filesystem(&hfsplus_fs_type);
+}
+
+static void __exit exit_hfsplus_fs(void)
+{
+ unregister_filesystem(&hfsplus_fs_type);
+}
+
+EXPORT_NO_SYMBOLS;
+
+#else
+
+static kmem_cache_t * hfsplus_inode_cachep;
+
+static void init_once(void *p, kmem_cache_t *cachep, unsigned long flags)
+{
+ struct hfsplus_inode_info *i = (struct hfsplus_inode_info *)p;
+
+ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == SLAB_CTOR_CONSTRUCTOR) {
+ inode_init_once(&i->vfs_inode);
+ }
+}
+
+static struct inode *hfsplus_alloc_inode(struct super_block *sb)
+{
+ struct hfsplus_inode_info *i;
+ i = (struct hfsplus_inode_info *)kmem_cache_alloc(hfsplus_inode_cachep, SLAB_KERNEL);
+ return i ? &i->vfs_inode : NULL;
+}
+
+static void hfsplus_destroy_inode(struct inode *inode)
+{
+ kmem_cache_free(hfsplus_inode_cachep, &HFSPLUS_I(inode));
+}
+
+static struct super_block *hfsplus_get_sb(struct file_system_type *fs_type,
+ int flags, char *dev_name, void *data)
+{
+ return get_sb_bdev(fs_type, flags, dev_name, data, hfsplus_fill_super);
+}
+
+static struct file_system_type hfsplus_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "hfsplus",
+ .get_sb = hfsplus_get_sb,
+ .kill_sb = kill_block_super,
+ .fs_flags = FS_REQUIRES_DEV,
+};
+
+static int __init init_hfsplus_fs(void)
+{
+ int err;
+
+ hfsplus_inode_cachep = kmem_cache_create("hfsplus_inode_cache",
+ sizeof(struct hfsplus_inode_info), 0, SLAB_HWCACHE_ALIGN,
+ init_once, NULL);
+ if (!hfsplus_inode_cachep)
+ return -ENOMEM;
+ err = register_filesystem(&hfsplus_fs_type);
+ if (err)
+ kmem_cache_destroy(hfsplus_inode_cachep);
+ return err;
+}
+
+static void __exit exit_hfsplus_fs(void)
+{
+ unregister_filesystem(&hfsplus_fs_type);
+ if (kmem_cache_destroy(hfsplus_inode_cachep))
+ printk(KERN_INFO "hfsplus_inode_cache: not all structures were freed\n");
+}
+#endif
+
+module_init(init_hfsplus_fs)
+module_exit(exit_hfsplus_fs)
diff --git a/fs/hfsplus/tables.c b/fs/hfsplus/tables.c
new file mode 100644
index 00000000000000..ce2ed6657715a2
--- /dev/null
+++ b/fs/hfsplus/tables.c
@@ -0,0 +1,408 @@
+/*
+ * linux/fs/hfsplus/tables.c
+ *
+ * Various data tables
+ */
+
+#include "hfsplus_fs.h"
+
+/*
+ * Unicode case folding table taken from Apple Technote #1150
+ * (HFS Plus Volume Format)
+ */
+
+u16 case_fold_table[] = {
+/*
+ * The lower case table consists of a 256-entry high-byte table followed by
+ * some number of 256-entry subtables. The high-byte table contains either an
+ * offset to the subtable for characters with that high byte or zero, which
+ * means that there are no case mappings or ignored characters in that block.
+ * Ignored characters are mapped to zero.
+ */
+
+ // High-byte indices ( == 0 iff no case mapping and no ignorables )
+
+
+ /* 0 */ 0x0100, 0x0200, 0x0000, 0x0300, 0x0400, 0x0500, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 1 */ 0x0600, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 2 */ 0x0700, 0x0800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 3 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 4 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 5 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 6 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 7 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 8 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 9 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* A */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* B */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* C */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* D */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* E */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* F */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0900, 0x0A00,
+
+ // Table 1 (for high byte 0x00)
+
+ /* 0 */ 0xFFFF, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
+ /* 1 */ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
+ /* 2 */ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
+ /* 3 */ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
+ /* 4 */ 0x0040, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+ /* 5 */ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
+ /* 6 */ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+ /* 7 */ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
+ /* 8 */ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,
+ 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
+ /* 9 */ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,
+ 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
+ /* A */ 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
+ 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
+ /* B */ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
+ 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
+ /* C */ 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00E6, 0x00C7,
+ 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
+ /* D */ 0x00F0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7,
+ 0x00F8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00FE, 0x00DF,
+ /* E */ 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
+ 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
+ /* F */ 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7,
+ 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF,
+
+ // Table 2 (for high byte 0x01)
+
+ /* 0 */ 0x0100, 0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107,
+ 0x0108, 0x0109, 0x010A, 0x010B, 0x010C, 0x010D, 0x010E, 0x010F,
+ /* 1 */ 0x0111, 0x0111, 0x0112, 0x0113, 0x0114, 0x0115, 0x0116, 0x0117,
+ 0x0118, 0x0119, 0x011A, 0x011B, 0x011C, 0x011D, 0x011E, 0x011F,
+ /* 2 */ 0x0120, 0x0121, 0x0122, 0x0123, 0x0124, 0x0125, 0x0127, 0x0127,
+ 0x0128, 0x0129, 0x012A, 0x012B, 0x012C, 0x012D, 0x012E, 0x012F,
+ /* 3 */ 0x0130, 0x0131, 0x0133, 0x0133, 0x0134, 0x0135, 0x0136, 0x0137,
+ 0x0138, 0x0139, 0x013A, 0x013B, 0x013C, 0x013D, 0x013E, 0x0140,
+ /* 4 */ 0x0140, 0x0142, 0x0142, 0x0143, 0x0144, 0x0145, 0x0146, 0x0147,
+ 0x0148, 0x0149, 0x014B, 0x014B, 0x014C, 0x014D, 0x014E, 0x014F,
+ /* 5 */ 0x0150, 0x0151, 0x0153, 0x0153, 0x0154, 0x0155, 0x0156, 0x0157,
+ 0x0158, 0x0159, 0x015A, 0x015B, 0x015C, 0x015D, 0x015E, 0x015F,
+ /* 6 */ 0x0160, 0x0161, 0x0162, 0x0163, 0x0164, 0x0165, 0x0167, 0x0167,
+ 0x0168, 0x0169, 0x016A, 0x016B, 0x016C, 0x016D, 0x016E, 0x016F,
+ /* 7 */ 0x0170, 0x0171, 0x0172, 0x0173, 0x0174, 0x0175, 0x0176, 0x0177,
+ 0x0178, 0x0179, 0x017A, 0x017B, 0x017C, 0x017D, 0x017E, 0x017F,
+ /* 8 */ 0x0180, 0x0253, 0x0183, 0x0183, 0x0185, 0x0185, 0x0254, 0x0188,
+ 0x0188, 0x0256, 0x0257, 0x018C, 0x018C, 0x018D, 0x01DD, 0x0259,
+ /* 9 */ 0x025B, 0x0192, 0x0192, 0x0260, 0x0263, 0x0195, 0x0269, 0x0268,
+ 0x0199, 0x0199, 0x019A, 0x019B, 0x026F, 0x0272, 0x019E, 0x0275,
+ /* A */ 0x01A0, 0x01A1, 0x01A3, 0x01A3, 0x01A5, 0x01A5, 0x01A6, 0x01A8,
+ 0x01A8, 0x0283, 0x01AA, 0x01AB, 0x01AD, 0x01AD, 0x0288, 0x01AF,
+ /* B */ 0x01B0, 0x028A, 0x028B, 0x01B4, 0x01B4, 0x01B6, 0x01B6, 0x0292,
+ 0x01B9, 0x01B9, 0x01BA, 0x01BB, 0x01BD, 0x01BD, 0x01BE, 0x01BF,
+ /* C */ 0x01C0, 0x01C1, 0x01C2, 0x01C3, 0x01C6, 0x01C6, 0x01C6, 0x01C9,
+ 0x01C9, 0x01C9, 0x01CC, 0x01CC, 0x01CC, 0x01CD, 0x01CE, 0x01CF,
+ /* D */ 0x01D0, 0x01D1, 0x01D2, 0x01D3, 0x01D4, 0x01D5, 0x01D6, 0x01D7,
+ 0x01D8, 0x01D9, 0x01DA, 0x01DB, 0x01DC, 0x01DD, 0x01DE, 0x01DF,
+ /* E */ 0x01E0, 0x01E1, 0x01E2, 0x01E3, 0x01E5, 0x01E5, 0x01E6, 0x01E7,
+ 0x01E8, 0x01E9, 0x01EA, 0x01EB, 0x01EC, 0x01ED, 0x01EE, 0x01EF,
+ /* F */ 0x01F0, 0x01F3, 0x01F3, 0x01F3, 0x01F4, 0x01F5, 0x01F6, 0x01F7,
+ 0x01F8, 0x01F9, 0x01FA, 0x01FB, 0x01FC, 0x01FD, 0x01FE, 0x01FF,
+
+ // Table 3 (for high byte 0x03)
+
+ /* 0 */ 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307,
+ 0x0308, 0x0309, 0x030A, 0x030B, 0x030C, 0x030D, 0x030E, 0x030F,
+ /* 1 */ 0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317,
+ 0x0318, 0x0319, 0x031A, 0x031B, 0x031C, 0x031D, 0x031E, 0x031F,
+ /* 2 */ 0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x0326, 0x0327,
+ 0x0328, 0x0329, 0x032A, 0x032B, 0x032C, 0x032D, 0x032E, 0x032F,
+ /* 3 */ 0x0330, 0x0331, 0x0332, 0x0333, 0x0334, 0x0335, 0x0336, 0x0337,
+ 0x0338, 0x0339, 0x033A, 0x033B, 0x033C, 0x033D, 0x033E, 0x033F,
+ /* 4 */ 0x0340, 0x0341, 0x0342, 0x0343, 0x0344, 0x0345, 0x0346, 0x0347,
+ 0x0348, 0x0349, 0x034A, 0x034B, 0x034C, 0x034D, 0x034E, 0x034F,
+ /* 5 */ 0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357,
+ 0x0358, 0x0359, 0x035A, 0x035B, 0x035C, 0x035D, 0x035E, 0x035F,
+ /* 6 */ 0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367,
+ 0x0368, 0x0369, 0x036A, 0x036B, 0x036C, 0x036D, 0x036E, 0x036F,
+ /* 7 */ 0x0370, 0x0371, 0x0372, 0x0373, 0x0374, 0x0375, 0x0376, 0x0377,
+ 0x0378, 0x0379, 0x037A, 0x037B, 0x037C, 0x037D, 0x037E, 0x037F,
+ /* 8 */ 0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x0386, 0x0387,
+ 0x0388, 0x0389, 0x038A, 0x038B, 0x038C, 0x038D, 0x038E, 0x038F,
+ /* 9 */ 0x0390, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7,
+ 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,
+ /* A */ 0x03C0, 0x03C1, 0x03A2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7,
+ 0x03C8, 0x03C9, 0x03AA, 0x03AB, 0x03AC, 0x03AD, 0x03AE, 0x03AF,
+ /* B */ 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7,
+ 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,
+ /* C */ 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7,
+ 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0x03CF,
+ /* D */ 0x03D0, 0x03D1, 0x03D2, 0x03D3, 0x03D4, 0x03D5, 0x03D6, 0x03D7,
+ 0x03D8, 0x03D9, 0x03DA, 0x03DB, 0x03DC, 0x03DD, 0x03DE, 0x03DF,
+ /* E */ 0x03E0, 0x03E1, 0x03E3, 0x03E3, 0x03E5, 0x03E5, 0x03E7, 0x03E7,
+ 0x03E9, 0x03E9, 0x03EB, 0x03EB, 0x03ED, 0x03ED, 0x03EF, 0x03EF,
+ /* F */ 0x03F0, 0x03F1, 0x03F2, 0x03F3, 0x03F4, 0x03F5, 0x03F6, 0x03F7,
+ 0x03F8, 0x03F9, 0x03FA, 0x03FB, 0x03FC, 0x03FD, 0x03FE, 0x03FF,
+
+ // Table 4 (for high byte 0x04)
+
+ /* 0 */ 0x0400, 0x0401, 0x0452, 0x0403, 0x0454, 0x0455, 0x0456, 0x0407,
+ 0x0458, 0x0459, 0x045A, 0x045B, 0x040C, 0x040D, 0x040E, 0x045F,
+ /* 1 */ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,
+ 0x0438, 0x0419, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
+ /* 2 */ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,
+ 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
+ /* 3 */ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,
+ 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
+ /* 4 */ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,
+ 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
+ /* 5 */ 0x0450, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457,
+ 0x0458, 0x0459, 0x045A, 0x045B, 0x045C, 0x045D, 0x045E, 0x045F,
+ /* 6 */ 0x0461, 0x0461, 0x0463, 0x0463, 0x0465, 0x0465, 0x0467, 0x0467,
+ 0x0469, 0x0469, 0x046B, 0x046B, 0x046D, 0x046D, 0x046F, 0x046F,
+ /* 7 */ 0x0471, 0x0471, 0x0473, 0x0473, 0x0475, 0x0475, 0x0476, 0x0477,
+ 0x0479, 0x0479, 0x047B, 0x047B, 0x047D, 0x047D, 0x047F, 0x047F,
+ /* 8 */ 0x0481, 0x0481, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487,
+ 0x0488, 0x0489, 0x048A, 0x048B, 0x048C, 0x048D, 0x048E, 0x048F,
+ /* 9 */ 0x0491, 0x0491, 0x0493, 0x0493, 0x0495, 0x0495, 0x0497, 0x0497,
+ 0x0499, 0x0499, 0x049B, 0x049B, 0x049D, 0x049D, 0x049F, 0x049F,
+ /* A */ 0x04A1, 0x04A1, 0x04A3, 0x04A3, 0x04A5, 0x04A5, 0x04A7, 0x04A7,
+ 0x04A9, 0x04A9, 0x04AB, 0x04AB, 0x04AD, 0x04AD, 0x04AF, 0x04AF,
+ /* B */ 0x04B1, 0x04B1, 0x04B3, 0x04B3, 0x04B5, 0x04B5, 0x04B7, 0x04B7,
+ 0x04B9, 0x04B9, 0x04BB, 0x04BB, 0x04BD, 0x04BD, 0x04BF, 0x04BF,
+ /* C */ 0x04C0, 0x04C1, 0x04C2, 0x04C4, 0x04C4, 0x04C5, 0x04C6, 0x04C8,
+ 0x04C8, 0x04C9, 0x04CA, 0x04CC, 0x04CC, 0x04CD, 0x04CE, 0x04CF,
+ /* D */ 0x04D0, 0x04D1, 0x04D2, 0x04D3, 0x04D4, 0x04D5, 0x04D6, 0x04D7,
+ 0x04D8, 0x04D9, 0x04DA, 0x04DB, 0x04DC, 0x04DD, 0x04DE, 0x04DF,
+ /* E */ 0x04E0, 0x04E1, 0x04E2, 0x04E3, 0x04E4, 0x04E5, 0x04E6, 0x04E7,
+ 0x04E8, 0x04E9, 0x04EA, 0x04EB, 0x04EC, 0x04ED, 0x04EE, 0x04EF,
+ /* F */ 0x04F0, 0x04F1, 0x04F2, 0x04F3, 0x04F4, 0x04F5, 0x04F6, 0x04F7,
+ 0x04F8, 0x04F9, 0x04FA, 0x04FB, 0x04FC, 0x04FD, 0x04FE, 0x04FF,
+
+ // Table 5 (for high byte 0x05)
+
+ /* 0 */ 0x0500, 0x0501, 0x0502, 0x0503, 0x0504, 0x0505, 0x0506, 0x0507,
+ 0x0508, 0x0509, 0x050A, 0x050B, 0x050C, 0x050D, 0x050E, 0x050F,
+ /* 1 */ 0x0510, 0x0511, 0x0512, 0x0513, 0x0514, 0x0515, 0x0516, 0x0517,
+ 0x0518, 0x0519, 0x051A, 0x051B, 0x051C, 0x051D, 0x051E, 0x051F,
+ /* 2 */ 0x0520, 0x0521, 0x0522, 0x0523, 0x0524, 0x0525, 0x0526, 0x0527,
+ 0x0528, 0x0529, 0x052A, 0x052B, 0x052C, 0x052D, 0x052E, 0x052F,
+ /* 3 */ 0x0530, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567,
+ 0x0568, 0x0569, 0x056A, 0x056B, 0x056C, 0x056D, 0x056E, 0x056F,
+ /* 4 */ 0x0570, 0x0571, 0x0572, 0x0573, 0x0574, 0x0575, 0x0576, 0x0577,
+ 0x0578, 0x0579, 0x057A, 0x057B, 0x057C, 0x057D, 0x057E, 0x057F,
+ /* 5 */ 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0586, 0x0557,
+ 0x0558, 0x0559, 0x055A, 0x055B, 0x055C, 0x055D, 0x055E, 0x055F,
+ /* 6 */ 0x0560, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567,
+ 0x0568, 0x0569, 0x056A, 0x056B, 0x056C, 0x056D, 0x056E, 0x056F,
+ /* 7 */ 0x0570, 0x0571, 0x0572, 0x0573, 0x0574, 0x0575, 0x0576, 0x0577,
+ 0x0578, 0x0579, 0x057A, 0x057B, 0x057C, 0x057D, 0x057E, 0x057F,
+ /* 8 */ 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0586, 0x0587,
+ 0x0588, 0x0589, 0x058A, 0x058B, 0x058C, 0x058D, 0x058E, 0x058F,
+ /* 9 */ 0x0590, 0x0591, 0x0592, 0x0593, 0x0594, 0x0595, 0x0596, 0x0597,
+ 0x0598, 0x0599, 0x059A, 0x059B, 0x059C, 0x059D, 0x059E, 0x059F,
+ /* A */ 0x05A0, 0x05A1, 0x05A2, 0x05A3, 0x05A4, 0x05A5, 0x05A6, 0x05A7,
+ 0x05A8, 0x05A9, 0x05AA, 0x05AB, 0x05AC, 0x05AD, 0x05AE, 0x05AF,
+ /* B */ 0x05B0, 0x05B1, 0x05B2, 0x05B3, 0x05B4, 0x05B5, 0x05B6, 0x05B7,
+ 0x05B8, 0x05B9, 0x05BA, 0x05BB, 0x05BC, 0x05BD, 0x05BE, 0x05BF,
+ /* C */ 0x05C0, 0x05C1, 0x05C2, 0x05C3, 0x05C4, 0x05C5, 0x05C6, 0x05C7,
+ 0x05C8, 0x05C9, 0x05CA, 0x05CB, 0x05CC, 0x05CD, 0x05CE, 0x05CF,
+ /* D */ 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7,
+ 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,
+ /* E */ 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7,
+ 0x05E8, 0x05E9, 0x05EA, 0x05EB, 0x05EC, 0x05ED, 0x05EE, 0x05EF,
+ /* F */ 0x05F0, 0x05F1, 0x05F2, 0x05F3, 0x05F4, 0x05F5, 0x05F6, 0x05F7,
+ 0x05F8, 0x05F9, 0x05FA, 0x05FB, 0x05FC, 0x05FD, 0x05FE, 0x05FF,
+
+ // Table 6 (for high byte 0x10)
+
+ /* 0 */ 0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, 0x1006, 0x1007,
+ 0x1008, 0x1009, 0x100A, 0x100B, 0x100C, 0x100D, 0x100E, 0x100F,
+ /* 1 */ 0x1010, 0x1011, 0x1012, 0x1013, 0x1014, 0x1015, 0x1016, 0x1017,
+ 0x1018, 0x1019, 0x101A, 0x101B, 0x101C, 0x101D, 0x101E, 0x101F,
+ /* 2 */ 0x1020, 0x1021, 0x1022, 0x1023, 0x1024, 0x1025, 0x1026, 0x1027,
+ 0x1028, 0x1029, 0x102A, 0x102B, 0x102C, 0x102D, 0x102E, 0x102F,
+ /* 3 */ 0x1030, 0x1031, 0x1032, 0x1033, 0x1034, 0x1035, 0x1036, 0x1037,
+ 0x1038, 0x1039, 0x103A, 0x103B, 0x103C, 0x103D, 0x103E, 0x103F,
+ /* 4 */ 0x1040, 0x1041, 0x1042, 0x1043, 0x1044, 0x1045, 0x1046, 0x1047,
+ 0x1048, 0x1049, 0x104A, 0x104B, 0x104C, 0x104D, 0x104E, 0x104F,
+ /* 5 */ 0x1050, 0x1051, 0x1052, 0x1053, 0x1054, 0x1055, 0x1056, 0x1057,
+ 0x1058, 0x1059, 0x105A, 0x105B, 0x105C, 0x105D, 0x105E, 0x105F,
+ /* 6 */ 0x1060, 0x1061, 0x1062, 0x1063, 0x1064, 0x1065, 0x1066, 0x1067,
+ 0x1068, 0x1069, 0x106A, 0x106B, 0x106C, 0x106D, 0x106E, 0x106F,
+ /* 7 */ 0x1070, 0x1071, 0x1072, 0x1073, 0x1074, 0x1075, 0x1076, 0x1077,
+ 0x1078, 0x1079, 0x107A, 0x107B, 0x107C, 0x107D, 0x107E, 0x107F,
+ /* 8 */ 0x1080, 0x1081, 0x1082, 0x1083, 0x1084, 0x1085, 0x1086, 0x1087,
+ 0x1088, 0x1089, 0x108A, 0x108B, 0x108C, 0x108D, 0x108E, 0x108F,
+ /* 9 */ 0x1090, 0x1091, 0x1092, 0x1093, 0x1094, 0x1095, 0x1096, 0x1097,
+ 0x1098, 0x1099, 0x109A, 0x109B, 0x109C, 0x109D, 0x109E, 0x109F,
+ /* A */ 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10D7,
+ 0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10DD, 0x10DE, 0x10DF,
+ /* B */ 0x10E0, 0x10E1, 0x10E2, 0x10E3, 0x10E4, 0x10E5, 0x10E6, 0x10E7,
+ 0x10E8, 0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10EF,
+ /* C */ 0x10F0, 0x10F1, 0x10F2, 0x10F3, 0x10F4, 0x10F5, 0x10C6, 0x10C7,
+ 0x10C8, 0x10C9, 0x10CA, 0x10CB, 0x10CC, 0x10CD, 0x10CE, 0x10CF,
+ /* D */ 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10D7,
+ 0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10DD, 0x10DE, 0x10DF,
+ /* E */ 0x10E0, 0x10E1, 0x10E2, 0x10E3, 0x10E4, 0x10E5, 0x10E6, 0x10E7,
+ 0x10E8, 0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10EF,
+ /* F */ 0x10F0, 0x10F1, 0x10F2, 0x10F3, 0x10F4, 0x10F5, 0x10F6, 0x10F7,
+ 0x10F8, 0x10F9, 0x10FA, 0x10FB, 0x10FC, 0x10FD, 0x10FE, 0x10FF,
+
+ // Table 7 (for high byte 0x20)
+
+ /* 0 */ 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007,
+ 0x2008, 0x2009, 0x200A, 0x200B, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 1 */ 0x2010, 0x2011, 0x2012, 0x2013, 0x2014, 0x2015, 0x2016, 0x2017,
+ 0x2018, 0x2019, 0x201A, 0x201B, 0x201C, 0x201D, 0x201E, 0x201F,
+ /* 2 */ 0x2020, 0x2021, 0x2022, 0x2023, 0x2024, 0x2025, 0x2026, 0x2027,
+ 0x2028, 0x2029, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x202F,
+ /* 3 */ 0x2030, 0x2031, 0x2032, 0x2033, 0x2034, 0x2035, 0x2036, 0x2037,
+ 0x2038, 0x2039, 0x203A, 0x203B, 0x203C, 0x203D, 0x203E, 0x203F,
+ /* 4 */ 0x2040, 0x2041, 0x2042, 0x2043, 0x2044, 0x2045, 0x2046, 0x2047,
+ 0x2048, 0x2049, 0x204A, 0x204B, 0x204C, 0x204D, 0x204E, 0x204F,
+ /* 5 */ 0x2050, 0x2051, 0x2052, 0x2053, 0x2054, 0x2055, 0x2056, 0x2057,
+ 0x2058, 0x2059, 0x205A, 0x205B, 0x205C, 0x205D, 0x205E, 0x205F,
+ /* 6 */ 0x2060, 0x2061, 0x2062, 0x2063, 0x2064, 0x2065, 0x2066, 0x2067,
+ 0x2068, 0x2069, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 7 */ 0x2070, 0x2071, 0x2072, 0x2073, 0x2074, 0x2075, 0x2076, 0x2077,
+ 0x2078, 0x2079, 0x207A, 0x207B, 0x207C, 0x207D, 0x207E, 0x207F,
+ /* 8 */ 0x2080, 0x2081, 0x2082, 0x2083, 0x2084, 0x2085, 0x2086, 0x2087,
+ 0x2088, 0x2089, 0x208A, 0x208B, 0x208C, 0x208D, 0x208E, 0x208F,
+ /* 9 */ 0x2090, 0x2091, 0x2092, 0x2093, 0x2094, 0x2095, 0x2096, 0x2097,
+ 0x2098, 0x2099, 0x209A, 0x209B, 0x209C, 0x209D, 0x209E, 0x209F,
+ /* A */ 0x20A0, 0x20A1, 0x20A2, 0x20A3, 0x20A4, 0x20A5, 0x20A6, 0x20A7,
+ 0x20A8, 0x20A9, 0x20AA, 0x20AB, 0x20AC, 0x20AD, 0x20AE, 0x20AF,
+ /* B */ 0x20B0, 0x20B1, 0x20B2, 0x20B3, 0x20B4, 0x20B5, 0x20B6, 0x20B7,
+ 0x20B8, 0x20B9, 0x20BA, 0x20BB, 0x20BC, 0x20BD, 0x20BE, 0x20BF,
+ /* C */ 0x20C0, 0x20C1, 0x20C2, 0x20C3, 0x20C4, 0x20C5, 0x20C6, 0x20C7,
+ 0x20C8, 0x20C9, 0x20CA, 0x20CB, 0x20CC, 0x20CD, 0x20CE, 0x20CF,
+ /* D */ 0x20D0, 0x20D1, 0x20D2, 0x20D3, 0x20D4, 0x20D5, 0x20D6, 0x20D7,
+ 0x20D8, 0x20D9, 0x20DA, 0x20DB, 0x20DC, 0x20DD, 0x20DE, 0x20DF,
+ /* E */ 0x20E0, 0x20E1, 0x20E2, 0x20E3, 0x20E4, 0x20E5, 0x20E6, 0x20E7,
+ 0x20E8, 0x20E9, 0x20EA, 0x20EB, 0x20EC, 0x20ED, 0x20EE, 0x20EF,
+ /* F */ 0x20F0, 0x20F1, 0x20F2, 0x20F3, 0x20F4, 0x20F5, 0x20F6, 0x20F7,
+ 0x20F8, 0x20F9, 0x20FA, 0x20FB, 0x20FC, 0x20FD, 0x20FE, 0x20FF,
+
+ // Table 8 (for high byte 0x21)
+
+ /* 0 */ 0x2100, 0x2101, 0x2102, 0x2103, 0x2104, 0x2105, 0x2106, 0x2107,
+ 0x2108, 0x2109, 0x210A, 0x210B, 0x210C, 0x210D, 0x210E, 0x210F,
+ /* 1 */ 0x2110, 0x2111, 0x2112, 0x2113, 0x2114, 0x2115, 0x2116, 0x2117,
+ 0x2118, 0x2119, 0x211A, 0x211B, 0x211C, 0x211D, 0x211E, 0x211F,
+ /* 2 */ 0x2120, 0x2121, 0x2122, 0x2123, 0x2124, 0x2125, 0x2126, 0x2127,
+ 0x2128, 0x2129, 0x212A, 0x212B, 0x212C, 0x212D, 0x212E, 0x212F,
+ /* 3 */ 0x2130, 0x2131, 0x2132, 0x2133, 0x2134, 0x2135, 0x2136, 0x2137,
+ 0x2138, 0x2139, 0x213A, 0x213B, 0x213C, 0x213D, 0x213E, 0x213F,
+ /* 4 */ 0x2140, 0x2141, 0x2142, 0x2143, 0x2144, 0x2145, 0x2146, 0x2147,
+ 0x2148, 0x2149, 0x214A, 0x214B, 0x214C, 0x214D, 0x214E, 0x214F,
+ /* 5 */ 0x2150, 0x2151, 0x2152, 0x2153, 0x2154, 0x2155, 0x2156, 0x2157,
+ 0x2158, 0x2159, 0x215A, 0x215B, 0x215C, 0x215D, 0x215E, 0x215F,
+ /* 6 */ 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177,
+ 0x2178, 0x2179, 0x217A, 0x217B, 0x217C, 0x217D, 0x217E, 0x217F,
+ /* 7 */ 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177,
+ 0x2178, 0x2179, 0x217A, 0x217B, 0x217C, 0x217D, 0x217E, 0x217F,
+ /* 8 */ 0x2180, 0x2181, 0x2182, 0x2183, 0x2184, 0x2185, 0x2186, 0x2187,
+ 0x2188, 0x2189, 0x218A, 0x218B, 0x218C, 0x218D, 0x218E, 0x218F,
+ /* 9 */ 0x2190, 0x2191, 0x2192, 0x2193, 0x2194, 0x2195, 0x2196, 0x2197,
+ 0x2198, 0x2199, 0x219A, 0x219B, 0x219C, 0x219D, 0x219E, 0x219F,
+ /* A */ 0x21A0, 0x21A1, 0x21A2, 0x21A3, 0x21A4, 0x21A5, 0x21A6, 0x21A7,
+ 0x21A8, 0x21A9, 0x21AA, 0x21AB, 0x21AC, 0x21AD, 0x21AE, 0x21AF,
+ /* B */ 0x21B0, 0x21B1, 0x21B2, 0x21B3, 0x21B4, 0x21B5, 0x21B6, 0x21B7,
+ 0x21B8, 0x21B9, 0x21BA, 0x21BB, 0x21BC, 0x21BD, 0x21BE, 0x21BF,
+ /* C */ 0x21C0, 0x21C1, 0x21C2, 0x21C3, 0x21C4, 0x21C5, 0x21C6, 0x21C7,
+ 0x21C8, 0x21C9, 0x21CA, 0x21CB, 0x21CC, 0x21CD, 0x21CE, 0x21CF,
+ /* D */ 0x21D0, 0x21D1, 0x21D2, 0x21D3, 0x21D4, 0x21D5, 0x21D6, 0x21D7,
+ 0x21D8, 0x21D9, 0x21DA, 0x21DB, 0x21DC, 0x21DD, 0x21DE, 0x21DF,
+ /* E */ 0x21E0, 0x21E1, 0x21E2, 0x21E3, 0x21E4, 0x21E5, 0x21E6, 0x21E7,
+ 0x21E8, 0x21E9, 0x21EA, 0x21EB, 0x21EC, 0x21ED, 0x21EE, 0x21EF,
+ /* F */ 0x21F0, 0x21F1, 0x21F2, 0x21F3, 0x21F4, 0x21F5, 0x21F6, 0x21F7,
+ 0x21F8, 0x21F9, 0x21FA, 0x21FB, 0x21FC, 0x21FD, 0x21FE, 0x21FF,
+
+ // Table 9 (for high byte 0xFE)
+
+ /* 0 */ 0xFE00, 0xFE01, 0xFE02, 0xFE03, 0xFE04, 0xFE05, 0xFE06, 0xFE07,
+ 0xFE08, 0xFE09, 0xFE0A, 0xFE0B, 0xFE0C, 0xFE0D, 0xFE0E, 0xFE0F,
+ /* 1 */ 0xFE10, 0xFE11, 0xFE12, 0xFE13, 0xFE14, 0xFE15, 0xFE16, 0xFE17,
+ 0xFE18, 0xFE19, 0xFE1A, 0xFE1B, 0xFE1C, 0xFE1D, 0xFE1E, 0xFE1F,
+ /* 2 */ 0xFE20, 0xFE21, 0xFE22, 0xFE23, 0xFE24, 0xFE25, 0xFE26, 0xFE27,
+ 0xFE28, 0xFE29, 0xFE2A, 0xFE2B, 0xFE2C, 0xFE2D, 0xFE2E, 0xFE2F,
+ /* 3 */ 0xFE30, 0xFE31, 0xFE32, 0xFE33, 0xFE34, 0xFE35, 0xFE36, 0xFE37,
+ 0xFE38, 0xFE39, 0xFE3A, 0xFE3B, 0xFE3C, 0xFE3D, 0xFE3E, 0xFE3F,
+ /* 4 */ 0xFE40, 0xFE41, 0xFE42, 0xFE43, 0xFE44, 0xFE45, 0xFE46, 0xFE47,
+ 0xFE48, 0xFE49, 0xFE4A, 0xFE4B, 0xFE4C, 0xFE4D, 0xFE4E, 0xFE4F,
+ /* 5 */ 0xFE50, 0xFE51, 0xFE52, 0xFE53, 0xFE54, 0xFE55, 0xFE56, 0xFE57,
+ 0xFE58, 0xFE59, 0xFE5A, 0xFE5B, 0xFE5C, 0xFE5D, 0xFE5E, 0xFE5F,
+ /* 6 */ 0xFE60, 0xFE61, 0xFE62, 0xFE63, 0xFE64, 0xFE65, 0xFE66, 0xFE67,
+ 0xFE68, 0xFE69, 0xFE6A, 0xFE6B, 0xFE6C, 0xFE6D, 0xFE6E, 0xFE6F,
+ /* 7 */ 0xFE70, 0xFE71, 0xFE72, 0xFE73, 0xFE74, 0xFE75, 0xFE76, 0xFE77,
+ 0xFE78, 0xFE79, 0xFE7A, 0xFE7B, 0xFE7C, 0xFE7D, 0xFE7E, 0xFE7F,
+ /* 8 */ 0xFE80, 0xFE81, 0xFE82, 0xFE83, 0xFE84, 0xFE85, 0xFE86, 0xFE87,
+ 0xFE88, 0xFE89, 0xFE8A, 0xFE8B, 0xFE8C, 0xFE8D, 0xFE8E, 0xFE8F,
+ /* 9 */ 0xFE90, 0xFE91, 0xFE92, 0xFE93, 0xFE94, 0xFE95, 0xFE96, 0xFE97,
+ 0xFE98, 0xFE99, 0xFE9A, 0xFE9B, 0xFE9C, 0xFE9D, 0xFE9E, 0xFE9F,
+ /* A */ 0xFEA0, 0xFEA1, 0xFEA2, 0xFEA3, 0xFEA4, 0xFEA5, 0xFEA6, 0xFEA7,
+ 0xFEA8, 0xFEA9, 0xFEAA, 0xFEAB, 0xFEAC, 0xFEAD, 0xFEAE, 0xFEAF,
+ /* B */ 0xFEB0, 0xFEB1, 0xFEB2, 0xFEB3, 0xFEB4, 0xFEB5, 0xFEB6, 0xFEB7,
+ 0xFEB8, 0xFEB9, 0xFEBA, 0xFEBB, 0xFEBC, 0xFEBD, 0xFEBE, 0xFEBF,
+ /* C */ 0xFEC0, 0xFEC1, 0xFEC2, 0xFEC3, 0xFEC4, 0xFEC5, 0xFEC6, 0xFEC7,
+ 0xFEC8, 0xFEC9, 0xFECA, 0xFECB, 0xFECC, 0xFECD, 0xFECE, 0xFECF,
+ /* D */ 0xFED0, 0xFED1, 0xFED2, 0xFED3, 0xFED4, 0xFED5, 0xFED6, 0xFED7,
+ 0xFED8, 0xFED9, 0xFEDA, 0xFEDB, 0xFEDC, 0xFEDD, 0xFEDE, 0xFEDF,
+ /* E */ 0xFEE0, 0xFEE1, 0xFEE2, 0xFEE3, 0xFEE4, 0xFEE5, 0xFEE6, 0xFEE7,
+ 0xFEE8, 0xFEE9, 0xFEEA, 0xFEEB, 0xFEEC, 0xFEED, 0xFEEE, 0xFEEF,
+ /* F */ 0xFEF0, 0xFEF1, 0xFEF2, 0xFEF3, 0xFEF4, 0xFEF5, 0xFEF6, 0xFEF7,
+ 0xFEF8, 0xFEF9, 0xFEFA, 0xFEFB, 0xFEFC, 0xFEFD, 0xFEFE, 0x0000,
+
+ // Table 10 (for high byte 0xFF)
+
+ /* 0 */ 0xFF00, 0xFF01, 0xFF02, 0xFF03, 0xFF04, 0xFF05, 0xFF06, 0xFF07,
+ 0xFF08, 0xFF09, 0xFF0A, 0xFF0B, 0xFF0C, 0xFF0D, 0xFF0E, 0xFF0F,
+ /* 1 */ 0xFF10, 0xFF11, 0xFF12, 0xFF13, 0xFF14, 0xFF15, 0xFF16, 0xFF17,
+ 0xFF18, 0xFF19, 0xFF1A, 0xFF1B, 0xFF1C, 0xFF1D, 0xFF1E, 0xFF1F,
+ /* 2 */ 0xFF20, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47,
+ 0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F,
+ /* 3 */ 0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57,
+ 0xFF58, 0xFF59, 0xFF5A, 0xFF3B, 0xFF3C, 0xFF3D, 0xFF3E, 0xFF3F,
+ /* 4 */ 0xFF40, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47,
+ 0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F,
+ /* 5 */ 0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57,
+ 0xFF58, 0xFF59, 0xFF5A, 0xFF5B, 0xFF5C, 0xFF5D, 0xFF5E, 0xFF5F,
+ /* 6 */ 0xFF60, 0xFF61, 0xFF62, 0xFF63, 0xFF64, 0xFF65, 0xFF66, 0xFF67,
+ 0xFF68, 0xFF69, 0xFF6A, 0xFF6B, 0xFF6C, 0xFF6D, 0xFF6E, 0xFF6F,
+ /* 7 */ 0xFF70, 0xFF71, 0xFF72, 0xFF73, 0xFF74, 0xFF75, 0xFF76, 0xFF77,
+ 0xFF78, 0xFF79, 0xFF7A, 0xFF7B, 0xFF7C, 0xFF7D, 0xFF7E, 0xFF7F,
+ /* 8 */ 0xFF80, 0xFF81, 0xFF82, 0xFF83, 0xFF84, 0xFF85, 0xFF86, 0xFF87,
+ 0xFF88, 0xFF89, 0xFF8A, 0xFF8B, 0xFF8C, 0xFF8D, 0xFF8E, 0xFF8F,
+ /* 9 */ 0xFF90, 0xFF91, 0xFF92, 0xFF93, 0xFF94, 0xFF95, 0xFF96, 0xFF97,
+ 0xFF98, 0xFF99, 0xFF9A, 0xFF9B, 0xFF9C, 0xFF9D, 0xFF9E, 0xFF9F,
+ /* A */ 0xFFA0, 0xFFA1, 0xFFA2, 0xFFA3, 0xFFA4, 0xFFA5, 0xFFA6, 0xFFA7,
+ 0xFFA8, 0xFFA9, 0xFFAA, 0xFFAB, 0xFFAC, 0xFFAD, 0xFFAE, 0xFFAF,
+ /* B */ 0xFFB0, 0xFFB1, 0xFFB2, 0xFFB3, 0xFFB4, 0xFFB5, 0xFFB6, 0xFFB7,
+ 0xFFB8, 0xFFB9, 0xFFBA, 0xFFBB, 0xFFBC, 0xFFBD, 0xFFBE, 0xFFBF,
+ /* C */ 0xFFC0, 0xFFC1, 0xFFC2, 0xFFC3, 0xFFC4, 0xFFC5, 0xFFC6, 0xFFC7,
+ 0xFFC8, 0xFFC9, 0xFFCA, 0xFFCB, 0xFFCC, 0xFFCD, 0xFFCE, 0xFFCF,
+ /* D */ 0xFFD0, 0xFFD1, 0xFFD2, 0xFFD3, 0xFFD4, 0xFFD5, 0xFFD6, 0xFFD7,
+ 0xFFD8, 0xFFD9, 0xFFDA, 0xFFDB, 0xFFDC, 0xFFDD, 0xFFDE, 0xFFDF,
+ /* E */ 0xFFE0, 0xFFE1, 0xFFE2, 0xFFE3, 0xFFE4, 0xFFE5, 0xFFE6, 0xFFE7,
+ 0xFFE8, 0xFFE9, 0xFFEA, 0xFFEB, 0xFFEC, 0xFFED, 0xFFEE, 0xFFEF,
+ /* F */ 0xFFF0, 0xFFF1, 0xFFF2, 0xFFF3, 0xFFF4, 0xFFF5, 0xFFF6, 0xFFF7,
+ 0xFFF8, 0xFFF9, 0xFFFA, 0xFFFB, 0xFFFC, 0xFFFD, 0xFFFE, 0xFFFF,
+};
diff --git a/fs/hfsplus/unicode.c b/fs/hfsplus/unicode.c
new file mode 100644
index 00000000000000..6a6b3a7e6e5b31
--- /dev/null
+++ b/fs/hfsplus/unicode.c
@@ -0,0 +1,126 @@
+/*
+ * linux/fs/hfsplus/unicode.c
+ *
+ * Copyright (C) 2001
+ * Brad Boyer (flar@allandria.com)
+ * (C) 2003 Ardis Technologies <roman@ardistech.com>
+ *
+ * Handler routines for unicode strings
+ */
+
+#include <linux/types.h>
+#include <linux/nls.h>
+#include "hfsplus_fs.h"
+#include "hfsplus_raw.h"
+
+/* Fold the case of a unicode char, given the 16 bit value */
+/* Returns folded char, or 0 if ignorable */
+static inline u16 case_fold(u16 c)
+{
+ u16 tmp;
+
+ tmp = case_fold_table[(c>>8)];
+ if (tmp)
+ tmp = case_fold_table[tmp + (c & 0xFF)];
+ else
+ tmp = c;
+ return tmp;
+}
+
+/* Compare unicode strings, return values like normal strcmp */
+int hfsplus_unistrcmp(const hfsplus_unistr *s1, const hfsplus_unistr *s2)
+{
+ u16 len1, len2, c1, c2;
+ const hfsplus_unichr *p1, *p2;
+
+ len1 = be16_to_cpu(s1->length);
+ len2 = be16_to_cpu(s2->length);
+ p1 = s1->unicode;
+ p2 = s2->unicode;
+
+ while (1) {
+ c1 = c2 = 0;
+
+ while (len1 && !c1) {
+ c1 = case_fold(be16_to_cpu(*p1));
+ p1++;
+ len1--;
+ }
+ while (len2 && !c2) {
+ c2 = case_fold(be16_to_cpu(*p2));
+ p2++;
+ len2--;
+ }
+
+ if (c1 != c2)
+ return (c1 < c2) ? -1 : 1;
+ if (!c1 && !c2)
+ return 0;
+ }
+}
+
+int hfsplus_uni2asc(const hfsplus_unistr *ustr, char *astr, int *len)
+{
+ const hfsplus_unichr *ip;
+ u8 *op;
+ u16 ustrlen, cc;
+ int size, tmp;
+
+ op = astr;
+ ip = ustr->unicode;
+ ustrlen = be16_to_cpu(ustr->length);
+ tmp = *len;
+ while (ustrlen > 0 && tmp > 0) {
+ cc = be16_to_cpu(*ip);
+ if (!cc || cc > 0x7f) {
+ size = utf8_wctomb(op, cc ? cc : 0x2400, tmp);
+ if (size == -1) {
+ /* ignore */
+ } else {
+ op += size;
+ tmp -= size;
+ }
+ } else {
+ *op++ = (u8) cc;
+ tmp--;
+ }
+ ip++;
+ ustrlen--;
+ }
+ *len = (char *)op - astr;
+ if (ustrlen)
+ return -ENAMETOOLONG;
+ return 0;
+}
+
+int hfsplus_asc2uni(hfsplus_unistr *ustr, const char *astr, int len)
+{
+ int tmp;
+ wchar_t c;
+ u16 outlen = 0;
+
+ while (outlen <= HFSPLUS_MAX_STRLEN && len > 0) {
+ if (*astr & 0x80) {
+ tmp = utf8_mbtowc(&c, astr, len);
+ if (tmp < 0) {
+ astr++;
+ len--;
+ continue;
+ } else {
+ astr += tmp;
+ len -= tmp;
+ }
+ if (c == 0x2400)
+ c = 0;
+ } else {
+ c = *astr++;
+ len--;
+ }
+ ustr->unicode[outlen] = cpu_to_be16(c);
+ outlen++;
+ }
+ ustr->length = cpu_to_be16(outlen);
+ if (len > 0)
+ return -ENAMETOOLONG;
+ return 0;
+}
diff --git a/fs/hfsplus/wrapper.c b/fs/hfsplus/wrapper.c
new file mode 100644
index 00000000000000..71e5818a074c33
--- /dev/null
+++ b/fs/hfsplus/wrapper.c
@@ -0,0 +1,149 @@
+/*
+ * linux/fs/hfsplus/wrapper.c
+ *
+ * Copyright (C) 2001
+ * Brad Boyer (flar@allandria.com)
+ * (C) 2003 Ardis Technologies <roman@ardistech.com>
+ *
+ * Handling of HFS wrappers around HFS+ volumes
+ */
+
+#include <linux/fs.h>
+#include <linux/blkdev.h>
+#include <linux/version.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
+#include <linux/buffer_head.h>
+#endif
+#include <asm/unaligned.h>
+
+#include "hfsplus_fs.h"
+#include "hfsplus_raw.h"
+
+struct hfsplus_wd {
+ u32 ablk_size;
+ u16 ablk_start;
+ u16 embed_start;
+ u16 embed_count;
+};
+
+static int hfsplus_read_mdb(unsigned char *bufptr, struct hfsplus_wd *wd)
+{
+ u32 extent;
+ u16 attrib;
+
+ if (be16_to_cpu(*(u16 *)(bufptr + HFSP_WRAPOFF_EMBEDSIG)) != HFSPLUS_VOLHEAD_SIG)
+ return 0;
+
+ attrib = be16_to_cpu(*(u16 *)(bufptr + HFSP_WRAPOFF_ATTRIB));
+ if (!(attrib & HFSP_WRAP_ATTRIB_SLOCK) ||
+ !(attrib & HFSP_WRAP_ATTRIB_SPARED))
+ return 0;
+
+ wd->ablk_size = be32_to_cpu(*(u32 *)(bufptr + HFSP_WRAPOFF_ABLKSIZE));
+ if (wd->ablk_size < HFSPLUS_SECTOR_SIZE)
+ return 0;
+ if (wd->ablk_size % HFSPLUS_SECTOR_SIZE)
+ return 0;
+ wd->ablk_start = be16_to_cpu(*(u16 *)(bufptr + HFSP_WRAPOFF_ABLKSTART));
+
+ extent = be32_to_cpu(get_unaligned((u32 *)(bufptr + HFSP_WRAPOFF_EMBEDEXT)));
+ wd->embed_start = (extent >> 16) & 0xFFFF;
+ wd->embed_count = extent & 0xFFFF;
+
+ return 1;
+}
+
+/* Find the volume header and fill in some minimum bits in superblock */
+/* Takes in super block, returns true if good data read */
+int hfsplus_read_wrapper(struct super_block *sb)
+{
+ struct buffer_head *bh;
+ struct hfsplus_vh *vhdr;
+ char *bufptr;
+ unsigned long block, offset, vhsect;
+ struct hfsplus_wd wd;
+ u32 blocksize, blockoffset;
+ u16 sig;
+
+ blocksize = sb_min_blocksize(sb, HFSPLUS_SECTOR_SIZE);
+ if (!blocksize) {
+ printk("HFS+-fs: unable to configure block size\n");
+ return -EINVAL;
+ }
+
+ block = (HFSPLUS_VOLHEAD_SECTOR * HFSPLUS_SECTOR_SIZE) / blocksize;
+ offset = (HFSPLUS_VOLHEAD_SECTOR * HFSPLUS_SECTOR_SIZE) % blocksize;
+
+ bh = sb_bread(sb, block);
+ if (!bh) {
+ printk("HFS+-fs: unable to read VHDR or MDB\n");
+ return -EIO;
+ }
+
+ bufptr = bh->b_data + offset;
+ sig = be16_to_cpu(*(u16 *)(bufptr + HFSP_WRAPOFF_SIG));
+ if (sig == HFSP_WRAP_MAGIC) {
+ if (!hfsplus_read_mdb(bufptr, &wd))
+ goto error;
+ vhsect = (wd.ablk_start + wd.embed_start * (wd.ablk_size >> 9))
+ + HFSPLUS_VOLHEAD_SECTOR;
+ block = (vhsect * HFSPLUS_SECTOR_SIZE) / blocksize;
+ offset = (vhsect * HFSPLUS_SECTOR_SIZE) % blocksize;
+ brelse(bh);
+ bh = sb_bread(sb, block);
+ if (!bh) {
+ printk("HFS+-fs: unable to read VHDR\n");
+ return -EIO;
+ }
+ HFSPLUS_SB(sb).sect_count = wd.embed_count * (wd.ablk_size >> 9);
+ } else {
+ wd.ablk_start = 0;
+ wd.ablk_size = blocksize;
+ wd.embed_start = 0;
+ HFSPLUS_SB(sb).sect_count = sb->s_bdev->bd_inode->i_size >> 9;
+ }
+ vhdr = (struct hfsplus_vh *)(bh->b_data + offset);
+ if (be16_to_cpu(vhdr->signature) != HFSPLUS_VOLHEAD_SIG)
+ goto error;
+ blocksize = be32_to_cpu(vhdr->blocksize);
+ brelse(bh);
+
+ /* block size must be at least as large as a sector
+ * and a multiple of 2
+ */
+ if (blocksize < HFSPLUS_SECTOR_SIZE ||
+ ((blocksize - 1) & blocksize))
+ return -EINVAL;
+
+ /* block offset must be a multiple of the block size */
+ blockoffset = wd.ablk_start + wd.embed_start * (wd.ablk_size >> 9);
+ if (blockoffset % (blocksize / HFSPLUS_SECTOR_SIZE)) {
+ printk("HFS+-fs: embedded blocks not aligned with wrapper\n");
+ return -EINVAL;
+ }
+ blockoffset /= blocksize / HFSPLUS_SECTOR_SIZE;
+ HFSPLUS_SB(sb).blockoffset = blockoffset;
+
+ if (sb_set_blocksize(sb, blocksize) != blocksize)
+ return -EINVAL;
+
+ block = blockoffset + HFSPLUS_VOLHEAD_SECTOR /
+ (blocksize / HFSPLUS_SECTOR_SIZE);
+ offset = (HFSPLUS_VOLHEAD_SECTOR * HFSPLUS_SECTOR_SIZE) % blocksize;
+ bh = sb_bread(sb, block);
+ if (!bh) {
+ printk("HFS+-fs: unable to read VHDR or MDB\n");
+ return -EIO;
+ }
+ vhdr = (struct hfsplus_vh *)(bh->b_data + offset);
+ /* should still be the same... */
+ if (be16_to_cpu(vhdr->signature) != HFSPLUS_VOLHEAD_SIG)
+ goto error;
+ HFSPLUS_SB(sb).s_vhbh = bh;
+ HFSPLUS_SB(sb).s_vhdr = vhdr;
+
+ return 0;
+ error:
+ brelse(bh);
+ return -EINVAL;
+}
diff --git a/fs/hpfs/Makefile b/fs/hpfs/Makefile
new file mode 100644
index 00000000000000..b2394dedd851bf
--- /dev/null
+++ b/fs/hpfs/Makefile
@@ -0,0 +1,6 @@
+O_TARGET := hpfs.o
+
+obj-y := alloc.o anode.o buffer.o dentry.o dir.o dnode.o ea.o file.o inode.o map.o name.o namei.o super.o
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/hpfs/alloc.c b/fs/hpfs/alloc.c
new file mode 100644
index 00000000000000..5b809941e66d6e
--- /dev/null
+++ b/fs/hpfs/alloc.c
@@ -0,0 +1,452 @@
+/*
+ * linux/fs/hpfs/alloc.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * HPFS bitmap operations
+ */
+
+#include "hpfs_fn.h"
+
+/*
+ * Check if a sector is allocated in bitmap
+ * This is really slow. Turned on only if chk==2
+ */
+
+static int chk_if_allocated(struct super_block *s, secno sec, char *msg)
+{
+ struct quad_buffer_head qbh;
+ unsigned *bmp;
+ if (!(bmp = hpfs_map_bitmap(s, sec >> 14, &qbh, "chk"))) goto fail;
+ if ((bmp[(sec & 0x3fff) >> 5] >> (sec & 0x1f)) & 1) {
+ hpfs_error(s, "sector '%s' - %08x not allocated in bitmap", msg, sec);
+ goto fail1;
+ }
+ hpfs_brelse4(&qbh);
+ if (sec >= s->s_hpfs_dirband_start && sec < s->s_hpfs_dirband_start + s->s_hpfs_dirband_size) {
+ unsigned ssec = (sec - s->s_hpfs_dirband_start) / 4;
+ if (!(bmp = hpfs_map_dnode_bitmap(s, &qbh))) goto fail;
+ if ((bmp[ssec >> 5] >> (ssec & 0x1f)) & 1) {
+ hpfs_error(s, "sector '%s' - %08x not allocated in directory bitmap", msg, sec);
+ goto fail1;
+ }
+ hpfs_brelse4(&qbh);
+ }
+ return 0;
+ fail1:
+ hpfs_brelse4(&qbh);
+ fail:
+ return 1;
+}
+
+/*
+ * Check if sector(s) have proper number and additionally check if they're
+ * allocated in bitmap.
+ */
+
+int hpfs_chk_sectors(struct super_block *s, secno start, int len, char *msg)
+{
+ if (start + len < start || start < 0x12 ||
+ start + len > s->s_hpfs_fs_size) {
+ hpfs_error(s, "sector(s) '%s' badly placed at %08x", msg, start);
+ return 1;
+ }
+ if (s->s_hpfs_chk>=2) {
+ int i;
+ for (i = 0; i < len; i++)
+ if (chk_if_allocated(s, start + i, msg)) return 1;
+ }
+ return 0;
+}
+
+static secno alloc_in_bmp(struct super_block *s, secno near, unsigned n, unsigned forward)
+{
+ struct quad_buffer_head qbh;
+ unsigned *bmp;
+ unsigned bs = near & ~0x3fff;
+ unsigned nr = (near & 0x3fff) & ~(n - 1);
+ /*unsigned mnr;*/
+ unsigned i, q;
+ int a, b;
+ secno ret = 0;
+ if (n != 1 && n != 4) {
+ hpfs_error(s, "Bad allocation size: %d", n);
+ return 0;
+ }
+ lock_super(s);
+ if (bs != ~0x3fff) {
+ if (!(bmp = hpfs_map_bitmap(s, near >> 14, &qbh, "aib"))) goto uls;
+ } else {
+ if (!(bmp = hpfs_map_dnode_bitmap(s, &qbh))) goto uls;
+ }
+ if (!tstbits(bmp, nr, n + forward)) {
+ ret = bs + nr;
+ goto rt;
+ }
+ /*if (!tstbits(bmp, nr + n, n + forward)) {
+ ret = bs + nr + n;
+ goto rt;
+ }*/
+ q = nr + n; b = 0;
+ while ((a = tstbits(bmp, q, n + forward))) {
+ q += a;
+ if (n != 1) q = ((q-1)&~(n-1))+n;
+ if (!b) {
+ if (q>>5 != nr>>5) {
+ b = 1;
+ q = nr & 0x1f;
+ }
+ } else if (q > nr) break;
+ }
+ if (!a) {
+ ret = bs + q;
+ goto rt;
+ }
+ nr >>= 5;
+ /*for (i = nr + 1; i != nr; i++, i &= 0x1ff) {*/
+ i = nr;
+ do {
+ if (!bmp[i]) goto cont;
+ if (n + forward >= 0x3f && bmp[i] != -1) goto cont;
+ q = i<<5;
+ if (i > 0) {
+ unsigned k = bmp[i-1];
+ while (k & 0x80000000) {
+ q--; k <<= 1;
+ }
+ }
+ if (n != 1) q = ((q-1)&~(n-1))+n;
+ while ((a = tstbits(bmp, q, n + forward))) {
+ q += a;
+ if (n != 1) q = ((q-1)&~(n-1))+n;
+ if (q>>5 > i) break;
+ }
+ if (!a) {
+ ret = bs + q;
+ goto rt;
+ }
+ cont:
+ i++, i &= 0x1ff;
+ } while (i != nr);
+ rt:
+ if (ret) {
+ if (s->s_hpfs_chk && ((ret >> 14) != (bs >> 14) || (bmp[(ret & 0x3fff) >> 5] | ~(((1 << n) - 1) << (ret & 0x1f))) != 0xffffffff)) {
+ hpfs_error(s, "Allocation doesn't work! Wanted %d, allocated at %08x", n, ret);
+ ret = 0;
+ goto b;
+ }
+ bmp[(ret & 0x3fff) >> 5] &= ~(((1 << n) - 1) << (ret & 0x1f));
+ hpfs_mark_4buffers_dirty(&qbh);
+ }
+ b:
+ hpfs_brelse4(&qbh);
+ uls:
+ unlock_super(s);
+ return ret;
+}
+
+/*
+ * Allocation strategy: 1) search place near the sector specified
+ * 2) search bitmap where free sectors last found
+ * 3) search all bitmaps
+ * 4) search all bitmaps ignoring number of pre-allocated
+ * sectors
+ */
+
+secno hpfs_alloc_sector(struct super_block *s, secno near, unsigned n, int forward, int lock)
+{
+ secno sec;
+ int i;
+ unsigned n_bmps;
+ int f_p = 0;
+ int near_bmp;
+ if (forward < 0) {
+ forward = -forward;
+ f_p = 1;
+ }
+ if (lock) hpfs_lock_creation(s);
+ n_bmps = (s->s_hpfs_fs_size + 0x4000 - 1) >> 14;
+ if (near && near < s->s_hpfs_fs_size) {
+ if ((sec = alloc_in_bmp(s, near, n, f_p ? forward : forward/4))) goto ret;
+ near_bmp = near >> 14;
+ } else near_bmp = n_bmps / 2;
+ /*
+ if (b != -1) {
+ if ((sec = alloc_in_bmp(s, b<<14, n, f_p ? forward : forward/2))) {
+ b &= 0x0fffffff;
+ goto ret;
+ }
+ if (b > 0x10000000) if ((sec = alloc_in_bmp(s, (b&0xfffffff)<<14, n, f_p ? forward : 0))) goto ret;
+ }
+ */
+ if (!f_p) if (forward > s->s_hpfs_max_fwd_alloc) forward = s->s_hpfs_max_fwd_alloc;
+ less_fwd:
+ for (i = 0; i < n_bmps; i++) {
+ if (near_bmp+i < n_bmps && ((sec = alloc_in_bmp(s, (near_bmp+i) << 14, n, forward)))) {
+ s->s_hpfs_c_bitmap = near_bmp+i;
+ goto ret;
+ }
+ if (!forward) {
+ if (near_bmp-i-1 >= 0 && ((sec = alloc_in_bmp(s, (near_bmp-i-1) << 14, n, forward)))) {
+ s->s_hpfs_c_bitmap = near_bmp-i-1;
+ goto ret;
+ }
+ } else {
+ if (near_bmp+i >= n_bmps && ((sec = alloc_in_bmp(s, (near_bmp+i-n_bmps) << 14, n, forward)))) {
+ s->s_hpfs_c_bitmap = near_bmp+i-n_bmps;
+ goto ret;
+ }
+ }
+ if (i == 1 && s->s_hpfs_c_bitmap != -1 && ((sec = alloc_in_bmp(s, (s->s_hpfs_c_bitmap) << 14, n, forward)))) {
+ goto ret;
+ }
+ }
+ if (!f_p) {
+ if (forward) {
+ s->s_hpfs_max_fwd_alloc = forward * 3 / 4;
+ forward /= 2;
+ goto less_fwd;
+ }
+ }
+ sec = 0;
+ ret:
+ if (sec && f_p) {
+ for (i = 0; i < forward; i++) {
+ if (!hpfs_alloc_if_possible_nolock(s, sec + i + 1)) {
+ hpfs_error(s, "Prealloc doesn't work! Wanted %d, allocated at %08x, can't allocate %d", forward, sec, i);
+ sec = 0;
+ break;
+ }
+ }
+ }
+ if (lock) hpfs_unlock_creation(s);
+ return sec;
+}
+
+static secno alloc_in_dirband(struct super_block *s, secno near, int lock)
+{
+ unsigned nr = near;
+ secno sec;
+ if (nr < s->s_hpfs_dirband_start)
+ nr = s->s_hpfs_dirband_start;
+ if (nr >= s->s_hpfs_dirband_start + s->s_hpfs_dirband_size)
+ nr = s->s_hpfs_dirband_start + s->s_hpfs_dirband_size - 4;
+ nr -= s->s_hpfs_dirband_start;
+ nr >>= 2;
+ if (lock) hpfs_lock_creation(s);
+ sec = alloc_in_bmp(s, (~0x3fff) | nr, 1, 0);
+ if (lock) hpfs_unlock_creation(s);
+ if (!sec) return 0;
+ return ((sec & 0x3fff) << 2) + s->s_hpfs_dirband_start;
+}
+
+/* Alloc sector if it's free */
+
+int hpfs_alloc_if_possible_nolock(struct super_block *s, secno sec)
+{
+ struct quad_buffer_head qbh;
+ unsigned *bmp;
+ lock_super(s);
+ if (!(bmp = hpfs_map_bitmap(s, sec >> 14, &qbh, "aip"))) goto end;
+ if (bmp[(sec & 0x3fff) >> 5] & (1 << (sec & 0x1f))) {
+ bmp[(sec & 0x3fff) >> 5] &= ~(1 << (sec & 0x1f));
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ unlock_super(s);
+ return 1;
+ }
+ hpfs_brelse4(&qbh);
+ end:
+ unlock_super(s);
+ return 0;
+}
+
+int hpfs_alloc_if_possible(struct super_block *s, secno sec)
+{
+ int r;
+ hpfs_lock_creation(s);
+ r = hpfs_alloc_if_possible_nolock(s, sec);
+ hpfs_unlock_creation(s);
+ return r;
+}
+
+/* Free sectors in bitmaps */
+
+void hpfs_free_sectors(struct super_block *s, secno sec, unsigned n)
+{
+ struct quad_buffer_head qbh;
+ unsigned *bmp;
+ /*printk("2 - ");*/
+ if (!n) return;
+ if (sec < 0x12) {
+ hpfs_error(s, "Trying to free reserved sector %08x", sec);
+ return;
+ }
+ lock_super(s);
+ s->s_hpfs_max_fwd_alloc += n > 0xffff ? 0xffff : n;
+ if (s->s_hpfs_max_fwd_alloc > 0xffffff) s->s_hpfs_max_fwd_alloc = 0xffffff;
+ new_map:
+ if (!(bmp = hpfs_map_bitmap(s, sec >> 14, &qbh, "free"))) {
+ unlock_super(s);
+ return;
+ }
+ new_tst:
+ if ((bmp[(sec & 0x3fff) >> 5] >> (sec & 0x1f) & 1)) {
+ hpfs_error(s, "sector %08x not allocated", sec);
+ hpfs_brelse4(&qbh);
+ unlock_super(s);
+ return;
+ }
+ bmp[(sec & 0x3fff) >> 5] |= 1 << (sec & 0x1f);
+ if (!--n) {
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ unlock_super(s);
+ return;
+ }
+ if (!(++sec & 0x3fff)) {
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ goto new_map;
+ }
+ goto new_tst;
+}
+
+/*
+ * Check if there are at least n free dnodes on the filesystem.
+ * Called before adding to dnode. If we run out of space while
+ * splitting dnodes, it would corrupt dnode tree.
+ */
+
+int hpfs_check_free_dnodes(struct super_block *s, int n)
+{
+ int n_bmps = (s->s_hpfs_fs_size + 0x4000 - 1) >> 14;
+ int b = s->s_hpfs_c_bitmap & 0x0fffffff;
+ int i, j;
+ unsigned *bmp;
+ struct quad_buffer_head qbh;
+ if ((bmp = hpfs_map_dnode_bitmap(s, &qbh))) {
+ for (j = 0; j < 512; j++) {
+ unsigned k;
+ if (!bmp[j]) continue;
+ for (k = bmp[j]; k; k >>= 1) if (k & 1) if (!--n) {
+ hpfs_brelse4(&qbh);
+ return 0;
+ }
+ }
+ }
+ hpfs_brelse4(&qbh);
+ i = 0;
+ if (s->s_hpfs_c_bitmap != -1) {
+ bmp = hpfs_map_bitmap(s, b, &qbh, "chkdn1");
+ goto chk_bmp;
+ }
+ chk_next:
+ if (i == b) i++;
+ if (i >= n_bmps) return 1;
+ bmp = hpfs_map_bitmap(s, i, &qbh, "chkdn2");
+ chk_bmp:
+ if (bmp) {
+ for (j = 0; j < 512; j++) {
+ unsigned k;
+ if (!bmp[j]) continue;
+ for (k = 0xf; k; k <<= 4)
+ if ((bmp[j] & k) == k) {
+ if (!--n) {
+ hpfs_brelse4(&qbh);
+ return 0;
+ }
+ }
+ }
+ hpfs_brelse4(&qbh);
+ }
+ i++;
+ goto chk_next;
+}
+
+void hpfs_free_dnode(struct super_block *s, dnode_secno dno)
+{
+ if (s->s_hpfs_chk) if (dno & 3) {
+ hpfs_error(s, "hpfs_free_dnode: dnode %08x not aligned", dno);
+ return;
+ }
+ if (dno < s->s_hpfs_dirband_start ||
+ dno >= s->s_hpfs_dirband_start + s->s_hpfs_dirband_size) {
+ hpfs_free_sectors(s, dno, 4);
+ } else {
+ struct quad_buffer_head qbh;
+ unsigned *bmp;
+ unsigned ssec = (dno - s->s_hpfs_dirband_start) / 4;
+ lock_super(s);
+ if (!(bmp = hpfs_map_dnode_bitmap(s, &qbh))) {
+ unlock_super(s);
+ return;
+ }
+ bmp[ssec >> 5] |= 1 << (ssec & 0x1f);
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ unlock_super(s);
+ }
+}
+
+struct dnode *hpfs_alloc_dnode(struct super_block *s, secno near,
+ dnode_secno *dno, struct quad_buffer_head *qbh,
+ int lock)
+{
+ struct dnode *d;
+ if (hpfs_count_one_bitmap(s, s->s_hpfs_dmap) > FREE_DNODES_ADD) {
+ if (!(*dno = alloc_in_dirband(s, near, lock)))
+ if (!(*dno = hpfs_alloc_sector(s, near, 4, 0, lock))) return NULL;
+ } else {
+ if (!(*dno = hpfs_alloc_sector(s, near, 4, 0, lock)))
+ if (!(*dno = alloc_in_dirband(s, near, lock))) return NULL;
+ }
+ if (!(d = hpfs_get_4sectors(s, *dno, qbh))) {
+ hpfs_free_dnode(s, *dno);
+ return NULL;
+ }
+ memset(d, 0, 2048);
+ d->magic = DNODE_MAGIC;
+ d->first_free = 52;
+ d->dirent[0] = 32;
+ d->dirent[2] = 8;
+ d->dirent[30] = 1;
+ d->dirent[31] = 255;
+ d->self = *dno;
+ return d;
+}
+
+struct fnode *hpfs_alloc_fnode(struct super_block *s, secno near, fnode_secno *fno,
+ struct buffer_head **bh)
+{
+ struct fnode *f;
+ if (!(*fno = hpfs_alloc_sector(s, near, 1, FNODE_ALLOC_FWD, 1))) return NULL;
+ if (!(f = hpfs_get_sector(s, *fno, bh))) {
+ hpfs_free_sectors(s, *fno, 1);
+ return NULL;
+ }
+ memset(f, 0, 512);
+ f->magic = FNODE_MAGIC;
+ f->ea_offs = 0xc4;
+ f->btree.n_free_nodes = 8;
+ f->btree.first_free = 8;
+ return f;
+}
+
+struct anode *hpfs_alloc_anode(struct super_block *s, secno near, anode_secno *ano,
+ struct buffer_head **bh)
+{
+ struct anode *a;
+ if (!(*ano = hpfs_alloc_sector(s, near, 1, ANODE_ALLOC_FWD, 1))) return NULL;
+ if (!(a = hpfs_get_sector(s, *ano, bh))) {
+ hpfs_free_sectors(s, *ano, 1);
+ return NULL;
+ }
+ memset(a, 0, 512);
+ a->magic = ANODE_MAGIC;
+ a->self = *ano;
+ a->btree.n_free_nodes = 40;
+ a->btree.n_used_nodes = 0;
+ a->btree.first_free = 8;
+ return a;
+}
diff --git a/fs/hpfs/anode.c b/fs/hpfs/anode.c
new file mode 100644
index 00000000000000..8077cfc2c29d49
--- /dev/null
+++ b/fs/hpfs/anode.c
@@ -0,0 +1,490 @@
+/*
+ * linux/fs/hpfs/anode.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * handling HPFS anode tree that contains file allocation info
+ */
+
+#include "hpfs_fn.h"
+
+/* Find a sector in allocation tree */
+
+secno hpfs_bplus_lookup(struct super_block *s, struct inode *inode,
+ struct bplus_header *btree, unsigned sec,
+ struct buffer_head *bh)
+{
+ anode_secno a = -1;
+ struct anode *anode;
+ int i;
+ int c1, c2 = 0;
+ go_down:
+ if (s->s_hpfs_chk) if (hpfs_stop_cycles(s, a, &c1, &c2, "hpfs_bplus_lookup")) return -1;
+ if (btree->internal) {
+ for (i = 0; i < btree->n_used_nodes; i++)
+ if (btree->u.internal[i].file_secno > sec) {
+ a = btree->u.internal[i].down;
+ brelse(bh);
+ if (!(anode = hpfs_map_anode(s, a, &bh))) return -1;
+ btree = &anode->btree;
+ goto go_down;
+ }
+ hpfs_error(s, "sector %08x not found in internal anode %08x", sec, a);
+ brelse(bh);
+ return -1;
+ }
+ for (i = 0; i < btree->n_used_nodes; i++)
+ if (btree->u.external[i].file_secno <= sec &&
+ btree->u.external[i].file_secno + btree->u.external[i].length > sec) {
+ a = btree->u.external[i].disk_secno + sec - btree->u.external[i].file_secno;
+ if (s->s_hpfs_chk) if (hpfs_chk_sectors(s, a, 1, "data")) {
+ brelse(bh);
+ return -1;
+ }
+ if (inode) {
+ inode->i_hpfs_file_sec = btree->u.external[i].file_secno;
+ inode->i_hpfs_disk_sec = btree->u.external[i].disk_secno;
+ inode->i_hpfs_n_secs = btree->u.external[i].length;
+ }
+ brelse(bh);
+ return a;
+ }
+ hpfs_error(s, "sector %08x not found in external anode %08x", sec, a);
+ brelse(bh);
+ return -1;
+}
+
+/* Add a sector to tree */
+
+secno hpfs_add_sector_to_btree(struct super_block *s, secno node, int fnod, unsigned fsecno)
+{
+ struct bplus_header *btree;
+ struct anode *anode = NULL, *ranode = NULL;
+ struct fnode *fnode;
+ anode_secno a, na = -1, ra, up = -1;
+ secno se;
+ struct buffer_head *bh, *bh1, *bh2;
+ int n;
+ unsigned fs;
+ int c1, c2 = 0;
+ if (fnod) {
+ if (!(fnode = hpfs_map_fnode(s, node, &bh))) return -1;
+ btree = &fnode->btree;
+ } else {
+ if (!(anode = hpfs_map_anode(s, node, &bh))) return -1;
+ btree = &anode->btree;
+ }
+ a = node;
+ go_down:
+ if ((n = btree->n_used_nodes - 1) < -!!fnod) {
+ hpfs_error(s, "anode %08x has no entries", a);
+ brelse(bh);
+ return -1;
+ }
+ if (btree->internal) {
+ a = btree->u.internal[n].down;
+ btree->u.internal[n].file_secno = -1;
+ mark_buffer_dirty(bh);
+ brelse(bh);
+ if (s->s_hpfs_chk)
+ if (hpfs_stop_cycles(s, a, &c1, &c2, "hpfs_add_sector_to_btree #1")) return -1;
+ if (!(anode = hpfs_map_anode(s, a, &bh))) return -1;
+ btree = &anode->btree;
+ goto go_down;
+ }
+ if (n >= 0) {
+ if (btree->u.external[n].file_secno + btree->u.external[n].length != fsecno) {
+ hpfs_error(s, "allocated size %08x, trying to add sector %08x, %cnode %08x",
+ btree->u.external[n].file_secno + btree->u.external[n].length, fsecno,
+ fnod?'f':'a', node);
+ brelse(bh);
+ return -1;
+ }
+ if (hpfs_alloc_if_possible(s, se = btree->u.external[n].disk_secno + btree->u.external[n].length)) {
+ btree->u.external[n].length++;
+ mark_buffer_dirty(bh);
+ brelse(bh);
+ return se;
+ }
+ } else {
+ if (fsecno) {
+ hpfs_error(s, "empty file %08x, trying to add sector %08x", node, fsecno);
+ brelse(bh);
+ return -1;
+ }
+ se = !fnod ? node : (node + 16384) & ~16383;
+ }
+ if (!(se = hpfs_alloc_sector(s, se, 1, fsecno*ALLOC_M>ALLOC_FWD_MAX ? ALLOC_FWD_MAX : fsecno*ALLOC_M<ALLOC_FWD_MIN ? ALLOC_FWD_MIN : fsecno*ALLOC_M, 1))) {
+ brelse(bh);
+ return -1;
+ }
+ fs = n < 0 ? 0 : btree->u.external[n].file_secno + btree->u.external[n].length;
+ if (!btree->n_free_nodes) {
+ up = a != node ? anode->up : -1;
+ if (!(anode = hpfs_alloc_anode(s, a, &na, &bh1))) {
+ brelse(bh);
+ hpfs_free_sectors(s, se, 1);
+ return -1;
+ }
+ if (a == node && fnod) {
+ anode->up = node;
+ anode->btree.fnode_parent = 1;
+ anode->btree.n_used_nodes = btree->n_used_nodes;
+ anode->btree.first_free = btree->first_free;
+ anode->btree.n_free_nodes = 40 - anode->btree.n_used_nodes;
+ memcpy(&anode->u, &btree->u, btree->n_used_nodes * 12);
+ btree->internal = 1;
+ btree->n_free_nodes = 11;
+ btree->n_used_nodes = 1;
+ btree->first_free = (char *)&(btree->u.internal[1]) - (char *)btree;
+ btree->u.internal[0].file_secno = -1;
+ btree->u.internal[0].down = na;
+ mark_buffer_dirty(bh);
+ } else if (!(ranode = hpfs_alloc_anode(s, /*a*/0, &ra, &bh2))) {
+ brelse(bh);
+ brelse(bh1);
+ hpfs_free_sectors(s, se, 1);
+ hpfs_free_sectors(s, na, 1);
+ return -1;
+ }
+ brelse(bh);
+ bh = bh1;
+ btree = &anode->btree;
+ }
+ btree->n_free_nodes--; n = btree->n_used_nodes++;
+ btree->first_free += 12;
+ btree->u.external[n].disk_secno = se;
+ btree->u.external[n].file_secno = fs;
+ btree->u.external[n].length = 1;
+ mark_buffer_dirty(bh);
+ brelse(bh);
+ if ((a == node && fnod) || na == -1) return se;
+ c2 = 0;
+ while (up != -1) {
+ struct anode *new_anode;
+ if (s->s_hpfs_chk)
+ if (hpfs_stop_cycles(s, up, &c1, &c2, "hpfs_add_sector_to_btree #2")) return -1;
+ if (up != node || !fnod) {
+ if (!(anode = hpfs_map_anode(s, up, &bh))) return -1;
+ btree = &anode->btree;
+ } else {
+ if (!(fnode = hpfs_map_fnode(s, up, &bh))) return -1;
+ btree = &fnode->btree;
+ }
+ if (btree->n_free_nodes) {
+ btree->n_free_nodes--; n = btree->n_used_nodes++;
+ btree->first_free += 8;
+ btree->u.internal[n].file_secno = -1;
+ btree->u.internal[n].down = na;
+ btree->u.internal[n-1].file_secno = fs;
+ mark_buffer_dirty(bh);
+ brelse(bh);
+ brelse(bh2);
+ hpfs_free_sectors(s, ra, 1);
+ if ((anode = hpfs_map_anode(s, na, &bh))) {
+ anode->up = up;
+ anode->btree.fnode_parent = up == node && fnod;
+ mark_buffer_dirty(bh);
+ brelse(bh);
+ }
+ return se;
+ }
+ up = up != node ? anode->up : -1;
+ btree->u.internal[btree->n_used_nodes - 1].file_secno = /*fs*/-1;
+ mark_buffer_dirty(bh);
+ brelse(bh);
+ a = na;
+ if ((new_anode = hpfs_alloc_anode(s, a, &na, &bh))) {
+ anode = new_anode;
+ /*anode->up = up != -1 ? up : ra;*/
+ anode->btree.internal = 1;
+ anode->btree.n_used_nodes = 1;
+ anode->btree.n_free_nodes = 59;
+ anode->btree.first_free = 16;
+ anode->btree.u.internal[0].down = a;
+ anode->btree.u.internal[0].file_secno = -1;
+ mark_buffer_dirty(bh);
+ brelse(bh);
+ if ((anode = hpfs_map_anode(s, a, &bh))) {
+ anode->up = na;
+ mark_buffer_dirty(bh);
+ brelse(bh);
+ }
+ } else na = a;
+ }
+ if ((anode = hpfs_map_anode(s, na, &bh))) {
+ anode->up = node;
+ if (fnod) anode->btree.fnode_parent = 1;
+ mark_buffer_dirty(bh);
+ brelse(bh);
+ }
+ if (!fnod) {
+ if (!(anode = hpfs_map_anode(s, node, &bh))) {
+ brelse(bh2);
+ return -1;
+ }
+ btree = &anode->btree;
+ } else {
+ if (!(fnode = hpfs_map_fnode(s, node, &bh))) {
+ brelse(bh2);
+ return -1;
+ }
+ btree = &fnode->btree;
+ }
+ ranode->up = node;
+ memcpy(&ranode->btree, btree, btree->first_free);
+ if (fnod) ranode->btree.fnode_parent = 1;
+ ranode->btree.n_free_nodes = (ranode->btree.internal ? 60 : 40) - ranode->btree.n_used_nodes;
+ if (ranode->btree.internal) for (n = 0; n < ranode->btree.n_used_nodes; n++) {
+ struct anode *unode;
+ if ((unode = hpfs_map_anode(s, ranode->u.internal[n].down, &bh1))) {
+ unode->up = ra;
+ unode->btree.fnode_parent = 0;
+ mark_buffer_dirty(bh1);
+ brelse(bh1);
+ }
+ }
+ btree->internal = 1;
+ btree->n_free_nodes = fnod ? 10 : 58;
+ btree->n_used_nodes = 2;
+ btree->first_free = (char *)&btree->u.internal[2] - (char *)btree;
+ btree->u.internal[0].file_secno = fs;
+ btree->u.internal[0].down = ra;
+ btree->u.internal[1].file_secno = -1;
+ btree->u.internal[1].down = na;
+ mark_buffer_dirty(bh);
+ brelse(bh);
+ mark_buffer_dirty(bh2);
+ brelse(bh2);
+ return se;
+}
+
+/*
+ * Remove allocation tree. Recursion would look much nicer but
+ * I want to avoid it because it can cause stack overflow.
+ */
+
+void hpfs_remove_btree(struct super_block *s, struct bplus_header *btree)
+{
+ struct bplus_header *btree1 = btree;
+ struct anode *anode = NULL;
+ anode_secno ano = 0, oano;
+ struct buffer_head *bh;
+ int level = 0;
+ int pos = 0;
+ int i;
+ int c1, c2 = 0;
+ int d1, d2;
+ go_down:
+ d2 = 0;
+ while (btree1->internal) {
+ ano = btree1->u.internal[pos].down;
+ if (level) brelse(bh);
+ if (s->s_hpfs_chk)
+ if (hpfs_stop_cycles(s, ano, &d1, &d2, "hpfs_remove_btree #1"))
+ return;
+ if (!(anode = hpfs_map_anode(s, ano, &bh))) return;
+ btree1 = &anode->btree;
+ level++;
+ pos = 0;
+ }
+ for (i = 0; i < btree1->n_used_nodes; i++)
+ hpfs_free_sectors(s, btree1->u.external[i].disk_secno, btree1->u.external[i].length);
+ go_up:
+ if (!level) return;
+ brelse(bh);
+ if (s->s_hpfs_chk)
+ if (hpfs_stop_cycles(s, ano, &c1, &c2, "hpfs_remove_btree #2")) return;
+ hpfs_free_sectors(s, ano, 1);
+ oano = ano;
+ ano = anode->up;
+ if (--level) {
+ if (!(anode = hpfs_map_anode(s, ano, &bh))) return;
+ btree1 = &anode->btree;
+ } else btree1 = btree;
+ for (i = 0; i < btree1->n_used_nodes; i++) {
+ if (btree1->u.internal[i].down == oano) {
+ if ((pos = i + 1) < btree1->n_used_nodes)
+ goto go_down;
+ else
+ goto go_up;
+ }
+ }
+ hpfs_error(s,
+ "reference to anode %08x not found in anode %08x "
+ "(probably bad up pointer)",
+ oano, level ? ano : -1);
+ if (level)
+ brelse(bh);
+}
+
+/* Just a wrapper around hpfs_bplus_lookup .. used for reading eas */
+
+static secno anode_lookup(struct super_block *s, anode_secno a, unsigned sec)
+{
+ struct anode *anode;
+ struct buffer_head *bh;
+ if (!(anode = hpfs_map_anode(s, a, &bh))) return -1;
+ return hpfs_bplus_lookup(s, NULL, &anode->btree, sec, bh);
+}
+
+int hpfs_ea_read(struct super_block *s, secno a, int ano, unsigned pos,
+ unsigned len, char *buf)
+{
+ struct buffer_head *bh;
+ char *data;
+ secno sec;
+ unsigned l;
+ while (len) {
+ if (ano) {
+ if ((sec = anode_lookup(s, a, pos >> 9)) == -1)
+ return -1;
+ } else sec = a + (pos >> 9);
+ if (s->s_hpfs_chk) if (hpfs_chk_sectors(s, sec, 1, "ea #1")) return -1;
+ if (!(data = hpfs_map_sector(s, sec, &bh, (len - 1) >> 9)))
+ return -1;
+ l = 0x200 - (pos & 0x1ff); if (l > len) l = len;
+ memcpy(buf, data + (pos & 0x1ff), l);
+ brelse(bh);
+ buf += l; pos += l; len -= l;
+ }
+ return 0;
+}
+
+int hpfs_ea_write(struct super_block *s, secno a, int ano, unsigned pos,
+ unsigned len, char *buf)
+{
+ struct buffer_head *bh;
+ char *data;
+ secno sec;
+ unsigned l;
+ while (len) {
+ if (ano) {
+ if ((sec = anode_lookup(s, a, pos >> 9)) == -1)
+ return -1;
+ } else sec = a + (pos >> 9);
+ if (s->s_hpfs_chk) if (hpfs_chk_sectors(s, sec, 1, "ea #2")) return -1;
+ if (!(data = hpfs_map_sector(s, sec, &bh, (len - 1) >> 9)))
+ return -1;
+ l = 0x200 - (pos & 0x1ff); if (l > len) l = len;
+ memcpy(data + (pos & 0x1ff), buf, l);
+ mark_buffer_dirty(bh);
+ brelse(bh);
+ buf += l; pos += l; len -= l;
+ }
+ return 0;
+}
+
+void hpfs_ea_remove(struct super_block *s, secno a, int ano, unsigned len)
+{
+ struct anode *anode;
+ struct buffer_head *bh;
+ if (ano) {
+ if (!(anode = hpfs_map_anode(s, a, &bh))) return;
+ hpfs_remove_btree(s, &anode->btree);
+ brelse(bh);
+ hpfs_free_sectors(s, a, 1);
+ } else hpfs_free_sectors(s, a, (len + 511) >> 9);
+}
+
+/* Truncate allocation tree. Doesn't join anodes - I hope it doesn't matter */
+
+void hpfs_truncate_btree(struct super_block *s, secno f, int fno, unsigned secs)
+{
+ struct fnode *fnode;
+ struct anode *anode;
+ struct buffer_head *bh;
+ struct bplus_header *btree;
+ anode_secno node = f;
+ int i, j, nodes;
+ int c1, c2 = 0;
+ if (fno) {
+ if (!(fnode = hpfs_map_fnode(s, f, &bh))) return;
+ btree = &fnode->btree;
+ } else {
+ if (!(anode = hpfs_map_anode(s, f, &bh))) return;
+ btree = &anode->btree;
+ }
+ if (!secs) {
+ hpfs_remove_btree(s, btree);
+ if (fno) {
+ btree->n_free_nodes = 8;
+ btree->n_used_nodes = 0;
+ btree->first_free = 8;
+ btree->internal = 0;
+ mark_buffer_dirty(bh);
+ } else hpfs_free_sectors(s, f, 1);
+ brelse(bh);
+ return;
+ }
+ while (btree->internal) {
+ nodes = btree->n_used_nodes + btree->n_free_nodes;
+ for (i = 0; i < btree->n_used_nodes; i++)
+ if (btree->u.internal[i].file_secno >= secs) goto f;
+ brelse(bh);
+ hpfs_error(s, "internal btree %08x doesn't end with -1", node);
+ return;
+ f:
+ for (j = i + 1; j < btree->n_used_nodes; j++)
+ hpfs_ea_remove(s, btree->u.internal[j].down, 1, 0);
+ btree->n_used_nodes = i + 1;
+ btree->n_free_nodes = nodes - btree->n_used_nodes;
+ btree->first_free = 8 + 8 * btree->n_used_nodes;
+ mark_buffer_dirty(bh);
+ if (btree->u.internal[i].file_secno == secs) {
+ brelse(bh);
+ return;
+ }
+ node = btree->u.internal[i].down;
+ brelse(bh);
+ if (s->s_hpfs_chk)
+ if (hpfs_stop_cycles(s, node, &c1, &c2, "hpfs_truncate_btree"))
+ return;
+ if (!(anode = hpfs_map_anode(s, node, &bh))) return;
+ btree = &anode->btree;
+ }
+ nodes = btree->n_used_nodes + btree->n_free_nodes;
+ for (i = 0; i < btree->n_used_nodes; i++)
+ if (btree->u.external[i].file_secno + btree->u.external[i].length >= secs) goto ff;
+ brelse(bh);
+ return;
+ ff:
+ if (secs <= btree->u.external[i].file_secno) {
+ hpfs_error(s, "there is an allocation error in file %08x, sector %08x", f, secs);
+ if (i) i--;
+ }
+ else if (btree->u.external[i].file_secno + btree->u.external[i].length > secs) {
+ hpfs_free_sectors(s, btree->u.external[i].disk_secno + secs -
+ btree->u.external[i].file_secno, btree->u.external[i].length
+ - secs + btree->u.external[i].file_secno); /* I hope gcc optimizes this :-) */
+ btree->u.external[i].length = secs - btree->u.external[i].file_secno;
+ }
+ for (j = i + 1; j < btree->n_used_nodes; j++)
+ hpfs_free_sectors(s, btree->u.external[j].disk_secno, btree->u.external[j].length);
+ btree->n_used_nodes = i + 1;
+ btree->n_free_nodes = nodes - btree->n_used_nodes;
+ btree->first_free = 8 + 12 * btree->n_used_nodes;
+ mark_buffer_dirty(bh);
+ brelse(bh);
+}
+
+/* Remove file or directory and it's eas - note that directory must
+ be empty when this is called. */
+
+void hpfs_remove_fnode(struct super_block *s, fnode_secno fno)
+{
+ struct buffer_head *bh;
+ struct fnode *fnode;
+ struct extended_attribute *ea;
+ struct extended_attribute *ea_end;
+ if (!(fnode = hpfs_map_fnode(s, fno, &bh))) return;
+ if (!fnode->dirflag) hpfs_remove_btree(s, &fnode->btree);
+ else hpfs_remove_dtree(s, fnode->u.external[0].disk_secno);
+ ea_end = fnode_end_ea(fnode);
+ for (ea = fnode_ea(fnode); ea < ea_end; ea = next_ea(ea))
+ if (ea->indirect)
+ hpfs_ea_remove(s, ea_sec(ea), ea->anode, ea_len(ea));
+ hpfs_ea_ext_remove(s, fnode->ea_secno, fnode->ea_anode, fnode->ea_size_l);
+ brelse(bh);
+ hpfs_free_sectors(s, fno, 1);
+}
diff --git a/fs/hpfs/buffer.c b/fs/hpfs/buffer.c
new file mode 100644
index 00000000000000..7be332f697669a
--- /dev/null
+++ b/fs/hpfs/buffer.c
@@ -0,0 +1,268 @@
+/*
+ * linux/fs/hpfs/buffer.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * general buffer i/o
+ */
+
+#include <linux/string.h>
+#include "hpfs_fn.h"
+
+void hpfs_lock_creation(struct super_block *s)
+{
+#ifdef DEBUG_LOCKS
+ printk("lock creation\n");
+#endif
+ while (s->s_hpfs_creation_de_lock) sleep_on(&s->s_hpfs_creation_de);
+ s->s_hpfs_creation_de_lock = 1;
+}
+
+void hpfs_unlock_creation(struct super_block *s)
+{
+#ifdef DEBUG_LOCKS
+ printk("unlock creation\n");
+#endif
+ s->s_hpfs_creation_de_lock = 0;
+ wake_up(&s->s_hpfs_creation_de);
+}
+
+void hpfs_lock_iget(struct super_block *s, int mode)
+{
+#ifdef DEBUG_LOCKS
+ printk("lock iget\n");
+#endif
+ while (s->s_hpfs_rd_inode) sleep_on(&s->s_hpfs_iget_q);
+ s->s_hpfs_rd_inode = mode;
+}
+
+void hpfs_unlock_iget(struct super_block *s)
+{
+#ifdef DEBUG_LOCKS
+ printk("unlock iget\n");
+#endif
+ s->s_hpfs_rd_inode = 0;
+ wake_up(&s->s_hpfs_iget_q);
+}
+
+void hpfs_lock_inode(struct inode *i)
+{
+ if (i) down(&i->i_hpfs_sem);
+}
+
+void hpfs_unlock_inode(struct inode *i)
+{
+ if (i) up(&i->i_hpfs_sem);
+}
+
+void hpfs_lock_2inodes(struct inode *i1, struct inode *i2)
+{
+ if (!i1) { if (i2) down(&i2->i_hpfs_sem); return; }
+ if (!i2) { if (i1) down(&i1->i_hpfs_sem); return; }
+ if (i1->i_ino < i2->i_ino) {
+ down(&i1->i_hpfs_sem);
+ down(&i2->i_hpfs_sem);
+ } else if (i1->i_ino > i2->i_ino) {
+ down(&i2->i_hpfs_sem);
+ down(&i1->i_hpfs_sem);
+ } else down(&i1->i_hpfs_sem);
+}
+
+void hpfs_unlock_2inodes(struct inode *i1, struct inode *i2)
+{
+ if (!i1) { if (i2) up(&i2->i_hpfs_sem); return; }
+ if (!i2) { if (i1) up(&i1->i_hpfs_sem); return; }
+ if (i1->i_ino < i2->i_ino) {
+ up(&i2->i_hpfs_sem);
+ up(&i1->i_hpfs_sem);
+ } else if (i1->i_ino > i2->i_ino) {
+ up(&i1->i_hpfs_sem);
+ up(&i2->i_hpfs_sem);
+ } else up(&i1->i_hpfs_sem);
+}
+
+void hpfs_lock_3inodes(struct inode *i1, struct inode *i2, struct inode *i3)
+{
+ if (!i1) { hpfs_lock_2inodes(i2, i3); return; }
+ if (!i2) { hpfs_lock_2inodes(i1, i3); return; }
+ if (!i3) { hpfs_lock_2inodes(i1, i2); return; }
+ if (i1->i_ino < i2->i_ino && i1->i_ino < i3->i_ino) {
+ down(&i1->i_hpfs_sem);
+ hpfs_lock_2inodes(i2, i3);
+ } else if (i2->i_ino < i1->i_ino && i2->i_ino < i3->i_ino) {
+ down(&i2->i_hpfs_sem);
+ hpfs_lock_2inodes(i1, i3);
+ } else if (i3->i_ino < i1->i_ino && i3->i_ino < i2->i_ino) {
+ down(&i3->i_hpfs_sem);
+ hpfs_lock_2inodes(i1, i2);
+ } else if (i1->i_ino != i2->i_ino) hpfs_lock_2inodes(i1, i2);
+ else hpfs_lock_2inodes(i1, i3);
+}
+
+void hpfs_unlock_3inodes(struct inode *i1, struct inode *i2, struct inode *i3)
+{
+ if (!i1) { hpfs_unlock_2inodes(i2, i3); return; }
+ if (!i2) { hpfs_unlock_2inodes(i1, i3); return; }
+ if (!i3) { hpfs_unlock_2inodes(i1, i2); return; }
+ if (i1->i_ino < i2->i_ino && i1->i_ino < i3->i_ino) {
+ hpfs_unlock_2inodes(i2, i3);
+ up(&i1->i_hpfs_sem);
+ } else if (i2->i_ino < i1->i_ino && i2->i_ino < i3->i_ino) {
+ hpfs_unlock_2inodes(i1, i3);
+ up(&i2->i_hpfs_sem);
+ } else if (i3->i_ino < i1->i_ino && i3->i_ino < i2->i_ino) {
+ hpfs_unlock_2inodes(i1, i2);
+ up(&i3->i_hpfs_sem);
+ } else if (i1->i_ino != i2->i_ino) hpfs_unlock_2inodes(i1, i2);
+ else hpfs_unlock_2inodes(i1, i3);
+}
+
+/* Map a sector into a buffer and return pointers to it and to the buffer. */
+
+void *hpfs_map_sector(struct super_block *s, unsigned secno, struct buffer_head **bhp,
+ int ahead)
+{
+ struct buffer_head *bh;
+
+ cond_resched();
+
+ *bhp = bh = sb_bread(s, secno);
+ if (bh != NULL)
+ return bh->b_data;
+ else {
+ printk("HPFS: hpfs_map_sector: read error\n");
+ return NULL;
+ }
+}
+
+/* Like hpfs_map_sector but don't read anything */
+
+void *hpfs_get_sector(struct super_block *s, unsigned secno, struct buffer_head **bhp)
+{
+ struct buffer_head *bh;
+ /*return hpfs_map_sector(s, secno, bhp, 0);*/
+
+ cond_resched();
+
+ if ((*bhp = bh = sb_getblk(s, secno)) != NULL) {
+ if (!buffer_uptodate(bh)) wait_on_buffer(bh);
+ mark_buffer_uptodate(bh, 1);
+ return bh->b_data;
+ } else {
+ printk("HPFS: hpfs_get_sector: getblk failed\n");
+ return NULL;
+ }
+}
+
+/* Map 4 sectors into a 4buffer and return pointers to it and to the buffer. */
+
+void *hpfs_map_4sectors(struct super_block *s, unsigned secno, struct quad_buffer_head *qbh,
+ int ahead)
+{
+ struct buffer_head *bh;
+ char *data;
+
+ cond_resched();
+
+ if (secno & 3) {
+ printk("HPFS: hpfs_map_4sectors: unaligned read\n");
+ return 0;
+ }
+
+ qbh->data = data = (char *)kmalloc(2048, GFP_KERNEL);
+ if (!data) {
+ printk("HPFS: hpfs_map_4sectors: out of memory\n");
+ goto bail;
+ }
+
+ qbh->bh[0] = bh = sb_bread(s, secno);
+ if (!bh)
+ goto bail0;
+ memcpy(data, bh->b_data, 512);
+
+ qbh->bh[1] = bh = sb_bread(s, secno + 1);
+ if (!bh)
+ goto bail1;
+ memcpy(data + 512, bh->b_data, 512);
+
+ qbh->bh[2] = bh = sb_bread(s, secno + 2);
+ if (!bh)
+ goto bail2;
+ memcpy(data + 2 * 512, bh->b_data, 512);
+
+ qbh->bh[3] = bh = sb_bread(s, secno + 3);
+ if (!bh)
+ goto bail3;
+ memcpy(data + 3 * 512, bh->b_data, 512);
+
+ return data;
+
+ bail3:
+ brelse(qbh->bh[2]);
+ bail2:
+ brelse(qbh->bh[1]);
+ bail1:
+ brelse(qbh->bh[0]);
+ bail0:
+ kfree(data);
+ printk("HPFS: hpfs_map_4sectors: read error\n");
+ bail:
+ return NULL;
+}
+
+/* Don't read sectors */
+
+void *hpfs_get_4sectors(struct super_block *s, unsigned secno,
+ struct quad_buffer_head *qbh)
+{
+ cond_resched();
+
+ if (secno & 3) {
+ printk("HPFS: hpfs_get_4sectors: unaligned read\n");
+ return 0;
+ }
+
+ /*return hpfs_map_4sectors(s, secno, qbh, 0);*/
+ if (!(qbh->data = kmalloc(2048, GFP_KERNEL))) {
+ printk("HPFS: hpfs_get_4sectors: out of memory\n");
+ return NULL;
+ }
+ if (!(hpfs_get_sector(s, secno, &qbh->bh[0]))) goto bail0;
+ if (!(hpfs_get_sector(s, secno + 1, &qbh->bh[1]))) goto bail1;
+ if (!(hpfs_get_sector(s, secno + 2, &qbh->bh[2]))) goto bail2;
+ if (!(hpfs_get_sector(s, secno + 3, &qbh->bh[3]))) goto bail3;
+ memcpy(qbh->data, qbh->bh[0]->b_data, 512);
+ memcpy(qbh->data + 512, qbh->bh[1]->b_data, 512);
+ memcpy(qbh->data + 2*512, qbh->bh[2]->b_data, 512);
+ memcpy(qbh->data + 3*512, qbh->bh[3]->b_data, 512);
+ return qbh->data;
+
+ bail3: brelse(qbh->bh[2]);
+ bail2: brelse(qbh->bh[1]);
+ bail1: brelse(qbh->bh[0]);
+ bail0:
+ return NULL;
+}
+
+
+void hpfs_brelse4(struct quad_buffer_head *qbh)
+{
+ brelse(qbh->bh[3]);
+ brelse(qbh->bh[2]);
+ brelse(qbh->bh[1]);
+ brelse(qbh->bh[0]);
+ kfree(qbh->data);
+}
+
+void hpfs_mark_4buffers_dirty(struct quad_buffer_head *qbh)
+{
+ PRINTK(("hpfs_mark_4buffers_dirty\n"));
+ memcpy(qbh->bh[0]->b_data, qbh->data, 512);
+ memcpy(qbh->bh[1]->b_data, qbh->data + 512, 512);
+ memcpy(qbh->bh[2]->b_data, qbh->data + 2 * 512, 512);
+ memcpy(qbh->bh[3]->b_data, qbh->data + 3 * 512, 512);
+ mark_buffer_dirty(qbh->bh[0]);
+ mark_buffer_dirty(qbh->bh[1]);
+ mark_buffer_dirty(qbh->bh[2]);
+ mark_buffer_dirty(qbh->bh[3]);
+}
diff --git a/fs/hpfs/dentry.c b/fs/hpfs/dentry.c
new file mode 100644
index 00000000000000..3aad4ba50ce253
--- /dev/null
+++ b/fs/hpfs/dentry.c
@@ -0,0 +1,60 @@
+/*
+ * linux/fs/hpfs/dentry.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * dcache operations
+ */
+
+#include "hpfs_fn.h"
+
+/*
+ * Note: the dentry argument is the parent dentry.
+ */
+
+int hpfs_hash_dentry(struct dentry *dentry, struct qstr *qstr)
+{
+ unsigned long hash;
+ int i;
+ unsigned l = qstr->len;
+
+ if (l == 1) if (qstr->name[0]=='.') goto x;
+ if (l == 2) if (qstr->name[0]=='.' || qstr->name[1]=='.') goto x;
+ hpfs_adjust_length((char *)qstr->name, &l);
+ /*if (hpfs_chk_name((char *)qstr->name,&l))*/
+ /*return -ENAMETOOLONG;*/
+ /*return -ENOENT;*/
+ x:
+
+ hash = init_name_hash();
+ for (i = 0; i < l; i++)
+ hash = partial_name_hash(hpfs_upcase(dentry->d_sb->s_hpfs_cp_table,qstr->name[i]), hash);
+ qstr->hash = end_name_hash(hash);
+
+ return 0;
+}
+
+int hpfs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
+{
+ unsigned al=a->len;
+ unsigned bl=b->len;
+ hpfs_adjust_length((char *)a->name, &al);
+ /*hpfs_adjust_length((char *)b->name, &bl);*/
+ /* 'a' is the qstr of an already existing dentry, so the name
+ * must be valid. 'b' must be validated first.
+ */
+
+ if (hpfs_chk_name((char *)b->name, &bl)) return 1;
+ if (hpfs_compare_names(dentry->d_sb, (char *)a->name, al, (char *)b->name, bl, 0)) return 1;
+ return 0;
+}
+
+struct dentry_operations hpfs_dentry_operations = {
+ d_hash: hpfs_hash_dentry,
+ d_compare: hpfs_compare_dentry,
+};
+
+void hpfs_set_dentry_operations(struct dentry *dentry)
+{
+ dentry->d_op = &hpfs_dentry_operations;
+}
diff --git a/fs/hpfs/dir.c b/fs/hpfs/dir.c
new file mode 100644
index 00000000000000..f2a732869ef9f6
--- /dev/null
+++ b/fs/hpfs/dir.c
@@ -0,0 +1,281 @@
+/*
+ * linux/fs/hpfs/dir.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * directory VFS functions
+ */
+
+#include "hpfs_fn.h"
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+
+int hpfs_dir_release(struct inode *inode, struct file *filp)
+{
+ lock_kernel();
+ hpfs_del_pos(inode, &filp->f_pos);
+ /*hpfs_write_if_changed(inode);*/
+ unlock_kernel();
+ return 0;
+}
+
+/* This is slow, but it's not used often */
+
+loff_t hpfs_dir_lseek(struct file *filp, loff_t off, int whence)
+{
+ loff_t new_off = off + (whence == 1 ? filp->f_pos : 0);
+ loff_t pos;
+ struct quad_buffer_head qbh;
+ struct inode *i = filp->f_dentry->d_inode;
+ struct super_block *s = i->i_sb;
+ /*printk("dir lseek\n");*/
+ if (new_off == 0 || new_off == 1 || new_off == 11 || new_off == 12 || new_off == 13) goto ok;
+ hpfs_lock_inode(i);
+ pos = ((loff_t) hpfs_de_as_down_as_possible(s, i->i_hpfs_dno) << 4) + 1;
+ while (pos != new_off) {
+ if (map_pos_dirent(i, &pos, &qbh)) hpfs_brelse4(&qbh);
+ else goto fail;
+ if (pos == 12) goto fail;
+ }
+ hpfs_unlock_inode(i);
+ ok:
+ return filp->f_pos = new_off;
+ fail:
+ hpfs_unlock_inode(i);
+ /*printk("illegal lseek: %016llx\n", new_off);*/
+ return -ESPIPE;
+}
+
+int hpfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ struct quad_buffer_head qbh;
+ struct hpfs_dirent *de;
+ int lc;
+ long old_pos;
+ char *tempname;
+ int c1, c2 = 0;
+
+ if (inode->i_sb->s_hpfs_chk) {
+ if (hpfs_chk_sectors(inode->i_sb, inode->i_ino, 1, "dir_fnode"))
+ return -EFSERROR;
+ if (hpfs_chk_sectors(inode->i_sb, inode->i_hpfs_dno, 4, "dir_dnode"))
+ return -EFSERROR;
+ }
+ if (inode->i_sb->s_hpfs_chk >= 2) {
+ struct buffer_head *bh;
+ struct fnode *fno;
+ int e = 0;
+ if (!(fno = hpfs_map_fnode(inode->i_sb, inode->i_ino, &bh)))
+ return -EIOERROR;
+ if (!fno->dirflag) {
+ e = 1;
+ hpfs_error(inode->i_sb, "not a directory, fnode %08x",inode->i_ino);
+ }
+ if (inode->i_hpfs_dno != fno->u.external[0].disk_secno) {
+ e = 1;
+ hpfs_error(inode->i_sb, "corrupted inode: i_hpfs_dno == %08x, fnode -> dnode == %08x", inode->i_hpfs_dno, fno->u.external[0].disk_secno);
+ }
+ brelse(bh);
+ if (e) return -EFSERROR;
+ }
+ lc = inode->i_sb->s_hpfs_lowercase;
+ if (filp->f_pos == 12) { /* diff -r requires this (note, that diff -r */
+ filp->f_pos = 13; /* also fails on msdos filesystem in 2.0) */
+ return 0;
+ }
+ if (filp->f_pos == 13) return -ENOENT;
+
+ hpfs_lock_inode(inode);
+
+ while (1) {
+ again:
+ /* This won't work when cycle is longer than number of dirents
+ accepted by filldir, but what can I do?
+ maybe killall -9 ls helps */
+ if (inode->i_sb->s_hpfs_chk)
+ if (hpfs_stop_cycles(inode->i_sb, filp->f_pos, &c1, &c2, "hpfs_readdir")) {
+ hpfs_unlock_inode(inode);
+ return -EFSERROR;
+ }
+ if (filp->f_pos == 12) {
+ hpfs_unlock_inode(inode);
+ return 0;
+ }
+ if (filp->f_pos == 3 || filp->f_pos == 4 || filp->f_pos == 5) {
+ printk("HPFS: warning: pos==%d\n",(int)filp->f_pos);
+ hpfs_unlock_inode(inode);
+ return 0;
+ }
+ if (filp->f_pos == 0) {
+ if (filldir(dirent, ".", 1, filp->f_pos, inode->i_ino, DT_DIR) < 0) {
+ hpfs_unlock_inode(inode);
+ return 0;
+ }
+ filp->f_pos = 11;
+ }
+ if (filp->f_pos == 11) {
+ if (filldir(dirent, "..", 2, filp->f_pos, inode->i_hpfs_parent_dir, DT_DIR) < 0) {
+ hpfs_unlock_inode(inode);
+ return 0;
+ }
+ filp->f_pos = 1;
+ }
+ if (filp->f_pos == 1) {
+ filp->f_pos = ((loff_t) hpfs_de_as_down_as_possible(inode->i_sb, inode->i_hpfs_dno) << 4) + 1;
+ hpfs_add_pos(inode, &filp->f_pos);
+ filp->f_version = inode->i_version;
+ }
+ /*if (filp->f_version != inode->i_version) {
+ hpfs_unlock_inode(inode);
+ return -ENOENT;
+ }*/
+ old_pos = filp->f_pos;
+ if (!(de = map_pos_dirent(inode, &filp->f_pos, &qbh))) {
+ hpfs_unlock_inode(inode);
+ return -EIOERROR;
+ }
+ if (de->first || de->last) {
+ if (inode->i_sb->s_hpfs_chk) {
+ if (de->first && !de->last && (de->namelen != 2 || de ->name[0] != 1 || de->name[1] != 1)) hpfs_error(inode->i_sb, "hpfs_readdir: bad ^A^A entry; pos = %08x", old_pos);
+ if (de->last && (de->namelen != 1 || de ->name[0] != 255)) hpfs_error(inode->i_sb, "hpfs_readdir: bad \\377 entry; pos = %08x", old_pos);
+ }
+ hpfs_brelse4(&qbh);
+ goto again;
+ }
+ tempname = hpfs_translate_name(inode->i_sb, de->name, de->namelen, lc, de->not_8x3);
+ if (filldir(dirent, tempname, de->namelen, old_pos, de->fnode, DT_UNKNOWN) < 0) {
+ filp->f_pos = old_pos;
+ if (tempname != (char *)de->name) kfree(tempname);
+ hpfs_brelse4(&qbh);
+ hpfs_unlock_inode(inode);
+ return 0;
+ }
+ if (tempname != (char *)de->name) kfree(tempname);
+ hpfs_brelse4(&qbh);
+ }
+}
+
+/*
+ * lookup. Search the specified directory for the specified name, set
+ * *result to the corresponding inode.
+ *
+ * lookup uses the inode number to tell read_inode whether it is reading
+ * the inode of a directory or a file -- file ino's are odd, directory
+ * ino's are even. read_inode avoids i/o for file inodes; everything
+ * needed is up here in the directory. (And file fnodes are out in
+ * the boondocks.)
+ *
+ * - M.P.: this is over, sometimes we've got to read file's fnode for eas
+ * inode numbers are just fnode sector numbers; iget lock is used
+ * to tell read_inode to read fnode or not.
+ */
+
+struct dentry *hpfs_lookup(struct inode *dir, struct dentry *dentry)
+{
+ const char *name = dentry->d_name.name;
+ unsigned len = dentry->d_name.len;
+ struct quad_buffer_head qbh;
+ struct hpfs_dirent *de;
+ ino_t ino;
+ int err;
+ struct inode *result = NULL;
+
+ if ((err = hpfs_chk_name((char *)name, &len))) {
+ if (err == -ENAMETOOLONG) return ERR_PTR(-ENAMETOOLONG);
+ goto end_add;
+ }
+
+ hpfs_lock_inode(dir);
+ /*
+ * '.' and '..' will never be passed here.
+ */
+
+ de = map_dirent(dir, dir->i_hpfs_dno, (char *) name, len, NULL, &qbh);
+
+ /*
+ * This is not really a bailout, just means file not found.
+ */
+
+ if (!de) goto end;
+
+ /*
+ * Get inode number, what we're after.
+ */
+
+ ino = de->fnode;
+
+ /*
+ * Go find or make an inode.
+ */
+
+ hpfs_lock_iget(dir->i_sb, de->directory || (de->ea_size && dir->i_sb->s_hpfs_eas) ? 1 : 2);
+ if (!(result = iget(dir->i_sb, ino))) {
+ hpfs_unlock_iget(dir->i_sb);
+ hpfs_error(dir->i_sb, "hpfs_lookup: can't get inode");
+ goto bail1;
+ }
+ if (!de->directory) result->i_hpfs_parent_dir = dir->i_ino;
+ hpfs_unlock_iget(dir->i_sb);
+
+ hpfs_decide_conv(result, (char *)name, len);
+
+ if (de->has_acl || de->has_xtd_perm) if (!(dir->i_sb->s_flags & MS_RDONLY)) {
+ hpfs_error(result->i_sb, "ACLs or XPERM found. This is probably HPFS386. This driver doesn't support it now. Send me some info on these structures");
+ goto bail1;
+ }
+
+ /*
+ * Fill in the info from the directory if this is a newly created
+ * inode.
+ */
+
+ if (!result->i_ctime) {
+ if (!(result->i_ctime = local_to_gmt(dir->i_sb, de->creation_date)))
+ result->i_ctime = 1;
+ result->i_mtime = local_to_gmt(dir->i_sb, de->write_date);
+ result->i_atime = local_to_gmt(dir->i_sb, de->read_date);
+ result->i_hpfs_ea_size = de->ea_size;
+ if (!result->i_hpfs_ea_mode && de->read_only)
+ result->i_mode &= ~0222;
+ if (!de->directory) {
+ if (result->i_size == -1) {
+ result->i_size = de->file_size;
+ result->i_data.a_ops = &hpfs_aops;
+ result->u.hpfs_i.mmu_private = result->i_size;
+ /*
+ * i_blocks should count the fnode and any anodes.
+ * We count 1 for the fnode and don't bother about
+ * anodes -- the disk heads are on the directory band
+ * and we want them to stay there.
+ */
+ result->i_blocks = 1 + ((result->i_size + 511) >> 9);
+ }
+ }
+ }
+
+ hpfs_brelse4(&qbh);
+
+ /*
+ * Made it.
+ */
+
+ end:
+ hpfs_unlock_inode(dir);
+ end_add:
+ hpfs_set_dentry_operations(dentry);
+ d_add(dentry, result);
+ return NULL;
+
+ /*
+ * Didn't.
+ */
+ bail1:
+
+ hpfs_brelse4(&qbh);
+
+ /*bail:*/
+
+ hpfs_unlock_inode(dir);
+ return ERR_PTR(-ENOENT);
+}
diff --git a/fs/hpfs/dnode.c b/fs/hpfs/dnode.c
new file mode 100644
index 00000000000000..78286ad36d118b
--- /dev/null
+++ b/fs/hpfs/dnode.c
@@ -0,0 +1,1070 @@
+/*
+ * linux/fs/hpfs/dnode.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * handling directory dnode tree - adding, deleteing & searching for dirents
+ */
+
+#include "hpfs_fn.h"
+
+static loff_t get_pos(struct dnode *d, struct hpfs_dirent *fde)
+{
+ struct hpfs_dirent *de;
+ struct hpfs_dirent *de_end = dnode_end_de(d);
+ int i = 1;
+ for (de = dnode_first_de(d); de < de_end; de = de_next_de(de)) {
+ if (de == fde) return ((loff_t) d->self << 4) | (loff_t)i;
+ i++;
+ }
+ printk("HPFS: get_pos: not_found\n");
+ return ((loff_t)d->self << 4) | (loff_t)1;
+}
+
+void hpfs_add_pos(struct inode *inode, loff_t *pos)
+{
+ int i = 0;
+ loff_t **ppos;
+ if (inode->i_hpfs_rddir_off)
+ for (; inode->i_hpfs_rddir_off[i]; i++)
+ if (inode->i_hpfs_rddir_off[i] == pos) return;
+ if (!(i&0x0f)) {
+ if (!(ppos = kmalloc((i+0x11) * sizeof(loff_t*), GFP_KERNEL))) {
+ printk("HPFS: out of memory for position list\n");
+ return;
+ }
+ if (inode->i_hpfs_rddir_off) {
+ memcpy(ppos, inode->i_hpfs_rddir_off, i * sizeof(loff_t));
+ kfree(inode->i_hpfs_rddir_off);
+ }
+ inode->i_hpfs_rddir_off = ppos;
+ }
+ inode->i_hpfs_rddir_off[i] = pos;
+ inode->i_hpfs_rddir_off[i + 1] = NULL;
+}
+
+void hpfs_del_pos(struct inode *inode, loff_t *pos)
+{
+ loff_t **i, **j;
+ if (!inode->i_hpfs_rddir_off) goto not_f;
+ for (i = inode->i_hpfs_rddir_off; *i; i++) if (*i == pos) goto fnd;
+ goto not_f;
+ fnd:
+ for (j = i + 1; *j; j++) ;
+ *i = *(j - 1);
+ *(j - 1) = NULL;
+ if (j - 1 == inode->i_hpfs_rddir_off) {
+ kfree(inode->i_hpfs_rddir_off);
+ inode->i_hpfs_rddir_off = NULL;
+ }
+ return;
+ not_f:
+ /*printk("HPFS: warning: position pointer %p->%08x not found\n", pos, (int)*pos);*/
+ return;
+}
+
+static void for_all_poss(struct inode *inode, void (*f)(loff_t *, loff_t, loff_t),
+ loff_t p1, loff_t p2)
+{
+ loff_t **i;
+ if (!inode->i_hpfs_rddir_off) return;
+ for (i = inode->i_hpfs_rddir_off; *i; i++) (*f)(*i, p1, p2);
+ return;
+}
+
+void hpfs_pos_subst(loff_t *p, loff_t f, loff_t t)
+{
+ if (*p == f) *p = t;
+}
+
+/*void hpfs_hpfs_pos_substd(loff_t *p, loff_t f, loff_t t)
+{
+ if ((*p & ~0x3f) == (f & ~0x3f)) *p = (t & ~0x3f) | (*p & 0x3f);
+}*/
+
+void hpfs_pos_ins(loff_t *p, loff_t d, loff_t c)
+{
+ if ((*p & ~0x3f) == (d & ~0x3f) && (*p & 0x3f) >= (d & 0x3f)) {
+ int n = (*p & 0x3f) + c;
+ if (n > 0x3f) printk("HPFS: hpfs_pos_ins: %08x + %d\n", (int)*p, (int)c >> 8);
+ else *p = (*p & ~0x3f) | n;
+ }
+}
+
+void hpfs_pos_del(loff_t *p, loff_t d, loff_t c)
+{
+ if ((*p & ~0x3f) == (d & ~0x3f) && (*p & 0x3f) >= (d & 0x3f)) {
+ int n = (*p & 0x3f) - c;
+ if (n < 1) printk("HPFS: hpfs_pos_ins: %08x - %d\n", (int)*p, (int)c >> 8);
+ else *p = (*p & ~0x3f) | n;
+ }
+}
+
+static struct hpfs_dirent *dnode_pre_last_de(struct dnode *d)
+{
+ struct hpfs_dirent *de, *de_end, *dee = NULL, *deee = NULL;
+ de_end = dnode_end_de(d);
+ for (de = dnode_first_de(d); de < de_end; de = de_next_de(de)) {
+ deee = dee; dee = de;
+ }
+ return deee;
+}
+
+static struct hpfs_dirent *dnode_last_de(struct dnode *d)
+{
+ struct hpfs_dirent *de, *de_end, *dee = NULL;
+ de_end = dnode_end_de(d);
+ for (de = dnode_first_de(d); de < de_end; de = de_next_de(de)) {
+ dee = de;
+ }
+ return dee;
+}
+
+static void set_last_pointer(struct super_block *s, struct dnode *d, dnode_secno ptr)
+{
+ struct hpfs_dirent *de;
+ if (!(de = dnode_last_de(d))) {
+ hpfs_error(s, "set_last_pointer: empty dnode %08x", d->self);
+ return;
+ }
+ if (s->s_hpfs_chk) {
+ if (de->down) {
+ hpfs_error(s, "set_last_pointer: dnode %08x has already last pointer %08x",
+ d->self, de_down_pointer(de));
+ return;
+ }
+ if (de->length != 32) {
+ hpfs_error(s, "set_last_pointer: bad last dirent in dnode %08x", d->self);
+ return;
+ }
+ }
+ if (ptr) {
+ if ((d->first_free += 4) > 2048) {
+ hpfs_error(s,"set_last_pointer: too long dnode %08x", d->self);
+ d->first_free -= 4;
+ return;
+ }
+ de->length = 36;
+ de->down = 1;
+ *(dnode_secno *)((char *)de + 32) = ptr;
+ }
+}
+
+/* Add an entry to dnode and don't care if it grows over 2048 bytes */
+
+struct hpfs_dirent *hpfs_add_de(struct super_block *s, struct dnode *d, unsigned char *name,
+ unsigned namelen, secno down_ptr)
+{
+ struct hpfs_dirent *de;
+ struct hpfs_dirent *de_end = dnode_end_de(d);
+ unsigned d_size = de_size(namelen, down_ptr);
+ for (de = dnode_first_de(d); de < de_end; de = de_next_de(de)) {
+ int c = hpfs_compare_names(s, name, namelen, de->name, de->namelen, de->last);
+ if (!c) {
+ hpfs_error(s, "name (%c,%d) already exists in dnode %08x", *name, namelen, d->self);
+ return NULL;
+ }
+ if (c < 0) break;
+ }
+ memmove((char *)de + d_size, de, (char *)de_end - (char *)de);
+ memset(de, 0, d_size);
+ if (down_ptr) {
+ *(int *)((char *)de + d_size - 4) = down_ptr;
+ de->down = 1;
+ }
+ de->length = d_size;
+ if (down_ptr) de->down = 1;
+ de->not_8x3 = hpfs_is_name_long(name, namelen);
+ de->namelen = namelen;
+ memcpy(de->name, name, namelen);
+ d->first_free += d_size;
+ return de;
+}
+
+/* Delete dirent and don't care about it's subtree */
+
+void hpfs_delete_de(struct super_block *s, struct dnode *d, struct hpfs_dirent *de)
+{
+ if (de->last) {
+ hpfs_error(s, "attempt to delete last dirent in dnode %08x", d->self);
+ return;
+ }
+ d->first_free -= de->length;
+ memmove(de, de_next_de(de), d->first_free + (char *)d - (char *)de);
+}
+
+static void fix_up_ptrs(struct super_block *s, struct dnode *d)
+{
+ struct hpfs_dirent *de;
+ struct hpfs_dirent *de_end = dnode_end_de(d);
+ dnode_secno dno = d->self;
+ for (de = dnode_first_de(d); de < de_end; de = de_next_de(de))
+ if (de->down) {
+ struct quad_buffer_head qbh;
+ struct dnode *dd;
+ if ((dd = hpfs_map_dnode(s, de_down_pointer(de), &qbh))) {
+ if (dd->up != dno || dd->root_dnode) {
+ dd->up = dno;
+ dd->root_dnode = 0;
+ hpfs_mark_4buffers_dirty(&qbh);
+ }
+ hpfs_brelse4(&qbh);
+ }
+ }
+}
+
+/* Add an entry to dnode and do dnode splitting if required */
+
+int hpfs_add_to_dnode(struct inode *i, dnode_secno dno, unsigned char *name, unsigned namelen,
+ struct hpfs_dirent *new_de, dnode_secno down_ptr)
+{
+ struct quad_buffer_head qbh, qbh1, qbh2;
+ struct dnode *d, *ad, *rd, *nd = NULL;
+ dnode_secno adno, rdno;
+ struct hpfs_dirent *de;
+ struct hpfs_dirent nde;
+ char *nname;
+ int h;
+ int pos;
+ struct buffer_head *bh;
+ struct fnode *fnode;
+ int c1, c2 = 0;
+ if (!(nname = kmalloc(256, GFP_KERNEL))) {
+ printk("HPFS: out of memory, can't add to dnode\n");
+ return 1;
+ }
+ go_up:
+ if (namelen >= 256) {
+ hpfs_error(i->i_sb, "hpfs_add_to_dnode: namelen == %d", namelen);
+ if (nd) kfree(nd);
+ kfree(nname);
+ return 1;
+ }
+ if (!(d = hpfs_map_dnode(i->i_sb, dno, &qbh))) {
+ if (nd) kfree(nd);
+ kfree(nname);
+ return 1;
+ }
+ go_up_a:
+ if (i->i_sb->s_hpfs_chk)
+ if (hpfs_stop_cycles(i->i_sb, dno, &c1, &c2, "hpfs_add_to_dnode")) {
+ hpfs_brelse4(&qbh);
+ if (nd) kfree(nd);
+ kfree(nname);
+ return 1;
+ }
+ if (d->first_free + de_size(namelen, down_ptr) <= 2048) {
+ loff_t t;
+ copy_de(de=hpfs_add_de(i->i_sb, d, name, namelen, down_ptr), new_de);
+ t = get_pos(d, de);
+ for_all_poss(i, hpfs_pos_ins, t, 1);
+ for_all_poss(i, hpfs_pos_subst, 4, t);
+ for_all_poss(i, hpfs_pos_subst, 5, t + 1);
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ if (nd) kfree(nd);
+ kfree(nname);
+ return 0;
+ }
+ if (!nd) if (!(nd = kmalloc(0x924, GFP_KERNEL))) {
+ /* 0x924 is a max size of dnode after adding a dirent with
+ max name length. We alloc this only once. There must
+ not be any error while splitting dnodes, otherwise the
+ whole directory, not only file we're adding, would
+ be lost. */
+ printk("HPFS: out of memory for dnode splitting\n");
+ hpfs_brelse4(&qbh);
+ kfree(nname);
+ return 1;
+ }
+ memcpy(nd, d, d->first_free);
+ copy_de(de = hpfs_add_de(i->i_sb, nd, name, namelen, down_ptr), new_de);
+ for_all_poss(i, hpfs_pos_ins, get_pos(nd, de), 1);
+ h = ((char *)dnode_last_de(nd) - (char *)nd) / 2 + 10;
+ if (!(ad = hpfs_alloc_dnode(i->i_sb, d->up, &adno, &qbh1, 0))) {
+ hpfs_error(i->i_sb, "unable to alloc dnode - dnode tree will be corrupted");
+ hpfs_brelse4(&qbh);
+ kfree(nd);
+ kfree(nname);
+ return 1;
+ }
+ i->i_size += 2048;
+ i->i_blocks += 4;
+ pos = 1;
+ for (de = dnode_first_de(nd); (char *)de_next_de(de) - (char *)nd < h; de = de_next_de(de)) {
+ copy_de(hpfs_add_de(i->i_sb, ad, de->name, de->namelen, de->down ? de_down_pointer(de) : 0), de);
+ for_all_poss(i, hpfs_pos_subst, ((loff_t)dno << 4) | pos, ((loff_t)adno << 4) | pos);
+ pos++;
+ }
+ copy_de(new_de = &nde, de);
+ memcpy(name = nname, de->name, namelen = de->namelen);
+ for_all_poss(i, hpfs_pos_subst, ((loff_t)dno << 4) | pos, 4);
+ down_ptr = adno;
+ set_last_pointer(i->i_sb, ad, de->down ? de_down_pointer(de) : 0);
+ de = de_next_de(de);
+ memmove((char *)nd + 20, de, nd->first_free + (char *)nd - (char *)de);
+ nd->first_free -= (char *)de - (char *)nd - 20;
+ memcpy(d, nd, nd->first_free);
+ for_all_poss(i, hpfs_pos_del, (loff_t)dno << 4, pos);
+ fix_up_ptrs(i->i_sb, ad);
+ if (!d->root_dnode) {
+ dno = ad->up = d->up;
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ hpfs_mark_4buffers_dirty(&qbh1);
+ hpfs_brelse4(&qbh1);
+ goto go_up;
+ }
+ if (!(rd = hpfs_alloc_dnode(i->i_sb, d->up, &rdno, &qbh2, 0))) {
+ hpfs_error(i->i_sb, "unable to alloc dnode - dnode tree will be corrupted");
+ hpfs_brelse4(&qbh);
+ hpfs_brelse4(&qbh1);
+ kfree(nd);
+ kfree(nname);
+ return 1;
+ }
+ i->i_size += 2048;
+ i->i_blocks += 4;
+ rd->root_dnode = 1;
+ rd->up = d->up;
+ if (!(fnode = hpfs_map_fnode(i->i_sb, d->up, &bh))) {
+ hpfs_free_dnode(i->i_sb, rdno);
+ hpfs_brelse4(&qbh);
+ hpfs_brelse4(&qbh1);
+ hpfs_brelse4(&qbh2);
+ kfree(nd);
+ kfree(nname);
+ return 1;
+ }
+ fnode->u.external[0].disk_secno = rdno;
+ mark_buffer_dirty(bh);
+ brelse(bh);
+ d->up = ad->up = i->i_hpfs_dno = rdno;
+ d->root_dnode = ad->root_dnode = 0;
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ hpfs_mark_4buffers_dirty(&qbh1);
+ hpfs_brelse4(&qbh1);
+ qbh = qbh2;
+ set_last_pointer(i->i_sb, rd, dno);
+ dno = rdno;
+ d = rd;
+ goto go_up_a;
+}
+
+/*
+ * Add an entry to directory btree.
+ * I hate such crazy directory structure.
+ * It's easy to read but terrible to write.
+ * I wrote this directory code 4 times.
+ * I hope, now it's finally bug-free.
+ */
+
+int hpfs_add_dirent(struct inode *i, unsigned char *name, unsigned namelen,
+ struct hpfs_dirent *new_de, int cdepth)
+{
+ struct dnode *d;
+ struct hpfs_dirent *de, *de_end;
+ struct quad_buffer_head qbh;
+ dnode_secno dno;
+ int c;
+ int c1, c2 = 0;
+ dno = i->i_hpfs_dno;
+ down:
+ if (i->i_sb->s_hpfs_chk)
+ if (hpfs_stop_cycles(i->i_sb, dno, &c1, &c2, "hpfs_add_dirent")) return 1;
+ if (!(d = hpfs_map_dnode(i->i_sb, dno, &qbh))) return 1;
+ de_end = dnode_end_de(d);
+ for (de = dnode_first_de(d); de < de_end; de = de_next_de(de)) {
+ if (!(c = hpfs_compare_names(i->i_sb, name, namelen, de->name, de->namelen, de->last))) {
+ hpfs_brelse4(&qbh);
+ return -1;
+ }
+ if (c < 0) {
+ if (de->down) {
+ dno = de_down_pointer(de);
+ hpfs_brelse4(&qbh);
+ goto down;
+ }
+ break;
+ }
+ }
+ hpfs_brelse4(&qbh);
+ if (!cdepth) hpfs_lock_creation(i->i_sb);
+ if (hpfs_check_free_dnodes(i->i_sb, FREE_DNODES_ADD)) {
+ c = 1;
+ goto ret;
+ }
+ i->i_version = ++event;
+ c = hpfs_add_to_dnode(i, dno, name, namelen, new_de, 0);
+ ret:
+ if (!cdepth) hpfs_unlock_creation(i->i_sb);
+ return c;
+}
+
+/*
+ * Find dirent with higher name in 'from' subtree and move it to 'to' dnode.
+ * Return the dnode we moved from (to be checked later if it's empty)
+ */
+
+static secno move_to_top(struct inode *i, dnode_secno from, dnode_secno to)
+{
+ dnode_secno dno, ddno;
+ dnode_secno chk_up = to;
+ struct dnode *dnode;
+ struct quad_buffer_head qbh;
+ struct hpfs_dirent *de, *nde;
+ int a;
+ loff_t t;
+ int c1, c2 = 0;
+ dno = from;
+ while (1) {
+ if (i->i_sb->s_hpfs_chk)
+ if (hpfs_stop_cycles(i->i_sb, dno, &c1, &c2, "move_to_top"))
+ return 0;
+ if (!(dnode = hpfs_map_dnode(i->i_sb, dno, &qbh))) return 0;
+ if (i->i_sb->s_hpfs_chk) {
+ if (dnode->up != chk_up) {
+ hpfs_error(i->i_sb, "move_to_top: up pointer from %08x should be %08x, is %08x",
+ dno, chk_up, dnode->up);
+ hpfs_brelse4(&qbh);
+ return 0;
+ }
+ chk_up = dno;
+ }
+ if (!(de = dnode_last_de(dnode))) {
+ hpfs_error(i->i_sb, "move_to_top: dnode %08x has no last de", dno);
+ hpfs_brelse4(&qbh);
+ return 0;
+ }
+ if (!de->down) break;
+ dno = de_down_pointer(de);
+ hpfs_brelse4(&qbh);
+ }
+ while (!(de = dnode_pre_last_de(dnode))) {
+ dnode_secno up = dnode->up;
+ hpfs_brelse4(&qbh);
+ hpfs_free_dnode(i->i_sb, dno);
+ i->i_size -= 2048;
+ i->i_blocks -= 4;
+ for_all_poss(i, hpfs_pos_subst, ((loff_t)dno << 4) | 1, 5);
+ if (up == to) return to;
+ if (!(dnode = hpfs_map_dnode(i->i_sb, up, &qbh))) return 0;
+ if (dnode->root_dnode) {
+ hpfs_error(i->i_sb, "move_to_top: got to root_dnode while moving from %08x to %08x", from, to);
+ hpfs_brelse4(&qbh);
+ return 0;
+ }
+ de = dnode_last_de(dnode);
+ if (!de || !de->down) {
+ hpfs_error(i->i_sb, "move_to_top: dnode %08x doesn't point down to %08x", up, dno);
+ hpfs_brelse4(&qbh);
+ return 0;
+ }
+ dnode->first_free -= 4;
+ de->length -= 4;
+ de->down = 0;
+ hpfs_mark_4buffers_dirty(&qbh);
+ dno = up;
+ }
+ t = get_pos(dnode, de);
+ for_all_poss(i, hpfs_pos_subst, t, 4);
+ for_all_poss(i, hpfs_pos_subst, t + 1, 5);
+ if (!(nde = kmalloc(de->length, GFP_KERNEL))) {
+ hpfs_error(i->i_sb, "out of memory for dirent - directory will be corrupted");
+ hpfs_brelse4(&qbh);
+ return 0;
+ }
+ memcpy(nde, de, de->length);
+ ddno = de->down ? de_down_pointer(de) : 0;
+ hpfs_delete_de(i->i_sb, dnode, de);
+ set_last_pointer(i->i_sb, dnode, ddno);
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ a = hpfs_add_to_dnode(i, to, nde->name, nde->namelen, nde, from);
+ kfree(nde);
+ if (a) return 0;
+ return dno;
+}
+
+/*
+ * Check if a dnode is empty and delete it from the tree
+ * (chkdsk doesn't like empty dnodes)
+ */
+
+static void delete_empty_dnode(struct inode *i, dnode_secno dno)
+{
+ struct quad_buffer_head qbh;
+ struct dnode *dnode;
+ dnode_secno down, up, ndown;
+ int p;
+ struct hpfs_dirent *de;
+ int c1, c2 = 0;
+ try_it_again:
+ if (hpfs_stop_cycles(i->i_sb, dno, &c1, &c2, "delete_empty_dnode")) return;
+ if (!(dnode = hpfs_map_dnode(i->i_sb, dno, &qbh))) return;
+ if (dnode->first_free > 56) goto end;
+ if (dnode->first_free == 52 || dnode->first_free == 56) {
+ struct hpfs_dirent *de_end;
+ int root = dnode->root_dnode;
+ up = dnode->up;
+ de = dnode_first_de(dnode);
+ down = de->down ? de_down_pointer(de) : 0;
+ if (i->i_sb->s_hpfs_chk) if (root && !down) {
+ hpfs_error(i->i_sb, "delete_empty_dnode: root dnode %08x is empty", dno);
+ goto end;
+ }
+ hpfs_brelse4(&qbh);
+ hpfs_free_dnode(i->i_sb, dno);
+ i->i_size -= 2048;
+ i->i_blocks -= 4;
+ if (root) {
+ struct fnode *fnode;
+ struct buffer_head *bh;
+ struct dnode *d1;
+ struct quad_buffer_head qbh1;
+ if (i->i_sb->s_hpfs_chk) if (up != i->i_ino) {
+ hpfs_error(i->i_sb, "bad pointer to fnode, dnode %08x, pointing to %08x, should be %08x", dno, up, i->i_ino);
+ return;
+ }
+ if ((d1 = hpfs_map_dnode(i->i_sb, down, &qbh1))) {
+ d1->up = up;
+ d1->root_dnode = 1;
+ hpfs_mark_4buffers_dirty(&qbh1);
+ hpfs_brelse4(&qbh1);
+ }
+ if ((fnode = hpfs_map_fnode(i->i_sb, up, &bh))) {
+ fnode->u.external[0].disk_secno = down;
+ mark_buffer_dirty(bh);
+ brelse(bh);
+ }
+ i->i_hpfs_dno = down;
+ for_all_poss(i, hpfs_pos_subst, ((loff_t)dno << 4) | 1, (loff_t) 12);
+ return;
+ }
+ if (!(dnode = hpfs_map_dnode(i->i_sb, up, &qbh))) return;
+ p = 1;
+ de_end = dnode_end_de(dnode);
+ for (de = dnode_first_de(dnode); de < de_end; de = de_next_de(de), p++)
+ if (de->down) if (de_down_pointer(de) == dno) goto fnd;
+ hpfs_error(i->i_sb, "delete_empty_dnode: pointer to dnode %08x not found in dnode %08x", dno, up);
+ goto end;
+ fnd:
+ for_all_poss(i, hpfs_pos_subst, ((loff_t)dno << 4) | 1, ((loff_t)up << 4) | p);
+ if (!down) {
+ de->down = 0;
+ de->length -= 4;
+ dnode->first_free -= 4;
+ memmove(de_next_de(de), (char *)de_next_de(de) + 4,
+ (char *)dnode + dnode->first_free - (char *)de_next_de(de));
+ } else {
+ struct dnode *d1;
+ struct quad_buffer_head qbh1;
+ *(dnode_secno *) ((void *) de + de->length - 4) = down;
+ if ((d1 = hpfs_map_dnode(i->i_sb, down, &qbh1))) {
+ d1->up = up;
+ hpfs_mark_4buffers_dirty(&qbh1);
+ hpfs_brelse4(&qbh1);
+ }
+ }
+ } else {
+ hpfs_error(i->i_sb, "delete_empty_dnode: dnode %08x, first_free == %03x", dno, dnode->first_free);
+ goto end;
+ }
+
+ if (!de->last) {
+ struct hpfs_dirent *de_next = de_next_de(de);
+ struct hpfs_dirent *de_cp;
+ struct dnode *d1;
+ struct quad_buffer_head qbh1;
+ if (!de_next->down) goto endm;
+ ndown = de_down_pointer(de_next);
+ if (!(de_cp = kmalloc(de->length, GFP_KERNEL))) {
+ printk("HPFS: out of memory for dtree balancing\n");
+ goto endm;
+ }
+ memcpy(de_cp, de, de->length);
+ hpfs_delete_de(i->i_sb, dnode, de);
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ for_all_poss(i, hpfs_pos_subst, ((loff_t)up << 4) | p, 4);
+ for_all_poss(i, hpfs_pos_del, ((loff_t)up << 4) | p, 1);
+ if (de_cp->down) if ((d1 = hpfs_map_dnode(i->i_sb, de_down_pointer(de_cp), &qbh1))) {
+ d1->up = ndown;
+ hpfs_mark_4buffers_dirty(&qbh1);
+ hpfs_brelse4(&qbh1);
+ }
+ hpfs_add_to_dnode(i, ndown, de_cp->name, de_cp->namelen, de_cp, de_cp->down ? de_down_pointer(de_cp) : 0);
+ /*printk("UP-TO-DNODE: %08x (ndown = %08x, down = %08x, dno = %08x)\n", up, ndown, down, dno);*/
+ dno = up;
+ kfree(de_cp);
+ goto try_it_again;
+ } else {
+ struct hpfs_dirent *de_prev = dnode_pre_last_de(dnode);
+ struct hpfs_dirent *de_cp;
+ struct dnode *d1;
+ struct quad_buffer_head qbh1;
+ dnode_secno dlp;
+ if (!de_prev) {
+ hpfs_error(i->i_sb, "delete_empty_dnode: empty dnode %08x", up);
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ dno = up;
+ goto try_it_again;
+ }
+ if (!de_prev->down) goto endm;
+ ndown = de_down_pointer(de_prev);
+ if ((d1 = hpfs_map_dnode(i->i_sb, ndown, &qbh1))) {
+ struct hpfs_dirent *del = dnode_last_de(d1);
+ dlp = del->down ? de_down_pointer(del) : 0;
+ if (!dlp && down) {
+ if (d1->first_free > 2044) {
+ if (i->i_sb->s_hpfs_chk >= 2) {
+ printk("HPFS: warning: unbalanced dnode tree, see hpfs.txt 4 more info\n");
+ printk("HPFS: warning: terminating balancing operation\n");
+ }
+ hpfs_brelse4(&qbh1);
+ goto endm;
+ }
+ if (i->i_sb->s_hpfs_chk >= 2) {
+ printk("HPFS: warning: unbalanced dnode tree, see hpfs.txt 4 more info\n");
+ printk("HPFS: warning: goin'on\n");
+ }
+ del->length += 4;
+ del->down = 1;
+ d1->first_free += 4;
+ }
+ if (dlp && !down) {
+ del->length -= 4;
+ del->down = 0;
+ d1->first_free -= 4;
+ } else if (down)
+ *(dnode_secno *) ((void *) del + del->length - 4) = down;
+ } else goto endm;
+ if (!(de_cp = kmalloc(de_prev->length, GFP_KERNEL))) {
+ printk("HPFS: out of memory for dtree balancing\n");
+ hpfs_brelse4(&qbh1);
+ goto endm;
+ }
+ hpfs_mark_4buffers_dirty(&qbh1);
+ hpfs_brelse4(&qbh1);
+ memcpy(de_cp, de_prev, de_prev->length);
+ hpfs_delete_de(i->i_sb, dnode, de_prev);
+ if (!de_prev->down) {
+ de_prev->length += 4;
+ de_prev->down = 1;
+ dnode->first_free += 4;
+ }
+ *(dnode_secno *) ((void *) de_prev + de_prev->length - 4) = ndown;
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ for_all_poss(i, hpfs_pos_subst, ((loff_t)up << 4) | (p - 1), 4);
+ for_all_poss(i, hpfs_pos_subst, ((loff_t)up << 4) | p, ((loff_t)up << 4) | (p - 1));
+ if (down) if ((d1 = hpfs_map_dnode(i->i_sb, de_down_pointer(de), &qbh1))) {
+ d1->up = ndown;
+ hpfs_mark_4buffers_dirty(&qbh1);
+ hpfs_brelse4(&qbh1);
+ }
+ hpfs_add_to_dnode(i, ndown, de_cp->name, de_cp->namelen, de_cp, dlp);
+ dno = up;
+ kfree(de_cp);
+ goto try_it_again;
+ }
+ endm:
+ hpfs_mark_4buffers_dirty(&qbh);
+ end:
+ hpfs_brelse4(&qbh);
+}
+
+
+/* Delete dirent from directory */
+
+int hpfs_remove_dirent(struct inode *i, dnode_secno dno, struct hpfs_dirent *de,
+ struct quad_buffer_head *qbh, int depth)
+{
+ struct dnode *dnode = qbh->data;
+ dnode_secno down = 0;
+ int lock = 0;
+ loff_t t;
+ if (de->first || de->last) {
+ hpfs_error(i->i_sb, "hpfs_remove_dirent: attempt to delete first or last dirent in dnode %08x", dno);
+ hpfs_brelse4(qbh);
+ return 1;
+ }
+ if (de->down) down = de_down_pointer(de);
+ if (depth && (de->down || (de == dnode_first_de(dnode) && de_next_de(de)->last))) {
+ lock = 1;
+ hpfs_lock_creation(i->i_sb);
+ if (hpfs_check_free_dnodes(i->i_sb, FREE_DNODES_DEL)) {
+ hpfs_brelse4(qbh);
+ hpfs_unlock_creation(i->i_sb);
+ return 2;
+ }
+ }
+ i->i_version = ++event;
+ for_all_poss(i, hpfs_pos_del, (t = get_pos(dnode, de)) + 1, 1);
+ hpfs_delete_de(i->i_sb, dnode, de);
+ hpfs_mark_4buffers_dirty(qbh);
+ hpfs_brelse4(qbh);
+ if (down) {
+ dnode_secno a = move_to_top(i, down, dno);
+ for_all_poss(i, hpfs_pos_subst, 5, t);
+ if (a) delete_empty_dnode(i, a);
+ if (lock) hpfs_unlock_creation(i->i_sb);
+ return !a;
+ }
+ delete_empty_dnode(i, dno);
+ if (lock) hpfs_unlock_creation(i->i_sb);
+ return 0;
+}
+
+void hpfs_count_dnodes(struct super_block *s, dnode_secno dno, int *n_dnodes,
+ int *n_subdirs, int *n_items)
+{
+ struct dnode *dnode;
+ struct quad_buffer_head qbh;
+ struct hpfs_dirent *de;
+ dnode_secno ptr, odno = 0;
+ int c1, c2 = 0;
+ int d1, d2 = 0;
+ go_down:
+ if (n_dnodes) (*n_dnodes)++;
+ if (s->s_hpfs_chk)
+ if (hpfs_stop_cycles(s, dno, &c1, &c2, "hpfs_count_dnodes #1")) return;
+ ptr = 0;
+ go_up:
+ if (!(dnode = hpfs_map_dnode(s, dno, &qbh))) return;
+ if (s->s_hpfs_chk) if (odno && odno != -1 && dnode->up != odno)
+ hpfs_error(s, "hpfs_count_dnodes: bad up pointer; dnode %08x, down %08x points to %08x", odno, dno, dnode->up);
+ de = dnode_first_de(dnode);
+ if (ptr) while(1) {
+ if (de->down) if (de_down_pointer(de) == ptr) goto process_de;
+ if (de->last) {
+ hpfs_brelse4(&qbh);
+ hpfs_error(s, "hpfs_count_dnodes: pointer to dnode %08x not found in dnode %08x, got here from %08x",
+ ptr, dno, odno);
+ return;
+ }
+ de = de_next_de(de);
+ }
+ next_de:
+ if (de->down) {
+ odno = dno;
+ dno = de_down_pointer(de);
+ hpfs_brelse4(&qbh);
+ goto go_down;
+ }
+ process_de:
+ if (!de->first && !de->last && de->directory && n_subdirs) (*n_subdirs)++;
+ if (!de->first && !de->last && n_items) (*n_items)++;
+ if ((de = de_next_de(de)) < dnode_end_de(dnode)) goto next_de;
+ ptr = dno;
+ dno = dnode->up;
+ if (dnode->root_dnode) {
+ hpfs_brelse4(&qbh);
+ return;
+ }
+ hpfs_brelse4(&qbh);
+ if (s->s_hpfs_chk)
+ if (hpfs_stop_cycles(s, ptr, &d1, &d2, "hpfs_count_dnodes #2")) return;
+ odno = -1;
+ goto go_up;
+}
+
+static struct hpfs_dirent *map_nth_dirent(struct super_block *s, dnode_secno dno, int n,
+ struct quad_buffer_head *qbh, struct dnode **dn)
+{
+ int i;
+ struct hpfs_dirent *de, *de_end;
+ struct dnode *dnode;
+ dnode = hpfs_map_dnode(s, dno, qbh);
+ if (!dnode) return NULL;
+ if (dn) *dn=dnode;
+ de = dnode_first_de(dnode);
+ de_end = dnode_end_de(dnode);
+ for (i = 1; de < de_end; i++, de = de_next_de(de)) {
+ if (i == n) {
+ return de;
+ }
+ if (de->last) break;
+ }
+ hpfs_brelse4(qbh);
+ hpfs_error(s, "map_nth_dirent: n too high; dnode = %08x, requested %08x", dno, n);
+ return NULL;
+}
+
+dnode_secno hpfs_de_as_down_as_possible(struct super_block *s, dnode_secno dno)
+{
+ struct quad_buffer_head qbh;
+ dnode_secno d = dno;
+ dnode_secno up = 0;
+ struct hpfs_dirent *de;
+ int c1, c2 = 0;
+
+ again:
+ if (s->s_hpfs_chk)
+ if (hpfs_stop_cycles(s, d, &c1, &c2, "hpfs_de_as_down_as_possible"))
+ return d;
+ if (!(de = map_nth_dirent(s, d, 1, &qbh, NULL))) return dno;
+ if (s->s_hpfs_chk)
+ if (up && ((struct dnode *)qbh.data)->up != up)
+ hpfs_error(s, "hpfs_de_as_down_as_possible: bad up pointer; dnode %08x, down %08x points to %08x", up, d, ((struct dnode *)qbh.data)->up);
+ if (!de->down) {
+ hpfs_brelse4(&qbh);
+ return d;
+ }
+ up = d;
+ d = de_down_pointer(de);
+ hpfs_brelse4(&qbh);
+ goto again;
+}
+
+struct hpfs_dirent *map_pos_dirent(struct inode *inode, loff_t *posp,
+ struct quad_buffer_head *qbh)
+{
+ loff_t pos;
+ unsigned c;
+ dnode_secno dno;
+ struct hpfs_dirent *de, *d;
+ struct hpfs_dirent *up_de;
+ struct hpfs_dirent *end_up_de;
+ struct dnode *dnode;
+ struct dnode *up_dnode;
+ struct quad_buffer_head qbh0;
+
+ pos = *posp;
+ dno = pos >> 6 << 2;
+ pos &= 077;
+ if (!(de = map_nth_dirent(inode->i_sb, dno, pos, qbh, &dnode)))
+ goto bail;
+
+ /* Going to the next dirent */
+ if ((d = de_next_de(de)) < dnode_end_de(dnode)) {
+ if (!(++*posp & 077)) {
+ hpfs_error(inode->i_sb, "map_pos_dirent: pos crossed dnode boundary; pos = %08x", *posp);
+ goto bail;
+ }
+ /* We're going down the tree */
+ if (d->down) {
+ *posp = ((loff_t) hpfs_de_as_down_as_possible(inode->i_sb, de_down_pointer(d)) << 4) + 1;
+ }
+
+ return de;
+ }
+
+ /* Going up */
+ if (dnode->root_dnode) goto bail;
+
+ if (!(up_dnode = hpfs_map_dnode(inode->i_sb, dnode->up, &qbh0)))
+ goto bail;
+
+ end_up_de = dnode_end_de(up_dnode);
+ c = 0;
+ for (up_de = dnode_first_de(up_dnode); up_de < end_up_de;
+ up_de = de_next_de(up_de)) {
+ if (!(++c & 077)) hpfs_error(inode->i_sb,
+ "map_pos_dirent: pos crossed dnode boundary; dnode = %08x", dnode->up);
+ if (up_de->down && de_down_pointer(up_de) == dno) {
+ *posp = ((loff_t) dnode->up << 4) + c;
+ hpfs_brelse4(&qbh0);
+ return de;
+ }
+ }
+
+ hpfs_error(inode->i_sb, "map_pos_dirent: pointer to dnode %08x not found in parent dnode %08x",
+ dno, dnode->up);
+ hpfs_brelse4(&qbh0);
+
+ bail:
+ *posp = 12;
+ return de;
+}
+
+/* Find a dirent in tree */
+
+struct hpfs_dirent *map_dirent(struct inode *inode, dnode_secno dno, char *name, unsigned len,
+ dnode_secno *dd, struct quad_buffer_head *qbh)
+{
+ struct dnode *dnode;
+ struct hpfs_dirent *de;
+ struct hpfs_dirent *de_end;
+ int c1, c2 = 0;
+
+ if (!S_ISDIR(inode->i_mode)) hpfs_error(inode->i_sb, "map_dirent: not a directory\n");
+ again:
+ if (inode->i_sb->s_hpfs_chk)
+ if (hpfs_stop_cycles(inode->i_sb, dno, &c1, &c2, "map_dirent")) return NULL;
+ if (!(dnode = hpfs_map_dnode(inode->i_sb, dno, qbh))) return NULL;
+
+ de_end = dnode_end_de(dnode);
+ for (de = dnode_first_de(dnode); de < de_end; de = de_next_de(de)) {
+ int t = hpfs_compare_names(inode->i_sb, name, len, de->name, de->namelen, de->last);
+ if (!t) {
+ if (dd) *dd = dno;
+ return de;
+ }
+ if (t < 0) {
+ if (de->down) {
+ dno = de_down_pointer(de);
+ hpfs_brelse4(qbh);
+ goto again;
+ }
+ break;
+ }
+ }
+ hpfs_brelse4(qbh);
+ return NULL;
+}
+
+/*
+ * Remove empty directory. In normal cases it is only one dnode with two
+ * entries, but we must handle also such obscure cases when it's a tree
+ * of empty dnodes.
+ */
+
+void hpfs_remove_dtree(struct super_block *s, dnode_secno dno)
+{
+ struct quad_buffer_head qbh;
+ struct dnode *dnode;
+ struct hpfs_dirent *de;
+ dnode_secno d1, d2, rdno = dno;
+ while (1) {
+ if (!(dnode = hpfs_map_dnode(s, dno, &qbh))) return;
+ de = dnode_first_de(dnode);
+ if (de->last) {
+ if (de->down) d1 = de_down_pointer(de);
+ else goto error;
+ hpfs_brelse4(&qbh);
+ hpfs_free_dnode(s, dno);
+ dno = d1;
+ } else break;
+ }
+ if (!de->first) goto error;
+ d1 = de->down ? de_down_pointer(de) : 0;
+ de = de_next_de(de);
+ if (!de->last) goto error;
+ d2 = de->down ? de_down_pointer(de) : 0;
+ hpfs_brelse4(&qbh);
+ hpfs_free_dnode(s, dno);
+ do {
+ while (d1) {
+ if (!(dnode = hpfs_map_dnode(s, dno = d1, &qbh))) return;
+ de = dnode_first_de(dnode);
+ if (!de->last) goto error;
+ d1 = de->down ? de_down_pointer(de) : 0;
+ hpfs_brelse4(&qbh);
+ hpfs_free_dnode(s, dno);
+ }
+ d1 = d2;
+ d2 = 0;
+ } while (d1);
+ return;
+ error:
+ hpfs_brelse4(&qbh);
+ hpfs_free_dnode(s, dno);
+ hpfs_error(s, "directory %08x is corrupted or not empty", rdno);
+}
+
+/*
+ * Find dirent for specified fnode. Use truncated 15-char name in fnode as
+ * a help for searching.
+ */
+
+struct hpfs_dirent *map_fnode_dirent(struct super_block *s, fnode_secno fno,
+ struct fnode *f, struct quad_buffer_head *qbh)
+{
+ char *name1;
+ char *name2;
+ int name1len, name2len;
+ struct dnode *d;
+ dnode_secno dno, downd;
+ struct fnode *upf;
+ struct buffer_head *bh;
+ struct hpfs_dirent *de, *de_end;
+ int c;
+ int c1, c2 = 0;
+ int d1, d2 = 0;
+ name1 = f->name;
+ if (!(name2 = kmalloc(256, GFP_KERNEL))) {
+ printk("HPFS: out of memory, can't map dirent\n");
+ return NULL;
+ }
+ if (f->len <= 15)
+ memcpy(name2, name1, name1len = name2len = f->len);
+ else {
+ memcpy(name2, name1, 15);
+ memset(name2 + 15, 0xff, 256 - 15);
+ /*name2[15] = 0xff;*/
+ name1len = 15; name2len = 256;
+ }
+ if (!(upf = hpfs_map_fnode(s, f->up, &bh))) {
+ kfree(name2);
+ return NULL;
+ }
+ if (!upf->dirflag) {
+ brelse(bh);
+ hpfs_error(s, "fnode %08x has non-directory parent %08x", fno, f->up);
+ kfree(name2);
+ return NULL;
+ }
+ dno = upf->u.external[0].disk_secno;
+ brelse(bh);
+ go_down:
+ downd = 0;
+ go_up:
+ if (!(d = hpfs_map_dnode(s, dno, qbh))) {
+ kfree(name2);
+ return NULL;
+ }
+ de_end = dnode_end_de(d);
+ de = dnode_first_de(d);
+ if (downd) {
+ while (de < de_end) {
+ if (de->down) if (de_down_pointer(de) == downd) goto f;
+ de = de_next_de(de);
+ }
+ hpfs_error(s, "pointer to dnode %08x not found in dnode %08x", downd, dno);
+ hpfs_brelse4(qbh);
+ kfree(name2);
+ return NULL;
+ }
+ next_de:
+ if (de->fnode == fno) {
+ kfree(name2);
+ return de;
+ }
+ c = hpfs_compare_names(s, name1, name1len, de->name, de->namelen, de->last);
+ if (c < 0 && de->down) {
+ dno = de_down_pointer(de);
+ hpfs_brelse4(qbh);
+ if (s->s_hpfs_chk)
+ if (hpfs_stop_cycles(s, dno, &c1, &c2, "map_fnode_dirent #1")) {
+ kfree(name2);
+ return NULL;
+ }
+ goto go_down;
+ }
+ f:
+ if (de->fnode == fno) {
+ kfree(name2);
+ return de;
+ }
+ c = hpfs_compare_names(s, name2, name2len, de->name, de->namelen, de->last);
+ if (c < 0 && !de->last) goto not_found;
+ if ((de = de_next_de(de)) < de_end) goto next_de;
+ if (d->root_dnode) goto not_found;
+ downd = dno;
+ dno = d->up;
+ hpfs_brelse4(qbh);
+ if (s->s_hpfs_chk)
+ if (hpfs_stop_cycles(s, downd, &d1, &d2, "map_fnode_dirent #2")) {
+ kfree(name2);
+ return NULL;
+ }
+ goto go_up;
+ not_found:
+ hpfs_brelse4(qbh);
+ hpfs_error(s, "dirent for fnode %08x not found", fno);
+ kfree(name2);
+ return NULL;
+}
diff --git a/fs/hpfs/ea.c b/fs/hpfs/ea.c
new file mode 100644
index 00000000000000..85ee927dcaa7ad
--- /dev/null
+++ b/fs/hpfs/ea.c
@@ -0,0 +1,364 @@
+/*
+ * linux/fs/hpfs/ea.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * handling extended attributes
+ */
+
+#include <linux/string.h>
+#include "hpfs_fn.h"
+
+/* Remove external extended attributes. ano specifies whether a is a
+ direct sector where eas starts or an anode */
+
+void hpfs_ea_ext_remove(struct super_block *s, secno a, int ano, unsigned len)
+{
+ unsigned pos = 0;
+ while (pos < len) {
+ char ex[4 + 255 + 1 + 8];
+ struct extended_attribute *ea = (struct extended_attribute *)ex;
+ if (pos + 4 > len) {
+ hpfs_error(s, "EAs don't end correctly, %s %08x, len %08x",
+ ano ? "anode" : "sectors", a, len);
+ return;
+ }
+ if (hpfs_ea_read(s, a, ano, pos, 4, ex)) return;
+ if (ea->indirect) {
+ if (ea->valuelen != 8) {
+ hpfs_error(s, "ea->indirect set while ea->valuelen!=8, %s %08x, pos %08x",
+ ano ? "anode" : "sectors", a, pos);
+ return;
+ }
+ if (hpfs_ea_read(s, a, ano, pos + 4, ea->namelen + 9, ex+4))
+ return;
+ hpfs_ea_remove(s, ea_sec(ea), ea->anode, ea_len(ea));
+ }
+ pos += ea->namelen + ea->valuelen + 5;
+ }
+ if (!ano) hpfs_free_sectors(s, a, (len+511) >> 9);
+ else {
+ struct buffer_head *bh;
+ struct anode *anode;
+ if ((anode = hpfs_map_anode(s, a, &bh))) {
+ hpfs_remove_btree(s, &anode->btree);
+ brelse(bh);
+ hpfs_free_sectors(s, a, 1);
+ }
+ }
+}
+
+static char *get_indirect_ea(struct super_block *s, int ano, secno a, int size)
+{
+ char *ret;
+ if (!(ret = kmalloc(size + 1, GFP_KERNEL))) {
+ printk("HPFS: out of memory for EA\n");
+ return NULL;
+ }
+ if (hpfs_ea_read(s, a, ano, 0, size, ret)) {
+ kfree(ret);
+ return NULL;
+ }
+ ret[size] = 0;
+ return ret;
+}
+
+static void set_indirect_ea(struct super_block *s, int ano, secno a, char *data,
+ int size)
+{
+ hpfs_ea_write(s, a, ano, 0, size, data);
+}
+
+/* Read an extended attribute named 'key' into the provided buffer */
+
+int hpfs_read_ea(struct super_block *s, struct fnode *fnode, char *key,
+ char *buf, int size)
+{
+ unsigned pos;
+ int ano, len;
+ secno a;
+ struct extended_attribute *ea;
+ struct extended_attribute *ea_end = fnode_end_ea(fnode);
+ for (ea = fnode_ea(fnode); ea < ea_end; ea = next_ea(ea))
+ if (!strcmp(ea->name, key)) {
+ if (ea->indirect)
+ goto indirect;
+ if (ea->valuelen >= size)
+ return -EINVAL;
+ memcpy(buf, ea_data(ea), ea->valuelen);
+ buf[ea->valuelen] = 0;
+ return 0;
+ }
+ a = fnode->ea_secno;
+ len = fnode->ea_size_l;
+ ano = fnode->ea_anode;
+ pos = 0;
+ while (pos < len) {
+ char ex[4 + 255 + 1 + 8];
+ ea = (struct extended_attribute *)ex;
+ if (pos + 4 > len) {
+ hpfs_error(s, "EAs don't end correctly, %s %08x, len %08x",
+ ano ? "anode" : "sectors", a, len);
+ return -EIO;
+ }
+ if (hpfs_ea_read(s, a, ano, pos, 4, ex)) return -EIO;
+ if (hpfs_ea_read(s, a, ano, pos + 4, ea->namelen + 1 + (ea->indirect ? 8 : 0), ex + 4))
+ return -EIO;
+ if (!strcmp(ea->name, key)) {
+ if (ea->indirect)
+ goto indirect;
+ if (ea->valuelen >= size)
+ return -EINVAL;
+ if (hpfs_ea_read(s, a, ano, pos + 4 + ea->namelen + 1, ea->valuelen, buf))
+ return -EIO;
+ buf[ea->valuelen] = 0;
+ return 0;
+ }
+ pos += ea->namelen + ea->valuelen + 5;
+ }
+ return -ENOENT;
+indirect:
+ if (ea_len(ea) >= size)
+ return -EINVAL;
+ if (hpfs_ea_read(s, ea_sec(ea), ea->anode, 0, ea_len(ea), buf))
+ return -EIO;
+ buf[ea_len(ea)] = 0;
+ return 0;
+}
+
+/* Read an extended attribute named 'key' */
+char *hpfs_get_ea(struct super_block *s, struct fnode *fnode, char *key, int *size)
+{
+ char *ret;
+ unsigned pos;
+ int ano, len;
+ secno a;
+ struct extended_attribute *ea;
+ struct extended_attribute *ea_end = fnode_end_ea(fnode);
+ for (ea = fnode_ea(fnode); ea < ea_end; ea = next_ea(ea))
+ if (!strcmp(ea->name, key)) {
+ if (ea->indirect)
+ return get_indirect_ea(s, ea->anode, ea_sec(ea), *size = ea_len(ea));
+ if (!(ret = kmalloc((*size = ea->valuelen) + 1, GFP_KERNEL))) {
+ printk("HPFS: out of memory for EA\n");
+ return NULL;
+ }
+ memcpy(ret, ea_data(ea), ea->valuelen);
+ ret[ea->valuelen] = 0;
+ return ret;
+ }
+ a = fnode->ea_secno;
+ len = fnode->ea_size_l;
+ ano = fnode->ea_anode;
+ pos = 0;
+ while (pos < len) {
+ char ex[4 + 255 + 1 + 8];
+ ea = (struct extended_attribute *)ex;
+ if (pos + 4 > len) {
+ hpfs_error(s, "EAs don't end correctly, %s %08x, len %08x",
+ ano ? "anode" : "sectors", a, len);
+ return NULL;
+ }
+ if (hpfs_ea_read(s, a, ano, pos, 4, ex)) return NULL;
+ if (hpfs_ea_read(s, a, ano, pos + 4, ea->namelen + 1 + (ea->indirect ? 8 : 0), ex + 4))
+ return NULL;
+ if (!strcmp(ea->name, key)) {
+ if (ea->indirect)
+ return get_indirect_ea(s, ea->anode, ea_sec(ea), *size = ea_len(ea));
+ if (!(ret = kmalloc((*size = ea->valuelen) + 1, GFP_KERNEL))) {
+ printk("HPFS: out of memory for EA\n");
+ return NULL;
+ }
+ if (hpfs_ea_read(s, a, ano, pos + 4 + ea->namelen + 1, ea->valuelen, ret)) {
+ kfree(ret);
+ return NULL;
+ }
+ ret[ea->valuelen] = 0;
+ return ret;
+ }
+ pos += ea->namelen + ea->valuelen + 5;
+ }
+ return NULL;
+}
+
+/*
+ * Update or create extended attribute 'key' with value 'data'. Note that
+ * when this ea exists, it MUST have the same size as size of data.
+ * This driver can't change sizes of eas ('cause I just don't need it).
+ */
+
+void hpfs_set_ea(struct inode *inode, struct fnode *fnode, char *key, char *data, int size)
+{
+ fnode_secno fno = inode->i_ino;
+ struct super_block *s = inode->i_sb;
+ unsigned pos;
+ int ano, len;
+ secno a;
+ unsigned char h[4];
+ struct extended_attribute *ea;
+ struct extended_attribute *ea_end = fnode_end_ea(fnode);
+ for (ea = fnode_ea(fnode); ea < ea_end; ea = next_ea(ea))
+ if (!strcmp(ea->name, key)) {
+ if (ea->indirect) {
+ if (ea_len(ea) == size)
+ set_indirect_ea(s, ea->anode, ea_sec(ea), data, size);
+ } else if (ea->valuelen == size) {
+ memcpy(ea_data(ea), data, size);
+ }
+ return;
+ }
+ a = fnode->ea_secno;
+ len = fnode->ea_size_l;
+ ano = fnode->ea_anode;
+ pos = 0;
+ while (pos < len) {
+ char ex[4 + 255 + 1 + 8];
+ ea = (struct extended_attribute *)ex;
+ if (pos + 4 > len) {
+ hpfs_error(s, "EAs don't end correctly, %s %08x, len %08x",
+ ano ? "anode" : "sectors", a, len);
+ return;
+ }
+ if (hpfs_ea_read(s, a, ano, pos, 4, ex)) return;
+ if (hpfs_ea_read(s, a, ano, pos + 4, ea->namelen + 1 + (ea->indirect ? 8 : 0), ex + 4))
+ return;
+ if (!strcmp(ea->name, key)) {
+ if (ea->indirect) {
+ if (ea_len(ea) == size)
+ set_indirect_ea(s, ea->anode, ea_sec(ea), data, size);
+ }
+ else {
+ if (ea->valuelen == size)
+ hpfs_ea_write(s, a, ano, pos + 4 + ea->namelen + 1, size, data);
+ }
+ return;
+ }
+ pos += ea->namelen + ea->valuelen + 5;
+ }
+ if (!fnode->ea_offs) {
+ /*if (fnode->ea_size_s) {
+ hpfs_error(s, "fnode %08x: ea_size_s == %03x, ea_offs == 0",
+ inode->i_ino, fnode->ea_size_s);
+ return;
+ }*/
+ fnode->ea_offs = 0xc4;
+ }
+ if (fnode->ea_offs < 0xc4 || fnode->ea_offs + fnode->acl_size_s + fnode->ea_size_s > 0x200) {
+ hpfs_error(s, "fnode %08x: ea_offs == %03x, ea_size_s == %03x",
+ inode->i_ino, fnode->ea_offs, fnode->ea_size_s);
+ return;
+ }
+ if ((fnode->ea_size_s || !fnode->ea_size_l) &&
+ fnode->ea_offs + fnode->acl_size_s + fnode->ea_size_s + strlen(key) + size + 5 <= 0x200) {
+ ea = fnode_end_ea(fnode);
+ *(char *)ea = 0;
+ ea->namelen = strlen(key);
+ ea->valuelen = size;
+ strcpy(ea->name, key);
+ memcpy(ea_data(ea), data, size);
+ fnode->ea_size_s += strlen(key) + size + 5;
+ goto ret;
+ }
+ /* Most the code here is 99.9993422% unused. I hope there are no bugs.
+ But what .. HPFS.IFS has also bugs in ea management. */
+ if (fnode->ea_size_s && !fnode->ea_size_l) {
+ secno n;
+ struct buffer_head *bh;
+ char *data;
+ if (!(n = hpfs_alloc_sector(s, fno, 1, 0, 1))) return;
+ if (!(data = hpfs_get_sector(s, n, &bh))) {
+ hpfs_free_sectors(s, n, 1);
+ return;
+ }
+ memcpy(data, fnode_ea(fnode), fnode->ea_size_s);
+ fnode->ea_size_l = fnode->ea_size_s;
+ fnode->ea_size_s = 0;
+ fnode->ea_secno = n;
+ fnode->ea_anode = 0;
+ mark_buffer_dirty(bh);
+ brelse(bh);
+ }
+ pos = fnode->ea_size_l + 5 + strlen(key) + size;
+ len = (fnode->ea_size_l + 511) >> 9;
+ if (pos >= 30000) goto bail;
+ while (((pos + 511) >> 9) > len) {
+ if (!len) {
+ if (!(fnode->ea_secno = hpfs_alloc_sector(s, fno, 1, 0, 1)))
+ goto bail;
+ fnode->ea_anode = 0;
+ len++;
+ } else if (!fnode->ea_anode) {
+ if (hpfs_alloc_if_possible(s, fnode->ea_secno + len)) {
+ len++;
+ } else {
+ /* Aargh... don't know how to create ea anodes :-( */
+ /*struct buffer_head *bh;
+ struct anode *anode;
+ anode_secno a_s;
+ if (!(anode = hpfs_alloc_anode(s, fno, &a_s, &bh)))
+ goto bail;
+ anode->up = fno;
+ anode->btree.fnode_parent = 1;
+ anode->btree.n_free_nodes--;
+ anode->btree.n_used_nodes++;
+ anode->btree.first_free += 12;
+ anode->u.external[0].disk_secno = fnode->ea_secno;
+ anode->u.external[0].file_secno = 0;
+ anode->u.external[0].length = len;
+ mark_buffer_dirty(bh);
+ brelse(bh);
+ fnode->ea_anode = 1;
+ fnode->ea_secno = a_s;*/
+ secno new_sec;
+ int i;
+ if (!(new_sec = hpfs_alloc_sector(s, fno, 1, 1 - ((pos + 511) >> 9), 1)))
+ goto bail;
+ for (i = 0; i < len; i++) {
+ struct buffer_head *bh1, *bh2;
+ void *b1, *b2;
+ if (!(b1 = hpfs_map_sector(s, fnode->ea_secno + i, &bh1, len - i - 1))) {
+ hpfs_free_sectors(s, new_sec, (pos + 511) >> 9);
+ goto bail;
+ }
+ if (!(b2 = hpfs_get_sector(s, new_sec + i, &bh2))) {
+ brelse(bh1);
+ hpfs_free_sectors(s, new_sec, (pos + 511) >> 9);
+ goto bail;
+ }
+ memcpy(b2, b1, 512);
+ brelse(bh1);
+ mark_buffer_dirty(bh2);
+ brelse(bh2);
+ }
+ hpfs_free_sectors(s, fnode->ea_secno, len);
+ fnode->ea_secno = new_sec;
+ len = (pos + 511) >> 9;
+ }
+ }
+ if (fnode->ea_anode) {
+ if (hpfs_add_sector_to_btree(s, fnode->ea_secno,
+ 0, len) != -1) {
+ len++;
+ } else {
+ goto bail;
+ }
+ }
+ }
+ h[0] = 0;
+ h[1] = strlen(key);
+ h[2] = size & 0xff;
+ h[3] = size >> 8;
+ if (hpfs_ea_write(s, fnode->ea_secno, fnode->ea_anode, fnode->ea_size_l, 4, h)) goto bail;
+ if (hpfs_ea_write(s, fnode->ea_secno, fnode->ea_anode, fnode->ea_size_l + 4, h[1] + 1, key)) goto bail;
+ if (hpfs_ea_write(s, fnode->ea_secno, fnode->ea_anode, fnode->ea_size_l + 5 + h[1], size, data)) goto bail;
+ fnode->ea_size_l = pos;
+ ret:
+ inode->i_hpfs_ea_size += 5 + strlen(key) + size;
+ return;
+ bail:
+ if (fnode->ea_secno)
+ if (fnode->ea_anode) hpfs_truncate_btree(s, fnode->ea_secno, 1, (fnode->ea_size_l + 511) >> 9);
+ else hpfs_free_sectors(s, fnode->ea_secno + ((fnode->ea_size_l + 511) >> 9), len - ((fnode->ea_size_l + 511) >> 9));
+ else fnode->ea_secno = fnode->ea_size_l = 0;
+}
+
diff --git a/fs/hpfs/file.c b/fs/hpfs/file.c
new file mode 100644
index 00000000000000..cefb5b32697704
--- /dev/null
+++ b/fs/hpfs/file.c
@@ -0,0 +1,136 @@
+/*
+ * linux/fs/hpfs/file.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * file VFS functions
+ */
+
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include "hpfs_fn.h"
+
+#define BLOCKS(size) (((size) + 511) >> 9)
+
+/* HUH? */
+int hpfs_open(struct inode *i, struct file *f)
+{
+ lock_kernel();
+ hpfs_lock_inode(i);
+ hpfs_unlock_inode(i); /* make sure nobody is deleting the file */
+ unlock_kernel();
+ if (!i->i_nlink) return -ENOENT;
+ return 0;
+}
+
+int hpfs_file_release(struct inode *inode, struct file *file)
+{
+ lock_kernel();
+ hpfs_write_if_changed(inode);
+ unlock_kernel();
+ return 0;
+}
+
+int hpfs_file_fsync(struct file *file, struct dentry *dentry, int datasync)
+{
+ /*return file_fsync(file, dentry);*/
+ return 0; /* Don't fsync :-) */
+}
+
+/*
+ * generic_file_read often calls bmap with non-existing sector,
+ * so we must ignore such errors.
+ */
+
+secno hpfs_bmap(struct inode *inode, unsigned file_secno)
+{
+ unsigned n, disk_secno;
+ struct fnode *fnode;
+ struct buffer_head *bh;
+ if (BLOCKS(inode->u.hpfs_i.mmu_private) <= file_secno) return 0;
+ n = file_secno - inode->i_hpfs_file_sec;
+ if (n < inode->i_hpfs_n_secs) return inode->i_hpfs_disk_sec + n;
+ if (!(fnode = hpfs_map_fnode(inode->i_sb, inode->i_ino, &bh))) return 0;
+ disk_secno = hpfs_bplus_lookup(inode->i_sb, inode, &fnode->btree, file_secno, bh);
+ if (disk_secno == -1) return 0;
+ if (hpfs_chk_sectors(inode->i_sb, disk_secno, 1, "bmap")) return 0;
+ return disk_secno;
+}
+
+void hpfs_truncate(struct inode *i)
+{
+ if (IS_IMMUTABLE(i)) return /*-EPERM*/;
+ i->i_hpfs_n_secs = 0;
+ i->i_blocks = 1 + ((i->i_size + 511) >> 9);
+ i->u.hpfs_i.mmu_private = i->i_size;
+ hpfs_truncate_btree(i->i_sb, i->i_ino, 1, ((i->i_size + 511) >> 9));
+ hpfs_write_inode(i);
+}
+
+int hpfs_get_block(struct inode *inode, long iblock, struct buffer_head *bh_result, int create)
+{
+ secno s;
+ s = hpfs_bmap(inode, iblock);
+ if (s) {
+ bh_result->b_dev = inode->i_dev;
+ bh_result->b_blocknr = s;
+ bh_result->b_state |= (1UL << BH_Mapped);
+ return 0;
+ }
+ if (!create) return 0;
+ if (iblock<<9 != inode->u.hpfs_i.mmu_private) {
+ BUG();
+ return -EIO;
+ }
+ if ((s = hpfs_add_sector_to_btree(inode->i_sb, inode->i_ino, 1, inode->i_blocks - 1)) == -1) {
+ hpfs_truncate_btree(inode->i_sb, inode->i_ino, 1, inode->i_blocks - 1);
+ return -ENOSPC;
+ }
+ inode->i_blocks++;
+ inode->u.hpfs_i.mmu_private += 512;
+ bh_result->b_dev = inode->i_dev;
+ bh_result->b_blocknr = s;
+ bh_result->b_state |= (1UL << BH_Mapped) | (1UL << BH_New);
+ return 0;
+}
+
+static int hpfs_writepage(struct page *page)
+{
+ return block_write_full_page(page,hpfs_get_block);
+}
+static int hpfs_readpage(struct file *file, struct page *page)
+{
+ return block_read_full_page(page,hpfs_get_block);
+}
+static int hpfs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
+{
+ return cont_prepare_write(page,from,to,hpfs_get_block,
+ &page->mapping->host->u.hpfs_i.mmu_private);
+}
+static int _hpfs_bmap(struct address_space *mapping, long block)
+{
+ return generic_block_bmap(mapping,block,hpfs_get_block);
+}
+struct address_space_operations hpfs_aops = {
+ readpage: hpfs_readpage,
+ writepage: hpfs_writepage,
+ sync_page: block_sync_page,
+ prepare_write: hpfs_prepare_write,
+ commit_write: generic_commit_write,
+ bmap: _hpfs_bmap
+};
+
+ssize_t hpfs_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
+{
+ ssize_t retval;
+
+ retval = generic_file_write(file, buf, count, ppos);
+ if (retval > 0) {
+ struct inode *inode = file->f_dentry->d_inode;
+ inode->i_mtime = CURRENT_TIME;
+ inode->i_hpfs_dirty = 1;
+ }
+ return retval;
+}
+
diff --git a/fs/hpfs/hpfs.h b/fs/hpfs/hpfs.h
new file mode 100644
index 00000000000000..6364fd9d19d1c5
--- /dev/null
+++ b/fs/hpfs/hpfs.h
@@ -0,0 +1,493 @@
+/*
+ * linux/fs/hpfs/hpfs.h
+ *
+ * HPFS structures by Chris Smith, 1993
+ *
+ * a little bit modified by Mikulas Patocka, 1998-1999
+ */
+
+/* The paper
+
+ Duncan, Roy
+ Design goals and implementation of the new High Performance File System
+ Microsoft Systems Journal Sept 1989 v4 n5 p1(13)
+
+ describes what HPFS looked like when it was new, and it is the source
+ of most of the information given here. The rest is conjecture.
+
+ For definitive information on the Duncan paper, see it, not this file.
+ For definitive information on HPFS, ask somebody else -- this is guesswork.
+ There are certain to be many mistakes. */
+
+/* Notation */
+
+typedef unsigned secno; /* sector number, partition relative */
+
+typedef secno dnode_secno; /* sector number of a dnode */
+typedef secno fnode_secno; /* sector number of an fnode */
+typedef secno anode_secno; /* sector number of an anode */
+
+typedef unsigned time32_t; /* 32-bit time_t type */
+
+/* sector 0 */
+
+/* The boot block is very like a FAT boot block, except that the
+ 29h signature byte is 28h instead, and the ID string is "HPFS". */
+
+#define BB_MAGIC 0xaa55
+
+struct hpfs_boot_block
+{
+ unsigned char jmp[3];
+ unsigned char oem_id[8];
+ unsigned char bytes_per_sector[2]; /* 512 */
+ unsigned char sectors_per_cluster;
+ unsigned char n_reserved_sectors[2];
+ unsigned char n_fats;
+ unsigned char n_rootdir_entries[2];
+ unsigned char n_sectors_s[2];
+ unsigned char media_byte;
+ unsigned short sectors_per_fat;
+ unsigned short sectors_per_track;
+ unsigned short heads_per_cyl;
+ unsigned int n_hidden_sectors;
+ unsigned int n_sectors_l; /* size of partition */
+ unsigned char drive_number;
+ unsigned char mbz;
+ unsigned char sig_28h; /* 28h */
+ unsigned char vol_serno[4];
+ unsigned char vol_label[11];
+ unsigned char sig_hpfs[8]; /* "HPFS " */
+ unsigned char pad[448];
+ unsigned short magic; /* aa55 */
+};
+
+
+/* sector 16 */
+
+/* The super block has the pointer to the root directory. */
+
+#define SB_MAGIC 0xf995e849
+
+struct hpfs_super_block
+{
+ unsigned magic; /* f995 e849 */
+ unsigned magic1; /* fa53 e9c5, more magic? */
+ /*unsigned huh202;*/ /* ?? 202 = N. of B. in 1.00390625 S.*/
+ char version; /* version of a filesystem usually 2 */
+ char funcversion; /* functional version - oldest version
+ of filesystem that can understand
+ this disk */
+ unsigned short int zero; /* 0 */
+ fnode_secno root; /* fnode of root directory */
+ secno n_sectors; /* size of filesystem */
+ unsigned n_badblocks; /* number of bad blocks */
+ secno bitmaps; /* pointers to free space bit maps */
+ unsigned zero1; /* 0 */
+ secno badblocks; /* bad block list */
+ unsigned zero3; /* 0 */
+ time32_t last_chkdsk; /* date last checked, 0 if never */
+ /*unsigned zero4;*/ /* 0 */
+ time32_t last_optimize; /* date last optimized, 0 if never */
+ secno n_dir_band; /* number of sectors in dir band */
+ secno dir_band_start; /* first sector in dir band */
+ secno dir_band_end; /* last sector in dir band */
+ secno dir_band_bitmap; /* free space map, 1 dnode per bit */
+ char volume_name[32]; /* not used */
+ secno user_id_table; /* 8 preallocated sectors - user id */
+ unsigned zero6[103]; /* 0 */
+};
+
+
+/* sector 17 */
+
+/* The spare block has pointers to spare sectors. */
+
+#define SP_MAGIC 0xf9911849
+
+struct hpfs_spare_block
+{
+ unsigned magic; /* f991 1849 */
+ unsigned magic1; /* fa52 29c5, more magic? */
+
+ unsigned dirty: 1; /* 0 clean, 1 "improperly stopped" */
+ /*unsigned flag1234: 4;*/ /* unknown flags */
+ unsigned sparedir_used: 1; /* spare dirblks used */
+ unsigned hotfixes_used: 1; /* hotfixes used */
+ unsigned bad_sector: 1; /* bad sector, corrupted disk (???) */
+ unsigned bad_bitmap: 1; /* bad bitmap */
+ unsigned fast: 1; /* partition was fast formatted */
+ unsigned old_wrote: 1; /* old version wrote to partion */
+ unsigned old_wrote_1: 1; /* old version wrote to partion (?) */
+ unsigned install_dasd_limits: 1; /* HPFS386 flags */
+ unsigned resynch_dasd_limits: 1;
+ unsigned dasd_limits_operational: 1;
+ unsigned multimedia_active: 1;
+ unsigned dce_acls_active: 1;
+ unsigned dasd_limits_dirty: 1;
+ unsigned flag67: 2;
+ unsigned char mm_contlgulty;
+ unsigned char unused;
+
+ secno hotfix_map; /* info about remapped bad sectors */
+ unsigned n_spares_used; /* number of hotfixes */
+ unsigned n_spares; /* number of spares in hotfix map */
+ unsigned n_dnode_spares_free; /* spare dnodes unused */
+ unsigned n_dnode_spares; /* length of spare_dnodes[] list,
+ follows in this block*/
+ secno code_page_dir; /* code page directory block */
+ unsigned n_code_pages; /* number of code pages */
+ /*unsigned large_numbers[2];*/ /* ?? */
+ unsigned super_crc; /* on HPFS386 and LAN Server this is
+ checksum of superblock, on normal
+ OS/2 unused */
+ unsigned spare_crc; /* on HPFS386 checksum of spareblock */
+ unsigned zero1[15]; /* unused */
+ dnode_secno spare_dnodes[100]; /* emergency free dnode list */
+ unsigned zero2[1]; /* room for more? */
+};
+
+/* The bad block list is 4 sectors long. The first word must be zero,
+ the remaining words give n_badblocks bad block numbers.
+ I bet you can see it coming... */
+
+#define BAD_MAGIC 0
+
+/* The hotfix map is 4 sectors long. It looks like
+
+ secno from[n_spares];
+ secno to[n_spares];
+
+ The to[] list is initialized to point to n_spares preallocated empty
+ sectors. The from[] list contains the sector numbers of bad blocks
+ which have been remapped to corresponding sectors in the to[] list.
+ n_spares_used gives the length of the from[] list. */
+
+
+/* Sectors 18 and 19 are preallocated and unused.
+ Maybe they're spares for 16 and 17, but simple substitution fails. */
+
+
+/* The code page info pointed to by the spare block consists of an index
+ block and blocks containing uppercasing tables. I don't know what
+ these are for (CHKDSK, maybe?) -- OS/2 does not seem to use them
+ itself. Linux doesn't use them either. */
+
+/* block pointed to by spareblock->code_page_dir */
+
+#define CP_DIR_MAGIC 0x494521f7
+
+struct code_page_directory
+{
+ unsigned magic; /* 4945 21f7 */
+ unsigned n_code_pages; /* number of pointers following */
+ unsigned zero1[2];
+ struct {
+ unsigned short ix; /* index */
+ unsigned short code_page_number; /* code page number */
+ unsigned bounds; /* matches corresponding word
+ in data block */
+ secno code_page_data; /* sector number of a code_page_data
+ containing c.p. array */
+ unsigned short index; /* index in c.p. array in that sector*/
+ unsigned short unknown; /* some unknown value; usually 0;
+ 2 in Japanese version */
+ } array[31]; /* unknown length */
+};
+
+/* blocks pointed to by code_page_directory */
+
+#define CP_DATA_MAGIC 0x894521f7
+
+struct code_page_data
+{
+ unsigned magic; /* 8945 21f7 */
+ unsigned n_used; /* # elements used in c_p_data[] */
+ unsigned bounds[3]; /* looks a bit like
+ (beg1,end1), (beg2,end2)
+ one byte each */
+ unsigned short offs[3]; /* offsets from start of sector
+ to start of c_p_data[ix] */
+ struct {
+ unsigned short ix; /* index */
+ unsigned short code_page_number; /* code page number */
+ unsigned short unknown; /* the same as in cp directory */
+ unsigned char map[128]; /* upcase table for chars 80..ff */
+ unsigned short zero2;
+ } code_page[3];
+ unsigned char incognita[78];
+};
+
+
+/* Free space bitmaps are 4 sectors long, which is 16384 bits.
+ 16384 sectors is 8 meg, and each 8 meg band has a 4-sector bitmap.
+ Bit order in the maps is little-endian. 0 means taken, 1 means free.
+
+ Bit map sectors are marked allocated in the bit maps, and so are sectors
+ off the end of the partition.
+
+ Band 0 is sectors 0-3fff, its map is in sectors 18-1b.
+ Band 1 is 4000-7fff, its map is in 7ffc-7fff.
+ Band 2 is 8000-ffff, its map is in 8000-8003.
+ The remaining bands have maps in their first (even) or last (odd) 4 sectors
+ -- if the last, partial, band is odd its map is in its last 4 sectors.
+
+ The bitmap locations are given in a table pointed to by the super block.
+ No doubt they aren't constrained to be at 18, 7ffc, 8000, ...; that is
+ just where they usually are.
+
+ The "directory band" is a bunch of sectors preallocated for dnodes.
+ It has a 4-sector free space bitmap of its own. Each bit in the map
+ corresponds to one 4-sector dnode, bit 0 of the map corresponding to
+ the first 4 sectors of the directory band. The entire band is marked
+ allocated in the main bitmap. The super block gives the locations
+ of the directory band and its bitmap. ("band" doesn't mean it is
+ 8 meg long; it isn't.) */
+
+
+/* dnode: directory. 4 sectors long */
+
+/* A directory is a tree of dnodes. The fnode for a directory
+ contains one pointer, to the root dnode of the tree. The fnode
+ never moves, the dnodes do the B-tree thing, splitting and merging
+ as files are added and removed. */
+
+#define DNODE_MAGIC 0x77e40aae
+
+struct dnode {
+ unsigned magic; /* 77e4 0aae */
+ unsigned first_free; /* offset from start of dnode to
+ first free dir entry */
+ unsigned root_dnode:1; /* Is it root dnode? */
+ unsigned increment_me:31; /* some kind of activity counter?
+ Neither HPFS.IFS nor CHKDSK cares
+ if you change this word */
+ secno up; /* (root dnode) directory's fnode
+ (nonroot) parent dnode */
+ dnode_secno self; /* pointer to this dnode */
+ unsigned char dirent[2028]; /* one or more dirents */
+};
+
+struct hpfs_dirent {
+ unsigned short length; /* offset to next dirent */
+ unsigned first: 1; /* set on phony ^A^A (".") entry */
+ unsigned has_acl: 1;
+ unsigned down: 1; /* down pointer present (after name) */
+ unsigned last: 1; /* set on phony \377 entry */
+ unsigned has_ea: 1; /* entry has EA */
+ unsigned has_xtd_perm: 1; /* has extended perm list (???) */
+ unsigned has_explicit_acl: 1;
+ unsigned has_needea: 1; /* ?? some EA has NEEDEA set
+ I have no idea why this is
+ interesting in a dir entry */
+ unsigned read_only: 1; /* dos attrib */
+ unsigned hidden: 1; /* dos attrib */
+ unsigned system: 1; /* dos attrib */
+ unsigned flag11: 1; /* would be volume label dos attrib */
+ unsigned directory: 1; /* dos attrib */
+ unsigned archive: 1; /* dos attrib */
+ unsigned not_8x3: 1; /* name is not 8.3 */
+ unsigned flag15: 1;
+ fnode_secno fnode; /* fnode giving allocation info */
+ time32_t write_date; /* mtime */
+ unsigned file_size; /* file length, bytes */
+ time32_t read_date; /* atime */
+ time32_t creation_date; /* ctime */
+ unsigned ea_size; /* total EA length, bytes */
+ unsigned char no_of_acls : 3; /* number of ACL's */
+ unsigned char reserver : 5;
+ unsigned char ix; /* code page index (of filename), see
+ struct code_page_data */
+ unsigned char namelen, name[1]; /* file name */
+ /* dnode_secno down; btree down pointer, if present,
+ follows name on next word boundary, or maybe it
+ precedes next dirent, which is on a word boundary. */
+};
+
+
+/* B+ tree: allocation info in fnodes and anodes */
+
+/* dnodes point to fnodes which are responsible for listing the sectors
+ assigned to the file. This is done with trees of (length,address)
+ pairs. (Actually triples, of (length, file-address, disk-address)
+ which can represent holes. Find out if HPFS does that.)
+ At any rate, fnodes contain a small tree; if subtrees are needed
+ they occupy essentially a full block in anodes. A leaf-level tree node
+ has 3-word entries giving sector runs, a non-leaf node has 2-word
+ entries giving subtree pointers. A flag in the header says which. */
+
+struct bplus_leaf_node
+{
+ unsigned file_secno; /* first file sector in extent */
+ unsigned length; /* length, sectors */
+ secno disk_secno; /* first corresponding disk sector */
+};
+
+struct bplus_internal_node
+{
+ unsigned file_secno; /* subtree maps sectors < this */
+ anode_secno down; /* pointer to subtree */
+};
+
+struct bplus_header
+{
+ unsigned hbff: 1; /* high bit of first free entry offset */
+ unsigned flag1: 1;
+ unsigned flag2: 1;
+ unsigned flag3: 1;
+ unsigned flag4: 1;
+ unsigned fnode_parent: 1; /* ? we're pointed to by an fnode,
+ the data btree or some ea or the
+ main ea bootage pointer ea_secno */
+ /* also can get set in fnodes, which
+ may be a chkdsk glitch or may mean
+ this bit is irrelevant in fnodes,
+ or this interpretation is all wet */
+ unsigned binary_search: 1; /* suggest binary search (unused) */
+ unsigned internal: 1; /* 1 -> (internal) tree of anodes
+ 0 -> (leaf) list of extents */
+ unsigned char fill[3];
+ unsigned char n_free_nodes; /* free nodes in following array */
+ unsigned char n_used_nodes; /* used nodes in following array */
+ unsigned short first_free; /* offset from start of header to
+ first free node in array */
+ union {
+ struct bplus_internal_node internal[0]; /* (internal) 2-word entries giving
+ subtree pointers */
+ struct bplus_leaf_node external[0]; /* (external) 3-word entries giving
+ sector runs */
+ } u;
+};
+
+/* fnode: root of allocation b+ tree, and EA's */
+
+/* Every file and every directory has one fnode, pointed to by the directory
+ entry and pointing to the file's sectors or directory's root dnode. EA's
+ are also stored here, and there are said to be ACL's somewhere here too. */
+
+#define FNODE_MAGIC 0xf7e40aae
+
+struct fnode
+{
+ unsigned magic; /* f7e4 0aae */
+ unsigned zero1[2]; /* read history */
+ unsigned char len, name[15]; /* true length, truncated name */
+ fnode_secno up; /* pointer to file's directory fnode */
+ /*unsigned zero2[3];*/
+ secno acl_size_l;
+ secno acl_secno;
+ unsigned short acl_size_s;
+ char acl_anode;
+ char zero2; /* history bit count */
+ unsigned ea_size_l; /* length of disk-resident ea's */
+ secno ea_secno; /* first sector of disk-resident ea's*/
+ unsigned short ea_size_s; /* length of fnode-resident ea's */
+
+ unsigned flag0: 1;
+ unsigned ea_anode: 1; /* 1 -> ea_secno is an anode */
+ unsigned flag2: 1;
+ unsigned flag3: 1;
+ unsigned flag4: 1;
+ unsigned flag5: 1;
+ unsigned flag6: 1;
+ unsigned flag7: 1;
+ unsigned dirflag: 1; /* 1 -> directory. first & only extent
+ points to dnode. */
+ unsigned flag9: 1;
+ unsigned flag10: 1;
+ unsigned flag11: 1;
+ unsigned flag12: 1;
+ unsigned flag13: 1;
+ unsigned flag14: 1;
+ unsigned flag15: 1;
+
+ struct bplus_header btree; /* b+ tree, 8 extents or 12 subtrees */
+ union {
+ struct bplus_leaf_node external[8];
+ struct bplus_internal_node internal[12];
+ } u;
+
+ unsigned file_size; /* file length, bytes */
+ unsigned n_needea; /* number of EA's with NEEDEA set */
+ char user_id[16]; /* unused */
+ unsigned short ea_offs; /* offset from start of fnode
+ to first fnode-resident ea */
+ char dasd_limit_treshhold;
+ char dasd_limit_delta;
+ unsigned dasd_limit;
+ unsigned dasd_usage;
+ /*unsigned zero5[2];*/
+ unsigned char ea[316]; /* zero or more EA's, packed together
+ with no alignment padding.
+ (Do not use this name, get here
+ via fnode + ea_offs. I think.) */
+};
+
+
+/* anode: 99.44% pure allocation tree */
+
+#define ANODE_MAGIC 0x37e40aae
+
+struct anode
+{
+ unsigned magic; /* 37e4 0aae */
+ anode_secno self; /* pointer to this anode */
+ secno up; /* parent anode or fnode */
+
+ struct bplus_header btree; /* b+tree, 40 extents or 60 subtrees */
+ union {
+ struct bplus_leaf_node external[40];
+ struct bplus_internal_node internal[60];
+ } u;
+
+ unsigned fill[3]; /* unused */
+};
+
+
+/* extended attributes.
+
+ A file's EA info is stored as a list of (name,value) pairs. It is
+ usually in the fnode, but (if it's large) it is moved to a single
+ sector run outside the fnode, or to multiple runs with an anode tree
+ that points to them.
+
+ The value of a single EA is stored along with the name, or (if large)
+ it is moved to a single sector run, or multiple runs pointed to by an
+ anode tree, pointed to by the value field of the (name,value) pair.
+
+ Flags in the EA tell whether the value is immediate, in a single sector
+ run, or in multiple runs. Flags in the fnode tell whether the EA list
+ is immediate, in a single run, or in multiple runs. */
+
+struct extended_attribute
+{
+ unsigned indirect: 1; /* 1 -> value gives sector number
+ where real value starts */
+ unsigned anode: 1; /* 1 -> sector is an anode
+ that points to fragmented value */
+ unsigned flag2: 1;
+ unsigned flag3: 1;
+ unsigned flag4: 1;
+ unsigned flag5: 1;
+ unsigned flag6: 1;
+ unsigned needea: 1; /* required ea */
+ unsigned char namelen; /* length of name, bytes */
+ unsigned short valuelen; /* length of value, bytes */
+ unsigned char name[0];
+ /*
+ unsigned char name[namelen]; ascii attrib name
+ unsigned char nul; terminating '\0', not counted
+ unsigned char value[valuelen]; value, arbitrary
+ if this.indirect, valuelen is 8 and the value is
+ unsigned length; real length of value, bytes
+ secno secno; sector address where it starts
+ if this.anode, the above sector number is the root of an anode tree
+ which points to the value.
+ */
+};
+
+/*
+ Local Variables:
+ comment-column: 40
+ End:
+*/
diff --git a/fs/hpfs/hpfs_fn.h b/fs/hpfs/hpfs_fn.h
new file mode 100644
index 00000000000000..0ccae15e448a90
--- /dev/null
+++ b/fs/hpfs/hpfs_fn.h
@@ -0,0 +1,315 @@
+/*
+ * linux/fs/hpfs/hpfs_fn.h
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * function headers
+ */
+
+//#define DBG
+//#define DEBUG_LOCKS
+
+#include <linux/fs.h>
+#include <linux/hpfs_fs.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/locks.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <asm/bitops.h>
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+#include <linux/smp_lock.h>
+
+#include <stdarg.h>
+
+#include "hpfs.h"
+
+#define EIOERROR EIO
+#define EFSERROR EPERM
+#define EMEMERROR ENOMEM
+
+#define ANODE_ALLOC_FWD 512
+#define FNODE_ALLOC_FWD 0
+#define ALLOC_FWD_MIN 16
+#define ALLOC_FWD_MAX 128
+#define ALLOC_M 1
+#define FNODE_RD_AHEAD 16
+#define ANODE_RD_AHEAD 16
+#define DNODE_RD_AHEAD 4
+
+#define FREE_DNODES_ADD 58
+#define FREE_DNODES_DEL 29
+
+#define CHKCOND(x,y) if (!(x)) printk y
+
+#ifdef DBG
+#define PRINTK(x) printk x
+#else
+#undef PRINTK
+#define PRINTK(x)
+#endif
+
+typedef void nonconst; /* What this is for ? */
+
+/*
+ * local time (HPFS) to GMT (Unix)
+ */
+
+extern inline time_t local_to_gmt(struct super_block *s, time32_t t)
+{
+ extern struct timezone sys_tz;
+ return t + sys_tz.tz_minuteswest * 60 + s->s_hpfs_timeshift;
+}
+
+extern inline time32_t gmt_to_local(struct super_block *s, time_t t)
+{
+ extern struct timezone sys_tz;
+ return t - sys_tz.tz_minuteswest * 60 - s->s_hpfs_timeshift;
+}
+
+/*
+ * conv= options
+ */
+
+#define CONV_BINARY 0 /* no conversion */
+#define CONV_TEXT 1 /* crlf->newline */
+#define CONV_AUTO 2 /* decide based on file contents */
+
+/* Four 512-byte buffers and the 2k block obtained by concatenating them */
+
+struct quad_buffer_head {
+ struct buffer_head *bh[4];
+ void *data;
+};
+
+/* The b-tree down pointer from a dir entry */
+
+extern inline dnode_secno de_down_pointer (struct hpfs_dirent *de)
+{
+ CHKCOND(de->down,("HPFS: de_down_pointer: !de->down\n"));
+ return *(dnode_secno *) ((void *) de + de->length - 4);
+}
+
+/* The first dir entry in a dnode */
+
+extern inline struct hpfs_dirent *dnode_first_de (struct dnode *dnode)
+{
+ return (void *) dnode->dirent;
+}
+
+/* The end+1 of the dir entries */
+
+extern inline struct hpfs_dirent *dnode_end_de (struct dnode *dnode)
+{
+ CHKCOND(dnode->first_free>=0x14 && dnode->first_free<=0xa00,("HPFS: dnode_end_de: dnode->first_free = %d\n",(int)dnode->first_free));
+ return (void *) dnode + dnode->first_free;
+}
+
+/* The dir entry after dir entry de */
+
+extern inline struct hpfs_dirent *de_next_de (struct hpfs_dirent *de)
+{
+ CHKCOND(de->length>=0x20 && de->length<0x800,("HPFS: de_next_de: de->length = %d\n",(int)de->length));
+ return (void *) de + de->length;
+}
+
+extern inline struct extended_attribute *fnode_ea(struct fnode *fnode)
+{
+ return (struct extended_attribute *)((char *)fnode + fnode->ea_offs + fnode->acl_size_s);
+}
+
+extern inline struct extended_attribute *fnode_end_ea(struct fnode *fnode)
+{
+ return (struct extended_attribute *)((char *)fnode + fnode->ea_offs + fnode->acl_size_s + fnode->ea_size_s);
+}
+
+extern inline struct extended_attribute *next_ea(struct extended_attribute *ea)
+{
+ return (struct extended_attribute *)((char *)ea + 5 + ea->namelen + ea->valuelen);
+}
+
+extern inline secno ea_sec(struct extended_attribute *ea)
+{
+ return *(secno *)((char *)ea + 9 + ea->namelen);
+}
+
+extern inline secno ea_len(struct extended_attribute *ea)
+{
+ return *(secno *)((char *)ea + 5 + ea->namelen);
+}
+
+extern inline char *ea_data(struct extended_attribute *ea)
+{
+ return (char *)((char *)ea + 5 + ea->namelen);
+}
+
+extern inline unsigned de_size(int namelen, secno down_ptr)
+{
+ return ((0x1f + namelen + 3) & ~3) + (down_ptr ? 4 : 0);
+}
+
+extern inline void copy_de(struct hpfs_dirent *dst, struct hpfs_dirent *src)
+{
+ int a;
+ int n;
+ if (!dst || !src) return;
+ a = dst->down;
+ n = dst->not_8x3;
+ memcpy((char *)dst + 2, (char *)src + 2, 28);
+ dst->down = a;
+ dst->not_8x3 = n;
+}
+
+extern inline unsigned tstbits(unsigned *bmp, unsigned b, unsigned n)
+{
+ int i;
+ if ((b >= 0x4000) || (b + n - 1 >= 0x4000)) return n;
+ if (!((bmp[(b & 0x3fff) >> 5] >> (b & 0x1f)) & 1)) return 1;
+ for (i = 1; i < n; i++)
+ if (/*b+i < 0x4000 &&*/ !((bmp[((b+i) & 0x3fff) >> 5] >> ((b+i) & 0x1f)) & 1))
+ return i + 1;
+ return 0;
+}
+
+/* alloc.c */
+
+int hpfs_chk_sectors(struct super_block *, secno, int, char *);
+secno hpfs_alloc_sector(struct super_block *, secno, unsigned, int, int);
+int hpfs_alloc_if_possible_nolock(struct super_block *, secno);
+int hpfs_alloc_if_possible(struct super_block *, secno);
+void hpfs_free_sectors(struct super_block *, secno, unsigned);
+int hpfs_check_free_dnodes(struct super_block *, int);
+void hpfs_free_dnode(struct super_block *, secno);
+struct dnode *hpfs_alloc_dnode(struct super_block *, secno, dnode_secno *, struct quad_buffer_head *, int);
+struct fnode *hpfs_alloc_fnode(struct super_block *, secno, fnode_secno *, struct buffer_head **);
+struct anode *hpfs_alloc_anode(struct super_block *, secno, anode_secno *, struct buffer_head **);
+
+/* anode.c */
+
+secno hpfs_bplus_lookup(struct super_block *, struct inode *, struct bplus_header *, unsigned, struct buffer_head *);
+secno hpfs_add_sector_to_btree(struct super_block *, secno, int, unsigned);
+void hpfs_remove_btree(struct super_block *, struct bplus_header *);
+int hpfs_ea_read(struct super_block *, secno, int, unsigned, unsigned, char *);
+int hpfs_ea_write(struct super_block *, secno, int, unsigned, unsigned, char *);
+void hpfs_ea_remove(struct super_block *, secno, int, unsigned);
+void hpfs_truncate_btree(struct super_block *, secno, int, unsigned);
+void hpfs_remove_fnode(struct super_block *, fnode_secno fno);
+
+/* buffer.c */
+
+void hpfs_lock_creation(struct super_block *);
+void hpfs_unlock_creation(struct super_block *);
+void hpfs_lock_iget(struct super_block *, int);
+void hpfs_unlock_iget(struct super_block *);
+void hpfs_lock_inode(struct inode *);
+void hpfs_unlock_inode(struct inode *);
+void hpfs_lock_2inodes(struct inode *, struct inode *);
+void hpfs_unlock_2inodes(struct inode *, struct inode *);
+void hpfs_lock_3inodes(struct inode *, struct inode *, struct inode *);
+void hpfs_unlock_3inodes(struct inode *, struct inode *, struct inode *);
+void *hpfs_map_sector(struct super_block *, unsigned, struct buffer_head **, int);
+void *hpfs_get_sector(struct super_block *, unsigned, struct buffer_head **);
+void *hpfs_map_4sectors(struct super_block *, unsigned, struct quad_buffer_head *, int);
+void *hpfs_get_4sectors(struct super_block *, unsigned, struct quad_buffer_head *);
+void hpfs_brelse4(struct quad_buffer_head *);
+void hpfs_mark_4buffers_dirty(struct quad_buffer_head *);
+
+/* dentry.c */
+
+void hpfs_set_dentry_operations(struct dentry *);
+
+/* dir.c */
+
+int hpfs_dir_release(struct inode *, struct file *);
+loff_t hpfs_dir_lseek(struct file *, loff_t, int);
+int hpfs_readdir(struct file *, void *, filldir_t);
+struct dentry *hpfs_lookup(struct inode *, struct dentry *);
+
+/* dnode.c */
+
+void hpfs_add_pos(struct inode *, loff_t *);
+void hpfs_del_pos(struct inode *, loff_t *);
+struct hpfs_dirent *hpfs_add_de(struct super_block *, struct dnode *, unsigned char *, unsigned, secno);
+void hpfs_delete_de(struct super_block *, struct dnode *, struct hpfs_dirent *);
+int hpfs_add_to_dnode(struct inode *, dnode_secno, unsigned char *, unsigned, struct hpfs_dirent *, dnode_secno);
+int hpfs_add_dirent(struct inode *, unsigned char *, unsigned, struct hpfs_dirent *, int);
+int hpfs_remove_dirent(struct inode *, dnode_secno, struct hpfs_dirent *, struct quad_buffer_head *, int);
+void hpfs_count_dnodes(struct super_block *, dnode_secno, int *, int *, int *);
+dnode_secno hpfs_de_as_down_as_possible(struct super_block *, dnode_secno dno);
+struct hpfs_dirent *map_pos_dirent(struct inode *, loff_t *, struct quad_buffer_head *);
+struct hpfs_dirent *map_dirent(struct inode *, dnode_secno, char *, unsigned, dnode_secno *, struct quad_buffer_head *);
+void hpfs_remove_dtree(struct super_block *, dnode_secno);
+struct hpfs_dirent *map_fnode_dirent(struct super_block *, fnode_secno, struct fnode *, struct quad_buffer_head *);
+
+/* ea.c */
+
+void hpfs_ea_ext_remove(struct super_block *, secno, int, unsigned);
+int hpfs_read_ea(struct super_block *, struct fnode *, char *, char *, int);
+char *hpfs_get_ea(struct super_block *, struct fnode *, char *, int *);
+void hpfs_set_ea(struct inode *, struct fnode *, char *, char *, int);
+
+/* file.c */
+
+int hpfs_file_release(struct inode *, struct file *);
+int hpfs_open(struct inode *, struct file *);
+int hpfs_file_fsync(struct file *, struct dentry *, int);
+secno hpfs_bmap(struct inode *, unsigned);
+void hpfs_truncate(struct inode *);
+int hpfs_get_block(struct inode *inode, long iblock, struct buffer_head *bh_result, int create);
+ssize_t hpfs_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos);
+
+/* inode.c */
+
+void hpfs_read_inode(struct inode *);
+void hpfs_write_inode_ea(struct inode *, struct fnode *);
+void hpfs_write_inode(struct inode *);
+void hpfs_write_inode_nolock(struct inode *);
+int hpfs_notify_change(struct dentry *, struct iattr *);
+void hpfs_write_if_changed(struct inode *);
+void hpfs_delete_inode(struct inode *);
+
+/* map.c */
+
+unsigned *hpfs_map_dnode_bitmap(struct super_block *, struct quad_buffer_head *);
+unsigned *hpfs_map_bitmap(struct super_block *, unsigned, struct quad_buffer_head *, char *);
+char *hpfs_load_code_page(struct super_block *, secno);
+secno *hpfs_load_bitmap_directory(struct super_block *, secno bmp);
+struct fnode *hpfs_map_fnode(struct super_block *s, ino_t, struct buffer_head **);
+struct anode *hpfs_map_anode(struct super_block *s, anode_secno, struct buffer_head **);
+struct dnode *hpfs_map_dnode(struct super_block *s, dnode_secno, struct quad_buffer_head *);
+dnode_secno hpfs_fnode_dno(struct super_block *s, ino_t ino);
+
+/* name.c */
+
+unsigned char hpfs_upcase(unsigned char *, unsigned char);
+int hpfs_chk_name(unsigned char *, unsigned *);
+char *hpfs_translate_name(struct super_block *, unsigned char *, unsigned, int, int);
+int hpfs_compare_names(struct super_block *, unsigned char *, unsigned, unsigned char *, unsigned, int);
+int hpfs_is_name_long(unsigned char *, unsigned);
+void hpfs_adjust_length(unsigned char *, unsigned *);
+void hpfs_decide_conv(struct inode *, unsigned char *, unsigned);
+
+/* namei.c */
+
+int hpfs_mkdir(struct inode *, struct dentry *, int);
+int hpfs_create(struct inode *, struct dentry *, int);
+int hpfs_mknod(struct inode *, struct dentry *, int, int);
+int hpfs_symlink(struct inode *, struct dentry *, const char *);
+int hpfs_unlink(struct inode *, struct dentry *);
+int hpfs_rmdir(struct inode *, struct dentry *);
+int hpfs_symlink_readpage(struct file *, struct page *);
+int hpfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *);
+
+/* super.c */
+
+void hpfs_error(struct super_block *, char *, ...);
+int hpfs_stop_cycles(struct super_block *, int, int *, int *, char *);
+int hpfs_remount_fs(struct super_block *, int *, char *);
+void hpfs_put_super(struct super_block *);
+unsigned hpfs_count_one_bitmap(struct super_block *, secno);
+int hpfs_statfs(struct super_block *, struct statfs *);
+
+extern struct address_space_operations hpfs_aops;
diff --git a/fs/hpfs/inode.c b/fs/hpfs/inode.c
new file mode 100644
index 00000000000000..9b224cf7ba2d2a
--- /dev/null
+++ b/fs/hpfs/inode.c
@@ -0,0 +1,328 @@
+/*
+ * linux/fs/hpfs/inode.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * inode VFS functions
+ */
+
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include "hpfs_fn.h"
+
+static struct file_operations hpfs_file_ops =
+{
+ llseek: generic_file_llseek,
+ read: generic_file_read,
+ write: hpfs_file_write,
+ mmap: generic_file_mmap,
+ open: hpfs_open,
+ release: hpfs_file_release,
+ fsync: hpfs_file_fsync,
+};
+
+static struct inode_operations hpfs_file_iops =
+{
+ truncate: hpfs_truncate,
+ setattr: hpfs_notify_change,
+};
+
+static struct file_operations hpfs_dir_ops =
+{
+ llseek: hpfs_dir_lseek,
+ read: generic_read_dir,
+ readdir: hpfs_readdir,
+ open: hpfs_open,
+ release: hpfs_dir_release,
+ fsync: hpfs_file_fsync,
+};
+
+static struct inode_operations hpfs_dir_iops =
+{
+ create: hpfs_create,
+ lookup: hpfs_lookup,
+ unlink: hpfs_unlink,
+ symlink: hpfs_symlink,
+ mkdir: hpfs_mkdir,
+ rmdir: hpfs_rmdir,
+ mknod: hpfs_mknod,
+ rename: hpfs_rename,
+ setattr: hpfs_notify_change,
+};
+
+struct address_space_operations hpfs_symlink_aops = {
+ readpage: hpfs_symlink_readpage
+};
+
+void hpfs_read_inode(struct inode *i)
+{
+ struct buffer_head *bh;
+ struct fnode *fnode;
+ struct super_block *sb = i->i_sb;
+ unsigned char *ea;
+ int ea_size;
+ init_MUTEX(&i->i_hpfs_sem);
+ i->i_uid = sb->s_hpfs_uid;
+ i->i_gid = sb->s_hpfs_gid;
+ i->i_mode = sb->s_hpfs_mode;
+ i->i_hpfs_conv = sb->s_hpfs_conv;
+ i->i_blksize = 512;
+ i->i_size = -1;
+ i->i_blocks = -1;
+
+ i->i_hpfs_dno = 0;
+ i->i_hpfs_n_secs = 0;
+ i->i_hpfs_file_sec = 0;
+ i->i_hpfs_disk_sec = 0;
+ i->i_hpfs_dpos = 0;
+ i->i_hpfs_dsubdno = 0;
+ i->i_hpfs_ea_mode = 0;
+ i->i_hpfs_ea_uid = 0;
+ i->i_hpfs_ea_gid = 0;
+ i->i_hpfs_ea_size = 0;
+ i->i_version = ++event;
+
+ i->i_hpfs_rddir_off = NULL;
+ i->i_hpfs_dirty = 0;
+
+ i->i_atime = 0;
+ i->i_mtime = 0;
+ i->i_ctime = 0;
+
+ if (!i->i_sb->s_hpfs_rd_inode)
+ hpfs_error(i->i_sb, "read_inode: s_hpfs_rd_inode == 0");
+ if (i->i_sb->s_hpfs_rd_inode == 2) {
+ i->i_mode |= S_IFREG;
+ i->i_mode &= ~0111;
+ i->i_op = &hpfs_file_iops;
+ i->i_fop = &hpfs_file_ops;
+ i->i_nlink = 1;
+ return;
+ }
+ if (!(fnode = hpfs_map_fnode(sb, i->i_ino, &bh))) {
+ /*i->i_mode |= S_IFREG;
+ i->i_mode &= ~0111;
+ i->i_op = &hpfs_file_iops;
+ i->i_fop = &hpfs_file_ops;
+ i->i_nlink = 0;*/
+ make_bad_inode(i);
+ return;
+ }
+ if (i->i_sb->s_hpfs_eas) {
+ if ((ea = hpfs_get_ea(i->i_sb, fnode, "UID", &ea_size))) {
+ if (ea_size == 2) {
+ i->i_uid = ea[0] + (ea[1] << 8);
+ i->i_hpfs_ea_uid = 1;
+ }
+ kfree(ea);
+ }
+ if ((ea = hpfs_get_ea(i->i_sb, fnode, "GID", &ea_size))) {
+ if (ea_size == 2) {
+ i->i_gid = ea[0] + (ea[1] << 8);
+ i->i_hpfs_ea_gid = 1;
+ }
+ kfree(ea);
+ }
+ if ((ea = hpfs_get_ea(i->i_sb, fnode, "SYMLINK", &ea_size))) {
+ kfree(ea);
+ i->i_mode = S_IFLNK | 0777;
+ i->i_op = &page_symlink_inode_operations;
+ i->i_data.a_ops = &hpfs_symlink_aops;
+ i->i_nlink = 1;
+ i->i_size = ea_size;
+ i->i_blocks = 1;
+ brelse(bh);
+ return;
+ }
+ if ((ea = hpfs_get_ea(i->i_sb, fnode, "MODE", &ea_size))) {
+ int rdev = 0;
+ umode_t mode = sb->s_hpfs_mode;
+ if (ea_size == 2) {
+ mode = ea[0] + (ea[1] << 8);
+ i->i_hpfs_ea_mode = 1;
+ }
+ kfree(ea);
+ i->i_mode = mode;
+ if (S_ISBLK(mode) || S_ISCHR(mode)) {
+ if ((ea = hpfs_get_ea(i->i_sb, fnode, "DEV", &ea_size))) {
+ if (ea_size == 4)
+ rdev = ea[0] + (ea[1] << 8) + (ea[2] << 16) + (ea[3] << 24);
+ kfree(ea);
+ }
+ }
+ if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode)) {
+ brelse(bh);
+ i->i_nlink = 1;
+ i->i_size = 0;
+ i->i_blocks = 1;
+ init_special_inode(i, mode, rdev);
+ return;
+ }
+ }
+ }
+ if (fnode->dirflag) {
+ unsigned n_dnodes, n_subdirs;
+ i->i_mode |= S_IFDIR;
+ i->i_op = &hpfs_dir_iops;
+ i->i_fop = &hpfs_dir_ops;
+ i->i_hpfs_parent_dir = fnode->up;
+ i->i_hpfs_dno = fnode->u.external[0].disk_secno;
+ if (sb->s_hpfs_chk >= 2) {
+ struct buffer_head *bh0;
+ if (hpfs_map_fnode(sb, i->i_hpfs_parent_dir, &bh0)) brelse(bh0);
+ }
+ n_dnodes = 0; n_subdirs = 0;
+ hpfs_count_dnodes(i->i_sb, i->i_hpfs_dno, &n_dnodes, &n_subdirs, NULL);
+ i->i_blocks = 4 * n_dnodes;
+ i->i_size = 2048 * n_dnodes;
+ i->i_nlink = 2 + n_subdirs;
+ } else {
+ i->i_mode |= S_IFREG;
+ if (!i->i_hpfs_ea_mode) i->i_mode &= ~0111;
+ i->i_op = &hpfs_file_iops;
+ i->i_fop = &hpfs_file_ops;
+ i->i_nlink = 1;
+ i->i_size = fnode->file_size;
+ i->i_blocks = ((i->i_size + 511) >> 9) + 1;
+ i->i_data.a_ops = &hpfs_aops;
+ i->u.hpfs_i.mmu_private = i->i_size;
+ }
+ brelse(bh);
+}
+
+void hpfs_write_inode_ea(struct inode *i, struct fnode *fnode)
+{
+ /*if (fnode->acl_size_l || fnode->acl_size_s) {
+ Some unknown structures like ACL may be in fnode,
+ we'd better not overwrite them
+ hpfs_error(i->i_sb, "fnode %08x has some unknown HPFS386 stuctures", i->i_ino);
+ } else*/ if (i->i_sb->s_hpfs_eas >= 2) {
+ unsigned char ea[4];
+ if ((i->i_uid != i->i_sb->s_hpfs_uid) || i->i_hpfs_ea_uid) {
+ ea[0] = i->i_uid & 0xff;
+ ea[1] = i->i_uid >> 8;
+ hpfs_set_ea(i, fnode, "UID", ea, 2);
+ i->i_hpfs_ea_uid = 1;
+ }
+ if ((i->i_gid != i->i_sb->s_hpfs_gid) || i->i_hpfs_ea_gid) {
+ ea[0] = i->i_gid & 0xff;
+ ea[1] = i->i_gid >> 8;
+ hpfs_set_ea(i, fnode, "GID", ea, 2);
+ i->i_hpfs_ea_gid = 1;
+ }
+ if (!S_ISLNK(i->i_mode))
+ if ((i->i_mode != ((i->i_sb->s_hpfs_mode & ~(S_ISDIR(i->i_mode) ? 0 : 0111))
+ | (S_ISDIR(i->i_mode) ? S_IFDIR : S_IFREG))
+ && i->i_mode != ((i->i_sb->s_hpfs_mode & ~(S_ISDIR(i->i_mode) ? 0222 : 0333))
+ | (S_ISDIR(i->i_mode) ? S_IFDIR : S_IFREG))) || i->i_hpfs_ea_mode) {
+ ea[0] = i->i_mode & 0xff;
+ ea[1] = i->i_mode >> 8;
+ hpfs_set_ea(i, fnode, "MODE", ea, 2);
+ i->i_hpfs_ea_mode = 1;
+ }
+ if (S_ISBLK(i->i_mode) || S_ISCHR(i->i_mode)) {
+ int d = kdev_t_to_nr(i->i_rdev);
+ ea[0] = d & 0xff;
+ ea[1] = (d >> 8) & 0xff;
+ ea[2] = (d >> 16) & 0xff;
+ ea[3] = d >> 24;
+ hpfs_set_ea(i, fnode, "DEV", ea, 4);
+ }
+ }
+}
+
+void hpfs_write_inode(struct inode *i)
+{
+ struct inode *parent;
+ if (!i->i_nlink) return;
+ if (i->i_ino == i->i_sb->s_hpfs_root) return;
+ if (i->i_hpfs_rddir_off && !atomic_read(&i->i_count)) {
+ if (*i->i_hpfs_rddir_off) printk("HPFS: write_inode: some position still there\n");
+ kfree(i->i_hpfs_rddir_off);
+ i->i_hpfs_rddir_off = NULL;
+ }
+ i->i_hpfs_dirty = 0;
+ hpfs_lock_iget(i->i_sb, 1);
+ parent = iget(i->i_sb, i->i_hpfs_parent_dir);
+ hpfs_unlock_iget(i->i_sb);
+ hpfs_lock_inode(parent);
+ hpfs_write_inode_nolock(i);
+ hpfs_unlock_inode(parent);
+ iput(parent);
+}
+
+void hpfs_write_inode_nolock(struct inode *i)
+{
+ struct buffer_head *bh;
+ struct fnode *fnode;
+ struct quad_buffer_head qbh;
+ struct hpfs_dirent *de;
+ if (i->i_ino == i->i_sb->s_hpfs_root) return;
+ if (!(fnode = hpfs_map_fnode(i->i_sb, i->i_ino, &bh))) return;
+ if (i->i_ino != i->i_sb->s_hpfs_root && i->i_nlink) {
+ if (!(de = map_fnode_dirent(i->i_sb, i->i_ino, fnode, &qbh))) {
+ brelse(bh);
+ return;
+ }
+ } else de = NULL;
+ if (S_ISREG(i->i_mode)) {
+ fnode->file_size = i->i_size;
+ if (de) de->file_size = i->i_size;
+ } else if (S_ISDIR(i->i_mode)) {
+ fnode->file_size = 0;
+ if (de) de->file_size = 0;
+ }
+ hpfs_write_inode_ea(i, fnode);
+ if (de) {
+ de->write_date = gmt_to_local(i->i_sb, i->i_mtime);
+ de->read_date = gmt_to_local(i->i_sb, i->i_atime);
+ de->creation_date = gmt_to_local(i->i_sb, i->i_ctime);
+ de->read_only = !(i->i_mode & 0222);
+ de->ea_size = i->i_hpfs_ea_size;
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ }
+ if (S_ISDIR(i->i_mode)) {
+ if ((de = map_dirent(i, i->i_hpfs_dno, "\001\001", 2, NULL, &qbh))) {
+ de->write_date = gmt_to_local(i->i_sb, i->i_mtime);
+ de->read_date = gmt_to_local(i->i_sb, i->i_atime);
+ de->creation_date = gmt_to_local(i->i_sb, i->i_ctime);
+ de->read_only = !(i->i_mode & 0222);
+ de->ea_size = /*i->i_hpfs_ea_size*/0;
+ de->file_size = 0;
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ } else hpfs_error(i->i_sb, "directory %08x doesn't have '.' entry", i->i_ino);
+ }
+ mark_buffer_dirty(bh);
+ brelse(bh);
+}
+
+int hpfs_notify_change(struct dentry *dentry, struct iattr *attr)
+{
+ struct inode *inode = dentry->d_inode;
+ int error;
+ if ((attr->ia_valid & ATTR_SIZE) && attr->ia_size > inode->i_size)
+ return -EINVAL;
+ if (inode->i_sb->s_hpfs_root == inode->i_ino) return -EINVAL;
+ if ((error = inode_change_ok(inode, attr))) return error;
+ error = inode_setattr(inode, attr);
+ if (error) return error;
+ hpfs_write_inode(inode);
+ return 0;
+}
+
+void hpfs_write_if_changed(struct inode *inode)
+{
+ if (inode->i_hpfs_dirty) {
+ hpfs_write_inode(inode);
+ }
+}
+
+void hpfs_delete_inode(struct inode *inode)
+{
+ lock_kernel();
+ hpfs_remove_fnode(inode->i_sb, inode->i_ino);
+ unlock_kernel();
+ clear_inode(inode);
+}
diff --git a/fs/hpfs/map.c b/fs/hpfs/map.c
new file mode 100644
index 00000000000000..eef5731b43cf64
--- /dev/null
+++ b/fs/hpfs/map.c
@@ -0,0 +1,275 @@
+/*
+ * linux/fs/hpfs/map.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * mapping structures to memory with some minimal checks
+ */
+
+#include "hpfs_fn.h"
+
+unsigned *hpfs_map_dnode_bitmap(struct super_block *s, struct quad_buffer_head *qbh)
+{
+ return hpfs_map_4sectors(s, s->s_hpfs_dmap, qbh, 0);
+}
+
+unsigned int *hpfs_map_bitmap(struct super_block *s, unsigned bmp_block,
+ struct quad_buffer_head *qbh, char *id)
+{
+ secno sec;
+ if (s->s_hpfs_chk) if (bmp_block * 16384 > s->s_hpfs_fs_size) {
+ hpfs_error(s, "hpfs_map_bitmap called with bad parameter: %08x at %s", bmp_block, id);
+ return NULL;
+ }
+ sec = s->s_hpfs_bmp_dir[bmp_block];
+ if (!sec || sec > s->s_hpfs_fs_size-4) {
+ hpfs_error(s, "invalid bitmap block pointer %08x -> %08x at %s", bmp_block, sec, id);
+ return NULL;
+ }
+ return hpfs_map_4sectors(s, sec, qbh, 4);
+}
+
+/*
+ * Load first code page into kernel memory, return pointer to 256-byte array,
+ * first 128 bytes are uppercasing table for chars 128-255, next 128 bytes are
+ * lowercasing table
+ */
+
+char *hpfs_load_code_page(struct super_block *s, secno cps)
+{
+ struct buffer_head *bh;
+ secno cpds;
+ unsigned cpi;
+ unsigned char *ptr;
+ unsigned char *cp_table;
+ int i;
+ struct code_page_data *cpd;
+ struct code_page_directory *cp = hpfs_map_sector(s, cps, &bh, 0);
+ if (!cp) return NULL;
+ if (cp->magic != CP_DIR_MAGIC) {
+ printk("HPFS: Code page directory magic doesn't match (magic = %08x)\n", cp->magic);
+ brelse(bh);
+ return NULL;
+ }
+ if (!cp->n_code_pages) {
+ printk("HPFS: n_code_pages == 0\n");
+ brelse(bh);
+ return NULL;
+ }
+ cpds = cp->array[0].code_page_data;
+ cpi = cp->array[0].index;
+ brelse(bh);
+
+ if (cpi >= 3) {
+ printk("HPFS: Code page index out of array\n");
+ return NULL;
+ }
+
+ if (!(cpd = hpfs_map_sector(s, cpds, &bh, 0))) return NULL;
+ if ((unsigned)cpd->offs[cpi] > 0x178) {
+ printk("HPFS: Code page index out of sector\n");
+ brelse(bh);
+ return NULL;
+ }
+ ptr = (char *)cpd + cpd->offs[cpi] + 6;
+ if (!(cp_table = kmalloc(256, GFP_KERNEL))) {
+ printk("HPFS: out of memory for code page table\n");
+ brelse(bh);
+ return NULL;
+ }
+ memcpy(cp_table, ptr, 128);
+ brelse(bh);
+
+ /* Try to build lowercasing table from uppercasing one */
+
+ for (i=128; i<256; i++) cp_table[i]=i;
+ for (i=128; i<256; i++) if (cp_table[i-128]!=i && cp_table[i-128]>=128)
+ cp_table[cp_table[i-128]] = i;
+
+ return cp_table;
+}
+
+secno *hpfs_load_bitmap_directory(struct super_block *s, secno bmp)
+{
+ struct buffer_head *bh;
+ int n = (s->s_hpfs_fs_size + 0x200000 - 1) >> 21;
+ int i;
+ secno *b;
+ if (!(b = kmalloc(n * 512, GFP_KERNEL))) {
+ printk("HPFS: can't allocate memory for bitmap directory\n");
+ return NULL;
+ }
+ for (i=0;i<n;i++) {
+ secno *d = hpfs_map_sector(s, bmp+i, &bh, n - i - 1);
+ if (!d) {
+ kfree(b);
+ return NULL;
+ }
+ memcpy((char *)b + 512 * i, d, 512);
+ brelse(bh);
+ }
+ return b;
+}
+
+/*
+ * Load fnode to memory
+ */
+
+struct fnode *hpfs_map_fnode(struct super_block *s, ino_t ino, struct buffer_head **bhp)
+{
+ struct fnode *fnode;
+ if (s->s_hpfs_chk) if (hpfs_chk_sectors(s, ino, 1, "fnode")) {
+ return NULL;
+ }
+ if ((fnode = hpfs_map_sector(s, ino, bhp, FNODE_RD_AHEAD))) {
+ if (s->s_hpfs_chk) {
+ struct extended_attribute *ea;
+ struct extended_attribute *ea_end;
+ if (fnode->magic != FNODE_MAGIC) {
+ hpfs_error(s, "bad magic on fnode %08x", ino);
+ goto bail;
+ }
+ if (!fnode->dirflag) {
+ if ((unsigned)fnode->btree.n_used_nodes + (unsigned)fnode->btree.n_free_nodes !=
+ (fnode->btree.internal ? 12 : 8)) {
+ hpfs_error(s, "bad number of nodes in fnode %08x", ino);
+ goto bail;
+ }
+ if (fnode->btree.first_free !=
+ 8 + fnode->btree.n_used_nodes * (fnode->btree.internal ? 8 : 12)) {
+ hpfs_error(s, "bad first_free pointer in fnode %08x", ino);
+ goto bail;
+ }
+ }
+ if (fnode->ea_size_s && ((signed int)fnode->ea_offs < 0xc4 ||
+ (signed int)fnode->ea_offs + fnode->acl_size_s + fnode->ea_size_s > 0x200)) {
+ hpfs_error(s, "bad EA info in fnode %08x: ea_offs == %04x ea_size_s == %04x",
+ ino, fnode->ea_offs, fnode->ea_size_s);
+ goto bail;
+ }
+ ea = fnode_ea(fnode);
+ ea_end = fnode_end_ea(fnode);
+ while (ea != ea_end) {
+ if (ea > ea_end) {
+ hpfs_error(s, "bad EA in fnode %08x", ino);
+ goto bail;
+ }
+ ea = next_ea(ea);
+ }
+ }
+ }
+ return fnode;
+ bail:
+ brelse(*bhp);
+ return NULL;
+}
+
+struct anode *hpfs_map_anode(struct super_block *s, anode_secno ano, struct buffer_head **bhp)
+{
+ struct anode *anode;
+ if (s->s_hpfs_chk) if (hpfs_chk_sectors(s, ano, 1, "anode")) return NULL;
+ if ((anode = hpfs_map_sector(s, ano, bhp, ANODE_RD_AHEAD)))
+ if (s->s_hpfs_chk) {
+ if (anode->magic != ANODE_MAGIC || anode->self != ano) {
+ hpfs_error(s, "bad magic on anode %08x", ano);
+ goto bail;
+ }
+ if ((unsigned)anode->btree.n_used_nodes + (unsigned)anode->btree.n_free_nodes !=
+ (anode->btree.internal ? 60 : 40)) {
+ hpfs_error(s, "bad number of nodes in anode %08x", ano);
+ goto bail;
+ }
+ if (anode->btree.first_free !=
+ 8 + anode->btree.n_used_nodes * (anode->btree.internal ? 8 : 12)) {
+ hpfs_error(s, "bad first_free pointer in anode %08x", ano);
+ goto bail;
+ }
+ }
+ return anode;
+ bail:
+ brelse(*bhp);
+ return NULL;
+}
+
+/*
+ * Load dnode to memory and do some checks
+ */
+
+struct dnode *hpfs_map_dnode(struct super_block *s, unsigned secno,
+ struct quad_buffer_head *qbh)
+{
+ struct dnode *dnode;
+ if (s->s_hpfs_chk) {
+ if (hpfs_chk_sectors(s, secno, 4, "dnode")) return NULL;
+ if (secno & 3) {
+ hpfs_error(s, "dnode %08x not byte-aligned", secno);
+ return NULL;
+ }
+ }
+ if ((dnode = hpfs_map_4sectors(s, secno, qbh, DNODE_RD_AHEAD)))
+ if (s->s_hpfs_chk) {
+ unsigned p, pp = 0;
+ unsigned char *d = (char *)dnode;
+ int b = 0;
+ if (dnode->magic != DNODE_MAGIC) {
+ hpfs_error(s, "bad magic on dnode %08x", secno);
+ goto bail;
+ }
+ if (dnode->self != secno)
+ hpfs_error(s, "bad self pointer on dnode %08x self = %08x", secno, dnode->self);
+ /* Check dirents - bad dirents would cause infinite
+ loops or shooting to memory */
+ if (dnode->first_free > 2048/* || dnode->first_free < 84*/) {
+ hpfs_error(s, "dnode %08x has first_free == %08x", secno, dnode->first_free);
+ goto bail;
+ }
+ for (p = 20; p < dnode->first_free; p += d[p] + (d[p+1] << 8)) {
+ struct hpfs_dirent *de = (struct hpfs_dirent *)((char *)dnode + p);
+ if (de->length > 292 || (de->length < 32) || (de->length & 3) || p + de->length > 2048) {
+ hpfs_error(s, "bad dirent size in dnode %08x, dirent %03x, last %03x", secno, p, pp);
+ goto bail;
+ }
+ if (((31 + de->namelen + de->down*4 + 3) & ~3) != de->length) {
+ if (((31 + de->namelen + de->down*4 + 3) & ~3) < de->length && s->s_flags & MS_RDONLY) goto ok;
+ hpfs_error(s, "namelen does not match dirent size in dnode %08x, dirent %03x, last %03x", secno, p, pp);
+ goto bail;
+ }
+ ok:
+ if (s->s_hpfs_chk >= 2) b |= 1 << de->down;
+ if (de->down) if (de_down_pointer(de) < 0x10) {
+ hpfs_error(s, "bad down pointer in dnode %08x, dirent %03x, last %03x", secno, p, pp);
+ goto bail;
+ }
+ pp = p;
+
+ }
+ if (p != dnode->first_free) {
+ hpfs_error(s, "size on last dirent does not match first_free; dnode %08x", secno);
+ goto bail;
+ }
+ if (d[pp + 30] != 1 || d[pp + 31] != 255) {
+ hpfs_error(s, "dnode %08x does not end with \\377 entry", secno);
+ goto bail;
+ }
+ if (b == 3) printk("HPFS: warning: unbalanced dnode tree, dnode %08x; see hpfs.txt 4 more info\n", secno);
+ }
+ return dnode;
+ bail:
+ hpfs_brelse4(qbh);
+ return NULL;
+}
+
+dnode_secno hpfs_fnode_dno(struct super_block *s, ino_t ino)
+{
+ struct buffer_head *bh;
+ struct fnode *fnode;
+ dnode_secno dno;
+
+ fnode = hpfs_map_fnode(s, ino, &bh);
+ if (!fnode)
+ return 0;
+
+ dno = fnode->u.external[0].disk_secno;
+ brelse(bh);
+ return dno;
+}
diff --git a/fs/hpfs/name.c b/fs/hpfs/name.c
new file mode 100644
index 00000000000000..f9fa94c7ef8665
--- /dev/null
+++ b/fs/hpfs/name.c
@@ -0,0 +1,144 @@
+/*
+ * linux/fs/hpfs/name.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * operations with filenames
+ */
+
+#include <linux/string.h>
+#include "hpfs_fn.h"
+
+char *text_postfix[]={
+".ASM", ".BAS", ".BAT", ".C", ".CC", ".CFG", ".CMD", ".CON", ".CPP", ".DEF",
+".DOC", ".DPR", ".ERX", ".H", ".HPP", ".HTM", ".HTML", ".JAVA", ".LOG", ".PAS",
+".RC", ".TEX", ".TXT", ".Y", ""};
+
+char *text_prefix[]={
+"AUTOEXEC.", "CHANGES", "COPYING", "CONFIG.", "CREDITS", "FAQ", "FILE_ID.DIZ",
+"MAKEFILE", "READ.ME", "README", "TERMCAP", ""};
+
+void hpfs_decide_conv(struct inode *inode, unsigned char *name, unsigned len)
+{
+ int i;
+ if (inode->i_hpfs_conv != CONV_AUTO) return;
+ for (i = 0; *text_postfix[i]; i++) {
+ int l = strlen(text_postfix[i]);
+ if (l <= len)
+ if (!hpfs_compare_names(inode->i_sb, text_postfix[i], l, name + len - l, l, 0))
+ goto text;
+ }
+ for (i = 0; *text_prefix[i]; i++) {
+ int l = strlen(text_prefix[i]);
+ if (l <= len)
+ if (!hpfs_compare_names(inode->i_sb, text_prefix[i], l, name, l, 0))
+ goto text;
+ }
+ inode->i_hpfs_conv = CONV_BINARY;
+ return;
+ text:
+ inode->i_hpfs_conv = CONV_TEXT;
+ return;
+}
+
+static inline int not_allowed_char(unsigned char c)
+{
+ return c<' ' || c=='"' || c=='*' || c=='/' || c==':' || c=='<' ||
+ c=='>' || c=='?' || c=='\\' || c=='|';
+}
+
+static inline int no_dos_char(unsigned char c)
+{ /* Characters that are allowed in HPFS but not in DOS */
+ return c=='+' || c==',' || c==';' || c=='=' || c=='[' || c==']';
+}
+
+static inline unsigned char upcase(unsigned char *dir, unsigned char a)
+{
+ if (a<128 || a==255) return a>='a' && a<='z' ? a - 0x20 : a;
+ if (!dir) return a;
+ return dir[a-128];
+}
+
+unsigned char hpfs_upcase(unsigned char *dir, unsigned char a)
+{
+ return upcase(dir, a);
+}
+
+static inline unsigned char locase(unsigned char *dir, unsigned char a)
+{
+ if (a<128 || a==255) return a>='A' && a<='Z' ? a + 0x20 : a;
+ if (!dir) return a;
+ return dir[a];
+}
+
+int hpfs_chk_name(unsigned char *name, unsigned *len)
+{
+ int i;
+ if (*len > 254) return -ENAMETOOLONG;
+ hpfs_adjust_length(name, len);
+ if (!*len) return -EINVAL;
+ for (i = 0; i < *len; i++) if (not_allowed_char(name[i])) return -EINVAL;
+ if (*len == 1) if (name[0] == '.') return -EINVAL;
+ if (*len == 2) if (name[0] == '.' && name[1] == '.') return -EINVAL;
+ return 0;
+}
+
+char *hpfs_translate_name(struct super_block *s, unsigned char *from,
+ unsigned len, int lc, int lng)
+{
+ char *to;
+ int i;
+ if (s->s_hpfs_chk >= 2) if (hpfs_is_name_long(from, len) != lng) {
+ printk("HPFS: Long name flag mismatch - name ");
+ for (i=0; i<len; i++) printk("%c", from[i]);
+ printk(" misidentified as %s.\n", lng ? "short" : "long");
+ printk("HPFS: It's nothing serious. It could happen because of bug in OS/2.\nHPFS: Set checks=normal to disable this message.\n");
+ }
+ if (!lc) return from;
+ if (!(to = kmalloc(len, GFP_KERNEL))) {
+ printk("HPFS: can't allocate memory for name conversion buffer\n");
+ return from;
+ }
+ for (i = 0; i < len; i++) to[i] = locase(s->s_hpfs_cp_table,from[i]);
+ return to;
+}
+
+int hpfs_compare_names(struct super_block *s, unsigned char *n1, unsigned l1,
+ unsigned char *n2, unsigned l2, int last)
+{
+ unsigned l = l1 < l2 ? l1 : l2;
+ unsigned i;
+ if (last) return -1;
+ for (i = 0; i < l; i++) {
+ unsigned char c1 = upcase(s->s_hpfs_cp_table,n1[i]);
+ unsigned char c2 = upcase(s->s_hpfs_cp_table,n2[i]);
+ if (c1 < c2) return -1;
+ if (c1 > c2) return 1;
+ }
+ if (l1 < l2) return -1;
+ if (l1 > l2) return 1;
+ return 0;
+}
+
+int hpfs_is_name_long(unsigned char *name, unsigned len)
+{
+ int i,j;
+ for (i = 0; i < len && name[i] != '.'; i++)
+ if (no_dos_char(name[i])) return 1;
+ if (!i || i > 8) return 1;
+ if (i == len) return 0;
+ for (j = i + 1; j < len; j++)
+ if (name[j] == '.' || no_dos_char(name[i])) return 1;
+ return j - i > 4;
+}
+
+/* OS/2 clears dots and spaces at the end of file name, so we have to */
+
+void hpfs_adjust_length(unsigned char *name, unsigned *len)
+{
+ if (!*len) return;
+ if (*len == 1 && name[0] == '.') return;
+ if (*len == 2 && name[0] == '.' && name[1] == '.') return;
+ while (*len && (name[*len - 1] == '.' || name[*len - 1] == ' '))
+ (*len)--;
+}
diff --git a/fs/hpfs/namei.c b/fs/hpfs/namei.c
new file mode 100644
index 00000000000000..0947673627120d
--- /dev/null
+++ b/fs/hpfs/namei.c
@@ -0,0 +1,534 @@
+/*
+ * linux/fs/hpfs/namei.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * adding & removing files & directories
+ */
+
+#include <linux/string.h>
+#include "hpfs_fn.h"
+
+int hpfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+ const char *name = dentry->d_name.name;
+ unsigned len = dentry->d_name.len;
+ struct quad_buffer_head qbh0;
+ struct buffer_head *bh;
+ struct hpfs_dirent *de;
+ struct fnode *fnode;
+ struct dnode *dnode;
+ struct inode *result;
+ fnode_secno fno;
+ dnode_secno dno;
+ int r;
+ struct hpfs_dirent dee;
+ int err;
+ if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err;
+ if (!(fnode = hpfs_alloc_fnode(dir->i_sb, dir->i_hpfs_dno, &fno, &bh))) goto bail;
+ if (!(dnode = hpfs_alloc_dnode(dir->i_sb, fno, &dno, &qbh0, 1))) goto bail1;
+ memset(&dee, 0, sizeof dee);
+ dee.directory = 1;
+ if (!(mode & 0222)) dee.read_only = 1;
+ /*dee.archive = 0;*/
+ dee.hidden = name[0] == '.';
+ dee.fnode = fno;
+ dee.creation_date = dee.write_date = dee.read_date = gmt_to_local(dir->i_sb, CURRENT_TIME);
+ hpfs_lock_inode(dir);
+ r = hpfs_add_dirent(dir, (char *)name, len, &dee, 0);
+ if (r == 1) goto bail2;
+ if (r == -1) {
+ brelse(bh);
+ hpfs_brelse4(&qbh0);
+ hpfs_free_sectors(dir->i_sb, fno, 1);
+ hpfs_free_dnode(dir->i_sb, dno);
+ hpfs_unlock_inode(dir);
+ return -EEXIST;
+ }
+ fnode->len = len;
+ memcpy(fnode->name, name, len > 15 ? 15 : len);
+ fnode->up = dir->i_ino;
+ fnode->dirflag = 1;
+ fnode->btree.n_free_nodes = 7;
+ fnode->btree.n_used_nodes = 1;
+ fnode->btree.first_free = 0x14;
+ fnode->u.external[0].disk_secno = dno;
+ fnode->u.external[0].file_secno = -1;
+ dnode->root_dnode = 1;
+ dnode->up = fno;
+ de = hpfs_add_de(dir->i_sb, dnode, "\001\001", 2, 0);
+ de->creation_date = de->write_date = de->read_date = gmt_to_local(dir->i_sb, CURRENT_TIME);
+ if (!(mode & 0222)) de->read_only = 1;
+ de->first = de->directory = 1;
+ /*de->hidden = de->system = 0;*/
+ de->fnode = fno;
+ mark_buffer_dirty(bh);
+ brelse(bh);
+ hpfs_mark_4buffers_dirty(&qbh0);
+ hpfs_brelse4(&qbh0);
+ dir->i_nlink++;
+ hpfs_lock_iget(dir->i_sb, 1);
+ if ((result = iget(dir->i_sb, fno))) {
+ result->i_hpfs_parent_dir = dir->i_ino;
+ result->i_ctime = result->i_mtime = result->i_atime = local_to_gmt(dir->i_sb, dee.creation_date);
+ result->i_hpfs_ea_size = 0;
+ if (dee.read_only) result->i_mode &= ~0222;
+ if (result->i_uid != current->fsuid ||
+ result->i_gid != current->fsgid ||
+ result->i_mode != (mode | S_IFDIR)) {
+ result->i_uid = current->fsuid;
+ result->i_gid = current->fsgid;
+ result->i_mode = mode | S_IFDIR;
+ hpfs_write_inode_nolock(result);
+ }
+ d_instantiate(dentry, result);
+ }
+ hpfs_unlock_iget(dir->i_sb);
+ hpfs_unlock_inode(dir);
+ return 0;
+ bail2:
+ hpfs_brelse4(&qbh0);
+ hpfs_free_dnode(dir->i_sb, dno);
+ hpfs_unlock_inode(dir);
+ bail1:
+ brelse(bh);
+ hpfs_free_sectors(dir->i_sb, fno, 1);
+ bail:
+ return -ENOSPC;
+}
+
+int hpfs_create(struct inode *dir, struct dentry *dentry, int mode)
+{
+ const char *name = dentry->d_name.name;
+ unsigned len = dentry->d_name.len;
+ struct inode *result = NULL;
+ struct buffer_head *bh;
+ struct fnode *fnode;
+ fnode_secno fno;
+ int r;
+ struct hpfs_dirent dee;
+ int err;
+ if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err;
+ if (!(fnode = hpfs_alloc_fnode(dir->i_sb, dir->i_hpfs_dno, &fno, &bh))) goto bail;
+ memset(&dee, 0, sizeof dee);
+ if (!(mode & 0222)) dee.read_only = 1;
+ dee.archive = 1;
+ dee.hidden = name[0] == '.';
+ dee.fnode = fno;
+ dee.creation_date = dee.write_date = dee.read_date = gmt_to_local(dir->i_sb, CURRENT_TIME);
+ hpfs_lock_inode(dir);
+ r = hpfs_add_dirent(dir, (char *)name, len, &dee, 0);
+ if (r == 1) goto bail1;
+ if (r == -1) {
+ brelse(bh);
+ hpfs_free_sectors(dir->i_sb, fno, 1);
+ hpfs_unlock_inode(dir);
+ return -EEXIST;
+ }
+ fnode->len = len;
+ memcpy(fnode->name, name, len > 15 ? 15 : len);
+ fnode->up = dir->i_ino;
+ mark_buffer_dirty(bh);
+ brelse(bh);
+ hpfs_lock_iget(dir->i_sb, 2);
+ if ((result = iget(dir->i_sb, fno))) {
+ hpfs_decide_conv(result, (char *)name, len);
+ result->i_hpfs_parent_dir = dir->i_ino;
+ result->i_ctime = result->i_mtime = result->i_atime = local_to_gmt(dir->i_sb, dee.creation_date);
+ result->i_hpfs_ea_size = 0;
+ if (dee.read_only) result->i_mode &= ~0222;
+ if (result->i_blocks == -1) result->i_blocks = 1;
+ if (result->i_size == -1) {
+ result->i_size = 0;
+ result->i_data.a_ops = &hpfs_aops;
+ result->u.hpfs_i.mmu_private = 0;
+ }
+ if (result->i_uid != current->fsuid ||
+ result->i_gid != current->fsgid ||
+ result->i_mode != (mode | S_IFREG)) {
+ result->i_uid = current->fsuid;
+ result->i_gid = current->fsgid;
+ result->i_mode = mode | S_IFREG;
+ hpfs_write_inode_nolock(result);
+ }
+ d_instantiate(dentry, result);
+ }
+ hpfs_unlock_iget(dir->i_sb);
+ hpfs_unlock_inode(dir);
+ return 0;
+ bail1:
+ brelse(bh);
+ hpfs_free_sectors(dir->i_sb, fno, 1);
+ hpfs_unlock_inode(dir);
+ bail:
+ return -ENOSPC;
+}
+
+int hpfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev)
+{
+ const char *name = dentry->d_name.name;
+ unsigned len = dentry->d_name.len;
+ struct buffer_head *bh;
+ struct fnode *fnode;
+ fnode_secno fno;
+ int r;
+ struct hpfs_dirent dee;
+ struct inode *result = NULL;
+ int err;
+ if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err;
+ if (dir->i_sb->s_hpfs_eas < 2) return -EPERM;
+ if (!(fnode = hpfs_alloc_fnode(dir->i_sb, dir->i_hpfs_dno, &fno, &bh))) goto bail;
+ memset(&dee, 0, sizeof dee);
+ if (!(mode & 0222)) dee.read_only = 1;
+ dee.archive = 1;
+ dee.hidden = name[0] == '.';
+ dee.fnode = fno;
+ dee.creation_date = dee.write_date = dee.read_date = gmt_to_local(dir->i_sb, CURRENT_TIME);
+ hpfs_lock_inode(dir);
+ r = hpfs_add_dirent(dir, (char *)name, len, &dee, 0);
+ if (r == 1) goto bail1;
+ if (r == -1) {
+ brelse(bh);
+ hpfs_free_sectors(dir->i_sb, fno, 1);
+ hpfs_unlock_inode(dir);
+ return -EEXIST;
+ }
+ fnode->len = len;
+ memcpy(fnode->name, name, len > 15 ? 15 : len);
+ fnode->up = dir->i_ino;
+ mark_buffer_dirty(bh);
+ hpfs_lock_iget(dir->i_sb, 2);
+ if ((result = iget(dir->i_sb, fno))) {
+ result->i_hpfs_parent_dir = dir->i_ino;
+ result->i_ctime = result->i_mtime = result->i_atime = local_to_gmt(dir->i_sb, dee.creation_date);
+ result->i_hpfs_ea_size = 0;
+ /*if (result->i_blocks == -1) result->i_blocks = 1;
+ if (result->i_size == -1) result->i_size = 0;*/
+ result->i_uid = current->fsuid;
+ result->i_gid = current->fsgid;
+ result->i_nlink = 1;
+ result->i_size = 0;
+ result->i_blocks = 1;
+ init_special_inode(result, mode, rdev);
+ hpfs_write_inode_nolock(result);
+ d_instantiate(dentry, result);
+ }
+ hpfs_unlock_iget(dir->i_sb);
+ hpfs_unlock_inode(dir);
+ brelse(bh);
+ return 0;
+ bail1:
+ brelse(bh);
+ hpfs_free_sectors(dir->i_sb, fno, 1);
+ hpfs_unlock_inode(dir);
+ bail:
+ return -ENOSPC;
+}
+
+extern struct address_space_operations hpfs_symlink_aops;
+
+int hpfs_symlink(struct inode *dir, struct dentry *dentry, const char *symlink)
+{
+ const char *name = dentry->d_name.name;
+ unsigned len = dentry->d_name.len;
+ struct buffer_head *bh;
+ struct fnode *fnode;
+ fnode_secno fno;
+ int r;
+ struct hpfs_dirent dee;
+ struct inode *result;
+ int err;
+ if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err;
+ if (dir->i_sb->s_hpfs_eas < 2) return -EPERM;
+ if (!(fnode = hpfs_alloc_fnode(dir->i_sb, dir->i_hpfs_dno, &fno, &bh))) goto bail;
+ memset(&dee, 0, sizeof dee);
+ dee.archive = 1;
+ dee.hidden = name[0] == '.';
+ dee.fnode = fno;
+ dee.creation_date = dee.write_date = dee.read_date = gmt_to_local(dir->i_sb, CURRENT_TIME);
+ hpfs_lock_inode(dir);
+ r = hpfs_add_dirent(dir, (char *)name, len, &dee, 0);
+ if (r == 1) goto bail1;
+ if (r == -1) {
+ brelse(bh);
+ hpfs_free_sectors(dir->i_sb, fno, 1);
+ hpfs_unlock_inode(dir);
+ return -EEXIST;
+ }
+ fnode->len = len;
+ memcpy(fnode->name, name, len > 15 ? 15 : len);
+ fnode->up = dir->i_ino;
+ mark_buffer_dirty(bh);
+ brelse(bh);
+ hpfs_lock_iget(dir->i_sb, 2);
+ if ((result = iget(dir->i_sb, fno))) {
+ result->i_hpfs_parent_dir = dir->i_ino;
+ result->i_ctime = result->i_mtime = result->i_atime = local_to_gmt(dir->i_sb, dee.creation_date);
+ result->i_hpfs_ea_size = 0;
+ /*if (result->i_blocks == -1) result->i_blocks = 1;
+ if (result->i_size == -1) result->i_size = 0;*/
+ result->i_mode = S_IFLNK | 0777;
+ result->i_uid = current->fsuid;
+ result->i_gid = current->fsgid;
+ result->i_blocks = 1;
+ result->i_size = strlen(symlink);
+ result->i_op = &page_symlink_inode_operations;
+ result->i_data.a_ops = &hpfs_symlink_aops;
+ if ((fnode = hpfs_map_fnode(dir->i_sb, fno, &bh))) {
+ hpfs_set_ea(result, fnode, "SYMLINK", (char *)symlink, strlen(symlink));
+ mark_buffer_dirty(bh);
+ brelse(bh);
+ }
+ hpfs_write_inode_nolock(result);
+ d_instantiate(dentry, result);
+ }
+ hpfs_unlock_iget(dir->i_sb);
+ hpfs_unlock_inode(dir);
+ return 0;
+ bail1:
+ brelse(bh);
+ hpfs_free_sectors(dir->i_sb, fno, 1);
+ hpfs_unlock_inode(dir);
+ bail:
+ return -ENOSPC;
+}
+
+int hpfs_unlink(struct inode *dir, struct dentry *dentry)
+{
+ const char *name = dentry->d_name.name;
+ unsigned len = dentry->d_name.len;
+ struct quad_buffer_head qbh;
+ struct hpfs_dirent *de;
+ struct inode *inode = dentry->d_inode;
+ dnode_secno dno;
+ fnode_secno fno;
+ int r;
+ int rep = 0;
+ hpfs_adjust_length((char *)name, &len);
+ again:
+ hpfs_lock_2inodes(dir, inode);
+ if (!(de = map_dirent(dir, dir->i_hpfs_dno, (char *)name, len, &dno, &qbh))) {
+ hpfs_unlock_2inodes(dir, inode);
+ return -ENOENT;
+ }
+ if (de->first) {
+ hpfs_brelse4(&qbh);
+ hpfs_unlock_2inodes(dir, inode);
+ return -EPERM;
+ }
+ if (de->directory) {
+ hpfs_brelse4(&qbh);
+ hpfs_unlock_2inodes(dir, inode);
+ return -EISDIR;
+ }
+ fno = de->fnode;
+ if ((r = hpfs_remove_dirent(dir, dno, de, &qbh, 1)) == 1) hpfs_error(dir->i_sb, "there was error when removing dirent");
+ if (r != 2) {
+ inode->i_nlink--;
+ hpfs_unlock_2inodes(dir, inode);
+ } else { /* no space for deleting, try to truncate file */
+ struct iattr newattrs;
+ int err;
+ hpfs_unlock_2inodes(dir, inode);
+ if (rep)
+ goto ret;
+ d_drop(dentry);
+ if (atomic_read(&dentry->d_count) > 1 ||
+ permission(inode, MAY_WRITE) ||
+ get_write_access(inode)) {
+ d_rehash(dentry);
+ goto ret;
+ }
+ /*printk("HPFS: truncating file before delete.\n");*/
+ down(&inode->i_sem);
+ newattrs.ia_size = 0;
+ newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME;
+ err = notify_change(dentry, &newattrs);
+ up(&inode->i_sem);
+ put_write_access(inode);
+ if (err)
+ goto ret;
+ rep = 1;
+ goto again;
+ }
+ ret:
+ return r == 2 ? -ENOSPC : r == 1 ? -EFSERROR : 0;
+}
+
+int hpfs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ const char *name = dentry->d_name.name;
+ unsigned len = dentry->d_name.len;
+ struct quad_buffer_head qbh;
+ struct hpfs_dirent *de;
+ struct inode *inode = dentry->d_inode;
+ dnode_secno dno;
+ fnode_secno fno;
+ int n_items = 0;
+ int r;
+ hpfs_adjust_length((char *)name, &len);
+ hpfs_lock_2inodes(dir, inode);
+ if (!(de = map_dirent(dir, dir->i_hpfs_dno, (char *)name, len, &dno, &qbh))) {
+ hpfs_unlock_2inodes(dir, inode);
+ return -ENOENT;
+ }
+ if (de->first) {
+ hpfs_brelse4(&qbh);
+ hpfs_unlock_2inodes(dir, inode);
+ return -EPERM;
+ }
+ if (!de->directory) {
+ hpfs_brelse4(&qbh);
+ hpfs_unlock_2inodes(dir, inode);
+ return -ENOTDIR;
+ }
+ hpfs_count_dnodes(dir->i_sb, inode->i_hpfs_dno, NULL, NULL, &n_items);
+ if (n_items) {
+ hpfs_brelse4(&qbh);
+ hpfs_unlock_2inodes(dir, inode);
+ return -ENOTEMPTY;
+ }
+ fno = de->fnode;
+ if ((r = hpfs_remove_dirent(dir, dno, de, &qbh, 1)) == 1)
+ hpfs_error(dir->i_sb, "there was error when removing dirent");
+ if (r != 2) {
+ dir->i_nlink--;
+ inode->i_nlink = 0;
+ hpfs_unlock_2inodes(dir, inode);
+ } else hpfs_unlock_2inodes(dir, inode);
+ return r == 2 ? -ENOSPC : r == 1 ? -EFSERROR : 0;
+}
+
+int hpfs_symlink_readpage(struct file *file, struct page *page)
+{
+ char *link = kmap(page);
+ struct inode *i = page->mapping->host;
+ struct fnode *fnode;
+ struct buffer_head *bh;
+ int err;
+
+ err = -EIO;
+ lock_kernel();
+ if (!(fnode = hpfs_map_fnode(i->i_sb, i->i_ino, &bh)))
+ goto fail;
+ err = hpfs_read_ea(i->i_sb, fnode, "SYMLINK", link, PAGE_SIZE);
+ brelse(bh);
+ if (err)
+ goto fail;
+ unlock_kernel();
+ SetPageUptodate(page);
+ kunmap(page);
+ UnlockPage(page);
+ return 0;
+
+fail:
+ unlock_kernel();
+ SetPageError(page);
+ kunmap(page);
+ UnlockPage(page);
+ return err;
+}
+
+int hpfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ char *old_name = (char *)old_dentry->d_name.name;
+ int old_len = old_dentry->d_name.len;
+ char *new_name = (char *)new_dentry->d_name.name;
+ int new_len = new_dentry->d_name.len;
+ struct inode *i = old_dentry->d_inode;
+ struct inode *new_inode = new_dentry->d_inode;
+ struct quad_buffer_head qbh, qbh1;
+ struct hpfs_dirent *dep, *nde;
+ struct hpfs_dirent de;
+ dnode_secno dno;
+ int r;
+ struct buffer_head *bh;
+ struct fnode *fnode;
+ int err;
+ if ((err = hpfs_chk_name((char *)new_name, &new_len))) return err;
+ err = 0;
+ hpfs_adjust_length((char *)old_name, &old_len);
+
+ hpfs_lock_3inodes(old_dir, new_dir, i);
+
+ /* Erm? Moving over the empty non-busy directory is perfectly legal */
+ if (new_inode && S_ISDIR(new_inode->i_mode)) {
+ err = -EINVAL;
+ goto end1;
+ }
+
+ if (!(dep = map_dirent(old_dir, old_dir->i_hpfs_dno, (char *)old_name, old_len, &dno, &qbh))) {
+ hpfs_error(i->i_sb, "lookup succeeded but map dirent failed");
+ err = -ENOENT;
+ goto end1;
+ }
+ copy_de(&de, dep);
+ de.hidden = new_name[0] == '.';
+
+ if (new_inode) {
+ int r;
+ if ((r = hpfs_remove_dirent(old_dir, dno, dep, &qbh, 1)) != 2) {
+ if ((nde = map_dirent(new_dir, new_dir->i_hpfs_dno, (char *)new_name, new_len, NULL, &qbh1))) {
+ new_inode->i_nlink = 0;
+ copy_de(nde, &de);
+ memcpy(nde->name, new_name, new_len);
+ hpfs_mark_4buffers_dirty(&qbh1);
+ hpfs_brelse4(&qbh1);
+ goto end;
+ }
+ hpfs_error(new_dir->i_sb, "hpfs_rename: could not find dirent");
+ err = -EFSERROR;
+ goto end1;
+ }
+ err = r == 2 ? -ENOSPC : r == 1 ? -EFSERROR : 0;
+ goto end1;
+ }
+
+ if (new_dir == old_dir) hpfs_brelse4(&qbh);
+
+ hpfs_lock_creation(i->i_sb);
+ if ((r = hpfs_add_dirent(new_dir, new_name, new_len, &de, 1))) {
+ hpfs_unlock_creation(i->i_sb);
+ if (r == -1) hpfs_error(new_dir->i_sb, "hpfs_rename: dirent already exists!");
+ err = r == 1 ? -ENOSPC : -EFSERROR;
+ if (new_dir != old_dir) hpfs_brelse4(&qbh);
+ goto end1;
+ }
+
+ if (new_dir == old_dir)
+ if (!(dep = map_dirent(old_dir, old_dir->i_hpfs_dno, (char *)old_name, old_len, &dno, &qbh))) {
+ hpfs_unlock_creation(i->i_sb);
+ hpfs_error(i->i_sb, "lookup succeeded but map dirent failed at #2");
+ err = -ENOENT;
+ goto end1;
+ }
+
+ if ((r = hpfs_remove_dirent(old_dir, dno, dep, &qbh, 0))) {
+ hpfs_unlock_creation(i->i_sb);
+ hpfs_error(i->i_sb, "hpfs_rename: could not remove dirent");
+ err = r == 2 ? -ENOSPC : -EFSERROR;
+ goto end1;
+ }
+ hpfs_unlock_creation(i->i_sb);
+
+ end:
+ i->i_hpfs_parent_dir = new_dir->i_ino;
+ if (S_ISDIR(i->i_mode)) {
+ new_dir->i_nlink++;
+ old_dir->i_nlink--;
+ }
+ if ((fnode = hpfs_map_fnode(i->i_sb, i->i_ino, &bh))) {
+ fnode->up = new_dir->i_ino;
+ fnode->len = new_len;
+ memcpy(fnode->name, new_name, new_len>15?15:new_len);
+ if (new_len < 15) memset(&fnode->name[new_len], 0, 15 - new_len);
+ mark_buffer_dirty(bh);
+ brelse(bh);
+ }
+ i->i_hpfs_conv = i->i_sb->s_hpfs_conv;
+ hpfs_decide_conv(i, (char *)new_name, new_len);
+ end1:
+ hpfs_unlock_3inodes(old_dir, new_dir, i);
+ return err;
+}
diff --git a/fs/hpfs/super.c b/fs/hpfs/super.c
new file mode 100644
index 00000000000000..9079a28344261b
--- /dev/null
+++ b/fs/hpfs/super.c
@@ -0,0 +1,588 @@
+/*
+ * linux/fs/hpfs/super.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * mounting, unmounting, error handling
+ */
+
+#include <linux/string.h>
+#include "hpfs_fn.h"
+#include <linux/module.h>
+#include <linux/init.h>
+
+/* Mark the filesystem dirty, so that chkdsk checks it when os/2 booted */
+
+static void mark_dirty(struct super_block *s)
+{
+ if (s->s_hpfs_chkdsk && !(s->s_flags & MS_RDONLY)) {
+ struct buffer_head *bh;
+ struct hpfs_spare_block *sb;
+ if ((sb = hpfs_map_sector(s, 17, &bh, 0))) {
+ sb->dirty = 1;
+ sb->old_wrote = 0;
+ mark_buffer_dirty(bh);
+ brelse(bh);
+ }
+ }
+}
+
+/* Mark the filesystem clean (mark it dirty for chkdsk if chkdsk==2 or if there
+ were errors) */
+
+static void unmark_dirty(struct super_block *s)
+{
+ struct buffer_head *bh;
+ struct hpfs_spare_block *sb;
+ if (s->s_flags & MS_RDONLY) return;
+ if ((sb = hpfs_map_sector(s, 17, &bh, 0))) {
+ sb->dirty = s->s_hpfs_chkdsk > 1 - s->s_hpfs_was_error;
+ sb->old_wrote = s->s_hpfs_chkdsk >= 2 && !s->s_hpfs_was_error;
+ mark_buffer_dirty(bh);
+ brelse(bh);
+ }
+}
+
+/* Filesystem error... */
+
+#define ERR_BUF_SIZE 1024
+
+void hpfs_error(struct super_block *s, char *m,...)
+{
+ char *buf;
+ va_list l;
+ va_start(l, m);
+ if (!(buf = kmalloc(ERR_BUF_SIZE, GFP_KERNEL)))
+ printk("HPFS: No memory for error message '%s'\n",m);
+ else if (vsprintf(buf, m, l) >= ERR_BUF_SIZE)
+ printk("HPFS: Grrrr... Kernel memory corrupted ... going on, but it'll crash very soon :-(\n");
+ printk("HPFS: filesystem error: ");
+ if (buf) printk("%s", buf);
+ else printk("%s\n",m);
+ if (!s->s_hpfs_was_error) {
+ if (s->s_hpfs_err == 2) {
+ printk("; crashing the system because you wanted it\n");
+ mark_dirty(s);
+ panic("HPFS panic");
+ } else if (s->s_hpfs_err == 1) {
+ if (s->s_flags & MS_RDONLY) printk("; already mounted read-only\n");
+ else {
+ printk("; remounting read-only\n");
+ mark_dirty(s);
+ s->s_flags |= MS_RDONLY;
+ }
+ } else if (s->s_flags & MS_RDONLY) printk("; going on - but anything won't be destroyed because it's read-only\n");
+ else printk("; corrupted filesystem mounted read/write - your computer will explode within 20 seconds ... but you wanted it so!\n");
+ } else printk("\n");
+ if (buf) kfree(buf);
+ s->s_hpfs_was_error = 1;
+}
+
+/*
+ * A little trick to detect cycles in many hpfs structures and don't let the
+ * kernel crash on corrupted filesystem. When first called, set c2 to 0.
+ *
+ * BTW. chkdsk doesn't detect cycles correctly. When I had 2 lost directories
+ * nested each in other, chkdsk locked up happilly.
+ */
+
+int hpfs_stop_cycles(struct super_block *s, int key, int *c1, int *c2,
+ char *msg)
+{
+ if (*c2 && *c1 == key) {
+ hpfs_error(s, "cycle detected on key %08x in %s", key, msg);
+ return 1;
+ }
+ (*c2)++;
+ if (!((*c2 - 1) & *c2)) *c1 = key;
+ return 0;
+}
+
+void hpfs_put_super(struct super_block *s)
+{
+ if (s->s_hpfs_cp_table) kfree(s->s_hpfs_cp_table);
+ if (s->s_hpfs_bmp_dir) kfree(s->s_hpfs_bmp_dir);
+ unmark_dirty(s);
+}
+
+unsigned hpfs_count_one_bitmap(struct super_block *s, secno secno)
+{
+ struct quad_buffer_head qbh;
+ unsigned *bits;
+ unsigned i, count;
+ if (!(bits = hpfs_map_4sectors(s, secno, &qbh, 4))) return 0;
+ count = 0;
+ for (i = 0; i < 2048 / sizeof(unsigned); i++) {
+ unsigned b;
+ if (!bits[i]) continue;
+ for (b = bits[i]; b; b>>=1) count += b & 1;
+ }
+ hpfs_brelse4(&qbh);
+ return count;
+}
+
+static unsigned count_bitmaps(struct super_block *s)
+{
+ unsigned n, count, n_bands;
+ n_bands = (s->s_hpfs_fs_size + 0x3fff) >> 14;
+ count = 0;
+ for (n = 0; n < n_bands; n++)
+ count += hpfs_count_one_bitmap(s, s->s_hpfs_bmp_dir[n]);
+ return count;
+}
+
+int hpfs_statfs(struct super_block *s, struct statfs *buf)
+{
+ /*if (s->s_hpfs_n_free == -1) {*/
+ s->s_hpfs_n_free = count_bitmaps(s);
+ s->s_hpfs_n_free_dnodes = hpfs_count_one_bitmap(s, s->s_hpfs_dmap);
+ /*}*/
+ buf->f_type = s->s_magic;
+ buf->f_bsize = 512;
+ buf->f_blocks = s->s_hpfs_fs_size;
+ buf->f_bfree = s->s_hpfs_n_free;
+ buf->f_bavail = s->s_hpfs_n_free;
+ buf->f_files = s->s_hpfs_dirband_size / 4;
+ buf->f_ffree = s->s_hpfs_n_free_dnodes;
+ buf->f_namelen = 254;
+ return 0;
+}
+
+/* Super operations */
+
+static struct super_operations hpfs_sops =
+{
+ read_inode: hpfs_read_inode,
+ delete_inode: hpfs_delete_inode,
+ put_super: hpfs_put_super,
+ statfs: hpfs_statfs,
+ remount_fs: hpfs_remount_fs,
+};
+
+/*
+ * A tiny parser for option strings, stolen from dosfs.
+ *
+ * Stolen again from read-only hpfs.
+ */
+
+int parse_opts(char *opts, uid_t *uid, gid_t *gid, umode_t *umask,
+ int *lowercase, int *conv, int *eas, int *chk, int *errs,
+ int *chkdsk, int *timeshift)
+{
+ char *p, *rhs;
+
+ if (!opts)
+ return 1;
+
+ /*printk("Parsing opts: '%s'\n",opts);*/
+
+ for (p = strtok(opts, ","); p != 0; p = strtok(0, ",")) {
+ if ((rhs = strchr(p, '=')) != 0)
+ *rhs++ = '\0';
+ if (!strcmp(p, "help")) return 2;
+ if (!strcmp(p, "uid")) {
+ if (!rhs || !*rhs)
+ return 0;
+ *uid = simple_strtoul(rhs, &rhs, 0);
+ if (*rhs)
+ return 0;
+ }
+ else if (!strcmp(p, "gid")) {
+ if (!rhs || !*rhs)
+ return 0;
+ *gid = simple_strtoul(rhs, &rhs, 0);
+ if (*rhs)
+ return 0;
+ }
+ else if (!strcmp(p, "umask")) {
+ if (!rhs || !*rhs)
+ return 0;
+ *umask = simple_strtoul(rhs, &rhs, 8);
+ if (*rhs)
+ return 0;
+ }
+ else if (!strcmp(p, "timeshift")) {
+ int m = 1;
+ if (!rhs || !*rhs)
+ return 0;
+ if (*rhs == '-') m = -1;
+ if (*rhs == '+' || *rhs == '-') rhs++;
+ *timeshift = simple_strtoul(rhs, &rhs, 0) * m;
+ if (*rhs)
+ return 0;
+ }
+ else if (!strcmp(p, "case")) {
+ if (!rhs || !*rhs)
+ return 0;
+ if (!strcmp(rhs, "lower"))
+ *lowercase = 1;
+ else if (!strcmp(rhs, "asis"))
+ *lowercase = 0;
+ else
+ return 0;
+ }
+ else if (!strcmp(p, "conv")) {
+ if (!rhs || !*rhs)
+ return 0;
+ if (!strcmp(rhs, "binary"))
+ *conv = CONV_BINARY;
+ else if (!strcmp(rhs, "text"))
+ *conv = CONV_TEXT;
+ else if (!strcmp(rhs, "auto"))
+ *conv = CONV_AUTO;
+ else
+ return 0;
+ }
+ else if (!strcmp(p, "check")) {
+ if (!rhs || !*rhs)
+ return 0;
+ if (!strcmp(rhs, "none"))
+ *chk = 0;
+ else if (!strcmp(rhs, "normal"))
+ *chk = 1;
+ else if (!strcmp(rhs, "strict"))
+ *chk = 2;
+ else
+ return 0;
+ }
+ else if (!strcmp(p, "errors")) {
+ if (!rhs || !*rhs)
+ return 0;
+ if (!strcmp(rhs, "continue"))
+ *errs = 0;
+ else if (!strcmp(rhs, "remount-ro"))
+ *errs = 1;
+ else if (!strcmp(rhs, "panic"))
+ *errs = 2;
+ else
+ return 0;
+ }
+ else if (!strcmp(p, "eas")) {
+ if (!rhs || !*rhs)
+ return 0;
+ if (!strcmp(rhs, "no"))
+ *eas = 0;
+ else if (!strcmp(rhs, "ro"))
+ *eas = 1;
+ else if (!strcmp(rhs, "rw"))
+ *eas = 2;
+ else
+ return 0;
+ }
+ else if (!strcmp(p, "chkdsk")) {
+ if (!rhs || !*rhs)
+ return 0;
+ if (!strcmp(rhs, "no"))
+ *chkdsk = 0;
+ else if (!strcmp(rhs, "errors"))
+ *chkdsk = 1;
+ else if (!strcmp(rhs, "always"))
+ *chkdsk = 2;
+ else
+ return 0;
+ }
+ else
+ return 0;
+ }
+ return 1;
+}
+
+static inline void hpfs_help(void)
+{
+ printk("\n\
+HPFS filesystem options:\n\
+ help do not mount and display this text\n\
+ uid=xxx set uid of files that don't have uid specified in eas\n\
+ gid=xxx set gid of files that don't have gid specified in eas\n\
+ umask=xxx set mode of files that don't have mode specified in eas\n\
+ case=lower lowercase all files\n\
+ case=asis do not lowercase files (default)\n\
+ conv=binary do not convert CR/LF -> LF (default)\n\
+ conv=auto convert only files with known text extensions\n\
+ conv=text convert all files\n\
+ check=none no fs checks - kernel may crash on corrupted filesystem\n\
+ check=normal do some checks - it should not crash (default)\n\
+ check=strict do extra time-consuming checks, used for debugging\n\
+ errors=continue continue on errors\n\
+ errors=remount-ro remount read-only if errors found (default)\n\
+ errors=panic panic on errors\n\
+ chkdsk=no do not mark fs for chkdsking even if there were errors\n\
+ chkdsk=errors mark fs dirty if errors found (default)\n\
+ chkdsk=always always mark fs dirty - used for debugging\n\
+ eas=no ignore extended attributes\n\
+ eas=ro read but do not write extended attributes\n\
+ eas=rw r/w eas => enables chmod, chown, mknod, ln -s (default)\n\
+ timeshift=nnn add nnn seconds to file times\n\
+\n");
+}
+
+int hpfs_remount_fs(struct super_block *s, int *flags, char *data)
+{
+ uid_t uid;
+ gid_t gid;
+ umode_t umask;
+ int lowercase, conv, eas, chk, errs, chkdsk, timeshift;
+ int o;
+
+ *flags |= MS_NOATIME;
+
+ uid = s->s_hpfs_uid; gid = s->s_hpfs_gid;
+ umask = 0777 & ~s->s_hpfs_mode;
+ lowercase = s->s_hpfs_lowercase; conv = s->s_hpfs_conv;
+ eas = s->s_hpfs_eas; chk = s->s_hpfs_chk; chkdsk = s->s_hpfs_chkdsk;
+ errs = s->s_hpfs_err; timeshift = s->s_hpfs_timeshift;
+
+ if (!(o = parse_opts(data, &uid, &gid, &umask, &lowercase, &conv,
+ &eas, &chk, &errs, &chkdsk, &timeshift))) {
+ printk("HPFS: bad mount options.\n");
+ return 1;
+ }
+ if (o == 2) {
+ hpfs_help();
+ return 1;
+ }
+ if (timeshift != s->s_hpfs_timeshift) {
+ printk("HPFS: timeshift can't be changed using remount.\n");
+ return 1;
+ }
+
+ unmark_dirty(s);
+
+ s->s_hpfs_uid = uid; s->s_hpfs_gid = gid;
+ s->s_hpfs_mode = 0777 & ~umask;
+ s->s_hpfs_lowercase = lowercase; s->s_hpfs_conv = conv;
+ s->s_hpfs_eas = eas; s->s_hpfs_chk = chk; s->s_hpfs_chkdsk = chkdsk;
+ s->s_hpfs_err = errs; s->s_hpfs_timeshift = timeshift;
+
+ if (!(*flags & MS_RDONLY)) mark_dirty(s);
+
+ return 0;
+}
+
+struct super_block *hpfs_read_super(struct super_block *s, void *options,
+ int silent)
+{
+ kdev_t dev;
+ struct buffer_head *bh0, *bh1, *bh2;
+ struct hpfs_boot_block *bootblock;
+ struct hpfs_super_block *superblock;
+ struct hpfs_spare_block *spareblock;
+
+ uid_t uid;
+ gid_t gid;
+ umode_t umask;
+ int lowercase, conv, eas, chk, errs, chkdsk, timeshift;
+
+ dnode_secno root_dno;
+ struct hpfs_dirent *de = NULL;
+ struct quad_buffer_head qbh;
+
+ int o;
+
+ s->s_hpfs_bmp_dir = NULL;
+ s->s_hpfs_cp_table = NULL;
+
+ s->s_hpfs_creation_de_lock = s->s_hpfs_rd_inode = 0;
+ init_waitqueue_head(&s->s_hpfs_creation_de);
+ init_waitqueue_head(&s->s_hpfs_iget_q);
+
+ uid = current->uid;
+ gid = current->gid;
+ umask = current->fs->umask;
+ lowercase = 0;
+ conv = CONV_BINARY;
+ eas = 2;
+ chk = 1;
+ errs = 1;
+ chkdsk = 1;
+ timeshift = 0;
+
+ if (!(o = parse_opts(options, &uid, &gid, &umask, &lowercase, &conv,
+ &eas, &chk, &errs, &chkdsk, &timeshift))) {
+ printk("HPFS: bad mount options.\n");
+ goto bail0;
+ }
+ if (o==2) {
+ hpfs_help();
+ goto bail0;
+ }
+
+ /*s->s_hpfs_mounting = 1;*/
+ dev = s->s_dev;
+ set_blocksize(dev, 512);
+ s->s_blocksize = 512;
+ s->s_blocksize_bits = 9;
+ s->s_hpfs_fs_size = -1;
+ if (!(bootblock = hpfs_map_sector(s, 0, &bh0, 0))) goto bail1;
+ if (!(superblock = hpfs_map_sector(s, 16, &bh1, 1))) goto bail2;
+ if (!(spareblock = hpfs_map_sector(s, 17, &bh2, 0))) goto bail3;
+
+ /* Check magics */
+ if (/*bootblock->magic != BB_MAGIC
+ ||*/ superblock->magic != SB_MAGIC
+ || spareblock->magic != SP_MAGIC) {
+ if (!silent) printk("HPFS: Bad magic ... probably not HPFS\n");
+ goto bail4;
+ }
+
+ /* Check version */
+ if (!(s->s_flags & MS_RDONLY) &&
+ superblock->funcversion != 2 && superblock->funcversion != 3) {
+ printk("HPFS: Bad version %d,%d. Mount readonly to go around\n",
+ (int)superblock->version, (int)superblock->funcversion);
+ printk("HPFS: please try recent version of HPFS driver at http://artax.karlin.mff.cuni.cz/~mikulas/vyplody/hpfs/index-e.cgi and if it still can't understand this format, contact author - mikulas@artax.karlin.mff.cuni.cz\n");
+ goto bail4;
+ }
+
+ s->s_flags |= MS_NOATIME;
+
+ /* Fill superblock stuff */
+ s->s_magic = HPFS_SUPER_MAGIC;
+ s->s_op = &hpfs_sops;
+
+ s->s_hpfs_root = superblock->root;
+ s->s_hpfs_fs_size = superblock->n_sectors;
+ s->s_hpfs_bitmaps = superblock->bitmaps;
+ s->s_hpfs_dirband_start = superblock->dir_band_start;
+ s->s_hpfs_dirband_size = superblock->n_dir_band;
+ s->s_hpfs_dmap = superblock->dir_band_bitmap;
+ s->s_hpfs_uid = uid;
+ s->s_hpfs_gid = gid;
+ s->s_hpfs_mode = 0777 & ~umask;
+ s->s_hpfs_n_free = -1;
+ s->s_hpfs_n_free_dnodes = -1;
+ s->s_hpfs_lowercase = lowercase;
+ s->s_hpfs_conv = conv;
+ s->s_hpfs_eas = eas;
+ s->s_hpfs_chk = chk;
+ s->s_hpfs_chkdsk = chkdsk;
+ s->s_hpfs_err = errs;
+ s->s_hpfs_timeshift = timeshift;
+ s->s_hpfs_was_error = 0;
+ s->s_hpfs_cp_table = NULL;
+ s->s_hpfs_c_bitmap = -1;
+ s->s_hpfs_max_fwd_alloc = 0xffffff;
+
+ /* Load bitmap directory */
+ if (!(s->s_hpfs_bmp_dir = hpfs_load_bitmap_directory(s, superblock->bitmaps)))
+ goto bail4;
+
+ /* Check for general fs errors*/
+ if (spareblock->dirty && !spareblock->old_wrote) {
+ if (errs == 2) {
+ printk("HPFS: Improperly stopped, not mounted\n");
+ goto bail4;
+ }
+ hpfs_error(s, "improperly stopped");
+ }
+
+ if (!(s->s_flags & MS_RDONLY)) {
+ spareblock->dirty = 1;
+ spareblock->old_wrote = 0;
+ mark_buffer_dirty(bh2);
+ }
+
+ if (spareblock->hotfixes_used || spareblock->n_spares_used) {
+ if (errs >= 2) {
+ printk("HPFS: Hotfixes not supported here, try chkdsk\n");
+ mark_dirty(s);
+ goto bail4;
+ }
+ hpfs_error(s, "hotfixes not supported here, try chkdsk");
+ if (errs == 0) printk("HPFS: Proceeding, but your filesystem will be probably corrupted by this driver...\n");
+ else printk("HPFS: This driver may read bad files or crash when operating on disk with hotfixes.\n");
+ }
+ if (spareblock->n_dnode_spares != spareblock->n_dnode_spares_free) {
+ if (errs >= 2) {
+ printk("HPFS: Spare dnodes used, try chkdsk\n");
+ mark_dirty(s);
+ goto bail4;
+ }
+ hpfs_error(s, "warning: spare dnodes used, try chkdsk");
+ if (errs == 0) printk("HPFS: Proceeding, but your filesystem could be corrupted if you delete files or directories\n");
+ }
+ if (chk) {
+ unsigned a;
+ if (superblock->dir_band_end - superblock->dir_band_start + 1 != superblock->n_dir_band ||
+ superblock->dir_band_end < superblock->dir_band_start || superblock->n_dir_band > 0x4000) {
+ hpfs_error(s, "dir band size mismatch: dir_band_start==%08x, dir_band_end==%08x, n_dir_band==%08x",
+ superblock->dir_band_start, superblock->dir_band_end, superblock->n_dir_band);
+ goto bail4;
+ }
+ a = s->s_hpfs_dirband_size;
+ s->s_hpfs_dirband_size = 0;
+ if (hpfs_chk_sectors(s, superblock->dir_band_start, superblock->n_dir_band, "dir_band") ||
+ hpfs_chk_sectors(s, superblock->dir_band_bitmap, 4, "dir_band_bitmap") ||
+ hpfs_chk_sectors(s, superblock->bitmaps, 4, "bitmaps")) {
+ mark_dirty(s);
+ goto bail4;
+ }
+ s->s_hpfs_dirband_size = a;
+ } else printk("HPFS: You really don't want any checks? You are crazy...\n");
+
+ /* Load code page table */
+ if (spareblock->n_code_pages)
+ if (!(s->s_hpfs_cp_table = hpfs_load_code_page(s, spareblock->code_page_dir)))
+ printk("HPFS: Warning: code page support is disabled\n");
+
+ brelse(bh2);
+ brelse(bh1);
+ brelse(bh0);
+
+ hpfs_lock_iget(s, 1);
+ s->s_root = d_alloc_root(iget(s, s->s_hpfs_root));
+ hpfs_unlock_iget(s);
+ if (!s->s_root || !s->s_root->d_inode) {
+ printk("HPFS: iget failed. Why???\n");
+ goto bail0;
+ }
+ hpfs_set_dentry_operations(s->s_root);
+
+ /*
+ * find the root directory's . pointer & finish filling in the inode
+ */
+
+ root_dno = hpfs_fnode_dno(s, s->s_hpfs_root);
+ if (root_dno)
+ de = map_dirent(s->s_root->d_inode, root_dno, "\001\001", 2, NULL, &qbh);
+ if (!root_dno || !de) hpfs_error(s, "unable to find root dir");
+ else {
+ s->s_root->d_inode->i_atime = local_to_gmt(s, de->read_date);
+ s->s_root->d_inode->i_mtime = local_to_gmt(s, de->write_date);
+ s->s_root->d_inode->i_ctime = local_to_gmt(s, de->creation_date);
+ s->s_root->d_inode->i_hpfs_ea_size = de->ea_size;
+ s->s_root->d_inode->i_hpfs_parent_dir = s->s_root->d_inode->i_ino;
+ if (s->s_root->d_inode->i_size == -1) s->s_root->d_inode->i_size = 2048;
+ if (s->s_root->d_inode->i_blocks == -1) s->s_root->d_inode->i_blocks = 5;
+ }
+ if (de) hpfs_brelse4(&qbh);
+
+ return s;
+
+bail4: brelse(bh2);
+bail3: brelse(bh1);
+bail2: brelse(bh0);
+bail1:
+bail0:
+ if (s->s_hpfs_bmp_dir) kfree(s->s_hpfs_bmp_dir);
+ if (s->s_hpfs_cp_table) kfree(s->s_hpfs_cp_table);
+ return NULL;
+}
+
+DECLARE_FSTYPE_DEV(hpfs_fs_type, "hpfs", hpfs_read_super);
+
+static int __init init_hpfs_fs(void)
+{
+ return register_filesystem(&hpfs_fs_type);
+}
+
+static void __exit exit_hpfs_fs(void)
+{
+ unregister_filesystem(&hpfs_fs_type);
+}
+
+EXPORT_NO_SYMBOLS;
+
+module_init(init_hpfs_fs)
+module_exit(exit_hpfs_fs)
+MODULE_LICENSE("GPL");
diff --git a/fs/inode.c b/fs/inode.c
new file mode 100644
index 00000000000000..22c49f2e024421
--- /dev/null
+++ b/fs/inode.c
@@ -0,0 +1,1444 @@
+/*
+ * linux/fs/inode.c
+ *
+ * (C) 1997 Linus Torvalds
+ */
+
+#include <linux/config.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/dcache.h>
+#include <linux/init.h>
+#include <linux/quotaops.h>
+#include <linux/slab.h>
+#include <linux/cache.h>
+#include <linux/swap.h>
+#include <linux/swapctl.h>
+#include <linux/prefetch.h>
+#include <linux/locks.h>
+
+/*
+ * New inode.c implementation.
+ *
+ * This implementation has the basic premise of trying
+ * to be extremely low-overhead and SMP-safe, yet be
+ * simple enough to be "obviously correct".
+ *
+ * Famous last words.
+ */
+
+/* inode dynamic allocation 1999, Andrea Arcangeli <andrea@suse.de> */
+
+/* #define INODE_PARANOIA 1 */
+/* #define INODE_DEBUG 1 */
+
+/*
+ * Inode lookup is no longer as critical as it used to be:
+ * most of the lookups are going to be through the dcache.
+ */
+#define I_HASHBITS i_hash_shift
+#define I_HASHMASK i_hash_mask
+
+static unsigned int i_hash_mask;
+static unsigned int i_hash_shift;
+
+/*
+ * Each inode can be on two separate lists. One is
+ * the hash list of the inode, used for lookups. The
+ * other linked list is the "type" list:
+ * "in_use" - valid inode, i_count > 0, i_nlink > 0
+ * "dirty" - as "in_use" but also dirty
+ * "unused" - valid inode, i_count = 0, no pages in the pagecache
+ * "unused_pagecache" - valid inode, i_count = 0, data in the pagecache
+ *
+ * A "dirty" list is maintained for each super block,
+ * allowing for low-overhead inode sync() operations.
+ */
+
+static LIST_HEAD(inode_in_use);
+static LIST_HEAD(inode_unused);
+static LIST_HEAD(inode_unused_pagecache);
+static struct list_head *inode_hashtable;
+static LIST_HEAD(anon_hash_chain); /* for inodes with NULL i_sb */
+
+/*
+ * A simple spinlock to protect the list manipulations.
+ *
+ * NOTE! You also have to own the lock if you change
+ * the i_state of an inode while it is in use..
+ */
+static spinlock_t inode_lock = SPIN_LOCK_UNLOCKED;
+
+/*
+ * Statistics gathering..
+ */
+struct inodes_stat_t inodes_stat;
+
+static kmem_cache_t * inode_cachep;
+
+static struct inode *alloc_inode(struct super_block *sb)
+{
+ static struct address_space_operations empty_aops;
+ static struct inode_operations empty_iops;
+ static struct file_operations empty_fops;
+ struct inode *inode;
+
+ if (sb->s_op->alloc_inode)
+ inode = sb->s_op->alloc_inode(sb);
+ else {
+ inode = (struct inode *) kmem_cache_alloc(inode_cachep, SLAB_KERNEL);
+ /* will die */
+ if (inode)
+ memset(&inode->u, 0, sizeof(inode->u));
+ }
+
+ if (inode) {
+ struct address_space * const mapping = &inode->i_data;
+
+ inode->i_sb = sb;
+ inode->i_dev = sb->s_dev;
+ inode->i_blkbits = sb->s_blocksize_bits;
+ inode->i_flags = 0;
+ atomic_set(&inode->i_count, 1);
+ inode->i_sock = 0;
+ inode->i_op = &empty_iops;
+ inode->i_fop = &empty_fops;
+ inode->i_nlink = 1;
+ atomic_set(&inode->i_writecount, 0);
+ inode->i_size = 0;
+ inode->i_blocks = 0;
+ inode->i_bytes = 0;
+ inode->i_generation = 0;
+ memset(&inode->i_dquot, 0, sizeof(inode->i_dquot));
+ inode->i_pipe = NULL;
+ inode->i_bdev = NULL;
+ inode->i_cdev = NULL;
+
+ mapping->a_ops = &empty_aops;
+ mapping->host = inode;
+ mapping->gfp_mask = GFP_HIGHUSER;
+ inode->i_mapping = mapping;
+ }
+ return inode;
+}
+
+static void destroy_inode(struct inode *inode)
+{
+ if (inode_has_buffers(inode))
+ BUG();
+ /* Reinitialise the waitqueue head because __wait_on_freeing_inode()
+ may have left stale entries on it which it can't remove (since
+ it knows we're freeing the inode right now */
+ init_waitqueue_head(&inode->i_wait);
+ if (inode->i_sb->s_op->destroy_inode)
+ inode->i_sb->s_op->destroy_inode(inode);
+ else
+ kmem_cache_free(inode_cachep, inode);
+}
+
+
+/*
+ * These are initializations that only need to be done
+ * once, because the fields are idempotent across use
+ * of the inode, so let the slab aware of that.
+ */
+void inode_init_once(struct inode *inode)
+{
+ memset(inode, 0, sizeof(*inode));
+ __inode_init_once(inode);
+}
+
+void __inode_init_once(struct inode *inode)
+{
+ init_waitqueue_head(&inode->i_wait);
+ INIT_LIST_HEAD(&inode->i_hash);
+ INIT_LIST_HEAD(&inode->i_data.clean_pages);
+ INIT_LIST_HEAD(&inode->i_data.dirty_pages);
+ INIT_LIST_HEAD(&inode->i_data.locked_pages);
+ INIT_LIST_HEAD(&inode->i_dentry);
+ INIT_LIST_HEAD(&inode->i_dirty_buffers);
+ INIT_LIST_HEAD(&inode->i_dirty_data_buffers);
+ INIT_LIST_HEAD(&inode->i_devices);
+ sema_init(&inode->i_sem, 1);
+ sema_init(&inode->i_zombie, 1);
+ init_rwsem(&inode->i_alloc_sem);
+ spin_lock_init(&inode->i_data.i_shared_lock);
+}
+
+static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
+{
+ struct inode * inode = (struct inode *) foo;
+
+ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+ SLAB_CTOR_CONSTRUCTOR)
+ inode_init_once(inode);
+}
+
+/*
+ * Put the inode on the super block's dirty list.
+ *
+ * CAREFUL! We mark it dirty unconditionally, but
+ * move it onto the dirty list only if it is hashed.
+ * If it was not hashed, it will never be added to
+ * the dirty list even if it is later hashed, as it
+ * will have been marked dirty already.
+ *
+ * In short, make sure you hash any inodes _before_
+ * you start marking them dirty..
+ */
+
+/**
+ * __mark_inode_dirty - internal function
+ * @inode: inode to mark
+ * @flags: what kind of dirty (i.e. I_DIRTY_SYNC)
+ * Mark an inode as dirty. Callers should use mark_inode_dirty or
+ * mark_inode_dirty_sync.
+ */
+
+void __mark_inode_dirty(struct inode *inode, int flags)
+{
+ struct super_block * sb = inode->i_sb;
+
+ if (!sb)
+ return;
+
+ /* Don't do this for I_DIRTY_PAGES - that doesn't actually dirty the inode itself */
+ if (flags & (I_DIRTY_SYNC | I_DIRTY_DATASYNC)) {
+ if (sb->s_op && sb->s_op->dirty_inode)
+ sb->s_op->dirty_inode(inode);
+ }
+
+ /* avoid the locking if we can */
+ if ((inode->i_state & flags) == flags)
+ return;
+
+ spin_lock(&inode_lock);
+ if ((inode->i_state & flags) != flags) {
+ inode->i_state |= flags;
+ /* Only add valid (ie hashed) inodes to the dirty list */
+ if (!(inode->i_state & (I_LOCK|I_FREEING|I_CLEAR)) &&
+ !list_empty(&inode->i_hash)) {
+ list_del(&inode->i_list);
+ list_add(&inode->i_list, &sb->s_dirty);
+ }
+ }
+ spin_unlock(&inode_lock);
+}
+
+static void __wait_on_inode(struct inode * inode)
+{
+ DECLARE_WAITQUEUE(wait, current);
+
+ add_wait_queue(&inode->i_wait, &wait);
+repeat:
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ if (inode->i_state & I_LOCK) {
+ schedule();
+ goto repeat;
+ }
+ remove_wait_queue(&inode->i_wait, &wait);
+ current->state = TASK_RUNNING;
+}
+
+static inline void wait_on_inode(struct inode *inode)
+{
+ if (inode->i_state & I_LOCK)
+ __wait_on_inode(inode);
+}
+
+/*
+ * If we try to find an inode in the inode hash while it is being deleted, we
+ * have to wait until the filesystem completes its deletion before reporting
+ * that it isn't found. This is because iget will immediately call
+ * ->read_inode, and we want to be sure that evidence of the deletion is found
+ * by ->read_inode.
+ *
+ * Unlike the 2.6 version, this call call cannot return early, since inodes
+ * do not share wait queue. Therefore, we don't call remove_wait_queue(); it
+ * would be dangerous to do so since the inode may have already been freed,
+ * and it's unnecessary, since the inode is definitely going to get freed.
+ *
+ * This is called with inode_lock held.
+ */
+static void __wait_on_freeing_inode(struct inode *inode)
+{
+ DECLARE_WAITQUEUE(wait, current);
+
+ add_wait_queue(&inode->i_wait, &wait);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ spin_unlock(&inode_lock);
+ schedule();
+
+ spin_lock(&inode_lock);
+}
+
+static inline void write_inode(struct inode *inode, int sync)
+{
+ if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->write_inode && !is_bad_inode(inode))
+ inode->i_sb->s_op->write_inode(inode, sync);
+}
+
+static inline void __iget(struct inode * inode)
+{
+ if (atomic_read(&inode->i_count)) {
+ atomic_inc(&inode->i_count);
+ return;
+ }
+ atomic_inc(&inode->i_count);
+ if (!(inode->i_state & (I_DIRTY|I_LOCK))) {
+ list_del(&inode->i_list);
+ list_add(&inode->i_list, &inode_in_use);
+ }
+ inodes_stat.nr_unused--;
+}
+
+static inline void __refile_inode(struct inode *inode)
+{
+ struct list_head *to;
+
+ if (inode->i_state & I_FREEING)
+ return;
+ if (list_empty(&inode->i_hash))
+ return;
+
+ if (inode->i_state & I_DIRTY)
+ to = &inode->i_sb->s_dirty;
+ else if (atomic_read(&inode->i_count))
+ to = &inode_in_use;
+ else if (inode->i_data.nrpages)
+ to = &inode_unused_pagecache;
+ else
+ to = &inode_unused;
+ list_del(&inode->i_list);
+ list_add(&inode->i_list, to);
+}
+
+void refile_inode(struct inode *inode)
+{
+ if (!inode)
+ return;
+ spin_lock(&inode_lock);
+ if (!(inode->i_state & I_LOCK))
+ __refile_inode(inode);
+ spin_unlock(&inode_lock);
+}
+
+static inline void __sync_one(struct inode *inode, int sync)
+{
+ unsigned dirty;
+
+ list_del(&inode->i_list);
+ list_add(&inode->i_list, &inode->i_sb->s_locked_inodes);
+
+ if (inode->i_state & (I_LOCK|I_FREEING))
+ BUG();
+
+ /* Set I_LOCK, reset I_DIRTY */
+ dirty = inode->i_state & I_DIRTY;
+ inode->i_state |= I_LOCK;
+ inode->i_state &= ~I_DIRTY;
+ spin_unlock(&inode_lock);
+
+ filemap_fdatasync(inode->i_mapping);
+
+ /* Don't write the inode if only I_DIRTY_PAGES was set */
+ if (dirty & (I_DIRTY_SYNC | I_DIRTY_DATASYNC))
+ write_inode(inode, sync);
+
+ filemap_fdatawait(inode->i_mapping);
+
+ spin_lock(&inode_lock);
+ inode->i_state &= ~I_LOCK;
+ __refile_inode(inode);
+ wake_up(&inode->i_wait);
+}
+
+static inline void sync_one(struct inode *inode, int sync)
+{
+ while (inode->i_state & I_LOCK) {
+ __iget(inode);
+ spin_unlock(&inode_lock);
+ __wait_on_inode(inode);
+ iput(inode);
+ spin_lock(&inode_lock);
+ }
+
+ __sync_one(inode, sync);
+}
+
+static inline void sync_list(struct list_head *head)
+{
+ struct list_head * tmp;
+
+ while ((tmp = head->prev) != head)
+ __sync_one(list_entry(tmp, struct inode, i_list), 0);
+}
+
+static inline void wait_on_locked(struct list_head *head)
+{
+ struct list_head * tmp;
+ while ((tmp = head->prev) != head) {
+ struct inode *inode = list_entry(tmp, struct inode, i_list);
+ __iget(inode);
+ spin_unlock(&inode_lock);
+ __wait_on_inode(inode);
+ iput(inode);
+ spin_lock(&inode_lock);
+ }
+}
+
+static inline int try_to_sync_unused_list(struct list_head *head, int nr_inodes)
+{
+ struct list_head *tmp = head;
+ struct inode *inode;
+
+ while (nr_inodes && (tmp = tmp->prev) != head) {
+ inode = list_entry(tmp, struct inode, i_list);
+
+ if (!atomic_read(&inode->i_count)) {
+ __sync_one(inode, 0);
+ nr_inodes--;
+
+ /*
+ * __sync_one moved the inode to another list,
+ * so we have to start looking from the list head.
+ */
+ tmp = head;
+ }
+ }
+
+ return nr_inodes;
+}
+
+void sync_inodes_sb(struct super_block *sb)
+{
+ spin_lock(&inode_lock);
+ while (!list_empty(&sb->s_dirty)||!list_empty(&sb->s_locked_inodes)) {
+ sync_list(&sb->s_dirty);
+ wait_on_locked(&sb->s_locked_inodes);
+ }
+ spin_unlock(&inode_lock);
+}
+
+/*
+ * Note:
+ * We don't need to grab a reference to superblock here. If it has non-empty
+ * ->s_dirty it's hadn't been killed yet and kill_super() won't proceed
+ * past sync_inodes_sb() until both ->s_dirty and ->s_locked_inodes are
+ * empty. Since __sync_one() regains inode_lock before it finally moves
+ * inode from superblock lists we are OK.
+ */
+
+void sync_unlocked_inodes(void)
+{
+ struct super_block * sb;
+ spin_lock(&inode_lock);
+ spin_lock(&sb_lock);
+ sb = sb_entry(super_blocks.next);
+ for (; sb != sb_entry(&super_blocks); sb = sb_entry(sb->s_list.next)) {
+ if (!list_empty(&sb->s_dirty)) {
+ spin_unlock(&sb_lock);
+ sync_list(&sb->s_dirty);
+ spin_lock(&sb_lock);
+ }
+ }
+ spin_unlock(&sb_lock);
+ spin_unlock(&inode_lock);
+}
+
+/*
+ * Find a superblock with inodes that need to be synced
+ */
+
+static struct super_block *get_super_to_sync(void)
+{
+ struct list_head *p;
+restart:
+ spin_lock(&inode_lock);
+ spin_lock(&sb_lock);
+ list_for_each(p, &super_blocks) {
+ struct super_block *s = list_entry(p,struct super_block,s_list);
+ if (list_empty(&s->s_dirty) && list_empty(&s->s_locked_inodes))
+ continue;
+ s->s_count++;
+ spin_unlock(&sb_lock);
+ spin_unlock(&inode_lock);
+ down_read(&s->s_umount);
+ if (!s->s_root) {
+ drop_super(s);
+ goto restart;
+ }
+ return s;
+ }
+ spin_unlock(&sb_lock);
+ spin_unlock(&inode_lock);
+ return NULL;
+}
+
+/**
+ * sync_inodes
+ * @dev: device to sync the inodes from.
+ *
+ * sync_inodes goes through the super block's dirty list,
+ * writes them out, and puts them back on the normal list.
+ */
+
+void sync_inodes(kdev_t dev)
+{
+ struct super_block * s;
+
+ /*
+ * Search the super_blocks array for the device(s) to sync.
+ */
+ if (dev) {
+ if ((s = get_super(dev)) != NULL) {
+ sync_inodes_sb(s);
+ drop_super(s);
+ }
+ } else {
+ while ((s = get_super_to_sync()) != NULL) {
+ sync_inodes_sb(s);
+ drop_super(s);
+ }
+ }
+}
+
+static void try_to_sync_unused_inodes(void * arg)
+{
+ struct super_block * sb;
+ int nr_inodes = inodes_stat.nr_unused;
+
+ spin_lock(&inode_lock);
+ spin_lock(&sb_lock);
+ sb = sb_entry(super_blocks.next);
+ for (; nr_inodes && sb != sb_entry(&super_blocks); sb = sb_entry(sb->s_list.next)) {
+ if (list_empty(&sb->s_dirty))
+ continue;
+ spin_unlock(&sb_lock);
+ nr_inodes = try_to_sync_unused_list(&sb->s_dirty, nr_inodes);
+ spin_lock(&sb_lock);
+ }
+ spin_unlock(&sb_lock);
+ spin_unlock(&inode_lock);
+}
+
+static struct tq_struct unused_inodes_flush_task;
+
+/**
+ * write_inode_now - write an inode to disk
+ * @inode: inode to write to disk
+ * @sync: whether the write should be synchronous or not
+ *
+ * This function commits an inode to disk immediately if it is
+ * dirty. This is primarily needed by knfsd.
+ */
+
+void write_inode_now(struct inode *inode, int sync)
+{
+ struct super_block * sb = inode->i_sb;
+
+ if (sb) {
+ spin_lock(&inode_lock);
+ while (inode->i_state & I_DIRTY)
+ sync_one(inode, sync);
+ spin_unlock(&inode_lock);
+ if (sync)
+ wait_on_inode(inode);
+ }
+ else
+ printk(KERN_ERR "write_inode_now: no super block\n");
+}
+
+/**
+ * generic_osync_inode - flush all dirty data for a given inode to disk
+ * @inode: inode to write
+ * @datasync: if set, don't bother flushing timestamps
+ *
+ * This can be called by file_write functions for files which have the
+ * O_SYNC flag set, to flush dirty writes to disk.
+ */
+
+int generic_osync_inode(struct inode *inode, int what)
+{
+ int err = 0, err2 = 0, need_write_inode_now = 0;
+
+ /*
+ * WARNING
+ *
+ * Currently, the filesystem write path does not pass the
+ * filp down to the low-level write functions. Therefore it
+ * is impossible for (say) __block_commit_write to know if
+ * the operation is O_SYNC or not.
+ *
+ * Ideally, O_SYNC writes would have the filesystem call
+ * ll_rw_block as it went to kick-start the writes, and we
+ * could call osync_inode_buffers() here to wait only for
+ * those IOs which have already been submitted to the device
+ * driver layer. As it stands, if we did this we'd not write
+ * anything to disk since our writes have not been queued by
+ * this point: they are still on the dirty LRU.
+ *
+ * So, currently we will call fsync_inode_buffers() instead,
+ * to flush _all_ dirty buffers for this inode to disk on
+ * every O_SYNC write, not just the synchronous I/Os. --sct
+ */
+
+ if (what & OSYNC_METADATA)
+ err = fsync_inode_buffers(inode);
+ if (what & OSYNC_DATA)
+ err2 = fsync_inode_data_buffers(inode);
+ if (!err)
+ err = err2;
+
+ spin_lock(&inode_lock);
+ if ((inode->i_state & I_DIRTY) &&
+ ((what & OSYNC_INODE) || (inode->i_state & I_DIRTY_DATASYNC)))
+ need_write_inode_now = 1;
+ spin_unlock(&inode_lock);
+
+ if (need_write_inode_now)
+ write_inode_now(inode, 1);
+ else
+ wait_on_inode(inode);
+
+ return err;
+}
+
+/**
+ * clear_inode - clear an inode
+ * @inode: inode to clear
+ *
+ * This is called by the filesystem to tell us
+ * that the inode is no longer useful. We just
+ * terminate it with extreme prejudice.
+ */
+
+void clear_inode(struct inode *inode)
+{
+ invalidate_inode_buffers(inode);
+
+ if (inode->i_data.nrpages)
+ BUG();
+ if (!(inode->i_state & I_FREEING))
+ BUG();
+ if (inode->i_state & I_CLEAR)
+ BUG();
+ wait_on_inode(inode);
+ DQUOT_DROP(inode);
+ if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->clear_inode)
+ inode->i_sb->s_op->clear_inode(inode);
+ if (inode->i_bdev)
+ bd_forget(inode);
+ else if (inode->i_cdev) {
+ cdput(inode->i_cdev);
+ inode->i_cdev = NULL;
+ }
+ inode->i_state = I_CLEAR;
+}
+
+/*
+ * Dispose-list gets a local list with local inodes in it, so it doesn't
+ * need to worry about list corruption and SMP locks.
+ */
+static void dispose_list(struct list_head *head)
+{
+ int nr_disposed = 0;
+
+ while (!list_empty(head)) {
+ struct inode *inode;
+
+ inode = list_entry(head->next, struct inode, i_list);
+ list_del(&inode->i_list);
+
+ if (inode->i_data.nrpages)
+ truncate_inode_pages(&inode->i_data, 0);
+ clear_inode(inode);
+ spin_lock(&inode_lock);
+ list_del(&inode->i_hash);
+ INIT_LIST_HEAD(&inode->i_hash);
+ spin_unlock(&inode_lock);
+ wake_up(&inode->i_wait);
+ destroy_inode(inode);
+ nr_disposed++;
+ }
+ spin_lock(&inode_lock);
+ inodes_stat.nr_inodes -= nr_disposed;
+ spin_unlock(&inode_lock);
+}
+
+/*
+ * Invalidate all inodes for a device.
+ */
+static int invalidate_list(struct list_head *head, struct super_block * sb, struct list_head * dispose)
+{
+ struct list_head *next;
+ int busy = 0, count = 0;
+
+ next = head->next;
+ for (;;) {
+ struct list_head * tmp = next;
+ struct inode * inode;
+
+ next = next->next;
+ if (tmp == head)
+ break;
+ inode = list_entry(tmp, struct inode, i_list);
+ if (inode->i_sb != sb)
+ continue;
+ invalidate_inode_buffers(inode);
+ if (!atomic_read(&inode->i_count)) {
+ list_del_init(&inode->i_hash);
+ list_del(&inode->i_list);
+ list_add(&inode->i_list, dispose);
+ inode->i_state |= I_FREEING;
+ count++;
+ continue;
+ }
+ busy = 1;
+ }
+ /* only unused inodes may be cached with i_count zero */
+ inodes_stat.nr_unused -= count;
+ return busy;
+}
+
+/*
+ * This is a two-stage process. First we collect all
+ * offending inodes onto the throw-away list, and in
+ * the second stage we actually dispose of them. This
+ * is because we don't want to sleep while messing
+ * with the global lists..
+ */
+
+/**
+ * invalidate_inodes - discard the inodes on a device
+ * @sb: superblock
+ *
+ * Discard all of the inodes for a given superblock. If the discard
+ * fails because there are busy inodes then a non zero value is returned.
+ * If the discard is successful all the inodes have been discarded.
+ */
+
+int invalidate_inodes(struct super_block * sb)
+{
+ int busy;
+ LIST_HEAD(throw_away);
+
+ spin_lock(&inode_lock);
+ busy = invalidate_list(&inode_in_use, sb, &throw_away);
+ busy |= invalidate_list(&inode_unused, sb, &throw_away);
+ busy |= invalidate_list(&inode_unused_pagecache, sb, &throw_away);
+ busy |= invalidate_list(&sb->s_dirty, sb, &throw_away);
+ busy |= invalidate_list(&sb->s_locked_inodes, sb, &throw_away);
+ spin_unlock(&inode_lock);
+
+ dispose_list(&throw_away);
+
+ return busy;
+}
+
+int invalidate_device(kdev_t dev, int do_sync)
+{
+ struct super_block *sb;
+ int res;
+
+ if (do_sync)
+ fsync_dev(dev);
+
+ res = 0;
+ sb = get_super(dev);
+ if (sb) {
+ /*
+ * no need to lock the super, get_super holds the
+ * read semaphore so the filesystem cannot go away
+ * under us (->put_super runs with the write lock
+ * hold).
+ */
+ shrink_dcache_sb(sb);
+ res = invalidate_inodes(sb);
+ drop_super(sb);
+ }
+ invalidate_buffers(dev);
+ return res;
+}
+
+
+/*
+ * This is called with the inode lock held. It searches
+ * the in-use for freeable inodes, which are moved to a
+ * temporary list and then placed on the unused list by
+ * dispose_list.
+ *
+ * We don't expect to have to call this very often.
+ *
+ * We leave the inode in the inode hash table until *after*
+ * the filesystem's ->delete_inode (in dispose_list) completes.
+ * This ensures that an iget (such as nfsd might instigate) will
+ * always find up-to-date information either in the hash or on disk.
+ *
+ * I_FREEING is set so that no-one will take a new reference
+ * to the inode while it is being deleted.
+ *
+ * N.B. The spinlock is released during the call to
+ * dispose_list.
+ */
+#define CAN_UNUSE(inode) \
+ ((((inode)->i_state | (inode)->i_data.nrpages) == 0) && \
+ !inode_has_buffers(inode))
+#define INODE(entry) (list_entry(entry, struct inode, i_list))
+
+void prune_icache(int goal)
+{
+ LIST_HEAD(list);
+ struct list_head *entry, *freeable = &list;
+ int count;
+#ifdef CONFIG_HIGHMEM
+ int avg_pages;
+#endif
+ struct inode * inode;
+
+ spin_lock(&inode_lock);
+
+ count = 0;
+ entry = inode_unused.prev;
+ while (entry != &inode_unused)
+ {
+ struct list_head *tmp = entry;
+
+ entry = entry->prev;
+ inode = INODE(tmp);
+ if (inode->i_state & (I_FREEING|I_CLEAR|I_LOCK))
+ continue;
+ if (!CAN_UNUSE(inode))
+ continue;
+ if (atomic_read(&inode->i_count))
+ continue;
+ list_del(tmp);
+ list_add(tmp, freeable);
+ inode->i_state |= I_FREEING;
+ count++;
+ if (--goal <= 0)
+ break;
+ }
+ inodes_stat.nr_unused -= count;
+ spin_unlock(&inode_lock);
+
+ dispose_list(freeable);
+
+ /*
+ * If we didn't freed enough clean inodes schedule
+ * a sync of the dirty inodes, we cannot do it
+ * from here or we're either synchronously dogslow
+ * or we deadlock with oom.
+ */
+ if (goal > 0)
+ schedule_task(&unused_inodes_flush_task);
+
+#ifdef CONFIG_HIGHMEM
+ /*
+ * On highmem machines it is possible to have low memory
+ * filled with inodes that cannot be reclaimed because they
+ * have page cache pages in highmem attached to them.
+ * This could deadlock the system if the memory used by
+ * inodes is significant compared to the amount of freeable
+ * low memory. In that case we forcefully remove the page
+ * cache pages from the inodes we want to reclaim.
+ *
+ * Note that this loop doesn't actually reclaim the inodes;
+ * once the last pagecache pages belonging to the inode is
+ * gone it will be placed on the inode_unused list and the
+ * loop above will prune it the next time prune_icache() is
+ * called.
+ */
+ if (goal <= 0)
+ return;
+ if (inodes_stat.nr_unused * sizeof(struct inode) * 10 <
+ freeable_lowmem() * PAGE_SIZE)
+ return;
+
+ wakeup_bdflush();
+
+ avg_pages = page_cache_size;
+ avg_pages -= atomic_read(&buffermem_pages) + swapper_space.nrpages;
+ avg_pages = avg_pages / (inodes_stat.nr_inodes + 1);
+ spin_lock(&inode_lock);
+ while (goal-- > 0) {
+ if (list_empty(&inode_unused_pagecache))
+ break;
+ entry = inode_unused_pagecache.prev;
+ list_del(entry);
+ list_add(entry, &inode_unused_pagecache);
+
+ inode = INODE(entry);
+ /* Don't nuke inodes with lots of page cache attached. */
+ if (inode->i_mapping->nrpages > 5 * avg_pages)
+ continue;
+ /* Because of locking we grab the inode and unlock the list .*/
+ if (inode->i_state & I_LOCK)
+ continue;
+ inode->i_state |= I_LOCK;
+ spin_unlock(&inode_lock);
+
+ /*
+ * If the inode has clean pages only, we can free all its
+ * pagecache memory; the inode will automagically be refiled
+ * onto the unused_list. The wakeup_bdflush above makes
+ * sure that all inodes become clean eventually.
+ */
+ if (list_empty(&inode->i_mapping->dirty_pages) &&
+ !inode_has_buffers(inode))
+ invalidate_inode_pages(inode);
+
+ /* Release the inode again. */
+ spin_lock(&inode_lock);
+ inode->i_state &= ~I_LOCK;
+ wake_up(&inode->i_wait);
+ }
+ spin_unlock(&inode_lock);
+#endif /* CONFIG_HIGHMEM */
+}
+
+int shrink_icache_memory(int priority, int gfp_mask)
+{
+ int count = 0;
+
+ /*
+ * Nasty deadlock avoidance..
+ *
+ * We may hold various FS locks, and we don't
+ * want to recurse into the FS that called us
+ * in clear_inode() and friends..
+ */
+ if (!(gfp_mask & __GFP_FS))
+ return 0;
+
+ count = inodes_stat.nr_unused / priority;
+
+ prune_icache(count);
+ return kmem_cache_shrink(inode_cachep);
+}
+
+/*
+ * Called with the inode lock held.
+ * NOTE: we are not increasing the inode-refcount, you must call __iget()
+ * by hand after calling find_inode now! This simplifies iunique and won't
+ * add any additional branch in the common code.
+ */
+static struct inode * find_inode(struct super_block * sb, unsigned long ino, struct list_head *head, find_inode_t find_actor, void *opaque)
+{
+ struct list_head *tmp;
+ struct inode * inode;
+
+repeat:
+ tmp = head;
+ for (;;) {
+ tmp = tmp->next;
+ inode = NULL;
+ if (tmp == head)
+ break;
+ inode = list_entry(tmp, struct inode, i_hash);
+ if (inode->i_ino != ino)
+ continue;
+ if (inode->i_sb != sb)
+ continue;
+ if (find_actor && !find_actor(inode, ino, opaque))
+ continue;
+ if (inode->i_state & (I_FREEING|I_CLEAR)) {
+ __wait_on_freeing_inode(inode);
+ goto repeat;
+ }
+ break;
+ }
+ return inode;
+}
+
+/**
+ * new_inode - obtain an inode
+ * @sb: superblock
+ *
+ * Allocates a new inode for given superblock.
+ */
+
+struct inode * new_inode(struct super_block *sb)
+{
+ static unsigned long last_ino;
+ struct inode * inode;
+
+ spin_lock_prefetch(&inode_lock);
+
+ inode = alloc_inode(sb);
+ if (inode) {
+ spin_lock(&inode_lock);
+ inodes_stat.nr_inodes++;
+ list_add(&inode->i_list, &inode_in_use);
+ inode->i_ino = ++last_ino;
+ inode->i_state = 0;
+ spin_unlock(&inode_lock);
+ }
+ return inode;
+}
+
+void unlock_new_inode(struct inode *inode)
+{
+ /*
+ * This is special! We do not need the spinlock
+ * when clearing I_LOCK, because we're guaranteed
+ * that nobody else tries to do anything about the
+ * state of the inode when it is locked, as we
+ * just created it (so there can be no old holders
+ * that haven't tested I_LOCK).
+ */
+ inode->i_state &= ~(I_LOCK|I_NEW);
+ wake_up(&inode->i_wait);
+}
+
+/*
+ * This is called without the inode lock held.. Be careful.
+ *
+ * We no longer cache the sb_flags in i_flags - see fs.h
+ * -- rmk@arm.uk.linux.org
+ */
+static struct inode * get_new_inode(struct super_block *sb, unsigned long ino, struct list_head *head, find_inode_t find_actor, void *opaque)
+{
+ struct inode * inode;
+
+ inode = alloc_inode(sb);
+ if (inode) {
+ struct inode * old;
+
+ spin_lock(&inode_lock);
+ /* We released the lock, so.. */
+ old = find_inode(sb, ino, head, find_actor, opaque);
+ if (!old) {
+ inodes_stat.nr_inodes++;
+ list_add(&inode->i_list, &inode_in_use);
+ list_add(&inode->i_hash, head);
+ inode->i_ino = ino;
+ inode->i_state = I_LOCK|I_NEW;
+ spin_unlock(&inode_lock);
+
+ /*
+ * Return the locked inode with I_NEW set, the
+ * caller is responsible for filling in the contents
+ */
+ return inode;
+ }
+
+ /*
+ * Uhhuh, somebody else created the same inode under
+ * us. Use the old inode instead of the one we just
+ * allocated.
+ */
+ __iget(old);
+ spin_unlock(&inode_lock);
+ destroy_inode(inode);
+ inode = old;
+ wait_on_inode(inode);
+ }
+ return inode;
+}
+
+static inline unsigned long hash(struct super_block *sb, unsigned long i_ino)
+{
+ unsigned long tmp = i_ino + ((unsigned long) sb / L1_CACHE_BYTES);
+ tmp = tmp + (tmp >> I_HASHBITS);
+ return tmp & I_HASHMASK;
+}
+
+/* Yeah, I know about quadratic hash. Maybe, later. */
+
+/**
+ * iunique - get a unique inode number
+ * @sb: superblock
+ * @max_reserved: highest reserved inode number
+ *
+ * Obtain an inode number that is unique on the system for a given
+ * superblock. This is used by file systems that have no natural
+ * permanent inode numbering system. An inode number is returned that
+ * is higher than the reserved limit but unique.
+ *
+ * BUGS:
+ * With a large number of inodes live on the file system this function
+ * currently becomes quite slow.
+ */
+
+ino_t iunique(struct super_block *sb, ino_t max_reserved)
+{
+ static ino_t counter = 0;
+ struct inode *inode;
+ struct list_head * head;
+ ino_t res;
+ spin_lock(&inode_lock);
+retry:
+ if (counter > max_reserved) {
+ head = inode_hashtable + hash(sb,counter);
+ inode = find_inode(sb, res = counter++, head, NULL, NULL);
+ if (!inode) {
+ spin_unlock(&inode_lock);
+ return res;
+ }
+ } else {
+ counter = max_reserved + 1;
+ }
+ goto retry;
+
+}
+
+/**
+ * ilookup - search for an inode in the inode cache
+ * @sb: super block of file system to search
+ * @ino: inode number to search for
+ *
+ * If the inode is in the cache, the inode is returned with an
+ * incremented reference count.
+ *
+ * Otherwise, %NULL is returned.
+ *
+ * This is almost certainly not the function you are looking for.
+ * If you think you need to use this, consult an expert first.
+ */
+struct inode *ilookup(struct super_block *sb, unsigned long ino)
+{
+ struct list_head * head = inode_hashtable + hash(sb,ino);
+ struct inode * inode;
+
+ spin_lock(&inode_lock);
+ inode = find_inode(sb, ino, head, NULL, NULL);
+ if (inode) {
+ __iget(inode);
+ spin_unlock(&inode_lock);
+ wait_on_inode(inode);
+ return inode;
+ }
+ spin_unlock(&inode_lock);
+
+ return inode;
+}
+
+struct inode *igrab(struct inode *inode)
+{
+ spin_lock(&inode_lock);
+ if (!(inode->i_state & I_FREEING))
+ __iget(inode);
+ else
+ /*
+ * Handle the case where s_op->clear_inode is not been
+ * called yet, and somebody is calling igrab
+ * while the inode is getting freed.
+ */
+ inode = NULL;
+ spin_unlock(&inode_lock);
+ return inode;
+}
+
+struct inode *iget4_locked(struct super_block *sb, unsigned long ino, find_inode_t find_actor, void *opaque)
+{
+ struct list_head * head = inode_hashtable + hash(sb,ino);
+ struct inode * inode;
+
+ spin_lock(&inode_lock);
+ inode = find_inode(sb, ino, head, find_actor, opaque);
+ if (inode) {
+ __iget(inode);
+ spin_unlock(&inode_lock);
+ wait_on_inode(inode);
+ return inode;
+ }
+ spin_unlock(&inode_lock);
+
+ /*
+ * get_new_inode() will do the right thing, re-trying the search
+ * in case it had to block at any point.
+ */
+ return get_new_inode(sb, ino, head, find_actor, opaque);
+}
+
+/**
+ * insert_inode_hash - hash an inode
+ * @inode: unhashed inode
+ *
+ * Add an inode to the inode hash for this superblock. If the inode
+ * has no superblock it is added to a separate anonymous chain.
+ */
+
+void insert_inode_hash(struct inode *inode)
+{
+ struct list_head *head = &anon_hash_chain;
+ if (inode->i_sb)
+ head = inode_hashtable + hash(inode->i_sb, inode->i_ino);
+ spin_lock(&inode_lock);
+ list_add(&inode->i_hash, head);
+ spin_unlock(&inode_lock);
+}
+
+/**
+ * remove_inode_hash - remove an inode from the hash
+ * @inode: inode to unhash
+ *
+ * Remove an inode from the superblock or anonymous hash.
+ */
+
+void remove_inode_hash(struct inode *inode)
+{
+ spin_lock(&inode_lock);
+ list_del(&inode->i_hash);
+ INIT_LIST_HEAD(&inode->i_hash);
+ spin_unlock(&inode_lock);
+}
+
+/**
+ * iput - put an inode
+ * @inode: inode to put
+ *
+ * Puts an inode, dropping its usage count. If the inode use count hits
+ * zero the inode is also then freed and may be destroyed.
+ */
+
+void iput(struct inode *inode)
+{
+ if (inode) {
+ struct super_block *sb = inode->i_sb;
+ struct super_operations *op = NULL;
+
+ if (inode->i_state == I_CLEAR)
+ BUG();
+
+ if (sb && sb->s_op)
+ op = sb->s_op;
+ if (op && op->put_inode)
+ op->put_inode(inode);
+
+ if (!atomic_dec_and_lock(&inode->i_count, &inode_lock))
+ return;
+
+ if (!inode->i_nlink) {
+ list_del(&inode->i_list);
+ INIT_LIST_HEAD(&inode->i_list);
+ inode->i_state|=I_FREEING;
+ inodes_stat.nr_inodes--;
+ spin_unlock(&inode_lock);
+
+ if (inode->i_data.nrpages)
+ truncate_inode_pages(&inode->i_data, 0);
+
+ if (op && op->delete_inode) {
+ void (*delete)(struct inode *) = op->delete_inode;
+ if (!is_bad_inode(inode))
+ DQUOT_INIT(inode);
+ /* s_op->delete_inode internally recalls clear_inode() */
+ delete(inode);
+ } else
+ clear_inode(inode);
+ spin_lock(&inode_lock);
+ list_del(&inode->i_hash);
+ INIT_LIST_HEAD(&inode->i_hash);
+ spin_unlock(&inode_lock);
+ wake_up(&inode->i_wait);
+ if (inode->i_state != I_CLEAR)
+ BUG();
+ } else {
+ if (!list_empty(&inode->i_hash)) {
+ if (!(inode->i_state & (I_DIRTY|I_LOCK)))
+ __refile_inode(inode);
+ inodes_stat.nr_unused++;
+ spin_unlock(&inode_lock);
+ if (!sb || (sb->s_flags & MS_ACTIVE))
+ return;
+ write_inode_now(inode, 1);
+ spin_lock(&inode_lock);
+ inodes_stat.nr_unused--;
+ list_del_init(&inode->i_hash);
+ }
+ list_del_init(&inode->i_list);
+ inode->i_state|=I_FREEING;
+ inodes_stat.nr_inodes--;
+ spin_unlock(&inode_lock);
+ if (inode->i_data.nrpages)
+ truncate_inode_pages(&inode->i_data, 0);
+ clear_inode(inode);
+ }
+ destroy_inode(inode);
+ }
+}
+
+void force_delete(struct inode *inode)
+{
+ /*
+ * Kill off unused inodes ... iput() will unhash and
+ * delete the inode if we set i_nlink to zero.
+ */
+ if (atomic_read(&inode->i_count) == 1)
+ inode->i_nlink = 0;
+}
+
+/**
+ * bmap - find a block number in a file
+ * @inode: inode of file
+ * @block: block to find
+ *
+ * Returns the block number on the device holding the inode that
+ * is the disk block number for the block of the file requested.
+ * That is, asked for block 4 of inode 1 the function will return the
+ * disk block relative to the disk start that holds that block of the
+ * file.
+ */
+
+int bmap(struct inode * inode, int block)
+{
+ int res = 0;
+ if (inode->i_mapping->a_ops->bmap)
+ res = inode->i_mapping->a_ops->bmap(inode->i_mapping, block);
+ return res;
+}
+
+/*
+ * Initialize the hash tables.
+ */
+void __init inode_init(unsigned long mempages)
+{
+ struct list_head *head;
+ unsigned long order;
+ unsigned int nr_hash;
+ int i;
+
+ mempages >>= (14 - PAGE_SHIFT);
+ mempages *= sizeof(struct list_head);
+ for (order = 0; ((1UL << order) << PAGE_SHIFT) < mempages; order++)
+ ;
+
+ do {
+ unsigned long tmp;
+
+ nr_hash = (1UL << order) * PAGE_SIZE /
+ sizeof(struct list_head);
+ i_hash_mask = (nr_hash - 1);
+
+ tmp = nr_hash;
+ i_hash_shift = 0;
+ while ((tmp >>= 1UL) != 0UL)
+ i_hash_shift++;
+
+ inode_hashtable = (struct list_head *)
+ __get_free_pages(GFP_ATOMIC, order);
+ } while (inode_hashtable == NULL && --order >= 0);
+
+ printk(KERN_INFO "Inode cache hash table entries: %d (order: %ld, %ld bytes)\n",
+ nr_hash, order, (PAGE_SIZE << order));
+
+ if (!inode_hashtable)
+ panic("Failed to allocate inode hash table\n");
+
+ head = inode_hashtable;
+ i = nr_hash;
+ do {
+ INIT_LIST_HEAD(head);
+ head++;
+ i--;
+ } while (i);
+
+ /* inode slab cache */
+ inode_cachep = kmem_cache_create("inode_cache", sizeof(struct inode),
+ 0, SLAB_HWCACHE_ALIGN, init_once,
+ NULL);
+ if (!inode_cachep)
+ panic("cannot create inode slab cache");
+
+ unused_inodes_flush_task.routine = try_to_sync_unused_inodes;
+}
+
+/**
+ * update_atime - update the access time
+ * @inode: inode accessed
+ *
+ * Update the accessed time on an inode and mark it for writeback.
+ * This function automatically handles read only file systems and media,
+ * as well as the "noatime" flag and inode specific "noatime" markers.
+ */
+
+void update_atime (struct inode *inode)
+{
+ if (inode->i_atime == CURRENT_TIME)
+ return;
+ if (IS_NOATIME(inode))
+ return;
+ if (IS_NODIRATIME(inode) && S_ISDIR(inode->i_mode))
+ return;
+ if (IS_RDONLY(inode))
+ return;
+ inode->i_atime = CURRENT_TIME;
+ mark_inode_dirty_sync (inode);
+}
+
+/**
+ * update_mctime - update the mtime and ctime
+ * @inode: inode accessed
+ *
+ * Update the modified and changed times on an inode for writes to special
+ * files such as fifos. No change is forced if the timestamps are already
+ * up-to-date or if the filesystem is readonly.
+ */
+
+void update_mctime (struct inode *inode)
+{
+ if (inode->i_mtime == CURRENT_TIME && inode->i_ctime == CURRENT_TIME)
+ return;
+ if (IS_RDONLY(inode))
+ return;
+ inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+ mark_inode_dirty (inode);
+}
+
+
+/*
+ * Quota functions that want to walk the inode lists..
+ */
+#ifdef CONFIG_QUOTA
+
+/* Functions back in dquot.c */
+void put_dquot_list(struct list_head *);
+int remove_inode_dquot_ref(struct inode *, short, struct list_head *);
+
+void remove_dquot_ref(struct super_block *sb, short type)
+{
+ struct inode *inode;
+ struct list_head *act_head;
+ LIST_HEAD(tofree_head);
+
+ if (!sb->dq_op)
+ return; /* nothing to do */
+ /* We have to be protected against other CPUs */
+ lock_kernel(); /* This lock is for quota code */
+ spin_lock(&inode_lock); /* This lock is for inodes code */
+
+ list_for_each(act_head, &inode_in_use) {
+ inode = list_entry(act_head, struct inode, i_list);
+ if (inode->i_sb == sb && IS_QUOTAINIT(inode))
+ remove_inode_dquot_ref(inode, type, &tofree_head);
+ }
+ list_for_each(act_head, &inode_unused) {
+ inode = list_entry(act_head, struct inode, i_list);
+ if (inode->i_sb == sb && IS_QUOTAINIT(inode))
+ remove_inode_dquot_ref(inode, type, &tofree_head);
+ }
+ list_for_each(act_head, &inode_unused_pagecache) {
+ inode = list_entry(act_head, struct inode, i_list);
+ if (inode->i_sb == sb && IS_QUOTAINIT(inode))
+ remove_inode_dquot_ref(inode, type, &tofree_head);
+ }
+ list_for_each(act_head, &sb->s_dirty) {
+ inode = list_entry(act_head, struct inode, i_list);
+ if (IS_QUOTAINIT(inode))
+ remove_inode_dquot_ref(inode, type, &tofree_head);
+ }
+ list_for_each(act_head, &sb->s_locked_inodes) {
+ inode = list_entry(act_head, struct inode, i_list);
+ if (IS_QUOTAINIT(inode))
+ remove_inode_dquot_ref(inode, type, &tofree_head);
+ }
+ spin_unlock(&inode_lock);
+ unlock_kernel();
+
+ put_dquot_list(&tofree_head);
+}
+
+#endif
diff --git a/fs/intermezzo/Makefile b/fs/intermezzo/Makefile
new file mode 100644
index 00000000000000..80679e33ad2458
--- /dev/null
+++ b/fs/intermezzo/Makefile
@@ -0,0 +1,15 @@
+#
+# Makefile 1.00 Peter Braam <braam@clusterfs.com>
+#
+
+O_TARGET := intermezzo.o
+
+obj-y := cache.o dcache.o dir.o ext_attr.o file.o fileset.o inode.o \
+ journal.o journal_ext2.o journal_ext3.o journal_obdfs.o \
+ journal_reiserfs.o journal_tmpfs.o journal_xfs.o kml_reint.o \
+ kml_unpack.o methods.o presto.o psdev.o replicator.o super.o \
+ sysctl.o upcall.o vfs.o
+
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/intermezzo/cache.c b/fs/intermezzo/cache.c
new file mode 100644
index 00000000000000..97f190fa4086cb
--- /dev/null
+++ b/fs/intermezzo/cache.c
@@ -0,0 +1,215 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Copyright (C) 2000 Stelias Computing, Inc.
+ * Copyright (C) 2000 Red Hat, Inc.
+ *
+ * This file is part of InterMezzo, http://www.inter-mezzo.org.
+ *
+ * InterMezzo is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * InterMezzo 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 InterMezzo; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <stdarg.h>
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/ext2_fs.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/blkdev.h>
+#include <linux/init.h>
+
+#include <linux/intermezzo_fs.h>
+#include <linux/intermezzo_psdev.h>
+
+/*
+ This file contains the routines associated with managing a
+ cache of files for InterMezzo. These caches have two reqs:
+ - need to be found fast so they are hashed by the device,
+ with an attempt to have collision chains of length 1.
+ The methods for the cache are set up in methods.
+*/
+
+extern kmem_cache_t * presto_dentry_slab;
+
+/* the intent of this hash is to have collision chains of length 1 */
+#define CACHES_BITS 8
+#define CACHES_SIZE (1 << CACHES_BITS)
+#define CACHES_MASK CACHES_SIZE - 1
+static struct list_head presto_caches[CACHES_SIZE];
+
+static inline int presto_cache_hash(kdev_t dev)
+{
+ return (CACHES_MASK) & ((0x000F & (dev)) + ((0x0F00 & (dev)) >>8));
+}
+
+inline void presto_cache_add(struct presto_cache *cache, kdev_t dev)
+{
+ list_add(&cache->cache_chain,
+ &presto_caches[presto_cache_hash(dev)]);
+ cache->cache_dev = dev;
+}
+
+inline void presto_cache_init_hash(void)
+{
+ int i;
+ for ( i = 0; i < CACHES_SIZE; i++ ) {
+ INIT_LIST_HEAD(&presto_caches[i]);
+ }
+}
+
+int izo_ioctl_packlen(struct izo_ioctl_data *data)
+{
+ int len = sizeof(struct izo_ioctl_data);
+ len += size_round(data->ioc_inllen1);
+ len += size_round(data->ioc_inllen2);
+ return len;
+}
+
+/* map a device to a cache */
+struct presto_cache *presto_cache_find(kdev_t dev)
+{
+ struct presto_cache *cache;
+ struct list_head *lh, *tmp;
+
+ lh = tmp = &(presto_caches[presto_cache_hash(dev)]);
+ while ( (tmp = lh->next) != lh ) {
+ cache = list_entry(tmp, struct presto_cache, cache_chain);
+ if ( cache->cache_dev == dev ) {
+ return cache;
+ }
+ }
+ return NULL;
+}
+
+
+/* map an inode to a cache */
+struct presto_cache *presto_get_cache(struct inode *inode)
+{
+ struct presto_cache *cache;
+ ENTRY;
+ /* find the correct presto_cache here, based on the device */
+ cache = presto_cache_find(inode->i_dev);
+ if ( !cache ) {
+ CERROR("WARNING: no presto cache for dev %x, ino %ld\n",
+ inode->i_dev, inode->i_ino);
+ EXIT;
+ return NULL;
+ }
+ EXIT;
+ return cache;
+}
+
+/* another debugging routine: check fs is InterMezzo fs */
+int presto_ispresto(struct inode *inode)
+{
+ struct presto_cache *cache;
+
+ if ( !inode )
+ return 0;
+ cache = presto_get_cache(inode);
+ if ( !cache )
+ return 0;
+ return (inode->i_dev == cache->cache_dev);
+}
+
+/* setup a cache structure when we need one */
+struct presto_cache *presto_cache_init(void)
+{
+ struct presto_cache *cache;
+
+ PRESTO_ALLOC(cache, sizeof(struct presto_cache));
+ if ( cache ) {
+ memset(cache, 0, sizeof(struct presto_cache));
+ INIT_LIST_HEAD(&cache->cache_chain);
+ INIT_LIST_HEAD(&cache->cache_fset_list);
+ cache->cache_lock = SPIN_LOCK_UNLOCKED;
+ cache->cache_reserved = 0;
+ }
+ return cache;
+}
+
+/* free a cache structure and all of the memory it is pointing to */
+inline void presto_free_cache(struct presto_cache *cache)
+{
+ if (!cache)
+ return;
+
+ list_del(&cache->cache_chain);
+ if (cache->cache_sb && cache->cache_sb->s_root &&
+ presto_d2d(cache->cache_sb->s_root)) {
+ kmem_cache_free(presto_dentry_slab,
+ presto_d2d(cache->cache_sb->s_root));
+ cache->cache_sb->s_root->d_fsdata = NULL;
+ }
+
+ if (cache->cache_type)
+ PRESTO_FREE(cache->cache_type, strlen(cache->cache_type) + 1 );
+
+ PRESTO_FREE(cache, sizeof(struct presto_cache));
+}
+
+int presto_reserve_space(struct presto_cache *cache, loff_t req)
+{
+ struct filter_fs *filter;
+ loff_t avail;
+ struct super_block *sb = cache->cache_sb;
+ filter = cache->cache_filter;
+ if (!filter ) {
+ EXIT;
+ return 0;
+ }
+ if (!filter->o_trops ) {
+ EXIT;
+ return 0;
+ }
+ if (!filter->o_trops->tr_avail ) {
+ EXIT;
+ return 0;
+ }
+
+ spin_lock(&cache->cache_lock);
+ avail = filter->o_trops->tr_avail(cache, sb);
+ CDEBUG(D_SUPER, "ESC::%ld +++> %ld \n", (long) cache->cache_reserved,
+ (long) (cache->cache_reserved + req));
+ CDEBUG(D_SUPER, "ESC::Avail::%ld \n", (long) avail);
+ if (req + cache->cache_reserved > avail) {
+ spin_unlock(&cache->cache_lock);
+ EXIT;
+ return -ENOSPC;
+ }
+ cache->cache_reserved += req;
+ spin_unlock(&cache->cache_lock);
+
+ EXIT;
+ return 0;
+}
+
+void presto_release_space(struct presto_cache *cache, loff_t req)
+{
+ CDEBUG(D_SUPER, "ESC::%ld ---> %ld \n", (long) cache->cache_reserved,
+ (long) (cache->cache_reserved - req));
+ spin_lock(&cache->cache_lock);
+ cache->cache_reserved -= req;
+ spin_unlock(&cache->cache_lock);
+}
diff --git a/fs/intermezzo/dcache.c b/fs/intermezzo/dcache.c
new file mode 100644
index 00000000000000..12184aefeb235d
--- /dev/null
+++ b/fs/intermezzo/dcache.c
@@ -0,0 +1,350 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Original version: Copyright (C) 1996 P. Braam and M. Callahan
+ * Rewritten for Linux 2.1. Copyright (C) 1997 Carnegie Mellon University
+ * d_fsdata and NFS compatiblity fixes Copyright (C) 2001 Tacit Networks, Inc.
+ *
+ * This file is part of InterMezzo, http://www.inter-mezzo.org.
+ *
+ * InterMezzo is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * InterMezzo 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 InterMezzo; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Directory operations for InterMezzo filesystem
+ */
+
+/* inode dentry alias list walking code adapted from linux/fs/dcache.c
+ *
+ * fs/dcache.c
+ *
+ * (C) 1997 Thomas Schoebel-Theuer,
+ * with heavy changes by Linus Torvalds
+ */
+
+#define __NO_VERSION__
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <linux/slab.h>
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+#include <linux/string.h>
+#include <linux/smp_lock.h>
+#include <linux/vmalloc.h>
+
+#include <linux/intermezzo_fs.h>
+
+kmem_cache_t * presto_dentry_slab;
+
+/* called when a cache lookup succeeds */
+static int presto_d_revalidate(struct dentry *de, int flag)
+{
+ struct inode *inode = de->d_inode;
+ struct presto_file_set * root_fset;
+
+ ENTRY;
+ if (!inode) {
+ EXIT;
+ return 0;
+ }
+
+ if (is_bad_inode(inode)) {
+ EXIT;
+ return 0;
+ }
+
+ if (!presto_d2d(de)) {
+ presto_set_dd(de);
+ }
+
+ if (!presto_d2d(de)) {
+ EXIT;
+ return 0;
+ }
+
+ root_fset = presto_d2d(de->d_inode->i_sb->s_root)->dd_fset;
+ if (root_fset->fset_flags & FSET_FLAT_BRANCH &&
+ (presto_d2d(de)->dd_fset != root_fset )) {
+ presto_d2d(de)->dd_fset = root_fset;
+ }
+
+ EXIT;
+ return 1;
+
+#if 0
+ /* The following is needed for metadata on demand. */
+ if ( S_ISDIR(inode->i_mode) ) {
+ EXIT;
+ return (presto_chk(de, PRESTO_DATA) &&
+ (presto_chk(de, PRESTO_ATTR)));
+ } else {
+ EXIT;
+ return presto_chk(de, PRESTO_ATTR);
+ }
+#endif
+}
+
+static void presto_d_release(struct dentry *dentry)
+{
+ if (!presto_d2d(dentry)) {
+ /* This can happen for dentries from NFSd */
+ return;
+ }
+ presto_d2d(dentry)->dd_count--;
+
+ if (!presto_d2d(dentry)->dd_count) {
+ kmem_cache_free(presto_dentry_slab, presto_d2d(dentry));
+ dentry->d_fsdata = NULL;
+ }
+}
+
+struct dentry_operations presto_dentry_ops =
+{
+ .d_revalidate = presto_d_revalidate,
+ .d_release = presto_d_release
+};
+
+static inline int presto_is_dentry_ROOT (struct dentry *dentry)
+{
+ return(dentry_name_cmp(dentry,"ROOT") &&
+ !dentry_name_cmp(dentry->d_parent,".intermezzo"));
+}
+
+static struct presto_file_set* presto_try_find_fset(struct dentry* dentry,
+ int *is_under_d_intermezzo)
+{
+ struct dentry* temp_dentry;
+ struct presto_dentry_data *d_data;
+ int found_root=0;
+
+ ENTRY;
+ CDEBUG(D_FSDATA, "finding fileset for %p:%s\n", dentry,
+ dentry->d_name.name);
+
+ *is_under_d_intermezzo = 0;
+
+ /* walk up through the branch to get the fileset */
+ /* The dentry we are passed presumably does not have the correct
+ * fset information. However, we still want to start walking up
+ * the branch from this dentry to get our found_root and
+ * is_under_d_intermezzo decisions correct
+ */
+ for (temp_dentry = dentry ; ; temp_dentry = temp_dentry->d_parent) {
+ CDEBUG(D_FSDATA, "--->dentry %p:%*s\n", temp_dentry,
+ temp_dentry->d_name.len,temp_dentry->d_name.name);
+ if (presto_is_dentry_ROOT(temp_dentry))
+ found_root = 1;
+ if (!found_root &&
+ dentry_name_cmp(temp_dentry, ".intermezzo")) {
+ *is_under_d_intermezzo = 1;
+ }
+ d_data = presto_d2d(temp_dentry);
+ if (d_data) {
+ /* If we found a "ROOT" dentry while walking up the
+ * branch, we will journal regardless of whether
+ * we are under .intermezzo or not.
+ * If we are already under d_intermezzo don't reverse
+ * the decision here...even if we found a "ROOT"
+ * dentry above .intermezzo (if we were ever to
+ * modify the directory structure).
+ */
+ if (!*is_under_d_intermezzo)
+ *is_under_d_intermezzo = !found_root &&
+ (d_data->dd_flags & PRESTO_DONT_JOURNAL);
+ EXIT;
+ return d_data->dd_fset;
+ }
+ if (temp_dentry->d_parent == temp_dentry) {
+ break;
+ }
+ }
+ EXIT;
+ return NULL;
+}
+
+/* Only call this function on positive dentries */
+static struct presto_dentry_data* presto_try_find_alias_with_dd (
+ struct dentry* dentry)
+{
+ struct inode *inode=dentry->d_inode;
+ struct list_head *head, *next, *tmp;
+ struct dentry *tmp_dentry;
+
+ /* Search through the alias list for dentries with d_fsdata */
+ spin_lock(&dcache_lock);
+ head = &inode->i_dentry;
+ next = inode->i_dentry.next;
+ while (next != head) {
+ tmp = next;
+ next = tmp->next;
+ tmp_dentry = list_entry(tmp, struct dentry, d_alias);
+ if (!presto_d2d(tmp_dentry)) {
+ spin_unlock(&dcache_lock);
+ return presto_d2d(tmp_dentry);
+ }
+ }
+ spin_unlock(&dcache_lock);
+ return NULL;
+}
+
+/* Only call this function on positive dentries */
+static void presto_set_alias_dd (struct dentry *dentry,
+ struct presto_dentry_data* dd)
+{
+ struct inode *inode=dentry->d_inode;
+ struct list_head *head, *next, *tmp;
+ struct dentry *tmp_dentry;
+
+ /* Set d_fsdata for this dentry */
+ dd->dd_count++;
+ dentry->d_fsdata = dd;
+
+ /* Now set d_fsdata for all dentries in the alias list. */
+ spin_lock(&dcache_lock);
+ head = &inode->i_dentry;
+ next = inode->i_dentry.next;
+ while (next != head) {
+ tmp = next;
+ next = tmp->next;
+ tmp_dentry = list_entry(tmp, struct dentry, d_alias);
+ if (!presto_d2d(tmp_dentry)) {
+ dd->dd_count++;
+ tmp_dentry->d_fsdata = dd;
+ }
+ }
+ spin_unlock(&dcache_lock);
+ return;
+}
+
+inline struct presto_dentry_data *izo_alloc_ddata(void)
+{
+ struct presto_dentry_data *dd;
+
+ dd = kmem_cache_alloc(presto_dentry_slab, SLAB_KERNEL);
+ if (dd == NULL) {
+ CERROR("IZO: out of memory trying to allocate presto_dentry_data\n");
+ return NULL;
+ }
+ memset(dd, 0, sizeof(*dd));
+ dd->dd_count = 1;
+
+ return dd;
+}
+
+/* This uses the BKL! */
+int presto_set_dd(struct dentry * dentry)
+{
+ struct presto_file_set *fset;
+ struct presto_dentry_data *dd;
+ int is_under_d_izo;
+ int error=0;
+
+ ENTRY;
+
+ if (!dentry)
+ BUG();
+
+ lock_kernel();
+
+ /* Did we lose a race? */
+ if (dentry->d_fsdata) {
+ CERROR("dentry %p already has d_fsdata set\n", dentry);
+ if (dentry->d_inode)
+ CERROR(" inode: %ld\n", dentry->d_inode->i_ino);
+ EXIT;
+ goto out_unlock;
+ }
+
+ if (dentry->d_inode != NULL) {
+ /* NFSd runs find_fh_dentry which instantiates disconnected
+ * dentries which are then connected without a lookup().
+ * So it is possible to have connected dentries that do not
+ * have d_fsdata set. So we walk the list trying to find
+ * an alias which has its d_fsdata set and then use that
+ * for all the other dentries as well.
+ * - SHP,Vinny.
+ */
+
+ /* If there is an alias with d_fsdata use it. */
+ if ((dd = presto_try_find_alias_with_dd (dentry))) {
+ presto_set_alias_dd (dentry, dd);
+ EXIT;
+ goto out_unlock;
+ }
+ } else {
+ /* Negative dentry */
+ CDEBUG(D_FSDATA,"negative dentry %p: %*s\n", dentry,
+ dentry->d_name.len, dentry->d_name.name);
+ }
+
+ /* No pre-existing d_fsdata, we need to construct one.
+ * First, we must walk up the tree to find the fileset
+ * If a fileset can't be found, we leave a null fsdata
+ * and return EROFS to indicate that we can't journal
+ * updates.
+ */
+ fset = presto_try_find_fset (dentry, &is_under_d_izo);
+ if (!fset) {
+#ifdef PRESTO_NO_NFS
+ CERROR("No fileset for dentry %p: %*s\n", dentry,
+ dentry->d_name.len, dentry->d_name.name);
+#endif
+ error = -EROFS;
+ EXIT;
+ goto out_unlock;
+ }
+
+ dentry->d_fsdata = izo_alloc_ddata();
+ if (!presto_d2d(dentry)) {
+ CERROR ("InterMezzo: out of memory allocating d_fsdata\n");
+ error = -ENOMEM;
+ goto out_unlock;
+ }
+ presto_d2d(dentry)->dd_fset = fset;
+ if (is_under_d_izo)
+ presto_d2d(dentry)->dd_flags |= PRESTO_DONT_JOURNAL;
+ EXIT;
+
+out_unlock:
+ CDEBUG(D_FSDATA,"presto_set_dd dentry %p: %*s, d_fsdata %p\n",
+ dentry, dentry->d_name.len, dentry->d_name.name,
+ dentry->d_fsdata);
+ unlock_kernel();
+
+ filter_setup_dentry_ops(fset->fset_cache->cache_filter,
+ dentry->d_op, &presto_dentry_ops);
+ dentry->d_op = filter_c2udops(fset->fset_cache->cache_filter);
+
+ return error;
+}
+
+int presto_init_ddata_cache(void)
+{
+ ENTRY;
+ presto_dentry_slab =
+ kmem_cache_create("presto_cache",
+ sizeof(struct presto_dentry_data), 0,
+ SLAB_HWCACHE_ALIGN, NULL,
+ NULL);
+ EXIT;
+ return (presto_dentry_slab != NULL);
+}
+
+void presto_cleanup_ddata_cache(void)
+{
+ kmem_cache_destroy(presto_dentry_slab);
+}
diff --git a/fs/intermezzo/dir.c b/fs/intermezzo/dir.c
new file mode 100644
index 00000000000000..b22c4b9fb014f9
--- /dev/null
+++ b/fs/intermezzo/dir.c
@@ -0,0 +1,1412 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Copyright (C) 2000 Stelias Computing, Inc.
+ * Copyright (C) 2000 Red Hat, Inc.
+ * Copyright (C) 2000 Tacitus Systems
+ * Copyright (C) 2000 Peter J. Braam
+ *
+ * This file is part of InterMezzo, http://www.inter-mezzo.org.
+ *
+ * InterMezzo is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * InterMezzo 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 InterMezzo; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdarg.h>
+
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/smp_lock.h>
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/ext2_fs.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/blkdev.h>
+#include <linux/init.h>
+#define __NO_VERSION__
+#include <linux/module.h>
+
+#include <linux/intermezzo_fs.h>
+#include <linux/intermezzo_psdev.h>
+
+static inline void presto_relock_sem(struct inode *dir)
+{
+ /* the lock from sys_mkdir / lookup_create */
+ down(&dir->i_sem);
+ /* the rest is done by the do_{create,mkdir, ...} */
+}
+
+static inline void presto_relock_other(struct inode *dir)
+{
+ /* vfs_mkdir locks */
+ down(&dir->i_zombie);
+ lock_kernel();
+}
+
+static inline void presto_fulllock(struct inode *dir)
+{
+ /* the lock from sys_mkdir / lookup_create */
+ down(&dir->i_sem);
+ /* vfs_mkdir locks */
+ down(&dir->i_zombie);
+ lock_kernel();
+}
+
+static inline void presto_unlock(struct inode *dir)
+{
+ /* vfs_mkdir locks */
+ unlock_kernel();
+ up(&dir->i_zombie);
+ /* the lock from sys_mkdir / lookup_create */
+ up(&dir->i_sem);
+}
+
+
+/*
+ * these are initialized in super.c
+ */
+extern int presto_permission(struct inode *inode, int mask);
+static int izo_authorized_uid = 0;
+
+int izo_dentry_is_ilookup(struct dentry *dentry, ino_t *id,
+ unsigned int *generation)
+{
+ char tmpname[64];
+ char *next;
+
+ ENTRY;
+ /* prefix is 7 characters: '...ino:' */
+ if ( dentry->d_name.len < 7 || dentry->d_name.len > 64 ||
+ memcmp(dentry->d_name.name, PRESTO_ILOOKUP_MAGIC, 7) != 0 ) {
+ EXIT;
+ return 0;
+ }
+
+ memcpy(tmpname, dentry->d_name.name + 7, dentry->d_name.len - 7);
+ *(tmpname + dentry->d_name.len - 7) = '\0';
+
+ /* name is of the form ...ino:<inode number>:<generation> */
+ *id = simple_strtoul(tmpname, &next, 16);
+ if ( *next == PRESTO_ILOOKUP_SEP ) {
+ *generation = simple_strtoul(next + 1, 0, 16);
+ CDEBUG(D_INODE, "ino string: %s, Id = %lx (%lu), "
+ "generation %x (%d)\n",
+ tmpname, *id, *id, *generation, *generation);
+ EXIT;
+ return 1;
+ } else {
+ EXIT;
+ return 0;
+ }
+}
+
+struct dentry *presto_tmpfs_ilookup(struct inode *dir,
+ struct dentry *dentry,
+ ino_t ino,
+ unsigned int generation)
+{
+ return dentry;
+}
+
+
+inline int presto_can_ilookup(void)
+{
+ return (current->euid == izo_authorized_uid ||
+ capable(CAP_DAC_READ_SEARCH));
+}
+
+struct dentry *presto_iget_ilookup(struct inode *dir,
+ struct dentry *dentry,
+ ino_t ino,
+ unsigned int generation)
+{
+ struct inode *inode;
+ int error;
+
+ ENTRY;
+
+ if ( !presto_can_ilookup() ) {
+ CERROR("ilookup denied: euid %u, authorized_uid %u\n",
+ current->euid, izo_authorized_uid);
+ return ERR_PTR(-EPERM);
+ }
+ error = -ENOENT;
+ inode = iget(dir->i_sb, ino);
+ if (!inode) {
+ CERROR("fatal: NULL inode ino %lu\n", ino);
+ goto cleanup_iput;
+ }
+ if (is_bad_inode(inode) || inode->i_nlink == 0) {
+ CERROR("fatal: bad inode ino %lu, links %d\n", ino, inode->i_nlink);
+ goto cleanup_iput;
+ }
+ if (inode->i_generation != generation) {
+ CERROR("fatal: bad generation %u (want %u)\n",
+ inode->i_generation, generation);
+ goto cleanup_iput;
+ }
+
+ d_instantiate(dentry, inode);
+ dentry->d_flags |= DCACHE_NFSD_DISCONNECTED; /* NFS hack */
+
+ EXIT;
+ return NULL;
+
+cleanup_iput:
+ if (inode)
+ iput(inode);
+ return ERR_PTR(error);
+}
+
+struct dentry *presto_add_ilookup_dentry(struct dentry *parent,
+ struct dentry *real)
+{
+ struct inode *inode = real->d_inode;
+ struct dentry *de;
+ char buf[32];
+ char *ptr = buf;
+ struct dentry *inodir;
+ struct presto_dentry_data *dd;
+
+ inodir = lookup_one_len("..iopen..", parent, strlen("..iopen.."));
+ if (!inodir || IS_ERR(inodir) || !inodir->d_inode ) {
+ CERROR("%s: bad ..iopen.. lookup\n", __FUNCTION__);
+ return NULL;
+ }
+ inodir->d_inode->i_op = &presto_dir_iops;
+
+ snprintf(ptr, 32, "...ino:%lx:%x", inode->i_ino, inode->i_generation);
+
+ de = lookup_one_len(ptr, inodir, strlen(ptr));
+ if (!de || IS_ERR(de)) {
+ CERROR("%s: bad ...ino lookup %ld\n",
+ __FUNCTION__, PTR_ERR(de));
+ dput(inodir);
+ return NULL;
+ }
+
+ dd = presto_d2d(real);
+ if (!dd)
+ BUG();
+
+ /* already exists */
+ if (de->d_inode)
+ BUG();
+#if 0
+ if (de->d_inode != inode ) {
+ CERROR("XX de->d_inode %ld, inode %ld\n",
+ de->d_inode->i_ino, inode->i_ino);
+ BUG();
+ }
+ if (dd->dd_inodentry) {
+ CERROR("inodentry exists %ld \n", inode->i_ino);
+ BUG();
+ }
+ dput(inodir);
+ return de;
+ }
+#endif
+
+ if (presto_d2d(de))
+ BUG();
+
+ atomic_inc(&inode->i_count);
+ de->d_op = &presto_dentry_ops;
+ d_add(de, inode);
+ if (!de->d_op)
+ CERROR("DD: no ops dentry %p, dd %p\n", de, dd);
+ dd->dd_inodentry = de;
+ dd->dd_count++;
+ de->d_fsdata = dd;
+
+ dput(inodir);
+ return de;
+}
+
+struct dentry *presto_lookup(struct inode * dir, struct dentry *dentry)
+{
+ int rc = 0;
+ struct dentry *de;
+ struct presto_cache *cache;
+ int minor;
+ ino_t ino;
+ unsigned int generation;
+ struct inode_operations *iops;
+ int is_ilookup = 0;
+
+ ENTRY;
+ cache = presto_get_cache(dir);
+ if (cache == NULL) {
+ CERROR("InterMezzo BUG: no cache in presto_lookup "
+ "(dir ino: %ld)!\n", dir->i_ino);
+ EXIT;
+ return NULL;
+ }
+ minor = presto_c2m(cache);
+
+ iops = filter_c2cdiops(cache->cache_filter);
+ if (!iops || !iops->lookup) {
+ CERROR("InterMezzo BUG: filesystem has no lookup\n");
+ EXIT;
+ return NULL;
+ }
+
+
+ CDEBUG(D_CACHE, "dentry %p, dir ino: %ld, name: %*s, islento: %d\n",
+ dentry, dir->i_ino, dentry->d_name.len, dentry->d_name.name,
+ ISLENTO(minor));
+
+ if (dentry->d_fsdata)
+ CERROR("DD -- BAD dentry %p has data\n", dentry);
+
+ dentry->d_fsdata = NULL;
+#if 0
+ if (ext2_check_for_iopen(dir, dentry))
+ de = NULL;
+ else {
+#endif
+ if ( izo_dentry_is_ilookup(dentry, &ino, &generation) ) {
+ de = cache->cache_filter->o_trops->tr_ilookup
+ (dir, dentry, ino, generation);
+ is_ilookup = 1;
+ } else
+ de = iops->lookup(dir, dentry);
+#if 0
+ }
+#endif
+
+ if ( IS_ERR(de) ) {
+ CERROR("dentry lookup error %ld\n", PTR_ERR(de));
+ return de;
+ }
+
+ /* some file systems have no read_inode: set methods here */
+ if (dentry->d_inode)
+ presto_set_ops(dentry->d_inode, cache->cache_filter);
+ /* dentry->d_op is now hooked in dcache.c:presto_set_dd */
+
+ /* In lookup we will tolerate EROFS return codes from presto_set_dd
+ * to placate NFS. EROFS indicates that a fileset was not found but
+ * we should still be able to continue through a lookup.
+ * Anything else is a hard error and must be returned to VFS. */
+ if (!is_ilookup)
+ rc = presto_set_dd(dentry);
+ if (rc && rc != -EROFS) {
+ CERROR("presto_set_dd failed (dir %ld, name %*s): %d\n",
+ dir->i_ino, dentry->d_name.len, dentry->d_name.name, rc);
+ return ERR_PTR(rc);
+ }
+
+ EXIT;
+ return NULL;
+}
+
+static inline int presto_check_set_fsdata (struct dentry *de)
+{
+ if (presto_d2d(de) == NULL) {
+#ifdef PRESTO_NO_NFS
+ CERROR("dentry without fsdata: %p: %*s\n", de,
+ de->d_name.len, de->d_name.name);
+ BUG();
+#endif
+ return presto_set_dd (de);
+ }
+
+ return 0;
+}
+
+int presto_setattr(struct dentry *de, struct iattr *iattr)
+{
+ int error;
+ struct presto_cache *cache;
+ struct presto_file_set *fset;
+ struct lento_vfs_context info = { 0, 0, 0 };
+
+ ENTRY;
+
+ error = presto_prep(de, &cache, &fset);
+ if ( error ) {
+ EXIT;
+ return error;
+ }
+
+ if (!iattr->ia_valid)
+ CDEBUG(D_INODE, "presto_setattr: iattr is not valid\n");
+
+ CDEBUG(D_INODE, "valid %#x, mode %#o, uid %u, gid %u, size %Lu, "
+ "atime %lu mtime %lu ctime %lu flags %d\n",
+ iattr->ia_valid, iattr->ia_mode, iattr->ia_uid, iattr->ia_gid,
+ iattr->ia_size, iattr->ia_atime, iattr->ia_mtime,
+ iattr->ia_ctime, iattr->ia_attr_flags);
+
+ if ( presto_get_permit(de->d_inode) < 0 ) {
+ EXIT;
+ return -EROFS;
+ }
+
+ if (!ISLENTO(presto_c2m(cache)))
+ info.flags = LENTO_FL_KML;
+ info.flags |= LENTO_FL_IGNORE_TIME;
+ error = presto_do_setattr(fset, de, iattr, &info);
+ presto_put_permit(de->d_inode);
+ return error;
+}
+
+/*
+ * Now the meat: the fs operations that require journaling
+ *
+ *
+ * XXX: some of these need modifications for hierarchical filesets
+ */
+
+int presto_prep(struct dentry *dentry, struct presto_cache **cache,
+ struct presto_file_set **fset)
+{
+ int rc;
+
+ /* NFS might pass us dentries which have not gone through lookup.
+ * Test and set d_fsdata for such dentries
+ */
+ rc = presto_check_set_fsdata (dentry);
+ if (rc) return rc;
+
+ *fset = presto_fset(dentry);
+ if ( *fset == NULL ) {
+ CERROR("No file set for dentry at %p: %*s\n", dentry,
+ dentry->d_name.len, dentry->d_name.name);
+ return -EROFS;
+ }
+
+ *cache = (*fset)->fset_cache;
+ if ( *cache == NULL ) {
+ CERROR("PRESTO: BAD, BAD: cannot find cache\n");
+ return -EBADF;
+ }
+
+ CDEBUG(D_PIOCTL, "---> cache flags %x, fset flags %x\n",
+ (*cache)->cache_flags, (*fset)->fset_flags);
+ if( presto_is_read_only(*fset) ) {
+ CERROR("PRESTO: cannot modify read-only fileset, minor %d.\n",
+ presto_c2m(*cache));
+ return -EROFS;
+ }
+ return 0;
+}
+
+static int presto_create(struct inode * dir, struct dentry * dentry, int mode)
+{
+ int error;
+ struct presto_cache *cache;
+ struct dentry *parent = dentry->d_parent;
+ struct lento_vfs_context info;
+ struct presto_file_set *fset;
+
+ ENTRY;
+ error = presto_check_set_fsdata(dentry);
+ if ( error ) {
+ EXIT;
+ return error;
+ }
+
+ error = presto_prep(dentry->d_parent, &cache, &fset);
+ if ( error ) {
+ EXIT;
+ return error;
+ }
+ presto_unlock(dir);
+
+ /* Does blocking and non-blocking behavious need to be
+ checked for. Without blocking (return 1), the permit
+ was acquired without reintegration
+ */
+ if ( presto_get_permit(dir) < 0 ) {
+ EXIT;
+ presto_fulllock(dir);
+ return -EROFS;
+ }
+
+ presto_relock_sem(dir);
+ parent = dentry->d_parent;
+ memset(&info, 0, sizeof(info));
+ if (!ISLENTO(presto_c2m(cache)))
+ info.flags = LENTO_FL_KML;
+ info.flags |= LENTO_FL_IGNORE_TIME;
+ error = presto_do_create(fset, parent, dentry, mode, &info);
+
+ presto_relock_other(dir);
+ presto_put_permit(dir);
+ EXIT;
+ return error;
+}
+
+static int presto_link(struct dentry *old_dentry, struct inode *dir,
+ struct dentry *new_dentry)
+{
+ int error;
+ struct presto_cache *cache, *new_cache;
+ struct presto_file_set *fset, *new_fset;
+ struct dentry *parent = new_dentry->d_parent;
+ struct lento_vfs_context info;
+
+ ENTRY;
+ error = presto_prep(old_dentry, &cache, &fset);
+ if ( error ) {
+ EXIT;
+ return error;
+ }
+
+ error = presto_check_set_fsdata(new_dentry);
+ if ( error ) {
+ EXIT;
+ return error;
+ }
+
+ error = presto_prep(new_dentry->d_parent, &new_cache, &new_fset);
+ if ( error ) {
+ EXIT;
+ return error;
+ }
+
+ if (fset != new_fset) {
+ EXIT;
+ return -EXDEV;
+ }
+
+ presto_unlock(dir);
+ if ( presto_get_permit(old_dentry->d_inode) < 0 ) {
+ EXIT;
+ presto_fulllock(dir);
+ return -EROFS;
+ }
+
+ if ( presto_get_permit(dir) < 0 ) {
+ EXIT;
+ presto_fulllock(dir);
+ return -EROFS;
+ }
+
+ presto_relock_sem(dir);
+ parent = new_dentry->d_parent;
+
+ memset(&info, 0, sizeof(info));
+ if (!ISLENTO(presto_c2m(cache)))
+ info.flags = LENTO_FL_KML;
+ info.flags |= LENTO_FL_IGNORE_TIME;
+ error = presto_do_link(fset, old_dentry, parent,
+ new_dentry, &info);
+
+#if 0
+ /* XXX for links this is not right */
+ if (cache->cache_filter->o_trops->tr_add_ilookup ) {
+ struct dentry *d;
+ d = cache->cache_filter->o_trops->tr_add_ilookup
+ (dir->i_sb->s_root, new_dentry, 1);
+ }
+#endif
+
+ presto_relock_other(dir);
+ presto_put_permit(dir);
+ presto_put_permit(old_dentry->d_inode);
+ return error;
+}
+
+static int presto_mkdir(struct inode * dir, struct dentry * dentry, int mode)
+{
+ int error;
+ struct presto_file_set *fset;
+ struct presto_cache *cache;
+ struct dentry *parent = dentry->d_parent;
+ struct lento_vfs_context info;
+
+ ENTRY;
+
+ error = presto_check_set_fsdata(dentry);
+ if ( error ) {
+ EXIT;
+ return error;
+ }
+
+ error = presto_prep(dentry->d_parent, &cache, &fset);
+ if ( error ) {
+ EXIT;
+ return error;
+ }
+
+ presto_unlock(dir);
+
+ if ( presto_get_permit(dir) < 0 ) {
+ EXIT;
+ presto_fulllock(dir);
+ return -EROFS;
+ }
+
+ memset(&info, 0, sizeof(info));
+ if (!ISLENTO(presto_c2m(cache)))
+ info.flags = LENTO_FL_KML;
+ info.flags |= LENTO_FL_IGNORE_TIME;
+
+ presto_relock_sem(dir);
+ parent = dentry->d_parent;
+ error = presto_do_mkdir(fset, parent, dentry, mode, &info);
+ presto_relock_other(dir);
+ presto_put_permit(dir);
+ return error;
+}
+
+
+
+static int presto_symlink(struct inode *dir, struct dentry *dentry,
+ const char *name)
+{
+ int error;
+ struct presto_cache *cache;
+ struct presto_file_set *fset;
+ struct dentry *parent = dentry->d_parent;
+ struct lento_vfs_context info;
+
+ ENTRY;
+ error = presto_check_set_fsdata(dentry);
+ if ( error ) {
+ EXIT;
+ return error;
+ }
+
+ error = presto_prep(dentry->d_parent, &cache, &fset);
+ if ( error ) {
+ EXIT;
+ return error;
+ }
+
+ presto_unlock(dir);
+ if ( presto_get_permit(dir) < 0 ) {
+ EXIT;
+ presto_fulllock(dir);
+ return -EROFS;
+ }
+
+ presto_relock_sem(dir);
+ parent = dentry->d_parent;
+ memset(&info, 0, sizeof(info));
+ if (!ISLENTO(presto_c2m(cache)))
+ info.flags = LENTO_FL_KML;
+ info.flags |= LENTO_FL_IGNORE_TIME;
+ error = presto_do_symlink(fset, parent, dentry, name, &info);
+ presto_relock_other(dir);
+ presto_put_permit(dir);
+ return error;
+}
+
+int presto_unlink(struct inode *dir, struct dentry *dentry)
+{
+ int error;
+ struct presto_cache *cache;
+ struct presto_file_set *fset;
+ struct dentry *parent = dentry->d_parent;
+ struct lento_vfs_context info;
+
+ ENTRY;
+ error = presto_check_set_fsdata(dentry);
+ if ( error ) {
+ EXIT;
+ return error;
+ }
+
+ error = presto_prep(dentry->d_parent, &cache, &fset);
+ if ( error ) {
+ EXIT;
+ return error;
+ }
+
+ presto_unlock(dir);
+ if ( presto_get_permit(dir) < 0 ) {
+ EXIT;
+ presto_fulllock(dir);
+ return -EROFS;
+ }
+
+ presto_relock_sem(dir);
+ parent = dentry->d_parent;
+ memset(&info, 0, sizeof(info));
+ if (!ISLENTO(presto_c2m(cache)))
+ info.flags = LENTO_FL_KML;
+ info.flags |= LENTO_FL_IGNORE_TIME;
+
+ error = presto_do_unlink(fset, parent, dentry, &info);
+
+ presto_relock_other(dir);
+ presto_put_permit(dir);
+ return error;
+}
+
+static int presto_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ int error;
+ struct presto_cache *cache;
+ struct presto_file_set *fset;
+ struct dentry *parent = dentry->d_parent;
+ struct lento_vfs_context info;
+
+ ENTRY;
+ CDEBUG(D_FILE, "prepping presto\n");
+ error = presto_check_set_fsdata(dentry);
+
+ if ( error ) {
+ EXIT;
+ return error;
+ }
+
+ error = presto_prep(dentry->d_parent, &cache, &fset);
+ if ( error ) {
+ EXIT;
+ return error;
+ }
+
+ CDEBUG(D_FILE, "unlocking\n");
+ /* We need to dget() before the dput in double_unlock, to ensure we
+ * still have dentry references. double_lock doesn't do dget for us.
+ */
+ unlock_kernel();
+ if (d_unhashed(dentry))
+ d_rehash(dentry);
+ double_up(&dir->i_zombie, &dentry->d_inode->i_zombie);
+ double_up(&dir->i_sem, &dentry->d_inode->i_sem);
+
+ CDEBUG(D_FILE, "getting permit\n");
+ if ( presto_get_permit(parent->d_inode) < 0 ) {
+ EXIT;
+ double_down(&dir->i_sem, &dentry->d_inode->i_sem);
+ double_down(&dir->i_zombie, &dentry->d_inode->i_zombie);
+
+ lock_kernel();
+ return -EROFS;
+ }
+ CDEBUG(D_FILE, "locking\n");
+
+ double_down(&dir->i_sem, &dentry->d_inode->i_sem);
+ parent = dentry->d_parent;
+ memset(&info, 0, sizeof(info));
+ if (!ISLENTO(presto_c2m(cache)))
+ info.flags = LENTO_FL_KML;
+ info.flags |= LENTO_FL_IGNORE_TIME;
+ error = presto_do_rmdir(fset, parent, dentry, &info);
+ presto_put_permit(parent->d_inode);
+ lock_kernel();
+ EXIT;
+ return error;
+}
+
+static int presto_mknod(struct inode * dir, struct dentry * dentry, int mode, int rdev)
+{
+ int error;
+ struct presto_cache *cache;
+ struct presto_file_set *fset;
+ struct dentry *parent = dentry->d_parent;
+ struct lento_vfs_context info;
+
+ ENTRY;
+ error = presto_check_set_fsdata(dentry);
+ if ( error ) {
+ EXIT;
+ return error;
+ }
+
+ error = presto_prep(dentry->d_parent, &cache, &fset);
+ if ( error ) {
+ EXIT;
+ return error;
+ }
+
+ presto_unlock(dir);
+ if ( presto_get_permit(dir) < 0 ) {
+ EXIT;
+ presto_fulllock(dir);
+ return -EROFS;
+ }
+
+ presto_relock_sem(dir);
+ parent = dentry->d_parent;
+ memset(&info, 0, sizeof(info));
+ if (!ISLENTO(presto_c2m(cache)))
+ info.flags = LENTO_FL_KML;
+ info.flags |= LENTO_FL_IGNORE_TIME;
+ error = presto_do_mknod(fset, parent, dentry, mode, rdev, &info);
+ presto_relock_other(dir);
+ presto_put_permit(dir);
+ EXIT;
+ return error;
+}
+
+inline void presto_triple_unlock(struct inode *old_dir, struct inode *new_dir,
+ struct dentry *old_dentry,
+ struct dentry *new_dentry, int triple)
+{
+ /* rename_dir case */
+ if (S_ISDIR(old_dentry->d_inode->i_mode)) {
+ if (triple) {
+ triple_up(&old_dir->i_zombie,
+ &new_dir->i_zombie,
+ &new_dentry->d_inode->i_zombie);
+ } else {
+ double_up(&old_dir->i_zombie,
+ &new_dir->i_zombie);
+ }
+ up(&old_dir->i_sb->s_vfs_rename_sem);
+ } else /* this case is rename_other */
+ double_up(&old_dir->i_zombie, &new_dir->i_zombie);
+ /* done by do_rename */
+ unlock_kernel();
+ double_up(&old_dir->i_sem, &new_dir->i_sem);
+}
+
+inline void presto_triple_fulllock(struct inode *old_dir,
+ struct inode *new_dir,
+ struct dentry *old_dentry,
+ struct dentry *new_dentry, int triple)
+{
+ /* done by do_rename */
+ double_down(&old_dir->i_sem, &new_dir->i_sem);
+ lock_kernel();
+ /* rename_dir case */
+ if (S_ISDIR(old_dentry->d_inode->i_mode)) {
+ down(&old_dir->i_sb->s_vfs_rename_sem);
+ if (triple) {
+ triple_down(&old_dir->i_zombie,
+ &new_dir->i_zombie,
+ &new_dentry->d_inode->i_zombie);
+ } else {
+ double_down(&old_dir->i_zombie,
+ &new_dir->i_zombie);
+ }
+ } else /* this case is rename_other */
+ double_down(&old_dir->i_zombie, &new_dir->i_zombie);
+}
+
+inline void presto_triple_relock_sem(struct inode *old_dir,
+ struct inode *new_dir,
+ struct dentry *old_dentry,
+ struct dentry *new_dentry, int triple)
+{
+ /* done by do_rename */
+ double_down(&old_dir->i_sem, &new_dir->i_sem);
+ lock_kernel();
+}
+
+inline void presto_triple_relock_other(struct inode *old_dir,
+ struct inode *new_dir,
+ struct dentry *old_dentry,
+ struct dentry *new_dentry, int triple)
+{
+ /* rename_dir case */
+ if (S_ISDIR(old_dentry->d_inode->i_mode)) {
+ down(&old_dir->i_sb->s_vfs_rename_sem);
+ if (triple) {
+ triple_down(&old_dir->i_zombie,
+ &new_dir->i_zombie,
+ &new_dentry->d_inode->i_zombie);
+ } else {
+ double_down(&old_dir->i_zombie,
+ &new_dir->i_zombie);
+ }
+ } else /* this case is rename_other */
+ double_down(&old_dir->i_zombie, &new_dir->i_zombie);
+}
+
+
+// XXX this can be optimized: renamtes across filesets only require
+// multiple KML records, but can locally be executed normally.
+int presto_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ int error;
+ struct presto_cache *cache, *new_cache;
+ struct presto_file_set *fset, *new_fset;
+ struct lento_vfs_context info;
+ struct dentry *old_parent = old_dentry->d_parent;
+ struct dentry *new_parent = new_dentry->d_parent;
+ int triple;
+
+ ENTRY;
+ error = presto_prep(old_dentry, &cache, &fset);
+ if ( error ) {
+ EXIT;
+ return error;
+ }
+ error = presto_prep(new_parent, &new_cache, &new_fset);
+ if ( error ) {
+ EXIT;
+ return error;
+ }
+
+ if ( fset != new_fset ) {
+ EXIT;
+ return -EXDEV;
+ }
+
+ /* We need to do dget before the dput in double_unlock, to ensure we
+ * still have dentry references. double_lock doesn't do dget for us.
+ */
+
+ triple = (S_ISDIR(old_dentry->d_inode->i_mode) && new_dentry->d_inode)?
+ 1:0;
+
+ presto_triple_unlock(old_dir, new_dir, old_dentry, new_dentry, triple);
+
+ if ( presto_get_permit(old_dir) < 0 ) {
+ EXIT;
+ presto_triple_fulllock(old_dir, new_dir, old_dentry, new_dentry, triple);
+ return -EROFS;
+ }
+ if ( presto_get_permit(new_dir) < 0 ) {
+ EXIT;
+ presto_triple_fulllock(old_dir, new_dir, old_dentry, new_dentry, triple);
+ return -EROFS;
+ }
+
+ presto_triple_relock_sem(old_dir, new_dir, old_dentry, new_dentry, triple);
+ memset(&info, 0, sizeof(info));
+ if (!ISLENTO(presto_c2m(cache)))
+ info.flags = LENTO_FL_KML;
+ info.flags |= LENTO_FL_IGNORE_TIME;
+ error = do_rename(fset, old_parent, old_dentry, new_parent,
+ new_dentry, &info);
+ presto_triple_relock_other(old_dir, new_dir, old_dentry, new_dentry, triple);
+
+ presto_put_permit(new_dir);
+ presto_put_permit(old_dir);
+ return error;
+}
+
+/* basically this allows the ilookup processes access to all files for
+ * reading, while not making ilookup totally insecure. This could all
+ * go away if we could set the CAP_DAC_READ_SEARCH capability for the client.
+ */
+/* If posix acls are available, the underlying cache fs will export the
+ * appropriate permission function. Thus we do not worry here about ACLs
+ * or EAs. -SHP
+ */
+int presto_permission(struct inode *inode, int mask)
+{
+ unsigned short mode = inode->i_mode;
+ struct presto_cache *cache;
+ int rc;
+
+ ENTRY;
+ if ( presto_can_ilookup() && !(mask & S_IWOTH)) {
+ CDEBUG(D_CACHE, "ilookup on %ld OK\n", inode->i_ino);
+ EXIT;
+ return 0;
+ }
+
+ cache = presto_get_cache(inode);
+
+ if ( cache ) {
+ /* we only override the file/dir permission operations */
+ struct inode_operations *fiops = filter_c2cfiops(cache->cache_filter);
+ struct inode_operations *diops = filter_c2cdiops(cache->cache_filter);
+
+ if ( S_ISREG(mode) && fiops && fiops->permission ) {
+ EXIT;
+ return fiops->permission(inode, mask);
+ }
+ if ( S_ISDIR(mode) && diops && diops->permission ) {
+ EXIT;
+ return diops->permission(inode, mask);
+ }
+ }
+
+ /* The cache filesystem doesn't have its own permission function,
+ * but we don't want to duplicate the VFS code here. In order
+ * to avoid looping from permission calling this function again,
+ * we temporarily override the permission operation while we call
+ * the VFS permission function.
+ */
+ inode->i_op->permission = NULL;
+ rc = permission(inode, mask);
+ inode->i_op->permission = &presto_permission;
+
+ EXIT;
+ return rc;
+}
+
+
+int presto_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ char buf[1024];
+ struct izo_ioctl_data *data = NULL;
+ struct presto_dentry_data *dd;
+ int rc;
+
+ ENTRY;
+
+ /* Try the filesystem's ioctl first, and return if it succeeded. */
+ dd = presto_d2d(file->f_dentry);
+ if (dd && dd->dd_fset) {
+ int (*cache_ioctl)(struct inode *, struct file *, unsigned int, unsigned long ) = filter_c2cdfops(dd->dd_fset->fset_cache->cache_filter)->ioctl;
+ rc = -ENOTTY;
+ if (cache_ioctl)
+ rc = cache_ioctl(inode, file, cmd, arg);
+ if (rc != -ENOTTY) {
+ EXIT;
+ return rc;
+ }
+ }
+
+ if (current->euid != 0 && current->euid != izo_authorized_uid) {
+ EXIT;
+ return -EPERM;
+ }
+
+ memset(buf, 0, sizeof(buf));
+
+ if (izo_ioctl_getdata(buf, buf + 1024, (void *)arg)) {
+ CERROR("intermezzo ioctl: data error\n");
+ return -EINVAL;
+ }
+ data = (struct izo_ioctl_data *)buf;
+
+ switch(cmd) {
+ case IZO_IOC_REINTKML: {
+ int rc;
+ int cperr;
+ rc = kml_reint_rec(file, data);
+
+ EXIT;
+ cperr = copy_to_user((char *)arg, data, sizeof(*data));
+ if (cperr) {
+ CERROR("WARNING: cperr %d\n", cperr);
+ rc = -EFAULT;
+ }
+ return rc;
+ }
+
+ case IZO_IOC_GET_RCVD: {
+ struct izo_rcvd_rec rec;
+ struct presto_file_set *fset;
+ int rc;
+
+ fset = presto_fset(file->f_dentry);
+ if (fset == NULL) {
+ EXIT;
+ return -ENODEV;
+ }
+ rc = izo_rcvd_get(&rec, fset, data->ioc_uuid);
+ if (rc < 0) {
+ EXIT;
+ return rc;
+ }
+
+ EXIT;
+ return copy_to_user((char *)arg, &rec, sizeof(rec))? -EFAULT : 0;
+ }
+
+ case IZO_IOC_REPSTATUS: {
+ __u64 client_kmlsize;
+ struct izo_rcvd_rec *lr_client;
+ struct izo_rcvd_rec rec;
+ struct presto_file_set *fset;
+ int minor;
+ int rc;
+
+ fset = presto_fset(file->f_dentry);
+ if (fset == NULL) {
+ EXIT;
+ return -ENODEV;
+ }
+ minor = presto_f2m(fset);
+
+ client_kmlsize = data->ioc_kmlsize;
+ lr_client = (struct izo_rcvd_rec *) data->ioc_pbuf1;
+
+ rc = izo_repstatus(fset, client_kmlsize,
+ lr_client, &rec);
+ if (rc < 0) {
+ EXIT;
+ return rc;
+ }
+
+ EXIT;
+ return copy_to_user((char *)arg, &rec, sizeof(rec))? -EFAULT : 0;
+ }
+
+ case IZO_IOC_GET_CHANNEL: {
+ struct presto_file_set *fset;
+
+ fset = presto_fset(file->f_dentry);
+ if (fset == NULL) {
+ EXIT;
+ return -ENODEV;
+ }
+
+ data->ioc_dev = fset->fset_cache->cache_psdev->uc_minor;
+ CDEBUG(D_PSDEV, "CHANNEL %d\n", data->ioc_dev);
+ EXIT;
+ return copy_to_user((char *)arg, data, sizeof(*data))? -EFAULT : 0;
+ }
+
+ case IZO_IOC_SET_IOCTL_UID:
+ izo_authorized_uid = data->ioc_uid;
+ EXIT;
+ return 0;
+
+ case IZO_IOC_SET_PID:
+ rc = izo_psdev_setpid(data->ioc_dev);
+ EXIT;
+ return rc;
+
+ case IZO_IOC_SET_CHANNEL:
+ rc = izo_psdev_setchannel(file, data->ioc_dev);
+ EXIT;
+ return rc;
+
+ case IZO_IOC_GET_KML_SIZE: {
+ struct presto_file_set *fset;
+ __u64 kmlsize;
+
+ fset = presto_fset(file->f_dentry);
+ if (fset == NULL) {
+ EXIT;
+ return -ENODEV;
+ }
+
+ kmlsize = presto_kml_offset(fset) + fset->fset_kml_logical_off;
+
+ EXIT;
+ return copy_to_user((char *)arg, &kmlsize, sizeof(kmlsize))?-EFAULT : 0;
+ }
+
+ case IZO_IOC_PURGE_FILE_DATA: {
+ struct presto_file_set *fset;
+
+ fset = presto_fset(file->f_dentry);
+ if (fset == NULL) {
+ EXIT;
+ return -ENODEV;
+ }
+
+ rc = izo_purge_file(fset, data->ioc_inlbuf1);
+ EXIT;
+ return rc;
+ }
+
+ case IZO_IOC_GET_FILEID: {
+ rc = izo_get_fileid(file, data);
+ EXIT;
+ if (rc)
+ return rc;
+ return copy_to_user((char *)arg, data, sizeof(*data))? -EFAULT : 0;
+ }
+
+ case IZO_IOC_SET_FILEID: {
+ rc = izo_set_fileid(file, data);
+ EXIT;
+ if (rc)
+ return rc;
+ return copy_to_user((char *)arg, data, sizeof(*data))? -EFAULT : 0;
+ }
+
+ case IZO_IOC_ADJUST_LML: {
+ struct lento_vfs_context *info;
+ info = (struct lento_vfs_context *)data->ioc_inlbuf1;
+ rc = presto_adjust_lml(file, info);
+ EXIT;
+ return rc;
+ }
+
+ case IZO_IOC_CONNECT: {
+ struct presto_file_set *fset;
+ int minor;
+
+ fset = presto_fset(file->f_dentry);
+ if (fset == NULL) {
+ EXIT;
+ return -ENODEV;
+ }
+ minor = presto_f2m(fset);
+
+ rc = izo_upc_connect(minor, data->ioc_ino,
+ data->ioc_generation, data->ioc_uuid,
+ data->ioc_flags);
+ EXIT;
+ return rc;
+ }
+
+ case IZO_IOC_GO_FETCH_KML: {
+ struct presto_file_set *fset;
+ int minor;
+
+ fset = presto_fset(file->f_dentry);
+ if (fset == NULL) {
+ EXIT;
+ return -ENODEV;
+ }
+ minor = presto_f2m(fset);
+
+ rc = izo_upc_go_fetch_kml(minor, fset->fset_name,
+ data->ioc_uuid, data->ioc_kmlsize);
+ EXIT;
+ return rc;
+ }
+
+ case IZO_IOC_REVOKE_PERMIT:
+ if (data->ioc_flags)
+ rc = izo_revoke_permit(file->f_dentry, data->ioc_uuid);
+ else
+ rc = izo_revoke_permit(file->f_dentry, NULL);
+ EXIT;
+ return rc;
+
+ case IZO_IOC_CLEAR_FSET:
+ rc = izo_clear_fsetroot(file->f_dentry);
+ EXIT;
+ return rc;
+
+ case IZO_IOC_CLEAR_ALL_FSETS: {
+ struct presto_file_set *fset;
+
+ fset = presto_fset(file->f_dentry);
+ if (fset == NULL) {
+ EXIT;
+ return -ENODEV;
+ }
+
+ rc = izo_clear_all_fsetroots(fset->fset_cache);
+ EXIT;
+ return rc;
+ }
+
+ case IZO_IOC_SET_FSET:
+ /*
+ * Mark this dentry as being a fileset root.
+ */
+ rc = presto_set_fsetroot_from_ioc(file->f_dentry,
+ data->ioc_inlbuf1,
+ data->ioc_flags);
+ EXIT;
+ return rc;
+
+
+ case IZO_IOC_MARK: {
+ int res = 0; /* resulting flags - returned to user */
+ int error;
+
+ CDEBUG(D_DOWNCALL, "mark inode: %ld, and: %x, or: %x, what %d\n",
+ file->f_dentry->d_inode->i_ino, data->ioc_and_flag,
+ data->ioc_or_flag, data->ioc_mark_what);
+
+ switch (data->ioc_mark_what) {
+ case MARK_DENTRY:
+ error = izo_mark_dentry(file->f_dentry,
+ data->ioc_and_flag,
+ data->ioc_or_flag, &res);
+ break;
+ case MARK_FSET:
+ error = izo_mark_fset(file->f_dentry,
+ data->ioc_and_flag,
+ data->ioc_or_flag, &res);
+ break;
+ case MARK_CACHE:
+ error = izo_mark_cache(file->f_dentry,
+ data->ioc_and_flag,
+ data->ioc_or_flag, &res);
+ break;
+ case MARK_GETFL: {
+ int fflags, cflags;
+ data->ioc_and_flag = 0xffffffff;
+ data->ioc_or_flag = 0;
+ error = izo_mark_dentry(file->f_dentry,
+ data->ioc_and_flag,
+ data->ioc_or_flag, &res);
+ if (error)
+ break;
+ error = izo_mark_fset(file->f_dentry,
+ data->ioc_and_flag,
+ data->ioc_or_flag, &fflags);
+ if (error)
+ break;
+ error = izo_mark_cache(file->f_dentry,
+ data->ioc_and_flag,
+ data->ioc_or_flag,
+ &cflags);
+
+ if (error)
+ break;
+ data->ioc_and_flag = fflags;
+ data->ioc_or_flag = cflags;
+ break;
+ }
+ default:
+ error = -EINVAL;
+ }
+
+ if (error) {
+ EXIT;
+ return error;
+ }
+ data->ioc_mark_what = res;
+ CDEBUG(D_DOWNCALL, "mark inode: %ld, and: %x, or: %x, what %x\n",
+ file->f_dentry->d_inode->i_ino, data->ioc_and_flag,
+ data->ioc_or_flag, data->ioc_mark_what);
+
+ EXIT;
+ return copy_to_user((char *)arg, data, sizeof(*data))? -EFAULT : 0;
+ }
+#if 0
+ case IZO_IOC_CLIENT_MAKE_BRANCH: {
+ struct presto_file_set *fset;
+ int minor;
+
+ fset = presto_fset(file->f_dentry);
+ if (fset == NULL) {
+ EXIT;
+ return -ENODEV;
+ }
+ minor = presto_f2m(fset);
+
+ rc = izo_upc_client_make_branch(minor, fset->fset_name,
+ data->ioc_inlbuf1,
+ data->ioc_inlbuf2);
+ EXIT;
+ return rc;
+ }
+#endif
+ case IZO_IOC_SERVER_MAKE_BRANCH: {
+ struct presto_file_set *fset;
+ int minor;
+
+ fset = presto_fset(file->f_dentry);
+ if (fset == NULL) {
+ EXIT;
+ return -ENODEV;
+ }
+ minor = presto_f2m(fset);
+
+ izo_upc_server_make_branch(minor, data->ioc_inlbuf1);
+ EXIT;
+ return 0;
+ }
+ case IZO_IOC_SET_KMLSIZE: {
+ struct presto_file_set *fset;
+ int minor;
+ struct izo_rcvd_rec rec;
+
+ fset = presto_fset(file->f_dentry);
+ if (fset == NULL) {
+ EXIT;
+ return -ENODEV;
+ }
+ minor = presto_f2m(fset);
+
+ rc = izo_upc_set_kmlsize(minor, fset->fset_name, data->ioc_uuid,
+ data->ioc_kmlsize);
+
+ if (rc != 0) {
+ EXIT;
+ return rc;
+ }
+
+ rc = izo_rcvd_get(&rec, fset, data->ioc_uuid);
+ if (rc == -EINVAL) {
+ /* We don't know anything about this uuid yet; no
+ * worries. */
+ memset(&rec, 0, sizeof(rec));
+ } else if (rc <= 0) {
+ CERROR("InterMezzo: error reading last_rcvd: %d\n", rc);
+ EXIT;
+ return rc;
+ }
+ rec.lr_remote_offset = data->ioc_kmlsize;
+ rc = izo_rcvd_write(fset, &rec);
+ if (rc <= 0) {
+ CERROR("InterMezzo: error writing last_rcvd: %d\n", rc);
+ EXIT;
+ return rc;
+ }
+ EXIT;
+ return rc;
+ }
+ case IZO_IOC_BRANCH_UNDO: {
+ struct presto_file_set *fset;
+ int minor;
+
+ fset = presto_fset(file->f_dentry);
+ if (fset == NULL) {
+ EXIT;
+ return -ENODEV;
+ }
+ minor = presto_f2m(fset);
+
+ rc = izo_upc_branch_undo(minor, fset->fset_name,
+ data->ioc_inlbuf1);
+ EXIT;
+ return rc;
+ }
+ case IZO_IOC_BRANCH_REDO: {
+ struct presto_file_set *fset;
+ int minor;
+
+ fset = presto_fset(file->f_dentry);
+ if (fset == NULL) {
+ EXIT;
+ return -ENODEV;
+ }
+ minor = presto_f2m(fset);
+
+ rc = izo_upc_branch_redo(minor, fset->fset_name,
+ data->ioc_inlbuf1);
+ EXIT;
+ return rc;
+ }
+
+ case TCGETS:
+ EXIT;
+ return -EINVAL;
+
+ default:
+ EXIT;
+ return -EINVAL;
+
+ }
+ EXIT;
+ return 0;
+}
+
+struct file_operations presto_dir_fops = {
+ .ioctl = presto_ioctl
+};
+
+struct inode_operations presto_dir_iops = {
+ .create = presto_create,
+ .lookup = presto_lookup,
+ .link = presto_link,
+ .unlink = presto_unlink,
+ .symlink = presto_symlink,
+ .mkdir = presto_mkdir,
+ .rmdir = presto_rmdir,
+ .mknod = presto_mknod,
+ .rename = presto_rename,
+ .permission = presto_permission,
+ .setattr = presto_setattr,
+#ifdef CONFIG_FS_EXT_ATTR
+ .set_ext_attr = presto_set_ext_attr,
+#endif
+};
+
+
diff --git a/fs/intermezzo/ext_attr.c b/fs/intermezzo/ext_attr.c
new file mode 100644
index 00000000000000..b14d028852c562
--- /dev/null
+++ b/fs/intermezzo/ext_attr.c
@@ -0,0 +1,205 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Copyright (C) 2001 Tacit Networks, Inc.
+ * Author: Shirish H. Phatak <shirish@tacitnetworks.com>
+ *
+ * This file is part of InterMezzo, http://www.inter-mezzo.org.
+ *
+ * InterMezzo is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * InterMezzo 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 InterMezzo; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Extended attribute handling for presto.
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <linux/unistd.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <linux/string.h>
+#include <asm/uaccess.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <asm/segment.h>
+#include <linux/smp_lock.h>
+
+#include <linux/intermezzo_fs.h>
+#include <linux/intermezzo_psdev.h>
+
+#ifdef CONFIG_FS_EXT_ATTR
+#include <linux/ext_attr.h>
+
+extern inline void presto_debug_fail_blkdev(struct presto_file_set *fset,
+ unsigned long value);
+
+
+/* VFS interface */
+/* XXX! Fixme test for user defined attributes */
+int presto_set_ext_attr(struct inode *inode,
+ const char *name, void *buffer,
+ size_t buffer_len, int flags)
+{
+ int error;
+ struct presto_cache *cache;
+ struct presto_file_set *fset;
+ struct lento_vfs_context info;
+ struct dentry *dentry;
+ int minor = presto_i2m(inode);
+ char *buf = NULL;
+
+ ENTRY;
+ if (minor < 0) {
+ EXIT;
+ return -1;
+ }
+
+ if ( ISLENTO(minor) ) {
+ EXIT;
+ return -EINVAL;
+ }
+
+ /* BAD...vfs should really pass down the dentry to use, especially
+ * since every other operation in iops does. But for now
+ * we do a reverse mapping from inode to the first dentry
+ */
+ if (list_empty(&inode->i_dentry)) {
+ CERROR("No alias for inode %d\n", (int) inode->i_ino);
+ EXIT;
+ return -EINVAL;
+ }
+
+ dentry = list_entry(inode->i_dentry.next, struct dentry, d_alias);
+
+ error = presto_prep(dentry, &cache, &fset);
+ if ( error ) {
+ EXIT;
+ return error;
+ }
+
+ if ((buffer != NULL) && (buffer_len != 0)) {
+ /* If buffer is a user space pointer copy it to kernel space
+ * and reset the flag. We do this since the journal functions need
+ * access to the contents of the buffer, and the file system
+ * does not care. When we actually invoke the function, we remove
+ * the EXT_ATTR_FLAG_USER flag.
+ *
+ * XXX:Check if the "fs does not care" assertion is always true -SHP
+ * (works for ext3)
+ */
+ if (flags & EXT_ATTR_FLAG_USER) {
+ PRESTO_ALLOC(buf, buffer_len);
+ if (!buf) {
+ CERROR("InterMezzo: out of memory!!!\n");
+ return -ENOMEM;
+ }
+ error = copy_from_user(buf, buffer, buffer_len);
+ if (error)
+ return -EFAULT;
+ } else
+ buf = buffer;
+ } else
+ buf = buffer;
+
+ if ( presto_get_permit(inode) < 0 ) {
+ EXIT;
+ if (buffer_len && (flags & EXT_ATTR_FLAG_USER))
+ PRESTO_FREE(buf, buffer_len);
+ return -EROFS;
+ }
+
+ /* Simulate presto_setup_info */
+ memset(&info, 0, sizeof(info));
+ /* For now redundant..but we keep it around just in case */
+ info.flags = LENTO_FL_IGNORE_TIME;
+ if (!ISLENTO(cache->cache_psdev->uc_minor))
+ info.flags |= LENTO_FL_KML;
+
+ /* We pass in the kernel space pointer and reset the
+ * EXT_ATTR_FLAG_USER flag.
+ * See comments above.
+ */
+ /* Note that mode is already set by VFS so we send in a NULL */
+ error = presto_do_set_ext_attr(fset, dentry, name, buf,
+ buffer_len, flags & ~EXT_ATTR_FLAG_USER,
+ NULL, &info);
+ presto_put_permit(inode);
+
+ if (buffer_len && (flags & EXT_ATTR_FLAG_USER))
+ PRESTO_FREE(buf, buffer_len);
+ EXIT;
+ return error;
+}
+
+/* Lento Interface */
+/* XXX: ignore flags? We should be forcing these operations through? -SHP*/
+int lento_set_ext_attr(const char *path, const char *name,
+ void *buffer, size_t buffer_len, int flags, mode_t mode,
+ struct lento_vfs_context *info)
+{
+ int error;
+ char * pathname;
+ struct nameidata nd;
+ struct dentry *dentry;
+ struct presto_file_set *fset;
+
+ ENTRY;
+ lock_kernel();
+
+ pathname=getname(path);
+ error = PTR_ERR(pathname);
+ if (IS_ERR(pathname)) {
+ EXIT;
+ goto exit;
+ }
+
+ /* Note that ext_attrs apply to both files and directories..*/
+ error=presto_walk(pathname,&nd);
+ if (error)
+ goto exit;
+ dentry = nd.dentry;
+
+ fset = presto_fset(dentry);
+ error = -EINVAL;
+ if ( !fset ) {
+ CERROR("No fileset!\n");
+ EXIT;
+ goto exit_dentry;
+ }
+
+ if (buffer==NULL) buffer_len=0;
+
+ error = presto_do_set_ext_attr(fset, dentry, name, buffer,
+ buffer_len, flags, &mode, info);
+exit_dentry:
+ path_release(&nd);
+exit_path:
+ putname(pathname);
+exit:
+ unlock_kernel();
+ return error;
+}
+
+#endif /*CONFIG_FS_EXT_ATTR*/
diff --git a/fs/intermezzo/file.c b/fs/intermezzo/file.c
new file mode 100644
index 00000000000000..0f826926c29648
--- /dev/null
+++ b/fs/intermezzo/file.c
@@ -0,0 +1,539 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Copyright (C) 2000 Stelias Computing, Inc.
+ * Copyright (C) 2000 Red Hat, Inc.
+ * Copyright (C) 2000 TurboLinux, Inc.
+ * Copyright (C) 2000 Los Alamos National Laboratory.
+ * Copyright (C) 2000, 2001 Tacit Networks, Inc.
+ * Copyright (C) 2000 Peter J. Braam
+ * Copyright (C) 2001 Mountain View Data, Inc.
+ * Copyright (C) 2001 Cluster File Systems, Inc.
+ *
+ * This file is part of InterMezzo, http://www.inter-mezzo.org.
+ *
+ * InterMezzo is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * InterMezzo 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 InterMezzo; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * This file manages file I/O
+ *
+ */
+
+#include <stdarg.h>
+
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/ext2_fs.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/blkdev.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+#define __NO_VERSION__
+#include <linux/module.h>
+
+#include <linux/intermezzo_fs.h>
+#include <linux/intermezzo_psdev.h>
+#include <linux/fsfilter.h>
+/*
+ * these are initialized in super.c
+ */
+extern int presto_permission(struct inode *inode, int mask);
+
+
+static int presto_open_upcall(int minor, struct dentry *de)
+{
+ int rc = 0;
+ char *path, *buffer;
+ struct presto_file_set *fset;
+ int pathlen;
+ struct lento_vfs_context info;
+ struct presto_dentry_data *dd = presto_d2d(de);
+
+ PRESTO_ALLOC(buffer, PAGE_SIZE);
+ if ( !buffer ) {
+ CERROR("PRESTO: out of memory!\n");
+ return -ENOMEM;
+ }
+ fset = presto_fset(de);
+ path = presto_path(de, fset->fset_dentry, buffer, PAGE_SIZE);
+ pathlen = MYPATHLEN(buffer, path);
+
+ CDEBUG(D_FILE, "de %p, dd %p\n", de, dd);
+ if (dd->remote_ino == 0) {
+ rc = presto_get_fileid(minor, fset, de);
+ }
+ memset (&info, 0, sizeof(info));
+ if (dd->remote_ino > 0) {
+ info.remote_ino = dd->remote_ino;
+ info.remote_generation = dd->remote_generation;
+ } else
+ CERROR("get_fileid failed %d, ino: %Lx, fetching by name\n", rc,
+ dd->remote_ino);
+
+ rc = izo_upc_open(minor, pathlen, path, fset->fset_name, &info);
+ PRESTO_FREE(buffer, PAGE_SIZE);
+ return rc;
+}
+
+static inline int open_check_dod(struct file *file,
+ struct presto_file_set *fset)
+{
+ int gen, is_iopen = 0, minor;
+ struct presto_cache *cache = fset->fset_cache;
+ ino_t inum;
+
+ minor = presto_c2m(cache);
+
+ if ( ISLENTO(minor) ) {
+ CDEBUG(D_CACHE, "is lento, not doing DOD.\n");
+ return 0;
+ }
+
+ /* Files are only ever opened by inode during backfetches, when by
+ * definition we have the authoritative copy of the data. No DOD. */
+ is_iopen = izo_dentry_is_ilookup(file->f_dentry, &inum, &gen);
+
+ if (is_iopen) {
+ CDEBUG(D_CACHE, "doing iopen, not doing DOD.\n");
+ return 0;
+ }
+
+ if (!(fset->fset_flags & FSET_DATA_ON_DEMAND)) {
+ CDEBUG(D_CACHE, "fileset not on demand.\n");
+ return 0;
+ }
+
+ if (file->f_flags & O_TRUNC) {
+ CDEBUG(D_CACHE, "fileset dod: O_TRUNC.\n");
+ return 0;
+ }
+
+ if (presto_chk(file->f_dentry, PRESTO_DONT_JOURNAL)) {
+ CDEBUG(D_CACHE, "file under .intermezzo, not doing DOD\n");
+ return 0;
+ }
+
+ if (presto_chk(file->f_dentry, PRESTO_DATA)) {
+ CDEBUG(D_CACHE, "PRESTO_DATA is set, not doing DOD.\n");
+ return 0;
+ }
+
+ if (cache->cache_filter->o_trops->tr_all_data(file->f_dentry->d_inode)) {
+ CDEBUG(D_CACHE, "file not sparse, not doing DOD.\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+static int presto_file_open(struct inode *inode, struct file *file)
+{
+ int rc = 0;
+ struct file_operations *fops;
+ struct presto_cache *cache;
+ struct presto_file_set *fset;
+ struct presto_file_data *fdata;
+ int writable = (file->f_flags & (O_RDWR | O_WRONLY));
+ int minor, i;
+
+ ENTRY;
+
+ if (presto_prep(file->f_dentry, &cache, &fset) < 0) {
+ EXIT;
+ return -EBADF;
+ }
+
+ minor = presto_c2m(cache);
+
+ CDEBUG(D_CACHE, "DATA_OK: %d, ino: %ld, islento: %d\n",
+ presto_chk(file->f_dentry, PRESTO_DATA), inode->i_ino,
+ ISLENTO(minor));
+
+ if ( !ISLENTO(minor) && (file->f_flags & O_RDWR ||
+ file->f_flags & O_WRONLY)) {
+ CDEBUG(D_CACHE, "calling presto_get_permit\n");
+ if ( presto_get_permit(inode) < 0 ) {
+ EXIT;
+ return -EROFS;
+ }
+ presto_put_permit(inode);
+ }
+
+ if (open_check_dod(file, fset)) {
+ CDEBUG(D_CACHE, "presto_open_upcall\n");
+ CDEBUG(D_CACHE, "dentry: %p setting DATA, ATTR\n", file->f_dentry);
+ presto_set(file->f_dentry, PRESTO_ATTR | PRESTO_DATA);
+ rc = presto_open_upcall(minor, file->f_dentry);
+ if (rc) {
+ EXIT;
+ CERROR("%s: returning error %d\n", __FUNCTION__, rc);
+ return rc;
+ }
+
+ }
+
+ /* file was truncated upon open: do not refetch */
+ if (file->f_flags & O_TRUNC) {
+ CDEBUG(D_CACHE, "setting DATA, ATTR\n");
+ presto_set(file->f_dentry, PRESTO_ATTR | PRESTO_DATA);
+ }
+
+ fops = filter_c2cffops(cache->cache_filter);
+ if ( fops->open ) {
+ CDEBUG(D_CACHE, "calling fs open\n");
+ rc = fops->open(inode, file);
+
+ if (rc) {
+ EXIT;
+ return rc;
+ }
+ }
+
+ if (writable) {
+ PRESTO_ALLOC(fdata, sizeof(*fdata));
+ if (!fdata) {
+ EXIT;
+ return -ENOMEM;
+ }
+ /* LOCK: XXX check that the kernel lock protects this alloc */
+ fdata->fd_do_lml = 0;
+ fdata->fd_bytes_written = 0;
+ fdata->fd_fsuid = current->fsuid;
+ fdata->fd_fsgid = current->fsgid;
+ fdata->fd_mode = file->f_dentry->d_inode->i_mode;
+ fdata->fd_uid = file->f_dentry->d_inode->i_uid;
+ fdata->fd_gid = file->f_dentry->d_inode->i_gid;
+ fdata->fd_ngroups = current->ngroups;
+ for (i=0 ; i < current->ngroups ; i++)
+ fdata->fd_groups[i] = current->groups[i];
+ if (!ISLENTO(minor))
+ fdata->fd_info.flags = LENTO_FL_KML;
+ else {
+ /* this is for the case of DOD,
+ reint_close will adjust flags if needed */
+ fdata->fd_info.flags = 0;
+ }
+
+ presto_getversion(&fdata->fd_version, inode);
+ file->private_data = fdata;
+ } else {
+ file->private_data = NULL;
+ }
+
+ EXIT;
+ return 0;
+}
+
+int presto_adjust_lml(struct file *file, struct lento_vfs_context *info)
+{
+ struct presto_file_data *fdata =
+ (struct presto_file_data *) file->private_data;
+
+ if (!fdata) {
+ EXIT;
+ return -EINVAL;
+ }
+
+ memcpy(&fdata->fd_info, info, sizeof(*info));
+ EXIT;
+ return 0;
+}
+
+
+static int presto_file_release(struct inode *inode, struct file *file)
+{
+ int rc;
+ struct file_operations *fops;
+ struct presto_cache *cache;
+ struct presto_file_set *fset;
+ struct presto_file_data *fdata =
+ (struct presto_file_data *)file->private_data;
+ ENTRY;
+
+ rc = presto_prep(file->f_dentry, &cache, &fset);
+ if ( rc ) {
+ EXIT;
+ return rc;
+ }
+
+ fops = filter_c2cffops(cache->cache_filter);
+ if (fops && fops->release)
+ rc = fops->release(inode, file);
+
+ CDEBUG(D_CACHE, "islento = %d (minor %d), rc %d, data %p\n",
+ ISLENTO(cache->cache_psdev->uc_minor),
+ cache->cache_psdev->uc_minor, rc, fdata);
+
+ /* this file was modified: ignore close errors, write KML */
+ if (fdata && fdata->fd_do_lml) {
+ /* XXX: remove when lento gets file granularity cd */
+ if ( presto_get_permit(inode) < 0 ) {
+ EXIT;
+ return -EROFS;
+ }
+
+ fdata->fd_info.updated_time = file->f_dentry->d_inode->i_mtime;
+ rc = presto_do_close(fset, file);
+ presto_put_permit(inode);
+ }
+
+ if (!rc && fdata) {
+ PRESTO_FREE(fdata, sizeof(*fdata));
+ file->private_data = NULL;
+ }
+
+ EXIT;
+ return rc;
+}
+
+static void presto_apply_write_policy(struct file *file,
+ struct presto_file_set *fset, loff_t res)
+{
+ struct presto_file_data *fdata =
+ (struct presto_file_data *)file->private_data;
+ struct presto_cache *cache = fset->fset_cache;
+ struct presto_version new_file_ver;
+ int error;
+ struct rec_info rec;
+
+ /* Here we do a journal close after a fixed or a specified
+ amount of KBytes, currently a global parameter set with
+ sysctl. If files are open for a long time, this gives added
+ protection. (XXX todo: per cache, add ioctl, handle
+ journaling in a thread, add more options etc.)
+ */
+
+ if ((fset->fset_flags & FSET_JCLOSE_ON_WRITE) &&
+ (!ISLENTO(cache->cache_psdev->uc_minor))) {
+ fdata->fd_bytes_written += res;
+
+ if (fdata->fd_bytes_written >= fset->fset_file_maxio) {
+ presto_getversion(&new_file_ver,
+ file->f_dentry->d_inode);
+ /* This is really heavy weight and should be fixed
+ ASAP. At most we should be recording the number
+ of bytes written and not locking the kernel,
+ wait for permits, etc, on the write path. SHP
+ */
+ lock_kernel();
+ if ( presto_get_permit(file->f_dentry->d_inode) < 0 ) {
+ EXIT;
+ /* we must be disconnected, not to worry */
+ unlock_kernel();
+ return;
+ }
+ error = presto_journal_close(&rec, fset, file,
+ file->f_dentry,
+ &fdata->fd_version,
+ &new_file_ver);
+ presto_put_permit(file->f_dentry->d_inode);
+ unlock_kernel();
+ if ( error ) {
+ CERROR("presto_close: cannot journal close\n");
+ /* XXX these errors are really bad */
+ /* panic(); */
+ return;
+ }
+ fdata->fd_bytes_written = 0;
+ }
+ }
+}
+
+static ssize_t presto_file_write(struct file *file, const char *buf,
+ size_t size, loff_t *off)
+{
+ struct rec_info rec;
+ int error;
+ struct presto_cache *cache;
+ struct presto_file_set *fset;
+ struct file_operations *fops;
+ ssize_t res;
+ int do_lml_here;
+ void *handle = NULL;
+ unsigned long blocks;
+ struct presto_file_data *fdata;
+ loff_t res_size;
+
+ error = presto_prep(file->f_dentry, &cache, &fset);
+ if ( error ) {
+ EXIT;
+ return error;
+ }
+
+ blocks = (size >> file->f_dentry->d_inode->i_sb->s_blocksize_bits) + 1;
+ /* XXX 3 is for ext2 indirect blocks ... */
+ res_size = 2 * PRESTO_REQHIGH + ((blocks+3)
+ << file->f_dentry->d_inode->i_sb->s_blocksize_bits);
+
+ error = presto_reserve_space(fset->fset_cache, res_size);
+ CDEBUG(D_INODE, "Reserved %Ld for %d\n", res_size, size);
+ if ( error ) {
+ EXIT;
+ return -ENOSPC;
+ }
+
+ CDEBUG(D_INODE, "islento %d, minor: %d\n",
+ ISLENTO(cache->cache_psdev->uc_minor),
+ cache->cache_psdev->uc_minor);
+
+ /*
+ * XXX this lock should become a per inode lock when
+ * Vinny's changes are in; we could just use i_sem.
+ */
+ read_lock(&fset->fset_lml.fd_lock);
+ fdata = (struct presto_file_data *)file->private_data;
+ do_lml_here = size && (fdata->fd_do_lml == 0) &&
+ !presto_chk(file->f_dentry, PRESTO_DONT_JOURNAL);
+
+ if (do_lml_here)
+ fdata->fd_do_lml = 1;
+ read_unlock(&fset->fset_lml.fd_lock);
+
+ /* XXX
+ There might be a bug here. We need to make
+ absolutely sure that the ext3_file_write commits
+ after our transaction that writes the LML record.
+ Nesting the file write helps if new blocks are allocated.
+ */
+ res = 0;
+ if (do_lml_here) {
+ struct presto_version file_version;
+ /* handle different space reqs from file system below! */
+ handle = presto_trans_start(fset, file->f_dentry->d_inode,
+ KML_OPCODE_WRITE);
+ if ( IS_ERR(handle) ) {
+ presto_release_space(fset->fset_cache, res_size);
+ CERROR("presto_write: no space for transaction\n");
+ return -ENOSPC;
+ }
+
+ presto_getversion(&file_version, file->f_dentry->d_inode);
+ res = presto_write_lml_close(&rec, fset, file,
+ fdata->fd_info.remote_ino,
+ fdata->fd_info.remote_generation,
+ &fdata->fd_info.remote_version,
+ &file_version);
+ fdata->fd_lml_offset = rec.offset;
+ if ( res ) {
+ CERROR("intermezzo: PANIC failed to write LML\n");
+ *(int *)0 = 1;
+ EXIT;
+ goto exit_write;
+ }
+ presto_trans_commit(fset, handle);
+ }
+
+ fops = filter_c2cffops(cache->cache_filter);
+ res = fops->write(file, buf, size, off);
+ if ( res != size ) {
+ CDEBUG(D_FILE, "file write returns short write: size %d, res %d\n", size, res);
+ }
+
+ if ( (res > 0) && fdata )
+ presto_apply_write_policy(file, fset, res);
+
+ exit_write:
+ presto_release_space(fset->fset_cache, res_size);
+ return res;
+}
+
+struct file_operations presto_file_fops = {
+ .write = presto_file_write,
+ .open = presto_file_open,
+ .release = presto_file_release,
+ .ioctl = presto_ioctl
+};
+
+struct inode_operations presto_file_iops = {
+ .permission = presto_permission,
+ .setattr = presto_setattr,
+#ifdef CONFIG_FS_EXT_ATTR
+ .set_ext_attr = presto_set_ext_attr,
+#endif
+};
+
+/* FIXME: I bet we want to add a lock here and in presto_file_open. */
+int izo_purge_file(struct presto_file_set *fset, char *file)
+{
+#if 0
+ void *handle = NULL;
+ char *path = NULL;
+ struct nameidata nd;
+ struct dentry *dentry;
+ int rc = 0, len;
+ loff_t oldsize;
+
+ /* FIXME: not mtpt it's gone */
+ len = strlen(fset->fset_cache->cache_mtpt) + strlen(file) + 1;
+ PRESTO_ALLOC(path, len + 1);
+ if (path == NULL)
+ return -1;
+
+ sprintf(path, "%s/%s", fset->fset_cache->cache_mtpt, file);
+ rc = izo_lookup_file(fset, path, &nd);
+ if (rc)
+ goto error;
+ dentry = nd.dentry;
+
+ /* FIXME: take a lock here */
+
+ if (dentry->d_inode->i_atime > CURRENT_TIME - 5) {
+ /* We lost the race; this file was accessed while we were doing
+ * ioctls and lookups and whatnot. */
+ rc = -EBUSY;
+ goto error_unlock;
+ }
+
+ /* FIXME: Check if this file is open. */
+
+ handle = presto_trans_start(fset, dentry->d_inode, KML_OPCODE_TRUNC);
+ if (IS_ERR(handle)) {
+ rc = -ENOMEM;
+ goto error_unlock;
+ }
+
+ /* FIXME: Write LML record */
+
+ oldsize = dentry->d_inode->i_size;
+ rc = izo_do_truncate(fset, dentry, 0, oldsize);
+ if (rc != 0)
+ goto error_clear;
+ rc = izo_do_truncate(fset, dentry, oldsize, 0);
+ if (rc != 0)
+ goto error_clear;
+
+ error_clear:
+ /* FIXME: clear LML record */
+
+ error_unlock:
+ /* FIXME: release the lock here */
+
+ error:
+ if (handle != NULL && !IS_ERR(handle))
+ presto_trans_commit(fset, handle);
+ if (path != NULL)
+ PRESTO_FREE(path, len + 1);
+ return rc;
+#else
+ return 0;
+#endif
+}
diff --git a/fs/intermezzo/fileset.c b/fs/intermezzo/fileset.c
new file mode 100644
index 00000000000000..ebfa7e02d7ac4a
--- /dev/null
+++ b/fs/intermezzo/fileset.c
@@ -0,0 +1,677 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Copyright (C) 2001 Cluster File Systems, Inc. <braam@clusterfs.com>
+ *
+ * This file is part of InterMezzo, http://www.inter-mezzo.org.
+ *
+ * InterMezzo is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * InterMezzo 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 InterMezzo; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Managing filesets
+ *
+ */
+
+#define __NO_VERSION__
+#include <stdarg.h>
+
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/ext2_fs.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/blkdev.h>
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include <linux/intermezzo_fs.h>
+#include <linux/intermezzo_psdev.h>
+
+static inline struct presto_file_set *presto_dentry2fset(struct dentry *dentry)
+{
+ if (presto_d2d(dentry) == NULL) {
+ EXIT;
+ return NULL;
+ }
+ return presto_d2d(dentry)->dd_fset;
+}
+
+/* find the fileset dentry for this dentry */
+struct presto_file_set *presto_fset(struct dentry *de)
+{
+ struct dentry *fsde;
+ ENTRY;
+ if ( !de->d_inode ) {
+ /* FIXME: is this ok to be NULL? */
+ CDEBUG(D_INODE,"presto_fset: warning %*s has NULL inode.\n",
+ de->d_name.len, de->d_name.name);
+ }
+ for (fsde = de;; fsde = fsde->d_parent) {
+ if ( presto_dentry2fset(fsde) ) {
+ EXIT;
+ return presto_dentry2fset(fsde);
+ }
+ if (fsde->d_parent == fsde)
+ break;
+ }
+ EXIT;
+ return NULL;
+}
+
+int presto_get_lastrecno(char *path, off_t *recno)
+{
+ struct nameidata nd;
+ struct presto_file_set *fset;
+ struct dentry *dentry;
+ int error;
+ ENTRY;
+
+ error = presto_walk(path, &nd);
+ if (error) {
+ EXIT;
+ return error;
+ }
+
+ dentry = nd.dentry;
+
+ error = -ENXIO;
+ if ( !presto_ispresto(dentry->d_inode) ) {
+ EXIT;
+ goto kml_out;
+ }
+
+ error = -EINVAL;
+ if ( ! presto_dentry2fset(dentry)) {
+ EXIT;
+ goto kml_out;
+ }
+
+ fset = presto_dentry2fset(dentry);
+ if (!fset) {
+ EXIT;
+ goto kml_out;
+ }
+ error = 0;
+ *recno = fset->fset_kml.fd_recno;
+
+ kml_out:
+ path_release(&nd);
+ return error;
+}
+
+static char * _izo_make_path(char *fsetname, char *name)
+{
+ char *path = NULL;
+ int len;
+
+ len = strlen("/.intermezzo/") + strlen(fsetname)
+ + 1 + strlen(name) + 1;
+
+ PRESTO_ALLOC(path, len);
+ if (path == NULL)
+ return NULL;
+
+ sprintf(path, "/.intermezzo/%s/%s", fsetname, name);
+
+ return path;
+}
+
+char * izo_make_path(struct presto_file_set *fset, char *name)
+{
+ return _izo_make_path(fset->fset_name, name);
+}
+
+static struct file *_izo_fset_open(char *fsetname, char *name, int flags, int mode)
+{
+ char *path;
+ struct file *f;
+ int error;
+ ENTRY;
+
+ path = _izo_make_path(fsetname, name);
+ if (path == NULL) {
+ EXIT;
+ return ERR_PTR(-ENOMEM);
+ }
+
+ CDEBUG(D_INODE, "opening file %s\n", path);
+ f = filp_open(path, flags, mode);
+ error = PTR_ERR(f);
+ if (IS_ERR(f)) {
+ CDEBUG(D_INODE, "Error %d\n", error);
+ }
+
+ PRESTO_FREE(path, strlen(path)+1);
+
+ EXIT;
+ return f;
+
+}
+
+struct file *izo_fset_open(struct presto_file_set *fset, char *name, int flags, int mode)
+{
+ return _izo_fset_open(fset->fset_name, name, flags, mode);
+}
+
+
+
+/*
+ * note: this routine "pins" a dentry for a fileset root
+ */
+int presto_set_fsetroot(struct dentry *ioctl_dentry, char *fsetname,
+ unsigned int flags)
+{
+ struct presto_file_set *fset = NULL;
+ struct presto_cache *cache;
+ int error;
+ struct file *fset_root;
+ struct dentry *dentry;
+
+ ENTRY;
+
+ fset_root = _izo_fset_open(fsetname, "ROOT", O_RDONLY, 000);
+ if (IS_ERR(fset_root)) {
+ CERROR("Can't open %s/ROOT\n", fsetname);
+ EXIT;
+ error = PTR_ERR(fset_root);
+ goto out;
+ }
+ dentry = dget(fset_root->f_dentry);
+ filp_close(fset_root, NULL);
+
+ dentry->d_inode->i_op = ioctl_dentry->d_inode->i_op;
+ dentry->d_inode->i_fop = ioctl_dentry->d_inode->i_fop;
+ dentry->d_op = ioctl_dentry->d_op;
+ fset = presto_dentry2fset(dentry);
+ if (fset && (fset->fset_dentry == dentry) ) {
+ CERROR("Fsetroot already set (inode %ld)\n",
+ dentry->d_inode->i_ino);
+ /* XXX: ignore because clear_fsetroot is broken */
+#if 0
+ dput(dentry);
+ EXIT;
+ error = -EEXIST;
+ goto out;
+#endif
+ }
+
+ cache = presto_get_cache(dentry->d_inode);
+ if (!cache) {
+ CERROR("No cache found for inode %ld\n",
+ dentry->d_inode->i_ino);
+ EXIT;
+ error = -ENODEV;
+ goto out_free;
+ }
+
+ PRESTO_ALLOC(fset, sizeof(*fset));
+ if ( !fset ) {
+ CERROR("No memory allocating fset for %s\n", fsetname);
+ EXIT;
+ error = -ENOMEM;
+ goto out_free;
+ }
+ CDEBUG(D_INODE, "fset at %p\n", fset);
+
+ CDEBUG(D_INODE, "InterMezzo: fsetroot: inode %ld, fileset name %s\n",
+ dentry->d_inode->i_ino, fsetname);
+
+ fset->fset_mnt = mntget(current->fs->pwdmnt);
+ fset->fset_cache = cache;
+ fset->fset_dentry = dentry;
+ fset->fset_name = strdup(fsetname);
+ fset->fset_chunkbits = CHUNK_BITS;
+ fset->fset_flags = flags;
+ fset->fset_file_maxio = FSET_DEFAULT_MAX_FILEIO;
+ fset->fset_permit_lock = SPIN_LOCK_UNLOCKED;
+ PRESTO_ALLOC(fset->fset_reint_buf, 64 * 1024);
+ if (fset->fset_reint_buf == NULL) {
+ EXIT;
+ error = -ENOMEM;
+ goto out_free;
+ }
+ init_waitqueue_head(&fset->fset_permit_queue);
+
+ if (presto_d2d(dentry) == NULL) {
+ dentry->d_fsdata = izo_alloc_ddata();
+ }
+ if (presto_d2d(dentry) == NULL) {
+ CERROR("InterMezzo: %s: no memory\n", __FUNCTION__);
+ EXIT;
+ error = -ENOMEM;
+ goto out_free;
+ }
+
+ presto_d2d(dentry)->dd_fset = fset;
+ list_add(&fset->fset_list, &cache->cache_fset_list);
+
+ error = izo_init_kml_file(fset, &fset->fset_kml);
+ if ( error ) {
+ EXIT;
+ CDEBUG(D_JOURNAL, "Error init_kml %d\n", error);
+ goto out_list_del;
+ }
+
+ error = izo_init_lml_file(fset, &fset->fset_lml);
+ if ( error ) {
+ int rc;
+ EXIT;
+ rc = izo_log_close(&fset->fset_kml);
+ CDEBUG(D_JOURNAL, "Error init_lml %d, cleanup %d\n", error, rc);
+ goto out_list_del;
+ }
+
+ /* init_last_rcvd_file could trigger a presto_file_write(), which
+ * requires that the lml structure be initialized. -phil */
+ error = izo_init_last_rcvd_file(fset, &fset->fset_rcvd);
+ if ( error ) {
+ int rc;
+ EXIT;
+ rc = izo_log_close(&fset->fset_kml);
+ rc = izo_log_close(&fset->fset_lml);
+ CDEBUG(D_JOURNAL, "Error init_lastrcvd %d, cleanup %d\n", error, rc);
+ goto out_list_del;
+ }
+
+ CDEBUG(D_PIOCTL, "-------> fset at %p, dentry at %p, mtpt %p,"
+ "fset %s, cache %p, presto_d2d(dentry)->dd_fset %p\n",
+ fset, dentry, fset->fset_dentry, fset->fset_name, cache,
+ presto_d2d(dentry)->dd_fset);
+
+ EXIT;
+ return 0;
+
+ out_list_del:
+ list_del(&fset->fset_list);
+ presto_d2d(dentry)->dd_fset = NULL;
+ out_free:
+ if (fset) {
+ mntput(fset->fset_mnt);
+ if (fset->fset_reint_buf != NULL)
+ PRESTO_FREE(fset->fset_reint_buf, 64 * 1024);
+ PRESTO_FREE(fset, sizeof(*fset));
+ }
+ dput(dentry);
+ out:
+ return error;
+}
+
+static int izo_cleanup_fset(struct presto_file_set *fset)
+{
+ int error;
+ struct presto_cache *cache;
+
+ ENTRY;
+
+ CERROR("Cleaning up fset %s\n", fset->fset_name);
+
+ error = izo_log_close(&fset->fset_kml);
+ if (error)
+ CERROR("InterMezzo: Closing kml for fset %s: %d\n",
+ fset->fset_name, error);
+ error = izo_log_close(&fset->fset_lml);
+ if (error)
+ CERROR("InterMezzo: Closing lml for fset %s: %d\n",
+ fset->fset_name, error);
+ error = izo_log_close(&fset->fset_rcvd);
+ if (error)
+ CERROR("InterMezzo: Closing last_rcvd for fset %s: %d\n",
+ fset->fset_name, error);
+
+ cache = fset->fset_cache;
+
+ list_del(&fset->fset_list);
+
+ presto_d2d(fset->fset_dentry)->dd_fset = NULL;
+ dput(fset->fset_dentry);
+ mntput(fset->fset_mnt);
+
+ izo_rep_cache_clean(fset);
+ PRESTO_FREE(fset->fset_name, strlen(fset->fset_name) + 1);
+ PRESTO_FREE(fset->fset_reint_buf, 64 * 1024);
+ PRESTO_FREE(fset, sizeof(*fset));
+ EXIT;
+ return error;
+}
+
+int izo_clear_fsetroot(struct dentry *dentry)
+{
+ struct presto_file_set *fset;
+
+ ENTRY;
+
+ fset = presto_dentry2fset(dentry);
+ if (!fset) {
+ EXIT;
+ return -EINVAL;
+ }
+
+ izo_cleanup_fset(fset);
+ EXIT;
+ return 0;
+}
+
+int izo_clear_all_fsetroots(struct presto_cache *cache)
+{
+ struct presto_file_set *fset;
+ struct list_head *tmp,*tmpnext;
+ int error;
+
+ error = 0;
+ tmp = &cache->cache_fset_list;
+ tmpnext = tmp->next;
+ while ( tmpnext != &cache->cache_fset_list) {
+ tmp = tmpnext;
+ tmpnext = tmp->next;
+ fset = list_entry(tmp, struct presto_file_set, fset_list);
+
+ error = izo_cleanup_fset(fset);
+ if (error)
+ break;
+ }
+ return error;
+}
+
+static struct vfsmount *izo_alloc_vfsmnt(void)
+{
+ struct vfsmount *mnt;
+ PRESTO_ALLOC(mnt, sizeof(*mnt));
+ if (mnt) {
+ memset(mnt, 0, sizeof(struct vfsmount));
+ atomic_set(&mnt->mnt_count,1);
+ INIT_LIST_HEAD(&mnt->mnt_hash);
+ INIT_LIST_HEAD(&mnt->mnt_child);
+ INIT_LIST_HEAD(&mnt->mnt_mounts);
+ INIT_LIST_HEAD(&mnt->mnt_list);
+ }
+ return mnt;
+}
+
+
+static void izo_setup_ctxt(struct dentry *root, struct vfsmount *mnt,
+ struct run_ctxt *save)
+{
+ struct run_ctxt new;
+
+ mnt->mnt_root = root;
+ mnt->mnt_sb = root->d_inode->i_sb;
+ unlock_super(mnt->mnt_sb);
+
+ new.rootmnt = mnt;
+ new.root = root;
+ new.pwdmnt = mnt;
+ new.pwd = root;
+ new.fsuid = 0;
+ new.fsgid = 0;
+ new.fs = get_fs();
+ /* XXX where can we get the groups from? */
+ new.ngroups = 0;
+
+ push_ctxt(save, &new);
+}
+
+static void izo_cleanup_ctxt(struct vfsmount *mnt, struct run_ctxt *save)
+{
+ lock_super(mnt->mnt_sb);
+ pop_ctxt(save);
+}
+
+static int izo_simple_mkdir(struct dentry *dir, char *name, int mode)
+{
+ struct dentry *dchild;
+ int err;
+ ENTRY;
+
+ dchild = lookup_one_len(name, dir, strlen(name));
+ if (IS_ERR(dchild)) {
+ EXIT;
+ return PTR_ERR(dchild);
+ }
+
+ if (dchild->d_inode) {
+ dput(dchild);
+ EXIT;
+ return -EEXIST;
+ }
+
+ err = vfs_mkdir(dir->d_inode, dchild, mode);
+ dput(dchild);
+
+ EXIT;
+ return err;
+}
+
+static int izo_simple_symlink(struct dentry *dir, char *name, char *tgt)
+{
+ struct dentry *dchild;
+ int err;
+ ENTRY;
+
+ dchild = lookup_one_len(name, dir, strlen(name));
+ if (IS_ERR(dchild)) {
+ EXIT;
+ return PTR_ERR(dchild);
+ }
+
+ if (dchild->d_inode) {
+ dput(dchild);
+ EXIT;
+ return -EEXIST;
+ }
+
+ err = vfs_symlink(dir->d_inode, dchild, tgt);
+ dput(dchild);
+
+ EXIT;
+ return err;
+}
+
+/*
+ * run set_fsetroot in chroot environment
+ */
+int presto_set_fsetroot_from_ioc(struct dentry *root, char *fsetname,
+ unsigned int flags)
+{
+ int rc;
+ struct presto_cache *cache;
+ struct vfsmount *mnt;
+ struct run_ctxt save;
+
+ if (root != root->d_inode->i_sb->s_root) {
+ CERROR ("IOC_SET_FSET must be called on mount point\n");
+ return -ENODEV;
+ }
+
+ cache = presto_get_cache(root->d_inode);
+ mnt = cache->cache_vfsmount;
+ if (!mnt) {
+ EXIT;
+ return -ENOMEM;
+ }
+
+ izo_setup_ctxt(root, mnt, &save);
+ rc = presto_set_fsetroot(root, fsetname, flags);
+ izo_cleanup_ctxt(mnt, &save);
+ return rc;
+}
+
+/* XXX: this function should detect if fsetname is already in use for
+ the cache under root
+*/
+int izo_prepare_fileset(struct dentry *root, char *fsetname)
+{
+ int err;
+ struct dentry *dotizo = NULL, *fsetdir = NULL, *dotiopen = NULL;
+ struct presto_cache *cache;
+ struct vfsmount *mnt;
+ struct run_ctxt save;
+
+ cache = presto_get_cache(root->d_inode);
+ mnt = cache->cache_vfsmount = izo_alloc_vfsmnt();
+ if (!mnt) {
+ EXIT;
+ return -ENOMEM;
+ }
+
+ if (!fsetname)
+ fsetname = "rootfset";
+
+ izo_setup_ctxt(root, mnt, &save);
+
+ err = izo_simple_mkdir(root, ".intermezzo", 0755);
+ CDEBUG(D_CACHE, "mkdir on .intermezzo err %d\n", err);
+
+ err = izo_simple_mkdir(root, "..iopen..", 0755);
+ CDEBUG(D_CACHE, "mkdir on ..iopen.. err %d\n", err);
+
+ dotiopen = lookup_one_len("..iopen..", root, strlen("..iopen.."));
+ if (IS_ERR(dotiopen)) {
+ EXIT;
+ goto out;
+ }
+ dotiopen->d_inode->i_op = &presto_dir_iops;
+ dput(dotiopen);
+
+
+ dotizo = lookup_one_len(".intermezzo", root, strlen(".intermezzo"));
+ if (IS_ERR(dotizo)) {
+ EXIT;
+ goto out;
+ }
+
+
+ err = izo_simple_mkdir(dotizo, fsetname, 0755);
+ CDEBUG(D_CACHE, "mkdir err %d\n", err);
+
+ /* XXX find the dentry of the root of the fileset (root for now) */
+ fsetdir = lookup_one_len(fsetname, dotizo, strlen(fsetname));
+ if (IS_ERR(fsetdir)) {
+ EXIT;
+ goto out;
+ }
+
+ err = izo_simple_symlink(fsetdir, "ROOT", "../..");
+
+ /* XXX read flags from flags file */
+ err = presto_set_fsetroot(root, fsetname, 0);
+ CDEBUG(D_CACHE, "set_fsetroot err %d\n", err);
+
+ out:
+ if (dotizo && !IS_ERR(dotizo))
+ dput(dotizo);
+ if (fsetdir && !IS_ERR(fsetdir))
+ dput(fsetdir);
+ izo_cleanup_ctxt(mnt, &save);
+ return err;
+}
+
+int izo_set_fileid(struct file *dir, struct izo_ioctl_data *data)
+{
+ int rc = 0;
+ struct presto_cache *cache;
+ struct vfsmount *mnt;
+ struct run_ctxt save;
+ struct nameidata nd;
+ struct dentry *dentry;
+ struct presto_dentry_data *dd;
+ struct dentry *root;
+ char *buf = NULL;
+
+ ENTRY;
+
+
+ root = dir->f_dentry;
+
+ /* actually, needs to be called on ROOT of fset, not mount point
+ if (root != root->d_inode->i_sb->s_root) {
+ CERROR ("IOC_SET_FSET must be called on mount point\n");
+ return -ENODEV;
+ }
+ */
+
+ cache = presto_get_cache(root->d_inode);
+ mnt = cache->cache_vfsmount;
+ if (!mnt) {
+ EXIT;
+ return -ENOMEM;
+ }
+
+ izo_setup_ctxt(root, mnt, &save);
+
+ PRESTO_ALLOC(buf, data->ioc_plen1);
+ if (!buf) {
+ rc = -ENOMEM;
+ EXIT;
+ goto out;
+ }
+ if (copy_from_user(buf, data->ioc_pbuf1, data->ioc_plen1)) {
+ rc = -EFAULT;
+ EXIT;
+ goto out;
+ }
+
+ rc = presto_walk(buf, &nd);
+ if (rc) {
+ CERROR("Unable to open: %s\n", buf);
+ EXIT;
+ goto out;
+ }
+ dentry = nd.dentry;
+ if (!dentry) {
+ CERROR("no dentry!\n");
+ rc = -EINVAL;
+ EXIT;
+ goto out_close;
+ }
+ dd = presto_d2d(dentry);
+ if (!dd) {
+ CERROR("no dentry_data!\n");
+ rc = -EINVAL;
+ EXIT;
+ goto out_close;
+ }
+
+ CDEBUG(D_FILE,"de:%p dd:%p\n", dentry, dd);
+
+ if (dd->remote_ino != 0) {
+ CERROR("remote_ino already set? %Lx:%Lx\n", dd->remote_ino,
+ dd->remote_generation);
+ rc = 0;
+ EXIT;
+ goto out_close;
+ }
+
+
+ CDEBUG(D_FILE,"setting %p %p, %s to %Lx:%Lx\n", dentry, dd,
+ buf, data->ioc_ino,
+ data->ioc_generation);
+ dd->remote_ino = data->ioc_ino;
+ dd->remote_generation = data->ioc_generation;
+
+ EXIT;
+ out_close:
+ path_release(&nd);
+ out:
+ if (buf)
+ PRESTO_FREE(buf, data->ioc_plen1);
+ izo_cleanup_ctxt(mnt, &save);
+ return rc;
+}
diff --git a/fs/intermezzo/inode.c b/fs/intermezzo/inode.c
new file mode 100644
index 00000000000000..6e1d8154579d3d
--- /dev/null
+++ b/fs/intermezzo/inode.c
@@ -0,0 +1,187 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Copyright (C) 1996 Peter J. Braam <braam@maths.ox.ac.uk> and
+ * Michael Callahan <callahan@maths.ox.ac.uk>
+ * Copyright (C) 1999 Carnegie Mellon University
+ * Rewritten for Linux 2.1. Peter Braam <braam@cs.cmu.edu>
+ *
+ * This file is part of InterMezzo, http://www.inter-mezzo.org.
+ *
+ * InterMezzo is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * InterMezzo 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 InterMezzo; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Super block/filesystem wide operations
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <linux/unistd.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <linux/string.h>
+#include <asm/uaccess.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <asm/segment.h>
+
+#include <linux/intermezzo_fs.h>
+#include <linux/intermezzo_psdev.h>
+
+extern void presto_free_cache(struct presto_cache *);
+
+void presto_set_ops(struct inode *inode, struct filter_fs *filter)
+{
+ ENTRY;
+
+ if (!inode || is_bad_inode(inode))
+ return;
+
+ if (S_ISREG(inode->i_mode)) {
+ if ( !filter_c2cfiops(filter) ) {
+ filter_setup_file_ops(filter,
+ inode, &presto_file_iops,
+ &presto_file_fops);
+ }
+ inode->i_op = filter_c2ufiops(filter);
+ inode->i_fop = filter_c2uffops(filter);
+ CDEBUG(D_INODE, "set file methods for %ld to %p\n",
+ inode->i_ino, inode->i_op);
+ } else if (S_ISDIR(inode->i_mode)) {
+ inode->i_op = filter_c2udiops(filter);
+ inode->i_fop = filter_c2udfops(filter);
+ CDEBUG(D_INODE, "set dir methods for %ld to %p ioctl %p\n",
+ inode->i_ino, inode->i_op, inode->i_fop->ioctl);
+ } else if (S_ISLNK(inode->i_mode)) {
+ if ( !filter_c2csiops(filter)) {
+ filter_setup_symlink_ops(filter,
+ inode,
+ &presto_sym_iops,
+ &presto_sym_fops);
+ }
+ inode->i_op = filter_c2usiops(filter);
+ inode->i_fop = filter_c2usfops(filter);
+ CDEBUG(D_INODE, "set link methods for %ld to %p\n",
+ inode->i_ino, inode->i_op);
+ }
+ EXIT;
+}
+
+void presto_read_inode(struct inode *inode)
+{
+ struct presto_cache *cache;
+
+ cache = presto_get_cache(inode);
+ if ( !cache ) {
+ CERROR("PRESTO: BAD, BAD: cannot find cache\n");
+ make_bad_inode(inode);
+ return ;
+ }
+
+ filter_c2csops(cache->cache_filter)->read_inode(inode);
+
+ CDEBUG(D_INODE, "presto_read_inode: ino %ld, gid %d\n",
+ inode->i_ino, inode->i_gid);
+
+ presto_set_ops(inode, cache->cache_filter);
+ /* XXX handle special inodes here or not - probably not? */
+}
+
+static void presto_put_super(struct super_block *sb)
+{
+ struct presto_cache *cache;
+ struct upc_channel *channel;
+ struct super_operations *sops;
+ struct list_head *lh;
+ int err;
+
+ ENTRY;
+ cache = presto_cache_find(sb->s_dev);
+ if (!cache) {
+ EXIT;
+ goto exit;
+ }
+ channel = &izo_channels[presto_c2m(cache)];
+ sops = filter_c2csops(cache->cache_filter);
+ err = izo_clear_all_fsetroots(cache);
+ if (err) {
+ CERROR("%s: err %d\n", __FUNCTION__, err);
+ }
+ PRESTO_FREE(cache->cache_vfsmount, sizeof(struct vfsmount));
+
+ /* look at kill_super - fsync_super is not exported GRRR but
+ probably not needed */
+ unlock_super(sb);
+ shrink_dcache_parent(cache->cache_root);
+ dput(cache->cache_root);
+ //fsync_super(sb);
+ lock_super(sb);
+
+ if (sops->write_super)
+ sops->write_super(sb);
+
+ if (sops->put_super)
+ sops->put_super(sb);
+
+ /* free any remaining async upcalls when the filesystem is unmounted */
+ spin_lock(&channel->uc_lock);
+ lh = channel->uc_pending.next;
+ while ( lh != &channel->uc_pending) {
+ struct upc_req *req;
+ req = list_entry(lh, struct upc_req, rq_chain);
+
+ /* assignment must be here: we are about to free &lh */
+ lh = lh->next;
+ if ( ! (req->rq_flags & REQ_ASYNC) )
+ continue;
+ list_del(&(req->rq_chain));
+ PRESTO_FREE(req->rq_data, req->rq_bufsize);
+ PRESTO_FREE(req, sizeof(struct upc_req));
+ }
+ list_del(&cache->cache_channel_list);
+ spin_unlock(&channel->uc_lock);
+
+ presto_free_cache(cache);
+
+exit:
+ CDEBUG(D_MALLOC, "after umount: kmem %ld, vmem %ld\n",
+ presto_kmemory, presto_vmemory);
+ MOD_DEC_USE_COUNT;
+ return ;
+}
+
+struct super_operations presto_super_ops = {
+ .read_inode = presto_read_inode,
+ .put_super = presto_put_super,
+};
+
+
+/* symlinks can be chowned */
+struct inode_operations presto_sym_iops = {
+ .setattr = presto_setattr
+};
+
+/* NULL for now */
+struct file_operations presto_sym_fops;
diff --git a/fs/intermezzo/journal.c b/fs/intermezzo/journal.c
new file mode 100644
index 00000000000000..2f68e5acecf907
--- /dev/null
+++ b/fs/intermezzo/journal.c
@@ -0,0 +1,2453 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Copyright (C) 1998 Peter J. Braam
+ * Copyright (C) 2001 Cluster File Systems, Inc.
+ * Copyright (C) 2001 Tacit Networks, Inc. <phil@off.net>
+ *
+ * Support for journalling extended attributes
+ * Copyright (C) 2001 Shirish H. Phatak, Tacit Networks, Inc.
+ *
+ * This file is part of InterMezzo, http://www.inter-mezzo.org.
+ *
+ * InterMezzo is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * InterMezzo 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 InterMezzo; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/time.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+#include <linux/string.h>
+#include <linux/smp_lock.h>
+#include <linux/intermezzo_fs.h>
+#include <linux/intermezzo_psdev.h>
+
+struct presto_reservation_data {
+ unsigned int ri_recno;
+ loff_t ri_offset;
+ loff_t ri_size;
+ struct list_head ri_list;
+};
+
+/*
+ * Locking Semantics
+ *
+ * write lock in struct presto_log_fd:
+ * - name: fd_lock
+ * - required for: accessing any field in a presto_log_fd
+ * - may not be held across I/O
+ * -
+ *
+ */
+
+/*
+ * reserve record space and/or atomically request state of the log
+ * rec will hold the location reserved record upon return
+ * this reservation will be placed in the queue
+ */
+static void presto_reserve_record(struct presto_file_set *fset,
+ struct presto_log_fd *fd,
+ struct rec_info *rec,
+ struct presto_reservation_data *rd)
+{
+ int chunked_record = 0;
+ ENTRY;
+
+ write_lock(&fd->fd_lock);
+ if ( rec->is_kml ) {
+ int chunk = 1 << fset->fset_chunkbits;
+ int chunk_mask = ~(chunk -1);
+ loff_t boundary;
+
+ boundary = (fd->fd_offset + chunk - 1) & chunk_mask;
+ if ( fd->fd_offset + rec->size >= boundary ) {
+ chunked_record = 1;
+ fd->fd_offset = boundary;
+ }
+ }
+
+ fd->fd_recno++;
+
+ /* this moves the fd_offset back after truncation */
+ if ( list_empty(&fd->fd_reservations) &&
+ !chunked_record) {
+ fd->fd_offset = fd->fd_file->f_dentry->d_inode->i_size;
+ }
+
+ rec->offset = fd->fd_offset;
+ if (rec->is_kml)
+ rec->offset += fset->fset_kml_logical_off;
+
+ rec->recno = fd->fd_recno;
+
+ /* add the reservation data to the end of the list */
+ rd->ri_offset = fd->fd_offset;
+ rd->ri_size = rec->size;
+ rd->ri_recno = rec->recno;
+ list_add(&rd->ri_list, fd->fd_reservations.prev);
+
+ fd->fd_offset += rec->size;
+
+ write_unlock(&fd->fd_lock);
+
+ EXIT;
+}
+
+static inline void presto_release_record(struct presto_log_fd *fd,
+ struct presto_reservation_data *rd)
+{
+ write_lock(&fd->fd_lock);
+ list_del(&rd->ri_list);
+ write_unlock(&fd->fd_lock);
+}
+
+/* XXX should we ask for do_truncate to be exported? */
+int izo_do_truncate(struct presto_file_set *fset, struct dentry *dentry,
+ loff_t length, loff_t size_check)
+{
+ struct inode *inode = dentry->d_inode;
+ int error;
+ struct iattr newattrs;
+
+ ENTRY;
+
+ if (length < 0) {
+ EXIT;
+ return -EINVAL;
+ }
+
+ down(&inode->i_sem);
+ lock_kernel();
+
+ if (size_check != inode->i_size) {
+ unlock_kernel();
+ up(&inode->i_sem);
+ EXIT;
+ return -EALREADY;
+ }
+
+ newattrs.ia_size = length;
+ newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME;
+
+ if (inode->i_op && inode->i_op->setattr)
+ error = inode->i_op->setattr(dentry, &newattrs);
+ else {
+ inode_setattr(dentry->d_inode, &newattrs);
+ error = 0;
+ }
+
+ unlock_kernel();
+ up(&inode->i_sem);
+ EXIT;
+ return error;
+}
+
+static void presto_kml_truncate(struct presto_file_set *fset)
+{
+ int rc;
+ ENTRY;
+
+ write_lock(&fset->fset_kml.fd_lock);
+ if (fset->fset_kml.fd_truncating == 1 ) {
+ write_unlock(&fset->fset_kml.fd_lock);
+ EXIT;
+ return;
+ }
+
+ fset->fset_kml.fd_truncating = 1;
+ write_unlock(&fset->fset_kml.fd_lock);
+
+ CERROR("islento: %d, count: %d\n",
+ ISLENTO(presto_i2m(fset->fset_dentry->d_inode)),
+ fset->fset_permit_count);
+
+ rc = izo_upc_kml_truncate(fset->fset_cache->cache_psdev->uc_minor,
+ fset->fset_lento_off, fset->fset_lento_recno,
+ fset->fset_name);
+
+ /* Userspace is the only permitholder now, and will retain an exclusive
+ * hold on the permit until KML truncation completes. */
+ /* FIXME: double check this code path now that the precise semantics of
+ * fset->fset_permit_count have changed. */
+
+ if (rc != 0) {
+ write_lock(&fset->fset_kml.fd_lock);
+ fset->fset_kml.fd_truncating = 0;
+ write_unlock(&fset->fset_kml.fd_lock);
+ }
+
+ EXIT;
+}
+
+void *presto_trans_start(struct presto_file_set *fset, struct inode *inode,
+ int op)
+{
+ ENTRY;
+ if ( !fset->fset_cache->cache_filter->o_trops ) {
+ EXIT;
+ return NULL;
+ }
+ EXIT;
+ return fset->fset_cache->cache_filter->o_trops->tr_start
+ (fset, inode, op);
+}
+
+void presto_trans_commit(struct presto_file_set *fset, void *handle)
+{
+ ENTRY;
+ if (!fset->fset_cache->cache_filter->o_trops ) {
+ EXIT;
+ return;
+ }
+
+ fset->fset_cache->cache_filter->o_trops->tr_commit(fset, handle);
+
+ /* Check to see if the KML needs truncated. */
+ if (fset->kml_truncate_size > 0 &&
+ !fset->fset_kml.fd_truncating &&
+ fset->fset_kml.fd_offset > fset->kml_truncate_size) {
+ CDEBUG(D_JOURNAL, "kml size: %lu; truncating\n",
+ (unsigned long)fset->fset_kml.fd_offset);
+ presto_kml_truncate(fset);
+ }
+ EXIT;
+}
+
+inline int presto_no_journal(struct presto_file_set *fset)
+{
+ int minor = fset->fset_cache->cache_psdev->uc_minor;
+ return izo_channels[minor].uc_no_journal;
+}
+
+#define size_round(x) (((x)+3) & ~0x3)
+
+#define BUFF_FREE(buf) PRESTO_FREE(buf, PAGE_SIZE)
+#define BUFF_ALLOC(newbuf, oldbuf) \
+ PRESTO_ALLOC(newbuf, PAGE_SIZE); \
+ if ( !newbuf ) { \
+ if (oldbuf) \
+ BUFF_FREE(oldbuf); \
+ return -ENOMEM; \
+ }
+
+/*
+ * "buflen" should be PAGE_SIZE or more.
+ * Give relative path wrt to a fsetroot
+ */
+char * presto_path(struct dentry *dentry, struct dentry *root,
+ char *buffer, int buflen)
+{
+ char * end = buffer+buflen;
+ char * retval;
+
+ *--end = '\0';
+ buflen--;
+ if (dentry->d_parent != dentry && list_empty(&dentry->d_hash)) {
+ buflen -= 10;
+ end -= 10;
+ memcpy(end, " (deleted)", 10);
+ }
+
+ /* Get '/' right */
+ retval = end-1;
+ *retval = '/';
+
+ for (;;) {
+ struct dentry * parent;
+ int namelen;
+
+ if (dentry == root)
+ break;
+ parent = dentry->d_parent;
+ if (dentry == parent)
+ break;
+ namelen = dentry->d_name.len;
+ buflen -= namelen + 1;
+ if (buflen < 0)
+ break;
+ end -= namelen;
+ memcpy(end, dentry->d_name.name, namelen);
+ *--end = '/';
+ retval = end;
+ dentry = parent;
+ }
+ return retval;
+}
+
+static inline char *logit(char *buf, const void *value, int size)
+{
+ char *ptr = (char *)value;
+
+ memcpy(buf, ptr, size);
+ buf += size;
+ return buf;
+}
+
+
+static inline char *
+journal_log_prefix_with_groups_and_ids(char *buf, int opcode,
+ struct rec_info *rec,
+ __u32 ngroups, gid_t *groups,
+ __u32 fsuid, __u32 fsgid)
+{
+ struct kml_prefix_hdr p;
+ u32 loggroups[NGROUPS_MAX];
+
+ int i;
+
+ p.len = cpu_to_le32(rec->size);
+ p.version = KML_MAJOR_VERSION | KML_MINOR_VERSION;
+ p.pid = cpu_to_le32(current->pid);
+ p.auid = cpu_to_le32(current->uid);
+ p.fsuid = cpu_to_le32(fsuid);
+ p.fsgid = cpu_to_le32(fsgid);
+ p.ngroups = cpu_to_le32(ngroups);
+ p.opcode = cpu_to_le32(opcode);
+ for (i=0 ; i < ngroups ; i++)
+ loggroups[i] = cpu_to_le32((__u32) groups[i]);
+
+ buf = logit(buf, &p, sizeof(struct kml_prefix_hdr));
+ buf = logit(buf, &loggroups, sizeof(__u32) * ngroups);
+ return buf;
+}
+
+static inline char *
+journal_log_prefix(char *buf, int opcode, struct rec_info *rec)
+{
+ __u32 groups[NGROUPS_MAX];
+ int i;
+
+ /* convert 16 bit gid's to 32 bit gid's */
+ for (i=0; i<current->ngroups; i++)
+ groups[i] = (__u32) current->groups[i];
+
+ return journal_log_prefix_with_groups_and_ids(buf, opcode, rec,
+ (__u32)current->ngroups,
+ groups,
+ (__u32)current->fsuid,
+ (__u32)current->fsgid);
+}
+
+static inline char *
+journal_log_prefix_with_groups(char *buf, int opcode, struct rec_info *rec,
+ __u32 ngroups, gid_t *groups)
+{
+ return journal_log_prefix_with_groups_and_ids(buf, opcode, rec,
+ ngroups, groups,
+ (__u32)current->fsuid,
+ (__u32)current->fsgid);
+}
+
+static inline char *log_dentry_version(char *buf, struct dentry *dentry)
+{
+ struct presto_version version;
+
+ presto_getversion(&version, dentry->d_inode);
+
+ version.pv_mtime = HTON__u64(version.pv_mtime);
+ version.pv_ctime = HTON__u64(version.pv_ctime);
+ version.pv_size = HTON__u64(version.pv_size);
+
+ return logit(buf, &version, sizeof(version));
+}
+
+static inline char *log_version(char *buf, struct presto_version *pv)
+{
+ struct presto_version version;
+
+ memcpy(&version, pv, sizeof(version));
+
+ version.pv_mtime = HTON__u64(version.pv_mtime);
+ version.pv_ctime = HTON__u64(version.pv_ctime);
+ version.pv_size = HTON__u64(version.pv_size);
+
+ return logit(buf, &version, sizeof(version));
+}
+
+static inline char *log_rollback(char *buf, struct izo_rollback_data *rb)
+{
+ struct izo_rollback_data rollback;
+
+ memcpy(&rollback, rb, sizeof(rollback));
+
+ rollback.rb_mode = HTON__u32(rollback.rb_mode);
+ rollback.rb_rdev = HTON__u32(rollback.rb_rdev);
+ rollback.rb_uid = HTON__u64(rollback.rb_uid);
+ rollback.rb_gid = HTON__u64(rollback.rb_gid);
+
+ return logit(buf, &rollback, sizeof(rollback));
+}
+
+static inline char *journal_log_suffix(char *buf, char *log,
+ struct presto_file_set *fset,
+ struct dentry *dentry,
+ struct rec_info *rec)
+{
+ struct kml_suffix s;
+ struct kml_prefix_hdr *p = (struct kml_prefix_hdr *)log;
+
+#if 0
+ /* XXX needs to be done after reservation,
+ disable ths until version 1.2 */
+ if ( dentry ) {
+ s.prevrec = cpu_to_le32(rec->offset -
+ presto_d2d(dentry)->dd_kml_offset);
+ presto_d2d(dentry)->dd_kml_offset = rec->offset;
+ } else {
+ s.prevrec = -1;
+ }
+#endif
+ s.prevrec = 0;
+
+ /* record number needs to be filled in after reservation
+ s.recno = cpu_to_le32(rec->recno); */
+ s.time = cpu_to_le32(CURRENT_TIME);
+ s.len = p->len;
+ return logit(buf, &s, sizeof(s));
+}
+
+int izo_log_close(struct presto_log_fd *logfd)
+{
+ int rc = 0;
+
+ if (logfd->fd_file) {
+ rc = filp_close(logfd->fd_file, 0);
+ logfd->fd_file = NULL;
+ } else
+ CERROR("InterMezzo: %s: no filp\n", __FUNCTION__);
+ if (rc != 0)
+ CERROR("InterMezzo: close files: filp won't close: %d\n", rc);
+
+ return rc;
+}
+
+int presto_fwrite(struct file *file, const char *str, int len, loff_t *off)
+{
+ int rc;
+ mm_segment_t old_fs;
+ ENTRY;
+
+ rc = -EINVAL;
+ if ( !off ) {
+ EXIT;
+ return rc;
+ }
+
+ if ( ! file ) {
+ EXIT;
+ return rc;
+ }
+
+ if ( ! file->f_op ) {
+ EXIT;
+ return rc;
+ }
+
+ if ( ! file->f_op->write ) {
+ EXIT;
+ return rc;
+ }
+
+ old_fs = get_fs();
+ set_fs(get_ds());
+ rc = file->f_op->write(file, str, len, off);
+ if (rc != len) {
+ CERROR("presto_fwrite: wrote %d bytes instead of "
+ "%d at %ld\n", rc, len, (long)*off);
+ rc = -EIO;
+ }
+ set_fs(old_fs);
+ EXIT;
+ return rc;
+}
+
+int presto_fread(struct file *file, char *str, int len, loff_t *off)
+{
+ int rc;
+ mm_segment_t old_fs;
+ ENTRY;
+
+ if (len > 512)
+ CERROR("presto_fread: read at %Ld for %d bytes, ino %ld\n",
+ *off, len, file->f_dentry->d_inode->i_ino);
+
+ rc = -EINVAL;
+ if ( !off ) {
+ EXIT;
+ return rc;
+ }
+
+ if ( ! file ) {
+ EXIT;
+ return rc;
+ }
+
+ if ( ! file->f_op ) {
+ EXIT;
+ return rc;
+ }
+
+ if ( ! file->f_op->read ) {
+ EXIT;
+ return rc;
+ }
+
+ old_fs = get_fs();
+ set_fs(get_ds());
+ rc = file->f_op->read(file, str, len, off);
+ if (rc != len) {
+ CDEBUG(D_FILE, "presto_fread: read %d bytes instead of "
+ "%d at %Ld\n", rc, len, *off);
+ rc = -EIO;
+ }
+ set_fs(old_fs);
+ EXIT;
+ return rc;
+}
+
+loff_t presto_kml_offset(struct presto_file_set *fset)
+{
+ unsigned int kml_recno;
+ struct presto_log_fd *fd = &fset->fset_kml;
+ loff_t offset;
+ ENTRY;
+
+ write_lock(&fd->fd_lock);
+
+ /* Determine the largest valid offset, i.e. up until the first
+ * reservation held on the file. */
+ if ( !list_empty(&fd->fd_reservations) ) {
+ struct presto_reservation_data *rd;
+ rd = list_entry(fd->fd_reservations.next,
+ struct presto_reservation_data,
+ ri_list);
+ offset = rd->ri_offset;
+ kml_recno = rd->ri_recno;
+ } else {
+ offset = fd->fd_file->f_dentry->d_inode->i_size;
+ kml_recno = fset->fset_kml.fd_recno;
+ }
+ write_unlock(&fd->fd_lock);
+ return offset;
+}
+
+static int presto_kml_dispatch(struct presto_file_set *fset)
+{
+ int rc = 0;
+ unsigned int kml_recno;
+ struct presto_log_fd *fd = &fset->fset_kml;
+ loff_t offset;
+ ENTRY;
+
+ write_lock(&fd->fd_lock);
+
+ /* Determine the largest valid offset, i.e. up until the first
+ * reservation held on the file. */
+ if ( !list_empty(&fd->fd_reservations) ) {
+ struct presto_reservation_data *rd;
+ rd = list_entry(fd->fd_reservations.next,
+ struct presto_reservation_data,
+ ri_list);
+ offset = rd->ri_offset;
+ kml_recno = rd->ri_recno;
+ } else {
+ offset = fd->fd_file->f_dentry->d_inode->i_size;
+ kml_recno = fset->fset_kml.fd_recno;
+ }
+
+ if ( kml_recno < fset->fset_lento_recno ) {
+ CERROR("presto_kml_dispatch: smoke is coming\n");
+ write_unlock(&fd->fd_lock);
+ EXIT;
+ return 0;
+ } else if ( kml_recno == fset->fset_lento_recno ) {
+ write_unlock(&fd->fd_lock);
+ EXIT;
+ return 0;
+ /* XXX add a further "if" here to delay the KML upcall */
+#if 0
+ } else if ( kml_recno < fset->fset_lento_recno + 100) {
+ write_unlock(&fd->fd_lock);
+ EXIT;
+ return 0;
+#endif
+ }
+ CDEBUG(D_PIOCTL, "fset: %s\n", fset->fset_name);
+
+ rc = izo_upc_kml(fset->fset_cache->cache_psdev->uc_minor,
+ fset->fset_lento_off, fset->fset_lento_recno,
+ offset + fset->fset_kml_logical_off, kml_recno,
+ fset->fset_name);
+
+ if ( rc ) {
+ write_unlock(&fd->fd_lock);
+ EXIT;
+ return rc;
+ }
+
+ fset->fset_lento_off = offset;
+ fset->fset_lento_recno = kml_recno;
+ write_unlock(&fd->fd_lock);
+ EXIT;
+ return 0;
+}
+
+int izo_lookup_file(struct presto_file_set *fset, char *path,
+ struct nameidata *nd)
+{
+ int error = 0;
+
+ CDEBUG(D_CACHE, "looking up: %s\n", path);
+
+ if (path_init(path, LOOKUP_PARENT, nd))
+ error = path_walk(path, nd);
+ if (error) {
+ EXIT;
+ return error;
+ }
+
+ return 0;
+}
+
+/* FIXME: this function is a mess of locking and error handling. There's got to
+ * be a better way. */
+static int do_truncate_rename(struct presto_file_set *fset, char *oldname,
+ char *newname)
+{
+ struct dentry *old_dentry, *new_dentry;
+ struct nameidata oldnd, newnd;
+ char *oldpath, *newpath;
+ int error;
+
+ ENTRY;
+
+ oldpath = izo_make_path(fset, oldname);
+ if (oldpath == NULL) {
+ EXIT;
+ return -ENOENT;
+ }
+
+ newpath = izo_make_path(fset, newname);
+ if (newpath == NULL) {
+ error = -ENOENT;
+ EXIT;
+ goto exit;
+ }
+
+ if ((error = izo_lookup_file(fset, oldpath, &oldnd)) != 0) {
+ EXIT;
+ goto exit1;
+ }
+
+ if ((error = izo_lookup_file(fset, newpath, &newnd)) != 0) {
+ EXIT;
+ goto exit2;
+ }
+
+ double_lock(newnd.dentry, oldnd.dentry);
+ old_dentry = lookup_hash(&oldnd.last, oldnd.dentry);
+ error = PTR_ERR(old_dentry);
+ if (IS_ERR(old_dentry)) {
+ EXIT;
+ goto exit3;
+ }
+ error = -ENOENT;
+ if (!old_dentry->d_inode) {
+ EXIT;
+ goto exit4;
+ }
+ new_dentry = lookup_hash(&newnd.last, newnd.dentry);
+ error = PTR_ERR(new_dentry);
+ if (IS_ERR(new_dentry)) {
+ EXIT;
+ goto exit4;
+ }
+
+ {
+ extern int presto_rename(struct inode *old_dir,struct dentry *old_dentry,
+ struct inode *new_dir,struct dentry *new_dentry);
+ error = presto_rename(old_dentry->d_parent->d_inode, old_dentry,
+ new_dentry->d_parent->d_inode, new_dentry);
+ }
+
+ dput(new_dentry);
+ EXIT;
+ exit4:
+ dput(old_dentry);
+ exit3:
+ double_up(&newnd.dentry->d_inode->i_sem, &oldnd.dentry->d_inode->i_sem);
+ path_release(&newnd);
+ exit2:
+ path_release(&oldnd);
+ exit1:
+ PRESTO_FREE(newpath, strlen(newpath) + 1);
+ exit:
+ PRESTO_FREE(oldpath, strlen(oldpath) + 1);
+ return error;
+}
+
+/* This function is called with the fset->fset_kml.fd_lock held */
+int presto_finish_kml_truncate(struct presto_file_set *fset,
+ unsigned long int offset)
+{
+ struct lento_vfs_context info;
+ void *handle;
+ struct file *f;
+ struct dentry *dentry;
+ int error = 0, len;
+ struct nameidata nd;
+ char *kmlpath = NULL, *smlpath = NULL;
+ ENTRY;
+
+ if (offset == 0) {
+ /* Lento couldn't do what it needed to; abort the truncation. */
+ fset->fset_kml.fd_truncating = 0;
+ EXIT;
+ return 0;
+ }
+
+ /* someone is about to write to the end of the KML; try again later. */
+ if ( !list_empty(&fset->fset_kml.fd_reservations) ) {
+ EXIT;
+ return -EAGAIN;
+ }
+
+ f = presto_copy_kml_tail(fset, offset);
+ if (IS_ERR(f)) {
+ EXIT;
+ return PTR_ERR(f);
+ }
+
+ /* In a single transaction:
+ *
+ * - unlink 'kml'
+ * - rename 'kml_tmp' to 'kml'
+ * - unlink 'sml'
+ * - rename 'sml_tmp' to 'sml'
+ * - rewrite the first record of last_rcvd with the new kml
+ * offset.
+ */
+ handle = presto_trans_start(fset, fset->fset_dentry->d_inode,
+ KML_OPCODE_KML_TRUNC);
+ if (IS_ERR(handle)) {
+ presto_release_space(fset->fset_cache, PRESTO_REQLOW);
+ CERROR("ERROR: presto_finish_kml_truncate: no space for transaction\n");
+ EXIT;
+ return -ENOMEM;
+ }
+
+ memset(&info, 0, sizeof(info));
+ info.flags = LENTO_FL_IGNORE_TIME;
+
+ kmlpath = izo_make_path(fset, "kml");
+ if (kmlpath == NULL) {
+ error = -ENOMEM;
+ CERROR("make_path failed: ENOMEM\n");
+ EXIT;
+ goto exit_commit;
+ }
+
+ if ((error = izo_lookup_file(fset, kmlpath, &nd)) != 0) {
+ CERROR("izo_lookup_file(kml) failed: %d.\n", error);
+ EXIT;
+ goto exit_commit;
+ }
+ down(&nd.dentry->d_inode->i_sem);
+ dentry = lookup_hash(&nd.last, nd.dentry);
+ error = PTR_ERR(dentry);
+ if (IS_ERR(dentry)) {
+ up(&nd.dentry->d_inode->i_sem);
+ path_release(&nd);
+ CERROR("lookup_hash failed\n");
+ EXIT;
+ goto exit_commit;
+ }
+ error = presto_do_unlink(fset, dentry->d_parent, dentry, &info);
+ dput(dentry);
+ up(&nd.dentry->d_inode->i_sem);
+ path_release(&nd);
+
+ if (error != 0) {
+ CERROR("presto_do_unlink(kml) failed: %d.\n", error);
+ EXIT;
+ goto exit_commit;
+ }
+
+ smlpath = izo_make_path(fset, "sml");
+ if (smlpath == NULL) {
+ error = -ENOMEM;
+ CERROR("make_path() failed: ENOMEM\n");
+ EXIT;
+ goto exit_commit;
+ }
+
+ if ((error = izo_lookup_file(fset, smlpath, &nd)) != 0) {
+ CERROR("izo_lookup_file(sml) failed: %d.\n", error);
+ EXIT;
+ goto exit_commit;
+ }
+ down(&nd.dentry->d_inode->i_sem);
+ dentry = lookup_hash(&nd.last, nd.dentry);
+ error = PTR_ERR(dentry);
+ if (IS_ERR(dentry)) {
+ up(&nd.dentry->d_inode->i_sem);
+ path_release(&nd);
+ CERROR("lookup_hash failed\n");
+ EXIT;
+ goto exit_commit;
+ }
+ error = presto_do_unlink(fset, dentry->d_parent, dentry, &info);
+ dput(dentry);
+ up(&nd.dentry->d_inode->i_sem);
+ path_release(&nd);
+
+ if (error != 0) {
+ CERROR("presto_do_unlink(sml) failed: %d.\n", error);
+ EXIT;
+ goto exit_commit;
+ }
+
+ error = do_truncate_rename(fset, "kml_tmp", "kml");
+ if (error != 0)
+ CERROR("do_truncate_rename(kml_tmp, kml) failed: %d\n", error);
+ error = do_truncate_rename(fset, "sml_tmp", "sml");
+ if (error != 0)
+ CERROR("do_truncate_rename(sml_tmp, sml) failed: %d\n", error);
+
+ /* Write a new 'last_rcvd' record with the new KML offset */
+ fset->fset_kml_logical_off += offset;
+ CDEBUG(D_CACHE, "new kml_logical_offset: %Lu\n",
+ fset->fset_kml_logical_off);
+ if (presto_write_kml_logical_offset(fset) != 0) {
+ CERROR("presto_write_kml_logical_offset failed\n");
+ }
+
+ presto_trans_commit(fset, handle);
+
+ /* Everything was successful, so swap the KML file descriptors */
+ filp_close(fset->fset_kml.fd_file, NULL);
+ fset->fset_kml.fd_file = f;
+ fset->fset_kml.fd_offset -= offset;
+ fset->fset_kml.fd_truncating = 0;
+
+ EXIT;
+ return 0;
+
+ exit_commit:
+ presto_trans_commit(fset, handle);
+ len = strlen("/.intermezzo/") + strlen(fset->fset_name) +strlen("sml");
+ if (kmlpath != NULL)
+ PRESTO_FREE(kmlpath, len);
+ if (smlpath != NULL)
+ PRESTO_FREE(smlpath, len);
+ return error;
+}
+
+/* structure of an extended log record:
+
+ buf-prefix buf-body [string1 [string2 [string3]]] buf-suffix
+
+ note: moves offset forward
+*/
+static inline int presto_write_record(struct file *f, loff_t *off,
+ const char *buf, size_t size,
+ const char *string1, int len1,
+ const char *string2, int len2,
+ const char *string3, int len3)
+{
+ size_t prefix_size;
+ int rc;
+
+ prefix_size = size - sizeof(struct kml_suffix);
+ rc = presto_fwrite(f, buf, prefix_size, off);
+ if ( rc != prefix_size ) {
+ CERROR("Write error!\n");
+ EXIT;
+ return -EIO;
+ }
+
+ if ( string1 && len1 ) {
+ rc = presto_fwrite(f, string1, len1, off);
+ if ( rc != len1 ) {
+ CERROR("Write error!\n");
+ EXIT;
+ return -EIO;
+ }
+ }
+
+ if ( string2 && len2 ) {
+ rc = presto_fwrite(f, string2, len2, off);
+ if ( rc != len2 ) {
+ CERROR("Write error!\n");
+ EXIT;
+ return -EIO;
+ }
+ }
+
+ if ( string3 && len3 ) {
+ rc = presto_fwrite(f, string3, len3, off);
+ if ( rc != len3 ) {
+ CERROR("Write error!\n");
+ EXIT;
+ return -EIO;
+ }
+ }
+
+ rc = presto_fwrite(f, buf + prefix_size,
+ sizeof(struct kml_suffix), off);
+ if ( rc != sizeof(struct kml_suffix) ) {
+ CERROR("Write error!\n");
+ EXIT;
+ return -EIO;
+ }
+ return 0;
+}
+
+
+/*
+ * rec->size must be valid prior to calling this function.
+ *
+ * had to export this for branch_reinter in kml_reint.c
+ */
+int presto_log(struct presto_file_set *fset, struct rec_info *rec,
+ const char *buf, size_t size,
+ const char *string1, int len1,
+ const char *string2, int len2,
+ const char *string3, int len3)
+{
+ int rc;
+ struct presto_reservation_data rd;
+ loff_t offset;
+ struct presto_log_fd *fd;
+ struct kml_suffix *s;
+ int prefix_size;
+
+ ENTRY;
+
+ /* buf is NULL when no_journal is in effect */
+ if (!buf) {
+ EXIT;
+ return -EINVAL;
+ }
+
+ if (rec->is_kml) {
+ fd = &fset->fset_kml;
+ } else {
+ fd = &fset->fset_lml;
+ }
+
+ presto_reserve_record(fset, fd, rec, &rd);
+
+ if (rec->is_kml) {
+ if (rec->offset < fset->fset_kml_logical_off) {
+ CERROR("record with pre-trunc offset. tell phil.\n");
+ BUG();
+ }
+ offset = rec->offset - fset->fset_kml_logical_off;
+ } else {
+ offset = rec->offset;
+ }
+
+ /* now we know the record number */
+ prefix_size = size - sizeof(struct kml_suffix);
+ s = (struct kml_suffix *) (buf + prefix_size);
+ s->recno = cpu_to_le32(rec->recno);
+
+ rc = presto_write_record(fd->fd_file, &offset, buf, size,
+ string1, len1, string2, len2, string3, len3);
+ if (rc) {
+ CERROR("presto: error writing record to %s\n",
+ rec->is_kml ? "KML" : "LML");
+ return rc;
+ }
+ presto_release_record(fd, &rd);
+
+ rc = presto_kml_dispatch(fset);
+
+ EXIT;
+ return rc;
+}
+
+/* read from the record at tail */
+static int presto_last_record(struct presto_log_fd *fd, loff_t *size,
+ loff_t *tail_offset, __u32 *recno, loff_t tail)
+{
+ struct kml_suffix suffix;
+ int rc;
+ loff_t zeroes;
+
+ *recno = 0;
+ *tail_offset = 0;
+ *size = 0;
+
+ if (tail < sizeof(struct kml_prefix_hdr) + sizeof(suffix)) {
+ EXIT;
+ return 0;
+ }
+
+ zeroes = tail - sizeof(int);
+ while ( zeroes >= 0 ) {
+ int data;
+ rc = presto_fread(fd->fd_file, (char *)&data, sizeof(data),
+ &zeroes);
+ if ( rc != sizeof(data) ) {
+ rc = -EIO;
+ return rc;
+ }
+ if (data)
+ break;
+ zeroes -= 2 * sizeof(data);
+ }
+
+ /* zeroes at the begining of file. this is needed to prevent
+ presto_fread errors -SHP
+ */
+ if (zeroes <= 0) return 0;
+
+ zeroes -= sizeof(suffix) + sizeof(int);
+ rc = presto_fread(fd->fd_file, (char *)&suffix, sizeof(suffix), &zeroes);
+ if ( rc != sizeof(suffix) ) {
+ EXIT;
+ return rc;
+ }
+ if ( suffix.len > 500 ) {
+ CERROR("InterMezzo: Warning long record tail at %ld, rec tail_offset at %ld (size %d)\n",
+ (long) zeroes, (long)*tail_offset, suffix.len);
+ }
+
+ *recno = suffix.recno;
+ *size = suffix.len;
+ *tail_offset = zeroes;
+ return 0;
+}
+
+static int izo_kml_last_recno(struct presto_log_fd *logfd)
+{
+ int rc;
+ loff_t size;
+ loff_t tail_offset;
+ int recno;
+ loff_t tail = logfd->fd_file->f_dentry->d_inode->i_size;
+
+ rc = presto_last_record(logfd, &size, &tail_offset, &recno, tail);
+ if (rc != 0) {
+ EXIT;
+ return rc;
+ }
+
+ logfd->fd_offset = tail_offset;
+ logfd->fd_recno = recno;
+ CDEBUG(D_JOURNAL, "setting fset_kml->fd_recno to %d, offset %Ld\n",
+ recno, tail_offset);
+ EXIT;
+ return 0;
+}
+
+struct file *izo_log_open(struct presto_file_set *fset, char *name, int flags)
+{
+ struct presto_cache *cache = fset->fset_cache;
+ struct file *f;
+ int error;
+ ENTRY;
+
+ f = izo_fset_open(fset, name, flags, 0644);
+ error = PTR_ERR(f);
+ if (IS_ERR(f)) {
+ EXIT;
+ return f;
+ }
+
+ error = -EINVAL;
+ if ( cache != presto_get_cache(f->f_dentry->d_inode) ) {
+ CERROR("InterMezzo: %s cache does not match fset cache!\n",name);
+ fset->fset_kml.fd_file = NULL;
+ filp_close(f, NULL);
+ f = NULL;
+ EXIT;
+ return f;
+ }
+
+ if (cache->cache_filter && cache->cache_filter->o_trops &&
+ cache->cache_filter->o_trops->tr_journal_data) {
+ cache->cache_filter->o_trops->tr_journal_data
+ (f->f_dentry->d_inode);
+ } else {
+ CERROR("InterMezzo WARNING: no file data logging!\n");
+ }
+
+ EXIT;
+
+ return f;
+}
+
+int izo_init_kml_file(struct presto_file_set *fset, struct presto_log_fd *logfd)
+{
+ int error = 0;
+ struct file *f;
+
+ ENTRY;
+ if (logfd->fd_file) {
+ CDEBUG(D_INODE, "fset already has KML open\n");
+ EXIT;
+ return 0;
+ }
+
+ logfd->fd_lock = RW_LOCK_UNLOCKED;
+ INIT_LIST_HEAD(&logfd->fd_reservations);
+ f = izo_log_open(fset, "kml", O_RDWR | O_CREAT);
+ if (IS_ERR(f)) {
+ error = PTR_ERR(f);
+ return error;
+ }
+
+ logfd->fd_file = f;
+ error = izo_kml_last_recno(logfd);
+
+ if (error) {
+ logfd->fd_file = NULL;
+ filp_close(f, NULL);
+ CERROR("InterMezzo: IO error in KML of fset %s\n",
+ fset->fset_name);
+ EXIT;
+ return error;
+ }
+ fset->fset_lento_off = logfd->fd_offset;
+ fset->fset_lento_recno = logfd->fd_recno;
+
+ EXIT;
+ return error;
+}
+
+int izo_init_last_rcvd_file(struct presto_file_set *fset, struct presto_log_fd *logfd)
+{
+ int error = 0;
+ struct file *f;
+ struct rec_info recinfo;
+
+ ENTRY;
+ if (logfd->fd_file != NULL) {
+ CDEBUG(D_INODE, "fset already has last_rcvd open\n");
+ EXIT;
+ return 0;
+ }
+
+ logfd->fd_lock = RW_LOCK_UNLOCKED;
+ INIT_LIST_HEAD(&logfd->fd_reservations);
+ f = izo_log_open(fset, "last_rcvd", O_RDWR | O_CREAT);
+ if (IS_ERR(f)) {
+ error = PTR_ERR(f);
+ return error;
+ }
+
+ logfd->fd_file = f;
+ logfd->fd_offset = f->f_dentry->d_inode->i_size;
+
+ error = izo_rep_cache_init(fset);
+
+ if (presto_read_kml_logical_offset(&recinfo, fset) == 0) {
+ fset->fset_kml_logical_off = recinfo.offset;
+ } else {
+ /* The 'last_rcvd' file doesn't contain a kml offset record,
+ * probably because we just created 'last_rcvd'. Write one. */
+ fset->fset_kml_logical_off = 0;
+ presto_write_kml_logical_offset(fset);
+ }
+
+ EXIT;
+ return error;
+}
+
+int izo_init_lml_file(struct presto_file_set *fset, struct presto_log_fd *logfd)
+{
+ int error = 0;
+ struct file *f;
+
+ ENTRY;
+ if (logfd->fd_file) {
+ CDEBUG(D_INODE, "fset already has lml open\n");
+ EXIT;
+ return 0;
+ }
+
+ logfd->fd_lock = RW_LOCK_UNLOCKED;
+ INIT_LIST_HEAD(&logfd->fd_reservations);
+ f = izo_log_open(fset, "lml", O_RDWR | O_CREAT);
+ if (IS_ERR(f)) {
+ error = PTR_ERR(f);
+ return error;
+ }
+
+ logfd->fd_file = f;
+ logfd->fd_offset = f->f_dentry->d_inode->i_size;
+
+ EXIT;
+ return error;
+}
+
+/* Get the KML-offset record from the last_rcvd file */
+int presto_read_kml_logical_offset(struct rec_info *recinfo,
+ struct presto_file_set *fset)
+{
+ loff_t off;
+ struct izo_rcvd_rec rec;
+ char uuid[16] = {0};
+
+ off = izo_rcvd_get(&rec, fset, uuid);
+ if (off < 0)
+ return -1;
+
+ recinfo->offset = rec.lr_local_offset;
+ return 0;
+}
+
+int presto_write_kml_logical_offset(struct presto_file_set *fset)
+{
+ loff_t rc;
+ struct izo_rcvd_rec rec;
+ char uuid[16] = {0};
+
+ rc = izo_rcvd_get(&rec, fset, uuid);
+ if (rc < 0)
+ memset(&rec, 0, sizeof(rec));
+
+ rec.lr_local_offset =
+ cpu_to_le64(fset->fset_kml_logical_off);
+
+ return izo_rcvd_write(fset, &rec);
+}
+
+struct file * presto_copy_kml_tail(struct presto_file_set *fset,
+ unsigned long int start)
+{
+ struct file *f;
+ int len;
+ loff_t read_off, write_off, bytes;
+
+ ENTRY;
+
+ /* Copy the tail of 'kml' to 'kml_tmp' */
+ f = izo_log_open(fset, "kml_tmp", O_RDWR);
+ if (IS_ERR(f)) {
+ EXIT;
+ return f;
+ }
+
+ write_off = 0;
+ read_off = start;
+ bytes = fset->fset_kml.fd_offset - start;
+ while (bytes > 0) {
+ char buf[4096];
+ int toread;
+
+ if (bytes > sizeof(buf))
+ toread = sizeof(buf);
+ else
+ toread = bytes;
+
+ len = presto_fread(fset->fset_kml.fd_file, buf, toread,
+ &read_off);
+ if (len <= 0)
+ break;
+
+ if (presto_fwrite(f, buf, len, &write_off) != len) {
+ filp_close(f, NULL);
+ EXIT;
+ return ERR_PTR(-EIO);
+ }
+
+ bytes -= len;
+ }
+
+ EXIT;
+ return f;
+}
+
+
+/* LML records here */
+/* this writes an LML record to the LML file (rec->is_kml =0) */
+int presto_write_lml_close(struct rec_info *rec,
+ struct presto_file_set *fset,
+ struct file *file,
+ __u64 remote_ino,
+ __u64 remote_generation,
+ struct presto_version *remote_version,
+ struct presto_version *new_file_ver)
+{
+ int opcode = KML_OPCODE_CLOSE;
+ char *buffer;
+ struct dentry *dentry = file->f_dentry;
+ __u64 ino;
+ __u32 pathlen;
+ char *path;
+ __u32 generation;
+ int size;
+ char *logrecord;
+ char record[292];
+ struct dentry *root;
+ int error;
+
+ ENTRY;
+
+ if ( presto_no_journal(fset) ) {
+ EXIT;
+ return 0;
+ }
+ root = fset->fset_dentry;
+
+ BUFF_ALLOC(buffer, NULL);
+ path = presto_path(dentry, root, buffer, PAGE_SIZE);
+ CDEBUG(D_INODE, "Path: %s\n", path);
+ pathlen = cpu_to_le32(MYPATHLEN(buffer, path));
+ ino = cpu_to_le64(dentry->d_inode->i_ino);
+ generation = cpu_to_le32(dentry->d_inode->i_generation);
+ size = sizeof(__u32) * current->ngroups +
+ sizeof(struct kml_prefix_hdr) + sizeof(*new_file_ver) +
+ sizeof(ino) + sizeof(generation) + sizeof(pathlen) +
+ sizeof(remote_ino) + sizeof(remote_generation) +
+ sizeof(remote_version) + sizeof(rec->offset) +
+ sizeof(struct kml_suffix);
+
+ if ( size > sizeof(record) )
+ CERROR("InterMezzo: BUFFER OVERFLOW in %s!\n", __FUNCTION__);
+
+ rec->is_kml = 0;
+ rec->size = size + size_round(le32_to_cpu(pathlen));
+
+ logrecord = journal_log_prefix(record, opcode, rec);
+ logrecord = log_version(logrecord, new_file_ver);
+ logrecord = logit(logrecord, &ino, sizeof(ino));
+ logrecord = logit(logrecord, &generation, sizeof(generation));
+ logrecord = logit(logrecord, &pathlen, sizeof(pathlen));
+ logrecord = logit(logrecord, &remote_ino, sizeof(remote_ino));
+ logrecord = logit(logrecord, &remote_generation,
+ sizeof(remote_generation));
+ logrecord = log_version(logrecord, remote_version);
+ logrecord = logit(logrecord, &rec->offset, sizeof(rec->offset));
+ logrecord = journal_log_suffix(logrecord, record, fset, dentry, rec);
+
+ error = presto_log(fset, rec, record, size,
+ path, size_round(le32_to_cpu(pathlen)),
+ NULL, 0, NULL, 0);
+
+ BUFF_FREE(buffer);
+
+ EXIT;
+ return error;
+}
+
+/*
+ * Check if the given record is at the end of the file. If it is, truncate
+ * the lml to the record's offset, removing it. Repeat on prior record,
+ * until we reach an active record or a reserved record (as defined by the
+ * reservations list).
+ */
+static int presto_truncate_lml_tail(struct presto_file_set *fset)
+{
+ loff_t lml_tail;
+ loff_t lml_last_rec;
+ loff_t lml_last_recsize;
+ loff_t local_offset;
+ int recno;
+ struct kml_prefix_hdr prefix;
+ struct inode *inode = fset->fset_lml.fd_file->f_dentry->d_inode;
+ void *handle;
+ int rc;
+
+ ENTRY;
+ /* If someone else is already truncating the LML, return. */
+ write_lock(&fset->fset_lml.fd_lock);
+ if (fset->fset_lml.fd_truncating == 1 ) {
+ write_unlock(&fset->fset_lml.fd_lock);
+ EXIT;
+ return 0;
+ }
+ /* someone is about to write to the end of the LML */
+ if ( !list_empty(&fset->fset_lml.fd_reservations) ) {
+ write_unlock(&fset->fset_lml.fd_lock);
+ EXIT;
+ return 0;
+ }
+ lml_tail = fset->fset_lml.fd_file->f_dentry->d_inode->i_size;
+ /* Nothing to truncate?*/
+ if (lml_tail == 0) {
+ write_unlock(&fset->fset_lml.fd_lock);
+ EXIT;
+ return 0;
+ }
+ fset->fset_lml.fd_truncating = 1;
+ write_unlock(&fset->fset_lml.fd_lock);
+
+ presto_last_record(&fset->fset_lml, &lml_last_recsize,
+ &lml_last_rec, &recno, lml_tail);
+ /* Do we have a record to check? If not we have zeroes at the
+ beginning of the file. -SHP
+ */
+ if (lml_last_recsize != 0) {
+ local_offset = lml_last_rec - lml_last_recsize;
+ rc = presto_fread(fset->fset_lml.fd_file, (char *)&prefix,
+ sizeof(prefix), &local_offset);
+ if (rc != sizeof(prefix)) {
+ EXIT;
+ goto tr_out;
+ }
+
+ if ( prefix.opcode != KML_OPCODE_NOOP ) {
+ EXIT;
+ rc = 0;
+ /* We may have zeroes at the end of the file, should
+ we clear them out? -SHP
+ */
+ goto tr_out;
+ }
+ } else
+ lml_last_rec=0;
+
+ handle = presto_trans_start(fset, inode, KML_OPCODE_TRUNC);
+ if ( IS_ERR(handle) ) {
+ EXIT;
+ rc = -ENOMEM;
+ goto tr_out;
+ }
+
+ rc = izo_do_truncate(fset, fset->fset_lml.fd_file->f_dentry,
+ lml_last_rec - lml_last_recsize, lml_tail);
+ presto_trans_commit(fset, handle);
+ if ( rc == 0 ) {
+ rc = 1;
+ }
+ EXIT;
+
+ tr_out:
+ CDEBUG(D_JOURNAL, "rc = %d\n", rc);
+ write_lock(&fset->fset_lml.fd_lock);
+ fset->fset_lml.fd_truncating = 0;
+ write_unlock(&fset->fset_lml.fd_lock);
+ return rc;
+}
+
+int presto_truncate_lml(struct presto_file_set *fset)
+{
+ int rc;
+ ENTRY;
+
+ while ( (rc = presto_truncate_lml_tail(fset)) > 0);
+ if ( rc < 0 && rc != -EALREADY) {
+ CERROR("truncate_lml error %d\n", rc);
+ }
+ EXIT;
+ return rc;
+}
+
+int presto_clear_lml_close(struct presto_file_set *fset, loff_t lml_offset)
+{
+ int rc;
+ struct kml_prefix_hdr record;
+ loff_t offset = lml_offset;
+
+ ENTRY;
+
+ if ( presto_no_journal(fset) ) {
+ EXIT;
+ return 0;
+ }
+
+ CDEBUG(D_JOURNAL, "reading prefix: off %ld, size %d\n",
+ (long)lml_offset, sizeof(record));
+ rc = presto_fread(fset->fset_lml.fd_file, (char *)&record,
+ sizeof(record), &offset);
+
+ if ( rc != sizeof(record) ) {
+ CERROR("presto: clear_lml io error %d\n", rc);
+ EXIT;
+ return -EIO;
+ }
+
+ /* overwrite the prefix */
+ CDEBUG(D_JOURNAL, "overwriting prefix: off %ld\n", (long)lml_offset);
+ record.opcode = KML_OPCODE_NOOP;
+ offset = lml_offset;
+ /* note: this does just a single transaction in the cache */
+ rc = presto_fwrite(fset->fset_lml.fd_file, (char *)(&record),
+ sizeof(record), &offset);
+ if ( rc != sizeof(record) ) {
+ EXIT;
+ return -EIO;
+ }
+
+ EXIT;
+ return 0;
+}
+
+
+
+/* now a journal function for every operation */
+
+int presto_journal_setattr(struct rec_info *rec, struct presto_file_set *fset,
+ struct dentry *dentry, struct presto_version *old_ver,
+ struct izo_rollback_data *rb, struct iattr *iattr)
+{
+ int opcode = KML_OPCODE_SETATTR;
+ char *buffer, *path, *logrecord, record[316];
+ struct dentry *root;
+ __u32 uid, gid, mode, valid, flags, pathlen;
+ __u64 fsize, mtime, ctime;
+ int error, size;
+
+ ENTRY;
+ if ( presto_no_journal(fset) ) {
+ EXIT;
+ return 0;
+ }
+
+ if (!dentry->d_inode || (dentry->d_inode->i_nlink == 0)
+ || ((dentry->d_parent != dentry) && list_empty(&dentry->d_hash))) {
+ EXIT;
+ return 0;
+ }
+
+ root = fset->fset_dentry;
+
+ BUFF_ALLOC(buffer, NULL);
+ path = presto_path(dentry, root, buffer, PAGE_SIZE);
+ pathlen = cpu_to_le32(MYPATHLEN(buffer, path));
+ size = sizeof(__u32) * current->ngroups +
+ sizeof(struct kml_prefix_hdr) + sizeof(*old_ver) +
+ sizeof(valid) + sizeof(mode) + sizeof(uid) + sizeof(gid) +
+ sizeof(fsize) + sizeof(mtime) + sizeof(ctime) + sizeof(flags) +
+ sizeof(pathlen) + sizeof(*rb) + sizeof(struct kml_suffix);
+
+ if ( size > sizeof(record) )
+ CERROR("InterMezzo: BUFFER OVERFLOW in %s!\n", __FUNCTION__);
+
+ /* Only journal one kind of mtime, and not atime at all. Also don't
+ * journal bogus data in iattr, to make the journal more compressible.
+ */
+ if (iattr->ia_valid & ATTR_MTIME_SET)
+ iattr->ia_valid = iattr->ia_valid | ATTR_MTIME;
+ valid = cpu_to_le32(iattr->ia_valid & ~(ATTR_ATIME | ATTR_MTIME_SET |
+ ATTR_ATIME_SET));
+ mode = iattr->ia_valid & ATTR_MODE ? cpu_to_le32(iattr->ia_mode): 0;
+ uid = iattr->ia_valid & ATTR_UID ? cpu_to_le32(iattr->ia_uid): 0;
+ gid = iattr->ia_valid & ATTR_GID ? cpu_to_le32(iattr->ia_gid): 0;
+ fsize = iattr->ia_valid & ATTR_SIZE ? cpu_to_le64(iattr->ia_size): 0;
+ mtime = iattr->ia_valid & ATTR_MTIME ? cpu_to_le64(iattr->ia_mtime): 0;
+ ctime = iattr->ia_valid & ATTR_CTIME ? cpu_to_le64(iattr->ia_ctime): 0;
+ flags = iattr->ia_valid & ATTR_ATTR_FLAG ?
+ cpu_to_le32(iattr->ia_attr_flags): 0;
+
+ rec->is_kml = 1;
+ rec->size = size + size_round(le32_to_cpu(pathlen));
+
+ logrecord = journal_log_prefix(record, opcode, rec);
+ logrecord = log_version(logrecord, old_ver);
+ logrecord = logit(logrecord, &valid, sizeof(valid));
+ logrecord = logit(logrecord, &mode, sizeof(mode));
+ logrecord = logit(logrecord, &uid, sizeof(uid));
+ logrecord = logit(logrecord, &gid, sizeof(gid));
+ logrecord = logit(logrecord, &fsize, sizeof(fsize));
+ logrecord = logit(logrecord, &mtime, sizeof(mtime));
+ logrecord = logit(logrecord, &ctime, sizeof(ctime));
+ logrecord = logit(logrecord, &flags, sizeof(flags));
+ logrecord = log_rollback(logrecord, rb);
+ logrecord = logit(logrecord, &pathlen, sizeof(pathlen));
+ logrecord = journal_log_suffix(logrecord, record, fset, dentry, rec);
+
+ error = presto_log(fset, rec, record, size,
+ path, size_round(le32_to_cpu(pathlen)),
+ NULL, 0, NULL, 0);
+
+ BUFF_FREE(buffer);
+ EXIT;
+ return error;
+}
+
+int presto_get_fileid(int minor, struct presto_file_set *fset,
+ struct dentry *dentry)
+{
+ int opcode = KML_OPCODE_GET_FILEID;
+ struct rec_info rec;
+ char *buffer, *path, *logrecord, record[4096]; /*include path*/
+ struct dentry *root;
+ __u32 uid, gid, pathlen;
+ int error, size;
+ struct kml_suffix *suffix;
+
+ ENTRY;
+
+ root = fset->fset_dentry;
+
+ uid = cpu_to_le32(dentry->d_inode->i_uid);
+ gid = cpu_to_le32(dentry->d_inode->i_gid);
+ BUFF_ALLOC(buffer, NULL);
+ path = presto_path(dentry, root, buffer, PAGE_SIZE);
+ pathlen = cpu_to_le32(MYPATHLEN(buffer, path));
+ size = sizeof(__u32) * current->ngroups +
+ sizeof(struct kml_prefix_hdr) + sizeof(pathlen) +
+ size_round(le32_to_cpu(pathlen)) +
+ sizeof(struct kml_suffix);
+
+ CDEBUG(D_FILE, "kml size: %d\n", size);
+ if ( size > sizeof(record) )
+ CERROR("InterMezzo: BUFFER OVERFLOW in %s!\n", __FUNCTION__);
+
+ memset(&rec, 0, sizeof(rec));
+ rec.is_kml = 1;
+ rec.size = size;
+
+ logrecord = journal_log_prefix(record, opcode, &rec);
+ logrecord = logit(logrecord, &pathlen, sizeof(pathlen));
+ logrecord = logit(logrecord, path, size_round(le32_to_cpu(pathlen)));
+ suffix = (struct kml_suffix *)logrecord;
+ logrecord = journal_log_suffix(logrecord, record, fset, dentry, &rec);
+ /* journal_log_suffix expects journal_log to set this */
+ suffix->recno = 0;
+
+ CDEBUG(D_FILE, "actual kml size: %d\n", logrecord - record);
+ CDEBUG(D_FILE, "get fileid: uid %d, gid %d, path: %s\n", uid, gid,path);
+
+ error = izo_upc_get_fileid(minor, size, record,
+ size_round(le32_to_cpu(pathlen)), path,
+ fset->fset_name);
+
+ BUFF_FREE(buffer);
+ EXIT;
+ return error;
+}
+
+int presto_journal_create(struct rec_info *rec, struct presto_file_set *fset,
+ struct dentry *dentry,
+ struct presto_version *tgt_dir_ver,
+ struct presto_version *new_file_ver, int mode)
+{
+ int opcode = KML_OPCODE_CREATE;
+ char *buffer, *path, *logrecord, record[292];
+ struct dentry *root;
+ __u32 uid, gid, lmode, pathlen;
+ int error, size;
+
+ ENTRY;
+ if ( presto_no_journal(fset) ) {
+ EXIT;
+ return 0;
+ }
+
+ root = fset->fset_dentry;
+
+ uid = cpu_to_le32(dentry->d_inode->i_uid);
+ gid = cpu_to_le32(dentry->d_inode->i_gid);
+ lmode = cpu_to_le32(mode);
+
+ BUFF_ALLOC(buffer, NULL);
+ path = presto_path(dentry, root, buffer, PAGE_SIZE);
+ pathlen = cpu_to_le32(MYPATHLEN(buffer, path));
+ size = sizeof(__u32) * current->ngroups +
+ sizeof(struct kml_prefix_hdr) + 3 * sizeof(*tgt_dir_ver) +
+ sizeof(lmode) + sizeof(uid) + sizeof(gid) + sizeof(pathlen) +
+ sizeof(struct kml_suffix);
+
+ if ( size > sizeof(record) )
+ CERROR("InterMezzo: BUFFER OVERFLOW in %s!\n", __FUNCTION__);
+
+ rec->is_kml = 1;
+ rec->size = size + size_round(le32_to_cpu(pathlen));
+
+ logrecord = journal_log_prefix(record, opcode, rec);
+ logrecord = log_version(logrecord, tgt_dir_ver);
+ logrecord = log_dentry_version(logrecord, dentry->d_parent);
+ logrecord = log_version(logrecord, new_file_ver);
+ logrecord = logit(logrecord, &lmode, sizeof(lmode));
+ logrecord = logit(logrecord, &uid, sizeof(uid));
+ logrecord = logit(logrecord, &gid, sizeof(gid));
+ logrecord = logit(logrecord, &pathlen, sizeof(pathlen));
+ logrecord = journal_log_suffix(logrecord, record, fset, dentry, rec);
+
+ error = presto_log(fset, rec, record, size,
+ path, size_round(le32_to_cpu(pathlen)),
+ NULL, 0, NULL, 0);
+
+ BUFF_FREE(buffer);
+ EXIT;
+ return error;
+}
+
+int presto_journal_symlink(struct rec_info *rec, struct presto_file_set *fset,
+ struct dentry *dentry, const char *target,
+ struct presto_version *tgt_dir_ver,
+ struct presto_version *new_link_ver)
+{
+ int opcode = KML_OPCODE_SYMLINK;
+ char *buffer, *path, *logrecord, record[292];
+ struct dentry *root;
+ __u32 uid, gid, pathlen;
+ __u32 targetlen = cpu_to_le32(strlen(target));
+ int error, size;
+
+ ENTRY;
+ if ( presto_no_journal(fset) ) {
+ EXIT;
+ return 0;
+ }
+
+ root = fset->fset_dentry;
+
+ uid = cpu_to_le32(dentry->d_inode->i_uid);
+ gid = cpu_to_le32(dentry->d_inode->i_gid);
+
+ BUFF_ALLOC(buffer, NULL);
+ path = presto_path(dentry, root, buffer, PAGE_SIZE);
+ pathlen = cpu_to_le32(MYPATHLEN(buffer, path));
+ size = sizeof(__u32) * current->ngroups +
+ sizeof(struct kml_prefix_hdr) + 3 * sizeof(*tgt_dir_ver) +
+ sizeof(uid) + sizeof(gid) + sizeof(pathlen) +
+ sizeof(targetlen) + sizeof(struct kml_suffix);
+
+ if ( size > sizeof(record) )
+ CERROR("InterMezzo: BUFFER OVERFLOW in %s!\n", __FUNCTION__);
+
+ rec->is_kml = 1;
+ rec->size = size + size_round(le32_to_cpu(pathlen)) +
+ size_round(le32_to_cpu(targetlen));
+
+ logrecord = journal_log_prefix(record, opcode, rec);
+ logrecord = log_version(logrecord, tgt_dir_ver);
+ logrecord = log_dentry_version(logrecord, dentry->d_parent);
+ logrecord = log_version(logrecord, new_link_ver);
+ logrecord = logit(logrecord, &uid, sizeof(uid));
+ logrecord = logit(logrecord, &gid, sizeof(gid));
+ logrecord = logit(logrecord, &pathlen, sizeof(pathlen));
+ logrecord = logit(logrecord, &targetlen, sizeof(targetlen));
+ logrecord = journal_log_suffix(logrecord, record, fset, dentry, rec);
+
+ error = presto_log(fset, rec, record, size,
+ path, size_round(le32_to_cpu(pathlen)),
+ target, size_round(le32_to_cpu(targetlen)),
+ NULL, 0);
+
+ BUFF_FREE(buffer);
+ EXIT;
+ return error;
+}
+
+int presto_journal_mkdir(struct rec_info *rec, struct presto_file_set *fset,
+ struct dentry *dentry,
+ struct presto_version *tgt_dir_ver,
+ struct presto_version *new_dir_ver, int mode)
+{
+ int opcode = KML_OPCODE_MKDIR;
+ char *buffer, *path, *logrecord, record[292];
+ struct dentry *root;
+ __u32 uid, gid, lmode, pathlen;
+ int error, size;
+
+ ENTRY;
+ if ( presto_no_journal(fset) ) {
+ EXIT;
+ return 0;
+ }
+
+ root = fset->fset_dentry;
+
+ uid = cpu_to_le32(dentry->d_inode->i_uid);
+ gid = cpu_to_le32(dentry->d_inode->i_gid);
+ lmode = cpu_to_le32(mode);
+
+ BUFF_ALLOC(buffer, NULL);
+ path = presto_path(dentry, root, buffer, PAGE_SIZE);
+ pathlen = cpu_to_le32(MYPATHLEN(buffer, path));
+ size = sizeof(__u32) * current->ngroups +
+ sizeof(struct kml_prefix_hdr) + 3 * sizeof(*tgt_dir_ver) +
+ sizeof(lmode) + sizeof(uid) + sizeof(gid) + sizeof(pathlen) +
+ sizeof(struct kml_suffix);
+
+ if ( size > sizeof(record) )
+ CERROR("InterMezzo: BUFFER OVERFLOW in %s!\n", __FUNCTION__);
+
+ rec->is_kml = 1;
+ rec->size = size + size_round(le32_to_cpu(pathlen));
+ logrecord = journal_log_prefix(record, opcode, rec);
+
+ logrecord = log_version(logrecord, tgt_dir_ver);
+ logrecord = log_dentry_version(logrecord, dentry->d_parent);
+ logrecord = log_version(logrecord, new_dir_ver);
+ logrecord = logit(logrecord, &lmode, sizeof(lmode));
+ logrecord = logit(logrecord, &uid, sizeof(uid));
+ logrecord = logit(logrecord, &gid, sizeof(gid));
+ logrecord = logit(logrecord, &pathlen, sizeof(pathlen));
+ logrecord = journal_log_suffix(logrecord, record, fset, dentry, rec);
+
+ error = presto_log(fset, rec, record, size,
+ path, size_round(le32_to_cpu(pathlen)),
+ NULL, 0, NULL, 0);
+
+ BUFF_FREE(buffer);
+ EXIT;
+ return error;
+}
+
+
+int
+presto_journal_rmdir(struct rec_info *rec, struct presto_file_set *fset,
+ struct dentry *dir, struct presto_version *tgt_dir_ver,
+ struct presto_version *old_dir_ver,
+ struct izo_rollback_data *rb, int len, const char *name)
+{
+ int opcode = KML_OPCODE_RMDIR;
+ char *buffer, *path, *logrecord, record[316];
+ __u32 pathlen, llen;
+ struct dentry *root;
+ int error, size;
+
+ ENTRY;
+ if ( presto_no_journal(fset) ) {
+ EXIT;
+ return 0;
+ }
+
+ root = fset->fset_dentry;
+
+ llen = cpu_to_le32(len);
+ BUFF_ALLOC(buffer, NULL);
+ path = presto_path(dir, root, buffer, PAGE_SIZE);
+ pathlen = cpu_to_le32(MYPATHLEN(buffer, path));
+ size = sizeof(__u32) * current->ngroups +
+ sizeof(struct kml_prefix_hdr) + 3 * sizeof(*tgt_dir_ver) +
+ sizeof(pathlen) + sizeof(llen) + sizeof(*rb) +
+ sizeof(struct kml_suffix);
+
+ if ( size > sizeof(record) )
+ CERROR("InterMezzo: BUFFER OVERFLOW in %s!\n", __FUNCTION__);
+
+ CDEBUG(D_JOURNAL, "path: %s (%d), name: %s (%d), size %d\n",
+ path, pathlen, name, len, size);
+
+ rec->is_kml = 1;
+ rec->size = size + size_round(le32_to_cpu(pathlen)) +
+ size_round(len);
+
+ logrecord = journal_log_prefix(record, opcode, rec);
+ logrecord = log_version(logrecord, tgt_dir_ver);
+ logrecord = log_dentry_version(logrecord, dir);
+ logrecord = log_version(logrecord, old_dir_ver);
+ logrecord = logit(logrecord, rb, sizeof(*rb));
+ logrecord = logit(logrecord, &pathlen, sizeof(pathlen));
+ logrecord = logit(logrecord, &llen, sizeof(llen));
+ logrecord = journal_log_suffix(logrecord, record, fset, dir, rec);
+ error = presto_log(fset, rec, record, size,
+ path, size_round(le32_to_cpu(pathlen)),
+ name, size_round(len),
+ NULL, 0);
+
+ BUFF_FREE(buffer);
+ EXIT;
+ return error;
+}
+
+
+int
+presto_journal_mknod(struct rec_info *rec, struct presto_file_set *fset,
+ struct dentry *dentry, struct presto_version *tgt_dir_ver,
+ struct presto_version *new_node_ver, int mode,
+ int dmajor, int dminor )
+{
+ int opcode = KML_OPCODE_MKNOD;
+ char *buffer, *path, *logrecord, record[292];
+ struct dentry *root;
+ __u32 uid, gid, lmode, lmajor, lminor, pathlen;
+ int error, size;
+
+ ENTRY;
+ if ( presto_no_journal(fset) ) {
+ EXIT;
+ return 0;
+ }
+
+ root = fset->fset_dentry;
+
+ uid = cpu_to_le32(dentry->d_inode->i_uid);
+ gid = cpu_to_le32(dentry->d_inode->i_gid);
+ lmode = cpu_to_le32(mode);
+ lmajor = cpu_to_le32(dmajor);
+ lminor = cpu_to_le32(dminor);
+
+ BUFF_ALLOC(buffer, NULL);
+ path = presto_path(dentry, root, buffer, PAGE_SIZE);
+ pathlen = cpu_to_le32(MYPATHLEN(buffer, path));
+ size = sizeof(__u32) * current->ngroups +
+ sizeof(struct kml_prefix_hdr) + 3 * sizeof(*tgt_dir_ver) +
+ sizeof(lmode) + sizeof(uid) + sizeof(gid) + sizeof(lmajor) +
+ sizeof(lminor) + sizeof(pathlen) +
+ sizeof(struct kml_suffix);
+
+ if ( size > sizeof(record) )
+ CERROR("InterMezzo: BUFFER OVERFLOW in %s!\n", __FUNCTION__);
+
+ rec->is_kml = 1;
+ rec->size = size + size_round(le32_to_cpu(pathlen));
+
+ logrecord = journal_log_prefix(record, opcode, rec);
+ logrecord = log_version(logrecord, tgt_dir_ver);
+ logrecord = log_dentry_version(logrecord, dentry->d_parent);
+ logrecord = log_version(logrecord, new_node_ver);
+ logrecord = logit(logrecord, &lmode, sizeof(lmode));
+ logrecord = logit(logrecord, &uid, sizeof(uid));
+ logrecord = logit(logrecord, &gid, sizeof(gid));
+ logrecord = logit(logrecord, &lmajor, sizeof(lmajor));
+ logrecord = logit(logrecord, &lminor, sizeof(lminor));
+ logrecord = logit(logrecord, &pathlen, sizeof(pathlen));
+ logrecord = journal_log_suffix(logrecord, record, fset, dentry, rec);
+
+ error = presto_log(fset, rec, record, size,
+ path, size_round(le32_to_cpu(pathlen)),
+ NULL, 0, NULL, 0);
+
+ BUFF_FREE(buffer);
+ EXIT;
+ return error;
+}
+
+int
+presto_journal_link(struct rec_info *rec, struct presto_file_set *fset,
+ struct dentry *src, struct dentry *tgt,
+ struct presto_version *tgt_dir_ver,
+ struct presto_version *new_link_ver)
+{
+ int opcode = KML_OPCODE_LINK;
+ char *buffer, *srcbuffer, *path, *srcpath, *logrecord, record[292];
+ __u32 pathlen, srcpathlen;
+ struct dentry *root;
+ int error, size;
+
+ ENTRY;
+ if ( presto_no_journal(fset) ) {
+ EXIT;
+ return 0;
+ }
+
+ root = fset->fset_dentry;
+
+ BUFF_ALLOC(srcbuffer, NULL);
+ srcpath = presto_path(src, root, srcbuffer, PAGE_SIZE);
+ srcpathlen = cpu_to_le32(MYPATHLEN(srcbuffer, srcpath));
+
+ BUFF_ALLOC(buffer, srcbuffer);
+ path = presto_path(tgt, root, buffer, PAGE_SIZE);
+ pathlen = cpu_to_le32(MYPATHLEN(buffer, path));
+ size = sizeof(__u32) * current->ngroups +
+ sizeof(struct kml_prefix_hdr) + 3 * sizeof(*tgt_dir_ver) +
+ sizeof(srcpathlen) + sizeof(pathlen) +
+ sizeof(struct kml_suffix);
+
+ if ( size > sizeof(record) )
+ CERROR("InterMezzo: BUFFER OVERFLOW in %s!\n", __FUNCTION__);
+
+ rec->is_kml = 1;
+ rec->size = size + size_round(le32_to_cpu(pathlen)) +
+ size_round(le32_to_cpu(srcpathlen));
+
+ logrecord = journal_log_prefix(record, opcode, rec);
+ logrecord = log_version(logrecord, tgt_dir_ver);
+ logrecord = log_dentry_version(logrecord, tgt->d_parent);
+ logrecord = log_version(logrecord, new_link_ver);
+ logrecord = logit(logrecord, &srcpathlen, sizeof(srcpathlen));
+ logrecord = logit(logrecord, &pathlen, sizeof(pathlen));
+ logrecord = journal_log_suffix(logrecord, record, fset, tgt, rec);
+
+ error = presto_log(fset, rec, record, size,
+ srcpath, size_round(le32_to_cpu(srcpathlen)),
+ path, size_round(le32_to_cpu(pathlen)),
+ NULL, 0);
+
+ BUFF_FREE(srcbuffer);
+ BUFF_FREE(buffer);
+ EXIT;
+ return error;
+}
+
+
+int presto_journal_rename(struct rec_info *rec, struct presto_file_set *fset,
+ struct dentry *src, struct dentry *tgt,
+ struct presto_version *src_dir_ver,
+ struct presto_version *tgt_dir_ver)
+{
+ int opcode = KML_OPCODE_RENAME;
+ char *buffer, *srcbuffer, *path, *srcpath, *logrecord, record[292];
+ __u32 pathlen, srcpathlen;
+ struct dentry *root;
+ int error, size;
+
+ ENTRY;
+ if ( presto_no_journal(fset) ) {
+ EXIT;
+ return 0;
+ }
+
+ root = fset->fset_dentry;
+
+ BUFF_ALLOC(srcbuffer, NULL);
+ srcpath = presto_path(src, root, srcbuffer, PAGE_SIZE);
+ srcpathlen = cpu_to_le32(MYPATHLEN(srcbuffer, srcpath));
+
+ BUFF_ALLOC(buffer, srcbuffer);
+ path = presto_path(tgt, root, buffer, PAGE_SIZE);
+ pathlen = cpu_to_le32(MYPATHLEN(buffer, path));
+ size = sizeof(__u32) * current->ngroups +
+ sizeof(struct kml_prefix_hdr) + 4 * sizeof(*src_dir_ver) +
+ sizeof(srcpathlen) + sizeof(pathlen) +
+ sizeof(struct kml_suffix);
+
+ if ( size > sizeof(record) )
+ CERROR("InterMezzo: BUFFER OVERFLOW in %s!\n", __FUNCTION__);
+
+ rec->is_kml = 1;
+ rec->size = size + size_round(le32_to_cpu(pathlen)) +
+ size_round(le32_to_cpu(srcpathlen));
+
+ logrecord = journal_log_prefix(record, opcode, rec);
+ logrecord = log_version(logrecord, src_dir_ver);
+ logrecord = log_dentry_version(logrecord, src->d_parent);
+ logrecord = log_version(logrecord, tgt_dir_ver);
+ logrecord = log_dentry_version(logrecord, tgt->d_parent);
+ logrecord = logit(logrecord, &srcpathlen, sizeof(srcpathlen));
+ logrecord = logit(logrecord, &pathlen, sizeof(pathlen));
+ logrecord = journal_log_suffix(logrecord, record, fset, tgt, rec);
+
+ error = presto_log(fset, rec, record, size,
+ srcpath, size_round(le32_to_cpu(srcpathlen)),
+ path, size_round(le32_to_cpu(pathlen)),
+ NULL, 0);
+
+ BUFF_FREE(buffer);
+ BUFF_FREE(srcbuffer);
+ EXIT;
+ return error;
+}
+
+int presto_journal_unlink(struct rec_info *rec, struct presto_file_set *fset,
+ struct dentry *dir, struct presto_version *tgt_dir_ver,
+ struct presto_version *old_file_ver,
+ struct izo_rollback_data *rb, struct dentry *dentry,
+ char *old_target, int old_targetlen)
+{
+ int opcode = KML_OPCODE_UNLINK;
+ char *buffer, *path, *logrecord, record[316];
+ const char *name;
+ __u32 pathlen, llen;
+ struct dentry *root;
+ int error, size, len;
+
+ ENTRY;
+ if ( presto_no_journal(fset) ) {
+ EXIT;
+ return 0;
+ }
+
+ root = fset->fset_dentry;
+
+ name = dentry->d_name.name;
+ len = dentry->d_name.len;
+
+ llen = cpu_to_le32(len);
+ BUFF_ALLOC(buffer, NULL);
+ path = presto_path(dir, root, buffer, PAGE_SIZE);
+ pathlen = cpu_to_le32(MYPATHLEN(buffer, path));
+ size = sizeof(__u32) * current->ngroups +
+ sizeof(struct kml_prefix_hdr) + 3 * sizeof(*tgt_dir_ver) +
+ sizeof(pathlen) + sizeof(llen) + sizeof(*rb) +
+ sizeof(old_targetlen) + sizeof(struct kml_suffix);
+
+ if ( size > sizeof(record) )
+ CERROR("InterMezzo: BUFFER OVERFLOW in %s!\n", __FUNCTION__);
+
+ rec->is_kml = 1;
+ rec->size = size + size_round(le32_to_cpu(pathlen)) + size_round(len) +
+ size_round(old_targetlen);
+
+ logrecord = journal_log_prefix(record, opcode, rec);
+ logrecord = log_version(logrecord, tgt_dir_ver);
+ logrecord = log_dentry_version(logrecord, dir);
+ logrecord = log_version(logrecord, old_file_ver);
+ logrecord = log_rollback(logrecord, rb);
+ logrecord = logit(logrecord, &pathlen, sizeof(pathlen));
+ logrecord = logit(logrecord, &llen, sizeof(llen));
+ logrecord = logit(logrecord, &old_targetlen, sizeof(old_targetlen));
+ logrecord = journal_log_suffix(logrecord, record, fset, dir, rec);
+
+ error = presto_log(fset, rec, record, size,
+ path, size_round(le32_to_cpu(pathlen)),
+ name, size_round(len),
+ old_target, size_round(old_targetlen));
+
+ BUFF_FREE(buffer);
+ EXIT;
+ return error;
+}
+
+int
+presto_journal_close(struct rec_info *rec, struct presto_file_set *fset,
+ struct file *file, struct dentry *dentry,
+ struct presto_version *old_file_ver,
+ struct presto_version *new_file_ver)
+{
+ int opcode = KML_OPCODE_CLOSE;
+ struct presto_file_data *fd;
+ char *buffer, *path, *logrecord, record[316];
+ struct dentry *root;
+ int error, size, i;
+ __u32 pathlen, generation;
+ __u64 ino;
+ __u32 open_fsuid;
+ __u32 open_fsgid;
+ __u32 open_ngroups;
+ __u32 open_groups[NGROUPS_MAX];
+ __u32 open_mode;
+ __u32 open_uid;
+ __u32 open_gid;
+
+ ENTRY;
+
+ if ( presto_no_journal(fset) ) {
+ EXIT;
+ return 0;
+ }
+
+ if (!dentry->d_inode || (dentry->d_inode->i_nlink == 0)
+ || ((dentry->d_parent != dentry) && list_empty(&dentry->d_hash))) {
+ EXIT;
+ return 0;
+ }
+
+ root = fset->fset_dentry;
+
+ fd = (struct presto_file_data *)file->private_data;
+ if (fd) {
+ open_ngroups = fd->fd_ngroups;
+ for (i = 0; i < fd->fd_ngroups; i++)
+ open_groups[i] = (__u32) fd->fd_groups[i];
+ open_mode = fd->fd_mode;
+ open_uid = fd->fd_uid;
+ open_gid = fd->fd_gid;
+ open_fsuid = fd->fd_fsuid;
+ open_fsgid = fd->fd_fsgid;
+ } else {
+ open_ngroups = current->ngroups;
+ for (i=0; i<current->ngroups; i++)
+ open_groups[i] = (__u32) current->groups[i];
+ open_mode = dentry->d_inode->i_mode;
+ open_uid = dentry->d_inode->i_uid;
+ open_gid = dentry->d_inode->i_gid;
+ open_fsuid = current->fsuid;
+ open_fsgid = current->fsgid;
+ }
+ BUFF_ALLOC(buffer, NULL);
+ path = presto_path(dentry, root, buffer, PAGE_SIZE);
+ pathlen = cpu_to_le32(MYPATHLEN(buffer, path));
+ ino = cpu_to_le64(dentry->d_inode->i_ino);
+ generation = cpu_to_le32(dentry->d_inode->i_generation);
+ size = sizeof(__u32) * open_ngroups +
+ sizeof(open_mode) + sizeof(open_uid) + sizeof(open_gid) +
+ sizeof(struct kml_prefix_hdr) + sizeof(*old_file_ver) +
+ sizeof(*new_file_ver) + sizeof(ino) + sizeof(generation) +
+ sizeof(pathlen) + sizeof(struct kml_suffix);
+
+ if ( size > sizeof(record) )
+ CERROR("InterMezzo: BUFFER OVERFLOW in %s!\n", __FUNCTION__);
+
+ rec->is_kml = 1;
+ rec->size = size + size_round(le32_to_cpu(pathlen));
+
+ logrecord = journal_log_prefix_with_groups_and_ids(
+ record, opcode, rec, open_ngroups, open_groups,
+ open_fsuid, open_fsgid);
+ logrecord = logit(logrecord, &open_mode, sizeof(open_mode));
+ logrecord = logit(logrecord, &open_uid, sizeof(open_uid));
+ logrecord = logit(logrecord, &open_gid, sizeof(open_gid));
+ logrecord = log_version(logrecord, old_file_ver);
+ logrecord = log_version(logrecord, new_file_ver);
+ logrecord = logit(logrecord, &ino, sizeof(ino));
+ logrecord = logit(logrecord, &generation, sizeof(generation));
+ logrecord = logit(logrecord, &pathlen, sizeof(pathlen));
+ logrecord = journal_log_suffix(logrecord, record, fset, dentry, rec);
+
+ error = presto_log(fset, rec, record, size,
+ path, size_round(le32_to_cpu(pathlen)),
+ NULL, 0, NULL, 0);
+ BUFF_FREE(buffer);
+
+ EXIT;
+ return error;
+}
+
+int presto_rewrite_close(struct rec_info *rec, struct presto_file_set *fset,
+ char *path, __u32 pathlen,
+ int ngroups, __u32 *groups,
+ __u64 ino, __u32 generation,
+ struct presto_version *new_file_ver)
+{
+ int opcode = KML_OPCODE_CLOSE;
+ char *logrecord, record[292];
+ struct dentry *root;
+ int error, size;
+
+ ENTRY;
+
+ if ( presto_no_journal(fset) ) {
+ EXIT;
+ return 0;
+ }
+
+ root = fset->fset_dentry;
+
+ size = sizeof(__u32) * ngroups +
+ sizeof(struct kml_prefix_hdr) + sizeof(*new_file_ver) +
+ sizeof(ino) + sizeof(generation) +
+ sizeof(le32_to_cpu(pathlen)) +
+ sizeof(struct kml_suffix);
+
+ if ( size > sizeof(record) )
+ CERROR("InterMezzo: BUFFER OVERFLOW in %s!\n", __FUNCTION__);
+
+ rec->is_kml = 1;
+ rec->size = size + size_round(le32_to_cpu(pathlen));
+
+ logrecord = journal_log_prefix_with_groups(record, opcode, rec,
+ ngroups, groups);
+ logrecord = log_version(logrecord, new_file_ver);
+ logrecord = logit(logrecord, &ino, sizeof(ino));
+ logrecord = logit(logrecord, &generation, sizeof(generation));
+ logrecord = logit(logrecord, &pathlen, sizeof(pathlen));
+ logrecord = journal_log_suffix(logrecord, record, fset, NULL, rec);
+
+ error = presto_log(fset, rec, record, size,
+ path, size_round(le32_to_cpu(pathlen)),
+ NULL, 0, NULL, 0);
+
+ EXIT;
+ return error;
+}
+
+
+/* write closes for the local close records in the LML */
+int presto_complete_lml(struct presto_file_set *fset)
+{
+ __u32 groups[NGROUPS_MAX];
+ loff_t lml_offset;
+ loff_t read_offset;
+ char *buffer;
+ void *handle;
+ struct rec_info rec;
+ struct close_rec {
+ struct presto_version new_file_ver;
+ __u64 ino;
+ __u32 generation;
+ __u32 pathlen;
+ __u64 remote_ino;
+ __u32 remote_generation;
+ __u32 remote_version;
+ __u64 lml_offset;
+ } close_rec;
+ struct file *file = fset->fset_lml.fd_file;
+ struct kml_prefix_hdr prefix;
+ int rc = 0;
+ ENTRY;
+
+ lml_offset = 0;
+ again:
+ if (lml_offset >= file->f_dentry->d_inode->i_size) {
+ EXIT;
+ return rc;
+ }
+
+ read_offset = lml_offset;
+ rc = presto_fread(file, (char *)&prefix,
+ sizeof(prefix), &read_offset);
+ if ( rc != sizeof(prefix) ) {
+ EXIT;
+ CERROR("presto_complete_lml: ioerror - 1, tell Peter\n");
+ return -EIO;
+ }
+
+ if ( prefix.opcode == KML_OPCODE_NOOP ) {
+ lml_offset += prefix.len;
+ goto again;
+ }
+
+ rc = presto_fread(file, (char *)groups,
+ prefix.ngroups * sizeof(__u32), &read_offset);
+ if ( rc != prefix.ngroups * sizeof(__u32) ) {
+ EXIT;
+ CERROR("presto_complete_lml: ioerror - 2, tell Peter\n");
+ return -EIO;
+ }
+
+ rc = presto_fread(file, (char *)&close_rec,
+ sizeof(close_rec), &read_offset);
+ if ( rc != sizeof(close_rec) ) {
+ EXIT;
+ CERROR("presto_complete_lml: ioerror - 3, tell Peter\n");
+ return -EIO;
+ }
+
+ /* is this a backfetch or a close record? */
+ if ( le64_to_cpu(close_rec.remote_ino) != 0 ) {
+ lml_offset += prefix.len;
+ goto again;
+ }
+
+ BUFF_ALLOC(buffer, NULL);
+ rc = presto_fread(file, (char *)buffer,
+ le32_to_cpu(close_rec.pathlen), &read_offset);
+ if ( rc != le32_to_cpu(close_rec.pathlen) ) {
+ EXIT;
+ CERROR("presto_complete_lml: ioerror - 4, tell Peter\n");
+ return -EIO;
+ }
+
+ handle = presto_trans_start(fset, file->f_dentry->d_inode,
+ KML_OPCODE_RELEASE);
+ if ( IS_ERR(handle) ) {
+ EXIT;
+ return -ENOMEM;
+ }
+
+ rc = presto_clear_lml_close(fset, lml_offset);
+ if ( rc ) {
+ CERROR("error during clearing: %d\n", rc);
+ presto_trans_commit(fset, handle);
+ EXIT;
+ return rc;
+ }
+
+ rc = presto_rewrite_close(&rec, fset, buffer, close_rec.pathlen,
+ prefix.ngroups, groups,
+ close_rec.ino, close_rec.generation,
+ &close_rec.new_file_ver);
+ if ( rc ) {
+ CERROR("error during rewrite close: %d\n", rc);
+ presto_trans_commit(fset, handle);
+ EXIT;
+ return rc;
+ }
+
+ presto_trans_commit(fset, handle);
+ if ( rc ) {
+ CERROR("error during truncation: %d\n", rc);
+ EXIT;
+ return rc;
+ }
+
+ lml_offset += prefix.len;
+ CDEBUG(D_JOURNAL, "next LML record at: %ld\n", (long)lml_offset);
+ goto again;
+
+ EXIT;
+ return -EINVAL;
+}
+
+
+#ifdef CONFIG_FS_EXT_ATTR
+/* Journal an ea operation. A NULL buffer implies the attribute is
+ * getting deleted. In this case we simply change the opcode, but nothing
+ * else is affected.
+ */
+int presto_journal_set_ext_attr (struct rec_info *rec,
+ struct presto_file_set *fset,
+ struct dentry *dentry,
+ struct presto_version *ver, const char *name,
+ const char *buffer, int buffer_len,
+ int flags)
+{
+ int opcode = (buffer == NULL) ?
+ KML_OPCODE_DELEXTATTR :
+ KML_OPCODE_SETEXTATTR ;
+ char *temp, *path, *logrecord, record[292];
+ struct dentry *root;
+ int error, size;
+ __u32 namelen=cpu_to_le32(strnlen(name,PRESTO_EXT_ATTR_NAME_MAX));
+ __u32 buflen=(buffer != NULL)? cpu_to_le32(buffer_len): cpu_to_le32(0);
+ __u32 mode, pathlen;
+
+ ENTRY;
+ if ( presto_no_journal(fset) ) {
+ EXIT;
+ return 0;
+ }
+
+ if (!dentry->d_inode || (dentry->d_inode->i_nlink == 0)
+ || ((dentry->d_parent != dentry) && list_empty(&dentry->d_hash))) {
+ EXIT;
+ return 0;
+ }
+
+ root = fset->fset_dentry;
+
+ BUFF_ALLOC(temp, NULL);
+ path = presto_path(dentry, root, temp, PAGE_SIZE);
+ pathlen = cpu_to_le32(MYPATHLEN(temp, path));
+
+ flags=cpu_to_le32(flags);
+ /* Ugly, but needed. posix ACLs change the mode without using
+ * setattr, we need to record these changes. The EA code per se
+ * is not really affected.
+ */
+ mode=cpu_to_le32(dentry->d_inode->i_mode);
+
+ size = sizeof(__u32) * current->ngroups +
+ sizeof(struct kml_prefix_hdr) +
+ 2 * sizeof(struct presto_version) +
+ sizeof(flags) + sizeof(mode) + sizeof(namelen) +
+ sizeof(buflen) + sizeof(pathlen) +
+ sizeof(struct kml_suffix);
+
+ if ( size > sizeof(record) )
+ CERROR("InterMezzo: BUFFER OVERFLOW in %s!\n", __FUNCTION__);
+
+ rec->is_kml = 1;
+ /* Make space for a path, a attr name and value*/
+ /* We use the buflen instead of buffer_len to make sure that we
+ * journal the right length. This may be a little paranoid, but
+ * with 64 bits round the corner, I would rather be safe than sorry!
+ * Also this handles deletes with non-zero buffer_lengths correctly.
+ * SHP
+ */
+ rec->size = size + size_round(le32_to_cpu(pathlen)) +
+ size_round(le32_to_cpu(namelen)) +
+ size_round(le32_to_cpu(buflen));
+
+ logrecord = journal_log_prefix(record, opcode, rec);
+ logrecord = log_version(logrecord, ver);
+ logrecord = log_dentry_version(logrecord, dentry);
+ logrecord = logit(logrecord, &flags, sizeof(flags));
+ logrecord = logit(logrecord, &mode, sizeof(flags));
+ logrecord = logit(logrecord, &pathlen, sizeof(pathlen));
+ logrecord = logit(logrecord, &namelen, sizeof(namelen));
+ logrecord = logit(logrecord, &buflen, sizeof(buflen));
+ logrecord = journal_log_suffix(logrecord, record, fset, dentry, rec);
+
+ error = presto_log(fset, rec, record, size,
+ path, size_round(le32_to_cpu(pathlen)),
+ name, size_round(le32_to_cpu(namelen)),
+ buffer, size_round(le32_to_cpu(buflen)));
+
+ BUFF_FREE(temp);
+ EXIT;
+ return error;
+}
+#endif
diff --git a/fs/intermezzo/journal_ext2.c b/fs/intermezzo/journal_ext2.c
new file mode 100644
index 00000000000000..8a76e2a63266c3
--- /dev/null
+++ b/fs/intermezzo/journal_ext2.c
@@ -0,0 +1,91 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Copyright (C) 1998 Peter J. Braam <braam@clusterfs.com>
+ *
+ * This file is part of InterMezzo, http://www.inter-mezzo.org.
+ *
+ * InterMezzo is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * InterMezzo 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 InterMezzo; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+#include <linux/string.h>
+#include <linux/ext2_fs.h>
+
+#include <linux/intermezzo_fs.h>
+#include <linux/intermezzo_psdev.h>
+
+#if defined(CONFIG_EXT2_FS)
+
+/* EXT2 has no journalling, so these functions do nothing */
+static loff_t presto_e2_freespace(struct presto_cache *cache,
+ struct super_block *sb)
+{
+ unsigned long freebl = le32_to_cpu(sb->u.ext2_sb.s_es->s_free_blocks_count);
+ unsigned long avail = freebl - le32_to_cpu(sb->u.ext2_sb.s_es->s_r_blocks_count);
+ return (avail << EXT2_BLOCK_SIZE_BITS(sb));
+}
+
+/* start the filesystem journal operations */
+static void *presto_e2_trans_start(struct presto_file_set *fset, struct inode *inode, int op)
+{
+ __u32 avail_kmlblocks;
+
+ if ( presto_no_journal(fset) ||
+ strcmp(fset->fset_cache->cache_type, "ext2"))
+ return NULL;
+
+ avail_kmlblocks = inode->i_sb->u.ext2_sb.s_es->s_free_blocks_count;
+
+ if ( avail_kmlblocks < 3 ) {
+ return ERR_PTR(-ENOSPC);
+ }
+
+ if ( (op != KML_OPCODE_UNLINK && op != KML_OPCODE_RMDIR)
+ && avail_kmlblocks < 6 ) {
+ return ERR_PTR(-ENOSPC);
+ }
+ return (void *) 1;
+}
+
+static void presto_e2_trans_commit(struct presto_file_set *fset, void *handle)
+{
+ do {} while (0);
+}
+
+static int presto_e2_has_all_data(struct inode *inode)
+{
+ BUG();
+ return 0;
+}
+
+struct journal_ops presto_ext2_journal_ops = {
+ tr_all_data: presto_e2_has_all_data,
+ tr_avail: presto_e2_freespace,
+ tr_start: presto_e2_trans_start,
+ tr_commit: presto_e2_trans_commit,
+ tr_journal_data: NULL
+};
+
+#endif /* CONFIG_EXT2_FS */
diff --git a/fs/intermezzo/journal_ext3.c b/fs/intermezzo/journal_ext3.c
new file mode 100644
index 00000000000000..de005486aad4c7
--- /dev/null
+++ b/fs/intermezzo/journal_ext3.c
@@ -0,0 +1,285 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Copyright (C) 1998 Peter J. Braam <braam@clusterfs.com>
+ * Copyright (C) 2000 Red Hat, Inc.
+ * Copyright (C) 2000 Los Alamos National Laboratory
+ * Copyright (C) 2000 TurboLinux, Inc.
+ * Copyright (C) 2001 Mountain View Data, Inc.
+ * Copyright (C) 2001 Tacit Networks, Inc. <phil@off.net>
+ *
+ * This file is part of InterMezzo, http://www.inter-mezzo.org.
+ *
+ * InterMezzo is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * InterMezzo 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 InterMezzo; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/types.h>
+#include <linux/param.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+#include <linux/string.h>
+#include <linux/smp_lock.h>
+#if defined(CONFIG_EXT3_FS) || defined (CONFIG_EXT3_FS_MODULE)
+#include <linux/jbd.h>
+#include <linux/ext3_fs.h>
+#include <linux/ext3_jbd.h>
+#endif
+
+#include <linux/intermezzo_fs.h>
+#include <linux/intermezzo_psdev.h>
+
+#if defined(CONFIG_EXT3_FS) || defined (CONFIG_EXT3_FS_MODULE)
+
+#define MAX_PATH_BLOCKS(inode) (PATH_MAX >> EXT3_BLOCK_SIZE_BITS((inode)->i_sb))
+#define MAX_NAME_BLOCKS(inode) (NAME_MAX >> EXT3_BLOCK_SIZE_BITS((inode)->i_sb))
+
+/* space requirements:
+ presto_do_truncate:
+ used to truncate the KML forward to next fset->chunksize boundary
+ - zero partial block
+ - update inode
+ presto_write_record:
+ write header (< one block)
+ write one path (< MAX_PATHLEN)
+ possibly write another path (< MAX_PATHLEN)
+ write suffix (< one block)
+ presto_update_last_rcvd
+ write one block
+*/
+
+static loff_t presto_e3_freespace(struct presto_cache *cache,
+ struct super_block *sb)
+{
+ loff_t freebl = le32_to_cpu(sb->u.ext3_sb.s_es->s_free_blocks_count);
+ loff_t avail = freebl -
+ le32_to_cpu(sb->u.ext3_sb.s_es->s_r_blocks_count);
+ return (avail << EXT3_BLOCK_SIZE_BITS(sb));
+}
+
+/* start the filesystem journal operations */
+static void *presto_e3_trans_start(struct presto_file_set *fset,
+ struct inode *inode,
+ int op)
+{
+ int jblocks;
+ int trunc_blks, one_path_blks, extra_path_blks,
+ extra_name_blks, lml_blks;
+ __u32 avail_kmlblocks;
+ handle_t *handle;
+
+ if ( presto_no_journal(fset) ||
+ strcmp(fset->fset_cache->cache_type, "ext3"))
+ {
+ CDEBUG(D_JOURNAL, "got cache_type \"%s\"\n",
+ fset->fset_cache->cache_type);
+ return NULL;
+ }
+
+ avail_kmlblocks = inode->i_sb->u.ext3_sb.s_es->s_free_blocks_count;
+
+ if ( avail_kmlblocks < 3 ) {
+ return ERR_PTR(-ENOSPC);
+ }
+
+ if ( (op != KML_OPCODE_UNLINK && op != KML_OPCODE_RMDIR)
+ && avail_kmlblocks < 6 ) {
+ return ERR_PTR(-ENOSPC);
+ }
+
+ /* Need journal space for:
+ at least three writes to KML (two one block writes, one a path)
+ possibly a second name (unlink, rmdir)
+ possibly a second path (symlink, rename)
+ a one block write to the last rcvd file
+ */
+
+ trunc_blks = EXT3_DATA_TRANS_BLOCKS + 1;
+ one_path_blks = 4*EXT3_DATA_TRANS_BLOCKS + MAX_PATH_BLOCKS(inode) + 3;
+ lml_blks = 4*EXT3_DATA_TRANS_BLOCKS + MAX_PATH_BLOCKS(inode) + 2;
+ extra_path_blks = EXT3_DATA_TRANS_BLOCKS + MAX_PATH_BLOCKS(inode);
+ extra_name_blks = EXT3_DATA_TRANS_BLOCKS + MAX_NAME_BLOCKS(inode);
+
+ /* additional blocks appear for "two pathname" operations
+ and operations involving the LML records
+ */
+ switch (op) {
+ case KML_OPCODE_TRUNC:
+ jblocks = one_path_blks + extra_name_blks + trunc_blks
+ + EXT3_DELETE_TRANS_BLOCKS;
+ break;
+ case KML_OPCODE_KML_TRUNC:
+ /* Hopefully this is a little better, but I'm still mostly
+ * guessing here. */
+ /* unlink 1 */
+ jblocks = extra_name_blks + trunc_blks +
+ EXT3_DELETE_TRANS_BLOCKS + 2;
+
+ /* unlink 2 */
+ jblocks += extra_name_blks + trunc_blks +
+ EXT3_DELETE_TRANS_BLOCKS + 2;
+
+ /* rename 1 */
+ jblocks += 2 * extra_path_blks + trunc_blks +
+ 2 * EXT3_DATA_TRANS_BLOCKS + 2 + 3;
+
+ /* rename 2 */
+ jblocks += 2 * extra_path_blks + trunc_blks +
+ 2 * EXT3_DATA_TRANS_BLOCKS + 2 + 3;
+ break;
+ case KML_OPCODE_RELEASE:
+ /*
+ jblocks = one_path_blks + lml_blks + 2*trunc_blks;
+ */
+ jblocks = one_path_blks;
+ break;
+ case KML_OPCODE_SETATTR:
+ jblocks = one_path_blks + trunc_blks + 1 ;
+ break;
+ case KML_OPCODE_CREATE:
+ jblocks = one_path_blks + trunc_blks
+ + EXT3_DATA_TRANS_BLOCKS + 3 + 2;
+ break;
+ case KML_OPCODE_LINK:
+ jblocks = one_path_blks + trunc_blks
+ + EXT3_DATA_TRANS_BLOCKS + 2;
+ break;
+ case KML_OPCODE_UNLINK:
+ jblocks = one_path_blks + extra_name_blks + trunc_blks
+ + EXT3_DELETE_TRANS_BLOCKS + 2;
+ break;
+ case KML_OPCODE_SYMLINK:
+ jblocks = one_path_blks + extra_path_blks + trunc_blks
+ + EXT3_DATA_TRANS_BLOCKS + 5;
+ break;
+ case KML_OPCODE_MKDIR:
+ jblocks = one_path_blks + trunc_blks
+ + EXT3_DATA_TRANS_BLOCKS + 4 + 2;
+ break;
+ case KML_OPCODE_RMDIR:
+ jblocks = one_path_blks + extra_name_blks + trunc_blks
+ + EXT3_DELETE_TRANS_BLOCKS + 1;
+ break;
+ case KML_OPCODE_MKNOD:
+ jblocks = one_path_blks + trunc_blks +
+ EXT3_DATA_TRANS_BLOCKS + 3 + 2;
+ break;
+ case KML_OPCODE_RENAME:
+ jblocks = one_path_blks + extra_path_blks + trunc_blks +
+ 2 * EXT3_DATA_TRANS_BLOCKS + 2 + 3;
+ break;
+ case KML_OPCODE_WRITE:
+ jblocks = one_path_blks;
+ /* add this when we can wrap our transaction with
+ that of ext3_file_write (ordered writes)
+ + EXT3_DATA_TRANS_BLOCKS;
+ */
+ break;
+ default:
+ CDEBUG(D_JOURNAL, "invalid operation %d for journal\n", op);
+ return NULL;
+ }
+
+ CDEBUG(D_JOURNAL, "creating journal handle (%d blocks) for op %d\n",
+ jblocks, op);
+ /* journal_start/stop does not do its own locking while updating
+ * the handle/transaction information. Hence we create our own
+ * critical section to protect these calls. -SHP
+ */
+ lock_kernel();
+ handle = journal_start(EXT3_JOURNAL(inode), jblocks);
+ unlock_kernel();
+ return handle;
+}
+
+static void presto_e3_trans_commit(struct presto_file_set *fset, void *handle)
+{
+ if ( presto_no_journal(fset) || !handle)
+ return;
+
+ /* See comments before journal_start above. -SHP */
+ lock_kernel();
+ journal_stop(handle);
+ unlock_kernel();
+}
+
+static void presto_e3_journal_file_data(struct inode *inode)
+{
+#ifdef EXT3_JOURNAL_DATA_FL
+ inode->u.ext3_i.i_flags |= EXT3_JOURNAL_DATA_FL;
+#else
+#warning You must have a facility to enable journaled writes for recovery!
+#endif
+}
+
+/* The logic here is a slightly modified version of ext3/inode.c:block_to_path
+ */
+static int presto_e3_has_all_data(struct inode *inode)
+{
+ int ptrs = EXT3_ADDR_PER_BLOCK(inode->i_sb);
+ int ptrs_bits = EXT3_ADDR_PER_BLOCK_BITS(inode->i_sb);
+ const long direct_blocks = EXT3_NDIR_BLOCKS,
+ indirect_blocks = ptrs,
+ double_blocks = (1 << (ptrs_bits * 2));
+ long block = (inode->i_size + inode->i_sb->s_blocksize - 1) >>
+ inode->i_sb->s_blocksize_bits;
+
+ ENTRY;
+
+ if (inode->i_size == 0) {
+ EXIT;
+ return 1;
+ }
+
+ if (block < direct_blocks) {
+ /* No indirect blocks, no problem. */
+ } else if (block < indirect_blocks + direct_blocks) {
+ block++;
+ } else if (block < double_blocks + indirect_blocks + direct_blocks) {
+ block += 2;
+ } else if (((block - double_blocks - indirect_blocks - direct_blocks)
+ >> (ptrs_bits * 2)) < ptrs) {
+ block += 3;
+ }
+
+ block *= (inode->i_sb->s_blocksize / 512);
+
+ CDEBUG(D_CACHE, "Need %ld blocks, have %ld.\n", block, inode->i_blocks);
+
+ if (block > inode->i_blocks) {
+ EXIT;
+ return 0;
+ }
+
+ EXIT;
+ return 1;
+}
+
+struct journal_ops presto_ext3_journal_ops = {
+ .tr_all_data = presto_e3_has_all_data,
+ .tr_avail = presto_e3_freespace,
+ .tr_start = presto_e3_trans_start,
+ .tr_commit = presto_e3_trans_commit,
+ .tr_journal_data = presto_e3_journal_file_data,
+ .tr_ilookup = presto_iget_ilookup
+};
+
+#endif /* CONFIG_EXT3_FS */
diff --git a/fs/intermezzo/journal_obdfs.c b/fs/intermezzo/journal_obdfs.c
new file mode 100644
index 00000000000000..7596c8c781bc28
--- /dev/null
+++ b/fs/intermezzo/journal_obdfs.c
@@ -0,0 +1,194 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Copyright (C) 1998 Peter J. Braam <braam@clusterfs.com>
+ * Copyright (C) 2000 Red Hat, Inc.
+ * Copyright (C) 2000 Los Alamos National Laboratory
+ * Copyright (C) 2000 TurboLinux, Inc.
+ * Copyright (C) 2001 Mountain View Data, Inc.
+ *
+ * This file is part of InterMezzo, http://www.inter-mezzo.org.
+ *
+ * InterMezzo is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * InterMezzo 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 InterMezzo; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/types.h>
+#include <linux/param.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+#include <linux/string.h>
+#ifdef CONFIG_OBDFS_FS
+#include /usr/src/obd/include/linux/obdfs.h
+#endif
+
+#include <linux/intermezzo_fs.h>
+#include <linux/intermezzo_psdev.h>
+
+#ifdef CONFIG_OBDFS_FS
+
+
+static unsigned long presto_obdfs_freespace(struct presto_file_set *fset,
+ struct super_block *sb)
+{
+ return 0x0fffff;
+}
+
+/* start the filesystem journal operations */
+static void *presto_obdfs_trans_start(struct presto_file_set *fset,
+ struct inode *inode,
+ int op)
+{
+
+ return (void *) 1;
+}
+
+#if 0
+ int jblocks;
+ int trunc_blks, one_path_blks, extra_path_blks,
+ extra_name_blks, lml_blks;
+ __u32 avail_kmlblocks;
+
+ if ( presto_no_journal(fset) ||
+ strcmp(fset->fset_cache->cache_type, "ext3"))
+ {
+ CDEBUG(D_JOURNAL, "got cache_type \"%s\"\n",
+ fset->fset_cache->cache_type);
+ return NULL;
+ }
+
+ avail_kmlblocks = inode->i_sb->u.ext3_sb.s_es->s_free_blocks_count;
+
+ if ( avail_kmlblocks < 3 ) {
+ return ERR_PTR(-ENOSPC);
+ }
+
+ if ( (op != PRESTO_OP_UNLINK && op != PRESTO_OP_RMDIR)
+ && avail_kmlblocks < 6 ) {
+ return ERR_PTR(-ENOSPC);
+ }
+
+ /* Need journal space for:
+ at least three writes to KML (two one block writes, one a path)
+ possibly a second name (unlink, rmdir)
+ possibly a second path (symlink, rename)
+ a one block write to the last rcvd file
+ */
+
+ trunc_blks = EXT3_DATA_TRANS_BLOCKS + 1;
+ one_path_blks = 4*EXT3_DATA_TRANS_BLOCKS + MAX_PATH_BLOCKS(inode) + 3;
+ lml_blks = 4*EXT3_DATA_TRANS_BLOCKS + MAX_PATH_BLOCKS(inode) + 2;
+ extra_path_blks = EXT3_DATA_TRANS_BLOCKS + MAX_PATH_BLOCKS(inode);
+ extra_name_blks = EXT3_DATA_TRANS_BLOCKS + MAX_NAME_BLOCKS(inode);
+
+ /* additional blocks appear for "two pathname" operations
+ and operations involving the LML records
+ */
+ switch (op) {
+ case PRESTO_OP_TRUNC:
+ jblocks = one_path_blks + extra_name_blks + trunc_blks
+ + EXT3_DELETE_TRANS_BLOCKS;
+ break;
+ case PRESTO_OP_RELEASE:
+ /*
+ jblocks = one_path_blks + lml_blks + 2*trunc_blks;
+ */
+ jblocks = one_path_blks;
+ break;
+ case PRESTO_OP_SETATTR:
+ jblocks = one_path_blks + trunc_blks + 1 ;
+ break;
+ case PRESTO_OP_CREATE:
+ jblocks = one_path_blks + trunc_blks
+ + EXT3_DATA_TRANS_BLOCKS + 3;
+ break;
+ case PRESTO_OP_LINK:
+ jblocks = one_path_blks + trunc_blks
+ + EXT3_DATA_TRANS_BLOCKS;
+ break;
+ case PRESTO_OP_UNLINK:
+ jblocks = one_path_blks + extra_name_blks + trunc_blks
+ + EXT3_DELETE_TRANS_BLOCKS;
+ break;
+ case PRESTO_OP_SYMLINK:
+ jblocks = one_path_blks + extra_path_blks + trunc_blks
+ + EXT3_DATA_TRANS_BLOCKS + 5;
+ break;
+ case PRESTO_OP_MKDIR:
+ jblocks = one_path_blks + trunc_blks
+ + EXT3_DATA_TRANS_BLOCKS + 4;
+ break;
+ case PRESTO_OP_RMDIR:
+ jblocks = one_path_blks + extra_name_blks + trunc_blks
+ + EXT3_DELETE_TRANS_BLOCKS;
+ break;
+ case PRESTO_OP_MKNOD:
+ jblocks = one_path_blks + trunc_blks +
+ EXT3_DATA_TRANS_BLOCKS + 3;
+ break;
+ case PRESTO_OP_RENAME:
+ jblocks = one_path_blks + extra_path_blks + trunc_blks +
+ 2 * EXT3_DATA_TRANS_BLOCKS + 2;
+ break;
+ case PRESTO_OP_WRITE:
+ jblocks = one_path_blks;
+ /* add this when we can wrap our transaction with
+ that of ext3_file_write (ordered writes)
+ + EXT3_DATA_TRANS_BLOCKS;
+ */
+ break;
+ default:
+ CDEBUG(D_JOURNAL, "invalid operation %d for journal\n", op);
+ return NULL;
+ }
+
+ CDEBUG(D_JOURNAL, "creating journal handle (%d blocks)\n", jblocks);
+ return journal_start(EXT3_JOURNAL(inode), jblocks);
+}
+#endif
+
+void presto_obdfs_trans_commit(struct presto_file_set *fset, void *handle)
+{
+#if 0
+ if ( presto_no_journal(fset) || !handle)
+ return;
+
+ journal_stop(handle);
+#endif
+}
+
+void presto_obdfs_journal_file_data(struct inode *inode)
+{
+#ifdef EXT3_JOURNAL_DATA_FL
+ inode->u.ext3_i.i_flags |= EXT3_JOURNAL_DATA_FL;
+#else
+#warning You must have a facility to enable journaled writes for recovery!
+#endif
+}
+
+struct journal_ops presto_obdfs_journal_ops = {
+ .tr_avail = presto_obdfs_freespace,
+ .tr_start = presto_obdfs_trans_start,
+ .tr_commit = presto_obdfs_trans_commit,
+ .tr_journal_data = presto_obdfs_journal_file_data
+};
+
+#endif
diff --git a/fs/intermezzo/journal_reiserfs.c b/fs/intermezzo/journal_reiserfs.c
new file mode 100644
index 00000000000000..8bbfc9d9f3504c
--- /dev/null
+++ b/fs/intermezzo/journal_reiserfs.c
@@ -0,0 +1,142 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Copyright (C) 1998 Peter J. Braam <braam@clusterfs.com>
+ * Copyright (C) 2000 Red Hat, Inc.
+ * Copyright (C) 2000 Los Alamos National Laboratory
+ * Copyright (C) 2000 TurboLinux, Inc.
+ * Copyright (C) 2001 Mountain View Data, Inc.
+ *
+ * This file is part of InterMezzo, http://www.inter-mezzo.org.
+ *
+ * InterMezzo is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * InterMezzo 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 InterMezzo; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/types.h>
+#include <linux/param.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/smp_lock.h>
+#include <linux/locks.h>
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+#include <linux/string.h>
+#if 0
+#if defined(CONFIG_REISERFS_FS) || defined(CONFIG_REISERFS_FS_MODULE)
+#include <linux/reiserfs_fs.h>
+#include <linux/reiserfs_fs_sb.h>
+#include <linux/reiserfs_fs_i.h>
+#endif
+
+#include <linux/intermezzo_fs.h>
+#include <linux/intermezzo_psdev.h>
+
+#if defined(CONFIG_REISERFS_FS) || defined(CONFIG_REISERFS_FS_MODULE)
+
+
+static loff_t presto_reiserfs_freespace(struct presto_cache *cache,
+ struct super_block *sb)
+{
+ struct reiserfs_super_block * rs = SB_DISK_SUPER_BLOCK (sb);
+ loff_t avail;
+
+ avail = le32_to_cpu(rs->s_free_blocks) *
+ le16_to_cpu(rs->s_blocksize);
+ return avail;
+}
+
+/* start the filesystem journal operations */
+static void *presto_reiserfs_trans_start(struct presto_file_set *fset,
+ struct inode *inode,
+ int op)
+{
+ int jblocks;
+ __u32 avail_kmlblocks;
+ struct reiserfs_transaction_handle *th ;
+
+ PRESTO_ALLOC(th, sizeof(*th));
+ if (!th) {
+ CERROR("presto: No memory for trans handle\n");
+ return NULL;
+ }
+
+ avail_kmlblocks = presto_reiserfs_freespace(fset->fset_cache,
+ inode->i_sb);
+ if ( presto_no_journal(fset) ||
+ strcmp(fset->fset_cache->cache_type, "reiserfs"))
+ {
+ CDEBUG(D_JOURNAL, "got cache_type \"%s\"\n",
+ fset->fset_cache->cache_type);
+ return NULL;
+ }
+
+ if ( avail_kmlblocks < 3 ) {
+ return ERR_PTR(-ENOSPC);
+ }
+
+ if ( (op != PRESTO_OP_UNLINK && op != PRESTO_OP_RMDIR)
+ && avail_kmlblocks < 6 ) {
+ return ERR_PTR(-ENOSPC);
+ }
+
+ jblocks = 3 + JOURNAL_PER_BALANCE_CNT * 4;
+ CDEBUG(D_JOURNAL, "creating journal handle (%d blocks)\n", jblocks);
+
+ lock_kernel();
+ journal_begin(th, inode->i_sb, jblocks);
+ unlock_kernel();
+ return th;
+}
+
+static void presto_reiserfs_trans_commit(struct presto_file_set *fset,
+ void *handle)
+{
+ int jblocks;
+ jblocks = 3 + JOURNAL_PER_BALANCE_CNT * 4;
+
+ lock_kernel();
+ journal_end(handle, fset->fset_cache->cache_sb, jblocks);
+ unlock_kernel();
+ PRESTO_FREE(handle, sizeof(struct reiserfs_transaction_handle));
+}
+
+static void presto_reiserfs_journal_file_data(struct inode *inode)
+{
+#ifdef EXT3_JOURNAL_DATA_FL
+ inode->u.ext3_i.i_flags |= EXT3_JOURNAL_DATA_FL;
+#else
+#warning You must have a facility to enable journaled writes for recovery!
+#endif
+}
+
+static int presto_reiserfs_has_all_data(struct inode *inode)
+{
+ BUG();
+ return 0;
+}
+
+struct journal_ops presto_reiserfs_journal_ops = {
+ .tr_all_data = presto_reiserfs_has_all_data,
+ .tr_avail = presto_reiserfs_freespace,
+ .tr_start = presto_reiserfs_trans_start,
+ .tr_commit = presto_reiserfs_trans_commit,
+ .tr_journal_data = presto_reiserfs_journal_file_data
+};
+
+#endif
+#endif
diff --git a/fs/intermezzo/journal_tmpfs.c b/fs/intermezzo/journal_tmpfs.c
new file mode 100644
index 00000000000000..d271ff12141f7f
--- /dev/null
+++ b/fs/intermezzo/journal_tmpfs.c
@@ -0,0 +1,109 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Copyright (C) 1998 Peter J. Braam <braam@clusterfs.com>
+ * Copyright (C) 2000 Red Hat, Inc.
+ * Copyright (C) 2000 Los Alamos National Laboratory
+ * Copyright (C) 2000 TurboLinux, Inc.
+ * Copyright (C) 2001 Mountain View Data, Inc.
+ * Copyright (C) 2001 Tacit Networks, Inc. <phil@off.net>
+ *
+ * This file is part of InterMezzo, http://www.inter-mezzo.org.
+ *
+ * InterMezzo is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * InterMezzo 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 InterMezzo; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/types.h>
+#include <linux/param.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+#include <linux/string.h>
+#include <linux/smp_lock.h>
+#if defined(CONFIG_TMPFS)
+#include <linux/jbd.h>
+#if defined(CONFIG_EXT3)
+#include <linux/ext3_fs.h>
+#include <linux/ext3_jbd.h>
+#endif
+#endif
+
+#include <linux/intermezzo_fs.h>
+#include <linux/intermezzo_psdev.h>
+
+#if defined(CONFIG_TMPFS)
+
+/* space requirements:
+ presto_do_truncate:
+ used to truncate the KML forward to next fset->chunksize boundary
+ - zero partial block
+ - update inode
+ presto_write_record:
+ write header (< one block)
+ write one path (< MAX_PATHLEN)
+ possibly write another path (< MAX_PATHLEN)
+ write suffix (< one block)
+ presto_update_last_rcvd
+ write one block
+*/
+
+static loff_t presto_tmpfs_freespace(struct presto_cache *cache,
+ struct super_block *sb)
+{
+ return (1<<30);
+}
+
+/* start the filesystem journal operations */
+static void *presto_tmpfs_trans_start(struct presto_file_set *fset,
+ struct inode *inode,
+ int op)
+{
+ return (void *)1;
+}
+
+static void presto_tmpfs_trans_commit(struct presto_file_set *fset, void *handle)
+{
+ return;
+}
+
+static void presto_tmpfs_journal_file_data(struct inode *inode)
+{
+ return;
+}
+
+/* The logic here is a slightly modified version of ext3/inode.c:block_to_path
+ */
+static int presto_tmpfs_has_all_data(struct inode *inode)
+{
+ return 0;
+}
+
+struct journal_ops presto_tmpfs_journal_ops = {
+ tr_all_data: presto_tmpfs_has_all_data,
+ tr_avail: presto_tmpfs_freespace,
+ tr_start: presto_tmpfs_trans_start,
+ tr_commit: presto_tmpfs_trans_commit,
+ tr_journal_data: presto_tmpfs_journal_file_data,
+ tr_ilookup: presto_tmpfs_ilookup,
+ tr_add_ilookup: presto_add_ilookup_dentry
+};
+
+#endif /* CONFIG_EXT3_FS */
diff --git a/fs/intermezzo/journal_xfs.c b/fs/intermezzo/journal_xfs.c
new file mode 100644
index 00000000000000..68d354a814a136
--- /dev/null
+++ b/fs/intermezzo/journal_xfs.c
@@ -0,0 +1,162 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Copyright (C) 1998 Peter J. Braam <braam@clusterfs.com>
+ *
+ * This file is part of InterMezzo, http://www.inter-mezzo.org.
+ *
+ * InterMezzo is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * InterMezzo 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 InterMezzo; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+#include <linux/string.h>
+#if 0
+/* XFS Support not there yet */
+#ifdef CONFIG_FS_XFS
+#include <linux/xfs_fs.h>
+#endif
+#include <linux/intermezzo_fs.h>
+#include <linux/intermezzo_psdev.h>
+#include <linux/intermezzo_journal.h>
+
+#if 0
+
+/* XFS has journalling, but these functions do nothing yet... */
+
+static unsigned long presto_xfs_freespace(struct presto_file_set *fset,
+ struct super_block *sb)
+{
+
+#if 0
+ vfs_t *vfsp = LINVFS_GET_VFS(sb);
+ struct statvfs_t stat;
+ bhv_desc_t *bdp;
+ unsigned long avail;
+ int rc;
+
+ VFS_STATVFS(vfsp, &stat, NULL, rc);
+ avail = statp.f_bfree;
+
+ return sbp->sb_fdblocks;
+#endif
+ return 0x0fffffff;
+}
+
+
+/* start the filesystem journal operations */
+static void *
+presto_xfs_trans_start(struct presto_file_set *fset,
+ struct inode *inode, int op)
+{
+ int xfs_op;
+ /* do a free blocks check as in journal_ext3? does anything protect
+ * the space in that case or can it disappear out from under us
+ * anyway? */
+
+/* copied from xfs_trans.h, skipping header maze for now */
+#define XFS_TRANS_SETATTR_NOT_SIZE 1
+#define XFS_TRANS_SETATTR_SIZE 2
+#define XFS_TRANS_INACTIVE 3
+#define XFS_TRANS_CREATE 4
+#define XFS_TRANS_CREATE_TRUNC 5
+#define XFS_TRANS_TRUNCATE_FILE 6
+#define XFS_TRANS_REMOVE 7
+#define XFS_TRANS_LINK 8
+#define XFS_TRANS_RENAME 9
+#define XFS_TRANS_MKDIR 10
+#define XFS_TRANS_RMDIR 11
+#define XFS_TRANS_SYMLINK 12
+
+ /* map the op onto the values for XFS so it can do reservation. if
+ * we don't have enough info to differentiate between e.g. setattr
+ * with or without size, what do we do? will it adjust? */
+ switch (op) {
+ case PRESTO_OP_SETATTR:
+ /* or XFS_TRANS_SETATTR_NOT_SIZE? */
+ xfs_op = XFS_TRANS_SETATTR_SIZE;
+ break;
+ case PRESTO_OP_CREATE:
+ /* or CREATE_TRUNC? */
+ xfs_op = XFS_TRANS_CREATE;
+ break;
+ case PRESTO_OP_LINK:
+ xfs_op = XFS_TRANS_LINK;
+ break;
+ case PRESTO_OP_UNLINK:
+ xfs_op = XFS_TRANS_REMOVE;
+ break;
+ case PRESTO_OP_SYMLINK:
+ xfs_op = XFS_TRANS_SYMLINK;
+ break;
+ case PRESTO_OP_MKDIR:
+ xfs_op = XFS_TRANS_MKDIR;
+ break;
+ case PRESTO_OP_RMDIR:
+ xfs_op = XFS_TRANS_RMDIR;
+ break;
+ case PRESTO_OP_MKNOD:
+ /* XXX can't find an analog for mknod? */
+ xfs_op = XFS_TRANS_CREATE;
+ break;
+ case PRESTO_OP_RENAME:
+ xfs_op = XFS_TRANS_RENAME;
+ break;
+ default:
+ CDEBUG(D_JOURNAL, "invalid operation %d for journal\n", op);
+ return NULL;
+ }
+
+ return xfs_trans_start(inode, xfs_op);
+}
+
+static void presto_xfs_trans_commit(struct presto_file_set *fset, void *handle)
+{
+ /* assert (handle == current->j_handle) */
+ xfs_trans_stop(handle);
+}
+
+static void presto_xfs_journal_file_data(struct inode *inode)
+{
+ return;
+}
+
+static int presto_xfs_has_all_data(struct inode *inode)
+{
+ BUG();
+ return 0;
+}
+
+struct journal_ops presto_xfs_journal_ops = {
+ .tr_all_data = presto_xfs_has_all_data,
+ .tr_avail = presto_xfs_freespace,
+ .tr_start = presto_xfs_trans_start,
+ .tr_commit = presto_xfs_trans_commit,
+ .tr_journal_data = presto_xfs_journal_file_data
+};
+
+#endif
+
+
+#endif /* CONFIG_XFS_FS */
+
diff --git a/fs/intermezzo/kml_reint.c b/fs/intermezzo/kml_reint.c
new file mode 100644
index 00000000000000..c7a36b32ad0cd7
--- /dev/null
+++ b/fs/intermezzo/kml_reint.c
@@ -0,0 +1,630 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Copyright (C) 2001 Cluster File Systems, Inc. <braam@clusterfs.com>
+ *
+ * This file is part of InterMezzo, http://www.inter-mezzo.org.
+ *
+ * InterMezzo is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * InterMezzo 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 InterMezzo; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Reintegration of KML records
+ *
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+#include <asm/mmu_context.h>
+#include <linux/intermezzo_fs.h>
+#include <linux/intermezzo_psdev.h>
+
+static void kmlreint_pre_secure(struct kml_rec *rec, struct file *dir,
+ struct run_ctxt *saved)
+{
+ struct run_ctxt ctxt;
+ struct presto_dentry_data *dd = presto_d2d(dir->f_dentry);
+ int i;
+
+ ctxt.fsuid = rec->prefix.hdr->fsuid;
+ ctxt.fsgid = rec->prefix.hdr->fsgid;
+ ctxt.fs = KERNEL_DS;
+ ctxt.pwd = dd->dd_fset->fset_dentry;
+ ctxt.pwdmnt = dd->dd_fset->fset_mnt;
+
+ ctxt.root = ctxt.pwd;
+ ctxt.rootmnt = ctxt.pwdmnt;
+ if (rec->prefix.hdr->ngroups > 0) {
+ ctxt.ngroups = rec->prefix.hdr->ngroups;
+ for (i = 0; i< ctxt.ngroups; i++)
+ ctxt.groups[i] = rec->prefix.groups[i];
+ } else
+ ctxt.ngroups = 0;
+
+ push_ctxt(saved, &ctxt);
+}
+
+
+/* Append two strings in a less-retarded fashion. */
+static char * path_join(char *p1, int p1len, char *p2, int p2len)
+{
+ int size = p1len + p2len + 2; /* possibly one extra /, one NULL */
+ char *path;
+
+ path = kmalloc(size, GFP_KERNEL);
+ if (path == NULL)
+ return NULL;
+
+ memcpy(path, p1, p1len);
+ if (path[p1len - 1] != '/') {
+ path[p1len] = '/';
+ p1len++;
+ }
+ memcpy(path + p1len, p2, p2len);
+ path[p1len + p2len] = '\0';
+
+ return path;
+}
+
+static inline int kml_recno_equal(struct kml_rec *rec,
+ struct presto_file_set *fset)
+{
+ return (rec->suffix->recno == fset->fset_lento_recno + 1);
+}
+
+static inline int version_equal(struct presto_version *a, struct inode *inode)
+{
+ if (a == NULL)
+ return 1;
+
+ if (inode == NULL) {
+ CERROR("InterMezzo: NULL inode in version_equal()\n");
+ return 0;
+ }
+
+ if (inode->i_mtime == a->pv_mtime &&
+ (S_ISDIR(inode->i_mode) || inode->i_size == a->pv_size))
+ return 1;
+
+ return 0;
+}
+
+static int reint_close(struct kml_rec *rec, struct file *file,
+ struct lento_vfs_context *given_info)
+{
+ struct run_ctxt saved_ctxt;
+ int error;
+ struct presto_file_set *fset;
+ struct lento_vfs_context info;
+ ENTRY;
+
+ memcpy(&info, given_info, sizeof(*given_info));
+
+
+ CDEBUG (D_KML, "=====REINT_CLOSE::%s\n", rec->path);
+
+ fset = presto_fset(file->f_dentry);
+ if (fset->fset_flags & FSET_DATA_ON_DEMAND) {
+ struct iattr iattr;
+
+ iattr.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_SIZE;
+ iattr.ia_mtime = (time_t)rec->new_objectv->pv_mtime;
+ iattr.ia_ctime = (time_t)rec->new_objectv->pv_ctime;
+ iattr.ia_size = (time_t)rec->new_objectv->pv_size;
+
+ /* no kml record, but update last rcvd */
+ /* save fileid in dentry for later backfetch */
+ info.flags |= LENTO_FL_EXPECT | LENTO_FL_SET_DDFILEID;
+ info.remote_ino = rec->ino;
+ info.remote_generation = rec->generation;
+ info.flags &= ~LENTO_FL_KML;
+ kmlreint_pre_secure(rec, file, &saved_ctxt);
+ error = lento_setattr(rec->path, &iattr, &info);
+ pop_ctxt(&saved_ctxt);
+
+ presto_d2d(file->f_dentry)->dd_flags &= ~PRESTO_DATA;
+ } else {
+ int minor = presto_f2m(fset);
+
+ info.updated_time = rec->new_objectv->pv_mtime;
+ memcpy(&info.remote_version, rec->old_objectv,
+ sizeof(*rec->old_objectv));
+ info.remote_ino = rec->ino;
+ info.remote_generation = rec->generation;
+ error = izo_upc_backfetch(minor, rec->path, fset->fset_name,
+ &info);
+ if (error) {
+ CERROR("backfetch error %d\n", error);
+ /* if file doesn't exist anymore, then ignore the CLOSE
+ * and just update the last_rcvd.
+ */
+ if (error == ENOENT) {
+ CDEBUG(D_KML, "manually updating remote offset uuid %s"
+ "recno %d offset %Lu\n", info.uuid, info.recno, info.kml_offset);
+ error = izo_rcvd_upd_remote(fset, info.uuid, info.recno, info.kml_offset);
+ if(error)
+ CERROR("izo_rcvd_upd_remote error %d\n", error);
+
+ }
+ }
+
+ /* propagate error to avoid further reint */
+ }
+
+ EXIT;
+ return error;
+}
+
+static int reint_create(struct kml_rec *rec, struct file *dir,
+ struct lento_vfs_context *info)
+{
+ struct run_ctxt saved_ctxt;
+ int error; ENTRY;
+
+ CDEBUG (D_KML, "=====REINT_CREATE::%s\n", rec->path);
+ info->updated_time = rec->new_objectv->pv_ctime;
+ kmlreint_pre_secure(rec, dir, &saved_ctxt);
+ error = lento_create(rec->path, rec->mode, info);
+ pop_ctxt(&saved_ctxt);
+
+ EXIT;
+ return error;
+}
+
+static int reint_link(struct kml_rec *rec, struct file *dir,
+ struct lento_vfs_context *info)
+{
+ struct run_ctxt saved_ctxt;
+ int error;
+
+ ENTRY;
+
+ CDEBUG (D_KML, "=====REINT_LINK::%s -> %s\n", rec->path, rec->target);
+ info->updated_time = rec->new_objectv->pv_mtime;
+ kmlreint_pre_secure(rec, dir, &saved_ctxt);
+ error = lento_link(rec->path, rec->target, info);
+ pop_ctxt(&saved_ctxt);
+
+ EXIT;
+ return error;
+}
+
+static int reint_mkdir(struct kml_rec *rec, struct file *dir,
+ struct lento_vfs_context *info)
+{
+ struct run_ctxt saved_ctxt;
+ int error;
+
+ ENTRY;
+
+ CDEBUG (D_KML, "=====REINT_MKDIR::%s\n", rec->path);
+ info->updated_time = rec->new_objectv->pv_ctime;
+ kmlreint_pre_secure(rec, dir, &saved_ctxt);
+ error = lento_mkdir(rec->path, rec->mode, info);
+ pop_ctxt(&saved_ctxt);
+
+ EXIT;
+ return error;
+}
+
+static int reint_mknod(struct kml_rec *rec, struct file *dir,
+ struct lento_vfs_context *info)
+{
+ struct run_ctxt saved_ctxt;
+ int error, dev;
+
+ ENTRY;
+
+ CDEBUG (D_KML, "=====REINT_MKNOD::%s\n", rec->path);
+ info->updated_time = rec->new_objectv->pv_ctime;
+ kmlreint_pre_secure(rec, dir, &saved_ctxt);
+
+ dev = rec->rdev ?: MKDEV(rec->major, rec->minor);
+
+ error = lento_mknod(rec->path, rec->mode, dev, info);
+ pop_ctxt(&saved_ctxt);
+
+ EXIT;
+ return error;
+}
+
+
+static int reint_noop(struct kml_rec *rec, struct file *dir,
+ struct lento_vfs_context *info)
+{
+ return 0;
+}
+
+static int reint_rename(struct kml_rec *rec, struct file *dir,
+ struct lento_vfs_context *info)
+{
+ struct run_ctxt saved_ctxt;
+ int error;
+
+ ENTRY;
+
+ CDEBUG (D_KML, "=====REINT_RENAME::%s -> %s\n", rec->path, rec->target);
+ info->updated_time = rec->new_objectv->pv_mtime;
+ kmlreint_pre_secure(rec, dir, &saved_ctxt);
+ error = lento_rename(rec->path, rec->target, info);
+ pop_ctxt(&saved_ctxt);
+
+ EXIT;
+ return error;
+}
+
+static int reint_rmdir(struct kml_rec *rec, struct file *dir,
+ struct lento_vfs_context *info)
+{
+ struct run_ctxt saved_ctxt;
+ int error;
+ char *path;
+
+ ENTRY;
+
+ path = path_join(rec->path, rec->pathlen - 1, rec->target, rec->targetlen);
+ if (path == NULL) {
+ EXIT;
+ return -ENOMEM;
+ }
+
+ CDEBUG (D_KML, "=====REINT_RMDIR::%s\n", path);
+ info->updated_time = rec->new_parentv->pv_mtime;
+ kmlreint_pre_secure(rec, dir, &saved_ctxt);
+ error = lento_rmdir(path, info);
+ pop_ctxt(&saved_ctxt);
+
+ kfree(path);
+ EXIT;
+ return error;
+}
+
+static int reint_setattr(struct kml_rec *rec, struct file *dir,
+ struct lento_vfs_context *info)
+{
+ struct run_ctxt saved_ctxt;
+ struct iattr iattr;
+ int error;
+
+ ENTRY;
+
+ iattr.ia_valid = rec->valid;
+ iattr.ia_mode = (umode_t)rec->mode;
+ iattr.ia_uid = (uid_t)rec->uid;
+ iattr.ia_gid = (gid_t)rec->gid;
+ iattr.ia_size = (off_t)rec->size;
+ iattr.ia_ctime = (time_t)rec->ctime;
+ iattr.ia_mtime = (time_t)rec->mtime;
+ iattr.ia_atime = iattr.ia_mtime; /* We don't track atimes. */
+ iattr.ia_attr_flags = rec->flags;
+
+ CDEBUG (D_KML, "=====REINT_SETATTR::%s (%d)\n", rec->path, rec->valid);
+ kmlreint_pre_secure(rec, dir, &saved_ctxt);
+ error = lento_setattr(rec->path, &iattr, info);
+ pop_ctxt(&saved_ctxt);
+
+ EXIT;
+ return error;
+}
+
+static int reint_symlink(struct kml_rec *rec, struct file *dir,
+ struct lento_vfs_context *info)
+{
+ struct run_ctxt saved_ctxt;
+ int error;
+
+ ENTRY;
+
+ CDEBUG (D_KML, "=====REINT_SYMLINK::%s -> %s\n", rec->path, rec->target);
+ info->updated_time = rec->new_objectv->pv_ctime;
+ kmlreint_pre_secure(rec, dir, &saved_ctxt);
+ error = lento_symlink(rec->target, rec->path, info);
+ pop_ctxt(&saved_ctxt);
+
+ EXIT;
+ return error;
+}
+
+static int reint_unlink(struct kml_rec *rec, struct file *dir,
+ struct lento_vfs_context *info)
+{
+ struct run_ctxt saved_ctxt;
+ int error;
+ char *path;
+
+ ENTRY;
+
+ path = path_join(rec->path, rec->pathlen - 1, rec->target, rec->targetlen);
+ if (path == NULL) {
+ EXIT;
+ return -ENOMEM;
+ }
+
+ CDEBUG (D_KML, "=====REINT_UNLINK::%s\n", path);
+ info->updated_time = rec->new_parentv->pv_mtime;
+ kmlreint_pre_secure(rec, dir, &saved_ctxt);
+ error = lento_unlink(path, info);
+ pop_ctxt(&saved_ctxt);
+
+ kfree(path);
+ EXIT;
+ return error;
+}
+
+static int branch_reint_rename(struct presto_file_set *fset, struct kml_rec *rec,
+ struct file *dir, struct lento_vfs_context *info,
+ char * kml_data, __u64 kml_size)
+{
+ int error;
+
+ ENTRY;
+
+ error = reint_rename(rec, dir, info);
+ if (error == -ENOENT) {
+ /* normal reint failed because path was not found */
+ struct rec_info rec;
+
+ CDEBUG(D_KML, "saving branch rename kml\n");
+ rec.is_kml = 1;
+ rec.size = kml_size;
+ error = presto_log(fset, &rec, kml_data, kml_size,
+ NULL, 0, NULL, 0, NULL, 0);
+ if (error == 0)
+ error = presto_write_last_rcvd(&rec, fset, info);
+ }
+
+ EXIT;
+ return error;
+}
+
+int branch_reinter(struct presto_file_set *fset, struct kml_rec *rec,
+ struct file *dir, struct lento_vfs_context *info,
+ char * kml_data, __u64 kml_size)
+{
+ int error = 0;
+ int op = rec->prefix.hdr->opcode;
+
+ if (op == KML_OPCODE_CLOSE) {
+ /* regular close and backfetch */
+ error = reint_close(rec, dir, info);
+ } else if (op == KML_OPCODE_RENAME) {
+ /* rename only if name already exists */
+ error = branch_reint_rename(fset, rec, dir, info,
+ kml_data, kml_size);
+ } else {
+ /* just rewrite kml into branch/kml and update last_rcvd */
+ struct rec_info rec;
+
+ CDEBUG(D_KML, "Saving branch kml\n");
+ rec.is_kml = 1;
+ rec.size = kml_size;
+ error = presto_log(fset, &rec, kml_data, kml_size,
+ NULL, 0, NULL, 0, NULL, 0);
+ if (error == 0)
+ error = presto_write_last_rcvd(&rec, fset, info);
+ }
+
+ return error;
+}
+
+typedef int (*reinter_t)(struct kml_rec *rec, struct file *basedir,
+ struct lento_vfs_context *info);
+
+static reinter_t presto_reinters[KML_OPCODE_NUM] =
+{
+ [KML_OPCODE_CLOSE] = reint_close,
+ [KML_OPCODE_CREATE] = reint_create,
+ [KML_OPCODE_LINK] = reint_link,
+ [KML_OPCODE_MKDIR] = reint_mkdir,
+ [KML_OPCODE_MKNOD] = reint_mknod,
+ [KML_OPCODE_NOOP] = reint_noop,
+ [KML_OPCODE_RENAME] = reint_rename,
+ [KML_OPCODE_RMDIR] = reint_rmdir,
+ [KML_OPCODE_SETATTR] = reint_setattr,
+ [KML_OPCODE_SYMLINK] = reint_symlink,
+ [KML_OPCODE_UNLINK] = reint_unlink,
+};
+
+static inline reinter_t get_reinter(int op)
+{
+ if (op < 0 || op >= sizeof(presto_reinters) / sizeof(reinter_t))
+ return NULL;
+ else
+ return presto_reinters[op];
+}
+
+int kml_reint_rec(struct file *dir, struct izo_ioctl_data *data)
+{
+ char *ptr;
+ char *end;
+ struct kml_rec rec;
+ int error = 0;
+ struct lento_vfs_context info;
+ struct presto_cache *cache;
+ struct presto_file_set *fset;
+ struct presto_dentry_data *dd = presto_d2d(dir->f_dentry);
+ int op;
+ reinter_t reinter;
+
+ struct izo_rcvd_rec lr_rec;
+ int off;
+
+ ENTRY;
+
+ error = presto_prep(dir->f_dentry, &cache, &fset);
+ if ( error ) {
+ CERROR("intermezzo: Reintegration on invalid file\n");
+ return error;
+ }
+
+ if (!dd || !dd->dd_fset || dd->dd_fset->fset_dentry != dir->f_dentry) {
+ CERROR("intermezzo: reintegration on non-fset root (ino %ld)\n",
+ dir->f_dentry->d_inode->i_ino);
+
+ return -EINVAL;
+ }
+
+ if (data->ioc_plen1 > 64 * 1024) {
+ EXIT;
+ return -ENOSPC;
+ }
+
+ ptr = fset->fset_reint_buf;
+ end = ptr + data->ioc_plen1;
+
+ if (copy_from_user(ptr, data->ioc_pbuf1, data->ioc_plen1)) {
+ EXIT;
+ error = -EFAULT;
+ goto out;
+ }
+
+ error = kml_unpack(&rec, &ptr, end);
+ if (error) {
+ EXIT;
+ error = -EFAULT;
+ goto out;
+ }
+
+ off = izo_rcvd_get(&lr_rec, fset, data->ioc_uuid);
+ if (off < 0) {
+ CERROR("No last_rcvd record, setting to 0\n");
+ memset(&lr_rec, 0, sizeof(lr_rec));
+ }
+
+ data->ioc_kmlsize = ptr - fset->fset_reint_buf;
+
+ if (rec.suffix->recno != lr_rec.lr_remote_recno + 1) {
+ CERROR("KML record number %Lu expected, not %d\n",
+ lr_rec.lr_remote_recno + 1,
+ rec.suffix->recno);
+
+#if 0
+ if (!version_check(&rec, dd->dd_fset, &info)) {
+ /* FIXME: do an upcall to resolve conflicts */
+ CERROR("intermezzo: would be a conflict!\n");
+ error = -EINVAL;
+ EXIT;
+ goto out;
+ }
+#endif
+ }
+
+ op = rec.prefix.hdr->opcode;
+
+ reinter = get_reinter(op);
+ if (!reinter) {
+ CERROR("%s: Unrecognized KML opcode %d\n", __FUNCTION__, op);
+ error = -EINVAL;
+ EXIT;
+ goto out;
+ }
+
+ info.kml_offset = data->ioc_offset + data->ioc_kmlsize;
+ info.recno = rec.suffix->recno;
+ info.flags = LENTO_FL_EXPECT;
+ if (data->ioc_flags)
+ info.flags |= LENTO_FL_KML;
+
+ memcpy(info.uuid, data->ioc_uuid, sizeof(info.uuid));
+
+ if (fset->fset_flags & FSET_IS_BRANCH && data->ioc_flags)
+ error = branch_reinter(fset, &rec, dir, &info, fset->fset_reint_buf,
+ data->ioc_kmlsize);
+ else
+ error = reinter(&rec, dir, &info);
+ out:
+ EXIT;
+ return error;
+}
+
+int izo_get_fileid(struct file *dir, struct izo_ioctl_data *data)
+{
+ char *buf = NULL;
+ char *ptr;
+ char *end;
+ struct kml_rec rec;
+ struct file *file;
+ struct presto_cache *cache;
+ struct presto_file_set *fset;
+ struct presto_dentry_data *dd = presto_d2d(dir->f_dentry);
+ struct run_ctxt saved_ctxt;
+ int error;
+
+ ENTRY;
+
+ error = presto_prep(dir->f_dentry, &cache, &fset);
+ if ( error ) {
+ CERROR("intermezzo: Reintegration on invalid file\n");
+ return error;
+ }
+
+ if (!dd || !dd->dd_fset || dd->dd_fset->fset_dentry != dir->f_dentry) {
+ CERROR("intermezzo: reintegration on non-fset root (ino %ld)\n",
+ dir->f_dentry->d_inode->i_ino);
+
+ return -EINVAL;
+ }
+
+
+ PRESTO_ALLOC(buf, data->ioc_plen1);
+ if (!buf) {
+ EXIT;
+ return -ENOMEM;
+ }
+ ptr = buf;
+ end = buf + data->ioc_plen1;
+
+ if (copy_from_user(buf, data->ioc_pbuf1, data->ioc_plen1)) {
+ EXIT;
+ PRESTO_FREE(buf, data->ioc_plen1);
+ return -EFAULT;
+ }
+
+ error = kml_unpack(&rec, &ptr, end);
+ if (error) {
+ EXIT;
+ PRESTO_FREE(buf, data->ioc_plen1);
+ return -EFAULT;
+ }
+
+ kmlreint_pre_secure(&rec, dir, &saved_ctxt);
+
+ file = filp_open(rec.path, O_RDONLY, 0);
+ if (!file || IS_ERR(file)) {
+ error = PTR_ERR(file);
+ goto out;
+ }
+ data->ioc_ino = file->f_dentry->d_inode->i_ino;
+ data->ioc_generation = file->f_dentry->d_inode->i_generation;
+ filp_close(file, 0);
+
+ CDEBUG(D_FILE, "%s ino %Lx, gen %Lx\n", rec.path,
+ data->ioc_ino, data->ioc_generation);
+
+ out:
+ if (buf)
+ PRESTO_FREE(buf, data->ioc_plen1);
+ pop_ctxt(&saved_ctxt);
+ EXIT;
+ return error;
+}
+
+
diff --git a/fs/intermezzo/kml_unpack.c b/fs/intermezzo/kml_unpack.c
new file mode 100644
index 00000000000000..febd9b10936298
--- /dev/null
+++ b/fs/intermezzo/kml_unpack.c
@@ -0,0 +1,708 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Copyright (C) 2001 Cluster File Systems, Inc. <braam@clusterfs.com>
+ *
+ * This file is part of InterMezzo, http://www.inter-mezzo.org.
+ *
+ * InterMezzo is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * InterMezzo 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 InterMezzo; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Unpacking of KML records
+ *
+ */
+
+#ifdef __KERNEL__
+# include <linux/module.h>
+# include <linux/errno.h>
+# include <linux/kernel.h>
+# include <linux/major.h>
+# include <linux/sched.h>
+# include <linux/lp.h>
+# include <linux/slab.h>
+# include <linux/ioport.h>
+# include <linux/fcntl.h>
+# include <linux/delay.h>
+# include <linux/skbuff.h>
+# include <linux/proc_fs.h>
+# include <linux/vmalloc.h>
+# include <linux/fs.h>
+# include <linux/poll.h>
+# include <linux/init.h>
+# include <linux/list.h>
+# include <linux/stat.h>
+# include <asm/io.h>
+# include <asm/segment.h>
+# include <asm/system.h>
+# include <asm/poll.h>
+# include <asm/uaccess.h>
+#else
+# include <time.h>
+# include <stdio.h>
+# include <string.h>
+# include <stdlib.h>
+# include <errno.h>
+# include <sys/stat.h>
+# include <glib.h>
+#endif
+
+#include <linux/intermezzo_lib.h>
+#include <linux/intermezzo_idl.h>
+#include <linux/intermezzo_fs.h>
+
+int kml_unpack_version(struct presto_version **ver, char **buf, char *end)
+{
+ char *ptr = *buf;
+ struct presto_version *pv;
+
+ UNLOGP(*ver, struct presto_version, ptr, end);
+ pv = *ver;
+ pv->pv_mtime = NTOH__u64(pv->pv_mtime);
+ pv->pv_ctime = NTOH__u64(pv->pv_ctime);
+ pv->pv_size = NTOH__u64(pv->pv_size);
+
+ *buf = ptr;
+
+ return 0;
+}
+
+
+static int kml_unpack_noop(struct kml_rec *rec, char **buf, char *end)
+{
+ return 0;
+}
+
+
+static int kml_unpack_get_fileid(struct kml_rec *rec, char **buf, char *end)
+{
+ char *ptr = *buf;
+
+ LUNLOGV(rec->pathlen, __u32, ptr, end);
+ UNLOGL(rec->path, char, rec->pathlen, ptr, end);
+
+ *buf = ptr;
+ return 0;
+}
+
+static int kml_unpack_create(struct kml_rec *rec, char **buf, char *end)
+{
+ char *ptr = *buf;
+
+ kml_unpack_version(&rec->old_parentv, &ptr, end);
+ kml_unpack_version(&rec->new_parentv, &ptr, end);
+ kml_unpack_version(&rec->new_objectv, &ptr, end);
+ LUNLOGV(rec->mode, __u32, ptr, end);
+ LUNLOGV(rec->uid, __u32, ptr, end);
+ LUNLOGV(rec->gid, __u32, ptr, end);
+ LUNLOGV(rec->pathlen, __u32, ptr, end);
+ UNLOGL(rec->path, char, rec->pathlen, ptr, end);
+
+ *buf = ptr;
+
+ return 0;
+}
+
+
+static int kml_unpack_mkdir(struct kml_rec *rec, char **buf, char *end)
+{
+ char *ptr = *buf;
+
+ kml_unpack_version(&rec->old_parentv, &ptr, end);
+ kml_unpack_version(&rec->new_parentv, &ptr, end);
+ kml_unpack_version(&rec->new_objectv, &ptr, end);
+ LUNLOGV(rec->mode, __u32, ptr, end);
+ LUNLOGV(rec->uid, __u32, ptr, end);
+ LUNLOGV(rec->gid, __u32, ptr, end);
+ LUNLOGV(rec->pathlen, __u32, ptr, end);
+ UNLOGL(rec->path, char, rec->pathlen, ptr, end);
+
+ *buf = ptr;
+
+ return 0;
+}
+
+
+static int kml_unpack_unlink(struct kml_rec *rec, char **buf, char *end)
+{
+ char *ptr = *buf;
+
+ kml_unpack_version(&rec->old_parentv, &ptr, end);
+ kml_unpack_version(&rec->new_parentv, &ptr, end);
+ kml_unpack_version(&rec->old_objectv, &ptr, end);
+ LUNLOGV(rec->old_mode, __u32, ptr, end);
+ LUNLOGV(rec->old_rdev, __u32, ptr, end);
+ LUNLOGV(rec->old_uid, __u64, ptr, end);
+ LUNLOGV(rec->old_gid, __u64, ptr, end);
+ LUNLOGV(rec->pathlen, __u32, ptr, end);
+ LUNLOGV(rec->targetlen, __u32, ptr, end);
+ LUNLOGV(rec->old_targetlen, __u32, ptr, end);
+ UNLOGL(rec->path, char, rec->pathlen, ptr, end);
+ UNLOGL(rec->target, char, rec->targetlen, ptr, end);
+ UNLOGL(rec->old_target, char, rec->old_targetlen, ptr, end);
+
+ *buf = ptr;
+
+ return 0;
+}
+
+
+static int kml_unpack_rmdir(struct kml_rec *rec, char **buf, char *end)
+{
+ char *ptr = *buf;
+
+ kml_unpack_version(&rec->old_parentv, &ptr, end);
+ kml_unpack_version(&rec->new_parentv, &ptr, end);
+ kml_unpack_version(&rec->old_objectv, &ptr, end);
+ LUNLOGV(rec->old_mode, __u32, ptr, end);
+ LUNLOGV(rec->old_rdev, __u32, ptr, end);
+ LUNLOGV(rec->old_uid, __u64, ptr, end);
+ LUNLOGV(rec->old_gid, __u64, ptr, end);
+ LUNLOGV(rec->pathlen, __u32, ptr, end);
+ LUNLOGV(rec->targetlen, __u32, ptr, end);
+ UNLOGL(rec->path, char, rec->pathlen, ptr, end);
+ UNLOGL(rec->target, char, rec->targetlen, ptr, end);
+
+ *buf = ptr;
+
+ return 0;
+}
+
+
+static int kml_unpack_close(struct kml_rec *rec, char **buf, char *end)
+{
+ char *ptr = *buf;
+
+ LUNLOGV(rec->mode, __u32, ptr, end); // used for open_mode
+ LUNLOGV(rec->uid, __u32, ptr, end); // used for open_uid
+ LUNLOGV(rec->gid, __u32, ptr, end); // used for open_gid
+ kml_unpack_version(&rec->old_objectv, &ptr, end);
+ kml_unpack_version(&rec->new_objectv, &ptr, end);
+ LUNLOGV(rec->ino, __u64, ptr, end);
+ LUNLOGV(rec->generation, __u32, ptr, end);
+ LUNLOGV(rec->pathlen, __u32, ptr, end);
+ UNLOGL(rec->path, char, rec->pathlen, ptr, end);
+
+ *buf = ptr;
+
+ return 0;
+}
+
+
+static int kml_unpack_symlink(struct kml_rec *rec, char **buf, char *end)
+{
+ char *ptr = *buf;
+
+ kml_unpack_version(&rec->old_parentv, &ptr, end);
+ kml_unpack_version(&rec->new_parentv, &ptr, end);
+ kml_unpack_version(&rec->new_objectv, &ptr, end);
+ LUNLOGV(rec->uid, __u32, ptr, end);
+ LUNLOGV(rec->gid, __u32, ptr, end);
+ LUNLOGV(rec->pathlen, __u32, ptr, end);
+ LUNLOGV(rec->targetlen, __u32, ptr, end);
+ UNLOGL(rec->path, char, rec->pathlen, ptr, end);
+ UNLOGL(rec->target, char, rec->targetlen, ptr, end);
+
+ *buf = ptr;
+
+ return 0;
+}
+
+
+static int kml_unpack_rename(struct kml_rec *rec, char **buf, char *end)
+{
+ char *ptr = *buf;
+
+ kml_unpack_version(&rec->old_objectv, &ptr, end);
+ kml_unpack_version(&rec->new_objectv, &ptr, end);
+ kml_unpack_version(&rec->old_parentv, &ptr, end);
+ kml_unpack_version(&rec->new_parentv, &ptr, end);
+ LUNLOGV(rec->pathlen, __u32, ptr, end);
+ LUNLOGV(rec->targetlen, __u32, ptr, end);
+ UNLOGL(rec->path, char, rec->pathlen, ptr, end);
+ UNLOGL(rec->target, char, rec->targetlen, ptr, end);
+
+ *buf = ptr;
+
+ return 0;
+}
+
+
+static int kml_unpack_setattr(struct kml_rec *rec, char **buf, char *end)
+{
+ char *ptr = *buf;
+
+ kml_unpack_version(&rec->old_objectv, &ptr, end);
+ LUNLOGV(rec->valid, __u32, ptr, end);
+ LUNLOGV(rec->mode, __u32, ptr, end);
+ LUNLOGV(rec->uid, __u32, ptr, end);
+ LUNLOGV(rec->gid, __u32, ptr, end);
+ LUNLOGV(rec->size, __u64, ptr, end);
+ LUNLOGV(rec->mtime, __u64, ptr, end);
+ LUNLOGV(rec->ctime, __u64, ptr, end);
+ LUNLOGV(rec->flags, __u32, ptr, end);
+ LUNLOGV(rec->old_mode, __u32, ptr, end);
+ LUNLOGV(rec->old_rdev, __u32, ptr, end);
+ LUNLOGV(rec->old_uid, __u64, ptr, end);
+ LUNLOGV(rec->old_gid, __u64, ptr, end);
+ LUNLOGV(rec->pathlen, __u32, ptr, end);
+ UNLOGL(rec->path, char, rec->pathlen, ptr, end);
+
+ *buf = ptr;
+
+ return 0;
+}
+
+
+static int kml_unpack_link(struct kml_rec *rec, char **buf, char *end)
+{
+ char *ptr = *buf;
+
+ kml_unpack_version(&rec->old_parentv, &ptr, end);
+ kml_unpack_version(&rec->new_parentv, &ptr, end);
+ kml_unpack_version(&rec->new_objectv, &ptr, end);
+ LUNLOGV(rec->pathlen, __u32, ptr, end);
+ LUNLOGV(rec->targetlen, __u32, ptr, end);
+ UNLOGL(rec->path, char, rec->pathlen, ptr, end);
+ UNLOGL(rec->target, char, rec->targetlen, ptr, end);
+
+ *buf = ptr;
+
+ return 0;
+}
+
+static int kml_unpack_mknod(struct kml_rec *rec, char **buf, char *end)
+{
+ char *ptr = *buf;
+
+ kml_unpack_version(&rec->old_parentv, &ptr, end);
+ kml_unpack_version(&rec->new_parentv, &ptr, end);
+ kml_unpack_version(&rec->new_objectv, &ptr, end);
+ LUNLOGV(rec->mode, __u32, ptr, end);
+ LUNLOGV(rec->uid, __u32, ptr, end);
+ LUNLOGV(rec->gid, __u32, ptr, end);
+ LUNLOGV(rec->major, __u32, ptr, end);
+ LUNLOGV(rec->minor, __u32, ptr, end);
+ LUNLOGV(rec->pathlen, __u32, ptr, end);
+ UNLOGL(rec->path, char, rec->pathlen, ptr, end);
+
+ *buf = ptr;
+
+ return 0;
+}
+
+
+static int kml_unpack_write(struct kml_rec *rec, char **buf, char *end)
+{
+ printf("NOT IMPLEMENTED");
+ return 0;
+}
+
+
+static int kml_unpack_release(struct kml_rec *rec, char **buf, char *end)
+{
+ printf("NOT IMPLEMENTED");
+ return 0;
+}
+
+
+static int kml_unpack_trunc(struct kml_rec *rec, char **buf, char *end)
+{
+ printf("NOT IMPLEMENTED");
+ return 0;
+}
+
+
+static int kml_unpack_setextattr(struct kml_rec *rec, char **buf, char *end)
+{
+ char *ptr = *buf;
+
+ kml_unpack_version(&rec->old_objectv, &ptr, end);
+ kml_unpack_version(&rec->new_objectv, &ptr, end);
+ LUNLOGV(rec->flags, __u32, ptr, end);
+ LUNLOGV(rec->mode, __u32, ptr, end);
+ LUNLOGV(rec->pathlen, __u32, ptr, end);
+ LUNLOGV(rec->namelen, __u32, ptr, end);
+ LUNLOGV(rec->targetlen, __u32, ptr, end);
+ UNLOGL(rec->path, char, rec->pathlen, ptr, end);
+ UNLOGL(rec->name, char, rec->namelen, ptr, end);
+ UNLOGL(rec->target, char, rec->targetlen, ptr, end);
+
+ *buf = ptr;
+
+ return 0;
+}
+
+
+static int kml_unpack_delextattr(struct kml_rec *rec, char **buf, char *end)
+{
+ char *ptr = *buf;
+
+ kml_unpack_version(&rec->old_objectv, &ptr, end);
+ kml_unpack_version(&rec->new_objectv, &ptr, end);
+ LUNLOGV(rec->flags, __u32, ptr, end);
+ LUNLOGV(rec->mode, __u32, ptr, end);
+ LUNLOGV(rec->pathlen, __u32, ptr, end);
+ LUNLOGV(rec->namelen, __u32, ptr, end);
+ LUNLOGV(rec->targetlen, __u32, ptr, end);
+ UNLOGL(rec->path, char, rec->pathlen, ptr, end);
+ UNLOGL(rec->name, char, rec->namelen, ptr, end);
+
+ *buf = ptr;
+
+ return 0;
+}
+
+static int kml_unpack_open(struct kml_rec *rec, char **buf, char *end)
+{
+ printf("NOT IMPLEMENTED");
+ return 0;
+}
+
+static int kml_unpack_kml_trunc(struct kml_rec *rec, char **buf, char *end)
+{
+
+ printf("NOT IMPLEMENTED");
+ return 0;
+}
+
+
+typedef int (*unpacker)(struct kml_rec *rec, char **buf, char *end);
+
+static unpacker unpackers[KML_OPCODE_NUM] =
+{
+ [KML_OPCODE_NOOP] = kml_unpack_noop,
+ [KML_OPCODE_CREATE] = kml_unpack_create,
+ [KML_OPCODE_MKDIR] = kml_unpack_mkdir,
+ [KML_OPCODE_UNLINK] = kml_unpack_unlink,
+ [KML_OPCODE_RMDIR] = kml_unpack_rmdir,
+ [KML_OPCODE_CLOSE] = kml_unpack_close,
+ [KML_OPCODE_SYMLINK] = kml_unpack_symlink,
+ [KML_OPCODE_RENAME] = kml_unpack_rename,
+ [KML_OPCODE_SETATTR] = kml_unpack_setattr,
+ [KML_OPCODE_LINK] = kml_unpack_link,
+ [KML_OPCODE_OPEN] = kml_unpack_open,
+ [KML_OPCODE_MKNOD] = kml_unpack_mknod,
+ [KML_OPCODE_WRITE] = kml_unpack_write,
+ [KML_OPCODE_RELEASE] = kml_unpack_release,
+ [KML_OPCODE_TRUNC] = kml_unpack_trunc,
+ [KML_OPCODE_SETEXTATTR] = kml_unpack_setextattr,
+ [KML_OPCODE_DELEXTATTR] = kml_unpack_delextattr,
+ [KML_OPCODE_KML_TRUNC] = kml_unpack_kml_trunc,
+ [KML_OPCODE_GET_FILEID] = kml_unpack_get_fileid
+};
+
+int kml_unpack_prefix(struct kml_rec *rec, char **buf, char *end)
+{
+ char *ptr = *buf;
+ int n;
+
+ UNLOGP(rec->prefix.hdr, struct kml_prefix_hdr, ptr, end);
+ rec->prefix.hdr->len = NTOH__u32(rec->prefix.hdr->len);
+ rec->prefix.hdr->version = NTOH__u32(rec->prefix.hdr->version);
+ rec->prefix.hdr->pid = NTOH__u32(rec->prefix.hdr->pid);
+ rec->prefix.hdr->auid = NTOH__u32(rec->prefix.hdr->auid);
+ rec->prefix.hdr->fsuid = NTOH__u32(rec->prefix.hdr->fsuid);
+ rec->prefix.hdr->fsgid = NTOH__u32(rec->prefix.hdr->fsgid);
+ rec->prefix.hdr->opcode = NTOH__u32(rec->prefix.hdr->opcode);
+ rec->prefix.hdr->ngroups = NTOH__u32(rec->prefix.hdr->ngroups);
+
+ UNLOGL(rec->prefix.groups, __u32, rec->prefix.hdr->ngroups, ptr, end);
+ for (n = 0; n < rec->prefix.hdr->ngroups; n++) {
+ rec->prefix.groups[n] = NTOH__u32(rec->prefix.groups[n]);
+ }
+
+ *buf = ptr;
+
+ return 0;
+}
+
+int kml_unpack_suffix(struct kml_rec *rec, char **buf, char *end)
+{
+ char *ptr = *buf;
+
+ UNLOGP(rec->suffix, struct kml_suffix, ptr, end);
+ rec->suffix->prevrec = NTOH__u32(rec->suffix->prevrec);
+ rec->suffix->recno = NTOH__u32(rec->suffix->recno);
+ rec->suffix->time = NTOH__u32(rec->suffix->time);
+ rec->suffix->len = NTOH__u32(rec->suffix->len);
+
+ *buf = ptr;
+
+ return 0;
+}
+
+int kml_unpack(struct kml_rec *rec, char **buf, char *end)
+{
+ char *ptr = *buf;
+ int err;
+
+ if (((unsigned long)ptr % 4) != 0) {
+ printf("InterMezzo: %s: record misaligned.\n", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ while (ptr < end) {
+ __u32 *i = (__u32 *)ptr;
+ if (*i)
+ break;
+ ptr += sizeof(*i);
+ }
+ *buf = ptr;
+
+ memset(rec, 0, sizeof(*rec));
+
+ err = kml_unpack_prefix(rec, &ptr, end);
+ if (err) {
+ printf("InterMezzo: %s: unpack_prefix failed: %d\n",
+ __FUNCTION__, err);
+ return err;
+ }
+
+ if (rec->prefix.hdr->opcode < 0 ||
+ rec->prefix.hdr->opcode >= KML_OPCODE_NUM) {
+ printf("InterMezzo: %s: invalid opcode (%d)\n",
+ __FUNCTION__, rec->prefix.hdr->opcode);
+ return -EINVAL;
+ }
+ err = unpackers[rec->prefix.hdr->opcode](rec, &ptr, end);
+ if (err) {
+ printf("InterMezzo: %s: unpacker failed: %d\n",
+ __FUNCTION__, err);
+ return err;
+ }
+
+ err = kml_unpack_suffix(rec, &ptr, end);
+ if (err) {
+ printf("InterMezzo: %s: unpack_suffix failed: %d\n",
+ __FUNCTION__, err);
+ return err;
+ }
+
+
+ if (rec->prefix.hdr->len != rec->suffix->len) {
+ printf("InterMezzo: %s: lengths don't match\n",
+ __FUNCTION__);
+ return -EINVAL;
+ }
+ if ((rec->prefix.hdr->len % 4) != 0) {
+ printf("InterMezzo: %s: record length not a "
+ "multiple of 4.\n", __FUNCTION__);
+ return -EINVAL;
+ }
+ if (ptr - *buf != rec->prefix.hdr->len) {
+ printf("InterMezzo: %s: unpacking error\n",
+ __FUNCTION__);
+ return -EINVAL;
+ }
+ while (ptr < end) {
+ __u32 *i = (__u32 *)ptr;
+ if (*i)
+ break;
+ ptr += sizeof(*i);
+ }
+ *buf = ptr;
+ return 0;
+}
+
+
+#ifndef __KERNEL__
+#define STR(ptr) ((ptr))? (ptr) : ""
+
+#define OPNAME(n) [KML_OPCODE_##n] = #n
+static char *opnames[KML_OPCODE_NUM] = {
+ OPNAME(NOOP),
+ OPNAME(CREATE),
+ OPNAME(MKDIR),
+ OPNAME(UNLINK),
+ OPNAME(RMDIR),
+ OPNAME(CLOSE),
+ OPNAME(SYMLINK),
+ OPNAME(RENAME),
+ OPNAME(SETATTR),
+ OPNAME(LINK),
+ OPNAME(OPEN),
+ OPNAME(MKNOD),
+ OPNAME(WRITE),
+ OPNAME(RELEASE),
+ OPNAME(TRUNC),
+ OPNAME(SETEXTATTR),
+ OPNAME(DELEXTATTR),
+ OPNAME(KML_TRUNC),
+ OPNAME(GET_FILEID)
+};
+#undef OPNAME
+
+static char *print_opname(int op)
+{
+ if (op < 0 || op >= sizeof (opnames) / sizeof (*opnames))
+ return NULL;
+ return opnames[op];
+}
+
+
+static char *print_time(__u64 i)
+{
+ char buf[128];
+
+ memset(buf, 0, 128);
+
+#ifndef __KERNEL__
+ strftime(buf, 128, "%Y/%m/%d %H:%M:%S", gmtime((time_t *)&i));
+#else
+ sprintf(buf, "%Ld\n", i);
+#endif
+
+ return strdup(buf);
+}
+
+static char *print_version(struct presto_version *ver)
+{
+ char ver_buf[128];
+ char *mtime;
+ char *ctime;
+
+ if (!ver || ver->pv_ctime == 0) {
+ return strdup("");
+ }
+ mtime = print_time(ver->pv_mtime);
+ ctime = print_time(ver->pv_ctime);
+ sprintf(ver_buf, "mtime %s, ctime %s, len %lld",
+ mtime, ctime, ver->pv_size);
+ free(mtime);
+ free(ctime);
+ return strdup(ver_buf);
+}
+
+
+char *kml_print_rec(struct kml_rec *rec, int brief)
+{
+ char *str;
+ char *nov, *oov, *ntv, *otv, *npv, *opv;
+ char *rectime, *mtime, *ctime;
+
+ if (brief) {
+ str = g_strdup_printf(" %08d %7s %*s %*s",
+ rec->suffix->recno,
+ print_opname (rec->prefix.hdr->opcode),
+ rec->pathlen, STR(rec->path),
+ rec->targetlen, STR(rec->target));
+
+ return str;
+ }
+
+ rectime = print_time(rec->suffix->time);
+ mtime = print_time(rec->mtime);
+ ctime = print_time(rec->ctime);
+
+ nov = print_version(rec->new_objectv);
+ oov = print_version(rec->old_objectv);
+ ntv = print_version(rec->new_targetv);
+ otv = print_version(rec->old_targetv);
+ npv = print_version(rec->new_parentv);
+ opv = print_version(rec->old_parentv);
+
+ str = g_strdup_printf("\n -- Record:\n"
+ " Recno %d\n"
+ " KML off %lld\n"
+ " Version %d\n"
+ " Len %d\n"
+ " Suf len %d\n"
+ " Time %s\n"
+ " Opcode %d\n"
+ " Op %s\n"
+ " Pid %d\n"
+ " AUid %d\n"
+ " Fsuid %d\n"
+ " Fsgid %d\n"
+ " Prevrec %d\n"
+ " Ngroups %d\n"
+ //" Groups @{$self->{groups}}\n"
+ " -- Path:\n"
+ " Inode %d\n"
+ " Gen num %u\n"
+ " Old mode %o\n"
+ " Old rdev %x\n"
+ " Old uid %llu\n"
+ " Old gid %llu\n"
+ " Path %*s\n"
+ //" Open_mode %o\n",
+ " Pathlen %d\n"
+ " Tgt %*s\n"
+ " Tgtlen %d\n"
+ " Old Tgt %*s\n"
+ " Old Tgtln %d\n"
+ " -- Attr:\n"
+ " Valid %x\n"
+ " mode %o, uid %d, gid %d, size %lld, mtime %s, ctime %s rdev %x (%d:%d)\n"
+ " -- Versions:\n"
+ " New object %s\n"
+ " Old object %s\n"
+ " New target %s\n"
+ " Old target %s\n"
+ " New parent %s\n"
+ " Old parent %s\n",
+
+ rec->suffix->recno,
+ rec->offset,
+ rec->prefix.hdr->version,
+ rec->prefix.hdr->len,
+ rec->suffix->len,
+ rectime,
+ rec->prefix.hdr->opcode,
+ print_opname (rec->prefix.hdr->opcode),
+ rec->prefix.hdr->pid,
+ rec->prefix.hdr->auid,
+ rec->prefix.hdr->fsuid,
+ rec->prefix.hdr->fsgid,
+ rec->suffix->prevrec,
+ rec->prefix.hdr->ngroups,
+ rec->ino,
+ rec->generation,
+ rec->old_mode,
+ rec->old_rdev,
+ rec->old_uid,
+ rec->old_gid,
+ rec->pathlen,
+ STR(rec->path),
+ rec->pathlen,
+ rec->targetlen,
+ STR(rec->target),
+ rec->targetlen,
+ rec->old_targetlen,
+ STR(rec->old_target),
+ rec->old_targetlen,
+
+ rec->valid,
+ rec->mode,
+ rec->uid,
+ rec->gid,
+ rec->size,
+ mtime,
+ ctime,
+ rec->rdev, rec->major, rec->minor,
+ nov, oov, ntv, otv, npv, opv);
+
+ free(nov);
+ free(oov);
+ free(ntv);
+ free(otv);
+ free(npv);
+ free(opv);
+
+ free(rectime);
+ free(ctime);
+ free(mtime);
+
+ return str;
+}
+#endif
diff --git a/fs/intermezzo/methods.c b/fs/intermezzo/methods.c
new file mode 100644
index 00000000000000..e50110877bd835
--- /dev/null
+++ b/fs/intermezzo/methods.c
@@ -0,0 +1,497 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Copyright (C) 2000 Stelias Computing, Inc.
+ * Copyright (C) 2000 Red Hat, Inc.
+ * Copyright (C) 2000 Mountain View Data, Inc.
+ *
+ * Extended Attribute Support
+ * Copyright (C) 2001 Shirish H. Phatak, Tacit Networks, Inc.
+ *
+ * This file is part of InterMezzo, http://www.inter-mezzo.org.
+ *
+ * InterMezzo is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * InterMezzo 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 InterMezzo; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <stdarg.h>
+
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/ext2_fs.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/blkdev.h>
+#include <linux/init.h>
+#define __NO_VERSION__
+#include <linux/module.h>
+
+#include <linux/fsfilter.h>
+#include <linux/intermezzo_fs.h>
+
+
+int filter_print_entry = 0;
+int filter_debug = 0xfffffff;
+/*
+ * The function in this file are responsible for setting up the
+ * correct methods layered file systems like InterMezzo and snapfs
+ */
+
+
+static struct filter_fs filter_oppar[FILTER_FS_TYPES];
+
+/* get to the upper methods (intermezzo, snapfs) */
+inline struct super_operations *filter_c2usops(struct filter_fs *cache)
+{
+ return &cache->o_fops.filter_sops;
+}
+
+inline struct inode_operations *filter_c2udiops(struct filter_fs *cache)
+{
+ return &cache->o_fops.filter_dir_iops;
+}
+
+
+inline struct inode_operations *filter_c2ufiops(struct filter_fs *cache)
+{
+ return &cache->o_fops.filter_file_iops;
+}
+
+inline struct inode_operations *filter_c2usiops(struct filter_fs *cache)
+{
+ return &cache->o_fops.filter_sym_iops;
+}
+
+
+inline struct file_operations *filter_c2udfops(struct filter_fs *cache)
+{
+ return &cache->o_fops.filter_dir_fops;
+}
+
+inline struct file_operations *filter_c2uffops(struct filter_fs *cache)
+{
+ return &cache->o_fops.filter_file_fops;
+}
+
+inline struct file_operations *filter_c2usfops(struct filter_fs *cache)
+{
+ return &cache->o_fops.filter_sym_fops;
+}
+
+inline struct dentry_operations *filter_c2udops(struct filter_fs *cache)
+{
+ return &cache->o_fops.filter_dentry_ops;
+}
+
+/* get to the cache (lower) methods */
+inline struct super_operations *filter_c2csops(struct filter_fs *cache)
+{
+ return cache->o_caops.cache_sops;
+}
+
+inline struct inode_operations *filter_c2cdiops(struct filter_fs *cache)
+{
+ return cache->o_caops.cache_dir_iops;
+}
+
+inline struct inode_operations *filter_c2cfiops(struct filter_fs *cache)
+{
+ return cache->o_caops.cache_file_iops;
+}
+
+inline struct inode_operations *filter_c2csiops(struct filter_fs *cache)
+{
+ return cache->o_caops.cache_sym_iops;
+}
+
+inline struct file_operations *filter_c2cdfops(struct filter_fs *cache)
+{
+ return cache->o_caops.cache_dir_fops;
+}
+
+inline struct file_operations *filter_c2cffops(struct filter_fs *cache)
+{
+ return cache->o_caops.cache_file_fops;
+}
+
+inline struct file_operations *filter_c2csfops(struct filter_fs *cache)
+{
+ return cache->o_caops.cache_sym_fops;
+}
+
+inline struct dentry_operations *filter_c2cdops(struct filter_fs *cache)
+{
+ return cache->o_caops.cache_dentry_ops;
+}
+
+
+void filter_setup_journal_ops(struct filter_fs *ops, char *cache_type)
+{
+ if ( strlen(cache_type) == strlen("ext2") &&
+ memcmp(cache_type, "ext2", strlen("ext2")) == 0 ) {
+#if CONFIG_EXT2_FS
+ ops->o_trops = &presto_ext2_journal_ops;
+#else
+ ops->o_trops = NULL;
+#endif
+ FDEBUG(D_SUPER, "ops at %p\n", ops);
+ }
+
+ if ( strlen(cache_type) == strlen("ext3") &&
+ memcmp(cache_type, "ext3", strlen("ext3")) == 0 ) {
+#if defined(CONFIG_EXT3_FS) || defined (CONFIG_EXT3_FS_MODULE)
+ ops->o_trops = &presto_ext3_journal_ops;
+#else
+ ops->o_trops = NULL;
+#endif
+ FDEBUG(D_SUPER, "ops at %p\n", ops);
+ }
+
+ if ( strlen(cache_type) == strlen("tmpfs") &&
+ memcmp(cache_type, "tmpfs", strlen("tmpfs")) == 0 ) {
+#if defined(CONFIG_TMPFS)
+ ops->o_trops = &presto_tmpfs_journal_ops;
+#else
+ ops->o_trops = NULL;
+#endif
+ FDEBUG(D_SUPER, "ops at %p\n", ops);
+ }
+
+ if ( strlen(cache_type) == strlen("reiserfs") &&
+ memcmp(cache_type, "reiserfs", strlen("reiserfs")) == 0 ) {
+#if 0
+ /* #if defined(CONFIG_REISERFS_FS) || defined(CONFIG_REISERFS_FS_MODULE) */
+ ops->o_trops = &presto_reiserfs_journal_ops;
+#else
+ ops->o_trops = NULL;
+#endif
+ FDEBUG(D_SUPER, "ops at %p\n", ops);
+ }
+
+ if ( strlen(cache_type) == strlen("xfs") &&
+ memcmp(cache_type, "xfs", strlen("xfs")) == 0 ) {
+#if 0
+/*#if defined(CONFIG_XFS_FS) || defined (CONFIG_XFS_FS_MODULE) */
+ ops->o_trops = &presto_xfs_journal_ops;
+#else
+ ops->o_trops = NULL;
+#endif
+ FDEBUG(D_SUPER, "ops at %p\n", ops);
+ }
+
+ if ( strlen(cache_type) == strlen("obdfs") &&
+ memcmp(cache_type, "obdfs", strlen("obdfs")) == 0 ) {
+#if defined(CONFIG_OBDFS_FS) || defined (CONFIG_OBDFS_FS_MODULE)
+ ops->o_trops = presto_obdfs_journal_ops;
+#else
+ ops->o_trops = NULL;
+#endif
+ FDEBUG(D_SUPER, "ops at %p\n", ops);
+ }
+}
+
+
+/* find the cache for this FS */
+struct filter_fs *filter_get_filter_fs(const char *cache_type)
+{
+ struct filter_fs *ops = NULL;
+ FENTRY;
+
+ if ( strlen(cache_type) == strlen("ext2") &&
+ memcmp(cache_type, "ext2", strlen("ext2")) == 0 ) {
+ ops = &filter_oppar[FILTER_FS_EXT2];
+ FDEBUG(D_SUPER, "ops at %p\n", ops);
+ }
+
+ if ( strlen(cache_type) == strlen("xfs") &&
+ memcmp(cache_type, "xfs", strlen("xfs")) == 0 ) {
+ ops = &filter_oppar[FILTER_FS_XFS];
+ FDEBUG(D_SUPER, "ops at %p\n", ops);
+ }
+
+ if ( strlen(cache_type) == strlen("ext3") &&
+ memcmp(cache_type, "ext3", strlen("ext3")) == 0 ) {
+ ops = &filter_oppar[FILTER_FS_EXT3];
+ FDEBUG(D_SUPER, "ops at %p\n", ops);
+ }
+
+ if ( strlen(cache_type) == strlen("tmpfs") &&
+ memcmp(cache_type, "tmpfs", strlen("tmpfs")) == 0 ) {
+ ops = &filter_oppar[FILTER_FS_TMPFS];
+ FDEBUG(D_SUPER, "ops at %p\n", ops);
+ }
+
+ if ( strlen(cache_type) == strlen("reiserfs") &&
+ memcmp(cache_type, "reiserfs", strlen("reiserfs")) == 0 ) {
+ ops = &filter_oppar[FILTER_FS_REISERFS];
+ FDEBUG(D_SUPER, "ops at %p\n", ops);
+ }
+ if ( strlen(cache_type) == strlen("obdfs") &&
+ memcmp(cache_type, "obdfs", strlen("obdfs")) == 0 ) {
+ ops = &filter_oppar[FILTER_FS_OBDFS];
+ FDEBUG(D_SUPER, "ops at %p\n", ops);
+ }
+
+ if (ops == NULL) {
+ CERROR("prepare to die: unrecognized cache type for Filter\n");
+ }
+ FEXIT;
+ return ops;
+}
+
+
+/*
+ * Frobnicate the InterMezzo operations
+ * this establishes the link between the InterMezzo file system
+ * and the underlying file system used for the cache.
+ */
+
+void filter_setup_super_ops(struct filter_fs *cache, struct super_operations *cache_sops, struct super_operations *filter_sops)
+{
+ /* Get ptr to the shared struct snapfs_ops structure. */
+ struct filter_ops *props = &cache->o_fops;
+ /* Get ptr to the shared struct cache_ops structure. */
+ struct cache_ops *caops = &cache->o_caops;
+
+ FENTRY;
+
+ if ( cache->o_flags & FILTER_DID_SUPER_OPS ) {
+ FEXIT;
+ return;
+ }
+ cache->o_flags |= FILTER_DID_SUPER_OPS;
+
+ /* Set the cache superblock operations to point to the
+ superblock operations of the underlying file system. */
+ caops->cache_sops = cache_sops;
+
+ /*
+ * Copy the cache (real fs) superblock ops to the "filter"
+ * superblock ops as defaults. Some will be changed below
+ */
+ memcpy(&props->filter_sops, cache_sops, sizeof(*cache_sops));
+
+ /* 'put_super' unconditionally is that of filter */
+ if (filter_sops->put_super) {
+ props->filter_sops.put_super = filter_sops->put_super;
+ }
+
+ if (cache_sops->read_inode) {
+ props->filter_sops.read_inode = filter_sops->read_inode;
+ FDEBUG(D_INODE, "setting filter_read_inode, cache_ops %p, cache %p, ri at %p\n",
+ cache, cache, props->filter_sops.read_inode);
+ }
+
+ if (cache_sops->remount_fs)
+ props->filter_sops.remount_fs = filter_sops->remount_fs;
+ FEXIT;
+}
+
+
+void filter_setup_dir_ops(struct filter_fs *cache, struct inode *inode, struct inode_operations *filter_iops, struct file_operations *filter_fops)
+{
+ struct inode_operations *cache_filter_iops;
+ struct inode_operations *cache_iops = inode->i_op;
+ struct file_operations *cache_fops = inode->i_fop;
+ FENTRY;
+
+ if ( cache->o_flags & FILTER_DID_DIR_OPS ) {
+ FEXIT;
+ return;
+ }
+ cache->o_flags |= FILTER_DID_DIR_OPS;
+
+ /* former ops become cache_ops */
+ cache->o_caops.cache_dir_iops = cache_iops;
+ cache->o_caops.cache_dir_fops = cache_fops;
+ FDEBUG(D_SUPER, "filter at %p, cache iops %p, iops %p\n",
+ cache, cache_iops, filter_c2udiops(cache));
+
+ /* setup our dir iops: copy and modify */
+ memcpy(filter_c2udiops(cache), cache_iops, sizeof(*cache_iops));
+
+ /* abbreviate */
+ cache_filter_iops = filter_c2udiops(cache);
+
+ /* methods that filter if cache filesystem has these ops */
+ if (cache_iops->lookup && filter_iops->lookup)
+ cache_filter_iops->lookup = filter_iops->lookup;
+ if (cache_iops->create && filter_iops->create)
+ cache_filter_iops->create = filter_iops->create;
+ if (cache_iops->link && filter_iops->link)
+ cache_filter_iops->link = filter_iops->link;
+ if (cache_iops->unlink && filter_iops->unlink)
+ cache_filter_iops->unlink = filter_iops->unlink;
+ if (cache_iops->mkdir && filter_iops->mkdir)
+ cache_filter_iops->mkdir = filter_iops->mkdir;
+ if (cache_iops->rmdir && filter_iops->rmdir)
+ cache_filter_iops->rmdir = filter_iops->rmdir;
+ if (cache_iops->symlink && filter_iops->symlink)
+ cache_filter_iops->symlink = filter_iops->symlink;
+ if (cache_iops->rename && filter_iops->rename)
+ cache_filter_iops->rename = filter_iops->rename;
+ if (cache_iops->mknod && filter_iops->mknod)
+ cache_filter_iops->mknod = filter_iops->mknod;
+ if (cache_iops->permission && filter_iops->permission)
+ cache_filter_iops->permission = filter_iops->permission;
+ if (cache_iops->getattr)
+ cache_filter_iops->getattr = filter_iops->getattr;
+ /* Some filesystems do not use a setattr method of their own
+ instead relying on inode_setattr/write_inode. We still need to
+ journal these so we make setattr an unconditional operation.
+ XXX: we should probably check for write_inode. SHP
+ */
+ /*if (cache_iops->setattr)*/
+ cache_filter_iops->setattr = filter_iops->setattr;
+#ifdef CONFIG_FS_EXT_ATTR
+ /* For now we assume that posix acls are handled through extended
+ * attributes. If this is not the case, we must explicitly trap
+ * posix_set_acl. SHP
+ */
+ if (cache_iops->set_ext_attr && filter_iops->set_ext_attr)
+ cache_filter_iops->set_ext_attr = filter_iops->set_ext_attr;
+#endif
+
+
+ /* copy dir fops */
+ memcpy(filter_c2udfops(cache), cache_fops, sizeof(*cache_fops));
+
+ /* unconditional filtering operations */
+ filter_c2udfops(cache)->ioctl = filter_fops->ioctl;
+
+ FEXIT;
+}
+
+
+void filter_setup_file_ops(struct filter_fs *cache, struct inode *inode, struct inode_operations *filter_iops, struct file_operations *filter_fops)
+{
+ struct inode_operations *pr_iops;
+ struct inode_operations *cache_iops = inode->i_op;
+ struct file_operations *cache_fops = inode->i_fop;
+ FENTRY;
+
+ if ( cache->o_flags & FILTER_DID_FILE_OPS ) {
+ FEXIT;
+ return;
+ }
+ cache->o_flags |= FILTER_DID_FILE_OPS;
+
+ /* steal the old ops */
+ /* former ops become cache_ops */
+ cache->o_caops.cache_file_iops = cache_iops;
+ cache->o_caops.cache_file_fops = cache_fops;
+
+ /* abbreviate */
+ pr_iops = filter_c2ufiops(cache);
+
+ /* setup our dir iops: copy and modify */
+ memcpy(pr_iops, cache_iops, sizeof(*cache_iops));
+
+ /* copy dir fops */
+ CERROR("*** cache file ops at %p\n", cache_fops);
+ memcpy(filter_c2uffops(cache), cache_fops, sizeof(*cache_fops));
+
+ /* assign */
+ /* See comments above in filter_setup_dir_ops. SHP */
+ /*if (cache_iops->setattr)*/
+ pr_iops->setattr = filter_iops->setattr;
+ if (cache_iops->getattr)
+ pr_iops->getattr = filter_iops->getattr;
+ /* XXX Should this be conditional rmr ? */
+ pr_iops->permission = filter_iops->permission;
+#ifdef CONFIG_FS_EXT_ATTR
+ /* For now we assume that posix acls are handled through extended
+ * attributes. If this is not the case, we must explicitly trap and
+ * posix_set_acl
+ */
+ if (cache_iops->set_ext_attr && filter_iops->set_ext_attr)
+ pr_iops->set_ext_attr = filter_iops->set_ext_attr;
+#endif
+
+
+ /* unconditional filtering operations */
+ filter_c2uffops(cache)->open = filter_fops->open;
+ filter_c2uffops(cache)->release = filter_fops->release;
+ filter_c2uffops(cache)->write = filter_fops->write;
+ filter_c2uffops(cache)->ioctl = filter_fops->ioctl;
+
+ FEXIT;
+}
+
+/* XXX in 2.3 there are "fast" and "slow" symlink ops for ext2 XXX */
+void filter_setup_symlink_ops(struct filter_fs *cache, struct inode *inode, struct inode_operations *filter_iops, struct file_operations *filter_fops)
+{
+ struct inode_operations *pr_iops;
+ struct inode_operations *cache_iops = inode->i_op;
+ struct file_operations *cache_fops = inode->i_fop;
+ FENTRY;
+
+ if ( cache->o_flags & FILTER_DID_SYMLINK_OPS ) {
+ FEXIT;
+ return;
+ }
+ cache->o_flags |= FILTER_DID_SYMLINK_OPS;
+
+ /* steal the old ops */
+ cache->o_caops.cache_sym_iops = cache_iops;
+ cache->o_caops.cache_sym_fops = cache_fops;
+
+ /* abbreviate */
+ pr_iops = filter_c2usiops(cache);
+
+ /* setup our dir iops: copy and modify */
+ memcpy(pr_iops, cache_iops, sizeof(*cache_iops));
+
+ /* See comments above in filter_setup_dir_ops. SHP */
+ /* if (cache_iops->setattr) */
+ pr_iops->setattr = filter_iops->setattr;
+ if (cache_iops->getattr)
+ pr_iops->getattr = filter_iops->getattr;
+
+ /* assign */
+ /* copy fops - careful for symlinks they might be NULL */
+ if ( cache_fops ) {
+ memcpy(filter_c2usfops(cache), cache_fops, sizeof(*cache_fops));
+ }
+
+ FEXIT;
+}
+
+void filter_setup_dentry_ops(struct filter_fs *cache,
+ struct dentry_operations *cache_dop,
+ struct dentry_operations *filter_dop)
+{
+ if ( cache->o_flags & FILTER_DID_DENTRY_OPS ) {
+ FEXIT;
+ return;
+ }
+ cache->o_flags |= FILTER_DID_DENTRY_OPS;
+
+ cache->o_caops.cache_dentry_ops = cache_dop;
+ memcpy(&cache->o_fops.filter_dentry_ops,
+ filter_dop, sizeof(*filter_dop));
+
+ if (cache_dop && cache_dop != filter_dop && cache_dop->d_revalidate){
+ CERROR("WARNING: filter overriding revalidation!\n");
+ }
+ return;
+}
diff --git a/fs/intermezzo/presto.c b/fs/intermezzo/presto.c
new file mode 100644
index 00000000000000..c665ea1096a07e
--- /dev/null
+++ b/fs/intermezzo/presto.c
@@ -0,0 +1,740 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Author: Peter J. Braam <braam@clusterfs.com>
+ * Copyright (C) 1998 Stelias Computing Inc
+ * Copyright (C) 1999 Red Hat Inc.
+ *
+ * This file is part of InterMezzo, http://www.inter-mezzo.org.
+ *
+ * InterMezzo is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * InterMezzo 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 InterMezzo; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * This file implements basic routines supporting the semantics
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/locks.h>
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+#include <linux/string.h>
+#include <linux/smp_lock.h>
+
+#include <linux/intermezzo_fs.h>
+#include <linux/intermezzo_psdev.h>
+
+int presto_walk(const char *name, struct nameidata *nd)
+{
+ int err;
+ /* we do not follow symlinks to support symlink operations
+ correctly. The vfs should always hand us resolved dentries
+ so we should not be required to use LOOKUP_FOLLOW. At the
+ reintegrating end, lento again should be working with the
+ resolved pathname and not the symlink. SHP
+ XXX: This code implies that direct symlinks do not work. SHP
+ */
+ unsigned int flags = LOOKUP_POSITIVE;
+
+ ENTRY;
+ err = 0;
+ if (path_init(name, flags, nd))
+ err = path_walk(name, nd);
+ return err;
+}
+
+
+/* find the presto minor device for this inode */
+int presto_i2m(struct inode *inode)
+{
+ struct presto_cache *cache;
+ ENTRY;
+ cache = presto_get_cache(inode);
+ CDEBUG(D_PSDEV, "\n");
+ if ( !cache ) {
+ CERROR("PRESTO: BAD: cannot find cache for dev %d, ino %ld\n",
+ inode->i_dev, inode->i_ino);
+ EXIT;
+ return -1;
+ }
+ EXIT;
+ return cache->cache_psdev->uc_minor;
+}
+
+inline int presto_f2m(struct presto_file_set *fset)
+{
+ return fset->fset_cache->cache_psdev->uc_minor;
+
+}
+
+inline int presto_c2m(struct presto_cache *cache)
+{
+ return cache->cache_psdev->uc_minor;
+
+}
+
+/* XXX check this out */
+struct presto_file_set *presto_path2fileset(const char *name)
+{
+ struct nameidata nd;
+ struct presto_file_set *fileset;
+ int error;
+ ENTRY;
+
+ error = presto_walk(name, &nd);
+ if (!error) {
+#if 0
+ error = do_revalidate(nd.dentry);
+#endif
+ if (!error)
+ fileset = presto_fset(nd.dentry);
+ path_release(&nd);
+ EXIT;
+ } else
+ fileset = ERR_PTR(error);
+
+ EXIT;
+ return fileset;
+}
+
+/* check a flag on this dentry or fset root. Semantics:
+ - most flags: test if it is set
+ - PRESTO_ATTR, PRESTO_DATA return 1 if PRESTO_FSETINSYNC is set
+*/
+int presto_chk(struct dentry *dentry, int flag)
+{
+ int minor;
+ struct presto_file_set *fset = presto_fset(dentry);
+
+ ENTRY;
+ minor = presto_i2m(dentry->d_inode);
+ if ( izo_channels[minor].uc_no_filter ) {
+ EXIT;
+ return ~0;
+ }
+
+ /* if the fileset is in sync DATA and ATTR are OK */
+ if ( fset &&
+ (flag == PRESTO_ATTR || flag == PRESTO_DATA) &&
+ (fset->fset_flags & FSET_INSYNC) ) {
+ CDEBUG(D_INODE, "fset in sync (ino %ld)!\n",
+ fset->fset_dentry->d_inode->i_ino);
+ EXIT;
+ return 1;
+ }
+
+ EXIT;
+ return (presto_d2d(dentry)->dd_flags & flag);
+}
+
+/* set a bit in the dentry flags */
+void presto_set(struct dentry *dentry, int flag)
+{
+ ENTRY;
+ if ( dentry->d_inode ) {
+ CDEBUG(D_INODE, "SET ino %ld, flag %x\n",
+ dentry->d_inode->i_ino, flag);
+ }
+ if ( presto_d2d(dentry) == NULL) {
+ CERROR("dentry without d_fsdata in presto_set: %p: %*s", dentry,
+ dentry->d_name.len, dentry->d_name.name);
+ BUG();
+ }
+ presto_d2d(dentry)->dd_flags |= flag;
+ EXIT;
+}
+
+/* given a path: complete the closes on the fset */
+int lento_complete_closes(char *path)
+{
+ struct nameidata nd;
+ struct dentry *dentry;
+ int error;
+ struct presto_file_set *fset;
+ ENTRY;
+
+ error = presto_walk(path, &nd);
+ if (error) {
+ EXIT;
+ return error;
+ }
+
+ dentry = nd.dentry;
+
+ error = -ENXIO;
+ if ( !presto_ispresto(dentry->d_inode) ) {
+ EXIT;
+ goto out_complete;
+ }
+
+ fset = presto_fset(dentry);
+ error = -EINVAL;
+ if ( !fset ) {
+ CERROR("No fileset!\n");
+ EXIT;
+ goto out_complete;
+ }
+
+ /* transactions and locking are internal to this function */
+ error = presto_complete_lml(fset);
+
+ EXIT;
+ out_complete:
+ path_release(&nd);
+ return error;
+}
+
+#if 0
+/* given a path: write a close record and cancel an LML record, finally
+ call truncate LML. Lento is doing this so it goes in with uid/gid's
+ root.
+*/
+int lento_cancel_lml(char *path,
+ __u64 lml_offset,
+ __u64 remote_ino,
+ __u32 remote_generation,
+ __u32 remote_version,
+ struct lento_vfs_context *info)
+{
+ struct nameidata nd;
+ struct rec_info rec;
+ struct dentry *dentry;
+ int error;
+ struct presto_file_set *fset;
+ void *handle;
+ struct presto_version new_ver;
+ ENTRY;
+
+
+ error = presto_walk(path, &nd);
+ if (error) {
+ EXIT;
+ return error;
+ }
+ dentry = nd.dentry;
+
+ error = -ENXIO;
+ if ( !presto_ispresto(dentry->d_inode) ) {
+ EXIT;
+ goto out_cancel_lml;
+ }
+
+ fset = presto_fset(dentry);
+
+ error=-EINVAL;
+ if (fset==NULL) {
+ CERROR("No fileset!\n");
+ EXIT;
+ goto out_cancel_lml;
+ }
+
+ /* this only requires a transaction below which is automatic */
+ handle = presto_trans_start(fset, dentry->d_inode, PRESTO_OP_RELEASE);
+ if ( IS_ERR(handle) ) {
+ error = -ENOMEM;
+ EXIT;
+ goto out_cancel_lml;
+ }
+
+ if (info->flags & LENTO_FL_CANCEL_LML) {
+ error = presto_clear_lml_close(fset, lml_offset);
+ if ( error ) {
+ presto_trans_commit(fset, handle);
+ EXIT;
+ goto out_cancel_lml;
+ }
+ }
+
+
+ if (info->flags & LENTO_FL_WRITE_KML) {
+ struct file file;
+ file.private_data = NULL;
+ file.f_dentry = dentry;
+ presto_getversion(&new_ver, dentry->d_inode);
+ error = presto_journal_close(&rec, fset, &file, dentry,
+ &new_ver);
+ if ( error ) {
+ EXIT;
+ presto_trans_commit(fset, handle);
+ goto out_cancel_lml;
+ }
+ }
+
+ if (info->flags & LENTO_FL_WRITE_EXPECT) {
+ error = presto_write_last_rcvd(&rec, fset, info);
+ if ( error < 0 ) {
+ EXIT;
+ presto_trans_commit(fset, handle);
+ goto out_cancel_lml;
+ }
+ }
+
+ presto_trans_commit(fset, handle);
+
+ if (info->flags & LENTO_FL_CANCEL_LML) {
+ presto_truncate_lml(fset);
+ }
+
+
+ out_cancel_lml:
+ EXIT;
+ path_release(&nd);
+ return error;
+}
+#endif
+
+/* given a dentry, operate on the flags in its dentry. Used by downcalls */
+int izo_mark_dentry(struct dentry *dentry, int and_flag, int or_flag,
+ int *res)
+{
+ int error = 0;
+
+ if (presto_d2d(dentry) == NULL) {
+ CERROR("InterMezzo: no ddata for inode %ld in %s\n",
+ dentry->d_inode->i_ino, __FUNCTION__);
+ return -EINVAL;
+ }
+
+ CDEBUG(D_INODE, "inode: %ld, and flag %x, or flag %x, dd_flags %x\n",
+ dentry->d_inode->i_ino, and_flag, or_flag,
+ presto_d2d(dentry)->dd_flags);
+
+ presto_d2d(dentry)->dd_flags &= and_flag;
+ presto_d2d(dentry)->dd_flags |= or_flag;
+ if (res)
+ *res = presto_d2d(dentry)->dd_flags;
+
+ return error;
+}
+
+/* given a path, operate on the flags in its cache. Used by mark_ioctl */
+int izo_mark_cache(struct dentry *dentry, int and_flag, int or_flag,
+ int *res)
+{
+ struct presto_cache *cache;
+
+ if (presto_d2d(dentry) == NULL) {
+ CERROR("InterMezzo: no ddata for inode %ld in %s\n",
+ dentry->d_inode->i_ino, __FUNCTION__);
+ return -EINVAL;
+ }
+
+ CDEBUG(D_INODE, "inode: %ld, and flag %x, or flag %x, dd_flags %x\n",
+ dentry->d_inode->i_ino, and_flag, or_flag,
+ presto_d2d(dentry)->dd_flags);
+
+ cache = presto_get_cache(dentry->d_inode);
+ if ( !cache ) {
+ CERROR("PRESTO: BAD: cannot find cache in izo_mark_cache\n");
+ return -EBADF;
+ }
+
+ ((int)cache->cache_flags) &= and_flag;
+ ((int)cache->cache_flags) |= or_flag;
+ if (res)
+ *res = (int)cache->cache_flags;
+
+ return 0;
+}
+
+int presto_set_max_kml_size(const char *path, unsigned long max_size)
+{
+ struct presto_file_set *fset;
+
+ ENTRY;
+
+ fset = presto_path2fileset(path);
+ if (IS_ERR(fset)) {
+ EXIT;
+ return PTR_ERR(fset);
+ }
+
+ fset->kml_truncate_size = max_size;
+ CDEBUG(D_CACHE, "KML truncate size set to %lu bytes for fset %s.\n",
+ max_size, path);
+
+ EXIT;
+ return 0;
+}
+
+int izo_mark_fset(struct dentry *dentry, int and_flag, int or_flag,
+ int * res)
+{
+ struct presto_file_set *fset;
+
+ fset = presto_fset(dentry);
+ if ( !fset ) {
+ CERROR("PRESTO: BAD: cannot find cache in izo_mark_cache\n");
+ make_bad_inode(dentry->d_inode);
+ return -EBADF;
+ }
+ ((int)fset->fset_flags) &= and_flag;
+ ((int)fset->fset_flags) |= or_flag;
+ if (res)
+ *res = (int)fset->fset_flags;
+
+ return 0;
+}
+
+/* talk to Lento about the permit */
+static int presto_permit_upcall(struct dentry *dentry)
+{
+ int rc;
+ char *path, *buffer;
+ int pathlen;
+ int minor;
+ int fsetnamelen;
+ struct presto_file_set *fset = NULL;
+
+ ENTRY;
+
+ if ( (minor = presto_i2m(dentry->d_inode)) < 0) {
+ EXIT;
+ return -EINVAL;
+ }
+
+ fset = presto_fset(dentry);
+ if (!fset) {
+ EXIT;
+ return -ENOTCONN;
+ }
+
+ if ( !presto_lento_up(minor) ) {
+ if ( fset->fset_flags & FSET_STEAL_PERMIT ) {
+ EXIT;
+ return 0;
+ } else {
+ EXIT;
+ return -ENOTCONN;
+ }
+ }
+
+ PRESTO_ALLOC(buffer, PAGE_SIZE);
+ if ( !buffer ) {
+ CERROR("PRESTO: out of memory!\n");
+ EXIT;
+ return -ENOMEM;
+ }
+ path = presto_path(dentry, fset->fset_dentry, buffer, PAGE_SIZE);
+ pathlen = MYPATHLEN(buffer, path);
+ fsetnamelen = strlen(fset->fset_name);
+ rc = izo_upc_permit(minor, dentry, pathlen, path, fset->fset_name);
+ PRESTO_FREE(buffer, PAGE_SIZE);
+ EXIT;
+ return rc;
+}
+
+/* get a write permit for the fileset of this inode
+ * - if this returns a negative value there was an error
+ * - if 0 is returned the permit was already in the kernel -- or --
+ * Lento gave us the permit without reintegration
+ * - lento returns the number of records it reintegrated
+ *
+ * Note that if this fileset has branches, a permit will -never- to a normal
+ * process for writing in the data area (ie, outside of .intermezzo)
+ */
+int presto_get_permit(struct inode * inode)
+{
+ struct dentry *de;
+ struct presto_file_set *fset;
+ int minor = presto_i2m(inode);
+ int rc = 0;
+
+ ENTRY;
+ if (minor < 0) {
+ EXIT;
+ return -1;
+ }
+
+ if ( ISLENTO(minor) ) {
+ EXIT;
+ return 0;
+ }
+
+ if (list_empty(&inode->i_dentry)) {
+ CERROR("No alias for inode %d\n", (int) inode->i_ino);
+ EXIT;
+ return -EINVAL;
+ }
+
+ de = list_entry(inode->i_dentry.next, struct dentry, d_alias);
+
+ if (presto_chk(de, PRESTO_DONT_JOURNAL)) {
+ EXIT;
+ return 0;
+ }
+
+ fset = presto_fset(de);
+ if ( !fset ) {
+ CERROR("Presto: no fileset in presto_get_permit!\n");
+ EXIT;
+ return -EINVAL;
+ }
+
+ if (fset->fset_flags & FSET_HAS_BRANCHES) {
+ EXIT;
+ return -EROFS;
+ }
+
+ spin_lock(&fset->fset_permit_lock);
+ if (fset->fset_flags & FSET_HASPERMIT) {
+ fset->fset_permit_count++;
+ CDEBUG(D_INODE, "permit count now %d, inode %lx\n",
+ fset->fset_permit_count, inode->i_ino);
+ spin_unlock(&fset->fset_permit_lock);
+ EXIT;
+ return 0;
+ }
+
+ /* Allow reintegration to proceed without locks -SHP */
+ fset->fset_permit_upcall_count++;
+ if (fset->fset_permit_upcall_count == 1) {
+ spin_unlock(&fset->fset_permit_lock);
+ rc = presto_permit_upcall(fset->fset_dentry);
+ spin_lock(&fset->fset_permit_lock);
+ fset->fset_permit_upcall_count--;
+ if (rc == 0) {
+ izo_mark_fset(fset->fset_dentry, ~0, FSET_HASPERMIT,
+ NULL);
+ fset->fset_permit_count++;
+ } else if (rc == ENOTCONN) {
+ CERROR("InterMezzo: disconnected operation. stealing permit.\n");
+ izo_mark_fset(fset->fset_dentry, ~0, FSET_HASPERMIT,
+ NULL);
+ fset->fset_permit_count++;
+ /* set a disconnected flag here to stop upcalls */
+ rc = 0;
+ } else {
+ CERROR("InterMezzo: presto_permit_upcall failed: %d\n", rc);
+ rc = -EROFS;
+ /* go to sleep here and try again? */
+ }
+ wake_up_interruptible(&fset->fset_permit_queue);
+ } else {
+ /* Someone is already doing an upcall; go to sleep. */
+ DECLARE_WAITQUEUE(wait, current);
+
+ spin_unlock(&fset->fset_permit_lock);
+ add_wait_queue(&fset->fset_permit_queue, &wait);
+ while (1) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ spin_lock(&fset->fset_permit_lock);
+ if (fset->fset_permit_upcall_count == 0)
+ break;
+ spin_unlock(&fset->fset_permit_lock);
+
+ if (signal_pending(current)) {
+ remove_wait_queue(&fset->fset_permit_queue,
+ &wait);
+ return -ERESTARTSYS;
+ }
+ schedule();
+ }
+ remove_wait_queue(&fset->fset_permit_queue, &wait);
+ /* We've been woken up: do we have the permit? */
+ if (fset->fset_flags & FSET_HASPERMIT)
+ /* FIXME: Is this the right thing? */
+ rc = -EAGAIN;
+ }
+
+ CDEBUG(D_INODE, "permit count now %d, ino %ld (likely 1), "
+ "rc %d\n", fset->fset_permit_count, inode->i_ino, rc);
+ spin_unlock(&fset->fset_permit_lock);
+ EXIT;
+ return rc;
+}
+
+int presto_put_permit(struct inode * inode)
+{
+ struct dentry *de;
+ struct presto_file_set *fset;
+ int minor = presto_i2m(inode);
+
+ ENTRY;
+ if (minor < 0) {
+ EXIT;
+ return -1;
+ }
+
+ if ( ISLENTO(minor) ) {
+ EXIT;
+ return 0;
+ }
+
+ if (list_empty(&inode->i_dentry)) {
+ CERROR("No alias for inode %d\n", (int) inode->i_ino);
+ EXIT;
+ return -1;
+ }
+
+ de = list_entry(inode->i_dentry.next, struct dentry, d_alias);
+
+ fset = presto_fset(de);
+ if ( !fset ) {
+ CERROR("InterMezzo: no fileset in %s!\n", __FUNCTION__);
+ EXIT;
+ return -1;
+ }
+
+ if (presto_chk(de, PRESTO_DONT_JOURNAL)) {
+ EXIT;
+ return 0;
+ }
+
+ spin_lock(&fset->fset_permit_lock);
+ if (fset->fset_flags & FSET_HASPERMIT) {
+ if (fset->fset_permit_count > 0)
+ fset->fset_permit_count--;
+ else
+ CERROR("Put permit while permit count is 0, "
+ "inode %ld!\n", inode->i_ino);
+ } else {
+ fset->fset_permit_count = 0;
+ CERROR("InterMezzo: put permit while no permit, inode %ld, "
+ "flags %x!\n", inode->i_ino, fset->fset_flags);
+ }
+
+ CDEBUG(D_INODE, "permit count now %d, inode %ld\n",
+ fset->fset_permit_count, inode->i_ino);
+
+ if (fset->fset_flags & FSET_PERMIT_WAITING &&
+ fset->fset_permit_count == 0) {
+ CDEBUG(D_INODE, "permit count now 0, ino %ld, wake sleepers\n",
+ inode->i_ino);
+ wake_up_interruptible(&fset->fset_permit_queue);
+ }
+ spin_unlock(&fset->fset_permit_lock);
+
+ EXIT;
+ return 0;
+}
+
+void presto_getversion(struct presto_version * presto_version,
+ struct inode * inode)
+{
+ presto_version->pv_mtime = (__u64)inode->i_mtime;
+ presto_version->pv_ctime = (__u64)inode->i_ctime;
+ presto_version->pv_size = (__u64)inode->i_size;
+}
+
+
+/* If uuid is non-null, it is the uuid of the peer that's making the revocation
+ * request. If it is null, this request was made locally, without external
+ * pressure to give up the permit. This most often occurs when a client
+ * starts up.
+ *
+ * FIXME: this function needs to be refactored slightly once we start handling
+ * multiple clients.
+ */
+int izo_revoke_permit(struct dentry *dentry, __u8 uuid[16])
+{
+ struct presto_file_set *fset;
+ DECLARE_WAITQUEUE(wait, current);
+ int minor, rc;
+
+ ENTRY;
+
+ minor = presto_i2m(dentry->d_inode);
+ if (minor < 0) {
+ EXIT;
+ return -ENODEV;
+ }
+
+ fset = presto_fset(dentry);
+ if (fset == NULL) {
+ EXIT;
+ return -ENODEV;
+ }
+
+ spin_lock(&fset->fset_permit_lock);
+ if (fset->fset_flags & FSET_PERMIT_WAITING) {
+ CERROR("InterMezzo: Two processes are waiting on the same permit--this not yet supported! Aborting this particular permit request...\n");
+ EXIT;
+ spin_unlock(&fset->fset_permit_lock);
+ return -EINVAL;
+ }
+
+ if (fset->fset_permit_count == 0)
+ goto got_permit;
+
+ /* Something is still using this permit. Mark that we're waiting for it
+ * and go to sleep. */
+ rc = izo_mark_fset(dentry, ~0, FSET_PERMIT_WAITING, NULL);
+ spin_unlock(&fset->fset_permit_lock);
+ if (rc < 0) {
+ EXIT;
+ return rc;
+ }
+
+ add_wait_queue(&fset->fset_permit_queue, &wait);
+ while (1) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ spin_lock(&fset->fset_permit_lock);
+ if (fset->fset_permit_count == 0)
+ break;
+ spin_unlock(&fset->fset_permit_lock);
+
+ if (signal_pending(current)) {
+ /* FIXME: there must be a better thing to return... */
+ remove_wait_queue(&fset->fset_permit_queue, &wait);
+ EXIT;
+ return -ERESTARTSYS;
+ }
+
+ /* FIXME: maybe there should be a timeout here. */
+
+ schedule();
+ }
+
+ remove_wait_queue(&fset->fset_permit_queue, &wait);
+ got_permit:
+ /* By this point fset->fset_permit_count is zero and we're holding the
+ * lock. */
+ CDEBUG(D_CACHE, "InterMezzo: releasing permit inode %ld\n",
+ dentry->d_inode->i_ino);
+
+ if (uuid != NULL) {
+ rc = izo_upc_revoke_permit(minor, fset->fset_name, uuid);
+ if (rc < 0) {
+ spin_unlock(&fset->fset_permit_lock);
+ EXIT;
+ return rc;
+ }
+ }
+
+ izo_mark_fset(fset->fset_dentry, ~FSET_PERMIT_WAITING, 0, NULL);
+ izo_mark_fset(fset->fset_dentry, ~FSET_HASPERMIT, 0, NULL);
+ spin_unlock(&fset->fset_permit_lock);
+ EXIT;
+ return 0;
+}
+
+inline int presto_is_read_only(struct presto_file_set * fset)
+{
+ int minor, mask;
+ struct presto_cache *cache = fset->fset_cache;
+
+ minor= cache->cache_psdev->uc_minor;
+ mask= (ISLENTO(minor)? FSET_LENTO_RO : FSET_CLIENT_RO);
+ if ( fset->fset_flags & mask )
+ return 1;
+ mask= (ISLENTO(minor)? CACHE_LENTO_RO : CACHE_CLIENT_RO);
+ return ((cache->cache_flags & mask)? 1 : 0);
+}
diff --git a/fs/intermezzo/psdev.c b/fs/intermezzo/psdev.c
new file mode 100644
index 00000000000000..6f73176619a053
--- /dev/null
+++ b/fs/intermezzo/psdev.c
@@ -0,0 +1,649 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * An implementation of a loadable kernel mode driver providing
+ * multiple kernel/user space bidirectional communications links.
+ *
+ * Author: Alan Cox <alan@cymru.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * Adapted to become the Linux 2.0 Coda pseudo device
+ * Peter Braam <braam@maths.ox.ac.uk>
+ * Michael Callahan <mjc@emmy.smith.edu>
+ *
+ * Changes for Linux 2.1
+ * Copyright (c) 1997 Carnegie-Mellon University
+ *
+ * Redone again for InterMezzo
+ * Copyright (c) 1998 Peter J. Braam
+ * Copyright (c) 2000 Mountain View Data, Inc.
+ * Copyright (c) 2000 Tacitus Systems, Inc.
+ * Copyright (c) 2001 Cluster File Systems, Inc.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/lp.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+#include <linux/delay.h>
+#include <linux/skbuff.h>
+#include <linux/proc_fs.h>
+#include <linux/vmalloc.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/devfs_fs_kernel.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/poll.h>
+#include <asm/uaccess.h>
+#include <linux/miscdevice.h>
+
+#include <linux/intermezzo_fs.h>
+#include <linux/intermezzo_psdev.h>
+
+
+#ifdef PRESTO_DEVEL
+int presto_print_entry = 1;
+int presto_debug = 4095;
+#else
+int presto_print_entry = 0;
+int presto_debug = 0;
+#endif
+
+/* Like inode.c (presto_sym_iops), the initializer is just to prevent
+ izo_channels from appearing as a COMMON symbol (and therefore
+ interfering with other modules that use the same variable name). */
+struct upc_channel izo_channels[MAX_CHANNEL] = {{0}};
+
+int izo_psdev_get_free_channel(void)
+{
+ int i, result = -1;
+
+ for (i = 0 ; i < MAX_CHANNEL ; i++ ) {
+ if (list_empty(&(izo_channels[i].uc_cache_list))) {
+ result = i;
+ break;
+ }
+ }
+ return result;
+}
+
+
+int izo_psdev_setpid(int minor)
+{
+ struct upc_channel *channel;
+ if (minor < 0 || minor >= MAX_CHANNEL) {
+ return -EINVAL;
+ }
+
+ channel = &(izo_channels[minor]);
+ /*
+ * This ioctl is performed by each Lento that starts up
+ * and wants to do further communication with presto.
+ */
+ CDEBUG(D_PSDEV, "Setting current pid to %d channel %d\n",
+ current->pid, minor);
+ channel->uc_pid = current->pid;
+ spin_lock(&channel->uc_lock);
+ if ( !list_empty(&channel->uc_processing) ) {
+ struct list_head *lh;
+ struct upc_req *req;
+ CERROR("WARNING: setpid & processing not empty!\n");
+ list_for_each(lh, &channel->uc_processing) {
+ req = list_entry(lh, struct upc_req, rq_chain);
+ /* freeing of req and data is done by the sleeper */
+ wake_up(&req->rq_sleep);
+ }
+ }
+ if ( !list_empty(&channel->uc_processing) ) {
+ CERROR("BAD: FAILDED TO CLEAN PROCESSING LIST!\n");
+ }
+ spin_unlock(&channel->uc_lock);
+ EXIT;
+ return 0;
+}
+
+int izo_psdev_setchannel(struct file *file, int fd)
+{
+
+ struct file *psdev_file = fget(fd);
+ struct presto_cache *cache = presto_get_cache(file->f_dentry->d_inode);
+
+ if (!psdev_file) {
+ CERROR("%s: no psdev_file!\n", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ if (!cache) {
+ CERROR("%s: no cache!\n", __FUNCTION__);
+ fput(psdev_file);
+ return -EINVAL;
+ }
+
+ if (psdev_file->private_data) {
+ CERROR("%s: channel already set!\n", __FUNCTION__);
+ fput(psdev_file);
+ return -EINVAL;
+ }
+
+ psdev_file->private_data = cache->cache_psdev;
+ fput(psdev_file);
+ EXIT;
+ return 0;
+}
+
+inline int presto_lento_up(int minor)
+{
+ return izo_channels[minor].uc_pid;
+}
+
+static unsigned int presto_psdev_poll(struct file *file, poll_table * wait)
+ {
+ struct upc_channel *channel = (struct upc_channel *)file->private_data;
+ unsigned int mask = POLLOUT | POLLWRNORM;
+
+ /* ENTRY; this will flood you */
+ if ( ! channel ) {
+ CERROR("%s: bad psdev file\n", __FUNCTION__);
+ return -EBADF;
+ }
+
+ poll_wait(file, &(channel->uc_waitq), wait);
+
+ spin_lock(&channel->uc_lock);
+ if (!list_empty(&channel->uc_pending)) {
+ CDEBUG(D_PSDEV, "Non-empty pending list.\n");
+ mask |= POLLIN | POLLRDNORM;
+ }
+ spin_unlock(&channel->uc_lock);
+
+ /* EXIT; will flood you */
+ return mask;
+}
+
+/*
+ * Receive a message written by Lento to the psdev
+ */
+static ssize_t presto_psdev_write(struct file *file, const char *buf,
+ size_t count, loff_t *off)
+{
+ struct upc_channel *channel = (struct upc_channel *)file->private_data;
+ struct upc_req *req = NULL;
+ struct upc_req *tmp;
+ struct list_head *lh;
+ struct izo_upcall_resp hdr;
+ int error;
+
+ if ( ! channel ) {
+ CERROR("%s: bad psdev file\n", __FUNCTION__);
+ return -EBADF;
+ }
+
+ /* Peek at the opcode, uniquefier */
+ if ( count < sizeof(hdr) ) {
+ CERROR("presto_psdev_write: Lento didn't write full hdr.\n");
+ return -EINVAL;
+ }
+
+ error = copy_from_user(&hdr, buf, sizeof(hdr));
+ if ( error )
+ return -EFAULT;
+
+ CDEBUG(D_PSDEV, "(process,opc,uniq)=(%d,%d,%d)\n",
+ current->pid, hdr.opcode, hdr.unique);
+
+ spin_lock(&channel->uc_lock);
+ /* Look for the message on the processing queue. */
+ list_for_each(lh, &channel->uc_processing) {
+ tmp = list_entry(lh, struct upc_req , rq_chain);
+ if (tmp->rq_unique == hdr.unique) {
+ req = tmp;
+ /* unlink here: keeps search length minimal */
+ list_del_init(&req->rq_chain);
+ CDEBUG(D_PSDEV,"Eureka opc %d uniq %d!\n",
+ hdr.opcode, hdr.unique);
+ break;
+ }
+ }
+ spin_unlock(&channel->uc_lock);
+ if (!req) {
+ CERROR("psdev_write: msg (%d, %d) not found\n",
+ hdr.opcode, hdr.unique);
+ return(-ESRCH);
+ }
+
+ /* move data into response buffer. */
+ if (req->rq_bufsize < count) {
+ CERROR("psdev_write: too much cnt: %d, cnt: %d, "
+ "opc: %d, uniq: %d.\n",
+ req->rq_bufsize, count, hdr.opcode, hdr.unique);
+ count = req->rq_bufsize; /* don't have more space! */
+ }
+ error = copy_from_user(req->rq_data, buf, count);
+ if ( error )
+ return -EFAULT;
+
+ /* adjust outsize: good upcalls can be aware of this */
+ req->rq_rep_size = count;
+ req->rq_flags |= REQ_WRITE;
+
+ wake_up(&req->rq_sleep);
+ return(count);
+}
+
+/*
+ * Read a message from the kernel to Lento
+ */
+static ssize_t presto_psdev_read(struct file * file, char * buf,
+ size_t count, loff_t *off)
+{
+ struct upc_channel *channel = (struct upc_channel *)file->private_data;
+ struct upc_req *req;
+ int result = count;
+
+ if ( ! channel ) {
+ CERROR("%s: bad psdev file\n", __FUNCTION__);
+ return -EBADF;
+ }
+
+ spin_lock(&channel->uc_lock);
+ if (list_empty(&(channel->uc_pending))) {
+ CDEBUG(D_UPCALL, "Empty pending list in read, not good\n");
+ spin_unlock(&channel->uc_lock);
+ return -EINVAL;
+ }
+ req = list_entry((channel->uc_pending.next), struct upc_req, rq_chain);
+ list_del(&(req->rq_chain));
+ if (! (req->rq_flags & REQ_ASYNC) ) {
+ list_add(&(req->rq_chain), channel->uc_processing.prev);
+ }
+ spin_unlock(&channel->uc_lock);
+
+ req->rq_flags |= REQ_READ;
+
+ /* Move the input args into userspace */
+ CDEBUG(D_PSDEV, "\n");
+ if (req->rq_bufsize <= count) {
+ result = req->rq_bufsize;
+ }
+
+ if (count < req->rq_bufsize) {
+ CERROR ("psdev_read: buffer too small, read %d of %d bytes\n",
+ count, req->rq_bufsize);
+ }
+
+ if ( copy_to_user(buf, req->rq_data, result) ) {
+ BUG();
+ return -EFAULT;
+ }
+
+ /* If request was asynchronous don't enqueue, but free */
+ if (req->rq_flags & REQ_ASYNC) {
+ CDEBUG(D_PSDEV, "psdev_read: async msg (%d, %d), result %d\n",
+ req->rq_opcode, req->rq_unique, result);
+ PRESTO_FREE(req->rq_data, req->rq_bufsize);
+ PRESTO_FREE(req, sizeof(*req));
+ return result;
+ }
+
+ return result;
+}
+
+
+static int presto_psdev_open(struct inode * inode, struct file * file)
+{
+ ENTRY;
+
+ file->private_data = NULL;
+
+ MOD_INC_USE_COUNT;
+
+ CDEBUG(D_PSDEV, "Psdev_open: caller: %d, flags: %d\n", current->pid, file->f_flags);
+
+ EXIT;
+ return 0;
+}
+
+
+
+static int presto_psdev_release(struct inode * inode, struct file * file)
+{
+ struct upc_channel *channel = (struct upc_channel *)file->private_data;
+ struct upc_req *req;
+ struct list_head *lh;
+ ENTRY;
+
+ if ( ! channel ) {
+ CERROR("%s: bad psdev file\n", __FUNCTION__);
+ return -EBADF;
+ }
+
+ MOD_DEC_USE_COUNT;
+ CDEBUG(D_PSDEV, "Lento: pid %d\n", current->pid);
+ channel->uc_pid = 0;
+
+ /* Wake up clients so they can return. */
+ CDEBUG(D_PSDEV, "Wake up clients sleeping for pending.\n");
+ spin_lock(&channel->uc_lock);
+ list_for_each(lh, &channel->uc_pending) {
+ req = list_entry(lh, struct upc_req, rq_chain);
+
+ /* Async requests stay around for a new lento */
+ if (req->rq_flags & REQ_ASYNC) {
+ continue;
+ }
+ /* the sleeper will free the req and data */
+ req->rq_flags |= REQ_DEAD;
+ wake_up(&req->rq_sleep);
+ }
+
+ CDEBUG(D_PSDEV, "Wake up clients sleeping for processing\n");
+ list_for_each(lh, &channel->uc_processing) {
+ req = list_entry(lh, struct upc_req, rq_chain);
+ /* freeing of req and data is done by the sleeper */
+ req->rq_flags |= REQ_DEAD;
+ wake_up(&req->rq_sleep);
+ }
+ spin_unlock(&channel->uc_lock);
+ CDEBUG(D_PSDEV, "Done.\n");
+
+ EXIT;
+ return 0;
+}
+
+static struct file_operations presto_psdev_fops = {
+ .read = presto_psdev_read,
+ .write = presto_psdev_write,
+ .poll = presto_psdev_poll,
+ .open = presto_psdev_open,
+ .release = presto_psdev_release
+};
+
+/* modules setup */
+static struct miscdevice intermezzo_psdev = {
+ INTERMEZZO_MINOR,
+ "intermezzo",
+ &presto_psdev_fops
+};
+
+int presto_psdev_init(void)
+{
+ int i;
+ int err;
+
+ if ( (err = misc_register(&intermezzo_psdev)) ) {
+ CERROR("%s: cannot register %d err %d\n",
+ __FUNCTION__, INTERMEZZO_MINOR, err);
+ return -EIO;
+ }
+
+ memset(&izo_channels, 0, sizeof(izo_channels));
+ for ( i = 0 ; i < MAX_CHANNEL ; i++ ) {
+ struct upc_channel *channel = &(izo_channels[i]);
+ INIT_LIST_HEAD(&channel->uc_pending);
+ INIT_LIST_HEAD(&channel->uc_processing);
+ INIT_LIST_HEAD(&channel->uc_cache_list);
+ init_waitqueue_head(&channel->uc_waitq);
+ channel->uc_lock = SPIN_LOCK_UNLOCKED;
+ channel->uc_hard = 0;
+ channel->uc_no_filter = 0;
+ channel->uc_no_journal = 0;
+ channel->uc_no_upcall = 0;
+ channel->uc_timeout = 30;
+ channel->uc_errorval = 0;
+ channel->uc_minor = i;
+ }
+ return 0;
+}
+
+void presto_psdev_cleanup(void)
+{
+ int i;
+
+ misc_deregister(&intermezzo_psdev);
+
+ for ( i = 0 ; i < MAX_CHANNEL ; i++ ) {
+ struct upc_channel *channel = &(izo_channels[i]);
+ struct list_head *lh, *next;
+
+ spin_lock(&channel->uc_lock);
+ if ( ! list_empty(&channel->uc_pending)) {
+ CERROR("Weird, tell Peter: module cleanup and pending list not empty dev %d\n", i);
+ }
+ if ( ! list_empty(&channel->uc_processing)) {
+ CERROR("Weird, tell Peter: module cleanup and processing list not empty dev %d\n", i);
+ }
+ if ( ! list_empty(&channel->uc_cache_list)) {
+ CERROR("Weird, tell Peter: module cleanup and cache listnot empty dev %d\n", i);
+ }
+ list_for_each_safe(lh, next, &channel->uc_pending) {
+ struct upc_req *req;
+
+ req = list_entry(lh, struct upc_req, rq_chain);
+ if ( req->rq_flags & REQ_ASYNC ) {
+ list_del(&(req->rq_chain));
+ CDEBUG(D_UPCALL, "free pending upcall type %d\n",
+ req->rq_opcode);
+ PRESTO_FREE(req->rq_data, req->rq_bufsize);
+ PRESTO_FREE(req, sizeof(struct upc_req));
+ } else {
+ req->rq_flags |= REQ_DEAD;
+ wake_up(&req->rq_sleep);
+ }
+ }
+ list_for_each(lh, &channel->uc_processing) {
+ struct upc_req *req;
+ req = list_entry(lh, struct upc_req, rq_chain);
+ list_del(&(req->rq_chain));
+ req->rq_flags |= REQ_DEAD;
+ wake_up(&req->rq_sleep);
+ }
+ spin_unlock(&channel->uc_lock);
+ }
+}
+
+/*
+ * lento_upcall and lento_downcall routines
+ */
+static inline unsigned long lento_waitfor_upcall
+ (struct upc_channel *channel, struct upc_req *req, int minor)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ unsigned long posttime;
+
+ req->rq_posttime = posttime = jiffies;
+
+ add_wait_queue(&req->rq_sleep, &wait);
+ for (;;) {
+ if ( izo_channels[minor].uc_hard == 0 )
+ set_current_state(TASK_INTERRUPTIBLE);
+ else
+ set_current_state(TASK_UNINTERRUPTIBLE);
+
+ /* got a reply */
+ if ( req->rq_flags & (REQ_WRITE | REQ_DEAD) )
+ break;
+
+ /* these cases only apply when TASK_INTERRUPTIBLE */
+ if ( !izo_channels[minor].uc_hard && signal_pending(current) ) {
+ /* if this process really wants to die, let it go */
+ if (sigismember(&(current->pending.signal), SIGKILL)||
+ sigismember(&(current->pending.signal), SIGINT) )
+ break;
+ /* signal is present: after timeout always return
+ really smart idea, probably useless ... */
+ if ( time_after(jiffies, req->rq_posttime +
+ izo_channels[minor].uc_timeout * HZ) )
+ break;
+ }
+ schedule();
+ }
+
+ spin_lock(&channel->uc_lock);
+ list_del_init(&req->rq_chain);
+ spin_unlock(&channel->uc_lock);
+ remove_wait_queue(&req->rq_sleep, &wait);
+ set_current_state(TASK_RUNNING);
+
+ CDEBUG(D_SPECIAL, "posttime: %ld, returned: %ld\n",
+ posttime, jiffies-posttime);
+ return (jiffies - posttime);
+}
+
+/*
+ * lento_upcall will return an error in the case of
+ * failed communication with Lento _or_ will peek at Lento
+ * reply and return Lento's error.
+ *
+ * As lento has 2 types of errors, normal errors (positive) and internal
+ * errors (negative), normal errors are negated, while internal errors
+ * are all mapped to -EINTR, while showing a nice warning message. (jh)
+ *
+ * lento_upcall will always free buffer, either directly, when an upcall
+ * is read (in presto_psdev_read), when the filesystem is unmounted, or
+ * when the module is unloaded.
+ */
+int izo_upc_upcall(int minor, int *size, struct izo_upcall_hdr *buffer,
+ int async)
+{
+ unsigned long runtime;
+ struct upc_channel *channel;
+ struct izo_upcall_resp *out;
+ struct upc_req *req;
+ int error = 0;
+
+ ENTRY;
+ channel = &(izo_channels[minor]);
+
+ if (channel->uc_no_upcall) {
+ EXIT;
+ goto exit_buf;
+ }
+ if (!channel->uc_pid && !async) {
+ EXIT;
+ error = -ENXIO;
+ goto exit_buf;
+ }
+
+ /* Format the request message. */
+ PRESTO_ALLOC(req, sizeof(struct upc_req));
+ if ( !req ) {
+ EXIT;
+ error = -ENOMEM;
+ goto exit_buf;
+ }
+ req->rq_data = (void *)buffer;
+ req->rq_flags = 0;
+ req->rq_bufsize = *size;
+ req->rq_rep_size = 0;
+ req->rq_opcode = buffer->u_opc;
+ req->rq_unique = ++channel->uc_seq;
+ init_waitqueue_head(&req->rq_sleep);
+
+ /* Fill in the common input args. */
+ buffer->u_uniq = req->rq_unique;
+ buffer->u_async = async;
+
+ /* Remove potential datarace possibility*/
+ if ( async )
+ req->rq_flags = REQ_ASYNC;
+
+ spin_lock(&channel->uc_lock);
+ /* Append msg to pending queue and poke Lento. */
+ list_add(&req->rq_chain, channel->uc_pending.prev);
+ spin_unlock(&channel->uc_lock);
+ CDEBUG(D_UPCALL,
+ "Proc %d waking Lento %d for(opc,uniq) =(%d,%d) msg at %p.\n",
+ current->pid, channel->uc_pid, req->rq_opcode,
+ req->rq_unique, req);
+ wake_up_interruptible(&channel->uc_waitq);
+
+ if ( async ) {
+ /* req, rq_data are freed in presto_psdev_read for async */
+ /* req->rq_flags = REQ_ASYNC;*/
+ EXIT;
+ return 0;
+ }
+
+ /* We can be interrupted while we wait for Lento to process
+ * our request. If the interrupt occurs before Lento has read
+ * the request, we dequeue and return. If it occurs after the
+ * read but before the reply, we dequeue, send a signal
+ * message, and return. If it occurs after the reply we ignore
+ * it. In no case do we want to restart the syscall. If it
+ * was interrupted by a lento shutdown (psdev_close), return
+ * ENODEV. */
+
+ /* Go to sleep. Wake up on signals only after the timeout. */
+ runtime = lento_waitfor_upcall(channel, req, minor);
+
+ CDEBUG(D_TIMING, "opc: %d time: %ld uniq: %d size: %d\n",
+ req->rq_opcode, jiffies - req->rq_posttime,
+ req->rq_unique, req->rq_rep_size);
+ CDEBUG(D_UPCALL,
+ "..process %d woken up by Lento for req at 0x%x, data at %x\n",
+ current->pid, (int)req, (int)req->rq_data);
+
+ if (channel->uc_pid) { /* i.e. Lento is still alive */
+ /* Op went through, interrupt or not we go on */
+ if (req->rq_flags & REQ_WRITE) {
+ out = (struct izo_upcall_resp *)req->rq_data;
+ /* here we map positive Lento errors to kernel errors */
+ if ( out->result < 0 ) {
+ CERROR("Tell Peter: Lento returns negative error %d, for oc %d!\n",
+ out->result, out->opcode);
+ out->result = EINVAL;
+ }
+ error = -out->result;
+ CDEBUG(D_UPCALL, "upcall: (u,o,r) (%d, %d, %d) out at %p\n",
+ out->unique, out->opcode, out->result, out);
+ *size = req->rq_rep_size;
+ EXIT;
+ goto exit_req;
+ }
+ /* Interrupted before lento read it. */
+ if ( !(req->rq_flags & REQ_READ) && signal_pending(current)) {
+ CDEBUG(D_UPCALL,
+ "Interrupt before read: (op,un)=(%d,%d), flags %x\n",
+ req->rq_opcode, req->rq_unique, req->rq_flags);
+ /* perhaps the best way to convince the app to give up? */
+ error = -EINTR;
+ EXIT;
+ goto exit_req;
+ }
+
+ /* interrupted after Lento did its read, send signal */
+ if ( (req->rq_flags & REQ_READ) && signal_pending(current) ) {
+ CDEBUG(D_UPCALL,"Interrupt after read: op = %d.%d, flags = %x\n",
+ req->rq_opcode, req->rq_unique, req->rq_flags);
+
+ error = -EINTR;
+ } else {
+ CERROR("Lento: Strange interruption - tell Peter.\n");
+ error = -EINTR;
+ }
+ } else { /* If lento died i.e. !UC_OPEN(channel) */
+ CERROR("lento_upcall: Lento dead on (op,un) (%d.%d) flags %d\n",
+ req->rq_opcode, req->rq_unique, req->rq_flags);
+ error = -ENODEV;
+ }
+
+exit_req:
+ PRESTO_FREE(req, sizeof(struct upc_req));
+exit_buf:
+ PRESTO_FREE(buffer,*size);
+ return error;
+}
diff --git a/fs/intermezzo/replicator.c b/fs/intermezzo/replicator.c
new file mode 100644
index 00000000000000..cc4cfae2f638be
--- /dev/null
+++ b/fs/intermezzo/replicator.c
@@ -0,0 +1,288 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Copyright (C) 2001 Cluster File Systems, Inc. <braam@clusterfs.com>
+ * Copyright (C) 2001 Tacit Networks, Inc. <phil@off.net>
+ *
+ * This file is part of InterMezzo, http://www.inter-mezzo.org.
+ *
+ * InterMezzo is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * InterMezzo 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 InterMezzo; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Manage RCVD records for clients in the kernel
+ *
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <stdarg.h>
+#include <asm/uaccess.h>
+
+#include <linux/errno.h>
+
+#include <linux/intermezzo_fs.h>
+
+/*
+ * this file contains a hash table of replicators/clients for a
+ * fileset. It allows fast lookup and update of reintegration status
+ */
+
+struct izo_offset_rec {
+ struct list_head or_list;
+ char or_uuid[16];
+ loff_t or_offset;
+};
+
+#define RCACHE_BITS 8
+#define RCACHE_SIZE (1 << RCACHE_BITS)
+#define RCACHE_MASK (RCACHE_SIZE - 1)
+
+static struct list_head *
+izo_rep_cache(void)
+{
+ int i;
+ struct list_head *cache;
+ PRESTO_ALLOC(cache, sizeof(struct list_head) * RCACHE_SIZE);
+ if (cache == NULL) {
+ CERROR("intermezzo-fatal: no memory for replicator cache\n");
+ return NULL;
+ }
+ memset(cache, 0, sizeof(struct list_head) * RCACHE_SIZE);
+ for (i = 0; i < RCACHE_SIZE; i++)
+ INIT_LIST_HEAD(&cache[i]);
+
+ return cache;
+}
+
+static struct list_head *
+izo_rep_hash(struct list_head *cache, char *uuid)
+{
+ return &cache[(RCACHE_MASK & uuid[1])];
+}
+
+void
+izo_rep_cache_clean(struct presto_file_set *fset)
+{
+ int i;
+ struct list_head *bucket;
+ struct list_head *tmp;
+
+ if (fset->fset_clients == NULL)
+ return;
+ for (i = 0; i < RCACHE_SIZE; i++) {
+
+ list_for_each_safe(tmp,bucket,&fset->fset_clients[i])
+ {
+ struct izo_offset_rec *offrec;
+ list_del(tmp);
+ offrec = list_entry(tmp, struct izo_offset_rec,or_list);
+ PRESTO_FREE(offrec, sizeof(struct izo_offset_rec));
+ }
+ }
+ PRESTO_FREE(fset->fset_clients,sizeof(struct list_head) * RCACHE_SIZE);
+}
+
+struct izo_offset_rec *
+izo_rep_cache_find(struct presto_file_set *fset, char *uuid)
+{
+ struct list_head *tmp, *buck = izo_rep_hash(fset->fset_clients, uuid);
+ struct izo_offset_rec *rec = NULL;
+
+ list_for_each(tmp, buck) {
+ rec = list_entry(tmp, struct izo_offset_rec, or_list);
+ if ( memcmp(rec->or_uuid, uuid, sizeof(rec->or_uuid)) == 0 )
+ return rec;
+ }
+
+ return NULL;
+}
+
+static int
+izo_rep_cache_add(struct presto_file_set *fset, struct izo_rcvd_rec *rec,
+ loff_t offset)
+{
+ struct izo_offset_rec *offrec;
+
+ if (izo_rep_cache_find(fset, rec->lr_uuid)) {
+ CERROR("izo: duplicate client entry %s off %Ld\n",
+ fset->fset_name, offset);
+ return -EINVAL;
+ }
+
+ PRESTO_ALLOC(offrec, sizeof(*offrec));
+ if (offrec == NULL) {
+ CERROR("izo: cannot allocate offrec\n");
+ return -ENOMEM;
+ }
+
+ memcpy(offrec->or_uuid, rec->lr_uuid, sizeof(rec->lr_uuid));
+ offrec->or_offset = offset;
+
+ list_add(&offrec->or_list,
+ izo_rep_hash(fset->fset_clients, rec->lr_uuid));
+ return 0;
+}
+
+int
+izo_rep_cache_init(struct presto_file_set *fset)
+{
+ struct izo_rcvd_rec rec;
+ loff_t offset = 0, last_offset = 0;
+
+ fset->fset_clients = izo_rep_cache();
+ if (fset->fset_clients == NULL) {
+ CERROR("Error initializing client cache\n");
+ return -ENOMEM;
+ }
+
+ while ( presto_fread(fset->fset_rcvd.fd_file, (char *)&rec,
+ sizeof(rec), &offset) == sizeof(rec) ) {
+ int rc;
+
+ if ((rc = izo_rep_cache_add(fset, &rec, last_offset)) < 0) {
+ izo_rep_cache_clean(fset);
+ return rc;
+ }
+
+ last_offset = offset;
+ }
+
+ return 0;
+}
+
+/*
+ * Return local last_rcvd record for the client. Update or create
+ * if necessary.
+ *
+ * XXX: After this call, any -EINVAL from izo_rcvd_get is a real error.
+ */
+int
+izo_repstatus(struct presto_file_set *fset, __u64 client_kmlsize,
+ struct izo_rcvd_rec *lr_client, struct izo_rcvd_rec *lr_server)
+{
+ int rc;
+ rc = izo_rcvd_get(lr_server, fset, lr_client->lr_uuid);
+ if (rc < 0 && rc != -EINVAL) {
+ return rc;
+ }
+
+ /* client is new or has been reset. */
+ if (rc < 0 || (client_kmlsize == 0 && lr_client->lr_remote_offset == 0)) {
+ memset(lr_server, 0, sizeof(*lr_server));
+ memcpy(lr_server->lr_uuid, lr_client->lr_uuid, sizeof(lr_server->lr_uuid));
+ rc = izo_rcvd_write(fset, lr_server);
+ if (rc < 0)
+ return rc;
+ }
+
+ /* update intersync */
+ rc = izo_upc_repstatus(presto_f2m(fset), fset->fset_name, lr_server);
+ return rc;
+}
+
+loff_t
+izo_rcvd_get(struct izo_rcvd_rec *rec, struct presto_file_set *fset, char *uuid)
+{
+ struct izo_offset_rec *offrec;
+ struct izo_rcvd_rec tmprec;
+ loff_t offset;
+
+ offrec = izo_rep_cache_find(fset, uuid);
+ if (offrec == NULL) {
+ CDEBUG(D_SPECIAL, "izo_get_rcvd: uuid not in hash.\n");
+ return -EINVAL;
+ }
+ offset = offrec->or_offset;
+
+ if (rec == NULL)
+ return offset;
+
+ if (presto_fread(fset->fset_rcvd.fd_file, (char *)&tmprec,
+ sizeof(tmprec), &offset) != sizeof(tmprec)) {
+ CERROR("izo_get_rcvd: Unable to read from last_rcvd file offset "
+ "%Lu\n", offset);
+ return -EIO;
+ }
+
+ memcpy(rec->lr_uuid, tmprec.lr_uuid, sizeof(tmprec.lr_uuid));
+ rec->lr_remote_recno = le64_to_cpu(tmprec.lr_remote_recno);
+ rec->lr_remote_offset = le64_to_cpu(tmprec.lr_remote_offset);
+ rec->lr_local_recno = le64_to_cpu(tmprec.lr_local_recno);
+ rec->lr_local_offset = le64_to_cpu(tmprec.lr_local_offset);
+ rec->lr_last_ctime = le64_to_cpu(tmprec.lr_last_ctime);
+
+ return offrec->or_offset;
+}
+
+/* Try to lookup the UUID in the hash. Insert it if it isn't found. Write the
+ * data to the file.
+ *
+ * Returns the offset of the beginning of the record in the last_rcvd file. */
+loff_t
+izo_rcvd_write(struct presto_file_set *fset, struct izo_rcvd_rec *rec)
+{
+ struct izo_offset_rec *offrec;
+ loff_t offset, rc;
+
+ ENTRY;
+
+ offrec = izo_rep_cache_find(fset, rec->lr_uuid);
+ if (offrec == NULL) {
+ /* I don't think it should be possible for an entry to be not in
+ * the hash table without also having an invalid offset, but we
+ * handle it gracefully regardless. */
+ write_lock(&fset->fset_rcvd.fd_lock);
+ offset = fset->fset_rcvd.fd_offset;
+ fset->fset_rcvd.fd_offset += sizeof(*rec);
+ write_unlock(&fset->fset_rcvd.fd_lock);
+
+ rc = izo_rep_cache_add(fset, rec, offset);
+ if (rc < 0) {
+ EXIT;
+ return rc;
+ }
+ } else
+ offset = offrec->or_offset;
+
+
+ rc = presto_fwrite(fset->fset_rcvd.fd_file, (char *)rec, sizeof(*rec),
+ &offset);
+ if (rc == sizeof(*rec))
+ /* presto_fwrite() advances 'offset' */
+ rc = offset - sizeof(*rec);
+
+ EXIT;
+ return rc;
+}
+
+loff_t
+izo_rcvd_upd_remote(struct presto_file_set *fset, char * uuid, __u64 remote_recno,
+ __u64 remote_offset)
+{
+ struct izo_rcvd_rec rec;
+
+ loff_t rc;
+
+ ENTRY;
+ rc = izo_rcvd_get(&rec, fset, uuid);
+ if (rc < 0)
+ return rc;
+ rec.lr_remote_recno = remote_recno;
+ rec.lr_remote_offset = remote_offset;
+
+ rc = izo_rcvd_write(fset, &rec);
+ EXIT;
+ if (rc < 0)
+ return rc;
+ return 0;
+}
diff --git a/fs/intermezzo/super.c b/fs/intermezzo/super.c
new file mode 100644
index 00000000000000..366a07df8d9149
--- /dev/null
+++ b/fs/intermezzo/super.c
@@ -0,0 +1,406 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Copyright (C) 1998 Peter J. Braam <braam@clusterfs.com>
+ * Copyright (C) 2000 Stelias Computing, Inc.
+ * Copyright (C) 2000 Red Hat, Inc.
+ *
+ * This file is part of InterMezzo, http://www.inter-mezzo.org.
+ *
+ * InterMezzo is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * InterMezzo 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 InterMezzo; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * presto's super.c
+ */
+
+static char rcsid[] __attribute ((unused)) = "$Id: super.c,v 1.42 2003/09/30 15:51:52 sunsetyang Exp $";
+#define INTERMEZZO_VERSION "$Revision: 1.42 $"
+
+#include <stdarg.h>
+
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/ext2_fs.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/blkdev.h>
+#include <linux/init.h>
+#include <linux/devfs_fs_kernel.h>
+#define __NO_VERSION__
+#include <linux/module.h>
+
+#include <linux/intermezzo_fs.h>
+#include <linux/intermezzo_psdev.h>
+
+#ifdef PRESTO_DEBUG
+long presto_vmemory = 0;
+long presto_kmemory = 0;
+#endif
+
+/* returns an allocated string, copied out from data if opt is found */
+static char *opt_read(const char *opt, char *data)
+{
+ char *value;
+ char *retval;
+
+ CDEBUG(D_SUPER, "option: %s, data %s\n", opt, data);
+ if ( strncmp(opt, data, strlen(opt)) )
+ return NULL;
+
+ if ( (value = strchr(data, '=')) == NULL )
+ return NULL;
+
+ value++;
+ PRESTO_ALLOC(retval, strlen(value) + 1);
+ if ( !retval ) {
+ CERROR("InterMezzo: Out of memory!\n");
+ return NULL;
+ }
+
+ strcpy(retval, value);
+ CDEBUG(D_SUPER, "Assigned option: %s, value %s\n", opt, retval);
+ return retval;
+}
+
+static void opt_store(char **dst, char *opt)
+{
+ if (!dst)
+ CERROR("intermezzo: store_opt, error dst == NULL\n");
+
+ if (*dst)
+ PRESTO_FREE(*dst, strlen(*dst) + 1);
+ *dst = opt;
+}
+
+static void opt_set_default(char **dst, char *defval)
+{
+ if (!dst)
+ CERROR("intermezzo: store_opt, error dst == NULL\n");
+
+ if (*dst)
+ PRESTO_FREE(*dst, strlen(*dst) + 1);
+ if (defval) {
+ char *def_alloced;
+ PRESTO_ALLOC(def_alloced, strlen(defval)+1);
+ if (!def_alloced) {
+ CERROR("InterMezzo: Out of memory!\n");
+ return ;
+ }
+ strcpy(def_alloced, defval);
+ *dst = def_alloced;
+ }
+}
+
+
+/* Find the options for InterMezzo in "options", saving them into the
+ * passed pointers. If the pointer is null, the option is discarded.
+ * Copy out all non-InterMezzo options into cache_data (to be passed
+ * to the read_super operation of the cache). The return value will
+ * be a pointer to the end of the cache_data.
+ */
+static char *presto_options(struct super_block *sb,
+ char *options, char *cache_data,
+ char **cache_type, char **fileset,
+ char **channel)
+{
+ char *this_char;
+ char *cache_data_end = cache_data;
+
+ /* set the defaults */
+ if (strcmp(sb->s_type->name, "intermezzo") == 0)
+ opt_set_default(cache_type, "ext3");
+ else
+ opt_set_default(cache_type, "tmpfs");
+
+ if (!options || !cache_data)
+ return cache_data_end;
+
+
+ CDEBUG(D_SUPER, "parsing options\n");
+ for (this_char = strtok (options, ",");
+ this_char != NULL;
+ this_char = strtok (NULL, ",")) {
+ char *opt;
+ CDEBUG(D_SUPER, "this_char %s\n", this_char);
+
+ if ( (opt = opt_read("fileset", this_char)) ) {
+ opt_store(fileset, opt);
+ continue;
+ }
+ if ( (opt = opt_read("cache_type", this_char)) ) {
+ opt_store(cache_type, opt);
+ continue;
+ }
+ if ( (opt = opt_read("channel", this_char)) ) {
+ opt_store(channel, opt);
+ continue;
+ }
+
+ cache_data_end +=
+ sprintf(cache_data_end, "%s%s",
+ cache_data_end != cache_data ? ",":"",
+ this_char);
+ }
+
+ return cache_data_end;
+}
+
+static int presto_set_channel(struct presto_cache *cache, char *channel)
+{
+ int minor;
+
+ ENTRY;
+ if (!channel) {
+ minor = izo_psdev_get_free_channel();
+ } else {
+ minor = simple_strtoul(channel, NULL, 0);
+ PRESTO_FREE(channel, strlen(channel) + 1);
+ }
+ if (minor < 0 || minor >= MAX_CHANNEL) {
+ CERROR("all channels in use or channel too large %d\n",
+ minor);
+ return -EINVAL;
+ }
+
+ cache->cache_psdev = &(izo_channels[minor]);
+ list_add(&cache->cache_channel_list,
+ &cache->cache_psdev->uc_cache_list);
+
+ EXIT;
+ return minor;
+}
+
+/* We always need to remove the presto options before passing
+ mount options to cache FS */
+struct super_block * presto_read_super(struct super_block * sb,
+ void * data, int silent)
+{
+ struct file_system_type *fstype;
+ struct presto_cache *cache = NULL;
+ char *cache_data = NULL;
+ char *cache_data_end;
+ char *cache_type = NULL;
+ char *fileset = NULL;
+ char *channel = NULL;
+ int err;
+ unsigned int minor;
+
+ ENTRY;
+
+ /* reserve space for the cache's data */
+ PRESTO_ALLOC(cache_data, PAGE_SIZE);
+ if ( !cache_data ) {
+ CERROR("presto_read_super: Cannot allocate data page.\n");
+ EXIT;
+ goto out_err;
+ }
+
+ /* read and validate options */
+ cache_data_end = presto_options(sb, data, cache_data, &cache_type,
+ &fileset, &channel);
+
+ /* was there anything for the cache filesystem in the data? */
+ if (cache_data_end == cache_data) {
+ PRESTO_FREE(cache_data, PAGE_SIZE);
+ cache_data = NULL;
+ } else {
+ CDEBUG(D_SUPER, "cache_data at %p is: %s\n", cache_data,
+ cache_data);
+ }
+
+ /* set up the cache */
+ cache = presto_cache_init();
+ if ( !cache ) {
+ CERROR("presto_read_super: failure allocating cache.\n");
+ EXIT;
+ goto out_err;
+ }
+ cache->cache_type = cache_type;
+
+ /* link cache to channel */
+ minor = presto_set_channel(cache, channel);
+ if (minor < 0) {
+ EXIT;
+ goto out_err;
+ }
+
+ CDEBUG(D_SUPER, "Presto: type=%s, fset=%s, dev= %d, flags %x\n",
+ cache_type, fileset?fileset:"NULL", minor, cache->cache_flags);
+
+ MOD_INC_USE_COUNT;
+
+ /* get the filter for the cache */
+ fstype = get_fs_type(cache_type);
+ cache->cache_filter = filter_get_filter_fs((const char *)cache_type);
+ if ( !fstype || !cache->cache_filter) {
+ CERROR("Presto: unrecognized fs type or cache type\n");
+ MOD_DEC_USE_COUNT;
+ EXIT;
+ goto out_err;
+ }
+
+ /* can we in fact mount the cache */
+ if ((fstype->fs_flags & FS_REQUIRES_DEV) && !sb->s_bdev) {
+ CERROR("filesystem \"%s\" requires a valid block device\n",
+ cache_type);
+ MOD_DEC_USE_COUNT;
+ EXIT;
+ goto out_err;
+ }
+
+ sb = fstype->read_super(sb, cache_data, silent);
+
+ /* this might have been freed above */
+ if (cache_data) {
+ PRESTO_FREE(cache_data, PAGE_SIZE);
+ cache_data = NULL;
+ }
+
+ if ( !sb ) {
+ CERROR("InterMezzo: cache mount failure.\n");
+ MOD_DEC_USE_COUNT;
+ EXIT;
+ goto out_err;
+ }
+
+ cache->cache_sb = sb;
+ cache->cache_root = dget(sb->s_root);
+
+ /* we now know the dev of the cache: hash the cache */
+ presto_cache_add(cache, sb->s_dev);
+ err = izo_prepare_fileset(sb->s_root, fileset);
+
+ if (fileset)
+ PRESTO_FREE(fileset, strlen(fileset) + 1);
+
+ filter_setup_journal_ops(cache->cache_filter, cache->cache_type);
+
+ /* make sure we have our own super operations: sb
+ still contains the cache operations */
+ filter_setup_super_ops(cache->cache_filter, sb->s_op,
+ &presto_super_ops);
+ sb->s_op = filter_c2usops(cache->cache_filter);
+
+ /* get izo directory operations: sb->s_root->d_inode exists now */
+ filter_setup_dir_ops(cache->cache_filter, sb->s_root->d_inode,
+ &presto_dir_iops, &presto_dir_fops);
+ filter_setup_dentry_ops(cache->cache_filter, sb->s_root->d_op,
+ &presto_dentry_ops);
+ sb->s_root->d_inode->i_op = filter_c2udiops(cache->cache_filter);
+ sb->s_root->d_inode->i_fop = filter_c2udfops(cache->cache_filter);
+ sb->s_root->d_op = filter_c2udops(cache->cache_filter);
+
+ EXIT;
+ return sb;
+
+ out_err:
+ CDEBUG(D_SUPER, "out_err called\n");
+ if (cache)
+ PRESTO_FREE(cache, sizeof(struct presto_cache));
+ if (cache_data)
+ PRESTO_FREE(cache_data, PAGE_SIZE);
+ if (fileset)
+ PRESTO_FREE(fileset, strlen(fileset) + 1);
+ if (channel)
+ PRESTO_FREE(channel, strlen(channel) + 1);
+ if (cache_type)
+ PRESTO_FREE(cache_type, strlen(cache_type) + 1);
+
+ CDEBUG(D_MALLOC, "mount error exit: kmem %ld, vmem %ld\n",
+ presto_kmemory, presto_vmemory);
+ return NULL;
+}
+
+
+
+#ifdef PRESTO_DEVEL
+static DECLARE_FSTYPE(presto_fs_type, "izo", presto_read_super, FS_REQUIRES_DEV);
+static DECLARE_FSTYPE(vpresto_fs_type, "vintermezzo", presto_read_super, FS_LITTER);
+#else
+static DECLARE_FSTYPE(vpresto_fs_type, "vintermezzo", presto_read_super, FS_LITTER);
+static DECLARE_FSTYPE(presto_fs_type, "intermezzo", presto_read_super, FS_REQUIRES_DEV);
+#endif
+
+
+
+int __init init_intermezzo_fs(void)
+{
+ int status;
+
+ printk(KERN_INFO "InterMezzo Kernel/Intersync communications " INTERMEZZO_VERSION
+ " info@clusterfs.com\n");
+
+ status = presto_psdev_init();
+ if ( status ) {
+ CERROR("Problem (%d) in init_intermezzo_psdev\n", status);
+ return status;
+ }
+
+ status = init_intermezzo_sysctl();
+ if (status) {
+ CERROR("presto: failed in init_intermezzo_sysctl!\n");
+ }
+
+ presto_cache_init_hash();
+
+ if (!presto_init_ddata_cache()) {
+ CERROR("presto out of memory!\n");
+ return -ENOMEM;
+ }
+
+ status = register_filesystem(&presto_fs_type);
+ if (status) {
+ CERROR("presto: failed in register_filesystem!\n");
+ }
+ status = register_filesystem(&vpresto_fs_type);
+ if (status) {
+ CERROR("vpresto: failed in register_filesystem!\n");
+ }
+ return status;
+}
+
+void __exit exit_intermezzo_fs(void)
+{
+ int err;
+
+ ENTRY;
+
+ if ( (err = unregister_filesystem(&presto_fs_type)) != 0 ) {
+ CERROR("presto: failed to unregister filesystem\n");
+ }
+ if ( (err = unregister_filesystem(&vpresto_fs_type)) != 0 ) {
+ CERROR("vpresto: failed to unregister filesystem\n");
+ }
+
+ presto_psdev_cleanup();
+ cleanup_intermezzo_sysctl();
+ presto_cleanup_ddata_cache();
+ CERROR("after cleanup: kmem %ld, vmem %ld\n",
+ presto_kmemory, presto_vmemory);
+}
+
+
+MODULE_AUTHOR("Cluster Filesystems Inc. <info@clusterfs.com>");
+MODULE_DESCRIPTION("InterMezzo Kernel/Intersync communications " INTERMEZZO_VERSION);
+MODULE_LICENSE("GPL");
+
+module_init(init_intermezzo_fs)
+module_exit(exit_intermezzo_fs)
diff --git a/fs/intermezzo/sysctl.c b/fs/intermezzo/sysctl.c
new file mode 100644
index 00000000000000..65b5bc8936c254
--- /dev/null
+++ b/fs/intermezzo/sysctl.c
@@ -0,0 +1,372 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Copyright (C) 1999 Peter J. Braam <braam@clusterfs.com>
+ *
+ * This file is part of InterMezzo, http://www.inter-mezzo.org.
+ *
+ * InterMezzo is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * InterMezzo 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 InterMezzo; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Sysctrl entries for Intermezzo!
+ */
+
+#define __NO_VERSION__
+#include <linux/config.h> /* for CONFIG_PROC_FS */
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/sysctl.h>
+#include <linux/swapctl.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/stat.h>
+#include <linux/ctype.h>
+#include <linux/init.h>
+#include <asm/bitops.h>
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+#include <linux/utsname.h>
+#include <linux/blk.h>
+
+
+#include <linux/intermezzo_fs.h>
+#include <linux/intermezzo_psdev.h>
+
+/* /proc entries */
+
+#ifdef CONFIG_PROC_FS
+struct proc_dir_entry *proc_fs_intermezzo;
+int intermezzo_mount_get_info( char * buffer, char ** start, off_t offset,
+ int length)
+{
+ int len=0;
+
+ /* this works as long as we are below 1024 characters! */
+ *start = buffer + offset;
+ len -= offset;
+
+ if ( len < 0 )
+ return -EINVAL;
+
+ return len;
+}
+
+#endif
+
+
+/* SYSCTL below */
+
+static struct ctl_table_header *intermezzo_table_header = NULL;
+/* 0x100 to avoid any chance of collisions at any point in the tree with
+ * non-directories
+ */
+#define PSDEV_INTERMEZZO (0x100)
+
+#define PSDEV_DEBUG 1 /* control debugging */
+#define PSDEV_TRACE 2 /* control enter/leave pattern */
+#define PSDEV_TIMEOUT 3 /* timeout on upcalls to become intrble */
+#define PSDEV_HARD 4 /* mount type "hard" or "soft" */
+#define PSDEV_NO_FILTER 5 /* controls presto_chk */
+#define PSDEV_NO_JOURNAL 6 /* controls presto_chk */
+#define PSDEV_NO_UPCALL 7 /* controls lento_upcall */
+#define PSDEV_ERRORVAL 8 /* controls presto_debug_fail_blkdev */
+#define PSDEV_EXCL_GID 9 /* which GID is ignored by presto */
+#define PSDEV_BYTES_TO_CLOSE 11 /* bytes to write before close */
+
+/* These are global presto control options */
+#define PRESTO_PRIMARY_CTLCNT 2
+static struct ctl_table presto_table[ PRESTO_PRIMARY_CTLCNT + MAX_CHANNEL + 1] =
+{
+ {PSDEV_DEBUG, "debug", &presto_debug, sizeof(int), 0644, NULL, &proc_dointvec},
+ {PSDEV_TRACE, "trace", &presto_print_entry, sizeof(int), 0644, NULL, &proc_dointvec},
+};
+
+/*
+ * Intalling the sysctl entries: strategy
+ * - have templates for each /proc/sys/intermezzo/ entry
+ * such an entry exists for each /dev/presto
+ * (proto_channel_entry)
+ * - have a template for the contents of such directories
+ * (proto_psdev_table)
+ * - have the master table (presto_table)
+ *
+ * When installing, malloc, memcpy and fix up the pointers to point to
+ * the appropriate constants in izo_channels[your_minor]
+ */
+
+static ctl_table proto_psdev_table[] = {
+ {PSDEV_HARD, "hard", 0, sizeof(int), 0644, NULL, &proc_dointvec},
+ {PSDEV_NO_FILTER, "no_filter", 0, sizeof(int), 0644, NULL, &proc_dointvec},
+ {PSDEV_NO_JOURNAL, "no_journal", NULL, sizeof(int), 0644, NULL, &proc_dointvec},
+ {PSDEV_NO_UPCALL, "no_upcall", NULL, sizeof(int), 0644, NULL, &proc_dointvec},
+ {PSDEV_TIMEOUT, "timeout", NULL, sizeof(int), 0644, NULL, &proc_dointvec},
+#ifdef PRESTO_DEBUG
+ {PSDEV_ERRORVAL, "errorval", NULL, sizeof(int), 0644, NULL, &proc_dointvec},
+#endif
+ { 0 }
+};
+
+static ctl_table proto_channel_entry = {
+ PSDEV_INTERMEZZO, 0, NULL, 0, 0555, 0,
+};
+
+static ctl_table intermezzo_table[2] = {
+ {PSDEV_INTERMEZZO, "intermezzo", NULL, 0, 0555, presto_table},
+ {0}
+};
+
+/* support for external setting and getting of opts. */
+/* particularly via ioctl. The Right way to do this is via sysctl,
+ * but that will have to wait until intermezzo gets its own nice set of
+ * sysctl IDs
+ */
+/* we made these separate as setting may in future be more restricted
+ * than getting
+ */
+#ifdef RON_MINNICH
+int dosetopt(int minor, struct psdev_opt *opt)
+{
+ int retval = 0;
+ int newval = opt->optval;
+
+ ENTRY;
+
+ switch(opt->optname) {
+
+ case PSDEV_TIMEOUT:
+ izo_channels[minor].uc_timeout = newval;
+ break;
+
+ case PSDEV_HARD:
+ izo_channels[minor].uc_hard = newval;
+ break;
+
+ case PSDEV_NO_FILTER:
+ izo_channels[minor].uc_no_filter = newval;
+ break;
+
+ case PSDEV_NO_JOURNAL:
+ izo_channels[minor].uc_no_journal = newval;
+ break;
+
+ case PSDEV_NO_UPCALL:
+ izo_channels[minor].uc_no_upcall = newval;
+ break;
+
+#ifdef PRESTO_DEBUG
+ case PSDEV_ERRORVAL: {
+ /* If we have a positive arg, set a breakpoint for that
+ * value. If we have a negative arg, make that device
+ * read-only. FIXME It would be much better to only
+ * allow setting the underlying device read-only for the
+ * current presto cache.
+ */
+ int errorval = izo_channels[minor].uc_errorval;
+ if (errorval < 0) {
+ if (newval == 0)
+ set_device_ro(-errorval, 0);
+ else
+ CERROR("device %s already read only\n",
+ kdevname(-errorval));
+ } else {
+ if (newval < 0)
+ set_device_ro(-newval, 1);
+ izo_channels[minor].uc_errorval = newval;
+ CDEBUG(D_PSDEV, "setting errorval to %d\n", newval);
+ }
+
+ break;
+ }
+#endif
+
+ case PSDEV_TRACE:
+ case PSDEV_DEBUG:
+ case PSDEV_BYTES_TO_CLOSE:
+ default:
+ CDEBUG(D_PSDEV,
+ "ioctl: dosetopt: minor %d, bad optname 0x%x, \n",
+ minor, opt->optname);
+
+ retval = -EINVAL;
+ }
+
+ EXIT;
+ return retval;
+}
+
+int dogetopt(int minor, struct psdev_opt *opt)
+{
+ int retval = 0;
+
+ ENTRY;
+
+ switch(opt->optname) {
+
+ case PSDEV_TIMEOUT:
+ opt->optval = izo_channels[minor].uc_timeout;
+ break;
+
+ case PSDEV_HARD:
+ opt->optval = izo_channels[minor].uc_hard;
+ break;
+
+ case PSDEV_NO_FILTER:
+ opt->optval = izo_channels[minor].uc_no_filter;
+ break;
+
+ case PSDEV_NO_JOURNAL:
+ opt->optval = izo_channels[minor].uc_no_journal;
+ break;
+
+ case PSDEV_NO_UPCALL:
+ opt->optval = izo_channels[minor].uc_no_upcall;
+ break;
+
+#ifdef PSDEV_DEBUG
+ case PSDEV_ERRORVAL: {
+ int errorval = izo_channels[minor].uc_errorval;
+ if (errorval < 0 && is_read_only(-errorval))
+ CERROR("device %s has been set read-only\n",
+ kdevname(-errorval));
+ opt->optval = izo_channels[minor].uc_errorval;
+ break;
+ }
+#endif
+
+ case PSDEV_TRACE:
+ case PSDEV_DEBUG:
+ case PSDEV_BYTES_TO_CLOSE:
+ default:
+ CDEBUG(D_PSDEV,
+ "ioctl: dogetopt: minor %d, bad optval 0x%x, \n",
+ minor, opt->optname);
+
+ retval = -EINVAL;
+ }
+
+ EXIT;
+ return retval;
+}
+#endif
+
+
+/* allocate the tables for the presto devices. We need
+ * sizeof(proto_channel_table)/sizeof(proto_channel_table[0])
+ * entries for each dev
+ */
+int /* __init */ init_intermezzo_sysctl(void)
+{
+ int i;
+ int total_dev = MAX_CHANNEL;
+ int entries_per_dev = sizeof(proto_psdev_table) /
+ sizeof(proto_psdev_table[0]);
+ int total_entries = entries_per_dev * total_dev;
+ ctl_table *dev_ctl_table;
+
+ PRESTO_ALLOC(dev_ctl_table, sizeof(ctl_table) * total_entries);
+
+ if (! dev_ctl_table) {
+ CERROR("WARNING: presto couldn't allocate dev_ctl_table\n");
+ EXIT;
+ return -ENOMEM;
+ }
+
+ /* now fill in the entries ... we put the individual presto<x>
+ * entries at the end of the table, and the per-presto stuff
+ * starting at the front. We assume that the compiler makes
+ * this code more efficient, but really, who cares ... it
+ * happens once per reboot.
+ */
+ for(i = 0; i < total_dev; i++) {
+ void *p;
+
+ /* entry for this /proc/sys/intermezzo/intermezzo"i" */
+ ctl_table *psdev = &presto_table[i + PRESTO_PRIMARY_CTLCNT];
+ /* entries for the individual "files" in this "directory" */
+ ctl_table *psdev_entries = &dev_ctl_table[i * entries_per_dev];
+ /* init the psdev and psdev_entries with the prototypes */
+ *psdev = proto_channel_entry;
+ memcpy(psdev_entries, proto_psdev_table,
+ sizeof(proto_psdev_table));
+ /* now specialize them ... */
+ /* the psdev has to point to psdev_entries, and fix the number */
+ psdev->ctl_name = psdev->ctl_name + i + 1; /* sorry */
+
+ PRESTO_ALLOC(p, PROCNAME_SIZE);
+ psdev->procname = p;
+ if (!psdev->procname) {
+ PRESTO_FREE(dev_ctl_table,
+ sizeof(ctl_table) * total_entries);
+ return -ENOMEM;
+ }
+ sprintf((char *) psdev->procname, "intermezzo%d", i);
+ /* hook presto into */
+ psdev->child = psdev_entries;
+
+ /* now for each psdev entry ... */
+ psdev_entries[0].data = &(izo_channels[i].uc_hard);
+ psdev_entries[1].data = &(izo_channels[i].uc_no_filter);
+ psdev_entries[2].data = &(izo_channels[i].uc_no_journal);
+ psdev_entries[3].data = &(izo_channels[i].uc_no_upcall);
+ psdev_entries[4].data = &(izo_channels[i].uc_timeout);
+#ifdef PRESTO_DEBUG
+ psdev_entries[5].data = &(izo_channels[i].uc_errorval);
+#endif
+ }
+
+
+#ifdef CONFIG_SYSCTL
+ if ( !intermezzo_table_header )
+ intermezzo_table_header =
+ register_sysctl_table(intermezzo_table, 0);
+#endif
+#ifdef CONFIG_PROC_FS
+ proc_fs_intermezzo = proc_mkdir("intermezzo", proc_root_fs);
+ proc_fs_intermezzo->owner = THIS_MODULE;
+ create_proc_info_entry("mounts", 0, proc_fs_intermezzo,
+ intermezzo_mount_get_info);
+#endif
+ return 0;
+}
+
+void cleanup_intermezzo_sysctl(void)
+{
+ int total_dev = MAX_CHANNEL;
+ int entries_per_dev = sizeof(proto_psdev_table) /
+ sizeof(proto_psdev_table[0]);
+ int total_entries = entries_per_dev * total_dev;
+ int i;
+
+#ifdef CONFIG_SYSCTL
+ if ( intermezzo_table_header )
+ unregister_sysctl_table(intermezzo_table_header);
+ intermezzo_table_header = NULL;
+#endif
+ for(i = 0; i < total_dev; i++) {
+ /* entry for this /proc/sys/intermezzo/intermezzo"i" */
+ ctl_table *psdev = &presto_table[i + PRESTO_PRIMARY_CTLCNT];
+ PRESTO_FREE(psdev->procname, PROCNAME_SIZE);
+ }
+ /* presto_table[PRESTO_PRIMARY_CTLCNT].child points to the
+ * dev_ctl_table previously allocated in init_intermezzo_psdev()
+ */
+ PRESTO_FREE(presto_table[PRESTO_PRIMARY_CTLCNT].child, sizeof(ctl_table) * total_entries);
+
+#if CONFIG_PROC_FS
+ remove_proc_entry("mounts", proc_fs_intermezzo);
+ remove_proc_entry("intermezzo", proc_root_fs);
+#endif
+}
+
diff --git a/fs/intermezzo/upcall.c b/fs/intermezzo/upcall.c
new file mode 100644
index 00000000000000..b11af21f60c1e4
--- /dev/null
+++ b/fs/intermezzo/upcall.c
@@ -0,0 +1,557 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Copyright (C) 2001, 2002 Cluster File Systems, Inc. <braam@clusterfs.com>
+ * Copyright (C) 2001 Tacit Networks, Inc. <phil@off.net>
+ *
+ * This file is part of InterMezzo, http://www.inter-mezzo.org.
+ *
+ * InterMezzo is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * InterMezzo 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 InterMezzo; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Mostly platform independent upcall operations to a cache manager:
+ * -- upcalls
+ * -- upcall routines
+ *
+ */
+
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/signal.h>
+#include <linux/signal.h>
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <linux/string.h>
+#include <asm/uaccess.h>
+#include <linux/vmalloc.h>
+#include <asm/segment.h>
+
+#include <linux/intermezzo_lib.h>
+#include <linux/intermezzo_fs.h>
+#include <linux/intermezzo_psdev.h>
+
+#include <linux/intermezzo_idl.h>
+
+/*
+ At present:
+ -- Asynchronous calls:
+ - kml: give a "more" kml indication to userland
+ - kml_truncate: initiate KML truncation
+ - release_permit: kernel is done with permit
+ -- Synchronous
+ - open: fetch file
+ - permit: get a permit
+
+ Errors returned by user level code are positive
+
+ */
+
+static struct izo_upcall_hdr *upc_pack(__u32 opcode, int pathlen, char *path,
+ char *fsetname, int reclen, char *rec,
+ int *size)
+{
+ struct izo_upcall_hdr *hdr;
+ char *ptr;
+ ENTRY;
+
+ *size = sizeof(struct izo_upcall_hdr);
+ if ( fsetname ) {
+ *size += round_strlen(fsetname);
+ }
+ if ( path ) {
+ *size += round_strlen(path);
+ }
+ if ( rec ) {
+ *size += size_round(reclen);
+ }
+ PRESTO_ALLOC(hdr, *size);
+ if (!hdr) {
+ CERROR("intermezzo upcall: out of memory (opc %d)\n", opcode);
+ EXIT;
+ return NULL;
+ }
+ memset(hdr, 0, *size);
+
+ ptr = (char *)hdr + sizeof(*hdr);
+
+ /* XXX do we need fsuid ? */
+ hdr->u_len = *size;
+ hdr->u_version = IZO_UPC_VERSION;
+ hdr->u_opc = opcode;
+ hdr->u_pid = current->pid;
+ hdr->u_uid = current->fsuid;
+
+ if (path) {
+ /*XXX Robert: please review what len to pass in for
+ NUL terminated strings */
+ hdr->u_pathlen = strlen(path);
+ LOGL0(path, hdr->u_pathlen, ptr);
+ }
+ if (fsetname) {
+ hdr->u_fsetlen = strlen(fsetname);
+ LOGL0(fsetname, strlen(fsetname), ptr);
+ }
+ if (rec) {
+ hdr->u_reclen = reclen;
+ LOGL(rec, reclen, ptr);
+ }
+
+ EXIT;
+ return hdr;
+}
+
+/* the upcalls */
+int izo_upc_kml(int minor, __u64 offset, __u32 first_recno, __u64 length, __u32 last_recno, char *fsetname)
+{
+ int size;
+ int error;
+ struct izo_upcall_hdr *hdr;
+
+ ENTRY;
+ if (!presto_lento_up(minor)) {
+ EXIT;
+ return 0;
+ }
+
+ hdr = upc_pack(IZO_UPC_KML, 0, NULL, fsetname, 0, NULL, &size);
+ if (!hdr || IS_ERR(hdr)) {
+ EXIT;
+ return -PTR_ERR(hdr);
+ }
+
+ hdr->u_offset = offset;
+ hdr->u_first_recno = first_recno;
+ hdr->u_length = length;
+ hdr->u_last_recno = last_recno;
+
+ CDEBUG(D_UPCALL, "KML: fileset %s, offset %Lu, length %Lu, "
+ "first %u, last %d; minor %d\n",
+ fsetname, hdr->u_offset, hdr->u_length, hdr->u_first_recno,
+ hdr->u_last_recno, minor);
+
+ error = izo_upc_upcall(minor, &size, hdr, ASYNCHRONOUS);
+
+ EXIT;
+ return -error;
+}
+
+int izo_upc_kml_truncate(int minor, __u64 length, __u32 last_recno, char *fsetname)
+{
+ int size;
+ int error;
+ struct izo_upcall_hdr *hdr;
+
+ ENTRY;
+ if (!presto_lento_up(minor)) {
+ EXIT;
+ return 0;
+ }
+
+ hdr = upc_pack(IZO_UPC_KML_TRUNC, 0, NULL, fsetname, 0, NULL, &size);
+ if (!hdr || IS_ERR(hdr)) {
+ EXIT;
+ return -PTR_ERR(hdr);
+ }
+
+ hdr->u_length = length;
+ hdr->u_last_recno = last_recno;
+
+ CDEBUG(D_UPCALL, "KML TRUNCATE: fileset %s, length %Lu, "
+ "last recno %d, minor %d\n",
+ fsetname, hdr->u_length, hdr->u_last_recno, minor);
+
+ error = izo_upc_upcall(minor, &size, hdr, ASYNCHRONOUS);
+
+ EXIT;
+ return error;
+}
+
+int izo_upc_open(int minor, __u32 pathlen, char *path, char *fsetname, struct lento_vfs_context *info)
+{
+ int size;
+ int error;
+ struct izo_upcall_hdr *hdr;
+ ENTRY;
+
+ if (!presto_lento_up(minor)) {
+ EXIT;
+ return -EIO;
+ }
+
+ hdr = upc_pack(IZO_UPC_OPEN, pathlen, path, fsetname,
+ sizeof(*info), (char*)info, &size);
+ if (!hdr || IS_ERR(hdr)) {
+ EXIT;
+ return -PTR_ERR(hdr);
+ }
+
+ CDEBUG(D_UPCALL, "path %s\n", path);
+
+ error = izo_upc_upcall(minor, &size, hdr, SYNCHRONOUS);
+ if (error)
+ CERROR("InterMezzo: %s: error %d\n", __FUNCTION__, error);
+
+ EXIT;
+ return -error;
+}
+
+int izo_upc_get_fileid(int minor, __u32 reclen, char *rec,
+ __u32 pathlen, char *path, char *fsetname)
+{
+ int size;
+ int error;
+ struct izo_upcall_hdr *hdr;
+ ENTRY;
+
+ if (!presto_lento_up(minor)) {
+ EXIT;
+ return -EIO;
+ }
+
+ hdr = upc_pack(IZO_UPC_GET_FILEID, pathlen, path, fsetname, reclen, rec, &size);
+ if (!hdr || IS_ERR(hdr)) {
+ EXIT;
+ return -PTR_ERR(hdr);
+ }
+
+ CDEBUG(D_UPCALL, "path %s\n", path);
+
+ error = izo_upc_upcall(minor, &size, hdr, SYNCHRONOUS);
+ if (error)
+ CERROR("InterMezzo: %s: error %d\n", __FUNCTION__, error);
+
+ EXIT;
+ return -error;
+}
+
+int izo_upc_backfetch(int minor, char *path, char *fsetname, struct lento_vfs_context *info)
+{
+ int size;
+ int error;
+ struct izo_upcall_hdr *hdr;
+ ENTRY;
+
+ if (!presto_lento_up(minor)) {
+ EXIT;
+ return -EIO;
+ }
+
+ hdr = upc_pack(IZO_UPC_BACKFETCH, strlen(path), path, fsetname,
+ sizeof(*info), (char *)info, &size);
+ if (!hdr || IS_ERR(hdr)) {
+ EXIT;
+ return -PTR_ERR(hdr);
+ }
+
+ /* This is currently synchronous, kml_reint_record blocks */
+ error = izo_upc_upcall(minor, &size, hdr, SYNCHRONOUS);
+ if (error)
+ CERROR("InterMezzo: %s: error %d\n", __FUNCTION__, error);
+
+ EXIT;
+ return -error;
+}
+
+int izo_upc_permit(int minor, struct dentry *dentry, __u32 pathlen, char *path,
+ char *fsetname)
+{
+ int size;
+ int error;
+ struct izo_upcall_hdr *hdr;
+
+ ENTRY;
+
+ hdr = upc_pack(IZO_UPC_PERMIT, pathlen, path, fsetname, 0, NULL, &size);
+ if (!hdr || IS_ERR(hdr)) {
+ EXIT;
+ return -PTR_ERR(hdr);
+ }
+
+ CDEBUG(D_UPCALL, "Permit minor %d path %s\n", minor, path);
+
+ error = izo_upc_upcall(minor, &size, hdr, SYNCHRONOUS);
+
+ if (error == -EROFS) {
+ int err;
+ CERROR("InterMezzo: ERROR - requested permit for read-only "
+ "fileset.\n Setting \"%s\" read-only!\n", path);
+ err = izo_mark_cache(dentry, 0xFFFFFFFF, CACHE_CLIENT_RO, NULL);
+ if (err)
+ CERROR("InterMezzo ERROR: mark_cache %d\n", err);
+ } else if (error) {
+ CERROR("InterMezzo: %s: error %d\n", __FUNCTION__, error);
+ }
+
+ EXIT;
+ return error;
+}
+
+/* This is a ping-pong upcall handled on the server when a client (uuid)
+ * requests the permit for itself. */
+int izo_upc_revoke_permit(int minor, char *fsetname, __u8 uuid[16])
+{
+ int size;
+ int error;
+ struct izo_upcall_hdr *hdr;
+
+ ENTRY;
+
+ hdr = upc_pack(IZO_UPC_REVOKE_PERMIT, 0, NULL, fsetname, 0, NULL, &size);
+ if (!hdr || IS_ERR(hdr)) {
+ EXIT;
+ return -PTR_ERR(hdr);
+ }
+
+ memcpy(hdr->u_uuid, uuid, sizeof(hdr->u_uuid));
+
+ error = izo_upc_upcall(minor, &size, hdr, SYNCHRONOUS);
+
+ if (error)
+ CERROR("InterMezzo: %s: error %d\n", __FUNCTION__, error);
+
+ EXIT;
+ return -error;
+}
+
+int izo_upc_go_fetch_kml(int minor, char *fsetname, __u8 uuid[16],
+ __u64 kmlsize)
+{
+ int size;
+ int error;
+ struct izo_upcall_hdr *hdr;
+ ENTRY;
+
+ if (!presto_lento_up(minor)) {
+ EXIT;
+ return -EIO;
+ }
+
+ hdr = upc_pack(IZO_UPC_GO_FETCH_KML, 0, NULL, fsetname, 0, NULL, &size);
+ if (!hdr || IS_ERR(hdr)) {
+ EXIT;
+ return -PTR_ERR(hdr);
+ }
+
+ hdr->u_offset = kmlsize;
+ memcpy(hdr->u_uuid, uuid, sizeof(hdr->u_uuid));
+
+ error = izo_upc_upcall(minor, &size, hdr, ASYNCHRONOUS);
+ if (error)
+ CERROR("%s: error %d\n", __FUNCTION__, error);
+
+ EXIT;
+ return -error;
+}
+
+int izo_upc_connect(int minor, __u64 ip_address, __u64 port, __u8 uuid[16],
+ int client_flag)
+{
+ int size;
+ int error;
+ struct izo_upcall_hdr *hdr;
+ ENTRY;
+
+ if (!presto_lento_up(minor)) {
+ EXIT;
+ return -EIO;
+ }
+
+ hdr = upc_pack(IZO_UPC_CONNECT, 0, NULL, NULL, 0, NULL, &size);
+ if (!hdr || IS_ERR(hdr)) {
+ EXIT;
+ return -PTR_ERR(hdr);
+ }
+
+ hdr->u_offset = ip_address;
+ hdr->u_length = port;
+ memcpy(hdr->u_uuid, uuid, sizeof(hdr->u_uuid));
+ hdr->u_first_recno = client_flag;
+
+ error = izo_upc_upcall(minor, &size, hdr, SYNCHRONOUS);
+ if (error) {
+ CERROR("%s: error %d\n", __FUNCTION__, error);
+ }
+
+ EXIT;
+ return -error;
+}
+
+int izo_upc_set_kmlsize(int minor, char *fsetname, __u8 uuid[16], __u64 kmlsize)
+{
+ int size;
+ int error;
+ struct izo_upcall_hdr *hdr;
+ ENTRY;
+
+ if (!presto_lento_up(minor)) {
+ EXIT;
+ return -EIO;
+ }
+
+ hdr = upc_pack(IZO_UPC_SET_KMLSIZE, 0, NULL, fsetname, 0, NULL, &size);
+ if (!hdr || IS_ERR(hdr)) {
+ EXIT;
+ return -PTR_ERR(hdr);
+ }
+
+ memcpy(hdr->u_uuid, uuid, sizeof(hdr->u_uuid));
+ hdr->u_length = kmlsize;
+
+ error = izo_upc_upcall(minor, &size, hdr, SYNCHRONOUS);
+ if (error)
+ CERROR("%s: error %d\n", __FUNCTION__, error);
+
+ EXIT;
+ return -error;
+}
+
+int izo_upc_repstatus(int minor, char * fsetname, struct izo_rcvd_rec *lr_server)
+{
+ int size;
+ int error;
+ struct izo_upcall_hdr *hdr;
+ ENTRY;
+
+ if (!presto_lento_up(minor)) {
+ EXIT;
+ return -EIO;
+ }
+
+ hdr = upc_pack(IZO_UPC_REPSTATUS, 0, NULL, fsetname,
+ sizeof(*lr_server), (char*)lr_server,
+ &size);
+ if (!hdr || IS_ERR(hdr)) {
+ EXIT;
+ return -PTR_ERR(hdr);
+ }
+
+ error = izo_upc_upcall(minor, &size, hdr, SYNCHRONOUS);
+ if (error)
+ CERROR("%s: error %d\n", __FUNCTION__, error);
+
+ EXIT;
+ return -error;
+}
+
+
+#if 0
+int izo_upc_client_make_branch(int minor, char *fsetname, char *tagname,
+ char *branchname)
+{
+ int size, error;
+ struct izo_upcall_hdr *hdr;
+ int pathlen;
+ char *path;
+ ENTRY;
+
+ hdr = upc_pack(IZO_UPC_CLIENT_MAKE_BRANCH, strlen(tagname), tagname,
+ fsetname, strlen(branchname) + 1, branchname, &size);
+ if (!hdr || IS_ERR(hdr)) {
+ error = -PTR_ERR(hdr);
+ goto error;
+ }
+
+ error = izo_upc_upcall(minor, &size, hdr, SYNCHRONOUS);
+ if (error)
+ CERROR("InterMezzo: error %d\n", error);
+
+ error:
+ PRESTO_FREE(path, pathlen);
+ EXIT;
+ return error;
+}
+#endif
+
+int izo_upc_server_make_branch(int minor, char *fsetname)
+{
+ int size, error;
+ struct izo_upcall_hdr *hdr;
+ ENTRY;
+
+ hdr = upc_pack(IZO_UPC_SERVER_MAKE_BRANCH, 0, NULL, fsetname, 0, NULL, &size);
+ if (!hdr || IS_ERR(hdr)) {
+ error = -PTR_ERR(hdr);
+ goto error;
+ }
+
+ error = izo_upc_upcall(minor, &size, hdr, SYNCHRONOUS);
+ if (error)
+ CERROR("InterMezzo: error %d\n", error);
+
+ error:
+ EXIT;
+ return -error;
+}
+
+int izo_upc_branch_undo(int minor, char *fsetname, char *branchname)
+{
+ int size;
+ int error;
+ struct izo_upcall_hdr *hdr;
+ ENTRY;
+
+ if (!presto_lento_up(minor)) {
+ EXIT;
+ return -EIO;
+ }
+
+ hdr = upc_pack(IZO_UPC_BRANCH_UNDO, strlen(branchname), branchname,
+ fsetname, 0, NULL, &size);
+ if (!hdr || IS_ERR(hdr)) {
+ EXIT;
+ return -PTR_ERR(hdr);
+ }
+
+ error = izo_upc_upcall(minor, &size, hdr, SYNCHRONOUS);
+ if (error)
+ CERROR("InterMezzo: %s: error %d\n", __FUNCTION__, error);
+
+ EXIT;
+ return -error;
+}
+
+int izo_upc_branch_redo(int minor, char *fsetname, char *branchname)
+{
+ int size;
+ int error;
+ struct izo_upcall_hdr *hdr;
+ ENTRY;
+
+ if (!presto_lento_up(minor)) {
+ EXIT;
+ return -EIO;
+ }
+
+ hdr = upc_pack(IZO_UPC_BRANCH_REDO, strlen(branchname) + 1, branchname,
+ fsetname, 0, NULL, &size);
+ if (!hdr || IS_ERR(hdr)) {
+ EXIT;
+ return -PTR_ERR(hdr);
+ }
+
+ error = izo_upc_upcall(minor, &size, hdr, SYNCHRONOUS);
+ if (error)
+ CERROR("InterMezzo: %s: error %d\n", __FUNCTION__, error);
+
+ EXIT;
+ return -error;
+}
diff --git a/fs/intermezzo/vfs.c b/fs/intermezzo/vfs.c
new file mode 100644
index 00000000000000..c57f2ab9800dc6
--- /dev/null
+++ b/fs/intermezzo/vfs.c
@@ -0,0 +1,2461 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * Copyright (C) 2001, 2002 Cluster File Systems, Inc.
+ * Copyright (C) 2000 Stelias Computing, Inc.
+ * Copyright (C) 2000 Red Hat, Inc.
+ *
+ * This file is part of InterMezzo, http://www.inter-mezzo.org.
+ *
+ * InterMezzo is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * InterMezzo 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 InterMezzo; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * vfs.c
+ *
+ * This file implements kernel downcalls from lento.
+ *
+ * Author: Rob Simmonds <simmonds@stelias.com>
+ * Andreas Dilger <adilger@stelias.com>
+ * Copyright (C) 2000 Stelias Computing Inc
+ * Copyright (C) 2000 Red Hat Inc.
+ *
+ * Extended attribute support
+ * Copyright (C) 2001 Shirish H. Phatak, Tacit Networks, Inc.
+ *
+ * This code is based on code from namei.c in the linux file system;
+ * see copyright notice below.
+ */
+
+/** namei.c copyright **/
+
+/*
+ * linux/fs/namei.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+/*
+ * Some corrections by tytso.
+ */
+
+/* [Feb 1997 T. Schoebel-Theuer] Complete rewrite of the pathname
+ * lookup logic.
+ */
+
+/** end of namei.c copyright **/
+
+#include <linux/mm.h>
+#include <linux/proc_fs.h>
+#include <linux/smp_lock.h>
+#include <linux/quotaops.h>
+
+#include <asm/uaccess.h>
+#include <asm/unaligned.h>
+#include <asm/semaphore.h>
+#include <asm/pgtable.h>
+
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/blk.h>
+
+#include <linux/intermezzo_fs.h>
+#include <linux/intermezzo_psdev.h>
+
+#ifdef CONFIG_FS_EXT_ATTR
+# include <linux/ext_attr.h>
+
+# ifdef CONFIG_FS_POSIX_ACL
+# include <linux/posix_acl.h>
+# endif
+#endif
+
+extern struct inode_operations presto_sym_iops;
+
+/* Write the last_rcvd values to the last_rcvd file. We don't know what the
+ * UUID or last_ctime values are, so we have to read from the file first
+ * (sigh).
+ * exported for branch_reinter in kml_reint.c*/
+int presto_write_last_rcvd(struct rec_info *recinfo,
+ struct presto_file_set *fset,
+ struct lento_vfs_context *info)
+{
+ int rc;
+ struct izo_rcvd_rec rcvd_rec;
+
+ ENTRY;
+
+ memset(&rcvd_rec, 0, sizeof(rcvd_rec));
+ memcpy(rcvd_rec.lr_uuid, info->uuid, sizeof(rcvd_rec.lr_uuid));
+ rcvd_rec.lr_remote_recno = HTON__u64(info->recno);
+ rcvd_rec.lr_remote_offset = HTON__u64(info->kml_offset);
+ rcvd_rec.lr_local_recno = HTON__u64(recinfo->recno);
+ rcvd_rec.lr_local_offset = HTON__u64(recinfo->offset + recinfo->size);
+
+ rc = izo_rcvd_write(fset, &rcvd_rec);
+ if (rc < 0) {
+ /* izo_rcvd_write returns negative errors and non-negative
+ * offsets */
+ CERROR("InterMezzo: izo_rcvd_write failed: %d\n", rc);
+ EXIT;
+ return rc;
+ }
+ EXIT;
+ return 0;
+}
+
+/*
+ * It's inline, so penalty for filesystems that don't use sticky bit is
+ * minimal.
+ */
+static inline int check_sticky(struct inode *dir, struct inode *inode)
+{
+ if (!(dir->i_mode & S_ISVTX))
+ return 0;
+ if (inode->i_uid == current->fsuid)
+ return 0;
+ if (dir->i_uid == current->fsuid)
+ return 0;
+ return !capable(CAP_FOWNER);
+}
+
+/* from linux/fs/namei.c */
+static inline int may_delete(struct inode *dir,struct dentry *victim, int isdir)
+{
+ int error;
+ if (!victim->d_inode || victim->d_parent->d_inode != dir)
+ return -ENOENT;
+ error = permission(dir,MAY_WRITE | MAY_EXEC);
+ if (error)
+ return error;
+ if (IS_APPEND(dir))
+ return -EPERM;
+ if (check_sticky(dir, victim->d_inode)||IS_APPEND(victim->d_inode)||
+ IS_IMMUTABLE(victim->d_inode))
+ return -EPERM;
+ if (isdir) {
+ if (!S_ISDIR(victim->d_inode->i_mode))
+ return -ENOTDIR;
+ if (IS_ROOT(victim))
+ return -EBUSY;
+ } else if (S_ISDIR(victim->d_inode->i_mode))
+ return -EISDIR;
+ return 0;
+}
+
+/* from linux/fs/namei.c */
+static inline int may_create(struct inode *dir, struct dentry *child) {
+ if (child->d_inode)
+ return -EEXIST;
+ if (IS_DEADDIR(dir))
+ return -ENOENT;
+ return permission(dir,MAY_WRITE | MAY_EXEC);
+}
+
+#ifdef PRESTO_DEBUG
+/* The loop_discard_io() function is available via a kernel patch to the
+ * loop block device. It "works" by accepting writes, but throwing them
+ * away, rather than trying to write them to disk. The old method worked
+ * by setting the underlying device read-only, but that has the problem
+ * that dirty buffers are kept in memory, and ext3 didn't like that at all.
+ */
+#ifdef CONFIG_LOOP_DISCARD
+#define BLKDEV_FAIL(dev,fail) loop_discard_io(dev,fail)
+#else
+#define BLKDEV_FAIL(dev,fail) set_device_ro(dev, 1)
+#endif
+
+/* If a breakpoint has been set via /proc/sys/intermezzo/intermezzoX/errorval,
+ * that is the same as "value", the underlying device will "fail" now.
+ */
+inline void presto_debug_fail_blkdev(struct presto_file_set *fset,
+ unsigned long value)
+{
+ int minor = presto_f2m(fset);
+ int errorval = izo_channels[minor].uc_errorval;
+ kdev_t dev = fset->fset_dentry->d_inode->i_dev;
+
+ if (errorval && errorval == (long)value && !is_read_only(dev)) {
+ CDEBUG(D_SUPER, "setting device %s read only\n", kdevname(dev));
+ BLKDEV_FAIL(dev, 1);
+ izo_channels[minor].uc_errorval = -dev;
+ }
+}
+#else
+#define presto_debug_fail_blkdev(dev,value) do {} while (0)
+#endif
+
+
+static inline int presto_do_kml(struct lento_vfs_context *info,
+ struct dentry *dentry)
+{
+ if ( ! (info->flags & LENTO_FL_KML) )
+ return 0;
+ if ( presto_chk(dentry, PRESTO_DONT_JOURNAL) )
+ return 0;
+ return 1;
+}
+
+static inline int presto_do_rcvd(struct lento_vfs_context *info,
+ struct dentry *dentry)
+{
+ if ( ! (info->flags & LENTO_FL_EXPECT) )
+ return 0;
+ if ( presto_chk(dentry, PRESTO_DONT_JOURNAL) )
+ return 0;
+ return 1;
+}
+
+
+/* XXX fixme: this should not fail, all these dentries are in memory
+ when _we_ call this */
+int presto_settime(struct presto_file_set *fset,
+ struct dentry *newobj,
+ struct dentry *parent,
+ struct dentry *target,
+ struct lento_vfs_context *ctx,
+ int valid)
+{
+ int error = 0;
+ struct dentry *dentry;
+ struct inode *inode;
+ struct inode_operations *iops;
+ struct iattr iattr;
+
+ ENTRY;
+ if (ctx->flags & LENTO_FL_IGNORE_TIME ) {
+ EXIT;
+ return 0;
+ }
+
+ iattr.ia_ctime = ctx->updated_time;
+ iattr.ia_mtime = ctx->updated_time;
+ iattr.ia_valid = valid;
+
+ while (1) {
+ if (parent && ctx->flags & LENTO_FL_TOUCH_PARENT) {
+ dentry = parent;
+ parent = NULL;
+ } else if (newobj && ctx->flags & LENTO_FL_TOUCH_NEWOBJ) {
+ dentry = newobj;
+ newobj = NULL;
+ } else if (target) {
+ dentry = target;
+ target = NULL;
+ } else
+ break;
+
+ inode = dentry->d_inode;
+
+ error = -EROFS;
+ if (IS_RDONLY(inode)) {
+ EXIT;
+ return -EROFS;
+ }
+
+ if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) {
+ EXIT;
+ return -EPERM;
+ }
+
+ error = -EPERM;
+ iops = filter_c2cdiops(fset->fset_cache->cache_filter);
+ if (!iops) {
+ EXIT;
+ return error;
+ }
+
+ if (iops->setattr != NULL)
+ error = iops->setattr(dentry, &iattr);
+ else {
+ error = 0;
+ inode_setattr(dentry->d_inode, &iattr);
+ }
+ }
+ EXIT;
+ return error;
+}
+
+void izo_get_rollback_data(struct inode *inode, struct izo_rollback_data *rb)
+{
+ rb->rb_mode = (__u32)inode->i_mode;
+ rb->rb_rdev = (__u32)inode->i_rdev;
+ rb->rb_uid = (__u64)inode->i_uid;
+ rb->rb_gid = (__u64)inode->i_gid;
+}
+
+
+int presto_do_close(struct presto_file_set *fset, struct file *file)
+{
+ struct rec_info rec;
+ int rc = -ENOSPC;
+ void *handle;
+ struct inode *inode = file->f_dentry->d_inode;
+ struct presto_file_data *fdata =
+ (struct presto_file_data *)file->private_data;
+
+ ENTRY;
+ presto_getversion(&fdata->fd_info.remote_version, inode);
+
+ rc = presto_reserve_space(fset->fset_cache, PRESTO_REQHIGH);
+ if (rc) {
+ EXIT;
+ return rc;
+ }
+
+ handle = presto_trans_start(fset, file->f_dentry->d_inode,
+ KML_OPCODE_RELEASE);
+ if ( IS_ERR(handle) ) {
+ CERROR("presto_release: no space for transaction\n");
+ return rc;
+ }
+
+ if (fdata->fd_info.flags & LENTO_FL_KML)
+ rc = presto_journal_close(&rec, fset, file, file->f_dentry,
+ &fdata->fd_version,
+ &fdata->fd_info.remote_version);
+ if (rc) {
+ CERROR("presto_close: cannot journal close\n");
+ goto out;
+ }
+
+ if (fdata->fd_info.flags & LENTO_FL_EXPECT)
+ rc = presto_write_last_rcvd(&rec, fset, &fdata->fd_info);
+
+ if (rc) {
+ CERROR("presto_close: cannot journal last_rcvd\n");
+ goto out;
+ }
+ presto_trans_commit(fset, handle);
+
+ /* cancel the LML record */
+ handle = presto_trans_start(fset, inode, KML_OPCODE_WRITE);
+ if ( IS_ERR(handle) ) {
+ CERROR("presto_release: no space for clear\n");
+ return -ENOSPC;
+ }
+
+ rc = presto_clear_lml_close(fset, fdata->fd_lml_offset);
+ if (rc < 0 ) {
+ CERROR("presto_close: cannot journal close\n");
+ goto out;
+ }
+ presto_truncate_lml(fset);
+
+ out:
+ presto_release_space(fset->fset_cache, PRESTO_REQHIGH);
+ presto_trans_commit(fset, handle);
+ EXIT;
+ return rc;
+}
+
+int presto_do_setattr(struct presto_file_set *fset, struct dentry *dentry,
+ struct iattr *iattr, struct lento_vfs_context *info)
+{
+ struct rec_info rec;
+ struct inode *inode = dentry->d_inode;
+ struct inode_operations *iops;
+ int error;
+ struct presto_version old_ver, new_ver;
+ struct izo_rollback_data rb;
+ void *handle;
+ loff_t old_size=inode->i_size;
+
+ ENTRY;
+ error = -EROFS;
+ if (IS_RDONLY(inode)) {
+ EXIT;
+ return -EROFS;
+ }
+
+ if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) {
+ EXIT;
+ return -EPERM;
+ }
+
+ presto_getversion(&old_ver, dentry->d_inode);
+ izo_get_rollback_data(dentry->d_inode, &rb);
+ error = -EPERM;
+ iops = filter_c2cdiops(fset->fset_cache->cache_filter);
+
+ error = presto_reserve_space(fset->fset_cache, 2*PRESTO_REQHIGH);
+ if (error) {
+ EXIT;
+ return error;
+ }
+
+ if (iattr->ia_valid & ATTR_SIZE) {
+ if (izo_mark_dentry(dentry, ~PRESTO_DATA, 0, NULL) != 0)
+ CERROR("izo_mark_dentry(inode %ld, ~PRESTO_DATA) "
+ "failed\n", dentry->d_inode->i_ino);
+ handle = presto_trans_start(fset, dentry->d_inode,
+ KML_OPCODE_TRUNC);
+ } else {
+ handle = presto_trans_start(fset, dentry->d_inode,
+ KML_OPCODE_SETATTR);
+ }
+
+ if ( IS_ERR(handle) ) {
+ CERROR("presto_do_setattr: no space for transaction\n");
+ presto_release_space(fset->fset_cache, 2*PRESTO_REQHIGH);
+ return -ENOSPC;
+ }
+
+ if (dentry->d_inode && iops && iops->setattr) {
+ error = iops->setattr(dentry, iattr);
+ } else {
+ error = inode_change_ok(dentry->d_inode, iattr);
+ if (!error)
+ inode_setattr(inode, iattr);
+ }
+
+ if (!error && (iattr->ia_valid & ATTR_SIZE))
+ vmtruncate(inode, iattr->ia_size);
+
+ if (error) {
+ EXIT;
+ goto exit;
+ }
+
+ presto_debug_fail_blkdev(fset, KML_OPCODE_SETATTR | 0x10);
+
+ if ( presto_do_kml(info, dentry) ) {
+ if ((iattr->ia_valid & ATTR_SIZE) && (old_size != inode->i_size)) {
+ struct file file;
+ /* Journal a close whenever we see a potential truncate
+ * At the receiving end, lento should explicitly remove
+ * ATTR_SIZE from the list of valid attributes */
+ presto_getversion(&new_ver, inode);
+ file.private_data = NULL;
+ file.f_dentry = dentry;
+ error = presto_journal_close(&rec, fset, &file, dentry,
+ &old_ver, &new_ver);
+ }
+
+ if (!error)
+ error = presto_journal_setattr(&rec, fset, dentry,
+ &old_ver, &rb, iattr);
+ }
+
+ presto_debug_fail_blkdev(fset, KML_OPCODE_SETATTR | 0x20);
+ if ( presto_do_rcvd(info, dentry) )
+ error = presto_write_last_rcvd(&rec, fset, info);
+
+ presto_debug_fail_blkdev(fset, KML_OPCODE_SETATTR | 0x30);
+
+ EXIT;
+exit:
+ presto_release_space(fset->fset_cache, 2*PRESTO_REQHIGH);
+ presto_trans_commit(fset, handle);
+ return error;
+}
+
+int lento_setattr(const char *name, struct iattr *iattr,
+ struct lento_vfs_context *info)
+{
+ struct nameidata nd;
+ struct dentry *dentry;
+ struct presto_file_set *fset;
+ int error;
+#ifdef CONFIG_FS_POSIX_ACL
+ int (*set_posix_acl)(struct inode *, int type, posix_acl_t *)=NULL;
+#endif
+
+ ENTRY;
+ CDEBUG(D_PIOCTL,"name %s, valid %#x, mode %#o, uid %d, gid %d, size %Ld\n",
+ name, iattr->ia_valid, iattr->ia_mode, iattr->ia_uid,
+ iattr->ia_gid, iattr->ia_size);
+ CDEBUG(D_PIOCTL, "atime %#lx, mtime %#lx, ctime %#lx, attr_flags %#x\n",
+ iattr->ia_atime, iattr->ia_mtime, iattr->ia_ctime,
+ iattr->ia_attr_flags);
+ CDEBUG(D_PIOCTL, "offset %d, recno %d, flags %#x\n",
+ info->slot_offset, info->recno, info->flags);
+
+ lock_kernel();
+ error = presto_walk(name, &nd);
+ if (error) {
+ EXIT;
+ goto exit;
+ }
+ dentry = nd.dentry;
+
+ fset = presto_fset(dentry);
+ error = -EINVAL;
+ if ( !fset ) {
+ CERROR("No fileset!\n");
+ EXIT;
+ goto exit_lock;
+ }
+
+ /* NOTE: this prevents us from changing the filetype on setattr,
+ * as we normally only want to change permission bits.
+ * If this is not correct, then we need to fix the perl code
+ * to always send the file type OR'ed with the permission.
+ */
+ if (iattr->ia_valid & ATTR_MODE) {
+ int set_mode = iattr->ia_mode;
+ iattr->ia_mode = (iattr->ia_mode & S_IALLUGO) |
+ (dentry->d_inode->i_mode & ~S_IALLUGO);
+ CDEBUG(D_PIOCTL, "chmod: orig %#o, set %#o, result %#o\n",
+ dentry->d_inode->i_mode, set_mode, iattr->ia_mode);
+#ifdef CONFIG_FS_POSIX_ACL
+ /* ACl code interacts badly with setattr
+ * since it tries to modify the ACL using
+ * set_ext_attr which recurses back into presto.
+ * This only happens if ATTR_MODE is set.
+ * Here we are doing a "forced" mode set
+ * (initiated by lento), so we disable the
+ * set_posix_acl operation which
+ * prevents such recursion. -SHP
+ *
+ * This will probably still be required when native
+ * acl journalling is in place.
+ */
+ set_posix_acl=dentry->d_inode->i_op->set_posix_acl;
+ dentry->d_inode->i_op->set_posix_acl=NULL;
+#endif
+ }
+
+ error = presto_do_setattr(fset, dentry, iattr, info);
+
+ if (info->flags & LENTO_FL_SET_DDFILEID) {
+ struct presto_dentry_data *dd = presto_d2d(dentry);
+ if (dd) {
+ dd->remote_ino = info->remote_ino;
+ dd->remote_generation = info->remote_generation;
+ }
+ }
+
+#ifdef CONFIG_FS_POSIX_ACL
+ /* restore the inode_operations if we changed them*/
+ if (iattr->ia_valid & ATTR_MODE)
+ dentry->d_inode->i_op->set_posix_acl=set_posix_acl;
+#endif
+
+
+ EXIT;
+exit_lock:
+ path_release(&nd);
+exit:
+ unlock_kernel();
+ return error;
+}
+
+int presto_do_create(struct presto_file_set *fset, struct dentry *dir,
+ struct dentry *dentry, int mode,
+ struct lento_vfs_context *info)
+{
+ struct rec_info rec;
+ int error;
+ struct presto_version tgt_dir_ver, new_file_ver;
+ struct inode_operations *iops;
+ void *handle;
+
+ ENTRY;
+ mode &= S_IALLUGO;
+ mode |= S_IFREG;
+
+ down(&dir->d_inode->i_zombie);
+ error = presto_reserve_space(fset->fset_cache, PRESTO_REQHIGH);
+ if (error) {
+ EXIT;
+ up(&dir->d_inode->i_zombie);
+ return error;
+ }
+
+ error = may_create(dir->d_inode, dentry);
+ if (error) {
+ EXIT;
+ goto exit_pre_lock;
+ }
+
+ error = -EPERM;
+ iops = filter_c2cdiops(fset->fset_cache->cache_filter);
+ if (!iops->create) {
+ EXIT;
+ goto exit_pre_lock;
+ }
+
+ presto_getversion(&tgt_dir_ver, dir->d_inode);
+ handle = presto_trans_start(fset, dir->d_inode, KML_OPCODE_CREATE);
+ if ( IS_ERR(handle) ) {
+ EXIT;
+ presto_release_space(fset->fset_cache, PRESTO_REQHIGH);
+ CERROR("presto_do_create: no space for transaction\n");
+ error=-ENOSPC;
+ goto exit_pre_lock;
+ }
+ DQUOT_INIT(dir->d_inode);
+ lock_kernel();
+ error = iops->create(dir->d_inode, dentry, mode);
+ if (error) {
+ EXIT;
+ goto exit_lock;
+ }
+
+ if (dentry->d_inode) {
+ struct presto_cache *cache = fset->fset_cache;
+ /* was this already done? */
+ presto_set_ops(dentry->d_inode, cache->cache_filter);
+
+ filter_setup_dentry_ops(cache->cache_filter,
+ dentry->d_op,
+ &presto_dentry_ops);
+ dentry->d_op = filter_c2udops(cache->cache_filter);
+
+ /* if Lento creates this file, we won't have data */
+ if ( ISLENTO(presto_c2m(cache)) ) {
+ presto_set(dentry, PRESTO_ATTR);
+ } else {
+ presto_set(dentry, PRESTO_ATTR | PRESTO_DATA);
+ }
+ }
+
+ info->flags |= LENTO_FL_TOUCH_PARENT;
+ error = presto_settime(fset, NULL, dir, dentry,
+ info, ATTR_CTIME | ATTR_MTIME);
+ if (error) {
+ EXIT;
+ goto exit_lock;
+ }
+
+ presto_debug_fail_blkdev(fset, KML_OPCODE_CREATE | 0x10);
+
+ if ( presto_do_kml(info, dentry) ) {
+ presto_getversion(&new_file_ver, dentry->d_inode);
+ error = presto_journal_create(&rec, fset, dentry, &tgt_dir_ver,
+ &new_file_ver,
+ dentry->d_inode->i_mode);
+ }
+
+ presto_debug_fail_blkdev(fset, KML_OPCODE_CREATE | 0x20);
+
+ if ( presto_do_rcvd(info, dentry) )
+ error = presto_write_last_rcvd(&rec, fset, info);
+
+ presto_debug_fail_blkdev(fset, KML_OPCODE_CREATE | 0x30);
+
+ /* add inode dentry */
+ if (fset->fset_cache->cache_filter->o_trops->tr_add_ilookup ) {
+ struct dentry *d;
+ d = fset->fset_cache->cache_filter->o_trops->tr_add_ilookup
+ (dir->d_inode->i_sb->s_root, dentry);
+ }
+
+ EXIT;
+
+ exit_lock:
+ unlock_kernel();
+ presto_trans_commit(fset, handle);
+ exit_pre_lock:
+ presto_release_space(fset->fset_cache, PRESTO_REQHIGH);
+ up(&dir->d_inode->i_zombie);
+ return error;
+}
+
+/* from namei.c */
+static struct dentry *lookup_create(struct nameidata *nd, int is_dir)
+{
+ struct dentry *dentry;
+
+ down(&nd->dentry->d_inode->i_sem);
+ dentry = ERR_PTR(-EEXIST);
+ if (nd->last_type != LAST_NORM)
+ goto fail;
+ dentry = lookup_hash(&nd->last, nd->dentry);
+ if (IS_ERR(dentry))
+ goto fail;
+ if (!is_dir && nd->last.name[nd->last.len] && !dentry->d_inode)
+ goto enoent;
+ return dentry;
+enoent:
+ dput(dentry);
+ dentry = ERR_PTR(-ENOENT);
+fail:
+ return dentry;
+}
+
+int lento_create(const char *name, int mode, struct lento_vfs_context *info)
+{
+ int error;
+ struct nameidata nd;
+ char * pathname;
+ struct dentry *dentry;
+ struct presto_file_set *fset;
+
+ ENTRY;
+ pathname = getname(name);
+ error = PTR_ERR(pathname);
+ if (IS_ERR(pathname)) {
+ EXIT;
+ goto exit;
+ }
+
+ /* this looks up the parent */
+// if (path_init(pathname, LOOKUP_FOLLOW | LOOKUP_POSITIVE, &nd))
+ if (path_init(pathname, LOOKUP_PARENT, &nd))
+ error = path_walk(pathname, &nd);
+ if (error) {
+ EXIT;
+ goto exit;
+ }
+ dentry = lookup_create(&nd, 0);
+ error = PTR_ERR(dentry);
+ if (IS_ERR(dentry)) {
+ EXIT;
+ goto exit_lock;
+ }
+
+ fset = presto_fset(dentry);
+ error = -EINVAL;
+ if ( !fset ) {
+ CERROR("No fileset!\n");
+ EXIT;
+ goto exit_lock;
+ }
+ error = presto_do_create(fset, dentry->d_parent, dentry, (mode&S_IALLUGO)|S_IFREG,
+ info);
+
+ EXIT;
+
+ exit_lock:
+ path_release (&nd);
+ dput(dentry);
+ up(&dentry->d_parent->d_inode->i_sem);
+ putname(pathname);
+exit:
+ return error;
+}
+
+int presto_do_link(struct presto_file_set *fset, struct dentry *old_dentry,
+ struct dentry *dir, struct dentry *new_dentry,
+ struct lento_vfs_context *info)
+{
+ struct rec_info rec;
+ struct inode *inode;
+ int error;
+ struct inode_operations *iops;
+ struct presto_version tgt_dir_ver;
+ struct presto_version new_link_ver;
+ void *handle;
+
+ down(&dir->d_inode->i_zombie);
+ error = presto_reserve_space(fset->fset_cache, PRESTO_REQHIGH);
+ if (error) {
+ EXIT;
+ up(&dir->d_inode->i_zombie);
+ return error;
+ }
+ error = -ENOENT;
+ inode = old_dentry->d_inode;
+ if (!inode)
+ goto exit_lock;
+
+ error = may_create(dir->d_inode, new_dentry);
+ if (error)
+ goto exit_lock;
+
+ error = -EXDEV;
+ if (dir->d_inode->i_dev != inode->i_dev)
+ goto exit_lock;
+
+ /*
+ * A link to an append-only or immutable file cannot be created.
+ */
+ error = -EPERM;
+ if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) {
+ EXIT;
+ goto exit_lock;
+ }
+
+ iops = filter_c2cdiops(fset->fset_cache->cache_filter);
+ if (!iops->link) {
+ EXIT;
+ goto exit_lock;
+ }
+
+
+ presto_getversion(&tgt_dir_ver, dir->d_inode);
+ handle = presto_trans_start(fset, dir->d_inode, KML_OPCODE_LINK);
+ if ( IS_ERR(handle) ) {
+ presto_release_space(fset->fset_cache, PRESTO_REQHIGH);
+ CERROR("presto_do_link: no space for transaction\n");
+ return -ENOSPC;
+ }
+
+ DQUOT_INIT(dir->d_inode);
+ lock_kernel();
+ error = iops->link(old_dentry, dir->d_inode, new_dentry);
+ unlock_kernel();
+ if (error) {
+ EXIT;
+ goto exit_lock;
+ }
+
+ /* link dd data to that of existing dentry */
+ old_dentry->d_op->d_release(new_dentry);
+ if (!presto_d2d(old_dentry))
+ BUG();
+ presto_d2d(old_dentry)->dd_count++;
+
+ new_dentry->d_fsdata = presto_d2d(old_dentry);
+
+ info->flags |= LENTO_FL_TOUCH_PARENT;
+ error = presto_settime(fset, NULL, dir, new_dentry,
+ info, ATTR_CTIME);
+ if (error) {
+ EXIT;
+ goto exit_lock;
+ }
+
+ presto_debug_fail_blkdev(fset, KML_OPCODE_LINK | 0x10);
+ presto_getversion(&new_link_ver, new_dentry->d_inode);
+ if ( presto_do_kml(info, old_dentry) )
+ error = presto_journal_link(&rec, fset, old_dentry, new_dentry,
+ &tgt_dir_ver, &new_link_ver);
+
+ presto_debug_fail_blkdev(fset, KML_OPCODE_LINK | 0x20);
+ if ( presto_do_rcvd(info, old_dentry) )
+ error = presto_write_last_rcvd(&rec, fset, info);
+
+ presto_debug_fail_blkdev(fset, KML_OPCODE_LINK | 0x30);
+ EXIT;
+ presto_trans_commit(fset, handle);
+exit_lock:
+ presto_release_space(fset->fset_cache, PRESTO_REQHIGH);
+ up(&dir->d_inode->i_zombie);
+ return error;
+}
+
+
+int lento_link(const char * oldname, const char * newname,
+ struct lento_vfs_context *info)
+{
+ int error;
+ char * from;
+ char * to;
+ struct presto_file_set *fset;
+
+ from = getname(oldname);
+ if(IS_ERR(from))
+ return PTR_ERR(from);
+ to = getname(newname);
+ error = PTR_ERR(to);
+ if (!IS_ERR(to)) {
+ struct dentry *new_dentry;
+ struct nameidata nd, old_nd;
+
+ error = 0;
+ if (path_init(from, LOOKUP_POSITIVE, &old_nd))
+ error = path_walk(from, &old_nd);
+ if (error)
+ goto exit;
+ if (path_init(to, LOOKUP_PARENT, &nd))
+ error = path_walk(to, &nd);
+ if (error)
+ goto out;
+ error = -EXDEV;
+ if (old_nd.mnt != nd.mnt)
+ goto out;
+ new_dentry = lookup_create(&nd, 0);
+ error = PTR_ERR(new_dentry);
+
+ if (!IS_ERR(new_dentry)) {
+ fset = presto_fset(new_dentry);
+ error = -EINVAL;
+ if ( !fset ) {
+ CERROR("No fileset!\n");
+ EXIT;
+ goto out2;
+ }
+ error = presto_do_link(fset, old_nd.dentry,
+ nd.dentry,
+ new_dentry, info);
+ dput(new_dentry);
+ }
+ out2:
+ up(&nd.dentry->d_inode->i_sem);
+ path_release(&nd);
+ out:
+ path_release(&old_nd);
+ exit:
+ putname(to);
+ }
+ putname(from);
+
+ return error;
+}
+
+int presto_do_unlink(struct presto_file_set *fset, struct dentry *dir,
+ struct dentry *dentry, struct lento_vfs_context *info)
+{
+ struct rec_info rec;
+ struct inode_operations *iops;
+ struct presto_version tgt_dir_ver, old_file_ver;
+ struct izo_rollback_data rb;
+ void *handle;
+ int do_kml = 0, do_rcvd = 0, linkno = 0, error, old_targetlen = 0;
+ char *old_target = NULL;
+
+ ENTRY;
+ down(&dir->d_inode->i_zombie);
+ error = may_delete(dir->d_inode, dentry, 0);
+ if (error) {
+ EXIT;
+ up(&dir->d_inode->i_zombie);
+ return error;
+ }
+
+ error = -EPERM;
+ iops = filter_c2cdiops(fset->fset_cache->cache_filter);
+ if (!iops->unlink) {
+ EXIT;
+ up(&dir->d_inode->i_zombie);
+ return error;
+ }
+
+ error = presto_reserve_space(fset->fset_cache, PRESTO_REQLOW);
+ if (error) {
+ EXIT;
+ up(&dir->d_inode->i_zombie);
+ return error;
+ }
+
+
+ if (presto_d2d(dentry)) {
+ struct presto_dentry_data *dd = presto_d2d(dentry);
+ struct dentry *de = dd->dd_inodentry;
+ if (de && dentry->d_inode->i_nlink == 1) {
+ dd->dd_count--;
+ dd->dd_inodentry = NULL;
+ de->d_fsdata = NULL;
+ atomic_dec(&de->d_inode->i_count);
+ de->d_inode = NULL;
+ dput(de);
+ }
+ }
+
+ presto_getversion(&tgt_dir_ver, dir->d_inode);
+ presto_getversion(&old_file_ver, dentry->d_inode);
+ izo_get_rollback_data(dentry->d_inode, &rb);
+ handle = presto_trans_start(fset, dir->d_inode, KML_OPCODE_UNLINK);
+ if ( IS_ERR(handle) ) {
+ presto_release_space(fset->fset_cache, PRESTO_REQLOW);
+ CERROR("ERROR: presto_do_unlink: no space for transaction. Tell Peter.\n");
+ up(&dir->d_inode->i_zombie);
+ return -ENOSPC;
+ }
+ DQUOT_INIT(dir->d_inode);
+ if (d_mountpoint(dentry))
+ error = -EBUSY;
+ else {
+ lock_kernel();
+ linkno = dentry->d_inode->i_nlink;
+ if (linkno > 1) {
+ dget(dentry);
+ }
+
+ if (S_ISLNK(dentry->d_inode->i_mode)) {
+ mm_segment_t old_fs;
+ struct inode_operations *riops;
+ riops = filter_c2csiops(fset->fset_cache->cache_filter);
+
+ PRESTO_ALLOC(old_target, PATH_MAX);
+ if (old_target == NULL) {
+ error = -ENOMEM;
+ EXIT;
+ goto exit;
+ }
+
+ old_fs = get_fs();
+ set_fs(get_ds());
+
+ if (riops->readlink == NULL)
+ CERROR("InterMezzo %s: no readlink iops.\n",
+ __FUNCTION__);
+ else
+ old_targetlen =
+ riops->readlink(dentry, old_target,
+ PATH_MAX);
+ if (old_targetlen < 0) {
+ CERROR("InterMezzo: readlink failed: %ld\n",
+ PTR_ERR(old_target));
+ PRESTO_FREE(old_target, PATH_MAX);
+ old_target = NULL;
+ old_targetlen = 0;
+ }
+ set_fs(old_fs);
+ }
+
+ do_kml = presto_do_kml(info, dir);
+ do_rcvd = presto_do_rcvd(info, dir);
+ error = iops->unlink(dir->d_inode, dentry);
+ unlock_kernel();
+ if (!error)
+ d_delete(dentry);
+ }
+
+ if (linkno > 1) {
+ /* FIXME: Combine this with the next call? */
+ error = presto_settime(fset, NULL, NULL, dentry,
+ info, ATTR_CTIME);
+ dput(dentry);
+ if (error) {
+ EXIT;
+ goto exit;
+ }
+ }
+
+ error = presto_settime(fset, NULL, NULL, dir,
+ info, ATTR_CTIME | ATTR_MTIME);
+ if (error) {
+ EXIT;
+ goto exit;
+ }
+
+ up(&dir->d_inode->i_zombie);
+ if (error) {
+ EXIT;
+ goto exit;
+ }
+
+ presto_debug_fail_blkdev(fset, KML_OPCODE_UNLINK | 0x10);
+ if ( do_kml )
+ error = presto_journal_unlink(&rec, fset, dir, &tgt_dir_ver,
+ &old_file_ver, &rb, dentry,
+ old_target, old_targetlen);
+ presto_debug_fail_blkdev(fset, KML_OPCODE_UNLINK | 0x20);
+ if ( do_rcvd ) {
+ error = presto_write_last_rcvd(&rec, fset, info);
+ }
+ presto_debug_fail_blkdev(fset, KML_OPCODE_UNLINK | 0x30);
+ EXIT;
+exit:
+ presto_release_space(fset->fset_cache, PRESTO_REQLOW);
+ presto_trans_commit(fset, handle);
+ if (old_target != NULL)
+ PRESTO_FREE(old_target, PATH_MAX);
+ return error;
+}
+
+
+int lento_unlink(const char *pathname, struct lento_vfs_context *info)
+{
+ int error = 0;
+ char * name;
+ struct dentry *dentry;
+ struct nameidata nd;
+ struct presto_file_set *fset;
+
+ ENTRY;
+
+ name = getname(pathname);
+ if(IS_ERR(name))
+ return PTR_ERR(name);
+
+ if (path_init(name, LOOKUP_PARENT, &nd))
+ error = path_walk(name, &nd);
+ if (error)
+ goto exit;
+ error = -EISDIR;
+ if (nd.last_type != LAST_NORM)
+ goto exit1;
+ down(&nd.dentry->d_inode->i_sem);
+ dentry = lookup_hash(&nd.last, nd.dentry);
+ error = PTR_ERR(dentry);
+ if (!IS_ERR(dentry)) {
+ fset = presto_fset(dentry);
+ error = -EINVAL;
+ if ( !fset ) {
+ CERROR("No fileset!\n");
+ EXIT;
+ goto exit2;
+ }
+ /* Why not before? Because we want correct error value */
+ if (nd.last.name[nd.last.len])
+ goto slashes;
+ error = presto_do_unlink(fset, nd.dentry, dentry, info);
+ exit2:
+ EXIT;
+ dput(dentry);
+ }
+ up(&nd.dentry->d_inode->i_sem);
+exit1:
+ path_release(&nd);
+exit:
+ putname(name);
+
+ return error;
+
+slashes:
+ error = !dentry->d_inode ? -ENOENT :
+ S_ISDIR(dentry->d_inode->i_mode) ? -EISDIR : -ENOTDIR;
+ goto exit2;
+}
+
+int presto_do_symlink(struct presto_file_set *fset, struct dentry *dir,
+ struct dentry *dentry, const char *oldname,
+ struct lento_vfs_context *info)
+{
+ struct rec_info rec;
+ int error;
+ struct presto_version tgt_dir_ver, new_link_ver;
+ struct inode_operations *iops;
+ void *handle;
+
+ ENTRY;
+ down(&dir->d_inode->i_zombie);
+ /* record + max path len + space to free */
+ error = presto_reserve_space(fset->fset_cache, PRESTO_REQHIGH + 4096);
+ if (error) {
+ EXIT;
+ up(&dir->d_inode->i_zombie);
+ return error;
+ }
+
+ error = may_create(dir->d_inode, dentry);
+ if (error) {
+ EXIT;
+ goto exit_lock;
+ }
+
+ error = -EPERM;
+ iops = filter_c2cdiops(fset->fset_cache->cache_filter);
+ if (!iops->symlink) {
+ EXIT;
+ goto exit_lock;
+ }
+
+ presto_getversion(&tgt_dir_ver, dir->d_inode);
+ handle = presto_trans_start(fset, dir->d_inode, KML_OPCODE_SYMLINK);
+ if ( IS_ERR(handle) ) {
+ presto_release_space(fset->fset_cache, PRESTO_REQHIGH + 4096);
+ CERROR("ERROR: presto_do_symlink: no space for transaction. Tell Peter.\n");
+ EXIT;
+ up(&dir->d_inode->i_zombie);
+ return -ENOSPC;
+ }
+ DQUOT_INIT(dir->d_inode);
+ lock_kernel();
+ error = iops->symlink(dir->d_inode, dentry, oldname);
+ if (error) {
+ EXIT;
+ goto exit;
+ }
+
+ if (dentry->d_inode) {
+ struct presto_cache *cache = fset->fset_cache;
+
+ presto_set_ops(dentry->d_inode, cache->cache_filter);
+
+ filter_setup_dentry_ops(cache->cache_filter, dentry->d_op,
+ &presto_dentry_ops);
+ dentry->d_op = filter_c2udops(cache->cache_filter);
+ /* XXX ? Cache state ? if Lento creates a symlink */
+ if ( ISLENTO(presto_c2m(cache)) ) {
+ presto_set(dentry, PRESTO_ATTR);
+ } else {
+ presto_set(dentry, PRESTO_ATTR | PRESTO_DATA);
+ }
+ }
+
+ info->flags |= LENTO_FL_TOUCH_PARENT;
+ error = presto_settime(fset, NULL, dir, dentry,
+ info, ATTR_CTIME | ATTR_MTIME);
+ if (error) {
+ EXIT;
+ goto exit;
+ }
+
+ presto_debug_fail_blkdev(fset, KML_OPCODE_SYMLINK | 0x10);
+ presto_getversion(&new_link_ver, dentry->d_inode);
+ if ( presto_do_kml(info, dentry) )
+ error = presto_journal_symlink(&rec, fset, dentry, oldname,
+ &tgt_dir_ver, &new_link_ver);
+
+ presto_debug_fail_blkdev(fset, KML_OPCODE_SYMLINK | 0x20);
+ if ( presto_do_rcvd(info, dentry) )
+ error = presto_write_last_rcvd(&rec, fset, info);
+
+ presto_debug_fail_blkdev(fset, KML_OPCODE_SYMLINK | 0x30);
+ EXIT;
+exit:
+ unlock_kernel();
+ presto_trans_commit(fset, handle);
+ exit_lock:
+ presto_release_space(fset->fset_cache, PRESTO_REQHIGH + 4096);
+ up(&dir->d_inode->i_zombie);
+ return error;
+}
+
+int lento_symlink(const char *oldname, const char *newname,
+ struct lento_vfs_context *info)
+{
+ int error;
+ char *from;
+ char *to;
+ struct dentry *dentry;
+ struct presto_file_set *fset;
+ struct nameidata nd;
+
+ ENTRY;
+ lock_kernel();
+ from = getname(oldname);
+ error = PTR_ERR(from);
+ if (IS_ERR(from)) {
+ EXIT;
+ goto exit;
+ }
+
+ to = getname(newname);
+ error = PTR_ERR(to);
+ if (IS_ERR(to)) {
+ EXIT;
+ goto exit_from;
+ }
+
+ if (path_init(to, LOOKUP_PARENT, &nd))
+ error = path_walk(to, &nd);
+ if (error) {
+ EXIT;
+ goto exit_to;
+ }
+
+ dentry = lookup_create(&nd, 0);
+ error = PTR_ERR(dentry);
+ if (IS_ERR(dentry)) {
+ path_release(&nd);
+ EXIT;
+ goto exit_to;
+ }
+
+ fset = presto_fset(dentry);
+ error = -EINVAL;
+ if ( !fset ) {
+ CERROR("No fileset!\n");
+ path_release(&nd);
+ EXIT;
+ goto exit_lock;
+ }
+ error = presto_do_symlink(fset, nd.dentry,
+ dentry, from, info);
+ path_release(&nd);
+ EXIT;
+ exit_lock:
+ up(&nd.dentry->d_inode->i_sem);
+ dput(dentry);
+ exit_to:
+ putname(to);
+ exit_from:
+ putname(from);
+ exit:
+ unlock_kernel();
+ return error;
+}
+
+int presto_do_mkdir(struct presto_file_set *fset, struct dentry *dir,
+ struct dentry *dentry, int mode,
+ struct lento_vfs_context *info)
+{
+ struct rec_info rec;
+ int error;
+ struct presto_version tgt_dir_ver, new_dir_ver;
+ void *handle;
+
+ ENTRY;
+ down(&dir->d_inode->i_zombie);
+
+ /* one journal record + directory block + room for removals*/
+ error = presto_reserve_space(fset->fset_cache, PRESTO_REQHIGH + 4096);
+ if (error) {
+ EXIT;
+ up(&dir->d_inode->i_zombie);
+ return error;
+ }
+
+ error = may_create(dir->d_inode, dentry);
+ if (error) {
+ EXIT;
+ goto exit_lock;
+ }
+
+ error = -EPERM;
+ if (!filter_c2cdiops(fset->fset_cache->cache_filter)->mkdir) {
+ EXIT;
+ goto exit_lock;
+ }
+
+ error = -ENOSPC;
+ presto_getversion(&tgt_dir_ver, dir->d_inode);
+ handle = presto_trans_start(fset, dir->d_inode, KML_OPCODE_MKDIR);
+ if ( IS_ERR(handle) ) {
+ presto_release_space(fset->fset_cache, PRESTO_REQHIGH + 4096);
+ CERROR("presto_do_mkdir: no space for transaction\n");
+ goto exit_lock;
+ }
+
+ DQUOT_INIT(dir->d_inode);
+ mode &= (S_IRWXUGO|S_ISVTX);
+ lock_kernel();
+ error = filter_c2cdiops(fset->fset_cache->cache_filter)->mkdir(dir->d_inode, dentry, mode);
+ if (error) {
+ EXIT;
+ goto exit;
+ }
+
+ if ( dentry->d_inode && !error) {
+ struct presto_cache *cache = fset->fset_cache;
+
+ presto_set_ops(dentry->d_inode, cache->cache_filter);
+
+ filter_setup_dentry_ops(cache->cache_filter,
+ dentry->d_op,
+ &presto_dentry_ops);
+ dentry->d_op = filter_c2udops(cache->cache_filter);
+ /* if Lento does this, we won't have data */
+ if ( ISLENTO(presto_c2m(cache)) ) {
+ presto_set(dentry, PRESTO_ATTR);
+ } else {
+ presto_set(dentry, PRESTO_ATTR | PRESTO_DATA);
+ }
+ }
+
+ info->flags |= LENTO_FL_TOUCH_PARENT;
+ error = presto_settime(fset, NULL, dir, dentry,
+ info, ATTR_CTIME | ATTR_MTIME);
+ if (error) {
+ EXIT;
+ goto exit;
+ }
+
+ presto_debug_fail_blkdev(fset, KML_OPCODE_MKDIR | 0x10);
+ presto_getversion(&new_dir_ver, dentry->d_inode);
+ if ( presto_do_kml(info, dir) )
+ error = presto_journal_mkdir(&rec, fset, dentry, &tgt_dir_ver,
+ &new_dir_ver,
+ dentry->d_inode->i_mode);
+
+ presto_debug_fail_blkdev(fset, KML_OPCODE_MKDIR | 0x20);
+ if ( presto_do_rcvd(info, dentry) )
+ error = presto_write_last_rcvd(&rec, fset, info);
+
+ presto_debug_fail_blkdev(fset, KML_OPCODE_MKDIR | 0x30);
+ EXIT;
+exit:
+ unlock_kernel();
+ presto_trans_commit(fset, handle);
+ exit_lock:
+ presto_release_space(fset->fset_cache, PRESTO_REQHIGH + 4096);
+ up(&dir->d_inode->i_zombie);
+ return error;
+}
+
+/*
+ * Look out: this function may change a normal dentry
+ * into a directory dentry (different size)..
+ */
+int lento_mkdir(const char *name, int mode, struct lento_vfs_context *info)
+{
+ int error;
+ char *pathname;
+ struct dentry *dentry;
+ struct presto_file_set *fset;
+ struct nameidata nd;
+
+ ENTRY;
+ CDEBUG(D_PIOCTL, "name: %s, mode %o, offset %d, recno %d, flags %x\n",
+ name, mode, info->slot_offset, info->recno, info->flags);
+ pathname = getname(name);
+ error = PTR_ERR(pathname);
+ if (IS_ERR(pathname)) {
+ EXIT;
+ return error;
+ }
+
+ if (path_init(pathname, LOOKUP_PARENT, &nd))
+ error = path_walk(pathname, &nd);
+ if (error)
+ goto out_name;
+
+ dentry = lookup_create(&nd, 1);
+ error = PTR_ERR(dentry);
+ if (!IS_ERR(dentry)) {
+ fset = presto_fset(dentry);
+ error = -EINVAL;
+ if (!fset) {
+ CERROR("No fileset!\n");
+ EXIT;
+ goto out_dput;
+ }
+
+ error = presto_do_mkdir(fset, nd.dentry, dentry,
+ mode & S_IALLUGO, info);
+out_dput:
+ dput(dentry);
+ }
+ up(&nd.dentry->d_inode->i_sem);
+ path_release(&nd);
+out_name:
+ EXIT;
+ putname(pathname);
+ CDEBUG(D_PIOCTL, "error: %d\n", error);
+ return error;
+}
+
+static void d_unhash(struct dentry *dentry)
+{
+ dget(dentry);
+ switch (atomic_read(&dentry->d_count)) {
+ default:
+ shrink_dcache_parent(dentry);
+ if (atomic_read(&dentry->d_count) != 2)
+ break;
+ case 2:
+ d_drop(dentry);
+ }
+}
+
+int presto_do_rmdir(struct presto_file_set *fset, struct dentry *dir,
+ struct dentry *dentry, struct lento_vfs_context *info)
+{
+ struct rec_info rec;
+ int error;
+ struct presto_version tgt_dir_ver, old_dir_ver;
+ struct izo_rollback_data rb;
+ struct inode_operations *iops;
+ void *handle;
+ int do_kml, do_rcvd;
+ int size;
+
+ ENTRY;
+ error = may_delete(dir->d_inode, dentry, 1);
+ if (error)
+ return error;
+
+ error = -EPERM;
+ iops = filter_c2cdiops(fset->fset_cache->cache_filter);
+ if (!iops->rmdir) {
+ EXIT;
+ return error;
+ }
+
+ size = PRESTO_REQHIGH - dentry->d_inode->i_size;
+ error = presto_reserve_space(fset->fset_cache, size);
+ if (error) {
+ EXIT;
+ return error;
+ }
+
+ presto_getversion(&tgt_dir_ver, dir->d_inode);
+ presto_getversion(&old_dir_ver, dentry->d_inode);
+ izo_get_rollback_data(dentry->d_inode, &rb);
+ handle = presto_trans_start(fset, dir->d_inode, KML_OPCODE_RMDIR);
+ if ( IS_ERR(handle) ) {
+ presto_release_space(fset->fset_cache, size);
+ CERROR("ERROR: presto_do_rmdir: no space for transaction. Tell Peter.\n");
+ return -ENOSPC;
+ }
+
+ DQUOT_INIT(dir->d_inode);
+
+ do_kml = presto_do_kml(info, dir);
+ do_rcvd = presto_do_rcvd(info, dir);
+
+ double_down(&dir->d_inode->i_zombie, &dentry->d_inode->i_zombie);
+ d_unhash(dentry);
+ if (IS_DEADDIR(dir->d_inode))
+ error = -ENOENT;
+ else if (d_mountpoint(dentry)) {
+ CERROR("foo: d_mountpoint(dentry): ino %ld\n",
+ dentry->d_inode->i_ino);
+ error = -EBUSY;
+ } else {
+ lock_kernel();
+ error = iops->rmdir(dir->d_inode, dentry);
+ unlock_kernel();
+ if (!error) {
+ dentry->d_inode->i_flags |= S_DEAD;
+ error = presto_settime(fset, NULL, NULL, dir, info,
+ ATTR_CTIME | ATTR_MTIME);
+ }
+ }
+ double_up(&dir->d_inode->i_zombie, &dentry->d_inode->i_zombie);
+ if (!error)
+ d_delete(dentry);
+ dput(dentry);
+
+ presto_debug_fail_blkdev(fset, KML_OPCODE_RMDIR | 0x10);
+ if ( !error && do_kml )
+ error = presto_journal_rmdir(&rec, fset, dir, &tgt_dir_ver,
+ &old_dir_ver, &rb,
+ dentry->d_name.len,
+ dentry->d_name.name);
+
+ presto_debug_fail_blkdev(fset, KML_OPCODE_RMDIR | 0x20);
+ if ( !error && do_rcvd )
+ error = presto_write_last_rcvd(&rec, fset, info);
+
+ presto_debug_fail_blkdev(fset, KML_OPCODE_RMDIR | 0x30);
+ EXIT;
+
+ presto_trans_commit(fset, handle);
+ presto_release_space(fset->fset_cache, size);
+ return error;
+}
+
+int lento_rmdir(const char *pathname, struct lento_vfs_context *info)
+{
+ int error = 0;
+ char * name;
+ struct dentry *dentry;
+ struct presto_file_set *fset;
+ struct nameidata nd;
+
+ ENTRY;
+ name = getname(pathname);
+ if(IS_ERR(name)) {
+ EXIT;
+ return PTR_ERR(name);
+ }
+
+ if (path_init(name, LOOKUP_PARENT, &nd))
+ error = path_walk(name, &nd);
+ if (error) {
+ EXIT;
+ goto exit;
+ }
+ switch(nd.last_type) {
+ case LAST_DOTDOT:
+ error = -ENOTEMPTY;
+ EXIT;
+ goto exit1;
+ case LAST_ROOT:
+ case LAST_DOT:
+ error = -EBUSY;
+ EXIT;
+ goto exit1;
+ }
+ down(&nd.dentry->d_inode->i_sem);
+ dentry = lookup_hash(&nd.last, nd.dentry);
+ error = PTR_ERR(dentry);
+ if (!IS_ERR(dentry)) {
+ fset = presto_fset(dentry);
+ error = -EINVAL;
+ if ( !fset ) {
+ CERROR("No fileset!\n");
+ EXIT;
+ goto exit_put;
+ }
+ error = presto_do_rmdir(fset, nd.dentry, dentry, info);
+ exit_put:
+ dput(dentry);
+ }
+ up(&nd.dentry->d_inode->i_sem);
+exit1:
+ path_release(&nd);
+exit:
+ putname(name);
+ EXIT;
+ return error;
+}
+
+int presto_do_mknod(struct presto_file_set *fset, struct dentry *dir,
+ struct dentry *dentry, int mode, dev_t dev,
+ struct lento_vfs_context *info)
+{
+ struct rec_info rec;
+ int error = -EPERM;
+ struct presto_version tgt_dir_ver, new_node_ver;
+ struct inode_operations *iops;
+ void *handle;
+
+ ENTRY;
+
+ down(&dir->d_inode->i_zombie);
+ /* one KML entry */
+ error = presto_reserve_space(fset->fset_cache, PRESTO_REQHIGH);
+ if (error) {
+ EXIT;
+ up(&dir->d_inode->i_zombie);
+ return error;
+ }
+
+ if ((S_ISCHR(mode) || S_ISBLK(mode)) && !capable(CAP_MKNOD)) {
+ EXIT;
+ goto exit_lock;
+ }
+
+ error = may_create(dir->d_inode, dentry);
+ if (error) {
+ EXIT;
+ goto exit_lock;
+ }
+
+ error = -EPERM;
+ iops = filter_c2cdiops(fset->fset_cache->cache_filter);
+ if (!iops->mknod) {
+ EXIT;
+ goto exit_lock;
+ }
+
+ DQUOT_INIT(dir->d_inode);
+ lock_kernel();
+
+ error = -ENOSPC;
+ presto_getversion(&tgt_dir_ver, dir->d_inode);
+ handle = presto_trans_start(fset, dir->d_inode, KML_OPCODE_MKNOD);
+ if ( IS_ERR(handle) ) {
+ presto_release_space(fset->fset_cache, PRESTO_REQHIGH);
+ CERROR("presto_do_mknod: no space for transaction\n");
+ goto exit_lock2;
+ }
+
+ error = iops->mknod(dir->d_inode, dentry, mode, dev);
+ if (error) {
+ EXIT;
+ goto exit_commit;
+ }
+ if ( dentry->d_inode) {
+ struct presto_cache *cache = fset->fset_cache;
+
+ presto_set_ops(dentry->d_inode, cache->cache_filter);
+
+ filter_setup_dentry_ops(cache->cache_filter, dentry->d_op,
+ &presto_dentry_ops);
+ dentry->d_op = filter_c2udops(cache->cache_filter);
+
+ /* if Lento does this, we won't have data */
+ if ( ISLENTO(presto_c2m(cache)) ) {
+ presto_set(dentry, PRESTO_ATTR);
+ } else {
+ presto_set(dentry, PRESTO_ATTR | PRESTO_DATA);
+ }
+ }
+
+ error = presto_settime(fset, NULL, NULL, dir,
+ info, ATTR_MTIME);
+ if (error) {
+ EXIT;
+ }
+ error = presto_settime(fset, NULL, NULL, dentry,
+ info, ATTR_CTIME | ATTR_MTIME);
+ if (error) {
+ EXIT;
+ }
+
+ presto_debug_fail_blkdev(fset, KML_OPCODE_MKNOD | 0x10);
+ presto_getversion(&new_node_ver, dentry->d_inode);
+ if ( presto_do_kml(info, dentry) )
+ error = presto_journal_mknod(&rec, fset, dentry, &tgt_dir_ver,
+ &new_node_ver,
+ dentry->d_inode->i_mode,
+ MAJOR(dev), MINOR(dev) );
+
+ presto_debug_fail_blkdev(fset, KML_OPCODE_MKNOD | 0x20);
+ if ( presto_do_rcvd(info, dentry) )
+ error = presto_write_last_rcvd(&rec, fset, info);
+
+ presto_debug_fail_blkdev(fset, KML_OPCODE_MKNOD | 0x30);
+ EXIT;
+ exit_commit:
+ presto_trans_commit(fset, handle);
+ exit_lock2:
+ unlock_kernel();
+ exit_lock:
+ presto_release_space(fset->fset_cache, PRESTO_REQHIGH);
+ up(&dir->d_inode->i_zombie);
+ return error;
+}
+
+int lento_mknod(const char *filename, int mode, dev_t dev,
+ struct lento_vfs_context *info)
+{
+ int error = 0;
+ char * tmp;
+ struct dentry * dentry;
+ struct nameidata nd;
+ struct presto_file_set *fset;
+
+ ENTRY;
+
+ if (S_ISDIR(mode))
+ return -EPERM;
+ tmp = getname(filename);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+
+ if (path_init(tmp, LOOKUP_PARENT, &nd))
+ error = path_walk(tmp, &nd);
+ if (error)
+ goto out;
+ dentry = lookup_create(&nd, 0);
+ error = PTR_ERR(dentry);
+ if (!IS_ERR(dentry)) {
+ fset = presto_fset(dentry);
+ error = -EINVAL;
+ if ( !fset ) {
+ CERROR("No fileset!\n");
+ EXIT;
+ goto exit_put;
+ }
+ switch (mode & S_IFMT) {
+ case 0: case S_IFREG:
+ error = -EOPNOTSUPP;
+ break;
+ case S_IFCHR: case S_IFBLK: case S_IFIFO: case S_IFSOCK:
+ error = presto_do_mknod(fset, nd.dentry, dentry,
+ mode, dev, info);
+ break;
+ case S_IFDIR:
+ error = -EPERM;
+ break;
+ default:
+ error = -EINVAL;
+ }
+ exit_put:
+ dput(dentry);
+ }
+ up(&nd.dentry->d_inode->i_sem);
+ path_release(&nd);
+out:
+ putname(tmp);
+
+ return error;
+}
+
+int do_rename(struct presto_file_set *fset,
+ struct dentry *old_parent, struct dentry *old_dentry,
+ struct dentry *new_parent, struct dentry *new_dentry,
+ struct lento_vfs_context *info)
+{
+ struct rec_info rec;
+ int error;
+ struct inode_operations *iops;
+ struct presto_version src_dir_ver, tgt_dir_ver;
+ void *handle;
+ int new_inode_unlink = 0;
+ struct inode *old_dir = old_parent->d_inode;
+ struct inode *new_dir = new_parent->d_inode;
+
+ ENTRY;
+ presto_getversion(&src_dir_ver, old_dir);
+ presto_getversion(&tgt_dir_ver, new_dir);
+
+ error = -EPERM;
+ iops = filter_c2cdiops(fset->fset_cache->cache_filter);
+ if (!iops || !iops->rename) {
+ EXIT;
+ return error;
+ }
+
+ error = presto_reserve_space(fset->fset_cache, PRESTO_REQHIGH);
+ if (error) {
+ EXIT;
+ return error;
+ }
+ handle = presto_trans_start(fset, old_dir, KML_OPCODE_RENAME);
+ if ( IS_ERR(handle) ) {
+ presto_release_space(fset->fset_cache, PRESTO_REQHIGH);
+ CERROR("presto_do_rename: no space for transaction\n");
+ return -ENOSPC;
+ }
+ if (new_dentry->d_inode && new_dentry->d_inode->i_nlink > 1) {
+ dget(new_dentry);
+ new_inode_unlink = 1;
+ }
+
+ error = iops->rename(old_dir, old_dentry, new_dir, new_dentry);
+
+ if (error) {
+ EXIT;
+ goto exit;
+ }
+
+ if (new_inode_unlink) {
+ error = presto_settime(fset, NULL, NULL, old_dentry,
+ info, ATTR_CTIME);
+ dput(old_dentry);
+ if (error) {
+ EXIT;
+ goto exit;
+ }
+ }
+ info->flags |= LENTO_FL_TOUCH_PARENT;
+ error = presto_settime(fset, NULL, new_parent, old_parent,
+ info, ATTR_CTIME | ATTR_MTIME);
+ if (error) {
+ EXIT;
+ goto exit;
+ }
+
+ /* XXX make a distinction between cross file set
+ * and intra file set renames here
+ */
+ presto_debug_fail_blkdev(fset, KML_OPCODE_RENAME | 0x10);
+ if ( presto_do_kml(info, old_dentry) )
+ error = presto_journal_rename(&rec, fset, old_dentry,
+ new_dentry,
+ &src_dir_ver, &tgt_dir_ver);
+
+ presto_debug_fail_blkdev(fset, KML_OPCODE_RENAME | 0x20);
+
+ if ( presto_do_rcvd(info, old_dentry) )
+ error = presto_write_last_rcvd(&rec, fset, info);
+
+ presto_debug_fail_blkdev(fset, KML_OPCODE_RENAME | 0x30);
+ EXIT;
+exit:
+ presto_trans_commit(fset, handle);
+ presto_release_space(fset->fset_cache, PRESTO_REQHIGH);
+ return error;
+}
+
+static
+int presto_rename_dir(struct presto_file_set *fset, struct dentry *old_parent,
+ struct dentry *old_dentry, struct dentry *new_parent,
+ struct dentry *new_dentry, struct lento_vfs_context *info)
+{
+ int error;
+ struct inode *target;
+ struct inode *old_dir = old_parent->d_inode;
+ struct inode *new_dir = new_parent->d_inode;
+
+ if (old_dentry->d_inode == new_dentry->d_inode)
+ return 0;
+
+ error = may_delete(old_dir, old_dentry, 1);
+ if (error)
+ return error;
+
+ if (new_dir->i_dev != old_dir->i_dev)
+ return -EXDEV;
+
+ if (!new_dentry->d_inode)
+ error = may_create(new_dir, new_dentry);
+ else
+ error = may_delete(new_dir, new_dentry, 1);
+ if (error)
+ return error;
+
+ if (!old_dir->i_op || !old_dir->i_op->rename)
+ return -EPERM;
+
+ /*
+ * If we are going to change the parent - check write permissions,
+ * we'll need to flip '..'.
+ */
+ if (new_dir != old_dir) {
+ error = permission(old_dentry->d_inode, MAY_WRITE);
+ }
+ if (error)
+ return error;
+
+ DQUOT_INIT(old_dir);
+ DQUOT_INIT(new_dir);
+ down(&old_dir->i_sb->s_vfs_rename_sem);
+ error = -EINVAL;
+ if (is_subdir(new_dentry, old_dentry))
+ goto out_unlock;
+ target = new_dentry->d_inode;
+ if (target) { /* Hastur! Hastur! Hastur! */
+ triple_down(&old_dir->i_zombie,
+ &new_dir->i_zombie,
+ &target->i_zombie);
+ d_unhash(new_dentry);
+ } else
+ double_down(&old_dir->i_zombie,
+ &new_dir->i_zombie);
+ if (IS_DEADDIR(old_dir)||IS_DEADDIR(new_dir))
+ error = -ENOENT;
+ else if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry))
+ error = -EBUSY;
+ else
+ error = do_rename(fset, old_parent, old_dentry,
+ new_parent, new_dentry, info);
+ if (target) {
+ if (!error)
+ target->i_flags |= S_DEAD;
+ triple_up(&old_dir->i_zombie,
+ &new_dir->i_zombie,
+ &target->i_zombie);
+ if (d_unhashed(new_dentry))
+ d_rehash(new_dentry);
+ dput(new_dentry);
+ } else
+ double_up(&old_dir->i_zombie,
+ &new_dir->i_zombie);
+
+ if (!error)
+ d_move(old_dentry,new_dentry);
+out_unlock:
+ up(&old_dir->i_sb->s_vfs_rename_sem);
+ return error;
+}
+
+static
+int presto_rename_other(struct presto_file_set *fset, struct dentry *old_parent,
+ struct dentry *old_dentry, struct dentry *new_parent,
+ struct dentry *new_dentry, struct lento_vfs_context *info)
+{
+ struct inode *old_dir = old_parent->d_inode;
+ struct inode *new_dir = new_parent->d_inode;
+ int error;
+
+ if (old_dentry->d_inode == new_dentry->d_inode)
+ return 0;
+
+ error = may_delete(old_dir, old_dentry, 0);
+ if (error)
+ return error;
+
+ if (new_dir->i_dev != old_dir->i_dev)
+ return -EXDEV;
+
+ if (!new_dentry->d_inode)
+ error = may_create(new_dir, new_dentry);
+ else
+ error = may_delete(new_dir, new_dentry, 0);
+ if (error)
+ return error;
+
+ if (!old_dir->i_op || !old_dir->i_op->rename)
+ return -EPERM;
+
+ DQUOT_INIT(old_dir);
+ DQUOT_INIT(new_dir);
+ double_down(&old_dir->i_zombie, &new_dir->i_zombie);
+ if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry))
+ error = -EBUSY;
+ else
+ error = do_rename(fset, old_parent, old_dentry,
+ new_parent, new_dentry, info);
+ double_up(&old_dir->i_zombie, &new_dir->i_zombie);
+ if (error)
+ return error;
+ /* The following d_move() should become unconditional */
+ if (!(old_dir->i_sb->s_type->fs_flags & FS_ODD_RENAME)) {
+ d_move(old_dentry, new_dentry);
+ }
+ return 0;
+}
+
+int presto_do_rename(struct presto_file_set *fset,
+ struct dentry *old_parent, struct dentry *old_dentry,
+ struct dentry *new_parent, struct dentry *new_dentry,
+ struct lento_vfs_context *info)
+{
+ if (S_ISDIR(old_dentry->d_inode->i_mode))
+ return presto_rename_dir(fset, old_parent,old_dentry,new_parent,
+ new_dentry, info);
+ else
+ return presto_rename_other(fset, old_parent, old_dentry,
+ new_parent,new_dentry, info);
+}
+
+
+int lento_do_rename(const char *oldname, const char *newname,
+ struct lento_vfs_context *info)
+{
+ int error = 0;
+ struct dentry * old_dir, * new_dir;
+ struct dentry * old_dentry, *new_dentry;
+ struct nameidata oldnd, newnd;
+ struct presto_file_set *fset;
+
+ ENTRY;
+
+ if (path_init(oldname, LOOKUP_PARENT, &oldnd))
+ error = path_walk(oldname, &oldnd);
+
+ if (error)
+ goto exit;
+
+ if (path_init(newname, LOOKUP_PARENT, &newnd))
+ error = path_walk(newname, &newnd);
+ if (error)
+ goto exit1;
+
+ error = -EXDEV;
+ if (oldnd.mnt != newnd.mnt)
+ goto exit2;
+
+ old_dir = oldnd.dentry;
+ error = -EBUSY;
+ if (oldnd.last_type != LAST_NORM)
+ goto exit2;
+
+ new_dir = newnd.dentry;
+ if (newnd.last_type != LAST_NORM)
+ goto exit2;
+
+ double_lock(new_dir, old_dir);
+
+ old_dentry = lookup_hash(&oldnd.last, old_dir);
+ error = PTR_ERR(old_dentry);
+ if (IS_ERR(old_dentry))
+ goto exit3;
+ /* source must exist */
+ error = -ENOENT;
+ if (!old_dentry->d_inode)
+ goto exit4;
+ fset = presto_fset(old_dentry);
+ error = -EINVAL;
+ if ( !fset ) {
+ CERROR("No fileset!\n");
+ EXIT;
+ goto exit4;
+ }
+ /* unless the source is a directory trailing slashes give -ENOTDIR */
+ if (!S_ISDIR(old_dentry->d_inode->i_mode)) {
+ error = -ENOTDIR;
+ if (oldnd.last.name[oldnd.last.len])
+ goto exit4;
+ if (newnd.last.name[newnd.last.len])
+ goto exit4;
+ }
+ new_dentry = lookup_hash(&newnd.last, new_dir);
+ error = PTR_ERR(new_dentry);
+ if (IS_ERR(new_dentry))
+ goto exit4;
+
+ lock_kernel();
+ error = presto_do_rename(fset, old_dir, old_dentry,
+ new_dir, new_dentry, info);
+ unlock_kernel();
+
+ dput(new_dentry);
+exit4:
+ dput(old_dentry);
+exit3:
+ double_up(&new_dir->d_inode->i_sem, &old_dir->d_inode->i_sem);
+exit2:
+ path_release(&newnd);
+exit1:
+ path_release(&oldnd);
+exit:
+ return error;
+}
+
+int lento_rename(const char * oldname, const char * newname,
+ struct lento_vfs_context *info)
+{
+ int error;
+ char * from;
+ char * to;
+
+ from = getname(oldname);
+ if(IS_ERR(from))
+ return PTR_ERR(from);
+ to = getname(newname);
+ error = PTR_ERR(to);
+ if (!IS_ERR(to)) {
+ error = lento_do_rename(from,to, info);
+ putname(to);
+ }
+ putname(from);
+ return error;
+}
+
+struct dentry *presto_iopen(struct dentry *dentry,
+ ino_t ino, unsigned int generation)
+{
+ struct presto_file_set *fset;
+ char name[48];
+ int error;
+
+ ENTRY;
+ /* see if we already have the dentry we want */
+ if (dentry->d_inode && dentry->d_inode->i_ino == ino &&
+ dentry->d_inode->i_generation == generation) {
+ EXIT;
+ return dentry;
+ }
+
+ /* Make sure we have a cache beneath us. We should always find at
+ * least one dentry inside the cache (if it exists), otherwise not
+ * even the cache root exists, or we passed in a bad name.
+ */
+ fset = presto_fset(dentry);
+ error = -EINVAL;
+ if (!fset) {
+ CERROR("No fileset for %*s!\n",
+ dentry->d_name.len, dentry->d_name.name);
+ EXIT;
+ dput(dentry);
+ return ERR_PTR(error);
+ }
+ dput(dentry);
+
+ sprintf(name, "%s%#lx%c%#x",
+ PRESTO_ILOOKUP_MAGIC, ino, PRESTO_ILOOKUP_SEP, generation);
+ CDEBUG(D_PIOCTL, "opening %ld by number (as %s)\n", ino, name);
+ return lookup_one_len(name, fset->fset_dentry, strlen(name));
+}
+
+static struct file *presto_filp_dopen(struct dentry *dentry, int flags)
+{
+ struct file *f;
+ struct inode *inode;
+ int flag, error;
+
+ ENTRY;
+ error = -ENFILE;
+ f = get_empty_filp();
+ if (!f) {
+ CDEBUG(D_PIOCTL, "error getting file pointer\n");
+ EXIT;
+ goto out;
+ }
+ f->f_flags = flag = flags;
+ f->f_mode = (flag+1) & O_ACCMODE;
+ inode = dentry->d_inode;
+ if (f->f_mode & FMODE_WRITE) {
+ error = get_write_access(inode);
+ if (error) {
+ CDEBUG(D_PIOCTL, "error getting write access\n");
+ EXIT; goto cleanup_file;
+ }
+ }
+
+ f->f_dentry = dentry;
+ f->f_pos = 0;
+ f->f_reada = 0;
+ f->f_op = NULL;
+ if (inode->i_op)
+ /* XXX should we set to presto ops, or leave at cache ops? */
+ f->f_op = inode->i_fop;
+ if (f->f_op && f->f_op->open) {
+ error = f->f_op->open(inode, f);
+ if (error) {
+ CDEBUG(D_PIOCTL, "error calling cache 'open'\n");
+ EXIT;
+ goto cleanup_all;
+ }
+ }
+ f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
+
+ return f;
+
+cleanup_all:
+ if (f->f_mode & FMODE_WRITE)
+ put_write_access(inode);
+cleanup_file:
+ put_filp(f);
+out:
+ return ERR_PTR(error);
+}
+
+
+/* Open an inode by number. We pass in the cache root name (or a subdirectory
+ * from the cache that is guaranteed to exist) to be able to access the cache.
+ */
+int lento_iopen(const char *name, ino_t ino, unsigned int generation,
+ int flags)
+{
+ char * tmp;
+ struct dentry *dentry;
+ struct nameidata nd;
+ int fd;
+ int error;
+
+ ENTRY;
+ CDEBUG(D_PIOCTL,
+ "open %s:inode %#lx (%ld), generation %x (%d), flags %d \n",
+ name, ino, ino, generation, generation, flags);
+ /* We don't allow creation of files by number only, as it would
+ * lead to a dangling files not in any directory. We could also
+ * just turn off the flag and ignore it.
+ */
+ if (flags & O_CREAT) {
+ CERROR("%s: create file by inode number (%ld) not allowed\n",
+ __FUNCTION__, ino);
+ EXIT;
+ return -EACCES;
+ }
+
+ tmp = getname(name);
+ if (IS_ERR(tmp)) {
+ EXIT;
+ return PTR_ERR(tmp);
+ }
+
+ lock_kernel();
+again: /* look the named file or a parent directory so we can get the cache */
+ error = presto_walk(tmp, &nd);
+ if ( error && error != -ENOENT ) {
+ EXIT;
+ unlock_kernel();
+ putname(tmp);
+ return error;
+ }
+ if (error == -ENOENT)
+ dentry = NULL;
+ else
+ dentry = nd.dentry;
+
+ /* we didn't find the named file, so see if a parent exists */
+ if (!dentry) {
+ char *slash;
+
+ slash = strrchr(tmp, '/');
+ if (slash && slash != tmp) {
+ *slash = '\0';
+ path_release(&nd);
+ goto again;
+ }
+ /* we should never get here... */
+ CDEBUG(D_PIOCTL, "no more path components to try!\n");
+ fd = -ENOENT;
+ goto exit;
+ }
+ CDEBUG(D_PIOCTL, "returned dentry %p\n", dentry);
+
+ dentry = presto_iopen(dentry, ino, generation);
+ fd = PTR_ERR(dentry);
+ if (IS_ERR(dentry)) {
+ EXIT;
+ goto exit;
+ }
+
+ /* XXX start of code that might be replaced by something like:
+ * if (flags & (O_WRONLY | O_RDWR)) {
+ * error = get_write_access(dentry->d_inode);
+ * if (error) {
+ * EXIT;
+ * goto cleanup_dput;
+ * }
+ * }
+ * fd = open_dentry(dentry, flags);
+ *
+ * including the presto_filp_dopen() function (check dget counts!)
+ */
+ fd = get_unused_fd();
+ if (fd < 0) {
+ EXIT;
+ goto exit;
+ }
+
+ {
+ int error;
+ struct file * f = presto_filp_dopen(dentry, flags);
+ error = PTR_ERR(f);
+ if (IS_ERR(f)) {
+ put_unused_fd(fd);
+ fd = error;
+ } else {
+ fd_install(fd, f);
+ }
+ }
+ /* end of code that might be replaced by open_dentry */
+
+ EXIT;
+exit:
+ unlock_kernel();
+ path_release(&nd);
+ putname(tmp);
+ return fd;
+}
+
+#ifdef CONFIG_FS_EXT_ATTR
+
+#ifdef CONFIG_FS_POSIX_ACL
+/* Posix ACL code changes i_mode without using a notify_change (or
+ * a mark_inode_dirty!). We need to duplicate this at the reintegrator
+ * which is done by this function. This function also takes care of
+ * resetting the cached posix acls in this inode. If we don't reset these
+ * VFS continues using the old acl information, which by now may be out of
+ * date.
+ */
+int presto_setmode(struct presto_file_set *fset, struct dentry *dentry,
+ mode_t mode)
+{
+ struct inode *inode = dentry->d_inode;
+
+ ENTRY;
+ /* The extended attributes for this inode were modified.
+ * At this point we can not be sure if any of the ACL
+ * information for this inode was updated. So we will
+ * force VFS to reread the acls. Note that we do this
+ * only when called from the SETEXTATTR ioctl, which is why we
+ * do this while setting the mode of the file. Also note
+ * that mark_inode_dirty is not be needed for i_*acl only
+ * to force i_mode info to disk, and should be removed once
+ * we use notify_change to update the mode.
+ * XXX: is mode setting really needed? Just setting acl's should
+ * be enough! VFS should change the i_mode as needed? SHP
+ */
+ if (inode->i_acl &&
+ inode->i_acl != POSIX_ACL_NOT_CACHED)
+ posix_acl_release(inode->i_acl);
+ if (inode->i_default_acl &&
+ inode->i_default_acl != POSIX_ACL_NOT_CACHED)
+ posix_acl_release(inode->i_default_acl);
+ inode->i_acl = POSIX_ACL_NOT_CACHED;
+ inode->i_default_acl = POSIX_ACL_NOT_CACHED;
+ inode->i_mode = mode;
+ /* inode should already be dirty...but just in case */
+ mark_inode_dirty(inode);
+ return 0;
+
+#if 0
+ /* XXX: The following code is the preferred way to set mode,
+ * however, I need to carefully go through possible recursion
+ * paths back into presto. See comments in presto_do_setattr.
+ */
+ {
+ int error=0;
+ struct super_operations *sops;
+ struct iattr iattr;
+
+ iattr.ia_mode = mode;
+ iattr.ia_valid = ATTR_MODE|ATTR_FORCE;
+
+ error = -EPERM;
+ sops = filter_c2csops(fset->fset_cache->cache_filter);
+ if (!sops &&
+ !sops->notify_change) {
+ EXIT;
+ return error;
+ }
+
+ error = sops->notify_change(dentry, &iattr);
+
+ EXIT;
+ return error;
+ }
+#endif
+}
+#endif
+
+/* setextattr Interface to cache filesystem */
+int presto_do_set_ext_attr(struct presto_file_set *fset,
+ struct dentry *dentry,
+ const char *name, void *buffer,
+ size_t buffer_len, int flags, mode_t *mode,
+ struct lento_vfs_context *info)
+{
+ struct rec_info rec;
+ struct inode *inode = dentry->d_inode;
+ struct inode_operations *iops;
+ int error;
+ struct presto_version ver;
+ void *handle;
+ char temp[PRESTO_EXT_ATTR_NAME_MAX+1];
+
+ ENTRY;
+ error = -EROFS;
+ if (IS_RDONLY(inode)) {
+ EXIT;
+ return -EROFS;
+ }
+
+ if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) {
+ EXIT;
+ return -EPERM;
+ }
+
+ presto_getversion(&ver, inode);
+ error = -EPERM;
+ /* We need to invoke different filters based on whether
+ * this dentry is a regular file, directory or symlink.
+ */
+ switch (inode->i_mode & S_IFMT) {
+ case S_IFLNK: /* symlink */
+ iops = filter_c2csiops(fset->fset_cache->cache_filter);
+ break;
+ case S_IFDIR: /* directory */
+ iops = filter_c2cdiops(fset->fset_cache->cache_filter);
+ break;
+ case S_IFREG:
+ default: /* everything else including regular files */
+ iops = filter_c2cfiops(fset->fset_cache->cache_filter);
+ }
+
+ if (!iops && !iops->set_ext_attr) {
+ EXIT;
+ return error;
+ }
+
+ error = presto_reserve_space(fset->fset_cache, PRESTO_REQHIGH);
+ if (error) {
+ EXIT;
+ return error;
+ }
+
+
+ handle = presto_trans_start(fset,dentry->d_inode,KML_OPCODE_SETEXTATTR);
+ if ( IS_ERR(handle) ) {
+ CERROR("presto_do_set_ext_attr: no space for transaction\n");
+ presto_release_space(fset->fset_cache, PRESTO_REQHIGH);
+ return -ENOSPC;
+ }
+
+ /* We first "truncate" name to the maximum allowable in presto */
+ /* This simulates the strncpy_from_use code in fs/ext_attr.c */
+ strncpy(temp,name,sizeof(temp));
+
+ /* Pass down to cache*/
+ error = iops->set_ext_attr(inode,temp,buffer,buffer_len,flags);
+ if (error) {
+ EXIT;
+ goto exit;
+ }
+
+#ifdef CONFIG_FS_POSIX_ACL
+ /* Reset mode if specified*/
+ /* XXX: when we do native acl support, move this code out! */
+ if (mode != NULL) {
+ error = presto_setmode(fset, dentry, *mode);
+ if (error) {
+ EXIT;
+ goto exit;
+ }
+ }
+#endif
+
+ /* Reset ctime. Only inode change time (ctime) is affected */
+ error = presto_settime(fset, NULL, NULL, dentry, info, ATTR_CTIME);
+ if (error) {
+ EXIT;
+ goto exit;
+ }
+
+ if (flags & EXT_ATTR_FLAG_USER) {
+ CERROR(" USER flag passed to presto_do_set_ext_attr!\n");
+ BUG();
+ }
+
+ /* We are here, so set_ext_attr succeeded. We no longer need to keep
+ * track of EXT_ATTR_FLAG_{EXISTS,CREATE}, instead, we will force
+ * the attribute value during log replay. -SHP
+ */
+ flags &= ~(EXT_ATTR_FLAG_EXISTS | EXT_ATTR_FLAG_CREATE);
+
+ presto_debug_fail_blkdev(fset, KML_OPCODE_SETEXTATTR | 0x10);
+ if ( presto_do_kml(info, dentry) )
+ error = presto_journal_set_ext_attr
+ (&rec, fset, dentry, &ver, name, buffer,
+ buffer_len, flags);
+
+ presto_debug_fail_blkdev(fset, KML_OPCODE_SETEXTATTR | 0x20);
+ if ( presto_do_rcvd(info, dentry) )
+ error = presto_write_last_rcvd(&rec, fset, info);
+
+ presto_debug_fail_blkdev(fset, KML_OPCODE_SETEXTATTR | 0x30);
+ EXIT;
+exit:
+ presto_release_space(fset->fset_cache, PRESTO_REQHIGH);
+ presto_trans_commit(fset, handle);
+
+ return error;
+}
+#endif
diff --git a/fs/iobuf.c b/fs/iobuf.c
new file mode 100644
index 00000000000000..981e8cbfbf675c
--- /dev/null
+++ b/fs/iobuf.c
@@ -0,0 +1,186 @@
+/*
+ * iobuf.c
+ *
+ * Keep track of the general-purpose IO-buffer structures used to track
+ * abstract kernel-space io buffers.
+ *
+ */
+
+#include <linux/iobuf.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+
+static kmem_cache_t *kiobuf_cachep;
+
+void end_kio_request(struct kiobuf *kiobuf, int uptodate)
+{
+ if ((!uptodate) && !kiobuf->errno)
+ kiobuf->errno = -EIO;
+
+ if (atomic_dec_and_test(&kiobuf->io_count)) {
+ if (kiobuf->end_io)
+ kiobuf->end_io(kiobuf);
+ wake_up(&kiobuf->wait_queue);
+ }
+}
+
+static int kiobuf_init(struct kiobuf *iobuf)
+{
+ init_waitqueue_head(&iobuf->wait_queue);
+ iobuf->array_len = 0;
+ iobuf->nr_pages = 0;
+ iobuf->locked = 0;
+ iobuf->bh = NULL;
+ iobuf->blocks = NULL;
+ atomic_set(&iobuf->io_count, 0);
+ iobuf->end_io = NULL;
+ return expand_kiobuf(iobuf, KIO_STATIC_PAGES);
+}
+
+int alloc_kiobuf_bhs(struct kiobuf * kiobuf)
+{
+ int i;
+
+ kiobuf->blocks =
+ kmalloc(sizeof(*kiobuf->blocks) * KIO_MAX_SECTORS, GFP_KERNEL);
+ if (unlikely(!kiobuf->blocks))
+ goto nomem;
+ kiobuf->bh =
+ kmalloc(sizeof(*kiobuf->bh) * KIO_MAX_SECTORS, GFP_KERNEL);
+ if (unlikely(!kiobuf->bh))
+ goto nomem;
+
+ for (i = 0; i < KIO_MAX_SECTORS; i++) {
+ kiobuf->bh[i] = kmem_cache_alloc(bh_cachep, GFP_KERNEL);
+ if (unlikely(!kiobuf->bh[i]))
+ goto nomem2;
+ }
+
+ return 0;
+
+nomem2:
+ while (i--) {
+ kmem_cache_free(bh_cachep, kiobuf->bh[i]);
+ kiobuf->bh[i] = NULL;
+ }
+ memset(kiobuf->bh, 0, sizeof(*kiobuf->bh) * KIO_MAX_SECTORS);
+
+nomem:
+ free_kiobuf_bhs(kiobuf);
+ return -ENOMEM;
+}
+
+void free_kiobuf_bhs(struct kiobuf * kiobuf)
+{
+ int i;
+
+ if (kiobuf->bh) {
+ for (i = 0; i < KIO_MAX_SECTORS; i++)
+ if (kiobuf->bh[i])
+ kmem_cache_free(bh_cachep, kiobuf->bh[i]);
+ kfree(kiobuf->bh);
+ kiobuf->bh = NULL;
+ }
+
+ if (kiobuf->blocks) {
+ kfree(kiobuf->blocks);
+ kiobuf->blocks = NULL;
+ }
+}
+
+int alloc_kiovec(int nr, struct kiobuf **bufp)
+{
+ int i;
+ struct kiobuf *iobuf;
+
+ for (i = 0; i < nr; i++) {
+ iobuf = kmem_cache_alloc(kiobuf_cachep, GFP_KERNEL);
+ if (unlikely(!iobuf))
+ goto nomem;
+ if (unlikely(kiobuf_init(iobuf)))
+ goto nomem2;
+ if (unlikely(alloc_kiobuf_bhs(iobuf)))
+ goto nomem2;
+ bufp[i] = iobuf;
+ }
+
+ return 0;
+
+nomem2:
+ kmem_cache_free(kiobuf_cachep, iobuf);
+nomem:
+ free_kiovec(i, bufp);
+ return -ENOMEM;
+}
+
+void free_kiovec(int nr, struct kiobuf **bufp)
+{
+ int i;
+ struct kiobuf *iobuf;
+
+ for (i = 0; i < nr; i++) {
+ iobuf = bufp[i];
+ if (iobuf->locked)
+ unlock_kiovec(1, &iobuf);
+ kfree(iobuf->maplist);
+ free_kiobuf_bhs(iobuf);
+ kmem_cache_free(kiobuf_cachep, bufp[i]);
+ }
+}
+
+int expand_kiobuf(struct kiobuf *iobuf, int wanted)
+{
+ struct page ** maplist;
+
+ if (iobuf->array_len >= wanted)
+ return 0;
+
+ maplist = kmalloc(wanted * sizeof(struct page **), GFP_KERNEL);
+ if (unlikely(!maplist))
+ return -ENOMEM;
+
+ /* Did it grow while we waited? */
+ if (unlikely(iobuf->array_len >= wanted)) {
+ kfree(maplist);
+ return 0;
+ }
+
+ if (iobuf->array_len) {
+ memcpy(maplist, iobuf->maplist, iobuf->array_len * sizeof(*maplist));
+ kfree(iobuf->maplist);
+ }
+
+ iobuf->maplist = maplist;
+ iobuf->array_len = wanted;
+ return 0;
+}
+
+void kiobuf_wait_for_io(struct kiobuf *kiobuf)
+{
+ struct task_struct *tsk = current;
+ DECLARE_WAITQUEUE(wait, tsk);
+
+ if (atomic_read(&kiobuf->io_count) == 0)
+ return;
+
+ add_wait_queue(&kiobuf->wait_queue, &wait);
+repeat:
+ set_task_state(tsk, TASK_UNINTERRUPTIBLE);
+ if (atomic_read(&kiobuf->io_count) != 0) {
+ run_task_queue(&tq_disk);
+ schedule();
+ if (atomic_read(&kiobuf->io_count) != 0)
+ goto repeat;
+ }
+ tsk->state = TASK_RUNNING;
+ remove_wait_queue(&kiobuf->wait_queue, &wait);
+}
+
+void __init iobuf_cache_init(void)
+{
+ kiobuf_cachep = kmem_cache_create("kiobuf", sizeof(struct kiobuf),
+ 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+ if (!kiobuf_cachep)
+ panic("Cannot create kiobuf SLAB cache");
+}
diff --git a/fs/ioctl.c b/fs/ioctl.c
new file mode 100644
index 00000000000000..daf20f094995bd
--- /dev/null
+++ b/fs/ioctl.c
@@ -0,0 +1,126 @@
+/*
+ * linux/fs/ioctl.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+#include <linux/mm.h>
+#include <linux/smp_lock.h>
+#include <linux/file.h>
+
+#include <asm/uaccess.h>
+#include <asm/ioctls.h>
+
+static int file_ioctl(struct file *filp,unsigned int cmd,unsigned long arg)
+{
+ int error;
+ int block;
+ struct inode * inode = filp->f_dentry->d_inode;
+
+ switch (cmd) {
+ case FIBMAP:
+ {
+ struct address_space *mapping = inode->i_mapping;
+ int res;
+ /* do we support this mess? */
+ if (!mapping->a_ops->bmap)
+ return -EINVAL;
+ if (!capable(CAP_SYS_RAWIO))
+ return -EPERM;
+ if ((error = get_user(block, (int *) arg)) != 0)
+ return error;
+
+ res = mapping->a_ops->bmap(mapping, block);
+ return put_user(res, (int *) arg);
+ }
+ case FIGETBSZ:
+ if (inode->i_sb == NULL)
+ return -EBADF;
+ return put_user(inode->i_sb->s_blocksize, (int *) arg);
+ case FIONREAD:
+ return put_user(inode->i_size - filp->f_pos, (int *) arg);
+ }
+ if (filp->f_op && filp->f_op->ioctl)
+ return filp->f_op->ioctl(inode, filp, cmd, arg);
+ return -ENOTTY;
+}
+
+
+asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
+{
+ struct file * filp;
+ unsigned int flag;
+ int on, error = -EBADF;
+
+ filp = fget(fd);
+ if (!filp)
+ goto out;
+ error = 0;
+ lock_kernel();
+ switch (cmd) {
+ case FIOCLEX:
+ set_close_on_exec(fd, 1);
+ break;
+
+ case FIONCLEX:
+ set_close_on_exec(fd, 0);
+ break;
+
+ case FIONBIO:
+ if ((error = get_user(on, (int *)arg)) != 0)
+ break;
+ flag = O_NONBLOCK;
+#ifdef __sparc__
+ /* SunOS compatibility item. */
+ if(O_NONBLOCK != O_NDELAY)
+ flag |= O_NDELAY;
+#endif
+ if (on)
+ filp->f_flags |= flag;
+ else
+ filp->f_flags &= ~flag;
+ break;
+
+ case FIOASYNC:
+ if ((error = get_user(on, (int *)arg)) != 0)
+ break;
+ flag = on ? FASYNC : 0;
+
+ /* Did FASYNC state change ? */
+ if ((flag ^ filp->f_flags) & FASYNC) {
+ if (filp->f_op && filp->f_op->fasync)
+ error = filp->f_op->fasync(fd, filp, on);
+ else error = -ENOTTY;
+ }
+ if (error != 0)
+ break;
+
+ if (on)
+ filp->f_flags |= FASYNC;
+ else
+ filp->f_flags &= ~FASYNC;
+ break;
+
+ case FIOQSIZE:
+ if (S_ISDIR(filp->f_dentry->d_inode->i_mode) ||
+ S_ISREG(filp->f_dentry->d_inode->i_mode) ||
+ S_ISLNK(filp->f_dentry->d_inode->i_mode)) {
+ loff_t res = inode_get_bytes(filp->f_dentry->d_inode);
+ error = copy_to_user((loff_t *)arg, &res, sizeof(res)) ? -EFAULT : 0;
+ }
+ else
+ error = -ENOTTY;
+ break;
+ default:
+ error = -ENOTTY;
+ if (S_ISREG(filp->f_dentry->d_inode->i_mode))
+ error = file_ioctl(filp, cmd, arg);
+ else if (filp->f_op && filp->f_op->ioctl)
+ error = filp->f_op->ioctl(filp->f_dentry->d_inode, filp, cmd, arg);
+ }
+ unlock_kernel();
+ fput(filp);
+
+out:
+ return error;
+}
diff --git a/fs/isofs/Makefile b/fs/isofs/Makefile
new file mode 100644
index 00000000000000..e35ceba0c6b57f
--- /dev/null
+++ b/fs/isofs/Makefile
@@ -0,0 +1,20 @@
+#
+# Makefile for the Linux isofs filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile.
+
+O_TARGET := isofs.o
+
+obj-y := namei.o inode.o dir.o util.o rock.o
+obj-$(CONFIG_JOLIET) += joliet.o
+obj-$(CONFIG_ZISOFS) += compress.o
+
+obj-m := $(O_TARGET)
+
+CFLAGS_compress.o := -I $(TOPDIR)/fs/inflate_fs
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/isofs/compress.c b/fs/isofs/compress.c
new file mode 100644
index 00000000000000..022454151c3d15
--- /dev/null
+++ b/fs/isofs/compress.c
@@ -0,0 +1,360 @@
+/* -*- linux-c -*- ------------------------------------------------------- *
+ *
+ * Copyright 2001 H. Peter Anvin - All Rights Reserved
+ *
+ * This program 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, Inc., 675 Mass Ave, Cambridge MA 02139,
+ * USA; either version 2 of the License, or (at your option) any later
+ * version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * linux/fs/isofs/compress.c
+ *
+ * Transparent decompression of files on an iso9660 filesystem
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/stat.h>
+#include <linux/sched.h>
+#include <linux/iso_fs.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/cdrom.h>
+#include <linux/init.h>
+#include <linux/nls.h>
+#include <linux/ctype.h>
+#include <linux/smp_lock.h>
+#include <linux/blkdev.h>
+#include <linux/vmalloc.h>
+#include <linux/zlib.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/semaphore.h>
+
+#include "zisofs.h"
+
+/* This should probably be global. */
+static char zisofs_sink_page[PAGE_CACHE_SIZE];
+
+/*
+ * This contains the zlib memory allocation and the mutex for the
+ * allocation; this avoids failures at block-decompression time.
+ */
+static void *zisofs_zlib_workspace;
+static struct semaphore zisofs_zlib_semaphore;
+
+/*
+ * When decompressing, we typically obtain more than one page
+ * per reference. We inject the additional pages into the page
+ * cache as a form of readahead.
+ */
+static int zisofs_readpage(struct file *file, struct page *page)
+{
+ struct inode *inode = file->f_dentry->d_inode;
+ struct address_space *mapping = inode->i_mapping;
+ unsigned int maxpage, xpage, fpage, blockindex;
+ unsigned long offset;
+ unsigned long blockptr, blockendptr, cstart, cend, csize;
+ struct buffer_head *bh, *ptrbh[2];
+ unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
+ unsigned int bufshift = ISOFS_BUFFER_BITS(inode);
+ unsigned long bufmask = bufsize - 1;
+ int err = -EIO;
+ int i;
+ unsigned int header_size = inode->u.isofs_i.i_format_parm[0];
+ unsigned int zisofs_block_shift = inode->u.isofs_i.i_format_parm[1];
+ /* unsigned long zisofs_block_size = 1UL << zisofs_block_shift; */
+ unsigned int zisofs_block_page_shift = zisofs_block_shift-PAGE_CACHE_SHIFT;
+ unsigned long zisofs_block_pages = 1UL << zisofs_block_page_shift;
+ unsigned long zisofs_block_page_mask = zisofs_block_pages-1;
+ struct page *pages[zisofs_block_pages];
+ unsigned long index = page->index;
+ int indexblocks;
+
+ /* We have already been given one page, this is the one
+ we must do. */
+ xpage = index & zisofs_block_page_mask;
+ pages[xpage] = page;
+
+ /* The remaining pages need to be allocated and inserted */
+ offset = index & ~zisofs_block_page_mask;
+ blockindex = offset >> zisofs_block_page_shift;
+ maxpage = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+ maxpage = min(zisofs_block_pages, maxpage-offset);
+
+ for ( i = 0 ; i < maxpage ; i++, offset++ ) {
+ if ( i != xpage ) {
+ pages[i] = grab_cache_page_nowait(mapping, offset);
+ }
+ page = pages[i];
+ if ( page ) {
+ ClearPageError(page);
+ kmap(page);
+ }
+ }
+
+ /* This is the last page filled, plus one; used in case of abort. */
+ fpage = 0;
+
+ /* Find the pointer to this specific chunk */
+ /* Note: we're not using isonum_731() here because the data is known aligned */
+ /* Note: header_size is in 32-bit words (4 bytes) */
+ blockptr = (header_size + blockindex) << 2;
+ blockendptr = blockptr + 4;
+
+ indexblocks = ((blockptr^blockendptr) >> bufshift) ? 2 : 1;
+ ptrbh[0] = ptrbh[1] = 0;
+
+ if ( isofs_get_blocks(inode, blockptr >> bufshift, ptrbh, indexblocks) != indexblocks ) {
+ if ( ptrbh[0] ) brelse(ptrbh[0]);
+ printk(KERN_DEBUG "zisofs: Null buffer on reading block table, inode = %lu, block = %lu\n",
+ inode->i_ino, blockptr >> bufshift);
+ goto eio;
+ }
+ ll_rw_block(READ, indexblocks, ptrbh);
+
+ bh = ptrbh[0];
+ if ( !bh || (wait_on_buffer(bh), !buffer_uptodate(bh)) ) {
+ printk(KERN_DEBUG "zisofs: Failed to read block table, inode = %lu, block = %lu\n",
+ inode->i_ino, blockptr >> bufshift);
+ if ( ptrbh[1] )
+ brelse(ptrbh[1]);
+ goto eio;
+ }
+ cstart = le32_to_cpu(*(u32 *)(bh->b_data + (blockptr & bufmask)));
+
+ if ( indexblocks == 2 ) {
+ /* We just crossed a block boundary. Switch to the next block */
+ brelse(bh);
+ bh = ptrbh[1];
+ if ( !bh || (wait_on_buffer(bh), !buffer_uptodate(bh)) ) {
+ printk(KERN_DEBUG "zisofs: Failed to read block table, inode = %lu, block = %lu\n",
+ inode->i_ino, blockendptr >> bufshift);
+ goto eio;
+ }
+ }
+ cend = le32_to_cpu(*(u32 *)(bh->b_data + (blockendptr & bufmask)));
+ brelse(bh);
+
+ csize = cend-cstart;
+
+ /* Now page[] contains an array of pages, any of which can be NULL,
+ and the locks on which we hold. We should now read the data and
+ release the pages. If the pages are NULL the decompressed data
+ for that particular page should be discarded. */
+
+ if ( csize == 0 ) {
+ /* This data block is empty. */
+
+ for ( fpage = 0 ; fpage < maxpage ; fpage++ ) {
+ if ( (page = pages[fpage]) != NULL ) {
+ memset(page_address(page), 0, PAGE_CACHE_SIZE);
+
+ flush_dcache_page(page);
+ SetPageUptodate(page);
+ kunmap(page);
+ UnlockPage(page);
+ if ( fpage == xpage )
+ err = 0; /* The critical page */
+ else
+ page_cache_release(page);
+ }
+ }
+ } else {
+ /* This data block is compressed. */
+ z_stream stream;
+ int bail = 0, left_out = -1;
+ int zerr;
+ int needblocks = (csize + (cstart & bufmask) + bufmask) >> bufshift;
+ int haveblocks;
+ struct buffer_head *bhs[needblocks+1];
+ struct buffer_head **bhptr;
+
+ /* Because zlib is not thread-safe, do all the I/O at the top. */
+
+ blockptr = cstart >> bufshift;
+ memset(bhs, 0, (needblocks+1)*sizeof(struct buffer_head *));
+ haveblocks = isofs_get_blocks(inode, blockptr, bhs, needblocks);
+ ll_rw_block(READ, haveblocks, bhs);
+
+ bhptr = &bhs[0];
+ bh = *bhptr++;
+
+ /* First block is special since it may be fractional.
+ We also wait for it before grabbing the zlib
+ semaphore; odds are that the subsequent blocks are
+ going to come in in short order so we don't hold
+ the zlib semaphore longer than necessary. */
+
+ if ( !bh || (wait_on_buffer(bh), !buffer_uptodate(bh)) ) {
+ printk(KERN_DEBUG "zisofs: Hit null buffer, fpage = %d, xpage = %d, csize = %ld\n",
+ fpage, xpage, csize);
+ goto b_eio;
+ }
+ stream.next_in = bh->b_data + (cstart & bufmask);
+ stream.avail_in = min(bufsize-(cstart & bufmask), csize);
+ csize -= stream.avail_in;
+
+ stream.workspace = zisofs_zlib_workspace;
+ down(&zisofs_zlib_semaphore);
+
+ zerr = zlib_inflateInit(&stream);
+ if ( zerr != Z_OK ) {
+ if ( err && zerr == Z_MEM_ERROR )
+ err = -ENOMEM;
+ printk(KERN_DEBUG "zisofs: zisofs_inflateInit returned %d\n",
+ zerr);
+ goto z_eio;
+ }
+
+ while ( !bail && fpage < maxpage ) {
+ page = pages[fpage];
+ if ( page )
+ stream.next_out = page_address(page);
+ else
+ stream.next_out = (void *)&zisofs_sink_page;
+ stream.avail_out = PAGE_CACHE_SIZE;
+
+ while ( stream.avail_out ) {
+ int ao, ai;
+ if ( stream.avail_in == 0 && left_out ) {
+ if ( !csize ) {
+ printk(KERN_WARNING "zisofs: ZF read beyond end of input\n");
+ bail = 1;
+ break;
+ } else {
+ bh = *bhptr++;
+ if ( !bh ||
+ (wait_on_buffer(bh), !buffer_uptodate(bh)) ) {
+ /* Reached an EIO */
+ printk(KERN_DEBUG "zisofs: Hit null buffer, fpage = %d, xpage = %d, csize = %ld\n",
+ fpage, xpage, csize);
+
+ bail = 1;
+ break;
+ }
+ stream.next_in = bh->b_data;
+ stream.avail_in = min(csize,bufsize);
+ csize -= stream.avail_in;
+ }
+ }
+ ao = stream.avail_out; ai = stream.avail_in;
+ zerr = zlib_inflate(&stream, Z_SYNC_FLUSH);
+ left_out = stream.avail_out;
+ if ( zerr == Z_BUF_ERROR && stream.avail_in == 0 )
+ continue;
+ if ( zerr != Z_OK ) {
+ /* EOF, error, or trying to read beyond end of input */
+ if ( err && zerr == Z_MEM_ERROR )
+ err = -ENOMEM;
+ if ( zerr != Z_STREAM_END )
+ printk(KERN_DEBUG "zisofs: zisofs_inflate returned %d, inode = %lu, index = %lu, fpage = %d, xpage = %d, avail_in = %d, avail_out = %d, ai = %d, ao = %d\n",
+ zerr, inode->i_ino, index,
+ fpage, xpage,
+ stream.avail_in, stream.avail_out,
+ ai, ao);
+ bail = 1;
+ break;
+ }
+ }
+
+ if ( stream.avail_out && zerr == Z_STREAM_END ) {
+ /* Fractional page written before EOF. This may
+ be the last page in the file. */
+ memset(stream.next_out, 0, stream.avail_out);
+ stream.avail_out = 0;
+ }
+
+ if ( !stream.avail_out ) {
+ /* This page completed */
+ if ( page ) {
+ flush_dcache_page(page);
+ SetPageUptodate(page);
+ kunmap(page);
+ UnlockPage(page);
+ if ( fpage == xpage )
+ err = 0; /* The critical page */
+ else
+ page_cache_release(page);
+ }
+ fpage++;
+ }
+ }
+ zlib_inflateEnd(&stream);
+
+ z_eio:
+ up(&zisofs_zlib_semaphore);
+
+ b_eio:
+ for ( i = 0 ; i < haveblocks ; i++ ) {
+ if ( bhs[i] )
+ brelse(bhs[i]);
+ }
+ }
+
+eio:
+
+ /* Release any residual pages, do not SetPageUptodate */
+ while ( fpage < maxpage ) {
+ page = pages[fpage];
+ if ( page ) {
+ flush_dcache_page(page);
+ if ( fpage == xpage )
+ SetPageError(page);
+ kunmap(page);
+ UnlockPage(page);
+ if ( fpage != xpage )
+ page_cache_release(page);
+ }
+ fpage++;
+ }
+
+ /* At this point, err contains 0 or -EIO depending on the "critical" page */
+ return err;
+}
+
+struct address_space_operations zisofs_aops = {
+ readpage: zisofs_readpage,
+ /* No sync_page operation supported? */
+ /* No bmap operation supported */
+};
+
+static int initialized = 0;
+
+int __init zisofs_init(void)
+{
+ if ( initialized ) {
+ printk("zisofs_init: called more than once\n");
+ return 0;
+ }
+
+ zisofs_zlib_workspace = vmalloc(zlib_inflate_workspacesize());
+ if ( !zisofs_zlib_workspace )
+ return -ENOMEM;
+ init_MUTEX(&zisofs_zlib_semaphore);
+
+ initialized = 1;
+ return 0;
+}
+
+void __exit zisofs_cleanup(void)
+{
+ if ( !initialized ) {
+ printk("zisofs_cleanup: called without initialization\n");
+ return;
+ }
+
+ vfree(zisofs_zlib_workspace);
+ initialized = 0;
+}
diff --git a/fs/isofs/dir.c b/fs/isofs/dir.c
new file mode 100644
index 00000000000000..29db6b6c01ee0e
--- /dev/null
+++ b/fs/isofs/dir.c
@@ -0,0 +1,266 @@
+/*
+ * linux/fs/isofs/dir.c
+ *
+ * (C) 1992, 1993, 1994 Eric Youngdale Modified for ISO 9660 filesystem.
+ *
+ * (C) 1991 Linus Torvalds - minix filesystem
+ *
+ * Steve Beynon : Missing last directory entries fixed
+ * (stephen@askone.demon.co.uk) : 21st June 1996
+ *
+ * isofs directory handling functions
+ */
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/iso_fs.h>
+#include <linux/kernel.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/locks.h>
+#include <linux/config.h>
+
+#include <asm/uaccess.h>
+
+static int isofs_readdir(struct file *, void *, filldir_t);
+
+struct file_operations isofs_dir_operations =
+{
+ read: generic_read_dir,
+ readdir: isofs_readdir,
+};
+
+/*
+ * directories can handle most operations...
+ */
+struct inode_operations isofs_dir_inode_operations =
+{
+ lookup: isofs_lookup,
+};
+
+int isofs_name_translate(struct iso_directory_record *de, char *new, struct inode *inode)
+{
+ char * old = de->name;
+ int len = de->name_len[0];
+ int i;
+
+ for (i = 0; i < len; i++) {
+ unsigned char c = old[i];
+ if (!c)
+ break;
+
+ if (c >= 'A' && c <= 'Z')
+ c |= 0x20; /* lower case */
+
+ /* Drop trailing '.;1' (ISO 9660:1988 7.5.1 requires period) */
+ if (c == '.' && i == len - 3 && old[i + 1] == ';' && old[i + 2] == '1')
+ break;
+
+ /* Drop trailing ';1' */
+ if (c == ';' && i == len - 2 && old[i + 1] == '1')
+ break;
+
+ /* Convert remaining ';' to '.' */
+ if (c == ';')
+ c = '.';
+
+ new[i] = c;
+ }
+ return i;
+}
+
+/* Acorn extensions written by Matthew Wilcox <willy@bofh.ai> 1998 */
+int get_acorn_filename(struct iso_directory_record * de,
+ char * retname, struct inode * inode)
+{
+ int std;
+ unsigned char * chr;
+ int retnamlen = isofs_name_translate(de, retname, inode);
+ if (retnamlen == 0) return 0;
+ std = sizeof(struct iso_directory_record) + de->name_len[0];
+ if (std & 1) std++;
+ if ((*((unsigned char *) de) - std) != 32) return retnamlen;
+ chr = ((unsigned char *) de) + std;
+ if (strncmp(chr, "ARCHIMEDES", 10)) return retnamlen;
+ if ((*retname == '_') && ((chr[19] & 1) == 1)) *retname = '!';
+ if (((de->flags[0] & 2) == 0) && (chr[13] == 0xff)
+ && ((chr[12] & 0xf0) == 0xf0))
+ {
+ retname[retnamlen] = ',';
+ sprintf(retname+retnamlen+1, "%3.3x",
+ ((chr[12] & 0xf) << 8) | chr[11]);
+ retnamlen += 4;
+ }
+ return retnamlen;
+}
+
+/*
+ * This should _really_ be cleaned up some day..
+ */
+static int do_isofs_readdir(struct inode *inode, struct file *filp,
+ void *dirent, filldir_t filldir,
+ char * tmpname, struct iso_directory_record * tmpde)
+{
+ unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
+ unsigned char bufbits = ISOFS_BUFFER_BITS(inode);
+ unsigned int block, offset;
+ int inode_number = 0; /* Quiet GCC */
+ struct buffer_head *bh = NULL;
+ int len;
+ int map;
+ int high_sierra;
+ int first_de = 1;
+ char *p = NULL; /* Quiet GCC */
+ struct iso_directory_record *de;
+
+ offset = filp->f_pos & (bufsize - 1);
+ block = filp->f_pos >> bufbits;
+ high_sierra = inode->i_sb->u.isofs_sb.s_high_sierra;
+
+ while (filp->f_pos < inode->i_size) {
+ int de_len;
+
+ if (!bh) {
+ bh = isofs_bread(inode, block);
+ if (!bh)
+ return 0;
+ }
+
+ de = (struct iso_directory_record *) (bh->b_data + offset);
+ if (first_de)
+ inode_number = (bh->b_blocknr << bufbits) + offset;
+
+ de_len = *(unsigned char *) de;
+
+ /* If the length byte is zero, we should move on to the next
+ CDROM sector. If we are at the end of the directory, we
+ kick out of the while loop. */
+
+ if (de_len == 0) {
+ brelse(bh);
+ bh = NULL;
+ filp->f_pos = (filp->f_pos + ISOFS_BLOCK_SIZE) & ~(ISOFS_BLOCK_SIZE - 1);
+ block = filp->f_pos >> bufbits;
+ offset = 0;
+ continue;
+ }
+
+ offset += de_len;
+
+ /* Make sure we have a full directory entry */
+ if (offset >= bufsize) {
+ int slop = bufsize - offset + de_len;
+ memcpy(tmpde, de, slop);
+ offset &= bufsize - 1;
+ block++;
+ brelse(bh);
+ bh = NULL;
+ if (offset) {
+ bh = isofs_bread(inode, block);
+ if (!bh)
+ return 0;
+ memcpy((void *) tmpde + slop, bh->b_data, offset);
+ }
+ de = tmpde;
+ }
+
+ if (de->flags[-high_sierra] & 0x80) {
+ first_de = 0;
+ filp->f_pos += de_len;
+ continue;
+ }
+ first_de = 1;
+
+ /* Handle the case of the '.' directory */
+ if (de->name_len[0] == 1 && de->name[0] == 0) {
+ if (filldir(dirent, ".", 1, filp->f_pos, inode->i_ino, DT_DIR) < 0)
+ break;
+ filp->f_pos += de_len;
+ continue;
+ }
+
+ len = 0;
+
+ /* Handle the case of the '..' directory */
+ if (de->name_len[0] == 1 && de->name[0] == 1) {
+ inode_number = filp->f_dentry->d_parent->d_inode->i_ino;
+ if (filldir(dirent, "..", 2, filp->f_pos, inode_number, DT_DIR) < 0)
+ break;
+ filp->f_pos += de_len;
+ continue;
+ }
+
+ /* Handle everything else. Do name translation if there
+ is no Rock Ridge NM field. */
+ if (inode->i_sb->u.isofs_sb.s_unhide == 'n') {
+ /* Do not report hidden or associated files */
+ if (de->flags[-high_sierra] & 5) {
+ filp->f_pos += de_len;
+ continue;
+ }
+ }
+
+ map = 1;
+ if (inode->i_sb->u.isofs_sb.s_rock) {
+ len = get_rock_ridge_filename(de, tmpname, inode);
+ if (len != 0) { /* may be -1 */
+ p = tmpname;
+ map = 0;
+ }
+ }
+ if (map) {
+#ifdef CONFIG_JOLIET
+ if (inode->i_sb->u.isofs_sb.s_joliet_level) {
+ len = get_joliet_filename(de, tmpname, inode);
+ p = tmpname;
+ } else
+#endif
+ if (inode->i_sb->u.isofs_sb.s_mapping == 'a') {
+ len = get_acorn_filename(de, tmpname, inode);
+ p = tmpname;
+ } else
+ if (inode->i_sb->u.isofs_sb.s_mapping == 'n') {
+ len = isofs_name_translate(de, tmpname, inode);
+ p = tmpname;
+ } else {
+ p = de->name;
+ len = de->name_len[0];
+ }
+ }
+ if (len > 0) {
+ if (filldir(dirent, p, len, filp->f_pos, inode_number, DT_UNKNOWN) < 0)
+ break;
+ }
+ filp->f_pos += de_len;
+
+ continue;
+ }
+ if (bh) brelse(bh);
+ return 0;
+}
+
+/*
+ * Handle allocation of temporary space for name translation and
+ * handling split directory entries.. The real work is done by
+ * "do_isofs_readdir()".
+ */
+static int isofs_readdir(struct file *filp,
+ void *dirent, filldir_t filldir)
+{
+ int result;
+ char * tmpname;
+ struct iso_directory_record * tmpde;
+ struct inode *inode = filp->f_dentry->d_inode;
+
+ tmpname = (char *) __get_free_page(GFP_KERNEL);
+ if (!tmpname)
+ return -ENOMEM;
+ tmpde = (struct iso_directory_record *) (tmpname+1024);
+
+ result = do_isofs_readdir(inode, filp, dirent, filldir, tmpname, tmpde);
+
+ free_page((unsigned long) tmpname);
+ return result;
+}
diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c
new file mode 100644
index 00000000000000..8c151771071952
--- /dev/null
+++ b/fs/isofs/inode.c
@@ -0,0 +1,1334 @@
+/*
+ * linux/fs/isofs/inode.c
+ *
+ * (C) 1991 Linus Torvalds - minix filesystem
+ * 1992, 1993, 1994 Eric Youngdale Modified for ISO 9660 filesystem.
+ * 1994 Eberhard Moenkeberg - multi session handling.
+ * 1995 Mark Dobie - allow mounting of some weird VideoCDs and PhotoCDs.
+ * 1997 Gordon Chaffee - Joliet CDs
+ * 1998 Eric Lammerts - ISO 9660 Level 3
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/stat.h>
+#include <linux/sched.h>
+#include <linux/iso_fs.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/cdrom.h>
+#include <linux/init.h>
+#include <linux/nls.h>
+#include <linux/ctype.h>
+#include <linux/smp_lock.h>
+#include <linux/blkdev.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include "zisofs.h"
+
+#define BEQUIET
+
+#ifdef LEAK_CHECK
+static int check_malloc = 0;
+static int check_bread = 0;
+#endif
+
+static int isofs_hashi(struct dentry *parent, struct qstr *qstr);
+static int isofs_hash(struct dentry *parent, struct qstr *qstr);
+static int isofs_dentry_cmpi(struct dentry *dentry, struct qstr *a, struct qstr *b);
+static int isofs_dentry_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b);
+
+#ifdef CONFIG_JOLIET
+static int isofs_hashi_ms(struct dentry *parent, struct qstr *qstr);
+static int isofs_hash_ms(struct dentry *parent, struct qstr *qstr);
+static int isofs_dentry_cmpi_ms(struct dentry *dentry, struct qstr *a, struct qstr *b);
+static int isofs_dentry_cmp_ms(struct dentry *dentry, struct qstr *a, struct qstr *b);
+#endif
+
+static void isofs_put_super(struct super_block *sb)
+{
+#ifdef CONFIG_JOLIET
+ if (sb->u.isofs_sb.s_nls_iocharset) {
+ unload_nls(sb->u.isofs_sb.s_nls_iocharset);
+ sb->u.isofs_sb.s_nls_iocharset = NULL;
+ }
+#endif
+
+#ifdef LEAK_CHECK
+ printk("Outstanding mallocs:%d, outstanding buffers: %d\n",
+ check_malloc, check_bread);
+#endif
+
+ return;
+}
+
+static void isofs_read_inode(struct inode *);
+static int isofs_statfs (struct super_block *, struct statfs *);
+
+static struct super_operations isofs_sops = {
+ read_inode: isofs_read_inode,
+ put_super: isofs_put_super,
+ statfs: isofs_statfs,
+};
+
+static struct dentry_operations isofs_dentry_ops[] = {
+ {
+ d_hash: isofs_hash,
+ d_compare: isofs_dentry_cmp,
+ },
+ {
+ d_hash: isofs_hashi,
+ d_compare: isofs_dentry_cmpi,
+ },
+#ifdef CONFIG_JOLIET
+ {
+ d_hash: isofs_hash_ms,
+ d_compare: isofs_dentry_cmp_ms,
+ },
+ {
+ d_hash: isofs_hashi_ms,
+ d_compare: isofs_dentry_cmpi_ms,
+ }
+#endif
+};
+
+struct iso9660_options{
+ char map;
+ char rock;
+ char joliet;
+ char cruft;
+ char unhide;
+ char nocompress;
+ unsigned char check;
+ unsigned int blocksize;
+ mode_t mode;
+ gid_t gid;
+ uid_t uid;
+ char *iocharset;
+ unsigned char utf8;
+ /* LVE */
+ s32 session;
+ s32 sbsector;
+};
+
+/*
+ * Compute the hash for the isofs name corresponding to the dentry.
+ */
+static int
+isofs_hash_common(struct dentry *dentry, struct qstr *qstr, int ms)
+{
+ const char *name;
+ int len;
+
+ len = qstr->len;
+ name = qstr->name;
+ if (ms) {
+ while (len && name[len-1] == '.')
+ len--;
+ }
+
+ qstr->hash = full_name_hash(name, len);
+
+ return 0;
+}
+
+/*
+ * Compute the hash for the isofs name corresponding to the dentry.
+ */
+static int
+isofs_hashi_common(struct dentry *dentry, struct qstr *qstr, int ms)
+{
+ const char *name;
+ int len;
+ char c;
+ unsigned long hash;
+
+ len = qstr->len;
+ name = qstr->name;
+ if (ms) {
+ while (len && name[len-1] == '.')
+ len--;
+ }
+
+ hash = init_name_hash();
+ while (len--) {
+ c = tolower(*name++);
+ hash = partial_name_hash(tolower(c), hash);
+ }
+ qstr->hash = end_name_hash(hash);
+
+ return 0;
+}
+
+/*
+ * Case insensitive compare of two isofs names.
+ */
+static int
+isofs_dentry_cmpi_common(struct dentry *dentry,struct qstr *a,struct qstr *b,int ms)
+{
+ int alen, blen;
+
+ /* A filename cannot end in '.' or we treat it like it has none */
+ alen = a->len;
+ blen = b->len;
+ if (ms) {
+ while (alen && a->name[alen-1] == '.')
+ alen--;
+ while (blen && b->name[blen-1] == '.')
+ blen--;
+ }
+ if (alen == blen) {
+ if (strnicmp(a->name, b->name, alen) == 0)
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Case sensitive compare of two isofs names.
+ */
+static int
+isofs_dentry_cmp_common(struct dentry *dentry,struct qstr *a,struct qstr *b,int ms)
+{
+ int alen, blen;
+
+ /* A filename cannot end in '.' or we treat it like it has none */
+ alen = a->len;
+ blen = b->len;
+ if (ms) {
+ while (alen && a->name[alen-1] == '.')
+ alen--;
+ while (blen && b->name[blen-1] == '.')
+ blen--;
+ }
+ if (alen == blen) {
+ if (strncmp(a->name, b->name, alen) == 0)
+ return 0;
+ }
+ return 1;
+}
+
+static int
+isofs_hash(struct dentry *dentry, struct qstr *qstr)
+{
+ return isofs_hash_common(dentry, qstr, 0);
+}
+
+static int
+isofs_hashi(struct dentry *dentry, struct qstr *qstr)
+{
+ return isofs_hashi_common(dentry, qstr, 0);
+}
+
+static int
+isofs_dentry_cmp(struct dentry *dentry,struct qstr *a,struct qstr *b)
+{
+ return isofs_dentry_cmp_common(dentry, a, b, 0);
+}
+
+static int
+isofs_dentry_cmpi(struct dentry *dentry,struct qstr *a,struct qstr *b)
+{
+ return isofs_dentry_cmpi_common(dentry, a, b, 0);
+}
+
+#ifdef CONFIG_JOLIET
+static int
+isofs_hash_ms(struct dentry *dentry, struct qstr *qstr)
+{
+ return isofs_hash_common(dentry, qstr, 1);
+}
+
+static int
+isofs_hashi_ms(struct dentry *dentry, struct qstr *qstr)
+{
+ return isofs_hashi_common(dentry, qstr, 1);
+}
+
+static int
+isofs_dentry_cmp_ms(struct dentry *dentry,struct qstr *a,struct qstr *b)
+{
+ return isofs_dentry_cmp_common(dentry, a, b, 1);
+}
+
+static int
+isofs_dentry_cmpi_ms(struct dentry *dentry,struct qstr *a,struct qstr *b)
+{
+ return isofs_dentry_cmpi_common(dentry, a, b, 1);
+}
+#endif
+
+static int parse_options(char *options, struct iso9660_options * popt)
+{
+ char *this_char,*value;
+
+ popt->map = 'n';
+ popt->rock = 'y';
+ popt->joliet = 'y';
+ popt->cruft = 'n';
+ popt->unhide = 'n';
+ popt->check = 'u'; /* unset */
+ popt->nocompress = 0;
+ popt->blocksize = 1024;
+ popt->mode = S_IRUGO | S_IXUGO; /* r-x for all. The disc could
+ be shared with DOS machines so
+ virtually anything could be
+ a valid executable. */
+ popt->gid = 0;
+ popt->uid = 0;
+ popt->iocharset = NULL;
+ popt->utf8 = 0;
+ popt->session=-1;
+ popt->sbsector=-1;
+ if (!options) return 1;
+ for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) {
+ if (strncmp(this_char,"norock",6) == 0) {
+ popt->rock = 'n';
+ continue;
+ }
+ if (strncmp(this_char,"nojoliet",8) == 0) {
+ popt->joliet = 'n';
+ continue;
+ }
+ if (strncmp(this_char,"unhide",6) == 0) {
+ popt->unhide = 'y';
+ continue;
+ }
+ if (strncmp(this_char,"cruft",5) == 0) {
+ popt->cruft = 'y';
+ continue;
+ }
+ if (strncmp(this_char,"utf8",4) == 0) {
+ popt->utf8 = 1;
+ continue;
+ }
+ if (strncmp(this_char,"nocompress",10) == 0) {
+ popt->nocompress = 1;
+ continue;
+ }
+ if ((value = strchr(this_char,'=')) != NULL)
+ *value++ = 0;
+
+#ifdef CONFIG_JOLIET
+ if (!strcmp(this_char,"iocharset") && value) {
+ popt->iocharset = value;
+ while (*value && *value != ',')
+ value++;
+ if (value == popt->iocharset)
+ return 0;
+ *value = 0;
+ } else
+#endif
+ if (!strcmp(this_char,"map") && value) {
+ if (value[0] && !value[1] && strchr("ano",*value))
+ popt->map = *value;
+ else if (!strcmp(value,"off")) popt->map = 'o';
+ else if (!strcmp(value,"normal")) popt->map = 'n';
+ else if (!strcmp(value,"acorn")) popt->map = 'a';
+ else return 0;
+ }
+ if (!strcmp(this_char,"session") && value) {
+ char * vpnt = value;
+ unsigned int ivalue = simple_strtoul(vpnt, &vpnt, 0);
+ if(ivalue < 0 || ivalue >99) return 0;
+ popt->session=ivalue+1;
+ }
+ if (!strcmp(this_char,"sbsector") && value) {
+ char * vpnt = value;
+ unsigned int ivalue = simple_strtoul(vpnt, &vpnt, 0);
+ if(ivalue < 0 || ivalue >660*512) return 0;
+ popt->sbsector=ivalue;
+ }
+ else if (!strcmp(this_char,"check") && value) {
+ if (value[0] && !value[1] && strchr("rs",*value))
+ popt->check = *value;
+ else if (!strcmp(value,"relaxed")) popt->check = 'r';
+ else if (!strcmp(value,"strict")) popt->check = 's';
+ else return 0;
+ }
+ else if (!strcmp(this_char,"conv") && value) {
+ /* no conversion is done anymore;
+ we still accept the same mount options,
+ but ignore them */
+ if (value[0] && !value[1] && strchr("btma",*value)) ;
+ else if (!strcmp(value,"binary")) ;
+ else if (!strcmp(value,"text")) ;
+ else if (!strcmp(value,"mtext")) ;
+ else if (!strcmp(value,"auto")) ;
+ else return 0;
+ }
+ else if (value &&
+ (!strcmp(this_char,"block") ||
+ !strcmp(this_char,"mode") ||
+ !strcmp(this_char,"uid") ||
+ !strcmp(this_char,"gid"))) {
+ char * vpnt = value;
+ unsigned int ivalue = simple_strtoul(vpnt, &vpnt, 0);
+ if (*vpnt) return 0;
+ switch(*this_char) {
+ case 'b':
+ if ( ivalue != 512
+ && ivalue != 1024
+ && ivalue != 2048) return 0;
+ popt->blocksize = ivalue;
+ break;
+ case 'u':
+ popt->uid = ivalue;
+ break;
+ case 'g':
+ popt->gid = ivalue;
+ break;
+ case 'm':
+ popt->mode = ivalue;
+ break;
+ }
+ }
+ else return 1;
+ }
+ return 1;
+}
+
+/*
+ * look if the driver can tell the multi session redirection value
+ *
+ * don't change this if you don't know what you do, please!
+ * Multisession is legal only with XA disks.
+ * A non-XA disk with more than one volume descriptor may do it right, but
+ * usually is written in a nowhere standardized "multi-partition" manner.
+ * Multisession uses absolute addressing (solely the first frame of the whole
+ * track is #0), multi-partition uses relative addressing (each first frame of
+ * each track is #0), and a track is not a session.
+ *
+ * A broken CDwriter software or drive firmware does not set new standards,
+ * at least not if conflicting with the existing ones.
+ *
+ * emoenke@gwdg.de
+ */
+#define WE_OBEY_THE_WRITTEN_STANDARDS 1
+
+static unsigned int isofs_get_last_session(struct super_block *sb,s32 session )
+{
+ struct cdrom_multisession ms_info;
+ unsigned int vol_desc_start;
+ struct block_device *bdev = sb->s_bdev;
+ int i;
+
+ vol_desc_start=0;
+ ms_info.addr_format=CDROM_LBA;
+ if(session >= 0 && session <= 99) {
+ struct cdrom_tocentry Te;
+ Te.cdte_track=session;
+ Te.cdte_format=CDROM_LBA;
+ i = ioctl_by_bdev(bdev, CDROMREADTOCENTRY, (unsigned long) &Te);
+ if (!i) {
+ printk(KERN_DEBUG "Session %d start %d type %d\n",
+ session, Te.cdte_addr.lba,
+ Te.cdte_ctrl&CDROM_DATA_TRACK);
+ if ((Te.cdte_ctrl&CDROM_DATA_TRACK) == 4)
+ return Te.cdte_addr.lba;
+ }
+
+ printk(KERN_ERR "Invalid session number or type of track\n");
+ }
+ i = ioctl_by_bdev(bdev, CDROMMULTISESSION, (unsigned long) &ms_info);
+ if(session > 0) printk(KERN_ERR "Invalid session number\n");
+#if 0
+ printk("isofs.inode: CDROMMULTISESSION: rc=%d\n",i);
+ if (i==0) {
+ printk("isofs.inode: XA disk: %s\n",ms_info.xa_flag?"yes":"no");
+ printk("isofs.inode: vol_desc_start = %d\n", ms_info.addr.lba);
+ }
+#endif
+ if (i==0)
+#if WE_OBEY_THE_WRITTEN_STANDARDS
+ if (ms_info.xa_flag) /* necessary for a valid ms_info.addr */
+#endif
+ vol_desc_start=ms_info.addr.lba;
+ return vol_desc_start;
+}
+
+/*
+ * Initialize the superblock and read the root inode.
+ *
+ * Note: a check_disk_change() has been done immediately prior
+ * to this call, so we don't need to check again.
+ */
+static struct super_block *isofs_read_super(struct super_block *s, void *data,
+ int silent)
+{
+ kdev_t dev = s->s_dev;
+ struct buffer_head * bh = NULL, *pri_bh = NULL;
+ struct hs_primary_descriptor * h_pri = NULL;
+ struct iso_primary_descriptor * pri = NULL;
+ struct iso_supplementary_descriptor *sec = NULL;
+ struct iso_directory_record * rootp;
+ int joliet_level = 0;
+ int high_sierra;
+ int iso_blknum, block;
+ int orig_zonesize;
+ int table;
+ unsigned int blocksize, blocksize_bits;
+ unsigned int vol_desc_start;
+ unsigned long first_data_zone;
+ struct inode * inode;
+ struct iso9660_options opt;
+
+ if (!parse_options((char *) data, &opt))
+ goto out_unlock;
+
+ /*
+ * First of all, get the hardware blocksize for this device.
+ * If we don't know what it is, or the hardware blocksize is
+ * larger than the blocksize the user specified, then use
+ * that value.
+ */
+ blocksize = get_hardsect_size(dev);
+ if(blocksize > opt.blocksize) {
+ /*
+ * Force the blocksize we are going to use to be the
+ * hardware blocksize.
+ */
+ opt.blocksize = blocksize;
+ }
+
+ blocksize_bits = 0;
+ {
+ int i = opt.blocksize;
+ while (i != 1){
+ blocksize_bits++;
+ i >>=1;
+ }
+ }
+
+ set_blocksize(dev, opt.blocksize);
+ s->s_blocksize = opt.blocksize;
+
+ s->u.isofs_sb.s_high_sierra = high_sierra = 0; /* default is iso9660 */
+
+ vol_desc_start = (opt.sbsector != -1) ?
+ opt.sbsector : isofs_get_last_session(s,opt.session);
+
+ for (iso_blknum = vol_desc_start+16;
+ iso_blknum < vol_desc_start+100; iso_blknum++)
+ {
+ struct hs_volume_descriptor * hdp;
+ struct iso_volume_descriptor * vdp;
+
+ block = iso_blknum << (ISOFS_BLOCK_BITS-blocksize_bits);
+ if (!(bh = sb_bread(s, block)))
+ goto out_no_read;
+
+ vdp = (struct iso_volume_descriptor *)bh->b_data;
+ hdp = (struct hs_volume_descriptor *)bh->b_data;
+
+ /* Due to the overlapping physical location of the descriptors,
+ * ISO CDs can match hdp->id==HS_STANDARD_ID as well. To ensure
+ * proper identification in this case, we first check for ISO.
+ */
+ if (strncmp (vdp->id, ISO_STANDARD_ID, sizeof vdp->id) == 0) {
+ if (isonum_711 (vdp->type) == ISO_VD_END)
+ break;
+ if (isonum_711 (vdp->type) == ISO_VD_PRIMARY) {
+ if (pri == NULL) {
+ pri = (struct iso_primary_descriptor *)vdp;
+ /* Save the buffer in case we need it ... */
+ pri_bh = bh;
+ bh = NULL;
+ }
+ }
+#ifdef CONFIG_JOLIET
+ else if (isonum_711 (vdp->type) == ISO_VD_SUPPLEMENTARY) {
+ sec = (struct iso_supplementary_descriptor *)vdp;
+ if (sec->escape[0] == 0x25 && sec->escape[1] == 0x2f) {
+ if (opt.joliet == 'y') {
+ if (sec->escape[2] == 0x40) {
+ joliet_level = 1;
+ } else if (sec->escape[2] == 0x43) {
+ joliet_level = 2;
+ } else if (sec->escape[2] == 0x45) {
+ joliet_level = 3;
+ }
+ printk(KERN_DEBUG"ISO 9660 Extensions: Microsoft Joliet Level %d\n",
+ joliet_level);
+ }
+ goto root_found;
+ } else {
+ /* Unknown supplementary volume descriptor */
+ sec = NULL;
+ }
+ }
+#endif
+ } else {
+ if (strncmp (hdp->id, HS_STANDARD_ID, sizeof hdp->id) == 0) {
+ if (isonum_711 (hdp->type) != ISO_VD_PRIMARY)
+ goto out_freebh;
+
+ s->u.isofs_sb.s_high_sierra = 1;
+ high_sierra = 1;
+ opt.rock = 'n';
+ h_pri = (struct hs_primary_descriptor *)vdp;
+ goto root_found;
+ }
+ }
+
+ /* Just skip any volume descriptors we don't recognize */
+
+ brelse(bh);
+ bh = NULL;
+ }
+ /*
+ * If we fall through, either no volume descriptor was found,
+ * or else we passed a primary descriptor looking for others.
+ */
+ if (!pri)
+ goto out_unknown_format;
+ brelse(bh);
+ bh = pri_bh;
+ pri_bh = NULL;
+
+root_found:
+
+ if (joliet_level && (pri == NULL || opt.rock == 'n')) {
+ /* This is the case of Joliet with the norock mount flag.
+ * A disc with both Joliet and Rock Ridge is handled later
+ */
+ pri = (struct iso_primary_descriptor *) sec;
+ }
+
+ if(high_sierra){
+ rootp = (struct iso_directory_record *) h_pri->root_directory_record;
+ s->u.isofs_sb.s_nzones = isonum_733 (h_pri->volume_space_size);
+ s->u.isofs_sb.s_log_zone_size = isonum_723 (h_pri->logical_block_size);
+ s->u.isofs_sb.s_max_size = isonum_733(h_pri->volume_space_size);
+ } else {
+ if (!pri)
+ goto out_freebh;
+ rootp = (struct iso_directory_record *) pri->root_directory_record;
+ s->u.isofs_sb.s_nzones = isonum_733 (pri->volume_space_size);
+ s->u.isofs_sb.s_log_zone_size = isonum_723 (pri->logical_block_size);
+ s->u.isofs_sb.s_max_size = isonum_733(pri->volume_space_size);
+ }
+
+ s->u.isofs_sb.s_ninodes = 0; /* No way to figure this out easily */
+
+ orig_zonesize = s -> u.isofs_sb.s_log_zone_size;
+ /*
+ * If the zone size is smaller than the hardware sector size,
+ * this is a fatal error. This would occur if the disc drive
+ * had sectors that were 2048 bytes, but the filesystem had
+ * blocks that were 512 bytes (which should only very rarely
+ * happen.)
+ */
+ if(blocksize != 0 && orig_zonesize < blocksize)
+ goto out_bad_size;
+
+ /* RDE: convert log zone size to bit shift */
+ switch (s -> u.isofs_sb.s_log_zone_size)
+ { case 512: s -> u.isofs_sb.s_log_zone_size = 9; break;
+ case 1024: s -> u.isofs_sb.s_log_zone_size = 10; break;
+ case 2048: s -> u.isofs_sb.s_log_zone_size = 11; break;
+
+ default:
+ goto out_bad_zone_size;
+ }
+
+ s->s_magic = ISOFS_SUPER_MAGIC;
+
+ /* The CDROM is read-only, has no nodes (devices) on it, and since
+ all of the files appear to be owned by root, we really do not want
+ to allow suid. (suid or devices will not show up unless we have
+ Rock Ridge extensions) */
+
+ s->s_flags |= MS_RDONLY /* | MS_NODEV | MS_NOSUID */;
+
+ /* Set this for reference. Its not currently used except on write
+ which we don't have .. */
+
+ /* RDE: data zone now byte offset! */
+
+ first_data_zone = ((isonum_733 (rootp->extent) +
+ isonum_711 (rootp->ext_attr_length))
+ << s -> u.isofs_sb.s_log_zone_size);
+ s->u.isofs_sb.s_firstdatazone = first_data_zone;
+#ifndef BEQUIET
+ printk(KERN_DEBUG "Max size:%ld Log zone size:%ld\n",
+ s->u.isofs_sb.s_max_size,
+ 1UL << s->u.isofs_sb.s_log_zone_size);
+ printk(KERN_DEBUG "First datazone:%ld Root inode number:%ld\n",
+ s->u.isofs_sb.s_firstdatazone >> s -> u.isofs_sb.s_log_zone_size,
+ s->u.isofs_sb.s_firstdatazone);
+ if(high_sierra)
+ printk(KERN_DEBUG "Disc in High Sierra format.\n");
+#endif
+
+ /*
+ * If the Joliet level is set, we _may_ decide to use the
+ * secondary descriptor, but can't be sure until after we
+ * read the root inode. But before reading the root inode
+ * we may need to change the device blocksize, and would
+ * rather release the old buffer first. So, we cache the
+ * first_data_zone value from the secondary descriptor.
+ */
+ if (joliet_level) {
+ pri = (struct iso_primary_descriptor *) sec;
+ rootp = (struct iso_directory_record *)
+ pri->root_directory_record;
+ first_data_zone = ((isonum_733 (rootp->extent) +
+ isonum_711 (rootp->ext_attr_length))
+ << s -> u.isofs_sb.s_log_zone_size);
+ }
+
+ /*
+ * We're all done using the volume descriptor, and may need
+ * to change the device blocksize, so release the buffer now.
+ */
+ brelse(pri_bh);
+ brelse(bh);
+
+ /*
+ * Force the blocksize to 512 for 512 byte sectors. The file
+ * read primitives really get it wrong in a bad way if we don't
+ * do this.
+ *
+ * Note - we should never be setting the blocksize to something
+ * less than the hardware sector size for the device. If we
+ * do, we would end up having to read larger buffers and split
+ * out portions to satisfy requests.
+ *
+ * Note2- the idea here is that we want to deal with the optimal
+ * zonesize in the filesystem. If we have it set to something less,
+ * then we have horrible problems with trying to piece together
+ * bits of adjacent blocks in order to properly read directory
+ * entries. By forcing the blocksize in this way, we ensure
+ * that we will never be required to do this.
+ */
+ if ( orig_zonesize != opt.blocksize ) {
+ set_blocksize(dev, orig_zonesize);
+#ifndef BEQUIET
+ printk(KERN_DEBUG
+ "ISOFS: Forcing new log zone size:%d\n", orig_zonesize);
+#endif
+ }
+ s->s_blocksize = orig_zonesize;
+ s->s_blocksize_bits = s -> u.isofs_sb.s_log_zone_size;
+
+ s->u.isofs_sb.s_nls_iocharset = NULL;
+
+#ifdef CONFIG_JOLIET
+ if (joliet_level && opt.utf8 == 0) {
+ char * p = opt.iocharset ? opt.iocharset : "iso8859-1";
+ s->u.isofs_sb.s_nls_iocharset = load_nls(p);
+ if (! s->u.isofs_sb.s_nls_iocharset) {
+ /* Fail only if explicit charset specified */
+ if (opt.iocharset)
+ goto out_unlock;
+ s->u.isofs_sb.s_nls_iocharset = load_nls_default();
+ }
+ }
+#endif
+ s->s_op = &isofs_sops;
+ s->u.isofs_sb.s_mapping = opt.map;
+ s->u.isofs_sb.s_rock = (opt.rock == 'y' ? 2 : 0);
+ s->u.isofs_sb.s_rock_offset = -1; /* initial offset, will guess until SP is found*/
+ s->u.isofs_sb.s_cruft = opt.cruft;
+ s->u.isofs_sb.s_unhide = opt.unhide;
+ s->u.isofs_sb.s_uid = opt.uid;
+ s->u.isofs_sb.s_gid = opt.gid;
+ s->u.isofs_sb.s_utf8 = opt.utf8;
+ s->u.isofs_sb.s_nocompress = opt.nocompress;
+ /*
+ * It would be incredibly stupid to allow people to mark every file
+ * on the disk as suid, so we merely allow them to set the default
+ * permissions.
+ */
+ s->u.isofs_sb.s_mode = opt.mode & 0777;
+
+ /*
+ * Read the root inode, which _may_ result in changing
+ * the s_rock flag. Once we have the final s_rock value,
+ * we then decide whether to use the Joliet descriptor.
+ */
+ inode = iget(s, s->u.isofs_sb.s_firstdatazone);
+
+ /*
+ * If this disk has both Rock Ridge and Joliet on it, then we
+ * want to use Rock Ridge by default. This can be overridden
+ * by using the norock mount option. There is still one other
+ * possibility that is not taken into account: a Rock Ridge
+ * CD with Unicode names. Until someone sees such a beast, it
+ * will not be supported.
+ */
+ if (s->u.isofs_sb.s_rock == 1) {
+ joliet_level = 0;
+ } else if (joliet_level) {
+ s->u.isofs_sb.s_rock = 0;
+ if (s->u.isofs_sb.s_firstdatazone != first_data_zone) {
+ s->u.isofs_sb.s_firstdatazone = first_data_zone;
+ printk(KERN_DEBUG
+ "ISOFS: changing to secondary root\n");
+ iput(inode);
+ inode = iget(s, s->u.isofs_sb.s_firstdatazone);
+ }
+ }
+
+ if (opt.check == 'u') {
+ /* Only Joliet is case insensitive by default */
+ if (joliet_level) opt.check = 'r';
+ else opt.check = 's';
+ }
+ s->u.isofs_sb.s_joliet_level = joliet_level;
+
+ /* check the root inode */
+ if (!inode)
+ goto out_no_root;
+ if (!inode->i_op)
+ goto out_bad_root;
+ /* get the root dentry */
+ s->s_root = d_alloc_root(inode);
+ if (!(s->s_root))
+ goto out_no_root;
+
+ table = 0;
+ if (joliet_level) table += 2;
+ if (opt.check == 'r') table++;
+ s->s_root->d_op = &isofs_dentry_ops[table];
+
+ return s;
+
+ /*
+ * Display error messages and free resources.
+ */
+out_bad_root:
+ printk(KERN_WARNING "isofs_read_super: root inode not initialized\n");
+ goto out_iput;
+out_no_root:
+ printk(KERN_WARNING "isofs_read_super: get root inode failed\n");
+out_iput:
+ iput(inode);
+#ifdef CONFIG_JOLIET
+ if (s->u.isofs_sb.s_nls_iocharset)
+ unload_nls(s->u.isofs_sb.s_nls_iocharset);
+#endif
+ goto out_unlock;
+out_no_read:
+ printk(KERN_WARNING "isofs_read_super: "
+ "bread failed, dev=%s, iso_blknum=%d, block=%d\n",
+ kdevname(dev), iso_blknum, block);
+ goto out_unlock;
+out_bad_zone_size:
+ printk(KERN_WARNING "Bad logical zone size %ld\n",
+ s->u.isofs_sb.s_log_zone_size);
+ goto out_freebh;
+out_bad_size:
+ printk(KERN_WARNING "Logical zone size(%d) < hardware blocksize(%u)\n",
+ orig_zonesize, blocksize);
+ goto out_freebh;
+out_unknown_format:
+ if (!silent)
+ printk(KERN_WARNING "Unable to identify CD-ROM format.\n");
+
+out_freebh:
+ brelse(bh);
+out_unlock:
+ return NULL;
+}
+
+static int isofs_statfs (struct super_block *sb, struct statfs *buf)
+{
+ buf->f_type = ISOFS_SUPER_MAGIC;
+ buf->f_bsize = sb->s_blocksize;
+ buf->f_blocks = (sb->u.isofs_sb.s_nzones
+ << (sb->u.isofs_sb.s_log_zone_size - sb->s_blocksize_bits));
+ buf->f_bfree = 0;
+ buf->f_bavail = 0;
+ buf->f_files = sb->u.isofs_sb.s_ninodes;
+ buf->f_ffree = 0;
+ buf->f_namelen = NAME_MAX;
+ return 0;
+}
+
+/*
+ * Get a set of blocks; filling in buffer_heads if already allocated
+ * or getblk() if they are not. Returns the number of blocks inserted
+ * (0 == error.)
+ */
+int isofs_get_blocks(struct inode *inode, long iblock,
+ struct buffer_head **bh_result, unsigned long nblocks)
+{
+ unsigned long b_off;
+ unsigned offset, sect_size;
+ unsigned int firstext;
+ unsigned long nextino;
+ int section, rv;
+
+ lock_kernel();
+
+ rv = 0;
+ if (iblock < 0) {
+ printk("isofs_get_blocks: block < 0\n");
+ goto abort;
+ }
+
+ b_off = iblock;
+
+ offset = 0;
+ firstext = inode->u.isofs_i.i_first_extent;
+ sect_size = inode->u.isofs_i.i_section_size >> ISOFS_BUFFER_BITS(inode);
+ nextino = inode->u.isofs_i.i_next_section_ino;
+ section = 0;
+
+ while ( nblocks ) {
+ /* If we are *way* beyond the end of the file, print a message.
+ * Access beyond the end of the file up to the next page boundary
+ * is normal, however because of the way the page cache works.
+ * In this case, we just return 0 so that we can properly fill
+ * the page with useless information without generating any
+ * I/O errors.
+ */
+ if (b_off > ((inode->i_size + PAGE_CACHE_SIZE - 1) >> ISOFS_BUFFER_BITS(inode))) {
+ printk("isofs_get_blocks: block >= EOF (%ld, %ld)\n",
+ iblock, (unsigned long) inode->i_size);
+ goto abort;
+ }
+
+ if (nextino) {
+ while (b_off >= (offset + sect_size)) {
+ struct inode *ninode;
+
+ offset += sect_size;
+ if (nextino == 0)
+ goto abort;
+ ninode = iget(inode->i_sb, nextino);
+ if (!ninode)
+ goto abort;
+ firstext = ninode->u.isofs_i.i_first_extent;
+ sect_size = ninode->u.isofs_i.i_section_size;
+ nextino = ninode->u.isofs_i.i_next_section_ino;
+ iput(ninode);
+
+ if (++section > 100) {
+ printk("isofs_get_blocks: More than 100 file sections ?!?, aborting...\n");
+ printk("isofs_get_blocks: ino=%lu block=%ld firstext=%u sect_size=%u nextino=%lu\n",
+ inode->i_ino, iblock, firstext, (unsigned) sect_size, nextino);
+ goto abort;
+ }
+ }
+ }
+
+ if ( *bh_result ) {
+ (*bh_result)->b_dev = inode->i_dev;
+ (*bh_result)->b_blocknr = firstext + b_off - offset;
+ (*bh_result)->b_state |= (1UL << BH_Mapped);
+ } else {
+ *bh_result = sb_getblk(inode->i_sb, firstext+b_off-offset);
+ if ( !*bh_result )
+ goto abort;
+ }
+ bh_result++; /* Next buffer head */
+ b_off++; /* Next buffer offset */
+ nblocks--;
+ rv++;
+ }
+
+
+abort:
+ unlock_kernel();
+ return rv;
+}
+
+/*
+ * Used by the standard interfaces.
+ */
+static int isofs_get_block(struct inode *inode, long iblock,
+ struct buffer_head *bh_result, int create)
+{
+ if ( create ) {
+ printk("isofs_get_block: Kernel tries to allocate a block\n");
+ return -EROFS;
+ }
+
+ return isofs_get_blocks(inode, iblock, &bh_result, 1) ? 0 : -EIO;
+}
+
+static int isofs_bmap(struct inode *inode, int block)
+{
+ struct buffer_head dummy;
+ int error;
+
+ dummy.b_state = 0;
+ dummy.b_blocknr = -1000;
+ error = isofs_get_block(inode, block, &dummy, 0);
+ if (!error)
+ return dummy.b_blocknr;
+ return 0;
+}
+
+struct buffer_head *isofs_bread(struct inode *inode, unsigned int block)
+{
+ unsigned int blknr = isofs_bmap(inode, block);
+ if (!blknr)
+ return NULL;
+ return sb_bread(inode->i_sb, blknr);
+}
+
+static int isofs_readpage(struct file *file, struct page *page)
+{
+ return block_read_full_page(page,isofs_get_block);
+}
+
+static int _isofs_bmap(struct address_space *mapping, long block)
+{
+ return generic_block_bmap(mapping,block,isofs_get_block);
+}
+
+static struct address_space_operations isofs_aops = {
+ readpage: isofs_readpage,
+ sync_page: block_sync_page,
+ bmap: _isofs_bmap
+};
+
+static inline void test_and_set_uid(uid_t *p, uid_t value)
+{
+ if(value) {
+ *p = value;
+ }
+}
+
+static inline void test_and_set_gid(gid_t *p, gid_t value)
+{
+ if(value) {
+ *p = value;
+ }
+}
+
+static int isofs_read_level3_size(struct inode * inode)
+{
+ unsigned long f_pos = inode->i_ino;
+ unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
+ int high_sierra = inode->i_sb->u.isofs_sb.s_high_sierra;
+ struct buffer_head * bh = NULL;
+ unsigned long block, offset;
+ int i = 0;
+ int more_entries = 0;
+ struct iso_directory_record * tmpde = NULL;
+
+ inode->i_size = 0;
+ inode->u.isofs_i.i_next_section_ino = 0;
+
+ block = f_pos >> ISOFS_BUFFER_BITS(inode);
+ offset = f_pos & (bufsize-1);
+
+ do {
+ struct iso_directory_record * de;
+ unsigned int de_len;
+
+ if (!bh) {
+ bh = sb_bread(inode->i_sb, block);
+ if (!bh)
+ goto out_noread;
+ }
+ de = (struct iso_directory_record *) (bh->b_data + offset);
+ de_len = *(unsigned char *) de;
+
+ if (de_len == 0) {
+ brelse(bh);
+ bh = NULL;
+ f_pos = (f_pos + ISOFS_BLOCK_SIZE) & ~(ISOFS_BLOCK_SIZE - 1);
+ block = f_pos >> ISOFS_BUFFER_BITS(inode);
+ offset = 0;
+ continue;
+ }
+
+ offset += de_len;
+
+ /* Make sure we have a full directory entry */
+ if (offset >= bufsize) {
+ int slop = bufsize - offset + de_len;
+ if (!tmpde) {
+ tmpde = kmalloc(256, GFP_KERNEL);
+ if (!tmpde)
+ goto out_nomem;
+ }
+ memcpy(tmpde, de, slop);
+ offset &= bufsize - 1;
+ block++;
+ brelse(bh);
+ bh = NULL;
+ if (offset) {
+ bh = sb_bread(inode->i_sb, block);
+ if (!bh)
+ goto out_noread;
+ memcpy((void *) tmpde + slop, bh->b_data, offset);
+ }
+ de = tmpde;
+ }
+
+ inode->i_size += isonum_733(de->size);
+ if (i == 1)
+ inode->u.isofs_i.i_next_section_ino = f_pos;
+
+ more_entries = de->flags[-high_sierra] & 0x80;
+
+ f_pos += de_len;
+ i++;
+ if(i > 100)
+ goto out_toomany;
+ } while(more_entries);
+out:
+ if (tmpde)
+ kfree(tmpde);
+ if (bh)
+ brelse(bh);
+ return 0;
+
+out_nomem:
+ if (bh)
+ brelse(bh);
+ return -ENOMEM;
+
+out_noread:
+ printk(KERN_INFO "ISOFS: unable to read i-node block %lu\n", block);
+ if (tmpde)
+ kfree(tmpde);
+ return -EIO;
+
+out_toomany:
+ printk(KERN_INFO "isofs_read_level3_size: "
+ "More than 100 file sections ?!?, aborting...\n"
+ "isofs_read_level3_size: inode=%lu ino=%lu\n",
+ inode->i_ino, f_pos);
+ goto out;
+}
+
+static void isofs_read_inode(struct inode * inode)
+{
+ struct super_block *sb = inode->i_sb;
+ unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
+ int block = inode->i_ino >> ISOFS_BUFFER_BITS(inode);
+ int high_sierra = sb->u.isofs_sb.s_high_sierra;
+ struct buffer_head * bh = NULL;
+ struct iso_directory_record * de;
+ struct iso_directory_record * tmpde = NULL;
+ unsigned int de_len;
+ unsigned long offset;
+ int volume_seq_no, i;
+
+ bh = sb_bread(inode->i_sb, block);
+ if (!bh)
+ goto out_badread;
+
+ offset = (inode->i_ino & (bufsize - 1));
+ de = (struct iso_directory_record *) (bh->b_data + offset);
+ de_len = *(unsigned char *) de;
+
+ if (offset + de_len > bufsize) {
+ int frag1 = bufsize - offset;
+
+ tmpde = kmalloc(de_len, GFP_KERNEL);
+ if (tmpde == NULL) {
+ printk(KERN_INFO "isofs_read_inode: out of memory\n");
+ goto fail;
+ }
+ memcpy(tmpde, bh->b_data + offset, frag1);
+ brelse(bh);
+ bh = sb_bread(inode->i_sb, ++block);
+ if (!bh)
+ goto out_badread;
+ memcpy((char *)tmpde+frag1, bh->b_data, de_len - frag1);
+ de = tmpde;
+ }
+
+ /* Assume it is a normal-format file unless told otherwise */
+ inode->u.isofs_i.i_file_format = isofs_file_normal;
+
+ if (de->flags[-high_sierra] & 2) {
+ inode->i_mode = S_IRUGO | S_IXUGO | S_IFDIR;
+ inode->i_nlink = 1; /* Set to 1. We know there are 2, but
+ the find utility tries to optimize
+ if it is 2, and it screws up. It is
+ easier to give 1 which tells find to
+ do it the hard way. */
+ } else {
+ /* Everybody gets to read the file. */
+ inode->i_mode = inode->i_sb->u.isofs_sb.s_mode;
+ inode->i_nlink = 1;
+ inode->i_mode |= S_IFREG;
+ /* If there are no periods in the name,
+ * then set the execute permission bit
+ */
+ for(i=0; i< de->name_len[0]; i++)
+ if(de->name[i]=='.' || de->name[i]==';')
+ break;
+ if(i == de->name_len[0] || de->name[i] == ';')
+ inode->i_mode |= S_IXUGO; /* execute permission */
+ }
+ inode->i_uid = inode->i_sb->u.isofs_sb.s_uid;
+ inode->i_gid = inode->i_sb->u.isofs_sb.s_gid;
+ inode->i_blocks = inode->i_blksize = 0;
+
+
+ inode->u.isofs_i.i_section_size = isonum_733 (de->size);
+ if(de->flags[-high_sierra] & 0x80) {
+ if(isofs_read_level3_size(inode)) goto fail;
+ } else {
+ inode->i_size = isonum_733 (de->size);
+ }
+
+ /*
+ * Some dipshit decided to store some other bit of information
+ * in the high byte of the file length. Truncate in case
+ * this CDROM was mounted with the cruft option.
+ */
+
+ if (inode->i_sb->u.isofs_sb.s_cruft == 'y')
+ inode->i_size &= 0x00ffffff;
+
+ if (de->interleave[0]) {
+ printk("Interleaved files not (yet) supported.\n");
+ inode->i_size = 0;
+ }
+
+ /* I have no idea what file_unit_size is used for, so
+ we will flag it for now */
+ if (de->file_unit_size[0] != 0) {
+ printk("File unit size != 0 for ISO file (%ld).\n",
+ inode->i_ino);
+ }
+
+ /* I have no idea what other flag bits are used for, so
+ we will flag it for now */
+#ifdef DEBUG
+ if((de->flags[-high_sierra] & ~2)!= 0){
+ printk("Unusual flag settings for ISO file (%ld %x).\n",
+ inode->i_ino, de->flags[-high_sierra]);
+ }
+#endif
+
+ inode->i_mtime = inode->i_atime = inode->i_ctime =
+ iso_date(de->date, high_sierra);
+
+ inode->u.isofs_i.i_first_extent = (isonum_733 (de->extent) +
+ isonum_711 (de->ext_attr_length));
+
+ /* Set the number of blocks for stat() - should be done before RR */
+ inode->i_blksize = PAGE_CACHE_SIZE; /* For stat() only */
+ inode->i_blocks = (inode->i_size + 511) >> 9;
+
+ /*
+ * Now test for possible Rock Ridge extensions which will override
+ * some of these numbers in the inode structure.
+ */
+
+ if (!high_sierra) {
+ parse_rock_ridge_inode(de, inode);
+ /* if we want uid/gid set, override the rock ridge setting */
+ test_and_set_uid(&inode->i_uid, inode->i_sb->u.isofs_sb.s_uid);
+ test_and_set_gid(&inode->i_gid, inode->i_sb->u.isofs_sb.s_gid);
+ }
+
+ /* get the volume sequence number */
+ volume_seq_no = isonum_723 (de->volume_sequence_number) ;
+
+ /* Install the inode operations vector */
+ if (S_ISREG(inode->i_mode)) {
+ inode->i_fop = &generic_ro_fops;
+ switch ( inode->u.isofs_i.i_file_format ) {
+#ifdef CONFIG_ZISOFS
+ case isofs_file_compressed:
+ inode->i_data.a_ops = &zisofs_aops;
+ break;
+#endif
+ default:
+ inode->i_data.a_ops = &isofs_aops;
+ break;
+ }
+ } else if (S_ISDIR(inode->i_mode)) {
+ inode->i_op = &isofs_dir_inode_operations;
+ inode->i_fop = &isofs_dir_operations;
+ } else if (S_ISLNK(inode->i_mode)) {
+ inode->i_op = &page_symlink_inode_operations;
+ inode->i_data.a_ops = &isofs_symlink_aops;
+ } else
+ /* XXX - parse_rock_ridge_inode() had already set i_rdev. */
+ init_special_inode(inode, inode->i_mode,
+ kdev_t_to_nr(inode->i_rdev));
+ out:
+ if (tmpde)
+ kfree(tmpde);
+ if (bh)
+ brelse(bh);
+ return;
+
+ out_badread:
+ printk(KERN_WARNING "ISOFS: unable to read i-node block\n");
+ fail:
+ make_bad_inode(inode);
+ goto out;
+}
+
+#ifdef LEAK_CHECK
+#undef malloc
+#undef free_s
+#undef sb_bread
+#undef brelse
+
+void * leak_check_malloc(unsigned int size){
+ void * tmp;
+ check_malloc++;
+ tmp = kmalloc(size, GFP_KERNEL);
+ return tmp;
+}
+
+void leak_check_free_s(void * obj, int size){
+ check_malloc--;
+ return kfree(obj);
+}
+
+struct buffer_head * leak_check_bread(struct super_block *sb, int block){
+ check_bread++;
+ return sb_bread(sb, block);
+}
+
+void leak_check_brelse(struct buffer_head * bh){
+ check_bread--;
+ return brelse(bh);
+}
+
+#endif
+
+static DECLARE_FSTYPE_DEV(iso9660_fs_type, "iso9660", isofs_read_super);
+
+static int __init init_iso9660_fs(void)
+{
+#ifdef CONFIG_ZISOFS
+ int err;
+
+ err = zisofs_init();
+ if ( err )
+ return err;
+#endif
+ return register_filesystem(&iso9660_fs_type);
+}
+
+static void __exit exit_iso9660_fs(void)
+{
+ unregister_filesystem(&iso9660_fs_type);
+#ifdef CONFIG_ZISOFS
+ zisofs_cleanup();
+#endif
+}
+
+EXPORT_NO_SYMBOLS;
+
+module_init(init_iso9660_fs)
+module_exit(exit_iso9660_fs)
+MODULE_LICENSE("GPL");
+
diff --git a/fs/isofs/joliet.c b/fs/isofs/joliet.c
new file mode 100644
index 00000000000000..427595f3840fca
--- /dev/null
+++ b/fs/isofs/joliet.c
@@ -0,0 +1,103 @@
+/*
+ * linux/fs/isofs/joliet.c
+ *
+ * (C) 1996 Gordon Chaffee
+ *
+ * Joliet: Microsoft's Unicode extensions to iso9660
+ */
+
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/slab.h>
+#include <linux/iso_fs.h>
+#include <asm/unaligned.h>
+
+/*
+ * Convert Unicode 16 to UTF8 or ASCII.
+ */
+static int
+uni16_to_x8(unsigned char *ascii, u16 *uni, int len, struct nls_table *nls)
+{
+ wchar_t *ip, ch;
+ unsigned char *op;
+
+ ip = uni;
+ op = ascii;
+
+ while ((ch = get_unaligned(ip)) && len) {
+ int llen;
+ ch = be16_to_cpu(ch);
+ if ((llen = nls->uni2char(ch, op, NLS_MAX_CHARSET_SIZE)) > 0)
+ op += llen;
+ else
+ *op++ = '?';
+ ip++;
+
+ len--;
+ }
+ *op = 0;
+ return (op - ascii);
+}
+
+/* Convert big endian wide character string to utf8 */
+static int
+wcsntombs_be(__u8 *s, const __u8 *pwcs, int inlen, int maxlen)
+{
+ const __u8 *ip;
+ __u8 *op;
+ int size;
+ __u16 c;
+
+ op = s;
+ ip = pwcs;
+ while ((*ip || ip[1]) && (maxlen > 0) && (inlen > 0)) {
+ c = (*ip << 8) | ip[1];
+ if (c > 0x7f) {
+ size = utf8_wctomb(op, c, maxlen);
+ if (size == -1) {
+ /* Ignore character and move on */
+ maxlen--;
+ } else {
+ op += size;
+ maxlen -= size;
+ }
+ } else {
+ *op++ = (__u8) c;
+ }
+ ip += 2;
+ inlen--;
+ }
+ return (op - s);
+}
+
+int
+get_joliet_filename(struct iso_directory_record * de, unsigned char *outname, struct inode * inode)
+{
+ unsigned char utf8;
+ struct nls_table *nls;
+ unsigned char len = 0;
+
+ utf8 = inode->i_sb->u.isofs_sb.s_utf8;
+ nls = inode->i_sb->u.isofs_sb.s_nls_iocharset;
+
+ if (utf8) {
+ len = wcsntombs_be(outname, de->name,
+ de->name_len[0] >> 1, PAGE_SIZE);
+ } else {
+ len = uni16_to_x8(outname, (u16 *) de->name,
+ de->name_len[0] >> 1, nls);
+ }
+ if ((len > 2) && (outname[len-2] == ';') && (outname[len-1] == '1')) {
+ len -= 2;
+ }
+
+ /*
+ * Windows doesn't like periods at the end of a name,
+ * so neither do we
+ */
+ while (len >= 2 && (outname[len-1] == '.')) {
+ len--;
+ }
+
+ return len;
+}
diff --git a/fs/isofs/namei.c b/fs/isofs/namei.c
new file mode 100644
index 00000000000000..13e79d4d5193c0
--- /dev/null
+++ b/fs/isofs/namei.c
@@ -0,0 +1,182 @@
+/*
+ * linux/fs/isofs/namei.c
+ *
+ * (C) 1992 Eric Youngdale Modified for ISO 9660 filesystem.
+ *
+ * (C) 1991 Linus Torvalds - minix filesystem
+ */
+
+#include <linux/sched.h>
+#include <linux/iso_fs.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/config.h> /* Joliet? */
+
+#include <asm/uaccess.h>
+
+/*
+ * ok, we cannot use strncmp, as the name is not in our data space.
+ * Thus we'll have to use isofs_match. No big problem. Match also makes
+ * some sanity tests.
+ */
+static int
+isofs_cmp(struct dentry * dentry, const char * compare, int dlen)
+{
+ struct qstr qstr;
+
+ if (!compare)
+ return 1;
+
+ /* check special "." and ".." files */
+ if (dlen == 1) {
+ /* "." */
+ if (compare[0] == 0) {
+ if (!dentry->d_name.len)
+ return 0;
+ compare = ".";
+ } else if (compare[0] == 1) {
+ compare = "..";
+ dlen = 2;
+ }
+ }
+
+ qstr.name = compare;
+ qstr.len = dlen;
+ return dentry->d_op->d_compare(dentry, &dentry->d_name, &qstr);
+}
+
+/*
+ * isofs_find_entry()
+ *
+ * finds an entry in the specified directory with the wanted name. It
+ * returns the inode number of the found entry, or 0 on error.
+ */
+static unsigned long
+isofs_find_entry(struct inode *dir, struct dentry *dentry,
+ char * tmpname, struct iso_directory_record * tmpde)
+{
+ unsigned long inode_number;
+ unsigned long bufsize = ISOFS_BUFFER_SIZE(dir);
+ unsigned char bufbits = ISOFS_BUFFER_BITS(dir);
+ unsigned int block, f_pos, offset;
+ struct buffer_head * bh = NULL;
+
+ if (!dir->u.isofs_i.i_first_extent)
+ return 0;
+
+ f_pos = 0;
+ offset = 0;
+ block = 0;
+
+ while (f_pos < dir->i_size) {
+ struct iso_directory_record * de;
+ int de_len, match, i, dlen;
+ char *dpnt;
+
+ if (!bh) {
+ bh = isofs_bread(dir, block);
+ if (!bh)
+ return 0;
+ }
+
+ de = (struct iso_directory_record *) (bh->b_data + offset);
+ inode_number = (bh->b_blocknr << bufbits) + offset;
+
+ de_len = *(unsigned char *) de;
+ if (!de_len) {
+ brelse(bh);
+ bh = NULL;
+ f_pos = (f_pos + ISOFS_BLOCK_SIZE) & ~(ISOFS_BLOCK_SIZE - 1);
+ block = f_pos >> bufbits;
+ offset = 0;
+ continue;
+ }
+
+ offset += de_len;
+ f_pos += de_len;
+
+ /* Make sure we have a full directory entry */
+ if (offset >= bufsize) {
+ int slop = bufsize - offset + de_len;
+ memcpy(tmpde, de, slop);
+ offset &= bufsize - 1;
+ block++;
+ brelse(bh);
+ bh = NULL;
+ if (offset) {
+ bh = isofs_bread(dir, block);
+ if (!bh)
+ return 0;
+ memcpy((void *) tmpde + slop, bh->b_data, offset);
+ }
+ de = tmpde;
+ }
+
+ dlen = de->name_len[0];
+ dpnt = de->name;
+
+ if (dir->i_sb->u.isofs_sb.s_rock &&
+ ((i = get_rock_ridge_filename(de, tmpname, dir)))) {
+ dlen = i; /* possibly -1 */
+ dpnt = tmpname;
+#ifdef CONFIG_JOLIET
+ } else if (dir->i_sb->u.isofs_sb.s_joliet_level) {
+ dlen = get_joliet_filename(de, tmpname, dir);
+ dpnt = tmpname;
+#endif
+ } else if (dir->i_sb->u.isofs_sb.s_mapping == 'a') {
+ dlen = get_acorn_filename(de, tmpname, dir);
+ dpnt = tmpname;
+ } else if (dir->i_sb->u.isofs_sb.s_mapping == 'n') {
+ dlen = isofs_name_translate(de, tmpname, dir);
+ dpnt = tmpname;
+ }
+
+ /*
+ * Skip hidden or associated files unless unhide is set
+ */
+ match = 0;
+ if (dlen > 0 &&
+ (!(de->flags[-dir->i_sb->u.isofs_sb.s_high_sierra] & 5)
+ || dir->i_sb->u.isofs_sb.s_unhide == 'y'))
+ {
+ match = (isofs_cmp(dentry,dpnt,dlen) == 0);
+ }
+ if (match) {
+ if (bh) brelse(bh);
+ return inode_number;
+ }
+ }
+ if (bh) brelse(bh);
+ return 0;
+}
+
+struct dentry *isofs_lookup(struct inode * dir, struct dentry * dentry)
+{
+ unsigned long ino;
+ struct inode *inode;
+ struct page *page;
+
+ dentry->d_op = dir->i_sb->s_root->d_op;
+
+ page = alloc_page(GFP_USER);
+ if (!page)
+ return ERR_PTR(-ENOMEM);
+
+ ino = isofs_find_entry(dir, dentry, page_address(page),
+ 1024 + page_address(page));
+ __free_page(page);
+
+ inode = NULL;
+ if (ino) {
+ inode = iget(dir->i_sb, ino);
+ if (!inode)
+ return ERR_PTR(-EACCES);
+ }
+ d_add(dentry, inode);
+ return NULL;
+}
diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c
new file mode 100644
index 00000000000000..71fc5797a147d6
--- /dev/null
+++ b/fs/isofs/rock.c
@@ -0,0 +1,623 @@
+/*
+ * linux/fs/isofs/rock.c
+ *
+ * (C) 1992, 1993 Eric Youngdale
+ *
+ * Rock Ridge Extensions to iso9660
+ */
+
+#include <linux/stat.h>
+#include <linux/sched.h>
+#include <linux/iso_fs.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/smp_lock.h>
+#include <asm/page.h>
+
+#include "rock.h"
+
+/* These functions are designed to read the system areas of a directory record
+ * and extract relevant information. There are different functions provided
+ * depending upon what information we need at the time. One function fills
+ * out an inode structure, a second one extracts a filename, a third one
+ * returns a symbolic link name, and a fourth one returns the extent number
+ * for the file. */
+
+#define SIG(A,B) ((A) | ((B) << 8)) /* isonum_721() */
+
+
+/* This is a way of ensuring that we have something in the system
+ use fields that is compatible with Rock Ridge */
+#define CHECK_SP(FAIL) \
+ if(rr->u.SP.magic[0] != 0xbe) FAIL; \
+ if(rr->u.SP.magic[1] != 0xef) FAIL; \
+ inode->i_sb->u.isofs_sb.s_rock_offset=rr->u.SP.skip;
+/* We define a series of macros because each function must do exactly the
+ same thing in certain places. We use the macros to ensure that everything
+ is done correctly */
+
+#define CONTINUE_DECLS \
+ int cont_extent = 0, cont_offset = 0, cont_size = 0; \
+ void * buffer = 0
+
+#define CHECK_CE \
+ {cont_extent = isonum_733(rr->u.CE.extent); \
+ cont_offset = isonum_733(rr->u.CE.offset); \
+ cont_size = isonum_733(rr->u.CE.size);}
+
+#define SETUP_ROCK_RIDGE(DE,CHR,LEN) \
+ {LEN= sizeof(struct iso_directory_record) + DE->name_len[0]; \
+ if(LEN & 1) LEN++; \
+ CHR = ((unsigned char *) DE) + LEN; \
+ LEN = *((unsigned char *) DE) - LEN; \
+ if (LEN<0) LEN=0; \
+ if (inode->i_sb->u.isofs_sb.s_rock_offset!=-1) \
+ { \
+ LEN-=inode->i_sb->u.isofs_sb.s_rock_offset; \
+ CHR+=inode->i_sb->u.isofs_sb.s_rock_offset; \
+ if (LEN<0) LEN=0; \
+ } \
+}
+
+#define MAYBE_CONTINUE(LABEL,DEV) \
+ {if (buffer) kfree(buffer); \
+ if (cont_extent){ \
+ int block, offset, offset1; \
+ struct buffer_head * pbh; \
+ buffer = kmalloc(cont_size,GFP_KERNEL); \
+ if (!buffer) goto out; \
+ block = cont_extent; \
+ offset = cont_offset; \
+ offset1 = 0; \
+ pbh = sb_bread(DEV->i_sb, block); \
+ if(pbh){ \
+ if (offset > pbh->b_size || offset + cont_size > pbh->b_size){ \
+ brelse(pbh); \
+ goto out; \
+ } \
+ memcpy(buffer + offset1, pbh->b_data + offset, cont_size - offset1); \
+ brelse(pbh); \
+ chr = (unsigned char *) buffer; \
+ len = cont_size; \
+ cont_extent = 0; \
+ cont_size = 0; \
+ cont_offset = 0; \
+ goto LABEL; \
+ } \
+ printk("Unable to read rock-ridge attributes\n"); \
+ }}
+
+/* This is the inner layer of the get filename routine, and is called
+ for each system area and continuation record related to the file */
+
+int find_rock_ridge_relocation(struct iso_directory_record * de,
+ struct inode * inode) {
+ int flag;
+ int len;
+ int retval;
+ unsigned char * chr;
+ CONTINUE_DECLS;
+ flag = 0;
+
+ /* If this is a '..' then we are looking for the parent, otherwise we
+ are looking for the child */
+
+ if (de->name[0]==1 && de->name_len[0]==1) flag = 1;
+ /* Return value if we do not find appropriate record. */
+ retval = isonum_733 (de->extent);
+
+ if (!inode->i_sb->u.isofs_sb.s_rock) return retval;
+
+ SETUP_ROCK_RIDGE(de, chr, len);
+ repeat:
+ {
+ int rrflag, sig;
+ struct rock_ridge * rr;
+
+ while (len > 1){ /* There may be one byte for padding somewhere */
+ rr = (struct rock_ridge *) chr;
+ if (rr->len == 0) goto out; /* Something got screwed up here */
+ sig = isonum_721(chr);
+ chr += rr->len;
+ len -= rr->len;
+
+ switch(sig){
+ case SIG('R','R'):
+ rrflag = rr->u.RR.flags[0];
+ if (flag && !(rrflag & RR_PL)) goto out;
+ if (!flag && !(rrflag & RR_CL)) goto out;
+ break;
+ case SIG('S','P'):
+ CHECK_SP(goto out);
+ break;
+ case SIG('C','L'):
+ if (flag == 0) {
+ retval = isonum_733(rr->u.CL.location);
+ goto out;
+ }
+ break;
+ case SIG('P','L'):
+ if (flag != 0) {
+ retval = isonum_733(rr->u.PL.location);
+ goto out;
+ }
+ break;
+ case SIG('C','E'):
+ CHECK_CE; /* This tells is if there is a continuation record */
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ MAYBE_CONTINUE(repeat, inode);
+ return retval;
+ out:
+ if(buffer) kfree(buffer);
+ return retval;
+}
+
+/* return length of name field; 0: not found, -1: to be ignored */
+int get_rock_ridge_filename(struct iso_directory_record * de,
+ char * retname, struct inode * inode)
+{
+ int len;
+ unsigned char * chr;
+ CONTINUE_DECLS;
+ int retnamlen = 0, truncate=0;
+
+ if (!inode->i_sb->u.isofs_sb.s_rock) return 0;
+ *retname = 0;
+
+ SETUP_ROCK_RIDGE(de, chr, len);
+ repeat:
+ {
+ struct rock_ridge * rr;
+ int sig;
+
+ while (len > 2){ /* There may be one byte for padding somewhere */
+ rr = (struct rock_ridge *) chr;
+ if (rr->len < 3) goto out; /* Something got screwed up here */
+ sig = isonum_721(chr);
+ chr += rr->len;
+ len -= rr->len;
+ if (len < 0) goto out; /* corrupted isofs */
+
+ switch(sig){
+ case SIG('R','R'):
+ if((rr->u.RR.flags[0] & RR_NM) == 0) goto out;
+ break;
+ case SIG('S','P'):
+ CHECK_SP(goto out);
+ break;
+ case SIG('C','E'):
+ CHECK_CE;
+ break;
+ case SIG('N','M'):
+ if (truncate) break;
+ if (rr->len < 5) break;
+ /*
+ * If the flags are 2 or 4, this indicates '.' or '..'.
+ * We don't want to do anything with this, because it
+ * screws up the code that calls us. We don't really
+ * care anyways, since we can just use the non-RR
+ * name.
+ */
+ if (rr->u.NM.flags & 6) {
+ break;
+ }
+
+ if (rr->u.NM.flags & ~1) {
+ printk("Unsupported NM flag settings (%d)\n",rr->u.NM.flags);
+ break;
+ }
+ if((strlen(retname) + rr->len - 5) >= 254) {
+ truncate = 1;
+ break;
+ }
+ strncat(retname, rr->u.NM.name, rr->len - 5);
+ retnamlen += rr->len - 5;
+ break;
+ case SIG('R','E'):
+ if (buffer) kfree(buffer);
+ return -1;
+ default:
+ break;
+ }
+ }
+ }
+ MAYBE_CONTINUE(repeat,inode);
+ return retnamlen; /* If 0, this file did not have a NM field */
+ out:
+ if(buffer) kfree(buffer);
+ return 0;
+}
+
+int parse_rock_ridge_inode_internal(struct iso_directory_record * de,
+ struct inode * inode,int regard_xa){
+ int len;
+ unsigned char * chr;
+ int symlink_len = 0;
+ CONTINUE_DECLS;
+
+ if (!inode->i_sb->u.isofs_sb.s_rock) return 0;
+
+ SETUP_ROCK_RIDGE(de, chr, len);
+ if (regard_xa)
+ {
+ chr+=14;
+ len-=14;
+ if (len<0) len=0;
+ };
+
+ repeat:
+ {
+ int cnt, sig;
+ struct inode * reloc;
+ struct rock_ridge * rr;
+ int rootflag;
+
+ while (len > 2){ /* There may be one byte for padding somewhere */
+ rr = (struct rock_ridge *) chr;
+ if (rr->len < 3) goto out; /* Something got screwed up here */
+ sig = isonum_721(chr);
+ chr += rr->len;
+ len -= rr->len;
+ if (len < 0) goto out; /* corrupted isofs */
+
+ switch(sig){
+#ifndef CONFIG_ZISOFS /* No flag for SF or ZF */
+ case SIG('R','R'):
+ if((rr->u.RR.flags[0] &
+ (RR_PX | RR_TF | RR_SL | RR_CL)) == 0) goto out;
+ break;
+#endif
+ case SIG('S','P'):
+ CHECK_SP(goto out);
+ break;
+ case SIG('C','E'):
+ CHECK_CE;
+ break;
+ case SIG('E','R'):
+ inode->i_sb->u.isofs_sb.s_rock = 1;
+ printk(KERN_DEBUG "ISO 9660 Extensions: ");
+ { int p;
+ for(p=0;p<rr->u.ER.len_id;p++) printk("%c",rr->u.ER.data[p]);
+ }
+ printk("\n");
+ break;
+ case SIG('P','X'):
+ inode->i_mode = isonum_733(rr->u.PX.mode);
+ inode->i_nlink = isonum_733(rr->u.PX.n_links);
+ inode->i_uid = isonum_733(rr->u.PX.uid);
+ inode->i_gid = isonum_733(rr->u.PX.gid);
+ break;
+ case SIG('P','N'):
+ { int high, low;
+ high = isonum_733(rr->u.PN.dev_high);
+ low = isonum_733(rr->u.PN.dev_low);
+ /*
+ * The Rock Ridge standard specifies that if sizeof(dev_t) <= 4,
+ * then the high field is unused, and the device number is completely
+ * stored in the low field. Some writers may ignore this subtlety,
+ * and as a result we test to see if the entire device number is
+ * stored in the low field, and use that.
+ */
+ if((low & ~0xff) && high == 0) {
+ inode->i_rdev = MKDEV(low >> 8, low & 0xff);
+ } else {
+ inode->i_rdev = MKDEV(high, low);
+ }
+ }
+ break;
+ case SIG('T','F'):
+ /* Some RRIP writers incorrectly place ctime in the TF_CREATE field.
+ Try to handle this correctly for either case. */
+ cnt = 0; /* Rock ridge never appears on a High Sierra disk */
+ if(rr->u.TF.flags & TF_CREATE)
+ inode->i_ctime = iso_date(rr->u.TF.times[cnt++].time, 0);
+ if(rr->u.TF.flags & TF_MODIFY)
+ inode->i_mtime = iso_date(rr->u.TF.times[cnt++].time, 0);
+ if(rr->u.TF.flags & TF_ACCESS)
+ inode->i_atime = iso_date(rr->u.TF.times[cnt++].time, 0);
+ if(rr->u.TF.flags & TF_ATTRIBUTES)
+ inode->i_ctime = iso_date(rr->u.TF.times[cnt++].time, 0);
+ break;
+ case SIG('S','L'):
+ {int slen;
+ struct SL_component * slp;
+ struct SL_component * oldslp;
+ slen = rr->len - 5;
+ slp = &rr->u.SL.link;
+ inode->i_size = symlink_len;
+ while (slen > 1){
+ rootflag = 0;
+ switch(slp->flags &~1){
+ case 0:
+ inode->i_size += slp->len;
+ break;
+ case 2:
+ inode->i_size += 1;
+ break;
+ case 4:
+ inode->i_size += 2;
+ break;
+ case 8:
+ rootflag = 1;
+ inode->i_size += 1;
+ break;
+ default:
+ printk("Symlink component flag not implemented\n");
+ }
+ slen -= slp->len + 2;
+ oldslp = slp;
+ slp = (struct SL_component *) (((char *) slp) + slp->len + 2);
+
+ if(slen < 2) {
+ if( ((rr->u.SL.flags & 1) != 0)
+ && ((oldslp->flags & 1) == 0) ) inode->i_size += 1;
+ break;
+ }
+
+ /*
+ * If this component record isn't continued, then append a '/'.
+ */
+ if (!rootflag && (oldslp->flags & 1) == 0)
+ inode->i_size += 1;
+ }
+ }
+ symlink_len = inode->i_size;
+ break;
+ case SIG('R','E'):
+ printk(KERN_WARNING "Attempt to read inode for relocated directory\n");
+ goto out;
+ case SIG('C','L'):
+ inode->u.isofs_i.i_first_extent = isonum_733(rr->u.CL.location);
+ reloc = iget(inode->i_sb,
+ (inode->u.isofs_i.i_first_extent <<
+ inode -> i_sb -> u.isofs_sb.s_log_zone_size));
+ if (!reloc)
+ goto out;
+ inode->i_mode = reloc->i_mode;
+ inode->i_nlink = reloc->i_nlink;
+ inode->i_uid = reloc->i_uid;
+ inode->i_gid = reloc->i_gid;
+ inode->i_rdev = reloc->i_rdev;
+ inode->i_size = reloc->i_size;
+ inode->i_blocks = reloc->i_blocks;
+ inode->i_atime = reloc->i_atime;
+ inode->i_ctime = reloc->i_ctime;
+ inode->i_mtime = reloc->i_mtime;
+ iput(reloc);
+ break;
+#ifdef CONFIG_ZISOFS
+ case SIG('Z','F'):
+ if ( !inode->i_sb->u.isofs_sb.s_nocompress ) {
+ int algo;
+ algo = isonum_721(rr->u.ZF.algorithm);
+ if ( algo == SIG('p','z') ) {
+ int block_shift = isonum_711(&rr->u.ZF.parms[1]);
+ if ( block_shift < PAGE_CACHE_SHIFT || block_shift > 17 ) {
+ printk(KERN_WARNING "isofs: Can't handle ZF block size of 2^%d\n", block_shift);
+ } else {
+ /* Note: we don't change i_blocks here */
+ inode->u.isofs_i.i_file_format = isofs_file_compressed;
+ /* Parameters to compression algorithm (header size, block size) */
+ inode->u.isofs_i.i_format_parm[0] = isonum_711(&rr->u.ZF.parms[0]);
+ inode->u.isofs_i.i_format_parm[1] = isonum_711(&rr->u.ZF.parms[1]);
+ inode->i_size = isonum_733(rr->u.ZF.real_size);
+ }
+ } else {
+ printk(KERN_WARNING "isofs: Unknown ZF compression algorithm: %c%c\n",
+ rr->u.ZF.algorithm[0], rr->u.ZF.algorithm[1]);
+ }
+ }
+ break;
+#endif
+ default:
+ break;
+ }
+ }
+ }
+ MAYBE_CONTINUE(repeat,inode);
+ return 0;
+ out:
+ if(buffer) kfree(buffer);
+ return 0;
+}
+
+static char *get_symlink_chunk(char *rpnt, struct rock_ridge *rr, char *plimit)
+{
+ int slen;
+ int rootflag;
+ struct SL_component *oldslp;
+ struct SL_component *slp;
+ slen = rr->len - 5;
+ slp = &rr->u.SL.link;
+ while (slen > 1) {
+ rootflag = 0;
+ switch (slp->flags & ~1) {
+ case 0:
+ if (slp->len > plimit - rpnt)
+ return NULL;
+ memcpy(rpnt, slp->text, slp->len);
+ rpnt+=slp->len;
+ break;
+ case 2:
+ if (rpnt >= plimit)
+ return NULL;
+ *rpnt++='.';
+ break;
+ case 4:
+ if (2 > plimit - rpnt)
+ return NULL;
+ *rpnt++='.';
+ *rpnt++='.';
+ break;
+ case 8:
+ if (rpnt >= plimit)
+ return NULL;
+ rootflag = 1;
+ *rpnt++='/';
+ break;
+ default:
+ printk("Symlink component flag not implemented (%d)\n",
+ slp->flags);
+ }
+ slen -= slp->len + 2;
+ oldslp = slp;
+ slp = (struct SL_component *) ((char *) slp + slp->len + 2);
+
+ if (slen < 2) {
+ /*
+ * If there is another SL record, and this component
+ * record isn't continued, then add a slash.
+ */
+ if ((!rootflag) && (rr->u.SL.flags & 1) &&
+ !(oldslp->flags & 1)) {
+ if (rpnt >= plimit)
+ return NULL;
+ *rpnt++='/';
+ }
+ break;
+ }
+
+ /*
+ * If this component record isn't continued, then append a '/'.
+ */
+ if (!rootflag && !(oldslp->flags & 1)) {
+ if (rpnt >= plimit)
+ return NULL;
+ *rpnt++='/';
+ }
+ }
+ return rpnt;
+}
+
+int parse_rock_ridge_inode(struct iso_directory_record * de,
+ struct inode * inode)
+{
+ int result=parse_rock_ridge_inode_internal(de,inode,0);
+ /* if rockridge flag was reset and we didn't look for attributes
+ * behind eventual XA attributes, have a look there */
+ if ((inode->i_sb->u.isofs_sb.s_rock_offset==-1)
+ &&(inode->i_sb->u.isofs_sb.s_rock==2))
+ {
+ result=parse_rock_ridge_inode_internal(de,inode,14);
+ };
+ return result;
+};
+
+/* readpage() for symlinks: reads symlink contents into the page and either
+ makes it uptodate and returns 0 or returns error (-EIO) */
+
+static int rock_ridge_symlink_readpage(struct file *file, struct page *page)
+{
+ struct inode *inode = page->mapping->host;
+ char *link = kmap(page);
+ unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
+ unsigned char bufbits = ISOFS_BUFFER_BITS(inode);
+ struct buffer_head *bh;
+ char *rpnt = link;
+ unsigned char *pnt;
+ struct iso_directory_record *raw_inode;
+ CONTINUE_DECLS;
+ int block;
+ int sig;
+ int len;
+ unsigned char *chr;
+ struct rock_ridge *rr;
+
+ if (!inode->i_sb->u.isofs_sb.s_rock)
+ goto error;
+
+ block = inode->i_ino >> bufbits;
+ lock_kernel();
+ bh = sb_bread(inode->i_sb, block);
+ if (!bh)
+ goto out_noread;
+
+ pnt = (unsigned char *) bh->b_data + (inode->i_ino & (bufsize - 1));
+
+ raw_inode = (struct iso_directory_record *) pnt;
+
+ /*
+ * If we go past the end of the buffer, there is some sort of error.
+ */
+ if ((inode->i_ino & (bufsize - 1)) + *pnt > bufsize)
+ goto out_bad_span;
+
+ /* Now test for possible Rock Ridge extensions which will override
+ some of these numbers in the inode structure. */
+
+ SETUP_ROCK_RIDGE(raw_inode, chr, len);
+
+ repeat:
+ while (len > 2) { /* There may be one byte for padding somewhere */
+ rr = (struct rock_ridge *) chr;
+ if (rr->len < 3)
+ goto out; /* Something got screwed up here */
+ sig = isonum_721(chr);
+ chr += rr->len;
+ len -= rr->len;
+ if (len < 0)
+ goto out; /* corrupted isofs */
+
+ switch (sig) {
+ case SIG('R', 'R'):
+ if ((rr->u.RR.flags[0] & RR_SL) == 0)
+ goto out;
+ break;
+ case SIG('S', 'P'):
+ CHECK_SP(goto out);
+ break;
+ case SIG('S', 'L'):
+ rpnt = get_symlink_chunk(rpnt, rr,
+ link + (PAGE_SIZE - 1));
+ if (rpnt == NULL)
+ goto out;
+ break;
+ case SIG('C', 'E'):
+ /* This tells is if there is a continuation record */
+ CHECK_CE;
+ default:
+ break;
+ }
+ }
+ MAYBE_CONTINUE(repeat, inode);
+
+ if (rpnt == link)
+ goto fail;
+ brelse(bh);
+ *rpnt = '\0';
+ unlock_kernel();
+ SetPageUptodate(page);
+ kunmap(page);
+ UnlockPage(page);
+ return 0;
+
+ /* error exit from macro */
+ out:
+ if (buffer)
+ kfree(buffer);
+ goto fail;
+ out_noread:
+ printk("unable to read i-node block");
+ goto fail;
+ out_bad_span:
+ printk("symlink spans iso9660 blocks\n");
+ fail:
+ brelse(bh);
+ unlock_kernel();
+ error:
+ SetPageError(page);
+ kunmap(page);
+ UnlockPage(page);
+ return -EIO;
+}
+
+struct address_space_operations isofs_symlink_aops = {
+ readpage: rock_ridge_symlink_readpage
+};
diff --git a/fs/isofs/rock.h b/fs/isofs/rock.h
new file mode 100644
index 00000000000000..deaf5c8e8b4a56
--- /dev/null
+++ b/fs/isofs/rock.h
@@ -0,0 +1,119 @@
+/* These structs are used by the system-use-sharing protocol, in which the
+ Rock Ridge extensions are embedded. It is quite possible that other
+ extensions are present on the disk, and this is fine as long as they
+ all use SUSP */
+
+struct SU_SP{
+ unsigned char magic[2];
+ unsigned char skip;
+} __attribute__((packed));
+
+struct SU_CE{
+ char extent[8];
+ char offset[8];
+ char size[8];
+};
+
+struct SU_ER{
+ unsigned char len_id;
+ unsigned char len_des;
+ unsigned char len_src;
+ unsigned char ext_ver;
+ char data[0];
+} __attribute__((packed));
+
+struct RR_RR{
+ char flags[1];
+} __attribute__((packed));
+
+struct RR_PX{
+ char mode[8];
+ char n_links[8];
+ char uid[8];
+ char gid[8];
+};
+
+struct RR_PN{
+ char dev_high[8];
+ char dev_low[8];
+};
+
+
+struct SL_component{
+ unsigned char flags;
+ unsigned char len;
+ char text[0];
+} __attribute__((packed));
+
+struct RR_SL{
+ unsigned char flags;
+ struct SL_component link;
+} __attribute__((packed));
+
+struct RR_NM{
+ unsigned char flags;
+ char name[0];
+} __attribute__((packed));
+
+struct RR_CL{
+ char location[8];
+};
+
+struct RR_PL{
+ char location[8];
+};
+
+struct stamp{
+ char time[7];
+} __attribute__((packed));
+
+struct RR_TF{
+ char flags;
+ struct stamp times[0]; /* Variable number of these beasts */
+} __attribute__((packed));
+
+/* Linux-specific extension for transparent decompression */
+struct RR_ZF{
+ char algorithm[2];
+ char parms[2];
+ char real_size[8];
+};
+
+/* These are the bits and their meanings for flags in the TF structure. */
+#define TF_CREATE 1
+#define TF_MODIFY 2
+#define TF_ACCESS 4
+#define TF_ATTRIBUTES 8
+#define TF_BACKUP 16
+#define TF_EXPIRATION 32
+#define TF_EFFECTIVE 64
+#define TF_LONG_FORM 128
+
+struct rock_ridge{
+ char signature[2];
+ unsigned char len;
+ unsigned char version;
+ union{
+ struct SU_SP SP;
+ struct SU_CE CE;
+ struct SU_ER ER;
+ struct RR_RR RR;
+ struct RR_PX PX;
+ struct RR_PN PN;
+ struct RR_SL SL;
+ struct RR_NM NM;
+ struct RR_CL CL;
+ struct RR_PL PL;
+ struct RR_TF TF;
+ struct RR_ZF ZF;
+ } u;
+};
+
+#define RR_PX 1 /* POSIX attributes */
+#define RR_PN 2 /* POSIX devices */
+#define RR_SL 4 /* Symbolic link */
+#define RR_NM 8 /* Alternate Name */
+#define RR_CL 16 /* Child link */
+#define RR_PL 32 /* Parent link */
+#define RR_RE 64 /* Relocation directory */
+#define RR_TF 128 /* Timestamps */
diff --git a/fs/isofs/util.c b/fs/isofs/util.c
new file mode 100644
index 00000000000000..69a58cb025e4a3
--- /dev/null
+++ b/fs/isofs/util.c
@@ -0,0 +1,82 @@
+/*
+ * linux/fs/isofs/util.c
+ */
+
+#include <linux/time.h>
+#include <linux/iso_fs.h>
+
+/*
+ * We have to convert from a MM/DD/YY format to the Unix ctime format.
+ * We have to take into account leap years and all of that good stuff.
+ * Unfortunately, the kernel does not have the information on hand to
+ * take into account daylight savings time, but it shouldn't matter.
+ * The time stored should be localtime (with or without DST in effect),
+ * and the timezone offset should hold the offset required to get back
+ * to GMT. Thus we should always be correct.
+ */
+
+int iso_date(char * p, int flag)
+{
+ int year, month, day, hour, minute, second, tz;
+ int crtime, days, i;
+
+ year = p[0] - 70;
+ month = p[1];
+ day = p[2];
+ hour = p[3];
+ minute = p[4];
+ second = p[5];
+ if (flag == 0) tz = p[6]; /* High sierra has no time zone */
+ else tz = 0;
+
+ if (year < 0) {
+ crtime = 0;
+ } else {
+ int monlen[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
+
+ days = year * 365;
+ if (year > 2)
+ days += (year+1) / 4;
+ for (i = 1; i < month; i++)
+ days += monlen[i-1];
+ if (((year+2) % 4) == 0 && month > 2)
+ days++;
+ days += day - 1;
+ crtime = ((((days * 24) + hour) * 60 + minute) * 60)
+ + second;
+
+ /* sign extend */
+ if (tz & 0x80)
+ tz |= (-1 << 8);
+
+ /*
+ * The timezone offset is unreliable on some disks,
+ * so we make a sanity check. In no case is it ever
+ * more than 13 hours from GMT, which is 52*15min.
+ * The time is always stored in localtime with the
+ * timezone offset being what get added to GMT to
+ * get to localtime. Thus we need to subtract the offset
+ * to get to true GMT, which is what we store the time
+ * as internally. On the local system, the user may set
+ * their timezone any way they wish, of course, so GMT
+ * gets converted back to localtime on the receiving
+ * system.
+ *
+ * NOTE: mkisofs in versions prior to mkisofs-1.10 had
+ * the sign wrong on the timezone offset. This has now
+ * been corrected there too, but if you are getting screwy
+ * results this may be the explanation. If enough people
+ * complain, a user configuration option could be added
+ * to add the timezone offset in with the wrong sign
+ * for 'compatibility' with older discs, but I cannot see how
+ * it will matter that much.
+ *
+ * Thanks to kuhlmav@elec.canterbury.ac.nz (Volker Kuhlmann)
+ * for pointing out the sign error.
+ */
+ if (-52 <= tz && tz <= 52)
+ crtime -= tz * 15 * 60;
+ }
+ return crtime;
+}
+
diff --git a/fs/isofs/zisofs.h b/fs/isofs/zisofs.h
new file mode 100644
index 00000000000000..b49ead7297dc60
--- /dev/null
+++ b/fs/isofs/zisofs.h
@@ -0,0 +1,21 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2001 H. Peter Anvin - All Rights Reserved
+ *
+ * This program 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, Inc., 675 Mass Ave, Cambridge MA 02139,
+ * USA; either version 2 of the License, or (at your option) any later
+ * version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * Prototypes for functions exported from the compressed isofs subsystem
+ */
+
+#ifdef CONFIG_ZISOFS
+extern struct address_space_operations zisofs_aops;
+extern int __init zisofs_init(void);
+extern void __exit zisofs_cleanup(void);
+#endif
diff --git a/fs/jbd/Makefile b/fs/jbd/Makefile
new file mode 100644
index 00000000000000..8f52c14c83a576
--- /dev/null
+++ b/fs/jbd/Makefile
@@ -0,0 +1,15 @@
+#
+# fs/jbd/Makefile
+#
+# Makefile for the linux journaling routines.
+#
+
+export-objs := journal.o
+O_TARGET := jbd.o
+
+obj-y := transaction.o commit.o recovery.o checkpoint.o revoke.o journal.o
+
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
+
diff --git a/fs/jbd/checkpoint.c b/fs/jbd/checkpoint.c
new file mode 100644
index 00000000000000..7dbc718ceb668f
--- /dev/null
+++ b/fs/jbd/checkpoint.c
@@ -0,0 +1,606 @@
+/*
+ * linux/fs/checkpoint.c
+ *
+ * Written by Stephen C. Tweedie <sct@redhat.com>, 1999
+ *
+ * Copyright 1999 Red Hat Software --- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * Checkpoint routines for the generic filesystem journaling code.
+ * Part of the ext2fs journaling system.
+ *
+ * Checkpointing is the process of ensuring that a section of the log is
+ * committed fully to disk, so that that portion of the log can be
+ * reused.
+ */
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/jbd.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/locks.h>
+
+extern spinlock_t journal_datalist_lock;
+
+/*
+ * Unlink a buffer from a transaction.
+ *
+ * Called with journal_datalist_lock held.
+ */
+
+static inline void __buffer_unlink(struct journal_head *jh)
+{
+ transaction_t *transaction;
+
+ transaction = jh->b_cp_transaction;
+ jh->b_cp_transaction = NULL;
+
+ jh->b_cpnext->b_cpprev = jh->b_cpprev;
+ jh->b_cpprev->b_cpnext = jh->b_cpnext;
+ if (transaction->t_checkpoint_list == jh)
+ transaction->t_checkpoint_list = jh->b_cpnext;
+ if (transaction->t_checkpoint_list == jh)
+ transaction->t_checkpoint_list = NULL;
+}
+
+/*
+ * Try to release a checkpointed buffer from its transaction.
+ * Returns 1 if we released it.
+ * Requires journal_datalist_lock
+ */
+static int __try_to_free_cp_buf(struct journal_head *jh)
+{
+ int ret = 0;
+ struct buffer_head *bh = jh2bh(jh);
+
+ if (jh->b_jlist == BJ_None && !buffer_locked(bh) && !buffer_dirty(bh)) {
+ JBUFFER_TRACE(jh, "remove from checkpoint list");
+ __journal_remove_checkpoint(jh);
+ __journal_remove_journal_head(bh);
+ BUFFER_TRACE(bh, "release");
+ /* BUF_LOCKED -> BUF_CLEAN (fwiw) */
+ refile_buffer(bh);
+ __brelse(bh);
+ ret = 1;
+ }
+ return ret;
+}
+
+/*
+ * log_wait_for_space: wait until there is space in the journal.
+ *
+ * Called with the journal already locked, but it will be unlocked if we have
+ * to wait for a checkpoint to free up some space in the log.
+ */
+
+void log_wait_for_space(journal_t *journal, int nblocks)
+{
+ while (log_space_left(journal) < nblocks) {
+ if (journal->j_flags & JFS_ABORT)
+ return;
+ unlock_journal(journal);
+ down(&journal->j_checkpoint_sem);
+ lock_journal(journal);
+
+ /* Test again, another process may have checkpointed
+ * while we were waiting for the checkpoint lock */
+ if (log_space_left(journal) < nblocks) {
+ log_do_checkpoint(journal, nblocks);
+ }
+ up(&journal->j_checkpoint_sem);
+ }
+}
+
+/*
+ * Clean up a transaction's checkpoint list.
+ *
+ * We wait for any pending IO to complete and make sure any clean
+ * buffers are removed from the transaction.
+ *
+ * Return 1 if we performed any actions which might have destroyed the
+ * checkpoint. (journal_remove_checkpoint() deletes the transaction when
+ * the last checkpoint buffer is cleansed)
+ *
+ * Called with the journal locked.
+ * Called with journal_datalist_lock held.
+ */
+static int __cleanup_transaction(journal_t *journal, transaction_t *transaction)
+{
+ struct journal_head *jh, *next_jh, *last_jh;
+ struct buffer_head *bh;
+ int ret = 0;
+
+ assert_spin_locked(&journal_datalist_lock);
+ jh = transaction->t_checkpoint_list;
+ if (!jh)
+ return 0;
+
+ last_jh = jh->b_cpprev;
+ next_jh = jh;
+ do {
+ jh = next_jh;
+ bh = jh2bh(jh);
+ if (buffer_locked(bh)) {
+ atomic_inc(&bh->b_count);
+ spin_unlock(&journal_datalist_lock);
+ unlock_journal(journal);
+ wait_on_buffer(bh);
+ /* the journal_head may have gone by now */
+ BUFFER_TRACE(bh, "brelse");
+ __brelse(bh);
+ goto out_return_1;
+ }
+
+ if (jh->b_transaction != NULL) {
+ transaction_t *transaction = jh->b_transaction;
+ tid_t tid = transaction->t_tid;
+
+ spin_unlock(&journal_datalist_lock);
+ log_start_commit(journal, transaction);
+ unlock_journal(journal);
+ log_wait_commit(journal, tid);
+ goto out_return_1;
+ }
+
+ /*
+ * We used to test for (jh->b_list != BUF_CLEAN) here.
+ * But unmap_underlying_metadata() can place buffer onto
+ * BUF_CLEAN. Since refile_buffer() no longer takes buffers
+ * off checkpoint lists, we cope with it here
+ */
+ /*
+ * AKPM: I think the buffer_jdirty test is redundant - it
+ * shouldn't have NULL b_transaction?
+ */
+ next_jh = jh->b_cpnext;
+ if (!buffer_dirty(bh) && !buffer_jdirty(bh)) {
+ BUFFER_TRACE(bh, "remove from checkpoint");
+ __journal_remove_checkpoint(jh);
+ __journal_remove_journal_head(bh);
+ refile_buffer(bh);
+ __brelse(bh);
+ ret = 1;
+ }
+
+ jh = next_jh;
+ } while (jh != last_jh);
+
+ return ret;
+out_return_1:
+ lock_journal(journal);
+ spin_lock(&journal_datalist_lock);
+ return 1;
+}
+
+#define NR_BATCH 64
+
+static void __flush_batch(struct buffer_head **bhs, int *batch_count)
+{
+ int i;
+
+ spin_unlock(&journal_datalist_lock);
+ ll_rw_block(WRITE, *batch_count, bhs);
+ run_task_queue(&tq_disk);
+ spin_lock(&journal_datalist_lock);
+ for (i = 0; i < *batch_count; i++) {
+ struct buffer_head *bh = bhs[i];
+ clear_bit(BH_JWrite, &bh->b_state);
+ BUFFER_TRACE(bh, "brelse");
+ __brelse(bh);
+ }
+ *batch_count = 0;
+}
+
+/*
+ * Try to flush one buffer from the checkpoint list to disk.
+ *
+ * Return 1 if something happened which requires us to abort the current
+ * scan of the checkpoint list.
+ *
+ * Called with journal_datalist_lock held.
+ */
+static int __flush_buffer(journal_t *journal, struct journal_head *jh,
+ struct buffer_head **bhs, int *batch_count,
+ int *drop_count)
+{
+ struct buffer_head *bh = jh2bh(jh);
+ int ret = 0;
+
+ if (buffer_dirty(bh) && !buffer_locked(bh) && jh->b_jlist == BJ_None) {
+ J_ASSERT_JH(jh, jh->b_transaction == NULL);
+
+ /*
+ * Important: we are about to write the buffer, and
+ * possibly block, while still holding the journal lock.
+ * We cannot afford to let the transaction logic start
+ * messing around with this buffer before we write it to
+ * disk, as that would break recoverability.
+ */
+ BUFFER_TRACE(bh, "queue");
+ atomic_inc(&bh->b_count);
+ J_ASSERT_BH(bh, !test_bit(BH_JWrite, &bh->b_state));
+ set_bit(BH_JWrite, &bh->b_state);
+ bhs[*batch_count] = bh;
+ (*batch_count)++;
+ if (*batch_count == NR_BATCH) {
+ __flush_batch(bhs, batch_count);
+ ret = 1;
+ }
+ } else {
+ int last_buffer = 0;
+ if (jh->b_cpnext == jh) {
+ /* We may be about to drop the transaction. Tell the
+ * caller that the lists have changed.
+ */
+ last_buffer = 1;
+ }
+ if (__try_to_free_cp_buf(jh)) {
+ (*drop_count)++;
+ ret = last_buffer;
+ }
+ }
+ return ret;
+}
+
+
+/*
+ * Perform an actual checkpoint. We don't write out only enough to
+ * satisfy the current blocked requests: rather we submit a reasonably
+ * sized chunk of the outstanding data to disk at once for
+ * efficiency. log_wait_for_space() will retry if we didn't free enough.
+ *
+ * However, we _do_ take into account the amount requested so that once
+ * the IO has been queued, we can return as soon as enough of it has
+ * completed to disk.
+ *
+ * The journal should be locked before calling this function.
+ */
+
+/* @@@ `nblocks' is unused. Should it be used? */
+int log_do_checkpoint (journal_t *journal, int nblocks)
+{
+ transaction_t *transaction, *last_transaction, *next_transaction;
+ int result;
+ int target;
+ int batch_count = 0;
+ struct buffer_head *bhs[NR_BATCH];
+
+ jbd_debug(1, "Start checkpoint\n");
+
+ /*
+ * First thing: if there are any transactions in the log which
+ * don't need checkpointing, just eliminate them from the
+ * journal straight away.
+ */
+ result = cleanup_journal_tail(journal);
+ jbd_debug(1, "cleanup_journal_tail returned %d\n", result);
+ if (result <= 0)
+ return result;
+
+ /*
+ * OK, we need to start writing disk blocks. Try to free up a
+ * quarter of the log in a single checkpoint if we can.
+ */
+ /*
+ * AKPM: check this code. I had a feeling a while back that it
+ * degenerates into a busy loop at unmount time.
+ */
+ target = (journal->j_last - journal->j_first) / 4;
+
+ spin_lock(&journal_datalist_lock);
+repeat:
+ transaction = journal->j_checkpoint_transactions;
+ if (transaction == NULL)
+ goto done;
+ last_transaction = transaction->t_cpprev;
+ next_transaction = transaction;
+
+ do {
+ struct journal_head *jh, *last_jh, *next_jh;
+ int drop_count = 0;
+ int cleanup_ret, retry = 0;
+
+ transaction = next_transaction;
+ next_transaction = transaction->t_cpnext;
+ jh = transaction->t_checkpoint_list;
+ last_jh = jh->b_cpprev;
+ next_jh = jh;
+ do {
+ jh = next_jh;
+ next_jh = jh->b_cpnext;
+ retry = __flush_buffer(journal, jh, bhs, &batch_count,
+ &drop_count);
+ } while (jh != last_jh && !retry);
+ if (batch_count) {
+ __flush_batch(bhs, &batch_count);
+ goto repeat;
+ }
+ if (retry)
+ goto repeat;
+ /*
+ * We have walked the whole transaction list without
+ * finding anything to write to disk. We had better be
+ * able to make some progress or we are in trouble.
+ */
+ cleanup_ret = __cleanup_transaction(journal, transaction);
+ J_ASSERT(drop_count != 0 || cleanup_ret != 0);
+ goto repeat; /* __cleanup may have dropped lock */
+ } while (transaction != last_transaction);
+
+done:
+ spin_unlock(&journal_datalist_lock);
+ result = cleanup_journal_tail(journal);
+ if (result < 0)
+ return result;
+
+ return 0;
+}
+
+/*
+ * Check the list of checkpoint transactions for the journal to see if
+ * we have already got rid of any since the last update of the log tail
+ * in the journal superblock. If so, we can instantly roll the
+ * superblock forward to remove those transactions from the log.
+ *
+ * Return <0 on error, 0 on success, 1 if there was nothing to clean up.
+ *
+ * Called with the journal lock held.
+ *
+ * This is the only part of the journaling code which really needs to be
+ * aware of transaction aborts. Checkpointing involves writing to the
+ * main filesystem area rather than to the journal, so it can proceed
+ * even in abort state, but we must not update the journal superblock if
+ * we have an abort error outstanding.
+ */
+
+int cleanup_journal_tail(journal_t *journal)
+{
+ transaction_t * transaction;
+ tid_t first_tid;
+ unsigned long blocknr, freed;
+
+ /* OK, work out the oldest transaction remaining in the log, and
+ * the log block it starts at.
+ *
+ * If the log is now empty, we need to work out which is the
+ * next transaction ID we will write, and where it will
+ * start. */
+
+ /* j_checkpoint_transactions needs locking */
+ spin_lock(&journal_datalist_lock);
+ transaction = journal->j_checkpoint_transactions;
+ if (transaction) {
+ first_tid = transaction->t_tid;
+ blocknr = transaction->t_log_start;
+ } else if ((transaction = journal->j_committing_transaction) != NULL) {
+ first_tid = transaction->t_tid;
+ blocknr = transaction->t_log_start;
+ } else if ((transaction = journal->j_running_transaction) != NULL) {
+ first_tid = transaction->t_tid;
+ blocknr = journal->j_head;
+ } else {
+ first_tid = journal->j_transaction_sequence;
+ blocknr = journal->j_head;
+ }
+ spin_unlock(&journal_datalist_lock);
+ J_ASSERT (blocknr != 0);
+
+ /* If the oldest pinned transaction is at the tail of the log
+ already then there's not much we can do right now. */
+ if (journal->j_tail_sequence == first_tid)
+ return 1;
+
+ /* OK, update the superblock to recover the freed space.
+ * Physical blocks come first: have we wrapped beyond the end of
+ * the log? */
+ freed = blocknr - journal->j_tail;
+ if (blocknr < journal->j_tail)
+ freed = freed + journal->j_last - journal->j_first;
+
+ jbd_debug(1,
+ "Cleaning journal tail from %d to %d (offset %lu), "
+ "freeing %lu\n",
+ journal->j_tail_sequence, first_tid, blocknr, freed);
+
+ journal->j_free += freed;
+ journal->j_tail_sequence = first_tid;
+ journal->j_tail = blocknr;
+ if (!(journal->j_flags & JFS_ABORT))
+ journal_update_superblock(journal, 1);
+ return 0;
+}
+
+
+/* Checkpoint list management */
+
+/*
+ * journal_clean_checkpoint_list
+ *
+ * Find all the written-back checkpoint buffers in the journal and release them.
+ *
+ * Called with the journal locked.
+ * Called with journal_datalist_lock held.
+ * Returns number of bufers reaped (for debug)
+ */
+
+int __journal_clean_checkpoint_list(journal_t *journal)
+{
+ transaction_t *transaction, *last_transaction, *next_transaction;
+ int ret = 0;
+
+ transaction = journal->j_checkpoint_transactions;
+ if (transaction == 0)
+ goto out;
+
+ last_transaction = transaction->t_cpprev;
+ next_transaction = transaction;
+ do {
+ struct journal_head *jh;
+
+ transaction = next_transaction;
+ next_transaction = transaction->t_cpnext;
+ jh = transaction->t_checkpoint_list;
+ if (jh) {
+ struct journal_head *last_jh = jh->b_cpprev;
+ struct journal_head *next_jh = jh;
+ do {
+ jh = next_jh;
+ next_jh = jh->b_cpnext;
+ ret += __try_to_free_cp_buf(jh);
+ } while (jh != last_jh);
+ }
+ } while (transaction != last_transaction);
+out:
+ return ret;
+}
+
+/*
+ * journal_remove_checkpoint: called after a buffer has been committed
+ * to disk (either by being write-back flushed to disk, or being
+ * committed to the log).
+ *
+ * We cannot safely clean a transaction out of the log until all of the
+ * buffer updates committed in that transaction have safely been stored
+ * elsewhere on disk. To achieve this, all of the buffers in a
+ * transaction need to be maintained on the transaction's checkpoint
+ * list until they have been rewritten, at which point this function is
+ * called to remove the buffer from the existing transaction's
+ * checkpoint list.
+ *
+ * This function is called with the journal locked.
+ * This function is called with journal_datalist_lock held.
+ */
+
+void __journal_remove_checkpoint(struct journal_head *jh)
+{
+ transaction_t *transaction;
+ journal_t *journal;
+
+ JBUFFER_TRACE(jh, "entry");
+
+ if ((transaction = jh->b_cp_transaction) == NULL) {
+ JBUFFER_TRACE(jh, "not on transaction");
+ goto out;
+ }
+
+ journal = transaction->t_journal;
+
+ __buffer_unlink(jh);
+
+ if (transaction->t_checkpoint_list != NULL)
+ goto out;
+ JBUFFER_TRACE(jh, "transaction has no more buffers");
+
+ /* There is one special case to worry about: if we have just
+ pulled the buffer off a committing transaction's forget list,
+ then even if the checkpoint list is empty, the transaction
+ obviously cannot be dropped! */
+
+ if (transaction == journal->j_committing_transaction) {
+ JBUFFER_TRACE(jh, "belongs to committing transaction");
+ goto out;
+ }
+
+ /* OK, that was the last buffer for the transaction: we can now
+ safely remove this transaction from the log */
+
+ __journal_drop_transaction(journal, transaction);
+
+ /* Just in case anybody was waiting for more transactions to be
+ checkpointed... */
+ wake_up(&journal->j_wait_logspace);
+out:
+ JBUFFER_TRACE(jh, "exit");
+}
+
+void journal_remove_checkpoint(struct journal_head *jh)
+{
+ spin_lock(&journal_datalist_lock);
+ __journal_remove_checkpoint(jh);
+ spin_unlock(&journal_datalist_lock);
+}
+
+/*
+ * journal_insert_checkpoint: put a committed buffer onto a checkpoint
+ * list so that we know when it is safe to clean the transaction out of
+ * the log.
+ *
+ * Called with the journal locked.
+ * Called with journal_datalist_lock held.
+ */
+void __journal_insert_checkpoint(struct journal_head *jh,
+ transaction_t *transaction)
+{
+ JBUFFER_TRACE(jh, "entry");
+ J_ASSERT_JH(jh, buffer_dirty(jh2bh(jh)) || buffer_jdirty(jh2bh(jh)));
+ J_ASSERT_JH(jh, jh->b_cp_transaction == NULL);
+
+ assert_spin_locked(&journal_datalist_lock);
+ jh->b_cp_transaction = transaction;
+
+ if (!transaction->t_checkpoint_list) {
+ jh->b_cpnext = jh->b_cpprev = jh;
+ } else {
+ jh->b_cpnext = transaction->t_checkpoint_list;
+ jh->b_cpprev = transaction->t_checkpoint_list->b_cpprev;
+ jh->b_cpprev->b_cpnext = jh;
+ jh->b_cpnext->b_cpprev = jh;
+ }
+ transaction->t_checkpoint_list = jh;
+}
+
+void journal_insert_checkpoint(struct journal_head *jh,
+ transaction_t *transaction)
+{
+ spin_lock(&journal_datalist_lock);
+ __journal_insert_checkpoint(jh, transaction);
+ spin_unlock(&journal_datalist_lock);
+}
+
+/*
+ * We've finished with this transaction structure: adios...
+ *
+ * The transaction must have no links except for the checkpoint by this
+ * point.
+ *
+ * Called with the journal locked.
+ * Called with journal_datalist_lock held.
+ */
+
+void __journal_drop_transaction(journal_t *journal, transaction_t *transaction)
+{
+ assert_spin_locked(&journal_datalist_lock);
+ if (transaction->t_cpnext) {
+ transaction->t_cpnext->t_cpprev = transaction->t_cpprev;
+ transaction->t_cpprev->t_cpnext = transaction->t_cpnext;
+ if (journal->j_checkpoint_transactions == transaction)
+ journal->j_checkpoint_transactions =
+ transaction->t_cpnext;
+ if (journal->j_checkpoint_transactions == transaction)
+ journal->j_checkpoint_transactions = NULL;
+ }
+
+ J_ASSERT (transaction->t_ilist == NULL);
+ J_ASSERT (transaction->t_buffers == NULL);
+ J_ASSERT (transaction->t_sync_datalist == NULL);
+ J_ASSERT (transaction->t_async_datalist == NULL);
+ J_ASSERT (transaction->t_forget == NULL);
+ J_ASSERT (transaction->t_iobuf_list == NULL);
+ J_ASSERT (transaction->t_shadow_list == NULL);
+ J_ASSERT (transaction->t_log_list == NULL);
+ J_ASSERT (transaction->t_checkpoint_list == NULL);
+ J_ASSERT (transaction->t_updates == 0);
+ J_ASSERT (list_empty(&transaction->t_jcb));
+
+ J_ASSERT (transaction->t_journal->j_committing_transaction !=
+ transaction);
+
+ jbd_debug (1, "Dropping transaction %d, all done\n",
+ transaction->t_tid);
+ kfree (transaction);
+}
+
diff --git a/fs/jbd/commit.c b/fs/jbd/commit.c
new file mode 100644
index 00000000000000..0fe067fc3ba941
--- /dev/null
+++ b/fs/jbd/commit.c
@@ -0,0 +1,811 @@
+/*
+ * linux/fs/commit.c
+ *
+ * Written by Stephen C. Tweedie <sct@redhat.com>, 1998
+ *
+ * Copyright 1998 Red Hat corp --- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * Journal commit routines for the generic filesystem journaling code;
+ * part of the ext2fs journaling system.
+ */
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/jbd.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/locks.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/smp_lock.h>
+
+extern spinlock_t journal_datalist_lock;
+
+/*
+ * Default IO end handler for temporary BJ_IO buffer_heads.
+ */
+void journal_end_buffer_io_sync(struct buffer_head *bh, int uptodate)
+{
+ BUFFER_TRACE(bh, "");
+ mark_buffer_uptodate(bh, uptodate);
+ unlock_buffer(bh);
+}
+
+/*
+ * When an ext3-ordered file is truncated, it is possible that many pages are
+ * not sucessfully freed, because they are attached to a committing transaction.
+ * After the transaction commits, these pages are left on the LRU, with no
+ * ->mapping, and with attached buffers. These pages are trivially reclaimable
+ * by the VM, but their apparent absence upsets the VM accounting, and it makes
+ * the numbers in /proc/meminfo look odd.
+ *
+ * So here, we have a buffer which has just come off the forget list. Look to
+ * see if we can strip all buffers from the backing page.
+ *
+ * Called under lock_journal(), and possibly under journal_datalist_lock. The
+ * caller provided us with a ref against the buffer, and we drop that here.
+ */
+static void release_buffer_page(struct buffer_head *bh)
+{
+ struct page *page;
+
+ if (buffer_dirty(bh))
+ goto nope;
+ if (atomic_read(&bh->b_count) != 1)
+ goto nope;
+ page = bh->b_page;
+ if (!page)
+ goto nope;
+ if (page->mapping)
+ goto nope;
+
+ /* OK, it's a truncated page */
+ if (TryLockPage(page))
+ goto nope;
+
+ page_cache_get(page);
+ __brelse(bh);
+ try_to_free_buffers(page, GFP_NOIO);
+ unlock_page(page);
+ page_cache_release(page);
+ return;
+
+nope:
+ __brelse(bh);
+}
+
+/*
+ * journal_commit_transaction
+ *
+ * The primary function for committing a transaction to the log. This
+ * function is called by the journal thread to begin a complete commit.
+ */
+void journal_commit_transaction(journal_t *journal)
+{
+ transaction_t *commit_transaction;
+ struct journal_head *jh, *new_jh, *descriptor;
+ struct journal_head *next_jh, *last_jh;
+ struct buffer_head *wbuf[64];
+ int bufs;
+ int flags;
+ int err;
+ unsigned long blocknr;
+ char *tagp = NULL;
+ journal_header_t *header;
+ journal_block_tag_t *tag = NULL;
+ int space_left = 0;
+ int first_tag = 0;
+ int tag_flag;
+ int i;
+
+ /*
+ * First job: lock down the current transaction and wait for
+ * all outstanding updates to complete.
+ */
+
+ lock_journal(journal); /* Protect journal->j_running_transaction */
+
+#ifdef COMMIT_STATS
+ spin_lock(&journal_datalist_lock);
+ summarise_journal_usage(journal);
+ spin_unlock(&journal_datalist_lock);
+#endif
+
+ lock_kernel();
+
+ J_ASSERT (journal->j_running_transaction != NULL);
+ J_ASSERT (journal->j_committing_transaction == NULL);
+
+ commit_transaction = journal->j_running_transaction;
+ J_ASSERT (commit_transaction->t_state == T_RUNNING);
+
+ jbd_debug (1, "JBD: starting commit of transaction %d\n",
+ commit_transaction->t_tid);
+
+ commit_transaction->t_state = T_LOCKED;
+ while (commit_transaction->t_updates != 0) {
+ unlock_journal(journal);
+ sleep_on(&journal->j_wait_updates);
+ lock_journal(journal);
+ }
+
+ J_ASSERT (commit_transaction->t_outstanding_credits <=
+ journal->j_max_transaction_buffers);
+
+ /* Do we need to erase the effects of a prior journal_flush? */
+ if (journal->j_flags & JFS_FLUSHED) {
+ jbd_debug(3, "super block updated\n");
+ journal_update_superblock(journal, 1);
+ } else {
+ jbd_debug(3, "superblock not updated\n");
+ }
+
+ /*
+ * First thing we are allowed to do is to discard any remaining
+ * BJ_Reserved buffers. Note, it is _not_ permissible to assume
+ * that there are no such buffers: if a large filesystem
+ * operation like a truncate needs to split itself over multiple
+ * transactions, then it may try to do a journal_restart() while
+ * there are still BJ_Reserved buffers outstanding. These must
+ * be released cleanly from the current transaction.
+ *
+ * In this case, the filesystem must still reserve write access
+ * again before modifying the buffer in the new transaction, but
+ * we do not require it to remember exactly which old buffers it
+ * has reserved. This is consistent with the existing behaviour
+ * that multiple journal_get_write_access() calls to the same
+ * buffer are perfectly permissable.
+ */
+
+ while (commit_transaction->t_reserved_list) {
+ jh = commit_transaction->t_reserved_list;
+ JBUFFER_TRACE(jh, "reserved, unused: refile");
+ journal_refile_buffer(jh);
+ }
+
+ /*
+ * Now try to drop any written-back buffers from the journal's
+ * checkpoint lists. We do this *before* commit because it potentially
+ * frees some memory
+ */
+ spin_lock(&journal_datalist_lock);
+ __journal_clean_checkpoint_list(journal);
+ spin_unlock(&journal_datalist_lock);
+
+ /* First part of the commit: force the revoke list out to disk.
+ * The revoke code generates its own metadata blocks on disk for this.
+ *
+ * It is important that we do this while the transaction is
+ * still locked. Generating the revoke records should not
+ * generate any IO stalls, so this should be quick; and doing
+ * the work while we have the transaction locked means that we
+ * only ever have to maintain the revoke list for one
+ * transaction at a time.
+ */
+
+ jbd_debug (3, "JBD: commit phase 1\n");
+
+ journal_write_revoke_records(journal, commit_transaction);
+
+ /*
+ * Now that we have built the revoke records, we can start
+ * reusing the revoke list for a new running transaction. We
+ * can now safely start committing the old transaction: time to
+ * get a new running transaction for incoming filesystem updates
+ */
+
+ commit_transaction->t_state = T_FLUSH;
+
+ wake_up(&journal->j_wait_transaction_locked);
+
+ journal->j_committing_transaction = commit_transaction;
+ journal->j_running_transaction = NULL;
+
+ commit_transaction->t_log_start = journal->j_head;
+
+ unlock_kernel();
+
+ jbd_debug (3, "JBD: commit phase 2\n");
+
+ /*
+ * Now start flushing things to disk, in the order they appear
+ * on the transaction lists. Data blocks go first.
+ */
+
+ /*
+ * Whenever we unlock the journal and sleep, things can get added
+ * onto ->t_datalist, so we have to keep looping back to write_out_data
+ * until we *know* that the list is empty.
+ */
+write_out_data:
+
+ /*
+ * Cleanup any flushed data buffers from the data list. Even in
+ * abort mode, we want to flush this out as soon as possible.
+ *
+ * We take journal_datalist_lock to protect the lists from
+ * journal_try_to_free_buffers().
+ */
+ spin_lock(&journal_datalist_lock);
+
+write_out_data_locked:
+ bufs = 0;
+ next_jh = commit_transaction->t_sync_datalist;
+ if (next_jh == NULL)
+ goto sync_datalist_empty;
+ last_jh = next_jh->b_tprev;
+
+ do {
+ struct buffer_head *bh;
+
+ jh = next_jh;
+ next_jh = jh->b_tnext;
+ bh = jh2bh(jh);
+ if (!buffer_locked(bh)) {
+ if (buffer_dirty(bh)) {
+ BUFFER_TRACE(bh, "start journal writeout");
+ atomic_inc(&bh->b_count);
+ wbuf[bufs++] = bh;
+ } else {
+ BUFFER_TRACE(bh, "writeout complete: unfile");
+ __journal_unfile_buffer(jh);
+ jh->b_transaction = NULL;
+ __journal_remove_journal_head(bh);
+ refile_buffer(bh);
+ release_buffer_page(bh);
+ }
+ }
+ if (bufs == ARRAY_SIZE(wbuf)) {
+ /*
+ * Major speedup: start here on the next scan
+ */
+ J_ASSERT(commit_transaction->t_sync_datalist != 0);
+ commit_transaction->t_sync_datalist = jh;
+ break;
+ }
+ } while (jh != last_jh);
+
+ if (bufs || current->need_resched) {
+ jbd_debug(2, "submit %d writes\n", bufs);
+ spin_unlock(&journal_datalist_lock);
+ unlock_journal(journal);
+ if (bufs)
+ ll_rw_block(WRITE, bufs, wbuf);
+ if (current->need_resched)
+ schedule();
+ journal_brelse_array(wbuf, bufs);
+ lock_journal(journal);
+ spin_lock(&journal_datalist_lock);
+ if (bufs)
+ goto write_out_data_locked;
+ }
+
+ /*
+ * Wait for all previously submitted IO on the data list to complete.
+ */
+ jh = commit_transaction->t_sync_datalist;
+ if (jh == NULL)
+ goto sync_datalist_empty;
+
+ do {
+ struct buffer_head *bh;
+ jh = jh->b_tprev; /* Wait on the last written */
+ bh = jh2bh(jh);
+ if (buffer_locked(bh)) {
+ spin_unlock(&journal_datalist_lock);
+ unlock_journal(journal);
+ wait_on_buffer(bh);
+ /* the journal_head may have been removed now */
+ lock_journal(journal);
+ goto write_out_data;
+ } else if (buffer_dirty(bh)) {
+ goto write_out_data_locked;
+ }
+ } while (jh != commit_transaction->t_sync_datalist);
+ goto write_out_data_locked;
+
+sync_datalist_empty:
+ /*
+ * Wait for all the async writepage data. As they become unlocked
+ * in end_buffer_io_async(), the only place where they can be
+ * reaped is in try_to_free_buffers(), and we're locked against
+ * that.
+ */
+ while ((jh = commit_transaction->t_async_datalist)) {
+ struct buffer_head *bh = jh2bh(jh);
+ if (__buffer_state(bh, Freed)) {
+ BUFFER_TRACE(bh, "Cleaning freed buffer");
+ clear_bit(BH_Freed, &bh->b_state);
+ clear_bit(BH_Dirty, &bh->b_state);
+ }
+ if (buffer_locked(bh)) {
+ spin_unlock(&journal_datalist_lock);
+ unlock_journal(journal);
+ wait_on_buffer(bh);
+ lock_journal(journal);
+ spin_lock(&journal_datalist_lock);
+ continue; /* List may have changed */
+ }
+ if (jh->b_next_transaction) {
+ /*
+ * For writepage() buffers in journalled data mode: a
+ * later transaction may want the buffer for "metadata"
+ */
+ __journal_refile_buffer(jh);
+ } else {
+ BUFFER_TRACE(bh, "finished async writeout: unfile");
+ __journal_unfile_buffer(jh);
+ jh->b_transaction = NULL;
+ __journal_remove_journal_head(bh);
+ BUFFER_TRACE(bh, "finished async writeout: refile");
+ /* It can sometimes be on BUF_LOCKED due to migration
+ * from syncdata to asyncdata */
+ if (bh->b_list != BUF_CLEAN)
+ refile_buffer(bh);
+ __brelse(bh);
+ }
+ }
+ spin_unlock(&journal_datalist_lock);
+
+ /*
+ * If we found any dirty or locked buffers, then we should have
+ * looped back up to the write_out_data label. If there weren't
+ * any then journal_clean_data_list should have wiped the list
+ * clean by now, so check that it is in fact empty.
+ */
+ J_ASSERT (commit_transaction->t_sync_datalist == NULL);
+ J_ASSERT (commit_transaction->t_async_datalist == NULL);
+
+ jbd_debug (3, "JBD: commit phase 3\n");
+
+ /*
+ * Way to go: we have now written out all of the data for a
+ * transaction! Now comes the tricky part: we need to write out
+ * metadata. Loop over the transaction's entire buffer list:
+ */
+ commit_transaction->t_state = T_COMMIT;
+
+ descriptor = 0;
+ bufs = 0;
+ while (commit_transaction->t_buffers) {
+
+ /* Find the next buffer to be journaled... */
+
+ jh = commit_transaction->t_buffers;
+
+ /* If we're in abort mode, we just un-journal the buffer and
+ release it for background writing. */
+
+ if (is_journal_aborted(journal)) {
+ JBUFFER_TRACE(jh, "journal is aborting: refile");
+ journal_refile_buffer(jh);
+ /* If that was the last one, we need to clean up
+ * any descriptor buffers which may have been
+ * already allocated, even if we are now
+ * aborting. */
+ if (!commit_transaction->t_buffers)
+ goto start_journal_io;
+ continue;
+ }
+
+ /* Make sure we have a descriptor block in which to
+ record the metadata buffer. */
+
+ if (!descriptor) {
+ struct buffer_head *bh;
+
+ J_ASSERT (bufs == 0);
+
+ jbd_debug(4, "JBD: get descriptor\n");
+
+ descriptor = journal_get_descriptor_buffer(journal);
+ if (!descriptor) {
+ __journal_abort_hard(journal);
+ continue;
+ }
+
+ bh = jh2bh(descriptor);
+ jbd_debug(4, "JBD: got buffer %ld (%p)\n",
+ bh->b_blocknr, bh->b_data);
+ header = (journal_header_t *)&bh->b_data[0];
+ header->h_magic = htonl(JFS_MAGIC_NUMBER);
+ header->h_blocktype = htonl(JFS_DESCRIPTOR_BLOCK);
+ header->h_sequence = htonl(commit_transaction->t_tid);
+
+ tagp = &bh->b_data[sizeof(journal_header_t)];
+ space_left = bh->b_size - sizeof(journal_header_t);
+ first_tag = 1;
+ set_bit(BH_JWrite, &bh->b_state);
+ wbuf[bufs++] = bh;
+
+ /* Record it so that we can wait for IO
+ completion later */
+ BUFFER_TRACE(bh, "ph3: file as descriptor");
+ journal_file_buffer(descriptor, commit_transaction,
+ BJ_LogCtl);
+ }
+
+ /* Where is the buffer to be written? */
+
+ err = journal_next_log_block(journal, &blocknr);
+ /* If the block mapping failed, just abandon the buffer
+ and repeat this loop: we'll fall into the
+ refile-on-abort condition above. */
+ if (err) {
+ __journal_abort_hard(journal);
+ continue;
+ }
+
+ /* Bump b_count to prevent truncate from stumbling over
+ the shadowed buffer! @@@ This can go if we ever get
+ rid of the BJ_IO/BJ_Shadow pairing of buffers. */
+ atomic_inc(&jh2bh(jh)->b_count);
+
+ /* Make a temporary IO buffer with which to write it out
+ (this will requeue both the metadata buffer and the
+ temporary IO buffer). new_bh goes on BJ_IO*/
+
+ set_bit(BH_JWrite, &jh2bh(jh)->b_state);
+ /*
+ * akpm: journal_write_metadata_buffer() sets
+ * new_bh->b_transaction to commit_transaction.
+ * We need to clean this up before we release new_bh
+ * (which is of type BJ_IO)
+ */
+ JBUFFER_TRACE(jh, "ph3: write metadata");
+ flags = journal_write_metadata_buffer(commit_transaction,
+ jh, &new_jh, blocknr);
+ set_bit(BH_JWrite, &jh2bh(new_jh)->b_state);
+ set_bit(BH_Lock, &jh2bh(new_jh)->b_state);
+ wbuf[bufs++] = jh2bh(new_jh);
+
+ /* Record the new block's tag in the current descriptor
+ buffer */
+
+ tag_flag = 0;
+ if (flags & 1)
+ tag_flag |= JFS_FLAG_ESCAPE;
+ if (!first_tag)
+ tag_flag |= JFS_FLAG_SAME_UUID;
+
+ tag = (journal_block_tag_t *) tagp;
+ tag->t_blocknr = htonl(jh2bh(jh)->b_blocknr);
+ tag->t_flags = htonl(tag_flag);
+ tagp += sizeof(journal_block_tag_t);
+ space_left -= sizeof(journal_block_tag_t);
+
+ if (first_tag) {
+ memcpy (tagp, journal->j_uuid, 16);
+ tagp += 16;
+ space_left -= 16;
+ first_tag = 0;
+ }
+
+ /* If there's no more to do, or if the descriptor is full,
+ let the IO rip! */
+
+ if (bufs == ARRAY_SIZE(wbuf) ||
+ commit_transaction->t_buffers == NULL ||
+ space_left < sizeof(journal_block_tag_t) + 16) {
+
+ jbd_debug(4, "JBD: Submit %d IOs\n", bufs);
+
+ /* Write an end-of-descriptor marker before
+ submitting the IOs. "tag" still points to
+ the last tag we set up. */
+
+ tag->t_flags |= htonl(JFS_FLAG_LAST_TAG);
+
+start_journal_io:
+ unlock_journal(journal);
+ for (i=0; i<bufs; i++) {
+ struct buffer_head *bh = wbuf[i];
+ clear_bit(BH_Dirty, &bh->b_state);
+ bh->b_end_io = journal_end_buffer_io_sync;
+ submit_bh(WRITE, bh);
+ }
+ if (current->need_resched)
+ schedule();
+ lock_journal(journal);
+
+ /* Force a new descriptor to be generated next
+ time round the loop. */
+ descriptor = NULL;
+ bufs = 0;
+ }
+ }
+
+ /* Lo and behold: we have just managed to send a transaction to
+ the log. Before we can commit it, wait for the IO so far to
+ complete. Control buffers being written are on the
+ transaction's t_log_list queue, and metadata buffers are on
+ the t_iobuf_list queue.
+
+ Wait for the buffers in reverse order. That way we are
+ less likely to be woken up until all IOs have completed, and
+ so we incur less scheduling load.
+ */
+
+ jbd_debug(3, "JBD: commit phase 4\n");
+
+ /* akpm: these are BJ_IO, and journal_datalist_lock is not needed */
+ wait_for_iobuf:
+ while (commit_transaction->t_iobuf_list != NULL) {
+ struct buffer_head *bh;
+ jh = commit_transaction->t_iobuf_list->b_tprev;
+ bh = jh2bh(jh);
+ if (buffer_locked(bh)) {
+ unlock_journal(journal);
+ wait_on_buffer(bh);
+ lock_journal(journal);
+ goto wait_for_iobuf;
+ }
+
+ clear_bit(BH_JWrite, &jh2bh(jh)->b_state);
+
+ JBUFFER_TRACE(jh, "ph4: unfile after journal write");
+ journal_unfile_buffer(jh);
+
+ /*
+ * akpm: don't put back a buffer_head with stale pointers
+ * dangling around.
+ */
+ J_ASSERT_JH(jh, jh->b_transaction != NULL);
+ jh->b_transaction = NULL;
+
+ /*
+ * ->t_iobuf_list should contain only dummy buffer_heads
+ * which were created by journal_write_metadata_buffer().
+ */
+ bh = jh2bh(jh);
+ BUFFER_TRACE(bh, "dumping temporary bh");
+ journal_unlock_journal_head(jh);
+ __brelse(bh);
+ J_ASSERT_BH(bh, atomic_read(&bh->b_count) == 0);
+ put_unused_buffer_head(bh);
+
+ /* We also have to unlock and free the corresponding
+ shadowed buffer */
+ jh = commit_transaction->t_shadow_list->b_tprev;
+ bh = jh2bh(jh);
+ clear_bit(BH_JWrite, &bh->b_state);
+ J_ASSERT_BH(bh, buffer_jdirty(bh));
+
+ /* The metadata is now released for reuse, but we need
+ to remember it against this transaction so that when
+ we finally commit, we can do any checkpointing
+ required. */
+ JBUFFER_TRACE(jh, "file as BJ_Forget");
+ journal_file_buffer(jh, commit_transaction, BJ_Forget);
+ /* Wake up any transactions which were waiting for this
+ IO to complete */
+ wake_up(&bh->b_wait);
+ JBUFFER_TRACE(jh, "brelse shadowed buffer");
+ __brelse(bh);
+ }
+
+ J_ASSERT (commit_transaction->t_shadow_list == NULL);
+
+ jbd_debug(3, "JBD: commit phase 5\n");
+
+ /* Here we wait for the revoke record and descriptor record buffers */
+ wait_for_ctlbuf:
+ while (commit_transaction->t_log_list != NULL) {
+ struct buffer_head *bh;
+
+ jh = commit_transaction->t_log_list->b_tprev;
+ bh = jh2bh(jh);
+ if (buffer_locked(bh)) {
+ unlock_journal(journal);
+ wait_on_buffer(bh);
+ lock_journal(journal);
+ goto wait_for_ctlbuf;
+ }
+
+ BUFFER_TRACE(bh, "ph5: control buffer writeout done: unfile");
+ clear_bit(BH_JWrite, &bh->b_state);
+ journal_unfile_buffer(jh);
+ jh->b_transaction = NULL;
+ journal_unlock_journal_head(jh);
+ put_bh(bh); /* One for getblk */
+ }
+
+ jbd_debug(3, "JBD: commit phase 6\n");
+
+ if (is_journal_aborted(journal)) {
+ unlock_journal(journal);
+ goto skip_commit;
+ }
+
+ /* Done it all: now write the commit record. We should have
+ * cleaned up our previous buffers by now, so if we are in abort
+ * mode we can now just skip the rest of the journal write
+ * entirely. */
+
+ descriptor = journal_get_descriptor_buffer(journal);
+ if (!descriptor) {
+ __journal_abort_hard(journal);
+ unlock_journal(journal);
+ goto skip_commit;
+ }
+
+ /* AKPM: buglet - add `i' to tmp! */
+ for (i = 0; i < jh2bh(descriptor)->b_size; i += 512) {
+ journal_header_t *tmp =
+ (journal_header_t*)jh2bh(descriptor)->b_data;
+ tmp->h_magic = htonl(JFS_MAGIC_NUMBER);
+ tmp->h_blocktype = htonl(JFS_COMMIT_BLOCK);
+ tmp->h_sequence = htonl(commit_transaction->t_tid);
+ }
+
+ unlock_journal(journal);
+ JBUFFER_TRACE(descriptor, "write commit block");
+ {
+ struct buffer_head *bh = jh2bh(descriptor);
+ clear_bit(BH_Dirty, &bh->b_state);
+ bh->b_end_io = journal_end_buffer_io_sync;
+ submit_bh(WRITE, bh);
+ wait_on_buffer(bh);
+ put_bh(bh); /* One for getblk() */
+ journal_unlock_journal_head(descriptor);
+ }
+
+ /* End of a transaction! Finally, we can do checkpoint
+ processing: any buffers committed as a result of this
+ transaction can be removed from any checkpoint list it was on
+ before. */
+
+skip_commit: /* The journal should be unlocked by now. */
+
+ /* Call any callbacks that had been registered for handles in this
+ * transaction. It is up to the callback to free any allocated
+ * memory.
+ */
+ if (!list_empty(&commit_transaction->t_jcb)) {
+ struct list_head *p, *n;
+ int error = is_journal_aborted(journal);
+
+ list_for_each_safe(p, n, &commit_transaction->t_jcb) {
+ struct journal_callback *jcb;
+
+ jcb = list_entry(p, struct journal_callback, jcb_list);
+ list_del(p);
+ jcb->jcb_func(jcb, error);
+ }
+ }
+
+ lock_journal(journal);
+
+ jbd_debug(3, "JBD: commit phase 7\n");
+
+ J_ASSERT(commit_transaction->t_sync_datalist == NULL);
+ J_ASSERT(commit_transaction->t_async_datalist == NULL);
+ J_ASSERT(commit_transaction->t_buffers == NULL);
+ J_ASSERT(commit_transaction->t_checkpoint_list == NULL);
+ J_ASSERT(commit_transaction->t_iobuf_list == NULL);
+ J_ASSERT(commit_transaction->t_shadow_list == NULL);
+ J_ASSERT(commit_transaction->t_log_list == NULL);
+
+ while (commit_transaction->t_forget) {
+ transaction_t *cp_transaction;
+ struct buffer_head *bh;
+ int was_freed = 0;
+
+ jh = commit_transaction->t_forget;
+ J_ASSERT_JH(jh, jh->b_transaction == commit_transaction ||
+ jh->b_transaction == journal->j_running_transaction);
+
+ /*
+ * If there is undo-protected committed data against
+ * this buffer, then we can remove it now. If it is a
+ * buffer needing such protection, the old frozen_data
+ * field now points to a committed version of the
+ * buffer, so rotate that field to the new committed
+ * data.
+ *
+ * Otherwise, we can just throw away the frozen data now.
+ */
+ if (jh->b_committed_data) {
+ kfree(jh->b_committed_data);
+ jh->b_committed_data = NULL;
+ if (jh->b_frozen_data) {
+ jh->b_committed_data = jh->b_frozen_data;
+ jh->b_frozen_data = NULL;
+ }
+ } else if (jh->b_frozen_data) {
+ kfree(jh->b_frozen_data);
+ jh->b_frozen_data = NULL;
+ }
+
+ spin_lock(&journal_datalist_lock);
+ cp_transaction = jh->b_cp_transaction;
+ if (cp_transaction) {
+ JBUFFER_TRACE(jh, "remove from old cp transaction");
+ J_ASSERT_JH(jh, commit_transaction != cp_transaction);
+ __journal_remove_checkpoint(jh);
+ }
+
+ /* Only re-checkpoint the buffer_head if it is marked
+ * dirty. If the buffer was added to the BJ_Forget list
+ * by journal_forget, it may no longer be dirty and
+ * there's no point in keeping a checkpoint record for
+ * it. */
+ bh = jh2bh(jh);
+
+ /* A buffer which has been freed while still being
+ * journaled by a previous transaction may end up still
+ * being dirty here, but we want to avoid writing back
+ * that buffer in the future now that the last use has
+ * been committed. That's not only a performance gain,
+ * it also stops aliasing problems if the buffer is left
+ * behind for writeback and gets reallocated for another
+ * use in a different page. */
+ if (__buffer_state(bh, Freed)) {
+ was_freed = 1;
+ clear_bit(BH_Freed, &bh->b_state);
+ clear_bit(BH_JBDDirty, &bh->b_state);
+ }
+
+ if (buffer_jdirty(bh)) {
+ JBUFFER_TRACE(jh, "add to new checkpointing trans");
+ __journal_insert_checkpoint(jh, commit_transaction);
+ JBUFFER_TRACE(jh, "refile for checkpoint writeback");
+ __journal_refile_buffer(jh);
+ } else {
+ J_ASSERT_BH(bh, !buffer_dirty(bh));
+ J_ASSERT_JH(jh, jh->b_next_transaction == NULL);
+ __journal_unfile_buffer(jh);
+ jh->b_transaction = 0;
+ __journal_remove_journal_head(bh);
+ spin_unlock(&journal_datalist_lock);
+ if (was_freed)
+ release_buffer_page(bh);
+ else
+ __brelse(bh);
+ continue;
+ }
+ spin_unlock(&journal_datalist_lock);
+ }
+
+ /* Done with this transaction! */
+
+ jbd_debug(3, "JBD: commit phase 8\n");
+
+ J_ASSERT (commit_transaction->t_state == T_COMMIT);
+ commit_transaction->t_state = T_FINISHED;
+
+ J_ASSERT (commit_transaction == journal->j_committing_transaction);
+ journal->j_commit_sequence = commit_transaction->t_tid;
+ journal->j_committing_transaction = NULL;
+
+ spin_lock(&journal_datalist_lock);
+ if (commit_transaction->t_checkpoint_list == NULL) {
+ __journal_drop_transaction(journal, commit_transaction);
+ } else {
+ if (journal->j_checkpoint_transactions == NULL) {
+ journal->j_checkpoint_transactions = commit_transaction;
+ commit_transaction->t_cpnext = commit_transaction;
+ commit_transaction->t_cpprev = commit_transaction;
+ } else {
+ commit_transaction->t_cpnext =
+ journal->j_checkpoint_transactions;
+ commit_transaction->t_cpprev =
+ commit_transaction->t_cpnext->t_cpprev;
+ commit_transaction->t_cpnext->t_cpprev =
+ commit_transaction;
+ commit_transaction->t_cpprev->t_cpnext =
+ commit_transaction;
+ }
+ }
+ spin_unlock(&journal_datalist_lock);
+
+ jbd_debug(1, "JBD: commit %d complete, head %d\n",
+ journal->j_commit_sequence, journal->j_tail_sequence);
+
+ unlock_journal(journal);
+ wake_up(&journal->j_wait_done_commit);
+}
diff --git a/fs/jbd/journal.c b/fs/jbd/journal.c
new file mode 100644
index 00000000000000..f57e6b09a88380
--- /dev/null
+++ b/fs/jbd/journal.c
@@ -0,0 +1,2000 @@
+/*
+ * linux/fs/journal.c
+ *
+ * Written by Stephen C. Tweedie <sct@redhat.com>, 1998
+ *
+ * Copyright 1998 Red Hat corp --- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * Generic filesystem journal-writing code; part of the ext2fs
+ * journaling system.
+ *
+ * This file manages journals: areas of disk reserved for logging
+ * transactional updates. This includes the kernel journaling thread
+ * which is responsible for scheduling updates to the log.
+ *
+ * We do not actually manage the physical storage of the journal in this
+ * file: that is left to a per-journal policy function, which allows us
+ * to store the journal within a filesystem-specified area for ext2
+ * journaling (ext2 can use a reserved inode for storing the log).
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/jbd.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/locks.h>
+#include <linux/smp_lock.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <asm/uaccess.h>
+#include <linux/proc_fs.h>
+
+EXPORT_SYMBOL(journal_start);
+EXPORT_SYMBOL(journal_try_start);
+EXPORT_SYMBOL(journal_restart);
+EXPORT_SYMBOL(journal_extend);
+EXPORT_SYMBOL(journal_stop);
+EXPORT_SYMBOL(journal_lock_updates);
+EXPORT_SYMBOL(journal_unlock_updates);
+EXPORT_SYMBOL(journal_get_write_access);
+EXPORT_SYMBOL(journal_get_create_access);
+EXPORT_SYMBOL(journal_get_undo_access);
+EXPORT_SYMBOL(journal_dirty_data);
+EXPORT_SYMBOL(journal_dirty_metadata);
+#if 0
+EXPORT_SYMBOL(journal_release_buffer);
+#endif
+EXPORT_SYMBOL(journal_forget);
+#if 0
+EXPORT_SYMBOL(journal_sync_buffer);
+#endif
+EXPORT_SYMBOL(journal_flush);
+EXPORT_SYMBOL(journal_revoke);
+EXPORT_SYMBOL(journal_callback_set);
+
+EXPORT_SYMBOL(journal_init_dev);
+EXPORT_SYMBOL(journal_init_inode);
+EXPORT_SYMBOL(journal_update_format);
+EXPORT_SYMBOL(journal_check_used_features);
+EXPORT_SYMBOL(journal_check_available_features);
+EXPORT_SYMBOL(journal_set_features);
+EXPORT_SYMBOL(journal_create);
+EXPORT_SYMBOL(journal_load);
+EXPORT_SYMBOL(journal_destroy);
+EXPORT_SYMBOL(journal_recover);
+EXPORT_SYMBOL(journal_update_superblock);
+EXPORT_SYMBOL(journal_abort);
+EXPORT_SYMBOL(journal_errno);
+EXPORT_SYMBOL(journal_ack_err);
+EXPORT_SYMBOL(journal_clear_err);
+EXPORT_SYMBOL(log_wait_commit);
+EXPORT_SYMBOL(log_start_commit);
+EXPORT_SYMBOL(journal_wipe);
+EXPORT_SYMBOL(journal_blocks_per_page);
+EXPORT_SYMBOL(journal_flushpage);
+EXPORT_SYMBOL(journal_try_to_free_buffers);
+EXPORT_SYMBOL(journal_bmap);
+EXPORT_SYMBOL(journal_force_commit);
+
+static int journal_convert_superblock_v1(journal_t *, journal_superblock_t *);
+
+/*
+ * journal_datalist_lock is used to protect data buffers:
+ *
+ * bh->b_transaction
+ * bh->b_tprev
+ * bh->b_tnext
+ *
+ * journal_free_buffer() is called from journal_try_to_free_buffer(), and is
+ * async wrt everything else.
+ *
+ * It is also used for checkpoint data, also to protect against
+ * journal_try_to_free_buffer():
+ *
+ * bh->b_cp_transaction
+ * bh->b_cpnext
+ * bh->b_cpprev
+ * transaction->t_checkpoint_list
+ * transaction->t_cpnext
+ * transaction->t_cpprev
+ * journal->j_checkpoint_transactions
+ *
+ * It is global at this time rather than per-journal because it's
+ * impossible for __journal_free_buffer to go from a buffer_head
+ * back to a journal_t unracily (well, not true. Fix later)
+ *
+ *
+ * The `datalist' and `checkpoint list' functions are quite
+ * separate and we could use two spinlocks here.
+ *
+ * lru_list_lock nests inside journal_datalist_lock.
+ */
+spinlock_t journal_datalist_lock = SPIN_LOCK_UNLOCKED;
+
+/*
+ * jh_splice_lock needs explantion.
+ *
+ * In a number of places we want to do things like:
+ *
+ * if (buffer_jbd(bh) && bh2jh(bh)->foo)
+ *
+ * This is racy on SMP, because another CPU could remove the journal_head
+ * in the middle of this expression. We need locking.
+ *
+ * But we can greatly optimise the locking cost by testing BH_JBD
+ * outside the lock. So, effectively:
+ *
+ * ret = 0;
+ * if (buffer_jbd(bh)) {
+ * spin_lock(&jh_splice_lock);
+ * if (buffer_jbd(bh)) { (* Still there? *)
+ * ret = bh2jh(bh)->foo;
+ * }
+ * spin_unlock(&jh_splice_lock);
+ * }
+ * return ret;
+ *
+ * Now, that protects us from races where another CPU can remove the
+ * journal_head. But it doesn't defend us from the situation where another
+ * CPU can *add* a journal_head. This is a correctness issue. But it's not
+ * a problem because a) the calling code was *already* racy and b) it often
+ * can't happen at the call site and c) the places where we add journal_heads
+ * tend to be under external locking.
+ */
+spinlock_t jh_splice_lock = SPIN_LOCK_UNLOCKED;
+
+/*
+ * List of all journals in the system. Protected by the BKL.
+ */
+static LIST_HEAD(all_journals);
+
+/*
+ * Helper function used to manage commit timeouts
+ */
+
+static void commit_timeout(unsigned long __data)
+{
+ struct task_struct * p = (struct task_struct *) __data;
+
+ wake_up_process(p);
+}
+
+/* Static check for data structure consistency. There's no code
+ * invoked --- we'll just get a linker failure if things aren't right.
+ */
+void __journal_internal_check(void)
+{
+ extern void journal_bad_superblock_size(void);
+ if (sizeof(struct journal_superblock_s) != 1024)
+ journal_bad_superblock_size();
+}
+
+/*
+ * kjournald: The main thread function used to manage a logging device
+ * journal.
+ *
+ * This kernel thread is responsible for two things:
+ *
+ * 1) COMMIT: Every so often we need to commit the current state of the
+ * filesystem to disk. The journal thread is responsible for writing
+ * all of the metadata buffers to disk.
+ *
+ * 2) CHECKPOINT: We cannot reuse a used section of the log file until all
+ * of the data in that part of the log has been rewritten elsewhere on
+ * the disk. Flushing these old buffers to reclaim space in the log is
+ * known as checkpointing, and this thread is responsible for that job.
+ */
+
+journal_t *current_journal; // AKPM: debug
+
+int kjournald(void *arg)
+{
+ journal_t *journal = (journal_t *) arg;
+ transaction_t *transaction;
+ struct timer_list timer;
+
+ current_journal = journal;
+
+ lock_kernel();
+ daemonize();
+ reparent_to_init();
+ spin_lock_irq(&current->sigmask_lock);
+ sigfillset(&current->blocked);
+ recalc_sigpending(current);
+ spin_unlock_irq(&current->sigmask_lock);
+
+ sprintf(current->comm, "kjournald");
+
+ /* Set up an interval timer which can be used to trigger a
+ commit wakeup after the commit interval expires */
+ init_timer(&timer);
+ timer.data = (unsigned long) current;
+ timer.function = commit_timeout;
+ journal->j_commit_timer = &timer;
+
+ /* Record that the journal thread is running */
+ journal->j_task = current;
+ wake_up(&journal->j_wait_done_commit);
+
+ printk(KERN_INFO "kjournald starting. Commit interval %ld seconds\n",
+ journal->j_commit_interval / HZ);
+ list_add(&journal->j_all_journals, &all_journals);
+
+ /* And now, wait forever for commit wakeup events. */
+ while (1) {
+ if (journal->j_flags & JFS_UNMOUNT)
+ break;
+
+ jbd_debug(1, "commit_sequence=%d, commit_request=%d\n",
+ journal->j_commit_sequence, journal->j_commit_request);
+
+ if (journal->j_commit_sequence != journal->j_commit_request) {
+ jbd_debug(1, "OK, requests differ\n");
+ if (journal->j_commit_timer_active) {
+ journal->j_commit_timer_active = 0;
+ del_timer(journal->j_commit_timer);
+ }
+
+ journal_commit_transaction(journal);
+ continue;
+ }
+
+ wake_up(&journal->j_wait_done_commit);
+ interruptible_sleep_on(&journal->j_wait_commit);
+
+ jbd_debug(1, "kjournald wakes\n");
+
+ /* Were we woken up by a commit wakeup event? */
+ if ((transaction = journal->j_running_transaction) != NULL &&
+ journal->j_commit_interval &&
+ time_after_eq(jiffies, transaction->t_expires)) {
+ journal->j_commit_request = transaction->t_tid;
+ jbd_debug(1, "woke because of timeout\n");
+ }
+ }
+
+ if (journal->j_commit_timer_active) {
+ journal->j_commit_timer_active = 0;
+ del_timer_sync(journal->j_commit_timer);
+ }
+
+ list_del(&journal->j_all_journals);
+
+ journal->j_task = NULL;
+ wake_up(&journal->j_wait_done_commit);
+ unlock_kernel();
+ jbd_debug(1, "Journal thread exiting.\n");
+ return 0;
+}
+
+static void journal_start_thread(journal_t *journal)
+{
+ kernel_thread(kjournald, (void *) journal,
+ CLONE_VM | CLONE_FS | CLONE_FILES);
+ while (!journal->j_task)
+ sleep_on(&journal->j_wait_done_commit);
+}
+
+static void journal_kill_thread(journal_t *journal)
+{
+ journal->j_flags |= JFS_UNMOUNT;
+
+ while (journal->j_task) {
+ wake_up(&journal->j_wait_commit);
+ sleep_on(&journal->j_wait_done_commit);
+ }
+}
+
+#if 0
+
+This is no longer needed - we do it in commit quite efficiently.
+Note that if this function is resurrected, the loop needs to
+be reorganised into the next_jh/last_jh algorithm.
+
+/*
+ * journal_clean_data_list: cleanup after data IO.
+ *
+ * Once the IO system has finished writing the buffers on the transaction's
+ * data list, we can remove those buffers from the list. This function
+ * scans the list for such buffers and removes them cleanly.
+ *
+ * We assume that the journal is already locked.
+ * We are called with journal_datalist_lock held.
+ *
+ * AKPM: This function looks inefficient. Approximately O(n^2)
+ * for potentially thousands of buffers. It no longer shows on profiles
+ * because these buffers are mainly dropped in journal_commit_transaction().
+ */
+
+void __journal_clean_data_list(transaction_t *transaction)
+{
+ struct journal_head *jh, *next;
+
+ assert_spin_locked(&journal_datalist_lock);
+
+restart:
+ jh = transaction->t_sync_datalist;
+ if (!jh)
+ goto out;
+ do {
+ next = jh->b_tnext;
+ if (!buffer_locked(jh2bh(jh)) && !buffer_dirty(jh2bh(jh))) {
+ struct buffer_head *bh = jh2bh(jh);
+ BUFFER_TRACE(bh, "data writeout complete: unfile");
+ __journal_unfile_buffer(jh);
+ jh->b_transaction = NULL;
+ __journal_remove_journal_head(bh);
+ refile_buffer(bh);
+ __brelse(bh);
+ goto restart;
+ }
+ jh = next;
+ } while (transaction->t_sync_datalist &&
+ jh != transaction->t_sync_datalist);
+out:
+ return;
+}
+#endif
+
+/*
+ * journal_write_metadata_buffer: write a metadata buffer to the journal.
+ *
+ * Writes a metadata buffer to a given disk block. The actual IO is not
+ * performed but a new buffer_head is constructed which labels the data
+ * to be written with the correct destination disk block.
+ *
+ * Any magic-number escaping which needs to be done will cause a
+ * copy-out here. If the buffer happens to start with the
+ * JFS_MAGIC_NUMBER, then we can't write it to the log directly: the
+ * magic number is only written to the log for descripter blocks. In
+ * this case, we copy the data and replace the first word with 0, and we
+ * return a result code which indicates that this buffer needs to be
+ * marked as an escaped buffer in the corresponding log descriptor
+ * block. The missing word can then be restored when the block is read
+ * during recovery.
+ *
+ * If the source buffer has already been modified by a new transaction
+ * since we took the last commit snapshot, we use the frozen copy of
+ * that data for IO. If we end up using the existing buffer_head's data
+ * for the write, then we *have* to lock the buffer to prevent anyone
+ * else from using and possibly modifying it while the IO is in
+ * progress.
+ *
+ * The function returns a pointer to the buffer_heads to be used for IO.
+ *
+ * We assume that the journal has already been locked in this function.
+ *
+ * Return value:
+ * <0: Error
+ * >=0: Finished OK
+ *
+ * On success:
+ * Bit 0 set == escape performed on the data
+ * Bit 1 set == buffer copy-out performed (kfree the data after IO)
+ */
+
+static inline unsigned long virt_to_offset(void *p)
+{return ((unsigned long) p) & ~PAGE_MASK;}
+
+int journal_write_metadata_buffer(transaction_t *transaction,
+ struct journal_head *jh_in,
+ struct journal_head **jh_out,
+ int blocknr)
+{
+ int need_copy_out = 0;
+ int done_copy_out = 0;
+ int do_escape = 0;
+ char *mapped_data;
+ struct buffer_head *new_bh;
+ struct journal_head * new_jh;
+ struct page *new_page;
+ unsigned int new_offset;
+
+ /*
+ * The buffer really shouldn't be locked: only the current committing
+ * transaction is allowed to write it, so nobody else is allowed
+ * to do any IO.
+ *
+ * akpm: except if we're journalling data, and write() output is
+ * also part of a shared mapping, and another thread has
+ * decided to launch a writepage() against this buffer.
+ */
+ J_ASSERT_JH(jh_in, buffer_jdirty(jh2bh(jh_in)));
+
+ /*
+ * If a new transaction has already done a buffer copy-out, then
+ * we use that version of the data for the commit.
+ */
+
+ if (jh_in->b_frozen_data) {
+ done_copy_out = 1;
+ new_page = virt_to_page(jh_in->b_frozen_data);
+ new_offset = virt_to_offset(jh_in->b_frozen_data);
+ } else {
+ new_page = jh2bh(jh_in)->b_page;
+ new_offset = virt_to_offset(jh2bh(jh_in)->b_data);
+ }
+
+ mapped_data = ((char *) kmap(new_page)) + new_offset;
+
+ /*
+ * Check for escaping
+ */
+ if (* ((unsigned int *) mapped_data) == htonl(JFS_MAGIC_NUMBER)) {
+ need_copy_out = 1;
+ do_escape = 1;
+ }
+
+ /*
+ * Do we need to do a data copy?
+ */
+
+ if (need_copy_out && !done_copy_out) {
+ char *tmp;
+ tmp = jbd_rep_kmalloc(jh2bh(jh_in)->b_size, GFP_NOFS);
+
+ jh_in->b_frozen_data = tmp;
+ memcpy (tmp, mapped_data, jh2bh(jh_in)->b_size);
+
+ /* If we get to this path, we'll always need the new
+ address kmapped so that we can clear the escaped
+ magic number below. */
+ kunmap(new_page);
+ new_page = virt_to_page(tmp);
+ new_offset = virt_to_offset(tmp);
+ mapped_data = ((char *) kmap(new_page)) + new_offset;
+
+ done_copy_out = 1;
+ }
+
+ /*
+ * Right, time to make up the new buffer_head.
+ */
+ do {
+ new_bh = get_unused_buffer_head(0);
+ if (!new_bh) {
+ printk (KERN_NOTICE "%s: ENOMEM at "
+ "get_unused_buffer_head, trying again.\n",
+ __FUNCTION__);
+ yield();
+ }
+ } while (!new_bh);
+ /* keep subsequent assertions sane */
+ new_bh->b_prev_free = 0;
+ new_bh->b_next_free = 0;
+ new_bh->b_state = 0;
+ init_buffer(new_bh, NULL, NULL);
+ atomic_set(&new_bh->b_count, 1);
+ new_jh = journal_add_journal_head(new_bh);
+
+ set_bh_page(new_bh, new_page, new_offset);
+
+ new_jh->b_transaction = NULL;
+ new_bh->b_size = jh2bh(jh_in)->b_size;
+ new_bh->b_dev = transaction->t_journal->j_dev;
+ new_bh->b_blocknr = blocknr;
+ new_bh->b_state |= (1 << BH_Mapped) | (1 << BH_Dirty);
+
+ *jh_out = new_jh;
+
+ /*
+ * Did we need to do an escaping? Now we've done all the
+ * copying, we can finally do so.
+ */
+
+ if (do_escape)
+ * ((unsigned int *) mapped_data) = 0;
+ kunmap(new_page);
+
+ /*
+ * The to-be-written buffer needs to get moved to the io queue,
+ * and the original buffer whose contents we are shadowing or
+ * copying is moved to the transaction's shadow queue.
+ */
+ JBUFFER_TRACE(jh_in, "file as BJ_Shadow");
+ journal_file_buffer(jh_in, transaction, BJ_Shadow);
+ JBUFFER_TRACE(new_jh, "file as BJ_IO");
+ journal_file_buffer(new_jh, transaction, BJ_IO);
+
+ return do_escape | (done_copy_out << 1);
+}
+
+/*
+ * Allocation code for the journal file. Manage the space left in the
+ * journal, so that we can begin checkpointing when appropriate.
+ */
+
+/*
+ * log_space_left: Return the number of free blocks left in the journal.
+ *
+ * Called with the journal already locked.
+ */
+
+int log_space_left (journal_t *journal)
+{
+ int left = journal->j_free;
+
+ /* Be pessimistic here about the number of those free blocks
+ * which might be required for log descriptor control blocks. */
+
+#define MIN_LOG_RESERVED_BLOCKS 32 /* Allow for rounding errors */
+
+ left -= MIN_LOG_RESERVED_BLOCKS;
+
+ if (left <= 0)
+ return 0;
+ left -= (left >> 3);
+ return left;
+}
+
+/*
+ * This function must be non-allocating for PF_MEMALLOC tasks
+ */
+tid_t log_start_commit (journal_t *journal, transaction_t *transaction)
+{
+ tid_t target = journal->j_commit_request;
+
+ lock_kernel(); /* Protect journal->j_running_transaction */
+
+ /*
+ * A NULL transaction asks us to commit the currently running
+ * transaction, if there is one.
+ */
+ if (transaction)
+ target = transaction->t_tid;
+ else {
+ transaction = journal->j_running_transaction;
+ if (!transaction)
+ goto out;
+ target = transaction->t_tid;
+ }
+
+ /*
+ * Are we already doing a recent enough commit?
+ */
+ if (tid_geq(journal->j_commit_request, target))
+ goto out;
+
+ /*
+ * We want a new commit: OK, mark the request and wakup the
+ * commit thread. We do _not_ do the commit ourselves.
+ */
+
+ journal->j_commit_request = target;
+ jbd_debug(1, "JBD: requesting commit %d/%d\n",
+ journal->j_commit_request,
+ journal->j_commit_sequence);
+ wake_up(&journal->j_wait_commit);
+
+out:
+ unlock_kernel();
+ return target;
+}
+
+/*
+ * Wait for a specified commit to complete.
+ * The caller may not hold the journal lock.
+ */
+void log_wait_commit (journal_t *journal, tid_t tid)
+{
+ lock_kernel();
+#ifdef CONFIG_JBD_DEBUG
+ lock_journal(journal);
+ if (!tid_geq(journal->j_commit_request, tid)) {
+ printk(KERN_EMERG "%s: error: j_commit_request=%d, tid=%d\n",
+ __FUNCTION__, journal->j_commit_request, tid);
+ }
+ unlock_journal(journal);
+#endif
+ while (tid_gt(tid, journal->j_commit_sequence)) {
+ jbd_debug(1, "JBD: want %d, j_commit_sequence=%d\n",
+ tid, journal->j_commit_sequence);
+ wake_up(&journal->j_wait_commit);
+ sleep_on(&journal->j_wait_done_commit);
+ }
+ unlock_kernel();
+}
+
+/*
+ * Log buffer allocation routines:
+ */
+
+int journal_next_log_block(journal_t *journal, unsigned long *retp)
+{
+ unsigned long blocknr;
+
+ J_ASSERT(journal->j_free > 1);
+
+ blocknr = journal->j_head;
+ journal->j_head++;
+ journal->j_free--;
+ if (journal->j_head == journal->j_last)
+ journal->j_head = journal->j_first;
+ return journal_bmap(journal, blocknr, retp);
+}
+
+/*
+ * Conversion of logical to physical block numbers for the journal
+ *
+ * On external journals the journal blocks are identity-mapped, so
+ * this is a no-op. If needed, we can use j_blk_offset - everything is
+ * ready.
+ */
+int journal_bmap(journal_t *journal, unsigned long blocknr,
+ unsigned long *retp)
+{
+ int err = 0;
+ unsigned long ret;
+
+ if (journal->j_inode) {
+ ret = bmap(journal->j_inode, blocknr);
+ if (ret)
+ *retp = ret;
+ else {
+ printk (KERN_ALERT "%s: journal block not found "
+ "at offset %lu on %s\n", __FUNCTION__,
+ blocknr, bdevname(journal->j_dev));
+ err = -EIO;
+ __journal_abort_soft(journal, err);
+ }
+ } else {
+ *retp = blocknr; /* +journal->j_blk_offset */
+ }
+ return err;
+}
+
+/*
+ * We play buffer_head aliasing tricks to write data/metadata blocks to
+ * the journal without copying their contents, but for journal
+ * descriptor blocks we do need to generate bona fide buffers.
+ *
+ * We return a jh whose bh is locked and ready to be populated.
+ */
+
+struct journal_head * journal_get_descriptor_buffer(journal_t *journal)
+{
+ struct buffer_head *bh;
+ unsigned long blocknr;
+ int err;
+
+ err = journal_next_log_block(journal, &blocknr);
+
+ if (err)
+ return NULL;
+
+ bh = getblk(journal->j_dev, blocknr, journal->j_blocksize);
+ lock_buffer(bh);
+ memset(bh->b_data, 0, journal->j_blocksize);
+ BUFFER_TRACE(bh, "return this buffer");
+ return journal_add_journal_head(bh);
+}
+
+/*
+ * Management for journal control blocks: functions to create and
+ * destroy journal_t structures, and to initialise and read existing
+ * journal blocks from disk. */
+
+/* First: create and setup a journal_t object in memory. We initialise
+ * very few fields yet: that has to wait until we have created the
+ * journal structures from from scratch, or loaded them from disk. */
+
+static journal_t * journal_init_common (void)
+{
+ journal_t *journal;
+ int err;
+
+ MOD_INC_USE_COUNT;
+
+ journal = jbd_kmalloc(sizeof(*journal), GFP_KERNEL);
+ if (!journal)
+ goto fail;
+ memset(journal, 0, sizeof(*journal));
+
+ init_waitqueue_head(&journal->j_wait_transaction_locked);
+ init_waitqueue_head(&journal->j_wait_logspace);
+ init_waitqueue_head(&journal->j_wait_done_commit);
+ init_waitqueue_head(&journal->j_wait_checkpoint);
+ init_waitqueue_head(&journal->j_wait_commit);
+ init_waitqueue_head(&journal->j_wait_updates);
+ init_MUTEX(&journal->j_barrier);
+ init_MUTEX(&journal->j_checkpoint_sem);
+ init_MUTEX(&journal->j_sem);
+
+ journal->j_commit_interval = get_buffer_flushtime();
+
+ /* The journal is marked for error until we succeed with recovery! */
+ journal->j_flags = JFS_ABORT;
+
+ /* Set up a default-sized revoke table for the new mount. */
+ err = journal_init_revoke(journal, JOURNAL_REVOKE_DEFAULT_HASH);
+ if (err) {
+ kfree(journal);
+ goto fail;
+ }
+ return journal;
+fail:
+ MOD_DEC_USE_COUNT;
+ return NULL;
+}
+
+/* journal_init_dev and journal_init_inode:
+ *
+ * Create a journal structure assigned some fixed set of disk blocks to
+ * the journal. We don't actually touch those disk blocks yet, but we
+ * need to set up all of the mapping information to tell the journaling
+ * system where the journal blocks are.
+ *
+ */
+
+ /**
+ * journal_t * journal_init_dev() - creates an initialises a journal structure
+ * @kdev: Block device on which to create the journal
+ * @fs_dev: Device which hold journalled filesystem for this journal.
+ * @start: Block nr Start of journal.
+ * @len: Lenght of the journal in blocks.
+ * @blocksize: blocksize of journalling device
+ * @returns: a newly created journal_t *
+ *
+ * journal_init_dev creates a journal which maps a fixed contiguous
+ * range of blocks on an arbitrary block device.
+ *
+ */
+journal_t * journal_init_dev(kdev_t dev, kdev_t fs_dev,
+ int start, int len, int blocksize)
+{
+ journal_t *journal = journal_init_common();
+ struct buffer_head *bh;
+
+ if (!journal)
+ return NULL;
+
+ journal->j_dev = dev;
+ journal->j_fs_dev = fs_dev;
+ journal->j_blk_offset = start;
+ journal->j_maxlen = len;
+ journal->j_blocksize = blocksize;
+
+ bh = getblk(journal->j_dev, start, journal->j_blocksize);
+ J_ASSERT(bh != NULL);
+ journal->j_sb_buffer = bh;
+ journal->j_superblock = (journal_superblock_t *)bh->b_data;
+
+ return journal;
+}
+
+/**
+ * journal_t * journal_init_inode () - creates a journal which maps to a inode.
+ * @inode: An inode to create the journal in
+ *
+ * journal_init_inode creates a journal which maps an on-disk inode as
+ * the journal. The inode must exist already, must support bmap() and
+ * must have all data blocks preallocated.
+ */
+journal_t * journal_init_inode (struct inode *inode)
+{
+ struct buffer_head *bh;
+ journal_t *journal = journal_init_common();
+ int err;
+ unsigned long blocknr;
+
+ if (!journal)
+ return NULL;
+
+ journal->j_dev = inode->i_dev;
+ journal->j_fs_dev = inode->i_dev;
+ journal->j_inode = inode;
+ jbd_debug(1,
+ "journal %p: inode %s/%ld, size %Ld, bits %d, blksize %ld\n",
+ journal, bdevname(inode->i_dev), inode->i_ino,
+ (long long) inode->i_size,
+ inode->i_sb->s_blocksize_bits, inode->i_sb->s_blocksize);
+
+ journal->j_maxlen = inode->i_size >> inode->i_sb->s_blocksize_bits;
+ journal->j_blocksize = inode->i_sb->s_blocksize;
+
+ err = journal_bmap(journal, 0, &blocknr);
+ /* If that failed, give up */
+ if (err) {
+ printk(KERN_ERR "%s: Cannnot locate journal superblock\n",
+ __FUNCTION__);
+ kfree(journal);
+ return NULL;
+ }
+
+ bh = getblk(journal->j_dev, blocknr, journal->j_blocksize);
+ J_ASSERT(bh != NULL);
+ journal->j_sb_buffer = bh;
+ journal->j_superblock = (journal_superblock_t *)bh->b_data;
+
+ return journal;
+}
+
+/*
+ * If the journal init or create aborts, we need to mark the journal
+ * superblock as being NULL to prevent the journal destroy from writing
+ * back a bogus superblock.
+ */
+static void journal_fail_superblock (journal_t *journal)
+{
+ struct buffer_head *bh = journal->j_sb_buffer;
+ brelse(bh);
+ journal->j_sb_buffer = NULL;
+}
+
+/*
+ * Given a journal_t structure, initialise the various fields for
+ * startup of a new journaling session. We use this both when creating
+ * a journal, and after recovering an old journal to reset it for
+ * subsequent use.
+ */
+
+static int journal_reset (journal_t *journal)
+{
+ journal_superblock_t *sb = journal->j_superblock;
+ unsigned int first, last;
+
+ first = ntohl(sb->s_first);
+ last = ntohl(sb->s_maxlen);
+
+ journal->j_first = first;
+ journal->j_last = last;
+
+ journal->j_head = first;
+ journal->j_tail = first;
+ journal->j_free = last - first;
+
+ journal->j_tail_sequence = journal->j_transaction_sequence;
+ journal->j_commit_sequence = journal->j_transaction_sequence - 1;
+ journal->j_commit_request = journal->j_commit_sequence;
+
+ journal->j_max_transaction_buffers = journal->j_maxlen / 4;
+
+ /* Add the dynamic fields and write it to disk. */
+ journal_update_superblock(journal, 1);
+
+ lock_journal(journal);
+ journal_start_thread(journal);
+ unlock_journal(journal);
+
+ return 0;
+}
+
+/**
+ * int journal_create() - Initialise the new journal file
+ * @journal: Journal to create. This structure must have been initialised
+ *
+ * Given a journal_t structure which tells us which disk blocks we can
+ * use, create a new journal superblock and initialise all of the
+ * journal fields from scratch.
+ **/
+int journal_create(journal_t *journal)
+{
+ unsigned long blocknr;
+ struct buffer_head *bh;
+ journal_superblock_t *sb;
+ int i, err;
+
+ if (journal->j_maxlen < JFS_MIN_JOURNAL_BLOCKS) {
+ printk (KERN_ERR "Journal length (%d blocks) too short.\n",
+ journal->j_maxlen);
+ journal_fail_superblock(journal);
+ return -EINVAL;
+ }
+
+ if (journal->j_inode == NULL) {
+ /*
+ * We don't know what block to start at!
+ */
+ printk(KERN_EMERG "%s: creation of journal on external "
+ "device!\n", __FUNCTION__);
+ BUG();
+ }
+
+ /* Zero out the entire journal on disk. We cannot afford to
+ have any blocks on disk beginning with JFS_MAGIC_NUMBER. */
+ jbd_debug(1, "JBD: Zeroing out journal blocks...\n");
+ for (i = 0; i < journal->j_maxlen; i++) {
+ err = journal_bmap(journal, i, &blocknr);
+ if (err)
+ return err;
+ bh = getblk(journal->j_dev, blocknr, journal->j_blocksize);
+ wait_on_buffer(bh);
+ memset (bh->b_data, 0, journal->j_blocksize);
+ BUFFER_TRACE(bh, "marking dirty");
+ mark_buffer_dirty(bh);
+ BUFFER_TRACE(bh, "marking uptodate");
+ mark_buffer_uptodate(bh, 1);
+ __brelse(bh);
+ }
+
+ fsync_no_super(journal->j_dev);
+ jbd_debug(1, "JBD: journal cleared.\n");
+
+ /* OK, fill in the initial static fields in the new superblock */
+ sb = journal->j_superblock;
+
+ sb->s_header.h_magic = htonl(JFS_MAGIC_NUMBER);
+ sb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V2);
+
+ sb->s_blocksize = htonl(journal->j_blocksize);
+ sb->s_maxlen = htonl(journal->j_maxlen);
+ sb->s_first = htonl(1);
+
+ journal->j_transaction_sequence = 1;
+
+ journal->j_flags &= ~JFS_ABORT;
+ journal->j_format_version = 2;
+
+ return journal_reset(journal);
+}
+
+/**
+ * void journal_update_superblock() - Update journal sb on disk.
+ * @journal: The journal to update.
+ * @wait: Set to '0' if you don't want to wait for IO completion.
+ *
+ * Update a journal's dynamic superblock fields and write it to disk,
+ * optionally waiting for the IO to complete.
+ */
+void journal_update_superblock(journal_t *journal, int wait)
+{
+ journal_superblock_t *sb = journal->j_superblock;
+ struct buffer_head *bh = journal->j_sb_buffer;
+
+ jbd_debug(1,"JBD: updating superblock (start %ld, seq %d, errno %d)\n",
+ journal->j_tail, journal->j_tail_sequence, journal->j_errno);
+
+ sb->s_sequence = htonl(journal->j_tail_sequence);
+ sb->s_start = htonl(journal->j_tail);
+ sb->s_errno = htonl(journal->j_errno);
+
+ BUFFER_TRACE(bh, "marking dirty");
+ mark_buffer_dirty(bh);
+ ll_rw_block(WRITE, 1, &bh);
+ if (wait)
+ wait_on_buffer(bh);
+
+ /* If we have just flushed the log (by marking s_start==0), then
+ * any future commit will have to be careful to update the
+ * superblock again to re-record the true start of the log. */
+
+ if (sb->s_start)
+ journal->j_flags &= ~JFS_FLUSHED;
+ else
+ journal->j_flags |= JFS_FLUSHED;
+}
+
+
+/*
+ * Read the superblock for a given journal, performing initial
+ * validation of the format.
+ */
+
+static int journal_get_superblock(journal_t *journal)
+{
+ struct buffer_head *bh;
+ journal_superblock_t *sb;
+ int err = -EIO;
+
+ bh = journal->j_sb_buffer;
+
+ J_ASSERT(bh != NULL);
+ if (!buffer_uptodate(bh)) {
+ ll_rw_block(READ, 1, &bh);
+ wait_on_buffer(bh);
+ if (!buffer_uptodate(bh)) {
+ printk (KERN_ERR
+ "JBD: IO error reading journal superblock\n");
+ goto out;
+ }
+ }
+
+ sb = journal->j_superblock;
+
+ err = -EINVAL;
+
+ if (sb->s_header.h_magic != htonl(JFS_MAGIC_NUMBER) ||
+ sb->s_blocksize != htonl(journal->j_blocksize)) {
+ printk(KERN_WARNING "JBD: no valid journal superblock found\n");
+ goto out;
+ }
+
+ switch(ntohl(sb->s_header.h_blocktype)) {
+ case JFS_SUPERBLOCK_V1:
+ journal->j_format_version = 1;
+ break;
+ case JFS_SUPERBLOCK_V2:
+ journal->j_format_version = 2;
+ break;
+ default:
+ printk(KERN_WARNING "JBD: unrecognised superblock format ID\n");
+ goto out;
+ }
+
+ if (ntohl(sb->s_maxlen) < journal->j_maxlen)
+ journal->j_maxlen = ntohl(sb->s_maxlen);
+ else if (ntohl(sb->s_maxlen) > journal->j_maxlen) {
+ printk (KERN_WARNING "JBD: journal file too short\n");
+ goto out;
+ }
+
+ return 0;
+
+out:
+ journal_fail_superblock(journal);
+ return err;
+}
+
+/*
+ * Load the on-disk journal superblock and read the key fields into the
+ * journal_t.
+ */
+
+static int load_superblock(journal_t *journal)
+{
+ int err;
+ journal_superblock_t *sb;
+
+ err = journal_get_superblock(journal);
+ if (err)
+ return err;
+
+ sb = journal->j_superblock;
+
+ journal->j_tail_sequence = ntohl(sb->s_sequence);
+ journal->j_tail = ntohl(sb->s_start);
+ journal->j_first = ntohl(sb->s_first);
+ journal->j_last = ntohl(sb->s_maxlen);
+ journal->j_errno = ntohl(sb->s_errno);
+
+ return 0;
+}
+
+
+/**
+ * int journal_load() - Read journal from disk.
+ * @journal: Journal to act on.
+ *
+ * Given a journal_t structure which tells us which disk blocks contain
+ * a journal, read the journal from disk to initialise the in-memory
+ * structures.
+ */
+int journal_load(journal_t *journal)
+{
+ int err;
+
+ err = load_superblock(journal);
+ if (err)
+ return err;
+
+ /* If this is a V2 superblock, then we have to check the
+ * features flags on it. */
+
+ if (journal->j_format_version >= 2) {
+ journal_superblock_t *sb = journal->j_superblock;
+
+ if ((sb->s_feature_ro_compat &
+ ~cpu_to_be32(JFS_KNOWN_ROCOMPAT_FEATURES)) ||
+ (sb->s_feature_incompat &
+ ~cpu_to_be32(JFS_KNOWN_INCOMPAT_FEATURES))) {
+ printk (KERN_WARNING
+ "JBD: Unrecognised features on journal\n");
+ return -EINVAL;
+ }
+ }
+
+ /* Let the recovery code check whether it needs to recover any
+ * data from the journal. */
+ if (journal_recover(journal))
+ goto recovery_error;
+
+ /* OK, we've finished with the dynamic journal bits:
+ * reinitialise the dynamic contents of the superblock in memory
+ * and reset them on disk. */
+ if (journal_reset(journal))
+ goto recovery_error;
+
+ journal->j_flags &= ~JFS_ABORT;
+ journal->j_flags |= JFS_LOADED;
+ return 0;
+
+recovery_error:
+ printk (KERN_WARNING "JBD: recovery failed\n");
+ return -EIO;
+}
+
+/**
+ * void journal_destroy() - Release a journal_t structure.
+ * @journal: Journal to act on.
+*
+ * Release a journal_t structure once it is no longer in use by the
+ * journaled object.
+ */
+void journal_destroy (journal_t *journal)
+{
+ /* Wait for the commit thread to wake up and die. */
+ journal_kill_thread(journal);
+
+ /* Force a final log commit */
+ if (journal->j_running_transaction)
+ journal_commit_transaction(journal);
+
+ /* Force any old transactions to disk */
+ lock_journal(journal);
+ while (journal->j_checkpoint_transactions != NULL)
+ log_do_checkpoint(journal, 1);
+
+ J_ASSERT(journal->j_running_transaction == NULL);
+ J_ASSERT(journal->j_committing_transaction == NULL);
+ J_ASSERT(journal->j_checkpoint_transactions == NULL);
+
+ /* We can now mark the journal as empty. */
+ journal->j_tail = 0;
+ journal->j_tail_sequence = ++journal->j_transaction_sequence;
+ if (journal->j_sb_buffer) {
+ journal_update_superblock(journal, 1);
+ brelse(journal->j_sb_buffer);
+ }
+
+ if (journal->j_inode)
+ iput(journal->j_inode);
+ if (journal->j_revoke)
+ journal_destroy_revoke(journal);
+
+ unlock_journal(journal);
+ kfree(journal);
+ MOD_DEC_USE_COUNT;
+}
+
+
+/**
+ *int journal_check_used_features () - Check if features specified are used.
+ *
+ * Check whether the journal uses all of a given set of
+ * features. Return true (non-zero) if it does.
+ **/
+
+int journal_check_used_features (journal_t *journal, unsigned long compat,
+ unsigned long ro, unsigned long incompat)
+{
+ journal_superblock_t *sb;
+
+ if (!compat && !ro && !incompat)
+ return 1;
+ if (journal->j_format_version == 1)
+ return 0;
+
+ sb = journal->j_superblock;
+
+ if (((be32_to_cpu(sb->s_feature_compat) & compat) == compat) &&
+ ((be32_to_cpu(sb->s_feature_ro_compat) & ro) == ro) &&
+ ((be32_to_cpu(sb->s_feature_incompat) & incompat) == incompat))
+ return 1;
+
+ return 0;
+}
+
+/**
+ * int journal_check_available_features() - Check feature set in journalling layer
+ *
+ * Check whether the journaling code supports the use of
+ * all of a given set of features on this journal. Return true
+ * (non-zero) if it can. */
+
+int journal_check_available_features (journal_t *journal, unsigned long compat,
+ unsigned long ro, unsigned long incompat)
+{
+ journal_superblock_t *sb;
+
+ if (!compat && !ro && !incompat)
+ return 1;
+
+ sb = journal->j_superblock;
+
+ /* We can support any known requested features iff the
+ * superblock is in version 2. Otherwise we fail to support any
+ * extended sb features. */
+
+ if (journal->j_format_version != 2)
+ return 0;
+
+ if ((compat & JFS_KNOWN_COMPAT_FEATURES) == compat &&
+ (ro & JFS_KNOWN_ROCOMPAT_FEATURES) == ro &&
+ (incompat & JFS_KNOWN_INCOMPAT_FEATURES) == incompat)
+ return 1;
+
+ return 0;
+}
+
+/**
+ * int journal_set_features () - Mark a given journal feature in the superblock
+ *
+ * Mark a given journal feature as present on the
+ * superblock. Returns true if the requested features could be set.
+ *
+ */
+
+int journal_set_features (journal_t *journal, unsigned long compat,
+ unsigned long ro, unsigned long incompat)
+{
+ journal_superblock_t *sb;
+
+ if (journal_check_used_features(journal, compat, ro, incompat))
+ return 1;
+
+ if (!journal_check_available_features(journal, compat, ro, incompat))
+ return 0;
+
+ jbd_debug(1, "Setting new features 0x%lx/0x%lx/0x%lx\n",
+ compat, ro, incompat);
+
+ sb = journal->j_superblock;
+
+ sb->s_feature_compat |= cpu_to_be32(compat);
+ sb->s_feature_ro_compat |= cpu_to_be32(ro);
+ sb->s_feature_incompat |= cpu_to_be32(incompat);
+
+ return 1;
+}
+
+
+/**
+ * int journal_update_format () - Update on-disk journal structure.
+ *
+ * Given an initialised but unloaded journal struct, poke about in the
+ * on-disk structure to update it to the most recent supported version.
+ */
+int journal_update_format (journal_t *journal)
+{
+ journal_superblock_t *sb;
+ int err;
+
+ err = journal_get_superblock(journal);
+ if (err)
+ return err;
+
+ sb = journal->j_superblock;
+
+ switch (ntohl(sb->s_header.h_blocktype)) {
+ case JFS_SUPERBLOCK_V2:
+ return 0;
+ case JFS_SUPERBLOCK_V1:
+ return journal_convert_superblock_v1(journal, sb);
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+static int journal_convert_superblock_v1(journal_t *journal,
+ journal_superblock_t *sb)
+{
+ int offset, blocksize;
+ struct buffer_head *bh;
+
+ printk(KERN_WARNING
+ "JBD: Converting superblock from version 1 to 2.\n");
+
+ /* Pre-initialise new fields to zero */
+ offset = ((char *) &(sb->s_feature_compat)) - ((char *) sb);
+ blocksize = ntohl(sb->s_blocksize);
+ memset(&sb->s_feature_compat, 0, blocksize-offset);
+
+ sb->s_nr_users = cpu_to_be32(1);
+ sb->s_header.h_blocktype = cpu_to_be32(JFS_SUPERBLOCK_V2);
+ journal->j_format_version = 2;
+
+ bh = journal->j_sb_buffer;
+ BUFFER_TRACE(bh, "marking dirty");
+ mark_buffer_dirty(bh);
+ ll_rw_block(WRITE, 1, &bh);
+ wait_on_buffer(bh);
+ return 0;
+}
+
+
+/**
+ * int journal_flush () - Flush journal
+ * @journal: Journal to act on.
+ *
+ * Flush all data for a given journal to disk and empty the journal.
+ * Filesystems can use this when remounting readonly to ensure that
+ * recovery does not need to happen on remount.
+ */
+
+int journal_flush (journal_t *journal)
+{
+ int err = 0;
+ transaction_t *transaction = NULL;
+ unsigned long old_tail;
+
+ lock_kernel();
+
+ /* Force everything buffered to the log... */
+ if (journal->j_running_transaction) {
+ transaction = journal->j_running_transaction;
+ log_start_commit(journal, transaction);
+ } else if (journal->j_committing_transaction)
+ transaction = journal->j_committing_transaction;
+
+ /* Wait for the log commit to complete... */
+ if (transaction)
+ log_wait_commit(journal, transaction->t_tid);
+
+ /* ...and flush everything in the log out to disk. */
+ lock_journal(journal);
+ while (!err && journal->j_checkpoint_transactions != NULL)
+ err = log_do_checkpoint(journal, journal->j_maxlen);
+ cleanup_journal_tail(journal);
+
+ /* Finally, mark the journal as really needing no recovery.
+ * This sets s_start==0 in the underlying superblock, which is
+ * the magic code for a fully-recovered superblock. Any future
+ * commits of data to the journal will restore the current
+ * s_start value. */
+ old_tail = journal->j_tail;
+ journal->j_tail = 0;
+ journal_update_superblock(journal, 1);
+ journal->j_tail = old_tail;
+
+ unlock_journal(journal);
+
+ J_ASSERT(!journal->j_running_transaction);
+ J_ASSERT(!journal->j_committing_transaction);
+ J_ASSERT(!journal->j_checkpoint_transactions);
+ J_ASSERT(journal->j_head == journal->j_tail);
+ J_ASSERT(journal->j_tail_sequence == journal->j_transaction_sequence);
+
+ unlock_kernel();
+
+ return err;
+}
+
+/**
+ * int journal_wipe() - Wipe journal contents
+ * @journal: Journal to act on.
+ * @write: flag (see below)
+ *
+ * Wipe out all of the contents of a journal, safely. This will produce
+ * a warning if the journal contains any valid recovery information.
+ * Must be called between journal_init_*() and journal_load().
+ *
+ * If 'write' is non-zero, then we wipe out the journal on disk; otherwise
+ * we merely suppress recovery.
+ */
+
+int journal_wipe (journal_t *journal, int write)
+{
+ journal_superblock_t *sb;
+ int err = 0;
+
+ J_ASSERT (!(journal->j_flags & JFS_LOADED));
+
+ err = load_superblock(journal);
+ if (err)
+ return err;
+
+ sb = journal->j_superblock;
+
+ if (!journal->j_tail)
+ goto no_recovery;
+
+ printk (KERN_WARNING "JBD: %s recovery information on journal\n",
+ write ? "Clearing" : "Ignoring");
+
+ err = journal_skip_recovery(journal);
+ if (write)
+ journal_update_superblock(journal, 1);
+
+ no_recovery:
+ return err;
+}
+
+/*
+ * journal_dev_name: format a character string to describe on what
+ * device this journal is present.
+ */
+
+const char * journal_dev_name(journal_t *journal)
+{
+ kdev_t dev;
+
+ if (journal->j_inode)
+ dev = journal->j_inode->i_dev;
+ else
+ dev = journal->j_dev;
+
+ return bdevname(dev);
+}
+
+/*
+ * Journal abort has very specific semantics, which we describe
+ * for journal abort.
+ *
+ * Two internal function, which provide abort to te jbd layer
+ * itself are here.
+ */
+
+/* Quick version for internal journal use (doesn't lock the journal).
+ * Aborts hard --- we mark the abort as occurred, but do _nothing_ else,
+ * and don't attempt to make any other journal updates. */
+void __journal_abort_hard (journal_t *journal)
+{
+ transaction_t *transaction;
+
+ if (journal->j_flags & JFS_ABORT)
+ return;
+
+ printk (KERN_ERR "Aborting journal on device %s.\n",
+ journal_dev_name(journal));
+
+ journal->j_flags |= JFS_ABORT;
+ transaction = journal->j_running_transaction;
+ if (transaction)
+ log_start_commit(journal, transaction);
+}
+
+/* Soft abort: record the abort error status in the journal superblock,
+ * but don't do any other IO. */
+void __journal_abort_soft (journal_t *journal, int errno)
+{
+ if (journal->j_flags & JFS_ABORT)
+ return;
+
+ if (!journal->j_errno)
+ journal->j_errno = errno;
+
+ __journal_abort_hard(journal);
+
+ if (errno)
+ journal_update_superblock(journal, 1);
+}
+
+/**
+ * void journal_abort () - Shutdown the journal immediately.
+ * @journal: the journal to shutdown.
+ * @errno: an error number to record in the journal indicating
+ * the reason for the shutdown.
+ *
+ * Perform a complete, immediate shutdown of the ENTIRE
+ * journal (not of a single transaction). This operation cannot be
+ * undone without closing and reopening the journal.
+ *
+ * The journal_abort function is intended to support higher level error
+ * recovery mechanisms such as the ext2/ext3 remount-readonly error
+ * mode.
+ *
+ * Journal abort has very specific semantics. Any existing dirty,
+ * unjournaled buffers in the main filesystem will still be written to
+ * disk by bdflush, but the journaling mechanism will be suspended
+ * immediately and no further transaction commits will be honoured.
+ *
+ * Any dirty, journaled buffers will be written back to disk without
+ * hitting the journal. Atomicity cannot be guaranteed on an aborted
+ * filesystem, but we _do_ attempt to leave as much data as possible
+ * behind for fsck to use for cleanup.
+ *
+ * Any attempt to get a new transaction handle on a journal which is in
+ * ABORT state will just result in an -EROFS error return. A
+ * journal_stop on an existing handle will return -EIO if we have
+ * entered abort state during the update.
+ *
+ * Recursive transactions are not disturbed by journal abort until the
+ * final journal_stop, which will receive the -EIO error.
+ *
+ * Finally, the journal_abort call allows the caller to supply an errno
+ * which will be recorded (if possible) in the journal superblock. This
+ * allows a client to record failure conditions in the middle of a
+ * transaction without having to complete the transaction to record the
+ * failure to disk. ext3_error, for example, now uses this
+ * functionality.
+ *
+ * Errors which originate from within the journaling layer will NOT
+ * supply an errno; a null errno implies that absolutely no further
+ * writes are done to the journal (unless there are any already in
+ * progress).
+ *
+ */
+
+void journal_abort (journal_t *journal, int errno)
+{
+ lock_journal(journal);
+ __journal_abort_soft(journal, errno);
+ unlock_journal(journal);
+}
+
+/**
+ * int journal_errno () - returns the journal's error state.
+ * @journal: journal to examine.
+ *
+ * This is the errno numbet set with journal_abort(), the last
+ * time the journal was mounted - if the journal was stopped
+ * without calling abort this will be 0.
+ *
+ * If the journal has been aborted on this mount time -EROFS will
+ * be returned.
+ */
+int journal_errno (journal_t *journal)
+{
+ int err;
+
+ lock_journal(journal);
+ if (journal->j_flags & JFS_ABORT)
+ err = -EROFS;
+ else
+ err = journal->j_errno;
+ unlock_journal(journal);
+ return err;
+}
+
+
+
+/**
+ * int journal_clear_err () - clears the journal's error state
+ *
+ * An error must be cleared or Acked to take a FS out of readonly
+ * mode.
+ */
+int journal_clear_err (journal_t *journal)
+{
+ int err = 0;
+
+ lock_journal(journal);
+ if (journal->j_flags & JFS_ABORT)
+ err = -EROFS;
+ else
+ journal->j_errno = 0;
+ unlock_journal(journal);
+ return err;
+}
+
+
+/**
+ * void journal_ack_err() - Ack journal err.
+ *
+ * An error must be cleared or Acked to take a FS out of readonly
+ * mode.
+ */
+void journal_ack_err (journal_t *journal)
+{
+ lock_journal(journal);
+ if (journal->j_errno)
+ journal->j_flags |= JFS_ACK_ERR;
+ unlock_journal(journal);
+}
+
+
+/*
+ * Report any unexpected dirty buffers which turn up. Normally those
+ * indicate an error, but they can occur if the user is running (say)
+ * tune2fs to modify the live filesystem, so we need the option of
+ * continuing as gracefully as possible. #
+ *
+ * The caller should already hold the journal lock and
+ * journal_datalist_lock spinlock: most callers will need those anyway
+ * in order to probe the buffer's journaling state safely.
+ */
+void __jbd_unexpected_dirty_buffer(const char *function, int line,
+ struct journal_head *jh)
+{
+ struct buffer_head *bh = jh2bh(jh);
+ int jlist;
+
+ if (buffer_dirty(bh)) {
+ printk ("%sUnexpected dirty buffer encountered at "
+ "%s:%d (%s blocknr %lu)\n",
+ KERN_WARNING, function, line,
+ kdevname(bh->b_dev), bh->b_blocknr);
+#ifdef JBD_PARANOID_WRITES
+ J_ASSERT_BH (bh, !buffer_dirty(bh));
+#endif
+
+ /* If this buffer is one which might reasonably be dirty
+ * --- ie. data, or not part of this journal --- then
+ * we're OK to leave it alone, but otherwise we need to
+ * move the dirty bit to the journal's own internal
+ * JBDDirty bit. */
+ jlist = jh->b_jlist;
+
+ if (jlist == BJ_Metadata || jlist == BJ_Reserved ||
+ jlist == BJ_Shadow || jlist == BJ_Forget) {
+ if (atomic_set_buffer_clean(jh2bh(jh))) {
+ set_bit(BH_JBDDirty, &jh2bh(jh)->b_state);
+ }
+ }
+ }
+}
+
+
+int journal_blocks_per_page(struct inode *inode)
+{
+ return 1 << (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits);
+}
+
+/*
+ * shrink_journal_memory().
+ * Called when we're under memory pressure. Free up all the written-back
+ * checkpointed metadata buffers.
+ */
+void shrink_journal_memory(void)
+{
+ struct list_head *list;
+
+ lock_kernel();
+ list_for_each(list, &all_journals) {
+ journal_t *journal =
+ list_entry(list, journal_t, j_all_journals);
+ spin_lock(&journal_datalist_lock);
+ __journal_clean_checkpoint_list(journal);
+ spin_unlock(&journal_datalist_lock);
+ }
+ unlock_kernel();
+}
+
+/*
+ * Simple support for retying memory allocations. Introduced to help to
+ * debug different VM deadlock avoidance strategies.
+ */
+/*
+ * Simple support for retying memory allocations. Introduced to help to
+ * debug different VM deadlock avoidance strategies.
+ */
+void * __jbd_kmalloc (const char *where, size_t size, int flags, int retry)
+{
+ void *p;
+ static unsigned long last_warning;
+
+ while (1) {
+ p = kmalloc(size, flags);
+ if (p)
+ return p;
+ if (!retry)
+ return NULL;
+ /* Log every retry for debugging. Also log them to the
+ * syslog, but do rate-limiting on the non-debugging
+ * messages. */
+ jbd_debug(1, "ENOMEM in %s, retrying.\n", where);
+
+ if (time_after(jiffies, last_warning + 120*HZ)) {
+ printk(KERN_NOTICE
+ "ENOMEM in %s, retrying.\n", where);
+ last_warning = jiffies;
+ }
+
+ yield();
+ }
+}
+
+/*
+ * Journal_head storage management
+ */
+static kmem_cache_t *journal_head_cache;
+#ifdef CONFIG_JBD_DEBUG
+static atomic_t nr_journal_heads = ATOMIC_INIT(0);
+#endif
+
+static int journal_init_journal_head_cache(void)
+{
+ int retval;
+
+ J_ASSERT(journal_head_cache == 0);
+ journal_head_cache = kmem_cache_create("journal_head",
+ sizeof(struct journal_head),
+ 0, /* offset */
+ 0, /* flags */
+ NULL, /* ctor */
+ NULL); /* dtor */
+ retval = 0;
+ if (journal_head_cache == 0) {
+ retval = -ENOMEM;
+ printk(KERN_EMERG "JBD: no memory for journal_head cache\n");
+ }
+ return retval;
+}
+
+static void journal_destroy_journal_head_cache(void)
+{
+ J_ASSERT(journal_head_cache != NULL);
+ kmem_cache_destroy(journal_head_cache);
+ journal_head_cache = 0;
+}
+
+/*
+ * journal_head splicing and dicing
+ */
+static struct journal_head *journal_alloc_journal_head(void)
+{
+ struct journal_head *ret;
+ static unsigned long last_warning;
+
+#ifdef CONFIG_JBD_DEBUG
+ atomic_inc(&nr_journal_heads);
+#endif
+ ret = kmem_cache_alloc(journal_head_cache, GFP_NOFS);
+ if (ret == 0) {
+ jbd_debug(1, "out of memory for journal_head\n");
+ if (time_after(jiffies, last_warning + 5*HZ)) {
+ printk(KERN_NOTICE "ENOMEM in %s, retrying.\n",
+ __FUNCTION__);
+ last_warning = jiffies;
+ }
+ while (ret == 0) {
+ yield();
+ ret = kmem_cache_alloc(journal_head_cache, GFP_NOFS);
+ }
+ }
+ return ret;
+}
+
+static void journal_free_journal_head(struct journal_head *jh)
+{
+#ifdef CONFIG_JBD_DEBUG
+ atomic_dec(&nr_journal_heads);
+ memset(jh, 0x5b, sizeof(*jh));
+#endif
+ kmem_cache_free(journal_head_cache, jh);
+}
+
+/*
+ * A journal_head is attached to a buffer_head whenever JBD has an
+ * interest in the buffer.
+ *
+ * Whenever a buffer has an attached journal_head, its ->b_state:BH_JBD bit
+ * is set. This bit is tested in core kernel code where we need to take
+ * JBD-specific actions. Testing the zeroness of ->b_journal_head is not
+ * reliable there.
+ *
+ * When a buffer has its BH_JBD bit set, its ->b_count is elevated by one.
+ *
+ * When a buffer has its BH_JBD bit set it is immune from being released by
+ * core kernel code, mainly via ->b_count.
+ *
+ * A journal_head may be detached from its buffer_head when the journal_head's
+ * b_transaction, b_cp_transaction and b_next_transaction pointers are NULL.
+ * Various places in JBD call journal_remove_journal_head() to indicate that the
+ * journal_head can be dropped if needed.
+ *
+ * Various places in the kernel want to attach a journal_head to a buffer_head
+ * _before_ attaching the journal_head to a transaction. To protect the
+ * journal_head in this situation, journal_add_journal_head elevates the
+ * journal_head's b_jcount refcount by one. The caller must call
+ * journal_unlock_journal_head() to undo this.
+ *
+ * So the typical usage would be:
+ *
+ * (Attach a journal_head if needed. Increments b_jcount)
+ * struct journal_head *jh = journal_add_journal_head(bh);
+ * ...
+ * jh->b_transaction = xxx;
+ * journal_unlock_journal_head(jh);
+ *
+ * Now, the journal_head's b_jcount is zero, but it is safe from being released
+ * because it has a non-zero b_transaction.
+ */
+
+/*
+ * Give a buffer_head a journal_head.
+ *
+ * Doesn't need the journal lock.
+ * May sleep.
+ * Cannot be called with journal_datalist_lock held.
+ */
+struct journal_head *journal_add_journal_head(struct buffer_head *bh)
+{
+ struct journal_head *jh;
+
+ spin_lock(&journal_datalist_lock);
+ if (buffer_jbd(bh)) {
+ jh = bh2jh(bh);
+ } else {
+ J_ASSERT_BH(bh,
+ (atomic_read(&bh->b_count) > 0) ||
+ (bh->b_page && bh->b_page->mapping));
+ spin_unlock(&journal_datalist_lock);
+ jh = journal_alloc_journal_head();
+ memset(jh, 0, sizeof(*jh));
+ spin_lock(&journal_datalist_lock);
+
+ if (buffer_jbd(bh)) {
+ /* Someone did it for us! */
+ J_ASSERT_BH(bh, bh->b_private != NULL);
+ journal_free_journal_head(jh);
+ jh = bh->b_private;
+ } else {
+ /*
+ * We actually don't need jh_splice_lock when
+ * adding a journal_head - only on removal.
+ */
+ spin_lock(&jh_splice_lock);
+ set_bit(BH_JBD, &bh->b_state);
+ bh->b_private = jh;
+ jh->b_bh = bh;
+ atomic_inc(&bh->b_count);
+ spin_unlock(&jh_splice_lock);
+ BUFFER_TRACE(bh, "added journal_head");
+ }
+ }
+ jh->b_jcount++;
+ spin_unlock(&journal_datalist_lock);
+ return bh->b_private;
+}
+
+/*
+ * journal_remove_journal_head(): if the buffer isn't attached to a transaction
+ * and has a zero b_jcount then remove and release its journal_head. If we did
+ * see that the buffer is not used by any transaction we also "logically"
+ * decrement ->b_count.
+ *
+ * We in fact take an additional increment on ->b_count as a convenience,
+ * because the caller usually wants to do additional things with the bh
+ * after calling here.
+ * The caller of journal_remove_journal_head() *must* run __brelse(bh) at some
+ * time. Once the caller has run __brelse(), the buffer is eligible for
+ * reaping by try_to_free_buffers().
+ *
+ * Requires journal_datalist_lock.
+ */
+void __journal_remove_journal_head(struct buffer_head *bh)
+{
+ struct journal_head *jh = bh2jh(bh);
+
+ assert_spin_locked(&journal_datalist_lock);
+ J_ASSERT_JH(jh, jh->b_jcount >= 0);
+ atomic_inc(&bh->b_count);
+ if (jh->b_jcount == 0) {
+ if (jh->b_transaction == NULL &&
+ jh->b_next_transaction == NULL &&
+ jh->b_cp_transaction == NULL) {
+ J_ASSERT_BH(bh, buffer_jbd(bh));
+ J_ASSERT_BH(bh, jh2bh(jh) == bh);
+ BUFFER_TRACE(bh, "remove journal_head");
+ spin_lock(&jh_splice_lock);
+ bh->b_private = NULL;
+ jh->b_bh = NULL; /* debug, really */
+ clear_bit(BH_JBD, &bh->b_state);
+ __brelse(bh);
+ spin_unlock(&jh_splice_lock);
+ journal_free_journal_head(jh);
+ } else {
+ BUFFER_TRACE(bh, "journal_head was locked");
+ }
+ }
+}
+
+void journal_unlock_journal_head(struct journal_head *jh)
+{
+ spin_lock(&journal_datalist_lock);
+ J_ASSERT_JH(jh, jh->b_jcount > 0);
+ --jh->b_jcount;
+ if (!jh->b_jcount && !jh->b_transaction) {
+ struct buffer_head *bh;
+ bh = jh2bh(jh);
+ __journal_remove_journal_head(bh);
+ __brelse(bh);
+ }
+
+ spin_unlock(&journal_datalist_lock);
+}
+
+void journal_remove_journal_head(struct buffer_head *bh)
+{
+ spin_lock(&journal_datalist_lock);
+ __journal_remove_journal_head(bh);
+ spin_unlock(&journal_datalist_lock);
+}
+
+/*
+ * /proc tunables
+ */
+#if defined(CONFIG_JBD_DEBUG)
+int journal_enable_debug;
+EXPORT_SYMBOL(journal_enable_debug);
+#endif
+
+#if defined(CONFIG_JBD_DEBUG) && defined(CONFIG_PROC_FS)
+
+static struct proc_dir_entry *proc_jbd_debug;
+
+int read_jbd_debug(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int ret;
+
+ ret = sprintf(page + off, "%d\n", journal_enable_debug);
+ *eof = 1;
+ return ret;
+}
+
+int write_jbd_debug(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+ char buf[32];
+
+ if (count > ARRAY_SIZE(buf) - 1)
+ count = ARRAY_SIZE(buf) - 1;
+ if (copy_from_user(buf, buffer, count))
+ return -EFAULT;
+ buf[ARRAY_SIZE(buf) - 1] = '\0';
+ journal_enable_debug = simple_strtoul(buf, NULL, 10);
+ return count;
+}
+
+#define JBD_PROC_NAME "sys/fs/jbd-debug"
+
+static void __init create_jbd_proc_entry(void)
+{
+ proc_jbd_debug = create_proc_entry(JBD_PROC_NAME, 0644, NULL);
+ if (proc_jbd_debug) {
+ /* Why is this so hard? */
+ proc_jbd_debug->read_proc = read_jbd_debug;
+ proc_jbd_debug->write_proc = write_jbd_debug;
+ }
+}
+
+static void __exit remove_jbd_proc_entry(void)
+{
+ if (proc_jbd_debug)
+ remove_proc_entry(JBD_PROC_NAME, NULL);
+}
+
+#else
+
+#define create_jbd_proc_entry() do {} while (0)
+#define remove_jbd_proc_entry() do {} while (0)
+
+#endif
+
+/*
+ * Module startup and shutdown
+ */
+
+static int __init journal_init_caches(void)
+{
+ int ret;
+
+ ret = journal_init_revoke_caches();
+ if (ret == 0)
+ ret = journal_init_journal_head_cache();
+ return ret;
+}
+
+static void journal_destroy_caches(void)
+{
+ journal_destroy_revoke_caches();
+ journal_destroy_journal_head_cache();
+}
+
+static int __init journal_init(void)
+{
+ int ret;
+
+ printk(KERN_INFO "Journalled Block Device driver loaded\n");
+ ret = journal_init_caches();
+ if (ret != 0)
+ journal_destroy_caches();
+ create_jbd_proc_entry();
+ return ret;
+}
+
+static void __exit journal_exit(void)
+{
+#ifdef CONFIG_JBD_DEBUG
+ int n = atomic_read(&nr_journal_heads);
+ if (n)
+ printk(KERN_EMERG "JBD: leaked %d journal_heads!\n", n);
+#endif
+ remove_jbd_proc_entry();
+ journal_destroy_caches();
+}
+
+MODULE_LICENSE("GPL");
+module_init(journal_init);
+module_exit(journal_exit);
+
diff --git a/fs/jbd/recovery.c b/fs/jbd/recovery.c
new file mode 100644
index 00000000000000..35ec8c278c8bd9
--- /dev/null
+++ b/fs/jbd/recovery.c
@@ -0,0 +1,594 @@
+/*
+ * linux/fs/recovery.c
+ *
+ * Written by Stephen C. Tweedie <sct@redhat.com>, 1999
+ *
+ * Copyright 1999-2000 Red Hat Software --- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * Journal recovery routines for the generic filesystem journaling code;
+ * part of the ext2fs journaling system.
+ */
+
+#ifndef __KERNEL__
+#include "jfs_user.h"
+#else
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/jbd.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/locks.h>
+#endif
+
+/*
+ * Maintain information about the progress of the recovery job, so that
+ * the different passes can carry information between them.
+ */
+struct recovery_info
+{
+ tid_t start_transaction;
+ tid_t end_transaction;
+
+ int nr_replays;
+ int nr_revokes;
+ int nr_revoke_hits;
+};
+
+enum passtype {PASS_SCAN, PASS_REVOKE, PASS_REPLAY};
+static int do_one_pass(journal_t *journal,
+ struct recovery_info *info, enum passtype pass);
+static int scan_revoke_records(journal_t *, struct buffer_head *,
+ tid_t, struct recovery_info *);
+
+#ifdef __KERNEL__
+
+/* Release readahead buffers after use */
+void journal_brelse_array(struct buffer_head *b[], int n)
+{
+ while (--n >= 0)
+ brelse (b[n]);
+}
+
+
+/*
+ * When reading from the journal, we are going through the block device
+ * layer directly and so there is no readahead being done for us. We
+ * need to implement any readahead ourselves if we want it to happen at
+ * all. Recovery is basically one long sequential read, so make sure we
+ * do the IO in reasonably large chunks.
+ *
+ * This is not so critical that we need to be enormously clever about
+ * the readahead size, though. 128K is a purely arbitrary, good-enough
+ * fixed value.
+ */
+
+#define MAXBUF 8
+static int do_readahead(journal_t *journal, unsigned int start)
+{
+ int err;
+ unsigned int max, nbufs, next;
+ unsigned long blocknr;
+ struct buffer_head *bh;
+
+ struct buffer_head * bufs[MAXBUF];
+
+ /* Do up to 128K of readahead */
+ max = start + (128 * 1024 / journal->j_blocksize);
+ if (max > journal->j_maxlen)
+ max = journal->j_maxlen;
+
+ /* Do the readahead itself. We'll submit MAXBUF buffer_heads at
+ * a time to the block device IO layer. */
+
+ nbufs = 0;
+
+ for (next = start; next < max; next++) {
+ err = journal_bmap(journal, next, &blocknr);
+
+ if (err) {
+ printk (KERN_ERR "JBD: bad block at offset %u\n",
+ next);
+ goto failed;
+ }
+
+ bh = getblk(journal->j_dev, blocknr, journal->j_blocksize);
+ if (!bh) {
+ err = -ENOMEM;
+ goto failed;
+ }
+
+ if (!buffer_uptodate(bh) && !buffer_locked(bh)) {
+ bufs[nbufs++] = bh;
+ if (nbufs == MAXBUF) {
+ ll_rw_block(READ, nbufs, bufs);
+ journal_brelse_array(bufs, nbufs);
+ nbufs = 0;
+ }
+ } else
+ brelse(bh);
+ }
+
+ if (nbufs)
+ ll_rw_block(READ, nbufs, bufs);
+ err = 0;
+
+failed:
+ if (nbufs)
+ journal_brelse_array(bufs, nbufs);
+ return err;
+}
+
+#endif /* __KERNEL__ */
+
+
+/*
+ * Read a block from the journal
+ */
+
+static int jread(struct buffer_head **bhp, journal_t *journal,
+ unsigned int offset)
+{
+ int err;
+ unsigned long blocknr;
+ struct buffer_head *bh;
+
+ *bhp = NULL;
+
+ J_ASSERT (offset < journal->j_maxlen);
+
+ err = journal_bmap(journal, offset, &blocknr);
+
+ if (err) {
+ printk (KERN_ERR "JBD: bad block at offset %u\n",
+ offset);
+ return err;
+ }
+
+ bh = getblk(journal->j_dev, blocknr, journal->j_blocksize);
+ if (!bh)
+ return -ENOMEM;
+
+ if (!buffer_uptodate(bh)) {
+ /* If this is a brand new buffer, start readahead.
+ Otherwise, we assume we are already reading it. */
+ if (!buffer_req(bh))
+ do_readahead(journal, offset);
+ wait_on_buffer(bh);
+ }
+
+ if (!buffer_uptodate(bh)) {
+ printk (KERN_ERR "JBD: Failed to read block at offset %u\n",
+ offset);
+ brelse(bh);
+ return -EIO;
+ }
+
+ *bhp = bh;
+ return 0;
+}
+
+
+/*
+ * Count the number of in-use tags in a journal descriptor block.
+ */
+
+static int count_tags(struct buffer_head *bh, int size)
+{
+ char * tagp;
+ journal_block_tag_t * tag;
+ int nr = 0;
+
+ tagp = &bh->b_data[sizeof(journal_header_t)];
+
+ while ((tagp - bh->b_data + sizeof(journal_block_tag_t)) <= size) {
+ tag = (journal_block_tag_t *) tagp;
+
+ nr++;
+ tagp += sizeof(journal_block_tag_t);
+ if (!(tag->t_flags & htonl(JFS_FLAG_SAME_UUID)))
+ tagp += 16;
+
+ if (tag->t_flags & htonl(JFS_FLAG_LAST_TAG))
+ break;
+ }
+
+ return nr;
+}
+
+
+/* Make sure we wrap around the log correctly! */
+#define wrap(journal, var) \
+do { \
+ if (var >= (journal)->j_last) \
+ var -= ((journal)->j_last - (journal)->j_first); \
+} while (0)
+
+/**
+ * int journal_recover(journal_t *journal) - recovers a on-disk journal
+ * @journal: the journal to recover
+ *
+ * The primary function for recovering the log contents when mounting a
+ * journaled device.
+ */
+int journal_recover(journal_t *journal)
+{
+/*
+ * Recovery is done in three passes. In the first pass, we look for the
+ * end of the log. In the second, we assemble the list of revoke
+ * blocks. In the third and final pass, we replay any un-revoked blocks
+ * in the log.
+ */
+
+ int err;
+ journal_superblock_t * sb;
+
+ struct recovery_info info;
+
+ memset(&info, 0, sizeof(info));
+ sb = journal->j_superblock;
+
+ /*
+ * The journal superblock's s_start field (the current log head)
+ * is always zero if, and only if, the journal was cleanly
+ * unmounted.
+ */
+
+ if (!sb->s_start) {
+ jbd_debug(1, "No recovery required, last transaction %d\n",
+ ntohl(sb->s_sequence));
+ journal->j_transaction_sequence = ntohl(sb->s_sequence) + 1;
+ return 0;
+ }
+
+
+ err = do_one_pass(journal, &info, PASS_SCAN);
+ if (!err)
+ err = do_one_pass(journal, &info, PASS_REVOKE);
+ if (!err)
+ err = do_one_pass(journal, &info, PASS_REPLAY);
+
+ jbd_debug(0, "JBD: recovery, exit status %d, "
+ "recovered transactions %u to %u\n",
+ err, info.start_transaction, info.end_transaction);
+ jbd_debug(0, "JBD: Replayed %d and revoked %d/%d blocks\n",
+ info.nr_replays, info.nr_revoke_hits, info.nr_revokes);
+
+ /* Restart the log at the next transaction ID, thus invalidating
+ * any existing commit records in the log. */
+ journal->j_transaction_sequence = ++info.end_transaction;
+
+ journal_clear_revoke(journal);
+ fsync_no_super(journal->j_fs_dev);
+ return err;
+}
+
+/**
+ * int journal_skip_recovery() - Start journal and wipe exiting records
+ * @journal: journal to startup
+ *
+ * Locate any valid recovery information from the journal and set up the
+ * journal structures in memory to ignore it (presumably because the
+ * caller has evidence that it is out of date).
+ * This function does'nt appear to be exorted..
+ */
+int journal_skip_recovery(journal_t *journal)
+{
+/*
+ * We perform one pass over the journal to allow us to tell the user how
+ * much recovery information is being erased, and to let us initialise
+ * the journal transaction sequence numbers to the next unused ID.
+ */
+
+ int err;
+ journal_superblock_t * sb;
+
+ struct recovery_info info;
+
+ memset (&info, 0, sizeof(info));
+ sb = journal->j_superblock;
+
+ err = do_one_pass(journal, &info, PASS_SCAN);
+
+ if (err) {
+ printk(KERN_ERR "JBD: error %d scanning journal\n", err);
+ ++journal->j_transaction_sequence;
+ } else {
+#ifdef CONFIG_JBD_DEBUG
+ int dropped = info.end_transaction - ntohl(sb->s_sequence);
+#endif
+
+ jbd_debug(0,
+ "JBD: ignoring %d transaction%s from the journal.\n",
+ dropped, (dropped == 1) ? "" : "s");
+ journal->j_transaction_sequence = ++info.end_transaction;
+ }
+
+ journal->j_tail = 0;
+
+ return err;
+}
+
+static int do_one_pass(journal_t *journal,
+ struct recovery_info *info, enum passtype pass)
+{
+
+ unsigned int first_commit_ID, next_commit_ID;
+ unsigned long next_log_block;
+ int err, success = 0;
+ journal_superblock_t * sb;
+ journal_header_t * tmp;
+ struct buffer_head * bh;
+ unsigned int sequence;
+ int blocktype;
+
+ /* Precompute the maximum metadata descriptors in a descriptor block */
+ int MAX_BLOCKS_PER_DESC;
+ MAX_BLOCKS_PER_DESC = ((journal->j_blocksize-sizeof(journal_header_t))
+ / sizeof(journal_block_tag_t));
+
+ /*
+ * First thing is to establish what we expect to find in the log
+ * (in terms of transaction IDs), and where (in terms of log
+ * block offsets): query the superblock.
+ */
+
+ sb = journal->j_superblock;
+ next_commit_ID = ntohl(sb->s_sequence);
+ next_log_block = ntohl(sb->s_start);
+
+ first_commit_ID = next_commit_ID;
+ if (pass == PASS_SCAN)
+ info->start_transaction = first_commit_ID;
+
+ jbd_debug(1, "Starting recovery pass %d\n", pass);
+
+ /*
+ * Now we walk through the log, transaction by transaction,
+ * making sure that each transaction has a commit block in the
+ * expected place. Each complete transaction gets replayed back
+ * into the main filesystem.
+ */
+
+ while (1) {
+ int flags;
+ char * tagp;
+ journal_block_tag_t * tag;
+ struct buffer_head * obh;
+ struct buffer_head * nbh;
+
+ /* If we already know where to stop the log traversal,
+ * check right now that we haven't gone past the end of
+ * the log. */
+
+ if (pass != PASS_SCAN)
+ if (tid_geq(next_commit_ID, info->end_transaction))
+ break;
+
+ jbd_debug(2, "Scanning for sequence ID %u at %lu/%lu\n",
+ next_commit_ID, next_log_block, journal->j_last);
+
+ /* Skip over each chunk of the transaction looking
+ * either the next descriptor block or the final commit
+ * record. */
+
+ jbd_debug(3, "JBD: checking block %ld\n", next_log_block);
+ err = jread(&bh, journal, next_log_block);
+ if (err)
+ goto failed;
+
+ next_log_block++;
+ wrap(journal, next_log_block);
+
+ /* What kind of buffer is it?
+ *
+ * If it is a descriptor block, check that it has the
+ * expected sequence number. Otherwise, we're all done
+ * here. */
+
+ tmp = (journal_header_t *)bh->b_data;
+
+ if (tmp->h_magic != htonl(JFS_MAGIC_NUMBER)) {
+ brelse(bh);
+ break;
+ }
+
+ blocktype = ntohl(tmp->h_blocktype);
+ sequence = ntohl(tmp->h_sequence);
+ jbd_debug(3, "Found magic %d, sequence %d\n",
+ blocktype, sequence);
+
+ if (sequence != next_commit_ID) {
+ brelse(bh);
+ break;
+ }
+
+ /* OK, we have a valid descriptor block which matches
+ * all of the sequence number checks. What are we going
+ * to do with it? That depends on the pass... */
+
+ switch(blocktype) {
+ case JFS_DESCRIPTOR_BLOCK:
+ /* If it is a valid descriptor block, replay it
+ * in pass REPLAY; otherwise, just skip over the
+ * blocks it describes. */
+ if (pass != PASS_REPLAY) {
+ next_log_block +=
+ count_tags(bh, journal->j_blocksize);
+ wrap(journal, next_log_block);
+ brelse(bh);
+ continue;
+ }
+
+ /* A descriptor block: we can now write all of
+ * the data blocks. Yay, useful work is finally
+ * getting done here! */
+
+ tagp = &bh->b_data[sizeof(journal_header_t)];
+ while ((tagp - bh->b_data +sizeof(journal_block_tag_t))
+ <= journal->j_blocksize) {
+ unsigned long io_block;
+
+ tag = (journal_block_tag_t *) tagp;
+ flags = ntohl(tag->t_flags);
+
+ io_block = next_log_block++;
+ wrap(journal, next_log_block);
+ err = jread(&obh, journal, io_block);
+ if (err) {
+ /* Recover what we can, but
+ * report failure at the end. */
+ success = err;
+ printk (KERN_ERR
+ "JBD: IO error %d recovering "
+ "block %ld in log\n",
+ err, io_block);
+ } else {
+ unsigned long blocknr;
+
+ J_ASSERT(obh != NULL);
+ blocknr = ntohl(tag->t_blocknr);
+
+ /* If the block has been
+ * revoked, then we're all done
+ * here. */
+ if (journal_test_revoke
+ (journal, blocknr,
+ next_commit_ID)) {
+ brelse(obh);
+ ++info->nr_revoke_hits;
+ goto skip_write;
+ }
+
+ /* Find a buffer for the new
+ * data being restored */
+ nbh = getblk(journal->j_fs_dev, blocknr,
+ journal->j_blocksize);
+ if (nbh == NULL) {
+ printk(KERN_ERR
+ "JBD: Out of memory "
+ "during recovery.\n");
+ err = -ENOMEM;
+ brelse(bh);
+ brelse(obh);
+ goto failed;
+ }
+
+ lock_buffer(nbh);
+ memcpy(nbh->b_data, obh->b_data,
+ journal->j_blocksize);
+ if (flags & JFS_FLAG_ESCAPE) {
+ *((unsigned int *)bh->b_data) =
+ htonl(JFS_MAGIC_NUMBER);
+ }
+
+ BUFFER_TRACE(nbh, "marking dirty");
+ mark_buffer_dirty(nbh);
+ BUFFER_TRACE(nbh, "marking uptodate");
+ mark_buffer_uptodate(nbh, 1);
+ unlock_buffer(nbh);
+ ++info->nr_replays;
+ /* ll_rw_block(WRITE, 1, &nbh); */
+ brelse(obh);
+ brelse(nbh);
+ }
+
+ skip_write:
+ tagp += sizeof(journal_block_tag_t);
+ if (!(flags & JFS_FLAG_SAME_UUID))
+ tagp += 16;
+
+ if (flags & JFS_FLAG_LAST_TAG)
+ break;
+ }
+
+ brelse(bh);
+ continue;
+
+ case JFS_COMMIT_BLOCK:
+ /* Found an expected commit block: not much to
+ * do other than move on to the next sequence
+ * number. */
+ brelse(bh);
+ next_commit_ID++;
+ continue;
+
+ case JFS_REVOKE_BLOCK:
+ /* If we aren't in the REVOKE pass, then we can
+ * just skip over this block. */
+ if (pass != PASS_REVOKE) {
+ brelse(bh);
+ continue;
+ }
+
+ err = scan_revoke_records(journal, bh,
+ next_commit_ID, info);
+ brelse(bh);
+ if (err)
+ goto failed;
+ continue;
+
+ default:
+ jbd_debug(3, "Unrecognised magic %d, end of scan.\n",
+ blocktype);
+ goto done;
+ }
+ }
+
+ done:
+ /*
+ * We broke out of the log scan loop: either we came to the
+ * known end of the log or we found an unexpected block in the
+ * log. If the latter happened, then we know that the "current"
+ * transaction marks the end of the valid log.
+ */
+
+ if (pass == PASS_SCAN)
+ info->end_transaction = next_commit_ID;
+ else {
+ /* It's really bad news if different passes end up at
+ * different places (but possible due to IO errors). */
+ if (info->end_transaction != next_commit_ID) {
+ printk (KERN_ERR "JBD: recovery pass %d ended at "
+ "transaction %u, expected %u\n",
+ pass, next_commit_ID, info->end_transaction);
+ if (!success)
+ success = -EIO;
+ }
+ }
+
+ return success;
+
+ failed:
+ return err;
+}
+
+
+/* Scan a revoke record, marking all blocks mentioned as revoked. */
+
+static int scan_revoke_records(journal_t *journal, struct buffer_head *bh,
+ tid_t sequence, struct recovery_info *info)
+{
+ journal_revoke_header_t *header;
+ int offset, max;
+
+ header = (journal_revoke_header_t *) bh->b_data;
+ offset = sizeof(journal_revoke_header_t);
+ max = ntohl(header->r_count);
+
+ while (offset < max) {
+ unsigned long blocknr;
+ int err;
+
+ blocknr = ntohl(* ((unsigned int *) (bh->b_data+offset)));
+ offset += 4;
+ err = journal_set_revoke(journal, blocknr, sequence);
+ if (err)
+ return err;
+ ++info->nr_revokes;
+ }
+ return 0;
+}
diff --git a/fs/jbd/revoke.c b/fs/jbd/revoke.c
new file mode 100644
index 00000000000000..e8bee4a2f06ab5
--- /dev/null
+++ b/fs/jbd/revoke.c
@@ -0,0 +1,635 @@
+/*
+ * linux/fs/revoke.c
+ *
+ * Written by Stephen C. Tweedie <sct@redhat.com>, 2000
+ *
+ * Copyright 2000 Red Hat corp --- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * Journal revoke routines for the generic filesystem journaling code;
+ * part of the ext2fs journaling system.
+ *
+ * Revoke is the mechanism used to prevent old log records for deleted
+ * metadata from being replayed on top of newer data using the same
+ * blocks. The revoke mechanism is used in two separate places:
+ *
+ * + Commit: during commit we write the entire list of the current
+ * transaction's revoked blocks to the journal
+ *
+ * + Recovery: during recovery we record the transaction ID of all
+ * revoked blocks. If there are multiple revoke records in the log
+ * for a single block, only the last one counts, and if there is a log
+ * entry for a block beyond the last revoke, then that log entry still
+ * gets replayed.
+ *
+ * We can get interactions between revokes and new log data within a
+ * single transaction:
+ *
+ * Block is revoked and then journaled:
+ * The desired end result is the journaling of the new block, so we
+ * cancel the revoke before the transaction commits.
+ *
+ * Block is journaled and then revoked:
+ * The revoke must take precedence over the write of the block, so we
+ * need either to cancel the journal entry or to write the revoke
+ * later in the log than the log block. In this case, we choose the
+ * latter: journaling a block cancels any revoke record for that block
+ * in the current transaction, so any revoke for that block in the
+ * transaction must have happened after the block was journaled and so
+ * the revoke must take precedence.
+ *
+ * Block is revoked and then written as data:
+ * The data write is allowed to succeed, but the revoke is _not_
+ * cancelled. We still need to prevent old log records from
+ * overwriting the new data. We don't even need to clear the revoke
+ * bit here.
+ *
+ * Revoke information on buffers is a tri-state value:
+ *
+ * RevokeValid clear: no cached revoke status, need to look it up
+ * RevokeValid set, Revoked clear:
+ * buffer has not been revoked, and cancel_revoke
+ * need do nothing.
+ * RevokeValid set, Revoked set:
+ * buffer has been revoked.
+ */
+
+#ifndef __KERNEL__
+#include "jfs_user.h"
+#else
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/jbd.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/locks.h>
+#include <linux/list.h>
+#include <linux/smp_lock.h>
+#include <linux/init.h>
+#endif
+
+static kmem_cache_t *revoke_record_cache;
+static kmem_cache_t *revoke_table_cache;
+
+/* Each revoke record represents one single revoked block. During
+ journal replay, this involves recording the transaction ID of the
+ last transaction to revoke this block. */
+
+struct jbd_revoke_record_s
+{
+ struct list_head hash;
+ tid_t sequence; /* Used for recovery only */
+ unsigned long blocknr;
+};
+
+
+/* The revoke table is just a simple hash table of revoke records. */
+struct jbd_revoke_table_s
+{
+ /* It is conceivable that we might want a larger hash table
+ * for recovery. Must be a power of two. */
+ int hash_size;
+ int hash_shift;
+ struct list_head *hash_table;
+};
+
+
+#ifdef __KERNEL__
+static void write_one_revoke_record(journal_t *, transaction_t *,
+ struct journal_head **, int *,
+ struct jbd_revoke_record_s *);
+static void flush_descriptor(journal_t *, struct journal_head *, int);
+#endif
+
+/* Utility functions to maintain the revoke table */
+
+/* Borrowed from buffer.c: this is a tried and tested block hash function */
+static inline int hash(journal_t *journal, unsigned long block)
+{
+ struct jbd_revoke_table_s *table = journal->j_revoke;
+ int hash_shift = table->hash_shift;
+
+ return ((block << (hash_shift - 6)) ^
+ (block >> 13) ^
+ (block << (hash_shift - 12))) & (table->hash_size - 1);
+}
+
+int insert_revoke_hash(journal_t *journal, unsigned long blocknr, tid_t seq)
+{
+ struct list_head *hash_list;
+ struct jbd_revoke_record_s *record;
+
+repeat:
+ record = kmem_cache_alloc(revoke_record_cache, GFP_NOFS);
+ if (!record)
+ goto oom;
+
+ record->sequence = seq;
+ record->blocknr = blocknr;
+ hash_list = &journal->j_revoke->hash_table[hash(journal, blocknr)];
+ list_add(&record->hash, hash_list);
+ return 0;
+
+oom:
+ if (!journal_oom_retry)
+ return -ENOMEM;
+ jbd_debug(1, "ENOMEM in %s, retrying.\n", __FUNCTION__);
+ yield();
+ goto repeat;
+}
+
+/* Find a revoke record in the journal's hash table. */
+
+static struct jbd_revoke_record_s *find_revoke_record(journal_t *journal,
+ unsigned long blocknr)
+{
+ struct list_head *hash_list;
+ struct jbd_revoke_record_s *record;
+
+ hash_list = &journal->j_revoke->hash_table[hash(journal, blocknr)];
+
+ record = (struct jbd_revoke_record_s *) hash_list->next;
+ while (&(record->hash) != hash_list) {
+ if (record->blocknr == blocknr)
+ return record;
+ record = (struct jbd_revoke_record_s *) record->hash.next;
+ }
+ return NULL;
+}
+
+int __init journal_init_revoke_caches(void)
+{
+ revoke_record_cache = kmem_cache_create("revoke_record",
+ sizeof(struct jbd_revoke_record_s),
+ 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+ if (revoke_record_cache == 0)
+ return -ENOMEM;
+
+ revoke_table_cache = kmem_cache_create("revoke_table",
+ sizeof(struct jbd_revoke_table_s),
+ 0, 0, NULL, NULL);
+ if (revoke_table_cache == 0) {
+ kmem_cache_destroy(revoke_record_cache);
+ revoke_record_cache = NULL;
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+void journal_destroy_revoke_caches(void)
+{
+ kmem_cache_destroy(revoke_record_cache);
+ revoke_record_cache = 0;
+ kmem_cache_destroy(revoke_table_cache);
+ revoke_table_cache = 0;
+}
+
+/* Initialise the revoke table for a given journal to a given size. */
+
+int journal_init_revoke(journal_t *journal, int hash_size)
+{
+ int shift, tmp;
+
+ J_ASSERT (journal->j_revoke == NULL);
+
+ journal->j_revoke = kmem_cache_alloc(revoke_table_cache, GFP_KERNEL);
+ if (!journal->j_revoke)
+ return -ENOMEM;
+
+ /* Check that the hash_size is a power of two */
+ J_ASSERT ((hash_size & (hash_size-1)) == 0);
+
+ journal->j_revoke->hash_size = hash_size;
+
+ shift = 0;
+ tmp = hash_size;
+ while((tmp >>= 1UL) != 0UL)
+ shift++;
+ journal->j_revoke->hash_shift = shift;
+
+ journal->j_revoke->hash_table =
+ kmalloc(hash_size * sizeof(struct list_head), GFP_KERNEL);
+ if (!journal->j_revoke->hash_table) {
+ kmem_cache_free(revoke_table_cache, journal->j_revoke);
+ journal->j_revoke = NULL;
+ return -ENOMEM;
+ }
+
+ for (tmp = 0; tmp < hash_size; tmp++)
+ INIT_LIST_HEAD(&journal->j_revoke->hash_table[tmp]);
+
+ return 0;
+}
+
+/* Destoy a journal's revoke table. The table must already be empty! */
+
+void journal_destroy_revoke(journal_t *journal)
+{
+ struct jbd_revoke_table_s *table;
+ struct list_head *hash_list;
+ int i;
+
+ table = journal->j_revoke;
+ if (!table)
+ return;
+
+ for (i=0; i<table->hash_size; i++) {
+ hash_list = &table->hash_table[i];
+ J_ASSERT (list_empty(hash_list));
+ }
+
+ kfree(table->hash_table);
+ kmem_cache_free(revoke_table_cache, table);
+ journal->j_revoke = NULL;
+}
+
+
+#ifdef __KERNEL__
+
+/*
+ * journal_revoke: revoke a given buffer_head from the journal. This
+ * prevents the block from being replayed during recovery if we take a
+ * crash after this current transaction commits. Any subsequent
+ * metadata writes of the buffer in this transaction cancel the
+ * revoke.
+ *
+ * Note that this call may block --- it is up to the caller to make
+ * sure that there are no further calls to journal_write_metadata
+ * before the revoke is complete. In ext3, this implies calling the
+ * revoke before clearing the block bitmap when we are deleting
+ * metadata.
+ *
+ * Revoke performs a journal_forget on any buffer_head passed in as a
+ * parameter, but does _not_ forget the buffer_head if the bh was only
+ * found implicitly.
+ *
+ * bh_in may not be a journalled buffer - it may have come off
+ * the hash tables without an attached journal_head.
+ *
+ * If bh_in is non-zero, journal_revoke() will decrement its b_count
+ * by one.
+ */
+
+int journal_revoke(handle_t *handle, unsigned long blocknr,
+ struct buffer_head *bh_in)
+{
+ struct buffer_head *bh = NULL;
+ journal_t *journal;
+ kdev_t dev;
+ int err;
+
+ if (bh_in)
+ BUFFER_TRACE(bh_in, "enter");
+
+ journal = handle->h_transaction->t_journal;
+ if (!journal_set_features(journal, 0, 0, JFS_FEATURE_INCOMPAT_REVOKE)){
+ J_ASSERT (!"Cannot set revoke feature!");
+ return -EINVAL;
+ }
+
+ dev = journal->j_fs_dev;
+ bh = bh_in;
+
+ if (!bh) {
+ bh = get_hash_table(dev, blocknr, journal->j_blocksize);
+ if (bh)
+ BUFFER_TRACE(bh, "found on hash");
+ }
+#ifdef JBD_EXPENSIVE_CHECKING
+ else {
+ struct buffer_head *bh2;
+
+ /* If there is a different buffer_head lying around in
+ * memory anywhere... */
+ bh2 = get_hash_table(dev, blocknr, journal->j_blocksize);
+ if (bh2) {
+ /* ... and it has RevokeValid status... */
+ if ((bh2 != bh) &&
+ test_bit(BH_RevokeValid, &bh2->b_state))
+ /* ...then it better be revoked too,
+ * since it's illegal to create a revoke
+ * record against a buffer_head which is
+ * not marked revoked --- that would
+ * risk missing a subsequent revoke
+ * cancel. */
+ J_ASSERT_BH(bh2, test_bit(BH_Revoked, &
+ bh2->b_state));
+ __brelse(bh2);
+ }
+ }
+#endif
+
+ /* We really ought not ever to revoke twice in a row without
+ first having the revoke cancelled: it's illegal to free a
+ block twice without allocating it in between! */
+ if (bh) {
+ J_ASSERT_BH(bh, !test_bit(BH_Revoked, &bh->b_state));
+ set_bit(BH_Revoked, &bh->b_state);
+ set_bit(BH_RevokeValid, &bh->b_state);
+ if (bh_in) {
+ BUFFER_TRACE(bh_in, "call journal_forget");
+ journal_forget(handle, bh_in);
+ } else {
+ BUFFER_TRACE(bh, "call brelse");
+ __brelse(bh);
+ }
+ }
+
+ lock_journal(journal);
+ jbd_debug(2, "insert revoke for block %lu, bh_in=%p\n", blocknr, bh_in);
+ err = insert_revoke_hash(journal, blocknr,
+ handle->h_transaction->t_tid);
+ unlock_journal(journal);
+ BUFFER_TRACE(bh_in, "exit");
+ return err;
+}
+
+/*
+ * Cancel an outstanding revoke. For use only internally by the
+ * journaling code (called from journal_get_write_access).
+ *
+ * We trust the BH_Revoked bit on the buffer if the buffer is already
+ * being journaled: if there is no revoke pending on the buffer, then we
+ * don't do anything here.
+ *
+ * This would break if it were possible for a buffer to be revoked and
+ * discarded, and then reallocated within the same transaction. In such
+ * a case we would have lost the revoked bit, but when we arrived here
+ * the second time we would still have a pending revoke to cancel. So,
+ * do not trust the Revoked bit on buffers unless RevokeValid is also
+ * set.
+ *
+ * The caller must have the journal locked.
+ */
+int journal_cancel_revoke(handle_t *handle, struct journal_head *jh)
+{
+ struct jbd_revoke_record_s *record;
+ journal_t *journal = handle->h_transaction->t_journal;
+ int need_cancel;
+ int did_revoke = 0; /* akpm: debug */
+ struct buffer_head *bh = jh2bh(jh);
+
+ jbd_debug(4, "journal_head %p, cancelling revoke\n", jh);
+
+ /* Is the existing Revoke bit valid? If so, we trust it, and
+ * only perform the full cancel if the revoke bit is set. If
+ * not, we can't trust the revoke bit, and we need to do the
+ * full search for a revoke record. */
+ if (test_and_set_bit(BH_RevokeValid, &bh->b_state))
+ need_cancel = (test_and_clear_bit(BH_Revoked, &bh->b_state));
+ else {
+ need_cancel = 1;
+ clear_bit(BH_Revoked, &bh->b_state);
+ }
+
+ if (need_cancel) {
+ record = find_revoke_record(journal, bh->b_blocknr);
+ if (record) {
+ jbd_debug(4, "cancelled existing revoke on "
+ "blocknr %lu\n", bh->b_blocknr);
+ list_del(&record->hash);
+ kmem_cache_free(revoke_record_cache, record);
+ did_revoke = 1;
+ }
+ }
+
+#ifdef JBD_EXPENSIVE_CHECKING
+ /* There better not be one left behind by now! */
+ record = find_revoke_record(journal, bh->b_blocknr);
+ J_ASSERT_JH(jh, record == NULL);
+#endif
+
+ /* Finally, have we just cleared revoke on an unhashed
+ * buffer_head? If so, we'd better make sure we clear the
+ * revoked status on any hashed alias too, otherwise the revoke
+ * state machine will get very upset later on. */
+ if (need_cancel && !bh->b_pprev) {
+ struct buffer_head *bh2;
+ bh2 = get_hash_table(bh->b_dev, bh->b_blocknr, bh->b_size);
+ if (bh2) {
+ clear_bit(BH_Revoked, &bh2->b_state);
+ __brelse(bh2);
+ }
+ }
+
+ return did_revoke;
+}
+
+
+/*
+ * Write revoke records to the journal for all entries in the current
+ * revoke hash, deleting the entries as we go.
+ *
+ * Called with the journal lock held.
+ */
+
+void journal_write_revoke_records(journal_t *journal,
+ transaction_t *transaction)
+{
+ struct journal_head *descriptor;
+ struct jbd_revoke_record_s *record;
+ struct jbd_revoke_table_s *revoke;
+ struct list_head *hash_list;
+ int i, offset, count;
+
+ descriptor = NULL;
+ offset = 0;
+ count = 0;
+ revoke = journal->j_revoke;
+
+ for (i = 0; i < revoke->hash_size; i++) {
+ hash_list = &revoke->hash_table[i];
+
+ while (!list_empty(hash_list)) {
+ record = (struct jbd_revoke_record_s *)
+ hash_list->next;
+ write_one_revoke_record(journal, transaction,
+ &descriptor, &offset,
+ record);
+ count++;
+ list_del(&record->hash);
+ kmem_cache_free(revoke_record_cache, record);
+ }
+ }
+ if (descriptor)
+ flush_descriptor(journal, descriptor, offset);
+ jbd_debug(1, "Wrote %d revoke records\n", count);
+}
+
+/*
+ * Write out one revoke record. We need to create a new descriptor
+ * block if the old one is full or if we have not already created one.
+ */
+
+static void write_one_revoke_record(journal_t *journal,
+ transaction_t *transaction,
+ struct journal_head **descriptorp,
+ int *offsetp,
+ struct jbd_revoke_record_s *record)
+{
+ struct journal_head *descriptor;
+ int offset;
+ journal_header_t *header;
+
+ /* If we are already aborting, this all becomes a noop. We
+ still need to go round the loop in
+ journal_write_revoke_records in order to free all of the
+ revoke records: only the IO to the journal is omitted. */
+ if (is_journal_aborted(journal))
+ return;
+
+ descriptor = *descriptorp;
+ offset = *offsetp;
+
+ /* Make sure we have a descriptor with space left for the record */
+ if (descriptor) {
+ if (offset == journal->j_blocksize) {
+ flush_descriptor(journal, descriptor, offset);
+ descriptor = NULL;
+ }
+ }
+
+ if (!descriptor) {
+ descriptor = journal_get_descriptor_buffer(journal);
+ if (!descriptor)
+ return;
+ header = (journal_header_t *) &jh2bh(descriptor)->b_data[0];
+ header->h_magic = htonl(JFS_MAGIC_NUMBER);
+ header->h_blocktype = htonl(JFS_REVOKE_BLOCK);
+ header->h_sequence = htonl(transaction->t_tid);
+
+ /* Record it so that we can wait for IO completion later */
+ JBUFFER_TRACE(descriptor, "file as BJ_LogCtl");
+ journal_file_buffer(descriptor, transaction, BJ_LogCtl);
+
+ offset = sizeof(journal_revoke_header_t);
+ *descriptorp = descriptor;
+ }
+
+ * ((unsigned int *)(&jh2bh(descriptor)->b_data[offset])) =
+ htonl(record->blocknr);
+ offset += 4;
+ *offsetp = offset;
+}
+
+/*
+ * Flush a revoke descriptor out to the journal. If we are aborting,
+ * this is a noop; otherwise we are generating a buffer which needs to
+ * be waited for during commit, so it has to go onto the appropriate
+ * journal buffer list.
+ */
+
+static void flush_descriptor(journal_t *journal,
+ struct journal_head *descriptor,
+ int offset)
+{
+ journal_revoke_header_t *header;
+
+ if (is_journal_aborted(journal)) {
+ JBUFFER_TRACE(descriptor, "brelse");
+ unlock_buffer(jh2bh(descriptor));
+ __brelse(jh2bh(descriptor));
+ return;
+ }
+
+ header = (journal_revoke_header_t *) jh2bh(descriptor)->b_data;
+ header->r_count = htonl(offset);
+ set_bit(BH_JWrite, &jh2bh(descriptor)->b_state);
+ {
+ struct buffer_head *bh = jh2bh(descriptor);
+ BUFFER_TRACE(bh, "write");
+ clear_bit(BH_Dirty, &bh->b_state);
+ bh->b_end_io = journal_end_buffer_io_sync;
+ submit_bh(WRITE, bh);
+ }
+}
+
+#endif
+
+/*
+ * Revoke support for recovery.
+ *
+ * Recovery needs to be able to:
+ *
+ * record all revoke records, including the tid of the latest instance
+ * of each revoke in the journal
+ *
+ * check whether a given block in a given transaction should be replayed
+ * (ie. has not been revoked by a revoke record in that or a subsequent
+ * transaction)
+ *
+ * empty the revoke table after recovery.
+ */
+
+/*
+ * First, setting revoke records. We create a new revoke record for
+ * every block ever revoked in the log as we scan it for recovery, and
+ * we update the existing records if we find multiple revokes for a
+ * single block.
+ */
+
+int journal_set_revoke(journal_t *journal,
+ unsigned long blocknr,
+ tid_t sequence)
+{
+ struct jbd_revoke_record_s *record;
+
+ record = find_revoke_record(journal, blocknr);
+ if (record) {
+ /* If we have multiple occurences, only record the
+ * latest sequence number in the hashed record */
+ if (tid_gt(sequence, record->sequence))
+ record->sequence = sequence;
+ return 0;
+ }
+ return insert_revoke_hash(journal, blocknr, sequence);
+}
+
+/*
+ * Test revoke records. For a given block referenced in the log, has
+ * that block been revoked? A revoke record with a given transaction
+ * sequence number revokes all blocks in that transaction and earlier
+ * ones, but later transactions still need replayed.
+ */
+
+int journal_test_revoke(journal_t *journal,
+ unsigned long blocknr,
+ tid_t sequence)
+{
+ struct jbd_revoke_record_s *record;
+
+ record = find_revoke_record(journal, blocknr);
+ if (!record)
+ return 0;
+ if (tid_gt(sequence, record->sequence))
+ return 0;
+ return 1;
+}
+
+/*
+ * Finally, once recovery is over, we need to clear the revoke table so
+ * that it can be reused by the running filesystem.
+ */
+
+void journal_clear_revoke(journal_t *journal)
+{
+ int i;
+ struct list_head *hash_list;
+ struct jbd_revoke_record_s *record;
+ struct jbd_revoke_table_s *revoke;
+
+ revoke = journal->j_revoke;
+
+ for (i = 0; i < revoke->hash_size; i++) {
+ hash_list = &revoke->hash_table[i];
+ while (!list_empty(hash_list)) {
+ record = (struct jbd_revoke_record_s*) hash_list->next;
+ list_del(&record->hash);
+ kmem_cache_free(revoke_record_cache, record);
+ }
+ }
+}
+
diff --git a/fs/jbd/transaction.c b/fs/jbd/transaction.c
new file mode 100644
index 00000000000000..b2897f022be4a7
--- /dev/null
+++ b/fs/jbd/transaction.c
@@ -0,0 +1,2182 @@
+/*
+ * linux/fs/transaction.c
+ *
+ * Written by Stephen C. Tweedie <sct@redhat.com>, 1998
+ *
+ * Copyright 1998 Red Hat corp --- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * Generic filesystem transaction handling code; part of the ext2fs
+ * journaling system.
+ *
+ * This file manages transactions (compound commits managed by the
+ * journaling code) and handles (individual atomic operations by the
+ * filesystem).
+ */
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/jbd.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/locks.h>
+#include <linux/timer.h>
+#include <linux/smp_lock.h>
+#include <linux/mm.h>
+
+extern spinlock_t journal_datalist_lock;
+
+/*
+ * get_transaction: obtain a new transaction_t object.
+ *
+ * Simply allocate and initialise a new transaction. Create it in
+ * RUNNING state and add it to the current journal (which should not
+ * have an existing running transaction: we only make a new transaction
+ * once we have started to commit the old one).
+ *
+ * Preconditions:
+ * The journal MUST be locked. We don't perform atomic mallocs on the
+ * new transaction and we can't block without protecting against other
+ * processes trying to touch the journal while it is in transition.
+ */
+
+static transaction_t * get_transaction (journal_t * journal, int is_try)
+{
+ transaction_t * transaction;
+
+ transaction = jbd_kmalloc (sizeof (transaction_t), GFP_NOFS);
+ if (!transaction)
+ return NULL;
+
+ memset (transaction, 0, sizeof (transaction_t));
+
+ transaction->t_journal = journal;
+ transaction->t_state = T_RUNNING;
+ transaction->t_tid = journal->j_transaction_sequence++;
+ transaction->t_expires = jiffies + journal->j_commit_interval;
+ INIT_LIST_HEAD(&transaction->t_jcb);
+
+ if (journal->j_commit_interval) {
+ /* Set up the commit timer for the new transaction. */
+ J_ASSERT (!journal->j_commit_timer_active);
+ journal->j_commit_timer_active = 1;
+ journal->j_commit_timer->expires = transaction->t_expires;
+ add_timer(journal->j_commit_timer);
+ }
+
+ J_ASSERT (journal->j_running_transaction == NULL);
+ journal->j_running_transaction = transaction;
+
+ return transaction;
+}
+
+/*
+ * Handle management.
+ *
+ * A handle_t is an object which represents a single atomic update to a
+ * filesystem, and which tracks all of the modifications which form part
+ * of that one update.
+ */
+
+/*
+ * start_this_handle: Given a handle, deal with any locking or stalling
+ * needed to make sure that there is enough journal space for the handle
+ * to begin. Attach the handle to a transaction and set up the
+ * transaction's buffer credits.
+ */
+
+static int start_this_handle(journal_t *journal, handle_t *handle)
+{
+ transaction_t *transaction;
+ int needed;
+ int nblocks = handle->h_buffer_credits;
+
+ if (nblocks > journal->j_max_transaction_buffers) {
+ jbd_debug(1, "JBD: %s wants too many credits (%d > %d)\n",
+ current->comm, nblocks,
+ journal->j_max_transaction_buffers);
+ return -ENOSPC;
+ }
+
+ jbd_debug(3, "New handle %p going live.\n", handle);
+
+repeat:
+
+ lock_journal(journal);
+
+repeat_locked:
+
+ if (is_journal_aborted(journal) ||
+ (journal->j_errno != 0 && !(journal->j_flags & JFS_ACK_ERR))) {
+ unlock_journal(journal);
+ return -EROFS;
+ }
+
+ /* Wait on the journal's transaction barrier if necessary */
+ if (journal->j_barrier_count) {
+ unlock_journal(journal);
+ sleep_on(&journal->j_wait_transaction_locked);
+ goto repeat;
+ }
+
+ if (!journal->j_running_transaction)
+ get_transaction(journal, 0);
+ /* @@@ Error? */
+ J_ASSERT(journal->j_running_transaction);
+
+ transaction = journal->j_running_transaction;
+
+ /* If the current transaction is locked down for commit, wait
+ * for the lock to be released. */
+
+ if (transaction->t_state == T_LOCKED) {
+ unlock_journal(journal);
+ jbd_debug(3, "Handle %p stalling...\n", handle);
+ sleep_on(&journal->j_wait_transaction_locked);
+ goto repeat;
+ }
+
+ /* If there is not enough space left in the log to write all
+ * potential buffers requested by this operation, we need to
+ * stall pending a log checkpoint to free some more log
+ * space. */
+
+ needed = transaction->t_outstanding_credits + nblocks;
+
+ if (needed > journal->j_max_transaction_buffers) {
+ /* If the current transaction is already too large, then
+ * start to commit it: we can then go back and attach
+ * this handle to a new transaction. */
+
+ jbd_debug(2, "Handle %p starting new commit...\n", handle);
+ log_start_commit(journal, transaction);
+ unlock_journal(journal);
+ sleep_on(&journal->j_wait_transaction_locked);
+ lock_journal(journal);
+ goto repeat_locked;
+ }
+
+ /*
+ * The commit code assumes that it can get enough log space
+ * without forcing a checkpoint. This is *critical* for
+ * correctness: a checkpoint of a buffer which is also
+ * associated with a committing transaction creates a deadlock,
+ * so commit simply cannot force through checkpoints.
+ *
+ * We must therefore ensure the necessary space in the journal
+ * *before* starting to dirty potentially checkpointed buffers
+ * in the new transaction.
+ *
+ * The worst part is, any transaction currently committing can
+ * reduce the free space arbitrarily. Be careful to account for
+ * those buffers when checkpointing.
+ */
+
+ /*
+ * @@@ AKPM: This seems rather over-defensive. We're giving commit
+ * a _lot_ of headroom: 1/4 of the journal plus the size of
+ * the committing transaction. Really, we only need to give it
+ * committing_transaction->t_outstanding_credits plus "enough" for
+ * the log control blocks.
+ * Also, this test is inconsitent with the matching one in
+ * journal_extend().
+ */
+ needed = journal->j_max_transaction_buffers;
+ if (journal->j_committing_transaction)
+ needed += journal->j_committing_transaction->
+ t_outstanding_credits;
+
+ if (log_space_left(journal) < needed) {
+ jbd_debug(2, "Handle %p waiting for checkpoint...\n", handle);
+ log_wait_for_space(journal, needed);
+ goto repeat_locked;
+ }
+
+ /* OK, account for the buffers that this operation expects to
+ * use and add the handle to the running transaction. */
+
+ handle->h_transaction = transaction;
+ transaction->t_outstanding_credits += nblocks;
+ transaction->t_updates++;
+ transaction->t_handle_count++;
+ jbd_debug(4, "Handle %p given %d credits (total %d, free %d)\n",
+ handle, nblocks, transaction->t_outstanding_credits,
+ log_space_left(journal));
+
+ unlock_journal(journal);
+
+ return 0;
+}
+
+/* Allocate a new handle. This should probably be in a slab... */
+static handle_t *new_handle(int nblocks)
+{
+ handle_t *handle = jbd_kmalloc(sizeof (handle_t), GFP_NOFS);
+ if (!handle)
+ return NULL;
+ memset(handle, 0, sizeof (handle_t));
+ handle->h_buffer_credits = nblocks;
+ handle->h_ref = 1;
+ INIT_LIST_HEAD(&handle->h_jcb);
+
+ return handle;
+}
+
+/**
+ * handle_t *journal_start() - Obtain a new handle.
+ * @journal: Journal to start transaction on.
+ * @nblocks: number of block buffer we might modify
+ *
+ * We make sure that the transaction can guarantee at least nblocks of
+ * modified buffers in the log. We block until the log can guarantee
+ * that much space.
+ *
+ * This function is visible to journal users (like ext3fs), so is not
+ * called with the journal already locked.
+ *
+ * Return a pointer to a newly allocated handle, or NULL on failure
+ */
+handle_t *journal_start(journal_t *journal, int nblocks)
+{
+ handle_t *handle = journal_current_handle();
+ int err;
+
+ if (!journal)
+ return ERR_PTR(-EROFS);
+
+ if (handle) {
+ J_ASSERT(handle->h_transaction->t_journal == journal);
+ handle->h_ref++;
+ return handle;
+ }
+
+ handle = new_handle(nblocks);
+ if (!handle)
+ return ERR_PTR(-ENOMEM);
+
+ current->journal_info = handle;
+
+ err = start_this_handle(journal, handle);
+ if (err < 0) {
+ kfree(handle);
+ current->journal_info = NULL;
+ return ERR_PTR(err);
+ }
+
+ return handle;
+}
+
+/*
+ * Return zero on success
+ */
+static int try_start_this_handle(journal_t *journal, handle_t *handle)
+{
+ transaction_t *transaction;
+ int needed;
+ int nblocks = handle->h_buffer_credits;
+ int ret = 0;
+
+ jbd_debug(3, "New handle %p maybe going live.\n", handle);
+
+ lock_journal(journal);
+
+ if (is_journal_aborted(journal) ||
+ (journal->j_errno != 0 && !(journal->j_flags & JFS_ACK_ERR))) {
+ ret = -EROFS;
+ goto fail_unlock;
+ }
+
+ if (journal->j_barrier_count)
+ goto fail_unlock;
+
+ if (!journal->j_running_transaction && get_transaction(journal, 1) == 0)
+ goto fail_unlock;
+
+ transaction = journal->j_running_transaction;
+ if (transaction->t_state == T_LOCKED)
+ goto fail_unlock;
+
+ needed = transaction->t_outstanding_credits + nblocks;
+ /* We could run log_start_commit here */
+ if (needed > journal->j_max_transaction_buffers)
+ goto fail_unlock;
+
+ needed = journal->j_max_transaction_buffers;
+ if (journal->j_committing_transaction)
+ needed += journal->j_committing_transaction->
+ t_outstanding_credits;
+
+ if (log_space_left(journal) < needed)
+ goto fail_unlock;
+
+ handle->h_transaction = transaction;
+ transaction->t_outstanding_credits += nblocks;
+ transaction->t_updates++;
+ jbd_debug(4, "Handle %p given %d credits (total %d, free %d)\n",
+ handle, nblocks, transaction->t_outstanding_credits,
+ log_space_left(journal));
+ unlock_journal(journal);
+ return 0;
+
+fail_unlock:
+ unlock_journal(journal);
+ if (ret >= 0)
+ ret = -1;
+ return ret;
+}
+
+/**
+ * handle_t *journal_try_start() - Don't block, but try and get a handle
+ * @journal: Journal to start transaction on.
+ * @nblocks: number of block buffer we might modify
+ *
+ * Try to start a handle, but non-blockingly. If we weren't able
+ * to, return an ERR_PTR value.
+ */
+handle_t *journal_try_start(journal_t *journal, int nblocks)
+{
+ handle_t *handle = journal_current_handle();
+ int err;
+
+ if (!journal)
+ return ERR_PTR(-EROFS);
+
+ if (handle) {
+ jbd_debug(4, "h_ref %d -> %d\n",
+ handle->h_ref,
+ handle->h_ref + 1);
+ J_ASSERT(handle->h_transaction->t_journal == journal);
+ if (is_handle_aborted(handle))
+ return ERR_PTR(-EIO);
+ handle->h_ref++;
+ return handle;
+ } else {
+ jbd_debug(4, "no current transaction\n");
+ }
+
+ if (is_journal_aborted(journal))
+ return ERR_PTR(-EIO);
+
+ handle = new_handle(nblocks);
+ if (!handle)
+ return ERR_PTR(-ENOMEM);
+
+ current->journal_info = handle;
+
+ err = try_start_this_handle(journal, handle);
+ if (err < 0) {
+ kfree(handle);
+ current->journal_info = NULL;
+ return ERR_PTR(err);
+ }
+
+ return handle;
+}
+
+/**
+ * int journal_extend() - extend buffer credits.
+ * @handle: handle to 'extend'
+ * @nblocks: nr blocks to try to extend by.
+ *
+ * Some transactions, such as large extends and truncates, can be done
+ * atomically all at once or in several stages. The operation requests
+ * a credit for a number of buffer modications in advance, but can
+ * extend its credit if it needs more.
+ *
+ * journal_extend tries to give the running handle more buffer credits.
+ * It does not guarantee that allocation - this is a best-effort only.
+ * The calling process MUST be able to deal cleanly with a failure to
+ * extend here.
+ *
+ * Return 0 on success, non-zero on failure.
+ *
+ * return code < 0 implies an error
+ * return code > 0 implies normal transaction-full status.
+ */
+int journal_extend (handle_t *handle, int nblocks)
+{
+ transaction_t *transaction = handle->h_transaction;
+ journal_t *journal = transaction->t_journal;
+ int result;
+ int wanted;
+
+ lock_journal (journal);
+
+ result = -EIO;
+ if (is_handle_aborted(handle))
+ goto error_out;
+
+ result = 1;
+
+ /* Don't extend a locked-down transaction! */
+ if (handle->h_transaction->t_state != T_RUNNING) {
+ jbd_debug(3, "denied handle %p %d blocks: "
+ "transaction not running\n", handle, nblocks);
+ goto error_out;
+ }
+
+ wanted = transaction->t_outstanding_credits + nblocks;
+
+ if (wanted > journal->j_max_transaction_buffers) {
+ jbd_debug(3, "denied handle %p %d blocks: "
+ "transaction too large\n", handle, nblocks);
+ goto error_out;
+ }
+
+ if (wanted > log_space_left(journal)) {
+ jbd_debug(3, "denied handle %p %d blocks: "
+ "insufficient log space\n", handle, nblocks);
+ goto error_out;
+ }
+
+ handle->h_buffer_credits += nblocks;
+ transaction->t_outstanding_credits += nblocks;
+ result = 0;
+
+ jbd_debug(3, "extended handle %p by %d\n", handle, nblocks);
+
+error_out:
+ unlock_journal (journal);
+ return result;
+}
+
+
+/**
+ * int journal_restart() - restart a handle .
+ * @handle: handle to restart
+ * @nblocks: nr credits requested
+ *
+ * Restart a handle for a multi-transaction filesystem
+ * operation.
+ *
+ * If the journal_extend() call above fails to grant new buffer credits
+ * to a running handle, a call to journal_restart will commit the
+ * handle's transaction so far and reattach the handle to a new
+ * transaction capabable of guaranteeing the requested number of
+ * credits.
+ */
+
+int journal_restart(handle_t *handle, int nblocks)
+{
+ transaction_t *transaction = handle->h_transaction;
+ journal_t *journal = transaction->t_journal;
+ int ret;
+
+ /* If we've had an abort of any type, don't even think about
+ * actually doing the restart! */
+ if (is_handle_aborted(handle))
+ return 0;
+
+ /* First unlink the handle from its current transaction, and
+ * start the commit on that. */
+
+ J_ASSERT (transaction->t_updates > 0);
+ J_ASSERT (journal_current_handle() == handle);
+
+ transaction->t_outstanding_credits -= handle->h_buffer_credits;
+ transaction->t_updates--;
+
+ if (!transaction->t_updates)
+ wake_up(&journal->j_wait_updates);
+
+ jbd_debug(2, "restarting handle %p\n", handle);
+ log_start_commit(journal, transaction);
+
+ handle->h_buffer_credits = nblocks;
+ ret = start_this_handle(journal, handle);
+ return ret;
+}
+
+
+/**
+ * void journal_lock_updates () - establish a transaction barrier.
+ * @journal: Journal to establish a barrier on.
+ *
+ * This locks out any further updates from being started, and blocks
+ * until all existing updates have completed, returning only once the
+ * journal is in a quiescent state with no updates running.
+ *
+ * The journal lock should not be held on entry.
+ */
+void journal_lock_updates (journal_t *journal)
+{
+ lock_journal(journal);
+ ++journal->j_barrier_count;
+
+ /* Wait until there are no running updates */
+ while (1) {
+ transaction_t *transaction = journal->j_running_transaction;
+ if (!transaction)
+ break;
+ if (!transaction->t_updates)
+ break;
+
+ unlock_journal(journal);
+ sleep_on(&journal->j_wait_updates);
+ lock_journal(journal);
+ }
+
+ unlock_journal(journal);
+
+ /* We have now established a barrier against other normal
+ * updates, but we also need to barrier against other
+ * journal_lock_updates() calls to make sure that we serialise
+ * special journal-locked operations too. */
+ down(&journal->j_barrier);
+}
+
+/**
+ * void journal_unlock_updates (journal_t* journal) - release barrier
+ * @journal: Journal to release the barrier on.
+ *
+ * Release a transaction barrier obtained with journal_lock_updates().
+ *
+ * Should be called without the journal lock held.
+ */
+void journal_unlock_updates (journal_t *journal)
+{
+ lock_journal(journal);
+
+ J_ASSERT (journal->j_barrier_count != 0);
+
+ up(&journal->j_barrier);
+ --journal->j_barrier_count;
+ wake_up(&journal->j_wait_transaction_locked);
+ unlock_journal(journal);
+}
+
+/*
+ * if the buffer is already part of the current transaction, then there
+ * is nothing we need to do. if it is already part of a prior
+ * transaction which we are still committing to disk, then we need to
+ * make sure that we do not overwrite the old copy: we do copy-out to
+ * preserve the copy going to disk. we also account the buffer against
+ * the handle's metadata buffer credits (unless the buffer is already
+ * part of the transaction, that is).
+ */
+static int
+do_get_write_access(handle_t *handle, struct journal_head *jh, int force_copy)
+{
+ struct buffer_head *bh;
+ transaction_t *transaction = handle->h_transaction;
+ journal_t *journal = transaction->t_journal;
+ int error;
+ char *frozen_buffer = NULL;
+ int need_copy = 0;
+ int locked;
+
+ jbd_debug(5, "buffer_head %p, force_copy %d\n", jh, force_copy);
+
+ JBUFFER_TRACE(jh, "entry");
+repeat:
+ bh = jh2bh(jh);
+
+ /* @@@ Need to check for errors here at some point. */
+
+ /*
+ * AKPM: we have replaced all the lock_journal_bh_wait() stuff with a
+ * simple lock_journal(). This code here will care for locked buffers.
+ */
+ locked = test_and_set_bit(BH_Lock, &bh->b_state);
+ if (locked) {
+ /* We can't reliably test the buffer state if we found
+ * it already locked, so just wait for the lock and
+ * retry. */
+ unlock_journal(journal);
+ __wait_on_buffer(bh);
+ lock_journal(journal);
+ goto repeat;
+ }
+
+ /* We now hold the buffer lock so it is safe to query the buffer
+ * state. Is the buffer dirty?
+ *
+ * If so, there are two possibilities. The buffer may be
+ * non-journaled, and undergoing a quite legitimate writeback.
+ * Otherwise, it is journaled, and we don't expect dirty buffers
+ * in that state (the buffers should be marked JBD_Dirty
+ * instead.) So either the IO is being done under our own
+ * control and this is a bug, or it's a third party IO such as
+ * dump(8) (which may leave the buffer scheduled for read ---
+ * ie. locked but not dirty) or tune2fs (which may actually have
+ * the buffer dirtied, ugh.) */
+
+ if (buffer_dirty(bh)) {
+ spin_lock(&journal_datalist_lock);
+ /* First question: is this buffer already part of the
+ * current transaction or the existing committing
+ * transaction? */
+ if (jh->b_transaction) {
+ J_ASSERT_JH(jh, jh->b_transaction == transaction ||
+ jh->b_transaction == journal->j_committing_transaction);
+ if (jh->b_next_transaction)
+ J_ASSERT_JH(jh, jh->b_next_transaction == transaction);
+ JBUFFER_TRACE(jh, "Unexpected dirty buffer");
+ jbd_unexpected_dirty_buffer(jh);
+ }
+ spin_unlock(&journal_datalist_lock);
+ }
+
+ unlock_buffer(bh);
+
+ error = -EROFS;
+ if (is_handle_aborted(handle))
+ goto out_unlocked;
+ error = 0;
+
+ spin_lock(&journal_datalist_lock);
+
+ /* The buffer is already part of this transaction if
+ * b_transaction or b_next_transaction points to it. */
+
+ if (jh->b_transaction == transaction ||
+ jh->b_next_transaction == transaction)
+ goto done_locked;
+
+ /* If there is already a copy-out version of this buffer, then
+ * we don't need to make another one. */
+
+ if (jh->b_frozen_data) {
+ JBUFFER_TRACE(jh, "has frozen data");
+ J_ASSERT_JH(jh, jh->b_next_transaction == NULL);
+ jh->b_next_transaction = transaction;
+
+ J_ASSERT_JH(jh, handle->h_buffer_credits > 0);
+ handle->h_buffer_credits--;
+ goto done_locked;
+ }
+
+ /* Is there data here we need to preserve? */
+
+ if (jh->b_transaction && jh->b_transaction != transaction) {
+ JBUFFER_TRACE(jh, "owned by older transaction");
+ J_ASSERT_JH(jh, jh->b_next_transaction == NULL);
+ J_ASSERT_JH(jh, jh->b_transaction ==
+ journal->j_committing_transaction);
+
+ /* There is one case we have to be very careful about.
+ * If the committing transaction is currently writing
+ * this buffer out to disk and has NOT made a copy-out,
+ * then we cannot modify the buffer contents at all
+ * right now. The essence of copy-out is that it is the
+ * extra copy, not the primary copy, which gets
+ * journaled. If the primary copy is already going to
+ * disk then we cannot do copy-out here. */
+
+ if (jh->b_jlist == BJ_Shadow) {
+ JBUFFER_TRACE(jh, "on shadow: sleep");
+ spin_unlock(&journal_datalist_lock);
+ unlock_journal(journal);
+ /* commit wakes up all shadow buffers after IO */
+ wait_event(jh2bh(jh)->b_wait,
+ jh->b_jlist != BJ_Shadow);
+ lock_journal(journal);
+ goto repeat;
+ }
+
+ /* Only do the copy if the currently-owning transaction
+ * still needs it. If it is on the Forget list, the
+ * committing transaction is past that stage. The
+ * buffer had better remain locked during the kmalloc,
+ * but that should be true --- we hold the journal lock
+ * still and the buffer is already on the BUF_JOURNAL
+ * list so won't be flushed.
+ *
+ * Subtle point, though: if this is a get_undo_access,
+ * then we will be relying on the frozen_data to contain
+ * the new value of the committed_data record after the
+ * transaction, so we HAVE to force the frozen_data copy
+ * in that case. */
+
+ if (jh->b_jlist != BJ_Forget || force_copy) {
+ JBUFFER_TRACE(jh, "generate frozen data");
+ if (!frozen_buffer) {
+ JBUFFER_TRACE(jh, "allocate memory for buffer");
+ spin_unlock(&journal_datalist_lock);
+ unlock_journal(journal);
+ frozen_buffer = jbd_kmalloc(jh2bh(jh)->b_size,
+ GFP_NOFS);
+ lock_journal(journal);
+ if (!frozen_buffer) {
+ printk(KERN_EMERG
+ "%s: OOM for frozen_buffer\n",
+ __FUNCTION__);
+ JBUFFER_TRACE(jh, "oom!");
+ error = -ENOMEM;
+ spin_lock(&journal_datalist_lock);
+ goto done_locked;
+ }
+ goto repeat;
+ }
+
+ jh->b_frozen_data = frozen_buffer;
+ frozen_buffer = NULL;
+ need_copy = 1;
+ }
+ jh->b_next_transaction = transaction;
+ }
+
+ J_ASSERT(handle->h_buffer_credits > 0);
+ handle->h_buffer_credits--;
+
+ /* Finally, if the buffer is not journaled right now, we need to
+ * make sure it doesn't get written to disk before the caller
+ * actually commits the new data. */
+
+ if (!jh->b_transaction) {
+ JBUFFER_TRACE(jh, "no transaction");
+ J_ASSERT_JH(jh, !jh->b_next_transaction);
+ jh->b_transaction = transaction;
+ JBUFFER_TRACE(jh, "file as BJ_Reserved");
+ __journal_file_buffer(jh, transaction, BJ_Reserved);
+ }
+
+done_locked:
+ spin_unlock(&journal_datalist_lock);
+ if (need_copy) {
+ struct page *page;
+ int offset;
+ char *source;
+
+ J_EXPECT_JH(jh, buffer_uptodate(jh2bh(jh)),
+ "Possible IO failure.\n");
+ page = jh2bh(jh)->b_page;
+ offset = ((unsigned long) jh2bh(jh)->b_data) & ~PAGE_MASK;
+ source = kmap(page);
+ memcpy(jh->b_frozen_data, source+offset, jh2bh(jh)->b_size);
+ kunmap(page);
+ }
+
+
+ /* If we are about to journal a buffer, then any revoke pending
+ on it is no longer valid. */
+ journal_cancel_revoke(handle, jh);
+
+out_unlocked:
+ if (frozen_buffer)
+ kfree(frozen_buffer);
+
+ JBUFFER_TRACE(jh, "exit");
+ return error;
+}
+
+/**
+ * int journal_get_write_access() - notify intent to modify a buffer for metadata (not data) update.
+ * @handle: transaction to add buffer modifications to
+ * @bh: bh to be used for metadata writes
+ *
+ * Returns an error code or 0 on success.
+ *
+ * In full data journalling mode the buffer may be of type BJ_AsyncData,
+ * because we're write()ing a buffer which is also part of a shared mapping.
+ */
+
+int journal_get_write_access (handle_t *handle, struct buffer_head *bh)
+{
+ transaction_t *transaction = handle->h_transaction;
+ journal_t *journal = transaction->t_journal;
+ struct journal_head *jh = journal_add_journal_head(bh);
+ int rc;
+
+ /* We do not want to get caught playing with fields which the
+ * log thread also manipulates. Make sure that the buffer
+ * completes any outstanding IO before proceeding. */
+ lock_journal(journal);
+ rc = do_get_write_access(handle, jh, 0);
+ journal_unlock_journal_head(jh);
+ unlock_journal(journal);
+ return rc;
+}
+
+
+/*
+ * When the user wants to journal a newly created buffer_head
+ * (ie. getblk() returned a new buffer and we are going to populate it
+ * manually rather than reading off disk), then we need to keep the
+ * buffer_head locked until it has been completely filled with new
+ * data. In this case, we should be able to make the assertion that
+ * the bh is not already part of an existing transaction.
+ *
+ * The buffer should already be locked by the caller by this point.
+ * There is no lock ranking violation: it was a newly created,
+ * unlocked buffer beforehand. */
+
+/**
+ * int journal_get_create_access () - notify intent to use newly created bh
+ * @handle: ransaction to new buffer to
+ * @bh: new buffer.
+ *
+ * Call this if you create a new bh.
+ */
+int journal_get_create_access (handle_t *handle, struct buffer_head *bh)
+{
+ transaction_t *transaction = handle->h_transaction;
+ journal_t *journal = transaction->t_journal;
+ struct journal_head *jh = journal_add_journal_head(bh);
+ int err;
+
+ jbd_debug(5, "journal_head %p\n", jh);
+ lock_journal(journal);
+ err = -EROFS;
+ if (is_handle_aborted(handle))
+ goto out;
+ err = 0;
+
+ JBUFFER_TRACE(jh, "entry");
+ /* The buffer may already belong to this transaction due to
+ * pre-zeroing in the filesystem's new_block code. It may also
+ * be on the previous, committing transaction's lists, but it
+ * HAS to be in Forget state in that case: the transaction must
+ * have deleted the buffer for it to be reused here. */
+ J_ASSERT_JH(jh, (jh->b_transaction == transaction ||
+ jh->b_transaction == NULL ||
+ (jh->b_transaction == journal->j_committing_transaction &&
+ jh->b_jlist == BJ_Forget)));
+
+ J_ASSERT_JH(jh, jh->b_next_transaction == NULL);
+ J_ASSERT_JH(jh, buffer_locked(jh2bh(jh)));
+
+ J_ASSERT_JH(jh, handle->h_buffer_credits > 0);
+ handle->h_buffer_credits--;
+
+ spin_lock(&journal_datalist_lock);
+ if (jh->b_transaction == NULL) {
+ jh->b_transaction = transaction;
+ JBUFFER_TRACE(jh, "file as BJ_Reserved");
+ __journal_file_buffer(jh, transaction, BJ_Reserved);
+ JBUFFER_TRACE(jh, "refile");
+ refile_buffer(jh2bh(jh));
+ } else if (jh->b_transaction == journal->j_committing_transaction) {
+ JBUFFER_TRACE(jh, "set next transaction");
+ jh->b_next_transaction = transaction;
+ }
+ spin_unlock(&journal_datalist_lock);
+
+ /*
+ * akpm: I added this. ext3_alloc_branch can pick up new indirect
+ * blocks which contain freed but then revoked metadata. We need
+ * to cancel the revoke in case we end up freeing it yet again
+ * and the reallocating as data - this would cause a second revoke,
+ * which hits an assertion error.
+ */
+ JBUFFER_TRACE(jh, "cancelling revoke");
+ journal_cancel_revoke(handle, jh);
+ journal_unlock_journal_head(jh);
+out:
+ unlock_journal(journal);
+ return err;
+}
+
+
+
+/**
+ * int journal_get_undo_access() - Notify intent to modify metadata with non-rewindable consequences
+ * @handle: transaction
+ * @bh: buffer to undo
+ *
+ * Sometimes there is a need to distinguish between metadata which has
+ * been committed to disk and that which has not. The ext3fs code uses
+ * this for freeing and allocating space, we have to make sure that we
+ * do not reuse freed space until the deallocation has been committed,
+ * since if we overwrote that space we would make the delete
+ * un-rewindable in case of a crash.
+ *
+ * To deal with that, journal_get_undo_access requests write access to a
+ * buffer for parts of non-rewindable operations such as delete
+ * operations on the bitmaps. The journaling code must keep a copy of
+ * the buffer's contents prior to the undo_access call until such time
+ * as we know that the buffer has definitely been committed to disk.
+ *
+ * We never need to know which transaction the committed data is part
+ * of, buffers touched here are guaranteed to be dirtied later and so
+ * will be committed to a new transaction in due course, at which point
+ * we can discard the old committed data pointer.
+ *
+ * Returns error number or 0 on success.
+ */
+int journal_get_undo_access (handle_t *handle, struct buffer_head *bh)
+{
+ journal_t *journal = handle->h_transaction->t_journal;
+ int err;
+ struct journal_head *jh = journal_add_journal_head(bh);
+
+ JBUFFER_TRACE(jh, "entry");
+ lock_journal(journal);
+
+ /* Do this first --- it can drop the journal lock, so we want to
+ * make sure that obtaining the committed_data is done
+ * atomically wrt. completion of any outstanding commits. */
+ err = do_get_write_access (handle, jh, 1);
+ if (err)
+ goto out;
+
+ if (!jh->b_committed_data) {
+ /* Copy out the current buffer contents into the
+ * preserved, committed copy. */
+ JBUFFER_TRACE(jh, "generate b_committed data");
+ jh->b_committed_data = jbd_kmalloc(jh2bh(jh)->b_size,
+ GFP_NOFS);
+ if (!jh->b_committed_data) {
+ printk(KERN_EMERG "%s: No memory for committed data!\n",
+ __FUNCTION__);
+ err = -ENOMEM;
+ goto out;
+ }
+
+ memcpy (jh->b_committed_data, jh2bh(jh)->b_data,
+ jh2bh(jh)->b_size);
+ }
+
+out:
+ if (!err)
+ J_ASSERT_JH(jh, jh->b_committed_data);
+ journal_unlock_journal_head(jh);
+ unlock_journal(journal);
+ return err;
+}
+
+/**
+ * int journal_dirty_data() - mark a buffer as containing dirty data which needs to be flushed before we can commit the current transaction.
+ * @handle: transaction
+ * @bh: bufferhead to mark
+ * @async: flag
+ *
+ * The buffer is placed on the transaction's data list and is marked as
+ * belonging to the transaction.
+ *
+ * If `async' is set then the writebask will be initiated by the caller
+ * using submit_bh -> end_buffer_io_async. We put the buffer onto
+ * t_async_datalist.
+ *
+ * Returns error number or 0 on success.
+ */
+int journal_dirty_data (handle_t *handle, struct buffer_head *bh, int async)
+{
+/*
+ * journal_dirty_data() can be called via page_launder->ext3_writepage
+ * by kswapd. So it cannot block. Happily, there's nothing here
+ * which needs lock_journal if `async' is set.
+ *
+ * When the buffer is on the current transaction we freely move it
+ * between BJ_AsyncData and BJ_SyncData according to who tried to
+ * change its state last.
+ */
+ journal_t *journal = handle->h_transaction->t_journal;
+ int need_brelse = 0;
+ int wanted_jlist = async ? BJ_AsyncData : BJ_SyncData;
+ struct journal_head *jh;
+
+ if (is_handle_aborted(handle))
+ return 0;
+
+ jh = journal_add_journal_head(bh);
+ JBUFFER_TRACE(jh, "entry");
+
+ /*
+ * The buffer could *already* be dirty. Writeout can start
+ * at any time.
+ */
+ jbd_debug(4, "jh: %p, tid:%d\n", jh, handle->h_transaction->t_tid);
+
+ /*
+ * What if the buffer is already part of a running transaction?
+ *
+ * There are two cases:
+ * 1) It is part of the current running transaction. Refile it,
+ * just in case we have allocated it as metadata, deallocated
+ * it, then reallocated it as data.
+ * 2) It is part of the previous, still-committing transaction.
+ * If all we want to do is to guarantee that the buffer will be
+ * written to disk before this new transaction commits, then
+ * being sure that the *previous* transaction has this same
+ * property is sufficient for us! Just leave it on its old
+ * transaction.
+ *
+ * In case (2), the buffer must not already exist as metadata
+ * --- that would violate write ordering (a transaction is free
+ * to write its data at any point, even before the previous
+ * committing transaction has committed). The caller must
+ * never, ever allow this to happen: there's nothing we can do
+ * about it in this layer.
+ */
+ spin_lock(&journal_datalist_lock);
+ if (jh->b_transaction) {
+ JBUFFER_TRACE(jh, "has transaction");
+ if (jh->b_transaction != handle->h_transaction) {
+ JBUFFER_TRACE(jh, "belongs to older transaction");
+ J_ASSERT_JH(jh, jh->b_transaction ==
+ journal->j_committing_transaction);
+
+ /* @@@ IS THIS TRUE ? */
+ /*
+ * Not any more. Scenario: someone does a write()
+ * in data=journal mode. The buffer's transaction has
+ * moved into commit. Then someone does another
+ * write() to the file. We do the frozen data copyout
+ * and set b_next_transaction to point to j_running_t.
+ * And while we're in that state, someone does a
+ * writepage() in an attempt to pageout the same area
+ * of the file via a shared mapping. At present that
+ * calls journal_dirty_data(), and we get right here.
+ * It may be too late to journal the data. Simply
+ * falling through to the next test will suffice: the
+ * data will be dirty and wil be checkpointed. The
+ * ordering comments in the next comment block still
+ * apply.
+ */
+ //J_ASSERT_JH(jh, jh->b_next_transaction == NULL);
+
+ /*
+ * If we're journalling data, and this buffer was
+ * subject to a write(), it could be metadata, forget
+ * or shadow against the committing transaction. Now,
+ * someone has dirtied the same darn page via a mapping
+ * and it is being writepage()'d.
+ * We *could* just steal the page from commit, with some
+ * fancy locking there. Instead, we just skip it -
+ * don't tie the page's buffers to the new transaction
+ * at all.
+ * Implication: if we crash before the writepage() data
+ * is written into the filesystem, recovery will replay
+ * the write() data.
+ */
+ if (jh->b_jlist != BJ_None &&
+ jh->b_jlist != BJ_SyncData &&
+ jh->b_jlist != BJ_AsyncData) {
+ JBUFFER_TRACE(jh, "Not stealing");
+ goto no_journal;
+ }
+
+ /*
+ * This buffer may be undergoing writeout in commit. We
+ * can't return from here and let the caller dirty it
+ * again because that can cause the write-out loop in
+ * commit to never terminate.
+ */
+ if (!async && buffer_dirty(bh)) {
+ atomic_inc(&bh->b_count);
+ spin_unlock(&journal_datalist_lock);
+ need_brelse = 1;
+ ll_rw_block(WRITE, 1, &bh);
+ wait_on_buffer(bh);
+ spin_lock(&journal_datalist_lock);
+ /* The buffer may become locked again at any
+ time if it is redirtied */
+ }
+
+ /* journal_clean_data_list() may have got there first */
+ if (jh->b_transaction != NULL) {
+ JBUFFER_TRACE(jh, "unfile from commit");
+ __journal_unfile_buffer(jh);
+ jh->b_transaction = NULL;
+ }
+ /* The buffer will be refiled below */
+
+ }
+ /*
+ * Special case --- the buffer might actually have been
+ * allocated and then immediately deallocated in the previous,
+ * committing transaction, so might still be left on that
+ * transaction's metadata lists.
+ */
+ if (jh->b_jlist != wanted_jlist) {
+ JBUFFER_TRACE(jh, "not on correct data list: unfile");
+ J_ASSERT_JH(jh, jh->b_jlist != BJ_Shadow);
+ __journal_unfile_buffer(jh);
+ jh->b_transaction = NULL;
+ JBUFFER_TRACE(jh, "file as data");
+ __journal_file_buffer(jh, handle->h_transaction,
+ wanted_jlist);
+ }
+ } else {
+ JBUFFER_TRACE(jh, "not on a transaction");
+ __journal_file_buffer(jh, handle->h_transaction, wanted_jlist);
+ }
+no_journal:
+ spin_unlock(&journal_datalist_lock);
+ if (need_brelse) {
+ BUFFER_TRACE(bh, "brelse");
+ __brelse(bh);
+ }
+ JBUFFER_TRACE(jh, "exit");
+ journal_unlock_journal_head(jh);
+ return 0;
+}
+
+/**
+ * int journal_dirty_metadata() - mark a buffer as containing dirty metadata
+ * @handle: transaction to add buffer to.
+ * @bh: buffer to mark
+ *
+ * mark dirty metadata which needs to be journaled as part of the current transaction.
+ *
+ * The buffer is placed on the transaction's metadata list and is marked
+ * as belonging to the transaction.
+ *
+ * Returns error number or 0 on success.
+ */
+int journal_dirty_metadata (handle_t *handle, struct buffer_head *bh)
+{
+/*
+ * Special care needs to be taken if the buffer already belongs to the
+ * current committing transaction (in which case we should have frozen
+ * data present for that commit). In that case, we don't relink the
+ * buffer: that only gets done when the old transaction finally
+ * completes its commit.
+ *
+ */
+ transaction_t *transaction = handle->h_transaction;
+ journal_t *journal = transaction->t_journal;
+ struct journal_head *jh = bh2jh(bh);
+
+ jbd_debug(5, "journal_head %p\n", jh);
+ JBUFFER_TRACE(jh, "entry");
+ lock_journal(journal);
+ if (is_handle_aborted(handle))
+ goto out_unlock;
+
+ spin_lock(&journal_datalist_lock);
+ set_bit(BH_JBDDirty, &bh->b_state);
+
+ J_ASSERT_JH(jh, jh->b_transaction != NULL);
+
+ /*
+ * Metadata already on the current transaction list doesn't
+ * need to be filed. Metadata on another transaction's list must
+ * be committing, and will be refiled once the commit completes:
+ * leave it alone for now.
+ */
+
+ if (jh->b_transaction != transaction) {
+ JBUFFER_TRACE(jh, "already on other transaction");
+ J_ASSERT_JH(jh, jh->b_transaction ==
+ journal->j_committing_transaction);
+ J_ASSERT_JH(jh, jh->b_next_transaction == transaction);
+ /* And this case is illegal: we can't reuse another
+ * transaction's data buffer, ever. */
+ /* FIXME: writepage() should be journalled */
+ J_ASSERT_JH(jh, jh->b_jlist != BJ_SyncData);
+ goto done_locked;
+ }
+
+ /* That test should have eliminated the following case: */
+ J_ASSERT_JH(jh, jh->b_frozen_data == 0);
+
+ JBUFFER_TRACE(jh, "file as BJ_Metadata");
+ __journal_file_buffer(jh, handle->h_transaction, BJ_Metadata);
+
+done_locked:
+ spin_unlock(&journal_datalist_lock);
+ JBUFFER_TRACE(jh, "exit");
+out_unlock:
+ unlock_journal(journal);
+ return 0;
+}
+
+#if 0
+/*
+ * journal_release_buffer: undo a get_write_access without any buffer
+ * updates, if the update decided in the end that it didn't need access.
+ *
+ * journal_get_write_access() can block, so it is quite possible for a
+ * journaling component to decide after the write access is returned
+ * that global state has changed and the update is no longer required. */
+
+void journal_release_buffer (handle_t *handle, struct buffer_head *bh)
+{
+ transaction_t *transaction = handle->h_transaction;
+ journal_t *journal = transaction->t_journal;
+ struct journal_head *jh = bh2jh(bh);
+
+ lock_journal(journal);
+ JBUFFER_TRACE(jh, "entry");
+
+ /* If the buffer is reserved but not modified by this
+ * transaction, then it is safe to release it. In all other
+ * cases, just leave the buffer as it is. */
+
+ spin_lock(&journal_datalist_lock);
+ if (jh->b_jlist == BJ_Reserved && jh->b_transaction == transaction &&
+ !buffer_jdirty(jh2bh(jh))) {
+ JBUFFER_TRACE(jh, "unused: refiling it");
+ handle->h_buffer_credits++;
+ __journal_refile_buffer(jh);
+ }
+ spin_unlock(&journal_datalist_lock);
+
+ JBUFFER_TRACE(jh, "exit");
+ unlock_journal(journal);
+}
+#endif
+
+/**
+ * void journal_forget() - bforget() for potentially-journaled buffers.
+ * @handle: transaction handle
+ * @bh: bh to 'forget'
+ *
+ * We can only do the bforget if there are no commits pending against the
+ * buffer. If the buffer is dirty in the current running transaction we
+ * can safely unlink it.
+ *
+ * bh may not be a journalled buffer at all - it may be a non-JBD
+ * buffer which came off the hashtable. Check for this.
+ *
+ * Decrements bh->b_count by one.
+ *
+ * Allow this call even if the handle has aborted --- it may be part of
+ * the caller's cleanup after an abort.
+ */
+void journal_forget (handle_t *handle, struct buffer_head *bh)
+{
+ transaction_t *transaction = handle->h_transaction;
+ journal_t *journal = transaction->t_journal;
+ struct journal_head *jh;
+
+ BUFFER_TRACE(bh, "entry");
+
+ lock_journal(journal);
+ spin_lock(&journal_datalist_lock);
+
+ if (!buffer_jbd(bh))
+ goto not_jbd;
+ jh = bh2jh(bh);
+
+ if (jh->b_transaction == handle->h_transaction) {
+ J_ASSERT_JH(jh, !jh->b_frozen_data);
+
+ /* If we are forgetting a buffer which is already part
+ * of this transaction, then we can just drop it from
+ * the transaction immediately. */
+ clear_bit(BH_Dirty, &bh->b_state);
+ clear_bit(BH_JBDDirty, &bh->b_state);
+
+ JBUFFER_TRACE(jh, "belongs to current transaction: unfile");
+ J_ASSERT_JH(jh, !jh->b_committed_data);
+
+ __journal_unfile_buffer(jh);
+ jh->b_transaction = 0;
+
+ /*
+ * We are no longer going to journal this buffer.
+ * However, the commit of this transaction is still
+ * important to the buffer: the delete that we are now
+ * processing might obsolete an old log entry, so by
+ * committing, we can satisfy the buffer's checkpoint.
+ *
+ * So, if we have a checkpoint on the buffer, we should
+ * now refile the buffer on our BJ_Forget list so that
+ * we know to remove the checkpoint after we commit.
+ */
+
+ if (jh->b_cp_transaction) {
+ __journal_file_buffer(jh, transaction, BJ_Forget);
+ } else {
+ __journal_remove_journal_head(bh);
+ __brelse(bh);
+ if (!buffer_jbd(bh)) {
+ spin_unlock(&journal_datalist_lock);
+ unlock_journal(journal);
+ __bforget(bh);
+ return;
+ }
+ }
+
+ } else if (jh->b_transaction) {
+ J_ASSERT_JH(jh, (jh->b_transaction ==
+ journal->j_committing_transaction));
+ /* However, if the buffer is still owned by a prior
+ * (committing) transaction, we can't drop it yet... */
+ JBUFFER_TRACE(jh, "belongs to older transaction");
+ /* ... but we CAN drop it from the new transaction if we
+ * have also modified it since the original commit. */
+
+ if (jh->b_next_transaction) {
+ J_ASSERT(jh->b_next_transaction == transaction);
+ jh->b_next_transaction = NULL;
+ }
+ }
+
+not_jbd:
+ spin_unlock(&journal_datalist_lock);
+ unlock_journal(journal);
+ __brelse(bh);
+ return;
+}
+
+#if 0 /* Unused */
+/*
+ * journal_sync_buffer: flush a potentially-journaled buffer to disk.
+ *
+ * Used for O_SYNC filesystem operations. If the buffer is journaled,
+ * we need to complete the O_SYNC by waiting for the transaction to
+ * complete. It is an error to call journal_sync_buffer before
+ * journal_stop!
+ */
+
+void journal_sync_buffer(struct buffer_head *bh)
+{
+ transaction_t *transaction;
+ journal_t *journal;
+ long sequence;
+ struct journal_head *jh;
+
+ /* If the buffer isn't journaled, this is easy: just sync it to
+ * disk. */
+ BUFFER_TRACE(bh, "entry");
+
+ spin_lock(&journal_datalist_lock);
+ if (!buffer_jbd(bh)) {
+ spin_unlock(&journal_datalist_lock);
+ return;
+ }
+ jh = bh2jh(bh);
+ if (jh->b_transaction == NULL) {
+ /* If the buffer has already been journaled, then this
+ * is a noop. */
+ if (jh->b_cp_transaction == NULL) {
+ spin_unlock(&journal_datalist_lock);
+ return;
+ }
+ atomic_inc(&bh->b_count);
+ spin_unlock(&journal_datalist_lock);
+ ll_rw_block (WRITE, 1, &bh);
+ wait_on_buffer(bh);
+ __brelse(bh);
+ goto out;
+ }
+
+ /* Otherwise, just wait until the transaction is synced to disk. */
+ transaction = jh->b_transaction;
+ journal = transaction->t_journal;
+ sequence = transaction->t_tid;
+ spin_unlock(&journal_datalist_lock);
+
+ jbd_debug(2, "requesting commit for jh %p\n", jh);
+ log_start_commit (journal, transaction);
+
+ while (tid_gt(sequence, journal->j_commit_sequence)) {
+ wake_up(&journal->j_wait_done_commit);
+ sleep_on(&journal->j_wait_done_commit);
+ }
+ JBUFFER_TRACE(jh, "exit");
+out:
+ return;
+}
+#endif
+
+/*
+ * Register a callback function for this handle. The function will be
+ * called when the transaction that this handle is part of has been
+ * committed to disk with the original callback data struct and the
+ * error status of the journal as parameters. There is no guarantee of
+ * ordering between handles within a single transaction, nor between
+ * callbacks registered on the same handle.
+ *
+ * The caller is responsible for allocating the journal_callback struct.
+ * This is to allow the caller to add as much extra data to the callback
+ * as needed, but reduce the overhead of multiple allocations. The caller
+ * allocated struct must start with a struct journal_callback at offset 0,
+ * and has the caller-specific data afterwards.
+ */
+void journal_callback_set(handle_t *handle,
+ void (*func)(struct journal_callback *jcb, int error),
+ struct journal_callback *jcb)
+{
+ list_add_tail(&jcb->jcb_list, &handle->h_jcb);
+ jcb->jcb_func = func;
+}
+
+/**
+ * int journal_stop() - complete a transaction
+ * @handle: tranaction to complete.
+ *
+ * All done for a particular handle.
+ *
+ * There is not much action needed here. We just return any remaining
+ * buffer credits to the transaction and remove the handle. The only
+ * complication is that we need to start a commit operation if the
+ * filesystem is marked for synchronous update.
+ *
+ * journal_stop itself will not usually return an error, but it may
+ * do so in unusual circumstances. In particular, expect it to
+ * return -EIO if a journal_abort has been executed since the
+ * transaction began.
+ */
+int journal_stop(handle_t *handle)
+{
+ transaction_t *transaction = handle->h_transaction;
+ journal_t *journal = transaction->t_journal;
+ int old_handle_count, err;
+
+ if (!handle)
+ return 0;
+
+ J_ASSERT (transaction->t_updates > 0);
+ J_ASSERT (journal_current_handle() == handle);
+
+ if (is_handle_aborted(handle))
+ err = -EIO;
+ else
+ err = 0;
+
+ if (--handle->h_ref > 0) {
+ jbd_debug(4, "h_ref %d -> %d\n", handle->h_ref + 1,
+ handle->h_ref);
+ return err;
+ }
+
+ jbd_debug(4, "Handle %p going down\n", handle);
+
+ /*
+ * Implement synchronous transaction batching. If the handle
+ * was synchronous, don't force a commit immediately. Let's
+ * yield and let another thread piggyback onto this transaction.
+ * Keep doing that while new threads continue to arrive.
+ * It doesn't cost much - we're about to run a commit and sleep
+ * on IO anyway. Speeds up many-threaded, many-dir operations
+ * by 30x or more...
+ */
+ if (handle->h_sync) {
+ do {
+ old_handle_count = transaction->t_handle_count;
+ yield();
+ } while (old_handle_count != transaction->t_handle_count);
+ }
+
+ current->journal_info = NULL;
+ transaction->t_outstanding_credits -= handle->h_buffer_credits;
+ transaction->t_updates--;
+ if (!transaction->t_updates) {
+ wake_up(&journal->j_wait_updates);
+ if (journal->j_barrier_count)
+ wake_up(&journal->j_wait_transaction_locked);
+ }
+
+ /* Move callbacks from the handle to the transaction. */
+ list_splice(&handle->h_jcb, &transaction->t_jcb);
+
+ /*
+ * If the handle is marked SYNC, we need to set another commit
+ * going! We also want to force a commit if the current
+ * transaction is occupying too much of the log, or if the
+ * transaction is too old now.
+ */
+ if (handle->h_sync ||
+ transaction->t_outstanding_credits >
+ journal->j_max_transaction_buffers ||
+ (journal->j_commit_interval &&
+ time_after_eq(jiffies, transaction->t_expires))) {
+ /* Do this even for aborted journals: an abort still
+ * completes the commit thread, it just doesn't write
+ * anything to disk. */
+ tid_t tid = transaction->t_tid;
+
+ jbd_debug(2, "transaction too old, requesting commit for "
+ "handle %p\n", handle);
+ /* This is non-blocking */
+ log_start_commit(journal, transaction);
+
+ /*
+ * Special case: JFS_SYNC synchronous updates require us
+ * to wait for the commit to complete.
+ */
+ if (handle->h_sync && !(current->flags & PF_MEMALLOC))
+ log_wait_commit(journal, tid);
+ }
+ kfree(handle);
+ return err;
+}
+
+/**int journal_force_commit() - force any uncommitted transactions
+ * @journal: journal to force
+ *
+ * For synchronous operations: force any uncommitted transactions
+ * to disk. May seem kludgy, but it reuses all the handle batching
+ * code in a very simple manner.
+ */
+int journal_force_commit(journal_t *journal)
+{
+ handle_t *handle;
+ int ret = 0;
+
+ lock_kernel();
+ handle = journal_start(journal, 1);
+ if (IS_ERR(handle)) {
+ ret = PTR_ERR(handle);
+ goto out;
+ }
+ handle->h_sync = 1;
+ journal_stop(handle);
+out:
+ unlock_kernel();
+ return ret;
+}
+
+/*
+ *
+ * List management code snippets: various functions for manipulating the
+ * transaction buffer lists.
+ *
+ */
+
+/*
+ * Append a buffer to a transaction list, given the transaction's list head
+ * pointer.
+ * journal_datalist_lock is held.
+ */
+
+static inline void
+__blist_add_buffer(struct journal_head **list, struct journal_head *jh)
+{
+ if (!*list) {
+ jh->b_tnext = jh->b_tprev = jh;
+ *list = jh;
+ } else {
+ /* Insert at the tail of the list to preserve order */
+ struct journal_head *first = *list, *last = first->b_tprev;
+ jh->b_tprev = last;
+ jh->b_tnext = first;
+ last->b_tnext = first->b_tprev = jh;
+ }
+}
+
+/*
+ * Remove a buffer from a transaction list, given the transaction's list
+ * head pointer.
+ *
+ * Called with journal_datalist_lock held, and the journal may not
+ * be locked.
+ */
+
+static inline void
+__blist_del_buffer(struct journal_head **list, struct journal_head *jh)
+{
+ if (*list == jh) {
+ *list = jh->b_tnext;
+ if (*list == jh)
+ *list = 0;
+ }
+ jh->b_tprev->b_tnext = jh->b_tnext;
+ jh->b_tnext->b_tprev = jh->b_tprev;
+}
+
+/*
+ * Remove a buffer from the appropriate transaction list.
+ *
+ * Note that this function can *change* the value of
+ * bh->b_transaction->t_sync_datalist, t_async_datalist, t_buffers, t_forget,
+ * t_iobuf_list, t_shadow_list, t_log_list or t_reserved_list. If the caller
+ * is holding onto a copy of one of thee pointers, it could go bad.
+ * Generally the caller needs to re-read the pointer from the transaction_t.
+ *
+ * If bh->b_jlist is BJ_SyncData or BJ_AsyncData then we may have been called
+ * via journal_try_to_free_buffer() or journal_clean_data_list(). In that
+ * case, journal_datalist_lock will be held, and the journal may not be locked.
+ */
+void __journal_unfile_buffer(struct journal_head *jh)
+{
+ struct journal_head **list = 0;
+ transaction_t * transaction;
+
+ assert_spin_locked(&journal_datalist_lock);
+ transaction = jh->b_transaction;
+
+ J_ASSERT_JH(jh, jh->b_jlist < BJ_Types);
+
+ if (jh->b_jlist != BJ_None)
+ J_ASSERT_JH(jh, transaction != 0);
+
+ switch (jh->b_jlist) {
+ case BJ_None:
+ return;
+ case BJ_SyncData:
+ list = &transaction->t_sync_datalist;
+ break;
+ case BJ_AsyncData:
+ list = &transaction->t_async_datalist;
+ break;
+ case BJ_Metadata:
+ transaction->t_nr_buffers--;
+ J_ASSERT_JH(jh, transaction->t_nr_buffers >= 0);
+ list = &transaction->t_buffers;
+ break;
+ case BJ_Forget:
+ list = &transaction->t_forget;
+ break;
+ case BJ_IO:
+ list = &transaction->t_iobuf_list;
+ break;
+ case BJ_Shadow:
+ list = &transaction->t_shadow_list;
+ break;
+ case BJ_LogCtl:
+ list = &transaction->t_log_list;
+ break;
+ case BJ_Reserved:
+ list = &transaction->t_reserved_list;
+ break;
+ }
+
+ __blist_del_buffer(list, jh);
+ jh->b_jlist = BJ_None;
+ if (test_and_clear_bit(BH_JBDDirty, &jh2bh(jh)->b_state)) {
+ set_bit(BH_Dirty, &jh2bh(jh)->b_state);
+ }
+}
+
+void journal_unfile_buffer(struct journal_head *jh)
+{
+ spin_lock(&journal_datalist_lock);
+ __journal_unfile_buffer(jh);
+ spin_unlock(&journal_datalist_lock);
+}
+
+/*
+ * Called from journal_try_to_free_buffers(). The journal is not
+ * locked. lru_list_lock is not held.
+ *
+ * Here we see why journal_datalist_lock is global and not per-journal.
+ * We cannot get back to this buffer's journal pointer without locking
+ * out journal_clean_data_list() in some manner.
+ *
+ * One could use journal_datalist_lock to get unracy access to a
+ * per-journal lock.
+ *
+ * Called with journal_datalist_lock held.
+ *
+ * Returns non-zero iff we were able to free the journal_head.
+ */
+static int __journal_try_to_free_buffer(struct buffer_head *bh,
+ int *locked_or_dirty)
+{
+ struct journal_head *jh;
+
+ assert_spin_locked(&journal_datalist_lock);
+
+ jh = bh2jh(bh);
+
+ if (buffer_locked(bh) || buffer_dirty(bh)) {
+ *locked_or_dirty = 1;
+ goto out;
+ }
+
+ if (!buffer_uptodate(bh))
+ goto out;
+
+ if (jh->b_next_transaction != 0)
+ goto out;
+
+ if (jh->b_transaction != 0 && jh->b_cp_transaction == 0) {
+ if (jh->b_jlist == BJ_SyncData || jh->b_jlist==BJ_AsyncData) {
+ /* A written-back ordered data buffer */
+ JBUFFER_TRACE(jh, "release data");
+ __journal_unfile_buffer(jh);
+ jh->b_transaction = 0;
+ __journal_remove_journal_head(bh);
+ __brelse(bh);
+ }
+ }
+ else if (jh->b_cp_transaction != 0 && jh->b_transaction == 0) {
+ /* written-back checkpointed metadata buffer */
+ if (jh->b_jlist == BJ_None) {
+ JBUFFER_TRACE(jh, "remove from checkpoint list");
+ __journal_remove_checkpoint(jh);
+ __journal_remove_journal_head(bh);
+ __brelse(bh);
+ }
+ }
+ return !buffer_jbd(bh);
+
+out:
+ return 0;
+}
+
+void debug_page(struct page *p)
+{
+ struct buffer_head *bh;
+
+ bh = p->buffers;
+
+ printk(KERN_ERR "%s: page index:%lu count:%d flags:%lx\n", __FUNCTION__,
+ p->index, atomic_read(&p->count), p->flags);
+
+ while (bh) {
+ printk(KERN_ERR "%s: bh b_next:%p blocknr:%lu b_list:%u state:%lx\n",
+ __FUNCTION__, bh->b_next, bh->b_blocknr, bh->b_list,
+ bh->b_state);
+ bh = bh->b_this_page;
+ }
+}
+
+
+/**
+ * int journal_try_to_free_buffers() - try to free page buffers.
+ * @journal: journal for operation
+ * @page: to try and free
+ * @gfp_mask: 'IO' mode for try_to_free_buffers()
+ *
+ *
+ * For all the buffers on this page,
+ * if they are fully written out ordered data, move them onto BUF_CLEAN
+ * so try_to_free_buffers() can reap them.
+ *
+ * This function returns non-zero if we wish try_to_free_buffers()
+ * to be called. We do this if the page is releasable by try_to_free_buffers().
+ * We also do it if the page has locked or dirty buffers and the caller wants
+ * us to perform sync or async writeout.
+ */
+int journal_try_to_free_buffers(journal_t *journal,
+ struct page *page, int gfp_mask)
+{
+/*
+ * journal_try_to_free_buffers(). For all the buffers on this page,
+ * if they are fully written out ordered data, move them onto BUF_CLEAN
+ * so try_to_free_buffers() can reap them. Called with lru_list_lock
+ * not held. Does its own locking.
+ *
+ * This complicates JBD locking somewhat. We aren't protected by the
+ * BKL here. We wish to remove the buffer from its committing or
+ * running transaction's ->t_datalist via __journal_unfile_buffer.
+ *
+ * This may *change* the value of transaction_t->t_datalist, so anyone
+ * who looks at t_datalist needs to lock against this function.
+ *
+ * Even worse, someone may be doing a journal_dirty_data on this
+ * buffer. So we need to lock against that. journal_dirty_data()
+ * will come out of the lock with the buffer dirty, which makes it
+ * ineligible for release here.
+ *
+ * Who else is affected by this? hmm... Really the only contender
+ * is do_get_write_access() - it could be looking at the buffer while
+ * journal_try_to_free_buffer() is changing its state. But that
+ * cannot happen because we never reallocate freed data as metadata
+ * while the data is part of a transaction. Yes?
+ *
+ */
+ struct buffer_head *bh;
+ struct buffer_head *tmp;
+ int locked_or_dirty = 0;
+ int call_ttfb = 1;
+
+ J_ASSERT(PageLocked(page));
+
+ bh = page->buffers;
+ tmp = bh;
+ spin_lock(&journal_datalist_lock);
+ do {
+ struct buffer_head *p = tmp;
+
+ if (unlikely(!tmp)) {
+ debug_page(page);
+ BUG();
+ }
+
+ tmp = tmp->b_this_page;
+ if (buffer_jbd(p))
+ if (!__journal_try_to_free_buffer(p, &locked_or_dirty))
+ call_ttfb = 0;
+ } while (tmp != bh);
+ spin_unlock(&journal_datalist_lock);
+
+ if (!(gfp_mask & (__GFP_IO|__GFP_WAIT)))
+ goto out;
+ if (!locked_or_dirty)
+ goto out;
+ /*
+ * The VM wants us to do writeout, or to block on IO, or both.
+ * So we allow try_to_free_buffers to be called even if the page
+ * still has journalled buffers.
+ */
+ call_ttfb = 1;
+out:
+ return call_ttfb;
+}
+
+/*
+ * This buffer is no longer needed. If it is on an older transaction's
+ * checkpoint list we need to record it on this transaction's forget list
+ * to pin this buffer (and hence its checkpointing transaction) down until
+ * this transaction commits. If the buffer isn't on a checkpoint list, we
+ * release it.
+ * Returns non-zero if JBD no longer has an interest in the buffer.
+ */
+static int dispose_buffer(struct journal_head *jh,
+ transaction_t *transaction)
+{
+ int may_free = 1;
+ struct buffer_head *bh = jh2bh(jh);
+
+ spin_lock(&journal_datalist_lock);
+ __journal_unfile_buffer(jh);
+ jh->b_transaction = 0;
+
+ if (jh->b_cp_transaction) {
+ JBUFFER_TRACE(jh, "on running+cp transaction");
+ __journal_file_buffer(jh, transaction, BJ_Forget);
+ clear_bit(BH_JBDDirty, &bh->b_state);
+ may_free = 0;
+ } else {
+ JBUFFER_TRACE(jh, "on running transaction");
+ __journal_remove_journal_head(bh);
+ __brelse(bh);
+ }
+ spin_unlock(&journal_datalist_lock);
+ return may_free;
+}
+
+/*
+ * journal_flushpage
+ *
+ * This code is tricky. It has a number of cases to deal with.
+ *
+ * There are two invariants which this code relies on:
+ *
+ * i_size must be updated on disk before we start calling flushpage on the
+ * data.
+ *
+ * This is done in ext3 by defining an ext3_setattr method which
+ * updates i_size before truncate gets going. By maintaining this
+ * invariant, we can be sure that it is safe to throw away any buffers
+ * attached to the current transaction: once the transaction commits,
+ * we know that the data will not be needed.
+ *
+ * Note however that we can *not* throw away data belonging to the
+ * previous, committing transaction!
+ *
+ * Any disk blocks which *are* part of the previous, committing
+ * transaction (and which therefore cannot be discarded immediately) are
+ * not going to be reused in the new running transaction
+ *
+ * The bitmap committed_data images guarantee this: any block which is
+ * allocated in one transaction and removed in the next will be marked
+ * as in-use in the committed_data bitmap, so cannot be reused until
+ * the next transaction to delete the block commits. This means that
+ * leaving committing buffers dirty is quite safe: the disk blocks
+ * cannot be reallocated to a different file and so buffer aliasing is
+ * not possible.
+ *
+ *
+ * The above applies mainly to ordered data mode. In writeback mode we
+ * don't make guarantees about the order in which data hits disk --- in
+ * particular we don't guarantee that new dirty data is flushed before
+ * transaction commit --- so it is always safe just to discard data
+ * immediately in that mode. --sct
+ */
+
+/*
+ * The journal_unmap_buffer helper function returns zero if the buffer
+ * concerned remains pinned as an anonymous buffer belonging to an older
+ * transaction.
+ *
+ * We're outside-transaction here. Either or both of j_running_transaction
+ * and j_committing_transaction may be NULL.
+ */
+static int journal_unmap_buffer(journal_t *journal, struct buffer_head *bh)
+{
+ transaction_t *transaction;
+ struct journal_head *jh;
+ int may_free = 1;
+
+ BUFFER_TRACE(bh, "entry");
+
+ if (!buffer_mapped(bh))
+ return 1;
+
+ /* It is safe to proceed here without the
+ * journal_datalist_spinlock because the buffers cannot be
+ * stolen by try_to_free_buffers as long as we are holding the
+ * page lock. --sct */
+
+ if (!buffer_jbd(bh))
+ goto zap_buffer;
+
+ jh = bh2jh(bh);
+ transaction = jh->b_transaction;
+ if (transaction == NULL) {
+ /* First case: not on any transaction. If it
+ * has no checkpoint link, then we can zap it:
+ * it's a writeback-mode buffer so we don't care
+ * if it hits disk safely. */
+ if (!jh->b_cp_transaction) {
+ JBUFFER_TRACE(jh, "not on any transaction: zap");
+ goto zap_buffer;
+ }
+
+ if (!buffer_dirty(bh)) {
+ /* bdflush has written it. We can drop it now */
+ goto zap_buffer;
+ }
+
+ /* OK, it must be in the journal but still not
+ * written fully to disk: it's metadata or
+ * journaled data... */
+
+ if (journal->j_running_transaction) {
+ /* ... and once the current transaction has
+ * committed, the buffer won't be needed any
+ * longer. */
+ JBUFFER_TRACE(jh, "checkpointed: add to BJ_Forget");
+ return dispose_buffer(jh,
+ journal->j_running_transaction);
+ } else {
+ /* There is no currently-running transaction. So the
+ * orphan record which we wrote for this file must have
+ * passed into commit. We must attach this buffer to
+ * the committing transaction, if it exists. */
+ if (journal->j_committing_transaction) {
+ JBUFFER_TRACE(jh, "give to committing trans");
+ return dispose_buffer(jh,
+ journal->j_committing_transaction);
+ } else {
+ /* The orphan record's transaction has
+ * committed. We can cleanse this buffer */
+ clear_bit(BH_JBDDirty, &bh->b_state);
+ goto zap_buffer;
+ }
+ }
+ } else if (transaction == journal->j_committing_transaction) {
+ /* If it is committing, we simply cannot touch it. We
+ * can remove it's next_transaction pointer from the
+ * running transaction if that is set, but nothing
+ * else. */
+ JBUFFER_TRACE(jh, "on committing transaction");
+ set_bit(BH_Freed, &bh->b_state);
+ if (jh->b_next_transaction) {
+ J_ASSERT(jh->b_next_transaction ==
+ journal->j_running_transaction);
+ jh->b_next_transaction = NULL;
+ }
+ return 0;
+ } else {
+ /* Good, the buffer belongs to the running transaction.
+ * We are writing our own transaction's data, not any
+ * previous one's, so it is safe to throw it away
+ * (remember that we expect the filesystem to have set
+ * i_size already for this truncate so recovery will not
+ * expose the disk blocks we are discarding here.) */
+ J_ASSERT_JH(jh, transaction == journal->j_running_transaction);
+ may_free = dispose_buffer(jh, transaction);
+ }
+
+zap_buffer:
+ if (buffer_dirty(bh))
+ mark_buffer_clean(bh);
+ J_ASSERT_BH(bh, !buffer_jdirty(bh));
+ clear_bit(BH_Uptodate, &bh->b_state);
+ clear_bit(BH_Mapped, &bh->b_state);
+ clear_bit(BH_Req, &bh->b_state);
+ clear_bit(BH_New, &bh->b_state);
+ return may_free;
+}
+
+/**
+ * int journal_flushpage()
+ * @journal: journal to use for flush...
+ * @page: page to flush
+ * @offset: length of page to flush.
+ *
+ * Reap page buffers containing data after offset in page.
+ *
+ * Return non-zero if the page's buffers were successfully reaped.
+ */
+int journal_flushpage(journal_t *journal,
+ struct page *page,
+ unsigned long offset)
+{
+ struct buffer_head *head, *bh, *next;
+ unsigned int curr_off = 0;
+ int may_free = 1;
+
+ if (!PageLocked(page))
+ BUG();
+ if (!page->buffers)
+ return 1;
+
+ /* We will potentially be playing with lists other than just the
+ * data lists (especially for journaled data mode), so be
+ * cautious in our locking. */
+ lock_journal(journal);
+
+ head = bh = page->buffers;
+ do {
+ unsigned int next_off = curr_off + bh->b_size;
+ next = bh->b_this_page;
+
+ /* AKPM: doing lock_buffer here may be overly paranoid */
+ if (offset <= curr_off) {
+ /* This block is wholly outside the truncation point */
+ lock_buffer(bh);
+ may_free &= journal_unmap_buffer(journal, bh);
+ unlock_buffer(bh);
+ }
+ curr_off = next_off;
+ bh = next;
+
+ } while (bh != head);
+
+ unlock_journal(journal);
+
+ if (!offset) {
+ if (!may_free || !try_to_free_buffers(page, 0))
+ return 0;
+ J_ASSERT(page->buffers == NULL);
+ }
+ return 1;
+}
+
+/*
+ * File a buffer on the given transaction list.
+ */
+void __journal_file_buffer(struct journal_head *jh,
+ transaction_t *transaction, int jlist)
+{
+ struct journal_head **list = 0;
+ int was_dirty = 0;
+
+ assert_spin_locked(&journal_datalist_lock);
+
+ J_ASSERT_JH(jh, jh->b_jlist < BJ_Types);
+ J_ASSERT_JH(jh, jh->b_transaction == transaction ||
+ jh->b_transaction == 0);
+
+ if (jh->b_transaction && jh->b_jlist == jlist)
+ return;
+
+ /* The following list of buffer states needs to be consistent
+ * with __jbd_unexpected_dirty_buffer()'s handling of dirty
+ * state. */
+
+ if (jlist == BJ_Metadata || jlist == BJ_Reserved ||
+ jlist == BJ_Shadow || jlist == BJ_Forget) {
+ if (atomic_set_buffer_clean(jh2bh(jh)) ||
+ test_and_clear_bit(BH_JBDDirty, &jh2bh(jh)->b_state))
+ was_dirty = 1;
+ }
+
+ if (jh->b_transaction)
+ __journal_unfile_buffer(jh);
+ else
+ jh->b_transaction = transaction;
+
+ switch (jlist) {
+ case BJ_None:
+ J_ASSERT_JH(jh, !jh->b_committed_data);
+ J_ASSERT_JH(jh, !jh->b_frozen_data);
+ return;
+ case BJ_SyncData:
+ list = &transaction->t_sync_datalist;
+ break;
+ case BJ_AsyncData:
+ list = &transaction->t_async_datalist;
+ break;
+ case BJ_Metadata:
+ transaction->t_nr_buffers++;
+ list = &transaction->t_buffers;
+ break;
+ case BJ_Forget:
+ list = &transaction->t_forget;
+ break;
+ case BJ_IO:
+ list = &transaction->t_iobuf_list;
+ break;
+ case BJ_Shadow:
+ list = &transaction->t_shadow_list;
+ break;
+ case BJ_LogCtl:
+ list = &transaction->t_log_list;
+ break;
+ case BJ_Reserved:
+ list = &transaction->t_reserved_list;
+ break;
+ }
+
+ __blist_add_buffer(list, jh);
+ jh->b_jlist = jlist;
+
+ if (was_dirty)
+ set_bit(BH_JBDDirty, &jh2bh(jh)->b_state);
+}
+
+void journal_file_buffer(struct journal_head *jh,
+ transaction_t *transaction, int jlist)
+{
+ spin_lock(&journal_datalist_lock);
+ __journal_file_buffer(jh, transaction, jlist);
+ spin_unlock(&journal_datalist_lock);
+}
+
+static void jbd_refile_buffer(struct buffer_head *bh)
+{
+ if (buffer_dirty(bh) && (bh->b_list != BUF_DIRTY))
+ set_buffer_flushtime(bh);
+ refile_buffer(bh);
+}
+
+/*
+ * Remove a buffer from its current buffer list in preparation for
+ * dropping it from its current transaction entirely. If the buffer has
+ * already started to be used by a subsequent transaction, refile the
+ * buffer on that transaction's metadata list.
+ */
+
+void __journal_refile_buffer(struct journal_head *jh)
+{
+ int was_dirty = 0;
+
+ assert_spin_locked(&journal_datalist_lock);
+ /* If the buffer is now unused, just drop it. */
+ if (jh->b_next_transaction == NULL) {
+ __journal_unfile_buffer(jh);
+ jh->b_transaction = NULL;
+ /* Onto BUF_DIRTY for writeback */
+ jbd_refile_buffer(jh2bh(jh));
+ return;
+ }
+
+ /* It has been modified by a later transaction: add it to the
+ * new transaction's metadata list. */
+
+ if (test_and_clear_bit(BH_JBDDirty, &jh2bh(jh)->b_state))
+ was_dirty = 1;
+
+ __journal_unfile_buffer(jh);
+ jh->b_transaction = jh->b_next_transaction;
+ jh->b_next_transaction = NULL;
+ __journal_file_buffer(jh, jh->b_transaction, BJ_Metadata);
+ J_ASSERT_JH(jh, jh->b_transaction->t_state == T_RUNNING);
+
+ if (was_dirty)
+ set_bit(BH_JBDDirty, &jh2bh(jh)->b_state);
+
+}
+
+/*
+ * For the unlocked version of this call, also make sure that any
+ * hanging journal_head is cleaned up if necessary.
+ *
+ * __journal_refile_buffer is usually called as part of a single locked
+ * operation on a buffer_head, in which the caller is probably going to
+ * be hooking the journal_head onto other lists. In that case it is up
+ * to the caller to remove the journal_head if necessary. For the
+ * unlocked journal_refile_buffer call, the caller isn't going to be
+ * doing anything else to the buffer so we need to do the cleanup
+ * ourselves to avoid a jh leak.
+ *
+ * *** The journal_head may be freed by this call! ***
+ */
+void journal_refile_buffer(struct journal_head *jh)
+{
+ struct buffer_head *bh;
+
+ spin_lock(&journal_datalist_lock);
+ bh = jh2bh(jh);
+
+ __journal_refile_buffer(jh);
+ __journal_remove_journal_head(bh);
+
+ spin_unlock(&journal_datalist_lock);
+ __brelse(bh);
+}
diff --git a/fs/jffs/Makefile b/fs/jffs/Makefile
new file mode 100644
index 00000000000000..546b9a739a0158
--- /dev/null
+++ b/fs/jffs/Makefile
@@ -0,0 +1,32 @@
+#
+# Makefile for the linux Journalling Flash FileSystem (JFFS) routines.
+#
+# $Id: Makefile,v 1.11 2001/09/25 20:59:41 dwmw2 Exp $
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+list-multi := jffs.o
+
+jffs-objs := jffs_fm.o intrep.o
+
+ifeq ($(PATCHLEVEL),2)
+ jffs-objs += inode-v22.o
+else
+ jffs-objs += inode-v23.o
+endif
+
+ifeq ($(CONFIG_JFFS_PROC_FS),y)
+ jffs-objs += jffs_proc.o
+endif
+
+O_TARGET := jffs.o
+
+obj-y := $(jffs-objs)
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
+
diff --git a/fs/jffs/inode-v23.c b/fs/jffs/inode-v23.c
new file mode 100644
index 00000000000000..b8e44cfed149af
--- /dev/null
+++ b/fs/jffs/inode-v23.c
@@ -0,0 +1,1770 @@
+/*
+ * JFFS -- Journalling Flash File System, Linux implementation.
+ *
+ * Copyright (C) 1999, 2000 Axis Communications AB.
+ *
+ * Created by Finn Hakansson <finn@axis.com>.
+ *
+ * This 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 of the License, or
+ * (at your option) any later version.
+ *
+ * $Id: inode-v23.c,v 1.70 2001/10/02 09:16:02 dwmw2 Exp $
+ *
+ * Ported to Linux 2.3.x and MTD:
+ * Copyright (C) 2000 Alexander Larsson (alex@cendio.se), Cendio Systems AB
+ *
+ * Copyright 2000, 2001 Red Hat, Inc.
+ */
+
+/* inode.c -- Contains the code that is called from the VFS. */
+
+/* TODO-ALEX:
+ * uid and gid are just 16 bit.
+ * jffs_file_write reads from user-space pointers without xx_from_user
+ * maybe other stuff do to.
+ */
+
+/* Argh. Some architectures have kernel_thread in asm/processor.h
+ Some have it in unistd.h and you need to define __KERNEL_SYSCALLS__
+ Pass me a baseball bat and the person responsible.
+ dwmw2
+*/
+#define __KERNEL_SYSCALLS__
+#include <linux/sched.h>
+#include <linux/unistd.h>
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/jffs.h>
+#include <linux/fs.h>
+#include <linux/locks.h>
+#include <linux/smp_lock.h>
+#include <linux/ioctl.h>
+#include <linux/stat.h>
+#include <linux/blkdev.h>
+#include <linux/quotaops.h>
+#include <asm/semaphore.h>
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+
+#include "jffs_fm.h"
+#include "intrep.h"
+#if CONFIG_JFFS_PROC_FS
+#include "jffs_proc.h"
+#endif
+
+static int jffs_remove(struct inode *dir, struct dentry *dentry, int type);
+
+static struct super_operations jffs_ops;
+static struct file_operations jffs_file_operations;
+static struct inode_operations jffs_file_inode_operations;
+static struct file_operations jffs_dir_operations;
+static struct inode_operations jffs_dir_inode_operations;
+static struct address_space_operations jffs_address_operations;
+
+kmem_cache_t *node_cache = NULL;
+kmem_cache_t *fm_cache = NULL;
+
+/* Called by the VFS at mount time to initialize the whole file system. */
+static struct super_block *
+jffs_read_super(struct super_block *sb, void *data, int silent)
+{
+ kdev_t dev = sb->s_dev;
+ struct inode *root_inode;
+ struct jffs_control *c;
+
+ D1(printk(KERN_NOTICE "JFFS: Trying to mount device %s.\n",
+ kdevname(dev)));
+
+ if (MAJOR(dev) != MTD_BLOCK_MAJOR) {
+ printk(KERN_WARNING "JFFS: Trying to mount a "
+ "non-mtd device.\n");
+ return 0;
+ }
+
+ sb->s_blocksize = PAGE_CACHE_SIZE;
+ sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+ sb->u.generic_sbp = (void *) 0;
+ sb->s_maxbytes = 0xFFFFFFFF;
+
+ /* Build the file system. */
+ if (jffs_build_fs(sb) < 0) {
+ goto jffs_sb_err1;
+ }
+
+ /*
+ * set up enough so that we can read an inode
+ */
+ sb->s_magic = JFFS_MAGIC_SB_BITMASK;
+ sb->s_op = &jffs_ops;
+
+ root_inode = iget(sb, JFFS_MIN_INO);
+ if (!root_inode)
+ goto jffs_sb_err2;
+
+ /* Get the root directory of this file system. */
+ if (!(sb->s_root = d_alloc_root(root_inode))) {
+ goto jffs_sb_err3;
+ }
+
+ c = (struct jffs_control *) sb->u.generic_sbp;
+
+#ifdef CONFIG_JFFS_PROC_FS
+ /* Set up the jffs proc file system. */
+ if (jffs_register_jffs_proc_dir(dev, c) < 0) {
+ printk(KERN_WARNING "JFFS: Failed to initialize the JFFS "
+ "proc file system for device %s.\n",
+ kdevname(dev));
+ }
+#endif
+
+ /* Set the Garbage Collection thresholds */
+
+ /* GC if free space goes below 5% of the total size */
+ c->gc_minfree_threshold = c->fmc->flash_size / 20;
+
+ if (c->gc_minfree_threshold < c->fmc->sector_size)
+ c->gc_minfree_threshold = c->fmc->sector_size;
+
+ /* GC if dirty space exceeds 33% of the total size. */
+ c->gc_maxdirty_threshold = c->fmc->flash_size / 3;
+
+ if (c->gc_maxdirty_threshold < c->fmc->sector_size)
+ c->gc_maxdirty_threshold = c->fmc->sector_size;
+
+
+ c->thread_pid = kernel_thread (jffs_garbage_collect_thread,
+ (void *) c,
+ CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+ D1(printk(KERN_NOTICE "JFFS: GC thread pid=%d.\n", (int) c->thread_pid));
+
+ D1(printk(KERN_NOTICE "JFFS: Successfully mounted device %s.\n",
+ kdevname(dev)));
+ return sb;
+
+jffs_sb_err3:
+ iput(root_inode);
+jffs_sb_err2:
+ jffs_cleanup_control((struct jffs_control *)sb->u.generic_sbp);
+jffs_sb_err1:
+ printk(KERN_WARNING "JFFS: Failed to mount device %s.\n",
+ kdevname(dev));
+ return 0;
+}
+
+
+/* This function is called when the file system is umounted. */
+static void
+jffs_put_super(struct super_block *sb)
+{
+ struct jffs_control *c = (struct jffs_control *) sb->u.generic_sbp;
+ D1(kdev_t dev = sb->s_dev);
+
+ D2(printk("jffs_put_super()\n"));
+
+#ifdef CONFIG_JFFS_PROC_FS
+ jffs_unregister_jffs_proc_dir(c);
+#endif
+
+ if (c->gc_task) {
+ D1(printk (KERN_NOTICE "jffs_put_super(): Telling gc thread to die.\n"));
+ send_sig(SIGKILL, c->gc_task, 1);
+ }
+ wait_for_completion(&c->gc_thread_comp);
+
+ D1(printk (KERN_NOTICE "jffs_put_super(): Successfully waited on thread.\n"));
+
+ jffs_cleanup_control((struct jffs_control *)sb->u.generic_sbp);
+ D1(printk(KERN_NOTICE "JFFS: Successfully unmounted device %s.\n",
+ kdevname(dev)));
+}
+
+
+/* This function is called when user commands like chmod, chgrp and
+ chown are executed. System calls like trunc() results in a call
+ to this function. */
+static int
+jffs_setattr(struct dentry *dentry, struct iattr *iattr)
+{
+ struct inode *inode = dentry->d_inode;
+ struct jffs_raw_inode raw_inode;
+ struct jffs_control *c;
+ struct jffs_fmcontrol *fmc;
+ struct jffs_file *f;
+ struct jffs_node *new_node;
+ int update_all;
+ int res;
+ int recoverable = 0;
+
+ if ((res = inode_change_ok(inode, iattr)))
+ return res;
+
+ c = (struct jffs_control *)inode->i_sb->u.generic_sbp;
+ fmc = c->fmc;
+
+ D3(printk (KERN_NOTICE "notify_change(): down biglock\n"));
+ down(&fmc->biglock);
+
+ f = jffs_find_file(c, inode->i_ino);
+
+ ASSERT(if (!f) {
+ printk("jffs_setattr(): Invalid inode number: %lu\n",
+ inode->i_ino);
+ D3(printk (KERN_NOTICE "notify_change(): up biglock\n"));
+ up(&fmc->biglock);
+ return -EINVAL;
+ });
+
+ D1(printk("***jffs_setattr(): file: \"%s\", ino: %u\n",
+ f->name, f->ino));
+
+ update_all = iattr->ia_valid & ATTR_FORCE;
+
+ if ( (update_all || iattr->ia_valid & ATTR_SIZE)
+ && (iattr->ia_size + 128 < f->size) ) {
+ /* We're shrinking the file by more than 128 bytes.
+ We'll be able to GC and recover this space, so
+ allow it to go into the reserved space. */
+ recoverable = 1;
+ }
+
+ if (!(new_node = jffs_alloc_node())) {
+ D(printk("jffs_setattr(): Allocation failed!\n"));
+ D3(printk (KERN_NOTICE "notify_change(): up biglock\n"));
+ up(&fmc->biglock);
+ return -ENOMEM;
+ }
+
+ new_node->data_offset = 0;
+ new_node->removed_size = 0;
+ raw_inode.magic = JFFS_MAGIC_BITMASK;
+ raw_inode.ino = f->ino;
+ raw_inode.pino = f->pino;
+ raw_inode.mode = f->mode;
+ raw_inode.uid = f->uid;
+ raw_inode.gid = f->gid;
+ raw_inode.atime = f->atime;
+ raw_inode.mtime = f->mtime;
+ raw_inode.ctime = f->ctime;
+ raw_inode.dsize = 0;
+ raw_inode.offset = 0;
+ raw_inode.rsize = 0;
+ raw_inode.dsize = 0;
+ raw_inode.nsize = f->nsize;
+ raw_inode.nlink = f->nlink;
+ raw_inode.spare = 0;
+ raw_inode.rename = 0;
+ raw_inode.deleted = 0;
+
+ if (update_all || iattr->ia_valid & ATTR_MODE) {
+ raw_inode.mode = iattr->ia_mode;
+ inode->i_mode = iattr->ia_mode;
+ }
+ if (update_all || iattr->ia_valid & ATTR_UID) {
+ raw_inode.uid = iattr->ia_uid;
+ inode->i_uid = iattr->ia_uid;
+ }
+ if (update_all || iattr->ia_valid & ATTR_GID) {
+ raw_inode.gid = iattr->ia_gid;
+ inode->i_gid = iattr->ia_gid;
+ }
+ if (update_all || iattr->ia_valid & ATTR_SIZE) {
+ int len;
+ D1(printk("jffs_notify_change(): Changing size "
+ "to %lu bytes!\n", (long)iattr->ia_size));
+ raw_inode.offset = iattr->ia_size;
+
+ /* Calculate how many bytes need to be removed from
+ the end. */
+ if (f->size < iattr->ia_size) {
+ len = 0;
+ }
+ else {
+ len = f->size - iattr->ia_size;
+ }
+
+ raw_inode.rsize = len;
+
+ /* The updated node will be a removal node, with
+ base at the new size and size of the nbr of bytes
+ to be removed. */
+ new_node->data_offset = iattr->ia_size;
+ new_node->removed_size = len;
+ inode->i_size = iattr->ia_size;
+ inode->i_blocks = (inode->i_size + 511) >> 9;
+
+ if (len) {
+ invalidate_inode_pages(inode);
+ }
+ inode->i_ctime = CURRENT_TIME;
+ inode->i_mtime = inode->i_ctime;
+ }
+ if (update_all || iattr->ia_valid & ATTR_ATIME) {
+ raw_inode.atime = iattr->ia_atime;
+ inode->i_atime = iattr->ia_atime;
+ }
+ if (update_all || iattr->ia_valid & ATTR_MTIME) {
+ raw_inode.mtime = iattr->ia_mtime;
+ inode->i_mtime = iattr->ia_mtime;
+ }
+ if (update_all || iattr->ia_valid & ATTR_CTIME) {
+ raw_inode.ctime = iattr->ia_ctime;
+ inode->i_ctime = iattr->ia_ctime;
+ }
+
+ /* Write this node to the flash. */
+ if ((res = jffs_write_node(c, new_node, &raw_inode, f->name, 0, recoverable, f)) < 0) {
+ D(printk("jffs_notify_change(): The write failed!\n"));
+ jffs_free_node(new_node);
+ D3(printk (KERN_NOTICE "n_c(): up biglock\n"));
+ up(&c->fmc->biglock);
+ return res;
+ }
+
+ jffs_insert_node(c, f, &raw_inode, 0, new_node);
+
+ mark_inode_dirty(inode);
+ D3(printk (KERN_NOTICE "n_c(): up biglock\n"));
+ up(&c->fmc->biglock);
+ return 0;
+} /* jffs_notify_change() */
+
+
+struct inode *
+jffs_new_inode(const struct inode * dir, struct jffs_raw_inode *raw_inode,
+ int * err)
+{
+ struct super_block * sb;
+ struct inode * inode;
+ struct jffs_control *c;
+ struct jffs_file *f;
+
+ sb = dir->i_sb;
+ inode = new_inode(sb);
+ if (!inode) {
+ *err = -ENOMEM;
+ return NULL;
+ }
+
+ c = (struct jffs_control *)sb->u.generic_sbp;
+
+ inode->i_ino = raw_inode->ino;
+ inode->i_mode = raw_inode->mode;
+ inode->i_nlink = raw_inode->nlink;
+ inode->i_uid = raw_inode->uid;
+ inode->i_gid = raw_inode->gid;
+ inode->i_rdev = 0;
+ inode->i_size = raw_inode->dsize;
+ inode->i_atime = raw_inode->atime;
+ inode->i_mtime = raw_inode->mtime;
+ inode->i_ctime = raw_inode->ctime;
+ inode->i_blksize = PAGE_SIZE;
+ inode->i_blocks = (inode->i_size + 511) >> 9;
+ inode->i_version = 0;
+
+ f = jffs_find_file(c, raw_inode->ino);
+
+ inode->u.generic_ip = (void *)f;
+ insert_inode_hash(inode);
+
+ return inode;
+}
+
+/* Get statistics of the file system. */
+int
+jffs_statfs(struct super_block *sb, struct statfs *buf)
+{
+ struct jffs_control *c = (struct jffs_control *) sb->u.generic_sbp;
+ struct jffs_fmcontrol *fmc = c->fmc;
+
+ D2(printk("jffs_statfs()\n"));
+
+ buf->f_type = JFFS_MAGIC_SB_BITMASK;
+ buf->f_bsize = PAGE_CACHE_SIZE;
+ buf->f_blocks = (fmc->flash_size / PAGE_CACHE_SIZE)
+ - (fmc->min_free_size / PAGE_CACHE_SIZE);
+ buf->f_bfree = (jffs_free_size1(fmc) + jffs_free_size2(fmc) +
+ fmc->dirty_size - fmc->min_free_size)
+ >> PAGE_CACHE_SHIFT;
+ buf->f_bavail = buf->f_bfree;
+
+ /* Find out how many files there are in the filesystem. */
+ buf->f_files = jffs_foreach_file(c, jffs_file_count);
+ buf->f_ffree = buf->f_bfree;
+ /* buf->f_fsid = 0; */
+ buf->f_namelen = JFFS_MAX_NAME_LEN;
+ return 0;
+}
+
+
+/* Rename a file. */
+int
+jffs_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ struct jffs_raw_inode raw_inode;
+ struct jffs_control *c;
+ struct jffs_file *old_dir_f;
+ struct jffs_file *new_dir_f;
+ struct jffs_file *del_f;
+ struct jffs_file *f;
+ struct jffs_node *node;
+ struct inode *inode;
+ int result = 0;
+ __u32 rename_data = 0;
+
+ D2(printk("***jffs_rename()\n"));
+
+ D(printk("jffs_rename(): old_dir: 0x%p, old name: 0x%p, "
+ "new_dir: 0x%p, new name: 0x%p\n",
+ old_dir, old_dentry->d_name.name,
+ new_dir, new_dentry->d_name.name));
+
+ c = (struct jffs_control *)old_dir->i_sb->u.generic_sbp;
+ ASSERT(if (!c) {
+ printk(KERN_ERR "jffs_rename(): The old_dir inode "
+ "didn't have a reference to a jffs_file struct\n");
+ return -EIO;
+ });
+
+ result = -ENOTDIR;
+ if (!(old_dir_f = (struct jffs_file *)old_dir->u.generic_ip)) {
+ D(printk("jffs_rename(): Old dir invalid.\n"));
+ goto jffs_rename_end;
+ }
+
+ /* Try to find the file to move. */
+ result = -ENOENT;
+ if (!(f = jffs_find_child(old_dir_f, old_dentry->d_name.name,
+ old_dentry->d_name.len))) {
+ goto jffs_rename_end;
+ }
+
+ /* Find the new directory. */
+ result = -ENOTDIR;
+ if (!(new_dir_f = (struct jffs_file *)new_dir->u.generic_ip)) {
+ D(printk("jffs_rename(): New dir invalid.\n"));
+ goto jffs_rename_end;
+ }
+ D3(printk (KERN_NOTICE "rename(): down biglock\n"));
+ down(&c->fmc->biglock);
+ /* Create a node and initialize as much as needed. */
+ result = -ENOMEM;
+ if (!(node = jffs_alloc_node())) {
+ D(printk("jffs_rename(): Allocation failed: node == 0\n"));
+ goto jffs_rename_end;
+ }
+ node->data_offset = 0;
+ node->removed_size = 0;
+
+ /* Initialize the raw inode. */
+ raw_inode.magic = JFFS_MAGIC_BITMASK;
+ raw_inode.ino = f->ino;
+ raw_inode.pino = new_dir_f->ino;
+/* raw_inode.version = f->highest_version + 1; */
+ raw_inode.mode = f->mode;
+ raw_inode.uid = current->fsuid;
+ raw_inode.gid = current->fsgid;
+#if 0
+ raw_inode.uid = f->uid;
+ raw_inode.gid = f->gid;
+#endif
+ raw_inode.atime = CURRENT_TIME;
+ raw_inode.mtime = raw_inode.atime;
+ raw_inode.ctime = f->ctime;
+ raw_inode.offset = 0;
+ raw_inode.dsize = 0;
+ raw_inode.rsize = 0;
+ raw_inode.nsize = new_dentry->d_name.len;
+ raw_inode.nlink = f->nlink;
+ raw_inode.spare = 0;
+ raw_inode.rename = 0;
+ raw_inode.deleted = 0;
+
+ /* See if there already exists a file with the same name as
+ new_name. */
+ if ((del_f = jffs_find_child(new_dir_f, new_dentry->d_name.name,
+ new_dentry->d_name.len))) {
+ raw_inode.rename = 1;
+ raw_inode.dsize = sizeof(__u32);
+ rename_data = del_f->ino;
+ }
+
+ /* Write the new node to the flash memory. */
+ if ((result = jffs_write_node(c, node, &raw_inode,
+ new_dentry->d_name.name,
+ (unsigned char*)&rename_data, 0, f)) < 0) {
+ D(printk("jffs_rename(): Failed to write node to flash.\n"));
+ jffs_free_node(node);
+ goto jffs_rename_end;
+ }
+ raw_inode.dsize = 0;
+
+ if (raw_inode.rename) {
+ /* The file with the same name must be deleted. */
+ //FIXME deadlock down(&c->fmc->gclock);
+ if ((result = jffs_remove(new_dir, new_dentry,
+ del_f->mode)) < 0) {
+ /* This is really bad. */
+ printk(KERN_ERR "JFFS: An error occurred in "
+ "rename().\n");
+ }
+ // up(&c->fmc->gclock);
+ }
+
+ if (old_dir_f != new_dir_f) {
+ /* Remove the file from its old position in the
+ filesystem tree. */
+ jffs_unlink_file_from_tree(f);
+ }
+
+ /* Insert the new node into the file system. */
+ if ((result = jffs_insert_node(c, f, &raw_inode,
+ new_dentry->d_name.name, node)) < 0) {
+ D(printk(KERN_ERR "jffs_rename(): jffs_insert_node() "
+ "failed!\n"));
+ }
+
+ if (old_dir_f != new_dir_f) {
+ /* Insert the file to its new position in the
+ file system. */
+ jffs_insert_file_into_tree(f);
+ }
+
+ /* This is a kind of update of the inode we're about to make
+ here. This is what they do in ext2fs. Kind of. */
+ if ((inode = iget(new_dir->i_sb, f->ino))) {
+ inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+ iput(inode);
+ }
+
+jffs_rename_end:
+ D3(printk (KERN_NOTICE "rename(): up biglock\n"));
+ up(&c->fmc->biglock);
+ return result;
+} /* jffs_rename() */
+
+
+/* Read the contents of a directory. Used by programs like `ls'
+ for instance. */
+static int
+jffs_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+ struct jffs_file *f;
+ struct dentry *dentry = filp->f_dentry;
+ struct inode *inode = dentry->d_inode;
+ struct jffs_control *c = (struct jffs_control *)inode->i_sb->u.generic_sbp;
+ int j;
+ int ddino;
+ D3(printk (KERN_NOTICE "readdir(): down biglock\n"));
+ down(&c->fmc->biglock);
+
+ D2(printk("jffs_readdir(): inode: 0x%p, filp: 0x%p\n", inode, filp));
+ if (filp->f_pos == 0) {
+ D3(printk("jffs_readdir(): \".\" %lu\n", inode->i_ino));
+ if (filldir(dirent, ".", 1, filp->f_pos, inode->i_ino, DT_DIR) < 0) {
+ D3(printk (KERN_NOTICE "readdir(): up biglock\n"));
+ up(&c->fmc->biglock);
+ return 0;
+ }
+ filp->f_pos = 1;
+ }
+ if (filp->f_pos == 1) {
+ if (inode->i_ino == JFFS_MIN_INO) {
+ ddino = JFFS_MIN_INO;
+ }
+ else {
+ ddino = ((struct jffs_file *)
+ inode->u.generic_ip)->pino;
+ }
+ D3(printk("jffs_readdir(): \"..\" %u\n", ddino));
+ if (filldir(dirent, "..", 2, filp->f_pos, ddino, DT_DIR) < 0) {
+ D3(printk (KERN_NOTICE "readdir(): up biglock\n"));
+ up(&c->fmc->biglock);
+ return 0;
+ }
+ filp->f_pos++;
+ }
+ f = ((struct jffs_file *)inode->u.generic_ip)->children;
+
+ j = 2;
+ while(f && (f->deleted || j++ < filp->f_pos )) {
+ f = f->sibling_next;
+ }
+
+ while (f) {
+ D3(printk("jffs_readdir(): \"%s\" ino: %u\n",
+ (f->name ? f->name : ""), f->ino));
+ if (filldir(dirent, f->name, f->nsize,
+ filp->f_pos , f->ino, DT_UNKNOWN) < 0) {
+ D3(printk (KERN_NOTICE "readdir(): up biglock\n"));
+ up(&c->fmc->biglock);
+ return 0;
+ }
+ filp->f_pos++;
+ do {
+ f = f->sibling_next;
+ } while(f && f->deleted);
+ }
+ D3(printk (KERN_NOTICE "readdir(): up biglock\n"));
+ up(&c->fmc->biglock);
+ return filp->f_pos;
+} /* jffs_readdir() */
+
+
+/* Find a file in a directory. If the file exists, return its
+ corresponding dentry. */
+static struct dentry *
+jffs_lookup(struct inode *dir, struct dentry *dentry)
+{
+ struct jffs_file *d;
+ struct jffs_file *f;
+ struct jffs_control *c = (struct jffs_control *)dir->i_sb->u.generic_sbp;
+ int len;
+ int r = 0;
+ const char *name;
+ struct inode *inode = NULL;
+
+ len = dentry->d_name.len;
+ name = dentry->d_name.name;
+
+ D3({
+ char *s = (char *)kmalloc(len + 1, GFP_KERNEL);
+ memcpy(s, name, len);
+ s[len] = '\0';
+ printk("jffs_lookup(): dir: 0x%p, name: \"%s\"\n", dir, s);
+ kfree(s);
+ });
+
+ D3(printk (KERN_NOTICE "lookup(): down biglock\n"));
+ down(&c->fmc->biglock);
+
+ r = -ENAMETOOLONG;
+ if (len > JFFS_MAX_NAME_LEN) {
+ goto jffs_lookup_end;
+ }
+
+ r = -EACCES;
+ if (!(d = (struct jffs_file *)dir->u.generic_ip)) {
+ D(printk("jffs_lookup(): No such inode! (%lu)\n",
+ dir->i_ino));
+ goto jffs_lookup_end;
+ }
+
+ /* Get the corresponding inode to the file. */
+
+ /* iget calls jffs_read_inode, so we need to drop the biglock
+ before calling iget. Unfortunately, the GC has a tendency
+ to sneak in here, because iget sometimes calls schedule ().
+ */
+
+ if ((len == 1) && (name[0] == '.')) {
+ D3(printk (KERN_NOTICE "lookup(): up biglock\n"));
+ up(&c->fmc->biglock);
+ if (!(inode = iget(dir->i_sb, d->ino))) {
+ D(printk("jffs_lookup(): . iget() ==> NULL\n"));
+ goto jffs_lookup_end_no_biglock;
+ }
+ D3(printk (KERN_NOTICE "lookup(): down biglock\n"));
+ down(&c->fmc->biglock);
+ } else if ((len == 2) && (name[0] == '.') && (name[1] == '.')) {
+ D3(printk (KERN_NOTICE "lookup(): up biglock\n"));
+ up(&c->fmc->biglock);
+ if (!(inode = iget(dir->i_sb, d->pino))) {
+ D(printk("jffs_lookup(): .. iget() ==> NULL\n"));
+ goto jffs_lookup_end_no_biglock;
+ }
+ D3(printk (KERN_NOTICE "lookup(): down biglock\n"));
+ down(&c->fmc->biglock);
+ } else if ((f = jffs_find_child(d, name, len))) {
+ D3(printk (KERN_NOTICE "lookup(): up biglock\n"));
+ up(&c->fmc->biglock);
+ if (!(inode = iget(dir->i_sb, f->ino))) {
+ D(printk("jffs_lookup(): iget() ==> NULL\n"));
+ goto jffs_lookup_end_no_biglock;
+ }
+ D3(printk (KERN_NOTICE "lookup(): down biglock\n"));
+ down(&c->fmc->biglock);
+ } else {
+ D3(printk("jffs_lookup(): Couldn't find the file. "
+ "f = 0x%p, name = \"%s\", d = 0x%p, d->ino = %u\n",
+ f, name, d, d->ino));
+ inode = NULL;
+ }
+
+ d_add(dentry, inode);
+ D3(printk (KERN_NOTICE "lookup(): up biglock\n"));
+ up(&c->fmc->biglock);
+ return NULL;
+
+jffs_lookup_end:
+ D3(printk (KERN_NOTICE "lookup(): up biglock\n"));
+ up(&c->fmc->biglock);
+
+jffs_lookup_end_no_biglock:
+ return ERR_PTR(r);
+} /* jffs_lookup() */
+
+
+/* Try to read a page of data from a file. */
+static int
+jffs_do_readpage_nolock(struct file *file, struct page *page)
+{
+ void *buf;
+ unsigned long read_len;
+ int result;
+ struct inode *inode = (struct inode*)page->mapping->host;
+ struct jffs_file *f = (struct jffs_file *)inode->u.generic_ip;
+ struct jffs_control *c = (struct jffs_control *)inode->i_sb->u.generic_sbp;
+ int r;
+ loff_t offset;
+
+ D2(printk("***jffs_readpage(): file = \"%s\", page->index = %lu\n",
+ (f->name ? f->name : ""), (long)page->index));
+
+ get_page(page);
+ /* Don't LockPage(page), should be locked already */
+ buf = page_address(page);
+ ClearPageUptodate(page);
+ ClearPageError(page);
+
+ D3(printk (KERN_NOTICE "readpage(): down biglock\n"));
+ down(&c->fmc->biglock);
+
+ read_len = 0;
+ result = 0;
+
+ offset = page->index << PAGE_CACHE_SHIFT;
+ if (offset < inode->i_size) {
+ read_len = min_t(long, inode->i_size - offset, PAGE_SIZE);
+ r = jffs_read_data(f, buf, offset, read_len);
+ if (r != read_len) {
+ result = -EIO;
+ D(
+ printk("***jffs_readpage(): Read error! "
+ "Wanted to read %lu bytes but only "
+ "read %d bytes.\n", read_len, r);
+ );
+ }
+
+ }
+
+ /* This handles the case of partial or no read in above */
+ if(read_len < PAGE_SIZE)
+ memset(buf + read_len, 0, PAGE_SIZE - read_len);
+
+ D3(printk (KERN_NOTICE "readpage(): up biglock\n"));
+ up(&c->fmc->biglock);
+
+ if (result) {
+ SetPageError(page);
+ }else {
+ SetPageUptodate(page);
+ }
+ flush_dcache_page(page);
+
+ put_page(page);
+
+ D3(printk("jffs_readpage(): Leaving...\n"));
+
+ return result;
+} /* jffs_do_readpage_nolock() */
+
+static int jffs_readpage(struct file *file, struct page *page)
+{
+ int ret = jffs_do_readpage_nolock(file, page);
+ UnlockPage(page);
+ return ret;
+}
+
+/* Create a new directory. */
+static int
+jffs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+ struct jffs_raw_inode raw_inode;
+ struct jffs_control *c;
+ struct jffs_node *node;
+ struct jffs_file *dir_f;
+ struct inode *inode;
+ int dir_mode;
+ int result = 0;
+ int err;
+
+ D1({
+ int len = dentry->d_name.len;
+ char *_name = (char *) kmalloc(len + 1, GFP_KERNEL);
+ memcpy(_name, dentry->d_name.name, len);
+ _name[len] = '\0';
+ printk("***jffs_mkdir(): dir = 0x%p, name = \"%s\", "
+ "len = %d, mode = 0x%08x\n", dir, _name, len, mode);
+ kfree(_name);
+ });
+
+ dir_f = (struct jffs_file *)dir->u.generic_ip;
+
+ ASSERT(if (!dir_f) {
+ printk(KERN_ERR "jffs_mkdir(): No reference to a "
+ "jffs_file struct in inode.\n");
+ return -EIO;
+ });
+
+ c = dir_f->c;
+ D3(printk (KERN_NOTICE "mkdir(): down biglock\n"));
+ down(&c->fmc->biglock);
+
+ dir_mode = S_IFDIR | (mode & (S_IRWXUGO|S_ISVTX)
+ & ~current->fs->umask);
+ if (dir->i_mode & S_ISGID) {
+ dir_mode |= S_ISGID;
+ }
+
+ /* Create a node and initialize it as much as needed. */
+ if (!(node = jffs_alloc_node())) {
+ D(printk("jffs_mkdir(): Allocation failed: node == 0\n"));
+ result = -ENOMEM;
+ goto jffs_mkdir_end;
+ }
+ node->data_offset = 0;
+ node->removed_size = 0;
+
+ /* Initialize the raw inode. */
+ raw_inode.magic = JFFS_MAGIC_BITMASK;
+ raw_inode.ino = c->next_ino++;
+ raw_inode.pino = dir_f->ino;
+ raw_inode.version = 1;
+ raw_inode.mode = dir_mode;
+ raw_inode.uid = current->fsuid;
+ raw_inode.gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid;
+ /* raw_inode.gid = current->fsgid; */
+ raw_inode.atime = CURRENT_TIME;
+ raw_inode.mtime = raw_inode.atime;
+ raw_inode.ctime = raw_inode.atime;
+ raw_inode.offset = 0;
+ raw_inode.dsize = 0;
+ raw_inode.rsize = 0;
+ raw_inode.nsize = dentry->d_name.len;
+ raw_inode.nlink = 1;
+ raw_inode.spare = 0;
+ raw_inode.rename = 0;
+ raw_inode.deleted = 0;
+
+ /* Write the new node to the flash. */
+ if ((result = jffs_write_node(c, node, &raw_inode,
+ dentry->d_name.name, 0, 0, NULL)) < 0) {
+ D(printk("jffs_mkdir(): jffs_write_node() failed.\n"));
+ jffs_free_node(node);
+ goto jffs_mkdir_end;
+ }
+
+ /* Insert the new node into the file system. */
+ if ((result = jffs_insert_node(c, 0, &raw_inode, dentry->d_name.name,
+ node)) < 0) {
+ goto jffs_mkdir_end;
+ }
+
+ inode = jffs_new_inode(dir, &raw_inode, &err);
+ if (inode == NULL) {
+ result = err;
+ goto jffs_mkdir_end;
+ }
+
+ inode->i_op = &jffs_dir_inode_operations;
+ inode->i_fop = &jffs_dir_operations;
+
+ mark_inode_dirty(dir);
+ d_instantiate(dentry, inode);
+
+ result = 0;
+jffs_mkdir_end:
+ D3(printk (KERN_NOTICE "mkdir(): up biglock\n"));
+ up(&c->fmc->biglock);
+ return result;
+} /* jffs_mkdir() */
+
+
+/* Remove a directory. */
+static int
+jffs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ struct jffs_control *c = (struct jffs_control *)dir->i_sb->u.generic_sbp;
+ int ret;
+ D3(printk("***jffs_rmdir()\n"));
+ D3(printk (KERN_NOTICE "rmdir(): down biglock\n"));
+ down(&c->fmc->biglock);
+ ret = jffs_remove(dir, dentry, S_IFDIR);
+ D3(printk (KERN_NOTICE "rmdir(): up biglock\n"));
+ up(&c->fmc->biglock);
+ return ret;
+}
+
+
+/* Remove any kind of file except for directories. */
+static int
+jffs_unlink(struct inode *dir, struct dentry *dentry)
+{
+ struct jffs_control *c = (struct jffs_control *)dir->i_sb->u.generic_sbp;
+ int ret;
+
+ D3(printk("***jffs_unlink()\n"));
+ D3(printk (KERN_NOTICE "unlink(): down biglock\n"));
+ down(&c->fmc->biglock);
+ ret = jffs_remove(dir, dentry, 0);
+ D3(printk (KERN_NOTICE "unlink(): up biglock\n"));
+ up(&c->fmc->biglock);
+ return ret;
+}
+
+
+/* Remove a JFFS entry, i.e. plain files, directories, etc. Here we
+ shouldn't test for free space on the device. */
+static int
+jffs_remove(struct inode *dir, struct dentry *dentry, int type)
+{
+ struct jffs_raw_inode raw_inode;
+ struct jffs_control *c;
+ struct jffs_file *dir_f; /* The file-to-remove's parent. */
+ struct jffs_file *del_f; /* The file to remove. */
+ struct jffs_node *del_node;
+ struct inode *inode = 0;
+ int result = 0;
+
+ D1({
+ int len = dentry->d_name.len;
+ const char *name = dentry->d_name.name;
+ char *_name = (char *) kmalloc(len + 1, GFP_KERNEL);
+ memcpy(_name, name, len);
+ _name[len] = '\0';
+ printk("***jffs_remove(): file = \"%s\", ino = %ld\n", _name, dentry->d_inode->i_ino);
+ kfree(_name);
+ });
+
+ dir_f = (struct jffs_file *) dir->u.generic_ip;
+ c = dir_f->c;
+
+ result = -ENOENT;
+ if (!(del_f = jffs_find_child(dir_f, dentry->d_name.name,
+ dentry->d_name.len))) {
+ D(printk("jffs_remove(): jffs_find_child() failed.\n"));
+ goto jffs_remove_end;
+ }
+
+ if (S_ISDIR(type)) {
+ struct jffs_file *child = del_f->children;
+ while(child) {
+ if( !child->deleted ) {
+ result = -ENOTEMPTY;
+ goto jffs_remove_end;
+ }
+ child = child->sibling_next;
+ }
+ }
+ else if (S_ISDIR(del_f->mode)) {
+ D(printk("jffs_remove(): node is a directory "
+ "but it shouldn't be.\n"));
+ result = -EPERM;
+ goto jffs_remove_end;
+ }
+
+ inode = dentry->d_inode;
+
+ result = -EIO;
+ if (del_f->ino != inode->i_ino)
+ goto jffs_remove_end;
+
+ if (!inode->i_nlink) {
+ printk("Deleting nonexistent file inode: %lu, nlink: %d\n",
+ inode->i_ino, inode->i_nlink);
+ inode->i_nlink=1;
+ }
+
+ /* Create a node for the deletion. */
+ result = -ENOMEM;
+ if (!(del_node = jffs_alloc_node())) {
+ D(printk("jffs_remove(): Allocation failed!\n"));
+ goto jffs_remove_end;
+ }
+ del_node->data_offset = 0;
+ del_node->removed_size = 0;
+
+ /* Initialize the raw inode. */
+ raw_inode.magic = JFFS_MAGIC_BITMASK;
+ raw_inode.ino = del_f->ino;
+ raw_inode.pino = del_f->pino;
+/* raw_inode.version = del_f->highest_version + 1; */
+ raw_inode.mode = del_f->mode;
+ raw_inode.uid = current->fsuid;
+ raw_inode.gid = current->fsgid;
+ raw_inode.atime = CURRENT_TIME;
+ raw_inode.mtime = del_f->mtime;
+ raw_inode.ctime = raw_inode.atime;
+ raw_inode.offset = 0;
+ raw_inode.dsize = 0;
+ raw_inode.rsize = 0;
+ raw_inode.nsize = 0;
+ raw_inode.nlink = del_f->nlink;
+ raw_inode.spare = 0;
+ raw_inode.rename = 0;
+ raw_inode.deleted = 1;
+
+ /* Write the new node to the flash memory. */
+ if (jffs_write_node(c, del_node, &raw_inode, 0, 0, 1, del_f) < 0) {
+ jffs_free_node(del_node);
+ result = -EIO;
+ goto jffs_remove_end;
+ }
+
+ /* Update the file. This operation will make the file disappear
+ from the in-memory file system structures. */
+ jffs_insert_node(c, del_f, &raw_inode, 0, del_node);
+
+ dir->i_version = ++event;
+ dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ mark_inode_dirty(dir);
+ inode->i_nlink--;
+ inode->i_ctime = dir->i_ctime;
+ mark_inode_dirty(inode);
+
+ d_delete(dentry); /* This also frees the inode */
+
+ result = 0;
+jffs_remove_end:
+ return result;
+} /* jffs_remove() */
+
+
+static int
+jffs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev)
+{
+ struct jffs_raw_inode raw_inode;
+ struct jffs_file *dir_f;
+ struct jffs_node *node = 0;
+ struct jffs_control *c;
+ struct inode *inode;
+ int result = 0;
+ kdev_t dev = to_kdev_t(rdev);
+ int err;
+
+ D1(printk("***jffs_mknod()\n"));
+
+ dir_f = (struct jffs_file *)dir->u.generic_ip;
+ c = dir_f->c;
+
+ D3(printk (KERN_NOTICE "mknod(): down biglock\n"));
+ down(&c->fmc->biglock);
+
+ /* Create and initialize a new node. */
+ if (!(node = jffs_alloc_node())) {
+ D(printk("jffs_mknod(): Allocation failed!\n"));
+ result = -ENOMEM;
+ goto jffs_mknod_err;
+ }
+ node->data_offset = 0;
+ node->removed_size = 0;
+
+ /* Initialize the raw inode. */
+ raw_inode.magic = JFFS_MAGIC_BITMASK;
+ raw_inode.ino = c->next_ino++;
+ raw_inode.pino = dir_f->ino;
+ raw_inode.version = 1;
+ raw_inode.mode = mode;
+ raw_inode.uid = current->fsuid;
+ raw_inode.gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid;
+ /* raw_inode.gid = current->fsgid; */
+ raw_inode.atime = CURRENT_TIME;
+ raw_inode.mtime = raw_inode.atime;
+ raw_inode.ctime = raw_inode.atime;
+ raw_inode.offset = 0;
+ raw_inode.dsize = sizeof(kdev_t);
+ raw_inode.rsize = 0;
+ raw_inode.nsize = dentry->d_name.len;
+ raw_inode.nlink = 1;
+ raw_inode.spare = 0;
+ raw_inode.rename = 0;
+ raw_inode.deleted = 0;
+
+ /* Write the new node to the flash. */
+ if ((err = jffs_write_node(c, node, &raw_inode, dentry->d_name.name,
+ (unsigned char *)&dev, 0, NULL)) < 0) {
+ D(printk("jffs_mknod(): jffs_write_node() failed.\n"));
+ result = err;
+ goto jffs_mknod_err;
+ }
+
+ /* Insert the new node into the file system. */
+ if ((err = jffs_insert_node(c, 0, &raw_inode, dentry->d_name.name,
+ node)) < 0) {
+ result = err;
+ goto jffs_mknod_end;
+ }
+
+ inode = jffs_new_inode(dir, &raw_inode, &err);
+ if (inode == NULL) {
+ result = err;
+ goto jffs_mknod_end;
+ }
+
+ init_special_inode(inode, mode, rdev);
+
+ d_instantiate(dentry, inode);
+
+ goto jffs_mknod_end;
+
+jffs_mknod_err:
+ if (node) {
+ jffs_free_node(node);
+ }
+
+jffs_mknod_end:
+ D3(printk (KERN_NOTICE "mknod(): up biglock\n"));
+ up(&c->fmc->biglock);
+ return result;
+} /* jffs_mknod() */
+
+
+static int
+jffs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
+{
+ struct jffs_raw_inode raw_inode;
+ struct jffs_control *c;
+ struct jffs_file *dir_f;
+ struct jffs_node *node;
+ struct inode *inode;
+
+ int symname_len = strlen(symname);
+ int err;
+
+ D1({
+ int len = dentry->d_name.len;
+ char *_name = (char *)kmalloc(len + 1, GFP_KERNEL);
+ char *_symname = (char *)kmalloc(symname_len + 1, GFP_KERNEL);
+ memcpy(_name, dentry->d_name.name, len);
+ _name[len] = '\0';
+ memcpy(_symname, symname, symname_len);
+ _symname[symname_len] = '\0';
+ printk("***jffs_symlink(): dir = 0x%p, "
+ "dentry->dname.name = \"%s\", "
+ "symname = \"%s\"\n", dir, _name, _symname);
+ kfree(_name);
+ kfree(_symname);
+ });
+
+ dir_f = (struct jffs_file *)dir->u.generic_ip;
+ ASSERT(if (!dir_f) {
+ printk(KERN_ERR "jffs_symlink(): No reference to a "
+ "jffs_file struct in inode.\n");
+ return -EIO;
+ });
+
+ c = dir_f->c;
+
+ /* Create a node and initialize it as much as needed. */
+ if (!(node = jffs_alloc_node())) {
+ D(printk("jffs_symlink(): Allocation failed: node = NULL\n"));
+ return -ENOMEM;
+ }
+ D3(printk (KERN_NOTICE "symlink(): down biglock\n"));
+ down(&c->fmc->biglock);
+
+ node->data_offset = 0;
+ node->removed_size = 0;
+
+ /* Initialize the raw inode. */
+ raw_inode.magic = JFFS_MAGIC_BITMASK;
+ raw_inode.ino = c->next_ino++;
+ raw_inode.pino = dir_f->ino;
+ raw_inode.version = 1;
+ raw_inode.mode = S_IFLNK | S_IRWXUGO;
+ raw_inode.uid = current->fsuid;
+ raw_inode.gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid;
+ raw_inode.atime = CURRENT_TIME;
+ raw_inode.mtime = raw_inode.atime;
+ raw_inode.ctime = raw_inode.atime;
+ raw_inode.offset = 0;
+ raw_inode.dsize = symname_len;
+ raw_inode.rsize = 0;
+ raw_inode.nsize = dentry->d_name.len;
+ raw_inode.nlink = 1;
+ raw_inode.spare = 0;
+ raw_inode.rename = 0;
+ raw_inode.deleted = 0;
+
+ /* Write the new node to the flash. */
+ if ((err = jffs_write_node(c, node, &raw_inode, dentry->d_name.name,
+ (const unsigned char *)symname, 0, NULL)) < 0) {
+ D(printk("jffs_symlink(): jffs_write_node() failed.\n"));
+ jffs_free_node(node);
+ goto jffs_symlink_end;
+ }
+
+ /* Insert the new node into the file system. */
+ if ((err = jffs_insert_node(c, 0, &raw_inode, dentry->d_name.name,
+ node)) < 0) {
+ goto jffs_symlink_end;
+ }
+
+ inode = jffs_new_inode(dir, &raw_inode, &err);
+ if (inode == NULL) {
+ goto jffs_symlink_end;
+ }
+ err = 0;
+ inode->i_op = &page_symlink_inode_operations;
+ inode->i_mapping->a_ops = &jffs_address_operations;
+
+ d_instantiate(dentry, inode);
+ jffs_symlink_end:
+ D3(printk (KERN_NOTICE "symlink(): up biglock\n"));
+ up(&c->fmc->biglock);
+ return err;
+} /* jffs_symlink() */
+
+
+/* Create an inode inside a JFFS directory (dir) and return it.
+ *
+ * By the time this is called, we already have created
+ * the directory cache entry for the new file, but it
+ * is so far negative - it has no inode.
+ *
+ * If the create succeeds, we fill in the inode information
+ * with d_instantiate().
+ */
+static int
+jffs_create(struct inode *dir, struct dentry *dentry, int mode)
+{
+ struct jffs_raw_inode raw_inode;
+ struct jffs_control *c;
+ struct jffs_node *node;
+ struct jffs_file *dir_f; /* JFFS representation of the directory. */
+ struct inode *inode;
+ int err;
+
+ D1({
+ int len = dentry->d_name.len;
+ char *s = (char *)kmalloc(len + 1, GFP_KERNEL);
+ memcpy(s, dentry->d_name.name, len);
+ s[len] = '\0';
+ printk("jffs_create(): dir: 0x%p, name: \"%s\"\n", dir, s);
+ kfree(s);
+ });
+
+ dir_f = (struct jffs_file *)dir->u.generic_ip;
+ ASSERT(if (!dir_f) {
+ printk(KERN_ERR "jffs_create(): No reference to a "
+ "jffs_file struct in inode.\n");
+ return -EIO;
+ });
+
+ c = dir_f->c;
+
+ /* Create a node and initialize as much as needed. */
+ if (!(node = jffs_alloc_node())) {
+ D(printk("jffs_create(): Allocation failed: node == 0\n"));
+ return -ENOMEM;
+ }
+ D3(printk (KERN_NOTICE "create(): down biglock\n"));
+ down(&c->fmc->biglock);
+
+ node->data_offset = 0;
+ node->removed_size = 0;
+
+ /* Initialize the raw inode. */
+ raw_inode.magic = JFFS_MAGIC_BITMASK;
+ raw_inode.ino = c->next_ino++;
+ raw_inode.pino = dir_f->ino;
+ raw_inode.version = 1;
+ raw_inode.mode = mode;
+ raw_inode.uid = current->fsuid;
+ raw_inode.gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid;
+ raw_inode.atime = CURRENT_TIME;
+ raw_inode.mtime = raw_inode.atime;
+ raw_inode.ctime = raw_inode.atime;
+ raw_inode.offset = 0;
+ raw_inode.dsize = 0;
+ raw_inode.rsize = 0;
+ raw_inode.nsize = dentry->d_name.len;
+ raw_inode.nlink = 1;
+ raw_inode.spare = 0;
+ raw_inode.rename = 0;
+ raw_inode.deleted = 0;
+
+ /* Write the new node to the flash. */
+ if ((err = jffs_write_node(c, node, &raw_inode,
+ dentry->d_name.name, 0, 0, NULL)) < 0) {
+ D(printk("jffs_create(): jffs_write_node() failed.\n"));
+ jffs_free_node(node);
+ goto jffs_create_end;
+ }
+
+ /* Insert the new node into the file system. */
+ if ((err = jffs_insert_node(c, 0, &raw_inode, dentry->d_name.name,
+ node)) < 0) {
+ goto jffs_create_end;
+ }
+
+ /* Initialize an inode. */
+ inode = jffs_new_inode(dir, &raw_inode, &err);
+ if (inode == NULL) {
+ goto jffs_create_end;
+ }
+ err = 0;
+ inode->i_op = &jffs_file_inode_operations;
+ inode->i_fop = &jffs_file_operations;
+ inode->i_mapping->a_ops = &jffs_address_operations;
+ inode->i_mapping->nrpages = 0;
+
+ d_instantiate(dentry, inode);
+ jffs_create_end:
+ D3(printk (KERN_NOTICE "create(): up biglock\n"));
+ up(&c->fmc->biglock);
+ return err;
+} /* jffs_create() */
+
+
+/* Write, append or rewrite data to an existing file. */
+static ssize_t
+jffs_file_write(struct file *filp, const char *buf, size_t count,
+ loff_t *ppos)
+{
+ struct jffs_raw_inode raw_inode;
+ struct jffs_control *c;
+ struct jffs_file *f;
+ struct jffs_node *node;
+ struct dentry *dentry = filp->f_dentry;
+ struct inode *inode = dentry->d_inode;
+ int recoverable = 0;
+ size_t written = 0;
+ __u32 thiscount = count;
+ loff_t pos = *ppos;
+ int err;
+
+ inode = filp->f_dentry->d_inode;
+
+ D2(printk("***jffs_file_write(): inode: 0x%p (ino: %lu), "
+ "filp: 0x%p, buf: 0x%p, count: %d\n",
+ inode, inode->i_ino, filp, buf, count));
+
+#if 0
+ if (inode->i_sb->s_flags & MS_RDONLY) {
+ D(printk("jffs_file_write(): MS_RDONLY\n"));
+ err = -EROFS;
+ goto out_isem;
+ }
+#endif
+ err = -EINVAL;
+
+ if (!S_ISREG(inode->i_mode)) {
+ D(printk("jffs_file_write(): inode->i_mode == 0x%08x\n",
+ inode->i_mode));
+ goto out_isem;
+ }
+
+ if (!(f = (struct jffs_file *)inode->u.generic_ip)) {
+ D(printk("jffs_file_write(): inode->u.generic_ip = 0x%p\n",
+ inode->u.generic_ip));
+ goto out_isem;
+ }
+
+ c = f->c;
+
+ /*
+ * This will never trigger with sane page sizes. leave it in
+ * anyway, since I'm thinking about how to merge larger writes
+ * (the current idea is to poke a thread that does the actual
+ * I/O and starts by doing a down(&inode->i_sem). then we
+ * would need to get the page cache pages and have a list of
+ * I/O requests and do write-merging here.
+ * -- prumpf
+ */
+ thiscount = min(c->fmc->max_chunk_size - sizeof(struct jffs_raw_inode), count);
+
+ D3(printk (KERN_NOTICE "file_write(): down biglock\n"));
+ down(&c->fmc->biglock);
+
+ /* Urgh. POSIX says we can do short writes if we feel like it.
+ * In practice, we can't. Nothing will cope. So we loop until
+ * we're done.
+ *
+ * <_Anarchy_> posix and reality are not interconnected on this issue
+ */
+ while (count) {
+ /* Things are going to be written so we could allocate and
+ initialize the necessary data structures now. */
+ if (!(node = jffs_alloc_node())) {
+ D(printk("jffs_file_write(): node == 0\n"));
+ err = -ENOMEM;
+ goto out;
+ }
+
+ node->data_offset = pos;
+ node->removed_size = 0;
+
+ /* Initialize the raw inode. */
+ raw_inode.magic = JFFS_MAGIC_BITMASK;
+ raw_inode.ino = f->ino;
+ raw_inode.pino = f->pino;
+
+ raw_inode.mode = f->mode;
+
+ raw_inode.uid = f->uid;
+ raw_inode.gid = f->gid;
+ raw_inode.atime = CURRENT_TIME;
+ raw_inode.mtime = raw_inode.atime;
+ raw_inode.ctime = f->ctime;
+ raw_inode.offset = pos;
+ raw_inode.dsize = thiscount;
+ raw_inode.rsize = 0;
+ raw_inode.nsize = f->nsize;
+ raw_inode.nlink = f->nlink;
+ raw_inode.spare = 0;
+ raw_inode.rename = 0;
+ raw_inode.deleted = 0;
+
+ if (pos < f->size) {
+ node->removed_size = raw_inode.rsize = min(thiscount, (__u32)(f->size - pos));
+
+ /* If this node is going entirely over the top of old data,
+ we can allow it to go into the reserved space, because
+ we know that GC can reclaim the space later.
+ */
+ if (pos + thiscount < f->size) {
+ /* If all the data we're overwriting are _real_,
+ not just holes, then:
+ recoverable = 1;
+ */
+ }
+ }
+
+ /* Write the new node to the flash. */
+ /* NOTE: We would be quite happy if jffs_write_node() wrote a
+ smaller node than we were expecting. There's no need for it
+ to waste the space at the end of the flash just because it's
+ a little smaller than what we asked for. But that's a whole
+ new can of worms which I'm not going to open this week.
+ -- dwmw2.
+ */
+ if ((err = jffs_write_node(c, node, &raw_inode, f->name,
+ (const unsigned char *)buf,
+ recoverable, f)) < 0) {
+ D(printk("jffs_file_write(): jffs_write_node() failed.\n"));
+ jffs_free_node(node);
+ goto out;
+ }
+
+ written += err;
+ buf += err;
+ count -= err;
+ pos += err;
+
+ /* Insert the new node into the file system. */
+ if ((err = jffs_insert_node(c, f, &raw_inode, 0, node)) < 0) {
+ goto out;
+ }
+
+ D3(printk("jffs_file_write(): new f_pos %ld.\n", (long)pos));
+
+ thiscount = min(c->fmc->max_chunk_size - sizeof(struct jffs_raw_inode), count);
+ }
+ out:
+ D3(printk (KERN_NOTICE "file_write(): up biglock\n"));
+ up(&c->fmc->biglock);
+
+ /* Fix things in the real inode. */
+ if (pos > inode->i_size) {
+ inode->i_size = pos;
+ inode->i_blocks = (inode->i_size + 511) >> 9;
+ }
+ inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+ invalidate_inode_pages(inode);
+
+ out_isem:
+ return err;
+} /* jffs_file_write() */
+
+static ssize_t
+jffs_prepare_write(struct file *filp, struct page *page,
+ unsigned from, unsigned to)
+{
+ /* FIXME: we should detect some error conditions here */
+
+ /* Bugger that. We should make sure the page is uptodate */
+ if (!Page_Uptodate(page) && (from || to < PAGE_CACHE_SIZE))
+ return jffs_do_readpage_nolock(filp, page);
+
+ return 0;
+} /* jffs_prepare_write() */
+
+static ssize_t
+jffs_commit_write(struct file *filp, struct page *page,
+ unsigned from, unsigned to)
+{
+ void *addr = page_address(page) + from;
+ /* XXX: PAGE_CACHE_SHIFT or PAGE_SHIFT */
+ loff_t pos = (page->index<<PAGE_CACHE_SHIFT) + from;
+
+ return jffs_file_write(filp, addr, to-from, &pos);
+} /* jffs_commit_write() */
+
+/* This is our ioctl() routine. */
+static int
+jffs_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ struct jffs_control *c;
+ int ret = 0;
+
+ D2(printk("***jffs_ioctl(): cmd = 0x%08x, arg = 0x%08lx\n",
+ cmd, arg));
+
+ if (!(c = (struct jffs_control *)inode->i_sb->u.generic_sbp)) {
+ printk(KERN_ERR "JFFS: Bad inode in ioctl() call. "
+ "(cmd = 0x%08x)\n", cmd);
+ return -EIO;
+ }
+ D3(printk (KERN_NOTICE "ioctl(): down biglock\n"));
+ down(&c->fmc->biglock);
+
+ switch (cmd) {
+ case JFFS_PRINT_HASH:
+ jffs_print_hash_table(c);
+ break;
+ case JFFS_PRINT_TREE:
+ jffs_print_tree(c->root, 0);
+ break;
+ case JFFS_GET_STATUS:
+ {
+ struct jffs_flash_status fst;
+ struct jffs_fmcontrol *fmc = c->fmc;
+ printk("Flash status -- ");
+ if (!access_ok(VERIFY_WRITE,
+ (struct jffs_flash_status *)arg,
+ sizeof(struct jffs_flash_status))) {
+ D(printk("jffs_ioctl(): Bad arg in "
+ "JFFS_GET_STATUS ioctl!\n"));
+ ret = -EFAULT;
+ break;
+ }
+ fst.size = fmc->flash_size;
+ fst.used = fmc->used_size;
+ fst.dirty = fmc->dirty_size;
+ fst.begin = fmc->head->offset;
+ fst.end = fmc->tail->offset + fmc->tail->size;
+ printk("size: %d, used: %d, dirty: %d, "
+ "begin: %d, end: %d\n",
+ fst.size, fst.used, fst.dirty,
+ fst.begin, fst.end);
+ if (copy_to_user((struct jffs_flash_status *)arg,
+ &fst,
+ sizeof(struct jffs_flash_status))) {
+ ret = -EFAULT;
+ }
+ }
+ break;
+ default:
+ ret = -ENOTTY;
+ }
+ D3(printk (KERN_NOTICE "ioctl(): up biglock\n"));
+ up(&c->fmc->biglock);
+ return ret;
+} /* jffs_ioctl() */
+
+
+static struct address_space_operations jffs_address_operations = {
+ readpage: jffs_readpage,
+ prepare_write: jffs_prepare_write,
+ commit_write: jffs_commit_write,
+};
+
+static int jffs_fsync(struct file *f, struct dentry *d, int datasync)
+{
+ /* We currently have O_SYNC operations at all times.
+ Do nothing.
+ */
+ return 0;
+}
+
+
+extern int generic_file_open(struct inode *, struct file *) __attribute__((weak));
+extern loff_t generic_file_llseek(struct file *, loff_t, int) __attribute__((weak));
+
+static struct file_operations jffs_file_operations =
+{
+ open: generic_file_open,
+ llseek: generic_file_llseek,
+ read: generic_file_read,
+ write: generic_file_write,
+ ioctl: jffs_ioctl,
+ mmap: generic_file_mmap,
+ fsync: jffs_fsync,
+};
+
+
+static struct inode_operations jffs_file_inode_operations =
+{
+ lookup: jffs_lookup, /* lookup */
+ setattr: jffs_setattr,
+};
+
+
+static struct file_operations jffs_dir_operations =
+{
+ readdir: jffs_readdir,
+};
+
+
+static struct inode_operations jffs_dir_inode_operations =
+{
+ create: jffs_create,
+ lookup: jffs_lookup,
+ unlink: jffs_unlink,
+ symlink: jffs_symlink,
+ mkdir: jffs_mkdir,
+ rmdir: jffs_rmdir,
+ mknod: jffs_mknod,
+ rename: jffs_rename,
+ setattr: jffs_setattr,
+};
+
+
+/* Initialize an inode for the VFS. */
+static void
+jffs_read_inode(struct inode *inode)
+{
+ struct jffs_file *f;
+ struct jffs_control *c;
+
+ D3(printk("jffs_read_inode(): inode->i_ino == %lu\n", inode->i_ino));
+
+ if (!inode->i_sb) {
+ D(printk("jffs_read_inode(): !inode->i_sb ==> "
+ "No super block!\n"));
+ return;
+ }
+ c = (struct jffs_control *)inode->i_sb->u.generic_sbp;
+ D3(printk (KERN_NOTICE "read_inode(): down biglock\n"));
+ down(&c->fmc->biglock);
+ if (!(f = jffs_find_file(c, inode->i_ino))) {
+ D(printk("jffs_read_inode(): No such inode (%lu).\n",
+ inode->i_ino));
+ D3(printk (KERN_NOTICE "read_inode(): up biglock\n"));
+ up(&c->fmc->biglock);
+ return;
+ }
+ inode->u.generic_ip = (void *)f;
+ inode->i_mode = f->mode;
+ inode->i_nlink = f->nlink;
+ inode->i_uid = f->uid;
+ inode->i_gid = f->gid;
+ inode->i_size = f->size;
+ inode->i_atime = f->atime;
+ inode->i_mtime = f->mtime;
+ inode->i_ctime = f->ctime;
+ inode->i_blksize = PAGE_SIZE;
+ inode->i_blocks = (inode->i_size + 511) >> 9;
+ if (S_ISREG(inode->i_mode)) {
+ inode->i_op = &jffs_file_inode_operations;
+ inode->i_fop = &jffs_file_operations;
+ inode->i_mapping->a_ops = &jffs_address_operations;
+ }
+ else if (S_ISDIR(inode->i_mode)) {
+ inode->i_op = &jffs_dir_inode_operations;
+ inode->i_fop = &jffs_dir_operations;
+ }
+ else if (S_ISLNK(inode->i_mode)) {
+ inode->i_op = &page_symlink_inode_operations;
+ inode->i_mapping->a_ops = &jffs_address_operations;
+ }
+ else {
+ /* If the node is a device of some sort, then the number of
+ the device should be read from the flash memory and then
+ added to the inode's i_rdev member. */
+ kdev_t rdev;
+ jffs_read_data(f, (char *)&rdev, 0, sizeof(kdev_t));
+ init_special_inode(inode, inode->i_mode, kdev_t_to_nr(rdev));
+ }
+
+ D3(printk (KERN_NOTICE "read_inode(): up biglock\n"));
+ up(&c->fmc->biglock);
+}
+
+
+void
+jffs_delete_inode(struct inode *inode)
+{
+ struct jffs_file *f;
+ struct jffs_control *c;
+ D3(printk("jffs_delete_inode(): inode->i_ino == %lu\n",
+ inode->i_ino));
+
+ lock_kernel();
+ inode->i_size = 0;
+ inode->i_blocks = 0;
+ inode->u.generic_ip = 0;
+ clear_inode(inode);
+ if (inode->i_nlink == 0) {
+ c = (struct jffs_control *) inode->i_sb->u.generic_sbp;
+ f = (struct jffs_file *) jffs_find_file (c, inode->i_ino);
+ jffs_possibly_delete_file(f);
+ }
+
+ unlock_kernel();
+}
+
+
+void
+jffs_write_super(struct super_block *sb)
+{
+ struct jffs_control *c = (struct jffs_control *)sb->u.generic_sbp;
+
+ jffs_garbage_collect_trigger(c);
+}
+
+static struct super_operations jffs_ops =
+{
+ read_inode: jffs_read_inode,
+ delete_inode: jffs_delete_inode,
+ put_super: jffs_put_super,
+ write_super: jffs_write_super,
+ statfs: jffs_statfs,
+};
+
+
+static DECLARE_FSTYPE_DEV(jffs_fs_type, "jffs", jffs_read_super);
+
+static int __init
+init_jffs_fs(void)
+{
+ printk(KERN_INFO "JFFS version " JFFS_VERSION_STRING
+ ", (C) 1999, 2000 Axis Communications AB\n");
+
+#ifdef CONFIG_JFFS_PROC_FS
+ jffs_proc_root = proc_mkdir("jffs", proc_root_fs);
+#endif
+ fm_cache = kmem_cache_create("jffs_fm", sizeof(struct jffs_fm),
+ 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+ node_cache = kmem_cache_create("jffs_node",sizeof(struct jffs_node),
+ 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+ return register_filesystem(&jffs_fs_type);
+}
+
+static void __exit
+exit_jffs_fs(void)
+{
+ unregister_filesystem(&jffs_fs_type);
+ kmem_cache_destroy(fm_cache);
+ kmem_cache_destroy(node_cache);
+}
+
+EXPORT_NO_SYMBOLS;
+
+module_init(init_jffs_fs)
+module_exit(exit_jffs_fs)
+
+MODULE_DESCRIPTION("The Journalling Flash File System");
+MODULE_AUTHOR("Axis Communications AB.");
+MODULE_LICENSE("GPL");
diff --git a/fs/jffs/intrep.c b/fs/jffs/intrep.c
new file mode 100644
index 00000000000000..054f2c3973b046
--- /dev/null
+++ b/fs/jffs/intrep.c
@@ -0,0 +1,3439 @@
+/*
+ * JFFS -- Journaling Flash File System, Linux implementation.
+ *
+ * Copyright (C) 1999, 2000 Axis Communications, Inc.
+ *
+ * Created by Finn Hakansson <finn@axis.com>.
+ *
+ * This 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 of the License, or
+ * (at your option) any later version.
+ *
+ * $Id: intrep.c,v 1.102 2001/09/23 23:28:36 dwmw2 Exp $
+ *
+ * Ported to Linux 2.3.x and MTD:
+ * Copyright (C) 2000 Alexander Larsson (alex@cendio.se), Cendio Systems AB
+ *
+ */
+
+/* This file contains the code for the internal structure of the
+ Journaling Flash File System, JFFS. */
+
+/*
+ * Todo list:
+ *
+ * memcpy_to_flash() and memcpy_from_flash() functions.
+ *
+ * Implementation of hard links.
+ *
+ * Organize the source code in a better way. Against the VFS we could
+ * have jffs_ext.c, and against the block device jffs_int.c.
+ * A better file-internal organization too.
+ *
+ * A better checksum algorithm.
+ *
+ * Consider endianness stuff. ntohl() etc.
+ *
+ * Are we handling the atime, mtime, ctime members of the inode right?
+ *
+ * Remove some duplicated code. Take a look at jffs_write_node() and
+ * jffs_rewrite_data() for instance.
+ *
+ * Implement more meaning of the nlink member in various data structures.
+ * nlink could be used in conjunction with hard links for instance.
+ *
+ * Better memory management. Allocate data structures in larger chunks
+ * if possible.
+ *
+ * If too much meta data is stored, a garbage collect should be issued.
+ * We have experienced problems with too much meta data with for instance
+ * log files.
+ *
+ * Improve the calls to jffs_ioctl(). We would like to retrieve more
+ * information to be able to debug (or to supervise) JFFS during run-time.
+ *
+ */
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/jffs.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/pagemap.h>
+#include <linux/locks.h>
+#include <asm/semaphore.h>
+#include <asm/byteorder.h>
+#include <linux/version.h>
+#include <linux/smp_lock.h>
+#include <linux/sched.h>
+#include <linux/ctype.h>
+
+#include "intrep.h"
+#include "jffs_fm.h"
+
+long no_jffs_node = 0;
+long no_jffs_file = 0;
+#if defined(JFFS_MEMORY_DEBUG) && JFFS_MEMORY_DEBUG
+long no_jffs_control = 0;
+long no_jffs_raw_inode = 0;
+long no_jffs_node_ref = 0;
+long no_jffs_fm = 0;
+long no_jffs_fmcontrol = 0;
+long no_hash = 0;
+long no_name = 0;
+#endif
+
+static int jffs_scan_flash(struct jffs_control *c);
+static int jffs_update_file(struct jffs_file *f, struct jffs_node *node);
+
+#if CONFIG_JFFS_FS_VERBOSE > 0
+static __u8
+flash_read_u8(struct mtd_info *mtd, loff_t from)
+{
+ size_t retlen;
+ __u8 ret;
+ int res;
+
+ res = MTD_READ(mtd, from, 1, &retlen, &ret);
+ if (retlen != 1) {
+ printk("Didn't read a byte in flash_read_u8(). Returned %d\n", res);
+ return 0;
+ }
+
+ return ret;
+}
+
+static void
+jffs_hexdump(struct mtd_info *mtd, loff_t pos, int size)
+{
+ char line[16];
+ int j = 0;
+
+ while (size > 0) {
+ int i;
+
+ printk("%ld:", (long) pos);
+ for (j = 0; j < 16; j++) {
+ line[j] = flash_read_u8(mtd, pos++);
+ }
+ for (i = 0; i < j; i++) {
+ if (!(i & 1)) {
+ printk(" %.2x", line[i] & 0xff);
+ }
+ else {
+ printk("%.2x", line[i] & 0xff);
+ }
+ }
+
+ /* Print empty space */
+ for (; i < 16; i++) {
+ if (!(i & 1)) {
+ printk(" ");
+ }
+ else {
+ printk(" ");
+ }
+ }
+ printk(" ");
+
+ for (i = 0; i < j; i++) {
+ if (isgraph(line[i])) {
+ printk("%c", line[i]);
+ }
+ else {
+ printk(".");
+ }
+ }
+ printk("\n");
+ size -= 16;
+ }
+}
+
+#endif
+
+#define flash_safe_acquire(arg)
+#define flash_safe_release(arg)
+
+
+static int
+flash_safe_read(struct mtd_info *mtd, loff_t from,
+ u_char *buf, size_t count)
+{
+ size_t retlen;
+ int res;
+
+ D3(printk(KERN_NOTICE "flash_safe_read(%p, %08x, %p, %08x)\n",
+ mtd, (unsigned int) from, buf, count));
+
+ res = MTD_READ(mtd, from, count, &retlen, buf);
+ if (retlen != count) {
+ panic("Didn't read all bytes in flash_safe_read(). Returned %d\n", res);
+ }
+ return res?res:retlen;
+}
+
+
+static __u32
+flash_read_u32(struct mtd_info *mtd, loff_t from)
+{
+ size_t retlen;
+ __u32 ret;
+ int res;
+
+ res = MTD_READ(mtd, from, 4, &retlen, (unsigned char *)&ret);
+ if (retlen != 4) {
+ printk("Didn't read all bytes in flash_read_u32(). Returned %d\n", res);
+ return 0;
+ }
+
+ return ret;
+}
+
+
+static int
+flash_safe_write(struct mtd_info *mtd, loff_t to,
+ const u_char *buf, size_t count)
+{
+ size_t retlen;
+ int res;
+
+ D3(printk(KERN_NOTICE "flash_safe_write(%p, %08x, %p, %08x)\n",
+ mtd, (unsigned int) to, buf, count));
+
+ res = MTD_WRITE(mtd, to, count, &retlen, buf);
+ if (retlen != count) {
+ printk("Didn't write all bytes in flash_safe_write(). Returned %d\n", res);
+ }
+ return res?res:retlen;
+}
+
+
+static int
+flash_safe_writev(struct mtd_info *mtd, const struct iovec *vecs,
+ unsigned long iovec_cnt, loff_t to)
+{
+ size_t retlen, retlen_a;
+ int i;
+ int res;
+
+ D3(printk(KERN_NOTICE "flash_safe_writev(%p, %08x, %p)\n",
+ mtd, (unsigned int) to, vecs));
+
+ if (mtd->writev) {
+ res = MTD_WRITEV(mtd, vecs, iovec_cnt, to, &retlen);
+ return res ? res : retlen;
+ }
+ /* Not implemented writev. Repeatedly use write - on the not so
+ unreasonable assumption that the mtd driver doesn't care how
+ many write cycles we use. */
+ res=0;
+ retlen=0;
+
+ for (i=0; !res && i<iovec_cnt; i++) {
+ res = MTD_WRITE(mtd, to, vecs[i].iov_len, &retlen_a, vecs[i].iov_base);
+ if (retlen_a != vecs[i].iov_len) {
+ printk("Didn't write all bytes in flash_safe_writev(). Returned %d\n", res);
+ if (i != iovec_cnt-1)
+ return -EIO;
+ }
+ /* If res is non-zero, retlen_a is undefined, but we don't
+ care because in that case it's not going to be
+ returned anyway.
+ */
+ to += retlen_a;
+ retlen += retlen_a;
+ }
+ return res?res:retlen;
+}
+
+
+static int
+flash_memset(struct mtd_info *mtd, loff_t to,
+ const u_char c, size_t size)
+{
+ static unsigned char pattern[64];
+ int i;
+
+ /* fill up pattern */
+
+ for(i = 0; i < 64; i++)
+ pattern[i] = c;
+
+ /* write as many 64-byte chunks as we can */
+
+ while (size >= 64) {
+ flash_safe_write(mtd, to, pattern, 64);
+ size -= 64;
+ to += 64;
+ }
+
+ /* and the rest */
+
+ if(size)
+ flash_safe_write(mtd, to, pattern, size);
+
+ return size;
+}
+
+
+static void
+intrep_erase_callback(struct erase_info *done)
+{
+ wait_queue_head_t *wait_q;
+
+ wait_q = (wait_queue_head_t *)done->priv;
+
+ wake_up(wait_q);
+}
+
+
+static int
+flash_erase_region(struct mtd_info *mtd, loff_t start,
+ size_t size)
+{
+ struct erase_info *erase;
+ DECLARE_WAITQUEUE(wait, current);
+ wait_queue_head_t wait_q;
+
+ erase = kmalloc(sizeof(struct erase_info), GFP_KERNEL);
+ if (!erase)
+ return -ENOMEM;
+
+ init_waitqueue_head(&wait_q);
+
+ erase->mtd = mtd;
+ erase->callback = intrep_erase_callback;
+ erase->addr = start;
+ erase->len = size;
+ erase->priv = (u_long)&wait_q;
+
+ /* FIXME: Use TASK_INTERRUPTIBLE and deal with being interrupted */
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ add_wait_queue(&wait_q, &wait);
+
+ if (MTD_ERASE(mtd, erase) < 0) {
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&wait_q, &wait);
+ kfree(erase);
+
+ printk(KERN_WARNING "flash: erase of region [0x%lx, 0x%lx] "
+ "totally failed\n", (long)start, (long)start + size);
+
+ return -1;
+ }
+
+ schedule(); /* Wait for flash to finish. */
+ remove_wait_queue(&wait_q, &wait);
+
+ kfree(erase);
+
+ return 0;
+}
+
+/* This routine calculates checksums in JFFS. */
+__u32
+jffs_checksum(const void *data, int size)
+{
+ __u32 sum = 0;
+ __u8 *ptr = (__u8 *)data;
+ while (size-- > 0) {
+ sum += *ptr++;
+ }
+ D3(printk(", result: 0x%08x\n", sum));
+ return sum;
+}
+
+
+int
+jffs_checksum_flash(struct mtd_info *mtd, loff_t start, int size, __u32 *result)
+{
+ __u32 sum = 0;
+ loff_t ptr = start;
+ __u8 *read_buf;
+ int i, length;
+
+ /* Allocate read buffer */
+ read_buf = (__u8 *) kmalloc (sizeof(__u8) * 4096, GFP_KERNEL);
+ if (!read_buf) {
+ printk(KERN_NOTICE "kmalloc failed in jffs_checksum_flash()\n");
+ return -ENOMEM;
+ }
+ /* Loop until checksum done */
+ while (size) {
+ /* Get amount of data to read */
+ if (size < 4096)
+ length = size;
+ else
+ length = 4096;
+
+ /* Perform flash read */
+ D3(printk(KERN_NOTICE "jffs_checksum_flash\n"));
+ flash_safe_read(mtd, ptr, &read_buf[0], length);
+
+ /* Compute checksum */
+ for (i=0; i < length ; i++)
+ sum += read_buf[i];
+
+ /* Update pointer and size */
+ size -= length;
+ ptr += length;
+ }
+
+ /* Free read buffer */
+ kfree (read_buf);
+
+ /* Return result */
+ D3(printk("checksum result: 0x%08x\n", sum));
+ *result = sum;
+ return 0;
+}
+
+static __inline__ void jffs_fm_write_lock(struct jffs_fmcontrol *fmc)
+{
+ // down(&fmc->wlock);
+}
+
+static __inline__ void jffs_fm_write_unlock(struct jffs_fmcontrol *fmc)
+{
+ // up(&fmc->wlock);
+}
+
+
+/* Create and initialize a new struct jffs_file. */
+static struct jffs_file *
+jffs_create_file(struct jffs_control *c,
+ const struct jffs_raw_inode *raw_inode)
+{
+ struct jffs_file *f;
+
+ if (!(f = (struct jffs_file *)kmalloc(sizeof(struct jffs_file),
+ GFP_KERNEL))) {
+ D(printk("jffs_create_file(): Failed!\n"));
+ return 0;
+ }
+ no_jffs_file++;
+ memset(f, 0, sizeof(struct jffs_file));
+ f->ino = raw_inode->ino;
+ f->pino = raw_inode->pino;
+ f->nlink = raw_inode->nlink;
+ f->deleted = raw_inode->deleted;
+ f->c = c;
+
+ return f;
+}
+
+
+/* Build a control block for the file system. */
+static struct jffs_control *
+jffs_create_control(kdev_t dev)
+{
+ struct jffs_control *c;
+ register int s = sizeof(struct jffs_control);
+ int i;
+ D(char *t = 0);
+
+ D2(printk("jffs_create_control()\n"));
+
+ if (!(c = (struct jffs_control *)kmalloc(s, GFP_KERNEL))) {
+ goto fail_control;
+ }
+ DJM(no_jffs_control++);
+ c->root = 0;
+ c->gc_task = 0;
+ c->hash_len = JFFS_HASH_SIZE;
+ s = sizeof(struct list_head) * c->hash_len;
+ if (!(c->hash = (struct list_head *)kmalloc(s, GFP_KERNEL))) {
+ goto fail_hash;
+ }
+ DJM(no_hash++);
+ for (i = 0; i < c->hash_len; i++)
+ INIT_LIST_HEAD(&c->hash[i]);
+ if (!(c->fmc = jffs_build_begin(c, dev))) {
+ goto fail_fminit;
+ }
+ c->next_ino = JFFS_MIN_INO + 1;
+ c->delete_list = (struct jffs_delete_list *) 0;
+ return c;
+
+fail_fminit:
+ D(t = "c->fmc");
+fail_hash:
+ kfree(c);
+ DJM(no_jffs_control--);
+ D(t = t ? t : "c->hash");
+fail_control:
+ D(t = t ? t : "control");
+ D(printk("jffs_create_control(): Allocation failed: (%s)\n", t));
+ return (struct jffs_control *)0;
+}
+
+
+/* Clean up all data structures associated with the file system. */
+void
+jffs_cleanup_control(struct jffs_control *c)
+{
+ D2(printk("jffs_cleanup_control()\n"));
+
+ if (!c) {
+ D(printk("jffs_cleanup_control(): c == NULL !!!\n"));
+ return;
+ }
+
+ while (c->delete_list) {
+ struct jffs_delete_list *delete_list_element;
+ delete_list_element = c->delete_list;
+ c->delete_list = c->delete_list->next;
+ kfree(delete_list_element);
+ }
+
+ /* Free all files and nodes. */
+ if (c->hash) {
+ jffs_foreach_file(c, jffs_free_node_list);
+ jffs_foreach_file(c, jffs_free_file);
+ kfree(c->hash);
+ DJM(no_hash--);
+ }
+ jffs_cleanup_fmcontrol(c->fmc);
+ kfree(c);
+ DJM(no_jffs_control--);
+ D3(printk("jffs_cleanup_control(): Leaving...\n"));
+}
+
+
+/* This function adds a virtual root node to the in-RAM representation.
+ Called by jffs_build_fs(). */
+static int
+jffs_add_virtual_root(struct jffs_control *c)
+{
+ struct jffs_file *root;
+ struct jffs_node *node;
+
+ D2(printk("jffs_add_virtual_root(): "
+ "Creating a virtual root directory.\n"));
+
+ if (!(root = (struct jffs_file *)kmalloc(sizeof(struct jffs_file),
+ GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+ no_jffs_file++;
+ if (!(node = jffs_alloc_node())) {
+ kfree(root);
+ no_jffs_file--;
+ return -ENOMEM;
+ }
+ DJM(no_jffs_node++);
+ memset(node, 0, sizeof(struct jffs_node));
+ node->ino = JFFS_MIN_INO;
+ memset(root, 0, sizeof(struct jffs_file));
+ root->ino = JFFS_MIN_INO;
+ root->mode = S_IFDIR | S_IRWXU | S_IRGRP
+ | S_IXGRP | S_IROTH | S_IXOTH;
+ root->atime = root->mtime = root->ctime = CURRENT_TIME;
+ root->nlink = 1;
+ root->c = c;
+ root->version_head = root->version_tail = node;
+ jffs_insert_file_into_hash(root);
+ return 0;
+}
+
+
+/* This is where the file system is built and initialized. */
+int
+jffs_build_fs(struct super_block *sb)
+{
+ struct jffs_control *c;
+ int err = 0;
+
+ D2(printk("jffs_build_fs()\n"));
+
+ if (!(c = jffs_create_control(sb->s_dev))) {
+ return -ENOMEM;
+ }
+ c->building_fs = 1;
+ c->sb = sb;
+ if ((err = jffs_scan_flash(c)) < 0) {
+ if(err == -EAGAIN){
+ /* scan_flash() wants us to try once more. A flipping
+ bits sector was detect in the middle of the scan flash.
+ Clean up old allocated memory before going in.
+ */
+ D1(printk("jffs_build_fs: Cleaning up all control structures,"
+ " reallocating them and trying mount again.\n"));
+ jffs_cleanup_control(c);
+ if (!(c = jffs_create_control(sb->s_dev))) {
+ return -ENOMEM;
+ }
+ c->building_fs = 1;
+ c->sb = sb;
+
+ if ((err = jffs_scan_flash(c)) < 0) {
+ goto jffs_build_fs_fail;
+ }
+ }else{
+ goto jffs_build_fs_fail;
+ }
+ }
+
+ /* Add a virtual root node if no one exists. */
+ if (!jffs_find_file(c, JFFS_MIN_INO)) {
+ if ((err = jffs_add_virtual_root(c)) < 0) {
+ goto jffs_build_fs_fail;
+ }
+ }
+
+ while (c->delete_list) {
+ struct jffs_file *f;
+ struct jffs_delete_list *delete_list_element;
+
+ if ((f = jffs_find_file(c, c->delete_list->ino))) {
+ f->deleted = 1;
+ }
+ delete_list_element = c->delete_list;
+ c->delete_list = c->delete_list->next;
+ kfree(delete_list_element);
+ }
+
+ /* Remove deleted nodes. */
+ if ((err = jffs_foreach_file(c, jffs_possibly_delete_file)) < 0) {
+ printk(KERN_ERR "JFFS: Failed to remove deleted nodes.\n");
+ goto jffs_build_fs_fail;
+ }
+ /* Remove redundant nodes. (We are not interested in the
+ return value in this case.) */
+ jffs_foreach_file(c, jffs_remove_redundant_nodes);
+ /* Try to build a tree from all the nodes. */
+ if ((err = jffs_foreach_file(c, jffs_insert_file_into_tree)) < 0) {
+ printk("JFFS: Failed to build tree.\n");
+ goto jffs_build_fs_fail;
+ }
+ /* Compute the sizes of all files in the filesystem. Adjust if
+ necessary. */
+ if ((err = jffs_foreach_file(c, jffs_build_file)) < 0) {
+ printk("JFFS: Failed to build file system.\n");
+ goto jffs_build_fs_fail;
+ }
+ sb->u.generic_sbp = (void *)c;
+ c->building_fs = 0;
+
+ D1(jffs_print_hash_table(c));
+ D1(jffs_print_tree(c->root, 0));
+
+ return 0;
+
+jffs_build_fs_fail:
+ jffs_cleanup_control(c);
+ return err;
+} /* jffs_build_fs() */
+
+
+/*
+ This checks for sectors that were being erased in their previous
+ lifetimes and for some reason or the other (power fail etc.),
+ the erase cycles never completed.
+ As the flash array would have reverted back to read status,
+ these sectors are detected by the symptom of the "flipping bits",
+ i.e. bits being read back differently from the same location in
+ flash if read multiple times.
+ The only solution to this is to re-erase the entire
+ sector.
+ Unfortunately detecting "flipping bits" is not a simple exercise
+ as a bit may be read back at 1 or 0 depending on the alignment
+ of the stars in the universe.
+ The level of confidence is in direct proportion to the number of
+ scans done. By power fail testing I (Vipin) have been able to
+ proove that reading twice is not enough.
+ Maybe 4 times? Change NUM_REREADS to a higher number if you want
+ a (even) higher degree of confidence in your mount process.
+ A higher number would of course slow down your mount.
+*/
+int check_partly_erased_sectors(struct jffs_fmcontrol *fmc){
+
+#define NUM_REREADS 4 /* see note above */
+#define READ_AHEAD_BYTES 4096 /* must be a multiple of 4,
+ usually set to kernel page size */
+
+ __u8 *read_buf1;
+ __u8 *read_buf2;
+
+ int err = 0;
+ int retlen;
+ int i;
+ int cnt;
+ __u32 offset;
+ loff_t pos = 0;
+ loff_t end = fmc->flash_size;
+
+
+ /* Allocate read buffers */
+ read_buf1 = (__u8 *) kmalloc (sizeof(__u8) * READ_AHEAD_BYTES, GFP_KERNEL);
+ if (!read_buf1)
+ return -ENOMEM;
+
+ read_buf2 = (__u8 *) kmalloc (sizeof(__u8) * READ_AHEAD_BYTES, GFP_KERNEL);
+ if (!read_buf2) {
+ kfree(read_buf1);
+ return -ENOMEM;
+ }
+
+ CHECK_NEXT:
+ while(pos < end){
+
+ D1(printk("check_partly_erased_sector():checking sector which contains"
+ " offset 0x%x for flipping bits..\n", (__u32)pos));
+
+ retlen = flash_safe_read(fmc->mtd, pos,
+ &read_buf1[0], READ_AHEAD_BYTES);
+ retlen &= ~3;
+
+ for(cnt = 0; cnt < NUM_REREADS; cnt++){
+ (void)flash_safe_read(fmc->mtd, pos,
+ &read_buf2[0], READ_AHEAD_BYTES);
+
+ for (i=0 ; i < retlen ; i+=4) {
+ /* buffers MUST match, double word for word! */
+ if(*((__u32 *) &read_buf1[i]) !=
+ *((__u32 *) &read_buf2[i])
+ ){
+ /* flipping bits detected, time to erase sector */
+ /* This will help us log some statistics etc. */
+ D1(printk("Flipping bits detected in re-read round:%i of %i\n",
+ cnt, NUM_REREADS));
+ D1(printk("check_partly_erased_sectors:flipping bits detected"
+ " @offset:0x%x(0x%x!=0x%x)\n",
+ (__u32)pos+i, *((__u32 *) &read_buf1[i]),
+ *((__u32 *) &read_buf2[i])));
+
+ /* calculate start of present sector */
+ offset = (((__u32)pos+i)/(__u32)fmc->sector_size) * (__u32)fmc->sector_size;
+
+ D1(printk("check_partly_erased_sector():erasing sector starting 0x%x.\n",
+ offset));
+
+ if (flash_erase_region(fmc->mtd,
+ offset, fmc->sector_size) < 0) {
+ printk(KERN_ERR "JFFS: Erase of flash failed. "
+ "offset = %u, erase_size = %d\n",
+ offset , fmc->sector_size);
+
+ err = -EIO;
+ goto returnBack;
+
+ }else{
+ D1(printk("JFFS: Erase of flash sector @0x%x successful.\n",
+ offset));
+ /* skip ahead to the next sector */
+ pos = (((__u32)pos+i)/(__u32)fmc->sector_size) * (__u32)fmc->sector_size;
+ pos += fmc->sector_size;
+ goto CHECK_NEXT;
+ }
+ }
+ }
+ }
+ pos += READ_AHEAD_BYTES;
+ }
+
+ returnBack:
+ kfree(read_buf1);
+ kfree(read_buf2);
+
+ D2(printk("check_partly_erased_sector():Done checking all sectors till offset 0x%x for flipping bits.\n",
+ (__u32)pos));
+
+ return err;
+
+}/* end check_partly_erased_sectors() */
+
+
+
+/* Scan the whole flash memory in order to find all nodes in the
+ file systems. */
+static int
+jffs_scan_flash(struct jffs_control *c)
+{
+ char name[JFFS_MAX_NAME_LEN + 2];
+ struct jffs_raw_inode raw_inode;
+ struct jffs_node *node = 0;
+ struct jffs_fmcontrol *fmc = c->fmc;
+ __u32 checksum;
+ __u8 tmp_accurate;
+ __u16 tmp_chksum;
+ __u32 deleted_file;
+ loff_t pos = 0;
+ loff_t start;
+ loff_t test_start;
+ loff_t end = fmc->flash_size;
+ __u8 *read_buf;
+ int i, len, retlen;
+ __u32 offset;
+
+ __u32 free_chunk_size1;
+ __u32 free_chunk_size2;
+
+
+#define NUMFREEALLOWED 2 /* 2 chunks of at least erase size space allowed */
+ int num_free_space = 0; /* Flag err if more than TWO
+ free blocks found. This is NOT allowed
+ by the current jffs design.
+ */
+ int num_free_spc_not_accp = 0; /* For debugging purposed keep count
+ of how much free space was rejected and
+ marked dirty
+ */
+
+ D1(printk("jffs_scan_flash(): start pos = 0x%lx, end = 0x%lx\n",
+ (long)pos, (long)end));
+
+ flash_safe_acquire(fmc->mtd);
+
+ /*
+ check and make sure that any sector does not suffer
+ from the "partly erased, bit flipping syndrome" (TM Vipin :)
+ If so, offending sectors will be erased.
+ */
+ if(check_partly_erased_sectors(fmc) < 0){
+
+ flash_safe_release(fmc->mtd);
+ return -EIO; /* bad, bad, bad error. Cannot continue.*/
+ }
+
+ /* Allocate read buffer */
+ read_buf = (__u8 *) kmalloc (sizeof(__u8) * 4096, GFP_KERNEL);
+ if (!read_buf) {
+ flash_safe_release(fmc->mtd);
+ return -ENOMEM;
+ }
+
+ /* Start the scan. */
+ while (pos < end) {
+ deleted_file = 0;
+
+ /* Remember the position from where we started this scan. */
+ start = pos;
+
+ switch (flash_read_u32(fmc->mtd, pos)) {
+ case JFFS_EMPTY_BITMASK:
+ /* We have found 0xffffffff at this position. We have to
+ scan the rest of the flash till the end or till
+ something else than 0xffffffff is found.
+ Keep going till we do not find JFFS_EMPTY_BITMASK
+ anymore */
+
+ D1(printk("jffs_scan_flash(): 0xffffffff at pos 0x%lx.\n",
+ (long)pos));
+
+ while(pos < end){
+
+ len = end - pos < 4096 ? end - pos : 4096;
+
+ retlen = flash_safe_read(fmc->mtd, pos,
+ &read_buf[0], len);
+
+ retlen &= ~3;
+
+ for (i=0 ; i < retlen ; i+=4, pos += 4) {
+ if(*((__u32 *) &read_buf[i]) !=
+ JFFS_EMPTY_BITMASK)
+ break;
+ }
+ if (i == retlen)
+ continue;
+ else
+ break;
+ }
+
+ D1(printk("jffs_scan_flash():0xffffffff ended at pos 0x%lx.\n",
+ (long)pos));
+
+ /* If some free space ends in the middle of a sector,
+ treat it as dirty rather than clean.
+ This is to handle the case where one thread
+ allocated space for a node, but didn't get to
+ actually _write_ it before power was lost, leaving
+ a gap in the log. Shifting all node writes into
+ a single kernel thread will fix the original problem.
+ */
+ if ((__u32) pos % fmc->sector_size) {
+ /* If there was free space in previous
+ sectors, don't mark that dirty too -
+ only from the beginning of this sector
+ (or from start)
+ */
+
+ test_start = pos & ~(fmc->sector_size-1); /* end of last sector */
+
+ if (start < test_start) {
+
+ /* free space started in the previous sector! */
+
+ if((num_free_space < NUMFREEALLOWED) &&
+ ((unsigned int)(test_start - start) >= fmc->sector_size)){
+
+ /*
+ Count it in if we are still under NUMFREEALLOWED *and* it is
+ at least 1 erase sector in length. This will keep us from
+ picking any little ole' space as "free".
+ */
+
+ D1(printk("Reducing end of free space to 0x%x from 0x%x\n",
+ (unsigned int)test_start, (unsigned int)pos));
+
+ D1(printk("Free space accepted: Starting 0x%x for 0x%x bytes\n",
+ (unsigned int) start,
+ (unsigned int)(test_start - start)));
+
+ /* below, space from "start" to "pos" will be marked dirty. */
+ start = test_start;
+
+ /* Being in here means that we have found at least an entire
+ erase sector size of free space ending on a sector boundary.
+ Keep track of free spaces accepted.
+ */
+ num_free_space++;
+ }else{
+ num_free_spc_not_accp++;
+ D1(printk("Free space (#%i) found but *Not* accepted: Starting"
+ " 0x%x for 0x%x bytes\n",
+ num_free_spc_not_accp, (unsigned int)start,
+ (unsigned int)((unsigned int)(pos & ~(fmc->sector_size-1)) - (unsigned int)start)));
+
+ }
+
+ }
+ if((((__u32)(pos - start)) != 0)){
+
+ D1(printk("Dirty space: Starting 0x%x for 0x%x bytes\n",
+ (unsigned int) start, (unsigned int) (pos - start)));
+ jffs_fmalloced(fmc, (__u32) start,
+ (__u32) (pos - start), 0);
+ }else{
+ /* "Flipping bits" detected. This means that our scan for them
+ did not catch this offset. See check_partly_erased_sectors() for
+ more info.
+ */
+
+ D1(printk("jffs_scan_flash():wants to allocate dirty flash "
+ "space for 0 bytes.\n"));
+ D1(printk("jffs_scan_flash(): Flipping bits! We will free "
+ "all allocated memory, erase this sector and remount\n"));
+
+ /* calculate start of present sector */
+ offset = (((__u32)pos)/(__u32)fmc->sector_size) * (__u32)fmc->sector_size;
+
+ D1(printk("jffs_scan_flash():erasing sector starting 0x%x.\n",
+ offset));
+
+ if (flash_erase_region(fmc->mtd,
+ offset, fmc->sector_size) < 0) {
+ printk(KERN_ERR "JFFS: Erase of flash failed. "
+ "offset = %u, erase_size = %d\n",
+ offset , fmc->sector_size);
+
+ flash_safe_release(fmc->mtd);
+ kfree (read_buf);
+ return -1; /* bad, bad, bad! */
+
+ }
+ flash_safe_release(fmc->mtd);
+ kfree (read_buf);
+
+ return -EAGAIN; /* erased offending sector. Try mount one more time please. */
+ }
+ }else{
+ /* Being in here means that we have found free space that ends on an erase sector
+ boundary.
+ Count it in if we are still under NUMFREEALLOWED *and* it is at least 1 erase
+ sector in length. This will keep us from picking any little ole' space as "free".
+ */
+ if((num_free_space < NUMFREEALLOWED) &&
+ ((unsigned int)(pos - start) >= fmc->sector_size)){
+ /* We really don't do anything to mark space as free, except *not*
+ mark it dirty and just advance the "pos" location pointer.
+ It will automatically be picked up as free space.
+ */
+ num_free_space++;
+ D1(printk("Free space accepted: Starting 0x%x for 0x%x bytes\n",
+ (unsigned int) start, (unsigned int) (pos - start)));
+ }else{
+ num_free_spc_not_accp++;
+ D1(printk("Free space (#%i) found but *Not* accepted: Starting "
+ "0x%x for 0x%x bytes\n", num_free_spc_not_accp,
+ (unsigned int) start,
+ (unsigned int) (pos - start)));
+
+ /* Mark this space as dirty. We already have our free space. */
+ D1(printk("Dirty space: Starting 0x%x for 0x%x bytes\n",
+ (unsigned int) start, (unsigned int) (pos - start)));
+ jffs_fmalloced(fmc, (__u32) start,
+ (__u32) (pos - start), 0);
+ }
+
+ }
+ if(num_free_space > NUMFREEALLOWED){
+ printk(KERN_WARNING "jffs_scan_flash(): Found free space "
+ "number %i. Only %i free space is allowed.\n",
+ num_free_space, NUMFREEALLOWED);
+ }
+ continue;
+
+ case JFFS_DIRTY_BITMASK:
+ /* We have found 0x00000000 at this position. Scan as far
+ as possible to find out how much is dirty. */
+ D1(printk("jffs_scan_flash(): 0x00000000 at pos 0x%lx.\n",
+ (long)pos));
+ for (; pos < end
+ && JFFS_DIRTY_BITMASK == flash_read_u32(fmc->mtd, pos);
+ pos += 4);
+ D1(printk("jffs_scan_flash(): 0x00 ended at "
+ "pos 0x%lx.\n", (long)pos));
+ jffs_fmalloced(fmc, (__u32) start,
+ (__u32) (pos - start), 0);
+ continue;
+
+ case JFFS_MAGIC_BITMASK:
+ /* We have probably found a new raw inode. */
+ break;
+
+ default:
+ bad_inode:
+ /* We're f*cked. This is not solved yet. We have
+ to scan for the magic pattern. */
+ D1(printk("*************** Dirty flash memory or "
+ "bad inode: "
+ "hexdump(pos = 0x%lx, len = 128):\n",
+ (long)pos));
+ D1(jffs_hexdump(fmc->mtd, pos, 128));
+
+ for (pos += 4; pos < end; pos += 4) {
+ switch (flash_read_u32(fmc->mtd, pos)) {
+ case JFFS_MAGIC_BITMASK:
+ case JFFS_EMPTY_BITMASK:
+ /* handle these in the main switch() loop */
+ goto cont_scan;
+
+ default:
+ break;
+ }
+ }
+
+ cont_scan:
+ /* First, mark as dirty the region
+ which really does contain crap. */
+ jffs_fmalloced(fmc, (__u32) start,
+ (__u32) (pos - start),
+ 0);
+
+ continue;
+ }/* switch */
+
+ /* We have found the beginning of an inode. Create a
+ node for it unless there already is one available. */
+ if (!node) {
+ if (!(node = jffs_alloc_node())) {
+ /* Free read buffer */
+ kfree (read_buf);
+
+ /* Release the flash device */
+ flash_safe_release(fmc->mtd);
+
+ return -ENOMEM;
+ }
+ DJM(no_jffs_node++);
+ }
+
+ /* Read the next raw inode. */
+
+ flash_safe_read(fmc->mtd, pos, (u_char *) &raw_inode,
+ sizeof(struct jffs_raw_inode));
+
+ /* When we compute the checksum for the inode, we never
+ count the 'accurate' or the 'checksum' fields. */
+ tmp_accurate = raw_inode.accurate;
+ tmp_chksum = raw_inode.chksum;
+ raw_inode.accurate = 0;
+ raw_inode.chksum = 0;
+ checksum = jffs_checksum(&raw_inode,
+ sizeof(struct jffs_raw_inode));
+ raw_inode.accurate = tmp_accurate;
+ raw_inode.chksum = tmp_chksum;
+
+ D3(printk("*** We have found this raw inode at pos 0x%lx "
+ "on the flash:\n", (long)pos));
+ D3(jffs_print_raw_inode(&raw_inode));
+
+ if (checksum != raw_inode.chksum) {
+ D1(printk("jffs_scan_flash(): Bad checksum: "
+ "checksum = %u, "
+ "raw_inode.chksum = %u\n",
+ checksum, raw_inode.chksum));
+ pos += sizeof(struct jffs_raw_inode);
+ jffs_fmalloced(fmc, (__u32) start,
+ (__u32) (pos - start), 0);
+ /* Reuse this unused struct jffs_node. */
+ continue;
+ }
+
+ /* Check the raw inode read so far. Start with the
+ maximum length of the filename. */
+ if (raw_inode.nsize > JFFS_MAX_NAME_LEN) {
+ printk(KERN_WARNING "jffs_scan_flash: Found a "
+ "JFFS node with name too large\n");
+ goto bad_inode;
+ }
+
+ if (raw_inode.rename && raw_inode.dsize != sizeof(__u32)) {
+ printk(KERN_WARNING "jffs_scan_flash: Found a "
+ "rename node with dsize %u.\n",
+ raw_inode.dsize);
+ jffs_print_raw_inode(&raw_inode);
+ goto bad_inode;
+ }
+
+ /* The node's data segment should not exceed a
+ certain length. */
+ if (raw_inode.dsize > fmc->max_chunk_size) {
+ printk(KERN_WARNING "jffs_scan_flash: Found a "
+ "JFFS node with dsize (0x%x) > max_chunk_size (0x%x)\n",
+ raw_inode.dsize, fmc->max_chunk_size);
+ goto bad_inode;
+ }
+
+ pos += sizeof(struct jffs_raw_inode);
+
+ /* This shouldn't be necessary because a node that
+ violates the flash boundaries shouldn't be written
+ in the first place. */
+ if (pos >= end) {
+ goto check_node;
+ }
+
+ /* Read the name. */
+ *name = 0;
+ if (raw_inode.nsize) {
+ flash_safe_read(fmc->mtd, pos, name, raw_inode.nsize);
+ name[raw_inode.nsize] = '\0';
+ pos += raw_inode.nsize
+ + JFFS_GET_PAD_BYTES(raw_inode.nsize);
+ D3(printk("name == \"%s\"\n", name));
+ checksum = jffs_checksum(name, raw_inode.nsize);
+ if (checksum != raw_inode.nchksum) {
+ D1(printk("jffs_scan_flash(): Bad checksum: "
+ "checksum = %u, "
+ "raw_inode.nchksum = %u\n",
+ checksum, raw_inode.nchksum));
+ jffs_fmalloced(fmc, (__u32) start,
+ (__u32) (pos - start), 0);
+ /* Reuse this unused struct jffs_node. */
+ continue;
+ }
+ if (pos >= end) {
+ goto check_node;
+ }
+ }
+
+ /* Read the data, if it exists, in order to be sure it
+ matches the checksum. */
+ if (raw_inode.dsize) {
+ if (raw_inode.rename) {
+ deleted_file = flash_read_u32(fmc->mtd, pos);
+ }
+ if (jffs_checksum_flash(fmc->mtd, pos, raw_inode.dsize, &checksum)) {
+ printk("jffs_checksum_flash() failed to calculate a checksum\n");
+ jffs_fmalloced(fmc, (__u32) start,
+ (__u32) (pos - start), 0);
+ /* Reuse this unused struct jffs_node. */
+ continue;
+ }
+ pos += raw_inode.dsize
+ + JFFS_GET_PAD_BYTES(raw_inode.dsize);
+
+ if (checksum != raw_inode.dchksum) {
+ D1(printk("jffs_scan_flash(): Bad checksum: "
+ "checksum = %u, "
+ "raw_inode.dchksum = %u\n",
+ checksum, raw_inode.dchksum));
+ jffs_fmalloced(fmc, (__u32) start,
+ (__u32) (pos - start), 0);
+ /* Reuse this unused struct jffs_node. */
+ continue;
+ }
+ }
+
+ check_node:
+
+ /* Remember the highest inode number in the whole file
+ system. This information will be used when assigning
+ new files new inode numbers. */
+ if (c->next_ino <= raw_inode.ino) {
+ c->next_ino = raw_inode.ino + 1;
+ }
+
+ if (raw_inode.accurate) {
+ int err;
+ node->data_offset = raw_inode.offset;
+ node->data_size = raw_inode.dsize;
+ node->removed_size = raw_inode.rsize;
+ /* Compute the offset to the actual data in the
+ on-flash node. */
+ node->fm_offset
+ = sizeof(struct jffs_raw_inode)
+ + raw_inode.nsize
+ + JFFS_GET_PAD_BYTES(raw_inode.nsize);
+ node->fm = jffs_fmalloced(fmc, (__u32) start,
+ (__u32) (pos - start),
+ node);
+ if (!node->fm) {
+ D(printk("jffs_scan_flash(): !node->fm\n"));
+ jffs_free_node(node);
+ DJM(no_jffs_node--);
+
+ /* Free read buffer */
+ kfree (read_buf);
+
+ /* Release the flash device */
+ flash_safe_release(fmc->mtd);
+
+ return -ENOMEM;
+ }
+ if ((err = jffs_insert_node(c, 0, &raw_inode,
+ name, node)) < 0) {
+ printk("JFFS: Failed to handle raw inode. "
+ "(err = %d)\n", err);
+ break;
+ }
+ if (raw_inode.rename) {
+ struct jffs_delete_list *dl
+ = (struct jffs_delete_list *)
+ kmalloc(sizeof(struct jffs_delete_list),
+ GFP_KERNEL);
+ if (!dl) {
+ D(printk("jffs_scan_flash: !dl\n"));
+ jffs_free_node(node);
+ DJM(no_jffs_node--);
+
+ /* Release the flash device */
+ flash_safe_release(fmc->flash_part);
+
+ /* Free read buffer */
+ kfree (read_buf);
+
+ return -ENOMEM;
+ }
+ dl->ino = deleted_file;
+ dl->next = c->delete_list;
+ c->delete_list = dl;
+ node->data_size = 0;
+ }
+ D3(jffs_print_node(node));
+ node = 0; /* Don't free the node! */
+ }
+ else {
+ jffs_fmalloced(fmc, (__u32) start,
+ (__u32) (pos - start), 0);
+ D3(printk("jffs_scan_flash(): Just found an obsolete "
+ "raw_inode. Continuing the scan...\n"));
+ /* Reuse this unused struct jffs_node. */
+ }
+ }
+
+ if (node) {
+ jffs_free_node(node);
+ DJM(no_jffs_node--);
+ }
+ jffs_build_end(fmc);
+
+ /* Free read buffer */
+ kfree (read_buf);
+
+ if(!num_free_space){
+ printk(KERN_WARNING "jffs_scan_flash(): Did not find even a single "
+ "chunk of free space. This is BAD!\n");
+ }
+
+ /* Return happy */
+ D3(printk("jffs_scan_flash(): Leaving...\n"));
+ flash_safe_release(fmc->mtd);
+
+ /* This is to trap the "free size accounting screwed error. */
+ free_chunk_size1 = jffs_free_size1(fmc);
+ free_chunk_size2 = jffs_free_size2(fmc);
+
+ if (free_chunk_size1 + free_chunk_size2 != fmc->free_size) {
+
+ printk(KERN_WARNING "jffs_scan_falsh():Free size accounting screwed\n");
+ printk(KERN_WARNING "jfffs_scan_flash():free_chunk_size1 == 0x%x, "
+ "free_chunk_size2 == 0x%x, fmc->free_size == 0x%x\n",
+ free_chunk_size1, free_chunk_size2, fmc->free_size);
+
+ return -1; /* Do NOT mount f/s so that we can inspect what happened.
+ Mounting this screwed up f/s will screw us up anyway.
+ */
+ }
+
+ return 0; /* as far as we are concerned, we are happy! */
+} /* jffs_scan_flash() */
+
+
+/* Insert any kind of node into the file system. Take care of data
+ insertions and deletions. Also remove redundant information. The
+ memory allocated for the `name' is regarded as "given away" in the
+ caller's perspective. */
+int
+jffs_insert_node(struct jffs_control *c, struct jffs_file *f,
+ const struct jffs_raw_inode *raw_inode,
+ const char *name, struct jffs_node *node)
+{
+ int update_name = 0;
+ int insert_into_tree = 0;
+
+ D2(printk("jffs_insert_node(): ino = %u, version = %u, "
+ "name = \"%s\", deleted = %d\n",
+ raw_inode->ino, raw_inode->version,
+ ((name && *name) ? name : ""), raw_inode->deleted));
+
+ /* If there doesn't exist an associated jffs_file, then
+ create, initialize and insert one into the file system. */
+ if (!f && !(f = jffs_find_file(c, raw_inode->ino))) {
+ if (!(f = jffs_create_file(c, raw_inode))) {
+ return -ENOMEM;
+ }
+ jffs_insert_file_into_hash(f);
+ insert_into_tree = 1;
+ }
+ node->ino = raw_inode->ino;
+ node->version = raw_inode->version;
+ node->data_size = raw_inode->dsize;
+ node->fm_offset = sizeof(struct jffs_raw_inode) + raw_inode->nsize
+ + JFFS_GET_PAD_BYTES(raw_inode->nsize);
+ node->name_size = raw_inode->nsize;
+
+ /* Now insert the node at the correct position into the file's
+ version list. */
+ if (!f->version_head) {
+ /* This is the first node. */
+ f->version_head = node;
+ f->version_tail = node;
+ node->version_prev = 0;
+ node->version_next = 0;
+ f->highest_version = node->version;
+ update_name = 1;
+ f->mode = raw_inode->mode;
+ f->uid = raw_inode->uid;
+ f->gid = raw_inode->gid;
+ f->atime = raw_inode->atime;
+ f->mtime = raw_inode->mtime;
+ f->ctime = raw_inode->ctime;
+ }
+ else if ((f->highest_version < node->version)
+ || (node->version == 0)) {
+ /* Insert at the end of the list. I.e. this node is the
+ newest one so far. */
+ node->version_prev = f->version_tail;
+ node->version_next = 0;
+ f->version_tail->version_next = node;
+ f->version_tail = node;
+ f->highest_version = node->version;
+ update_name = 1;
+ f->pino = raw_inode->pino;
+ f->mode = raw_inode->mode;
+ f->uid = raw_inode->uid;
+ f->gid = raw_inode->gid;
+ f->atime = raw_inode->atime;
+ f->mtime = raw_inode->mtime;
+ f->ctime = raw_inode->ctime;
+ }
+ else if (f->version_head->version > node->version) {
+ /* Insert at the bottom of the list. */
+ node->version_prev = 0;
+ node->version_next = f->version_head;
+ f->version_head->version_prev = node;
+ f->version_head = node;
+ if (!f->name) {
+ update_name = 1;
+ }
+ }
+ else {
+ struct jffs_node *n;
+ int newer_name = 0;
+ /* Search for the insertion position starting from
+ the tail (newest node). */
+ for (n = f->version_tail; n; n = n->version_prev) {
+ if (n->version < node->version) {
+ node->version_prev = n;
+ node->version_next = n->version_next;
+ node->version_next->version_prev = node;
+ n->version_next = node;
+ if (!newer_name) {
+ update_name = 1;
+ }
+ break;
+ }
+ if (n->name_size) {
+ newer_name = 1;
+ }
+ }
+ }
+
+ /* Deletion is irreversible. If any 'deleted' node is ever
+ written, the file is deleted */
+ if (raw_inode->deleted)
+ f->deleted = raw_inode->deleted;
+
+ /* Perhaps update the name. */
+ if (raw_inode->nsize && update_name && name && *name && (name != f->name)) {
+ if (f->name) {
+ kfree(f->name);
+ DJM(no_name--);
+ }
+ if (!(f->name = (char *) kmalloc(raw_inode->nsize + 1,
+ GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+ DJM(no_name++);
+ memcpy(f->name, name, raw_inode->nsize);
+ f->name[raw_inode->nsize] = '\0';
+ f->nsize = raw_inode->nsize;
+ D3(printk("jffs_insert_node(): Updated the name of "
+ "the file to \"%s\".\n", name));
+ }
+
+ if (!c->building_fs) {
+ D3(printk("jffs_insert_node(): ---------------------------"
+ "------------------------------------------- 1\n"));
+ if (insert_into_tree) {
+ jffs_insert_file_into_tree(f);
+ }
+ /* Once upon a time, we would call jffs_possibly_delete_file()
+ here. That causes an oops if someone's still got the file
+ open, so now we only do it in jffs_delete_inode()
+ -- dwmw2
+ */
+ if (node->data_size || node->removed_size) {
+ jffs_update_file(f, node);
+ }
+ jffs_remove_redundant_nodes(f);
+
+ jffs_garbage_collect_trigger(c);
+
+ D3(printk("jffs_insert_node(): ---------------------------"
+ "------------------------------------------- 2\n"));
+ }
+
+ return 0;
+} /* jffs_insert_node() */
+
+
+/* Unlink a jffs_node from the version list it is in. */
+static inline void
+jffs_unlink_node_from_version_list(struct jffs_file *f,
+ struct jffs_node *node)
+{
+ if (node->version_prev) {
+ node->version_prev->version_next = node->version_next;
+ } else {
+ f->version_head = node->version_next;
+ }
+ if (node->version_next) {
+ node->version_next->version_prev = node->version_prev;
+ } else {
+ f->version_tail = node->version_prev;
+ }
+}
+
+
+/* Unlink a jffs_node from the range list it is in. */
+static inline void
+jffs_unlink_node_from_range_list(struct jffs_file *f, struct jffs_node *node)
+{
+ if (node->range_prev) {
+ node->range_prev->range_next = node->range_next;
+ }
+ else {
+ f->range_head = node->range_next;
+ }
+ if (node->range_next) {
+ node->range_next->range_prev = node->range_prev;
+ }
+ else {
+ f->range_tail = node->range_prev;
+ }
+}
+
+
+/* Function used by jffs_remove_redundant_nodes() below. This function
+ classifies what kind of information a node adds to a file. */
+static inline __u8
+jffs_classify_node(struct jffs_node *node)
+{
+ __u8 mod_type = JFFS_MODIFY_INODE;
+
+ if (node->name_size) {
+ mod_type |= JFFS_MODIFY_NAME;
+ }
+ if (node->data_size || node->removed_size) {
+ mod_type |= JFFS_MODIFY_DATA;
+ }
+ return mod_type;
+}
+
+
+/* Remove redundant nodes from a file. Mark the on-flash memory
+ as dirty. */
+int
+jffs_remove_redundant_nodes(struct jffs_file *f)
+{
+ struct jffs_node *newest_node;
+ struct jffs_node *cur;
+ struct jffs_node *prev;
+ __u8 newest_type;
+ __u8 mod_type;
+ __u8 node_with_name_later = 0;
+
+ if (!(newest_node = f->version_tail)) {
+ return 0;
+ }
+
+ /* What does the `newest_node' modify? */
+ newest_type = jffs_classify_node(newest_node);
+ node_with_name_later = newest_type & JFFS_MODIFY_NAME;
+
+ D3(printk("jffs_remove_redundant_nodes(): ino: %u, name: \"%s\", "
+ "newest_type: %u\n", f->ino, (f->name ? f->name : ""),
+ newest_type));
+
+ /* Traverse the file's nodes and determine which of them that are
+ superfluous. Yeah, this might look very complex at first
+ glance but it is actually very simple. */
+ for (cur = newest_node->version_prev; cur; cur = prev) {
+ prev = cur->version_prev;
+ mod_type = jffs_classify_node(cur);
+ if ((mod_type <= JFFS_MODIFY_INODE)
+ || ((newest_type & JFFS_MODIFY_NAME)
+ && (mod_type
+ <= (JFFS_MODIFY_INODE + JFFS_MODIFY_NAME)))
+ || (cur->data_size == 0 && cur->removed_size
+ && !cur->version_prev && node_with_name_later)) {
+ /* Yes, this node is redundant. Remove it. */
+ D2(printk("jffs_remove_redundant_nodes(): "
+ "Removing node: ino: %u, version: %u, "
+ "mod_type: %u\n", cur->ino, cur->version,
+ mod_type));
+ jffs_unlink_node_from_version_list(f, cur);
+ jffs_fmfree(f->c->fmc, cur->fm, cur);
+ jffs_free_node(cur);
+ DJM(no_jffs_node--);
+ }
+ else {
+ node_with_name_later |= (mod_type & JFFS_MODIFY_NAME);
+ }
+ }
+
+ return 0;
+}
+
+
+/* Insert a file into the hash table. */
+int
+jffs_insert_file_into_hash(struct jffs_file *f)
+{
+ int i = f->ino % f->c->hash_len;
+
+ D3(printk("jffs_insert_file_into_hash(): f->ino: %u\n", f->ino));
+
+ list_add(&f->hash, &f->c->hash[i]);
+ return 0;
+}
+
+
+/* Insert a file into the file system tree. */
+int
+jffs_insert_file_into_tree(struct jffs_file *f)
+{
+ struct jffs_file *parent;
+
+ D3(printk("jffs_insert_file_into_tree(): name: \"%s\"\n",
+ (f->name ? f->name : "")));
+
+ if (!(parent = jffs_find_file(f->c, f->pino))) {
+ if (f->pino == 0) {
+ f->c->root = f;
+ f->parent = 0;
+ f->sibling_prev = 0;
+ f->sibling_next = 0;
+ return 0;
+ }
+ else {
+ D1(printk("jffs_insert_file_into_tree(): Found "
+ "inode with no parent and pino == %u\n",
+ f->pino));
+ return -1;
+ }
+ }
+ f->parent = parent;
+ f->sibling_next = parent->children;
+ if (f->sibling_next) {
+ f->sibling_next->sibling_prev = f;
+ }
+ f->sibling_prev = 0;
+ parent->children = f;
+ return 0;
+}
+
+
+/* Remove a file from the hash table. */
+int
+jffs_unlink_file_from_hash(struct jffs_file *f)
+{
+ D3(printk("jffs_unlink_file_from_hash(): f: 0x%p, "
+ "ino %u\n", f, f->ino));
+
+ list_del(&f->hash);
+ return 0;
+}
+
+
+/* Just remove the file from the parent's children. Don't free
+ any memory. */
+int
+jffs_unlink_file_from_tree(struct jffs_file *f)
+{
+ D3(printk("jffs_unlink_file_from_tree(): ino: %d, pino: %d, name: "
+ "\"%s\"\n", f->ino, f->pino, (f->name ? f->name : "")));
+
+ if (f->sibling_prev) {
+ f->sibling_prev->sibling_next = f->sibling_next;
+ }
+ else if (f->parent) {
+ D3(printk("f->parent=%p\n", f->parent));
+ f->parent->children = f->sibling_next;
+ }
+ if (f->sibling_next) {
+ f->sibling_next->sibling_prev = f->sibling_prev;
+ }
+ return 0;
+}
+
+
+/* Find a file with its inode number. */
+struct jffs_file *
+jffs_find_file(struct jffs_control *c, __u32 ino)
+{
+ struct jffs_file *f;
+ int i = ino % c->hash_len;
+ struct list_head *tmp;
+
+ D3(printk("jffs_find_file(): ino: %u\n", ino));
+
+ for (tmp = c->hash[i].next; tmp != &c->hash[i]; tmp = tmp->next) {
+ f = list_entry(tmp, struct jffs_file, hash);
+ if (ino != f->ino)
+ continue;
+ D3(printk("jffs_find_file(): Found file with ino "
+ "%u. (name: \"%s\")\n",
+ ino, (f->name ? f->name : ""));
+ );
+ return f;
+ }
+ D3(printk("jffs_find_file(): Didn't find file "
+ "with ino %u.\n", ino);
+ );
+ return NULL;
+}
+
+
+/* Find a file in a directory. We are comparing the names. */
+struct jffs_file *
+jffs_find_child(struct jffs_file *dir, const char *name, int len)
+{
+ struct jffs_file *f;
+
+ D3(printk("jffs_find_child()\n"));
+
+ for (f = dir->children; f; f = f->sibling_next) {
+ if (!f->deleted && f->name
+ && !strncmp(f->name, name, len)
+ && f->name[len] == '\0') {
+ break;
+ }
+ }
+
+ D3(if (f) {
+ printk("jffs_find_child(): Found \"%s\".\n", f->name);
+ }
+ else {
+ char *copy = (char *) kmalloc(len + 1, GFP_KERNEL);
+ if (copy) {
+ memcpy(copy, name, len);
+ copy[len] = '\0';
+ }
+ printk("jffs_find_child(): Didn't find the file \"%s\".\n",
+ (copy ? copy : ""));
+ if (copy) {
+ kfree(copy);
+ }
+ });
+
+ return f;
+}
+
+
+/* Write a raw inode that takes up a certain amount of space in the flash
+ memory. At the end of the flash device, there is often space that is
+ impossible to use. At these times we want to mark this space as not
+ used. In the cases when the amount of space is greater or equal than
+ a struct jffs_raw_inode, we write a "dummy node" that takes up this
+ space. The space after the raw inode, if it exists, is left as it is.
+ Since this space after the raw inode contains JFFS_EMPTY_BITMASK bytes,
+ we can compute the checksum of it; we don't have to manipulate it any
+ further.
+
+ If the space left on the device is less than the size of a struct
+ jffs_raw_inode, this space is filled with JFFS_DIRTY_BITMASK bytes.
+ No raw inode is written this time. */
+static int
+jffs_write_dummy_node(struct jffs_control *c, struct jffs_fm *dirty_fm)
+{
+ struct jffs_fmcontrol *fmc = c->fmc;
+ int err;
+
+ D1(printk("jffs_write_dummy_node(): dirty_fm->offset = 0x%08x, "
+ "dirty_fm->size = %u\n",
+ dirty_fm->offset, dirty_fm->size));
+
+ if (dirty_fm->size >= sizeof(struct jffs_raw_inode)) {
+ struct jffs_raw_inode raw_inode;
+ memset(&raw_inode, 0, sizeof(struct jffs_raw_inode));
+ raw_inode.magic = JFFS_MAGIC_BITMASK;
+ raw_inode.dsize = dirty_fm->size
+ - sizeof(struct jffs_raw_inode);
+ raw_inode.dchksum = raw_inode.dsize * 0xff;
+ raw_inode.chksum
+ = jffs_checksum(&raw_inode, sizeof(struct jffs_raw_inode));
+
+ if ((err = flash_safe_write(fmc->mtd,
+ dirty_fm->offset,
+ (u_char *)&raw_inode,
+ sizeof(struct jffs_raw_inode)))
+ < 0) {
+ printk(KERN_ERR "JFFS: jffs_write_dummy_node: "
+ "flash_safe_write failed!\n");
+ return err;
+ }
+ }
+ else {
+ flash_safe_acquire(fmc->mtd);
+ flash_memset(fmc->mtd, dirty_fm->offset, 0, dirty_fm->size);
+ flash_safe_release(fmc->mtd);
+ }
+
+ D3(printk("jffs_write_dummy_node(): Leaving...\n"));
+ return 0;
+}
+
+
+/* Write a raw inode, possibly its name and possibly some data. */
+int
+jffs_write_node(struct jffs_control *c, struct jffs_node *node,
+ struct jffs_raw_inode *raw_inode,
+ const char *name, const unsigned char *data,
+ int recoverable,
+ struct jffs_file *f)
+{
+ struct jffs_fmcontrol *fmc = c->fmc;
+ struct jffs_fm *fm;
+ struct iovec node_iovec[4];
+ unsigned long iovec_cnt;
+
+ __u32 pos;
+ int err;
+ __u32 slack = 0;
+
+ __u32 total_name_size = raw_inode->nsize
+ + JFFS_GET_PAD_BYTES(raw_inode->nsize);
+ __u32 total_data_size = raw_inode->dsize
+ + JFFS_GET_PAD_BYTES(raw_inode->dsize);
+ __u32 total_size = sizeof(struct jffs_raw_inode)
+ + total_name_size + total_data_size;
+
+ /* If this node isn't something that will eventually let
+ GC free even more space, then don't allow it unless
+ there's at least max_chunk_size space still available
+ */
+ if (!recoverable)
+ slack = fmc->max_chunk_size;
+
+
+ /* Fire the retrorockets and shoot the fruiton torpedoes, sir! */
+
+ ASSERT(if (!node) {
+ printk("jffs_write_node(): node == NULL\n");
+ return -EINVAL;
+ });
+ ASSERT(if (raw_inode && raw_inode->nsize && !name) {
+ printk("*** jffs_write_node(): nsize = %u but name == NULL\n",
+ raw_inode->nsize);
+ return -EINVAL;
+ });
+
+ D1(printk("jffs_write_node(): filename = \"%s\", ino = %u, "
+ "total_size = %u\n",
+ (name ? name : ""), raw_inode->ino,
+ total_size));
+
+ jffs_fm_write_lock(fmc);
+
+retry:
+ fm = NULL;
+ err = 0;
+ while (!fm) {
+
+ /* Deadlocks suck. */
+ while(fmc->free_size < fmc->min_free_size + total_size + slack) {
+ jffs_fm_write_unlock(fmc);
+ if (!JFFS_ENOUGH_SPACE(c, total_size + slack))
+ return -ENOSPC;
+ jffs_fm_write_lock(fmc);
+ }
+
+ /* First try to allocate some flash memory. */
+ err = jffs_fmalloc(fmc, total_size, node, &fm);
+
+ if (err == -ENOSPC) {
+ /* Just out of space. GC and try again */
+ if (fmc->dirty_size < fmc->sector_size) {
+ D(printk("jffs_write_node(): jffs_fmalloc(0x%p, %u) "
+ "failed, no dirty space to GC\n", fmc,
+ total_size));
+ return err;
+ }
+
+ D1(printk(KERN_INFO "jffs_write_node(): Calling jffs_garbage_collect_now()\n"));
+ jffs_fm_write_unlock(fmc);
+ if ((err = jffs_garbage_collect_now(c))) {
+ D(printk("jffs_write_node(): jffs_garbage_collect_now() failed\n"));
+ return err;
+ }
+ jffs_fm_write_lock(fmc);
+ continue;
+ }
+
+ if (err < 0) {
+ jffs_fm_write_unlock(fmc);
+
+ D(printk("jffs_write_node(): jffs_fmalloc(0x%p, %u) "
+ "failed!\n", fmc, total_size));
+ return err;
+ }
+
+ if (!fm->nodes) {
+ /* The jffs_fm struct that we got is not good enough.
+ Make that space dirty and try again */
+ if ((err = jffs_write_dummy_node(c, fm)) < 0) {
+ kfree(fm);
+ DJM(no_jffs_fm--);
+ jffs_fm_write_unlock(fmc);
+ D(printk("jffs_write_node(): "
+ "jffs_write_dummy_node(): Failed!\n"));
+ return err;
+ }
+ fm = NULL;
+ }
+ } /* while(!fm) */
+ node->fm = fm;
+
+ ASSERT(if (fm->nodes == 0) {
+ printk(KERN_ERR "jffs_write_node(): fm->nodes == 0\n");
+ });
+
+ pos = node->fm->offset;
+
+ /* Increment the version number here. We can't let the caller
+ set it beforehand, because we might have had to do GC on a node
+ of this file - and we'd end up reusing version numbers.
+ */
+ if (f) {
+ raw_inode->version = f->highest_version + 1;
+ D1(printk (KERN_NOTICE "jffs_write_node(): setting version of %s to %d\n", f->name, raw_inode->version));
+
+ /* if the file was deleted, set the deleted bit in the raw inode */
+ if (f->deleted)
+ raw_inode->deleted = 1;
+ }
+
+ /* Compute the checksum for the data and name chunks. */
+ raw_inode->dchksum = jffs_checksum(data, raw_inode->dsize);
+ raw_inode->nchksum = jffs_checksum(name, raw_inode->nsize);
+
+ /* The checksum is calculated without the chksum and accurate
+ fields so set them to zero first. */
+ raw_inode->accurate = 0;
+ raw_inode->chksum = 0;
+ raw_inode->chksum = jffs_checksum(raw_inode,
+ sizeof(struct jffs_raw_inode));
+ raw_inode->accurate = 0xff;
+
+ D3(printk("jffs_write_node(): About to write this raw inode to the "
+ "flash at pos 0x%lx:\n", (long)pos));
+ D3(jffs_print_raw_inode(raw_inode));
+
+ /* The actual raw JFFS node */
+ node_iovec[0].iov_base = (void *) raw_inode;
+ node_iovec[0].iov_len = (size_t) sizeof(struct jffs_raw_inode);
+ iovec_cnt = 1;
+
+ /* Get name and size if there is one */
+ if (raw_inode->nsize) {
+ node_iovec[iovec_cnt].iov_base = (void *) name;
+ node_iovec[iovec_cnt].iov_len = (size_t) raw_inode->nsize;
+ iovec_cnt++;
+
+ if (JFFS_GET_PAD_BYTES(raw_inode->nsize)) {
+ static char allff[3]={255,255,255};
+ /* Add some extra padding if necessary */
+ node_iovec[iovec_cnt].iov_base = allff;
+ node_iovec[iovec_cnt].iov_len =
+ JFFS_GET_PAD_BYTES(raw_inode->nsize);
+ iovec_cnt++;
+ }
+ }
+
+ /* Get data and size if there is any */
+ if (raw_inode->dsize) {
+ node_iovec[iovec_cnt].iov_base = (void *) data;
+ node_iovec[iovec_cnt].iov_len = (size_t) raw_inode->dsize;
+ iovec_cnt++;
+ /* No need to pad this because we're not actually putting
+ anything after it.
+ */
+ }
+
+ if ((err = flash_safe_writev(fmc->mtd, node_iovec, iovec_cnt,
+ pos)) < 0) {
+ jffs_fmfree_partly(fmc, fm, 0);
+ jffs_fm_write_unlock(fmc);
+ printk(KERN_ERR "JFFS: jffs_write_node: Failed to write, "
+ "requested %i, wrote %i\n", total_size, err);
+ goto retry;
+ }
+ if (raw_inode->deleted)
+ f->deleted = 1;
+
+ jffs_fm_write_unlock(fmc);
+ D3(printk("jffs_write_node(): Leaving...\n"));
+ return raw_inode->dsize;
+} /* jffs_write_node() */
+
+
+/* Read data from the node and write it to the buffer. 'node_offset'
+ is how much we have read from this particular node before and which
+ shouldn't be read again. 'max_size' is how much space there is in
+ the buffer. */
+static int
+jffs_get_node_data(struct jffs_file *f, struct jffs_node *node,
+ unsigned char *buf,__u32 node_offset, __u32 max_size,
+ kdev_t dev)
+{
+ struct jffs_fmcontrol *fmc = f->c->fmc;
+ __u32 pos = node->fm->offset + node->fm_offset + node_offset;
+ __u32 avail = node->data_size - node_offset;
+ __u32 r;
+
+ D2(printk(" jffs_get_node_data(): file: \"%s\", ino: %u, "
+ "version: %u, node_offset: %u\n",
+ f->name, node->ino, node->version, node_offset));
+
+ r = min(avail, max_size);
+ D3(printk(KERN_NOTICE "jffs_get_node_data\n"));
+ flash_safe_read(fmc->mtd, pos, buf, r);
+
+ D3(printk(" jffs_get_node_data(): Read %u byte%s.\n",
+ r, (r == 1 ? "" : "s")));
+
+ return r;
+}
+
+
+/* Read data from the file's nodes. Write the data to the buffer
+ 'buf'. 'read_offset' tells how much data we should skip. */
+int
+jffs_read_data(struct jffs_file *f, unsigned char *buf, __u32 read_offset,
+ __u32 size)
+{
+ struct jffs_node *node;
+ __u32 read_data = 0; /* Total amount of read data. */
+ __u32 node_offset = 0;
+ __u32 pos = 0; /* Number of bytes traversed. */
+
+ D2(printk("jffs_read_data(): file = \"%s\", read_offset = %d, "
+ "size = %u\n",
+ (f->name ? f->name : ""), read_offset, size));
+
+ if (read_offset >= f->size) {
+ D(printk(" f->size: %d\n", f->size));
+ return 0;
+ }
+
+ /* First find the node to read data from. */
+ node = f->range_head;
+ while (pos <= read_offset) {
+ node_offset = read_offset - pos;
+ if (node_offset >= node->data_size) {
+ pos += node->data_size;
+ node = node->range_next;
+ }
+ else {
+ break;
+ }
+ }
+
+ /* "Cats are living proof that not everything in nature
+ has to be useful."
+ - Garrison Keilor ('97) */
+
+ /* Fill the buffer. */
+ while (node && (read_data < size)) {
+ int r;
+ if (!node->fm) {
+ /* This node does not refer to real data. */
+ r = min(size - read_data,
+ node->data_size - node_offset);
+ memset(&buf[read_data], 0, r);
+ }
+ else if ((r = jffs_get_node_data(f, node, &buf[read_data],
+ node_offset,
+ size - read_data,
+ f->c->sb->s_dev)) < 0) {
+ return r;
+ }
+ read_data += r;
+ node_offset = 0;
+ node = node->range_next;
+ }
+ D3(printk(" jffs_read_data(): Read %u bytes.\n", read_data));
+ return read_data;
+}
+
+
+/* Used for traversing all nodes in the hash table. */
+int
+jffs_foreach_file(struct jffs_control *c, int (*func)(struct jffs_file *))
+{
+ int pos;
+ int r;
+ int result = 0;
+
+ for (pos = 0; pos < c->hash_len; pos++) {
+ struct list_head *p, *next;
+ for (p = c->hash[pos].next; p != &c->hash[pos]; p = next) {
+ /* We need a reference to the next file in the
+ list because `func' might remove the current
+ file `f'. */
+ next = p->next;
+ r = func(list_entry(p, struct jffs_file, hash));
+ if (r < 0)
+ return r;
+ result += r;
+ }
+ }
+
+ return result;
+}
+
+
+/* Free all nodes associated with a file. */
+int
+jffs_free_node_list(struct jffs_file *f)
+{
+ struct jffs_node *node;
+ struct jffs_node *p;
+
+ D3(printk("jffs_free_node_list(): f #%u, \"%s\"\n",
+ f->ino, (f->name ? f->name : "")));
+ node = f->version_head;
+ while (node) {
+ p = node;
+ node = node->version_next;
+ jffs_free_node(p);
+ DJM(no_jffs_node--);
+ }
+ return 0;
+}
+
+
+/* Free a file and its name. */
+int
+jffs_free_file(struct jffs_file *f)
+{
+ D3(printk("jffs_free_file: f #%u, \"%s\"\n",
+ f->ino, (f->name ? f->name : "")));
+
+ if (f->name) {
+ kfree(f->name);
+ DJM(no_name--);
+ }
+ kfree(f);
+ no_jffs_file--;
+ return 0;
+}
+
+long
+jffs_get_file_count(void)
+{
+ return no_jffs_file;
+}
+
+/* See if a file is deleted. If so, mark that file's nodes as obsolete. */
+int
+jffs_possibly_delete_file(struct jffs_file *f)
+{
+ struct jffs_node *n;
+
+ D3(printk("jffs_possibly_delete_file(): ino: %u\n",
+ f->ino));
+
+ ASSERT(if (!f) {
+ printk(KERN_ERR "jffs_possibly_delete_file(): f == NULL\n");
+ return -1;
+ });
+
+ if (f->deleted) {
+ /* First try to remove all older versions. Commence with
+ the oldest node. */
+ for (n = f->version_head; n; n = n->version_next) {
+ if (!n->fm) {
+ continue;
+ }
+ if (jffs_fmfree(f->c->fmc, n->fm, n) < 0) {
+ break;
+ }
+ }
+ /* Unlink the file from the filesystem. */
+ if (!f->c->building_fs) {
+ jffs_unlink_file_from_tree(f);
+ }
+ jffs_unlink_file_from_hash(f);
+ jffs_free_node_list(f);
+ jffs_free_file(f);
+ }
+ return 0;
+}
+
+
+/* Used in conjunction with jffs_foreach_file() to count the number
+ of files in the file system. */
+int
+jffs_file_count(struct jffs_file *f)
+{
+ return 1;
+}
+
+
+/* Build up a file's range list from scratch by going through the
+ version list. */
+int
+jffs_build_file(struct jffs_file *f)
+{
+ struct jffs_node *n;
+
+ D3(printk("jffs_build_file(): ino: %u, name: \"%s\"\n",
+ f->ino, (f->name ? f->name : "")));
+
+ for (n = f->version_head; n; n = n->version_next) {
+ jffs_update_file(f, n);
+ }
+ return 0;
+}
+
+
+/* Remove an amount of data from a file. If this amount of data is
+ zero, that could mean that a node should be split in two parts.
+ We remove or change the appropriate nodes in the lists.
+
+ Starting offset of area to be removed is node->data_offset,
+ and the length of the area is in node->removed_size. */
+static int
+jffs_delete_data(struct jffs_file *f, struct jffs_node *node)
+{
+ struct jffs_node *n;
+ __u32 offset = node->data_offset;
+ __u32 remove_size = node->removed_size;
+
+ D3(printk("jffs_delete_data(): offset = %u, remove_size = %u\n",
+ offset, remove_size));
+
+ if (remove_size == 0
+ && f->range_tail
+ && f->range_tail->data_offset + f->range_tail->data_size
+ == offset) {
+ /* A simple append; nothing to remove or no node to split. */
+ return 0;
+ }
+
+ /* Find the node where we should begin the removal. */
+ for (n = f->range_head; n; n = n->range_next) {
+ if (n->data_offset + n->data_size > offset) {
+ break;
+ }
+ }
+ if (!n) {
+ /* If there's no data in the file there's no data to
+ remove either. */
+ return 0;
+ }
+
+ if (n->data_offset > offset) {
+ /* XXX: Not implemented yet. */
+ printk(KERN_WARNING "JFFS: An unexpected situation "
+ "occurred in jffs_delete_data.\n");
+ }
+ else if (n->data_offset < offset) {
+ /* See if the node has to be split into two parts. */
+ if (n->data_offset + n->data_size > offset + remove_size) {
+ /* Do the split. */
+ struct jffs_node *new_node;
+ D3(printk("jffs_delete_data(): Split node with "
+ "version number %u.\n", n->version));
+
+ if (!(new_node = jffs_alloc_node())) {
+ D(printk("jffs_delete_data(): -ENOMEM\n"));
+ return -ENOMEM;
+ }
+ DJM(no_jffs_node++);
+
+ new_node->ino = n->ino;
+ new_node->version = n->version;
+ new_node->data_offset = offset;
+ new_node->data_size = n->data_size - (remove_size + (offset - n->data_offset));
+ new_node->fm_offset = n->fm_offset + (remove_size + (offset - n->data_offset));
+ new_node->name_size = n->name_size;
+ new_node->fm = n->fm;
+ new_node->version_prev = n;
+ new_node->version_next = n->version_next;
+ if (new_node->version_next) {
+ new_node->version_next->version_prev
+ = new_node;
+ }
+ else {
+ f->version_tail = new_node;
+ }
+ n->version_next = new_node;
+ new_node->range_prev = n;
+ new_node->range_next = n->range_next;
+ if (new_node->range_next) {
+ new_node->range_next->range_prev = new_node;
+ }
+ else {
+ f->range_tail = new_node;
+ }
+ /* A very interesting can of worms. */
+ n->range_next = new_node;
+ n->data_size = offset - n->data_offset;
+ if (new_node->fm)
+ jffs_add_node(new_node);
+ else {
+ D1(printk(KERN_WARNING "jffs_delete_data(): Splitting an empty node (file hold).\n!"));
+ D1(printk(KERN_WARNING "FIXME: Did dwmw2 do the right thing here?\n"));
+ }
+ n = new_node->range_next;
+ remove_size = 0;
+ }
+ else {
+ /* No. No need to split the node. Just remove
+ the end of the node. */
+ int r = min(n->data_offset + n->data_size
+ - offset, remove_size);
+ n->data_size -= r;
+ remove_size -= r;
+ n = n->range_next;
+ }
+ }
+
+ /* Remove as many nodes as necessary. */
+ while (n && remove_size) {
+ if (n->data_size <= remove_size) {
+ struct jffs_node *p = n;
+ remove_size -= n->data_size;
+ n = n->range_next;
+ D3(printk("jffs_delete_data(): Removing node: "
+ "ino: %u, version: %u%s\n",
+ p->ino, p->version,
+ (p->fm ? "" : " (virtual)")));
+ if (p->fm) {
+ jffs_fmfree(f->c->fmc, p->fm, p);
+ }
+ jffs_unlink_node_from_range_list(f, p);
+ jffs_unlink_node_from_version_list(f, p);
+ jffs_free_node(p);
+ DJM(no_jffs_node--);
+ }
+ else {
+ n->data_size -= remove_size;
+ n->fm_offset += remove_size;
+ n->data_offset -= (node->removed_size - remove_size);
+ n = n->range_next;
+ break;
+ }
+ }
+
+ /* Adjust the following nodes' information about offsets etc. */
+ while (n && node->removed_size) {
+ n->data_offset -= node->removed_size;
+ n = n->range_next;
+ }
+
+ if (node->removed_size > (f->size - node->data_offset)) {
+ /* It's possible that the removed_size is in fact
+ * greater than the amount of data we actually thought
+ * were present in the first place - some of the nodes
+ * which this node originally obsoleted may already have
+ * been deleted from the flash by subsequent garbage
+ * collection.
+ *
+ * If this is the case, don't let f->size go negative.
+ * Bad things would happen :)
+ */
+ f->size = node->data_offset;
+ } else {
+ f->size -= node->removed_size;
+ }
+ D3(printk("jffs_delete_data(): f->size = %d\n", f->size));
+ return 0;
+} /* jffs_delete_data() */
+
+
+/* Insert some data into a file. Prior to the call to this function,
+ jffs_delete_data should be called. */
+static int
+jffs_insert_data(struct jffs_file *f, struct jffs_node *node)
+{
+ D3(printk("jffs_insert_data(): node->data_offset = %u, "
+ "node->data_size = %u, f->size = %u\n",
+ node->data_offset, node->data_size, f->size));
+
+ /* Find the position where we should insert data. */
+ retry:
+ if (node->data_offset == f->size) {
+ /* A simple append. This is the most common operation. */
+ node->range_next = 0;
+ node->range_prev = f->range_tail;
+ if (node->range_prev) {
+ node->range_prev->range_next = node;
+ }
+ f->range_tail = node;
+ f->size += node->data_size;
+ if (!f->range_head) {
+ f->range_head = node;
+ }
+ }
+ else if (node->data_offset < f->size) {
+ /* Trying to insert data into the middle of the file. This
+ means no problem because jffs_delete_data() has already
+ prepared the range list for us. */
+ struct jffs_node *n;
+
+ /* Find the correct place for the insertion and then insert
+ the node. */
+ for (n = f->range_head; n; n = n->range_next) {
+ D2(printk("Cool stuff's happening!\n"));
+
+ if (n->data_offset == node->data_offset) {
+ node->range_prev = n->range_prev;
+ if (node->range_prev) {
+ node->range_prev->range_next = node;
+ }
+ else {
+ f->range_head = node;
+ }
+ node->range_next = n;
+ n->range_prev = node;
+ break;
+ }
+ ASSERT(else if (n->data_offset + n->data_size >
+ node->data_offset) {
+ printk(KERN_ERR "jffs_insert_data(): "
+ "Couldn't find a place to insert "
+ "the data!\n");
+ return -1;
+ });
+ }
+
+ /* Adjust later nodes' offsets etc. */
+ n = node->range_next;
+ while (n) {
+ n->data_offset += node->data_size;
+ n = n->range_next;
+ }
+ f->size += node->data_size;
+ }
+ else if (node->data_offset > f->size) {
+ /* Okay. This is tricky. This means that we want to insert
+ data at a place that is beyond the limits of the file as
+ it is constructed right now. This is actually a common
+ event that for instance could occur during the mounting
+ of the file system if a large file have been truncated,
+ rewritten and then only partially garbage collected. */
+
+ struct jffs_node *n;
+
+ /* We need a place holder for the data that is missing in
+ front of this insertion. This "virtual node" will not
+ be associated with any space on the flash device. */
+ struct jffs_node *virtual_node;
+ if (!(virtual_node = jffs_alloc_node())) {
+ return -ENOMEM;
+ }
+
+ D(printk("jffs_insert_data: Inserting a virtual node.\n"));
+ D(printk(" node->data_offset = %u\n", node->data_offset));
+ D(printk(" f->size = %u\n", f->size));
+
+ virtual_node->ino = node->ino;
+ virtual_node->version = node->version;
+ virtual_node->removed_size = 0;
+ virtual_node->fm_offset = 0;
+ virtual_node->name_size = 0;
+ virtual_node->fm = 0; /* This is a virtual data holder. */
+ virtual_node->version_prev = 0;
+ virtual_node->version_next = 0;
+ virtual_node->range_next = 0;
+
+ /* Are there any data at all in the file yet? */
+ if (f->range_head) {
+ virtual_node->data_offset
+ = f->range_tail->data_offset
+ + f->range_tail->data_size;
+ virtual_node->data_size
+ = node->data_offset - virtual_node->data_offset;
+ virtual_node->range_prev = f->range_tail;
+ f->range_tail->range_next = virtual_node;
+ }
+ else {
+ virtual_node->data_offset = 0;
+ virtual_node->data_size = node->data_offset;
+ virtual_node->range_prev = 0;
+ f->range_head = virtual_node;
+ }
+
+ f->range_tail = virtual_node;
+ f->size += virtual_node->data_size;
+
+ /* Insert this virtual node in the version list as well. */
+ for (n = f->version_head; n ; n = n->version_next) {
+ if (n->version == virtual_node->version) {
+ virtual_node->version_prev = n->version_prev;
+ n->version_prev = virtual_node;
+ if (virtual_node->version_prev) {
+ virtual_node->version_prev
+ ->version_next = virtual_node;
+ }
+ else {
+ f->version_head = virtual_node;
+ }
+ virtual_node->version_next = n;
+ break;
+ }
+ }
+
+ D(jffs_print_node(virtual_node));
+
+ /* Make a new try to insert the node. */
+ goto retry;
+ }
+
+ D3(printk("jffs_insert_data(): f->size = %d\n", f->size));
+ return 0;
+}
+
+
+/* A new node (with data) has been added to the file and now the range
+ list has to be modified. */
+static int
+jffs_update_file(struct jffs_file *f, struct jffs_node *node)
+{
+ int err;
+
+ D3(printk("jffs_update_file(): ino: %u, version: %u\n",
+ f->ino, node->version));
+
+ if (node->data_size == 0) {
+ if (node->removed_size == 0) {
+ /* data_offset == X */
+ /* data_size == 0 */
+ /* remove_size == 0 */
+ }
+ else {
+ /* data_offset == X */
+ /* data_size == 0 */
+ /* remove_size != 0 */
+ if ((err = jffs_delete_data(f, node)) < 0) {
+ return err;
+ }
+ }
+ }
+ else {
+ /* data_offset == X */
+ /* data_size != 0 */
+ /* remove_size == Y */
+ if ((err = jffs_delete_data(f, node)) < 0) {
+ return err;
+ }
+ if ((err = jffs_insert_data(f, node)) < 0) {
+ return err;
+ }
+ }
+ return 0;
+}
+
+
+/* Print the contents of a node. */
+void
+jffs_print_node(struct jffs_node *n)
+{
+ D(printk("jffs_node: 0x%p\n", n));
+ D(printk("{\n"));
+ D(printk(" 0x%08x, /* version */\n", n->version));
+ D(printk(" 0x%08x, /* data_offset */\n", n->data_offset));
+ D(printk(" 0x%08x, /* data_size */\n", n->data_size));
+ D(printk(" 0x%08x, /* removed_size */\n", n->removed_size));
+ D(printk(" 0x%08x, /* fm_offset */\n", n->fm_offset));
+ D(printk(" 0x%02x, /* name_size */\n", n->name_size));
+ D(printk(" 0x%p, /* fm, fm->offset: %u */\n",
+ n->fm, (n->fm ? n->fm->offset : 0)));
+ D(printk(" 0x%p, /* version_prev */\n", n->version_prev));
+ D(printk(" 0x%p, /* version_next */\n", n->version_next));
+ D(printk(" 0x%p, /* range_prev */\n", n->range_prev));
+ D(printk(" 0x%p, /* range_next */\n", n->range_next));
+ D(printk("}\n"));
+}
+
+
+/* Print the contents of a raw inode. */
+void
+jffs_print_raw_inode(struct jffs_raw_inode *raw_inode)
+{
+ D(printk("jffs_raw_inode: inode number: %u\n", raw_inode->ino));
+ D(printk("{\n"));
+ D(printk(" 0x%08x, /* magic */\n", raw_inode->magic));
+ D(printk(" 0x%08x, /* ino */\n", raw_inode->ino));
+ D(printk(" 0x%08x, /* pino */\n", raw_inode->pino));
+ D(printk(" 0x%08x, /* version */\n", raw_inode->version));
+ D(printk(" 0x%08x, /* mode */\n", raw_inode->mode));
+ D(printk(" 0x%04x, /* uid */\n", raw_inode->uid));
+ D(printk(" 0x%04x, /* gid */\n", raw_inode->gid));
+ D(printk(" 0x%08x, /* atime */\n", raw_inode->atime));
+ D(printk(" 0x%08x, /* mtime */\n", raw_inode->mtime));
+ D(printk(" 0x%08x, /* ctime */\n", raw_inode->ctime));
+ D(printk(" 0x%08x, /* offset */\n", raw_inode->offset));
+ D(printk(" 0x%08x, /* dsize */\n", raw_inode->dsize));
+ D(printk(" 0x%08x, /* rsize */\n", raw_inode->rsize));
+ D(printk(" 0x%02x, /* nsize */\n", raw_inode->nsize));
+ D(printk(" 0x%02x, /* nlink */\n", raw_inode->nlink));
+ D(printk(" 0x%02x, /* spare */\n",
+ raw_inode->spare));
+ D(printk(" %u, /* rename */\n",
+ raw_inode->rename));
+ D(printk(" %u, /* deleted */\n",
+ raw_inode->deleted));
+ D(printk(" 0x%02x, /* accurate */\n",
+ raw_inode->accurate));
+ D(printk(" 0x%08x, /* dchksum */\n", raw_inode->dchksum));
+ D(printk(" 0x%04x, /* nchksum */\n", raw_inode->nchksum));
+ D(printk(" 0x%04x, /* chksum */\n", raw_inode->chksum));
+ D(printk("}\n"));
+}
+
+
+/* Print the contents of a file. */
+int
+jffs_print_file(struct jffs_file *f)
+{
+ D(int i);
+ D(printk("jffs_file: 0x%p\n", f));
+ D(printk("{\n"));
+ D(printk(" 0x%08x, /* ino */\n", f->ino));
+ D(printk(" 0x%08x, /* pino */\n", f->pino));
+ D(printk(" 0x%08x, /* mode */\n", f->mode));
+ D(printk(" 0x%04x, /* uid */\n", f->uid));
+ D(printk(" 0x%04x, /* gid */\n", f->gid));
+ D(printk(" 0x%08x, /* atime */\n", f->atime));
+ D(printk(" 0x%08x, /* mtime */\n", f->mtime));
+ D(printk(" 0x%08x, /* ctime */\n", f->ctime));
+ D(printk(" 0x%02x, /* nsize */\n", f->nsize));
+ D(printk(" 0x%02x, /* nlink */\n", f->nlink));
+ D(printk(" 0x%02x, /* deleted */\n", f->deleted));
+ D(printk(" \"%s\", ", (f->name ? f->name : "")));
+ D(for (i = strlen(f->name ? f->name : ""); i < 8; ++i) {
+ printk(" ");
+ });
+ D(printk("/* name */\n"));
+ D(printk(" 0x%08x, /* size */\n", f->size));
+ D(printk(" 0x%08x, /* highest_version */\n",
+ f->highest_version));
+ D(printk(" 0x%p, /* c */\n", f->c));
+ D(printk(" 0x%p, /* parent */\n", f->parent));
+ D(printk(" 0x%p, /* children */\n", f->children));
+ D(printk(" 0x%p, /* sibling_prev */\n", f->sibling_prev));
+ D(printk(" 0x%p, /* sibling_next */\n", f->sibling_next));
+ D(printk(" 0x%p, /* hash_prev */\n", f->hash.prev));
+ D(printk(" 0x%p, /* hash_next */\n", f->hash.next));
+ D(printk(" 0x%p, /* range_head */\n", f->range_head));
+ D(printk(" 0x%p, /* range_tail */\n", f->range_tail));
+ D(printk(" 0x%p, /* version_head */\n", f->version_head));
+ D(printk(" 0x%p, /* version_tail */\n", f->version_tail));
+ D(printk("}\n"));
+ return 0;
+}
+
+
+void
+jffs_print_hash_table(struct jffs_control *c)
+{
+ int i;
+
+ printk("JFFS: Dumping the file system's hash table...\n");
+ for (i = 0; i < c->hash_len; i++) {
+ struct list_head *p;
+ for (p = c->hash[i].next; p != &c->hash[i]; p = p->next) {
+ struct jffs_file *f=list_entry(p,struct jffs_file,hash);
+ printk("*** c->hash[%u]: \"%s\" "
+ "(ino: %u, pino: %u)\n",
+ i, (f->name ? f->name : ""),
+ f->ino, f->pino);
+ }
+ }
+}
+
+
+void
+jffs_print_tree(struct jffs_file *first_file, int indent)
+{
+ struct jffs_file *f;
+ char *space;
+ int dir;
+
+ if (!first_file) {
+ return;
+ }
+
+ if (!(space = (char *) kmalloc(indent + 1, GFP_KERNEL))) {
+ printk("jffs_print_tree(): Out of memory!\n");
+ return;
+ }
+
+ memset(space, ' ', indent);
+ space[indent] = '\0';
+
+ for (f = first_file; f; f = f->sibling_next) {
+ dir = S_ISDIR(f->mode);
+ printk("%s%s%s (ino: %u, highest_version: %u, size: %u)\n",
+ space, (f->name ? f->name : ""), (dir ? "/" : ""),
+ f->ino, f->highest_version, f->size);
+ if (dir) {
+ jffs_print_tree(f->children, indent + 2);
+ }
+ }
+
+ kfree(space);
+}
+
+
+#if defined(JFFS_MEMORY_DEBUG) && JFFS_MEMORY_DEBUG
+void
+jffs_print_memory_allocation_statistics(void)
+{
+ static long printout = 0;
+ printk("________ Memory printout #%ld ________\n", ++printout);
+ printk("no_jffs_file = %ld\n", no_jffs_file);
+ printk("no_jffs_node = %ld\n", no_jffs_node);
+ printk("no_jffs_control = %ld\n", no_jffs_control);
+ printk("no_jffs_raw_inode = %ld\n", no_jffs_raw_inode);
+ printk("no_jffs_node_ref = %ld\n", no_jffs_node_ref);
+ printk("no_jffs_fm = %ld\n", no_jffs_fm);
+ printk("no_jffs_fmcontrol = %ld\n", no_jffs_fmcontrol);
+ printk("no_hash = %ld\n", no_hash);
+ printk("no_name = %ld\n", no_name);
+ printk("\n");
+}
+#endif
+
+
+/* Rewrite `size' bytes, and begin at `node'. */
+int
+jffs_rewrite_data(struct jffs_file *f, struct jffs_node *node, __u32 size)
+{
+ struct jffs_control *c = f->c;
+ struct jffs_fmcontrol *fmc = c->fmc;
+ struct jffs_raw_inode raw_inode;
+ struct jffs_node *new_node;
+ struct jffs_fm *fm;
+ __u32 pos;
+ __u32 pos_dchksum;
+ __u32 total_name_size;
+ __u32 total_data_size;
+ __u32 total_size;
+ int err;
+
+ D1(printk("***jffs_rewrite_data(): node: %u, name: \"%s\", size: %u\n",
+ f->ino, (f->name ? f->name : "(null)"), size));
+
+ /* Create and initialize the new node. */
+ if (!(new_node = jffs_alloc_node())) {
+ D(printk("jffs_rewrite_data(): "
+ "Failed to allocate node.\n"));
+ return -ENOMEM;
+ }
+ DJM(no_jffs_node++);
+ new_node->data_offset = node->data_offset;
+ new_node->removed_size = size;
+ total_name_size = JFFS_PAD(f->nsize);
+ total_data_size = JFFS_PAD(size);
+ total_size = sizeof(struct jffs_raw_inode)
+ + total_name_size + total_data_size;
+ new_node->fm_offset = sizeof(struct jffs_raw_inode)
+ + total_name_size;
+
+retry:
+ jffs_fm_write_lock(fmc);
+ err = 0;
+
+ if ((err = jffs_fmalloc(fmc, total_size, new_node, &fm)) < 0) {
+ DJM(no_jffs_node--);
+ jffs_fm_write_unlock(fmc);
+ D(printk("jffs_rewrite_data(): Failed to allocate fm.\n"));
+ jffs_free_node(new_node);
+ return err;
+ }
+ else if (!fm->nodes) {
+ /* The jffs_fm struct that we got is not big enough. */
+ /* This should never happen, because we deal with this case
+ in jffs_garbage_collect_next().*/
+ printk(KERN_WARNING "jffs_rewrite_data(): Allocated node is too small (%d bytes of %d)\n", fm->size, total_size);
+ if ((err = jffs_write_dummy_node(c, fm)) < 0) {
+ D(printk("jffs_rewrite_data(): "
+ "jffs_write_dummy_node() Failed!\n"));
+ } else {
+ err = -ENOSPC;
+ }
+ DJM(no_jffs_fm--);
+ jffs_fm_write_unlock(fmc);
+ kfree(fm);
+
+ return err;
+ }
+ new_node->fm = fm;
+
+ /* Initialize the raw inode. */
+ raw_inode.magic = JFFS_MAGIC_BITMASK;
+ raw_inode.ino = f->ino;
+ raw_inode.pino = f->pino;
+ raw_inode.version = f->highest_version + 1;
+ raw_inode.mode = f->mode;
+ raw_inode.uid = f->uid;
+ raw_inode.gid = f->gid;
+ raw_inode.atime = f->atime;
+ raw_inode.mtime = f->mtime;
+ raw_inode.ctime = f->ctime;
+ raw_inode.offset = node->data_offset;
+ raw_inode.dsize = size;
+ raw_inode.rsize = size;
+ raw_inode.nsize = f->nsize;
+ raw_inode.nlink = f->nlink;
+ raw_inode.spare = 0;
+ raw_inode.rename = 0;
+ raw_inode.deleted = f->deleted;
+ raw_inode.accurate = 0xff;
+ raw_inode.dchksum = 0;
+ raw_inode.nchksum = 0;
+
+ pos = new_node->fm->offset;
+ pos_dchksum = pos +JFFS_RAW_INODE_DCHKSUM_OFFSET;
+
+ D3(printk("jffs_rewrite_data(): Writing this raw inode "
+ "to pos 0x%ul.\n", pos));
+ D3(jffs_print_raw_inode(&raw_inode));
+
+ if ((err = flash_safe_write(fmc->mtd, pos,
+ (u_char *) &raw_inode,
+ sizeof(struct jffs_raw_inode)
+ - sizeof(__u32)
+ - sizeof(__u16) - sizeof(__u16))) < 0) {
+ jffs_fmfree_partly(fmc, fm,
+ total_name_size + total_data_size);
+ jffs_fm_write_unlock(fmc);
+ printk(KERN_ERR "JFFS: jffs_rewrite_data: Write error during "
+ "rewrite. (raw inode)\n");
+ printk(KERN_ERR "JFFS: jffs_rewrite_data: Now retrying "
+ "rewrite. (raw inode)\n");
+ goto retry;
+ }
+ pos += sizeof(struct jffs_raw_inode);
+
+ /* Write the name to the flash memory. */
+ if (f->nsize) {
+ D3(printk("jffs_rewrite_data(): Writing name \"%s\" to "
+ "pos 0x%ul.\n", f->name, (unsigned int) pos));
+ if ((err = flash_safe_write(fmc->mtd, pos,
+ (u_char *)f->name,
+ f->nsize)) < 0) {
+ jffs_fmfree_partly(fmc, fm, total_data_size);
+ jffs_fm_write_unlock(fmc);
+ printk(KERN_ERR "JFFS: jffs_rewrite_data: Write "
+ "error during rewrite. (name)\n");
+ printk(KERN_ERR "JFFS: jffs_rewrite_data: Now retrying "
+ "rewrite. (name)\n");
+ goto retry;
+ }
+ pos += total_name_size;
+ raw_inode.nchksum = jffs_checksum(f->name, f->nsize);
+ }
+
+ /* Write the data. */
+ if (size) {
+ int r;
+ unsigned char *page;
+ __u32 offset = node->data_offset;
+
+ if (!(page = (unsigned char *)__get_free_page(GFP_KERNEL))) {
+ jffs_fmfree_partly(fmc, fm, 0);
+ return -1;
+ }
+
+ while (size) {
+ __u32 s = min(size, (__u32)PAGE_SIZE);
+ if ((r = jffs_read_data(f, (char *)page,
+ offset, s)) < s) {
+ free_page((unsigned long)page);
+ jffs_fmfree_partly(fmc, fm, 0);
+ jffs_fm_write_unlock(fmc);
+ printk(KERN_ERR "JFFS: jffs_rewrite_data: "
+ "jffs_read_data() "
+ "failed! (r = %d)\n", r);
+ return -1;
+ }
+ if ((err = flash_safe_write(fmc->mtd,
+ pos, page, r)) < 0) {
+ free_page((unsigned long)page);
+ jffs_fmfree_partly(fmc, fm, 0);
+ jffs_fm_write_unlock(fmc);
+ printk(KERN_ERR "JFFS: jffs_rewrite_data: "
+ "Write error during rewrite. "
+ "(data)\n");
+ goto retry;
+ }
+ pos += r;
+ size -= r;
+ offset += r;
+ raw_inode.dchksum += jffs_checksum(page, r);
+ }
+
+ free_page((unsigned long)page);
+ }
+
+ raw_inode.accurate = 0;
+ raw_inode.chksum = jffs_checksum(&raw_inode,
+ sizeof(struct jffs_raw_inode)
+ - sizeof(__u16));
+
+ /* Add the checksum. */
+ if ((err
+ = flash_safe_write(fmc->mtd, pos_dchksum,
+ &((u_char *)
+ &raw_inode)[JFFS_RAW_INODE_DCHKSUM_OFFSET],
+ sizeof(__u32) + sizeof(__u16)
+ + sizeof(__u16))) < 0) {
+ jffs_fmfree_partly(fmc, fm, 0);
+ jffs_fm_write_unlock(fmc);
+ printk(KERN_ERR "JFFS: jffs_rewrite_data: Write error during "
+ "rewrite. (checksum)\n");
+ goto retry;
+ }
+
+ /* Now make the file system aware of the newly written node. */
+ jffs_insert_node(c, f, &raw_inode, f->name, new_node);
+ jffs_fm_write_unlock(fmc);
+
+ D3(printk("jffs_rewrite_data(): Leaving...\n"));
+ return 0;
+} /* jffs_rewrite_data() */
+
+
+/* jffs_garbage_collect_next implements one step in the garbage collect
+ process and is often called multiple times at each occasion of a
+ garbage collect. */
+
+int
+jffs_garbage_collect_next(struct jffs_control *c)
+{
+ struct jffs_fmcontrol *fmc = c->fmc;
+ struct jffs_node *node;
+ struct jffs_file *f;
+ int err = 0;
+ __u32 size;
+ __u32 data_size;
+ __u32 total_name_size;
+ __u32 extra_available;
+ __u32 space_needed;
+ __u32 free_chunk_size1 = jffs_free_size1(fmc);
+ D2(__u32 free_chunk_size2 = jffs_free_size2(fmc));
+
+ /* Get the oldest node in the flash. */
+ node = jffs_get_oldest_node(fmc);
+ ASSERT(if (!node) {
+ printk(KERN_ERR "JFFS: jffs_garbage_collect_next: "
+ "No oldest node found!\n");
+ err = -1;
+ goto jffs_garbage_collect_next_end;
+
+
+ });
+
+ /* Find its corresponding file too. */
+ f = jffs_find_file(c, node->ino);
+
+ if (!f) {
+ printk (KERN_ERR "JFFS: jffs_garbage_collect_next: "
+ "No file to garbage collect! "
+ "(ino = 0x%08x)\n", node->ino);
+ /* FIXME: Free the offending node and recover. */
+ err = -1;
+ goto jffs_garbage_collect_next_end;
+ }
+
+ /* We always write out the name. Theoretically, we don't need
+ to, but for now it's easier - because otherwise we'd have
+ to keep track of how many times the current name exists on
+ the flash and make sure it never reaches zero.
+
+ The current approach means that would be possible to cause
+ the GC to end up eating its tail by writing lots of nodes
+ with no name for it to garbage-collect. Hence the change in
+ inode.c to write names with _every_ node.
+
+ It sucks, but it _should_ work.
+ */
+ total_name_size = JFFS_PAD(f->nsize);
+
+ D1(printk("jffs_garbage_collect_next(): \"%s\", "
+ "ino: %u, version: %u, location 0x%x, dsize %u\n",
+ (f->name ? f->name : ""), node->ino, node->version,
+ node->fm->offset, node->data_size));
+
+ /* Compute how many data it's possible to rewrite at the moment. */
+ data_size = f->size - node->data_offset;
+
+ /* And from that, the total size of the chunk we want to write */
+ size = sizeof(struct jffs_raw_inode) + total_name_size
+ + data_size + JFFS_GET_PAD_BYTES(data_size);
+
+ /* If that's more than max_chunk_size, reduce it accordingly */
+ if (size > fmc->max_chunk_size) {
+ size = fmc->max_chunk_size;
+ data_size = size - sizeof(struct jffs_raw_inode)
+ - total_name_size;
+ }
+
+ /* If we're asking to take up more space than free_chunk_size1
+ but we _could_ fit in it, shrink accordingly.
+ */
+ if (size > free_chunk_size1) {
+
+ if (free_chunk_size1 <
+ (sizeof(struct jffs_raw_inode) + total_name_size + BLOCK_SIZE)){
+ /* The space left is too small to be of any
+ use really. */
+ struct jffs_fm *dirty_fm
+ = jffs_fmalloced(fmc,
+ fmc->tail->offset + fmc->tail->size,
+ free_chunk_size1, NULL);
+ if (!dirty_fm) {
+ printk(KERN_ERR "JFFS: "
+ "jffs_garbage_collect_next: "
+ "Failed to allocate `dirty' "
+ "flash memory!\n");
+ err = -1;
+ goto jffs_garbage_collect_next_end;
+ }
+ D1(printk("Dirtying end of flash - too small\n"));
+ jffs_write_dummy_node(c, dirty_fm);
+ err = 0;
+ goto jffs_garbage_collect_next_end;
+ }
+ D1(printk("Reducing size of new node from %d to %d to avoid "
+ " exceeding free_chunk_size1\n",
+ size, free_chunk_size1));
+
+ size = free_chunk_size1;
+ data_size = size - sizeof(struct jffs_raw_inode)
+ - total_name_size;
+ }
+
+
+ /* Calculate the amount of space needed to hold the nodes
+ which are remaining in the tail */
+ space_needed = fmc->min_free_size - (node->fm->offset % fmc->sector_size);
+
+ /* From that, calculate how much 'extra' space we can use to
+ increase the size of the node we're writing from the size
+ of the node we're obsoleting
+ */
+ if (space_needed > fmc->free_size) {
+ /* If we've gone below min_free_size for some reason,
+ don't fuck up. This is why we have
+ min_free_size > sector_size. Whinge about it though,
+ just so I can convince myself my maths is right.
+ */
+ D1(printk(KERN_WARNING "jffs_garbage_collect_next(): "
+ "space_needed %d exceeded free_size %d\n",
+ space_needed, fmc->free_size));
+ extra_available = 0;
+ } else {
+ extra_available = fmc->free_size - space_needed;
+ }
+
+ /* Check that we don't use up any more 'extra' space than
+ what's available */
+ if (size > JFFS_PAD(node->data_size) + total_name_size +
+ sizeof(struct jffs_raw_inode) + extra_available) {
+ D1(printk("Reducing size of new node from %d to %ld to avoid "
+ "catching our tail\n", size,
+ (long) (JFFS_PAD(node->data_size) + JFFS_PAD(node->name_size) +
+ sizeof(struct jffs_raw_inode) + extra_available)));
+ D1(printk("space_needed = %d, extra_available = %d\n",
+ space_needed, extra_available));
+
+ size = JFFS_PAD(node->data_size) + total_name_size +
+ sizeof(struct jffs_raw_inode) + extra_available;
+ data_size = size - sizeof(struct jffs_raw_inode)
+ - total_name_size;
+ };
+
+ D2(printk(" total_name_size: %u\n", total_name_size));
+ D2(printk(" data_size: %u\n", data_size));
+ D2(printk(" size: %u\n", size));
+ D2(printk(" f->nsize: %u\n", f->nsize));
+ D2(printk(" f->size: %u\n", f->size));
+ D2(printk(" node->data_offset: %u\n", node->data_offset));
+ D2(printk(" free_chunk_size1: %u\n", free_chunk_size1));
+ D2(printk(" free_chunk_size2: %u\n", free_chunk_size2));
+ D2(printk(" node->fm->offset: 0x%08x\n", node->fm->offset));
+
+ if ((err = jffs_rewrite_data(f, node, data_size))) {
+ printk(KERN_WARNING "jffs_rewrite_data() failed: %d\n", err);
+ return err;
+ }
+
+jffs_garbage_collect_next_end:
+ D3(printk("jffs_garbage_collect_next: Leaving...\n"));
+ return err;
+} /* jffs_garbage_collect_next */
+
+
+/* If an obsolete node is partly going to be erased due to garbage
+ collection, the part that isn't going to be erased must be filled
+ with zeroes so that the scan of the flash will work smoothly next
+ time. (The data in the file could for instance be a JFFS image
+ which could cause enormous confusion during a scan of the flash
+ device if we didn't do this.)
+ There are two phases in this procedure: First, the clearing of
+ the name and data parts of the node. Second, possibly also clearing
+ a part of the raw inode as well. If the box is power cycled during
+ the first phase, only the checksum of this node-to-be-cleared-at-
+ the-end will be wrong. If the box is power cycled during, or after,
+ the clearing of the raw inode, the information like the length of
+ the name and data parts are zeroed. The next time the box is
+ powered up, the scanning algorithm manages this faulty data too
+ because:
+
+ - The checksum is invalid and thus the raw inode must be discarded
+ in any case.
+ - If the lengths of the data part or the name part are zeroed, the
+ scanning just continues after the raw inode. But after the inode
+ the scanning procedure just finds zeroes which is the same as
+ dirt.
+
+ So, in the end, this could never fail. :-) Even if it does fail,
+ the scanning algorithm should manage that too. */
+
+static int
+jffs_clear_end_of_node(struct jffs_control *c, __u32 erase_size)
+{
+ struct jffs_fm *fm;
+ struct jffs_fmcontrol *fmc = c->fmc;
+ __u32 zero_offset;
+ __u32 zero_size;
+ __u32 zero_offset_data;
+ __u32 zero_size_data;
+ __u32 cutting_raw_inode = 0;
+
+ if (!(fm = jffs_cut_node(fmc, erase_size))) {
+ D3(printk("jffs_clear_end_of_node(): fm == NULL\n"));
+ return 0;
+ }
+
+ /* Where and how much shall we clear? */
+ zero_offset = fmc->head->offset + erase_size;
+ zero_size = fm->offset + fm->size - zero_offset;
+
+ /* Do we have to clear the raw_inode explicitly? */
+ if (fm->size - zero_size < sizeof(struct jffs_raw_inode)) {
+ cutting_raw_inode = sizeof(struct jffs_raw_inode)
+ - (fm->size - zero_size);
+ }
+
+ /* First, clear the name and data fields. */
+ zero_offset_data = zero_offset + cutting_raw_inode;
+ zero_size_data = zero_size - cutting_raw_inode;
+ flash_safe_acquire(fmc->mtd);
+ flash_memset(fmc->mtd, zero_offset_data, 0, zero_size_data);
+ flash_safe_release(fmc->mtd);
+
+ /* Should we clear a part of the raw inode? */
+ if (cutting_raw_inode) {
+ /* I guess it is ok to clear the raw inode in this order. */
+ flash_safe_acquire(fmc->mtd);
+ flash_memset(fmc->mtd, zero_offset, 0,
+ cutting_raw_inode);
+ flash_safe_release(fmc->mtd);
+ }
+
+ return 0;
+} /* jffs_clear_end_of_node() */
+
+/* Try to erase as much as possible of the dirt in the flash memory. */
+long
+jffs_try_to_erase(struct jffs_control *c)
+{
+ struct jffs_fmcontrol *fmc = c->fmc;
+ long erase_size;
+ int err;
+ __u32 offset;
+
+ D3(printk("jffs_try_to_erase()\n"));
+
+ erase_size = jffs_erasable_size(fmc);
+
+ D2(printk("jffs_try_to_erase(): erase_size = %ld\n", erase_size));
+
+ if (erase_size == 0) {
+ return 0;
+ }
+ else if (erase_size < 0) {
+ printk(KERN_ERR "JFFS: jffs_try_to_erase: "
+ "jffs_erasable_size returned %ld.\n", erase_size);
+ return erase_size;
+ }
+
+ if ((err = jffs_clear_end_of_node(c, erase_size)) < 0) {
+ printk(KERN_ERR "JFFS: jffs_try_to_erase: "
+ "Clearing of node failed.\n");
+ return err;
+ }
+
+ offset = fmc->head->offset;
+
+ /* Now, let's try to do the erase. */
+ if ((err = flash_erase_region(fmc->mtd,
+ offset, erase_size)) < 0) {
+ printk(KERN_ERR "JFFS: Erase of flash failed. "
+ "offset = %u, erase_size = %ld\n",
+ offset, erase_size);
+ /* XXX: Here we should allocate this area as dirty
+ with jffs_fmalloced or something similar. Now
+ we just report the error. */
+ return err;
+ }
+
+#if 0
+ /* Check if the erased sectors really got erased. */
+ {
+ __u32 pos;
+ __u32 end;
+
+ pos = (__u32)flash_get_direct_pointer(c->sb->s_dev, offset);
+ end = pos + erase_size;
+
+ D2(printk("JFFS: Checking erased sector(s)...\n"));
+
+ flash_safe_acquire(fmc->mtd);
+
+ for (; pos < end; pos += 4) {
+ if (*(__u32 *)pos != JFFS_EMPTY_BITMASK) {
+ printk("JFFS: Erase failed! pos = 0x%lx\n",
+ (long)pos);
+ jffs_hexdump(fmc->mtd, pos,
+ jffs_min(256, end - pos));
+ err = -1;
+ break;
+ }
+ }
+
+ flash_safe_release(fmc->mtd);
+
+ if (!err) {
+ D2(printk("JFFS: Erase succeeded.\n"));
+ }
+ else {
+ /* XXX: Here we should allocate the memory
+ with jffs_fmalloced() in order to prevent
+ JFFS from using this area accidentally. */
+ return err;
+ }
+ }
+#endif
+
+ /* Update the flash memory data structures. */
+ jffs_sync_erase(fmc, erase_size);
+
+ return erase_size;
+}
+
+
+/* There are different criteria that should trigger a garbage collect:
+
+ 1. There is too much dirt in the memory.
+ 2. The free space is becoming small.
+ 3. There are many versions of a node.
+
+ The garbage collect should always be done in a manner that guarantees
+ that future garbage collects cannot be locked. E.g. Rewritten chunks
+ should not be too large (span more than one sector in the flash memory
+ for exemple). Of course there is a limit on how intelligent this garbage
+ collection can be. */
+
+
+int
+jffs_garbage_collect_now(struct jffs_control *c)
+{
+ struct jffs_fmcontrol *fmc = c->fmc;
+ long erased = 0;
+ int result = 0;
+ D1(int i = 1);
+ D2(printk("***jffs_garbage_collect_now(): fmc->dirty_size = %u, fmc->free_size = 0x%x\n, fcs1=0x%x, fcs2=0x%x",
+ fmc->dirty_size, fmc->free_size, jffs_free_size1(fmc), jffs_free_size2(fmc)));
+ D2(jffs_print_fmcontrol(fmc));
+
+ // down(&fmc->gclock);
+
+ /* If it is possible to garbage collect, do so. */
+
+ while (erased == 0) {
+ D1(printk("***jffs_garbage_collect_now(): round #%u, "
+ "fmc->dirty_size = %u\n", i++, fmc->dirty_size));
+ D2(jffs_print_fmcontrol(fmc));
+
+ if ((erased = jffs_try_to_erase(c)) < 0) {
+ printk(KERN_WARNING "JFFS: Error in "
+ "garbage collector.\n");
+ result = erased;
+ goto gc_end;
+ }
+ if (erased)
+ break;
+
+ if (fmc->free_size == 0) {
+ /* Argh */
+ printk(KERN_ERR "jffs_garbage_collect_now(): free_size == 0. This is BAD.\n");
+ result = -ENOSPC;
+ break;
+ }
+
+ if (fmc->dirty_size < fmc->sector_size) {
+ /* Actually, we _may_ have been able to free some,
+ * if there are many overlapping nodes which aren't
+ * actually marked dirty because they still have
+ * some valid data in each.
+ */
+ result = -ENOSPC;
+ break;
+ }
+
+ /* Let's dare to make a garbage collect. */
+ if ((result = jffs_garbage_collect_next(c)) < 0) {
+ printk(KERN_ERR "JFFS: Something "
+ "has gone seriously wrong "
+ "with a garbage collect.\n");
+ goto gc_end;
+ }
+
+ D1(printk(" jffs_garbage_collect_now(): erased: %ld\n", erased));
+ DJM(jffs_print_memory_allocation_statistics());
+ }
+
+gc_end:
+ // up(&fmc->gclock);
+
+ D3(printk(" jffs_garbage_collect_now(): Leaving...\n"));
+ D1(if (erased) {
+ printk("jffs_g_c_now(): erased = %ld\n", erased);
+ jffs_print_fmcontrol(fmc);
+ });
+
+ if (!erased && !result)
+ return -ENOSPC;
+
+ return result;
+} /* jffs_garbage_collect_now() */
+
+
+/* Determine if it is reasonable to start garbage collection.
+ We start a gc pass if either:
+ - The number of free bytes < MIN_FREE_BYTES && at least one
+ block is dirty, OR
+ - The number of dirty bytes > MAX_DIRTY_BYTES
+*/
+static inline int thread_should_wake (struct jffs_control *c)
+{
+ D1(printk (KERN_NOTICE "thread_should_wake(): free=%d, dirty=%d, blocksize=%d.\n",
+ c->fmc->free_size, c->fmc->dirty_size, c->fmc->sector_size));
+
+ /* If there's not enough dirty space to free a block, there's no point. */
+ if (c->fmc->dirty_size < c->fmc->sector_size) {
+ D2(printk(KERN_NOTICE "thread_should_wake(): Not waking. Insufficient dirty space\n"));
+ return 0;
+ }
+#if 1
+ /* If there is too much RAM used by the various structures, GC */
+ if (jffs_get_node_inuse() > (c->fmc->used_size/c->fmc->max_chunk_size * 5 + jffs_get_file_count() * 2 + 50)) {
+ /* FIXME: Provide proof that this test can be satisfied. We
+ don't want a filesystem doing endless GC just because this
+ condition cannot ever be false.
+ */
+ D2(printk(KERN_NOTICE "thread_should_wake(): Waking due to number of nodes\n"));
+ return 1;
+ }
+#endif
+ /* If there are fewer free bytes than the threshold, GC */
+ if (c->fmc->free_size < c->gc_minfree_threshold) {
+ D2(printk(KERN_NOTICE "thread_should_wake(): Waking due to insufficent free space\n"));
+ return 1;
+ }
+ /* If there are more dirty bytes than the threshold, GC */
+ if (c->fmc->dirty_size > c->gc_maxdirty_threshold) {
+ D2(printk(KERN_NOTICE "thread_should_wake(): Waking due to excessive dirty space\n"));
+ return 1;
+ }
+ /* FIXME: What about the "There are many versions of a node" condition? */
+
+ return 0;
+}
+
+
+void jffs_garbage_collect_trigger(struct jffs_control *c)
+{
+ /* NOTE: We rely on the fact that we have the BKL here.
+ * Otherwise, the gc_task could go away between the check
+ * and the wake_up_process()
+ */
+ if (c->gc_task && thread_should_wake(c))
+ send_sig(SIGHUP, c->gc_task, 1);
+}
+
+
+/* Kernel threads take (void *) as arguments. Thus we pass
+ the jffs_control data as a (void *) and then cast it. */
+int
+jffs_garbage_collect_thread(void *ptr)
+{
+ struct jffs_control *c = (struct jffs_control *) ptr;
+ struct jffs_fmcontrol *fmc = c->fmc;
+ long erased;
+ int result = 0;
+ D1(int i = 1);
+
+ c->gc_task = current;
+
+ lock_kernel();
+ exit_mm(c->gc_task);
+
+ current->session = 1;
+ current->pgrp = 1;
+ init_completion(&c->gc_thread_comp); /* barrier */
+ spin_lock_irq(&current->sigmask_lock);
+ siginitsetinv (&current->blocked, sigmask(SIGHUP) | sigmask(SIGKILL) | sigmask(SIGSTOP) | sigmask(SIGCONT));
+ recalc_sigpending(current);
+ spin_unlock_irq(&current->sigmask_lock);
+ strcpy(current->comm, "jffs_gcd");
+
+ D1(printk (KERN_NOTICE "jffs_garbage_collect_thread(): Starting infinite loop.\n"));
+
+ for (;;) {
+
+ /* See if we need to start gc. If we don't, go to sleep.
+
+ Current implementation is a BAD THING(tm). If we try
+ to unmount the FS, the unmount operation will sleep waiting
+ for this thread to exit. We need to arrange to send it a
+ sig before the umount process sleeps.
+ */
+
+ if (!thread_should_wake(c))
+ set_current_state (TASK_INTERRUPTIBLE);
+
+ schedule(); /* Yes, we do this even if we want to go
+ on immediately - we're a low priority
+ background task. */
+
+ /* Put_super will send a SIGKILL and then wait on the sem.
+ */
+ while (signal_pending(current)) {
+ siginfo_t info;
+ unsigned long signr;
+
+ spin_lock_irq(&current->sigmask_lock);
+ signr = dequeue_signal(&current->blocked, &info);
+ spin_unlock_irq(&current->sigmask_lock);
+
+ switch(signr) {
+ case SIGSTOP:
+ D1(printk("jffs_garbage_collect_thread(): SIGSTOP received.\n"));
+ set_current_state(TASK_STOPPED);
+ schedule();
+ break;
+
+ case SIGKILL:
+ D1(printk("jffs_garbage_collect_thread(): SIGKILL received.\n"));
+ c->gc_task = NULL;
+ complete_and_exit(&c->gc_thread_comp, 0);
+ }
+ }
+
+
+ D1(printk (KERN_NOTICE "jffs_garbage_collect_thread(): collecting.\n"));
+
+ D3(printk (KERN_NOTICE "g_c_thread(): down biglock\n"));
+ down(&fmc->biglock);
+
+ D1(printk("***jffs_garbage_collect_thread(): round #%u, "
+ "fmc->dirty_size = %u\n", i++, fmc->dirty_size));
+ D2(jffs_print_fmcontrol(fmc));
+
+ if ((erased = jffs_try_to_erase(c)) < 0) {
+ printk(KERN_WARNING "JFFS: Error in "
+ "garbage collector: %ld.\n", erased);
+ }
+
+ if (erased)
+ goto gc_end;
+
+ if (fmc->free_size == 0) {
+ /* Argh. Might as well commit suicide. */
+ printk(KERN_ERR "jffs_garbage_collect_thread(): free_size == 0. This is BAD.\n");
+ send_sig(SIGQUIT, c->gc_task, 1);
+ // panic()
+ goto gc_end;
+ }
+
+ /* Let's dare to make a garbage collect. */
+ if ((result = jffs_garbage_collect_next(c)) < 0) {
+ printk(KERN_ERR "JFFS: Something "
+ "has gone seriously wrong "
+ "with a garbage collect: %d\n", result);
+ }
+
+ gc_end:
+ D3(printk (KERN_NOTICE "g_c_thread(): up biglock\n"));
+ up(&fmc->biglock);
+ } /* for (;;) */
+} /* jffs_garbage_collect_thread() */
diff --git a/fs/jffs/intrep.h b/fs/jffs/intrep.h
new file mode 100644
index 00000000000000..53705880116792
--- /dev/null
+++ b/fs/jffs/intrep.h
@@ -0,0 +1,91 @@
+/*
+ * JFFS -- Journaling Flash File System, Linux implementation.
+ *
+ * Copyright (C) 1999, 2000 Axis Communications AB.
+ *
+ * Created by Finn Hakansson <finn@axis.com>.
+ *
+ * This 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 of the License, or
+ * (at your option) any later version.
+ *
+ * $Id: intrep.h,v 1.14 2001/09/23 23:28:37 dwmw2 Exp $
+ *
+ */
+
+#ifndef __LINUX_JFFS_INTREP_H__
+#define __LINUX_JFFS_INTREP_H__
+#include "jffs_fm.h"
+struct jffs_node *jffs_alloc_node(void);
+void jffs_free_node(struct jffs_node *n);
+int jffs_get_node_inuse(void);
+long jffs_get_file_count(void);
+
+__u32 jffs_checksum(const void *data, int size);
+
+void jffs_cleanup_control(struct jffs_control *c);
+int jffs_build_fs(struct super_block *sb);
+
+int jffs_insert_node(struct jffs_control *c, struct jffs_file *f,
+ const struct jffs_raw_inode *raw_inode,
+ const char *name, struct jffs_node *node);
+struct jffs_file *jffs_find_file(struct jffs_control *c, __u32 ino);
+struct jffs_file *jffs_find_child(struct jffs_file *dir, const char *name, int len);
+
+void jffs_free_node(struct jffs_node *node);
+
+int jffs_foreach_file(struct jffs_control *c, int (*func)(struct jffs_file *));
+int jffs_free_node_list(struct jffs_file *f);
+int jffs_free_file(struct jffs_file *f);
+int jffs_possibly_delete_file(struct jffs_file *f);
+int jffs_build_file(struct jffs_file *f);
+int jffs_insert_file_into_hash(struct jffs_file *f);
+int jffs_insert_file_into_tree(struct jffs_file *f);
+int jffs_unlink_file_from_hash(struct jffs_file *f);
+int jffs_unlink_file_from_tree(struct jffs_file *f);
+int jffs_remove_redundant_nodes(struct jffs_file *f);
+int jffs_file_count(struct jffs_file *f);
+
+int jffs_write_node(struct jffs_control *c, struct jffs_node *node,
+ struct jffs_raw_inode *raw_inode,
+ const char *name, const unsigned char *buf,
+ int recoverable, struct jffs_file *f);
+int jffs_read_data(struct jffs_file *f, unsigned char *buf, __u32 read_offset, __u32 size);
+
+/* Garbage collection stuff. */
+int jffs_garbage_collect_thread(void *c);
+void jffs_garbage_collect_trigger(struct jffs_control *c);
+int jffs_garbage_collect_now(struct jffs_control *c);
+
+/* Is there enough space on the flash? */
+static inline int JFFS_ENOUGH_SPACE(struct jffs_control *c, __u32 space)
+{
+ struct jffs_fmcontrol *fmc = c->fmc;
+
+ while (1) {
+ if ((fmc->flash_size - (fmc->used_size + fmc->dirty_size))
+ >= fmc->min_free_size + space) {
+ return 1;
+ }
+ if (fmc->dirty_size < fmc->sector_size)
+ return 0;
+
+ if (jffs_garbage_collect_now(c)) {
+ D1(printk("JFFS_ENOUGH_SPACE: jffs_garbage_collect_now() failed.\n"));
+ return 0;
+ }
+ }
+}
+
+/* For debugging purposes. */
+void jffs_print_node(struct jffs_node *n);
+void jffs_print_raw_inode(struct jffs_raw_inode *raw_inode);
+int jffs_print_file(struct jffs_file *f);
+void jffs_print_hash_table(struct jffs_control *c);
+void jffs_print_tree(struct jffs_file *first_file, int indent);
+
+struct buffer_head *jffs_get_write_buffer(kdev_t dev, int block);
+void jffs_put_write_buffer(struct buffer_head *bh);
+
+#endif /* __LINUX_JFFS_INTREP_H__ */
diff --git a/fs/jffs/jffs_fm.c b/fs/jffs/jffs_fm.c
new file mode 100644
index 00000000000000..47655fd338c310
--- /dev/null
+++ b/fs/jffs/jffs_fm.c
@@ -0,0 +1,791 @@
+/*
+ * JFFS -- Journaling Flash File System, Linux implementation.
+ *
+ * Copyright (C) 1999, 2000 Axis Communications AB.
+ *
+ * Created by Finn Hakansson <finn@axis.com>.
+ *
+ * This 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 of the License, or
+ * (at your option) any later version.
+ *
+ * $Id: jffs_fm.c,v 1.27 2001/09/20 12:29:47 dwmw2 Exp $
+ *
+ * Ported to Linux 2.3.x and MTD:
+ * Copyright (C) 2000 Alexander Larsson (alex@cendio.se), Cendio Systems AB
+ *
+ */
+#define __NO_VERSION__
+#include <linux/slab.h>
+#include <linux/blkdev.h>
+#include <linux/jffs.h>
+#include "jffs_fm.h"
+
+#if defined(JFFS_MARK_OBSOLETE) && JFFS_MARK_OBSOLETE
+static int jffs_mark_obsolete(struct jffs_fmcontrol *fmc, __u32 fm_offset);
+#endif
+
+extern kmem_cache_t *fm_cache;
+extern kmem_cache_t *node_cache;
+
+/* This function creates a new shiny flash memory control structure. */
+struct jffs_fmcontrol *
+jffs_build_begin(struct jffs_control *c, kdev_t dev)
+{
+ struct jffs_fmcontrol *fmc;
+ struct mtd_info *mtd;
+
+ D3(printk("jffs_build_begin()\n"));
+ fmc = (struct jffs_fmcontrol *)kmalloc(sizeof(struct jffs_fmcontrol),
+ GFP_KERNEL);
+ if (!fmc) {
+ D(printk("jffs_build_begin(): Allocation of "
+ "struct jffs_fmcontrol failed!\n"));
+ return (struct jffs_fmcontrol *)0;
+ }
+ DJM(no_jffs_fmcontrol++);
+
+ mtd = get_mtd_device(NULL, MINOR(dev));
+
+ if (!mtd) {
+ kfree(fmc);
+ DJM(no_jffs_fmcontrol--);
+ return NULL;
+ }
+
+ /* Retrieve the size of the flash memory. */
+ fmc->flash_size = mtd->size;
+ D3(printk(" fmc->flash_size = %d bytes\n", fmc->flash_size));
+
+ fmc->used_size = 0;
+ fmc->dirty_size = 0;
+ fmc->free_size = mtd->size;
+ fmc->sector_size = mtd->erasesize;
+ fmc->max_chunk_size = fmc->sector_size >> 1;
+ /* min_free_size:
+ 1 sector, obviously.
+ + 1 x max_chunk_size, for when a nodes overlaps the end of a sector
+ + 1 x max_chunk_size again, which ought to be enough to handle
+ the case where a rename causes a name to grow, and GC has
+ to write out larger nodes than the ones it's obsoleting.
+ We should fix it so it doesn't have to write the name
+ _every_ time. Later.
+ + another 2 sectors because people keep getting GC stuck and
+ we don't know why. This scares me - I want formal proof
+ of correctness of whatever number we put here. dwmw2.
+ */
+ fmc->min_free_size = fmc->sector_size << 2;
+ fmc->mtd = mtd;
+ fmc->c = c;
+ fmc->head = 0;
+ fmc->tail = 0;
+ fmc->head_extra = 0;
+ fmc->tail_extra = 0;
+ init_MUTEX(&fmc->biglock);
+ return fmc;
+}
+
+
+/* When the flash memory scan has completed, this function should be called
+ before use of the control structure. */
+void
+jffs_build_end(struct jffs_fmcontrol *fmc)
+{
+ D3(printk("jffs_build_end()\n"));
+
+ if (!fmc->head) {
+ fmc->head = fmc->head_extra;
+ fmc->tail = fmc->tail_extra;
+ }
+ else if (fmc->head_extra) {
+ fmc->tail_extra->next = fmc->head;
+ fmc->head->prev = fmc->tail_extra;
+ fmc->head = fmc->head_extra;
+ }
+ fmc->head_extra = 0; /* These two instructions should be omitted. */
+ fmc->tail_extra = 0;
+ D3(jffs_print_fmcontrol(fmc));
+}
+
+
+/* Call this function when the file system is unmounted. This function
+ frees all memory used by this module. */
+void
+jffs_cleanup_fmcontrol(struct jffs_fmcontrol *fmc)
+{
+ if (fmc) {
+ struct jffs_fm *cur;
+ struct jffs_fm *next = fmc->head;
+
+ while ((cur = next)) {
+ next = next->next;
+ jffs_free_fm(cur);
+ }
+ put_mtd_device(fmc->mtd);
+ kfree(fmc);
+ DJM(no_jffs_fmcontrol--);
+ }
+}
+
+
+/* This function returns the size of the first chunk of free space on the
+ flash memory. This function will return something nonzero if the flash
+ memory contains any free space. */
+__u32
+jffs_free_size1(struct jffs_fmcontrol *fmc)
+{
+ __u32 head;
+ __u32 tail;
+ __u32 end = fmc->flash_size;
+
+ if (!fmc->head) {
+ /* There is nothing on the flash. */
+ return fmc->flash_size;
+ }
+
+ /* Compute the beginning and ending of the contents of the flash. */
+ head = fmc->head->offset;
+ tail = fmc->tail->offset + fmc->tail->size;
+ if (tail == end) {
+ tail = 0;
+ }
+ ASSERT(else if (tail > end) {
+ printk(KERN_WARNING "jffs_free_size1(): tail > end\n");
+ tail = 0;
+ });
+
+ if (head <= tail) {
+ return end - tail;
+ }
+ else {
+ return head - tail;
+ }
+}
+
+/* This function will return something nonzero in case there are two free
+ areas on the flash. Like this:
+
+ +----------------+------------------+----------------+
+ | FREE 1 | USED / DIRTY | FREE 2 |
+ +----------------+------------------+----------------+
+ fmc->head -----^
+ fmc->tail ------------------------^
+
+ The value returned, will be the size of the first empty area on the
+ flash, in this case marked "FREE 1". */
+__u32
+jffs_free_size2(struct jffs_fmcontrol *fmc)
+{
+ if (fmc->head) {
+ __u32 head = fmc->head->offset;
+ __u32 tail = fmc->tail->offset + fmc->tail->size;
+ if (tail == fmc->flash_size) {
+ tail = 0;
+ }
+
+ if (tail >= head) {
+ return head;
+ }
+ }
+ return 0;
+}
+
+
+/* Allocate a chunk of flash memory. If there is enough space on the
+ device, a reference to the associated node is stored in the jffs_fm
+ struct. */
+int
+jffs_fmalloc(struct jffs_fmcontrol *fmc, __u32 size, struct jffs_node *node,
+ struct jffs_fm **result)
+{
+ struct jffs_fm *fm;
+ __u32 free_chunk_size1;
+ __u32 free_chunk_size2;
+
+ D2(printk("jffs_fmalloc(): fmc = 0x%p, size = %d, "
+ "node = 0x%p\n", fmc, size, node));
+
+ *result = 0;
+
+ if (!(fm = jffs_alloc_fm())) {
+ D(printk("jffs_fmalloc(): kmalloc() failed! (fm)\n"));
+ return -ENOMEM;
+ }
+
+ free_chunk_size1 = jffs_free_size1(fmc);
+ free_chunk_size2 = jffs_free_size2(fmc);
+ if (free_chunk_size1 + free_chunk_size2 != fmc->free_size) {
+ printk(KERN_WARNING "Free size accounting screwed\n");
+ printk(KERN_WARNING "free_chunk_size1 == 0x%x, free_chunk_size2 == 0x%x, fmc->free_size == 0x%x\n", free_chunk_size1, free_chunk_size2, fmc->free_size);
+ }
+
+ D3(printk("jffs_fmalloc(): free_chunk_size1 = %u, "
+ "free_chunk_size2 = %u\n",
+ free_chunk_size1, free_chunk_size2));
+
+ if (size <= free_chunk_size1) {
+ if (!(fm->nodes = (struct jffs_node_ref *)
+ kmalloc(sizeof(struct jffs_node_ref),
+ GFP_KERNEL))) {
+ D(printk("jffs_fmalloc(): kmalloc() failed! "
+ "(node_ref)\n"));
+ jffs_free_fm(fm);
+ return -ENOMEM;
+ }
+ DJM(no_jffs_node_ref++);
+ fm->nodes->node = node;
+ fm->nodes->next = 0;
+ if (fmc->tail) {
+ fm->offset = fmc->tail->offset + fmc->tail->size;
+ if (fm->offset == fmc->flash_size) {
+ fm->offset = 0;
+ }
+ ASSERT(else if (fm->offset > fmc->flash_size) {
+ printk(KERN_WARNING "jffs_fmalloc(): "
+ "offset > flash_end\n");
+ fm->offset = 0;
+ });
+ }
+ else {
+ /* There don't have to be files in the file
+ system yet. */
+ fm->offset = 0;
+ }
+ fm->size = size;
+ fmc->free_size -= size;
+ fmc->used_size += size;
+ }
+ else if (size > free_chunk_size2) {
+ printk(KERN_WARNING "JFFS: Tried to allocate a too "
+ "large flash memory chunk. (size = %u)\n", size);
+ jffs_free_fm(fm);
+ return -ENOSPC;
+ }
+ else {
+ fm->offset = fmc->tail->offset + fmc->tail->size;
+ fm->size = free_chunk_size1;
+ fm->nodes = 0;
+ fmc->free_size -= fm->size;
+ fmc->dirty_size += fm->size; /* Changed by simonk. This seemingly fixes a
+ bug that caused infinite garbage collection.
+ It previously set fmc->dirty_size to size (which is the
+ size of the requested chunk).
+ */
+ }
+
+ fm->next = 0;
+ if (!fmc->head) {
+ fm->prev = 0;
+ fmc->head = fm;
+ fmc->tail = fm;
+ }
+ else {
+ fm->prev = fmc->tail;
+ fmc->tail->next = fm;
+ fmc->tail = fm;
+ }
+
+ D3(jffs_print_fmcontrol(fmc));
+ D3(jffs_print_fm(fm));
+ *result = fm;
+ return 0;
+}
+
+
+/* The on-flash space is not needed anymore by the passed node. Remove
+ the reference to the node from the node list. If the data chunk in
+ the flash memory isn't used by any more nodes anymore (fm->nodes == 0),
+ then mark that chunk as dirty. */
+int
+jffs_fmfree(struct jffs_fmcontrol *fmc, struct jffs_fm *fm, struct jffs_node *node)
+{
+ struct jffs_node_ref *ref;
+ struct jffs_node_ref *prev;
+ ASSERT(int del = 0);
+
+ D2(printk("jffs_fmfree(): node->ino = %u, node->version = %u\n",
+ node->ino, node->version));
+
+ ASSERT(if (!fmc || !fm || !fm->nodes) {
+ printk(KERN_ERR "jffs_fmfree(): fmc: 0x%p, fm: 0x%p, "
+ "fm->nodes: 0x%p\n",
+ fmc, fm, (fm ? fm->nodes : 0));
+ return -1;
+ });
+
+ /* Find the reference to the node that is going to be removed
+ and remove it. */
+ for (ref = fm->nodes, prev = 0; ref; ref = ref->next) {
+ if (ref->node == node) {
+ if (prev) {
+ prev->next = ref->next;
+ }
+ else {
+ fm->nodes = ref->next;
+ }
+ kfree(ref);
+ DJM(no_jffs_node_ref--);
+ ASSERT(del = 1);
+ break;
+ }
+ prev = ref;
+ }
+
+ /* If the data chunk in the flash memory isn't used anymore
+ just mark it as obsolete. */
+ if (!fm->nodes) {
+ /* No node uses this chunk so let's remove it. */
+ fmc->used_size -= fm->size;
+ fmc->dirty_size += fm->size;
+#if defined(JFFS_MARK_OBSOLETE) && JFFS_MARK_OBSOLETE
+ if (jffs_mark_obsolete(fmc, fm->offset) < 0) {
+ D1(printk("jffs_fmfree(): Failed to mark an on-flash "
+ "node obsolete!\n"));
+ return -1;
+ }
+#endif
+ }
+
+ ASSERT(if (!del) {
+ printk(KERN_WARNING "***jffs_fmfree(): "
+ "Didn't delete any node reference!\n");
+ });
+
+ return 0;
+}
+
+
+/* This allocation function is used during the initialization of
+ the file system. */
+struct jffs_fm *
+jffs_fmalloced(struct jffs_fmcontrol *fmc, __u32 offset, __u32 size,
+ struct jffs_node *node)
+{
+ struct jffs_fm *fm;
+
+ D3(printk("jffs_fmalloced()\n"));
+
+ if (!(fm = jffs_alloc_fm())) {
+ D(printk("jffs_fmalloced(0x%p, %u, %u, 0x%p): failed!\n",
+ fmc, offset, size, node));
+ return 0;
+ }
+ fm->offset = offset;
+ fm->size = size;
+ fm->prev = 0;
+ fm->next = 0;
+ fm->nodes = 0;
+ if (node) {
+ /* `node' exists and it should be associated with the
+ jffs_fm structure `fm'. */
+ if (!(fm->nodes = (struct jffs_node_ref *)
+ kmalloc(sizeof(struct jffs_node_ref),
+ GFP_KERNEL))) {
+ D(printk("jffs_fmalloced(): !fm->nodes\n"));
+ jffs_free_fm(fm);
+ return 0;
+ }
+ DJM(no_jffs_node_ref++);
+ fm->nodes->node = node;
+ fm->nodes->next = 0;
+ fmc->used_size += size;
+ fmc->free_size -= size;
+ }
+ else {
+ /* If there is no node, then this is just a chunk of dirt. */
+ fmc->dirty_size += size;
+ fmc->free_size -= size;
+ }
+
+ if (fmc->head_extra) {
+ fm->prev = fmc->tail_extra;
+ fmc->tail_extra->next = fm;
+ fmc->tail_extra = fm;
+ }
+ else if (!fmc->head) {
+ fmc->head = fm;
+ fmc->tail = fm;
+ }
+ else if (fmc->tail->offset + fmc->tail->size < offset) {
+ fmc->head_extra = fm;
+ fmc->tail_extra = fm;
+ }
+ else {
+ fm->prev = fmc->tail;
+ fmc->tail->next = fm;
+ fmc->tail = fm;
+ }
+ D3(jffs_print_fmcontrol(fmc));
+ D3(jffs_print_fm(fm));
+ return fm;
+}
+
+
+/* Add a new node to an already existing jffs_fm struct. */
+int
+jffs_add_node(struct jffs_node *node)
+{
+ struct jffs_node_ref *ref;
+
+ D3(printk("jffs_add_node(): ino = %u\n", node->ino));
+
+ ref = (struct jffs_node_ref *)kmalloc(sizeof(struct jffs_node_ref),
+ GFP_KERNEL);
+ if (!ref)
+ return -ENOMEM;
+
+ DJM(no_jffs_node_ref++);
+ ref->node = node;
+ ref->next = node->fm->nodes;
+ node->fm->nodes = ref;
+ return 0;
+}
+
+
+/* Free a part of some allocated space. */
+void
+jffs_fmfree_partly(struct jffs_fmcontrol *fmc, struct jffs_fm *fm, __u32 size)
+{
+ D1(printk("***jffs_fmfree_partly(): fm = 0x%p, fm->nodes = 0x%p, "
+ "fm->nodes->node->ino = %u, size = %u\n",
+ fm, (fm ? fm->nodes : 0),
+ (!fm ? 0 : (!fm->nodes ? 0 : fm->nodes->node->ino)), size));
+
+ if (fm->nodes) {
+ kfree(fm->nodes);
+ DJM(no_jffs_node_ref--);
+ fm->nodes = 0;
+ }
+ fmc->used_size -= fm->size;
+ if (fm == fmc->tail) {
+ fm->size -= size;
+ fmc->free_size += size;
+ }
+ fmc->dirty_size += fm->size;
+}
+
+
+/* Find the jffs_fm struct that contains the end of the data chunk that
+ begins at the logical beginning of the flash memory and spans `size'
+ bytes. If we want to erase a sector of the flash memory, we use this
+ function to find where the sector limit cuts a chunk of data. */
+struct jffs_fm *
+jffs_cut_node(struct jffs_fmcontrol *fmc, __u32 size)
+{
+ struct jffs_fm *fm;
+ __u32 pos = 0;
+
+ if (size == 0) {
+ return 0;
+ }
+
+ ASSERT(if (!fmc) {
+ printk(KERN_ERR "jffs_cut_node(): fmc == NULL\n");
+ return 0;
+ });
+
+ fm = fmc->head;
+
+ while (fm) {
+ pos += fm->size;
+ if (pos < size) {
+ fm = fm->next;
+ }
+ else if (pos > size) {
+ break;
+ }
+ else {
+ fm = 0;
+ break;
+ }
+ }
+
+ return fm;
+}
+
+
+/* Move the head of the fmc structures and delete the obsolete parts. */
+void
+jffs_sync_erase(struct jffs_fmcontrol *fmc, int erased_size)
+{
+ struct jffs_fm *fm;
+ struct jffs_fm *del;
+
+ ASSERT(if (!fmc) {
+ printk(KERN_ERR "jffs_sync_erase(): fmc == NULL\n");
+ return;
+ });
+
+ fmc->dirty_size -= erased_size;
+ fmc->free_size += erased_size;
+
+ for (fm = fmc->head; fm && (erased_size > 0);) {
+ if (erased_size >= fm->size) {
+ erased_size -= fm->size;
+ del = fm;
+ fm = fm->next;
+ fm->prev = 0;
+ fmc->head = fm;
+ jffs_free_fm(del);
+ }
+ else {
+ fm->size -= erased_size;
+ fm->offset += erased_size;
+ break;
+ }
+ }
+}
+
+
+/* Return the oldest used node in the flash memory. */
+struct jffs_node *
+jffs_get_oldest_node(struct jffs_fmcontrol *fmc)
+{
+ struct jffs_fm *fm;
+ struct jffs_node_ref *nref;
+ struct jffs_node *node = 0;
+
+ ASSERT(if (!fmc) {
+ printk(KERN_ERR "jffs_get_oldest_node(): fmc == NULL\n");
+ return 0;
+ });
+
+ for (fm = fmc->head; fm && !fm->nodes; fm = fm->next);
+
+ if (!fm) {
+ return 0;
+ }
+
+ /* The oldest node is the last one in the reference list. This list
+ shouldn't be too long; just one or perhaps two elements. */
+ for (nref = fm->nodes; nref; nref = nref->next) {
+ node = nref->node;
+ }
+
+ D2(printk("jffs_get_oldest_node(): ino = %u, version = %u\n",
+ (node ? node->ino : 0), (node ? node->version : 0)));
+
+ return node;
+}
+
+
+#if defined(JFFS_MARK_OBSOLETE) && JFFS_MARK_OBSOLETE
+
+/* Mark an on-flash node as obsolete.
+
+ Note that this is just an optimization that isn't necessary for the
+ filesystem to work. */
+
+static int
+jffs_mark_obsolete(struct jffs_fmcontrol *fmc, __u32 fm_offset)
+{
+ /* The `accurate_pos' holds the position of the accurate byte
+ in the jffs_raw_inode structure that we are going to mark
+ as obsolete. */
+ __u32 accurate_pos = fm_offset + JFFS_RAW_INODE_ACCURATE_OFFSET;
+ unsigned char zero = 0x00;
+ size_t len;
+
+ D3(printk("jffs_mark_obsolete(): accurate_pos = %u\n", accurate_pos));
+ ASSERT(if (!fmc) {
+ printk(KERN_ERR "jffs_mark_obsolete(): fmc == NULL\n");
+ return -1;
+ });
+
+ /* Write 0x00 to the raw inode's accurate member. Don't care
+ about the return value. */
+ MTD_WRITE(fmc->mtd, accurate_pos, 1, &len, &zero);
+ return 0;
+}
+
+#endif /* JFFS_MARK_OBSOLETE */
+
+/* check if it's possible to erase the wanted range, and if not, return
+ * the range that IS erasable, or a negative error code.
+ */
+long
+jffs_flash_erasable_size(struct mtd_info *mtd, __u32 offset, __u32 size)
+{
+ u_long ssize;
+
+ /* assume that sector size for a partition is constant even
+ * if it spans more than one chip (you usually put the same
+ * type of chips in a system)
+ */
+
+ ssize = mtd->erasesize;
+
+ if (offset % ssize) {
+ printk(KERN_WARNING "jffs_flash_erasable_size() given non-aligned offset %x (erasesize %lx)\n", offset, ssize);
+ /* The offset is not sector size aligned. */
+ return -1;
+ }
+ else if (offset > mtd->size) {
+ printk(KERN_WARNING "jffs_flash_erasable_size given offset off the end of device (%x > %x)\n", offset, mtd->size);
+ return -2;
+ }
+ else if (offset + size > mtd->size) {
+ printk(KERN_WARNING "jffs_flash_erasable_size() given length which runs off the end of device (ofs %x + len %x = %x, > %x)\n", offset,size, offset+size, mtd->size);
+ return -3;
+ }
+
+ return (size / ssize) * ssize;
+}
+
+
+/* How much dirty flash memory is possible to erase at the moment? */
+long
+jffs_erasable_size(struct jffs_fmcontrol *fmc)
+{
+ struct jffs_fm *fm;
+ __u32 size = 0;
+ long ret;
+
+ ASSERT(if (!fmc) {
+ printk(KERN_ERR "jffs_erasable_size(): fmc = NULL\n");
+ return -1;
+ });
+
+ if (!fmc->head) {
+ /* The flash memory is totally empty. No nodes. No dirt.
+ Just return. */
+ return 0;
+ }
+
+ /* Calculate how much space that is dirty. */
+ for (fm = fmc->head; fm && !fm->nodes; fm = fm->next) {
+ if (size && fm->offset == 0) {
+ /* We have reached the beginning of the flash. */
+ break;
+ }
+ size += fm->size;
+ }
+
+ /* Someone's signature contained this:
+ There's a fine line between fishing and just standing on
+ the shore like an idiot... */
+ ret = jffs_flash_erasable_size(fmc->mtd, fmc->head->offset, size);
+
+ ASSERT(if (ret < 0) {
+ printk("jffs_erasable_size: flash_erasable_size() "
+ "returned something less than zero (%ld).\n", ret);
+ printk("jffs_erasable_size: offset = 0x%08x\n",
+ fmc->head->offset);
+ });
+
+ /* If there is dirt on the flash (which is the reason to why
+ this function was called in the first place) but no space is
+ possible to erase right now, the initial part of the list of
+ jffs_fm structs, that hold place for dirty space, could perhaps
+ be shortened. The list's initial "dirty" elements are merged
+ into just one large dirty jffs_fm struct. This operation must
+ only be performed if nothing is possible to erase. Otherwise,
+ jffs_clear_end_of_node() won't work as expected. */
+ if (ret == 0) {
+ struct jffs_fm *head = fmc->head;
+ struct jffs_fm *del;
+ /* While there are two dirty nodes beside each other.*/
+ while (head->nodes == 0
+ && head->next
+ && head->next->nodes == 0) {
+ del = head->next;
+ head->size += del->size;
+ head->next = del->next;
+ if (del->next) {
+ del->next->prev = head;
+ }
+ jffs_free_fm(del);
+ }
+ }
+
+ return (ret >= 0 ? ret : 0);
+}
+
+struct jffs_fm *jffs_alloc_fm(void)
+{
+ struct jffs_fm *fm;
+
+ fm = kmem_cache_alloc(fm_cache,GFP_KERNEL);
+ DJM(if (fm) no_jffs_fm++;);
+
+ return fm;
+}
+
+void jffs_free_fm(struct jffs_fm *n)
+{
+ kmem_cache_free(fm_cache,n);
+ DJM(no_jffs_fm--);
+}
+
+
+
+struct jffs_node *jffs_alloc_node(void)
+{
+ struct jffs_node *n;
+
+ n = (struct jffs_node *)kmem_cache_alloc(node_cache,GFP_KERNEL);
+ if(n != NULL)
+ no_jffs_node++;
+ return n;
+}
+
+void jffs_free_node(struct jffs_node *n)
+{
+ kmem_cache_free(node_cache,n);
+ no_jffs_node--;
+}
+
+
+int jffs_get_node_inuse(void)
+{
+ return no_jffs_node;
+}
+
+void
+jffs_print_fmcontrol(struct jffs_fmcontrol *fmc)
+{
+ D(printk("struct jffs_fmcontrol: 0x%p\n", fmc));
+ D(printk("{\n"));
+ D(printk(" %u, /* flash_size */\n", fmc->flash_size));
+ D(printk(" %u, /* used_size */\n", fmc->used_size));
+ D(printk(" %u, /* dirty_size */\n", fmc->dirty_size));
+ D(printk(" %u, /* free_size */\n", fmc->free_size));
+ D(printk(" %u, /* sector_size */\n", fmc->sector_size));
+ D(printk(" %u, /* min_free_size */\n", fmc->min_free_size));
+ D(printk(" %u, /* max_chunk_size */\n", fmc->max_chunk_size));
+ D(printk(" 0x%p, /* mtd */\n", fmc->mtd));
+ D(printk(" 0x%p, /* head */ "
+ "(head->offset = 0x%08x)\n",
+ fmc->head, (fmc->head ? fmc->head->offset : 0)));
+ D(printk(" 0x%p, /* tail */ "
+ "(tail->offset + tail->size = 0x%08x)\n",
+ fmc->tail,
+ (fmc->tail ? fmc->tail->offset + fmc->tail->size : 0)));
+ D(printk(" 0x%p, /* head_extra */\n", fmc->head_extra));
+ D(printk(" 0x%p, /* tail_extra */\n", fmc->tail_extra));
+ D(printk("}\n"));
+}
+
+void
+jffs_print_fm(struct jffs_fm *fm)
+{
+ D(printk("struct jffs_fm: 0x%p\n", fm));
+ D(printk("{\n"));
+ D(printk(" 0x%08x, /* offset */\n", fm->offset));
+ D(printk(" %u, /* size */\n", fm->size));
+ D(printk(" 0x%p, /* prev */\n", fm->prev));
+ D(printk(" 0x%p, /* next */\n", fm->next));
+ D(printk(" 0x%p, /* nodes */\n", fm->nodes));
+ D(printk("}\n"));
+}
+
+void
+jffs_print_node_ref(struct jffs_node_ref *ref)
+{
+ D(printk("struct jffs_node_ref: 0x%p\n", ref));
+ D(printk("{\n"));
+ D(printk(" 0x%p, /* node */\n", ref->node));
+ D(printk(" 0x%p, /* next */\n", ref->next));
+ D(printk("}\n"));
+}
diff --git a/fs/jffs/jffs_fm.h b/fs/jffs/jffs_fm.h
new file mode 100644
index 00000000000000..4aea0554e6c728
--- /dev/null
+++ b/fs/jffs/jffs_fm.h
@@ -0,0 +1,150 @@
+/*
+ * JFFS -- Journaling Flash File System, Linux implementation.
+ *
+ * Copyright (C) 1999, 2000 Axis Communications AB.
+ *
+ * Created by Finn Hakansson <finn@axis.com>.
+ *
+ * This 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 of the License, or
+ * (at your option) any later version.
+ *
+ * $Id: jffs_fm.h,v 1.13 2001/01/11 12:03:25 dwmw2 Exp $
+ *
+ * Ported to Linux 2.3.x and MTD:
+ * Copyright (C) 2000 Alexander Larsson (alex@cendio.se), Cendio Systems AB
+ *
+ */
+
+#ifndef __LINUX_JFFS_FM_H__
+#define __LINUX_JFFS_FM_H__
+
+#include <linux/types.h>
+#include <linux/jffs.h>
+#include <linux/mtd/mtd.h>
+#include <linux/config.h>
+
+/* The alignment between two nodes in the flash memory. */
+#define JFFS_ALIGN_SIZE 4
+
+/* Mark the on-flash space as obsolete when appropriate. */
+#define JFFS_MARK_OBSOLETE 0
+
+#ifndef CONFIG_JFFS_FS_VERBOSE
+#define CONFIG_JFFS_FS_VERBOSE 1
+#endif
+
+#if CONFIG_JFFS_FS_VERBOSE > 0
+#define D(x) x
+#define D1(x) D(x)
+#else
+#define D(x)
+#define D1(x)
+#endif
+
+#if CONFIG_JFFS_FS_VERBOSE > 1
+#define D2(x) D(x)
+#else
+#define D2(x)
+#endif
+
+#if CONFIG_JFFS_FS_VERBOSE > 2
+#define D3(x) D(x)
+#else
+#define D3(x)
+#endif
+
+#define ASSERT(x) x
+
+/* How many padding bytes should be inserted between two chunks of data
+ on the flash? */
+#define JFFS_GET_PAD_BYTES(size) ( (JFFS_ALIGN_SIZE-1) & -(__u32)(size) )
+#define JFFS_PAD(size) ( (size + (JFFS_ALIGN_SIZE-1)) & ~(JFFS_ALIGN_SIZE-1) )
+
+
+
+void jffs_free_fm(struct jffs_fm *n);
+struct jffs_fm *jffs_alloc_fm(void);
+
+
+struct jffs_node_ref
+{
+ struct jffs_node *node;
+ struct jffs_node_ref *next;
+};
+
+
+/* The struct jffs_fm represents a chunk of data in the flash memory. */
+struct jffs_fm
+{
+ __u32 offset;
+ __u32 size;
+ struct jffs_fm *prev;
+ struct jffs_fm *next;
+ struct jffs_node_ref *nodes; /* USED if != 0. */
+};
+
+struct jffs_fmcontrol
+{
+ __u32 flash_size;
+ __u32 used_size;
+ __u32 dirty_size;
+ __u32 free_size;
+ __u32 sector_size;
+ __u32 min_free_size; /* The minimum free space needed to be able
+ to perform garbage collections. */
+ __u32 max_chunk_size; /* The maximum size of a chunk of data. */
+ struct mtd_info *mtd;
+ struct jffs_control *c;
+ struct jffs_fm *head;
+ struct jffs_fm *tail;
+ struct jffs_fm *head_extra;
+ struct jffs_fm *tail_extra;
+ struct semaphore biglock;
+};
+
+/* Notice the two members head_extra and tail_extra in the jffs_control
+ structure above. Those are only used during the scanning of the flash
+ memory; while the file system is being built. If the data in the flash
+ memory is organized like
+
+ +----------------+------------------+----------------+
+ | USED / DIRTY | FREE | USED / DIRTY |
+ +----------------+------------------+----------------+
+
+ then the scan is split in two parts. The first scanned part of the
+ flash memory is organized through the members head and tail. The
+ second scanned part is organized with head_extra and tail_extra. When
+ the scan is completed, the two lists are merged together. The jffs_fm
+ struct that head_extra references is the logical beginning of the
+ flash memory so it will be referenced by the head member. */
+
+
+
+struct jffs_fmcontrol *jffs_build_begin(struct jffs_control *c, kdev_t dev);
+void jffs_build_end(struct jffs_fmcontrol *fmc);
+void jffs_cleanup_fmcontrol(struct jffs_fmcontrol *fmc);
+
+int jffs_fmalloc(struct jffs_fmcontrol *fmc, __u32 size,
+ struct jffs_node *node, struct jffs_fm **result);
+int jffs_fmfree(struct jffs_fmcontrol *fmc, struct jffs_fm *fm,
+ struct jffs_node *node);
+
+__u32 jffs_free_size1(struct jffs_fmcontrol *fmc);
+__u32 jffs_free_size2(struct jffs_fmcontrol *fmc);
+void jffs_sync_erase(struct jffs_fmcontrol *fmc, int erased_size);
+struct jffs_fm *jffs_cut_node(struct jffs_fmcontrol *fmc, __u32 size);
+struct jffs_node *jffs_get_oldest_node(struct jffs_fmcontrol *fmc);
+long jffs_erasable_size(struct jffs_fmcontrol *fmc);
+struct jffs_fm *jffs_fmalloced(struct jffs_fmcontrol *fmc, __u32 offset,
+ __u32 size, struct jffs_node *node);
+int jffs_add_node(struct jffs_node *node);
+void jffs_fmfree_partly(struct jffs_fmcontrol *fmc, struct jffs_fm *fm,
+ __u32 size);
+
+void jffs_print_fmcontrol(struct jffs_fmcontrol *fmc);
+void jffs_print_fm(struct jffs_fm *fm);
+void jffs_print_node_ref(struct jffs_node_ref *ref);
+
+#endif /* __LINUX_JFFS_FM_H__ */
diff --git a/fs/jffs/jffs_proc.c b/fs/jffs/jffs_proc.c
new file mode 100644
index 00000000000000..22107954fb0f7d
--- /dev/null
+++ b/fs/jffs/jffs_proc.c
@@ -0,0 +1,269 @@
+/*
+ * JFFS -- Journaling Flash File System, Linux implementation.
+ *
+ * Copyright (C) 2000 Axis Communications AB.
+ *
+ * Created by Simon Kagstrom <simonk@axis.com>.
+ *
+ * $Id: jffs_proc.c,v 1.5 2001/06/02 14:34:55 dwmw2 Exp $
+ *
+ * This 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 of the License, or
+ * (at your option) any later version.
+ *
+ * Overview:
+ * This file defines JFFS partition entries in the proc file system.
+ *
+ * TODO:
+ * Create some more proc files for different kinds of info, i.e. statistics
+ * about written and read bytes, number of calls to different routines,
+ * reports about failures.
+ */
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/jffs.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include "jffs_fm.h"
+#include "jffs_proc.h"
+
+/*
+ * Structure for a JFFS partition in the system
+ */
+struct jffs_partition_dir {
+ struct jffs_control *c;
+ struct proc_dir_entry *part_root;
+ struct proc_dir_entry *part_info;
+ struct proc_dir_entry *part_layout;
+ struct jffs_partition_dir *next;
+};
+
+/*
+ * Structure for top-level entry in '/proc/fs' directory
+ */
+struct proc_dir_entry *jffs_proc_root;
+
+/*
+ * Linked list of 'jffs_partition_dirs' to help us track
+ * the mounted JFFS partitions in the system
+ */
+static struct jffs_partition_dir *jffs_part_dirs = 0;
+
+/*
+ * Read functions for entries
+ */
+static int jffs_proc_info_read(char *page, char **start, off_t off,
+ int count, int *eof, void *data);
+static int jffs_proc_layout_read (char *page, char **start, off_t off,
+ int count, int *eof, void *data);
+
+
+/*
+ * Register a JFFS partition directory (called upon mount)
+ */
+int jffs_register_jffs_proc_dir(kdev_t dev, struct jffs_control *c)
+{
+ struct jffs_partition_dir *part_dir;
+ struct proc_dir_entry *part_info = 0;
+ struct proc_dir_entry *part_layout = 0;
+ struct proc_dir_entry *part_root = 0;
+
+ /* Allocate structure for local JFFS partition table */
+ if (!(part_dir = (struct jffs_partition_dir *)
+ kmalloc (sizeof (struct jffs_partition_dir), GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+
+ /* Create entry for this partition */
+ if ((part_root = create_proc_entry (kdevname(dev),
+ S_IFDIR | S_IRUGO | S_IXUGO, jffs_proc_root))) {
+ part_root->read_proc = jffs_proc_info_read;
+ part_root->data = (void *) c;
+ }
+ else {
+ kfree (part_dir);
+ return -ENOMEM;
+ }
+
+ /* Create entry for 'info' file */
+ if ((part_info = create_proc_entry ("info", 0, part_root))) {
+ part_info->read_proc = jffs_proc_info_read;
+ part_info->data = (void *) c;
+ }
+ else {
+ remove_proc_entry (part_root->name, jffs_proc_root);
+ kfree (part_dir);
+ return -ENOMEM;
+ }
+
+ /* Create entry for 'layout' file */
+ if ((part_layout = create_proc_entry ("layout", 0, part_root))) {
+ part_layout->read_proc = jffs_proc_layout_read;
+ part_layout->data = (void *) c;
+ }
+ else {
+ remove_proc_entry (part_info->name, part_root);
+ remove_proc_entry (part_root->name, jffs_proc_root);
+ kfree (part_dir);
+ return -ENOMEM;
+ }
+
+ /* Fill in structure for table and insert in the list */
+ part_dir->c = c;
+ part_dir->part_root = part_root;
+ part_dir->part_info = part_info;
+ part_dir->part_layout = part_layout;
+ part_dir->next = jffs_part_dirs;
+ jffs_part_dirs = part_dir;
+
+ /* Return happy */
+ return 0;
+}
+
+
+/*
+ * Unregister a JFFS partition directory (called at umount)
+ */
+int jffs_unregister_jffs_proc_dir(struct jffs_control *c)
+{
+ struct jffs_partition_dir *part_dir = jffs_part_dirs;
+ struct jffs_partition_dir *prev_part_dir = 0;
+
+ while (part_dir) {
+ if (part_dir->c == c) {
+ /* Remove entries for partition */
+ remove_proc_entry (part_dir->part_info->name,
+ part_dir->part_root);
+ remove_proc_entry (part_dir->part_layout->name,
+ part_dir->part_root);
+ remove_proc_entry (part_dir->part_root->name,
+ jffs_proc_root);
+
+ /* Remove entry from list */
+ if (prev_part_dir)
+ prev_part_dir->next = part_dir->next;
+ else
+ jffs_part_dirs = part_dir->next;
+
+ /*
+ * Check to see if this is the last one
+ * and remove the entry from '/proc/fs'
+ * if it is.
+ */
+ if (jffs_part_dirs == part_dir->next)
+#if LINUX_VERSION_CODE < 0x020300
+ remove_proc_entry ("jffs", &proc_root_fs);
+#else
+ remove_proc_entry ("jffs", proc_root_fs);
+#endif
+
+ /* Free memory for entry */
+ kfree(part_dir);
+
+ /* Return happy */
+ return 0;
+ }
+
+ /* Move to next entry */
+ prev_part_dir = part_dir;
+ part_dir = part_dir->next;
+ }
+
+ /* Return unhappy */
+ return -1;
+}
+
+
+/*
+ * Read a JFFS partition's `info' file
+ */
+static int jffs_proc_info_read (char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ struct jffs_control *c = (struct jffs_control *) data;
+ int len = 0;
+
+ /* Get information on the parition */
+ len += sprintf (page,
+ "partition size: %08lX (%u)\n"
+ "sector size: %08lX (%u)\n"
+ "used size: %08lX (%u)\n"
+ "dirty size: %08lX (%u)\n"
+ "free size: %08lX (%u)\n\n",
+ (unsigned long) c->fmc->flash_size, c->fmc->flash_size,
+ (unsigned long) c->fmc->sector_size, c->fmc->sector_size,
+ (unsigned long) c->fmc->used_size, c->fmc->used_size,
+ (unsigned long) c->fmc->dirty_size, c->fmc->dirty_size,
+ (unsigned long) (c->fmc->flash_size -
+ (c->fmc->used_size + c->fmc->dirty_size)),
+ c->fmc->flash_size - (c->fmc->used_size + c->fmc->dirty_size));
+
+ /* We're done */
+ *eof = 1;
+
+ /* Return length */
+ return len;
+}
+
+
+/*
+ * Read a JFFS partition's `layout' file
+ */
+static int jffs_proc_layout_read (char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ struct jffs_control *c = (struct jffs_control *) data;
+ struct jffs_fm *fm = 0;
+ struct jffs_fm *last_fm = 0;
+ int len = 0;
+
+ /* Get the first item in the list */
+ fm = c->fmc->head;
+
+ /* Print free space */
+ if (fm && fm->offset) {
+ len += sprintf (page, "00000000 %08lX free\n",
+ (unsigned long) fm->offset);
+ }
+
+ /* Loop through all of the flash control structures */
+ while (fm && (len < (off + count))) {
+ if (fm->nodes) {
+ len += sprintf (page + len,
+ "%08lX %08lX ino=%08lX, ver=%08lX\n",
+ (unsigned long) fm->offset,
+ (unsigned long) fm->size,
+ (unsigned long) fm->nodes->node->ino,
+ (unsigned long) fm->nodes->node->version);
+ }
+ else {
+ len += sprintf (page + len,
+ "%08lX %08lX dirty\n",
+ (unsigned long) fm->offset,
+ (unsigned long) fm->size);
+ }
+ last_fm = fm;
+ fm = fm->next;
+ }
+
+ /* Print free space */
+ if ((len < (off + count)) && last_fm
+ && (last_fm->offset < c->fmc->flash_size)) {
+ len += sprintf (page + len,
+ "%08lX %08lX free\n",
+ (unsigned long) last_fm->offset +
+ last_fm->size,
+ (unsigned long) (c->fmc->flash_size -
+ (last_fm->offset + last_fm->size)));
+ }
+
+ /* We're done */
+ *eof = 1;
+
+ /* Return length */
+ return len;
+}
diff --git a/fs/jffs/jffs_proc.h b/fs/jffs/jffs_proc.h
new file mode 100644
index 00000000000000..bf36c9d68cf679
--- /dev/null
+++ b/fs/jffs/jffs_proc.h
@@ -0,0 +1,28 @@
+/*
+ * JFFS -- Journaling Flash File System, Linux implementation.
+ *
+ * Copyright (C) 2000 Axis Communications AB.
+ *
+ * Created by Simon Kagstrom <simonk@axis.com>.
+ *
+ * This 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 of the License, or
+ * (at your option) any later version.
+ *
+ * $Id: jffs_proc.h,v 1.2 2000/11/15 22:04:12 sjhill Exp $
+ */
+
+/* jffs_proc.h defines a structure for inclusion in the proc-file system. */
+#ifndef __LINUX_JFFS_PROC_H__
+#define __LINUX_JFFS_PROC_H__
+
+#include <linux/proc_fs.h>
+
+/* The proc_dir_entry for jffs (defined in jffs_proc.c). */
+extern struct proc_dir_entry *jffs_proc_root;
+
+int jffs_register_jffs_proc_dir(kdev_t dev, struct jffs_control *c);
+int jffs_unregister_jffs_proc_dir(struct jffs_control *c);
+
+#endif /* __LINUX_JFFS_PROC_H__ */
diff --git a/fs/jffs2/Makefile b/fs/jffs2/Makefile
new file mode 100644
index 00000000000000..e9e6e948c9f130
--- /dev/null
+++ b/fs/jffs2/Makefile
@@ -0,0 +1,25 @@
+#
+# Makefile for the linux Journalling Flash FileSystem (JFFS) routines.
+#
+# $Id: Makefile,v 1.25.2.1 2002/10/11 09:04:44 dwmw2 Exp $
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+
+COMPR_OBJS := compr.o compr_rubin.o compr_rtime.o pushpull.o \
+ compr_zlib.o
+JFFS2_OBJS := dir.o file.o ioctl.o nodelist.o malloc.o \
+ read.o nodemgmt.o readinode.o super.o write.o scan.o gc.o \
+ symlink.o build.o erase.o background.o
+
+O_TARGET := jffs2.o
+
+obj-y := $(COMPR_OBJS) $(JFFS2_OBJS)
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
+
diff --git a/fs/jffs2/TODO b/fs/jffs2/TODO
new file mode 100644
index 00000000000000..f5066b10073f7a
--- /dev/null
+++ b/fs/jffs2/TODO
@@ -0,0 +1,20 @@
+$Id: TODO,v 1.3 2001/03/01 23:26:48 dwmw2 Exp $
+
+ - disable compression in commit_write()? Or at least optimise the 'always write
+ whole page' bit.
+ - fix zlib. It's ugly as hell and there are at least three copies in the kernel tree
+ - fine-tune the allocation / GC thresholds
+ - chattr support - turning on/off and tuning compression per-inode
+ - checkpointing (do we need this? scan is quite fast)
+ - make the scan code populate real inodes so read_inode just after
+ mount doesn't have to read the flash twice for large files.
+ Make this a per-inode option, changable with chattr, so you can
+ decide which inodes should be in-core immediately after mount.
+ - stop it depending on a block device. mount(8) needs a change for this.
+ - make it work on NAND flash. We need to know when we can GC
+ deletion dirents, etc. And think about holes/truncation. It can
+ all be done reasonably simply, but it need implementing.
+ - NAND flash will require new dirent/dnode structures on the medium with
+ ECC data in rather than just the CRC we're using ATM.
+ - test, test, test
+
diff --git a/fs/jffs2/background.c b/fs/jffs2/background.c
new file mode 100644
index 00000000000000..b0f01c283de023
--- /dev/null
+++ b/fs/jffs2/background.c
@@ -0,0 +1,183 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ *
+ * The original JFFS, from which the design for JFFS2 was derived,
+ * was designed and implemented by Axis Communications AB.
+ *
+ * The contents of this file are subject to the Red Hat eCos Public
+ * License Version 1.1 (the "Licence"); you may not use this file
+ * except in compliance with the Licence. You may obtain a copy of
+ * the Licence at http://www.redhat.com/
+ *
+ * Software distributed under the Licence is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing rights and
+ * limitations under the Licence.
+ *
+ * The Original Code is JFFS2 - Journalling Flash File System, version 2
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in
+ * which case the provisions of the GPL are applicable instead of the
+ * above. If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use your
+ * version of this file under the RHEPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice and
+ * other provisions required by the GPL. If you do not delete the
+ * provisions above, a recipient may use your version of this file
+ * under either the RHEPL or the GPL.
+ *
+ * $Id: background.c,v 1.16 2001/10/08 09:22:38 dwmw2 Exp $
+ *
+ */
+
+#define __KERNEL_SYSCALLS__
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/unistd.h>
+#include <linux/jffs2.h>
+#include <linux/mtd/mtd.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include "nodelist.h"
+
+
+static int jffs2_garbage_collect_thread(void *);
+static int thread_should_wake(struct jffs2_sb_info *c);
+
+void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c)
+{
+ spin_lock_bh(&c->erase_completion_lock);
+ if (c->gc_task && thread_should_wake(c))
+ send_sig(SIGHUP, c->gc_task, 1);
+ spin_unlock_bh(&c->erase_completion_lock);
+}
+
+/* This must only ever be called when no GC thread is currently running */
+int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c)
+{
+ pid_t pid;
+ int ret = 0;
+
+ if (c->gc_task)
+ BUG();
+
+ init_MUTEX_LOCKED(&c->gc_thread_start);
+ init_completion(&c->gc_thread_exit);
+
+ pid = kernel_thread(jffs2_garbage_collect_thread, c, CLONE_FS|CLONE_FILES);
+ if (pid < 0) {
+ printk(KERN_WARNING "fork failed for JFFS2 garbage collect thread: %d\n", -pid);
+ complete(&c->gc_thread_exit);
+ ret = pid;
+ } else {
+ /* Wait for it... */
+ D1(printk(KERN_DEBUG "JFFS2: Garbage collect thread is pid %d\n", pid));
+ down(&c->gc_thread_start);
+ }
+
+ return ret;
+}
+
+void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c)
+{
+ spin_lock_bh(&c->erase_completion_lock);
+ if (c->gc_task) {
+ D1(printk(KERN_DEBUG "jffs2: Killing GC task %d\n", c->gc_task->pid));
+ send_sig(SIGKILL, c->gc_task, 1);
+ }
+ spin_unlock_bh(&c->erase_completion_lock);
+ wait_for_completion(&c->gc_thread_exit);
+}
+
+static int jffs2_garbage_collect_thread(void *_c)
+{
+ struct jffs2_sb_info *c = _c;
+
+ daemonize();
+ current->tty = NULL;
+ c->gc_task = current;
+ up(&c->gc_thread_start);
+
+ sprintf(current->comm, "jffs2_gcd_mtd%d", c->mtd->index);
+
+ /* FIXME in the 2.2 backport */
+ current->nice = 10;
+
+ for (;;) {
+ spin_lock_irq(&current->sigmask_lock);
+ siginitsetinv (&current->blocked, sigmask(SIGHUP) | sigmask(SIGKILL) | sigmask(SIGSTOP) | sigmask(SIGCONT));
+ recalc_sigpending(current);
+ spin_unlock_irq(&current->sigmask_lock);
+
+ if (!thread_should_wake(c)) {
+ set_current_state (TASK_INTERRUPTIBLE);
+ D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread sleeping...\n"));
+ /* Yes, there's a race here; we checked thread_should_wake() before
+ setting current->state to TASK_INTERRUPTIBLE. But it doesn't
+ matter - We don't care if we miss a wakeup, because the GC thread
+ is only an optimisation anyway. */
+ schedule();
+ }
+
+ if (current->need_resched)
+ schedule();
+
+ /* Put_super will send a SIGKILL and then wait on the sem.
+ */
+ while (signal_pending(current)) {
+ siginfo_t info;
+ unsigned long signr;
+
+ spin_lock_irq(&current->sigmask_lock);
+ signr = dequeue_signal(&current->blocked, &info);
+ spin_unlock_irq(&current->sigmask_lock);
+
+ switch(signr) {
+ case SIGSTOP:
+ D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGSTOP received.\n"));
+ set_current_state(TASK_STOPPED);
+ schedule();
+ break;
+
+ case SIGKILL:
+ D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGKILL received.\n"));
+ spin_lock_bh(&c->erase_completion_lock);
+ c->gc_task = NULL;
+ spin_unlock_bh(&c->erase_completion_lock);
+ complete_and_exit(&c->gc_thread_exit, 0);
+
+ case SIGHUP:
+ D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGHUP received.\n"));
+ break;
+ default:
+ D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): signal %ld received\n", signr));
+
+ }
+ }
+ /* We don't want SIGHUP to interrupt us. STOP and KILL are OK though. */
+ spin_lock_irq(&current->sigmask_lock);
+ siginitsetinv (&current->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP) | sigmask(SIGCONT));
+ recalc_sigpending(current);
+ spin_unlock_irq(&current->sigmask_lock);
+
+ D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): pass\n"));
+ jffs2_garbage_collect_pass(c);
+ }
+}
+
+static int thread_should_wake(struct jffs2_sb_info *c)
+{
+ D1(printk(KERN_DEBUG "thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x\n",
+ c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size));
+ if (c->nr_free_blocks + c->nr_erasing_blocks < JFFS2_RESERVED_BLOCKS_GCTRIGGER &&
+ c->dirty_size > c->sector_size)
+ return 1;
+ else
+ return 0;
+}
diff --git a/fs/jffs2/build.c b/fs/jffs2/build.c
new file mode 100644
index 00000000000000..3686771036f3d2
--- /dev/null
+++ b/fs/jffs2/build.c
@@ -0,0 +1,287 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ *
+ * The original JFFS, from which the design for JFFS2 was derived,
+ * was designed and implemented by Axis Communications AB.
+ *
+ * The contents of this file are subject to the Red Hat eCos Public
+ * License Version 1.1 (the "Licence"); you may not use this file
+ * except in compliance with the Licence. You may obtain a copy of
+ * the Licence at http://www.redhat.com/
+ *
+ * Software distributed under the Licence is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing rights and
+ * limitations under the Licence.
+ *
+ * The Original Code is JFFS2 - Journalling Flash File System, version 2
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in
+ * which case the provisions of the GPL are applicable instead of the
+ * above. If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use your
+ * version of this file under the RHEPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice and
+ * other provisions required by the GPL. If you do not delete the
+ * provisions above, a recipient may use your version of this file
+ * under either the RHEPL or the GPL.
+ *
+ * $Id: build.c,v 1.16.2.3 2003/04/30 09:43:32 dwmw2 Exp $
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/jffs2.h>
+#include <linux/slab.h>
+#include "nodelist.h"
+
+int jffs2_build_inode_pass1(struct jffs2_sb_info *, struct jffs2_inode_cache *);
+int jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode_cache *);
+
+static inline struct jffs2_inode_cache *
+first_inode_chain(int *i, struct jffs2_sb_info *c)
+{
+ for (; *i < INOCACHE_HASHSIZE; (*i)++) {
+ if (c->inocache_list[*i])
+ return c->inocache_list[*i];
+ }
+ return NULL;
+}
+
+static inline struct jffs2_inode_cache *
+next_inode(int *i, struct jffs2_inode_cache *ic, struct jffs2_sb_info *c)
+{
+ /* More in this chain? */
+ if (ic->next)
+ return ic->next;
+ (*i)++;
+ return first_inode_chain(i, c);
+}
+
+#define for_each_inode(i, c, ic) \
+ for (i = 0, ic = first_inode_chain(&i, (c)); \
+ ic; \
+ ic = next_inode(&i, ic, (c)))
+
+/* Scan plan:
+ - Scan physical nodes. Build map of inodes/dirents. Allocate inocaches as we go
+ - Scan directory tree from top down, setting nlink in inocaches
+ - Scan inocaches for inodes with nlink==0
+*/
+int jffs2_build_filesystem(struct jffs2_sb_info *c)
+{
+ int ret;
+ int i;
+ struct jffs2_inode_cache *ic;
+
+ /* First, scan the medium and build all the inode caches with
+ lists of physical nodes */
+
+ c->flags |= JFFS2_SB_FLAG_MOUNTING;
+ ret = jffs2_scan_medium(c);
+ c->flags &= ~JFFS2_SB_FLAG_MOUNTING;
+
+ if (ret)
+ return ret;
+
+ D1(printk(KERN_DEBUG "Scanned flash completely\n"));
+ /* Now build the data map for each inode, marking obsoleted nodes
+ as such, and also increase nlink of any children. */
+ for_each_inode(i, c, ic) {
+ D1(printk(KERN_DEBUG "Pass 1: ino #%u\n", ic->ino));
+ ret = jffs2_build_inode_pass1(c, ic);
+ if (ret) {
+ D1(printk(KERN_WARNING "Eep. jffs2_build_inode_pass1 for ino %d returned %d\n", ic->ino, ret));
+ return ret;
+ }
+ }
+ D1(printk(KERN_DEBUG "Pass 1 complete\n"));
+
+ /* Next, scan for inodes with nlink == 0 and remove them. If
+ they were directories, then decrement the nlink of their
+ children too, and repeat the scan. As that's going to be
+ a fairly uncommon occurrence, it's not so evil to do it this
+ way. Recursion bad. */
+ do {
+ D1(printk(KERN_DEBUG "Pass 2 (re)starting\n"));
+ ret = 0;
+ for_each_inode(i, c, ic) {
+ D1(printk(KERN_DEBUG "Pass 2: ino #%u, nlink %d, ic %p, nodes %p\n", ic->ino, ic->nlink, ic, ic->nodes));
+ if (ic->nlink)
+ continue;
+
+ ret = jffs2_build_remove_unlinked_inode(c, ic);
+ if (ret)
+ break;
+ /* -EAGAIN means the inode's nlink was zero, so we deleted it,
+ and furthermore that it had children and their nlink has now
+ gone to zero too. So we have to restart the scan. */
+ }
+ } while(ret == -EAGAIN);
+
+ D1(printk(KERN_DEBUG "Pass 2 complete\n"));
+
+ /* Finally, we can scan again and free the dirent nodes and scan_info structs */
+ for_each_inode(i, c, ic) {
+ struct jffs2_scan_info *scan = ic->scan;
+ struct jffs2_full_dirent *fd;
+ D1(printk(KERN_DEBUG "Pass 3: ino #%u, ic %p, nodes %p\n", ic->ino, ic, ic->nodes));
+ if (!scan) {
+ if (ic->nlink) {
+ D1(printk(KERN_WARNING "Why no scan struct for ino #%u which has nlink %d?\n", ic->ino, ic->nlink));
+ }
+ continue;
+ }
+ ic->scan = NULL;
+ while(scan->dents) {
+ fd = scan->dents;
+ scan->dents = fd->next;
+ jffs2_free_full_dirent(fd);
+ }
+ kfree(scan);
+ }
+ D1(printk(KERN_DEBUG "Pass 3 complete\n"));
+
+ return ret;
+}
+
+int jffs2_build_inode_pass1(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
+{
+ struct jffs2_tmp_dnode_info *tn;
+ struct jffs2_full_dirent *fd;
+ struct jffs2_node_frag *fraglist = NULL;
+ struct jffs2_tmp_dnode_info *metadata = NULL;
+
+ D1(printk(KERN_DEBUG "jffs2_build_inode building inode #%u\n", ic->ino));
+ if (ic->ino > c->highest_ino)
+ c->highest_ino = ic->ino;
+
+ if (!ic->scan->tmpnodes && ic->ino != 1) {
+ D1(printk(KERN_DEBUG "jffs2_build_inode: ino #%u has no data nodes!\n", ic->ino));
+ }
+ /* Build the list to make sure any obsolete nodes are marked as such */
+ while(ic->scan->tmpnodes) {
+ tn = ic->scan->tmpnodes;
+ ic->scan->tmpnodes = tn->next;
+
+ if (metadata && tn->version > metadata->version) {
+ D1(printk(KERN_DEBUG "jffs2_build_inode_pass1 ignoring old metadata at 0x%08x\n",
+ metadata->fn->raw->flash_offset &~3));
+
+ jffs2_free_full_dnode(metadata->fn);
+ jffs2_free_tmp_dnode_info(metadata);
+ metadata = NULL;
+ }
+
+ if (tn->fn->size) {
+ jffs2_add_full_dnode_to_fraglist (c, &fraglist, tn->fn);
+ jffs2_free_tmp_dnode_info(tn);
+ } else {
+ if (!metadata) {
+ metadata = tn;
+ } else {
+ D1(printk(KERN_DEBUG "jffs2_build_inode_pass1 ignoring new metadata at 0x%08x\n",
+ tn->fn->raw->flash_offset &~3));
+
+ jffs2_free_full_dnode(tn->fn);
+ jffs2_free_tmp_dnode_info(tn);
+ }
+ }
+ }
+
+ /* OK. Now clear up */
+ if (metadata) {
+ jffs2_free_full_dnode(metadata->fn);
+ jffs2_free_tmp_dnode_info(metadata);
+ }
+ metadata = NULL;
+
+ while (fraglist) {
+ struct jffs2_node_frag *frag;
+ frag = fraglist;
+ fraglist = fraglist->next;
+
+ if (frag->node && !(--frag->node->frags)) {
+ jffs2_free_full_dnode(frag->node);
+ }
+ jffs2_free_node_frag(frag);
+ }
+
+ /* Now for each child, increase nlink */
+ for(fd=ic->scan->dents; fd; fd = fd->next) {
+ struct jffs2_inode_cache *child_ic;
+ if (!fd->ino)
+ continue;
+
+ child_ic = jffs2_get_ino_cache(c, fd->ino);
+ if (!child_ic) {
+ printk(KERN_NOTICE "Eep. Child \"%s\" (ino #%u) of dir ino #%u doesn't exist!\n",
+ fd->name, fd->ino, ic->ino);
+ continue;
+ }
+
+ if (child_ic->nlink++ && fd->type == DT_DIR) {
+ printk(KERN_NOTICE "Child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n", fd->name, fd->ino, ic->ino);
+ if (fd->ino == 1 && ic->ino == 1) {
+ printk(KERN_NOTICE "This is mostly harmless, and probably caused by creating a JFFS2 image\n");
+ printk(KERN_NOTICE "using a buggy version of mkfs.jffs2. Use at least v1.17.\n");
+ }
+ /* What do we do about it? */
+ }
+ D1(printk(KERN_DEBUG "Increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino));
+ /* Can't free them. We might need them in pass 2 */
+ }
+ return 0;
+}
+
+int jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
+{
+ struct jffs2_raw_node_ref *raw;
+ struct jffs2_full_dirent *fd;
+ int ret = 0;
+
+ if(!ic->scan) {
+ D1(printk(KERN_DEBUG "ino #%u was already removed\n", ic->ino));
+ return 0;
+ }
+
+ D1(printk(KERN_DEBUG "JFFS2: Removing ino #%u with nlink == zero.\n", ic->ino));
+
+ for (raw = ic->nodes; raw != (void *)ic; raw = raw->next_in_ino) {
+ D1(printk(KERN_DEBUG "obsoleting node at 0x%08x\n", raw->flash_offset&~3));
+ jffs2_mark_node_obsolete(c, raw);
+ }
+
+ if (ic->scan->dents) {
+ printk(KERN_NOTICE "Inode #%u was a directory with children - removing those too...\n", ic->ino);
+
+ while(ic->scan->dents) {
+ struct jffs2_inode_cache *child_ic;
+
+ fd = ic->scan->dents;
+ ic->scan->dents = fd->next;
+
+ D1(printk(KERN_DEBUG "Removing child \"%s\", ino #%u\n",
+ fd->name, fd->ino));
+
+ child_ic = jffs2_get_ino_cache(c, fd->ino);
+ if (!child_ic) {
+ printk(KERN_NOTICE "Cannot remove child \"%s\", ino #%u, because it doesn't exist\n", fd->name, fd->ino);
+ continue;
+ }
+ jffs2_free_full_dirent(fd);
+ child_ic->nlink--;
+ }
+ ret = -EAGAIN;
+ }
+ kfree(ic->scan);
+ ic->scan = NULL;
+ // jffs2_del_ino_cache(c, ic);
+ // jffs2_free_inode_cache(ic);
+ return ret;
+}
diff --git a/fs/jffs2/compr.c b/fs/jffs2/compr.c
new file mode 100644
index 00000000000000..258be645652060
--- /dev/null
+++ b/fs/jffs2/compr.c
@@ -0,0 +1,151 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001 Red Hat, Inc.
+ *
+ * Created by Arjan van de Ven <arjanv@redhat.com>
+ *
+ * The original JFFS, from which the design for JFFS2 was derived,
+ * was designed and implemented by Axis Communications AB.
+ *
+ * The contents of this file are subject to the Red Hat eCos Public
+ * License Version 1.1 (the "Licence"); you may not use this file
+ * except in compliance with the Licence. You may obtain a copy of
+ * the Licence at http://www.redhat.com/
+ *
+ * Software distributed under the Licence is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing rights and
+ * limitations under the Licence.
+ *
+ * The Original Code is JFFS2 - Journalling Flash File System, version 2
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in
+ * which case the provisions of the GPL are applicable instead of the
+ * above. If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use your
+ * version of this file under the RHEPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice and
+ * other provisions required by the GPL. If you do not delete the
+ * provisions above, a recipient may use your version of this file
+ * under either the RHEPL or the GPL.
+ *
+ * $Id: compr.c,v 1.17 2001/09/23 09:56:46 dwmw2 Exp $
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/jffs2.h>
+
+int zlib_compress(unsigned char *data_in, unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen);
+void zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, __u32 srclen, __u32 destlen);
+int rtime_compress(unsigned char *data_in, unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen);
+void rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, __u32 srclen, __u32 destlen);
+int rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen);
+void rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, __u32 srclen, __u32 destlen);
+int dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen);
+void dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, __u32 srclen, __u32 destlen);
+
+
+/* jffs2_compress:
+ * @data: Pointer to uncompressed data
+ * @cdata: Pointer to buffer for compressed data
+ * @datalen: On entry, holds the amount of data available for compression.
+ * On exit, expected to hold the amount of data actually compressed.
+ * @cdatalen: On entry, holds the amount of space available for compressed
+ * data. On exit, expected to hold the actual size of the compressed
+ * data.
+ *
+ * Returns: Byte to be stored with data indicating compression type used.
+ * Zero is used to show that the data could not be compressed - the
+ * compressed version was actually larger than the original.
+ *
+ * If the cdata buffer isn't large enough to hold all the uncompressed data,
+ * jffs2_compress should compress as much as will fit, and should set
+ * *datalen accordingly to show the amount of data which were compressed.
+ */
+unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out,
+ __u32 *datalen, __u32 *cdatalen)
+{
+ int ret;
+
+ ret = zlib_compress(data_in, cpage_out, datalen, cdatalen);
+ if (!ret) {
+ return JFFS2_COMPR_ZLIB;
+ }
+#if 0 /* Disabled 23/9/1. With zlib it hardly ever gets a look in */
+ ret = dynrubin_compress(data_in, cpage_out, datalen, cdatalen);
+ if (!ret) {
+ return JFFS2_COMPR_DYNRUBIN;
+ }
+#endif
+#if 0 /* Disabled 26/2/1. Obsoleted by dynrubin */
+ ret = rubinmips_compress(data_in, cpage_out, datalen, cdatalen);
+ if (!ret) {
+ return JFFS2_COMPR_RUBINMIPS;
+ }
+#endif
+ /* rtime does manage to recompress already-compressed data */
+ ret = rtime_compress(data_in, cpage_out, datalen, cdatalen);
+ if (!ret) {
+ return JFFS2_COMPR_RTIME;
+ }
+#if 0
+ /* We don't need to copy. Let the caller special-case the COMPR_NONE case. */
+ /* If we get here, no compression is going to work */
+ /* But we might want to use the fragmentation part -- Arjan */
+ memcpy(cpage_out,data_in,min(*datalen,*cdatalen));
+ if (*datalen > *cdatalen)
+ *datalen = *cdatalen;
+#endif
+ return JFFS2_COMPR_NONE; /* We failed to compress */
+
+}
+
+
+int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in,
+ unsigned char *data_out, __u32 cdatalen, __u32 datalen)
+{
+ switch (comprtype) {
+ case JFFS2_COMPR_NONE:
+ /* This should be special-cased elsewhere, but we might as well deal with it */
+ memcpy(data_out, cdata_in, datalen);
+ break;
+
+ case JFFS2_COMPR_ZERO:
+ memset(data_out, 0, datalen);
+ break;
+
+ case JFFS2_COMPR_ZLIB:
+ zlib_decompress(cdata_in, data_out, cdatalen, datalen);
+ break;
+
+ case JFFS2_COMPR_RTIME:
+ rtime_decompress(cdata_in, data_out, cdatalen, datalen);
+ break;
+
+ case JFFS2_COMPR_RUBINMIPS:
+#if 0 /* Disabled 23/9/1 */
+ rubinmips_decompress(cdata_in, data_out, cdatalen, datalen);
+#else
+ printk(KERN_WARNING "JFFS2: Rubinmips compression encountered but support not compiled in!\n");
+#endif
+ break;
+ case JFFS2_COMPR_DYNRUBIN:
+#if 1 /* Phase this one out */
+ dynrubin_decompress(cdata_in, data_out, cdatalen, datalen);
+#else
+ printk(KERN_WARNING "JFFS2: Dynrubin compression encountered but support not compiled in!\n");
+#endif
+ break;
+
+ default:
+ printk(KERN_NOTICE "Unknown JFFS2 compression type 0x%02x\n", comprtype);
+ return -EIO;
+ }
+ return 0;
+}
diff --git a/fs/jffs2/compr_rtime.c b/fs/jffs2/compr_rtime.c
new file mode 100644
index 00000000000000..1a71020c9938ac
--- /dev/null
+++ b/fs/jffs2/compr_rtime.c
@@ -0,0 +1,128 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001 Red Hat, Inc.
+ *
+ * Created by Arjan van de Ven <arjanv@redhat.com>
+ *
+ * The original JFFS, from which the design for JFFS2 was derived,
+ * was designed and implemented by Axis Communications AB.
+ *
+ * The contents of this file are subject to the Red Hat eCos Public
+ * License Version 1.1 (the "Licence"); you may not use this file
+ * except in compliance with the Licence. You may obtain a copy of
+ * the Licence at http://www.redhat.com/
+ *
+ * Software distributed under the Licence is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing rights and
+ * limitations under the Licence.
+ *
+ * The Original Code is JFFS2 - Journalling Flash File System, version 2
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in
+ * which case the provisions of the GPL are applicable instead of the
+ * above. If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use your
+ * version of this file under the RHEPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice and
+ * other provisions required by the GPL. If you do not delete the
+ * provisions above, a recipient may use your version of this file
+ * under either the RHEPL or the GPL.
+ *
+ * $Id: compr_rtime.c,v 1.5 2001/03/15 15:38:23 dwmw2 Exp $
+ *
+ *
+ * Very simple lz77-ish encoder.
+ *
+ * Theory of operation: Both encoder and decoder have a list of "last
+ * occurances" for every possible source-value; after sending the
+ * first source-byte, the second byte indicated the "run" length of
+ * matches
+ *
+ * The algorithm is intended to only send "whole bytes", no bit-messing.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+
+/* _compress returns the compressed size, -1 if bigger */
+int rtime_compress(unsigned char *data_in, unsigned char *cpage_out,
+ __u32 *sourcelen, __u32 *dstlen)
+{
+ int positions[256];
+ int outpos = 0;
+ int pos=0;
+
+ memset(positions,0,sizeof(positions));
+
+ while (pos < (*sourcelen) && outpos <= (*dstlen)-2) {
+ int backpos, runlen=0;
+ unsigned char value;
+
+ value = data_in[pos];
+
+ cpage_out[outpos++] = data_in[pos++];
+
+ backpos = positions[value];
+ positions[value]=pos;
+
+ while ((backpos < pos) && (pos < (*sourcelen)) &&
+ (data_in[pos]==data_in[backpos++]) && (runlen<255)) {
+ pos++;
+ runlen++;
+ }
+ cpage_out[outpos++] = runlen;
+ }
+
+ if (outpos >= pos) {
+ /* We failed */
+ return -1;
+ }
+
+ /* Tell the caller how much we managed to compress, and how much space it took */
+ *sourcelen = pos;
+ *dstlen = outpos;
+ return 0;
+}
+
+
+void rtime_decompress(unsigned char *data_in, unsigned char *cpage_out,
+ __u32 srclen, __u32 destlen)
+{
+ int positions[256];
+ int outpos = 0;
+ int pos=0;
+
+ memset(positions,0,sizeof(positions));
+
+ while (outpos<destlen) {
+ unsigned char value;
+ int backoffs;
+ int repeat;
+
+ value = data_in[pos++];
+ cpage_out[outpos++] = value; /* first the verbatim copied byte */
+ repeat = data_in[pos++];
+ backoffs = positions[value];
+
+ positions[value]=outpos;
+ if (repeat) {
+ if (backoffs + repeat >= outpos) {
+ while(repeat) {
+ cpage_out[outpos++] = cpage_out[backoffs++];
+ repeat--;
+ }
+ } else {
+ memcpy(&cpage_out[outpos],&cpage_out[backoffs],repeat);
+ outpos+=repeat;
+ }
+ }
+ }
+}
+
+
diff --git a/fs/jffs2/compr_rubin.c b/fs/jffs2/compr_rubin.c
new file mode 100644
index 00000000000000..22d8dd570f5682
--- /dev/null
+++ b/fs/jffs2/compr_rubin.c
@@ -0,0 +1,349 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001 Red Hat, Inc.
+ *
+ * Created by Arjan van de Ven <arjanv@redhat.com>
+ *
+ * The original JFFS, from which the design for JFFS2 was derived,
+ * was designed and implemented by Axis Communications AB.
+ *
+ * The contents of this file are subject to the Red Hat eCos Public
+ * License Version 1.1 (the "Licence"); you may not use this file
+ * except in compliance with the Licence. You may obtain a copy of
+ * the Licence at http://www.redhat.com/
+ *
+ * Software distributed under the Licence is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing rights and
+ * limitations under the Licence.
+ *
+ * The Original Code is JFFS2 - Journalling Flash File System, version 2
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in
+ * which case the provisions of the GPL are applicable instead of the
+ * above. If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use your
+ * version of this file under the RHEPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice and
+ * other provisions required by the GPL. If you do not delete the
+ * provisions above, a recipient may use your version of this file
+ * under either the RHEPL or the GPL.
+ *
+ * $Id: compr_rubin.c,v 1.13 2001/09/23 10:06:05 rmk Exp $
+ *
+ */
+
+
+#include <linux/string.h>
+#include <linux/types.h>
+#include "compr_rubin.h"
+#include "histo_mips.h"
+
+
+
+void init_rubin(struct rubin_state *rs, int div, int *bits)
+{
+ int c;
+
+ rs->q = 0;
+ rs->p = (long) (2 * UPPER_BIT_RUBIN);
+ rs->bit_number = (long) 0;
+ rs->bit_divider = div;
+ for (c=0; c<8; c++)
+ rs->bits[c] = bits[c];
+}
+
+
+int encode(struct rubin_state *rs, long A, long B, int symbol)
+{
+
+ long i0, i1;
+ int ret;
+
+ while ((rs->q >= UPPER_BIT_RUBIN) || ((rs->p + rs->q) <= UPPER_BIT_RUBIN)) {
+ rs->bit_number++;
+
+ ret = pushbit(&rs->pp, (rs->q & UPPER_BIT_RUBIN) ? 1 : 0, 0);
+ if (ret)
+ return ret;
+ rs->q &= LOWER_BITS_RUBIN;
+ rs->q <<= 1;
+ rs->p <<= 1;
+ }
+ i0 = A * rs->p / (A + B);
+ if (i0 <= 0) {
+ i0 = 1;
+ }
+ if (i0 >= rs->p) {
+ i0 = rs->p - 1;
+ }
+ i1 = rs->p - i0;
+
+ if (symbol == 0)
+ rs->p = i0;
+ else {
+ rs->p = i1;
+ rs->q += i0;
+ }
+ return 0;
+}
+
+
+void end_rubin(struct rubin_state *rs)
+{
+
+ int i;
+
+ for (i = 0; i < RUBIN_REG_SIZE; i++) {
+ pushbit(&rs->pp, (UPPER_BIT_RUBIN & rs->q) ? 1 : 0, 1);
+ rs->q &= LOWER_BITS_RUBIN;
+ rs->q <<= 1;
+ }
+}
+
+
+void init_decode(struct rubin_state *rs, int div, int *bits)
+{
+ init_rubin(rs, div, bits);
+
+ /* behalve lower */
+ rs->rec_q = 0;
+
+ for (rs->bit_number = 0; rs->bit_number++ < RUBIN_REG_SIZE; rs->rec_q = rs->rec_q * 2 + (long) (pullbit(&rs->pp)))
+ ;
+}
+
+static void __do_decode(struct rubin_state *rs, unsigned long p, unsigned long q)
+{
+ register unsigned long lower_bits_rubin = LOWER_BITS_RUBIN;
+ unsigned long rec_q;
+ int c, bits = 0;
+
+ /*
+ * First, work out how many bits we need from the input stream.
+ * Note that we have already done the initial check on this
+ * loop prior to calling this function.
+ */
+ do {
+ bits++;
+ q &= lower_bits_rubin;
+ q <<= 1;
+ p <<= 1;
+ } while ((q >= UPPER_BIT_RUBIN) || ((p + q) <= UPPER_BIT_RUBIN));
+
+ rs->p = p;
+ rs->q = q;
+
+ rs->bit_number += bits;
+
+ /*
+ * Now get the bits. We really want this to be "get n bits".
+ */
+ rec_q = rs->rec_q;
+ do {
+ c = pullbit(&rs->pp);
+ rec_q &= lower_bits_rubin;
+ rec_q <<= 1;
+ rec_q += c;
+ } while (--bits);
+ rs->rec_q = rec_q;
+}
+
+int decode(struct rubin_state *rs, long A, long B)
+{
+ unsigned long p = rs->p, q = rs->q;
+ long i0, threshold;
+ int symbol;
+
+ if (q >= UPPER_BIT_RUBIN || ((p + q) <= UPPER_BIT_RUBIN))
+ __do_decode(rs, p, q);
+
+ i0 = A * rs->p / (A + B);
+ if (i0 <= 0) {
+ i0 = 1;
+ }
+ if (i0 >= rs->p) {
+ i0 = rs->p - 1;
+ }
+
+ threshold = rs->q + i0;
+ symbol = rs->rec_q >= threshold;
+ if (rs->rec_q >= threshold) {
+ rs->q += i0;
+ i0 = rs->p - i0;
+ }
+
+ rs->p = i0;
+
+ return symbol;
+}
+
+
+
+static int out_byte(struct rubin_state *rs, unsigned char byte)
+{
+ int i, ret;
+ struct rubin_state rs_copy;
+ rs_copy = *rs;
+
+ for (i=0;i<8;i++) {
+ ret = encode(rs, rs->bit_divider-rs->bits[i],rs->bits[i],byte&1);
+ if (ret) {
+ /* Failed. Restore old state */
+ *rs = rs_copy;
+ return ret;
+ }
+ byte=byte>>1;
+ }
+ return 0;
+}
+
+static int in_byte(struct rubin_state *rs)
+{
+ int i, result = 0, bit_divider = rs->bit_divider;
+
+ for (i = 0; i < 8; i++)
+ result |= decode(rs, bit_divider - rs->bits[i], rs->bits[i]) << i;
+
+ return result;
+}
+
+
+
+int rubin_do_compress(int bit_divider, int *bits, unsigned char *data_in,
+ unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen)
+ {
+ int outpos = 0;
+ int pos=0;
+ struct rubin_state rs;
+
+ init_pushpull(&rs.pp, cpage_out, *dstlen * 8, 0, 32);
+
+ init_rubin(&rs, bit_divider, bits);
+
+ while (pos < (*sourcelen) && !out_byte(&rs, data_in[pos]))
+ pos++;
+
+ end_rubin(&rs);
+
+ if (outpos > pos) {
+ /* We failed */
+ return -1;
+ }
+
+ /* Tell the caller how much we managed to compress,
+ * and how much space it took */
+
+ outpos = (pushedbits(&rs.pp)+7)/8;
+
+ if (outpos >= pos)
+ return -1; /* We didn't actually compress */
+ *sourcelen = pos;
+ *dstlen = outpos;
+ return 0;
+}
+#if 0
+/* _compress returns the compressed size, -1 if bigger */
+int rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out,
+ __u32 *sourcelen, __u32 *dstlen)
+{
+ return rubin_do_compress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen);
+}
+#endif
+int dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out,
+ __u32 *sourcelen, __u32 *dstlen)
+{
+ int bits[8];
+ unsigned char histo[256];
+ int i;
+ int ret;
+ __u32 mysrclen, mydstlen;
+
+ mysrclen = *sourcelen;
+ mydstlen = *dstlen - 8;
+
+ if (*dstlen <= 12)
+ return -1;
+
+ memset(histo, 0, 256);
+ for (i=0; i<mysrclen; i++) {
+ histo[data_in[i]]++;
+ }
+ memset(bits, 0, sizeof(int)*8);
+ for (i=0; i<256; i++) {
+ if (i&128)
+ bits[7] += histo[i];
+ if (i&64)
+ bits[6] += histo[i];
+ if (i&32)
+ bits[5] += histo[i];
+ if (i&16)
+ bits[4] += histo[i];
+ if (i&8)
+ bits[3] += histo[i];
+ if (i&4)
+ bits[2] += histo[i];
+ if (i&2)
+ bits[1] += histo[i];
+ if (i&1)
+ bits[0] += histo[i];
+ }
+
+ for (i=0; i<8; i++) {
+ bits[i] = (bits[i] * 256) / mysrclen;
+ if (!bits[i]) bits[i] = 1;
+ if (bits[i] > 255) bits[i] = 255;
+ cpage_out[i] = bits[i];
+ }
+
+ ret = rubin_do_compress(256, bits, data_in, cpage_out+8, &mysrclen, &mydstlen);
+ if (ret)
+ return ret;
+
+ /* Add back the 8 bytes we took for the probabilities */
+ mydstlen += 8;
+
+ if (mysrclen <= mydstlen) {
+ /* We compressed */
+ return -1;
+ }
+
+ *sourcelen = mysrclen;
+ *dstlen = mydstlen;
+ return 0;
+}
+
+void rubin_do_decompress(int bit_divider, int *bits, unsigned char *cdata_in,
+ unsigned char *page_out, __u32 srclen, __u32 destlen)
+{
+ int outpos = 0;
+ struct rubin_state rs;
+
+ init_pushpull(&rs.pp, cdata_in, srclen, 0, 0);
+ init_decode(&rs, bit_divider, bits);
+
+ while (outpos < destlen) {
+ page_out[outpos++] = in_byte(&rs);
+ }
+}
+
+
+void rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out,
+ __u32 sourcelen, __u32 dstlen)
+{
+ rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen);
+}
+
+void dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out,
+ __u32 sourcelen, __u32 dstlen)
+{
+ int bits[8];
+ int c;
+
+ for (c=0; c<8; c++)
+ bits[c] = data_in[c];
+
+ rubin_do_decompress(256, bits, data_in+8, cpage_out, sourcelen-8, dstlen);
+}
diff --git a/fs/jffs2/compr_rubin.h b/fs/jffs2/compr_rubin.h
new file mode 100644
index 00000000000000..58ccc2b8a7dac6
--- /dev/null
+++ b/fs/jffs2/compr_rubin.h
@@ -0,0 +1,28 @@
+/* Rubin encoder/decoder header */
+/* work started at : aug 3, 1994 */
+/* last modification : aug 15, 1994 */
+/* $Id: compr_rubin.h,v 1.5 2001/02/26 13:50:01 dwmw2 Exp $ */
+
+#include "pushpull.h"
+
+#define RUBIN_REG_SIZE 16
+#define UPPER_BIT_RUBIN (((long) 1)<<(RUBIN_REG_SIZE-1))
+#define LOWER_BITS_RUBIN ((((long) 1)<<(RUBIN_REG_SIZE-1))-1)
+
+
+struct rubin_state {
+ unsigned long p;
+ unsigned long q;
+ unsigned long rec_q;
+ long bit_number;
+ struct pushpull pp;
+ int bit_divider;
+ int bits[8];
+};
+
+
+void init_rubin (struct rubin_state *rs, int div, int *bits);
+int encode (struct rubin_state *, long, long, int);
+void end_rubin (struct rubin_state *);
+void init_decode (struct rubin_state *, int div, int *bits);
+int decode (struct rubin_state *, long, long);
diff --git a/fs/jffs2/compr_zlib.c b/fs/jffs2/compr_zlib.c
new file mode 100644
index 00000000000000..137568cff5ffff
--- /dev/null
+++ b/fs/jffs2/compr_zlib.c
@@ -0,0 +1,177 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001, 2002 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ *
+ * The original JFFS, from which the design for JFFS2 was derived,
+ * was designed and implemented by Axis Communications AB.
+ *
+ * The contents of this file are subject to the Red Hat eCos Public
+ * License Version 1.1 (the "Licence"); you may not use this file
+ * except in compliance with the Licence. You may obtain a copy of
+ * the Licence at http://www.redhat.com/
+ *
+ * Software distributed under the Licence is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing rights and
+ * limitations under the Licence.
+ *
+ * The Original Code is JFFS2 - Journalling Flash File System, version 2
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in
+ * which case the provisions of the GPL are applicable instead of the
+ * above. If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use your
+ * version of this file under the RHEPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice and
+ * other provisions required by the GPL. If you do not delete the
+ * provisions above, a recipient may use your version of this file
+ * under either the RHEPL or the GPL.
+ *
+ * $Id: compr_zlib.c,v 1.8.2.1 2002/10/11 09:04:44 dwmw2 Exp $
+ *
+ */
+
+#ifndef __KERNEL__
+#error "The userspace support got too messy and was removed. Update your mkfs.jffs2"
+#endif
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/mtd/compatmac.h> /* for min() */
+#include <linux/slab.h>
+#include <linux/jffs2.h>
+#include <linux/zlib.h>
+#include "nodelist.h"
+
+ /* Plan: call deflate() with avail_in == *sourcelen,
+ avail_out = *dstlen - 12 and flush == Z_FINISH.
+ If it doesn't manage to finish, call it again with
+ avail_in == 0 and avail_out set to the remaining 12
+ bytes for it to clean up.
+ Q: Is 12 bytes sufficient?
+ */
+#define STREAM_END_SPACE 12
+
+static DECLARE_MUTEX(deflate_sem);
+static DECLARE_MUTEX(inflate_sem);
+static void *deflate_workspace;
+static void *inflate_workspace;
+
+int __init jffs2_zlib_init(void)
+{
+ deflate_workspace = vmalloc(zlib_deflate_workspacesize());
+ if (!deflate_workspace) {
+ printk(KERN_WARNING "Failed to allocate %d bytes for deflate workspace\n", zlib_deflate_workspacesize());
+ return -ENOMEM;
+ }
+ D1(printk(KERN_DEBUG "Allocated %d bytes for deflate workspace\n", zlib_deflate_workspacesize()));
+ inflate_workspace = vmalloc(zlib_inflate_workspacesize());
+ if (!inflate_workspace) {
+ printk(KERN_WARNING "Failed to allocate %d bytes for inflate workspace\n", zlib_inflate_workspacesize());
+ vfree(deflate_workspace);
+ return -ENOMEM;
+ }
+ D1(printk(KERN_DEBUG "Allocated %d bytes for inflate workspace\n", zlib_inflate_workspacesize()));
+ return 0;
+}
+
+void jffs2_zlib_exit(void)
+{
+ vfree(deflate_workspace);
+ vfree(inflate_workspace);
+}
+
+int zlib_compress(unsigned char *data_in, unsigned char *cpage_out,
+ __u32 *sourcelen, __u32 *dstlen)
+{
+ z_stream strm;
+ int ret;
+
+ if (*dstlen <= STREAM_END_SPACE)
+ return -1;
+
+ down(&deflate_sem);
+ strm.workspace = deflate_workspace;
+
+ if (Z_OK != zlib_deflateInit(&strm, 3)) {
+ printk(KERN_WARNING "deflateInit failed\n");
+ up(&deflate_sem);
+ return -1;
+ }
+
+ strm.next_in = data_in;
+ strm.total_in = 0;
+
+ strm.next_out = cpage_out;
+ strm.total_out = 0;
+
+ while (strm.total_out < *dstlen - STREAM_END_SPACE && strm.total_in < *sourcelen) {
+ strm.avail_out = *dstlen - (strm.total_out + STREAM_END_SPACE);
+ strm.avail_in = min((unsigned)(*sourcelen-strm.total_in), strm.avail_out);
+ D1(printk(KERN_DEBUG "calling deflate with avail_in %d, avail_out %d\n",
+ strm.avail_in, strm.avail_out));
+ ret = zlib_deflate(&strm, Z_PARTIAL_FLUSH);
+ D1(printk(KERN_DEBUG "deflate returned with avail_in %d, avail_out %d, total_in %ld, total_out %ld\n",
+ strm.avail_in, strm.avail_out, strm.total_in, strm.total_out));
+ if (ret != Z_OK) {
+ D1(printk(KERN_DEBUG "deflate in loop returned %d\n", ret));
+ zlib_deflateEnd(&strm);
+ up(&deflate_sem);
+ return -1;
+ }
+ }
+ strm.avail_out += STREAM_END_SPACE;
+ strm.avail_in = 0;
+ ret = zlib_deflate(&strm, Z_FINISH);
+ zlib_deflateEnd(&strm);
+ up(&deflate_sem);
+ if (ret != Z_STREAM_END) {
+ D1(printk(KERN_DEBUG "final deflate returned %d\n", ret));
+ return -1;
+ }
+
+ D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld\n",
+ strm.total_in, strm.total_out));
+
+ if (strm.total_out >= strm.total_in)
+ return -1;
+
+ *dstlen = strm.total_out;
+ *sourcelen = strm.total_in;
+ return 0;
+}
+
+void zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
+ __u32 srclen, __u32 destlen)
+{
+ z_stream strm;
+ int ret;
+
+ down(&inflate_sem);
+ strm.workspace = inflate_workspace;
+
+ if (Z_OK != zlib_inflateInit(&strm)) {
+ printk(KERN_WARNING "inflateInit failed\n");
+ up(&inflate_sem);
+ return;
+ }
+ strm.next_in = data_in;
+ strm.avail_in = srclen;
+ strm.total_in = 0;
+
+ strm.next_out = cpage_out;
+ strm.avail_out = destlen;
+ strm.total_out = 0;
+
+ while((ret = zlib_inflate(&strm, Z_FINISH)) == Z_OK)
+ ;
+ if (ret != Z_STREAM_END) {
+ printk(KERN_NOTICE "inflate returned %d\n", ret);
+ }
+ zlib_inflateEnd(&strm);
+ up(&inflate_sem);
+}
diff --git a/fs/jffs2/comprtest.c b/fs/jffs2/comprtest.c
new file mode 100644
index 00000000000000..dbc12ccb494917
--- /dev/null
+++ b/fs/jffs2/comprtest.c
@@ -0,0 +1,307 @@
+/* $Id: comprtest.c,v 1.4 2001/02/21 14:03:20 dwmw2 Exp $ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <asm/types.h>
+#if 0
+#define TESTDATA_LEN 512
+static unsigned char testdata[TESTDATA_LEN] = {
+ 0x7f, 0x45, 0x4c, 0x46, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0x83, 0x04, 0x08, 0x34, 0x00, 0x00, 0x00,
+ 0xb0, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x20, 0x00, 0x06, 0x00, 0x28, 0x00,
+ 0x1e, 0x00, 0x1b, 0x00, 0x06, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x34, 0x80, 0x04, 0x08,
+ 0x34, 0x80, 0x04, 0x08, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x00, 0xf4, 0x80, 0x04, 0x08,
+ 0xf4, 0x80, 0x04, 0x08, 0x13, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x04, 0x08,
+ 0x00, 0x80, 0x04, 0x08, 0x0d, 0x05, 0x00, 0x00, 0x0d, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x05, 0x00, 0x00, 0x10, 0x95, 0x04, 0x08,
+ 0x10, 0x95, 0x04, 0x08, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x58, 0x05, 0x00, 0x00, 0x58, 0x95, 0x04, 0x08,
+ 0x58, 0x95, 0x04, 0x08, 0xa0, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x08, 0x81, 0x04, 0x08,
+ 0x08, 0x81, 0x04, 0x08, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x2f, 0x6c, 0x69, 0x62, 0x2f, 0x6c, 0x64, 0x2d, 0x6c, 0x69, 0x6e, 0x75,
+ 0x78, 0x2e, 0x73, 0x6f, 0x2e, 0x32, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x47, 0x4e, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00,
+ 0x0c, 0x83, 0x04, 0x08, 0x81, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x1c, 0x83, 0x04, 0x08, 0xac, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00,
+ 0x2c, 0x83, 0x04, 0x08, 0xdd, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00,
+ 0x3c, 0x83, 0x04, 0x08, 0x2e, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
+ 0x4c, 0x83, 0x04, 0x08, 0x7d, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00,
+ 0x00, 0x85, 0x04, 0x08, 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x0e, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x5f, 0x67,
+ 0x6d, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x5f, 0x00, 0x6c, 0x69, 0x62, 0x63,
+ 0x2e, 0x73, 0x6f, 0x2e, 0x36, 0x00, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x00, 0x5f, 0x5f, 0x63};
+#else
+#define TESTDATA_LEN 3481
+static unsigned char testdata[TESTDATA_LEN] = {
+ 0x23, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x20, 0x22, 0x64, 0x62, 0x65, 0x6e, 0x63, 0x68,
+ 0x2e, 0x68, 0x22, 0x0a, 0x0a, 0x23, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x20, 0x4d, 0x41, 0x58,
+ 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x20, 0x31, 0x30, 0x30, 0x30, 0x0a, 0x0a, 0x73, 0x74, 0x61,
+ 0x74, 0x69, 0x63, 0x20, 0x63, 0x68, 0x61, 0x72, 0x20, 0x62, 0x75, 0x66, 0x5b, 0x37, 0x30, 0x30,
+ 0x30, 0x30, 0x5d, 0x3b, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x69, 0x6e, 0x74, 0x20,
+ 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x3b, 0x0a, 0x0a, 0x73, 0x74, 0x61,
+ 0x74, 0x69, 0x63, 0x20, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x7b, 0x0a, 0x09, 0x69, 0x6e,
+ 0x74, 0x20, 0x66, 0x64, 0x3b, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c,
+ 0x65, 0x3b, 0x0a, 0x7d, 0x20, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x4d, 0x41, 0x58, 0x5f,
+ 0x46, 0x49, 0x4c, 0x45, 0x53, 0x5d, 0x3b, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f,
+ 0x5f, 0x75, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x28, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x66, 0x6e,
+ 0x61, 0x6d, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x70, 0x70, 0x65, 0x72,
+ 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x75,
+ 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x20, 0x21, 0x3d, 0x20,
+ 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x28,
+ 0x25, 0x64, 0x29, 0x20, 0x75, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x20, 0x25, 0x73, 0x20, 0x66, 0x61,
+ 0x69, 0x6c, 0x65, 0x64, 0x20, 0x28, 0x25, 0x73, 0x29, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09,
+ 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75,
+ 0x6e, 0x74, 0x2c, 0x20, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x65, 0x72,
+ 0x72, 0x6f, 0x72, 0x28, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a,
+ 0x7d, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x5f, 0x66,
+ 0x69, 0x6c, 0x65, 0x28, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x64, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20,
+ 0x73, 0x69, 0x7a, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x73, 0x3b, 0x0a,
+ 0x09, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x20, 0x28, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20, 0x7b, 0x0a,
+ 0x09, 0x09, 0x73, 0x20, 0x3d, 0x20, 0x4d, 0x49, 0x4e, 0x28, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66,
+ 0x28, 0x62, 0x75, 0x66, 0x29, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x09,
+ 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x66, 0x64, 0x2c, 0x20, 0x62, 0x75, 0x66, 0x2c, 0x20, 0x73,
+ 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x2d, 0x3d, 0x20, 0x73, 0x3b, 0x0a,
+ 0x09, 0x7d, 0x0a, 0x7d, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x6f, 0x70,
+ 0x65, 0x6e, 0x28, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20,
+ 0x69, 0x6e, 0x74, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20,
+ 0x73, 0x69, 0x7a, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x64, 0x2c,
+ 0x20, 0x69, 0x3b, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x20, 0x3d,
+ 0x20, 0x4f, 0x5f, 0x52, 0x44, 0x57, 0x52, 0x7c, 0x4f, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x3b,
+ 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x73, 0x74, 0x61, 0x74, 0x20, 0x73, 0x74,
+ 0x3b, 0x0a, 0x09, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x63, 0x6f,
+ 0x75, 0x6e, 0x74, 0x3b, 0x0a, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x70, 0x70, 0x65, 0x72, 0x28,
+ 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x73, 0x69,
+ 0x7a, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x20, 0x7c,
+ 0x3d, 0x20, 0x4f, 0x5f, 0x54, 0x52, 0x55, 0x4e, 0x43, 0x3b, 0x0a, 0x0a, 0x09, 0x66, 0x64, 0x20,
+ 0x3d, 0x20, 0x6f, 0x70, 0x65, 0x6e, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x66, 0x6c,
+ 0x61, 0x67, 0x73, 0x2c, 0x20, 0x30, 0x36, 0x30, 0x30, 0x29, 0x3b, 0x0a, 0x09, 0x69, 0x66, 0x20,
+ 0x28, 0x66, 0x64, 0x20, 0x3d, 0x3d, 0x20, 0x2d, 0x31, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70,
+ 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x28, 0x25, 0x64, 0x29, 0x20, 0x6f, 0x70, 0x65, 0x6e,
+ 0x20, 0x25, 0x73, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x68,
+ 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x25, 0x64, 0x20, 0x28, 0x25, 0x73, 0x29, 0x5c, 0x6e, 0x22,
+ 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65,
+ 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x68,
+ 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28,
+ 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72,
+ 0x6e, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x66, 0x73, 0x74, 0x61, 0x74, 0x28, 0x66, 0x64, 0x2c,
+ 0x20, 0x26, 0x73, 0x74, 0x29, 0x3b, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x73, 0x69, 0x7a, 0x65,
+ 0x20, 0x3e, 0x20, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20, 0x7b,
+ 0x0a, 0x23, 0x69, 0x66, 0x20, 0x44, 0x45, 0x42, 0x55, 0x47, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69,
+ 0x6e, 0x74, 0x66, 0x28, 0x22, 0x28, 0x25, 0x64, 0x29, 0x20, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64,
+ 0x69, 0x6e, 0x67, 0x20, 0x25, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x25, 0x64, 0x20, 0x66, 0x72, 0x6f,
+ 0x6d, 0x20, 0x25, 0x64, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x66,
+ 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2c, 0x20, 0x28, 0x69, 0x6e, 0x74,
+ 0x29, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x23, 0x65,
+ 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x09, 0x09, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x64, 0x5f, 0x66, 0x69,
+ 0x6c, 0x65, 0x28, 0x66, 0x64, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x2d, 0x20, 0x73, 0x74,
+ 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x20, 0x65, 0x6c,
+ 0x73, 0x65, 0x20, 0x69, 0x66, 0x20, 0x28, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x3c, 0x20, 0x73, 0x74,
+ 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72,
+ 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x74, 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x69, 0x6e, 0x67,
+ 0x20, 0x25, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x25, 0x64, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x25,
+ 0x64, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2c, 0x20, 0x28, 0x69, 0x6e,
+ 0x74, 0x29, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x09,
+ 0x09, 0x66, 0x74, 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x28, 0x66, 0x64, 0x2c, 0x20, 0x73,
+ 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x69,
+ 0x3d, 0x30, 0x3b, 0x69, 0x3c, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x3b, 0x69,
+ 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x66, 0x74, 0x61, 0x62,
+ 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x3d, 0x3d, 0x20,
+ 0x30, 0x29, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x69, 0x66,
+ 0x20, 0x28, 0x69, 0x20, 0x3d, 0x3d, 0x20, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53,
+ 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x66, 0x69,
+ 0x6c, 0x65, 0x20, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x66, 0x75, 0x6c, 0x6c, 0x20, 0x66, 0x6f,
+ 0x72, 0x20, 0x25, 0x73, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x3b,
+ 0x0a, 0x09, 0x09, 0x65, 0x78, 0x69, 0x74, 0x28, 0x31, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09,
+ 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65,
+ 0x20, 0x3d, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x3b, 0x0a, 0x09, 0x66, 0x74, 0x61, 0x62,
+ 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x66, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x64, 0x3b, 0x0a, 0x09,
+ 0x69, 0x66, 0x20, 0x28, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2b, 0x2b, 0x20, 0x25, 0x20, 0x31, 0x30,
+ 0x30, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e,
+ 0x74, 0x66, 0x28, 0x22, 0x2e, 0x22, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x7d, 0x0a, 0x0a, 0x76,
+ 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x69, 0x6e, 0x74,
+ 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x7a,
+ 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x29, 0x0a, 0x7b,
+ 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x62,
+ 0x75, 0x66, 0x5b, 0x30, 0x5d, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x6d, 0x65, 0x6d, 0x73,
+ 0x65, 0x74, 0x28, 0x62, 0x75, 0x66, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f,
+ 0x66, 0x28, 0x62, 0x75, 0x66, 0x29, 0x29, 0x3b, 0x0a, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28,
+ 0x69, 0x3d, 0x30, 0x3b, 0x69, 0x3c, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x3b,
+ 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x66, 0x74, 0x61,
+ 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x3d, 0x3d,
+ 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a,
+ 0x09, 0x7d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x69, 0x20, 0x3d, 0x3d, 0x20, 0x4d, 0x41, 0x58,
+ 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x29, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x31, 0x0a,
+ 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x28, 0x25, 0x64, 0x29, 0x20, 0x64,
+ 0x6f, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x3a, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20,
+ 0x25, 0x64, 0x20, 0x77, 0x61, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6f, 0x70, 0x65, 0x6e, 0x20,
+ 0x73, 0x69, 0x7a, 0x65, 0x3d, 0x25, 0x64, 0x20, 0x6f, 0x66, 0x73, 0x3d, 0x25, 0x64, 0x5c, 0x6e,
+ 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e,
+ 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c,
+ 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2c, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x29, 0x3b, 0x0a,
+ 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b,
+ 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x6c, 0x73, 0x65, 0x65, 0x6b, 0x28, 0x66, 0x74, 0x61, 0x62, 0x6c,
+ 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x66, 0x64, 0x2c, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x2c,
+ 0x20, 0x53, 0x45, 0x45, 0x4b, 0x5f, 0x53, 0x45, 0x54, 0x29, 0x3b, 0x0a, 0x09, 0x69, 0x66, 0x20,
+ 0x28, 0x77, 0x72, 0x69, 0x74, 0x65, 0x28, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d,
+ 0x2e, 0x66, 0x64, 0x2c, 0x20, 0x62, 0x75, 0x66, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20,
+ 0x21, 0x3d, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69,
+ 0x6e, 0x74, 0x66, 0x28, 0x22, 0x77, 0x72, 0x69, 0x74, 0x65, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x65,
+ 0x64, 0x20, 0x6f, 0x6e, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x25, 0x64, 0x5c, 0x6e,
+ 0x22, 0x2c, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x7d,
+ 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x28, 0x69,
+ 0x6e, 0x74, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x73,
+ 0x69, 0x7a, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x29,
+ 0x0a, 0x7b, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x3b, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x20,
+ 0x28, 0x69, 0x3d, 0x30, 0x3b, 0x69, 0x3c, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53,
+ 0x3b, 0x69, 0x2b, 0x2b, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x66, 0x74,
+ 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x3d,
+ 0x3d, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b,
+ 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x69, 0x20, 0x3d, 0x3d, 0x20, 0x4d, 0x41,
+ 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69,
+ 0x6e, 0x74, 0x66, 0x28, 0x22, 0x28, 0x25, 0x64, 0x29, 0x20, 0x64, 0x6f, 0x5f, 0x72, 0x65, 0x61,
+ 0x64, 0x3a, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x25, 0x64, 0x20, 0x77, 0x61, 0x73,
+ 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x3d, 0x25,
+ 0x64, 0x20, 0x6f, 0x66, 0x73, 0x3d, 0x25, 0x64, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e,
+ 0x74, 0x2c, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2c,
+ 0x20, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75,
+ 0x72, 0x6e, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x6c, 0x73, 0x65, 0x65, 0x6b, 0x28, 0x66, 0x74,
+ 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x66, 0x64, 0x2c, 0x20, 0x6f, 0x66, 0x66, 0x73,
+ 0x65, 0x74, 0x2c, 0x20, 0x53, 0x45, 0x45, 0x4b, 0x5f, 0x53, 0x45, 0x54, 0x29, 0x3b, 0x0a, 0x09,
+ 0x72, 0x65, 0x61, 0x64, 0x28, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x66,
+ 0x64, 0x2c, 0x20, 0x62, 0x75, 0x66, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x7d,
+ 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x28,
+ 0x69, 0x6e, 0x74, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x69,
+ 0x6e, 0x74, 0x20, 0x69, 0x3b, 0x0a, 0x09, 0x66, 0x6f, 0x72, 0x20, 0x28, 0x69, 0x3d, 0x30, 0x3b,
+ 0x69, 0x3c, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x3b, 0x69, 0x2b, 0x2b, 0x29,
+ 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x66, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5b,
+ 0x69, 0x5d, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x68, 0x61, 0x6e,
+ 0x64, 0x6c, 0x65, 0x29, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09,
+ 0x69, 0x66, 0x20, 0x28, 0x69, 0x20, 0x3d, 0x3d, 0x20, 0x4d, 0x41, 0x58, 0x5f, 0x46, 0x49, 0x4c,
+ 0x45, 0x53, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22,
+ 0x28, 0x25, 0x64, 0x29, 0x20, 0x64, 0x6f, 0x5f, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x3a, 0x20, 0x68,
+ 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x25, 0x64, 0x20, 0x77, 0x61, 0x73, 0x20, 0x6e, 0x6f, 0x74,
+ 0x20, 0x6f, 0x70, 0x65, 0x6e, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20,
+ 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72,
+ 0x6e, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x28, 0x66, 0x74, 0x61,
+ 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x66, 0x64, 0x29, 0x3b, 0x0a, 0x09, 0x66, 0x74, 0x61,
+ 0x62, 0x6c, 0x65, 0x5b, 0x69, 0x5d, 0x2e, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x20, 0x3d, 0x20,
+ 0x30, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x6d, 0x6b,
+ 0x64, 0x69, 0x72, 0x28, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29,
+ 0x0a, 0x7b, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x70, 0x70, 0x65, 0x72, 0x28, 0x66, 0x6e, 0x61,
+ 0x6d, 0x65, 0x29, 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x6d, 0x6b, 0x64, 0x69, 0x72,
+ 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x30, 0x37, 0x30, 0x30, 0x29, 0x20, 0x21, 0x3d,
+ 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x23, 0x69, 0x66, 0x20, 0x44, 0x45, 0x42, 0x55, 0x47, 0x0a,
+ 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x6d, 0x6b, 0x64, 0x69, 0x72, 0x20,
+ 0x25, 0x73, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x20, 0x28, 0x25, 0x73, 0x29, 0x5c, 0x6e,
+ 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6e, 0x61,
+ 0x6d, 0x65, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x65, 0x72, 0x72,
+ 0x6e, 0x6f, 0x29, 0x29, 0x3b, 0x0a, 0x23, 0x65, 0x6e, 0x64, 0x69, 0x66, 0x0a, 0x09, 0x7d, 0x0a,
+ 0x7d, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x72, 0x6d, 0x64, 0x69, 0x72,
+ 0x28, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x0a, 0x7b, 0x0a,
+ 0x09, 0x73, 0x74, 0x72, 0x75, 0x70, 0x70, 0x65, 0x72, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29,
+ 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x72, 0x6d, 0x64, 0x69, 0x72, 0x28, 0x66, 0x6e,
+ 0x61, 0x6d, 0x65, 0x29, 0x20, 0x21, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70,
+ 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x72, 0x6d, 0x64, 0x69, 0x72, 0x20, 0x25, 0x73, 0x20,
+ 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x20, 0x28, 0x25, 0x73, 0x29, 0x5c, 0x6e, 0x22, 0x2c, 0x20,
+ 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c,
+ 0x20, 0x73, 0x74, 0x72, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x28, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x29,
+ 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x7d, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f,
+ 0x5f, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x28, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x6f, 0x6c,
+ 0x64, 0x2c, 0x20, 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x6e, 0x65, 0x77, 0x29, 0x0a, 0x7b, 0x0a,
+ 0x09, 0x73, 0x74, 0x72, 0x75, 0x70, 0x70, 0x65, 0x72, 0x28, 0x6f, 0x6c, 0x64, 0x29, 0x3b, 0x0a,
+ 0x09, 0x73, 0x74, 0x72, 0x75, 0x70, 0x70, 0x65, 0x72, 0x28, 0x6e, 0x65, 0x77, 0x29, 0x3b, 0x0a,
+ 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x28, 0x6f, 0x6c, 0x64,
+ 0x2c, 0x20, 0x6e, 0x65, 0x77, 0x29, 0x20, 0x21, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x09,
+ 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20,
+ 0x25, 0x73, 0x20, 0x25, 0x73, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x20, 0x28, 0x25, 0x73,
+ 0x29, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x6f, 0x6c, 0x64, 0x2c, 0x20, 0x6e, 0x65, 0x77, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x65, 0x72, 0x72,
+ 0x6f, 0x72, 0x28, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x7d,
+ 0x0a, 0x0a, 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x28,
+ 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74,
+ 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74,
+ 0x20, 0x73, 0x74, 0x61, 0x74, 0x20, 0x73, 0x74, 0x3b, 0x0a, 0x0a, 0x09, 0x73, 0x74, 0x72, 0x75,
+ 0x70, 0x70, 0x65, 0x72, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x3b, 0x0a, 0x0a, 0x09, 0x69,
+ 0x66, 0x20, 0x28, 0x73, 0x74, 0x61, 0x74, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x26,
+ 0x73, 0x74, 0x29, 0x20, 0x21, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72,
+ 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22, 0x28, 0x25, 0x64, 0x29, 0x20, 0x64, 0x6f, 0x5f, 0x73, 0x74,
+ 0x61, 0x74, 0x3a, 0x20, 0x25, 0x73, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x3d, 0x25, 0x64, 0x20, 0x25,
+ 0x73, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x66, 0x6e, 0x61, 0x6d,
+ 0x65, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x65, 0x72, 0x72, 0x6f,
+ 0x72, 0x28, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x29, 0x29, 0x3b, 0x0a, 0x09, 0x09, 0x72, 0x65, 0x74,
+ 0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28, 0x53, 0x5f, 0x49,
+ 0x53, 0x44, 0x49, 0x52, 0x28, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x29,
+ 0x29, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3b, 0x0a, 0x0a, 0x09, 0x69, 0x66, 0x20, 0x28,
+ 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x21, 0x3d, 0x20, 0x73, 0x69,
+ 0x7a, 0x65, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x09, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x28, 0x22,
+ 0x28, 0x25, 0x64, 0x29, 0x20, 0x64, 0x6f, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x3a, 0x20, 0x25, 0x73,
+ 0x20, 0x77, 0x72, 0x6f, 0x6e, 0x67, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20, 0x25, 0x64, 0x20, 0x25,
+ 0x64, 0x5c, 0x6e, 0x22, 0x2c, 0x20, 0x0a, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2c, 0x20, 0x66, 0x6e, 0x61, 0x6d,
+ 0x65, 0x2c, 0x20, 0x28, 0x69, 0x6e, 0x74, 0x29, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x5f, 0x73, 0x69,
+ 0x7a, 0x65, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x7d, 0x0a, 0x7d, 0x0a,
+ 0x0a, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x64, 0x6f, 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x28,
+ 0x63, 0x68, 0x61, 0x72, 0x20, 0x2a, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x69, 0x6e, 0x74,
+ 0x20, 0x73, 0x69, 0x7a, 0x65, 0x29, 0x0a, 0x7b, 0x0a, 0x09, 0x64, 0x6f, 0x5f, 0x6f, 0x70, 0x65,
+ 0x6e, 0x28, 0x66, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x35, 0x30, 0x30, 0x30, 0x2c, 0x20, 0x73,
+ 0x69, 0x7a, 0x65, 0x29, 0x3b, 0x0a, 0x09, 0x64, 0x6f, 0x5f, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x28,
+ 0x35, 0x30, 0x30, 0x30, 0x29, 0x3b, 0x0a, 0x7d, 0x0a
+};
+#endif
+static unsigned char comprbuf[TESTDATA_LEN];
+static unsigned char decomprbuf[TESTDATA_LEN];
+
+int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in,
+ unsigned char *data_out, __u32 cdatalen, __u32 datalen);
+unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out,
+ __u32 *datalen, __u32 *cdatalen);
+
+int init_module(void ) {
+ unsigned char comprtype;
+ __u32 c, d;
+ int ret;
+
+ printk("Original data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ testdata[0],testdata[1],testdata[2],testdata[3],
+ testdata[4],testdata[5],testdata[6],testdata[7],
+ testdata[8],testdata[9],testdata[10],testdata[11],
+ testdata[12],testdata[13],testdata[14],testdata[15]);
+ d = TESTDATA_LEN;
+ c = TESTDATA_LEN;
+ comprtype = jffs2_compress(testdata, comprbuf, &d, &c);
+
+ printk("jffs2_compress used compression type %d. Compressed size %d, uncompressed size %d\n",
+ comprtype, c, d);
+ printk("Compressed data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ comprbuf[0],comprbuf[1],comprbuf[2],comprbuf[3],
+ comprbuf[4],comprbuf[5],comprbuf[6],comprbuf[7],
+ comprbuf[8],comprbuf[9],comprbuf[10],comprbuf[11],
+ comprbuf[12],comprbuf[13],comprbuf[14],comprbuf[15]);
+
+ ret = jffs2_decompress(comprtype, comprbuf, decomprbuf, c, d);
+ printk("jffs2_decompress returned %d\n", ret);
+ printk("Decompressed data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ decomprbuf[0],decomprbuf[1],decomprbuf[2],decomprbuf[3],
+ decomprbuf[4],decomprbuf[5],decomprbuf[6],decomprbuf[7],
+ decomprbuf[8],decomprbuf[9],decomprbuf[10],decomprbuf[11],
+ decomprbuf[12],decomprbuf[13],decomprbuf[14],decomprbuf[15]);
+ if (memcmp(decomprbuf, testdata, d))
+ printk("Compression and decompression corrupted data\n");
+ else
+ printk("Compression good for %d bytes\n", d);
+ return 1;
+}
diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c
new file mode 100644
index 00000000000000..e3fcbc5277f405
--- /dev/null
+++ b/fs/jffs2/dir.c
@@ -0,0 +1,1010 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ *
+ * The original JFFS, from which the design for JFFS2 was derived,
+ * was designed and implemented by Axis Communications AB.
+ *
+ * The contents of this file are subject to the Red Hat eCos Public
+ * License Version 1.1 (the "Licence"); you may not use this file
+ * except in compliance with the Licence. You may obtain a copy of
+ * the Licence at http://www.redhat.com/
+ *
+ * Software distributed under the Licence is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing rights and
+ * limitations under the Licence.
+ *
+ * The Original Code is JFFS2 - Journalling Flash File System, version 2
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in
+ * which case the provisions of the GPL are applicable instead of the
+ * above. If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use your
+ * version of this file under the RHEPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice and
+ * other provisions required by the GPL. If you do not delete the
+ * provisions above, a recipient may use your version of this file
+ * under either the RHEPL or the GPL.
+ *
+ * $Id: dir.c,v 1.45.2.8 2003/11/02 13:51:17 dwmw2 Exp $
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/mtd/compatmac.h> /* For completion */
+#include <linux/jffs2.h>
+#include <linux/jffs2_fs_i.h>
+#include <linux/jffs2_fs_sb.h>
+#include "nodelist.h"
+#include <linux/crc32.h>
+
+static int jffs2_readdir (struct file *, void *, filldir_t);
+
+static int jffs2_create (struct inode *,struct dentry *,int);
+static struct dentry *jffs2_lookup (struct inode *,struct dentry *);
+static int jffs2_link (struct dentry *,struct inode *,struct dentry *);
+static int jffs2_unlink (struct inode *,struct dentry *);
+static int jffs2_symlink (struct inode *,struct dentry *,const char *);
+static int jffs2_mkdir (struct inode *,struct dentry *,int);
+static int jffs2_rmdir (struct inode *,struct dentry *);
+static int jffs2_mknod (struct inode *,struct dentry *,int,int);
+static int jffs2_rename (struct inode *, struct dentry *,
+ struct inode *, struct dentry *);
+
+struct file_operations jffs2_dir_operations =
+{
+ read: generic_read_dir,
+ readdir: jffs2_readdir,
+ ioctl: jffs2_ioctl,
+ fsync: jffs2_null_fsync
+};
+
+
+struct inode_operations jffs2_dir_inode_operations =
+{
+ create: jffs2_create,
+ lookup: jffs2_lookup,
+ link: jffs2_link,
+ unlink: jffs2_unlink,
+ symlink: jffs2_symlink,
+ mkdir: jffs2_mkdir,
+ rmdir: jffs2_rmdir,
+ mknod: jffs2_mknod,
+ rename: jffs2_rename,
+ setattr: jffs2_setattr,
+};
+
+/***********************************************************************/
+
+
+/* We keep the dirent list sorted in increasing order of name hash,
+ and we use the same hash function as the dentries. Makes this
+ nice and simple
+*/
+static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target)
+{
+ struct jffs2_inode_info *dir_f;
+ struct jffs2_sb_info *c;
+ struct jffs2_full_dirent *fd = NULL, *fd_list;
+ __u32 ino = 0;
+ struct inode *inode = NULL;
+
+ D1(printk(KERN_DEBUG "jffs2_lookup()\n"));
+
+ dir_f = JFFS2_INODE_INFO(dir_i);
+ c = JFFS2_SB_INFO(dir_i->i_sb);
+
+ down(&dir_f->sem);
+
+ /* NB: The 2.2 backport will need to explicitly check for '.' and '..' here */
+ for (fd_list = dir_f->dents; fd_list && fd_list->nhash <= target->d_name.hash; fd_list = fd_list->next) {
+ if (fd_list->nhash == target->d_name.hash &&
+ (!fd || fd_list->version > fd->version) &&
+ strlen(fd_list->name) == target->d_name.len &&
+ !strncmp(fd_list->name, target->d_name.name, target->d_name.len)) {
+ fd = fd_list;
+ }
+ }
+ if (fd)
+ ino = fd->ino;
+ up(&dir_f->sem);
+ if (ino) {
+ inode = iget(dir_i->i_sb, ino);
+ if (!inode) {
+ printk(KERN_WARNING "iget() failed for ino #%u\n", ino);
+ return (ERR_PTR(-EIO));
+ }
+ }
+
+ d_add(target, inode);
+
+ return NULL;
+}
+
+/***********************************************************************/
+
+
+static int jffs2_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+ struct jffs2_inode_info *f;
+ struct jffs2_sb_info *c;
+ struct inode *inode = filp->f_dentry->d_inode;
+ struct jffs2_full_dirent *fd;
+ unsigned long offset, curofs;
+
+ D1(printk(KERN_DEBUG "jffs2_readdir() for dir_i #%lu\n", filp->f_dentry->d_inode->i_ino));
+
+ f = JFFS2_INODE_INFO(inode);
+ c = JFFS2_SB_INFO(inode->i_sb);
+
+ offset = filp->f_pos;
+
+ if (offset == 0) {
+ D1(printk(KERN_DEBUG "Dirent 0: \".\", ino #%lu\n", inode->i_ino));
+ if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR) < 0)
+ goto out;
+ offset++;
+ }
+ if (offset == 1) {
+ D1(printk(KERN_DEBUG "Dirent 1: \"..\", ino #%lu\n", filp->f_dentry->d_parent->d_inode->i_ino));
+ if (filldir(dirent, "..", 2, 1, filp->f_dentry->d_parent->d_inode->i_ino, DT_DIR) < 0)
+ goto out;
+ offset++;
+ }
+
+ curofs=1;
+ down(&f->sem);
+ for (fd = f->dents; fd; fd = fd->next) {
+
+ curofs++;
+ /* First loop: curofs = 2; offset = 2 */
+ if (curofs < offset) {
+ D2(printk(KERN_DEBUG "Skipping dirent: \"%s\", ino #%u, type %d, because curofs %ld < offset %ld\n",
+ fd->name, fd->ino, fd->type, curofs, offset));
+ continue;
+ }
+ if (!fd->ino) {
+ D2(printk(KERN_DEBUG "Skipping deletion dirent \"%s\"\n", fd->name));
+ offset++;
+ continue;
+ }
+ D2(printk(KERN_DEBUG "Dirent %ld: \"%s\", ino #%u, type %d\n", offset, fd->name, fd->ino, fd->type));
+ if (filldir(dirent, fd->name, strlen(fd->name), offset, fd->ino, fd->type) < 0)
+ break;
+ offset++;
+ }
+ up(&f->sem);
+ out:
+ filp->f_pos = offset;
+ return 0;
+}
+
+/***********************************************************************/
+
+static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode)
+{
+ struct jffs2_inode_info *f, *dir_f;
+ struct jffs2_sb_info *c;
+ struct inode *inode;
+ struct jffs2_raw_inode *ri;
+ struct jffs2_raw_dirent *rd;
+ struct jffs2_full_dnode *fn;
+ struct jffs2_full_dirent *fd;
+ int namelen;
+ __u32 alloclen, phys_ofs;
+ __u32 writtenlen;
+ int ret;
+
+ ri = jffs2_alloc_raw_inode();
+ if (!ri)
+ return -ENOMEM;
+
+ c = JFFS2_SB_INFO(dir_i->i_sb);
+
+ D1(printk(KERN_DEBUG "jffs2_create()\n"));
+
+ /* Try to reserve enough space for both node and dirent.
+ * Just the node will do for now, though
+ */
+ namelen = dentry->d_name.len;
+ ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL);
+ D1(printk(KERN_DEBUG "jffs2_create(): reserved 0x%x bytes\n", alloclen));
+ if (ret) {
+ jffs2_free_raw_inode(ri);
+ return ret;
+ }
+
+ inode = jffs2_new_inode(dir_i, mode, ri);
+
+ if (IS_ERR(inode)) {
+ D1(printk(KERN_DEBUG "jffs2_new_inode() failed\n"));
+ jffs2_free_raw_inode(ri);
+ jffs2_complete_reservation(c);
+ return PTR_ERR(inode);
+ }
+
+ inode->i_op = &jffs2_file_inode_operations;
+ inode->i_fop = &jffs2_file_operations;
+ inode->i_mapping->a_ops = &jffs2_file_address_operations;
+ inode->i_mapping->nrpages = 0;
+
+ f = JFFS2_INODE_INFO(inode);
+
+ ri->data_crc = 0;
+ ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
+
+ fn = jffs2_write_dnode(inode, ri, NULL, 0, phys_ofs, &writtenlen);
+ D1(printk(KERN_DEBUG "jffs2_create created file with mode 0x%x\n", ri->mode));
+ jffs2_free_raw_inode(ri);
+
+ if (IS_ERR(fn)) {
+ D1(printk(KERN_DEBUG "jffs2_write_dnode() failed\n"));
+ /* Eeek. Wave bye bye */
+ up(&f->sem);
+ jffs2_complete_reservation(c);
+ jffs2_clear_inode(inode);
+ return PTR_ERR(fn);
+ }
+ /* No data here. Only a metadata node, which will be
+ obsoleted by the first data write
+ */
+ f->metadata = fn;
+
+ /* Work out where to put the dirent node now. */
+ writtenlen = PAD(writtenlen);
+ phys_ofs += writtenlen;
+ alloclen -= writtenlen;
+ up(&f->sem);
+
+ if (alloclen < sizeof(*rd)+namelen) {
+ /* Not enough space left in this chunk. Get some more */
+ jffs2_complete_reservation(c);
+ ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
+
+ if (ret) {
+ /* Eep. */
+ D1(printk(KERN_DEBUG "jffs2_reserve_space() for dirent failed\n"));
+ jffs2_clear_inode(inode);
+ return ret;
+ }
+ }
+
+ rd = jffs2_alloc_raw_dirent();
+ if (!rd) {
+ /* Argh. Now we treat it like a normal delete */
+ jffs2_complete_reservation(c);
+ jffs2_clear_inode(inode);
+ return -ENOMEM;
+ }
+
+ dir_f = JFFS2_INODE_INFO(dir_i);
+ down(&dir_f->sem);
+
+ rd->magic = JFFS2_MAGIC_BITMASK;
+ rd->nodetype = JFFS2_NODETYPE_DIRENT;
+ rd->totlen = sizeof(*rd) + namelen;
+ rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
+
+ rd->pino = dir_i->i_ino;
+ rd->version = ++dir_f->highest_version;
+ rd->ino = inode->i_ino;
+ rd->mctime = CURRENT_TIME;
+ rd->nsize = namelen;
+ rd->type = DT_REG;
+ rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
+ rd->name_crc = crc32(0, dentry->d_name.name, namelen);
+
+ fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen);
+
+ jffs2_complete_reservation(c);
+
+ if (IS_ERR(fd)) {
+ /* dirent failed to write. Delete the inode normally
+ as if it were the final unlink() */
+ jffs2_free_raw_dirent(rd);
+ up(&dir_f->sem);
+ jffs2_clear_inode(inode);
+ return PTR_ERR(fd);
+ }
+
+ dir_i->i_mtime = dir_i->i_ctime = rd->mctime;
+
+ jffs2_free_raw_dirent(rd);
+
+ /* Link the fd into the inode's list, obsoleting an old
+ one if necessary. */
+ jffs2_add_fd_to_list(c, fd, &dir_f->dents);
+ up(&dir_f->sem);
+
+ d_instantiate(dentry, inode);
+
+ D1(printk(KERN_DEBUG "jffs2_create: Created ino #%lu with mode %o, nlink %d(%d). nrpages %ld\n",
+ inode->i_ino, inode->i_mode, inode->i_nlink, f->inocache->nlink, inode->i_mapping->nrpages));
+ return 0;
+}
+
+/***********************************************************************/
+
+static int jffs2_do_unlink(struct inode *dir_i, struct dentry *dentry, int rename)
+{
+ struct jffs2_inode_info *dir_f, *f;
+ struct jffs2_sb_info *c;
+ struct jffs2_raw_dirent *rd;
+ struct jffs2_full_dirent *fd;
+ __u32 alloclen, phys_ofs;
+ int ret;
+
+ c = JFFS2_SB_INFO(dir_i->i_sb);
+
+ rd = jffs2_alloc_raw_dirent();
+ if (!rd)
+ return -ENOMEM;
+
+ ret = jffs2_reserve_space(c, sizeof(*rd)+dentry->d_name.len, &phys_ofs, &alloclen, ALLOC_DELETION);
+ if (ret) {
+ jffs2_free_raw_dirent(rd);
+ return ret;
+ }
+
+ dir_f = JFFS2_INODE_INFO(dir_i);
+ down(&dir_f->sem);
+
+ /* Build a deletion node */
+ rd->magic = JFFS2_MAGIC_BITMASK;
+ rd->nodetype = JFFS2_NODETYPE_DIRENT;
+ rd->totlen = sizeof(*rd) + dentry->d_name.len;
+ rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
+
+ rd->pino = dir_i->i_ino;
+ rd->version = ++dir_f->highest_version;
+ rd->ino = 0;
+ rd->mctime = CURRENT_TIME;
+ rd->nsize = dentry->d_name.len;
+ rd->type = DT_UNKNOWN;
+ rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
+ rd->name_crc = crc32(0, dentry->d_name.name, dentry->d_name.len);
+
+ fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, dentry->d_name.len, phys_ofs, NULL);
+
+ jffs2_complete_reservation(c);
+ jffs2_free_raw_dirent(rd);
+
+ if (IS_ERR(fd)) {
+ up(&dir_f->sem);
+ return PTR_ERR(fd);
+ }
+
+ /* File it. This will mark the old one obsolete. */
+ jffs2_add_fd_to_list(c, fd, &dir_f->dents);
+ up(&dir_f->sem);
+
+ if (!rename) {
+ f = JFFS2_INODE_INFO(dentry->d_inode);
+ down(&f->sem);
+
+ while (f->dents) {
+ /* There can be only deleted ones */
+ fd = f->dents;
+
+ f->dents = fd->next;
+
+ if (fd->ino) {
+ printk(KERN_WARNING "Deleting inode #%u with active dentry \"%s\"->ino #%u\n",
+ f->inocache->ino, fd->name, fd->ino);
+ } else {
+ D1(printk(KERN_DEBUG "Removing deletion dirent for \"%s\" from dir ino #%u\n", fd->name, f->inocache->ino));
+ }
+ jffs2_mark_node_obsolete(c, fd->raw);
+ jffs2_free_full_dirent(fd);
+ }
+ /* Don't oops on unlinking a bad inode */
+ if (f->inocache)
+ f->inocache->nlink--;
+ dentry->d_inode->i_nlink--;
+ up(&f->sem);
+ }
+
+ return 0;
+}
+
+static int jffs2_unlink(struct inode *dir_i, struct dentry *dentry)
+{
+ return jffs2_do_unlink(dir_i, dentry, 0);
+}
+/***********************************************************************/
+
+static int jffs2_do_link (struct dentry *old_dentry, struct inode *dir_i, struct dentry *dentry, int rename)
+{
+ struct jffs2_inode_info *dir_f, *f;
+ struct jffs2_sb_info *c;
+ struct jffs2_raw_dirent *rd;
+ struct jffs2_full_dirent *fd;
+ __u32 alloclen, phys_ofs;
+ int ret;
+
+ c = JFFS2_SB_INFO(dir_i->i_sb);
+
+ rd = jffs2_alloc_raw_dirent();
+ if (!rd)
+ return -ENOMEM;
+
+ ret = jffs2_reserve_space(c, sizeof(*rd)+dentry->d_name.len, &phys_ofs, &alloclen, ALLOC_NORMAL);
+ if (ret) {
+ jffs2_free_raw_dirent(rd);
+ return ret;
+ }
+
+ dir_f = JFFS2_INODE_INFO(dir_i);
+ down(&dir_f->sem);
+
+ /* Build a deletion node */
+ rd->magic = JFFS2_MAGIC_BITMASK;
+ rd->nodetype = JFFS2_NODETYPE_DIRENT;
+ rd->totlen = sizeof(*rd) + dentry->d_name.len;
+ rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
+
+ rd->pino = dir_i->i_ino;
+ rd->version = ++dir_f->highest_version;
+ rd->ino = old_dentry->d_inode->i_ino;
+ rd->mctime = CURRENT_TIME;
+ rd->nsize = dentry->d_name.len;
+
+ /* XXX: This is ugly. */
+ rd->type = (old_dentry->d_inode->i_mode & S_IFMT) >> 12;
+ if (!rd->type) rd->type = DT_REG;
+
+ rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
+ rd->name_crc = crc32(0, dentry->d_name.name, dentry->d_name.len);
+
+ fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, dentry->d_name.len, phys_ofs, NULL);
+
+ jffs2_complete_reservation(c);
+ jffs2_free_raw_dirent(rd);
+
+ if (IS_ERR(fd)) {
+ up(&dir_f->sem);
+ return PTR_ERR(fd);
+ }
+
+ /* File it. This will mark the old one obsolete. */
+ jffs2_add_fd_to_list(c, fd, &dir_f->dents);
+ up(&dir_f->sem);
+
+ if (!rename) {
+ f = JFFS2_INODE_INFO(old_dentry->d_inode);
+ down(&f->sem);
+ old_dentry->d_inode->i_nlink = ++f->inocache->nlink;
+ up(&f->sem);
+ }
+ return 0;
+}
+
+static int jffs2_link (struct dentry *old_dentry, struct inode *dir_i, struct dentry *dentry)
+{
+ int ret;
+
+ /* Can't link a bad inode. */
+ if (!JFFS2_INODE_INFO(old_dentry->d_inode)->inocache)
+ return -EIO;
+
+ if (S_ISDIR(old_dentry->d_inode->i_mode))
+ return -EPERM;
+
+ ret = jffs2_do_link(old_dentry, dir_i, dentry, 0);
+ if (!ret) {
+ d_instantiate(dentry, old_dentry->d_inode);
+ atomic_inc(&old_dentry->d_inode->i_count);
+ }
+ return ret;
+}
+
+/***********************************************************************/
+
+static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char *target)
+{
+ struct jffs2_inode_info *f, *dir_f;
+ struct jffs2_sb_info *c;
+ struct inode *inode;
+ struct jffs2_raw_inode *ri;
+ struct jffs2_raw_dirent *rd;
+ struct jffs2_full_dnode *fn;
+ struct jffs2_full_dirent *fd;
+ int namelen;
+ __u32 alloclen, phys_ofs;
+ __u32 writtenlen;
+ int ret;
+
+ /* FIXME: If you care. We'd need to use frags for the target
+ if it grows much more than this */
+ if (strlen(target) > 254)
+ return -EINVAL;
+
+ ri = jffs2_alloc_raw_inode();
+
+ if (!ri)
+ return -ENOMEM;
+
+ c = JFFS2_SB_INFO(dir_i->i_sb);
+
+ /* Try to reserve enough space for both node and dirent.
+ * Just the node will do for now, though
+ */
+ namelen = dentry->d_name.len;
+ ret = jffs2_reserve_space(c, sizeof(*ri) + strlen(target), &phys_ofs, &alloclen, ALLOC_NORMAL);
+
+ if (ret) {
+ jffs2_free_raw_inode(ri);
+ return ret;
+ }
+
+ inode = jffs2_new_inode(dir_i, S_IFLNK | S_IRWXUGO, ri);
+
+ if (IS_ERR(inode)) {
+ jffs2_free_raw_inode(ri);
+ jffs2_complete_reservation(c);
+ return PTR_ERR(inode);
+ }
+
+ inode->i_op = &jffs2_symlink_inode_operations;
+
+ f = JFFS2_INODE_INFO(inode);
+
+ inode->i_size = ri->isize = ri->dsize = ri->csize = strlen(target);
+ ri->totlen = sizeof(*ri) + ri->dsize;
+ ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4);
+
+ ri->compr = JFFS2_COMPR_NONE;
+ ri->data_crc = crc32(0, target, strlen(target));
+ ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
+
+ fn = jffs2_write_dnode(inode, ri, target, strlen(target), phys_ofs, &writtenlen);
+
+ jffs2_free_raw_inode(ri);
+
+ if (IS_ERR(fn)) {
+ /* Eeek. Wave bye bye */
+ up(&f->sem);
+ jffs2_complete_reservation(c);
+ jffs2_clear_inode(inode);
+ return PTR_ERR(fn);
+ }
+ /* No data here. Only a metadata node, which will be
+ obsoleted by the first data write
+ */
+ f->metadata = fn;
+ up(&f->sem);
+
+ /* Work out where to put the dirent node now. */
+ writtenlen = (writtenlen+3)&~3;
+ phys_ofs += writtenlen;
+ alloclen -= writtenlen;
+
+ if (alloclen < sizeof(*rd)+namelen) {
+ /* Not enough space left in this chunk. Get some more */
+ jffs2_complete_reservation(c);
+ ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
+ if (ret) {
+ /* Eep. */
+ jffs2_clear_inode(inode);
+ return ret;
+ }
+ }
+
+ rd = jffs2_alloc_raw_dirent();
+ if (!rd) {
+ /* Argh. Now we treat it like a normal delete */
+ jffs2_complete_reservation(c);
+ jffs2_clear_inode(inode);
+ return -ENOMEM;
+ }
+
+ dir_f = JFFS2_INODE_INFO(dir_i);
+ down(&dir_f->sem);
+
+ rd->magic = JFFS2_MAGIC_BITMASK;
+ rd->nodetype = JFFS2_NODETYPE_DIRENT;
+ rd->totlen = sizeof(*rd) + namelen;
+ rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
+
+ rd->pino = dir_i->i_ino;
+ rd->version = ++dir_f->highest_version;
+ rd->ino = inode->i_ino;
+ rd->mctime = CURRENT_TIME;
+ rd->nsize = namelen;
+ rd->type = DT_LNK;
+ rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
+ rd->name_crc = crc32(0, dentry->d_name.name, namelen);
+
+ fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen);
+
+ jffs2_complete_reservation(c);
+
+ if (IS_ERR(fd)) {
+ /* dirent failed to write. Delete the inode normally
+ as if it were the final unlink() */
+ jffs2_free_raw_dirent(rd);
+ up(&dir_f->sem);
+ jffs2_clear_inode(inode);
+ return PTR_ERR(fd);
+ }
+
+ dir_i->i_mtime = dir_i->i_ctime = rd->mctime;
+
+ jffs2_free_raw_dirent(rd);
+
+ /* Link the fd into the inode's list, obsoleting an old
+ one if necessary. */
+ jffs2_add_fd_to_list(c, fd, &dir_f->dents);
+ up(&dir_f->sem);
+
+ d_instantiate(dentry, inode);
+ return 0;
+}
+
+
+static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode)
+{
+ struct jffs2_inode_info *f, *dir_f;
+ struct jffs2_sb_info *c;
+ struct inode *inode;
+ struct jffs2_raw_inode *ri;
+ struct jffs2_raw_dirent *rd;
+ struct jffs2_full_dnode *fn;
+ struct jffs2_full_dirent *fd;
+ int namelen;
+ __u32 alloclen, phys_ofs;
+ __u32 writtenlen;
+ int ret;
+
+ mode |= S_IFDIR;
+
+ ri = jffs2_alloc_raw_inode();
+ if (!ri)
+ return -ENOMEM;
+
+ c = JFFS2_SB_INFO(dir_i->i_sb);
+
+ /* Try to reserve enough space for both node and dirent.
+ * Just the node will do for now, though
+ */
+ namelen = dentry->d_name.len;
+ ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL);
+
+ if (ret) {
+ jffs2_free_raw_inode(ri);
+ return ret;
+ }
+
+ inode = jffs2_new_inode(dir_i, mode, ri);
+
+ if (IS_ERR(inode)) {
+ jffs2_free_raw_inode(ri);
+ jffs2_complete_reservation(c);
+ return PTR_ERR(inode);
+ }
+
+ inode->i_op = &jffs2_dir_inode_operations;
+ inode->i_fop = &jffs2_dir_operations;
+
+ f = JFFS2_INODE_INFO(inode);
+
+ ri->data_crc = 0;
+ ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
+
+ fn = jffs2_write_dnode(inode, ri, NULL, 0, phys_ofs, &writtenlen);
+
+ jffs2_free_raw_inode(ri);
+
+ if (IS_ERR(fn)) {
+ /* Eeek. Wave bye bye */
+ up(&f->sem);
+ jffs2_complete_reservation(c);
+ jffs2_clear_inode(inode);
+ return PTR_ERR(fn);
+ }
+ /* No data here. Only a metadata node, which will be
+ obsoleted by the first data write
+ */
+ f->metadata = fn;
+ up(&f->sem);
+
+ /* Work out where to put the dirent node now. */
+ writtenlen = PAD(writtenlen);
+ phys_ofs += writtenlen;
+ alloclen -= writtenlen;
+
+ if (alloclen < sizeof(*rd)+namelen) {
+ /* Not enough space left in this chunk. Get some more */
+ jffs2_complete_reservation(c);
+ ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
+ if (ret) {
+ /* Eep. */
+ jffs2_clear_inode(inode);
+ return ret;
+ }
+ }
+
+ rd = jffs2_alloc_raw_dirent();
+ if (!rd) {
+ /* Argh. Now we treat it like a normal delete */
+ jffs2_complete_reservation(c);
+ jffs2_clear_inode(inode);
+ return -ENOMEM;
+ }
+
+ dir_f = JFFS2_INODE_INFO(dir_i);
+ down(&dir_f->sem);
+
+ rd->magic = JFFS2_MAGIC_BITMASK;
+ rd->nodetype = JFFS2_NODETYPE_DIRENT;
+ rd->totlen = sizeof(*rd) + namelen;
+ rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
+
+ rd->pino = dir_i->i_ino;
+ rd->version = ++dir_f->highest_version;
+ rd->ino = inode->i_ino;
+ rd->mctime = CURRENT_TIME;
+ rd->nsize = namelen;
+ rd->type = DT_DIR;
+ rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
+ rd->name_crc = crc32(0, dentry->d_name.name, namelen);
+
+ fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen);
+
+ jffs2_complete_reservation(c);
+
+ if (IS_ERR(fd)) {
+ /* dirent failed to write. Delete the inode normally
+ as if it were the final unlink() */
+ jffs2_free_raw_dirent(rd);
+ up(&dir_f->sem);
+ jffs2_clear_inode(inode);
+ return PTR_ERR(fd);
+ }
+
+ dir_i->i_mtime = dir_i->i_ctime = rd->mctime;
+
+ jffs2_free_raw_dirent(rd);
+
+ /* Link the fd into the inode's list, obsoleting an old
+ one if necessary. */
+ jffs2_add_fd_to_list(c, fd, &dir_f->dents);
+ up(&dir_f->sem);
+
+ d_instantiate(dentry, inode);
+ return 0;
+}
+
+static int jffs2_rmdir (struct inode *dir_i, struct dentry *dentry)
+{
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode);
+ struct jffs2_full_dirent *fd;
+
+ for (fd = f->dents ; fd; fd = fd->next) {
+ if (fd->ino)
+ return -ENOTEMPTY;
+ }
+ return jffs2_unlink(dir_i, dentry);
+}
+
+static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, int rdev)
+{
+ struct jffs2_inode_info *f, *dir_f;
+ struct jffs2_sb_info *c;
+ struct inode *inode;
+ struct jffs2_raw_inode *ri;
+ struct jffs2_raw_dirent *rd;
+ struct jffs2_full_dnode *fn;
+ struct jffs2_full_dirent *fd;
+ int namelen;
+ unsigned short dev;
+ int devlen = 0;
+ __u32 alloclen, phys_ofs;
+ __u32 writtenlen;
+ int ret;
+
+ ri = jffs2_alloc_raw_inode();
+ if (!ri)
+ return -ENOMEM;
+
+ c = JFFS2_SB_INFO(dir_i->i_sb);
+
+ if (S_ISBLK(mode) || S_ISCHR(mode)) {
+ dev = (MAJOR(to_kdev_t(rdev)) << 8) | MINOR(to_kdev_t(rdev));
+ devlen = sizeof(dev);
+ }
+
+ /* Try to reserve enough space for both node and dirent.
+ * Just the node will do for now, though
+ */
+ namelen = dentry->d_name.len;
+ ret = jffs2_reserve_space(c, sizeof(*ri) + devlen, &phys_ofs, &alloclen, ALLOC_NORMAL);
+
+ if (ret) {
+ jffs2_free_raw_inode(ri);
+ return ret;
+ }
+
+ inode = jffs2_new_inode(dir_i, mode, ri);
+
+ if (IS_ERR(inode)) {
+ jffs2_free_raw_inode(ri);
+ jffs2_complete_reservation(c);
+ return PTR_ERR(inode);
+ }
+ inode->i_op = &jffs2_file_inode_operations;
+ init_special_inode(inode, inode->i_mode, rdev);
+
+ f = JFFS2_INODE_INFO(inode);
+
+ ri->dsize = ri->csize = devlen;
+ ri->totlen = sizeof(*ri) + ri->csize;
+ ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4);
+
+ ri->compr = JFFS2_COMPR_NONE;
+ ri->data_crc = crc32(0, &dev, devlen);
+ ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
+
+ fn = jffs2_write_dnode(inode, ri, (char *)&dev, devlen, phys_ofs, &writtenlen);
+
+ jffs2_free_raw_inode(ri);
+
+ if (IS_ERR(fn)) {
+ /* Eeek. Wave bye bye */
+ up(&f->sem);
+ jffs2_complete_reservation(c);
+ jffs2_clear_inode(inode);
+ return PTR_ERR(fn);
+ }
+ /* No data here. Only a metadata node, which will be
+ obsoleted by the first data write
+ */
+ f->metadata = fn;
+ up(&f->sem);
+
+ /* Work out where to put the dirent node now. */
+ writtenlen = (writtenlen+3)&~3;
+ phys_ofs += writtenlen;
+ alloclen -= writtenlen;
+
+ if (alloclen < sizeof(*rd)+namelen) {
+ /* Not enough space left in this chunk. Get some more */
+ jffs2_complete_reservation(c);
+ ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
+ if (ret) {
+ /* Eep. */
+ jffs2_clear_inode(inode);
+ return ret;
+ }
+ }
+
+ rd = jffs2_alloc_raw_dirent();
+ if (!rd) {
+ /* Argh. Now we treat it like a normal delete */
+ jffs2_complete_reservation(c);
+ jffs2_clear_inode(inode);
+ return -ENOMEM;
+ }
+
+ dir_f = JFFS2_INODE_INFO(dir_i);
+ down(&dir_f->sem);
+
+ rd->magic = JFFS2_MAGIC_BITMASK;
+ rd->nodetype = JFFS2_NODETYPE_DIRENT;
+ rd->totlen = sizeof(*rd) + namelen;
+ rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
+
+ rd->pino = dir_i->i_ino;
+ rd->version = ++dir_f->highest_version;
+ rd->ino = inode->i_ino;
+ rd->mctime = CURRENT_TIME;
+ rd->nsize = namelen;
+
+ /* XXX: This is ugly. */
+ rd->type = (mode & S_IFMT) >> 12;
+
+ rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
+ rd->name_crc = crc32(0, dentry->d_name.name, namelen);
+
+ fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen);
+
+ jffs2_complete_reservation(c);
+
+ if (IS_ERR(fd)) {
+ /* dirent failed to write. Delete the inode normally
+ as if it were the final unlink() */
+ jffs2_free_raw_dirent(rd);
+ up(&dir_f->sem);
+ jffs2_clear_inode(inode);
+ return PTR_ERR(fd);
+ }
+
+ dir_i->i_mtime = dir_i->i_ctime = rd->mctime;
+
+ jffs2_free_raw_dirent(rd);
+
+ /* Link the fd into the inode's list, obsoleting an old
+ one if necessary. */
+ jffs2_add_fd_to_list(c, fd, &dir_f->dents);
+ up(&dir_f->sem);
+
+ d_instantiate(dentry, inode);
+
+ return 0;
+}
+
+static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry,
+ struct inode *new_dir_i, struct dentry *new_dentry)
+{
+ int ret;
+ struct jffs2_inode_info *victim_f = NULL;
+
+ /* The VFS will check for us and prevent trying to rename a
+ * file over a directory and vice versa, but if it's a directory,
+ * the VFS can't check whether the victim is empty. The filesystem
+ * needs to do that for itself.
+ */
+ if (new_dentry->d_inode) {
+ victim_f = JFFS2_INODE_INFO(new_dentry->d_inode);
+ if (S_ISDIR(new_dentry->d_inode->i_mode)) {
+ struct jffs2_full_dirent *fd;
+
+ down(&victim_f->sem);
+ for (fd = victim_f->dents; fd; fd = fd->next) {
+ if (fd->ino) {
+ up(&victim_f->sem);
+ return -ENOTEMPTY;
+ }
+ }
+ up(&victim_f->sem);
+ }
+ }
+
+ /* XXX: We probably ought to alloc enough space for
+ both nodes at the same time. Writing the new link,
+ then getting -ENOSPC, is quite bad :)
+ */
+
+ /* Make a hard link */
+ ret = jffs2_do_link(old_dentry, new_dir_i, new_dentry, 1);
+ if (ret)
+ return ret;
+
+ if (victim_f) {
+ /* There was a victim. Kill it off nicely */
+ new_dentry->d_inode->i_nlink--;
+ /* Don't oops if the victim was a dirent pointing to an
+ inode which didn't exist. */
+ if (victim_f->inocache) {
+ down(&victim_f->sem);
+ victim_f->inocache->nlink--;
+ up(&victim_f->sem);
+ }
+ }
+
+ /* Unlink the original */
+ ret = jffs2_do_unlink(old_dir_i, old_dentry, 1);
+
+ if (ret) {
+ /* Oh shit. We really ought to make a single node which can do both atomically */
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(old_dentry->d_inode);
+ down(&f->sem);
+ if (f->inocache)
+ old_dentry->d_inode->i_nlink = f->inocache->nlink++;
+ up(&f->sem);
+
+ printk(KERN_NOTICE "jffs2_rename(): Link succeeded, unlink failed (err %d). You now have a hard link\n", ret);
+ /* Might as well let the VFS know */
+ d_instantiate(new_dentry, old_dentry->d_inode);
+ atomic_inc(&old_dentry->d_inode->i_count);
+ }
+ return ret;
+}
+
diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c
new file mode 100644
index 00000000000000..b10013e47fa404
--- /dev/null
+++ b/fs/jffs2/erase.c
@@ -0,0 +1,373 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ *
+ * The original JFFS, from which the design for JFFS2 was derived,
+ * was designed and implemented by Axis Communications AB.
+ *
+ * The contents of this file are subject to the Red Hat eCos Public
+ * License Version 1.1 (the "Licence"); you may not use this file
+ * except in compliance with the Licence. You may obtain a copy of
+ * the Licence at http://www.redhat.com/
+ *
+ * Software distributed under the Licence is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing rights and
+ * limitations under the Licence.
+ *
+ * The Original Code is JFFS2 - Journalling Flash File System, version 2
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in
+ * which case the provisions of the GPL are applicable instead of the
+ * above. If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use your
+ * version of this file under the RHEPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice and
+ * other provisions required by the GPL. If you do not delete the
+ * provisions above, a recipient may use your version of this file
+ * under either the RHEPL or the GPL.
+ *
+ * $Id: erase.c,v 1.24.2.1 2003/11/02 13:51:17 dwmw2 Exp $
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/jffs2.h>
+#include <linux/interrupt.h>
+#include "nodelist.h"
+#include <linux/crc32.h>
+
+struct erase_priv_struct {
+ struct jffs2_eraseblock *jeb;
+ struct jffs2_sb_info *c;
+};
+
+static void jffs2_erase_callback(struct erase_info *);
+static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
+
+void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+{
+ struct erase_info *instr;
+ int ret;
+
+ instr = kmalloc(sizeof(struct erase_info) + sizeof(struct erase_priv_struct), GFP_KERNEL);
+ if (!instr) {
+ printk(KERN_WARNING "kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n");
+ spin_lock_bh(&c->erase_completion_lock);
+ list_del(&jeb->list);
+ list_add(&jeb->list, &c->erase_pending_list);
+ c->erasing_size -= c->sector_size;
+ spin_unlock_bh(&c->erase_completion_lock);
+ return;
+ }
+
+ memset(instr, 0, sizeof(*instr));
+
+ instr->mtd = c->mtd;
+ instr->addr = jeb->offset;
+ instr->len = c->sector_size;
+ instr->callback = jffs2_erase_callback;
+ instr->priv = (unsigned long)(&instr[1]);
+
+ ((struct erase_priv_struct *)instr->priv)->jeb = jeb;
+ ((struct erase_priv_struct *)instr->priv)->c = c;
+
+ ret = c->mtd->erase(c->mtd, instr);
+ if (!ret) {
+ return;
+ }
+ if (ret == -ENOMEM || ret == -EAGAIN) {
+ /* Erase failed immediately. Refile it on the list */
+ D1(printk(KERN_DEBUG "Erase at 0x%08x failed: %d. Refiling on erase_pending_list\n", jeb->offset, ret));
+ spin_lock_bh(&c->erase_completion_lock);
+ list_del(&jeb->list);
+ list_add(&jeb->list, &c->erase_pending_list);
+ c->erasing_size -= c->sector_size;
+ spin_unlock_bh(&c->erase_completion_lock);
+ kfree(instr);
+ return;
+ }
+
+ if (ret == -EROFS)
+ printk(KERN_WARNING "Erase at 0x%08x failed immediately: -EROFS. Is the sector locked?\n", jeb->offset);
+ else
+ printk(KERN_WARNING "Erase at 0x%08x failed immediately: errno %d\n", jeb->offset, ret);
+ spin_lock_bh(&c->erase_completion_lock);
+ list_del(&jeb->list);
+ list_add(&jeb->list, &c->bad_list);
+ c->nr_erasing_blocks--;
+ c->bad_size += c->sector_size;
+ c->erasing_size -= c->sector_size;
+ spin_unlock_bh(&c->erase_completion_lock);
+ wake_up(&c->erase_wait);
+ kfree(instr);
+}
+
+void jffs2_erase_pending_blocks(struct jffs2_sb_info *c)
+{
+ struct jffs2_eraseblock *jeb;
+
+ spin_lock_bh(&c->erase_completion_lock);
+ while (!list_empty(&c->erase_pending_list)) {
+
+ jeb = list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list);
+
+ D1(printk(KERN_DEBUG "Starting erase of pending block 0x%08x\n", jeb->offset));
+
+ list_del(&jeb->list);
+ c->erasing_size += c->sector_size;
+ c->free_size -= jeb->free_size;
+ c->used_size -= jeb->used_size;
+ c->dirty_size -= jeb->dirty_size;
+ jeb->used_size = jeb->dirty_size = jeb->free_size = 0;
+ jffs2_free_all_node_refs(c, jeb);
+ list_add(&jeb->list, &c->erasing_list);
+ spin_unlock_bh(&c->erase_completion_lock);
+
+ jffs2_erase_block(c, jeb);
+ /* Be nice */
+ if (current->need_resched)
+ schedule();
+ spin_lock_bh(&c->erase_completion_lock);
+ }
+ spin_unlock_bh(&c->erase_completion_lock);
+ D1(printk(KERN_DEBUG "jffs2_erase_pending_blocks completed\n"));
+}
+
+
+static void jffs2_erase_callback(struct erase_info *instr)
+{
+ struct erase_priv_struct *priv = (void *)instr->priv;
+
+ if(instr->state != MTD_ERASE_DONE) {
+ printk(KERN_WARNING "Erase at 0x%08x finished, but state != MTD_ERASE_DONE. State is 0x%x instead.\n", instr->addr, instr->state);
+ spin_lock(&priv->c->erase_completion_lock);
+ priv->c->erasing_size -= priv->c->sector_size;
+ priv->c->bad_size += priv->c->sector_size;
+ list_del(&priv->jeb->list);
+ list_add(&priv->jeb->list, &priv->c->bad_list);
+ priv->c->nr_erasing_blocks--;
+ spin_unlock(&priv->c->erase_completion_lock);
+ wake_up(&priv->c->erase_wait);
+ } else {
+ D1(printk(KERN_DEBUG "Erase completed successfully at 0x%08x\n", instr->addr));
+ spin_lock(&priv->c->erase_completion_lock);
+ list_del(&priv->jeb->list);
+ list_add_tail(&priv->jeb->list, &priv->c->erase_complete_list);
+ spin_unlock(&priv->c->erase_completion_lock);
+ }
+ /* Make sure someone picks up the block off the erase_complete list */
+ OFNI_BS_2SFFJ(priv->c)->s_dirt = 1;
+ kfree(instr);
+}
+
+/* Hmmm. Maybe we should accept the extra space it takes and make
+ this a standard doubly-linked list? */
+static inline void jffs2_remove_node_refs_from_ino_list(struct jffs2_sb_info *c,
+ struct jffs2_raw_node_ref *ref, struct jffs2_eraseblock *jeb)
+{
+ struct jffs2_inode_cache *ic = NULL;
+ struct jffs2_raw_node_ref **prev;
+
+ prev = &ref->next_in_ino;
+
+ /* Walk the inode's list once, removing any nodes from this eraseblock */
+ while (1) {
+ if (!(*prev)->next_in_ino) {
+ /* We're looking at the jffs2_inode_cache, which is
+ at the end of the linked list. Stash it and continue
+ from the beginning of the list */
+ ic = (struct jffs2_inode_cache *)(*prev);
+ prev = &ic->nodes;
+ continue;
+ }
+
+ if (((*prev)->flash_offset & ~(c->sector_size -1)) == jeb->offset) {
+ /* It's in the block we're erasing */
+ struct jffs2_raw_node_ref *this;
+
+ this = *prev;
+ *prev = this->next_in_ino;
+ this->next_in_ino = NULL;
+
+ if (this == ref)
+ break;
+
+ continue;
+ }
+ /* Not to be deleted. Skip */
+ prev = &((*prev)->next_in_ino);
+ }
+
+ /* PARANOIA */
+ if (!ic) {
+ printk(KERN_WARNING "inode_cache not found in remove_node_refs()!!\n");
+ return;
+ }
+
+ D1(printk(KERN_DEBUG "Removed nodes in range 0x%08x-0x%08x from ino #%u\n",
+ jeb->offset, jeb->offset + c->sector_size, ic->ino));
+
+ D2({
+ int i=0;
+ struct jffs2_raw_node_ref *this;
+ printk(KERN_DEBUG "After remove_node_refs_from_ino_list: \n" KERN_DEBUG);
+
+ this = ic->nodes;
+
+ while(this) {
+ printk( "0x%08x(%d)->", this->flash_offset & ~3, this->flash_offset &3);
+ if (++i == 5) {
+ printk("\n" KERN_DEBUG);
+ i=0;
+ }
+ this = this->next_in_ino;
+ }
+ printk("\n");
+ });
+
+ if (ic->nodes == (void *)ic) {
+ D1(printk(KERN_DEBUG "inocache for ino #%u is all gone now. Freeing\n", ic->ino));
+ jffs2_del_ino_cache(c, ic);
+ jffs2_free_inode_cache(ic);
+ }
+}
+
+static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+{
+ struct jffs2_raw_node_ref *ref;
+ D1(printk(KERN_DEBUG "Freeing all node refs for eraseblock offset 0x%08x\n", jeb->offset));
+ while(jeb->first_node) {
+ ref = jeb->first_node;
+ jeb->first_node = ref->next_phys;
+
+ /* Remove from the inode-list */
+ if (ref->next_in_ino)
+ jffs2_remove_node_refs_from_ino_list(c, ref, jeb);
+ /* else it was a non-inode node or already removed, so don't bother */
+
+ jffs2_free_raw_node_ref(ref);
+ }
+ jeb->last_node = NULL;
+}
+
+void jffs2_erase_pending_trigger(struct jffs2_sb_info *c)
+{
+ OFNI_BS_2SFFJ(c)->s_dirt = 1;
+}
+
+void jffs2_mark_erased_blocks(struct jffs2_sb_info *c)
+{
+ static struct jffs2_unknown_node marker = {JFFS2_MAGIC_BITMASK, JFFS2_NODETYPE_CLEANMARKER, sizeof(struct jffs2_unknown_node)};
+ struct jffs2_eraseblock *jeb;
+ struct jffs2_raw_node_ref *marker_ref;
+ unsigned char *ebuf;
+ ssize_t retlen;
+ int ret;
+
+ marker.hdr_crc = crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4);
+
+ spin_lock_bh(&c->erase_completion_lock);
+ while (!list_empty(&c->erase_complete_list)) {
+ jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list);
+ list_del(&jeb->list);
+ spin_unlock_bh(&c->erase_completion_lock);
+
+ marker_ref = jffs2_alloc_raw_node_ref();
+ if (!marker_ref) {
+ printk(KERN_WARNING "Failed to allocate raw node ref for clean marker\n");
+ /* Come back later */
+ jffs2_erase_pending_trigger(c);
+ return;
+ }
+
+ ebuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!ebuf) {
+ printk(KERN_WARNING "Failed to allocate page buffer for verifying erase at 0x%08x. Assuming it worked\n", jeb->offset);
+ } else {
+ __u32 ofs = jeb->offset;
+
+ D1(printk(KERN_DEBUG "Verifying erase at 0x%08x\n", jeb->offset));
+ while(ofs < jeb->offset + c->sector_size) {
+ __u32 readlen = min((__u32)PAGE_SIZE, jeb->offset + c->sector_size - ofs);
+ int i;
+
+ ret = c->mtd->read(c->mtd, ofs, readlen, &retlen, ebuf);
+ if (ret < 0) {
+ printk(KERN_WARNING "Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n", ofs, ret);
+ goto bad;
+ }
+ if (retlen != readlen) {
+ printk(KERN_WARNING "Short read from newly-erased block at 0x%08x. Wanted %d, got %d\n", ofs, readlen, retlen);
+ goto bad;
+ }
+ for (i=0; i<readlen; i += sizeof(unsigned long)) {
+ /* It's OK. We know it's properly aligned */
+ unsigned long datum = *(unsigned long *)(&ebuf[i]);
+ if (datum + 1) {
+ printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", datum, ofs + i);
+ bad:
+ jffs2_free_raw_node_ref(marker_ref);
+ kfree(ebuf);
+ bad2:
+ spin_lock_bh(&c->erase_completion_lock);
+ c->erasing_size -= c->sector_size;
+ c->bad_size += c->sector_size;
+
+ list_add_tail(&jeb->list, &c->bad_list);
+ c->nr_erasing_blocks--;
+ spin_unlock_bh(&c->erase_completion_lock);
+ wake_up(&c->erase_wait);
+ return;
+ }
+ }
+ ofs += readlen;
+ }
+ kfree(ebuf);
+ }
+
+ /* Write the erase complete marker */
+ D1(printk(KERN_DEBUG "Writing erased marker to block at 0x%08x\n", jeb->offset));
+ ret = c->mtd->write(c->mtd, jeb->offset, sizeof(marker), &retlen, (char *)&marker);
+ if (ret) {
+ printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n",
+ jeb->offset, ret);
+ goto bad2;
+ }
+ if (retlen != sizeof(marker)) {
+ printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %d, got %d\n",
+ jeb->offset, sizeof(marker), retlen);
+ goto bad2;
+ }
+
+ marker_ref->next_in_ino = NULL;
+ marker_ref->next_phys = NULL;
+ marker_ref->flash_offset = jeb->offset;
+ marker_ref->totlen = PAD(sizeof(marker));
+
+ jeb->first_node = jeb->last_node = marker_ref;
+
+ jeb->free_size = c->sector_size - marker_ref->totlen;
+ jeb->used_size = marker_ref->totlen;
+ jeb->dirty_size = 0;
+
+ spin_lock_bh(&c->erase_completion_lock);
+ c->erasing_size -= c->sector_size;
+ c->free_size += jeb->free_size;
+ c->used_size += jeb->used_size;
+
+ ACCT_SANITY_CHECK(c,jeb);
+ ACCT_PARANOIA_CHECK(jeb);
+
+ list_add_tail(&jeb->list, &c->free_list);
+ c->nr_erasing_blocks--;
+ c->nr_free_blocks++;
+ wake_up(&c->erase_wait);
+ }
+ spin_unlock_bh(&c->erase_completion_lock);
+}
diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c
new file mode 100644
index 00000000000000..36eb32ba435065
--- /dev/null
+++ b/fs/jffs2/file.c
@@ -0,0 +1,557 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ *
+ * The original JFFS, from which the design for JFFS2 was derived,
+ * was designed and implemented by Axis Communications AB.
+ *
+ * The contents of this file are subject to the Red Hat eCos Public
+ * License Version 1.1 (the "Licence"); you may not use this file
+ * except in compliance with the Licence. You may obtain a copy of
+ * the Licence at http://www.redhat.com/
+ *
+ * Software distributed under the Licence is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing rights and
+ * limitations under the Licence.
+ *
+ * The Original Code is JFFS2 - Journalling Flash File System, version 2
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in
+ * which case the provisions of the GPL are applicable instead of the
+ * above. If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use your
+ * version of this file under the RHEPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice and
+ * other provisions required by the GPL. If you do not delete the
+ * provisions above, a recipient may use your version of this file
+ * under either the RHEPL or the GPL.
+ *
+ * $Id: file.c,v 1.58.2.7 2003/11/02 13:51:17 dwmw2 Exp $
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/mtd/compatmac.h> /* for min() */
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/pagemap.h>
+#include <linux/jffs2.h>
+#include "nodelist.h"
+#include <linux/crc32.h>
+
+extern int generic_file_open(struct inode *, struct file *) __attribute__((weak));
+extern loff_t generic_file_llseek(struct file *file, loff_t offset, int origin) __attribute__((weak));
+
+
+int jffs2_null_fsync(struct file *filp, struct dentry *dentry, int datasync)
+{
+ /* Move along. Nothing to see here */
+ return 0;
+}
+
+struct file_operations jffs2_file_operations =
+{
+ llseek: generic_file_llseek,
+ open: generic_file_open,
+ read: generic_file_read,
+ write: generic_file_write,
+ ioctl: jffs2_ioctl,
+ mmap: generic_file_mmap,
+ fsync: jffs2_null_fsync
+};
+
+/* jffs2_file_inode_operations */
+
+struct inode_operations jffs2_file_inode_operations =
+{
+ setattr: jffs2_setattr
+};
+
+struct address_space_operations jffs2_file_address_operations =
+{
+ readpage: jffs2_readpage,
+ prepare_write: jffs2_prepare_write,
+ commit_write: jffs2_commit_write
+};
+
+int jffs2_setattr (struct dentry *dentry, struct iattr *iattr)
+{
+ struct jffs2_full_dnode *old_metadata, *new_metadata;
+ struct inode *inode = dentry->d_inode;
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
+ struct jffs2_raw_inode *ri;
+ unsigned short dev;
+ unsigned char *mdata = NULL;
+ int mdatalen = 0;
+ unsigned int ivalid;
+ __u32 phys_ofs, alloclen;
+ int ret;
+ D1(printk(KERN_DEBUG "jffs2_setattr(): ino #%lu\n", inode->i_ino));
+ ret = inode_change_ok(inode, iattr);
+ if (ret)
+ return ret;
+
+ /* Special cases - we don't want more than one data node
+ for these types on the medium at any time. So setattr
+ must read the original data associated with the node
+ (i.e. the device numbers or the target name) and write
+ it out again with the appropriate data attached */
+ if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) {
+ /* For these, we don't actually need to read the old node */
+ dev = (MAJOR(to_kdev_t(dentry->d_inode->i_rdev)) << 8) |
+ MINOR(to_kdev_t(dentry->d_inode->i_rdev));
+ mdata = (char *)&dev;
+ mdatalen = sizeof(dev);
+ D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of kdev_t\n", mdatalen));
+ } else if (S_ISLNK(inode->i_mode)) {
+ mdatalen = f->metadata->size;
+ mdata = kmalloc(f->metadata->size, GFP_USER);
+ if (!mdata)
+ return -ENOMEM;
+ ret = jffs2_read_dnode(c, f->metadata, mdata, 0, mdatalen);
+ if (ret) {
+ kfree(mdata);
+ return ret;
+ }
+ D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of symlink target\n", mdatalen));
+ }
+
+ ri = jffs2_alloc_raw_inode();
+ if (!ri) {
+ if (S_ISLNK(inode->i_mode))
+ kfree(mdata);
+ return -ENOMEM;
+ }
+
+ ret = jffs2_reserve_space(c, sizeof(*ri) + mdatalen, &phys_ofs, &alloclen, ALLOC_NORMAL);
+ if (ret) {
+ jffs2_free_raw_inode(ri);
+ if (S_ISLNK(inode->i_mode))
+ kfree(mdata);
+ return ret;
+ }
+ down(&f->sem);
+ ivalid = iattr->ia_valid;
+
+ ri->magic = JFFS2_MAGIC_BITMASK;
+ ri->nodetype = JFFS2_NODETYPE_INODE;
+ ri->totlen = sizeof(*ri) + mdatalen;
+ ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4);
+
+ ri->ino = inode->i_ino;
+ ri->version = ++f->highest_version;
+
+ ri->mode = (ivalid & ATTR_MODE)?iattr->ia_mode:inode->i_mode;
+ ri->uid = (ivalid & ATTR_UID)?iattr->ia_uid:inode->i_uid;
+ ri->gid = (ivalid & ATTR_GID)?iattr->ia_gid:inode->i_gid;
+
+ if (ivalid & ATTR_MODE && ri->mode & S_ISGID &&
+ !in_group_p(ri->gid) && !capable(CAP_FSETID))
+ ri->mode &= ~S_ISGID;
+
+ ri->isize = (ivalid & ATTR_SIZE)?iattr->ia_size:inode->i_size;
+ ri->atime = (ivalid & ATTR_ATIME)?iattr->ia_atime:inode->i_atime;
+ ri->mtime = (ivalid & ATTR_MTIME)?iattr->ia_mtime:inode->i_mtime;
+ ri->ctime = (ivalid & ATTR_CTIME)?iattr->ia_ctime:inode->i_ctime;
+
+ ri->offset = 0;
+ ri->csize = ri->dsize = mdatalen;
+ ri->compr = JFFS2_COMPR_NONE;
+ if (inode->i_size < ri->isize) {
+ /* It's an extension. Make it a hole node */
+ ri->compr = JFFS2_COMPR_ZERO;
+ ri->dsize = ri->isize - inode->i_size;
+ ri->offset = inode->i_size;
+ }
+ ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
+ if (mdatalen)
+ ri->data_crc = crc32(0, mdata, mdatalen);
+ else
+ ri->data_crc = 0;
+
+ new_metadata = jffs2_write_dnode(inode, ri, mdata, mdatalen, phys_ofs, NULL);
+ if (S_ISLNK(inode->i_mode))
+ kfree(mdata);
+
+ jffs2_complete_reservation(c);
+
+ if (IS_ERR(new_metadata)) {
+ jffs2_free_raw_inode(ri);
+ up(&f->sem);
+ return PTR_ERR(new_metadata);
+ }
+ /* It worked. Update the inode */
+ inode->i_atime = ri->atime;
+ inode->i_ctime = ri->ctime;
+ inode->i_mtime = ri->mtime;
+ inode->i_mode = ri->mode;
+ inode->i_uid = ri->uid;
+ inode->i_gid = ri->gid;
+
+
+ old_metadata = f->metadata;
+
+ if (inode->i_size > ri->isize) {
+ vmtruncate(inode, ri->isize);
+ jffs2_truncate_fraglist (c, &f->fraglist, ri->isize);
+ }
+
+ if (inode->i_size < ri->isize) {
+ jffs2_add_full_dnode_to_inode(c, f, new_metadata);
+ inode->i_size = ri->isize;
+ f->metadata = NULL;
+ } else {
+ f->metadata = new_metadata;
+ }
+ if (old_metadata) {
+ jffs2_mark_node_obsolete(c, old_metadata->raw);
+ jffs2_free_full_dnode(old_metadata);
+ }
+ jffs2_free_raw_inode(ri);
+ up(&f->sem);
+ return 0;
+}
+
+int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg)
+{
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
+ struct jffs2_node_frag *frag = f->fraglist;
+ __u32 offset = pg->index << PAGE_CACHE_SHIFT;
+ __u32 end = offset + PAGE_CACHE_SIZE;
+ unsigned char *pg_buf;
+ int ret;
+
+ D1(printk(KERN_DEBUG "jffs2_do_readpage_nolock(): ino #%lu, page at offset 0x%x\n", inode->i_ino, offset));
+
+ if (!PageLocked(pg))
+ PAGE_BUG(pg);
+
+ while(frag && frag->ofs + frag->size <= offset) {
+ // D1(printk(KERN_DEBUG "skipping frag %d-%d; before the region we care about\n", frag->ofs, frag->ofs + frag->size));
+ frag = frag->next;
+ }
+
+ pg_buf = kmap(pg);
+
+ /* XXX FIXME: Where a single physical node actually shows up in two
+ frags, we read it twice. Don't do that. */
+ /* Now we're pointing at the first frag which overlaps our page */
+ while(offset < end) {
+ D2(printk(KERN_DEBUG "jffs2_readpage: offset %d, end %d\n", offset, end));
+ if (!frag || frag->ofs > offset) {
+ __u32 holesize = end - offset;
+ if (frag) {
+ D1(printk(KERN_NOTICE "Eep. Hole in ino %ld fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n", inode->i_ino, frag->ofs, offset));
+ holesize = min(holesize, frag->ofs - offset);
+ D1(jffs2_print_frag_list(f));
+ }
+ D1(printk(KERN_DEBUG "Filling non-frag hole from %d-%d\n", offset, offset+holesize));
+ memset(pg_buf, 0, holesize);
+ pg_buf += holesize;
+ offset += holesize;
+ continue;
+ } else if (frag->ofs < offset && (offset & (PAGE_CACHE_SIZE-1)) != 0) {
+ D1(printk(KERN_NOTICE "Eep. Overlap in ino #%ld fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n",
+ inode->i_ino, frag->ofs, offset));
+ D1(jffs2_print_frag_list(f));
+ memset(pg_buf, 0, end - offset);
+ ClearPageUptodate(pg);
+ SetPageError(pg);
+ kunmap(pg);
+ return -EIO;
+ } else if (!frag->node) {
+ __u32 holeend = min(end, frag->ofs + frag->size);
+ D1(printk(KERN_DEBUG "Filling frag hole from %d-%d (frag 0x%x 0x%x)\n", offset, holeend, frag->ofs, frag->ofs + frag->size));
+ memset(pg_buf, 0, holeend - offset);
+ pg_buf += holeend - offset;
+ offset = holeend;
+ frag = frag->next;
+ continue;
+ } else {
+ __u32 readlen;
+ __u32 fragofs; /* offset within the frag to start reading */
+
+ fragofs = offset - frag->ofs;
+ readlen = min(frag->size - fragofs, end - offset);
+ D1(printk(KERN_DEBUG "Reading %d-%d from node at 0x%x\n", frag->ofs+fragofs,
+ fragofs+frag->ofs+readlen, frag->node->raw->flash_offset & ~3));
+ ret = jffs2_read_dnode(c, frag->node, pg_buf, fragofs + frag->ofs - frag->node->ofs, readlen);
+ D2(printk(KERN_DEBUG "node read done\n"));
+ if (ret) {
+ D1(printk(KERN_DEBUG"jffs2_readpage error %d\n",ret));
+ memset(pg_buf, 0, readlen);
+ ClearPageUptodate(pg);
+ SetPageError(pg);
+ kunmap(pg);
+ return ret;
+ }
+
+ pg_buf += readlen;
+ offset += readlen;
+ frag = frag->next;
+ D2(printk(KERN_DEBUG "node read was OK. Looping\n"));
+ }
+ }
+ D2(printk(KERN_DEBUG "readpage finishing\n"));
+ SetPageUptodate(pg);
+ ClearPageError(pg);
+
+ flush_dcache_page(pg);
+
+ kunmap(pg);
+ D1(printk(KERN_DEBUG "readpage finished\n"));
+ return 0;
+}
+
+int jffs2_do_readpage_unlock(struct inode *inode, struct page *pg)
+{
+ int ret = jffs2_do_readpage_nolock(inode, pg);
+ UnlockPage(pg);
+ return ret;
+}
+
+
+int jffs2_readpage (struct file *filp, struct page *pg)
+{
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(pg->mapping->host);
+ int ret;
+
+ down(&f->sem);
+ ret = jffs2_do_readpage_unlock(pg->mapping->host, pg);
+ up(&f->sem);
+ return ret;
+}
+
+int jffs2_prepare_write (struct file *filp, struct page *pg, unsigned start, unsigned end)
+{
+ struct inode *inode = pg->mapping->host;
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ __u32 pageofs = pg->index << PAGE_CACHE_SHIFT;
+ int ret = 0;
+
+ D1(printk(KERN_DEBUG "jffs2_prepare_write() nrpages %ld\n", inode->i_mapping->nrpages));
+
+ if (pageofs > inode->i_size) {
+ /* Make new hole frag from old EOF to new page */
+ struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
+ struct jffs2_raw_inode ri;
+ struct jffs2_full_dnode *fn;
+ __u32 phys_ofs, alloc_len;
+
+ D1(printk(KERN_DEBUG "Writing new hole frag 0x%x-0x%x between current EOF and new page\n",
+ (unsigned int)inode->i_size, pageofs));
+
+ ret = jffs2_reserve_space(c, sizeof(ri), &phys_ofs, &alloc_len, ALLOC_NORMAL);
+ if (ret)
+ return ret;
+
+ down(&f->sem);
+ memset(&ri, 0, sizeof(ri));
+
+ ri.magic = JFFS2_MAGIC_BITMASK;
+ ri.nodetype = JFFS2_NODETYPE_INODE;
+ ri.totlen = sizeof(ri);
+ ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4);
+
+ ri.ino = f->inocache->ino;
+ ri.version = ++f->highest_version;
+ ri.mode = inode->i_mode;
+ ri.uid = inode->i_uid;
+ ri.gid = inode->i_gid;
+ ri.isize = max((__u32)inode->i_size, pageofs);
+ ri.atime = ri.ctime = ri.mtime = CURRENT_TIME;
+ ri.offset = inode->i_size;
+ ri.dsize = pageofs - inode->i_size;
+ ri.csize = 0;
+ ri.compr = JFFS2_COMPR_ZERO;
+ ri.node_crc = crc32(0, &ri, sizeof(ri)-8);
+ ri.data_crc = 0;
+
+ fn = jffs2_write_dnode(inode, &ri, NULL, 0, phys_ofs, NULL);
+ jffs2_complete_reservation(c);
+ if (IS_ERR(fn)) {
+ ret = PTR_ERR(fn);
+ up(&f->sem);
+ return ret;
+ }
+ ret = jffs2_add_full_dnode_to_inode(c, f, fn);
+ if (f->metadata) {
+ jffs2_mark_node_obsolete(c, f->metadata->raw);
+ jffs2_free_full_dnode(f->metadata);
+ f->metadata = NULL;
+ }
+ if (ret) {
+ D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in prepare_write, returned %d\n", ret));
+ jffs2_mark_node_obsolete(c, fn->raw);
+ jffs2_free_full_dnode(fn);
+ up(&f->sem);
+ return ret;
+ }
+ inode->i_size = pageofs;
+ up(&f->sem);
+ }
+
+
+ /* Read in the page if it wasn't already present, unless it's a whole page */
+ if (!Page_Uptodate(pg) && (start || end < PAGE_CACHE_SIZE)) {
+ down(&f->sem);
+ ret = jffs2_do_readpage_nolock(inode, pg);
+ up(&f->sem);
+ }
+ D1(printk(KERN_DEBUG "end prepare_write(). pg->flags %lx\n", pg->flags));
+ return ret;
+}
+
+int jffs2_commit_write (struct file *filp, struct page *pg, unsigned start, unsigned end)
+{
+ /* Actually commit the write from the page cache page we're looking at.
+ * For now, we write the full page out each time. It sucks, but it's simple
+ */
+ struct inode *inode = pg->mapping->host;
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
+ __u32 newsize = max_t(__u32, filp->f_dentry->d_inode->i_size, (pg->index << PAGE_CACHE_SHIFT) + end);
+ __u32 file_ofs = (pg->index << PAGE_CACHE_SHIFT);
+ __u32 writelen = min((__u32)PAGE_CACHE_SIZE, newsize - file_ofs);
+ struct jffs2_raw_inode *ri;
+ int ret = 0;
+ ssize_t writtenlen = 0;
+
+ D1(printk(KERN_DEBUG "jffs2_commit_write(): ino #%lu, page at 0x%lx, range %d-%d, flags %lx\n", inode->i_ino, pg->index << PAGE_CACHE_SHIFT, start, end, pg->flags));
+
+ if (!start && end == PAGE_CACHE_SIZE) {
+ /* We need to avoid deadlock with page_cache_read() in
+ jffs2_garbage_collect_pass(). So we have to mark the
+ page up to date, to prevent page_cache_read() from
+ trying to re-lock it. */
+ SetPageUptodate(pg);
+ }
+
+ ri = jffs2_alloc_raw_inode();
+ if (!ri)
+ return -ENOMEM;
+
+ while(writelen) {
+ struct jffs2_full_dnode *fn;
+ unsigned char *comprbuf = NULL;
+ unsigned char comprtype = JFFS2_COMPR_NONE;
+ __u32 phys_ofs, alloclen;
+ __u32 datalen, cdatalen;
+
+ D2(printk(KERN_DEBUG "jffs2_commit_write() loop: 0x%x to write to 0x%x\n", writelen, file_ofs));
+
+ ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen, ALLOC_NORMAL);
+ if (ret) {
+ SetPageError(pg);
+ D1(printk(KERN_DEBUG "jffs2_reserve_space returned %d\n", ret));
+ break;
+ }
+ down(&f->sem);
+ datalen = writelen;
+ cdatalen = min(alloclen - sizeof(*ri), writelen);
+
+ comprbuf = kmalloc(cdatalen, GFP_KERNEL);
+ if (comprbuf) {
+ comprtype = jffs2_compress(page_address(pg)+ (file_ofs & (PAGE_CACHE_SIZE-1)), comprbuf, &datalen, &cdatalen);
+ }
+ if (comprtype == JFFS2_COMPR_NONE) {
+ /* Either compression failed, or the allocation of comprbuf failed */
+ if (comprbuf)
+ kfree(comprbuf);
+ comprbuf = page_address(pg) + (file_ofs & (PAGE_CACHE_SIZE -1));
+ datalen = cdatalen;
+ }
+ /* Now comprbuf points to the data to be written, be it compressed or not.
+ comprtype holds the compression type, and comprtype == JFFS2_COMPR_NONE means
+ that the comprbuf doesn't need to be kfree()d.
+ */
+
+ ri->magic = JFFS2_MAGIC_BITMASK;
+ ri->nodetype = JFFS2_NODETYPE_INODE;
+ ri->totlen = sizeof(*ri) + cdatalen;
+ ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4);
+
+ ri->ino = inode->i_ino;
+ ri->version = ++f->highest_version;
+ ri->mode = inode->i_mode;
+ ri->uid = inode->i_uid;
+ ri->gid = inode->i_gid;
+ ri->isize = max((__u32)inode->i_size, file_ofs + datalen);
+ ri->atime = ri->ctime = ri->mtime = CURRENT_TIME;
+ ri->offset = file_ofs;
+ ri->csize = cdatalen;
+ ri->dsize = datalen;
+ ri->compr = comprtype;
+ ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
+ ri->data_crc = crc32(0, comprbuf, cdatalen);
+
+ fn = jffs2_write_dnode(inode, ri, comprbuf, cdatalen, phys_ofs, NULL);
+
+ jffs2_complete_reservation(c);
+
+ if (comprtype != JFFS2_COMPR_NONE)
+ kfree(comprbuf);
+
+ if (IS_ERR(fn)) {
+ ret = PTR_ERR(fn);
+ up(&f->sem);
+ SetPageError(pg);
+ break;
+ }
+ ret = jffs2_add_full_dnode_to_inode(c, f, fn);
+ if (f->metadata) {
+ jffs2_mark_node_obsolete(c, f->metadata->raw);
+ jffs2_free_full_dnode(f->metadata);
+ f->metadata = NULL;
+ }
+ up(&f->sem);
+ if (ret) {
+ /* Eep */
+ D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in commit_write, returned %d\n", ret));
+ jffs2_mark_node_obsolete(c, fn->raw);
+ jffs2_free_full_dnode(fn);
+ SetPageError(pg);
+ break;
+ }
+ inode->i_size = ri->isize;
+ inode->i_blocks = (inode->i_size + 511) >> 9;
+ inode->i_ctime = inode->i_mtime = ri->ctime;
+ if (!datalen) {
+ printk(KERN_WARNING "Eep. We didn't actually write any bloody data\n");
+ ret = -EIO;
+ SetPageError(pg);
+ break;
+ }
+ D1(printk(KERN_DEBUG "increasing writtenlen by %d\n", datalen));
+ writtenlen += datalen;
+ file_ofs += datalen;
+ writelen -= datalen;
+ }
+
+ jffs2_free_raw_inode(ri);
+
+ if (writtenlen < end) {
+ /* generic_file_write has written more to the page cache than we've
+ actually written to the medium. Mark the page !Uptodate so that
+ it gets reread */
+ D1(printk(KERN_DEBUG "jffs2_commit_write(): Not all bytes written. Marking page !uptodate\n"));
+ SetPageError(pg);
+ ClearPageUptodate(pg);
+ }
+ if (writtenlen <= start) {
+ /* We didn't even get to the start of the affected part */
+ ret = ret?ret:-ENOSPC;
+ D1(printk(KERN_DEBUG "jffs2_commit_write(): Only %x bytes written to page. start (%x) not reached, returning %d\n", writtenlen, start, ret));
+ }
+ writtenlen = min(end-start, writtenlen-start);
+
+ D1(printk(KERN_DEBUG "jffs2_commit_write() returning %d. nrpages is %ld\n",writtenlen?writtenlen:ret, inode->i_mapping->nrpages));
+ return writtenlen?writtenlen:ret;
+}
diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c
new file mode 100644
index 00000000000000..1312afa6ad8213
--- /dev/null
+++ b/fs/jffs2/gc.c
@@ -0,0 +1,705 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ *
+ * The original JFFS, from which the design for JFFS2 was derived,
+ * was designed and implemented by Axis Communications AB.
+ *
+ * The contents of this file are subject to the Red Hat eCos Public
+ * License Version 1.1 (the "Licence"); you may not use this file
+ * except in compliance with the Licence. You may obtain a copy of
+ * the Licence at http://www.redhat.com/
+ *
+ * Software distributed under the Licence is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing rights and
+ * limitations under the Licence.
+ *
+ * The Original Code is JFFS2 - Journalling Flash File System, version 2
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in
+ * which case the provisions of the GPL are applicable instead of the
+ * above. If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use your
+ * version of this file under the RHEPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice and
+ * other provisions required by the GPL. If you do not delete the
+ * provisions above, a recipient may use your version of this file
+ * under either the RHEPL or the GPL.
+ *
+ * $Id: gc.c,v 1.52.2.7 2003/11/02 13:54:20 dwmw2 Exp $
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/mtd/mtd.h>
+#include <linux/slab.h>
+#include <linux/jffs2.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/pagemap.h>
+#include "nodelist.h"
+#include <linux/crc32.h>
+
+static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+ struct inode *inode, struct jffs2_full_dnode *fd);
+static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+ struct inode *inode, struct jffs2_full_dirent *fd);
+static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+ struct inode *inode, struct jffs2_full_dirent *fd);
+static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+ struct inode *indeo, struct jffs2_full_dnode *fn,
+ __u32 start, __u32 end);
+static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+ struct inode *inode, struct jffs2_full_dnode *fn,
+ __u32 start, __u32 end);
+
+/* Called with erase_completion_lock held */
+static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c)
+{
+ struct jffs2_eraseblock *ret;
+ struct list_head *nextlist = NULL;
+
+ /* Pick an eraseblock to garbage collect next. This is where we'll
+ put the clever wear-levelling algorithms. Eventually. */
+ if (!list_empty(&c->bad_used_list) && c->nr_free_blocks > JFFS2_RESERVED_BLOCKS_GCBAD) {
+ D1(printk(KERN_DEBUG "Picking block from bad_used_list to GC next\n"));
+ nextlist = &c->bad_used_list;
+ } else if (jiffies % 100 && !list_empty(&c->dirty_list)) {
+ /* Most of the time, pick one off the dirty list */
+ D1(printk(KERN_DEBUG "Picking block from dirty_list to GC next\n"));
+ nextlist = &c->dirty_list;
+ } else if (!list_empty(&c->clean_list)) {
+ D1(printk(KERN_DEBUG "Picking block from clean_list to GC next\n"));
+ nextlist = &c->clean_list;
+ } else if (!list_empty(&c->dirty_list)) {
+ D1(printk(KERN_DEBUG "Picking block from dirty_list to GC next (clean_list was empty)\n"));
+
+ nextlist = &c->dirty_list;
+ } else {
+ /* Eep. Both were empty */
+ printk(KERN_NOTICE "jffs2: No clean _or_ dirty blocks to GC from! Where are they all?\n");
+ return NULL;
+ }
+
+ ret = list_entry(nextlist->next, struct jffs2_eraseblock, list);
+ list_del(&ret->list);
+ c->gcblock = ret;
+ ret->gc_node = ret->first_node;
+ if (!ret->gc_node) {
+ printk(KERN_WARNING "Eep. ret->gc_node for block at 0x%08x is NULL\n", ret->offset);
+ BUG();
+ }
+ return ret;
+}
+
+/* jffs2_garbage_collect_pass
+ * Make a single attempt to progress GC. Move one node, and possibly
+ * start erasing one eraseblock.
+ */
+int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
+{
+ struct jffs2_eraseblock *jeb;
+ struct jffs2_inode_info *f;
+ struct jffs2_raw_node_ref *raw;
+ struct jffs2_node_frag *frag;
+ struct jffs2_full_dnode *fn = NULL;
+ struct jffs2_full_dirent *fd;
+ struct jffs2_inode_cache *ic;
+ __u32 start = 0, end = 0, nrfrags = 0;
+ struct inode *inode;
+ int ret = 0;
+
+ if (down_interruptible(&c->alloc_sem))
+ return -EINTR;
+
+ spin_lock_bh(&c->erase_completion_lock);
+
+ /* First, work out which block we're garbage-collecting */
+ jeb = c->gcblock;
+
+ if (!jeb)
+ jeb = jffs2_find_gc_block(c);
+
+ if (!jeb) {
+ printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n");
+ spin_unlock_bh(&c->erase_completion_lock);
+ up(&c->alloc_sem);
+ return -EIO;
+ }
+
+ D1(printk(KERN_DEBUG "garbage collect from block at phys 0x%08x\n", jeb->offset));
+
+ if (!jeb->used_size) {
+ up(&c->alloc_sem);
+ goto eraseit;
+ }
+
+ raw = jeb->gc_node;
+
+ while(raw->flash_offset & 1) {
+ D1(printk(KERN_DEBUG "Node at 0x%08x is obsolete... skipping\n", raw->flash_offset &~3));
+ jeb->gc_node = raw = raw->next_phys;
+ if (!raw) {
+ printk(KERN_WARNING "eep. End of raw list while still supposedly nodes to GC\n");
+ printk(KERN_WARNING "erase block at 0x%08x. free_size 0x%08x, dirty_size 0x%08x, used_size 0x%08x\n",
+ jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size);
+ spin_unlock_bh(&c->erase_completion_lock);
+ up(&c->alloc_sem);
+ BUG();
+ }
+ }
+ D1(printk(KERN_DEBUG "Going to garbage collect node at 0x%08x\n", raw->flash_offset &~3));
+ if (!raw->next_in_ino) {
+ /* Inode-less node. Clean marker, snapshot or something like that */
+ spin_unlock_bh(&c->erase_completion_lock);
+ jffs2_mark_node_obsolete(c, raw);
+ up(&c->alloc_sem);
+ goto eraseit_lock;
+ }
+
+ ic = jffs2_raw_ref_to_ic(raw);
+ D1(printk(KERN_DEBUG "Inode number is #%u\n", ic->ino));
+
+ spin_unlock_bh(&c->erase_completion_lock);
+
+ D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x, ino #%u\n", jeb->offset, raw->flash_offset&~3, ic->ino));
+ if (!ic->nlink) {
+ /* The inode has zero nlink but its nodes weren't yet marked
+ obsolete. This has to be because we're still waiting for
+ the final (close() and) iput() to happen.
+
+ There's a possibility that the final iput() could have
+ happened while we were contemplating. In order to ensure
+ that we don't cause a new read_inode() (which would fail)
+ for the inode in question, we use ilookup() in this case
+ instead of iget().
+
+ The nlink can't _become_ zero at this point because we're
+ holding the alloc_sem, and jffs2_do_unlink() would also
+ need that while decrementing nlink on any inode.
+ */
+ inode = ilookup(OFNI_BS_2SFFJ(c), ic->ino);
+ if (!inode) {
+ D1(printk(KERN_DEBUG "ilookup() failed for ino #%u; inode is probably deleted.\n",
+ ic->ino));
+ up(&c->alloc_sem);
+ return 0;
+ }
+ } else {
+ /* Inode has links to it still; they're not going away because
+ jffs2_do_unlink() would need the alloc_sem and we have it.
+ Just iget() it, and if read_inode() is necessary that's OK.
+ */
+ inode = iget(OFNI_BS_2SFFJ(c), ic->ino);
+ if (!inode) {
+ up(&c->alloc_sem);
+ return -ENOMEM;
+ }
+ }
+ if (is_bad_inode(inode)) {
+ printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u\n", ic->ino);
+ /* NB. This will happen again. We need to do something appropriate here. */
+ up(&c->alloc_sem);
+ iput(inode);
+ return -EIO;
+ }
+
+ f = JFFS2_INODE_INFO(inode);
+ down(&f->sem);
+ /* Now we have the lock for this inode. Check that it's still the one at the head
+ of the list. */
+
+ if (raw->flash_offset & 1) {
+ D1(printk(KERN_DEBUG "node to be GC'd was obsoleted in the meantime.\n"));
+ /* They'll call again */
+ goto upnout;
+ }
+ /* OK. Looks safe. And nobody can get us now because we have the semaphore. Move the block */
+ if (f->metadata && f->metadata->raw == raw) {
+ fn = f->metadata;
+ ret = jffs2_garbage_collect_metadata(c, jeb, inode, fn);
+ goto upnout;
+ }
+
+ for (frag = f->fraglist; frag; frag = frag->next) {
+ if (frag->node && frag->node->raw == raw) {
+ fn = frag->node;
+ end = frag->ofs + frag->size;
+ if (!nrfrags++)
+ start = frag->ofs;
+ if (nrfrags == frag->node->frags)
+ break; /* We've found them all */
+ }
+ }
+ if (fn) {
+ /* We found a datanode. Do the GC */
+ if((start >> PAGE_CACHE_SHIFT) < ((end-1) >> PAGE_CACHE_SHIFT)) {
+ /* It crosses a page boundary. Therefore, it must be a hole. */
+ ret = jffs2_garbage_collect_hole(c, jeb, inode, fn, start, end);
+ } else {
+ /* It could still be a hole. But we GC the page this way anyway */
+ ret = jffs2_garbage_collect_dnode(c, jeb, inode, fn, start, end);
+ }
+ goto upnout;
+ }
+
+ /* Wasn't a dnode. Try dirent */
+ for (fd = f->dents; fd; fd=fd->next) {
+ if (fd->raw == raw)
+ break;
+ }
+
+ if (fd && fd->ino) {
+ ret = jffs2_garbage_collect_dirent(c, jeb, inode, fd);
+ } else if (fd) {
+ ret = jffs2_garbage_collect_deletion_dirent(c, jeb, inode, fd);
+ } else {
+ printk(KERN_WARNING "Raw node at 0x%08x wasn't in node lists for ino #%lu\n", raw->flash_offset&~3, inode->i_ino);
+ if (raw->flash_offset & 1) {
+ printk(KERN_WARNING "But it's obsolete so we don't mind too much\n");
+ } else {
+ ret = -EIO;
+ }
+ }
+ upnout:
+ up(&f->sem);
+ up(&c->alloc_sem);
+ iput(inode);
+
+ eraseit_lock:
+ /* If we've finished this block, start it erasing */
+ spin_lock_bh(&c->erase_completion_lock);
+
+ eraseit:
+ if (c->gcblock && !c->gcblock->used_size) {
+ D1(printk(KERN_DEBUG "Block at 0x%08x completely obsoleted by GC. Moving to erase_pending_list\n", c->gcblock->offset));
+ /* We're GC'ing an empty block? */
+ list_add_tail(&c->gcblock->list, &c->erase_pending_list);
+ c->gcblock = NULL;
+ c->nr_erasing_blocks++;
+ jffs2_erase_pending_trigger(c);
+ }
+ spin_unlock_bh(&c->erase_completion_lock);
+
+ return ret;
+}
+
+static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+ struct inode *inode, struct jffs2_full_dnode *fn)
+{
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ struct jffs2_full_dnode *new_fn;
+ struct jffs2_raw_inode ri;
+ unsigned short dev;
+ char *mdata = NULL, mdatalen = 0;
+ __u32 alloclen, phys_ofs;
+ int ret;
+
+ if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) {
+ /* For these, we don't actually need to read the old node */
+ dev = (MAJOR(to_kdev_t(inode->i_rdev)) << 8) |
+ MINOR(to_kdev_t(inode->i_rdev));
+ mdata = (char *)&dev;
+ mdatalen = sizeof(dev);
+ D1(printk(KERN_DEBUG "jffs2_garbage_collect_metadata(): Writing %d bytes of kdev_t\n", mdatalen));
+ } else if (S_ISLNK(inode->i_mode)) {
+ mdatalen = fn->size;
+ mdata = kmalloc(fn->size, GFP_KERNEL);
+ if (!mdata) {
+ printk(KERN_WARNING "kmalloc of mdata failed in jffs2_garbage_collect_metadata()\n");
+ return -ENOMEM;
+ }
+ ret = jffs2_read_dnode(c, fn, mdata, 0, mdatalen);
+ if (ret) {
+ printk(KERN_WARNING "read of old metadata failed in jffs2_garbage_collect_metadata(): %d\n", ret);
+ kfree(mdata);
+ return ret;
+ }
+ D1(printk(KERN_DEBUG "jffs2_garbage_collect_metadata(): Writing %d bites of symlink target\n", mdatalen));
+
+ }
+
+ ret = jffs2_reserve_space_gc(c, sizeof(ri) + mdatalen, &phys_ofs, &alloclen);
+ if (ret) {
+ printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_metadata failed: %d\n",
+ sizeof(ri)+ mdatalen, ret);
+ goto out;
+ }
+
+ memset(&ri, 0, sizeof(ri));
+ ri.magic = JFFS2_MAGIC_BITMASK;
+ ri.nodetype = JFFS2_NODETYPE_INODE;
+ ri.totlen = sizeof(ri) + mdatalen;
+ ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4);
+
+ ri.ino = inode->i_ino;
+ ri.version = ++f->highest_version;
+ ri.mode = inode->i_mode;
+ ri.uid = inode->i_uid;
+ ri.gid = inode->i_gid;
+ ri.isize = inode->i_size;
+ ri.atime = inode->i_atime;
+ ri.ctime = inode->i_ctime;
+ ri.mtime = inode->i_mtime;
+ ri.offset = 0;
+ ri.csize = mdatalen;
+ ri.dsize = mdatalen;
+ ri.compr = JFFS2_COMPR_NONE;
+ ri.node_crc = crc32(0, &ri, sizeof(ri)-8);
+ ri.data_crc = crc32(0, mdata, mdatalen);
+
+ new_fn = jffs2_write_dnode(inode, &ri, mdata, mdatalen, phys_ofs, NULL);
+
+ if (IS_ERR(new_fn)) {
+ printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn));
+ ret = PTR_ERR(new_fn);
+ goto out;
+ }
+ jffs2_mark_node_obsolete(c, fn->raw);
+ jffs2_free_full_dnode(fn);
+ f->metadata = new_fn;
+ out:
+ if (S_ISLNK(inode->i_mode))
+ kfree(mdata);
+ return ret;
+}
+
+static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+ struct inode *inode, struct jffs2_full_dirent *fd)
+{
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ struct jffs2_full_dirent *new_fd;
+ struct jffs2_raw_dirent rd;
+ __u32 alloclen, phys_ofs;
+ int ret;
+
+ rd.magic = JFFS2_MAGIC_BITMASK;
+ rd.nodetype = JFFS2_NODETYPE_DIRENT;
+ rd.nsize = strlen(fd->name);
+ rd.totlen = sizeof(rd) + rd.nsize;
+ rd.hdr_crc = crc32(0, &rd, sizeof(struct jffs2_unknown_node)-4);
+
+ rd.pino = inode->i_ino;
+ rd.version = ++f->highest_version;
+ rd.ino = fd->ino;
+ rd.mctime = max(inode->i_mtime, inode->i_ctime);
+ rd.type = fd->type;
+ rd.node_crc = crc32(0, &rd, sizeof(rd)-8);
+ rd.name_crc = crc32(0, fd->name, rd.nsize);
+
+ ret = jffs2_reserve_space_gc(c, sizeof(rd)+rd.nsize, &phys_ofs, &alloclen);
+ if (ret) {
+ printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_dirent failed: %d\n",
+ sizeof(rd)+rd.nsize, ret);
+ return ret;
+ }
+ new_fd = jffs2_write_dirent(inode, &rd, fd->name, rd.nsize, phys_ofs, NULL);
+
+ if (IS_ERR(new_fd)) {
+ printk(KERN_WARNING "jffs2_write_dirent in garbage_collect_dirent failed: %ld\n", PTR_ERR(new_fd));
+ return PTR_ERR(new_fd);
+ }
+ jffs2_add_fd_to_list(c, new_fd, &f->dents);
+ return 0;
+}
+
+static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+ struct inode *inode, struct jffs2_full_dirent *fd)
+{
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ struct jffs2_full_dirent **fdp = &f->dents;
+ int found = 0;
+
+ /* FIXME: When we run on NAND flash, we need to work out whether
+ this deletion dirent is still needed to actively delete a
+ 'real' dirent with the same name that's still somewhere else
+ on the flash. For now, we know that we've actually obliterated
+ all the older dirents when they became obsolete, so we didn't
+ really need to write the deletion to flash in the first place.
+ */
+ while (*fdp) {
+ if ((*fdp) == fd) {
+ found = 1;
+ *fdp = fd->next;
+ break;
+ }
+ fdp = &(*fdp)->next;
+ }
+ if (!found) {
+ printk(KERN_WARNING "Deletion dirent \"%s\" not found in list for ino #%lu\n", fd->name, inode->i_ino);
+ }
+ jffs2_mark_node_obsolete(c, fd->raw);
+ jffs2_free_full_dirent(fd);
+ return 0;
+}
+
+static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+ struct inode *inode, struct jffs2_full_dnode *fn,
+ __u32 start, __u32 end)
+{
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ struct jffs2_raw_inode ri;
+ struct jffs2_node_frag *frag;
+ struct jffs2_full_dnode *new_fn;
+ __u32 alloclen, phys_ofs;
+ int ret;
+
+ D1(printk(KERN_DEBUG "Writing replacement hole node for ino #%lu from offset 0x%x to 0x%x\n",
+ inode->i_ino, start, end));
+
+ memset(&ri, 0, sizeof(ri));
+
+ if(fn->frags > 1) {
+ size_t readlen;
+ __u32 crc;
+ /* It's partially obsoleted by a later write. So we have to
+ write it out again with the _same_ version as before */
+ ret = c->mtd->read(c->mtd, fn->raw->flash_offset & ~3, sizeof(ri), &readlen, (char *)&ri);
+ if (readlen != sizeof(ri) || ret) {
+ printk(KERN_WARNING "Node read failed in jffs2_garbage_collect_hole. Ret %d, retlen %d. Data will be lost by writing new hold node\n", ret, readlen);
+ goto fill;
+ }
+ if (ri.nodetype != JFFS2_NODETYPE_INODE) {
+ printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had node type 0x%04x instead of JFFS2_NODETYPE_INODE(0x%04x)\n",
+ fn->raw->flash_offset & ~3, ri.nodetype, JFFS2_NODETYPE_INODE);
+ return -EIO;
+ }
+ if (ri.totlen != sizeof(ri)) {
+ printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had totlen 0x%x instead of expected 0x%x\n",
+ fn->raw->flash_offset & ~3, ri.totlen, sizeof(ri));
+ return -EIO;
+ }
+ crc = crc32(0, &ri, sizeof(ri)-8);
+ if (crc != ri.node_crc) {
+ printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had CRC 0x%08x which doesn't match calculated CRC 0x%08x\n",
+ fn->raw->flash_offset & ~3, ri.node_crc, crc);
+ /* FIXME: We could possibly deal with this by writing new holes for each frag */
+ printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%lu will be lost\n",
+ start, end, inode->i_ino);
+ goto fill;
+ }
+ if (ri.compr != JFFS2_COMPR_ZERO) {
+ printk(KERN_WARNING "jffs2_garbage_collect_hole: Node 0x%08x wasn't a hole node!\n", fn->raw->flash_offset & ~3);
+ printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%lu will be lost\n",
+ start, end, inode->i_ino);
+ goto fill;
+ }
+ } else {
+ fill:
+ ri.magic = JFFS2_MAGIC_BITMASK;
+ ri.nodetype = JFFS2_NODETYPE_INODE;
+ ri.totlen = sizeof(ri);
+ ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4);
+
+ ri.ino = inode->i_ino;
+ ri.version = ++f->highest_version;
+ ri.offset = start;
+ ri.dsize = end - start;
+ ri.csize = 0;
+ ri.compr = JFFS2_COMPR_ZERO;
+ }
+ ri.mode = inode->i_mode;
+ ri.uid = inode->i_uid;
+ ri.gid = inode->i_gid;
+ ri.isize = inode->i_size;
+ ri.atime = inode->i_atime;
+ ri.ctime = inode->i_ctime;
+ ri.mtime = inode->i_mtime;
+ ri.data_crc = 0;
+ ri.node_crc = crc32(0, &ri, sizeof(ri)-8);
+
+ ret = jffs2_reserve_space_gc(c, sizeof(ri), &phys_ofs, &alloclen);
+ if (ret) {
+ printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_hole failed: %d\n",
+ sizeof(ri), ret);
+ return ret;
+ }
+ new_fn = jffs2_write_dnode(inode, &ri, NULL, 0, phys_ofs, NULL);
+
+ if (IS_ERR(new_fn)) {
+ printk(KERN_WARNING "Error writing new hole node: %ld\n", PTR_ERR(new_fn));
+ return PTR_ERR(new_fn);
+ }
+ if (ri.version == f->highest_version) {
+ jffs2_add_full_dnode_to_inode(c, f, new_fn);
+ if (f->metadata) {
+ jffs2_mark_node_obsolete(c, f->metadata->raw);
+ jffs2_free_full_dnode(f->metadata);
+ f->metadata = NULL;
+ }
+ return 0;
+ }
+
+ /*
+ * We should only get here in the case where the node we are
+ * replacing had more than one frag, so we kept the same version
+ * number as before. (Except in case of error -- see 'goto fill;'
+ * above.)
+ */
+ D1(if(fn->frags <= 1) {
+ printk(KERN_WARNING "jffs2_garbage_collect_hole: Replacing fn with %d frag(s) but new ver %d != highest_version %d of ino #%d\n",
+ fn->frags, ri.version, f->highest_version, ri.ino);
+ });
+
+ for (frag = f->fraglist; frag; frag = frag->next) {
+ if (frag->ofs > fn->size + fn->ofs)
+ break;
+ if (frag->node == fn) {
+ frag->node = new_fn;
+ new_fn->frags++;
+ fn->frags--;
+ }
+ }
+ if (fn->frags) {
+ printk(KERN_WARNING "jffs2_garbage_collect_hole: Old node still has frags!\n");
+ BUG();
+ }
+ if (!new_fn->frags) {
+ printk(KERN_WARNING "jffs2_garbage_collect_hole: New node has no frags!\n");
+ BUG();
+ }
+
+ jffs2_mark_node_obsolete(c, fn->raw);
+ jffs2_free_full_dnode(fn);
+
+ return 0;
+}
+
+static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+ struct inode *inode, struct jffs2_full_dnode *fn,
+ __u32 start, __u32 end)
+{
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ struct jffs2_full_dnode *new_fn;
+ struct jffs2_raw_inode ri;
+ __u32 alloclen, phys_ofs, offset, orig_end;
+ int ret = 0;
+ unsigned char *comprbuf = NULL, *writebuf;
+ struct page *pg;
+ unsigned char *pg_ptr;
+
+
+ memset(&ri, 0, sizeof(ri));
+
+ D1(printk(KERN_DEBUG "Writing replacement dnode for ino #%lu from offset 0x%x to 0x%x\n",
+ inode->i_ino, start, end));
+
+ orig_end = end;
+
+
+ /* If we're looking at the last node in the block we're
+ garbage-collecting, we allow ourselves to merge as if the
+ block was already erasing. We're likely to be GC'ing a
+ partial page, and the next block we GC is likely to have
+ the other half of this page right at the beginning, which
+ means we'd expand it _then_, as nr_erasing_blocks would have
+ increased since we checked, and in doing so would obsolete
+ the partial node which we'd have written here. Meaning that
+ the GC would churn and churn, and just leave dirty blocks in
+ it's wake.
+ */
+ if(c->nr_free_blocks + c->nr_erasing_blocks > JFFS2_RESERVED_BLOCKS_GCMERGE - (fn->raw->next_phys?0:1)) {
+ /* Shitloads of space */
+ /* FIXME: Integrate this properly with GC calculations */
+ start &= ~(PAGE_CACHE_SIZE-1);
+ end = min_t(__u32, start + PAGE_CACHE_SIZE, inode->i_size);
+ D1(printk(KERN_DEBUG "Plenty of free space, so expanding to write from offset 0x%x to 0x%x\n",
+ start, end));
+ if (end < orig_end) {
+ printk(KERN_WARNING "Eep. jffs2_garbage_collect_dnode extended node to write, but it got smaller: start 0x%x, orig_end 0x%x, end 0x%x\n", start, orig_end, end);
+ end = orig_end;
+ }
+ }
+
+ /* First, use readpage() to read the appropriate page into the page cache */
+ /* Q: What happens if we actually try to GC the _same_ page for which commit_write()
+ * triggered garbage collection in the first place?
+ * A: I _think_ it's OK. read_cache_page shouldn't deadlock, we'll write out the
+ * page OK. We'll actually write it out again in commit_write, which is a little
+ * suboptimal, but at least we're correct.
+ */
+ pg = read_cache_page(inode->i_mapping, start >> PAGE_CACHE_SHIFT, (void *)jffs2_do_readpage_unlock, inode);
+
+ if (IS_ERR(pg)) {
+ printk(KERN_WARNING "read_cache_page() returned error: %ld\n", PTR_ERR(pg));
+ return PTR_ERR(pg);
+ }
+ pg_ptr = (char *)kmap(pg);
+ comprbuf = kmalloc(end - start, GFP_KERNEL);
+
+ offset = start;
+ while(offset < orig_end) {
+ __u32 datalen;
+ __u32 cdatalen;
+ char comprtype = JFFS2_COMPR_NONE;
+
+ ret = jffs2_reserve_space_gc(c, sizeof(ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen);
+
+ if (ret) {
+ printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_dnode failed: %d\n",
+ sizeof(ri)+ JFFS2_MIN_DATA_LEN, ret);
+ break;
+ }
+ cdatalen = min(alloclen - sizeof(ri), end - offset);
+ datalen = end - offset;
+
+ writebuf = pg_ptr + (offset & (PAGE_CACHE_SIZE -1));
+
+ if (comprbuf) {
+ comprtype = jffs2_compress(writebuf, comprbuf, &datalen, &cdatalen);
+ }
+ if (comprtype) {
+ writebuf = comprbuf;
+ } else {
+ datalen = cdatalen;
+ }
+ ri.magic = JFFS2_MAGIC_BITMASK;
+ ri.nodetype = JFFS2_NODETYPE_INODE;
+ ri.totlen = sizeof(ri) + cdatalen;
+ ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4);
+
+ ri.ino = inode->i_ino;
+ ri.version = ++f->highest_version;
+ ri.mode = inode->i_mode;
+ ri.uid = inode->i_uid;
+ ri.gid = inode->i_gid;
+ ri.isize = inode->i_size;
+ ri.atime = inode->i_atime;
+ ri.ctime = inode->i_ctime;
+ ri.mtime = inode->i_mtime;
+ ri.offset = offset;
+ ri.csize = cdatalen;
+ ri.dsize = datalen;
+ ri.compr = comprtype;
+ ri.node_crc = crc32(0, &ri, sizeof(ri)-8);
+ ri.data_crc = crc32(0, writebuf, cdatalen);
+
+ new_fn = jffs2_write_dnode(inode, &ri, writebuf, cdatalen, phys_ofs, NULL);
+
+ if (IS_ERR(new_fn)) {
+ printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn));
+ ret = PTR_ERR(new_fn);
+ break;
+ }
+ ret = jffs2_add_full_dnode_to_inode(c, f, new_fn);
+ offset += datalen;
+ if (f->metadata) {
+ jffs2_mark_node_obsolete(c, f->metadata->raw);
+ jffs2_free_full_dnode(f->metadata);
+ f->metadata = NULL;
+ }
+ }
+ if (comprbuf) kfree(comprbuf);
+
+ kunmap(pg);
+ /* XXX: Does the page get freed automatically? */
+ /* AAA: Judging by the unmount getting stuck in __wait_on_page, nope. */
+ page_cache_release(pg);
+ return ret;
+}
+
diff --git a/fs/jffs2/histo.h b/fs/jffs2/histo.h
new file mode 100644
index 00000000000000..84f184f0836f2a
--- /dev/null
+++ b/fs/jffs2/histo.h
@@ -0,0 +1,3 @@
+/* This file provides the bit-probabilities for the input file */
+#define BIT_DIVIDER 629
+static int bits[9] = { 179,167,183,165,159,198,178,119,}; /* ia32 .so files */
diff --git a/fs/jffs2/histo_mips.h b/fs/jffs2/histo_mips.h
new file mode 100644
index 00000000000000..9a443268d88576
--- /dev/null
+++ b/fs/jffs2/histo_mips.h
@@ -0,0 +1,2 @@
+#define BIT_DIVIDER_MIPS 1043
+static int bits_mips[8] = { 277,249,290,267,229,341,212,241}; /* mips32 */
diff --git a/fs/jffs2/ioctl.c b/fs/jffs2/ioctl.c
new file mode 100644
index 00000000000000..38a52d3b252a24
--- /dev/null
+++ b/fs/jffs2/ioctl.c
@@ -0,0 +1,47 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ *
+ * The original JFFS, from which the design for JFFS2 was derived,
+ * was designed and implemented by Axis Communications AB.
+ *
+ * The contents of this file are subject to the Red Hat eCos Public
+ * License Version 1.1 (the "Licence"); you may not use this file
+ * except in compliance with the Licence. You may obtain a copy of
+ * the Licence at http://www.redhat.com/
+ *
+ * Software distributed under the Licence is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing rights and
+ * limitations under the Licence.
+ *
+ * The Original Code is JFFS2 - Journalling Flash File System, version 2
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in
+ * which case the provisions of the GPL are applicable instead of the
+ * above. If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use your
+ * version of this file under the RHEPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice and
+ * other provisions required by the GPL. If you do not delete the
+ * provisions above, a recipient may use your version of this file
+ * under either the RHEPL or the GPL.
+ *
+ * $Id: ioctl.c,v 1.5 2001/03/15 15:38:24 dwmw2 Exp $
+ *
+ */
+
+#include <linux/fs.h>
+
+int jffs2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ /* Later, this will provide for lsattr.jffs2 and chattr.jffs2, which
+ will include compression support etc. */
+ return -EINVAL;
+}
+
diff --git a/fs/jffs2/malloc.c b/fs/jffs2/malloc.c
new file mode 100644
index 00000000000000..6ce14c9e5010ff
--- /dev/null
+++ b/fs/jffs2/malloc.c
@@ -0,0 +1,220 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ *
+ * The original JFFS, from which the design for JFFS2 was derived,
+ * was designed and implemented by Axis Communications AB.
+ *
+ * The contents of this file are subject to the Red Hat eCos Public
+ * License Version 1.1 (the "Licence"); you may not use this file
+ * except in compliance with the Licence. You may obtain a copy of
+ * the Licence at http://www.redhat.com/
+ *
+ * Software distributed under the Licence is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing rights and
+ * limitations under the Licence.
+ *
+ * The Original Code is JFFS2 - Journalling Flash File System, version 2
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in
+ * which case the provisions of the GPL are applicable instead of the
+ * above. If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use your
+ * version of this file under the RHEPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice and
+ * other provisions required by the GPL. If you do not delete the
+ * provisions above, a recipient may use your version of this file
+ * under either the RHEPL or the GPL.
+ *
+ * $Id: malloc.c,v 1.16 2001/03/15 15:38:24 dwmw2 Exp $
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/jffs2.h>
+#include "nodelist.h"
+
+#if 0
+#define JFFS2_SLAB_POISON SLAB_POISON
+#else
+#define JFFS2_SLAB_POISON 0
+#endif
+
+/* These are initialised to NULL in the kernel startup code.
+ If you're porting to other operating systems, beware */
+static kmem_cache_t *full_dnode_slab;
+static kmem_cache_t *raw_dirent_slab;
+static kmem_cache_t *raw_inode_slab;
+static kmem_cache_t *tmp_dnode_info_slab;
+static kmem_cache_t *raw_node_ref_slab;
+static kmem_cache_t *node_frag_slab;
+static kmem_cache_t *inode_cache_slab;
+
+void jffs2_free_tmp_dnode_info_list(struct jffs2_tmp_dnode_info *tn)
+{
+ struct jffs2_tmp_dnode_info *next;
+
+ while (tn) {
+ next = tn;
+ tn = tn->next;
+ jffs2_free_full_dnode(next->fn);
+ jffs2_free_tmp_dnode_info(next);
+ }
+}
+
+void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd)
+{
+ struct jffs2_full_dirent *next;
+
+ while (fd) {
+ next = fd->next;
+ jffs2_free_full_dirent(fd);
+ fd = next;
+ }
+}
+
+int __init jffs2_create_slab_caches(void)
+{
+ full_dnode_slab = kmem_cache_create("jffs2_full_dnode", sizeof(struct jffs2_full_dnode), 0, JFFS2_SLAB_POISON, NULL, NULL);
+ if (!full_dnode_slab)
+ goto err;
+
+ raw_dirent_slab = kmem_cache_create("jffs2_raw_dirent", sizeof(struct jffs2_raw_dirent), 0, JFFS2_SLAB_POISON, NULL, NULL);
+ if (!raw_dirent_slab)
+ goto err;
+
+ raw_inode_slab = kmem_cache_create("jffs2_raw_inode", sizeof(struct jffs2_raw_inode), 0, JFFS2_SLAB_POISON, NULL, NULL);
+ if (!raw_inode_slab)
+ goto err;
+
+ tmp_dnode_info_slab = kmem_cache_create("jffs2_tmp_dnode", sizeof(struct jffs2_tmp_dnode_info), 0, JFFS2_SLAB_POISON, NULL, NULL);
+ if (!tmp_dnode_info_slab)
+ goto err;
+
+ raw_node_ref_slab = kmem_cache_create("jffs2_raw_node_ref", sizeof(struct jffs2_raw_node_ref), 0, JFFS2_SLAB_POISON, NULL, NULL);
+ if (!raw_node_ref_slab)
+ goto err;
+
+ node_frag_slab = kmem_cache_create("jffs2_node_frag", sizeof(struct jffs2_node_frag), 0, JFFS2_SLAB_POISON, NULL, NULL);
+ if (!node_frag_slab)
+ goto err;
+
+ inode_cache_slab = kmem_cache_create("jffs2_inode_cache", sizeof(struct jffs2_inode_cache), 0, JFFS2_SLAB_POISON, NULL, NULL);
+
+ if (inode_cache_slab)
+ return 0;
+ err:
+ jffs2_destroy_slab_caches();
+ return -ENOMEM;
+}
+
+void jffs2_destroy_slab_caches(void)
+{
+ if(full_dnode_slab)
+ kmem_cache_destroy(full_dnode_slab);
+ if(raw_dirent_slab)
+ kmem_cache_destroy(raw_dirent_slab);
+ if(raw_inode_slab)
+ kmem_cache_destroy(raw_inode_slab);
+ if(tmp_dnode_info_slab)
+ kmem_cache_destroy(tmp_dnode_info_slab);
+ if(raw_node_ref_slab)
+ kmem_cache_destroy(raw_node_ref_slab);
+ if(node_frag_slab)
+ kmem_cache_destroy(node_frag_slab);
+ if(inode_cache_slab)
+ kmem_cache_destroy(inode_cache_slab);
+
+}
+
+struct jffs2_full_dirent *jffs2_alloc_full_dirent(int namesize)
+{
+ return kmalloc(sizeof(struct jffs2_full_dirent) + namesize, GFP_KERNEL);
+}
+
+void jffs2_free_full_dirent(struct jffs2_full_dirent *x)
+{
+ kfree(x);
+}
+
+struct jffs2_full_dnode *jffs2_alloc_full_dnode(void)
+{
+ void *ret = kmem_cache_alloc(full_dnode_slab, GFP_KERNEL);
+ return ret;
+}
+
+void jffs2_free_full_dnode(struct jffs2_full_dnode *x)
+{
+ kmem_cache_free(full_dnode_slab, x);
+}
+
+struct jffs2_raw_dirent *jffs2_alloc_raw_dirent(void)
+{
+ return kmem_cache_alloc(raw_dirent_slab, GFP_KERNEL);
+}
+
+void jffs2_free_raw_dirent(struct jffs2_raw_dirent *x)
+{
+ kmem_cache_free(raw_dirent_slab, x);
+}
+
+struct jffs2_raw_inode *jffs2_alloc_raw_inode(void)
+{
+ return kmem_cache_alloc(raw_inode_slab, GFP_KERNEL);
+}
+
+void jffs2_free_raw_inode(struct jffs2_raw_inode *x)
+{
+ kmem_cache_free(raw_inode_slab, x);
+}
+
+struct jffs2_tmp_dnode_info *jffs2_alloc_tmp_dnode_info(void)
+{
+ return kmem_cache_alloc(tmp_dnode_info_slab, GFP_KERNEL);
+}
+
+void jffs2_free_tmp_dnode_info(struct jffs2_tmp_dnode_info *x)
+{
+ kmem_cache_free(tmp_dnode_info_slab, x);
+}
+
+struct jffs2_raw_node_ref *jffs2_alloc_raw_node_ref(void)
+{
+ return kmem_cache_alloc(raw_node_ref_slab, GFP_KERNEL);
+}
+
+void jffs2_free_raw_node_ref(struct jffs2_raw_node_ref *x)
+{
+ kmem_cache_free(raw_node_ref_slab, x);
+}
+
+struct jffs2_node_frag *jffs2_alloc_node_frag(void)
+{
+ return kmem_cache_alloc(node_frag_slab, GFP_KERNEL);
+}
+
+void jffs2_free_node_frag(struct jffs2_node_frag *x)
+{
+ kmem_cache_free(node_frag_slab, x);
+}
+
+struct jffs2_inode_cache *jffs2_alloc_inode_cache(void)
+{
+ struct jffs2_inode_cache *ret = kmem_cache_alloc(inode_cache_slab, GFP_KERNEL);
+ D1(printk(KERN_DEBUG "Allocated inocache at %p\n", ret));
+ return ret;
+}
+
+void jffs2_free_inode_cache(struct jffs2_inode_cache *x)
+{
+ D1(printk(KERN_DEBUG "Freeing inocache at %p\n", x));
+ kmem_cache_free(inode_cache_slab, x);
+}
+
diff --git a/fs/jffs2/nodelist.c b/fs/jffs2/nodelist.c
new file mode 100644
index 00000000000000..80e8800cb4ba8b
--- /dev/null
+++ b/fs/jffs2/nodelist.c
@@ -0,0 +1,354 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001, 2002 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ *
+ * The original JFFS, from which the design for JFFS2 was derived,
+ * was designed and implemented by Axis Communications AB.
+ *
+ * The contents of this file are subject to the Red Hat eCos Public
+ * License Version 1.1 (the "Licence"); you may not use this file
+ * except in compliance with the Licence. You may obtain a copy of
+ * the Licence at http://www.redhat.com/
+ *
+ * Software distributed under the Licence is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing rights and
+ * limitations under the Licence.
+ *
+ * The Original Code is JFFS2 - Journalling Flash File System, version 2
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in
+ * which case the provisions of the GPL are applicable instead of the
+ * above. If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use your
+ * version of this file under the RHEPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice and
+ * other provisions required by the GPL. If you do not delete the
+ * provisions above, a recipient may use your version of this file
+ * under either the RHEPL or the GPL.
+ *
+ * $Id: nodelist.c,v 1.30.2.6 2003/02/24 21:49:33 dwmw2 Exp $
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/jffs2.h>
+#include <linux/fs.h>
+#include <linux/mtd/mtd.h>
+#include "nodelist.h"
+
+void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list)
+{
+ struct jffs2_full_dirent **prev = list;
+ D1(printk(KERN_DEBUG "jffs2_add_fd_to_list( %p, %p (->%p))\n", new, list, *list));
+
+ while ((*prev) && (*prev)->nhash <= new->nhash) {
+ if ((*prev)->nhash == new->nhash && !strcmp((*prev)->name, new->name)) {
+ /* Duplicate. Free one */
+ if (new->version < (*prev)->version) {
+ D1(printk(KERN_DEBUG "Eep! Marking new dirent node obsolete\n"));
+ D1(printk(KERN_DEBUG "New dirent is \"%s\"->ino #%u. Old is \"%s\"->ino #%u\n", new->name, new->ino, (*prev)->name, (*prev)->ino));
+ jffs2_mark_node_obsolete(c, new->raw);
+ jffs2_free_full_dirent(new);
+ } else {
+ D1(printk(KERN_DEBUG "Marking old dirent node (ino #%u) obsolete\n", (*prev)->ino));
+ new->next = (*prev)->next;
+ jffs2_mark_node_obsolete(c, ((*prev)->raw));
+ jffs2_free_full_dirent(*prev);
+ *prev = new;
+ }
+ goto out;
+ }
+ prev = &((*prev)->next);
+ }
+ new->next = *prev;
+ *prev = new;
+
+ out:
+ D2(while(*list) {
+ printk(KERN_DEBUG "Dirent \"%s\" (hash 0x%08x, ino #%u\n", (*list)->name, (*list)->nhash, (*list)->ino);
+ list = &(*list)->next;
+ });
+}
+
+/* Put a new tmp_dnode_info into the list, keeping the list in
+ order of increasing version
+*/
+void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list)
+{
+ struct jffs2_tmp_dnode_info **prev = list;
+
+ while ((*prev) && (*prev)->version < tn->version) {
+ prev = &((*prev)->next);
+ }
+ tn->next = (*prev);
+ *prev = tn;
+}
+
+/* Get tmp_dnode_info and full_dirent for all non-obsolete nodes associated
+ with this ino, returning the former in order of version */
+
+int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode_info *f,
+ struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp,
+ __u32 *highest_version, __u32 *latest_mctime,
+ __u32 *mctime_ver)
+{
+ struct jffs2_raw_node_ref *ref = f->inocache->nodes;
+ struct jffs2_tmp_dnode_info *tn, *ret_tn = NULL;
+ struct jffs2_full_dirent *fd, *ret_fd = NULL;
+
+ union jffs2_node_union node;
+ size_t retlen;
+ int err;
+
+ *mctime_ver = 0;
+
+ D1(printk(KERN_DEBUG "jffs2_get_inode_nodes(): ino #%lu\n", ino));
+ if (!f->inocache->nodes) {
+ printk(KERN_WARNING "Eep. no nodes for ino #%lu\n", ino);
+ }
+ for (ref = f->inocache->nodes; ref && ref->next_in_ino; ref = ref->next_in_ino) {
+ /* Work out whether it's a data node or a dirent node */
+ if (ref->flash_offset & 1) {
+ /* FIXME: On NAND flash we may need to read these */
+ D1(printk(KERN_DEBUG "node at 0x%08x is obsoleted. Ignoring.\n", ref->flash_offset &~3));
+ continue;
+ }
+ err = c->mtd->read(c->mtd, (ref->flash_offset & ~3), min(ref->totlen, sizeof(node)), &retlen, (void *)&node);
+ if (err) {
+ printk(KERN_WARNING "error %d reading node at 0x%08x in get_inode_nodes()\n", err, (ref->flash_offset) & ~3);
+ goto free_out;
+ }
+
+
+ /* Check we've managed to read at least the common node header */
+ if (retlen < min(ref->totlen, sizeof(node.u))) {
+ printk(KERN_WARNING "short read in get_inode_nodes()\n");
+ err = -EIO;
+ goto free_out;
+ }
+
+ switch (node.u.nodetype) {
+ case JFFS2_NODETYPE_DIRENT:
+ D1(printk(KERN_DEBUG "Node at %08x is a dirent node\n", ref->flash_offset &~3));
+ if (retlen < sizeof(node.d)) {
+ printk(KERN_WARNING "short read in get_inode_nodes()\n");
+ err = -EIO;
+ goto free_out;
+ }
+ if (node.d.version > *highest_version)
+ *highest_version = node.d.version;
+ if (ref->flash_offset & 1) {
+ /* Obsoleted */
+ continue;
+ }
+ fd = jffs2_alloc_full_dirent(node.d.nsize+1);
+ if (!fd) {
+ err = -ENOMEM;
+ goto free_out;
+ }
+ memset(fd,0,sizeof(struct jffs2_full_dirent) + node.d.nsize+1);
+ fd->raw = ref;
+ fd->version = node.d.version;
+ fd->ino = node.d.ino;
+ fd->type = node.d.type;
+
+ /* Pick out the mctime of the latest dirent */
+ if(fd->version > *mctime_ver) {
+ *mctime_ver = fd->version;
+ *latest_mctime = node.d.mctime;
+ }
+
+ /* memcpy as much of the name as possible from the raw
+ dirent we've already read from the flash
+ */
+ if (retlen > sizeof(struct jffs2_raw_dirent))
+ memcpy(&fd->name[0], &node.d.name[0], min((__u32)node.d.nsize, (retlen-sizeof(struct jffs2_raw_dirent))));
+
+ /* Do we need to copy any more of the name directly
+ from the flash?
+ */
+ if (node.d.nsize + sizeof(struct jffs2_raw_dirent) > retlen) {
+ int already = retlen - sizeof(struct jffs2_raw_dirent);
+
+ err = c->mtd->read(c->mtd, (ref->flash_offset & ~3) + retlen,
+ node.d.nsize - already, &retlen, &fd->name[already]);
+ if (!err && retlen != node.d.nsize - already)
+ err = -EIO;
+
+ if (err) {
+ printk(KERN_WARNING "Read remainder of name in jffs2_get_inode_nodes(): error %d\n", err);
+ jffs2_free_full_dirent(fd);
+ goto free_out;
+ }
+ }
+ fd->nhash = full_name_hash(fd->name, node.d.nsize);
+ fd->next = NULL;
+ /* Wheee. We now have a complete jffs2_full_dirent structure, with
+ the name in it and everything. Link it into the list
+ */
+ D1(printk(KERN_DEBUG "Adding fd \"%s\", ino #%u\n", fd->name, fd->ino));
+ jffs2_add_fd_to_list(c, fd, &ret_fd);
+ break;
+
+ case JFFS2_NODETYPE_INODE:
+ D1(printk(KERN_DEBUG "Node at %08x is a data node\n", ref->flash_offset &~3));
+ if (retlen < sizeof(node.i)) {
+ printk(KERN_WARNING "read too short for dnode\n");
+ err = -EIO;
+ goto free_out;
+ }
+ if (node.i.version > *highest_version)
+ *highest_version = node.i.version;
+ D1(printk(KERN_DEBUG "version %d, highest_version now %d\n", node.i.version, *highest_version));
+
+ if (ref->flash_offset & 1) {
+ D1(printk(KERN_DEBUG "obsoleted\n"));
+ /* Obsoleted */
+ continue;
+ }
+ tn = jffs2_alloc_tmp_dnode_info();
+ if (!tn) {
+ D1(printk(KERN_DEBUG "alloc tn failed\n"));
+ err = -ENOMEM;
+ goto free_out;
+ }
+
+ tn->fn = jffs2_alloc_full_dnode();
+ if (!tn->fn) {
+ D1(printk(KERN_DEBUG "alloc fn failed\n"));
+ err = -ENOMEM;
+ jffs2_free_tmp_dnode_info(tn);
+ goto free_out;
+ }
+ tn->version = node.i.version;
+ tn->fn->ofs = node.i.offset;
+ /* There was a bug where we wrote hole nodes out with
+ csize/dsize swapped. Deal with it */
+ if (node.i.compr == JFFS2_COMPR_ZERO && !node.i.dsize && node.i.csize)
+ tn->fn->size = node.i.csize;
+ else // normal case...
+ tn->fn->size = node.i.dsize;
+ tn->fn->raw = ref;
+ D1(printk(KERN_DEBUG "dnode @%08x: ver %u, offset %04x, dsize %04x\n", ref->flash_offset &~3, node.i.version, node.i.offset, node.i.dsize));
+ jffs2_add_tn_to_list(tn, &ret_tn);
+ break;
+
+ default:
+ switch(node.u.nodetype & JFFS2_COMPAT_MASK) {
+ case JFFS2_FEATURE_INCOMPAT:
+ printk(KERN_NOTICE "Unknown INCOMPAT nodetype %04X at %08X\n", node.u.nodetype, ref->flash_offset & ~3);
+ break;
+ case JFFS2_FEATURE_ROCOMPAT:
+ printk(KERN_NOTICE "Unknown ROCOMPAT nodetype %04X at %08X\n", node.u.nodetype, ref->flash_offset & ~3);
+ break;
+ case JFFS2_FEATURE_RWCOMPAT_COPY:
+ printk(KERN_NOTICE "Unknown RWCOMPAT_COPY nodetype %04X at %08X\n", node.u.nodetype, ref->flash_offset & ~3);
+ break;
+ case JFFS2_FEATURE_RWCOMPAT_DELETE:
+ printk(KERN_NOTICE "Unknown RWCOMPAT_DELETE nodetype %04X at %08X\n", node.u.nodetype, ref->flash_offset & ~3);
+ break;
+ }
+ }
+ }
+ *tnp = ret_tn;
+ *fdp = ret_fd;
+
+ return 0;
+
+ free_out:
+ jffs2_free_tmp_dnode_info_list(ret_tn);
+ jffs2_free_full_dirent_list(ret_fd);
+ return err;
+}
+
+struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, uint32_t ino)
+{
+ struct jffs2_inode_cache *ret;
+
+ D2(printk(KERN_DEBUG "jffs2_get_ino_cache(): ino %u\n", ino));
+ spin_lock (&c->inocache_lock);
+ ret = c->inocache_list[ino % INOCACHE_HASHSIZE];
+ while (ret && ret->ino < ino) {
+ ret = ret->next;
+ }
+
+ spin_unlock(&c->inocache_lock);
+
+ if (ret && ret->ino != ino)
+ ret = NULL;
+
+ D2(printk(KERN_DEBUG "jffs2_get_ino_cache found %p for ino %u\n", ret, ino));
+ return ret;
+}
+
+void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new)
+{
+ struct jffs2_inode_cache **prev;
+ D2(printk(KERN_DEBUG "jffs2_add_ino_cache: Add %p (ino #%u)\n", new, new->ino));
+ spin_lock(&c->inocache_lock);
+
+ prev = &c->inocache_list[new->ino % INOCACHE_HASHSIZE];
+
+ while ((*prev) && (*prev)->ino < new->ino) {
+ prev = &(*prev)->next;
+ }
+ new->next = *prev;
+ *prev = new;
+ spin_unlock(&c->inocache_lock);
+}
+
+void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old)
+{
+ struct jffs2_inode_cache **prev;
+ D2(printk(KERN_DEBUG "jffs2_del_ino_cache: Del %p (ino #%u)\n", old, old->ino));
+ spin_lock(&c->inocache_lock);
+
+ prev = &c->inocache_list[old->ino % INOCACHE_HASHSIZE];
+
+ while ((*prev) && (*prev)->ino < old->ino) {
+ prev = &(*prev)->next;
+ }
+ if ((*prev) == old) {
+ *prev = old->next;
+ }
+ spin_unlock(&c->inocache_lock);
+}
+
+void jffs2_free_ino_caches(struct jffs2_sb_info *c)
+{
+ int i;
+ struct jffs2_inode_cache *this, *next;
+
+ for (i=0; i<INOCACHE_HASHSIZE; i++) {
+ this = c->inocache_list[i];
+ while (this) {
+ next = this->next;
+ D2(printk(KERN_DEBUG "jffs2_free_ino_caches: Freeing ino #%u at %p\n", this->ino, this));
+ jffs2_free_inode_cache(this);
+ this = next;
+ }
+ c->inocache_list[i] = NULL;
+ }
+}
+
+void jffs2_free_raw_node_refs(struct jffs2_sb_info *c)
+{
+ int i;
+ struct jffs2_raw_node_ref *this, *next;
+
+ for (i=0; i<c->nr_blocks; i++) {
+ this = c->blocks[i].first_node;
+ while(this) {
+ next = this->next_phys;
+ jffs2_free_raw_node_ref(this);
+ this = next;
+ }
+ c->blocks[i].first_node = c->blocks[i].last_node = NULL;
+ }
+}
+
diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h
new file mode 100644
index 00000000000000..795e1b48a5e9a3
--- /dev/null
+++ b/fs/jffs2/nodelist.h
@@ -0,0 +1,354 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ *
+ * The original JFFS, from which the design for JFFS2 was derived,
+ * was designed and implemented by Axis Communications AB.
+ *
+ * The contents of this file are subject to the Red Hat eCos Public
+ * License Version 1.1 (the "Licence"); you may not use this file
+ * except in compliance with the Licence. You may obtain a copy of
+ * the Licence at http://www.redhat.com/
+ *
+ * Software distributed under the Licence is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing rights and
+ * limitations under the Licence.
+ *
+ * The Original Code is JFFS2 - Journalling Flash File System, version 2
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in
+ * which case the provisions of the GPL are applicable instead of the
+ * above. If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use your
+ * version of this file under the RHEPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice and
+ * other provisions required by the GPL. If you do not delete the
+ * provisions above, a recipient may use your version of this file
+ * under either the RHEPL or the GPL.
+ *
+ * $Id: nodelist.h,v 1.46.2.5 2003/11/02 13:54:20 dwmw2 Exp $
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/fs.h>
+
+#include <linux/jffs2_fs_sb.h>
+#include <linux/jffs2_fs_i.h>
+
+#ifndef CONFIG_JFFS2_FS_DEBUG
+#define CONFIG_JFFS2_FS_DEBUG 2
+#endif
+
+#if CONFIG_JFFS2_FS_DEBUG > 0
+#define D1(x) x
+#else
+#define D1(x)
+#endif
+
+#if CONFIG_JFFS2_FS_DEBUG > 1
+#define D2(x) x
+#else
+#define D2(x)
+#endif
+
+/*
+ This is all we need to keep in-core for each raw node during normal
+ operation. As and when we do read_inode on a particular inode, we can
+ scan the nodes which are listed for it and build up a proper map of
+ which nodes are currently valid. JFFSv1 always used to keep that whole
+ map in core for each inode.
+*/
+struct jffs2_raw_node_ref
+{
+ struct jffs2_raw_node_ref *next_in_ino; /* Points to the next raw_node_ref
+ for this inode. If this is the last, it points to the inode_cache
+ for this inode instead. The inode_cache will have NULL in the first
+ word so you know when you've got there :) */
+ struct jffs2_raw_node_ref *next_phys;
+ // __u32 ino;
+ __u32 flash_offset;
+ __u32 totlen;
+// __u16 nodetype;
+
+ /* flash_offset & 3 always has to be zero, because nodes are
+ always aligned at 4 bytes. So we have a couple of extra bits
+ to play with. So we set the least significant bit to 1 to
+ signify that the node is obsoleted by later nodes.
+ */
+};
+
+/*
+ Used for keeping track of deletion nodes &c, which can only be marked
+ as obsolete when the node which they mark as deleted has actually been
+ removed from the flash.
+*/
+struct jffs2_raw_node_ref_list {
+ struct jffs2_raw_node_ref *rew;
+ struct jffs2_raw_node_ref_list *next;
+};
+
+/* For each inode in the filesystem, we need to keep a record of
+ nlink, because it would be a PITA to scan the whole directory tree
+ at read_inode() time to calculate it, and to keep sufficient information
+ in the raw_node_ref (basically both parent and child inode number for
+ dirent nodes) would take more space than this does. We also keep
+ a pointer to the first physical node which is part of this inode, too.
+*/
+struct jffs2_inode_cache {
+ struct jffs2_scan_info *scan; /* Used during scan to hold
+ temporary lists of nodes, and later must be set to
+ NULL to mark the end of the raw_node_ref->next_in_ino
+ chain. */
+ struct jffs2_inode_cache *next;
+ struct jffs2_raw_node_ref *nodes;
+ __u32 ino;
+ int nlink;
+};
+
+struct jffs2_scan_info {
+ struct jffs2_full_dirent *dents;
+ struct jffs2_tmp_dnode_info *tmpnodes;
+};
+/*
+ Larger representation of a raw node, kept in-core only when the
+ struct inode for this particular ino is instantiated.
+*/
+
+struct jffs2_full_dnode
+{
+ struct jffs2_raw_node_ref *raw;
+ __u32 ofs; /* Don't really need this, but optimisation */
+ __u32 size;
+ __u32 frags; /* Number of fragments which currently refer
+ to this node. When this reaches zero,
+ the node is obsolete.
+ */
+};
+
+/*
+ Even larger representation of a raw node, kept in-core only while
+ we're actually building up the original map of which nodes go where,
+ in read_inode()
+*/
+struct jffs2_tmp_dnode_info
+{
+ struct jffs2_tmp_dnode_info *next;
+ struct jffs2_full_dnode *fn;
+ __u32 version;
+};
+
+struct jffs2_full_dirent
+{
+ struct jffs2_raw_node_ref *raw;
+ struct jffs2_full_dirent *next;
+ __u32 version;
+ __u32 ino; /* == zero for unlink */
+ unsigned int nhash;
+ unsigned char type;
+ unsigned char name[0];
+};
+/*
+ Fragments - used to build a map of which raw node to obtain
+ data from for each part of the ino
+*/
+struct jffs2_node_frag
+{
+ struct jffs2_node_frag *next;
+ struct jffs2_full_dnode *node; /* NULL for holes */
+ __u32 size;
+ __u32 ofs; /* Don't really need this, but optimisation */
+};
+
+struct jffs2_eraseblock
+{
+ struct list_head list;
+ int bad_count;
+ __u32 offset; /* of this block in the MTD */
+
+ __u32 used_size;
+ __u32 dirty_size;
+ __u32 free_size; /* Note that sector_size - free_size
+ is the address of the first free space */
+ struct jffs2_raw_node_ref *first_node;
+ struct jffs2_raw_node_ref *last_node;
+
+ struct jffs2_raw_node_ref *gc_node; /* Next node to be garbage collected */
+
+ /* For deletia. When a dirent node in this eraseblock is
+ deleted by a node elsewhere, that other node can only
+ be marked as obsolete when this block is actually erased.
+ So we keep a list of the nodes to mark as obsolete when
+ the erase is completed.
+ */
+ // MAYBE struct jffs2_raw_node_ref_list *deletia;
+};
+
+#define ACCT_SANITY_CHECK(c, jeb) do { \
+ if (jeb->used_size + jeb->dirty_size + jeb->free_size != c->sector_size) { \
+ printk(KERN_NOTICE "Eeep. Space accounting for block at 0x%08x is screwed\n", jeb->offset); \
+ printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x != total %08x\n", \
+ jeb->free_size, jeb->dirty_size, jeb->used_size, c->sector_size); \
+ BUG(); \
+ } \
+ if (c->used_size + c->dirty_size + c->free_size + c->erasing_size + c->bad_size != c->flash_size) { \
+ printk(KERN_NOTICE "Eeep. Space accounting superblock info is screwed\n"); \
+ printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x + erasing %08x + bad %08x != total %08x\n", \
+ c->free_size, c->dirty_size, c->used_size, c->erasing_size, c->bad_size, c->flash_size); \
+ BUG(); \
+ } \
+} while(0)
+
+#define ACCT_PARANOIA_CHECK(jeb) do { \
+ __u32 my_used_size = 0; \
+ struct jffs2_raw_node_ref *ref2 = jeb->first_node; \
+ while (ref2) { \
+ if (!(ref2->flash_offset & 1)) \
+ my_used_size += ref2->totlen; \
+ ref2 = ref2->next_phys; \
+ } \
+ if (my_used_size != jeb->used_size) { \
+ printk(KERN_NOTICE "Calculated used size %08x != stored used size %08x\n", my_used_size, jeb->used_size); \
+ BUG(); \
+ } \
+ } while(0)
+
+#define ALLOC_NORMAL 0 /* Normal allocation */
+#define ALLOC_DELETION 1 /* Deletion node. Best to allow it */
+#define ALLOC_GC 2 /* Space requested for GC. Give it or die */
+
+#define JFFS2_RESERVED_BLOCKS_BASE 3 /* Number of free blocks there must be before we... */
+#define JFFS2_RESERVED_BLOCKS_WRITE (JFFS2_RESERVED_BLOCKS_BASE + 2) /* ... allow a normal filesystem write */
+#define JFFS2_RESERVED_BLOCKS_DELETION (JFFS2_RESERVED_BLOCKS_BASE + 1) /* ... allow a normal filesystem deletion */
+#define JFFS2_RESERVED_BLOCKS_GCTRIGGER (JFFS2_RESERVED_BLOCKS_BASE + 3) /* ... wake up the GC thread */
+#define JFFS2_RESERVED_BLOCKS_GCBAD (JFFS2_RESERVED_BLOCKS_BASE + 1) /* ... pick a block from the bad_list to GC */
+#define JFFS2_RESERVED_BLOCKS_GCMERGE (JFFS2_RESERVED_BLOCKS_BASE) /* ... merge pages when garbage collecting */
+
+
+#define PAD(x) (((x)+3)&~3)
+
+static inline struct jffs2_inode_cache *jffs2_raw_ref_to_ic(struct jffs2_raw_node_ref *raw)
+{
+ while(raw->next_in_ino) {
+ raw = raw->next_in_ino;
+ }
+
+ return ((struct jffs2_inode_cache *)raw);
+}
+
+/* nodelist.c */
+D1(void jffs2_print_frag_list(struct jffs2_inode_info *f));
+void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list);
+void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list);
+int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode_info *f,
+ struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp,
+ __u32 *highest_version, __u32 *latest_mctime,
+ __u32 *mctime_ver);
+struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, uint32_t ino);
+void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new);
+void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old);
+void jffs2_free_ino_caches(struct jffs2_sb_info *c);
+void jffs2_free_raw_node_refs(struct jffs2_sb_info *c);
+
+/* nodemgmt.c */
+int jffs2_reserve_space(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len, int prio);
+int jffs2_reserve_space_gc(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len);
+int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new, __u32 len, int dirty);
+void jffs2_complete_reservation(struct jffs2_sb_info *c);
+void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *raw);
+
+/* write.c */
+struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri);
+struct jffs2_full_dnode *jffs2_write_dnode(struct inode *inode, struct jffs2_raw_inode *ri, const unsigned char *data, __u32 datalen, __u32 flash_ofs, __u32 *writelen);
+struct jffs2_full_dirent *jffs2_write_dirent(struct inode *inode, struct jffs2_raw_dirent *rd, const unsigned char *name, __u32 namelen, __u32 flash_ofs, __u32 *writelen);
+
+/* readinode.c */
+void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct jffs2_node_frag **list, __u32 size);
+int jffs2_add_full_dnode_to_fraglist(struct jffs2_sb_info *c, struct jffs2_node_frag **list, struct jffs2_full_dnode *fn);
+int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn);
+void jffs2_read_inode (struct inode *);
+void jffs2_clear_inode (struct inode *);
+
+/* malloc.c */
+void jffs2_free_tmp_dnode_info_list(struct jffs2_tmp_dnode_info *tn);
+void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd);
+
+int jffs2_create_slab_caches(void);
+void jffs2_destroy_slab_caches(void);
+
+struct jffs2_full_dirent *jffs2_alloc_full_dirent(int namesize);
+void jffs2_free_full_dirent(struct jffs2_full_dirent *);
+struct jffs2_full_dnode *jffs2_alloc_full_dnode(void);
+void jffs2_free_full_dnode(struct jffs2_full_dnode *);
+struct jffs2_raw_dirent *jffs2_alloc_raw_dirent(void);
+void jffs2_free_raw_dirent(struct jffs2_raw_dirent *);
+struct jffs2_raw_inode *jffs2_alloc_raw_inode(void);
+void jffs2_free_raw_inode(struct jffs2_raw_inode *);
+struct jffs2_tmp_dnode_info *jffs2_alloc_tmp_dnode_info(void);
+void jffs2_free_tmp_dnode_info(struct jffs2_tmp_dnode_info *);
+struct jffs2_raw_node_ref *jffs2_alloc_raw_node_ref(void);
+void jffs2_free_raw_node_ref(struct jffs2_raw_node_ref *);
+struct jffs2_node_frag *jffs2_alloc_node_frag(void);
+void jffs2_free_node_frag(struct jffs2_node_frag *);
+struct jffs2_inode_cache *jffs2_alloc_inode_cache(void);
+void jffs2_free_inode_cache(struct jffs2_inode_cache *);
+
+/* gc.c */
+int jffs2_garbage_collect_pass(struct jffs2_sb_info *c);
+
+/* background.c */
+int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c);
+void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c);
+void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c);
+
+/* dir.c */
+extern struct file_operations jffs2_dir_operations;
+extern struct inode_operations jffs2_dir_inode_operations;
+
+/* file.c */
+extern struct file_operations jffs2_file_operations;
+extern struct inode_operations jffs2_file_inode_operations;
+extern struct address_space_operations jffs2_file_address_operations;
+int jffs2_null_fsync(struct file *, struct dentry *, int);
+int jffs2_setattr (struct dentry *dentry, struct iattr *iattr);
+int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg);
+int jffs2_do_readpage_unlock (struct inode *inode, struct page *pg);
+int jffs2_readpage (struct file *, struct page *);
+int jffs2_prepare_write (struct file *, struct page *, unsigned, unsigned);
+int jffs2_commit_write (struct file *, struct page *, unsigned, unsigned);
+
+/* ioctl.c */
+int jffs2_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
+
+/* read.c */
+int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len);
+
+/* compr.c */
+unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out,
+ __u32 *datalen, __u32 *cdatalen);
+int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in,
+ unsigned char *data_out, __u32 cdatalen, __u32 datalen);
+
+/* scan.c */
+int jffs2_scan_medium(struct jffs2_sb_info *c);
+
+/* build.c */
+int jffs2_build_filesystem(struct jffs2_sb_info *c);
+
+/* symlink.c */
+extern struct inode_operations jffs2_symlink_inode_operations;
+
+/* erase.c */
+void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
+void jffs2_erase_pending_blocks(struct jffs2_sb_info *c);
+void jffs2_mark_erased_blocks(struct jffs2_sb_info *c);
+void jffs2_erase_pending_trigger(struct jffs2_sb_info *c);
+
+/* compr_zlib.c */
+int jffs2_zlib_init(void);
+void jffs2_zlib_exit(void);
diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c
new file mode 100644
index 00000000000000..066decf852ffe7
--- /dev/null
+++ b/fs/jffs2/nodemgmt.c
@@ -0,0 +1,392 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ *
+ * The original JFFS, from which the design for JFFS2 was derived,
+ * was designed and implemented by Axis Communications AB.
+ *
+ * The contents of this file are subject to the Red Hat eCos Public
+ * License Version 1.1 (the "Licence"); you may not use this file
+ * except in compliance with the Licence. You may obtain a copy of
+ * the Licence at http://www.redhat.com/
+ *
+ * Software distributed under the Licence is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing rights and
+ * limitations under the Licence.
+ *
+ * The Original Code is JFFS2 - Journalling Flash File System, version 2
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in
+ * which case the provisions of the GPL are applicable instead of the
+ * above. If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use your
+ * version of this file under the RHEPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice and
+ * other provisions required by the GPL. If you do not delete the
+ * provisions above, a recipient may use your version of this file
+ * under either the RHEPL or the GPL.
+ *
+ * $Id: nodemgmt.c,v 1.45.2.1 2002/02/23 14:13:34 dwmw2 Exp $
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/jffs2.h>
+#include <linux/mtd/mtd.h>
+#include <linux/interrupt.h>
+#include "nodelist.h"
+
+/**
+ * jffs2_reserve_space - request physical space to write nodes to flash
+ * @c: superblock info
+ * @minsize: Minimum acceptable size of allocation
+ * @ofs: Returned value of node offset
+ * @len: Returned value of allocation length
+ * @prio: Allocation type - ALLOC_{NORMAL,DELETION}
+ *
+ * Requests a block of physical space on the flash. Returns zero for success
+ * and puts 'ofs' and 'len' into the appriopriate place, or returns -ENOSPC
+ * or other error if appropriate.
+ *
+ * If it returns zero, jffs2_reserve_space() also downs the per-filesystem
+ * allocation semaphore, to prevent more than one allocation from being
+ * active at any time. The semaphore is later released by jffs2_commit_allocation()
+ *
+ * jffs2_reserve_space() may trigger garbage collection in order to make room
+ * for the requested allocation.
+ */
+
+static int jffs2_do_reserve_space(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len);
+
+int jffs2_reserve_space(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len, int prio)
+{
+ int ret = -EAGAIN;
+ int blocksneeded = JFFS2_RESERVED_BLOCKS_WRITE;
+ /* align it */
+ minsize = PAD(minsize);
+
+ if (prio == ALLOC_DELETION)
+ blocksneeded = JFFS2_RESERVED_BLOCKS_DELETION;
+
+ D1(printk(KERN_DEBUG "jffs2_reserve_space(): Requested 0x%x bytes\n", minsize));
+ down(&c->alloc_sem);
+
+ D1(printk(KERN_DEBUG "jffs2_reserve_space(): alloc sem got\n"));
+
+ spin_lock_bh(&c->erase_completion_lock);
+
+ /* this needs a little more thought */
+ while(ret == -EAGAIN) {
+ while(c->nr_free_blocks + c->nr_erasing_blocks < blocksneeded) {
+ int ret;
+
+ up(&c->alloc_sem);
+ if (c->dirty_size < c->sector_size) {
+ D1(printk(KERN_DEBUG "Short on space, but total dirty size 0x%08x < sector size 0x%08x, so -ENOSPC\n", c->dirty_size, c->sector_size));
+ spin_unlock_bh(&c->erase_completion_lock);
+ return -ENOSPC;
+ }
+ D1(printk(KERN_DEBUG "Triggering GC pass. nr_free_blocks %d, nr_erasing_blocks %d, free_size 0x%08x, dirty_size 0x%08x, used_size 0x%08x, erasing_size 0x%08x, bad_size 0x%08x (total 0x%08x of 0x%08x)\n",
+ c->nr_free_blocks, c->nr_erasing_blocks, c->free_size, c->dirty_size, c->used_size, c->erasing_size, c->bad_size,
+ c->free_size + c->dirty_size + c->used_size + c->erasing_size + c->bad_size, c->flash_size));
+ spin_unlock_bh(&c->erase_completion_lock);
+
+ ret = jffs2_garbage_collect_pass(c);
+ if (ret)
+ return ret;
+
+ if (current->need_resched)
+ schedule();
+
+ if (signal_pending(current))
+ return -EINTR;
+
+ down(&c->alloc_sem);
+ spin_lock_bh(&c->erase_completion_lock);
+ }
+
+ ret = jffs2_do_reserve_space(c, minsize, ofs, len);
+ if (ret) {
+ D1(printk(KERN_DEBUG "jffs2_reserve_space: ret is %d\n", ret));
+ }
+ }
+ spin_unlock_bh(&c->erase_completion_lock);
+ if (ret)
+ up(&c->alloc_sem);
+ return ret;
+}
+
+int jffs2_reserve_space_gc(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len)
+{
+ int ret = -EAGAIN;
+ minsize = PAD(minsize);
+
+ D1(printk(KERN_DEBUG "jffs2_reserve_space_gc(): Requested 0x%x bytes\n", minsize));
+
+ spin_lock_bh(&c->erase_completion_lock);
+ while(ret == -EAGAIN) {
+ ret = jffs2_do_reserve_space(c, minsize, ofs, len);
+ if (ret) {
+ D1(printk(KERN_DEBUG "jffs2_reserve_space_gc: looping, ret is %d\n", ret));
+ }
+ }
+ spin_unlock_bh(&c->erase_completion_lock);
+ return ret;
+}
+
+/* Called with alloc sem _and_ erase_completion_lock */
+static int jffs2_do_reserve_space(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len)
+{
+ struct jffs2_eraseblock *jeb = c->nextblock;
+
+ restart:
+ if (jeb && minsize > jeb->free_size) {
+ /* Skip the end of this block and file it as having some dirty space */
+ c->dirty_size += jeb->free_size;
+ c->free_size -= jeb->free_size;
+ jeb->dirty_size += jeb->free_size;
+ jeb->free_size = 0;
+ D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
+ jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
+ list_add_tail(&jeb->list, &c->dirty_list);
+ c->nextblock = jeb = NULL;
+ }
+
+ if (!jeb) {
+ struct list_head *next;
+ /* Take the next block off the 'free' list */
+
+ if (list_empty(&c->free_list)) {
+
+ DECLARE_WAITQUEUE(wait, current);
+
+ if (!c->nr_erasing_blocks) {
+// if (list_empty(&c->erasing_list) && list_empty(&c->erase_pending_list) && list_empty(c->erase_complete_list)) {
+ /* Ouch. We're in GC, or we wouldn't have got here.
+ And there's no space left. At all. */
+ printk(KERN_CRIT "Argh. No free space left for GC. nr_erasing_blocks is %d. nr_free_blocks is %d. (erasingempty: %s, erasependingempty: %s)\n",
+ c->nr_erasing_blocks, c->nr_free_blocks, list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no");
+ return -ENOSPC;
+ }
+ /* Make sure this can't deadlock. Someone has to start the erases
+ of erase_pending blocks */
+ set_current_state(TASK_INTERRUPTIBLE);
+ add_wait_queue(&c->erase_wait, &wait);
+ D1(printk(KERN_DEBUG "Waiting for erases to complete. erasing_blocks is %d. (erasingempty: %s, erasependingempty: %s)\n",
+ c->nr_erasing_blocks, list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no"));
+ if (!list_empty(&c->erase_pending_list)) {
+ D1(printk(KERN_DEBUG "Triggering pending erases\n"));
+ jffs2_erase_pending_trigger(c);
+ }
+ spin_unlock_bh(&c->erase_completion_lock);
+ schedule();
+ remove_wait_queue(&c->erase_wait, &wait);
+ spin_lock_bh(&c->erase_completion_lock);
+ if (signal_pending(current)) {
+ return -EINTR;
+ }
+ /* An erase may have failed, decreasing the
+ amount of free space available. So we must
+ restart from the beginning */
+ return -EAGAIN;
+ }
+
+ next = c->free_list.next;
+ list_del(next);
+ c->nextblock = jeb = list_entry(next, struct jffs2_eraseblock, list);
+ c->nr_free_blocks--;
+ if (jeb->free_size != c->sector_size - sizeof(struct jffs2_unknown_node)) {
+ printk(KERN_WARNING "Eep. Block 0x%08x taken from free_list had free_size of 0x%08x!!\n", jeb->offset, jeb->free_size);
+ goto restart;
+ }
+ }
+ /* OK, jeb (==c->nextblock) is now pointing at a block which definitely has
+ enough space */
+ *ofs = jeb->offset + (c->sector_size - jeb->free_size);
+ *len = jeb->free_size;
+ D1(printk(KERN_DEBUG "jffs2_do_reserve_space(): Giving 0x%x bytes at 0x%x\n", *len, *ofs));
+ return 0;
+}
+
+/**
+ * jffs2_add_physical_node_ref - add a physical node reference to the list
+ * @c: superblock info
+ * @ofs: physical location of this physical node
+ * @len: length of this physical node
+ * @ino: inode number with which this physical node is associated
+ *
+ * Should only be used to report nodes for which space has been allocated
+ * by jffs2_reserve_space.
+ *
+ * Must be called with the alloc_sem held.
+ */
+
+int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new, __u32 len, int dirty)
+{
+ struct jffs2_eraseblock *jeb;
+
+ len = PAD(len);
+ jeb = &c->blocks[(new->flash_offset & ~3) / c->sector_size];
+ D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x, size 0x%x\n", new->flash_offset & ~3, len));
+#if 1
+ if (jeb != c->nextblock || (new->flash_offset & ~3) != jeb->offset + (c->sector_size - jeb->free_size)) {
+ printk(KERN_WARNING "argh. node added in wrong place\n");
+ jffs2_free_raw_node_ref(new);
+ return -EINVAL;
+ }
+#endif
+ if (!jeb->first_node)
+ jeb->first_node = new;
+ if (jeb->last_node)
+ jeb->last_node->next_phys = new;
+ jeb->last_node = new;
+
+ spin_lock_bh(&c->erase_completion_lock);
+ jeb->free_size -= len;
+ c->free_size -= len;
+ if (dirty) {
+ new->flash_offset |= 1;
+ jeb->dirty_size += len;
+ c->dirty_size += len;
+ } else {
+ jeb->used_size += len;
+ c->used_size += len;
+ }
+ spin_unlock_bh(&c->erase_completion_lock);
+ if (!jeb->free_size && !jeb->dirty_size) {
+ /* If it lives on the dirty_list, jffs2_reserve_space will put it there */
+ D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
+ jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
+ list_add_tail(&jeb->list, &c->clean_list);
+ c->nextblock = NULL;
+ }
+ ACCT_SANITY_CHECK(c,jeb);
+ ACCT_PARANOIA_CHECK(jeb);
+
+ return 0;
+}
+
+
+void jffs2_complete_reservation(struct jffs2_sb_info *c)
+{
+ D1(printk(KERN_DEBUG "jffs2_complete_reservation()\n"));
+ jffs2_garbage_collect_trigger(c);
+ up(&c->alloc_sem);
+}
+
+void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref)
+{
+ struct jffs2_eraseblock *jeb;
+ int blocknr;
+ struct jffs2_unknown_node n;
+ int ret;
+ ssize_t retlen;
+
+ if(!ref) {
+ printk(KERN_NOTICE "EEEEEK. jffs2_mark_node_obsolete called with NULL node\n");
+ return;
+ }
+ if (ref->flash_offset & 1) {
+ D1(printk(KERN_DEBUG "jffs2_mark_node_obsolete called with already obsolete node at 0x%08x\n", ref->flash_offset &~3));
+ return;
+ }
+ blocknr = ref->flash_offset / c->sector_size;
+ if (blocknr >= c->nr_blocks) {
+ printk(KERN_NOTICE "raw node at 0x%08x is off the end of device!\n", ref->flash_offset);
+ BUG();
+ }
+ jeb = &c->blocks[blocknr];
+ if (jeb->used_size < ref->totlen) {
+ printk(KERN_NOTICE "raw node of size 0x%08x freed from erase block %d at 0x%08x, but used_size was already 0x%08x\n",
+ ref->totlen, blocknr, ref->flash_offset, jeb->used_size);
+ BUG();
+ }
+
+ spin_lock_bh(&c->erase_completion_lock);
+ jeb->used_size -= ref->totlen;
+ jeb->dirty_size += ref->totlen;
+ c->used_size -= ref->totlen;
+ c->dirty_size += ref->totlen;
+ ref->flash_offset |= 1;
+
+ ACCT_SANITY_CHECK(c, jeb);
+
+ ACCT_PARANOIA_CHECK(jeb);
+
+ if (c->flags & JFFS2_SB_FLAG_MOUNTING) {
+ /* Mount in progress. Don't muck about with the block
+ lists because they're not ready yet, and don't actually
+ obliterate nodes that look obsolete. If they weren't
+ marked obsolete on the flash at the time they _became_
+ obsolete, there was probably a reason for that. */
+ spin_unlock_bh(&c->erase_completion_lock);
+ return;
+ }
+ if (jeb == c->nextblock) {
+ D2(printk(KERN_DEBUG "Not moving nextblock 0x%08x to dirty/erase_pending list\n", jeb->offset));
+ } else if (jeb == c->gcblock) {
+ D2(printk(KERN_DEBUG "Not moving gcblock 0x%08x to dirty/erase_pending list\n", jeb->offset));
+#if 0 /* We no longer do this here. It can screw the wear levelling. If you have a lot of static
+ data and a few blocks free, and you just create new files and keep deleting/overwriting
+ them, then you'd keep erasing and reusing those blocks without ever moving stuff around.
+ So we leave completely obsoleted blocks on the dirty_list and let the GC delete them
+ when it finds them there. That way, we still get the 'once in a while, take a clean block'
+ to spread out the flash usage */
+ } else if (!jeb->used_size) {
+ D1(printk(KERN_DEBUG "Eraseblock at 0x%08x completely dirtied. Removing from (dirty?) list...\n", jeb->offset));
+ list_del(&jeb->list);
+ D1(printk(KERN_DEBUG "...and adding to erase_pending_list\n"));
+ list_add_tail(&jeb->list, &c->erase_pending_list);
+ c->nr_erasing_blocks++;
+ jffs2_erase_pending_trigger(c);
+ // OFNI_BS_2SFFJ(c)->s_dirt = 1;
+ D1(printk(KERN_DEBUG "Done OK\n"));
+#endif
+ } else if (jeb->dirty_size == ref->totlen) {
+ D1(printk(KERN_DEBUG "Eraseblock at 0x%08x is freshly dirtied. Removing from clean list...\n", jeb->offset));
+ list_del(&jeb->list);
+ D1(printk(KERN_DEBUG "...and adding to dirty_list\n"));
+ list_add_tail(&jeb->list, &c->dirty_list);
+ }
+ spin_unlock_bh(&c->erase_completion_lock);
+
+ if (c->mtd->type != MTD_NORFLASH && c->mtd->type != MTD_RAM)
+ return;
+ if (OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY)
+ return;
+
+ D1(printk(KERN_DEBUG "obliterating obsoleted node at 0x%08x\n", ref->flash_offset &~3));
+ ret = c->mtd->read(c->mtd, ref->flash_offset &~3, sizeof(n), &retlen, (char *)&n);
+ if (ret) {
+ printk(KERN_WARNING "Read error reading from obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, ret);
+ return;
+ }
+ if (retlen != sizeof(n)) {
+ printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, retlen);
+ return;
+ }
+ if (PAD(n.totlen) != PAD(ref->totlen)) {
+ printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen in node ref (0x%08x)\n", n.totlen, ref->totlen);
+ return;
+ }
+ if (!(n.nodetype & JFFS2_NODE_ACCURATE)) {
+ D1(printk(KERN_DEBUG "Node at 0x%08x was already marked obsolete (nodetype 0x%04x\n", ref->flash_offset &~3, n.nodetype));
+ return;
+ }
+ n.nodetype &= ~JFFS2_NODE_ACCURATE;
+ ret = c->mtd->write(c->mtd, ref->flash_offset&~3, sizeof(n), &retlen, (char *)&n);
+ if (ret) {
+ printk(KERN_WARNING "Write error in obliterating obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, ret);
+ return;
+ }
+ if (retlen != sizeof(n)) {
+ printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, retlen);
+ return;
+ }
+}
diff --git a/fs/jffs2/pushpull.c b/fs/jffs2/pushpull.c
new file mode 100644
index 00000000000000..934d4df7291704
--- /dev/null
+++ b/fs/jffs2/pushpull.c
@@ -0,0 +1,71 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ *
+ * The original JFFS, from which the design for JFFS2 was derived,
+ * was designed and implemented by Axis Communications AB.
+ *
+ * The contents of this file are subject to the Red Hat eCos Public
+ * License Version 1.1 (the "Licence"); you may not use this file
+ * except in compliance with the Licence. You may obtain a copy of
+ * the Licence at http://www.redhat.com/
+ *
+ * Software distributed under the Licence is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing rights and
+ * limitations under the Licence.
+ *
+ * The Original Code is JFFS2 - Journalling Flash File System, version 2
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in
+ * which case the provisions of the GPL are applicable instead of the
+ * above. If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use your
+ * version of this file under the RHEPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice and
+ * other provisions required by the GPL. If you do not delete the
+ * provisions above, a recipient may use your version of this file
+ * under either the RHEPL or the GPL.
+ *
+ * $Id: pushpull.c,v 1.7 2001/09/23 10:04:15 rmk Exp $
+ *
+ */
+
+#include <linux/string.h>
+#include "pushpull.h"
+#include <linux/errno.h>
+
+void init_pushpull(struct pushpull *pp, char *buf, unsigned buflen, unsigned ofs, unsigned reserve)
+{
+ pp->buf = buf;
+ pp->buflen = buflen;
+ pp->ofs = ofs;
+ pp->reserve = reserve;
+}
+
+
+int pushbit(struct pushpull *pp, int bit, int use_reserved)
+{
+ if (pp->ofs >= pp->buflen - (use_reserved?0:pp->reserve)) {
+ return -ENOSPC;
+ }
+
+ if (bit) {
+ pp->buf[pp->ofs >> 3] |= (1<<(7-(pp->ofs &7)));
+ }
+ else {
+ pp->buf[pp->ofs >> 3] &= ~(1<<(7-(pp->ofs &7)));
+ }
+ pp->ofs++;
+
+ return 0;
+}
+
+int pushedbits(struct pushpull *pp)
+{
+ return pp->ofs;
+}
diff --git a/fs/jffs2/pushpull.h b/fs/jffs2/pushpull.h
new file mode 100644
index 00000000000000..dba10f3eac3f4e
--- /dev/null
+++ b/fs/jffs2/pushpull.h
@@ -0,0 +1,66 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ *
+ * The original JFFS, from which the design for JFFS2 was derived,
+ * was designed and implemented by Axis Communications AB.
+ *
+ * The contents of this file are subject to the Red Hat eCos Public
+ * License Version 1.1 (the "Licence"); you may not use this file
+ * except in compliance with the Licence. You may obtain a copy of
+ * the Licence at http://www.redhat.com/
+ *
+ * Software distributed under the Licence is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing rights and
+ * limitations under the Licence.
+ *
+ * The Original Code is JFFS2 - Journalling Flash File System, version 2
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in
+ * which case the provisions of the GPL are applicable instead of the
+ * above. If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use your
+ * version of this file under the RHEPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice and
+ * other provisions required by the GPL. If you do not delete the
+ * provisions above, a recipient may use your version of this file
+ * under either the RHEPL or the GPL.
+ *
+ * $Id: pushpull.h,v 1.5 2001/09/23 10:04:15 rmk Exp $
+ *
+ */
+
+#ifndef __PUSHPULL_H__
+#define __PUSHPULL_H__
+struct pushpull {
+ unsigned char *buf;
+ unsigned int buflen;
+ unsigned int ofs;
+ unsigned int reserve;
+};
+
+void init_pushpull(struct pushpull *, char *, unsigned, unsigned, unsigned);
+int pushbit(struct pushpull *pp, int bit, int use_reserved);
+int pushedbits(struct pushpull *pp);
+
+static inline int pullbit(struct pushpull *pp)
+{
+ int bit;
+
+ bit = (pp->buf[pp->ofs >> 3] >> (7-(pp->ofs & 7))) & 1;
+
+ pp->ofs++;
+ return bit;
+}
+
+static inline int pulledbits(struct pushpull *pp)
+{
+ return pp->ofs;
+}
+
+#endif /* __PUSHPULL_H__ */
diff --git a/fs/jffs2/read.c b/fs/jffs2/read.c
new file mode 100644
index 00000000000000..398ff5624baa51
--- /dev/null
+++ b/fs/jffs2/read.c
@@ -0,0 +1,163 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ *
+ * The original JFFS, from which the design for JFFS2 was derived,
+ * was designed and implemented by Axis Communications AB.
+ *
+ * The contents of this file are subject to the Red Hat eCos Public
+ * License Version 1.1 (the "Licence"); you may not use this file
+ * except in compliance with the Licence. You may obtain a copy of
+ * the Licence at http://www.redhat.com/
+ *
+ * Software distributed under the Licence is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing rights and
+ * limitations under the Licence.
+ *
+ * The Original Code is JFFS2 - Journalling Flash File System, version 2
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in
+ * which case the provisions of the GPL are applicable instead of the
+ * above. If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use your
+ * version of this file under the RHEPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice and
+ * other provisions required by the GPL. If you do not delete the
+ * provisions above, a recipient may use your version of this file
+ * under either the RHEPL or the GPL.
+ *
+ * $Id: read.c,v 1.13.2.2 2003/11/02 13:51:18 dwmw2 Exp $
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/jffs2.h>
+#include <linux/mtd/mtd.h>
+#include "nodelist.h"
+#include <linux/crc32.h>
+
+int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len)
+{
+ struct jffs2_raw_inode *ri;
+ size_t readlen;
+ __u32 crc;
+ unsigned char *decomprbuf = NULL;
+ unsigned char *readbuf = NULL;
+ int ret = 0;
+
+ ri = jffs2_alloc_raw_inode();
+ if (!ri)
+ return -ENOMEM;
+
+ ret = c->mtd->read(c->mtd, fd->raw->flash_offset & ~3, sizeof(*ri), &readlen, (char *)ri);
+ if (ret) {
+ jffs2_free_raw_inode(ri);
+ printk(KERN_WARNING "Error reading node from 0x%08x: %d\n", fd->raw->flash_offset & ~3, ret);
+ return ret;
+ }
+ if (readlen != sizeof(*ri)) {
+ jffs2_free_raw_inode(ri);
+ printk(KERN_WARNING "Short read from 0x%08x: wanted 0x%x bytes, got 0x%x\n",
+ fd->raw->flash_offset & ~3, sizeof(*ri), readlen);
+ return -EIO;
+ }
+ crc = crc32(0, ri, sizeof(*ri)-8);
+
+ D1(printk(KERN_DEBUG "Node read from %08x: node_crc %08x, calculated CRC %08x. dsize %x, csize %x, offset %x, buf %p\n", fd->raw->flash_offset & ~3, ri->node_crc, crc, ri->dsize, ri->csize, ri->offset, buf));
+ if (crc != ri->node_crc) {
+ printk(KERN_WARNING "Node CRC %08x != calculated CRC %08x for node at %08x\n", ri->node_crc, crc, fd->raw->flash_offset & ~3);
+ ret = -EIO;
+ goto out_ri;
+ }
+ /* There was a bug where we wrote hole nodes out with csize/dsize
+ swapped. Deal with it */
+ if (ri->compr == JFFS2_COMPR_ZERO && !ri->dsize && ri->csize) {
+ ri->dsize = ri->csize;
+ ri->csize = 0;
+ }
+
+ D1(if(ofs + len > ri->dsize) {
+ printk(KERN_WARNING "jffs2_read_dnode() asked for %d bytes at %d from %d-byte node\n", len, ofs, ri->dsize);
+ ret = -EINVAL;
+ goto out_ri;
+ });
+
+
+ if (ri->compr == JFFS2_COMPR_ZERO) {
+ memset(buf, 0, len);
+ goto out_ri;
+ }
+
+ /* Cases:
+ Reading whole node and it's uncompressed - read directly to buffer provided, check CRC.
+ Reading whole node and it's compressed - read into comprbuf, check CRC and decompress to buffer provided
+ Reading partial node and it's uncompressed - read into readbuf, check CRC, and copy
+ Reading partial node and it's compressed - read into readbuf, check checksum, decompress to decomprbuf and copy
+ */
+ if (ri->compr == JFFS2_COMPR_NONE && len == ri->dsize) {
+ readbuf = buf;
+ } else {
+ readbuf = kmalloc(ri->csize, GFP_KERNEL);
+ if (!readbuf) {
+ ret = -ENOMEM;
+ goto out_ri;
+ }
+ }
+ if (ri->compr != JFFS2_COMPR_NONE) {
+ if (len < ri->dsize) {
+ decomprbuf = kmalloc(ri->dsize, GFP_KERNEL);
+ if (!decomprbuf) {
+ ret = -ENOMEM;
+ goto out_readbuf;
+ }
+ } else {
+ decomprbuf = buf;
+ }
+ } else {
+ decomprbuf = readbuf;
+ }
+
+ D2(printk(KERN_DEBUG "Read %d bytes to %p\n", ri->csize, readbuf));
+ ret = c->mtd->read(c->mtd, (fd->raw->flash_offset &~3) + sizeof(*ri), ri->csize, &readlen, readbuf);
+
+ if (!ret && readlen != ri->csize)
+ ret = -EIO;
+ if (ret)
+ goto out_decomprbuf;
+
+ crc = crc32(0, readbuf, ri->csize);
+ if (crc != ri->data_crc) {
+ printk(KERN_WARNING "Data CRC %08x != calculated CRC %08x for node at %08x\n", ri->data_crc, crc, fd->raw->flash_offset & ~3);
+ ret = -EIO;
+ goto out_decomprbuf;
+ }
+ D2(printk(KERN_DEBUG "Data CRC matches calculated CRC %08x\n", crc));
+ if (ri->compr != JFFS2_COMPR_NONE) {
+ D2(printk(KERN_DEBUG "Decompress %d bytes from %p to %d bytes at %p\n", ri->csize, readbuf, ri->dsize, decomprbuf));
+ ret = jffs2_decompress(ri->compr, readbuf, decomprbuf, ri->csize, ri->dsize);
+ if (ret) {
+ printk(KERN_WARNING "Error: jffs2_decompress returned %d\n", ret);
+ goto out_decomprbuf;
+ }
+ }
+
+ if (len < ri->dsize) {
+ memcpy(buf, decomprbuf+ofs, len);
+ }
+ out_decomprbuf:
+ if(decomprbuf != buf && decomprbuf != readbuf)
+ kfree(decomprbuf);
+ out_readbuf:
+ if(readbuf != buf)
+ kfree(readbuf);
+ out_ri:
+ jffs2_free_raw_inode(ri);
+
+ return ret;
+}
diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c
new file mode 100644
index 00000000000000..f81bd41ebddf82
--- /dev/null
+++ b/fs/jffs2/readinode.c
@@ -0,0 +1,508 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ *
+ * The original JFFS, from which the design for JFFS2 was derived,
+ * was designed and implemented by Axis Communications AB.
+ *
+ * The contents of this file are subject to the Red Hat eCos Public
+ * License Version 1.1 (the "Licence"); you may not use this file
+ * except in compliance with the Licence. You may obtain a copy of
+ * the Licence at http://www.redhat.com/
+ *
+ * Software distributed under the Licence is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing rights and
+ * limitations under the Licence.
+ *
+ * The Original Code is JFFS2 - Journalling Flash File System, version 2
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in
+ * which case the provisions of the GPL are applicable instead of the
+ * above. If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use your
+ * version of this file under the RHEPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice and
+ * other provisions required by the GPL. If you do not delete the
+ * provisions above, a recipient may use your version of this file
+ * under either the RHEPL or the GPL.
+ *
+ * $Id: readinode.c,v 1.58.2.8 2003/11/02 13:54:20 dwmw2 Exp $
+ *
+ */
+
+/* Given an inode, probably with existing list of fragments, add the new node
+ * to the fragment list.
+ */
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/mtd/mtd.h>
+#include <linux/jffs2.h>
+#include "nodelist.h"
+#include <linux/crc32.h>
+
+
+D1(void jffs2_print_frag_list(struct jffs2_inode_info *f)
+{
+ struct jffs2_node_frag *this = f->fraglist;
+
+ while(this) {
+ if (this->node)
+ printk(KERN_DEBUG "frag %04x-%04x: 0x%08x on flash (*%p->%p)\n", this->ofs, this->ofs+this->size, this->node->raw->flash_offset &~3, this, this->next);
+ else
+ printk(KERN_DEBUG "frag %04x-%04x: hole (*%p->%p)\n", this->ofs, this->ofs+this->size, this, this->next);
+ this = this->next;
+ }
+ if (f->metadata) {
+ printk(KERN_DEBUG "metadata at 0x%08x\n", f->metadata->raw->flash_offset &~3);
+ }
+})
+
+
+int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn)
+{
+ int ret;
+ D1(printk(KERN_DEBUG "jffs2_add_full_dnode_to_inode(ino #%u, f %p, fn %p)\n", f->inocache->ino, f, fn));
+
+ ret = jffs2_add_full_dnode_to_fraglist(c, &f->fraglist, fn);
+
+ D2(jffs2_print_frag_list(f));
+ return ret;
+}
+
+static void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_frag *this)
+{
+ if (this->node) {
+ this->node->frags--;
+ if (!this->node->frags) {
+ /* The node has no valid frags left. It's totally obsoleted */
+ D2(printk(KERN_DEBUG "Marking old node @0x%08x (0x%04x-0x%04x) obsolete\n",
+ this->node->raw->flash_offset &~3, this->node->ofs, this->node->ofs+this->node->size));
+ jffs2_mark_node_obsolete(c, this->node->raw);
+ jffs2_free_full_dnode(this->node);
+ } else {
+ D2(printk(KERN_DEBUG "Not marking old node @0x%08x (0x%04x-0x%04x) obsolete. frags is %d\n",
+ this->node->raw->flash_offset &~3, this->node->ofs, this->node->ofs+this->node->size,
+ this->node->frags));
+ }
+
+ }
+ jffs2_free_node_frag(this);
+}
+
+/* Doesn't set inode->i_size */
+int jffs2_add_full_dnode_to_fraglist(struct jffs2_sb_info *c, struct jffs2_node_frag **list, struct jffs2_full_dnode *fn)
+{
+
+ struct jffs2_node_frag *this, **prev, *old;
+ struct jffs2_node_frag *newfrag, *newfrag2;
+ __u32 lastend = 0;
+
+
+ newfrag = jffs2_alloc_node_frag();
+ if (!newfrag) {
+ return -ENOMEM;
+ }
+
+ D2(if (fn->raw)
+ printk(KERN_DEBUG "adding node %04x-%04x @0x%08x on flash, newfrag *%p\n", fn->ofs, fn->ofs+fn->size, fn->raw->flash_offset &~3, newfrag);
+ else
+ printk(KERN_DEBUG "adding hole node %04x-%04x on flash, newfrag *%p\n", fn->ofs, fn->ofs+fn->size, newfrag));
+
+ prev = list;
+ this = *list;
+
+ if (!fn->size) {
+ jffs2_free_node_frag(newfrag);
+ return 0;
+ }
+
+ newfrag->ofs = fn->ofs;
+ newfrag->size = fn->size;
+ newfrag->node = fn;
+ newfrag->node->frags = 1;
+ newfrag->next = (void *)0xdeadbeef;
+
+ /* Skip all the nodes which are completed before this one starts */
+ while(this && fn->ofs >= this->ofs+this->size) {
+ lastend = this->ofs + this->size;
+
+ D2(printk(KERN_DEBUG "j_a_f_d_t_f: skipping frag 0x%04x-0x%04x; phys 0x%08x (*%p->%p)\n",
+ this->ofs, this->ofs+this->size, this->node?(this->node->raw->flash_offset &~3):0xffffffff, this, this->next));
+ prev = &this->next;
+ this = this->next;
+ }
+
+ /* See if we ran off the end of the list */
+ if (!this) {
+ /* We did */
+ if (lastend < fn->ofs) {
+ /* ... and we need to put a hole in before the new node */
+ struct jffs2_node_frag *holefrag = jffs2_alloc_node_frag();
+ if (!holefrag)
+ return -ENOMEM;
+ holefrag->ofs = lastend;
+ holefrag->size = fn->ofs - lastend;
+ holefrag->next = NULL;
+ holefrag->node = NULL;
+ *prev = holefrag;
+ prev = &holefrag->next;
+ }
+ newfrag->next = NULL;
+ *prev = newfrag;
+ return 0;
+ }
+
+ D2(printk(KERN_DEBUG "j_a_f_d_t_f: dealing with frag 0x%04x-0x%04x; phys 0x%08x (*%p->%p)\n",
+ this->ofs, this->ofs+this->size, this->node?(this->node->raw->flash_offset &~3):0xffffffff, this, this->next));
+
+ /* OK. 'this' is pointing at the first frag that fn->ofs at least partially obsoletes,
+ * - i.e. fn->ofs < this->ofs+this->size && fn->ofs >= this->ofs
+ */
+ if (fn->ofs > this->ofs) {
+ /* This node isn't completely obsoleted. The start of it remains valid */
+ if (this->ofs + this->size > fn->ofs + fn->size) {
+ /* The new node splits 'this' frag into two */
+ newfrag2 = jffs2_alloc_node_frag();
+ if (!newfrag2) {
+ jffs2_free_node_frag(newfrag);
+ return -ENOMEM;
+ }
+ D1(printk(KERN_DEBUG "split old frag 0x%04x-0x%04x -->", this->ofs, this->ofs+this->size);
+ if (this->node)
+ printk("phys 0x%08x\n", this->node->raw->flash_offset &~3);
+ else
+ printk("hole\n");
+ )
+ newfrag2->ofs = fn->ofs + fn->size;
+ newfrag2->size = (this->ofs+this->size) - newfrag2->ofs;
+ newfrag2->next = this->next;
+ newfrag2->node = this->node;
+ if (this->node)
+ this->node->frags++;
+ newfrag->next = newfrag2;
+ this->next = newfrag;
+ this->size = newfrag->ofs - this->ofs;
+ return 0;
+ }
+ /* New node just reduces 'this' frag in size, doesn't split it */
+ this->size = fn->ofs - this->ofs;
+ newfrag->next = this->next;
+ this->next = newfrag;
+ this = newfrag->next;
+ } else {
+ D2(printk(KERN_DEBUG "Inserting newfrag (*%p) in before 'this' (*%p)\n", newfrag, this));
+ *prev = newfrag;
+ newfrag->next = this;
+ }
+ /* OK, now we have newfrag added in the correct place in the list, but
+ newfrag->next points to a fragment which may be overlapping it
+ */
+ while (this && newfrag->ofs + newfrag->size >= this->ofs + this->size) {
+ /* 'this' frag is obsoleted. */
+ old = this;
+ this = old->next;
+ jffs2_obsolete_node_frag(c, old);
+ }
+ /* Now we're pointing at the first frag which isn't totally obsoleted by
+ the new frag */
+ newfrag->next = this;
+
+ if (!this || newfrag->ofs + newfrag->size == this->ofs) {
+ return 0;
+ }
+ /* Still some overlap */
+ this->size = (this->ofs + this->size) - (newfrag->ofs + newfrag->size);
+ this->ofs = newfrag->ofs + newfrag->size;
+ return 0;
+}
+
+void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct jffs2_node_frag **list, __u32 size)
+{
+ D1(printk(KERN_DEBUG "Truncating fraglist to 0x%08x bytes\n", size));
+
+ while (*list) {
+ if ((*list)->ofs >= size) {
+ struct jffs2_node_frag *this = *list;
+ *list = this->next;
+ D1(printk(KERN_DEBUG "Removing frag 0x%08x-0x%08x\n", this->ofs, this->ofs+this->size));
+ jffs2_obsolete_node_frag(c, this);
+ continue;
+ } else if ((*list)->ofs + (*list)->size > size) {
+ D1(printk(KERN_DEBUG "Truncating frag 0x%08x-0x%08x\n", (*list)->ofs, (*list)->ofs + (*list)->size));
+ (*list)->size = size - (*list)->ofs;
+ }
+ list = &(*list)->next;
+ }
+}
+
+/* Scan the list of all nodes present for this ino, build map of versions, etc. */
+
+void jffs2_read_inode (struct inode *inode)
+{
+ struct jffs2_tmp_dnode_info *tn_list, *tn;
+ struct jffs2_full_dirent *fd_list;
+ struct jffs2_inode_info *f;
+ struct jffs2_full_dnode *fn = NULL;
+ struct jffs2_sb_info *c;
+ struct jffs2_raw_inode latest_node;
+ __u32 latest_mctime, mctime_ver;
+ __u32 mdata_ver = 0;
+ int ret;
+ ssize_t retlen;
+
+ D1(printk(KERN_DEBUG "jffs2_read_inode(): inode->i_ino == %lu\n", inode->i_ino));
+
+ f = JFFS2_INODE_INFO(inode);
+ c = JFFS2_SB_INFO(inode->i_sb);
+
+ memset(f, 0, sizeof(*f));
+ D2(printk(KERN_DEBUG "getting inocache\n"));
+ init_MUTEX(&f->sem);
+ f->inocache = jffs2_get_ino_cache(c, inode->i_ino);
+ D2(printk(KERN_DEBUG "jffs2_read_inode(): Got inocache at %p\n", f->inocache));
+
+ if (!f->inocache && inode->i_ino == 1) {
+ /* Special case - no root inode on medium */
+ f->inocache = jffs2_alloc_inode_cache();
+ if (!f->inocache) {
+ printk(KERN_CRIT "jffs2_read_inode(): Cannot allocate inocache for root inode\n");
+ make_bad_inode(inode);
+ return;
+ }
+ D1(printk(KERN_DEBUG "jffs2_read_inode(): Creating inocache for root inode\n"));
+ memset(f->inocache, 0, sizeof(struct jffs2_inode_cache));
+ f->inocache->ino = f->inocache->nlink = 1;
+ f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
+ jffs2_add_ino_cache(c, f->inocache);
+ }
+ if (!f->inocache) {
+ printk(KERN_WARNING "jffs2_read_inode() on nonexistent ino %lu\n", (unsigned long)inode->i_ino);
+ make_bad_inode(inode);
+ return;
+ }
+ D1(printk(KERN_DEBUG "jffs2_read_inode(): ino #%lu nlink is %d\n", (unsigned long)inode->i_ino, f->inocache->nlink));
+ inode->i_nlink = f->inocache->nlink;
+
+ /* Grab all nodes relevant to this ino */
+ ret = jffs2_get_inode_nodes(c, inode->i_ino, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver);
+
+ if (ret) {
+ printk(KERN_CRIT "jffs2_get_inode_nodes() for ino %lu returned %d\n", inode->i_ino, ret);
+ make_bad_inode(inode);
+ return;
+ }
+ f->dents = fd_list;
+
+ while (tn_list) {
+ tn = tn_list;
+
+ fn = tn->fn;
+
+ if (f->metadata && tn->version > mdata_ver) {
+ D1(printk(KERN_DEBUG "Obsoleting old metadata at 0x%08x\n", f->metadata->raw->flash_offset &~3));
+ jffs2_mark_node_obsolete(c, f->metadata->raw);
+ jffs2_free_full_dnode(f->metadata);
+ f->metadata = NULL;
+
+ mdata_ver = 0;
+ }
+
+ if (fn->size) {
+ jffs2_add_full_dnode_to_inode(c, f, fn);
+ } else {
+ /* Zero-sized node at end of version list. Just a metadata update */
+ D1(printk(KERN_DEBUG "metadata @%08x: ver %d\n", fn->raw->flash_offset &~3, tn->version));
+ f->metadata = fn;
+ mdata_ver = tn->version;
+ }
+ tn_list = tn->next;
+ jffs2_free_tmp_dnode_info(tn);
+ }
+ if (!fn) {
+ /* No data nodes for this inode. */
+ if (inode->i_ino != 1) {
+ printk(KERN_WARNING "jffs2_read_inode(): No data nodes found for ino #%lu\n", inode->i_ino);
+ if (!fd_list) {
+ make_bad_inode(inode);
+ return;
+ }
+ printk(KERN_WARNING "jffs2_read_inode(): But it has children so we fake some modes for it\n");
+ }
+ inode->i_mode = S_IFDIR | S_IRUGO | S_IWUSR | S_IXUGO;
+ latest_node.version = 0;
+ inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+ inode->i_nlink = f->inocache->nlink;
+ inode->i_size = 0;
+ } else {
+ __u32 crc;
+
+ ret = c->mtd->read(c->mtd, fn->raw->flash_offset & ~3, sizeof(latest_node), &retlen, (void *)&latest_node);
+ if (ret || retlen != sizeof(latest_node)) {
+ printk(KERN_NOTICE "MTD read in jffs2_read_inode() failed: Returned %d, %ld of %d bytes read\n",
+ ret, (long)retlen, sizeof(latest_node));
+ jffs2_clear_inode(inode);
+ make_bad_inode(inode);
+ return;
+ }
+
+ crc = crc32(0, &latest_node, sizeof(latest_node)-8);
+ if (crc != latest_node.node_crc) {
+ printk(KERN_NOTICE "CRC failed for read_inode of inode %ld at physical location 0x%x\n", inode->i_ino, fn->raw->flash_offset & ~3);
+ jffs2_clear_inode(inode);
+ make_bad_inode(inode);
+ return;
+ }
+
+ inode->i_mode = latest_node.mode;
+ inode->i_uid = latest_node.uid;
+ inode->i_gid = latest_node.gid;
+ inode->i_size = latest_node.isize;
+ if (S_ISREG(inode->i_mode))
+ jffs2_truncate_fraglist(c, &f->fraglist, latest_node.isize);
+ inode->i_atime = latest_node.atime;
+ inode->i_mtime = latest_node.mtime;
+ inode->i_ctime = latest_node.ctime;
+ }
+
+ /* OK, now the special cases. Certain inode types should
+ have only one data node, and it's kept as the metadata
+ node */
+ if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode) ||
+ S_ISLNK(inode->i_mode)) {
+ if (f->metadata) {
+ printk(KERN_WARNING "Argh. Special inode #%lu with mode 0%o had metadata node\n", inode->i_ino, inode->i_mode);
+ jffs2_clear_inode(inode);
+ make_bad_inode(inode);
+ return;
+ }
+ if (!f->fraglist) {
+ printk(KERN_WARNING "Argh. Special inode #%lu with mode 0%o has no fragments\n", inode->i_ino, inode->i_mode);
+ jffs2_clear_inode(inode);
+ make_bad_inode(inode);
+ return;
+ }
+ /* ASSERT: f->fraglist != NULL */
+ if (f->fraglist->next) {
+ printk(KERN_WARNING "Argh. Special inode #%lu with mode 0%o had more than one node\n", inode->i_ino, inode->i_mode);
+ /* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */
+ jffs2_clear_inode(inode);
+ make_bad_inode(inode);
+ return;
+ }
+ /* OK. We're happy */
+ f->metadata = f->fraglist->node;
+ jffs2_free_node_frag(f->fraglist);
+ f->fraglist = NULL;
+ }
+
+ inode->i_blksize = PAGE_SIZE;
+ inode->i_blocks = (inode->i_size + 511) >> 9;
+
+ switch (inode->i_mode & S_IFMT) {
+ unsigned short rdev;
+
+ case S_IFLNK:
+ inode->i_op = &jffs2_symlink_inode_operations;
+ /* Hack to work around broken isize in old symlink code.
+ Remove this when dwmw2 comes to his senses and stops
+ symlinks from being an entirely gratuitous special
+ case. */
+ if (!inode->i_size)
+ inode->i_size = latest_node.dsize;
+ break;
+
+ case S_IFDIR:
+ if (mctime_ver > latest_node.version) {
+ /* The times in the latest_node are actually older than
+ mctime in the latest dirent. Cheat. */
+ inode->i_mtime = inode->i_ctime = inode->i_atime =
+ latest_mctime;
+ }
+ inode->i_op = &jffs2_dir_inode_operations;
+ inode->i_fop = &jffs2_dir_operations;
+ break;
+
+ case S_IFREG:
+ inode->i_op = &jffs2_file_inode_operations;
+ inode->i_fop = &jffs2_file_operations;
+ inode->i_mapping->a_ops = &jffs2_file_address_operations;
+ inode->i_mapping->nrpages = 0;
+ break;
+
+ case S_IFBLK:
+ case S_IFCHR:
+ /* Read the device numbers from the media */
+ D1(printk(KERN_DEBUG "Reading device numbers from flash\n"));
+ if (jffs2_read_dnode(c, f->metadata, (char *)&rdev, 0, sizeof(rdev)) < 0) {
+ /* Eep */
+ printk(KERN_NOTICE "Read device numbers for inode %lu failed\n", (unsigned long)inode->i_ino);
+ jffs2_clear_inode(inode);
+ make_bad_inode(inode);
+ return;
+ }
+
+ case S_IFSOCK:
+ case S_IFIFO:
+ inode->i_op = &jffs2_file_inode_operations;
+ init_special_inode(inode, inode->i_mode, kdev_t_to_nr(MKDEV(rdev>>8, rdev&0xff)));
+ break;
+
+ default:
+ printk(KERN_WARNING "jffs2_read_inode(): Bogus imode %o for ino %lu", inode->i_mode, (unsigned long)inode->i_ino);
+ }
+ D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n"));
+}
+
+void jffs2_clear_inode (struct inode *inode)
+{
+ /* We can forget about this inode for now - drop all
+ * the nodelists associated with it, etc.
+ */
+ struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
+ struct jffs2_node_frag *frag, *frags;
+ struct jffs2_full_dirent *fd, *fds;
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ int deleted;
+
+ D1(printk(KERN_DEBUG "jffs2_clear_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode));
+
+ down(&f->sem);
+ deleted = f->inocache && !f->inocache->nlink;
+
+ frags = f->fraglist;
+ fds = f->dents;
+ if (f->metadata) {
+ if (deleted)
+ jffs2_mark_node_obsolete(c, f->metadata->raw);
+ jffs2_free_full_dnode(f->metadata);
+ }
+
+ while (frags) {
+ frag = frags;
+ frags = frag->next;
+ D2(printk(KERN_DEBUG "jffs2_clear_inode: frag at 0x%x-0x%x: node %p, frags %d--\n", frag->ofs, frag->ofs+frag->size, frag->node, frag->node?frag->node->frags:0));
+
+ if (frag->node && !(--frag->node->frags)) {
+ /* Not a hole, and it's the final remaining frag of this node. Free the node */
+ if (deleted)
+ jffs2_mark_node_obsolete(c, frag->node->raw);
+
+ jffs2_free_full_dnode(frag->node);
+ }
+ jffs2_free_node_frag(frag);
+ }
+ while(fds) {
+ fd = fds;
+ fds = fd->next;
+ jffs2_free_full_dirent(fd);
+ }
+
+ up(&f->sem);
+};
+
diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c
new file mode 100644
index 00000000000000..ccfab13ba92d16
--- /dev/null
+++ b/fs/jffs2/scan.c
@@ -0,0 +1,756 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ *
+ * The original JFFS, from which the design for JFFS2 was derived,
+ * was designed and implemented by Axis Communications AB.
+ *
+ * The contents of this file are subject to the Red Hat eCos Public
+ * License Version 1.1 (the "Licence"); you may not use this file
+ * except in compliance with the Licence. You may obtain a copy of
+ * the Licence at http://www.redhat.com/
+ *
+ * Software distributed under the Licence is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing rights and
+ * limitations under the Licence.
+ *
+ * The Original Code is JFFS2 - Journalling Flash File System, version 2
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in
+ * which case the provisions of the GPL are applicable instead of the
+ * above. If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use your
+ * version of this file under the RHEPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice and
+ * other provisions required by the GPL. If you do not delete the
+ * provisions above, a recipient may use your version of this file
+ * under either the RHEPL or the GPL.
+ *
+ * $Id: scan.c,v 1.51.2.4 2003/11/02 13:51:18 dwmw2 Exp $
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/jffs2.h>
+#include <linux/mtd/mtd.h>
+#include <linux/pagemap.h>
+#include "nodelist.h"
+#include <linux/crc32.h>
+
+
+#define DIRTY_SPACE(x) do { typeof(x) _x = (x); \
+ c->free_size -= _x; c->dirty_size += _x; \
+ jeb->free_size -= _x ; jeb->dirty_size += _x; \
+ }while(0)
+#define USED_SPACE(x) do { typeof(x) _x = (x); \
+ c->free_size -= _x; c->used_size += _x; \
+ jeb->free_size -= _x ; jeb->used_size += _x; \
+ }while(0)
+
+#define noisy_printk(noise, args...) do { \
+ if (*(noise)) { \
+ printk(KERN_NOTICE args); \
+ (*(noise))--; \
+ if (!(*(noise))) { \
+ printk(KERN_NOTICE "Further such events for this erase block will not be printed\n"); \
+ } \
+ } \
+} while(0)
+
+static uint32_t pseudo_random;
+static void jffs2_rotate_lists(struct jffs2_sb_info *c);
+
+static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
+
+/* These helper functions _must_ increase ofs and also do the dirty/used space accounting.
+ * Returning an error will abort the mount - bad checksums etc. should just mark the space
+ * as dirty.
+ */
+static int jffs2_scan_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs, int *noise);
+static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs);
+static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs);
+
+
+int jffs2_scan_medium(struct jffs2_sb_info *c)
+{
+ int i, ret;
+ __u32 empty_blocks = 0;
+
+ if (!c->blocks) {
+ printk(KERN_WARNING "EEEK! c->blocks is NULL!\n");
+ return -EINVAL;
+ }
+ for (i=0; i<c->nr_blocks; i++) {
+ struct jffs2_eraseblock *jeb = &c->blocks[i];
+
+ ret = jffs2_scan_eraseblock(c, jeb);
+ if (ret < 0)
+ return ret;
+
+ ACCT_PARANOIA_CHECK(jeb);
+
+ /* Now decide which list to put it on */
+ if (ret == 1) {
+ /*
+ * Empty block. Since we can't be sure it
+ * was entirely erased, we just queue it for erase
+ * again. It will be marked as such when the erase
+ * is complete. Meanwhile we still count it as empty
+ * for later checks.
+ */
+ list_add(&jeb->list, &c->erase_pending_list);
+ empty_blocks++;
+ c->nr_erasing_blocks++;
+ } else if (jeb->used_size == PAD(sizeof(struct jffs2_unknown_node)) && !jeb->first_node->next_in_ino) {
+ /* Only a CLEANMARKER node is valid */
+ if (!jeb->dirty_size) {
+ /* It's actually free */
+ list_add(&jeb->list, &c->free_list);
+ c->nr_free_blocks++;
+ } else {
+ /* Dirt */
+ D1(printk(KERN_DEBUG "Adding all-dirty block at 0x%08x to erase_pending_list\n", jeb->offset));
+ list_add(&jeb->list, &c->erase_pending_list);
+ c->nr_erasing_blocks++;
+ }
+ } else if (jeb->used_size > c->sector_size - (2*sizeof(struct jffs2_raw_inode))) {
+ /* Full (or almost full) of clean data. Clean list */
+ list_add(&jeb->list, &c->clean_list);
+ } else if (jeb->used_size) {
+ /* Some data, but not full. Dirty list. */
+ /* Except that we want to remember the block with most free space,
+ and stick it in the 'nextblock' position to start writing to it.
+ Later when we do snapshots, this must be the most recent block,
+ not the one with most free space.
+ */
+ if (jeb->free_size > 2*sizeof(struct jffs2_raw_inode) &&
+ (!c->nextblock || c->nextblock->free_size < jeb->free_size)) {
+ /* Better candidate for the next writes to go to */
+ if (c->nextblock)
+ list_add(&c->nextblock->list, &c->dirty_list);
+ c->nextblock = jeb;
+ } else {
+ list_add(&jeb->list, &c->dirty_list);
+ }
+ } else {
+ /* Nothing valid - not even a clean marker. Needs erasing. */
+ /* For now we just put it on the erasing list. We'll start the erases later */
+ printk(KERN_NOTICE "JFFS2: Erase block at 0x%08x is not formatted. It will be erased\n", jeb->offset);
+ list_add(&jeb->list, &c->erase_pending_list);
+ c->nr_erasing_blocks++;
+ }
+ }
+ /* Rotate the lists by some number to ensure wear levelling */
+ jffs2_rotate_lists(c);
+
+ if (c->nr_erasing_blocks) {
+ if (!c->used_size && empty_blocks != c->nr_blocks) {
+ printk(KERN_NOTICE "Cowardly refusing to erase blocks on filesystem with no valid JFFS2 nodes\n");
+ return -EIO;
+ }
+ jffs2_erase_pending_trigger(c);
+ }
+ return 0;
+}
+
+static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) {
+ struct jffs2_unknown_node node;
+ __u32 ofs, prevofs;
+ __u32 hdr_crc, nodetype;
+ int err;
+ int noise = 0;
+
+ ofs = jeb->offset;
+ prevofs = jeb->offset - 1;
+
+ D1(printk(KERN_DEBUG "jffs2_scan_eraseblock(): Scanning block at 0x%x\n", ofs));
+
+ err = jffs2_scan_empty(c, jeb, &ofs, &noise);
+ if (err) return err;
+ if (ofs == jeb->offset + c->sector_size) {
+ D1(printk(KERN_DEBUG "Block at 0x%08x is empty (erased)\n", jeb->offset));
+ return 1; /* special return code */
+ }
+
+ noise = 10;
+
+ while(ofs < jeb->offset + c->sector_size) {
+ ssize_t retlen;
+ ACCT_PARANOIA_CHECK(jeb);
+
+ if (ofs & 3) {
+ printk(KERN_WARNING "Eep. ofs 0x%08x not word-aligned!\n", ofs);
+ ofs = (ofs+3)&~3;
+ continue;
+ }
+ if (ofs == prevofs) {
+ printk(KERN_WARNING "ofs 0x%08x has already been seen. Skipping\n", ofs);
+ DIRTY_SPACE(4);
+ ofs += 4;
+ continue;
+ }
+ prevofs = ofs;
+
+ if (jeb->offset + c->sector_size < ofs + sizeof(node)) {
+ D1(printk(KERN_DEBUG "Fewer than %d bytes left to end of block. Not reading\n", sizeof(struct jffs2_unknown_node)));
+ DIRTY_SPACE((jeb->offset + c->sector_size)-ofs);
+ break;
+ }
+
+ err = c->mtd->read(c->mtd, ofs, sizeof(node), &retlen, (char *)&node);
+
+ if (err) {
+ D1(printk(KERN_WARNING "mtd->read(0x%x bytes from 0x%x) returned %d\n", sizeof(node), ofs, err));
+ return err;
+ }
+ if (retlen < sizeof(node)) {
+ D1(printk(KERN_WARNING "Read at 0x%x gave only 0x%x bytes\n", ofs, retlen));
+ DIRTY_SPACE(retlen);
+ ofs += retlen;
+ continue;
+ }
+
+ if (node.magic == JFFS2_EMPTY_BITMASK && node.nodetype == JFFS2_EMPTY_BITMASK) {
+ D1(printk(KERN_DEBUG "Found empty flash at 0x%x\n", ofs));
+ err = jffs2_scan_empty(c, jeb, &ofs, &noise);
+ if (err) return err;
+ continue;
+ }
+
+ if (ofs == jeb->offset && node.magic == KSAMTIB_CIGAM_2SFFJ) {
+ printk(KERN_WARNING "Magic bitmask is backwards at offset 0x%08x. Wrong endian filesystem?\n", ofs);
+ DIRTY_SPACE(4);
+ ofs += 4;
+ continue;
+ }
+ if (node.magic == JFFS2_DIRTY_BITMASK) {
+ D1(printk(KERN_DEBUG "Empty bitmask at 0x%08x\n", ofs));
+ DIRTY_SPACE(4);
+ ofs += 4;
+ continue;
+ }
+ if (node.magic == JFFS2_OLD_MAGIC_BITMASK) {
+ printk(KERN_WARNING "Old JFFS2 bitmask found at 0x%08x\n", ofs);
+ printk(KERN_WARNING "You cannot use older JFFS2 filesystems with newer kernels\n");
+ DIRTY_SPACE(4);
+ ofs += 4;
+ continue;
+ }
+ if (node.magic != JFFS2_MAGIC_BITMASK) {
+ /* OK. We're out of possibilities. Whinge and move on */
+ noisy_printk(&noise, "jffs2_scan_eraseblock(): Magic bitmask 0x%04x not found at 0x%08x: 0x%04x instead\n", JFFS2_MAGIC_BITMASK, ofs, node.magic);
+ DIRTY_SPACE(4);
+ ofs += 4;
+ continue;
+ }
+ /* We seem to have a node of sorts. Check the CRC */
+ nodetype = node.nodetype;
+ node.nodetype |= JFFS2_NODE_ACCURATE;
+ hdr_crc = crc32(0, &node, sizeof(node)-4);
+ node.nodetype = nodetype;
+ if (hdr_crc != node.hdr_crc) {
+ noisy_printk(&noise, "jffs2_scan_eraseblock(): Node at 0x%08x {0x%04x, 0x%04x, 0x%08x) has invalid CRC 0x%08x (calculated 0x%08x)\n",
+ ofs, node.magic, node.nodetype, node.totlen, node.hdr_crc, hdr_crc);
+ DIRTY_SPACE(4);
+ ofs += 4;
+ continue;
+ }
+
+ if (ofs + node.totlen > jeb->offset + c->sector_size) {
+ /* Eep. Node goes over the end of the erase block. */
+ printk(KERN_WARNING "Node at 0x%08x with length 0x%08x would run over the end of the erase block\n",
+ ofs, node.totlen);
+ printk(KERN_WARNING "Perhaps the file system was created with the wrong erase size?\n");
+ DIRTY_SPACE(4);
+ ofs += 4;
+ continue;
+ }
+
+ switch(node.nodetype | JFFS2_NODE_ACCURATE) {
+ case JFFS2_NODETYPE_INODE:
+ err = jffs2_scan_inode_node(c, jeb, &ofs);
+ if (err) return err;
+ break;
+
+ case JFFS2_NODETYPE_DIRENT:
+ err = jffs2_scan_dirent_node(c, jeb, &ofs);
+ if (err) return err;
+ break;
+
+ case JFFS2_NODETYPE_CLEANMARKER:
+ if (node.totlen != sizeof(struct jffs2_unknown_node)) {
+ printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x has totlen 0x%x != normal 0x%x\n",
+ ofs, node.totlen, sizeof(struct jffs2_unknown_node));
+ DIRTY_SPACE(PAD(sizeof(struct jffs2_unknown_node)));
+ } else if (jeb->first_node) {
+ printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x, not first node in block (0x%08x)\n", ofs, jeb->offset);
+ DIRTY_SPACE(PAD(sizeof(struct jffs2_unknown_node)));
+ ofs += PAD(sizeof(struct jffs2_unknown_node));
+ continue;
+ } else {
+ struct jffs2_raw_node_ref *marker_ref = jffs2_alloc_raw_node_ref();
+ if (!marker_ref) {
+ printk(KERN_NOTICE "Failed to allocate node ref for clean marker\n");
+ return -ENOMEM;
+ }
+ marker_ref->next_in_ino = NULL;
+ marker_ref->next_phys = NULL;
+ marker_ref->flash_offset = ofs;
+ marker_ref->totlen = sizeof(struct jffs2_unknown_node);
+ jeb->first_node = jeb->last_node = marker_ref;
+
+ USED_SPACE(PAD(sizeof(struct jffs2_unknown_node)));
+ }
+ ofs += PAD(sizeof(struct jffs2_unknown_node));
+ break;
+
+ default:
+ switch (node.nodetype & JFFS2_COMPAT_MASK) {
+ case JFFS2_FEATURE_ROCOMPAT:
+ printk(KERN_NOTICE "Read-only compatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs);
+ c->flags |= JFFS2_SB_FLAG_RO;
+ if (!(OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY))
+ return -EROFS;
+ DIRTY_SPACE(PAD(node.totlen));
+ ofs += PAD(node.totlen);
+ continue;
+
+ case JFFS2_FEATURE_INCOMPAT:
+ printk(KERN_NOTICE "Incompatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs);
+ return -EINVAL;
+
+ case JFFS2_FEATURE_RWCOMPAT_DELETE:
+ printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs);
+ DIRTY_SPACE(PAD(node.totlen));
+ ofs += PAD(node.totlen);
+ break;
+
+ case JFFS2_FEATURE_RWCOMPAT_COPY:
+ printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs);
+ USED_SPACE(PAD(node.totlen));
+ ofs += PAD(node.totlen);
+ break;
+ }
+ }
+ }
+ D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, used 0x%08x\n", jeb->offset,
+ jeb->free_size, jeb->dirty_size, jeb->used_size));
+ return 0;
+}
+
+/* We're pointing at the first empty word on the flash. Scan and account for the whole dirty region */
+static int jffs2_scan_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *startofs, int *noise)
+{
+ __u32 *buf;
+ __u32 scanlen = (jeb->offset + c->sector_size) - *startofs;
+ __u32 curofs = *startofs;
+
+ buf = kmalloc(min((__u32)PAGE_SIZE, scanlen), GFP_KERNEL);
+ if (!buf) {
+ printk(KERN_WARNING "Scan buffer allocation failed\n");
+ return -ENOMEM;
+ }
+ while(scanlen) {
+ ssize_t retlen;
+ int ret, i;
+
+ ret = c->mtd->read(c->mtd, curofs, min((__u32)PAGE_SIZE, scanlen), &retlen, (char *)buf);
+ if(ret) {
+ D1(printk(KERN_WARNING "jffs2_scan_empty(): Read 0x%x bytes at 0x%08x returned %d\n", min((__u32)PAGE_SIZE, scanlen), curofs, ret));
+ kfree(buf);
+ return ret;
+ }
+ if (retlen < 4) {
+ D1(printk(KERN_WARNING "Eep. too few bytes read in scan_empty()\n"));
+ kfree(buf);
+ return -EIO;
+ }
+ for (i=0; i<(retlen / 4); i++) {
+ if (buf[i] != 0xffffffff) {
+ curofs += i*4;
+
+ noisy_printk(noise, "jffs2_scan_empty(): Empty block at 0x%08x ends at 0x%08x (with 0x%08x)! Marking dirty\n", *startofs, curofs, buf[i]);
+ DIRTY_SPACE(curofs - (*startofs));
+ *startofs = curofs;
+ kfree(buf);
+ return 0;
+ }
+ }
+ scanlen -= retlen&~3;
+ curofs += retlen&~3;
+ }
+
+ D1(printk(KERN_DEBUG "Empty flash detected from 0x%08x to 0x%08x\n", *startofs, curofs));
+ kfree(buf);
+ *startofs = curofs;
+ return 0;
+}
+
+static struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, __u32 ino)
+{
+ struct jffs2_inode_cache *ic;
+
+ ic = jffs2_get_ino_cache(c, ino);
+ if (ic)
+ return ic;
+
+ ic = jffs2_alloc_inode_cache();
+ if (!ic) {
+ printk(KERN_NOTICE "jffs2_scan_make_inode_cache(): allocation of inode cache failed\n");
+ return NULL;
+ }
+ memset(ic, 0, sizeof(*ic));
+ ic->scan = kmalloc(sizeof(struct jffs2_scan_info), GFP_KERNEL);
+ if (!ic->scan) {
+ printk(KERN_NOTICE "jffs2_scan_make_inode_cache(): allocation of scan info for inode cache failed\n");
+ jffs2_free_inode_cache(ic);
+ return NULL;
+ }
+ memset(ic->scan, 0, sizeof(*ic->scan));
+ ic->ino = ino;
+ ic->nodes = (void *)ic;
+ jffs2_add_ino_cache(c, ic);
+ if (ino == 1)
+ ic->nlink=1;
+ return ic;
+}
+
+static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs)
+{
+ struct jffs2_raw_node_ref *raw;
+ struct jffs2_full_dnode *fn;
+ struct jffs2_tmp_dnode_info *tn, **tn_list;
+ struct jffs2_inode_cache *ic;
+ struct jffs2_raw_inode ri;
+ __u32 crc;
+ __u16 oldnodetype;
+ int ret;
+ ssize_t retlen;
+
+ D1(printk(KERN_DEBUG "jffs2_scan_inode_node(): Node at 0x%08x\n", *ofs));
+
+ ret = c->mtd->read(c->mtd, *ofs, sizeof(ri), &retlen, (char *)&ri);
+ if (ret) {
+ printk(KERN_NOTICE "jffs2_scan_inode_node(): Read error at 0x%08x: %d\n", *ofs, ret);
+ return ret;
+ }
+ if (retlen != sizeof(ri)) {
+ printk(KERN_NOTICE "Short read: 0x%x bytes at 0x%08x instead of requested %x\n",
+ retlen, *ofs, sizeof(ri));
+ return -EIO;
+ }
+
+ /* We sort of assume that the node was accurate when it was
+ first written to the medium :) */
+ oldnodetype = ri.nodetype;
+ ri.nodetype |= JFFS2_NODE_ACCURATE;
+ crc = crc32(0, &ri, sizeof(ri)-8);
+ ri.nodetype = oldnodetype;
+
+ if(crc != ri.node_crc) {
+ printk(KERN_NOTICE "jffs2_scan_inode_node(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+ *ofs, ri.node_crc, crc);
+ /* FIXME: Why do we believe totlen? */
+ DIRTY_SPACE(4);
+ *ofs += 4;
+ return 0;
+ }
+ /* There was a bug where we wrote hole nodes out with csize/dsize
+ swapped. Deal with it */
+ if (ri.compr == JFFS2_COMPR_ZERO && !ri.dsize && ri.csize) {
+ ri.dsize = ri.csize;
+ ri.csize = 0;
+ }
+
+ if (ri.csize) {
+ /* Check data CRC too */
+ unsigned char *dbuf;
+ __u32 crc;
+
+ dbuf = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL);
+ if (!dbuf) {
+ printk(KERN_NOTICE "jffs2_scan_inode_node(): allocation of temporary data buffer for CRC check failed\n");
+ return -ENOMEM;
+ }
+ ret = c->mtd->read(c->mtd, *ofs+sizeof(ri), ri.csize, &retlen, dbuf);
+ if (ret) {
+ printk(KERN_NOTICE "jffs2_scan_inode_node(): Read error at 0x%08x: %d\n", *ofs+sizeof(ri), ret);
+ kfree(dbuf);
+ return ret;
+ }
+ if (retlen != ri.csize) {
+ printk(KERN_NOTICE "Short read: 0x%x bytes at 0x%08x instead of requested %x\n",
+ retlen, *ofs+ sizeof(ri), ri.csize);
+ kfree(dbuf);
+ return -EIO;
+ }
+ crc = crc32(0, dbuf, ri.csize);
+ kfree(dbuf);
+ if (crc != ri.data_crc) {
+ printk(KERN_NOTICE "jffs2_scan_inode_node(): Data CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+ *ofs, ri.data_crc, crc);
+ DIRTY_SPACE(PAD(ri.totlen));
+ *ofs += PAD(ri.totlen);
+ return 0;
+ }
+ }
+
+ /* Wheee. It worked */
+ raw = jffs2_alloc_raw_node_ref();
+ if (!raw) {
+ printk(KERN_NOTICE "jffs2_scan_inode_node(): allocation of node reference failed\n");
+ return -ENOMEM;
+ }
+ tn = jffs2_alloc_tmp_dnode_info();
+ if (!tn) {
+ jffs2_free_raw_node_ref(raw);
+ return -ENOMEM;
+ }
+ fn = jffs2_alloc_full_dnode();
+ if (!fn) {
+ jffs2_free_tmp_dnode_info(tn);
+ jffs2_free_raw_node_ref(raw);
+ return -ENOMEM;
+ }
+ ic = jffs2_scan_make_ino_cache(c, ri.ino);
+ if (!ic) {
+ jffs2_free_full_dnode(fn);
+ jffs2_free_tmp_dnode_info(tn);
+ jffs2_free_raw_node_ref(raw);
+ return -ENOMEM;
+ }
+
+ /* Build the data structures and file them for later */
+ raw->flash_offset = *ofs;
+ raw->totlen = PAD(ri.totlen);
+ raw->next_phys = NULL;
+ raw->next_in_ino = ic->nodes;
+ ic->nodes = raw;
+ if (!jeb->first_node)
+ jeb->first_node = raw;
+ if (jeb->last_node)
+ jeb->last_node->next_phys = raw;
+ jeb->last_node = raw;
+
+ D1(printk(KERN_DEBUG "Node is ino #%u, version %d. Range 0x%x-0x%x\n",
+ ri.ino, ri.version, ri.offset, ri.offset+ri.dsize));
+
+ pseudo_random += ri.version;
+
+ for (tn_list = &ic->scan->tmpnodes; *tn_list; tn_list = &((*tn_list)->next)) {
+ if ((*tn_list)->version < ri.version)
+ continue;
+ if ((*tn_list)->version > ri.version)
+ break;
+ /* Wheee. We've found another instance of the same version number.
+ We should obsolete one of them.
+ */
+ D1(printk(KERN_DEBUG "Duplicate version %d found in ino #%u. Previous one is at 0x%08x\n", ri.version, ic->ino, (*tn_list)->fn->raw->flash_offset &~3));
+ if (!jeb->used_size) {
+ D1(printk(KERN_DEBUG "No valid nodes yet found in this eraseblock 0x%08x, so obsoleting the new instance at 0x%08x\n",
+ jeb->offset, raw->flash_offset & ~3));
+ ri.nodetype &= ~JFFS2_NODE_ACCURATE;
+ /* Perhaps we could also mark it as such on the medium. Maybe later */
+ }
+ break;
+ }
+
+ if (ri.nodetype & JFFS2_NODE_ACCURATE) {
+ memset(fn,0,sizeof(*fn));
+
+ fn->ofs = ri.offset;
+ fn->size = ri.dsize;
+ fn->frags = 0;
+ fn->raw = raw;
+
+ tn->next = NULL;
+ tn->fn = fn;
+ tn->version = ri.version;
+
+ USED_SPACE(PAD(ri.totlen));
+ jffs2_add_tn_to_list(tn, &ic->scan->tmpnodes);
+ /* Make sure the one we just added is the _last_ in the list
+ with this version number, so the older ones get obsoleted */
+ while (tn->next && tn->next->version == tn->version) {
+
+ D1(printk(KERN_DEBUG "Shifting new node at 0x%08x after other node at 0x%08x for version %d in list\n",
+ fn->raw->flash_offset&~3, tn->next->fn->raw->flash_offset &~3, ri.version));
+
+ if(tn->fn != fn)
+ BUG();
+ tn->fn = tn->next->fn;
+ tn->next->fn = fn;
+ tn = tn->next;
+ }
+ } else {
+ jffs2_free_full_dnode(fn);
+ jffs2_free_tmp_dnode_info(tn);
+ raw->flash_offset |= 1;
+ DIRTY_SPACE(PAD(ri.totlen));
+ }
+ *ofs += PAD(ri.totlen);
+ return 0;
+}
+
+static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs)
+{
+ struct jffs2_raw_node_ref *raw;
+ struct jffs2_full_dirent *fd;
+ struct jffs2_inode_cache *ic;
+ struct jffs2_raw_dirent rd;
+ __u16 oldnodetype;
+ int ret;
+ __u32 crc;
+ ssize_t retlen;
+
+ D1(printk(KERN_DEBUG "jffs2_scan_dirent_node(): Node at 0x%08x\n", *ofs));
+
+ ret = c->mtd->read(c->mtd, *ofs, sizeof(rd), &retlen, (char *)&rd);
+ if (ret) {
+ printk(KERN_NOTICE "jffs2_scan_dirent_node(): Read error at 0x%08x: %d\n", *ofs, ret);
+ return ret;
+ }
+ if (retlen != sizeof(rd)) {
+ printk(KERN_NOTICE "Short read: 0x%x bytes at 0x%08x instead of requested %x\n",
+ retlen, *ofs, sizeof(rd));
+ return -EIO;
+ }
+
+ /* We sort of assume that the node was accurate when it was
+ first written to the medium :) */
+ oldnodetype = rd.nodetype;
+ rd.nodetype |= JFFS2_NODE_ACCURATE;
+ crc = crc32(0, &rd, sizeof(rd)-8);
+ rd.nodetype = oldnodetype;
+
+ if (crc != rd.node_crc) {
+ printk(KERN_NOTICE "jffs2_scan_dirent_node(): Node CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+ *ofs, rd.node_crc, crc);
+ /* FIXME: Why do we believe totlen? */
+ DIRTY_SPACE(4);
+ *ofs += 4;
+ return 0;
+ }
+
+ pseudo_random += rd.version;
+
+ fd = jffs2_alloc_full_dirent(rd.nsize+1);
+ if (!fd) {
+ return -ENOMEM;
+}
+ ret = c->mtd->read(c->mtd, *ofs + sizeof(rd), rd.nsize, &retlen, &fd->name[0]);
+ if (ret) {
+ jffs2_free_full_dirent(fd);
+ printk(KERN_NOTICE "jffs2_scan_dirent_node(): Read error at 0x%08x: %d\n",
+ *ofs + sizeof(rd), ret);
+ return ret;
+ }
+ if (retlen != rd.nsize) {
+ jffs2_free_full_dirent(fd);
+ printk(KERN_NOTICE "Short read: 0x%x bytes at 0x%08x instead of requested %x\n",
+ retlen, *ofs + sizeof(rd), rd.nsize);
+ return -EIO;
+ }
+ crc = crc32(0, fd->name, rd.nsize);
+ if (crc != rd.name_crc) {
+ printk(KERN_NOTICE "jffs2_scan_dirent_node(): Name CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+ *ofs, rd.name_crc, crc);
+ fd->name[rd.nsize]=0;
+ D1(printk(KERN_NOTICE "Name for which CRC failed is (now) '%s', ino #%d\n", fd->name, rd.ino));
+ jffs2_free_full_dirent(fd);
+ /* FIXME: Why do we believe totlen? */
+ DIRTY_SPACE(PAD(rd.totlen));
+ *ofs += PAD(rd.totlen);
+ return 0;
+ }
+ raw = jffs2_alloc_raw_node_ref();
+ if (!raw) {
+ jffs2_free_full_dirent(fd);
+ printk(KERN_NOTICE "jffs2_scan_dirent_node(): allocation of node reference failed\n");
+ return -ENOMEM;
+ }
+ ic = jffs2_scan_make_ino_cache(c, rd.pino);
+ if (!ic) {
+ jffs2_free_full_dirent(fd);
+ jffs2_free_raw_node_ref(raw);
+ return -ENOMEM;
+ }
+
+ raw->totlen = PAD(rd.totlen);
+ raw->flash_offset = *ofs;
+ raw->next_phys = NULL;
+ raw->next_in_ino = ic->nodes;
+ ic->nodes = raw;
+ if (!jeb->first_node)
+ jeb->first_node = raw;
+ if (jeb->last_node)
+ jeb->last_node->next_phys = raw;
+ jeb->last_node = raw;
+
+ if (rd.nodetype & JFFS2_NODE_ACCURATE) {
+ fd->raw = raw;
+ fd->next = NULL;
+ fd->version = rd.version;
+ fd->ino = rd.ino;
+ fd->name[rd.nsize]=0;
+ fd->nhash = full_name_hash(fd->name, rd.nsize);
+ fd->type = rd.type;
+
+ USED_SPACE(PAD(rd.totlen));
+ jffs2_add_fd_to_list(c, fd, &ic->scan->dents);
+ } else {
+ raw->flash_offset |= 1;
+ jffs2_free_full_dirent(fd);
+
+ DIRTY_SPACE(PAD(rd.totlen));
+ }
+ *ofs += PAD(rd.totlen);
+ return 0;
+}
+
+static int count_list(struct list_head *l)
+{
+ uint32_t count = 0;
+ struct list_head *tmp;
+
+ list_for_each(tmp, l) {
+ count++;
+ }
+ return count;
+}
+
+/* Note: This breaks if list_empty(head). I don't care. You
+ might, if you copy this code and use it elsewhere :) */
+static void rotate_list(struct list_head *head, uint32_t count)
+{
+ struct list_head *n = head->next;
+
+ list_del(head);
+ while(count--)
+ n = n->next;
+ list_add(head, n);
+}
+
+static void jffs2_rotate_lists(struct jffs2_sb_info *c)
+{
+ uint32_t x;
+
+ x = count_list(&c->clean_list);
+ if (x)
+ rotate_list((&c->clean_list), pseudo_random % x);
+
+ x = count_list(&c->dirty_list);
+ if (x)
+ rotate_list((&c->dirty_list), pseudo_random % x);
+
+ if (c->nr_erasing_blocks)
+ rotate_list((&c->erase_pending_list), pseudo_random % c->nr_erasing_blocks);
+
+ if (c->nr_free_blocks) /* Not that it should ever be zero */
+ rotate_list((&c->free_list), pseudo_random % c->nr_free_blocks);
+}
diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c
new file mode 100644
index 00000000000000..5f01337ad4fd2b
--- /dev/null
+++ b/fs/jffs2/super.c
@@ -0,0 +1,402 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ *
+ * The original JFFS, from which the design for JFFS2 was derived,
+ * was designed and implemented by Axis Communications AB.
+ *
+ * The contents of this file are subject to the Red Hat eCos Public
+ * License Version 1.1 (the "Licence"); you may not use this file
+ * except in compliance with the Licence. You may obtain a copy of
+ * the Licence at http://www.redhat.com/
+ *
+ * Software distributed under the Licence is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing rights and
+ * limitations under the Licence.
+ *
+ * The Original Code is JFFS2 - Journalling Flash File System, version 2
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in
+ * which case the provisions of the GPL are applicable instead of the
+ * above. If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use your
+ * version of this file under the RHEPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice and
+ * other provisions required by the GPL. If you do not delete the
+ * provisions above, a recipient may use your version of this file
+ * under either the RHEPL or the GPL.
+ *
+ * $Id: super.c,v 1.48.2.3 2002/10/11 09:04:44 dwmw2 Exp $
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/fs.h>
+#include <linux/jffs2.h>
+#include <linux/pagemap.h>
+#include <linux/mtd/mtd.h>
+#include <linux/interrupt.h>
+#include "nodelist.h"
+
+#ifndef MTD_BLOCK_MAJOR
+#define MTD_BLOCK_MAJOR 31
+#endif
+
+extern void jffs2_read_inode (struct inode *);
+void jffs2_put_super (struct super_block *);
+void jffs2_write_super (struct super_block *);
+static int jffs2_statfs (struct super_block *, struct statfs *);
+int jffs2_remount_fs (struct super_block *, int *, char *);
+extern void jffs2_clear_inode (struct inode *);
+
+static struct super_operations jffs2_super_operations =
+{
+ read_inode: jffs2_read_inode,
+// delete_inode: jffs2_delete_inode,
+ put_super: jffs2_put_super,
+ write_super: jffs2_write_super,
+ statfs: jffs2_statfs,
+ remount_fs: jffs2_remount_fs,
+ clear_inode: jffs2_clear_inode
+};
+
+static int jffs2_statfs(struct super_block *sb, struct statfs *buf)
+{
+ struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
+ unsigned long avail;
+
+ buf->f_type = JFFS2_SUPER_MAGIC;
+ buf->f_bsize = 1 << PAGE_SHIFT;
+ buf->f_blocks = c->flash_size >> PAGE_SHIFT;
+ buf->f_files = 0;
+ buf->f_ffree = 0;
+ buf->f_namelen = JFFS2_MAX_NAME_LEN;
+
+ spin_lock_bh(&c->erase_completion_lock);
+
+ avail = c->dirty_size + c->free_size;
+ if (avail > c->sector_size * JFFS2_RESERVED_BLOCKS_WRITE)
+ avail -= c->sector_size * JFFS2_RESERVED_BLOCKS_WRITE;
+ else
+ avail = 0;
+
+ buf->f_bavail = buf->f_bfree = avail >> PAGE_SHIFT;
+
+#if CONFIG_JFFS2_FS_DEBUG > 0
+ printk(KERN_DEBUG "STATFS:\n");
+ printk(KERN_DEBUG "flash_size: %08x\n", c->flash_size);
+ printk(KERN_DEBUG "used_size: %08x\n", c->used_size);
+ printk(KERN_DEBUG "dirty_size: %08x\n", c->dirty_size);
+ printk(KERN_DEBUG "free_size: %08x\n", c->free_size);
+ printk(KERN_DEBUG "erasing_size: %08x\n", c->erasing_size);
+ printk(KERN_DEBUG "bad_size: %08x\n", c->bad_size);
+ printk(KERN_DEBUG "sector_size: %08x\n", c->sector_size);
+
+ if (c->nextblock) {
+ printk(KERN_DEBUG "nextblock: 0x%08x\n", c->nextblock->offset);
+ } else {
+ printk(KERN_DEBUG "nextblock: NULL\n");
+ }
+ if (c->gcblock) {
+ printk(KERN_DEBUG "gcblock: 0x%08x\n", c->gcblock->offset);
+ } else {
+ printk(KERN_DEBUG "gcblock: NULL\n");
+ }
+ if (list_empty(&c->clean_list)) {
+ printk(KERN_DEBUG "clean_list: empty\n");
+ } else {
+ struct list_head *this;
+
+ list_for_each(this, &c->clean_list) {
+ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+ printk(KERN_DEBUG "clean_list: %08x\n", jeb->offset);
+ }
+ }
+ if (list_empty(&c->dirty_list)) {
+ printk(KERN_DEBUG "dirty_list: empty\n");
+ } else {
+ struct list_head *this;
+
+ list_for_each(this, &c->dirty_list) {
+ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+ printk(KERN_DEBUG "dirty_list: %08x\n", jeb->offset);
+ }
+ }
+ if (list_empty(&c->erasing_list)) {
+ printk(KERN_DEBUG "erasing_list: empty\n");
+ } else {
+ struct list_head *this;
+
+ list_for_each(this, &c->erasing_list) {
+ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+ printk(KERN_DEBUG "erasing_list: %08x\n", jeb->offset);
+ }
+ }
+ if (list_empty(&c->erase_pending_list)) {
+ printk(KERN_DEBUG "erase_pending_list: empty\n");
+ } else {
+ struct list_head *this;
+
+ list_for_each(this, &c->erase_pending_list) {
+ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+ printk(KERN_DEBUG "erase_pending_list: %08x\n", jeb->offset);
+ }
+ }
+ if (list_empty(&c->free_list)) {
+ printk(KERN_DEBUG "free_list: empty\n");
+ } else {
+ struct list_head *this;
+
+ list_for_each(this, &c->free_list) {
+ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+ printk(KERN_DEBUG "free_list: %08x\n", jeb->offset);
+ }
+ }
+ if (list_empty(&c->bad_list)) {
+ printk(KERN_DEBUG "bad_list: empty\n");
+ } else {
+ struct list_head *this;
+
+ list_for_each(this, &c->bad_list) {
+ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+ printk(KERN_DEBUG "bad_list: %08x\n", jeb->offset);
+ }
+ }
+ if (list_empty(&c->bad_used_list)) {
+ printk(KERN_DEBUG "bad_used_list: empty\n");
+ } else {
+ struct list_head *this;
+
+ list_for_each(this, &c->bad_used_list) {
+ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+ printk(KERN_DEBUG "bad_used_list: %08x\n", jeb->offset);
+ }
+ }
+#endif /* CONFIG_JFFS2_FS_DEBUG */
+
+ spin_unlock_bh(&c->erase_completion_lock);
+
+
+ return 0;
+}
+
+static struct super_block *jffs2_read_super(struct super_block *sb, void *data, int silent)
+{
+ struct jffs2_sb_info *c;
+ struct inode *root_i;
+ int i;
+
+ D1(printk(KERN_DEBUG "jffs2: read_super for device %s\n", kdevname(sb->s_dev)));
+
+ if (MAJOR(sb->s_dev) != MTD_BLOCK_MAJOR) {
+ if (!silent)
+ printk(KERN_DEBUG "jffs2: attempt to mount non-MTD device %s\n", kdevname(sb->s_dev));
+ return NULL;
+ }
+
+ c = JFFS2_SB_INFO(sb);
+ memset(c, 0, sizeof(*c));
+
+ c->mtd = get_mtd_device(NULL, MINOR(sb->s_dev));
+ if (!c->mtd) {
+ D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", MINOR(sb->s_dev)));
+ return NULL;
+ }
+ c->sector_size = c->mtd->erasesize;
+ c->free_size = c->flash_size = c->mtd->size;
+ c->nr_blocks = c->mtd->size / c->mtd->erasesize;
+ c->blocks = kmalloc(sizeof(struct jffs2_eraseblock) * c->nr_blocks, GFP_KERNEL);
+ if (!c->blocks)
+ goto out_mtd;
+ for (i=0; i<c->nr_blocks; i++) {
+ INIT_LIST_HEAD(&c->blocks[i].list);
+ c->blocks[i].offset = i * c->sector_size;
+ c->blocks[i].free_size = c->sector_size;
+ c->blocks[i].dirty_size = 0;
+ c->blocks[i].used_size = 0;
+ c->blocks[i].first_node = NULL;
+ c->blocks[i].last_node = NULL;
+ }
+
+ spin_lock_init(&c->nodelist_lock);
+ init_MUTEX(&c->alloc_sem);
+ init_waitqueue_head(&c->erase_wait);
+ spin_lock_init(&c->erase_completion_lock);
+ spin_lock_init(&c->inocache_lock);
+
+ INIT_LIST_HEAD(&c->clean_list);
+ INIT_LIST_HEAD(&c->dirty_list);
+ INIT_LIST_HEAD(&c->erasing_list);
+ INIT_LIST_HEAD(&c->erase_pending_list);
+ INIT_LIST_HEAD(&c->erase_complete_list);
+ INIT_LIST_HEAD(&c->free_list);
+ INIT_LIST_HEAD(&c->bad_list);
+ INIT_LIST_HEAD(&c->bad_used_list);
+ c->highest_ino = 1;
+
+ if (jffs2_build_filesystem(c)) {
+ D1(printk(KERN_DEBUG "build_fs failed\n"));
+ goto out_nodes;
+ }
+
+ sb->s_op = &jffs2_super_operations;
+
+ D1(printk(KERN_DEBUG "jffs2_read_super(): Getting root inode\n"));
+ root_i = iget(sb, 1);
+ if (is_bad_inode(root_i)) {
+ D1(printk(KERN_WARNING "get root inode failed\n"));
+ goto out_nodes;
+ }
+
+ D1(printk(KERN_DEBUG "jffs2_read_super(): d_alloc_root()\n"));
+ sb->s_root = d_alloc_root(root_i);
+ if (!sb->s_root)
+ goto out_root_i;
+
+#if LINUX_VERSION_CODE >= 0x20403
+ sb->s_maxbytes = 0xFFFFFFFF;
+#endif
+ sb->s_blocksize = PAGE_CACHE_SIZE;
+ sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+ sb->s_magic = JFFS2_SUPER_MAGIC;
+ if (!(sb->s_flags & MS_RDONLY))
+ jffs2_start_garbage_collect_thread(c);
+ return sb;
+
+ out_root_i:
+ iput(root_i);
+ out_nodes:
+ jffs2_free_ino_caches(c);
+ jffs2_free_raw_node_refs(c);
+ kfree(c->blocks);
+ out_mtd:
+ put_mtd_device(c->mtd);
+ return NULL;
+}
+
+void jffs2_put_super (struct super_block *sb)
+{
+ struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
+
+ D2(printk(KERN_DEBUG "jffs2: jffs2_put_super()\n"));
+
+ if (!(sb->s_flags & MS_RDONLY))
+ jffs2_stop_garbage_collect_thread(c);
+ jffs2_free_ino_caches(c);
+ jffs2_free_raw_node_refs(c);
+ kfree(c->blocks);
+ if (c->mtd->sync)
+ c->mtd->sync(c->mtd);
+ put_mtd_device(c->mtd);
+
+ D1(printk(KERN_DEBUG "jffs2_put_super returning\n"));
+}
+
+int jffs2_remount_fs (struct super_block *sb, int *flags, char *data)
+{
+ struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
+
+ if (c->flags & JFFS2_SB_FLAG_RO && !(sb->s_flags & MS_RDONLY))
+ return -EROFS;
+
+ /* We stop if it was running, then restart if it needs to.
+ This also catches the case where it was stopped and this
+ is just a remount to restart it */
+ if (!(sb->s_flags & MS_RDONLY))
+ jffs2_stop_garbage_collect_thread(c);
+
+ if (!(*flags & MS_RDONLY))
+ jffs2_start_garbage_collect_thread(c);
+
+ sb->s_flags = (sb->s_flags & ~MS_RDONLY)|(*flags & MS_RDONLY);
+
+ return 0;
+}
+
+void jffs2_write_super (struct super_block *sb)
+{
+ struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
+ sb->s_dirt = 0;
+
+ if (sb->s_flags & MS_RDONLY)
+ return;
+
+ jffs2_garbage_collect_trigger(c);
+ jffs2_erase_pending_blocks(c);
+ jffs2_mark_erased_blocks(c);
+}
+
+
+static DECLARE_FSTYPE_DEV(jffs2_fs_type, "jffs2", jffs2_read_super);
+
+static int __init init_jffs2_fs(void)
+{
+ int ret;
+
+ printk(KERN_NOTICE "JFFS2 version 2.1. (C) 2001 Red Hat, Inc., designed by Axis Communications AB.\n");
+
+#ifdef JFFS2_OUT_OF_KERNEL
+ /* sanity checks. Could we do these at compile time? */
+ if (sizeof(struct jffs2_sb_info) > sizeof (((struct super_block *)NULL)->u)) {
+ printk(KERN_ERR "JFFS2 error: struct jffs2_sb_info (%d bytes) doesn't fit in the super_block union (%d bytes)\n",
+ sizeof(struct jffs2_sb_info), sizeof (((struct super_block *)NULL)->u));
+ return -EIO;
+ }
+
+ if (sizeof(struct jffs2_inode_info) > sizeof (((struct inode *)NULL)->u)) {
+ printk(KERN_ERR "JFFS2 error: struct jffs2_inode_info (%d bytes) doesn't fit in the inode union (%d bytes)\n",
+ sizeof(struct jffs2_inode_info), sizeof (((struct inode *)NULL)->u));
+ return -EIO;
+ }
+#endif
+
+ ret = jffs2_zlib_init();
+ if (ret) {
+ printk(KERN_ERR "JFFS2 error: Failed to initialise zlib workspaces\n");
+ goto out;
+ }
+ ret = jffs2_create_slab_caches();
+ if (ret) {
+ printk(KERN_ERR "JFFS2 error: Failed to initialise slab caches\n");
+ goto out_zlib;
+ }
+ ret = register_filesystem(&jffs2_fs_type);
+ if (ret) {
+ printk(KERN_ERR "JFFS2 error: Failed to register filesystem\n");
+ goto out_slab;
+ }
+ return 0;
+
+ out_slab:
+ jffs2_destroy_slab_caches();
+ out_zlib:
+ jffs2_zlib_exit();
+ out:
+ return ret;
+}
+
+static void __exit exit_jffs2_fs(void)
+{
+ jffs2_destroy_slab_caches();
+ jffs2_zlib_exit();
+ unregister_filesystem(&jffs2_fs_type);
+}
+
+module_init(init_jffs2_fs);
+module_exit(exit_jffs2_fs);
+
+MODULE_DESCRIPTION("The Journalling Flash File System, v2");
+MODULE_AUTHOR("Red Hat, Inc.");
+MODULE_LICENSE("GPL"); // Actually dual-licensed, but it doesn't matter for
+ // the sake of this tag. It's Free Software.
diff --git a/fs/jffs2/symlink.c b/fs/jffs2/symlink.c
new file mode 100644
index 00000000000000..e1f7dec49f61c0
--- /dev/null
+++ b/fs/jffs2/symlink.c
@@ -0,0 +1,110 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001, 2002 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ *
+ * The original JFFS, from which the design for JFFS2 was derived,
+ * was designed and implemented by Axis Communications AB.
+ *
+ * The contents of this file are subject to the Red Hat eCos Public
+ * License Version 1.1 (the "Licence"); you may not use this file
+ * except in compliance with the Licence. You may obtain a copy of
+ * the Licence at http://www.redhat.com/
+ *
+ * Software distributed under the Licence is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing rights and
+ * limitations under the Licence.
+ *
+ * The Original Code is JFFS2 - Journalling Flash File System, version 2
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in
+ * which case the provisions of the GPL are applicable instead of the
+ * above. If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use your
+ * version of this file under the RHEPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice and
+ * other provisions required by the GPL. If you do not delete the
+ * provisions above, a recipient may use your version of this file
+ * under either the RHEPL or the GPL.
+ *
+ * $Id: symlink.c,v 1.5.2.1 2002/01/15 10:39:06 dwmw2 Exp $
+ *
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/jffs2.h>
+#include "nodelist.h"
+
+int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen);
+int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd);
+
+struct inode_operations jffs2_symlink_inode_operations =
+{
+ readlink: jffs2_readlink,
+ follow_link: jffs2_follow_link,
+ setattr: jffs2_setattr
+};
+
+static char *jffs2_getlink(struct dentry *dentry)
+{
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode);
+ char *buf;
+ int ret;
+
+ down(&f->sem);
+ if (!f->metadata) {
+ up(&f->sem);
+ printk(KERN_NOTICE "No metadata for symlink inode #%lu\n", dentry->d_inode->i_ino);
+ return ERR_PTR(-EINVAL);
+ }
+ buf = kmalloc(f->metadata->size+1, GFP_USER);
+ if (!buf) {
+ up(&f->sem);
+ return ERR_PTR(-ENOMEM);
+ }
+ buf[f->metadata->size]=0;
+
+ ret = jffs2_read_dnode(JFFS2_SB_INFO(dentry->d_inode->i_sb), f->metadata, buf, 0, f->metadata->size);
+ up(&f->sem);
+ if (ret) {
+ kfree(buf);
+ return ERR_PTR(ret);
+ }
+ return buf;
+
+}
+int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen)
+{
+ unsigned char *kbuf;
+ int ret;
+
+ kbuf = jffs2_getlink(dentry);
+ if (IS_ERR(kbuf))
+ return PTR_ERR(kbuf);
+
+ ret = vfs_readlink(dentry, buffer, buflen, kbuf);
+ kfree(kbuf);
+ return ret;
+}
+
+int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+ unsigned char *buf;
+ int ret;
+
+ buf = jffs2_getlink(dentry);
+
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ ret = vfs_follow_link(nd, buf);
+ kfree(buf);
+ return ret;
+}
diff --git a/fs/jffs2/write.c b/fs/jffs2/write.c
new file mode 100644
index 00000000000000..4bec0e15457e9a
--- /dev/null
+++ b/fs/jffs2/write.c
@@ -0,0 +1,334 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ *
+ * The original JFFS, from which the design for JFFS2 was derived,
+ * was designed and implemented by Axis Communications AB.
+ *
+ * The contents of this file are subject to the Red Hat eCos Public
+ * License Version 1.1 (the "Licence"); you may not use this file
+ * except in compliance with the Licence. You may obtain a copy of
+ * the Licence at http://www.redhat.com/
+ *
+ * Software distributed under the Licence is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing rights and
+ * limitations under the Licence.
+ *
+ * The Original Code is JFFS2 - Journalling Flash File System, version 2
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in
+ * which case the provisions of the GPL are applicable instead of the
+ * above. If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use your
+ * version of this file under the RHEPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice and
+ * other provisions required by the GPL. If you do not delete the
+ * provisions above, a recipient may use your version of this file
+ * under either the RHEPL or the GPL.
+ *
+ * $Id: write.c,v 1.30.2.2 2003/11/02 13:51:18 dwmw2 Exp $
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/jffs2.h>
+#include <linux/mtd/mtd.h>
+#include "nodelist.h"
+#include <linux/crc32.h>
+
+/* jffs2_new_inode: allocate a new inode and inocache, add it to the hash,
+ fill in the raw_inode while you're at it. */
+struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri)
+{
+ struct inode *inode;
+ struct super_block *sb = dir_i->i_sb;
+ struct jffs2_inode_cache *ic;
+ struct jffs2_sb_info *c;
+ struct jffs2_inode_info *f;
+
+ D1(printk(KERN_DEBUG "jffs2_new_inode(): dir_i %ld, mode 0x%x\n", dir_i->i_ino, mode));
+
+ c = JFFS2_SB_INFO(sb);
+ memset(ri, 0, sizeof(*ri));
+
+ ic = jffs2_alloc_inode_cache();
+ if (!ic) {
+ return ERR_PTR(-ENOMEM);
+ }
+ memset(ic, 0, sizeof(*ic));
+
+ inode = new_inode(sb);
+
+ if (!inode) {
+ jffs2_free_inode_cache(ic);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* Alloc jffs2_inode_info when that's split in 2.5 */
+
+ f = JFFS2_INODE_INFO(inode);
+ memset(f, 0, sizeof(*f));
+ init_MUTEX_LOCKED(&f->sem);
+ f->inocache = ic;
+ inode->i_nlink = f->inocache->nlink = 1;
+ f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
+ f->inocache->ino = ri->ino = inode->i_ino = ++c->highest_ino;
+ D1(printk(KERN_DEBUG "jffs2_new_inode(): Assigned ino# %d\n", ri->ino));
+ jffs2_add_ino_cache(c, f->inocache);
+
+ ri->magic = JFFS2_MAGIC_BITMASK;
+ ri->nodetype = JFFS2_NODETYPE_INODE;
+ ri->totlen = PAD(sizeof(*ri));
+ ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4);
+ ri->mode = mode;
+ f->highest_version = ri->version = 1;
+ ri->uid = current->fsuid;
+ if (dir_i->i_mode & S_ISGID) {
+ ri->gid = dir_i->i_gid;
+ if (S_ISDIR(mode))
+ ri->mode |= S_ISGID;
+ } else {
+ ri->gid = current->fsgid;
+ }
+ inode->i_mode = ri->mode;
+ inode->i_gid = ri->gid;
+ inode->i_uid = ri->uid;
+ inode->i_atime = inode->i_ctime = inode->i_mtime =
+ ri->atime = ri->mtime = ri->ctime = CURRENT_TIME;
+ inode->i_blksize = PAGE_SIZE;
+ inode->i_blocks = 0;
+ inode->i_size = 0;
+
+ insert_inode_hash(inode);
+
+ return inode;
+}
+
+/* This ought to be in core MTD code. All registered MTD devices
+ without writev should have this put in place. Bug the MTD
+ maintainer */
+static int mtd_fake_writev(struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen)
+{
+ unsigned long i;
+ size_t totlen = 0, thislen;
+ int ret = 0;
+
+ for (i=0; i<count; i++) {
+ ret = mtd->write(mtd, to, vecs[i].iov_len, &thislen, vecs[i].iov_base);
+ totlen += thislen;
+ if (ret || thislen != vecs[i].iov_len)
+ break;
+ to += vecs[i].iov_len;
+ }
+ if (retlen)
+ *retlen = totlen;
+ return ret;
+}
+
+
+static inline int mtd_writev(struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen)
+{
+ if (mtd->writev)
+ return mtd->writev(mtd,vecs,count,to,retlen);
+ else
+ return mtd_fake_writev(mtd, vecs, count, to, retlen);
+}
+
+static void writecheck(struct mtd_info *mtd, __u32 ofs)
+{
+ unsigned char buf[16];
+ ssize_t retlen;
+ int ret, i;
+
+ ret = mtd->read(mtd, ofs, 16, &retlen, buf);
+ if (ret && retlen != 16) {
+ D1(printk(KERN_DEBUG "read failed or short in writecheck(). ret %d, retlen %d\n", ret, retlen));
+ return;
+ }
+ ret = 0;
+ for (i=0; i<16; i++) {
+ if (buf[i] != 0xff)
+ ret = 1;
+ }
+ if (ret) {
+ printk(KERN_WARNING "ARGH. About to write node to 0x%08x on flash, but there's data already there:\n", ofs);
+ printk(KERN_WARNING "0x%08x: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ ofs,
+ buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7],
+ buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]);
+ }
+}
+
+
+
+
+/* jffs2_write_dnode - given a raw_inode, allocate a full_dnode for it,
+ write it to the flash, link it into the existing inode/fragment list */
+
+struct jffs2_full_dnode *jffs2_write_dnode(struct inode *inode, struct jffs2_raw_inode *ri, const unsigned char *data, __u32 datalen, __u32 flash_ofs, __u32 *writelen)
+
+{
+ struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ struct jffs2_raw_node_ref *raw;
+ struct jffs2_full_dnode *fn;
+ ssize_t retlen;
+ struct iovec vecs[2];
+ int ret;
+
+ D1(if(ri->hdr_crc != crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)) {
+ printk(KERN_CRIT "Eep. CRC not correct in jffs2_write_dnode()\n");
+ BUG();
+ }
+ );
+ vecs[0].iov_base = ri;
+ vecs[0].iov_len = sizeof(*ri);
+ vecs[1].iov_base = (unsigned char *)data;
+ vecs[1].iov_len = datalen;
+
+ writecheck(c->mtd, flash_ofs);
+
+ if (ri->totlen != sizeof(*ri) + datalen) {
+ printk(KERN_WARNING "jffs2_write_dnode: ri->totlen (0x%08x) != sizeof(*ri) (0x%08x) + datalen (0x%08x)\n", ri->totlen, sizeof(*ri), datalen);
+ }
+ raw = jffs2_alloc_raw_node_ref();
+ if (!raw)
+ return ERR_PTR(-ENOMEM);
+
+ fn = jffs2_alloc_full_dnode();
+ if (!fn) {
+ jffs2_free_raw_node_ref(raw);
+ return ERR_PTR(-ENOMEM);
+ }
+ raw->flash_offset = flash_ofs;
+ raw->totlen = PAD(ri->totlen);
+ raw->next_phys = NULL;
+
+ fn->ofs = ri->offset;
+ fn->size = ri->dsize;
+ fn->frags = 0;
+ fn->raw = raw;
+
+ ret = mtd_writev(c->mtd, vecs, 2, flash_ofs, &retlen);
+ if (ret || (retlen != sizeof(*ri) + datalen)) {
+ printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %d\n",
+ sizeof(*ri)+datalen, flash_ofs, ret, retlen);
+ /* Mark the space as dirtied */
+ if (retlen) {
+ /* Doesn't belong to any inode */
+ raw->next_in_ino = NULL;
+
+ /* Don't change raw->size to match retlen. We may have
+ written the node header already, and only the data will
+ seem corrupted, in which case the scan would skip over
+ any node we write before the original intended end of
+ this node */
+ jffs2_add_physical_node_ref(c, raw, sizeof(*ri)+datalen, 1);
+ jffs2_mark_node_obsolete(c, raw);
+ } else {
+ printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset);
+ jffs2_free_raw_node_ref(raw);
+ }
+
+ /* Release the full_dnode which is now useless, and return */
+ jffs2_free_full_dnode(fn);
+ if (writelen)
+ *writelen = retlen;
+ return ERR_PTR(ret?ret:-EIO);
+ }
+ /* Mark the space used */
+ jffs2_add_physical_node_ref(c, raw, retlen, 0);
+
+ /* Link into per-inode list */
+ raw->next_in_ino = f->inocache->nodes;
+ f->inocache->nodes = raw;
+
+ D1(printk(KERN_DEBUG "jffs2_write_dnode wrote node at 0x%08x with dsize 0x%x, csize 0x%x, node_crc 0x%08x, data_crc 0x%08x, totlen 0x%08x\n", flash_ofs, ri->dsize, ri->csize, ri->node_crc, ri->data_crc, ri->totlen));
+ if (writelen)
+ *writelen = retlen;
+
+ f->inocache->nodes = raw;
+ return fn;
+}
+
+struct jffs2_full_dirent *jffs2_write_dirent(struct inode *inode, struct jffs2_raw_dirent *rd, const unsigned char *name, __u32 namelen, __u32 flash_ofs, __u32 *writelen)
+{
+ struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
+ struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+ struct jffs2_raw_node_ref *raw;
+ struct jffs2_full_dirent *fd;
+ ssize_t retlen;
+ struct iovec vecs[2];
+ int ret;
+
+ D1(printk(KERN_DEBUG "jffs2_write_dirent(ino #%u, name at *0x%p \"%s\"->ino #%u, name_crc 0x%08x)\n", rd->pino, name, name, rd->ino, rd->name_crc));
+ writecheck(c->mtd, flash_ofs);
+
+ D1(if(rd->hdr_crc != crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)) {
+ printk(KERN_CRIT "Eep. CRC not correct in jffs2_write_dirent()\n");
+ BUG();
+ }
+ );
+
+ vecs[0].iov_base = rd;
+ vecs[0].iov_len = sizeof(*rd);
+ vecs[1].iov_base = (unsigned char *)name;
+ vecs[1].iov_len = namelen;
+
+ raw = jffs2_alloc_raw_node_ref();
+
+ if (!raw)
+ return ERR_PTR(-ENOMEM);
+
+ fd = jffs2_alloc_full_dirent(namelen+1);
+ if (!fd) {
+ jffs2_free_raw_node_ref(raw);
+ return ERR_PTR(-ENOMEM);
+ }
+ raw->flash_offset = flash_ofs;
+ raw->totlen = PAD(rd->totlen);
+ raw->next_in_ino = f->inocache->nodes;
+ f->inocache->nodes = raw;
+ raw->next_phys = NULL;
+
+ fd->version = rd->version;
+ fd->ino = rd->ino;
+ fd->nhash = full_name_hash(name, strlen(name));
+ fd->type = rd->type;
+ memcpy(fd->name, name, namelen);
+ fd->name[namelen]=0;
+ fd->raw = raw;
+
+ ret = mtd_writev(c->mtd, vecs, 2, flash_ofs, &retlen);
+ if (ret || (retlen != sizeof(*rd) + namelen)) {
+ printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %d\n",
+ sizeof(*rd)+namelen, flash_ofs, ret, retlen);
+ /* Mark the space as dirtied */
+ if (retlen) {
+ jffs2_add_physical_node_ref(c, raw, sizeof(*rd)+namelen, 1);
+ jffs2_mark_node_obsolete(c, raw);
+ } else {
+ printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset);
+ jffs2_free_raw_node_ref(raw);
+ }
+
+ /* Release the full_dnode which is now useless, and return */
+ jffs2_free_full_dirent(fd);
+ if (writelen)
+ *writelen = retlen;
+ return ERR_PTR(ret?ret:-EIO);
+ }
+ /* Mark the space used */
+ jffs2_add_physical_node_ref(c, raw, retlen, 0);
+ if (writelen)
+ *writelen = retlen;
+
+ f->inocache->nodes = raw;
+ return fd;
+}
diff --git a/fs/jfs/Makefile b/fs/jfs/Makefile
new file mode 100644
index 00000000000000..af4d80e808d6b8
--- /dev/null
+++ b/fs/jfs/Makefile
@@ -0,0 +1,15 @@
+#
+# Makefile for the Linux JFS filesystem routines.
+#
+
+O_TARGET := jfs.o
+obj-y := super.o file.o inode.o namei.o jfs_mount.o jfs_umount.o \
+ jfs_xtree.o jfs_imap.o jfs_debug.o jfs_dmap.o \
+ jfs_unicode.o jfs_dtree.o jfs_inode.o \
+ jfs_extent.o symlink.o jfs_metapage.o \
+ jfs_logmgr.o jfs_txnmgr.o jfs_uniupr.o resize.o xattr.o
+obj-m := $(O_TARGET)
+
+EXTRA_CFLAGS += -D_JFS_4K
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/jfs/file.c b/fs/jfs/file.c
new file mode 100644
index 00000000000000..23a194f42aab25
--- /dev/null
+++ b/fs/jfs/file.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2000-2002
+ * Portions Copyright (c) Christoph Hellwig, 2001-2002
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/fs.h>
+#include "jfs_incore.h"
+#include "jfs_dmap.h"
+#include "jfs_txnmgr.h"
+#include "jfs_xattr.h"
+#include "jfs_debug.h"
+
+
+extern int jfs_commit_inode(struct inode *, int);
+extern void jfs_truncate(struct inode *);
+
+int jfs_fsync(struct file *file, struct dentry *dentry, int datasync)
+{
+ struct inode *inode = dentry->d_inode;
+ int rc = 0;
+
+ /* No need to resync the data at commit time, unless this flag gets
+ * set again */
+ clear_cflag(COMMIT_Syncdata, inode);
+
+ rc = fsync_inode_data_buffers(inode);
+
+ if (!(inode->i_state & I_DIRTY) ||
+ (datasync && !(inode->i_state & I_DIRTY_DATASYNC))) {
+ /* Make sure committed changes hit the disk */
+ jfs_flush_journal(JFS_SBI(inode->i_sb)->log, 1);
+ return rc;
+ }
+
+ rc |= jfs_commit_inode(inode, 1);
+
+ return rc ? -EIO : 0;
+}
+
+static int jfs_open(struct inode *inode, struct file *file)
+{
+ int rc;
+
+ if ((rc = generic_file_open(inode, file)))
+ return rc;
+
+ /*
+ * We attempt to allow only one "active" file open per aggregate
+ * group. Otherwise, appending to files in parallel can cause
+ * fragmentation within the files.
+ *
+ * If the file is empty, it was probably just created and going
+ * to be written to. If it has a size, we'll hold off until the
+ * file is actually grown.
+ */
+ if (S_ISREG(inode->i_mode) && file->f_mode & FMODE_WRITE &&
+ (inode->i_size == 0)) {
+ struct jfs_inode_info *ji = JFS_IP(inode);
+ spin_lock_irq(&ji->ag_lock);
+ if (ji->active_ag == -1) {
+ ji->active_ag = ji->agno;
+ atomic_inc(
+ &JFS_SBI(inode->i_sb)->bmap->db_active[ji->agno]);
+ }
+ spin_unlock_irq(&ji->ag_lock);
+ }
+
+ return 0;
+}
+static int jfs_release(struct inode *inode, struct file *file)
+{
+ struct jfs_inode_info *ji = JFS_IP(inode);
+
+ spin_lock_irq(&ji->ag_lock);
+ if (ji->active_ag != -1) {
+ struct bmap *bmap = JFS_SBI(inode->i_sb)->bmap;
+ atomic_dec(&bmap->db_active[ji->active_ag]);
+ ji->active_ag = -1;
+ }
+ spin_unlock_irq(&ji->ag_lock);
+
+ return 0;
+}
+
+struct inode_operations jfs_file_inode_operations = {
+ .truncate = jfs_truncate,
+ .setxattr = jfs_setxattr,
+ .getxattr = jfs_getxattr,
+ .listxattr = jfs_listxattr,
+ .removexattr = jfs_removexattr,
+};
+
+struct file_operations jfs_file_operations = {
+ .open = jfs_open,
+ .llseek = generic_file_llseek,
+ .write = generic_file_write,
+ .read = generic_file_read,
+ .mmap = generic_file_mmap,
+ .fsync = jfs_fsync,
+ .release = jfs_release,
+};
diff --git a/fs/jfs/inode.c b/fs/jfs/inode.c
new file mode 100644
index 00000000000000..871af3c78f5fae
--- /dev/null
+++ b/fs/jfs/inode.c
@@ -0,0 +1,401 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2000-2002
+ * Portions Copyright (c) Christoph Hellwig, 2001-2002
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/fs.h>
+//#include <linux/locks.h>
+#include "jfs_incore.h"
+#include "jfs_filsys.h"
+#include "jfs_dmap.h"
+#include "jfs_imap.h"
+#include "jfs_extent.h"
+#include "jfs_unicode.h"
+#include "jfs_debug.h"
+
+
+extern struct inode_operations jfs_dir_inode_operations;
+extern struct inode_operations jfs_file_inode_operations;
+extern struct inode_operations jfs_symlink_inode_operations;
+extern struct file_operations jfs_dir_operations;
+extern struct file_operations jfs_file_operations;
+struct address_space_operations jfs_aops;
+extern int freeZeroLink(struct inode *);
+
+void jfs_clear_inode(struct inode *inode)
+{
+ struct jfs_inode_info *ji = JFS_IP(inode);
+
+ if (is_bad_inode(inode))
+ /*
+ * We free the fs-dependent structure before making the
+ * inode bad
+ */
+ return;
+
+ jfs_info("jfs_clear_inode called ip = 0x%p", inode);
+
+ spin_lock_irq(&ji->ag_lock);
+ if (ji->active_ag != -1) {
+ struct bmap *bmap = JFS_SBI(inode->i_sb)->bmap;
+ atomic_dec(&bmap->db_active[ji->active_ag]);
+ ji->active_ag = -1;
+ }
+ spin_unlock_irq(&ji->ag_lock);
+
+ ASSERT(list_empty(&ji->anon_inode_list));
+
+ if (ji->atlhead) {
+ jfs_err("jfs_clear_inode: inode %p has anonymous tlocks",
+ inode);
+ jfs_err("i_state = 0x%lx, cflag = 0x%lx", inode->i_state,
+ ji->cflag);
+ }
+
+ free_jfs_inode(inode);
+}
+
+void jfs_read_inode(struct inode *inode)
+{
+ int rc;
+
+ rc = alloc_jfs_inode(inode);
+ if (rc) {
+ jfs_warn("In jfs_read_inode, alloc_jfs_inode failed");
+ goto bad_inode;
+ }
+ jfs_info("In jfs_read_inode, inode = 0x%p", inode);
+
+ if (diRead(inode))
+ goto bad_inode_free;
+
+ if (S_ISREG(inode->i_mode)) {
+ inode->i_op = &jfs_file_inode_operations;
+ inode->i_fop = &jfs_file_operations;
+ inode->i_mapping->a_ops = &jfs_aops;
+ } else if (S_ISDIR(inode->i_mode)) {
+ inode->i_op = &jfs_dir_inode_operations;
+ inode->i_fop = &jfs_dir_operations;
+ } else if (S_ISLNK(inode->i_mode)) {
+ if (inode->i_size >= IDATASIZE) {
+ inode->i_op = &page_symlink_inode_operations;
+ inode->i_mapping->a_ops = &jfs_aops;
+ } else
+ inode->i_op = &jfs_symlink_inode_operations;
+ } else {
+ inode->i_op = &jfs_file_inode_operations;
+ init_special_inode(inode, inode->i_mode,
+ kdev_t_to_nr(inode->i_rdev));
+ }
+
+ return;
+
+ bad_inode_free:
+ free_jfs_inode(inode);
+ bad_inode:
+ make_bad_inode(inode);
+}
+
+/* This define is from fs/open.c */
+#define special_file(m) (S_ISCHR(m)||S_ISBLK(m)||S_ISFIFO(m)||S_ISSOCK(m))
+
+/*
+ * Workhorse of both fsync & write_inode
+ */
+int jfs_commit_inode(struct inode *inode, int wait)
+{
+ int rc = 0;
+ tid_t tid;
+ static int noisy = 5;
+
+ jfs_info("In jfs_commit_inode, inode = 0x%p", inode);
+
+ /*
+ * Don't commit if inode has been committed since last being
+ * marked dirty, or if it has been deleted.
+ */
+ if (inode->i_nlink == 0 || !test_cflag(COMMIT_Dirty, inode))
+ return 0;
+
+ if (isReadOnly(inode)) {
+ /* kernel allows writes to devices on read-only
+ * partitions and may think inode is dirty
+ */
+ if (!special_file(inode->i_mode) && noisy) {
+ jfs_err("jfs_commit_inode(0x%p) called on "
+ "read-only volume", inode);
+ jfs_err("Is remount racy?");
+ noisy--;
+ }
+ return 0;
+ }
+
+ tid = txBegin(inode->i_sb, COMMIT_INODE);
+ down(&JFS_IP(inode)->commit_sem);
+
+ /*
+ * Retest inode state after taking commit_sem
+ */
+ if (inode->i_nlink && test_cflag(COMMIT_Dirty, inode))
+ rc = txCommit(tid, 1, &inode, wait ? COMMIT_SYNC : 0);
+
+ txEnd(tid);
+ up(&JFS_IP(inode)->commit_sem);
+ return rc;
+}
+
+void jfs_write_inode(struct inode *inode, int wait)
+{
+ if (test_cflag(COMMIT_Nolink, inode))
+ return;
+ /*
+ * If COMMIT_DIRTY is not set, the inode isn't really dirty.
+ * It has been committed since the last change, but was still
+ * on the dirty inode list.
+ */
+ if (!test_cflag(COMMIT_Dirty, inode)) {
+ /* Make sure committed changes hit the disk */
+ jfs_flush_journal(JFS_SBI(inode->i_sb)->log, wait);
+ return;
+ }
+
+ if (jfs_commit_inode(inode, wait)) {
+ jfs_err("jfs_write_inode: jfs_commit_inode failed!");
+ }
+}
+
+void jfs_delete_inode(struct inode *inode)
+{
+ jfs_info("In jfs_delete_inode, inode = 0x%p", inode);
+
+ if (test_cflag(COMMIT_Freewmap, inode))
+ freeZeroLink(inode);
+
+ diFree(inode);
+
+ clear_inode(inode);
+}
+
+void jfs_dirty_inode(struct inode *inode)
+{
+ static int noisy = 5;
+
+ if (isReadOnly(inode)) {
+ if (!special_file(inode->i_mode) && noisy) {
+ /* kernel allows writes to devices on read-only
+ * partitions and may try to mark inode dirty
+ */
+ jfs_err("jfs_dirty_inode called on read-only volume");
+ jfs_err("Is remount racy?");
+ noisy--;
+ }
+ return;
+ }
+
+ set_cflag(COMMIT_Dirty, inode);
+}
+
+static int jfs_get_block(struct inode *ip, long lblock,
+ struct buffer_head *bh_result, int create)
+{
+ s64 lblock64 = lblock;
+ int rc = 0;
+ int take_locks;
+ xad_t xad;
+ s64 xaddr;
+ int xflag;
+ s32 xlen;
+
+ /*
+ * If this is a special inode (imap, dmap)
+ * the lock should already be taken
+ */
+ take_locks = (JFS_IP(ip)->fileset != AGGREGATE_I);
+
+ /*
+ * Take appropriate lock on inode
+ */
+ if (take_locks) {
+ if (create)
+ IWRITE_LOCK(ip);
+ else
+ IREAD_LOCK(ip);
+ }
+
+ if (((lblock64 << ip->i_sb->s_blocksize_bits) < ip->i_size) &&
+ (xtLookup(ip, lblock64, 1, &xflag, &xaddr, &xlen, 0) == 0) &&
+ xlen) {
+ if (xflag & XAD_NOTRECORDED) {
+ if (!create)
+ /*
+ * Allocated but not recorded, read treats
+ * this as a hole
+ */
+ goto unlock;
+#ifdef _JFS_4K
+ XADoffset(&xad, lblock64);
+ XADlength(&xad, xlen);
+ XADaddress(&xad, xaddr);
+#else /* _JFS_4K */
+ /*
+ * As long as block size = 4K, this isn't a problem.
+ * We should mark the whole page not ABNR, but how
+ * will we know to mark the other blocks BH_New?
+ */
+ BUG();
+#endif /* _JFS_4K */
+ rc = extRecord(ip, &xad);
+ if (rc)
+ goto unlock;
+ bh_result->b_state |= (1UL << BH_New);
+ }
+
+ bh_result->b_dev = ip->i_dev;
+ bh_result->b_blocknr = xaddr;
+ bh_result->b_state |= (1UL << BH_Mapped);
+ goto unlock;
+ }
+ if (!create)
+ goto unlock;
+
+ /*
+ * Allocate a new block
+ */
+#ifdef _JFS_4K
+ if ((rc = extHint(ip, lblock64 << ip->i_sb->s_blocksize_bits, &xad)))
+ goto unlock;
+ rc = extAlloc(ip, 1, lblock64, &xad, FALSE);
+ if (rc)
+ goto unlock;
+
+ bh_result->b_dev = ip->i_dev;
+ bh_result->b_blocknr = addressXAD(&xad);
+ bh_result->b_state |= ((1UL << BH_Mapped) | (1UL << BH_New));
+
+#else /* _JFS_4K */
+ /*
+ * We need to do whatever it takes to keep all but the last buffers
+ * in 4K pages - see jfs_write.c
+ */
+ BUG();
+#endif /* _JFS_4K */
+
+ unlock:
+ /*
+ * Release lock on inode
+ */
+ if (take_locks) {
+ if (create)
+ IWRITE_UNLOCK(ip);
+ else
+ IREAD_UNLOCK(ip);
+ }
+ return rc;
+}
+
+static int jfs_writepage(struct page *page)
+{
+ return block_write_full_page(page, jfs_get_block);
+}
+
+static int jfs_readpage(struct file *file, struct page *page)
+{
+ return block_read_full_page(page, jfs_get_block);
+}
+
+static int jfs_prepare_write(struct file *file,
+ struct page *page, unsigned from, unsigned to)
+{
+ return block_prepare_write(page, from, to, jfs_get_block);
+}
+
+static int jfs_bmap(struct address_space *mapping, long block)
+{
+ return generic_block_bmap(mapping, block, jfs_get_block);
+}
+
+static int jfs_direct_IO(int rw, struct inode *inode, struct kiobuf *iobuf,
+ unsigned long blocknr, int blocksize)
+{
+ return generic_direct_IO(rw, inode, iobuf, blocknr,
+ blocksize, jfs_get_block);
+}
+
+struct address_space_operations jfs_aops = {
+ .readpage = jfs_readpage,
+ .writepage = jfs_writepage,
+ .sync_page = block_sync_page,
+ .prepare_write = jfs_prepare_write,
+ .commit_write = generic_commit_write,
+ .bmap = jfs_bmap,
+ .direct_IO = jfs_direct_IO,
+};
+
+/*
+ * Guts of jfs_truncate. Called with locks already held. Can be called
+ * with directory for truncating directory index table.
+ */
+void jfs_truncate_nolock(struct inode *ip, loff_t length)
+{
+ loff_t newsize;
+ tid_t tid;
+
+ ASSERT(length >= 0);
+
+ if (test_cflag(COMMIT_Nolink, ip)) {
+ xtTruncate(0, ip, length, COMMIT_WMAP);
+ return;
+ }
+
+ do {
+ tid = txBegin(ip->i_sb, 0);
+
+ /*
+ * The commit_sem cannot be taken before txBegin.
+ * txBegin may block and there is a chance the inode
+ * could be marked dirty and need to be committed
+ * before txBegin unblocks
+ */
+ down(&JFS_IP(ip)->commit_sem);
+
+ newsize = xtTruncate(tid, ip, length,
+ COMMIT_TRUNCATE | COMMIT_PWMAP);
+ if (newsize < 0) {
+ txEnd(tid);
+ up(&JFS_IP(ip)->commit_sem);
+ break;
+ }
+
+ ip->i_mtime = ip->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(ip);
+
+ txCommit(tid, 1, &ip, 0);
+ txEnd(tid);
+ up(&JFS_IP(ip)->commit_sem);
+ } while (newsize > length); /* Truncate isn't always atomic */
+}
+
+void jfs_truncate(struct inode *ip)
+{
+ jfs_info("jfs_truncate: size = 0x%lx", (ulong) ip->i_size);
+
+ block_truncate_page(ip->i_mapping, ip->i_size, jfs_get_block);
+
+ IWRITE_LOCK(ip);
+ jfs_truncate_nolock(ip, ip->i_size);
+ IWRITE_UNLOCK(ip);
+}
diff --git a/fs/jfs/jfs_btree.h b/fs/jfs/jfs_btree.h
new file mode 100644
index 00000000000000..7f3e9ac454fff1
--- /dev/null
+++ b/fs/jfs/jfs_btree.h
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2000-2004
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _H_JFS_BTREE
+#define _H_JFS_BTREE
+
+/*
+ * jfs_btree.h: B+-tree
+ *
+ * JFS B+-tree (dtree and xtree) common definitions
+ */
+
+/*
+ * basic btree page - btpage
+ *
+struct btpage {
+ s64 next; right sibling bn
+ s64 prev; left sibling bn
+
+ u8 flag;
+ u8 rsrvd[7]; type specific
+ s64 self; self address
+
+ u8 entry[4064];
+}; */
+
+/* btpaget_t flag */
+#define BT_TYPE 0x07 /* B+-tree index */
+#define BT_ROOT 0x01 /* root page */
+#define BT_LEAF 0x02 /* leaf page */
+#define BT_INTERNAL 0x04 /* internal page */
+#define BT_RIGHTMOST 0x10 /* rightmost page */
+#define BT_LEFTMOST 0x20 /* leftmost page */
+#define BT_SWAPPED 0x80 /* used by fsck for endian swapping */
+
+/* btorder (in inode) */
+#define BT_RANDOM 0x0000
+#define BT_SEQUENTIAL 0x0001
+#define BT_LOOKUP 0x0010
+#define BT_INSERT 0x0020
+#define BT_DELETE 0x0040
+
+/*
+ * btree page buffer cache access
+ */
+#define BT_IS_ROOT(MP) (((MP)->xflag & COMMIT_PAGE) == 0)
+
+/* get page from buffer page */
+#define BT_PAGE(IP, MP, TYPE, ROOT)\
+ (BT_IS_ROOT(MP) ? (TYPE *)&JFS_IP(IP)->ROOT : (TYPE *)(MP)->data)
+
+/* get the page buffer and the page for specified block address */
+#define BT_GETPAGE(IP, BN, MP, TYPE, SIZE, P, RC, ROOT)\
+{\
+ if ((BN) == 0)\
+ {\
+ MP = (struct metapage *)&JFS_IP(IP)->bxflag;\
+ P = (TYPE *)&JFS_IP(IP)->ROOT;\
+ RC = 0;\
+ }\
+ else\
+ {\
+ MP = read_metapage((IP), BN, SIZE, 1);\
+ if (MP) {\
+ RC = 0;\
+ P = (MP)->data;\
+ } else {\
+ P = NULL;\
+ jfs_err("bread failed!");\
+ RC = -EIO;\
+ }\
+ }\
+}
+
+#define BT_MARK_DIRTY(MP, IP)\
+{\
+ if (BT_IS_ROOT(MP))\
+ mark_inode_dirty(IP);\
+ else\
+ mark_metapage_dirty(MP);\
+}
+
+/* put the page buffer */
+#define BT_PUTPAGE(MP)\
+{\
+ if (! BT_IS_ROOT(MP)) \
+ release_metapage(MP); \
+}
+
+
+/*
+ * btree traversal stack
+ *
+ * record the path traversed during the search;
+ * top frame record the leaf page/entry selected.
+ */
+struct btframe { /* stack frame */
+ s64 bn; /* 8: */
+ s16 index; /* 2: */
+ s16 lastindex; /* 2: unused */
+ struct metapage *mp; /* 4/8: */
+}; /* (16/24) */
+
+struct btstack {
+ struct btframe *top;
+ int nsplit;
+ struct btframe stack[MAXTREEHEIGHT];
+};
+
+#define BT_CLR(btstack)\
+ (btstack)->top = (btstack)->stack
+
+#define BT_STACK_FULL(btstack)\
+ ( (btstack)->top == &((btstack)->stack[MAXTREEHEIGHT-1]))
+
+#define BT_PUSH(BTSTACK, BN, INDEX)\
+{\
+ assert(!BT_STACK_FULL(BTSTACK));\
+ (BTSTACK)->top->bn = BN;\
+ (BTSTACK)->top->index = INDEX;\
+ ++(BTSTACK)->top;\
+}
+
+#define BT_POP(btstack)\
+ ( (btstack)->top == (btstack)->stack ? NULL : --(btstack)->top )
+
+#define BT_STACK(btstack)\
+ ( (btstack)->top == (btstack)->stack ? NULL : (btstack)->top )
+
+static inline void BT_STACK_DUMP(struct btstack *btstack)
+{
+ int i;
+ printk("btstack dump:\n");
+ for (i = 0; i < MAXTREEHEIGHT; i++)
+ printk(KERN_ERR "bn = %Lx, index = %d\n",
+ (long long)btstack->stack[i].bn,
+ btstack->stack[i].index);
+}
+
+/* retrieve search results */
+#define BT_GETSEARCH(IP, LEAF, BN, MP, TYPE, P, INDEX, ROOT)\
+{\
+ BN = (LEAF)->bn;\
+ MP = (LEAF)->mp;\
+ if (BN)\
+ P = (TYPE *)MP->data;\
+ else\
+ P = (TYPE *)&JFS_IP(IP)->ROOT;\
+ INDEX = (LEAF)->index;\
+}
+
+/* put the page buffer of search */
+#define BT_PUTSEARCH(BTSTACK)\
+{\
+ if (! BT_IS_ROOT((BTSTACK)->top->mp))\
+ release_metapage((BTSTACK)->top->mp);\
+}
+#endif /* _H_JFS_BTREE */
diff --git a/fs/jfs/jfs_debug.c b/fs/jfs/jfs_debug.c
new file mode 100644
index 00000000000000..83e6bc03da8add
--- /dev/null
+++ b/fs/jfs/jfs_debug.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2000-2004
+ * Portions Copyright (C) Christoph Hellwig, 2001-2002
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/fs.h>
+#include <linux/ctype.h>
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <asm/uaccess.h>
+#include "jfs_incore.h"
+#include "jfs_filsys.h"
+#include "jfs_debug.h"
+
+#ifdef CONFIG_JFS_DEBUG
+void dump_mem(char *label, void *data, int length)
+{
+ int i, j;
+ int *intptr = data;
+ char *charptr = data;
+ char buf[10], line[80];
+
+ printk("%s: dump of %d bytes of data at 0x%p\n\n", label, length,
+ data);
+ for (i = 0; i < length; i += 16) {
+ line[0] = 0;
+ for (j = 0; (j < 4) && (i + j * 4 < length); j++) {
+ sprintf(buf, " %08x", intptr[i / 4 + j]);
+ strcat(line, buf);
+ }
+ buf[0] = ' ';
+ buf[2] = 0;
+ for (j = 0; (j < 16) && (i + j < length); j++) {
+ buf[1] =
+ isprint(charptr[i + j]) ? charptr[i + j] : '.';
+ strcat(line, buf);
+ }
+ printk("%s\n", line);
+ }
+}
+#endif
+
+#ifdef PROC_FS_JFS /* see jfs_debug.h */
+
+static struct proc_dir_entry *base;
+#ifdef CONFIG_JFS_DEBUG
+extern read_proc_t jfs_txanchor_read;
+
+static int loglevel_read(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len;
+
+ len = sprintf(page, "%d\n", jfsloglevel);
+
+ len -= off;
+ *start = page + off;
+
+ if (len > count)
+ len = count;
+ else
+ *eof = 1;
+
+ if (len < 0)
+ len = 0;
+
+ return len;
+}
+
+static int loglevel_write(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+ char c;
+
+ if (get_user(c, buffer))
+ return -EFAULT;
+
+ /* yes, I know this is an ASCIIism. --hch */
+ if (c < '0' || c > '9')
+ return -EINVAL;
+ jfsloglevel = c - '0';
+ return count;
+}
+#endif
+
+
+#ifdef CONFIG_JFS_STATISTICS
+extern read_proc_t jfs_lmstats_read;
+extern read_proc_t jfs_txstats_read;
+extern read_proc_t jfs_xtstat_read;
+extern read_proc_t jfs_mpstat_read;
+#endif
+
+static struct {
+ const char *name;
+ read_proc_t *read_fn;
+ write_proc_t *write_fn;
+} Entries[] = {
+#ifdef CONFIG_JFS_STATISTICS
+ { "lmstats", jfs_lmstats_read, },
+ { "txstats", jfs_txstats_read, },
+ { "xtstat", jfs_xtstat_read, },
+ { "mpstat", jfs_mpstat_read, },
+#endif
+#ifdef CONFIG_JFS_DEBUG
+ { "TxAnchor", jfs_txanchor_read, },
+ { "loglevel", loglevel_read, loglevel_write }
+#endif
+};
+#define NPROCENT (sizeof(Entries)/sizeof(Entries[0]))
+
+void jfs_proc_init(void)
+{
+ int i;
+
+ if (!(base = proc_mkdir("jfs", proc_root_fs)))
+ return;
+ base->owner = THIS_MODULE;
+
+ for (i = 0; i < NPROCENT; i++) {
+ struct proc_dir_entry *p;
+ if ((p = create_proc_entry(Entries[i].name, 0, base))) {
+ p->read_proc = Entries[i].read_fn;
+ p->write_proc = Entries[i].write_fn;
+ }
+ }
+}
+
+void jfs_proc_clean(void)
+{
+ int i;
+
+ if (base) {
+ for (i = 0; i < NPROCENT; i++)
+ remove_proc_entry(Entries[i].name, base);
+ remove_proc_entry("jfs", proc_root_fs);
+ }
+}
+
+#endif /* PROC_FS_JFS */
diff --git a/fs/jfs/jfs_debug.h b/fs/jfs/jfs_debug.h
new file mode 100644
index 00000000000000..a38079ae1e005f
--- /dev/null
+++ b/fs/jfs/jfs_debug.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2000-2002
+ * Portions Copyright (c) Christoph Hellwig, 2001-2002
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _H_JFS_DEBUG
+#define _H_JFS_DEBUG
+
+/*
+ * jfs_debug.h
+ *
+ * global debug message, data structure/macro definitions
+ * under control of CONFIG_JFS_DEBUG, CONFIG_JFS_STATISTICS;
+ */
+
+/*
+ * Create /proc/fs/jfs if procfs is enabled andeither
+ * CONFIG_JFS_DEBUG or CONFIG_JFS_STATISTICS is defined
+ */
+#if defined(CONFIG_PROC_FS) && (defined(CONFIG_JFS_DEBUG) || defined(CONFIG_JFS_STATISTICS))
+ #define PROC_FS_JFS
+#endif
+
+/*
+ * assert with traditional printf/panic
+ */
+#ifdef CONFIG_KERNEL_ASSERTS
+/* kgdb stuff */
+#define assert(p) KERNEL_ASSERT(#p, p)
+#else
+#define assert(p) do { \
+ if (!(p)) { \
+ printk(KERN_CRIT "BUG at %s:%d assert(%s)\n", \
+ __FILE__, __LINE__, #p); \
+ BUG(); \
+ } \
+} while (0)
+#endif
+
+/*
+ * debug ON
+ * --------
+ */
+#ifdef CONFIG_JFS_DEBUG
+#define ASSERT(p) assert(p)
+
+/* printk verbosity */
+#define JFS_LOGLEVEL_ERR 1
+#define JFS_LOGLEVEL_WARN 2
+#define JFS_LOGLEVEL_DEBUG 3
+#define JFS_LOGLEVEL_INFO 4
+
+extern int jfsloglevel;
+
+/* dump memory contents */
+extern void dump_mem(char *label, void *data, int length);
+
+/* information message: e.g., configuration, major event */
+#define jfs_info(fmt, arg...) do { \
+ if (jfsloglevel >= JFS_LOGLEVEL_INFO) \
+ printk(KERN_INFO fmt "\n", ## arg); \
+} while (0)
+
+/* debug message: ad hoc */
+#define jfs_debug(fmt, arg...) do { \
+ if (jfsloglevel >= JFS_LOGLEVEL_DEBUG) \
+ printk(KERN_DEBUG fmt "\n", ## arg); \
+} while (0)
+
+/* warn message: */
+#define jfs_warn(fmt, arg...) do { \
+ if (jfsloglevel >= JFS_LOGLEVEL_WARN) \
+ printk(KERN_WARNING fmt "\n", ## arg); \
+} while (0)
+
+/* error event message: e.g., i/o error */
+#define jfs_err(fmt, arg...) do { \
+ if (jfsloglevel >= JFS_LOGLEVEL_ERR) \
+ printk(KERN_ERR fmt "\n", ## arg); \
+} while (0)
+
+/*
+ * debug OFF
+ * ---------
+ */
+#else /* CONFIG_JFS_DEBUG */
+#define dump_mem(label,data,length) do {} while (0)
+#define ASSERT(p) do {} while (0)
+#define jfs_info(fmt, arg...) do {} while (0)
+#define jfs_debug(fmt, arg...) do {} while (0)
+#define jfs_warn(fmt, arg...) do {} while (0)
+#define jfs_err(fmt, arg...) do {} while (0)
+#endif /* CONFIG_JFS_DEBUG */
+
+/*
+ * statistics
+ * ----------
+ */
+#ifdef CONFIG_JFS_STATISTICS
+#define INCREMENT(x) ((x)++)
+#define DECREMENT(x) ((x)--)
+#define HIGHWATERMARK(x,y) ((x) = max((x), (y)))
+#else
+#define INCREMENT(x)
+#define DECREMENT(x)
+#define HIGHWATERMARK(x,y)
+#endif /* CONFIG_JFS_STATISTICS */
+
+#endif /* _H_JFS_DEBUG */
diff --git a/fs/jfs/jfs_dinode.h b/fs/jfs/jfs_dinode.h
new file mode 100644
index 00000000000000..b3388dfa4fb19d
--- /dev/null
+++ b/fs/jfs/jfs_dinode.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2000-2001
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _H_JFS_DINODE
+#define _H_JFS_DINODE
+
+/*
+ * jfs_dinode.h: on-disk inode manager
+ */
+
+#define INODESLOTSIZE 128
+#define L2INODESLOTSIZE 7
+#define log2INODESIZE 9 /* log2(bytes per dinode) */
+
+
+/*
+ * on-disk inode : 512 bytes
+ *
+ * note: align 64-bit fields on 8-byte boundary.
+ */
+struct dinode {
+ /*
+ * I. base area (128 bytes)
+ * ------------------------
+ *
+ * define generic/POSIX attributes
+ */
+ u32 di_inostamp; /* 4: stamp to show inode belongs to fileset */
+ s32 di_fileset; /* 4: fileset number */
+ u32 di_number; /* 4: inode number, aka file serial number */
+ u32 di_gen; /* 4: inode generation number */
+
+ pxd_t di_ixpxd; /* 8: inode extent descriptor */
+
+ s64 di_size; /* 8: size */
+ s64 di_nblocks; /* 8: number of blocks allocated */
+
+ u32 di_nlink; /* 4: number of links to the object */
+
+ u32 di_uid; /* 4: user id of owner */
+ u32 di_gid; /* 4: group id of owner */
+
+ u32 di_mode; /* 4: attribute, format and permission */
+
+ struct timestruc_t di_atime; /* 8: time last data accessed */
+ struct timestruc_t di_ctime; /* 8: time last status changed */
+ struct timestruc_t di_mtime; /* 8: time last data modified */
+ struct timestruc_t di_otime; /* 8: time created */
+
+ dxd_t di_acl; /* 16: acl descriptor */
+
+ dxd_t di_ea; /* 16: ea descriptor */
+
+ u32 di_next_index; /* 4: Next available dir_table index */
+
+ s32 di_acltype; /* 4: Type of ACL */
+
+ /*
+ * Extension Areas.
+ *
+ * Historically, the inode was partitioned into 4 128-byte areas,
+ * the last 3 being defined as unions which could have multiple
+ * uses. The first 96 bytes had been completely unused until
+ * an index table was added to the directory. It is now more
+ * useful to describe the last 3/4 of the inode as a single
+ * union. We would probably be better off redesigning the
+ * entire structure from scratch, but we don't want to break
+ * commonality with OS/2's JFS at this time.
+ */
+ union {
+ struct {
+ /*
+ * This table contains the information needed to
+ * find a directory entry from a 32-bit index.
+ * If the index is small enough, the table is inline,
+ * otherwise, an x-tree root overlays this table
+ */
+ struct dir_table_slot _table[12]; /* 96: inline */
+
+ dtroot_t _dtroot; /* 288: dtree root */
+ } _dir; /* (384) */
+#define di_dirtable u._dir._table
+#define di_dtroot u._dir._dtroot
+#define di_parent di_dtroot.header.idotdot
+#define di_DASD di_dtroot.header.DASD
+
+ struct {
+ union {
+ u8 _data[96]; /* 96: unused */
+ struct {
+ void *_imap; /* 4: unused */
+ u32 _gengen; /* 4: generator */
+ } _imap;
+ } _u1; /* 96: */
+#define di_gengen u._file._u1._imap._gengen
+
+ union {
+ xtpage_t _xtroot;
+ struct {
+ u8 unused[16]; /* 16: */
+ dxd_t _dxd; /* 16: */
+ union {
+ u32 _rdev; /* 4: */
+ u8 _fastsymlink[128];
+ } _u;
+ u8 _inlineea[128];
+ } _special;
+ } _u2;
+ } _file;
+#define di_xtroot u._file._u2._xtroot
+#define di_dxd u._file._u2._special._dxd
+#define di_btroot di_xtroot
+#define di_inlinedata u._file._u2._special._u
+#define di_rdev u._file._u2._special._u._rdev
+#define di_fastsymlink u._file._u2._special._u._fastsymlink
+#define di_inlineea u._file._u2._special._inlineea
+ } u;
+};
+
+/* extended mode bits (on-disk inode di_mode) */
+#define IFJOURNAL 0x00010000 /* journalled file */
+#define ISPARSE 0x00020000 /* sparse file enabled */
+#define INLINEEA 0x00040000 /* inline EA area free */
+#define ISWAPFILE 0x00800000 /* file open for pager swap space */
+
+/* more extended mode bits: attributes for OS/2 */
+#define IREADONLY 0x02000000 /* no write access to file */
+#define IARCHIVE 0x40000000 /* file archive bit */
+#define ISYSTEM 0x08000000 /* system file */
+#define IHIDDEN 0x04000000 /* hidden file */
+#define IRASH 0x4E000000 /* mask for changeable attributes */
+#define INEWNAME 0x80000000 /* non-8.3 filename format */
+#define IDIRECTORY 0x20000000 /* directory (shadow of real bit) */
+#define ATTRSHIFT 25 /* bits to shift to move attribute
+ specification to mode position */
+
+#endif /*_H_JFS_DINODE */
diff --git a/fs/jfs/jfs_dmap.c b/fs/jfs/jfs_dmap.c
new file mode 100644
index 00000000000000..46f0cf2d50423e
--- /dev/null
+++ b/fs/jfs/jfs_dmap.c
@@ -0,0 +1,4273 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2000-2004
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/fs.h>
+#include "jfs_incore.h"
+#include "jfs_superblock.h"
+#include "jfs_dmap.h"
+#include "jfs_imap.h"
+#include "jfs_lock.h"
+#include "jfs_metapage.h"
+#include "jfs_superblock.h"
+#include "jfs_debug.h"
+
+/*
+ * Debug code for double-checking block map
+ */
+/* #define _JFS_DEBUG_DMAP 1 */
+
+#ifdef _JFS_DEBUG_DMAP
+#define DBINITMAP(size,ipbmap,results) \
+ DBinitmap(size,ipbmap,results)
+#define DBALLOC(dbmap,mapsize,blkno,nblocks) \
+ DBAlloc(dbmap,mapsize,blkno,nblocks)
+#define DBFREE(dbmap,mapsize,blkno,nblocks) \
+ DBFree(dbmap,mapsize,blkno,nblocks)
+#define DBALLOCCK(dbmap,mapsize,blkno,nblocks) \
+ DBAllocCK(dbmap,mapsize,blkno,nblocks)
+#define DBFREECK(dbmap,mapsize,blkno,nblocks) \
+ DBFreeCK(dbmap,mapsize,blkno,nblocks)
+
+static void DBinitmap(s64, struct inode *, u32 **);
+static void DBAlloc(uint *, s64, s64, s64);
+static void DBFree(uint *, s64, s64, s64);
+static void DBAllocCK(uint *, s64, s64, s64);
+static void DBFreeCK(uint *, s64, s64, s64);
+#else
+#define DBINITMAP(size,ipbmap,results)
+#define DBALLOC(dbmap, mapsize, blkno, nblocks)
+#define DBFREE(dbmap, mapsize, blkno, nblocks)
+#define DBALLOCCK(dbmap, mapsize, blkno, nblocks)
+#define DBFREECK(dbmap, mapsize, blkno, nblocks)
+#endif /* _JFS_DEBUG_DMAP */
+
+/*
+ * SERIALIZATION of the Block Allocation Map.
+ *
+ * the working state of the block allocation map is accessed in
+ * two directions:
+ *
+ * 1) allocation and free requests that start at the dmap
+ * level and move up through the dmap control pages (i.e.
+ * the vast majority of requests).
+ *
+ * 2) allocation requests that start at dmap control page
+ * level and work down towards the dmaps.
+ *
+ * the serialization scheme used here is as follows.
+ *
+ * requests which start at the bottom are serialized against each
+ * other through buffers and each requests holds onto its buffers
+ * as it works it way up from a single dmap to the required level
+ * of dmap control page.
+ * requests that start at the top are serialized against each other
+ * and request that start from the bottom by the multiple read/single
+ * write inode lock of the bmap inode. requests starting at the top
+ * take this lock in write mode while request starting at the bottom
+ * take the lock in read mode. a single top-down request may proceed
+ * exclusively while multiple bottoms-up requests may proceed
+ * simultaneously (under the protection of busy buffers).
+ *
+ * in addition to information found in dmaps and dmap control pages,
+ * the working state of the block allocation map also includes read/
+ * write information maintained in the bmap descriptor (i.e. total
+ * free block count, allocation group level free block counts).
+ * a single exclusive lock (BMAP_LOCK) is used to guard this information
+ * in the face of multiple-bottoms up requests.
+ * (lock ordering: IREAD_LOCK, BMAP_LOCK);
+ *
+ * accesses to the persistent state of the block allocation map (limited
+ * to the persistent bitmaps in dmaps) is guarded by (busy) buffers.
+ */
+
+#define BMAP_LOCK_INIT(bmp) init_MUTEX(&bmp->db_bmaplock)
+#define BMAP_LOCK(bmp) down(&bmp->db_bmaplock)
+#define BMAP_UNLOCK(bmp) up(&bmp->db_bmaplock)
+
+/*
+ * forward references
+ */
+static void dbAllocBits(struct bmap * bmp, struct dmap * dp, s64 blkno,
+ int nblocks);
+static void dbSplit(dmtree_t * tp, int leafno, int splitsz, int newval);
+static void dbBackSplit(dmtree_t * tp, int leafno);
+static void dbJoin(dmtree_t * tp, int leafno, int newval);
+static void dbAdjTree(dmtree_t * tp, int leafno, int newval);
+static int dbAdjCtl(struct bmap * bmp, s64 blkno, int newval, int alloc,
+ int level);
+static int dbAllocAny(struct bmap * bmp, s64 nblocks, int l2nb, s64 * results);
+static int dbAllocNext(struct bmap * bmp, struct dmap * dp, s64 blkno,
+ int nblocks);
+static int dbAllocNear(struct bmap * bmp, struct dmap * dp, s64 blkno,
+ int nblocks,
+ int l2nb, s64 * results);
+static int dbAllocDmap(struct bmap * bmp, struct dmap * dp, s64 blkno,
+ int nblocks);
+static int dbAllocDmapLev(struct bmap * bmp, struct dmap * dp, int nblocks,
+ int l2nb,
+ s64 * results);
+static int dbAllocAG(struct bmap * bmp, int agno, s64 nblocks, int l2nb,
+ s64 * results);
+static int dbAllocCtl(struct bmap * bmp, s64 nblocks, int l2nb, s64 blkno,
+ s64 * results);
+static int dbExtend(struct inode *ip, s64 blkno, s64 nblocks, s64 addnblocks);
+static int dbFindBits(u32 word, int l2nb);
+static int dbFindCtl(struct bmap * bmp, int l2nb, int level, s64 * blkno);
+static int dbFindLeaf(dmtree_t * tp, int l2nb, int *leafidx);
+static void dbFreeBits(struct bmap * bmp, struct dmap * dp, s64 blkno,
+ int nblocks);
+static int dbFreeDmap(struct bmap * bmp, struct dmap * dp, s64 blkno,
+ int nblocks);
+static int dbMaxBud(u8 * cp);
+s64 dbMapFileSizeToMapSize(struct inode *ipbmap);
+static int blkstol2(s64 nb);
+
+static int cntlz(u32 value);
+static int cnttz(u32 word);
+
+static int dbAllocDmapBU(struct bmap * bmp, struct dmap * dp, s64 blkno,
+ int nblocks);
+static int dbInitDmap(struct dmap * dp, s64 blkno, int nblocks);
+static int dbInitDmapTree(struct dmap * dp);
+static int dbInitTree(struct dmaptree * dtp);
+static int dbInitDmapCtl(struct dmapctl * dcp, int level, int i);
+static int dbGetL2AGSize(s64 nblocks);
+
+/*
+ * buddy table
+ *
+ * table used for determining buddy sizes within characters of
+ * dmap bitmap words. the characters themselves serve as indexes
+ * into the table, with the table elements yielding the maximum
+ * binary buddy of free bits within the character.
+ */
+static s8 budtab[256] = {
+ 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
+ 2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
+ 2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
+ 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
+ 2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
+ 2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
+ 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
+ 2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,
+ 2, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -1
+};
+
+
+/*
+ * NAME: dbMount()
+ *
+ * FUNCTION: initializate the block allocation map.
+ *
+ * memory is allocated for the in-core bmap descriptor and
+ * the in-core descriptor is initialized from disk.
+ *
+ * PARAMETERS:
+ * ipbmap - pointer to in-core inode for the block map.
+ *
+ * RETURN VALUES:
+ * 0 - success
+ * -ENOMEM - insufficient memory
+ * -EIO - i/o error
+ */
+int dbMount(struct inode *ipbmap)
+{
+ struct bmap *bmp;
+ struct dbmap *dbmp_le;
+ struct metapage *mp;
+ int i;
+
+ /*
+ * allocate/initialize the in-memory bmap descriptor
+ */
+ /* allocate memory for the in-memory bmap descriptor */
+ bmp = kmalloc(sizeof(struct bmap), GFP_KERNEL);
+ if (bmp == NULL)
+ return -ENOMEM;
+
+ /* read the on-disk bmap descriptor. */
+ mp = read_metapage(ipbmap,
+ BMAPBLKNO << JFS_SBI(ipbmap->i_sb)->l2nbperpage,
+ PSIZE, 0);
+ if (mp == NULL) {
+ kfree(bmp);
+ return -EIO;
+ }
+
+ /* copy the on-disk bmap descriptor to its in-memory version. */
+ dbmp_le = (struct dbmap *) mp->data;
+ bmp->db_mapsize = le64_to_cpu(dbmp_le->dn_mapsize);
+ bmp->db_nfree = le64_to_cpu(dbmp_le->dn_nfree);
+ bmp->db_l2nbperpage = le32_to_cpu(dbmp_le->dn_l2nbperpage);
+ bmp->db_numag = le32_to_cpu(dbmp_le->dn_numag);
+ bmp->db_maxlevel = le32_to_cpu(dbmp_le->dn_maxlevel);
+ bmp->db_maxag = le32_to_cpu(dbmp_le->dn_maxag);
+ bmp->db_agpref = le32_to_cpu(dbmp_le->dn_agpref);
+ bmp->db_aglevel = le32_to_cpu(dbmp_le->dn_aglevel);
+ bmp->db_agheigth = le32_to_cpu(dbmp_le->dn_agheigth);
+ bmp->db_agwidth = le32_to_cpu(dbmp_le->dn_agwidth);
+ bmp->db_agstart = le32_to_cpu(dbmp_le->dn_agstart);
+ bmp->db_agl2size = le32_to_cpu(dbmp_le->dn_agl2size);
+ for (i = 0; i < MAXAG; i++)
+ bmp->db_agfree[i] = le64_to_cpu(dbmp_le->dn_agfree[i]);
+ bmp->db_agsize = le64_to_cpu(dbmp_le->dn_agsize);
+ bmp->db_maxfreebud = dbmp_le->dn_maxfreebud;
+
+ /* release the buffer. */
+ release_metapage(mp);
+
+ /* bind the bmap inode and the bmap descriptor to each other. */
+ bmp->db_ipbmap = ipbmap;
+ JFS_SBI(ipbmap->i_sb)->bmap = bmp;
+
+ memset(bmp->db_active, 0, sizeof(bmp->db_active));
+ DBINITMAP(bmp->db_mapsize, ipbmap, &bmp->db_DBmap);
+
+ /*
+ * allocate/initialize the bmap lock
+ */
+ BMAP_LOCK_INIT(bmp);
+
+ return (0);
+}
+
+
+/*
+ * NAME: dbUnmount()
+ *
+ * FUNCTION: terminate the block allocation map in preparation for
+ * file system unmount.
+ *
+ * the in-core bmap descriptor is written to disk and
+ * the memory for this descriptor is freed.
+ *
+ * PARAMETERS:
+ * ipbmap - pointer to in-core inode for the block map.
+ *
+ * RETURN VALUES:
+ * 0 - success
+ * -EIO - i/o error
+ */
+int dbUnmount(struct inode *ipbmap, int mounterror)
+{
+ struct bmap *bmp = JFS_SBI(ipbmap->i_sb)->bmap;
+ int i;
+
+ if (!(mounterror || isReadOnly(ipbmap)))
+ dbSync(ipbmap);
+
+ /*
+ * Invalidate the page cache buffers
+ */
+ truncate_inode_pages(ipbmap->i_mapping, 0);
+
+ /*
+ * Sanity Check
+ */
+ for (i = 0; i < bmp->db_numag; i++)
+ if (atomic_read(&bmp->db_active[i]))
+ printk(KERN_ERR "dbUnmount: db_active[%d] = %d\n",
+ i, atomic_read(&bmp->db_active[i]));
+
+ /* free the memory for the in-memory bmap. */
+ kfree(bmp);
+
+ return (0);
+}
+
+/*
+ * dbSync()
+ */
+int dbSync(struct inode *ipbmap)
+{
+ struct dbmap *dbmp_le;
+ struct bmap *bmp = JFS_SBI(ipbmap->i_sb)->bmap;
+ struct metapage *mp;
+ int i;
+
+ /*
+ * write bmap global control page
+ */
+ /* get the buffer for the on-disk bmap descriptor. */
+ mp = read_metapage(ipbmap,
+ BMAPBLKNO << JFS_SBI(ipbmap->i_sb)->l2nbperpage,
+ PSIZE, 0);
+ if (mp == NULL) {
+ jfs_err("dbSync: read_metapage failed!");
+ return -EIO;
+ }
+ /* copy the in-memory version of the bmap to the on-disk version */
+ dbmp_le = (struct dbmap *) mp->data;
+ dbmp_le->dn_mapsize = cpu_to_le64(bmp->db_mapsize);
+ dbmp_le->dn_nfree = cpu_to_le64(bmp->db_nfree);
+ dbmp_le->dn_l2nbperpage = cpu_to_le32(bmp->db_l2nbperpage);
+ dbmp_le->dn_numag = cpu_to_le32(bmp->db_numag);
+ dbmp_le->dn_maxlevel = cpu_to_le32(bmp->db_maxlevel);
+ dbmp_le->dn_maxag = cpu_to_le32(bmp->db_maxag);
+ dbmp_le->dn_agpref = cpu_to_le32(bmp->db_agpref);
+ dbmp_le->dn_aglevel = cpu_to_le32(bmp->db_aglevel);
+ dbmp_le->dn_agheigth = cpu_to_le32(bmp->db_agheigth);
+ dbmp_le->dn_agwidth = cpu_to_le32(bmp->db_agwidth);
+ dbmp_le->dn_agstart = cpu_to_le32(bmp->db_agstart);
+ dbmp_le->dn_agl2size = cpu_to_le32(bmp->db_agl2size);
+ for (i = 0; i < MAXAG; i++)
+ dbmp_le->dn_agfree[i] = cpu_to_le64(bmp->db_agfree[i]);
+ dbmp_le->dn_agsize = cpu_to_le64(bmp->db_agsize);
+ dbmp_le->dn_maxfreebud = bmp->db_maxfreebud;
+
+ /* write the buffer */
+ write_metapage(mp);
+
+ /*
+ * write out dirty pages of bmap
+ */
+ fsync_inode_data_buffers(ipbmap);
+
+ ipbmap->i_state |= I_DIRTY;
+ diWriteSpecial(ipbmap, 0);
+
+ return (0);
+}
+
+
+/*
+ * NAME: dbFree()
+ *
+ * FUNCTION: free the specified block range from the working block
+ * allocation map.
+ *
+ * the blocks will be free from the working map one dmap
+ * at a time.
+ *
+ * PARAMETERS:
+ * ip - pointer to in-core inode;
+ * blkno - starting block number to be freed.
+ * nblocks - number of blocks to be freed.
+ *
+ * RETURN VALUES:
+ * 0 - success
+ * -EIO - i/o error
+ */
+int dbFree(struct inode *ip, s64 blkno, s64 nblocks)
+{
+ struct metapage *mp;
+ struct dmap *dp;
+ int nb, rc;
+ s64 lblkno, rem;
+ struct inode *ipbmap = JFS_SBI(ip->i_sb)->ipbmap;
+ struct bmap *bmp = JFS_SBI(ip->i_sb)->bmap;
+
+ IREAD_LOCK(ipbmap);
+
+ /* block to be freed better be within the mapsize. */
+ if (unlikely((blkno == 0) || (blkno + nblocks > bmp->db_mapsize))) {
+ IREAD_UNLOCK(ipbmap);
+ printk(KERN_ERR "blkno = %Lx, nblocks = %Lx\n",
+ (unsigned long long) blkno,
+ (unsigned long long) nblocks);
+ jfs_error(ip->i_sb,
+ "dbFree: block to be freed is outside the map");
+ return -EIO;
+ }
+
+ /*
+ * free the blocks a dmap at a time.
+ */
+ mp = NULL;
+ for (rem = nblocks; rem > 0; rem -= nb, blkno += nb) {
+ /* release previous dmap if any */
+ if (mp) {
+ write_metapage(mp);
+ }
+
+ /* get the buffer for the current dmap. */
+ lblkno = BLKTODMAP(blkno, bmp->db_l2nbperpage);
+ mp = read_metapage(ipbmap, lblkno, PSIZE, 0);
+ if (mp == NULL) {
+ IREAD_UNLOCK(ipbmap);
+ return -EIO;
+ }
+ dp = (struct dmap *) mp->data;
+
+ /* determine the number of blocks to be freed from
+ * this dmap.
+ */
+ nb = min(rem, BPERDMAP - (blkno & (BPERDMAP - 1)));
+
+ DBALLOCCK(bmp->db_DBmap, bmp->db_mapsize, blkno, nb);
+
+ /* free the blocks. */
+ if ((rc = dbFreeDmap(bmp, dp, blkno, nb))) {
+ release_metapage(mp);
+ IREAD_UNLOCK(ipbmap);
+ return (rc);
+ }
+
+ DBFREE(bmp->db_DBmap, bmp->db_mapsize, blkno, nb);
+ }
+
+ /* write the last buffer. */
+ write_metapage(mp);
+
+ IREAD_UNLOCK(ipbmap);
+
+ return (0);
+}
+
+
+/*
+ * NAME: dbUpdatePMap()
+ *
+ * FUNCTION: update the allocation state (free or allocate) of the
+ * specified block range in the persistent block allocation map.
+ *
+ * the blocks will be updated in the persistent map one
+ * dmap at a time.
+ *
+ * PARAMETERS:
+ * ipbmap - pointer to in-core inode for the block map.
+ * free - TRUE if block range is to be freed from the persistent
+ * map; FALSE if it is to be allocated.
+ * blkno - starting block number of the range.
+ * nblocks - number of contiguous blocks in the range.
+ * tblk - transaction block;
+ *
+ * RETURN VALUES:
+ * 0 - success
+ * -EIO - i/o error
+ */
+int
+dbUpdatePMap(struct inode *ipbmap,
+ int free, s64 blkno, s64 nblocks, struct tblock * tblk)
+{
+ int nblks, dbitno, wbitno, rbits;
+ int word, nbits, nwords;
+ struct bmap *bmp = JFS_SBI(ipbmap->i_sb)->bmap;
+ s64 lblkno, rem, lastlblkno;
+ u32 mask;
+ struct dmap *dp;
+ struct metapage *mp;
+ struct jfs_log *log;
+ int lsn, difft, diffp;
+
+ /* the blocks better be within the mapsize. */
+ if (blkno + nblocks > bmp->db_mapsize) {
+ printk(KERN_ERR "blkno = %Lx, nblocks = %Lx\n",
+ (unsigned long long) blkno,
+ (unsigned long long) nblocks);
+ jfs_error(ipbmap->i_sb,
+ "dbUpdatePMap: blocks are outside the map");
+ return -EIO;
+ }
+
+ /* compute delta of transaction lsn from log syncpt */
+ lsn = tblk->lsn;
+ log = (struct jfs_log *) JFS_SBI(tblk->sb)->log;
+ logdiff(difft, lsn, log);
+
+ /*
+ * update the block state a dmap at a time.
+ */
+ mp = NULL;
+ lastlblkno = 0;
+ for (rem = nblocks; rem > 0; rem -= nblks, blkno += nblks) {
+ /* get the buffer for the current dmap. */
+ lblkno = BLKTODMAP(blkno, bmp->db_l2nbperpage);
+ if (lblkno != lastlblkno) {
+ if (mp) {
+ write_metapage(mp);
+ }
+
+ mp = read_metapage(bmp->db_ipbmap, lblkno, PSIZE,
+ 0);
+ if (mp == NULL)
+ return -EIO;
+ }
+ dp = (struct dmap *) mp->data;
+
+ /* determine the bit number and word within the dmap of
+ * the starting block. also determine how many blocks
+ * are to be updated within this dmap.
+ */
+ dbitno = blkno & (BPERDMAP - 1);
+ word = dbitno >> L2DBWORD;
+ nblks = min(rem, (s64)BPERDMAP - dbitno);
+
+ /* update the bits of the dmap words. the first and last
+ * words may only have a subset of their bits updated. if
+ * this is the case, we'll work against that word (i.e.
+ * partial first and/or last) only in a single pass. a
+ * single pass will also be used to update all words that
+ * are to have all their bits updated.
+ */
+ for (rbits = nblks; rbits > 0;
+ rbits -= nbits, dbitno += nbits) {
+ /* determine the bit number within the word and
+ * the number of bits within the word.
+ */
+ wbitno = dbitno & (DBWORD - 1);
+ nbits = min(rbits, DBWORD - wbitno);
+
+ /* check if only part of the word is to be updated. */
+ if (nbits < DBWORD) {
+ /* update (free or allocate) the bits
+ * in this word.
+ */
+ mask =
+ (ONES << (DBWORD - nbits) >> wbitno);
+ if (free)
+ dp->pmap[word] &=
+ cpu_to_le32(~mask);
+ else
+ dp->pmap[word] |=
+ cpu_to_le32(mask);
+
+ word += 1;
+ } else {
+ /* one or more words are to have all
+ * their bits updated. determine how
+ * many words and how many bits.
+ */
+ nwords = rbits >> L2DBWORD;
+ nbits = nwords << L2DBWORD;
+
+ /* update (free or allocate) the bits
+ * in these words.
+ */
+ if (free)
+ memset(&dp->pmap[word], 0,
+ nwords * 4);
+ else
+ memset(&dp->pmap[word], (int) ONES,
+ nwords * 4);
+
+ word += nwords;
+ }
+ }
+
+ /*
+ * update dmap lsn
+ */
+ if (lblkno == lastlblkno)
+ continue;
+
+ lastlblkno = lblkno;
+
+ if (mp->lsn != 0) {
+ /* inherit older/smaller lsn */
+ logdiff(diffp, mp->lsn, log);
+ if (difft < diffp) {
+ mp->lsn = lsn;
+
+ /* move bp after tblock in logsync list */
+ LOGSYNC_LOCK(log);
+ list_del(&mp->synclist);
+ list_add(&mp->synclist, &tblk->synclist);
+ LOGSYNC_UNLOCK(log);
+ }
+
+ /* inherit younger/larger clsn */
+ LOGSYNC_LOCK(log);
+ logdiff(difft, tblk->clsn, log);
+ logdiff(diffp, mp->clsn, log);
+ if (difft > diffp)
+ mp->clsn = tblk->clsn;
+ LOGSYNC_UNLOCK(log);
+ } else {
+ mp->log = log;
+ mp->lsn = lsn;
+
+ /* insert bp after tblock in logsync list */
+ LOGSYNC_LOCK(log);
+
+ log->count++;
+ list_add(&mp->synclist, &tblk->synclist);
+
+ mp->clsn = tblk->clsn;
+ LOGSYNC_UNLOCK(log);
+ }
+ }
+
+ /* write the last buffer. */
+ if (mp) {
+ write_metapage(mp);
+ }
+
+ return (0);
+}
+
+
+/*
+ * NAME: dbNextAG()
+ *
+ * FUNCTION: find the preferred allocation group for new allocations.
+ *
+ * Within the allocation groups, we maintain a preferred
+ * allocation group which consists of a group with at least
+ * average free space. It is the preferred group that we target
+ * new inode allocation towards. The tie-in between inode
+ * allocation and block allocation occurs as we allocate the
+ * first (data) block of an inode and specify the inode (block)
+ * as the allocation hint for this block.
+ *
+ * We try to avoid having more than one open file growing in
+ * an allocation group, as this will lead to fragmentation.
+ * This differs from the old OS/2 method of trying to keep
+ * empty ags around for large allocations.
+ *
+ * PARAMETERS:
+ * ipbmap - pointer to in-core inode for the block map.
+ *
+ * RETURN VALUES:
+ * the preferred allocation group number.
+ */
+int dbNextAG(struct inode *ipbmap)
+{
+ s64 avgfree;
+ int agpref;
+ s64 hwm = 0;
+ int i;
+ int next_best = -1;
+ struct bmap *bmp = JFS_SBI(ipbmap->i_sb)->bmap;
+
+ BMAP_LOCK(bmp);
+
+ /* determine the average number of free blocks within the ags. */
+ avgfree = (u32)bmp->db_nfree / bmp->db_numag;
+
+ /*
+ * if the current preferred ag does not have an active allocator
+ * and has at least average freespace, return it
+ */
+ agpref = bmp->db_agpref;
+ if ((atomic_read(&bmp->db_active[agpref]) == 0) &&
+ (bmp->db_agfree[agpref] >= avgfree))
+ goto unlock;
+
+ /* From the last preferred ag, find the next one with at least
+ * average free space.
+ */
+ for (i = 0 ; i < bmp->db_numag; i++, agpref++) {
+ if (agpref == bmp->db_numag)
+ agpref = 0;
+
+ if (atomic_read(&bmp->db_active[agpref]))
+ /* open file is currently growing in this ag */
+ continue;
+ if (bmp->db_agfree[agpref] >= avgfree) {
+ /* Return this one */
+ bmp->db_agpref = agpref;
+ goto unlock;
+ } else if (bmp->db_agfree[agpref] > hwm) {
+ /* Less than avg. freespace, but best so far */
+ hwm = bmp->db_agfree[agpref];
+ next_best = agpref;
+ }
+ }
+
+ /*
+ * If no inactive ag was found with average freespace, use the
+ * next best
+ */
+ if (next_best != -1)
+ bmp->db_agpref = next_best;
+ /* else leave db_agpref unchanged */
+unlock:
+ BMAP_UNLOCK(bmp);
+
+ /* return the preferred group.
+ */
+ return (bmp->db_agpref);
+}
+
+/*
+ * NAME: dbAlloc()
+ *
+ * FUNCTION: attempt to allocate a specified number of contiguous free
+ * blocks from the working allocation block map.
+ *
+ * the block allocation policy uses hints and a multi-step
+ * approach.
+ *
+ * for allocation requests smaller than the number of blocks
+ * per dmap, we first try to allocate the new blocks
+ * immediately following the hint. if these blocks are not
+ * available, we try to allocate blocks near the hint. if
+ * no blocks near the hint are available, we next try to
+ * allocate within the same dmap as contains the hint.
+ *
+ * if no blocks are available in the dmap or the allocation
+ * request is larger than the dmap size, we try to allocate
+ * within the same allocation group as contains the hint. if
+ * this does not succeed, we finally try to allocate anywhere
+ * within the aggregate.
+ *
+ * we also try to allocate anywhere within the aggregate for
+ * for allocation requests larger than the allocation group
+ * size or requests that specify no hint value.
+ *
+ * PARAMETERS:
+ * ip - pointer to in-core inode;
+ * hint - allocation hint.
+ * nblocks - number of contiguous blocks in the range.
+ * results - on successful return, set to the starting block number
+ * of the newly allocated contiguous range.
+ *
+ * RETURN VALUES:
+ * 0 - success
+ * -ENOSPC - insufficient disk resources
+ * -EIO - i/o error
+ */
+int dbAlloc(struct inode *ip, s64 hint, s64 nblocks, s64 * results)
+{
+ int rc, agno;
+ struct inode *ipbmap = JFS_SBI(ip->i_sb)->ipbmap;
+ struct bmap *bmp;
+ struct metapage *mp;
+ s64 lblkno, blkno;
+ struct dmap *dp;
+ int l2nb;
+ s64 mapSize;
+ int writers;
+
+ /* assert that nblocks is valid */
+ assert(nblocks > 0);
+
+#ifdef _STILL_TO_PORT
+ /* DASD limit check F226941 */
+ if (OVER_LIMIT(ip, nblocks))
+ return -ENOSPC;
+#endif /* _STILL_TO_PORT */
+
+ /* get the log2 number of blocks to be allocated.
+ * if the number of blocks is not a log2 multiple,
+ * it will be rounded up to the next log2 multiple.
+ */
+ l2nb = BLKSTOL2(nblocks);
+
+ bmp = JFS_SBI(ip->i_sb)->bmap;
+
+//retry: /* serialize w.r.t.extendfs() */
+ mapSize = bmp->db_mapsize;
+
+ /* the hint should be within the map */
+ if (hint >= mapSize) {
+ jfs_error(ip->i_sb, "dbAlloc: the hint is outside the map");
+ return -EIO;
+ }
+
+ /* if the number of blocks to be allocated is greater than the
+ * allocation group size, try to allocate anywhere.
+ */
+ if (l2nb > bmp->db_agl2size) {
+ IWRITE_LOCK(ipbmap);
+
+ rc = dbAllocAny(bmp, nblocks, l2nb, results);
+ if (rc == 0) {
+ DBALLOC(bmp->db_DBmap, bmp->db_mapsize, *results,
+ nblocks);
+ }
+
+ goto write_unlock;
+ }
+
+ /*
+ * If no hint, let dbNextAG recommend an allocation group
+ */
+ if (hint == 0)
+ goto pref_ag;
+
+ /* we would like to allocate close to the hint. adjust the
+ * hint to the block following the hint since the allocators
+ * will start looking for free space starting at this point.
+ */
+ blkno = hint + 1;
+
+ if (blkno >= bmp->db_mapsize)
+ goto pref_ag;
+
+ agno = blkno >> bmp->db_agl2size;
+
+ /* check if blkno crosses over into a new allocation group.
+ * if so, check if we should allow allocations within this
+ * allocation group.
+ */
+ if ((blkno & (bmp->db_agsize - 1)) == 0)
+ /* check if the AG is currenly being written to.
+ * if so, call dbNextAG() to find a non-busy
+ * AG with sufficient free space.
+ */
+ if (atomic_read(&bmp->db_active[agno]))
+ goto pref_ag;
+
+ /* check if the allocation request size can be satisfied from a
+ * single dmap. if so, try to allocate from the dmap containing
+ * the hint using a tiered strategy.
+ */
+ if (nblocks <= BPERDMAP) {
+ IREAD_LOCK(ipbmap);
+
+ /* get the buffer for the dmap containing the hint.
+ */
+ rc = -EIO;
+ lblkno = BLKTODMAP(blkno, bmp->db_l2nbperpage);
+ mp = read_metapage(ipbmap, lblkno, PSIZE, 0);
+ if (mp == NULL)
+ goto read_unlock;
+
+ dp = (struct dmap *) mp->data;
+
+ /* first, try to satisfy the allocation request with the
+ * blocks beginning at the hint.
+ */
+ if ((rc = dbAllocNext(bmp, dp, blkno, (int) nblocks))
+ != -ENOSPC) {
+ if (rc == 0) {
+ *results = blkno;
+ DBALLOC(bmp->db_DBmap, bmp->db_mapsize,
+ *results, nblocks);
+ mark_metapage_dirty(mp);
+ }
+
+ release_metapage(mp);
+ goto read_unlock;
+ }
+
+ writers = atomic_read(&bmp->db_active[agno]);
+ if ((writers > 1) ||
+ ((writers == 1) && (JFS_IP(ip)->active_ag != agno))) {
+ /*
+ * Someone else is writing in this allocation
+ * group. To avoid fragmenting, try another ag
+ */
+ release_metapage(mp);
+ IREAD_UNLOCK(ipbmap);
+ goto pref_ag;
+ }
+
+ /* next, try to satisfy the allocation request with blocks
+ * near the hint.
+ */
+ if ((rc =
+ dbAllocNear(bmp, dp, blkno, (int) nblocks, l2nb, results))
+ != -ENOSPC) {
+ if (rc == 0) {
+ DBALLOC(bmp->db_DBmap, bmp->db_mapsize,
+ *results, nblocks);
+ mark_metapage_dirty(mp);
+ }
+
+ release_metapage(mp);
+ goto read_unlock;
+ }
+
+ /* try to satisfy the allocation request with blocks within
+ * the same dmap as the hint.
+ */
+ if ((rc = dbAllocDmapLev(bmp, dp, (int) nblocks, l2nb, results))
+ != -ENOSPC) {
+ if (rc == 0) {
+ DBALLOC(bmp->db_DBmap, bmp->db_mapsize,
+ *results, nblocks);
+ mark_metapage_dirty(mp);
+ }
+
+ release_metapage(mp);
+ goto read_unlock;
+ }
+
+ release_metapage(mp);
+ IREAD_UNLOCK(ipbmap);
+ }
+
+ /* try to satisfy the allocation request with blocks within
+ * the same allocation group as the hint.
+ */
+ IWRITE_LOCK(ipbmap);
+ if ((rc = dbAllocAG(bmp, agno, nblocks, l2nb, results))
+ != -ENOSPC) {
+ if (rc == 0)
+ DBALLOC(bmp->db_DBmap, bmp->db_mapsize,
+ *results, nblocks);
+ goto write_unlock;
+ }
+ IWRITE_UNLOCK(ipbmap);
+
+
+ pref_ag:
+ /*
+ * Let dbNextAG recommend a preferred allocation group
+ */
+ agno = dbNextAG(ipbmap);
+ IWRITE_LOCK(ipbmap);
+
+ /* Try to allocate within this allocation group. if that fails, try to
+ * allocate anywhere in the map.
+ */
+ if ((rc = dbAllocAG(bmp, agno, nblocks, l2nb, results)) == -ENOSPC)
+ rc = dbAllocAny(bmp, nblocks, l2nb, results);
+ if (rc == 0) {
+ DBALLOC(bmp->db_DBmap, bmp->db_mapsize, *results, nblocks);
+ }
+
+ write_unlock:
+ IWRITE_UNLOCK(ipbmap);
+
+ return (rc);
+
+ read_unlock:
+ IREAD_UNLOCK(ipbmap);
+
+ return (rc);
+}
+
+#ifdef _NOTYET
+/*
+ * NAME: dbAllocExact()
+ *
+ * FUNCTION: try to allocate the requested extent;
+ *
+ * PARAMETERS:
+ * ip - pointer to in-core inode;
+ * blkno - extent address;
+ * nblocks - extent length;
+ *
+ * RETURN VALUES:
+ * 0 - success
+ * -ENOSPC - insufficient disk resources
+ * -EIO - i/o error
+ */
+int dbAllocExact(struct inode *ip, s64 blkno, int nblocks)
+{
+ int rc;
+ struct inode *ipbmap = JFS_SBI(ip->i_sb)->ipbmap;
+ struct bmap *bmp = JFS_SBI(ip->i_sb)->bmap;
+ struct dmap *dp;
+ s64 lblkno;
+ struct metapage *mp;
+
+ IREAD_LOCK(ipbmap);
+
+ /*
+ * validate extent request:
+ *
+ * note: defragfs policy:
+ * max 64 blocks will be moved.
+ * allocation request size must be satisfied from a single dmap.
+ */
+ if (nblocks <= 0 || nblocks > BPERDMAP || blkno >= bmp->db_mapsize) {
+ IREAD_UNLOCK(ipbmap);
+ return -EINVAL;
+ }
+
+ if (nblocks > ((s64) 1 << bmp->db_maxfreebud)) {
+ /* the free space is no longer available */
+ IREAD_UNLOCK(ipbmap);
+ return -ENOSPC;
+ }
+
+ /* read in the dmap covering the extent */
+ lblkno = BLKTODMAP(blkno, bmp->db_l2nbperpage);
+ mp = read_metapage(ipbmap, lblkno, PSIZE, 0);
+ if (mp == NULL) {
+ IREAD_UNLOCK(ipbmap);
+ return -EIO;
+ }
+ dp = (struct dmap *) mp->data;
+
+ /* try to allocate the requested extent */
+ rc = dbAllocNext(bmp, dp, blkno, nblocks);
+
+ IREAD_UNLOCK(ipbmap);
+
+ if (rc == 0) {
+ DBALLOC(bmp->db_DBmap, bmp->db_mapsize, blkno, nblocks);
+ mark_metapage_dirty(mp);
+ }
+ release_metapage(mp);
+
+ return (rc);
+}
+#endif /* _NOTYET */
+
+/*
+ * NAME: dbReAlloc()
+ *
+ * FUNCTION: attempt to extend a current allocation by a specified
+ * number of blocks.
+ *
+ * this routine attempts to satisfy the allocation request
+ * by first trying to extend the existing allocation in
+ * place by allocating the additional blocks as the blocks
+ * immediately following the current allocation. if these
+ * blocks are not available, this routine will attempt to
+ * allocate a new set of contiguous blocks large enough
+ * to cover the existing allocation plus the additional
+ * number of blocks required.
+ *
+ * PARAMETERS:
+ * ip - pointer to in-core inode requiring allocation.
+ * blkno - starting block of the current allocation.
+ * nblocks - number of contiguous blocks within the current
+ * allocation.
+ * addnblocks - number of blocks to add to the allocation.
+ * results - on successful return, set to the starting block number
+ * of the existing allocation if the existing allocation
+ * was extended in place or to a newly allocated contiguous
+ * range if the existing allocation could not be extended
+ * in place.
+ *
+ * RETURN VALUES:
+ * 0 - success
+ * -ENOSPC - insufficient disk resources
+ * -EIO - i/o error
+ */
+int
+dbReAlloc(struct inode *ip,
+ s64 blkno, s64 nblocks, s64 addnblocks, s64 * results)
+{
+ int rc;
+
+ /* try to extend the allocation in place.
+ */
+ if ((rc = dbExtend(ip, blkno, nblocks, addnblocks)) == 0) {
+ *results = blkno;
+ return (0);
+ } else {
+ if (rc != -ENOSPC)
+ return (rc);
+ }
+
+ /* could not extend the allocation in place, so allocate a
+ * new set of blocks for the entire request (i.e. try to get
+ * a range of contiguous blocks large enough to cover the
+ * existing allocation plus the additional blocks.)
+ */
+ return (dbAlloc
+ (ip, blkno + nblocks - 1, addnblocks + nblocks, results));
+}
+
+
+/*
+ * NAME: dbExtend()
+ *
+ * FUNCTION: attempt to extend a current allocation by a specified
+ * number of blocks.
+ *
+ * this routine attempts to satisfy the allocation request
+ * by first trying to extend the existing allocation in
+ * place by allocating the additional blocks as the blocks
+ * immediately following the current allocation.
+ *
+ * PARAMETERS:
+ * ip - pointer to in-core inode requiring allocation.
+ * blkno - starting block of the current allocation.
+ * nblocks - number of contiguous blocks within the current
+ * allocation.
+ * addnblocks - number of blocks to add to the allocation.
+ *
+ * RETURN VALUES:
+ * 0 - success
+ * -ENOSPC - insufficient disk resources
+ * -EIO - i/o error
+ */
+static int dbExtend(struct inode *ip, s64 blkno, s64 nblocks, s64 addnblocks)
+{
+ struct jfs_sb_info *sbi = JFS_SBI(ip->i_sb);
+ s64 lblkno, lastblkno, extblkno;
+ uint rel_block;
+ struct metapage *mp;
+ struct dmap *dp;
+ int rc;
+ struct inode *ipbmap = sbi->ipbmap;
+ struct bmap *bmp;
+
+ /*
+ * We don't want a non-aligned extent to cross a page boundary
+ */
+ if (((rel_block = blkno & (sbi->nbperpage - 1))) &&
+ (rel_block + nblocks + addnblocks > sbi->nbperpage))
+ return -ENOSPC;
+
+ /* get the last block of the current allocation */
+ lastblkno = blkno + nblocks - 1;
+
+ /* determine the block number of the block following
+ * the existing allocation.
+ */
+ extblkno = lastblkno + 1;
+
+ IREAD_LOCK(ipbmap);
+
+ /* better be within the file system */
+ bmp = sbi->bmap;
+ if (lastblkno < 0 || lastblkno >= bmp->db_mapsize) {
+ IREAD_UNLOCK(ipbmap);
+ jfs_error(ip->i_sb,
+ "dbExtend: the block is outside the filesystem");
+ return -EIO;
+ }
+
+ /* we'll attempt to extend the current allocation in place by
+ * allocating the additional blocks as the blocks immediately
+ * following the current allocation. we only try to extend the
+ * current allocation in place if the number of additional blocks
+ * can fit into a dmap, the last block of the current allocation
+ * is not the last block of the file system, and the start of the
+ * inplace extension is not on an allocation group boundry.
+ */
+ if (addnblocks > BPERDMAP || extblkno >= bmp->db_mapsize ||
+ (extblkno & (bmp->db_agsize - 1)) == 0) {
+ IREAD_UNLOCK(ipbmap);
+ return -ENOSPC;
+ }
+
+ /* get the buffer for the dmap containing the first block
+ * of the extension.
+ */
+ lblkno = BLKTODMAP(extblkno, bmp->db_l2nbperpage);
+ mp = read_metapage(ipbmap, lblkno, PSIZE, 0);
+ if (mp == NULL) {
+ IREAD_UNLOCK(ipbmap);
+ return -EIO;
+ }
+
+ DBALLOCCK(bmp->db_DBmap, bmp->db_mapsize, blkno, nblocks);
+ dp = (struct dmap *) mp->data;
+
+ /* try to allocate the blocks immediately following the
+ * current allocation.
+ */
+ rc = dbAllocNext(bmp, dp, extblkno, (int) addnblocks);
+
+ IREAD_UNLOCK(ipbmap);
+
+ /* were we successful ? */
+ if (rc == 0) {
+ DBALLOC(bmp->db_DBmap, bmp->db_mapsize, extblkno,
+ addnblocks);
+ write_metapage(mp);
+ } else
+ /* we were not successful */
+ release_metapage(mp);
+
+
+ return (rc);
+}
+
+
+/*
+ * NAME: dbAllocNext()
+ *
+ * FUNCTION: attempt to allocate the blocks of the specified block
+ * range within a dmap.
+ *
+ * PARAMETERS:
+ * bmp - pointer to bmap descriptor
+ * dp - pointer to dmap.
+ * blkno - starting block number of the range.
+ * nblocks - number of contiguous free blocks of the range.
+ *
+ * RETURN VALUES:
+ * 0 - success
+ * -ENOSPC - insufficient disk resources
+ * -EIO - i/o error
+ *
+ * serialization: IREAD_LOCK(ipbmap) held on entry/exit;
+ */
+static int dbAllocNext(struct bmap * bmp, struct dmap * dp, s64 blkno,
+ int nblocks)
+{
+ int dbitno, word, rembits, nb, nwords, wbitno, nw;
+ int l2size;
+ s8 *leaf;
+ u32 mask;
+
+ if (dp->tree.leafidx != cpu_to_le32(LEAFIND)) {
+ jfs_error(bmp->db_ipbmap->i_sb,
+ "dbAllocNext: Corrupt dmap page");
+ return -EIO;
+ }
+
+ /* pick up a pointer to the leaves of the dmap tree.
+ */
+ leaf = dp->tree.stree + le32_to_cpu(dp->tree.leafidx);
+
+ /* determine the bit number and word within the dmap of the
+ * starting block.
+ */
+ dbitno = blkno & (BPERDMAP - 1);
+ word = dbitno >> L2DBWORD;
+
+ /* check if the specified block range is contained within
+ * this dmap.
+ */
+ if (dbitno + nblocks > BPERDMAP)
+ return -ENOSPC;
+
+ /* check if the starting leaf indicates that anything
+ * is free.
+ */
+ if (leaf[word] == NOFREE)
+ return -ENOSPC;
+
+ /* check the dmaps words corresponding to block range to see
+ * if the block range is free. not all bits of the first and
+ * last words may be contained within the block range. if this
+ * is the case, we'll work against those words (i.e. partial first
+ * and/or last) on an individual basis (a single pass) and examine
+ * the actual bits to determine if they are free. a single pass
+ * will be used for all dmap words fully contained within the
+ * specified range. within this pass, the leaves of the dmap
+ * tree will be examined to determine if the blocks are free. a
+ * single leaf may describe the free space of multiple dmap
+ * words, so we may visit only a subset of the actual leaves
+ * corresponding to the dmap words of the block range.
+ */
+ for (rembits = nblocks; rembits > 0; rembits -= nb, dbitno += nb) {
+ /* determine the bit number within the word and
+ * the number of bits within the word.
+ */
+ wbitno = dbitno & (DBWORD - 1);
+ nb = min(rembits, DBWORD - wbitno);
+
+ /* check if only part of the word is to be examined.
+ */
+ if (nb < DBWORD) {
+ /* check if the bits are free.
+ */
+ mask = (ONES << (DBWORD - nb) >> wbitno);
+ if ((mask & ~le32_to_cpu(dp->wmap[word])) != mask)
+ return -ENOSPC;
+
+ word += 1;
+ } else {
+ /* one or more dmap words are fully contained
+ * within the block range. determine how many
+ * words and how many bits.
+ */
+ nwords = rembits >> L2DBWORD;
+ nb = nwords << L2DBWORD;
+
+ /* now examine the appropriate leaves to determine
+ * if the blocks are free.
+ */
+ while (nwords > 0) {
+ /* does the leaf describe any free space ?
+ */
+ if (leaf[word] < BUDMIN)
+ return -ENOSPC;
+
+ /* determine the l2 number of bits provided
+ * by this leaf.
+ */
+ l2size =
+ min((int)leaf[word], NLSTOL2BSZ(nwords));
+
+ /* determine how many words were handled.
+ */
+ nw = BUDSIZE(l2size, BUDMIN);
+
+ nwords -= nw;
+ word += nw;
+ }
+ }
+ }
+
+ /* allocate the blocks.
+ */
+ return (dbAllocDmap(bmp, dp, blkno, nblocks));
+}
+
+
+/*
+ * NAME: dbAllocNear()
+ *
+ * FUNCTION: attempt to allocate a number of contiguous free blocks near
+ * a specified block (hint) within a dmap.
+ *
+ * starting with the dmap leaf that covers the hint, we'll
+ * check the next four contiguous leaves for sufficient free
+ * space. if sufficient free space is found, we'll allocate
+ * the desired free space.
+ *
+ * PARAMETERS:
+ * bmp - pointer to bmap descriptor
+ * dp - pointer to dmap.
+ * blkno - block number to allocate near.
+ * nblocks - actual number of contiguous free blocks desired.
+ * l2nb - log2 number of contiguous free blocks desired.
+ * results - on successful return, set to the starting block number
+ * of the newly allocated range.
+ *
+ * RETURN VALUES:
+ * 0 - success
+ * -ENOSPC - insufficient disk resources
+ * -EIO - i/o error
+ *
+ * serialization: IREAD_LOCK(ipbmap) held on entry/exit;
+ */
+static int
+dbAllocNear(struct bmap * bmp,
+ struct dmap * dp, s64 blkno, int nblocks, int l2nb, s64 * results)
+{
+ int word, lword, rc;
+ s8 *leaf;
+
+ if (dp->tree.leafidx != cpu_to_le32(LEAFIND)) {
+ jfs_error(bmp->db_ipbmap->i_sb,
+ "dbAllocNear: Corrupt dmap page");
+ return -EIO;
+ }
+
+ leaf = dp->tree.stree + le32_to_cpu(dp->tree.leafidx);
+
+ /* determine the word within the dmap that holds the hint
+ * (i.e. blkno). also, determine the last word in the dmap
+ * that we'll include in our examination.
+ */
+ word = (blkno & (BPERDMAP - 1)) >> L2DBWORD;
+ lword = min(word + 4, LPERDMAP);
+
+ /* examine the leaves for sufficient free space.
+ */
+ for (; word < lword; word++) {
+ /* does the leaf describe sufficient free space ?
+ */
+ if (leaf[word] < l2nb)
+ continue;
+
+ /* determine the block number within the file system
+ * of the first block described by this dmap word.
+ */
+ blkno = le64_to_cpu(dp->start) + (word << L2DBWORD);
+
+ /* if not all bits of the dmap word are free, get the
+ * starting bit number within the dmap word of the required
+ * string of free bits and adjust the block number with the
+ * value.
+ */
+ if (leaf[word] < BUDMIN)
+ blkno +=
+ dbFindBits(le32_to_cpu(dp->wmap[word]), l2nb);
+
+ /* allocate the blocks.
+ */
+ if ((rc = dbAllocDmap(bmp, dp, blkno, nblocks)) == 0)
+ *results = blkno;
+
+ return (rc);
+ }
+
+ return -ENOSPC;
+}
+
+
+/*
+ * NAME: dbAllocAG()
+ *
+ * FUNCTION: attempt to allocate the specified number of contiguous
+ * free blocks within the specified allocation group.
+ *
+ * unless the allocation group size is equal to the number
+ * of blocks per dmap, the dmap control pages will be used to
+ * find the required free space, if available. we start the
+ * search at the highest dmap control page level which
+ * distinctly describes the allocation group's free space
+ * (i.e. the highest level at which the allocation group's
+ * free space is not mixed in with that of any other group).
+ * in addition, we start the search within this level at a
+ * height of the dmapctl dmtree at which the nodes distinctly
+ * describe the allocation group's free space. at this height,
+ * the allocation group's free space may be represented by 1
+ * or two sub-trees, depending on the allocation group size.
+ * we search the top nodes of these subtrees left to right for
+ * sufficient free space. if sufficient free space is found,
+ * the subtree is searched to find the leftmost leaf that
+ * has free space. once we have made it to the leaf, we
+ * move the search to the next lower level dmap control page
+ * corresponding to this leaf. we continue down the dmap control
+ * pages until we find the dmap that contains or starts the
+ * sufficient free space and we allocate at this dmap.
+ *
+ * if the allocation group size is equal to the dmap size,
+ * we'll start at the dmap corresponding to the allocation
+ * group and attempt the allocation at this level.
+ *
+ * the dmap control page search is also not performed if the
+ * allocation group is completely free and we go to the first
+ * dmap of the allocation group to do the allocation. this is
+ * done because the allocation group may be part (not the first
+ * part) of a larger binary buddy system, causing the dmap
+ * control pages to indicate no free space (NOFREE) within
+ * the allocation group.
+ *
+ * PARAMETERS:
+ * bmp - pointer to bmap descriptor
+ * agno - allocation group number.
+ * nblocks - actual number of contiguous free blocks desired.
+ * l2nb - log2 number of contiguous free blocks desired.
+ * results - on successful return, set to the starting block number
+ * of the newly allocated range.
+ *
+ * RETURN VALUES:
+ * 0 - success
+ * -ENOSPC - insufficient disk resources
+ * -EIO - i/o error
+ *
+ * note: IWRITE_LOCK(ipmap) held on entry/exit;
+ */
+static int
+dbAllocAG(struct bmap * bmp, int agno, s64 nblocks, int l2nb, s64 * results)
+{
+ struct metapage *mp;
+ struct dmapctl *dcp;
+ int rc, ti, i, k, m, n, agperlev;
+ s64 blkno, lblkno;
+ int budmin;
+
+ /* allocation request should not be for more than the
+ * allocation group size.
+ */
+ if (l2nb > bmp->db_agl2size) {
+ jfs_error(bmp->db_ipbmap->i_sb,
+ "dbAllocAG: allocation request is larger than the "
+ "allocation group size");
+ return -EIO;
+ }
+
+ /* determine the starting block number of the allocation
+ * group.
+ */
+ blkno = (s64) agno << bmp->db_agl2size;
+
+ /* check if the allocation group size is the minimum allocation
+ * group size or if the allocation group is completely free. if
+ * the allocation group size is the minimum size of BPERDMAP (i.e.
+ * 1 dmap), there is no need to search the dmap control page (below)
+ * that fully describes the allocation group since the allocation
+ * group is already fully described by a dmap. in this case, we
+ * just call dbAllocCtl() to search the dmap tree and allocate the
+ * required space if available.
+ *
+ * if the allocation group is completely free, dbAllocCtl() is
+ * also called to allocate the required space. this is done for
+ * two reasons. first, it makes no sense searching the dmap control
+ * pages for free space when we know that free space exists. second,
+ * the dmap control pages may indicate that the allocation group
+ * has no free space if the allocation group is part (not the first
+ * part) of a larger binary buddy system.
+ */
+ if (bmp->db_agsize == BPERDMAP
+ || bmp->db_agfree[agno] == bmp->db_agsize) {
+ rc = dbAllocCtl(bmp, nblocks, l2nb, blkno, results);
+ if ((rc == -ENOSPC) &&
+ (bmp->db_agfree[agno] == bmp->db_agsize)) {
+ printk(KERN_ERR "blkno = %Lx, blocks = %Lx\n",
+ (unsigned long long) blkno,
+ (unsigned long long) nblocks);
+ jfs_error(bmp->db_ipbmap->i_sb,
+ "dbAllocAG: dbAllocCtl failed in free AG");
+ }
+ return (rc);
+ }
+
+ /* the buffer for the dmap control page that fully describes the
+ * allocation group.
+ */
+ lblkno = BLKTOCTL(blkno, bmp->db_l2nbperpage, bmp->db_aglevel);
+ mp = read_metapage(bmp->db_ipbmap, lblkno, PSIZE, 0);
+ if (mp == NULL)
+ return -EIO;
+ dcp = (struct dmapctl *) mp->data;
+ budmin = dcp->budmin;
+
+ if (dcp->leafidx != cpu_to_le32(CTLLEAFIND)) {
+ jfs_error(bmp->db_ipbmap->i_sb,
+ "dbAllocAG: Corrupt dmapctl page");
+ release_metapage(mp);
+ return -EIO;
+ }
+
+ /* search the subtree(s) of the dmap control page that describes
+ * the allocation group, looking for sufficient free space. to begin,
+ * determine how many allocation groups are represented in a dmap
+ * control page at the control page level (i.e. L0, L1, L2) that
+ * fully describes an allocation group. next, determine the starting
+ * tree index of this allocation group within the control page.
+ */
+ agperlev =
+ (1 << (L2LPERCTL - (bmp->db_agheigth << 1))) / bmp->db_agwidth;
+ ti = bmp->db_agstart + bmp->db_agwidth * (agno & (agperlev - 1));
+
+ /* dmap control page trees fan-out by 4 and a single allocation
+ * group may be described by 1 or 2 subtrees within the ag level
+ * dmap control page, depending upon the ag size. examine the ag's
+ * subtrees for sufficient free space, starting with the leftmost
+ * subtree.
+ */
+ for (i = 0; i < bmp->db_agwidth; i++, ti++) {
+ /* is there sufficient free space ?
+ */
+ if (l2nb > dcp->stree[ti])
+ continue;
+
+ /* sufficient free space found in a subtree. now search down
+ * the subtree to find the leftmost leaf that describes this
+ * free space.
+ */
+ for (k = bmp->db_agheigth; k > 0; k--) {
+ for (n = 0, m = (ti << 2) + 1; n < 4; n++) {
+ if (l2nb <= dcp->stree[m + n]) {
+ ti = m + n;
+ break;
+ }
+ }
+ if (n == 4) {
+ jfs_error(bmp->db_ipbmap->i_sb,
+ "dbAllocAG: failed descending stree");
+ release_metapage(mp);
+ return -EIO;
+ }
+ }
+
+ /* determine the block number within the file system
+ * that corresponds to this leaf.
+ */
+ if (bmp->db_aglevel == 2)
+ blkno = 0;
+ else if (bmp->db_aglevel == 1)
+ blkno &= ~(MAXL1SIZE - 1);
+ else /* bmp->db_aglevel == 0 */
+ blkno &= ~(MAXL0SIZE - 1);
+
+ blkno +=
+ ((s64) (ti - le32_to_cpu(dcp->leafidx))) << budmin;
+
+ /* release the buffer in preparation for going down
+ * the next level of dmap control pages.
+ */
+ release_metapage(mp);
+
+ /* check if we need to continue to search down the lower
+ * level dmap control pages. we need to if the number of
+ * blocks required is less than maximum number of blocks
+ * described at the next lower level.
+ */
+ if (l2nb < budmin) {
+
+ /* search the lower level dmap control pages to get
+ * the starting block number of the the dmap that
+ * contains or starts off the free space.
+ */
+ if ((rc =
+ dbFindCtl(bmp, l2nb, bmp->db_aglevel - 1,
+ &blkno))) {
+ if (rc == -ENOSPC) {
+ jfs_error(bmp->db_ipbmap->i_sb,
+ "dbAllocAG: control page "
+ "inconsistent");
+ return -EIO;
+ }
+ return (rc);
+ }
+ }
+
+ /* allocate the blocks.
+ */
+ rc = dbAllocCtl(bmp, nblocks, l2nb, blkno, results);
+ if (rc == -ENOSPC) {
+ jfs_error(bmp->db_ipbmap->i_sb,
+ "dbAllocAG: unable to allocate blocks");
+ rc = -EIO;
+ }
+ return (rc);
+ }
+
+ /* no space in the allocation group. release the buffer and
+ * return -ENOSPC.
+ */
+ release_metapage(mp);
+
+ return -ENOSPC;
+}
+
+
+/*
+ * NAME: dbAllocAny()
+ *
+ * FUNCTION: attempt to allocate the specified number of contiguous
+ * free blocks anywhere in the file system.
+ *
+ * dbAllocAny() attempts to find the sufficient free space by
+ * searching down the dmap control pages, starting with the
+ * highest level (i.e. L0, L1, L2) control page. if free space
+ * large enough to satisfy the desired free space is found, the
+ * desired free space is allocated.
+ *
+ * PARAMETERS:
+ * bmp - pointer to bmap descriptor
+ * nblocks - actual number of contiguous free blocks desired.
+ * l2nb - log2 number of contiguous free blocks desired.
+ * results - on successful return, set to the starting block number
+ * of the newly allocated range.
+ *
+ * RETURN VALUES:
+ * 0 - success
+ * -ENOSPC - insufficient disk resources
+ * -EIO - i/o error
+ *
+ * serialization: IWRITE_LOCK(ipbmap) held on entry/exit;
+ */
+static int dbAllocAny(struct bmap * bmp, s64 nblocks, int l2nb, s64 * results)
+{
+ int rc;
+ s64 blkno = 0;
+
+ /* starting with the top level dmap control page, search
+ * down the dmap control levels for sufficient free space.
+ * if free space is found, dbFindCtl() returns the starting
+ * block number of the dmap that contains or starts off the
+ * range of free space.
+ */
+ if ((rc = dbFindCtl(bmp, l2nb, bmp->db_maxlevel, &blkno)))
+ return (rc);
+
+ /* allocate the blocks.
+ */
+ rc = dbAllocCtl(bmp, nblocks, l2nb, blkno, results);
+ if (rc == -ENOSPC) {
+ jfs_error(bmp->db_ipbmap->i_sb,
+ "dbAllocAny: unable to allocate blocks");
+ return -EIO;
+ }
+ return (rc);
+}
+
+
+/*
+ * NAME: dbFindCtl()
+ *
+ * FUNCTION: starting at a specified dmap control page level and block
+ * number, search down the dmap control levels for a range of
+ * contiguous free blocks large enough to satisfy an allocation
+ * request for the specified number of free blocks.
+ *
+ * if sufficient contiguous free blocks are found, this routine
+ * returns the starting block number within a dmap page that
+ * contains or starts a range of contiqious free blocks that
+ * is sufficient in size.
+ *
+ * PARAMETERS:
+ * bmp - pointer to bmap descriptor
+ * level - starting dmap control page level.
+ * l2nb - log2 number of contiguous free blocks desired.
+ * *blkno - on entry, starting block number for conducting the search.
+ * on successful return, the first block within a dmap page
+ * that contains or starts a range of contiguous free blocks.
+ *
+ * RETURN VALUES:
+ * 0 - success
+ * -ENOSPC - insufficient disk resources
+ * -EIO - i/o error
+ *
+ * serialization: IWRITE_LOCK(ipbmap) held on entry/exit;
+ */
+static int dbFindCtl(struct bmap * bmp, int l2nb, int level, s64 * blkno)
+{
+ int rc, leafidx, lev;
+ s64 b, lblkno;
+ struct dmapctl *dcp;
+ int budmin;
+ struct metapage *mp;
+
+ /* starting at the specified dmap control page level and block
+ * number, search down the dmap control levels for the starting
+ * block number of a dmap page that contains or starts off
+ * sufficient free blocks.
+ */
+ for (lev = level, b = *blkno; lev >= 0; lev--) {
+ /* get the buffer of the dmap control page for the block
+ * number and level (i.e. L0, L1, L2).
+ */
+ lblkno = BLKTOCTL(b, bmp->db_l2nbperpage, lev);
+ mp = read_metapage(bmp->db_ipbmap, lblkno, PSIZE, 0);
+ if (mp == NULL)
+ return -EIO;
+ dcp = (struct dmapctl *) mp->data;
+ budmin = dcp->budmin;
+
+ if (dcp->leafidx != cpu_to_le32(CTLLEAFIND)) {
+ jfs_error(bmp->db_ipbmap->i_sb,
+ "dbFindCtl: Corrupt dmapctl page");
+ release_metapage(mp);
+ return -EIO;
+ }
+
+ /* search the tree within the dmap control page for
+ * sufficent free space. if sufficient free space is found,
+ * dbFindLeaf() returns the index of the leaf at which
+ * free space was found.
+ */
+ rc = dbFindLeaf((dmtree_t *) dcp, l2nb, &leafidx);
+
+ /* release the buffer.
+ */
+ release_metapage(mp);
+
+ /* space found ?
+ */
+ if (rc) {
+ if (lev != level) {
+ jfs_error(bmp->db_ipbmap->i_sb,
+ "dbFindCtl: dmap inconsistent");
+ return -EIO;
+ }
+ return -ENOSPC;
+ }
+
+ /* adjust the block number to reflect the location within
+ * the dmap control page (i.e. the leaf) at which free
+ * space was found.
+ */
+ b += (((s64) leafidx) << budmin);
+
+ /* we stop the search at this dmap control page level if
+ * the number of blocks required is greater than or equal
+ * to the maximum number of blocks described at the next
+ * (lower) level.
+ */
+ if (l2nb >= budmin)
+ break;
+ }
+
+ *blkno = b;
+ return (0);
+}
+
+
+/*
+ * NAME: dbAllocCtl()
+ *
+ * FUNCTION: attempt to allocate a specified number of contiguous
+ * blocks starting within a specific dmap.
+ *
+ * this routine is called by higher level routines that search
+ * the dmap control pages above the actual dmaps for contiguous
+ * free space. the result of successful searches by these
+ * routines are the starting block numbers within dmaps, with
+ * the dmaps themselves containing the desired contiguous free
+ * space or starting a contiguous free space of desired size
+ * that is made up of the blocks of one or more dmaps. these
+ * calls should not fail due to insufficent resources.
+ *
+ * this routine is called in some cases where it is not known
+ * whether it will fail due to insufficient resources. more
+ * specifically, this occurs when allocating from an allocation
+ * group whose size is equal to the number of blocks per dmap.
+ * in this case, the dmap control pages are not examined prior
+ * to calling this routine (to save pathlength) and the call
+ * might fail.
+ *
+ * for a request size that fits within a dmap, this routine relies
+ * upon the dmap's dmtree to find the requested contiguous free
+ * space. for request sizes that are larger than a dmap, the
+ * requested free space will start at the first block of the
+ * first dmap (i.e. blkno).
+ *
+ * PARAMETERS:
+ * bmp - pointer to bmap descriptor
+ * nblocks - actual number of contiguous free blocks to allocate.
+ * l2nb - log2 number of contiguous free blocks to allocate.
+ * blkno - starting block number of the dmap to start the allocation
+ * from.
+ * results - on successful return, set to the starting block number
+ * of the newly allocated range.
+ *
+ * RETURN VALUES:
+ * 0 - success
+ * -ENOSPC - insufficient disk resources
+ * -EIO - i/o error
+ *
+ * serialization: IWRITE_LOCK(ipbmap) held on entry/exit;
+ */
+static int
+dbAllocCtl(struct bmap * bmp, s64 nblocks, int l2nb, s64 blkno, s64 * results)
+{
+ int rc, nb;
+ s64 b, lblkno, n;
+ struct metapage *mp;
+ struct dmap *dp;
+
+ /* check if the allocation request is confined to a single dmap.
+ */
+ if (l2nb <= L2BPERDMAP) {
+ /* get the buffer for the dmap.
+ */
+ lblkno = BLKTODMAP(blkno, bmp->db_l2nbperpage);
+ mp = read_metapage(bmp->db_ipbmap, lblkno, PSIZE, 0);
+ if (mp == NULL)
+ return -EIO;
+ dp = (struct dmap *) mp->data;
+
+ /* try to allocate the blocks.
+ */
+ rc = dbAllocDmapLev(bmp, dp, (int) nblocks, l2nb, results);
+ if (rc == 0)
+ mark_metapage_dirty(mp);
+
+ release_metapage(mp);
+
+ return (rc);
+ }
+
+ /* allocation request involving multiple dmaps. it must start on
+ * a dmap boundary.
+ */
+ assert((blkno & (BPERDMAP - 1)) == 0);
+
+ /* allocate the blocks dmap by dmap.
+ */
+ for (n = nblocks, b = blkno; n > 0; n -= nb, b += nb) {
+ /* get the buffer for the dmap.
+ */
+ lblkno = BLKTODMAP(b, bmp->db_l2nbperpage);
+ mp = read_metapage(bmp->db_ipbmap, lblkno, PSIZE, 0);
+ if (mp == NULL) {
+ rc = -EIO;
+ goto backout;
+ }
+ dp = (struct dmap *) mp->data;
+
+ /* the dmap better be all free.
+ */
+ if (dp->tree.stree[ROOT] != L2BPERDMAP) {
+ release_metapage(mp);
+ jfs_error(bmp->db_ipbmap->i_sb,
+ "dbAllocCtl: the dmap is not all free");
+ rc = -EIO;
+ goto backout;
+ }
+
+ /* determine how many blocks to allocate from this dmap.
+ */
+ nb = min(n, (s64)BPERDMAP);
+
+ /* allocate the blocks from the dmap.
+ */
+ if ((rc = dbAllocDmap(bmp, dp, b, nb))) {
+ release_metapage(mp);
+ goto backout;
+ }
+
+ /* write the buffer.
+ */
+ write_metapage(mp);
+ }
+
+ /* set the results (starting block number) and return.
+ */
+ *results = blkno;
+ return (0);
+
+ /* something failed in handling an allocation request involving
+ * multiple dmaps. we'll try to clean up by backing out any
+ * allocation that has already happened for this request. if
+ * we fail in backing out the allocation, we'll mark the file
+ * system to indicate that blocks have been leaked.
+ */
+ backout:
+
+ /* try to backout the allocations dmap by dmap.
+ */
+ for (n = nblocks - n, b = blkno; n > 0;
+ n -= BPERDMAP, b += BPERDMAP) {
+ /* get the buffer for this dmap.
+ */
+ lblkno = BLKTODMAP(b, bmp->db_l2nbperpage);
+ mp = read_metapage(bmp->db_ipbmap, lblkno, PSIZE, 0);
+ if (mp == NULL) {
+ /* could not back out. mark the file system
+ * to indicate that we have leaked blocks.
+ */
+ jfs_error(bmp->db_ipbmap->i_sb,
+ "dbAllocCtl: I/O Error: Block Leakage.");
+ continue;
+ }
+ dp = (struct dmap *) mp->data;
+
+ /* free the blocks is this dmap.
+ */
+ if (dbFreeDmap(bmp, dp, b, BPERDMAP)) {
+ /* could not back out. mark the file system
+ * to indicate that we have leaked blocks.
+ */
+ release_metapage(mp);
+ jfs_error(bmp->db_ipbmap->i_sb,
+ "dbAllocCtl: Block Leakage.");
+ continue;
+ }
+
+ /* write the buffer.
+ */
+ write_metapage(mp);
+ }
+
+ return (rc);
+}
+
+
+/*
+ * NAME: dbAllocDmapLev()
+ *
+ * FUNCTION: attempt to allocate a specified number of contiguous blocks
+ * from a specified dmap.
+ *
+ * this routine checks if the contiguous blocks are available.
+ * if so, nblocks of blocks are allocated; otherwise, ENOSPC is
+ * returned.
+ *
+ * PARAMETERS:
+ * mp - pointer to bmap descriptor
+ * dp - pointer to dmap to attempt to allocate blocks from.
+ * l2nb - log2 number of contiguous block desired.
+ * nblocks - actual number of contiguous block desired.
+ * results - on successful return, set to the starting block number
+ * of the newly allocated range.
+ *
+ * RETURN VALUES:
+ * 0 - success
+ * -ENOSPC - insufficient disk resources
+ * -EIO - i/o error
+ *
+ * serialization: IREAD_LOCK(ipbmap), e.g., from dbAlloc(), or
+ * IWRITE_LOCK(ipbmap), e.g., dbAllocCtl(), held on entry/exit;
+ */
+static int
+dbAllocDmapLev(struct bmap * bmp,
+ struct dmap * dp, int nblocks, int l2nb, s64 * results)
+{
+ s64 blkno;
+ int leafidx, rc;
+
+ /* can't be more than a dmaps worth of blocks */
+ assert(l2nb <= L2BPERDMAP);
+
+ /* search the tree within the dmap page for sufficient
+ * free space. if sufficient free space is found, dbFindLeaf()
+ * returns the index of the leaf at which free space was found.
+ */
+ if (dbFindLeaf((dmtree_t *) & dp->tree, l2nb, &leafidx))
+ return -ENOSPC;
+
+ /* determine the block number within the file system corresponding
+ * to the leaf at which free space was found.
+ */
+ blkno = le64_to_cpu(dp->start) + (leafidx << L2DBWORD);
+
+ /* if not all bits of the dmap word are free, get the starting
+ * bit number within the dmap word of the required string of free
+ * bits and adjust the block number with this value.
+ */
+ if (dp->tree.stree[leafidx + LEAFIND] < BUDMIN)
+ blkno += dbFindBits(le32_to_cpu(dp->wmap[leafidx]), l2nb);
+
+ /* allocate the blocks */
+ if ((rc = dbAllocDmap(bmp, dp, blkno, nblocks)) == 0)
+ *results = blkno;
+
+ return (rc);
+}
+
+
+/*
+ * NAME: dbAllocDmap()
+ *
+ * FUNCTION: adjust the disk allocation map to reflect the allocation
+ * of a specified block range within a dmap.
+ *
+ * this routine allocates the specified blocks from the dmap
+ * through a call to dbAllocBits(). if the allocation of the
+ * block range causes the maximum string of free blocks within
+ * the dmap to change (i.e. the value of the root of the dmap's
+ * dmtree), this routine will cause this change to be reflected
+ * up through the appropriate levels of the dmap control pages
+ * by a call to dbAdjCtl() for the L0 dmap control page that
+ * covers this dmap.
+ *
+ * PARAMETERS:
+ * bmp - pointer to bmap descriptor
+ * dp - pointer to dmap to allocate the block range from.
+ * blkno - starting block number of the block to be allocated.
+ * nblocks - number of blocks to be allocated.
+ *
+ * RETURN VALUES:
+ * 0 - success
+ * -EIO - i/o error
+ *
+ * serialization: IREAD_LOCK(ipbmap) or IWRITE_LOCK(ipbmap) held on entry/exit;
+ */
+static int dbAllocDmap(struct bmap * bmp, struct dmap * dp, s64 blkno,
+ int nblocks)
+{
+ s8 oldroot;
+ int rc;
+
+ /* save the current value of the root (i.e. maximum free string)
+ * of the dmap tree.
+ */
+ oldroot = dp->tree.stree[ROOT];
+
+ /* allocate the specified (blocks) bits */
+ dbAllocBits(bmp, dp, blkno, nblocks);
+
+ /* if the root has not changed, done. */
+ if (dp->tree.stree[ROOT] == oldroot)
+ return (0);
+
+ /* root changed. bubble the change up to the dmap control pages.
+ * if the adjustment of the upper level control pages fails,
+ * backout the bit allocation (thus making everything consistent).
+ */
+ if ((rc = dbAdjCtl(bmp, blkno, dp->tree.stree[ROOT], 1, 0)))
+ dbFreeBits(bmp, dp, blkno, nblocks);
+
+ return (rc);
+}
+
+
+/*
+ * NAME: dbFreeDmap()
+ *
+ * FUNCTION: adjust the disk allocation map to reflect the allocation
+ * of a specified block range within a dmap.
+ *
+ * this routine frees the specified blocks from the dmap through
+ * a call to dbFreeBits(). if the deallocation of the block range
+ * causes the maximum string of free blocks within the dmap to
+ * change (i.e. the value of the root of the dmap's dmtree), this
+ * routine will cause this change to be reflected up through the
+ * appropriate levels of the dmap control pages by a call to
+ * dbAdjCtl() for the L0 dmap control page that covers this dmap.
+ *
+ * PARAMETERS:
+ * bmp - pointer to bmap descriptor
+ * dp - pointer to dmap to free the block range from.
+ * blkno - starting block number of the block to be freed.
+ * nblocks - number of blocks to be freed.
+ *
+ * RETURN VALUES:
+ * 0 - success
+ * -EIO - i/o error
+ *
+ * serialization: IREAD_LOCK(ipbmap) or IWRITE_LOCK(ipbmap) held on entry/exit;
+ */
+static int dbFreeDmap(struct bmap * bmp, struct dmap * dp, s64 blkno,
+ int nblocks)
+{
+ s8 oldroot;
+ int rc, word;
+
+ /* save the current value of the root (i.e. maximum free string)
+ * of the dmap tree.
+ */
+ oldroot = dp->tree.stree[ROOT];
+
+ /* free the specified (blocks) bits */
+ dbFreeBits(bmp, dp, blkno, nblocks);
+
+ /* if the root has not changed, done. */
+ if (dp->tree.stree[ROOT] == oldroot)
+ return (0);
+
+ /* root changed. bubble the change up to the dmap control pages.
+ * if the adjustment of the upper level control pages fails,
+ * backout the deallocation.
+ */
+ if ((rc = dbAdjCtl(bmp, blkno, dp->tree.stree[ROOT], 0, 0))) {
+ word = (blkno & (BPERDMAP - 1)) >> L2DBWORD;
+
+ /* as part of backing out the deallocation, we will have
+ * to back split the dmap tree if the deallocation caused
+ * the freed blocks to become part of a larger binary buddy
+ * system.
+ */
+ if (dp->tree.stree[word] == NOFREE)
+ dbBackSplit((dmtree_t *) & dp->tree, word);
+
+ dbAllocBits(bmp, dp, blkno, nblocks);
+ }
+
+ return (rc);
+}
+
+
+/*
+ * NAME: dbAllocBits()
+ *
+ * FUNCTION: allocate a specified block range from a dmap.
+ *
+ * this routine updates the dmap to reflect the working
+ * state allocation of the specified block range. it directly
+ * updates the bits of the working map and causes the adjustment
+ * of the binary buddy system described by the dmap's dmtree
+ * leaves to reflect the bits allocated. it also causes the
+ * dmap's dmtree, as a whole, to reflect the allocated range.
+ *
+ * PARAMETERS:
+ * bmp - pointer to bmap descriptor
+ * dp - pointer to dmap to allocate bits from.
+ * blkno - starting block number of the bits to be allocated.
+ * nblocks - number of bits to be allocated.
+ *
+ * RETURN VALUES: none
+ *
+ * serialization: IREAD_LOCK(ipbmap) or IWRITE_LOCK(ipbmap) held on entry/exit;
+ */
+static void dbAllocBits(struct bmap * bmp, struct dmap * dp, s64 blkno,
+ int nblocks)
+{
+ int dbitno, word, rembits, nb, nwords, wbitno, nw, agno;
+ dmtree_t *tp = (dmtree_t *) & dp->tree;
+ int size;
+ s8 *leaf;
+
+ /* pick up a pointer to the leaves of the dmap tree */
+ leaf = dp->tree.stree + LEAFIND;
+
+ /* determine the bit number and word within the dmap of the
+ * starting block.
+ */
+ dbitno = blkno & (BPERDMAP - 1);
+ word = dbitno >> L2DBWORD;
+
+ /* block range better be within the dmap */
+ assert(dbitno + nblocks <= BPERDMAP);
+
+ /* allocate the bits of the dmap's words corresponding to the block
+ * range. not all bits of the first and last words may be contained
+ * within the block range. if this is the case, we'll work against
+ * those words (i.e. partial first and/or last) on an individual basis
+ * (a single pass), allocating the bits of interest by hand and
+ * updating the leaf corresponding to the dmap word. a single pass
+ * will be used for all dmap words fully contained within the
+ * specified range. within this pass, the bits of all fully contained
+ * dmap words will be marked as free in a single shot and the leaves
+ * will be updated. a single leaf may describe the free space of
+ * multiple dmap words, so we may update only a subset of the actual
+ * leaves corresponding to the dmap words of the block range.
+ */
+ for (rembits = nblocks; rembits > 0; rembits -= nb, dbitno += nb) {
+ /* determine the bit number within the word and
+ * the number of bits within the word.
+ */
+ wbitno = dbitno & (DBWORD - 1);
+ nb = min(rembits, DBWORD - wbitno);
+
+ /* check if only part of a word is to be allocated.
+ */
+ if (nb < DBWORD) {
+ /* allocate (set to 1) the appropriate bits within
+ * this dmap word.
+ */
+ dp->wmap[word] |= cpu_to_le32(ONES << (DBWORD - nb)
+ >> wbitno);
+
+ /* update the leaf for this dmap word. in addition
+ * to setting the leaf value to the binary buddy max
+ * of the updated dmap word, dbSplit() will split
+ * the binary system of the leaves if need be.
+ */
+ dbSplit(tp, word, BUDMIN,
+ dbMaxBud((u8 *) & dp->wmap[word]));
+
+ word += 1;
+ } else {
+ /* one or more dmap words are fully contained
+ * within the block range. determine how many
+ * words and allocate (set to 1) the bits of these
+ * words.
+ */
+ nwords = rembits >> L2DBWORD;
+ memset(&dp->wmap[word], (int) ONES, nwords * 4);
+
+ /* determine how many bits.
+ */
+ nb = nwords << L2DBWORD;
+
+ /* now update the appropriate leaves to reflect
+ * the allocated words.
+ */
+ for (; nwords > 0; nwords -= nw) {
+ if (leaf[word] < BUDMIN) {
+ jfs_error(bmp->db_ipbmap->i_sb,
+ "dbAllocBits: leaf page "
+ "corrupt");
+ break;
+ }
+
+ /* determine what the leaf value should be
+ * updated to as the minimum of the l2 number
+ * of bits being allocated and the l2 number
+ * of bits currently described by this leaf.
+ */
+ size = min((int)leaf[word], NLSTOL2BSZ(nwords));
+
+ /* update the leaf to reflect the allocation.
+ * in addition to setting the leaf value to
+ * NOFREE, dbSplit() will split the binary
+ * system of the leaves to reflect the current
+ * allocation (size).
+ */
+ dbSplit(tp, word, size, NOFREE);
+
+ /* get the number of dmap words handled */
+ nw = BUDSIZE(size, BUDMIN);
+ word += nw;
+ }
+ }
+ }
+
+ /* update the free count for this dmap */
+ dp->nfree = cpu_to_le32(le32_to_cpu(dp->nfree) - nblocks);
+
+ BMAP_LOCK(bmp);
+
+ /* if this allocation group is completely free,
+ * update the maximum allocation group number if this allocation
+ * group is the new max.
+ */
+ agno = blkno >> bmp->db_agl2size;
+ if (agno > bmp->db_maxag)
+ bmp->db_maxag = agno;
+
+ /* update the free count for the allocation group and map */
+ bmp->db_agfree[agno] -= nblocks;
+ bmp->db_nfree -= nblocks;
+
+ BMAP_UNLOCK(bmp);
+}
+
+
+/*
+ * NAME: dbFreeBits()
+ *
+ * FUNCTION: free a specified block range from a dmap.
+ *
+ * this routine updates the dmap to reflect the working
+ * state allocation of the specified block range. it directly
+ * updates the bits of the working map and causes the adjustment
+ * of the binary buddy system described by the dmap's dmtree
+ * leaves to reflect the bits freed. it also causes the dmap's
+ * dmtree, as a whole, to reflect the deallocated range.
+ *
+ * PARAMETERS:
+ * bmp - pointer to bmap descriptor
+ * dp - pointer to dmap to free bits from.
+ * blkno - starting block number of the bits to be freed.
+ * nblocks - number of bits to be freed.
+ *
+ * RETURN VALUES: none
+ *
+ * serialization: IREAD_LOCK(ipbmap) or IWRITE_LOCK(ipbmap) held on entry/exit;
+ */
+static void dbFreeBits(struct bmap * bmp, struct dmap * dp, s64 blkno,
+ int nblocks)
+{
+ int dbitno, word, rembits, nb, nwords, wbitno, nw, agno;
+ dmtree_t *tp = (dmtree_t *) & dp->tree;
+ int size;
+
+ /* determine the bit number and word within the dmap of the
+ * starting block.
+ */
+ dbitno = blkno & (BPERDMAP - 1);
+ word = dbitno >> L2DBWORD;
+
+ /* block range better be within the dmap.
+ */
+ assert(dbitno + nblocks <= BPERDMAP);
+
+ /* free the bits of the dmaps words corresponding to the block range.
+ * not all bits of the first and last words may be contained within
+ * the block range. if this is the case, we'll work against those
+ * words (i.e. partial first and/or last) on an individual basis
+ * (a single pass), freeing the bits of interest by hand and updating
+ * the leaf corresponding to the dmap word. a single pass will be used
+ * for all dmap words fully contained within the specified range.
+ * within this pass, the bits of all fully contained dmap words will
+ * be marked as free in a single shot and the leaves will be updated. a
+ * single leaf may describe the free space of multiple dmap words,
+ * so we may update only a subset of the actual leaves corresponding
+ * to the dmap words of the block range.
+ *
+ * dbJoin() is used to update leaf values and will join the binary
+ * buddy system of the leaves if the new leaf values indicate this
+ * should be done.
+ */
+ for (rembits = nblocks; rembits > 0; rembits -= nb, dbitno += nb) {
+ /* determine the bit number within the word and
+ * the number of bits within the word.
+ */
+ wbitno = dbitno & (DBWORD - 1);
+ nb = min(rembits, DBWORD - wbitno);
+
+ /* check if only part of a word is to be freed.
+ */
+ if (nb < DBWORD) {
+ /* free (zero) the appropriate bits within this
+ * dmap word.
+ */
+ dp->wmap[word] &=
+ cpu_to_le32(~(ONES << (DBWORD - nb)
+ >> wbitno));
+
+ /* update the leaf for this dmap word.
+ */
+ dbJoin(tp, word,
+ dbMaxBud((u8 *) & dp->wmap[word]));
+
+ word += 1;
+ } else {
+ /* one or more dmap words are fully contained
+ * within the block range. determine how many
+ * words and free (zero) the bits of these words.
+ */
+ nwords = rembits >> L2DBWORD;
+ memset(&dp->wmap[word], 0, nwords * 4);
+
+ /* determine how many bits.
+ */
+ nb = nwords << L2DBWORD;
+
+ /* now update the appropriate leaves to reflect
+ * the freed words.
+ */
+ for (; nwords > 0; nwords -= nw) {
+ /* determine what the leaf value should be
+ * updated to as the minimum of the l2 number
+ * of bits being freed and the l2 (max) number
+ * of bits that can be described by this leaf.
+ */
+ size =
+ min(LITOL2BSZ
+ (word, L2LPERDMAP, BUDMIN),
+ NLSTOL2BSZ(nwords));
+
+ /* update the leaf.
+ */
+ dbJoin(tp, word, size);
+
+ /* get the number of dmap words handled.
+ */
+ nw = BUDSIZE(size, BUDMIN);
+ word += nw;
+ }
+ }
+ }
+
+ /* update the free count for this dmap.
+ */
+ dp->nfree = cpu_to_le32(le32_to_cpu(dp->nfree) + nblocks);
+
+ BMAP_LOCK(bmp);
+
+ /* update the free count for the allocation group and
+ * map.
+ */
+ agno = blkno >> bmp->db_agl2size;
+ bmp->db_nfree += nblocks;
+ bmp->db_agfree[agno] += nblocks;
+
+ /* check if this allocation group is not completely free and
+ * if it is currently the maximum (rightmost) allocation group.
+ * if so, establish the new maximum allocation group number by
+ * searching left for the first allocation group with allocation.
+ */
+ if ((bmp->db_agfree[agno] == bmp->db_agsize && agno == bmp->db_maxag) ||
+ (agno == bmp->db_numag - 1 &&
+ bmp->db_agfree[agno] == (bmp-> db_mapsize & (BPERDMAP - 1)))) {
+ while (bmp->db_maxag > 0) {
+ bmp->db_maxag -= 1;
+ if (bmp->db_agfree[bmp->db_maxag] !=
+ bmp->db_agsize)
+ break;
+ }
+
+ /* re-establish the allocation group preference if the
+ * current preference is right of the maximum allocation
+ * group.
+ */
+ if (bmp->db_agpref > bmp->db_maxag)
+ bmp->db_agpref = bmp->db_maxag;
+ }
+
+ BMAP_UNLOCK(bmp);
+}
+
+
+/*
+ * NAME: dbAdjCtl()
+ *
+ * FUNCTION: adjust a dmap control page at a specified level to reflect
+ * the change in a lower level dmap or dmap control page's
+ * maximum string of free blocks (i.e. a change in the root
+ * of the lower level object's dmtree) due to the allocation
+ * or deallocation of a range of blocks with a single dmap.
+ *
+ * on entry, this routine is provided with the new value of
+ * the lower level dmap or dmap control page root and the
+ * starting block number of the block range whose allocation
+ * or deallocation resulted in the root change. this range
+ * is respresented by a single leaf of the current dmapctl
+ * and the leaf will be updated with this value, possibly
+ * causing a binary buddy system within the leaves to be
+ * split or joined. the update may also cause the dmapctl's
+ * dmtree to be updated.
+ *
+ * if the adjustment of the dmap control page, itself, causes its
+ * root to change, this change will be bubbled up to the next dmap
+ * control level by a recursive call to this routine, specifying
+ * the new root value and the next dmap control page level to
+ * be adjusted.
+ * PARAMETERS:
+ * bmp - pointer to bmap descriptor
+ * blkno - the first block of a block range within a dmap. it is
+ * the allocation or deallocation of this block range that
+ * requires the dmap control page to be adjusted.
+ * newval - the new value of the lower level dmap or dmap control
+ * page root.
+ * alloc - TRUE if adjustment is due to an allocation.
+ * level - current level of dmap control page (i.e. L0, L1, L2) to
+ * be adjusted.
+ *
+ * RETURN VALUES:
+ * 0 - success
+ * -EIO - i/o error
+ *
+ * serialization: IREAD_LOCK(ipbmap) or IWRITE_LOCK(ipbmap) held on entry/exit;
+ */
+static int
+dbAdjCtl(struct bmap * bmp, s64 blkno, int newval, int alloc, int level)
+{
+ struct metapage *mp;
+ s8 oldroot;
+ int oldval;
+ s64 lblkno;
+ struct dmapctl *dcp;
+ int rc, leafno, ti;
+
+ /* get the buffer for the dmap control page for the specified
+ * block number and control page level.
+ */
+ lblkno = BLKTOCTL(blkno, bmp->db_l2nbperpage, level);
+ mp = read_metapage(bmp->db_ipbmap, lblkno, PSIZE, 0);
+ if (mp == NULL)
+ return -EIO;
+ dcp = (struct dmapctl *) mp->data;
+
+ if (dcp->leafidx != cpu_to_le32(CTLLEAFIND)) {
+ jfs_error(bmp->db_ipbmap->i_sb,
+ "dbAdjCtl: Corrupt dmapctl page");
+ release_metapage(mp);
+ return -EIO;
+ }
+
+ /* determine the leaf number corresponding to the block and
+ * the index within the dmap control tree.
+ */
+ leafno = BLKTOCTLLEAF(blkno, dcp->budmin);
+ ti = leafno + le32_to_cpu(dcp->leafidx);
+
+ /* save the current leaf value and the current root level (i.e.
+ * maximum l2 free string described by this dmapctl).
+ */
+ oldval = dcp->stree[ti];
+ oldroot = dcp->stree[ROOT];
+
+ /* check if this is a control page update for an allocation.
+ * if so, update the leaf to reflect the new leaf value using
+ * dbSplit(); otherwise (deallocation), use dbJoin() to udpate
+ * the leaf with the new value. in addition to updating the
+ * leaf, dbSplit() will also split the binary buddy system of
+ * the leaves, if required, and bubble new values within the
+ * dmapctl tree, if required. similarly, dbJoin() will join
+ * the binary buddy system of leaves and bubble new values up
+ * the dmapctl tree as required by the new leaf value.
+ */
+ if (alloc) {
+ /* check if we are in the middle of a binary buddy
+ * system. this happens when we are performing the
+ * first allocation out of an allocation group that
+ * is part (not the first part) of a larger binary
+ * buddy system. if we are in the middle, back split
+ * the system prior to calling dbSplit() which assumes
+ * that it is at the front of a binary buddy system.
+ */
+ if (oldval == NOFREE) {
+ dbBackSplit((dmtree_t *) dcp, leafno);
+ oldval = dcp->stree[ti];
+ }
+ dbSplit((dmtree_t *) dcp, leafno, dcp->budmin, newval);
+ } else {
+ dbJoin((dmtree_t *) dcp, leafno, newval);
+ }
+
+ /* check if the root of the current dmap control page changed due
+ * to the update and if the current dmap control page is not at
+ * the current top level (i.e. L0, L1, L2) of the map. if so (i.e.
+ * root changed and this is not the top level), call this routine
+ * again (recursion) for the next higher level of the mapping to
+ * reflect the change in root for the current dmap control page.
+ */
+ if (dcp->stree[ROOT] != oldroot) {
+ /* are we below the top level of the map. if so,
+ * bubble the root up to the next higher level.
+ */
+ if (level < bmp->db_maxlevel) {
+ /* bubble up the new root of this dmap control page to
+ * the next level.
+ */
+ if ((rc =
+ dbAdjCtl(bmp, blkno, dcp->stree[ROOT], alloc,
+ level + 1))) {
+ /* something went wrong in bubbling up the new
+ * root value, so backout the changes to the
+ * current dmap control page.
+ */
+ if (alloc) {
+ dbJoin((dmtree_t *) dcp, leafno,
+ oldval);
+ } else {
+ /* the dbJoin() above might have
+ * caused a larger binary buddy system
+ * to form and we may now be in the
+ * middle of it. if this is the case,
+ * back split the buddies.
+ */
+ if (dcp->stree[ti] == NOFREE)
+ dbBackSplit((dmtree_t *)
+ dcp, leafno);
+ dbSplit((dmtree_t *) dcp, leafno,
+ dcp->budmin, oldval);
+ }
+
+ /* release the buffer and return the error.
+ */
+ release_metapage(mp);
+ return (rc);
+ }
+ } else {
+ /* we're at the top level of the map. update
+ * the bmap control page to reflect the size
+ * of the maximum free buddy system.
+ */
+ assert(level == bmp->db_maxlevel);
+ if (bmp->db_maxfreebud != oldroot) {
+ jfs_error(bmp->db_ipbmap->i_sb,
+ "dbAdjCtl: the maximum free buddy is "
+ "not the old root");
+ }
+ bmp->db_maxfreebud = dcp->stree[ROOT];
+ }
+ }
+
+ /* write the buffer.
+ */
+ write_metapage(mp);
+
+ return (0);
+}
+
+
+/*
+ * NAME: dbSplit()
+ *
+ * FUNCTION: update the leaf of a dmtree with a new value, splitting
+ * the leaf from the binary buddy system of the dmtree's
+ * leaves, as required.
+ *
+ * PARAMETERS:
+ * tp - pointer to the tree containing the leaf.
+ * leafno - the number of the leaf to be updated.
+ * splitsz - the size the binary buddy system starting at the leaf
+ * must be split to, specified as the log2 number of blocks.
+ * newval - the new value for the leaf.
+ *
+ * RETURN VALUES: none
+ *
+ * serialization: IREAD_LOCK(ipbmap) or IWRITE_LOCK(ipbmap) held on entry/exit;
+ */
+static void dbSplit(dmtree_t * tp, int leafno, int splitsz, int newval)
+{
+ int budsz;
+ int cursz;
+ s8 *leaf = tp->dmt_stree + le32_to_cpu(tp->dmt_leafidx);
+
+ /* check if the leaf needs to be split.
+ */
+ if (leaf[leafno] > tp->dmt_budmin) {
+ /* the split occurs by cutting the buddy system in half
+ * at the specified leaf until we reach the specified
+ * size. pick up the starting split size (current size
+ * - 1 in l2) and the corresponding buddy size.
+ */
+ cursz = leaf[leafno] - 1;
+ budsz = BUDSIZE(cursz, tp->dmt_budmin);
+
+ /* split until we reach the specified size.
+ */
+ while (cursz >= splitsz) {
+ /* update the buddy's leaf with its new value.
+ */
+ dbAdjTree(tp, leafno ^ budsz, cursz);
+
+ /* on to the next size and buddy.
+ */
+ cursz -= 1;
+ budsz >>= 1;
+ }
+ }
+
+ /* adjust the dmap tree to reflect the specified leaf's new
+ * value.
+ */
+ dbAdjTree(tp, leafno, newval);
+}
+
+
+/*
+ * NAME: dbBackSplit()
+ *
+ * FUNCTION: back split the binary buddy system of dmtree leaves
+ * that hold a specified leaf until the specified leaf
+ * starts its own binary buddy system.
+ *
+ * the allocators typically perform allocations at the start
+ * of binary buddy systems and dbSplit() is used to accomplish
+ * any required splits. in some cases, however, allocation
+ * may occur in the middle of a binary system and requires a
+ * back split, with the split proceeding out from the middle of
+ * the system (less efficient) rather than the start of the
+ * system (more efficient). the cases in which a back split
+ * is required are rare and are limited to the first allocation
+ * within an allocation group which is a part (not first part)
+ * of a larger binary buddy system and a few exception cases
+ * in which a previous join operation must be backed out.
+ *
+ * PARAMETERS:
+ * tp - pointer to the tree containing the leaf.
+ * leafno - the number of the leaf to be updated.
+ *
+ * RETURN VALUES: none
+ *
+ * serialization: IREAD_LOCK(ipbmap) or IWRITE_LOCK(ipbmap) held on entry/exit;
+ */
+static void dbBackSplit(dmtree_t * tp, int leafno)
+{
+ int budsz, bud, w, bsz, size;
+ int cursz;
+ s8 *leaf = tp->dmt_stree + le32_to_cpu(tp->dmt_leafidx);
+
+ /* leaf should be part (not first part) of a binary
+ * buddy system.
+ */
+ assert(leaf[leafno] == NOFREE);
+
+ /* the back split is accomplished by iteratively finding the leaf
+ * that starts the buddy system that contains the specified leaf and
+ * splitting that system in two. this iteration continues until
+ * the specified leaf becomes the start of a buddy system.
+ *
+ * determine maximum possible l2 size for the specified leaf.
+ */
+ size =
+ LITOL2BSZ(leafno, le32_to_cpu(tp->dmt_l2nleafs),
+ tp->dmt_budmin);
+
+ /* determine the number of leaves covered by this size. this
+ * is the buddy size that we will start with as we search for
+ * the buddy system that contains the specified leaf.
+ */
+ budsz = BUDSIZE(size, tp->dmt_budmin);
+
+ /* back split.
+ */
+ while (leaf[leafno] == NOFREE) {
+ /* find the leftmost buddy leaf.
+ */
+ for (w = leafno, bsz = budsz;; bsz <<= 1,
+ w = (w < bud) ? w : bud) {
+ assert(bsz < le32_to_cpu(tp->dmt_nleafs));
+
+ /* determine the buddy.
+ */
+ bud = w ^ bsz;
+
+ /* check if this buddy is the start of the system.
+ */
+ if (leaf[bud] != NOFREE) {
+ /* split the leaf at the start of the
+ * system in two.
+ */
+ cursz = leaf[bud] - 1;
+ dbSplit(tp, bud, cursz, cursz);
+ break;
+ }
+ }
+ }
+
+ assert(leaf[leafno] == size);
+}
+
+
+/*
+ * NAME: dbJoin()
+ *
+ * FUNCTION: update the leaf of a dmtree with a new value, joining
+ * the leaf with other leaves of the dmtree into a multi-leaf
+ * binary buddy system, as required.
+ *
+ * PARAMETERS:
+ * tp - pointer to the tree containing the leaf.
+ * leafno - the number of the leaf to be updated.
+ * newval - the new value for the leaf.
+ *
+ * RETURN VALUES: none
+ */
+static void dbJoin(dmtree_t * tp, int leafno, int newval)
+{
+ int budsz, buddy;
+ s8 *leaf;
+
+ /* can the new leaf value require a join with other leaves ?
+ */
+ if (newval >= tp->dmt_budmin) {
+ /* pickup a pointer to the leaves of the tree.
+ */
+ leaf = tp->dmt_stree + le32_to_cpu(tp->dmt_leafidx);
+
+ /* try to join the specified leaf into a large binary
+ * buddy system. the join proceeds by attempting to join
+ * the specified leafno with its buddy (leaf) at new value.
+ * if the join occurs, we attempt to join the left leaf
+ * of the joined buddies with its buddy at new value + 1.
+ * we continue to join until we find a buddy that cannot be
+ * joined (does not have a value equal to the size of the
+ * last join) or until all leaves have been joined into a
+ * single system.
+ *
+ * get the buddy size (number of words covered) of
+ * the new value.
+ */
+ budsz = BUDSIZE(newval, tp->dmt_budmin);
+
+ /* try to join.
+ */
+ while (budsz < le32_to_cpu(tp->dmt_nleafs)) {
+ /* get the buddy leaf.
+ */
+ buddy = leafno ^ budsz;
+
+ /* if the leaf's new value is greater than its
+ * buddy's value, we join no more.
+ */
+ if (newval > leaf[buddy])
+ break;
+
+ assert(newval == leaf[buddy]);
+
+ /* check which (leafno or buddy) is the left buddy.
+ * the left buddy gets to claim the blocks resulting
+ * from the join while the right gets to claim none.
+ * the left buddy is also eligable to participate in
+ * a join at the next higher level while the right
+ * is not.
+ *
+ */
+ if (leafno < buddy) {
+ /* leafno is the left buddy.
+ */
+ dbAdjTree(tp, buddy, NOFREE);
+ } else {
+ /* buddy is the left buddy and becomes
+ * leafno.
+ */
+ dbAdjTree(tp, leafno, NOFREE);
+ leafno = buddy;
+ }
+
+ /* on to try the next join.
+ */
+ newval += 1;
+ budsz <<= 1;
+ }
+ }
+
+ /* update the leaf value.
+ */
+ dbAdjTree(tp, leafno, newval);
+}
+
+
+/*
+ * NAME: dbAdjTree()
+ *
+ * FUNCTION: update a leaf of a dmtree with a new value, adjusting
+ * the dmtree, as required, to reflect the new leaf value.
+ * the combination of any buddies must already be done before
+ * this is called.
+ *
+ * PARAMETERS:
+ * tp - pointer to the tree to be adjusted.
+ * leafno - the number of the leaf to be updated.
+ * newval - the new value for the leaf.
+ *
+ * RETURN VALUES: none
+ */
+static void dbAdjTree(dmtree_t * tp, int leafno, int newval)
+{
+ int lp, pp, k;
+ int max;
+
+ /* pick up the index of the leaf for this leafno.
+ */
+ lp = leafno + le32_to_cpu(tp->dmt_leafidx);
+
+ /* is the current value the same as the old value ? if so,
+ * there is nothing to do.
+ */
+ if (tp->dmt_stree[lp] == newval)
+ return;
+
+ /* set the new value.
+ */
+ tp->dmt_stree[lp] = newval;
+
+ /* bubble the new value up the tree as required.
+ */
+ for (k = 0; k < le32_to_cpu(tp->dmt_height); k++) {
+ /* get the index of the first leaf of the 4 leaf
+ * group containing the specified leaf (leafno).
+ */
+ lp = ((lp - 1) & ~0x03) + 1;
+
+ /* get the index of the parent of this 4 leaf group.
+ */
+ pp = (lp - 1) >> 2;
+
+ /* determine the maximum of the 4 leaves.
+ */
+ max = TREEMAX(&tp->dmt_stree[lp]);
+
+ /* if the maximum of the 4 is the same as the
+ * parent's value, we're done.
+ */
+ if (tp->dmt_stree[pp] == max)
+ break;
+
+ /* parent gets new value.
+ */
+ tp->dmt_stree[pp] = max;
+
+ /* parent becomes leaf for next go-round.
+ */
+ lp = pp;
+ }
+}
+
+
+/*
+ * NAME: dbFindLeaf()
+ *
+ * FUNCTION: search a dmtree_t for sufficient free blocks, returning
+ * the index of a leaf describing the free blocks if
+ * sufficient free blocks are found.
+ *
+ * the search starts at the top of the dmtree_t tree and
+ * proceeds down the tree to the leftmost leaf with sufficient
+ * free space.
+ *
+ * PARAMETERS:
+ * tp - pointer to the tree to be searched.
+ * l2nb - log2 number of free blocks to search for.
+ * leafidx - return pointer to be set to the index of the leaf
+ * describing at least l2nb free blocks if sufficient
+ * free blocks are found.
+ *
+ * RETURN VALUES:
+ * 0 - success
+ * -ENOSPC - insufficient free blocks.
+ */
+static int dbFindLeaf(dmtree_t * tp, int l2nb, int *leafidx)
+{
+ int ti, n = 0, k, x = 0;
+
+ /* first check the root of the tree to see if there is
+ * sufficient free space.
+ */
+ if (l2nb > tp->dmt_stree[ROOT])
+ return -ENOSPC;
+
+ /* sufficient free space available. now search down the tree
+ * starting at the next level for the leftmost leaf that
+ * describes sufficient free space.
+ */
+ for (k = le32_to_cpu(tp->dmt_height), ti = 1;
+ k > 0; k--, ti = ((ti + n) << 2) + 1) {
+ /* search the four nodes at this level, starting from
+ * the left.
+ */
+ for (x = ti, n = 0; n < 4; n++) {
+ /* sufficient free space found. move to the next
+ * level (or quit if this is the last level).
+ */
+ if (l2nb <= tp->dmt_stree[x + n])
+ break;
+ }
+
+ /* better have found something since the higher
+ * levels of the tree said it was here.
+ */
+ assert(n < 4);
+ }
+
+ /* set the return to the leftmost leaf describing sufficient
+ * free space.
+ */
+ *leafidx = x + n - le32_to_cpu(tp->dmt_leafidx);
+
+ return (0);
+}
+
+
+/*
+ * NAME: dbFindBits()
+ *
+ * FUNCTION: find a specified number of binary buddy free bits within a
+ * dmap bitmap word value.
+ *
+ * this routine searches the bitmap value for (1 << l2nb) free
+ * bits at (1 << l2nb) alignments within the value.
+ *
+ * PARAMETERS:
+ * word - dmap bitmap word value.
+ * l2nb - number of free bits specified as a log2 number.
+ *
+ * RETURN VALUES:
+ * starting bit number of free bits.
+ */
+static int dbFindBits(u32 word, int l2nb)
+{
+ int bitno, nb;
+ u32 mask;
+
+ /* get the number of bits.
+ */
+ nb = 1 << l2nb;
+ assert(nb <= DBWORD);
+
+ /* complement the word so we can use a mask (i.e. 0s represent
+ * free bits) and compute the mask.
+ */
+ word = ~word;
+ mask = ONES << (DBWORD - nb);
+
+ /* scan the word for nb free bits at nb alignments.
+ */
+ for (bitno = 0; mask != 0; bitno += nb, mask >>= nb) {
+ if ((mask & word) == mask)
+ break;
+ }
+
+ ASSERT(bitno < 32);
+
+ /* return the bit number.
+ */
+ return (bitno);
+}
+
+
+/*
+ * NAME: dbMaxBud(u8 *cp)
+ *
+ * FUNCTION: determine the largest binary buddy string of free
+ * bits within 32-bits of the map.
+ *
+ * PARAMETERS:
+ * cp - pointer to the 32-bit value.
+ *
+ * RETURN VALUES:
+ * largest binary buddy of free bits within a dmap word.
+ */
+static int dbMaxBud(u8 * cp)
+{
+ signed char tmp1, tmp2;
+
+ /* check if the wmap word is all free. if so, the
+ * free buddy size is BUDMIN.
+ */
+ if (*((uint *) cp) == 0)
+ return (BUDMIN);
+
+ /* check if the wmap word is half free. if so, the
+ * free buddy size is BUDMIN-1.
+ */
+ if (*((u16 *) cp) == 0 || *((u16 *) cp + 1) == 0)
+ return (BUDMIN - 1);
+
+ /* not all free or half free. determine the free buddy
+ * size thru table lookup using quarters of the wmap word.
+ */
+ tmp1 = max(budtab[cp[2]], budtab[cp[3]]);
+ tmp2 = max(budtab[cp[0]], budtab[cp[1]]);
+ return (max(tmp1, tmp2));
+}
+
+
+/*
+ * NAME: cnttz(uint word)
+ *
+ * FUNCTION: determine the number of trailing zeros within a 32-bit
+ * value.
+ *
+ * PARAMETERS:
+ * value - 32-bit value to be examined.
+ *
+ * RETURN VALUES:
+ * count of trailing zeros
+ */
+static int cnttz(u32 word)
+{
+ int n;
+
+ for (n = 0; n < 32; n++, word >>= 1) {
+ if (word & 0x01)
+ break;
+ }
+
+ return (n);
+}
+
+
+/*
+ * NAME: cntlz(u32 value)
+ *
+ * FUNCTION: determine the number of leading zeros within a 32-bit
+ * value.
+ *
+ * PARAMETERS:
+ * value - 32-bit value to be examined.
+ *
+ * RETURN VALUES:
+ * count of leading zeros
+ */
+static int cntlz(u32 value)
+{
+ int n;
+
+ for (n = 0; n < 32; n++, value <<= 1) {
+ if (value & HIGHORDER)
+ break;
+ }
+ return (n);
+}
+
+
+/*
+ * NAME: blkstol2(s64 nb)
+ *
+ * FUNCTION: convert a block count to its log2 value. if the block
+ * count is not a l2 multiple, it is rounded up to the next
+ * larger l2 multiple.
+ *
+ * PARAMETERS:
+ * nb - number of blocks
+ *
+ * RETURN VALUES:
+ * log2 number of blocks
+ */
+int blkstol2(s64 nb)
+{
+ int l2nb;
+ s64 mask; /* meant to be signed */
+
+ mask = (s64) 1 << (64 - 1);
+
+ /* count the leading bits.
+ */
+ for (l2nb = 0; l2nb < 64; l2nb++, mask >>= 1) {
+ /* leading bit found.
+ */
+ if (nb & mask) {
+ /* determine the l2 value.
+ */
+ l2nb = (64 - 1) - l2nb;
+
+ /* check if we need to round up.
+ */
+ if (~mask & nb)
+ l2nb++;
+
+ return (l2nb);
+ }
+ }
+ assert(0);
+ return 0; /* fix compiler warning */
+}
+
+
+/*
+ * NAME: dbAllocBottomUp()
+ *
+ * FUNCTION: alloc the specified block range from the working block
+ * allocation map.
+ *
+ * the blocks will be alloc from the working map one dmap
+ * at a time.
+ *
+ * PARAMETERS:
+ * ip - pointer to in-core inode;
+ * blkno - starting block number to be freed.
+ * nblocks - number of blocks to be freed.
+ *
+ * RETURN VALUES:
+ * 0 - success
+ * -EIO - i/o error
+ */
+int dbAllocBottomUp(struct inode *ip, s64 blkno, s64 nblocks)
+{
+ struct metapage *mp;
+ struct dmap *dp;
+ int nb, rc;
+ s64 lblkno, rem;
+ struct inode *ipbmap = JFS_SBI(ip->i_sb)->ipbmap;
+ struct bmap *bmp = JFS_SBI(ip->i_sb)->bmap;
+
+ IREAD_LOCK(ipbmap);
+
+ /* block to be allocated better be within the mapsize. */
+ ASSERT(nblocks <= bmp->db_mapsize - blkno);
+
+ /*
+ * allocate the blocks a dmap at a time.
+ */
+ mp = NULL;
+ for (rem = nblocks; rem > 0; rem -= nb, blkno += nb) {
+ /* release previous dmap if any */
+ if (mp) {
+ write_metapage(mp);
+ }
+
+ /* get the buffer for the current dmap. */
+ lblkno = BLKTODMAP(blkno, bmp->db_l2nbperpage);
+ mp = read_metapage(ipbmap, lblkno, PSIZE, 0);
+ if (mp == NULL) {
+ IREAD_UNLOCK(ipbmap);
+ return -EIO;
+ }
+ dp = (struct dmap *) mp->data;
+
+ /* determine the number of blocks to be allocated from
+ * this dmap.
+ */
+ nb = min(rem, BPERDMAP - (blkno & (BPERDMAP - 1)));
+
+ DBFREECK(bmp->db_DBmap, bmp->db_mapsize, blkno, nb);
+
+ /* allocate the blocks. */
+ if ((rc = dbAllocDmapBU(bmp, dp, blkno, nb))) {
+ release_metapage(mp);
+ IREAD_UNLOCK(ipbmap);
+ return (rc);
+ }
+
+ DBALLOC(bmp->db_DBmap, bmp->db_mapsize, blkno, nb);
+ }
+
+ /* write the last buffer. */
+ write_metapage(mp);
+
+ IREAD_UNLOCK(ipbmap);
+
+ return (0);
+}
+
+
+static int dbAllocDmapBU(struct bmap * bmp, struct dmap * dp, s64 blkno,
+ int nblocks)
+{
+ int rc;
+ int dbitno, word, rembits, nb, nwords, wbitno, agno;
+ s8 oldroot, *leaf;
+ struct dmaptree *tp = (struct dmaptree *) & dp->tree;
+
+ /* save the current value of the root (i.e. maximum free string)
+ * of the dmap tree.
+ */
+ oldroot = tp->stree[ROOT];
+
+ /* pick up a pointer to the leaves of the dmap tree */
+ leaf = tp->stree + LEAFIND;
+
+ /* determine the bit number and word within the dmap of the
+ * starting block.
+ */
+ dbitno = blkno & (BPERDMAP - 1);
+ word = dbitno >> L2DBWORD;
+
+ /* block range better be within the dmap */
+ assert(dbitno + nblocks <= BPERDMAP);
+
+ /* allocate the bits of the dmap's words corresponding to the block
+ * range. not all bits of the first and last words may be contained
+ * within the block range. if this is the case, we'll work against
+ * those words (i.e. partial first and/or last) on an individual basis
+ * (a single pass), allocating the bits of interest by hand and
+ * updating the leaf corresponding to the dmap word. a single pass
+ * will be used for all dmap words fully contained within the
+ * specified range. within this pass, the bits of all fully contained
+ * dmap words will be marked as free in a single shot and the leaves
+ * will be updated. a single leaf may describe the free space of
+ * multiple dmap words, so we may update only a subset of the actual
+ * leaves corresponding to the dmap words of the block range.
+ */
+ for (rembits = nblocks; rembits > 0; rembits -= nb, dbitno += nb) {
+ /* determine the bit number within the word and
+ * the number of bits within the word.
+ */
+ wbitno = dbitno & (DBWORD - 1);
+ nb = min(rembits, DBWORD - wbitno);
+
+ /* check if only part of a word is to be allocated.
+ */
+ if (nb < DBWORD) {
+ /* allocate (set to 1) the appropriate bits within
+ * this dmap word.
+ */
+ dp->wmap[word] |= cpu_to_le32(ONES << (DBWORD - nb)
+ >> wbitno);
+
+ word++;
+ } else {
+ /* one or more dmap words are fully contained
+ * within the block range. determine how many
+ * words and allocate (set to 1) the bits of these
+ * words.
+ */
+ nwords = rembits >> L2DBWORD;
+ memset(&dp->wmap[word], (int) ONES, nwords * 4);
+
+ /* determine how many bits */
+ nb = nwords << L2DBWORD;
+ word += nwords;
+ }
+ }
+
+ /* update the free count for this dmap */
+ dp->nfree = cpu_to_le32(le32_to_cpu(dp->nfree) - nblocks);
+
+ /* reconstruct summary tree */
+ dbInitDmapTree(dp);
+
+ BMAP_LOCK(bmp);
+
+ /* if this allocation group is completely free,
+ * update the highest active allocation group number
+ * if this allocation group is the new max.
+ */
+ agno = blkno >> bmp->db_agl2size;
+ if (agno > bmp->db_maxag)
+ bmp->db_maxag = agno;
+
+ /* update the free count for the allocation group and map */
+ bmp->db_agfree[agno] -= nblocks;
+ bmp->db_nfree -= nblocks;
+
+ BMAP_UNLOCK(bmp);
+
+ /* if the root has not changed, done. */
+ if (tp->stree[ROOT] == oldroot)
+ return (0);
+
+ /* root changed. bubble the change up to the dmap control pages.
+ * if the adjustment of the upper level control pages fails,
+ * backout the bit allocation (thus making everything consistent).
+ */
+ if ((rc = dbAdjCtl(bmp, blkno, tp->stree[ROOT], 1, 0)))
+ dbFreeBits(bmp, dp, blkno, nblocks);
+
+ return (rc);
+}
+
+
+/*
+ * NAME: dbExtendFS()
+ *
+ * FUNCTION: extend bmap from blkno for nblocks;
+ * dbExtendFS() updates bmap ready for dbAllocBottomUp();
+ *
+ * L2
+ * |
+ * L1---------------------------------L1
+ * | |
+ * L0---------L0---------L0 L0---------L0---------L0
+ * | | | | | |
+ * d0,...,dn d0,...,dn d0,...,dn d0,...,dn d0,...,dn d0,.,dm;
+ * L2L1L0d0,...,dnL0d0,...,dnL0d0,...,dnL1L0d0,...,dnL0d0,...,dnL0d0,..dm
+ *
+ * <---old---><----------------------------extend----------------------->
+ */
+int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks)
+{
+ struct jfs_sb_info *sbi = JFS_SBI(ipbmap->i_sb);
+ int nbperpage = sbi->nbperpage;
+ int i, i0 = TRUE, j, j0 = TRUE, k, n;
+ s64 newsize;
+ s64 p;
+ struct metapage *mp, *l2mp, *l1mp = NULL, *l0mp = NULL;
+ struct dmapctl *l2dcp, *l1dcp, *l0dcp;
+ struct dmap *dp;
+ s8 *l0leaf, *l1leaf, *l2leaf;
+ struct bmap *bmp = sbi->bmap;
+ int agno, l2agsize, oldl2agsize;
+ s64 ag_rem;
+
+ newsize = blkno + nblocks;
+
+ jfs_info("dbExtendFS: blkno:%Ld nblocks:%Ld newsize:%Ld",
+ (long long) blkno, (long long) nblocks, (long long) newsize);
+
+ /*
+ * initialize bmap control page.
+ *
+ * all the data in bmap control page should exclude
+ * the mkfs hidden dmap page.
+ */
+
+ /* update mapsize */
+ bmp->db_mapsize = newsize;
+ bmp->db_maxlevel = BMAPSZTOLEV(bmp->db_mapsize);
+
+ /* compute new AG size */
+ l2agsize = dbGetL2AGSize(newsize);
+ oldl2agsize = bmp->db_agl2size;
+
+ bmp->db_agl2size = l2agsize;
+ bmp->db_agsize = 1 << l2agsize;
+
+ /* compute new number of AG */
+ agno = bmp->db_numag;
+ bmp->db_numag = newsize >> l2agsize;
+ bmp->db_numag += ((u32) newsize % (u32) bmp->db_agsize) ? 1 : 0;
+
+ /*
+ * reconfigure db_agfree[]
+ * from old AG configuration to new AG configuration;
+ *
+ * coalesce contiguous k (newAGSize/oldAGSize) AGs;
+ * i.e., (AGi, ..., AGj) where i = k*n and j = k*(n+1) - 1 to AGn;
+ * note: new AG size = old AG size * (2**x).
+ */
+ if (l2agsize == oldl2agsize)
+ goto extend;
+ k = 1 << (l2agsize - oldl2agsize);
+ ag_rem = bmp->db_agfree[0]; /* save agfree[0] */
+ for (i = 0, n = 0; i < agno; n++) {
+ bmp->db_agfree[n] = 0; /* init collection point */
+
+ /* coalesce cotiguous k AGs; */
+ for (j = 0; j < k && i < agno; j++, i++) {
+ /* merge AGi to AGn */
+ bmp->db_agfree[n] += bmp->db_agfree[i];
+ }
+ }
+ bmp->db_agfree[0] += ag_rem; /* restore agfree[0] */
+
+ for (; n < MAXAG; n++)
+ bmp->db_agfree[n] = 0;
+
+ /*
+ * update highest active ag number
+ */
+
+ bmp->db_maxag = bmp->db_maxag / k;
+
+ /*
+ * extend bmap
+ *
+ * update bit maps and corresponding level control pages;
+ * global control page db_nfree, db_agfree[agno], db_maxfreebud;
+ */
+ extend:
+ /* get L2 page */
+ p = BMAPBLKNO + nbperpage; /* L2 page */
+ l2mp = read_metapage(ipbmap, p, PSIZE, 0);
+ if (!l2mp) {
+ jfs_error(ipbmap->i_sb, "dbExtendFS: L2 page could not be read");
+ return -EIO;
+ }
+ l2dcp = (struct dmapctl *) l2mp->data;
+
+ /* compute start L1 */
+ k = blkno >> L2MAXL1SIZE;
+ l2leaf = l2dcp->stree + CTLLEAFIND + k;
+ p = BLKTOL1(blkno, sbi->l2nbperpage); /* L1 page */
+
+ /*
+ * extend each L1 in L2
+ */
+ for (; k < LPERCTL; k++, p += nbperpage) {
+ /* get L1 page */
+ if (j0) {
+ /* read in L1 page: (blkno & (MAXL1SIZE - 1)) */
+ l1mp = read_metapage(ipbmap, p, PSIZE, 0);
+ if (l1mp == NULL)
+ goto errout;
+ l1dcp = (struct dmapctl *) l1mp->data;
+
+ /* compute start L0 */
+ j = (blkno & (MAXL1SIZE - 1)) >> L2MAXL0SIZE;
+ l1leaf = l1dcp->stree + CTLLEAFIND + j;
+ p = BLKTOL0(blkno, sbi->l2nbperpage);
+ j0 = FALSE;
+ } else {
+ /* assign/init L1 page */
+ l1mp = get_metapage(ipbmap, p, PSIZE, 0);
+ if (l1mp == NULL)
+ goto errout;
+
+ l1dcp = (struct dmapctl *) l1mp->data;
+
+ /* compute start L0 */
+ j = 0;
+ l1leaf = l1dcp->stree + CTLLEAFIND;
+ p += nbperpage; /* 1st L0 of L1.k */
+ }
+
+ /*
+ * extend each L0 in L1
+ */
+ for (; j < LPERCTL; j++) {
+ /* get L0 page */
+ if (i0) {
+ /* read in L0 page: (blkno & (MAXL0SIZE - 1)) */
+
+ l0mp = read_metapage(ipbmap, p, PSIZE, 0);
+ if (l0mp == NULL)
+ goto errout;
+ l0dcp = (struct dmapctl *) l0mp->data;
+
+ /* compute start dmap */
+ i = (blkno & (MAXL0SIZE - 1)) >>
+ L2BPERDMAP;
+ l0leaf = l0dcp->stree + CTLLEAFIND + i;
+ p = BLKTODMAP(blkno,
+ sbi->l2nbperpage);
+ i0 = FALSE;
+ } else {
+ /* assign/init L0 page */
+ l0mp = get_metapage(ipbmap, p, PSIZE, 0);
+ if (l0mp == NULL)
+ goto errout;
+
+ l0dcp = (struct dmapctl *) l0mp->data;
+
+ /* compute start dmap */
+ i = 0;
+ l0leaf = l0dcp->stree + CTLLEAFIND;
+ p += nbperpage; /* 1st dmap of L0.j */
+ }
+
+ /*
+ * extend each dmap in L0
+ */
+ for (; i < LPERCTL; i++) {
+ /*
+ * reconstruct the dmap page, and
+ * initialize corresponding parent L0 leaf
+ */
+ if ((n = blkno & (BPERDMAP - 1))) {
+ /* read in dmap page: */
+ mp = read_metapage(ipbmap, p,
+ PSIZE, 0);
+ if (mp == NULL)
+ goto errout;
+ n = min(nblocks, (s64)BPERDMAP - n);
+ } else {
+ /* assign/init dmap page */
+ mp = read_metapage(ipbmap, p,
+ PSIZE, 0);
+ if (mp == NULL)
+ goto errout;
+
+ n = min(nblocks, (s64)BPERDMAP);
+ }
+
+ dp = (struct dmap *) mp->data;
+ *l0leaf = dbInitDmap(dp, blkno, n);
+
+ bmp->db_nfree += n;
+ agno = le64_to_cpu(dp->start) >> l2agsize;
+ bmp->db_agfree[agno] += n;
+
+ write_metapage(mp);
+
+ l0leaf++;
+ p += nbperpage;
+
+ blkno += n;
+ nblocks -= n;
+ if (nblocks == 0)
+ break;
+ } /* for each dmap in a L0 */
+
+ /*
+ * build current L0 page from its leaves, and
+ * initialize corresponding parent L1 leaf
+ */
+ *l1leaf = dbInitDmapCtl(l0dcp, 0, ++i);
+ write_metapage(l0mp);
+ l0mp = NULL;
+
+ if (nblocks)
+ l1leaf++; /* continue for next L0 */
+ else {
+ /* more than 1 L0 ? */
+ if (j > 0)
+ break; /* build L1 page */
+ else {
+ /* summarize in global bmap page */
+ bmp->db_maxfreebud = *l1leaf;
+ release_metapage(l1mp);
+ release_metapage(l2mp);
+ goto finalize;
+ }
+ }
+ } /* for each L0 in a L1 */
+
+ /*
+ * build current L1 page from its leaves, and
+ * initialize corresponding parent L2 leaf
+ */
+ *l2leaf = dbInitDmapCtl(l1dcp, 1, ++j);
+ write_metapage(l1mp);
+ l1mp = NULL;
+
+ if (nblocks)
+ l2leaf++; /* continue for next L1 */
+ else {
+ /* more than 1 L1 ? */
+ if (k > 0)
+ break; /* build L2 page */
+ else {
+ /* summarize in global bmap page */
+ bmp->db_maxfreebud = *l2leaf;
+ release_metapage(l2mp);
+ goto finalize;
+ }
+ }
+ } /* for each L1 in a L2 */
+
+ jfs_error(ipbmap->i_sb,
+ "dbExtendFS: function has not returned as expected");
+errout:
+ if (l0mp)
+ release_metapage(l0mp);
+ if (l1mp)
+ release_metapage(l1mp);
+ release_metapage(l2mp);
+ return -EIO;
+
+ /*
+ * finalize bmap control page
+ */
+finalize:
+
+ return 0;
+}
+
+
+/*
+ * dbFinalizeBmap()
+ */
+void dbFinalizeBmap(struct inode *ipbmap)
+{
+ struct bmap *bmp = JFS_SBI(ipbmap->i_sb)->bmap;
+ int actags, inactags, l2nl;
+ s64 ag_rem, actfree, inactfree, avgfree;
+ int i, n;
+
+ /*
+ * finalize bmap control page
+ */
+//finalize:
+ /*
+ * compute db_agpref: preferred ag to allocate from
+ * (the leftmost ag with average free space in it);
+ */
+//agpref:
+ /* get the number of active ags and inacitve ags */
+ actags = bmp->db_maxag + 1;
+ inactags = bmp->db_numag - actags;
+ ag_rem = bmp->db_mapsize & (bmp->db_agsize - 1); /* ??? */
+
+ /* determine how many blocks are in the inactive allocation
+ * groups. in doing this, we must account for the fact that
+ * the rightmost group might be a partial group (i.e. file
+ * system size is not a multiple of the group size).
+ */
+ inactfree = (inactags && ag_rem) ?
+ ((inactags - 1) << bmp->db_agl2size) + ag_rem
+ : inactags << bmp->db_agl2size;
+
+ /* determine how many free blocks are in the active
+ * allocation groups plus the average number of free blocks
+ * within the active ags.
+ */
+ actfree = bmp->db_nfree - inactfree;
+ avgfree = (u32) actfree / (u32) actags;
+
+ /* if the preferred allocation group has not average free space.
+ * re-establish the preferred group as the leftmost
+ * group with average free space.
+ */
+ if (bmp->db_agfree[bmp->db_agpref] < avgfree) {
+ for (bmp->db_agpref = 0; bmp->db_agpref < actags;
+ bmp->db_agpref++) {
+ if (bmp->db_agfree[bmp->db_agpref] >= avgfree)
+ break;
+ }
+ if (bmp->db_agpref >= bmp->db_numag) {
+ jfs_error(ipbmap->i_sb,
+ "cannot find ag with average freespace");
+ }
+ }
+
+ /*
+ * compute db_aglevel, db_agheigth, db_width, db_agstart:
+ * an ag is covered in aglevel dmapctl summary tree,
+ * at agheight level height (from leaf) with agwidth number of nodes
+ * each, which starts at agstart index node of the smmary tree node
+ * array;
+ */
+ bmp->db_aglevel = BMAPSZTOLEV(bmp->db_agsize);
+ l2nl =
+ bmp->db_agl2size - (L2BPERDMAP + bmp->db_aglevel * L2LPERCTL);
+ bmp->db_agheigth = l2nl >> 1;
+ bmp->db_agwidth = 1 << (l2nl - (bmp->db_agheigth << 1));
+ for (i = 5 - bmp->db_agheigth, bmp->db_agstart = 0, n = 1; i > 0;
+ i--) {
+ bmp->db_agstart += n;
+ n <<= 2;
+ }
+
+}
+
+
+/*
+ * NAME: dbInitDmap()/ujfs_idmap_page()
+ *
+ * FUNCTION: initialize working/persistent bitmap of the dmap page
+ * for the specified number of blocks:
+ *
+ * at entry, the bitmaps had been initialized as free (ZEROS);
+ * The number of blocks will only account for the actually
+ * existing blocks. Blocks which don't actually exist in
+ * the aggregate will be marked as allocated (ONES);
+ *
+ * PARAMETERS:
+ * dp - pointer to page of map
+ * nblocks - number of blocks this page
+ *
+ * RETURNS: NONE
+ */
+static int dbInitDmap(struct dmap * dp, s64 Blkno, int nblocks)
+{
+ int blkno, w, b, r, nw, nb, i;
+
+ /* starting block number within the dmap */
+ blkno = Blkno & (BPERDMAP - 1);
+
+ if (blkno == 0) {
+ dp->nblocks = dp->nfree = cpu_to_le32(nblocks);
+ dp->start = cpu_to_le64(Blkno);
+
+ if (nblocks == BPERDMAP) {
+ memset(&dp->wmap[0], 0, LPERDMAP * 4);
+ memset(&dp->pmap[0], 0, LPERDMAP * 4);
+ goto initTree;
+ }
+ } else {
+ dp->nblocks =
+ cpu_to_le32(le32_to_cpu(dp->nblocks) + nblocks);
+ dp->nfree = cpu_to_le32(le32_to_cpu(dp->nfree) + nblocks);
+ }
+
+ /* word number containing start block number */
+ w = blkno >> L2DBWORD;
+
+ /*
+ * free the bits corresponding to the block range (ZEROS):
+ * note: not all bits of the first and last words may be contained
+ * within the block range.
+ */
+ for (r = nblocks; r > 0; r -= nb, blkno += nb) {
+ /* number of bits preceding range to be freed in the word */
+ b = blkno & (DBWORD - 1);
+ /* number of bits to free in the word */
+ nb = min(r, DBWORD - b);
+
+ /* is partial word to be freed ? */
+ if (nb < DBWORD) {
+ /* free (set to 0) from the bitmap word */
+ dp->wmap[w] &= cpu_to_le32(~(ONES << (DBWORD - nb)
+ >> b));
+ dp->pmap[w] &= cpu_to_le32(~(ONES << (DBWORD - nb)
+ >> b));
+
+ /* skip the word freed */
+ w++;
+ } else {
+ /* free (set to 0) contiguous bitmap words */
+ nw = r >> L2DBWORD;
+ memset(&dp->wmap[w], 0, nw * 4);
+ memset(&dp->pmap[w], 0, nw * 4);
+
+ /* skip the words freed */
+ nb = nw << L2DBWORD;
+ w += nw;
+ }
+ }
+
+ /*
+ * mark bits following the range to be freed (non-existing
+ * blocks) as allocated (ONES)
+ */
+
+ if (blkno == BPERDMAP)
+ goto initTree;
+
+ /* the first word beyond the end of existing blocks */
+ w = blkno >> L2DBWORD;
+
+ /* does nblocks fall on a 32-bit boundary ? */
+ b = blkno & (DBWORD - 1);
+ if (b) {
+ /* mark a partial word allocated */
+ dp->wmap[w] = dp->pmap[w] = cpu_to_le32(ONES >> b);
+ w++;
+ }
+
+ /* set the rest of the words in the page to allocated (ONES) */
+ for (i = w; i < LPERDMAP; i++)
+ dp->pmap[i] = dp->wmap[i] = ONES;
+
+ /*
+ * init tree
+ */
+ initTree:
+ return (dbInitDmapTree(dp));
+}
+
+
+/*
+ * NAME: dbInitDmapTree()/ujfs_complete_dmap()
+ *
+ * FUNCTION: initialize summary tree of the specified dmap:
+ *
+ * at entry, bitmap of the dmap has been initialized;
+ *
+ * PARAMETERS:
+ * dp - dmap to complete
+ * blkno - starting block number for this dmap
+ * treemax - will be filled in with max free for this dmap
+ *
+ * RETURNS: max free string at the root of the tree
+ */
+static int dbInitDmapTree(struct dmap * dp)
+{
+ struct dmaptree *tp;
+ s8 *cp;
+ int i;
+
+ /* init fixed info of tree */
+ tp = &dp->tree;
+ tp->nleafs = cpu_to_le32(LPERDMAP);
+ tp->l2nleafs = cpu_to_le32(L2LPERDMAP);
+ tp->leafidx = cpu_to_le32(LEAFIND);
+ tp->height = cpu_to_le32(4);
+ tp->budmin = BUDMIN;
+
+ /* init each leaf from corresponding wmap word:
+ * note: leaf is set to NOFREE(-1) if all blocks of corresponding
+ * bitmap word are allocated.
+ */
+ cp = tp->stree + le32_to_cpu(tp->leafidx);
+ for (i = 0; i < LPERDMAP; i++)
+ *cp++ = dbMaxBud((u8 *) & dp->wmap[i]);
+
+ /* build the dmap's binary buddy summary tree */
+ return (dbInitTree(tp));
+}
+
+
+/*
+ * NAME: dbInitTree()/ujfs_adjtree()
+ *
+ * FUNCTION: initialize binary buddy summary tree of a dmap or dmapctl.
+ *
+ * at entry, the leaves of the tree has been initialized
+ * from corresponding bitmap word or root of summary tree
+ * of the child control page;
+ * configure binary buddy system at the leaf level, then
+ * bubble up the values of the leaf nodes up the tree.
+ *
+ * PARAMETERS:
+ * cp - Pointer to the root of the tree
+ * l2leaves- Number of leaf nodes as a power of 2
+ * l2min - Number of blocks that can be covered by a leaf
+ * as a power of 2
+ *
+ * RETURNS: max free string at the root of the tree
+ */
+static int dbInitTree(struct dmaptree * dtp)
+{
+ int l2max, l2free, bsize, nextb, i;
+ int child, parent, nparent;
+ s8 *tp, *cp, *cp1;
+
+ tp = dtp->stree;
+
+ /* Determine the maximum free string possible for the leaves */
+ l2max = le32_to_cpu(dtp->l2nleafs) + dtp->budmin;
+
+ /*
+ * configure the leaf levevl into binary buddy system
+ *
+ * Try to combine buddies starting with a buddy size of 1
+ * (i.e. two leaves). At a buddy size of 1 two buddy leaves
+ * can be combined if both buddies have a maximum free of l2min;
+ * the combination will result in the left-most buddy leaf having
+ * a maximum free of l2min+1.
+ * After processing all buddies for a given size, process buddies
+ * at the next higher buddy size (i.e. current size * 2) and
+ * the next maximum free (current free + 1).
+ * This continues until the maximum possible buddy combination
+ * yields maximum free.
+ */
+ for (l2free = dtp->budmin, bsize = 1; l2free < l2max;
+ l2free++, bsize = nextb) {
+ /* get next buddy size == current buddy pair size */
+ nextb = bsize << 1;
+
+ /* scan each adjacent buddy pair at current buddy size */
+ for (i = 0, cp = tp + le32_to_cpu(dtp->leafidx);
+ i < le32_to_cpu(dtp->nleafs);
+ i += nextb, cp += nextb) {
+ /* coalesce if both adjacent buddies are max free */
+ if (*cp == l2free && *(cp + bsize) == l2free) {
+ *cp = l2free + 1; /* left take right */
+ *(cp + bsize) = -1; /* right give left */
+ }
+ }
+ }
+
+ /*
+ * bubble summary information of leaves up the tree.
+ *
+ * Starting at the leaf node level, the four nodes described by
+ * the higher level parent node are compared for a maximum free and
+ * this maximum becomes the value of the parent node.
+ * when all lower level nodes are processed in this fashion then
+ * move up to the next level (parent becomes a lower level node) and
+ * continue the process for that level.
+ */
+ for (child = le32_to_cpu(dtp->leafidx),
+ nparent = le32_to_cpu(dtp->nleafs) >> 2;
+ nparent > 0; nparent >>= 2, child = parent) {
+ /* get index of 1st node of parent level */
+ parent = (child - 1) >> 2;
+
+ /* set the value of the parent node as the maximum
+ * of the four nodes of the current level.
+ */
+ for (i = 0, cp = tp + child, cp1 = tp + parent;
+ i < nparent; i++, cp += 4, cp1++)
+ *cp1 = TREEMAX(cp);
+ }
+
+ return (*tp);
+}
+
+
+/*
+ * dbInitDmapCtl()
+ *
+ * function: initialize dmapctl page
+ */
+static int dbInitDmapCtl(struct dmapctl * dcp, int level, int i)
+{ /* start leaf index not covered by range */
+ s8 *cp;
+
+ dcp->nleafs = cpu_to_le32(LPERCTL);
+ dcp->l2nleafs = cpu_to_le32(L2LPERCTL);
+ dcp->leafidx = cpu_to_le32(CTLLEAFIND);
+ dcp->height = cpu_to_le32(5);
+ dcp->budmin = L2BPERDMAP + L2LPERCTL * level;
+
+ /*
+ * initialize the leaves of current level that were not covered
+ * by the specified input block range (i.e. the leaves have no
+ * low level dmapctl or dmap).
+ */
+ cp = &dcp->stree[CTLLEAFIND + i];
+ for (; i < LPERCTL; i++)
+ *cp++ = NOFREE;
+
+ /* build the dmap's binary buddy summary tree */
+ return (dbInitTree((struct dmaptree *) dcp));
+}
+
+
+/*
+ * NAME: dbGetL2AGSize()/ujfs_getagl2size()
+ *
+ * FUNCTION: Determine log2(allocation group size) from aggregate size
+ *
+ * PARAMETERS:
+ * nblocks - Number of blocks in aggregate
+ *
+ * RETURNS: log2(allocation group size) in aggregate blocks
+ */
+static int dbGetL2AGSize(s64 nblocks)
+{
+ s64 sz;
+ s64 m;
+ int l2sz;
+
+ if (nblocks < BPERDMAP * MAXAG)
+ return (L2BPERDMAP);
+
+ /* round up aggregate size to power of 2 */
+ m = ((u64) 1 << (64 - 1));
+ for (l2sz = 64; l2sz >= 0; l2sz--, m >>= 1) {
+ if (m & nblocks)
+ break;
+ }
+
+ sz = (s64) 1 << l2sz;
+ if (sz < nblocks)
+ l2sz += 1;
+
+ /* agsize = roundupSize/max_number_of_ag */
+ return (l2sz - L2MAXAG);
+}
+
+
+/*
+ * NAME: dbMapFileSizeToMapSize()
+ *
+ * FUNCTION: compute number of blocks the block allocation map file
+ * can cover from the map file size;
+ *
+ * RETURNS: Number of blocks which can be covered by this block map file;
+ */
+
+/*
+ * maximum number of map pages at each level including control pages
+ */
+#define MAXL0PAGES (1 + LPERCTL)
+#define MAXL1PAGES (1 + LPERCTL * MAXL0PAGES)
+#define MAXL2PAGES (1 + LPERCTL * MAXL1PAGES)
+
+/*
+ * convert number of map pages to the zero origin top dmapctl level
+ */
+#define BMAPPGTOLEV(npages) \
+ (((npages) <= 3 + MAXL0PAGES) ? 0 \
+ : ((npages) <= 2 + MAXL1PAGES) ? 1 : 2)
+
+s64 dbMapFileSizeToMapSize(struct inode * ipbmap)
+{
+ struct super_block *sb = ipbmap->i_sb;
+ s64 nblocks;
+ s64 npages, ndmaps;
+ int level, i;
+ int complete, factor;
+
+ nblocks = ipbmap->i_size >> JFS_SBI(sb)->l2bsize;
+ npages = nblocks >> JFS_SBI(sb)->l2nbperpage;
+ level = BMAPPGTOLEV(npages);
+
+ /* At each level, accumulate the number of dmap pages covered by
+ * the number of full child levels below it;
+ * repeat for the last incomplete child level.
+ */
+ ndmaps = 0;
+ npages--; /* skip the first global control page */
+ /* skip higher level control pages above top level covered by map */
+ npages -= (2 - level);
+ npages--; /* skip top level's control page */
+ for (i = level; i >= 0; i--) {
+ factor =
+ (i == 2) ? MAXL1PAGES : ((i == 1) ? MAXL0PAGES : 1);
+ complete = (u32) npages / factor;
+ ndmaps += complete * ((i == 2) ? LPERCTL * LPERCTL
+ : ((i == 1) ? LPERCTL : 1));
+
+ /* pages in last/incomplete child */
+ npages = (u32) npages % factor;
+ /* skip incomplete child's level control page */
+ npages--;
+ }
+
+ /* convert the number of dmaps into the number of blocks
+ * which can be covered by the dmaps;
+ */
+ nblocks = ndmaps << L2BPERDMAP;
+
+ return (nblocks);
+}
+
+
+#ifdef _JFS_DEBUG_DMAP
+/*
+ * DBinitmap()
+ */
+static void DBinitmap(s64 size, struct inode *ipbmap, u32 ** results)
+{
+ int npages;
+ u32 *dbmap, *d;
+ int n;
+ s64 lblkno, cur_block;
+ struct dmap *dp;
+ struct metapage *mp;
+
+ npages = size / 32768;
+ npages += (size % 32768) ? 1 : 0;
+
+ dbmap = (u32 *) xmalloc(npages * 4096, L2PSIZE, kernel_heap);
+ if (dbmap == NULL)
+ BUG(); /* Not robust since this is only unused debug code */
+
+ for (n = 0, d = dbmap; n < npages; n++, d += 1024)
+ bzero(d, 4096);
+
+ /* Need to initialize from disk map pages
+ */
+ for (d = dbmap, cur_block = 0; cur_block < size;
+ cur_block += BPERDMAP, d += LPERDMAP) {
+ lblkno = BLKTODMAP(cur_block,
+ JFS_SBI(ipbmap->i_sb)->bmap->
+ db_l2nbperpage);
+ mp = read_metapage(ipbmap, lblkno, PSIZE, 0);
+ if (mp == NULL) {
+ jfs_error(ipbmap->i_sb,
+ "DBinitmap: could not read disk map page");
+ continue;
+ }
+ dp = (struct dmap *) mp->data;
+
+ for (n = 0; n < LPERDMAP; n++)
+ d[n] = le32_to_cpu(dp->wmap[n]);
+
+ release_metapage(mp);
+ }
+
+ *results = dbmap;
+}
+
+
+/*
+ * DBAlloc()
+ */
+void DBAlloc(uint * dbmap, s64 mapsize, s64 blkno, s64 nblocks)
+{
+ int word, nb, bitno;
+ u32 mask;
+
+ assert(blkno > 0 && blkno < mapsize);
+ assert(nblocks > 0 && nblocks <= mapsize);
+
+ assert(blkno + nblocks <= mapsize);
+
+ dbmap += (blkno / 32);
+ while (nblocks > 0) {
+ bitno = blkno & (32 - 1);
+ nb = min(nblocks, 32 - bitno);
+
+ mask = (0xffffffff << (32 - nb) >> bitno);
+ assert((mask & *dbmap) == 0);
+ *dbmap |= mask;
+
+ dbmap++;
+ blkno += nb;
+ nblocks -= nb;
+ }
+}
+
+
+/*
+ * DBFree()
+ */
+static void DBFree(uint * dbmap, s64 mapsize, s64 blkno, s64 nblocks)
+{
+ int word, nb, bitno;
+ u32 mask;
+
+ assert(blkno > 0 && blkno < mapsize);
+ assert(nblocks > 0 && nblocks <= mapsize);
+
+ assert(blkno + nblocks <= mapsize);
+
+ dbmap += (blkno / 32);
+ while (nblocks > 0) {
+ bitno = blkno & (32 - 1);
+ nb = min(nblocks, 32 - bitno);
+
+ mask = (0xffffffff << (32 - nb) >> bitno);
+ assert((mask & *dbmap) == mask);
+ *dbmap &= ~mask;
+
+ dbmap++;
+ blkno += nb;
+ nblocks -= nb;
+ }
+}
+
+
+/*
+ * DBAllocCK()
+ */
+static void DBAllocCK(uint * dbmap, s64 mapsize, s64 blkno, s64 nblocks)
+{
+ int word, nb, bitno;
+ u32 mask;
+
+ assert(blkno > 0 && blkno < mapsize);
+ assert(nblocks > 0 && nblocks <= mapsize);
+
+ assert(blkno + nblocks <= mapsize);
+
+ dbmap += (blkno / 32);
+ while (nblocks > 0) {
+ bitno = blkno & (32 - 1);
+ nb = min(nblocks, 32 - bitno);
+
+ mask = (0xffffffff << (32 - nb) >> bitno);
+ assert((mask & *dbmap) == mask);
+
+ dbmap++;
+ blkno += nb;
+ nblocks -= nb;
+ }
+}
+
+
+/*
+ * DBFreeCK()
+ */
+static void DBFreeCK(uint * dbmap, s64 mapsize, s64 blkno, s64 nblocks)
+{
+ int word, nb, bitno;
+ u32 mask;
+
+ assert(blkno > 0 && blkno < mapsize);
+ assert(nblocks > 0 && nblocks <= mapsize);
+
+ assert(blkno + nblocks <= mapsize);
+
+ dbmap += (blkno / 32);
+ while (nblocks > 0) {
+ bitno = blkno & (32 - 1);
+ nb = min(nblocks, 32 - bitno);
+
+ mask = (0xffffffff << (32 - nb) >> bitno);
+ assert((mask & *dbmap) == 0);
+
+ dbmap++;
+ blkno += nb;
+ nblocks -= nb;
+ }
+}
+
+
+/*
+ * dbPrtMap()
+ */
+static void dbPrtMap(struct bmap * bmp)
+{
+ printk(" mapsize: %d%d\n", bmp->db_mapsize);
+ printk(" nfree: %d%d\n", bmp->db_nfree);
+ printk(" numag: %d\n", bmp->db_numag);
+ printk(" agsize: %d%d\n", bmp->db_agsize);
+ printk(" agl2size: %d\n", bmp->db_agl2size);
+ printk(" agwidth: %d\n", bmp->db_agwidth);
+ printk(" agstart: %d\n", bmp->db_agstart);
+ printk(" agheigth: %d\n", bmp->db_agheigth);
+ printk(" aglevel: %d\n", bmp->db_aglevel);
+ printk(" maxlevel: %d\n", bmp->db_maxlevel);
+ printk(" maxag: %d\n", bmp->db_maxag);
+ printk(" agpref: %d\n", bmp->db_agpref);
+ printk(" l2nbppg: %d\n", bmp->db_l2nbperpage);
+}
+
+
+/*
+ * dbPrtCtl()
+ */
+static void dbPrtCtl(struct dmapctl * dcp)
+{
+ int i, j, n;
+
+ printk(" height: %08x\n", le32_to_cpu(dcp->height));
+ printk(" leafidx: %08x\n", le32_to_cpu(dcp->leafidx));
+ printk(" budmin: %08x\n", dcp->budmin);
+ printk(" nleafs: %08x\n", le32_to_cpu(dcp->nleafs));
+ printk(" l2nleafs: %08x\n", le32_to_cpu(dcp->l2nleafs));
+
+ printk("\n Tree:\n");
+ for (i = 0; i < CTLLEAFIND; i += 8) {
+ n = min(8, CTLLEAFIND - i);
+
+ for (j = 0; j < n; j++)
+ printf(" [%03x]: %02x", i + j,
+ (char) dcp->stree[i + j]);
+ printf("\n");
+ }
+
+ printk("\n Tree Leaves:\n");
+ for (i = 0; i < LPERCTL; i += 8) {
+ n = min(8, LPERCTL - i);
+
+ for (j = 0; j < n; j++)
+ printf(" [%03x]: %02x",
+ i + j,
+ (char) dcp->stree[i + j + CTLLEAFIND]);
+ printf("\n");
+ }
+}
+#endif /* _JFS_DEBUG_DMAP */
diff --git a/fs/jfs/jfs_dmap.h b/fs/jfs/jfs_dmap.h
new file mode 100644
index 00000000000000..9599b9ff109d0c
--- /dev/null
+++ b/fs/jfs/jfs_dmap.h
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2000-2002
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _H_JFS_DMAP
+#define _H_JFS_DMAP
+
+#include "jfs_txnmgr.h"
+
+#define BMAPVERSION 1 /* version number */
+#define TREESIZE (256+64+16+4+1) /* size of a dmap tree */
+#define LEAFIND (64+16+4+1) /* index of 1st leaf of a dmap tree */
+#define LPERDMAP 256 /* num leaves per dmap tree */
+#define L2LPERDMAP 8 /* l2 number of leaves per dmap tree */
+#define DBWORD 32 /* # of blks covered by a map word */
+#define L2DBWORD 5 /* l2 # of blks covered by a mword */
+#define BUDMIN L2DBWORD /* max free string in a map word */
+#define BPERDMAP (LPERDMAP * DBWORD) /* num of blks per dmap */
+#define L2BPERDMAP 13 /* l2 num of blks per dmap */
+#define CTLTREESIZE (1024+256+64+16+4+1) /* size of a dmapctl tree */
+#define CTLLEAFIND (256+64+16+4+1) /* idx of 1st leaf of a dmapctl tree */
+#define LPERCTL 1024 /* num of leaves per dmapctl tree */
+#define L2LPERCTL 10 /* l2 num of leaves per dmapctl tree */
+#define ROOT 0 /* index of the root of a tree */
+#define NOFREE ((s8) -1) /* no blocks free */
+#define MAXAG 128 /* max number of allocation groups */
+#define L2MAXAG 7 /* l2 max num of AG */
+#define L2MINAGSZ 25 /* l2 of minimum AG size in bytes */
+#define BMAPBLKNO 0 /* lblkno of bmap within the map */
+
+/*
+ * maximum l2 number of disk blocks at the various dmapctl levels.
+ */
+#define L2MAXL0SIZE (L2BPERDMAP + 1 * L2LPERCTL)
+#define L2MAXL1SIZE (L2BPERDMAP + 2 * L2LPERCTL)
+#define L2MAXL2SIZE (L2BPERDMAP + 3 * L2LPERCTL)
+
+/*
+ * maximum number of disk blocks at the various dmapctl levels.
+ */
+#define MAXL0SIZE ((s64)1 << L2MAXL0SIZE)
+#define MAXL1SIZE ((s64)1 << L2MAXL1SIZE)
+#define MAXL2SIZE ((s64)1 << L2MAXL2SIZE)
+
+#define MAXMAPSIZE MAXL2SIZE /* maximum aggregate map size */
+
+/*
+ * determine the maximum free string for four (lower level) nodes
+ * of the tree.
+ */
+static __inline signed char TREEMAX(signed char *cp)
+{
+ signed char tmp1, tmp2;
+
+ tmp1 = max(*(cp+2), *(cp+3));
+ tmp2 = max(*(cp), *(cp+1));
+
+ return max(tmp1, tmp2);
+}
+
+/*
+ * convert disk block number to the logical block number of the dmap
+ * describing the disk block. s is the log2(number of logical blocks per page)
+ *
+ * The calculation figures out how many logical pages are in front of the dmap.
+ * - the number of dmaps preceding it
+ * - the number of L0 pages preceding its L0 page
+ * - the number of L1 pages preceding its L1 page
+ * - 3 is added to account for the L2, L1, and L0 page for this dmap
+ * - 1 is added to account for the control page of the map.
+ */
+#define BLKTODMAP(b,s) \
+ ((((b) >> 13) + ((b) >> 23) + ((b) >> 33) + 3 + 1) << (s))
+
+/*
+ * convert disk block number to the logical block number of the LEVEL 0
+ * dmapctl describing the disk block. s is the log2(number of logical blocks
+ * per page)
+ *
+ * The calculation figures out how many logical pages are in front of the L0.
+ * - the number of dmap pages preceding it
+ * - the number of L0 pages preceding it
+ * - the number of L1 pages preceding its L1 page
+ * - 2 is added to account for the L2, and L1 page for this L0
+ * - 1 is added to account for the control page of the map.
+ */
+#define BLKTOL0(b,s) \
+ (((((b) >> 23) << 10) + ((b) >> 23) + ((b) >> 33) + 2 + 1) << (s))
+
+/*
+ * convert disk block number to the logical block number of the LEVEL 1
+ * dmapctl describing the disk block. s is the log2(number of logical blocks
+ * per page)
+ *
+ * The calculation figures out how many logical pages are in front of the L1.
+ * - the number of dmap pages preceding it
+ * - the number of L0 pages preceding it
+ * - the number of L1 pages preceding it
+ * - 1 is added to account for the L2 page
+ * - 1 is added to account for the control page of the map.
+ */
+#define BLKTOL1(b,s) \
+ (((((b) >> 33) << 20) + (((b) >> 33) << 10) + ((b) >> 33) + 1 + 1) << (s))
+
+/*
+ * convert disk block number to the logical block number of the dmapctl
+ * at the specified level which describes the disk block.
+ */
+#define BLKTOCTL(b,s,l) \
+ (((l) == 2) ? 1 : ((l) == 1) ? BLKTOL1((b),(s)) : BLKTOL0((b),(s)))
+
+/*
+ * convert aggregate map size to the zero origin dmapctl level of the
+ * top dmapctl.
+ */
+#define BMAPSZTOLEV(size) \
+ (((size) <= MAXL0SIZE) ? 0 : ((size) <= MAXL1SIZE) ? 1 : 2)
+
+/* convert disk block number to allocation group number.
+ */
+#define BLKTOAG(b,sbi) ((b) >> ((sbi)->bmap->db_agl2size))
+
+/* convert allocation group number to starting disk block
+ * number.
+ */
+#define AGTOBLK(a,ip) \
+ ((s64)(a) << (JFS_SBI((ip)->i_sb)->bmap->db_agl2size))
+
+/*
+ * dmap summary tree
+ *
+ * dmaptree must be consistent with dmapctl.
+ */
+struct dmaptree {
+ s32 nleafs; /* 4: number of tree leafs */
+ s32 l2nleafs; /* 4: l2 number of tree leafs */
+ s32 leafidx; /* 4: index of first tree leaf */
+ s32 height; /* 4: height of the tree */
+ s8 budmin; /* 1: min l2 tree leaf value to combine */
+ s8 stree[TREESIZE]; /* TREESIZE: tree */
+ u8 pad[2]; /* 2: pad to word boundary */
+}; /* - 360 - */
+
+/*
+ * dmap page per 8K blocks bitmap
+ */
+struct dmap {
+ s32 nblocks; /* 4: num blks covered by this dmap */
+ s32 nfree; /* 4: num of free blks in this dmap */
+ s64 start; /* 8: starting blkno for this dmap */
+ struct dmaptree tree; /* 360: dmap tree */
+ u8 pad[1672]; /* 1672: pad to 2048 bytes */
+ u32 wmap[LPERDMAP]; /* 1024: bits of the working map */
+ u32 pmap[LPERDMAP]; /* 1024: bits of the persistent map */
+}; /* - 4096 - */
+
+/*
+ * disk map control page per level.
+ *
+ * dmapctl must be consistent with dmaptree.
+ */
+struct dmapctl {
+ s32 nleafs; /* 4: number of tree leafs */
+ s32 l2nleafs; /* 4: l2 number of tree leafs */
+ s32 leafidx; /* 4: index of the first tree leaf */
+ s32 height; /* 4: height of tree */
+ s8 budmin; /* 1: minimum l2 tree leaf value */
+ s8 stree[CTLTREESIZE]; /* CTLTREESIZE: dmapctl tree */
+ u8 pad[2714]; /* 2714: pad to 4096 */
+}; /* - 4096 - */
+
+/*
+ * common definition for dmaptree within dmap and dmapctl
+ */
+typedef union dmtree {
+ struct dmaptree t1;
+ struct dmapctl t2;
+} dmtree_t;
+
+/* macros for accessing fields within dmtree */
+#define dmt_nleafs t1.nleafs
+#define dmt_l2nleafs t1.l2nleafs
+#define dmt_leafidx t1.leafidx
+#define dmt_height t1.height
+#define dmt_budmin t1.budmin
+#define dmt_stree t1.stree
+
+/*
+ * on-disk aggregate disk allocation map descriptor.
+ */
+struct dbmap {
+ s64 dn_mapsize; /* 8: number of blocks in aggregate */
+ s64 dn_nfree; /* 8: num free blks in aggregate map */
+ s32 dn_l2nbperpage; /* 4: number of blks per page */
+ s32 dn_numag; /* 4: total number of ags */
+ s32 dn_maxlevel; /* 4: number of active ags */
+ s32 dn_maxag; /* 4: max active alloc group number */
+ s32 dn_agpref; /* 4: preferred alloc group (hint) */
+ s32 dn_aglevel; /* 4: dmapctl level holding the AG */
+ s32 dn_agheigth; /* 4: height in dmapctl of the AG */
+ s32 dn_agwidth; /* 4: width in dmapctl of the AG */
+ s32 dn_agstart; /* 4: start tree index at AG height */
+ s32 dn_agl2size; /* 4: l2 num of blks per alloc group */
+ s64 dn_agfree[MAXAG]; /* 8*MAXAG: per AG free count */
+ s64 dn_agsize; /* 8: num of blks per alloc group */
+ s8 dn_maxfreebud; /* 1: max free buddy system */
+ u8 pad[3007]; /* 3007: pad to 4096 */
+}; /* - 4096 - */
+
+/*
+ * in-memory aggregate disk allocation map descriptor.
+ */
+struct bmap {
+ struct dbmap db_bmap; /* on-disk aggregate map descriptor */
+ struct inode *db_ipbmap; /* ptr to aggregate map incore inode */
+ struct semaphore db_bmaplock; /* aggregate map lock */
+ atomic_t db_active[MAXAG]; /* count of active, open files in AG */
+ u32 *db_DBmap;
+};
+
+/* macros for accessing fields within in-memory aggregate map descriptor */
+#define db_mapsize db_bmap.dn_mapsize
+#define db_nfree db_bmap.dn_nfree
+#define db_agfree db_bmap.dn_agfree
+#define db_agsize db_bmap.dn_agsize
+#define db_agl2size db_bmap.dn_agl2size
+#define db_agwidth db_bmap.dn_agwidth
+#define db_agheigth db_bmap.dn_agheigth
+#define db_agstart db_bmap.dn_agstart
+#define db_numag db_bmap.dn_numag
+#define db_maxlevel db_bmap.dn_maxlevel
+#define db_aglevel db_bmap.dn_aglevel
+#define db_agpref db_bmap.dn_agpref
+#define db_maxag db_bmap.dn_maxag
+#define db_maxfreebud db_bmap.dn_maxfreebud
+#define db_l2nbperpage db_bmap.dn_l2nbperpage
+
+/*
+ * macros for various conversions needed by the allocators.
+ * blkstol2(), cntlz(), and cnttz() are operating system dependent functions.
+ */
+/* convert number of blocks to log2 number of blocks, rounding up to
+ * the next log2 value if blocks is not a l2 multiple.
+ */
+#define BLKSTOL2(d) (blkstol2(d))
+
+/* convert number of leafs to log2 leaf value */
+#define NLSTOL2BSZ(n) (31 - cntlz((n)) + BUDMIN)
+
+/* convert leaf index to log2 leaf value */
+#define LITOL2BSZ(n,m,b) ((((n) == 0) ? (m) : cnttz((n))) + (b))
+
+/* convert a block number to a dmap control leaf index */
+#define BLKTOCTLLEAF(b,m) \
+ (((b) & (((s64)1 << ((m) + L2LPERCTL)) - 1)) >> (m))
+
+/* convert log2 leaf value to buddy size */
+#define BUDSIZE(s,m) (1 << ((s) - (m)))
+
+/*
+ * external references.
+ */
+extern int dbMount(struct inode *ipbmap);
+
+extern int dbUnmount(struct inode *ipbmap, int mounterror);
+
+extern int dbFree(struct inode *ipbmap, s64 blkno, s64 nblocks);
+
+extern int dbUpdatePMap(struct inode *ipbmap,
+ int free, s64 blkno, s64 nblocks, struct tblock * tblk);
+
+extern int dbNextAG(struct inode *ipbmap);
+
+extern int dbAlloc(struct inode *ipbmap, s64 hint, s64 nblocks, s64 * results);
+
+extern int dbReAlloc(struct inode *ipbmap,
+ s64 blkno, s64 nblocks, s64 addnblocks, s64 * results);
+
+extern int dbSync(struct inode *ipbmap);
+extern int dbAllocBottomUp(struct inode *ip, s64 blkno, s64 nblocks);
+extern int dbExtendFS(struct inode *ipbmap, s64 blkno, s64 nblocks);
+extern void dbFinalizeBmap(struct inode *ipbmap);
+extern s64 dbMapFileSizeToMapSize(struct inode *ipbmap);
+#endif /* _H_JFS_DMAP */
diff --git a/fs/jfs/jfs_dtree.c b/fs/jfs/jfs_dtree.c
new file mode 100644
index 00000000000000..dc749c2e6fa87c
--- /dev/null
+++ b/fs/jfs/jfs_dtree.c
@@ -0,0 +1,4739 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2000-2004
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * jfs_dtree.c: directory B+-tree manager
+ *
+ * B+-tree with variable length key directory:
+ *
+ * each directory page is structured as an array of 32-byte
+ * directory entry slots initialized as a freelist
+ * to avoid search/compaction of free space at insertion.
+ * when an entry is inserted, a number of slots are allocated
+ * from the freelist as required to store variable length data
+ * of the entry; when the entry is deleted, slots of the entry
+ * are returned to freelist.
+ *
+ * leaf entry stores full name as key and file serial number
+ * (aka inode number) as data.
+ * internal/router entry stores sufffix compressed name
+ * as key and simple extent descriptor as data.
+ *
+ * each directory page maintains a sorted entry index table
+ * which stores the start slot index of sorted entries
+ * to allow binary search on the table.
+ *
+ * directory starts as a root/leaf page in on-disk inode
+ * inline data area.
+ * when it becomes full, it starts a leaf of a external extent
+ * of length of 1 block. each time the first leaf becomes full,
+ * it is extended rather than split (its size is doubled),
+ * until its length becoms 4 KBytes, from then the extent is split
+ * with new 4 Kbyte extent when it becomes full
+ * to reduce external fragmentation of small directories.
+ *
+ * blah, blah, blah, for linear scan of directory in pieces by
+ * readdir().
+ *
+ *
+ * case-insensitive directory file system
+ *
+ * names are stored in case-sensitive way in leaf entry.
+ * but stored, searched and compared in case-insensitive (uppercase) order
+ * (i.e., both search key and entry key are folded for search/compare):
+ * (note that case-sensitive order is BROKEN in storage, e.g.,
+ * sensitive: Ad, aB, aC, aD -> insensitive: aB, aC, aD, Ad
+ *
+ * entries which folds to the same key makes up a equivalent class
+ * whose members are stored as contiguous cluster (may cross page boundary)
+ * but whose order is arbitrary and acts as duplicate, e.g.,
+ * abc, Abc, aBc, abC)
+ *
+ * once match is found at leaf, requires scan forward/backward
+ * either for, in case-insensitive search, duplicate
+ * or for, in case-sensitive search, for exact match
+ *
+ * router entry must be created/stored in case-insensitive way
+ * in internal entry:
+ * (right most key of left page and left most key of right page
+ * are folded, and its suffix compression is propagated as router
+ * key in parent)
+ * (e.g., if split occurs <abc> and <aBd>, <ABD> trather than <aB>
+ * should be made the router key for the split)
+ *
+ * case-insensitive search:
+ *
+ * fold search key;
+ *
+ * case-insensitive search of B-tree:
+ * for internal entry, router key is already folded;
+ * for leaf entry, fold the entry key before comparison.
+ *
+ * if (leaf entry case-insensitive match found)
+ * if (next entry satisfies case-insensitive match)
+ * return EDUPLICATE;
+ * if (prev entry satisfies case-insensitive match)
+ * return EDUPLICATE;
+ * return match;
+ * else
+ * return no match;
+ *
+ * serialization:
+ * target directory inode lock is being held on entry/exit
+ * of all main directory service routines.
+ *
+ * log based recovery:
+ */
+
+#include <linux/fs.h>
+#include "jfs_incore.h"
+#include "jfs_superblock.h"
+#include "jfs_filsys.h"
+#include "jfs_metapage.h"
+#include "jfs_dmap.h"
+#include "jfs_unicode.h"
+#include "jfs_debug.h"
+
+/* dtree split parameter */
+struct dtsplit {
+ struct metapage *mp;
+ s16 index;
+ s16 nslot;
+ struct component_name *key;
+ ddata_t *data;
+ struct pxdlist *pxdlist;
+};
+
+#define DT_PAGE(IP, MP) BT_PAGE(IP, MP, dtpage_t, i_dtroot)
+
+/* get page buffer for specified block address */
+#define DT_GETPAGE(IP, BN, MP, SIZE, P, RC)\
+{\
+ BT_GETPAGE(IP, BN, MP, dtpage_t, SIZE, P, RC, i_dtroot)\
+ if (!(RC))\
+ {\
+ if (((P)->header.nextindex > (((BN)==0)?DTROOTMAXSLOT:(P)->header.maxslot)) ||\
+ ((BN) && ((P)->header.maxslot > DTPAGEMAXSLOT)))\
+ {\
+ BT_PUTPAGE(MP);\
+ jfs_error((IP)->i_sb, "DT_GETPAGE: dtree page corrupt");\
+ MP = NULL;\
+ RC = -EIO;\
+ }\
+ }\
+}
+
+/* for consistency */
+#define DT_PUTPAGE(MP) BT_PUTPAGE(MP)
+
+#define DT_GETSEARCH(IP, LEAF, BN, MP, P, INDEX) \
+ BT_GETSEARCH(IP, LEAF, BN, MP, dtpage_t, P, INDEX, i_dtroot)
+
+/*
+ * forward references
+ */
+static int dtSplitUp(tid_t tid, struct inode *ip,
+ struct dtsplit * split, struct btstack * btstack);
+
+static int dtSplitPage(tid_t tid, struct inode *ip, struct dtsplit * split,
+ struct metapage ** rmpp, dtpage_t ** rpp, pxd_t * rxdp);
+
+static int dtExtendPage(tid_t tid, struct inode *ip,
+ struct dtsplit * split, struct btstack * btstack);
+
+static int dtSplitRoot(tid_t tid, struct inode *ip,
+ struct dtsplit * split, struct metapage ** rmpp);
+
+static int dtDeleteUp(tid_t tid, struct inode *ip, struct metapage * fmp,
+ dtpage_t * fp, struct btstack * btstack);
+
+static int dtRelink(tid_t tid, struct inode *ip, dtpage_t * p);
+
+static int dtReadFirst(struct inode *ip, struct btstack * btstack);
+
+static int dtReadNext(struct inode *ip,
+ loff_t * offset, struct btstack * btstack);
+
+static int dtCompare(struct component_name * key, dtpage_t * p, int si);
+
+static int ciCompare(struct component_name * key, dtpage_t * p, int si,
+ int flag);
+
+static void dtGetKey(dtpage_t * p, int i, struct component_name * key,
+ int flag);
+
+static int ciGetLeafPrefixKey(dtpage_t * lp, int li, dtpage_t * rp,
+ int ri, struct component_name * key, int flag);
+
+static void dtInsertEntry(dtpage_t * p, int index, struct component_name * key,
+ ddata_t * data, struct dt_lock **);
+
+static void dtMoveEntry(dtpage_t * sp, int si, dtpage_t * dp,
+ struct dt_lock ** sdtlock, struct dt_lock ** ddtlock,
+ int do_index);
+
+static void dtDeleteEntry(dtpage_t * p, int fi, struct dt_lock ** dtlock);
+
+static void dtTruncateEntry(dtpage_t * p, int ti, struct dt_lock ** dtlock);
+
+static void dtLinelockFreelist(dtpage_t * p, int m, struct dt_lock ** dtlock);
+
+#define ciToUpper(c) UniStrupr((c)->name)
+
+/*
+ * read_index_page()
+ *
+ * Reads a page of a directory's index table.
+ * Having metadata mapped into the directory inode's address space
+ * presents a multitude of problems. We avoid this by mapping to
+ * the absolute address space outside of the *_metapage routines
+ */
+static struct metapage *read_index_page(struct inode *inode, s64 blkno)
+{
+ int rc;
+ s64 xaddr;
+ int xflag;
+ s32 xlen;
+
+ rc = xtLookup(inode, blkno, 1, &xflag, &xaddr, &xlen, 1);
+ if (rc || (xlen == 0))
+ return NULL;
+
+ return read_metapage(inode, xaddr, PSIZE, 1);
+}
+
+/*
+ * get_index_page()
+ *
+ * Same as get_index_page(), but get's a new page without reading
+ */
+static struct metapage *get_index_page(struct inode *inode, s64 blkno)
+{
+ int rc;
+ s64 xaddr;
+ int xflag;
+ s32 xlen;
+
+ rc = xtLookup(inode, blkno, 1, &xflag, &xaddr, &xlen, 1);
+ if (rc || (xlen == 0))
+ return NULL;
+
+ return get_metapage(inode, xaddr, PSIZE, 1);
+}
+
+/*
+ * find_index()
+ *
+ * Returns dtree page containing directory table entry for specified
+ * index and pointer to its entry.
+ *
+ * mp must be released by caller.
+ */
+static struct dir_table_slot *find_index(struct inode *ip, u32 index,
+ struct metapage ** mp, s64 *lblock)
+{
+ struct jfs_inode_info *jfs_ip = JFS_IP(ip);
+ s64 blkno;
+ s64 offset;
+ int page_offset;
+ struct dir_table_slot *slot;
+ static int maxWarnings = 10;
+
+ if (index < 2) {
+ if (maxWarnings) {
+ jfs_warn("find_entry called with index = %d", index);
+ maxWarnings--;
+ }
+ return 0;
+ }
+
+ if (index >= jfs_ip->next_index) {
+ jfs_warn("find_entry called with index >= next_index");
+ return 0;
+ }
+
+ if (jfs_ip->next_index <= (MAX_INLINE_DIRTABLE_ENTRY + 1)) {
+ /*
+ * Inline directory table
+ */
+ *mp = 0;
+ slot = &jfs_ip->i_dirtable[index - 2];
+ } else {
+ offset = (index - 2) * sizeof(struct dir_table_slot);
+ page_offset = offset & (PSIZE - 1);
+ blkno = ((offset + 1) >> L2PSIZE) <<
+ JFS_SBI(ip->i_sb)->l2nbperpage;
+
+ if (*mp && (*lblock != blkno)) {
+ release_metapage(*mp);
+ *mp = 0;
+ }
+ if (*mp == 0) {
+ *lblock = blkno;
+ *mp = read_index_page(ip, blkno);
+ }
+ if (*mp == 0) {
+ jfs_err("free_index: error reading directory table");
+ return 0;
+ }
+
+ slot =
+ (struct dir_table_slot *) ((char *) (*mp)->data +
+ page_offset);
+ }
+ return slot;
+}
+
+static inline void lock_index(tid_t tid, struct inode *ip, struct metapage * mp,
+ u32 index)
+{
+ struct tlock *tlck;
+ struct linelock *llck;
+ struct lv *lv;
+
+ tlck = txLock(tid, ip, mp, tlckDATA);
+ llck = (struct linelock *) tlck->lock;
+
+ if (llck->index >= llck->maxcnt)
+ llck = txLinelock(llck);
+ lv = &llck->lv[llck->index];
+
+ /*
+ * Linelock slot size is twice the size of directory table
+ * slot size. 512 entries per page.
+ */
+ lv->offset = ((index - 2) & 511) >> 1;
+ lv->length = 1;
+ llck->index++;
+}
+
+/*
+ * add_index()
+ *
+ * Adds an entry to the directory index table. This is used to provide
+ * each directory entry with a persistent index in which to resume
+ * directory traversals
+ */
+static u32 add_index(tid_t tid, struct inode *ip, s64 bn, int slot)
+{
+ struct super_block *sb = ip->i_sb;
+ struct jfs_sb_info *sbi = JFS_SBI(sb);
+ struct jfs_inode_info *jfs_ip = JFS_IP(ip);
+ u64 blkno;
+ struct dir_table_slot *dirtab_slot;
+ u32 index;
+ struct linelock *llck;
+ struct lv *lv;
+ struct metapage *mp;
+ s64 offset;
+ uint page_offset;
+ struct tlock *tlck;
+ s64 xaddr;
+
+ ASSERT(DO_INDEX(ip));
+
+ if (jfs_ip->next_index < 2) {
+ jfs_warn("add_index: next_index = %d. Resetting!",
+ jfs_ip->next_index);
+ jfs_ip->next_index = 2;
+ }
+
+ index = jfs_ip->next_index++;
+
+ if (index <= MAX_INLINE_DIRTABLE_ENTRY) {
+ /*
+ * i_size reflects size of index table, or 8 bytes per entry.
+ */
+ ip->i_size = (loff_t) (index - 1) << 3;
+
+ /*
+ * dir table fits inline within inode
+ */
+ dirtab_slot = &jfs_ip->i_dirtable[index-2];
+ dirtab_slot->flag = DIR_INDEX_VALID;
+ dirtab_slot->slot = slot;
+ DTSaddress(dirtab_slot, bn);
+
+ set_cflag(COMMIT_Dirtable, ip);
+
+ return index;
+ }
+ if (index == (MAX_INLINE_DIRTABLE_ENTRY + 1)) {
+ struct dir_table_slot temp_table[12];
+
+ /*
+ * It's time to move the inline table to an external
+ * page and begin to build the xtree
+ */
+ if (dbAlloc(ip, 0, sbi->nbperpage, &xaddr))
+ goto clean_up; /* No space */
+
+ /*
+ * Save the table, we're going to overwrite it with the
+ * xtree root
+ */
+ memcpy(temp_table, &jfs_ip->i_dirtable, sizeof(temp_table));
+
+ /*
+ * Initialize empty x-tree
+ */
+ xtInitRoot(tid, ip);
+
+ /*
+ * Allocate the first block & add it to the xtree
+ */
+ if (xtInsert(tid, ip, 0, 0, sbi->nbperpage, &xaddr, 0)) {
+ /* This really shouldn't fail */
+ jfs_warn("add_index: xtInsert failed!");
+ memcpy(&jfs_ip->i_dirtable, temp_table,
+ sizeof (temp_table));
+ goto clean_up;
+ }
+ ip->i_size = PSIZE;
+ ip->i_blocks += LBLK2PBLK(sb, sbi->nbperpage);
+
+ if ((mp = get_index_page(ip, 0)) == 0) {
+ jfs_err("add_index: get_metapage failed!");
+ xtTruncate(tid, ip, 0, COMMIT_PWMAP);
+ memcpy(&jfs_ip->i_dirtable, temp_table,
+ sizeof (temp_table));
+ goto clean_up;
+ }
+ tlck = txLock(tid, ip, mp, tlckDATA);
+ llck = (struct linelock *) & tlck->lock;
+ ASSERT(llck->index == 0);
+ lv = &llck->lv[0];
+
+ lv->offset = 0;
+ lv->length = 6; /* tlckDATA slot size is 16 bytes */
+ llck->index++;
+
+ memcpy(mp->data, temp_table, sizeof(temp_table));
+
+ mark_metapage_dirty(mp);
+ release_metapage(mp);
+
+ /*
+ * Logging is now directed by xtree tlocks
+ */
+ clear_cflag(COMMIT_Dirtable, ip);
+ }
+
+ offset = (index - 2) * sizeof(struct dir_table_slot);
+ page_offset = offset & (PSIZE - 1);
+ blkno = ((offset + 1) >> L2PSIZE) << sbi->l2nbperpage;
+ if (page_offset == 0) {
+ /*
+ * This will be the beginning of a new page
+ */
+ xaddr = 0;
+ if (xtInsert(tid, ip, 0, blkno, sbi->nbperpage, &xaddr, 0)) {
+ jfs_warn("add_index: xtInsert failed!");
+ goto clean_up;
+ }
+ ip->i_size += PSIZE;
+ ip->i_blocks += LBLK2PBLK(sb, sbi->nbperpage);
+
+ if ((mp = get_index_page(ip, blkno)))
+ memset(mp->data, 0, PSIZE); /* Just looks better */
+ else
+ xtTruncate(tid, ip, offset, COMMIT_PWMAP);
+ } else
+ mp = read_index_page(ip, blkno);
+
+ if (mp == 0) {
+ jfs_err("add_index: get/read_metapage failed!");
+ goto clean_up;
+ }
+
+ lock_index(tid, ip, mp, index);
+
+ dirtab_slot =
+ (struct dir_table_slot *) ((char *) mp->data + page_offset);
+ dirtab_slot->flag = DIR_INDEX_VALID;
+ dirtab_slot->slot = slot;
+ DTSaddress(dirtab_slot, bn);
+
+ mark_metapage_dirty(mp);
+ release_metapage(mp);
+
+ return index;
+
+ clean_up:
+
+ jfs_ip->next_index--;
+
+ return 0;
+}
+
+/*
+ * free_index()
+ *
+ * Marks an entry to the directory index table as free.
+ */
+static void free_index(tid_t tid, struct inode *ip, u32 index, u32 next)
+{
+ struct dir_table_slot *dirtab_slot;
+ s64 lblock;
+ struct metapage *mp = 0;
+
+ dirtab_slot = find_index(ip, index, &mp, &lblock);
+
+ if (dirtab_slot == 0)
+ return;
+
+ dirtab_slot->flag = DIR_INDEX_FREE;
+ dirtab_slot->slot = dirtab_slot->addr1 = 0;
+ dirtab_slot->addr2 = cpu_to_le32(next);
+
+ if (mp) {
+ lock_index(tid, ip, mp, index);
+ mark_metapage_dirty(mp);
+ release_metapage(mp);
+ } else
+ set_cflag(COMMIT_Dirtable, ip);
+}
+
+/*
+ * modify_index()
+ *
+ * Changes an entry in the directory index table
+ */
+static void modify_index(tid_t tid, struct inode *ip, u32 index, s64 bn,
+ int slot, struct metapage ** mp, u64 *lblock)
+{
+ struct dir_table_slot *dirtab_slot;
+
+ dirtab_slot = find_index(ip, index, mp, lblock);
+
+ if (dirtab_slot == 0)
+ return;
+
+ DTSaddress(dirtab_slot, bn);
+ dirtab_slot->slot = slot;
+
+ if (*mp) {
+ lock_index(tid, ip, *mp, index);
+ mark_metapage_dirty(*mp);
+ } else
+ set_cflag(COMMIT_Dirtable, ip);
+}
+
+/*
+ * read_index()
+ *
+ * reads a directory table slot
+ */
+static int read_index(struct inode *ip, u32 index,
+ struct dir_table_slot * dirtab_slot)
+{
+ s64 lblock;
+ struct metapage *mp = 0;
+ struct dir_table_slot *slot;
+
+ slot = find_index(ip, index, &mp, &lblock);
+ if (slot == 0) {
+ return -EIO;
+ }
+
+ memcpy(dirtab_slot, slot, sizeof(struct dir_table_slot));
+
+ if (mp)
+ release_metapage(mp);
+
+ return 0;
+}
+
+/*
+ * dtSearch()
+ *
+ * function:
+ * Search for the entry with specified key
+ *
+ * parameter:
+ *
+ * return: 0 - search result on stack, leaf page pinned;
+ * errno - I/O error
+ */
+int dtSearch(struct inode *ip, struct component_name * key, ino_t * data,
+ struct btstack * btstack, int flag)
+{
+ int rc = 0;
+ int cmp = 1; /* init for empty page */
+ s64 bn;
+ struct metapage *mp;
+ dtpage_t *p;
+ s8 *stbl;
+ int base, index, lim;
+ struct btframe *btsp;
+ pxd_t *pxd;
+ int psize = 288; /* initial in-line directory */
+ ino_t inumber;
+ struct component_name ciKey;
+ struct super_block *sb = ip->i_sb;
+
+ ciKey.name =
+ (wchar_t *) kmalloc((JFS_NAME_MAX + 1) * sizeof(wchar_t),
+ GFP_NOFS);
+ if (ciKey.name == 0) {
+ rc = -ENOMEM;
+ goto dtSearch_Exit2;
+ }
+
+
+ /* uppercase search key for c-i directory */
+ UniStrcpy(ciKey.name, key->name);
+ ciKey.namlen = key->namlen;
+
+ /* only uppercase if case-insensitive support is on */
+ if ((JFS_SBI(sb)->mntflag & JFS_OS2) == JFS_OS2) {
+ ciToUpper(&ciKey);
+ }
+ BT_CLR(btstack); /* reset stack */
+
+ /* init level count for max pages to split */
+ btstack->nsplit = 1;
+
+ /*
+ * search down tree from root:
+ *
+ * between two consecutive entries of <Ki, Pi> and <Kj, Pj> of
+ * internal page, child page Pi contains entry with k, Ki <= K < Kj.
+ *
+ * if entry with search key K is not found
+ * internal page search find the entry with largest key Ki
+ * less than K which point to the child page to search;
+ * leaf page search find the entry with smallest key Kj
+ * greater than K so that the returned index is the position of
+ * the entry to be shifted right for insertion of new entry.
+ * for empty tree, search key is greater than any key of the tree.
+ *
+ * by convention, root bn = 0.
+ */
+ for (bn = 0;;) {
+ /* get/pin the page to search */
+ DT_GETPAGE(ip, bn, mp, psize, p, rc);
+ if (rc)
+ goto dtSearch_Exit1;
+
+ /* get sorted entry table of the page */
+ stbl = DT_GETSTBL(p);
+
+ /*
+ * binary search with search key K on the current page.
+ */
+ for (base = 0, lim = p->header.nextindex; lim; lim >>= 1) {
+ index = base + (lim >> 1);
+
+ if (p->header.flag & BT_LEAF) {
+ /* uppercase leaf name to compare */
+ cmp =
+ ciCompare(&ciKey, p, stbl[index],
+ JFS_SBI(sb)->mntflag);
+ } else {
+ /* router key is in uppercase */
+
+ cmp = dtCompare(&ciKey, p, stbl[index]);
+
+
+ }
+ if (cmp == 0) {
+ /*
+ * search hit
+ */
+ /* search hit - leaf page:
+ * return the entry found
+ */
+ if (p->header.flag & BT_LEAF) {
+ inumber = le32_to_cpu(
+ ((struct ldtentry *) & p->slot[stbl[index]])->inumber);
+
+ /*
+ * search for JFS_LOOKUP
+ */
+ if (flag == JFS_LOOKUP) {
+ *data = inumber;
+ rc = 0;
+ goto out;
+ }
+
+ /*
+ * search for JFS_CREATE
+ */
+ if (flag == JFS_CREATE) {
+ *data = inumber;
+ rc = -EEXIST;
+ goto out;
+ }
+
+ /*
+ * search for JFS_REMOVE or JFS_RENAME
+ */
+ if ((flag == JFS_REMOVE ||
+ flag == JFS_RENAME) &&
+ *data != inumber) {
+ rc = -ESTALE;
+ goto out;
+ }
+
+ /*
+ * JFS_REMOVE|JFS_FINDDIR|JFS_RENAME
+ */
+ /* save search result */
+ *data = inumber;
+ btsp = btstack->top;
+ btsp->bn = bn;
+ btsp->index = index;
+ btsp->mp = mp;
+
+ rc = 0;
+ goto dtSearch_Exit1;
+ }
+
+ /* search hit - internal page:
+ * descend/search its child page
+ */
+ goto getChild;
+ }
+
+ if (cmp > 0) {
+ base = index + 1;
+ --lim;
+ }
+ }
+
+ /*
+ * search miss
+ *
+ * base is the smallest index with key (Kj) greater than
+ * search key (K) and may be zero or (maxindex + 1) index.
+ */
+ /*
+ * search miss - leaf page
+ *
+ * return location of entry (base) where new entry with
+ * search key K is to be inserted.
+ */
+ if (p->header.flag & BT_LEAF) {
+ /*
+ * search for JFS_LOOKUP, JFS_REMOVE, or JFS_RENAME
+ */
+ if (flag == JFS_LOOKUP || flag == JFS_REMOVE ||
+ flag == JFS_RENAME) {
+ rc = -ENOENT;
+ goto out;
+ }
+
+ /*
+ * search for JFS_CREATE|JFS_FINDDIR:
+ *
+ * save search result
+ */
+ *data = 0;
+ btsp = btstack->top;
+ btsp->bn = bn;
+ btsp->index = base;
+ btsp->mp = mp;
+
+ rc = 0;
+ goto dtSearch_Exit1;
+ }
+
+ /*
+ * search miss - internal page
+ *
+ * if base is non-zero, decrement base by one to get the parent
+ * entry of the child page to search.
+ */
+ index = base ? base - 1 : base;
+
+ /*
+ * go down to child page
+ */
+ getChild:
+ /* update max. number of pages to split */
+ if (BT_STACK_FULL(btstack)) {
+ /* Something's corrupted, mark filesytem dirty so
+ * chkdsk will fix it.
+ */
+ jfs_error(sb, "stack overrun in dtSearch!");
+ BT_STACK_DUMP(btstack);
+ rc = -EIO;
+ goto out;
+ }
+ btstack->nsplit++;
+
+ /* push (bn, index) of the parent page/entry */
+ BT_PUSH(btstack, bn, index);
+
+ /* get the child page block number */
+ pxd = (pxd_t *) & p->slot[stbl[index]];
+ bn = addressPXD(pxd);
+ psize = lengthPXD(pxd) << JFS_SBI(ip->i_sb)->l2bsize;
+
+ /* unpin the parent page */
+ DT_PUTPAGE(mp);
+ }
+
+ out:
+ DT_PUTPAGE(mp);
+
+ dtSearch_Exit1:
+
+ kfree(ciKey.name);
+
+ dtSearch_Exit2:
+
+ return rc;
+}
+
+
+/*
+ * dtInsert()
+ *
+ * function: insert an entry to directory tree
+ *
+ * parameter:
+ *
+ * return: 0 - success;
+ * errno - failure;
+ */
+int dtInsert(tid_t tid, struct inode *ip,
+ struct component_name * name, ino_t * fsn, struct btstack * btstack)
+{
+ int rc = 0;
+ struct metapage *mp; /* meta-page buffer */
+ dtpage_t *p; /* base B+-tree index page */
+ s64 bn;
+ int index;
+ struct dtsplit split; /* split information */
+ ddata_t data;
+ struct dt_lock *dtlck;
+ int n;
+ struct tlock *tlck;
+ struct lv *lv;
+
+ /*
+ * retrieve search result
+ *
+ * dtSearch() returns (leaf page pinned, index at which to insert).
+ * n.b. dtSearch() may return index of (maxindex + 1) of
+ * the full page.
+ */
+ DT_GETSEARCH(ip, btstack->top, bn, mp, p, index);
+
+ /*
+ * insert entry for new key
+ */
+ if (DO_INDEX(ip)) {
+ if (JFS_IP(ip)->next_index == DIREND) {
+ DT_PUTPAGE(mp);
+ return -EMLINK;
+ }
+ n = NDTLEAF(name->namlen);
+ data.leaf.tid = tid;
+ data.leaf.ip = ip;
+ } else {
+ n = NDTLEAF_LEGACY(name->namlen);
+ data.leaf.ip = 0; /* signifies legacy directory format */
+ }
+ data.leaf.ino = cpu_to_le32(*fsn);
+
+ /*
+ * leaf page does not have enough room for new entry:
+ *
+ * extend/split the leaf page;
+ *
+ * dtSplitUp() will insert the entry and unpin the leaf page.
+ */
+ if (n > p->header.freecnt) {
+ split.mp = mp;
+ split.index = index;
+ split.nslot = n;
+ split.key = name;
+ split.data = &data;
+ rc = dtSplitUp(tid, ip, &split, btstack);
+ return rc;
+ }
+
+ /*
+ * leaf page does have enough room for new entry:
+ *
+ * insert the new data entry into the leaf page;
+ */
+ BT_MARK_DIRTY(mp, ip);
+ /*
+ * acquire a transaction lock on the leaf page
+ */
+ tlck = txLock(tid, ip, mp, tlckDTREE | tlckENTRY);
+ dtlck = (struct dt_lock *) & tlck->lock;
+ ASSERT(dtlck->index == 0);
+ lv = & dtlck->lv[0];
+
+ /* linelock header */
+ lv->offset = 0;
+ lv->length = 1;
+ dtlck->index++;
+
+ dtInsertEntry(p, index, name, &data, &dtlck);
+
+ /* linelock stbl of non-root leaf page */
+ if (!(p->header.flag & BT_ROOT)) {
+ if (dtlck->index >= dtlck->maxcnt)
+ dtlck = (struct dt_lock *) txLinelock(dtlck);
+ lv = & dtlck->lv[dtlck->index];
+ n = index >> L2DTSLOTSIZE;
+ lv->offset = p->header.stblindex + n;
+ lv->length =
+ ((p->header.nextindex - 1) >> L2DTSLOTSIZE) - n + 1;
+ dtlck->index++;
+ }
+
+ /* unpin the leaf page */
+ DT_PUTPAGE(mp);
+
+ return 0;
+}
+
+
+/*
+ * dtSplitUp()
+ *
+ * function: propagate insertion bottom up;
+ *
+ * parameter:
+ *
+ * return: 0 - success;
+ * errno - failure;
+ * leaf page unpinned;
+ */
+static int dtSplitUp(tid_t tid,
+ struct inode *ip, struct dtsplit * split, struct btstack * btstack)
+{
+ struct jfs_sb_info *sbi = JFS_SBI(ip->i_sb);
+ int rc = 0;
+ struct metapage *smp;
+ dtpage_t *sp; /* split page */
+ struct metapage *rmp;
+ dtpage_t *rp; /* new right page split from sp */
+ pxd_t rpxd; /* new right page extent descriptor */
+ struct metapage *lmp;
+ dtpage_t *lp; /* left child page */
+ int skip; /* index of entry of insertion */
+ struct btframe *parent; /* parent page entry on traverse stack */
+ s64 xaddr, nxaddr;
+ int xlen, xsize;
+ struct pxdlist pxdlist;
+ pxd_t *pxd;
+ struct component_name key = { 0, 0 };
+ ddata_t *data = split->data;
+ int n;
+ struct dt_lock *dtlck;
+ struct tlock *tlck;
+ struct lv *lv;
+
+ /* get split page */
+ smp = split->mp;
+ sp = DT_PAGE(ip, smp);
+
+ key.name =
+ (wchar_t *) kmalloc((JFS_NAME_MAX + 2) * sizeof(wchar_t),
+ GFP_NOFS);
+ if (key.name == 0) {
+ DT_PUTPAGE(smp);
+ rc = -ENOMEM;
+ goto dtSplitUp_Exit;
+ }
+
+ /*
+ * split leaf page
+ *
+ * The split routines insert the new entry, and
+ * acquire txLock as appropriate.
+ */
+ /*
+ * split root leaf page:
+ */
+ if (sp->header.flag & BT_ROOT) {
+ /*
+ * allocate a single extent child page
+ */
+ xlen = 1;
+ n = sbi->bsize >> L2DTSLOTSIZE;
+ n -= (n + 31) >> L2DTSLOTSIZE; /* stbl size */
+ n -= DTROOTMAXSLOT - sp->header.freecnt; /* header + entries */
+ if (n <= split->nslot)
+ xlen++;
+ if ((rc = dbAlloc(ip, 0, (s64) xlen, &xaddr))) {
+ DT_PUTPAGE(smp);
+ goto freeKeyName;
+ }
+
+ pxdlist.maxnpxd = 1;
+ pxdlist.npxd = 0;
+ pxd = &pxdlist.pxd[0];
+ PXDaddress(pxd, xaddr);
+ PXDlength(pxd, xlen);
+ split->pxdlist = &pxdlist;
+ rc = dtSplitRoot(tid, ip, split, &rmp);
+
+ if (!rc)
+ DT_PUTPAGE(rmp);
+
+ DT_PUTPAGE(smp);
+
+ goto freeKeyName;
+ }
+
+ /*
+ * extend first leaf page
+ *
+ * extend the 1st extent if less than buffer page size
+ * (dtExtendPage() reurns leaf page unpinned)
+ */
+ pxd = &sp->header.self;
+ xlen = lengthPXD(pxd);
+ xsize = xlen << sbi->l2bsize;
+ if (xsize < PSIZE) {
+ xaddr = addressPXD(pxd);
+ n = xsize >> L2DTSLOTSIZE;
+ n -= (n + 31) >> L2DTSLOTSIZE; /* stbl size */
+ if ((n + sp->header.freecnt) <= split->nslot)
+ n = xlen + (xlen << 1);
+ else
+ n = xlen;
+ if ((rc = dbReAlloc(sbi->ipbmap, xaddr, (s64) xlen,
+ (s64) n, &nxaddr)))
+ goto extendOut;
+
+ pxdlist.maxnpxd = 1;
+ pxdlist.npxd = 0;
+ pxd = &pxdlist.pxd[0];
+ PXDaddress(pxd, nxaddr)
+ PXDlength(pxd, xlen + n);
+ split->pxdlist = &pxdlist;
+ if ((rc = dtExtendPage(tid, ip, split, btstack))) {
+ nxaddr = addressPXD(pxd);
+ if (xaddr != nxaddr) {
+ /* free relocated extent */
+ xlen = lengthPXD(pxd);
+ dbFree(ip, nxaddr, (s64) xlen);
+ } else {
+ /* free extended delta */
+ xlen = lengthPXD(pxd) - n;
+ xaddr = addressPXD(pxd) + xlen;
+ dbFree(ip, xaddr, (s64) n);
+ }
+ }
+
+ extendOut:
+ DT_PUTPAGE(smp);
+ goto freeKeyName;
+ }
+
+ /*
+ * split leaf page <sp> into <sp> and a new right page <rp>.
+ *
+ * return <rp> pinned and its extent descriptor <rpxd>
+ */
+ /*
+ * allocate new directory page extent and
+ * new index page(s) to cover page split(s)
+ *
+ * allocation hint: ?
+ */
+ n = btstack->nsplit;
+ pxdlist.maxnpxd = pxdlist.npxd = 0;
+ xlen = sbi->nbperpage;
+ for (pxd = pxdlist.pxd; n > 0; n--, pxd++) {
+ if ((rc = dbAlloc(ip, 0, (s64) xlen, &xaddr)) == 0) {
+ PXDaddress(pxd, xaddr);
+ PXDlength(pxd, xlen);
+ pxdlist.maxnpxd++;
+ continue;
+ }
+
+ DT_PUTPAGE(smp);
+
+ /* undo allocation */
+ goto splitOut;
+ }
+
+ split->pxdlist = &pxdlist;
+ if ((rc = dtSplitPage(tid, ip, split, &rmp, &rp, &rpxd))) {
+ DT_PUTPAGE(smp);
+
+ /* undo allocation */
+ goto splitOut;
+ }
+
+ /*
+ * propagate up the router entry for the leaf page just split
+ *
+ * insert a router entry for the new page into the parent page,
+ * propagate the insert/split up the tree by walking back the stack
+ * of (bn of parent page, index of child page entry in parent page)
+ * that were traversed during the search for the page that split.
+ *
+ * the propagation of insert/split up the tree stops if the root
+ * splits or the page inserted into doesn't have to split to hold
+ * the new entry.
+ *
+ * the parent entry for the split page remains the same, and
+ * a new entry is inserted at its right with the first key and
+ * block number of the new right page.
+ *
+ * There are a maximum of 4 pages pinned at any time:
+ * two children, left parent and right parent (when the parent splits).
+ * keep the child pages pinned while working on the parent.
+ * make sure that all pins are released at exit.
+ */
+ while ((parent = BT_POP(btstack)) != NULL) {
+ /* parent page specified by stack frame <parent> */
+
+ /* keep current child pages (<lp>, <rp>) pinned */
+ lmp = smp;
+ lp = sp;
+
+ /*
+ * insert router entry in parent for new right child page <rp>
+ */
+ /* get the parent page <sp> */
+ DT_GETPAGE(ip, parent->bn, smp, PSIZE, sp, rc);
+ if (rc) {
+ DT_PUTPAGE(lmp);
+ DT_PUTPAGE(rmp);
+ goto splitOut;
+ }
+
+ /*
+ * The new key entry goes ONE AFTER the index of parent entry,
+ * because the split was to the right.
+ */
+ skip = parent->index + 1;
+
+ /*
+ * compute the key for the router entry
+ *
+ * key suffix compression:
+ * for internal pages that have leaf pages as children,
+ * retain only what's needed to distinguish between
+ * the new entry and the entry on the page to its left.
+ * If the keys compare equal, retain the entire key.
+ *
+ * note that compression is performed only at computing
+ * router key at the lowest internal level.
+ * further compression of the key between pairs of higher
+ * level internal pages loses too much information and
+ * the search may fail.
+ * (e.g., two adjacent leaf pages of {a, ..., x} {xx, ...,}
+ * results in two adjacent parent entries (a)(xx).
+ * if split occurs between these two entries, and
+ * if compression is applied, the router key of parent entry
+ * of right page (x) will divert search for x into right
+ * subtree and miss x in the left subtree.)
+ *
+ * the entire key must be retained for the next-to-leftmost
+ * internal key at any level of the tree, or search may fail
+ * (e.g., ?)
+ */
+ switch (rp->header.flag & BT_TYPE) {
+ case BT_LEAF:
+ /*
+ * compute the length of prefix for suffix compression
+ * between last entry of left page and first entry
+ * of right page
+ */
+ if ((sp->header.flag & BT_ROOT && skip > 1) ||
+ sp->header.prev != 0 || skip > 1) {
+ /* compute uppercase router prefix key */
+ rc = ciGetLeafPrefixKey(lp,
+ lp->header.nextindex-1,
+ rp, 0, &key,
+ sbi->mntflag);
+ if (rc) {
+ DT_PUTPAGE(lmp);
+ DT_PUTPAGE(rmp);
+ DT_PUTPAGE(smp);
+ goto splitOut;
+ }
+ } else {
+ /* next to leftmost entry of
+ lowest internal level */
+
+ /* compute uppercase router key */
+ dtGetKey(rp, 0, &key, sbi->mntflag);
+ key.name[key.namlen] = 0;
+
+ if ((sbi->mntflag & JFS_OS2) == JFS_OS2)
+ ciToUpper(&key);
+ }
+
+ n = NDTINTERNAL(key.namlen);
+ break;
+
+ case BT_INTERNAL:
+ dtGetKey(rp, 0, &key, sbi->mntflag);
+ n = NDTINTERNAL(key.namlen);
+ break;
+
+ default:
+ jfs_err("dtSplitUp(): UFO!");
+ break;
+ }
+
+ /* unpin left child page */
+ DT_PUTPAGE(lmp);
+
+ /*
+ * compute the data for the router entry
+ */
+ data->xd = rpxd; /* child page xd */
+
+ /*
+ * parent page is full - split the parent page
+ */
+ if (n > sp->header.freecnt) {
+ /* init for parent page split */
+ split->mp = smp;
+ split->index = skip; /* index at insert */
+ split->nslot = n;
+ split->key = &key;
+ /* split->data = data; */
+
+ /* unpin right child page */
+ DT_PUTPAGE(rmp);
+
+ /* The split routines insert the new entry,
+ * acquire txLock as appropriate.
+ * return <rp> pinned and its block number <rbn>.
+ */
+ rc = (sp->header.flag & BT_ROOT) ?
+ dtSplitRoot(tid, ip, split, &rmp) :
+ dtSplitPage(tid, ip, split, &rmp, &rp, &rpxd);
+ if (rc) {
+ DT_PUTPAGE(smp);
+ goto splitOut;
+ }
+
+ /* smp and rmp are pinned */
+ }
+ /*
+ * parent page is not full - insert router entry in parent page
+ */
+ else {
+ BT_MARK_DIRTY(smp, ip);
+ /*
+ * acquire a transaction lock on the parent page
+ */
+ tlck = txLock(tid, ip, smp, tlckDTREE | tlckENTRY);
+ dtlck = (struct dt_lock *) & tlck->lock;
+ ASSERT(dtlck->index == 0);
+ lv = & dtlck->lv[0];
+
+ /* linelock header */
+ lv->offset = 0;
+ lv->length = 1;
+ dtlck->index++;
+
+ /* linelock stbl of non-root parent page */
+ if (!(sp->header.flag & BT_ROOT)) {
+ lv++;
+ n = skip >> L2DTSLOTSIZE;
+ lv->offset = sp->header.stblindex + n;
+ lv->length =
+ ((sp->header.nextindex -
+ 1) >> L2DTSLOTSIZE) - n + 1;
+ dtlck->index++;
+ }
+
+ dtInsertEntry(sp, skip, &key, data, &dtlck);
+
+ /* exit propagate up */
+ break;
+ }
+ }
+
+ /* unpin current split and its right page */
+ DT_PUTPAGE(smp);
+ DT_PUTPAGE(rmp);
+
+ /*
+ * free remaining extents allocated for split
+ */
+ splitOut:
+ n = pxdlist.npxd;
+ pxd = &pxdlist.pxd[n];
+ for (; n < pxdlist.maxnpxd; n++, pxd++)
+ dbFree(ip, addressPXD(pxd), (s64) lengthPXD(pxd));
+
+ freeKeyName:
+ kfree(key.name);
+
+ dtSplitUp_Exit:
+
+ return rc;
+}
+
+
+/*
+ * dtSplitPage()
+ *
+ * function: Split a non-root page of a btree.
+ *
+ * parameter:
+ *
+ * return: 0 - success;
+ * errno - failure;
+ * return split and new page pinned;
+ */
+static int dtSplitPage(tid_t tid, struct inode *ip, struct dtsplit * split,
+ struct metapage ** rmpp, dtpage_t ** rpp, pxd_t * rpxdp)
+{
+ struct super_block *sb = ip->i_sb;
+ int rc = 0;
+ struct metapage *smp;
+ dtpage_t *sp;
+ struct metapage *rmp;
+ dtpage_t *rp; /* new right page allocated */
+ s64 rbn; /* new right page block number */
+ struct metapage *mp;
+ dtpage_t *p;
+ s64 nextbn;
+ struct pxdlist *pxdlist;
+ pxd_t *pxd;
+ int skip, nextindex, half, left, nxt, off, si;
+ struct ldtentry *ldtentry;
+ struct idtentry *idtentry;
+ u8 *stbl;
+ struct dtslot *f;
+ int fsi, stblsize;
+ int n;
+ struct dt_lock *sdtlck, *rdtlck;
+ struct tlock *tlck;
+ struct dt_lock *dtlck;
+ struct lv *slv, *rlv, *lv;
+
+ /* get split page */
+ smp = split->mp;
+ sp = DT_PAGE(ip, smp);
+
+ /*
+ * allocate the new right page for the split
+ */
+ pxdlist = split->pxdlist;
+ pxd = &pxdlist->pxd[pxdlist->npxd];
+ pxdlist->npxd++;
+ rbn = addressPXD(pxd);
+ rmp = get_metapage(ip, rbn, PSIZE, 1);
+ if (rmp == NULL)
+ return -EIO;
+
+ jfs_info("dtSplitPage: ip:0x%p smp:0x%p rmp:0x%p", ip, smp, rmp);
+
+ BT_MARK_DIRTY(rmp, ip);
+ /*
+ * acquire a transaction lock on the new right page
+ */
+ tlck = txLock(tid, ip, rmp, tlckDTREE | tlckNEW);
+ rdtlck = (struct dt_lock *) & tlck->lock;
+
+ rp = (dtpage_t *) rmp->data;
+ *rpp = rp;
+ rp->header.self = *pxd;
+
+ BT_MARK_DIRTY(smp, ip);
+ /*
+ * acquire a transaction lock on the split page
+ *
+ * action:
+ */
+ tlck = txLock(tid, ip, smp, tlckDTREE | tlckENTRY);
+ sdtlck = (struct dt_lock *) & tlck->lock;
+
+ /* linelock header of split page */
+ ASSERT(sdtlck->index == 0);
+ slv = & sdtlck->lv[0];
+ slv->offset = 0;
+ slv->length = 1;
+ sdtlck->index++;
+
+ /*
+ * initialize/update sibling pointers between sp and rp
+ */
+ nextbn = le64_to_cpu(sp->header.next);
+ rp->header.next = cpu_to_le64(nextbn);
+ rp->header.prev = cpu_to_le64(addressPXD(&sp->header.self));
+ sp->header.next = cpu_to_le64(rbn);
+
+ /*
+ * initialize new right page
+ */
+ rp->header.flag = sp->header.flag;
+
+ /* compute sorted entry table at start of extent data area */
+ rp->header.nextindex = 0;
+ rp->header.stblindex = 1;
+
+ n = PSIZE >> L2DTSLOTSIZE;
+ rp->header.maxslot = n;
+ stblsize = (n + 31) >> L2DTSLOTSIZE; /* in unit of slot */
+
+ /* init freelist */
+ fsi = rp->header.stblindex + stblsize;
+ rp->header.freelist = fsi;
+ rp->header.freecnt = rp->header.maxslot - fsi;
+
+ /*
+ * sequential append at tail: append without split
+ *
+ * If splitting the last page on a level because of appending
+ * a entry to it (skip is maxentry), it's likely that the access is
+ * sequential. Adding an empty page on the side of the level is less
+ * work and can push the fill factor much higher than normal.
+ * If we're wrong it's no big deal, we'll just do the split the right
+ * way next time.
+ * (It may look like it's equally easy to do a similar hack for
+ * reverse sorted data, that is, split the tree left,
+ * but it's not. Be my guest.)
+ */
+ if (nextbn == 0 && split->index == sp->header.nextindex) {
+ /* linelock header + stbl (first slot) of new page */
+ rlv = & rdtlck->lv[rdtlck->index];
+ rlv->offset = 0;
+ rlv->length = 2;
+ rdtlck->index++;
+
+ /*
+ * initialize freelist of new right page
+ */
+ f = &rp->slot[fsi];
+ for (fsi++; fsi < rp->header.maxslot; f++, fsi++)
+ f->next = fsi;
+ f->next = -1;
+
+ /* insert entry at the first entry of the new right page */
+ dtInsertEntry(rp, 0, split->key, split->data, &rdtlck);
+
+ goto out;
+ }
+
+ /*
+ * non-sequential insert (at possibly middle page)
+ */
+
+ /*
+ * update prev pointer of previous right sibling page;
+ */
+ if (nextbn != 0) {
+ DT_GETPAGE(ip, nextbn, mp, PSIZE, p, rc);
+ if (rc) {
+ discard_metapage(rmp);
+ return rc;
+ }
+
+ BT_MARK_DIRTY(mp, ip);
+ /*
+ * acquire a transaction lock on the next page
+ */
+ tlck = txLock(tid, ip, mp, tlckDTREE | tlckRELINK);
+ jfs_info("dtSplitPage: tlck = 0x%p, ip = 0x%p, mp=0x%p",
+ tlck, ip, mp);
+ dtlck = (struct dt_lock *) & tlck->lock;
+
+ /* linelock header of previous right sibling page */
+ lv = & dtlck->lv[dtlck->index];
+ lv->offset = 0;
+ lv->length = 1;
+ dtlck->index++;
+
+ p->header.prev = cpu_to_le64(rbn);
+
+ DT_PUTPAGE(mp);
+ }
+
+ /*
+ * split the data between the split and right pages.
+ */
+ skip = split->index;
+ half = (PSIZE >> L2DTSLOTSIZE) >> 1; /* swag */
+ left = 0;
+
+ /*
+ * compute fill factor for split pages
+ *
+ * <nxt> traces the next entry to move to rp
+ * <off> traces the next entry to stay in sp
+ */
+ stbl = (u8 *) & sp->slot[sp->header.stblindex];
+ nextindex = sp->header.nextindex;
+ for (nxt = off = 0; nxt < nextindex; ++off) {
+ if (off == skip)
+ /* check for fill factor with new entry size */
+ n = split->nslot;
+ else {
+ si = stbl[nxt];
+ switch (sp->header.flag & BT_TYPE) {
+ case BT_LEAF:
+ ldtentry = (struct ldtentry *) & sp->slot[si];
+ if (DO_INDEX(ip))
+ n = NDTLEAF(ldtentry->namlen);
+ else
+ n = NDTLEAF_LEGACY(ldtentry->
+ namlen);
+ break;
+
+ case BT_INTERNAL:
+ idtentry = (struct idtentry *) & sp->slot[si];
+ n = NDTINTERNAL(idtentry->namlen);
+ break;
+
+ default:
+ break;
+ }
+
+ ++nxt; /* advance to next entry to move in sp */
+ }
+
+ left += n;
+ if (left >= half)
+ break;
+ }
+
+ /* <nxt> poins to the 1st entry to move */
+
+ /*
+ * move entries to right page
+ *
+ * dtMoveEntry() initializes rp and reserves entry for insertion
+ *
+ * split page moved out entries are linelocked;
+ * new/right page moved in entries are linelocked;
+ */
+ /* linelock header + stbl of new right page */
+ rlv = & rdtlck->lv[rdtlck->index];
+ rlv->offset = 0;
+ rlv->length = 5;
+ rdtlck->index++;
+
+ dtMoveEntry(sp, nxt, rp, &sdtlck, &rdtlck, DO_INDEX(ip));
+
+ sp->header.nextindex = nxt;
+
+ /*
+ * finalize freelist of new right page
+ */
+ fsi = rp->header.freelist;
+ f = &rp->slot[fsi];
+ for (fsi++; fsi < rp->header.maxslot; f++, fsi++)
+ f->next = fsi;
+ f->next = -1;
+
+ /*
+ * Update directory index table for entries now in right page
+ */
+ if ((rp->header.flag & BT_LEAF) && DO_INDEX(ip)) {
+ s64 lblock;
+
+ mp = 0;
+ stbl = DT_GETSTBL(rp);
+ for (n = 0; n < rp->header.nextindex; n++) {
+ ldtentry = (struct ldtentry *) & rp->slot[stbl[n]];
+ modify_index(tid, ip, le32_to_cpu(ldtentry->index),
+ rbn, n, &mp, &lblock);
+ }
+ if (mp)
+ release_metapage(mp);
+ }
+
+ /*
+ * the skipped index was on the left page,
+ */
+ if (skip <= off) {
+ /* insert the new entry in the split page */
+ dtInsertEntry(sp, skip, split->key, split->data, &sdtlck);
+
+ /* linelock stbl of split page */
+ if (sdtlck->index >= sdtlck->maxcnt)
+ sdtlck = (struct dt_lock *) txLinelock(sdtlck);
+ slv = & sdtlck->lv[sdtlck->index];
+ n = skip >> L2DTSLOTSIZE;
+ slv->offset = sp->header.stblindex + n;
+ slv->length =
+ ((sp->header.nextindex - 1) >> L2DTSLOTSIZE) - n + 1;
+ sdtlck->index++;
+ }
+ /*
+ * the skipped index was on the right page,
+ */
+ else {
+ /* adjust the skip index to reflect the new position */
+ skip -= nxt;
+
+ /* insert the new entry in the right page */
+ dtInsertEntry(rp, skip, split->key, split->data, &rdtlck);
+ }
+
+ out:
+ *rmpp = rmp;
+ *rpxdp = *pxd;
+
+ ip->i_blocks += LBLK2PBLK(sb, lengthPXD(pxd));
+
+ return rc;
+}
+
+
+/*
+ * dtExtendPage()
+ *
+ * function: extend 1st/only directory leaf page
+ *
+ * parameter:
+ *
+ * return: 0 - success;
+ * errno - failure;
+ * return extended page pinned;
+ */
+static int dtExtendPage(tid_t tid,
+ struct inode *ip, struct dtsplit * split, struct btstack * btstack)
+{
+ struct super_block *sb = ip->i_sb;
+ int rc;
+ struct metapage *smp, *pmp, *mp;
+ dtpage_t *sp, *pp;
+ struct pxdlist *pxdlist;
+ pxd_t *pxd, *tpxd;
+ int xlen, xsize;
+ int newstblindex, newstblsize;
+ int oldstblindex, oldstblsize;
+ int fsi, last;
+ struct dtslot *f;
+ struct btframe *parent;
+ int n;
+ struct dt_lock *dtlck;
+ s64 xaddr, txaddr;
+ struct tlock *tlck;
+ struct pxd_lock *pxdlock;
+ struct lv *lv;
+ uint type;
+ struct ldtentry *ldtentry;
+ u8 *stbl;
+
+ /* get page to extend */
+ smp = split->mp;
+ sp = DT_PAGE(ip, smp);
+
+ /* get parent/root page */
+ parent = BT_POP(btstack);
+ DT_GETPAGE(ip, parent->bn, pmp, PSIZE, pp, rc);
+ if (rc)
+ return (rc);
+
+ /*
+ * extend the extent
+ */
+ pxdlist = split->pxdlist;
+ pxd = &pxdlist->pxd[pxdlist->npxd];
+ pxdlist->npxd++;
+
+ xaddr = addressPXD(pxd);
+ tpxd = &sp->header.self;
+ txaddr = addressPXD(tpxd);
+ /* in-place extension */
+ if (xaddr == txaddr) {
+ type = tlckEXTEND;
+ }
+ /* relocation */
+ else {
+ type = tlckNEW;
+
+ /* save moved extent descriptor for later free */
+ tlck = txMaplock(tid, ip, tlckDTREE | tlckRELOCATE);
+ pxdlock = (struct pxd_lock *) & tlck->lock;
+ pxdlock->flag = mlckFREEPXD;
+ pxdlock->pxd = sp->header.self;
+ pxdlock->index = 1;
+
+ /*
+ * Update directory index table to reflect new page address
+ */
+ if (DO_INDEX(ip)) {
+ s64 lblock;
+
+ mp = 0;
+ stbl = DT_GETSTBL(sp);
+ for (n = 0; n < sp->header.nextindex; n++) {
+ ldtentry =
+ (struct ldtentry *) & sp->slot[stbl[n]];
+ modify_index(tid, ip,
+ le32_to_cpu(ldtentry->index),
+ xaddr, n, &mp, &lblock);
+ }
+ if (mp)
+ release_metapage(mp);
+ }
+ }
+
+ /*
+ * extend the page
+ */
+ sp->header.self = *pxd;
+
+ jfs_info("dtExtendPage: ip:0x%p smp:0x%p sp:0x%p", ip, smp, sp);
+
+ BT_MARK_DIRTY(smp, ip);
+ /*
+ * acquire a transaction lock on the extended/leaf page
+ */
+ tlck = txLock(tid, ip, smp, tlckDTREE | type);
+ dtlck = (struct dt_lock *) & tlck->lock;
+ lv = & dtlck->lv[0];
+
+ /* update buffer extent descriptor of extended page */
+ xlen = lengthPXD(pxd);
+ xsize = xlen << JFS_SBI(sb)->l2bsize;
+#ifdef _STILL_TO_PORT
+ bmSetXD(smp, xaddr, xsize);
+#endif /* _STILL_TO_PORT */
+
+ /*
+ * copy old stbl to new stbl at start of extended area
+ */
+ oldstblindex = sp->header.stblindex;
+ oldstblsize = (sp->header.maxslot + 31) >> L2DTSLOTSIZE;
+ newstblindex = sp->header.maxslot;
+ n = xsize >> L2DTSLOTSIZE;
+ newstblsize = (n + 31) >> L2DTSLOTSIZE;
+ memcpy(&sp->slot[newstblindex], &sp->slot[oldstblindex],
+ sp->header.nextindex);
+
+ /*
+ * in-line extension: linelock old area of extended page
+ */
+ if (type == tlckEXTEND) {
+ /* linelock header */
+ lv->offset = 0;
+ lv->length = 1;
+ dtlck->index++;
+ lv++;
+
+ /* linelock new stbl of extended page */
+ lv->offset = newstblindex;
+ lv->length = newstblsize;
+ }
+ /*
+ * relocation: linelock whole relocated area
+ */
+ else {
+ lv->offset = 0;
+ lv->length = sp->header.maxslot + newstblsize;
+ }
+
+ dtlck->index++;
+
+ sp->header.maxslot = n;
+ sp->header.stblindex = newstblindex;
+ /* sp->header.nextindex remains the same */
+
+ /*
+ * add old stbl region at head of freelist
+ */
+ fsi = oldstblindex;
+ f = &sp->slot[fsi];
+ last = sp->header.freelist;
+ for (n = 0; n < oldstblsize; n++, fsi++, f++) {
+ f->next = last;
+ last = fsi;
+ }
+ sp->header.freelist = last;
+ sp->header.freecnt += oldstblsize;
+
+ /*
+ * append free region of newly extended area at tail of freelist
+ */
+ /* init free region of newly extended area */
+ fsi = n = newstblindex + newstblsize;
+ f = &sp->slot[fsi];
+ for (fsi++; fsi < sp->header.maxslot; f++, fsi++)
+ f->next = fsi;
+ f->next = -1;
+
+ /* append new free region at tail of old freelist */
+ fsi = sp->header.freelist;
+ if (fsi == -1)
+ sp->header.freelist = n;
+ else {
+ do {
+ f = &sp->slot[fsi];
+ fsi = f->next;
+ } while (fsi != -1);
+
+ f->next = n;
+ }
+
+ sp->header.freecnt += sp->header.maxslot - n;
+
+ /*
+ * insert the new entry
+ */
+ dtInsertEntry(sp, split->index, split->key, split->data, &dtlck);
+
+ BT_MARK_DIRTY(pmp, ip);
+ /*
+ * linelock any freeslots residing in old extent
+ */
+ if (type == tlckEXTEND) {
+ n = sp->header.maxslot >> 2;
+ if (sp->header.freelist < n)
+ dtLinelockFreelist(sp, n, &dtlck);
+ }
+
+ /*
+ * update parent entry on the parent/root page
+ */
+ /*
+ * acquire a transaction lock on the parent/root page
+ */
+ tlck = txLock(tid, ip, pmp, tlckDTREE | tlckENTRY);
+ dtlck = (struct dt_lock *) & tlck->lock;
+ lv = & dtlck->lv[dtlck->index];
+
+ /* linelock parent entry - 1st slot */
+ lv->offset = 1;
+ lv->length = 1;
+ dtlck->index++;
+
+ /* update the parent pxd for page extension */
+ tpxd = (pxd_t *) & pp->slot[1];
+ *tpxd = *pxd;
+
+ /* Since the directory might have an EA and/or ACL associated with it
+ * we need to make sure we take that into account when setting the
+ * i_nblocks
+ */
+ ip->i_blocks = LBLK2PBLK(ip->i_sb, xlen +
+ ((JFS_IP(ip)->ea.flag & DXD_EXTENT) ?
+ lengthDXD(&JFS_IP(ip)->ea) : 0) +
+ ((JFS_IP(ip)->acl.flag & DXD_EXTENT) ?
+ lengthDXD(&JFS_IP(ip)->acl) : 0));
+
+ DT_PUTPAGE(pmp);
+ return 0;
+}
+
+
+/*
+ * dtSplitRoot()
+ *
+ * function:
+ * split the full root page into
+ * original/root/split page and new right page
+ * i.e., root remains fixed in tree anchor (inode) and
+ * the root is copied to a single new right child page
+ * since root page << non-root page, and
+ * the split root page contains a single entry for the
+ * new right child page.
+ *
+ * parameter:
+ *
+ * return: 0 - success;
+ * errno - failure;
+ * return new page pinned;
+ */
+static int dtSplitRoot(tid_t tid,
+ struct inode *ip, struct dtsplit * split, struct metapage ** rmpp)
+{
+ struct super_block *sb = ip->i_sb;
+ struct metapage *smp;
+ dtroot_t *sp;
+ struct metapage *rmp;
+ dtpage_t *rp;
+ s64 rbn;
+ int xlen;
+ int xsize;
+ struct dtslot *f;
+ s8 *stbl;
+ int fsi, stblsize, n;
+ struct idtentry *s;
+ pxd_t *ppxd;
+ struct pxdlist *pxdlist;
+ pxd_t *pxd;
+ struct dt_lock *dtlck;
+ struct tlock *tlck;
+ struct lv *lv;
+
+ /* get split root page */
+ smp = split->mp;
+ sp = &JFS_IP(ip)->i_dtroot;
+
+ /*
+ * allocate/initialize a single (right) child page
+ *
+ * N.B. at first split, a one (or two) block to fit new entry
+ * is allocated; at subsequent split, a full page is allocated;
+ */
+ pxdlist = split->pxdlist;
+ pxd = &pxdlist->pxd[pxdlist->npxd];
+ pxdlist->npxd++;
+ rbn = addressPXD(pxd);
+ xlen = lengthPXD(pxd);
+ xsize = xlen << JFS_SBI(sb)->l2bsize;
+ rmp = get_metapage(ip, rbn, xsize, 1);
+ if (!rmp)
+ return -EIO;
+
+ rp = rmp->data;
+
+ BT_MARK_DIRTY(rmp, ip);
+ /*
+ * acquire a transaction lock on the new right page
+ */
+ tlck = txLock(tid, ip, rmp, tlckDTREE | tlckNEW);
+ dtlck = (struct dt_lock *) & tlck->lock;
+
+ rp->header.flag =
+ (sp->header.flag & BT_LEAF) ? BT_LEAF : BT_INTERNAL;
+ rp->header.self = *pxd;
+
+ /* initialize sibling pointers */
+ rp->header.next = 0;
+ rp->header.prev = 0;
+
+ /*
+ * move in-line root page into new right page extent
+ */
+ /* linelock header + copied entries + new stbl (1st slot) in new page */
+ ASSERT(dtlck->index == 0);
+ lv = & dtlck->lv[0];
+ lv->offset = 0;
+ lv->length = 10; /* 1 + 8 + 1 */
+ dtlck->index++;
+
+ n = xsize >> L2DTSLOTSIZE;
+ rp->header.maxslot = n;
+ stblsize = (n + 31) >> L2DTSLOTSIZE;
+
+ /* copy old stbl to new stbl at start of extended area */
+ rp->header.stblindex = DTROOTMAXSLOT;
+ stbl = (s8 *) & rp->slot[DTROOTMAXSLOT];
+ memcpy(stbl, sp->header.stbl, sp->header.nextindex);
+ rp->header.nextindex = sp->header.nextindex;
+
+ /* copy old data area to start of new data area */
+ memcpy(&rp->slot[1], &sp->slot[1], IDATASIZE);
+
+ /*
+ * append free region of newly extended area at tail of freelist
+ */
+ /* init free region of newly extended area */
+ fsi = n = DTROOTMAXSLOT + stblsize;
+ f = &rp->slot[fsi];
+ for (fsi++; fsi < rp->header.maxslot; f++, fsi++)
+ f->next = fsi;
+ f->next = -1;
+
+ /* append new free region at tail of old freelist */
+ fsi = sp->header.freelist;
+ if (fsi == -1)
+ rp->header.freelist = n;
+ else {
+ rp->header.freelist = fsi;
+
+ do {
+ f = &rp->slot[fsi];
+ fsi = f->next;
+ } while (fsi != -1);
+
+ f->next = n;
+ }
+
+ rp->header.freecnt = sp->header.freecnt + rp->header.maxslot - n;
+
+ /*
+ * Update directory index table for entries now in right page
+ */
+ if ((rp->header.flag & BT_LEAF) && DO_INDEX(ip)) {
+ s64 lblock;
+ struct metapage *mp = 0;
+ struct ldtentry *ldtentry;
+
+ stbl = DT_GETSTBL(rp);
+ for (n = 0; n < rp->header.nextindex; n++) {
+ ldtentry = (struct ldtentry *) & rp->slot[stbl[n]];
+ modify_index(tid, ip, le32_to_cpu(ldtentry->index),
+ rbn, n, &mp, &lblock);
+ }
+ if (mp)
+ release_metapage(mp);
+ }
+ /*
+ * insert the new entry into the new right/child page
+ * (skip index in the new right page will not change)
+ */
+ dtInsertEntry(rp, split->index, split->key, split->data, &dtlck);
+
+ /*
+ * reset parent/root page
+ *
+ * set the 1st entry offset to 0, which force the left-most key
+ * at any level of the tree to be less than any search key.
+ *
+ * The btree comparison code guarantees that the left-most key on any
+ * level of the tree is never used, so it doesn't need to be filled in.
+ */
+ BT_MARK_DIRTY(smp, ip);
+ /*
+ * acquire a transaction lock on the root page (in-memory inode)
+ */
+ tlck = txLock(tid, ip, smp, tlckDTREE | tlckNEW | tlckBTROOT);
+ dtlck = (struct dt_lock *) & tlck->lock;
+
+ /* linelock root */
+ ASSERT(dtlck->index == 0);
+ lv = & dtlck->lv[0];
+ lv->offset = 0;
+ lv->length = DTROOTMAXSLOT;
+ dtlck->index++;
+
+ /* update page header of root */
+ if (sp->header.flag & BT_LEAF) {
+ sp->header.flag &= ~BT_LEAF;
+ sp->header.flag |= BT_INTERNAL;
+ }
+
+ /* init the first entry */
+ s = (struct idtentry *) & sp->slot[DTENTRYSTART];
+ ppxd = (pxd_t *) s;
+ *ppxd = *pxd;
+ s->next = -1;
+ s->namlen = 0;
+
+ stbl = sp->header.stbl;
+ stbl[0] = DTENTRYSTART;
+ sp->header.nextindex = 1;
+
+ /* init freelist */
+ fsi = DTENTRYSTART + 1;
+ f = &sp->slot[fsi];
+
+ /* init free region of remaining area */
+ for (fsi++; fsi < DTROOTMAXSLOT; f++, fsi++)
+ f->next = fsi;
+ f->next = -1;
+
+ sp->header.freelist = DTENTRYSTART + 1;
+ sp->header.freecnt = DTROOTMAXSLOT - (DTENTRYSTART + 1);
+
+ *rmpp = rmp;
+
+ ip->i_blocks += LBLK2PBLK(ip->i_sb, lengthPXD(pxd));
+ return 0;
+}
+
+
+/*
+ * dtDelete()
+ *
+ * function: delete the entry(s) referenced by a key.
+ *
+ * parameter:
+ *
+ * return:
+ */
+int dtDelete(tid_t tid,
+ struct inode *ip, struct component_name * key, ino_t * ino, int flag)
+{
+ int rc = 0;
+ s64 bn;
+ struct metapage *mp, *imp;
+ dtpage_t *p;
+ int index;
+ struct btstack btstack;
+ struct dt_lock *dtlck;
+ struct tlock *tlck;
+ struct lv *lv;
+ int i;
+ struct ldtentry *ldtentry;
+ u8 *stbl;
+ u32 table_index, next_index;
+ struct metapage *nmp;
+ dtpage_t *np;
+
+ /*
+ * search for the entry to delete:
+ *
+ * dtSearch() returns (leaf page pinned, index at which to delete).
+ */
+ if ((rc = dtSearch(ip, key, ino, &btstack, flag)))
+ return rc;
+
+ /* retrieve search result */
+ DT_GETSEARCH(ip, btstack.top, bn, mp, p, index);
+
+ /*
+ * We need to find put the index of the next entry into the
+ * directory index table in order to resume a readdir from this
+ * entry.
+ */
+ if (DO_INDEX(ip)) {
+ stbl = DT_GETSTBL(p);
+ ldtentry = (struct ldtentry *) & p->slot[stbl[index]];
+ table_index = le32_to_cpu(ldtentry->index);
+ if (index == (p->header.nextindex - 1)) {
+ /*
+ * Last entry in this leaf page
+ */
+ if ((p->header.flag & BT_ROOT)
+ || (p->header.next == 0))
+ next_index = -1;
+ else {
+ /* Read next leaf page */
+ DT_GETPAGE(ip, le64_to_cpu(p->header.next),
+ nmp, PSIZE, np, rc);
+ if (rc)
+ next_index = -1;
+ else {
+ stbl = DT_GETSTBL(np);
+ ldtentry =
+ (struct ldtentry *) & np->
+ slot[stbl[0]];
+ next_index =
+ le32_to_cpu(ldtentry->index);
+ DT_PUTPAGE(nmp);
+ }
+ }
+ } else {
+ ldtentry =
+ (struct ldtentry *) & p->slot[stbl[index + 1]];
+ next_index = le32_to_cpu(ldtentry->index);
+ }
+ free_index(tid, ip, table_index, next_index);
+ }
+ /*
+ * the leaf page becomes empty, delete the page
+ */
+ if (p->header.nextindex == 1) {
+ /* delete empty page */
+ rc = dtDeleteUp(tid, ip, mp, p, &btstack);
+ }
+ /*
+ * the leaf page has other entries remaining:
+ *
+ * delete the entry from the leaf page.
+ */
+ else {
+ BT_MARK_DIRTY(mp, ip);
+ /*
+ * acquire a transaction lock on the leaf page
+ */
+ tlck = txLock(tid, ip, mp, tlckDTREE | tlckENTRY);
+ dtlck = (struct dt_lock *) & tlck->lock;
+
+ /*
+ * Do not assume that dtlck->index will be zero. During a
+ * rename within a directory, this transaction may have
+ * modified this page already when adding the new entry.
+ */
+
+ /* linelock header */
+ if (dtlck->index >= dtlck->maxcnt)
+ dtlck = (struct dt_lock *) txLinelock(dtlck);
+ lv = & dtlck->lv[dtlck->index];
+ lv->offset = 0;
+ lv->length = 1;
+ dtlck->index++;
+
+ /* linelock stbl of non-root leaf page */
+ if (!(p->header.flag & BT_ROOT)) {
+ if (dtlck->index >= dtlck->maxcnt)
+ dtlck = (struct dt_lock *) txLinelock(dtlck);
+ lv = & dtlck->lv[dtlck->index];
+ i = index >> L2DTSLOTSIZE;
+ lv->offset = p->header.stblindex + i;
+ lv->length =
+ ((p->header.nextindex - 1) >> L2DTSLOTSIZE) -
+ i + 1;
+ dtlck->index++;
+ }
+
+ /* free the leaf entry */
+ dtDeleteEntry(p, index, &dtlck);
+
+ /*
+ * Update directory index table for entries moved in stbl
+ */
+ if (DO_INDEX(ip) && index < p->header.nextindex) {
+ s64 lblock;
+
+ imp = 0;
+ stbl = DT_GETSTBL(p);
+ for (i = index; i < p->header.nextindex; i++) {
+ ldtentry =
+ (struct ldtentry *) & p->slot[stbl[i]];
+ modify_index(tid, ip,
+ le32_to_cpu(ldtentry->index),
+ bn, i, &imp, &lblock);
+ }
+ if (imp)
+ release_metapage(imp);
+ }
+
+ DT_PUTPAGE(mp);
+ }
+
+ return rc;
+}
+
+
+/*
+ * dtDeleteUp()
+ *
+ * function:
+ * free empty pages as propagating deletion up the tree
+ *
+ * parameter:
+ *
+ * return:
+ */
+static int dtDeleteUp(tid_t tid, struct inode *ip,
+ struct metapage * fmp, dtpage_t * fp, struct btstack * btstack)
+{
+ int rc = 0;
+ struct metapage *mp;
+ dtpage_t *p;
+ int index, nextindex;
+ int xlen;
+ struct btframe *parent;
+ struct dt_lock *dtlck;
+ struct tlock *tlck;
+ struct lv *lv;
+ struct pxd_lock *pxdlock;
+ int i;
+
+ /*
+ * keep the root leaf page which has become empty
+ */
+ if (BT_IS_ROOT(fmp)) {
+ /*
+ * reset the root
+ *
+ * dtInitRoot() acquires txlock on the root
+ */
+ dtInitRoot(tid, ip, PARENT(ip));
+
+ DT_PUTPAGE(fmp);
+
+ return 0;
+ }
+
+ /*
+ * free the non-root leaf page
+ */
+ /*
+ * acquire a transaction lock on the page
+ *
+ * write FREEXTENT|NOREDOPAGE log record
+ * N.B. linelock is overlaid as freed extent descriptor, and
+ * the buffer page is freed;
+ */
+ tlck = txMaplock(tid, ip, tlckDTREE | tlckFREE);
+ pxdlock = (struct pxd_lock *) & tlck->lock;
+ pxdlock->flag = mlckFREEPXD;
+ pxdlock->pxd = fp->header.self;
+ pxdlock->index = 1;
+
+ /* update sibling pointers */
+ if ((rc = dtRelink(tid, ip, fp))) {
+ BT_PUTPAGE(fmp);
+ return rc;
+ }
+
+ xlen = lengthPXD(&fp->header.self);
+ ip->i_blocks -= LBLK2PBLK(ip->i_sb, xlen);
+
+ /* free/invalidate its buffer page */
+ discard_metapage(fmp);
+
+ /*
+ * propagate page deletion up the directory tree
+ *
+ * If the delete from the parent page makes it empty,
+ * continue all the way up the tree.
+ * stop if the root page is reached (which is never deleted) or
+ * if the entry deletion does not empty the page.
+ */
+ while ((parent = BT_POP(btstack)) != NULL) {
+ /* pin the parent page <sp> */
+ DT_GETPAGE(ip, parent->bn, mp, PSIZE, p, rc);
+ if (rc)
+ return rc;
+
+ /*
+ * free the extent of the child page deleted
+ */
+ index = parent->index;
+
+ /*
+ * delete the entry for the child page from parent
+ */
+ nextindex = p->header.nextindex;
+
+ /*
+ * the parent has the single entry being deleted:
+ *
+ * free the parent page which has become empty.
+ */
+ if (nextindex == 1) {
+ /*
+ * keep the root internal page which has become empty
+ */
+ if (p->header.flag & BT_ROOT) {
+ /*
+ * reset the root
+ *
+ * dtInitRoot() acquires txlock on the root
+ */
+ dtInitRoot(tid, ip, PARENT(ip));
+
+ DT_PUTPAGE(mp);
+
+ return 0;
+ }
+ /*
+ * free the parent page
+ */
+ else {
+ /*
+ * acquire a transaction lock on the page
+ *
+ * write FREEXTENT|NOREDOPAGE log record
+ */
+ tlck =
+ txMaplock(tid, ip,
+ tlckDTREE | tlckFREE);
+ pxdlock = (struct pxd_lock *) & tlck->lock;
+ pxdlock->flag = mlckFREEPXD;
+ pxdlock->pxd = p->header.self;
+ pxdlock->index = 1;
+
+ /* update sibling pointers */
+ if ((rc = dtRelink(tid, ip, p))) {
+ DT_PUTPAGE(mp);
+ return rc;
+ }
+
+ xlen = lengthPXD(&p->header.self);
+ ip->i_blocks -= LBLK2PBLK(ip->i_sb, xlen);
+
+ /* free/invalidate its buffer page */
+ discard_metapage(mp);
+
+ /* propagate up */
+ continue;
+ }
+ }
+
+ /*
+ * the parent has other entries remaining:
+ *
+ * delete the router entry from the parent page.
+ */
+ BT_MARK_DIRTY(mp, ip);
+ /*
+ * acquire a transaction lock on the page
+ *
+ * action: router entry deletion
+ */
+ tlck = txLock(tid, ip, mp, tlckDTREE | tlckENTRY);
+ dtlck = (struct dt_lock *) & tlck->lock;
+
+ /* linelock header */
+ if (dtlck->index >= dtlck->maxcnt)
+ dtlck = (struct dt_lock *) txLinelock(dtlck);
+ lv = & dtlck->lv[dtlck->index];
+ lv->offset = 0;
+ lv->length = 1;
+ dtlck->index++;
+
+ /* linelock stbl of non-root leaf page */
+ if (!(p->header.flag & BT_ROOT)) {
+ if (dtlck->index < dtlck->maxcnt)
+ lv++;
+ else {
+ dtlck = (struct dt_lock *) txLinelock(dtlck);
+ lv = & dtlck->lv[0];
+ }
+ i = index >> L2DTSLOTSIZE;
+ lv->offset = p->header.stblindex + i;
+ lv->length =
+ ((p->header.nextindex - 1) >> L2DTSLOTSIZE) -
+ i + 1;
+ dtlck->index++;
+ }
+
+ /* free the router entry */
+ dtDeleteEntry(p, index, &dtlck);
+
+ /* reset key of new leftmost entry of level (for consistency) */
+ if (index == 0 &&
+ ((p->header.flag & BT_ROOT) || p->header.prev == 0))
+ dtTruncateEntry(p, 0, &dtlck);
+
+ /* unpin the parent page */
+ DT_PUTPAGE(mp);
+
+ /* exit propagation up */
+ break;
+ }
+
+ return 0;
+}
+
+#ifdef _NOTYET
+/*
+ * NAME: dtRelocate()
+ *
+ * FUNCTION: relocate dtpage (internal or leaf) of directory;
+ * This function is mainly used by defragfs utility.
+ */
+int dtRelocate(tid_t tid, struct inode *ip, s64 lmxaddr, pxd_t * opxd,
+ s64 nxaddr)
+{
+ int rc = 0;
+ struct metapage *mp, *pmp, *lmp, *rmp;
+ dtpage_t *p, *pp, *rp = 0, *lp= 0;
+ s64 bn;
+ int index;
+ struct btstack btstack;
+ pxd_t *pxd;
+ s64 oxaddr, nextbn, prevbn;
+ int xlen, xsize;
+ struct tlock *tlck;
+ struct dt_lock *dtlck;
+ struct pxd_lock *pxdlock;
+ s8 *stbl;
+ struct lv *lv;
+
+ oxaddr = addressPXD(opxd);
+ xlen = lengthPXD(opxd);
+
+ jfs_info("dtRelocate: lmxaddr:%Ld xaddr:%Ld:%Ld xlen:%d",
+ (long long)lmxaddr, (long long)oxaddr, (long long)nxaddr,
+ xlen);
+
+ /*
+ * 1. get the internal parent dtpage covering
+ * router entry for the tartget page to be relocated;
+ */
+ rc = dtSearchNode(ip, lmxaddr, opxd, &btstack);
+ if (rc)
+ return rc;
+
+ /* retrieve search result */
+ DT_GETSEARCH(ip, btstack.top, bn, pmp, pp, index);
+ jfs_info("dtRelocate: parent router entry validated.");
+
+ /*
+ * 2. relocate the target dtpage
+ */
+ /* read in the target page from src extent */
+ DT_GETPAGE(ip, oxaddr, mp, PSIZE, p, rc);
+ if (rc) {
+ /* release the pinned parent page */
+ DT_PUTPAGE(pmp);
+ return rc;
+ }
+
+ /*
+ * read in sibling pages if any to update sibling pointers;
+ */
+ rmp = NULL;
+ if (p->header.next) {
+ nextbn = le64_to_cpu(p->header.next);
+ DT_GETPAGE(ip, nextbn, rmp, PSIZE, rp, rc);
+ if (rc) {
+ DT_PUTPAGE(mp);
+ DT_PUTPAGE(pmp);
+ return (rc);
+ }
+ }
+
+ lmp = NULL;
+ if (p->header.prev) {
+ prevbn = le64_to_cpu(p->header.prev);
+ DT_GETPAGE(ip, prevbn, lmp, PSIZE, lp, rc);
+ if (rc) {
+ DT_PUTPAGE(mp);
+ DT_PUTPAGE(pmp);
+ if (rmp)
+ DT_PUTPAGE(rmp);
+ return (rc);
+ }
+ }
+
+ /* at this point, all xtpages to be updated are in memory */
+
+ /*
+ * update sibling pointers of sibling dtpages if any;
+ */
+ if (lmp) {
+ tlck = txLock(tid, ip, lmp, tlckDTREE | tlckRELINK);
+ dtlck = (struct dt_lock *) & tlck->lock;
+ /* linelock header */
+ ASSERT(dtlck->index == 0);
+ lv = & dtlck->lv[0];
+ lv->offset = 0;
+ lv->length = 1;
+ dtlck->index++;
+
+ lp->header.next = cpu_to_le64(nxaddr);
+ DT_PUTPAGE(lmp);
+ }
+
+ if (rmp) {
+ tlck = txLock(tid, ip, rmp, tlckDTREE | tlckRELINK);
+ dtlck = (struct dt_lock *) & tlck->lock;
+ /* linelock header */
+ ASSERT(dtlck->index == 0);
+ lv = & dtlck->lv[0];
+ lv->offset = 0;
+ lv->length = 1;
+ dtlck->index++;
+
+ rp->header.prev = cpu_to_le64(nxaddr);
+ DT_PUTPAGE(rmp);
+ }
+
+ /*
+ * update the target dtpage to be relocated
+ *
+ * write LOG_REDOPAGE of LOG_NEW type for dst page
+ * for the whole target page (logredo() will apply
+ * after image and update bmap for allocation of the
+ * dst extent), and update bmap for allocation of
+ * the dst extent;
+ */
+ tlck = txLock(tid, ip, mp, tlckDTREE | tlckNEW);
+ dtlck = (struct dt_lock *) & tlck->lock;
+ /* linelock header */
+ ASSERT(dtlck->index == 0);
+ lv = & dtlck->lv[0];
+
+ /* update the self address in the dtpage header */
+ pxd = &p->header.self;
+ PXDaddress(pxd, nxaddr);
+
+ /* the dst page is the same as the src page, i.e.,
+ * linelock for afterimage of the whole page;
+ */
+ lv->offset = 0;
+ lv->length = p->header.maxslot;
+ dtlck->index++;
+
+ /* update the buffer extent descriptor of the dtpage */
+ xsize = xlen << JFS_SBI(ip->i_sb)->l2bsize;
+#ifdef _STILL_TO_PORT
+ bmSetXD(mp, nxaddr, xsize);
+#endif /* _STILL_TO_PORT */
+ /* unpin the relocated page */
+ DT_PUTPAGE(mp);
+ jfs_info("dtRelocate: target dtpage relocated.");
+
+ /* the moved extent is dtpage, then a LOG_NOREDOPAGE log rec
+ * needs to be written (in logredo(), the LOG_NOREDOPAGE log rec
+ * will also force a bmap update ).
+ */
+
+ /*
+ * 3. acquire maplock for the source extent to be freed;
+ */
+ /* for dtpage relocation, write a LOG_NOREDOPAGE record
+ * for the source dtpage (logredo() will init NoRedoPage
+ * filter and will also update bmap for free of the source
+ * dtpage), and upadte bmap for free of the source dtpage;
+ */
+ tlck = txMaplock(tid, ip, tlckDTREE | tlckFREE);
+ pxdlock = (struct pxd_lock *) & tlck->lock;
+ pxdlock->flag = mlckFREEPXD;
+ PXDaddress(&pxdlock->pxd, oxaddr);
+ PXDlength(&pxdlock->pxd, xlen);
+ pxdlock->index = 1;
+
+ /*
+ * 4. update the parent router entry for relocation;
+ *
+ * acquire tlck for the parent entry covering the target dtpage;
+ * write LOG_REDOPAGE to apply after image only;
+ */
+ jfs_info("dtRelocate: update parent router entry.");
+ tlck = txLock(tid, ip, pmp, tlckDTREE | tlckENTRY);
+ dtlck = (struct dt_lock *) & tlck->lock;
+ lv = & dtlck->lv[dtlck->index];
+
+ /* update the PXD with the new address */
+ stbl = DT_GETSTBL(pp);
+ pxd = (pxd_t *) & pp->slot[stbl[index]];
+ PXDaddress(pxd, nxaddr);
+ lv->offset = stbl[index];
+ lv->length = 1;
+ dtlck->index++;
+
+ /* unpin the parent dtpage */
+ DT_PUTPAGE(pmp);
+
+ return rc;
+}
+
+/*
+ * NAME: dtSearchNode()
+ *
+ * FUNCTION: Search for an dtpage containing a specified address
+ * This function is mainly used by defragfs utility.
+ *
+ * NOTE: Search result on stack, the found page is pinned at exit.
+ * The result page must be an internal dtpage.
+ * lmxaddr give the address of the left most page of the
+ * dtree level, in which the required dtpage resides.
+ */
+static int dtSearchNode(struct inode *ip, s64 lmxaddr, pxd_t * kpxd,
+ struct btstack * btstack)
+{
+ int rc = 0;
+ s64 bn;
+ struct metapage *mp;
+ dtpage_t *p;
+ int psize = 288; /* initial in-line directory */
+ s8 *stbl;
+ int i;
+ pxd_t *pxd;
+ struct btframe *btsp;
+
+ BT_CLR(btstack); /* reset stack */
+
+ /*
+ * descend tree to the level with specified leftmost page
+ *
+ * by convention, root bn = 0.
+ */
+ for (bn = 0;;) {
+ /* get/pin the page to search */
+ DT_GETPAGE(ip, bn, mp, psize, p, rc);
+ if (rc)
+ return rc;
+
+ /* does the xaddr of leftmost page of the levevl
+ * matches levevl search key ?
+ */
+ if (p->header.flag & BT_ROOT) {
+ if (lmxaddr == 0)
+ break;
+ } else if (addressPXD(&p->header.self) == lmxaddr)
+ break;
+
+ /*
+ * descend down to leftmost child page
+ */
+ if (p->header.flag & BT_LEAF) {
+ DT_PUTPAGE(mp);
+ return -ESTALE;
+ }
+
+ /* get the leftmost entry */
+ stbl = DT_GETSTBL(p);
+ pxd = (pxd_t *) & p->slot[stbl[0]];
+
+ /* get the child page block address */
+ bn = addressPXD(pxd);
+ psize = lengthPXD(pxd) << JFS_SBI(ip->i_sb)->l2bsize;
+ /* unpin the parent page */
+ DT_PUTPAGE(mp);
+ }
+
+ /*
+ * search each page at the current levevl
+ */
+ loop:
+ stbl = DT_GETSTBL(p);
+ for (i = 0; i < p->header.nextindex; i++) {
+ pxd = (pxd_t *) & p->slot[stbl[i]];
+
+ /* found the specified router entry */
+ if (addressPXD(pxd) == addressPXD(kpxd) &&
+ lengthPXD(pxd) == lengthPXD(kpxd)) {
+ btsp = btstack->top;
+ btsp->bn = bn;
+ btsp->index = i;
+ btsp->mp = mp;
+
+ return 0;
+ }
+ }
+
+ /* get the right sibling page if any */
+ if (p->header.next)
+ bn = le64_to_cpu(p->header.next);
+ else {
+ DT_PUTPAGE(mp);
+ return -ESTALE;
+ }
+
+ /* unpin current page */
+ DT_PUTPAGE(mp);
+
+ /* get the right sibling page */
+ DT_GETPAGE(ip, bn, mp, PSIZE, p, rc);
+ if (rc)
+ return rc;
+
+ goto loop;
+}
+#endif /* _NOTYET */
+
+/*
+ * dtRelink()
+ *
+ * function:
+ * link around a freed page.
+ *
+ * parameter:
+ * fp: page to be freed
+ *
+ * return:
+ */
+static int dtRelink(tid_t tid, struct inode *ip, dtpage_t * p)
+{
+ int rc;
+ struct metapage *mp;
+ s64 nextbn, prevbn;
+ struct tlock *tlck;
+ struct dt_lock *dtlck;
+ struct lv *lv;
+
+ nextbn = le64_to_cpu(p->header.next);
+ prevbn = le64_to_cpu(p->header.prev);
+
+ /* update prev pointer of the next page */
+ if (nextbn != 0) {
+ DT_GETPAGE(ip, nextbn, mp, PSIZE, p, rc);
+ if (rc)
+ return rc;
+
+ BT_MARK_DIRTY(mp, ip);
+ /*
+ * acquire a transaction lock on the next page
+ *
+ * action: update prev pointer;
+ */
+ tlck = txLock(tid, ip, mp, tlckDTREE | tlckRELINK);
+ jfs_info("dtRelink nextbn: tlck = 0x%p, ip = 0x%p, mp=0x%p",
+ tlck, ip, mp);
+ dtlck = (struct dt_lock *) & tlck->lock;
+
+ /* linelock header */
+ if (dtlck->index >= dtlck->maxcnt)
+ dtlck = (struct dt_lock *) txLinelock(dtlck);
+ lv = & dtlck->lv[dtlck->index];
+ lv->offset = 0;
+ lv->length = 1;
+ dtlck->index++;
+
+ p->header.prev = cpu_to_le64(prevbn);
+ DT_PUTPAGE(mp);
+ }
+
+ /* update next pointer of the previous page */
+ if (prevbn != 0) {
+ DT_GETPAGE(ip, prevbn, mp, PSIZE, p, rc);
+ if (rc)
+ return rc;
+
+ BT_MARK_DIRTY(mp, ip);
+ /*
+ * acquire a transaction lock on the prev page
+ *
+ * action: update next pointer;
+ */
+ tlck = txLock(tid, ip, mp, tlckDTREE | tlckRELINK);
+ jfs_info("dtRelink prevbn: tlck = 0x%p, ip = 0x%p, mp=0x%p",
+ tlck, ip, mp);
+ dtlck = (struct dt_lock *) & tlck->lock;
+
+ /* linelock header */
+ if (dtlck->index >= dtlck->maxcnt)
+ dtlck = (struct dt_lock *) txLinelock(dtlck);
+ lv = & dtlck->lv[dtlck->index];
+ lv->offset = 0;
+ lv->length = 1;
+ dtlck->index++;
+
+ p->header.next = cpu_to_le64(nextbn);
+ DT_PUTPAGE(mp);
+ }
+
+ return 0;
+}
+
+
+/*
+ * dtInitRoot()
+ *
+ * initialize directory root (inline in inode)
+ */
+void dtInitRoot(tid_t tid, struct inode *ip, u32 idotdot)
+{
+ struct jfs_inode_info *jfs_ip = JFS_IP(ip);
+ dtroot_t *p;
+ int fsi;
+ struct dtslot *f;
+ struct tlock *tlck;
+ struct dt_lock *dtlck;
+ struct lv *lv;
+ u16 xflag_save;
+
+ /*
+ * If this was previously an non-empty directory, we need to remove
+ * the old directory table.
+ */
+ if (DO_INDEX(ip)) {
+ if (jfs_ip->next_index > (MAX_INLINE_DIRTABLE_ENTRY + 1)) {
+ struct tblock *tblk = tid_to_tblock(tid);
+ /*
+ * We're playing games with the tid's xflag. If
+ * we're removing a regular file, the file's xtree
+ * is committed with COMMIT_PMAP, but we always
+ * commit the directories xtree with COMMIT_PWMAP.
+ */
+ xflag_save = tblk->xflag;
+ tblk->xflag = 0;
+ /*
+ * xtTruncate isn't guaranteed to fully truncate
+ * the xtree. The caller needs to check i_size
+ * after committing the transaction to see if
+ * additional truncation is needed. The
+ * COMMIT_Stale flag tells caller that we
+ * initiated the truncation.
+ */
+ xtTruncate(tid, ip, 0, COMMIT_PWMAP);
+ set_cflag(COMMIT_Stale, ip);
+
+ tblk->xflag = xflag_save;
+ } else
+ ip->i_size = 1;
+
+ jfs_ip->next_index = 2;
+ } else
+ ip->i_size = IDATASIZE;
+
+ /*
+ * acquire a transaction lock on the root
+ *
+ * action: directory initialization;
+ */
+ tlck = txLock(tid, ip, (struct metapage *) & jfs_ip->bxflag,
+ tlckDTREE | tlckENTRY | tlckBTROOT);
+ dtlck = (struct dt_lock *) & tlck->lock;
+
+ /* linelock root */
+ ASSERT(dtlck->index == 0);
+ lv = & dtlck->lv[0];
+ lv->offset = 0;
+ lv->length = DTROOTMAXSLOT;
+ dtlck->index++;
+
+ p = &jfs_ip->i_dtroot;
+
+ p->header.flag = DXD_INDEX | BT_ROOT | BT_LEAF;
+
+ p->header.nextindex = 0;
+
+ /* init freelist */
+ fsi = 1;
+ f = &p->slot[fsi];
+
+ /* init data area of root */
+ for (fsi++; fsi < DTROOTMAXSLOT; f++, fsi++)
+ f->next = fsi;
+ f->next = -1;
+
+ p->header.freelist = 1;
+ p->header.freecnt = 8;
+
+ /* init '..' entry */
+ p->header.idotdot = cpu_to_le32(idotdot);
+
+#if 0
+ ip->i_blocks = LBLK2PBLK(ip->i_sb,
+ ((jfs_ip->ea.flag & DXD_EXTENT) ?
+ lengthDXD(&jfs_ip->ea) : 0) +
+ ((jfs_ip->acl.flag & DXD_EXTENT) ?
+ lengthDXD(&jfs_ip->acl) : 0));
+#endif
+
+ return;
+}
+
+/*
+ * add_missing_indices()
+ *
+ * function: Fix dtree page in which one or more entries has an invalid index.
+ * fsck.jfs should really fix this, but it currently does not.
+ * Called from jfs_readdir when bad index is detected.
+ */
+static void add_missing_indices(struct inode *inode, s64 bn)
+{
+ struct ldtentry *d;
+ struct dt_lock *dtlck;
+ int i;
+ uint index;
+ struct lv *lv;
+ struct metapage *mp;
+ dtpage_t *p;
+ int rc;
+ s8 *stbl;
+ tid_t tid;
+ struct tlock *tlck;
+
+ tid = txBegin(inode->i_sb, 0);
+
+ DT_GETPAGE(inode, bn, mp, PSIZE, p, rc);
+
+ if (rc) {
+ printk(KERN_ERR "DT_GETPAGE failed!\n");
+ goto end;
+ }
+ BT_MARK_DIRTY(mp, inode);
+
+ ASSERT(p->header.flag & BT_LEAF);
+
+ tlck = txLock(tid, inode, mp, tlckDTREE | tlckENTRY);
+ dtlck = (struct dt_lock *) &tlck->lock;
+
+ stbl = DT_GETSTBL(p);
+ for (i = 0; i < p->header.nextindex; i++) {
+ d = (struct ldtentry *) &p->slot[stbl[i]];
+ index = le32_to_cpu(d->index);
+ if ((index < 2) || (index >= JFS_IP(inode)->next_index)) {
+ d->index = cpu_to_le32(add_index(tid, inode, bn, i));
+ if (dtlck->index >= dtlck->maxcnt)
+ dtlck = (struct dt_lock *) txLinelock(dtlck);
+ lv = &dtlck->lv[dtlck->index];
+ lv->offset = stbl[i];
+ lv->length = 1;
+ dtlck->index++;
+ }
+ }
+
+ DT_PUTPAGE(mp);
+ (void) txCommit(tid, 1, &inode, 0);
+end:
+ txEnd(tid);
+}
+
+/*
+ * Buffer to hold directory entry info while traversing a dtree page
+ * before being fed to the filldir function
+ */
+struct jfs_dirent {
+ loff_t position;
+ int ino;
+ u16 name_len;
+ char name[0];
+};
+
+/*
+ * function to determine next variable-sized jfs_dirent in buffer
+ */
+static inline struct jfs_dirent *next_jfs_dirent(struct jfs_dirent *dirent)
+{
+ return (struct jfs_dirent *)
+ ((char *)dirent +
+ ((sizeof (struct jfs_dirent) + dirent->name_len + 1 +
+ sizeof (loff_t) - 1) &
+ ~(sizeof (loff_t) - 1)));
+}
+
+/*
+ * jfs_readdir()
+ *
+ * function: read directory entries sequentially
+ * from the specified entry offset
+ *
+ * parameter:
+ *
+ * return: offset = (pn, index) of start entry
+ * of next jfs_readdir()/dtRead()
+ */
+int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+ struct inode *ip = filp->f_dentry->d_inode;
+ struct nls_table *codepage = JFS_SBI(ip->i_sb)->nls_tab;
+ int rc = 0;
+ loff_t dtpos; /* legacy OS/2 style position */
+ struct dtoffset {
+ s16 pn;
+ s16 index;
+ s32 unused;
+ } *dtoffset = (struct dtoffset *) &dtpos;
+ s64 bn;
+ struct metapage *mp;
+ dtpage_t *p;
+ int index;
+ s8 *stbl;
+ struct btstack btstack;
+ int i, next;
+ struct ldtentry *d;
+ struct dtslot *t;
+ int d_namleft, len, outlen;
+ unsigned long dirent_buf;
+ char *name_ptr;
+ u32 dir_index;
+ int do_index = 0;
+ uint loop_count = 0;
+ struct jfs_dirent *jfs_dirent;
+ int jfs_dirents;
+ int overflow, fix_page, page_fixed = 0;
+ static int unique_pos = 2; /* If we can't fix broken index */
+
+ if (filp->f_pos == DIREND)
+ return 0;
+
+ if (DO_INDEX(ip)) {
+ /*
+ * persistent index is stored in directory entries.
+ * Special cases: 0 = .
+ * 1 = ..
+ * -1 = End of directory
+ */
+ do_index = 1;
+
+ dir_index = (u32) filp->f_pos;
+
+ if (dir_index > 1) {
+ struct dir_table_slot dirtab_slot;
+
+ if (dtEmpty(ip) ||
+ (dir_index >= JFS_IP(ip)->next_index)) {
+ /* Stale position. Directory has shrunk */
+ filp->f_pos = DIREND;
+ return 0;
+ }
+ repeat:
+ rc = read_index(ip, dir_index, &dirtab_slot);
+ if (rc) {
+ filp->f_pos = DIREND;
+ return rc;
+ }
+ if (dirtab_slot.flag == DIR_INDEX_FREE) {
+ if (loop_count++ > JFS_IP(ip)->next_index) {
+ jfs_err("jfs_readdir detected "
+ "infinite loop!");
+ filp->f_pos = DIREND;
+ return 0;
+ }
+ dir_index = le32_to_cpu(dirtab_slot.addr2);
+ if (dir_index == -1) {
+ filp->f_pos = DIREND;
+ return 0;
+ }
+ goto repeat;
+ }
+ bn = addressDTS(&dirtab_slot);
+ index = dirtab_slot.slot;
+ DT_GETPAGE(ip, bn, mp, PSIZE, p, rc);
+ if (rc) {
+ filp->f_pos = DIREND;
+ return 0;
+ }
+ if (p->header.flag & BT_INTERNAL) {
+ jfs_err("jfs_readdir: bad index table");
+ DT_PUTPAGE(mp);
+ filp->f_pos = -1;
+ return 0;
+ }
+ } else {
+ if (dir_index == 0) {
+ /*
+ * self "."
+ */
+ filp->f_pos = 0;
+ if (filldir(dirent, ".", 1, 0, ip->i_ino,
+ DT_DIR))
+ return 0;
+ }
+ /*
+ * parent ".."
+ */
+ filp->f_pos = 1;
+ if (filldir(dirent, "..", 2, 1, PARENT(ip), DT_DIR))
+ return 0;
+
+ /*
+ * Find first entry of left-most leaf
+ */
+ if (dtEmpty(ip)) {
+ filp->f_pos = DIREND;
+ return 0;
+ }
+
+ if ((rc = dtReadFirst(ip, &btstack)))
+ return rc;
+
+ DT_GETSEARCH(ip, btstack.top, bn, mp, p, index);
+ }
+ } else {
+ /*
+ * Legacy filesystem - OS/2 & Linux JFS < 0.3.6
+ *
+ * pn = index = 0: First entry "."
+ * pn = 0; index = 1: Second entry ".."
+ * pn > 0: Real entries, pn=1 -> leftmost page
+ * pn = index = -1: No more entries
+ */
+ dtpos = filp->f_pos;
+ if (dtpos == 0) {
+ /* build "." entry */
+
+ if (filldir(dirent, ".", 1, filp->f_pos, ip->i_ino,
+ DT_DIR))
+ return 0;
+ dtoffset->index = 1;
+ filp->f_pos = dtpos;
+ }
+
+ if (dtoffset->pn == 0) {
+ if (dtoffset->index == 1) {
+ /* build ".." entry */
+
+ if (filldir(dirent, "..", 2, filp->f_pos,
+ PARENT(ip), DT_DIR))
+ return 0;
+ } else {
+ jfs_err("jfs_readdir called with "
+ "invalid offset!");
+ }
+ dtoffset->pn = 1;
+ dtoffset->index = 0;
+ filp->f_pos = dtpos;
+ }
+
+ if (dtEmpty(ip)) {
+ filp->f_pos = DIREND;
+ return 0;
+ }
+
+ if ((rc = dtReadNext(ip, &filp->f_pos, &btstack))) {
+ jfs_err("jfs_readdir: unexpected rc = %d "
+ "from dtReadNext", rc);
+ filp->f_pos = DIREND;
+ return 0;
+ }
+ /* get start leaf page and index */
+ DT_GETSEARCH(ip, btstack.top, bn, mp, p, index);
+
+ /* offset beyond directory eof ? */
+ if (bn < 0) {
+ filp->f_pos = DIREND;
+ return 0;
+ }
+ }
+
+ dirent_buf = __get_free_page(GFP_KERNEL);
+ if (dirent_buf == 0) {
+ DT_PUTPAGE(mp);
+ jfs_warn("jfs_readdir: __get_free_page failed!");
+ filp->f_pos = DIREND;
+ return -ENOMEM;
+ }
+
+ while (1) {
+ jfs_dirent = (struct jfs_dirent *) dirent_buf;
+ jfs_dirents = 0;
+ overflow = fix_page = 0;
+
+ stbl = DT_GETSTBL(p);
+
+ for (i = index; i < p->header.nextindex; i++) {
+ d = (struct ldtentry *) & p->slot[stbl[i]];
+
+ if (((long) jfs_dirent + d->namlen + 1) >
+ (dirent_buf + PSIZE)) {
+ /* DBCS codepages could overrun dirent_buf */
+ index = i;
+ overflow = 1;
+ break;
+ }
+
+ d_namleft = d->namlen;
+ name_ptr = jfs_dirent->name;
+ jfs_dirent->ino = le32_to_cpu(d->inumber);
+
+ if (do_index) {
+ len = min(d_namleft, DTLHDRDATALEN);
+ jfs_dirent->position = le32_to_cpu(d->index);
+ /*
+ * d->index should always be valid, but it
+ * isn't. fsck.jfs doesn't create the
+ * directory index for the lost+found
+ * directory. Rather than let it go,
+ * we can try to fix it.
+ */
+ if ((jfs_dirent->position < 2) ||
+ (jfs_dirent->position >=
+ JFS_IP(ip)->next_index)) {
+ if (!page_fixed && !isReadOnly(ip)) {
+ fix_page = 1;
+ /*
+ * setting overflow and setting
+ * index to i will cause the
+ * same page to be processed
+ * again starting here
+ */
+ overflow = 1;
+ index = i;
+ break;
+ }
+ jfs_dirent->position = unique_pos++;
+ }
+ } else {
+ jfs_dirent->position = dtpos;
+ len = min(d_namleft, DTLHDRDATALEN_LEGACY);
+ }
+
+ /* copy the name of head/only segment */
+ outlen = jfs_strfromUCS_le(name_ptr, d->name, len,
+ codepage);
+ jfs_dirent->name_len = outlen;
+
+ /* copy name in the additional segment(s) */
+ next = d->next;
+ while (next >= 0) {
+ t = (struct dtslot *) & p->slot[next];
+ name_ptr += outlen;
+ d_namleft -= len;
+ /* Sanity Check */
+ if (d_namleft == 0) {
+ jfs_error(ip->i_sb,
+ "JFS:Dtree error: ino = "
+ "%ld, bn=%Ld, index = %d",
+ (long)ip->i_ino,
+ (long long)bn,
+ i);
+ goto skip_one;
+ }
+ len = min(d_namleft, DTSLOTDATALEN);
+ outlen = jfs_strfromUCS_le(name_ptr, t->name,
+ len, codepage);
+ jfs_dirent->name_len += outlen;
+
+ next = t->next;
+ }
+
+ jfs_dirents++;
+ jfs_dirent = next_jfs_dirent(jfs_dirent);
+skip_one:
+ if (!do_index)
+ dtoffset->index++;
+ }
+
+ if (!overflow) {
+ /* Point to next leaf page */
+ if (p->header.flag & BT_ROOT)
+ bn = 0;
+ else {
+ bn = le64_to_cpu(p->header.next);
+ index = 0;
+ /* update offset (pn:index) for new page */
+ if (!do_index) {
+ dtoffset->pn++;
+ dtoffset->index = 0;
+ }
+ }
+ page_fixed = 0;
+ }
+
+ /* unpin previous leaf page */
+ DT_PUTPAGE(mp);
+
+ jfs_dirent = (struct jfs_dirent *) dirent_buf;
+ while (jfs_dirents--) {
+ filp->f_pos = jfs_dirent->position;
+ if (filldir(dirent, jfs_dirent->name,
+ jfs_dirent->name_len, filp->f_pos,
+ jfs_dirent->ino, DT_UNKNOWN))
+ goto out;
+ jfs_dirent = next_jfs_dirent(jfs_dirent);
+ }
+
+ if (fix_page) {
+ add_missing_indices(ip, bn);
+ page_fixed = 1;
+ }
+
+ if (!overflow && (bn == 0)) {
+ filp->f_pos = DIREND;
+ break;
+ }
+
+ DT_GETPAGE(ip, bn, mp, PSIZE, p, rc);
+ if (rc) {
+ free_page(dirent_buf);
+ return rc;
+ }
+ }
+
+ out:
+ free_page(dirent_buf);
+
+ return rc;
+}
+
+
+/*
+ * dtReadFirst()
+ *
+ * function: get the leftmost page of the directory
+ */
+static int dtReadFirst(struct inode *ip, struct btstack * btstack)
+{
+ int rc = 0;
+ s64 bn;
+ int psize = 288; /* initial in-line directory */
+ struct metapage *mp;
+ dtpage_t *p;
+ s8 *stbl;
+ struct btframe *btsp;
+ pxd_t *xd;
+
+ BT_CLR(btstack); /* reset stack */
+
+ /*
+ * descend leftmost path of the tree
+ *
+ * by convention, root bn = 0.
+ */
+ for (bn = 0;;) {
+ DT_GETPAGE(ip, bn, mp, psize, p, rc);
+ if (rc)
+ return rc;
+
+ /*
+ * leftmost leaf page
+ */
+ if (p->header.flag & BT_LEAF) {
+ /* return leftmost entry */
+ btsp = btstack->top;
+ btsp->bn = bn;
+ btsp->index = 0;
+ btsp->mp = mp;
+
+ return 0;
+ }
+
+ /*
+ * descend down to leftmost child page
+ */
+ if (BT_STACK_FULL(btstack)) {
+ DT_PUTPAGE(mp);
+ jfs_error(ip->i_sb, "dtReadFirst: btstack overrun");
+ BT_STACK_DUMP(btstack);
+ return -EIO;
+ }
+ /* push (bn, index) of the parent page/entry */
+ BT_PUSH(btstack, bn, 0);
+
+ /* get the leftmost entry */
+ stbl = DT_GETSTBL(p);
+ xd = (pxd_t *) & p->slot[stbl[0]];
+
+ /* get the child page block address */
+ bn = addressPXD(xd);
+ psize = lengthPXD(xd) << JFS_SBI(ip->i_sb)->l2bsize;
+
+ /* unpin the parent page */
+ DT_PUTPAGE(mp);
+ }
+}
+
+
+/*
+ * dtReadNext()
+ *
+ * function: get the page of the specified offset (pn:index)
+ *
+ * return: if (offset > eof), bn = -1;
+ *
+ * note: if index > nextindex of the target leaf page,
+ * start with 1st entry of next leaf page;
+ */
+static int dtReadNext(struct inode *ip, loff_t * offset,
+ struct btstack * btstack)
+{
+ int rc = 0;
+ struct dtoffset {
+ s16 pn;
+ s16 index;
+ s32 unused;
+ } *dtoffset = (struct dtoffset *) offset;
+ s64 bn;
+ struct metapage *mp;
+ dtpage_t *p;
+ int index;
+ int pn;
+ s8 *stbl;
+ struct btframe *btsp, *parent;
+ pxd_t *xd;
+
+ /*
+ * get leftmost leaf page pinned
+ */
+ if ((rc = dtReadFirst(ip, btstack)))
+ return rc;
+
+ /* get leaf page */
+ DT_GETSEARCH(ip, btstack->top, bn, mp, p, index);
+
+ /* get the start offset (pn:index) */
+ pn = dtoffset->pn - 1; /* Now pn = 0 represents leftmost leaf */
+ index = dtoffset->index;
+
+ /* start at leftmost page ? */
+ if (pn == 0) {
+ /* offset beyond eof ? */
+ if (index < p->header.nextindex)
+ goto out;
+
+ if (p->header.flag & BT_ROOT) {
+ bn = -1;
+ goto out;
+ }
+
+ /* start with 1st entry of next leaf page */
+ dtoffset->pn++;
+ dtoffset->index = index = 0;
+ goto a;
+ }
+
+ /* start at non-leftmost page: scan parent pages for large pn */
+ if (p->header.flag & BT_ROOT) {
+ bn = -1;
+ goto out;
+ }
+
+ /* start after next leaf page ? */
+ if (pn > 1)
+ goto b;
+
+ /* get leaf page pn = 1 */
+ a:
+ bn = le64_to_cpu(p->header.next);
+
+ /* unpin leaf page */
+ DT_PUTPAGE(mp);
+
+ /* offset beyond eof ? */
+ if (bn == 0) {
+ bn = -1;
+ goto out;
+ }
+
+ goto c;
+
+ /*
+ * scan last internal page level to get target leaf page
+ */
+ b:
+ /* unpin leftmost leaf page */
+ DT_PUTPAGE(mp);
+
+ /* get left most parent page */
+ btsp = btstack->top;
+ parent = btsp - 1;
+ bn = parent->bn;
+ DT_GETPAGE(ip, bn, mp, PSIZE, p, rc);
+ if (rc)
+ return rc;
+
+ /* scan parent pages at last internal page level */
+ while (pn >= p->header.nextindex) {
+ pn -= p->header.nextindex;
+
+ /* get next parent page address */
+ bn = le64_to_cpu(p->header.next);
+
+ /* unpin current parent page */
+ DT_PUTPAGE(mp);
+
+ /* offset beyond eof ? */
+ if (bn == 0) {
+ bn = -1;
+ goto out;
+ }
+
+ /* get next parent page */
+ DT_GETPAGE(ip, bn, mp, PSIZE, p, rc);
+ if (rc)
+ return rc;
+
+ /* update parent page stack frame */
+ parent->bn = bn;
+ }
+
+ /* get leaf page address */
+ stbl = DT_GETSTBL(p);
+ xd = (pxd_t *) & p->slot[stbl[pn]];
+ bn = addressPXD(xd);
+
+ /* unpin parent page */
+ DT_PUTPAGE(mp);
+
+ /*
+ * get target leaf page
+ */
+ c:
+ DT_GETPAGE(ip, bn, mp, PSIZE, p, rc);
+ if (rc)
+ return rc;
+
+ /*
+ * leaf page has been completed:
+ * start with 1st entry of next leaf page
+ */
+ if (index >= p->header.nextindex) {
+ bn = le64_to_cpu(p->header.next);
+
+ /* unpin leaf page */
+ DT_PUTPAGE(mp);
+
+ /* offset beyond eof ? */
+ if (bn == 0) {
+ bn = -1;
+ goto out;
+ }
+
+ /* get next leaf page */
+ DT_GETPAGE(ip, bn, mp, PSIZE, p, rc);
+ if (rc)
+ return rc;
+
+ /* start with 1st entry of next leaf page */
+ dtoffset->pn++;
+ dtoffset->index = 0;
+ }
+
+ out:
+ /* return target leaf page pinned */
+ btsp = btstack->top;
+ btsp->bn = bn;
+ btsp->index = dtoffset->index;
+ btsp->mp = mp;
+
+ return 0;
+}
+
+
+/*
+ * dtCompare()
+ *
+ * function: compare search key with an internal entry
+ *
+ * return:
+ * < 0 if k is < record
+ * = 0 if k is = record
+ * > 0 if k is > record
+ */
+static int dtCompare(struct component_name * key, /* search key */
+ dtpage_t * p, /* directory page */
+ int si)
+{ /* entry slot index */
+ wchar_t *kname, *name;
+ int klen, namlen, len, rc;
+ struct idtentry *ih;
+ struct dtslot *t;
+
+ /*
+ * force the left-most key on internal pages, at any level of
+ * the tree, to be less than any search key.
+ * this obviates having to update the leftmost key on an internal
+ * page when the user inserts a new key in the tree smaller than
+ * anything that has been stored.
+ *
+ * (? if/when dtSearch() narrows down to 1st entry (index = 0),
+ * at any internal page at any level of the tree,
+ * it descends to child of the entry anyway -
+ * ? make the entry as min size dummy entry)
+ *
+ * if (e->index == 0 && h->prevpg == P_INVALID && !(h->flags & BT_LEAF))
+ * return (1);
+ */
+
+ kname = key->name;
+ klen = key->namlen;
+
+ ih = (struct idtentry *) & p->slot[si];
+ si = ih->next;
+ name = ih->name;
+ namlen = ih->namlen;
+ len = min(namlen, DTIHDRDATALEN);
+
+ /* compare with head/only segment */
+ len = min(klen, len);
+ if ((rc = UniStrncmp_le(kname, name, len)))
+ return rc;
+
+ klen -= len;
+ namlen -= len;
+
+ /* compare with additional segment(s) */
+ kname += len;
+ while (klen > 0 && namlen > 0) {
+ /* compare with next name segment */
+ t = (struct dtslot *) & p->slot[si];
+ len = min(namlen, DTSLOTDATALEN);
+ len = min(klen, len);
+ name = t->name;
+ if ((rc = UniStrncmp_le(kname, name, len)))
+ return rc;
+
+ klen -= len;
+ namlen -= len;
+ kname += len;
+ si = t->next;
+ }
+
+ return (klen - namlen);
+}
+
+
+
+
+/*
+ * ciCompare()
+ *
+ * function: compare search key with an (leaf/internal) entry
+ *
+ * return:
+ * < 0 if k is < record
+ * = 0 if k is = record
+ * > 0 if k is > record
+ */
+static int ciCompare(struct component_name * key, /* search key */
+ dtpage_t * p, /* directory page */
+ int si, /* entry slot index */
+ int flag)
+{
+ wchar_t *kname, *name, x;
+ int klen, namlen, len, rc;
+ struct ldtentry *lh;
+ struct idtentry *ih;
+ struct dtslot *t;
+ int i;
+
+ /*
+ * force the left-most key on internal pages, at any level of
+ * the tree, to be less than any search key.
+ * this obviates having to update the leftmost key on an internal
+ * page when the user inserts a new key in the tree smaller than
+ * anything that has been stored.
+ *
+ * (? if/when dtSearch() narrows down to 1st entry (index = 0),
+ * at any internal page at any level of the tree,
+ * it descends to child of the entry anyway -
+ * ? make the entry as min size dummy entry)
+ *
+ * if (e->index == 0 && h->prevpg == P_INVALID && !(h->flags & BT_LEAF))
+ * return (1);
+ */
+
+ kname = key->name;
+ klen = key->namlen;
+
+ /*
+ * leaf page entry
+ */
+ if (p->header.flag & BT_LEAF) {
+ lh = (struct ldtentry *) & p->slot[si];
+ si = lh->next;
+ name = lh->name;
+ namlen = lh->namlen;
+ if (flag & JFS_DIR_INDEX)
+ len = min(namlen, DTLHDRDATALEN);
+ else
+ len = min(namlen, DTLHDRDATALEN_LEGACY);
+ }
+ /*
+ * internal page entry
+ */
+ else {
+ ih = (struct idtentry *) & p->slot[si];
+ si = ih->next;
+ name = ih->name;
+ namlen = ih->namlen;
+ len = min(namlen, DTIHDRDATALEN);
+ }
+
+ /* compare with head/only segment */
+ len = min(klen, len);
+ for (i = 0; i < len; i++, kname++, name++) {
+ /* only uppercase if case-insensitive support is on */
+ if ((flag & JFS_OS2) == JFS_OS2)
+ x = UniToupper(le16_to_cpu(*name));
+ else
+ x = le16_to_cpu(*name);
+ if ((rc = *kname - x))
+ return rc;
+ }
+
+ klen -= len;
+ namlen -= len;
+
+ /* compare with additional segment(s) */
+ while (klen > 0 && namlen > 0) {
+ /* compare with next name segment */
+ t = (struct dtslot *) & p->slot[si];
+ len = min(namlen, DTSLOTDATALEN);
+ len = min(klen, len);
+ name = t->name;
+ for (i = 0; i < len; i++, kname++, name++) {
+ /* only uppercase if case-insensitive support is on */
+ if ((flag & JFS_OS2) == JFS_OS2)
+ x = UniToupper(le16_to_cpu(*name));
+ else
+ x = le16_to_cpu(*name);
+
+ if ((rc = *kname - x))
+ return rc;
+ }
+
+ klen -= len;
+ namlen -= len;
+ si = t->next;
+ }
+
+ return (klen - namlen);
+}
+
+
+/*
+ * ciGetLeafPrefixKey()
+ *
+ * function: compute prefix of suffix compression
+ * from two adjacent leaf entries
+ * across page boundary
+ *
+ * return: non-zero on error
+ *
+ */
+static int ciGetLeafPrefixKey(dtpage_t * lp, int li, dtpage_t * rp,
+ int ri, struct component_name * key, int flag)
+{
+ int klen, namlen;
+ wchar_t *pl, *pr, *kname;
+ struct component_name lkey;
+ struct component_name rkey;
+
+ lkey.name = (wchar_t *) kmalloc((JFS_NAME_MAX + 1) * sizeof(wchar_t),
+ GFP_KERNEL);
+ if (lkey.name == NULL)
+ return -ENOSPC;
+
+ rkey.name = (wchar_t *) kmalloc((JFS_NAME_MAX + 1) * sizeof(wchar_t),
+ GFP_KERNEL);
+ if (rkey.name == NULL) {
+ kfree(lkey.name);
+ return -ENOSPC;
+ }
+
+ /* get left and right key */
+ dtGetKey(lp, li, &lkey, flag);
+ lkey.name[lkey.namlen] = 0;
+
+ if ((flag & JFS_OS2) == JFS_OS2)
+ ciToUpper(&lkey);
+
+ dtGetKey(rp, ri, &rkey, flag);
+ rkey.name[rkey.namlen] = 0;
+
+
+ if ((flag & JFS_OS2) == JFS_OS2)
+ ciToUpper(&rkey);
+
+ /* compute prefix */
+ klen = 0;
+ kname = key->name;
+ namlen = min(lkey.namlen, rkey.namlen);
+ for (pl = lkey.name, pr = rkey.name;
+ namlen; pl++, pr++, namlen--, klen++, kname++) {
+ *kname = *pr;
+ if (*pl != *pr) {
+ key->namlen = klen + 1;
+ goto free_names;
+ }
+ }
+
+ /* l->namlen <= r->namlen since l <= r */
+ if (lkey.namlen < rkey.namlen) {
+ *kname = *pr;
+ key->namlen = klen + 1;
+ } else /* l->namelen == r->namelen */
+ key->namlen = klen;
+
+free_names:
+ kfree(lkey.name);
+ kfree(rkey.name);
+ return 0;
+}
+
+
+
+/*
+ * dtGetKey()
+ *
+ * function: get key of the entry
+ */
+static void dtGetKey(dtpage_t * p, int i, /* entry index */
+ struct component_name * key, int flag)
+{
+ int si;
+ s8 *stbl;
+ struct ldtentry *lh;
+ struct idtentry *ih;
+ struct dtslot *t;
+ int namlen, len;
+ wchar_t *name, *kname;
+
+ /* get entry */
+ stbl = DT_GETSTBL(p);
+ si = stbl[i];
+ if (p->header.flag & BT_LEAF) {
+ lh = (struct ldtentry *) & p->slot[si];
+ si = lh->next;
+ namlen = lh->namlen;
+ name = lh->name;
+ if (flag & JFS_DIR_INDEX)
+ len = min(namlen, DTLHDRDATALEN);
+ else
+ len = min(namlen, DTLHDRDATALEN_LEGACY);
+ } else {
+ ih = (struct idtentry *) & p->slot[si];
+ si = ih->next;
+ namlen = ih->namlen;
+ name = ih->name;
+ len = min(namlen, DTIHDRDATALEN);
+ }
+
+ key->namlen = namlen;
+ kname = key->name;
+
+ /*
+ * move head/only segment
+ */
+ UniStrncpy_le(kname, name, len);
+
+ /*
+ * move additional segment(s)
+ */
+ while (si >= 0) {
+ /* get next segment */
+ t = &p->slot[si];
+ kname += len;
+ namlen -= len;
+ len = min(namlen, DTSLOTDATALEN);
+ UniStrncpy_le(kname, t->name, len);
+
+ si = t->next;
+ }
+}
+
+
+/*
+ * dtInsertEntry()
+ *
+ * function: allocate free slot(s) and
+ * write a leaf/internal entry
+ *
+ * return: entry slot index
+ */
+static void dtInsertEntry(dtpage_t * p, int index, struct component_name * key,
+ ddata_t * data, struct dt_lock ** dtlock)
+{
+ struct dtslot *h, *t;
+ struct ldtentry *lh = 0;
+ struct idtentry *ih = 0;
+ int hsi, fsi, klen, len, nextindex;
+ wchar_t *kname, *name;
+ s8 *stbl;
+ pxd_t *xd;
+ struct dt_lock *dtlck = *dtlock;
+ struct lv *lv;
+ int xsi, n;
+ s64 bn = 0;
+ struct metapage *mp = 0;
+
+ klen = key->namlen;
+ kname = key->name;
+
+ /* allocate a free slot */
+ hsi = fsi = p->header.freelist;
+ h = &p->slot[fsi];
+ p->header.freelist = h->next;
+ --p->header.freecnt;
+
+ /* open new linelock */
+ if (dtlck->index >= dtlck->maxcnt)
+ dtlck = (struct dt_lock *) txLinelock(dtlck);
+
+ lv = & dtlck->lv[dtlck->index];
+ lv->offset = hsi;
+
+ /* write head/only segment */
+ if (p->header.flag & BT_LEAF) {
+ lh = (struct ldtentry *) h;
+ lh->next = h->next;
+ lh->inumber = data->leaf.ino; /* little-endian */
+ lh->namlen = klen;
+ name = lh->name;
+ if (data->leaf.ip) {
+ len = min(klen, DTLHDRDATALEN);
+ if (!(p->header.flag & BT_ROOT))
+ bn = addressPXD(&p->header.self);
+ lh->index = cpu_to_le32(add_index(data->leaf.tid,
+ data->leaf.ip,
+ bn, index));
+ } else
+ len = min(klen, DTLHDRDATALEN_LEGACY);
+ } else {
+ ih = (struct idtentry *) h;
+ ih->next = h->next;
+ xd = (pxd_t *) ih;
+ *xd = data->xd;
+ ih->namlen = klen;
+ name = ih->name;
+ len = min(klen, DTIHDRDATALEN);
+ }
+
+ UniStrncpy_le(name, kname, len);
+
+ n = 1;
+ xsi = hsi;
+
+ /* write additional segment(s) */
+ t = h;
+ klen -= len;
+ while (klen) {
+ /* get free slot */
+ fsi = p->header.freelist;
+ t = &p->slot[fsi];
+ p->header.freelist = t->next;
+ --p->header.freecnt;
+
+ /* is next slot contiguous ? */
+ if (fsi != xsi + 1) {
+ /* close current linelock */
+ lv->length = n;
+ dtlck->index++;
+
+ /* open new linelock */
+ if (dtlck->index < dtlck->maxcnt)
+ lv++;
+ else {
+ dtlck = (struct dt_lock *) txLinelock(dtlck);
+ lv = & dtlck->lv[0];
+ }
+
+ lv->offset = fsi;
+ n = 0;
+ }
+
+ kname += len;
+ len = min(klen, DTSLOTDATALEN);
+ UniStrncpy_le(t->name, kname, len);
+
+ n++;
+ xsi = fsi;
+ klen -= len;
+ }
+
+ /* close current linelock */
+ lv->length = n;
+ dtlck->index++;
+
+ *dtlock = dtlck;
+
+ /* terminate last/only segment */
+ if (h == t) {
+ /* single segment entry */
+ if (p->header.flag & BT_LEAF)
+ lh->next = -1;
+ else
+ ih->next = -1;
+ } else
+ /* multi-segment entry */
+ t->next = -1;
+
+ /* if insert into middle, shift right succeeding entries in stbl */
+ stbl = DT_GETSTBL(p);
+ nextindex = p->header.nextindex;
+ if (index < nextindex) {
+ memmove(stbl + index + 1, stbl + index, nextindex - index);
+
+ if ((p->header.flag & BT_LEAF) && data->leaf.ip) {
+ s64 lblock;
+
+ /*
+ * Need to update slot number for entries that moved
+ * in the stbl
+ */
+ mp = 0;
+ for (n = index + 1; n <= nextindex; n++) {
+ lh = (struct ldtentry *) & (p->slot[stbl[n]]);
+ modify_index(data->leaf.tid, data->leaf.ip,
+ le32_to_cpu(lh->index), bn, n,
+ &mp, &lblock);
+ }
+ if (mp)
+ release_metapage(mp);
+ }
+ }
+
+ stbl[index] = hsi;
+
+ /* advance next available entry index of stbl */
+ ++p->header.nextindex;
+}
+
+
+/*
+ * dtMoveEntry()
+ *
+ * function: move entries from split/left page to new/right page
+ *
+ * nextindex of dst page and freelist/freecnt of both pages
+ * are updated.
+ */
+static void dtMoveEntry(dtpage_t * sp, int si, dtpage_t * dp,
+ struct dt_lock ** sdtlock, struct dt_lock ** ddtlock,
+ int do_index)
+{
+ int ssi, next; /* src slot index */
+ int di; /* dst entry index */
+ int dsi; /* dst slot index */
+ s8 *sstbl, *dstbl; /* sorted entry table */
+ int snamlen, len;
+ struct ldtentry *slh, *dlh = 0;
+ struct idtentry *sih, *dih = 0;
+ struct dtslot *h, *s, *d;
+ struct dt_lock *sdtlck = *sdtlock, *ddtlck = *ddtlock;
+ struct lv *slv, *dlv;
+ int xssi, ns, nd;
+ int sfsi;
+
+ sstbl = (s8 *) & sp->slot[sp->header.stblindex];
+ dstbl = (s8 *) & dp->slot[dp->header.stblindex];
+
+ dsi = dp->header.freelist; /* first (whole page) free slot */
+ sfsi = sp->header.freelist;
+
+ /* linelock destination entry slot */
+ dlv = & ddtlck->lv[ddtlck->index];
+ dlv->offset = dsi;
+
+ /* linelock source entry slot */
+ slv = & sdtlck->lv[sdtlck->index];
+ slv->offset = sstbl[si];
+ xssi = slv->offset - 1;
+
+ /*
+ * move entries
+ */
+ ns = nd = 0;
+ for (di = 0; si < sp->header.nextindex; si++, di++) {
+ ssi = sstbl[si];
+ dstbl[di] = dsi;
+
+ /* is next slot contiguous ? */
+ if (ssi != xssi + 1) {
+ /* close current linelock */
+ slv->length = ns;
+ sdtlck->index++;
+
+ /* open new linelock */
+ if (sdtlck->index < sdtlck->maxcnt)
+ slv++;
+ else {
+ sdtlck = (struct dt_lock *) txLinelock(sdtlck);
+ slv = & sdtlck->lv[0];
+ }
+
+ slv->offset = ssi;
+ ns = 0;
+ }
+
+ /*
+ * move head/only segment of an entry
+ */
+ /* get dst slot */
+ h = d = &dp->slot[dsi];
+
+ /* get src slot and move */
+ s = &sp->slot[ssi];
+ if (sp->header.flag & BT_LEAF) {
+ /* get source entry */
+ slh = (struct ldtentry *) s;
+ dlh = (struct ldtentry *) h;
+ snamlen = slh->namlen;
+
+ if (do_index) {
+ len = min(snamlen, DTLHDRDATALEN);
+ dlh->index = slh->index; /* little-endian */
+ } else
+ len = min(snamlen, DTLHDRDATALEN_LEGACY);
+
+ memcpy(dlh, slh, 6 + len * 2);
+
+ next = slh->next;
+
+ /* update dst head/only segment next field */
+ dsi++;
+ dlh->next = dsi;
+ } else {
+ sih = (struct idtentry *) s;
+ snamlen = sih->namlen;
+
+ len = min(snamlen, DTIHDRDATALEN);
+ dih = (struct idtentry *) h;
+ memcpy(dih, sih, 10 + len * 2);
+ next = sih->next;
+
+ dsi++;
+ dih->next = dsi;
+ }
+
+ /* free src head/only segment */
+ s->next = sfsi;
+ s->cnt = 1;
+ sfsi = ssi;
+
+ ns++;
+ nd++;
+ xssi = ssi;
+
+ /*
+ * move additional segment(s) of the entry
+ */
+ snamlen -= len;
+ while ((ssi = next) >= 0) {
+ /* is next slot contiguous ? */
+ if (ssi != xssi + 1) {
+ /* close current linelock */
+ slv->length = ns;
+ sdtlck->index++;
+
+ /* open new linelock */
+ if (sdtlck->index < sdtlck->maxcnt)
+ slv++;
+ else {
+ sdtlck =
+ (struct dt_lock *)
+ txLinelock(sdtlck);
+ slv = & sdtlck->lv[0];
+ }
+
+ slv->offset = ssi;
+ ns = 0;
+ }
+
+ /* get next source segment */
+ s = &sp->slot[ssi];
+
+ /* get next destination free slot */
+ d++;
+
+ len = min(snamlen, DTSLOTDATALEN);
+ UniStrncpy(d->name, s->name, len);
+
+ ns++;
+ nd++;
+ xssi = ssi;
+
+ dsi++;
+ d->next = dsi;
+
+ /* free source segment */
+ next = s->next;
+ s->next = sfsi;
+ s->cnt = 1;
+ sfsi = ssi;
+
+ snamlen -= len;
+ } /* end while */
+
+ /* terminate dst last/only segment */
+ if (h == d) {
+ /* single segment entry */
+ if (dp->header.flag & BT_LEAF)
+ dlh->next = -1;
+ else
+ dih->next = -1;
+ } else
+ /* multi-segment entry */
+ d->next = -1;
+ } /* end for */
+
+ /* close current linelock */
+ slv->length = ns;
+ sdtlck->index++;
+ *sdtlock = sdtlck;
+
+ dlv->length = nd;
+ ddtlck->index++;
+ *ddtlock = ddtlck;
+
+ /* update source header */
+ sp->header.freelist = sfsi;
+ sp->header.freecnt += nd;
+
+ /* update destination header */
+ dp->header.nextindex = di;
+
+ dp->header.freelist = dsi;
+ dp->header.freecnt -= nd;
+}
+
+
+/*
+ * dtDeleteEntry()
+ *
+ * function: free a (leaf/internal) entry
+ *
+ * log freelist header, stbl, and each segment slot of entry
+ * (even though last/only segment next field is modified,
+ * physical image logging requires all segment slots of
+ * the entry logged to avoid applying previous updates
+ * to the same slots)
+ */
+static void dtDeleteEntry(dtpage_t * p, int fi, struct dt_lock ** dtlock)
+{
+ int fsi; /* free entry slot index */
+ s8 *stbl;
+ struct dtslot *t;
+ int si, freecnt;
+ struct dt_lock *dtlck = *dtlock;
+ struct lv *lv;
+ int xsi, n;
+
+ /* get free entry slot index */
+ stbl = DT_GETSTBL(p);
+ fsi = stbl[fi];
+
+ /* open new linelock */
+ if (dtlck->index >= dtlck->maxcnt)
+ dtlck = (struct dt_lock *) txLinelock(dtlck);
+ lv = & dtlck->lv[dtlck->index];
+
+ lv->offset = fsi;
+
+ /* get the head/only segment */
+ t = &p->slot[fsi];
+ if (p->header.flag & BT_LEAF)
+ si = ((struct ldtentry *) t)->next;
+ else
+ si = ((struct idtentry *) t)->next;
+ t->next = si;
+ t->cnt = 1;
+
+ n = freecnt = 1;
+ xsi = fsi;
+
+ /* find the last/only segment */
+ while (si >= 0) {
+ /* is next slot contiguous ? */
+ if (si != xsi + 1) {
+ /* close current linelock */
+ lv->length = n;
+ dtlck->index++;
+
+ /* open new linelock */
+ if (dtlck->index < dtlck->maxcnt)
+ lv++;
+ else {
+ dtlck = (struct dt_lock *) txLinelock(dtlck);
+ lv = & dtlck->lv[0];
+ }
+
+ lv->offset = si;
+ n = 0;
+ }
+
+ n++;
+ xsi = si;
+ freecnt++;
+
+ t = &p->slot[si];
+ t->cnt = 1;
+ si = t->next;
+ }
+
+ /* close current linelock */
+ lv->length = n;
+ dtlck->index++;
+
+ *dtlock = dtlck;
+
+ /* update freelist */
+ t->next = p->header.freelist;
+ p->header.freelist = fsi;
+ p->header.freecnt += freecnt;
+
+ /* if delete from middle,
+ * shift left the succedding entries in the stbl
+ */
+ si = p->header.nextindex;
+ if (fi < si - 1)
+ memmove(&stbl[fi], &stbl[fi + 1], si - fi - 1);
+
+ p->header.nextindex--;
+}
+
+
+/*
+ * dtTruncateEntry()
+ *
+ * function: truncate a (leaf/internal) entry
+ *
+ * log freelist header, stbl, and each segment slot of entry
+ * (even though last/only segment next field is modified,
+ * physical image logging requires all segment slots of
+ * the entry logged to avoid applying previous updates
+ * to the same slots)
+ */
+static void dtTruncateEntry(dtpage_t * p, int ti, struct dt_lock ** dtlock)
+{
+ int tsi; /* truncate entry slot index */
+ s8 *stbl;
+ struct dtslot *t;
+ int si, freecnt;
+ struct dt_lock *dtlck = *dtlock;
+ struct lv *lv;
+ int fsi, xsi, n;
+
+ /* get free entry slot index */
+ stbl = DT_GETSTBL(p);
+ tsi = stbl[ti];
+
+ /* open new linelock */
+ if (dtlck->index >= dtlck->maxcnt)
+ dtlck = (struct dt_lock *) txLinelock(dtlck);
+ lv = & dtlck->lv[dtlck->index];
+
+ lv->offset = tsi;
+
+ /* get the head/only segment */
+ t = &p->slot[tsi];
+ ASSERT(p->header.flag & BT_INTERNAL);
+ ((struct idtentry *) t)->namlen = 0;
+ si = ((struct idtentry *) t)->next;
+ ((struct idtentry *) t)->next = -1;
+
+ n = 1;
+ freecnt = 0;
+ fsi = si;
+ xsi = tsi;
+
+ /* find the last/only segment */
+ while (si >= 0) {
+ /* is next slot contiguous ? */
+ if (si != xsi + 1) {
+ /* close current linelock */
+ lv->length = n;
+ dtlck->index++;
+
+ /* open new linelock */
+ if (dtlck->index < dtlck->maxcnt)
+ lv++;
+ else {
+ dtlck = (struct dt_lock *) txLinelock(dtlck);
+ lv = & dtlck->lv[0];
+ }
+
+ lv->offset = si;
+ n = 0;
+ }
+
+ n++;
+ xsi = si;
+ freecnt++;
+
+ t = &p->slot[si];
+ t->cnt = 1;
+ si = t->next;
+ }
+
+ /* close current linelock */
+ lv->length = n;
+ dtlck->index++;
+
+ *dtlock = dtlck;
+
+ /* update freelist */
+ if (freecnt == 0)
+ return;
+ t->next = p->header.freelist;
+ p->header.freelist = fsi;
+ p->header.freecnt += freecnt;
+}
+
+
+/*
+ * dtLinelockFreelist()
+ */
+static void dtLinelockFreelist(dtpage_t * p, /* directory page */
+ int m, /* max slot index */
+ struct dt_lock ** dtlock)
+{
+ int fsi; /* free entry slot index */
+ struct dtslot *t;
+ int si;
+ struct dt_lock *dtlck = *dtlock;
+ struct lv *lv;
+ int xsi, n;
+
+ /* get free entry slot index */
+ fsi = p->header.freelist;
+
+ /* open new linelock */
+ if (dtlck->index >= dtlck->maxcnt)
+ dtlck = (struct dt_lock *) txLinelock(dtlck);
+ lv = & dtlck->lv[dtlck->index];
+
+ lv->offset = fsi;
+
+ n = 1;
+ xsi = fsi;
+
+ t = &p->slot[fsi];
+ si = t->next;
+
+ /* find the last/only segment */
+ while (si < m && si >= 0) {
+ /* is next slot contiguous ? */
+ if (si != xsi + 1) {
+ /* close current linelock */
+ lv->length = n;
+ dtlck->index++;
+
+ /* open new linelock */
+ if (dtlck->index < dtlck->maxcnt)
+ lv++;
+ else {
+ dtlck = (struct dt_lock *) txLinelock(dtlck);
+ lv = & dtlck->lv[0];
+ }
+
+ lv->offset = si;
+ n = 0;
+ }
+
+ n++;
+ xsi = si;
+
+ t = &p->slot[si];
+ si = t->next;
+ }
+
+ /* close current linelock */
+ lv->length = n;
+ dtlck->index++;
+
+ *dtlock = dtlck;
+}
+
+
+/*
+ * NAME: dtModify
+ *
+ * FUNCTION: Modify the inode number part of a directory entry
+ *
+ * PARAMETERS:
+ * tid - Transaction id
+ * ip - Inode of parent directory
+ * key - Name of entry to be modified
+ * orig_ino - Original inode number expected in entry
+ * new_ino - New inode number to put into entry
+ * flag - JFS_RENAME
+ *
+ * RETURNS:
+ * -ESTALE - If entry found does not match orig_ino passed in
+ * -ENOENT - If no entry can be found to match key
+ * 0 - If successfully modified entry
+ */
+int dtModify(tid_t tid, struct inode *ip,
+ struct component_name * key, ino_t * orig_ino, ino_t new_ino, int flag)
+{
+ int rc;
+ s64 bn;
+ struct metapage *mp;
+ dtpage_t *p;
+ int index;
+ struct btstack btstack;
+ struct tlock *tlck;
+ struct dt_lock *dtlck;
+ struct lv *lv;
+ s8 *stbl;
+ int entry_si; /* entry slot index */
+ struct ldtentry *entry;
+
+ /*
+ * search for the entry to modify:
+ *
+ * dtSearch() returns (leaf page pinned, index at which to modify).
+ */
+ if ((rc = dtSearch(ip, key, orig_ino, &btstack, flag)))
+ return rc;
+
+ /* retrieve search result */
+ DT_GETSEARCH(ip, btstack.top, bn, mp, p, index);
+
+ BT_MARK_DIRTY(mp, ip);
+ /*
+ * acquire a transaction lock on the leaf page of named entry
+ */
+ tlck = txLock(tid, ip, mp, tlckDTREE | tlckENTRY);
+ dtlck = (struct dt_lock *) & tlck->lock;
+
+ /* get slot index of the entry */
+ stbl = DT_GETSTBL(p);
+ entry_si = stbl[index];
+
+ /* linelock entry */
+ ASSERT(dtlck->index == 0);
+ lv = & dtlck->lv[0];
+ lv->offset = entry_si;
+ lv->length = 1;
+ dtlck->index++;
+
+ /* get the head/only segment */
+ entry = (struct ldtentry *) & p->slot[entry_si];
+
+ /* substitute the inode number of the entry */
+ entry->inumber = cpu_to_le32(new_ino);
+
+ /* unpin the leaf page */
+ DT_PUTPAGE(mp);
+
+ return 0;
+}
+
+#ifdef _JFS_DEBUG_DTREE
+/*
+ * dtDisplayTree()
+ *
+ * function: traverse forward
+ */
+int dtDisplayTree(struct inode *ip)
+{
+ int rc;
+ struct metapage *mp;
+ dtpage_t *p;
+ s64 bn, pbn;
+ int index, lastindex, v, h;
+ pxd_t *xd;
+ struct btstack btstack;
+ struct btframe *btsp;
+ struct btframe *parent;
+ u8 *stbl;
+ int psize = 256;
+
+ printk("display B+-tree.\n");
+
+ /* clear stack */
+ btsp = btstack.stack;
+
+ /*
+ * start with root
+ *
+ * root resides in the inode
+ */
+ bn = 0;
+ v = h = 0;
+
+ /*
+ * first access of each page:
+ */
+ newPage:
+ DT_GETPAGE(ip, bn, mp, psize, p, rc);
+ if (rc)
+ return rc;
+
+ /* process entries forward from first index */
+ index = 0;
+ lastindex = p->header.nextindex - 1;
+
+ if (p->header.flag & BT_INTERNAL) {
+ /*
+ * first access of each internal page
+ */
+ printf("internal page ");
+ dtDisplayPage(ip, bn, p);
+
+ goto getChild;
+ } else { /* (p->header.flag & BT_LEAF) */
+
+ /*
+ * first access of each leaf page
+ */
+ printf("leaf page ");
+ dtDisplayPage(ip, bn, p);
+
+ /*
+ * process leaf page entries
+ *
+ for ( ; index <= lastindex; index++)
+ {
+ }
+ */
+
+ /* unpin the leaf page */
+ DT_PUTPAGE(mp);
+ }
+
+ /*
+ * go back up to the parent page
+ */
+ getParent:
+ /* pop/restore parent entry for the current child page */
+ if ((parent = (btsp == btstack.stack ? NULL : --btsp)) == NULL)
+ /* current page must have been root */
+ return;
+
+ /*
+ * parent page scan completed
+ */
+ if ((index = parent->index) == (lastindex = parent->lastindex)) {
+ /* go back up to the parent page */
+ goto getParent;
+ }
+
+ /*
+ * parent page has entries remaining
+ */
+ /* get back the parent page */
+ bn = parent->bn;
+ /* v = parent->level; */
+ DT_GETPAGE(ip, bn, mp, PSIZE, p, rc);
+ if (rc)
+ return rc;
+
+ /* get next parent entry */
+ index++;
+
+ /*
+ * internal page: go down to child page of current entry
+ */
+ getChild:
+ /* push/save current parent entry for the child page */
+ btsp->bn = pbn = bn;
+ btsp->index = index;
+ btsp->lastindex = lastindex;
+ /* btsp->level = v; */
+ /* btsp->node = h; */
+ ++btsp;
+
+ /* get current entry for the child page */
+ stbl = DT_GETSTBL(p);
+ xd = (pxd_t *) & p->slot[stbl[index]];
+
+ /*
+ * first access of each internal entry:
+ */
+
+ /* get child page */
+ bn = addressPXD(xd);
+ psize = lengthPXD(xd) << ip->i_ipmnt->i_l2bsize;
+
+ printk("traverse down 0x%Lx[%d]->0x%Lx\n", pbn, index, bn);
+ v++;
+ h = index;
+
+ /* release parent page */
+ DT_PUTPAGE(mp);
+
+ /* process the child page */
+ goto newPage;
+}
+
+
+/*
+ * dtDisplayPage()
+ *
+ * function: display page
+ */
+int dtDisplayPage(struct inode *ip, s64 bn, dtpage_t * p)
+{
+ int rc;
+ struct metapage *mp;
+ struct ldtentry *lh;
+ struct idtentry *ih;
+ pxd_t *xd;
+ int i, j;
+ u8 *stbl;
+ wchar_t name[JFS_NAME_MAX + 1];
+ struct component_name key = { 0, name };
+ int freepage = 0;
+
+ if (p == NULL) {
+ freepage = 1;
+ DT_GETPAGE(ip, bn, mp, PSIZE, p, rc);
+ if (rc)
+ return rc;
+ }
+
+ /* display page control */
+ printk("bn:0x%Lx flag:0x%08x nextindex:%d\n",
+ bn, p->header.flag, p->header.nextindex);
+
+ /* display entries */
+ stbl = DT_GETSTBL(p);
+ for (i = 0, j = 1; i < p->header.nextindex; i++, j++) {
+ dtGetKey(p, i, &key, JFS_SBI(ip->i_sb)->mntflag);
+ key.name[key.namlen] = '\0';
+ if (p->header.flag & BT_LEAF) {
+ lh = (struct ldtentry *) & p->slot[stbl[i]];
+ printf("\t[%d] %s:%d", i, key.name,
+ le32_to_cpu(lh->inumber));
+ } else {
+ ih = (struct idtentry *) & p->slot[stbl[i]];
+ xd = (pxd_t *) ih;
+ bn = addressPXD(xd);
+ printf("\t[%d] %s:0x%Lx", i, key.name, bn);
+ }
+
+ if (j == 4) {
+ printf("\n");
+ j = 0;
+ }
+ }
+
+ printf("\n");
+
+ if (freepage)
+ DT_PUTPAGE(mp);
+
+ return 0;
+}
+#endif /* _JFS_DEBUG_DTREE */
diff --git a/fs/jfs/jfs_dtree.h b/fs/jfs/jfs_dtree.h
new file mode 100644
index 00000000000000..d7bc0cfae1f621
--- /dev/null
+++ b/fs/jfs/jfs_dtree.h
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2000-2002
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _H_JFS_DTREE
+#define _H_JFS_DTREE
+
+/*
+ * jfs_dtree.h: directory B+-tree manager
+ */
+
+#include "jfs_btree.h"
+
+typedef union {
+ struct {
+ tid_t tid;
+ struct inode *ip;
+ u32 ino;
+ } leaf;
+ pxd_t xd;
+} ddata_t;
+
+
+/*
+ * entry segment/slot
+ *
+ * an entry consists of type dependent head/only segment/slot and
+ * additional segments/slots linked vi next field;
+ * N.B. last/only segment of entry is terminated by next = -1;
+ */
+/*
+ * directory page slot
+ */
+struct dtslot {
+ s8 next; /* 1: */
+ s8 cnt; /* 1: */
+ wchar_t name[15]; /* 30: */
+}; /* (32) */
+
+
+#define DATASLOTSIZE 16
+#define L2DATASLOTSIZE 4
+#define DTSLOTSIZE 32
+#define L2DTSLOTSIZE 5
+#define DTSLOTHDRSIZE 2
+#define DTSLOTDATASIZE 30
+#define DTSLOTDATALEN 15
+
+/*
+ * internal node entry head/only segment
+ */
+struct idtentry {
+ pxd_t xd; /* 8: child extent descriptor */
+
+ s8 next; /* 1: */
+ u8 namlen; /* 1: */
+ wchar_t name[11]; /* 22: 2-byte aligned */
+}; /* (32) */
+
+#define DTIHDRSIZE 10
+#define DTIHDRDATALEN 11
+
+/* compute number of slots for entry */
+#define NDTINTERNAL(klen) ( ((4 + (klen)) + (15 - 1)) / 15 )
+
+
+/*
+ * leaf node entry head/only segment
+ *
+ * For legacy filesystems, name contains 13 wchars -- no index field
+ */
+struct ldtentry {
+ u32 inumber; /* 4: 4-byte aligned */
+ s8 next; /* 1: */
+ u8 namlen; /* 1: */
+ wchar_t name[11]; /* 22: 2-byte aligned */
+ u32 index; /* 4: index into dir_table */
+}; /* (32) */
+
+#define DTLHDRSIZE 6
+#define DTLHDRDATALEN_LEGACY 13 /* Old (OS/2) format */
+#define DTLHDRDATALEN 11
+
+/*
+ * dir_table used for directory traversal during readdir
+ */
+
+/*
+ * Keep persistent index for directory entries
+ */
+#define DO_INDEX(INODE) (JFS_SBI((INODE)->i_sb)->mntflag & JFS_DIR_INDEX)
+
+/*
+ * Maximum entry in inline directory table
+ */
+#define MAX_INLINE_DIRTABLE_ENTRY 13
+
+struct dir_table_slot {
+ u8 rsrvd; /* 1: */
+ u8 flag; /* 1: 0 if free */
+ u8 slot; /* 1: slot within leaf page of entry */
+ u8 addr1; /* 1: upper 8 bits of leaf page address */
+ u32 addr2; /* 4: lower 32 bits of leaf page address -OR-
+ index of next entry when this entry was deleted */
+}; /* (8) */
+
+/*
+ * flag values
+ */
+#define DIR_INDEX_VALID 1
+#define DIR_INDEX_FREE 0
+
+#define DTSaddress(dir_table_slot, address64)\
+{\
+ (dir_table_slot)->addr1 = ((u64)address64) >> 32;\
+ (dir_table_slot)->addr2 = __cpu_to_le32((address64) & 0xffffffff);\
+}
+
+#define addressDTS(dts)\
+ ( ((s64)((dts)->addr1)) << 32 | __le32_to_cpu((dts)->addr2) )
+
+/* compute number of slots for entry */
+#define NDTLEAF_LEGACY(klen) ( ((2 + (klen)) + (15 - 1)) / 15 )
+#define NDTLEAF NDTINTERNAL
+
+
+/*
+ * directory root page (in-line in on-disk inode):
+ *
+ * cf. dtpage_t below.
+ */
+typedef union {
+ struct {
+ struct dasd DASD; /* 16: DASD limit/usage info */
+
+ u8 flag; /* 1: */
+ u8 nextindex; /* 1: next free entry in stbl */
+ s8 freecnt; /* 1: free count */
+ s8 freelist; /* 1: freelist header */
+
+ u32 idotdot; /* 4: parent inode number */
+
+ s8 stbl[8]; /* 8: sorted entry index table */
+ } header; /* (32) */
+
+ struct dtslot slot[9];
+} dtroot_t;
+
+#define PARENT(IP) \
+ (le32_to_cpu(JFS_IP(IP)->i_dtroot.header.idotdot))
+
+#define DTROOTMAXSLOT 9
+
+#define dtEmpty(IP) (JFS_IP(IP)->i_dtroot.header.nextindex == 0)
+
+
+/*
+ * directory regular page:
+ *
+ * entry slot array of 32 byte slot
+ *
+ * sorted entry slot index table (stbl):
+ * contiguous slots at slot specified by stblindex,
+ * 1-byte per entry
+ * 512 byte block: 16 entry tbl (1 slot)
+ * 1024 byte block: 32 entry tbl (1 slot)
+ * 2048 byte block: 64 entry tbl (2 slot)
+ * 4096 byte block: 128 entry tbl (4 slot)
+ *
+ * data area:
+ * 512 byte block: 16 - 2 = 14 slot
+ * 1024 byte block: 32 - 2 = 30 slot
+ * 2048 byte block: 64 - 3 = 61 slot
+ * 4096 byte block: 128 - 5 = 123 slot
+ *
+ * N.B. index is 0-based; index fields refer to slot index
+ * except nextindex which refers to entry index in stbl;
+ * end of entry stot list or freelist is marked with -1.
+ */
+typedef union {
+ struct {
+ s64 next; /* 8: next sibling */
+ s64 prev; /* 8: previous sibling */
+
+ u8 flag; /* 1: */
+ u8 nextindex; /* 1: next entry index in stbl */
+ s8 freecnt; /* 1: */
+ s8 freelist; /* 1: slot index of head of freelist */
+
+ u8 maxslot; /* 1: number of slots in page slot[] */
+ u8 stblindex; /* 1: slot index of start of stbl */
+ u8 rsrvd[2]; /* 2: */
+
+ pxd_t self; /* 8: self pxd */
+ } header; /* (32) */
+
+ struct dtslot slot[128];
+} dtpage_t;
+
+#define DTPAGEMAXSLOT 128
+
+#define DT8THPGNODEBYTES 512
+#define DT8THPGNODETSLOTS 1
+#define DT8THPGNODESLOTS 16
+
+#define DTQTRPGNODEBYTES 1024
+#define DTQTRPGNODETSLOTS 1
+#define DTQTRPGNODESLOTS 32
+
+#define DTHALFPGNODEBYTES 2048
+#define DTHALFPGNODETSLOTS 2
+#define DTHALFPGNODESLOTS 64
+
+#define DTFULLPGNODEBYTES 4096
+#define DTFULLPGNODETSLOTS 4
+#define DTFULLPGNODESLOTS 128
+
+#define DTENTRYSTART 1
+
+/* get sorted entry table of the page */
+#define DT_GETSTBL(p) ( ((p)->header.flag & BT_ROOT) ?\
+ ((dtroot_t *)(p))->header.stbl : \
+ (s8 *)&(p)->slot[(p)->header.stblindex] )
+
+/*
+ * Flags for dtSearch
+ */
+#define JFS_CREATE 1
+#define JFS_LOOKUP 2
+#define JFS_REMOVE 3
+#define JFS_RENAME 4
+
+#define DIRENTSIZ(namlen) \
+ ( (sizeof(struct dirent) - 2*(JFS_NAME_MAX+1) + 2*((namlen)+1) + 3) &~ 3 )
+
+/*
+ * Maximum file offset for directories.
+ */
+#define DIREND INT_MAX
+
+/*
+ * external declarations
+ */
+extern void dtInitRoot(tid_t tid, struct inode *ip, u32 idotdot);
+
+extern int dtSearch(struct inode *ip, struct component_name * key,
+ ino_t * data, struct btstack * btstack, int flag);
+
+extern int dtInsert(tid_t tid, struct inode *ip, struct component_name * key,
+ ino_t * ino, struct btstack * btstack);
+
+extern int dtDelete(tid_t tid, struct inode *ip, struct component_name * key,
+ ino_t * data, int flag);
+
+extern int dtModify(tid_t tid, struct inode *ip, struct component_name * key,
+ ino_t * orig_ino, ino_t new_ino, int flag);
+
+extern int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir);
+
+#ifdef _JFS_DEBUG_DTREE
+extern int dtDisplayTree(struct inode *ip);
+
+extern int dtDisplayPage(struct inode *ip, s64 bn, dtpage_t * p);
+#endif /* _JFS_DEBUG_DTREE */
+
+#endif /* !_H_JFS_DTREE */
diff --git a/fs/jfs/jfs_extent.c b/fs/jfs/jfs_extent.c
new file mode 100644
index 00000000000000..c68920f858b277
--- /dev/null
+++ b/fs/jfs/jfs_extent.c
@@ -0,0 +1,657 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2000-2003
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/fs.h>
+#include "jfs_incore.h"
+#include "jfs_superblock.h"
+#include "jfs_dmap.h"
+#include "jfs_extent.h"
+#include "jfs_debug.h"
+
+/*
+ * forward references
+ */
+static int extBalloc(struct inode *, s64, s64 *, s64 *);
+#ifdef _NOTYET
+static int extBrealloc(struct inode *, s64, s64, s64 *, s64 *);
+#endif
+static s64 extRoundDown(s64 nb);
+
+/*
+ * external references
+ */
+extern int jfs_commit_inode(struct inode *, int);
+
+
+#define DPD(a) (printk("(a): %d\n",(a)))
+#define DPC(a) (printk("(a): %c\n",(a)))
+#define DPL1(a) \
+{ \
+ if ((a) >> 32) \
+ printk("(a): %x%08x ",(a)); \
+ else \
+ printk("(a): %x ",(a) << 32); \
+}
+#define DPL(a) \
+{ \
+ if ((a) >> 32) \
+ printk("(a): %x%08x\n",(a)); \
+ else \
+ printk("(a): %x\n",(a) << 32); \
+}
+
+#define DPD1(a) (printk("(a): %d ",(a)))
+#define DPX(a) (printk("(a): %08x\n",(a)))
+#define DPX1(a) (printk("(a): %08x ",(a)))
+#define DPS(a) (printk("%s\n",(a)))
+#define DPE(a) (printk("\nENTERING: %s\n",(a)))
+#define DPE1(a) (printk("\nENTERING: %s",(a)))
+#define DPS1(a) (printk(" %s ",(a)))
+
+
+/*
+ * NAME: extAlloc()
+ *
+ * FUNCTION: allocate an extent for a specified page range within a
+ * file.
+ *
+ * PARAMETERS:
+ * ip - the inode of the file.
+ * xlen - requested extent length.
+ * pno - the starting page number with the file.
+ * xp - pointer to an xad. on entry, xad describes an
+ * extent that is used as an allocation hint if the
+ * xaddr of the xad is non-zero. on successful exit,
+ * the xad describes the newly allocated extent.
+ * abnr - boolean_t indicating whether the newly allocated extent
+ * should be marked as allocated but not recorded.
+ *
+ * RETURN VALUES:
+ * 0 - success
+ * -EIO - i/o error.
+ * -ENOSPC - insufficient disk resources.
+ */
+int
+extAlloc(struct inode *ip, s64 xlen, s64 pno, xad_t * xp, boolean_t abnr)
+{
+ struct jfs_sb_info *sbi = JFS_SBI(ip->i_sb);
+ s64 nxlen, nxaddr, xoff, hint, xaddr = 0;
+ int rc;
+ int xflag;
+
+ /* This blocks if we are low on resources */
+ txBeginAnon(ip->i_sb);
+
+ /* Avoid race with jfs_commit_inode() */
+ down(&JFS_IP(ip)->commit_sem);
+
+ /* validate extent length */
+ if (xlen > MAXXLEN)
+ xlen = MAXXLEN;
+
+ /* get the page's starting extent offset */
+ xoff = pno << sbi->l2nbperpage;
+
+ /* check if an allocation hint was provided */
+ if ((hint = addressXAD(xp))) {
+ /* get the size of the extent described by the hint */
+ nxlen = lengthXAD(xp);
+
+ /* check if the hint is for the portion of the file
+ * immediately previous to the current allocation
+ * request and if hint extent has the same abnr
+ * value as the current request. if so, we can
+ * extend the hint extent to include the current
+ * extent if we can allocate the blocks immediately
+ * following the hint extent.
+ */
+ if (offsetXAD(xp) + nxlen == xoff &&
+ abnr == ((xp->flag & XAD_NOTRECORDED) ? TRUE : FALSE))
+ xaddr = hint + nxlen;
+
+ /* adjust the hint to the last block of the extent */
+ hint += (nxlen - 1);
+ }
+
+ /* allocate the disk blocks for the extent. initially, extBalloc()
+ * will try to allocate disk blocks for the requested size (xlen).
+ * if this fails (xlen contigious free blocks not avaliable), it'll
+ * try to allocate a smaller number of blocks (producing a smaller
+ * extent), with this smaller number of blocks consisting of the
+ * requested number of blocks rounded down to the next smaller
+ * power of 2 number (i.e. 16 -> 8). it'll continue to round down
+ * and retry the allocation until the number of blocks to allocate
+ * is smaller than the number of blocks per page.
+ */
+ nxlen = xlen;
+ if ((rc = extBalloc(ip, hint ? hint : INOHINT(ip), &nxlen, &nxaddr))) {
+ up(&JFS_IP(ip)->commit_sem);
+ return (rc);
+ }
+
+ /* determine the value of the extent flag */
+ xflag = (abnr == TRUE) ? XAD_NOTRECORDED : 0;
+
+ /* if we can extend the hint extent to cover the current request,
+ * extend it. otherwise, insert a new extent to
+ * cover the current request.
+ */
+ if (xaddr && xaddr == nxaddr)
+ rc = xtExtend(0, ip, xoff, (int) nxlen, 0);
+ else
+ rc = xtInsert(0, ip, xflag, xoff, (int) nxlen, &nxaddr, 0);
+
+ /* if the extend or insert failed,
+ * free the newly allocated blocks and return the error.
+ */
+ if (rc) {
+ dbFree(ip, nxaddr, nxlen);
+ up(&JFS_IP(ip)->commit_sem);
+ return (rc);
+ }
+
+ /* update the number of blocks allocated to the file */
+ ip->i_blocks += LBLK2PBLK(ip->i_sb, nxlen);
+
+ /* set the results of the extent allocation */
+ XADaddress(xp, nxaddr);
+ XADlength(xp, nxlen);
+ XADoffset(xp, xoff);
+ xp->flag = xflag;
+
+ mark_inode_dirty(ip);
+ set_cflag(COMMIT_Syncdata, ip);
+
+ up(&JFS_IP(ip)->commit_sem);
+ /*
+ * COMMIT_SyncList flags an anonymous tlock on page that is on
+ * sync list.
+ * We need to commit the inode to get the page written disk.
+ */
+ if (test_and_clear_cflag(COMMIT_Synclist,ip))
+ jfs_commit_inode(ip, 0);
+
+ return (0);
+}
+
+
+#ifdef _NOTYET
+/*
+ * NAME: extRealloc()
+ *
+ * FUNCTION: extend the allocation of a file extent containing a
+ * partial back last page.
+ *
+ * PARAMETERS:
+ * ip - the inode of the file.
+ * cp - cbuf for the partial backed last page.
+ * xlen - request size of the resulting extent.
+ * xp - pointer to an xad. on successful exit, the xad
+ * describes the newly allocated extent.
+ * abnr - boolean_t indicating whether the newly allocated extent
+ * should be marked as allocated but not recorded.
+ *
+ * RETURN VALUES:
+ * 0 - success
+ * -EIO - i/o error.
+ * -ENOSPC - insufficient disk resources.
+ */
+int extRealloc(struct inode *ip, s64 nxlen, xad_t * xp, boolean_t abnr)
+{
+ struct super_block *sb = ip->i_sb;
+ s64 xaddr, xlen, nxaddr, delta, xoff;
+ s64 ntail, nextend, ninsert;
+ int rc, nbperpage = JFS_SBI(sb)->nbperpage;
+ int xflag;
+
+ /* This blocks if we are low on resources */
+ txBeginAnon(ip->i_sb);
+
+ down(&JFS_IP(ip)->commit_sem);
+ /* validate extent length */
+ if (nxlen > MAXXLEN)
+ nxlen = MAXXLEN;
+
+ /* get the extend (partial) page's disk block address and
+ * number of blocks.
+ */
+ xaddr = addressXAD(xp);
+ xlen = lengthXAD(xp);
+ xoff = offsetXAD(xp);
+
+ /* if the extend page is abnr and if the request is for
+ * the extent to be allocated and recorded,
+ * make the page allocated and recorded.
+ */
+ if ((xp->flag & XAD_NOTRECORDED) && !abnr) {
+ xp->flag = 0;
+ if ((rc = xtUpdate(0, ip, xp)))
+ goto exit;
+ }
+
+ /* try to allocated the request number of blocks for the
+ * extent. dbRealloc() first tries to satisfy the request
+ * by extending the allocation in place. otherwise, it will
+ * try to allocate a new set of blocks large enough for the
+ * request. in satisfying a request, dbReAlloc() may allocate
+ * less than what was request but will always allocate enough
+ * space as to satisfy the extend page.
+ */
+ if ((rc = extBrealloc(ip, xaddr, xlen, &nxlen, &nxaddr)))
+ goto exit;
+
+ delta = nxlen - xlen;
+
+ /* check if the extend page is not abnr but the request is abnr
+ * and the allocated disk space is for more than one page. if this
+ * is the case, there is a miss match of abnr between the extend page
+ * and the one or more pages following the extend page. as a result,
+ * two extents will have to be manipulated. the first will be that
+ * of the extent of the extend page and will be manipulated thru
+ * an xtExtend() or an xtTailgate(), depending upon whether the
+ * disk allocation occurred as an inplace extension. the second
+ * extent will be manipulated (created) through an xtInsert() and
+ * will be for the pages following the extend page.
+ */
+ if (abnr && (!(xp->flag & XAD_NOTRECORDED)) && (nxlen > nbperpage)) {
+ ntail = nbperpage;
+ nextend = ntail - xlen;
+ ninsert = nxlen - nbperpage;
+
+ xflag = XAD_NOTRECORDED;
+ } else {
+ ntail = nxlen;
+ nextend = delta;
+ ninsert = 0;
+
+ xflag = xp->flag;
+ }
+
+ /* if we were able to extend the disk allocation in place,
+ * extend the extent. otherwise, move the extent to a
+ * new disk location.
+ */
+ if (xaddr == nxaddr) {
+ /* extend the extent */
+ if ((rc = xtExtend(0, ip, xoff + xlen, (int) nextend, 0))) {
+ dbFree(ip, xaddr + xlen, delta);
+ goto exit;
+ }
+ } else {
+ /*
+ * move the extent to a new location:
+ *
+ * xtTailgate() accounts for relocated tail extent;
+ */
+ if ((rc = xtTailgate(0, ip, xoff, (int) ntail, nxaddr, 0))) {
+ dbFree(ip, nxaddr, nxlen);
+ goto exit;
+ }
+ }
+
+
+ /* check if we need to also insert a new extent */
+ if (ninsert) {
+ /* perform the insert. if it fails, free the blocks
+ * to be inserted and make it appear that we only did
+ * the xtExtend() or xtTailgate() above.
+ */
+ xaddr = nxaddr + ntail;
+ if (xtInsert (0, ip, xflag, xoff + ntail, (int) ninsert,
+ &xaddr, 0)) {
+ dbFree(ip, xaddr, (s64) ninsert);
+ delta = nextend;
+ nxlen = ntail;
+ xflag = 0;
+ }
+ }
+
+ /* update the inode with the number of blocks allocated */
+ ip->i_blocks += LBLK2PBLK(sb, delta);
+
+ /* set the return results */
+ XADaddress(xp, nxaddr);
+ XADlength(xp, nxlen);
+ XADoffset(xp, xoff);
+ xp->flag = xflag;
+
+ mark_inode_dirty(ip);
+exit:
+ up(&JFS_IP(ip)->commit_sem);
+ return (rc);
+}
+#endif /* _NOTYET */
+
+
+/*
+ * NAME: extHint()
+ *
+ * FUNCTION: produce an extent allocation hint for a file offset.
+ *
+ * PARAMETERS:
+ * ip - the inode of the file.
+ * offset - file offset for which the hint is needed.
+ * xp - pointer to the xad that is to be filled in with
+ * the hint.
+ *
+ * RETURN VALUES:
+ * 0 - success
+ * -EIO - i/o error.
+ */
+int extHint(struct inode *ip, s64 offset, xad_t * xp)
+{
+ struct super_block *sb = ip->i_sb;
+ struct xadlist xadl;
+ struct lxdlist lxdl;
+ lxd_t lxd;
+ s64 prev;
+ int rc, nbperpage = JFS_SBI(sb)->nbperpage;
+
+ /* init the hint as "no hint provided" */
+ XADaddress(xp, 0);
+
+ /* determine the starting extent offset of the page previous
+ * to the page containing the offset.
+ */
+ prev = ((offset & ~POFFSET) >> JFS_SBI(sb)->l2bsize) - nbperpage;
+
+ /* if the offsets in the first page of the file,
+ * no hint provided.
+ */
+ if (prev < 0)
+ return (0);
+
+ /* prepare to lookup the previous page's extent info */
+ lxdl.maxnlxd = 1;
+ lxdl.nlxd = 1;
+ lxdl.lxd = &lxd;
+ LXDoffset(&lxd, prev)
+ LXDlength(&lxd, nbperpage);
+
+ xadl.maxnxad = 1;
+ xadl.nxad = 0;
+ xadl.xad = xp;
+
+ /* perform the lookup */
+ if ((rc = xtLookupList(ip, &lxdl, &xadl, 0)))
+ return (rc);
+
+ /* check if not extent exists for the previous page.
+ * this is possible for sparse files.
+ */
+ if (xadl.nxad == 0) {
+// assert(ISSPARSE(ip));
+ return (0);
+ }
+
+ /* only preserve the abnr flag within the xad flags
+ * of the returned hint.
+ */
+ xp->flag &= XAD_NOTRECORDED;
+
+ if(xadl.nxad != 1 || lengthXAD(xp) != nbperpage) {
+ jfs_error(ip->i_sb, "extHint: corrupt xtree");
+ return -EIO;
+ }
+
+ return (0);
+}
+
+
+/*
+ * NAME: extRecord()
+ *
+ * FUNCTION: change a page with a file from not recorded to recorded.
+ *
+ * PARAMETERS:
+ * ip - inode of the file.
+ * cp - cbuf of the file page.
+ *
+ * RETURN VALUES:
+ * 0 - success
+ * -EIO - i/o error.
+ * -ENOSPC - insufficient disk resources.
+ */
+int extRecord(struct inode *ip, xad_t * xp)
+{
+ int rc;
+
+ txBeginAnon(ip->i_sb);
+
+ down(&JFS_IP(ip)->commit_sem);
+
+ /* update the extent */
+ rc = xtUpdate(0, ip, xp);
+
+ up(&JFS_IP(ip)->commit_sem);
+ return rc;
+}
+
+
+#ifdef _NOTYET
+/*
+ * NAME: extFill()
+ *
+ * FUNCTION: allocate disk space for a file page that represents
+ * a file hole.
+ *
+ * PARAMETERS:
+ * ip - the inode of the file.
+ * cp - cbuf of the file page represent the hole.
+ *
+ * RETURN VALUES:
+ * 0 - success
+ * -EIO - i/o error.
+ * -ENOSPC - insufficient disk resources.
+ */
+int extFill(struct inode *ip, xad_t * xp)
+{
+ int rc, nbperpage = JFS_SBI(ip->i_sb)->nbperpage;
+ s64 blkno = offsetXAD(xp) >> ip->i_blksize;
+
+// assert(ISSPARSE(ip));
+
+ /* initialize the extent allocation hint */
+ XADaddress(xp, 0);
+
+ /* allocate an extent to fill the hole */
+ if ((rc = extAlloc(ip, nbperpage, blkno, xp, FALSE)))
+ return (rc);
+
+ assert(lengthPXD(xp) == nbperpage);
+
+ return (0);
+}
+#endif /* _NOTYET */
+
+
+/*
+ * NAME: extBalloc()
+ *
+ * FUNCTION: allocate disk blocks to form an extent.
+ *
+ * initially, we will try to allocate disk blocks for the
+ * requested size (nblocks). if this fails (nblocks
+ * contigious free blocks not avaliable), we'll try to allocate
+ * a smaller number of blocks (producing a smaller extent), with
+ * this smaller number of blocks consisting of the requested
+ * number of blocks rounded down to the next smaller power of 2
+ * number (i.e. 16 -> 8). we'll continue to round down and
+ * retry the allocation until the number of blocks to allocate
+ * is smaller than the number of blocks per page.
+ *
+ * PARAMETERS:
+ * ip - the inode of the file.
+ * hint - disk block number to be used as an allocation hint.
+ * *nblocks - pointer to an s64 value. on entry, this value specifies
+ * the desired number of block to be allocated. on successful
+ * exit, this value is set to the number of blocks actually
+ * allocated.
+ * blkno - pointer to a block address that is filled in on successful
+ * return with the starting block number of the newly
+ * allocated block range.
+ *
+ * RETURN VALUES:
+ * 0 - success
+ * -EIO - i/o error.
+ * -ENOSPC - insufficient disk resources.
+ */
+static int
+extBalloc(struct inode *ip, s64 hint, s64 * nblocks, s64 * blkno)
+{
+ struct jfs_inode_info *ji = JFS_IP(ip);
+ struct jfs_sb_info *sbi = JFS_SBI(ip->i_sb);
+ s64 nb, nblks, daddr, max;
+ int rc, nbperpage = sbi->nbperpage;
+ struct bmap *bmp = sbi->bmap;
+ int ag;
+
+ /* get the number of blocks to initially attempt to allocate.
+ * we'll first try the number of blocks requested unless this
+ * number is greater than the maximum number of contigious free
+ * blocks in the map. in that case, we'll start off with the
+ * maximum free.
+ */
+ max = (s64) 1 << bmp->db_maxfreebud;
+ if (*nblocks >= max && *nblocks > nbperpage)
+ nb = nblks = (max > nbperpage) ? max : nbperpage;
+ else
+ nb = nblks = *nblocks;
+
+ /* try to allocate blocks */
+ while ((rc = dbAlloc(ip, hint, nb, &daddr))) {
+ /* if something other than an out of space error,
+ * stop and return this error.
+ */
+ if (rc != -ENOSPC)
+ return (rc);
+
+ /* decrease the allocation request size */
+ nb = min(nblks, extRoundDown(nb));
+
+ /* give up if we cannot cover a page */
+ if (nb < nbperpage)
+ return (rc);
+ }
+
+ *nblocks = nb;
+ *blkno = daddr;
+
+ if (S_ISREG(ip->i_mode) && (ji->fileset == FILESYSTEM_I)) {
+ ag = BLKTOAG(daddr, sbi);
+ spin_lock_irq(&ji->ag_lock);
+ if (ji->active_ag == -1) {
+ atomic_inc(&bmp->db_active[ag]);
+ ji->active_ag = ag;
+ } else if (ji->active_ag != ag) {
+ atomic_dec(&bmp->db_active[ji->active_ag]);
+ atomic_inc(&bmp->db_active[ag]);
+ ji->active_ag = ag;
+ }
+ spin_unlock_irq(&ji->ag_lock);
+ }
+
+ return (0);
+}
+
+
+#ifdef _NOTYET
+/*
+ * NAME: extBrealloc()
+ *
+ * FUNCTION: attempt to extend an extent's allocation.
+ *
+ * initially, we will try to extend the extent's allocation
+ * in place. if this fails, we'll try to move the extent
+ * to a new set of blocks. if moving the extent, we initially
+ * will try to allocate disk blocks for the requested size
+ * (nnew). if this fails (nnew contigious free blocks not
+ * avaliable), we'll try to allocate a smaller number of
+ * blocks (producing a smaller extent), with this smaller
+ * number of blocks consisting of the requested number of
+ * blocks rounded down to the next smaller power of 2
+ * number (i.e. 16 -> 8). we'll continue to round down and
+ * retry the allocation until the number of blocks to allocate
+ * is smaller than the number of blocks per page.
+ *
+ * PARAMETERS:
+ * ip - the inode of the file.
+ * blkno - starting block number of the extents current allocation.
+ * nblks - number of blocks within the extents current allocation.
+ * newnblks - pointer to a s64 value. on entry, this value is the
+ * the new desired extent size (number of blocks). on
+ * successful exit, this value is set to the extent's actual
+ * new size (new number of blocks).
+ * newblkno - the starting block number of the extents new allocation.
+ *
+ * RETURN VALUES:
+ * 0 - success
+ * -EIO - i/o error.
+ * -ENOSPC - insufficient disk resources.
+ */
+static int
+extBrealloc(struct inode *ip,
+ s64 blkno, s64 nblks, s64 * newnblks, s64 * newblkno)
+{
+ int rc;
+
+ /* try to extend in place */
+ if ((rc = dbExtend(ip, blkno, nblks, *newnblks - nblks)) == 0) {
+ *newblkno = blkno;
+ return (0);
+ } else {
+ if (rc != -ENOSPC)
+ return (rc);
+ }
+
+ /* in place extension not possible.
+ * try to move the extent to a new set of blocks.
+ */
+ return (extBalloc(ip, blkno, newnblks, newblkno));
+}
+#endif /* _NOTYET */
+
+
+/*
+ * NAME: extRoundDown()
+ *
+ * FUNCTION: round down a specified number of blocks to the next
+ * smallest power of 2 number.
+ *
+ * PARAMETERS:
+ * nb - the inode of the file.
+ *
+ * RETURN VALUES:
+ * next smallest power of 2 number.
+ */
+static s64 extRoundDown(s64 nb)
+{
+ int i;
+ u64 m, k;
+
+ for (i = 0, m = (u64) 1 << 63; i < 64; i++, m >>= 1) {
+ if (m & nb)
+ break;
+ }
+
+ i = 63 - i;
+ k = (u64) 1 << i;
+ k = ((k - 1) & nb) ? k : k >> 1;
+
+ return (k);
+}
diff --git a/fs/jfs/jfs_extent.h b/fs/jfs/jfs_extent.h
new file mode 100644
index 00000000000000..e80fc7ced87d96
--- /dev/null
+++ b/fs/jfs/jfs_extent.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2000-2001
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _H_JFS_EXTENT
+#define _H_JFS_EXTENT
+
+/* get block allocation allocation hint as location of disk inode */
+#define INOHINT(ip) \
+ (addressPXD(&(JFS_IP(ip)->ixpxd)) + lengthPXD(&(JFS_IP(ip)->ixpxd)) - 1)
+
+extern int extAlloc(struct inode *, s64, s64, xad_t *, boolean_t);
+extern int extFill(struct inode *, xad_t *);
+extern int extHint(struct inode *, s64, xad_t *);
+extern int extRealloc(struct inode *, s64, xad_t *, boolean_t);
+extern int extRecord(struct inode *, xad_t *);
+
+#endif /* _H_JFS_EXTENT */
diff --git a/fs/jfs/jfs_filsys.h b/fs/jfs/jfs_filsys.h
new file mode 100644
index 00000000000000..75d9f6788913ef
--- /dev/null
+++ b/fs/jfs/jfs_filsys.h
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2000-2003
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _H_JFS_FILSYS
+#define _H_JFS_FILSYS
+
+/*
+ * jfs_filsys.h
+ *
+ * file system (implementation-dependent) constants
+ *
+ * refer to <limits.h> for system wide implementation-dependent constants
+ */
+
+/*
+ * file system option (superblock flag)
+ */
+/* mount time flag to disable journaling to disk */
+#define JFS_NOINTEGRITY 0x00000010
+
+/* mount time flags for error handling */
+#define JFS_ERR_REMOUNT_RO 0x00000002 /* remount read-only */
+#define JFS_ERR_CONTINUE 0x00000004 /* continue */
+#define JFS_ERR_PANIC 0x00000008 /* panic */
+
+/* platform option (conditional compilation) */
+#define JFS_AIX 0x80000000 /* AIX support */
+/* POSIX name/directory support */
+
+#define JFS_OS2 0x40000000 /* OS/2 support */
+/* case-insensitive name/directory support */
+
+#define JFS_DFS 0x20000000 /* DCE DFS LFS support */
+
+#define JFS_LINUX 0x10000000 /* Linux support */
+/* case-sensitive name/directory support */
+
+/* directory option */
+#define JFS_UNICODE 0x00000001 /* unicode name */
+
+/* commit option */
+#define JFS_COMMIT 0x00000f00 /* commit option mask */
+#define JFS_GROUPCOMMIT 0x00000100 /* group (of 1) commit */
+#define JFS_LAZYCOMMIT 0x00000200 /* lazy commit */
+#define JFS_TMPFS 0x00000400 /* temporary file system -
+ * do not log/commit:
+ */
+
+/* log logical volume option */
+#define JFS_INLINELOG 0x00000800 /* inline log within file system */
+#define JFS_INLINEMOVE 0x00001000 /* inline log being moved */
+
+/* Secondary aggregate inode table */
+#define JFS_BAD_SAIT 0x00010000 /* current secondary ait is bad */
+
+/* sparse regular file support */
+#define JFS_SPARSE 0x00020000 /* sparse regular file */
+
+/* DASD Limits F226941 */
+#define JFS_DASD_ENABLED 0x00040000 /* DASD limits enabled */
+#define JFS_DASD_PRIME 0x00080000 /* Prime DASD usage on boot */
+
+/* big endian flag */
+#define JFS_SWAP_BYTES 0x00100000 /* running on big endian computer */
+
+/* Directory index */
+#define JFS_DIR_INDEX 0x00200000 /* Persistant index for */
+ /* directory entries */
+
+
+/*
+ * buffer cache configuration
+ */
+/* page size */
+#ifdef PSIZE
+#undef PSIZE
+#endif
+#define PSIZE 4096 /* page size (in byte) */
+#define L2PSIZE 12 /* log2(PSIZE) */
+#define POFFSET 4095 /* offset within page */
+
+/* buffer page size */
+#define BPSIZE PSIZE
+
+/*
+ * fs fundamental size
+ *
+ * PSIZE >= file system block size >= PBSIZE >= DISIZE
+ */
+#define PBSIZE 512 /* physical block size (in byte) */
+#define L2PBSIZE 9 /* log2(PBSIZE) */
+
+#define DISIZE 512 /* on-disk inode size (in byte) */
+#define L2DISIZE 9 /* log2(DISIZE) */
+
+#define IDATASIZE 256 /* inode inline data size */
+#define IXATTRSIZE 128 /* inode inline extended attribute size */
+
+#define XTPAGE_SIZE 4096
+#define log2_PAGESIZE 12
+
+#define IAG_SIZE 4096
+#define IAG_EXTENT_SIZE 4096
+#define INOSPERIAG 4096 /* number of disk inodes per iag */
+#define L2INOSPERIAG 12 /* l2 number of disk inodes per iag */
+#define INOSPEREXT 32 /* number of disk inode per extent */
+#define L2INOSPEREXT 5 /* l2 number of disk inode per extent */
+#define IXSIZE (DISIZE * INOSPEREXT) /* inode extent size */
+#define INOSPERPAGE 8 /* number of disk inodes per 4K page */
+#define L2INOSPERPAGE 3 /* log2(INOSPERPAGE) */
+
+#define IAGFREELIST_LWM 64
+
+#define INODE_EXTENT_SIZE IXSIZE /* inode extent size */
+#define NUM_INODE_PER_EXTENT INOSPEREXT
+#define NUM_INODE_PER_IAG INOSPERIAG
+
+#define MINBLOCKSIZE 512
+#define MAXBLOCKSIZE 4096
+#define MAXFILESIZE ((s64)1 << 52)
+
+#define JFS_LINK_MAX 65535 /* nlink_t is unsigned short */
+
+/* Minimum number of bytes supported for a JFS partition */
+#define MINJFS (0x1000000)
+#define MINJFSTEXT "16"
+
+/*
+ * file system block size -> physical block size
+ */
+#define LBOFFSET(x) ((x) & (PBSIZE - 1))
+#define LBNUMBER(x) ((x) >> L2PBSIZE)
+#define LBLK2PBLK(sb,b) ((b) << (sb->s_blocksize_bits - L2PBSIZE))
+#define PBLK2LBLK(sb,b) ((b) >> (sb->s_blocksize_bits - L2PBSIZE))
+/* size in byte -> last page number */
+#define SIZE2PN(size) ( ((s64)((size) - 1)) >> (L2PSIZE) )
+/* size in byte -> last file system block number */
+#define SIZE2BN(size, l2bsize) ( ((s64)((size) - 1)) >> (l2bsize) )
+
+/*
+ * fixed physical block address (physical block size = 512 byte)
+ *
+ * NOTE: since we can't guarantee a physical block size of 512 bytes the use of
+ * these macros should be removed and the byte offset macros used instead.
+ */
+#define SUPER1_B 64 /* primary superblock */
+#define AIMAP_B (SUPER1_B + 8) /* 1st extent of aggregate inode map */
+#define AITBL_B (AIMAP_B + 16) /*
+ * 1st extent of aggregate inode table
+ */
+#define SUPER2_B (AITBL_B + 32) /* 2ndary superblock pbn */
+#define BMAP_B (SUPER2_B + 8) /* block allocation map */
+
+/*
+ * SIZE_OF_SUPER defines the total amount of space reserved on disk for the
+ * superblock. This is not the same as the superblock structure, since all of
+ * this space is not currently being used.
+ */
+#define SIZE_OF_SUPER PSIZE
+
+/*
+ * SIZE_OF_AG_TABLE defines the amount of space reserved to hold the AG table
+ */
+#define SIZE_OF_AG_TABLE PSIZE
+
+/*
+ * SIZE_OF_MAP_PAGE defines the amount of disk space reserved for each page of
+ * the inode allocation map (to hold iag)
+ */
+#define SIZE_OF_MAP_PAGE PSIZE
+
+/*
+ * fixed byte offset address
+ */
+#define SUPER1_OFF 0x8000 /* primary superblock */
+#define AIMAP_OFF (SUPER1_OFF + SIZE_OF_SUPER)
+ /*
+ * Control page of aggregate inode map
+ * followed by 1st extent of map
+ */
+#define AITBL_OFF (AIMAP_OFF + (SIZE_OF_MAP_PAGE << 1))
+ /*
+ * 1st extent of aggregate inode table
+ */
+#define SUPER2_OFF (AITBL_OFF + INODE_EXTENT_SIZE)
+ /*
+ * secondary superblock
+ */
+#define BMAP_OFF (SUPER2_OFF + SIZE_OF_SUPER)
+ /*
+ * block allocation map
+ */
+
+/*
+ * The following macro is used to indicate the number of reserved disk blocks at
+ * the front of an aggregate, in terms of physical blocks. This value is
+ * currently defined to be 32K. This turns out to be the same as the primary
+ * superblock's address, since it directly follows the reserved blocks.
+ */
+#define AGGR_RSVD_BLOCKS SUPER1_B
+
+/*
+ * The following macro is used to indicate the number of reserved bytes at the
+ * front of an aggregate. This value is currently defined to be 32K. This
+ * turns out to be the same as the primary superblock's byte offset, since it
+ * directly follows the reserved blocks.
+ */
+#define AGGR_RSVD_BYTES SUPER1_OFF
+
+/*
+ * The following macro defines the byte offset for the first inode extent in
+ * the aggregate inode table. This allows us to find the self inode to find the
+ * rest of the table. Currently this value is 44K.
+ */
+#define AGGR_INODE_TABLE_START AITBL_OFF
+
+/*
+ * fixed reserved inode number
+ */
+/* aggregate inode */
+#define AGGR_RESERVED_I 0 /* aggregate inode (reserved) */
+#define AGGREGATE_I 1 /* aggregate inode map inode */
+#define BMAP_I 2 /* aggregate block allocation map inode */
+#define LOG_I 3 /* aggregate inline log inode */
+#define BADBLOCK_I 4 /* aggregate bad block inode */
+#define FILESYSTEM_I 16 /* 1st/only fileset inode in ait:
+ * fileset inode map inode
+ */
+
+/* per fileset inode */
+#define FILESET_RSVD_I 0 /* fileset inode (reserved) */
+#define FILESET_EXT_I 1 /* fileset inode extension */
+#define ROOT_I 2 /* fileset root inode */
+#define ACL_I 3 /* fileset ACL inode */
+
+#define FILESET_OBJECT_I 4 /* the first fileset inode available for a file
+ * or directory or link...
+ */
+#define FIRST_FILESET_INO 16 /* the first aggregate inode which describes
+ * an inode. (To fsck this is also the first
+ * inode in part 2 of the agg inode table.)
+ */
+
+/*
+ * directory configuration
+ */
+#define JFS_NAME_MAX 255
+#define JFS_PATH_MAX BPSIZE
+
+
+/*
+ * file system state (superblock state)
+ */
+#define FM_CLEAN 0x00000000 /* file system is unmounted and clean */
+#define FM_MOUNT 0x00000001 /* file system is mounted cleanly */
+#define FM_DIRTY 0x00000002 /* file system was not unmounted and clean
+ * when mounted or
+ * commit failure occurred while being mounted:
+ * fsck() must be run to repair
+ */
+#define FM_LOGREDO 0x00000004 /* log based recovery (logredo()) failed:
+ * fsck() must be run to repair
+ */
+#define FM_EXTENDFS 0x00000008 /* file system extendfs() in progress */
+
+#endif /* _H_JFS_FILSYS */
diff --git a/fs/jfs/jfs_imap.c b/fs/jfs/jfs_imap.c
new file mode 100644
index 00000000000000..fdb7ddcc690a70
--- /dev/null
+++ b/fs/jfs/jfs_imap.c
@@ -0,0 +1,3279 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2000-2003
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * jfs_imap.c: inode allocation map manager
+ *
+ * Serialization:
+ * Each AG has a simple lock which is used to control the serialization of
+ * the AG level lists. This lock should be taken first whenever an AG
+ * level list will be modified or accessed.
+ *
+ * Each IAG is locked by obtaining the buffer for the IAG page.
+ *
+ * There is also a inode lock for the inode map inode. A read lock needs to
+ * be taken whenever an IAG is read from the map or the global level
+ * information is read. A write lock needs to be taken whenever the global
+ * level information is modified or an atomic operation needs to be used.
+ *
+ * If more than one IAG is read at one time, the read lock may not
+ * be given up until all of the IAG's are read. Otherwise, a deadlock
+ * may occur when trying to obtain the read lock while another thread
+ * holding the read lock is waiting on the IAG already being held.
+ *
+ * The control page of the inode map is read into memory by diMount().
+ * Thereafter it should only be modified in memory and then it will be
+ * written out when the filesystem is unmounted by diUnmount().
+ */
+
+#include <linux/fs.h>
+#include <linux/locks.h>
+#include "jfs_incore.h"
+#include "jfs_filsys.h"
+#include "jfs_dinode.h"
+#include "jfs_dmap.h"
+#include "jfs_imap.h"
+#include "jfs_metapage.h"
+#include "jfs_superblock.h"
+#include "jfs_debug.h"
+
+/*
+ * imap locks
+ */
+/* iag free list lock */
+#define IAGFREE_LOCK_INIT(imap) init_MUTEX(&imap->im_freelock)
+#define IAGFREE_LOCK(imap) down(&imap->im_freelock)
+#define IAGFREE_UNLOCK(imap) up(&imap->im_freelock)
+
+/* per ag iag list locks */
+#define AG_LOCK_INIT(imap,index) init_MUTEX(&(imap->im_aglock[index]))
+#define AG_LOCK(imap,agno) down(&imap->im_aglock[agno])
+#define AG_UNLOCK(imap,agno) up(&imap->im_aglock[agno])
+
+/*
+ * external references
+ */
+extern struct address_space_operations jfs_aops;
+
+/*
+ * forward references
+ */
+static int diAllocAG(struct inomap *, int, boolean_t, struct inode *);
+static int diAllocAny(struct inomap *, int, boolean_t, struct inode *);
+static int diAllocBit(struct inomap *, struct iag *, int);
+static int diAllocExt(struct inomap *, int, struct inode *);
+static int diAllocIno(struct inomap *, int, struct inode *);
+static int diFindFree(u32, int);
+static int diNewExt(struct inomap *, struct iag *, int);
+static int diNewIAG(struct inomap *, int *, int, struct metapage **);
+static void duplicateIXtree(struct super_block *, s64, int, s64 *);
+
+static int diIAGRead(struct inomap * imap, int, struct metapage **);
+static int copy_from_dinode(struct dinode *, struct inode *);
+static void copy_to_dinode(struct dinode *, struct inode *);
+
+/*
+ * debug code for double-checking inode map
+ */
+/* #define _JFS_DEBUG_IMAP 1 */
+
+#ifdef _JFS_DEBUG_IMAP
+#define DBG_DIINIT(imap) DBGdiInit(imap)
+#define DBG_DIALLOC(imap, ino) DBGdiAlloc(imap, ino)
+#define DBG_DIFREE(imap, ino) DBGdiFree(imap, ino)
+
+static void *DBGdiInit(struct inomap * imap);
+static void DBGdiAlloc(struct inomap * imap, ino_t ino);
+static void DBGdiFree(struct inomap * imap, ino_t ino);
+#else
+#define DBG_DIINIT(imap)
+#define DBG_DIALLOC(imap, ino)
+#define DBG_DIFREE(imap, ino)
+#endif /* _JFS_DEBUG_IMAP */
+
+/*
+ * NAME: diMount()
+ *
+ * FUNCTION: initialize the incore inode map control structures for
+ * a fileset or aggregate init time.
+ *
+ * the inode map's control structure (dinomap) is
+ * brought in from disk and placed in virtual memory.
+ *
+ * PARAMETERS:
+ * ipimap - pointer to inode map inode for the aggregate or fileset.
+ *
+ * RETURN VALUES:
+ * 0 - success
+ * -ENOMEM - insufficient free virtual memory.
+ * -EIO - i/o error.
+ */
+int diMount(struct inode *ipimap)
+{
+ struct inomap *imap;
+ struct metapage *mp;
+ int index;
+ struct dinomap *dinom_le;
+
+ /*
+ * allocate/initialize the in-memory inode map control structure
+ */
+ /* allocate the in-memory inode map control structure. */
+ imap = (struct inomap *) kmalloc(sizeof(struct inomap), GFP_KERNEL);
+ if (imap == NULL) {
+ jfs_err("diMount: kmalloc returned NULL!");
+ return -ENOMEM;
+ }
+
+ /* read the on-disk inode map control structure. */
+
+ mp = read_metapage(ipimap,
+ IMAPBLKNO << JFS_SBI(ipimap->i_sb)->l2nbperpage,
+ PSIZE, 0);
+ if (mp == NULL) {
+ kfree(imap);
+ return -EIO;
+ }
+
+ /* copy the on-disk version to the in-memory version. */
+ dinom_le = (struct dinomap *) mp->data;
+ imap->im_freeiag = le32_to_cpu(dinom_le->in_freeiag);
+ imap->im_nextiag = le32_to_cpu(dinom_le->in_nextiag);
+ atomic_set(&imap->im_numinos, le32_to_cpu(dinom_le->in_numinos));
+ atomic_set(&imap->im_numfree, le32_to_cpu(dinom_le->in_numfree));
+ imap->im_nbperiext = le32_to_cpu(dinom_le->in_nbperiext);
+ imap->im_l2nbperiext = le32_to_cpu(dinom_le->in_l2nbperiext);
+ for (index = 0; index < MAXAG; index++) {
+ imap->im_agctl[index].inofree =
+ le32_to_cpu(dinom_le->in_agctl[index].inofree);
+ imap->im_agctl[index].extfree =
+ le32_to_cpu(dinom_le->in_agctl[index].extfree);
+ imap->im_agctl[index].numinos =
+ le32_to_cpu(dinom_le->in_agctl[index].numinos);
+ imap->im_agctl[index].numfree =
+ le32_to_cpu(dinom_le->in_agctl[index].numfree);
+ }
+
+ /* release the buffer. */
+ release_metapage(mp);
+
+ /*
+ * allocate/initialize inode allocation map locks
+ */
+ /* allocate and init iag free list lock */
+ IAGFREE_LOCK_INIT(imap);
+
+ /* allocate and init ag list locks */
+ for (index = 0; index < MAXAG; index++) {
+ AG_LOCK_INIT(imap, index);
+ }
+
+ /* bind the inode map inode and inode map control structure
+ * to each other.
+ */
+ imap->im_ipimap = ipimap;
+ JFS_IP(ipimap)->i_imap = imap;
+
+// DBG_DIINIT(imap);
+
+ return (0);
+}
+
+
+/*
+ * NAME: diUnmount()
+ *
+ * FUNCTION: write to disk the incore inode map control structures for
+ * a fileset or aggregate at unmount time.
+ *
+ * PARAMETERS:
+ * ipimap - pointer to inode map inode for the aggregate or fileset.
+ *
+ * RETURN VALUES:
+ * 0 - success
+ * -ENOMEM - insufficient free virtual memory.
+ * -EIO - i/o error.
+ */
+int diUnmount(struct inode *ipimap, int mounterror)
+{
+ struct inomap *imap = JFS_IP(ipimap)->i_imap;
+
+ /*
+ * update the on-disk inode map control structure
+ */
+
+ if (!(mounterror || isReadOnly(ipimap)))
+ diSync(ipimap);
+
+ /*
+ * Invalidate the page cache buffers
+ */
+ truncate_inode_pages(ipimap->i_mapping, 0);
+
+ /*
+ * free in-memory control structure
+ */
+ kfree(imap);
+
+ return (0);
+}
+
+
+/*
+ * diSync()
+ */
+int diSync(struct inode *ipimap)
+{
+ struct dinomap *dinom_le;
+ struct inomap *imp = JFS_IP(ipimap)->i_imap;
+ struct metapage *mp;
+ int index;
+
+ /*
+ * write imap global conrol page
+ */
+ /* read the on-disk inode map control structure */
+ mp = get_metapage(ipimap,
+ IMAPBLKNO << JFS_SBI(ipimap->i_sb)->l2nbperpage,
+ PSIZE, 0);
+ if (mp == NULL) {
+ jfs_err("diSync: get_metapage failed!");
+ return -EIO;
+ }
+
+ /* copy the in-memory version to the on-disk version */
+ dinom_le = (struct dinomap *) mp->data;
+ dinom_le->in_freeiag = cpu_to_le32(imp->im_freeiag);
+ dinom_le->in_nextiag = cpu_to_le32(imp->im_nextiag);
+ dinom_le->in_numinos = cpu_to_le32(atomic_read(&imp->im_numinos));
+ dinom_le->in_numfree = cpu_to_le32(atomic_read(&imp->im_numfree));
+ dinom_le->in_nbperiext = cpu_to_le32(imp->im_nbperiext);
+ dinom_le->in_l2nbperiext = cpu_to_le32(imp->im_l2nbperiext);
+ for (index = 0; index < MAXAG; index++) {
+ dinom_le->in_agctl[index].inofree =
+ cpu_to_le32(imp->im_agctl[index].inofree);
+ dinom_le->in_agctl[index].extfree =
+ cpu_to_le32(imp->im_agctl[index].extfree);
+ dinom_le->in_agctl[index].numinos =
+ cpu_to_le32(imp->im_agctl[index].numinos);
+ dinom_le->in_agctl[index].numfree =
+ cpu_to_le32(imp->im_agctl[index].numfree);
+ }
+
+ /* write out the control structure */
+ write_metapage(mp);
+
+ /*
+ * write out dirty pages of imap
+ */
+ fsync_inode_data_buffers(ipimap);
+
+ diWriteSpecial(ipimap, 0);
+
+ return (0);
+}
+
+
+/*
+ * NAME: diRead()
+ *
+ * FUNCTION: initialize an incore inode from disk.
+ *
+ * on entry, the specifed incore inode should itself
+ * specify the disk inode number corresponding to the
+ * incore inode (i.e. i_number should be initialized).
+ *
+ * this routine handles incore inode initialization for
+ * both "special" and "regular" inodes. special inodes
+ * are those required early in the mount process and
+ * require special handling since much of the file system
+ * is not yet initialized. these "special" inodes are
+ * identified by a NULL inode map inode pointer and are
+ * actually initialized by a call to diReadSpecial().
+ *
+ * for regular inodes, the iag describing the disk inode
+ * is read from disk to determine the inode extent address
+ * for the disk inode. with the inode extent address in
+ * hand, the page of the extent that contains the disk
+ * inode is read and the disk inode is copied to the
+ * incore inode.
+ *
+ * PARAMETERS:
+ * ip - pointer to incore inode to be initialized from disk.
+ *
+ * RETURN VALUES:
+ * 0 - success
+ * -EIO - i/o error.
+ * -ENOMEM - insufficient memory
+ *
+ */
+int diRead(struct inode *ip)
+{
+ struct jfs_sb_info *sbi = JFS_SBI(ip->i_sb);
+ int iagno, ino, extno, rc;
+ struct inode *ipimap;
+ struct dinode *dp;
+ struct iag *iagp;
+ struct metapage *mp;
+ s64 blkno, agstart;
+ struct inomap *imap;
+ int block_offset;
+ int inodes_left;
+ uint pageno;
+ int rel_inode;
+
+ jfs_info("diRead: ino = %ld", ip->i_ino);
+
+ ipimap = sbi->ipimap;
+ JFS_IP(ip)->ipimap = ipimap;
+
+ /* determine the iag number for this inode (number) */
+ iagno = INOTOIAG(ip->i_ino);
+
+ /* read the iag */
+ imap = JFS_IP(ipimap)->i_imap;
+ IREAD_LOCK(ipimap);
+ rc = diIAGRead(imap, iagno, &mp);
+ IREAD_UNLOCK(ipimap);
+ if (rc) {
+ jfs_err("diRead: diIAGRead returned %d", rc);
+ return (rc);
+ }
+
+ iagp = (struct iag *) mp->data;
+
+ /* determine inode extent that holds the disk inode */
+ ino = ip->i_ino & (INOSPERIAG - 1);
+ extno = ino >> L2INOSPEREXT;
+
+ if ((lengthPXD(&iagp->inoext[extno]) != imap->im_nbperiext) ||
+ (addressPXD(&iagp->inoext[extno]) == 0)) {
+ release_metapage(mp);
+ return -ESTALE;
+ }
+
+ /* get disk block number of the page within the inode extent
+ * that holds the disk inode.
+ */
+ blkno = INOPBLK(&iagp->inoext[extno], ino, sbi->l2nbperpage);
+
+ /* get the ag for the iag */
+ agstart = le64_to_cpu(iagp->agstart);
+
+ release_metapage(mp);
+
+ rel_inode = (ino & (INOSPERPAGE - 1));
+ pageno = blkno >> sbi->l2nbperpage;
+
+ if ((block_offset = ((u32) blkno & (sbi->nbperpage - 1)))) {
+ /*
+ * OS/2 didn't always align inode extents on page boundaries
+ */
+ inodes_left =
+ (sbi->nbperpage - block_offset) << sbi->l2niperblk;
+
+ if (rel_inode < inodes_left)
+ rel_inode += block_offset << sbi->l2niperblk;
+ else {
+ pageno += 1;
+ rel_inode -= inodes_left;
+ }
+ }
+
+ /* read the page of disk inode */
+ mp = read_metapage(ipimap, pageno << sbi->l2nbperpage, PSIZE, 1);
+ if (mp == 0) {
+ jfs_err("diRead: read_metapage failed");
+ return -EIO;
+ }
+
+ /* locate the the disk inode requested */
+ dp = (struct dinode *) mp->data;
+ dp += rel_inode;
+
+ if (ip->i_ino != le32_to_cpu(dp->di_number)) {
+ jfs_error(ip->i_sb, "diRead: i_ino != di_number");
+ rc = -EIO;
+ } else if (le32_to_cpu(dp->di_nlink) == 0)
+ rc = -ESTALE;
+ else
+ /* copy the disk inode to the in-memory inode */
+ rc = copy_from_dinode(dp, ip);
+
+ release_metapage(mp);
+
+ /* set the ag for the inode */
+ JFS_IP(ip)->agno = BLKTOAG(agstart, sbi);
+ JFS_IP(ip)->active_ag = -1;
+
+ return (rc);
+}
+
+
+/*
+ * NAME: diReadSpecial()
+ *
+ * FUNCTION: initialize a 'special' inode from disk.
+ *
+ * this routines handles aggregate level inodes. The
+ * inode cache cannot differentiate between the
+ * aggregate inodes and the filesystem inodes, so we
+ * handle these here. We don't actually use the aggregate
+ * inode map, since these inodes are at a fixed location
+ * and in some cases the aggregate inode map isn't initialized
+ * yet.
+ *
+ * PARAMETERS:
+ * sb - filesystem superblock
+ * inum - aggregate inode number
+ * secondary - 1 if secondary aggregate inode table
+ *
+ * RETURN VALUES:
+ * new inode - success
+ * NULL - i/o error.
+ */
+struct inode *diReadSpecial(struct super_block *sb, ino_t inum, int secondary)
+{
+ struct jfs_sb_info *sbi = JFS_SBI(sb);
+ uint address;
+ struct dinode *dp;
+ struct inode *ip;
+ struct metapage *mp;
+ int rc;
+
+ ip = new_inode(sb);
+ if (ip == NULL) {
+ jfs_err("diReadSpecial: new_inode returned NULL!");
+ return ip;
+ }
+
+ rc = alloc_jfs_inode(ip);
+ if (rc) {
+ make_bad_inode(ip);
+ iput(ip);
+ return NULL;
+ }
+
+ if (secondary) {
+ address = addressPXD(&sbi->ait2) >> sbi->l2nbperpage;
+ JFS_IP(ip)->ipimap = sbi->ipaimap2;
+ } else {
+ address = AITBL_OFF >> L2PSIZE;
+ JFS_IP(ip)->ipimap = sbi->ipaimap;
+ }
+
+ ASSERT(inum < INOSPEREXT);
+
+ ip->i_ino = inum;
+
+ address += inum >> 3; /* 8 inodes per 4K page */
+
+ /* read the page of fixed disk inode (AIT) in raw mode */
+ mp = read_metapage(ip, address << sbi->l2nbperpage, PSIZE, 1);
+ if (mp == NULL) {
+ ip->i_nlink = 1; /* Don't want iput() deleting it */
+ iput(ip);
+ return (NULL);
+ }
+
+ /* get the pointer to the disk inode of interest */
+ dp = (struct dinode *) (mp->data);
+ dp += inum % 8; /* 8 inodes per 4K page */
+
+ /* copy on-disk inode to in-memory inode */
+ if ((copy_from_dinode(dp, ip)) != 0) {
+ /* handle bad return by returning NULL for ip */
+ ip->i_nlink = 1; /* Don't want iput() deleting it */
+ iput(ip);
+ /* release the page */
+ release_metapage(mp);
+ return (NULL);
+
+ }
+
+ ip->i_mapping->a_ops = &jfs_aops;
+ ip->i_mapping->gfp_mask = GFP_NOFS;
+
+ if ((inum == FILESYSTEM_I) && (JFS_IP(ip)->ipimap == sbi->ipaimap)) {
+ sbi->gengen = le32_to_cpu(dp->di_gengen);
+ sbi->inostamp = le32_to_cpu(dp->di_inostamp);
+ }
+
+ /* release the page */
+ release_metapage(mp);
+
+ return (ip);
+}
+
+/*
+ * NAME: diWriteSpecial()
+ *
+ * FUNCTION: Write the special inode to disk
+ *
+ * PARAMETERS:
+ * ip - special inode
+ * secondary - 1 if secondary aggregate inode table
+ *
+ * RETURN VALUES: none
+ */
+
+void diWriteSpecial(struct inode *ip, int secondary)
+{
+ struct jfs_sb_info *sbi = JFS_SBI(ip->i_sb);
+ uint address;
+ struct dinode *dp;
+ ino_t inum = ip->i_ino;
+ struct metapage *mp;
+
+ ip->i_state &= ~I_DIRTY;
+
+ if (secondary)
+ address = addressPXD(&sbi->ait2) >> sbi->l2nbperpage;
+ else
+ address = AITBL_OFF >> L2PSIZE;
+
+ ASSERT(inum < INOSPEREXT);
+
+ address += inum >> 3; /* 8 inodes per 4K page */
+
+ /* read the page of fixed disk inode (AIT) in raw mode */
+ mp = read_metapage(ip, address << sbi->l2nbperpage, PSIZE, 1);
+ if (mp == NULL) {
+ jfs_err("diWriteSpecial: failed to read aggregate inode "
+ "extent!");
+ return;
+ }
+
+ /* get the pointer to the disk inode of interest */
+ dp = (struct dinode *) (mp->data);
+ dp += inum % 8; /* 8 inodes per 4K page */
+
+ /* copy on-disk inode to in-memory inode */
+ copy_to_dinode(dp, ip);
+ memcpy(&dp->di_xtroot, &JFS_IP(ip)->i_xtroot, 288);
+
+ if (inum == FILESYSTEM_I)
+ dp->di_gengen = cpu_to_le32(sbi->gengen);
+
+ /* write the page */
+ write_metapage(mp);
+}
+
+/*
+ * NAME: diFreeSpecial()
+ *
+ * FUNCTION: Free allocated space for special inode
+ */
+void diFreeSpecial(struct inode *ip)
+{
+ if (ip == NULL) {
+ jfs_err("diFreeSpecial called with NULL ip!");
+ return;
+ }
+ fsync_inode_data_buffers(ip);
+ truncate_inode_pages(ip->i_mapping, 0);
+ iput(ip);
+}
+
+
+
+/*
+ * NAME: diWrite()
+ *
+ * FUNCTION: write the on-disk inode portion of the in-memory inode
+ * to its corresponding on-disk inode.
+ *
+ * on entry, the specifed incore inode should itself
+ * specify the disk inode number corresponding to the
+ * incore inode (i.e. i_number should be initialized).
+ *
+ * the inode contains the inode extent address for the disk
+ * inode. with the inode extent address in hand, the
+ * page of the extent that contains the disk inode is
+ * read and the disk inode portion of the incore inode
+ * is copied to the disk inode.
+ *
+ * PARAMETERS:
+ * tid - transacation id
+ * ip - pointer to incore inode to be written to the inode extent.
+ *
+ * RETURN VALUES:
+ * 0 - success
+ * -EIO - i/o error.
+ */
+int diWrite(tid_t tid, struct inode *ip)
+{
+ struct jfs_sb_info *sbi = JFS_SBI(ip->i_sb);
+ struct jfs_inode_info *jfs_ip = JFS_IP(ip);
+ int rc = 0;
+ s32 ino;
+ struct dinode *dp;
+ s64 blkno;
+ int block_offset;
+ int inodes_left;
+ struct metapage *mp;
+ uint pageno;
+ int rel_inode;
+ int dioffset;
+ struct inode *ipimap;
+ uint type;
+ lid_t lid;
+ struct tlock *ditlck, *tlck;
+ struct linelock *dilinelock, *ilinelock;
+ struct lv *lv;
+ int n;
+
+ ipimap = jfs_ip->ipimap;
+
+ ino = ip->i_ino & (INOSPERIAG - 1);
+
+ if (!addressPXD(&(jfs_ip->ixpxd)) ||
+ (lengthPXD(&(jfs_ip->ixpxd)) !=
+ JFS_IP(ipimap)->i_imap->im_nbperiext)) {
+ jfs_error(ip->i_sb, "diWrite: ixpxd invalid");
+ return -EIO;
+ }
+
+ /*
+ * read the page of disk inode containing the specified inode:
+ */
+ /* compute the block address of the page */
+ blkno = INOPBLK(&(jfs_ip->ixpxd), ino, sbi->l2nbperpage);
+
+ rel_inode = (ino & (INOSPERPAGE - 1));
+ pageno = blkno >> sbi->l2nbperpage;
+
+ if ((block_offset = ((u32) blkno & (sbi->nbperpage - 1)))) {
+ /*
+ * OS/2 didn't always align inode extents on page boundaries
+ */
+ inodes_left =
+ (sbi->nbperpage - block_offset) << sbi->l2niperblk;
+
+ if (rel_inode < inodes_left)
+ rel_inode += block_offset << sbi->l2niperblk;
+ else {
+ pageno += 1;
+ rel_inode -= inodes_left;
+ }
+ }
+ /* read the page of disk inode */
+ retry:
+ mp = read_metapage(ipimap, pageno << sbi->l2nbperpage, PSIZE, 1);
+ if (mp == 0)
+ return -EIO;
+
+ /* get the pointer to the disk inode */
+ dp = (struct dinode *) mp->data;
+ dp += rel_inode;
+
+ dioffset = (ino & (INOSPERPAGE - 1)) << L2DISIZE;
+
+ /*
+ * acquire transaction lock on the on-disk inode;
+ * N.B. tlock is acquired on ipimap not ip;
+ */
+ if ((ditlck =
+ txLock(tid, ipimap, mp, tlckINODE | tlckENTRY)) == NULL)
+ goto retry;
+ dilinelock = (struct linelock *) & ditlck->lock;
+
+ /*
+ * copy btree root from in-memory inode to on-disk inode
+ *
+ * (tlock is taken from inline B+-tree root in in-memory
+ * inode when the B+-tree root is updated, which is pointed
+ * by jfs_ip->blid as well as being on tx tlock list)
+ *
+ * further processing of btree root is based on the copy
+ * in in-memory inode, where txLog() will log from, and,
+ * for xtree root, txUpdateMap() will update map and reset
+ * XAD_NEW bit;
+ */
+
+ if (S_ISDIR(ip->i_mode) && (lid = jfs_ip->xtlid)) {
+ /*
+ * This is the special xtree inside the directory for storing
+ * the directory table
+ */
+ xtpage_t *p, *xp;
+ xad_t *xad;
+
+ jfs_ip->xtlid = 0;
+ tlck = lid_to_tlock(lid);
+ assert(tlck->type & tlckXTREE);
+ tlck->type |= tlckBTROOT;
+ tlck->mp = mp;
+ ilinelock = (struct linelock *) & tlck->lock;
+
+ /*
+ * copy xtree root from inode to dinode:
+ */
+ p = &jfs_ip->i_xtroot;
+ xp = (xtpage_t *) &dp->di_dirtable;
+ lv = ilinelock->lv;
+ for (n = 0; n < ilinelock->index; n++, lv++) {
+ memcpy(&xp->xad[lv->offset], &p->xad[lv->offset],
+ lv->length << L2XTSLOTSIZE);
+ }
+
+ /* reset on-disk (metadata page) xtree XAD_NEW bit */
+ xad = &xp->xad[XTENTRYSTART];
+ for (n = XTENTRYSTART;
+ n < le16_to_cpu(xp->header.nextindex); n++, xad++)
+ if (xad->flag & (XAD_NEW | XAD_EXTENDED))
+ xad->flag &= ~(XAD_NEW | XAD_EXTENDED);
+ }
+
+ if ((lid = jfs_ip->blid) == 0)
+ goto inlineData;
+ jfs_ip->blid = 0;
+
+ tlck = lid_to_tlock(lid);
+ type = tlck->type;
+ tlck->type |= tlckBTROOT;
+ tlck->mp = mp;
+ ilinelock = (struct linelock *) & tlck->lock;
+
+ /*
+ * regular file: 16 byte (XAD slot) granularity
+ */
+ if (type & tlckXTREE) {
+ xtpage_t *p, *xp;
+ xad_t *xad;
+
+ /*
+ * copy xtree root from inode to dinode:
+ */
+ p = &jfs_ip->i_xtroot;
+ xp = &dp->di_xtroot;
+ lv = ilinelock->lv;
+ for (n = 0; n < ilinelock->index; n++, lv++) {
+ memcpy(&xp->xad[lv->offset], &p->xad[lv->offset],
+ lv->length << L2XTSLOTSIZE);
+ }
+
+ /* reset on-disk (metadata page) xtree XAD_NEW bit */
+ xad = &xp->xad[XTENTRYSTART];
+ for (n = XTENTRYSTART;
+ n < le16_to_cpu(xp->header.nextindex); n++, xad++)
+ if (xad->flag & (XAD_NEW | XAD_EXTENDED))
+ xad->flag &= ~(XAD_NEW | XAD_EXTENDED);
+ }
+ /*
+ * directory: 32 byte (directory entry slot) granularity
+ */
+ else if (type & tlckDTREE) {
+ dtpage_t *p, *xp;
+
+ /*
+ * copy dtree root from inode to dinode:
+ */
+ p = (dtpage_t *) &jfs_ip->i_dtroot;
+ xp = (dtpage_t *) & dp->di_dtroot;
+ lv = ilinelock->lv;
+ for (n = 0; n < ilinelock->index; n++, lv++) {
+ memcpy(&xp->slot[lv->offset], &p->slot[lv->offset],
+ lv->length << L2DTSLOTSIZE);
+ }
+ } else {
+ jfs_err("diWrite: UFO tlock");
+ }
+
+ inlineData:
+ /*
+ * copy inline symlink from in-memory inode to on-disk inode
+ */
+ if (S_ISLNK(ip->i_mode) && ip->i_size < IDATASIZE) {
+ lv = & dilinelock->lv[dilinelock->index];
+ lv->offset = (dioffset + 2 * 128) >> L2INODESLOTSIZE;
+ lv->length = 2;
+ memcpy(&dp->di_fastsymlink, jfs_ip->i_inline, IDATASIZE);
+ dilinelock->index++;
+ }
+ /*
+ * copy inline data from in-memory inode to on-disk inode:
+ * 128 byte slot granularity
+ */
+ if (test_cflag(COMMIT_Inlineea, ip)) {
+ lv = & dilinelock->lv[dilinelock->index];
+ lv->offset = (dioffset + 3 * 128) >> L2INODESLOTSIZE;
+ lv->length = 1;
+ memcpy(&dp->di_inlineea, jfs_ip->i_inline_ea, INODESLOTSIZE);
+ dilinelock->index++;
+
+ clear_cflag(COMMIT_Inlineea, ip);
+ }
+
+ /*
+ * lock/copy inode base: 128 byte slot granularity
+ */
+// baseDinode:
+ lv = & dilinelock->lv[dilinelock->index];
+ lv->offset = dioffset >> L2INODESLOTSIZE;
+ copy_to_dinode(dp, ip);
+ if (test_and_clear_cflag(COMMIT_Dirtable, ip)) {
+ lv->length = 2;
+ memcpy(&dp->di_dirtable, &jfs_ip->i_dirtable, 96);
+ } else
+ lv->length = 1;
+ dilinelock->index++;
+
+#ifdef _JFS_FASTDASD
+ /*
+ * We aren't logging changes to the DASD used in directory inodes,
+ * but we need to write them to disk. If we don't unmount cleanly,
+ * mount will recalculate the DASD used.
+ */
+ if (S_ISDIR(ip->i_mode)
+ && (ip->i_ipmnt->i_mntflag & JFS_DASD_ENABLED))
+ bcopy(&ip->i_DASD, &dp->di_DASD, sizeof(struct dasd));
+#endif /* _JFS_FASTDASD */
+
+ /* release the buffer holding the updated on-disk inode.
+ * the buffer will be later written by commit processing.
+ */
+ write_metapage(mp);
+
+ return (rc);
+}
+
+
+/*
+ * NAME: diFree(ip)
+ *
+ * FUNCTION: free a specified inode from the inode working map
+ * for a fileset or aggregate.
+ *
+ * if the inode to be freed represents the first (only)
+ * free inode within the iag, the iag will be placed on
+ * the ag free inode list.
+ *
+ * freeing the inode will cause the inode extent to be
+ * freed if the inode is the only allocated inode within
+ * the extent. in this case all the disk resource backing
+ * up the inode extent will be freed. in addition, the iag
+ * will be placed on the ag extent free list if the extent
+ * is the first free extent in the iag. if freeing the
+ * extent also means that no free inodes will exist for
+ * the iag, the iag will also be removed from the ag free
+ * inode list.
+ *
+ * the iag describing the inode will be freed if the extent
+ * is to be freed and it is the only backed extent within
+ * the iag. in this case, the iag will be removed from the
+ * ag free extent list and ag free inode list and placed on
+ * the inode map's free iag list.
+ *
+ * a careful update approach is used to provide consistency
+ * in the face of updates to multiple buffers. under this
+ * approach, all required buffers are obtained before making
+ * any updates and are held until all updates are complete.
+ *
+ * PARAMETERS:
+ * ip - inode to be freed.
+ *
+ * RETURN VALUES:
+ * 0 - success
+ * -EIO - i/o error.
+ */
+int diFree(struct inode *ip)
+{
+ int rc;
+ ino_t inum = ip->i_ino;
+ struct iag *iagp, *aiagp, *biagp, *ciagp, *diagp;
+ struct metapage *mp, *amp, *bmp, *cmp, *dmp;
+ int iagno, ino, extno, bitno, sword, agno;
+ int back, fwd;
+ u32 bitmap, mask;
+ struct inode *ipimap = JFS_SBI(ip->i_sb)->ipimap;
+ struct inomap *imap = JFS_IP(ipimap)->i_imap;
+ pxd_t freepxd;
+ tid_t tid;
+ struct inode *iplist[3];
+ struct tlock *tlck;
+ struct pxd_lock *pxdlock;
+
+ /*
+ * This is just to suppress compiler warnings. The same logic that
+ * references these variables is used to initialize them.
+ */
+ aiagp = biagp = ciagp = diagp = NULL;
+
+ /* get the iag number containing the inode.
+ */
+ iagno = INOTOIAG(inum);
+
+ /* make sure that the iag is contained within
+ * the map.
+ */
+ if (iagno >= imap->im_nextiag) {
+ dump_mem("imap", imap, 32);
+ jfs_error(ip->i_sb,
+ "diFree: inum = %d, iagno = %d, nextiag = %d",
+ (uint) inum, iagno, imap->im_nextiag);
+ return -EIO;
+ }
+
+ /* get the allocation group for this ino.
+ */
+ agno = JFS_IP(ip)->agno;
+
+ /* Lock the AG specific inode map information
+ */
+ AG_LOCK(imap, agno);
+
+ /* Obtain read lock in imap inode. Don't release it until we have
+ * read all of the IAG's that we are going to.
+ */
+ IREAD_LOCK(ipimap);
+
+ /* read the iag.
+ */
+ if ((rc = diIAGRead(imap, iagno, &mp))) {
+ IREAD_UNLOCK(ipimap);
+ AG_UNLOCK(imap, agno);
+ return (rc);
+ }
+ iagp = (struct iag *) mp->data;
+
+ /* get the inode number and extent number of the inode within
+ * the iag and the inode number within the extent.
+ */
+ ino = inum & (INOSPERIAG - 1);
+ extno = ino >> L2INOSPEREXT;
+ bitno = ino & (INOSPEREXT - 1);
+ mask = HIGHORDER >> bitno;
+
+ if (!(le32_to_cpu(iagp->wmap[extno]) & mask)) {
+ jfs_error(ip->i_sb,
+ "diFree: wmap shows inode already free");
+ }
+
+ if (!addressPXD(&iagp->inoext[extno])) {
+ release_metapage(mp);
+ IREAD_UNLOCK(ipimap);
+ AG_UNLOCK(imap, agno);
+ jfs_error(ip->i_sb, "diFree: invalid inoext");
+ return -EIO;
+ }
+
+ /* compute the bitmap for the extent reflecting the freed inode.
+ */
+ bitmap = le32_to_cpu(iagp->wmap[extno]) & ~mask;
+
+ if (imap->im_agctl[agno].numfree > imap->im_agctl[agno].numinos) {
+ release_metapage(mp);
+ IREAD_UNLOCK(ipimap);
+ AG_UNLOCK(imap, agno);
+ jfs_error(ip->i_sb, "diFree: numfree > numinos");
+ return -EIO;
+ }
+ /*
+ * inode extent still has some inodes or below low water mark:
+ * keep the inode extent;
+ */
+ if (bitmap ||
+ imap->im_agctl[agno].numfree < 96 ||
+ (imap->im_agctl[agno].numfree < 288 &&
+ (((imap->im_agctl[agno].numfree * 100) /
+ imap->im_agctl[agno].numinos) <= 25))) {
+ /* if the iag currently has no free inodes (i.e.,
+ * the inode being freed is the first free inode of iag),
+ * insert the iag at head of the inode free list for the ag.
+ */
+ if (iagp->nfreeinos == 0) {
+ /* check if there are any iags on the ag inode
+ * free list. if so, read the first one so that
+ * we can link the current iag onto the list at
+ * the head.
+ */
+ if ((fwd = imap->im_agctl[agno].inofree) >= 0) {
+ /* read the iag that currently is the head
+ * of the list.
+ */
+ if ((rc = diIAGRead(imap, fwd, &amp))) {
+ IREAD_UNLOCK(ipimap);
+ AG_UNLOCK(imap, agno);
+ release_metapage(mp);
+ return (rc);
+ }
+ aiagp = (struct iag *) amp->data;
+
+ /* make current head point back to the iag.
+ */
+ aiagp->inofreeback = cpu_to_le32(iagno);
+
+ write_metapage(amp);
+ }
+
+ /* iag points forward to current head and iag
+ * becomes the new head of the list.
+ */
+ iagp->inofreefwd =
+ cpu_to_le32(imap->im_agctl[agno].inofree);
+ iagp->inofreeback = -1;
+ imap->im_agctl[agno].inofree = iagno;
+ }
+ IREAD_UNLOCK(ipimap);
+
+ /* update the free inode summary map for the extent if
+ * freeing the inode means the extent will now have free
+ * inodes (i.e., the inode being freed is the first free
+ * inode of extent),
+ */
+ if (iagp->wmap[extno] == ONES) {
+ sword = extno >> L2EXTSPERSUM;
+ bitno = extno & (EXTSPERSUM - 1);
+ iagp->inosmap[sword] &=
+ cpu_to_le32(~(HIGHORDER >> bitno));
+ }
+
+ /* update the bitmap.
+ */
+ iagp->wmap[extno] = cpu_to_le32(bitmap);
+ DBG_DIFREE(imap, inum);
+
+ /* update the free inode counts at the iag, ag and
+ * map level.
+ */
+ iagp->nfreeinos =
+ cpu_to_le32(le32_to_cpu(iagp->nfreeinos) + 1);
+ imap->im_agctl[agno].numfree += 1;
+ atomic_inc(&imap->im_numfree);
+
+ /* release the AG inode map lock
+ */
+ AG_UNLOCK(imap, agno);
+
+ /* write the iag */
+ write_metapage(mp);
+
+ return (0);
+ }
+
+
+ /*
+ * inode extent has become free and above low water mark:
+ * free the inode extent;
+ */
+
+ /*
+ * prepare to update iag list(s) (careful update step 1)
+ */
+ amp = bmp = cmp = dmp = NULL;
+ fwd = back = -1;
+
+ /* check if the iag currently has no free extents. if so,
+ * it will be placed on the head of the ag extent free list.
+ */
+ if (iagp->nfreeexts == 0) {
+ /* check if the ag extent free list has any iags.
+ * if so, read the iag at the head of the list now.
+ * this (head) iag will be updated later to reflect
+ * the addition of the current iag at the head of
+ * the list.
+ */
+ if ((fwd = imap->im_agctl[agno].extfree) >= 0) {
+ if ((rc = diIAGRead(imap, fwd, &amp)))
+ goto error_out;
+ aiagp = (struct iag *) amp->data;
+ }
+ } else {
+ /* iag has free extents. check if the addition of a free
+ * extent will cause all extents to be free within this
+ * iag. if so, the iag will be removed from the ag extent
+ * free list and placed on the inode map's free iag list.
+ */
+ if (iagp->nfreeexts == cpu_to_le32(EXTSPERIAG - 1)) {
+ /* in preparation for removing the iag from the
+ * ag extent free list, read the iags preceeding
+ * and following the iag on the ag extent free
+ * list.
+ */
+ if ((fwd = le32_to_cpu(iagp->extfreefwd)) >= 0) {
+ if ((rc = diIAGRead(imap, fwd, &amp)))
+ goto error_out;
+ aiagp = (struct iag *) amp->data;
+ }
+
+ if ((back = le32_to_cpu(iagp->extfreeback)) >= 0) {
+ if ((rc = diIAGRead(imap, back, &bmp)))
+ goto error_out;
+ biagp = (struct iag *) bmp->data;
+ }
+ }
+ }
+
+ /* remove the iag from the ag inode free list if freeing
+ * this extent cause the iag to have no free inodes.
+ */
+ if (iagp->nfreeinos == cpu_to_le32(INOSPEREXT - 1)) {
+ int inofreeback = le32_to_cpu(iagp->inofreeback);
+ int inofreefwd = le32_to_cpu(iagp->inofreefwd);
+
+ /* in preparation for removing the iag from the
+ * ag inode free list, read the iags preceeding
+ * and following the iag on the ag inode free
+ * list. before reading these iags, we must make
+ * sure that we already don't have them in hand
+ * from up above, since re-reading an iag (buffer)
+ * we are currently holding would cause a deadlock.
+ */
+ if (inofreefwd >= 0) {
+
+ if (inofreefwd == fwd)
+ ciagp = (struct iag *) amp->data;
+ else if (inofreefwd == back)
+ ciagp = (struct iag *) bmp->data;
+ else {
+ if ((rc =
+ diIAGRead(imap, inofreefwd, &cmp)))
+ goto error_out;
+ ciagp = (struct iag *) cmp->data;
+ }
+ assert(ciagp != NULL);
+ }
+
+ if (inofreeback >= 0) {
+ if (inofreeback == fwd)
+ diagp = (struct iag *) amp->data;
+ else if (inofreeback == back)
+ diagp = (struct iag *) bmp->data;
+ else {
+ if ((rc =
+ diIAGRead(imap, inofreeback, &dmp)))
+ goto error_out;
+ diagp = (struct iag *) dmp->data;
+ }
+ assert(diagp != NULL);
+ }
+ }
+
+ IREAD_UNLOCK(ipimap);
+
+ /*
+ * invalidate any page of the inode extent freed from buffer cache;
+ */
+ freepxd = iagp->inoext[extno];
+ invalidate_pxd_metapages(ip, freepxd);
+
+ /*
+ * update iag list(s) (careful update step 2)
+ */
+ /* add the iag to the ag extent free list if this is the
+ * first free extent for the iag.
+ */
+ if (iagp->nfreeexts == 0) {
+ if (fwd >= 0)
+ aiagp->extfreeback = cpu_to_le32(iagno);
+
+ iagp->extfreefwd =
+ cpu_to_le32(imap->im_agctl[agno].extfree);
+ iagp->extfreeback = -1;
+ imap->im_agctl[agno].extfree = iagno;
+ } else {
+ /* remove the iag from the ag extent list if all extents
+ * are now free and place it on the inode map iag free list.
+ */
+ if (iagp->nfreeexts == cpu_to_le32(EXTSPERIAG - 1)) {
+ if (fwd >= 0)
+ aiagp->extfreeback = iagp->extfreeback;
+
+ if (back >= 0)
+ biagp->extfreefwd = iagp->extfreefwd;
+ else
+ imap->im_agctl[agno].extfree =
+ le32_to_cpu(iagp->extfreefwd);
+
+ iagp->extfreefwd = iagp->extfreeback = -1;
+
+ IAGFREE_LOCK(imap);
+ iagp->iagfree = cpu_to_le32(imap->im_freeiag);
+ imap->im_freeiag = iagno;
+ IAGFREE_UNLOCK(imap);
+ }
+ }
+
+ /* remove the iag from the ag inode free list if freeing
+ * this extent causes the iag to have no free inodes.
+ */
+ if (iagp->nfreeinos == cpu_to_le32(INOSPEREXT - 1)) {
+ if ((int) le32_to_cpu(iagp->inofreefwd) >= 0)
+ ciagp->inofreeback = iagp->inofreeback;
+
+ if ((int) le32_to_cpu(iagp->inofreeback) >= 0)
+ diagp->inofreefwd = iagp->inofreefwd;
+ else
+ imap->im_agctl[agno].inofree =
+ le32_to_cpu(iagp->inofreefwd);
+
+ iagp->inofreefwd = iagp->inofreeback = -1;
+ }
+
+ /* update the inode extent address and working map
+ * to reflect the free extent.
+ * the permanent map should have been updated already
+ * for the inode being freed.
+ */
+ if (iagp->pmap[extno] != 0) {
+ jfs_error(ip->i_sb, "diFree: the pmap does not show inode free");
+ }
+ iagp->wmap[extno] = 0;
+ DBG_DIFREE(imap, inum);
+ PXDlength(&iagp->inoext[extno], 0);
+ PXDaddress(&iagp->inoext[extno], 0);
+
+ /* update the free extent and free inode summary maps
+ * to reflect the freed extent.
+ * the inode summary map is marked to indicate no inodes
+ * available for the freed extent.
+ */
+ sword = extno >> L2EXTSPERSUM;
+ bitno = extno & (EXTSPERSUM - 1);
+ mask = HIGHORDER >> bitno;
+ iagp->inosmap[sword] |= cpu_to_le32(mask);
+ iagp->extsmap[sword] &= cpu_to_le32(~mask);
+
+ /* update the number of free inodes and number of free extents
+ * for the iag.
+ */
+ iagp->nfreeinos = cpu_to_le32(le32_to_cpu(iagp->nfreeinos) -
+ (INOSPEREXT - 1));
+ iagp->nfreeexts = cpu_to_le32(le32_to_cpu(iagp->nfreeexts) + 1);
+
+ /* update the number of free inodes and backed inodes
+ * at the ag and inode map level.
+ */
+ imap->im_agctl[agno].numfree -= (INOSPEREXT - 1);
+ imap->im_agctl[agno].numinos -= INOSPEREXT;
+ atomic_sub(INOSPEREXT - 1, &imap->im_numfree);
+ atomic_sub(INOSPEREXT, &imap->im_numinos);
+
+ if (amp)
+ write_metapage(amp);
+ if (bmp)
+ write_metapage(bmp);
+ if (cmp)
+ write_metapage(cmp);
+ if (dmp)
+ write_metapage(dmp);
+
+ /*
+ * start transaction to update block allocation map
+ * for the inode extent freed;
+ *
+ * N.B. AG_LOCK is released and iag will be released below, and
+ * other thread may allocate inode from/reusing the ixad freed
+ * BUT with new/different backing inode extent from the extent
+ * to be freed by the transaction;
+ */
+ tid = txBegin(ipimap->i_sb, COMMIT_FORCE);
+ down(&JFS_IP(ipimap)->commit_sem);
+
+ /* acquire tlock of the iag page of the freed ixad
+ * to force the page NOHOMEOK (even though no data is
+ * logged from the iag page) until NOREDOPAGE|FREEXTENT log
+ * for the free of the extent is committed;
+ * write FREEXTENT|NOREDOPAGE log record
+ * N.B. linelock is overlaid as freed extent descriptor;
+ */
+ tlck = txLock(tid, ipimap, mp, tlckINODE | tlckFREE);
+ pxdlock = (struct pxd_lock *) & tlck->lock;
+ pxdlock->flag = mlckFREEPXD;
+ pxdlock->pxd = freepxd;
+ pxdlock->index = 1;
+
+ write_metapage(mp);
+
+ iplist[0] = ipimap;
+
+ /*
+ * logredo needs the IAG number and IAG extent index in order
+ * to ensure that the IMap is consistent. The least disruptive
+ * way to pass these values through to the transaction manager
+ * is in the iplist array.
+ *
+ * It's not pretty, but it works.
+ */
+ iplist[1] = (struct inode *) (size_t)iagno;
+ iplist[2] = (struct inode *) (size_t)extno;
+
+ rc = txCommit(tid, 1, &iplist[0], COMMIT_FORCE);
+
+ txEnd(tid);
+ up(&JFS_IP(ipimap)->commit_sem);
+
+ /* unlock the AG inode map information */
+ AG_UNLOCK(imap, agno);
+
+ return (0);
+
+ error_out:
+ IREAD_UNLOCK(ipimap);
+
+ if (amp)
+ release_metapage(amp);
+ if (bmp)
+ release_metapage(bmp);
+ if (cmp)
+ release_metapage(cmp);
+ if (dmp)
+ release_metapage(dmp);
+
+ AG_UNLOCK(imap, agno);
+
+ release_metapage(mp);
+
+ return (rc);
+}
+
+/*
+ * There are several places in the diAlloc* routines where we initialize
+ * the inode.
+ */
+static inline void
+diInitInode(struct inode *ip, int iagno, int ino, int extno, struct iag * iagp)
+{
+ struct jfs_sb_info *sbi = JFS_SBI(ip->i_sb);
+ struct jfs_inode_info *jfs_ip = JFS_IP(ip);
+
+ ip->i_ino = (iagno << L2INOSPERIAG) + ino;
+ DBG_DIALLOC(JFS_IP(ipimap)->i_imap, ip->i_ino);
+ jfs_ip->ixpxd = iagp->inoext[extno];
+ jfs_ip->agno = BLKTOAG(le64_to_cpu(iagp->agstart), sbi);
+ jfs_ip->active_ag = -1;
+}
+
+
+/*
+ * NAME: diAlloc(pip,dir,ip)
+ *
+ * FUNCTION: allocate a disk inode from the inode working map
+ * for a fileset or aggregate.
+ *
+ * PARAMETERS:
+ * pip - pointer to incore inode for the parent inode.
+ * dir - TRUE if the new disk inode is for a directory.
+ * ip - pointer to a new inode
+ *
+ * RETURN VALUES:
+ * 0 - success.
+ * -ENOSPC - insufficient disk resources.
+ * -EIO - i/o error.
+ */
+int diAlloc(struct inode *pip, boolean_t dir, struct inode *ip)
+{
+ int rc, ino, iagno, addext, extno, bitno, sword;
+ int nwords, rem, i, agno;
+ u32 mask, inosmap, extsmap;
+ struct inode *ipimap;
+ struct metapage *mp;
+ ino_t inum;
+ struct iag *iagp;
+ struct inomap *imap;
+
+ /* get the pointers to the inode map inode and the
+ * corresponding imap control structure.
+ */
+ ipimap = JFS_SBI(pip->i_sb)->ipimap;
+ imap = JFS_IP(ipimap)->i_imap;
+ JFS_IP(ip)->ipimap = ipimap;
+ JFS_IP(ip)->fileset = FILESYSTEM_I;
+
+ /* for a directory, the allocation policy is to start
+ * at the ag level using the preferred ag.
+ */
+ if (dir == TRUE) {
+ agno = dbNextAG(JFS_SBI(pip->i_sb)->ipbmap);
+ AG_LOCK(imap, agno);
+ goto tryag;
+ }
+
+ /* for files, the policy starts off by trying to allocate from
+ * the same iag containing the parent disk inode:
+ * try to allocate the new disk inode close to the parent disk
+ * inode, using parent disk inode number + 1 as the allocation
+ * hint. (we use a left-to-right policy to attempt to avoid
+ * moving backward on the disk.) compute the hint within the
+ * file system and the iag.
+ */
+
+ /* get the ag number of this iag */
+ agno = JFS_IP(pip)->agno;
+
+ if (atomic_read(&JFS_SBI(pip->i_sb)->bmap->db_active[agno])) {
+ /*
+ * There is an open file actively growing. We want to
+ * allocate new inodes from a different ag to avoid
+ * fragmentation problems.
+ */
+ agno = dbNextAG(JFS_SBI(pip->i_sb)->ipbmap);
+ AG_LOCK(imap, agno);
+ goto tryag;
+ }
+
+ inum = pip->i_ino + 1;
+ ino = inum & (INOSPERIAG - 1);
+
+ /* back off the the hint if it is outside of the iag */
+ if (ino == 0)
+ inum = pip->i_ino;
+
+ /* lock the AG inode map information */
+ AG_LOCK(imap, agno);
+
+ /* Get read lock on imap inode */
+ IREAD_LOCK(ipimap);
+
+ /* get the iag number and read the iag */
+ iagno = INOTOIAG(inum);
+ if ((rc = diIAGRead(imap, iagno, &mp))) {
+ IREAD_UNLOCK(ipimap);
+ AG_UNLOCK(imap, agno);
+ return (rc);
+ }
+ iagp = (struct iag *) mp->data;
+
+ /* determine if new inode extent is allowed to be added to the iag.
+ * new inode extent can be added to the iag if the ag
+ * has less than 32 free disk inodes and the iag has free extents.
+ */
+ addext = (imap->im_agctl[agno].numfree < 32 && iagp->nfreeexts);
+
+ /*
+ * try to allocate from the IAG
+ */
+ /* check if the inode may be allocated from the iag
+ * (i.e. the inode has free inodes or new extent can be added).
+ */
+ if (iagp->nfreeinos || addext) {
+ /* determine the extent number of the hint.
+ */
+ extno = ino >> L2INOSPEREXT;
+
+ /* check if the extent containing the hint has backed
+ * inodes. if so, try to allocate within this extent.
+ */
+ if (addressPXD(&iagp->inoext[extno])) {
+ bitno = ino & (INOSPEREXT - 1);
+ if ((bitno =
+ diFindFree(le32_to_cpu(iagp->wmap[extno]),
+ bitno))
+ < INOSPEREXT) {
+ ino = (extno << L2INOSPEREXT) + bitno;
+
+ /* a free inode (bit) was found within this
+ * extent, so allocate it.
+ */
+ rc = diAllocBit(imap, iagp, ino);
+ IREAD_UNLOCK(ipimap);
+ if (rc) {
+ assert(rc == -EIO);
+ } else {
+ /* set the results of the allocation
+ * and write the iag.
+ */
+ diInitInode(ip, iagno, ino, extno,
+ iagp);
+ mark_metapage_dirty(mp);
+ }
+ release_metapage(mp);
+
+ /* free the AG lock and return.
+ */
+ AG_UNLOCK(imap, agno);
+ return (rc);
+ }
+
+ if (!addext)
+ extno =
+ (extno ==
+ EXTSPERIAG - 1) ? 0 : extno + 1;
+ }
+
+ /*
+ * no free inodes within the extent containing the hint.
+ *
+ * try to allocate from the backed extents following
+ * hint or, if appropriate (i.e. addext is true), allocate
+ * an extent of free inodes at or following the extent
+ * containing the hint.
+ *
+ * the free inode and free extent summary maps are used
+ * here, so determine the starting summary map position
+ * and the number of words we'll have to examine. again,
+ * the approach is to allocate following the hint, so we
+ * might have to initially ignore prior bits of the summary
+ * map that represent extents prior to the extent containing
+ * the hint and later revisit these bits.
+ */
+ bitno = extno & (EXTSPERSUM - 1);
+ nwords = (bitno == 0) ? SMAPSZ : SMAPSZ + 1;
+ sword = extno >> L2EXTSPERSUM;
+
+ /* mask any prior bits for the starting words of the
+ * summary map.
+ */
+ mask = ONES << (EXTSPERSUM - bitno);
+ inosmap = le32_to_cpu(iagp->inosmap[sword]) | mask;
+ extsmap = le32_to_cpu(iagp->extsmap[sword]) | mask;
+
+ /* scan the free inode and free extent summary maps for
+ * free resources.
+ */
+ for (i = 0; i < nwords; i++) {
+ /* check if this word of the free inode summary
+ * map describes an extent with free inodes.
+ */
+ if (~inosmap) {
+ /* an extent with free inodes has been
+ * found. determine the extent number
+ * and the inode number within the extent.
+ */
+ rem = diFindFree(inosmap, 0);
+ extno = (sword << L2EXTSPERSUM) + rem;
+ rem = diFindFree(le32_to_cpu(iagp->wmap[extno]),
+ 0);
+ if (rem >= INOSPEREXT) {
+ IREAD_UNLOCK(ipimap);
+ release_metapage(mp);
+ AG_UNLOCK(imap, agno);
+ jfs_error(ip->i_sb,
+ "diAlloc: can't find free bit "
+ "in wmap");
+ return EIO;
+ }
+
+ /* determine the inode number within the
+ * iag and allocate the inode from the
+ * map.
+ */
+ ino = (extno << L2INOSPEREXT) + rem;
+ rc = diAllocBit(imap, iagp, ino);
+ IREAD_UNLOCK(ipimap);
+ if (rc)
+ assert(rc == -EIO);
+ else {
+ /* set the results of the allocation
+ * and write the iag.
+ */
+ diInitInode(ip, iagno, ino, extno,
+ iagp);
+ mark_metapage_dirty(mp);
+ }
+ release_metapage(mp);
+
+ /* free the AG lock and return.
+ */
+ AG_UNLOCK(imap, agno);
+ return (rc);
+
+ }
+
+ /* check if we may allocate an extent of free
+ * inodes and whether this word of the free
+ * extents summary map describes a free extent.
+ */
+ if (addext && ~extsmap) {
+ /* a free extent has been found. determine
+ * the extent number.
+ */
+ rem = diFindFree(extsmap, 0);
+ extno = (sword << L2EXTSPERSUM) + rem;
+
+ /* allocate an extent of free inodes.
+ */
+ if ((rc = diNewExt(imap, iagp, extno))) {
+ /* if there is no disk space for a
+ * new extent, try to allocate the
+ * disk inode from somewhere else.
+ */
+ if (rc == -ENOSPC)
+ break;
+
+ assert(rc == -EIO);
+ } else {
+ /* set the results of the allocation
+ * and write the iag.
+ */
+ diInitInode(ip, iagno,
+ extno << L2INOSPEREXT,
+ extno, iagp);
+ mark_metapage_dirty(mp);
+ }
+ release_metapage(mp);
+ /* free the imap inode & the AG lock & return.
+ */
+ IREAD_UNLOCK(ipimap);
+ AG_UNLOCK(imap, agno);
+ return (rc);
+ }
+
+ /* move on to the next set of summary map words.
+ */
+ sword = (sword == SMAPSZ - 1) ? 0 : sword + 1;
+ inosmap = le32_to_cpu(iagp->inosmap[sword]);
+ extsmap = le32_to_cpu(iagp->extsmap[sword]);
+ }
+ }
+ /* unlock imap inode */
+ IREAD_UNLOCK(ipimap);
+
+ /* nothing doing in this iag, so release it. */
+ release_metapage(mp);
+
+ tryag:
+ /*
+ * try to allocate anywhere within the same AG as the parent inode.
+ */
+ rc = diAllocAG(imap, agno, dir, ip);
+
+ AG_UNLOCK(imap, agno);
+
+ if (rc != -ENOSPC)
+ return (rc);
+
+ /*
+ * try to allocate in any AG.
+ */
+ return (diAllocAny(imap, agno, dir, ip));
+}
+
+
+/*
+ * NAME: diAllocAG(imap,agno,dir,ip)
+ *
+ * FUNCTION: allocate a disk inode from the allocation group.
+ *
+ * this routine first determines if a new extent of free
+ * inodes should be added for the allocation group, with
+ * the current request satisfied from this extent. if this
+ * is the case, an attempt will be made to do just that. if
+ * this attempt fails or it has been determined that a new
+ * extent should not be added, an attempt is made to satisfy
+ * the request by allocating an existing (backed) free inode
+ * from the allocation group.
+ *
+ * PRE CONDITION: Already have the AG lock for this AG.
+ *
+ * PARAMETERS:
+ * imap - pointer to inode map control structure.
+ * agno - allocation group to allocate from.
+ * dir - TRUE if the new disk inode is for a directory.
+ * ip - pointer to the new inode to be filled in on successful return
+ * with the disk inode number allocated, its extent address
+ * and the start of the ag.
+ *
+ * RETURN VALUES:
+ * 0 - success.
+ * -ENOSPC - insufficient disk resources.
+ * -EIO - i/o error.
+ */
+static int
+diAllocAG(struct inomap * imap, int agno, boolean_t dir, struct inode *ip)
+{
+ int rc, addext, numfree, numinos;
+
+ /* get the number of free and the number of backed disk
+ * inodes currently within the ag.
+ */
+ numfree = imap->im_agctl[agno].numfree;
+ numinos = imap->im_agctl[agno].numinos;
+
+ if (numfree > numinos) {
+ jfs_error(ip->i_sb, "diAllocAG: numfree > numinos");
+ return -EIO;
+ }
+
+ /* determine if we should allocate a new extent of free inodes
+ * within the ag: for directory inodes, add a new extent
+ * if there are a small number of free inodes or number of free
+ * inodes is a small percentage of the number of backed inodes.
+ */
+ if (dir == TRUE)
+ addext = (numfree < 64 ||
+ (numfree < 256
+ && ((numfree * 100) / numinos) <= 20));
+ else
+ addext = (numfree == 0);
+
+ /*
+ * try to allocate a new extent of free inodes.
+ */
+ if (addext) {
+ /* if free space is not avaliable for this new extent, try
+ * below to allocate a free and existing (already backed)
+ * inode from the ag.
+ */
+ if ((rc = diAllocExt(imap, agno, ip)) != -ENOSPC)
+ return (rc);
+ }
+
+ /*
+ * try to allocate an existing free inode from the ag.
+ */
+ return (diAllocIno(imap, agno, ip));
+}
+
+
+/*
+ * NAME: diAllocAny(imap,agno,dir,iap)
+ *
+ * FUNCTION: allocate a disk inode from any other allocation group.
+ *
+ * this routine is called when an allocation attempt within
+ * the primary allocation group has failed. if attempts to
+ * allocate an inode from any allocation group other than the
+ * specified primary group.
+ *
+ * PARAMETERS:
+ * imap - pointer to inode map control structure.
+ * agno - primary allocation group (to avoid).
+ * dir - TRUE if the new disk inode is for a directory.
+ * ip - pointer to a new inode to be filled in on successful return
+ * with the disk inode number allocated, its extent address
+ * and the start of the ag.
+ *
+ * RETURN VALUES:
+ * 0 - success.
+ * -ENOSPC - insufficient disk resources.
+ * -EIO - i/o error.
+ */
+static int
+diAllocAny(struct inomap * imap, int agno, boolean_t dir, struct inode *ip)
+{
+ int ag, rc;
+ int maxag = JFS_SBI(imap->im_ipimap->i_sb)->bmap->db_maxag;
+
+
+ /* try to allocate from the ags following agno up to
+ * the maximum ag number.
+ */
+ for (ag = agno + 1; ag <= maxag; ag++) {
+ AG_LOCK(imap, ag);
+
+ rc = diAllocAG(imap, ag, dir, ip);
+
+ AG_UNLOCK(imap, ag);
+
+ if (rc != -ENOSPC)
+ return (rc);
+ }
+
+ /* try to allocate from the ags in front of agno.
+ */
+ for (ag = 0; ag < agno; ag++) {
+ AG_LOCK(imap, ag);
+
+ rc = diAllocAG(imap, ag, dir, ip);
+
+ AG_UNLOCK(imap, ag);
+
+ if (rc != -ENOSPC)
+ return (rc);
+ }
+
+ /* no free disk inodes.
+ */
+ return -ENOSPC;
+}
+
+
+/*
+ * NAME: diAllocIno(imap,agno,ip)
+ *
+ * FUNCTION: allocate a disk inode from the allocation group's free
+ * inode list, returning an error if this free list is
+ * empty (i.e. no iags on the list).
+ *
+ * allocation occurs from the first iag on the list using
+ * the iag's free inode summary map to find the leftmost
+ * free inode in the iag.
+ *
+ * PRE CONDITION: Already have AG lock for this AG.
+ *
+ * PARAMETERS:
+ * imap - pointer to inode map control structure.
+ * agno - allocation group.
+ * ip - pointer to new inode to be filled in on successful return
+ * with the disk inode number allocated, its extent address
+ * and the start of the ag.
+ *
+ * RETURN VALUES:
+ * 0 - success.
+ * -ENOSPC - insufficient disk resources.
+ * -EIO - i/o error.
+ */
+static int diAllocIno(struct inomap * imap, int agno, struct inode *ip)
+{
+ int iagno, ino, rc, rem, extno, sword;
+ struct metapage *mp;
+ struct iag *iagp;
+
+ /* check if there are iags on the ag's free inode list.
+ */
+ if ((iagno = imap->im_agctl[agno].inofree) < 0)
+ return -ENOSPC;
+
+ /* obtain read lock on imap inode */
+ IREAD_LOCK(imap->im_ipimap);
+
+ /* read the iag at the head of the list.
+ */
+ if ((rc = diIAGRead(imap, iagno, &mp))) {
+ IREAD_UNLOCK(imap->im_ipimap);
+ return (rc);
+ }
+ iagp = (struct iag *) mp->data;
+
+ /* better be free inodes in this iag if it is on the
+ * list.
+ */
+ if (!iagp->nfreeinos) {
+ IREAD_UNLOCK(imap->im_ipimap);
+ release_metapage(mp);
+ jfs_error(ip->i_sb,
+ "diAllocIno: nfreeinos = 0, but iag on freelist");
+ return -EIO;
+ }
+
+ /* scan the free inode summary map to find an extent
+ * with free inodes.
+ */
+ for (sword = 0;; sword++) {
+ if (sword >= SMAPSZ) {
+ IREAD_UNLOCK(imap->im_ipimap);
+ release_metapage(mp);
+ jfs_error(ip->i_sb,
+ "diAllocIno: free inode not found in summary map");
+ return -EIO;
+ }
+
+ if (~iagp->inosmap[sword])
+ break;
+ }
+
+ /* found a extent with free inodes. determine
+ * the extent number.
+ */
+ rem = diFindFree(le32_to_cpu(iagp->inosmap[sword]), 0);
+ if (rem >= EXTSPERSUM) {
+ IREAD_UNLOCK(imap->im_ipimap);
+ release_metapage(mp);
+ jfs_error(ip->i_sb, "diAllocIno: no free extent found");
+ return -EIO;
+ }
+ extno = (sword << L2EXTSPERSUM) + rem;
+
+ /* find the first free inode in the extent.
+ */
+ rem = diFindFree(le32_to_cpu(iagp->wmap[extno]), 0);
+ if (rem >= INOSPEREXT) {
+ IREAD_UNLOCK(imap->im_ipimap);
+ release_metapage(mp);
+ jfs_error(ip->i_sb, "diAllocIno: free inode not found");
+ return -EIO;
+ }
+
+ /* compute the inode number within the iag.
+ */
+ ino = (extno << L2INOSPEREXT) + rem;
+
+ /* allocate the inode.
+ */
+ rc = diAllocBit(imap, iagp, ino);
+ IREAD_UNLOCK(imap->im_ipimap);
+ if (rc) {
+ release_metapage(mp);
+ return (rc);
+ }
+
+ /* set the results of the allocation and write the iag.
+ */
+ diInitInode(ip, iagno, ino, extno, iagp);
+ write_metapage(mp);
+
+ return (0);
+}
+
+
+/*
+ * NAME: diAllocExt(imap,agno,ip)
+ *
+ * FUNCTION: add a new extent of free inodes to an iag, allocating
+ * an inode from this extent to satisfy the current allocation
+ * request.
+ *
+ * this routine first tries to find an existing iag with free
+ * extents through the ag free extent list. if list is not
+ * empty, the head of the list will be selected as the home
+ * of the new extent of free inodes. otherwise (the list is
+ * empty), a new iag will be allocated for the ag to contain
+ * the extent.
+ *
+ * once an iag has been selected, the free extent summary map
+ * is used to locate a free extent within the iag and diNewExt()
+ * is called to initialize the extent, with initialization
+ * including the allocation of the first inode of the extent
+ * for the purpose of satisfying this request.
+ *
+ * PARAMETERS:
+ * imap - pointer to inode map control structure.
+ * agno - allocation group number.
+ * ip - pointer to new inode to be filled in on successful return
+ * with the disk inode number allocated, its extent address
+ * and the start of the ag.
+ *
+ * RETURN VALUES:
+ * 0 - success.
+ * -ENOSPC - insufficient disk resources.
+ * -EIO - i/o error.
+ */
+static int diAllocExt(struct inomap * imap, int agno, struct inode *ip)
+{
+ int rem, iagno, sword, extno, rc;
+ struct metapage *mp;
+ struct iag *iagp;
+
+ /* check if the ag has any iags with free extents. if not,
+ * allocate a new iag for the ag.
+ */
+ if ((iagno = imap->im_agctl[agno].extfree) < 0) {
+ /* If successful, diNewIAG will obtain the read lock on the
+ * imap inode.
+ */
+ if ((rc = diNewIAG(imap, &iagno, agno, &mp))) {
+ return (rc);
+ }
+ iagp = (struct iag *) mp->data;
+
+ /* set the ag number if this a brand new iag
+ */
+ iagp->agstart =
+ cpu_to_le64(AGTOBLK(agno, imap->im_ipimap));
+ } else {
+ /* read the iag.
+ */
+ IREAD_LOCK(imap->im_ipimap);
+ if ((rc = diIAGRead(imap, iagno, &mp))) {
+ IREAD_UNLOCK(imap->im_ipimap);
+ jfs_error(ip->i_sb, "diAllocExt: error reading iag");
+ return rc;
+ }
+ iagp = (struct iag *) mp->data;
+ }
+
+ /* using the free extent summary map, find a free extent.
+ */
+ for (sword = 0;; sword++) {
+ if (sword >= SMAPSZ) {
+ release_metapage(mp);
+ IREAD_UNLOCK(imap->im_ipimap);
+ jfs_error(ip->i_sb,
+ "diAllocExt: free ext summary map not found");
+ return -EIO;
+ }
+ if (~iagp->extsmap[sword])
+ break;
+ }
+
+ /* determine the extent number of the free extent.
+ */
+ rem = diFindFree(le32_to_cpu(iagp->extsmap[sword]), 0);
+ if (rem >= EXTSPERSUM) {
+ release_metapage(mp);
+ IREAD_UNLOCK(imap->im_ipimap);
+ jfs_error(ip->i_sb, "diAllocExt: free extent not found");
+ return -EIO;
+ }
+ extno = (sword << L2EXTSPERSUM) + rem;
+
+ /* initialize the new extent.
+ */
+ rc = diNewExt(imap, iagp, extno);
+ IREAD_UNLOCK(imap->im_ipimap);
+ if (rc) {
+ /* something bad happened. if a new iag was allocated,
+ * place it back on the inode map's iag free list, and
+ * clear the ag number information.
+ */
+ if (iagp->nfreeexts == cpu_to_le32(EXTSPERIAG)) {
+ IAGFREE_LOCK(imap);
+ iagp->iagfree = cpu_to_le32(imap->im_freeiag);
+ imap->im_freeiag = iagno;
+ IAGFREE_UNLOCK(imap);
+ }
+ write_metapage(mp);
+ return (rc);
+ }
+
+ /* set the results of the allocation and write the iag.
+ */
+ diInitInode(ip, iagno, extno << L2INOSPEREXT, extno, iagp);
+
+ write_metapage(mp);
+
+ return (0);
+}
+
+
+/*
+ * NAME: diAllocBit(imap,iagp,ino)
+ *
+ * FUNCTION: allocate a backed inode from an iag.
+ *
+ * this routine performs the mechanics of allocating a
+ * specified inode from a backed extent.
+ *
+ * if the inode to be allocated represents the last free
+ * inode within the iag, the iag will be removed from the
+ * ag free inode list.
+ *
+ * a careful update approach is used to provide consistency
+ * in the face of updates to multiple buffers. under this
+ * approach, all required buffers are obtained before making
+ * any updates and are held all are updates are complete.
+ *
+ * PRE CONDITION: Already have buffer lock on iagp. Already have AG lock on
+ * this AG. Must have read lock on imap inode.
+ *
+ * PARAMETERS:
+ * imap - pointer to inode map control structure.
+ * iagp - pointer to iag.
+ * ino - inode number to be allocated within the iag.
+ *
+ * RETURN VALUES:
+ * 0 - success.
+ * -ENOSPC - insufficient disk resources.
+ * -EIO - i/o error.
+ */
+static int diAllocBit(struct inomap * imap, struct iag * iagp, int ino)
+{
+ int extno, bitno, agno, sword, rc;
+ struct metapage *amp, *bmp;
+ struct iag *aiagp = 0, *biagp = 0;
+ u32 mask;
+
+ /* check if this is the last free inode within the iag.
+ * if so, it will have to be removed from the ag free
+ * inode list, so get the iags preceeding and following
+ * it on the list.
+ */
+ if (iagp->nfreeinos == cpu_to_le32(1)) {
+ amp = bmp = NULL;
+
+ if ((int) le32_to_cpu(iagp->inofreefwd) >= 0) {
+ if ((rc =
+ diIAGRead(imap, le32_to_cpu(iagp->inofreefwd),
+ &amp)))
+ return (rc);
+ aiagp = (struct iag *) amp->data;
+ }
+
+ if ((int) le32_to_cpu(iagp->inofreeback) >= 0) {
+ if ((rc =
+ diIAGRead(imap,
+ le32_to_cpu(iagp->inofreeback),
+ &bmp))) {
+ if (amp)
+ release_metapage(amp);
+ return (rc);
+ }
+ biagp = (struct iag *) bmp->data;
+ }
+ }
+
+ /* get the ag number, extent number, inode number within
+ * the extent.
+ */
+ agno = BLKTOAG(le64_to_cpu(iagp->agstart), JFS_SBI(imap->im_ipimap->i_sb));
+ extno = ino >> L2INOSPEREXT;
+ bitno = ino & (INOSPEREXT - 1);
+
+ /* compute the mask for setting the map.
+ */
+ mask = HIGHORDER >> bitno;
+
+ /* the inode should be free and backed.
+ */
+ if (((le32_to_cpu(iagp->pmap[extno]) & mask) != 0) ||
+ ((le32_to_cpu(iagp->wmap[extno]) & mask) != 0) ||
+ (addressPXD(&iagp->inoext[extno]) == 0)) {
+ if (amp)
+ release_metapage(amp);
+ if (bmp)
+ release_metapage(bmp);
+
+ jfs_error(imap->im_ipimap->i_sb,
+ "diAllocBit: iag inconsistent");
+ return -EIO;
+ }
+
+ /* mark the inode as allocated in the working map.
+ */
+ iagp->wmap[extno] |= cpu_to_le32(mask);
+
+ /* check if all inodes within the extent are now
+ * allocated. if so, update the free inode summary
+ * map to reflect this.
+ */
+ if (iagp->wmap[extno] == ONES) {
+ sword = extno >> L2EXTSPERSUM;
+ bitno = extno & (EXTSPERSUM - 1);
+ iagp->inosmap[sword] |= cpu_to_le32(HIGHORDER >> bitno);
+ }
+
+ /* if this was the last free inode in the iag, remove the
+ * iag from the ag free inode list.
+ */
+ if (iagp->nfreeinos == cpu_to_le32(1)) {
+ if (amp) {
+ aiagp->inofreeback = iagp->inofreeback;
+ write_metapage(amp);
+ }
+
+ if (bmp) {
+ biagp->inofreefwd = iagp->inofreefwd;
+ write_metapage(bmp);
+ } else {
+ imap->im_agctl[agno].inofree =
+ le32_to_cpu(iagp->inofreefwd);
+ }
+ iagp->inofreefwd = iagp->inofreeback = -1;
+ }
+
+ /* update the free inode count at the iag, ag, inode
+ * map levels.
+ */
+ iagp->nfreeinos = cpu_to_le32(le32_to_cpu(iagp->nfreeinos) - 1);
+ imap->im_agctl[agno].numfree -= 1;
+ atomic_dec(&imap->im_numfree);
+
+ return (0);
+}
+
+
+/*
+ * NAME: diNewExt(imap,iagp,extno)
+ *
+ * FUNCTION: initialize a new extent of inodes for an iag, allocating
+ * the first inode of the extent for use for the current
+ * allocation request.
+ *
+ * disk resources are allocated for the new extent of inodes
+ * and the inodes themselves are initialized to reflect their
+ * existence within the extent (i.e. their inode numbers and
+ * inode extent addresses are set) and their initial state
+ * (mode and link count are set to zero).
+ *
+ * if the iag is new, it is not yet on an ag extent free list
+ * but will now be placed on this list.
+ *
+ * if the allocation of the new extent causes the iag to
+ * have no free extent, the iag will be removed from the
+ * ag extent free list.
+ *
+ * if the iag has no free backed inodes, it will be placed
+ * on the ag free inode list, since the addition of the new
+ * extent will now cause it to have free inodes.
+ *
+ * a careful update approach is used to provide consistency
+ * (i.e. list consistency) in the face of updates to multiple
+ * buffers. under this approach, all required buffers are
+ * obtained before making any updates and are held until all
+ * updates are complete.
+ *
+ * PRE CONDITION: Already have buffer lock on iagp. Already have AG lock on
+ * this AG. Must have read lock on imap inode.
+ *
+ * PARAMETERS:
+ * imap - pointer to inode map control structure.
+ * iagp - pointer to iag.
+ * extno - extent number.
+ *
+ * RETURN VALUES:
+ * 0 - success.
+ * -ENOSPC - insufficient disk resources.
+ * -EIO - i/o error.
+ */
+static int diNewExt(struct inomap * imap, struct iag * iagp, int extno)
+{
+ int agno, iagno, fwd, back, freei = 0, sword, rc;
+ struct iag *aiagp = 0, *biagp = 0, *ciagp = 0;
+ struct metapage *amp, *bmp, *cmp, *dmp;
+ struct inode *ipimap;
+ s64 blkno, hint;
+ int i, j;
+ u32 mask;
+ ino_t ino;
+ struct dinode *dp;
+ struct jfs_sb_info *sbi;
+
+ /* better have free extents.
+ */
+ if (!iagp->nfreeexts) {
+ jfs_error(imap->im_ipimap->i_sb, "diNewExt: no free extents");
+ return -EIO;
+ }
+
+ /* get the inode map inode.
+ */
+ ipimap = imap->im_ipimap;
+ sbi = JFS_SBI(ipimap->i_sb);
+
+ amp = bmp = cmp = NULL;
+
+ /* get the ag and iag numbers for this iag.
+ */
+ agno = BLKTOAG(le64_to_cpu(iagp->agstart), sbi);
+ iagno = le32_to_cpu(iagp->iagnum);
+
+ /* check if this is the last free extent within the
+ * iag. if so, the iag must be removed from the ag
+ * free extent list, so get the iags preceeding and
+ * following the iag on this list.
+ */
+ if (iagp->nfreeexts == cpu_to_le32(1)) {
+ if ((fwd = le32_to_cpu(iagp->extfreefwd)) >= 0) {
+ if ((rc = diIAGRead(imap, fwd, &amp)))
+ return (rc);
+ aiagp = (struct iag *) amp->data;
+ }
+
+ if ((back = le32_to_cpu(iagp->extfreeback)) >= 0) {
+ if ((rc = diIAGRead(imap, back, &bmp)))
+ goto error_out;
+ biagp = (struct iag *) bmp->data;
+ }
+ } else {
+ /* the iag has free extents. if all extents are free
+ * (as is the case for a newly allocated iag), the iag
+ * must be added to the ag free extent list, so get
+ * the iag at the head of the list in preparation for
+ * adding this iag to this list.
+ */
+ fwd = back = -1;
+ if (iagp->nfreeexts == cpu_to_le32(EXTSPERIAG)) {
+ if ((fwd = imap->im_agctl[agno].extfree) >= 0) {
+ if ((rc = diIAGRead(imap, fwd, &amp)))
+ goto error_out;
+ aiagp = (struct iag *) amp->data;
+ }
+ }
+ }
+
+ /* check if the iag has no free inodes. if so, the iag
+ * will have to be added to the ag free inode list, so get
+ * the iag at the head of the list in preparation for
+ * adding this iag to this list. in doing this, we must
+ * check if we already have the iag at the head of
+ * the list in hand.
+ */
+ if (iagp->nfreeinos == 0) {
+ freei = imap->im_agctl[agno].inofree;
+
+ if (freei >= 0) {
+ if (freei == fwd) {
+ ciagp = aiagp;
+ } else if (freei == back) {
+ ciagp = biagp;
+ } else {
+ if ((rc = diIAGRead(imap, freei, &cmp)))
+ goto error_out;
+ ciagp = (struct iag *) cmp->data;
+ }
+ if (ciagp == NULL) {
+ jfs_error(imap->im_ipimap->i_sb,
+ "diNewExt: ciagp == NULL");
+ rc = -EIO;
+ goto error_out;
+ }
+ }
+ }
+
+ /* allocate disk space for the inode extent.
+ */
+ if ((extno == 0) || (addressPXD(&iagp->inoext[extno - 1]) == 0))
+ hint = ((s64) agno << sbi->bmap->db_agl2size) - 1;
+ else
+ hint = addressPXD(&iagp->inoext[extno - 1]) +
+ lengthPXD(&iagp->inoext[extno - 1]) - 1;
+
+ if ((rc = dbAlloc(ipimap, hint, (s64) imap->im_nbperiext, &blkno)))
+ goto error_out;
+
+ /* compute the inode number of the first inode within the
+ * extent.
+ */
+ ino = (iagno << L2INOSPERIAG) + (extno << L2INOSPEREXT);
+
+ /* initialize the inodes within the newly allocated extent a
+ * page at a time.
+ */
+ for (i = 0; i < imap->im_nbperiext; i += sbi->nbperpage) {
+ /* get a buffer for this page of disk inodes.
+ */
+ dmp = get_metapage(ipimap, blkno + i, PSIZE, 1);
+ if (dmp == NULL) {
+ rc = -EIO;
+ goto error_out;
+ }
+ dp = (struct dinode *) dmp->data;
+
+ /* initialize the inode number, mode, link count and
+ * inode extent address.
+ */
+ for (j = 0; j < INOSPERPAGE; j++, dp++, ino++) {
+ dp->di_inostamp = cpu_to_le32(sbi->inostamp);
+ dp->di_number = cpu_to_le32(ino);
+ dp->di_fileset = cpu_to_le32(FILESYSTEM_I);
+ dp->di_mode = 0;
+ dp->di_nlink = 0;
+ PXDaddress(&(dp->di_ixpxd), blkno);
+ PXDlength(&(dp->di_ixpxd), imap->im_nbperiext);
+ }
+ write_metapage(dmp);
+ }
+
+ /* if this is the last free extent within the iag, remove the
+ * iag from the ag free extent list.
+ */
+ if (iagp->nfreeexts == cpu_to_le32(1)) {
+ if (fwd >= 0)
+ aiagp->extfreeback = iagp->extfreeback;
+
+ if (back >= 0)
+ biagp->extfreefwd = iagp->extfreefwd;
+ else
+ imap->im_agctl[agno].extfree =
+ le32_to_cpu(iagp->extfreefwd);
+
+ iagp->extfreefwd = iagp->extfreeback = -1;
+ } else {
+ /* if the iag has all free extents (newly allocated iag),
+ * add the iag to the ag free extent list.
+ */
+ if (iagp->nfreeexts == cpu_to_le32(EXTSPERIAG)) {
+ if (fwd >= 0)
+ aiagp->extfreeback = cpu_to_le32(iagno);
+
+ iagp->extfreefwd = cpu_to_le32(fwd);
+ iagp->extfreeback = -1;
+ imap->im_agctl[agno].extfree = iagno;
+ }
+ }
+
+ /* if the iag has no free inodes, add the iag to the
+ * ag free inode list.
+ */
+ if (iagp->nfreeinos == 0) {
+ if (freei >= 0)
+ ciagp->inofreeback = cpu_to_le32(iagno);
+
+ iagp->inofreefwd =
+ cpu_to_le32(imap->im_agctl[agno].inofree);
+ iagp->inofreeback = -1;
+ imap->im_agctl[agno].inofree = iagno;
+ }
+
+ /* initialize the extent descriptor of the extent. */
+ PXDlength(&iagp->inoext[extno], imap->im_nbperiext);
+ PXDaddress(&iagp->inoext[extno], blkno);
+
+ /* initialize the working and persistent map of the extent.
+ * the working map will be initialized such that
+ * it indicates the first inode of the extent is allocated.
+ */
+ iagp->wmap[extno] = cpu_to_le32(HIGHORDER);
+ iagp->pmap[extno] = 0;
+
+ /* update the free inode and free extent summary maps
+ * for the extent to indicate the extent has free inodes
+ * and no longer represents a free extent.
+ */
+ sword = extno >> L2EXTSPERSUM;
+ mask = HIGHORDER >> (extno & (EXTSPERSUM - 1));
+ iagp->extsmap[sword] |= cpu_to_le32(mask);
+ iagp->inosmap[sword] &= cpu_to_le32(~mask);
+
+ /* update the free inode and free extent counts for the
+ * iag.
+ */
+ iagp->nfreeinos = cpu_to_le32(le32_to_cpu(iagp->nfreeinos) +
+ (INOSPEREXT - 1));
+ iagp->nfreeexts = cpu_to_le32(le32_to_cpu(iagp->nfreeexts) - 1);
+
+ /* update the free and backed inode counts for the ag.
+ */
+ imap->im_agctl[agno].numfree += (INOSPEREXT - 1);
+ imap->im_agctl[agno].numinos += INOSPEREXT;
+
+ /* update the free and backed inode counts for the inode map.
+ */
+ atomic_add(INOSPEREXT - 1, &imap->im_numfree);
+ atomic_add(INOSPEREXT, &imap->im_numinos);
+
+ /* write the iags.
+ */
+ if (amp)
+ write_metapage(amp);
+ if (bmp)
+ write_metapage(bmp);
+ if (cmp)
+ write_metapage(cmp);
+
+ return (0);
+
+ error_out:
+
+ /* release the iags.
+ */
+ if (amp)
+ release_metapage(amp);
+ if (bmp)
+ release_metapage(bmp);
+ if (cmp)
+ release_metapage(cmp);
+
+ return (rc);
+}
+
+
+/*
+ * NAME: diNewIAG(imap,iagnop,agno)
+ *
+ * FUNCTION: allocate a new iag for an allocation group.
+ *
+ * first tries to allocate the iag from the inode map
+ * iagfree list:
+ * if the list has free iags, the head of the list is removed
+ * and returned to satisfy the request.
+ * if the inode map's iag free list is empty, the inode map
+ * is extended to hold a new iag. this new iag is initialized
+ * and returned to satisfy the request.
+ *
+ * PARAMETERS:
+ * imap - pointer to inode map control structure.
+ * iagnop - pointer to an iag number set with the number of the
+ * newly allocated iag upon successful return.
+ * agno - allocation group number.
+ * bpp - Buffer pointer to be filled in with new IAG's buffer
+ *
+ * RETURN VALUES:
+ * 0 - success.
+ * -ENOSPC - insufficient disk resources.
+ * -EIO - i/o error.
+ *
+ * serialization:
+ * AG lock held on entry/exit;
+ * write lock on the map is held inside;
+ * read lock on the map is held on successful completion;
+ *
+ * note: new iag transaction:
+ * . synchronously write iag;
+ * . write log of xtree and inode of imap;
+ * . commit;
+ * . synchronous write of xtree (right to left, bottom to top);
+ * . at start of logredo(): init in-memory imap with one additional iag page;
+ * . at end of logredo(): re-read imap inode to determine
+ * new imap size;
+ */
+static int
+diNewIAG(struct inomap * imap, int *iagnop, int agno, struct metapage ** mpp)
+{
+ int rc;
+ int iagno, i, xlen;
+ struct inode *ipimap;
+ struct super_block *sb;
+ struct jfs_sb_info *sbi;
+ struct metapage *mp;
+ struct iag *iagp;
+ s64 xaddr = 0;
+ s64 blkno;
+ tid_t tid;
+#ifdef _STILL_TO_PORT
+ xad_t xad;
+#endif /* _STILL_TO_PORT */
+ struct inode *iplist[1];
+
+ /* pick up pointers to the inode map and mount inodes */
+ ipimap = imap->im_ipimap;
+ sb = ipimap->i_sb;
+ sbi = JFS_SBI(sb);
+
+ /* acquire the free iag lock */
+ IAGFREE_LOCK(imap);
+
+ /* if there are any iags on the inode map free iag list,
+ * allocate the iag from the head of the list.
+ */
+ if (imap->im_freeiag >= 0) {
+ /* pick up the iag number at the head of the list */
+ iagno = imap->im_freeiag;
+
+ /* determine the logical block number of the iag */
+ blkno = IAGTOLBLK(iagno, sbi->l2nbperpage);
+ } else {
+ /* no free iags. the inode map will have to be extented
+ * to include a new iag.
+ */
+
+ /* acquire inode map lock */
+ IWRITE_LOCK(ipimap);
+
+ if (ipimap->i_size >> L2PSIZE != imap->im_nextiag + 1) {
+ IWRITE_UNLOCK(ipimap);
+ IAGFREE_UNLOCK(imap);
+ jfs_error(imap->im_ipimap->i_sb,
+ "diNewIAG: ipimap->i_size is wrong");
+ return -EIO;
+ }
+
+
+ /* get the next avaliable iag number */
+ iagno = imap->im_nextiag;
+
+ /* make sure that we have not exceeded the maximum inode
+ * number limit.
+ */
+ if (iagno > (MAXIAGS - 1)) {
+ /* release the inode map lock */
+ IWRITE_UNLOCK(ipimap);
+
+ rc = -ENOSPC;
+ goto out;
+ }
+
+ /*
+ * synchronously append new iag page.
+ */
+ /* determine the logical address of iag page to append */
+ blkno = IAGTOLBLK(iagno, sbi->l2nbperpage);
+
+ /* Allocate extent for new iag page */
+ xlen = sbi->nbperpage;
+ if ((rc = dbAlloc(ipimap, 0, (s64) xlen, &xaddr))) {
+ /* release the inode map lock */
+ IWRITE_UNLOCK(ipimap);
+
+ goto out;
+ }
+
+ /* assign a buffer for the page */
+ mp = get_metapage(ipimap, xaddr, PSIZE, 1);
+ if (!mp) {
+ /* Free the blocks allocated for the iag since it was
+ * not successfully added to the inode map
+ */
+ dbFree(ipimap, xaddr, (s64) xlen);
+
+ /* release the inode map lock */
+ IWRITE_UNLOCK(ipimap);
+
+ rc = -EIO;
+ goto out;
+ }
+ iagp = (struct iag *) mp->data;
+
+ /* init the iag */
+ memset(iagp, 0, sizeof(struct iag));
+ iagp->iagnum = cpu_to_le32(iagno);
+ iagp->inofreefwd = iagp->inofreeback = -1;
+ iagp->extfreefwd = iagp->extfreeback = -1;
+ iagp->iagfree = -1;
+ iagp->nfreeinos = 0;
+ iagp->nfreeexts = cpu_to_le32(EXTSPERIAG);
+
+ /* initialize the free inode summary map (free extent
+ * summary map initialization handled by bzero).
+ */
+ for (i = 0; i < SMAPSZ; i++)
+ iagp->inosmap[i] = ONES;
+
+ flush_metapage(mp);
+#ifdef _STILL_TO_PORT
+ /* synchronously write the iag page */
+ if (bmWrite(bp)) {
+ /* Free the blocks allocated for the iag since it was
+ * not successfully added to the inode map
+ */
+ dbFree(ipimap, xaddr, (s64) xlen);
+
+ /* release the inode map lock */
+ IWRITE_UNLOCK(ipimap);
+
+ rc = -EIO;
+ goto out;
+ }
+
+ /* Now the iag is on disk */
+
+ /*
+ * start tyransaction of update of the inode map
+ * addressing structure pointing to the new iag page;
+ */
+#endif /* _STILL_TO_PORT */
+ tid = txBegin(sb, COMMIT_FORCE);
+ down(&JFS_IP(ipimap)->commit_sem);
+
+ /* update the inode map addressing structure to point to it */
+ if ((rc =
+ xtInsert(tid, ipimap, 0, blkno, xlen, &xaddr, 0))) {
+ txEnd(tid);
+ up(&JFS_IP(ipimap)->commit_sem);
+ /* Free the blocks allocated for the iag since it was
+ * not successfully added to the inode map
+ */
+ dbFree(ipimap, xaddr, (s64) xlen);
+
+ /* release the inode map lock */
+ IWRITE_UNLOCK(ipimap);
+
+ goto out;
+ }
+
+ /* update the inode map's inode to reflect the extension */
+ ipimap->i_size += PSIZE;
+ ipimap->i_blocks += LBLK2PBLK(sb, xlen);
+
+ /*
+ * txCommit(COMMIT_FORCE) will synchronously write address
+ * index pages and inode after commit in careful update order
+ * of address index pages (right to left, bottom up);
+ */
+ iplist[0] = ipimap;
+ rc = txCommit(tid, 1, &iplist[0], COMMIT_FORCE);
+
+ txEnd(tid);
+ up(&JFS_IP(ipimap)->commit_sem);
+
+ duplicateIXtree(sb, blkno, xlen, &xaddr);
+
+ /* update the next avaliable iag number */
+ imap->im_nextiag += 1;
+
+ /* Add the iag to the iag free list so we don't lose the iag
+ * if a failure happens now.
+ */
+ imap->im_freeiag = iagno;
+
+ /* Until we have logredo working, we want the imap inode &
+ * control page to be up to date.
+ */
+ diSync(ipimap);
+
+ /* release the inode map lock */
+ IWRITE_UNLOCK(ipimap);
+ }
+
+ /* obtain read lock on map */
+ IREAD_LOCK(ipimap);
+
+ /* read the iag */
+ if ((rc = diIAGRead(imap, iagno, &mp))) {
+ IREAD_UNLOCK(ipimap);
+ rc = -EIO;
+ goto out;
+ }
+ iagp = (struct iag *) mp->data;
+
+ /* remove the iag from the iag free list */
+ imap->im_freeiag = le32_to_cpu(iagp->iagfree);
+ iagp->iagfree = -1;
+
+ /* set the return iag number and buffer pointer */
+ *iagnop = iagno;
+ *mpp = mp;
+
+ out:
+ /* release the iag free lock */
+ IAGFREE_UNLOCK(imap);
+
+ return (rc);
+}
+
+/*
+ * NAME: diIAGRead()
+ *
+ * FUNCTION: get the buffer for the specified iag within a fileset
+ * or aggregate inode map.
+ *
+ * PARAMETERS:
+ * imap - pointer to inode map control structure.
+ * iagno - iag number.
+ * bpp - point to buffer pointer to be filled in on successful
+ * exit.
+ *
+ * SERIALIZATION:
+ * must have read lock on imap inode
+ * (When called by diExtendFS, the filesystem is quiesced, therefore
+ * the read lock is unnecessary.)
+ *
+ * RETURN VALUES:
+ * 0 - success.
+ * -EIO - i/o error.
+ */
+static int diIAGRead(struct inomap * imap, int iagno, struct metapage ** mpp)
+{
+ struct inode *ipimap = imap->im_ipimap;
+ s64 blkno;
+
+ /* compute the logical block number of the iag. */
+ blkno = IAGTOLBLK(iagno, JFS_SBI(ipimap->i_sb)->l2nbperpage);
+
+ /* read the iag. */
+ *mpp = read_metapage(ipimap, blkno, PSIZE, 0);
+ if (*mpp == NULL) {
+ return -EIO;
+ }
+
+ return (0);
+}
+
+/*
+ * NAME: diFindFree()
+ *
+ * FUNCTION: find the first free bit in a word starting at
+ * the specified bit position.
+ *
+ * PARAMETERS:
+ * word - word to be examined.
+ * start - starting bit position.
+ *
+ * RETURN VALUES:
+ * bit position of first free bit in the word or 32 if
+ * no free bits were found.
+ */
+static int diFindFree(u32 word, int start)
+{
+ int bitno;
+ assert(start < 32);
+ /* scan the word for the first free bit. */
+ for (word <<= start, bitno = start; bitno < 32;
+ bitno++, word <<= 1) {
+ if ((word & HIGHORDER) == 0)
+ break;
+ }
+ return (bitno);
+}
+
+/*
+ * NAME: diUpdatePMap()
+ *
+ * FUNCTION: Update the persistent map in an IAG for the allocation or
+ * freeing of the specified inode.
+ *
+ * PRE CONDITIONS: Working map has already been updated for allocate.
+ *
+ * PARAMETERS:
+ * ipimap - Incore inode map inode
+ * inum - Number of inode to mark in permanent map
+ * is_free - If TRUE indicates inode should be marked freed, otherwise
+ * indicates inode should be marked allocated.
+ *
+ * RETURN VALUES:
+ * 0 for success
+ */
+int
+diUpdatePMap(struct inode *ipimap,
+ unsigned long inum, boolean_t is_free, struct tblock * tblk)
+{
+ int rc;
+ struct iag *iagp;
+ struct metapage *mp;
+ int iagno, ino, extno, bitno;
+ struct inomap *imap;
+ u32 mask;
+ struct jfs_log *log;
+ int lsn, difft, diffp;
+
+ imap = JFS_IP(ipimap)->i_imap;
+ /* get the iag number containing the inode */
+ iagno = INOTOIAG(inum);
+ /* make sure that the iag is contained within the map */
+ if (iagno >= imap->im_nextiag) {
+ jfs_error(ipimap->i_sb,
+ "diUpdatePMap: the iag is outside the map");
+ return -EIO;
+ }
+ /* read the iag */
+ IREAD_LOCK(ipimap);
+ rc = diIAGRead(imap, iagno, &mp);
+ IREAD_UNLOCK(ipimap);
+ if (rc)
+ return (rc);
+ iagp = (struct iag *) mp->data;
+ /* get the inode number and extent number of the inode within
+ * the iag and the inode number within the extent.
+ */
+ ino = inum & (INOSPERIAG - 1);
+ extno = ino >> L2INOSPEREXT;
+ bitno = ino & (INOSPEREXT - 1);
+ mask = HIGHORDER >> bitno;
+ /*
+ * mark the inode free in persistent map:
+ */
+ if (is_free == TRUE) {
+ /* The inode should have been allocated both in working
+ * map and in persistent map;
+ * the inode will be freed from working map at the release
+ * of last reference release;
+ */
+ if (!(le32_to_cpu(iagp->wmap[extno]) & mask)) {
+ jfs_error(ipimap->i_sb,
+ "diUpdatePMap: inode %ld not marked as "
+ "allocated in wmap!", inum);
+ }
+ if (!(le32_to_cpu(iagp->pmap[extno]) & mask)) {
+ jfs_error(ipimap->i_sb,
+ "diUpdatePMap: inode %ld not marked as "
+ "allocated in pmap!", inum);
+ }
+ /* update the bitmap for the extent of the freed inode */
+ iagp->pmap[extno] &= cpu_to_le32(~mask);
+ }
+ /*
+ * mark the inode allocated in persistent map:
+ */
+ else {
+ /* The inode should be already allocated in the working map
+ * and should be free in persistent map;
+ */
+ if (!(le32_to_cpu(iagp->wmap[extno]) & mask)) {
+ release_metapage(mp);
+ jfs_error(ipimap->i_sb,
+ "diUpdatePMap: the inode is not allocated in "
+ "the working map");
+ return -EIO;
+ }
+ if ((le32_to_cpu(iagp->pmap[extno]) & mask) != 0) {
+ release_metapage(mp);
+ jfs_error(ipimap->i_sb,
+ "diUpdatePMap: the inode is not free in the "
+ "persistent map");
+ return -EIO;
+ }
+ /* update the bitmap for the extent of the allocated inode */
+ iagp->pmap[extno] |= cpu_to_le32(mask);
+ }
+ /*
+ * update iag lsn
+ */
+ lsn = tblk->lsn;
+ log = JFS_SBI(tblk->sb)->log;
+ if (mp->lsn != 0) {
+ /* inherit older/smaller lsn */
+ logdiff(difft, lsn, log);
+ logdiff(diffp, mp->lsn, log);
+ if (difft < diffp) {
+ mp->lsn = lsn;
+ /* move mp after tblock in logsync list */
+ LOGSYNC_LOCK(log);
+ list_del(&mp->synclist);
+ list_add(&mp->synclist, &tblk->synclist);
+ LOGSYNC_UNLOCK(log);
+ }
+ /* inherit younger/larger clsn */
+ LOGSYNC_LOCK(log);
+ assert(mp->clsn);
+ logdiff(difft, tblk->clsn, log);
+ logdiff(diffp, mp->clsn, log);
+ if (difft > diffp)
+ mp->clsn = tblk->clsn;
+ LOGSYNC_UNLOCK(log);
+ } else {
+ mp->log = log;
+ mp->lsn = lsn;
+ /* insert mp after tblock in logsync list */
+ LOGSYNC_LOCK(log);
+ log->count++;
+ list_add(&mp->synclist, &tblk->synclist);
+ mp->clsn = tblk->clsn;
+ LOGSYNC_UNLOCK(log);
+ }
+ write_metapage(mp);
+ return (0);
+}
+
+/*
+ * diExtendFS()
+ *
+ * function: update imap for extendfs();
+ *
+ * note: AG size has been increased s.t. each k old contiguous AGs are
+ * coalesced into a new AG;
+ */
+int diExtendFS(struct inode *ipimap, struct inode *ipbmap)
+{
+ int rc, rcx = 0;
+ struct inomap *imap = JFS_IP(ipimap)->i_imap;
+ struct iag *iagp = 0, *hiagp = 0;
+ struct bmap *mp = JFS_SBI(ipbmap->i_sb)->bmap;
+ struct metapage *bp, *hbp;
+ int i, n, head;
+ int numinos, xnuminos = 0, xnumfree = 0;
+ s64 agstart;
+
+ jfs_info("diExtendFS: nextiag:%d numinos:%d numfree:%d",
+ imap->im_nextiag, atomic_read(&imap->im_numinos),
+ atomic_read(&imap->im_numfree));
+
+ /*
+ * reconstruct imap
+ *
+ * coalesce contiguous k (newAGSize/oldAGSize) AGs;
+ * i.e., (AGi, ..., AGj) where i = k*n and j = k*(n+1) - 1 to AGn;
+ * note: new AG size = old AG size * (2**x).
+ */
+
+ /* init per AG control information im_agctl[] */
+ for (i = 0; i < MAXAG; i++) {
+ imap->im_agctl[i].inofree = -1; /* free inode list */
+ imap->im_agctl[i].extfree = -1; /* free extent list */
+ imap->im_agctl[i].numinos = 0; /* number of backed inodes */
+ imap->im_agctl[i].numfree = 0; /* number of free backed inodes */
+ }
+
+ /*
+ * process each iag page of the map.
+ *
+ * rebuild AG Free Inode List, AG Free Inode Extent List;
+ */
+ for (i = 0; i < imap->im_nextiag; i++) {
+ if ((rc = diIAGRead(imap, i, &bp))) {
+ rcx = rc;
+ continue;
+ }
+ iagp = (struct iag *) bp->data;
+ if (le32_to_cpu(iagp->iagnum) != i) {
+ release_metapage(bp);
+ jfs_error(ipimap->i_sb,
+ "diExtendFs: unexpected value of iagnum");
+ return -EIO;
+ }
+
+ /* leave free iag in the free iag list */
+ if (iagp->nfreeexts == cpu_to_le32(EXTSPERIAG)) {
+ release_metapage(bp);
+ continue;
+ }
+
+ /* agstart that computes to the same ag is treated as same; */
+ agstart = le64_to_cpu(iagp->agstart);
+ /* iagp->agstart = agstart & ~(mp->db_agsize - 1); */
+ n = agstart >> mp->db_agl2size;
+
+ /* compute backed inodes */
+ numinos = (EXTSPERIAG - le32_to_cpu(iagp->nfreeexts))
+ << L2INOSPEREXT;
+ if (numinos > 0) {
+ /* merge AG backed inodes */
+ imap->im_agctl[n].numinos += numinos;
+ xnuminos += numinos;
+ }
+
+ /* if any backed free inodes, insert at AG free inode list */
+ if ((int) le32_to_cpu(iagp->nfreeinos) > 0) {
+ if ((head = imap->im_agctl[n].inofree) == -1)
+ iagp->inofreefwd = iagp->inofreeback = -1;
+ else {
+ if ((rc = diIAGRead(imap, head, &hbp))) {
+ rcx = rc;
+ goto nextiag;
+ }
+ hiagp = (struct iag *) hbp->data;
+ hiagp->inofreeback = iagp->iagnum;
+ iagp->inofreefwd = cpu_to_le32(head);
+ iagp->inofreeback = -1;
+ write_metapage(hbp);
+ }
+
+ imap->im_agctl[n].inofree =
+ le32_to_cpu(iagp->iagnum);
+
+ /* merge AG backed free inodes */
+ imap->im_agctl[n].numfree +=
+ le32_to_cpu(iagp->nfreeinos);
+ xnumfree += le32_to_cpu(iagp->nfreeinos);
+ }
+
+ /* if any free extents, insert at AG free extent list */
+ if (le32_to_cpu(iagp->nfreeexts) > 0) {
+ if ((head = imap->im_agctl[n].extfree) == -1)
+ iagp->extfreefwd = iagp->extfreeback = -1;
+ else {
+ if ((rc = diIAGRead(imap, head, &hbp))) {
+ rcx = rc;
+ goto nextiag;
+ }
+ hiagp = (struct iag *) hbp->data;
+ hiagp->extfreeback = iagp->iagnum;
+ iagp->extfreefwd = cpu_to_le32(head);
+ iagp->extfreeback = -1;
+ write_metapage(hbp);
+ }
+
+ imap->im_agctl[n].extfree =
+ le32_to_cpu(iagp->iagnum);
+ }
+
+ nextiag:
+ write_metapage(bp);
+ }
+
+ if (xnuminos != atomic_read(&imap->im_numinos) ||
+ xnumfree != atomic_read(&imap->im_numfree)) {
+ jfs_error(ipimap->i_sb,
+ "diExtendFs: numinos or numfree incorrect");
+ return -EIO;
+ }
+
+ return rcx;
+}
+
+
+/*
+ * duplicateIXtree()
+ *
+ * serialization: IWRITE_LOCK held on entry/exit
+ *
+ * note: shadow page with regular inode (rel.2);
+ */
+static void duplicateIXtree(struct super_block *sb, s64 blkno,
+ int xlen, s64 *xaddr)
+{
+ struct jfs_superblock *j_sb;
+ struct buffer_head *bh;
+ struct inode *ip;
+ tid_t tid;
+
+ /* if AIT2 ipmap2 is bad, do not try to update it */
+ if (JFS_SBI(sb)->mntflag & JFS_BAD_SAIT) /* s_flag */
+ return;
+ ip = diReadSpecial(sb, FILESYSTEM_I, 1);
+ if (ip == NULL) {
+ JFS_SBI(sb)->mntflag |= JFS_BAD_SAIT;
+ if (readSuper(sb, &bh))
+ return;
+ j_sb = (struct jfs_superblock *)bh->b_data;
+ j_sb->s_flag |= cpu_to_le32(JFS_BAD_SAIT);
+
+ mark_buffer_dirty(bh);
+ ll_rw_block(WRITE, 1, &bh);
+ wait_on_buffer(bh);
+ brelse(bh);
+ return;
+ }
+
+ /* start transaction */
+ tid = txBegin(sb, COMMIT_FORCE);
+ /* update the inode map addressing structure to point to it */
+ if (xtInsert(tid, ip, 0, blkno, xlen, xaddr, 0)) {
+ JFS_SBI(sb)->mntflag |= JFS_BAD_SAIT;
+ txAbort(tid, 1);
+ goto cleanup;
+
+ }
+ /* update the inode map's inode to reflect the extension */
+ ip->i_size += PSIZE;
+ ip->i_blocks += LBLK2PBLK(sb, xlen);
+ txCommit(tid, 1, &ip, COMMIT_FORCE);
+ cleanup:
+ txEnd(tid);
+ diFreeSpecial(ip);
+}
+
+/*
+ * NAME: copy_from_dinode()
+ *
+ * FUNCTION: Copies inode info from disk inode to in-memory inode
+ *
+ * RETURN VALUES:
+ * 0 - success
+ * -ENOMEM - insufficient memory
+ */
+static int copy_from_dinode(struct dinode * dip, struct inode *ip)
+{
+ struct jfs_inode_info *jfs_ip = JFS_IP(ip);
+
+ jfs_ip->fileset = le32_to_cpu(dip->di_fileset);
+ jfs_ip->mode2 = le32_to_cpu(dip->di_mode);
+
+ ip->i_mode = le32_to_cpu(dip->di_mode) & 0xffff;
+ ip->i_nlink = le32_to_cpu(dip->di_nlink);
+ ip->i_uid = le32_to_cpu(dip->di_uid);
+ ip->i_gid = le32_to_cpu(dip->di_gid);
+ ip->i_size = le64_to_cpu(dip->di_size);
+ ip->i_atime = le32_to_cpu(dip->di_atime.tv_sec);
+ ip->i_mtime = le32_to_cpu(dip->di_mtime.tv_sec);
+ ip->i_ctime = le32_to_cpu(dip->di_ctime.tv_sec);
+ ip->i_blksize = ip->i_sb->s_blocksize;
+ ip->i_blocks = LBLK2PBLK(ip->i_sb, le64_to_cpu(dip->di_nblocks));
+ ip->i_generation = le32_to_cpu(dip->di_gen);
+
+ jfs_ip->ixpxd = dip->di_ixpxd; /* in-memory pxd's are little-endian */
+ jfs_ip->acl = dip->di_acl; /* as are dxd's */
+ jfs_ip->ea = dip->di_ea;
+ jfs_ip->next_index = le32_to_cpu(dip->di_next_index);
+ jfs_ip->otime = le32_to_cpu(dip->di_otime.tv_sec);
+ jfs_ip->acltype = le32_to_cpu(dip->di_acltype);
+
+ if (S_ISCHR(ip->i_mode) || S_ISBLK(ip->i_mode))
+ ip->i_rdev = to_kdev_t(le32_to_cpu(dip->di_rdev));
+
+ if (S_ISDIR(ip->i_mode)) {
+ memcpy(&jfs_ip->i_dirtable, &dip->di_dirtable, 384);
+ } else if (S_ISREG(ip->i_mode) || S_ISLNK(ip->i_mode)) {
+ memcpy(&jfs_ip->i_xtroot, &dip->di_xtroot, 288);
+ } else
+ memcpy(&jfs_ip->i_inline_ea, &dip->di_inlineea, 128);
+
+ /* Zero the in-memory-only stuff */
+ jfs_ip->cflag = 0;
+ jfs_ip->btindex = 0;
+ jfs_ip->btorder = 0;
+ jfs_ip->bxflag = 0;
+ jfs_ip->blid = 0;
+ jfs_ip->atlhead = 0;
+ jfs_ip->atltail = 0;
+ jfs_ip->xtlid = 0;
+ return (0);
+}
+
+/*
+ * NAME: copy_to_dinode()
+ *
+ * FUNCTION: Copies inode info from in-memory inode to disk inode
+ */
+static void copy_to_dinode(struct dinode * dip, struct inode *ip)
+{
+ struct jfs_inode_info *jfs_ip = JFS_IP(ip);
+
+ dip->di_fileset = cpu_to_le32(jfs_ip->fileset);
+ dip->di_inostamp = cpu_to_le32(JFS_SBI(ip->i_sb)->inostamp);
+ dip->di_number = cpu_to_le32(ip->i_ino);
+ dip->di_gen = cpu_to_le32(ip->i_generation);
+ dip->di_size = cpu_to_le64(ip->i_size);
+ dip->di_nblocks = cpu_to_le64(PBLK2LBLK(ip->i_sb, ip->i_blocks));
+ dip->di_nlink = cpu_to_le32(ip->i_nlink);
+ dip->di_uid = cpu_to_le32(ip->i_uid);
+ dip->di_gid = cpu_to_le32(ip->i_gid);
+ /*
+ * mode2 is only needed for storing the higher order bits.
+ * Trust i_mode for the lower order ones
+ */
+ dip->di_mode = cpu_to_le32((jfs_ip->mode2 & 0xffff0000) | ip->i_mode);
+ dip->di_atime.tv_sec = cpu_to_le32(ip->i_atime);
+ dip->di_atime.tv_nsec = 0;
+ dip->di_ctime.tv_sec = cpu_to_le32(ip->i_ctime);
+ dip->di_ctime.tv_nsec = 0;
+ dip->di_mtime.tv_sec = cpu_to_le32(ip->i_mtime);
+ dip->di_mtime.tv_nsec = 0;
+ dip->di_ixpxd = jfs_ip->ixpxd; /* in-memory pxd's are little-endian */
+ dip->di_acl = jfs_ip->acl; /* as are dxd's */
+ dip->di_ea = jfs_ip->ea;
+ dip->di_next_index = cpu_to_le32(jfs_ip->next_index);
+ dip->di_otime.tv_sec = cpu_to_le32(jfs_ip->otime);
+ dip->di_otime.tv_nsec = 0;
+ dip->di_acltype = cpu_to_le32(jfs_ip->acltype);
+
+ if (S_ISCHR(ip->i_mode) || S_ISBLK(ip->i_mode))
+ dip->di_rdev = cpu_to_le32(kdev_t_to_nr(ip->i_rdev));
+}
+
+#ifdef _JFS_DEBUG_IMAP
+/*
+ * DBGdiInit()
+ */
+static void *DBGdiInit(struct inomap * imap)
+{
+ u32 *dimap;
+ int size;
+ size = 64 * 1024;
+ if ((dimap = (u32 *) xmalloc(size, L2PSIZE, kernel_heap)) == NULL)
+ assert(0);
+ bzero((void *) dimap, size);
+ imap->im_DBGdimap = dimap;
+}
+
+/*
+ * DBGdiAlloc()
+ */
+static void DBGdiAlloc(struct inomap * imap, ino_t ino)
+{
+ u32 *dimap = imap->im_DBGdimap;
+ int w, b;
+ u32 m;
+ w = ino >> 5;
+ b = ino & 31;
+ m = 0x80000000 >> b;
+ assert(w < 64 * 256);
+ if (dimap[w] & m) {
+ printk("DEBUG diAlloc: duplicate alloc ino:0x%x\n", ino);
+ }
+ dimap[w] |= m;
+}
+
+/*
+ * DBGdiFree()
+ */
+static void DBGdiFree(struct inomap * imap, ino_t ino)
+{
+ u32 *dimap = imap->im_DBGdimap;
+ int w, b;
+ u32 m;
+ w = ino >> 5;
+ b = ino & 31;
+ m = 0x80000000 >> b;
+ assert(w < 64 * 256);
+ if ((dimap[w] & m) == 0) {
+ printk("DEBUG diFree: duplicate free ino:0x%x\n", ino);
+ }
+ dimap[w] &= ~m;
+}
+
+static void dump_cp(struct inomap * ipimap, char *function, int line)
+{
+ printk("\n* ********* *\nControl Page %s %d\n", function, line);
+ printk("FreeIAG %d\tNextIAG %d\n", ipimap->im_freeiag,
+ ipimap->im_nextiag);
+ printk("NumInos %d\tNumFree %d\n",
+ atomic_read(&ipimap->im_numinos),
+ atomic_read(&ipimap->im_numfree));
+ printk("AG InoFree %d\tAG ExtFree %d\n",
+ ipimap->im_agctl[0].inofree, ipimap->im_agctl[0].extfree);
+ printk("AG NumInos %d\tAG NumFree %d\n",
+ ipimap->im_agctl[0].numinos, ipimap->im_agctl[0].numfree);
+}
+
+static void dump_iag(struct iag * iag, char *function, int line)
+{
+ printk("\n* ********* *\nIAG %s %d\n", function, line);
+ printk("IagNum %d\tIAG Free %d\n", le32_to_cpu(iag->iagnum),
+ le32_to_cpu(iag->iagfree));
+ printk("InoFreeFwd %d\tInoFreeBack %d\n",
+ le32_to_cpu(iag->inofreefwd),
+ le32_to_cpu(iag->inofreeback));
+ printk("ExtFreeFwd %d\tExtFreeBack %d\n",
+ le32_to_cpu(iag->extfreefwd),
+ le32_to_cpu(iag->extfreeback));
+ printk("NFreeInos %d\tNFreeExts %d\n", le32_to_cpu(iag->nfreeinos),
+ le32_to_cpu(iag->nfreeexts));
+}
+#endif /* _JFS_DEBUG_IMAP */
diff --git a/fs/jfs/jfs_imap.h b/fs/jfs/jfs_imap.h
new file mode 100644
index 00000000000000..41255872172b86
--- /dev/null
+++ b/fs/jfs/jfs_imap.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2000-2002
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _H_JFS_IMAP
+#define _H_JFS_IMAP
+
+#include "jfs_txnmgr.h"
+
+/*
+ * jfs_imap.h: disk inode manager
+ */
+
+#define EXTSPERIAG 128 /* number of disk inode extent per iag */
+#define IMAPBLKNO 0 /* lblkno of dinomap within inode map */
+#define SMAPSZ 4 /* number of words per summary map */
+#define EXTSPERSUM 32 /* number of extents per summary map entry */
+#define L2EXTSPERSUM 5 /* l2 number of extents per summary map */
+#define PGSPERIEXT 4 /* number of 4K pages per dinode extent */
+#define MAXIAGS ((1<<20)-1) /* maximum number of iags */
+#define MAXAG 128 /* maximum number of allocation groups */
+
+#define AMAPSIZE 512 /* bytes in the IAG allocation maps */
+#define SMAPSIZE 16 /* bytes in the IAG summary maps */
+
+/* convert inode number to iag number */
+#define INOTOIAG(ino) ((ino) >> L2INOSPERIAG)
+
+/* convert iag number to logical block number of the iag page */
+#define IAGTOLBLK(iagno,l2nbperpg) (((iagno) + 1) << (l2nbperpg))
+
+/* get the starting block number of the 4K page of an inode extent
+ * that contains ino.
+ */
+#define INOPBLK(pxd,ino,l2nbperpg) (addressPXD((pxd)) + \
+ ((((ino) & (INOSPEREXT-1)) >> L2INOSPERPAGE) << (l2nbperpg)))
+
+/*
+ * inode allocation map:
+ *
+ * inode allocation map consists of
+ * . the inode map control page and
+ * . inode allocation group pages (per 4096 inodes)
+ * which are addressed by standard JFS xtree.
+ */
+/*
+ * inode allocation group page (per 4096 inodes of an AG)
+ */
+struct iag {
+ s64 agstart; /* 8: starting block of ag */
+ s32 iagnum; /* 4: inode allocation group number */
+ s32 inofreefwd; /* 4: ag inode free list forward */
+ s32 inofreeback; /* 4: ag inode free list back */
+ s32 extfreefwd; /* 4: ag inode extent free list forward */
+ s32 extfreeback; /* 4: ag inode extent free list back */
+ s32 iagfree; /* 4: iag free list */
+
+ /* summary map: 1 bit per inode extent */
+ s32 inosmap[SMAPSZ]; /* 16: sum map of mapwords w/ free inodes;
+ * note: this indicates free and backed
+ * inodes, if the extent is not backed the
+ * value will be 1. if the extent is
+ * backed but all inodes are being used the
+ * value will be 1. if the extent is
+ * backed but at least one of the inodes is
+ * free the value will be 0.
+ */
+ s32 extsmap[SMAPSZ]; /* 16: sum map of mapwords w/ free extents */
+ s32 nfreeinos; /* 4: number of free inodes */
+ s32 nfreeexts; /* 4: number of free extents */
+ /* (72) */
+ u8 pad[1976]; /* 1976: pad to 2048 bytes */
+ /* allocation bit map: 1 bit per inode (0 - free, 1 - allocated) */
+ u32 wmap[EXTSPERIAG]; /* 512: working allocation map */
+ u32 pmap[EXTSPERIAG]; /* 512: persistent allocation map */
+ pxd_t inoext[EXTSPERIAG]; /* 1024: inode extent addresses */
+}; /* (4096) */
+
+/*
+ * per AG control information (in inode map control page)
+ */
+struct iagctl {
+ s32 inofree; /* 4: free inode list anchor */
+ s32 extfree; /* 4: free extent list anchor */
+ s32 numinos; /* 4: number of backed inodes */
+ s32 numfree; /* 4: number of free inodes */
+}; /* (16) */
+
+/*
+ * per fileset/aggregate inode map control page
+ */
+struct dinomap {
+ s32 in_freeiag; /* 4: free iag list anchor */
+ s32 in_nextiag; /* 4: next free iag number */
+ s32 in_numinos; /* 4: num of backed inodes */
+ s32 in_numfree; /* 4: num of free backed inodes */
+ s32 in_nbperiext; /* 4: num of blocks per inode extent */
+ s32 in_l2nbperiext; /* 4: l2 of in_nbperiext */
+ s32 in_diskblock; /* 4: for standalone test driver */
+ s32 in_maxag; /* 4: for standalone test driver */
+ u8 pad[2016]; /* 2016: pad to 2048 */
+ struct iagctl in_agctl[MAXAG]; /* 2048: AG control information */
+}; /* (4096) */
+
+
+/*
+ * In-core inode map control page
+ */
+struct inomap {
+ struct dinomap im_imap; /* 4096: inode allocation control */
+ struct inode *im_ipimap; /* 4: ptr to inode for imap */
+ struct semaphore im_freelock; /* 4: iag free list lock */
+ struct semaphore im_aglock[MAXAG]; /* 512: per AG locks */
+ u32 *im_DBGdimap;
+ atomic_t im_numinos; /* num of backed inodes */
+ atomic_t im_numfree; /* num of free backed inodes */
+};
+
+#define im_freeiag im_imap.in_freeiag
+#define im_nextiag im_imap.in_nextiag
+#define im_agctl im_imap.in_agctl
+#define im_nbperiext im_imap.in_nbperiext
+#define im_l2nbperiext im_imap.in_l2nbperiext
+
+/* for standalone testdriver
+ */
+#define im_diskblock im_imap.in_diskblock
+#define im_maxag im_imap.in_maxag
+
+extern int diFree(struct inode *);
+extern int diAlloc(struct inode *, boolean_t, struct inode *);
+extern int diSync(struct inode *);
+/* external references */
+extern int diUpdatePMap(struct inode *ipimap, unsigned long inum,
+ boolean_t is_free, struct tblock * tblk);
+extern int diExtendFS(struct inode *ipimap, struct inode *ipbmap);
+extern int diMount(struct inode *);
+extern int diUnmount(struct inode *, int);
+extern int diRead(struct inode *);
+extern struct inode *diReadSpecial(struct super_block *, ino_t, int);
+extern void diWriteSpecial(struct inode *, int);
+extern void diFreeSpecial(struct inode *);
+extern int diWrite(tid_t tid, struct inode *);
+#endif /* _H_JFS_IMAP */
diff --git a/fs/jfs/jfs_incore.h b/fs/jfs/jfs_incore.h
new file mode 100644
index 00000000000000..fc422b24eb69d4
--- /dev/null
+++ b/fs/jfs/jfs_incore.h
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2000-2003
+ * Portions Copyright (c) Christoph Hellwig, 2001-2002
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _H_JFS_INCORE
+#define _H_JFS_INCORE
+
+#include <linux/rwsem.h>
+#include <linux/slab.h>
+#include <asm/bitops.h>
+#include "jfs_types.h"
+#include "jfs_xtree.h"
+#include "jfs_dtree.h"
+
+/*
+ * JFS magic number
+ */
+#define JFS_SUPER_MAGIC 0x3153464a /* "JFS1" */
+
+/*
+ * JFS-private inode information
+ */
+struct jfs_inode_info {
+ struct inode *inode; /* pointer back to fs-independent inode */
+ int fileset; /* fileset number (always 16)*/
+ uint mode2; /* jfs-specific mode */
+ pxd_t ixpxd; /* inode extent descriptor */
+ dxd_t acl; /* dxd describing acl */
+ dxd_t ea; /* dxd describing ea */
+ time_t otime; /* time created */
+ uint next_index; /* next available directory entry index */
+ int acltype; /* Type of ACL */
+ short btorder; /* access order */
+ short btindex; /* btpage entry index*/
+ struct inode *ipimap; /* inode map */
+ long cflag; /* commit flags */
+ u16 bxflag; /* xflag of pseudo buffer? */
+ unchar agno; /* ag number */
+ signed char active_ag; /* ag currently allocating from */
+ lid_t blid; /* lid of pseudo buffer? */
+ lid_t atlhead; /* anonymous tlock list head */
+ lid_t atltail; /* anonymous tlock list tail */
+ spinlock_t ag_lock; /* protects active_ag */
+ struct list_head anon_inode_list; /* inodes having anonymous txns */
+ /*
+ * rdwrlock serializes xtree between reads & writes and synchronizes
+ * changes to special inodes. It's use would be redundant on
+ * directories since the i_sem taken in the VFS is sufficient.
+ */
+ struct rw_semaphore rdwrlock;
+ /*
+ * commit_sem serializes transaction processing on an inode.
+ * It must be taken after beginning a transaction (txBegin), since
+ * dirty inodes may be committed while a new transaction on the
+ * inode is blocked in txBegin or TxBeginAnon
+ */
+ struct semaphore commit_sem;
+ lid_t xtlid; /* lid of xtree lock on directory */
+ union {
+ struct {
+ xtpage_t _xtroot; /* 288: xtree root */
+ struct inomap *_imap; /* 4: inode map header */
+ } file;
+ struct {
+ struct dir_table_slot _table[12]; /* 96: dir index */
+ dtroot_t _dtroot; /* 288: dtree root */
+ } dir;
+ struct {
+ unchar _unused[16]; /* 16: */
+ dxd_t _dxd; /* 16: */
+ unchar _inline[128]; /* 128: inline symlink */
+ /* _inline_ea may overlay the last part of
+ * file._xtroot if maxentry = XTROOTINITSLOT
+ */
+ unchar _inline_ea[128]; /* 128: inline extended attr */
+ } link;
+ } u;
+};
+#define i_xtroot u.file._xtroot
+#define i_imap u.file._imap
+#define i_dirtable u.dir._table
+#define i_dtroot u.dir._dtroot
+#define i_inline u.link._inline
+#define i_inline_ea u.link._inline_ea
+
+
+#define IREAD_LOCK(ip) down_read(&JFS_IP(ip)->rdwrlock)
+#define IREAD_UNLOCK(ip) up_read(&JFS_IP(ip)->rdwrlock)
+#define IWRITE_LOCK(ip) down_write(&JFS_IP(ip)->rdwrlock)
+#define IWRITE_UNLOCK(ip) up_write(&JFS_IP(ip)->rdwrlock)
+
+/*
+ * cflag
+ */
+enum cflags {
+ COMMIT_New, /* never committed inode */
+ COMMIT_Nolink, /* inode committed with zero link count */
+ COMMIT_Inlineea, /* commit inode inline EA */
+ COMMIT_Freewmap, /* free WMAP at iClose() */
+ COMMIT_Dirty, /* Inode is really dirty */
+ COMMIT_Dirtable, /* commit changes to di_dirtable */
+ COMMIT_Stale, /* data extent is no longer valid */
+ COMMIT_Synclist, /* metadata pages on group commit synclist */
+ COMMIT_Syncdata, /* Data must be synced before inode committed */
+};
+
+#define set_cflag(flag, ip) set_bit(flag, &(JFS_IP(ip)->cflag))
+#define clear_cflag(flag, ip) clear_bit(flag, &(JFS_IP(ip)->cflag))
+#define test_cflag(flag, ip) test_bit(flag, &(JFS_IP(ip)->cflag))
+#define test_and_clear_cflag(flag, ip) \
+ test_and_clear_bit(flag, &(JFS_IP(ip)->cflag))
+/*
+ * JFS-private superblock information.
+ */
+struct jfs_sb_info {
+ unsigned long mntflag; /* aggregate attributes */
+ struct inode *ipbmap; /* block map inode */
+ struct inode *ipaimap; /* aggregate inode map inode */
+ struct inode *ipaimap2; /* secondary aimap inode */
+ struct inode *ipimap; /* aggregate inode map inode */
+ struct jfs_log *log; /* log */
+ short bsize; /* logical block size */
+ short l2bsize; /* log2 logical block size */
+ short nbperpage; /* blocks per page */
+ short l2nbperpage; /* log2 blocks per page */
+ short l2niperblk; /* log2 inodes per page */
+ kdev_t logdev; /* external log device */
+ uint aggregate; /* volume identifier in log record */
+ pxd_t logpxd; /* pxd describing log */
+ pxd_t fsckpxd; /* pxd describing fsck wkspc */
+ pxd_t ait2; /* pxd describing AIT copy */
+ char uuid[16]; /* 128-bit uuid for volume */
+ char loguuid[16]; /* 128-bit uuid for log */
+ /* Formerly in ipimap */
+ uint gengen; /* inode generation generator*/
+ uint inostamp; /* shows inode belongs to fileset*/
+
+ /* Formerly in ipbmap */
+ struct bmap *bmap; /* incore bmap descriptor */
+ struct nls_table *nls_tab; /* current codepage */
+ uint state; /* mount/recovery state */
+ unsigned long flag; /* mount time flags */
+ uint p_state; /* state prior to going no integrity */
+};
+
+static inline struct jfs_inode_info *JFS_IP(struct inode *inode)
+{
+ return inode->u.generic_ip;
+}
+
+static inline struct jfs_sb_info *JFS_SBI(struct super_block *sb)
+{
+ return sb->u.generic_sbp;
+}
+
+static inline int isReadOnly(struct inode *inode)
+{
+ if (JFS_SBI(inode->i_sb)->log)
+ return 0;
+ return 1;
+}
+
+/*
+ * Allocating and freeing the structure
+ */
+extern kmem_cache_t *jfs_inode_cachep;
+extern int alloc_jfs_inode(struct inode *);
+
+#define free_jfs_inode(inode) \
+ kmem_cache_free(jfs_inode_cachep, (inode)->u.generic_ip)
+
+#endif /* _H_JFS_INCORE */
diff --git a/fs/jfs/jfs_inode.c b/fs/jfs/jfs_inode.c
new file mode 100644
index 00000000000000..412dcf54b6a075
--- /dev/null
+++ b/fs/jfs/jfs_inode.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2000-2002
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/fs.h>
+#include "jfs_incore.h"
+#include "jfs_filsys.h"
+#include "jfs_imap.h"
+#include "jfs_dinode.h"
+#include "jfs_debug.h"
+
+kmem_cache_t *jfs_inode_cachep;
+
+/*
+ * NAME: ialloc()
+ *
+ * FUNCTION: Allocate a new inode
+ *
+ */
+struct inode *ialloc(struct inode *parent, umode_t mode)
+{
+ struct super_block *sb = parent->i_sb;
+ struct inode *inode;
+ struct jfs_inode_info *jfs_inode;
+ int rc;
+
+ inode = new_inode(sb);
+ if (!inode) {
+ jfs_warn("ialloc: new_inode returned NULL!");
+ return inode;
+ }
+
+ rc = alloc_jfs_inode(inode);
+ if (rc) {
+ make_bad_inode(inode);
+ iput(inode);
+ return NULL;
+ }
+ jfs_inode = JFS_IP(inode);
+
+ rc = diAlloc(parent, S_ISDIR(mode), inode);
+ if (rc) {
+ jfs_warn("ialloc: diAlloc returned %d!", rc);
+ free_jfs_inode(inode);
+ make_bad_inode(inode);
+ iput(inode);
+ return NULL;
+ }
+
+ inode->i_uid = current->fsuid;
+ if (parent->i_mode & S_ISGID) {
+ inode->i_gid = parent->i_gid;
+ if (S_ISDIR(mode))
+ mode |= S_ISGID;
+ } else
+ inode->i_gid = current->fsgid;
+
+ inode->i_mode = mode;
+ if (S_ISDIR(mode))
+ jfs_inode->mode2 = IDIRECTORY | mode;
+ else
+ jfs_inode->mode2 = INLINEEA | ISPARSE | mode;
+ inode->i_blksize = sb->s_blocksize;
+ inode->i_blocks = 0;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+ jfs_inode->otime = inode->i_ctime;
+ inode->i_generation = JFS_SBI(sb)->gengen++;
+
+ jfs_inode->cflag = 0;
+ set_cflag(COMMIT_New, inode);
+
+ /* Zero remaining fields */
+ memset(&jfs_inode->acl, 0, sizeof(dxd_t));
+ memset(&jfs_inode->ea, 0, sizeof(dxd_t));
+ jfs_inode->next_index = 0;
+ jfs_inode->acltype = 0;
+ jfs_inode->btorder = 0;
+ jfs_inode->btindex = 0;
+ jfs_inode->bxflag = 0;
+ jfs_inode->blid = 0;
+ jfs_inode->atlhead = 0;
+ jfs_inode->atltail = 0;
+ jfs_inode->xtlid = 0;
+
+ jfs_info("ialloc returns inode = 0x%p\n", inode);
+
+ return inode;
+}
+
+/*
+ * NAME: alloc_jfs_inode()
+ *
+ * FUNCTION: Allocate jfs portion of in-memory inode
+ *
+ */
+int alloc_jfs_inode(struct inode *inode)
+{
+ struct jfs_inode_info *jfs_inode;
+
+ jfs_inode = kmem_cache_alloc(jfs_inode_cachep, GFP_NOFS);
+ inode->u.generic_ip = jfs_inode;
+ if (!jfs_inode)
+ return -ENOSPC;
+ jfs_inode->inode = inode;
+
+ return 0;
+}
diff --git a/fs/jfs/jfs_inode.h b/fs/jfs/jfs_inode.h
new file mode 100644
index 00000000000000..3df91fbfe7812e
--- /dev/null
+++ b/fs/jfs/jfs_inode.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2000-2001
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _H_JFS_INODE
+#define _H_JFS_INODE
+
+extern struct inode *ialloc(struct inode *, umode_t);
+
+#endif /* _H_JFS_INODE */
diff --git a/fs/jfs/jfs_lock.h b/fs/jfs/jfs_lock.h
new file mode 100644
index 00000000000000..10ad1d08668562
--- /dev/null
+++ b/fs/jfs/jfs_lock.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2000-2001
+ * Portions Copyright (c) Christoph Hellwig, 2001-2002
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _H_JFS_LOCK
+#define _H_JFS_LOCK
+
+#include <linux/spinlock.h>
+#include <linux/sched.h>
+
+/*
+ * jfs_lock.h
+ */
+
+/*
+ * Conditional sleep where condition is protected by spinlock
+ *
+ * lock_cmd and unlock_cmd take and release the spinlock
+ */
+#define __SLEEP_COND(wq, cond, lock_cmd, unlock_cmd) \
+do { \
+ DECLARE_WAITQUEUE(__wait, current); \
+ \
+ add_wait_queue(&wq, &__wait); \
+ for (;;) { \
+ set_current_state(TASK_UNINTERRUPTIBLE);\
+ if (cond) \
+ break; \
+ unlock_cmd; \
+ schedule(); \
+ lock_cmd; \
+ } \
+ current->state = TASK_RUNNING; \
+ remove_wait_queue(&wq, &__wait); \
+} while (0)
+
+#endif /* _H_JFS_LOCK */
diff --git a/fs/jfs/jfs_logmgr.c b/fs/jfs/jfs_logmgr.c
new file mode 100644
index 00000000000000..bd23204ad99104
--- /dev/null
+++ b/fs/jfs/jfs_logmgr.c
@@ -0,0 +1,2392 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2000-2003
+ * Portions Copyright (c) Christoph Hellwig, 2001-2002
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * jfs_logmgr.c: log manager
+ *
+ * for related information, see transaction manager (jfs_txnmgr.c), and
+ * recovery manager (jfs_logredo.c).
+ *
+ * note: for detail, RTFS.
+ *
+ * log buffer manager:
+ * special purpose buffer manager supporting log i/o requirements.
+ * per log serial pageout of logpage
+ * queuing i/o requests and redrive i/o at iodone
+ * maintain current logpage buffer
+ * no caching since append only
+ * appropriate jfs buffer cache buffers as needed
+ *
+ * group commit:
+ * transactions which wrote COMMIT records in the same in-memory
+ * log page during the pageout of previous/current log page(s) are
+ * committed together by the pageout of the page.
+ *
+ * TBD lazy commit:
+ * transactions are committed asynchronously when the log page
+ * containing it COMMIT is paged out when it becomes full;
+ *
+ * serialization:
+ * . a per log lock serialize log write.
+ * . a per log lock serialize group commit.
+ * . a per log lock serialize log open/close;
+ *
+ * TBD log integrity:
+ * careful-write (ping-pong) of last logpage to recover from crash
+ * in overwrite.
+ * detection of split (out-of-order) write of physical sectors
+ * of last logpage via timestamp at end of each sector
+ * with its mirror data array at trailer).
+ *
+ * alternatives:
+ * lsn - 64-bit monotonically increasing integer vs
+ * 32-bit lspn and page eor.
+ */
+
+#include <linux/fs.h>
+#include <linux/locks.h>
+#include <linux/blkdev.h>
+#include <linux/interrupt.h>
+#include <linux/smp_lock.h>
+#include <linux/completion.h>
+#include "jfs_incore.h"
+#include "jfs_filsys.h"
+#include "jfs_metapage.h"
+#include "jfs_txnmgr.h"
+#include "jfs_debug.h"
+
+
+/*
+ * lbuf's ready to be redriven. Protected by log_redrive_lock (jfsIO thread)
+ */
+static struct lbuf *log_redrive_list;
+static spinlock_t log_redrive_lock = SPIN_LOCK_UNLOCKED;
+DECLARE_WAIT_QUEUE_HEAD(jfs_IO_thread_wait);
+
+
+/*
+ * log read/write serialization (per log)
+ */
+#define LOG_LOCK_INIT(log) init_MUTEX(&(log)->loglock)
+#define LOG_LOCK(log) down(&((log)->loglock))
+#define LOG_UNLOCK(log) up(&((log)->loglock))
+
+
+/*
+ * log group commit serialization (per log)
+ */
+
+#define LOGGC_LOCK_INIT(log) spin_lock_init(&(log)->gclock)
+#define LOGGC_LOCK(log) spin_lock_irq(&(log)->gclock)
+#define LOGGC_UNLOCK(log) spin_unlock_irq(&(log)->gclock)
+#define LOGGC_WAKEUP(tblk) wake_up_all(&(tblk)->gcwait)
+
+/*
+ * log sync serialization (per log)
+ */
+#define LOGSYNC_DELTA(logsize) min((logsize)/8, 128*LOGPSIZE)
+#define LOGSYNC_BARRIER(logsize) ((logsize)/4)
+/*
+#define LOGSYNC_DELTA(logsize) min((logsize)/4, 256*LOGPSIZE)
+#define LOGSYNC_BARRIER(logsize) ((logsize)/2)
+*/
+
+
+/*
+ * log buffer cache synchronization
+ */
+static spinlock_t jfsLCacheLock = SPIN_LOCK_UNLOCKED;
+
+#define LCACHE_LOCK(flags) spin_lock_irqsave(&jfsLCacheLock, flags)
+#define LCACHE_UNLOCK(flags) spin_unlock_irqrestore(&jfsLCacheLock, flags)
+
+/*
+ * See __SLEEP_COND in jfs_locks.h
+ */
+#define LCACHE_SLEEP_COND(wq, cond, flags) \
+do { \
+ if (cond) \
+ break; \
+ __SLEEP_COND(wq, cond, LCACHE_LOCK(flags), LCACHE_UNLOCK(flags)); \
+} while (0)
+
+#define LCACHE_WAKEUP(event) wake_up(event)
+
+
+/*
+ * lbuf buffer cache (lCache) control
+ */
+/* log buffer manager pageout control (cumulative, inclusive) */
+#define lbmREAD 0x0001
+#define lbmWRITE 0x0002 /* enqueue at tail of write queue;
+ * init pageout if at head of queue;
+ */
+#define lbmRELEASE 0x0004 /* remove from write queue
+ * at completion of pageout;
+ * do not free/recycle it yet:
+ * caller will free it;
+ */
+#define lbmSYNC 0x0008 /* do not return to freelist
+ * when removed from write queue;
+ */
+#define lbmFREE 0x0010 /* return to freelist
+ * at completion of pageout;
+ * the buffer may be recycled;
+ */
+#define lbmDONE 0x0020
+#define lbmERROR 0x0040
+#define lbmGC 0x0080 /* lbmIODone to perform post-GC processing
+ * of log page
+ */
+#define lbmDIRECT 0x0100
+
+/*
+ * external references
+ */
+extern void txLazyUnlock(struct tblock * tblk);
+extern int jfs_stop_threads;
+extern struct completion jfsIOwait;
+
+/*
+ * forward references
+ */
+static int lmWriteRecord(struct jfs_log * log, struct tblock * tblk,
+ struct lrd * lrd, struct tlock * tlck);
+
+static int lmNextPage(struct jfs_log * log);
+static int lmLogFileSystem(struct jfs_log * log, char *uuid, int activate);
+
+static int lbmLogInit(struct jfs_log * log);
+static void lbmLogShutdown(struct jfs_log * log);
+static struct lbuf *lbmAllocate(struct jfs_log * log, int);
+static void lbmFree(struct lbuf * bp);
+static void lbmfree(struct lbuf * bp);
+static int lbmRead(struct jfs_log * log, int pn, struct lbuf ** bpp);
+static void lbmWrite(struct jfs_log * log, struct lbuf * bp, int flag,
+ int cant_block);
+static void lbmDirectWrite(struct jfs_log * log, struct lbuf * bp, int flag);
+static int lbmIOWait(struct lbuf * bp, int flag);
+static void lbmIODone(struct buffer_head *bh, int);
+static void lbmStartIO(struct lbuf * bp);
+static void lmGCwrite(struct jfs_log * log, int cant_block);
+static int lmLogSync(struct jfs_log * log, int nosyncwait);
+
+
+/*
+ * statistics
+ */
+#ifdef CONFIG_JFS_STATISTICS
+struct lmStat {
+ uint commit; /* # of commit */
+ uint pagedone; /* # of page written */
+ uint submitted; /* # of pages submitted */
+ uint full_page; /* # of full pages submitted */
+ uint partial_page; /* # of partial pages submitted */
+} lmStat;
+#endif
+
+
+/*
+ * NAME: lmLog()
+ *
+ * FUNCTION: write a log record;
+ *
+ * PARAMETER:
+ *
+ * RETURN: lsn - offset to the next log record to write (end-of-log);
+ * -1 - error;
+ *
+ * note: todo: log error handler
+ */
+int lmLog(struct jfs_log * log, struct tblock * tblk, struct lrd * lrd,
+ struct tlock * tlck)
+{
+ int lsn;
+ int diffp, difft;
+ struct metapage *mp = NULL;
+
+ jfs_info("lmLog: log:0x%p tblk:0x%p, lrd:0x%p tlck:0x%p",
+ log, tblk, lrd, tlck);
+
+ LOG_LOCK(log);
+
+ /* log by (out-of-transaction) JFS ? */
+ if (tblk == NULL)
+ goto writeRecord;
+
+ /* log from page ? */
+ if (tlck == NULL ||
+ tlck->type & tlckBTROOT || (mp = tlck->mp) == NULL)
+ goto writeRecord;
+
+ /*
+ * initialize/update page/transaction recovery lsn
+ */
+ lsn = log->lsn;
+
+ LOGSYNC_LOCK(log);
+
+ /*
+ * initialize page lsn if first log write of the page
+ */
+ if (mp->lsn == 0) {
+ mp->log = log;
+ mp->lsn = lsn;
+ log->count++;
+
+ /* insert page at tail of logsynclist */
+ list_add_tail(&mp->synclist, &log->synclist);
+ }
+
+ /*
+ * initialize/update lsn of tblock of the page
+ *
+ * transaction inherits oldest lsn of pages associated
+ * with allocation/deallocation of resources (their
+ * log records are used to reconstruct allocation map
+ * at recovery time: inode for inode allocation map,
+ * B+-tree index of extent descriptors for block
+ * allocation map);
+ * allocation map pages inherit transaction lsn at
+ * commit time to allow forwarding log syncpt past log
+ * records associated with allocation/deallocation of
+ * resources only after persistent map of these map pages
+ * have been updated and propagated to home.
+ */
+ /*
+ * initialize transaction lsn:
+ */
+ if (tblk->lsn == 0) {
+ /* inherit lsn of its first page logged */
+ tblk->lsn = mp->lsn;
+ log->count++;
+
+ /* insert tblock after the page on logsynclist */
+ list_add(&tblk->synclist, &mp->synclist);
+ }
+ /*
+ * update transaction lsn:
+ */
+ else {
+ /* inherit oldest/smallest lsn of page */
+ logdiff(diffp, mp->lsn, log);
+ logdiff(difft, tblk->lsn, log);
+ if (diffp < difft) {
+ /* update tblock lsn with page lsn */
+ tblk->lsn = mp->lsn;
+
+ /* move tblock after page on logsynclist */
+ list_del(&tblk->synclist);
+ list_add(&tblk->synclist, &mp->synclist);
+ }
+ }
+
+ LOGSYNC_UNLOCK(log);
+
+ /*
+ * write the log record
+ */
+ writeRecord:
+ lsn = lmWriteRecord(log, tblk, lrd, tlck);
+
+ /*
+ * forward log syncpt if log reached next syncpt trigger
+ */
+ logdiff(diffp, lsn, log);
+ if (diffp >= log->nextsync)
+ lsn = lmLogSync(log, 0);
+
+ /* update end-of-log lsn */
+ log->lsn = lsn;
+
+ LOG_UNLOCK(log);
+
+ /* return end-of-log address */
+ return lsn;
+}
+
+
+/*
+ * NAME: lmWriteRecord()
+ *
+ * FUNCTION: move the log record to current log page
+ *
+ * PARAMETER: cd - commit descriptor
+ *
+ * RETURN: end-of-log address
+ *
+ * serialization: LOG_LOCK() held on entry/exit
+ */
+static int
+lmWriteRecord(struct jfs_log * log, struct tblock * tblk, struct lrd * lrd,
+ struct tlock * tlck)
+{
+ int lsn = 0; /* end-of-log address */
+ struct lbuf *bp; /* dst log page buffer */
+ struct logpage *lp; /* dst log page */
+ caddr_t dst; /* destination address in log page */
+ int dstoffset; /* end-of-log offset in log page */
+ int freespace; /* free space in log page */
+ caddr_t p; /* src meta-data page */
+ caddr_t src;
+ int srclen;
+ int nbytes; /* number of bytes to move */
+ int i;
+ int len;
+ struct linelock *linelock;
+ struct lv *lv;
+ struct lvd *lvd;
+ int l2linesize;
+
+ len = 0;
+
+ /* retrieve destination log page to write */
+ bp = (struct lbuf *) log->bp;
+ lp = (struct logpage *) bp->l_ldata;
+ dstoffset = log->eor;
+
+ /* any log data to write ? */
+ if (tlck == NULL)
+ goto moveLrd;
+
+ /*
+ * move log record data
+ */
+ /* retrieve source meta-data page to log */
+ if (tlck->flag & tlckPAGELOCK) {
+ p = (caddr_t) (tlck->mp->data);
+ linelock = (struct linelock *) & tlck->lock;
+ }
+ /* retrieve source in-memory inode to log */
+ else if (tlck->flag & tlckINODELOCK) {
+ if (tlck->type & tlckDTREE)
+ p = (caddr_t) &JFS_IP(tlck->ip)->i_dtroot;
+ else
+ p = (caddr_t) &JFS_IP(tlck->ip)->i_xtroot;
+ linelock = (struct linelock *) & tlck->lock;
+ }
+#ifdef _JFS_WIP
+ else if (tlck->flag & tlckINLINELOCK) {
+
+ inlinelock = (struct inlinelock *) & tlck;
+ p = (caddr_t) & inlinelock->pxd;
+ linelock = (struct linelock *) & tlck;
+ }
+#endif /* _JFS_WIP */
+ else {
+ jfs_err("lmWriteRecord: UFO tlck:0x%p", tlck);
+ return 0; /* Probably should trap */
+ }
+ l2linesize = linelock->l2linesize;
+
+ moveData:
+ ASSERT(linelock->index <= linelock->maxcnt);
+
+ lv = linelock->lv;
+ for (i = 0; i < linelock->index; i++, lv++) {
+ if (lv->length == 0)
+ continue;
+
+ /* is page full ? */
+ if (dstoffset >= LOGPSIZE - LOGPTLRSIZE) {
+ /* page become full: move on to next page */
+ lmNextPage(log);
+
+ bp = log->bp;
+ lp = (struct logpage *) bp->l_ldata;
+ dstoffset = LOGPHDRSIZE;
+ }
+
+ /*
+ * move log vector data
+ */
+ src = (u8 *) p + (lv->offset << l2linesize);
+ srclen = lv->length << l2linesize;
+ len += srclen;
+ while (srclen > 0) {
+ freespace = (LOGPSIZE - LOGPTLRSIZE) - dstoffset;
+ nbytes = min(freespace, srclen);
+ dst = (caddr_t) lp + dstoffset;
+ memcpy(dst, src, nbytes);
+ dstoffset += nbytes;
+
+ /* is page not full ? */
+ if (dstoffset < LOGPSIZE - LOGPTLRSIZE)
+ break;
+
+ /* page become full: move on to next page */
+ lmNextPage(log);
+
+ bp = (struct lbuf *) log->bp;
+ lp = (struct logpage *) bp->l_ldata;
+ dstoffset = LOGPHDRSIZE;
+
+ srclen -= nbytes;
+ src += nbytes;
+ }
+
+ /*
+ * move log vector descriptor
+ */
+ len += 4;
+ lvd = (struct lvd *) ((caddr_t) lp + dstoffset);
+ lvd->offset = cpu_to_le16(lv->offset);
+ lvd->length = cpu_to_le16(lv->length);
+ dstoffset += 4;
+ jfs_info("lmWriteRecord: lv offset:%d length:%d",
+ lv->offset, lv->length);
+ }
+
+ if ((i = linelock->next)) {
+ linelock = (struct linelock *) lid_to_tlock(i);
+ goto moveData;
+ }
+
+ /*
+ * move log record descriptor
+ */
+ moveLrd:
+ lrd->length = cpu_to_le16(len);
+
+ src = (caddr_t) lrd;
+ srclen = LOGRDSIZE;
+
+ while (srclen > 0) {
+ freespace = (LOGPSIZE - LOGPTLRSIZE) - dstoffset;
+ nbytes = min(freespace, srclen);
+ dst = (caddr_t) lp + dstoffset;
+ memcpy(dst, src, nbytes);
+
+ dstoffset += nbytes;
+ srclen -= nbytes;
+
+ /* are there more to move than freespace of page ? */
+ if (srclen)
+ goto pageFull;
+
+ /*
+ * end of log record descriptor
+ */
+
+ /* update last log record eor */
+ log->eor = dstoffset;
+ bp->l_eor = dstoffset;
+ lsn = (log->page << L2LOGPSIZE) + dstoffset;
+
+ if (lrd->type & cpu_to_le16(LOG_COMMIT)) {
+ tblk->clsn = lsn;
+ jfs_info("wr: tclsn:0x%x, beor:0x%x", tblk->clsn,
+ bp->l_eor);
+
+ INCREMENT(lmStat.commit); /* # of commit */
+
+ /*
+ * enqueue tblock for group commit:
+ *
+ * enqueue tblock of non-trivial/synchronous COMMIT
+ * at tail of group commit queue
+ * (trivial/asynchronous COMMITs are ignored by
+ * group commit.)
+ */
+ LOGGC_LOCK(log);
+
+ /* init tblock gc state */
+ tblk->flag = tblkGC_QUEUE;
+ tblk->bp = log->bp;
+ tblk->pn = log->page;
+ tblk->eor = log->eor;
+
+ /* enqueue transaction to commit queue */
+ tblk->cqnext = NULL;
+ if (log->cqueue.head) {
+ log->cqueue.tail->cqnext = tblk;
+ log->cqueue.tail = tblk;
+ } else
+ log->cqueue.head = log->cqueue.tail = tblk;
+
+ LOGGC_UNLOCK(log);
+ }
+
+ jfs_info("lmWriteRecord: lrd:0x%04x bp:0x%p pn:%d eor:0x%x",
+ le16_to_cpu(lrd->type), log->bp, log->page, dstoffset);
+
+ /* page not full ? */
+ if (dstoffset < LOGPSIZE - LOGPTLRSIZE)
+ return lsn;
+
+ pageFull:
+ /* page become full: move on to next page */
+ lmNextPage(log);
+
+ bp = (struct lbuf *) log->bp;
+ lp = (struct logpage *) bp->l_ldata;
+ dstoffset = LOGPHDRSIZE;
+ src += nbytes;
+ }
+
+ return lsn;
+}
+
+
+/*
+ * NAME: lmNextPage()
+ *
+ * FUNCTION: write current page and allocate next page.
+ *
+ * PARAMETER: log
+ *
+ * RETURN: 0
+ *
+ * serialization: LOG_LOCK() held on entry/exit
+ */
+static int lmNextPage(struct jfs_log * log)
+{
+ struct logpage *lp;
+ int lspn; /* log sequence page number */
+ int pn; /* current page number */
+ struct lbuf *bp;
+ struct lbuf *nextbp;
+ struct tblock *tblk;
+
+ /* get current log page number and log sequence page number */
+ pn = log->page;
+ bp = log->bp;
+ lp = (struct logpage *) bp->l_ldata;
+ lspn = le32_to_cpu(lp->h.page);
+
+ LOGGC_LOCK(log);
+
+ /*
+ * write or queue the full page at the tail of write queue
+ */
+ /* get the tail tblk on commit queue */
+ tblk = log->cqueue.tail;
+
+ /* every tblk who has COMMIT record on the current page,
+ * and has not been committed, must be on commit queue
+ * since tblk is queued at commit queueu at the time
+ * of writing its COMMIT record on the page before
+ * page becomes full (even though the tblk thread
+ * who wrote COMMIT record may have been suspended
+ * currently);
+ */
+
+ /* is page bound with outstanding tail tblk ? */
+ if (tblk && tblk->pn == pn) {
+ /* mark tblk for end-of-page */
+ tblk->flag |= tblkGC_EOP;
+
+ if (log->cflag & logGC_PAGEOUT) {
+ /* if page is not already on write queue,
+ * just enqueue (no lbmWRITE to prevent redrive)
+ * buffer to wqueue to ensure correct serial order
+ * of the pages since log pages will be added
+ * continuously
+ */
+ if (bp->l_wqnext == NULL)
+ lbmWrite(log, bp, 0, 0);
+ } else {
+ /*
+ * No current GC leader, initiate group commit
+ */
+ log->cflag |= logGC_PAGEOUT;
+ lmGCwrite(log, 0);
+ }
+ }
+ /* page is not bound with outstanding tblk:
+ * init write or mark it to be redriven (lbmWRITE)
+ */
+ else {
+ /* finalize the page */
+ bp->l_ceor = bp->l_eor;
+ lp->h.eor = lp->t.eor = cpu_to_le16(bp->l_ceor);
+ lbmWrite(log, bp, lbmWRITE | lbmRELEASE | lbmFREE, 0);
+ }
+ LOGGC_UNLOCK(log);
+
+ /*
+ * allocate/initialize next page
+ */
+ /* if log wraps, the first data page of log is 2
+ * (0 never used, 1 is superblock).
+ */
+ log->page = (pn == log->size - 1) ? 2 : pn + 1;
+ log->eor = LOGPHDRSIZE; /* ? valid page empty/full at logRedo() */
+
+ /* allocate/initialize next log page buffer */
+ nextbp = lbmAllocate(log, log->page);
+ nextbp->l_eor = log->eor;
+ log->bp = nextbp;
+
+ /* initialize next log page */
+ lp = (struct logpage *) nextbp->l_ldata;
+ lp->h.page = lp->t.page = cpu_to_le32(lspn + 1);
+ lp->h.eor = lp->t.eor = cpu_to_le16(LOGPHDRSIZE);
+
+ return 0;
+}
+
+
+/*
+ * NAME: lmGroupCommit()
+ *
+ * FUNCTION: group commit
+ * initiate pageout of the pages with COMMIT in the order of
+ * page number - redrive pageout of the page at the head of
+ * pageout queue until full page has been written.
+ *
+ * RETURN:
+ *
+ * NOTE:
+ * LOGGC_LOCK serializes log group commit queue, and
+ * transaction blocks on the commit queue.
+ * N.B. LOG_LOCK is NOT held during lmGroupCommit().
+ */
+int lmGroupCommit(struct jfs_log * log, struct tblock * tblk)
+{
+ int rc = 0;
+
+ LOGGC_LOCK(log);
+
+ /* group committed already ? */
+ if (tblk->flag & tblkGC_COMMITTED) {
+ if (tblk->flag & tblkGC_ERROR)
+ rc = -EIO;
+
+ LOGGC_UNLOCK(log);
+ return rc;
+ }
+ jfs_info("lmGroup Commit: tblk = 0x%p, gcrtc = %d", tblk, log->gcrtc);
+
+ if (tblk->xflag & COMMIT_LAZY)
+ tblk->flag |= tblkGC_LAZY;
+
+ if ((!(log->cflag & logGC_PAGEOUT)) && log->cqueue.head &&
+ (!(tblk->xflag & COMMIT_LAZY) || test_bit(log_FLUSH, &log->flag))) {
+ /*
+ * No pageout in progress
+ *
+ * start group commit as its group leader.
+ */
+ log->cflag |= logGC_PAGEOUT;
+
+ lmGCwrite(log, 0);
+ }
+
+ if (tblk->xflag & COMMIT_LAZY) {
+ /*
+ * Lazy transactions can leave now
+ */
+ LOGGC_UNLOCK(log);
+ return 0;
+ }
+
+ /* lmGCwrite gives up LOGGC_LOCK, check again */
+
+ if (tblk->flag & tblkGC_COMMITTED) {
+ if (tblk->flag & tblkGC_ERROR)
+ rc = -EIO;
+
+ LOGGC_UNLOCK(log);
+ return rc;
+ }
+
+ /* upcount transaction waiting for completion
+ */
+ log->gcrtc++;
+ tblk->flag |= tblkGC_READY;
+
+ __SLEEP_COND(tblk->gcwait, (tblk->flag & tblkGC_COMMITTED),
+ LOGGC_LOCK(log), LOGGC_UNLOCK(log));
+
+ /* removed from commit queue */
+ if (tblk->flag & tblkGC_ERROR)
+ rc = -EIO;
+
+ LOGGC_UNLOCK(log);
+ return rc;
+}
+
+/*
+ * NAME: lmGCwrite()
+ *
+ * FUNCTION: group commit write
+ * initiate write of log page, building a group of all transactions
+ * with commit records on that page.
+ *
+ * RETURN: None
+ *
+ * NOTE:
+ * LOGGC_LOCK must be held by caller.
+ * N.B. LOG_LOCK is NOT held during lmGroupCommit().
+ */
+static void lmGCwrite(struct jfs_log * log, int cant_write)
+{
+ struct lbuf *bp;
+ struct logpage *lp;
+ int gcpn; /* group commit page number */
+ struct tblock *tblk;
+ struct tblock *xtblk;
+
+ /*
+ * build the commit group of a log page
+ *
+ * scan commit queue and make a commit group of all
+ * transactions with COMMIT records on the same log page.
+ */
+ /* get the head tblk on the commit queue */
+ tblk = xtblk = log->cqueue.head;
+ gcpn = tblk->pn;
+
+ while (tblk && tblk->pn == gcpn) {
+ xtblk = tblk;
+
+ /* state transition: (QUEUE, READY) -> COMMIT */
+ tblk->flag |= tblkGC_COMMIT;
+ tblk = tblk->cqnext;
+ }
+ tblk = xtblk; /* last tblk of the page */
+
+ /*
+ * pageout to commit transactions on the log page.
+ */
+ bp = (struct lbuf *) tblk->bp;
+ lp = (struct logpage *) bp->l_ldata;
+ /* is page already full ? */
+ if (tblk->flag & tblkGC_EOP) {
+ /* mark page to free at end of group commit of the page */
+ tblk->flag &= ~tblkGC_EOP;
+ tblk->flag |= tblkGC_FREE;
+ bp->l_ceor = bp->l_eor;
+ lp->h.eor = lp->t.eor = cpu_to_le16(bp->l_ceor);
+ lbmWrite(log, bp, lbmWRITE | lbmRELEASE | lbmGC,
+ cant_write);
+ INCREMENT(lmStat.full_page);
+ }
+ /* page is not yet full */
+ else {
+ bp->l_ceor = tblk->eor; /* ? bp->l_ceor = bp->l_eor; */
+ lp->h.eor = lp->t.eor = cpu_to_le16(bp->l_ceor);
+ lbmWrite(log, bp, lbmWRITE | lbmGC, cant_write);
+ INCREMENT(lmStat.partial_page);
+ }
+}
+
+/*
+ * NAME: lmPostGC()
+ *
+ * FUNCTION: group commit post-processing
+ * Processes transactions after their commit records have been written
+ * to disk, redriving log I/O if necessary.
+ *
+ * RETURN: None
+ *
+ * NOTE:
+ * This routine is called a interrupt time by lbmIODone
+ */
+static void lmPostGC(struct lbuf * bp)
+{
+ unsigned long flags;
+ struct jfs_log *log = bp->l_log;
+ struct logpage *lp;
+ struct tblock *tblk;
+
+ //LOGGC_LOCK(log);
+ spin_lock_irqsave(&log->gclock, flags);
+ /*
+ * current pageout of group commit completed.
+ *
+ * remove/wakeup transactions from commit queue who were
+ * group committed with the current log page
+ */
+ while ((tblk = log->cqueue.head) && (tblk->flag & tblkGC_COMMIT)) {
+ /* if transaction was marked GC_COMMIT then
+ * it has been shipped in the current pageout
+ * and made it to disk - it is committed.
+ */
+
+ if (bp->l_flag & lbmERROR)
+ tblk->flag |= tblkGC_ERROR;
+
+ /* remove it from the commit queue */
+ log->cqueue.head = tblk->cqnext;
+ if (log->cqueue.head == NULL)
+ log->cqueue.tail = NULL;
+ tblk->flag &= ~tblkGC_QUEUE;
+ tblk->cqnext = 0;
+
+ if (tblk == log->flush_tblk) {
+ /* we can stop flushing the log now */
+ clear_bit(log_FLUSH, &log->flag);
+ log->flush_tblk = NULL;
+ }
+
+ jfs_info("lmPostGC: tblk = 0x%p, flag = 0x%x", tblk,
+ tblk->flag);
+
+ if (!(tblk->xflag & COMMIT_FORCE))
+ /*
+ * Hand tblk over to lazy commit thread
+ */
+ txLazyUnlock(tblk);
+ else {
+ /* state transition: COMMIT -> COMMITTED */
+ tblk->flag |= tblkGC_COMMITTED;
+
+ if (tblk->flag & tblkGC_READY)
+ log->gcrtc--;
+
+ LOGGC_WAKEUP(tblk);
+ }
+
+ /* was page full before pageout ?
+ * (and this is the last tblk bound with the page)
+ */
+ if (tblk->flag & tblkGC_FREE)
+ lbmFree(bp);
+ /* did page become full after pageout ?
+ * (and this is the last tblk bound with the page)
+ */
+ else if (tblk->flag & tblkGC_EOP) {
+ /* finalize the page */
+ lp = (struct logpage *) bp->l_ldata;
+ bp->l_ceor = bp->l_eor;
+ lp->h.eor = lp->t.eor = cpu_to_le16(bp->l_eor);
+ jfs_info("lmPostGC: calling lbmWrite");
+ lbmWrite(log, bp, lbmWRITE | lbmRELEASE | lbmFREE,
+ 1);
+ }
+
+ }
+
+ /* are there any transactions who have entered lnGroupCommit()
+ * (whose COMMITs are after that of the last log page written.
+ * They are waiting for new group commit (above at (SLEEP 1))
+ * or lazy transactions are on a full (queued) log page,
+ * select the latest ready transaction as new group leader and
+ * wake her up to lead her group.
+ */
+ if ((tblk = log->cqueue.head) &&
+ ((log->gcrtc > 0) || (tblk->bp->l_wqnext != NULL) ||
+ test_bit(log_FLUSH, &log->flag)))
+ /*
+ * Call lmGCwrite with new group leader
+ */
+ lmGCwrite(log, 1);
+
+ /* no transaction are ready yet (transactions are only just
+ * queued (GC_QUEUE) and not entered for group commit yet).
+ * the first transaction entering group commit
+ * will elect herself as new group leader.
+ */
+ else
+ log->cflag &= ~logGC_PAGEOUT;
+
+ //LOGGC_UNLOCK(log);
+ spin_unlock_irqrestore(&log->gclock, flags);
+ return;
+}
+
+/*
+ * NAME: lmLogSync()
+ *
+ * FUNCTION: write log SYNCPT record for specified log
+ * if new sync address is available
+ * (normally the case if sync() is executed by back-ground
+ * process).
+ * if not, explicitly run jfs_blogsync() to initiate
+ * getting of new sync address.
+ * calculate new value of i_nextsync which determines when
+ * this code is called again.
+ *
+ * this is called only from lmLog().
+ *
+ * PARAMETER: ip - pointer to logs inode.
+ *
+ * RETURN: 0
+ *
+ * serialization: LOG_LOCK() held on entry/exit
+ */
+static int lmLogSync(struct jfs_log * log, int nosyncwait)
+{
+ int logsize;
+ int written; /* written since last syncpt */
+ int free; /* free space left available */
+ int delta; /* additional delta to write normally */
+ int more; /* additional write granted */
+ struct lrd lrd;
+ int lsn;
+ struct logsyncblk *lp;
+
+ /*
+ * forward syncpt
+ */
+ /* if last sync is same as last syncpt,
+ * invoke sync point forward processing to update sync.
+ */
+
+ if (log->sync == log->syncpt) {
+ LOGSYNC_LOCK(log);
+ /* ToDo: push dirty metapages out to disk */
+// bmLogSync(log);
+
+ if (list_empty(&log->synclist))
+ log->sync = log->lsn;
+ else {
+ lp = list_entry(log->synclist.next,
+ struct logsyncblk, synclist);
+ log->sync = lp->lsn;
+ }
+ LOGSYNC_UNLOCK(log);
+
+ }
+
+ /* if sync is different from last syncpt,
+ * write a SYNCPT record with syncpt = sync.
+ * reset syncpt = sync
+ */
+ if (log->sync != log->syncpt) {
+ struct super_block *sb = log->sb;
+ struct jfs_sb_info *sbi = JFS_SBI(sb);
+
+ /*
+ * We need to make sure all of the "written" metapages
+ * actually make it to disk
+ */
+ fsync_inode_data_buffers(sbi->ipbmap);
+ fsync_inode_data_buffers(sbi->ipimap);
+ fsync_inode_data_buffers(sb->s_bdev->bd_inode);
+
+ lrd.logtid = 0;
+ lrd.backchain = 0;
+ lrd.type = cpu_to_le16(LOG_SYNCPT);
+ lrd.length = 0;
+ lrd.log.syncpt.sync = cpu_to_le32(log->sync);
+ lsn = lmWriteRecord(log, NULL, &lrd, NULL);
+
+ log->syncpt = log->sync;
+ } else
+ lsn = log->lsn;
+
+ /*
+ * setup next syncpt trigger (SWAG)
+ */
+ logsize = log->logsize;
+
+ logdiff(written, lsn, log);
+ free = logsize - written;
+ delta = LOGSYNC_DELTA(logsize);
+ more = min(free / 2, delta);
+ if (more < 2 * LOGPSIZE) {
+ jfs_warn("\n ... Log Wrap ... Log Wrap ... Log Wrap ...\n");
+ /*
+ * log wrapping
+ *
+ * option 1 - panic ? No.!
+ * option 2 - shutdown file systems
+ * associated with log ?
+ * option 3 - extend log ?
+ */
+ /*
+ * option 4 - second chance
+ *
+ * mark log wrapped, and continue.
+ * when all active transactions are completed,
+ * mark log vaild for recovery.
+ * if crashed during invalid state, log state
+ * implies invald log, forcing fsck().
+ */
+ /* mark log state log wrap in log superblock */
+ /* log->state = LOGWRAP; */
+
+ /* reset sync point computation */
+ log->syncpt = log->sync = lsn;
+ log->nextsync = delta;
+ } else
+ /* next syncpt trigger = written + more */
+ log->nextsync = written + more;
+
+ /* return if lmLogSync() from outside of transaction, e.g., sync() */
+ if (nosyncwait)
+ return lsn;
+
+ /* if number of bytes written from last sync point is more
+ * than 1/4 of the log size, stop new transactions from
+ * starting until all current transactions are completed
+ * by setting syncbarrier flag.
+ */
+ if (written > LOGSYNC_BARRIER(logsize) && logsize > 32 * LOGPSIZE) {
+ set_bit(log_SYNCBARRIER, &log->flag);
+ jfs_info("log barrier on: lsn=0x%x syncpt=0x%x", lsn,
+ log->syncpt);
+ /*
+ * We may have to initiate group commit
+ */
+ jfs_flush_journal(log, 0);
+ }
+
+ return lsn;
+}
+
+
+/*
+ * NAME: lmLogOpen()
+ *
+ * FUNCTION: open the log on first open;
+ * insert filesystem in the active list of the log.
+ *
+ * PARAMETER: ipmnt - file system mount inode
+ * iplog - log inode (out)
+ *
+ * RETURN:
+ *
+ * serialization:
+ */
+int lmLogOpen(struct super_block *sb, struct jfs_log ** logptr)
+{
+ int rc;
+ struct block_device *bdev;
+ struct jfs_log *log;
+
+ if (!(log = kmalloc(sizeof(struct jfs_log), GFP_KERNEL)))
+ return -ENOMEM;
+ memset(log, 0, sizeof(struct jfs_log));
+ init_waitqueue_head(&log->syncwait);
+
+ log->sb = sb; /* This should be a list */
+
+ if (!(JFS_SBI(sb)->mntflag & JFS_INLINELOG))
+ goto externalLog;
+
+ /*
+ * in-line log in host file system
+ *
+ * file system to log have 1-to-1 relationship;
+ */
+
+ set_bit(log_INLINELOG, &log->flag);
+ log->bdev = sb->s_bdev;
+ log->base = addressPXD(&JFS_SBI(sb)->logpxd);
+ log->size = lengthPXD(&JFS_SBI(sb)->logpxd) >>
+ (L2LOGPSIZE - sb->s_blocksize_bits);
+ log->l2bsize = sb->s_blocksize_bits;
+ ASSERT(L2LOGPSIZE >= sb->s_blocksize_bits);
+
+ /*
+ * initialize log.
+ */
+ if ((rc = lmLogInit(log)))
+ goto free;
+ goto out;
+
+ /*
+ * external log as separate logical volume
+ *
+ * file systems to log may have n-to-1 relationship;
+ */
+ externalLog:
+
+ /*
+ * TODO: Check for already opened log devices
+ */
+
+ if (!(bdev = bdget(kdev_t_to_nr(JFS_SBI(sb)->logdev)))) {
+ rc = -ENODEV;
+ goto free;
+ }
+
+ if ((rc = blkdev_get(bdev, FMODE_READ|FMODE_WRITE, 0, BDEV_FS))) {
+ goto free;
+ }
+
+ log->bdev = bdev;
+ memcpy(log->uuid, JFS_SBI(sb)->loguuid, sizeof(log->uuid));
+
+ /*
+ * initialize log:
+ */
+ if ((rc = lmLogInit(log)))
+ goto close;
+
+ /*
+ * add file system to log active file system list
+ */
+ if ((rc = lmLogFileSystem(log, JFS_SBI(sb)->uuid, 1)))
+ goto shutdown;
+
+ out:
+ *logptr = log;
+ return 0;
+
+ /*
+ * unwind on error
+ */
+ shutdown: /* unwind lbmLogInit() */
+ lbmLogShutdown(log);
+
+ close: /* close external log device */
+ blkdev_put(bdev, BDEV_FS);
+
+ free: /* free log descriptor */
+ kfree(log);
+
+ jfs_warn("lmLogOpen: exit(%d)", rc);
+ return rc;
+}
+
+
+/*
+ * NAME: lmLogInit()
+ *
+ * FUNCTION: log initialization at first log open.
+ *
+ * logredo() (or logformat()) should have been run previously.
+ * initialize the log inode from log superblock.
+ * set the log state in the superblock to LOGMOUNT and
+ * write SYNCPT log record.
+ *
+ * PARAMETER: log - log structure
+ *
+ * RETURN: 0 - if ok
+ * -EINVAL - bad log magic number or superblock dirty
+ * error returned from logwait()
+ *
+ * serialization: single first open thread
+ */
+int lmLogInit(struct jfs_log * log)
+{
+ int rc = 0;
+ struct lrd lrd;
+ struct logsuper *logsuper;
+ struct lbuf *bpsuper;
+ struct lbuf *bp;
+ struct logpage *lp;
+ int lsn;
+
+ jfs_info("lmLogInit: log:0x%p", log);
+
+ /*
+ * log inode is overlaid on generic inode where
+ * dinode have been zeroed out by iRead();
+ */
+
+ /*
+ * initialize log i/o
+ */
+ if ((rc = lbmLogInit(log)))
+ return rc;
+
+ /*
+ * validate log superblock
+ */
+
+
+ if (!test_bit(log_INLINELOG, &log->flag))
+ log->l2bsize = 12; /* XXX kludge alert XXX */
+ if ((rc = lbmRead(log, 1, &bpsuper)))
+ goto errout10;
+
+ logsuper = (struct logsuper *) bpsuper->l_ldata;
+
+ if (logsuper->magic != cpu_to_le32(LOGMAGIC)) {
+ jfs_warn("*** Log Format Error ! ***");
+ rc = -EINVAL;
+ goto errout20;
+ }
+
+ /* logredo() should have been run successfully. */
+ if (logsuper->state != cpu_to_le32(LOGREDONE)) {
+ jfs_warn("*** Log Is Dirty ! ***");
+ rc = -EINVAL;
+ goto errout20;
+ }
+
+ /* initialize log inode from log superblock */
+ if (test_bit(log_INLINELOG,&log->flag)) {
+ if (log->size != le32_to_cpu(logsuper->size)) {
+ rc = -EINVAL;
+ goto errout20;
+ }
+ jfs_info("lmLogInit: inline log:0x%p base:0x%Lx size:0x%x",
+ log, (unsigned long long) log->base, log->size);
+ } else {
+ if (memcmp(logsuper->uuid, log->uuid, 16)) {
+ jfs_warn("wrong uuid on JFS log device");
+ goto errout20;
+ }
+ log->size = le32_to_cpu(logsuper->size);
+ log->l2bsize = le32_to_cpu(logsuper->l2bsize);
+ jfs_info("lmLogInit: external log:0x%p base:0x%Lx size:0x%x",
+ log, (unsigned long long) log->base, log->size);
+ }
+
+ log->page = le32_to_cpu(logsuper->end) / LOGPSIZE;
+ log->eor = le32_to_cpu(logsuper->end) - (LOGPSIZE * log->page);
+
+ /* check for disabled journaling to disk */
+ if (JFS_SBI(log->sb)->flag & JFS_NOINTEGRITY) {
+ log->no_integrity = 1;
+ log->ni_page = log->page;
+ log->ni_eor = log->eor;
+ }
+ else
+ log->no_integrity = 0;
+
+ /*
+ * initialize for log append write mode
+ */
+ /* establish current/end-of-log page/buffer */
+ if ((rc = lbmRead(log, log->page, &bp)))
+ goto errout20;
+
+ lp = (struct logpage *) bp->l_ldata;
+
+ jfs_info("lmLogInit: lsn:0x%x page:%d eor:%d:%d",
+ le32_to_cpu(logsuper->end), log->page, log->eor,
+ le16_to_cpu(lp->h.eor));
+
+// ASSERT(log->eor == lp->h.eor);
+
+ log->bp = bp;
+ bp->l_pn = log->page;
+ bp->l_eor = log->eor;
+
+ /* initialize the group commit serialization lock */
+ LOGGC_LOCK_INIT(log);
+
+ /* if current page is full, move on to next page */
+ if (log->eor >= LOGPSIZE - LOGPTLRSIZE)
+ lmNextPage(log);
+
+ /* allocate/initialize the log write serialization lock */
+ LOG_LOCK_INIT(log);
+
+ /*
+ * initialize log syncpoint
+ */
+ /*
+ * write the first SYNCPT record with syncpoint = 0
+ * (i.e., log redo up to HERE !);
+ * remove current page from lbm write queue at end of pageout
+ * (to write log superblock update), but do not release to freelist;
+ */
+ lrd.logtid = 0;
+ lrd.backchain = 0;
+ lrd.type = cpu_to_le16(LOG_SYNCPT);
+ lrd.length = 0;
+ lrd.log.syncpt.sync = 0;
+ lsn = lmWriteRecord(log, NULL, &lrd, NULL);
+ bp = log->bp;
+ bp->l_ceor = bp->l_eor;
+ lp = (struct logpage *) bp->l_ldata;
+ lp->h.eor = lp->t.eor = cpu_to_le16(bp->l_eor);
+ lbmWrite(log, bp, lbmWRITE | lbmSYNC, 0);
+ if ((rc = lbmIOWait(bp, 0)))
+ goto errout30;
+
+ /* initialize logsync parameters */
+ log->logsize = (log->size - 2) << L2LOGPSIZE;
+ log->lsn = lsn;
+ log->syncpt = lsn;
+ log->sync = log->syncpt;
+ log->nextsync = LOGSYNC_DELTA(log->logsize);
+
+ jfs_info("lmLogInit: lsn:0x%x syncpt:0x%x sync:0x%x",
+ log->lsn, log->syncpt, log->sync);
+
+ LOGSYNC_LOCK_INIT(log);
+
+ INIT_LIST_HEAD(&log->synclist);
+
+ log->cqueue.head = log->cqueue.tail = NULL;
+ log->flush_tblk = NULL;
+
+ log->count = 0;
+
+ /*
+ * initialize for lazy/group commit
+ */
+ log->clsn = lsn;
+
+ /*
+ * update/write superblock
+ */
+ logsuper->state = cpu_to_le32(LOGMOUNT);
+ log->serial = le32_to_cpu(logsuper->serial) + 1;
+ logsuper->serial = cpu_to_le32(log->serial);
+ lbmDirectWrite(log, bpsuper, lbmWRITE | lbmRELEASE | lbmSYNC);
+ if ((rc = lbmIOWait(bpsuper, lbmFREE)))
+ goto errout30;
+
+ return 0;
+
+ /*
+ * unwind on error
+ */
+ errout30: /* release log page */
+ log->wqueue = NULL;
+ bp->l_wqnext = NULL;
+ lbmFree(bp);
+
+ errout20: /* release log superblock */
+ lbmFree(bpsuper);
+
+ errout10: /* unwind lbmLogInit() */
+ lbmLogShutdown(log);
+
+ jfs_warn("lmLogInit: exit(%d)", rc);
+ return rc;
+}
+
+
+/*
+ * NAME: lmLogClose()
+ *
+ * FUNCTION: remove file system <ipmnt> from active list of log <iplog>
+ * and close it on last close.
+ *
+ * PARAMETER: sb - superblock
+ * log - log inode
+ *
+ * RETURN: errors from subroutines
+ *
+ * serialization:
+ */
+int lmLogClose(struct super_block *sb, struct jfs_log * log)
+{
+ int rc;
+
+ jfs_info("lmLogClose: log:0x%p", log);
+
+ if (!test_bit(log_INLINELOG, &log->flag))
+ goto externalLog;
+
+ /*
+ * in-line log in host file system
+ */
+ rc = lmLogShutdown(log);
+ goto out;
+
+ /*
+ * external log as separate logical volume
+ */
+ externalLog:
+ lmLogFileSystem(log, JFS_SBI(sb)->uuid, 0);
+ rc = lmLogShutdown(log);
+ blkdev_put(log->bdev, BDEV_FS);
+
+ out:
+ kfree(log);
+ jfs_info("lmLogClose: exit(%d)", rc);
+ return rc;
+}
+
+
+/*
+ * NAME: jfs_flush_journal()
+ *
+ * FUNCTION: initiate write of any outstanding transactions to the journal
+ * and optionally wait until they are all written to disk
+ *
+ * wait == 0 flush until latest txn is committed, don't wait
+ * wait == 1 flush until latest txn is committed, wait
+ * wait > 1 flush until all txn's are complete, wait
+ */
+void jfs_flush_journal(struct jfs_log *log, int wait)
+{
+ int i;
+ struct tblock *target;
+
+ if (!log)
+ /* jfs_write_inode may call us during read-only mount */
+ return;
+
+ jfs_info("jfs_flush_journal: log:0x%p wait=%d", log, wait);
+
+ LOGGC_LOCK(log);
+
+ target = log->cqueue.head;
+
+ if (target) {
+ /*
+ * This ensures that we will keep writing to the journal as long
+ * as there are unwritten commit records
+ */
+
+ if (test_bit(log_FLUSH, &log->flag)) {
+ /*
+ * We're already flushing.
+ * if flush_tblk is NULL, we are flushing everything,
+ * so leave it that way. Otherwise, update it to the
+ * latest transaction
+ */
+ if (log->flush_tblk)
+ log->flush_tblk = target;
+ } else {
+ /* Only flush until latest transaction is committed */
+ log->flush_tblk = target;
+ set_bit(log_FLUSH, &log->flag);
+
+ /*
+ * Initiate I/O on outstanding transactions
+ */
+ if (!(log->cflag & logGC_PAGEOUT)) {
+ log->cflag |= logGC_PAGEOUT;
+ lmGCwrite(log, 0);
+ }
+ }
+ }
+ if ((wait > 1) || test_bit(log_SYNCBARRIER, &log->flag)) {
+ /* Flush until all activity complete */
+ set_bit(log_FLUSH, &log->flag);
+ log->flush_tblk = NULL;
+ }
+
+ if (wait && target && !(target->flag & tblkGC_COMMITTED)) {
+ DECLARE_WAITQUEUE(__wait, current);
+
+ add_wait_queue(&target->gcwait, &__wait);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ LOGGC_UNLOCK(log);
+ schedule();
+ current->state = TASK_RUNNING;
+ LOGGC_LOCK(log);
+ remove_wait_queue(&target->gcwait, &__wait);
+ }
+ LOGGC_UNLOCK(log);
+
+ if (wait < 2)
+ return;
+
+ /*
+ * If there was recent activity, we may need to wait
+ * for the lazycommit thread to catch up
+ */
+ if (log->cqueue.head || !list_empty(&log->synclist)) {
+ for (i = 0; i < 800; i++) { /* Too much? */
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(HZ / 4);
+ if ((log->cqueue.head == NULL) &&
+ list_empty(&log->synclist))
+ break;
+ }
+ }
+ assert(log->cqueue.head == NULL);
+ assert(list_empty(&log->synclist));
+ clear_bit(log_FLUSH, &log->flag);
+}
+
+/*
+ * NAME: lmLogShutdown()
+ *
+ * FUNCTION: log shutdown at last LogClose().
+ *
+ * write log syncpt record.
+ * update super block to set redone flag to 0.
+ *
+ * PARAMETER: log - log inode
+ *
+ * RETURN: 0 - success
+ *
+ * serialization: single last close thread
+ */
+int lmLogShutdown(struct jfs_log * log)
+{
+ int rc;
+ struct lrd lrd;
+ int lsn;
+ struct logsuper *logsuper;
+ struct lbuf *bpsuper;
+ struct lbuf *bp;
+ struct logpage *lp;
+
+ jfs_info("lmLogShutdown: log:0x%p", log);
+
+ jfs_flush_journal(log, 2);
+
+ /*
+ * We need to make sure all of the "written" metapages
+ * actually make it to disk
+ */
+ fsync_no_super(log->sb->s_dev);
+
+ /*
+ * write the last SYNCPT record with syncpoint = 0
+ * (i.e., log redo up to HERE !)
+ */
+ lrd.logtid = 0;
+ lrd.backchain = 0;
+ lrd.type = cpu_to_le16(LOG_SYNCPT);
+ lrd.length = 0;
+ lrd.log.syncpt.sync = 0;
+
+ /* check for disabled journaling to disk */
+ if (JFS_SBI(log->sb)->flag & JFS_NOINTEGRITY) {
+ log->no_integrity = 0;
+ log->page = log->ni_page;
+ log->eor = log->ni_eor;
+ }
+
+ lsn = lmWriteRecord(log, NULL, &lrd, NULL);
+ bp = log->bp;
+ lp = (struct logpage *) bp->l_ldata;
+ lp->h.eor = lp->t.eor = cpu_to_le16(bp->l_eor);
+ lbmWrite(log, log->bp, lbmWRITE | lbmRELEASE | lbmSYNC, 0);
+ lbmIOWait(log->bp, lbmFREE);
+
+ /*
+ * synchronous update log superblock
+ * mark log state as shutdown cleanly
+ * (i.e., Log does not need to be replayed).
+ */
+ if ((rc = lbmRead(log, 1, &bpsuper)))
+ goto out;
+
+ logsuper = (struct logsuper *) bpsuper->l_ldata;
+ logsuper->state = cpu_to_le32(LOGREDONE);
+ logsuper->end = cpu_to_le32(lsn);
+ lbmDirectWrite(log, bpsuper, lbmWRITE | lbmRELEASE | lbmSYNC);
+ rc = lbmIOWait(bpsuper, lbmFREE);
+
+ jfs_info("lmLogShutdown: lsn:0x%x page:%d eor:%d",
+ lsn, log->page, log->eor);
+
+ out:
+ /*
+ * shutdown per log i/o
+ */
+ lbmLogShutdown(log);
+
+ if (rc) {
+ jfs_warn("lmLogShutdown: exit(%d)", rc);
+ }
+ return rc;
+}
+
+
+/*
+ * NAME: lmLogFileSystem()
+ *
+ * FUNCTION: insert (<activate> = true)/remove (<activate> = false)
+ * file system into/from log active file system list.
+ *
+ * PARAMETE: log - pointer to logs inode.
+ * fsdev - kdev_t of filesystem.
+ * serial - pointer to returned log serial number
+ * activate - insert/remove device from active list.
+ *
+ * RETURN: 0 - success
+ * errors returned by vms_iowait().
+ */
+static int lmLogFileSystem(struct jfs_log * log, char *uuid, int activate)
+{
+ int rc = 0;
+ int i;
+ struct logsuper *logsuper;
+ struct lbuf *bpsuper;
+
+ /*
+ * insert/remove file system device to log active file system list.
+ */
+ if ((rc = lbmRead(log, 1, &bpsuper)))
+ return rc;
+
+ logsuper = (struct logsuper *) bpsuper->l_ldata;
+ if (activate) {
+ for (i = 0; i < MAX_ACTIVE; i++)
+ if (!memcmp(logsuper->active[i].uuid, NULL_UUID, 16)) {
+ memcpy(logsuper->active[i].uuid, uuid, 16);
+ break;
+ }
+ if (i == MAX_ACTIVE) {
+ jfs_warn("Too many file systems sharing journal!");
+ lbmFree(bpsuper);
+ return -EMFILE; /* Is there a better rc? */
+ }
+ } else {
+ for (i = 0; i < MAX_ACTIVE; i++)
+ if (!memcmp(logsuper->active[i].uuid, uuid, 16)) {
+ memcpy(logsuper->active[i].uuid, NULL_UUID, 16);
+ break;
+ }
+ if (i == MAX_ACTIVE) {
+ jfs_warn("Somebody stomped on the journal!");
+ lbmFree(bpsuper);
+ return -EIO;
+ }
+
+ }
+
+ /*
+ * synchronous write log superblock:
+ *
+ * write sidestream bypassing write queue:
+ * at file system mount, log super block is updated for
+ * activation of the file system before any log record
+ * (MOUNT record) of the file system, and at file system
+ * unmount, all meta data for the file system has been
+ * flushed before log super block is updated for deactivation
+ * of the file system.
+ */
+ lbmDirectWrite(log, bpsuper, lbmWRITE | lbmRELEASE | lbmSYNC);
+ rc = lbmIOWait(bpsuper, lbmFREE);
+
+ return rc;
+}
+
+/*
+ * log buffer manager (lbm)
+ * ------------------------
+ *
+ * special purpose buffer manager supporting log i/o requirements.
+ *
+ * per log write queue:
+ * log pageout occurs in serial order by fifo write queue and
+ * restricting to a single i/o in pregress at any one time.
+ * a circular singly-linked list
+ * (log->wrqueue points to the tail, and buffers are linked via
+ * bp->wrqueue field), and
+ * maintains log page in pageout ot waiting for pageout in serial pageout.
+ */
+
+/*
+ * lbmLogInit()
+ *
+ * initialize per log I/O setup at lmLogInit()
+ */
+static int lbmLogInit(struct jfs_log * log)
+{ /* log inode */
+ int i;
+ struct lbuf *lbuf;
+
+ jfs_info("lbmLogInit: log:0x%p", log);
+
+ /* initialize current buffer cursor */
+ log->bp = NULL;
+
+ /* initialize log device write queue */
+ log->wqueue = NULL;
+
+ /*
+ * Each log has its own buffer pages allocated to it. These are
+ * not managed by the page cache. This ensures that a transaction
+ * writing to the log does not block trying to allocate a page from
+ * the page cache (for the log). This would be bad, since page
+ * allocation waits on the kswapd thread that may be committing inodes
+ * which would cause log activity. Was that clear? I'm trying to
+ * avoid deadlock here.
+ */
+ init_waitqueue_head(&log->free_wait);
+
+ log->lbuf_free = NULL;
+
+ for (i = 0; i < LOGPAGES; i++) {
+ lbuf = kmalloc(sizeof(struct lbuf), GFP_KERNEL);
+ if (lbuf == 0)
+ goto error;
+ lbuf->l_bh.b_data = lbuf->l_ldata =
+ (char *) get_zeroed_page(GFP_KERNEL);
+ if (lbuf->l_ldata == 0) {
+ kfree(lbuf);
+ goto error;
+ }
+ lbuf->l_log = log;
+ init_waitqueue_head(&lbuf->l_ioevent);
+
+ lbuf->l_bh.b_size = LOGPSIZE;
+ lbuf->l_bh.b_dev = to_kdev_t(log->bdev->bd_dev);
+ lbuf->l_bh.b_end_io = lbmIODone;
+ lbuf->l_bh.b_private = lbuf;
+ lbuf->l_bh.b_page = virt_to_page(lbuf->l_ldata);
+ lbuf->l_bh.b_state = 0;
+ init_waitqueue_head(&lbuf->l_bh.b_wait);
+
+ lbuf->l_freelist = log->lbuf_free;
+ log->lbuf_free = lbuf;
+ }
+
+ return (0);
+
+ error:
+ lbmLogShutdown(log);
+ return -ENOMEM;
+}
+
+
+/*
+ * lbmLogShutdown()
+ *
+ * finalize per log I/O setup at lmLogShutdown()
+ */
+static void lbmLogShutdown(struct jfs_log * log)
+{
+ struct lbuf *lbuf;
+
+ jfs_info("lbmLogShutdown: log:0x%p", log);
+
+ lbuf = log->lbuf_free;
+ while (lbuf) {
+ struct lbuf *next = lbuf->l_freelist;
+ free_page((unsigned long) lbuf->l_ldata);
+ kfree(lbuf);
+ lbuf = next;
+ }
+
+ log->bp = NULL;
+}
+
+
+/*
+ * lbmAllocate()
+ *
+ * allocate an empty log buffer
+ */
+static struct lbuf *lbmAllocate(struct jfs_log * log, int pn)
+{
+ struct lbuf *bp;
+ unsigned long flags;
+
+ /*
+ * recycle from log buffer freelist if any
+ */
+ LCACHE_LOCK(flags);
+ LCACHE_SLEEP_COND(log->free_wait, (bp = log->lbuf_free), flags);
+ log->lbuf_free = bp->l_freelist;
+ LCACHE_UNLOCK(flags);
+
+ bp->l_flag = 0;
+
+ bp->l_wqnext = NULL;
+ bp->l_freelist = NULL;
+
+ bp->l_pn = pn;
+ bp->l_blkno = log->base + (pn << (L2LOGPSIZE - log->l2bsize));
+ bp->l_bh.b_blocknr = bp->l_blkno;
+ bp->l_ceor = 0;
+
+ return bp;
+}
+
+
+/*
+ * lbmFree()
+ *
+ * release a log buffer to freelist
+ */
+static void lbmFree(struct lbuf * bp)
+{
+ unsigned long flags;
+
+ LCACHE_LOCK(flags);
+
+ lbmfree(bp);
+
+ LCACHE_UNLOCK(flags);
+}
+
+static void lbmfree(struct lbuf * bp)
+{
+ struct jfs_log *log = bp->l_log;
+
+ assert(bp->l_wqnext == NULL);
+
+ /*
+ * return the buffer to head of freelist
+ */
+ bp->l_freelist = log->lbuf_free;
+ log->lbuf_free = bp;
+
+ wake_up(&log->free_wait);
+ return;
+}
+
+
+/*
+ * NAME: lbmRedrive
+ *
+ * FUNCTION: add a log buffer to the the log redrive list
+ *
+ * PARAMETER:
+ * bp - log buffer
+ *
+ * NOTES:
+ * Takes log_redrive_lock.
+ */
+static inline void lbmRedrive(struct lbuf *bp)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&log_redrive_lock, flags);
+ bp->l_redrive_next = log_redrive_list;
+ log_redrive_list = bp;
+ spin_unlock_irqrestore(&log_redrive_lock, flags);
+
+ wake_up(&jfs_IO_thread_wait);
+}
+
+
+/*
+ * lbmRead()
+ */
+static int lbmRead(struct jfs_log * log, int pn, struct lbuf ** bpp)
+{
+ struct lbuf *bp;
+
+ /*
+ * allocate a log buffer
+ */
+ *bpp = bp = lbmAllocate(log, pn);
+ jfs_info("lbmRead: bp:0x%p pn:0x%x", bp, pn);
+
+ bp->l_flag |= lbmREAD;
+ bp->l_bh.b_reqnext = NULL;
+ clear_bit(BH_Uptodate, &bp->l_bh.b_state);
+ lock_buffer(&bp->l_bh);
+ set_bit(BH_Mapped, &bp->l_bh.b_state);
+ set_bit(BH_Req, &bp->l_bh.b_state);
+ bp->l_bh.b_rdev = bp->l_bh.b_dev;
+ bp->l_bh.b_rsector = bp->l_blkno << (log->l2bsize - 9);
+ generic_make_request(READ, &bp->l_bh);
+ run_task_queue(&tq_disk);
+
+ wait_event(bp->l_ioevent, (bp->l_flag != lbmREAD));
+
+ return 0;
+}
+
+
+/*
+ * lbmWrite()
+ *
+ * buffer at head of pageout queue stays after completion of
+ * partial-page pageout and redriven by explicit initiation of
+ * pageout by caller until full-page pageout is completed and
+ * released.
+ *
+ * device driver i/o done redrives pageout of new buffer at
+ * head of pageout queue when current buffer at head of pageout
+ * queue is released at the completion of its full-page pageout.
+ *
+ * LOGGC_LOCK() serializes lbmWrite() by lmNextPage() and lmGroupCommit().
+ * LCACHE_LOCK() serializes xflag between lbmWrite() and lbmIODone()
+ */
+static void lbmWrite(struct jfs_log * log, struct lbuf * bp, int flag,
+ int cant_block)
+{
+ struct lbuf *tail;
+ unsigned long flags;
+
+ jfs_info("lbmWrite: bp:0x%p flag:0x%x pn:0x%x", bp, flag, bp->l_pn);
+
+ /* map the logical block address to physical block address */
+ bp->l_blkno =
+ log->base + (bp->l_pn << (L2LOGPSIZE - log->l2bsize));
+
+ LCACHE_LOCK(flags); /* disable+lock */
+
+ /*
+ * initialize buffer for device driver
+ */
+ bp->l_flag = flag;
+
+ /*
+ * insert bp at tail of write queue associated with log
+ *
+ * (request is either for bp already/currently at head of queue
+ * or new bp to be inserted at tail)
+ */
+ tail = log->wqueue;
+
+ /* is buffer not already on write queue ? */
+ if (bp->l_wqnext == NULL) {
+ /* insert at tail of wqueue */
+ if (tail == NULL) {
+ log->wqueue = bp;
+ bp->l_wqnext = bp;
+ } else {
+ log->wqueue = bp;
+ bp->l_wqnext = tail->l_wqnext;
+ tail->l_wqnext = bp;
+ }
+
+ tail = bp;
+ }
+
+ /* is buffer at head of wqueue and for write ? */
+ if ((bp != tail->l_wqnext) || !(flag & lbmWRITE)) {
+ LCACHE_UNLOCK(flags); /* unlock+enable */
+ return;
+ }
+
+ LCACHE_UNLOCK(flags); /* unlock+enable */
+
+ if (cant_block)
+ lbmRedrive(bp);
+ else if (flag & lbmSYNC)
+ lbmStartIO(bp);
+ else {
+ LOGGC_UNLOCK(log);
+ lbmStartIO(bp);
+ LOGGC_LOCK(log);
+ }
+}
+
+
+/*
+ * lbmDirectWrite()
+ *
+ * initiate pageout bypassing write queue for sidestream
+ * (e.g., log superblock) write;
+ */
+static void lbmDirectWrite(struct jfs_log * log, struct lbuf * bp, int flag)
+{
+ jfs_info("lbmDirectWrite: bp:0x%p flag:0x%x pn:0x%x",
+ bp, flag, bp->l_pn);
+
+ /*
+ * initialize buffer for device driver
+ */
+ bp->l_flag = flag | lbmDIRECT;
+
+ /* map the logical block address to physical block address */
+ bp->l_blkno =
+ log->base + (bp->l_pn << (L2LOGPSIZE - log->l2bsize));
+
+ /*
+ * initiate pageout of the page
+ */
+ lbmStartIO(bp);
+}
+
+
+/*
+ * NAME: lbmStartIO()
+ *
+ * FUNCTION: Interface to DD strategy routine
+ *
+ * RETURN: none
+ *
+ * serialization: LCACHE_LOCK() is NOT held during log i/o;
+ */
+static void lbmStartIO(struct lbuf * bp)
+{
+ jfs_info("lbmStartIO");
+
+ bp->l_bh.b_reqnext = NULL;
+ set_bit(BH_Dirty, &bp->l_bh.b_state);
+// lock_buffer(&bp->l_bh);
+ assert(!test_bit(BH_Lock, &bp->l_bh.b_state));
+ set_bit(BH_Lock, &bp->l_bh.b_state);
+
+ set_bit(BH_Mapped, &bp->l_bh.b_state);
+ set_bit(BH_Req, &bp->l_bh.b_state);
+ bp->l_bh.b_rdev = bp->l_bh.b_dev;
+ bp->l_bh.b_rsector = bp->l_blkno << (bp->l_log->l2bsize - 9);
+
+ if (bp->l_log->no_integrity)
+ /* don't really do I/O */
+ lbmIODone(&bp->l_bh, 1);
+ else
+ generic_make_request(WRITE, &bp->l_bh);
+
+ INCREMENT(lmStat.submitted);
+ run_task_queue(&tq_disk);
+}
+
+
+/*
+ * lbmIOWait()
+ */
+static int lbmIOWait(struct lbuf * bp, int flag)
+{
+ unsigned long flags;
+ int rc = 0;
+
+ jfs_info("lbmIOWait1: bp:0x%p flag:0x%x:0x%x", bp, bp->l_flag, flag);
+
+ LCACHE_LOCK(flags); /* disable+lock */
+
+ LCACHE_SLEEP_COND(bp->l_ioevent, (bp->l_flag & lbmDONE), flags);
+
+ rc = (bp->l_flag & lbmERROR) ? -EIO : 0;
+
+ if (flag & lbmFREE)
+ lbmfree(bp);
+
+ LCACHE_UNLOCK(flags); /* unlock+enable */
+
+ jfs_info("lbmIOWait2: bp:0x%p flag:0x%x:0x%x", bp, bp->l_flag, flag);
+ return rc;
+}
+
+/*
+ * lbmIODone()
+ *
+ * executed at INTIODONE level
+ */
+static void lbmIODone(struct buffer_head *bh, int uptodate)
+{
+ struct lbuf *bp = bh->b_private;
+ struct lbuf *nextbp, *tail;
+ struct jfs_log *log;
+ unsigned long flags;
+
+ /*
+ * get back jfs buffer bound to the i/o buffer
+ */
+ jfs_info("lbmIODone: bp:0x%p flag:0x%x", bp, bp->l_flag);
+
+ LCACHE_LOCK(flags); /* disable+lock */
+
+ unlock_buffer(&bp->l_bh);
+ bp->l_flag |= lbmDONE;
+
+ if (!uptodate) {
+ bp->l_flag |= lbmERROR;
+
+ jfs_err("lbmIODone: I/O error in JFS log");
+ }
+
+ /*
+ * pagein completion
+ */
+ if (bp->l_flag & lbmREAD) {
+ bp->l_flag &= ~lbmREAD;
+
+ LCACHE_UNLOCK(flags); /* unlock+enable */
+
+ /* wakeup I/O initiator */
+ LCACHE_WAKEUP(&bp->l_ioevent);
+
+ return;
+ }
+
+ /*
+ * pageout completion
+ *
+ * the bp at the head of write queue has completed pageout.
+ *
+ * if single-commit/full-page pageout, remove the current buffer
+ * from head of pageout queue, and redrive pageout with
+ * the new buffer at head of pageout queue;
+ * otherwise, the partial-page pageout buffer stays at
+ * the head of pageout queue to be redriven for pageout
+ * by lmGroupCommit() until full-page pageout is completed.
+ */
+ bp->l_flag &= ~lbmWRITE;
+ INCREMENT(lmStat.pagedone);
+
+ /* update committed lsn */
+ log = bp->l_log;
+ log->clsn = (bp->l_pn << L2LOGPSIZE) + bp->l_ceor;
+
+ if (bp->l_flag & lbmDIRECT) {
+ LCACHE_WAKEUP(&bp->l_ioevent);
+ LCACHE_UNLOCK(flags);
+ return;
+ }
+
+ tail = log->wqueue;
+
+ /* single element queue */
+ if (bp == tail) {
+ /* remove head buffer of full-page pageout
+ * from log device write queue
+ */
+ if (bp->l_flag & lbmRELEASE) {
+ log->wqueue = NULL;
+ bp->l_wqnext = NULL;
+ }
+ }
+ /* multi element queue */
+ else {
+ /* remove head buffer of full-page pageout
+ * from log device write queue
+ */
+ if (bp->l_flag & lbmRELEASE) {
+ nextbp = tail->l_wqnext = bp->l_wqnext;
+ bp->l_wqnext = NULL;
+
+ /*
+ * redrive pageout of next page at head of write queue:
+ * redrive next page without any bound tblk
+ * (i.e., page w/o any COMMIT records), or
+ * first page of new group commit which has been
+ * queued after current page (subsequent pageout
+ * is performed synchronously, except page without
+ * any COMMITs) by lmGroupCommit() as indicated
+ * by lbmWRITE flag;
+ */
+ if (nextbp->l_flag & lbmWRITE) {
+ /*
+ * We can't do the I/O at interrupt time.
+ * The jfsIO thread can do it
+ */
+ lbmRedrive(nextbp);
+ }
+ }
+ }
+
+ /*
+ * synchronous pageout:
+ *
+ * buffer has not necessarily been removed from write queue
+ * (e.g., synchronous write of partial-page with COMMIT):
+ * leave buffer for i/o initiator to dispose
+ */
+ if (bp->l_flag & lbmSYNC) {
+ LCACHE_UNLOCK(flags); /* unlock+enable */
+
+ /* wakeup I/O initiator */
+ LCACHE_WAKEUP(&bp->l_ioevent);
+ }
+
+ /*
+ * Group Commit pageout:
+ */
+ else if (bp->l_flag & lbmGC) {
+ LCACHE_UNLOCK(flags);
+ lmPostGC(bp);
+ }
+
+ /*
+ * asynchronous pageout:
+ *
+ * buffer must have been removed from write queue:
+ * insert buffer at head of freelist where it can be recycled
+ */
+ else {
+ assert(bp->l_flag & lbmRELEASE);
+ assert(bp->l_flag & lbmFREE);
+ lbmfree(bp);
+
+ LCACHE_UNLOCK(flags); /* unlock+enable */
+ }
+}
+
+int jfsIOWait(void *arg)
+{
+ struct lbuf *bp;
+
+ lock_kernel();
+
+ daemonize();
+ current->tty = NULL;
+ strcpy(current->comm, "jfsIO");
+
+ unlock_kernel();
+
+ spin_lock_irq(&current->sigmask_lock);
+ sigfillset(&current->blocked);
+ recalc_sigpending(current);
+ spin_unlock_irq(&current->sigmask_lock);
+
+ complete(&jfsIOwait);
+
+ do {
+ DECLARE_WAITQUEUE(wq, current);
+
+ spin_lock_irq(&log_redrive_lock);
+ while ((bp = log_redrive_list)) {
+ log_redrive_list = bp->l_redrive_next;
+ bp->l_redrive_next = NULL;
+ spin_unlock_irq(&log_redrive_lock);
+ lbmStartIO(bp);
+ spin_lock_irq(&log_redrive_lock);
+ }
+ add_wait_queue(&jfs_IO_thread_wait, &wq);
+ set_current_state(TASK_INTERRUPTIBLE);
+ spin_unlock_irq(&log_redrive_lock);
+ schedule();
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&jfs_IO_thread_wait, &wq);
+ } while (!jfs_stop_threads);
+
+ jfs_info("jfsIOWait being killed!");
+ complete_and_exit(&jfsIOwait, 0);
+}
+
+/*
+ * NAME: lmLogFormat()/jfs_logform()
+ *
+ * FUNCTION: format file system log
+ *
+ * PARAMETERS:
+ * log - volume log
+ * logAddress - start address of log space in FS block
+ * logSize - length of log space in FS block;
+ *
+ * RETURN: 0 - success
+ * -EIO - i/o error
+ *
+ * XXX: We're synchronously writing one page at a time. This needs to
+ * be improved by writing multiple pages at once.
+ */
+int lmLogFormat(struct jfs_log *log, s64 logAddress, int logSize)
+{
+ int rc = -EIO;
+ struct jfs_sb_info *sbi = JFS_SBI(log->sb);
+ struct logsuper *logsuper;
+ struct logpage *lp;
+ int lspn; /* log sequence page number */
+ struct lrd *lrd_ptr;
+ int npages = 0;
+ struct lbuf *bp;
+
+ jfs_info("lmLogFormat: logAddress:%Ld logSize:%d",
+ (long long)logAddress, logSize);
+
+ /* allocate a log buffer */
+ bp = lbmAllocate(log, 1);
+
+ npages = logSize >> sbi->l2nbperpage;
+
+ /*
+ * log space:
+ *
+ * page 0 - reserved;
+ * page 1 - log superblock;
+ * page 2 - log data page: A SYNC log record is written
+ * into this page at logform time;
+ * pages 3-N - log data page: set to empty log data pages;
+ */
+ /*
+ * init log superblock: log page 1
+ */
+ logsuper = (struct logsuper *) bp->l_ldata;
+
+ logsuper->magic = cpu_to_le32(LOGMAGIC);
+ logsuper->version = cpu_to_le32(LOGVERSION);
+ logsuper->state = cpu_to_le32(LOGREDONE);
+ logsuper->flag = cpu_to_le32(sbi->mntflag); /* ? */
+ logsuper->size = cpu_to_le32(npages);
+ logsuper->bsize = cpu_to_le32(sbi->bsize);
+ logsuper->l2bsize = cpu_to_le32(sbi->l2bsize);
+ logsuper->end = cpu_to_le32(2 * LOGPSIZE + LOGPHDRSIZE + LOGRDSIZE);
+
+ bp->l_flag = lbmWRITE | lbmSYNC | lbmDIRECT;
+ bp->l_blkno = logAddress + sbi->nbperpage;
+ lbmStartIO(bp);
+ if ((rc = lbmIOWait(bp, 0)))
+ goto exit;
+
+ /*
+ * init pages 2 to npages-1 as log data pages:
+ *
+ * log page sequence number (lpsn) initialization:
+ *
+ * pn: 0 1 2 3 n-1
+ * +-----+-----+=====+=====+===.....===+=====+
+ * lspn: N-1 0 1 N-2
+ * <--- N page circular file ---->
+ *
+ * the N (= npages-2) data pages of the log is maintained as
+ * a circular file for the log records;
+ * lpsn grows by 1 monotonically as each log page is written
+ * to the circular file of the log;
+ * and setLogpage() will not reset the page number even if
+ * the eor is equal to LOGPHDRSIZE. In order for binary search
+ * still work in find log end process, we have to simulate the
+ * log wrap situation at the log format time.
+ * The 1st log page written will have the highest lpsn. Then
+ * the succeeding log pages will have ascending order of
+ * the lspn starting from 0, ... (N-2)
+ */
+ lp = (struct logpage *) bp->l_ldata;
+ /*
+ * initialize 1st log page to be written: lpsn = N - 1,
+ * write a SYNCPT log record is written to this page
+ */
+ lp->h.page = lp->t.page = cpu_to_le32(npages - 3);
+ lp->h.eor = lp->t.eor = cpu_to_le16(LOGPHDRSIZE + LOGRDSIZE);
+
+ lrd_ptr = (struct lrd *) &lp->data;
+ lrd_ptr->logtid = 0;
+ lrd_ptr->backchain = 0;
+ lrd_ptr->type = cpu_to_le16(LOG_SYNCPT);
+ lrd_ptr->length = 0;
+ lrd_ptr->log.syncpt.sync = 0;
+
+ bp->l_blkno += sbi->nbperpage;
+ bp->l_flag = lbmWRITE | lbmSYNC | lbmDIRECT;
+ lbmStartIO(bp);
+ if ((rc = lbmIOWait(bp, 0)))
+ goto exit;
+
+ /*
+ * initialize succeeding log pages: lpsn = 0, 1, ..., (N-2)
+ */
+ for (lspn = 0; lspn < npages - 3; lspn++) {
+ lp->h.page = lp->t.page = cpu_to_le32(lspn);
+ lp->h.eor = lp->t.eor = cpu_to_le16(LOGPHDRSIZE);
+
+ bp->l_blkno += sbi->nbperpage;
+ bp->l_flag = lbmWRITE | lbmSYNC | lbmDIRECT;
+ lbmStartIO(bp);
+ if ((rc = lbmIOWait(bp, 0)))
+ goto exit;
+ }
+
+ rc = 0;
+exit:
+ /*
+ * finalize log
+ */
+ /* release the buffer */
+ lbmFree(bp);
+
+ return rc;
+}
+
+#ifdef CONFIG_JFS_STATISTICS
+int jfs_lmstats_read(char *buffer, char **start, off_t offset, int length,
+ int *eof, void *data)
+{
+ int len = 0;
+ off_t begin;
+
+ len += sprintf(buffer,
+ "JFS Logmgr stats\n"
+ "================\n"
+ "commits = %d\n"
+ "writes submitted = %d\n"
+ "writes completed = %d\n"
+ "full pages submitted = %d\n"
+ "partial pages submitted = %d\n",
+ lmStat.commit,
+ lmStat.submitted,
+ lmStat.pagedone,
+ lmStat.full_page,
+ lmStat.partial_page);
+
+ begin = offset;
+ *start = buffer + begin;
+ len -= begin;
+
+ if (len > length)
+ len = length;
+ else
+ *eof = 1;
+
+ if (len < 0)
+ len = 0;
+
+ return len;
+}
+#endif /* CONFIG_JFS_STATISTICS */
diff --git a/fs/jfs/jfs_logmgr.h b/fs/jfs/jfs_logmgr.h
new file mode 100644
index 00000000000000..1fdebeea50bd70
--- /dev/null
+++ b/fs/jfs/jfs_logmgr.h
@@ -0,0 +1,517 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2000-2003
+ * Portions Copyright (c) Christoph Hellwig, 2001-2002
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _H_JFS_LOGMGR
+#define _H_JFS_LOGMGR
+
+#include "jfs_filsys.h"
+#include "jfs_lock.h"
+
+/*
+ * log manager configuration parameters
+ */
+
+/* log page size */
+#define LOGPSIZE 4096
+#define L2LOGPSIZE 12
+
+#define LOGPAGES 16 /* Log pages per mounted file system */
+
+/*
+ * log logical volume
+ *
+ * a log is used to make the commit operation on journalled
+ * files within the same logical volume group atomic.
+ * a log is implemented with a logical volume.
+ * there is one log per logical volume group.
+ *
+ * block 0 of the log logical volume is not used (ipl etc).
+ * block 1 contains a log "superblock" and is used by logFormat(),
+ * lmLogInit(), lmLogShutdown(), and logRedo() to record status
+ * of the log but is not otherwise used during normal processing.
+ * blocks 2 - (N-1) are used to contain log records.
+ *
+ * when a volume group is varied-on-line, logRedo() must have
+ * been executed before the file systems (logical volumes) in
+ * the volume group can be mounted.
+ */
+/*
+ * log superblock (block 1 of logical volume)
+ */
+#define LOGSUPER_B 1
+#define LOGSTART_B 2
+
+#define LOGMAGIC 0x87654321
+#define LOGVERSION 1
+
+#define MAX_ACTIVE 128 /* Max active file systems sharing log */
+
+struct logsuper {
+ u32 magic; /* 4: log lv identifier */
+ s32 version; /* 4: version number */
+ s32 serial; /* 4: log open/mount counter */
+ s32 size; /* 4: size in number of LOGPSIZE blocks */
+ s32 bsize; /* 4: logical block size in byte */
+ s32 l2bsize; /* 4: log2 of bsize */
+
+ u32 flag; /* 4: option */
+ u32 state; /* 4: state - see below */
+
+ s32 end; /* 4: addr of last log record set by logredo */
+ char uuid[16]; /* 16: 128-bit journal uuid */
+ char label[16]; /* 16: journal label */
+ struct {
+ char uuid[16];
+ } active[MAX_ACTIVE]; /* 2048: active file systems list */
+};
+
+#define NULL_UUID "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+
+/* log flag: commit option (see jfs_filsys.h) */
+
+/* log state */
+#define LOGMOUNT 0 /* log mounted by lmLogInit() */
+#define LOGREDONE 1 /* log shutdown by lmLogShutdown().
+ * log redo completed by logredo().
+ */
+#define LOGWRAP 2 /* log wrapped */
+#define LOGREADERR 3 /* log read error detected in logredo() */
+
+
+/*
+ * log logical page
+ *
+ * (this comment should be rewritten !)
+ * the header and trailer structures (h,t) will normally have
+ * the same page and eor value.
+ * An exception to this occurs when a complete page write is not
+ * accomplished on a power failure. Since the hardware may "split write"
+ * sectors in the page, any out of order sequence may occur during powerfail
+ * and needs to be recognized during log replay. The xor value is
+ * an "exclusive or" of all log words in the page up to eor. This
+ * 32 bit eor is stored with the top 16 bits in the header and the
+ * bottom 16 bits in the trailer. logredo can easily recognize pages
+ * that were not completed by reconstructing this eor and checking
+ * the log page.
+ *
+ * Previous versions of the operating system did not allow split
+ * writes and detected partially written records in logredo by
+ * ordering the updates to the header, trailer, and the move of data
+ * into the logdata area. The order: (1) data is moved (2) header
+ * is updated (3) trailer is updated. In logredo, when the header
+ * differed from the trailer, the header and trailer were reconciled
+ * as follows: if h.page != t.page they were set to the smaller of
+ * the two and h.eor and t.eor set to 8 (i.e. empty page). if (only)
+ * h.eor != t.eor they were set to the smaller of their two values.
+ */
+struct logpage {
+ struct { /* header */
+ s32 page; /* 4: log sequence page number */
+ s16 rsrvd; /* 2: */
+ s16 eor; /* 2: end-of-log offset of lasrt record write */
+ } h;
+
+ s32 data[LOGPSIZE / 4 - 4]; /* log record area */
+
+ struct { /* trailer */
+ s32 page; /* 4: normally the same as h.page */
+ s16 rsrvd; /* 2: */
+ s16 eor; /* 2: normally the same as h.eor */
+ } t;
+};
+
+#define LOGPHDRSIZE 8 /* log page header size */
+#define LOGPTLRSIZE 8 /* log page trailer size */
+
+
+/*
+ * log record
+ *
+ * (this comment should be rewritten !)
+ * jfs uses only "after" log records (only a single writer is allowed
+ * in a page, pages are written to temporary paging space if
+ * if they must be written to disk before commit, and i/o is
+ * scheduled for modified pages to their home location after
+ * the log records containing the after values and the commit
+ * record is written to the log on disk, undo discards the copy
+ * in main-memory.)
+ *
+ * a log record consists of a data area of variable length followed by
+ * a descriptor of fixed size LOGRDSIZE bytes.
+ * the data area is rounded up to an integral number of 4-bytes and
+ * must be no longer than LOGPSIZE.
+ * the descriptor is of size of multiple of 4-bytes and aligned on a
+ * 4-byte boundary.
+ * records are packed one after the other in the data area of log pages.
+ * (sometimes a DUMMY record is inserted so that at least one record ends
+ * on every page or the longest record is placed on at most two pages).
+ * the field eor in page header/trailer points to the byte following
+ * the last record on a page.
+ */
+
+/* log record types */
+#define LOG_COMMIT 0x8000
+#define LOG_SYNCPT 0x4000
+#define LOG_MOUNT 0x2000
+#define LOG_REDOPAGE 0x0800
+#define LOG_NOREDOPAGE 0x0080
+#define LOG_NOREDOINOEXT 0x0040
+#define LOG_UPDATEMAP 0x0008
+#define LOG_NOREDOFILE 0x0001
+
+/* REDOPAGE/NOREDOPAGE log record data type */
+#define LOG_INODE 0x0001
+#define LOG_XTREE 0x0002
+#define LOG_DTREE 0x0004
+#define LOG_BTROOT 0x0010
+#define LOG_EA 0x0020
+#define LOG_ACL 0x0040
+#define LOG_DATA 0x0080
+#define LOG_NEW 0x0100
+#define LOG_EXTEND 0x0200
+#define LOG_RELOCATE 0x0400
+#define LOG_DIR_XTREE 0x0800 /* Xtree is in directory inode */
+
+/* UPDATEMAP log record descriptor type */
+#define LOG_ALLOCXADLIST 0x0080
+#define LOG_ALLOCPXDLIST 0x0040
+#define LOG_ALLOCXAD 0x0020
+#define LOG_ALLOCPXD 0x0010
+#define LOG_FREEXADLIST 0x0008
+#define LOG_FREEPXDLIST 0x0004
+#define LOG_FREEXAD 0x0002
+#define LOG_FREEPXD 0x0001
+
+
+struct lrd {
+ /*
+ * type independent area
+ */
+ s32 logtid; /* 4: log transaction identifier */
+ s32 backchain; /* 4: ptr to prev record of same transaction */
+ u16 type; /* 2: record type */
+ s16 length; /* 2: length of data in record (in byte) */
+ u32 aggregate; /* 4: file system lv/aggregate */
+ /* (16) */
+
+ /*
+ * type dependent area (20)
+ */
+ union {
+
+ /*
+ * COMMIT: commit
+ *
+ * transaction commit: no type-dependent information;
+ */
+
+ /*
+ * REDOPAGE: after-image
+ *
+ * apply after-image;
+ *
+ * N.B. REDOPAGE, NOREDOPAGE, and UPDATEMAP must be same format;
+ */
+ struct {
+ u32 fileset; /* 4: fileset number */
+ u32 inode; /* 4: inode number */
+ u16 type; /* 2: REDOPAGE record type */
+ s16 l2linesize; /* 2: log2 of line size */
+ pxd_t pxd; /* 8: on-disk page pxd */
+ } redopage; /* (20) */
+
+ /*
+ * NOREDOPAGE: the page is freed
+ *
+ * do not apply after-image records which precede this record
+ * in the log with the same page block number to this page.
+ *
+ * N.B. REDOPAGE, NOREDOPAGE, and UPDATEMAP must be same format;
+ */
+ struct {
+ s32 fileset; /* 4: fileset number */
+ u32 inode; /* 4: inode number */
+ u16 type; /* 2: NOREDOPAGE record type */
+ s16 rsrvd; /* 2: reserved */
+ pxd_t pxd; /* 8: on-disk page pxd */
+ } noredopage; /* (20) */
+
+ /*
+ * UPDATEMAP: update block allocation map
+ *
+ * either in-line PXD,
+ * or out-of-line XADLIST;
+ *
+ * N.B. REDOPAGE, NOREDOPAGE, and UPDATEMAP must be same format;
+ */
+ struct {
+ u32 fileset; /* 4: fileset number */
+ u32 inode; /* 4: inode number */
+ u16 type; /* 2: UPDATEMAP record type */
+ s16 nxd; /* 2: number of extents */
+ pxd_t pxd; /* 8: pxd */
+ } updatemap; /* (20) */
+
+ /*
+ * NOREDOINOEXT: the inode extent is freed
+ *
+ * do not apply after-image records which precede this
+ * record in the log with the any of the 4 page block
+ * numbers in this inode extent.
+ *
+ * NOTE: The fileset and pxd fields MUST remain in
+ * the same fields in the REDOPAGE record format.
+ *
+ */
+ struct {
+ s32 fileset; /* 4: fileset number */
+ s32 iagnum; /* 4: IAG number */
+ s32 inoext_idx; /* 4: inode extent index */
+ pxd_t pxd; /* 8: on-disk page pxd */
+ } noredoinoext; /* (20) */
+
+ /*
+ * SYNCPT: log sync point
+ *
+ * replay log upto syncpt address specified;
+ */
+ struct {
+ s32 sync; /* 4: syncpt address (0 = here) */
+ } syncpt;
+
+ /*
+ * MOUNT: file system mount
+ *
+ * file system mount: no type-dependent information;
+ */
+
+ /*
+ * ? FREEXTENT: free specified extent(s)
+ *
+ * free specified extent(s) from block allocation map
+ * N.B.: nextents should be length of data/sizeof(xad_t)
+ */
+ struct {
+ s32 type; /* 4: FREEXTENT record type */
+ s32 nextent; /* 4: number of extents */
+
+ /* data: PXD or XAD list */
+ } freextent;
+
+ /*
+ * ? NOREDOFILE: this file is freed
+ *
+ * do not apply records which precede this record in the log
+ * with the same inode number.
+ *
+ * NOREDILE must be the first to be written at commit
+ * (last to be read in logredo()) - it prevents
+ * replay of preceding updates of all preceding generations
+ * of the inumber esp. the on-disk inode itself,
+ * but does NOT prevent
+ * replay of the
+ */
+ struct {
+ s32 fileset; /* 4: fileset number */
+ u32 inode; /* 4: inode number */
+ } noredofile;
+
+ /*
+ * ? NEWPAGE:
+ *
+ * metadata type dependent
+ */
+ struct {
+ s32 fileset; /* 4: fileset number */
+ u32 inode; /* 4: inode number */
+ s32 type; /* 4: NEWPAGE record type */
+ pxd_t pxd; /* 8: on-disk page pxd */
+ } newpage;
+
+ /*
+ * ? DUMMY: filler
+ *
+ * no type-dependent information
+ */
+ } log;
+}; /* (36) */
+
+#define LOGRDSIZE (sizeof(struct lrd))
+
+/*
+ * line vector descriptor
+ */
+struct lvd {
+ s16 offset;
+ s16 length;
+};
+
+
+/*
+ * log logical volume
+ */
+struct jfs_log {
+
+ struct super_block *sb; /* 4: This is used to sync metadata
+ * before writing syncpt. Will
+ * need to be a list if we share
+ * the log between fs's
+ */
+ struct block_device *bdev; /* 4: log lv pointer */
+ s32 serial; /* 4: log mount serial number */
+
+ s64 base; /* @8: log extent address (inline log ) */
+ int size; /* 4: log size in log page (in page) */
+ int l2bsize; /* 4: log2 of bsize */
+
+ long flag; /* 4: flag */
+
+ struct lbuf *lbuf_free; /* 4: free lbufs */
+ wait_queue_head_t free_wait; /* 4: */
+
+ /* log write */
+ int logtid; /* 4: log tid */
+ int page; /* 4: page number of eol page */
+ int eor; /* 4: eor of last record in eol page */
+ struct lbuf *bp; /* 4: current log page buffer */
+
+ struct semaphore loglock; /* 4: log write serialization lock */
+
+ /* syncpt */
+ int nextsync; /* 4: bytes to write before next syncpt */
+ int active; /* 4: */
+ wait_queue_head_t syncwait; /* 4: */
+
+ /* commit */
+ uint cflag; /* 4: */
+ struct { /* 8: FIFO commit queue header */
+ struct tblock *head;
+ struct tblock *tail;
+ } cqueue;
+ struct tblock *flush_tblk; /* tblk we're waiting on for flush */
+ int gcrtc; /* 4: GC_READY transaction count */
+ struct tblock *gclrt; /* 4: latest GC_READY transaction */
+ spinlock_t gclock; /* 4: group commit lock */
+ int logsize; /* 4: log data area size in byte */
+ int lsn; /* 4: end-of-log */
+ int clsn; /* 4: clsn */
+ int syncpt; /* 4: addr of last syncpt record */
+ int sync; /* 4: addr from last logsync() */
+ struct list_head synclist; /* 8: logsynclist anchor */
+ spinlock_t synclock; /* 4: synclist lock */
+ struct lbuf *wqueue; /* 4: log pageout queue */
+ int count; /* 4: count */
+ char uuid[16]; /* 16: 128-bit uuid of log device */
+
+ int no_integrity; /* flag to disable journaling to disk */
+ int ni_page; /* backup of page for nointegrity option */
+ int ni_eor; /* backup of eor for nointegrity option */
+};
+
+/*
+ * Log flag
+ */
+#define log_INLINELOG 1
+#define log_SYNCBARRIER 2
+#define log_QUIESCE 3
+#define log_FLUSH 4
+
+/*
+ * group commit flag
+ */
+/* jfs_log */
+#define logGC_PAGEOUT 0x00000001
+
+/* tblock/lbuf */
+#define tblkGC_QUEUE 0x0001
+#define tblkGC_READY 0x0002
+#define tblkGC_COMMIT 0x0004
+#define tblkGC_COMMITTED 0x0008
+#define tblkGC_EOP 0x0010
+#define tblkGC_FREE 0x0020
+#define tblkGC_LEADER 0x0040
+#define tblkGC_ERROR 0x0080
+#define tblkGC_LAZY 0x0100 // D230860
+#define tblkGC_UNLOCKED 0x0200 // D230860
+
+/*
+ * log cache buffer header
+ */
+struct lbuf {
+ struct buffer_head l_bh; /* for doing I/O */
+ struct jfs_log *l_log; /* 4: log associated with buffer */
+
+ /*
+ * data buffer base area
+ */
+ uint l_flag; /* 4: pageout control flags */
+
+ struct lbuf *l_wqnext; /* 4: write queue link */
+ struct lbuf *l_freelist; /* 4: freelistlink */
+
+ int l_pn; /* 4: log page number */
+ int l_eor; /* 4: log record eor */
+ int l_ceor; /* 4: committed log record eor */
+
+ s64 l_blkno; /* 8: log page block number */
+ caddr_t l_ldata; /* 4: data page */
+
+ wait_queue_head_t l_ioevent; /* 4: i/o done event */
+ struct page *l_page; /* The page itself */
+};
+
+/* Reuse l_freelist for redrive list */
+#define l_redrive_next l_freelist
+
+/*
+ * logsynclist block
+ *
+ * common logsyncblk prefix for jbuf_t and tblock
+ */
+struct logsyncblk {
+ u16 xflag; /* flags */
+ u16 flag; /* only meaninful in tblock */
+ lid_t lid; /* lock id */
+ s32 lsn; /* log sequence number */
+ struct list_head synclist; /* log sync list link */
+};
+
+/*
+ * logsynclist serialization (per log)
+ */
+
+#define LOGSYNC_LOCK_INIT(log) spin_lock_init(&(log)->synclock)
+#define LOGSYNC_LOCK(log) spin_lock(&(log)->synclock)
+#define LOGSYNC_UNLOCK(log) spin_unlock(&(log)->synclock)
+
+/* compute the difference in bytes of lsn from sync point */
+#define logdiff(diff, lsn, log)\
+{\
+ diff = (lsn) - (log)->syncpt;\
+ if (diff < 0)\
+ diff += (log)->logsize;\
+}
+
+extern int lmLogOpen(struct super_block *sb, struct jfs_log ** log);
+extern int lmLogClose(struct super_block *sb, struct jfs_log * log);
+extern int lmLogShutdown(struct jfs_log * log);
+extern int lmLogInit(struct jfs_log * log);
+extern int lmLogFormat(struct jfs_log *log, s64 logAddress, int logSize);
+extern void jfs_flush_journal(struct jfs_log * log, int wait);
+
+#endif /* _H_JFS_LOGMGR */
diff --git a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c
new file mode 100644
index 00000000000000..84d86a56e5e29c
--- /dev/null
+++ b/fs/jfs/jfs_metapage.c
@@ -0,0 +1,637 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2000-2004
+ * Portions Copyright (C) Christoph Hellwig, 2001-2002
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/fs.h>
+#include <linux/init.h>
+#include "jfs_incore.h"
+#include "jfs_superblock.h"
+#include "jfs_filsys.h"
+#include "jfs_metapage.h"
+#include "jfs_txnmgr.h"
+#include "jfs_debug.h"
+
+extern struct task_struct *jfsCommitTask;
+static spinlock_t meta_lock = SPIN_LOCK_UNLOCKED;
+static wait_queue_head_t meta_wait;
+
+#ifdef CONFIG_JFS_STATISTICS
+struct {
+ uint pagealloc; /* # of page allocations */
+ uint pagefree; /* # of page frees */
+ uint lockwait; /* # of sleeping lock_metapage() calls */
+ uint allocwait; /* # of sleeping alloc_metapage() calls */
+} mpStat;
+#endif
+
+
+#define HASH_BITS 10 /* This makes hash_table 1 4K page */
+#define HASH_SIZE (1 << HASH_BITS)
+static struct metapage **hash_table = NULL;
+static unsigned long hash_order;
+
+
+static inline int metapage_locked(struct metapage *mp)
+{
+ return test_bit(META_locked, &mp->flag);
+}
+
+static inline int trylock_metapage(struct metapage *mp)
+{
+ return test_and_set_bit(META_locked, &mp->flag);
+}
+
+static inline void unlock_metapage(struct metapage *mp)
+{
+ clear_bit(META_locked, &mp->flag);
+ wake_up(&mp->wait);
+}
+
+static void __lock_metapage(struct metapage *mp)
+{
+ DECLARE_WAITQUEUE(wait, current);
+
+ INCREMENT(mpStat.lockwait);
+
+ add_wait_queue_exclusive(&mp->wait, &wait);
+ do {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ if (metapage_locked(mp)) {
+ spin_unlock(&meta_lock);
+ schedule();
+ spin_lock(&meta_lock);
+ }
+ } while (trylock_metapage(mp));
+ __set_current_state(TASK_RUNNING);
+ remove_wait_queue(&mp->wait, &wait);
+}
+
+/* needs meta_lock */
+static inline void lock_metapage(struct metapage *mp)
+{
+ if (trylock_metapage(mp))
+ __lock_metapage(mp);
+}
+
+/*
+ * metapage pool is based on Linux 2.5's mempool
+ *
+ * Tap into reserved structures in critical paths where waiting on a
+ * memory allocation could cause deadlock
+ */
+#define METAPOOL_MIN_PAGES 32
+static struct metapage *reserved_metapages[METAPOOL_MIN_PAGES];
+static int num_reserved = 0;
+kmem_cache_t *metapage_cache;
+
+static void init_once(void *foo, kmem_cache_t *cachep, unsigned long flags)
+{
+ struct metapage *mp = (struct metapage *)foo;
+
+ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+ SLAB_CTOR_CONSTRUCTOR) {
+ mp->lid = 0;
+ mp->lsn = 0;
+ mp->flag = 0;
+ mp->data = NULL;
+ mp->clsn = 0;
+ mp->log = NULL;
+ set_bit(META_free, &mp->flag);
+ init_waitqueue_head(&mp->wait);
+ }
+}
+
+static void empty_reserved(void)
+{
+ while (num_reserved--)
+ kmem_cache_free(metapage_cache,
+ reserved_metapages[num_reserved]);
+}
+
+static struct metapage *alloc_metapage(int *dropped_lock, int no_wait)
+{
+ struct metapage *new;
+
+ *dropped_lock = 0;
+
+ /*
+ * Always try an atomic alloc first, to avoid dropping the
+ * spinlock
+ */
+ new = kmem_cache_alloc(metapage_cache, GFP_ATOMIC);
+ if (new)
+ return new;
+
+ if (no_wait && num_reserved)
+ return reserved_metapages[--num_reserved];
+
+ *dropped_lock = 1;
+ spin_unlock(&meta_lock);
+ new = kmem_cache_alloc(metapage_cache, GFP_NOFS);
+ spin_lock(&meta_lock);
+ return new;
+}
+
+static void __free_metapage(struct metapage *mp)
+{
+ mp->flag = 0;
+ set_bit(META_free, &mp->flag);
+
+ if (num_reserved < METAPOOL_MIN_PAGES)
+ reserved_metapages[num_reserved++] = mp;
+ else
+ kmem_cache_free(metapage_cache, mp);
+}
+
+static inline void free_metapage(struct metapage * mp)
+{
+ spin_lock(&meta_lock);
+ __free_metapage(mp);
+ spin_unlock(&meta_lock);
+}
+
+int __init metapage_init(void)
+{
+ struct metapage *mp;
+
+ /*
+ * Initialize wait queue
+ */
+ init_waitqueue_head(&meta_wait);
+
+ /*
+ * Allocate the metapage structures
+ */
+ metapage_cache = kmem_cache_create("jfs_mp", sizeof(struct metapage),
+ 0, 0, init_once, NULL);
+ if (metapage_cache == NULL)
+ return -ENOMEM;
+
+ while (num_reserved < METAPOOL_MIN_PAGES) {
+ mp = kmem_cache_alloc(metapage_cache, GFP_NOFS);
+ if (mp)
+ reserved_metapages[num_reserved++] = mp;
+ else {
+ empty_reserved();
+ kmem_cache_destroy(metapage_cache);
+ return -ENOMEM;
+ }
+ }
+ /*
+ * Now the hash list
+ */
+ for (hash_order = 0;
+ ((PAGE_SIZE << hash_order) / sizeof(void *)) < HASH_SIZE;
+ hash_order++);
+ hash_table =
+ (struct metapage **) __get_free_pages(GFP_KERNEL, hash_order);
+ assert(hash_table);
+ memset(hash_table, 0, PAGE_SIZE << hash_order);
+
+ return 0;
+}
+
+void metapage_exit(void)
+{
+ empty_reserved();
+ kmem_cache_destroy(metapage_cache);
+}
+
+/*
+ * Basically same hash as in pagemap.h, but using our hash table
+ */
+static struct metapage **meta_hash(struct address_space *mapping,
+ unsigned long index)
+{
+#define i (((unsigned long)mapping)/ \
+ (sizeof(struct inode) & ~(sizeof(struct inode) -1 )))
+#define s(x) ((x) + ((x) >> HASH_BITS))
+ return hash_table + (s(i + index) & (HASH_SIZE - 1));
+#undef i
+#undef s
+}
+
+static struct metapage *search_hash(struct metapage ** hash_ptr,
+ struct address_space *mapping,
+ unsigned long index)
+{
+ struct metapage *ptr;
+
+ for (ptr = *hash_ptr; ptr; ptr = ptr->hash_next) {
+ if ((ptr->mapping == mapping) && (ptr->index == index))
+ return ptr;
+ }
+
+ return NULL;
+}
+
+static void add_to_hash(struct metapage * mp, struct metapage ** hash_ptr)
+{
+ if (*hash_ptr)
+ (*hash_ptr)->hash_prev = mp;
+
+ mp->hash_prev = NULL;
+ mp->hash_next = *hash_ptr;
+ *hash_ptr = mp;
+}
+
+static void remove_from_hash(struct metapage * mp, struct metapage ** hash_ptr)
+{
+ if (mp->hash_prev)
+ mp->hash_prev->hash_next = mp->hash_next;
+ else {
+ assert(*hash_ptr == mp);
+ *hash_ptr = mp->hash_next;
+ }
+
+ if (mp->hash_next)
+ mp->hash_next->hash_prev = mp->hash_prev;
+}
+
+struct metapage *__get_metapage(struct inode *inode, unsigned long lblock,
+ unsigned int size, int absolute,
+ unsigned long new)
+{
+ int dropped_lock;
+ struct metapage **hash_ptr;
+ int l2BlocksPerPage;
+ int l2bsize;
+ int no_wait;
+ struct address_space *mapping;
+ struct metapage *mp;
+ unsigned long page_index;
+ unsigned long page_offset;
+
+ jfs_info("__get_metapage: inode = 0x%p, lblock = 0x%lx", inode, lblock);
+
+ if (absolute)
+ mapping = inode->i_sb->s_bdev->bd_inode->i_mapping;
+ else {
+ /*
+ * If an nfs client tries to read an inode that is larger
+ * than any existing inodes, we may try to read past the
+ * end of the inode map
+ */
+ if ((lblock << inode->i_blkbits) >= inode->i_size)
+ return NULL;
+ mapping = inode->i_mapping;
+ }
+
+ hash_ptr = meta_hash(mapping, lblock);
+again:
+ spin_lock(&meta_lock);
+ mp = search_hash(hash_ptr, mapping, lblock);
+ if (mp) {
+ page_found:
+ if (test_bit(META_stale, &mp->flag)) {
+ spin_unlock(&meta_lock);
+ yield();
+ goto again;
+ }
+ mp->count++;
+ lock_metapage(mp);
+ spin_unlock(&meta_lock);
+ if (test_bit(META_discard, &mp->flag)) {
+ if (!new) {
+ jfs_error(inode->i_sb,
+ "__get_metapage: using a "
+ "discarded metapage");
+ release_metapage(mp);
+ return NULL;
+ }
+ clear_bit(META_discard, &mp->flag);
+ }
+ jfs_info("__get_metapage: found 0x%p, in hash", mp);
+ if (mp->logical_size != size) {
+ jfs_error(inode->i_sb,
+ "__get_metapage: mp->logical_size != size");
+ release_metapage(mp);
+ return NULL;
+ }
+ } else {
+ l2bsize = inode->i_blkbits;
+ l2BlocksPerPage = PAGE_CACHE_SHIFT - l2bsize;
+ page_index = lblock >> l2BlocksPerPage;
+ page_offset = (lblock - (page_index << l2BlocksPerPage)) <<
+ l2bsize;
+ if ((page_offset + size) > PAGE_CACHE_SIZE) {
+ spin_unlock(&meta_lock);
+ jfs_err("MetaData crosses page boundary!!");
+ return NULL;
+ }
+
+ /*
+ * Locks held on aggregate inode pages are usually
+ * not held long, and they are taken in critical code
+ * paths (committing dirty inodes, txCommit thread)
+ *
+ * Attempt to get metapage without blocking, tapping into
+ * reserves if necessary.
+ */
+ if (JFS_IP(inode)->fileset == AGGREGATE_I)
+ no_wait = 1;
+ else
+ no_wait = 0;
+
+ mp = alloc_metapage(&dropped_lock, no_wait);
+ if (!mp) {
+ spin_unlock(&meta_lock);
+ return NULL;
+ }
+ if (dropped_lock) {
+ /* alloc_metapage blocked, we need to search the hash
+ * again.
+ */
+ struct metapage *mp2;
+ mp2 = search_hash(hash_ptr, mapping, lblock);
+ if (mp2) {
+ __free_metapage(mp);
+ mp = mp2;
+ goto page_found;
+ }
+ }
+ mp->flag = 0;
+ lock_metapage(mp);
+ if (absolute)
+ set_bit(META_absolute, &mp->flag);
+ mp->xflag = COMMIT_PAGE;
+ mp->count = 1;
+ atomic_set(&mp->nohomeok,0);
+ mp->mapping = mapping;
+ mp->index = lblock;
+ mp->page = 0;
+ mp->logical_size = size;
+ add_to_hash(mp, hash_ptr);
+ spin_unlock(&meta_lock);
+
+ if (new) {
+ jfs_info("__get_metapage: Calling grab_cache_page");
+ mp->page = grab_cache_page(mapping, page_index);
+ if (!mp->page) {
+ jfs_err("grab_cache_page failed!");
+ goto freeit;
+ } else {
+ INCREMENT(mpStat.pagealloc);
+ UnlockPage(mp->page);
+ }
+ } else {
+ jfs_info("__get_metapage: Calling read_cache_page");
+ mp->page = read_cache_page(mapping, lblock,
+ (filler_t *)mapping->a_ops->readpage, NULL);
+ if (IS_ERR(mp->page)) {
+ jfs_err("read_cache_page failed!");
+ goto freeit;
+ } else
+ INCREMENT(mpStat.pagealloc);
+ }
+ mp->data = kmap(mp->page) + page_offset;
+ }
+
+ if (new)
+ memset(mp->data, 0, PSIZE);
+
+ jfs_info("__get_metapage: returning = 0x%p", mp);
+ return mp;
+
+freeit:
+ spin_lock(&meta_lock);
+ remove_from_hash(mp, hash_ptr);
+ __free_metapage(mp);
+ spin_unlock(&meta_lock);
+ return NULL;
+}
+
+void hold_metapage(struct metapage * mp, int force)
+{
+ spin_lock(&meta_lock);
+
+ mp->count++;
+
+ if (force) {
+ ASSERT (!(test_bit(META_forced, &mp->flag)));
+ if (trylock_metapage(mp))
+ set_bit(META_forced, &mp->flag);
+ } else
+ lock_metapage(mp);
+
+ spin_unlock(&meta_lock);
+}
+
+static void __write_metapage(struct metapage * mp)
+{
+ int l2bsize = mp->mapping->host->i_blkbits;
+ int l2BlocksPerPage = PAGE_CACHE_SHIFT - l2bsize;
+ unsigned long page_index;
+ unsigned long page_offset;
+ int rc;
+
+ jfs_info("__write_metapage: mp = 0x%p", mp);
+
+ if (test_bit(META_discard, &mp->flag)) {
+ /*
+ * This metadata is no longer valid
+ */
+ clear_bit(META_dirty, &mp->flag);
+ return;
+ }
+
+ page_index = mp->page->index;
+ page_offset =
+ (mp->index - (page_index << l2BlocksPerPage)) << l2bsize;
+
+ lock_page(mp->page);
+ rc = mp->mapping->a_ops->prepare_write(NULL, mp->page, page_offset,
+ page_offset +
+ mp->logical_size);
+ if (rc) {
+ jfs_err("prepare_write return %d!", rc);
+ ClearPageUptodate(mp->page);
+ UnlockPage(mp->page);
+ kunmap(mp->page);
+ clear_bit(META_dirty, &mp->flag);
+ return;
+ }
+ rc = mp->mapping->a_ops->commit_write(NULL, mp->page, page_offset,
+ page_offset +
+ mp->logical_size);
+ if (rc) {
+ jfs_err("commit_write returned %d", rc);
+ }
+
+ UnlockPage(mp->page);
+ clear_bit(META_dirty, &mp->flag);
+
+ jfs_info("__write_metapage done");
+}
+
+static inline void sync_metapage(struct metapage *mp)
+{
+ struct page *page = mp->page;
+
+ page_cache_get(page);
+ lock_page(page);
+
+ /* we're done with this page - no need to check for errors */
+ if (page->buffers) {
+ writeout_one_page(page);
+ waitfor_one_page(page);
+ }
+
+ UnlockPage(page);
+ page_cache_release(page);
+}
+
+void release_metapage(struct metapage * mp)
+{
+ struct jfs_log *log;
+
+ jfs_info("release_metapage: mp = 0x%p, flag = 0x%lx", mp, mp->flag);
+
+ spin_lock(&meta_lock);
+ if (test_bit(META_forced, &mp->flag)) {
+ clear_bit(META_forced, &mp->flag);
+ mp->count--;
+ spin_unlock(&meta_lock);
+ return;
+ }
+
+ assert(mp->count);
+ if (--mp->count || atomic_read(&mp->nohomeok)) {
+ unlock_metapage(mp);
+ spin_unlock(&meta_lock);
+ return;
+ }
+
+ if (mp->page) {
+ set_bit(META_stale, &mp->flag);
+ spin_unlock(&meta_lock);
+ kunmap(mp->page);
+ mp->data = 0;
+ if (test_bit(META_dirty, &mp->flag))
+ __write_metapage(mp);
+ if (test_bit(META_sync, &mp->flag)) {
+ sync_metapage(mp);
+ clear_bit(META_sync, &mp->flag);
+ }
+
+ if (test_bit(META_discard, &mp->flag)) {
+ lock_page(mp->page);
+ block_flushpage(mp->page, 0);
+ UnlockPage(mp->page);
+ }
+
+ page_cache_release(mp->page);
+ mp->page = NULL;
+ INCREMENT(mpStat.pagefree);
+ spin_lock(&meta_lock);
+ }
+
+ if (mp->lsn) {
+ /*
+ * Remove metapage from logsynclist.
+ */
+ log = mp->log;
+ LOGSYNC_LOCK(log);
+ mp->log = 0;
+ mp->lsn = 0;
+ mp->clsn = 0;
+ log->count--;
+ list_del(&mp->synclist);
+ LOGSYNC_UNLOCK(log);
+ }
+ remove_from_hash(mp, meta_hash(mp->mapping, mp->index));
+ spin_unlock(&meta_lock);
+
+ free_metapage(mp);
+}
+
+void __invalidate_metapages(struct inode *ip, s64 addr, int len)
+{
+ struct metapage **hash_ptr;
+ unsigned long lblock;
+ int l2BlocksPerPage = PAGE_CACHE_SHIFT - ip->i_blkbits;
+ /* All callers are interested in block device's mapping */
+ struct address_space *mapping = ip->i_sb->s_bdev->bd_inode->i_mapping;
+ struct metapage *mp;
+ struct page *page;
+
+ /*
+ * First, mark metapages to discard. They will eventually be
+ * released, but should not be written.
+ */
+ for (lblock = addr; lblock < addr + len;
+ lblock += 1 << l2BlocksPerPage) {
+ hash_ptr = meta_hash(mapping, lblock);
+again:
+ spin_lock(&meta_lock);
+ mp = search_hash(hash_ptr, mapping, lblock);
+ if (mp) {
+ if (test_bit(META_stale, &mp->flag)) {
+ spin_unlock(&meta_lock);
+ yield();
+ goto again;
+ }
+
+ set_bit(META_discard, &mp->flag);
+ spin_unlock(&meta_lock);
+ } else {
+ spin_unlock(&meta_lock);
+ page = find_lock_page(mapping, lblock>>l2BlocksPerPage);
+ if (page) {
+ block_flushpage(page, 0);
+ UnlockPage(page);
+ page_cache_release(page);
+ }
+ }
+ }
+}
+
+#ifdef CONFIG_JFS_STATISTICS
+int jfs_mpstat_read(char *buffer, char **start, off_t offset, int length,
+ int *eof, void *data)
+{
+ int len = 0;
+ off_t begin;
+
+ len += sprintf(buffer,
+ "JFS Metapage statistics\n"
+ "=======================\n"
+ "page allocations = %d\n"
+ "page frees = %d\n"
+ "lock waits = %d\n"
+ "allocation waits = %d\n",
+ mpStat.pagealloc,
+ mpStat.pagefree,
+ mpStat.lockwait,
+ mpStat.allocwait);
+
+ begin = offset;
+ *start = buffer + begin;
+ len -= begin;
+
+ if (len > length)
+ len = length;
+ else
+ *eof = 1;
+
+ if (len < 0)
+ len = 0;
+
+ return len;
+}
+#endif
diff --git a/fs/jfs/jfs_metapage.h b/fs/jfs/jfs_metapage.h
new file mode 100644
index 00000000000000..0e58aba58c37c0
--- /dev/null
+++ b/fs/jfs/jfs_metapage.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2000-2002
+ * Portions Copyright (c) Christoph Hellwig, 2001-2002
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _H_JFS_METAPAGE
+#define _H_JFS_METAPAGE
+
+#include <linux/pagemap.h>
+
+struct metapage {
+ /* Common logsyncblk prefix (see jfs_logmgr.h) */
+ u16 xflag;
+ u16 unused;
+ lid_t lid;
+ int lsn;
+ struct list_head synclist;
+ /* End of logsyncblk prefix */
+
+ unsigned long flag; /* See Below */
+ unsigned long count; /* Reference count */
+ void *data; /* Data pointer */
+
+ /* list management stuff */
+ struct metapage *hash_prev;
+ struct metapage *hash_next; /* Also used for free list */
+
+ /*
+ * mapping & index become redundant, but we need these here to
+ * add the metapage to the hash before we have the real page
+ */
+ struct address_space *mapping;
+ unsigned long index;
+ wait_queue_head_t wait;
+
+ /* implementation */
+ struct page *page;
+ unsigned long logical_size;
+
+ /* Journal management */
+ int clsn;
+ atomic_t nohomeok;
+ struct jfs_log *log;
+};
+
+/* metapage flag */
+#define META_locked 0
+#define META_absolute 1
+#define META_free 2
+#define META_dirty 3
+#define META_sync 4
+#define META_discard 5
+#define META_forced 6
+#define META_stale 7
+
+#define mark_metapage_dirty(mp) set_bit(META_dirty, &(mp)->flag)
+
+/* function prototypes */
+extern struct metapage *__get_metapage(struct inode *inode,
+ unsigned long lblock, unsigned int size,
+ int absolute, unsigned long new);
+
+#define read_metapage(inode, lblock, size, absolute)\
+ __get_metapage(inode, lblock, size, absolute, FALSE)
+
+#define get_metapage(inode, lblock, size, absolute)\
+ __get_metapage(inode, lblock, size, absolute, TRUE)
+
+extern void release_metapage(struct metapage *);
+extern void hold_metapage(struct metapage *, int);
+
+static inline void write_metapage(struct metapage *mp)
+{
+ set_bit(META_dirty, &mp->flag);
+ release_metapage(mp);
+}
+
+static inline void flush_metapage(struct metapage *mp)
+{
+ set_bit(META_sync, &mp->flag);
+ write_metapage(mp);
+}
+
+static inline void discard_metapage(struct metapage *mp)
+{
+ clear_bit(META_dirty, &mp->flag);
+ set_bit(META_discard, &mp->flag);
+ release_metapage(mp);
+}
+
+/*
+ * This routines invalidate all pages for an extent.
+ */
+extern void __invalidate_metapages(struct inode *, s64, int);
+#define invalidate_pxd_metapages(ip, pxd) \
+ __invalidate_metapages((ip), addressPXD(&(pxd)), lengthPXD(&(pxd)))
+#define invalidate_dxd_metapages(ip, dxd) \
+ __invalidate_metapages((ip), addressDXD(&(dxd)), lengthDXD(&(dxd)))
+#define invalidate_xad_metapages(ip, xad) \
+ __invalidate_metapages((ip), addressXAD(&(xad)), lengthXAD(&(xad)))
+
+#endif /* _H_JFS_METAPAGE */
diff --git a/fs/jfs/jfs_mount.c b/fs/jfs/jfs_mount.c
new file mode 100644
index 00000000000000..54e154a5783d67
--- /dev/null
+++ b/fs/jfs/jfs_mount.c
@@ -0,0 +1,512 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2000-2003
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Module: jfs_mount.c
+ *
+ * note: file system in transition to aggregate/fileset:
+ *
+ * file system mount is interpreted as the mount of aggregate,
+ * if not already mounted, and mount of the single/only fileset in
+ * the aggregate;
+ *
+ * a file system/aggregate is represented by an internal inode
+ * (aka mount inode) initialized with aggregate superblock;
+ * each vfs represents a fileset, and points to its "fileset inode
+ * allocation map inode" (aka fileset inode):
+ * (an aggregate itself is structured recursively as a filset:
+ * an internal vfs is constructed and points to its "fileset inode
+ * allocation map inode" (aka aggregate inode) where each inode
+ * represents a fileset inode) so that inode number is mapped to
+ * on-disk inode in uniform way at both aggregate and fileset level;
+ *
+ * each vnode/inode of a fileset is linked to its vfs (to facilitate
+ * per fileset inode operations, e.g., unmount of a fileset, etc.);
+ * each inode points to the mount inode (to facilitate access to
+ * per aggregate information, e.g., block size, etc.) as well as
+ * its file set inode.
+ *
+ * aggregate
+ * ipmnt
+ * mntvfs -> fileset ipimap+ -> aggregate ipbmap -> aggregate ipaimap;
+ * fileset vfs -> vp(1) <-> ... <-> vp(n) <->vproot;
+ */
+
+#include <linux/fs.h>
+#include <linux/locks.h>
+
+#include "jfs_incore.h"
+#include "jfs_filsys.h"
+#include "jfs_superblock.h"
+#include "jfs_dmap.h"
+#include "jfs_imap.h"
+#include "jfs_metapage.h"
+#include "jfs_debug.h"
+
+
+/*
+ * forward references
+ */
+static int chkSuper(struct super_block *);
+static int logMOUNT(struct super_block *sb);
+
+/*
+ * NAME: jfs_mount(sb)
+ *
+ * FUNCTION: vfs_mount()
+ *
+ * PARAMETER: sb - super block
+ *
+ * RETURN: -EBUSY - device already mounted or open for write
+ * -EBUSY - cvrdvp already mounted;
+ * -EBUSY - mount table full
+ * -ENOTDIR- cvrdvp not directory on a device mount
+ * -ENXIO - device open failure
+ */
+int jfs_mount(struct super_block *sb)
+{
+ int rc = 0; /* Return code */
+ struct jfs_sb_info *sbi = JFS_SBI(sb);
+ struct inode *ipaimap = NULL;
+ struct inode *ipaimap2 = NULL;
+ struct inode *ipimap = NULL;
+ struct inode *ipbmap = NULL;
+
+ /*
+ * read/validate superblock
+ * (initialize mount inode from the superblock)
+ */
+ if ((rc = chkSuper(sb))) {
+ goto errout20;
+ }
+
+ ipaimap = diReadSpecial(sb, AGGREGATE_I, 0);
+ if (ipaimap == NULL) {
+ jfs_err("jfs_mount: Faild to read AGGREGATE_I");
+ rc = -EIO;
+ goto errout20;
+ }
+ sbi->ipaimap = ipaimap;
+
+ jfs_info("jfs_mount: ipaimap:0x%p", ipaimap);
+
+ /*
+ * initialize aggregate inode allocation map
+ */
+ if ((rc = diMount(ipaimap))) {
+ jfs_err("jfs_mount: diMount(ipaimap) failed w/rc = %d", rc);
+ goto errout21;
+ }
+
+ /*
+ * open aggregate block allocation map
+ */
+ ipbmap = diReadSpecial(sb, BMAP_I, 0);
+ if (ipbmap == NULL) {
+ rc = -EIO;
+ goto errout22;
+ }
+
+ jfs_info("jfs_mount: ipbmap:0x%p", ipbmap);
+
+ sbi->ipbmap = ipbmap;
+
+ /*
+ * initialize aggregate block allocation map
+ */
+ if ((rc = dbMount(ipbmap))) {
+ jfs_err("jfs_mount: dbMount failed w/rc = %d", rc);
+ goto errout22;
+ }
+
+ /*
+ * open the secondary aggregate inode allocation map
+ *
+ * This is a duplicate of the aggregate inode allocation map.
+ *
+ * hand craft a vfs in the same fashion as we did to read ipaimap.
+ * By adding INOSPEREXT (32) to the inode number, we are telling
+ * diReadSpecial that we are reading from the secondary aggregate
+ * inode table. This also creates a unique entry in the inode hash
+ * table.
+ */
+ if ((sbi->mntflag & JFS_BAD_SAIT) == 0) {
+ ipaimap2 = diReadSpecial(sb, AGGREGATE_I, 1);
+ if (ipaimap2 == 0) {
+ jfs_err("jfs_mount: Faild to read AGGREGATE_I");
+ rc = -EIO;
+ goto errout35;
+ }
+ sbi->ipaimap2 = ipaimap2;
+
+ jfs_info("jfs_mount: ipaimap2:0x%p", ipaimap2);
+
+ /*
+ * initialize secondary aggregate inode allocation map
+ */
+ if ((rc = diMount(ipaimap2))) {
+ jfs_err("jfs_mount: diMount(ipaimap2) failed, rc = %d",
+ rc);
+ goto errout35;
+ }
+ } else
+ /* Secondary aggregate inode table is not valid */
+ sbi->ipaimap2 = 0;
+
+ /*
+ * mount (the only/single) fileset
+ */
+ /*
+ * open fileset inode allocation map (aka fileset inode)
+ */
+ ipimap = diReadSpecial(sb, FILESYSTEM_I, 0);
+ if (ipimap == NULL) {
+ jfs_err("jfs_mount: Failed to read FILESYSTEM_I");
+ /* open fileset secondary inode allocation map */
+ rc = -EIO;
+ goto errout40;
+ }
+ jfs_info("jfs_mount: ipimap:0x%p", ipimap);
+
+ /* map further access of per fileset inodes by the fileset inode */
+ sbi->ipimap = ipimap;
+
+ /* initialize fileset inode allocation map */
+ if ((rc = diMount(ipimap))) {
+ jfs_err("jfs_mount: diMount failed w/rc = %d", rc);
+ goto errout41;
+ }
+
+ goto out;
+
+ /*
+ * unwind on error
+ */
+ errout41: /* close fileset inode allocation map inode */
+ diFreeSpecial(ipimap);
+
+ errout40: /* fileset closed */
+
+ /* close secondary aggregate inode allocation map */
+ if (ipaimap2) {
+ diUnmount(ipaimap2, 1);
+ diFreeSpecial(ipaimap2);
+ }
+
+ errout35:
+
+ /* close aggregate block allocation map */
+ dbUnmount(ipbmap, 1);
+ diFreeSpecial(ipbmap);
+
+ errout22: /* close aggregate inode allocation map */
+
+ diUnmount(ipaimap, 1);
+
+ errout21: /* close aggregate inodes */
+ diFreeSpecial(ipaimap);
+ errout20: /* aggregate closed */
+
+ out:
+
+ if (rc)
+ jfs_err("Mount JFS Failure: %d", rc);
+
+ return rc;
+}
+
+/*
+ * NAME: jfs_mount_rw(sb, remount)
+ *
+ * FUNCTION: Completes read-write mount, or remounts read-only volume
+ * as read-write
+ */
+int jfs_mount_rw(struct super_block *sb, int remount)
+{
+ struct jfs_sb_info *sbi = JFS_SBI(sb);
+ struct jfs_log *log;
+ int rc;
+
+ /*
+ * If we are re-mounting a previously read-only volume, we want to
+ * re-read the inode and block maps, since fsck.jfs may have updated
+ * them.
+ */
+ if (remount) {
+ if (chkSuper(sb) || (sbi->state != FM_CLEAN))
+ return -EINVAL;
+
+ truncate_inode_pages(sbi->ipimap->i_mapping, 0);
+ truncate_inode_pages(sbi->ipbmap->i_mapping, 0);
+ diUnmount(sbi->ipimap, 1);
+ if ((rc = diMount(sbi->ipimap))) {
+ jfs_err("jfs_mount_rw: diMount failed!");
+ return rc;
+ }
+
+ dbUnmount(sbi->ipbmap, 1);
+ if ((rc = dbMount(sbi->ipbmap))) {
+ jfs_err("jfs_mount_rw: dbMount failed!");
+ return rc;
+ }
+ }
+
+ /*
+ * open/initialize log
+ */
+ if ((rc = lmLogOpen(sb, &log)))
+ return rc;
+
+ JFS_SBI(sb)->log = log;
+
+ /*
+ * update file system superblock;
+ */
+ if ((rc = updateSuper(sb, FM_MOUNT))) {
+ jfs_err("jfs_mount: updateSuper failed w/rc = %d", rc);
+ lmLogClose(sb, log);
+ JFS_SBI(sb)->log = 0;
+ return rc;
+ }
+
+ /*
+ * write MOUNT log record of the file system
+ */
+ logMOUNT(sb);
+
+ return rc;
+}
+
+/*
+ * chkSuper()
+ *
+ * validate the superblock of the file system to be mounted and
+ * get the file system parameters.
+ *
+ * returns
+ * 0 with fragsize set if check successful
+ * error code if not successful
+ */
+static int chkSuper(struct super_block *sb)
+{
+ int rc = 0;
+ struct jfs_sb_info *sbi = JFS_SBI(sb);
+ struct jfs_superblock *j_sb;
+ struct buffer_head *bh;
+ int AIM_bytesize, AIT_bytesize;
+ int expected_AIM_bytesize, expected_AIT_bytesize;
+ s64 AIM_byte_addr, AIT_byte_addr, fsckwsp_addr;
+ s64 byte_addr_diff0, byte_addr_diff1;
+ s32 bsize;
+
+ if ((rc = readSuper(sb, &bh)))
+ return rc;
+ j_sb = (struct jfs_superblock *)bh->b_data;
+
+ /*
+ * validate superblock
+ */
+ /* validate fs signature */
+ if (strncmp(j_sb->s_magic, JFS_MAGIC, 4) ||
+ le32_to_cpu(j_sb->s_version) > JFS_VERSION) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ bsize = le32_to_cpu(j_sb->s_bsize);
+#ifdef _JFS_4K
+ if (bsize != PSIZE) {
+ jfs_err("Currently only 4K block size supported!");
+ rc = -EINVAL;
+ goto out;
+ }
+#endif /* _JFS_4K */
+
+ jfs_info("superblock: flag:0x%08x state:0x%08x size:0x%Lx",
+ le32_to_cpu(j_sb->s_flag), le32_to_cpu(j_sb->s_state),
+ (unsigned long long) le64_to_cpu(j_sb->s_size));
+
+ /* validate the descriptors for Secondary AIM and AIT */
+ if ((j_sb->s_flag & cpu_to_le32(JFS_BAD_SAIT)) !=
+ cpu_to_le32(JFS_BAD_SAIT)) {
+ expected_AIM_bytesize = 2 * PSIZE;
+ AIM_bytesize = lengthPXD(&(j_sb->s_aim2)) * bsize;
+ expected_AIT_bytesize = 4 * PSIZE;
+ AIT_bytesize = lengthPXD(&(j_sb->s_ait2)) * bsize;
+ AIM_byte_addr = addressPXD(&(j_sb->s_aim2)) * bsize;
+ AIT_byte_addr = addressPXD(&(j_sb->s_ait2)) * bsize;
+ byte_addr_diff0 = AIT_byte_addr - AIM_byte_addr;
+ fsckwsp_addr = addressPXD(&(j_sb->s_fsckpxd)) * bsize;
+ byte_addr_diff1 = fsckwsp_addr - AIT_byte_addr;
+ if ((AIM_bytesize != expected_AIM_bytesize) ||
+ (AIT_bytesize != expected_AIT_bytesize) ||
+ (byte_addr_diff0 != AIM_bytesize) ||
+ (byte_addr_diff1 <= AIT_bytesize))
+ j_sb->s_flag |= cpu_to_le32(JFS_BAD_SAIT);
+ }
+
+ if ((j_sb->s_flag & cpu_to_le32(JFS_GROUPCOMMIT)) !=
+ cpu_to_le32(JFS_GROUPCOMMIT))
+ j_sb->s_flag |= cpu_to_le32(JFS_GROUPCOMMIT);
+
+ /* validate fs state */
+ if (j_sb->s_state != cpu_to_le32(FM_CLEAN) &&
+ !(sb->s_flags & MS_RDONLY)) {
+ jfs_err("jfs_mount: Mount Failure: File System Dirty.");
+ rc = -EINVAL;
+ goto out;
+ }
+
+ sbi->state = le32_to_cpu(j_sb->s_state);
+ sbi->mntflag = le32_to_cpu(j_sb->s_flag);
+
+ /*
+ * JFS always does I/O by 4K pages. Don't tell the buffer cache
+ * that we use anything else (leave s_blocksize alone).
+ */
+ sbi->bsize = bsize;
+ sbi->l2bsize = le16_to_cpu(j_sb->s_l2bsize);
+
+ /*
+ * For now, ignore s_pbsize, l2bfactor. All I/O going through buffer
+ * cache.
+ */
+ sbi->nbperpage = PSIZE >> sbi->l2bsize;
+ sbi->l2nbperpage = L2PSIZE - sbi->l2bsize;
+ sbi->l2niperblk = sbi->l2bsize - L2DISIZE;
+ if (sbi->mntflag & JFS_INLINELOG)
+ sbi->logpxd = j_sb->s_logpxd;
+ else {
+ sbi->logdev = to_kdev_t(le32_to_cpu(j_sb->s_logdev));
+ memcpy(sbi->uuid, j_sb->s_uuid, sizeof(sbi->uuid));
+ memcpy(sbi->loguuid, j_sb->s_loguuid, sizeof(sbi->uuid));
+ }
+ sbi->fsckpxd = j_sb->s_fsckpxd;
+ sbi->ait2 = j_sb->s_ait2;
+
+ out:
+ brelse(bh);
+ return rc;
+}
+
+
+/*
+ * updateSuper()
+ *
+ * update synchronously superblock if it is mounted read-write.
+ */
+int updateSuper(struct super_block *sb, uint state)
+{
+ struct jfs_superblock *j_sb;
+ struct jfs_sb_info *sbi = JFS_SBI(sb);
+ struct buffer_head *bh;
+ int rc;
+
+ if (sbi->flag & JFS_NOINTEGRITY) {
+ if (state == FM_DIRTY) {
+ sbi->p_state = state;
+ return 0;
+ } else if (state == FM_MOUNT) {
+ sbi->p_state = sbi->state;
+ state = FM_DIRTY;
+ } else if (state == FM_CLEAN) {
+ state = sbi->p_state;
+ } else
+ jfs_err("updateSuper: bad state");
+ } else if (sbi->state == FM_DIRTY)
+ return 0;
+
+ if ((rc = readSuper(sb, &bh)))
+ return rc;
+
+ j_sb = (struct jfs_superblock *)bh->b_data;
+
+ j_sb->s_state = cpu_to_le32(state);
+ sbi->state = state;
+
+ if (state == FM_MOUNT) {
+ /* record log's dev_t and mount serial number */
+ j_sb->s_logdev = cpu_to_le32(sbi->log->bdev->bd_dev);
+ j_sb->s_logserial = cpu_to_le32(sbi->log->serial);
+ } else if (state == FM_CLEAN) {
+ /*
+ * If this volume is shared with OS/2, OS/2 will need to
+ * recalculate DASD usage, since we don't deal with it.
+ */
+ if (j_sb->s_flag & cpu_to_le32(JFS_DASD_ENABLED))
+ j_sb->s_flag |= cpu_to_le32(JFS_DASD_PRIME);
+ }
+
+ mark_buffer_dirty(bh);
+ ll_rw_block(WRITE, 1, &bh);
+ wait_on_buffer(bh);
+ brelse(bh);
+
+ return 0;
+}
+
+
+/*
+ * readSuper()
+ *
+ * read superblock by raw sector address
+ */
+int readSuper(struct super_block *sb, struct buffer_head **bpp)
+{
+ /* read in primary superblock */
+ *bpp = sb_bread(sb, SUPER1_OFF >> sb->s_blocksize_bits);
+ if (*bpp)
+ return 0;
+
+ /* read in secondary/replicated superblock */
+ *bpp = sb_bread(sb, SUPER2_OFF >> sb->s_blocksize_bits);
+ if (*bpp)
+ return 0;
+
+ return -EIO;
+}
+
+
+/*
+ * logMOUNT()
+ *
+ * function: write a MOUNT log record for file system.
+ *
+ * MOUNT record keeps logredo() from processing log records
+ * for this file system past this point in log.
+ * it is harmless if mount fails.
+ *
+ * note: MOUNT record is at aggregate level, not at fileset level,
+ * since log records of previous mounts of a fileset
+ * (e.g., AFTER record of extent allocation) have to be processed
+ * to update block allocation map at aggregate level.
+ */
+static int logMOUNT(struct super_block *sb)
+{
+ struct jfs_log *log = JFS_SBI(sb)->log;
+ struct lrd lrd;
+
+ lrd.logtid = 0;
+ lrd.backchain = 0;
+ lrd.type = cpu_to_le16(LOG_MOUNT);
+ lrd.length = 0;
+ lrd.aggregate = cpu_to_le32(kdev_t_to_nr(sb->s_dev));
+ lmLog(log, NULL, &lrd, NULL);
+
+ return 0;
+}
diff --git a/fs/jfs/jfs_superblock.h b/fs/jfs/jfs_superblock.h
new file mode 100644
index 00000000000000..5a558ce3e81d1b
--- /dev/null
+++ b/fs/jfs/jfs_superblock.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2000-2003
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _H_JFS_SUPERBLOCK
+#define _H_JFS_SUPERBLOCK
+
+/*
+ * make the magic number something a human could read
+ */
+#define JFS_MAGIC "JFS1" /* Magic word */
+
+#define JFS_VERSION 2 /* Version number: Version 2 */
+
+#define LV_NAME_SIZE 11 /* MUST BE 11 for OS/2 boot sector */
+
+/*
+ * aggregate superblock
+ *
+ * The name superblock is too close to super_block, so the name has been
+ * changed to jfs_superblock. The utilities are still using the old name.
+ */
+struct jfs_superblock {
+ char s_magic[4]; /* 4: magic number */
+ u32 s_version; /* 4: version number */
+
+ s64 s_size; /* 8: aggregate size in hardware/LVM blocks;
+ * VFS: number of blocks
+ */
+ s32 s_bsize; /* 4: aggregate block size in bytes;
+ * VFS: fragment size
+ */
+ s16 s_l2bsize; /* 2: log2 of s_bsize */
+ s16 s_l2bfactor; /* 2: log2(s_bsize/hardware block size) */
+ s32 s_pbsize; /* 4: hardware/LVM block size in bytes */
+ s16 s_l2pbsize; /* 2: log2 of s_pbsize */
+ s16 pad; /* 2: padding necessary for alignment */
+
+ u32 s_agsize; /* 4: allocation group size in aggr. blocks */
+
+ u32 s_flag; /* 4: aggregate attributes:
+ * see jfs_filsys.h
+ */
+ u32 s_state; /* 4: mount/unmount/recovery state:
+ * see jfs_filsys.h
+ */
+ s32 s_compress; /* 4: > 0 if data compression */
+
+ pxd_t s_ait2; /* 8: first extent of secondary
+ * aggregate inode table
+ */
+
+ pxd_t s_aim2; /* 8: first extent of secondary
+ * aggregate inode map
+ */
+ u32 s_logdev; /* 4: device address of log */
+ s32 s_logserial; /* 4: log serial number at aggregate mount */
+ pxd_t s_logpxd; /* 8: inline log extent */
+
+ pxd_t s_fsckpxd; /* 8: inline fsck work space extent */
+
+ struct timestruc_t s_time; /* 8: time last updated */
+
+ s32 s_fsckloglen; /* 4: Number of filesystem blocks reserved for
+ * the fsck service log.
+ * N.B. These blocks are divided among the
+ * versions kept. This is not a per
+ * version size.
+ * N.B. These blocks are included in the
+ * length field of s_fsckpxd.
+ */
+ s8 s_fscklog; /* 1: which fsck service log is most recent
+ * 0 => no service log data yet
+ * 1 => the first one
+ * 2 => the 2nd one
+ */
+ char s_fpack[11]; /* 11: file system volume name
+ * N.B. This must be 11 bytes to
+ * conform with the OS/2 BootSector
+ * requirements
+ * Only used when s_version is 1
+ */
+
+ /* extendfs() parameter under s_state & FM_EXTENDFS */
+ s64 s_xsize; /* 8: extendfs s_size */
+ pxd_t s_xfsckpxd; /* 8: extendfs fsckpxd */
+ pxd_t s_xlogpxd; /* 8: extendfs logpxd */
+ /* - 128 byte boundary - */
+
+ char s_uuid[16]; /* 16: 128-bit uuid for volume */
+ char s_label[16]; /* 16: volume label */
+ char s_loguuid[16]; /* 16: 128-bit uuid for log device */
+
+};
+
+extern int readSuper(struct super_block *, struct buffer_head **);
+extern int updateSuper(struct super_block *, uint);
+extern void jfs_error(struct super_block *, const char *, ...);
+
+#endif /*_H_JFS_SUPERBLOCK */
diff --git a/fs/jfs/jfs_txnmgr.c b/fs/jfs/jfs_txnmgr.c
new file mode 100644
index 00000000000000..62e649341823e4
--- /dev/null
+++ b/fs/jfs/jfs_txnmgr.c
@@ -0,0 +1,3080 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2000-2004
+ * Portions Copyright (C) Christoph Hellwig, 2001-2002
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * jfs_txnmgr.c: transaction manager
+ *
+ * notes:
+ * transaction starts with txBegin() and ends with txCommit()
+ * or txAbort().
+ *
+ * tlock is acquired at the time of update;
+ * (obviate scan at commit time for xtree and dtree)
+ * tlock and mp points to each other;
+ * (no hashlist for mp -> tlock).
+ *
+ * special cases:
+ * tlock on in-memory inode:
+ * in-place tlock in the in-memory inode itself;
+ * converted to page lock by iWrite() at commit time.
+ *
+ * tlock during write()/mmap() under anonymous transaction (tid = 0):
+ * transferred (?) to transaction at commit time.
+ *
+ * use the page itself to update allocation maps
+ * (obviate intermediate replication of allocation/deallocation data)
+ * hold on to mp+lock thru update of maps
+ */
+
+
+#include <linux/fs.h>
+#include <linux/vmalloc.h>
+#include <linux/smp_lock.h>
+#include <linux/completion.h>
+#include "jfs_incore.h"
+#include "jfs_filsys.h"
+#include "jfs_metapage.h"
+#include "jfs_dinode.h"
+#include "jfs_imap.h"
+#include "jfs_dmap.h"
+#include "jfs_superblock.h"
+#include "jfs_debug.h"
+
+/*
+ * transaction management structures
+ */
+static struct {
+ /* tblock */
+ int freetid; /* index of a free tid structure */
+ wait_queue_head_t freewait; /* eventlist of free tblock */
+
+ /* tlock */
+ int freelock; /* index first free lock word */
+ wait_queue_head_t freelockwait; /* eventlist of free tlock */
+ wait_queue_head_t lowlockwait; /* eventlist of ample tlocks */
+ int tlocksInUse; /* Number of tlocks in use */
+ int TlocksLow; /* Indicates low number of available tlocks */
+ spinlock_t LazyLock; /* synchronize sync_queue & unlock_queue */
+/* struct tblock *sync_queue; * Transactions waiting for data sync */
+ struct tblock *unlock_queue; /* Txns waiting to be released */
+ struct tblock *unlock_tail; /* Tail of unlock_queue */
+ struct list_head anon_list; /* inodes having anonymous txns */
+ struct list_head anon_list2; /* inodes having anonymous txns
+ that couldn't be sync'ed */
+} TxAnchor;
+
+#ifdef CONFIG_JFS_STATISTICS
+struct {
+ uint txBegin;
+ uint txBegin_barrier;
+ uint txBegin_lockslow;
+ uint txBegin_freetid;
+ uint txBeginAnon;
+ uint txBeginAnon_barrier;
+ uint txBeginAnon_lockslow;
+ uint txLockAlloc;
+ uint txLockAlloc_freelock;
+} TxStat;
+#endif
+
+static int nTxBlock = 512; /* number of transaction blocks */
+struct tblock *TxBlock; /* transaction block table */
+
+static int nTxLock = 4096; /* number of transaction locks */
+static int TxLockLWM = 4096*.4; /* Low water mark for number of txLocks used */
+static int TxLockHWM = 4096*.8; /* High water mark for number of txLocks used */
+struct tlock *TxLock; /* transaction lock table */
+
+
+/*
+ * transaction management lock
+ */
+static spinlock_t jfsTxnLock = SPIN_LOCK_UNLOCKED;
+
+#define TXN_LOCK() spin_lock(&jfsTxnLock)
+#define TXN_UNLOCK() spin_unlock(&jfsTxnLock)
+
+#define LAZY_LOCK_INIT() spin_lock_init(&TxAnchor.LazyLock);
+#define LAZY_LOCK(flags) spin_lock_irqsave(&TxAnchor.LazyLock, flags)
+#define LAZY_UNLOCK(flags) spin_unlock_irqrestore(&TxAnchor.LazyLock, flags)
+
+DECLARE_WAIT_QUEUE_HEAD(jfs_sync_thread_wait);
+DECLARE_WAIT_QUEUE_HEAD(jfs_commit_thread_wait);
+
+/*
+ * Retry logic exist outside these macros to protect from spurrious wakeups.
+ */
+static inline void TXN_SLEEP_DROP_LOCK(wait_queue_head_t * event)
+{
+ DECLARE_WAITQUEUE(wait, current);
+
+ add_wait_queue(event, &wait);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ TXN_UNLOCK();
+ schedule();
+ current->state = TASK_RUNNING;
+ remove_wait_queue(event, &wait);
+}
+
+#define TXN_SLEEP(event)\
+{\
+ TXN_SLEEP_DROP_LOCK(event);\
+ TXN_LOCK();\
+}
+
+#define TXN_WAKEUP(event) wake_up_all(event)
+
+
+/*
+ * statistics
+ */
+struct {
+ tid_t maxtid; /* 4: biggest tid ever used */
+ lid_t maxlid; /* 4: biggest lid ever used */
+ int ntid; /* 4: # of transactions performed */
+ int nlid; /* 4: # of tlocks acquired */
+ int waitlock; /* 4: # of tlock wait */
+} stattx;
+
+
+/*
+ * external references
+ */
+extern int lmGroupCommit(struct jfs_log *, struct tblock *);
+extern void lmSync(struct jfs_log *);
+extern int jfs_commit_inode(struct inode *, int);
+extern int jfs_stop_threads;
+
+struct task_struct *jfsCommitTask;
+extern struct completion jfsIOwait;
+
+/*
+ * forward references
+ */
+static int diLog(struct jfs_log * log, struct tblock * tblk, struct lrd * lrd,
+ struct tlock * tlck, struct commit * cd);
+static int dataLog(struct jfs_log * log, struct tblock * tblk, struct lrd * lrd,
+ struct tlock * tlck);
+static void dtLog(struct jfs_log * log, struct tblock * tblk, struct lrd * lrd,
+ struct tlock * tlck);
+static void mapLog(struct jfs_log * log, struct tblock * tblk, struct lrd * lrd,
+ struct tlock * tlck);
+static void txAllocPMap(struct inode *ip, struct maplock * maplock,
+ struct tblock * tblk);
+static void txForce(struct tblock * tblk);
+static int txLog(struct jfs_log * log, struct tblock * tblk,
+ struct commit * cd);
+static void txUpdateMap(struct tblock * tblk);
+static void txRelease(struct tblock * tblk);
+static void xtLog(struct jfs_log * log, struct tblock * tblk, struct lrd * lrd,
+ struct tlock * tlck);
+static void LogSyncRelease(struct metapage * mp);
+
+/*
+ * transaction block/lock management
+ * ---------------------------------
+ */
+
+/*
+ * Get a transaction lock from the free list. If the number in use is
+ * greater than the high water mark, wake up the sync daemon. This should
+ * free some anonymous transaction locks. (TXN_LOCK must be held.)
+ */
+static lid_t txLockAlloc(void)
+{
+ lid_t lid;
+
+ INCREMENT(TxStat.txLockAlloc);
+ if (!TxAnchor.freelock) {
+ INCREMENT(TxStat.txLockAlloc_freelock);
+ }
+
+ while (!(lid = TxAnchor.freelock))
+ TXN_SLEEP(&TxAnchor.freelockwait);
+ TxAnchor.freelock = TxLock[lid].next;
+ HIGHWATERMARK(stattx.maxlid, lid);
+ if ((++TxAnchor.tlocksInUse > TxLockHWM) && (TxAnchor.TlocksLow == 0)) {
+ jfs_info("txLockAlloc TlocksLow");
+ TxAnchor.TlocksLow = 1;
+ wake_up(&jfs_sync_thread_wait);
+ }
+
+ return lid;
+}
+
+static void txLockFree(lid_t lid)
+{
+ TxLock[lid].next = TxAnchor.freelock;
+ TxAnchor.freelock = lid;
+ TxAnchor.tlocksInUse--;
+ if (TxAnchor.TlocksLow && (TxAnchor.tlocksInUse < TxLockLWM)) {
+ jfs_info("txLockFree TlocksLow no more");
+ TxAnchor.TlocksLow = 0;
+ TXN_WAKEUP(&TxAnchor.lowlockwait);
+ }
+ TXN_WAKEUP(&TxAnchor.freelockwait);
+}
+
+/*
+ * NAME: txInit()
+ *
+ * FUNCTION: initialize transaction management structures
+ *
+ * RETURN:
+ *
+ * serialization: single thread at jfs_init()
+ */
+int txInit(void)
+{
+ int k, size;
+
+ /*
+ * initialize transaction block (tblock) table
+ *
+ * transaction id (tid) = tblock index
+ * tid = 0 is reserved.
+ */
+ size = sizeof(struct tblock) * nTxBlock;
+ TxBlock = (struct tblock *) vmalloc(size);
+ if (TxBlock == NULL)
+ return -ENOMEM;
+
+ for (k = 1; k < nTxBlock - 1; k++) {
+ TxBlock[k].next = k + 1;
+ init_waitqueue_head(&TxBlock[k].gcwait);
+ init_waitqueue_head(&TxBlock[k].waitor);
+ }
+ TxBlock[k].next = 0;
+ init_waitqueue_head(&TxBlock[k].gcwait);
+ init_waitqueue_head(&TxBlock[k].waitor);
+
+ TxAnchor.freetid = 1;
+ init_waitqueue_head(&TxAnchor.freewait);
+
+ stattx.maxtid = 1; /* statistics */
+
+ /*
+ * initialize transaction lock (tlock) table
+ *
+ * transaction lock id = tlock index
+ * tlock id = 0 is reserved.
+ */
+ size = sizeof(struct tlock) * nTxLock;
+ TxLock = (struct tlock *) vmalloc(size);
+ if (TxLock == NULL) {
+ vfree(TxBlock);
+ return -ENOMEM;
+ }
+
+ /* initialize tlock table */
+ for (k = 1; k < nTxLock - 1; k++)
+ TxLock[k].next = k + 1;
+ TxLock[k].next = 0;
+ init_waitqueue_head(&TxAnchor.freelockwait);
+ init_waitqueue_head(&TxAnchor.lowlockwait);
+
+ TxAnchor.freelock = 1;
+ TxAnchor.tlocksInUse = 0;
+ INIT_LIST_HEAD(&TxAnchor.anon_list);
+ INIT_LIST_HEAD(&TxAnchor.anon_list2);
+
+ stattx.maxlid = 1; /* statistics */
+
+ return 0;
+}
+
+/*
+ * NAME: txExit()
+ *
+ * FUNCTION: clean up when module is unloaded
+ */
+void txExit(void)
+{
+ vfree(TxLock);
+ TxLock = 0;
+ vfree(TxBlock);
+ TxBlock = 0;
+}
+
+
+/*
+ * NAME: txBegin()
+ *
+ * FUNCTION: start a transaction.
+ *
+ * PARAMETER: sb - superblock
+ * flag - force for nested tx;
+ *
+ * RETURN: tid - transaction id
+ *
+ * note: flag force allows to start tx for nested tx
+ * to prevent deadlock on logsync barrier;
+ */
+tid_t txBegin(struct super_block *sb, int flag)
+{
+ tid_t t;
+ struct tblock *tblk;
+ struct jfs_log *log;
+
+ jfs_info("txBegin: flag = 0x%x", flag);
+ log = JFS_SBI(sb)->log;
+
+ TXN_LOCK();
+
+ INCREMENT(TxStat.txBegin);
+
+ retry:
+ if (!(flag & COMMIT_FORCE)) {
+ /*
+ * synchronize with logsync barrier
+ */
+ if (test_bit(log_SYNCBARRIER, &log->flag) ||
+ test_bit(log_QUIESCE, &log->flag)) {
+ INCREMENT(TxStat.txBegin_barrier);
+ TXN_SLEEP(&log->syncwait);
+ goto retry;
+ }
+ }
+ if (flag == 0) {
+ /*
+ * Don't begin transaction if we're getting starved for tlocks
+ * unless COMMIT_FORCE or COMMIT_INODE (which may ultimately
+ * free tlocks)
+ */
+ if (TxAnchor.TlocksLow) {
+ INCREMENT(TxStat.txBegin_lockslow);
+ TXN_SLEEP(&TxAnchor.lowlockwait);
+ goto retry;
+ }
+ }
+
+ /*
+ * allocate transaction id/block
+ */
+ if ((t = TxAnchor.freetid) == 0) {
+ jfs_info("txBegin: waiting for free tid");
+ INCREMENT(TxStat.txBegin_freetid);
+ TXN_SLEEP(&TxAnchor.freewait);
+ goto retry;
+ }
+
+ tblk = tid_to_tblock(t);
+
+ if ((tblk->next == 0) && !(flag & COMMIT_FORCE)) {
+ /* Don't let a non-forced transaction take the last tblk */
+ jfs_info("txBegin: waiting for free tid");
+ INCREMENT(TxStat.txBegin_freetid);
+ TXN_SLEEP(&TxAnchor.freewait);
+ goto retry;
+ }
+
+ TxAnchor.freetid = tblk->next;
+
+ /*
+ * initialize transaction
+ */
+
+ /*
+ * We can't zero the whole thing or we screw up another thread being
+ * awakened after sleeping on tblk->waitor
+ *
+ * memset(tblk, 0, sizeof(struct tblock));
+ */
+ tblk->next = tblk->last = tblk->xflag = tblk->flag = tblk->lsn = 0;
+
+ tblk->sb = sb;
+ ++log->logtid;
+ tblk->logtid = log->logtid;
+
+ ++log->active;
+
+ HIGHWATERMARK(stattx.maxtid, t); /* statistics */
+ INCREMENT(stattx.ntid); /* statistics */
+
+ TXN_UNLOCK();
+
+ jfs_info("txBegin: returning tid = %d", t);
+
+ return t;
+}
+
+
+/*
+ * NAME: txBeginAnon()
+ *
+ * FUNCTION: start an anonymous transaction.
+ * Blocks if logsync or available tlocks are low to prevent
+ * anonymous tlocks from depleting supply.
+ *
+ * PARAMETER: sb - superblock
+ *
+ * RETURN: none
+ */
+void txBeginAnon(struct super_block *sb)
+{
+ struct jfs_log *log;
+
+ log = JFS_SBI(sb)->log;
+
+ TXN_LOCK();
+ INCREMENT(TxStat.txBeginAnon);
+
+ retry:
+ /*
+ * synchronize with logsync barrier
+ */
+ if (test_bit(log_SYNCBARRIER, &log->flag) ||
+ test_bit(log_QUIESCE, &log->flag)) {
+ INCREMENT(TxStat.txBeginAnon_barrier);
+ TXN_SLEEP(&log->syncwait);
+ goto retry;
+ }
+
+ /*
+ * Don't begin transaction if we're getting starved for tlocks
+ */
+ if (TxAnchor.TlocksLow) {
+ INCREMENT(TxStat.txBeginAnon_lockslow);
+ TXN_SLEEP(&TxAnchor.lowlockwait);
+ goto retry;
+ }
+ TXN_UNLOCK();
+}
+
+
+/*
+ * txEnd()
+ *
+ * function: free specified transaction block.
+ *
+ * logsync barrier processing:
+ *
+ * serialization:
+ */
+void txEnd(tid_t tid)
+{
+ struct tblock *tblk = tid_to_tblock(tid);
+ struct jfs_log *log;
+
+ jfs_info("txEnd: tid = %d", tid);
+ TXN_LOCK();
+
+ /*
+ * wakeup transactions waiting on the page locked
+ * by the current transaction
+ */
+ TXN_WAKEUP(&tblk->waitor);
+
+ log = JFS_SBI(tblk->sb)->log;
+
+ /*
+ * Lazy commit thread can't free this guy until we mark it UNLOCKED,
+ * otherwise, we would be left with a transaction that may have been
+ * reused.
+ *
+ * Lazy commit thread will turn off tblkGC_LAZY before calling this
+ * routine.
+ */
+ if (tblk->flag & tblkGC_LAZY) {
+ jfs_info("txEnd called w/lazy tid: %d, tblk = 0x%p", tid, tblk);
+ TXN_UNLOCK();
+
+ spin_lock_irq(&log->gclock); // LOGGC_LOCK
+ tblk->flag |= tblkGC_UNLOCKED;
+ spin_unlock_irq(&log->gclock); // LOGGC_UNLOCK
+ return;
+ }
+
+ jfs_info("txEnd: tid: %d, tblk = 0x%p", tid, tblk);
+
+ assert(tblk->next == 0);
+
+ /*
+ * insert tblock back on freelist
+ */
+ tblk->next = TxAnchor.freetid;
+ TxAnchor.freetid = tid;
+
+ /*
+ * mark the tblock not active
+ */
+ if (--log->active == 0) {
+ clear_bit(log_FLUSH, &log->flag);
+
+ /*
+ * synchronize with logsync barrier
+ */
+ if (test_bit(log_SYNCBARRIER, &log->flag)) {
+ /* forward log syncpt */
+ /* lmSync(log); */
+
+ jfs_info("log barrier off: 0x%x", log->lsn);
+
+ /* enable new transactions start */
+ clear_bit(log_SYNCBARRIER, &log->flag);
+
+ /* wakeup all waitors for logsync barrier */
+ TXN_WAKEUP(&log->syncwait);
+ }
+ }
+
+ /*
+ * wakeup all waitors for a free tblock
+ */
+ TXN_WAKEUP(&TxAnchor.freewait);
+
+ TXN_UNLOCK();
+}
+
+
+/*
+ * txLock()
+ *
+ * function: acquire a transaction lock on the specified <mp>
+ *
+ * parameter:
+ *
+ * return: transaction lock id
+ *
+ * serialization:
+ */
+struct tlock *txLock(tid_t tid, struct inode *ip, struct metapage * mp,
+ int type)
+{
+ struct jfs_inode_info *jfs_ip = JFS_IP(ip);
+ int dir_xtree = 0;
+ lid_t lid;
+ tid_t xtid;
+ struct tlock *tlck;
+ struct xtlock *xtlck;
+ struct linelock *linelock;
+ xtpage_t *p;
+ struct tblock *tblk;
+
+ assert(!test_cflag(COMMIT_Nolink, ip));
+
+ TXN_LOCK();
+
+ if (S_ISDIR(ip->i_mode) && (type & tlckXTREE) &&
+ !(mp->xflag & COMMIT_PAGE)) {
+ /*
+ * Directory inode is special. It can have both an xtree tlock
+ * and a dtree tlock associated with it.
+ */
+ dir_xtree = 1;
+ lid = jfs_ip->xtlid;
+ } else
+ lid = mp->lid;
+
+ /* is page not locked by a transaction ? */
+ if (lid == 0)
+ goto allocateLock;
+
+ jfs_info("txLock: tid:%d ip:0x%p mp:0x%p lid:%d", tid, ip, mp, lid);
+
+ /* is page locked by the requester transaction ? */
+ tlck = lid_to_tlock(lid);
+ if ((xtid = tlck->tid) == tid)
+ goto grantLock;
+
+ /*
+ * is page locked by anonymous transaction/lock ?
+ *
+ * (page update without transaction (i.e., file write) is
+ * locked under anonymous transaction tid = 0:
+ * anonymous tlocks maintained on anonymous tlock list of
+ * the inode of the page and available to all anonymous
+ * transactions until txCommit() time at which point
+ * they are transferred to the transaction tlock list of
+ * the commiting transaction of the inode)
+ */
+ if (xtid == 0) {
+ tlck->tid = tid;
+ tblk = tid_to_tblock(tid);
+ /*
+ * The order of the tlocks in the transaction is important
+ * (during truncate, child xtree pages must be freed before
+ * parent's tlocks change the working map).
+ * Take tlock off anonymous list and add to tail of
+ * transaction list
+ *
+ * Note: We really need to get rid of the tid & lid and
+ * use list_head's. This code is getting UGLY!
+ */
+ if (jfs_ip->atlhead == lid) {
+ if (jfs_ip->atltail == lid) {
+ /* only anonymous txn.
+ * Remove from anon_list
+ */
+ list_del_init(&jfs_ip->anon_inode_list);
+ }
+ jfs_ip->atlhead = tlck->next;
+ } else {
+ lid_t last;
+ for (last = jfs_ip->atlhead;
+ lid_to_tlock(last)->next != lid;
+ last = lid_to_tlock(last)->next) {
+ assert(last);
+ }
+ lid_to_tlock(last)->next = tlck->next;
+ if (jfs_ip->atltail == lid)
+ jfs_ip->atltail = last;
+ }
+
+ /* insert the tlock at tail of transaction tlock list */
+
+ if (tblk->next)
+ lid_to_tlock(tblk->last)->next = lid;
+ else
+ tblk->next = lid;
+ tlck->next = 0;
+ tblk->last = lid;
+
+ goto grantLock;
+ }
+
+ goto waitLock;
+
+ /*
+ * allocate a tlock
+ */
+ allocateLock:
+ lid = txLockAlloc();
+ tlck = lid_to_tlock(lid);
+
+ /*
+ * initialize tlock
+ */
+ tlck->tid = tid;
+
+ /* mark tlock for meta-data page */
+ if (mp->xflag & COMMIT_PAGE) {
+
+ tlck->flag = tlckPAGELOCK;
+
+ /* mark the page dirty and nohomeok */
+ mark_metapage_dirty(mp);
+ atomic_inc(&mp->nohomeok);
+
+ jfs_info("locking mp = 0x%p, nohomeok = %d tid = %d tlck = 0x%p",
+ mp, atomic_read(&mp->nohomeok), tid, tlck);
+
+ /* if anonymous transaction, and buffer is on the group
+ * commit synclist, mark inode to show this. This will
+ * prevent the buffer from being marked nohomeok for too
+ * long a time.
+ */
+ if ((tid == 0) && mp->lsn)
+ set_cflag(COMMIT_Synclist, ip);
+ }
+ /* mark tlock for in-memory inode */
+ else
+ tlck->flag = tlckINODELOCK;
+
+ tlck->type = 0;
+
+ /* bind the tlock and the page */
+ tlck->ip = ip;
+ tlck->mp = mp;
+ if (dir_xtree)
+ jfs_ip->xtlid = lid;
+ else
+ mp->lid = lid;
+
+ /*
+ * enqueue transaction lock to transaction/inode
+ */
+ /* insert the tlock at tail of transaction tlock list */
+ if (tid) {
+ tblk = tid_to_tblock(tid);
+ if (tblk->next)
+ lid_to_tlock(tblk->last)->next = lid;
+ else
+ tblk->next = lid;
+ tlck->next = 0;
+ tblk->last = lid;
+ }
+ /* anonymous transaction:
+ * insert the tlock at head of inode anonymous tlock list
+ */
+ else {
+ tlck->next = jfs_ip->atlhead;
+ jfs_ip->atlhead = lid;
+ if (tlck->next == 0) {
+ /* This inode's first anonymous transaction */
+ jfs_ip->atltail = lid;
+ list_add_tail(&jfs_ip->anon_inode_list,
+ &TxAnchor.anon_list);
+ }
+ }
+
+ /* initialize type dependent area for linelock */
+ linelock = (struct linelock *) & tlck->lock;
+ linelock->next = 0;
+ linelock->flag = tlckLINELOCK;
+ linelock->maxcnt = TLOCKSHORT;
+ linelock->index = 0;
+
+ switch (type & tlckTYPE) {
+ case tlckDTREE:
+ linelock->l2linesize = L2DTSLOTSIZE;
+ break;
+
+ case tlckXTREE:
+ linelock->l2linesize = L2XTSLOTSIZE;
+
+ xtlck = (struct xtlock *) linelock;
+ xtlck->header.offset = 0;
+ xtlck->header.length = 2;
+
+ if (type & tlckNEW) {
+ xtlck->lwm.offset = XTENTRYSTART;
+ } else {
+ if (mp->xflag & COMMIT_PAGE)
+ p = (xtpage_t *) mp->data;
+ else
+ p = &jfs_ip->i_xtroot;
+ xtlck->lwm.offset =
+ le16_to_cpu(p->header.nextindex);
+ }
+ xtlck->lwm.length = 0; /* ! */
+ xtlck->twm.offset = 0;
+ xtlck->hwm.offset = 0;
+
+ xtlck->index = 2;
+ break;
+
+ case tlckINODE:
+ linelock->l2linesize = L2INODESLOTSIZE;
+ break;
+
+ case tlckDATA:
+ linelock->l2linesize = L2DATASLOTSIZE;
+ break;
+
+ default:
+ jfs_err("UFO tlock:0x%p", tlck);
+ }
+
+ /*
+ * update tlock vector
+ */
+ grantLock:
+ tlck->type |= type;
+
+ TXN_UNLOCK();
+
+ return tlck;
+
+ /*
+ * page is being locked by another transaction:
+ */
+ waitLock:
+ /* Only locks on ipimap or ipaimap should reach here */
+ /* assert(jfs_ip->fileset == AGGREGATE_I); */
+ if (jfs_ip->fileset != AGGREGATE_I) {
+ jfs_err("txLock: trying to lock locked page!");
+ dump_mem("ip", ip, sizeof(struct inode));
+ dump_mem("mp", mp, sizeof(struct metapage));
+ dump_mem("Locker's tblk", tid_to_tblock(tid),
+ sizeof(struct tblock));
+ dump_mem("Tlock", tlck, sizeof(struct tlock));
+ BUG();
+ }
+ INCREMENT(stattx.waitlock); /* statistics */
+ release_metapage(mp);
+
+ jfs_info("txLock: in waitLock, tid = %d, xtid = %d, lid = %d",
+ tid, xtid, lid);
+ TXN_SLEEP_DROP_LOCK(&tid_to_tblock(xtid)->waitor);
+ jfs_info("txLock: awakened tid = %d, lid = %d", tid, lid);
+
+ return NULL;
+}
+
+
+/*
+ * NAME: txRelease()
+ *
+ * FUNCTION: Release buffers associated with transaction locks, but don't
+ * mark homeok yet. The allows other transactions to modify
+ * buffers, but won't let them go to disk until commit record
+ * actually gets written.
+ *
+ * PARAMETER:
+ * tblk -
+ *
+ * RETURN: Errors from subroutines.
+ */
+static void txRelease(struct tblock * tblk)
+{
+ struct metapage *mp;
+ lid_t lid;
+ struct tlock *tlck;
+
+ TXN_LOCK();
+
+ for (lid = tblk->next; lid; lid = tlck->next) {
+ tlck = lid_to_tlock(lid);
+ if ((mp = tlck->mp) != NULL &&
+ (tlck->type & tlckBTROOT) == 0) {
+ assert(mp->xflag & COMMIT_PAGE);
+ mp->lid = 0;
+ }
+ }
+
+ /*
+ * wakeup transactions waiting on a page locked
+ * by the current transaction
+ */
+ TXN_WAKEUP(&tblk->waitor);
+
+ TXN_UNLOCK();
+}
+
+
+/*
+ * NAME: txUnlock()
+ *
+ * FUNCTION: Initiates pageout of pages modified by tid in journalled
+ * objects and frees their lockwords.
+ */
+static void txUnlock(struct tblock * tblk)
+{
+ struct tlock *tlck;
+ struct linelock *linelock;
+ lid_t lid, next, llid, k;
+ struct metapage *mp;
+ struct jfs_log *log;
+ int difft, diffp;
+
+ jfs_info("txUnlock: tblk = 0x%p", tblk);
+ log = JFS_SBI(tblk->sb)->log;
+
+ /*
+ * mark page under tlock homeok (its log has been written):
+ */
+ for (lid = tblk->next; lid; lid = next) {
+ tlck = lid_to_tlock(lid);
+ next = tlck->next;
+
+ jfs_info("unlocking lid = %d, tlck = 0x%p", lid, tlck);
+
+ /* unbind page from tlock */
+ if ((mp = tlck->mp) != NULL &&
+ (tlck->type & tlckBTROOT) == 0) {
+ assert(mp->xflag & COMMIT_PAGE);
+
+ /* hold buffer
+ *
+ * It's possible that someone else has the metapage.
+ * The only things were changing are nohomeok, which
+ * is handled atomically, and clsn which is protected
+ * by the LOGSYNC_LOCK.
+ */
+ hold_metapage(mp, 1);
+
+ assert(atomic_read(&mp->nohomeok) > 0);
+ atomic_dec(&mp->nohomeok);
+
+ /* inherit younger/larger clsn */
+ LOGSYNC_LOCK(log);
+ if (mp->clsn) {
+ logdiff(difft, tblk->clsn, log);
+ logdiff(diffp, mp->clsn, log);
+ if (difft > diffp)
+ mp->clsn = tblk->clsn;
+ } else
+ mp->clsn = tblk->clsn;
+ LOGSYNC_UNLOCK(log);
+
+ assert(!(tlck->flag & tlckFREEPAGE));
+
+ if (tlck->flag & tlckWRITEPAGE) {
+ write_metapage(mp);
+ } else {
+ /* release page which has been forced */
+ release_metapage(mp);
+ }
+ }
+
+ /* insert tlock, and linelock(s) of the tlock if any,
+ * at head of freelist
+ */
+ TXN_LOCK();
+
+ llid = ((struct linelock *) & tlck->lock)->next;
+ while (llid) {
+ linelock = (struct linelock *) lid_to_tlock(llid);
+ k = linelock->next;
+ txLockFree(llid);
+ llid = k;
+ }
+ txLockFree(lid);
+
+ TXN_UNLOCK();
+ }
+ tblk->next = tblk->last = 0;
+
+ /*
+ * remove tblock from logsynclist
+ * (allocation map pages inherited lsn of tblk and
+ * has been inserted in logsync list at txUpdateMap())
+ */
+ if (tblk->lsn) {
+ LOGSYNC_LOCK(log);
+ log->count--;
+ list_del(&tblk->synclist);
+ LOGSYNC_UNLOCK(log);
+ }
+}
+
+
+/*
+ * txMaplock()
+ *
+ * function: allocate a transaction lock for freed page/entry;
+ * for freed page, maplock is used as xtlock/dtlock type;
+ */
+struct tlock *txMaplock(tid_t tid, struct inode *ip, int type)
+{
+ struct jfs_inode_info *jfs_ip = JFS_IP(ip);
+ lid_t lid;
+ struct tblock *tblk;
+ struct tlock *tlck;
+ struct maplock *maplock;
+
+ TXN_LOCK();
+
+ /*
+ * allocate a tlock
+ */
+ lid = txLockAlloc();
+ tlck = lid_to_tlock(lid);
+
+ /*
+ * initialize tlock
+ */
+ tlck->tid = tid;
+
+ /* bind the tlock and the object */
+ tlck->flag = tlckINODELOCK;
+ tlck->ip = ip;
+ tlck->mp = NULL;
+
+ tlck->type = type;
+
+ /*
+ * enqueue transaction lock to transaction/inode
+ */
+ /* insert the tlock at tail of transaction tlock list */
+ if (tid) {
+ tblk = tid_to_tblock(tid);
+ if (tblk->next)
+ lid_to_tlock(tblk->last)->next = lid;
+ else
+ tblk->next = lid;
+ tlck->next = 0;
+ tblk->last = lid;
+ }
+ /* anonymous transaction:
+ * insert the tlock at head of inode anonymous tlock list
+ */
+ else {
+ tlck->next = jfs_ip->atlhead;
+ jfs_ip->atlhead = lid;
+ if (tlck->next == 0) {
+ /* This inode's first anonymous transaction */
+ jfs_ip->atltail = lid;
+ list_add_tail(&jfs_ip->anon_inode_list,
+ &TxAnchor.anon_list);
+ }
+ }
+
+ TXN_UNLOCK();
+
+ /* initialize type dependent area for maplock */
+ maplock = (struct maplock *) & tlck->lock;
+ maplock->next = 0;
+ maplock->maxcnt = 0;
+ maplock->index = 0;
+
+ return tlck;
+}
+
+
+/*
+ * txLinelock()
+ *
+ * function: allocate a transaction lock for log vector list
+ */
+struct linelock *txLinelock(struct linelock * tlock)
+{
+ lid_t lid;
+ struct tlock *tlck;
+ struct linelock *linelock;
+
+ TXN_LOCK();
+
+ /* allocate a TxLock structure */
+ lid = txLockAlloc();
+ tlck = lid_to_tlock(lid);
+
+ TXN_UNLOCK();
+
+ /* initialize linelock */
+ linelock = (struct linelock *) tlck;
+ linelock->next = 0;
+ linelock->flag = tlckLINELOCK;
+ linelock->maxcnt = TLOCKLONG;
+ linelock->index = 0;
+
+ /* append linelock after tlock */
+ linelock->next = tlock->next;
+ tlock->next = lid;
+
+ return linelock;
+}
+
+
+
+/*
+ * transaction commit management
+ * -----------------------------
+ */
+
+/*
+ * NAME: txCommit()
+ *
+ * FUNCTION: commit the changes to the objects specified in
+ * clist. For journalled segments only the
+ * changes of the caller are committed, ie by tid.
+ * for non-journalled segments the data are flushed to
+ * disk and then the change to the disk inode and indirect
+ * blocks committed (so blocks newly allocated to the
+ * segment will be made a part of the segment atomically).
+ *
+ * all of the segments specified in clist must be in
+ * one file system. no more than 6 segments are needed
+ * to handle all unix svcs.
+ *
+ * if the i_nlink field (i.e. disk inode link count)
+ * is zero, and the type of inode is a regular file or
+ * directory, or symbolic link , the inode is truncated
+ * to zero length. the truncation is committed but the
+ * VM resources are unaffected until it is closed (see
+ * iput and iclose).
+ *
+ * PARAMETER:
+ *
+ * RETURN:
+ *
+ * serialization:
+ * on entry the inode lock on each segment is assumed
+ * to be held.
+ *
+ * i/o error:
+ */
+int txCommit(tid_t tid, /* transaction identifier */
+ int nip, /* number of inodes to commit */
+ struct inode **iplist, /* list of inode to commit */
+ int flag)
+{
+ int rc = 0;
+ struct commit cd;
+ struct jfs_log *log;
+ struct tblock *tblk;
+ struct lrd *lrd;
+ int lsn;
+ struct inode *ip;
+ struct jfs_inode_info *jfs_ip;
+ int k, n;
+ ino_t top;
+ struct super_block *sb;
+
+ jfs_info("txCommit, tid = %d, flag = %d", tid, flag);
+ /* is read-only file system ? */
+ if (isReadOnly(iplist[0])) {
+ rc = -EROFS;
+ goto TheEnd;
+ }
+
+ sb = cd.sb = iplist[0]->i_sb;
+ cd.tid = tid;
+
+ if (tid == 0)
+ tid = txBegin(sb, 0);
+ tblk = tid_to_tblock(tid);
+
+ /*
+ * initialize commit structure
+ */
+ log = JFS_SBI(sb)->log;
+ cd.log = log;
+
+ /* initialize log record descriptor in commit */
+ lrd = &cd.lrd;
+ lrd->logtid = cpu_to_le32(tblk->logtid);
+ lrd->backchain = 0;
+
+ tblk->xflag |= flag;
+
+ if ((flag & (COMMIT_FORCE | COMMIT_SYNC)) == 0)
+ tblk->xflag |= COMMIT_LAZY;
+ /*
+ * prepare non-journaled objects for commit
+ *
+ * flush data pages of non-journaled file
+ * to prevent the file getting non-initialized disk blocks
+ * in case of crash.
+ * (new blocks - )
+ */
+ cd.iplist = iplist;
+ cd.nip = nip;
+
+ /*
+ * acquire transaction lock on (on-disk) inodes
+ *
+ * update on-disk inode from in-memory inode
+ * acquiring transaction locks for AFTER records
+ * on the on-disk inode of file object
+ *
+ * sort the inodes array by inode number in descending order
+ * to prevent deadlock when acquiring transaction lock
+ * of on-disk inodes on multiple on-disk inode pages by
+ * multiple concurrent transactions
+ */
+ for (k = 0; k < cd.nip; k++) {
+ top = (cd.iplist[k])->i_ino;
+ for (n = k + 1; n < cd.nip; n++) {
+ ip = cd.iplist[n];
+ if (ip->i_ino > top) {
+ top = ip->i_ino;
+ cd.iplist[n] = cd.iplist[k];
+ cd.iplist[k] = ip;
+ }
+ }
+
+ ip = cd.iplist[k];
+ jfs_ip = JFS_IP(ip);
+
+ if (test_and_clear_cflag(COMMIT_Syncdata, ip) &&
+ ((tblk->flag && COMMIT_DELETE) == 0))
+ fsync_inode_data_buffers(ip);
+
+ /*
+ * Mark inode as not dirty. It will still be on the dirty
+ * inode list, but we'll know not to commit it again unless
+ * it gets marked dirty again
+ */
+ clear_cflag(COMMIT_Dirty, ip);
+
+ /* inherit anonymous tlock(s) of inode */
+ if (jfs_ip->atlhead) {
+ lid_to_tlock(jfs_ip->atltail)->next = tblk->next;
+ tblk->next = jfs_ip->atlhead;
+ if (!tblk->last)
+ tblk->last = jfs_ip->atltail;
+ jfs_ip->atlhead = jfs_ip->atltail = 0;
+ TXN_LOCK();
+ list_del_init(&jfs_ip->anon_inode_list);
+ TXN_UNLOCK();
+ }
+
+ /*
+ * acquire transaction lock on on-disk inode page
+ * (become first tlock of the tblk's tlock list)
+ */
+ if (((rc = diWrite(tid, ip))))
+ goto out;
+ }
+
+ /*
+ * write log records from transaction locks
+ *
+ * txUpdateMap() resets XAD_NEW in XAD.
+ */
+ if ((rc = txLog(log, tblk, &cd)))
+ goto TheEnd;
+
+ /*
+ * Ensure that inode isn't reused before
+ * lazy commit thread finishes processing
+ */
+ if (tblk->xflag & (COMMIT_CREATE | COMMIT_DELETE)) {
+ atomic_inc(&tblk->ip->i_count);
+ /*
+ * Avoid a rare deadlock
+ *
+ * If the inode is locked, we may be blocked in
+ * jfs_commit_inode. If so, we don't want the
+ * lazy_commit thread doing the last iput() on the inode
+ * since that may block on the locked inode. Instead,
+ * commit the transaction synchronously, so the last iput
+ * will be done by the calling thread (or later)
+ */
+ if (tblk->ip->i_state & I_LOCK)
+ tblk->xflag &= ~COMMIT_LAZY;
+ }
+
+ ASSERT((!(tblk->xflag & COMMIT_DELETE)) ||
+ ((tblk->ip->i_nlink == 0) &&
+ !test_cflag(COMMIT_Nolink, tblk->ip)));
+
+ /*
+ * write COMMIT log record
+ */
+ lrd->type = cpu_to_le16(LOG_COMMIT);
+ lrd->length = 0;
+ lsn = lmLog(log, tblk, lrd, NULL);
+
+ lmGroupCommit(log, tblk);
+
+ /*
+ * - transaction is now committed -
+ */
+
+ /*
+ * force pages in careful update
+ * (imap addressing structure update)
+ */
+ if (flag & COMMIT_FORCE)
+ txForce(tblk);
+
+ /*
+ * update allocation map.
+ *
+ * update inode allocation map and inode:
+ * free pager lock on memory object of inode if any.
+ * update block allocation map.
+ *
+ * txUpdateMap() resets XAD_NEW in XAD.
+ */
+ if (tblk->xflag & COMMIT_FORCE)
+ txUpdateMap(tblk);
+
+ /*
+ * free transaction locks and pageout/free pages
+ */
+ txRelease(tblk);
+
+ if ((tblk->flag & tblkGC_LAZY) == 0)
+ txUnlock(tblk);
+
+
+ /*
+ * reset in-memory object state
+ */
+ for (k = 0; k < cd.nip; k++) {
+ ip = cd.iplist[k];
+ jfs_ip = JFS_IP(ip);
+
+ /*
+ * reset in-memory inode state
+ */
+ jfs_ip->bxflag = 0;
+ jfs_ip->blid = 0;
+ }
+
+ out:
+ if (rc != 0)
+ txAbort(tid, 1);
+
+ TheEnd:
+ jfs_info("txCommit: tid = %d, returning %d", tid, rc);
+ return rc;
+}
+
+
+/*
+ * NAME: txLog()
+ *
+ * FUNCTION: Writes AFTER log records for all lines modified
+ * by tid for segments specified by inodes in comdata.
+ * Code assumes only WRITELOCKS are recorded in lockwords.
+ *
+ * PARAMETERS:
+ *
+ * RETURN :
+ */
+static int txLog(struct jfs_log * log, struct tblock * tblk, struct commit * cd)
+{
+ int rc = 0;
+ struct inode *ip;
+ lid_t lid;
+ struct tlock *tlck;
+ struct lrd *lrd = &cd->lrd;
+
+ /*
+ * write log record(s) for each tlock of transaction,
+ */
+ for (lid = tblk->next; lid; lid = tlck->next) {
+ tlck = lid_to_tlock(lid);
+
+ tlck->flag |= tlckLOG;
+
+ /* initialize lrd common */
+ ip = tlck->ip;
+ lrd->aggregate = cpu_to_le32(kdev_t_to_nr(ip->i_dev));
+ lrd->log.redopage.fileset = cpu_to_le32(JFS_IP(ip)->fileset);
+ lrd->log.redopage.inode = cpu_to_le32(ip->i_ino);
+
+ /* write log record of page from the tlock */
+ switch (tlck->type & tlckTYPE) {
+ case tlckXTREE:
+ xtLog(log, tblk, lrd, tlck);
+ break;
+
+ case tlckDTREE:
+ dtLog(log, tblk, lrd, tlck);
+ break;
+
+ case tlckINODE:
+ diLog(log, tblk, lrd, tlck, cd);
+ break;
+
+ case tlckMAP:
+ mapLog(log, tblk, lrd, tlck);
+ break;
+
+ case tlckDATA:
+ dataLog(log, tblk, lrd, tlck);
+ break;
+
+ default:
+ jfs_err("UFO tlock:0x%p", tlck);
+ }
+ }
+
+ return rc;
+}
+
+
+/*
+ * diLog()
+ *
+ * function: log inode tlock and format maplock to update bmap;
+ */
+static int diLog(struct jfs_log * log, struct tblock * tblk, struct lrd * lrd,
+ struct tlock * tlck, struct commit * cd)
+{
+ int rc = 0;
+ struct metapage *mp;
+ pxd_t *pxd;
+ struct pxd_lock *pxdlock;
+
+ mp = tlck->mp;
+
+ /* initialize as REDOPAGE record format */
+ lrd->log.redopage.type = cpu_to_le16(LOG_INODE);
+ lrd->log.redopage.l2linesize = cpu_to_le16(L2INODESLOTSIZE);
+
+ pxd = &lrd->log.redopage.pxd;
+
+ /*
+ * inode after image
+ */
+ if (tlck->type & tlckENTRY) {
+ /* log after-image for logredo(): */
+ lrd->type = cpu_to_le16(LOG_REDOPAGE);
+// *pxd = mp->cm_pxd;
+ PXDaddress(pxd, mp->index);
+ PXDlength(pxd,
+ mp->logical_size >> tblk->sb->s_blocksize_bits);
+ lrd->backchain = cpu_to_le32(lmLog(log, tblk, lrd, tlck));
+
+ /* mark page as homeward bound */
+ tlck->flag |= tlckWRITEPAGE;
+ } else if (tlck->type & tlckFREE) {
+ /*
+ * free inode extent
+ *
+ * (pages of the freed inode extent have been invalidated and
+ * a maplock for free of the extent has been formatted at
+ * txLock() time);
+ *
+ * the tlock had been acquired on the inode allocation map page
+ * (iag) that specifies the freed extent, even though the map
+ * page is not itself logged, to prevent pageout of the map
+ * page before the log;
+ */
+
+ /* log LOG_NOREDOINOEXT of the freed inode extent for
+ * logredo() to start NoRedoPage filters, and to update
+ * imap and bmap for free of the extent;
+ */
+ lrd->type = cpu_to_le16(LOG_NOREDOINOEXT);
+ /*
+ * For the LOG_NOREDOINOEXT record, we need
+ * to pass the IAG number and inode extent
+ * index (within that IAG) from which the
+ * the extent being released. These have been
+ * passed to us in the iplist[1] and iplist[2].
+ */
+ lrd->log.noredoinoext.iagnum =
+ cpu_to_le32((u32) (size_t) cd->iplist[1]);
+ lrd->log.noredoinoext.inoext_idx =
+ cpu_to_le32((u32) (size_t) cd->iplist[2]);
+
+ pxdlock = (struct pxd_lock *) & tlck->lock;
+ *pxd = pxdlock->pxd;
+ lrd->backchain = cpu_to_le32(lmLog(log, tblk, lrd, NULL));
+
+ /* update bmap */
+ tlck->flag |= tlckUPDATEMAP;
+
+ /* mark page as homeward bound */
+ tlck->flag |= tlckWRITEPAGE;
+ } else
+ jfs_err("diLog: UFO type tlck:0x%p", tlck);
+#ifdef _JFS_WIP
+ /*
+ * alloc/free external EA extent
+ *
+ * a maplock for txUpdateMap() to update bPWMAP for alloc/free
+ * of the extent has been formatted at txLock() time;
+ */
+ else {
+ assert(tlck->type & tlckEA);
+
+ /* log LOG_UPDATEMAP for logredo() to update bmap for
+ * alloc of new (and free of old) external EA extent;
+ */
+ lrd->type = cpu_to_le16(LOG_UPDATEMAP);
+ pxdlock = (struct pxd_lock *) & tlck->lock;
+ nlock = pxdlock->index;
+ for (i = 0; i < nlock; i++, pxdlock++) {
+ if (pxdlock->flag & mlckALLOCPXD)
+ lrd->log.updatemap.type =
+ cpu_to_le16(LOG_ALLOCPXD);
+ else
+ lrd->log.updatemap.type =
+ cpu_to_le16(LOG_FREEPXD);
+ lrd->log.updatemap.nxd = cpu_to_le16(1);
+ lrd->log.updatemap.pxd = pxdlock->pxd;
+ lrd->backchain =
+ cpu_to_le32(lmLog(log, tblk, lrd, NULL));
+ }
+
+ /* update bmap */
+ tlck->flag |= tlckUPDATEMAP;
+ }
+#endif /* _JFS_WIP */
+
+ return rc;
+}
+
+
+/*
+ * dataLog()
+ *
+ * function: log data tlock
+ */
+static int dataLog(struct jfs_log * log, struct tblock * tblk, struct lrd * lrd,
+ struct tlock * tlck)
+{
+ struct metapage *mp;
+ pxd_t *pxd;
+
+ mp = tlck->mp;
+
+ /* initialize as REDOPAGE record format */
+ lrd->log.redopage.type = cpu_to_le16(LOG_DATA);
+ lrd->log.redopage.l2linesize = cpu_to_le16(L2DATASLOTSIZE);
+
+ pxd = &lrd->log.redopage.pxd;
+
+ /* log after-image for logredo(): */
+ lrd->type = cpu_to_le16(LOG_REDOPAGE);
+
+ if (JFS_IP(tlck->ip)->next_index < MAX_INLINE_DIRTABLE_ENTRY) {
+ /*
+ * The table has been truncated, we've must have deleted
+ * the last entry, so don't bother logging this
+ */
+ mp->lid = 0;
+ hold_metapage(mp, 0);
+ atomic_dec(&mp->nohomeok);
+ discard_metapage(mp);
+ tlck->mp = 0;
+ return 0;
+ }
+
+ PXDaddress(pxd, mp->index);
+ PXDlength(pxd, mp->logical_size >> tblk->sb->s_blocksize_bits);
+
+ lrd->backchain = cpu_to_le32(lmLog(log, tblk, lrd, tlck));
+
+ /* mark page as homeward bound */
+ tlck->flag |= tlckWRITEPAGE;
+
+ return 0;
+}
+
+
+/*
+ * dtLog()
+ *
+ * function: log dtree tlock and format maplock to update bmap;
+ */
+static void dtLog(struct jfs_log * log, struct tblock * tblk, struct lrd * lrd,
+ struct tlock * tlck)
+{
+ struct metapage *mp;
+ struct pxd_lock *pxdlock;
+ pxd_t *pxd;
+
+ mp = tlck->mp;
+
+ /* initialize as REDOPAGE/NOREDOPAGE record format */
+ lrd->log.redopage.type = cpu_to_le16(LOG_DTREE);
+ lrd->log.redopage.l2linesize = cpu_to_le16(L2DTSLOTSIZE);
+
+ pxd = &lrd->log.redopage.pxd;
+
+ if (tlck->type & tlckBTROOT)
+ lrd->log.redopage.type |= cpu_to_le16(LOG_BTROOT);
+
+ /*
+ * page extension via relocation: entry insertion;
+ * page extension in-place: entry insertion;
+ * new right page from page split, reinitialized in-line
+ * root from root page split: entry insertion;
+ */
+ if (tlck->type & (tlckNEW | tlckEXTEND)) {
+ /* log after-image of the new page for logredo():
+ * mark log (LOG_NEW) for logredo() to initialize
+ * freelist and update bmap for alloc of the new page;
+ */
+ lrd->type = cpu_to_le16(LOG_REDOPAGE);
+ if (tlck->type & tlckEXTEND)
+ lrd->log.redopage.type |= cpu_to_le16(LOG_EXTEND);
+ else
+ lrd->log.redopage.type |= cpu_to_le16(LOG_NEW);
+// *pxd = mp->cm_pxd;
+ PXDaddress(pxd, mp->index);
+ PXDlength(pxd,
+ mp->logical_size >> tblk->sb->s_blocksize_bits);
+ lrd->backchain = cpu_to_le32(lmLog(log, tblk, lrd, tlck));
+
+ /* format a maplock for txUpdateMap() to update bPMAP for
+ * alloc of the new page;
+ */
+ if (tlck->type & tlckBTROOT)
+ return;
+ tlck->flag |= tlckUPDATEMAP;
+ pxdlock = (struct pxd_lock *) & tlck->lock;
+ pxdlock->flag = mlckALLOCPXD;
+ pxdlock->pxd = *pxd;
+
+ pxdlock->index = 1;
+
+ /* mark page as homeward bound */
+ tlck->flag |= tlckWRITEPAGE;
+ return;
+ }
+
+ /*
+ * entry insertion/deletion,
+ * sibling page link update (old right page before split);
+ */
+ if (tlck->type & (tlckENTRY | tlckRELINK)) {
+ /* log after-image for logredo(): */
+ lrd->type = cpu_to_le16(LOG_REDOPAGE);
+ PXDaddress(pxd, mp->index);
+ PXDlength(pxd,
+ mp->logical_size >> tblk->sb->s_blocksize_bits);
+ lrd->backchain = cpu_to_le32(lmLog(log, tblk, lrd, tlck));
+
+ /* mark page as homeward bound */
+ tlck->flag |= tlckWRITEPAGE;
+ return;
+ }
+
+ /*
+ * page deletion: page has been invalidated
+ * page relocation: source extent
+ *
+ * a maplock for free of the page has been formatted
+ * at txLock() time);
+ */
+ if (tlck->type & (tlckFREE | tlckRELOCATE)) {
+ /* log LOG_NOREDOPAGE of the deleted page for logredo()
+ * to start NoRedoPage filter and to update bmap for free
+ * of the deletd page
+ */
+ lrd->type = cpu_to_le16(LOG_NOREDOPAGE);
+ pxdlock = (struct pxd_lock *) & tlck->lock;
+ *pxd = pxdlock->pxd;
+ lrd->backchain = cpu_to_le32(lmLog(log, tblk, lrd, NULL));
+
+ /* a maplock for txUpdateMap() for free of the page
+ * has been formatted at txLock() time;
+ */
+ tlck->flag |= tlckUPDATEMAP;
+ }
+ return;
+}
+
+
+/*
+ * xtLog()
+ *
+ * function: log xtree tlock and format maplock to update bmap;
+ */
+static void xtLog(struct jfs_log * log, struct tblock * tblk, struct lrd * lrd,
+ struct tlock * tlck)
+{
+ struct inode *ip;
+ struct metapage *mp;
+ xtpage_t *p;
+ struct xtlock *xtlck;
+ struct maplock *maplock;
+ struct xdlistlock *xadlock;
+ struct pxd_lock *pxdlock;
+ pxd_t *pxd;
+ int next, lwm, hwm;
+
+ ip = tlck->ip;
+ mp = tlck->mp;
+
+ /* initialize as REDOPAGE/NOREDOPAGE record format */
+ lrd->log.redopage.type = cpu_to_le16(LOG_XTREE);
+ lrd->log.redopage.l2linesize = cpu_to_le16(L2XTSLOTSIZE);
+
+ pxd = &lrd->log.redopage.pxd;
+
+ if (tlck->type & tlckBTROOT) {
+ lrd->log.redopage.type |= cpu_to_le16(LOG_BTROOT);
+ p = &JFS_IP(ip)->i_xtroot;
+ if (S_ISDIR(ip->i_mode))
+ lrd->log.redopage.type |=
+ cpu_to_le16(LOG_DIR_XTREE);
+ } else
+ p = (xtpage_t *) mp->data;
+ next = le16_to_cpu(p->header.nextindex);
+
+ xtlck = (struct xtlock *) & tlck->lock;
+
+ maplock = (struct maplock *) & tlck->lock;
+ xadlock = (struct xdlistlock *) maplock;
+
+ /*
+ * entry insertion/extension;
+ * sibling page link update (old right page before split);
+ */
+ if (tlck->type & (tlckNEW | tlckGROW | tlckRELINK)) {
+ /* log after-image for logredo():
+ * logredo() will update bmap for alloc of new/extended
+ * extents (XAD_NEW|XAD_EXTEND) of XAD[lwm:next) from
+ * after-image of XADlist;
+ * logredo() resets (XAD_NEW|XAD_EXTEND) flag when
+ * applying the after-image to the meta-data page.
+ */
+ lrd->type = cpu_to_le16(LOG_REDOPAGE);
+// *pxd = mp->cm_pxd;
+ PXDaddress(pxd, mp->index);
+ PXDlength(pxd,
+ mp->logical_size >> tblk->sb->s_blocksize_bits);
+ lrd->backchain = cpu_to_le32(lmLog(log, tblk, lrd, tlck));
+
+ /* format a maplock for txUpdateMap() to update bPMAP
+ * for alloc of new/extended extents of XAD[lwm:next)
+ * from the page itself;
+ * txUpdateMap() resets (XAD_NEW|XAD_EXTEND) flag.
+ */
+ lwm = xtlck->lwm.offset;
+ if (lwm == 0)
+ lwm = XTPAGEMAXSLOT;
+
+ if (lwm == next)
+ goto out;
+ if (lwm > next) {
+ jfs_err("xtLog: lwm > next\n");
+ goto out;
+ }
+ tlck->flag |= tlckUPDATEMAP;
+ xadlock->flag = mlckALLOCXADLIST;
+ xadlock->count = next - lwm;
+ if ((xadlock->count <= 2) && (tblk->xflag & COMMIT_LAZY)) {
+ int i;
+ /*
+ * Lazy commit may allow xtree to be modified before
+ * txUpdateMap runs. Copy xad into linelock to
+ * preserve correct data.
+ */
+ xadlock->xdlist = &xtlck->pxdlock;
+ memcpy(xadlock->xdlist, &p->xad[lwm],
+ sizeof(xad_t) * xadlock->count);
+
+ for (i = 0; i < xadlock->count; i++)
+ p->xad[lwm + i].flag &=
+ ~(XAD_NEW | XAD_EXTENDED);
+ } else {
+ /*
+ * xdlist will point to into inode's xtree, ensure
+ * that transaction is not committed lazily.
+ */
+ xadlock->xdlist = &p->xad[lwm];
+ tblk->xflag &= ~COMMIT_LAZY;
+ }
+ jfs_info("xtLog: alloc ip:0x%p mp:0x%p tlck:0x%p lwm:%d "
+ "count:%d", tlck->ip, mp, tlck, lwm, xadlock->count);
+
+ maplock->index = 1;
+
+ out:
+ /* mark page as homeward bound */
+ tlck->flag |= tlckWRITEPAGE;
+
+ return;
+ }
+
+ /*
+ * page deletion: file deletion/truncation (ref. xtTruncate())
+ *
+ * (page will be invalidated after log is written and bmap
+ * is updated from the page);
+ */
+ if (tlck->type & tlckFREE) {
+ /* LOG_NOREDOPAGE log for NoRedoPage filter:
+ * if page free from file delete, NoRedoFile filter from
+ * inode image of zero link count will subsume NoRedoPage
+ * filters for each page;
+ * if page free from file truncattion, write NoRedoPage
+ * filter;
+ *
+ * upadte of block allocation map for the page itself:
+ * if page free from deletion and truncation, LOG_UPDATEMAP
+ * log for the page itself is generated from processing
+ * its parent page xad entries;
+ */
+ /* if page free from file truncation, log LOG_NOREDOPAGE
+ * of the deleted page for logredo() to start NoRedoPage
+ * filter for the page;
+ */
+ if (tblk->xflag & COMMIT_TRUNCATE) {
+ /* write NOREDOPAGE for the page */
+ lrd->type = cpu_to_le16(LOG_NOREDOPAGE);
+ PXDaddress(pxd, mp->index);
+ PXDlength(pxd,
+ mp->logical_size >> tblk->sb->
+ s_blocksize_bits);
+ lrd->backchain =
+ cpu_to_le32(lmLog(log, tblk, lrd, NULL));
+
+ if (tlck->type & tlckBTROOT) {
+ /* Empty xtree must be logged */
+ lrd->type = cpu_to_le16(LOG_REDOPAGE);
+ lrd->backchain =
+ cpu_to_le32(lmLog(log, tblk, lrd, tlck));
+ }
+ }
+
+ /* init LOG_UPDATEMAP of the freed extents
+ * XAD[XTENTRYSTART:hwm) from the deleted page itself
+ * for logredo() to update bmap;
+ */
+ lrd->type = cpu_to_le16(LOG_UPDATEMAP);
+ lrd->log.updatemap.type = cpu_to_le16(LOG_FREEXADLIST);
+ xtlck = (struct xtlock *) & tlck->lock;
+ hwm = xtlck->hwm.offset;
+ lrd->log.updatemap.nxd =
+ cpu_to_le16(hwm - XTENTRYSTART + 1);
+ /* reformat linelock for lmLog() */
+ xtlck->header.offset = XTENTRYSTART;
+ xtlck->header.length = hwm - XTENTRYSTART + 1;
+ xtlck->index = 1;
+ lrd->backchain = cpu_to_le32(lmLog(log, tblk, lrd, tlck));
+
+ /* format a maplock for txUpdateMap() to update bmap
+ * to free extents of XAD[XTENTRYSTART:hwm) from the
+ * deleted page itself;
+ */
+ tlck->flag |= tlckUPDATEMAP;
+ xadlock->flag = mlckFREEXADLIST;
+ xadlock->count = hwm - XTENTRYSTART + 1;
+ if ((xadlock->count <= 2) && (tblk->xflag & COMMIT_LAZY)) {
+ /*
+ * Lazy commit may allow xtree to be modified before
+ * txUpdateMap runs. Copy xad into linelock to
+ * preserve correct data.
+ */
+ xadlock->xdlist = &xtlck->pxdlock;
+ memcpy(xadlock->xdlist, &p->xad[XTENTRYSTART],
+ sizeof(xad_t) * xadlock->count);
+ } else {
+ /*
+ * xdlist will point to into inode's xtree, ensure
+ * that transaction is not committed lazily.
+ */
+ xadlock->xdlist = &p->xad[XTENTRYSTART];
+ tblk->xflag &= ~COMMIT_LAZY;
+ }
+ jfs_info("xtLog: free ip:0x%p mp:0x%p count:%d lwm:2",
+ tlck->ip, mp, xadlock->count);
+
+ maplock->index = 1;
+
+ /* mark page as invalid */
+ if (((tblk->xflag & COMMIT_PWMAP) || S_ISDIR(ip->i_mode))
+ && !(tlck->type & tlckBTROOT))
+ tlck->flag |= tlckFREEPAGE;
+ /*
+ else (tblk->xflag & COMMIT_PMAP)
+ ? release the page;
+ */
+ return;
+ }
+
+ /*
+ * page/entry truncation: file truncation (ref. xtTruncate())
+ *
+ * |----------+------+------+---------------|
+ * | | |
+ * | | hwm - hwm before truncation
+ * | next - truncation point
+ * lwm - lwm before truncation
+ * header ?
+ */
+ if (tlck->type & tlckTRUNCATE) {
+ pxd_t tpxd; /* truncated extent of xad */
+ int twm;
+
+ /*
+ * For truncation the entire linelock may be used, so it would
+ * be difficult to store xad list in linelock itself.
+ * Therefore, we'll just force transaction to be committed
+ * synchronously, so that xtree pages won't be changed before
+ * txUpdateMap runs.
+ */
+ tblk->xflag &= ~COMMIT_LAZY;
+ lwm = xtlck->lwm.offset;
+ if (lwm == 0)
+ lwm = XTPAGEMAXSLOT;
+ hwm = xtlck->hwm.offset;
+ twm = xtlck->twm.offset;
+
+ /*
+ * write log records
+ */
+ /* log after-image for logredo():
+ *
+ * logredo() will update bmap for alloc of new/extended
+ * extents (XAD_NEW|XAD_EXTEND) of XAD[lwm:next) from
+ * after-image of XADlist;
+ * logredo() resets (XAD_NEW|XAD_EXTEND) flag when
+ * applying the after-image to the meta-data page.
+ */
+ lrd->type = cpu_to_le16(LOG_REDOPAGE);
+ PXDaddress(pxd, mp->index);
+ PXDlength(pxd, mp->logical_size >> tblk->sb->s_blocksize_bits);
+ lrd->backchain = cpu_to_le32(lmLog(log, tblk, lrd, tlck));
+
+ /*
+ * truncate entry XAD[twm == next - 1]:
+ */
+ if (twm == next - 1) {
+ /* init LOG_UPDATEMAP for logredo() to update bmap for
+ * free of truncated delta extent of the truncated
+ * entry XAD[next - 1]:
+ * (xtlck->pxdlock = truncated delta extent);
+ */
+ pxdlock = (struct pxd_lock *) & xtlck->pxdlock;
+ /* assert(pxdlock->type & tlckTRUNCATE); */
+ lrd->type = cpu_to_le16(LOG_UPDATEMAP);
+ lrd->log.updatemap.type = cpu_to_le16(LOG_FREEPXD);
+ lrd->log.updatemap.nxd = cpu_to_le16(1);
+ lrd->log.updatemap.pxd = pxdlock->pxd;
+ tpxd = pxdlock->pxd; /* save to format maplock */
+ lrd->backchain =
+ cpu_to_le32(lmLog(log, tblk, lrd, NULL));
+ }
+
+ /*
+ * free entries XAD[next:hwm]:
+ */
+ if (hwm >= next) {
+ /* init LOG_UPDATEMAP of the freed extents
+ * XAD[next:hwm] from the deleted page itself
+ * for logredo() to update bmap;
+ */
+ lrd->type = cpu_to_le16(LOG_UPDATEMAP);
+ lrd->log.updatemap.type =
+ cpu_to_le16(LOG_FREEXADLIST);
+ xtlck = (struct xtlock *) & tlck->lock;
+ hwm = xtlck->hwm.offset;
+ lrd->log.updatemap.nxd =
+ cpu_to_le16(hwm - next + 1);
+ /* reformat linelock for lmLog() */
+ xtlck->header.offset = next;
+ xtlck->header.length = hwm - next + 1;
+ xtlck->index = 1;
+ lrd->backchain =
+ cpu_to_le32(lmLog(log, tblk, lrd, tlck));
+ }
+
+ /*
+ * format maplock(s) for txUpdateMap() to update bmap
+ */
+ maplock->index = 0;
+
+ /*
+ * allocate entries XAD[lwm:next):
+ */
+ if (lwm < next) {
+ /* format a maplock for txUpdateMap() to update bPMAP
+ * for alloc of new/extended extents of XAD[lwm:next)
+ * from the page itself;
+ * txUpdateMap() resets (XAD_NEW|XAD_EXTEND) flag.
+ */
+ tlck->flag |= tlckUPDATEMAP;
+ xadlock->flag = mlckALLOCXADLIST;
+ xadlock->count = next - lwm;
+ xadlock->xdlist = &p->xad[lwm];
+
+ jfs_info("xtLog: alloc ip:0x%p mp:0x%p count:%d "
+ "lwm:%d next:%d",
+ tlck->ip, mp, xadlock->count, lwm, next);
+ maplock->index++;
+ xadlock++;
+ }
+
+ /*
+ * truncate entry XAD[twm == next - 1]:
+ */
+ if (twm == next - 1) {
+ struct pxd_lock *pxdlock;
+
+ /* format a maplock for txUpdateMap() to update bmap
+ * to free truncated delta extent of the truncated
+ * entry XAD[next - 1];
+ * (xtlck->pxdlock = truncated delta extent);
+ */
+ tlck->flag |= tlckUPDATEMAP;
+ pxdlock = (struct pxd_lock *) xadlock;
+ pxdlock->flag = mlckFREEPXD;
+ pxdlock->count = 1;
+ pxdlock->pxd = tpxd;
+
+ jfs_info("xtLog: truncate ip:0x%p mp:0x%p count:%d "
+ "hwm:%d", ip, mp, pxdlock->count, hwm);
+ maplock->index++;
+ xadlock++;
+ }
+
+ /*
+ * free entries XAD[next:hwm]:
+ */
+ if (hwm >= next) {
+ /* format a maplock for txUpdateMap() to update bmap
+ * to free extents of XAD[next:hwm] from thedeleted
+ * page itself;
+ */
+ tlck->flag |= tlckUPDATEMAP;
+ xadlock->flag = mlckFREEXADLIST;
+ xadlock->count = hwm - next + 1;
+ xadlock->xdlist = &p->xad[next];
+
+ jfs_info("xtLog: free ip:0x%p mp:0x%p count:%d "
+ "next:%d hwm:%d",
+ tlck->ip, mp, xadlock->count, next, hwm);
+ maplock->index++;
+ }
+
+ /* mark page as homeward bound */
+ tlck->flag |= tlckWRITEPAGE;
+ }
+ return;
+}
+
+
+/*
+ * mapLog()
+ *
+ * function: log from maplock of freed data extents;
+ */
+void mapLog(struct jfs_log * log, struct tblock * tblk, struct lrd * lrd,
+ struct tlock * tlck)
+{
+ struct pxd_lock *pxdlock;
+ int i, nlock;
+ pxd_t *pxd;
+
+ /*
+ * page relocation: free the source page extent
+ *
+ * a maplock for txUpdateMap() for free of the page
+ * has been formatted at txLock() time saving the src
+ * relocated page address;
+ */
+ if (tlck->type & tlckRELOCATE) {
+ /* log LOG_NOREDOPAGE of the old relocated page
+ * for logredo() to start NoRedoPage filter;
+ */
+ lrd->type = cpu_to_le16(LOG_NOREDOPAGE);
+ pxdlock = (struct pxd_lock *) & tlck->lock;
+ pxd = &lrd->log.redopage.pxd;
+ *pxd = pxdlock->pxd;
+ lrd->backchain = cpu_to_le32(lmLog(log, tblk, lrd, NULL));
+
+ /* (N.B. currently, logredo() does NOT update bmap
+ * for free of the page itself for (LOG_XTREE|LOG_NOREDOPAGE);
+ * if page free from relocation, LOG_UPDATEMAP log is
+ * specifically generated now for logredo()
+ * to update bmap for free of src relocated page;
+ * (new flag LOG_RELOCATE may be introduced which will
+ * inform logredo() to start NORedoPage filter and also
+ * update block allocation map at the same time, thus
+ * avoiding an extra log write);
+ */
+ lrd->type = cpu_to_le16(LOG_UPDATEMAP);
+ lrd->log.updatemap.type = cpu_to_le16(LOG_FREEPXD);
+ lrd->log.updatemap.nxd = cpu_to_le16(1);
+ lrd->log.updatemap.pxd = pxdlock->pxd;
+ lrd->backchain = cpu_to_le32(lmLog(log, tblk, lrd, NULL));
+
+ /* a maplock for txUpdateMap() for free of the page
+ * has been formatted at txLock() time;
+ */
+ tlck->flag |= tlckUPDATEMAP;
+ return;
+ }
+ /*
+
+ * Otherwise it's not a relocate request
+ *
+ */
+ else {
+ /* log LOG_UPDATEMAP for logredo() to update bmap for
+ * free of truncated/relocated delta extent of the data;
+ * e.g.: external EA extent, relocated/truncated extent
+ * from xtTailgate();
+ */
+ lrd->type = cpu_to_le16(LOG_UPDATEMAP);
+ pxdlock = (struct pxd_lock *) & tlck->lock;
+ nlock = pxdlock->index;
+ for (i = 0; i < nlock; i++, pxdlock++) {
+ if (pxdlock->flag & mlckALLOCPXD)
+ lrd->log.updatemap.type =
+ cpu_to_le16(LOG_ALLOCPXD);
+ else
+ lrd->log.updatemap.type =
+ cpu_to_le16(LOG_FREEPXD);
+ lrd->log.updatemap.nxd = cpu_to_le16(1);
+ lrd->log.updatemap.pxd = pxdlock->pxd;
+ lrd->backchain =
+ cpu_to_le32(lmLog(log, tblk, lrd, NULL));
+ jfs_info("mapLog: xaddr:0x%lx xlen:0x%x",
+ (ulong) addressPXD(&pxdlock->pxd),
+ lengthPXD(&pxdlock->pxd));
+ }
+
+ /* update bmap */
+ tlck->flag |= tlckUPDATEMAP;
+ }
+}
+
+
+/*
+ * txEA()
+ *
+ * function: acquire maplock for EA/ACL extents or
+ * set COMMIT_INLINE flag;
+ */
+void txEA(tid_t tid, struct inode *ip, dxd_t * oldea, dxd_t * newea)
+{
+ struct tlock *tlck = NULL;
+ struct pxd_lock *maplock = NULL, *pxdlock = NULL;
+
+ /*
+ * format maplock for alloc of new EA extent
+ */
+ if (newea) {
+ /* Since the newea could be a completely zeroed entry we need to
+ * check for the two flags which indicate we should actually
+ * commit new EA data
+ */
+ if (newea->flag & DXD_EXTENT) {
+ tlck = txMaplock(tid, ip, tlckMAP);
+ maplock = (struct pxd_lock *) & tlck->lock;
+ pxdlock = (struct pxd_lock *) maplock;
+ pxdlock->flag = mlckALLOCPXD;
+ PXDaddress(&pxdlock->pxd, addressDXD(newea));
+ PXDlength(&pxdlock->pxd, lengthDXD(newea));
+ pxdlock++;
+ maplock->index = 1;
+ } else if (newea->flag & DXD_INLINE) {
+ tlck = NULL;
+
+ set_cflag(COMMIT_Inlineea, ip);
+ }
+ }
+
+ /*
+ * format maplock for free of old EA extent
+ */
+ if (!test_cflag(COMMIT_Nolink, ip) && oldea->flag & DXD_EXTENT) {
+ if (tlck == NULL) {
+ tlck = txMaplock(tid, ip, tlckMAP);
+ maplock = (struct pxd_lock *) & tlck->lock;
+ pxdlock = (struct pxd_lock *) maplock;
+ maplock->index = 0;
+ }
+ pxdlock->flag = mlckFREEPXD;
+ PXDaddress(&pxdlock->pxd, addressDXD(oldea));
+ PXDlength(&pxdlock->pxd, lengthDXD(oldea));
+ maplock->index++;
+ }
+}
+
+
+/*
+ * txForce()
+ *
+ * function: synchronously write pages locked by transaction
+ * after txLog() but before txUpdateMap();
+ */
+void txForce(struct tblock * tblk)
+{
+ struct tlock *tlck;
+ lid_t lid, next;
+ struct metapage *mp;
+
+ /*
+ * reverse the order of transaction tlocks in
+ * careful update order of address index pages
+ * (right to left, bottom up)
+ */
+ tlck = lid_to_tlock(tblk->next);
+ lid = tlck->next;
+ tlck->next = 0;
+ while (lid) {
+ tlck = lid_to_tlock(lid);
+ next = tlck->next;
+ tlck->next = tblk->next;
+ tblk->next = lid;
+ lid = next;
+ }
+
+ /*
+ * synchronously write the page, and
+ * hold the page for txUpdateMap();
+ */
+ for (lid = tblk->next; lid; lid = next) {
+ tlck = lid_to_tlock(lid);
+ next = tlck->next;
+
+ if ((mp = tlck->mp) != NULL &&
+ (tlck->type & tlckBTROOT) == 0) {
+ assert(mp->xflag & COMMIT_PAGE);
+
+ if (tlck->flag & tlckWRITEPAGE) {
+ tlck->flag &= ~tlckWRITEPAGE;
+
+ /* do not release page to freelist */
+
+ /*
+ * The "right" thing to do here is to
+ * synchronously write the metadata.
+ * With the current implementation this
+ * is hard since write_metapage requires
+ * us to kunmap & remap the page. If we
+ * have tlocks pointing into the metadata
+ * pages, we don't want to do this. I think
+ * we can get by with synchronously writing
+ * the pages when they are released.
+ */
+ assert(atomic_read(&mp->nohomeok));
+ set_bit(META_dirty, &mp->flag);
+ set_bit(META_sync, &mp->flag);
+ }
+ }
+ }
+}
+
+
+/*
+ * txUpdateMap()
+ *
+ * function: update persistent allocation map (and working map
+ * if appropriate);
+ *
+ * parameter:
+ */
+static void txUpdateMap(struct tblock * tblk)
+{
+ struct inode *ip;
+ struct inode *ipimap;
+ lid_t lid;
+ struct tlock *tlck;
+ struct maplock *maplock;
+ struct pxd_lock pxdlock;
+ int maptype;
+ int k, nlock;
+ struct metapage *mp = 0;
+
+ ipimap = JFS_SBI(tblk->sb)->ipimap;
+
+ maptype = (tblk->xflag & COMMIT_PMAP) ? COMMIT_PMAP : COMMIT_PWMAP;
+
+
+ /*
+ * update block allocation map
+ *
+ * update allocation state in pmap (and wmap) and
+ * update lsn of the pmap page;
+ */
+ /*
+ * scan each tlock/page of transaction for block allocation/free:
+ *
+ * for each tlock/page of transaction, update map.
+ * ? are there tlock for pmap and pwmap at the same time ?
+ */
+ for (lid = tblk->next; lid; lid = tlck->next) {
+ tlck = lid_to_tlock(lid);
+
+ if ((tlck->flag & tlckUPDATEMAP) == 0)
+ continue;
+
+ if (tlck->flag & tlckFREEPAGE) {
+ /*
+ * Another thread may attempt to reuse freed space
+ * immediately, so we want to get rid of the metapage
+ * before anyone else has a chance to get it.
+ * Lock metapage, update maps, then invalidate
+ * the metapage.
+ */
+ mp = tlck->mp;
+ ASSERT(mp->xflag & COMMIT_PAGE);
+ hold_metapage(mp, 0);
+ }
+
+ /*
+ * extent list:
+ * . in-line PXD list:
+ * . out-of-line XAD list:
+ */
+ maplock = (struct maplock *) & tlck->lock;
+ nlock = maplock->index;
+
+ for (k = 0; k < nlock; k++, maplock++) {
+ /*
+ * allocate blocks in persistent map:
+ *
+ * blocks have been allocated from wmap at alloc time;
+ */
+ if (maplock->flag & mlckALLOC) {
+ txAllocPMap(ipimap, maplock, tblk);
+ }
+ /*
+ * free blocks in persistent and working map:
+ * blocks will be freed in pmap and then in wmap;
+ *
+ * ? tblock specifies the PMAP/PWMAP based upon
+ * transaction
+ *
+ * free blocks in persistent map:
+ * blocks will be freed from wmap at last reference
+ * release of the object for regular files;
+ *
+ * Alway free blocks from both persistent & working
+ * maps for directories
+ */
+ else { /* (maplock->flag & mlckFREE) */
+
+ if (S_ISDIR(tlck->ip->i_mode))
+ txFreeMap(ipimap, maplock,
+ tblk, COMMIT_PWMAP);
+ else
+ txFreeMap(ipimap, maplock,
+ tblk, maptype);
+ }
+ }
+ if (tlck->flag & tlckFREEPAGE) {
+ if (!(tblk->flag & tblkGC_LAZY)) {
+ /* This is equivalent to txRelease */
+ ASSERT(mp->lid == lid);
+ tlck->mp->lid = 0;
+ }
+ assert(atomic_read(&mp->nohomeok) == 1);
+ atomic_dec(&mp->nohomeok);
+ discard_metapage(mp);
+ tlck->mp = 0;
+ }
+ }
+ /*
+ * update inode allocation map
+ *
+ * update allocation state in pmap and
+ * update lsn of the pmap page;
+ * update in-memory inode flag/state
+ *
+ * unlock mapper/write lock
+ */
+ if (tblk->xflag & COMMIT_CREATE) {
+ ip = tblk->ip;
+
+ ASSERT(test_cflag(COMMIT_New, ip));
+ clear_cflag(COMMIT_New, ip);
+
+ diUpdatePMap(ipimap, ip->i_ino, FALSE, tblk);
+ ipimap->i_state |= I_DIRTY;
+ /* update persistent block allocation map
+ * for the allocation of inode extent;
+ */
+ pxdlock.flag = mlckALLOCPXD;
+ pxdlock.pxd = JFS_IP(ip)->ixpxd;
+ pxdlock.index = 1;
+ txAllocPMap(ip, (struct maplock *) & pxdlock, tblk);
+ iput(ip);
+ } else if (tblk->xflag & COMMIT_DELETE) {
+ ip = tblk->ip;
+ diUpdatePMap(ipimap, ip->i_ino, TRUE, tblk);
+ ipimap->i_state |= I_DIRTY;
+ iput(ip);
+ }
+}
+
+
+/*
+ * txAllocPMap()
+ *
+ * function: allocate from persistent map;
+ *
+ * parameter:
+ * ipbmap -
+ * malock -
+ * xad list:
+ * pxd:
+ *
+ * maptype -
+ * allocate from persistent map;
+ * free from persistent map;
+ * (e.g., tmp file - free from working map at releae
+ * of last reference);
+ * free from persistent and working map;
+ *
+ * lsn - log sequence number;
+ */
+static void txAllocPMap(struct inode *ip, struct maplock * maplock,
+ struct tblock * tblk)
+{
+ struct inode *ipbmap = JFS_SBI(ip->i_sb)->ipbmap;
+ struct xdlistlock *xadlistlock;
+ xad_t *xad;
+ s64 xaddr;
+ int xlen;
+ struct pxd_lock *pxdlock;
+ struct xdlistlock *pxdlistlock;
+ pxd_t *pxd;
+ int n;
+
+ /*
+ * allocate from persistent map;
+ */
+ if (maplock->flag & mlckALLOCXADLIST) {
+ xadlistlock = (struct xdlistlock *) maplock;
+ xad = xadlistlock->xdlist;
+ for (n = 0; n < xadlistlock->count; n++, xad++) {
+ if (xad->flag & (XAD_NEW | XAD_EXTENDED)) {
+ xaddr = addressXAD(xad);
+ xlen = lengthXAD(xad);
+ dbUpdatePMap(ipbmap, FALSE, xaddr,
+ (s64) xlen, tblk);
+ xad->flag &= ~(XAD_NEW | XAD_EXTENDED);
+ jfs_info("allocPMap: xaddr:0x%lx xlen:%d",
+ (ulong) xaddr, xlen);
+ }
+ }
+ } else if (maplock->flag & mlckALLOCPXD) {
+ pxdlock = (struct pxd_lock *) maplock;
+ xaddr = addressPXD(&pxdlock->pxd);
+ xlen = lengthPXD(&pxdlock->pxd);
+ dbUpdatePMap(ipbmap, FALSE, xaddr, (s64) xlen, tblk);
+ jfs_info("allocPMap: xaddr:0x%lx xlen:%d", (ulong) xaddr, xlen);
+ } else { /* (maplock->flag & mlckALLOCPXDLIST) */
+
+ pxdlistlock = (struct xdlistlock *) maplock;
+ pxd = pxdlistlock->xdlist;
+ for (n = 0; n < pxdlistlock->count; n++, pxd++) {
+ xaddr = addressPXD(pxd);
+ xlen = lengthPXD(pxd);
+ dbUpdatePMap(ipbmap, FALSE, xaddr, (s64) xlen,
+ tblk);
+ jfs_info("allocPMap: xaddr:0x%lx xlen:%d",
+ (ulong) xaddr, xlen);
+ }
+ }
+}
+
+
+/*
+ * txFreeMap()
+ *
+ * function: free from persistent and/or working map;
+ *
+ * todo: optimization
+ */
+void txFreeMap(struct inode *ip,
+ struct maplock * maplock, struct tblock * tblk, int maptype)
+{
+ struct inode *ipbmap = JFS_SBI(ip->i_sb)->ipbmap;
+ struct xdlistlock *xadlistlock;
+ xad_t *xad;
+ s64 xaddr;
+ int xlen;
+ struct pxd_lock *pxdlock;
+ struct xdlistlock *pxdlistlock;
+ pxd_t *pxd;
+ int n;
+
+ jfs_info("txFreeMap: tblk:0x%p maplock:0x%p maptype:0x%x",
+ tblk, maplock, maptype);
+
+ /*
+ * free from persistent map;
+ */
+ if (maptype == COMMIT_PMAP || maptype == COMMIT_PWMAP) {
+ if (maplock->flag & mlckFREEXADLIST) {
+ xadlistlock = (struct xdlistlock *) maplock;
+ xad = xadlistlock->xdlist;
+ for (n = 0; n < xadlistlock->count; n++, xad++) {
+ if (!(xad->flag & XAD_NEW)) {
+ xaddr = addressXAD(xad);
+ xlen = lengthXAD(xad);
+ dbUpdatePMap(ipbmap, TRUE, xaddr,
+ (s64) xlen, tblk);
+ jfs_info("freePMap: xaddr:0x%lx "
+ "xlen:%d",
+ (ulong) xaddr, xlen);
+ }
+ }
+ } else if (maplock->flag & mlckFREEPXD) {
+ pxdlock = (struct pxd_lock *) maplock;
+ xaddr = addressPXD(&pxdlock->pxd);
+ xlen = lengthPXD(&pxdlock->pxd);
+ dbUpdatePMap(ipbmap, TRUE, xaddr, (s64) xlen,
+ tblk);
+ jfs_info("freePMap: xaddr:0x%lx xlen:%d",
+ (ulong) xaddr, xlen);
+ } else { /* (maplock->flag & mlckALLOCPXDLIST) */
+
+ pxdlistlock = (struct xdlistlock *) maplock;
+ pxd = pxdlistlock->xdlist;
+ for (n = 0; n < pxdlistlock->count; n++, pxd++) {
+ xaddr = addressPXD(pxd);
+ xlen = lengthPXD(pxd);
+ dbUpdatePMap(ipbmap, TRUE, xaddr,
+ (s64) xlen, tblk);
+ jfs_info("freePMap: xaddr:0x%lx xlen:%d",
+ (ulong) xaddr, xlen);
+ }
+ }
+ }
+
+ /*
+ * free from working map;
+ */
+ if (maptype == COMMIT_PWMAP || maptype == COMMIT_WMAP) {
+ if (maplock->flag & mlckFREEXADLIST) {
+ xadlistlock = (struct xdlistlock *) maplock;
+ xad = xadlistlock->xdlist;
+ for (n = 0; n < xadlistlock->count; n++, xad++) {
+ xaddr = addressXAD(xad);
+ xlen = lengthXAD(xad);
+ dbFree(ip, xaddr, (s64) xlen);
+ xad->flag = 0;
+ jfs_info("freeWMap: xaddr:0x%lx xlen:%d",
+ (ulong) xaddr, xlen);
+ }
+ } else if (maplock->flag & mlckFREEPXD) {
+ pxdlock = (struct pxd_lock *) maplock;
+ xaddr = addressPXD(&pxdlock->pxd);
+ xlen = lengthPXD(&pxdlock->pxd);
+ dbFree(ip, xaddr, (s64) xlen);
+ jfs_info("freeWMap: xaddr:0x%lx xlen:%d",
+ (ulong) xaddr, xlen);
+ } else { /* (maplock->flag & mlckFREEPXDLIST) */
+
+ pxdlistlock = (struct xdlistlock *) maplock;
+ pxd = pxdlistlock->xdlist;
+ for (n = 0; n < pxdlistlock->count; n++, pxd++) {
+ xaddr = addressPXD(pxd);
+ xlen = lengthPXD(pxd);
+ dbFree(ip, xaddr, (s64) xlen);
+ jfs_info("freeWMap: xaddr:0x%lx xlen:%d",
+ (ulong) xaddr, xlen);
+ }
+ }
+ }
+}
+
+
+/*
+ * txFreelock()
+ *
+ * function: remove tlock from inode anonymous locklist
+ */
+void txFreelock(struct inode *ip)
+{
+ struct jfs_inode_info *jfs_ip = JFS_IP(ip);
+ struct tlock *xtlck, *tlck;
+ lid_t xlid = 0, lid;
+
+ if (!jfs_ip->atlhead)
+ return;
+
+ xtlck = (struct tlock *) &jfs_ip->atlhead;
+
+ while ((lid = xtlck->next)) {
+ tlck = lid_to_tlock(lid);
+ if (tlck->flag & tlckFREELOCK) {
+ xtlck->next = tlck->next;
+ txLockFree(lid);
+ } else {
+ xtlck = tlck;
+ xlid = lid;
+ }
+ }
+
+ if (jfs_ip->atlhead)
+ jfs_ip->atltail = xlid;
+ else {
+ jfs_ip->atltail = 0;
+ /*
+ * If inode was on anon_list, remove it
+ */
+ TXN_LOCK();
+ list_del_init(&jfs_ip->anon_inode_list);
+ TXN_UNLOCK();
+ }
+}
+
+
+/*
+ * txAbort()
+ *
+ * function: abort tx before commit;
+ *
+ * frees line-locks and segment locks for all
+ * segments in comdata structure.
+ * Optionally sets state of file-system to FM_DIRTY in super-block.
+ * log age of page-frames in memory for which caller has
+ * are reset to 0 (to avoid logwarap).
+ */
+void txAbort(tid_t tid, int dirty)
+{
+ lid_t lid, next;
+ struct metapage *mp;
+ struct tblock *tblk = tid_to_tblock(tid);
+ struct tlock *tlck;
+
+ jfs_warn("txAbort: tid:%d dirty:0x%x", tid, dirty);
+
+ /*
+ * free tlocks of the transaction
+ */
+ for (lid = tblk->next; lid; lid = next) {
+ tlck = lid_to_tlock(lid);
+ next = tlck->next;
+ mp = tlck->mp;
+ JFS_IP(tlck->ip)->xtlid = 0;
+
+ if (mp) {
+ mp->lid = 0;
+
+ /*
+ * reset lsn of page to avoid logwarap:
+ *
+ * (page may have been previously committed by another
+ * transaction(s) but has not been paged, i.e.,
+ * it may be on logsync list even though it has not
+ * been logged for the current tx.)
+ */
+ if (mp->xflag & COMMIT_PAGE && mp->lsn)
+ LogSyncRelease(mp);
+ }
+ /* insert tlock at head of freelist */
+ TXN_LOCK();
+ txLockFree(lid);
+ TXN_UNLOCK();
+ }
+
+ /* caller will free the transaction block */
+
+ tblk->next = tblk->last = 0;
+
+ /*
+ * mark filesystem dirty
+ */
+ if (dirty)
+ jfs_error(tblk->sb, "txAbort");
+
+ return;
+}
+
+/*
+ * txLazyCommit(void)
+ *
+ * All transactions except those changing ipimap (COMMIT_FORCE) are
+ * processed by this routine. This insures that the inode and block
+ * allocation maps are updated in order. For synchronous transactions,
+ * let the user thread finish processing after txUpdateMap() is called.
+ */
+static void txLazyCommit(struct tblock * tblk)
+{
+ struct jfs_log *log;
+
+ while (((tblk->flag & tblkGC_READY) == 0) &&
+ ((tblk->flag & tblkGC_UNLOCKED) == 0)) {
+ /* We must have gotten ahead of the user thread
+ */
+ jfs_info("txLazyCommit: tblk 0x%p not unlocked", tblk);
+ yield();
+ }
+
+ jfs_info("txLazyCommit: processing tblk 0x%p", tblk);
+
+ txUpdateMap(tblk);
+
+ log = (struct jfs_log *) JFS_SBI(tblk->sb)->log;
+
+ spin_lock_irq(&log->gclock); // LOGGC_LOCK
+
+ tblk->flag |= tblkGC_COMMITTED;
+
+ if (tblk->flag & tblkGC_READY)
+ log->gcrtc--;
+
+ wake_up_all(&tblk->gcwait); // LOGGC_WAKEUP
+
+ /*
+ * Can't release log->gclock until we've tested tblk->flag
+ */
+ if (tblk->flag & tblkGC_LAZY) {
+ spin_unlock_irq(&log->gclock); // LOGGC_UNLOCK
+ txUnlock(tblk);
+ tblk->flag &= ~tblkGC_LAZY;
+ txEnd(tblk - TxBlock); /* Convert back to tid */
+ } else
+ spin_unlock_irq(&log->gclock); // LOGGC_UNLOCK
+
+ jfs_info("txLazyCommit: done: tblk = 0x%p", tblk);
+}
+
+/*
+ * jfs_lazycommit(void)
+ *
+ * To be run as a kernel daemon. If lbmIODone is called in an interrupt
+ * context, or where blocking is not wanted, this routine will process
+ * committed transactions from the unlock queue.
+ */
+int jfs_lazycommit(void *arg)
+{
+ int WorkDone;
+ struct tblock *tblk;
+ unsigned long flags;
+
+ lock_kernel();
+
+ daemonize();
+ current->tty = NULL;
+ strcpy(current->comm, "jfsCommit");
+
+ unlock_kernel();
+
+ jfsCommitTask = current;
+
+ spin_lock_irq(&current->sigmask_lock);
+ sigfillset(&current->blocked);
+ recalc_sigpending(current);
+ spin_unlock_irq(&current->sigmask_lock);
+
+ LAZY_LOCK_INIT();
+ TxAnchor.unlock_queue = TxAnchor.unlock_tail = 0;
+
+ complete(&jfsIOwait);
+
+ do {
+ DECLARE_WAITQUEUE(wq, current);
+
+ LAZY_LOCK(flags);
+restart:
+ WorkDone = 0;
+ while ((tblk = TxAnchor.unlock_queue)) {
+ /*
+ * We can't get ahead of user thread. Spinning is
+ * simpler than blocking/waking. We shouldn't spin
+ * very long, since user thread shouldn't be blocking
+ * between lmGroupCommit & txEnd.
+ */
+ WorkDone = 1;
+
+ /*
+ * Remove first transaction from queue
+ */
+ TxAnchor.unlock_queue = tblk->cqnext;
+ tblk->cqnext = 0;
+ if (TxAnchor.unlock_tail == tblk)
+ TxAnchor.unlock_tail = 0;
+
+ LAZY_UNLOCK(flags);
+ txLazyCommit(tblk);
+
+ /*
+ * We can be running indefinately if other processors
+ * are adding transactions to this list
+ */
+ cond_resched();
+ LAZY_LOCK(flags);
+ }
+
+ if (WorkDone)
+ goto restart;
+
+ add_wait_queue(&jfs_commit_thread_wait, &wq);
+ set_current_state(TASK_INTERRUPTIBLE);
+ LAZY_UNLOCK(flags);
+ schedule();
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&jfs_commit_thread_wait, &wq);
+ } while (!jfs_stop_threads);
+
+ if (TxAnchor.unlock_queue)
+ jfs_err("jfs_lazycommit being killed w/pending transactions!");
+ else
+ jfs_info("jfs_lazycommit being killed\n");
+ complete_and_exit(&jfsIOwait, 0);
+}
+
+void txLazyUnlock(struct tblock * tblk)
+{
+ unsigned long flags;
+
+ LAZY_LOCK(flags);
+
+ if (TxAnchor.unlock_tail)
+ TxAnchor.unlock_tail->cqnext = tblk;
+ else
+ TxAnchor.unlock_queue = tblk;
+ TxAnchor.unlock_tail = tblk;
+ tblk->cqnext = 0;
+ LAZY_UNLOCK(flags);
+ wake_up(&jfs_commit_thread_wait);
+}
+
+static void LogSyncRelease(struct metapage * mp)
+{
+ struct jfs_log *log = mp->log;
+
+ assert(atomic_read(&mp->nohomeok));
+ assert(log);
+ atomic_dec(&mp->nohomeok);
+
+ if (atomic_read(&mp->nohomeok))
+ return;
+
+ hold_metapage(mp, 0);
+
+ LOGSYNC_LOCK(log);
+ mp->log = NULL;
+ mp->lsn = 0;
+ mp->clsn = 0;
+ log->count--;
+ list_del_init(&mp->synclist);
+ LOGSYNC_UNLOCK(log);
+
+ release_metapage(mp);
+}
+
+/*
+ * txQuiesce
+ *
+ * Block all new transactions and push anonymous transactions to
+ * completion
+ *
+ * This does almost the same thing as jfs_sync below. We don't
+ * worry about deadlocking when TlocksLow is set, since we would
+ * expect jfs_sync to get us out of that jam.
+ */
+void txQuiesce(struct super_block *sb)
+{
+ struct inode *ip;
+ struct jfs_inode_info *jfs_ip;
+ struct jfs_log *log = JFS_SBI(sb)->log;
+ tid_t tid;
+
+ set_bit(log_QUIESCE, &log->flag);
+
+ TXN_LOCK();
+restart:
+ while (!list_empty(&TxAnchor.anon_list)) {
+ jfs_ip = list_entry(TxAnchor.anon_list.next,
+ struct jfs_inode_info,
+ anon_inode_list);
+ ip = jfs_ip->inode;
+
+ /*
+ * inode will be removed from anonymous list
+ * when it is committed
+ */
+ TXN_UNLOCK();
+ tid = txBegin(ip->i_sb, COMMIT_INODE | COMMIT_FORCE);
+ down(&jfs_ip->commit_sem);
+ txCommit(tid, 1, &ip, 0);
+ txEnd(tid);
+ up(&jfs_ip->commit_sem);
+ /*
+ * Just to be safe. I don't know how
+ * long we can run without blocking
+ */
+ cond_resched();
+ TXN_LOCK();
+ }
+
+ /*
+ * If jfs_sync is running in parallel, there could be some inodes
+ * on anon_list2. Let's check.
+ */
+ if (!list_empty(&TxAnchor.anon_list2)) {
+ list_splice(&TxAnchor.anon_list2, &TxAnchor.anon_list);
+ INIT_LIST_HEAD(&TxAnchor.anon_list2);
+ goto restart;
+ }
+ TXN_UNLOCK();
+
+ /*
+ * We may need to kick off the group commit
+ */
+ jfs_flush_journal(log, 0);
+}
+
+/*
+ * txResume()
+ *
+ * Allows transactions to start again following txQuiesce
+ */
+void txResume(struct super_block *sb)
+{
+ struct jfs_log *log = JFS_SBI(sb)->log;
+
+ clear_bit(log_QUIESCE, &log->flag);
+ TXN_WAKEUP(&log->syncwait);
+}
+
+/*
+ * jfs_sync(void)
+ *
+ * To be run as a kernel daemon. This is awakened when tlocks run low.
+ * We write any inodes that have anonymous tlocks so they will become
+ * available.
+ */
+int jfs_sync(void *arg)
+{
+ struct inode *ip;
+ struct jfs_inode_info *jfs_ip;
+ int rc;
+ tid_t tid;
+
+ lock_kernel();
+
+ daemonize();
+ current->tty = NULL;
+ strcpy(current->comm, "jfsSync");
+
+ unlock_kernel();
+
+ spin_lock_irq(&current->sigmask_lock);
+ sigfillset(&current->blocked);
+ recalc_sigpending(current);
+ spin_unlock_irq(&current->sigmask_lock);
+
+ complete(&jfsIOwait);
+
+ do {
+ DECLARE_WAITQUEUE(wq, current);
+ /*
+ * write each inode on the anonymous inode list
+ */
+ TXN_LOCK();
+ while (TxAnchor.TlocksLow && !list_empty(&TxAnchor.anon_list)) {
+ jfs_ip = list_entry(TxAnchor.anon_list.next,
+ struct jfs_inode_info,
+ anon_inode_list);
+ ip = jfs_ip->inode;
+
+ if (! igrab(ip)) {
+ /*
+ * Inode is being freed
+ */
+ list_del_init(&jfs_ip->anon_inode_list);
+ } else if (! down_trylock(&jfs_ip->commit_sem)) {
+ /*
+ * inode will be removed from anonymous list
+ * when it is committed
+ */
+ TXN_UNLOCK();
+ tid = txBegin(ip->i_sb, COMMIT_INODE);
+ rc = txCommit(tid, 1, &ip, 0);
+ txEnd(tid);
+ up(&jfs_ip->commit_sem);
+
+ iput(ip);
+ /*
+ * Just to be safe. I don't know how
+ * long we can run without blocking
+ */
+ cond_resched();
+ TXN_LOCK();
+ } else {
+ /* We can't get the commit semaphore. It may
+ * be held by a thread waiting for tlock's
+ * so let's not block here. Save it to
+ * put back on the anon_list.
+ */
+
+ /* Take off anon_list */
+ list_del(&jfs_ip->anon_inode_list);
+
+ /* Put on anon_list2 */
+ list_add(&jfs_ip->anon_inode_list,
+ &TxAnchor.anon_list2);
+
+ TXN_UNLOCK();
+ iput(ip);
+ TXN_LOCK();
+ }
+ }
+ /* Add anon_list2 back to anon_list */
+ if (!list_empty(&TxAnchor.anon_list2)) {
+ list_splice(&TxAnchor.anon_list2, &TxAnchor.anon_list);
+ INIT_LIST_HEAD(&TxAnchor.anon_list2);
+ }
+ add_wait_queue(&jfs_sync_thread_wait, &wq);
+ set_current_state(TASK_INTERRUPTIBLE);
+ TXN_UNLOCK();
+ schedule();
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&jfs_sync_thread_wait, &wq);
+ } while (!jfs_stop_threads);
+
+ jfs_info("jfs_sync being killed");
+ complete_and_exit(&jfsIOwait, 0);
+}
+
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_JFS_DEBUG)
+int jfs_txanchor_read(char *buffer, char **start, off_t offset, int length,
+ int *eof, void *data)
+{
+ int len = 0;
+ off_t begin;
+ char *freewait;
+ char *freelockwait;
+ char *lowlockwait;
+
+ freewait =
+ waitqueue_active(&TxAnchor.freewait) ? "active" : "empty";
+ freelockwait =
+ waitqueue_active(&TxAnchor.freelockwait) ? "active" : "empty";
+ lowlockwait =
+ waitqueue_active(&TxAnchor.lowlockwait) ? "active" : "empty";
+
+ len += sprintf(buffer,
+ "JFS TxAnchor\n"
+ "============\n"
+ "freetid = %d\n"
+ "freewait = %s\n"
+ "freelock = %d\n"
+ "freelockwait = %s\n"
+ "lowlockwait = %s\n"
+ "tlocksInUse = %d\n"
+ "TlocksLow = %d\n"
+ "unlock_queue = 0x%p\n"
+ "unlock_tail = 0x%p\n",
+ TxAnchor.freetid,
+ freewait,
+ TxAnchor.freelock,
+ freelockwait,
+ lowlockwait,
+ TxAnchor.tlocksInUse,
+ TxAnchor.TlocksLow,
+ TxAnchor.unlock_queue,
+ TxAnchor.unlock_tail);
+
+ begin = offset;
+ *start = buffer + begin;
+ len -= begin;
+
+ if (len > length)
+ len = length;
+ else
+ *eof = 1;
+
+ if (len < 0)
+ len = 0;
+
+ return len;
+}
+#endif
+
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_JFS_STATISTICS)
+int jfs_txstats_read(char *buffer, char **start, off_t offset, int length,
+ int *eof, void *data)
+{
+ int len = 0;
+ off_t begin;
+
+ len += sprintf(buffer,
+ "JFS TxStats\n"
+ "===========\n"
+ "calls to txBegin = %d\n"
+ "txBegin blocked by sync barrier = %d\n"
+ "txBegin blocked by tlocks low = %d\n"
+ "txBegin blocked by no free tid = %d\n"
+ "calls to txBeginAnon = %d\n"
+ "txBeginAnon blocked by sync barrier = %d\n"
+ "txBeginAnon blocked by tlocks low = %d\n"
+ "calls to txLockAlloc = %d\n"
+ "tLockAlloc blocked by no free lock = %d\n",
+ TxStat.txBegin,
+ TxStat.txBegin_barrier,
+ TxStat.txBegin_lockslow,
+ TxStat.txBegin_freetid,
+ TxStat.txBeginAnon,
+ TxStat.txBeginAnon_barrier,
+ TxStat.txBeginAnon_lockslow,
+ TxStat.txLockAlloc,
+ TxStat.txLockAlloc_freelock);
+
+ begin = offset;
+ *start = buffer + begin;
+ len -= begin;
+
+ if (len > length)
+ len = length;
+ else
+ *eof = 1;
+
+ if (len < 0)
+ len = 0;
+
+ return len;
+}
+#endif
diff --git a/fs/jfs/jfs_txnmgr.h b/fs/jfs/jfs_txnmgr.h
new file mode 100644
index 00000000000000..020c67ad2d6c4b
--- /dev/null
+++ b/fs/jfs/jfs_txnmgr.h
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2000-2004
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _H_JFS_TXNMGR
+#define _H_JFS_TXNMGR
+
+#include "jfs_logmgr.h"
+
+/*
+ * Hide implementation of TxBlock and TxLock
+ */
+#define tid_to_tblock(tid) (&TxBlock[tid])
+
+#define lid_to_tlock(lid) (&TxLock[lid])
+
+/*
+ * transaction block
+ */
+struct tblock {
+ /*
+ * tblock and jbuf_t common area: struct logsyncblk
+ *
+ * the following 5 fields are the same as struct logsyncblk
+ * which is common to tblock and jbuf to form logsynclist
+ */
+ u16 xflag; /* tx commit type */
+ u16 flag; /* tx commit state */
+ lid_t dummy; /* Must keep structures common */
+ s32 lsn; /* recovery lsn */
+ struct list_head synclist; /* logsynclist link */
+
+ /* lock management */
+ struct super_block *sb; /* super block */
+ lid_t next; /* index of first tlock of tid */
+ lid_t last; /* index of last tlock of tid */
+ wait_queue_head_t waitor; /* tids waiting on this tid */
+
+ /* log management */
+ u32 logtid; /* log transaction id */
+
+ /* commit management */
+ struct tblock *cqnext; /* commit queue link */
+ s32 clsn; /* commit lsn */
+ struct lbuf *bp;
+ s32 pn; /* commit record log page number */
+ s32 eor; /* commit record eor */
+ wait_queue_head_t gcwait; /* group commit event list:
+ * ready transactions wait on this
+ * event for group commit completion.
+ */
+ struct inode *ip; /* inode being created or deleted */
+};
+
+extern struct tblock *TxBlock; /* transaction block table */
+
+/* commit flags: tblk->xflag */
+#define COMMIT_SYNC 0x0001 /* synchronous commit */
+#define COMMIT_FORCE 0x0002 /* force pageout at end of commit */
+#define COMMIT_FLUSH 0x0004 /* init flush at end of commit */
+#define COMMIT_MAP 0x00f0
+#define COMMIT_PMAP 0x0010 /* update pmap */
+#define COMMIT_WMAP 0x0020 /* update wmap */
+#define COMMIT_PWMAP 0x0040 /* update pwmap */
+#define COMMIT_FREE 0x0f00
+#define COMMIT_DELETE 0x0100 /* inode delete */
+#define COMMIT_TRUNCATE 0x0200 /* file truncation */
+#define COMMIT_CREATE 0x0400 /* inode create */
+#define COMMIT_LAZY 0x0800 /* lazy commit */
+#define COMMIT_PAGE 0x1000 /* Identifies element as metapage */
+#define COMMIT_INODE 0x2000 /* Identifies element as inode */
+
+/* group commit flags tblk->flag: see jfs_logmgr.h */
+
+/*
+ * transaction lock
+ */
+struct tlock {
+ lid_t next; /* 2: index next lockword on tid locklist
+ * next lockword on freelist
+ */
+ tid_t tid; /* 2: transaction id holding lock */
+
+ u16 flag; /* 2: lock control */
+ u16 type; /* 2: log type */
+
+ struct metapage *mp; /* 4/8: object page buffer locked */
+ struct inode *ip; /* 4/8: object */
+ /* (16) */
+
+ s16 lock[24]; /* 48: overlay area */
+}; /* (64) */
+
+extern struct tlock *TxLock; /* transaction lock table */
+
+/*
+ * tlock flag
+ */
+/* txLock state */
+#define tlckPAGELOCK 0x8000
+#define tlckINODELOCK 0x4000
+#define tlckLINELOCK 0x2000
+#define tlckINLINELOCK 0x1000
+/* lmLog state */
+#define tlckLOG 0x0800
+/* updateMap state */
+#define tlckUPDATEMAP 0x0080
+/* freeLock state */
+#define tlckFREELOCK 0x0008
+#define tlckWRITEPAGE 0x0004
+#define tlckFREEPAGE 0x0002
+
+/*
+ * tlock type
+ */
+#define tlckTYPE 0xfe00
+#define tlckINODE 0x8000
+#define tlckXTREE 0x4000
+#define tlckDTREE 0x2000
+#define tlckMAP 0x1000
+#define tlckEA 0x0800
+#define tlckACL 0x0400
+#define tlckDATA 0x0200
+#define tlckBTROOT 0x0100
+
+#define tlckOPERATION 0x00ff
+#define tlckGROW 0x0001 /* file grow */
+#define tlckREMOVE 0x0002 /* file delete */
+#define tlckTRUNCATE 0x0004 /* file truncate */
+#define tlckRELOCATE 0x0008 /* file/directory relocate */
+#define tlckENTRY 0x0001 /* directory insert/delete */
+#define tlckEXTEND 0x0002 /* directory extend in-line */
+#define tlckSPLIT 0x0010 /* splited page */
+#define tlckNEW 0x0020 /* new page from split */
+#define tlckFREE 0x0040 /* free page */
+#define tlckRELINK 0x0080 /* update sibling pointer */
+
+/*
+ * linelock for lmLog()
+ *
+ * note: linelock and its variations are overlaid
+ * at tlock.lock: watch for alignment;
+ */
+struct lv {
+ u8 offset; /* 1: */
+ u8 length; /* 1: */
+}; /* (2) */
+
+#define TLOCKSHORT 20
+#define TLOCKLONG 28
+
+struct linelock {
+ lid_t next; /* 2: next linelock */
+
+ s8 maxcnt; /* 1: */
+ s8 index; /* 1: */
+
+ u16 flag; /* 2: */
+ u8 type; /* 1: */
+ u8 l2linesize; /* 1: log2 of linesize */
+ /* (8) */
+
+ struct lv lv[20]; /* 40: */
+}; /* (48) */
+
+#define dt_lock linelock
+
+struct xtlock {
+ lid_t next; /* 2: */
+
+ s8 maxcnt; /* 1: */
+ s8 index; /* 1: */
+
+ u16 flag; /* 2: */
+ u8 type; /* 1: */
+ u8 l2linesize; /* 1: log2 of linesize */
+ /* (8) */
+
+ struct lv header; /* 2: */
+ struct lv lwm; /* 2: low water mark */
+ struct lv hwm; /* 2: high water mark */
+ struct lv twm; /* 2: */
+ /* (16) */
+
+ s32 pxdlock[8]; /* 32: */
+}; /* (48) */
+
+
+/*
+ * maplock for txUpdateMap()
+ *
+ * note: maplock and its variations are overlaid
+ * at tlock.lock/linelock: watch for alignment;
+ * N.B. next field may be set by linelock, and should not
+ * be modified by maplock;
+ * N.B. index of the first pxdlock specifies index of next
+ * free maplock (i.e., number of maplock) in the tlock;
+ */
+struct maplock {
+ lid_t next; /* 2: */
+
+ u8 maxcnt; /* 2: */
+ u8 index; /* 2: next free maplock index */
+
+ u16 flag; /* 2: */
+ u8 type; /* 1: */
+ u8 count; /* 1: number of pxd/xad */
+ /* (8) */
+
+ pxd_t pxd; /* 8: */
+}; /* (16): */
+
+/* maplock flag */
+#define mlckALLOC 0x00f0
+#define mlckALLOCXADLIST 0x0080
+#define mlckALLOCPXDLIST 0x0040
+#define mlckALLOCXAD 0x0020
+#define mlckALLOCPXD 0x0010
+#define mlckFREE 0x000f
+#define mlckFREEXADLIST 0x0008
+#define mlckFREEPXDLIST 0x0004
+#define mlckFREEXAD 0x0002
+#define mlckFREEPXD 0x0001
+
+#define pxd_lock maplock
+
+struct xdlistlock {
+ lid_t next; /* 2: */
+
+ u8 maxcnt; /* 2: */
+ u8 index; /* 2: */
+
+ u16 flag; /* 2: */
+ u8 type; /* 1: */
+ u8 count; /* 1: number of pxd/xad */
+ /* (8) */
+
+ /*
+ * We need xdlist to be 64 bits (8 bytes), regardless of
+ * whether void * is 32 or 64 bits
+ */
+ union {
+ void *_xdlist; /* pxd/xad list */
+ s64 pad; /* 8: Force 64-bit xdlist size */
+ } union64;
+}; /* (16): */
+
+#define xdlist union64._xdlist
+
+/*
+ * commit
+ *
+ * parameter to the commit manager routines
+ */
+struct commit {
+ tid_t tid; /* tid = index of tblock */
+ int flag; /* flags */
+ struct jfs_log *log; /* log */
+ struct super_block *sb; /* superblock */
+
+ int nip; /* number of entries in iplist */
+ struct inode **iplist; /* list of pointers to inodes */
+
+ /* log record descriptor on 64-bit boundary */
+ struct lrd lrd; /* : log record descriptor */
+};
+
+/*
+ * external declarations
+ */
+extern struct tlock *txLock(tid_t tid, struct inode *ip, struct metapage *mp,
+ int flag);
+
+extern struct tlock *txMaplock(tid_t tid, struct inode *ip, int flag);
+
+extern int txCommit(tid_t tid, int nip, struct inode **iplist, int flag);
+
+extern tid_t txBegin(struct super_block *sb, int flag);
+
+extern void txBeginAnon(struct super_block *sb);
+
+extern void txEnd(tid_t tid);
+
+extern void txAbort(tid_t tid, int dirty);
+
+extern struct linelock *txLinelock(struct linelock * tlock);
+
+extern void txFreeMap(struct inode *ip, struct maplock * maplock,
+ struct tblock * tblk, int maptype);
+
+extern void txEA(tid_t tid, struct inode *ip, dxd_t * oldea, dxd_t * newea);
+
+extern void txFreelock(struct inode *ip);
+
+extern int lmLog(struct jfs_log * log, struct tblock * tblk, struct lrd * lrd,
+ struct tlock * tlck);
+
+extern void txQuiesce(struct super_block *sb);
+
+extern void txResume(struct super_block *sb);
+#endif /* _H_JFS_TXNMGR */
diff --git a/fs/jfs/jfs_types.h b/fs/jfs/jfs_types.h
new file mode 100644
index 00000000000000..d85eaf22d3c01f
--- /dev/null
+++ b/fs/jfs/jfs_types.h
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2000-2004
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _H_JFS_TYPES
+#define _H_JFS_TYPES
+
+/*
+ * jfs_types.h:
+ *
+ * basic type/utility definitions
+ *
+ * note: this header file must be the 1st include file
+ * of JFS include list in all JFS .c file.
+ */
+
+#include <linux/types.h>
+#include <linux/nls.h>
+
+/*
+ * transaction and lock id's
+ *
+ * Don't change these without carefully considering the impact on the
+ * size and alignment of all of the linelock variants
+ */
+typedef u16 tid_t;
+typedef u16 lid_t;
+
+/*
+ * Almost identical to Linux's timespec, but not quite
+ */
+struct timestruc_t {
+ u32 tv_sec;
+ u32 tv_nsec;
+};
+
+/*
+ * handy
+ */
+
+#define LEFTMOSTONE 0x80000000
+#define HIGHORDER 0x80000000u /* high order bit on */
+#define ONES 0xffffffffu /* all bit on */
+
+typedef int boolean_t;
+#define TRUE 1
+#define FALSE 0
+
+/*
+ * logical xd (lxd)
+ */
+typedef struct {
+ unsigned len:24;
+ unsigned off1:8;
+ u32 off2;
+} lxd_t;
+
+/* lxd_t field construction */
+#define LXDlength(lxd, length32) ( (lxd)->len = length32 )
+#define LXDoffset(lxd, offset64)\
+{\
+ (lxd)->off1 = ((s64)offset64) >> 32;\
+ (lxd)->off2 = (offset64) & 0xffffffff;\
+}
+
+/* lxd_t field extraction */
+#define lengthLXD(lxd) ( (lxd)->len )
+#define offsetLXD(lxd)\
+ ( ((s64)((lxd)->off1)) << 32 | (lxd)->off2 )
+
+/* lxd list */
+struct lxdlist {
+ s16 maxnlxd;
+ s16 nlxd;
+ lxd_t *lxd;
+};
+
+/*
+ * physical xd (pxd)
+ */
+typedef struct {
+ unsigned len:24;
+ unsigned addr1:8;
+ u32 addr2;
+} pxd_t;
+
+/* xd_t field construction */
+
+#define PXDlength(pxd, length32) ((pxd)->len = __cpu_to_le24(length32))
+#define PXDaddress(pxd, address64)\
+{\
+ (pxd)->addr1 = ((s64)address64) >> 32;\
+ (pxd)->addr2 = __cpu_to_le32((address64) & 0xffffffff);\
+}
+
+/* xd_t field extraction */
+#define lengthPXD(pxd) __le24_to_cpu((pxd)->len)
+#define addressPXD(pxd)\
+ ( ((s64)((pxd)->addr1)) << 32 | __le32_to_cpu((pxd)->addr2))
+
+#define MAXTREEHEIGHT 8
+/* pxd list */
+struct pxdlist {
+ s16 maxnpxd;
+ s16 npxd;
+ pxd_t pxd[MAXTREEHEIGHT];
+};
+
+
+/*
+ * data extent descriptor (dxd)
+ */
+typedef struct {
+ unsigned flag:8; /* 1: flags */
+ unsigned rsrvd:24; /* 3: */
+ u32 size; /* 4: size in byte */
+ unsigned len:24; /* 3: length in unit of fsblksize */
+ unsigned addr1:8; /* 1: address in unit of fsblksize */
+ u32 addr2; /* 4: address in unit of fsblksize */
+} dxd_t; /* - 16 - */
+
+/* dxd_t flags */
+#define DXD_INDEX 0x80 /* B+-tree index */
+#define DXD_INLINE 0x40 /* in-line data extent */
+#define DXD_EXTENT 0x20 /* out-of-line single extent */
+#define DXD_FILE 0x10 /* out-of-line file (inode) */
+#define DXD_CORRUPT 0x08 /* Inconsistency detected */
+
+/* dxd_t field construction
+ * Conveniently, the PXD macros work for DXD
+ */
+#define DXDlength PXDlength
+#define DXDaddress PXDaddress
+#define lengthDXD lengthPXD
+#define addressDXD addressPXD
+#define DXDsize(dxd, size32) ((dxd)->size = cpu_to_le32(size32))
+#define sizeDXD(dxd) le32_to_cpu((dxd)->size)
+
+/*
+ * directory entry argument
+ */
+struct component_name {
+ int namlen;
+ wchar_t *name;
+};
+
+
+/*
+ * DASD limit information - stored in directory inode
+ */
+struct dasd {
+ u8 thresh; /* Alert Threshold (in percent) */
+ u8 delta; /* Alert Threshold delta (in percent) */
+ u8 rsrvd1;
+ u8 limit_hi; /* DASD limit (in logical blocks) */
+ u32 limit_lo; /* DASD limit (in logical blocks) */
+ u8 rsrvd2[3];
+ u8 used_hi; /* DASD usage (in logical blocks) */
+ u32 used_lo; /* DASD usage (in logical blocks) */
+};
+
+#define DASDLIMIT(dasdp) \
+ (((u64)((dasdp)->limit_hi) << 32) + __le32_to_cpu((dasdp)->limit_lo))
+#define setDASDLIMIT(dasdp, limit)\
+{\
+ (dasdp)->limit_hi = ((u64)limit) >> 32;\
+ (dasdp)->limit_lo = __cpu_to_le32(limit);\
+}
+#define DASDUSED(dasdp) \
+ (((u64)((dasdp)->used_hi) << 32) + __le32_to_cpu((dasdp)->used_lo))
+#define setDASDUSED(dasdp, used)\
+{\
+ (dasdp)->used_hi = ((u64)used) >> 32;\
+ (dasdp)->used_lo = __cpu_to_le32(used);\
+}
+
+#endif /* !_H_JFS_TYPES */
diff --git a/fs/jfs/jfs_umount.c b/fs/jfs/jfs_umount.c
new file mode 100644
index 00000000000000..c9fa767198fc17
--- /dev/null
+++ b/fs/jfs/jfs_umount.c
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2000-2002
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * jfs_umount.c
+ *
+ * note: file system in transition to aggregate/fileset:
+ * (ref. jfs_mount.c)
+ *
+ * file system unmount is interpreted as mount of the single/only
+ * fileset in the aggregate and, if unmount of the last fileset,
+ * as unmount of the aggerate;
+ */
+
+#include <linux/fs.h>
+#include "jfs_incore.h"
+#include "jfs_filsys.h"
+#include "jfs_superblock.h"
+#include "jfs_dmap.h"
+#include "jfs_imap.h"
+#include "jfs_metapage.h"
+#include "jfs_debug.h"
+
+/*
+ * NAME: jfs_umount(vfsp, flags, crp)
+ *
+ * FUNCTION: vfs_umount()
+ *
+ * PARAMETERS: vfsp - virtual file system pointer
+ * flags - unmount for shutdown
+ * crp - credential
+ *
+ * RETURN : EBUSY - device has open files
+ */
+int jfs_umount(struct super_block *sb)
+{
+ struct jfs_sb_info *sbi = JFS_SBI(sb);
+ struct inode *ipbmap = sbi->ipbmap;
+ struct inode *ipimap = sbi->ipimap;
+ struct inode *ipaimap = sbi->ipaimap;
+ struct inode *ipaimap2 = sbi->ipaimap2;
+ struct jfs_log *log;
+ int rc = 0;
+
+ jfs_info("UnMount JFS: sb:0x%p", sb);
+
+ /*
+ * update superblock and close log
+ *
+ * if mounted read-write and log based recovery was enabled
+ */
+ if ((log = sbi->log))
+ /*
+ * Wait for outstanding transactions to be written to log:
+ */
+ jfs_flush_journal(log, 2);
+
+ /*
+ * close fileset inode allocation map (aka fileset inode)
+ */
+ diUnmount(ipimap, 0);
+
+ diFreeSpecial(ipimap);
+ sbi->ipimap = NULL;
+
+ /*
+ * close secondary aggregate inode allocation map
+ */
+ ipaimap2 = sbi->ipaimap2;
+ if (ipaimap2) {
+ diUnmount(ipaimap2, 0);
+ diFreeSpecial(ipaimap2);
+ sbi->ipaimap2 = NULL;
+ }
+
+ /*
+ * close aggregate inode allocation map
+ */
+ ipaimap = sbi->ipaimap;
+ diUnmount(ipaimap, 0);
+ diFreeSpecial(ipaimap);
+ sbi->ipaimap = NULL;
+
+ /*
+ * close aggregate block allocation map
+ */
+ dbUnmount(ipbmap, 0);
+
+ diFreeSpecial(ipbmap);
+ sbi->ipimap = NULL;
+
+ /*
+ * Make sure all metadata makes it to disk before we mark
+ * the superblock as clean
+ */
+ fsync_inode_data_buffers(sb->s_bdev->bd_inode);
+
+ /*
+ * ensure all file system file pages are propagated to their
+ * home blocks on disk (and their in-memory buffer pages are
+ * invalidated) BEFORE updating file system superblock state
+ * (to signify file system is unmounted cleanly, and thus in
+ * consistent state) and log superblock active file system
+ * list (to signify skip logredo()).
+ */
+ if (log) { /* log = NULL if read-only mount */
+ rc = updateSuper(sb, FM_CLEAN);
+
+ /*
+ * close log:
+ *
+ * remove file system from log active file system list.
+ */
+ rc = lmLogClose(sb, log);
+ }
+ jfs_info("UnMount JFS Complete: rc = %d", rc);
+ return rc;
+}
+
+
+int jfs_umount_rw(struct super_block *sb)
+{
+ struct jfs_sb_info *sbi = JFS_SBI(sb);
+ struct jfs_log *log = sbi->log;
+
+ if (!log)
+ return 0;
+
+ /*
+ * close log:
+ *
+ * remove file system from log active file system list.
+ */
+ jfs_flush_journal(log, 2);
+
+ /*
+ * Make sure all metadata makes it to disk
+ */
+ dbSync(sbi->ipbmap);
+ diSync(sbi->ipimap);
+ fsync_inode_data_buffers(sb->s_bdev->bd_inode);
+
+ updateSuper(sb, FM_CLEAN);
+ sbi->log = NULL;
+
+ return lmLogClose(sb, log);
+}
diff --git a/fs/jfs/jfs_unicode.c b/fs/jfs/jfs_unicode.c
new file mode 100644
index 00000000000000..ecb0ecc53f3de5
--- /dev/null
+++ b/fs/jfs/jfs_unicode.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2000-2005
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include "jfs_incore.h"
+#include "jfs_filsys.h"
+#include "jfs_unicode.h"
+#include "jfs_debug.h"
+
+/*
+ * NAME: jfs_strfromUCS()
+ *
+ * FUNCTION: Convert little-endian unicode string to character string
+ *
+ */
+int jfs_strfromUCS_le(char *to, const wchar_t * from, /* LITTLE ENDIAN */
+ int len, struct nls_table *codepage)
+{
+ int i;
+ int outlen = 0;
+ static int warn_again = 5; /* Only warn up to 5 times total */
+ int warn = !!warn_again; /* once per string */
+
+ if (codepage) {
+ for (i = 0; (i < len) && from[i]; i++) {
+ int charlen;
+ charlen =
+ codepage->uni2char(le16_to_cpu(from[i]),
+ &to[outlen],
+ NLS_MAX_CHARSET_SIZE);
+ if (charlen > 0)
+ outlen += charlen;
+ else
+ to[outlen++] = '?';
+ }
+ } else {
+ for (i = 0; (i < len) && from[i]; i++) {
+ if (le16_to_cpu(from[i]) & 0xff00) {
+ if (warn) {
+ warn--;
+ warn_again--;
+ printk(KERN_ERR
+ "non-latin1 character 0x%x found in JFS file name\n",
+ le16_to_cpu(from[i]));
+ printk(KERN_ERR
+ "mount with iocharset=utf8 to access\n");
+ }
+ to[i] = '?';
+ }
+ else
+ to[i] = (char) (le16_to_cpu(from[i]));
+ }
+ outlen = i;
+
+ }
+ to[outlen] = 0;
+ return outlen;
+}
+
+/*
+ * NAME: jfs_strtoUCS()
+ *
+ * FUNCTION: Convert character string to unicode string
+ *
+ */
+static int jfs_strtoUCS(wchar_t * to, const unsigned char *from, int len,
+ struct nls_table *codepage)
+{
+ int charlen;
+ int i;
+
+ if (codepage) {
+ for (i = 0; len && *from; i++, from += charlen, len -= charlen)
+ {
+ charlen = codepage->char2uni(from, len, &to[i]);
+ if (charlen < 1) {
+ jfs_err("jfs_strtoUCS: char2uni returned %d.",
+ charlen);
+ jfs_err("charset = %s, char = 0x%x",
+ codepage->charset, *from);
+ return charlen;
+ }
+ }
+ } else {
+ for (i = 0; (i < len) && from[i]; i++)
+ to[i] = (wchar_t) from[i];
+ }
+
+ to[i] = 0;
+ return i;
+}
+
+/*
+ * NAME: get_UCSname()
+ *
+ * FUNCTION: Allocate and translate to unicode string
+ *
+ */
+int get_UCSname(struct component_name * uniName, struct dentry *dentry)
+{
+ struct nls_table *nls_tab = JFS_SBI(dentry->d_sb)->nls_tab;
+ int length = dentry->d_name.len;
+
+ if (length > JFS_NAME_MAX)
+ return -ENAMETOOLONG;
+
+ uniName->name =
+ kmalloc((length + 1) * sizeof(wchar_t), GFP_NOFS);
+
+ if (uniName->name == NULL)
+ return -ENOSPC;
+
+ uniName->namlen = jfs_strtoUCS(uniName->name, dentry->d_name.name,
+ length, nls_tab);
+
+ if (uniName->namlen < 0) {
+ kfree(uniName->name);
+ return uniName->namlen;
+ }
+
+ return 0;
+}
diff --git a/fs/jfs/jfs_unicode.h b/fs/jfs/jfs_unicode.h
new file mode 100644
index 00000000000000..e90c788c2b1ad2
--- /dev/null
+++ b/fs/jfs/jfs_unicode.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2000-2002
+ * Portions Copyright (C) Christoph Hellwig, 2001-2002
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _H_JFS_UNICODE
+#define _H_JFS_UNICODE
+
+#include <asm/byteorder.h>
+#include "jfs_types.h"
+
+typedef struct {
+ wchar_t start;
+ wchar_t end;
+ signed char *table;
+} UNICASERANGE;
+
+extern signed char UniUpperTable[512];
+extern UNICASERANGE UniUpperRange[];
+extern int get_UCSname(struct component_name *, struct dentry *);
+extern int jfs_strfromUCS_le(char *, const wchar_t *, int, struct nls_table *);
+
+#define free_UCSname(COMP) kfree((COMP)->name)
+
+/*
+ * UniStrcpy: Copy a string
+ */
+static inline wchar_t *UniStrcpy(wchar_t * ucs1, const wchar_t * ucs2)
+{
+ wchar_t *anchor = ucs1; /* save the start of result string */
+
+ while ((*ucs1++ = *ucs2++));
+ return anchor;
+}
+
+
+
+/*
+ * UniStrncpy: Copy length limited string with pad
+ */
+static inline wchar_t *UniStrncpy(wchar_t * ucs1, const wchar_t * ucs2,
+ size_t n)
+{
+ wchar_t *anchor = ucs1;
+
+ while (n-- && *ucs2) /* Copy the strings */
+ *ucs1++ = *ucs2++;
+
+ n++;
+ while (n--) /* Pad with nulls */
+ *ucs1++ = 0;
+ return anchor;
+}
+
+/*
+ * UniStrncmp_le: Compare length limited string - native to little-endian
+ */
+static inline int UniStrncmp_le(const wchar_t * ucs1, const wchar_t * ucs2,
+ size_t n)
+{
+ if (!n)
+ return 0; /* Null strings are equal */
+ while ((*ucs1 == __le16_to_cpu(*ucs2)) && *ucs1 && --n) {
+ ucs1++;
+ ucs2++;
+ }
+ return (int) *ucs1 - (int) __le16_to_cpu(*ucs2);
+}
+
+/*
+ * UniStrncpy_le: Copy length limited string with pad to little-endian
+ */
+static inline wchar_t *UniStrncpy_le(wchar_t * ucs1, const wchar_t * ucs2,
+ size_t n)
+{
+ wchar_t *anchor = ucs1;
+
+ while (n-- && *ucs2) /* Copy the strings */
+ *ucs1++ = __le16_to_cpu(*ucs2++);
+
+ n++;
+ while (n--) /* Pad with nulls */
+ *ucs1++ = 0;
+ return anchor;
+}
+
+
+/*
+ * UniToupper: Convert a unicode character to upper case
+ */
+static inline wchar_t UniToupper(wchar_t uc)
+{
+ UNICASERANGE *rp;
+
+ if (uc < sizeof(UniUpperTable)) { /* Latin characters */
+ return uc + UniUpperTable[uc]; /* Use base tables */
+ } else {
+ rp = UniUpperRange; /* Use range tables */
+ while (rp->start) {
+ if (uc < rp->start) /* Before start of range */
+ return uc; /* Uppercase = input */
+ if (uc <= rp->end) /* In range */
+ return uc + rp->table[uc - rp->start];
+ rp++; /* Try next range */
+ }
+ }
+ return uc; /* Past last range */
+}
+
+
+/*
+ * UniStrupr: Upper case a unicode string
+ */
+static inline wchar_t *UniStrupr(wchar_t * upin)
+{
+ wchar_t *up;
+
+ up = upin;
+ while (*up) { /* For all characters */
+ *up = UniToupper(*up);
+ up++;
+ }
+ return upin; /* Return input pointer */
+}
+
+#endif /* !_H_JFS_UNICODE */
diff --git a/fs/jfs/jfs_uniupr.c b/fs/jfs/jfs_uniupr.c
new file mode 100644
index 00000000000000..af63d97bb1af52
--- /dev/null
+++ b/fs/jfs/jfs_uniupr.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2000-2002
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/fs.h>
+#include "jfs_unicode.h"
+
+/*
+ * Latin upper case
+ */
+signed char UniUpperTable[512] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 030-03f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 040-04f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 050-05f */
+ 0,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32, /* 060-06f */
+ -32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32, 0, 0, 0, 0, 0, /* 070-07f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 080-08f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 090-09f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0a0-0af */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0b0-0bf */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0c0-0cf */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0d0-0df */
+ -32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32, /* 0e0-0ef */
+ -32,-32,-32,-32,-32,-32,-32, 0,-32,-32,-32,-32,-32,-32,-32,121, /* 0f0-0ff */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 100-10f */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 110-11f */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 120-12f */
+ 0, 0, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 130-13f */
+ -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, /* 140-14f */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 150-15f */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 160-16f */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 170-17f */
+ 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, /* 180-18f */
+ 0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, /* 190-19f */
+ 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, /* 1a0-1af */
+ -1, 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, /* 1b0-1bf */
+ 0, 0, 0, 0, 0, -1, -2, 0, -1, -2, 0, -1, -2, 0, -1, 0, /* 1c0-1cf */
+ -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1,-79, 0, -1, /* 1d0-1df */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e0-1ef */
+ 0, 0, -1, -2, 0, -1, 0, 0, 0, -1, 0, -1, 0, -1, 0, -1, /* 1f0-1ff */
+};
+
+/* Upper case range - Greek */
+static signed char UniCaseRangeU03a0[47] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-38,-37,-37,-37, /* 3a0-3af */
+ 0,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32, /* 3b0-3bf */
+ -32,-32,-31,-32,-32,-32,-32,-32,-32,-32,-32,-32,-64,-63,-63,
+};
+
+/* Upper case range - Cyrillic */
+static signed char UniCaseRangeU0430[48] = {
+ -32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32, /* 430-43f */
+ -32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32, /* 440-44f */
+ 0,-80,-80,-80,-80,-80,-80,-80,-80,-80,-80,-80,-80, 0,-80,-80, /* 450-45f */
+};
+
+/* Upper case range - Extended cyrillic */
+static signed char UniCaseRangeU0490[61] = {
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 490-49f */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4a0-4af */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4b0-4bf */
+ 0, 0, -1, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1,
+};
+
+/* Upper case range - Extended latin and greek */
+static signed char UniCaseRangeU1e00[509] = {
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e00-1e0f */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e10-1e1f */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e20-1e2f */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e30-1e3f */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e40-1e4f */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e50-1e5f */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e60-1e6f */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e70-1e7f */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e80-1e8f */
+ 0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0,-59, 0, -1, 0, -1, /* 1e90-1e9f */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ea0-1eaf */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1eb0-1ebf */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ec0-1ecf */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ed0-1edf */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ee0-1eef */
+ 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, /* 1ef0-1eff */
+ 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f00-1f0f */
+ 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f10-1f1f */
+ 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f20-1f2f */
+ 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f30-1f3f */
+ 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f40-1f4f */
+ 0, 8, 0, 8, 0, 8, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f50-1f5f */
+ 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f60-1f6f */
+ 74, 74, 86, 86, 86, 86,100,100, 0, 0,112,112,126,126, 0, 0, /* 1f70-1f7f */
+ 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f80-1f8f */
+ 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f90-1f9f */
+ 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fa0-1faf */
+ 8, 8, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fb0-1fbf */
+ 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fc0-1fcf */
+ 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fd0-1fdf */
+ 8, 8, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fe0-1fef */
+ 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+/* Upper case range - Wide latin */
+static signed char UniCaseRangeUff40[27] = {
+ 0,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32, /* ff40-ff4f */
+ -32,-32,-32,-32,-32,-32,-32,-32,-32,-32,-32,
+};
+
+/*
+ * Upper Case Range
+ */
+UNICASERANGE UniUpperRange[] = {
+ { 0x03a0, 0x03ce, UniCaseRangeU03a0 },
+ { 0x0430, 0x045f, UniCaseRangeU0430 },
+ { 0x0490, 0x04cc, UniCaseRangeU0490 },
+ { 0x1e00, 0x1ffc, UniCaseRangeU1e00 },
+ { 0xff40, 0xff5a, UniCaseRangeUff40 },
+ { 0, 0, 0 }
+};
diff --git a/fs/jfs/jfs_xattr.h b/fs/jfs/jfs_xattr.h
new file mode 100644
index 00000000000000..ff32d0b7805dae
--- /dev/null
+++ b/fs/jfs/jfs_xattr.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2000-2002
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef H_JFS_XATTR
+#define H_JFS_XATTR
+
+/*
+ * jfs_ea_list describe the on-disk format of the extended attributes.
+ * I know the null-terminator is redundant since namelen is stored, but
+ * I am maintaining compatibility with OS/2 where possible.
+ */
+struct jfs_ea {
+ u8 flag; /* Unused? */
+ u8 namelen; /* Length of name */
+ u16 valuelen; /* Length of value */
+ char name[0]; /* Attribute name (includes null-terminator) */
+}; /* Value immediately follows name */
+
+struct jfs_ea_list {
+ u32 size; /* overall size */
+ struct jfs_ea ea[0]; /* Variable length list */
+};
+
+/* Macros for defining maxiumum number of bytes supported for EAs */
+#define MAXEASIZE 65535
+#define MAXEALISTSIZE MAXEASIZE
+
+/*
+ * some macros for dealing with variable length EA lists.
+ */
+#define EA_SIZE(ea) \
+ (sizeof (struct jfs_ea) + (ea)->namelen + 1 + \
+ le16_to_cpu((ea)->valuelen))
+#define NEXT_EA(ea) ((struct jfs_ea *) (((char *) (ea)) + (EA_SIZE (ea))))
+#define FIRST_EA(ealist) ((ealist)->ea)
+#define EALIST_SIZE(ealist) le32_to_cpu((ealist)->size)
+#define END_EALIST(ealist) \
+ ((struct jfs_ea *) (((char *) (ealist)) + EALIST_SIZE(ealist)))
+
+extern int __jfs_setxattr(struct inode *, const char *, void *, size_t, int);
+extern int jfs_setxattr(struct dentry *, const char *, void *, size_t, int);
+extern ssize_t __jfs_getxattr(struct inode *, const char *, void *, size_t);
+extern ssize_t jfs_getxattr(struct dentry *, const char *, void *, size_t);
+extern ssize_t jfs_listxattr(struct dentry *, char *, size_t);
+extern int jfs_removexattr(struct dentry *, const char *);
+
+#endif /* H_JFS_XATTR */
diff --git a/fs/jfs/jfs_xtree.c b/fs/jfs/jfs_xtree.c
new file mode 100644
index 00000000000000..3f485327298de6
--- /dev/null
+++ b/fs/jfs/jfs_xtree.c
@@ -0,0 +1,4458 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2000-2004
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/*
+ * jfs_xtree.c: extent allocation descriptor B+-tree manager
+ */
+
+#include <linux/fs.h>
+#include "jfs_incore.h"
+#include "jfs_filsys.h"
+#include "jfs_metapage.h"
+#include "jfs_dmap.h"
+#include "jfs_dinode.h"
+#include "jfs_superblock.h"
+#include "jfs_debug.h"
+
+/*
+ * xtree local flag
+ */
+#define XT_INSERT 0x00000001
+
+/*
+ * xtree key/entry comparison: extent offset
+ *
+ * return:
+ * -1: k < start of extent
+ * 0: start_of_extent <= k <= end_of_extent
+ * 1: k > end_of_extent
+ */
+#define XT_CMP(CMP, K, X, OFFSET64)\
+{\
+ OFFSET64 = offsetXAD(X);\
+ (CMP) = ((K) >= OFFSET64 + lengthXAD(X)) ? 1 :\
+ ((K) < OFFSET64) ? -1 : 0;\
+}
+
+/* write a xad entry */
+#define XT_PUTENTRY(XAD, FLAG, OFF, LEN, ADDR)\
+{\
+ (XAD)->flag = (FLAG);\
+ XADoffset((XAD), (OFF));\
+ XADlength((XAD), (LEN));\
+ XADaddress((XAD), (ADDR));\
+}
+
+#define XT_PAGE(IP, MP) BT_PAGE(IP, MP, xtpage_t, i_xtroot)
+
+/* get page buffer for specified block address */
+/* ToDo: Replace this ugly macro with a function */
+#define XT_GETPAGE(IP, BN, MP, SIZE, P, RC)\
+{\
+ BT_GETPAGE(IP, BN, MP, xtpage_t, SIZE, P, RC, i_xtroot)\
+ if (!(RC))\
+ {\
+ if ((le16_to_cpu((P)->header.nextindex) < XTENTRYSTART) ||\
+ (le16_to_cpu((P)->header.nextindex) > le16_to_cpu((P)->header.maxentry)) ||\
+ (le16_to_cpu((P)->header.maxentry) > (((BN)==0)?XTROOTMAXSLOT:PSIZE>>L2XTSLOTSIZE)))\
+ {\
+ jfs_error((IP)->i_sb, "XT_GETPAGE: xtree page corrupt");\
+ BT_PUTPAGE(MP);\
+ MP = NULL;\
+ RC = -EIO;\
+ }\
+ }\
+}
+
+/* for consistency */
+#define XT_PUTPAGE(MP) BT_PUTPAGE(MP)
+
+#define XT_GETSEARCH(IP, LEAF, BN, MP, P, INDEX) \
+ BT_GETSEARCH(IP, LEAF, BN, MP, xtpage_t, P, INDEX, i_xtroot)
+/* xtree entry parameter descriptor */
+struct xtsplit {
+ struct metapage *mp;
+ s16 index;
+ u8 flag;
+ s64 off;
+ s64 addr;
+ int len;
+ struct pxdlist *pxdlist;
+};
+
+
+/*
+ * statistics
+ */
+#ifdef CONFIG_JFS_STATISTICS
+static struct {
+ uint search;
+ uint fastSearch;
+ uint split;
+} xtStat;
+#endif
+
+
+/*
+ * forward references
+ */
+static int xtSearch(struct inode *ip,
+ s64 xoff, int *cmpp, struct btstack * btstack, int flag);
+
+static int xtSplitUp(tid_t tid,
+ struct inode *ip,
+ struct xtsplit * split, struct btstack * btstack);
+
+static int xtSplitPage(tid_t tid, struct inode *ip, struct xtsplit * split,
+ struct metapage ** rmpp, s64 * rbnp);
+
+static int xtSplitRoot(tid_t tid, struct inode *ip,
+ struct xtsplit * split, struct metapage ** rmpp);
+
+#ifdef _STILL_TO_PORT
+static int xtDeleteUp(tid_t tid, struct inode *ip, struct metapage * fmp,
+ xtpage_t * fp, struct btstack * btstack);
+
+static int xtSearchNode(struct inode *ip,
+ xad_t * xad,
+ int *cmpp, struct btstack * btstack, int flag);
+
+static int xtRelink(tid_t tid, struct inode *ip, xtpage_t * fp);
+#endif /* _STILL_TO_PORT */
+
+/* External references */
+
+/*
+ * debug control
+ */
+/* #define _JFS_DEBUG_XTREE 1 */
+
+
+/*
+ * xtLookup()
+ *
+ * function: map a single page into a physical extent;
+ */
+int xtLookup(struct inode *ip, s64 lstart,
+ s64 llen, int *pflag, s64 * paddr, s32 * plen, int no_check)
+{
+ int rc = 0;
+ struct btstack btstack;
+ int cmp;
+ s64 bn;
+ struct metapage *mp;
+ xtpage_t *p;
+ int index;
+ xad_t *xad;
+ s64 size, xoff, xend;
+ int xlen;
+ s64 xaddr;
+
+ *plen = 0;
+
+ if (!no_check) {
+ /* is lookup offset beyond eof ? */
+ size = ((u64) ip->i_size + (JFS_SBI(ip->i_sb)->bsize - 1)) >>
+ JFS_SBI(ip->i_sb)->l2bsize;
+ if (lstart >= size) {
+ jfs_err("xtLookup: lstart (0x%lx) >= size (0x%lx)",
+ (ulong) lstart, (ulong) size);
+ return 0;
+ }
+ }
+
+ /*
+ * search for the xad entry covering the logical extent
+ */
+//search:
+ if ((rc = xtSearch(ip, lstart, &cmp, &btstack, 0))) {
+ jfs_err("xtLookup: xtSearch returned %d", rc);
+ return rc;
+ }
+
+ /*
+ * compute the physical extent covering logical extent
+ *
+ * N.B. search may have failed (e.g., hole in sparse file),
+ * and returned the index of the next entry.
+ */
+ /* retrieve search result */
+ XT_GETSEARCH(ip, btstack.top, bn, mp, p, index);
+
+ /* is xad found covering start of logical extent ?
+ * lstart is a page start address,
+ * i.e., lstart cannot start in a hole;
+ */
+ if (cmp)
+ goto out;
+
+ /*
+ * lxd covered by xad
+ */
+ xad = &p->xad[index];
+ xoff = offsetXAD(xad);
+ xlen = lengthXAD(xad);
+ xend = xoff + xlen;
+ xaddr = addressXAD(xad);
+
+ /* initialize new pxd */
+ *pflag = xad->flag;
+ *paddr = xaddr + (lstart - xoff);
+ /* a page must be fully covered by an xad */
+ *plen = min(xend - lstart, llen);
+
+ out:
+ XT_PUTPAGE(mp);
+
+ return rc;
+}
+
+
+/*
+ * xtLookupList()
+ *
+ * function: map a single logical extent into a list of physical extent;
+ *
+ * parameter:
+ * struct inode *ip,
+ * struct lxdlist *lxdlist, lxd list (in)
+ * struct xadlist *xadlist, xad list (in/out)
+ * int flag)
+ *
+ * coverage of lxd by xad under assumption of
+ * . lxd's are ordered and disjoint.
+ * . xad's are ordered and disjoint.
+ *
+ * return:
+ * 0: success
+ *
+ * note: a page being written (even a single byte) is backed fully,
+ * except the last page which is only backed with blocks
+ * required to cover the last byte;
+ * the extent backing a page is fully contained within an xad;
+ */
+int xtLookupList(struct inode *ip, struct lxdlist * lxdlist,
+ struct xadlist * xadlist, int flag)
+{
+ int rc = 0;
+ struct btstack btstack;
+ int cmp;
+ s64 bn;
+ struct metapage *mp;
+ xtpage_t *p;
+ int index;
+ lxd_t *lxd;
+ xad_t *xad, *pxd;
+ s64 size, lstart, lend, xstart, xend, pstart;
+ s64 llen, xlen, plen;
+ s64 xaddr, paddr;
+ int nlxd, npxd, maxnpxd;
+
+ npxd = xadlist->nxad = 0;
+ maxnpxd = xadlist->maxnxad;
+ pxd = xadlist->xad;
+
+ nlxd = lxdlist->nlxd;
+ lxd = lxdlist->lxd;
+
+ lstart = offsetLXD(lxd);
+ llen = lengthLXD(lxd);
+ lend = lstart + llen;
+
+ size = (ip->i_size + (JFS_SBI(ip->i_sb)->bsize - 1)) >>
+ JFS_SBI(ip->i_sb)->l2bsize;
+
+ /*
+ * search for the xad entry covering the logical extent
+ */
+ search:
+ if (lstart >= size)
+ return 0;
+
+ if ((rc = xtSearch(ip, lstart, &cmp, &btstack, 0)))
+ return rc;
+
+ /*
+ * compute the physical extent covering logical extent
+ *
+ * N.B. search may have failed (e.g., hole in sparse file),
+ * and returned the index of the next entry.
+ */
+//map:
+ /* retrieve search result */
+ XT_GETSEARCH(ip, btstack.top, bn, mp, p, index);
+
+ /* is xad on the next sibling page ? */
+ if (index == le16_to_cpu(p->header.nextindex)) {
+ if (p->header.flag & BT_ROOT)
+ goto mapend;
+
+ if ((bn = le64_to_cpu(p->header.next)) == 0)
+ goto mapend;
+
+ XT_PUTPAGE(mp);
+
+ /* get next sibling page */
+ XT_GETPAGE(ip, bn, mp, PSIZE, p, rc);
+ if (rc)
+ return rc;
+
+ index = XTENTRYSTART;
+ }
+
+ xad = &p->xad[index];
+
+ /*
+ * is lxd covered by xad ?
+ */
+ compare:
+ xstart = offsetXAD(xad);
+ xlen = lengthXAD(xad);
+ xend = xstart + xlen;
+ xaddr = addressXAD(xad);
+
+ compare1:
+ if (xstart < lstart)
+ goto compare2;
+
+ /* (lstart <= xstart) */
+
+ /* lxd is NOT covered by xad */
+ if (lend <= xstart) {
+ /*
+ * get next lxd
+ */
+ if (--nlxd == 0)
+ goto mapend;
+ lxd++;
+
+ lstart = offsetLXD(lxd);
+ llen = lengthLXD(lxd);
+ lend = lstart + llen;
+ if (lstart >= size)
+ goto mapend;
+
+ /* compare with the current xad */
+ goto compare1;
+ }
+ /* lxd is covered by xad */
+ else { /* (xstart < lend) */
+
+ /* initialize new pxd */
+ pstart = xstart;
+ plen = min(lend - xstart, xlen);
+ paddr = xaddr;
+
+ goto cover;
+ }
+
+ /* (xstart < lstart) */
+ compare2:
+ /* lxd is covered by xad */
+ if (lstart < xend) {
+ /* initialize new pxd */
+ pstart = lstart;
+ plen = min(xend - lstart, llen);
+ paddr = xaddr + (lstart - xstart);
+
+ goto cover;
+ }
+ /* lxd is NOT covered by xad */
+ else { /* (xend <= lstart) */
+
+ /*
+ * get next xad
+ *
+ * linear search next xad covering lxd on
+ * the current xad page, and then tree search
+ */
+ if (index == le16_to_cpu(p->header.nextindex) - 1) {
+ if (p->header.flag & BT_ROOT)
+ goto mapend;
+
+ XT_PUTPAGE(mp);
+ goto search;
+ } else {
+ index++;
+ xad++;
+
+ /* compare with new xad */
+ goto compare;
+ }
+ }
+
+ /*
+ * lxd is covered by xad and a new pxd has been initialized
+ * (lstart <= xstart < lend) or (xstart < lstart < xend)
+ */
+ cover:
+ /* finalize pxd corresponding to current xad */
+ XT_PUTENTRY(pxd, xad->flag, pstart, plen, paddr);
+
+ if (++npxd >= maxnpxd)
+ goto mapend;
+ pxd++;
+
+ /*
+ * lxd is fully covered by xad
+ */
+ if (lend <= xend) {
+ /*
+ * get next lxd
+ */
+ if (--nlxd == 0)
+ goto mapend;
+ lxd++;
+
+ lstart = offsetLXD(lxd);
+ llen = lengthLXD(lxd);
+ lend = lstart + llen;
+ if (lstart >= size)
+ goto mapend;
+
+ /*
+ * test for old xad covering new lxd
+ * (old xstart < new lstart)
+ */
+ goto compare2;
+ }
+ /*
+ * lxd is partially covered by xad
+ */
+ else { /* (xend < lend) */
+
+ /*
+ * get next xad
+ *
+ * linear search next xad covering lxd on
+ * the current xad page, and then next xad page search
+ */
+ if (index == le16_to_cpu(p->header.nextindex) - 1) {
+ if (p->header.flag & BT_ROOT)
+ goto mapend;
+
+ if ((bn = le64_to_cpu(p->header.next)) == 0)
+ goto mapend;
+
+ XT_PUTPAGE(mp);
+
+ /* get next sibling page */
+ XT_GETPAGE(ip, bn, mp, PSIZE, p, rc);
+ if (rc)
+ return rc;
+
+ index = XTENTRYSTART;
+ xad = &p->xad[index];
+ } else {
+ index++;
+ xad++;
+ }
+
+ /*
+ * test for new xad covering old lxd
+ * (old lstart < new xstart)
+ */
+ goto compare;
+ }
+
+ mapend:
+ xadlist->nxad = npxd;
+
+//out:
+ XT_PUTPAGE(mp);
+
+ return rc;
+}
+
+
+/*
+ * xtSearch()
+ *
+ * function: search for the xad entry covering specified offset.
+ *
+ * parameters:
+ * ip - file object;
+ * xoff - extent offset;
+ * cmpp - comparison result:
+ * btstack - traverse stack;
+ * flag - search process flag (XT_INSERT);
+ *
+ * returns:
+ * btstack contains (bn, index) of search path traversed to the entry.
+ * *cmpp is set to result of comparison with the entry returned.
+ * the page containing the entry is pinned at exit.
+ */
+static int xtSearch(struct inode *ip, s64 xoff, /* offset of extent */
+ int *cmpp, struct btstack * btstack, int flag)
+{
+ struct jfs_inode_info *jfs_ip = JFS_IP(ip);
+ int rc = 0;
+ int cmp = 1; /* init for empty page */
+ s64 bn; /* block number */
+ struct metapage *mp; /* page buffer */
+ xtpage_t *p; /* page */
+ xad_t *xad;
+ int base, index, lim, btindex;
+ struct btframe *btsp;
+ int nsplit = 0; /* number of pages to split */
+ s64 t64;
+
+ INCREMENT(xtStat.search);
+
+ BT_CLR(btstack);
+
+ btstack->nsplit = 0;
+
+ /*
+ * search down tree from root:
+ *
+ * between two consecutive entries of <Ki, Pi> and <Kj, Pj> of
+ * internal page, child page Pi contains entry with k, Ki <= K < Kj.
+ *
+ * if entry with search key K is not found
+ * internal page search find the entry with largest key Ki
+ * less than K which point to the child page to search;
+ * leaf page search find the entry with smallest key Kj
+ * greater than K so that the returned index is the position of
+ * the entry to be shifted right for insertion of new entry.
+ * for empty tree, search key is greater than any key of the tree.
+ *
+ * by convention, root bn = 0.
+ */
+ for (bn = 0;;) {
+ /* get/pin the page to search */
+ XT_GETPAGE(ip, bn, mp, PSIZE, p, rc);
+ if (rc)
+ return rc;
+
+ /* try sequential access heuristics with the previous
+ * access entry in target leaf page:
+ * once search narrowed down into the target leaf,
+ * key must either match an entry in the leaf or
+ * key entry does not exist in the tree;
+ */
+//fastSearch:
+ if ((jfs_ip->btorder & BT_SEQUENTIAL) &&
+ (p->header.flag & BT_LEAF) &&
+ (index = jfs_ip->btindex) <
+ le16_to_cpu(p->header.nextindex)) {
+ xad = &p->xad[index];
+ t64 = offsetXAD(xad);
+ if (xoff < t64 + lengthXAD(xad)) {
+ if (xoff >= t64) {
+ *cmpp = 0;
+ goto out;
+ }
+
+ /* stop sequential access heuristics */
+ goto binarySearch;
+ } else { /* (t64 + lengthXAD(xad)) <= xoff */
+
+ /* try next sequential entry */
+ index++;
+ if (index <
+ le16_to_cpu(p->header.nextindex)) {
+ xad++;
+ t64 = offsetXAD(xad);
+ if (xoff < t64 + lengthXAD(xad)) {
+ if (xoff >= t64) {
+ *cmpp = 0;
+ goto out;
+ }
+
+ /* miss: key falls between
+ * previous and this entry
+ */
+ *cmpp = 1;
+ goto out;
+ }
+
+ /* (xoff >= t64 + lengthXAD(xad));
+ * matching entry may be further out:
+ * stop heuristic search
+ */
+ /* stop sequential access heuristics */
+ goto binarySearch;
+ }
+
+ /* (index == p->header.nextindex);
+ * miss: key entry does not exist in
+ * the target leaf/tree
+ */
+ *cmpp = 1;
+ goto out;
+ }
+
+ /*
+ * if hit, return index of the entry found, and
+ * if miss, where new entry with search key is
+ * to be inserted;
+ */
+ out:
+ /* compute number of pages to split */
+ if (flag & XT_INSERT) {
+ if (p->header.nextindex == /* little-endian */
+ p->header.maxentry)
+ nsplit++;
+ else
+ nsplit = 0;
+ btstack->nsplit = nsplit;
+ }
+
+ /* save search result */
+ btsp = btstack->top;
+ btsp->bn = bn;
+ btsp->index = index;
+ btsp->mp = mp;
+
+ /* update sequential access heuristics */
+ jfs_ip->btindex = index;
+
+ INCREMENT(xtStat.fastSearch);
+ return 0;
+ }
+
+ /* well, ... full search now */
+ binarySearch:
+ lim = le16_to_cpu(p->header.nextindex) - XTENTRYSTART;
+
+ /*
+ * binary search with search key K on the current page
+ */
+ for (base = XTENTRYSTART; lim; lim >>= 1) {
+ index = base + (lim >> 1);
+
+ XT_CMP(cmp, xoff, &p->xad[index], t64);
+ if (cmp == 0) {
+ /*
+ * search hit
+ */
+ /* search hit - leaf page:
+ * return the entry found
+ */
+ if (p->header.flag & BT_LEAF) {
+ *cmpp = cmp;
+
+ /* compute number of pages to split */
+ if (flag & XT_INSERT) {
+ if (p->header.nextindex ==
+ p->header.maxentry)
+ nsplit++;
+ else
+ nsplit = 0;
+ btstack->nsplit = nsplit;
+ }
+
+ /* save search result */
+ btsp = btstack->top;
+ btsp->bn = bn;
+ btsp->index = index;
+ btsp->mp = mp;
+
+ /* init sequential access heuristics */
+ btindex = jfs_ip->btindex;
+ if (index == btindex ||
+ index == btindex + 1)
+ jfs_ip->btorder = BT_SEQUENTIAL;
+ else
+ jfs_ip->btorder = BT_RANDOM;
+ jfs_ip->btindex = index;
+
+ return 0;
+ }
+
+ /* search hit - internal page:
+ * descend/search its child page
+ */
+ goto next;
+ }
+
+ if (cmp > 0) {
+ base = index + 1;
+ --lim;
+ }
+ }
+
+ /*
+ * search miss
+ *
+ * base is the smallest index with key (Kj) greater than
+ * search key (K) and may be zero or maxentry index.
+ */
+ /*
+ * search miss - leaf page:
+ *
+ * return location of entry (base) where new entry with
+ * search key K is to be inserted.
+ */
+ if (p->header.flag & BT_LEAF) {
+ *cmpp = cmp;
+
+ /* compute number of pages to split */
+ if (flag & XT_INSERT) {
+ if (p->header.nextindex ==
+ p->header.maxentry)
+ nsplit++;
+ else
+ nsplit = 0;
+ btstack->nsplit = nsplit;
+ }
+
+ /* save search result */
+ btsp = btstack->top;
+ btsp->bn = bn;
+ btsp->index = base;
+ btsp->mp = mp;
+
+ /* init sequential access heuristics */
+ btindex = jfs_ip->btindex;
+ if (base == btindex || base == btindex + 1)
+ jfs_ip->btorder = BT_SEQUENTIAL;
+ else
+ jfs_ip->btorder = BT_RANDOM;
+ jfs_ip->btindex = base;
+
+ return 0;
+ }
+
+ /*
+ * search miss - non-leaf page:
+ *
+ * if base is non-zero, decrement base by one to get the parent
+ * entry of the child page to search.
+ */
+ index = base ? base - 1 : base;
+
+ /*
+ * go down to child page
+ */
+ next:
+ /* update number of pages to split */
+ if (p->header.nextindex == p->header.maxentry)
+ nsplit++;
+ else
+ nsplit = 0;
+
+ /* push (bn, index) of the parent page/entry */
+ BT_PUSH(btstack, bn, index);
+
+ /* get the child page block number */
+ bn = addressXAD(&p->xad[index]);
+
+ /* unpin the parent page */
+ XT_PUTPAGE(mp);
+ }
+}
+
+/*
+ * xtInsert()
+ *
+ * function:
+ *
+ * parameter:
+ * tid - transaction id;
+ * ip - file object;
+ * xflag - extent flag (XAD_NOTRECORDED):
+ * xoff - extent offset;
+ * xlen - extent length;
+ * xaddrp - extent address pointer (in/out):
+ * if (*xaddrp)
+ * caller allocated data extent at *xaddrp;
+ * else
+ * allocate data extent and return its xaddr;
+ * flag -
+ *
+ * return:
+ */
+int xtInsert(tid_t tid, /* transaction id */
+ struct inode *ip, int xflag, s64 xoff, s32 xlen, s64 * xaddrp,
+ int flag)
+{
+ int rc = 0;
+ s64 xaddr, hint;
+ struct metapage *mp; /* meta-page buffer */
+ xtpage_t *p; /* base B+-tree index page */
+ s64 bn;
+ int index, nextindex;
+ struct btstack btstack; /* traverse stack */
+ struct xtsplit split; /* split information */
+ xad_t *xad;
+ int cmp;
+ struct tlock *tlck;
+ struct xtlock *xtlck;
+
+ jfs_info("xtInsert: nxoff:0x%lx nxlen:0x%x", (ulong) xoff, xlen);
+
+ /*
+ * search for the entry location at which to insert:
+ *
+ * xtFastSearch() and xtSearch() both returns (leaf page
+ * pinned, index at which to insert).
+ * n.b. xtSearch() may return index of maxentry of
+ * the full page.
+ */
+ if ((rc = xtSearch(ip, xoff, &cmp, &btstack, XT_INSERT)))
+ return rc;
+
+ /* retrieve search result */
+ XT_GETSEARCH(ip, btstack.top, bn, mp, p, index);
+
+ /* This test must follow XT_GETSEARCH since mp must be valid if
+ * we branch to out: */
+ if (cmp == 0) {
+ rc = -EEXIST;
+ goto out;
+ }
+
+ /*
+ * allocate data extent requested
+ *
+ * allocation hint: last xad
+ */
+ if ((xaddr = *xaddrp) == 0) {
+ if (index > XTENTRYSTART) {
+ xad = &p->xad[index - 1];
+ hint = addressXAD(xad) + lengthXAD(xad) - 1;
+ } else
+ hint = 0;
+ if ((rc = dbAlloc(ip, hint, (s64) xlen, &xaddr)))
+ goto out;
+ }
+
+ /*
+ * insert entry for new extent
+ */
+ xflag |= XAD_NEW;
+
+ /*
+ * if the leaf page is full, split the page and
+ * propagate up the router entry for the new page from split
+ *
+ * The xtSplitUp() will insert the entry and unpin the leaf page.
+ */
+ nextindex = le16_to_cpu(p->header.nextindex);
+ if (nextindex == le16_to_cpu(p->header.maxentry)) {
+ split.mp = mp;
+ split.index = index;
+ split.flag = xflag;
+ split.off = xoff;
+ split.len = xlen;
+ split.addr = xaddr;
+ split.pxdlist = NULL;
+ if ((rc = xtSplitUp(tid, ip, &split, &btstack))) {
+ /* undo data extent allocation */
+ if (*xaddrp == 0)
+ dbFree(ip, xaddr, (s64) xlen);
+ return rc;
+ }
+
+ *xaddrp = xaddr;
+ return 0;
+ }
+
+ /*
+ * insert the new entry into the leaf page
+ */
+ /*
+ * acquire a transaction lock on the leaf page;
+ *
+ * action: xad insertion/extension;
+ */
+ BT_MARK_DIRTY(mp, ip);
+
+ /* if insert into middle, shift right remaining entries. */
+ if (index < nextindex)
+ memmove(&p->xad[index + 1], &p->xad[index],
+ (nextindex - index) * sizeof(xad_t));
+
+ /* insert the new entry: mark the entry NEW */
+ xad = &p->xad[index];
+ XT_PUTENTRY(xad, xflag, xoff, xlen, xaddr);
+
+ /* advance next available entry index */
+ p->header.nextindex =
+ cpu_to_le16(le16_to_cpu(p->header.nextindex) + 1);
+
+ /* Don't log it if there are no links to the file */
+ if (!test_cflag(COMMIT_Nolink, ip)) {
+ tlck = txLock(tid, ip, mp, tlckXTREE | tlckGROW);
+ xtlck = (struct xtlock *) & tlck->lock;
+ xtlck->lwm.offset =
+ (xtlck->lwm.offset) ? min(index,
+ (int)xtlck->lwm.offset) : index;
+ xtlck->lwm.length =
+ le16_to_cpu(p->header.nextindex) - xtlck->lwm.offset;
+ }
+
+ *xaddrp = xaddr;
+
+ out:
+ /* unpin the leaf page */
+ XT_PUTPAGE(mp);
+
+ return rc;
+}
+
+
+/*
+ * xtSplitUp()
+ *
+ * function:
+ * split full pages as propagating insertion up the tree
+ *
+ * parameter:
+ * tid - transaction id;
+ * ip - file object;
+ * split - entry parameter descriptor;
+ * btstack - traverse stack from xtSearch()
+ *
+ * return:
+ */
+static int
+xtSplitUp(tid_t tid,
+ struct inode *ip, struct xtsplit * split, struct btstack * btstack)
+{
+ int rc = 0;
+ struct metapage *smp;
+ xtpage_t *sp; /* split page */
+ struct metapage *rmp;
+ s64 rbn; /* new right page block number */
+ struct metapage *rcmp;
+ xtpage_t *rcp; /* right child page */
+ s64 rcbn; /* right child page block number */
+ int skip; /* index of entry of insertion */
+ int nextindex; /* next available entry index of p */
+ struct btframe *parent; /* parent page entry on traverse stack */
+ xad_t *xad;
+ s64 xaddr;
+ int xlen;
+ int nsplit; /* number of pages split */
+ struct pxdlist pxdlist;
+ pxd_t *pxd;
+ struct tlock *tlck;
+ struct xtlock *xtlck;
+
+ smp = split->mp;
+ sp = XT_PAGE(ip, smp);
+
+ /* is inode xtree root extension/inline EA area free ? */
+ if ((sp->header.flag & BT_ROOT) && (!S_ISDIR(ip->i_mode)) &&
+ (le16_to_cpu(sp->header.maxentry) < XTROOTMAXSLOT) &&
+ (JFS_IP(ip)->mode2 & INLINEEA)) {
+ sp->header.maxentry = cpu_to_le16(XTROOTMAXSLOT);
+ JFS_IP(ip)->mode2 &= ~INLINEEA;
+
+ BT_MARK_DIRTY(smp, ip);
+ /*
+ * acquire a transaction lock on the leaf page;
+ *
+ * action: xad insertion/extension;
+ */
+
+ /* if insert into middle, shift right remaining entries. */
+ skip = split->index;
+ nextindex = le16_to_cpu(sp->header.nextindex);
+ if (skip < nextindex)
+ memmove(&sp->xad[skip + 1], &sp->xad[skip],
+ (nextindex - skip) * sizeof(xad_t));
+
+ /* insert the new entry: mark the entry NEW */
+ xad = &sp->xad[skip];
+ XT_PUTENTRY(xad, split->flag, split->off, split->len,
+ split->addr);
+
+ /* advance next available entry index */
+ sp->header.nextindex =
+ cpu_to_le16(le16_to_cpu(sp->header.nextindex) + 1);
+
+ /* Don't log it if there are no links to the file */
+ if (!test_cflag(COMMIT_Nolink, ip)) {
+ tlck = txLock(tid, ip, smp, tlckXTREE | tlckGROW);
+ xtlck = (struct xtlock *) & tlck->lock;
+ xtlck->lwm.offset = (xtlck->lwm.offset) ?
+ min(skip, (int)xtlck->lwm.offset) : skip;
+ xtlck->lwm.length =
+ le16_to_cpu(sp->header.nextindex) -
+ xtlck->lwm.offset;
+ }
+
+ return 0;
+ }
+
+ /*
+ * allocate new index blocks to cover index page split(s)
+ *
+ * allocation hint: ?
+ */
+ if (split->pxdlist == NULL) {
+ nsplit = btstack->nsplit;
+ split->pxdlist = &pxdlist;
+ pxdlist.maxnpxd = pxdlist.npxd = 0;
+ pxd = &pxdlist.pxd[0];
+ xlen = JFS_SBI(ip->i_sb)->nbperpage;
+ for (; nsplit > 0; nsplit--, pxd++) {
+ if ((rc = dbAlloc(ip, (s64) 0, (s64) xlen, &xaddr))
+ == 0) {
+ PXDaddress(pxd, xaddr);
+ PXDlength(pxd, xlen);
+
+ pxdlist.maxnpxd++;
+
+ continue;
+ }
+
+ /* undo allocation */
+
+ XT_PUTPAGE(smp);
+ return rc;
+ }
+ }
+
+ /*
+ * Split leaf page <sp> into <sp> and a new right page <rp>.
+ *
+ * The split routines insert the new entry into the leaf page,
+ * and acquire txLock as appropriate.
+ * return <rp> pinned and its block number <rpbn>.
+ */
+ rc = (sp->header.flag & BT_ROOT) ?
+ xtSplitRoot(tid, ip, split, &rmp) :
+ xtSplitPage(tid, ip, split, &rmp, &rbn);
+
+ XT_PUTPAGE(smp);
+
+ if (rc)
+ return -EIO;
+ /*
+ * propagate up the router entry for the leaf page just split
+ *
+ * insert a router entry for the new page into the parent page,
+ * propagate the insert/split up the tree by walking back the stack
+ * of (bn of parent page, index of child page entry in parent page)
+ * that were traversed during the search for the page that split.
+ *
+ * the propagation of insert/split up the tree stops if the root
+ * splits or the page inserted into doesn't have to split to hold
+ * the new entry.
+ *
+ * the parent entry for the split page remains the same, and
+ * a new entry is inserted at its right with the first key and
+ * block number of the new right page.
+ *
+ * There are a maximum of 3 pages pinned at any time:
+ * right child, left parent and right parent (when the parent splits)
+ * to keep the child page pinned while working on the parent.
+ * make sure that all pins are released at exit.
+ */
+ while ((parent = BT_POP(btstack)) != NULL) {
+ /* parent page specified by stack frame <parent> */
+
+ /* keep current child pages <rcp> pinned */
+ rcmp = rmp;
+ rcbn = rbn;
+ rcp = XT_PAGE(ip, rcmp);
+
+ /*
+ * insert router entry in parent for new right child page <rp>
+ */
+ /* get/pin the parent page <sp> */
+ XT_GETPAGE(ip, parent->bn, smp, PSIZE, sp, rc);
+ if (rc) {
+ XT_PUTPAGE(rcmp);
+ return rc;
+ }
+
+ /*
+ * The new key entry goes ONE AFTER the index of parent entry,
+ * because the split was to the right.
+ */
+ skip = parent->index + 1;
+
+ /*
+ * split or shift right remaining entries of the parent page
+ */
+ nextindex = le16_to_cpu(sp->header.nextindex);
+ /*
+ * parent page is full - split the parent page
+ */
+ if (nextindex == le16_to_cpu(sp->header.maxentry)) {
+ /* init for parent page split */
+ split->mp = smp;
+ split->index = skip; /* index at insert */
+ split->flag = XAD_NEW;
+ split->off = offsetXAD(&rcp->xad[XTENTRYSTART]);
+ split->len = JFS_SBI(ip->i_sb)->nbperpage;
+ split->addr = rcbn;
+
+ /* unpin previous right child page */
+ XT_PUTPAGE(rcmp);
+
+ /* The split routines insert the new entry,
+ * and acquire txLock as appropriate.
+ * return <rp> pinned and its block number <rpbn>.
+ */
+ rc = (sp->header.flag & BT_ROOT) ?
+ xtSplitRoot(tid, ip, split, &rmp) :
+ xtSplitPage(tid, ip, split, &rmp, &rbn);
+ if (rc) {
+ XT_PUTPAGE(smp);
+ return rc;
+ }
+
+ XT_PUTPAGE(smp);
+ /* keep new child page <rp> pinned */
+ }
+ /*
+ * parent page is not full - insert in parent page
+ */
+ else {
+ /*
+ * insert router entry in parent for the right child
+ * page from the first entry of the right child page:
+ */
+ /*
+ * acquire a transaction lock on the parent page;
+ *
+ * action: router xad insertion;
+ */
+ BT_MARK_DIRTY(smp, ip);
+
+ /*
+ * if insert into middle, shift right remaining entries
+ */
+ if (skip < nextindex)
+ memmove(&sp->xad[skip + 1], &sp->xad[skip],
+ (nextindex -
+ skip) << L2XTSLOTSIZE);
+
+ /* insert the router entry */
+ xad = &sp->xad[skip];
+ XT_PUTENTRY(xad, XAD_NEW,
+ offsetXAD(&rcp->xad[XTENTRYSTART]),
+ JFS_SBI(ip->i_sb)->nbperpage, rcbn);
+
+ /* advance next available entry index. */
+ sp->header.nextindex =
+ cpu_to_le16(le16_to_cpu(sp->header.nextindex) +
+ 1);
+
+ /* Don't log it if there are no links to the file */
+ if (!test_cflag(COMMIT_Nolink, ip)) {
+ tlck = txLock(tid, ip, smp,
+ tlckXTREE | tlckGROW);
+ xtlck = (struct xtlock *) & tlck->lock;
+ xtlck->lwm.offset = (xtlck->lwm.offset) ?
+ min(skip, (int)xtlck->lwm.offset) : skip;
+ xtlck->lwm.length =
+ le16_to_cpu(sp->header.nextindex) -
+ xtlck->lwm.offset;
+ }
+
+ /* unpin parent page */
+ XT_PUTPAGE(smp);
+
+ /* exit propagate up */
+ break;
+ }
+ }
+
+ /* unpin current right page */
+ XT_PUTPAGE(rmp);
+
+ return 0;
+}
+
+
+/*
+ * xtSplitPage()
+ *
+ * function:
+ * split a full non-root page into
+ * original/split/left page and new right page
+ * i.e., the original/split page remains as left page.
+ *
+ * parameter:
+ * int tid,
+ * struct inode *ip,
+ * struct xtsplit *split,
+ * struct metapage **rmpp,
+ * u64 *rbnp,
+ *
+ * return:
+ * Pointer to page in which to insert or NULL on error.
+ */
+static int
+xtSplitPage(tid_t tid, struct inode *ip,
+ struct xtsplit * split, struct metapage ** rmpp, s64 * rbnp)
+{
+ int rc = 0;
+ struct metapage *smp;
+ xtpage_t *sp;
+ struct metapage *rmp;
+ xtpage_t *rp; /* new right page allocated */
+ s64 rbn; /* new right page block number */
+ struct metapage *mp;
+ xtpage_t *p;
+ s64 nextbn;
+ int skip, maxentry, middle, righthalf, n;
+ xad_t *xad;
+ struct pxdlist *pxdlist;
+ pxd_t *pxd;
+ struct tlock *tlck;
+ struct xtlock *sxtlck = 0, *rxtlck = 0;
+
+ smp = split->mp;
+ sp = XT_PAGE(ip, smp);
+
+ INCREMENT(xtStat.split);
+
+ /*
+ * allocate the new right page for the split
+ */
+ pxdlist = split->pxdlist;
+ pxd = &pxdlist->pxd[pxdlist->npxd];
+ pxdlist->npxd++;
+ rbn = addressPXD(pxd);
+ rmp = get_metapage(ip, rbn, PSIZE, 1);
+ if (rmp == NULL)
+ return -EIO;
+
+ jfs_info("xtSplitPage: ip:0x%p smp:0x%p rmp:0x%p", ip, smp, rmp);
+
+ BT_MARK_DIRTY(rmp, ip);
+ /*
+ * action: new page;
+ */
+
+ rp = (xtpage_t *) rmp->data;
+ rp->header.self = *pxd;
+ rp->header.flag = sp->header.flag & BT_TYPE;
+ rp->header.maxentry = sp->header.maxentry; /* little-endian */
+ rp->header.nextindex = cpu_to_le16(XTENTRYSTART);
+
+ BT_MARK_DIRTY(smp, ip);
+ /* Don't log it if there are no links to the file */
+ if (!test_cflag(COMMIT_Nolink, ip)) {
+ /*
+ * acquire a transaction lock on the new right page;
+ */
+ tlck = txLock(tid, ip, rmp, tlckXTREE | tlckNEW);
+ rxtlck = (struct xtlock *) & tlck->lock;
+ rxtlck->lwm.offset = XTENTRYSTART;
+ /*
+ * acquire a transaction lock on the split page
+ */
+ tlck = txLock(tid, ip, smp, tlckXTREE | tlckGROW);
+ sxtlck = (struct xtlock *) & tlck->lock;
+ }
+
+ /*
+ * initialize/update sibling pointers of <sp> and <rp>
+ */
+ nextbn = le64_to_cpu(sp->header.next);
+ rp->header.next = cpu_to_le64(nextbn);
+ rp->header.prev = cpu_to_le64(addressPXD(&sp->header.self));
+ sp->header.next = cpu_to_le64(rbn);
+
+ skip = split->index;
+
+ /*
+ * sequential append at tail (after last entry of last page)
+ *
+ * if splitting the last page on a level because of appending
+ * a entry to it (skip is maxentry), it's likely that the access is
+ * sequential. adding an empty page on the side of the level is less
+ * work and can push the fill factor much higher than normal.
+ * if we're wrong it's no big deal - we will do the split the right
+ * way next time.
+ * (it may look like it's equally easy to do a similar hack for
+ * reverse sorted data, that is, split the tree left, but it's not.
+ * Be my guest.)
+ */
+ if (nextbn == 0 && skip == le16_to_cpu(sp->header.maxentry)) {
+ /*
+ * acquire a transaction lock on the new/right page;
+ *
+ * action: xad insertion;
+ */
+ /* insert entry at the first entry of the new right page */
+ xad = &rp->xad[XTENTRYSTART];
+ XT_PUTENTRY(xad, split->flag, split->off, split->len,
+ split->addr);
+
+ rp->header.nextindex = cpu_to_le16(XTENTRYSTART + 1);
+
+ if (!test_cflag(COMMIT_Nolink, ip)) {
+ /* rxtlck->lwm.offset = XTENTRYSTART; */
+ rxtlck->lwm.length = 1;
+ }
+
+ *rmpp = rmp;
+ *rbnp = rbn;
+
+ ip->i_blocks += LBLK2PBLK(ip->i_sb, lengthPXD(pxd));
+
+ jfs_info("xtSplitPage: sp:0x%p rp:0x%p", sp, rp);
+ return 0;
+ }
+
+ /*
+ * non-sequential insert (at possibly middle page)
+ */
+
+ /*
+ * update previous pointer of old next/right page of <sp>
+ */
+ if (nextbn != 0) {
+ XT_GETPAGE(ip, nextbn, mp, PSIZE, p, rc);
+ if (rc) {
+ XT_PUTPAGE(rmp);
+ return rc;
+ }
+
+ BT_MARK_DIRTY(mp, ip);
+ /*
+ * acquire a transaction lock on the next page;
+ *
+ * action:sibling pointer update;
+ */
+ if (!test_cflag(COMMIT_Nolink, ip))
+ tlck = txLock(tid, ip, mp, tlckXTREE | tlckRELINK);
+
+ p->header.prev = cpu_to_le64(rbn);
+
+ /* sibling page may have been updated previously, or
+ * it may be updated later;
+ */
+
+ XT_PUTPAGE(mp);
+ }
+
+ /*
+ * split the data between the split and new/right pages
+ */
+ maxentry = le16_to_cpu(sp->header.maxentry);
+ middle = maxentry >> 1;
+ righthalf = maxentry - middle;
+
+ /*
+ * skip index in old split/left page - insert into left page:
+ */
+ if (skip <= middle) {
+ /* move right half of split page to the new right page */
+ memmove(&rp->xad[XTENTRYSTART], &sp->xad[middle],
+ righthalf << L2XTSLOTSIZE);
+
+ /* shift right tail of left half to make room for new entry */
+ if (skip < middle)
+ memmove(&sp->xad[skip + 1], &sp->xad[skip],
+ (middle - skip) << L2XTSLOTSIZE);
+
+ /* insert new entry */
+ xad = &sp->xad[skip];
+ XT_PUTENTRY(xad, split->flag, split->off, split->len,
+ split->addr);
+
+ /* update page header */
+ sp->header.nextindex = cpu_to_le16(middle + 1);
+ if (!test_cflag(COMMIT_Nolink, ip)) {
+ sxtlck->lwm.offset = (sxtlck->lwm.offset) ?
+ min(skip, (int)sxtlck->lwm.offset) : skip;
+ }
+
+ rp->header.nextindex =
+ cpu_to_le16(XTENTRYSTART + righthalf);
+ }
+ /*
+ * skip index in new right page - insert into right page:
+ */
+ else {
+ /* move left head of right half to right page */
+ n = skip - middle;
+ memmove(&rp->xad[XTENTRYSTART], &sp->xad[middle],
+ n << L2XTSLOTSIZE);
+
+ /* insert new entry */
+ n += XTENTRYSTART;
+ xad = &rp->xad[n];
+ XT_PUTENTRY(xad, split->flag, split->off, split->len,
+ split->addr);
+
+ /* move right tail of right half to right page */
+ if (skip < maxentry)
+ memmove(&rp->xad[n + 1], &sp->xad[skip],
+ (maxentry - skip) << L2XTSLOTSIZE);
+
+ /* update page header */
+ sp->header.nextindex = cpu_to_le16(middle);
+ if (!test_cflag(COMMIT_Nolink, ip)) {
+ sxtlck->lwm.offset = (sxtlck->lwm.offset) ?
+ min(middle, (int)sxtlck->lwm.offset) : middle;
+ }
+
+ rp->header.nextindex = cpu_to_le16(XTENTRYSTART +
+ righthalf + 1);
+ }
+
+ if (!test_cflag(COMMIT_Nolink, ip)) {
+ sxtlck->lwm.length = le16_to_cpu(sp->header.nextindex) -
+ sxtlck->lwm.offset;
+
+ /* rxtlck->lwm.offset = XTENTRYSTART; */
+ rxtlck->lwm.length = le16_to_cpu(rp->header.nextindex) -
+ XTENTRYSTART;
+ }
+
+ *rmpp = rmp;
+ *rbnp = rbn;
+
+ ip->i_blocks += LBLK2PBLK(ip->i_sb, lengthPXD(pxd));
+
+ jfs_info("xtSplitPage: sp:0x%p rp:0x%p", sp, rp);
+ return rc;
+}
+
+
+/*
+ * xtSplitRoot()
+ *
+ * function:
+ * split the full root page into
+ * original/root/split page and new right page
+ * i.e., root remains fixed in tree anchor (inode) and
+ * the root is copied to a single new right child page
+ * since root page << non-root page, and
+ * the split root page contains a single entry for the
+ * new right child page.
+ *
+ * parameter:
+ * int tid,
+ * struct inode *ip,
+ * struct xtsplit *split,
+ * struct metapage **rmpp)
+ *
+ * return:
+ * Pointer to page in which to insert or NULL on error.
+ */
+static int
+xtSplitRoot(tid_t tid,
+ struct inode *ip, struct xtsplit * split, struct metapage ** rmpp)
+{
+ xtpage_t *sp;
+ struct metapage *rmp;
+ xtpage_t *rp;
+ s64 rbn;
+ int skip, nextindex;
+ xad_t *xad;
+ pxd_t *pxd;
+ struct pxdlist *pxdlist;
+ struct tlock *tlck;
+ struct xtlock *xtlck;
+
+ sp = &JFS_IP(ip)->i_xtroot;
+
+ INCREMENT(xtStat.split);
+
+ /*
+ * allocate a single (right) child page
+ */
+ pxdlist = split->pxdlist;
+ pxd = &pxdlist->pxd[pxdlist->npxd];
+ pxdlist->npxd++;
+ rbn = addressPXD(pxd);
+ rmp = get_metapage(ip, rbn, PSIZE, 1);
+ if (rmp == NULL)
+ return -EIO;
+
+ jfs_info("xtSplitRoot: ip:0x%p rmp:0x%p", ip, rmp);
+
+ /*
+ * acquire a transaction lock on the new right page;
+ *
+ * action: new page;
+ */
+ BT_MARK_DIRTY(rmp, ip);
+
+ rp = (xtpage_t *) rmp->data;
+ rp->header.flag =
+ (sp->header.flag & BT_LEAF) ? BT_LEAF : BT_INTERNAL;
+ rp->header.self = *pxd;
+ rp->header.nextindex = cpu_to_le16(XTENTRYSTART);
+ rp->header.maxentry = cpu_to_le16(PSIZE >> L2XTSLOTSIZE);
+
+ /* initialize sibling pointers */
+ rp->header.next = 0;
+ rp->header.prev = 0;
+
+ /*
+ * copy the in-line root page into new right page extent
+ */
+ nextindex = le16_to_cpu(sp->header.maxentry);
+ memmove(&rp->xad[XTENTRYSTART], &sp->xad[XTENTRYSTART],
+ (nextindex - XTENTRYSTART) << L2XTSLOTSIZE);
+
+ /*
+ * insert the new entry into the new right/child page
+ * (skip index in the new right page will not change)
+ */
+ skip = split->index;
+ /* if insert into middle, shift right remaining entries */
+ if (skip != nextindex)
+ memmove(&rp->xad[skip + 1], &rp->xad[skip],
+ (nextindex - skip) * sizeof(xad_t));
+
+ xad = &rp->xad[skip];
+ XT_PUTENTRY(xad, split->flag, split->off, split->len, split->addr);
+
+ /* update page header */
+ rp->header.nextindex = cpu_to_le16(nextindex + 1);
+
+ if (!test_cflag(COMMIT_Nolink, ip)) {
+ tlck = txLock(tid, ip, rmp, tlckXTREE | tlckNEW);
+ xtlck = (struct xtlock *) & tlck->lock;
+ xtlck->lwm.offset = XTENTRYSTART;
+ xtlck->lwm.length = le16_to_cpu(rp->header.nextindex) -
+ XTENTRYSTART;
+ }
+
+ /*
+ * reset the root
+ *
+ * init root with the single entry for the new right page
+ * set the 1st entry offset to 0, which force the left-most key
+ * at any level of the tree to be less than any search key.
+ */
+ /*
+ * acquire a transaction lock on the root page (in-memory inode);
+ *
+ * action: root split;
+ */
+ BT_MARK_DIRTY(split->mp, ip);
+
+ xad = &sp->xad[XTENTRYSTART];
+ XT_PUTENTRY(xad, XAD_NEW, 0, JFS_SBI(ip->i_sb)->nbperpage, rbn);
+
+ /* update page header of root */
+ sp->header.flag &= ~BT_LEAF;
+ sp->header.flag |= BT_INTERNAL;
+
+ sp->header.nextindex = cpu_to_le16(XTENTRYSTART + 1);
+
+ if (!test_cflag(COMMIT_Nolink, ip)) {
+ tlck = txLock(tid, ip, split->mp, tlckXTREE | tlckGROW);
+ xtlck = (struct xtlock *) & tlck->lock;
+ xtlck->lwm.offset = XTENTRYSTART;
+ xtlck->lwm.length = 1;
+ }
+
+ *rmpp = rmp;
+
+ ip->i_blocks += LBLK2PBLK(ip->i_sb, lengthPXD(pxd));
+
+ jfs_info("xtSplitRoot: sp:0x%p rp:0x%p", sp, rp);
+ return 0;
+}
+
+
+/*
+ * xtExtend()
+ *
+ * function: extend in-place;
+ *
+ * note: existing extent may or may not have been committed.
+ * caller is responsible for pager buffer cache update, and
+ * working block allocation map update;
+ * update pmap: alloc whole extended extent;
+ */
+int xtExtend(tid_t tid, /* transaction id */
+ struct inode *ip, s64 xoff, /* delta extent offset */
+ s32 xlen, /* delta extent length */
+ int flag)
+{
+ int rc = 0;
+ int cmp;
+ struct metapage *mp; /* meta-page buffer */
+ xtpage_t *p; /* base B+-tree index page */
+ s64 bn;
+ int index, nextindex, len;
+ struct btstack btstack; /* traverse stack */
+ struct xtsplit split; /* split information */
+ xad_t *xad;
+ s64 xaddr;
+ struct tlock *tlck;
+ struct xtlock *xtlck = 0;
+
+ jfs_info("xtExtend: nxoff:0x%lx nxlen:0x%x", (ulong) xoff, xlen);
+
+ /* there must exist extent to be extended */
+ if ((rc = xtSearch(ip, xoff - 1, &cmp, &btstack, XT_INSERT)))
+ return rc;
+
+ /* retrieve search result */
+ XT_GETSEARCH(ip, btstack.top, bn, mp, p, index);
+
+ if (cmp != 0) {
+ XT_PUTPAGE(mp);
+ jfs_error(ip->i_sb, "xtExtend: xtSearch did not find extent");
+ return -EIO;
+ }
+
+ /* extension must be contiguous */
+ xad = &p->xad[index];
+ if ((offsetXAD(xad) + lengthXAD(xad)) != xoff) {
+ XT_PUTPAGE(mp);
+ jfs_error(ip->i_sb, "xtExtend: extension is not contiguous");
+ return -EIO;
+ }
+
+ /*
+ * acquire a transaction lock on the leaf page;
+ *
+ * action: xad insertion/extension;
+ */
+ BT_MARK_DIRTY(mp, ip);
+ if (!test_cflag(COMMIT_Nolink, ip)) {
+ tlck = txLock(tid, ip, mp, tlckXTREE | tlckGROW);
+ xtlck = (struct xtlock *) & tlck->lock;
+ }
+
+ /* extend will overflow extent ? */
+ xlen = lengthXAD(xad) + xlen;
+ if ((len = xlen - MAXXLEN) <= 0)
+ goto extendOld;
+
+ /*
+ * extent overflow: insert entry for new extent
+ */
+//insertNew:
+ xoff = offsetXAD(xad) + MAXXLEN;
+ xaddr = addressXAD(xad) + MAXXLEN;
+ nextindex = le16_to_cpu(p->header.nextindex);
+
+ /*
+ * if the leaf page is full, insert the new entry and
+ * propagate up the router entry for the new page from split
+ *
+ * The xtSplitUp() will insert the entry and unpin the leaf page.
+ */
+ if (nextindex == le16_to_cpu(p->header.maxentry)) {
+ /* xtSpliUp() unpins leaf pages */
+ split.mp = mp;
+ split.index = index + 1;
+ split.flag = XAD_NEW;
+ split.off = xoff; /* split offset */
+ split.len = len;
+ split.addr = xaddr;
+ split.pxdlist = NULL;
+ if ((rc = xtSplitUp(tid, ip, &split, &btstack)))
+ return rc;
+
+ /* get back old page */
+ XT_GETPAGE(ip, bn, mp, PSIZE, p, rc);
+ if (rc)
+ return rc;
+ /*
+ * if leaf root has been split, original root has been
+ * copied to new child page, i.e., original entry now
+ * resides on the new child page;
+ */
+ if (p->header.flag & BT_INTERNAL) {
+ ASSERT(p->header.nextindex ==
+ cpu_to_le16(XTENTRYSTART + 1));
+ xad = &p->xad[XTENTRYSTART];
+ bn = addressXAD(xad);
+ XT_PUTPAGE(mp);
+
+ /* get new child page */
+ XT_GETPAGE(ip, bn, mp, PSIZE, p, rc);
+ if (rc)
+ return rc;
+
+ BT_MARK_DIRTY(mp, ip);
+ if (!test_cflag(COMMIT_Nolink, ip)) {
+ tlck = txLock(tid, ip, mp, tlckXTREE|tlckGROW);
+ xtlck = (struct xtlock *) & tlck->lock;
+ }
+ }
+ }
+ /*
+ * insert the new entry into the leaf page
+ */
+ else {
+ /* insert the new entry: mark the entry NEW */
+ xad = &p->xad[index + 1];
+ XT_PUTENTRY(xad, XAD_NEW, xoff, len, xaddr);
+
+ /* advance next available entry index */
+ p->header.nextindex =
+ cpu_to_le16(le16_to_cpu(p->header.nextindex) + 1);
+ }
+
+ /* get back old entry */
+ xad = &p->xad[index];
+ xlen = MAXXLEN;
+
+ /*
+ * extend old extent
+ */
+ extendOld:
+ XADlength(xad, xlen);
+ if (!(xad->flag & XAD_NEW))
+ xad->flag |= XAD_EXTENDED;
+
+ if (!test_cflag(COMMIT_Nolink, ip)) {
+ xtlck->lwm.offset =
+ (xtlck->lwm.offset) ? min(index,
+ (int)xtlck->lwm.offset) : index;
+ xtlck->lwm.length =
+ le16_to_cpu(p->header.nextindex) - xtlck->lwm.offset;
+ }
+
+ /* unpin the leaf page */
+ XT_PUTPAGE(mp);
+
+ return rc;
+}
+
+#ifdef _NOTYET
+/*
+ * xtTailgate()
+ *
+ * function: split existing 'tail' extent
+ * (split offset >= start offset of tail extent), and
+ * relocate and extend the split tail half;
+ *
+ * note: existing extent may or may not have been committed.
+ * caller is responsible for pager buffer cache update, and
+ * working block allocation map update;
+ * update pmap: free old split tail extent, alloc new extent;
+ */
+int xtTailgate(tid_t tid, /* transaction id */
+ struct inode *ip, s64 xoff, /* split/new extent offset */
+ s32 xlen, /* new extent length */
+ s64 xaddr, /* new extent address */
+ int flag)
+{
+ int rc = 0;
+ int cmp;
+ struct metapage *mp; /* meta-page buffer */
+ xtpage_t *p; /* base B+-tree index page */
+ s64 bn;
+ int index, nextindex, llen, rlen;
+ struct btstack btstack; /* traverse stack */
+ struct xtsplit split; /* split information */
+ xad_t *xad;
+ struct tlock *tlck;
+ struct xtlock *xtlck = 0;
+ struct tlock *mtlck;
+ struct maplock *pxdlock;
+
+/*
+printf("xtTailgate: nxoff:0x%lx nxlen:0x%x nxaddr:0x%lx\n",
+ (ulong)xoff, xlen, (ulong)xaddr);
+*/
+
+ /* there must exist extent to be tailgated */
+ if ((rc = xtSearch(ip, xoff, &cmp, &btstack, XT_INSERT)))
+ return rc;
+
+ /* retrieve search result */
+ XT_GETSEARCH(ip, btstack.top, bn, mp, p, index);
+
+ if (cmp != 0) {
+ XT_PUTPAGE(mp);
+ jfs_error(ip->i_sb, "xtTailgate: couldn't find extent");
+ return -EIO;
+ }
+
+ /* entry found must be last entry */
+ nextindex = le16_to_cpu(p->header.nextindex);
+ if (index != nextindex - 1) {
+ XT_PUTPAGE(mp);
+ jfs_error(ip->i_sb,
+ "xtTailgate: the entry found is not the last entry");
+ return -EIO;
+ }
+
+ BT_MARK_DIRTY(mp, ip);
+ /*
+ * acquire tlock of the leaf page containing original entry
+ */
+ if (!test_cflag(COMMIT_Nolink, ip)) {
+ tlck = txLock(tid, ip, mp, tlckXTREE | tlckGROW);
+ xtlck = (struct xtlock *) & tlck->lock;
+ }
+
+ /* completely replace extent ? */
+ xad = &p->xad[index];
+/*
+printf("xtTailgate: xoff:0x%lx xlen:0x%x xaddr:0x%lx\n",
+ (ulong)offsetXAD(xad), lengthXAD(xad), (ulong)addressXAD(xad));
+*/
+ if ((llen = xoff - offsetXAD(xad)) == 0)
+ goto updateOld;
+
+ /*
+ * partially replace extent: insert entry for new extent
+ */
+//insertNew:
+ /*
+ * if the leaf page is full, insert the new entry and
+ * propagate up the router entry for the new page from split
+ *
+ * The xtSplitUp() will insert the entry and unpin the leaf page.
+ */
+ if (nextindex == le16_to_cpu(p->header.maxentry)) {
+ /* xtSpliUp() unpins leaf pages */
+ split.mp = mp;
+ split.index = index + 1;
+ split.flag = XAD_NEW;
+ split.off = xoff; /* split offset */
+ split.len = xlen;
+ split.addr = xaddr;
+ split.pxdlist = NULL;
+ if ((rc = xtSplitUp(tid, ip, &split, &btstack)))
+ return rc;
+
+ /* get back old page */
+ XT_GETPAGE(ip, bn, mp, PSIZE, p, rc);
+ if (rc)
+ return rc;
+ /*
+ * if leaf root has been split, original root has been
+ * copied to new child page, i.e., original entry now
+ * resides on the new child page;
+ */
+ if (p->header.flag & BT_INTERNAL) {
+ ASSERT(p->header.nextindex ==
+ cpu_to_le16(XTENTRYSTART + 1));
+ xad = &p->xad[XTENTRYSTART];
+ bn = addressXAD(xad);
+ XT_PUTPAGE(mp);
+
+ /* get new child page */
+ XT_GETPAGE(ip, bn, mp, PSIZE, p, rc);
+ if (rc)
+ return rc;
+
+ BT_MARK_DIRTY(mp, ip);
+ if (!test_cflag(COMMIT_Nolink, ip)) {
+ tlck = txLock(tid, ip, mp, tlckXTREE|tlckGROW);
+ xtlck = (struct xtlock *) & tlck->lock;
+ }
+ }
+ }
+ /*
+ * insert the new entry into the leaf page
+ */
+ else {
+ /* insert the new entry: mark the entry NEW */
+ xad = &p->xad[index + 1];
+ XT_PUTENTRY(xad, XAD_NEW, xoff, xlen, xaddr);
+
+ /* advance next available entry index */
+ p->header.nextindex =
+ cpu_to_le16(le16_to_cpu(p->header.nextindex) + 1);
+ }
+
+ /* get back old XAD */
+ xad = &p->xad[index];
+
+ /*
+ * truncate/relocate old extent at split offset
+ */
+ updateOld:
+ /* update dmap for old/committed/truncated extent */
+ rlen = lengthXAD(xad) - llen;
+ if (!(xad->flag & XAD_NEW)) {
+ /* free from PWMAP at commit */
+ if (!test_cflag(COMMIT_Nolink, ip)) {
+ mtlck = txMaplock(tid, ip, tlckMAP);
+ pxdlock = (struct maplock *) & mtlck->lock;
+ pxdlock->flag = mlckFREEPXD;
+ PXDaddress(&pxdlock->pxd, addressXAD(xad) + llen);
+ PXDlength(&pxdlock->pxd, rlen);
+ pxdlock->index = 1;
+ }
+ } else
+ /* free from WMAP */
+ dbFree(ip, addressXAD(xad) + llen, (s64) rlen);
+
+ if (llen)
+ /* truncate */
+ XADlength(xad, llen);
+ else
+ /* replace */
+ XT_PUTENTRY(xad, XAD_NEW, xoff, xlen, xaddr);
+
+ if (!test_cflag(COMMIT_Nolink, ip)) {
+ xtlck->lwm.offset = (xtlck->lwm.offset) ?
+ min(index, (int)xtlck->lwm.offset) : index;
+ xtlck->lwm.length = le16_to_cpu(p->header.nextindex) -
+ xtlck->lwm.offset;
+ }
+
+ /* unpin the leaf page */
+ XT_PUTPAGE(mp);
+
+ return rc;
+}
+#endif /* _NOTYET */
+
+/*
+ * xtUpdate()
+ *
+ * function: update XAD;
+ *
+ * update extent for allocated_but_not_recorded or
+ * compressed extent;
+ *
+ * parameter:
+ * nxad - new XAD;
+ * logical extent of the specified XAD must be completely
+ * contained by an existing XAD;
+ */
+int xtUpdate(tid_t tid, struct inode *ip, xad_t * nxad)
+{ /* new XAD */
+ int rc = 0;
+ int cmp;
+ struct metapage *mp; /* meta-page buffer */
+ xtpage_t *p; /* base B+-tree index page */
+ s64 bn;
+ int index0, index, newindex, nextindex;
+ struct btstack btstack; /* traverse stack */
+ struct xtsplit split; /* split information */
+ xad_t *xad, *lxad, *rxad;
+ int xflag;
+ s64 nxoff, xoff;
+ int nxlen, xlen, lxlen, rxlen;
+ s64 nxaddr, xaddr;
+ struct tlock *tlck;
+ struct xtlock *xtlck = 0;
+ int newpage = 0;
+
+ /* there must exist extent to be tailgated */
+ nxoff = offsetXAD(nxad);
+ nxlen = lengthXAD(nxad);
+ nxaddr = addressXAD(nxad);
+
+ if ((rc = xtSearch(ip, nxoff, &cmp, &btstack, XT_INSERT)))
+ return rc;
+
+ /* retrieve search result */
+ XT_GETSEARCH(ip, btstack.top, bn, mp, p, index0);
+
+ if (cmp != 0) {
+ XT_PUTPAGE(mp);
+ jfs_error(ip->i_sb, "xtUpdate: Could not find extent");
+ return -EIO;
+ }
+
+ BT_MARK_DIRTY(mp, ip);
+ /*
+ * acquire tlock of the leaf page containing original entry
+ */
+ if (!test_cflag(COMMIT_Nolink, ip)) {
+ tlck = txLock(tid, ip, mp, tlckXTREE | tlckGROW);
+ xtlck = (struct xtlock *) & tlck->lock;
+ }
+
+ xad = &p->xad[index0];
+ xflag = xad->flag;
+ xoff = offsetXAD(xad);
+ xlen = lengthXAD(xad);
+ xaddr = addressXAD(xad);
+
+ /* nXAD must be completely contained within XAD */
+ if ((xoff > nxoff) ||
+ (nxoff + nxlen > xoff + xlen)) {
+ XT_PUTPAGE(mp);
+ jfs_error(ip->i_sb,
+ "xtUpdate: nXAD in not completely contained within XAD");
+ return -EIO;
+ }
+
+ index = index0;
+ newindex = index + 1;
+ nextindex = le16_to_cpu(p->header.nextindex);
+
+#ifdef _JFS_WIP_NOCOALESCE
+ if (xoff < nxoff)
+ goto updateRight;
+
+ /*
+ * replace XAD with nXAD
+ */
+ replace: /* (nxoff == xoff) */
+ if (nxlen == xlen) {
+ /* replace XAD with nXAD:recorded */
+ *xad = *nxad;
+ xad->flag = xflag & ~XAD_NOTRECORDED;
+
+ goto out;
+ } else /* (nxlen < xlen) */
+ goto updateLeft;
+#endif /* _JFS_WIP_NOCOALESCE */
+
+/* #ifdef _JFS_WIP_COALESCE */
+ if (xoff < nxoff)
+ goto coalesceRight;
+
+ /*
+ * coalesce with left XAD
+ */
+//coalesceLeft: /* (xoff == nxoff) */
+ /* is XAD first entry of page ? */
+ if (index == XTENTRYSTART)
+ goto replace;
+
+ /* is nXAD logically and physically contiguous with lXAD ? */
+ lxad = &p->xad[index - 1];
+ lxlen = lengthXAD(lxad);
+ if (!(lxad->flag & XAD_NOTRECORDED) &&
+ (nxoff == offsetXAD(lxad) + lxlen) &&
+ (nxaddr == addressXAD(lxad) + lxlen) &&
+ (lxlen + nxlen < MAXXLEN)) {
+ /* extend right lXAD */
+ index0 = index - 1;
+ XADlength(lxad, lxlen + nxlen);
+
+ /* If we just merged two extents together, need to make sure the
+ * right extent gets logged. If the left one is marked XAD_NEW,
+ * then we know it will be logged. Otherwise, mark as
+ * XAD_EXTENDED
+ */
+ if (!(lxad->flag & XAD_NEW))
+ lxad->flag |= XAD_EXTENDED;
+
+ if (xlen > nxlen) {
+ /* truncate XAD */
+ XADoffset(xad, xoff + nxlen);
+ XADlength(xad, xlen - nxlen);
+ XADaddress(xad, xaddr + nxlen);
+ goto out;
+ } else { /* (xlen == nxlen) */
+
+ /* remove XAD */
+ if (index < nextindex - 1)
+ memmove(&p->xad[index], &p->xad[index + 1],
+ (nextindex - index -
+ 1) << L2XTSLOTSIZE);
+
+ p->header.nextindex =
+ cpu_to_le16(le16_to_cpu(p->header.nextindex) -
+ 1);
+
+ index = index0;
+ newindex = index + 1;
+ nextindex = le16_to_cpu(p->header.nextindex);
+ xoff = nxoff = offsetXAD(lxad);
+ xlen = nxlen = lxlen + nxlen;
+ xaddr = nxaddr = addressXAD(lxad);
+ goto coalesceRight;
+ }
+ }
+
+ /*
+ * replace XAD with nXAD
+ */
+ replace: /* (nxoff == xoff) */
+ if (nxlen == xlen) {
+ /* replace XAD with nXAD:recorded */
+ *xad = *nxad;
+ xad->flag = xflag & ~XAD_NOTRECORDED;
+
+ goto coalesceRight;
+ } else /* (nxlen < xlen) */
+ goto updateLeft;
+
+ /*
+ * coalesce with right XAD
+ */
+ coalesceRight: /* (xoff <= nxoff) */
+ /* is XAD last entry of page ? */
+ if (newindex == nextindex) {
+ if (xoff == nxoff)
+ goto out;
+ goto updateRight;
+ }
+
+ /* is nXAD logically and physically contiguous with rXAD ? */
+ rxad = &p->xad[index + 1];
+ rxlen = lengthXAD(rxad);
+ if (!(rxad->flag & XAD_NOTRECORDED) &&
+ (nxoff + nxlen == offsetXAD(rxad)) &&
+ (nxaddr + nxlen == addressXAD(rxad)) &&
+ (rxlen + nxlen < MAXXLEN)) {
+ /* extend left rXAD */
+ XADoffset(rxad, nxoff);
+ XADlength(rxad, rxlen + nxlen);
+ XADaddress(rxad, nxaddr);
+
+ /* If we just merged two extents together, need to make sure
+ * the left extent gets logged. If the right one is marked
+ * XAD_NEW, then we know it will be logged. Otherwise, mark as
+ * XAD_EXTENDED
+ */
+ if (!(rxad->flag & XAD_NEW))
+ rxad->flag |= XAD_EXTENDED;
+
+ if (xlen > nxlen)
+ /* truncate XAD */
+ XADlength(xad, xlen - nxlen);
+ else { /* (xlen == nxlen) */
+
+ /* remove XAD */
+ memmove(&p->xad[index], &p->xad[index + 1],
+ (nextindex - index - 1) << L2XTSLOTSIZE);
+
+ p->header.nextindex =
+ cpu_to_le16(le16_to_cpu(p->header.nextindex) -
+ 1);
+ }
+
+ goto out;
+ } else if (xoff == nxoff)
+ goto out;
+
+ if (xoff >= nxoff) {
+ XT_PUTPAGE(mp);
+ jfs_error(ip->i_sb, "xtUpdate: xoff >= nxoff");
+ return -EIO;
+ }
+/* #endif _JFS_WIP_COALESCE */
+
+ /*
+ * split XAD into (lXAD, nXAD):
+ *
+ * |---nXAD--->
+ * --|----------XAD----------|--
+ * |-lXAD-|
+ */
+ updateRight: /* (xoff < nxoff) */
+ /* truncate old XAD as lXAD:not_recorded */
+ xad = &p->xad[index];
+ XADlength(xad, nxoff - xoff);
+
+ /* insert nXAD:recorded */
+ if (nextindex == le16_to_cpu(p->header.maxentry)) {
+
+ /* xtSpliUp() unpins leaf pages */
+ split.mp = mp;
+ split.index = newindex;
+ split.flag = xflag & ~XAD_NOTRECORDED;
+ split.off = nxoff;
+ split.len = nxlen;
+ split.addr = nxaddr;
+ split.pxdlist = NULL;
+ if ((rc = xtSplitUp(tid, ip, &split, &btstack)))
+ return rc;
+
+ /* get back old page */
+ XT_GETPAGE(ip, bn, mp, PSIZE, p, rc);
+ if (rc)
+ return rc;
+ /*
+ * if leaf root has been split, original root has been
+ * copied to new child page, i.e., original entry now
+ * resides on the new child page;
+ */
+ if (p->header.flag & BT_INTERNAL) {
+ ASSERT(p->header.nextindex ==
+ cpu_to_le16(XTENTRYSTART + 1));
+ xad = &p->xad[XTENTRYSTART];
+ bn = addressXAD(xad);
+ XT_PUTPAGE(mp);
+
+ /* get new child page */
+ XT_GETPAGE(ip, bn, mp, PSIZE, p, rc);
+ if (rc)
+ return rc;
+
+ BT_MARK_DIRTY(mp, ip);
+ if (!test_cflag(COMMIT_Nolink, ip)) {
+ tlck = txLock(tid, ip, mp, tlckXTREE|tlckGROW);
+ xtlck = (struct xtlock *) & tlck->lock;
+ }
+ } else {
+ /* is nXAD on new page ? */
+ if (newindex >
+ (le16_to_cpu(p->header.maxentry) >> 1)) {
+ newindex =
+ newindex -
+ le16_to_cpu(p->header.nextindex) +
+ XTENTRYSTART;
+ newpage = 1;
+ }
+ }
+ } else {
+ /* if insert into middle, shift right remaining entries */
+ if (newindex < nextindex)
+ memmove(&p->xad[newindex + 1], &p->xad[newindex],
+ (nextindex - newindex) << L2XTSLOTSIZE);
+
+ /* insert the entry */
+ xad = &p->xad[newindex];
+ *xad = *nxad;
+ xad->flag = xflag & ~XAD_NOTRECORDED;
+
+ /* advance next available entry index. */
+ p->header.nextindex =
+ cpu_to_le16(le16_to_cpu(p->header.nextindex) + 1);
+ }
+
+ /*
+ * does nXAD force 3-way split ?
+ *
+ * |---nXAD--->|
+ * --|----------XAD-------------|--
+ * |-lXAD-| |-rXAD -|
+ */
+ if (nxoff + nxlen == xoff + xlen)
+ goto out;
+
+ /* reorient nXAD as XAD for further split XAD into (nXAD, rXAD) */
+ if (newpage) {
+ /* close out old page */
+ if (!test_cflag(COMMIT_Nolink, ip)) {
+ xtlck->lwm.offset = (xtlck->lwm.offset) ?
+ min(index0, (int)xtlck->lwm.offset) : index0;
+ xtlck->lwm.length =
+ le16_to_cpu(p->header.nextindex) -
+ xtlck->lwm.offset;
+ }
+
+ bn = le64_to_cpu(p->header.next);
+ XT_PUTPAGE(mp);
+
+ /* get new right page */
+ XT_GETPAGE(ip, bn, mp, PSIZE, p, rc);
+ if (rc)
+ return rc;
+
+ BT_MARK_DIRTY(mp, ip);
+ if (!test_cflag(COMMIT_Nolink, ip)) {
+ tlck = txLock(tid, ip, mp, tlckXTREE | tlckGROW);
+ xtlck = (struct xtlock *) & tlck->lock;
+ }
+
+ index0 = index = newindex;
+ } else
+ index++;
+
+ newindex = index + 1;
+ nextindex = le16_to_cpu(p->header.nextindex);
+ xlen = xlen - (nxoff - xoff);
+ xoff = nxoff;
+ xaddr = nxaddr;
+
+ /* recompute split pages */
+ if (nextindex == le16_to_cpu(p->header.maxentry)) {
+ XT_PUTPAGE(mp);
+
+ if ((rc = xtSearch(ip, nxoff, &cmp, &btstack, XT_INSERT)))
+ return rc;
+
+ /* retrieve search result */
+ XT_GETSEARCH(ip, btstack.top, bn, mp, p, index0);
+
+ if (cmp != 0) {
+ XT_PUTPAGE(mp);
+ jfs_error(ip->i_sb, "xtUpdate: xtSearch failed");
+ return -EIO;
+ }
+
+ if (index0 != index) {
+ XT_PUTPAGE(mp);
+ jfs_error(ip->i_sb,
+ "xtUpdate: unexpected value of index");
+ return -EIO;
+ }
+ }
+
+ /*
+ * split XAD into (nXAD, rXAD)
+ *
+ * ---nXAD---|
+ * --|----------XAD----------|--
+ * |-rXAD-|
+ */
+ updateLeft: /* (nxoff == xoff) && (nxlen < xlen) */
+ /* update old XAD with nXAD:recorded */
+ xad = &p->xad[index];
+ *xad = *nxad;
+ xad->flag = xflag & ~XAD_NOTRECORDED;
+
+ /* insert rXAD:not_recorded */
+ xoff = xoff + nxlen;
+ xlen = xlen - nxlen;
+ xaddr = xaddr + nxlen;
+ if (nextindex == le16_to_cpu(p->header.maxentry)) {
+/*
+printf("xtUpdate.updateLeft.split p:0x%p\n", p);
+*/
+ /* xtSpliUp() unpins leaf pages */
+ split.mp = mp;
+ split.index = newindex;
+ split.flag = xflag;
+ split.off = xoff;
+ split.len = xlen;
+ split.addr = xaddr;
+ split.pxdlist = NULL;
+ if ((rc = xtSplitUp(tid, ip, &split, &btstack)))
+ return rc;
+
+ /* get back old page */
+ XT_GETPAGE(ip, bn, mp, PSIZE, p, rc);
+ if (rc)
+ return rc;
+
+ /*
+ * if leaf root has been split, original root has been
+ * copied to new child page, i.e., original entry now
+ * resides on the new child page;
+ */
+ if (p->header.flag & BT_INTERNAL) {
+ ASSERT(p->header.nextindex ==
+ cpu_to_le16(XTENTRYSTART + 1));
+ xad = &p->xad[XTENTRYSTART];
+ bn = addressXAD(xad);
+ XT_PUTPAGE(mp);
+
+ /* get new child page */
+ XT_GETPAGE(ip, bn, mp, PSIZE, p, rc);
+ if (rc)
+ return rc;
+
+ BT_MARK_DIRTY(mp, ip);
+ if (!test_cflag(COMMIT_Nolink, ip)) {
+ tlck = txLock(tid, ip, mp, tlckXTREE|tlckGROW);
+ xtlck = (struct xtlock *) & tlck->lock;
+ }
+ }
+ } else {
+ /* if insert into middle, shift right remaining entries */
+ if (newindex < nextindex)
+ memmove(&p->xad[newindex + 1], &p->xad[newindex],
+ (nextindex - newindex) << L2XTSLOTSIZE);
+
+ /* insert the entry */
+ xad = &p->xad[newindex];
+ XT_PUTENTRY(xad, xflag, xoff, xlen, xaddr);
+
+ /* advance next available entry index. */
+ p->header.nextindex =
+ cpu_to_le16(le16_to_cpu(p->header.nextindex) + 1);
+ }
+
+ out:
+ if (!test_cflag(COMMIT_Nolink, ip)) {
+ xtlck->lwm.offset = (xtlck->lwm.offset) ?
+ min(index0, (int)xtlck->lwm.offset) : index0;
+ xtlck->lwm.length = le16_to_cpu(p->header.nextindex) -
+ xtlck->lwm.offset;
+ }
+
+ /* unpin the leaf page */
+ XT_PUTPAGE(mp);
+
+ return rc;
+}
+
+
+/*
+ * xtAppend()
+ *
+ * function: grow in append mode from contiguous region specified ;
+ *
+ * parameter:
+ * tid - transaction id;
+ * ip - file object;
+ * xflag - extent flag:
+ * xoff - extent offset;
+ * maxblocks - max extent length;
+ * xlen - extent length (in/out);
+ * xaddrp - extent address pointer (in/out):
+ * flag -
+ *
+ * return:
+ */
+int xtAppend(tid_t tid, /* transaction id */
+ struct inode *ip, int xflag, s64 xoff, s32 maxblocks,
+ s32 * xlenp, /* (in/out) */
+ s64 * xaddrp, /* (in/out) */
+ int flag)
+{
+ int rc = 0;
+ struct metapage *mp; /* meta-page buffer */
+ xtpage_t *p; /* base B+-tree index page */
+ s64 bn, xaddr;
+ int index, nextindex;
+ struct btstack btstack; /* traverse stack */
+ struct xtsplit split; /* split information */
+ xad_t *xad;
+ int cmp;
+ struct tlock *tlck;
+ struct xtlock *xtlck;
+ int nsplit, nblocks, xlen;
+ struct pxdlist pxdlist;
+ pxd_t *pxd;
+
+ xaddr = *xaddrp;
+ xlen = *xlenp;
+ jfs_info("xtAppend: xoff:0x%lx maxblocks:%d xlen:%d xaddr:0x%lx",
+ (ulong) xoff, maxblocks, xlen, (ulong) xaddr);
+
+ /*
+ * search for the entry location at which to insert:
+ *
+ * xtFastSearch() and xtSearch() both returns (leaf page
+ * pinned, index at which to insert).
+ * n.b. xtSearch() may return index of maxentry of
+ * the full page.
+ */
+ if ((rc = xtSearch(ip, xoff, &cmp, &btstack, XT_INSERT)))
+ return rc;
+
+ /* retrieve search result */
+ XT_GETSEARCH(ip, btstack.top, bn, mp, p, index);
+
+ if (cmp == 0) {
+ rc = -EEXIST;
+ goto out;
+ }
+//insert:
+ /*
+ * insert entry for new extent
+ */
+ xflag |= XAD_NEW;
+
+ /*
+ * if the leaf page is full, split the page and
+ * propagate up the router entry for the new page from split
+ *
+ * The xtSplitUp() will insert the entry and unpin the leaf page.
+ */
+ nextindex = le16_to_cpu(p->header.nextindex);
+ if (nextindex < le16_to_cpu(p->header.maxentry))
+ goto insertLeaf;
+
+ /*
+ * allocate new index blocks to cover index page split(s)
+ */
+ nsplit = btstack.nsplit;
+ split.pxdlist = &pxdlist;
+ pxdlist.maxnpxd = pxdlist.npxd = 0;
+ pxd = &pxdlist.pxd[0];
+ nblocks = JFS_SBI(ip->i_sb)->nbperpage;
+ for (; nsplit > 0; nsplit--, pxd++, xaddr += nblocks, maxblocks -= nblocks) {
+ if ((rc = dbAllocBottomUp(ip, xaddr, (s64) nblocks)) == 0) {
+ PXDaddress(pxd, xaddr);
+ PXDlength(pxd, nblocks);
+
+ pxdlist.maxnpxd++;
+
+ continue;
+ }
+
+ /* undo allocation */
+
+ goto out;
+ }
+
+ xlen = min(xlen, maxblocks);
+
+ /*
+ * allocate data extent requested
+ */
+ if ((rc = dbAllocBottomUp(ip, xaddr, (s64) xlen)))
+ goto out;
+
+ split.mp = mp;
+ split.index = index;
+ split.flag = xflag;
+ split.off = xoff;
+ split.len = xlen;
+ split.addr = xaddr;
+ if ((rc = xtSplitUp(tid, ip, &split, &btstack))) {
+ /* undo data extent allocation */
+ dbFree(ip, *xaddrp, (s64) * xlenp);
+
+ return rc;
+ }
+
+ *xaddrp = xaddr;
+ *xlenp = xlen;
+ return 0;
+
+ /*
+ * insert the new entry into the leaf page
+ */
+ insertLeaf:
+ /*
+ * allocate data extent requested
+ */
+ if ((rc = dbAllocBottomUp(ip, xaddr, (s64) xlen)))
+ goto out;
+
+ BT_MARK_DIRTY(mp, ip);
+ /*
+ * acquire a transaction lock on the leaf page;
+ *
+ * action: xad insertion/extension;
+ */
+ tlck = txLock(tid, ip, mp, tlckXTREE | tlckGROW);
+ xtlck = (struct xtlock *) & tlck->lock;
+
+ /* insert the new entry: mark the entry NEW */
+ xad = &p->xad[index];
+ XT_PUTENTRY(xad, xflag, xoff, xlen, xaddr);
+
+ /* advance next available entry index */
+ p->header.nextindex =
+ cpu_to_le16(le16_to_cpu(p->header.nextindex) + 1);
+
+ xtlck->lwm.offset =
+ (xtlck->lwm.offset) ? min(index,(int) xtlck->lwm.offset) : index;
+ xtlck->lwm.length = le16_to_cpu(p->header.nextindex) -
+ xtlck->lwm.offset;
+
+ *xaddrp = xaddr;
+ *xlenp = xlen;
+
+ out:
+ /* unpin the leaf page */
+ XT_PUTPAGE(mp);
+
+ return rc;
+}
+#ifdef _STILL_TO_PORT
+
+/* - TBD for defragmentaion/reorganization -
+ *
+ * xtDelete()
+ *
+ * function:
+ * delete the entry with the specified key.
+ *
+ * N.B.: whole extent of the entry is assumed to be deleted.
+ *
+ * parameter:
+ *
+ * return:
+ * ENOENT: if the entry is not found.
+ *
+ * exception:
+ */
+int xtDelete(tid_t tid, struct inode *ip, s64 xoff, s32 xlen, int flag)
+{
+ int rc = 0;
+ struct btstack btstack;
+ int cmp;
+ s64 bn;
+ struct metapage *mp;
+ xtpage_t *p;
+ int index, nextindex;
+ struct tlock *tlck;
+ struct xtlock *xtlck;
+
+ /*
+ * find the matching entry; xtSearch() pins the page
+ */
+ if ((rc = xtSearch(ip, xoff, &cmp, &btstack, 0)))
+ return rc;
+
+ XT_GETSEARCH(ip, btstack.top, bn, mp, p, index);
+ if (cmp) {
+ /* unpin the leaf page */
+ XT_PUTPAGE(mp);
+ return -ENOENT;
+ }
+
+ /*
+ * delete the entry from the leaf page
+ */
+ nextindex = le16_to_cpu(p->header.nextindex);
+ p->header.nextindex =
+ cpu_to_le16(le16_to_cpu(p->header.nextindex) - 1);
+
+ /*
+ * if the leaf page bocome empty, free the page
+ */
+ if (p->header.nextindex == cpu_to_le16(XTENTRYSTART))
+ return (xtDeleteUp(tid, ip, mp, p, &btstack));
+
+ BT_MARK_DIRTY(mp, ip);
+ /*
+ * acquire a transaction lock on the leaf page;
+ *
+ * action:xad deletion;
+ */
+ tlck = txLock(tid, ip, mp, tlckXTREE);
+ xtlck = (struct xtlock *) & tlck->lock;
+ xtlck->lwm.offset =
+ (xtlck->lwm.offset) ? min(index, xtlck->lwm.offset) : index;
+
+ /* if delete from middle, shift left/compact the remaining entries */
+ if (index < nextindex - 1)
+ memmove(&p->xad[index], &p->xad[index + 1],
+ (nextindex - index - 1) * sizeof(xad_t));
+
+ XT_PUTPAGE(mp);
+
+ return 0;
+}
+
+
+/* - TBD for defragmentaion/reorganization -
+ *
+ * xtDeleteUp()
+ *
+ * function:
+ * free empty pages as propagating deletion up the tree
+ *
+ * parameter:
+ *
+ * return:
+ */
+static int
+xtDeleteUp(tid_t tid, struct inode *ip,
+ struct metapage * fmp, xtpage_t * fp, struct btstack * btstack)
+{
+ int rc = 0;
+ struct metapage *mp;
+ xtpage_t *p;
+ int index, nextindex;
+ s64 xaddr;
+ int xlen;
+ struct btframe *parent;
+ struct tlock *tlck;
+ struct xtlock *xtlck;
+
+ /*
+ * keep root leaf page which has become empty
+ */
+ if (fp->header.flag & BT_ROOT) {
+ /* keep the root page */
+ fp->header.flag &= ~BT_INTERNAL;
+ fp->header.flag |= BT_LEAF;
+ fp->header.nextindex = cpu_to_le16(XTENTRYSTART);
+
+ /* XT_PUTPAGE(fmp); */
+
+ return 0;
+ }
+
+ /*
+ * free non-root leaf page
+ */
+ if ((rc = xtRelink(tid, ip, fp))) {
+ XT_PUTPAGE(fmp);
+ return rc;
+ }
+
+ xaddr = addressPXD(&fp->header.self);
+ xlen = lengthPXD(&fp->header.self);
+ /* free the page extent */
+ dbFree(ip, xaddr, (s64) xlen);
+
+ /* free the buffer page */
+ discard_metapage(fmp);
+
+ /*
+ * propagate page deletion up the index tree
+ *
+ * If the delete from the parent page makes it empty,
+ * continue all the way up the tree.
+ * stop if the root page is reached (which is never deleted) or
+ * if the entry deletion does not empty the page.
+ */
+ while ((parent = BT_POP(btstack)) != NULL) {
+ /* get/pin the parent page <sp> */
+ XT_GETPAGE(ip, parent->bn, mp, PSIZE, p, rc);
+ if (rc)
+ return rc;
+
+ index = parent->index;
+
+ /* delete the entry for the freed child page from parent.
+ */
+ nextindex = le16_to_cpu(p->header.nextindex);
+
+ /*
+ * the parent has the single entry being deleted:
+ * free the parent page which has become empty.
+ */
+ if (nextindex == 1) {
+ if (p->header.flag & BT_ROOT) {
+ /* keep the root page */
+ p->header.flag &= ~BT_INTERNAL;
+ p->header.flag |= BT_LEAF;
+ p->header.nextindex =
+ cpu_to_le16(XTENTRYSTART);
+
+ /* XT_PUTPAGE(mp); */
+
+ break;
+ } else {
+ /* free the parent page */
+ if ((rc = xtRelink(tid, ip, p)))
+ return rc;
+
+ xaddr = addressPXD(&p->header.self);
+ /* free the page extent */
+ dbFree(ip, xaddr,
+ (s64) JFS_SBI(ip->i_sb)->nbperpage);
+
+ /* unpin/free the buffer page */
+ discard_metapage(mp);
+
+ /* propagate up */
+ continue;
+ }
+ }
+ /*
+ * the parent has other entries remaining:
+ * delete the router entry from the parent page.
+ */
+ else {
+ BT_MARK_DIRTY(mp, ip);
+ /*
+ * acquire a transaction lock on the leaf page;
+ *
+ * action:xad deletion;
+ */
+ tlck = txLock(tid, ip, mp, tlckXTREE);
+ xtlck = (struct xtlock *) & tlck->lock;
+ xtlck->lwm.offset =
+ (xtlck->lwm.offset) ? min(index,
+ xtlck->lwm.
+ offset) : index;
+
+ /* if delete from middle,
+ * shift left/compact the remaining entries in the page
+ */
+ if (index < nextindex - 1)
+ memmove(&p->xad[index], &p->xad[index + 1],
+ (nextindex - index -
+ 1) << L2XTSLOTSIZE);
+
+ p->header.nextindex =
+ cpu_to_le16(le16_to_cpu(p->header.nextindex) -
+ 1);
+ jfs_info("xtDeleteUp(entry): 0x%lx[%d]",
+ (ulong) parent->bn, index);
+ }
+
+ /* unpin the parent page */
+ XT_PUTPAGE(mp);
+
+ /* exit propagation up */
+ break;
+ }
+
+ return 0;
+}
+
+
+/*
+ * NAME: xtRelocate()
+ *
+ * FUNCTION: relocate xtpage or data extent of regular file;
+ * This function is mainly used by defragfs utility.
+ *
+ * NOTE: This routine does not have the logic to handle
+ * uncommitted allocated extent. The caller should call
+ * txCommit() to commit all the allocation before call
+ * this routine.
+ */
+int
+xtRelocate(tid_t tid, struct inode * ip, xad_t * oxad, /* old XAD */
+ s64 nxaddr, /* new xaddr */
+ int xtype)
+{ /* extent type: XTPAGE or DATAEXT */
+ int rc = 0;
+ struct tblock *tblk;
+ struct tlock *tlck;
+ struct xtlock *xtlck;
+ struct metapage *mp, *pmp, *lmp, *rmp; /* meta-page buffer */
+ xtpage_t *p, *pp, *rp, *lp; /* base B+-tree index page */
+ xad_t *xad;
+ pxd_t *pxd;
+ s64 xoff, xsize;
+ int xlen;
+ s64 oxaddr, sxaddr, dxaddr, nextbn, prevbn;
+ cbuf_t *cp;
+ s64 offset, nbytes, nbrd, pno;
+ int nb, npages, nblks;
+ s64 bn;
+ int cmp;
+ int index;
+ struct pxd_lock *pxdlock;
+ struct btstack btstack; /* traverse stack */
+
+ xtype = xtype & EXTENT_TYPE;
+
+ xoff = offsetXAD(oxad);
+ oxaddr = addressXAD(oxad);
+ xlen = lengthXAD(oxad);
+
+ /* validate extent offset */
+ offset = xoff << JFS_SBI(ip->i_sb)->l2bsize;
+ if (offset >= ip->i_size)
+ return -ESTALE; /* stale extent */
+
+ jfs_info("xtRelocate: xtype:%d xoff:0x%lx xlen:0x%x xaddr:0x%lx:0x%lx",
+ xtype, (ulong) xoff, xlen, (ulong) oxaddr, (ulong) nxaddr);
+
+ /*
+ * 1. get and validate the parent xtpage/xad entry
+ * covering the source extent to be relocated;
+ */
+ if (xtype == DATAEXT) {
+ /* search in leaf entry */
+ rc = xtSearch(ip, xoff, &cmp, &btstack, 0);
+ if (rc)
+ return rc;
+
+ /* retrieve search result */
+ XT_GETSEARCH(ip, btstack.top, bn, pmp, pp, index);
+
+ if (cmp) {
+ XT_PUTPAGE(pmp);
+ return -ESTALE;
+ }
+
+ /* validate for exact match with a single entry */
+ xad = &pp->xad[index];
+ if (addressXAD(xad) != oxaddr || lengthXAD(xad) != xlen) {
+ XT_PUTPAGE(pmp);
+ return -ESTALE;
+ }
+ } else { /* (xtype == XTPAGE) */
+
+ /* search in internal entry */
+ rc = xtSearchNode(ip, oxad, &cmp, &btstack, 0);
+ if (rc)
+ return rc;
+
+ /* retrieve search result */
+ XT_GETSEARCH(ip, btstack.top, bn, pmp, pp, index);
+
+ if (cmp) {
+ XT_PUTPAGE(pmp);
+ return -ESTALE;
+ }
+
+ /* xtSearchNode() validated for exact match with a single entry
+ */
+ xad = &pp->xad[index];
+ }
+ jfs_info("xtRelocate: parent xad entry validated.");
+
+ /*
+ * 2. relocate the extent
+ */
+ if (xtype == DATAEXT) {
+ /* if the extent is allocated-but-not-recorded
+ * there is no real data to be moved in this extent,
+ */
+ if (xad->flag & XAD_NOTRECORDED)
+ goto out;
+ else
+ /* release xtpage for cmRead()/xtLookup() */
+ XT_PUTPAGE(pmp);
+
+ /*
+ * cmRelocate()
+ *
+ * copy target data pages to be relocated;
+ *
+ * data extent must start at page boundary and
+ * multiple of page size (except the last data extent);
+ * read in each page of the source data extent into cbuf,
+ * update the cbuf extent descriptor of the page to be
+ * homeward bound to new dst data extent
+ * copy the data from the old extent to new extent.
+ * copy is essential for compressed files to avoid problems
+ * that can arise if there was a change in compression
+ * algorithms.
+ * it is a good strategy because it may disrupt cache
+ * policy to keep the pages in memory afterwards.
+ */
+ offset = xoff << JFS_SBI(ip->i_sb)->l2bsize;
+ assert((offset & CM_OFFSET) == 0);
+ nbytes = xlen << JFS_SBI(ip->i_sb)->l2bsize;
+ pno = offset >> CM_L2BSIZE;
+ npages = (nbytes + (CM_BSIZE - 1)) >> CM_L2BSIZE;
+/*
+ npages = ((offset + nbytes - 1) >> CM_L2BSIZE) -
+ (offset >> CM_L2BSIZE) + 1;
+*/
+ sxaddr = oxaddr;
+ dxaddr = nxaddr;
+
+ /* process the request one cache buffer at a time */
+ for (nbrd = 0; nbrd < nbytes; nbrd += nb,
+ offset += nb, pno++, npages--) {
+ /* compute page size */
+ nb = min(nbytes - nbrd, CM_BSIZE);
+
+ /* get the cache buffer of the page */
+ if (rc = cmRead(ip, offset, npages, &cp))
+ break;
+
+ assert(addressPXD(&cp->cm_pxd) == sxaddr);
+ assert(!cp->cm_modified);
+
+ /* bind buffer with the new extent address */
+ nblks = nb >> JFS_IP(ip->i_sb)->l2bsize;
+ cmSetXD(ip, cp, pno, dxaddr, nblks);
+
+ /* release the cbuf, mark it as modified */
+ cmPut(cp, TRUE);
+
+ dxaddr += nblks;
+ sxaddr += nblks;
+ }
+
+ /* get back parent page */
+ if ((rc = xtSearch(ip, xoff, &cmp, &btstack, 0)))
+ return rc;
+
+ XT_GETSEARCH(ip, btstack.top, bn, pmp, pp, index);
+ jfs_info("xtRelocate: target data extent relocated.");
+ } else { /* (xtype == XTPAGE) */
+
+ /*
+ * read in the target xtpage from the source extent;
+ */
+ XT_GETPAGE(ip, oxaddr, mp, PSIZE, p, rc);
+ if (rc) {
+ XT_PUTPAGE(pmp);
+ return rc;
+ }
+
+ /*
+ * read in sibling pages if any to update sibling pointers;
+ */
+ rmp = NULL;
+ if (p->header.next) {
+ nextbn = le64_to_cpu(p->header.next);
+ XT_GETPAGE(ip, nextbn, rmp, PSIZE, rp, rc);
+ if (rc) {
+ XT_PUTPAGE(pmp);
+ XT_PUTPAGE(mp);
+ return (rc);
+ }
+ }
+
+ lmp = NULL;
+ if (p->header.prev) {
+ prevbn = le64_to_cpu(p->header.prev);
+ XT_GETPAGE(ip, prevbn, lmp, PSIZE, lp, rc);
+ if (rc) {
+ XT_PUTPAGE(pmp);
+ XT_PUTPAGE(mp);
+ if (rmp)
+ XT_PUTPAGE(rmp);
+ return (rc);
+ }
+ }
+
+ /* at this point, all xtpages to be updated are in memory */
+
+ /*
+ * update sibling pointers of sibling xtpages if any;
+ */
+ if (lmp) {
+ BT_MARK_DIRTY(lmp, ip);
+ tlck =
+ txLock(tid, ip, lmp, tlckXTREE | tlckRELINK);
+ lp->header.next = cpu_to_le64(nxaddr);
+ XT_PUTPAGE(lmp);
+ }
+
+ if (rmp) {
+ BT_MARK_DIRTY(rmp, ip);
+ tlck =
+ txLock(tid, ip, rmp, tlckXTREE | tlckRELINK);
+ rp->header.prev = cpu_to_le64(nxaddr);
+ XT_PUTPAGE(rmp);
+ }
+
+ /*
+ * update the target xtpage to be relocated
+ *
+ * update the self address of the target page
+ * and write to destination extent;
+ * redo image covers the whole xtpage since it is new page
+ * to the destination extent;
+ * update of bmap for the free of source extent
+ * of the target xtpage itself:
+ * update of bmap for the allocation of destination extent
+ * of the target xtpage itself:
+ * update of bmap for the extents covered by xad entries in
+ * the target xtpage is not necessary since they are not
+ * updated;
+ * if not committed before this relocation,
+ * target page may contain XAD_NEW entries which must
+ * be scanned for bmap update (logredo() always
+ * scan xtpage REDOPAGE image for bmap update);
+ * if committed before this relocation (tlckRELOCATE),
+ * scan may be skipped by commit() and logredo();
+ */
+ BT_MARK_DIRTY(mp, ip);
+ /* tlckNEW init xtlck->lwm.offset = XTENTRYSTART; */
+ tlck = txLock(tid, ip, mp, tlckXTREE | tlckNEW);
+ xtlck = (struct xtlock *) & tlck->lock;
+
+ /* update the self address in the xtpage header */
+ pxd = &p->header.self;
+ PXDaddress(pxd, nxaddr);
+
+ /* linelock for the after image of the whole page */
+ xtlck->lwm.length =
+ le16_to_cpu(p->header.nextindex) - xtlck->lwm.offset;
+
+ /* update the buffer extent descriptor of target xtpage */
+ xsize = xlen << JFS_SBI(ip->i_sb)->l2bsize;
+ bmSetXD(mp, nxaddr, xsize);
+
+ /* unpin the target page to new homeward bound */
+ XT_PUTPAGE(mp);
+ jfs_info("xtRelocate: target xtpage relocated.");
+ }
+
+ /*
+ * 3. acquire maplock for the source extent to be freed;
+ *
+ * acquire a maplock saving the src relocated extent address;
+ * to free of the extent at commit time;
+ */
+ out:
+ /* if DATAEXT relocation, write a LOG_UPDATEMAP record for
+ * free PXD of the source data extent (logredo() will update
+ * bmap for free of source data extent), and update bmap for
+ * free of the source data extent;
+ */
+ if (xtype == DATAEXT)
+ tlck = txMaplock(tid, ip, tlckMAP);
+ /* if XTPAGE relocation, write a LOG_NOREDOPAGE record
+ * for the source xtpage (logredo() will init NoRedoPage
+ * filter and will also update bmap for free of the source
+ * xtpage), and update bmap for free of the source xtpage;
+ * N.B. We use tlckMAP instead of tlkcXTREE because there
+ * is no buffer associated with this lock since the buffer
+ * has been redirected to the target location.
+ */
+ else /* (xtype == XTPAGE) */
+ tlck = txMaplock(tid, ip, tlckMAP | tlckRELOCATE);
+
+ pxdlock = (struct pxd_lock *) & tlck->lock;
+ pxdlock->flag = mlckFREEPXD;
+ PXDaddress(&pxdlock->pxd, oxaddr);
+ PXDlength(&pxdlock->pxd, xlen);
+ pxdlock->index = 1;
+
+ /*
+ * 4. update the parent xad entry for relocation;
+ *
+ * acquire tlck for the parent entry with XAD_NEW as entry
+ * update which will write LOG_REDOPAGE and update bmap for
+ * allocation of XAD_NEW destination extent;
+ */
+ jfs_info("xtRelocate: update parent xad entry.");
+ BT_MARK_DIRTY(pmp, ip);
+ tlck = txLock(tid, ip, pmp, tlckXTREE | tlckGROW);
+ xtlck = (struct xtlock *) & tlck->lock;
+
+ /* update the XAD with the new destination extent; */
+ xad = &pp->xad[index];
+ xad->flag |= XAD_NEW;
+ XADaddress(xad, nxaddr);
+
+ xtlck->lwm.offset = min(index, xtlck->lwm.offset);
+ xtlck->lwm.length = le16_to_cpu(pp->header.nextindex) -
+ xtlck->lwm.offset;
+
+ /* unpin the parent xtpage */
+ XT_PUTPAGE(pmp);
+
+ return rc;
+}
+
+
+/*
+ * xtSearchNode()
+ *
+ * function: search for the internal xad entry covering specified extent.
+ * This function is mainly used by defragfs utility.
+ *
+ * parameters:
+ * ip - file object;
+ * xad - extent to find;
+ * cmpp - comparison result:
+ * btstack - traverse stack;
+ * flag - search process flag;
+ *
+ * returns:
+ * btstack contains (bn, index) of search path traversed to the entry.
+ * *cmpp is set to result of comparison with the entry returned.
+ * the page containing the entry is pinned at exit.
+ */
+static int xtSearchNode(struct inode *ip, xad_t * xad, /* required XAD entry */
+ int *cmpp, struct btstack * btstack, int flag)
+{
+ int rc = 0;
+ s64 xoff, xaddr;
+ int xlen;
+ int cmp = 1; /* init for empty page */
+ s64 bn; /* block number */
+ struct metapage *mp; /* meta-page buffer */
+ xtpage_t *p; /* page */
+ int base, index, lim;
+ struct btframe *btsp;
+ s64 t64;
+
+ BT_CLR(btstack);
+
+ xoff = offsetXAD(xad);
+ xlen = lengthXAD(xad);
+ xaddr = addressXAD(xad);
+
+ /*
+ * search down tree from root:
+ *
+ * between two consecutive entries of <Ki, Pi> and <Kj, Pj> of
+ * internal page, child page Pi contains entry with k, Ki <= K < Kj.
+ *
+ * if entry with search key K is not found
+ * internal page search find the entry with largest key Ki
+ * less than K which point to the child page to search;
+ * leaf page search find the entry with smallest key Kj
+ * greater than K so that the returned index is the position of
+ * the entry to be shifted right for insertion of new entry.
+ * for empty tree, search key is greater than any key of the tree.
+ *
+ * by convention, root bn = 0.
+ */
+ for (bn = 0;;) {
+ /* get/pin the page to search */
+ XT_GETPAGE(ip, bn, mp, PSIZE, p, rc);
+ if (rc)
+ return rc;
+ if (p->header.flag & BT_LEAF) {
+ XT_PUTPAGE(mp);
+ return -ESTALE;
+ }
+
+ lim = le16_to_cpu(p->header.nextindex) - XTENTRYSTART;
+
+ /*
+ * binary search with search key K on the current page
+ */
+ for (base = XTENTRYSTART; lim; lim >>= 1) {
+ index = base + (lim >> 1);
+
+ XT_CMP(cmp, xoff, &p->xad[index], t64);
+ if (cmp == 0) {
+ /*
+ * search hit
+ *
+ * verify for exact match;
+ */
+ if (xaddr == addressXAD(&p->xad[index]) &&
+ xoff == offsetXAD(&p->xad[index])) {
+ *cmpp = cmp;
+
+ /* save search result */
+ btsp = btstack->top;
+ btsp->bn = bn;
+ btsp->index = index;
+ btsp->mp = mp;
+
+ return 0;
+ }
+
+ /* descend/search its child page */
+ goto next;
+ }
+
+ if (cmp > 0) {
+ base = index + 1;
+ --lim;
+ }
+ }
+
+ /*
+ * search miss - non-leaf page:
+ *
+ * base is the smallest index with key (Kj) greater than
+ * search key (K) and may be zero or maxentry index.
+ * if base is non-zero, decrement base by one to get the parent
+ * entry of the child page to search.
+ */
+ index = base ? base - 1 : base;
+
+ /*
+ * go down to child page
+ */
+ next:
+ /* get the child page block number */
+ bn = addressXAD(&p->xad[index]);
+
+ /* unpin the parent page */
+ XT_PUTPAGE(mp);
+ }
+}
+
+
+/*
+ * xtRelink()
+ *
+ * function:
+ * link around a freed page.
+ *
+ * Parameter:
+ * int tid,
+ * struct inode *ip,
+ * xtpage_t *p)
+ *
+ * returns:
+ */
+static int xtRelink(tid_t tid, struct inode *ip, xtpage_t * p)
+{
+ int rc = 0;
+ struct metapage *mp;
+ s64 nextbn, prevbn;
+ struct tlock *tlck;
+
+ nextbn = le64_to_cpu(p->header.next);
+ prevbn = le64_to_cpu(p->header.prev);
+
+ /* update prev pointer of the next page */
+ if (nextbn != 0) {
+ XT_GETPAGE(ip, nextbn, mp, PSIZE, p, rc);
+ if (rc)
+ return rc;
+
+ /*
+ * acquire a transaction lock on the page;
+ *
+ * action: update prev pointer;
+ */
+ BT_MARK_DIRTY(mp, ip);
+ tlck = txLock(tid, ip, mp, tlckXTREE | tlckRELINK);
+
+ /* the page may already have been tlock'd */
+
+ p->header.prev = cpu_to_le64(prevbn);
+
+ XT_PUTPAGE(mp);
+ }
+
+ /* update next pointer of the previous page */
+ if (prevbn != 0) {
+ XT_GETPAGE(ip, prevbn, mp, PSIZE, p, rc);
+ if (rc)
+ return rc;
+
+ /*
+ * acquire a transaction lock on the page;
+ *
+ * action: update next pointer;
+ */
+ BT_MARK_DIRTY(mp, ip);
+ tlck = txLock(tid, ip, mp, tlckXTREE | tlckRELINK);
+
+ /* the page may already have been tlock'd */
+
+ p->header.next = le64_to_cpu(nextbn);
+
+ XT_PUTPAGE(mp);
+ }
+
+ return 0;
+}
+#endif /* _STILL_TO_PORT */
+
+
+/*
+ * xtInitRoot()
+ *
+ * initialize file root (inline in inode)
+ */
+void xtInitRoot(tid_t tid, struct inode *ip)
+{
+ xtpage_t *p;
+
+ /*
+ * acquire a transaction lock on the root
+ *
+ * action:
+ */
+ txLock(tid, ip, (struct metapage *) &JFS_IP(ip)->bxflag,
+ tlckXTREE | tlckNEW);
+ p = &JFS_IP(ip)->i_xtroot;
+
+ p->header.flag = DXD_INDEX | BT_ROOT | BT_LEAF;
+ p->header.nextindex = cpu_to_le16(XTENTRYSTART);
+
+ if (S_ISDIR(ip->i_mode))
+ p->header.maxentry = cpu_to_le16(XTROOTINITSLOT_DIR);
+ else {
+ p->header.maxentry = cpu_to_le16(XTROOTINITSLOT);
+ ip->i_size = 0;
+ }
+
+
+ return;
+}
+
+
+/*
+ * We can run into a deadlock truncating a file with a large number of
+ * xtree pages (large fragmented file). A robust fix would entail a
+ * reservation system where we would reserve a number of metadata pages
+ * and tlocks which we would be guaranteed without a deadlock. Without
+ * this, a partial fix is to limit number of metadata pages we will lock
+ * in a single transaction. Currently we will truncate the file so that
+ * no more than 50 leaf pages will be locked. The caller of xtTruncate
+ * will be responsible for ensuring that the current transaction gets
+ * committed, and that subsequent transactions are created to truncate
+ * the file further if needed.
+ */
+#define MAX_TRUNCATE_LEAVES 50
+
+/*
+ * xtTruncate()
+ *
+ * function:
+ * traverse for truncation logging backward bottom up;
+ * terminate at the last extent entry at the current subtree
+ * root page covering new down size.
+ * truncation may occur within the last extent entry.
+ *
+ * parameter:
+ * int tid,
+ * struct inode *ip,
+ * s64 newsize,
+ * int type) {PWMAP, PMAP, WMAP; DELETE, TRUNCATE}
+ *
+ * return:
+ *
+ * note:
+ * PWMAP:
+ * 1. truncate (non-COMMIT_NOLINK file)
+ * by jfs_truncate() or jfs_open(O_TRUNC):
+ * xtree is updated;
+ * 2. truncate index table of directory when last entry removed
+ * map update via tlock at commit time;
+ * PMAP:
+ * Call xtTruncate_pmap instead
+ * WMAP:
+ * 1. remove (free zero link count) on last reference release
+ * (pmap has been freed at commit zero link count);
+ * 2. truncate (COMMIT_NOLINK file, i.e., tmp file):
+ * xtree is updated;
+ * map update directly at truncation time;
+ *
+ * if (DELETE)
+ * no LOG_NOREDOPAGE is required (NOREDOFILE is sufficient);
+ * else if (TRUNCATE)
+ * must write LOG_NOREDOPAGE for deleted index page;
+ *
+ * pages may already have been tlocked by anonymous transactions
+ * during file growth (i.e., write) before truncation;
+ *
+ * except last truncated entry, deleted entries remains as is
+ * in the page (nextindex is updated) for other use
+ * (e.g., log/update allocation map): this avoid copying the page
+ * info but delay free of pages;
+ *
+ */
+s64 xtTruncate(tid_t tid, struct inode *ip, s64 newsize, int flag)
+{
+ int rc = 0;
+ s64 teof;
+ struct metapage *mp;
+ xtpage_t *p;
+ s64 bn;
+ int index, nextindex;
+ xad_t *xad;
+ s64 xoff, xaddr;
+ int xlen, len, freexlen;
+ struct btstack btstack;
+ struct btframe *parent;
+ struct tblock *tblk = 0;
+ struct tlock *tlck = 0;
+ struct xtlock *xtlck = 0;
+ struct xdlistlock xadlock; /* maplock for COMMIT_WMAP */
+ struct pxd_lock *pxdlock; /* maplock for COMMIT_WMAP */
+ s64 nfreed;
+ int freed, log;
+ int locked_leaves = 0;
+
+ /* save object truncation type */
+ if (tid) {
+ tblk = tid_to_tblock(tid);
+ tblk->xflag |= flag;
+ }
+
+ nfreed = 0;
+
+ flag &= COMMIT_MAP;
+ assert(flag != COMMIT_PMAP);
+
+ if (flag == COMMIT_PWMAP)
+ log = 1;
+ else {
+ log = 0;
+ xadlock.flag = mlckFREEXADLIST;
+ xadlock.index = 1;
+ }
+
+ /*
+ * if the newsize is not an integral number of pages,
+ * the file between newsize and next page boundary will
+ * be cleared.
+ * if truncating into a file hole, it will cause
+ * a full block to be allocated for the logical block.
+ */
+
+ /*
+ * release page blocks of truncated region <teof, eof>
+ *
+ * free the data blocks from the leaf index blocks.
+ * delete the parent index entries corresponding to
+ * the freed child data/index blocks.
+ * free the index blocks themselves which aren't needed
+ * in new sized file.
+ *
+ * index blocks are updated only if the blocks are to be
+ * retained in the new sized file.
+ * if type is PMAP, the data and index pages are NOT
+ * freed, and the data and index blocks are NOT freed
+ * from working map.
+ * (this will allow continued access of data/index of
+ * temporary file (zerolink count file truncated to zero-length)).
+ */
+ teof = (newsize + (JFS_SBI(ip->i_sb)->bsize - 1)) >>
+ JFS_SBI(ip->i_sb)->l2bsize;
+
+ /* clear stack */
+ BT_CLR(&btstack);
+
+ /*
+ * start with root
+ *
+ * root resides in the inode
+ */
+ bn = 0;
+
+ /*
+ * first access of each page:
+ */
+ getPage:
+ XT_GETPAGE(ip, bn, mp, PSIZE, p, rc);
+ if (rc)
+ return rc;
+
+ /* process entries backward from last index */
+ index = le16_to_cpu(p->header.nextindex) - 1;
+
+ if (p->header.flag & BT_INTERNAL)
+ goto getChild;
+
+ /*
+ * leaf page
+ */
+
+ /* Since this is the rightmost leaf, and we may have already freed
+ * a page that was formerly to the right, let's make sure that the
+ * next pointer is zero.
+ */
+ if (p->header.next) {
+ if (log)
+ /*
+ * Make sure this change to the header is logged.
+ * If we really truncate this leaf, the flag
+ * will be changed to tlckTRUNCATE
+ */
+ tlck = txLock(tid, ip, mp, tlckXTREE|tlckGROW);
+ BT_MARK_DIRTY(mp, ip);
+ p->header.next = 0;
+ }
+
+ freed = 0;
+
+ /* does region covered by leaf page precede Teof ? */
+ xad = &p->xad[index];
+ xoff = offsetXAD(xad);
+ xlen = lengthXAD(xad);
+ if (teof >= xoff + xlen) {
+ XT_PUTPAGE(mp);
+ goto getParent;
+ }
+
+ /* (re)acquire tlock of the leaf page */
+ if (log) {
+ if (++locked_leaves > MAX_TRUNCATE_LEAVES) {
+ /*
+ * We need to limit the size of the transaction
+ * to avoid exhausting pagecache & tlocks
+ */
+ XT_PUTPAGE(mp);
+ newsize = (xoff + xlen) << JFS_SBI(ip->i_sb)->l2bsize;
+ goto getParent;
+ }
+ tlck = txLock(tid, ip, mp, tlckXTREE);
+ tlck->type = tlckXTREE | tlckTRUNCATE;
+ xtlck = (struct xtlock *) & tlck->lock;
+ xtlck->hwm.offset = le16_to_cpu(p->header.nextindex) - 1;
+ }
+ BT_MARK_DIRTY(mp, ip);
+
+ /*
+ * scan backward leaf page entries
+ */
+ for (; index >= XTENTRYSTART; index--) {
+ xad = &p->xad[index];
+ xoff = offsetXAD(xad);
+ xlen = lengthXAD(xad);
+ xaddr = addressXAD(xad);
+
+ /*
+ * The "data" for a directory is indexed by the block
+ * device's address space. This metadata must be invalidated
+ * here
+ */
+ if (S_ISDIR(ip->i_mode) && (teof == 0))
+ invalidate_xad_metapages(ip, *xad);
+ /*
+ * entry beyond eof: continue scan of current page
+ * xad
+ * ---|---=======------->
+ * eof
+ */
+ if (teof < xoff) {
+ nfreed += xlen;
+ continue;
+ }
+
+ /*
+ * (xoff <= teof): last entry to be deleted from page;
+ * If other entries remain in page: keep and update the page.
+ */
+
+ /*
+ * eof == entry_start: delete the entry
+ * xad
+ * -------|=======------->
+ * eof
+ *
+ */
+ if (teof == xoff) {
+ nfreed += xlen;
+
+ if (index == XTENTRYSTART)
+ break;
+
+ nextindex = index;
+ }
+ /*
+ * eof within the entry: truncate the entry.
+ * xad
+ * -------===|===------->
+ * eof
+ */
+ else if (teof < xoff + xlen) {
+ /* update truncated entry */
+ len = teof - xoff;
+ freexlen = xlen - len;
+ XADlength(xad, len);
+
+ /* save pxd of truncated extent in tlck */
+ xaddr += len;
+ if (log) { /* COMMIT_PWMAP */
+ xtlck->lwm.offset = (xtlck->lwm.offset) ?
+ min(index, (int)xtlck->lwm.offset) : index;
+ xtlck->lwm.length = index + 1 -
+ xtlck->lwm.offset;
+ xtlck->twm.offset = index;
+ pxdlock = (struct pxd_lock *) & xtlck->pxdlock;
+ pxdlock->flag = mlckFREEPXD;
+ PXDaddress(&pxdlock->pxd, xaddr);
+ PXDlength(&pxdlock->pxd, freexlen);
+ }
+ /* free truncated extent */
+ else { /* COMMIT_WMAP */
+
+ pxdlock = (struct pxd_lock *) & xadlock;
+ pxdlock->flag = mlckFREEPXD;
+ PXDaddress(&pxdlock->pxd, xaddr);
+ PXDlength(&pxdlock->pxd, freexlen);
+ txFreeMap(ip, pxdlock, 0, COMMIT_WMAP);
+
+ /* reset map lock */
+ xadlock.flag = mlckFREEXADLIST;
+ }
+
+ /* current entry is new last entry; */
+ nextindex = index + 1;
+
+ nfreed += freexlen;
+ }
+ /*
+ * eof beyond the entry:
+ * xad
+ * -------=======---|--->
+ * eof
+ */
+ else { /* (xoff + xlen < teof) */
+
+ nextindex = index + 1;
+ }
+
+ if (nextindex < le16_to_cpu(p->header.nextindex)) {
+ if (!log) { /* COMMIT_WAMP */
+ xadlock.xdlist = &p->xad[nextindex];
+ xadlock.count =
+ le16_to_cpu(p->header.nextindex) -
+ nextindex;
+ txFreeMap(ip, (struct maplock *) & xadlock, 0,
+ COMMIT_WMAP);
+ }
+ p->header.nextindex = cpu_to_le16(nextindex);
+ }
+
+ XT_PUTPAGE(mp);
+
+ /* assert(freed == 0); */
+ goto getParent;
+ } /* end scan of leaf page entries */
+
+ freed = 1;
+
+ /*
+ * leaf page become empty: free the page if type != PMAP
+ */
+ if (log) { /* COMMIT_PWMAP */
+ /* txCommit() with tlckFREE:
+ * free data extents covered by leaf [XTENTRYSTART:hwm);
+ * invalidate leaf if COMMIT_PWMAP;
+ * if (TRUNCATE), will write LOG_NOREDOPAGE;
+ */
+ tlck->type = tlckXTREE | tlckFREE;
+ } else { /* COMMIT_WAMP */
+
+ /* free data extents covered by leaf */
+ xadlock.xdlist = &p->xad[XTENTRYSTART];
+ xadlock.count =
+ le16_to_cpu(p->header.nextindex) - XTENTRYSTART;
+ txFreeMap(ip, (struct maplock *) & xadlock, 0, COMMIT_WMAP);
+ }
+
+ if (p->header.flag & BT_ROOT) {
+ p->header.flag &= ~BT_INTERNAL;
+ p->header.flag |= BT_LEAF;
+ p->header.nextindex = cpu_to_le16(XTENTRYSTART);
+
+ XT_PUTPAGE(mp); /* debug */
+ goto out;
+ } else {
+ if (log) { /* COMMIT_PWMAP */
+ /* page will be invalidated at tx completion
+ */
+ XT_PUTPAGE(mp);
+ } else { /* COMMIT_WMAP */
+
+ if (mp->lid)
+ lid_to_tlock(mp->lid)->flag |= tlckFREELOCK;
+
+ /* invalidate empty leaf page */
+ discard_metapage(mp);
+ }
+ }
+
+ /*
+ * the leaf page become empty: delete the parent entry
+ * for the leaf page if the parent page is to be kept
+ * in the new sized file.
+ */
+
+ /*
+ * go back up to the parent page
+ */
+ getParent:
+ /* pop/restore parent entry for the current child page */
+ if ((parent = BT_POP(&btstack)) == NULL)
+ /* current page must have been root */
+ goto out;
+
+ /* get back the parent page */
+ bn = parent->bn;
+ XT_GETPAGE(ip, bn, mp, PSIZE, p, rc);
+ if (rc)
+ return rc;
+
+ index = parent->index;
+
+ /*
+ * child page was not empty:
+ */
+ if (freed == 0) {
+ /* has any entry deleted from parent ? */
+ if (index < le16_to_cpu(p->header.nextindex) - 1) {
+ /* (re)acquire tlock on the parent page */
+ if (log) { /* COMMIT_PWMAP */
+ /* txCommit() with tlckTRUNCATE:
+ * free child extents covered by parent [);
+ */
+ tlck = txLock(tid, ip, mp, tlckXTREE);
+ xtlck = (struct xtlock *) & tlck->lock;
+ if (!(tlck->type & tlckTRUNCATE)) {
+ xtlck->hwm.offset =
+ le16_to_cpu(p->header.
+ nextindex) - 1;
+ tlck->type =
+ tlckXTREE | tlckTRUNCATE;
+ }
+ } else { /* COMMIT_WMAP */
+
+ /* free child extents covered by parent */
+ xadlock.xdlist = &p->xad[index + 1];
+ xadlock.count =
+ le16_to_cpu(p->header.nextindex) -
+ index - 1;
+ txFreeMap(ip, (struct maplock *) & xadlock, 0,
+ COMMIT_WMAP);
+ }
+ BT_MARK_DIRTY(mp, ip);
+
+ p->header.nextindex = cpu_to_le16(index + 1);
+ }
+ XT_PUTPAGE(mp);
+ goto getParent;
+ }
+
+ /*
+ * child page was empty:
+ */
+ nfreed += lengthXAD(&p->xad[index]);
+
+ /*
+ * During working map update, child page's tlock must be handled
+ * before parent's. This is because the parent's tlock will cause
+ * the child's disk space to be marked available in the wmap, so
+ * it's important that the child page be released by that time.
+ *
+ * ToDo: tlocks should be on doubly-linked list, so we can
+ * quickly remove it and add it to the end.
+ */
+
+ /*
+ * Move parent page's tlock to the end of the tid's tlock list
+ */
+ if (log && mp->lid && (tblk->last != mp->lid) &&
+ lid_to_tlock(mp->lid)->tid) {
+ lid_t lid = mp->lid;
+ struct tlock *prev;
+
+ tlck = lid_to_tlock(lid);
+
+ if (tblk->next == lid)
+ tblk->next = tlck->next;
+ else {
+ for (prev = lid_to_tlock(tblk->next);
+ prev->next != lid;
+ prev = lid_to_tlock(prev->next)) {
+ assert(prev->next);
+ }
+ prev->next = tlck->next;
+ }
+ lid_to_tlock(tblk->last)->next = lid;
+ tlck->next = 0;
+ tblk->last = lid;
+ }
+
+ /*
+ * parent page become empty: free the page
+ */
+ if (index == XTENTRYSTART) {
+ if (log) { /* COMMIT_PWMAP */
+ /* txCommit() with tlckFREE:
+ * free child extents covered by parent;
+ * invalidate parent if COMMIT_PWMAP;
+ */
+ tlck = txLock(tid, ip, mp, tlckXTREE);
+ xtlck = (struct xtlock *) & tlck->lock;
+ xtlck->hwm.offset =
+ le16_to_cpu(p->header.nextindex) - 1;
+ tlck->type = tlckXTREE | tlckFREE;
+ } else { /* COMMIT_WMAP */
+
+ /* free child extents covered by parent */
+ xadlock.xdlist = &p->xad[XTENTRYSTART];
+ xadlock.count =
+ le16_to_cpu(p->header.nextindex) -
+ XTENTRYSTART;
+ txFreeMap(ip, (struct maplock *) & xadlock, 0,
+ COMMIT_WMAP);
+ }
+ BT_MARK_DIRTY(mp, ip);
+
+ if (p->header.flag & BT_ROOT) {
+ p->header.flag &= ~BT_INTERNAL;
+ p->header.flag |= BT_LEAF;
+ p->header.nextindex = cpu_to_le16(XTENTRYSTART);
+ if (le16_to_cpu(p->header.maxentry) == XTROOTMAXSLOT) {
+ /*
+ * Shrink root down to allow inline
+ * EA (otherwise fsck complains)
+ */
+ p->header.maxentry =
+ cpu_to_le16(XTROOTINITSLOT);
+ JFS_IP(ip)->mode2 |= INLINEEA;
+ }
+
+ XT_PUTPAGE(mp); /* debug */
+ goto out;
+ } else {
+ if (log) { /* COMMIT_PWMAP */
+ /* page will be invalidated at tx completion
+ */
+ XT_PUTPAGE(mp);
+ } else { /* COMMIT_WMAP */
+
+ if (mp->lid)
+ lid_to_tlock(mp->lid)->flag |=
+ tlckFREELOCK;
+
+ /* invalidate parent page */
+ discard_metapage(mp);
+ }
+
+ /* parent has become empty and freed:
+ * go back up to its parent page
+ */
+ /* freed = 1; */
+ goto getParent;
+ }
+ }
+ /*
+ * parent page still has entries for front region;
+ */
+ else {
+ /* try truncate region covered by preceding entry
+ * (process backward)
+ */
+ index--;
+
+ /* go back down to the child page corresponding
+ * to the entry
+ */
+ goto getChild;
+ }
+
+ /*
+ * internal page: go down to child page of current entry
+ */
+ getChild:
+ /* save current parent entry for the child page */
+ BT_PUSH(&btstack, bn, index);
+
+ /* get child page */
+ xad = &p->xad[index];
+ bn = addressXAD(xad);
+
+ /*
+ * first access of each internal entry:
+ */
+ /* release parent page */
+ XT_PUTPAGE(mp);
+
+ /* process the child page */
+ goto getPage;
+
+ out:
+ /*
+ * update file resource stat
+ */
+ /* set size
+ */
+ if (S_ISDIR(ip->i_mode) && !newsize)
+ ip->i_size = 1; /* fsck hates zero-length directories */
+ else
+ ip->i_size = newsize;
+
+ /* update nblocks to reflect freed blocks */
+ ip->i_blocks -= LBLK2PBLK(ip->i_sb, nfreed);
+
+ /*
+ * free tlock of invalidated pages
+ */
+ if (flag == COMMIT_WMAP)
+ txFreelock(ip);
+
+ return newsize;
+}
+
+
+/*
+ * xtTruncate_pmap()
+ *
+ * function:
+ * Perform truncate to zero lenghth for deleted file, leaving the
+ * the xtree and working map untouched. This allows the file to
+ * be accessed via open file handles, while the delete of the file
+ * is committed to disk.
+ *
+ * parameter:
+ * tid_t tid,
+ * struct inode *ip,
+ * s64 committed_size)
+ *
+ * return: new committed size
+ *
+ * note:
+ *
+ * To avoid deadlock by holding too many transaction locks, the
+ * truncation may be broken up into multiple transactions.
+ * The committed_size keeps track of part of the file has been
+ * freed from the pmaps.
+ */
+s64 xtTruncate_pmap(tid_t tid, struct inode *ip, s64 committed_size)
+{
+ s64 bn;
+ struct btstack btstack;
+ int cmp;
+ int index;
+ int locked_leaves = 0;
+ struct metapage *mp;
+ xtpage_t *p;
+ struct btframe *parent;
+ int rc;
+ struct tblock *tblk;
+ struct tlock *tlck = 0;
+ xad_t *xad;
+ int xlen;
+ s64 xoff;
+ struct xtlock *xtlck = 0;
+
+ /* save object truncation type */
+ tblk = tid_to_tblock(tid);
+ tblk->xflag |= COMMIT_PMAP;
+
+ /* clear stack */
+ BT_CLR(&btstack);
+
+ if (committed_size) {
+ xoff = (committed_size >> JFS_SBI(ip->i_sb)->l2bsize) - 1;
+ rc = xtSearch(ip, xoff, &cmp, &btstack, 0);
+ if (rc)
+ return rc;
+
+ XT_GETSEARCH(ip, btstack.top, bn, mp, p, index);
+
+ if (cmp != 0) {
+ XT_PUTPAGE(mp);
+ jfs_error(ip->i_sb,
+ "xtTruncate_pmap: did not find extent");
+ return -EIO;
+ }
+ } else {
+ /*
+ * start with root
+ *
+ * root resides in the inode
+ */
+ bn = 0;
+
+ /*
+ * first access of each page:
+ */
+ getPage:
+ XT_GETPAGE(ip, bn, mp, PSIZE, p, rc);
+ if (rc)
+ return rc;
+
+ /* process entries backward from last index */
+ index = le16_to_cpu(p->header.nextindex) - 1;
+
+ if (p->header.flag & BT_INTERNAL)
+ goto getChild;
+ }
+
+ /*
+ * leaf page
+ */
+
+ if (++locked_leaves > MAX_TRUNCATE_LEAVES) {
+ /*
+ * We need to limit the size of the transaction
+ * to avoid exhausting pagecache & tlocks
+ */
+ xad = &p->xad[index];
+ xoff = offsetXAD(xad);
+ xlen = lengthXAD(xad);
+ XT_PUTPAGE(mp);
+ return (xoff + xlen) << JFS_SBI(ip->i_sb)->l2bsize;
+ }
+ tlck = txLock(tid, ip, mp, tlckXTREE);
+ tlck->type = tlckXTREE | tlckFREE;
+ xtlck = (struct xtlock *) & tlck->lock;
+ xtlck->hwm.offset = index;
+
+
+ XT_PUTPAGE(mp);
+
+ /*
+ * go back up to the parent page
+ */
+ getParent:
+ /* pop/restore parent entry for the current child page */
+ if ((parent = BT_POP(&btstack)) == NULL)
+ /* current page must have been root */
+ goto out;
+
+ /* get back the parent page */
+ bn = parent->bn;
+ XT_GETPAGE(ip, bn, mp, PSIZE, p, rc);
+ if (rc)
+ return rc;
+
+ index = parent->index;
+
+ /*
+ * parent page become empty: free the page
+ */
+ if (index == XTENTRYSTART) {
+ /* txCommit() with tlckFREE:
+ * free child extents covered by parent;
+ * invalidate parent if COMMIT_PWMAP;
+ */
+ tlck = txLock(tid, ip, mp, tlckXTREE);
+ xtlck = (struct xtlock *) & tlck->lock;
+ xtlck->hwm.offset =
+ le16_to_cpu(p->header.nextindex) - 1;
+ tlck->type = tlckXTREE | tlckFREE;
+
+ XT_PUTPAGE(mp);
+
+ if (p->header.flag & BT_ROOT) {
+
+ goto out;
+ } else {
+ goto getParent;
+ }
+ }
+ /*
+ * parent page still has entries for front region;
+ */
+ else
+ index--;
+ /*
+ * internal page: go down to child page of current entry
+ */
+ getChild:
+ /* save current parent entry for the child page */
+ BT_PUSH(&btstack, bn, index);
+
+ /* get child page */
+ xad = &p->xad[index];
+ bn = addressXAD(xad);
+
+ /*
+ * first access of each internal entry:
+ */
+ /* release parent page */
+ XT_PUTPAGE(mp);
+
+ /* process the child page */
+ goto getPage;
+
+ out:
+
+ return 0;
+}
+
+
+#ifdef _JFS_DEBUG_XTREE
+/*
+ * xtDisplayTree()
+ *
+ * function: traverse forward
+ */
+int xtDisplayTree(struct inode *ip)
+{
+ int rc = 0;
+ struct metapage *mp;
+ xtpage_t *p;
+ s64 bn, pbn;
+ int index, lastindex, v, h;
+ xad_t *xad;
+ struct btstack btstack;
+ struct btframe *btsp;
+ struct btframe *parent;
+
+ printk("display B+-tree.\n");
+
+ /* clear stack */
+ btsp = btstack.stack;
+
+ /*
+ * start with root
+ *
+ * root resides in the inode
+ */
+ bn = 0;
+ v = h = 0;
+
+ /*
+ * first access of each page:
+ */
+ getPage:
+ XT_GETPAGE(ip, bn, mp, PSIZE, p, rc);
+ if (rc)
+ return rc;
+
+ /* process entries forward from first index */
+ index = XTENTRYSTART;
+ lastindex = le16_to_cpu(p->header.nextindex) - 1;
+
+ if (p->header.flag & BT_INTERNAL) {
+ /*
+ * first access of each internal page
+ */
+ goto getChild;
+ } else { /* (p->header.flag & BT_LEAF) */
+
+ /*
+ * first access of each leaf page
+ */
+ printf("leaf page ");
+ xtDisplayPage(ip, bn, p);
+
+ /* unpin the leaf page */
+ XT_PUTPAGE(mp);
+ }
+
+ /*
+ * go back up to the parent page
+ */
+ getParent:
+ /* pop/restore parent entry for the current child page */
+ if ((parent = (btsp == btstack.stack ? NULL : --btsp)) == NULL)
+ /* current page must have been root */
+ return;
+
+ /*
+ * parent page scan completed
+ */
+ if ((index = parent->index) == (lastindex = parent->lastindex)) {
+ /* go back up to the parent page */
+ goto getParent;
+ }
+
+ /*
+ * parent page has entries remaining
+ */
+ /* get back the parent page */
+ bn = parent->bn;
+ /* v = parent->level; */
+ XT_GETPAGE(ip, bn, mp, PSIZE, p, rc);
+ if (rc)
+ return rc;
+
+ /* get next parent entry */
+ index++;
+
+ /*
+ * internal page: go down to child page of current entry
+ */
+ getChild:
+ /* push/save current parent entry for the child page */
+ btsp->bn = pbn = bn;
+ btsp->index = index;
+ btsp->lastindex = lastindex;
+ /* btsp->level = v; */
+ /* btsp->node = h; */
+ ++btsp;
+
+ /* get child page */
+ xad = &p->xad[index];
+ bn = addressXAD(xad);
+
+ /*
+ * first access of each internal entry:
+ */
+ /* release parent page */
+ XT_PUTPAGE(mp);
+
+ printk("traverse down 0x%lx[%d]->0x%lx\n", (ulong) pbn, index,
+ (ulong) bn);
+ v++;
+ h = index;
+
+ /* process the child page */
+ goto getPage;
+}
+
+
+/*
+ * xtDisplayPage()
+ *
+ * function: display page
+ */
+int xtDisplayPage(struct inode *ip, s64 bn, xtpage_t * p)
+{
+ int rc = 0;
+ xad_t *xad;
+ s64 xaddr, xoff;
+ int xlen, i, j;
+
+ /* display page control */
+ printf("bn:0x%lx flag:0x%x nextindex:%d\n",
+ (ulong) bn, p->header.flag,
+ le16_to_cpu(p->header.nextindex));
+
+ /* display entries */
+ xad = &p->xad[XTENTRYSTART];
+ for (i = XTENTRYSTART, j = 1; i < le16_to_cpu(p->header.nextindex);
+ i++, xad++, j++) {
+ xoff = offsetXAD(xad);
+ xaddr = addressXAD(xad);
+ xlen = lengthXAD(xad);
+ printf("\t[%d] 0x%lx:0x%lx(0x%x)", i, (ulong) xoff,
+ (ulong) xaddr, xlen);
+
+ if (j == 4) {
+ printf("\n");
+ j = 0;
+ }
+ }
+
+ printf("\n");
+}
+#endif /* _JFS_DEBUG_XTREE */
+
+
+#ifdef _JFS_WIP
+/*
+ * xtGather()
+ *
+ * function:
+ * traverse for allocation acquiring tlock at commit time
+ * (vs at the time of update) logging backward top down
+ *
+ * note:
+ * problem - establishing that all new allocation have been
+ * processed both for append and random write in sparse file
+ * at the current entry at the current subtree root page
+ *
+ */
+int xtGather(btree_t *t)
+{
+ int rc = 0;
+ xtpage_t *p;
+ u64 bn;
+ int index;
+ btentry_t *e;
+ struct btstack btstack;
+ struct btsf *parent;
+
+ /* clear stack */
+ BT_CLR(&btstack);
+
+ /*
+ * start with root
+ *
+ * root resides in the inode
+ */
+ bn = 0;
+ XT_GETPAGE(ip, bn, mp, PSIZE, p, rc);
+ if (rc)
+ return rc;
+
+ /* new root is NOT pointed by a new entry
+ if (p->header.flag & NEW)
+ allocate new page lock;
+ write a NEWPAGE log;
+ */
+
+ dopage:
+ /*
+ * first access of each page:
+ */
+ /* process entries backward from last index */
+ index = le16_to_cpu(p->header.nextindex) - 1;
+
+ if (p->header.flag & BT_LEAF) {
+ /*
+ * first access of each leaf page
+ */
+ /* process leaf page entries backward */
+ for (; index >= XTENTRYSTART; index--) {
+ e = &p->xad[index];
+ /*
+ * if newpage, log NEWPAGE.
+ *
+ if (e->flag & XAD_NEW) {
+ nfound =+ entry->length;
+ update current page lock for the entry;
+ newpage(entry);
+ *
+ * if moved, log move.
+ *
+ } else if (e->flag & XAD_MOVED) {
+ reset flag;
+ update current page lock for the entry;
+ }
+ */
+ }
+
+ /* unpin the leaf page */
+ XT_PUTPAGE(mp);
+
+ /*
+ * go back up to the parent page
+ */
+ getParent:
+ /* restore parent entry for the current child page */
+ if ((parent = BT_POP(&btstack)) == NULL)
+ /* current page must have been root */
+ return 0;
+
+ if ((index = parent->index) == XTENTRYSTART) {
+ /*
+ * parent page scan completed
+ */
+ /* go back up to the parent page */
+ goto getParent;
+ } else {
+ /*
+ * parent page has entries remaining
+ */
+ /* get back the parent page */
+ bn = parent->bn;
+ XT_GETPAGE(ip, bn, mp, PSIZE, p, rc);
+ if (rc)
+ return -EIO;
+
+ /* first subroot page which
+ * covers all new allocated blocks
+ * itself not new/modified.
+ * (if modified from split of descendent,
+ * go down path of split page)
+
+ if (nfound == nnew &&
+ !(p->header.flag & (NEW | MOD)))
+ exit scan;
+ */
+
+ /* process parent page entries backward */
+ index--;
+ }
+ } else {
+ /*
+ * first access of each internal page
+ */
+ }
+
+ /*
+ * internal page: go down to child page of current entry
+ */
+
+ /* save current parent entry for the child page */
+ BT_PUSH(&btstack, bn, index);
+
+ /* get current entry for the child page */
+ e = &p->xad[index];
+
+ /*
+ * first access of each internal entry:
+ */
+ /*
+ * if new entry, log btree_tnewentry.
+ *
+ if (e->flag & XAD_NEW)
+ update parent page lock for the entry;
+ */
+
+ /* release parent page */
+ XT_PUTPAGE(mp);
+
+ /* get child page */
+ bn = e->bn;
+ XT_GETPAGE(ip, bn, mp, PSIZE, p, rc);
+ if (rc)
+ return rc;
+
+ /*
+ * first access of each non-root page:
+ */
+ /*
+ * if new, log btree_newpage.
+ *
+ if (p->header.flag & NEW)
+ allocate new page lock;
+ write a NEWPAGE log (next, prev);
+ */
+
+ /* process the child page */
+ goto dopage;
+
+ out:
+ return 0;
+}
+#endif /* _JFS_WIP */
+
+
+#ifdef CONFIG_JFS_STATISTICS
+int jfs_xtstat_read(char *buffer, char **start, off_t offset, int length,
+ int *eof, void *data)
+{
+ int len = 0;
+ off_t begin;
+
+ len += sprintf(buffer,
+ "JFS Xtree statistics\n"
+ "====================\n"
+ "searches = %d\n"
+ "fast searches = %d\n"
+ "splits = %d\n",
+ xtStat.search,
+ xtStat.fastSearch,
+ xtStat.split);
+
+ begin = offset;
+ *start = buffer + begin;
+ len -= begin;
+
+ if (len > length)
+ len = length;
+ else
+ *eof = 1;
+
+ if (len < 0)
+ len = 0;
+
+ return len;
+}
+#endif
diff --git a/fs/jfs/jfs_xtree.h b/fs/jfs/jfs_xtree.h
new file mode 100644
index 00000000000000..7ae64d5986ee9c
--- /dev/null
+++ b/fs/jfs/jfs_xtree.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2000-2002
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _H_JFS_XTREE
+#define _H_JFS_XTREE
+
+/*
+ * jfs_xtree.h: extent allocation descriptor B+-tree manager
+ */
+
+#include "jfs_btree.h"
+
+
+/*
+ * extent allocation descriptor (xad)
+ */
+typedef struct xad {
+ unsigned flag:8; /* 1: flag */
+ unsigned rsvrd:16; /* 2: reserved */
+ unsigned off1:8; /* 1: offset in unit of fsblksize */
+ u32 off2; /* 4: offset in unit of fsblksize */
+ unsigned len:24; /* 3: length in unit of fsblksize */
+ unsigned addr1:8; /* 1: address in unit of fsblksize */
+ u32 addr2; /* 4: address in unit of fsblksize */
+} xad_t; /* (16) */
+
+#define MAXXLEN ((1 << 24) - 1)
+
+#define XTSLOTSIZE 16
+#define L2XTSLOTSIZE 4
+
+/* xad_t field construction */
+#define XADoffset(xad, offset64)\
+{\
+ (xad)->off1 = ((u64)offset64) >> 32;\
+ (xad)->off2 = __cpu_to_le32((offset64) & 0xffffffff);\
+}
+#define XADaddress(xad, address64)\
+{\
+ (xad)->addr1 = ((u64)address64) >> 32;\
+ (xad)->addr2 = __cpu_to_le32((address64) & 0xffffffff);\
+}
+#define XADlength(xad, length32) (xad)->len = __cpu_to_le24(length32)
+
+/* xad_t field extraction */
+#define offsetXAD(xad)\
+ ( ((s64)((xad)->off1)) << 32 | __le32_to_cpu((xad)->off2))
+#define addressXAD(xad)\
+ ( ((s64)((xad)->addr1)) << 32 | __le32_to_cpu((xad)->addr2))
+#define lengthXAD(xad) __le24_to_cpu((xad)->len)
+
+/* xad list */
+struct xadlist {
+ s16 maxnxad;
+ s16 nxad;
+ xad_t *xad;
+};
+
+/* xad_t flags */
+#define XAD_NEW 0x01 /* new */
+#define XAD_EXTENDED 0x02 /* extended */
+#define XAD_COMPRESSED 0x04 /* compressed with recorded length */
+#define XAD_NOTRECORDED 0x08 /* allocated but not recorded */
+#define XAD_COW 0x10 /* copy-on-write */
+
+
+/* possible values for maxentry */
+#define XTROOTINITSLOT_DIR 6
+#define XTROOTINITSLOT 10
+#define XTROOTMAXSLOT 18
+#define XTPAGEMAXSLOT 256
+#define XTENTRYSTART 2
+
+/*
+ * xtree page:
+ */
+typedef union {
+ struct xtheader {
+ s64 next; /* 8: */
+ s64 prev; /* 8: */
+
+ u8 flag; /* 1: */
+ u8 rsrvd1; /* 1: */
+ s16 nextindex; /* 2: next index = number of entries */
+ s16 maxentry; /* 2: max number of entries */
+ s16 rsrvd2; /* 2: */
+
+ pxd_t self; /* 8: self */
+ } header; /* (32) */
+
+ xad_t xad[XTROOTMAXSLOT]; /* 16 * maxentry: xad array */
+} xtpage_t;
+
+/*
+ * external declaration
+ */
+extern int xtLookup(struct inode *ip, s64 lstart, s64 llen,
+ int *pflag, s64 * paddr, int *plen, int flag);
+extern int xtLookupList(struct inode *ip, struct lxdlist * lxdlist,
+ struct xadlist * xadlist, int flag);
+extern void xtInitRoot(tid_t tid, struct inode *ip);
+extern int xtInsert(tid_t tid, struct inode *ip,
+ int xflag, s64 xoff, int xlen, s64 * xaddrp, int flag);
+extern int xtExtend(tid_t tid, struct inode *ip, s64 xoff, int xlen,
+ int flag);
+#ifdef _NOTYET
+extern int xtTailgate(tid_t tid, struct inode *ip,
+ s64 xoff, int xlen, s64 xaddr, int flag);
+#endif
+extern int xtUpdate(tid_t tid, struct inode *ip, struct xad *nxad);
+extern int xtDelete(tid_t tid, struct inode *ip, s64 xoff, int xlen,
+ int flag);
+extern s64 xtTruncate(tid_t tid, struct inode *ip, s64 newsize, int type);
+extern s64 xtTruncate_pmap(tid_t tid, struct inode *ip, s64 committed_size);
+extern int xtRelocate(tid_t tid, struct inode *ip,
+ xad_t * oxad, s64 nxaddr, int xtype);
+extern int xtAppend(tid_t tid,
+ struct inode *ip, int xflag, s64 xoff, int maxblocks,
+ int *xlenp, s64 * xaddrp, int flag);
+
+#ifdef _JFS_DEBUG_XTREE
+extern int xtDisplayTree(struct inode *ip);
+extern int xtDisplayPage(struct inode *ip, s64 bn, xtpage_t * p);
+#endif /* _JFS_DEBUG_XTREE */
+
+#endif /* !_H_JFS_XTREE */
diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c
new file mode 100644
index 00000000000000..65327b730911ec
--- /dev/null
+++ b/fs/jfs/namei.c
@@ -0,0 +1,1424 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2000-2003
+ * Portions Copyright (C) Christoph Hellwig, 2001-2002
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/fs.h>
+#include "jfs_incore.h"
+#include "jfs_superblock.h"
+#include "jfs_inode.h"
+#include "jfs_dinode.h"
+#include "jfs_dmap.h"
+#include "jfs_unicode.h"
+#include "jfs_metapage.h"
+#include "jfs_xattr.h"
+#include "jfs_debug.h"
+
+extern struct inode_operations jfs_file_inode_operations;
+extern struct inode_operations jfs_symlink_inode_operations;
+extern struct file_operations jfs_file_operations;
+extern struct address_space_operations jfs_aops;
+
+extern int jfs_fsync(struct file *, struct dentry *, int);
+extern void jfs_truncate_nolock(struct inode *, loff_t);
+
+/*
+ * forward references
+ */
+struct inode_operations jfs_dir_inode_operations;
+struct file_operations jfs_dir_operations;
+
+static s64 commitZeroLink(tid_t, struct inode *);
+
+/*
+ * NAME: jfs_create(dip, dentry, mode)
+ *
+ * FUNCTION: create a regular file in the parent directory <dip>
+ * with name = <from dentry> and mode = <mode>
+ *
+ * PARAMETER: dip - parent directory vnode
+ * dentry - dentry of new file
+ * mode - create mode (rwxrwxrwx).
+ *
+ * RETURN: Errors from subroutines
+ *
+ */
+static int jfs_create(struct inode *dip, struct dentry *dentry, int mode)
+{
+ int rc = 0;
+ tid_t tid; /* transaction id */
+ struct inode *ip = NULL; /* child directory inode */
+ ino_t ino;
+ struct component_name dname; /* child directory name */
+ struct btstack btstack;
+ struct inode *iplist[2];
+ struct tblock *tblk;
+
+ jfs_info("jfs_create: dip:0x%p name:%s", dip, dentry->d_name.name);
+
+ /*
+ * search parent directory for entry/freespace
+ * (dtSearch() returns parent directory page pinned)
+ */
+ if ((rc = get_UCSname(&dname, dentry)))
+ goto out1;
+
+ /*
+ * Either iAlloc() or txBegin() may block. Deadlock can occur if we
+ * block there while holding dtree page, so we allocate the inode &
+ * begin the transaction before we search the directory.
+ */
+ ip = ialloc(dip, mode);
+ if (ip == NULL) {
+ rc = -ENOSPC;
+ goto out2;
+ }
+
+ tid = txBegin(dip->i_sb, 0);
+
+ down(&JFS_IP(dip)->commit_sem);
+ down(&JFS_IP(ip)->commit_sem);
+
+ if ((rc = dtSearch(dip, &dname, &ino, &btstack, JFS_CREATE))) {
+ jfs_err("jfs_create: dtSearch returned %d", rc);
+ goto out3;
+ }
+
+ tblk = tid_to_tblock(tid);
+ tblk->xflag |= COMMIT_CREATE;
+ tblk->ip = ip;
+
+ iplist[0] = dip;
+ iplist[1] = ip;
+
+ /*
+ * initialize the child XAD tree root in-line in inode
+ */
+ xtInitRoot(tid, ip);
+
+ /*
+ * create entry in parent directory for child directory
+ * (dtInsert() releases parent directory page)
+ */
+ ino = ip->i_ino;
+ if ((rc = dtInsert(tid, dip, &dname, &ino, &btstack))) {
+ jfs_err("jfs_create: dtInsert returned %d", rc);
+ if (rc == -EIO)
+ txAbort(tid, 1); /* Marks Filesystem dirty */
+ else
+ txAbort(tid, 0); /* Filesystem full */
+ goto out3;
+ }
+
+ ip->i_op = &jfs_file_inode_operations;
+ ip->i_fop = &jfs_file_operations;
+ ip->i_mapping->a_ops = &jfs_aops;
+
+ insert_inode_hash(ip);
+ mark_inode_dirty(ip);
+
+ dip->i_ctime = dip->i_mtime = CURRENT_TIME;
+
+ mark_inode_dirty(dip);
+
+ rc = txCommit(tid, 2, &iplist[0], 0);
+
+ out3:
+ txEnd(tid);
+ up(&JFS_IP(dip)->commit_sem);
+ up(&JFS_IP(ip)->commit_sem);
+ if (rc) {
+ ip->i_nlink = 0;
+ iput(ip);
+ } else
+ d_instantiate(dentry, ip);
+
+ out2:
+ free_UCSname(&dname);
+
+ out1:
+
+ jfs_info("jfs_create: rc:%d", rc);
+ return rc;
+}
+
+
+/*
+ * NAME: jfs_mkdir(dip, dentry, mode)
+ *
+ * FUNCTION: create a child directory in the parent directory <dip>
+ * with name = <from dentry> and mode = <mode>
+ *
+ * PARAMETER: dip - parent directory vnode
+ * dentry - dentry of child directory
+ * mode - create mode (rwxrwxrwx).
+ *
+ * RETURN: Errors from subroutines
+ *
+ * note:
+ * EACCESS: user needs search+write permission on the parent directory
+ */
+static int jfs_mkdir(struct inode *dip, struct dentry *dentry, int mode)
+{
+ int rc = 0;
+ tid_t tid; /* transaction id */
+ struct inode *ip = NULL; /* child directory inode */
+ ino_t ino;
+ struct component_name dname; /* child directory name */
+ struct btstack btstack;
+ struct inode *iplist[2];
+ struct tblock *tblk;
+
+ jfs_info("jfs_mkdir: dip:0x%p name:%s", dip, dentry->d_name.name);
+
+ /* link count overflow on parent directory ? */
+ if (dip->i_nlink == JFS_LINK_MAX) {
+ rc = -EMLINK;
+ goto out1;
+ }
+
+ /*
+ * search parent directory for entry/freespace
+ * (dtSearch() returns parent directory page pinned)
+ */
+ if ((rc = get_UCSname(&dname, dentry)))
+ goto out1;
+
+ /*
+ * Either iAlloc() or txBegin() may block. Deadlock can occur if we
+ * block there while holding dtree page, so we allocate the inode &
+ * begin the transaction before we search the directory.
+ */
+ ip = ialloc(dip, S_IFDIR | mode);
+ if (ip == NULL) {
+ rc = -ENOSPC;
+ goto out2;
+ }
+
+ tid = txBegin(dip->i_sb, 0);
+
+ down(&JFS_IP(dip)->commit_sem);
+ down(&JFS_IP(ip)->commit_sem);
+
+ if ((rc = dtSearch(dip, &dname, &ino, &btstack, JFS_CREATE))) {
+ jfs_err("jfs_mkdir: dtSearch returned %d", rc);
+ goto out3;
+ }
+
+ tblk = tid_to_tblock(tid);
+ tblk->xflag |= COMMIT_CREATE;
+ tblk->ip = ip;
+
+ iplist[0] = dip;
+ iplist[1] = ip;
+
+ /*
+ * initialize the child directory in-line in inode
+ */
+ dtInitRoot(tid, ip, dip->i_ino);
+
+ /*
+ * create entry in parent directory for child directory
+ * (dtInsert() releases parent directory page)
+ */
+ ino = ip->i_ino;
+ if ((rc = dtInsert(tid, dip, &dname, &ino, &btstack))) {
+ jfs_err("jfs_mkdir: dtInsert returned %d", rc);
+
+ if (rc == -EIO)
+ txAbort(tid, 1); /* Marks Filesystem dirty */
+ else
+ txAbort(tid, 0); /* Filesystem full */
+ goto out3;
+ }
+
+ ip->i_nlink = 2; /* for '.' */
+ ip->i_op = &jfs_dir_inode_operations;
+ ip->i_fop = &jfs_dir_operations;
+
+ insert_inode_hash(ip);
+ mark_inode_dirty(ip);
+
+ /* update parent directory inode */
+ dip->i_nlink++; /* for '..' from child directory */
+ dip->i_ctime = dip->i_mtime = CURRENT_TIME;
+ mark_inode_dirty(dip);
+
+ rc = txCommit(tid, 2, &iplist[0], 0);
+
+ out3:
+ txEnd(tid);
+ up(&JFS_IP(dip)->commit_sem);
+ up(&JFS_IP(ip)->commit_sem);
+ if (rc) {
+ ip->i_nlink = 0;
+ iput(ip);
+ } else
+ d_instantiate(dentry, ip);
+
+ out2:
+ free_UCSname(&dname);
+
+ out1:
+
+ jfs_info("jfs_mkdir: rc:%d", rc);
+ return rc;
+}
+
+/*
+ * NAME: jfs_rmdir(dip, dentry)
+ *
+ * FUNCTION: remove a link to child directory
+ *
+ * PARAMETER: dip - parent inode
+ * dentry - child directory dentry
+ *
+ * RETURN: -EINVAL - if name is . or ..
+ * -EINVAL - if . or .. exist but are invalid.
+ * errors from subroutines
+ *
+ * note:
+ * if other threads have the directory open when the last link
+ * is removed, the "." and ".." entries, if present, are removed before
+ * rmdir() returns and no new entries may be created in the directory,
+ * but the directory is not removed until the last reference to
+ * the directory is released (cf.unlink() of regular file).
+ */
+static int jfs_rmdir(struct inode *dip, struct dentry *dentry)
+{
+ int rc;
+ tid_t tid; /* transaction id */
+ struct inode *ip = dentry->d_inode;
+ ino_t ino;
+ struct component_name dname;
+ struct inode *iplist[2];
+ struct tblock *tblk;
+
+ jfs_info("jfs_rmdir: dip:0x%p name:%s", dip, dentry->d_name.name);
+
+ /* directory must be empty to be removed */
+ if (!dtEmpty(ip)) {
+ rc = -ENOTEMPTY;
+ goto out;
+ }
+
+ if ((rc = get_UCSname(&dname, dentry)))
+ goto out;
+
+ tid = txBegin(dip->i_sb, 0);
+
+ down(&JFS_IP(dip)->commit_sem);
+ down(&JFS_IP(ip)->commit_sem);
+
+ iplist[0] = dip;
+ iplist[1] = ip;
+
+ tblk = tid_to_tblock(tid);
+ tblk->xflag |= COMMIT_DELETE;
+ tblk->ip = ip;
+
+ /*
+ * delete the entry of target directory from parent directory
+ */
+ ino = ip->i_ino;
+ if ((rc = dtDelete(tid, dip, &dname, &ino, JFS_REMOVE))) {
+ jfs_err("jfs_rmdir: dtDelete returned %d", rc);
+ if (rc == -EIO)
+ txAbort(tid, 1);
+ txEnd(tid);
+ up(&JFS_IP(dip)->commit_sem);
+ up(&JFS_IP(ip)->commit_sem);
+
+ goto out2;
+ }
+
+ /* update parent directory's link count corresponding
+ * to ".." entry of the target directory deleted
+ */
+ dip->i_nlink--;
+ dip->i_ctime = dip->i_mtime = CURRENT_TIME;
+ mark_inode_dirty(dip);
+
+ /*
+ * OS/2 could have created EA and/or ACL
+ */
+ /* free EA from both persistent and working map */
+ if (JFS_IP(ip)->ea.flag & DXD_EXTENT) {
+ /* free EA pages */
+ txEA(tid, ip, &JFS_IP(ip)->ea, NULL);
+ }
+ JFS_IP(ip)->ea.flag = 0;
+
+ /* free ACL from both persistent and working map */
+ if (JFS_IP(ip)->acl.flag & DXD_EXTENT) {
+ /* free ACL pages */
+ txEA(tid, ip, &JFS_IP(ip)->acl, NULL);
+ }
+ JFS_IP(ip)->acl.flag = 0;
+
+ /* mark the target directory as deleted */
+ ip->i_nlink = 0;
+ mark_inode_dirty(ip);
+
+ rc = txCommit(tid, 2, &iplist[0], 0);
+
+ txEnd(tid);
+
+ up(&JFS_IP(dip)->commit_sem);
+ up(&JFS_IP(ip)->commit_sem);
+
+ /*
+ * Truncating the directory index table is not guaranteed. It
+ * may need to be done iteratively
+ */
+ if (test_cflag(COMMIT_Stale, dip)) {
+ if (dip->i_size > 1)
+ jfs_truncate_nolock(dip, 0);
+
+ clear_cflag(COMMIT_Stale, dip);
+ }
+
+ out2:
+ free_UCSname(&dname);
+
+ out:
+ jfs_info("jfs_rmdir: rc:%d", rc);
+ return rc;
+}
+
+/*
+ * NAME: jfs_unlink(dip, dentry)
+ *
+ * FUNCTION: remove a link to object <vp> named by <name>
+ * from parent directory <dvp>
+ *
+ * PARAMETER: dip - inode of parent directory
+ * dentry - dentry of object to be removed
+ *
+ * RETURN: errors from subroutines
+ *
+ * note:
+ * temporary file: if one or more processes have the file open
+ * when the last link is removed, the link will be removed before
+ * unlink() returns, but the removal of the file contents will be
+ * postponed until all references to the files are closed.
+ *
+ * JFS does NOT support unlink() on directories.
+ *
+ */
+static int jfs_unlink(struct inode *dip, struct dentry *dentry)
+{
+ int rc;
+ tid_t tid; /* transaction id */
+ struct inode *ip = dentry->d_inode;
+ ino_t ino;
+ struct component_name dname; /* object name */
+ struct inode *iplist[2];
+ struct tblock *tblk;
+ s64 new_size = 0;
+ int commit_flag;
+
+ jfs_info("jfs_unlink: dip:0x%p name:%s", dip, dentry->d_name.name);
+
+ if ((rc = get_UCSname(&dname, dentry)))
+ goto out;
+
+ IWRITE_LOCK(ip);
+
+ tid = txBegin(dip->i_sb, 0);
+
+ down(&JFS_IP(dip)->commit_sem);
+ down(&JFS_IP(ip)->commit_sem);
+
+ iplist[0] = dip;
+ iplist[1] = ip;
+
+ /*
+ * delete the entry of target file from parent directory
+ */
+ ino = ip->i_ino;
+ if ((rc = dtDelete(tid, dip, &dname, &ino, JFS_REMOVE))) {
+ jfs_err("jfs_unlink: dtDelete returned %d", rc);
+ if (rc == -EIO)
+ txAbort(tid, 1); /* Marks FS Dirty */
+ txEnd(tid);
+ up(&JFS_IP(dip)->commit_sem);
+ up(&JFS_IP(ip)->commit_sem);
+ IWRITE_UNLOCK(ip);
+ goto out1;
+ }
+
+ ASSERT(ip->i_nlink);
+
+ ip->i_ctime = dip->i_ctime = dip->i_mtime = CURRENT_TIME;
+ mark_inode_dirty(dip);
+
+ /* update target's inode */
+ ip->i_nlink--;
+ mark_inode_dirty(ip);
+
+ /*
+ * commit zero link count object
+ */
+ if (ip->i_nlink == 0) {
+ assert(!test_cflag(COMMIT_Nolink, ip));
+ /* free block resources */
+ if ((new_size = commitZeroLink(tid, ip)) < 0) {
+ txAbort(tid, 1); /* Marks FS Dirty */
+ txEnd(tid);
+ up(&JFS_IP(dip)->commit_sem);
+ up(&JFS_IP(ip)->commit_sem);
+ IWRITE_UNLOCK(ip);
+ rc = new_size;
+ goto out1;
+ }
+ tblk = tid_to_tblock(tid);
+ tblk->xflag |= COMMIT_DELETE;
+ tblk->ip = ip;
+ }
+
+ /*
+ * Incomplete truncate of file data can
+ * result in timing problems unless we synchronously commit the
+ * transaction.
+ */
+ if (new_size)
+ commit_flag = COMMIT_SYNC;
+ else
+ commit_flag = 0;
+
+ /*
+ * If xtTruncate was incomplete, commit synchronously to avoid
+ * timing complications
+ */
+ rc = txCommit(tid, 2, &iplist[0], commit_flag);
+
+ txEnd(tid);
+
+ up(&JFS_IP(dip)->commit_sem);
+ up(&JFS_IP(ip)->commit_sem);
+
+
+ while (new_size && (rc == 0)) {
+ tid = txBegin(dip->i_sb, 0);
+ down(&JFS_IP(ip)->commit_sem);
+ new_size = xtTruncate_pmap(tid, ip, new_size);
+ if (new_size < 0) {
+ txAbort(tid, 1); /* Marks FS Dirty */
+ rc = new_size;
+ } else
+ rc = txCommit(tid, 2, &iplist[0], COMMIT_SYNC);
+ txEnd(tid);
+ up(&JFS_IP(ip)->commit_sem);
+ }
+
+ if (ip->i_nlink == 0)
+ set_cflag(COMMIT_Nolink, ip);
+
+ IWRITE_UNLOCK(ip);
+
+ /*
+ * Truncating the directory index table is not guaranteed. It
+ * may need to be done iteratively
+ */
+ if (test_cflag(COMMIT_Stale, dip)) {
+ if (dip->i_size > 1)
+ jfs_truncate_nolock(dip, 0);
+
+ clear_cflag(COMMIT_Stale, dip);
+ }
+
+ out1:
+ free_UCSname(&dname);
+ out:
+ jfs_info("jfs_unlink: rc:%d", rc);
+ return rc;
+}
+
+/*
+ * NAME: commitZeroLink()
+ *
+ * FUNCTION: for non-directory, called by jfs_remove(),
+ * truncate a regular file, directory or symbolic
+ * link to zero length. return 0 if type is not
+ * one of these.
+ *
+ * if the file is currently associated with a VM segment
+ * only permanent disk and inode map resources are freed,
+ * and neither the inode nor indirect blocks are modified
+ * so that the resources can be later freed in the work
+ * map by ctrunc1.
+ * if there is no VM segment on entry, the resources are
+ * freed in both work and permanent map.
+ * (? for temporary file - memory object is cached even
+ * after no reference:
+ * reference count > 0 - )
+ *
+ * PARAMETERS: cd - pointer to commit data structure.
+ * current inode is the one to truncate.
+ *
+ * RETURN: Errors from subroutines
+ */
+static s64 commitZeroLink(tid_t tid, struct inode *ip)
+{
+ int filetype;
+ struct tblock *tblk;
+
+ jfs_info("commitZeroLink: tid = %d, ip = 0x%p", tid, ip);
+
+ filetype = ip->i_mode & S_IFMT;
+ switch (filetype) {
+ case S_IFREG:
+ break;
+ case S_IFLNK:
+ /* fast symbolic link */
+ if (ip->i_size < IDATASIZE) {
+ ip->i_size = 0;
+ return 0;
+ }
+ break;
+ default:
+ assert(filetype != S_IFDIR);
+ return 0;
+ }
+
+ set_cflag(COMMIT_Freewmap, ip);
+
+ /* mark transaction of block map update type */
+ tblk = tid_to_tblock(tid);
+ tblk->xflag |= COMMIT_PMAP;
+
+ /*
+ * free EA
+ */
+ if (JFS_IP(ip)->ea.flag & DXD_EXTENT)
+ /* acquire maplock on EA to be freed from block map */
+ txEA(tid, ip, &JFS_IP(ip)->ea, NULL);
+
+ /*
+ * free ACL
+ */
+ if (JFS_IP(ip)->acl.flag & DXD_EXTENT)
+ /* acquire maplock on EA to be freed from block map */
+ txEA(tid, ip, &JFS_IP(ip)->acl, NULL);
+
+ /*
+ * free xtree/data (truncate to zero length):
+ * free xtree/data pages from cache if COMMIT_PWMAP,
+ * free xtree/data blocks from persistent block map, and
+ * free xtree/data blocks from working block map if COMMIT_PWMAP;
+ */
+ if (ip->i_size)
+ return xtTruncate_pmap(tid, ip, 0);
+
+ return 0;
+}
+
+
+/*
+ * NAME: freeZeroLink()
+ *
+ * FUNCTION: for non-directory, called by iClose(),
+ * free resources of a file from cache and WORKING map
+ * for a file previously committed with zero link count
+ * while associated with a pager object,
+ *
+ * PARAMETER: ip - pointer to inode of file.
+ *
+ * RETURN: 0 -ok
+ */
+int freeZeroLink(struct inode *ip)
+{
+ int rc = 0;
+ int type;
+
+ jfs_info("freeZeroLink: ip = 0x%p", ip);
+
+ /* return if not reg or symbolic link or if size is
+ * already ok.
+ */
+ type = ip->i_mode & S_IFMT;
+
+ switch (type) {
+ case S_IFREG:
+ break;
+ case S_IFLNK:
+ /* if its contained in inode nothing to do */
+ if (ip->i_size < IDATASIZE)
+ return 0;
+ break;
+ default:
+ return 0;
+ }
+
+ /*
+ * free EA
+ */
+ if (JFS_IP(ip)->ea.flag & DXD_EXTENT) {
+ s64 xaddr = addressDXD(&JFS_IP(ip)->ea);
+ int xlen = lengthDXD(&JFS_IP(ip)->ea);
+ struct maplock maplock; /* maplock for COMMIT_WMAP */
+ struct pxd_lock *pxdlock; /* maplock for COMMIT_WMAP */
+
+ /* free EA pages from cache */
+ invalidate_dxd_metapages(ip, JFS_IP(ip)->ea);
+
+ /* free EA extent from working block map */
+ maplock.index = 1;
+ pxdlock = (struct pxd_lock *) & maplock;
+ pxdlock->flag = mlckFREEPXD;
+ PXDaddress(&pxdlock->pxd, xaddr);
+ PXDlength(&pxdlock->pxd, xlen);
+ txFreeMap(ip, pxdlock, 0, COMMIT_WMAP);
+ }
+
+ /*
+ * free ACL
+ */
+ if (JFS_IP(ip)->acl.flag & DXD_EXTENT) {
+ s64 xaddr = addressDXD(&JFS_IP(ip)->acl);
+ int xlen = lengthDXD(&JFS_IP(ip)->acl);
+ struct maplock maplock; /* maplock for COMMIT_WMAP */
+ struct pxd_lock *pxdlock; /* maplock for COMMIT_WMAP */
+
+ invalidate_dxd_metapages(ip, JFS_IP(ip)->acl);
+
+ /* free ACL extent from working block map */
+ maplock.index = 1;
+ pxdlock = (struct pxd_lock *) & maplock;
+ pxdlock->flag = mlckFREEPXD;
+ PXDaddress(&pxdlock->pxd, xaddr);
+ PXDlength(&pxdlock->pxd, xlen);
+ txFreeMap(ip, pxdlock, 0, COMMIT_WMAP);
+ }
+
+ /*
+ * free xtree/data (truncate to zero length):
+ * free xtree/data pages from cache, and
+ * free xtree/data blocks from working block map;
+ */
+ if (ip->i_size)
+ rc = xtTruncate(0, ip, 0, COMMIT_WMAP);
+
+ return rc;
+}
+
+/*
+ * NAME: jfs_link(vp, dvp, name, crp)
+ *
+ * FUNCTION: create a link to <vp> by the name = <name>
+ * in the parent directory <dvp>
+ *
+ * PARAMETER: vp - target object
+ * dvp - parent directory of new link
+ * name - name of new link to target object
+ * crp - credential
+ *
+ * RETURN: Errors from subroutines
+ *
+ * note:
+ * JFS does NOT support link() on directories (to prevent circular
+ * path in the directory hierarchy);
+ * EPERM: the target object is a directory, and either the caller
+ * does not have appropriate privileges or the implementation prohibits
+ * using link() on directories [XPG4.2].
+ *
+ * JFS does NOT support links between file systems:
+ * EXDEV: target object and new link are on different file systems and
+ * implementation does not support links between file systems [XPG4.2].
+ */
+static int jfs_link(struct dentry *old_dentry,
+ struct inode *dir, struct dentry *dentry)
+{
+ int rc;
+ tid_t tid;
+ struct inode *ip = old_dentry->d_inode;
+ ino_t ino;
+ struct component_name dname;
+ struct btstack btstack;
+ struct inode *iplist[2];
+
+ jfs_info("jfs_link: %s %s", old_dentry->d_name.name,
+ dentry->d_name.name);
+
+ /* JFS does NOT support link() on directories */
+ if (S_ISDIR(ip->i_mode))
+ return -EPERM;
+
+ tid = txBegin(ip->i_sb, 0);
+
+ down(&JFS_IP(dir)->commit_sem);
+ down(&JFS_IP(ip)->commit_sem);
+
+ if (ip->i_nlink == JFS_LINK_MAX) {
+ rc = -EMLINK;
+ goto out;
+ }
+
+ if (ip->i_nlink == 0) {
+ rc = -ENOENT;
+ goto out;
+ }
+
+ /*
+ * scan parent directory for entry/freespace
+ */
+ if ((rc = get_UCSname(&dname, dentry)))
+ goto out;
+
+ if ((rc = dtSearch(dir, &dname, &ino, &btstack, JFS_CREATE)))
+ goto free_dname;
+
+ /*
+ * create entry for new link in parent directory
+ */
+ ino = ip->i_ino;
+ if ((rc = dtInsert(tid, dir, &dname, &ino, &btstack)))
+ goto free_dname;
+
+ /* update object inode */
+ ip->i_nlink++; /* for new link */
+ ip->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(dir);
+ atomic_inc(&ip->i_count);
+
+ iplist[0] = ip;
+ iplist[1] = dir;
+ rc = txCommit(tid, 2, &iplist[0], 0);
+
+ if (!rc)
+ d_instantiate(dentry, ip);
+ free_dname:
+ free_UCSname(&dname);
+
+ out:
+ txEnd(tid);
+
+ up(&JFS_IP(dir)->commit_sem);
+ up(&JFS_IP(ip)->commit_sem);
+
+ jfs_info("jfs_link: rc:%d", rc);
+ return rc;
+}
+
+/*
+ * NAME: jfs_symlink(dip, dentry, name)
+ *
+ * FUNCTION: creates a symbolic link to <symlink> by name <name>
+ * in directory <dip>
+ *
+ * PARAMETER: dip - parent directory vnode
+ * dentry - dentry of symbolic link
+ * name - the path name of the existing object
+ * that will be the source of the link
+ *
+ * RETURN: errors from subroutines
+ *
+ * note:
+ * ENAMETOOLONG: pathname resolution of a symbolic link produced
+ * an intermediate result whose length exceeds PATH_MAX [XPG4.2]
+*/
+
+static int jfs_symlink(struct inode *dip, struct dentry *dentry,
+ const char *name)
+{
+ int rc;
+ tid_t tid;
+ ino_t ino = 0;
+ struct component_name dname;
+ int ssize; /* source pathname size */
+ struct btstack btstack;
+ struct inode *ip = dentry->d_inode;
+ unchar *i_fastsymlink;
+ s64 xlen = 0;
+ int bmask = 0, xsize;
+ s64 extent = 0, xaddr;
+ struct metapage *mp;
+ struct super_block *sb;
+ struct tblock *tblk;
+
+ struct inode *iplist[2];
+
+ jfs_info("jfs_symlink: dip:0x%p name:%s", dip, name);
+
+ ssize = strlen(name) + 1;
+
+ /*
+ * search parent directory for entry/freespace
+ * (dtSearch() returns parent directory page pinned)
+ */
+
+ if ((rc = get_UCSname(&dname, dentry)))
+ goto out1;
+
+ /*
+ * allocate on-disk/in-memory inode for symbolic link:
+ * (iAlloc() returns new, locked inode)
+ */
+ ip = ialloc(dip, S_IFLNK | 0777);
+ if (ip == NULL) {
+ rc = -ENOSPC;
+ goto out2;
+ }
+
+ tid = txBegin(dip->i_sb, 0);
+
+ down(&JFS_IP(dip)->commit_sem);
+ down(&JFS_IP(ip)->commit_sem);
+
+ tblk = tid_to_tblock(tid);
+ tblk->xflag |= COMMIT_CREATE;
+ tblk->ip = ip;
+
+ /* fix symlink access permission
+ * (dir_create() ANDs in the u.u_cmask,
+ * but symlinks really need to be 777 access)
+ */
+ ip->i_mode |= 0777;
+
+ /*
+ * write symbolic link target path name
+ */
+ xtInitRoot(tid, ip);
+
+ /*
+ * write source path name inline in on-disk inode (fast symbolic link)
+ */
+
+ if (ssize <= IDATASIZE) {
+ ip->i_op = &jfs_symlink_inode_operations;
+
+ i_fastsymlink = JFS_IP(ip)->i_inline;
+ memcpy(i_fastsymlink, name, ssize);
+ ip->i_size = ssize - 1;
+
+ /*
+ * if symlink is > 128 bytes, we don't have the space to
+ * store inline extended attributes
+ */
+ if (ssize > sizeof (JFS_IP(ip)->i_inline))
+ JFS_IP(ip)->mode2 &= ~INLINEEA;
+
+ jfs_info("jfs_symlink: fast symlink added ssize:%d name:%s ",
+ ssize, name);
+ }
+ /*
+ * write source path name in a single extent
+ */
+ else {
+ jfs_info("jfs_symlink: allocate extent ip:0x%p", ip);
+
+ ip->i_op = &page_symlink_inode_operations;
+ ip->i_mapping->a_ops = &jfs_aops;
+
+ /*
+ * even though the data of symlink object (source
+ * path name) is treated as non-journaled user data,
+ * it is read/written thru buffer cache for performance.
+ */
+ sb = ip->i_sb;
+ bmask = JFS_SBI(sb)->bsize - 1;
+ xsize = (ssize + bmask) & ~bmask;
+ xaddr = 0;
+ xlen = xsize >> JFS_SBI(sb)->l2bsize;
+ if ((rc = xtInsert(tid, ip, 0, 0, xlen, &xaddr, 0))) {
+ txAbort(tid, 0);
+ rc = -ENOSPC;
+ goto out3;
+ }
+ extent = xaddr;
+ ip->i_size = ssize - 1;
+ while (ssize) {
+ /* This is kind of silly since PATH_MAX == 4K */
+ int copy_size = min(ssize, PSIZE);
+
+ mp = get_metapage(ip, xaddr, PSIZE, 1);
+
+ if (mp == NULL) {
+ dbFree(ip, extent, xlen);
+ rc = -EIO;
+ txAbort(tid, 0);
+ goto out3;
+ }
+ memcpy(mp->data, name, copy_size);
+ flush_metapage(mp);
+ ssize -= copy_size;
+ name += copy_size;
+ xaddr += JFS_SBI(sb)->nbperpage;
+ }
+ ip->i_blocks = LBLK2PBLK(sb, xlen);
+ }
+
+ /*
+ * create entry for symbolic link in parent directory
+ */
+ rc = dtSearch(dip, &dname, &ino, &btstack, JFS_CREATE);
+ if (rc == 0) {
+ ino = ip->i_ino;
+ rc = dtInsert(tid, dip, &dname, &ino, &btstack);
+ }
+ if (rc) {
+ if (xlen)
+ dbFree(ip, extent, xlen);
+ txAbort(tid, 0);
+ /* discard new inode */
+ goto out3;
+ }
+
+ insert_inode_hash(ip);
+ mark_inode_dirty(ip);
+
+ /*
+ * commit update of parent directory and link object
+ */
+
+ iplist[0] = dip;
+ iplist[1] = ip;
+ rc = txCommit(tid, 2, &iplist[0], 0);
+
+ out3:
+ txEnd(tid);
+ up(&JFS_IP(dip)->commit_sem);
+ up(&JFS_IP(ip)->commit_sem);
+ if (rc) {
+ ip->i_nlink = 0;
+ iput(ip);
+ } else
+ d_instantiate(dentry, ip);
+
+ out2:
+ free_UCSname(&dname);
+
+ out1:
+ jfs_info("jfs_symlink: rc:%d", rc);
+ return rc;
+}
+
+
+/*
+ * NAME: jfs_rename
+ *
+ * FUNCTION: rename a file or directory
+ */
+static int jfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ struct btstack btstack;
+ ino_t ino;
+ struct component_name new_dname;
+ struct inode *new_ip;
+ struct component_name old_dname;
+ struct inode *old_ip;
+ int rc;
+ tid_t tid;
+ struct tlock *tlck;
+ struct dt_lock *dtlck;
+ struct lv *lv;
+ int ipcount;
+ struct inode *iplist[4];
+ struct tblock *tblk;
+ s64 new_size = 0;
+ int commit_flag;
+
+
+ jfs_info("jfs_rename: %s %s", old_dentry->d_name.name,
+ new_dentry->d_name.name);
+
+ old_ip = old_dentry->d_inode;
+ new_ip = new_dentry->d_inode;
+
+ if ((rc = get_UCSname(&old_dname, old_dentry)))
+ goto out1;
+
+ if ((rc = get_UCSname(&new_dname, new_dentry)))
+ goto out2;
+
+ /*
+ * Make sure source inode number is what we think it is
+ */
+ rc = dtSearch(old_dir, &old_dname, &ino, &btstack, JFS_LOOKUP);
+ if (rc || (ino != old_ip->i_ino)) {
+ rc = -ENOENT;
+ goto out3;
+ }
+
+ /*
+ * Make sure dest inode number (if any) is what we think it is
+ */
+ rc = dtSearch(new_dir, &new_dname, &ino, &btstack, JFS_LOOKUP);
+ if (rc == 0) {
+ if ((new_ip == 0) || (ino != new_ip->i_ino)) {
+ rc = -ESTALE;
+ goto out3;
+ }
+ } else if (rc != -ENOENT)
+ goto out3;
+ else if (new_ip) {
+ /* no entry exists, but one was expected */
+ rc = -ESTALE;
+ goto out3;
+ }
+
+ if (S_ISDIR(old_ip->i_mode)) {
+ if (new_ip) {
+ if (!dtEmpty(new_ip)) {
+ rc = -ENOTEMPTY;
+ goto out3;
+ }
+ } else if ((new_dir != old_dir) &&
+ (new_dir->i_nlink == JFS_LINK_MAX)) {
+ rc = -EMLINK;
+ goto out3;
+ }
+ } else if (new_ip)
+ IWRITE_LOCK(new_ip);
+
+ /*
+ * The real work starts here
+ */
+ tid = txBegin(new_dir->i_sb, 0);
+
+ down(&JFS_IP(new_dir)->commit_sem);
+ down(&JFS_IP(old_ip)->commit_sem);
+ if (old_dir != new_dir)
+ down(&JFS_IP(old_dir)->commit_sem);
+
+ if (new_ip) {
+ down(&JFS_IP(new_ip)->commit_sem);
+ /*
+ * Change existing directory entry to new inode number
+ */
+ ino = new_ip->i_ino;
+ rc = dtModify(tid, new_dir, &new_dname, &ino,
+ old_ip->i_ino, JFS_RENAME);
+ if (rc)
+ goto out4;
+ new_ip->i_nlink--;
+ if (S_ISDIR(new_ip->i_mode)) {
+ new_ip->i_nlink--;
+ if (new_ip->i_nlink) {
+ up(&JFS_IP(new_dir)->commit_sem);
+ up(&JFS_IP(old_ip)->commit_sem);
+ if (old_dir != new_dir)
+ up(&JFS_IP(old_dir)->commit_sem);
+ if (!S_ISDIR(old_ip->i_mode) && new_ip)
+ IWRITE_UNLOCK(new_ip);
+ jfs_error(new_ip->i_sb,
+ "jfs_rename: new_ip->i_nlink != 0");
+ return -EIO;
+ }
+ tblk = tid_to_tblock(tid);
+ tblk->xflag |= COMMIT_DELETE;
+ tblk->ip = new_ip;
+ } else if (new_ip->i_nlink == 0) {
+ assert(!test_cflag(COMMIT_Nolink, new_ip));
+ /* free block resources */
+ if ((new_size = commitZeroLink(tid, new_ip)) < 0) {
+ txAbort(tid, 1); /* Marks FS Dirty */
+ rc = new_size;
+ goto out4;
+ }
+ tblk = tid_to_tblock(tid);
+ tblk->xflag |= COMMIT_DELETE;
+ tblk->ip = new_ip;
+ } else {
+ new_ip->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(new_ip);
+ }
+ } else {
+ /*
+ * Add new directory entry
+ */
+ rc = dtSearch(new_dir, &new_dname, &ino, &btstack,
+ JFS_CREATE);
+ if (rc) {
+ jfs_err("jfs_rename didn't expect dtSearch to fail "
+ "w/rc = %d", rc);
+ goto out4;
+ }
+
+ ino = old_ip->i_ino;
+ rc = dtInsert(tid, new_dir, &new_dname, &ino, &btstack);
+ if (rc) {
+ jfs_err("jfs_rename: dtInsert failed w/rc = %d",
+ rc);
+ goto out4;
+ }
+ if (S_ISDIR(old_ip->i_mode))
+ new_dir->i_nlink++;
+ }
+ /*
+ * Remove old directory entry
+ */
+
+ ino = old_ip->i_ino;
+ rc = dtDelete(tid, old_dir, &old_dname, &ino, JFS_REMOVE);
+ if (rc) {
+ jfs_err("jfs_rename did not expect dtDelete to return rc = %d",
+ rc);
+ txAbort(tid, 1); /* Marks Filesystem dirty */
+ goto out4;
+ }
+ if (S_ISDIR(old_ip->i_mode)) {
+ old_dir->i_nlink--;
+ if (old_dir != new_dir) {
+ /*
+ * Change inode number of parent for moved directory
+ */
+
+ JFS_IP(old_ip)->i_dtroot.header.idotdot =
+ cpu_to_le32(new_dir->i_ino);
+
+ /* Linelock header of dtree */
+ tlck = txLock(tid, old_ip,
+ (struct metapage *) &JFS_IP(old_ip)->bxflag,
+ tlckDTREE | tlckBTROOT | tlckRELINK);
+ dtlck = (struct dt_lock *) & tlck->lock;
+ ASSERT(dtlck->index == 0);
+ lv = & dtlck->lv[0];
+ lv->offset = 0;
+ lv->length = 1;
+ dtlck->index++;
+ }
+ }
+
+ /*
+ * Update ctime on changed/moved inodes & mark dirty
+ */
+ old_ip->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(old_ip);
+
+ new_dir->i_ctime = new_dir->i_mtime = CURRENT_TIME;
+ mark_inode_dirty(new_dir);
+
+ /* Build list of inodes modified by this transaction */
+ ipcount = 0;
+ iplist[ipcount++] = old_ip;
+ if (new_ip)
+ iplist[ipcount++] = new_ip;
+ iplist[ipcount++] = old_dir;
+
+ if (old_dir != new_dir) {
+ iplist[ipcount++] = new_dir;
+ old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
+ mark_inode_dirty(old_dir);
+ }
+
+ /*
+ * Incomplete truncate of file data can
+ * result in timing problems unless we synchronously commit the
+ * transaction.
+ */
+ if (new_size)
+ commit_flag = COMMIT_SYNC;
+ else
+ commit_flag = 0;
+
+ rc = txCommit(tid, ipcount, iplist, commit_flag);
+
+ out4:
+ txEnd(tid);
+
+ up(&JFS_IP(new_dir)->commit_sem);
+ up(&JFS_IP(old_ip)->commit_sem);
+ if (old_dir != new_dir)
+ up(&JFS_IP(old_dir)->commit_sem);
+ if (new_ip)
+ up(&JFS_IP(new_ip)->commit_sem);
+
+ while (new_size && (rc == 0)) {
+ tid = txBegin(new_ip->i_sb, 0);
+ down(&JFS_IP(new_ip)->commit_sem);
+ new_size = xtTruncate_pmap(tid, new_ip, new_size);
+ if (new_size < 0) {
+ txAbort(tid, 1);
+ rc = new_size;
+ } else
+ rc = txCommit(tid, 1, &new_ip, COMMIT_SYNC);
+ txEnd(tid);
+ up(&JFS_IP(new_ip)->commit_sem);
+ }
+ if (new_ip && (new_ip->i_nlink == 0))
+ set_cflag(COMMIT_Nolink, new_ip);
+ out3:
+ free_UCSname(&new_dname);
+ out2:
+ free_UCSname(&old_dname);
+ out1:
+ if (new_ip && !S_ISDIR(new_ip->i_mode))
+ IWRITE_UNLOCK(new_ip);
+ /*
+ * Truncating the directory index table is not guaranteed. It
+ * may need to be done iteratively
+ */
+ if (test_cflag(COMMIT_Stale, old_dir)) {
+ if (old_dir->i_size > 1)
+ jfs_truncate_nolock(old_dir, 0);
+
+ clear_cflag(COMMIT_Stale, old_dir);
+ }
+
+ jfs_info("jfs_rename: returning %d", rc);
+ return rc;
+}
+
+
+/*
+ * NAME: jfs_mknod
+ *
+ * FUNCTION: Create a special file (device)
+ */
+static int jfs_mknod(struct inode *dir, struct dentry *dentry,
+ int mode, int rdev)
+{
+ struct btstack btstack;
+ struct component_name dname;
+ ino_t ino;
+ struct inode *ip;
+ struct inode *iplist[2];
+ int rc;
+ tid_t tid;
+ struct tblock *tblk;
+
+ jfs_info("jfs_mknod: %s", dentry->d_name.name);
+
+ if ((rc = get_UCSname(&dname, dentry)))
+ goto out;
+
+ ip = ialloc(dir, mode);
+ if (ip == NULL) {
+ rc = -ENOSPC;
+ goto out1;
+ }
+
+ tid = txBegin(dir->i_sb, 0);
+
+ down(&JFS_IP(dir)->commit_sem);
+ down(&JFS_IP(ip)->commit_sem);
+
+ if ((rc = dtSearch(dir, &dname, &ino, &btstack, JFS_CREATE)))
+ goto out3;
+
+ tblk = tid_to_tblock(tid);
+ tblk->xflag |= COMMIT_CREATE;
+ tblk->ip = ip;
+
+ ino = ip->i_ino;
+ if ((rc = dtInsert(tid, dir, &dname, &ino, &btstack)))
+ goto out3;
+
+ ip->i_op = &jfs_file_inode_operations;
+ init_special_inode(ip, ip->i_mode, rdev);
+
+ insert_inode_hash(ip);
+ mark_inode_dirty(ip);
+
+ dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+
+ mark_inode_dirty(dir);
+
+ iplist[0] = dir;
+ iplist[1] = ip;
+ rc = txCommit(tid, 2, iplist, 0);
+
+ out3:
+ txEnd(tid);
+ up(&JFS_IP(ip)->commit_sem);
+ up(&JFS_IP(dir)->commit_sem);
+ if (rc) {
+ ip->i_nlink = 0;
+ iput(ip);
+ } else
+ d_instantiate(dentry, ip);
+
+ out1:
+ free_UCSname(&dname);
+
+ out:
+ jfs_info("jfs_mknod: returning %d", rc);
+ return rc;
+}
+
+static struct dentry *jfs_lookup(struct inode *dip, struct dentry *dentry)
+{
+ struct btstack btstack;
+ ino_t inum;
+ struct inode *ip;
+ struct component_name key;
+ const char *name = dentry->d_name.name;
+ int len = dentry->d_name.len;
+ int rc;
+
+ jfs_info("jfs_lookup: name = %s", name);
+
+
+ if ((name[0] == '.') && (len == 1))
+ inum = dip->i_ino;
+ else if (strcmp(name, "..") == 0)
+ inum = PARENT(dip);
+ else {
+ if ((rc = get_UCSname(&key, dentry)))
+ return ERR_PTR(rc);
+ rc = dtSearch(dip, &key, &inum, &btstack, JFS_LOOKUP);
+ free_UCSname(&key);
+ if (rc == -ENOENT) {
+ d_add(dentry, NULL);
+ return ERR_PTR(0);
+ } else if (rc) {
+ jfs_err("jfs_lookup: dtSearch returned %d", rc);
+ return ERR_PTR(rc);
+ }
+ }
+
+ ip = iget(dip->i_sb, inum);
+ if (ip == NULL) {
+ jfs_err("jfs_lookup: iget failed on inum %d", (uint) inum);
+ return ERR_PTR(-EACCES);
+ }
+ if (is_bad_inode(ip)) {
+ jfs_err("jfs_lookup: iget returned bad inode, inum = %d",
+ (uint) inum);
+ iput(ip);
+ return ERR_PTR(-EACCES);
+ }
+
+ d_add(dentry, ip);
+
+ return ERR_PTR(0);
+}
+
+struct inode_operations jfs_dir_inode_operations = {
+ .create = jfs_create,
+ .lookup = jfs_lookup,
+ .link = jfs_link,
+ .unlink = jfs_unlink,
+ .symlink = jfs_symlink,
+ .mkdir = jfs_mkdir,
+ .rmdir = jfs_rmdir,
+ .mknod = jfs_mknod,
+ .rename = jfs_rename,
+ .setxattr = jfs_setxattr,
+ .getxattr = jfs_getxattr,
+ .listxattr = jfs_listxattr,
+ .removexattr = jfs_removexattr,
+};
+
+struct file_operations jfs_dir_operations = {
+ .read = generic_read_dir,
+ .readdir = jfs_readdir,
+ .fsync = jfs_fsync,
+};
diff --git a/fs/jfs/resize.c b/fs/jfs/resize.c
new file mode 100644
index 00000000000000..a8f8663fcd1334
--- /dev/null
+++ b/fs/jfs/resize.c
@@ -0,0 +1,539 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2000-2003
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <linux/fs.h>
+#include <linux/locks.h>
+
+#include "jfs_incore.h"
+#include "jfs_filsys.h"
+#include "jfs_metapage.h"
+#include "jfs_dinode.h"
+#include "jfs_imap.h"
+#include "jfs_dmap.h"
+#include "jfs_superblock.h"
+#include "jfs_txnmgr.h"
+#include "jfs_debug.h"
+
+extern s64 jfs_get_volume_size(struct super_block *);
+
+#define BITSPERPAGE (PSIZE << 3)
+#define L2MEGABYTE 20
+#define MEGABYTE (1 << L2MEGABYTE)
+#define MEGABYTE32 (MEGABYTE << 5)
+
+/* convert block number to bmap file page number */
+#define BLKTODMAPN(b)\
+ (((b) >> 13) + ((b) >> 23) + ((b) >> 33) + 3 + 1)
+
+/*
+ * jfs_extendfs()
+ *
+ * function: extend file system;
+ *
+ * |-------------------------------|----------|----------|
+ * file system space fsck inline log
+ * workspace space
+ *
+ * input:
+ * new LVSize: in LV blocks (required)
+ * new LogSize: in LV blocks (optional)
+ * new FSSize: in LV blocks (optional)
+ *
+ * new configuration:
+ * 1. set new LogSize as specified or default from new LVSize;
+ * 2. compute new FSCKSize from new LVSize;
+ * 3. set new FSSize as MIN(FSSize, LVSize-(LogSize+FSCKSize)) where
+ * assert(new FSSize >= old FSSize),
+ * i.e., file system must not be shrinked;
+ */
+int jfs_extendfs(struct super_block *sb, s64 newLVSize, int newLogSize)
+{
+ int rc = 0;
+ struct jfs_sb_info *sbi = JFS_SBI(sb);
+ struct inode *ipbmap = sbi->ipbmap;
+ struct inode *ipbmap2;
+ struct inode *ipimap = sbi->ipimap;
+ struct jfs_log *log = sbi->log;
+ struct bmap *bmp = sbi->bmap;
+ s64 newLogAddress, newFSCKAddress;
+ int newFSCKSize;
+ s64 newMapSize = 0, mapSize;
+ s64 XAddress, XSize, nblocks, xoff, xaddr, t64;
+ s64 oldLVSize;
+ s64 newFSSize;
+ s64 VolumeSize;
+ int newNpages = 0, nPages, newPage, xlen, t32;
+ int tid;
+ int log_formatted = 0;
+ struct inode *iplist[1];
+ struct jfs_superblock *j_sb, *j_sb2;
+ uint old_agsize;
+ struct buffer_head *bh, *bh2;
+
+ /* If the volume hasn't grown, get out now */
+
+ if (sbi->mntflag & JFS_INLINELOG)
+ oldLVSize = addressPXD(&sbi->logpxd) + lengthPXD(&sbi->logpxd);
+ else
+ oldLVSize = addressPXD(&sbi->fsckpxd) +
+ lengthPXD(&sbi->fsckpxd);
+
+ if (oldLVSize >= newLVSize) {
+ printk(KERN_WARNING
+ "jfs_extendfs: volume hasn't grown, returning\n");
+ goto out;
+ }
+
+ VolumeSize = jfs_get_volume_size(sb);
+ if (VolumeSize) {
+ if (newLVSize > VolumeSize) {
+ printk(KERN_WARNING "jfs_extendfs: invalid size\n");
+ rc = -EINVAL;
+ goto out;
+ }
+ } else {
+ /* check the device */
+ bh = sb_bread(sb, newLVSize - 1);
+ if (!bh) {
+ printk(KERN_WARNING "jfs_extendfs: invalid size\n");
+ rc = -EINVAL;
+ goto out;
+ }
+ bforget(bh);
+ }
+
+ /* Can't extend write-protected drive */
+
+ if (isReadOnly(ipbmap)) {
+ printk(KERN_WARNING "jfs_extendfs: read-only file system\n");
+ rc = -EROFS;
+ goto out;
+ }
+
+ /*
+ * reconfigure LV spaces
+ * ---------------------
+ *
+ * validate new size, or, if not specified, determine new size
+ */
+
+ /*
+ * reconfigure inline log space:
+ */
+ if ((sbi->mntflag & JFS_INLINELOG)) {
+ if (newLogSize == 0) {
+ /*
+ * no size specified: default to 1/256 of aggregate
+ * size; rounded up to a megabyte boundary;
+ */
+ newLogSize = newLVSize >> 8;
+ t32 = (1 << (20 - sbi->l2bsize)) - 1;
+ newLogSize = (newLogSize + t32) & ~t32;
+ newLogSize =
+ min(newLogSize, MEGABYTE32 >> sbi->l2bsize);
+ } else {
+ /*
+ * convert the newLogSize to fs blocks.
+ *
+ * Since this is given in megabytes, it will always be
+ * an even number of pages.
+ */
+ newLogSize = (newLogSize * MEGABYTE) >> sbi->l2bsize;
+ }
+
+ } else
+ newLogSize = 0;
+
+ newLogAddress = newLVSize - newLogSize;
+
+ /*
+ * reconfigure fsck work space:
+ *
+ * configure it to the end of the logical volume regardless of
+ * whether file system extends to the end of the aggregate;
+ * Need enough 4k pages to cover:
+ * - 1 bit per block in aggregate rounded up to BPERDMAP boundary
+ * - 1 extra page to handle control page and intermediate level pages
+ * - 50 extra pages for the chkdsk service log
+ */
+ t64 = ((newLVSize - newLogSize + BPERDMAP - 1) >> L2BPERDMAP)
+ << L2BPERDMAP;
+ t32 = ((t64 + (BITSPERPAGE - 1)) / BITSPERPAGE) + 1 + 50;
+ newFSCKSize = t32 << sbi->l2nbperpage;
+ newFSCKAddress = newLogAddress - newFSCKSize;
+
+ /*
+ * compute new file system space;
+ */
+ newFSSize = newLVSize - newLogSize - newFSCKSize;
+
+ /* file system cannot be shrinked */
+ if (newFSSize < bmp->db_mapsize) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * If we're expanding enough that the inline log does not overlap
+ * the old one, we can format the new log before we quiesce the
+ * filesystem.
+ */
+ if ((sbi->mntflag & JFS_INLINELOG) && (newLogAddress > oldLVSize)) {
+ if ((rc = lmLogFormat(log, newLogAddress, newLogSize)))
+ goto out;
+ log_formatted = 1;
+ }
+ /*
+ * quiesce file system
+ *
+ * (prepare to move the inline log and to prevent map update)
+ *
+ * block any new transactions and wait for completion of
+ * all wip transactions and flush modified pages s.t.
+ * on-disk file system is in consistent state and
+ * log is not required for recovery.
+ */
+ txQuiesce(sb);
+
+ if (sbi->mntflag & JFS_INLINELOG) {
+ /*
+ * deactivate old inline log
+ */
+ lmLogShutdown(log);
+
+ /*
+ * mark on-disk super block for fs in transition;
+ *
+ * update on-disk superblock for the new space configuration
+ * of inline log space and fsck work space descriptors:
+ * N.B. FS descriptor is NOT updated;
+ *
+ * crash recovery:
+ * logredo(): if FM_EXTENDFS, return to fsck() for cleanup;
+ * fsck(): if FM_EXTENDFS, reformat inline log and fsck
+ * workspace from superblock inline log descriptor and fsck
+ * workspace descriptor;
+ */
+
+ /* read in superblock */
+ if ((rc = readSuper(sb, &bh)))
+ goto error_out;
+ j_sb = (struct jfs_superblock *)bh->b_data;
+
+ /* mark extendfs() in progress */
+ j_sb->s_state |= cpu_to_le32(FM_EXTENDFS);
+ j_sb->s_xsize = cpu_to_le64(newFSSize);
+ PXDaddress(&j_sb->s_xfsckpxd, newFSCKAddress);
+ PXDlength(&j_sb->s_xfsckpxd, newFSCKSize);
+ PXDaddress(&j_sb->s_xlogpxd, newLogAddress);
+ PXDlength(&j_sb->s_xlogpxd, newLogSize);
+
+ /* synchronously update superblock */
+ mark_buffer_dirty(bh);
+ ll_rw_block(WRITE, 1, &bh);
+ wait_on_buffer(bh);
+ brelse(bh);
+
+ /*
+ * format new inline log synchronously;
+ *
+ * crash recovery: if log move in progress,
+ * reformat log and exit success;
+ */
+ if (!log_formatted)
+ if ((rc = lmLogFormat(log, newLogAddress, newLogSize)))
+ goto error_out;
+
+ /*
+ * activate new log
+ */
+ log->base = newLogAddress;
+ log->size = newLogSize >> (L2LOGPSIZE - sb->s_blocksize_bits);
+ if ((rc = lmLogInit(log)))
+ goto error_out;
+ }
+
+ /*
+ * extend block allocation map
+ * ---------------------------
+ *
+ * extendfs() for new extension, retry after crash recovery;
+ *
+ * note: both logredo() and fsck() rebuild map from
+ * the bitmap and configuration parameter from superblock
+ * (disregarding all other control information in the map);
+ *
+ * superblock:
+ * s_size: aggregate size in physical blocks;
+ */
+ /*
+ * compute the new block allocation map configuration
+ *
+ * map dinode:
+ * di_size: map file size in byte;
+ * di_nblocks: number of blocks allocated for map file;
+ * di_mapsize: number of blocks in aggregate (covered by map);
+ * map control page:
+ * db_mapsize: number of blocks in aggregate (covered by map);
+ */
+ newMapSize = newFSSize;
+ /* number of data pages of new bmap file:
+ * roundup new size to full dmap page boundary and
+ * add 1 extra dmap page for next extendfs()
+ */
+ t64 = (newMapSize - 1) + BPERDMAP;
+ newNpages = BLKTODMAPN(t64) + 1;
+
+ /*
+ * extend map from current map (WITHOUT growing mapfile)
+ *
+ * map new extension with unmapped part of the last partial
+ * dmap page, if applicable, and extra page(s) allocated
+ * at end of bmap by mkfs() or previous extendfs();
+ */
+ extendBmap:
+ /* compute number of blocks requested to extend */
+ mapSize = bmp->db_mapsize;
+ XAddress = mapSize; /* eXtension Address */
+ XSize = newMapSize - mapSize; /* eXtension Size */
+ old_agsize = bmp->db_agsize; /* We need to know if this changes */
+
+ /* compute number of blocks that can be extended by current mapfile */
+ t64 = dbMapFileSizeToMapSize(ipbmap);
+ if (mapSize > t64) {
+ printk(KERN_ERR "jfs_extendfs: mapSize (0x%Lx) > t64 (0x%Lx)\n",
+ (long long) mapSize, (long long) t64);
+ rc = -EIO;
+ goto error_out;
+ }
+ nblocks = min(t64 - mapSize, XSize);
+
+ /*
+ * update map pages for new extension:
+ *
+ * update/init dmap and bubble up the control hierarchy
+ * incrementally fold up dmaps into upper levels;
+ * update bmap control page;
+ */
+ if ((rc = dbExtendFS(ipbmap, XAddress, nblocks)))
+ goto error_out;
+ /*
+ * the map now has extended to cover additional nblocks:
+ * dn_mapsize = oldMapsize + nblocks;
+ */
+ /* ipbmap->i_mapsize += nblocks; */
+ XSize -= nblocks;
+
+ /*
+ * grow map file to cover remaining extension
+ * and/or one extra dmap page for next extendfs();
+ *
+ * allocate new map pages and its backing blocks, and
+ * update map file xtree
+ */
+ /* compute number of data pages of current bmap file */
+ nPages = ipbmap->i_size >> L2PSIZE;
+
+ /* need to grow map file ? */
+ if (nPages == newNpages)
+ goto finalizeBmap;
+
+ /*
+ * grow bmap file for the new map pages required:
+ *
+ * allocate growth at the start of newly extended region;
+ * bmap file only grows sequentially, i.e., both data pages
+ * and possibly xtree index pages may grow in append mode,
+ * s.t. logredo() can reconstruct pre-extension state
+ * by washing away bmap file of pages outside s_size boundary;
+ */
+ /*
+ * journal map file growth as if a regular file growth:
+ * (note: bmap is created with di_mode = IFJOURNAL|IFREG);
+ *
+ * journaling of bmap file growth is not required since
+ * logredo() do/can not use log records of bmap file growth
+ * but it provides careful write semantics, pmap update, etc.;
+ */
+ /* synchronous write of data pages: bmap data pages are
+ * cached in meta-data cache, and not written out
+ * by txCommit();
+ */
+ fsync_inode_data_buffers(ipbmap);
+ diWriteSpecial(ipbmap, 0);
+
+ newPage = nPages; /* first new page number */
+ xoff = newPage << sbi->l2nbperpage;
+ xlen = (newNpages - nPages) << sbi->l2nbperpage;
+ xlen = min(xlen, (int) nblocks) & ~(sbi->nbperpage - 1);
+ xaddr = XAddress;
+
+ tid = txBegin(sb, COMMIT_FORCE);
+
+ if ((rc = xtAppend(tid, ipbmap, 0, xoff, nblocks, &xlen, &xaddr, 0))) {
+ txEnd(tid);
+ goto error_out;
+ }
+ /* update bmap file size */
+ ipbmap->i_size += xlen << sbi->l2bsize;
+ ipbmap->i_blocks += LBLK2PBLK(sb, xlen);
+
+ iplist[0] = ipbmap;
+ rc = txCommit(tid, 1, &iplist[0], COMMIT_FORCE);
+
+ txEnd(tid);
+
+ if (rc)
+ goto error_out;
+
+ /*
+ * map file has been grown now to cover extension to further out;
+ * di_size = new map file size;
+ *
+ * if huge extension, the previous extension based on previous
+ * map file size may not have been sufficient to cover whole extension
+ * (it could have been used up for new map pages),
+ * but the newly grown map file now covers lot bigger new free space
+ * available for further extension of map;
+ */
+ /* any more blocks to extend ? */
+ if (XSize)
+ goto extendBmap;
+
+ finalizeBmap:
+ /* finalize bmap */
+ dbFinalizeBmap(ipbmap);
+
+ /*
+ * update inode allocation map
+ * ---------------------------
+ *
+ * move iag lists from old to new iag;
+ * agstart field is not updated for logredo() to reconstruct
+ * iag lists if system crash occurs.
+ * (computation of ag number from agstart based on agsize
+ * will correctly identify the new ag);
+ */
+ /* if new AG size the same as old AG size, done! */
+ if (bmp->db_agsize != old_agsize) {
+ if ((rc = diExtendFS(ipimap, ipbmap)))
+ goto error_out;
+
+ /* finalize imap */
+ if ((rc = diSync(ipimap)))
+ goto error_out;
+ }
+
+ /*
+ * finalize
+ * --------
+ *
+ * extension is committed when on-disk super block is
+ * updated with new descriptors: logredo will recover
+ * crash before it to pre-extension state;
+ */
+
+ /* sync log to skip log replay of bmap file growth transaction; */
+ /* lmLogSync(log, 1); */
+
+ /*
+ * synchronous write bmap global control page;
+ * for crash before completion of write
+ * logredo() will recover to pre-extendfs state;
+ * for crash after completion of write,
+ * logredo() will recover post-extendfs state;
+ */
+ if ((rc = dbSync(ipbmap)))
+ goto error_out;
+
+ /*
+ * copy primary bmap inode to secondary bmap inode
+ */
+
+ ipbmap2 = diReadSpecial(sb, BMAP_I, 1);
+ if (ipbmap2 == NULL) {
+ printk(KERN_ERR "jfs_extendfs: diReadSpecial(bmap) failed\n");
+ goto error_out;
+ }
+ memcpy(&JFS_IP(ipbmap2)->i_xtroot, &JFS_IP(ipbmap)->i_xtroot, 288);
+ ipbmap2->i_size = ipbmap->i_size;
+ ipbmap2->i_blocks = ipbmap->i_blocks;
+
+ diWriteSpecial(ipbmap2, 1);
+ diFreeSpecial(ipbmap2);
+
+ /*
+ * update superblock
+ */
+ if ((rc = readSuper(sb, &bh)))
+ goto error_out;
+ j_sb = (struct jfs_superblock *)bh->b_data;
+
+ /* mark extendfs() completion */
+ j_sb->s_state &= cpu_to_le32(~FM_EXTENDFS);
+ j_sb->s_size = cpu_to_le64(bmp->db_mapsize <<
+ le16_to_cpu(j_sb->s_l2bfactor));
+ j_sb->s_agsize = cpu_to_le32(bmp->db_agsize);
+
+ /* update inline log space descriptor */
+ if (sbi->mntflag & JFS_INLINELOG) {
+ PXDaddress(&(j_sb->s_logpxd), newLogAddress);
+ PXDlength(&(j_sb->s_logpxd), newLogSize);
+ }
+
+ /* record log's mount serial number */
+ j_sb->s_logserial = cpu_to_le32(log->serial);
+
+ /* update fsck work space descriptor */
+ PXDaddress(&(j_sb->s_fsckpxd), newFSCKAddress);
+ PXDlength(&(j_sb->s_fsckpxd), newFSCKSize);
+ j_sb->s_fscklog = 1;
+ /* sb->s_fsckloglen remains the same */
+
+ /* Update secondary superblock */
+ bh2 = sb_bread(sb, SUPER2_OFF >> sb->s_blocksize_bits);
+ if (bh2) {
+ j_sb2 = (struct jfs_superblock *)bh2->b_data;
+ memcpy(j_sb2, j_sb, sizeof (struct jfs_superblock));
+
+ mark_buffer_dirty(bh);
+ ll_rw_block(WRITE, 1, &bh2);
+ wait_on_buffer(bh2);
+ brelse(bh2);
+ }
+
+ /* write primary superblock */
+ mark_buffer_dirty(bh);
+ ll_rw_block(WRITE, 1, &bh);
+ wait_on_buffer(bh);
+ brelse(bh);
+
+ goto resume;
+
+ error_out:
+ jfs_error(sb, "jfs_extendfs");
+
+ resume:
+ /*
+ * resume file system transactions
+ */
+ txResume(sb);
+
+ out:
+ return rc;
+}
diff --git a/fs/jfs/super.c b/fs/jfs/super.c
new file mode 100644
index 00000000000000..76744105ee1267
--- /dev/null
+++ b/fs/jfs/super.c
@@ -0,0 +1,597 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2000-2005
+ * Portions Copyright (C) Christoph Hellwig, 2001-2002
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/config.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/blkdev.h>
+#include <linux/completion.h>
+#include <asm/uaccess.h>
+#include "jfs_incore.h"
+#include "jfs_filsys.h"
+#include "jfs_metapage.h"
+#include "jfs_superblock.h"
+#include "jfs_dmap.h"
+#include "jfs_imap.h"
+#include "jfs_debug.h"
+
+MODULE_DESCRIPTION("The Journaled Filesystem (JFS)");
+MODULE_AUTHOR("Steve Best/Dave Kleikamp/Barry Arndt, IBM");
+MODULE_LICENSE("GPL");
+
+static struct super_operations jfs_super_operations;
+static struct file_system_type jfs_fs_type;
+
+int jfs_stop_threads;
+static pid_t jfsIOthread;
+static pid_t jfsCommitThread;
+static pid_t jfsSyncThread;
+DECLARE_COMPLETION(jfsIOwait);
+
+#ifdef CONFIG_JFS_DEBUG
+int jfsloglevel = JFS_LOGLEVEL_WARN;
+MODULE_PARM(jfsloglevel, "i");
+MODULE_PARM_DESC(jfsloglevel, "Specify JFS loglevel (0, 1 or 2)");
+#endif
+
+/*
+ * External declarations
+ */
+extern int jfs_mount(struct super_block *);
+extern int jfs_mount_rw(struct super_block *, int);
+extern int jfs_umount(struct super_block *);
+extern int jfs_umount_rw(struct super_block *);
+
+extern int jfsIOWait(void *);
+extern int jfs_lazycommit(void *);
+extern int jfs_sync(void *);
+extern void jfs_clear_inode(struct inode *inode);
+extern void jfs_read_inode(struct inode *inode);
+extern void jfs_dirty_inode(struct inode *inode);
+extern void jfs_delete_inode(struct inode *inode);
+extern void jfs_write_inode(struct inode *inode, int wait);
+extern int jfs_extendfs(struct super_block *, s64, int);
+
+#ifdef PROC_FS_JFS /* see jfs_debug.h */
+extern void jfs_proc_init(void);
+extern void jfs_proc_clean(void);
+#endif
+
+extern wait_queue_head_t jfs_IO_thread_wait;
+extern wait_queue_head_t jfs_commit_thread_wait;
+extern wait_queue_head_t jfs_sync_thread_wait;
+
+static void jfs_handle_error(struct super_block *sb)
+{
+ struct jfs_sb_info *sbi = JFS_SBI(sb);
+
+ if (sb->s_flags & MS_RDONLY)
+ return;
+
+ updateSuper(sb, FM_DIRTY);
+
+ if (sbi->flag & JFS_ERR_PANIC)
+ panic("JFS (device %s): panic forced after error\n",
+ bdevname(sb->s_dev));
+ else if (sbi->flag & JFS_ERR_REMOUNT_RO) {
+ jfs_err("ERROR: (device %s): remounting filesystem "
+ "as read-only\n",
+ bdevname(sb->s_dev));
+ sb->s_flags |= MS_RDONLY;
+ }
+
+ /* nothing is done for continue beyond marking the superblock dirty */
+}
+
+void jfs_error(struct super_block *sb, const char * function, ...)
+{
+ static char error_buf[256];
+ va_list args;
+
+ va_start(args, function);
+ vsprintf(error_buf, function, args);
+ va_end(args);
+
+ printk(KERN_ERR "ERROR: (device %s): %s\n", bdevname(sb->s_dev),
+ error_buf);
+
+ jfs_handle_error(sb);
+}
+
+static int jfs_statfs(struct super_block *sb, struct statfs *buf)
+{
+ struct jfs_sb_info *sbi = JFS_SBI(sb);
+ s64 maxinodes;
+ struct inomap *imap = JFS_IP(sbi->ipimap)->i_imap;
+
+ jfs_info("In jfs_statfs");
+ buf->f_type = JFS_SUPER_MAGIC;
+ buf->f_bsize = sbi->bsize;
+ buf->f_blocks = sbi->bmap->db_mapsize;
+ buf->f_bfree = sbi->bmap->db_nfree;
+ buf->f_bavail = sbi->bmap->db_nfree;
+ /*
+ * If we really return the number of allocated & free inodes, some
+ * applications will fail because they won't see enough free inodes.
+ * We'll try to calculate some guess as to how may inodes we can
+ * really allocate
+ *
+ * buf->f_files = atomic_read(&imap->im_numinos);
+ * buf->f_ffree = atomic_read(&imap->im_numfree);
+ */
+ maxinodes = min((s64) atomic_read(&imap->im_numinos) +
+ ((sbi->bmap->db_nfree >> imap->im_l2nbperiext)
+ << L2INOSPEREXT), (s64) 0xffffffffLL);
+ buf->f_files = maxinodes;
+ buf->f_ffree = maxinodes - (atomic_read(&imap->im_numinos) -
+ atomic_read(&imap->im_numfree));
+
+ buf->f_namelen = JFS_NAME_MAX;
+ return 0;
+}
+
+static void jfs_put_super(struct super_block *sb)
+{
+ struct jfs_sb_info *sbi = JFS_SBI(sb);
+ int rc;
+
+ jfs_info("In jfs_put_super");
+ rc = jfs_umount(sb);
+ if (rc)
+ jfs_err("jfs_umount failed with return code %d", rc);
+ if (sbi->nls_tab) {
+ unload_nls(sbi->nls_tab);
+ sbi->nls_tab = NULL;
+ }
+
+ kfree(sbi);
+}
+
+s64 jfs_get_volume_size(struct super_block *sb)
+{
+ uint blocks = 0;
+ s64 bytes;
+ kdev_t dev = sb->s_dev;
+ int major = MAJOR(dev);
+ int minor = MINOR(dev);
+
+ if (blk_size[major]) {
+ blocks = blk_size[major][minor];
+ if (blocks) {
+ bytes = ((s64)blocks) << BLOCK_SIZE_BITS;
+ return bytes >> sb->s_blocksize_bits;
+ }
+ }
+ return 0;
+}
+
+static int parse_options(char *options, struct super_block *sb, s64 *newLVSize,
+ int *flag)
+{
+ void *nls_map = (void *)-1; /* -1: no change; NULL: none */
+ char *this_char;
+ char *value;
+ struct jfs_sb_info *sbi = JFS_SBI(sb);
+
+ *newLVSize = 0;
+
+ if (!options)
+ return 1;
+ while ((this_char = strsep(&options, ",")) != NULL) {
+ if (!*this_char)
+ continue;
+ if ((value = strchr(this_char, '=')) != NULL)
+ *value++ = 0;
+ if (!strcmp(this_char, "errors")) {
+ if (!value || !*value)
+ goto needs_arg;
+ if (!strcmp(value, "continue")) {
+ *flag &= ~JFS_ERR_REMOUNT_RO;
+ *flag &= ~JFS_ERR_PANIC;
+ *flag |= JFS_ERR_CONTINUE;
+ } else if (!strcmp(value, "remount-ro")) {
+ *flag &= ~JFS_ERR_CONTINUE;
+ *flag &= ~JFS_ERR_PANIC;
+ *flag |= JFS_ERR_REMOUNT_RO;
+ } else if (!strcmp(value, "panic")) {
+ *flag &= ~JFS_ERR_CONTINUE;
+ *flag &= ~JFS_ERR_REMOUNT_RO;
+ *flag |= JFS_ERR_PANIC;
+ } else {
+ printk(KERN_ERR "JFS: %s is an invalid error handler\n", value);
+ goto cleanup;
+ }
+ } else if (!strcmp(this_char, "integrity")) {
+ *flag &= ~JFS_NOINTEGRITY;
+ } else if (!strcmp(this_char, "nointegrity")) {
+ *flag |= JFS_NOINTEGRITY;
+ } else if (!strcmp(this_char, "iocharset")) {
+ if (!value || !*value)
+ goto needs_arg;
+ if (nls_map && nls_map != (void *) -1)
+ unload_nls(nls_map);
+ if (!strcmp(value, "none"))
+ nls_map = NULL;
+ else
+ nls_map = load_nls(value);
+ } else if (!strcmp(this_char, "resize")) {
+ if (!value || !*value) {
+ *newLVSize = jfs_get_volume_size(sb);
+ if (*newLVSize == 0)
+ printk(KERN_ERR
+ "JFS: Cannot determine volume size\n");
+ } else
+ *newLVSize = simple_strtoull(value, &value, 0);
+
+ /* Silently ignore the quota options */
+ } else if (!strcmp(this_char, "grpquota")
+ || !strcmp(this_char, "noquota")
+ || !strcmp(this_char, "quota")
+ || !strcmp(this_char, "usrquota"))
+ /* Don't do anything ;-) */ ;
+ else {
+ printk("jfs: Unrecognized mount option %s\n",
+ this_char);
+ goto cleanup;
+ }
+ }
+ if (nls_map != (void *) -1) {
+ /* Discard old (if remount) */
+ if (sbi->nls_tab && sbi->nls_tab != (void *) -1)
+ unload_nls(sbi->nls_tab);
+ sbi->nls_tab = nls_map;
+ }
+ return 1;
+needs_arg:
+ printk(KERN_ERR "JFS: %s needs an argument\n", this_char);
+cleanup:
+ if (nls_map && nls_map != (void *) -1)
+ unload_nls(nls_map);
+ return 0;
+}
+
+static int jfs_remount(struct super_block *sb, int *flags, char *data)
+{
+ s64 newLVSize = 0;
+ int rc = 0;
+ int flag = JFS_SBI(sb)->flag;
+
+ if (!parse_options(data, sb, &newLVSize, &flag)) {
+ return -EINVAL;
+ }
+ if (newLVSize) {
+ if (sb->s_flags & MS_RDONLY) {
+ printk(KERN_ERR
+ "JFS: resize requires volume to be mounted read-write\n");
+ return -EROFS;
+ }
+ rc = jfs_extendfs(sb, newLVSize, 0);
+ if (rc)
+ return rc;
+ }
+
+ if ((sb->s_flags & MS_RDONLY) && !(*flags & MS_RDONLY)) {
+ JFS_SBI(sb)->flag = flag;
+ return jfs_mount_rw(sb, 1);
+ }
+ if ((!(sb->s_flags & MS_RDONLY)) && (*flags & MS_RDONLY)) {
+ rc = jfs_umount_rw(sb);
+ JFS_SBI(sb)->flag = flag;
+ return rc;
+ }
+ if ((JFS_SBI(sb)->flag & JFS_NOINTEGRITY) != (flag & JFS_NOINTEGRITY))
+ if (!(sb->s_flags & MS_RDONLY)) {
+ rc = jfs_umount_rw(sb);
+ if (rc)
+ return rc;
+ JFS_SBI(sb)->flag = flag;
+ return jfs_mount_rw(sb, 1);
+ }
+ JFS_SBI(sb)->flag = flag;
+
+ return 0;
+}
+
+static struct super_block *jfs_read_super(struct super_block *sb,
+ void *data, int silent)
+{
+ struct jfs_sb_info *sbi;
+ struct inode *inode;
+ int rc;
+ s64 newLVSize = 0;
+ int flag;
+
+ jfs_info("In jfs_read_super s_dev=0x%x s_flags=0x%lx", sb->s_dev,
+ sb->s_flags);
+
+ sbi = kmalloc(sizeof (struct jfs_sb_info), GFP_KERNEL);
+ if (!sbi)
+ return NULL;
+ memset(sbi, 0, sizeof (struct jfs_sb_info));
+ sb->u.generic_sbp = sbi;
+
+ /* initialize the mount flag and determine the default error handler */
+ flag = JFS_ERR_REMOUNT_RO;
+
+ /* nls_tab will be set to NULL if no character mapping is requested */
+ sbi->nls_tab = (void *) -1;
+ if (!parse_options((char *) data, sb, &newLVSize, &flag)) {
+ kfree(sbi);
+ return NULL;
+ }
+ sbi->flag = flag;
+
+ if (newLVSize) {
+ printk(KERN_ERR "resize option for remount only\n");
+ return NULL;
+ }
+
+ /*
+ * Initialize blocksize to 4K.
+ */
+ sb_set_blocksize(sb, PSIZE);
+
+ /*
+ * Set method vectors.
+ */
+ sb->s_op = &jfs_super_operations;
+
+ rc = jfs_mount(sb);
+ if (rc) {
+ if (!silent) {
+ jfs_err("jfs_mount failed w/return code = %d", rc);
+ }
+ goto out_kfree;
+ }
+ if (sb->s_flags & MS_RDONLY)
+ sbi->log = 0;
+ else {
+ rc = jfs_mount_rw(sb, 0);
+ if (rc) {
+ if (!silent) {
+ jfs_err("jfs_mount_rw failed, return code = %d",
+ rc);
+ }
+ goto out_no_rw;
+ }
+ }
+
+ sb->s_magic = JFS_SUPER_MAGIC;
+
+ inode = iget(sb, ROOT_I);
+ if (!inode || is_bad_inode(inode))
+ goto out_no_root;
+ sb->s_root = d_alloc_root(inode);
+ if (!sb->s_root)
+ goto out_no_root;
+
+ if (sbi->nls_tab == (void *) -1)
+ sbi->nls_tab = load_nls_default();
+
+ /* logical blocks are represented by 40 bits in pxd_t, etc. */
+ sb->s_maxbytes = ((u64) sb->s_blocksize) << 40;
+#if BITS_PER_LONG == 32
+ /*
+ * Page cache is indexed by long.
+ * I would use MAX_LFS_FILESIZE, but it's only half as big
+ */
+ sb->s_maxbytes = min(((u64) PAGE_CACHE_SIZE << 32) - 1, sb->s_maxbytes);
+#endif
+
+ return sb;
+
+out_no_root:
+ jfs_err("jfs_read_super: get root inode failed");
+ if (inode)
+ iput(inode);
+
+out_no_rw:
+ rc = jfs_umount(sb);
+ if (rc) {
+ jfs_err("jfs_umount failed with return code %d", rc);
+ }
+out_kfree:
+ if (sbi->nls_tab && sbi->nls_tab != (void *) -1)
+ unload_nls(sbi->nls_tab);
+ kfree(sbi);
+ return NULL;
+}
+
+static void jfs_write_super_lockfs(struct super_block *sb)
+{
+ struct jfs_sb_info *sbi = JFS_SBI(sb);
+ struct jfs_log *log = sbi->log;
+
+ if (!(sb->s_flags & MS_RDONLY)) {
+ txQuiesce(sb);
+ lmLogShutdown(log);
+ updateSuper(sb, FM_CLEAN);
+ }
+}
+
+static void jfs_unlockfs(struct super_block *sb)
+{
+ struct jfs_sb_info *sbi = JFS_SBI(sb);
+ struct jfs_log *log = sbi->log;
+ int rc = 0;
+
+ if (!(sb->s_flags & MS_RDONLY)) {
+ updateSuper(sb, FM_MOUNT);
+ if ((rc = lmLogInit(log)))
+ jfs_err("jfs_unlock failed with return code %d", rc);
+ else
+ txResume(sb);
+ }
+}
+
+
+static int jfs_sync_fs(struct super_block *sb)
+{
+ struct jfs_log *log = JFS_SBI(sb)->log;
+
+ /* log == NULL indicates read-only mount */
+ if (log)
+ jfs_flush_journal(log, 1);
+
+ return 0;
+}
+
+static struct super_operations jfs_super_operations = {
+ .read_inode = jfs_read_inode,
+ .dirty_inode = jfs_dirty_inode,
+ .write_inode = jfs_write_inode,
+ .clear_inode = jfs_clear_inode,
+ .delete_inode = jfs_delete_inode,
+ .put_super = jfs_put_super,
+ .sync_fs = jfs_sync_fs,
+ .write_super_lockfs = jfs_write_super_lockfs,
+ .unlockfs = jfs_unlockfs,
+ .statfs = jfs_statfs,
+ .remount_fs = jfs_remount,
+};
+
+static struct file_system_type jfs_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "jfs",
+ .read_super = jfs_read_super,
+ .fs_flags = FS_REQUIRES_DEV,
+};
+
+extern int metapage_init(void);
+extern int txInit(void);
+extern void txExit(void);
+extern void metapage_exit(void);
+
+static void init_once(void *foo, kmem_cache_t * cachep, unsigned long flags)
+{
+ struct jfs_inode_info *jfs_ip = (struct jfs_inode_info *) foo;
+
+ if ((flags & (SLAB_CTOR_VERIFY | SLAB_CTOR_CONSTRUCTOR)) ==
+ SLAB_CTOR_CONSTRUCTOR) {
+ memset(jfs_ip, 0, sizeof(struct jfs_inode_info));
+ INIT_LIST_HEAD(&jfs_ip->anon_inode_list);
+ init_rwsem(&jfs_ip->rdwrlock);
+ init_MUTEX(&jfs_ip->commit_sem);
+ spin_lock_init(&jfs_ip->ag_lock);
+ jfs_ip->active_ag = -1;
+ }
+}
+
+static int __init init_jfs_fs(void)
+{
+ int rc;
+
+ jfs_inode_cachep =
+ kmem_cache_create("jfs_ip", sizeof (struct jfs_inode_info), 0, 0,
+ init_once, NULL);
+ if (jfs_inode_cachep == NULL)
+ return -ENOMEM;
+
+ /*
+ * Metapage initialization
+ */
+ rc = metapage_init();
+ if (rc) {
+ jfs_err("metapage_init failed w/rc = %d", rc);
+ goto free_slab;
+ }
+
+ /*
+ * Transaction Manager initialization
+ */
+ rc = txInit();
+ if (rc) {
+ jfs_err("txInit failed w/rc = %d", rc);
+ goto free_metapage;
+ }
+
+ /*
+ * I/O completion thread (endio)
+ */
+ jfsIOthread = kernel_thread(jfsIOWait, 0,
+ CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+ if (jfsIOthread < 0) {
+ jfs_err("init_jfs_fs: fork failed w/rc = %d", jfsIOthread);
+ goto end_txmngr;
+ }
+ wait_for_completion(&jfsIOwait); /* Wait until thread starts */
+
+ jfsCommitThread = kernel_thread(jfs_lazycommit, 0,
+ CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+ if (jfsCommitThread < 0) {
+ jfs_err("init_jfs_fs: fork failed w/rc = %d", jfsCommitThread);
+ goto kill_iotask;
+ }
+ wait_for_completion(&jfsIOwait); /* Wait until thread starts */
+
+ jfsSyncThread = kernel_thread(jfs_sync, 0,
+ CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+ if (jfsSyncThread < 0) {
+ jfs_err("init_jfs_fs: fork failed w/rc = %d", jfsSyncThread);
+ goto kill_committask;
+ }
+ wait_for_completion(&jfsIOwait); /* Wait until thread starts */
+
+#ifdef PROC_FS_JFS
+ jfs_proc_init();
+#endif
+
+ return register_filesystem(&jfs_fs_type);
+
+kill_committask:
+ jfs_stop_threads = 1;
+ wake_up(&jfs_commit_thread_wait);
+ wait_for_completion(&jfsIOwait); /* Wait for thread exit */
+kill_iotask:
+ jfs_stop_threads = 1;
+ wake_up(&jfs_IO_thread_wait);
+ wait_for_completion(&jfsIOwait); /* Wait for thread exit */
+end_txmngr:
+ txExit();
+free_metapage:
+ metapage_exit();
+free_slab:
+ kmem_cache_destroy(jfs_inode_cachep);
+ return rc;
+}
+
+static void __exit exit_jfs_fs(void)
+{
+ jfs_info("exit_jfs_fs called");
+
+ jfs_stop_threads = 1;
+ txExit();
+ metapage_exit();
+ wake_up(&jfs_IO_thread_wait);
+ wait_for_completion(&jfsIOwait); /* Wait for IO thread exit */
+ wake_up(&jfs_commit_thread_wait);
+ wait_for_completion(&jfsIOwait); /* Wait for Commit thread exit */
+ wake_up(&jfs_sync_thread_wait);
+ wait_for_completion(&jfsIOwait); /* Wait for Sync thread exit */
+#ifdef PROC_FS_JFS
+ jfs_proc_clean();
+#endif
+ unregister_filesystem(&jfs_fs_type);
+ kmem_cache_destroy(jfs_inode_cachep);
+}
+
+EXPORT_NO_SYMBOLS;
+
+module_init(init_jfs_fs)
+module_exit(exit_jfs_fs)
diff --git a/fs/jfs/symlink.c b/fs/jfs/symlink.c
new file mode 100644
index 00000000000000..749a68d9f7434e
--- /dev/null
+++ b/fs/jfs/symlink.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) Christoph Hellwig, 2001-2002
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/fs.h>
+#include "jfs_incore.h"
+#include "jfs_xattr.h"
+
+static int jfs_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+ char *s = JFS_IP(dentry->d_inode)->i_inline;
+ return vfs_follow_link(nd, s);
+}
+
+static int jfs_readlink(struct dentry *dentry, char *buffer, int buflen)
+{
+ char *s = JFS_IP(dentry->d_inode)->i_inline;
+ return vfs_readlink(dentry, buffer, buflen, s);
+}
+
+struct inode_operations jfs_symlink_inode_operations = {
+ .readlink = jfs_readlink,
+ .follow_link = jfs_follow_link,
+ .setxattr = jfs_setxattr,
+ .getxattr = jfs_getxattr,
+ .listxattr = jfs_listxattr,
+ .removexattr = jfs_removexattr,
+};
+
diff --git a/fs/jfs/xattr.c b/fs/jfs/xattr.c
new file mode 100644
index 00000000000000..aff116c6457522
--- /dev/null
+++ b/fs/jfs/xattr.c
@@ -0,0 +1,947 @@
+/*
+ * Copyright (C) International Business Machines Corp., 2000-2003
+ * Copyright (C) Christoph Hellwig, 2002
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/fs.h>
+#include <linux/xattr.h>
+#include "jfs_incore.h"
+#include "jfs_superblock.h"
+#include "jfs_dmap.h"
+#include "jfs_debug.h"
+#include "jfs_dinode.h"
+#include "jfs_extent.h"
+#include "jfs_metapage.h"
+#include "jfs_xattr.h"
+
+/*
+ * jfs_xattr.c: extended attribute service
+ *
+ * Overall design --
+ *
+ * Format:
+ *
+ * Extended attribute lists (jfs_ea_list) consist of an overall size (32 bit
+ * value) and a variable (0 or more) number of extended attribute
+ * entries. Each extended attribute entry (jfs_ea) is a <name,value> double
+ * where <name> is constructed from a null-terminated ascii string
+ * (1 ... 255 bytes in the name) and <value> is arbitrary 8 bit data
+ * (1 ... 65535 bytes). The in-memory format is
+ *
+ * 0 1 2 4 4 + namelen + 1
+ * +-------+--------+--------+----------------+-------------------+
+ * | Flags | Name | Value | Name String \0 | Data . . . . |
+ * | | Length | Length | | |
+ * +-------+--------+--------+----------------+-------------------+
+ *
+ * A jfs_ea_list then is structured as
+ *
+ * 0 4 4 + EA_SIZE(ea1)
+ * +------------+-------------------+--------------------+-----
+ * | Overall EA | First FEA Element | Second FEA Element | .....
+ * | List Size | | |
+ * +------------+-------------------+--------------------+-----
+ *
+ * On-disk:
+ *
+ * FEALISTs are stored on disk using blocks allocated by dbAlloc() and
+ * written directly. An EA list may be in-lined in the inode if there is
+ * sufficient room available.
+ */
+
+struct ea_buffer {
+ int flag; /* Indicates what storage xattr points to */
+ int max_size; /* largest xattr that fits in current buffer */
+ dxd_t new_ea; /* dxd to replace ea when modifying xattr */
+ struct metapage *mp; /* metapage containing ea list */
+ struct jfs_ea_list *xattr; /* buffer containing ea list */
+};
+
+/*
+ * ea_buffer.flag values
+ */
+#define EA_INLINE 0x0001
+#define EA_EXTENT 0x0002
+#define EA_NEW 0x0004
+#define EA_MALLOC 0x0008
+
+/* Namespaces */
+#define XATTR_SYSTEM_PREFIX "system."
+#define XATTR_SYSTEM_PREFIX_LEN (sizeof (XATTR_SYSTEM_PREFIX) - 1)
+
+#define XATTR_USER_PREFIX "user."
+#define XATTR_USER_PREFIX_LEN (sizeof (XATTR_USER_PREFIX) - 1)
+
+#define XATTR_OS2_PREFIX "os2."
+#define XATTR_OS2_PREFIX_LEN (sizeof (XATTR_OS2_PREFIX) - 1)
+
+/*
+ * These three routines are used to recognize on-disk extended attributes
+ * that are in a recognized namespace. If the attribute is not recognized,
+ * "os2." is prepended to the name
+ */
+static inline int is_os2_xattr(struct jfs_ea *ea)
+{
+ /*
+ * Check for "system."
+ */
+ if ((ea->namelen >= XATTR_SYSTEM_PREFIX_LEN) &&
+ !strncmp(ea->name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN))
+ return FALSE;
+ /*
+ * Check for "user."
+ */
+ if ((ea->namelen >= XATTR_USER_PREFIX_LEN) &&
+ !strncmp(ea->name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN))
+ return FALSE;
+ /*
+ * Add any other valid namespace prefixes here
+ */
+
+ /*
+ * We assume it's OS/2's flat namespace
+ */
+ return TRUE;
+}
+
+static inline int name_size(struct jfs_ea *ea)
+{
+ if (is_os2_xattr(ea))
+ return ea->namelen + XATTR_OS2_PREFIX_LEN;
+ else
+ return ea->namelen;
+}
+
+static inline int copy_name(char *buffer, struct jfs_ea *ea)
+{
+ int len = ea->namelen;
+
+ if (is_os2_xattr(ea)) {
+ memcpy(buffer, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN);
+ buffer += XATTR_OS2_PREFIX_LEN;
+ len += XATTR_OS2_PREFIX_LEN;
+ }
+ memcpy(buffer, ea->name, ea->namelen);
+ buffer[ea->namelen] = 0;
+
+ return len;
+}
+
+/* Forward references */
+static void ea_release(struct inode *inode, struct ea_buffer *ea_buf);
+
+/*
+ * NAME: ea_write_inline
+ *
+ * FUNCTION: Attempt to write an EA inline if area is available
+ *
+ * PRE CONDITIONS:
+ * Already verified that the specified EA is small enough to fit inline
+ *
+ * PARAMETERS:
+ * ip - Inode pointer
+ * ealist - EA list pointer
+ * size - size of ealist in bytes
+ * ea - dxd_t structure to be filled in with necessary EA information
+ * if we successfully copy the EA inline
+ *
+ * NOTES:
+ * Checks if the inode's inline area is available. If so, copies EA inline
+ * and sets <ea> fields appropriately. Otherwise, returns failure, EA will
+ * have to be put into an extent.
+ *
+ * RETURNS: 0 for successful copy to inline area; -1 if area not available
+ */
+static int ea_write_inline(struct inode *ip, struct jfs_ea_list *ealist,
+ int size, dxd_t * ea)
+{
+ struct jfs_inode_info *ji = JFS_IP(ip);
+
+ /*
+ * Make sure we have an EA -- the NULL EA list is valid, but you
+ * can't copy it!
+ */
+ if (ealist && size > sizeof (struct jfs_ea_list)) {
+ assert(size <= sizeof (ji->i_inline_ea));
+
+ /*
+ * See if the space is available or if it is already being
+ * used for an inline EA.
+ */
+ if (!(ji->mode2 & INLINEEA) && !(ji->ea.flag & DXD_INLINE))
+ return -EPERM;
+
+ DXDsize(ea, size);
+ DXDlength(ea, 0);
+ DXDaddress(ea, 0);
+ memcpy(ji->i_inline_ea, ealist, size);
+ ea->flag = DXD_INLINE;
+ ji->mode2 &= ~INLINEEA;
+ } else {
+ ea->flag = 0;
+ DXDsize(ea, 0);
+ DXDlength(ea, 0);
+ DXDaddress(ea, 0);
+
+ /* Free up INLINE area */
+ if (ji->ea.flag & DXD_INLINE)
+ ji->mode2 |= INLINEEA;
+ }
+
+ mark_inode_dirty(ip);
+ return 0;
+}
+
+/*
+ * NAME: ea_write
+ *
+ * FUNCTION: Write an EA for an inode
+ *
+ * PRE CONDITIONS: EA has been verified
+ *
+ * PARAMETERS:
+ * ip - Inode pointer
+ * ealist - EA list pointer
+ * size - size of ealist in bytes
+ * ea - dxd_t structure to be filled in appropriately with where the
+ * EA was copied
+ *
+ * NOTES: Will write EA inline if able to, otherwise allocates blocks for an
+ * extent and synchronously writes it to those blocks.
+ *
+ * RETURNS: 0 for success; Anything else indicates failure
+ */
+static int ea_write(struct inode *ip, struct jfs_ea_list *ealist, int size,
+ dxd_t * ea)
+{
+ struct super_block *sb = ip->i_sb;
+ struct jfs_inode_info *ji = JFS_IP(ip);
+ struct jfs_sb_info *sbi = JFS_SBI(sb);
+ int nblocks;
+ s64 blkno;
+ int rc = 0, i;
+ char *cp;
+ s32 nbytes, nb;
+ s32 bytes_to_write;
+ struct metapage *mp;
+
+ /*
+ * Quick check to see if this is an in-linable EA. Short EAs
+ * and empty EAs are all in-linable, provided the space exists.
+ */
+ if (!ealist || size <= sizeof (ji->i_inline_ea)) {
+ if (!ea_write_inline(ip, ealist, size, ea))
+ return 0;
+ }
+
+ /* figure out how many blocks we need */
+ nblocks = (size + (sb->s_blocksize - 1)) >> sb->s_blocksize_bits;
+
+ rc = dbAlloc(ip, INOHINT(ip), nblocks, &blkno);
+ if (rc)
+ return rc;
+
+ /*
+ * Now have nblocks worth of storage to stuff into the FEALIST.
+ * loop over the FEALIST copying data into the buffer one page at
+ * a time.
+ */
+ cp = (char *) ealist;
+ nbytes = size;
+ for (i = 0; i < nblocks; i += sbi->nbperpage) {
+ /*
+ * Determine how many bytes for this request, and round up to
+ * the nearest aggregate block size
+ */
+ nb = min(PSIZE, nbytes);
+ bytes_to_write =
+ ((((nb + sb->s_blocksize - 1)) >> sb->s_blocksize_bits))
+ << sb->s_blocksize_bits;
+
+ if (!(mp = get_metapage(ip, blkno + i, bytes_to_write, 1))) {
+ rc = -EIO;
+ goto failed;
+ }
+
+ memcpy(mp->data, cp, nb);
+
+ /*
+ * We really need a way to propagate errors for
+ * forced writes like this one. --hch
+ *
+ * (__write_metapage => release_metapage => flush_metapage)
+ */
+#ifdef _JFS_FIXME
+ if ((rc = flush_metapage(mp))) {
+ /*
+ * the write failed -- this means that the buffer
+ * is still assigned and the blocks are not being
+ * used. this seems like the best error recovery
+ * we can get ...
+ */
+ goto failed;
+ }
+#else
+ flush_metapage(mp);
+#endif
+
+ cp += PSIZE;
+ nbytes -= nb;
+ }
+
+ ea->flag = DXD_EXTENT;
+ DXDsize(ea, le32_to_cpu(ealist->size));
+ DXDlength(ea, nblocks);
+ DXDaddress(ea, blkno);
+
+ /* Free up INLINE area */
+ if (ji->ea.flag & DXD_INLINE)
+ ji->mode2 |= INLINEEA;
+
+ return 0;
+
+ failed:
+ dbFree(ip, blkno, nblocks);
+ return rc;
+}
+
+/*
+ * NAME: ea_read_inline
+ *
+ * FUNCTION: Read an inlined EA into user's buffer
+ *
+ * PARAMETERS:
+ * ip - Inode pointer
+ * ealist - Pointer to buffer to fill in with EA
+ *
+ * RETURNS: 0
+ */
+static int ea_read_inline(struct inode *ip, struct jfs_ea_list *ealist)
+{
+ struct jfs_inode_info *ji = JFS_IP(ip);
+ int ea_size = sizeDXD(&ji->ea);
+
+ if (ea_size == 0) {
+ ealist->size = 0;
+ return 0;
+ }
+
+ /* Sanity Check */
+ if ((sizeDXD(&ji->ea) > sizeof (ji->i_inline_ea)))
+ return -EIO;
+ if (le32_to_cpu(((struct jfs_ea_list *) &ji->i_inline_ea)->size)
+ != ea_size)
+ return -EIO;
+
+ memcpy(ealist, ji->i_inline_ea, ea_size);
+ return 0;
+}
+
+/*
+ * NAME: ea_read
+ *
+ * FUNCTION: copy EA data into user's buffer
+ *
+ * PARAMETERS:
+ * ip - Inode pointer
+ * ealist - Pointer to buffer to fill in with EA
+ *
+ * NOTES: If EA is inline calls ea_read_inline() to copy EA.
+ *
+ * RETURNS: 0 for success; other indicates failure
+ */
+static int ea_read(struct inode *ip, struct jfs_ea_list *ealist)
+{
+ struct super_block *sb = ip->i_sb;
+ struct jfs_inode_info *ji = JFS_IP(ip);
+ struct jfs_sb_info *sbi = JFS_SBI(sb);
+ int nblocks;
+ s64 blkno;
+ char *cp = (char *) ealist;
+ int i;
+ int nbytes, nb;
+ s32 bytes_to_read;
+ struct metapage *mp;
+
+ /* quick check for in-line EA */
+ if (ji->ea.flag & DXD_INLINE)
+ return ea_read_inline(ip, ealist);
+
+ nbytes = sizeDXD(&ji->ea);
+ if (!nbytes) {
+ jfs_error(sb, "ea_read: nbytes is 0");
+ return -EIO;
+ }
+
+ /*
+ * Figure out how many blocks were allocated when this EA list was
+ * originally written to disk.
+ */
+ nblocks = lengthDXD(&ji->ea) << sbi->l2nbperpage;
+ blkno = addressDXD(&ji->ea) << sbi->l2nbperpage;
+
+ /*
+ * I have found the disk blocks which were originally used to store
+ * the FEALIST. now i loop over each contiguous block copying the
+ * data into the buffer.
+ */
+ for (i = 0; i < nblocks; i += sbi->nbperpage) {
+ /*
+ * Determine how many bytes for this request, and round up to
+ * the nearest aggregate block size
+ */
+ nb = min(PSIZE, nbytes);
+ bytes_to_read =
+ ((((nb + sb->s_blocksize - 1)) >> sb->s_blocksize_bits))
+ << sb->s_blocksize_bits;
+
+ if (!(mp = read_metapage(ip, blkno + i, bytes_to_read, 1)))
+ return -EIO;
+
+ memcpy(cp, mp->data, nb);
+ release_metapage(mp);
+
+ cp += PSIZE;
+ nbytes -= nb;
+ }
+
+ return 0;
+}
+
+/*
+ * NAME: ea_get
+ *
+ * FUNCTION: Returns buffer containing existing extended attributes.
+ * The size of the buffer will be the larger of the existing
+ * attributes size, or min_size.
+ *
+ * The buffer, which may be inlined in the inode or in the
+ * page cache must be release by calling ea_release or ea_put
+ *
+ * PARAMETERS:
+ * inode - Inode pointer
+ * ea_buf - Structure to be populated with ealist and its metadata
+ * min_size- minimum size of buffer to be returned
+ *
+ * RETURNS: 0 for success; Other indicates failure
+ */
+static int ea_get(struct inode *inode, struct ea_buffer *ea_buf, int min_size)
+{
+ struct jfs_inode_info *ji = JFS_IP(inode);
+ struct super_block *sb = inode->i_sb;
+ int size;
+ int ea_size = sizeDXD(&ji->ea);
+ int blocks_needed, current_blocks;
+ s64 blkno;
+ int rc;
+
+ /* When fsck.jfs clears a bad ea, it doesn't clear the size */
+ if (ji->ea.flag == 0)
+ ea_size = 0;
+
+ if (ea_size == 0) {
+ if (min_size == 0) {
+ ea_buf->flag = 0;
+ ea_buf->max_size = 0;
+ ea_buf->xattr = NULL;
+ return 0;
+ }
+ if ((min_size <= sizeof (ji->i_inline_ea)) &&
+ (ji->mode2 & INLINEEA)) {
+ ea_buf->flag = EA_INLINE | EA_NEW;
+ ea_buf->max_size = sizeof (ji->i_inline_ea);
+ ea_buf->xattr = (struct jfs_ea_list *) ji->i_inline_ea;
+ DXDlength(&ea_buf->new_ea, 0);
+ DXDaddress(&ea_buf->new_ea, 0);
+ ea_buf->new_ea.flag = DXD_INLINE;
+ DXDsize(&ea_buf->new_ea, min_size);
+ return 0;
+ }
+ current_blocks = 0;
+ } else if (ji->ea.flag & DXD_INLINE) {
+ if (min_size <= sizeof (ji->i_inline_ea)) {
+ ea_buf->flag = EA_INLINE;
+ ea_buf->max_size = sizeof (ji->i_inline_ea);
+ ea_buf->xattr = (struct jfs_ea_list *) ji->i_inline_ea;
+ goto size_check;
+ }
+ current_blocks = 0;
+ } else {
+ if (!(ji->ea.flag & DXD_EXTENT)) {
+ jfs_error(sb, "ea_get: invalid ea.flag)");
+ return -EIO;
+ }
+ current_blocks = (ea_size + sb->s_blocksize - 1) >>
+ sb->s_blocksize_bits;
+ }
+ size = max(min_size, ea_size);
+
+ if (size > PSIZE) {
+ /*
+ * To keep the rest of the code simple. Allocate a
+ * contiguous buffer to work with
+ */
+ ea_buf->xattr = kmalloc(size, GFP_KERNEL);
+ if (ea_buf->xattr == NULL)
+ return -ENOMEM;
+
+ ea_buf->flag = EA_MALLOC;
+ ea_buf->max_size = (size + sb->s_blocksize - 1) &
+ ~(sb->s_blocksize - 1);
+
+ if (ea_size == 0)
+ return 0;
+
+ if ((rc = ea_read(inode, ea_buf->xattr))) {
+ kfree(ea_buf->xattr);
+ ea_buf->xattr = NULL;
+ return rc;
+ }
+ goto size_check;
+ }
+ blocks_needed = (min_size + sb->s_blocksize - 1) >>
+ sb->s_blocksize_bits;
+
+ if (blocks_needed > current_blocks) {
+ rc = dbAlloc(inode, INOHINT(inode), (s64) blocks_needed,
+ &blkno);
+ if (rc)
+ return rc;
+
+ DXDlength(&ea_buf->new_ea, blocks_needed);
+ DXDaddress(&ea_buf->new_ea, blkno);
+ ea_buf->new_ea.flag = DXD_EXTENT;
+ DXDsize(&ea_buf->new_ea, min_size);
+
+ ea_buf->flag = EA_EXTENT | EA_NEW;
+
+ ea_buf->mp = get_metapage(inode, blkno,
+ blocks_needed << sb->s_blocksize_bits,
+ 1);
+ if (ea_buf->mp == NULL) {
+ dbFree(inode, blkno, (s64) blocks_needed);
+ return -EIO;
+ }
+ ea_buf->xattr = ea_buf->mp->data;
+ ea_buf->max_size = (min_size + sb->s_blocksize - 1) &
+ ~(sb->s_blocksize - 1);
+ if (ea_size == 0)
+ return 0;
+ if ((rc = ea_read(inode, ea_buf->xattr))) {
+ discard_metapage(ea_buf->mp);
+ dbFree(inode, blkno, (s64) blocks_needed);
+ return rc;
+ }
+ goto size_check;
+ }
+ ea_buf->flag = EA_EXTENT;
+ ea_buf->mp = read_metapage(inode, addressDXD(&ji->ea),
+ lengthDXD(&ji->ea) << sb->s_blocksize_bits,
+ 1);
+ if (ea_buf->mp == NULL)
+ return -EIO;
+ ea_buf->xattr = ea_buf->mp->data;
+ ea_buf->max_size = (ea_size + sb->s_blocksize - 1) &
+ ~(sb->s_blocksize - 1);
+
+ size_check:
+ if (EALIST_SIZE(ea_buf->xattr) != ea_size) {
+ printk(KERN_ERR "ea_get: invalid extended attribute\n");
+ dump_mem("xattr", ea_buf->xattr, ea_size);
+ ea_release(inode, ea_buf);
+ return -EIO;
+ }
+
+ return ea_size;
+}
+
+static void ea_release(struct inode *inode, struct ea_buffer *ea_buf)
+{
+ if (ea_buf->flag & EA_MALLOC)
+ kfree(ea_buf->xattr);
+ else if (ea_buf->flag & EA_EXTENT) {
+ assert(ea_buf->mp);
+ release_metapage(ea_buf->mp);
+
+ if (ea_buf->flag & EA_NEW)
+ dbFree(inode, addressDXD(&ea_buf->new_ea),
+ lengthDXD(&ea_buf->new_ea));
+ }
+}
+
+static int ea_put(struct inode *inode, struct ea_buffer *ea_buf, int new_size)
+{
+ struct jfs_inode_info *ji = JFS_IP(inode);
+ unsigned long old_blocks, new_blocks;
+ int rc = 0;
+ tid_t tid;
+
+ if (new_size == 0) {
+ ea_release(inode, ea_buf);
+ ea_buf = 0;
+ } else if (ea_buf->flag & EA_INLINE) {
+ assert(new_size <= sizeof (ji->i_inline_ea));
+ ji->mode2 &= ~INLINEEA;
+ ea_buf->new_ea.flag = DXD_INLINE;
+ DXDsize(&ea_buf->new_ea, new_size);
+ DXDaddress(&ea_buf->new_ea, 0);
+ DXDlength(&ea_buf->new_ea, 0);
+ } else if (ea_buf->flag & EA_MALLOC) {
+ rc = ea_write(inode, ea_buf->xattr, new_size, &ea_buf->new_ea);
+ kfree(ea_buf->xattr);
+ } else if (ea_buf->flag & EA_NEW) {
+ /* We have already allocated a new dxd */
+ flush_metapage(ea_buf->mp);
+ } else {
+ /* ->xattr must point to original ea's metapage */
+ rc = ea_write(inode, ea_buf->xattr, new_size, &ea_buf->new_ea);
+ discard_metapage(ea_buf->mp);
+ }
+ if (rc)
+ return rc;
+
+ tid = txBegin(inode->i_sb, 0);
+ down(&ji->commit_sem);
+
+ old_blocks = new_blocks = 0;
+
+ if (ji->ea.flag & DXD_EXTENT) {
+ invalidate_dxd_metapages(inode, ji->ea);
+ old_blocks = lengthDXD(&ji->ea);
+ }
+
+ if (ea_buf) {
+ txEA(tid, inode, &ji->ea, &ea_buf->new_ea);
+ if (ea_buf->new_ea.flag & DXD_EXTENT) {
+ new_blocks = lengthDXD(&ea_buf->new_ea);
+ if (ji->ea.flag & DXD_INLINE)
+ ji->mode2 |= INLINEEA;
+ }
+ ji->ea = ea_buf->new_ea;
+ } else {
+ txEA(tid, inode, &ji->ea, 0);
+ if (ji->ea.flag & DXD_INLINE)
+ ji->mode2 |= INLINEEA;
+ ji->ea.flag = 0;
+ ji->ea.size = 0;
+ }
+
+ inode->i_blocks += LBLK2PBLK(inode->i_sb, new_blocks - old_blocks);
+ rc = txCommit(tid, 1, &inode, 0);
+ txEnd(tid);
+ up(&ji->commit_sem);
+
+ return rc;
+}
+
+static int can_set_xattr(struct inode *inode, const char *name,
+ void *value, size_t value_len)
+{
+ if (IS_RDONLY(inode))
+ return -EROFS;
+
+ if (IS_IMMUTABLE(inode) || IS_APPEND(inode) || S_ISLNK(inode->i_mode))
+ return -EPERM;
+
+ if((strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) != 0) &&
+ (strncmp(name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN) != 0))
+ return -EOPNOTSUPP;
+
+ if (!S_ISREG(inode->i_mode) &&
+ (!S_ISDIR(inode->i_mode) || inode->i_mode &S_ISVTX))
+ return -EPERM;
+
+ return permission(inode, MAY_WRITE);
+}
+
+int __jfs_setxattr(struct inode *inode, const char *name, void *value,
+ size_t value_len, int flags)
+{
+ struct jfs_ea_list *ealist;
+ struct jfs_ea *ea, *old_ea = NULL, *next_ea = NULL;
+ struct ea_buffer ea_buf;
+ int old_ea_size = 0;
+ int xattr_size;
+ int new_size;
+ int namelen = strlen(name);
+ char *os2name = NULL;
+ int found = 0;
+ int rc;
+ int length;
+
+ if ((rc = can_set_xattr(inode, name, value, value_len)))
+ return rc;
+
+ if (strncmp(name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN) == 0) {
+ os2name = kmalloc(namelen - XATTR_OS2_PREFIX_LEN + 1,
+ GFP_KERNEL);
+ if (!os2name)
+ return -ENOMEM;
+ strcpy(os2name, name + XATTR_OS2_PREFIX_LEN);
+ name = os2name;
+ namelen -= XATTR_OS2_PREFIX_LEN;
+ }
+
+ xattr_size = ea_get(inode, &ea_buf, 0);
+ if (xattr_size < 0) {
+ rc = xattr_size;
+ goto out;
+ }
+
+ again:
+ ealist = (struct jfs_ea_list *) ea_buf.xattr;
+ new_size = sizeof (struct jfs_ea_list);
+
+ if (xattr_size) {
+ for (ea = FIRST_EA(ealist); ea < END_EALIST(ealist);
+ ea = NEXT_EA(ea)) {
+ if ((namelen == ea->namelen) &&
+ (memcmp(name, ea->name, namelen) == 0)) {
+ found = 1;
+ if (flags & XATTR_CREATE) {
+ rc = -EEXIST;
+ goto release;
+ }
+ old_ea = ea;
+ old_ea_size = EA_SIZE(ea);
+ next_ea = NEXT_EA(ea);
+ } else
+ new_size += EA_SIZE(ea);
+ }
+ }
+
+ if (!found) {
+ if (flags & XATTR_REPLACE) {
+ rc = -ENODATA;
+ goto release;
+ }
+ if (value == NULL) {
+ rc = 0;
+ goto release;
+ }
+ }
+ if (value)
+ new_size += sizeof (struct jfs_ea) + namelen + 1 + value_len;
+
+ if (new_size > ea_buf.max_size) {
+ /*
+ * We need to allocate more space for merged ea list.
+ * We should only have loop to again: once.
+ */
+ ea_release(inode, &ea_buf);
+ xattr_size = ea_get(inode, &ea_buf, new_size);
+ if (xattr_size < 0) {
+ rc = xattr_size;
+ goto out;
+ }
+ goto again;
+ }
+
+ /* Remove old ea of the same name */
+ if (found) {
+ /* number of bytes following target EA */
+ length = (char *) END_EALIST(ealist) - (char *) next_ea;
+ if (length > 0)
+ memmove(old_ea, next_ea, length);
+ xattr_size -= old_ea_size;
+ }
+
+ /* Add new entry to the end */
+ if (value) {
+ if (xattr_size == 0)
+ /* Completely new ea list */
+ xattr_size = sizeof (struct jfs_ea_list);
+
+ ea = (struct jfs_ea *) ((char *) ealist + xattr_size);
+ ea->flag = 0;
+ ea->namelen = namelen;
+ ea->valuelen = (cpu_to_le16(value_len));
+ memcpy(ea->name, name, namelen);
+ ea->name[namelen] = 0;
+ if (value_len)
+ memcpy(&ea->name[namelen + 1], value, value_len);
+ xattr_size += EA_SIZE(ea);
+ }
+
+ /* DEBUG - If we did this right, these number match */
+ if (xattr_size != new_size) {
+ printk(KERN_ERR
+ "jfs_xsetattr: xattr_size = %d, new_size = %d\n",
+ xattr_size, new_size);
+
+ rc = -EINVAL;
+ goto release;
+ }
+
+ /*
+ * If we're left with an empty list, there's no ea
+ */
+ if (new_size == sizeof (struct jfs_ea_list))
+ new_size = 0;
+
+ ealist->size = cpu_to_le32(new_size);
+
+ rc = ea_put(inode, &ea_buf, new_size);
+
+ goto out;
+ release:
+ ea_release(inode, &ea_buf);
+ out:
+ if (os2name)
+ kfree(os2name);
+
+ return rc;
+}
+
+int jfs_setxattr(struct dentry *dentry, const char *name, void *value,
+ size_t value_len, int flags)
+{
+ if (value == NULL) { /* empty EA, do not remove */
+ value = "";
+ value_len = 0;
+ }
+
+ return __jfs_setxattr(dentry->d_inode, name, value, value_len, flags);
+}
+
+static inline int can_get_xattr(struct inode *inode, const char *name)
+{
+ if(strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN) == 0)
+ return 0;
+ return permission(inode, MAY_READ);
+}
+
+ssize_t __jfs_getxattr(struct inode *inode, const char *name, void *data,
+ size_t buf_size)
+{
+ struct jfs_ea_list *ealist;
+ struct jfs_ea *ea;
+ struct ea_buffer ea_buf;
+ int xattr_size;
+ ssize_t size;
+ int namelen = strlen(name);
+ char *os2name = NULL;
+ int rc;
+ char *value;
+
+ if ((rc = can_get_xattr(inode, name)))
+ return rc;
+
+ if (strncmp(name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN) == 0) {
+ os2name = kmalloc(namelen - XATTR_OS2_PREFIX_LEN + 1,
+ GFP_KERNEL);
+ if (!os2name)
+ return -ENOMEM;
+ strcpy(os2name, name + XATTR_OS2_PREFIX_LEN);
+ name = os2name;
+ namelen -= XATTR_OS2_PREFIX_LEN;
+ }
+
+ xattr_size = ea_get(inode, &ea_buf, 0);
+ if (xattr_size < 0) {
+ size = xattr_size;
+ goto out;
+ }
+
+ if (xattr_size == 0)
+ goto not_found;
+
+ ealist = (struct jfs_ea_list *) ea_buf.xattr;
+
+ /* Find the named attribute */
+ for (ea = FIRST_EA(ealist); ea < END_EALIST(ealist); ea = NEXT_EA(ea))
+ if ((namelen == ea->namelen) &&
+ memcmp(name, ea->name, namelen) == 0) {
+ /* Found it */
+ size = le16_to_cpu(ea->valuelen);
+ if (!data)
+ goto release;
+ else if (size > buf_size) {
+ size = -ERANGE;
+ goto release;
+ }
+ value = ((char *) &ea->name) + ea->namelen + 1;
+ memcpy(data, value, size);
+ goto release;
+ }
+ not_found:
+ size = -ENODATA;
+ release:
+ ea_release(inode, &ea_buf);
+ out:
+ if (os2name)
+ kfree(os2name);
+
+ return size;
+}
+
+ssize_t jfs_getxattr(struct dentry *dentry, const char *name, void *data,
+ size_t buf_size)
+{
+ return __jfs_getxattr(dentry->d_inode, name, data, buf_size);
+}
+
+ssize_t jfs_listxattr(struct dentry * dentry, char *data, size_t buf_size)
+{
+ struct inode *inode = dentry->d_inode;
+ char *buffer;
+ ssize_t size = 0;
+ int xattr_size;
+ struct jfs_ea_list *ealist;
+ struct jfs_ea *ea;
+ struct ea_buffer ea_buf;
+
+ xattr_size = ea_get(inode, &ea_buf, 0);
+ if (xattr_size < 0) {
+ size = xattr_size;
+ goto out;
+ }
+
+ if (xattr_size == 0)
+ goto release;
+
+ ealist = (struct jfs_ea_list *) ea_buf.xattr;
+
+ /* compute required size of list */
+ for (ea = FIRST_EA(ealist); ea < END_EALIST(ealist); ea = NEXT_EA(ea))
+ size += name_size(ea) + 1;
+
+ if (!data)
+ goto release;
+
+ if (size > buf_size) {
+ size = -ERANGE;
+ goto release;
+ }
+
+ /* Copy attribute names to buffer */
+ buffer = data;
+ for (ea = FIRST_EA(ealist); ea < END_EALIST(ealist); ea = NEXT_EA(ea)) {
+ int namelen = copy_name(buffer, ea);
+ buffer += namelen + 1;
+ }
+
+ release:
+ ea_release(inode, &ea_buf);
+ out:
+ return size;
+}
+
+int jfs_removexattr(struct dentry *dentry, const char *name)
+{
+ return __jfs_setxattr(dentry->d_inode, name, 0, 0, XATTR_REPLACE);
+}
diff --git a/fs/lockd/Makefile b/fs/lockd/Makefile
new file mode 100644
index 00000000000000..98f4ae53112b01
--- /dev/null
+++ b/fs/lockd/Makefile
@@ -0,0 +1,21 @@
+#
+# Makefile for the linux lock manager stuff
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+O_TARGET := lockd.o
+
+export-objs := lockd_syms.o
+
+obj-y := clntlock.o clntproc.o host.o svc.o svclock.o svcshare.o \
+ svcproc.o svcsubs.o mon.o xdr.o lockd_syms.o
+
+obj-$(CONFIG_LOCKD_V4) += xdr4.o svc4proc.o
+
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c
new file mode 100644
index 00000000000000..ef0f0054595e5d
--- /dev/null
+++ b/fs/lockd/clntlock.c
@@ -0,0 +1,254 @@
+/*
+ * linux/fs/lockd/clntlock.c
+ *
+ * Lock handling for the client side NLM implementation
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#define __KERNEL_SYSCALLS__
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/nfs_fs.h>
+#include <linux/unistd.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/lockd/lockd.h>
+#include <linux/smp_lock.h>
+
+#define NLMDBG_FACILITY NLMDBG_CLIENT
+
+/*
+ * Local function prototypes
+ */
+static int reclaimer(void *ptr);
+
+/*
+ * The following functions handle blocking and granting from the
+ * client perspective.
+ */
+
+/*
+ * This is the representation of a blocked client lock.
+ */
+struct nlm_wait {
+ struct nlm_wait * b_next; /* linked list */
+ wait_queue_head_t b_wait; /* where to wait on */
+ struct nlm_host * b_host;
+ struct file_lock * b_lock; /* local file lock */
+ unsigned short b_reclaim; /* got to reclaim lock */
+ u32 b_status; /* grant callback status */
+};
+
+static struct nlm_wait * nlm_blocked;
+
+/*
+ * Block on a lock
+ */
+int
+nlmclnt_block(struct nlm_host *host, struct file_lock *fl, u32 *statp)
+{
+ struct nlm_wait block, **head;
+ int err;
+ u32 pstate;
+
+ block.b_host = host;
+ block.b_lock = fl;
+ init_waitqueue_head(&block.b_wait);
+ block.b_status = NLM_LCK_BLOCKED;
+ block.b_next = nlm_blocked;
+ nlm_blocked = &block;
+
+ /* Remember pseudo nsm state */
+ pstate = host->h_state;
+
+ /* Go to sleep waiting for GRANT callback. Some servers seem
+ * to lose callbacks, however, so we're going to poll from
+ * time to time just to make sure.
+ *
+ * For now, the retry frequency is pretty high; normally
+ * a 1 minute timeout would do. See the comment before
+ * nlmclnt_lock for an explanation.
+ */
+ sleep_on_timeout(&block.b_wait, 30*HZ);
+
+ for (head = &nlm_blocked; *head; head = &(*head)->b_next) {
+ if (*head == &block) {
+ *head = block.b_next;
+ break;
+ }
+ }
+
+ if (!signalled()) {
+ *statp = block.b_status;
+ return 0;
+ }
+
+ /* Okay, we were interrupted. Cancel the pending request
+ * unless the server has rebooted.
+ */
+ if (pstate == host->h_state && (err = nlmclnt_cancel(host, fl)) < 0)
+ printk(KERN_NOTICE
+ "lockd: CANCEL call failed (errno %d)\n", -err);
+
+ return -ERESTARTSYS;
+}
+
+/*
+ * The server lockd has called us back to tell us the lock was granted
+ */
+u32
+nlmclnt_grant(struct nlm_lock *lock)
+{
+ struct nlm_wait *block;
+
+ /*
+ * Look up blocked request based on arguments.
+ * Warning: must not use cookie to match it!
+ */
+ for (block = nlm_blocked; block; block = block->b_next) {
+ if (nlm_compare_locks(block->b_lock, &lock->fl))
+ break;
+ }
+
+ /* Ooops, no blocked request found. */
+ if (block == NULL)
+ return nlm_lck_denied;
+
+ /* Alright, we found the lock. Set the return status and
+ * wake up the caller.
+ */
+ block->b_status = NLM_LCK_GRANTED;
+ wake_up(&block->b_wait);
+
+ return nlm_granted;
+}
+
+/*
+ * The following procedures deal with the recovery of locks after a
+ * server crash.
+ */
+
+/*
+ * Mark the locks for reclaiming.
+ * FIXME: In 2.5 we don't want to iterate through any global file_lock_list.
+ * Maintain NLM lock reclaiming lists in the nlm_host instead.
+ */
+static
+void nlmclnt_mark_reclaim(struct nlm_host *host)
+{
+ struct file_lock *fl;
+ struct inode *inode;
+ struct list_head *tmp;
+
+ list_for_each(tmp, &file_lock_list) {
+ fl = list_entry(tmp, struct file_lock, fl_link);
+
+ inode = fl->fl_file->f_dentry->d_inode;
+ if (inode->i_sb->s_magic != NFS_SUPER_MAGIC)
+ continue;
+ if (fl->fl_u.nfs_fl.host != host)
+ continue;
+ if (!(fl->fl_u.nfs_fl.flags & NFS_LCK_GRANTED))
+ continue;
+ fl->fl_u.nfs_fl.flags |= NFS_LCK_RECLAIM;
+ }
+}
+
+/*
+ * Someone has sent us an SM_NOTIFY. Ensure we bind to the new port number,
+ * that we mark locks for reclaiming, and that we bump the pseudo NSM state.
+ */
+static inline
+void nlmclnt_prepare_reclaim(struct nlm_host *host, u32 newstate)
+{
+ host->h_monitored = 0;
+ host->h_nsmstate = newstate;
+ host->h_state++;
+ host->h_nextrebind = 0;
+ nlm_rebind_host(host);
+ nlmclnt_mark_reclaim(host);
+ dprintk("NLM: reclaiming locks for host %s", host->h_name);
+}
+
+/*
+ * Reclaim all locks on server host. We do this by spawning a separate
+ * reclaimer thread.
+ */
+void
+nlmclnt_recovery(struct nlm_host *host, u32 newstate)
+{
+ if (host->h_reclaiming++) {
+ if (host->h_nsmstate == newstate)
+ return;
+ nlmclnt_prepare_reclaim(host, newstate);
+ } else {
+ nlmclnt_prepare_reclaim(host, newstate);
+ nlm_get_host(host);
+ MOD_INC_USE_COUNT;
+ if (kernel_thread(reclaimer, host, CLONE_SIGNAL) < 0)
+ MOD_DEC_USE_COUNT;
+ }
+}
+
+static int
+reclaimer(void *ptr)
+{
+ struct nlm_host *host = (struct nlm_host *) ptr;
+ struct nlm_wait *block;
+ struct list_head *tmp;
+ struct file_lock *fl;
+ struct inode *inode;
+
+ daemonize();
+ reparent_to_init();
+ snprintf(current->comm, sizeof(current->comm),
+ "%s-reclaim",
+ host->h_name);
+
+ /* This one ensures that our parent doesn't terminate while the
+ * reclaim is in progress */
+ lock_kernel();
+ lockd_up();
+
+ /* First, reclaim all locks that have been marked. */
+restart:
+ list_for_each(tmp, &file_lock_list) {
+ fl = list_entry(tmp, struct file_lock, fl_link);
+
+ inode = fl->fl_file->f_dentry->d_inode;
+ if (inode->i_sb->s_magic != NFS_SUPER_MAGIC)
+ continue;
+ if (fl->fl_u.nfs_fl.host != host)
+ continue;
+ if (!(fl->fl_u.nfs_fl.flags & NFS_LCK_RECLAIM))
+ continue;
+
+ fl->fl_u.nfs_fl.flags &= ~NFS_LCK_RECLAIM;
+ nlmclnt_reclaim(host, fl);
+ if (signalled())
+ break;
+ goto restart;
+ }
+
+ host->h_reclaiming = 0;
+ wake_up(&host->h_gracewait);
+
+ /* Now, wake up all processes that sleep on a blocked lock */
+ for (block = nlm_blocked; block; block = block->b_next) {
+ if (block->b_host == host) {
+ block->b_status = NLM_LCK_DENIED_GRACE_PERIOD;
+ wake_up(&block->b_wait);
+ }
+ }
+
+ /* Release host handle after use */
+ nlm_release_host(host);
+ lockd_down();
+ unlock_kernel();
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c
new file mode 100644
index 00000000000000..57ade71987eba3
--- /dev/null
+++ b/fs/lockd/clntproc.c
@@ -0,0 +1,695 @@
+/*
+ * linux/fs/lockd/clntproc.c
+ *
+ * RPC procedures for the client side NLM implementation
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/nfs_fs.h>
+#include <linux/utsname.h>
+#include <linux/smp_lock.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/lockd/lockd.h>
+#include <linux/lockd/sm_inter.h>
+
+#define NLMDBG_FACILITY NLMDBG_CLIENT
+#define NLMCLNT_GRACE_WAIT (5*HZ)
+
+static int nlmclnt_test(struct nlm_rqst *, struct file_lock *);
+static int nlmclnt_lock(struct nlm_rqst *, struct file_lock *);
+static int nlmclnt_unlock(struct nlm_rqst *, struct file_lock *);
+static void nlmclnt_unlock_callback(struct rpc_task *);
+static void nlmclnt_cancel_callback(struct rpc_task *);
+static int nlm_stat_to_errno(u32 stat);
+
+/*
+ * Cookie counter for NLM requests
+ */
+static u32 nlm_cookie = 0x1234;
+
+static inline void nlmclnt_next_cookie(struct nlm_cookie *c)
+{
+ memcpy(c->data, &nlm_cookie, 4);
+ memset(c->data+4, 0, 4);
+ c->len=4;
+ nlm_cookie++;
+}
+
+/*
+ * Initialize arguments for TEST/LOCK/UNLOCK/CANCEL calls
+ */
+static inline void
+nlmclnt_setlockargs(struct nlm_rqst *req, struct file_lock *fl)
+{
+ struct nlm_args *argp = &req->a_args;
+ struct nlm_lock *lock = &argp->lock;
+
+ nlmclnt_next_cookie(&argp->cookie);
+ argp->state = nsm_local_state;
+ memcpy(&lock->fh, NFS_FH(fl->fl_file->f_dentry->d_inode), sizeof(struct nfs_fh));
+ lock->caller = system_utsname.nodename;
+ lock->oh.data = req->a_owner;
+ lock->oh.len = sprintf(req->a_owner, "%d@%s",
+ current->pid, system_utsname.nodename);
+ locks_copy_lock(&lock->fl, fl);
+}
+
+/*
+ * Initialize arguments for GRANTED call. The nlm_rqst structure
+ * has been cleared already.
+ */
+int
+nlmclnt_setgrantargs(struct nlm_rqst *call, struct nlm_lock *lock)
+{
+ locks_copy_lock(&call->a_args.lock.fl, &lock->fl);
+ memcpy(&call->a_args.lock.fh, &lock->fh, sizeof(call->a_args.lock.fh));
+ call->a_args.lock.caller = system_utsname.nodename;
+ call->a_args.lock.oh.len = lock->oh.len;
+
+ /* set default data area */
+ call->a_args.lock.oh.data = call->a_owner;
+
+ if (lock->oh.len > NLMCLNT_OHSIZE) {
+ void *data = kmalloc(lock->oh.len, GFP_KERNEL);
+ if (!data)
+ return 0;
+ call->a_args.lock.oh.data = (u8 *) data;
+ }
+
+ memcpy(call->a_args.lock.oh.data, lock->oh.data, lock->oh.len);
+ return 1;
+}
+
+void
+nlmclnt_freegrantargs(struct nlm_rqst *call)
+{
+ /*
+ * Check whether we allocated memory for the owner.
+ */
+ if (call->a_args.lock.oh.data != (u8 *) call->a_owner) {
+ kfree(call->a_args.lock.oh.data);
+ }
+}
+
+/*
+ * This is the main entry point for the NLM client.
+ */
+int
+nlmclnt_proc(struct inode *inode, int cmd, struct file_lock *fl)
+{
+ struct nfs_server *nfssrv = NFS_SERVER(inode);
+ struct nlm_host *host;
+ struct nlm_rqst reqst, *call = &reqst;
+ sigset_t oldset;
+ unsigned long flags;
+ int status, proto, vers;
+
+ vers = (NFS_PROTO(inode)->version == 3) ? 4 : 1;
+ if (NFS_PROTO(inode)->version > 3) {
+ printk(KERN_NOTICE "NFSv4 file locking not implemented!\n");
+ return -ENOLCK;
+ }
+
+ /* Retrieve transport protocol from NFS client */
+ proto = NFS_CLIENT(inode)->cl_xprt->prot;
+
+ if (!(host = nlmclnt_lookup_host(NFS_ADDR(inode), proto, vers)))
+ return -ENOLCK;
+
+ /* Create RPC client handle if not there, and copy soft
+ * and intr flags from NFS client. */
+ if (host->h_rpcclnt == NULL) {
+ struct rpc_clnt *clnt;
+
+ /* Bind an rpc client to this host handle (does not
+ * perform a portmapper lookup) */
+ if (!(clnt = nlm_bind_host(host))) {
+ status = -ENOLCK;
+ goto done;
+ }
+ clnt->cl_softrtry = nfssrv->client->cl_softrtry;
+ clnt->cl_intr = nfssrv->client->cl_intr;
+ clnt->cl_chatty = nfssrv->client->cl_chatty;
+ }
+
+ /* Keep the old signal mask */
+ spin_lock_irqsave(&current->sigmask_lock, flags);
+ oldset = current->blocked;
+
+ /* If we're cleaning up locks because the process is exiting,
+ * perform the RPC call asynchronously. */
+ if ((IS_SETLK(cmd) || IS_SETLKW(cmd))
+ && fl->fl_type == F_UNLCK
+ && (current->flags & PF_EXITING)) {
+ sigfillset(&current->blocked); /* Mask all signals */
+ recalc_sigpending(current);
+ spin_unlock_irqrestore(&current->sigmask_lock, flags);
+
+ call = nlmclnt_alloc_call();
+ if (!call) {
+ status = -ENOMEM;
+ goto out_restore;
+ }
+ call->a_flags = RPC_TASK_ASYNC;
+ } else {
+ spin_unlock_irqrestore(&current->sigmask_lock, flags);
+ memset(call, 0, sizeof(*call));
+ locks_init_lock(&call->a_args.lock.fl);
+ locks_init_lock(&call->a_res.lock.fl);
+ }
+ call->a_host = host;
+
+ /* Set up the argument struct */
+ nlmclnt_setlockargs(call, fl);
+
+ if (IS_SETLK(cmd) || IS_SETLKW(cmd)) {
+ if (fl->fl_type != F_UNLCK) {
+ call->a_args.block = IS_SETLKW(cmd) ? 1 : 0;
+ status = nlmclnt_lock(call, fl);
+ } else
+ status = nlmclnt_unlock(call, fl);
+ } else if (IS_GETLK(cmd))
+ status = nlmclnt_test(call, fl);
+ else
+ status = -EINVAL;
+
+ if (status < 0 && (call->a_flags & RPC_TASK_ASYNC))
+ kfree(call);
+
+ out_restore:
+ spin_lock_irqsave(&current->sigmask_lock, flags);
+ current->blocked = oldset;
+ recalc_sigpending(current);
+ spin_unlock_irqrestore(&current->sigmask_lock, flags);
+
+done:
+ dprintk("lockd: clnt proc returns %d\n", status);
+ nlm_release_host(host);
+ return status;
+}
+
+/*
+ * Wait while server is in grace period
+ */
+static inline int
+nlmclnt_grace_wait(struct nlm_host *host)
+{
+ if (!host->h_reclaiming)
+ interruptible_sleep_on_timeout(&host->h_gracewait, 10*HZ);
+ else
+ interruptible_sleep_on(&host->h_gracewait);
+ return signalled()? -ERESTARTSYS : 0;
+}
+
+/*
+ * Allocate an NLM RPC call struct
+ */
+struct nlm_rqst *
+nlmclnt_alloc_call(void)
+{
+ struct nlm_rqst *call;
+
+ while (!signalled()) {
+ call = (struct nlm_rqst *) kmalloc(sizeof(struct nlm_rqst), GFP_KERNEL);
+ if (call) {
+ memset(call, 0, sizeof(*call));
+ locks_init_lock(&call->a_args.lock.fl);
+ locks_init_lock(&call->a_res.lock.fl);
+ return call;
+ }
+ printk("nlmclnt_alloc_call: failed, waiting for memory\n");
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(5*HZ);
+ }
+ return NULL;
+}
+
+/*
+ * Generic NLM call
+ */
+int
+nlmclnt_call(struct nlm_rqst *req, u32 proc)
+{
+ struct nlm_host *host = req->a_host;
+ struct rpc_clnt *clnt;
+ struct nlm_args *argp = &req->a_args;
+ struct nlm_res *resp = &req->a_res;
+ struct file *filp = argp->lock.fl.fl_file;
+ struct rpc_message msg;
+ int status;
+
+ dprintk("lockd: call procedure %s on %s\n",
+ nlm_procname(proc), host->h_name);
+
+ msg.rpc_proc = proc;
+ msg.rpc_argp = argp;
+ msg.rpc_resp = resp;
+ if (filp)
+ msg.rpc_cred = nfs_file_cred(filp);
+ else
+ msg.rpc_cred = NULL;
+
+ do {
+ if (host->h_reclaiming && !argp->reclaim) {
+ interruptible_sleep_on(&host->h_gracewait);
+ continue;
+ }
+
+ /* If we have no RPC client yet, create one. */
+ if ((clnt = nlm_bind_host(host)) == NULL)
+ return -ENOLCK;
+
+ /* Perform the RPC call. If an error occurs, try again */
+ if ((status = rpc_call_sync(clnt, &msg, 0)) < 0) {
+ dprintk("lockd: rpc_call returned error %d\n", -status);
+ switch (status) {
+ case -EPROTONOSUPPORT:
+ status = -EINVAL;
+ break;
+ case -ECONNREFUSED:
+ case -ETIMEDOUT:
+ case -ENOTCONN:
+ nlm_rebind_host(host);
+ status = -EAGAIN;
+ break;
+ case -ERESTARTSYS:
+ return signalled () ? -EINTR : status;
+ default:
+ break;
+ }
+ break;
+ } else
+ if (resp->status == NLM_LCK_DENIED_GRACE_PERIOD) {
+ dprintk("lockd: server in grace period\n");
+ if (argp->reclaim) {
+ printk(KERN_WARNING
+ "lockd: spurious grace period reject?!\n");
+ return -ENOLCK;
+ }
+ } else {
+ dprintk("lockd: server returns status %d\n", resp->status);
+ return 0; /* Okay, call complete */
+ }
+
+ /* Back off a little and try again */
+ interruptible_sleep_on_timeout(&host->h_gracewait, 15*HZ);
+
+ /* When the lock requested by F_SETLKW isn't available,
+ we will wait until the request can be satisfied. If
+ a signal is received during wait, we should return
+ -EINTR. */
+ if (signalled ()) {
+ status = -EINTR;
+ break;
+ }
+ } while (1);
+
+ return status;
+}
+
+/*
+ * Generic NLM call, async version.
+ */
+int
+nlmsvc_async_call(struct nlm_rqst *req, u32 proc, rpc_action callback)
+{
+ struct nlm_host *host = req->a_host;
+ struct rpc_clnt *clnt;
+ struct nlm_args *argp = &req->a_args;
+ struct nlm_res *resp = &req->a_res;
+ struct rpc_message msg;
+ int status;
+
+ dprintk("lockd: call procedure %s on %s (async)\n",
+ nlm_procname(proc), host->h_name);
+
+ /* If we have no RPC client yet, create one. */
+ if ((clnt = nlm_bind_host(host)) == NULL)
+ return -ENOLCK;
+
+ /* bootstrap and kick off the async RPC call */
+ msg.rpc_proc = proc;
+ msg.rpc_argp = argp;
+ msg.rpc_resp =resp;
+ msg.rpc_cred = NULL;
+ status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, callback, req);
+
+ return status;
+}
+
+int
+nlmclnt_async_call(struct nlm_rqst *req, u32 proc, rpc_action callback)
+{
+ struct nlm_host *host = req->a_host;
+ struct rpc_clnt *clnt;
+ struct nlm_args *argp = &req->a_args;
+ struct nlm_res *resp = &req->a_res;
+ struct file *file = argp->lock.fl.fl_file;
+ struct rpc_message msg;
+ int status;
+
+ dprintk("lockd: call procedure %s on %s (async)\n",
+ nlm_procname(proc), host->h_name);
+
+ /* If we have no RPC client yet, create one. */
+ if ((clnt = nlm_bind_host(host)) == NULL)
+ return -ENOLCK;
+
+ /* bootstrap and kick off the async RPC call */
+ msg.rpc_proc = proc;
+ msg.rpc_argp = argp;
+ msg.rpc_resp =resp;
+ if (file)
+ msg.rpc_cred = nfs_file_cred(file);
+ else
+ msg.rpc_cred = NULL;
+ /* Increment host refcount */
+ nlm_get_host(host);
+ status = rpc_call_async(clnt, &msg, RPC_TASK_ASYNC, callback, req);
+ if (status < 0)
+ nlm_release_host(host);
+ return status;
+}
+
+/*
+ * TEST for the presence of a conflicting lock
+ */
+static int
+nlmclnt_test(struct nlm_rqst *req, struct file_lock *fl)
+{
+ int status;
+
+ if ((status = nlmclnt_call(req, NLMPROC_TEST)) < 0)
+ return status;
+
+ status = req->a_res.status;
+ if (status == NLM_LCK_GRANTED) {
+ fl->fl_type = F_UNLCK;
+ } if (status == NLM_LCK_DENIED) {
+ /*
+ * Report the conflicting lock back to the application.
+ * FIXME: Is it OK to report the pid back as well?
+ */
+ locks_copy_lock(fl, &req->a_res.lock.fl);
+ /* fl->fl_pid = 0; */
+ } else {
+ return nlm_stat_to_errno(req->a_res.status);
+ }
+
+ return 0;
+}
+
+static
+void nlmclnt_insert_lock_callback(struct file_lock *fl)
+{
+ nlm_get_host(fl->fl_u.nfs_fl.host);
+}
+static
+void nlmclnt_remove_lock_callback(struct file_lock *fl)
+{
+ if (fl->fl_u.nfs_fl.host) {
+ nlm_release_host(fl->fl_u.nfs_fl.host);
+ fl->fl_u.nfs_fl.host = NULL;
+ }
+}
+
+/*
+ * LOCK: Try to create a lock
+ *
+ * Programmer Harassment Alert
+ *
+ * When given a blocking lock request in a sync RPC call, the HPUX lockd
+ * will faithfully return LCK_BLOCKED but never cares to notify us when
+ * the lock could be granted. This way, our local process could hang
+ * around forever waiting for the callback.
+ *
+ * Solution A: Implement busy-waiting
+ * Solution B: Use the async version of the call (NLM_LOCK_{MSG,RES})
+ *
+ * For now I am implementing solution A, because I hate the idea of
+ * re-implementing lockd for a third time in two months. The async
+ * calls shouldn't be too hard to do, however.
+ *
+ * This is one of the lovely things about standards in the NFS area:
+ * they're so soft and squishy you can't really blame HP for doing this.
+ */
+static int
+nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl)
+{
+ struct nlm_host *host = req->a_host;
+ struct nlm_res *resp = &req->a_res;
+ int status;
+
+ if (!host->h_monitored && nsm_monitor(host) < 0) {
+ printk(KERN_NOTICE "lockd: failed to monitor %s\n",
+ host->h_name);
+ return -ENOLCK;
+ }
+
+ do {
+ if ((status = nlmclnt_call(req, NLMPROC_LOCK)) >= 0) {
+ if (resp->status != NLM_LCK_BLOCKED)
+ break;
+ status = nlmclnt_block(host, fl, &resp->status);
+ }
+ if (status < 0)
+ return status;
+ } while (resp->status == NLM_LCK_BLOCKED && req->a_args.block);
+
+ if (resp->status == NLM_LCK_GRANTED) {
+ fl->fl_u.nfs_fl.state = host->h_state;
+ fl->fl_u.nfs_fl.flags |= NFS_LCK_GRANTED;
+ fl->fl_u.nfs_fl.host = host;
+ fl->fl_insert = nlmclnt_insert_lock_callback;
+ fl->fl_remove = nlmclnt_remove_lock_callback;
+ }
+
+ return nlm_stat_to_errno(resp->status);
+}
+
+/*
+ * RECLAIM: Try to reclaim a lock
+ */
+int
+nlmclnt_reclaim(struct nlm_host *host, struct file_lock *fl)
+{
+ struct nlm_rqst reqst, *req;
+ int status;
+
+ req = &reqst;
+ memset(req, 0, sizeof(*req));
+ locks_init_lock(&req->a_args.lock.fl);
+ locks_init_lock(&req->a_res.lock.fl);
+ req->a_host = host;
+ req->a_flags = 0;
+
+ /* Set up the argument struct */
+ nlmclnt_setlockargs(req, fl);
+ req->a_args.reclaim = 1;
+
+ if ((status = nlmclnt_call(req, NLMPROC_LOCK)) >= 0
+ && req->a_res.status == NLM_LCK_GRANTED)
+ return 0;
+
+ printk(KERN_WARNING "lockd: failed to reclaim lock for pid %d "
+ "(errno %d, status %d)\n", fl->fl_pid,
+ status, req->a_res.status);
+
+ /*
+ * FIXME: This is a serious failure. We can
+ *
+ * a. Ignore the problem
+ * b. Send the owning process some signal (Linux doesn't have
+ * SIGLOST, though...)
+ * c. Retry the operation
+ *
+ * Until someone comes up with a simple implementation
+ * for b or c, I'll choose option a.
+ */
+
+ return -ENOLCK;
+}
+
+/*
+ * UNLOCK: remove an existing lock
+ */
+static int
+nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl)
+{
+ struct nlm_res *resp = &req->a_res;
+ int status;
+
+ /* Clean the GRANTED flag now so the lock doesn't get
+ * reclaimed while we're stuck in the unlock call. */
+ fl->fl_u.nfs_fl.flags &= ~NFS_LCK_GRANTED;
+
+ if (req->a_flags & RPC_TASK_ASYNC) {
+ return nlmclnt_async_call(req, NLMPROC_UNLOCK,
+ nlmclnt_unlock_callback);
+ }
+
+ if ((status = nlmclnt_call(req, NLMPROC_UNLOCK)) < 0)
+ return status;
+
+ if (resp->status == NLM_LCK_GRANTED)
+ return 0;
+
+ if (resp->status != NLM_LCK_DENIED_NOLOCKS)
+ printk("lockd: unexpected unlock status: %d\n", resp->status);
+
+ /* What to do now? I'm out of my depth... */
+
+ return -ENOLCK;
+}
+
+static void
+nlmclnt_unlock_callback(struct rpc_task *task)
+{
+ struct nlm_rqst *req = (struct nlm_rqst *) task->tk_calldata;
+ int status = req->a_res.status;
+
+ if (RPC_ASSASSINATED(task))
+ goto die;
+
+ if (task->tk_status < 0) {
+ dprintk("lockd: unlock failed (err = %d)\n", -task->tk_status);
+ goto retry_rebind;
+ }
+ if (status == NLM_LCK_DENIED_GRACE_PERIOD) {
+ rpc_delay(task, NLMCLNT_GRACE_WAIT);
+ goto retry_unlock;
+ }
+ if (status != NLM_LCK_GRANTED)
+ printk(KERN_WARNING "lockd: unexpected unlock status: %d\n", status);
+
+die:
+ nlm_release_host(req->a_host);
+ kfree(req);
+ return;
+ retry_rebind:
+ nlm_rebind_host(req->a_host);
+ retry_unlock:
+ rpc_restart_call(task);
+}
+
+/*
+ * Cancel a blocked lock request.
+ * We always use an async RPC call for this in order not to hang a
+ * process that has been Ctrl-C'ed.
+ */
+int
+nlmclnt_cancel(struct nlm_host *host, struct file_lock *fl)
+{
+ struct nlm_rqst *req;
+ unsigned long flags;
+ sigset_t oldset;
+ int status;
+
+ /* Block all signals while setting up call */
+ spin_lock_irqsave(&current->sigmask_lock, flags);
+ oldset = current->blocked;
+ sigfillset(&current->blocked);
+ recalc_sigpending(current);
+ spin_unlock_irqrestore(&current->sigmask_lock, flags);
+
+ req = nlmclnt_alloc_call();
+ if (!req)
+ return -ENOMEM;
+ req->a_host = host;
+ req->a_flags = RPC_TASK_ASYNC;
+
+ nlmclnt_setlockargs(req, fl);
+
+ status = nlmclnt_async_call(req, NLMPROC_CANCEL,
+ nlmclnt_cancel_callback);
+ if (status < 0)
+ kfree(req);
+
+ spin_lock_irqsave(&current->sigmask_lock, flags);
+ current->blocked = oldset;
+ recalc_sigpending(current);
+ spin_unlock_irqrestore(&current->sigmask_lock, flags);
+
+ return status;
+}
+
+static void
+nlmclnt_cancel_callback(struct rpc_task *task)
+{
+ struct nlm_rqst *req = (struct nlm_rqst *) task->tk_calldata;
+
+ if (RPC_ASSASSINATED(task))
+ goto die;
+
+ if (task->tk_status < 0) {
+ dprintk("lockd: CANCEL call error %d, retrying.\n",
+ task->tk_status);
+ goto retry_cancel;
+ }
+
+ dprintk("lockd: cancel status %d (task %d)\n",
+ req->a_res.status, task->tk_pid);
+
+ switch (req->a_res.status) {
+ case NLM_LCK_GRANTED:
+ case NLM_LCK_DENIED_GRACE_PERIOD:
+ /* Everything's good */
+ break;
+ case NLM_LCK_DENIED_NOLOCKS:
+ dprintk("lockd: CANCEL failed (server has no locks)\n");
+ goto retry_cancel;
+ default:
+ printk(KERN_NOTICE "lockd: weird return %d for CANCEL call\n",
+ req->a_res.status);
+ }
+
+die:
+ nlm_release_host(req->a_host);
+ kfree(req);
+ return;
+
+retry_cancel:
+ nlm_rebind_host(req->a_host);
+ rpc_restart_call(task);
+ rpc_delay(task, 30 * HZ);
+}
+
+/*
+ * Convert an NLM status code to a generic kernel errno
+ */
+static int
+nlm_stat_to_errno(u32 status)
+{
+ switch(status) {
+ case NLM_LCK_GRANTED:
+ return 0;
+ case NLM_LCK_DENIED:
+ return -EAGAIN;
+ case NLM_LCK_DENIED_NOLOCKS:
+ case NLM_LCK_DENIED_GRACE_PERIOD:
+ return -ENOLCK;
+ case NLM_LCK_BLOCKED:
+ printk(KERN_NOTICE "lockd: unexpected status NLM_BLOCKED\n");
+ return -ENOLCK;
+#ifdef CONFIG_LOCKD_V4
+ case NLM_DEADLCK:
+ return -EDEADLK;
+ case NLM_ROFS:
+ return -EROFS;
+ case NLM_STALE_FH:
+ return -ESTALE;
+ case NLM_FBIG:
+ return -EOVERFLOW;
+ case NLM_FAILED:
+ return -ENOLCK;
+#endif
+ }
+ printk(KERN_NOTICE "lockd: unexpected server status %d\n", status);
+ return -ENOLCK;
+}
diff --git a/fs/lockd/host.c b/fs/lockd/host.c
new file mode 100644
index 00000000000000..68da5cd4b3f18f
--- /dev/null
+++ b/fs/lockd/host.c
@@ -0,0 +1,343 @@
+/*
+ * linux/fs/lockd/host.c
+ *
+ * Management for NLM peer hosts. The nlm_host struct is shared
+ * between client and server implementation. The only reason to
+ * do so is to reduce code bloat.
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/in.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/lockd/lockd.h>
+#include <linux/lockd/sm_inter.h>
+
+
+#define NLMDBG_FACILITY NLMDBG_HOSTCACHE
+#define NLM_HOST_MAX 64
+#define NLM_HOST_NRHASH 32
+#define NLM_ADDRHASH(addr) (ntohl(addr) & (NLM_HOST_NRHASH-1))
+#define NLM_PTRHASH(ptr) ((((u32)(unsigned long) ptr) / 32) & (NLM_HOST_NRHASH-1))
+#define NLM_HOST_REBIND (60 * HZ)
+#define NLM_HOST_EXPIRE ((nrhosts > NLM_HOST_MAX)? 300 * HZ : 120 * HZ)
+#define NLM_HOST_COLLECT ((nrhosts > NLM_HOST_MAX)? 120 * HZ : 60 * HZ)
+#define NLM_HOST_ADDR(sv) (&(sv)->s_nlmclnt->cl_xprt->addr)
+
+static struct nlm_host * nlm_hosts[NLM_HOST_NRHASH];
+static unsigned long next_gc;
+static int nrhosts;
+static DECLARE_MUTEX(nlm_host_sema);
+
+
+static void nlm_gc_hosts(void);
+
+/*
+ * Find an NLM server handle in the cache. If there is none, create it.
+ */
+struct nlm_host *
+nlmclnt_lookup_host(struct sockaddr_in *sin, int proto, int version)
+{
+ return nlm_lookup_host(NULL, sin, proto, version);
+}
+
+/*
+ * Find an NLM client handle in the cache. If there is none, create it.
+ */
+struct nlm_host *
+nlmsvc_lookup_host(struct svc_rqst *rqstp)
+{
+ return nlm_lookup_host(rqstp->rq_client, &rqstp->rq_addr,
+ rqstp->rq_prot, rqstp->rq_vers);
+}
+
+/*
+ * Match the given host against client/address
+ */
+static inline int
+nlm_match_host(struct nlm_host *host, struct svc_client *clnt,
+ struct sockaddr_in *sin)
+{
+ if (clnt)
+ return host->h_exportent == clnt;
+ return nlm_cmp_addr(&host->h_addr, sin);
+}
+
+/*
+ * Common host lookup routine for server & client
+ */
+struct nlm_host *
+nlm_lookup_host(struct svc_client *clnt, struct sockaddr_in *sin,
+ int proto, int version)
+{
+ struct nlm_host *host, **hp;
+ u32 addr;
+ int hash;
+
+ if (!clnt && !sin) {
+ printk(KERN_NOTICE "lockd: no clnt or addr in lookup_host!\n");
+ return NULL;
+ }
+
+ dprintk("lockd: nlm_lookup_host(%08x, p=%d, v=%d)\n",
+ (unsigned)(sin? ntohl(sin->sin_addr.s_addr) : 0), proto, version);
+
+ if (clnt)
+ hash = NLM_PTRHASH(clnt);
+ else
+ hash = NLM_ADDRHASH(sin->sin_addr.s_addr);
+
+ /* Lock hash table */
+ down(&nlm_host_sema);
+
+ if (time_after_eq(jiffies, next_gc))
+ nlm_gc_hosts();
+
+ for (hp = &nlm_hosts[hash]; (host = *hp); hp = &host->h_next) {
+ if (proto && host->h_proto != proto)
+ continue;
+ if (version && host->h_version != version)
+ continue;
+
+ if (nlm_match_host(host, clnt, sin)) {
+ if (hp != nlm_hosts + hash) {
+ *hp = host->h_next;
+ host->h_next = nlm_hosts[hash];
+ nlm_hosts[hash] = host;
+ }
+ nlm_get_host(host);
+ up(&nlm_host_sema);
+ return host;
+ }
+ }
+
+ /* special hack for nlmsvc_invalidate_client */
+ if (sin == NULL)
+ goto nohost;
+
+ /* Ooops, no host found, create it */
+ dprintk("lockd: creating host entry\n");
+
+ if (!(host = (struct nlm_host *) kmalloc(sizeof(*host), GFP_KERNEL)))
+ goto nohost;
+ memset(host, 0, sizeof(*host));
+
+ addr = sin->sin_addr.s_addr;
+ sprintf(host->h_name, "%d.%d.%d.%d",
+ (unsigned char) (ntohl(addr) >> 24),
+ (unsigned char) (ntohl(addr) >> 16),
+ (unsigned char) (ntohl(addr) >> 8),
+ (unsigned char) (ntohl(addr) >> 0));
+
+ host->h_addr = *sin;
+ host->h_addr.sin_port = 0; /* ouch! */
+ host->h_version = version;
+ host->h_proto = proto;
+ host->h_authflavor = RPC_AUTH_UNIX;
+ host->h_rpcclnt = NULL;
+ init_MUTEX(&host->h_sema);
+ host->h_nextrebind = jiffies + NLM_HOST_REBIND;
+ host->h_expires = jiffies + NLM_HOST_EXPIRE;
+ host->h_count = 1;
+ init_waitqueue_head(&host->h_gracewait);
+ host->h_state = 0; /* pseudo NSM state */
+ host->h_nsmstate = 0; /* real NSM state */
+ host->h_exportent = clnt;
+
+ host->h_next = nlm_hosts[hash];
+ nlm_hosts[hash] = host;
+
+ if (++nrhosts > NLM_HOST_MAX)
+ next_gc = 0;
+
+nohost:
+ up(&nlm_host_sema);
+ return host;
+}
+
+/*
+ * Create the NLM RPC client for an NLM peer
+ */
+struct rpc_clnt *
+nlm_bind_host(struct nlm_host *host)
+{
+ struct rpc_clnt *clnt;
+ struct rpc_xprt *xprt;
+
+ dprintk("lockd: nlm_bind_host(%08x)\n",
+ (unsigned)ntohl(host->h_addr.sin_addr.s_addr));
+
+ /* Lock host handle */
+ down(&host->h_sema);
+
+ /* If we've already created an RPC client, check whether
+ * RPC rebind is required
+ * Note: why keep rebinding if we're on a tcp connection?
+ */
+ if ((clnt = host->h_rpcclnt) != NULL) {
+ xprt = clnt->cl_xprt;
+ if (!xprt->stream && time_after_eq(jiffies, host->h_nextrebind)) {
+ clnt->cl_port = 0;
+ host->h_nextrebind = jiffies + NLM_HOST_REBIND;
+ dprintk("lockd: next rebind in %ld jiffies\n",
+ host->h_nextrebind - jiffies);
+ }
+ } else {
+ xprt = xprt_create_proto(host->h_proto, &host->h_addr, NULL);
+ if (xprt == NULL)
+ goto forgetit;
+
+ xprt_set_timeout(&xprt->timeout, 5, nlmsvc_timeout);
+
+ clnt = rpc_create_client(xprt, host->h_name, &nlm_program,
+ host->h_version, host->h_authflavor);
+ if (clnt == NULL) {
+ xprt_destroy(xprt);
+ goto forgetit;
+ }
+ clnt->cl_autobind = 1; /* turn on pmap queries */
+ xprt->nocong = 1; /* No congestion control for NLM */
+ xprt->resvport = 1; /* NLM requires a reserved port */
+
+ host->h_rpcclnt = clnt;
+ }
+
+ up(&host->h_sema);
+ return clnt;
+
+forgetit:
+ printk("lockd: couldn't create RPC handle for %s\n", host->h_name);
+ up(&host->h_sema);
+ return NULL;
+}
+
+/*
+ * Force a portmap lookup of the remote lockd port
+ */
+void
+nlm_rebind_host(struct nlm_host *host)
+{
+ dprintk("lockd: rebind host %s\n", host->h_name);
+ if (host->h_rpcclnt && time_after_eq(jiffies, host->h_nextrebind)) {
+ host->h_rpcclnt->cl_port = 0;
+ host->h_nextrebind = jiffies + NLM_HOST_REBIND;
+ }
+}
+
+/*
+ * Increment NLM host count
+ */
+struct nlm_host * nlm_get_host(struct nlm_host *host)
+{
+ if (host) {
+ dprintk("lockd: get host %s\n", host->h_name);
+ host->h_count ++;
+ host->h_expires = jiffies + NLM_HOST_EXPIRE;
+ }
+ return host;
+}
+
+/*
+ * Release NLM host after use
+ */
+void nlm_release_host(struct nlm_host *host)
+{
+ if (host && host->h_count) {
+ dprintk("lockd: release host %s\n", host->h_name);
+ host->h_count --;
+ }
+}
+
+/*
+ * Shut down the hosts module.
+ * Note that this routine is called only at server shutdown time.
+ */
+void
+nlm_shutdown_hosts(void)
+{
+ struct nlm_host *host;
+ int i;
+
+ dprintk("lockd: shutting down host module\n");
+ down(&nlm_host_sema);
+
+ /* First, make all hosts eligible for gc */
+ dprintk("lockd: nuking all hosts...\n");
+ for (i = 0; i < NLM_HOST_NRHASH; i++) {
+ for (host = nlm_hosts[i]; host; host = host->h_next)
+ host->h_expires = 0;
+ }
+
+ /* Then, perform a garbage collection pass */
+ nlm_gc_hosts();
+ up(&nlm_host_sema);
+
+ /* complain if any hosts are left */
+ if (nrhosts) {
+ printk(KERN_WARNING "lockd: couldn't shutdown host module!\n");
+ dprintk("lockd: %d hosts left:\n", nrhosts);
+ for (i = 0; i < NLM_HOST_NRHASH; i++) {
+ for (host = nlm_hosts[i]; host; host = host->h_next) {
+ dprintk(" %s (cnt %d use %d exp %ld)\n",
+ host->h_name, host->h_count,
+ host->h_inuse, host->h_expires);
+ }
+ }
+ }
+}
+
+/*
+ * Garbage collect any unused NLM hosts.
+ * This GC combines reference counting for async operations with
+ * mark & sweep for resources held by remote clients.
+ */
+static void
+nlm_gc_hosts(void)
+{
+ struct nlm_host **q, *host;
+ struct rpc_clnt *clnt;
+ int i;
+
+ dprintk("lockd: host garbage collection\n");
+ for (i = 0; i < NLM_HOST_NRHASH; i++) {
+ for (host = nlm_hosts[i]; host; host = host->h_next)
+ host->h_inuse = 0;
+ }
+
+ /* Mark all hosts that hold locks, blocks or shares */
+ nlmsvc_mark_resources();
+
+ for (i = 0; i < NLM_HOST_NRHASH; i++) {
+ q = &nlm_hosts[i];
+ while ((host = *q) != NULL) {
+ if (host->h_count || host->h_inuse
+ || time_before(jiffies, host->h_expires)) {
+ q = &host->h_next;
+ continue;
+ }
+ dprintk("lockd: delete host %s\n", host->h_name);
+ *q = host->h_next;
+ /* Don't unmonitor hosts that have been invalidated */
+ if (host->h_monitored && !host->h_killed)
+ nsm_unmonitor(host);
+ if ((clnt = host->h_rpcclnt) != NULL) {
+ if (atomic_read(&clnt->cl_users)) {
+ printk(KERN_WARNING
+ "lockd: active RPC handle\n");
+ clnt->cl_dead = 1;
+ } else {
+ rpc_destroy_client(host->h_rpcclnt);
+ }
+ }
+ kfree(host);
+ nrhosts--;
+ }
+ }
+
+ next_gc = jiffies + NLM_HOST_COLLECT;
+}
+
diff --git a/fs/lockd/lockd_syms.c b/fs/lockd/lockd_syms.c
new file mode 100644
index 00000000000000..2de24bbfd29cde
--- /dev/null
+++ b/fs/lockd/lockd_syms.c
@@ -0,0 +1,38 @@
+/*
+ * linux/fs/lockd/lockd_syms.c
+ *
+ * Symbols exported by the lockd module.
+ *
+ * Authors: Olaf Kirch (okir@monad.swb.de)
+ *
+ * Copyright (C) 1997 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include <linux/module.h>
+
+#ifdef CONFIG_MODULES
+
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/sched.h>
+#include <linux/uio.h>
+#include <linux/unistd.h>
+
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/lockd/lockd.h>
+
+/* Start/stop the daemon */
+EXPORT_SYMBOL(lockd_up);
+EXPORT_SYMBOL(lockd_down);
+
+/* NFS client entry */
+EXPORT_SYMBOL(nlmclnt_proc);
+
+/* NFS server entry points/hooks */
+EXPORT_SYMBOL(nlmsvc_invalidate_client);
+EXPORT_SYMBOL(nlmsvc_ops);
+
+#endif /* CONFIG_MODULES */
diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c
new file mode 100644
index 00000000000000..b12c311aca7f54
--- /dev/null
+++ b/fs/lockd/mon.c
@@ -0,0 +1,251 @@
+/*
+ * linux/fs/lockd/mon.c
+ *
+ * The kernel statd client.
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/utsname.h>
+#include <linux/kernel.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/lockd/lockd.h>
+#include <linux/lockd/sm_inter.h>
+
+
+#define NLMDBG_FACILITY NLMDBG_MONITOR
+
+static struct rpc_clnt * nsm_create(void);
+
+extern struct rpc_program nsm_program;
+
+/*
+ * Local NSM state
+ */
+u32 nsm_local_state;
+
+/*
+ * Common procedure for SM_MON/SM_UNMON calls
+ */
+static int
+nsm_mon_unmon(struct nlm_host *host, u32 proc, struct nsm_res *res)
+{
+ struct rpc_clnt *clnt;
+ int status;
+ struct nsm_args args;
+
+ status = -EACCES;
+ clnt = nsm_create();
+ if (!clnt)
+ goto out;
+
+ args.addr = host->h_addr.sin_addr.s_addr;
+ args.prog = NLM_PROGRAM;
+ args.vers = host->h_version;
+ args.proc = NLMPROC_NSM_NOTIFY;
+ memset(res, 0, sizeof(*res));
+
+ status = rpc_call(clnt, proc, &args, res, 0);
+ if (status < 0)
+ printk(KERN_DEBUG "nsm_mon_unmon: rpc failed, status=%d\n",
+ status);
+ else
+ status = 0;
+ out:
+ return status;
+}
+
+/*
+ * Set up monitoring of a remote host
+ */
+int
+nsm_monitor(struct nlm_host *host)
+{
+ struct nsm_res res;
+ int status;
+
+ dprintk("lockd: nsm_monitor(%s)\n", host->h_name);
+
+ status = nsm_mon_unmon(host, SM_MON, &res);
+
+ if (status < 0 || res.status != 0)
+ printk(KERN_NOTICE "lockd: cannot monitor %s\n", host->h_name);
+ else
+ host->h_monitored = 1;
+ return status;
+}
+
+/*
+ * Cease to monitor remote host
+ */
+int
+nsm_unmonitor(struct nlm_host *host)
+{
+ struct nsm_res res;
+ int status;
+
+ dprintk("lockd: nsm_unmonitor(%s)\n", host->h_name);
+
+ status = nsm_mon_unmon(host, SM_UNMON, &res);
+ if (status < 0)
+ printk(KERN_NOTICE "lockd: cannot unmonitor %s\n", host->h_name);
+ else
+ host->h_monitored = 0;
+ return status;
+}
+
+/*
+ * Create NSM client for the local host
+ */
+static struct rpc_clnt *
+nsm_create(void)
+{
+ struct rpc_xprt *xprt;
+ struct rpc_clnt *clnt = NULL;
+ struct sockaddr_in sin;
+
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ sin.sin_port = 0;
+
+ xprt = xprt_create_proto(IPPROTO_UDP, &sin, NULL);
+ if (!xprt)
+ goto out;
+
+ clnt = rpc_create_client(xprt, "localhost",
+ &nsm_program, SM_VERSION,
+ RPC_AUTH_NULL);
+ if (!clnt)
+ goto out_destroy;
+ clnt->cl_softrtry = 1;
+ clnt->cl_chatty = 1;
+ clnt->cl_oneshot = 1;
+ xprt->resvport = 1; /* NSM requires a reserved port */
+out:
+ return clnt;
+
+out_destroy:
+ xprt_destroy(xprt);
+ goto out;
+}
+
+/*
+ * XDR functions for NSM.
+ */
+static int
+xdr_error(struct rpc_rqst *rqstp, u32 *p, void *dummy)
+{
+ return -EACCES;
+}
+
+static int
+xdr_encode_mon(struct rpc_rqst *rqstp, u32 *p, struct nsm_args *argp)
+{
+ char buffer[20];
+ u32 addr = ntohl(argp->addr);
+
+ dprintk("nsm: xdr_encode_mon(%08x, %d, %d, %d)\n",
+ htonl(argp->addr), htonl(argp->prog),
+ htonl(argp->vers), htonl(argp->proc));
+
+ /*
+ * Use the dotted-quad IP address of the remote host as
+ * identifier. Linux statd always looks up the canonical
+ * hostname first for whatever remote hostname it receives,
+ * so this works alright.
+ */
+ sprintf(buffer, "%d.%d.%d.%d", (addr>>24) & 0xff, (addr>>16) & 0xff,
+ (addr>>8) & 0xff, (addr) & 0xff);
+ if (!(p = xdr_encode_string(p, buffer))
+ || !(p = xdr_encode_string(p, system_utsname.nodename)))
+ return -EIO;
+ *p++ = htonl(argp->prog);
+ *p++ = htonl(argp->vers);
+ *p++ = htonl(argp->proc);
+
+ /* This is the private part. Needed only for SM_MON call */
+ if (rqstp->rq_task->tk_msg.rpc_proc == SM_MON) {
+ *p++ = argp->addr;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ }
+
+ rqstp->rq_slen = xdr_adjust_iovec(rqstp->rq_svec, p);
+ return 0;
+}
+
+static int
+xdr_decode_stat_res(struct rpc_rqst *rqstp, u32 *p, struct nsm_res *resp)
+{
+ resp->status = ntohl(*p++);
+ resp->state = ntohl(*p++);
+ dprintk("nsm: xdr_decode_stat_res status %d state %d\n",
+ resp->status, resp->state);
+ return 0;
+}
+
+static int
+xdr_decode_stat(struct rpc_rqst *rqstp, u32 *p, struct nsm_res *resp)
+{
+ resp->state = ntohl(*p++);
+ return 0;
+}
+
+#define SM_my_name_sz (1+XDR_QUADLEN(SM_MAXSTRLEN))
+#define SM_my_id_sz (3+1+SM_my_name_sz)
+#define SM_mon_id_sz (1+XDR_QUADLEN(20)+SM_my_id_sz)
+#define SM_mon_sz (SM_mon_id_sz+4)
+#define SM_monres_sz 2
+#define SM_unmonres_sz 1
+
+#ifndef MAX
+# define MAX(a, b) (((a) > (b))? (a) : (b))
+#endif
+
+static struct rpc_procinfo nsm_procedures[] = {
+ { "sm_null",
+ (kxdrproc_t) xdr_error,
+ (kxdrproc_t) xdr_error, 0, 0 },
+ { "sm_stat",
+ (kxdrproc_t) xdr_error,
+ (kxdrproc_t) xdr_error, 0, 0 },
+ { "sm_mon",
+ (kxdrproc_t) xdr_encode_mon,
+ (kxdrproc_t) xdr_decode_stat_res, MAX(SM_mon_sz, SM_monres_sz) << 2, 0 },
+ { "sm_unmon",
+ (kxdrproc_t) xdr_encode_mon,
+ (kxdrproc_t) xdr_decode_stat, MAX(SM_mon_id_sz, SM_unmonres_sz) << 2, 0 },
+ { "sm_unmon_all",
+ (kxdrproc_t) xdr_error,
+ (kxdrproc_t) xdr_error, 0, 0 },
+ { "sm_simu_crash",
+ (kxdrproc_t) xdr_error,
+ (kxdrproc_t) xdr_error, 0, 0 },
+ { "sm_notify",
+ (kxdrproc_t) xdr_error,
+ (kxdrproc_t) xdr_error, 0, 0 },
+};
+
+static struct rpc_version nsm_version1 = {
+ 1,
+ sizeof(nsm_procedures)/sizeof(nsm_procedures[0]),
+ nsm_procedures
+};
+
+static struct rpc_version * nsm_version[] = {
+ NULL,
+ &nsm_version1,
+};
+
+static struct rpc_stat nsm_stats;
+
+struct rpc_program nsm_program = {
+ "statd",
+ SM_PROGRAM,
+ sizeof(nsm_version)/sizeof(nsm_version[0]),
+ nsm_version,
+ &nsm_stats
+};
diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c
new file mode 100644
index 00000000000000..39720c5180c40f
--- /dev/null
+++ b/fs/lockd/svc.c
@@ -0,0 +1,398 @@
+/*
+ * linux/fs/lockd/svc.c
+ *
+ * This is the central lockd service.
+ *
+ * FIXME: Separate the lockd NFS server functionality from the lockd NFS
+ * client functionality. Oh why didn't Sun create two separate
+ * services in the first place?
+ *
+ * Authors: Olaf Kirch (okir@monad.swb.de)
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#define __KERNEL_SYSCALLS__
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/in.h>
+#include <linux/uio.h>
+#include <linux/version.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+
+#include <linux/sunrpc/types.h>
+#include <linux/sunrpc/stats.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/sunrpc/svcsock.h>
+#include <linux/lockd/lockd.h>
+#include <linux/nfs.h>
+
+#define NLMDBG_FACILITY NLMDBG_SVC
+#define LOCKD_BUFSIZE (1024 + NLMSSVC_XDRSIZE)
+#define ALLOWED_SIGS (sigmask(SIGKILL))
+
+extern struct svc_program nlmsvc_program;
+struct nlmsvc_binding * nlmsvc_ops;
+static DECLARE_MUTEX(nlmsvc_sema);
+static unsigned int nlmsvc_users;
+static pid_t nlmsvc_pid;
+int nlmsvc_grace_period;
+unsigned long nlmsvc_timeout;
+
+static DECLARE_MUTEX_LOCKED(lockd_start);
+static DECLARE_WAIT_QUEUE_HEAD(lockd_exit);
+
+/*
+ * Currently the following can be set only at insmod time.
+ * Ideally, they would be accessible through the sysctl interface.
+ */
+unsigned long nlm_grace_period;
+unsigned long nlm_timeout = LOCKD_DFLT_TIMEO;
+unsigned long nlm_udpport, nlm_tcpport;
+
+static unsigned long set_grace_period(void)
+{
+ unsigned long grace_period;
+
+ /* Note: nlm_timeout should always be nonzero */
+ if (nlm_grace_period)
+ grace_period = ((nlm_grace_period + nlm_timeout - 1)
+ / nlm_timeout) * nlm_timeout * HZ;
+ else
+ grace_period = nlm_timeout * 5 * HZ;
+ nlmsvc_grace_period = 1;
+ return grace_period + jiffies;
+}
+
+/*
+ * This is the lockd kernel thread
+ */
+static void
+lockd(struct svc_rqst *rqstp)
+{
+ struct svc_serv *serv = rqstp->rq_server;
+ int err = 0;
+ unsigned long grace_period_expire;
+
+ /* Lock module and set up kernel thread */
+ MOD_INC_USE_COUNT;
+ lock_kernel();
+
+ /*
+ * Let our maker know we're running.
+ */
+ nlmsvc_pid = current->pid;
+ up(&lockd_start);
+
+ daemonize();
+ reparent_to_init();
+ sprintf(current->comm, "lockd");
+
+ /* Process request with signals blocked. */
+ spin_lock_irq(&current->sigmask_lock);
+ siginitsetinv(&current->blocked, sigmask(SIGKILL));
+ recalc_sigpending(current);
+ spin_unlock_irq(&current->sigmask_lock);
+
+ /* kick rpciod */
+ rpciod_up();
+
+ dprintk("NFS locking service started (ver " LOCKD_VERSION ").\n");
+
+ if (!nlm_timeout)
+ nlm_timeout = LOCKD_DFLT_TIMEO;
+ nlmsvc_timeout = nlm_timeout * HZ;
+
+ grace_period_expire = set_grace_period();
+
+ /*
+ * The main request loop. We don't terminate until the last
+ * NFS mount or NFS daemon has gone away, and we've been sent a
+ * signal, or else another process has taken over our job.
+ */
+ while ((nlmsvc_users || !signalled()) && nlmsvc_pid == current->pid)
+ {
+ long timeout = MAX_SCHEDULE_TIMEOUT;
+ if (signalled()) {
+ spin_lock_irq(&current->sigmask_lock);
+ flush_signals(current);
+ spin_unlock_irq(&current->sigmask_lock);
+ if (nlmsvc_ops) {
+ nlmsvc_ops->detach();
+ grace_period_expire = set_grace_period();
+ }
+ }
+
+ /*
+ * Retry any blocked locks that have been notified by
+ * the VFS. Don't do this during grace period.
+ * (Theoretically, there shouldn't even be blocked locks
+ * during grace period).
+ */
+ if (!nlmsvc_grace_period)
+ timeout = nlmsvc_retry_blocked();
+
+ /*
+ * Find a socket with data available and call its
+ * recvfrom routine.
+ */
+ err = svc_recv(serv, rqstp, timeout);
+ if (err == -EAGAIN || err == -EINTR)
+ continue;
+ if (err < 0) {
+ printk(KERN_WARNING
+ "lockd: terminating on error %d\n",
+ -err);
+ break;
+ }
+
+ dprintk("lockd: request from %08x\n",
+ (unsigned)ntohl(rqstp->rq_addr.sin_addr.s_addr));
+
+ /*
+ * Look up the NFS client handle. The handle is needed for
+ * all but the GRANTED callback RPCs.
+ */
+ rqstp->rq_client = NULL;
+ if (nlmsvc_ops) {
+ nlmsvc_ops->exp_readlock();
+ rqstp->rq_client =
+ nlmsvc_ops->exp_getclient(&rqstp->rq_addr);
+ }
+
+ if (nlmsvc_grace_period &&
+ time_before(grace_period_expire, jiffies))
+ nlmsvc_grace_period = 0;
+ svc_process(serv, rqstp);
+
+ /* Unlock export hash tables */
+ if (nlmsvc_ops)
+ nlmsvc_ops->exp_unlock();
+ }
+
+ /*
+ * Check whether there's a new lockd process before
+ * shutting down the hosts and clearing the slot.
+ */
+ if (!nlmsvc_pid || current->pid == nlmsvc_pid) {
+ if (nlmsvc_ops)
+ nlmsvc_ops->detach();
+ nlm_shutdown_hosts();
+ nlmsvc_pid = 0;
+ } else
+ printk(KERN_DEBUG
+ "lockd: new process, skipping host shutdown\n");
+ wake_up(&lockd_exit);
+
+ /* Exit the RPC thread */
+ svc_exit_thread(rqstp);
+
+ /* release rpciod */
+ rpciod_down();
+
+ /* Release module */
+ MOD_DEC_USE_COUNT;
+}
+
+/*
+ * Bring up the lockd process if it's not already up.
+ */
+int
+lockd_up(void)
+{
+ static int warned = 0;
+ struct svc_serv * serv;
+ int error = 0;
+
+ down(&nlmsvc_sema);
+ /*
+ * Unconditionally increment the user count ... this is
+ * the number of clients who _want_ a lockd process.
+ */
+ nlmsvc_users++;
+ /*
+ * Check whether we're already up and running.
+ */
+ if (nlmsvc_pid)
+ goto out;
+
+ /*
+ * Sanity check: if there's no pid,
+ * we should be the first user ...
+ */
+ if (nlmsvc_users > 1)
+ printk(KERN_WARNING
+ "lockd_up: no pid, %d users??\n", nlmsvc_users);
+
+ error = -ENOMEM;
+ serv = svc_create(&nlmsvc_program, 0, NLMSVC_XDRSIZE);
+ if (!serv) {
+ printk(KERN_WARNING "lockd_up: create service failed\n");
+ goto out;
+ }
+
+ if ((error = svc_makesock(serv, IPPROTO_UDP, nlm_udpport)) < 0
+#ifdef CONFIG_NFSD_TCP
+ || (error = svc_makesock(serv, IPPROTO_TCP, nlm_tcpport)) < 0
+#endif
+ ) {
+ if (warned++ == 0)
+ printk(KERN_WARNING
+ "lockd_up: makesock failed, error=%d\n", error);
+ goto destroy_and_out;
+ }
+ warned = 0;
+
+ /*
+ * Create the kernel thread and wait for it to start.
+ */
+ error = svc_create_thread(lockd, serv);
+ if (error) {
+ printk(KERN_WARNING
+ "lockd_up: create thread failed, error=%d\n", error);
+ goto destroy_and_out;
+ }
+ down(&lockd_start);
+
+ /*
+ * Note: svc_serv structures have an initial use count of 1,
+ * so we exit through here on both success and failure.
+ */
+destroy_and_out:
+ svc_destroy(serv);
+out:
+ up(&nlmsvc_sema);
+ return error;
+}
+
+/*
+ * Decrement the user count and bring down lockd if we're the last.
+ */
+void
+lockd_down(void)
+{
+ static int warned = 0;
+
+ down(&nlmsvc_sema);
+ if (nlmsvc_users) {
+ if (--nlmsvc_users)
+ goto out;
+ } else
+ printk(KERN_WARNING "lockd_down: no users! pid=%d\n", nlmsvc_pid);
+
+ if (!nlmsvc_pid) {
+ if (warned++ == 0)
+ printk(KERN_WARNING "lockd_down: no lockd running.\n");
+ goto out;
+ }
+ warned = 0;
+
+ kill_proc(nlmsvc_pid, SIGKILL, 1);
+ /*
+ * Wait for the lockd process to exit, but since we're holding
+ * the lockd semaphore, we can't wait around forever ...
+ */
+ current->sigpending = 0;
+ interruptible_sleep_on_timeout(&lockd_exit, HZ);
+ if (nlmsvc_pid) {
+ printk(KERN_WARNING
+ "lockd_down: lockd failed to exit, clearing pid\n");
+ nlmsvc_pid = 0;
+ }
+ spin_lock_irq(&current->sigmask_lock);
+ recalc_sigpending(current);
+ spin_unlock_irq(&current->sigmask_lock);
+out:
+ up(&nlmsvc_sema);
+}
+
+#ifdef MODULE
+/* New module support in 2.1.18 */
+
+MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
+MODULE_DESCRIPTION("NFS file locking service version " LOCKD_VERSION ".");
+MODULE_LICENSE("GPL");
+MODULE_PARM(nlm_grace_period, "10-240l");
+MODULE_PARM(nlm_timeout, "3-20l");
+MODULE_PARM(nlm_udpport, "0-65535l");
+MODULE_PARM(nlm_tcpport, "0-65535l");
+
+int
+init_module(void)
+{
+ /* Init the static variables */
+ init_MUTEX(&nlmsvc_sema);
+ nlmsvc_users = 0;
+ nlmsvc_pid = 0;
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ /* FIXME: delete all NLM clients */
+ nlm_shutdown_hosts();
+}
+#else
+/* not a module, so process bootargs
+ * lockd.udpport and lockd.tcpport
+ */
+
+static int __init udpport_set(char *str)
+{
+ nlm_udpport = simple_strtoul(str, NULL, 0);
+ return 1;
+}
+static int __init tcpport_set(char *str)
+{
+ nlm_tcpport = simple_strtoul(str, NULL, 0);
+ return 1;
+}
+__setup("lockd.udpport=", udpport_set);
+__setup("lockd.tcpport=", tcpport_set);
+
+#endif
+
+/*
+ * Define NLM program and procedures
+ */
+static struct svc_version nlmsvc_version1 = {
+ 1, 17, nlmsvc_procedures, NULL
+};
+static struct svc_version nlmsvc_version3 = {
+ 3, 24, nlmsvc_procedures, NULL
+};
+#ifdef CONFIG_LOCKD_V4
+static struct svc_version nlmsvc_version4 = {
+ 4, 24, nlmsvc_procedures4, NULL
+};
+#endif
+static struct svc_version * nlmsvc_version[] = {
+ NULL,
+ &nlmsvc_version1,
+ NULL,
+ &nlmsvc_version3,
+#ifdef CONFIG_LOCKD_V4
+ &nlmsvc_version4,
+#endif
+};
+
+static struct svc_stat nlmsvc_stats;
+
+#define NLM_NRVERS (sizeof(nlmsvc_version)/sizeof(nlmsvc_version[0]))
+struct svc_program nlmsvc_program = {
+ NLM_PROGRAM, /* program number */
+ 1, NLM_NRVERS-1, /* version range */
+ NLM_NRVERS, /* number of entries in nlmsvc_version */
+ nlmsvc_version, /* version table */
+ "lockd", /* service name */
+ &nlmsvc_stats, /* stats table */
+};
diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c
new file mode 100644
index 00000000000000..90bad36540d21e
--- /dev/null
+++ b/fs/lockd/svc4proc.c
@@ -0,0 +1,588 @@
+/*
+ * linux/fs/lockd/svc4proc.c
+ *
+ * Lockd server procedures. We don't implement the NLM_*_RES
+ * procedures because we don't use the async procedures.
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/in.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/nfsd/nfsd.h>
+#include <linux/lockd/lockd.h>
+#include <linux/lockd/share.h>
+#include <linux/lockd/sm_inter.h>
+
+
+#define NLMDBG_FACILITY NLMDBG_CLIENT
+
+static u32 nlm4svc_callback(struct svc_rqst *, u32, struct nlm_res *);
+static void nlm4svc_callback_exit(struct rpc_task *);
+
+/*
+ * Obtain client and file from arguments
+ */
+static u32
+nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
+ struct nlm_host **hostp, struct nlm_file **filp)
+{
+ struct nlm_host *host = NULL;
+ struct nlm_file *file = NULL;
+ struct nlm_lock *lock = &argp->lock;
+ u32 error = 0;
+
+ /* nfsd callbacks must have been installed for this procedure */
+ if (!nlmsvc_ops)
+ return nlm_lck_denied_nolocks;
+
+ /* Obtain handle for client host */
+ if (rqstp->rq_client == NULL) {
+ printk(KERN_NOTICE
+ "lockd: unauthenticated request from (%08x:%d)\n",
+ ntohl(rqstp->rq_addr.sin_addr.s_addr),
+ ntohs(rqstp->rq_addr.sin_port));
+ return nlm_lck_denied_nolocks;
+ }
+
+ /* Obtain host handle */
+ if (!(host = nlmsvc_lookup_host(rqstp))
+ || (argp->monitor && !host->h_monitored && nsm_monitor(host) < 0))
+ goto no_locks;
+ *hostp = host;
+
+ /* Obtain file pointer. Not used by FREE_ALL call. */
+ if (filp != NULL) {
+ if ((error = nlm_lookup_file(rqstp, &file, &lock->fh)) != 0)
+ goto no_locks;
+ *filp = file;
+
+ /* Set up the missing parts of the file_lock structure */
+ lock->fl.fl_file = &file->f_file;
+ lock->fl.fl_owner = (fl_owner_t) host;
+ }
+
+ return 0;
+
+no_locks:
+ if (host)
+ nlm_release_host(host);
+ if (error)
+ return error;
+ return nlm_lck_denied_nolocks;
+}
+
+/*
+ * NULL: Test for presence of service
+ */
+static int
+nlm4svc_proc_null(struct svc_rqst *rqstp, void *argp, void *resp)
+{
+ dprintk("lockd: NULL called\n");
+ return rpc_success;
+}
+
+/*
+ * TEST: Check for conflicting lock
+ */
+static int
+nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp,
+ struct nlm_res *resp)
+{
+ struct nlm_host *host;
+ struct nlm_file *file;
+
+ dprintk("lockd: TEST4 called\n");
+ resp->cookie = argp->cookie;
+
+ /* Don't accept test requests during grace period */
+ if (nlmsvc_grace_period) {
+ resp->status = nlm_lck_denied_grace_period;
+ return rpc_success;
+ }
+
+ /* Obtain client and file */
+ if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file)))
+ return rpc_success;
+
+ /* Now check for conflicting locks */
+ resp->status = nlmsvc_testlock(file, &argp->lock, &resp->lock);
+
+ dprintk("lockd: TEST4 status %d\n", ntohl(resp->status));
+ nlm_release_host(host);
+ nlm_release_file(file);
+ return rpc_success;
+}
+
+static int
+nlm4svc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp,
+ struct nlm_res *resp)
+{
+ struct nlm_host *host;
+ struct nlm_file *file;
+
+ dprintk("lockd: LOCK called\n");
+
+ resp->cookie = argp->cookie;
+
+ /* Don't accept new lock requests during grace period */
+ if (nlmsvc_grace_period && !argp->reclaim) {
+ resp->status = nlm_lck_denied_grace_period;
+ return rpc_success;
+ }
+
+ /* Obtain client and file */
+ if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file)))
+ return rpc_success;
+
+#if 0
+ /* If supplied state doesn't match current state, we assume it's
+ * an old request that time-warped somehow. Any error return would
+ * do in this case because it's irrelevant anyway.
+ *
+ * NB: We don't retrieve the remote host's state yet.
+ */
+ if (host->h_nsmstate && host->h_nsmstate != argp->state) {
+ resp->status = nlm_lck_denied_nolocks;
+ } else
+#endif
+
+ /* Now try to lock the file */
+ resp->status = nlmsvc_lock(rqstp, file, &argp->lock,
+ argp->block, &argp->cookie);
+
+ dprintk("lockd: LOCK status %d\n", ntohl(resp->status));
+ nlm_release_host(host);
+ nlm_release_file(file);
+ return rpc_success;
+}
+
+static int
+nlm4svc_proc_cancel(struct svc_rqst *rqstp, struct nlm_args *argp,
+ struct nlm_res *resp)
+{
+ struct nlm_host *host;
+ struct nlm_file *file;
+
+ dprintk("lockd: CANCEL called\n");
+
+ resp->cookie = argp->cookie;
+
+ /* Don't accept requests during grace period */
+ if (nlmsvc_grace_period) {
+ resp->status = nlm_lck_denied_grace_period;
+ return rpc_success;
+ }
+
+ /* Obtain client and file */
+ if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file)))
+ return rpc_success;
+
+ /* Try to cancel request. */
+ resp->status = nlmsvc_cancel_blocked(file, &argp->lock);
+
+ dprintk("lockd: CANCEL status %d\n", ntohl(resp->status));
+ nlm_release_host(host);
+ nlm_release_file(file);
+ return rpc_success;
+}
+
+/*
+ * UNLOCK: release a lock
+ */
+static int
+nlm4svc_proc_unlock(struct svc_rqst *rqstp, struct nlm_args *argp,
+ struct nlm_res *resp)
+{
+ struct nlm_host *host;
+ struct nlm_file *file;
+
+ dprintk("lockd: UNLOCK called\n");
+
+ resp->cookie = argp->cookie;
+
+ /* Don't accept new lock requests during grace period */
+ if (nlmsvc_grace_period) {
+ resp->status = nlm_lck_denied_grace_period;
+ return rpc_success;
+ }
+
+ /* Obtain client and file */
+ if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file)))
+ return rpc_success;
+
+ /* Now try to remove the lock */
+ resp->status = nlmsvc_unlock(file, &argp->lock);
+
+ dprintk("lockd: UNLOCK status %d\n", ntohl(resp->status));
+ nlm_release_host(host);
+ nlm_release_file(file);
+ return rpc_success;
+}
+
+/*
+ * GRANTED: A server calls us to tell that a process' lock request
+ * was granted
+ */
+static int
+nlm4svc_proc_granted(struct svc_rqst *rqstp, struct nlm_args *argp,
+ struct nlm_res *resp)
+{
+ resp->cookie = argp->cookie;
+
+ dprintk("lockd: GRANTED called\n");
+ resp->status = nlmclnt_grant(&argp->lock);
+ dprintk("lockd: GRANTED status %d\n", ntohl(resp->status));
+ return rpc_success;
+}
+
+/*
+ * `Async' versions of the above service routines. They aren't really,
+ * because we send the callback before the reply proper. I hope this
+ * doesn't break any clients.
+ */
+static int
+nlm4svc_proc_test_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
+ void *resp)
+{
+ struct nlm_res res;
+ u32 stat;
+
+ dprintk("lockd: TEST_MSG called\n");
+
+ memset(&res, 0, sizeof(res));
+
+ if ((stat = nlm4svc_proc_test(rqstp, argp, &res)) == 0)
+ stat = nlm4svc_callback(rqstp, NLMPROC_TEST_RES, &res);
+ return stat;
+}
+
+static int
+nlm4svc_proc_lock_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
+ void *resp)
+{
+ struct nlm_res res;
+ u32 stat;
+
+ dprintk("lockd: LOCK_MSG called\n");
+
+ if ((stat = nlm4svc_proc_lock(rqstp, argp, &res)) == 0)
+ stat = nlm4svc_callback(rqstp, NLMPROC_LOCK_RES, &res);
+ return stat;
+}
+
+static int
+nlm4svc_proc_cancel_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
+ void *resp)
+{
+ struct nlm_res res;
+ u32 stat;
+
+ dprintk("lockd: CANCEL_MSG called\n");
+
+ if ((stat = nlm4svc_proc_cancel(rqstp, argp, &res)) == 0)
+ stat = nlm4svc_callback(rqstp, NLMPROC_CANCEL_RES, &res);
+ return stat;
+}
+
+static int
+nlm4svc_proc_unlock_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
+ void *resp)
+{
+ struct nlm_res res;
+ u32 stat;
+
+ dprintk("lockd: UNLOCK_MSG called\n");
+
+ if ((stat = nlm4svc_proc_unlock(rqstp, argp, &res)) == 0)
+ stat = nlm4svc_callback(rqstp, NLMPROC_UNLOCK_RES, &res);
+ return stat;
+}
+
+static int
+nlm4svc_proc_granted_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
+ void *resp)
+{
+ struct nlm_res res;
+ u32 stat;
+
+ dprintk("lockd: GRANTED_MSG called\n");
+
+ if ((stat = nlm4svc_proc_granted(rqstp, argp, &res)) == 0)
+ stat = nlm4svc_callback(rqstp, NLMPROC_GRANTED_RES, &res);
+ return stat;
+}
+
+/*
+ * SHARE: create a DOS share or alter existing share.
+ */
+static int
+nlm4svc_proc_share(struct svc_rqst *rqstp, struct nlm_args *argp,
+ struct nlm_res *resp)
+{
+ struct nlm_host *host;
+ struct nlm_file *file;
+
+ dprintk("lockd: SHARE called\n");
+
+ resp->cookie = argp->cookie;
+
+ /* Don't accept new lock requests during grace period */
+ if (nlmsvc_grace_period && !argp->reclaim) {
+ resp->status = nlm_lck_denied_grace_period;
+ return rpc_success;
+ }
+
+ /* Obtain client and file */
+ if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file)))
+ return rpc_success;
+
+ /* Now try to create the share */
+ resp->status = nlmsvc_share_file(host, file, argp);
+
+ dprintk("lockd: SHARE status %d\n", ntohl(resp->status));
+ nlm_release_host(host);
+ nlm_release_file(file);
+ return rpc_success;
+}
+
+/*
+ * UNSHARE: Release a DOS share.
+ */
+static int
+nlm4svc_proc_unshare(struct svc_rqst *rqstp, struct nlm_args *argp,
+ struct nlm_res *resp)
+{
+ struct nlm_host *host;
+ struct nlm_file *file;
+
+ dprintk("lockd: UNSHARE called\n");
+
+ resp->cookie = argp->cookie;
+
+ /* Don't accept requests during grace period */
+ if (nlmsvc_grace_period) {
+ resp->status = nlm_lck_denied_grace_period;
+ return rpc_success;
+ }
+
+ /* Obtain client and file */
+ if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file)))
+ return rpc_success;
+
+ /* Now try to lock the file */
+ resp->status = nlmsvc_unshare_file(host, file, argp);
+
+ dprintk("lockd: UNSHARE status %d\n", ntohl(resp->status));
+ nlm_release_host(host);
+ nlm_release_file(file);
+ return rpc_success;
+}
+
+/*
+ * NM_LOCK: Create an unmonitored lock
+ */
+static int
+nlm4svc_proc_nm_lock(struct svc_rqst *rqstp, struct nlm_args *argp,
+ struct nlm_res *resp)
+{
+ dprintk("lockd: NM_LOCK called\n");
+
+ argp->monitor = 0; /* just clean the monitor flag */
+ return nlm4svc_proc_lock(rqstp, argp, resp);
+}
+
+/*
+ * FREE_ALL: Release all locks and shares held by client
+ */
+static int
+nlm4svc_proc_free_all(struct svc_rqst *rqstp, struct nlm_args *argp,
+ void *resp)
+{
+ struct nlm_host *host;
+
+ /* Obtain client */
+ if (nlm4svc_retrieve_args(rqstp, argp, &host, NULL))
+ return rpc_success;
+
+ nlmsvc_free_host_resources(host);
+ nlm_release_host(host);
+ return rpc_success;
+}
+
+/*
+ * SM_NOTIFY: private callback from statd (not part of official NLM proto)
+ */
+static int
+nlm4svc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp,
+ void *resp)
+{
+ struct sockaddr_in saddr = rqstp->rq_addr;
+ int vers = rqstp->rq_vers;
+ int prot = rqstp->rq_prot;
+ struct nlm_host *host;
+
+ dprintk("lockd: SM_NOTIFY called\n");
+ if (saddr.sin_addr.s_addr != htonl(INADDR_LOOPBACK)
+ || ntohs(saddr.sin_port) >= 1024) {
+ printk(KERN_WARNING
+ "lockd: rejected NSM callback from %08x:%d\n",
+ ntohl(rqstp->rq_addr.sin_addr.s_addr),
+ ntohs(rqstp->rq_addr.sin_port));
+ return rpc_system_err;
+ }
+
+ /* Obtain the host pointer for this NFS server and try to
+ * reclaim all locks we hold on this server.
+ */
+ saddr.sin_addr.s_addr = argp->addr;
+ if ((host = nlmclnt_lookup_host(&saddr, prot, vers)) != NULL) {
+ nlmclnt_recovery(host, argp->state);
+ nlm_release_host(host);
+ }
+
+ /* If we run on an NFS server, delete all locks held by the client */
+ if (nlmsvc_ops != NULL) {
+ struct svc_client *clnt;
+ saddr.sin_addr.s_addr = argp->addr;
+ if ((clnt = nlmsvc_ops->exp_getclient(&saddr)) != NULL
+ && (host = nlm_lookup_host(clnt, &saddr, 0, 0)) != NULL) {
+ nlmsvc_free_host_resources(host);
+ }
+ nlm_release_host(host);
+ }
+
+ return rpc_success;
+}
+
+/*
+ * client sent a GRANTED_RES, let's remove the associated block
+ */
+static int
+nlm4svc_proc_granted_res(struct svc_rqst *rqstp, struct nlm_res *argp,
+ void *resp)
+{
+ if (!nlmsvc_ops)
+ return rpc_success;
+
+ dprintk("lockd: GRANTED_RES called\n");
+
+ nlmsvc_grant_reply(rqstp, &argp->cookie, argp->status);
+ return rpc_success;
+}
+
+
+
+/*
+ * This is the generic lockd callback for async RPC calls
+ */
+static u32
+nlm4svc_callback(struct svc_rqst *rqstp, u32 proc, struct nlm_res *resp)
+{
+ struct nlm_host *host;
+ struct nlm_rqst *call;
+
+ if (!(call = nlmclnt_alloc_call()))
+ return rpc_system_err;
+
+ host = nlmclnt_lookup_host(&rqstp->rq_addr,
+ rqstp->rq_prot, rqstp->rq_vers);
+ if (!host) {
+ kfree(call);
+ return rpc_system_err;
+ }
+
+ call->a_flags = RPC_TASK_ASYNC;
+ call->a_host = host;
+ memcpy(&call->a_args, resp, sizeof(*resp));
+
+ if (nlmsvc_async_call(call, proc, nlm4svc_callback_exit) < 0)
+ goto error;
+
+ return rpc_success;
+ error:
+ kfree(call);
+ nlm_release_host(host);
+ return rpc_system_err;
+}
+
+static void
+nlm4svc_callback_exit(struct rpc_task *task)
+{
+ struct nlm_rqst *call = (struct nlm_rqst *) task->tk_calldata;
+
+ if (task->tk_status < 0) {
+ dprintk("lockd: %4d callback failed (errno = %d)\n",
+ task->tk_pid, -task->tk_status);
+ }
+ nlm_release_host(call->a_host);
+ kfree(call);
+}
+
+/*
+ * NLM Server procedures.
+ */
+
+#define nlm4svc_encode_norep nlm4svc_encode_void
+#define nlm4svc_decode_norep nlm4svc_decode_void
+#define nlm4svc_decode_testres nlm4svc_decode_void
+#define nlm4svc_decode_lockres nlm4svc_decode_void
+#define nlm4svc_decode_unlockres nlm4svc_decode_void
+#define nlm4svc_decode_cancelres nlm4svc_decode_void
+#define nlm4svc_decode_grantedres nlm4svc_decode_void
+
+#define nlm4svc_proc_none nlm4svc_proc_null
+#define nlm4svc_proc_test_res nlm4svc_proc_null
+#define nlm4svc_proc_lock_res nlm4svc_proc_null
+#define nlm4svc_proc_cancel_res nlm4svc_proc_null
+#define nlm4svc_proc_unlock_res nlm4svc_proc_null
+
+struct nlm_void { int dummy; };
+
+#define PROC(name, xargt, xrest, argt, rest, respsize) \
+ { (svc_procfunc) nlm4svc_proc_##name, \
+ (kxdrproc_t) nlm4svc_decode_##xargt, \
+ (kxdrproc_t) nlm4svc_encode_##xrest, \
+ NULL, \
+ sizeof(struct nlm_##argt), \
+ sizeof(struct nlm_##rest), \
+ 0, \
+ 0, \
+ respsize, \
+ }
+#define Ck (1+8) /* cookie */
+#define No (1+1024/4) /* netobj */
+#define St 1 /* status */
+#define Rg 4 /* range (offset + length) */
+struct svc_procedure nlmsvc_procedures4[] = {
+ PROC(null, void, void, void, void, 1),
+ PROC(test, testargs, testres, args, res, Ck+St+2+No+Rg),
+ PROC(lock, lockargs, res, args, res, Ck+St),
+ PROC(cancel, cancargs, res, args, res, Ck+St),
+ PROC(unlock, unlockargs, res, args, res, Ck+St),
+ PROC(granted, testargs, res, args, res, Ck+St),
+ PROC(test_msg, testargs, norep, args, void, 1),
+ PROC(lock_msg, lockargs, norep, args, void, 1),
+ PROC(cancel_msg, cancargs, norep, args, void, 1),
+ PROC(unlock_msg, unlockargs, norep, args, void, 1),
+ PROC(granted_msg, testargs, norep, args, void, 1),
+ PROC(test_res, testres, norep, res, void, 1),
+ PROC(lock_res, lockres, norep, res, void, 1),
+ PROC(cancel_res, cancelres, norep, res, void, 1),
+ PROC(unlock_res, unlockres, norep, res, void, 1),
+ PROC(granted_res, res, norep, res, void, 1),
+ /* statd callback */
+ PROC(sm_notify, reboot, void, reboot, void, 1),
+ PROC(none, void, void, void, void, 0),
+ PROC(none, void, void, void, void, 0),
+ PROC(none, void, void, void, void, 0),
+ PROC(share, shareargs, shareres, args, res, Ck+St+1),
+ PROC(unshare, shareargs, shareres, args, res, Ck+St+1),
+ PROC(nm_lock, lockargs, res, args, res, Ck+St),
+ PROC(free_all, notify, void, args, void, 1),
+
+};
diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c
new file mode 100644
index 00000000000000..310a5245a0c11d
--- /dev/null
+++ b/fs/lockd/svclock.c
@@ -0,0 +1,685 @@
+/*
+ * linux/fs/lockd/svclock.c
+ *
+ * Handling of server-side locks, mostly of the blocked variety.
+ * This is the ugliest part of lockd because we tread on very thin ice.
+ * GRANT and CANCEL calls may get stuck, meet in mid-flight, etc.
+ * IMNSHO introducing the grant callback into the NLM protocol was one
+ * of the worst ideas Sun ever had. Except maybe for the idea of doing
+ * NFS file locking at all.
+ *
+ * I'm trying hard to avoid race conditions by protecting most accesses
+ * to a file's list of blocked locks through a semaphore. The global
+ * list of blocked locks is not protected in this fashion however.
+ * Therefore, some functions (such as the RPC callback for the async grant
+ * call) move blocked locks towards the head of the list *while some other
+ * process might be traversing it*. This should not be a problem in
+ * practice, because this will only cause functions traversing the list
+ * to visit some blocks twice.
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/lockd/nlm.h>
+#include <linux/lockd/lockd.h>
+
+#define NLMDBG_FACILITY NLMDBG_SVCLOCK
+
+#ifdef CONFIG_LOCKD_V4
+#define nlm_deadlock nlm4_deadlock
+#else
+#define nlm_deadlock nlm_lck_denied
+#endif
+
+static void nlmsvc_insert_block(struct nlm_block *block, unsigned long);
+static int nlmsvc_remove_block(struct nlm_block *block);
+static void nlmsvc_grant_callback(struct rpc_task *task);
+static void nlmsvc_notify_blocked(struct file_lock *);
+
+/*
+ * The list of blocked locks to retry
+ */
+static struct nlm_block * nlm_blocked;
+
+/*
+ * Insert a blocked lock into the global list
+ */
+static void
+nlmsvc_insert_block(struct nlm_block *block, unsigned long when)
+{
+ struct nlm_block **bp, *b;
+
+ dprintk("lockd: nlmsvc_insert_block(%p, %ld)\n", block, when);
+ if (block->b_queued)
+ nlmsvc_remove_block(block);
+ bp = &nlm_blocked;
+ if (when != NLM_NEVER) {
+ if ((when += jiffies) == NLM_NEVER)
+ when ++;
+ while ((b = *bp) && time_before_eq(b->b_when,when) && b->b_when != NLM_NEVER)
+ bp = &b->b_next;
+ } else
+ while ((b = *bp))
+ bp = &b->b_next;
+
+ block->b_queued = 1;
+ block->b_when = when;
+ block->b_next = b;
+ *bp = block;
+}
+
+/*
+ * Remove a block from the global list
+ */
+static int
+nlmsvc_remove_block(struct nlm_block *block)
+{
+ struct nlm_block **bp, *b;
+
+ if (!block->b_queued)
+ return 1;
+ for (bp = &nlm_blocked; (b = *bp); bp = &b->b_next) {
+ if (b == block) {
+ *bp = block->b_next;
+ block->b_queued = 0;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Find a block for a given lock and optionally remove it from
+ * the list.
+ */
+static struct nlm_block *
+nlmsvc_lookup_block(struct nlm_file *file, struct nlm_lock *lock, int remove)
+{
+ struct nlm_block **head, *block;
+ struct file_lock *fl;
+
+ dprintk("lockd: nlmsvc_lookup_block f=%p pd=%d %Ld-%Ld ty=%d\n",
+ file, lock->fl.fl_pid,
+ (long long)lock->fl.fl_start,
+ (long long)lock->fl.fl_end, lock->fl.fl_type);
+ for (head = &nlm_blocked; (block = *head); head = &block->b_next) {
+ fl = &block->b_call.a_args.lock.fl;
+ dprintk("lockd: check f=%p pd=%d %Ld-%Ld ty=%d cookie=%s\n",
+ block->b_file, fl->fl_pid,
+ (long long)fl->fl_start,
+ (long long)fl->fl_end, fl->fl_type,
+ nlmdbg_cookie2a(&block->b_call.a_args.cookie));
+ if (block->b_file == file && nlm_compare_locks(fl, &lock->fl)) {
+ if (remove) {
+ *head = block->b_next;
+ block->b_queued = 0;
+ }
+ return block;
+ }
+ }
+
+ return NULL;
+}
+
+static inline int nlm_cookie_match(struct nlm_cookie *a, struct nlm_cookie *b)
+{
+ if(a->len != b->len)
+ return 0;
+ if(memcmp(a->data,b->data,a->len))
+ return 0;
+ return 1;
+}
+
+/*
+ * Find a block with a given NLM cookie.
+ */
+static inline struct nlm_block *
+nlmsvc_find_block(struct nlm_cookie *cookie, struct sockaddr_in *sin)
+{
+ struct nlm_block *block;
+
+ for (block = nlm_blocked; block; block = block->b_next) {
+ dprintk("cookie: head of blocked queue %p, block %p\n",
+ nlm_blocked, block);
+ if (nlm_cookie_match(&block->b_call.a_args.cookie,cookie)
+ && nlm_cmp_addr(sin, &block->b_host->h_addr))
+ break;
+ }
+
+ return block;
+}
+
+/*
+ * Create a block and initialize it.
+ *
+ * Note: we explicitly set the cookie of the grant reply to that of
+ * the blocked lock request. The spec explicitly mentions that the client
+ * should _not_ rely on the callback containing the same cookie as the
+ * request, but (as I found out later) that's because some implementations
+ * do just this. Never mind the standards comittees, they support our
+ * logging industries.
+ */
+static inline struct nlm_block *
+nlmsvc_create_block(struct svc_rqst *rqstp, struct nlm_file *file,
+ struct nlm_lock *lock, struct nlm_cookie *cookie)
+{
+ struct nlm_block *block;
+ struct nlm_host *host;
+ struct nlm_rqst *call;
+
+ /* Create host handle for callback */
+ host = nlmclnt_lookup_host(&rqstp->rq_addr,
+ rqstp->rq_prot, rqstp->rq_vers);
+ if (host == NULL)
+ return NULL;
+
+ /* Allocate memory for block, and initialize arguments */
+ if (!(block = (struct nlm_block *) kmalloc(sizeof(*block), GFP_KERNEL)))
+ goto failed;
+ memset(block, 0, sizeof(*block));
+ locks_init_lock(&block->b_call.a_args.lock.fl);
+ locks_init_lock(&block->b_call.a_res.lock.fl);
+
+ block->b_host = nlmsvc_lookup_host(rqstp);
+ if (block->b_host == NULL) {
+ goto failed_free;
+ }
+
+ if (!nlmclnt_setgrantargs(&block->b_call, lock))
+ goto failed_free;
+
+ /* Set notifier function for VFS, and init args */
+ block->b_call.a_args.lock.fl.fl_notify = nlmsvc_notify_blocked;
+ block->b_call.a_args.cookie = *cookie; /* see above */
+
+ dprintk("lockd: created block %p...\n", block);
+
+ /* Create and initialize the block */
+ block->b_daemon = rqstp->rq_server;
+ block->b_file = file;
+
+ /* Add to file's list of blocks */
+ block->b_fnext = file->f_blocks;
+ file->f_blocks = block;
+
+ /* Set up RPC arguments for callback */
+ call = &block->b_call;
+ call->a_host = host;
+ call->a_flags = RPC_TASK_ASYNC;
+
+ return block;
+
+failed_free:
+ kfree(block);
+failed:
+ nlm_release_host(host);
+ return NULL;
+}
+
+/*
+ * Delete a block. If the lock was cancelled or the grant callback
+ * failed, unlock is set to 1.
+ * It is the caller's responsibility to check whether the file
+ * can be closed hereafter.
+ */
+static void
+nlmsvc_delete_block(struct nlm_block *block, int unlock)
+{
+ struct file_lock *fl = &block->b_call.a_args.lock.fl;
+ struct nlm_file *file = block->b_file;
+ struct nlm_block **bp;
+
+ dprintk("lockd: deleting block %p...\n", block);
+
+ /* Remove block from list */
+ nlmsvc_remove_block(block);
+
+ /* If granted, unlock it, else remove from inode block list */
+ if (unlock && block->b_granted) {
+ dprintk("lockd: deleting granted lock\n");
+ fl->fl_type = F_UNLCK;
+ posix_lock_file(&block->b_file->f_file, fl, 0);
+ block->b_granted = 0;
+ } else {
+ dprintk("lockd: unblocking blocked lock\n");
+ posix_unblock_lock(fl);
+ }
+
+ /* If the block is in the middle of a GRANT callback,
+ * don't kill it yet. */
+ if (block->b_incall) {
+ nlmsvc_insert_block(block, NLM_NEVER);
+ block->b_done = 1;
+ return;
+ }
+
+ /* Remove block from file's list of blocks */
+ for (bp = &file->f_blocks; *bp; bp = &(*bp)->b_fnext) {
+ if (*bp == block) {
+ *bp = block->b_fnext;
+ break;
+ }
+ }
+
+ nlm_release_host(block->b_host);
+ nlmclnt_freegrantargs(&block->b_call);
+ kfree(block);
+}
+
+/*
+ * Loop over all blocks and perform the action specified.
+ * (NLM_ACT_CHECK handled by nlmsvc_inspect_file).
+ */
+int
+nlmsvc_traverse_blocks(struct nlm_host *host, struct nlm_file *file, int action)
+{
+ struct nlm_block *block, *next;
+
+ down(&file->f_sema);
+ for (block = file->f_blocks; block; block = next) {
+ next = block->b_fnext;
+ if (action == NLM_ACT_MARK)
+ block->b_host->h_inuse = 1;
+ else if (action == NLM_ACT_UNLOCK) {
+ if (host == NULL || host == block->b_host)
+ nlmsvc_delete_block(block, 1);
+ }
+ }
+ up(&file->f_sema);
+ return 0;
+}
+
+/*
+ * Attempt to establish a lock, and if it can't be granted, block it
+ * if required.
+ */
+u32
+nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
+ struct nlm_lock *lock, int wait, struct nlm_cookie *cookie)
+{
+ struct file_lock *conflock;
+ struct nlm_block *block;
+ int error;
+
+ dprintk("lockd: nlmsvc_lock(%04x/%ld, ty=%d, pi=%d, %Ld-%Ld, bl=%d)\n",
+ file->f_file.f_dentry->d_inode->i_dev,
+ file->f_file.f_dentry->d_inode->i_ino,
+ lock->fl.fl_type, lock->fl.fl_pid,
+ (long long)lock->fl.fl_start,
+ (long long)lock->fl.fl_end,
+ wait);
+
+
+ /* Get existing block (in case client is busy-waiting) */
+ block = nlmsvc_lookup_block(file, lock, 0);
+
+ lock->fl.fl_flags |= FL_LOCKD;
+
+again:
+ /* Lock file against concurrent access */
+ down(&file->f_sema);
+
+ if (!(conflock = posix_test_lock(&file->f_file, &lock->fl))) {
+ error = posix_lock_file(&file->f_file, &lock->fl, 0);
+
+ if (block)
+ nlmsvc_delete_block(block, 0);
+ up(&file->f_sema);
+
+ dprintk("lockd: posix_lock_file returned %d\n", -error);
+ switch(-error) {
+ case 0:
+ return nlm_granted;
+ case EDEADLK:
+ return nlm_deadlock;
+ case EAGAIN:
+ return nlm_lck_denied;
+ default: /* includes ENOLCK */
+ return nlm_lck_denied_nolocks;
+ }
+ }
+
+ if (!wait) {
+ up(&file->f_sema);
+ return nlm_lck_denied;
+ }
+
+ if (posix_locks_deadlock(&lock->fl, conflock)) {
+ up(&file->f_sema);
+ return nlm_deadlock;
+ }
+
+ /* If we don't have a block, create and initialize it. Then
+ * retry because we may have slept in kmalloc. */
+ /* We have to release f_sema as nlmsvc_create_block may try to
+ * claim it while doing host garbage collection */
+ if (block == NULL) {
+ up(&file->f_sema);
+ dprintk("lockd: blocking on this lock (allocating).\n");
+ if (!(block = nlmsvc_create_block(rqstp, file, lock, cookie)))
+ return nlm_lck_denied_nolocks;
+ goto again;
+ }
+
+ /* Append to list of blocked */
+ nlmsvc_insert_block(block, NLM_NEVER);
+
+ if (list_empty(&block->b_call.a_args.lock.fl.fl_block)) {
+ /* Now add block to block list of the conflicting lock
+ if we haven't done so. */
+ dprintk("lockd: blocking on this lock.\n");
+ posix_block_lock(conflock, &block->b_call.a_args.lock.fl);
+ }
+
+ up(&file->f_sema);
+ return nlm_lck_blocked;
+}
+
+/*
+ * Test for presence of a conflicting lock.
+ */
+u32
+nlmsvc_testlock(struct nlm_file *file, struct nlm_lock *lock,
+ struct nlm_lock *conflock)
+{
+ struct file_lock *fl;
+
+ dprintk("lockd: nlmsvc_testlock(%04x/%ld, ty=%d, %Ld-%Ld)\n",
+ file->f_file.f_dentry->d_inode->i_dev,
+ file->f_file.f_dentry->d_inode->i_ino,
+ lock->fl.fl_type,
+ (long long)lock->fl.fl_start,
+ (long long)lock->fl.fl_end);
+
+ if ((fl = posix_test_lock(&file->f_file, &lock->fl)) != NULL) {
+ dprintk("lockd: conflicting lock(ty=%d, %Ld-%Ld)\n",
+ fl->fl_type, (long long)fl->fl_start,
+ (long long)fl->fl_end);
+ conflock->caller = "somehost"; /* FIXME */
+ conflock->oh.len = 0; /* don't return OH info */
+ conflock->fl = *fl;
+ return nlm_lck_denied;
+ }
+
+ return nlm_granted;
+}
+
+/*
+ * Remove a lock.
+ * This implies a CANCEL call: We send a GRANT_MSG, the client replies
+ * with a GRANT_RES call which gets lost, and calls UNLOCK immediately
+ * afterwards. In this case the block will still be there, and hence
+ * must be removed.
+ */
+u32
+nlmsvc_unlock(struct nlm_file *file, struct nlm_lock *lock)
+{
+ int error;
+
+ dprintk("lockd: nlmsvc_unlock(%04x/%ld, pi=%d, %Ld-%Ld)\n",
+ file->f_file.f_dentry->d_inode->i_dev,
+ file->f_file.f_dentry->d_inode->i_ino,
+ lock->fl.fl_pid,
+ (long long)lock->fl.fl_start,
+ (long long)lock->fl.fl_end);
+
+ /* First, cancel any lock that might be there */
+ nlmsvc_cancel_blocked(file, lock);
+
+ lock->fl.fl_type = F_UNLCK;
+ error = posix_lock_file(&file->f_file, &lock->fl, 0);
+
+ return (error < 0)? nlm_lck_denied_nolocks : nlm_granted;
+}
+
+/*
+ * Cancel a previously blocked request.
+ *
+ * A cancel request always overrides any grant that may currently
+ * be in progress.
+ * The calling procedure must check whether the file can be closed.
+ */
+u32
+nlmsvc_cancel_blocked(struct nlm_file *file, struct nlm_lock *lock)
+{
+ struct nlm_block *block;
+
+ dprintk("lockd: nlmsvc_cancel(%04x/%ld, pi=%d, %Ld-%Ld)\n",
+ file->f_file.f_dentry->d_inode->i_dev,
+ file->f_file.f_dentry->d_inode->i_ino,
+ lock->fl.fl_pid,
+ (long long)lock->fl.fl_start,
+ (long long)lock->fl.fl_end);
+
+ down(&file->f_sema);
+ if ((block = nlmsvc_lookup_block(file, lock, 1)) != NULL)
+ nlmsvc_delete_block(block, 1);
+ up(&file->f_sema);
+ return nlm_granted;
+}
+
+/*
+ * Unblock a blocked lock request. This is a callback invoked from the
+ * VFS layer when a lock on which we blocked is removed.
+ *
+ * This function doesn't grant the blocked lock instantly, but rather moves
+ * the block to the head of nlm_blocked where it can be picked up by lockd.
+ */
+static void
+nlmsvc_notify_blocked(struct file_lock *fl)
+{
+ struct nlm_block **bp, *block;
+
+ dprintk("lockd: VFS unblock notification for block %p\n", fl);
+ posix_unblock_lock(fl);
+ for (bp = &nlm_blocked; (block = *bp); bp = &block->b_next) {
+ if (nlm_compare_locks(&block->b_call.a_args.lock.fl, fl)) {
+ nlmsvc_insert_block(block, 0);
+ svc_wake_up(block->b_daemon);
+ return;
+ }
+ }
+
+ printk(KERN_WARNING "lockd: notification for unknown block!\n");
+}
+
+/*
+ * Try to claim a lock that was previously blocked.
+ *
+ * Note that we use both the RPC_GRANTED_MSG call _and_ an async
+ * RPC thread when notifying the client. This seems like overkill...
+ * Here's why:
+ * - we don't want to use a synchronous RPC thread, otherwise
+ * we might find ourselves hanging on a dead portmapper.
+ * - Some lockd implementations (e.g. HP) don't react to
+ * RPC_GRANTED calls; they seem to insist on RPC_GRANTED_MSG calls.
+ */
+static void
+nlmsvc_grant_blocked(struct nlm_block *block)
+{
+ struct nlm_file *file = block->b_file;
+ struct nlm_lock *lock = &block->b_call.a_args.lock;
+ struct file_lock *conflock;
+ int error;
+
+ dprintk("lockd: grant blocked lock %p\n", block);
+
+ /* First thing is lock the file */
+ down(&file->f_sema);
+
+ /* Unlink block request from list */
+ nlmsvc_remove_block(block);
+
+ /* If b_granted is true this means we've been here before.
+ * Just retry the grant callback, possibly refreshing the RPC
+ * binding */
+ if (block->b_granted) {
+ nlm_rebind_host(block->b_call.a_host);
+ goto callback;
+ }
+
+ /* Try the lock operation again */
+ if ((conflock = posix_test_lock(&file->f_file, &lock->fl)) != NULL) {
+ /* Bummer, we blocked again */
+ dprintk("lockd: lock still blocked\n");
+ nlmsvc_insert_block(block, NLM_NEVER);
+ posix_block_lock(conflock, &lock->fl);
+ up(&file->f_sema);
+ return;
+ }
+
+ /* Alright, no conflicting lock. Now lock it for real. If the
+ * following yields an error, this is most probably due to low
+ * memory. Retry the lock in a few seconds.
+ */
+ if ((error = posix_lock_file(&file->f_file, &lock->fl, 0)) < 0) {
+ printk(KERN_WARNING "lockd: unexpected error %d in %s!\n",
+ -error, __FUNCTION__);
+ nlmsvc_insert_block(block, 10 * HZ);
+ up(&file->f_sema);
+ return;
+ }
+
+callback:
+ /* Lock was granted by VFS. */
+ dprintk("lockd: GRANTing blocked lock.\n");
+ block->b_granted = 1;
+ block->b_incall = 1;
+
+ /* Schedule next grant callback in 30 seconds */
+ nlmsvc_insert_block(block, 30 * HZ);
+
+ /* Call the client */
+ nlm_get_host(block->b_call.a_host);
+ if (nlmsvc_async_call(&block->b_call, NLMPROC_GRANTED_MSG,
+ nlmsvc_grant_callback) < 0)
+ nlm_release_host(block->b_call.a_host);
+ up(&file->f_sema);
+}
+
+/*
+ * This is the callback from the RPC layer when the NLM_GRANTED_MSG
+ * RPC call has succeeded or timed out.
+ * Like all RPC callbacks, it is invoked by the rpciod process, so it
+ * better not sleep. Therefore, we put the blocked lock on the nlm_blocked
+ * chain once more in order to have it removed by lockd itself (which can
+ * then sleep on the file semaphore without disrupting e.g. the nfs client).
+ */
+static void
+nlmsvc_grant_callback(struct rpc_task *task)
+{
+ struct nlm_rqst *call = (struct nlm_rqst *) task->tk_calldata;
+ struct nlm_block *block;
+ unsigned long timeout;
+ struct sockaddr_in *peer_addr = RPC_PEERADDR(task->tk_client);
+
+ dprintk("lockd: GRANT_MSG RPC callback\n");
+ dprintk("callback: looking for cookie %s, host %u.%u.%u.%u\n",
+ nlmdbg_cookie2a(&call->a_args.cookie),
+ NIPQUAD(peer_addr->sin_addr.s_addr));
+ if (!(block = nlmsvc_find_block(&call->a_args.cookie, peer_addr))) {
+ dprintk("lockd: no block for cookie %s, host %u.%u.%u.%u\n",
+ nlmdbg_cookie2a(&call->a_args.cookie),
+ NIPQUAD(peer_addr->sin_addr.s_addr));
+ return;
+ }
+
+ /* Technically, we should down the file semaphore here. Since we
+ * move the block towards the head of the queue only, no harm
+ * can be done, though. */
+ if (task->tk_status < 0) {
+ /* RPC error: Re-insert for retransmission */
+ timeout = 10 * HZ;
+ } else if (block->b_done) {
+ /* Block already removed, kill it for real */
+ timeout = 0;
+ } else {
+ /* Call was successful, now wait for client callback */
+ timeout = 60 * HZ;
+ }
+ nlmsvc_insert_block(block, timeout);
+ svc_wake_up(block->b_daemon);
+ block->b_incall = 0;
+
+ nlm_release_host(call->a_host);
+}
+
+/*
+ * We received a GRANT_RES callback. Try to find the corresponding
+ * block.
+ */
+void
+nlmsvc_grant_reply(struct svc_rqst *rqstp, struct nlm_cookie *cookie, u32 status)
+{
+ struct nlm_block *block;
+ struct nlm_file *file;
+
+ dprintk("grant_reply: looking for cookie %x, host (%08x), s=%d \n",
+ *(unsigned int *)(cookie->data),
+ ntohl(rqstp->rq_addr.sin_addr.s_addr), status);
+ if (!(block = nlmsvc_find_block(cookie, &rqstp->rq_addr)))
+ return;
+ file = block->b_file;
+
+ file->f_count++;
+ down(&file->f_sema);
+ if ((block = nlmsvc_find_block(cookie,&rqstp->rq_addr)) != NULL) {
+ if (status == NLM_LCK_DENIED_GRACE_PERIOD) {
+ /* Try again in a couple of seconds */
+ nlmsvc_insert_block(block, 10 * HZ);
+ block = NULL;
+ } else {
+ /* Lock is now held by client, or has been rejected.
+ * In both cases, the block should be removed. */
+ up(&file->f_sema);
+ if (status == NLM_LCK_GRANTED)
+ nlmsvc_delete_block(block, 0);
+ else
+ nlmsvc_delete_block(block, 1);
+ }
+ }
+ if (!block)
+ up(&file->f_sema);
+ nlm_release_file(file);
+}
+
+/*
+ * Retry all blocked locks that have been notified. This is where lockd
+ * picks up locks that can be granted, or grant notifications that must
+ * be retransmitted.
+ */
+unsigned long
+nlmsvc_retry_blocked(void)
+{
+ struct nlm_block *block;
+
+ dprintk("nlmsvc_retry_blocked(%p, when=%ld)\n",
+ nlm_blocked,
+ nlm_blocked? nlm_blocked->b_when : 0);
+ while ((block = nlm_blocked)) {
+ if (block->b_when == NLM_NEVER)
+ break;
+ if (time_after(block->b_when,jiffies))
+ break;
+ dprintk("nlmsvc_retry_blocked(%p, when=%ld, done=%d)\n",
+ block, block->b_when, block->b_done);
+ if (block->b_done)
+ nlmsvc_delete_block(block, 0);
+ else
+ nlmsvc_grant_blocked(block);
+ }
+
+ if ((block = nlm_blocked) && block->b_when != NLM_NEVER)
+ return (block->b_when - jiffies);
+
+ return MAX_SCHEDULE_TIMEOUT;
+}
diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c
new file mode 100644
index 00000000000000..8759aca43742fc
--- /dev/null
+++ b/fs/lockd/svcproc.c
@@ -0,0 +1,616 @@
+/*
+ * linux/fs/lockd/svcproc.c
+ *
+ * Lockd server procedures. We don't implement the NLM_*_RES
+ * procedures because we don't use the async procedures.
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/in.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/nfsd/nfsd.h>
+#include <linux/lockd/lockd.h>
+#include <linux/lockd/share.h>
+#include <linux/lockd/sm_inter.h>
+
+
+#define NLMDBG_FACILITY NLMDBG_CLIENT
+
+static u32 nlmsvc_callback(struct svc_rqst *, u32, struct nlm_res *);
+static void nlmsvc_callback_exit(struct rpc_task *);
+
+#ifdef CONFIG_LOCKD_V4
+static u32
+cast_to_nlm(u32 status, u32 vers)
+{
+ /* Note: status is assumed to be in network byte order !!! */
+ if (vers != 4){
+ switch (status) {
+ case nlm_granted:
+ case nlm_lck_denied:
+ case nlm_lck_denied_nolocks:
+ case nlm_lck_blocked:
+ case nlm_lck_denied_grace_period:
+ break;
+ case nlm4_deadlock:
+ status = nlm_lck_denied;
+ break;
+ default:
+ status = nlm_lck_denied_nolocks;
+ }
+ }
+
+ return (status);
+}
+#define cast_status(status) (cast_to_nlm(status, rqstp->rq_vers))
+#else
+#define cast_status(status) (status)
+#endif
+
+/*
+ * Obtain client and file from arguments
+ */
+static u32
+nlmsvc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
+ struct nlm_host **hostp, struct nlm_file **filp)
+{
+ struct nlm_host *host = NULL;
+ struct nlm_file *file = NULL;
+ struct nlm_lock *lock = &argp->lock;
+ u32 error;
+
+ /* nfsd callbacks must have been installed for this procedure */
+ if (!nlmsvc_ops)
+ return nlm_lck_denied_nolocks;
+
+ /* Obtain handle for client host */
+ if (rqstp->rq_client == NULL) {
+ printk(KERN_NOTICE
+ "lockd: unauthenticated request from (%08x:%d)\n",
+ ntohl(rqstp->rq_addr.sin_addr.s_addr),
+ ntohs(rqstp->rq_addr.sin_port));
+ return nlm_lck_denied_nolocks;
+ }
+
+ /* Obtain host handle */
+ if (!(host = nlmsvc_lookup_host(rqstp))
+ || (argp->monitor && !host->h_monitored && nsm_monitor(host) < 0))
+ goto no_locks;
+ *hostp = host;
+
+ /* Obtain file pointer. Not used by FREE_ALL call. */
+ if (filp != NULL) {
+ if ((error = nlm_lookup_file(rqstp, &file, &lock->fh)) != 0)
+ goto no_locks;
+ *filp = file;
+
+ /* Set up the missing parts of the file_lock structure */
+ lock->fl.fl_file = &file->f_file;
+ lock->fl.fl_owner = (fl_owner_t) host;
+ }
+
+ return 0;
+
+no_locks:
+ if (host)
+ nlm_release_host(host);
+ return nlm_lck_denied_nolocks;
+}
+
+/*
+ * NULL: Test for presence of service
+ */
+static int
+nlmsvc_proc_null(struct svc_rqst *rqstp, void *argp, void *resp)
+{
+ dprintk("lockd: NULL called\n");
+ return rpc_success;
+}
+
+/*
+ * TEST: Check for conflicting lock
+ */
+static int
+nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp,
+ struct nlm_res *resp)
+{
+ struct nlm_host *host;
+ struct nlm_file *file;
+
+ dprintk("lockd: TEST called\n");
+ resp->cookie = argp->cookie;
+
+ /* Don't accept test requests during grace period */
+ if (nlmsvc_grace_period) {
+ resp->status = nlm_lck_denied_grace_period;
+ return rpc_success;
+ }
+
+ /* Obtain client and file */
+ if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
+ return rpc_success;
+
+ /* Now check for conflicting locks */
+ resp->status = cast_status(nlmsvc_testlock(file, &argp->lock, &resp->lock));
+
+ dprintk("lockd: TEST status %d vers %d\n",
+ ntohl(resp->status), rqstp->rq_vers);
+ nlm_release_host(host);
+ nlm_release_file(file);
+ return rpc_success;
+}
+
+static int
+nlmsvc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp,
+ struct nlm_res *resp)
+{
+ struct nlm_host *host;
+ struct nlm_file *file;
+
+ dprintk("lockd: LOCK called\n");
+
+ resp->cookie = argp->cookie;
+
+ /* Don't accept new lock requests during grace period */
+ if (nlmsvc_grace_period && !argp->reclaim) {
+ resp->status = nlm_lck_denied_grace_period;
+ return rpc_success;
+ }
+
+ /* Obtain client and file */
+ if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
+ return rpc_success;
+
+#if 0
+ /* If supplied state doesn't match current state, we assume it's
+ * an old request that time-warped somehow. Any error return would
+ * do in this case because it's irrelevant anyway.
+ *
+ * NB: We don't retrieve the remote host's state yet.
+ */
+ if (host->h_nsmstate && host->h_nsmstate != argp->state) {
+ resp->status = nlm_lck_denied_nolocks;
+ } else
+#endif
+
+ /* Now try to lock the file */
+ resp->status = cast_status(nlmsvc_lock(rqstp, file, &argp->lock,
+ argp->block, &argp->cookie));
+
+ dprintk("lockd: LOCK status %d\n", ntohl(resp->status));
+ nlm_release_host(host);
+ nlm_release_file(file);
+ return rpc_success;
+}
+
+static int
+nlmsvc_proc_cancel(struct svc_rqst *rqstp, struct nlm_args *argp,
+ struct nlm_res *resp)
+{
+ struct nlm_host *host;
+ struct nlm_file *file;
+
+ dprintk("lockd: CANCEL called\n");
+
+ resp->cookie = argp->cookie;
+
+ /* Don't accept requests during grace period */
+ if (nlmsvc_grace_period) {
+ resp->status = nlm_lck_denied_grace_period;
+ return rpc_success;
+ }
+
+ /* Obtain client and file */
+ if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
+ return rpc_success;
+
+ /* Try to cancel request. */
+ resp->status = cast_status(nlmsvc_cancel_blocked(file, &argp->lock));
+
+ dprintk("lockd: CANCEL status %d\n", ntohl(resp->status));
+ nlm_release_host(host);
+ nlm_release_file(file);
+ return rpc_success;
+}
+
+/*
+ * UNLOCK: release a lock
+ */
+static int
+nlmsvc_proc_unlock(struct svc_rqst *rqstp, struct nlm_args *argp,
+ struct nlm_res *resp)
+{
+ struct nlm_host *host;
+ struct nlm_file *file;
+
+ dprintk("lockd: UNLOCK called\n");
+
+ resp->cookie = argp->cookie;
+
+ /* Don't accept new lock requests during grace period */
+ if (nlmsvc_grace_period) {
+ resp->status = nlm_lck_denied_grace_period;
+ return rpc_success;
+ }
+
+ /* Obtain client and file */
+ if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
+ return rpc_success;
+
+ /* Now try to remove the lock */
+ resp->status = cast_status(nlmsvc_unlock(file, &argp->lock));
+
+ dprintk("lockd: UNLOCK status %d\n", ntohl(resp->status));
+ nlm_release_host(host);
+ nlm_release_file(file);
+ return rpc_success;
+}
+
+/*
+ * GRANTED: A server calls us to tell that a process' lock request
+ * was granted
+ */
+static int
+nlmsvc_proc_granted(struct svc_rqst *rqstp, struct nlm_args *argp,
+ struct nlm_res *resp)
+{
+ resp->cookie = argp->cookie;
+
+ dprintk("lockd: GRANTED called\n");
+ resp->status = nlmclnt_grant(&argp->lock);
+ dprintk("lockd: GRANTED status %d\n", ntohl(resp->status));
+ return rpc_success;
+}
+
+/*
+ * `Async' versions of the above service routines. They aren't really,
+ * because we send the callback before the reply proper. I hope this
+ * doesn't break any clients.
+ */
+static int
+nlmsvc_proc_test_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
+ void *resp)
+{
+ struct nlm_res res;
+ u32 stat;
+
+ dprintk("lockd: TEST_MSG called\n");
+
+ memset(&res, 0, sizeof(res));
+
+ if ((stat = nlmsvc_proc_test(rqstp, argp, &res)) == 0)
+ stat = nlmsvc_callback(rqstp, NLMPROC_TEST_RES, &res);
+ return stat;
+}
+
+static int
+nlmsvc_proc_lock_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
+ void *resp)
+{
+ struct nlm_res res;
+ u32 stat;
+
+ dprintk("lockd: LOCK_MSG called\n");
+
+ if ((stat = nlmsvc_proc_lock(rqstp, argp, &res)) == 0)
+ stat = nlmsvc_callback(rqstp, NLMPROC_LOCK_RES, &res);
+ return stat;
+}
+
+static int
+nlmsvc_proc_cancel_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
+ void *resp)
+{
+ struct nlm_res res;
+ u32 stat;
+
+ dprintk("lockd: CANCEL_MSG called\n");
+
+ if ((stat = nlmsvc_proc_cancel(rqstp, argp, &res)) == 0)
+ stat = nlmsvc_callback(rqstp, NLMPROC_CANCEL_RES, &res);
+ return stat;
+}
+
+static int
+nlmsvc_proc_unlock_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
+ void *resp)
+{
+ struct nlm_res res;
+ u32 stat;
+
+ dprintk("lockd: UNLOCK_MSG called\n");
+
+ if ((stat = nlmsvc_proc_unlock(rqstp, argp, &res)) == 0)
+ stat = nlmsvc_callback(rqstp, NLMPROC_UNLOCK_RES, &res);
+ return stat;
+}
+
+static int
+nlmsvc_proc_granted_msg(struct svc_rqst *rqstp, struct nlm_args *argp,
+ void *resp)
+{
+ struct nlm_res res;
+ u32 stat;
+
+ dprintk("lockd: GRANTED_MSG called\n");
+
+ if ((stat = nlmsvc_proc_granted(rqstp, argp, &res)) == 0)
+ stat = nlmsvc_callback(rqstp, NLMPROC_GRANTED_RES, &res);
+ return stat;
+}
+
+/*
+ * SHARE: create a DOS share or alter existing share.
+ */
+static int
+nlmsvc_proc_share(struct svc_rqst *rqstp, struct nlm_args *argp,
+ struct nlm_res *resp)
+{
+ struct nlm_host *host;
+ struct nlm_file *file;
+
+ dprintk("lockd: SHARE called\n");
+
+ resp->cookie = argp->cookie;
+
+ /* Don't accept new lock requests during grace period */
+ if (nlmsvc_grace_period && !argp->reclaim) {
+ resp->status = nlm_lck_denied_grace_period;
+ return rpc_success;
+ }
+
+ /* Obtain client and file */
+ if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
+ return rpc_success;
+
+ /* Now try to create the share */
+ resp->status = cast_status(nlmsvc_share_file(host, file, argp));
+
+ dprintk("lockd: SHARE status %d\n", ntohl(resp->status));
+ nlm_release_host(host);
+ nlm_release_file(file);
+ return rpc_success;
+}
+
+/*
+ * UNSHARE: Release a DOS share.
+ */
+static int
+nlmsvc_proc_unshare(struct svc_rqst *rqstp, struct nlm_args *argp,
+ struct nlm_res *resp)
+{
+ struct nlm_host *host;
+ struct nlm_file *file;
+
+ dprintk("lockd: UNSHARE called\n");
+
+ resp->cookie = argp->cookie;
+
+ /* Don't accept requests during grace period */
+ if (nlmsvc_grace_period) {
+ resp->status = nlm_lck_denied_grace_period;
+ return rpc_success;
+ }
+
+ /* Obtain client and file */
+ if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
+ return rpc_success;
+
+ /* Now try to unshare the file */
+ resp->status = cast_status(nlmsvc_unshare_file(host, file, argp));
+
+ dprintk("lockd: UNSHARE status %d\n", ntohl(resp->status));
+ nlm_release_host(host);
+ nlm_release_file(file);
+ return rpc_success;
+}
+
+/*
+ * NM_LOCK: Create an unmonitored lock
+ */
+static int
+nlmsvc_proc_nm_lock(struct svc_rqst *rqstp, struct nlm_args *argp,
+ struct nlm_res *resp)
+{
+ dprintk("lockd: NM_LOCK called\n");
+
+ argp->monitor = 0; /* just clean the monitor flag */
+ return nlmsvc_proc_lock(rqstp, argp, resp);
+}
+
+/*
+ * FREE_ALL: Release all locks and shares held by client
+ */
+static int
+nlmsvc_proc_free_all(struct svc_rqst *rqstp, struct nlm_args *argp,
+ void *resp)
+{
+ struct nlm_host *host;
+
+ /* Obtain client */
+ if (nlmsvc_retrieve_args(rqstp, argp, &host, NULL))
+ return rpc_success;
+
+ nlmsvc_free_host_resources(host);
+ nlm_release_host(host);
+ return rpc_success;
+}
+
+/*
+ * SM_NOTIFY: private callback from statd (not part of official NLM proto)
+ */
+static int
+nlmsvc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp,
+ void *resp)
+{
+ struct sockaddr_in saddr = rqstp->rq_addr;
+ int vers = rqstp->rq_vers;
+ int prot = rqstp->rq_prot;
+ struct nlm_host *host;
+
+ dprintk("lockd: SM_NOTIFY called\n");
+ if (saddr.sin_addr.s_addr != htonl(INADDR_LOOPBACK)
+ || ntohs(saddr.sin_port) >= 1024) {
+ printk(KERN_WARNING
+ "lockd: rejected NSM callback from %08x:%d\n",
+ ntohl(rqstp->rq_addr.sin_addr.s_addr),
+ ntohs(rqstp->rq_addr.sin_port));
+ return rpc_system_err;
+ }
+
+ /* Obtain the host pointer for this NFS server and try to
+ * reclaim all locks we hold on this server.
+ */
+ saddr.sin_addr.s_addr = argp->addr;
+ if ((host = nlmclnt_lookup_host(&saddr, prot, vers)) != NULL) {
+ nlmclnt_recovery(host, argp->state);
+ nlm_release_host(host);
+ }
+
+ /* If we run on an NFS server, delete all locks held by the client */
+ if (nlmsvc_ops != NULL) {
+ struct svc_client *clnt;
+ saddr.sin_addr.s_addr = argp->addr;
+ if ((clnt = nlmsvc_ops->exp_getclient(&saddr)) != NULL
+ && (host = nlm_lookup_host(clnt, &saddr, 0, 0)) != NULL) {
+ nlmsvc_free_host_resources(host);
+ }
+ nlm_release_host(host);
+ }
+
+ return rpc_success;
+}
+
+/*
+ * client sent a GRANTED_RES, let's remove the associated block
+ */
+static int
+nlmsvc_proc_granted_res(struct svc_rqst *rqstp, struct nlm_res *argp,
+ void *resp)
+{
+ if (!nlmsvc_ops)
+ return rpc_success;
+
+ dprintk("lockd: GRANTED_RES called\n");
+
+ nlmsvc_grant_reply(rqstp, &argp->cookie, argp->status);
+ return rpc_success;
+}
+
+/*
+ * This is the generic lockd callback for async RPC calls
+ */
+static u32
+nlmsvc_callback(struct svc_rqst *rqstp, u32 proc, struct nlm_res *resp)
+{
+ struct nlm_host *host;
+ struct nlm_rqst *call;
+
+ if (!(call = nlmclnt_alloc_call()))
+ return rpc_system_err;
+
+ host = nlmclnt_lookup_host(&rqstp->rq_addr,
+ rqstp->rq_prot, rqstp->rq_vers);
+ if (!host) {
+ kfree(call);
+ return rpc_system_err;
+ }
+
+ call->a_flags = RPC_TASK_ASYNC;
+ call->a_host = host;
+ memcpy(&call->a_args, resp, sizeof(*resp));
+
+ if (nlmsvc_async_call(call, proc, nlmsvc_callback_exit) < 0)
+ goto error;
+
+ return rpc_success;
+ error:
+ nlm_release_host(host);
+ kfree(call);
+ return rpc_system_err;
+}
+
+static void
+nlmsvc_callback_exit(struct rpc_task *task)
+{
+ struct nlm_rqst *call = (struct nlm_rqst *) task->tk_calldata;
+
+ if (task->tk_status < 0) {
+ dprintk("lockd: %4d callback failed (errno = %d)\n",
+ task->tk_pid, -task->tk_status);
+ }
+ nlm_release_host(call->a_host);
+ kfree(call);
+}
+
+/*
+ * NLM Server procedures.
+ */
+
+#define nlmsvc_encode_norep nlmsvc_encode_void
+#define nlmsvc_decode_norep nlmsvc_decode_void
+#define nlmsvc_decode_testres nlmsvc_decode_void
+#define nlmsvc_decode_lockres nlmsvc_decode_void
+#define nlmsvc_decode_unlockres nlmsvc_decode_void
+#define nlmsvc_decode_cancelres nlmsvc_decode_void
+#define nlmsvc_decode_grantedres nlmsvc_decode_void
+
+#define nlmsvc_proc_none nlmsvc_proc_null
+#define nlmsvc_proc_test_res nlmsvc_proc_null
+#define nlmsvc_proc_lock_res nlmsvc_proc_null
+#define nlmsvc_proc_cancel_res nlmsvc_proc_null
+#define nlmsvc_proc_unlock_res nlmsvc_proc_null
+
+struct nlm_void { int dummy; };
+
+#define PROC(name, xargt, xrest, argt, rest, respsize) \
+ { (svc_procfunc) nlmsvc_proc_##name, \
+ (kxdrproc_t) nlmsvc_decode_##xargt, \
+ (kxdrproc_t) nlmsvc_encode_##xrest, \
+ NULL, \
+ sizeof(struct nlm_##argt), \
+ sizeof(struct nlm_##rest), \
+ 0, \
+ 0, \
+ respsize, \
+ }
+
+#define Ck (1+8) /* cookie */
+#define St 1 /* status */
+#define No (1+1024/4) /* Net Obj */
+#define Rg 2 /* range - offset + size */
+
+struct svc_procedure nlmsvc_procedures[] = {
+ PROC(null, void, void, void, void, 1),
+ PROC(test, testargs, testres, args, res, Ck+St+2+No+Rg),
+ PROC(lock, lockargs, res, args, res, Ck+St),
+ PROC(cancel, cancargs, res, args, res, Ck+St),
+ PROC(unlock, unlockargs, res, args, res, Ck+St),
+ PROC(granted, testargs, res, args, res, Ck+St),
+ PROC(test_msg, testargs, norep, args, void, 1),
+ PROC(lock_msg, lockargs, norep, args, void, 1),
+ PROC(cancel_msg, cancargs, norep, args, void, 1),
+ PROC(unlock_msg, unlockargs, norep, args, void, 1),
+ PROC(granted_msg, testargs, norep, args, void, 1),
+ PROC(test_res, testres, norep, res, void, 1),
+ PROC(lock_res, lockres, norep, res, void, 1),
+ PROC(cancel_res, cancelres, norep, res, void, 1),
+ PROC(unlock_res, unlockres, norep, res, void, 1),
+ PROC(granted_res, res, norep, res, void, 1),
+ /* statd callback */
+ PROC(sm_notify, reboot, void, reboot, void, 1),
+ PROC(none, void, void, void, void, 1),
+ PROC(none, void, void, void, void, 1),
+ PROC(none, void, void, void, void, 1),
+ PROC(share, shareargs, shareres, args, res, Ck+St+1),
+ PROC(unshare, shareargs, shareres, args, res, Ck+St+1),
+ PROC(nm_lock, lockargs, res, args, res, Ck+St),
+ PROC(free_all, notify, void, args, void, 0),
+
+};
diff --git a/fs/lockd/svcshare.c b/fs/lockd/svcshare.c
new file mode 100644
index 00000000000000..73fbb1ba414b77
--- /dev/null
+++ b/fs/lockd/svcshare.c
@@ -0,0 +1,111 @@
+/*
+ * linux/fs/lockd/svcshare.c
+ *
+ * Management of DOS shares.
+ *
+ * Copyright (C) 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/sched.h>
+#include <linux/unistd.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/lockd/lockd.h>
+#include <linux/lockd/share.h>
+
+static inline int
+nlm_cmp_owner(struct nlm_share *share, struct xdr_netobj *oh)
+{
+ return share->s_owner.len == oh->len
+ && !memcmp(share->s_owner.data, oh->data, oh->len);
+}
+
+u32
+nlmsvc_share_file(struct nlm_host *host, struct nlm_file *file,
+ struct nlm_args *argp)
+{
+ struct nlm_share *share;
+ struct xdr_netobj *oh = &argp->lock.oh;
+ u8 *ohdata;
+
+ for (share = file->f_shares; share; share = share->s_next) {
+ if (share->s_host == host && nlm_cmp_owner(share, oh))
+ goto update;
+ if ((argp->fsm_access & share->s_mode)
+ || (argp->fsm_mode & share->s_access ))
+ return nlm_lck_denied;
+ }
+
+ share = (struct nlm_share *) kmalloc(sizeof(*share) + oh->len,
+ GFP_KERNEL);
+ if (share == NULL)
+ return nlm_lck_denied_nolocks;
+
+ /* Copy owner handle */
+ ohdata = (u8 *) (share + 1);
+ memcpy(ohdata, oh->data, oh->len);
+
+ share->s_file = file;
+ share->s_host = host;
+ share->s_owner.data = ohdata;
+ share->s_owner.len = oh->len;
+ share->s_next = file->f_shares;
+ file->f_shares = share;
+
+update:
+ share->s_access = argp->fsm_access;
+ share->s_mode = argp->fsm_mode;
+ return nlm_granted;
+}
+
+/*
+ * Delete a share.
+ */
+u32
+nlmsvc_unshare_file(struct nlm_host *host, struct nlm_file *file,
+ struct nlm_args *argp)
+{
+ struct nlm_share *share, **shpp;
+ struct xdr_netobj *oh = &argp->lock.oh;
+
+ for (shpp = &file->f_shares; (share = *shpp); shpp = &share->s_next) {
+ if (share->s_host == host && nlm_cmp_owner(share, oh)) {
+ *shpp = share->s_next;
+ kfree(share);
+ return nlm_granted;
+ }
+ }
+
+ /* X/Open spec says return success even if there was no
+ * corresponding share. */
+ return nlm_granted;
+}
+
+/*
+ * Traverse all shares for a given file (and host).
+ * NLM_ACT_CHECK is handled by nlmsvc_inspect_file.
+ */
+int
+nlmsvc_traverse_shares(struct nlm_host *host, struct nlm_file *file, int action)
+{
+ struct nlm_share *share, **shpp;
+
+ shpp = &file->f_shares;
+ while ((share = *shpp) != NULL) {
+ if (action == NLM_ACT_MARK)
+ share->s_host->h_inuse = 1;
+ else if (action == NLM_ACT_UNLOCK) {
+ if (host == NULL || host == share->s_host) {
+ *shpp = share->s_next;
+ kfree(share);
+ continue;
+ }
+ }
+ shpp = &share->s_next;
+ }
+
+ return 0;
+}
diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c
new file mode 100644
index 00000000000000..e7bcab77540c39
--- /dev/null
+++ b/fs/lockd/svcsubs.c
@@ -0,0 +1,311 @@
+/*
+ * linux/fs/lockd/svcsubs.c
+ *
+ * Various support routines for the NLM server.
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/in.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/nfsd/nfsfh.h>
+#include <linux/nfsd/export.h>
+#include <linux/lockd/lockd.h>
+#include <linux/lockd/share.h>
+#include <linux/lockd/sm_inter.h>
+
+#define NLMDBG_FACILITY NLMDBG_SVCSUBS
+
+
+/*
+ * Global file hash table
+ */
+#define FILE_HASH_BITS 5
+#define FILE_NRHASH (1<<FILE_HASH_BITS)
+static struct nlm_file * nlm_files[FILE_NRHASH];
+static DECLARE_MUTEX(nlm_file_sema);
+
+static inline unsigned int file_hash(struct nfs_fh *f)
+{
+ unsigned int tmp=0;
+ int i;
+ for (i=0; i<NFS2_FHSIZE;i++)
+ tmp += f->data[i];
+ return tmp & (FILE_NRHASH - 1);
+}
+
+/*
+ * Lookup file info. If it doesn't exist, create a file info struct
+ * and open a (VFS) file for the given inode.
+ *
+ * FIXME:
+ * Note that we open the file O_RDONLY even when creating write locks.
+ * This is not quite right, but for now, we assume the client performs
+ * the proper R/W checking.
+ */
+u32
+nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
+ struct nfs_fh *f)
+{
+ struct nlm_file *file;
+ unsigned int hash;
+ u32 nfserr;
+ u32 *fhp = (u32*)f->data;
+
+ dprintk("lockd: nlm_file_lookup(%08x %08x %08x %08x %08x %08x)\n",
+ fhp[0], fhp[1], fhp[2], fhp[3], fhp[4], fhp[5]);
+
+
+ hash = file_hash(f);
+
+ /* Lock file table */
+ down(&nlm_file_sema);
+
+ for (file = nlm_files[hash]; file; file = file->f_next)
+ if (!memcmp(&file->f_handle, f, sizeof(*f)))
+ goto found;
+
+ dprintk("lockd: creating file for (%08x %08x %08x %08x %08x %08x)\n",
+ fhp[0], fhp[1], fhp[2], fhp[3], fhp[4], fhp[5]);
+
+ nfserr = nlm_lck_denied_nolocks;
+ file = (struct nlm_file *) kmalloc(sizeof(*file), GFP_KERNEL);
+ if (!file)
+ goto out_unlock;
+
+ memset(file, 0, sizeof(*file));
+ memcpy(&file->f_handle, f, sizeof(struct nfs_fh));
+ file->f_hash = hash;
+ init_MUTEX(&file->f_sema);
+
+ /* Open the file. Note that this must not sleep for too long, else
+ * we would lock up lockd:-) So no NFS re-exports, folks.
+ *
+ * We have to make sure we have the right credential to open
+ * the file.
+ */
+ if ((nfserr = nlmsvc_ops->fopen(rqstp, f, &file->f_file)) != 0) {
+ dprintk("lockd: open failed (nfserr %d)\n", ntohl(nfserr));
+ goto out_free;
+ }
+
+ file->f_next = nlm_files[hash];
+ nlm_files[hash] = file;
+
+found:
+ dprintk("lockd: found file %p (count %d)\n", file, file->f_count);
+ *result = file;
+ file->f_count++;
+ nfserr = 0;
+
+out_unlock:
+ up(&nlm_file_sema);
+ return nfserr;
+
+out_free:
+ kfree(file);
+#ifdef CONFIG_LOCKD_V4
+ if (nfserr == 1)
+ nfserr = nlm4_stale_fh;
+ else
+#endif
+ nfserr = nlm_lck_denied;
+ goto out_unlock;
+}
+
+/*
+ * Delete a file after having released all locks, blocks and shares
+ */
+static inline void
+nlm_delete_file(struct nlm_file *file)
+{
+ struct inode *inode = file->f_file.f_dentry->d_inode;
+ struct nlm_file **fp, *f;
+
+ dprintk("lockd: closing file %s/%ld\n",
+ kdevname(inode->i_dev), inode->i_ino);
+ fp = nlm_files + file->f_hash;
+ while ((f = *fp) != NULL) {
+ if (f == file) {
+ *fp = file->f_next;
+ nlmsvc_ops->fclose(&file->f_file);
+ kfree(file);
+ return;
+ }
+ fp = &f->f_next;
+ }
+
+ printk(KERN_WARNING "lockd: attempt to release unknown file!\n");
+}
+
+/*
+ * Loop over all locks on the given file and perform the specified
+ * action.
+ */
+static int
+nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file, int action)
+{
+ struct inode *inode = nlmsvc_file_inode(file);
+ struct file_lock *fl;
+ struct nlm_host *lockhost;
+
+again:
+ file->f_locks = 0;
+ for (fl = inode->i_flock; fl; fl = fl->fl_next) {
+ if (!(fl->fl_flags & FL_LOCKD))
+ continue;
+
+ /* update current lock count */
+ file->f_locks++;
+ lockhost = (struct nlm_host *) fl->fl_owner;
+ if (action == NLM_ACT_MARK)
+ lockhost->h_inuse = 1;
+ else if (action == NLM_ACT_CHECK)
+ return 1;
+ else if (action == NLM_ACT_UNLOCK) {
+ struct file_lock lock = *fl;
+
+ if (host && lockhost != host)
+ continue;
+
+ lock.fl_type = F_UNLCK;
+ lock.fl_start = 0;
+ lock.fl_end = OFFSET_MAX;
+ if (posix_lock_file(&file->f_file, &lock, 0) < 0) {
+ printk("lockd: unlock failure in %s:%d\n",
+ __FILE__, __LINE__);
+ return 1;
+ }
+ goto again;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Operate on a single file
+ */
+static inline int
+nlm_inspect_file(struct nlm_host *host, struct nlm_file *file, int action)
+{
+ if (action == NLM_ACT_CHECK) {
+ /* Fast path for mark and sweep garbage collection */
+ if (file->f_count || file->f_blocks || file->f_shares)
+ return 1;
+ } else {
+ if (nlmsvc_traverse_blocks(host, file, action)
+ || nlmsvc_traverse_shares(host, file, action))
+ return 1;
+ }
+ return nlm_traverse_locks(host, file, action);
+}
+
+/*
+ * Loop over all files in the file table.
+ */
+static int
+nlm_traverse_files(struct nlm_host *host, int action)
+{
+ struct nlm_file *file, **fp;
+ int i;
+
+ down(&nlm_file_sema);
+ for (i = 0; i < FILE_NRHASH; i++) {
+ fp = nlm_files + i;
+ while ((file = *fp) != NULL) {
+ /* Traverse locks, blocks and shares of this file
+ * and update file->f_locks count */
+ if (nlm_inspect_file(host, file, action)) {
+ up(&nlm_file_sema);
+ return 1;
+ }
+
+ /* No more references to this file. Let go of it. */
+ if (!file->f_blocks && !file->f_locks
+ && !file->f_shares && !file->f_count) {
+ *fp = file->f_next;
+ nlmsvc_ops->fclose(&file->f_file);
+ kfree(file);
+ } else {
+ fp = &file->f_next;
+ }
+ }
+ }
+ up(&nlm_file_sema);
+ return 0;
+}
+
+/*
+ * Release file. If there are no more remote locks on this file,
+ * close it and free the handle.
+ *
+ * Note that we can't do proper reference counting without major
+ * contortions because the code in fs/locks.c creates, deletes and
+ * splits locks without notification. Our only way is to walk the
+ * entire lock list each time we remove a lock.
+ */
+void
+nlm_release_file(struct nlm_file *file)
+{
+ dprintk("lockd: nlm_release_file(%p, ct = %d)\n",
+ file, file->f_count);
+
+ /* Lock file table */
+ down(&nlm_file_sema);
+
+ /* If there are no more locks etc, delete the file */
+ if(--file->f_count == 0) {
+ if(!nlm_inspect_file(NULL, file, NLM_ACT_CHECK))
+ nlm_delete_file(file);
+ }
+
+ up(&nlm_file_sema);
+}
+
+/*
+ * Mark all hosts that still hold resources
+ */
+void
+nlmsvc_mark_resources(void)
+{
+ dprintk("lockd: nlmsvc_mark_resources\n");
+
+ nlm_traverse_files(NULL, NLM_ACT_MARK);
+}
+
+/*
+ * Release all resources held by the given client
+ */
+void
+nlmsvc_free_host_resources(struct nlm_host *host)
+{
+ dprintk("lockd: nlmsvc_free_host_resources\n");
+
+ if (nlm_traverse_files(host, NLM_ACT_UNLOCK))
+ printk(KERN_WARNING
+ "lockd: couldn't remove all locks held by %s",
+ host->h_name);
+}
+
+/*
+ * Delete a client when the nfsd entry is removed.
+ */
+void
+nlmsvc_invalidate_client(struct svc_client *clnt)
+{
+ struct nlm_host *host;
+
+ if ((host = nlm_lookup_host(clnt, NULL, 0, 0)) != NULL) {
+ dprintk("lockd: invalidating client for %s\n", host->h_name);
+ nlmsvc_free_host_resources(host);
+ host->h_expires = 0;
+ host->h_killed = 1;
+ nlm_release_host(host);
+ }
+}
diff --git a/fs/lockd/xdr.c b/fs/lockd/xdr.c
new file mode 100644
index 00000000000000..691bdded7bbc81
--- /dev/null
+++ b/fs/lockd/xdr.c
@@ -0,0 +1,674 @@
+/*
+ * linux/fs/lockd/xdr.c
+ *
+ * XDR support for lockd and the lock client.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/utsname.h>
+#include <linux/nfs.h>
+
+#include <linux/sunrpc/xdr.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/sunrpc/stats.h>
+#include <linux/lockd/lockd.h>
+#include <linux/lockd/sm_inter.h>
+
+#define NLMDBG_FACILITY NLMDBG_XDR
+
+
+static inline loff_t
+s32_to_loff_t(__s32 offset)
+{
+ return (loff_t)offset;
+}
+
+static inline __s32
+loff_t_to_s32(loff_t offset)
+{
+ __s32 res;
+ if (offset >= NLM_OFFSET_MAX)
+ res = NLM_OFFSET_MAX;
+ else if (offset <= -NLM_OFFSET_MAX)
+ res = -NLM_OFFSET_MAX;
+ else
+ res = offset;
+ return res;
+}
+
+/*
+ * XDR functions for basic NLM types
+ */
+static inline u32 *nlm_decode_cookie(u32 *p, struct nlm_cookie *c)
+{
+ unsigned int len;
+
+ len = ntohl(*p++);
+
+ if(len==0)
+ {
+ c->len=4;
+ memset(c->data, 0, 4); /* hockeypux brain damage */
+ }
+ else if(len<=NLM_MAXCOOKIELEN)
+ {
+ c->len=len;
+ memcpy(c->data, p, len);
+ p+=(len+3)>>2;
+ }
+ else
+ {
+ printk(KERN_NOTICE
+ "lockd: bad cookie size %d (only cookies under %d bytes are supported.)\n", len, NLM_MAXCOOKIELEN);
+ return NULL;
+ }
+ return p;
+}
+
+static inline u32 *
+nlm_encode_cookie(u32 *p, struct nlm_cookie *c)
+{
+ *p++ = htonl(c->len);
+ memcpy(p, c->data, c->len);
+ p+=(c->len+3)>>2;
+ return p;
+}
+
+static inline u32 *
+nlm_decode_fh(u32 *p, struct nfs_fh *f)
+{
+ unsigned int len;
+
+ if ((len = ntohl(*p++)) != NFS2_FHSIZE) {
+ printk(KERN_NOTICE
+ "lockd: bad fhandle size %d (should be %d)\n",
+ len, NFS2_FHSIZE);
+ return NULL;
+ }
+ f->size = NFS2_FHSIZE;
+ memset(f->data, 0, sizeof(f->data));
+ memcpy(f->data, p, NFS2_FHSIZE);
+ return p + XDR_QUADLEN(NFS2_FHSIZE);
+}
+
+static inline u32 *
+nlm_encode_fh(u32 *p, struct nfs_fh *f)
+{
+ *p++ = htonl(NFS2_FHSIZE);
+ memcpy(p, f->data, NFS2_FHSIZE);
+ return p + XDR_QUADLEN(NFS2_FHSIZE);
+}
+
+/*
+ * Encode and decode owner handle
+ */
+static inline u32 *
+nlm_decode_oh(u32 *p, struct xdr_netobj *oh)
+{
+ return xdr_decode_netobj(p, oh);
+}
+
+static inline u32 *
+nlm_encode_oh(u32 *p, struct xdr_netobj *oh)
+{
+ return xdr_encode_netobj(p, oh);
+}
+
+static inline u32 *
+nlm_decode_lock(u32 *p, struct nlm_lock *lock)
+{
+ struct file_lock *fl = &lock->fl;
+ s32 start, len, end;
+
+ if (!(p = xdr_decode_string_inplace(p, &lock->caller,
+ &lock->len,
+ NLM_MAXSTRLEN))
+ || !(p = nlm_decode_fh(p, &lock->fh))
+ || !(p = nlm_decode_oh(p, &lock->oh)))
+ return NULL;
+
+ locks_init_lock(fl);
+ fl->fl_owner = current->files;
+ fl->fl_pid = ntohl(*p++);
+ fl->fl_flags = FL_POSIX;
+ fl->fl_type = F_RDLCK; /* as good as anything else */
+ start = ntohl(*p++);
+ len = ntohl(*p++);
+ end = start + len - 1;
+
+ fl->fl_start = s32_to_loff_t(start);
+
+ if (len == 0 || end < 0)
+ fl->fl_end = OFFSET_MAX;
+ else
+ fl->fl_end = s32_to_loff_t(end);
+ return p;
+}
+
+/*
+ * Encode a lock as part of an NLM call
+ */
+static u32 *
+nlm_encode_lock(u32 *p, struct nlm_lock *lock)
+{
+ struct file_lock *fl = &lock->fl;
+ __s32 start, len;
+
+ if (!(p = xdr_encode_string(p, lock->caller))
+ || !(p = nlm_encode_fh(p, &lock->fh))
+ || !(p = nlm_encode_oh(p, &lock->oh)))
+ return NULL;
+
+ if (fl->fl_start > NLM_OFFSET_MAX
+ || (fl->fl_end > NLM_OFFSET_MAX && fl->fl_end != OFFSET_MAX))
+ return NULL;
+
+ start = loff_t_to_s32(fl->fl_start);
+ if (fl->fl_end == OFFSET_MAX)
+ len = 0;
+ else
+ len = loff_t_to_s32(fl->fl_end - fl->fl_start + 1);
+
+ *p++ = htonl(fl->fl_pid);
+ *p++ = htonl(start);
+ *p++ = htonl(len);
+
+ return p;
+}
+
+/*
+ * Encode result of a TEST/TEST_MSG call
+ */
+static u32 *
+nlm_encode_testres(u32 *p, struct nlm_res *resp)
+{
+ s32 start, len;
+
+ if (!(p = nlm_encode_cookie(p, &resp->cookie)))
+ return 0;
+ *p++ = resp->status;
+
+ if (resp->status == nlm_lck_denied) {
+ struct file_lock *fl = &resp->lock.fl;
+
+ *p++ = (fl->fl_type == F_RDLCK)? xdr_zero : xdr_one;
+ *p++ = htonl(fl->fl_pid);
+
+ /* Encode owner handle. */
+ if (!(p = xdr_encode_netobj(p, &resp->lock.oh)))
+ return 0;
+
+ start = loff_t_to_s32(fl->fl_start);
+ if (fl->fl_end == OFFSET_MAX)
+ len = 0;
+ else
+ len = loff_t_to_s32(fl->fl_end - fl->fl_start + 1);
+
+ *p++ = htonl(start);
+ *p++ = htonl(len);
+ }
+
+ return p;
+}
+
+/*
+ * Check buffer bounds after decoding arguments
+ */
+static inline int
+xdr_argsize_check(struct svc_rqst *rqstp, u32 *p)
+{
+ struct svc_buf *buf = &rqstp->rq_argbuf;
+
+ return p - buf->base <= buf->buflen;
+}
+
+static inline int
+xdr_ressize_check(struct svc_rqst *rqstp, u32 *p)
+{
+ struct svc_buf *buf = &rqstp->rq_resbuf;
+
+ buf->len = p - buf->base;
+ return (buf->len <= buf->buflen);
+}
+
+/*
+ * First, the server side XDR functions
+ */
+int
+nlmsvc_decode_testargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp)
+{
+ u32 exclusive;
+
+ if (!(p = nlm_decode_cookie(p, &argp->cookie)))
+ return 0;
+
+ exclusive = ntohl(*p++);
+ if (!(p = nlm_decode_lock(p, &argp->lock)))
+ return 0;
+ if (exclusive)
+ argp->lock.fl.fl_type = F_WRLCK;
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlmsvc_encode_testres(struct svc_rqst *rqstp, u32 *p, struct nlm_res *resp)
+{
+ if (!(p = nlm_encode_testres(p, resp)))
+ return 0;
+ return xdr_ressize_check(rqstp, p);
+}
+
+int
+nlmsvc_decode_lockargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp)
+{
+ u32 exclusive;
+
+ if (!(p = nlm_decode_cookie(p, &argp->cookie)))
+ return 0;
+ argp->block = ntohl(*p++);
+ exclusive = ntohl(*p++);
+ if (!(p = nlm_decode_lock(p, &argp->lock)))
+ return 0;
+ if (exclusive)
+ argp->lock.fl.fl_type = F_WRLCK;
+ argp->reclaim = ntohl(*p++);
+ argp->state = ntohl(*p++);
+ argp->monitor = 1; /* monitor client by default */
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlmsvc_decode_cancargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp)
+{
+ u32 exclusive;
+
+ if (!(p = nlm_decode_cookie(p, &argp->cookie)))
+ return 0;
+ argp->block = ntohl(*p++);
+ exclusive = ntohl(*p++);
+ if (!(p = nlm_decode_lock(p, &argp->lock)))
+ return 0;
+ if (exclusive)
+ argp->lock.fl.fl_type = F_WRLCK;
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlmsvc_decode_unlockargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp)
+{
+ if (!(p = nlm_decode_cookie(p, &argp->cookie))
+ || !(p = nlm_decode_lock(p, &argp->lock)))
+ return 0;
+ argp->lock.fl.fl_type = F_UNLCK;
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlmsvc_decode_shareargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp)
+{
+ struct nlm_lock *lock = &argp->lock;
+
+ memset(lock, 0, sizeof(*lock));
+ locks_init_lock(&lock->fl);
+ lock->fl.fl_pid = ~(u32) 0;
+
+ if (!(p = nlm_decode_cookie(p, &argp->cookie))
+ || !(p = xdr_decode_string_inplace(p, &lock->caller,
+ &lock->len, NLM_MAXSTRLEN))
+ || !(p = nlm_decode_fh(p, &lock->fh))
+ || !(p = nlm_decode_oh(p, &lock->oh)))
+ return 0;
+ argp->fsm_mode = ntohl(*p++);
+ argp->fsm_access = ntohl(*p++);
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlmsvc_encode_shareres(struct svc_rqst *rqstp, u32 *p, struct nlm_res *resp)
+{
+ if (!(p = nlm_encode_cookie(p, &resp->cookie)))
+ return 0;
+ *p++ = resp->status;
+ *p++ = xdr_zero; /* sequence argument */
+ return xdr_ressize_check(rqstp, p);
+}
+
+int
+nlmsvc_encode_res(struct svc_rqst *rqstp, u32 *p, struct nlm_res *resp)
+{
+ if (!(p = nlm_encode_cookie(p, &resp->cookie)))
+ return 0;
+ *p++ = resp->status;
+ return xdr_ressize_check(rqstp, p);
+}
+
+int
+nlmsvc_decode_notify(struct svc_rqst *rqstp, u32 *p, struct nlm_args *argp)
+{
+ struct nlm_lock *lock = &argp->lock;
+
+ if (!(p = xdr_decode_string_inplace(p, &lock->caller,
+ &lock->len, NLM_MAXSTRLEN)))
+ return 0;
+ argp->state = ntohl(*p++);
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlmsvc_decode_reboot(struct svc_rqst *rqstp, u32 *p, struct nlm_reboot *argp)
+{
+ if (!(p = xdr_decode_string_inplace(p, &argp->mon, &argp->len, SM_MAXSTRLEN)))
+ return 0;
+ argp->state = ntohl(*p++);
+ /* Preserve the address in network byte order */
+ argp->addr = *p++;
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlmsvc_decode_res(struct svc_rqst *rqstp, u32 *p, struct nlm_res *resp)
+{
+ if (!(p = nlm_decode_cookie(p, &resp->cookie)))
+ return 0;
+ resp->status = ntohl(*p++);
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlmsvc_decode_void(struct svc_rqst *rqstp, u32 *p, void *dummy)
+{
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlmsvc_encode_void(struct svc_rqst *rqstp, u32 *p, void *dummy)
+{
+ return xdr_ressize_check(rqstp, p);
+}
+
+/*
+ * Now, the client side XDR functions
+ */
+static int
+nlmclt_encode_void(struct rpc_rqst *req, u32 *p, void *ptr)
+{
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+static int
+nlmclt_decode_void(struct rpc_rqst *req, u32 *p, void *ptr)
+{
+ return 0;
+}
+
+static int
+nlmclt_encode_testargs(struct rpc_rqst *req, u32 *p, nlm_args *argp)
+{
+ struct nlm_lock *lock = &argp->lock;
+
+ if (!(p = nlm_encode_cookie(p, &argp->cookie)))
+ return -EIO;
+ *p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
+ if (!(p = nlm_encode_lock(p, lock)))
+ return -EIO;
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+static int
+nlmclt_decode_testres(struct rpc_rqst *req, u32 *p, struct nlm_res *resp)
+{
+ if (!(p = nlm_decode_cookie(p, &resp->cookie)))
+ return -EIO;
+ resp->status = ntohl(*p++);
+ if (resp->status == NLM_LCK_DENIED) {
+ struct file_lock *fl = &resp->lock.fl;
+ u32 excl;
+ s32 start, len, end;
+
+ memset(&resp->lock, 0, sizeof(resp->lock));
+ locks_init_lock(fl);
+ excl = ntohl(*p++);
+ fl->fl_pid = ntohl(*p++);
+ if (!(p = nlm_decode_oh(p, &resp->lock.oh)))
+ return -EIO;
+
+ fl->fl_flags = FL_POSIX;
+ fl->fl_type = excl? F_WRLCK : F_RDLCK;
+ start = ntohl(*p++);
+ len = ntohl(*p++);
+ end = start + len - 1;
+
+ fl->fl_start = s32_to_loff_t(start);
+ if (len == 0 || end < 0)
+ fl->fl_end = OFFSET_MAX;
+ else
+ fl->fl_end = s32_to_loff_t(end);
+ }
+ return 0;
+}
+
+
+static int
+nlmclt_encode_lockargs(struct rpc_rqst *req, u32 *p, nlm_args *argp)
+{
+ struct nlm_lock *lock = &argp->lock;
+
+ if (!(p = nlm_encode_cookie(p, &argp->cookie)))
+ return -EIO;
+ *p++ = argp->block? xdr_one : xdr_zero;
+ *p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
+ if (!(p = nlm_encode_lock(p, lock)))
+ return -EIO;
+ *p++ = argp->reclaim? xdr_one : xdr_zero;
+ *p++ = htonl(argp->state);
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+static int
+nlmclt_encode_cancargs(struct rpc_rqst *req, u32 *p, nlm_args *argp)
+{
+ struct nlm_lock *lock = &argp->lock;
+
+ if (!(p = nlm_encode_cookie(p, &argp->cookie)))
+ return -EIO;
+ *p++ = argp->block? xdr_one : xdr_zero;
+ *p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
+ if (!(p = nlm_encode_lock(p, lock)))
+ return -EIO;
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+static int
+nlmclt_encode_unlockargs(struct rpc_rqst *req, u32 *p, nlm_args *argp)
+{
+ struct nlm_lock *lock = &argp->lock;
+
+ if (!(p = nlm_encode_cookie(p, &argp->cookie)))
+ return -EIO;
+ if (!(p = nlm_encode_lock(p, lock)))
+ return -EIO;
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+static int
+nlmclt_encode_res(struct rpc_rqst *req, u32 *p, struct nlm_res *resp)
+{
+ if (!(p = nlm_encode_cookie(p, &resp->cookie)))
+ return -EIO;
+ *p++ = resp->status;
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+static int
+nlmclt_encode_testres(struct rpc_rqst *req, u32 *p, struct nlm_res *resp)
+{
+ if (!(p = nlm_encode_testres(p, resp)))
+ return -EIO;
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+static int
+nlmclt_decode_res(struct rpc_rqst *req, u32 *p, struct nlm_res *resp)
+{
+ if (!(p = nlm_decode_cookie(p, &resp->cookie)))
+ return -EIO;
+ resp->status = ntohl(*p++);
+ return 0;
+}
+
+/*
+ * Buffer requirements for NLM
+ */
+#define NLM_void_sz 0
+#define NLM_cookie_sz 1+QUADLEN(NLM_MAXCOOKIELEN)
+#define NLM_caller_sz 1+QUADLEN(sizeof(system_utsname.nodename))
+#define NLM_netobj_sz 1+QUADLEN(XDR_MAX_NETOBJ)
+/* #define NLM_owner_sz 1+QUADLEN(NLM_MAXOWNER) */
+#define NLM_fhandle_sz 1+QUADLEN(NFS2_FHSIZE)
+#define NLM_lock_sz 3+NLM_caller_sz+NLM_netobj_sz+NLM_fhandle_sz
+#define NLM_holder_sz 4+NLM_netobj_sz
+
+#define NLM_testargs_sz NLM_cookie_sz+1+NLM_lock_sz
+#define NLM_lockargs_sz NLM_cookie_sz+4+NLM_lock_sz
+#define NLM_cancargs_sz NLM_cookie_sz+2+NLM_lock_sz
+#define NLM_unlockargs_sz NLM_cookie_sz+NLM_lock_sz
+
+#define NLM_testres_sz NLM_cookie_sz+1+NLM_holder_sz
+#define NLM_res_sz NLM_cookie_sz+1
+#define NLM_norep_sz 0
+
+#ifndef MAX
+# define MAX(a, b) (((a) > (b))? (a) : (b))
+#endif
+
+/*
+ * For NLM, a void procedure really returns nothing
+ */
+#define nlmclt_decode_norep NULL
+
+#define PROC(proc, argtype, restype) \
+ { .p_procname = "nlm_" #proc, \
+ .p_encode = (kxdrproc_t) nlmclt_encode_##argtype, \
+ .p_decode = (kxdrproc_t) nlmclt_decode_##restype, \
+ .p_bufsiz = MAX(NLM_##argtype##_sz, NLM_##restype##_sz) << 2 \
+ }
+
+static struct rpc_procinfo nlm_procedures[] = {
+ PROC(null, void, void),
+ PROC(test, testargs, testres),
+ PROC(lock, lockargs, res),
+ PROC(canc, cancargs, res),
+ PROC(unlock, unlockargs, res),
+ PROC(granted, testargs, res),
+ PROC(test_msg, testargs, norep),
+ PROC(lock_msg, lockargs, norep),
+ PROC(canc_msg, cancargs, norep),
+ PROC(unlock_msg, unlockargs, norep),
+ PROC(granted_msg, testargs, norep),
+ PROC(test_res, testres, norep),
+ PROC(lock_res, res, norep),
+ PROC(canc_res, res, norep),
+ PROC(unlock_res, res, norep),
+ PROC(granted_res, res, norep),
+ PROC(undef, void, void),
+ PROC(undef, void, void),
+ PROC(undef, void, void),
+ PROC(undef, void, void),
+#ifdef NLMCLNT_SUPPORT_SHARES
+ PROC(share, shareargs, shareres),
+ PROC(unshare, shareargs, shareres),
+ PROC(nm_lock, lockargs, res),
+ PROC(free_all, notify, void),
+#else
+ PROC(undef, void, void),
+ PROC(undef, void, void),
+ PROC(undef, void, void),
+ PROC(undef, void, void),
+#endif
+};
+
+static struct rpc_version nlm_version1 = {
+ 1, 16, nlm_procedures,
+};
+
+static struct rpc_version nlm_version3 = {
+ 3, 24, nlm_procedures,
+};
+
+#ifdef CONFIG_LOCKD_V4
+extern struct rpc_version nlm_version4;
+#endif
+
+static struct rpc_version * nlm_versions[] = {
+ NULL,
+ &nlm_version1,
+ NULL,
+ &nlm_version3,
+#ifdef CONFIG_LOCKD_V4
+ &nlm_version4,
+#endif
+};
+
+static struct rpc_stat nlm_stats;
+
+struct rpc_program nlm_program = {
+ "lockd",
+ NLM_PROGRAM,
+ sizeof(nlm_versions) / sizeof(nlm_versions[0]),
+ nlm_versions,
+ &nlm_stats,
+};
+
+#ifdef LOCKD_DEBUG
+char *
+nlm_procname(u32 proc)
+{
+ if (proc < sizeof(nlm_procedures)/sizeof(nlm_procedures[0]))
+ return nlm_procedures[proc].p_procname;
+ return "unknown";
+}
+#endif
+
+#ifdef RPC_DEBUG
+const char *nlmdbg_cookie2a(const struct nlm_cookie *cookie)
+{
+ /*
+ * We can get away with a static buffer because we're only
+ * called with BKL held.
+ */
+ static char buf[2*NLM_MAXCOOKIELEN+1];
+ int i;
+ int len = sizeof(buf);
+ char *p = buf;
+
+ len--; /* allow for trailing \0 */
+ if (len < 3)
+ return "???";
+ for (i = 0 ; i < cookie->len ; i++) {
+ if (len < 2) {
+ strcpy(p-3, "...");
+ break;
+ }
+ sprintf(p, "%02x", cookie->data[i]);
+ p += 2;
+ len -= 2;
+ }
+ *p = '\0';
+
+ return buf;
+}
+#endif
diff --git a/fs/lockd/xdr4.c b/fs/lockd/xdr4.c
new file mode 100644
index 00000000000000..5c336fdb341dad
--- /dev/null
+++ b/fs/lockd/xdr4.c
@@ -0,0 +1,612 @@
+/*
+ * linux/fs/lockd/xdr4.c
+ *
+ * XDR support for lockd and the lock client.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ * Copyright (C) 1999, Trond Myklebust <trond.myklebust@fys.uio.no>
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/utsname.h>
+#include <linux/nfs.h>
+
+#include <linux/sunrpc/xdr.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/sunrpc/stats.h>
+#include <linux/lockd/lockd.h>
+#include <linux/lockd/sm_inter.h>
+
+#define NLMDBG_FACILITY NLMDBG_XDR
+
+static inline loff_t
+s64_to_loff_t(__s64 offset)
+{
+ return (loff_t)offset;
+}
+
+
+static inline s64
+loff_t_to_s64(loff_t offset)
+{
+ s64 res;
+ if (offset > NLM4_OFFSET_MAX)
+ res = NLM4_OFFSET_MAX;
+ else if (offset < -NLM4_OFFSET_MAX)
+ res = -NLM4_OFFSET_MAX;
+ else
+ res = offset;
+ return res;
+}
+
+/*
+ * XDR functions for basic NLM types
+ */
+static u32 *
+nlm4_decode_cookie(u32 *p, struct nlm_cookie *c)
+{
+ unsigned int len;
+
+ len = ntohl(*p++);
+
+ if(len==0)
+ {
+ c->len=4;
+ memset(c->data, 0, 4); /* hockeypux brain damage */
+ }
+ else if(len<=NLM_MAXCOOKIELEN)
+ {
+ c->len=len;
+ memcpy(c->data, p, len);
+ p+=(len+3)>>2;
+ }
+ else
+ {
+ printk(KERN_NOTICE
+ "lockd: bad cookie size %d (only cookies under %d bytes are supported.)\n", len, NLM_MAXCOOKIELEN);
+ return NULL;
+ }
+ return p;
+}
+
+static u32 *
+nlm4_encode_cookie(u32 *p, struct nlm_cookie *c)
+{
+ *p++ = htonl(c->len);
+ memcpy(p, c->data, c->len);
+ p+=(c->len+3)>>2;
+ return p;
+}
+
+static u32 *
+nlm4_decode_fh(u32 *p, struct nfs_fh *f)
+{
+ memset(f->data, 0, sizeof(f->data));
+ f->size = ntohl(*p++);
+ if (f->size > NFS_MAXFHSIZE) {
+ printk(KERN_NOTICE
+ "lockd: bad fhandle size %d (should be <=%d)\n",
+ f->size, NFS_MAXFHSIZE);
+ return NULL;
+ }
+ memcpy(f->data, p, f->size);
+ return p + XDR_QUADLEN(f->size);
+}
+
+static u32 *
+nlm4_encode_fh(u32 *p, struct nfs_fh *f)
+{
+ *p++ = htonl(f->size);
+ if (f->size) p[XDR_QUADLEN(f->size)-1] = 0; /* don't leak anything */
+ memcpy(p, f->data, f->size);
+ return p + XDR_QUADLEN(f->size);
+}
+
+/*
+ * Encode and decode owner handle
+ */
+static u32 *
+nlm4_decode_oh(u32 *p, struct xdr_netobj *oh)
+{
+ return xdr_decode_netobj(p, oh);
+}
+
+static u32 *
+nlm4_encode_oh(u32 *p, struct xdr_netobj *oh)
+{
+ return xdr_encode_netobj(p, oh);
+}
+
+static u32 *
+nlm4_decode_lock(u32 *p, struct nlm_lock *lock)
+{
+ struct file_lock *fl = &lock->fl;
+ __s64 len, start, end;
+
+ if (!(p = xdr_decode_string_inplace(p, &lock->caller,
+ &lock->len, NLM_MAXSTRLEN))
+ || !(p = nlm4_decode_fh(p, &lock->fh))
+ || !(p = nlm4_decode_oh(p, &lock->oh)))
+ return NULL;
+
+ locks_init_lock(fl);
+ fl->fl_owner = current->files;
+ fl->fl_pid = ntohl(*p++);
+ fl->fl_flags = FL_POSIX;
+ fl->fl_type = F_RDLCK; /* as good as anything else */
+ p = xdr_decode_hyper(p, &start);
+ p = xdr_decode_hyper(p, &len);
+ end = start + len - 1;
+
+ fl->fl_start = s64_to_loff_t(start);
+
+ if (len == 0 || end < 0)
+ fl->fl_end = OFFSET_MAX;
+ else
+ fl->fl_end = s64_to_loff_t(end);
+ return p;
+}
+
+/*
+ * Encode a lock as part of an NLM call
+ */
+static u32 *
+nlm4_encode_lock(u32 *p, struct nlm_lock *lock)
+{
+ struct file_lock *fl = &lock->fl;
+ __s64 start, len;
+
+ if (!(p = xdr_encode_string(p, lock->caller))
+ || !(p = nlm4_encode_fh(p, &lock->fh))
+ || !(p = nlm4_encode_oh(p, &lock->oh)))
+ return NULL;
+
+ if (fl->fl_start > NLM4_OFFSET_MAX
+ || (fl->fl_end > NLM4_OFFSET_MAX && fl->fl_end != OFFSET_MAX))
+ return NULL;
+
+ *p++ = htonl(fl->fl_pid);
+
+ start = loff_t_to_s64(fl->fl_start);
+ if (fl->fl_end == OFFSET_MAX)
+ len = 0;
+ else
+ len = loff_t_to_s64(fl->fl_end - fl->fl_start + 1);
+
+ p = xdr_encode_hyper(p, start);
+ p = xdr_encode_hyper(p, len);
+
+ return p;
+}
+
+/*
+ * Encode result of a TEST/TEST_MSG call
+ */
+static u32 *
+nlm4_encode_testres(u32 *p, struct nlm_res *resp)
+{
+ s64 start, len;
+
+ dprintk("xdr: before encode_testres (p %p resp %p)\n", p, resp);
+ if (!(p = nlm4_encode_cookie(p, &resp->cookie)))
+ return 0;
+ *p++ = resp->status;
+
+ if (resp->status == nlm_lck_denied) {
+ struct file_lock *fl = &resp->lock.fl;
+
+ *p++ = (fl->fl_type == F_RDLCK)? xdr_zero : xdr_one;
+ *p++ = htonl(fl->fl_pid);
+
+ /* Encode owner handle. */
+ if (!(p = xdr_encode_netobj(p, &resp->lock.oh)))
+ return 0;
+
+ start = loff_t_to_s64(fl->fl_start);
+ if (fl->fl_end == OFFSET_MAX)
+ len = 0;
+ else
+ len = loff_t_to_s64(fl->fl_end - fl->fl_start + 1);
+
+ p = xdr_encode_hyper(p, start);
+ p = xdr_encode_hyper(p, len);
+ dprintk("xdr: encode_testres (status %d pid %d type %d start %Ld end %Ld)\n",
+ resp->status, fl->fl_pid, fl->fl_type,
+ (long long)fl->fl_start, (long long)fl->fl_end);
+ }
+
+ dprintk("xdr: after encode_testres (p %p resp %p)\n", p, resp);
+ return p;
+}
+
+
+/*
+ * Check buffer bounds after decoding arguments
+ */
+static int
+xdr_argsize_check(struct svc_rqst *rqstp, u32 *p)
+{
+ struct svc_buf *buf = &rqstp->rq_argbuf;
+
+ return p - buf->base <= buf->buflen;
+}
+
+static int
+xdr_ressize_check(struct svc_rqst *rqstp, u32 *p)
+{
+ struct svc_buf *buf = &rqstp->rq_resbuf;
+
+ buf->len = p - buf->base;
+ return (buf->len <= buf->buflen);
+}
+
+/*
+ * First, the server side XDR functions
+ */
+int
+nlm4svc_decode_testargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp)
+{
+ u32 exclusive;
+
+ if (!(p = nlm4_decode_cookie(p, &argp->cookie)))
+ return 0;
+
+ exclusive = ntohl(*p++);
+ if (!(p = nlm4_decode_lock(p, &argp->lock)))
+ return 0;
+ if (exclusive)
+ argp->lock.fl.fl_type = F_WRLCK;
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlm4svc_encode_testres(struct svc_rqst *rqstp, u32 *p, struct nlm_res *resp)
+{
+ if (!(p = nlm4_encode_testres(p, resp)))
+ return 0;
+ return xdr_ressize_check(rqstp, p);
+}
+
+int
+nlm4svc_decode_lockargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp)
+{
+ u32 exclusive;
+
+ if (!(p = nlm4_decode_cookie(p, &argp->cookie)))
+ return 0;
+ argp->block = ntohl(*p++);
+ exclusive = ntohl(*p++);
+ if (!(p = nlm4_decode_lock(p, &argp->lock)))
+ return 0;
+ if (exclusive)
+ argp->lock.fl.fl_type = F_WRLCK;
+ argp->reclaim = ntohl(*p++);
+ argp->state = ntohl(*p++);
+ argp->monitor = 1; /* monitor client by default */
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlm4svc_decode_cancargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp)
+{
+ u32 exclusive;
+
+ if (!(p = nlm4_decode_cookie(p, &argp->cookie)))
+ return 0;
+ argp->block = ntohl(*p++);
+ exclusive = ntohl(*p++);
+ if (!(p = nlm4_decode_lock(p, &argp->lock)))
+ return 0;
+ if (exclusive)
+ argp->lock.fl.fl_type = F_WRLCK;
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlm4svc_decode_unlockargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp)
+{
+ if (!(p = nlm4_decode_cookie(p, &argp->cookie))
+ || !(p = nlm4_decode_lock(p, &argp->lock)))
+ return 0;
+ argp->lock.fl.fl_type = F_UNLCK;
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlm4svc_decode_shareargs(struct svc_rqst *rqstp, u32 *p, nlm_args *argp)
+{
+ struct nlm_lock *lock = &argp->lock;
+
+ memset(lock, 0, sizeof(*lock));
+ locks_init_lock(&lock->fl);
+ lock->fl.fl_pid = ~(u32) 0;
+
+ if (!(p = nlm4_decode_cookie(p, &argp->cookie))
+ || !(p = xdr_decode_string_inplace(p, &lock->caller,
+ &lock->len, NLM_MAXSTRLEN))
+ || !(p = nlm4_decode_fh(p, &lock->fh))
+ || !(p = nlm4_decode_oh(p, &lock->oh)))
+ return 0;
+ argp->fsm_mode = ntohl(*p++);
+ argp->fsm_access = ntohl(*p++);
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlm4svc_encode_shareres(struct svc_rqst *rqstp, u32 *p, struct nlm_res *resp)
+{
+ if (!(p = nlm4_encode_cookie(p, &resp->cookie)))
+ return 0;
+ *p++ = resp->status;
+ *p++ = xdr_zero; /* sequence argument */
+ return xdr_ressize_check(rqstp, p);
+}
+
+int
+nlm4svc_encode_res(struct svc_rqst *rqstp, u32 *p, struct nlm_res *resp)
+{
+ if (!(p = nlm4_encode_cookie(p, &resp->cookie)))
+ return 0;
+ *p++ = resp->status;
+ return xdr_ressize_check(rqstp, p);
+}
+
+int
+nlm4svc_decode_notify(struct svc_rqst *rqstp, u32 *p, struct nlm_args *argp)
+{
+ struct nlm_lock *lock = &argp->lock;
+
+ if (!(p = xdr_decode_string_inplace(p, &lock->caller,
+ &lock->len, NLM_MAXSTRLEN)))
+ return 0;
+ argp->state = ntohl(*p++);
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlm4svc_decode_reboot(struct svc_rqst *rqstp, u32 *p, struct nlm_reboot *argp)
+{
+ if (!(p = xdr_decode_string_inplace(p, &argp->mon, &argp->len, SM_MAXSTRLEN)))
+ return 0;
+ argp->state = ntohl(*p++);
+ /* Preserve the address in network byte order */
+ argp->addr = *p++;
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlm4svc_decode_res(struct svc_rqst *rqstp, u32 *p, struct nlm_res *resp)
+{
+ if (!(p = nlm4_decode_cookie(p, &resp->cookie)))
+ return 0;
+ resp->status = ntohl(*p++);
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlm4svc_decode_void(struct svc_rqst *rqstp, u32 *p, void *dummy)
+{
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nlm4svc_encode_void(struct svc_rqst *rqstp, u32 *p, void *dummy)
+{
+ return xdr_ressize_check(rqstp, p);
+}
+
+/*
+ * Now, the client side XDR functions
+ */
+static int
+nlm4clt_encode_void(struct rpc_rqst *req, u32 *p, void *ptr)
+{
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+static int
+nlm4clt_decode_void(struct rpc_rqst *req, u32 *p, void *ptr)
+{
+ return 0;
+}
+
+static int
+nlm4clt_encode_testargs(struct rpc_rqst *req, u32 *p, nlm_args *argp)
+{
+ struct nlm_lock *lock = &argp->lock;
+
+ if (!(p = nlm4_encode_cookie(p, &argp->cookie)))
+ return -EIO;
+ *p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
+ if (!(p = nlm4_encode_lock(p, lock)))
+ return -EIO;
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+static int
+nlm4clt_decode_testres(struct rpc_rqst *req, u32 *p, struct nlm_res *resp)
+{
+ if (!(p = nlm4_decode_cookie(p, &resp->cookie)))
+ return -EIO;
+ resp->status = ntohl(*p++);
+ if (resp->status == NLM_LCK_DENIED) {
+ struct file_lock *fl = &resp->lock.fl;
+ u32 excl;
+ s64 start, end, len;
+
+ memset(&resp->lock, 0, sizeof(resp->lock));
+ locks_init_lock(fl);
+ excl = ntohl(*p++);
+ fl->fl_pid = ntohl(*p++);
+ if (!(p = nlm4_decode_oh(p, &resp->lock.oh)))
+ return -EIO;
+
+ fl->fl_flags = FL_POSIX;
+ fl->fl_type = excl? F_WRLCK : F_RDLCK;
+ p = xdr_decode_hyper(p, &start);
+ p = xdr_decode_hyper(p, &len);
+ end = start + len - 1;
+
+ fl->fl_start = s64_to_loff_t(start);
+ if (len == 0 || end < 0)
+ fl->fl_end = OFFSET_MAX;
+ else
+ fl->fl_end = s64_to_loff_t(end);
+ }
+ return 0;
+}
+
+
+static int
+nlm4clt_encode_lockargs(struct rpc_rqst *req, u32 *p, nlm_args *argp)
+{
+ struct nlm_lock *lock = &argp->lock;
+
+ if (!(p = nlm4_encode_cookie(p, &argp->cookie)))
+ return -EIO;
+ *p++ = argp->block? xdr_one : xdr_zero;
+ *p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
+ if (!(p = nlm4_encode_lock(p, lock)))
+ return -EIO;
+ *p++ = argp->reclaim? xdr_one : xdr_zero;
+ *p++ = htonl(argp->state);
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+static int
+nlm4clt_encode_cancargs(struct rpc_rqst *req, u32 *p, nlm_args *argp)
+{
+ struct nlm_lock *lock = &argp->lock;
+
+ if (!(p = nlm4_encode_cookie(p, &argp->cookie)))
+ return -EIO;
+ *p++ = argp->block? xdr_one : xdr_zero;
+ *p++ = (lock->fl.fl_type == F_WRLCK)? xdr_one : xdr_zero;
+ if (!(p = nlm4_encode_lock(p, lock)))
+ return -EIO;
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+static int
+nlm4clt_encode_unlockargs(struct rpc_rqst *req, u32 *p, nlm_args *argp)
+{
+ struct nlm_lock *lock = &argp->lock;
+
+ if (!(p = nlm4_encode_cookie(p, &argp->cookie)))
+ return -EIO;
+ if (!(p = nlm4_encode_lock(p, lock)))
+ return -EIO;
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+static int
+nlm4clt_encode_res(struct rpc_rqst *req, u32 *p, struct nlm_res *resp)
+{
+ if (!(p = nlm4_encode_cookie(p, &resp->cookie)))
+ return -EIO;
+ *p++ = resp->status;
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+static int
+nlm4clt_encode_testres(struct rpc_rqst *req, u32 *p, struct nlm_res *resp)
+{
+ if (!(p = nlm4_encode_testres(p, resp)))
+ return -EIO;
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+static int
+nlm4clt_decode_res(struct rpc_rqst *req, u32 *p, struct nlm_res *resp)
+{
+ if (!(p = nlm4_decode_cookie(p, &resp->cookie)))
+ return -EIO;
+ resp->status = ntohl(*p++);
+ return 0;
+}
+
+/*
+ * Buffer requirements for NLM
+ */
+#define NLM4_void_sz 0
+#define NLM4_cookie_sz 1+XDR_QUADLEN(NLM_MAXCOOKIELEN)
+#define NLM4_caller_sz 1+XDR_QUADLEN(NLM_MAXSTRLEN)
+#define NLM4_netobj_sz 1+XDR_QUADLEN(XDR_MAX_NETOBJ)
+/* #define NLM4_owner_sz 1+XDR_QUADLEN(NLM4_MAXOWNER) */
+#define NLM4_fhandle_sz 1+XDR_QUADLEN(NFS3_FHSIZE)
+#define NLM4_lock_sz 5+NLM4_caller_sz+NLM4_netobj_sz+NLM4_fhandle_sz
+#define NLM4_holder_sz 6+NLM4_netobj_sz
+
+#define NLM4_testargs_sz NLM4_cookie_sz+1+NLM4_lock_sz
+#define NLM4_lockargs_sz NLM4_cookie_sz+4+NLM4_lock_sz
+#define NLM4_cancargs_sz NLM4_cookie_sz+2+NLM4_lock_sz
+#define NLM4_unlockargs_sz NLM4_cookie_sz+NLM4_lock_sz
+
+#define NLM4_testres_sz NLM4_cookie_sz+1+NLM4_holder_sz
+#define NLM4_res_sz NLM4_cookie_sz+1
+#define NLM4_norep_sz 0
+
+#ifndef MAX
+# define MAX(a,b) (((a) > (b))? (a) : (b))
+#endif
+
+/*
+ * For NLM, a void procedure really returns nothing
+ */
+#define nlm4clt_decode_norep NULL
+
+#define PROC(proc, argtype, restype) \
+ { .p_procname = "nlm4_" #proc, \
+ .p_encode = (kxdrproc_t) nlm4clt_encode_##argtype, \
+ .p_decode = (kxdrproc_t) nlm4clt_decode_##restype, \
+ .p_bufsiz = MAX(NLM4_##argtype##_sz, NLM4_##restype##_sz) << 2 \
+ }
+
+static struct rpc_procinfo nlm4_procedures[] = {
+ PROC(null, void, void),
+ PROC(test, testargs, testres),
+ PROC(lock, lockargs, res),
+ PROC(canc, cancargs, res),
+ PROC(unlock, unlockargs, res),
+ PROC(granted, testargs, res),
+ PROC(test_msg, testargs, norep),
+ PROC(lock_msg, lockargs, norep),
+ PROC(canc_msg, cancargs, norep),
+ PROC(unlock_msg, unlockargs, norep),
+ PROC(granted_msg, testargs, norep),
+ PROC(test_res, testres, norep),
+ PROC(lock_res, res, norep),
+ PROC(canc_res, res, norep),
+ PROC(unlock_res, res, norep),
+ PROC(granted_res, res, norep),
+ PROC(undef, void, void),
+ PROC(undef, void, void),
+ PROC(undef, void, void),
+ PROC(undef, void, void),
+#ifdef NLMCLNT_SUPPORT_SHARES
+ PROC(share, shareargs, shareres),
+ PROC(unshare, shareargs, shareres),
+ PROC(nm_lock, lockargs, res),
+ PROC(free_all, notify, void),
+#else
+ PROC(undef, void, void),
+ PROC(undef, void, void),
+ PROC(undef, void, void),
+ PROC(undef, void, void),
+#endif
+};
+
+struct rpc_version nlm_version4 = {
+ 4, 24, nlm4_procedures,
+};
diff --git a/fs/locks.c b/fs/locks.c
new file mode 100644
index 00000000000000..370ed4cc4ebb1b
--- /dev/null
+++ b/fs/locks.c
@@ -0,0 +1,2042 @@
+#define MSNFS /* HACK HACK */
+/*
+ * linux/fs/locks.c
+ *
+ * Provide support for fcntl()'s F_GETLK, F_SETLK, and F_SETLKW calls.
+ * Doug Evans (dje@spiff.uucp), August 07, 1992
+ *
+ * Deadlock detection added.
+ * FIXME: one thing isn't handled yet:
+ * - mandatory locks (requires lots of changes elsewhere)
+ * Kelly Carmichael (kelly@[142.24.8.65]), September 17, 1994.
+ *
+ * Miscellaneous edits, and a total rewrite of posix_lock_file() code.
+ * Kai Petzke (wpp@marie.physik.tu-berlin.de), 1994
+ *
+ * Converted file_lock_table to a linked list from an array, which eliminates
+ * the limits on how many active file locks are open.
+ * Chad Page (pageone@netcom.com), November 27, 1994
+ *
+ * Removed dependency on file descriptors. dup()'ed file descriptors now
+ * get the same locks as the original file descriptors, and a close() on
+ * any file descriptor removes ALL the locks on the file for the current
+ * process. Since locks still depend on the process id, locks are inherited
+ * after an exec() but not after a fork(). This agrees with POSIX, and both
+ * BSD and SVR4 practice.
+ * Andy Walker (andy@lysaker.kvaerner.no), February 14, 1995
+ *
+ * Scrapped free list which is redundant now that we allocate locks
+ * dynamically with kmalloc()/kfree().
+ * Andy Walker (andy@lysaker.kvaerner.no), February 21, 1995
+ *
+ * Implemented two lock personalities - FL_FLOCK and FL_POSIX.
+ *
+ * FL_POSIX locks are created with calls to fcntl() and lockf() through the
+ * fcntl() system call. They have the semantics described above.
+ *
+ * FL_FLOCK locks are created with calls to flock(), through the flock()
+ * system call, which is new. Old C libraries implement flock() via fcntl()
+ * and will continue to use the old, broken implementation.
+ *
+ * FL_FLOCK locks follow the 4.4 BSD flock() semantics. They are associated
+ * with a file pointer (filp). As a result they can be shared by a parent
+ * process and its children after a fork(). They are removed when the last
+ * file descriptor referring to the file pointer is closed (unless explicitly
+ * unlocked).
+ *
+ * FL_FLOCK locks never deadlock, an existing lock is always removed before
+ * upgrading from shared to exclusive (or vice versa). When this happens
+ * any processes blocked by the current lock are woken up and allowed to
+ * run before the new lock is applied.
+ * Andy Walker (andy@lysaker.kvaerner.no), June 09, 1995
+ *
+ * Removed some race conditions in flock_lock_file(), marked other possible
+ * races. Just grep for FIXME to see them.
+ * Dmitry Gorodchanin (pgmdsg@ibi.com), February 09, 1996.
+ *
+ * Addressed Dmitry's concerns. Deadlock checking no longer recursive.
+ * Lock allocation changed to GFP_ATOMIC as we can't afford to sleep
+ * once we've checked for blocking and deadlocking.
+ * Andy Walker (andy@lysaker.kvaerner.no), April 03, 1996.
+ *
+ * Initial implementation of mandatory locks. SunOS turned out to be
+ * a rotten model, so I implemented the "obvious" semantics.
+ * See 'linux/Documentation/mandatory.txt' for details.
+ * Andy Walker (andy@lysaker.kvaerner.no), April 06, 1996.
+ *
+ * Don't allow mandatory locks on mmap()'ed files. Added simple functions to
+ * check if a file has mandatory locks, used by mmap(), open() and creat() to
+ * see if system call should be rejected. Ref. HP-UX/SunOS/Solaris Reference
+ * Manual, Section 2.
+ * Andy Walker (andy@lysaker.kvaerner.no), April 09, 1996.
+ *
+ * Tidied up block list handling. Added '/proc/locks' interface.
+ * Andy Walker (andy@lysaker.kvaerner.no), April 24, 1996.
+ *
+ * Fixed deadlock condition for pathological code that mixes calls to
+ * flock() and fcntl().
+ * Andy Walker (andy@lysaker.kvaerner.no), April 29, 1996.
+ *
+ * Allow only one type of locking scheme (FL_POSIX or FL_FLOCK) to be in use
+ * for a given file at a time. Changed the CONFIG_LOCK_MANDATORY scheme to
+ * guarantee sensible behaviour in the case where file system modules might
+ * be compiled with different options than the kernel itself.
+ * Andy Walker (andy@lysaker.kvaerner.no), May 15, 1996.
+ *
+ * Added a couple of missing wake_up() calls. Thanks to Thomas Meckel
+ * (Thomas.Meckel@mni.fh-giessen.de) for spotting this.
+ * Andy Walker (andy@lysaker.kvaerner.no), May 15, 1996.
+ *
+ * Changed FL_POSIX locks to use the block list in the same way as FL_FLOCK
+ * locks. Changed process synchronisation to avoid dereferencing locks that
+ * have already been freed.
+ * Andy Walker (andy@lysaker.kvaerner.no), Sep 21, 1996.
+ *
+ * Made the block list a circular list to minimise searching in the list.
+ * Andy Walker (andy@lysaker.kvaerner.no), Sep 25, 1996.
+ *
+ * Made mandatory locking a mount option. Default is not to allow mandatory
+ * locking.
+ * Andy Walker (andy@lysaker.kvaerner.no), Oct 04, 1996.
+ *
+ * Some adaptations for NFS support.
+ * Olaf Kirch (okir@monad.swb.de), Dec 1996,
+ *
+ * Fixed /proc/locks interface so that we can't overrun the buffer we are handed.
+ * Andy Walker (andy@lysaker.kvaerner.no), May 12, 1997.
+ *
+ * Use slab allocator instead of kmalloc/kfree.
+ * Use generic list implementation from <linux/list.h>.
+ * Sped up posix_locks_deadlock by only considering blocked locks.
+ * Matthew Wilcox <willy@thepuffingroup.com>, March, 2000.
+ *
+ * Leases and LOCK_MAND
+ * Matthew Wilcox <willy@linuxcare.com>, June, 2000.
+ * Stephen Rothwell <sfr@canb.auug.org.au>, June, 2000.
+ */
+
+#include <linux/slab.h>
+#include <linux/file.h>
+#include <linux/smp_lock.h>
+#include <linux/init.h>
+#include <linux/capability.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+
+int leases_enable = 1;
+int lease_break_time = 45;
+
+LIST_HEAD(file_lock_list);
+static LIST_HEAD(blocked_list);
+
+static kmem_cache_t *filelock_cache;
+
+/* Allocate an empty lock structure. */
+static struct file_lock *locks_alloc_lock(void)
+{
+ return kmem_cache_alloc(filelock_cache, SLAB_KERNEL);
+}
+
+/* Free a lock which is not in use. */
+static inline void locks_free_lock(struct file_lock *fl)
+{
+ if (fl == NULL) {
+ BUG();
+ return;
+ }
+ if (waitqueue_active(&fl->fl_wait))
+ panic("Attempting to free lock with active wait queue");
+
+ if (!list_empty(&fl->fl_block))
+ panic("Attempting to free lock with active block list");
+
+ if (!list_empty(&fl->fl_link))
+ panic("Attempting to free lock on active lock list");
+
+ kmem_cache_free(filelock_cache, fl);
+}
+
+void locks_init_lock(struct file_lock *fl)
+{
+ INIT_LIST_HEAD(&fl->fl_link);
+ INIT_LIST_HEAD(&fl->fl_block);
+ init_waitqueue_head(&fl->fl_wait);
+ fl->fl_next = NULL;
+ fl->fl_fasync = NULL;
+ fl->fl_owner = 0;
+ fl->fl_pid = 0;
+ fl->fl_file = NULL;
+ fl->fl_flags = 0;
+ fl->fl_type = 0;
+ fl->fl_start = fl->fl_end = 0;
+ fl->fl_notify = NULL;
+ fl->fl_insert = NULL;
+ fl->fl_remove = NULL;
+}
+
+/*
+ * Initialises the fields of the file lock which are invariant for
+ * free file_locks.
+ */
+static void init_once(void *foo, kmem_cache_t *cache, unsigned long flags)
+{
+ struct file_lock *lock = (struct file_lock *) foo;
+
+ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) !=
+ SLAB_CTOR_CONSTRUCTOR)
+ return;
+
+ locks_init_lock(lock);
+}
+
+/*
+ * Initialize a new lock from an existing file_lock structure.
+ */
+void locks_copy_lock(struct file_lock *new, struct file_lock *fl)
+{
+ new->fl_owner = fl->fl_owner;
+ new->fl_pid = fl->fl_pid;
+ new->fl_file = fl->fl_file;
+ new->fl_flags = fl->fl_flags;
+ new->fl_type = fl->fl_type;
+ new->fl_start = fl->fl_start;
+ new->fl_end = fl->fl_end;
+ new->fl_notify = fl->fl_notify;
+ new->fl_insert = fl->fl_insert;
+ new->fl_remove = fl->fl_remove;
+ new->fl_u = fl->fl_u;
+}
+
+/* Fill in a file_lock structure with an appropriate FLOCK lock. */
+static struct file_lock *flock_make_lock(struct file *filp, unsigned int type)
+{
+ struct file_lock *fl = locks_alloc_lock();
+ if (fl == NULL)
+ return NULL;
+
+ fl->fl_owner = NULL;
+ fl->fl_file = filp;
+ fl->fl_pid = current->pid;
+ fl->fl_flags = FL_FLOCK;
+ fl->fl_type = type;
+ fl->fl_start = 0;
+ fl->fl_end = OFFSET_MAX;
+ fl->fl_notify = NULL;
+ fl->fl_insert = NULL;
+ fl->fl_remove = NULL;
+
+ return fl;
+}
+
+static int assign_type(struct file_lock *fl, int type)
+{
+ switch (type) {
+ case F_RDLCK:
+ case F_WRLCK:
+ case F_UNLCK:
+ fl->fl_type = type;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX
+ * style lock.
+ */
+static int flock_to_posix_lock(struct file *filp, struct file_lock *fl,
+ struct flock *l)
+{
+ off_t start, end;
+
+ switch (l->l_whence) {
+ case 0: /*SEEK_SET*/
+ start = 0;
+ break;
+ case 1: /*SEEK_CUR*/
+ start = filp->f_pos;
+ break;
+ case 2: /*SEEK_END*/
+ start = filp->f_dentry->d_inode->i_size;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* POSIX-1996 leaves the case l->l_len < 0 undefined;
+ POSIX-2001 defines it. */
+ start += l->l_start;
+ if (l->l_len < 0) {
+ end = start - 1;
+ start += l->l_len;
+ } else {
+ end = start + l->l_len - 1;
+ }
+
+ if (start < 0)
+ return -EINVAL;
+ if (l->l_len > 0 && end < 0)
+ return -EOVERFLOW;
+ fl->fl_start = start; /* we record the absolute position */
+ fl->fl_end = end;
+ if (l->l_len == 0)
+ fl->fl_end = OFFSET_MAX;
+
+ fl->fl_owner = current->files;
+ fl->fl_pid = current->pid;
+ fl->fl_file = filp;
+ fl->fl_flags = FL_POSIX;
+ fl->fl_notify = NULL;
+ fl->fl_insert = NULL;
+ fl->fl_remove = NULL;
+
+ return assign_type(fl, l->l_type);
+}
+
+#if BITS_PER_LONG == 32
+static int flock64_to_posix_lock(struct file *filp, struct file_lock *fl,
+ struct flock64 *l)
+{
+ loff_t start;
+
+ switch (l->l_whence) {
+ case 0: /*SEEK_SET*/
+ start = 0;
+ break;
+ case 1: /*SEEK_CUR*/
+ start = filp->f_pos;
+ break;
+ case 2: /*SEEK_END*/
+ start = filp->f_dentry->d_inode->i_size;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (((start += l->l_start) < 0) || (l->l_len < 0))
+ return -EINVAL;
+ fl->fl_end = start + l->l_len - 1;
+ if (l->l_len > 0 && fl->fl_end < 0)
+ return -EOVERFLOW;
+ fl->fl_start = start; /* we record the absolute position */
+ if (l->l_len == 0)
+ fl->fl_end = OFFSET_MAX;
+
+ fl->fl_owner = current->files;
+ fl->fl_pid = current->pid;
+ fl->fl_file = filp;
+ fl->fl_flags = FL_POSIX;
+ fl->fl_notify = NULL;
+ fl->fl_insert = NULL;
+ fl->fl_remove = NULL;
+
+ switch (l->l_type) {
+ case F_RDLCK:
+ case F_WRLCK:
+ case F_UNLCK:
+ fl->fl_type = l->l_type;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return (0);
+}
+#endif
+
+/* Allocate a file_lock initialised to this type of lease */
+static int lease_alloc(struct file *filp, int type, struct file_lock **flp)
+{
+ struct file_lock *fl = locks_alloc_lock();
+ if (fl == NULL)
+ return -ENOMEM;
+
+ fl->fl_owner = current->files;
+ fl->fl_pid = current->pid;
+
+ fl->fl_file = filp;
+ fl->fl_flags = FL_LEASE;
+ if (assign_type(fl, type) != 0) {
+ locks_free_lock(fl);
+ return -EINVAL;
+ }
+ fl->fl_start = 0;
+ fl->fl_end = OFFSET_MAX;
+ fl->fl_notify = NULL;
+ fl->fl_insert = NULL;
+ fl->fl_remove = NULL;
+
+ *flp = fl;
+ return 0;
+}
+
+/* Check if two locks overlap each other.
+ */
+static inline int locks_overlap(struct file_lock *fl1, struct file_lock *fl2)
+{
+ return ((fl1->fl_end >= fl2->fl_start) &&
+ (fl2->fl_end >= fl1->fl_start));
+}
+
+/*
+ * Check whether two locks have the same owner
+ * N.B. Do we need the test on PID as well as owner?
+ * (Clone tasks should be considered as one "owner".)
+ */
+static inline int
+locks_same_owner(struct file_lock *fl1, struct file_lock *fl2)
+{
+ return (fl1->fl_owner == fl2->fl_owner) &&
+ (fl1->fl_pid == fl2->fl_pid);
+}
+
+/* Remove waiter from blocker's block list.
+ * When blocker ends up pointing to itself then the list is empty.
+ */
+static void locks_delete_block(struct file_lock *waiter)
+{
+ list_del(&waiter->fl_block);
+ INIT_LIST_HEAD(&waiter->fl_block);
+ list_del(&waiter->fl_link);
+ INIT_LIST_HEAD(&waiter->fl_link);
+ waiter->fl_next = NULL;
+}
+
+/* Insert waiter into blocker's block list.
+ * We use a circular list so that processes can be easily woken up in
+ * the order they blocked. The documentation doesn't require this but
+ * it seems like the reasonable thing to do.
+ */
+static void locks_insert_block(struct file_lock *blocker,
+ struct file_lock *waiter)
+{
+ if (!list_empty(&waiter->fl_block)) {
+ printk(KERN_ERR "locks_insert_block: removing duplicated lock "
+ "(pid=%d %Ld-%Ld type=%d)\n", waiter->fl_pid,
+ waiter->fl_start, waiter->fl_end, waiter->fl_type);
+ locks_delete_block(waiter);
+ }
+ list_add_tail(&waiter->fl_block, &blocker->fl_block);
+ waiter->fl_next = blocker;
+ list_add(&waiter->fl_link, &blocked_list);
+}
+
+static inline
+void locks_notify_blocked(struct file_lock *waiter)
+{
+ if (waiter->fl_notify)
+ waiter->fl_notify(waiter);
+ else
+ wake_up(&waiter->fl_wait);
+}
+
+/* Wake up processes blocked waiting for blocker.
+ * If told to wait then schedule the processes until the block list
+ * is empty, otherwise empty the block list ourselves.
+ */
+static void locks_wake_up_blocks(struct file_lock *blocker, unsigned int wait)
+{
+ while (!list_empty(&blocker->fl_block)) {
+ struct file_lock *waiter = list_entry(blocker->fl_block.next, struct file_lock, fl_block);
+
+ if (wait) {
+ locks_notify_blocked(waiter);
+ /* Let the blocked process remove waiter from the
+ * block list when it gets scheduled.
+ */
+ yield();
+ } else {
+ /* Remove waiter from the block list, because by the
+ * time it wakes up blocker won't exist any more.
+ */
+ locks_delete_block(waiter);
+ locks_notify_blocked(waiter);
+ }
+ }
+}
+
+/* Insert file lock fl into an inode's lock list at the position indicated
+ * by pos. At the same time add the lock to the global file lock list.
+ */
+static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl)
+{
+ list_add(&fl->fl_link, &file_lock_list);
+
+ /* insert into file's list */
+ fl->fl_next = *pos;
+ *pos = fl;
+
+ if (fl->fl_insert)
+ fl->fl_insert(fl);
+}
+
+/*
+ * Remove lock from the lock lists
+ */
+static inline void _unhash_lock(struct file_lock **thisfl_p)
+{
+ struct file_lock *fl = *thisfl_p;
+
+ *thisfl_p = fl->fl_next;
+ fl->fl_next = NULL;
+
+ list_del_init(&fl->fl_link);
+}
+
+/*
+ * Wake up processes that are blocked waiting for this lock,
+ * notify the FS that the lock has been cleared and
+ * finally free the lock.
+ */
+static inline void _delete_lock(struct file_lock *fl, unsigned int wait)
+{
+ fasync_helper(0, fl->fl_file, 0, &fl->fl_fasync);
+ if (fl->fl_fasync != NULL){
+ printk(KERN_ERR "locks_delete_lock: fasync == %p\n", fl->fl_fasync);
+ fl->fl_fasync = NULL;
+ }
+
+ if (fl->fl_remove)
+ fl->fl_remove(fl);
+
+ locks_wake_up_blocks(fl, wait);
+ locks_free_lock(fl);
+}
+
+/*
+ * Delete a lock and then free it.
+ */
+static void locks_delete_lock(struct file_lock **thisfl_p, unsigned int wait)
+{
+ struct file_lock *fl = *thisfl_p;
+
+ _unhash_lock(thisfl_p);
+ _delete_lock(fl, wait);
+}
+
+/*
+ * Call back client filesystem in order to get it to unregister a lock,
+ * then delete lock. Essentially useful only in locks_remove_*().
+ * Note: this must be called with the semaphore already held!
+ */
+static inline void locks_unlock_delete(struct file_lock **thisfl_p)
+{
+ struct file_lock *fl = *thisfl_p;
+ int (*lock)(struct file *, int, struct file_lock *);
+
+ _unhash_lock(thisfl_p);
+ if (fl->fl_file->f_op &&
+ (lock = fl->fl_file->f_op->lock) != NULL) {
+ fl->fl_type = F_UNLCK;
+ lock(fl->fl_file, F_SETLK, fl);
+ }
+ _delete_lock(fl, 0);
+}
+
+/* Determine if lock sys_fl blocks lock caller_fl. Common functionality
+ * checks for shared/exclusive status of overlapping locks.
+ */
+static int locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl)
+{
+ switch (caller_fl->fl_type) {
+ case F_RDLCK:
+ return (sys_fl->fl_type == F_WRLCK);
+
+ case F_WRLCK:
+ return (1);
+
+ default:
+ printk(KERN_ERR "locks_conflict(): impossible lock type - %d\n",
+ caller_fl->fl_type);
+ break;
+ }
+ return (0); /* This should never happen */
+}
+
+/* Determine if lock sys_fl blocks lock caller_fl. POSIX specific
+ * checking before calling the locks_conflict().
+ */
+static int posix_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl)
+{
+ /* POSIX locks owned by the same process do not conflict with
+ * each other.
+ */
+ if (!(sys_fl->fl_flags & FL_POSIX) ||
+ locks_same_owner(caller_fl, sys_fl))
+ return (0);
+
+ /* Check whether they overlap */
+ if (!locks_overlap(caller_fl, sys_fl))
+ return 0;
+
+ return (locks_conflict(caller_fl, sys_fl));
+}
+
+/* Determine if lock sys_fl blocks lock caller_fl. FLOCK specific
+ * checking before calling the locks_conflict().
+ */
+static int flock_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl)
+{
+ /* FLOCK locks referring to the same filp do not conflict with
+ * each other.
+ */
+ if (!(sys_fl->fl_flags & FL_FLOCK) ||
+ (caller_fl->fl_file == sys_fl->fl_file))
+ return (0);
+#ifdef MSNFS
+ if ((caller_fl->fl_type & LOCK_MAND) || (sys_fl->fl_type & LOCK_MAND))
+ return 0;
+#endif
+
+ return (locks_conflict(caller_fl, sys_fl));
+}
+
+static int interruptible_sleep_on_locked(wait_queue_head_t *fl_wait, int timeout)
+{
+ int result = 0;
+ DECLARE_WAITQUEUE(wait, current);
+
+ current->state = TASK_INTERRUPTIBLE;
+ add_wait_queue(fl_wait, &wait);
+ if (timeout == 0)
+ schedule();
+ else
+ result = schedule_timeout(timeout);
+ if (signal_pending(current))
+ result = -ERESTARTSYS;
+ remove_wait_queue(fl_wait, &wait);
+ current->state = TASK_RUNNING;
+ return result;
+}
+
+static int locks_block_on(struct file_lock *blocker, struct file_lock *waiter)
+{
+ int result;
+ locks_insert_block(blocker, waiter);
+ result = interruptible_sleep_on_locked(&waiter->fl_wait, 0);
+ locks_delete_block(waiter);
+ return result;
+}
+
+static int locks_block_on_timeout(struct file_lock *blocker, struct file_lock *waiter, int time)
+{
+ int result;
+ locks_insert_block(blocker, waiter);
+ result = interruptible_sleep_on_locked(&waiter->fl_wait, time);
+ locks_delete_block(waiter);
+ return result;
+}
+
+struct file_lock *
+posix_test_lock(struct file *filp, struct file_lock *fl)
+{
+ struct file_lock *cfl;
+
+ lock_kernel();
+ for (cfl = filp->f_dentry->d_inode->i_flock; cfl; cfl = cfl->fl_next) {
+ if (!(cfl->fl_flags & FL_POSIX))
+ continue;
+ if (posix_locks_conflict(cfl, fl))
+ break;
+ }
+ unlock_kernel();
+
+ return (cfl);
+}
+
+/* This function tests for deadlock condition before putting a process to
+ * sleep. The detection scheme is no longer recursive. Recursive was neat,
+ * but dangerous - we risked stack corruption if the lock data was bad, or
+ * if the recursion was too deep for any other reason.
+ *
+ * We rely on the fact that a task can only be on one lock's wait queue
+ * at a time. When we find blocked_task on a wait queue we can re-search
+ * with blocked_task equal to that queue's owner, until either blocked_task
+ * isn't found, or blocked_task is found on a queue owned by my_task.
+ *
+ * Note: the above assumption may not be true when handling lock requests
+ * from a broken NFS client. But broken NFS clients have a lot more to
+ * worry about than proper deadlock detection anyway... --okir
+ */
+int posix_locks_deadlock(struct file_lock *caller_fl,
+ struct file_lock *block_fl)
+{
+ struct list_head *tmp;
+ fl_owner_t caller_owner, blocked_owner;
+ unsigned int caller_pid, blocked_pid;
+
+ caller_owner = caller_fl->fl_owner;
+ caller_pid = caller_fl->fl_pid;
+ blocked_owner = block_fl->fl_owner;
+ blocked_pid = block_fl->fl_pid;
+
+next_task:
+ if (caller_owner == blocked_owner && caller_pid == blocked_pid)
+ return 1;
+ list_for_each(tmp, &blocked_list) {
+ struct file_lock *fl = list_entry(tmp, struct file_lock, fl_link);
+ if ((fl->fl_owner == blocked_owner)
+ && (fl->fl_pid == blocked_pid)) {
+ fl = fl->fl_next;
+ blocked_owner = fl->fl_owner;
+ blocked_pid = fl->fl_pid;
+ goto next_task;
+ }
+ }
+ return 0;
+}
+
+int locks_mandatory_locked(struct inode *inode)
+{
+ fl_owner_t owner = current->files;
+ struct file_lock *fl;
+
+ /*
+ * Search the lock list for this inode for any POSIX locks.
+ */
+ lock_kernel();
+ for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
+ if (!(fl->fl_flags & FL_POSIX))
+ continue;
+ if (fl->fl_owner != owner)
+ break;
+ }
+ unlock_kernel();
+ return fl ? -EAGAIN : 0;
+}
+
+int locks_mandatory_area(int read_write, struct inode *inode,
+ struct file *filp, loff_t offset,
+ size_t count)
+{
+ struct file_lock *fl;
+ struct file_lock *new_fl = locks_alloc_lock();
+ int error;
+
+ if (new_fl == NULL)
+ return -ENOMEM;
+
+ new_fl->fl_owner = current->files;
+ new_fl->fl_pid = current->pid;
+ new_fl->fl_file = filp;
+ new_fl->fl_flags = FL_POSIX | FL_ACCESS;
+ new_fl->fl_type = (read_write == FLOCK_VERIFY_WRITE) ? F_WRLCK : F_RDLCK;
+ new_fl->fl_start = offset;
+ new_fl->fl_end = offset + count - 1;
+
+ error = 0;
+ lock_kernel();
+
+repeat:
+ /* Search the lock list for this inode for locks that conflict with
+ * the proposed read/write.
+ */
+ for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
+ if (!(fl->fl_flags & FL_POSIX))
+ continue;
+ if (fl->fl_start > new_fl->fl_end)
+ break;
+ if (posix_locks_conflict(new_fl, fl)) {
+ error = -EAGAIN;
+ if (filp && (filp->f_flags & O_NONBLOCK))
+ break;
+ error = -EDEADLK;
+ if (posix_locks_deadlock(new_fl, fl))
+ break;
+
+ error = locks_block_on(fl, new_fl);
+ if (error != 0)
+ break;
+
+ /*
+ * If we've been sleeping someone might have
+ * changed the permissions behind our back.
+ */
+ if ((inode->i_mode & (S_ISGID | S_IXGRP)) != S_ISGID)
+ break;
+ goto repeat;
+ }
+ }
+ locks_free_lock(new_fl);
+ unlock_kernel();
+ return error;
+}
+
+/* Try to create a FLOCK lock on filp. We always insert new FLOCK locks
+ * at the head of the list, but that's secret knowledge known only to
+ * flock_lock_file and posix_lock_file.
+ */
+static int flock_lock_file(struct file *filp, unsigned int lock_type,
+ unsigned int wait)
+{
+ struct file_lock *fl;
+ struct file_lock *new_fl = NULL;
+ struct file_lock **before;
+ struct inode * inode = filp->f_dentry->d_inode;
+ int error, change;
+ int unlock = (lock_type == F_UNLCK);
+
+ /*
+ * If we need a new lock, get it in advance to avoid races.
+ */
+ if (!unlock) {
+ error = -ENOLCK;
+ new_fl = flock_make_lock(filp, lock_type);
+ if (!new_fl)
+ return error;
+ }
+
+ error = 0;
+search:
+ change = 0;
+ before = &inode->i_flock;
+ while (((fl = *before) != NULL) && (fl->fl_flags & FL_FLOCK)) {
+ if (filp == fl->fl_file) {
+ if (lock_type == fl->fl_type)
+ goto out;
+ change = 1;
+ break;
+ }
+ before = &fl->fl_next;
+ }
+ /* change means that we are changing the type of an existing lock,
+ * or else unlocking it.
+ */
+ if (change) {
+ /* N.B. What if the wait argument is false? */
+ locks_delete_lock(before, !unlock);
+ /*
+ * If we waited, another lock may have been added ...
+ */
+ if (!unlock)
+ goto search;
+ }
+ if (unlock)
+ goto out;
+
+repeat:
+ for (fl = inode->i_flock; (fl != NULL) && (fl->fl_flags & FL_FLOCK);
+ fl = fl->fl_next) {
+ if (!flock_locks_conflict(new_fl, fl))
+ continue;
+ error = -EAGAIN;
+ if (!wait)
+ goto out;
+ error = locks_block_on(fl, new_fl);
+ if (error != 0)
+ goto out;
+ goto repeat;
+ }
+ locks_insert_lock(&inode->i_flock, new_fl);
+ new_fl = NULL;
+ error = 0;
+
+out:
+ if (new_fl)
+ locks_free_lock(new_fl);
+ return error;
+}
+
+/**
+ * posix_lock_file:
+ * @filp: The file to apply the lock to
+ * @caller: The lock to be applied
+ * @wait: 1 to retry automatically, 0 to return -EAGAIN
+ *
+ * Add a POSIX style lock to a file.
+ * We merge adjacent locks whenever possible. POSIX locks are sorted by owner
+ * task, then by starting address
+ *
+ * Kai Petzke writes:
+ * To make freeing a lock much faster, we keep a pointer to the lock before the
+ * actual one. But the real gain of the new coding was, that lock_it() and
+ * unlock_it() became one function.
+ *
+ * To all purists: Yes, I use a few goto's. Just pass on to the next function.
+ */
+
+int posix_lock_file(struct file *filp, struct file_lock *caller,
+ unsigned int wait)
+{
+ struct file_lock *fl;
+ struct file_lock *new_fl, *new_fl2;
+ struct file_lock *left = NULL;
+ struct file_lock *right = NULL;
+ struct file_lock **before;
+ struct inode * inode = filp->f_dentry->d_inode;
+ int error, added = 0;
+
+ /*
+ * We may need two file_lock structures for this operation,
+ * so we get them in advance to avoid races.
+ */
+ new_fl = locks_alloc_lock();
+ new_fl2 = locks_alloc_lock();
+ error = -ENOLCK; /* "no luck" */
+ if (!(new_fl && new_fl2))
+ goto out_nolock;
+
+ lock_kernel();
+ if (caller->fl_type != F_UNLCK) {
+ repeat:
+ for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
+ if (!(fl->fl_flags & FL_POSIX))
+ continue;
+ if (!posix_locks_conflict(caller, fl))
+ continue;
+ error = -EAGAIN;
+ if (!wait)
+ goto out;
+ error = -EDEADLK;
+ if (posix_locks_deadlock(caller, fl))
+ goto out;
+
+ error = locks_block_on(fl, caller);
+ if (error != 0)
+ goto out;
+ goto repeat;
+ }
+ }
+
+ /*
+ * We've allocated the new locks in advance, so there are no
+ * errors possible (and no blocking operations) from here on.
+ *
+ * Find the first old lock with the same owner as the new lock.
+ */
+
+ before = &inode->i_flock;
+
+ /* First skip locks owned by other processes.
+ */
+ while ((fl = *before) && (!(fl->fl_flags & FL_POSIX) ||
+ !locks_same_owner(caller, fl))) {
+ before = &fl->fl_next;
+ }
+
+ /* Process locks with this owner.
+ */
+ while ((fl = *before) && locks_same_owner(caller, fl)) {
+ /* Detect adjacent or overlapping regions (if same lock type)
+ */
+ if (caller->fl_type == fl->fl_type) {
+ if (fl->fl_end < caller->fl_start - 1)
+ goto next_lock;
+ /* If the next lock in the list has entirely bigger
+ * addresses than the new one, insert the lock here.
+ */
+ if (fl->fl_start > caller->fl_end + 1)
+ break;
+
+ /* If we come here, the new and old lock are of the
+ * same type and adjacent or overlapping. Make one
+ * lock yielding from the lower start address of both
+ * locks to the higher end address.
+ */
+ if (fl->fl_start > caller->fl_start)
+ fl->fl_start = caller->fl_start;
+ else
+ caller->fl_start = fl->fl_start;
+ if (fl->fl_end < caller->fl_end)
+ fl->fl_end = caller->fl_end;
+ else
+ caller->fl_end = fl->fl_end;
+ if (added) {
+ locks_delete_lock(before, 0);
+ continue;
+ }
+ caller = fl;
+ added = 1;
+ }
+ else {
+ /* Processing for different lock types is a bit
+ * more complex.
+ */
+ if (fl->fl_end < caller->fl_start)
+ goto next_lock;
+ if (fl->fl_start > caller->fl_end)
+ break;
+ if (caller->fl_type == F_UNLCK)
+ added = 1;
+ if (fl->fl_start < caller->fl_start)
+ left = fl;
+ /* If the next lock in the list has a higher end
+ * address than the new one, insert the new one here.
+ */
+ if (fl->fl_end > caller->fl_end) {
+ right = fl;
+ break;
+ }
+ if (fl->fl_start >= caller->fl_start) {
+ /* The new lock completely replaces an old
+ * one (This may happen several times).
+ */
+ if (added) {
+ locks_delete_lock(before, 0);
+ continue;
+ }
+ /* Replace the old lock with the new one.
+ * Wake up anybody waiting for the old one,
+ * as the change in lock type might satisfy
+ * their needs.
+ */
+ locks_wake_up_blocks(fl, 0); /* This cannot schedule()! */
+ fl->fl_start = caller->fl_start;
+ fl->fl_end = caller->fl_end;
+ fl->fl_type = caller->fl_type;
+ fl->fl_u = caller->fl_u;
+ caller = fl;
+ added = 1;
+ }
+ }
+ /* Go on to next lock.
+ */
+ next_lock:
+ before = &fl->fl_next;
+ }
+
+ error = 0;
+ if (!added) {
+ if (caller->fl_type == F_UNLCK)
+ goto out;
+ locks_copy_lock(new_fl, caller);
+ locks_insert_lock(before, new_fl);
+ new_fl = NULL;
+ }
+ if (right) {
+ if (left == right) {
+ /* The new lock breaks the old one in two pieces,
+ * so we have to use the second new lock.
+ */
+ left = new_fl2;
+ new_fl2 = NULL;
+ locks_copy_lock(left, right);
+ locks_insert_lock(before, left);
+ }
+ right->fl_start = caller->fl_end + 1;
+ locks_wake_up_blocks(right, 0);
+ }
+ if (left) {
+ left->fl_end = caller->fl_start - 1;
+ locks_wake_up_blocks(left, 0);
+ }
+out:
+ unlock_kernel();
+out_nolock:
+ /*
+ * Free any unused locks.
+ */
+ if (new_fl)
+ locks_free_lock(new_fl);
+ if (new_fl2)
+ locks_free_lock(new_fl2);
+ return error;
+}
+
+static inline int flock_translate_cmd(int cmd) {
+#ifdef MSNFS
+ if (cmd & LOCK_MAND)
+ return cmd & (LOCK_MAND | LOCK_RW);
+#endif
+ switch (cmd &~ LOCK_NB) {
+ case LOCK_SH:
+ return F_RDLCK;
+ case LOCK_EX:
+ return F_WRLCK;
+ case LOCK_UN:
+ return F_UNLCK;
+ }
+ return -EINVAL;
+}
+
+/* We already had a lease on this file; just change its type */
+static int lease_modify(struct file_lock **before, int arg)
+{
+ struct file_lock *fl = *before;
+ int error = assign_type(fl, arg);
+
+ if (error)
+ return error;
+ locks_wake_up_blocks(fl, 0);
+ if (arg == F_UNLCK) {
+ struct file *filp = fl->fl_file;
+
+ filp->f_owner.pid = 0;
+ filp->f_owner.uid = 0;
+ filp->f_owner.euid = 0;
+ filp->f_owner.signum = 0;
+ locks_delete_lock(before, 0);
+ }
+ return 0;
+}
+
+static void time_out_leases(struct inode *inode)
+{
+ struct file_lock **before;
+ struct file_lock *fl;
+
+ before = &inode->i_flock;
+ while ((fl = *before) && (fl->fl_flags & FL_LEASE)
+ && (fl->fl_type & F_INPROGRESS)) {
+ if ((fl->fl_break_time == 0)
+ || time_before(jiffies, fl->fl_break_time)) {
+ before = &fl->fl_next;
+ continue;
+ }
+ printk(KERN_INFO "lease broken - owner pid = %d\n", fl->fl_pid);
+ lease_modify(before, fl->fl_type & ~F_INPROGRESS);
+ if (fl == *before) /* lease_modify may have freed fl */
+ before = &fl->fl_next;
+ }
+}
+
+/**
+ * __get_lease - revoke all outstanding leases on file
+ * @inode: the inode of the file to return
+ * @mode: the open mode (read or write)
+ *
+ * get_lease (inlined for speed) has checked there already
+ * is a lease on this file. Leases are broken on a call to open()
+ * or truncate(). This function can sleep unless you
+ * specified %O_NONBLOCK to your open().
+ */
+int __get_lease(struct inode *inode, unsigned int mode)
+{
+ int error = 0, future;
+ struct file_lock *new_fl, *flock;
+ struct file_lock *fl;
+ int alloc_err;
+ unsigned long break_time;
+ int i_have_this_lease = 0;
+
+ alloc_err = lease_alloc(NULL, mode & FMODE_WRITE ? F_WRLCK : F_RDLCK,
+ &new_fl);
+
+ lock_kernel();
+
+ time_out_leases(inode);
+
+ flock = inode->i_flock;
+ if ((flock == NULL) || (flock->fl_flags & FL_LEASE) == 0)
+ goto out;
+
+ for (fl = flock; fl && (fl->fl_flags & FL_LEASE); fl = fl->fl_next)
+ if (fl->fl_owner == current->files)
+ i_have_this_lease = 1;
+
+ if (mode & FMODE_WRITE) {
+ /* If we want write access, we have to revoke any lease. */
+ future = F_UNLCK | F_INPROGRESS;
+ } else if (flock->fl_type & F_INPROGRESS) {
+ /* If the lease is already being broken, we just leave it */
+ future = flock->fl_type;
+ } else if (flock->fl_type & F_WRLCK) {
+ /* Downgrade the exclusive lease to a read-only lease. */
+ future = F_RDLCK | F_INPROGRESS;
+ } else {
+ /* the existing lease was read-only, so we can read too. */
+ goto out;
+ }
+
+ if (alloc_err && !i_have_this_lease && ((mode & O_NONBLOCK) == 0)) {
+ error = alloc_err;
+ goto out;
+ }
+
+ break_time = 0;
+ if (lease_break_time > 0) {
+ break_time = jiffies + lease_break_time * HZ;
+ if (break_time == 0)
+ break_time++; /* so that 0 means no break time */
+ }
+
+ for (fl = flock; fl && (fl->fl_flags & FL_LEASE); fl = fl->fl_next) {
+ if (fl->fl_type != future) {
+ fl->fl_type = future;
+ fl->fl_break_time = break_time;
+ kill_fasync(&fl->fl_fasync, SIGIO, POLL_MSG);
+ }
+ }
+
+ if (i_have_this_lease || (mode & O_NONBLOCK)) {
+ error = -EWOULDBLOCK;
+ goto out;
+ }
+
+restart:
+ break_time = flock->fl_break_time;
+ if (break_time != 0) {
+ break_time -= jiffies;
+ if (break_time == 0)
+ break_time++;
+ }
+ error = locks_block_on_timeout(flock, new_fl, break_time);
+ if (error >= 0) {
+ if (error == 0)
+ time_out_leases(inode);
+ /* Wait for the next lease that has not been broken yet */
+ for (flock = inode->i_flock;
+ flock && (flock->fl_flags & FL_LEASE);
+ flock = flock->fl_next) {
+ if (flock->fl_type & F_INPROGRESS)
+ goto restart;
+ }
+ error = 0;
+ }
+
+out:
+ unlock_kernel();
+ if (!alloc_err)
+ locks_free_lock(new_fl);
+ return error;
+}
+
+/**
+ * lease_get_mtime
+ * @inode: the inode
+ *
+ * This is to force NFS clients to flush their caches for files with
+ * exclusive leases. The justification is that if someone has an
+ * exclusive lease, then they could be modifiying it.
+ */
+time_t lease_get_mtime(struct inode *inode)
+{
+ struct file_lock *flock = inode->i_flock;
+ if (flock && (flock->fl_flags & FL_LEASE) && (flock->fl_type & F_WRLCK))
+ return CURRENT_TIME;
+ return inode->i_mtime;
+}
+
+/**
+ * fcntl_getlease - Enquire what lease is currently active
+ * @filp: the file
+ *
+ * The value returned by this function will be one of
+ * (if no lease break is pending):
+ *
+ * %F_RDLCK to indicate a shared lease is held.
+ *
+ * %F_WRLCK to indicate an exclusive lease is held.
+ *
+ * %F_UNLCK to indicate no lease is held.
+ *
+ * (if a lease break is pending):
+ *
+ * %F_RDLCK to indicate an exclusive lease needs to be
+ * changed to a shared lease (or removed).
+ *
+ * %F_UNLCK to indicate the lease needs to be removed.
+ *
+ * XXX: sfr & willy disagree over whether F_INPROGRESS
+ * should be returned to userspace.
+ */
+int fcntl_getlease(struct file *filp)
+{
+ struct file_lock *fl;
+ int type = F_UNLCK;
+
+ lock_kernel();
+ time_out_leases(filp->f_dentry->d_inode);
+ for (fl = filp->f_dentry->d_inode->i_flock;
+ fl && (fl->fl_flags & FL_LEASE);
+ fl = fl->fl_next) {
+ if (fl->fl_file == filp) {
+ type = fl->fl_type & ~F_INPROGRESS;
+ break;
+ }
+ }
+ unlock_kernel();
+ return type;
+}
+
+/**
+ * fcntl_setlease - sets a lease on an open file
+ * @fd: open file descriptor
+ * @filp: file pointer
+ * @arg: type of lease to obtain
+ *
+ * Call this fcntl to establish a lease on the file.
+ * Note that you also need to call %F_SETSIG to
+ * receive a signal when the lease is broken.
+ */
+int fcntl_setlease(unsigned int fd, struct file *filp, long arg)
+{
+ struct file_lock *fl, **before, **my_before = NULL;
+ struct dentry *dentry;
+ struct inode *inode;
+ int error, rdlease_count = 0, wrlease_count = 0;
+
+ dentry = filp->f_dentry;
+ inode = dentry->d_inode;
+
+ if ((current->fsuid != inode->i_uid) && !capable(CAP_LEASE))
+ return -EACCES;
+ if (!S_ISREG(inode->i_mode))
+ return -EINVAL;
+
+ lock_kernel();
+
+ time_out_leases(inode);
+
+ /*
+ * FIXME: What about F_RDLCK and files open for writing?
+ */
+ error = -EAGAIN;
+ if ((arg == F_WRLCK)
+ && ((atomic_read(&dentry->d_count) > 1)
+ || (atomic_read(&inode->i_count) > 1)))
+ goto out_unlock;
+
+ /*
+ * At this point, we know that if there is an exclusive
+ * lease on this file, then we hold it on this filp
+ * (otherwise our open of this file would have blocked).
+ * And if we are trying to acquire an exclusive lease,
+ * then the file is not open by anyone (including us)
+ * except for this filp.
+ */
+ for (before = &inode->i_flock;
+ ((fl = *before) != NULL) && (fl->fl_flags & FL_LEASE);
+ before = &fl->fl_next) {
+ if (fl->fl_file == filp)
+ my_before = before;
+ else if (fl->fl_type == (F_INPROGRESS | F_UNLCK))
+ /*
+ * Someone is in the process of opening this
+ * file for writing so we may not take an
+ * exclusive lease on it.
+ */
+ wrlease_count++;
+ else
+ rdlease_count++;
+ }
+
+ if ((arg == F_RDLCK && (wrlease_count > 0)) ||
+ (arg == F_WRLCK && ((rdlease_count + wrlease_count) > 0)))
+ goto out_unlock;
+
+ if (my_before != NULL) {
+ error = lease_modify(my_before, arg);
+ goto out_unlock;
+ }
+
+ error = 0;
+ if (arg == F_UNLCK)
+ goto out_unlock;
+
+ error = -EINVAL;
+ if (!leases_enable)
+ goto out_unlock;
+
+ error = lease_alloc(filp, arg, &fl);
+ if (error)
+ goto out_unlock;
+
+ error = fasync_helper(fd, filp, 1, &fl->fl_fasync);
+ if (error < 0) {
+ locks_free_lock(fl);
+ goto out_unlock;
+ }
+ fl->fl_next = *before;
+ *before = fl;
+ list_add(&fl->fl_link, &file_lock_list);
+ filp->f_owner.pid = current->pid;
+ filp->f_owner.uid = current->uid;
+ filp->f_owner.euid = current->euid;
+out_unlock:
+ unlock_kernel();
+ return error;
+}
+
+/**
+ * sys_flock: - flock() system call.
+ * @fd: the file descriptor to lock.
+ * @cmd: the type of lock to apply.
+ *
+ * Apply a %FL_FLOCK style lock to an open file descriptor.
+ * The @cmd can be one of
+ *
+ * %LOCK_SH -- a shared lock.
+ *
+ * %LOCK_EX -- an exclusive lock.
+ *
+ * %LOCK_UN -- remove an existing lock.
+ *
+ * %LOCK_MAND -- a `mandatory' flock. This exists to emulate Windows Share Modes.
+ *
+ * %LOCK_MAND can be combined with %LOCK_READ or %LOCK_WRITE to allow other
+ * processes read and write access respectively.
+ */
+asmlinkage long sys_flock(unsigned int fd, unsigned int cmd)
+{
+ struct file *filp;
+ int error, type;
+
+ error = -EBADF;
+ filp = fget(fd);
+ if (!filp)
+ goto out;
+
+ error = flock_translate_cmd(cmd);
+ if (error < 0)
+ goto out_putf;
+ type = error;
+
+ error = -EBADF;
+ if ((type != F_UNLCK)
+#ifdef MSNFS
+ && !(type & LOCK_MAND)
+#endif
+ && !(filp->f_mode & 3))
+ goto out_putf;
+
+ lock_kernel();
+ error = flock_lock_file(filp, type,
+ (cmd & (LOCK_UN | LOCK_NB)) ? 0 : 1);
+ unlock_kernel();
+
+out_putf:
+ fput(filp);
+out:
+ return error;
+}
+
+/* Report the first existing lock that would conflict with l.
+ * This implements the F_GETLK command of fcntl().
+ */
+int fcntl_getlk(unsigned int fd, struct flock *l)
+{
+ struct file *filp;
+ struct file_lock *fl, file_lock;
+ struct flock flock;
+ int error;
+
+ error = -EFAULT;
+ if (copy_from_user(&flock, l, sizeof(flock)))
+ goto out;
+ error = -EINVAL;
+ if ((flock.l_type != F_RDLCK) && (flock.l_type != F_WRLCK))
+ goto out;
+
+ error = -EBADF;
+ filp = fget(fd);
+ if (!filp)
+ goto out;
+
+ error = flock_to_posix_lock(filp, &file_lock, &flock);
+ if (error)
+ goto out_putf;
+
+ if (filp->f_op && filp->f_op->lock) {
+ error = filp->f_op->lock(filp, F_GETLK, &file_lock);
+ if (error < 0)
+ goto out_putf;
+ else if (error == LOCK_USE_CLNT)
+ /* Bypass for NFS with no locking - 2.0.36 compat */
+ fl = posix_test_lock(filp, &file_lock);
+ else
+ fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock);
+ } else {
+ fl = posix_test_lock(filp, &file_lock);
+ }
+
+ flock.l_type = F_UNLCK;
+ if (fl != NULL) {
+ flock.l_pid = fl->fl_pid;
+#if BITS_PER_LONG == 32
+ /*
+ * Make sure we can represent the posix lock via
+ * legacy 32bit flock.
+ */
+ error = -EOVERFLOW;
+ if (fl->fl_start > OFFT_OFFSET_MAX)
+ goto out_putf;
+ if ((fl->fl_end != OFFSET_MAX)
+ && (fl->fl_end > OFFT_OFFSET_MAX))
+ goto out_putf;
+#endif
+ flock.l_start = fl->fl_start;
+ flock.l_len = fl->fl_end == OFFSET_MAX ? 0 :
+ fl->fl_end - fl->fl_start + 1;
+ flock.l_whence = 0;
+ flock.l_type = fl->fl_type;
+ }
+ error = -EFAULT;
+ if (!copy_to_user(l, &flock, sizeof(flock)))
+ error = 0;
+
+out_putf:
+ fput(filp);
+out:
+ return error;
+}
+
+/* Apply the lock described by l to an open file descriptor.
+ * This implements both the F_SETLK and F_SETLKW commands of fcntl().
+ */
+int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l)
+{
+ struct file *filp;
+ struct file_lock *file_lock = locks_alloc_lock();
+ struct flock flock;
+ struct inode *inode;
+ int error;
+
+ if (file_lock == NULL)
+ return -ENOLCK;
+
+ /*
+ * This might block, so we do it before checking the inode.
+ */
+ error = -EFAULT;
+ if (copy_from_user(&flock, l, sizeof(flock)))
+ goto out;
+
+ /* Get arguments and validate them ...
+ */
+
+ error = -EBADF;
+ filp = fget(fd);
+ if (!filp)
+ goto out;
+
+ error = -EINVAL;
+ inode = filp->f_dentry->d_inode;
+
+ /* Don't allow mandatory locks on files that may be memory mapped
+ * and shared.
+ */
+ if (IS_MANDLOCK(inode) &&
+ (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID) {
+ struct address_space *mapping = inode->i_mapping;
+
+ if (mapping->i_mmap_shared != NULL) {
+ error = -EAGAIN;
+ goto out_putf;
+ }
+ }
+
+ error = flock_to_posix_lock(filp, file_lock, &flock);
+ if (error)
+ goto out_putf;
+
+ error = -EBADF;
+ switch (flock.l_type) {
+ case F_RDLCK:
+ if (!(filp->f_mode & FMODE_READ))
+ goto out_putf;
+ break;
+ case F_WRLCK:
+ if (!(filp->f_mode & FMODE_WRITE))
+ goto out_putf;
+ break;
+ case F_UNLCK:
+ break;
+ case F_SHLCK:
+ case F_EXLCK:
+#ifdef __sparc__
+/* warn a bit for now, but don't overdo it */
+{
+ static int count = 0;
+ if (!count) {
+ count=1;
+ printk(KERN_WARNING
+ "fcntl_setlk() called by process %d (%s) with broken flock() emulation\n",
+ current->pid, current->comm);
+ }
+}
+ if (!(filp->f_mode & 3))
+ goto out_putf;
+ break;
+#endif
+ default:
+ error = -EINVAL;
+ goto out_putf;
+ }
+
+ if (filp->f_op && filp->f_op->lock != NULL) {
+ error = filp->f_op->lock(filp, cmd, file_lock);
+ if (error < 0)
+ goto out_putf;
+ }
+ error = posix_lock_file(filp, file_lock, cmd == F_SETLKW);
+
+out_putf:
+ fput(filp);
+out:
+ locks_free_lock(file_lock);
+ return error;
+}
+
+#if BITS_PER_LONG == 32
+/* Report the first existing lock that would conflict with l.
+ * This implements the F_GETLK command of fcntl().
+ */
+int fcntl_getlk64(unsigned int fd, struct flock64 *l)
+{
+ struct file *filp;
+ struct file_lock *fl, file_lock;
+ struct flock64 flock;
+ int error;
+
+ error = -EFAULT;
+ if (copy_from_user(&flock, l, sizeof(flock)))
+ goto out;
+ error = -EINVAL;
+ if ((flock.l_type != F_RDLCK) && (flock.l_type != F_WRLCK))
+ goto out;
+
+ error = -EBADF;
+ filp = fget(fd);
+ if (!filp)
+ goto out;
+
+ error = flock64_to_posix_lock(filp, &file_lock, &flock);
+ if (error)
+ goto out_putf;
+
+ if (filp->f_op && filp->f_op->lock) {
+ error = filp->f_op->lock(filp, F_GETLK, &file_lock);
+ if (error < 0)
+ goto out_putf;
+ else if (error == LOCK_USE_CLNT)
+ /* Bypass for NFS with no locking - 2.0.36 compat */
+ fl = posix_test_lock(filp, &file_lock);
+ else
+ fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock);
+ } else {
+ fl = posix_test_lock(filp, &file_lock);
+ }
+
+ flock.l_type = F_UNLCK;
+ if (fl != NULL) {
+ flock.l_pid = fl->fl_pid;
+ flock.l_start = fl->fl_start;
+ flock.l_len = fl->fl_end == OFFSET_MAX ? 0 :
+ fl->fl_end - fl->fl_start + 1;
+ flock.l_whence = 0;
+ flock.l_type = fl->fl_type;
+ }
+ error = -EFAULT;
+ if (!copy_to_user(l, &flock, sizeof(flock)))
+ error = 0;
+
+out_putf:
+ fput(filp);
+out:
+ return error;
+}
+
+/* Apply the lock described by l to an open file descriptor.
+ * This implements both the F_SETLK and F_SETLKW commands of fcntl().
+ */
+int fcntl_setlk64(unsigned int fd, unsigned int cmd, struct flock64 *l)
+{
+ struct file *filp;
+ struct file_lock *file_lock = locks_alloc_lock();
+ struct flock64 flock;
+ struct inode *inode;
+ int error;
+
+ if (file_lock == NULL)
+ return -ENOLCK;
+
+ /*
+ * This might block, so we do it before checking the inode.
+ */
+ error = -EFAULT;
+ if (copy_from_user(&flock, l, sizeof(flock)))
+ goto out;
+
+ /* Get arguments and validate them ...
+ */
+
+ error = -EBADF;
+ filp = fget(fd);
+ if (!filp)
+ goto out;
+
+ error = -EINVAL;
+ inode = filp->f_dentry->d_inode;
+
+ /* Don't allow mandatory locks on files that may be memory mapped
+ * and shared.
+ */
+ if (IS_MANDLOCK(inode) &&
+ (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID) {
+ struct address_space *mapping = inode->i_mapping;
+
+ if (mapping->i_mmap_shared != NULL) {
+ error = -EAGAIN;
+ goto out_putf;
+ }
+ }
+
+ error = flock64_to_posix_lock(filp, file_lock, &flock);
+ if (error)
+ goto out_putf;
+
+ error = -EBADF;
+ switch (flock.l_type) {
+ case F_RDLCK:
+ if (!(filp->f_mode & FMODE_READ))
+ goto out_putf;
+ break;
+ case F_WRLCK:
+ if (!(filp->f_mode & FMODE_WRITE))
+ goto out_putf;
+ break;
+ case F_UNLCK:
+ break;
+ case F_SHLCK:
+ case F_EXLCK:
+ default:
+ error = -EINVAL;
+ goto out_putf;
+ }
+
+ if (filp->f_op && filp->f_op->lock != NULL) {
+ error = filp->f_op->lock(filp, cmd, file_lock);
+ if (error < 0)
+ goto out_putf;
+ }
+ error = posix_lock_file(filp, file_lock, cmd == F_SETLKW64);
+
+out_putf:
+ fput(filp);
+out:
+ locks_free_lock(file_lock);
+ return error;
+}
+#endif /* BITS_PER_LONG == 32 */
+
+/*
+ * This function is called when the file is being removed
+ * from the task's fd array.
+ */
+void locks_remove_posix(struct file *filp, fl_owner_t owner)
+{
+ struct inode * inode = filp->f_dentry->d_inode;
+ struct file_lock *fl;
+ struct file_lock **before;
+
+ /*
+ * For POSIX locks we free all locks on this file for the given task.
+ */
+ if (!inode->i_flock) {
+ /*
+ * Notice that something might be grabbing a lock right now.
+ * Consider it as a race won by us - event is async, so even if
+ * we miss the lock added we can trivially consider it as added
+ * after we went through this call.
+ */
+ return;
+ }
+ lock_kernel();
+ before = &inode->i_flock;
+ while ((fl = *before) != NULL) {
+ if ((fl->fl_flags & FL_POSIX) && fl->fl_owner == owner) {
+ struct file *filp = fl->fl_file;
+ /* Note: locks_unlock_delete() can sleep, and
+ * so we may race with the call to sys_close()
+ * by the thread that actually owns this filp.
+ */
+ get_file(filp);
+ locks_unlock_delete(before);
+ fput(filp);
+ before = &inode->i_flock;
+ continue;
+ }
+ before = &fl->fl_next;
+ }
+ unlock_kernel();
+}
+
+/*
+ * This function is called on the last close of an open file.
+ */
+void locks_remove_flock(struct file *filp)
+{
+ struct inode * inode = filp->f_dentry->d_inode;
+ struct file_lock *fl;
+ struct file_lock **before;
+
+ if (!inode->i_flock)
+ return;
+
+ lock_kernel();
+ before = &inode->i_flock;
+
+ while ((fl = *before) != NULL) {
+ if (fl->fl_file == filp) {
+ if (fl->fl_flags & FL_FLOCK) {
+ locks_delete_lock(before, 0);
+ continue;
+ }
+ if (fl->fl_flags & FL_LEASE) {
+ lease_modify(before, F_UNLCK);
+ continue;
+ }
+ }
+ before = &fl->fl_next;
+ }
+ unlock_kernel();
+}
+
+/**
+ * posix_block_lock - blocks waiting for a file lock
+ * @blocker: the lock which is blocking
+ * @waiter: the lock which conflicts and has to wait
+ *
+ * lockd needs to block waiting for locks.
+ */
+void
+posix_block_lock(struct file_lock *blocker, struct file_lock *waiter)
+{
+ locks_insert_block(blocker, waiter);
+}
+
+/**
+ * posix_unblock_lock - stop waiting for a file lock
+ * @waiter: the lock which was waiting
+ *
+ * lockd needs to block waiting for locks.
+ */
+void
+posix_unblock_lock(struct file_lock *waiter)
+{
+ if (!list_empty(&waiter->fl_block))
+ locks_delete_block(waiter);
+}
+
+static void lock_get_status(char* out, struct file_lock *fl, int id, char *pfx)
+{
+ struct inode *inode = NULL;
+
+ if (fl->fl_file != NULL)
+ inode = fl->fl_file->f_dentry->d_inode;
+
+ out += sprintf(out, "%d:%s ", id, pfx);
+ if (fl->fl_flags & FL_POSIX) {
+ out += sprintf(out, "%6s %s ",
+ (fl->fl_flags & FL_ACCESS) ? "ACCESS" : "POSIX ",
+ (inode == NULL) ? "*NOINODE*" :
+ (IS_MANDLOCK(inode) &&
+ (inode->i_mode & (S_IXGRP | S_ISGID)) == S_ISGID) ?
+ "MANDATORY" : "ADVISORY ");
+ } else if (fl->fl_flags & FL_FLOCK) {
+#ifdef MSNFS
+ if (fl->fl_type & LOCK_MAND) {
+ out += sprintf(out, "FLOCK MSNFS ");
+ } else
+#endif
+ out += sprintf(out, "FLOCK ADVISORY ");
+ } else if (fl->fl_flags & FL_LEASE) {
+ out += sprintf(out, "LEASE ");
+ if (fl->fl_type & F_INPROGRESS)
+ out += sprintf(out, "BREAKING ");
+ else if (fl->fl_file)
+ out += sprintf(out, "ACTIVE ");
+ else
+ out += sprintf(out, "BREAKER ");
+ } else {
+ out += sprintf(out, "UNKNOWN UNKNOWN ");
+ }
+#ifdef MSNFS
+ if (fl->fl_type & LOCK_MAND) {
+ out += sprintf(out, "%s ",
+ (fl->fl_type & LOCK_READ)
+ ? (fl->fl_type & LOCK_WRITE) ? "RW " : "READ "
+ : (fl->fl_type & LOCK_WRITE) ? "WRITE" : "NONE ");
+ } else
+#endif
+ out += sprintf(out, "%s ",
+ (fl->fl_type & F_INPROGRESS)
+ ? (fl->fl_type & F_UNLCK) ? "UNLCK" : "READ "
+ : (fl->fl_type & F_WRLCK) ? "WRITE" : "READ ");
+ out += sprintf(out, "%d %s:%ld ",
+ fl->fl_pid,
+ inode ? kdevname(inode->i_dev) : "<none>",
+ inode ? inode->i_ino : 0);
+ out += sprintf(out, "%Ld ", fl->fl_start);
+ if (fl->fl_end == OFFSET_MAX)
+ out += sprintf(out, "EOF ");
+ else
+ out += sprintf(out, "%Ld ", fl->fl_end);
+ sprintf(out, "%08lx %08lx %08lx %08lx %08lx\n",
+ (long)fl, (long)fl->fl_link.prev, (long)fl->fl_link.next,
+ (long)fl->fl_next, (long)fl->fl_block.next);
+}
+
+static void move_lock_status(char **p, off_t* pos, off_t offset)
+{
+ int len;
+ len = strlen(*p);
+ if(*pos >= offset) {
+ /* the complete line is valid */
+ *p += len;
+ *pos += len;
+ return;
+ }
+ if(*pos+len > offset) {
+ /* use the second part of the line */
+ int i = offset-*pos;
+ memmove(*p,*p+i,len-i);
+ *p += len-i;
+ *pos += len;
+ return;
+ }
+ /* discard the complete line */
+ *pos += len;
+}
+
+/**
+ * get_locks_status - reports lock usage in /proc/locks
+ * @buffer: address in userspace to write into
+ * @start: ?
+ * @offset: how far we are through the buffer
+ * @length: how much to read
+ */
+
+int get_locks_status(char *buffer, char **start, off_t offset, int length)
+{
+ struct list_head *tmp;
+ char *q = buffer;
+ off_t pos = 0;
+ int i = 0;
+
+ lock_kernel();
+ list_for_each(tmp, &file_lock_list) {
+ struct list_head *btmp;
+ struct file_lock *fl = list_entry(tmp, struct file_lock, fl_link);
+ lock_get_status(q, fl, ++i, "");
+ move_lock_status(&q, &pos, offset);
+
+ if(pos >= offset+length)
+ goto done;
+
+ list_for_each(btmp, &fl->fl_block) {
+ struct file_lock *bfl = list_entry(btmp,
+ struct file_lock, fl_block);
+ lock_get_status(q, bfl, i, " ->");
+ move_lock_status(&q, &pos, offset);
+
+ if(pos >= offset+length)
+ goto done;
+ }
+ }
+done:
+ unlock_kernel();
+ *start = buffer;
+ if(q-buffer < length)
+ return (q-buffer);
+ return length;
+}
+
+void steal_locks(fl_owner_t from)
+{
+ struct list_head *tmp;
+
+ if (from == current->files)
+ return;
+
+ lock_kernel();
+ list_for_each(tmp, &file_lock_list) {
+ struct file_lock *fl = list_entry(tmp, struct file_lock,
+ fl_link);
+ if (fl->fl_owner == from)
+ fl->fl_owner = current->files;
+ }
+ unlock_kernel();
+}
+
+#ifdef MSNFS
+/**
+ * lock_may_read - checks that the region is free of locks
+ * @inode: the inode that is being read
+ * @start: the first byte to read
+ * @len: the number of bytes to read
+ *
+ * Emulates Windows locking requirements. Whole-file
+ * mandatory locks (share modes) can prohibit a read and
+ * byte-range POSIX locks can prohibit a read if they overlap.
+ *
+ * N.B. this function is only ever called
+ * from knfsd and ownership of locks is never checked.
+ */
+int lock_may_read(struct inode *inode, loff_t start, unsigned long len)
+{
+ struct file_lock *fl;
+ int result = 1;
+ lock_kernel();
+ for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
+ if (fl->fl_flags == FL_POSIX) {
+ if (fl->fl_type == F_RDLCK)
+ continue;
+ if ((fl->fl_end < start) || (fl->fl_start > (start + len)))
+ continue;
+ } else if (fl->fl_flags == FL_FLOCK) {
+ if (!(fl->fl_type & LOCK_MAND))
+ continue;
+ if (fl->fl_type & LOCK_READ)
+ continue;
+ } else
+ continue;
+ result = 0;
+ break;
+ }
+ unlock_kernel();
+ return result;
+}
+
+/**
+ * lock_may_write - checks that the region is free of locks
+ * @inode: the inode that is being written
+ * @start: the first byte to write
+ * @len: the number of bytes to write
+ *
+ * Emulates Windows locking requirements. Whole-file
+ * mandatory locks (share modes) can prohibit a write and
+ * byte-range POSIX locks can prohibit a write if they overlap.
+ *
+ * N.B. this function is only ever called
+ * from knfsd and ownership of locks is never checked.
+ */
+int lock_may_write(struct inode *inode, loff_t start, unsigned long len)
+{
+ struct file_lock *fl;
+ int result = 1;
+ lock_kernel();
+ for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
+ if (fl->fl_flags == FL_POSIX) {
+ if ((fl->fl_end < start) || (fl->fl_start > (start + len)))
+ continue;
+ } else if (fl->fl_flags == FL_FLOCK) {
+ if (!(fl->fl_type & LOCK_MAND))
+ continue;
+ if (fl->fl_type & LOCK_WRITE)
+ continue;
+ } else
+ continue;
+ result = 0;
+ break;
+ }
+ unlock_kernel();
+ return result;
+}
+#endif
+
+static int __init filelock_init(void)
+{
+ filelock_cache = kmem_cache_create("file_lock_cache",
+ sizeof(struct file_lock), 0, 0, init_once, NULL);
+ if (!filelock_cache)
+ panic("cannot create file lock slab cache");
+ return 0;
+}
+
+module_init(filelock_init)
diff --git a/fs/minix/Makefile b/fs/minix/Makefile
new file mode 100644
index 00000000000000..13505618163370
--- /dev/null
+++ b/fs/minix/Makefile
@@ -0,0 +1,15 @@
+#
+# Makefile for the Linux minix filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile.
+
+O_TARGET := minix.o
+
+obj-y := bitmap.o itree_v1.o itree_v2.o namei.o inode.o file.o dir.o
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/minix/bitmap.c b/fs/minix/bitmap.c
new file mode 100644
index 00000000000000..6347bb16996d10
--- /dev/null
+++ b/fs/minix/bitmap.c
@@ -0,0 +1,276 @@
+/*
+ * linux/fs/minix/bitmap.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+/*
+ * Modified for 680x0 by Hamish Macdonald
+ * Fixed for 680x0 by Andreas Schwab
+ */
+
+/* bitmap.c contains the code that handles the inode and block bitmaps */
+
+#include <linux/fs.h>
+#include <linux/minix_fs.h>
+#include <linux/locks.h>
+
+#include <asm/bitops.h>
+
+static int nibblemap[] = { 4,3,3,2,3,2,2,1,3,2,2,1,2,1,1,0 };
+
+static unsigned long count_free(struct buffer_head *map[], unsigned numblocks, __u32 numbits)
+{
+ unsigned i, j, sum = 0;
+ struct buffer_head *bh;
+
+ for (i=0; i<numblocks-1; i++) {
+ if (!(bh=map[i]))
+ return(0);
+ for (j=0; j<BLOCK_SIZE; j++)
+ sum += nibblemap[bh->b_data[j] & 0xf]
+ + nibblemap[(bh->b_data[j]>>4) & 0xf];
+ }
+
+ if (numblocks==0 || !(bh=map[numblocks-1]))
+ return(0);
+ i = ((numbits-(numblocks-1)*BLOCK_SIZE*8)/16)*2;
+ for (j=0; j<i; j++) {
+ sum += nibblemap[bh->b_data[j] & 0xf]
+ + nibblemap[(bh->b_data[j]>>4) & 0xf];
+ }
+
+ i = numbits%16;
+ if (i!=0) {
+ i = *(__u16 *)(&bh->b_data[j]) | ~((1<<i) - 1);
+ sum += nibblemap[i & 0xf] + nibblemap[(i>>4) & 0xf];
+ sum += nibblemap[(i>>8) & 0xf] + nibblemap[(i>>12) & 0xf];
+ }
+ return(sum);
+}
+
+void minix_free_block(struct inode * inode, int block)
+{
+ struct super_block * sb = inode->i_sb;
+ struct buffer_head * bh;
+ unsigned int bit,zone;
+
+ if (!sb) {
+ printk("trying to free block on nonexistent device\n");
+ return;
+ }
+ if (block < sb->u.minix_sb.s_firstdatazone ||
+ block >= sb->u.minix_sb.s_nzones) {
+ printk("trying to free block not in datazone\n");
+ return;
+ }
+ zone = block - sb->u.minix_sb.s_firstdatazone + 1;
+ bit = zone & 8191;
+ zone >>= 13;
+ if (zone >= sb->u.minix_sb.s_zmap_blocks) {
+ printk("minix_free_block: nonexistent bitmap buffer\n");
+ return;
+ }
+ bh = sb->u.minix_sb.s_zmap[zone];
+ if (!minix_test_and_clear_bit(bit,bh->b_data))
+ printk("free_block (%s:%d): bit already cleared\n",
+ kdevname(sb->s_dev), block);
+ mark_buffer_dirty(bh);
+ return;
+}
+
+int minix_new_block(struct inode * inode)
+{
+ struct super_block * sb = inode->i_sb;
+ struct buffer_head * bh;
+ int i,j;
+
+ if (!sb) {
+ printk("trying to get new block from nonexistent device\n");
+ return 0;
+ }
+repeat:
+ j = 8192;
+ bh = NULL;
+ for (i = 0; i < sb->u.minix_sb.s_zmap_blocks; i++) {
+ bh = sb->u.minix_sb.s_zmap[i];
+ if ((j = minix_find_first_zero_bit(bh->b_data, 8192)) < 8192)
+ break;
+ }
+ if (!bh || j >= 8192)
+ return 0;
+ if (minix_test_and_set_bit(j,bh->b_data)) {
+ printk("new_block: bit already set");
+ goto repeat;
+ }
+ mark_buffer_dirty(bh);
+ j += i*8192 + sb->u.minix_sb.s_firstdatazone-1;
+ if (j < sb->u.minix_sb.s_firstdatazone ||
+ j >= sb->u.minix_sb.s_nzones)
+ return 0;
+ return j;
+}
+
+unsigned long minix_count_free_blocks(struct super_block *sb)
+{
+ return (count_free(sb->u.minix_sb.s_zmap, sb->u.minix_sb.s_zmap_blocks,
+ sb->u.minix_sb.s_nzones - sb->u.minix_sb.s_firstdatazone + 1)
+ << sb->u.minix_sb.s_log_zone_size);
+}
+
+struct minix_inode *
+minix_V1_raw_inode(struct super_block *sb, ino_t ino, struct buffer_head **bh)
+{
+ int block;
+ struct minix_sb_info *sbi = &sb->u.minix_sb;
+ struct minix_inode *p;
+
+ if (!ino || ino > sbi->s_ninodes) {
+ printk("Bad inode number on dev %s: %ld is out of range\n",
+ bdevname(sb->s_dev), ino);
+ return NULL;
+ }
+ ino--;
+ block = 2 + sbi->s_imap_blocks + sbi->s_zmap_blocks +
+ ino / MINIX_INODES_PER_BLOCK;
+ *bh = sb_bread(sb, block);
+ if (!*bh) {
+ printk("unable to read i-node block\n");
+ return NULL;
+ }
+ p = (void *)(*bh)->b_data;
+ return p + ino % MINIX_INODES_PER_BLOCK;
+}
+
+struct minix2_inode *
+minix_V2_raw_inode(struct super_block *sb, ino_t ino, struct buffer_head **bh)
+{
+ int block;
+ struct minix_sb_info *sbi = &sb->u.minix_sb;
+ struct minix2_inode *p;
+
+ *bh = NULL;
+ if (!ino || ino > sbi->s_ninodes) {
+ printk("Bad inode number on dev %s: %ld is out of range\n",
+ bdevname(sb->s_dev), ino);
+ return NULL;
+ }
+ ino--;
+ block = 2 + sbi->s_imap_blocks + sbi->s_zmap_blocks +
+ ino / MINIX2_INODES_PER_BLOCK;
+ *bh = sb_bread(sb, block);
+ if (!*bh) {
+ printk("unable to read i-node block\n");
+ return NULL;
+ }
+ p = (void *)(*bh)->b_data;
+ return p + ino % MINIX2_INODES_PER_BLOCK;
+}
+
+/* Clear the link count and mode of a deleted inode on disk. */
+
+static void minix_clear_inode(struct inode *inode)
+{
+ struct buffer_head *bh;
+ if (INODE_VERSION(inode) == MINIX_V1) {
+ struct minix_inode *raw_inode;
+ raw_inode = minix_V1_raw_inode(inode->i_sb, inode->i_ino, &bh);
+ if (raw_inode) {
+ raw_inode->i_nlinks = 0;
+ raw_inode->i_mode = 0;
+ }
+ } else {
+ struct minix2_inode *raw_inode;
+ raw_inode = minix_V2_raw_inode(inode->i_sb, inode->i_ino, &bh);
+ if (raw_inode) {
+ raw_inode->i_nlinks = 0;
+ raw_inode->i_mode = 0;
+ }
+ }
+ if (bh) {
+ mark_buffer_dirty(bh);
+ brelse (bh);
+ }
+}
+
+void minix_free_inode(struct inode * inode)
+{
+ struct buffer_head * bh;
+ unsigned long ino;
+
+ if (inode->i_ino < 1 || inode->i_ino > inode->i_sb->u.minix_sb.s_ninodes) {
+ printk("free_inode: inode 0 or nonexistent inode\n");
+ return;
+ }
+ ino = inode->i_ino;
+ if ((ino >> 13) >= inode->i_sb->u.minix_sb.s_imap_blocks) {
+ printk("free_inode: nonexistent imap in superblock\n");
+ return;
+ }
+
+ bh = inode->i_sb->u.minix_sb.s_imap[ino >> 13];
+ minix_clear_inode(inode);
+ clear_inode(inode);
+ if (!minix_test_and_clear_bit(ino & 8191, bh->b_data))
+ printk("free_inode: bit %lu already cleared.\n",ino);
+ mark_buffer_dirty(bh);
+}
+
+struct inode * minix_new_inode(const struct inode * dir, int * error)
+{
+ struct super_block * sb;
+ struct inode * inode;
+ struct buffer_head * bh;
+ int i,j;
+
+ sb = dir->i_sb;
+ inode = new_inode(sb);
+ if (!inode) {
+ *error = -ENOMEM;
+ return NULL;
+ }
+ j = 8192;
+ bh = NULL;
+ *error = -ENOSPC;
+ lock_super(sb);
+ for (i = 0; i < sb->u.minix_sb.s_imap_blocks; i++) {
+ bh = inode->i_sb->u.minix_sb.s_imap[i];
+ if ((j = minix_find_first_zero_bit(bh->b_data, 8192)) < 8192)
+ break;
+ }
+ if (!bh || j >= 8192) {
+ iput(inode);
+ unlock_super(sb);
+ return NULL;
+ }
+ if (minix_test_and_set_bit(j,bh->b_data)) { /* shouldn't happen */
+ printk("new_inode: bit already set");
+ iput(inode);
+ unlock_super(sb);
+ return NULL;
+ }
+ mark_buffer_dirty(bh);
+ j += i*8192;
+ if (!j || j > inode->i_sb->u.minix_sb.s_ninodes) {
+ iput(inode);
+ unlock_super(sb);
+ return NULL;
+ }
+ inode->i_uid = current->fsuid;
+ inode->i_gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid;
+ inode->i_ino = j;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+ inode->i_blocks = inode->i_blksize = 0;
+ insert_inode_hash(inode);
+ mark_inode_dirty(inode);
+
+ unlock_super(sb);
+ *error = 0;
+ return inode;
+}
+
+unsigned long minix_count_free_inodes(struct super_block *sb)
+{
+ return count_free(sb->u.minix_sb.s_imap, sb->u.minix_sb.s_imap_blocks,
+ sb->u.minix_sb.s_ninodes + 1);
+}
diff --git a/fs/minix/dir.c b/fs/minix/dir.c
new file mode 100644
index 00000000000000..c363a6d009250c
--- /dev/null
+++ b/fs/minix/dir.c
@@ -0,0 +1,378 @@
+/*
+ * linux/fs/minix/dir.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * minix directory handling functions
+ */
+
+#include <linux/fs.h>
+#include <linux/minix_fs.h>
+#include <linux/pagemap.h>
+
+typedef struct minix_dir_entry minix_dirent;
+
+static int minix_readdir(struct file *, void *, filldir_t);
+
+struct file_operations minix_dir_operations = {
+ read: generic_read_dir,
+ readdir: minix_readdir,
+ fsync: minix_sync_file,
+};
+
+static inline void dir_put_page(struct page *page)
+{
+ kunmap(page);
+ page_cache_release(page);
+}
+
+static inline unsigned long dir_pages(struct inode *inode)
+{
+ return (inode->i_size+PAGE_CACHE_SIZE-1)>>PAGE_CACHE_SHIFT;
+}
+
+static int dir_commit_chunk(struct page *page, unsigned from, unsigned to)
+{
+ struct inode *dir = (struct inode *)page->mapping->host;
+ int err = 0;
+ page->mapping->a_ops->commit_write(NULL, page, from, to);
+ if (IS_SYNC(dir)) {
+ int err2;
+ err = writeout_one_page(page);
+ err2 = waitfor_one_page(page);
+ if (err == 0)
+ err = err2;
+ }
+ return err;
+}
+
+static struct page * dir_get_page(struct inode *dir, unsigned long n)
+{
+ struct address_space *mapping = dir->i_mapping;
+ struct page *page = read_cache_page(mapping, n,
+ (filler_t*)mapping->a_ops->readpage, NULL);
+ if (!IS_ERR(page)) {
+ wait_on_page(page);
+ kmap(page);
+ if (!Page_Uptodate(page))
+ goto fail;
+ }
+ return page;
+
+fail:
+ dir_put_page(page);
+ return ERR_PTR(-EIO);
+}
+
+static inline void *minix_next_entry(void *de, struct minix_sb_info *sbi)
+{
+ return (void*)((char*)de + sbi->s_dirsize);
+}
+
+static int minix_readdir(struct file * filp, void * dirent, filldir_t filldir)
+{
+ unsigned long pos = filp->f_pos;
+ struct inode *inode = filp->f_dentry->d_inode;
+ struct super_block *sb = inode->i_sb;
+ unsigned offset = pos & ~PAGE_CACHE_MASK;
+ unsigned long n = pos >> PAGE_CACHE_SHIFT;
+ unsigned long npages = dir_pages(inode);
+ struct minix_sb_info *sbi = &sb->u.minix_sb;
+ unsigned chunk_size = sbi->s_dirsize;
+
+ pos = (pos + chunk_size-1) & ~(chunk_size-1);
+ if (pos >= inode->i_size)
+ goto done;
+
+ for ( ; n < npages; n++, offset = 0) {
+ char *p, *kaddr, *limit;
+ struct page *page = dir_get_page(inode, n);
+
+ if (IS_ERR(page))
+ continue;
+ kaddr = (char *)page_address(page);
+ p = kaddr+offset;
+ limit = kaddr + PAGE_CACHE_SIZE - chunk_size;
+ for ( ; p <= limit ; p = minix_next_entry(p, sbi)) {
+ minix_dirent *de = (minix_dirent *)p;
+ if (de->inode) {
+ int over;
+ unsigned l = strnlen(de->name,sbi->s_namelen);
+
+ offset = p - kaddr;
+ over = filldir(dirent, de->name, l,
+ (n<<PAGE_CACHE_SHIFT) | offset,
+ de->inode, DT_UNKNOWN);
+ if (over) {
+ dir_put_page(page);
+ goto done;
+ }
+ }
+ }
+ dir_put_page(page);
+ }
+
+done:
+ filp->f_pos = (n << PAGE_CACHE_SHIFT) | offset;
+ UPDATE_ATIME(inode);
+ return 0;
+}
+
+static inline int namecompare(int len, int maxlen,
+ const char * name, const char * buffer)
+{
+ if (len < maxlen && buffer[len])
+ return 0;
+ return !memcmp(name, buffer, len);
+}
+
+/*
+ * minix_find_entry()
+ *
+ * finds an entry in the specified directory with the wanted name. It
+ * returns the cache buffer in which the entry was found, and the entry
+ * itself (as a parameter - res_dir). It does NOT read the inode of the
+ * entry - you'll have to do that yourself if you want to.
+ */
+minix_dirent *minix_find_entry(struct dentry *dentry, struct page **res_page)
+{
+ const char * name = dentry->d_name.name;
+ int namelen = dentry->d_name.len;
+ struct inode * dir = dentry->d_parent->d_inode;
+ struct super_block * sb = dir->i_sb;
+ struct minix_sb_info * sbi = &sb->u.minix_sb;
+ unsigned long n;
+ unsigned long npages = dir_pages(dir);
+ struct page *page = NULL;
+ struct minix_dir_entry *de;
+
+ *res_page = NULL;
+
+ for (n = 0; n < npages; n++) {
+ char *kaddr;
+ page = dir_get_page(dir, n);
+ if (IS_ERR(page))
+ continue;
+
+ kaddr = (char*)page_address(page);
+ de = (struct minix_dir_entry *) kaddr;
+ kaddr += PAGE_CACHE_SIZE - sbi->s_dirsize;
+ for ( ; (char *) de <= kaddr ; de = minix_next_entry(de,sbi)) {
+ if (!de->inode)
+ continue;
+ if (namecompare(namelen,sbi->s_namelen,name,de->name))
+ goto found;
+ }
+ dir_put_page(page);
+ }
+ return NULL;
+
+found:
+ *res_page = page;
+ return de;
+}
+
+int minix_add_link(struct dentry *dentry, struct inode *inode)
+{
+ struct inode *dir = dentry->d_parent->d_inode;
+ const char * name = dentry->d_name.name;
+ int namelen = dentry->d_name.len;
+ struct super_block * sb = dir->i_sb;
+ struct minix_sb_info * sbi = &sb->u.minix_sb;
+ struct page *page = NULL;
+ struct minix_dir_entry * de;
+ unsigned long npages = dir_pages(dir);
+ unsigned long n;
+ char *kaddr;
+ unsigned from, to;
+ int err;
+
+ /* We take care of directory expansion in the same loop */
+ for (n = 0; n <= npages; n++) {
+ page = dir_get_page(dir, n);
+ err = PTR_ERR(page);
+ if (IS_ERR(page))
+ goto out;
+ kaddr = (char*)page_address(page);
+ de = (minix_dirent *)kaddr;
+ kaddr += PAGE_CACHE_SIZE - sbi->s_dirsize;
+ while ((char *)de <= kaddr) {
+ if (!de->inode)
+ goto got_it;
+ err = -EEXIST;
+ if (namecompare(namelen,sbi->s_namelen,name,de->name))
+ goto out_page;
+ de = minix_next_entry(de, sbi);
+ }
+ dir_put_page(page);
+ }
+ BUG();
+ return -EINVAL;
+
+got_it:
+ from = (char*)de - (char*)page_address(page);
+ to = from + sbi->s_dirsize;
+ lock_page(page);
+ err = page->mapping->a_ops->prepare_write(NULL, page, from, to);
+ if (err)
+ goto out_unlock;
+ memcpy (de->name, name, namelen);
+ memset (de->name + namelen, 0, sbi->s_dirsize - namelen - 2);
+ de->inode = inode->i_ino;
+ err = dir_commit_chunk(page, from, to);
+ dir->i_mtime = dir->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(dir);
+out_unlock:
+ UnlockPage(page);
+out_page:
+ dir_put_page(page);
+out:
+ return err;
+}
+
+int minix_delete_entry(struct minix_dir_entry *de, struct page *page)
+{
+ struct address_space *mapping = page->mapping;
+ struct inode *inode = (struct inode*)mapping->host;
+ char *kaddr = (char*)page_address(page);
+ unsigned from = (char*)de - kaddr;
+ unsigned to = from + inode->i_sb->u.minix_sb.s_dirsize;
+ int err;
+
+ lock_page(page);
+ err = mapping->a_ops->prepare_write(NULL, page, from, to);
+ if (err == 0) {
+ de->inode = 0;
+ err = dir_commit_chunk(page, from, to);
+ }
+ UnlockPage(page);
+ dir_put_page(page);
+ inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+ return err;
+}
+
+int minix_make_empty(struct inode *inode, struct inode *dir)
+{
+ struct address_space *mapping = inode->i_mapping;
+ struct page *page = grab_cache_page(mapping, 0);
+ struct minix_sb_info * sbi = &inode->i_sb->u.minix_sb;
+ struct minix_dir_entry * de;
+ char *base;
+ int err;
+
+ if (!page)
+ return -ENOMEM;
+ err = mapping->a_ops->prepare_write(NULL, page, 0, 2 * sbi->s_dirsize);
+ if (err)
+ goto fail;
+
+ base = (char*)page_address(page);
+ memset(base, 0, PAGE_CACHE_SIZE);
+
+ de = (struct minix_dir_entry *) base;
+ de->inode = inode->i_ino;
+ strcpy(de->name,".");
+ de = minix_next_entry(de, sbi);
+ de->inode = dir->i_ino;
+ strcpy(de->name,"..");
+
+ err = dir_commit_chunk(page, 0, 2 * sbi->s_dirsize);
+fail:
+ UnlockPage(page);
+ page_cache_release(page);
+ return err;
+}
+
+/*
+ * routine to check that the specified directory is empty (for rmdir)
+ */
+int minix_empty_dir(struct inode * inode)
+{
+ struct page *page = NULL;
+ unsigned long i, npages = dir_pages(inode);
+ struct minix_sb_info *sbi = &inode->i_sb->u.minix_sb;
+
+ for (i = 0; i < npages; i++) {
+ char *kaddr;
+ minix_dirent * de;
+ page = dir_get_page(inode, i);
+
+ if (IS_ERR(page))
+ continue;
+
+ kaddr = (char *)page_address(page);
+ de = (minix_dirent *)kaddr;
+ kaddr += PAGE_CACHE_SIZE - sbi->s_dirsize;
+
+ while ((char *)de <= kaddr) {
+ if (de->inode != 0) {
+ /* check for . and .. */
+ if (de->name[0] != '.')
+ goto not_empty;
+ if (!de->name[1]) {
+ if (de->inode != inode->i_ino)
+ goto not_empty;
+ } else if (de->name[1] != '.')
+ goto not_empty;
+ else if (de->name[2])
+ goto not_empty;
+ }
+ de = minix_next_entry(de, sbi);
+ }
+ dir_put_page(page);
+ }
+ return 1;
+
+not_empty:
+ dir_put_page(page);
+ return 0;
+}
+
+/* Releases the page */
+void minix_set_link(struct minix_dir_entry *de, struct page *page,
+ struct inode *inode)
+{
+ struct inode *dir = (struct inode*)page->mapping->host;
+ struct minix_sb_info *sbi = &dir->i_sb->u.minix_sb;
+ unsigned from = (char *)de-(char*)page_address(page);
+ unsigned to = from + sbi->s_dirsize;
+ int err;
+
+ lock_page(page);
+ err = page->mapping->a_ops->prepare_write(NULL, page, from, to);
+ if (err == 0) {
+ de->inode = inode->i_ino;
+ err = dir_commit_chunk(page, from, to);
+ }
+ UnlockPage(page);
+ dir_put_page(page);
+ dir->i_mtime = dir->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(dir);
+}
+
+struct minix_dir_entry * minix_dotdot (struct inode *dir, struct page **p)
+{
+ struct page *page = dir_get_page(dir, 0);
+ struct minix_sb_info *sbi = &dir->i_sb->u.minix_sb;
+ struct minix_dir_entry *de = NULL;
+
+ if (!IS_ERR(page)) {
+ de = minix_next_entry(page_address(page), sbi);
+ *p = page;
+ }
+ return de;
+}
+
+ino_t minix_inode_by_name(struct dentry *dentry)
+{
+ struct page *page;
+ struct minix_dir_entry *de = minix_find_entry(dentry, &page);
+ ino_t res = 0;
+
+ if (de) {
+ res = de->inode;
+ dir_put_page(page);
+ }
+ return res;
+}
diff --git a/fs/minix/file.c b/fs/minix/file.c
new file mode 100644
index 00000000000000..ab8682902b2678
--- /dev/null
+++ b/fs/minix/file.c
@@ -0,0 +1,44 @@
+/*
+ * linux/fs/minix/file.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * minix regular file handling primitives
+ */
+
+#include <linux/fs.h>
+#include <linux/minix_fs.h>
+
+/*
+ * We have mostly NULLs here: the current defaults are OK for
+ * the minix filesystem.
+ */
+int minix_sync_file(struct file *, struct dentry *, int);
+
+struct file_operations minix_file_operations = {
+ llseek: generic_file_llseek,
+ read: generic_file_read,
+ write: generic_file_write,
+ mmap: generic_file_mmap,
+ fsync: minix_sync_file,
+};
+
+struct inode_operations minix_file_inode_operations = {
+ truncate: minix_truncate,
+};
+
+int minix_sync_file(struct file * file, struct dentry *dentry, int datasync)
+{
+ struct inode *inode = dentry->d_inode;
+ int err;
+
+ err = fsync_inode_buffers(inode);
+ err |= fsync_inode_data_buffers(inode);
+ if (!(inode->i_state & I_DIRTY))
+ return err;
+ if (datasync && !(inode->i_state & I_DIRTY_DATASYNC))
+ return err;
+
+ err |= minix_sync_inode(inode);
+ return err ? -EIO : 0;
+}
diff --git a/fs/minix/inode.c b/fs/minix/inode.c
new file mode 100644
index 00000000000000..4fcf2fa8a64200
--- /dev/null
+++ b/fs/minix/inode.c
@@ -0,0 +1,539 @@
+/*
+ * linux/fs/minix/inode.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * Copyright (C) 1996 Gertjan van Wingerde (gertjan@cs.vu.nl)
+ * Minix V2 fs support.
+ *
+ * Modified for 680x0 by Andreas Schwab
+ */
+
+#include <linux/module.h>
+
+#include <linux/fs.h>
+#include <linux/minix_fs.h>
+#include <linux/slab.h>
+#include <linux/locks.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+#include <linux/highuid.h>
+#include <linux/blkdev.h>
+
+static void minix_read_inode(struct inode * inode);
+static void minix_write_inode(struct inode * inode, int wait);
+static int minix_statfs(struct super_block *sb, struct statfs *buf);
+static int minix_remount (struct super_block * sb, int * flags, char * data);
+
+static void minix_delete_inode(struct inode *inode)
+{
+ lock_kernel();
+
+ inode->i_size = 0;
+ minix_truncate(inode);
+ minix_free_inode(inode);
+
+ unlock_kernel();
+}
+
+static void minix_commit_super(struct super_block * sb)
+{
+ mark_buffer_dirty(sb->u.minix_sb.s_sbh);
+ sb->s_dirt = 0;
+}
+
+static void minix_write_super(struct super_block * sb)
+{
+ struct minix_super_block * ms;
+
+ if (!(sb->s_flags & MS_RDONLY)) {
+ ms = sb->u.minix_sb.s_ms;
+
+ if (ms->s_state & MINIX_VALID_FS)
+ ms->s_state &= ~MINIX_VALID_FS;
+ minix_commit_super(sb);
+ }
+ sb->s_dirt = 0;
+}
+
+
+static void minix_put_super(struct super_block *sb)
+{
+ int i;
+
+ if (!(sb->s_flags & MS_RDONLY)) {
+ sb->u.minix_sb.s_ms->s_state = sb->u.minix_sb.s_mount_state;
+ mark_buffer_dirty(sb->u.minix_sb.s_sbh);
+ }
+ for (i = 0; i < sb->u.minix_sb.s_imap_blocks; i++)
+ brelse(sb->u.minix_sb.s_imap[i]);
+ for (i = 0; i < sb->u.minix_sb.s_zmap_blocks; i++)
+ brelse(sb->u.minix_sb.s_zmap[i]);
+ brelse (sb->u.minix_sb.s_sbh);
+ kfree(sb->u.minix_sb.s_imap);
+
+ return;
+}
+
+static struct super_operations minix_sops = {
+ read_inode: minix_read_inode,
+ write_inode: minix_write_inode,
+ delete_inode: minix_delete_inode,
+ put_super: minix_put_super,
+ write_super: minix_write_super,
+ statfs: minix_statfs,
+ remount_fs: minix_remount,
+};
+
+static int minix_remount (struct super_block * sb, int * flags, char * data)
+{
+ struct minix_super_block * ms;
+
+ ms = sb->u.minix_sb.s_ms;
+ if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
+ return 0;
+ if (*flags & MS_RDONLY) {
+ if (ms->s_state & MINIX_VALID_FS ||
+ !(sb->u.minix_sb.s_mount_state & MINIX_VALID_FS))
+ return 0;
+ /* Mounting a rw partition read-only. */
+ ms->s_state = sb->u.minix_sb.s_mount_state;
+ mark_buffer_dirty(sb->u.minix_sb.s_sbh);
+ sb->s_dirt = 1;
+ minix_commit_super(sb);
+ }
+ else {
+ /* Mount a partition which is read-only, read-write. */
+ sb->u.minix_sb.s_mount_state = ms->s_state;
+ ms->s_state &= ~MINIX_VALID_FS;
+ mark_buffer_dirty(sb->u.minix_sb.s_sbh);
+ sb->s_dirt = 1;
+
+ if (!(sb->u.minix_sb.s_mount_state & MINIX_VALID_FS))
+ printk ("MINIX-fs warning: remounting unchecked fs, "
+ "running fsck is recommended.\n");
+ else if ((sb->u.minix_sb.s_mount_state & MINIX_ERROR_FS))
+ printk ("MINIX-fs warning: remounting fs with errors, "
+ "running fsck is recommended.\n");
+ }
+ return 0;
+}
+
+static struct super_block *minix_read_super(struct super_block *s, void *data,
+ int silent)
+{
+ struct buffer_head *bh;
+ struct buffer_head **map;
+ struct minix_super_block *ms;
+ int i, block;
+ kdev_t dev = s->s_dev;
+ struct inode *root_inode;
+ unsigned int hblock;
+ struct minix_sb_info *sbi = &s->u.minix_sb;
+
+ /* N.B. These should be compile-time tests.
+ Unfortunately that is impossible. */
+ if (32 != sizeof (struct minix_inode))
+ panic("bad V1 i-node size");
+ if (64 != sizeof(struct minix2_inode))
+ panic("bad V2 i-node size");
+
+ hblock = get_hardsect_size(dev);
+ if (hblock > BLOCK_SIZE)
+ goto out_bad_hblock;
+
+ set_blocksize(dev, BLOCK_SIZE);
+ s->s_blocksize = BLOCK_SIZE;
+ s->s_blocksize_bits = BLOCK_SIZE_BITS;
+ if (!(bh = sb_bread(s, 1)))
+ goto out_bad_sb;
+
+ ms = (struct minix_super_block *) bh->b_data;
+ sbi->s_ms = ms;
+ sbi->s_sbh = bh;
+ sbi->s_mount_state = ms->s_state;
+ sbi->s_ninodes = ms->s_ninodes;
+ sbi->s_nzones = ms->s_nzones;
+ sbi->s_imap_blocks = ms->s_imap_blocks;
+ sbi->s_zmap_blocks = ms->s_zmap_blocks;
+ sbi->s_firstdatazone = ms->s_firstdatazone;
+ sbi->s_log_zone_size = ms->s_log_zone_size;
+ sbi->s_max_size = ms->s_max_size;
+ s->s_magic = ms->s_magic;
+ if (s->s_magic == MINIX_SUPER_MAGIC) {
+ sbi->s_version = MINIX_V1;
+ sbi->s_dirsize = 16;
+ sbi->s_namelen = 14;
+ sbi->s_link_max = MINIX_LINK_MAX;
+ } else if (s->s_magic == MINIX_SUPER_MAGIC2) {
+ sbi->s_version = MINIX_V1;
+ sbi->s_dirsize = 32;
+ sbi->s_namelen = 30;
+ sbi->s_link_max = MINIX_LINK_MAX;
+ } else if (s->s_magic == MINIX2_SUPER_MAGIC) {
+ sbi->s_version = MINIX_V2;
+ sbi->s_nzones = ms->s_zones;
+ sbi->s_dirsize = 16;
+ sbi->s_namelen = 14;
+ sbi->s_link_max = MINIX2_LINK_MAX;
+ } else if (s->s_magic == MINIX2_SUPER_MAGIC2) {
+ sbi->s_version = MINIX_V2;
+ sbi->s_nzones = ms->s_zones;
+ sbi->s_dirsize = 32;
+ sbi->s_namelen = 30;
+ sbi->s_link_max = MINIX2_LINK_MAX;
+ } else
+ goto out_no_fs;
+
+ /*
+ * Allocate the buffer map to keep the superblock small.
+ */
+ i = (sbi->s_imap_blocks + sbi->s_zmap_blocks) * sizeof(bh);
+ map = kmalloc(i, GFP_KERNEL);
+ if (!map)
+ goto out_no_map;
+ memset(map, 0, i);
+ sbi->s_imap = &map[0];
+ sbi->s_zmap = &map[sbi->s_imap_blocks];
+
+ block=2;
+ for (i=0 ; i < sbi->s_imap_blocks ; i++) {
+ if (!(sbi->s_imap[i]=sb_bread(s, block)))
+ goto out_no_bitmap;
+ block++;
+ }
+ for (i=0 ; i < sbi->s_zmap_blocks ; i++) {
+ if (!(sbi->s_zmap[i]=sb_bread(s, block)))
+ goto out_no_bitmap;
+ block++;
+ }
+
+ minix_set_bit(0,sbi->s_imap[0]->b_data);
+ minix_set_bit(0,sbi->s_zmap[0]->b_data);
+
+ /* set up enough so that it can read an inode */
+ s->s_op = &minix_sops;
+ root_inode = iget(s, MINIX_ROOT_INO);
+ if (!root_inode || is_bad_inode(root_inode))
+ goto out_no_root;
+
+ s->s_root = d_alloc_root(root_inode);
+ if (!s->s_root)
+ goto out_iput;
+
+ if (!NO_TRUNCATE)
+ s->s_root->d_op = &minix_dentry_operations;
+
+ if (!(s->s_flags & MS_RDONLY)) {
+ ms->s_state &= ~MINIX_VALID_FS;
+ mark_buffer_dirty(bh);
+ s->s_dirt = 1;
+ }
+ if (!(sbi->s_mount_state & MINIX_VALID_FS))
+ printk ("MINIX-fs: mounting unchecked file system, "
+ "running fsck is recommended.\n");
+ else if (sbi->s_mount_state & MINIX_ERROR_FS)
+ printk ("MINIX-fs: mounting file system with errors, "
+ "running fsck is recommended.\n");
+ return s;
+
+out_iput:
+ iput(root_inode);
+ goto out_freemap;
+
+out_no_root:
+ if (!silent)
+ printk("MINIX-fs: get root inode failed\n");
+ goto out_freemap;
+
+out_no_bitmap:
+ printk("MINIX-fs: bad superblock or unable to read bitmaps\n");
+ out_freemap:
+ for (i = 0; i < sbi->s_imap_blocks; i++)
+ brelse(sbi->s_imap[i]);
+ for (i = 0; i < sbi->s_zmap_blocks; i++)
+ brelse(sbi->s_zmap[i]);
+ kfree(sbi->s_imap);
+ goto out_release;
+
+out_no_map:
+ if (!silent)
+ printk ("MINIX-fs: can't allocate map\n");
+ goto out_release;
+
+out_no_fs:
+ if (!silent)
+ printk("VFS: Can't find a Minix or Minix V2 filesystem on device "
+ "%s.\n", kdevname(dev));
+ out_release:
+ brelse(bh);
+ goto out;
+
+out_bad_hblock:
+ printk("MINIX-fs: blocksize too small for device.\n");
+ goto out;
+
+out_bad_sb:
+ printk("MINIX-fs: unable to read superblock\n");
+ out:
+ return NULL;
+}
+
+static int minix_statfs(struct super_block *sb, struct statfs *buf)
+{
+ buf->f_type = sb->s_magic;
+ buf->f_bsize = sb->s_blocksize;
+ buf->f_blocks = (sb->u.minix_sb.s_nzones - sb->u.minix_sb.s_firstdatazone) << sb->u.minix_sb.s_log_zone_size;
+ buf->f_bfree = minix_count_free_blocks(sb);
+ buf->f_bavail = buf->f_bfree;
+ buf->f_files = sb->u.minix_sb.s_ninodes;
+ buf->f_ffree = minix_count_free_inodes(sb);
+ buf->f_namelen = sb->u.minix_sb.s_namelen;
+ return 0;
+}
+
+static int minix_get_block(struct inode *inode, long block,
+ struct buffer_head *bh_result, int create)
+{
+ if (INODE_VERSION(inode) == MINIX_V1)
+ return V1_minix_get_block(inode, block, bh_result, create);
+ else
+ return V2_minix_get_block(inode, block, bh_result, create);
+}
+
+static int minix_writepage(struct page *page)
+{
+ return block_write_full_page(page,minix_get_block);
+}
+static int minix_readpage(struct file *file, struct page *page)
+{
+ return block_read_full_page(page,minix_get_block);
+}
+static int minix_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
+{
+ return block_prepare_write(page,from,to,minix_get_block);
+}
+static int minix_bmap(struct address_space *mapping, long block)
+{
+ return generic_block_bmap(mapping,block,minix_get_block);
+}
+static struct address_space_operations minix_aops = {
+ readpage: minix_readpage,
+ writepage: minix_writepage,
+ sync_page: block_sync_page,
+ prepare_write: minix_prepare_write,
+ commit_write: generic_commit_write,
+ bmap: minix_bmap
+};
+
+void minix_set_inode(struct inode *inode, dev_t rdev)
+{
+ if (S_ISREG(inode->i_mode)) {
+ inode->i_op = &minix_file_inode_operations;
+ inode->i_fop = &minix_file_operations;
+ inode->i_mapping->a_ops = &minix_aops;
+ } else if (S_ISDIR(inode->i_mode)) {
+ inode->i_op = &minix_dir_inode_operations;
+ inode->i_fop = &minix_dir_operations;
+ inode->i_mapping->a_ops = &minix_aops;
+ } else if (S_ISLNK(inode->i_mode)) {
+ inode->i_op = &page_symlink_inode_operations;
+ inode->i_mapping->a_ops = &minix_aops;
+ } else
+ init_special_inode(inode, inode->i_mode, rdev);
+}
+
+/*
+ * The minix V1 function to read an inode.
+ */
+static void V1_minix_read_inode(struct inode * inode)
+{
+ struct buffer_head * bh;
+ struct minix_inode * raw_inode;
+ int i;
+
+ raw_inode = minix_V1_raw_inode(inode->i_sb, inode->i_ino, &bh);
+ if (!raw_inode) {
+ make_bad_inode(inode);
+ return;
+ }
+ inode->i_mode = raw_inode->i_mode;
+ inode->i_uid = (uid_t)raw_inode->i_uid;
+ inode->i_gid = (gid_t)raw_inode->i_gid;
+ inode->i_nlink = raw_inode->i_nlinks;
+ inode->i_size = raw_inode->i_size;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = raw_inode->i_time;
+ inode->i_blocks = inode->i_blksize = 0;
+ for (i = 0; i < 9; i++)
+ inode->u.minix_i.u.i1_data[i] = raw_inode->i_zone[i];
+ minix_set_inode(inode, raw_inode->i_zone[0]);
+ brelse(bh);
+}
+
+/*
+ * The minix V2 function to read an inode.
+ */
+static void V2_minix_read_inode(struct inode * inode)
+{
+ struct buffer_head * bh;
+ struct minix2_inode * raw_inode;
+ int i;
+
+ raw_inode = minix_V2_raw_inode(inode->i_sb, inode->i_ino, &bh);
+ if (!raw_inode) {
+ make_bad_inode(inode);
+ return;
+ }
+ inode->i_mode = raw_inode->i_mode;
+ inode->i_uid = (uid_t)raw_inode->i_uid;
+ inode->i_gid = (gid_t)raw_inode->i_gid;
+ inode->i_nlink = raw_inode->i_nlinks;
+ inode->i_size = raw_inode->i_size;
+ inode->i_mtime = raw_inode->i_mtime;
+ inode->i_atime = raw_inode->i_atime;
+ inode->i_ctime = raw_inode->i_ctime;
+ inode->i_blocks = inode->i_blksize = 0;
+ for (i = 0; i < 10; i++)
+ inode->u.minix_i.u.i2_data[i] = raw_inode->i_zone[i];
+ minix_set_inode(inode, raw_inode->i_zone[0]);
+ brelse(bh);
+}
+
+/*
+ * The global function to read an inode.
+ */
+static void minix_read_inode(struct inode * inode)
+{
+ if (INODE_VERSION(inode) == MINIX_V1)
+ V1_minix_read_inode(inode);
+ else
+ V2_minix_read_inode(inode);
+}
+
+/*
+ * The minix V1 function to synchronize an inode.
+ */
+static struct buffer_head * V1_minix_update_inode(struct inode * inode)
+{
+ struct buffer_head * bh;
+ struct minix_inode * raw_inode;
+ int i;
+
+ raw_inode = minix_V1_raw_inode(inode->i_sb, inode->i_ino, &bh);
+ if (!raw_inode)
+ return 0;
+ raw_inode->i_mode = inode->i_mode;
+ raw_inode->i_uid = fs_high2lowuid(inode->i_uid);
+ raw_inode->i_gid = fs_high2lowgid(inode->i_gid);
+ raw_inode->i_nlinks = inode->i_nlink;
+ raw_inode->i_size = inode->i_size;
+ raw_inode->i_time = inode->i_mtime;
+ if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+ raw_inode->i_zone[0] = kdev_t_to_nr(inode->i_rdev);
+ else for (i = 0; i < 9; i++)
+ raw_inode->i_zone[i] = inode->u.minix_i.u.i1_data[i];
+ mark_buffer_dirty(bh);
+ return bh;
+}
+
+/*
+ * The minix V2 function to synchronize an inode.
+ */
+static struct buffer_head * V2_minix_update_inode(struct inode * inode)
+{
+ struct buffer_head * bh;
+ struct minix2_inode * raw_inode;
+ int i;
+
+ raw_inode = minix_V2_raw_inode(inode->i_sb, inode->i_ino, &bh);
+ if (!raw_inode)
+ return 0;
+ raw_inode->i_mode = inode->i_mode;
+ raw_inode->i_uid = fs_high2lowuid(inode->i_uid);
+ raw_inode->i_gid = fs_high2lowgid(inode->i_gid);
+ raw_inode->i_nlinks = inode->i_nlink;
+ raw_inode->i_size = inode->i_size;
+ raw_inode->i_mtime = inode->i_mtime;
+ raw_inode->i_atime = inode->i_atime;
+ raw_inode->i_ctime = inode->i_ctime;
+ if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+ raw_inode->i_zone[0] = kdev_t_to_nr(inode->i_rdev);
+ else for (i = 0; i < 10; i++)
+ raw_inode->i_zone[i] = inode->u.minix_i.u.i2_data[i];
+ mark_buffer_dirty(bh);
+ return bh;
+}
+
+static struct buffer_head *minix_update_inode(struct inode *inode)
+{
+ if (INODE_VERSION(inode) == MINIX_V1)
+ return V1_minix_update_inode(inode);
+ else
+ return V2_minix_update_inode(inode);
+}
+
+static void minix_write_inode(struct inode * inode, int wait)
+{
+ struct buffer_head *bh;
+
+ lock_kernel();
+ bh = minix_update_inode(inode);
+ unlock_kernel();
+ brelse(bh);
+}
+
+int minix_sync_inode(struct inode * inode)
+{
+ int err = 0;
+ struct buffer_head *bh;
+
+ bh = minix_update_inode(inode);
+ if (bh && buffer_dirty(bh))
+ {
+ ll_rw_block(WRITE, 1, &bh);
+ wait_on_buffer(bh);
+ if (buffer_req(bh) && !buffer_uptodate(bh))
+ {
+ printk ("IO error syncing minix inode ["
+ "%s:%08lx]\n",
+ kdevname(inode->i_dev), inode->i_ino);
+ err = -1;
+ }
+ }
+ else if (!bh)
+ err = -1;
+ brelse (bh);
+ return err;
+}
+
+/*
+ * The function that is called for file truncation.
+ */
+void minix_truncate(struct inode * inode)
+{
+ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)))
+ return;
+ if (INODE_VERSION(inode) == MINIX_V1)
+ V1_minix_truncate(inode);
+ else
+ V2_minix_truncate(inode);
+}
+
+static DECLARE_FSTYPE_DEV(minix_fs_type,"minix",minix_read_super);
+
+static int __init init_minix_fs(void)
+{
+ return register_filesystem(&minix_fs_type);
+}
+
+static void __exit exit_minix_fs(void)
+{
+ unregister_filesystem(&minix_fs_type);
+}
+
+EXPORT_NO_SYMBOLS;
+
+module_init(init_minix_fs)
+module_exit(exit_minix_fs)
+MODULE_LICENSE("GPL");
+
diff --git a/fs/minix/itree_common.c b/fs/minix/itree_common.c
new file mode 100644
index 00000000000000..89da206c38b901
--- /dev/null
+++ b/fs/minix/itree_common.c
@@ -0,0 +1,347 @@
+/* Generic part */
+
+typedef struct {
+ block_t *p;
+ block_t key;
+ struct buffer_head *bh;
+} Indirect;
+
+static inline void add_chain(Indirect *p, struct buffer_head *bh, block_t *v)
+{
+ p->key = *(p->p = v);
+ p->bh = bh;
+}
+
+static inline int verify_chain(Indirect *from, Indirect *to)
+{
+ while (from <= to && from->key == *from->p)
+ from++;
+ return (from > to);
+}
+
+static inline block_t *block_end(struct buffer_head *bh)
+{
+ return (block_t *)((char*)bh->b_data + BLOCK_SIZE);
+}
+
+static inline Indirect *get_branch(struct inode *inode,
+ int depth,
+ int *offsets,
+ Indirect chain[DEPTH],
+ int *err)
+{
+ struct super_block *sb = inode->i_sb;
+ Indirect *p = chain;
+ struct buffer_head *bh;
+
+ *err = 0;
+ /* i_data is not going away, no lock needed */
+ add_chain (chain, NULL, i_data(inode) + *offsets);
+ if (!p->key)
+ goto no_block;
+ while (--depth) {
+ bh = sb_bread(sb, block_to_cpu(p->key));
+ if (!bh)
+ goto failure;
+ /* Reader: pointers */
+ if (!verify_chain(chain, p))
+ goto changed;
+ add_chain(++p, bh, (block_t *)bh->b_data + *++offsets);
+ /* Reader: end */
+ if (!p->key)
+ goto no_block;
+ }
+ return NULL;
+
+changed:
+ *err = -EAGAIN;
+ goto no_block;
+failure:
+ *err = -EIO;
+no_block:
+ return p;
+}
+
+static int alloc_branch(struct inode *inode,
+ int num,
+ int *offsets,
+ Indirect *branch)
+{
+ int n = 0;
+ int i;
+ int parent = minix_new_block(inode);
+
+ branch[0].key = cpu_to_block(parent);
+ if (parent) for (n = 1; n < num; n++) {
+ struct buffer_head *bh;
+ /* Allocate the next block */
+ int nr = minix_new_block(inode);
+ if (!nr)
+ break;
+ branch[n].key = cpu_to_block(nr);
+ bh = sb_getblk(inode->i_sb, parent);
+ lock_buffer(bh);
+ memset(bh->b_data, 0, BLOCK_SIZE);
+ branch[n].bh = bh;
+ branch[n].p = (block_t*) bh->b_data + offsets[n];
+ *branch[n].p = branch[n].key;
+ mark_buffer_uptodate(bh, 1);
+ unlock_buffer(bh);
+ mark_buffer_dirty_inode(bh, inode);
+ parent = nr;
+ }
+ if (n == num)
+ return 0;
+
+ /* Allocation failed, free what we already allocated */
+ for (i = 1; i < n; i++)
+ bforget(branch[i].bh);
+ for (i = 0; i < n; i++)
+ minix_free_block(inode, block_to_cpu(branch[i].key));
+ return -ENOSPC;
+}
+
+static inline int splice_branch(struct inode *inode,
+ Indirect chain[DEPTH],
+ Indirect *where,
+ int num)
+{
+ int i;
+
+ /* Verify that place we are splicing to is still there and vacant */
+
+ /* Writer: pointers */
+ if (!verify_chain(chain, where-1) || *where->p)
+ /* Writer: end */
+ goto changed;
+
+ /* That's it */
+
+ *where->p = where->key;
+
+ /* Writer: end */
+
+ /* We are done with atomic stuff, now do the rest of housekeeping */
+
+ inode->i_ctime = CURRENT_TIME;
+
+ /* had we spliced it onto indirect block? */
+ if (where->bh)
+ mark_buffer_dirty_inode(where->bh, inode);
+
+ mark_inode_dirty(inode);
+ return 0;
+
+changed:
+ for (i = 1; i < num; i++)
+ bforget(where[i].bh);
+ for (i = 0; i < num; i++)
+ minix_free_block(inode, block_to_cpu(where[i].key));
+ return -EAGAIN;
+}
+
+static inline int get_block(struct inode * inode, long block,
+ struct buffer_head *bh_result, int create)
+{
+ int err = -EIO;
+ int offsets[DEPTH];
+ Indirect chain[DEPTH];
+ Indirect *partial;
+ int left;
+ int depth = block_to_path(inode, block, offsets);
+
+ if (depth == 0)
+ goto out;
+
+ lock_kernel();
+reread:
+ partial = get_branch(inode, depth, offsets, chain, &err);
+
+ /* Simplest case - block found, no allocation needed */
+ if (!partial) {
+got_it:
+ bh_result->b_dev = inode->i_dev;
+ bh_result->b_blocknr = block_to_cpu(chain[depth-1].key);
+ bh_result->b_state |= (1UL << BH_Mapped);
+ /* Clean up and exit */
+ partial = chain+depth-1; /* the whole chain */
+ goto cleanup;
+ }
+
+ /* Next simple case - plain lookup or failed read of indirect block */
+ if (!create || err == -EIO) {
+cleanup:
+ while (partial > chain) {
+ brelse(partial->bh);
+ partial--;
+ }
+ unlock_kernel();
+out:
+ return err;
+ }
+
+ /*
+ * Indirect block might be removed by truncate while we were
+ * reading it. Handling of that case (forget what we've got and
+ * reread) is taken out of the main path.
+ */
+ if (err == -EAGAIN)
+ goto changed;
+
+ left = (chain + depth) - partial;
+ err = alloc_branch(inode, left, offsets+(partial-chain), partial);
+ if (err)
+ goto cleanup;
+
+ if (splice_branch(inode, chain, partial, left) < 0)
+ goto changed;
+
+ bh_result->b_state |= (1UL << BH_New);
+ goto got_it;
+
+changed:
+ while (partial > chain) {
+ brelse(partial->bh);
+ partial--;
+ }
+ goto reread;
+}
+
+static inline int all_zeroes(block_t *p, block_t *q)
+{
+ while (p < q)
+ if (*p++)
+ return 0;
+ return 1;
+}
+
+static Indirect *find_shared(struct inode *inode,
+ int depth,
+ int offsets[DEPTH],
+ Indirect chain[DEPTH],
+ block_t *top)
+{
+ Indirect *partial, *p;
+ int k, err;
+
+ *top = 0;
+ for (k = depth; k > 1 && !offsets[k-1]; k--)
+ ;
+ partial = get_branch(inode, k, offsets, chain, &err);
+ /* Writer: pointers */
+ if (!partial)
+ partial = chain + k-1;
+ if (!partial->key && *partial->p)
+ /* Writer: end */
+ goto no_top;
+ for (p=partial;p>chain && all_zeroes((block_t*)p->bh->b_data,p->p);p--)
+ ;
+ if (p == chain + k - 1 && p > chain) {
+ p->p--;
+ } else {
+ *top = *p->p;
+ *p->p = 0;
+ }
+ /* Writer: end */
+
+ while(partial > p)
+ {
+ brelse(partial->bh);
+ partial--;
+ }
+no_top:
+ return partial;
+}
+
+static inline void free_data(struct inode *inode, block_t *p, block_t *q)
+{
+ unsigned long nr;
+
+ for ( ; p < q ; p++) {
+ nr = block_to_cpu(*p);
+ if (nr) {
+ *p = 0;
+ minix_free_block(inode, nr);
+ }
+ }
+}
+
+static void free_branches(struct inode *inode, block_t *p, block_t *q, int depth)
+{
+ struct buffer_head * bh;
+ unsigned long nr;
+
+ if (depth--) {
+ for ( ; p < q ; p++) {
+ nr = block_to_cpu(*p);
+ if (!nr)
+ continue;
+ *p = 0;
+ bh = sb_bread(inode->i_sb, nr);
+ if (!bh)
+ continue;
+ free_branches(inode, (block_t*)bh->b_data,
+ block_end(bh), depth);
+ bforget(bh);
+ minix_free_block(inode, nr);
+ mark_inode_dirty(inode);
+ }
+ } else
+ free_data(inode, p, q);
+}
+
+static inline void truncate (struct inode * inode)
+{
+ block_t *idata = i_data(inode);
+ int offsets[DEPTH];
+ Indirect chain[DEPTH];
+ Indirect *partial;
+ block_t nr = 0;
+ int n;
+ int first_whole;
+ long iblock;
+
+ iblock = (inode->i_size + BLOCK_SIZE-1) >> 10;
+ block_truncate_page(inode->i_mapping, inode->i_size, get_block);
+
+ n = block_to_path(inode, iblock, offsets);
+ if (!n)
+ return;
+
+ if (n == 1) {
+ free_data(inode, idata+offsets[0], idata + DIRECT);
+ first_whole = 0;
+ goto do_indirects;
+ }
+
+ first_whole = offsets[0] + 1 - DIRECT;
+ partial = find_shared(inode, n, offsets, chain, &nr);
+ if (nr) {
+ if (partial == chain)
+ mark_inode_dirty(inode);
+ else
+ mark_buffer_dirty_inode(partial->bh, inode);
+ free_branches(inode, &nr, &nr+1, (chain+n-1) - partial);
+ }
+ /* Clear the ends of indirect blocks on the shared branch */
+ while (partial > chain) {
+ free_branches(inode, partial->p + 1, block_end(partial->bh),
+ (chain+n-1) - partial);
+ mark_buffer_dirty_inode(partial->bh, inode);
+ brelse (partial->bh);
+ partial--;
+ }
+do_indirects:
+ /* Kill the remaining (whole) subtrees */
+ while (first_whole < DEPTH-1) {
+ nr = idata[DIRECT+first_whole];
+ if (nr) {
+ idata[DIRECT+first_whole] = 0;
+ mark_inode_dirty(inode);
+ free_branches(inode, &nr, &nr+1, first_whole+1);
+ }
+ first_whole++;
+ }
+ inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+}
diff --git a/fs/minix/itree_v1.c b/fs/minix/itree_v1.c
new file mode 100644
index 00000000000000..990dddd0d73e25
--- /dev/null
+++ b/fs/minix/itree_v1.c
@@ -0,0 +1,58 @@
+#include <linux/fs.h>
+#include <linux/minix_fs.h>
+#include <linux/locks.h>
+#include <linux/smp_lock.h>
+
+enum {DEPTH = 3, DIRECT = 7}; /* Only double indirect */
+
+typedef u16 block_t; /* 16 bit, host order */
+
+static inline unsigned long block_to_cpu(block_t n)
+{
+ return n;
+}
+
+static inline block_t cpu_to_block(unsigned long n)
+{
+ return n;
+}
+
+static inline block_t *i_data(struct inode *inode)
+{
+ return (block_t *)inode->u.minix_i.u.i1_data;
+}
+
+static int block_to_path(struct inode * inode, long block, int offsets[DEPTH])
+{
+ int n = 0;
+
+ if (block < 0) {
+ printk("minix_bmap: block<0");
+ } else if (block >= (inode->i_sb->u.minix_sb.s_max_size/BLOCK_SIZE)) {
+ printk("minix_bmap: block>big");
+ } else if (block < 7) {
+ offsets[n++] = block;
+ } else if ((block -= 7) < 512) {
+ offsets[n++] = 7;
+ offsets[n++] = block;
+ } else {
+ block -= 512;
+ offsets[n++] = 8;
+ offsets[n++] = block>>9;
+ offsets[n++] = block & 511;
+ }
+ return n;
+}
+
+#include "itree_common.c"
+
+int V1_minix_get_block(struct inode * inode, long block,
+ struct buffer_head *bh_result, int create)
+{
+ return get_block(inode, block, bh_result, create);
+}
+
+void V1_minix_truncate(struct inode * inode)
+{
+ truncate(inode);
+}
diff --git a/fs/minix/itree_v2.c b/fs/minix/itree_v2.c
new file mode 100644
index 00000000000000..bbff60d850fc8c
--- /dev/null
+++ b/fs/minix/itree_v2.c
@@ -0,0 +1,63 @@
+#include <linux/fs.h>
+#include <linux/minix_fs.h>
+#include <linux/locks.h>
+#include <linux/smp_lock.h>
+
+enum {DIRECT = 7, DEPTH = 4}; /* Have triple indirect */
+
+typedef u32 block_t; /* 32 bit, host order */
+
+static inline unsigned long block_to_cpu(block_t n)
+{
+ return n;
+}
+
+static inline block_t cpu_to_block(unsigned long n)
+{
+ return n;
+}
+
+static inline block_t *i_data(struct inode *inode)
+{
+ return (block_t *)inode->u.minix_i.u.i2_data;
+}
+
+static int block_to_path(struct inode * inode, long block, int offsets[DEPTH])
+{
+ int n = 0;
+
+ if (block < 0) {
+ printk("minix_bmap: block<0");
+ } else if (block >= (inode->i_sb->u.minix_sb.s_max_size/BLOCK_SIZE)) {
+ printk("minix_bmap: block>big");
+ } else if (block < 7) {
+ offsets[n++] = block;
+ } else if ((block -= 7) < 256) {
+ offsets[n++] = 7;
+ offsets[n++] = block;
+ } else if ((block -= 256) < 256*256) {
+ offsets[n++] = 8;
+ offsets[n++] = block>>8;
+ offsets[n++] = block & 255;
+ } else {
+ block -= 256*256;
+ offsets[n++] = 9;
+ offsets[n++] = block>>16;
+ offsets[n++] = (block>>8) & 255;
+ offsets[n++] = block & 255;
+ }
+ return n;
+}
+
+#include "itree_common.c"
+
+int V2_minix_get_block(struct inode * inode, long block,
+ struct buffer_head *bh_result, int create)
+{
+ return get_block(inode, block, bh_result, create);
+}
+
+void V2_minix_truncate(struct inode * inode)
+{
+ truncate(inode);
+}
diff --git a/fs/minix/namei.c b/fs/minix/namei.c
new file mode 100644
index 00000000000000..c0e9ea190812e5
--- /dev/null
+++ b/fs/minix/namei.c
@@ -0,0 +1,315 @@
+/*
+ * linux/fs/minix/namei.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+#include <linux/fs.h>
+#include <linux/minix_fs.h>
+#include <linux/pagemap.h>
+
+static inline void inc_count(struct inode *inode)
+{
+ inode->i_nlink++;
+ mark_inode_dirty(inode);
+}
+
+static inline void dec_count(struct inode *inode)
+{
+ inode->i_nlink--;
+ mark_inode_dirty(inode);
+}
+
+static int add_nondir(struct dentry *dentry, struct inode *inode)
+{
+ int err = minix_add_link(dentry, inode);
+ if (!err) {
+ d_instantiate(dentry, inode);
+ return 0;
+ }
+ dec_count(inode);
+ iput(inode);
+ return err;
+}
+
+static int minix_hash(struct dentry *dentry, struct qstr *qstr)
+{
+ unsigned long hash;
+ int i;
+ const unsigned char *name;
+
+ i = dentry->d_inode->i_sb->u.minix_sb.s_namelen;
+ if (i >= qstr->len)
+ return 0;
+ /* Truncate the name in place, avoids having to define a compare
+ function. */
+ qstr->len = i;
+ name = qstr->name;
+ hash = init_name_hash();
+ while (i--)
+ hash = partial_name_hash(*name++, hash);
+ qstr->hash = end_name_hash(hash);
+ return 0;
+}
+
+struct dentry_operations minix_dentry_operations = {
+ d_hash: minix_hash,
+};
+
+static struct dentry *minix_lookup(struct inode * dir, struct dentry *dentry)
+{
+ struct inode * inode = NULL;
+ ino_t ino;
+
+ dentry->d_op = dir->i_sb->s_root->d_op;
+
+ if (dentry->d_name.len > dir->i_sb->u.minix_sb.s_namelen)
+ return ERR_PTR(-ENAMETOOLONG);
+
+ ino = minix_inode_by_name(dentry);
+ if (ino) {
+ inode = iget(dir->i_sb, ino);
+
+ if (!inode)
+ return ERR_PTR(-EACCES);
+ }
+ d_add(dentry, inode);
+ return NULL;
+}
+
+static int minix_mknod(struct inode * dir, struct dentry *dentry, int mode, int rdev)
+{
+ int error;
+ struct inode * inode = minix_new_inode(dir, &error);
+
+ if (inode) {
+ inode->i_mode = mode;
+ minix_set_inode(inode, rdev);
+ mark_inode_dirty(inode);
+ error = add_nondir(dentry, inode);
+ }
+ return error;
+}
+
+static int minix_create(struct inode * dir, struct dentry *dentry, int mode)
+{
+ return minix_mknod(dir, dentry, mode, 0);
+}
+
+static int minix_symlink(struct inode * dir, struct dentry *dentry,
+ const char * symname)
+{
+ int err = -ENAMETOOLONG;
+ int i = strlen(symname)+1;
+ struct inode * inode;
+
+ if (i > dir->i_sb->s_blocksize)
+ goto out;
+
+ inode = minix_new_inode(dir, &err);
+ if (!inode)
+ goto out;
+
+ inode->i_mode = S_IFLNK | 0777;
+ minix_set_inode(inode, 0);
+ err = block_symlink(inode, symname, i);
+ if (err)
+ goto out_fail;
+
+ err = add_nondir(dentry, inode);
+out:
+ return err;
+
+out_fail:
+ dec_count(inode);
+ iput(inode);
+ goto out;
+}
+
+static int minix_link(struct dentry * old_dentry, struct inode * dir,
+ struct dentry *dentry)
+{
+ struct inode *inode = old_dentry->d_inode;
+
+ if (S_ISDIR(inode->i_mode))
+ return -EPERM;
+
+ if (inode->i_nlink >= inode->i_sb->u.minix_sb.s_link_max)
+ return -EMLINK;
+
+ inode->i_ctime = CURRENT_TIME;
+ inc_count(inode);
+ atomic_inc(&inode->i_count);
+ return add_nondir(dentry, inode);
+}
+
+static int minix_mkdir(struct inode * dir, struct dentry *dentry, int mode)
+{
+ struct inode * inode;
+ int err = -EMLINK;
+
+ if (dir->i_nlink >= dir->i_sb->u.minix_sb.s_link_max)
+ goto out;
+
+ inc_count(dir);
+
+ inode = minix_new_inode(dir, &err);
+ if (!inode)
+ goto out_dir;
+
+ inode->i_mode = S_IFDIR | mode;
+ if (dir->i_mode & S_ISGID)
+ inode->i_mode |= S_ISGID;
+ minix_set_inode(inode, 0);
+
+ inc_count(inode);
+
+ err = minix_make_empty(inode, dir);
+ if (err)
+ goto out_fail;
+
+ err = minix_add_link(dentry, inode);
+ if (err)
+ goto out_fail;
+
+ d_instantiate(dentry, inode);
+out:
+ return err;
+
+out_fail:
+ dec_count(inode);
+ dec_count(inode);
+ iput(inode);
+out_dir:
+ dec_count(dir);
+ goto out;
+}
+
+static int minix_unlink(struct inode * dir, struct dentry *dentry)
+{
+ int err = -ENOENT;
+ struct inode * inode = dentry->d_inode;
+ struct page * page;
+ struct minix_dir_entry * de;
+
+ de = minix_find_entry(dentry, &page);
+ if (!de)
+ goto end_unlink;
+
+ err = minix_delete_entry(de, page);
+ if (err)
+ goto end_unlink;
+
+ inode->i_ctime = dir->i_ctime;
+ dec_count(inode);
+end_unlink:
+ return err;
+}
+
+static int minix_rmdir(struct inode * dir, struct dentry *dentry)
+{
+ struct inode * inode = dentry->d_inode;
+ int err = -ENOTEMPTY;
+
+ if (minix_empty_dir(inode)) {
+ err = minix_unlink(dir, dentry);
+ if (!err) {
+ dec_count(dir);
+ dec_count(inode);
+ }
+ }
+ return err;
+}
+
+static int minix_rename(struct inode * old_dir, struct dentry *old_dentry,
+ struct inode * new_dir, struct dentry *new_dentry)
+{
+ struct minix_sb_info * info = &old_dir->i_sb->u.minix_sb;
+ struct inode * old_inode = old_dentry->d_inode;
+ struct inode * new_inode = new_dentry->d_inode;
+ struct page * dir_page = NULL;
+ struct minix_dir_entry * dir_de = NULL;
+ struct page * old_page;
+ struct minix_dir_entry * old_de;
+ int err = -ENOENT;
+
+ old_de = minix_find_entry(old_dentry, &old_page);
+ if (!old_de)
+ goto out;
+
+ if (S_ISDIR(old_inode->i_mode)) {
+ err = -EIO;
+ dir_de = minix_dotdot(old_inode, &dir_page);
+ if (!dir_de)
+ goto out_old;
+ }
+
+ if (new_inode) {
+ struct page * new_page;
+ struct minix_dir_entry * new_de;
+
+ err = -ENOTEMPTY;
+ if (dir_de && !minix_empty_dir(new_inode))
+ goto out_dir;
+
+ err = -ENOENT;
+ new_de = minix_find_entry(new_dentry, &new_page);
+ if (!new_de)
+ goto out_dir;
+ inc_count(old_inode);
+ minix_set_link(new_de, new_page, old_inode);
+ new_inode->i_ctime = CURRENT_TIME;
+ if (dir_de)
+ new_inode->i_nlink--;
+ dec_count(new_inode);
+ } else {
+ if (dir_de) {
+ err = -EMLINK;
+ if (new_dir->i_nlink >= info->s_link_max)
+ goto out_dir;
+ }
+ inc_count(old_inode);
+ err = minix_add_link(new_dentry, old_inode);
+ if (err) {
+ dec_count(old_inode);
+ goto out_dir;
+ }
+ if (dir_de)
+ inc_count(new_dir);
+ }
+
+ minix_delete_entry(old_de, old_page);
+ dec_count(old_inode);
+
+ if (dir_de) {
+ minix_set_link(dir_de, dir_page, new_dir);
+ dec_count(old_dir);
+ }
+ return 0;
+
+out_dir:
+ if (dir_de) {
+ kunmap(dir_page);
+ page_cache_release(dir_page);
+ }
+out_old:
+ kunmap(old_page);
+ page_cache_release(old_page);
+out:
+ return err;
+}
+
+/*
+ * directories can handle most operations...
+ */
+struct inode_operations minix_dir_inode_operations = {
+ create: minix_create,
+ lookup: minix_lookup,
+ link: minix_link,
+ unlink: minix_unlink,
+ symlink: minix_symlink,
+ mkdir: minix_mkdir,
+ rmdir: minix_rmdir,
+ mknod: minix_mknod,
+ rename: minix_rename,
+};
diff --git a/fs/msdos/Makefile b/fs/msdos/Makefile
new file mode 100644
index 00000000000000..a0424c507247d9
--- /dev/null
+++ b/fs/msdos/Makefile
@@ -0,0 +1,17 @@
+#
+# Makefile for the Linux msdos filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile.
+
+O_TARGET := msdos.o
+
+export-objs := msdosfs_syms.o
+
+obj-y := namei.o msdosfs_syms.o
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/msdos/msdosfs_syms.c b/fs/msdos/msdosfs_syms.c
new file mode 100644
index 00000000000000..38b2e0d6c70a01
--- /dev/null
+++ b/fs/msdos/msdosfs_syms.c
@@ -0,0 +1,43 @@
+/*
+ * linux/fs/msdos/msdosfs_syms.c
+ *
+ * Exported kernel symbols for the MS-DOS filesystem.
+ * These symbols are used by umsdos.
+ */
+
+#include <linux/module.h>
+
+#include <linux/mm.h>
+#include <linux/msdos_fs.h>
+#include <linux/init.h>
+
+/*
+ * Support for umsdos fs
+ *
+ * These symbols are _always_ exported, in case someone
+ * wants to install the umsdos module later.
+ */
+EXPORT_SYMBOL(msdos_create);
+EXPORT_SYMBOL(msdos_lookup);
+EXPORT_SYMBOL(msdos_mkdir);
+EXPORT_SYMBOL(msdos_rename);
+EXPORT_SYMBOL(msdos_rmdir);
+EXPORT_SYMBOL(msdos_unlink);
+EXPORT_SYMBOL(msdos_read_super);
+EXPORT_SYMBOL(msdos_put_super);
+
+static DECLARE_FSTYPE_DEV(msdos_fs_type, "msdos", msdos_read_super);
+
+static int __init init_msdos_fs(void)
+{
+ return register_filesystem(&msdos_fs_type);
+}
+
+static void __exit exit_msdos_fs(void)
+{
+ unregister_filesystem(&msdos_fs_type);
+}
+
+module_init(init_msdos_fs)
+module_exit(exit_msdos_fs)
+MODULE_LICENSE("GPL");
diff --git a/fs/msdos/namei.c b/fs/msdos/namei.c
new file mode 100644
index 00000000000000..ac4f2848c1c59a
--- /dev/null
+++ b/fs/msdos/namei.c
@@ -0,0 +1,600 @@
+/*
+ * linux/fs/msdos/namei.c
+ *
+ * Written 1992,1993 by Werner Almesberger
+ * Hidden files 1995 by Albert Cahalan <albert@ccs.neu.edu> <adc@coe.neu.edu>
+ * Rewritten for constant inumbers 1999 by Al Viro
+ */
+
+
+#define __NO_VERSION__
+#include <linux/module.h>
+
+#include <linux/sched.h>
+#include <linux/msdos_fs.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+
+#include <asm/uaccess.h>
+
+#define MSDOS_DEBUG 0
+#define PRINTK(x)
+
+/* MS-DOS "device special files" */
+
+static const char *reserved_names[] = {
+ "CON ","PRN ","NUL ","AUX ",
+ "LPT1 ","LPT2 ","LPT3 ","LPT4 ",
+ "COM1 ","COM2 ","COM3 ","COM4 ",
+ NULL };
+
+
+/* Characters that are undesirable in an MS-DOS file name */
+
+static char bad_chars[] = "*?<>|\"";
+static char bad_if_strict_pc[] = "+=,; ";
+static char bad_if_strict_atari[] = " "; /* GEMDOS is less restrictive */
+#define bad_if_strict(opts) ((opts)->atari ? bad_if_strict_atari : bad_if_strict_pc)
+
+/* Must die */
+void msdos_put_super(struct super_block *sb)
+{
+ fat_put_super(sb);
+}
+
+/***** Formats an MS-DOS file name. Rejects invalid names. */
+static int msdos_format_name(const char *name,int len,
+ char *res,struct fat_mount_options *opts)
+ /* name is the proposed name, len is its length, res is
+ * the resulting name, opts->name_check is either (r)elaxed,
+ * (n)ormal or (s)trict, opts->dotsOK allows dots at the
+ * beginning of name (for hidden files)
+ */
+{
+ char *walk;
+ const char **reserved;
+ unsigned char c;
+ int space;
+
+ if (name[0] == '.') { /* dotfile because . and .. already done */
+ if (opts->dotsOK) {
+ /* Get rid of dot - test for it elsewhere */
+ name++; len--;
+ }
+ else if (!opts->atari) return -EINVAL;
+ }
+ /* disallow names that _really_ start with a dot for MS-DOS, GEMDOS does
+ * not care */
+ space = !opts->atari;
+ c = 0;
+ for (walk = res; len && walk-res < 8; walk++) {
+ c = *name++;
+ len--;
+ if (opts->name_check != 'r' && strchr(bad_chars,c))
+ return -EINVAL;
+ if (opts->name_check == 's' && strchr(bad_if_strict(opts),c))
+ return -EINVAL;
+ if (c >= 'A' && c <= 'Z' && opts->name_check == 's')
+ return -EINVAL;
+ if (c < ' ' || c == ':' || c == '\\') return -EINVAL;
+/* 0xE5 is legal as a first character, but we must substitute 0x05 */
+/* because 0xE5 marks deleted files. Yes, DOS really does this. */
+/* It seems that Microsoft hacked DOS to support non-US characters */
+/* after the 0xE5 character was already in use to mark deleted files. */
+ if((res==walk) && (c==0xE5)) c=0x05;
+ if (c == '.') break;
+ space = (c == ' ');
+ *walk = (!opts->nocase && c >= 'a' && c <= 'z') ? c-32 : c;
+ }
+ if (space) return -EINVAL;
+ if (opts->name_check == 's' && len && c != '.') {
+ c = *name++;
+ len--;
+ if (c != '.') return -EINVAL;
+ }
+ while (c != '.' && len--) c = *name++;
+ if (c == '.') {
+ while (walk-res < 8) *walk++ = ' ';
+ while (len > 0 && walk-res < MSDOS_NAME) {
+ c = *name++;
+ len--;
+ if (opts->name_check != 'r' && strchr(bad_chars,c))
+ return -EINVAL;
+ if (opts->name_check == 's' &&
+ strchr(bad_if_strict(opts),c))
+ return -EINVAL;
+ if (c < ' ' || c == ':' || c == '\\')
+ return -EINVAL;
+ if (c == '.') {
+ if (opts->name_check == 's')
+ return -EINVAL;
+ break;
+ }
+ if (c >= 'A' && c <= 'Z' && opts->name_check == 's')
+ return -EINVAL;
+ space = c == ' ';
+ *walk++ = (!opts->nocase && c >= 'a' && c <= 'z') ? c-32 : c;
+ }
+ if (space) return -EINVAL;
+ if (opts->name_check == 's' && len) return -EINVAL;
+ }
+ while (walk-res < MSDOS_NAME) *walk++ = ' ';
+ if (!opts->atari)
+ /* GEMDOS is less stupid and has no reserved names */
+ for (reserved = reserved_names; *reserved; reserved++)
+ if (!strncmp(res,*reserved,8)) return -EINVAL;
+ return 0;
+}
+
+/***** Locates a directory entry. Uses unformatted name. */
+static int msdos_find(struct inode *dir, const char *name, int len,
+ struct buffer_head **bh, struct msdos_dir_entry **de,
+ loff_t *i_pos)
+{
+ int res;
+ char dotsOK;
+ char msdos_name[MSDOS_NAME];
+
+ dotsOK = MSDOS_SB(dir->i_sb)->options.dotsOK;
+ res = msdos_format_name(name,len, msdos_name,&MSDOS_SB(dir->i_sb)->options);
+ if (res < 0)
+ return -ENOENT;
+ res = fat_scan(dir, msdos_name, bh, de, i_pos);
+ if (!res && dotsOK) {
+ if (name[0]=='.') {
+ if (!((*de)->attr & ATTR_HIDDEN))
+ res = -ENOENT;
+ } else {
+ if ((*de)->attr & ATTR_HIDDEN)
+ res = -ENOENT;
+ }
+ }
+ return res;
+}
+
+/*
+ * Compute the hash for the msdos name corresponding to the dentry.
+ * Note: if the name is invalid, we leave the hash code unchanged so
+ * that the existing dentry can be used. The msdos fs routines will
+ * return ENOENT or EINVAL as appropriate.
+ */
+static int msdos_hash(struct dentry *dentry, struct qstr *qstr)
+{
+ struct fat_mount_options *options = & (MSDOS_SB(dentry->d_sb)->options);
+ int error;
+ char msdos_name[MSDOS_NAME];
+
+ error = msdos_format_name(qstr->name, qstr->len, msdos_name, options);
+ if (!error)
+ qstr->hash = full_name_hash(msdos_name, MSDOS_NAME);
+ return 0;
+}
+
+/*
+ * Compare two msdos names. If either of the names are invalid,
+ * we fall back to doing the standard name comparison.
+ */
+static int msdos_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b)
+{
+ struct fat_mount_options *options = & (MSDOS_SB(dentry->d_sb)->options);
+ int error;
+ char a_msdos_name[MSDOS_NAME], b_msdos_name[MSDOS_NAME];
+
+ error = msdos_format_name(a->name, a->len, a_msdos_name, options);
+ if (error)
+ goto old_compare;
+ error = msdos_format_name(b->name, b->len, b_msdos_name, options);
+ if (error)
+ goto old_compare;
+ error = memcmp(a_msdos_name, b_msdos_name, MSDOS_NAME);
+out:
+ return error;
+
+old_compare:
+ error = 1;
+ if (a->len == b->len)
+ error = memcmp(a->name, b->name, a->len);
+ goto out;
+}
+
+
+static struct dentry_operations msdos_dentry_operations = {
+ d_hash: msdos_hash,
+ d_compare: msdos_cmp,
+};
+
+/*
+ * AV. Wrappers for FAT sb operations. Is it wise?
+ */
+
+/***** Get inode using directory and name */
+struct dentry *msdos_lookup(struct inode *dir,struct dentry *dentry)
+{
+ struct super_block *sb = dir->i_sb;
+ struct inode *inode = NULL;
+ struct msdos_dir_entry *de;
+ struct buffer_head *bh = NULL;
+ loff_t i_pos;
+ int res;
+
+ PRINTK (("msdos_lookup\n"));
+
+ dentry->d_op = &msdos_dentry_operations;
+
+ res = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &bh,
+ &de, &i_pos);
+ if (res == -ENOENT)
+ goto add;
+ if (res < 0)
+ goto out;
+ inode = fat_build_inode(sb, de, i_pos, &res);
+ if (res)
+ goto out;
+add:
+ d_add(dentry, inode);
+ res = 0;
+out:
+ if (bh)
+ fat_brelse(sb, bh);
+ return ERR_PTR(res);
+}
+
+/***** Creates a directory entry (name is already formatted). */
+static int msdos_add_entry(struct inode *dir, const char *name,
+ struct buffer_head **bh,
+ struct msdos_dir_entry **de,
+ loff_t *i_pos, int is_dir, int is_hid)
+{
+ struct super_block *sb = dir->i_sb;
+ int res;
+
+ res = fat_add_entries(dir, 1, bh, de, i_pos);
+ if (res < 0)
+ return res;
+ /*
+ * XXX all times should be set by caller upon successful completion.
+ */
+ dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ mark_inode_dirty(dir);
+ memcpy((*de)->name,name,MSDOS_NAME);
+ (*de)->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
+ if (is_hid)
+ (*de)->attr |= ATTR_HIDDEN;
+ (*de)->start = 0;
+ (*de)->starthi = 0;
+ fat_date_unix2dos(dir->i_mtime,&(*de)->time,&(*de)->date);
+ (*de)->size = 0;
+ fat_mark_buffer_dirty(sb, *bh);
+ return 0;
+}
+
+/*
+ * AV. Huh??? It's exported. Oughtta check usage.
+ */
+
+/***** Create a file */
+int msdos_create(struct inode *dir,struct dentry *dentry,int mode)
+{
+ struct super_block *sb = dir->i_sb;
+ struct buffer_head *bh;
+ struct msdos_dir_entry *de;
+ struct inode *inode;
+ loff_t i_pos;
+ int res, is_hid;
+ char msdos_name[MSDOS_NAME];
+
+ res = msdos_format_name(dentry->d_name.name,dentry->d_name.len,
+ msdos_name, &MSDOS_SB(sb)->options);
+ if (res < 0)
+ return res;
+ is_hid = (dentry->d_name.name[0]=='.') && (msdos_name[0]!='.');
+ /* Have to do it due to foo vs. .foo conflicts */
+ if (fat_scan(dir, msdos_name, &bh, &de, &i_pos) >= 0) {
+ fat_brelse(sb, bh);
+ return -EINVAL;
+ }
+ inode = NULL;
+ res = msdos_add_entry(dir, msdos_name, &bh, &de, &i_pos, 0, is_hid);
+ if (res)
+ return res;
+ inode = fat_build_inode(dir->i_sb, de, i_pos, &res);
+ fat_brelse(sb, bh);
+ if (!inode)
+ return res;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+ d_instantiate(dentry, inode);
+ return 0;
+}
+
+/***** Remove a directory */
+int msdos_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ struct super_block *sb = dir->i_sb;
+ struct inode *inode = dentry->d_inode;
+ loff_t i_pos;
+ int res;
+ struct buffer_head *bh;
+ struct msdos_dir_entry *de;
+
+ bh = NULL;
+ res = msdos_find(dir, dentry->d_name.name, dentry->d_name.len,
+ &bh, &de, &i_pos);
+ if (res < 0)
+ goto rmdir_done;
+ /*
+ * Check whether the directory is not in use, then check
+ * whether it is empty.
+ */
+ res = fat_dir_empty(inode);
+ if (res)
+ goto rmdir_done;
+
+ de->name[0] = DELETED_FLAG;
+ fat_mark_buffer_dirty(sb, bh);
+ fat_detach(inode);
+ inode->i_nlink = 0;
+ inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ dir->i_nlink--;
+ mark_inode_dirty(inode);
+ mark_inode_dirty(dir);
+ res = 0;
+
+rmdir_done:
+ fat_brelse(sb, bh);
+ return res;
+}
+
+/***** Make a directory */
+int msdos_mkdir(struct inode *dir,struct dentry *dentry,int mode)
+{
+ struct super_block *sb = dir->i_sb;
+ struct buffer_head *bh;
+ struct msdos_dir_entry *de;
+ struct inode *inode;
+ int res,is_hid;
+ char msdos_name[MSDOS_NAME];
+ loff_t i_pos;
+
+ res = msdos_format_name(dentry->d_name.name,dentry->d_name.len,
+ msdos_name, &MSDOS_SB(sb)->options);
+ if (res < 0)
+ return res;
+ is_hid = (dentry->d_name.name[0]=='.') && (msdos_name[0]!='.');
+ /* foo vs .foo situation */
+ if (fat_scan(dir, msdos_name, &bh, &de, &i_pos) >= 0)
+ goto out_exist;
+
+ res = msdos_add_entry(dir, msdos_name, &bh, &de, &i_pos, 1, is_hid);
+ if (res)
+ goto out_unlock;
+ inode = fat_build_inode(dir->i_sb, de, i_pos, &res);
+ if (!inode) {
+ fat_brelse(sb, bh);
+ goto out_unlock;
+ }
+ res = 0;
+
+ dir->i_nlink++;
+ inode->i_nlink = 2; /* no need to mark them dirty */
+
+ res = fat_new_dir(inode, dir, 0);
+ if (res)
+ goto mkdir_error;
+
+ fat_brelse(sb, bh);
+ d_instantiate(dentry, inode);
+ res = 0;
+
+out_unlock:
+ return res;
+
+mkdir_error:
+ printk(KERN_WARNING "msdos_mkdir: error=%d, attempting cleanup\n", res);
+ inode->i_nlink = 0;
+ inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ dir->i_nlink--;
+ mark_inode_dirty(inode);
+ mark_inode_dirty(dir);
+ de->name[0] = DELETED_FLAG;
+ fat_mark_buffer_dirty(sb, bh);
+ fat_brelse(sb, bh);
+ fat_detach(inode);
+ iput(inode);
+ goto out_unlock;
+
+out_exist:
+ fat_brelse(sb, bh);
+ res = -EINVAL;
+ goto out_unlock;
+}
+
+/***** Unlink a file */
+int msdos_unlink( struct inode *dir, struct dentry *dentry)
+{
+ struct super_block *sb = dir->i_sb;
+ struct inode *inode = dentry->d_inode;
+ loff_t i_pos;
+ int res;
+ struct buffer_head *bh;
+ struct msdos_dir_entry *de;
+
+ bh = NULL;
+ res = msdos_find(dir, dentry->d_name.name, dentry->d_name.len,
+ &bh, &de, &i_pos);
+ if (res < 0)
+ goto unlink_done;
+
+ de->name[0] = DELETED_FLAG;
+ fat_mark_buffer_dirty(sb, bh);
+ fat_detach(inode);
+ fat_brelse(sb, bh);
+ inode->i_nlink = 0;
+ inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+ mark_inode_dirty(dir);
+ res = 0;
+unlink_done:
+ return res;
+}
+
+static int do_msdos_rename(struct inode *old_dir, char *old_name,
+ struct dentry *old_dentry,
+ struct inode *new_dir,char *new_name, struct dentry *new_dentry,
+ struct buffer_head *old_bh,
+ struct msdos_dir_entry *old_de, loff_t old_i_pos, int is_hid)
+{
+ struct super_block *sb = old_dir->i_sb;
+ struct buffer_head *new_bh=NULL,*dotdot_bh=NULL;
+ struct msdos_dir_entry *new_de,*dotdot_de;
+ struct inode *old_inode,*new_inode;
+ loff_t new_i_pos, dotdot_i_pos;
+ int error;
+ int is_dir;
+
+ old_inode = old_dentry->d_inode;
+ new_inode = new_dentry->d_inode;
+ is_dir = S_ISDIR(old_inode->i_mode);
+
+ if (fat_scan(new_dir, new_name, &new_bh, &new_de, &new_i_pos) >= 0
+ && !new_inode)
+ goto degenerate_case;
+ if (is_dir) {
+ if (new_inode) {
+ error = fat_dir_empty(new_inode);
+ if (error)
+ goto out;
+ }
+ error = fat_scan(old_inode, MSDOS_DOTDOT, &dotdot_bh,
+ &dotdot_de, &dotdot_i_pos);
+ if (error < 0) {
+ printk(KERN_WARNING
+ "MSDOS: %s/%s, get dotdot failed, ret=%d\n",
+ old_dentry->d_parent->d_name.name,
+ old_dentry->d_name.name, error);
+ goto out;
+ }
+ }
+ if (!new_bh) {
+ error = msdos_add_entry(new_dir, new_name, &new_bh, &new_de,
+ &new_i_pos, is_dir, is_hid);
+ if (error)
+ goto out;
+ }
+ new_dir->i_version = ++event;
+
+ /* There we go */
+
+ if (new_inode)
+ fat_detach(new_inode);
+ old_de->name[0] = DELETED_FLAG;
+ fat_mark_buffer_dirty(sb, old_bh);
+ fat_detach(old_inode);
+ fat_attach(old_inode, new_i_pos);
+ if (is_hid)
+ MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
+ else
+ MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
+ mark_inode_dirty(old_inode);
+ old_dir->i_version = ++event;
+ old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
+ mark_inode_dirty(old_dir);
+ if (new_inode) {
+ new_inode->i_nlink--;
+ new_inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(new_inode);
+ }
+ if (dotdot_bh) {
+ dotdot_de->start = CT_LE_W(MSDOS_I(new_dir)->i_logstart);
+ dotdot_de->starthi = CT_LE_W((MSDOS_I(new_dir)->i_logstart) >> 16);
+ fat_mark_buffer_dirty(sb, dotdot_bh);
+ old_dir->i_nlink--;
+ mark_inode_dirty(old_dir);
+ if (new_inode) {
+ new_inode->i_nlink--;
+ mark_inode_dirty(new_inode);
+ } else {
+ new_dir->i_nlink++;
+ mark_inode_dirty(new_dir);
+ }
+ }
+ error = 0;
+out:
+ fat_brelse(sb, new_bh);
+ fat_brelse(sb, dotdot_bh);
+ return error;
+
+degenerate_case:
+ error = -EINVAL;
+ if (new_de!=old_de)
+ goto out;
+ if (is_hid)
+ MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
+ else
+ MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
+ mark_inode_dirty(old_inode);
+ old_dir->i_version = ++event;
+ old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
+ mark_inode_dirty(old_dir);
+ return 0;
+}
+
+/***** Rename, a wrapper for rename_same_dir & rename_diff_dir */
+int msdos_rename(struct inode *old_dir,struct dentry *old_dentry,
+ struct inode *new_dir,struct dentry *new_dentry)
+{
+ struct super_block *sb = old_dir->i_sb;
+ struct buffer_head *old_bh;
+ struct msdos_dir_entry *old_de;
+ loff_t old_i_pos;
+ int error, is_hid, old_hid; /* if new file and old file are hidden */
+ char old_msdos_name[MSDOS_NAME], new_msdos_name[MSDOS_NAME];
+
+ error = msdos_format_name(old_dentry->d_name.name,
+ old_dentry->d_name.len,old_msdos_name,
+ &MSDOS_SB(old_dir->i_sb)->options);
+ if (error < 0)
+ goto rename_done;
+ error = msdos_format_name(new_dentry->d_name.name,
+ new_dentry->d_name.len,new_msdos_name,
+ &MSDOS_SB(new_dir->i_sb)->options);
+ if (error < 0)
+ goto rename_done;
+
+ is_hid = (new_dentry->d_name.name[0]=='.') && (new_msdos_name[0]!='.');
+ old_hid = (old_dentry->d_name.name[0]=='.') && (old_msdos_name[0]!='.');
+ error = fat_scan(old_dir, old_msdos_name, &old_bh, &old_de, &old_i_pos);
+ if (error < 0)
+ goto rename_done;
+
+ error = do_msdos_rename(old_dir, old_msdos_name, old_dentry,
+ new_dir, new_msdos_name, new_dentry,
+ old_bh, old_de, old_i_pos, is_hid);
+ fat_brelse(sb, old_bh);
+
+rename_done:
+ return error;
+}
+
+
+/* The public inode operations for the msdos fs */
+struct inode_operations msdos_dir_inode_operations = {
+ create: msdos_create,
+ lookup: msdos_lookup,
+ unlink: msdos_unlink,
+ mkdir: msdos_mkdir,
+ rmdir: msdos_rmdir,
+ rename: msdos_rename,
+ setattr: fat_notify_change,
+};
+
+struct super_block *msdos_read_super(struct super_block *sb,void *data, int silent)
+{
+ struct super_block *res;
+
+ MSDOS_SB(sb)->options.isvfat = 0;
+ res = fat_read_super(sb, data, silent, &msdos_dir_inode_operations);
+ if (res)
+ sb->s_root->d_op = &msdos_dentry_operations;
+ return res;
+}
diff --git a/fs/namei.c b/fs/namei.c
new file mode 100644
index 00000000000000..da80d852d43cf9
--- /dev/null
+++ b/fs/namei.c
@@ -0,0 +1,2053 @@
+/*
+ * linux/fs/namei.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+/*
+ * Some corrections by tytso.
+ */
+
+/* [Feb 1997 T. Schoebel-Theuer] Complete rewrite of the pathname
+ * lookup logic.
+ */
+/* [Feb-Apr 2000, AV] Rewrite to the new namespace architecture.
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/quotaops.h>
+#include <linux/pagemap.h>
+#include <linux/dnotify.h>
+#include <linux/smp_lock.h>
+#include <linux/personality.h>
+
+#include <asm/namei.h>
+#include <asm/uaccess.h>
+
+#define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])
+
+/* [Feb-1997 T. Schoebel-Theuer]
+ * Fundamental changes in the pathname lookup mechanisms (namei)
+ * were necessary because of omirr. The reason is that omirr needs
+ * to know the _real_ pathname, not the user-supplied one, in case
+ * of symlinks (and also when transname replacements occur).
+ *
+ * The new code replaces the old recursive symlink resolution with
+ * an iterative one (in case of non-nested symlink chains). It does
+ * this with calls to <fs>_follow_link().
+ * As a side effect, dir_namei(), _namei() and follow_link() are now
+ * replaced with a single function lookup_dentry() that can handle all
+ * the special cases of the former code.
+ *
+ * With the new dcache, the pathname is stored at each inode, at least as
+ * long as the refcount of the inode is positive. As a side effect, the
+ * size of the dcache depends on the inode cache and thus is dynamic.
+ *
+ * [29-Apr-1998 C. Scott Ananian] Updated above description of symlink
+ * resolution to correspond with current state of the code.
+ *
+ * Note that the symlink resolution is not *completely* iterative.
+ * There is still a significant amount of tail- and mid- recursion in
+ * the algorithm. Also, note that <fs>_readlink() is not used in
+ * lookup_dentry(): lookup_dentry() on the result of <fs>_readlink()
+ * may return different results than <fs>_follow_link(). Many virtual
+ * filesystems (including /proc) exhibit this behavior.
+ */
+
+/* [24-Feb-97 T. Schoebel-Theuer] Side effects caused by new implementation:
+ * New symlink semantics: when open() is called with flags O_CREAT | O_EXCL
+ * and the name already exists in form of a symlink, try to create the new
+ * name indicated by the symlink. The old code always complained that the
+ * name already exists, due to not following the symlink even if its target
+ * is nonexistent. The new semantics affects also mknod() and link() when
+ * the name is a symlink pointing to a non-existant name.
+ *
+ * I don't know which semantics is the right one, since I have no access
+ * to standards. But I found by trial that HP-UX 9.0 has the full "new"
+ * semantics implemented, while SunOS 4.1.1 and Solaris (SunOS 5.4) have the
+ * "old" one. Personally, I think the new semantics is much more logical.
+ * Note that "ln old new" where "new" is a symlink pointing to a non-existing
+ * file does succeed in both HP-UX and SunOs, but not in Solaris
+ * and in the old Linux semantics.
+ */
+
+/* [16-Dec-97 Kevin Buhr] For security reasons, we change some symlink
+ * semantics. See the comments in "open_namei" and "do_link" below.
+ *
+ * [10-Sep-98 Alan Modra] Another symlink change.
+ */
+
+/* [Feb-Apr 2000 AV] Complete rewrite. Rules for symlinks:
+ * inside the path - always follow.
+ * in the last component in creation/removal/renaming - never follow.
+ * if LOOKUP_FOLLOW passed - follow.
+ * if the pathname has trailing slashes - follow.
+ * otherwise - don't follow.
+ * (applied in that order).
+ *
+ * [Jun 2000 AV] Inconsistent behaviour of open() in case if flags==O_CREAT
+ * restored for 2.4. This is the last surviving part of old 4.2BSD bug.
+ * During the 2.4 we need to fix the userland stuff depending on it -
+ * hopefully we will be able to get rid of that wart in 2.5. So far only
+ * XEmacs seems to be relying on it...
+ */
+
+/* In order to reduce some races, while at the same time doing additional
+ * checking and hopefully speeding things up, we copy filenames to the
+ * kernel data space before using them..
+ *
+ * POSIX.1 2.4: an empty pathname is invalid (ENOENT).
+ * PATH_MAX includes the nul terminator --RR.
+ */
+static inline int do_getname(const char *filename, char *page)
+{
+ int retval;
+ unsigned long len = PATH_MAX;
+
+ if ((unsigned long) filename >= TASK_SIZE) {
+ if (!segment_eq(get_fs(), KERNEL_DS))
+ return -EFAULT;
+ } else if (TASK_SIZE - (unsigned long) filename < PATH_MAX)
+ len = TASK_SIZE - (unsigned long) filename;
+
+ retval = strncpy_from_user((char *)page, filename, len);
+ if (retval > 0) {
+ if (retval < len)
+ return 0;
+ return -ENAMETOOLONG;
+ } else if (!retval)
+ retval = -ENOENT;
+ return retval;
+}
+
+char * getname(const char * filename)
+{
+ char *tmp, *result;
+
+ result = ERR_PTR(-ENOMEM);
+ tmp = __getname();
+ if (tmp) {
+ int retval = do_getname(filename, tmp);
+
+ result = tmp;
+ if (retval < 0) {
+ putname(tmp);
+ result = ERR_PTR(retval);
+ }
+ }
+ return result;
+}
+
+/*
+ * vfs_permission()
+ *
+ * is used to check for read/write/execute permissions on a file.
+ * We use "fsuid" for this, letting us set arbitrary permissions
+ * for filesystem access without changing the "normal" uids which
+ * are used for other things..
+ */
+int vfs_permission(struct inode * inode, int mask)
+{
+ umode_t mode = inode->i_mode;
+
+ if (mask & MAY_WRITE) {
+ /*
+ * Nobody gets write access to a read-only fs.
+ */
+ if (IS_RDONLY(inode) &&
+ (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
+ return -EROFS;
+
+ /*
+ * Nobody gets write access to an immutable file.
+ */
+ if (IS_IMMUTABLE(inode))
+ return -EACCES;
+ }
+
+ if (current->fsuid == inode->i_uid)
+ mode >>= 6;
+ else if (in_group_p(inode->i_gid))
+ mode >>= 3;
+
+ /*
+ * If the DACs are ok we don't need any capability check.
+ */
+ if (((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask))
+ return 0;
+
+ /*
+ * Read/write DACs are always overridable.
+ * Executable DACs are overridable if at least one exec bit is set.
+ */
+ if ((mask & (MAY_READ|MAY_WRITE)) || (inode->i_mode & S_IXUGO))
+ if (capable(CAP_DAC_OVERRIDE))
+ return 0;
+
+ /*
+ * Searching includes executable on directories, else just read.
+ */
+ if (mask == MAY_READ || (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE)))
+ if (capable(CAP_DAC_READ_SEARCH))
+ return 0;
+
+ return -EACCES;
+}
+
+int permission(struct inode * inode,int mask)
+{
+ if (inode->i_op && inode->i_op->permission) {
+ int retval;
+ lock_kernel();
+ retval = inode->i_op->permission(inode, mask);
+ unlock_kernel();
+ return retval;
+ }
+ return vfs_permission(inode, mask);
+}
+
+/*
+ * get_write_access() gets write permission for a file.
+ * put_write_access() releases this write permission.
+ * This is used for regular files.
+ * We cannot support write (and maybe mmap read-write shared) accesses and
+ * MAP_DENYWRITE mmappings simultaneously. The i_writecount field of an inode
+ * can have the following values:
+ * 0: no writers, no VM_DENYWRITE mappings
+ * < 0: (-i_writecount) vm_area_structs with VM_DENYWRITE set exist
+ * > 0: (i_writecount) users are writing to the file.
+ *
+ * Normally we operate on that counter with atomic_{inc,dec} and it's safe
+ * except for the cases where we don't hold i_writecount yet. Then we need to
+ * use {get,deny}_write_access() - these functions check the sign and refuse
+ * to do the change if sign is wrong. Exclusion between them is provided by
+ * spinlock (arbitration_lock) and I'll rip the second arsehole to the first
+ * who will try to move it in struct inode - just leave it here.
+ */
+static spinlock_t arbitration_lock = SPIN_LOCK_UNLOCKED;
+int get_write_access(struct inode * inode)
+{
+ spin_lock(&arbitration_lock);
+ if (atomic_read(&inode->i_writecount) < 0) {
+ spin_unlock(&arbitration_lock);
+ return -ETXTBSY;
+ }
+ atomic_inc(&inode->i_writecount);
+ spin_unlock(&arbitration_lock);
+ return 0;
+}
+int deny_write_access(struct file * file)
+{
+ spin_lock(&arbitration_lock);
+ if (atomic_read(&file->f_dentry->d_inode->i_writecount) > 0) {
+ spin_unlock(&arbitration_lock);
+ return -ETXTBSY;
+ }
+ atomic_dec(&file->f_dentry->d_inode->i_writecount);
+ spin_unlock(&arbitration_lock);
+ return 0;
+}
+
+void path_release(struct nameidata *nd)
+{
+ dput(nd->dentry);
+ mntput(nd->mnt);
+}
+
+/*
+ * Internal lookup() using the new generic dcache.
+ * SMP-safe
+ */
+static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name, int flags)
+{
+ struct dentry * dentry = d_lookup(parent, name);
+
+ if (dentry && dentry->d_op && dentry->d_op->d_revalidate) {
+ if (!dentry->d_op->d_revalidate(dentry, flags) && !d_invalidate(dentry)) {
+ dput(dentry);
+ dentry = NULL;
+ }
+ }
+ return dentry;
+}
+
+/*
+ * This is called when everything else fails, and we actually have
+ * to go to the low-level filesystem to find out what we should do..
+ *
+ * We get the directory semaphore, and after getting that we also
+ * make sure that nobody added the entry to the dcache in the meantime..
+ * SMP-safe
+ */
+static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, int flags)
+{
+ struct dentry * result;
+ struct inode *dir = parent->d_inode;
+
+ down(&dir->i_sem);
+ /*
+ * First re-do the cached lookup just in case it was created
+ * while we waited for the directory semaphore..
+ *
+ * FIXME! This could use version numbering or similar to
+ * avoid unnecessary cache lookups.
+ */
+ result = d_lookup(parent, name);
+ if (!result) {
+ struct dentry * dentry = d_alloc(parent, name);
+ result = ERR_PTR(-ENOMEM);
+ if (dentry) {
+ lock_kernel();
+ result = dir->i_op->lookup(dir, dentry);
+ unlock_kernel();
+ if (result)
+ dput(dentry);
+ else
+ result = dentry;
+ }
+ up(&dir->i_sem);
+ return result;
+ }
+
+ /*
+ * Uhhuh! Nasty case: the cache was re-populated while
+ * we waited on the semaphore. Need to revalidate.
+ */
+ up(&dir->i_sem);
+ if (result->d_op && result->d_op->d_revalidate) {
+ if (!result->d_op->d_revalidate(result, flags) && !d_invalidate(result)) {
+ dput(result);
+ result = ERR_PTR(-ENOENT);
+ }
+ }
+ return result;
+}
+
+/*
+ * This limits recursive symlink follows to 5, while
+ * limiting consecutive symlinks to 40.
+ *
+ * Without that kind of total limit, nasty chains of consecutive
+ * symlinks can cause almost arbitrarily long lookups.
+ */
+static inline int do_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+ int err;
+ if (current->link_count >= 5)
+ goto loop;
+ if (current->total_link_count >= 40)
+ goto loop;
+ if (current->need_resched) {
+ current->state = TASK_RUNNING;
+ schedule();
+ }
+ current->link_count++;
+ current->total_link_count++;
+ UPDATE_ATIME(dentry->d_inode);
+ err = dentry->d_inode->i_op->follow_link(dentry, nd);
+ current->link_count--;
+ return err;
+loop:
+ path_release(nd);
+ return -ELOOP;
+}
+
+static inline int __follow_up(struct vfsmount **mnt, struct dentry **base)
+{
+ struct vfsmount *parent;
+ struct dentry *dentry;
+ spin_lock(&dcache_lock);
+ parent=(*mnt)->mnt_parent;
+ if (parent == *mnt) {
+ spin_unlock(&dcache_lock);
+ return 0;
+ }
+ mntget(parent);
+ dentry=dget((*mnt)->mnt_mountpoint);
+ spin_unlock(&dcache_lock);
+ dput(*base);
+ *base = dentry;
+ mntput(*mnt);
+ *mnt = parent;
+ return 1;
+}
+
+int follow_up(struct vfsmount **mnt, struct dentry **dentry)
+{
+ return __follow_up(mnt, dentry);
+}
+
+static inline int __follow_down(struct vfsmount **mnt, struct dentry **dentry)
+{
+ struct vfsmount *mounted;
+
+ spin_lock(&dcache_lock);
+ mounted = lookup_mnt(*mnt, *dentry);
+ if (mounted) {
+ *mnt = mntget(mounted);
+ spin_unlock(&dcache_lock);
+ dput(*dentry);
+ mntput(mounted->mnt_parent);
+ *dentry = dget(mounted->mnt_root);
+ return 1;
+ }
+ spin_unlock(&dcache_lock);
+ return 0;
+}
+
+int follow_down(struct vfsmount **mnt, struct dentry **dentry)
+{
+ return __follow_down(mnt,dentry);
+}
+
+static inline void follow_dotdot(struct nameidata *nd)
+{
+ while(1) {
+ struct vfsmount *parent;
+ struct dentry *dentry;
+ read_lock(&current->fs->lock);
+ if (nd->dentry == current->fs->root &&
+ nd->mnt == current->fs->rootmnt) {
+ read_unlock(&current->fs->lock);
+ break;
+ }
+ read_unlock(&current->fs->lock);
+ spin_lock(&dcache_lock);
+ if (nd->dentry != nd->mnt->mnt_root) {
+ dentry = dget(nd->dentry->d_parent);
+ spin_unlock(&dcache_lock);
+ dput(nd->dentry);
+ nd->dentry = dentry;
+ break;
+ }
+ parent=nd->mnt->mnt_parent;
+ if (parent == nd->mnt) {
+ spin_unlock(&dcache_lock);
+ break;
+ }
+ mntget(parent);
+ dentry=dget(nd->mnt->mnt_mountpoint);
+ spin_unlock(&dcache_lock);
+ dput(nd->dentry);
+ nd->dentry = dentry;
+ mntput(nd->mnt);
+ nd->mnt = parent;
+ }
+ while (d_mountpoint(nd->dentry) && __follow_down(&nd->mnt, &nd->dentry))
+ ;
+}
+
+/*
+ * Name resolution.
+ *
+ * This is the basic name resolution function, turning a pathname
+ * into the final dentry.
+ *
+ * We expect 'base' to be positive and a directory.
+ */
+int fastcall link_path_walk(const char * name, struct nameidata *nd)
+{
+ struct dentry *dentry;
+ struct inode *inode;
+ int err;
+ unsigned int lookup_flags = nd->flags;
+
+ while (*name=='/')
+ name++;
+ if (!*name)
+ goto return_reval;
+
+ inode = nd->dentry->d_inode;
+ if (current->link_count)
+ lookup_flags = LOOKUP_FOLLOW;
+
+ /* At this point we know we have a real path component. */
+ for(;;) {
+ unsigned long hash;
+ struct qstr this;
+ unsigned int c;
+
+ err = permission(inode, MAY_EXEC);
+ dentry = ERR_PTR(err);
+ if (err)
+ break;
+
+ this.name = name;
+ c = *(const unsigned char *)name;
+
+ hash = init_name_hash();
+ do {
+ name++;
+ hash = partial_name_hash(c, hash);
+ c = *(const unsigned char *)name;
+ } while (c && (c != '/'));
+ this.len = name - (const char *) this.name;
+ this.hash = end_name_hash(hash);
+
+ /* remove trailing slashes? */
+ if (!c)
+ goto last_component;
+ while (*++name == '/');
+ if (!*name)
+ goto last_with_slashes;
+
+ /*
+ * "." and ".." are special - ".." especially so because it has
+ * to be able to know about the current root directory and
+ * parent relationships.
+ */
+ if (this.name[0] == '.') switch (this.len) {
+ default:
+ break;
+ case 2:
+ if (this.name[1] != '.')
+ break;
+ follow_dotdot(nd);
+ inode = nd->dentry->d_inode;
+ /* fallthrough */
+ case 1:
+ continue;
+ }
+ /*
+ * See if the low-level filesystem might want
+ * to use its own hash..
+ */
+ if (nd->dentry->d_op && nd->dentry->d_op->d_hash) {
+ err = nd->dentry->d_op->d_hash(nd->dentry, &this);
+ if (err < 0)
+ break;
+ }
+ /* This does the actual lookups.. */
+ dentry = cached_lookup(nd->dentry, &this, LOOKUP_CONTINUE);
+ if (!dentry) {
+ dentry = real_lookup(nd->dentry, &this, LOOKUP_CONTINUE);
+ err = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
+ break;
+ }
+ /* Check mountpoints.. */
+ while (d_mountpoint(dentry) && __follow_down(&nd->mnt, &dentry))
+ ;
+
+ err = -ENOENT;
+ inode = dentry->d_inode;
+ if (!inode)
+ goto out_dput;
+ err = -ENOTDIR;
+ if (!inode->i_op)
+ goto out_dput;
+
+ if (inode->i_op->follow_link) {
+ struct vfsmount *mnt = mntget(nd->mnt);
+ err = do_follow_link(dentry, nd);
+ dput(dentry);
+ mntput(mnt);
+ if (err)
+ goto return_err;
+ err = -ENOENT;
+ inode = nd->dentry->d_inode;
+ if (!inode)
+ break;
+ err = -ENOTDIR;
+ if (!inode->i_op)
+ break;
+ } else {
+ dput(nd->dentry);
+ nd->dentry = dentry;
+ }
+ err = -ENOTDIR;
+ if (!inode->i_op->lookup)
+ break;
+ continue;
+ /* here ends the main loop */
+
+last_with_slashes:
+ lookup_flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
+last_component:
+ if (lookup_flags & LOOKUP_PARENT)
+ goto lookup_parent;
+ if (this.name[0] == '.') switch (this.len) {
+ default:
+ break;
+ case 2:
+ if (this.name[1] != '.')
+ break;
+ follow_dotdot(nd);
+ inode = nd->dentry->d_inode;
+ /* fallthrough */
+ case 1:
+ goto return_reval;
+ }
+ if (nd->dentry->d_op && nd->dentry->d_op->d_hash) {
+ err = nd->dentry->d_op->d_hash(nd->dentry, &this);
+ if (err < 0)
+ break;
+ }
+ dentry = cached_lookup(nd->dentry, &this, nd->flags);
+ if (!dentry) {
+ dentry = real_lookup(nd->dentry, &this, nd->flags);
+ err = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
+ break;
+ }
+ while (d_mountpoint(dentry) && __follow_down(&nd->mnt, &dentry))
+ ;
+ inode = dentry->d_inode;
+ if ((lookup_flags & LOOKUP_FOLLOW)
+ && inode && inode->i_op && inode->i_op->follow_link) {
+ struct vfsmount *mnt = mntget(nd->mnt);
+ err = do_follow_link(dentry, nd);
+ dput(dentry);
+ mntput(mnt);
+ if (err)
+ goto return_err;
+ inode = nd->dentry->d_inode;
+ } else {
+ dput(nd->dentry);
+ nd->dentry = dentry;
+ }
+ err = -ENOENT;
+ if (!inode)
+ goto no_inode;
+ if (lookup_flags & LOOKUP_DIRECTORY) {
+ err = -ENOTDIR;
+ if (!inode->i_op || !inode->i_op->lookup)
+ break;
+ }
+ goto return_base;
+no_inode:
+ err = -ENOENT;
+ if (lookup_flags & (LOOKUP_POSITIVE|LOOKUP_DIRECTORY))
+ break;
+ goto return_base;
+lookup_parent:
+ nd->last = this;
+ nd->last_type = LAST_NORM;
+ if (this.name[0] != '.')
+ goto return_base;
+ if (this.len == 1)
+ nd->last_type = LAST_DOT;
+ else if (this.len == 2 && this.name[1] == '.')
+ nd->last_type = LAST_DOTDOT;
+ else
+ goto return_base;
+return_reval:
+ /*
+ * We bypassed the ordinary revalidation routines.
+ * Check the cached dentry for staleness.
+ */
+ dentry = nd->dentry;
+ if (dentry && dentry->d_op && dentry->d_op->d_revalidate) {
+ err = -ESTALE;
+ if (!dentry->d_op->d_revalidate(dentry, 0)) {
+ d_invalidate(dentry);
+ break;
+ }
+ }
+return_base:
+ return 0;
+out_dput:
+ dput(dentry);
+ break;
+ }
+ path_release(nd);
+return_err:
+ return err;
+}
+
+int fastcall path_walk(const char * name, struct nameidata *nd)
+{
+ current->total_link_count = 0;
+ return link_path_walk(name, nd);
+}
+
+/* SMP-safe */
+/* returns 1 if everything is done */
+static int __emul_lookup_dentry(const char *name, struct nameidata *nd)
+{
+ if (path_walk(name, nd))
+ return 0; /* something went wrong... */
+
+ if (!nd->dentry->d_inode || S_ISDIR(nd->dentry->d_inode->i_mode)) {
+ struct nameidata nd_root;
+ /*
+ * NAME was not found in alternate root or it's a directory. Try to find
+ * it in the normal root:
+ */
+ nd_root.last_type = LAST_ROOT;
+ nd_root.flags = nd->flags;
+ read_lock(&current->fs->lock);
+ nd_root.mnt = mntget(current->fs->rootmnt);
+ nd_root.dentry = dget(current->fs->root);
+ read_unlock(&current->fs->lock);
+ if (path_walk(name, &nd_root))
+ return 1;
+ if (nd_root.dentry->d_inode) {
+ path_release(nd);
+ nd->dentry = nd_root.dentry;
+ nd->mnt = nd_root.mnt;
+ nd->last = nd_root.last;
+ return 1;
+ }
+ path_release(&nd_root);
+ }
+ return 1;
+}
+
+void set_fs_altroot(void)
+{
+ char *emul = __emul_prefix();
+ struct nameidata nd;
+ struct vfsmount *mnt = NULL, *oldmnt;
+ struct dentry *dentry = NULL, *olddentry;
+ if (emul) {
+ read_lock(&current->fs->lock);
+ nd.mnt = mntget(current->fs->rootmnt);
+ nd.dentry = dget(current->fs->root);
+ read_unlock(&current->fs->lock);
+ nd.flags = LOOKUP_FOLLOW|LOOKUP_DIRECTORY|LOOKUP_POSITIVE;
+ if (path_walk(emul,&nd) == 0) {
+ mnt = nd.mnt;
+ dentry = nd.dentry;
+ }
+ }
+ write_lock(&current->fs->lock);
+ oldmnt = current->fs->altrootmnt;
+ olddentry = current->fs->altroot;
+ current->fs->altrootmnt = mnt;
+ current->fs->altroot = dentry;
+ write_unlock(&current->fs->lock);
+ if (olddentry) {
+ dput(olddentry);
+ mntput(oldmnt);
+ }
+}
+
+/* SMP-safe */
+static inline int
+walk_init_root(const char *name, struct nameidata *nd)
+{
+ read_lock(&current->fs->lock);
+ if (current->fs->altroot && !(nd->flags & LOOKUP_NOALT)) {
+ nd->mnt = mntget(current->fs->altrootmnt);
+ nd->dentry = dget(current->fs->altroot);
+ read_unlock(&current->fs->lock);
+ if (__emul_lookup_dentry(name,nd))
+ return 0;
+ read_lock(&current->fs->lock);
+ }
+ nd->mnt = mntget(current->fs->rootmnt);
+ nd->dentry = dget(current->fs->root);
+ read_unlock(&current->fs->lock);
+ return 1;
+}
+
+/* SMP-safe */
+int fastcall path_lookup(const char *path, unsigned flags, struct nameidata *nd)
+{
+ int error = 0;
+ if (path_init(path, flags, nd))
+ error = path_walk(path, nd);
+ return error;
+}
+
+
+/* SMP-safe */
+int fastcall path_init(const char *name, unsigned int flags, struct nameidata *nd)
+{
+ nd->last_type = LAST_ROOT; /* if there are only slashes... */
+ nd->flags = flags;
+ if (*name=='/')
+ return walk_init_root(name,nd);
+ read_lock(&current->fs->lock);
+ nd->mnt = mntget(current->fs->pwdmnt);
+ nd->dentry = dget(current->fs->pwd);
+ read_unlock(&current->fs->lock);
+ return 1;
+}
+
+/*
+ * Restricted form of lookup. Doesn't follow links, single-component only,
+ * needs parent already locked. Doesn't follow mounts.
+ * SMP-safe.
+ */
+struct dentry * lookup_hash(struct qstr *name, struct dentry * base)
+{
+ struct dentry * dentry;
+ struct inode *inode;
+ int err;
+
+ inode = base->d_inode;
+ err = permission(inode, MAY_EXEC);
+ dentry = ERR_PTR(err);
+ if (err)
+ goto out;
+
+ /*
+ * See if the low-level filesystem might want
+ * to use its own hash..
+ */
+ if (base->d_op && base->d_op->d_hash) {
+ err = base->d_op->d_hash(base, name);
+ dentry = ERR_PTR(err);
+ if (err < 0)
+ goto out;
+ }
+
+ dentry = cached_lookup(base, name, 0);
+ if (!dentry) {
+ struct dentry *new = d_alloc(base, name);
+ dentry = ERR_PTR(-ENOMEM);
+ if (!new)
+ goto out;
+ lock_kernel();
+ dentry = inode->i_op->lookup(inode, new);
+ unlock_kernel();
+ if (!dentry)
+ dentry = new;
+ else
+ dput(new);
+ }
+out:
+ return dentry;
+}
+
+/* SMP-safe */
+struct dentry * lookup_one_len(const char * name, struct dentry * base, int len)
+{
+ unsigned long hash;
+ struct qstr this;
+ unsigned int c;
+
+ this.name = name;
+ this.len = len;
+ if (!len)
+ goto access;
+
+ hash = init_name_hash();
+ while (len--) {
+ c = *(const unsigned char *)name++;
+ if (c == '/' || c == '\0')
+ goto access;
+ hash = partial_name_hash(c, hash);
+ }
+ this.hash = end_name_hash(hash);
+
+ return lookup_hash(&this, base);
+access:
+ return ERR_PTR(-EACCES);
+}
+
+/*
+ * namei()
+ *
+ * is used by most simple commands to get the inode of a specified name.
+ * Open, link etc use their own routines, but this is enough for things
+ * like 'chmod' etc.
+ *
+ * namei exists in two versions: namei/lnamei. The only difference is
+ * that namei follows links, while lnamei does not.
+ * SMP-safe
+ */
+int fastcall __user_walk(const char *name, unsigned flags, struct nameidata *nd)
+{
+ char *tmp;
+ int err;
+
+ tmp = getname(name);
+ err = PTR_ERR(tmp);
+ if (!IS_ERR(tmp)) {
+ err = 0;
+ err = path_lookup(tmp, flags, nd);
+ putname(tmp);
+ }
+ return err;
+}
+
+/*
+ * It's inline, so penalty for filesystems that don't use sticky bit is
+ * minimal.
+ */
+static inline int check_sticky(struct inode *dir, struct inode *inode)
+{
+ if (!(dir->i_mode & S_ISVTX))
+ return 0;
+ if (inode->i_uid == current->fsuid)
+ return 0;
+ if (dir->i_uid == current->fsuid)
+ return 0;
+ return !capable(CAP_FOWNER);
+}
+
+/*
+ * Check whether we can remove a link victim from directory dir, check
+ * whether the type of victim is right.
+ * 1. We can't do it if dir is read-only (done in permission())
+ * 2. We should have write and exec permissions on dir
+ * 3. We can't remove anything from append-only dir
+ * 4. We can't do anything with immutable dir (done in permission())
+ * 5. If the sticky bit on dir is set we should either
+ * a. be owner of dir, or
+ * b. be owner of victim, or
+ * c. have CAP_FOWNER capability
+ * 6. If the victim is append-only or immutable we can't do antyhing with
+ * links pointing to it.
+ * 7. If we were asked to remove a directory and victim isn't one - ENOTDIR.
+ * 8. If we were asked to remove a non-directory and victim isn't one - EISDIR.
+ * 9. We can't remove a root or mountpoint.
+ */
+static inline int may_delete(struct inode *dir,struct dentry *victim, int isdir)
+{
+ int error;
+ if (!victim->d_inode || victim->d_parent->d_inode != dir)
+ return -ENOENT;
+ error = permission(dir,MAY_WRITE | MAY_EXEC);
+ if (error)
+ return error;
+ if (IS_APPEND(dir))
+ return -EPERM;
+ if (check_sticky(dir, victim->d_inode)||IS_APPEND(victim->d_inode)||
+ IS_IMMUTABLE(victim->d_inode))
+ return -EPERM;
+ if (isdir) {
+ if (!S_ISDIR(victim->d_inode->i_mode))
+ return -ENOTDIR;
+ if (IS_ROOT(victim))
+ return -EBUSY;
+ } else if (S_ISDIR(victim->d_inode->i_mode))
+ return -EISDIR;
+ if (IS_DEADDIR(dir))
+ return -ENOENT;
+ return 0;
+}
+
+/* Check whether we can create an object with dentry child in directory
+ * dir.
+ * 1. We can't do it if child already exists (open has special treatment for
+ * this case, but since we are inlined it's OK)
+ * 2. We can't do it if dir is read-only (done in permission())
+ * 3. We should have write and exec permissions on dir
+ * 4. We can't do it if dir is immutable (done in permission())
+ */
+static inline int may_create(struct inode *dir, struct dentry *child) {
+ if (child->d_inode)
+ return -EEXIST;
+ if (IS_DEADDIR(dir))
+ return -ENOENT;
+ return permission(dir,MAY_WRITE | MAY_EXEC);
+}
+
+/*
+ * Special case: O_CREAT|O_EXCL implies O_NOFOLLOW for security
+ * reasons.
+ *
+ * O_DIRECTORY translates into forcing a directory lookup.
+ */
+static inline int lookup_flags(unsigned int f)
+{
+ unsigned long retval = LOOKUP_FOLLOW;
+
+ if (f & O_NOFOLLOW)
+ retval &= ~LOOKUP_FOLLOW;
+
+ if ((f & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL))
+ retval &= ~LOOKUP_FOLLOW;
+
+ if (f & O_DIRECTORY)
+ retval |= LOOKUP_DIRECTORY;
+
+ return retval;
+}
+
+int vfs_create(struct inode *dir, struct dentry *dentry, int mode)
+{
+ int error;
+
+ mode &= S_IALLUGO;
+ mode |= S_IFREG;
+
+ down(&dir->i_zombie);
+ error = may_create(dir, dentry);
+ if (error)
+ goto exit_lock;
+
+ error = -EACCES; /* shouldn't it be ENOSYS? */
+ if (!dir->i_op || !dir->i_op->create)
+ goto exit_lock;
+
+ DQUOT_INIT(dir);
+ lock_kernel();
+ error = dir->i_op->create(dir, dentry, mode);
+ unlock_kernel();
+exit_lock:
+ up(&dir->i_zombie);
+ if (!error)
+ inode_dir_notify(dir, DN_CREATE);
+ return error;
+}
+
+/*
+ * open_namei()
+ *
+ * namei for open - this is in fact almost the whole open-routine.
+ *
+ * Note that the low bits of "flag" aren't the same as in the open
+ * system call - they are 00 - no permissions needed
+ * 01 - read permission needed
+ * 10 - write permission needed
+ * 11 - read/write permissions needed
+ * which is a lot more logical, and also allows the "no perm" needed
+ * for symlinks (where the permissions are checked later).
+ * SMP-safe
+ */
+int open_namei(const char * pathname, int flag, int mode, struct nameidata *nd)
+{
+ int acc_mode, error = 0;
+ struct inode *inode;
+ struct dentry *dentry;
+ struct vfsmount *mnt;
+ struct dentry *dir;
+ int count = 0;
+
+ acc_mode = ACC_MODE(flag);
+
+ /*
+ * The simplest case - just a plain lookup.
+ */
+ if (!(flag & O_CREAT)) {
+ error = path_lookup(pathname, lookup_flags(flag), nd);
+ if (error)
+ return error;
+ dentry = nd->dentry;
+ goto ok;
+ }
+
+ /*
+ * Create - we need to know the parent.
+ */
+ error = path_lookup(pathname, LOOKUP_PARENT, nd);
+ if (error)
+ return error;
+
+ /*
+ * We have the parent and last component. First of all, check
+ * that we are not asked to creat(2) an obvious directory - that
+ * will not do.
+ */
+ error = -EISDIR;
+ if (nd->last_type != LAST_NORM || nd->last.name[nd->last.len])
+ goto exit;
+
+ dir = nd->dentry;
+ down(&dir->d_inode->i_sem);
+ dentry = lookup_hash(&nd->last, nd->dentry);
+
+do_last:
+ error = PTR_ERR(dentry);
+ if (IS_ERR(dentry)) {
+ up(&dir->d_inode->i_sem);
+ goto exit;
+ }
+
+ /* Negative dentry, just create the file */
+ if (!dentry->d_inode) {
+ error = vfs_create(dir->d_inode, dentry,
+ mode & ~current->fs->umask);
+ up(&dir->d_inode->i_sem);
+ dput(nd->dentry);
+ nd->dentry = dentry;
+ if (error)
+ goto exit;
+ /* Don't check for write permission, don't truncate */
+ acc_mode = 0;
+ flag &= ~O_TRUNC;
+ goto ok;
+ }
+
+ /*
+ * It already exists.
+ */
+ up(&dir->d_inode->i_sem);
+
+ error = -EEXIST;
+ if (flag & O_EXCL)
+ goto exit_dput;
+
+ if (d_mountpoint(dentry)) {
+ error = -ELOOP;
+ if (flag & O_NOFOLLOW)
+ goto exit_dput;
+ while (__follow_down(&nd->mnt,&dentry) && d_mountpoint(dentry));
+ }
+ error = -ENOENT;
+ if (!dentry->d_inode)
+ goto exit_dput;
+ if (dentry->d_inode->i_op && dentry->d_inode->i_op->follow_link)
+ goto do_link;
+
+ dput(nd->dentry);
+ nd->dentry = dentry;
+ error = -EISDIR;
+ if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode))
+ goto exit;
+ok:
+ error = -ENOENT;
+ inode = dentry->d_inode;
+ if (!inode)
+ goto exit;
+
+ error = -ELOOP;
+ if (S_ISLNK(inode->i_mode))
+ goto exit;
+
+ error = -EISDIR;
+ if (S_ISDIR(inode->i_mode) && (flag & FMODE_WRITE))
+ goto exit;
+
+ error = permission(inode,acc_mode);
+ if (error)
+ goto exit;
+
+ /*
+ * FIFO's, sockets and device files are special: they don't
+ * actually live on the filesystem itself, and as such you
+ * can write to them even if the filesystem is read-only.
+ */
+ if (S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
+ flag &= ~O_TRUNC;
+ } else if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) {
+ error = -EACCES;
+ if (nd->mnt->mnt_flags & MNT_NODEV)
+ goto exit;
+
+ flag &= ~O_TRUNC;
+ } else {
+ error = -EROFS;
+ if (IS_RDONLY(inode) && (flag & 2))
+ goto exit;
+ }
+ /*
+ * An append-only file must be opened in append mode for writing.
+ */
+ error = -EPERM;
+ if (IS_APPEND(inode)) {
+ if ((flag & FMODE_WRITE) && !(flag & O_APPEND))
+ goto exit;
+ if (flag & O_TRUNC)
+ goto exit;
+ }
+
+ /*
+ * Ensure there are no outstanding leases on the file.
+ */
+ error = get_lease(inode, flag);
+ if (error)
+ goto exit;
+
+ if (flag & O_TRUNC) {
+ error = get_write_access(inode);
+ if (error)
+ goto exit;
+
+ /*
+ * Refuse to truncate files with mandatory locks held on them.
+ */
+ error = locks_verify_locked(inode);
+ if (!error) {
+ DQUOT_INIT(inode);
+
+ error = do_truncate(dentry, 0);
+ }
+ put_write_access(inode);
+ if (error)
+ goto exit;
+ } else
+ if (flag & FMODE_WRITE)
+ DQUOT_INIT(inode);
+
+ return 0;
+
+exit_dput:
+ dput(dentry);
+exit:
+ path_release(nd);
+ return error;
+
+do_link:
+ error = -ELOOP;
+ if (flag & O_NOFOLLOW)
+ goto exit_dput;
+ /*
+ * This is subtle. Instead of calling do_follow_link() we do the
+ * thing by hands. The reason is that this way we have zero link_count
+ * and path_walk() (called from ->follow_link) honoring LOOKUP_PARENT.
+ * After that we have the parent and last component, i.e.
+ * we are in the same situation as after the first path_walk().
+ * Well, almost - if the last component is normal we get its copy
+ * stored in nd->last.name and we will have to putname() it when we
+ * are done. Procfs-like symlinks just set LAST_BIND.
+ */
+ UPDATE_ATIME(dentry->d_inode);
+ mnt = mntget(nd->mnt);
+ error = dentry->d_inode->i_op->follow_link(dentry, nd);
+ dput(dentry);
+ mntput(mnt);
+ if (error)
+ return error;
+ if (nd->last_type == LAST_BIND) {
+ dentry = nd->dentry;
+ goto ok;
+ }
+ error = -EISDIR;
+ if (nd->last_type != LAST_NORM)
+ goto exit;
+ if (nd->last.name[nd->last.len]) {
+ putname(nd->last.name);
+ goto exit;
+ }
+ error = -ELOOP;
+ if (count++==32) {
+ putname(nd->last.name);
+ goto exit;
+ }
+ dir = nd->dentry;
+ down(&dir->d_inode->i_sem);
+ dentry = lookup_hash(&nd->last, nd->dentry);
+ putname(nd->last.name);
+ goto do_last;
+}
+
+/* SMP-safe */
+static struct dentry *lookup_create(struct nameidata *nd, int is_dir)
+{
+ struct dentry *dentry;
+
+ down(&nd->dentry->d_inode->i_sem);
+ dentry = ERR_PTR(-EEXIST);
+ if (nd->last_type != LAST_NORM)
+ goto fail;
+ dentry = lookup_hash(&nd->last, nd->dentry);
+ if (IS_ERR(dentry))
+ goto fail;
+ if (!is_dir && nd->last.name[nd->last.len] && !dentry->d_inode)
+ goto enoent;
+ return dentry;
+enoent:
+ dput(dentry);
+ dentry = ERR_PTR(-ENOENT);
+fail:
+ return dentry;
+}
+
+int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
+{
+ int error = -EPERM;
+
+ down(&dir->i_zombie);
+ if ((S_ISCHR(mode) || S_ISBLK(mode)) && !capable(CAP_MKNOD))
+ goto exit_lock;
+
+ error = may_create(dir, dentry);
+ if (error)
+ goto exit_lock;
+
+ error = -EPERM;
+ if (!dir->i_op || !dir->i_op->mknod)
+ goto exit_lock;
+
+ DQUOT_INIT(dir);
+ lock_kernel();
+ error = dir->i_op->mknod(dir, dentry, mode, dev);
+ unlock_kernel();
+exit_lock:
+ up(&dir->i_zombie);
+ if (!error)
+ inode_dir_notify(dir, DN_CREATE);
+ return error;
+}
+
+asmlinkage long sys_mknod(const char * filename, int mode, dev_t dev)
+{
+ int error = 0;
+ char * tmp;
+ struct dentry * dentry;
+ struct nameidata nd;
+
+ if (S_ISDIR(mode))
+ return -EPERM;
+ tmp = getname(filename);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+
+ error = path_lookup(tmp, LOOKUP_PARENT, &nd);
+ if (error)
+ goto out;
+ dentry = lookup_create(&nd, 0);
+ error = PTR_ERR(dentry);
+
+ mode &= ~current->fs->umask;
+ if (!IS_ERR(dentry)) {
+ switch (mode & S_IFMT) {
+ case 0: case S_IFREG:
+ error = vfs_create(nd.dentry->d_inode,dentry,mode);
+ break;
+ case S_IFCHR: case S_IFBLK: case S_IFIFO: case S_IFSOCK:
+ error = vfs_mknod(nd.dentry->d_inode,dentry,mode,dev);
+ break;
+ case S_IFDIR:
+ error = -EPERM;
+ break;
+ default:
+ error = -EINVAL;
+ }
+ dput(dentry);
+ }
+ up(&nd.dentry->d_inode->i_sem);
+ path_release(&nd);
+out:
+ putname(tmp);
+
+ return error;
+}
+
+int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+ int error;
+
+ down(&dir->i_zombie);
+ error = may_create(dir, dentry);
+ if (error)
+ goto exit_lock;
+
+ error = -EPERM;
+ if (!dir->i_op || !dir->i_op->mkdir)
+ goto exit_lock;
+
+ DQUOT_INIT(dir);
+ mode &= (S_IRWXUGO|S_ISVTX);
+ lock_kernel();
+ error = dir->i_op->mkdir(dir, dentry, mode);
+ unlock_kernel();
+
+exit_lock:
+ up(&dir->i_zombie);
+ if (!error)
+ inode_dir_notify(dir, DN_CREATE);
+ return error;
+}
+
+asmlinkage long sys_mkdir(const char * pathname, int mode)
+{
+ int error = 0;
+ char * tmp;
+
+ tmp = getname(pathname);
+ error = PTR_ERR(tmp);
+ if (!IS_ERR(tmp)) {
+ struct dentry *dentry;
+ struct nameidata nd;
+
+ error = path_lookup(tmp, LOOKUP_PARENT, &nd);
+ if (error)
+ goto out;
+ dentry = lookup_create(&nd, 1);
+ error = PTR_ERR(dentry);
+ if (!IS_ERR(dentry)) {
+ error = vfs_mkdir(nd.dentry->d_inode, dentry,
+ mode & ~current->fs->umask);
+ dput(dentry);
+ }
+ up(&nd.dentry->d_inode->i_sem);
+ path_release(&nd);
+out:
+ putname(tmp);
+ }
+
+ return error;
+}
+
+/*
+ * We try to drop the dentry early: we should have
+ * a usage count of 2 if we're the only user of this
+ * dentry, and if that is true (possibly after pruning
+ * the dcache), then we drop the dentry now.
+ *
+ * A low-level filesystem can, if it choses, legally
+ * do a
+ *
+ * if (!d_unhashed(dentry))
+ * return -EBUSY;
+ *
+ * if it cannot handle the case of removing a directory
+ * that is still in use by something else..
+ */
+static void d_unhash(struct dentry *dentry)
+{
+ dget(dentry);
+ spin_lock(&dcache_lock);
+ switch (atomic_read(&dentry->d_count)) {
+ default:
+ spin_unlock(&dcache_lock);
+ shrink_dcache_parent(dentry);
+ spin_lock(&dcache_lock);
+ if (atomic_read(&dentry->d_count) != 2)
+ break;
+ case 2:
+ list_del_init(&dentry->d_hash);
+ }
+ spin_unlock(&dcache_lock);
+}
+
+int vfs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ int error;
+
+ error = may_delete(dir, dentry, 1);
+ if (error)
+ return error;
+
+ if (!dir->i_op || !dir->i_op->rmdir)
+ return -EPERM;
+
+ DQUOT_INIT(dir);
+
+ double_down(&dir->i_zombie, &dentry->d_inode->i_zombie);
+ d_unhash(dentry);
+ if (d_mountpoint(dentry))
+ error = -EBUSY;
+ else {
+ lock_kernel();
+ error = dir->i_op->rmdir(dir, dentry);
+ unlock_kernel();
+ if (!error)
+ dentry->d_inode->i_flags |= S_DEAD;
+ }
+ double_up(&dir->i_zombie, &dentry->d_inode->i_zombie);
+ if (!error) {
+ inode_dir_notify(dir, DN_DELETE);
+ d_delete(dentry);
+ }
+ dput(dentry);
+
+ return error;
+}
+
+asmlinkage long sys_rmdir(const char * pathname)
+{
+ int error = 0;
+ char * name;
+ struct dentry *dentry;
+ struct nameidata nd;
+
+ name = getname(pathname);
+ if(IS_ERR(name))
+ return PTR_ERR(name);
+
+ error = path_lookup(name, LOOKUP_PARENT, &nd);
+ if (error)
+ goto exit;
+
+ switch(nd.last_type) {
+ case LAST_DOTDOT:
+ error = -ENOTEMPTY;
+ goto exit1;
+ case LAST_DOT:
+ error = -EINVAL;
+ goto exit1;
+ case LAST_ROOT:
+ error = -EBUSY;
+ goto exit1;
+ }
+ down(&nd.dentry->d_inode->i_sem);
+ dentry = lookup_hash(&nd.last, nd.dentry);
+ error = PTR_ERR(dentry);
+ if (!IS_ERR(dentry)) {
+ error = vfs_rmdir(nd.dentry->d_inode, dentry);
+ dput(dentry);
+ }
+ up(&nd.dentry->d_inode->i_sem);
+exit1:
+ path_release(&nd);
+exit:
+ putname(name);
+ return error;
+}
+
+int vfs_unlink(struct inode *dir, struct dentry *dentry)
+{
+ int error;
+
+ down(&dir->i_zombie);
+ error = may_delete(dir, dentry, 0);
+ if (!error) {
+ error = -EPERM;
+ if (dir->i_op && dir->i_op->unlink) {
+ DQUOT_INIT(dir);
+ if (d_mountpoint(dentry))
+ error = -EBUSY;
+ else {
+ lock_kernel();
+ error = dir->i_op->unlink(dir, dentry);
+ unlock_kernel();
+ if (!error)
+ d_delete(dentry);
+ }
+ }
+ }
+ up(&dir->i_zombie);
+ if (!error)
+ inode_dir_notify(dir, DN_DELETE);
+ return error;
+}
+
+asmlinkage long sys_unlink(const char * pathname)
+{
+ int error = 0;
+ char * name;
+ struct dentry *dentry;
+ struct nameidata nd;
+
+ name = getname(pathname);
+ if(IS_ERR(name))
+ return PTR_ERR(name);
+
+ error = path_lookup(name, LOOKUP_PARENT, &nd);
+ if (error)
+ goto exit;
+ error = -EISDIR;
+ if (nd.last_type != LAST_NORM)
+ goto exit1;
+ down(&nd.dentry->d_inode->i_sem);
+ dentry = lookup_hash(&nd.last, nd.dentry);
+ error = PTR_ERR(dentry);
+ if (!IS_ERR(dentry)) {
+ /* Why not before? Because we want correct error value */
+ if (nd.last.name[nd.last.len])
+ goto slashes;
+ error = vfs_unlink(nd.dentry->d_inode, dentry);
+ exit2:
+ dput(dentry);
+ }
+ up(&nd.dentry->d_inode->i_sem);
+exit1:
+ path_release(&nd);
+exit:
+ putname(name);
+
+ return error;
+
+slashes:
+ error = !dentry->d_inode ? -ENOENT :
+ S_ISDIR(dentry->d_inode->i_mode) ? -EISDIR : -ENOTDIR;
+ goto exit2;
+}
+
+int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
+{
+ int error;
+
+ down(&dir->i_zombie);
+ error = may_create(dir, dentry);
+ if (error)
+ goto exit_lock;
+
+ error = -EPERM;
+ if (!dir->i_op || !dir->i_op->symlink)
+ goto exit_lock;
+
+ DQUOT_INIT(dir);
+ lock_kernel();
+ error = dir->i_op->symlink(dir, dentry, oldname);
+ unlock_kernel();
+
+exit_lock:
+ up(&dir->i_zombie);
+ if (!error)
+ inode_dir_notify(dir, DN_CREATE);
+ return error;
+}
+
+asmlinkage long sys_symlink(const char * oldname, const char * newname)
+{
+ int error = 0;
+ char * from;
+ char * to;
+
+ from = getname(oldname);
+ if(IS_ERR(from))
+ return PTR_ERR(from);
+ to = getname(newname);
+ error = PTR_ERR(to);
+ if (!IS_ERR(to)) {
+ struct dentry *dentry;
+ struct nameidata nd;
+
+ error = path_lookup(to, LOOKUP_PARENT, &nd);
+ if (error)
+ goto out;
+ dentry = lookup_create(&nd, 0);
+ error = PTR_ERR(dentry);
+ if (!IS_ERR(dentry)) {
+ error = vfs_symlink(nd.dentry->d_inode, dentry, from);
+ dput(dentry);
+ }
+ up(&nd.dentry->d_inode->i_sem);
+ path_release(&nd);
+out:
+ putname(to);
+ }
+ putname(from);
+ return error;
+}
+
+int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
+{
+ struct inode *inode;
+ int error;
+
+ down(&dir->i_zombie);
+ error = -ENOENT;
+ inode = old_dentry->d_inode;
+ if (!inode)
+ goto exit_lock;
+
+ error = may_create(dir, new_dentry);
+ if (error)
+ goto exit_lock;
+
+ error = -EXDEV;
+ if (dir->i_dev != inode->i_dev)
+ goto exit_lock;
+
+ /*
+ * A link to an append-only or immutable file cannot be created.
+ */
+ error = -EPERM;
+ if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
+ goto exit_lock;
+ if (!dir->i_op || !dir->i_op->link)
+ goto exit_lock;
+
+ DQUOT_INIT(dir);
+ lock_kernel();
+ error = dir->i_op->link(old_dentry, dir, new_dentry);
+ unlock_kernel();
+
+exit_lock:
+ up(&dir->i_zombie);
+ if (!error)
+ inode_dir_notify(dir, DN_CREATE);
+ return error;
+}
+
+/*
+ * Hardlinks are often used in delicate situations. We avoid
+ * security-related surprises by not following symlinks on the
+ * newname. --KAB
+ *
+ * We don't follow them on the oldname either to be compatible
+ * with linux 2.0, and to avoid hard-linking to directories
+ * and other special files. --ADM
+ */
+asmlinkage long sys_link(const char * oldname, const char * newname)
+{
+ int error;
+ char * to;
+
+ to = getname(newname);
+ error = PTR_ERR(to);
+ if (!IS_ERR(to)) {
+ struct dentry *new_dentry;
+ struct nameidata nd, old_nd;
+
+ error = __user_walk(oldname, LOOKUP_POSITIVE, &old_nd);
+ if (error)
+ goto exit;
+ error = path_lookup(to, LOOKUP_PARENT, &nd);
+ if (error)
+ goto out;
+ error = -EXDEV;
+ if (old_nd.mnt != nd.mnt)
+ goto out_release;
+ new_dentry = lookup_create(&nd, 0);
+ error = PTR_ERR(new_dentry);
+ if (!IS_ERR(new_dentry)) {
+ error = vfs_link(old_nd.dentry, nd.dentry->d_inode, new_dentry);
+ dput(new_dentry);
+ }
+ up(&nd.dentry->d_inode->i_sem);
+out_release:
+ path_release(&nd);
+out:
+ path_release(&old_nd);
+exit:
+ putname(to);
+ }
+ return error;
+}
+
+/*
+ * The worst of all namespace operations - renaming directory. "Perverted"
+ * doesn't even start to describe it. Somebody in UCB had a heck of a trip...
+ * Problems:
+ * a) we can get into loop creation. Check is done in is_subdir().
+ * b) race potential - two innocent renames can create a loop together.
+ * That's where 4.4 screws up. Current fix: serialization on
+ * sb->s_vfs_rename_sem. We might be more accurate, but that's another
+ * story.
+ * c) we have to lock _three_ objects - parents and victim (if it exists).
+ * And that - after we got ->i_sem on parents (until then we don't know
+ * whether the target exists at all, let alone whether it is a directory
+ * or not). Solution: ->i_zombie. Taken only after ->i_sem. Always taken
+ * on link creation/removal of any kind. And taken (without ->i_sem) on
+ * directory that will be removed (both in rmdir() and here).
+ * d) some filesystems don't support opened-but-unlinked directories,
+ * either because of layout or because they are not ready to deal with
+ * all cases correctly. The latter will be fixed (taking this sort of
+ * stuff into VFS), but the former is not going away. Solution: the same
+ * trick as in rmdir().
+ * e) conversion from fhandle to dentry may come in the wrong moment - when
+ * we are removing the target. Solution: we will have to grab ->i_zombie
+ * in the fhandle_to_dentry code. [FIXME - current nfsfh.c relies on
+ * ->i_sem on parents, which works but leads to some truely excessive
+ * locking].
+ */
+int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ int error;
+ struct inode *target;
+
+ if (old_dentry->d_inode == new_dentry->d_inode)
+ return 0;
+
+ error = may_delete(old_dir, old_dentry, 1);
+ if (error)
+ return error;
+
+ if (new_dir->i_dev != old_dir->i_dev)
+ return -EXDEV;
+
+ if (!new_dentry->d_inode)
+ error = may_create(new_dir, new_dentry);
+ else
+ error = may_delete(new_dir, new_dentry, 1);
+ if (error)
+ return error;
+
+ if (!old_dir->i_op || !old_dir->i_op->rename)
+ return -EPERM;
+
+ /*
+ * If we are going to change the parent - check write permissions,
+ * we'll need to flip '..'.
+ */
+ if (new_dir != old_dir) {
+ error = permission(old_dentry->d_inode, MAY_WRITE);
+ }
+ if (error)
+ return error;
+
+ DQUOT_INIT(old_dir);
+ DQUOT_INIT(new_dir);
+ down(&old_dir->i_sb->s_vfs_rename_sem);
+ error = -EINVAL;
+ if (is_subdir(new_dentry, old_dentry))
+ goto out_unlock;
+ /* Don't eat your daddy, dear... */
+ /* This also avoids locking issues */
+ if (old_dentry->d_parent == new_dentry)
+ goto out_unlock;
+ target = new_dentry->d_inode;
+ if (target) { /* Hastur! Hastur! Hastur! */
+ triple_down(&old_dir->i_zombie,
+ &new_dir->i_zombie,
+ &target->i_zombie);
+ d_unhash(new_dentry);
+ } else
+ double_down(&old_dir->i_zombie,
+ &new_dir->i_zombie);
+ if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry))
+ error = -EBUSY;
+ else
+ error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
+ if (target) {
+ if (!error)
+ target->i_flags |= S_DEAD;
+ triple_up(&old_dir->i_zombie,
+ &new_dir->i_zombie,
+ &target->i_zombie);
+ if (d_unhashed(new_dentry))
+ d_rehash(new_dentry);
+ dput(new_dentry);
+ } else
+ double_up(&old_dir->i_zombie,
+ &new_dir->i_zombie);
+
+ if (!error)
+ d_move(old_dentry,new_dentry);
+out_unlock:
+ up(&old_dir->i_sb->s_vfs_rename_sem);
+ return error;
+}
+
+int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ int error;
+
+ if (old_dentry->d_inode == new_dentry->d_inode)
+ return 0;
+
+ error = may_delete(old_dir, old_dentry, 0);
+ if (error)
+ return error;
+
+ if (new_dir->i_dev != old_dir->i_dev)
+ return -EXDEV;
+
+ if (!new_dentry->d_inode)
+ error = may_create(new_dir, new_dentry);
+ else
+ error = may_delete(new_dir, new_dentry, 0);
+ if (error)
+ return error;
+
+ if (!old_dir->i_op || !old_dir->i_op->rename)
+ return -EPERM;
+
+ DQUOT_INIT(old_dir);
+ DQUOT_INIT(new_dir);
+ double_down(&old_dir->i_zombie, &new_dir->i_zombie);
+ if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry))
+ error = -EBUSY;
+ else
+ error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
+ double_up(&old_dir->i_zombie, &new_dir->i_zombie);
+ if (error)
+ return error;
+ /* The following d_move() should become unconditional */
+ if (!(old_dir->i_sb->s_type->fs_flags & FS_ODD_RENAME)) {
+ d_move(old_dentry, new_dentry);
+ }
+ return 0;
+}
+
+int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ int error;
+ if (S_ISDIR(old_dentry->d_inode->i_mode))
+ error = vfs_rename_dir(old_dir,old_dentry,new_dir,new_dentry);
+ else
+ error = vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry);
+ if (!error) {
+ if (old_dir == new_dir)
+ inode_dir_notify(old_dir, DN_RENAME);
+ else {
+ inode_dir_notify(old_dir, DN_DELETE);
+ inode_dir_notify(new_dir, DN_CREATE);
+ }
+ }
+ return error;
+}
+
+static inline int do_rename(const char * oldname, const char * newname)
+{
+ int error = 0;
+ struct dentry * old_dir, * new_dir;
+ struct dentry * old_dentry, *new_dentry;
+ struct nameidata oldnd, newnd;
+
+ error = path_lookup(oldname, LOOKUP_PARENT, &oldnd);
+ if (error)
+ goto exit;
+
+ error = path_lookup(newname, LOOKUP_PARENT, &newnd);
+ if (error)
+ goto exit1;
+
+ error = -EXDEV;
+ if (oldnd.mnt != newnd.mnt)
+ goto exit2;
+
+ old_dir = oldnd.dentry;
+ error = -EBUSY;
+ if (oldnd.last_type != LAST_NORM)
+ goto exit2;
+
+ new_dir = newnd.dentry;
+ if (newnd.last_type != LAST_NORM)
+ goto exit2;
+
+ double_lock(new_dir, old_dir);
+
+ old_dentry = lookup_hash(&oldnd.last, old_dir);
+ error = PTR_ERR(old_dentry);
+ if (IS_ERR(old_dentry))
+ goto exit3;
+ /* source must exist */
+ error = -ENOENT;
+ if (!old_dentry->d_inode)
+ goto exit4;
+ /* unless the source is a directory trailing slashes give -ENOTDIR */
+ if (!S_ISDIR(old_dentry->d_inode->i_mode)) {
+ error = -ENOTDIR;
+ if (oldnd.last.name[oldnd.last.len])
+ goto exit4;
+ if (newnd.last.name[newnd.last.len])
+ goto exit4;
+ }
+ new_dentry = lookup_hash(&newnd.last, new_dir);
+ error = PTR_ERR(new_dentry);
+ if (IS_ERR(new_dentry))
+ goto exit4;
+
+ lock_kernel();
+ error = vfs_rename(old_dir->d_inode, old_dentry,
+ new_dir->d_inode, new_dentry);
+ unlock_kernel();
+
+ dput(new_dentry);
+exit4:
+ dput(old_dentry);
+exit3:
+ double_up(&new_dir->d_inode->i_sem, &old_dir->d_inode->i_sem);
+exit2:
+ path_release(&newnd);
+exit1:
+ path_release(&oldnd);
+exit:
+ return error;
+}
+
+asmlinkage long sys_rename(const char * oldname, const char * newname)
+{
+ int error;
+ char * from;
+ char * to;
+
+ from = getname(oldname);
+ if(IS_ERR(from))
+ return PTR_ERR(from);
+ to = getname(newname);
+ error = PTR_ERR(to);
+ if (!IS_ERR(to)) {
+ error = do_rename(from,to);
+ putname(to);
+ }
+ putname(from);
+ return error;
+}
+
+int vfs_readlink(struct dentry *dentry, char *buffer, int buflen, const char *link)
+{
+ int len;
+
+ len = PTR_ERR(link);
+ if (IS_ERR(link))
+ goto out;
+
+ len = strlen(link);
+ if (len > (unsigned) buflen)
+ len = buflen;
+ if (copy_to_user(buffer, link, len))
+ len = -EFAULT;
+out:
+ return len;
+}
+
+static inline int
+__vfs_follow_link(struct nameidata *nd, const char *link)
+{
+ int res = 0;
+ char *name;
+ if (IS_ERR(link))
+ goto fail;
+
+ if (*link == '/') {
+ path_release(nd);
+ if (!walk_init_root(link, nd))
+ /* weird __emul_prefix() stuff did it */
+ goto out;
+ }
+ res = link_path_walk(link, nd);
+out:
+ if (current->link_count || res || nd->last_type!=LAST_NORM)
+ return res;
+ /*
+ * If it is an iterative symlinks resolution in open_namei() we
+ * have to copy the last component. And all that crap because of
+ * bloody create() on broken symlinks. Furrfu...
+ */
+ name = __getname();
+ if (!name) {
+ path_release(nd);
+ return -ENOMEM;
+ }
+ strcpy(name, nd->last.name);
+ nd->last.name = name;
+ return 0;
+fail:
+ path_release(nd);
+ return PTR_ERR(link);
+}
+
+int vfs_follow_link(struct nameidata *nd, const char *link)
+{
+ return __vfs_follow_link(nd, link);
+}
+
+/* get the link contents into pagecache */
+static char *page_getlink(struct dentry * dentry, struct page **ppage)
+{
+ struct page * page;
+ struct address_space *mapping = dentry->d_inode->i_mapping;
+ page = read_cache_page(mapping, 0, (filler_t *)mapping->a_ops->readpage,
+ NULL);
+ if (IS_ERR(page))
+ goto sync_fail;
+ wait_on_page(page);
+ if (!Page_Uptodate(page))
+ goto async_fail;
+ *ppage = page;
+ return kmap(page);
+
+async_fail:
+ page_cache_release(page);
+ return ERR_PTR(-EIO);
+
+sync_fail:
+ return (char*)page;
+}
+
+int page_readlink(struct dentry *dentry, char *buffer, int buflen)
+{
+ struct page *page = NULL;
+ char *s = page_getlink(dentry, &page);
+ int res = vfs_readlink(dentry,buffer,buflen,s);
+ if (page) {
+ kunmap(page);
+ page_cache_release(page);
+ }
+ return res;
+}
+
+int page_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+ struct page *page = NULL;
+ char *s = page_getlink(dentry, &page);
+ int res = __vfs_follow_link(nd, s);
+ if (page) {
+ kunmap(page);
+ page_cache_release(page);
+ }
+ return res;
+}
+
+struct inode_operations page_symlink_inode_operations = {
+ readlink: page_readlink,
+ follow_link: page_follow_link,
+};
diff --git a/fs/namespace.c b/fs/namespace.c
new file mode 100644
index 00000000000000..d467ddc08f54f1
--- /dev/null
+++ b/fs/namespace.c
@@ -0,0 +1,1073 @@
+/*
+ * linux/fs/namespace.c
+ *
+ * (C) Copyright Al Viro 2000, 2001
+ * Released under GPL v2.
+ *
+ * Based on code from fs/super.c, copyright Linus Torvalds and others.
+ * Heavily rewritten.
+ */
+
+#include <linux/config.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/init.h>
+#include <linux/quotaops.h>
+#include <linux/acct.h>
+#include <linux/module.h>
+
+#include <asm/uaccess.h>
+
+#include <linux/seq_file.h>
+#include <linux/namespace.h>
+
+struct vfsmount *do_kern_mount(const char *type, int flags, char *name, void *data);
+int do_remount_sb(struct super_block *sb, int flags, void * data);
+void kill_super(struct super_block *sb);
+extern int __init init_rootfs(void);
+
+static struct list_head *mount_hashtable;
+static int hash_mask, hash_bits;
+static kmem_cache_t *mnt_cache;
+
+static inline unsigned long hash(struct vfsmount *mnt, struct dentry *dentry)
+{
+ unsigned long tmp = ((unsigned long) mnt / L1_CACHE_BYTES);
+ tmp += ((unsigned long) dentry / L1_CACHE_BYTES);
+ tmp = tmp + (tmp >> hash_bits);
+ return tmp & hash_mask;
+}
+
+struct vfsmount *alloc_vfsmnt(char *name)
+{
+ struct vfsmount *mnt = kmem_cache_alloc(mnt_cache, GFP_KERNEL);
+ if (mnt) {
+ memset(mnt, 0, sizeof(struct vfsmount));
+ atomic_set(&mnt->mnt_count,1);
+ INIT_LIST_HEAD(&mnt->mnt_hash);
+ INIT_LIST_HEAD(&mnt->mnt_child);
+ INIT_LIST_HEAD(&mnt->mnt_mounts);
+ INIT_LIST_HEAD(&mnt->mnt_list);
+ if (name) {
+ int size = strlen(name)+1;
+ char * newname = kmalloc(size, GFP_KERNEL);
+ if (newname) {
+ memcpy(newname, name, size);
+ mnt->mnt_devname = newname;
+ }
+ }
+ }
+ return mnt;
+}
+
+void free_vfsmnt(struct vfsmount *mnt)
+{
+ if (mnt->mnt_devname)
+ kfree(mnt->mnt_devname);
+ kmem_cache_free(mnt_cache, mnt);
+}
+
+struct vfsmount *lookup_mnt(struct vfsmount *mnt, struct dentry *dentry)
+{
+ struct list_head * head = mount_hashtable + hash(mnt, dentry);
+ struct list_head * tmp = head;
+ struct vfsmount *p;
+
+ for (;;) {
+ tmp = tmp->next;
+ p = NULL;
+ if (tmp == head)
+ break;
+ p = list_entry(tmp, struct vfsmount, mnt_hash);
+ if (p->mnt_parent == mnt && p->mnt_mountpoint == dentry)
+ break;
+ }
+ return p;
+}
+
+static int check_mnt(struct vfsmount *mnt)
+{
+ spin_lock(&dcache_lock);
+ while (mnt->mnt_parent != mnt)
+ mnt = mnt->mnt_parent;
+ spin_unlock(&dcache_lock);
+ return mnt == current->namespace->root;
+}
+
+static void detach_mnt(struct vfsmount *mnt, struct nameidata *old_nd)
+{
+ old_nd->dentry = mnt->mnt_mountpoint;
+ old_nd->mnt = mnt->mnt_parent;
+ mnt->mnt_parent = mnt;
+ mnt->mnt_mountpoint = mnt->mnt_root;
+ list_del_init(&mnt->mnt_child);
+ list_del_init(&mnt->mnt_hash);
+ old_nd->dentry->d_mounted--;
+}
+
+static void attach_mnt(struct vfsmount *mnt, struct nameidata *nd)
+{
+ mnt->mnt_parent = mntget(nd->mnt);
+ mnt->mnt_mountpoint = dget(nd->dentry);
+ list_add(&mnt->mnt_hash, mount_hashtable+hash(nd->mnt, nd->dentry));
+ list_add_tail(&mnt->mnt_child, &nd->mnt->mnt_mounts);
+ nd->dentry->d_mounted++;
+}
+
+static struct vfsmount *next_mnt(struct vfsmount *p, struct vfsmount *root)
+{
+ struct list_head *next = p->mnt_mounts.next;
+ if (next == &p->mnt_mounts) {
+ while (1) {
+ if (p == root)
+ return NULL;
+ next = p->mnt_child.next;
+ if (next != &p->mnt_parent->mnt_mounts)
+ break;
+ p = p->mnt_parent;
+ }
+ }
+ return list_entry(next, struct vfsmount, mnt_child);
+}
+
+static struct vfsmount *
+clone_mnt(struct vfsmount *old, struct dentry *root)
+{
+ struct super_block *sb = old->mnt_sb;
+ struct vfsmount *mnt = alloc_vfsmnt(old->mnt_devname);
+
+ if (mnt) {
+ mnt->mnt_flags = old->mnt_flags;
+ atomic_inc(&sb->s_active);
+ mnt->mnt_sb = sb;
+ mnt->mnt_root = dget(root);
+ mnt->mnt_mountpoint = mnt->mnt_root;
+ mnt->mnt_parent = mnt;
+ }
+ return mnt;
+}
+
+void __mntput(struct vfsmount *mnt)
+{
+ struct super_block *sb = mnt->mnt_sb;
+ dput(mnt->mnt_root);
+ free_vfsmnt(mnt);
+ kill_super(sb);
+}
+
+/* iterator */
+static void *m_start(struct seq_file *m, loff_t *pos)
+{
+ struct namespace *n = m->private;
+ struct list_head *p;
+ loff_t l = *pos;
+
+ down_read(&n->sem);
+ list_for_each(p, &n->list)
+ if (!l--)
+ return list_entry(p, struct vfsmount, mnt_list);
+ return NULL;
+}
+
+static void *m_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ struct namespace *n = m->private;
+ struct list_head *p = ((struct vfsmount *)v)->mnt_list.next;
+ (*pos)++;
+ return p==&n->list ? NULL : list_entry(p, struct vfsmount, mnt_list);
+}
+
+static void m_stop(struct seq_file *m, void *v)
+{
+ struct namespace *n = m->private;
+ up_read(&n->sem);
+}
+
+static inline void mangle(struct seq_file *m, const char *s)
+{
+ seq_escape(m, s, " \t\n\\");
+}
+
+static int show_vfsmnt(struct seq_file *m, void *v)
+{
+ struct vfsmount *mnt = v;
+ int err = 0;
+ static struct proc_fs_info {
+ int flag;
+ char *str;
+ } fs_info[] = {
+ { MS_SYNCHRONOUS, ",sync" },
+ { MS_MANDLOCK, ",mand" },
+ { MS_NOATIME, ",noatime" },
+ { MS_NODIRATIME, ",nodiratime" },
+ { 0, NULL }
+ };
+ static struct proc_fs_info mnt_info[] = {
+ { MNT_NOSUID, ",nosuid" },
+ { MNT_NODEV, ",nodev" },
+ { MNT_NOEXEC, ",noexec" },
+ { 0, NULL }
+ };
+ struct proc_fs_info *fs_infop;
+ char *path_buf, *path;
+
+ path_buf = (char *) __get_free_page(GFP_KERNEL);
+ if (!path_buf)
+ return -ENOMEM;
+ path = d_path(mnt->mnt_root, mnt, path_buf, PAGE_SIZE);
+ if (IS_ERR(path)) {
+ free_page((unsigned long) path_buf);
+ return PTR_ERR(path);
+ }
+
+ mangle(m, mnt->mnt_devname ? mnt->mnt_devname : "none");
+ seq_putc(m, ' ');
+ mangle(m, path);
+ free_page((unsigned long) path_buf);
+ seq_putc(m, ' ');
+ mangle(m, mnt->mnt_sb->s_type->name);
+ seq_puts(m, mnt->mnt_sb->s_flags & MS_RDONLY ? " ro" : " rw");
+ for (fs_infop = fs_info; fs_infop->flag; fs_infop++) {
+ if (mnt->mnt_sb->s_flags & fs_infop->flag)
+ seq_puts(m, fs_infop->str);
+ }
+ for (fs_infop = mnt_info; fs_infop->flag; fs_infop++) {
+ if (mnt->mnt_flags & fs_infop->flag)
+ seq_puts(m, fs_infop->str);
+ }
+ if (mnt->mnt_sb->s_op->show_options)
+ err = mnt->mnt_sb->s_op->show_options(m, mnt);
+ seq_puts(m, " 0 0\n");
+ return err;
+}
+
+struct seq_operations mounts_op = {
+ start: m_start,
+ next: m_next,
+ stop: m_stop,
+ show: show_vfsmnt
+};
+
+/*
+ * Doesn't take quota and stuff into account. IOW, in some cases it will
+ * give false negatives. The main reason why it's here is that we need
+ * a non-destructive way to look for easily umountable filesystems.
+ */
+int may_umount(struct vfsmount *mnt)
+{
+ if (atomic_read(&mnt->mnt_count) > 2)
+ return -EBUSY;
+ return 0;
+}
+
+void umount_tree(struct vfsmount *mnt)
+{
+ struct vfsmount *p;
+ LIST_HEAD(kill);
+
+ for (p = mnt; p; p = next_mnt(p, mnt)) {
+ list_del(&p->mnt_list);
+ list_add(&p->mnt_list, &kill);
+ }
+
+ while (!list_empty(&kill)) {
+ mnt = list_entry(kill.next, struct vfsmount, mnt_list);
+ list_del_init(&mnt->mnt_list);
+ if (mnt->mnt_parent == mnt) {
+ spin_unlock(&dcache_lock);
+ } else {
+ struct nameidata old_nd;
+ detach_mnt(mnt, &old_nd);
+ spin_unlock(&dcache_lock);
+ path_release(&old_nd);
+ }
+ mntput(mnt);
+ spin_lock(&dcache_lock);
+ }
+}
+
+static int do_umount(struct vfsmount *mnt, int flags)
+{
+ struct super_block * sb = mnt->mnt_sb;
+ int retval = 0;
+
+ /*
+ * If we may have to abort operations to get out of this
+ * mount, and they will themselves hold resources we must
+ * allow the fs to do things. In the Unix tradition of
+ * 'Gee thats tricky lets do it in userspace' the umount_begin
+ * might fail to complete on the first run through as other tasks
+ * must return, and the like. Thats for the mount program to worry
+ * about for the moment.
+ */
+
+ lock_kernel();
+ if( (flags&MNT_FORCE) && sb->s_op->umount_begin)
+ sb->s_op->umount_begin(sb);
+ unlock_kernel();
+
+ /*
+ * No sense to grab the lock for this test, but test itself looks
+ * somewhat bogus. Suggestions for better replacement?
+ * Ho-hum... In principle, we might treat that as umount + switch
+ * to rootfs. GC would eventually take care of the old vfsmount.
+ * Actually it makes sense, especially if rootfs would contain a
+ * /reboot - static binary that would close all descriptors and
+ * call reboot(9). Then init(8) could umount root and exec /reboot.
+ */
+ if (mnt == current->fs->rootmnt && !(flags & MNT_DETACH)) {
+ /*
+ * Special case for "unmounting" root ...
+ * we just try to remount it readonly.
+ */
+ down_write(&sb->s_umount);
+ if (!(sb->s_flags & MS_RDONLY)) {
+ lock_kernel();
+ retval = do_remount_sb(sb, MS_RDONLY, 0);
+ unlock_kernel();
+ }
+ up_write(&sb->s_umount);
+ return retval;
+ }
+
+ down_write(&current->namespace->sem);
+ spin_lock(&dcache_lock);
+
+ if (atomic_read(&sb->s_active) == 1) {
+ /* last instance - try to be smart */
+ spin_unlock(&dcache_lock);
+ lock_kernel();
+ DQUOT_OFF(sb);
+ acct_auto_close(sb->s_dev);
+ unlock_kernel();
+ spin_lock(&dcache_lock);
+ }
+ retval = -EBUSY;
+ if (atomic_read(&mnt->mnt_count) == 2 || flags & MNT_DETACH) {
+ if (!list_empty(&mnt->mnt_list))
+ umount_tree(mnt);
+ retval = 0;
+ }
+ spin_unlock(&dcache_lock);
+ up_write(&current->namespace->sem);
+ return retval;
+}
+
+/*
+ * Now umount can handle mount points as well as block devices.
+ * This is important for filesystems which use unnamed block devices.
+ *
+ * We now support a flag for forced unmount like the other 'big iron'
+ * unixes. Our API is identical to OSF/1 to avoid making a mess of AMD
+ */
+
+asmlinkage long sys_umount(char * name, int flags)
+{
+ struct nameidata nd;
+ int retval;
+
+ retval = __user_walk(name, LOOKUP_POSITIVE|LOOKUP_FOLLOW, &nd);
+ if (retval)
+ goto out;
+ retval = -EINVAL;
+ if (nd.dentry != nd.mnt->mnt_root)
+ goto dput_and_out;
+ if (!check_mnt(nd.mnt))
+ goto dput_and_out;
+
+ retval = -EPERM;
+ if (!capable(CAP_SYS_ADMIN))
+ goto dput_and_out;
+
+ retval = do_umount(nd.mnt, flags);
+dput_and_out:
+ path_release(&nd);
+out:
+ return retval;
+}
+
+/*
+ * The 2.0 compatible umount. No flags.
+ */
+
+asmlinkage long sys_oldumount(char * name)
+{
+ return sys_umount(name,0);
+}
+
+static int mount_is_safe(struct nameidata *nd)
+{
+ if (capable(CAP_SYS_ADMIN))
+ return 0;
+ return -EPERM;
+#ifdef notyet
+ if (S_ISLNK(nd->dentry->d_inode->i_mode))
+ return -EPERM;
+ if (nd->dentry->d_inode->i_mode & S_ISVTX) {
+ if (current->uid != nd->dentry->d_inode->i_uid)
+ return -EPERM;
+ }
+ if (permission(nd->dentry->d_inode, MAY_WRITE))
+ return -EPERM;
+ return 0;
+#endif
+}
+
+static struct vfsmount *copy_tree(struct vfsmount *mnt, struct dentry *dentry)
+{
+ struct vfsmount *p, *next, *q, *res;
+ struct nameidata nd;
+
+ p = mnt;
+ res = nd.mnt = q = clone_mnt(p, dentry);
+ if (!q)
+ goto Enomem;
+ q->mnt_parent = q;
+ q->mnt_mountpoint = p->mnt_mountpoint;
+
+ while ( (next = next_mnt(p, mnt)) != NULL) {
+ while (p != next->mnt_parent) {
+ p = p->mnt_parent;
+ q = q->mnt_parent;
+ }
+ p = next;
+ nd.mnt = q;
+ nd.dentry = p->mnt_mountpoint;
+ q = clone_mnt(p, p->mnt_root);
+ if (!q)
+ goto Enomem;
+ spin_lock(&dcache_lock);
+ list_add_tail(&q->mnt_list, &res->mnt_list);
+ attach_mnt(q, &nd);
+ spin_unlock(&dcache_lock);
+ }
+ return res;
+Enomem:
+ if (res) {
+ spin_lock(&dcache_lock);
+ umount_tree(res);
+ spin_unlock(&dcache_lock);
+ }
+ return NULL;
+}
+
+static int graft_tree(struct vfsmount *mnt, struct nameidata *nd)
+{
+ int err;
+ if (mnt->mnt_sb->s_flags & MS_NOUSER)
+ return -EINVAL;
+
+ if (S_ISDIR(nd->dentry->d_inode->i_mode) !=
+ S_ISDIR(mnt->mnt_root->d_inode->i_mode))
+ return -ENOTDIR;
+
+ err = -ENOENT;
+ down(&nd->dentry->d_inode->i_zombie);
+ if (IS_DEADDIR(nd->dentry->d_inode))
+ goto out_unlock;
+
+ spin_lock(&dcache_lock);
+ if (IS_ROOT(nd->dentry) || !d_unhashed(nd->dentry)) {
+ struct list_head head;
+ attach_mnt(mnt, nd);
+ list_add_tail(&head, &mnt->mnt_list);
+ list_splice(&head, current->namespace->list.prev);
+ mntget(mnt);
+ err = 0;
+ }
+ spin_unlock(&dcache_lock);
+out_unlock:
+ up(&nd->dentry->d_inode->i_zombie);
+ return err;
+}
+
+/*
+ * do loopback mount.
+ */
+static int do_loopback(struct nameidata *nd, char *old_name, int recurse)
+{
+ struct nameidata old_nd;
+ struct vfsmount *mnt = NULL;
+ int err = mount_is_safe(nd);
+ if (err)
+ return err;
+ if (!old_name || !*old_name)
+ return -EINVAL;
+ err = path_lookup(old_name, LOOKUP_POSITIVE|LOOKUP_FOLLOW, &old_nd);
+ if (err)
+ return err;
+
+ down_write(&current->namespace->sem);
+ err = -EINVAL;
+ if (check_mnt(nd->mnt) && (!recurse || check_mnt(old_nd.mnt))) {
+ err = -ENOMEM;
+ if (recurse)
+ mnt = copy_tree(old_nd.mnt, old_nd.dentry);
+ else
+ mnt = clone_mnt(old_nd.mnt, old_nd.dentry);
+ }
+
+ if (mnt) {
+ err = graft_tree(mnt, nd);
+ if (err) {
+ spin_lock(&dcache_lock);
+ umount_tree(mnt);
+ spin_unlock(&dcache_lock);
+ } else
+ mntput(mnt);
+ }
+
+ up_write(&current->namespace->sem);
+ path_release(&old_nd);
+ return err;
+}
+
+/*
+ * change filesystem flags. dir should be a physical root of filesystem.
+ * If you've mounted a non-root directory somewhere and want to do remount
+ * on it - tough luck.
+ */
+
+static int do_remount(struct nameidata *nd,int flags,int mnt_flags,void *data)
+{
+ int err;
+ struct super_block * sb = nd->mnt->mnt_sb;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (!check_mnt(nd->mnt))
+ return -EINVAL;
+
+ if (nd->dentry != nd->mnt->mnt_root)
+ return -EINVAL;
+
+ down_write(&sb->s_umount);
+ err = do_remount_sb(sb, flags, data);
+ if (!err)
+ nd->mnt->mnt_flags=mnt_flags;
+ up_write(&sb->s_umount);
+ return err;
+}
+
+static int do_move_mount(struct nameidata *nd, char *old_name)
+{
+ struct nameidata old_nd, parent_nd;
+ struct vfsmount *p;
+ int err = 0;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (!old_name || !*old_name)
+ return -EINVAL;
+ err = path_lookup(old_name, LOOKUP_POSITIVE|LOOKUP_FOLLOW, &old_nd);
+ if (err)
+ return err;
+
+ down_write(&current->namespace->sem);
+ while(d_mountpoint(nd->dentry) && follow_down(&nd->mnt, &nd->dentry))
+ ;
+ err = -EINVAL;
+ if (!check_mnt(nd->mnt) || !check_mnt(old_nd.mnt))
+ goto out;
+
+ err = -ENOENT;
+ down(&nd->dentry->d_inode->i_zombie);
+ if (IS_DEADDIR(nd->dentry->d_inode))
+ goto out1;
+
+ spin_lock(&dcache_lock);
+ if (!IS_ROOT(nd->dentry) && d_unhashed(nd->dentry))
+ goto out2;
+
+ err = -EINVAL;
+ if (old_nd.dentry != old_nd.mnt->mnt_root)
+ goto out2;
+
+ if (old_nd.mnt == old_nd.mnt->mnt_parent)
+ goto out2;
+
+ if (S_ISDIR(nd->dentry->d_inode->i_mode) !=
+ S_ISDIR(old_nd.dentry->d_inode->i_mode))
+ goto out2;
+
+ err = -ELOOP;
+ for (p = nd->mnt; p->mnt_parent!=p; p = p->mnt_parent)
+ if (p == old_nd.mnt)
+ goto out2;
+ err = 0;
+
+ detach_mnt(old_nd.mnt, &parent_nd);
+ attach_mnt(old_nd.mnt, nd);
+out2:
+ spin_unlock(&dcache_lock);
+out1:
+ up(&nd->dentry->d_inode->i_zombie);
+out:
+ up_write(&current->namespace->sem);
+ if (!err)
+ path_release(&parent_nd);
+ path_release(&old_nd);
+ return err;
+}
+
+static int do_add_mount(struct nameidata *nd, char *type, int flags,
+ int mnt_flags, char *name, void *data)
+{
+ struct vfsmount *mnt;
+ int err;
+
+ if (!type || !memchr(type, 0, PAGE_SIZE))
+ return -EINVAL;
+
+ /* we need capabilities... */
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ mnt = do_kern_mount(type, flags, name, data);
+ err = PTR_ERR(mnt);
+ if (IS_ERR(mnt))
+ goto out;
+
+ down_write(&current->namespace->sem);
+ /* Something was mounted here while we slept */
+ while(d_mountpoint(nd->dentry) && follow_down(&nd->mnt, &nd->dentry))
+ ;
+ err = -EINVAL;
+ if (!check_mnt(nd->mnt))
+ goto unlock;
+
+ /* Refuse the same filesystem on the same mount point */
+ err = -EBUSY;
+ if (nd->mnt->mnt_sb == mnt->mnt_sb && nd->mnt->mnt_root == nd->dentry)
+ goto unlock;
+
+ mnt->mnt_flags = mnt_flags;
+ err = graft_tree(mnt, nd);
+unlock:
+ up_write(&current->namespace->sem);
+ mntput(mnt);
+out:
+ return err;
+}
+
+static int copy_mount_options (const void *data, unsigned long *where)
+{
+ int i;
+ unsigned long page;
+ unsigned long size;
+
+ *where = 0;
+ if (!data)
+ return 0;
+
+ if (!(page = __get_free_page(GFP_KERNEL)))
+ return -ENOMEM;
+
+ /* We only care that *some* data at the address the user
+ * gave us is valid. Just in case, we'll zero
+ * the remainder of the page.
+ */
+ /* copy_from_user cannot cross TASK_SIZE ! */
+ size = TASK_SIZE - (unsigned long)data;
+ if (size > PAGE_SIZE)
+ size = PAGE_SIZE;
+
+ i = size - copy_from_user((void *)page, data, size);
+ if (!i) {
+ free_page(page);
+ return -EFAULT;
+ }
+ if (i != PAGE_SIZE)
+ memset((char *)page + i, 0, PAGE_SIZE - i);
+ *where = page;
+ return 0;
+}
+
+/*
+ * Flags is a 32-bit value that allows up to 31 non-fs dependent flags to
+ * be given to the mount() call (ie: read-only, no-dev, no-suid etc).
+ *
+ * data is a (void *) that can point to any structure up to
+ * PAGE_SIZE-1 bytes, which can contain arbitrary fs-dependent
+ * information (or be NULL).
+ *
+ * Pre-0.97 versions of mount() didn't have a flags word.
+ * When the flags word was introduced its top half was required
+ * to have the magic value 0xC0ED, and this remained so until 2.4.0-test9.
+ * Therefore, if this magic number is present, it carries no information
+ * and must be discarded.
+ */
+long do_mount(char * dev_name, char * dir_name, char *type_page,
+ unsigned long flags, void *data_page)
+{
+ struct nameidata nd;
+ int retval = 0;
+ int mnt_flags = 0;
+
+ /* Discard magic */
+ if ((flags & MS_MGC_MSK) == MS_MGC_VAL)
+ flags &= ~MS_MGC_MSK;
+
+ /* Basic sanity checks */
+
+ if (!dir_name || !*dir_name || !memchr(dir_name, 0, PAGE_SIZE))
+ return -EINVAL;
+ if (dev_name && !memchr(dev_name, 0, PAGE_SIZE))
+ return -EINVAL;
+
+ if (data_page)
+ ((char *)data_page)[PAGE_SIZE - 1] = 0;
+
+ /* Separate the per-mountpoint flags */
+ if (flags & MS_NOSUID)
+ mnt_flags |= MNT_NOSUID;
+ if (flags & MS_NODEV)
+ mnt_flags |= MNT_NODEV;
+ if (flags & MS_NOEXEC)
+ mnt_flags |= MNT_NOEXEC;
+ flags &= ~(MS_NOSUID|MS_NOEXEC|MS_NODEV);
+
+ /* ... and get the mountpoint */
+ retval = path_lookup(dir_name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, &nd);
+ if (retval)
+ return retval;
+
+ if (flags & MS_REMOUNT)
+ retval = do_remount(&nd, flags & ~MS_REMOUNT, mnt_flags,
+ data_page);
+ else if (flags & MS_BIND)
+ retval = do_loopback(&nd, dev_name, flags & MS_REC);
+ else if (flags & MS_MOVE)
+ retval = do_move_mount(&nd, dev_name);
+ else
+ retval = do_add_mount(&nd, type_page, flags, mnt_flags,
+ dev_name, data_page);
+ path_release(&nd);
+ return retval;
+}
+
+int copy_namespace(int flags, struct task_struct *tsk)
+{
+ struct namespace *namespace = tsk->namespace;
+ struct namespace *new_ns;
+ struct vfsmount *rootmnt = NULL, *pwdmnt = NULL, *altrootmnt = NULL;
+ struct fs_struct *fs = tsk->fs;
+
+ if (!namespace)
+ return 0;
+
+ get_namespace(namespace);
+
+ if (! (flags & CLONE_NEWNS))
+ return 0;
+
+ if (!capable(CAP_SYS_ADMIN)) {
+ put_namespace(namespace);
+ return -EPERM;
+ }
+
+ new_ns = kmalloc(sizeof(struct namespace), GFP_KERNEL);
+ if (!new_ns)
+ goto out;
+
+ atomic_set(&new_ns->count, 1);
+ init_rwsem(&new_ns->sem);
+ new_ns->root = NULL;
+ INIT_LIST_HEAD(&new_ns->list);
+
+ down_write(&tsk->namespace->sem);
+ /* First pass: copy the tree topology */
+ new_ns->root = copy_tree(namespace->root, namespace->root->mnt_root);
+ spin_lock(&dcache_lock);
+ list_add_tail(&new_ns->list, &new_ns->root->mnt_list);
+ spin_unlock(&dcache_lock);
+
+ /* Second pass: switch the tsk->fs->* elements */
+ if (fs) {
+ struct vfsmount *p, *q;
+ write_lock(&fs->lock);
+
+ p = namespace->root;
+ q = new_ns->root;
+ while (p) {
+ if (p == fs->rootmnt) {
+ rootmnt = p;
+ fs->rootmnt = mntget(q);
+ }
+ if (p == fs->pwdmnt) {
+ pwdmnt = p;
+ fs->pwdmnt = mntget(q);
+ }
+ if (p == fs->altrootmnt) {
+ altrootmnt = p;
+ fs->altrootmnt = mntget(q);
+ }
+ p = next_mnt(p, namespace->root);
+ q = next_mnt(q, new_ns->root);
+ }
+ write_unlock(&fs->lock);
+ }
+ up_write(&tsk->namespace->sem);
+
+ tsk->namespace = new_ns;
+
+ if (rootmnt)
+ mntput(rootmnt);
+ if (pwdmnt)
+ mntput(pwdmnt);
+ if (altrootmnt)
+ mntput(altrootmnt);
+
+ put_namespace(namespace);
+ return 0;
+
+out:
+ put_namespace(namespace);
+ return -ENOMEM;
+}
+
+asmlinkage long sys_mount(char * dev_name, char * dir_name, char * type,
+ unsigned long flags, void * data)
+{
+ int retval;
+ unsigned long data_page;
+ unsigned long type_page;
+ unsigned long dev_page;
+ char *dir_page;
+
+ retval = copy_mount_options (type, &type_page);
+ if (retval < 0)
+ return retval;
+
+ dir_page = getname(dir_name);
+ retval = PTR_ERR(dir_page);
+ if (IS_ERR(dir_page))
+ goto out1;
+
+ retval = copy_mount_options (dev_name, &dev_page);
+ if (retval < 0)
+ goto out2;
+
+ retval = copy_mount_options (data, &data_page);
+ if (retval < 0)
+ goto out3;
+
+ lock_kernel();
+ retval = do_mount((char*)dev_page, dir_page, (char*)type_page,
+ flags, (void*)data_page);
+ unlock_kernel();
+ free_page(data_page);
+
+out3:
+ free_page(dev_page);
+out2:
+ putname(dir_page);
+out1:
+ free_page(type_page);
+ return retval;
+}
+
+static void chroot_fs_refs(struct nameidata *old_nd, struct nameidata *new_nd)
+{
+ struct task_struct *p;
+ struct fs_struct *fs;
+
+ read_lock(&tasklist_lock);
+ for_each_task(p) {
+ task_lock(p);
+ fs = p->fs;
+ if (fs) {
+ atomic_inc(&fs->count);
+ task_unlock(p);
+ if (fs->root==old_nd->dentry&&fs->rootmnt==old_nd->mnt)
+ set_fs_root(fs, new_nd->mnt, new_nd->dentry);
+ if (fs->pwd==old_nd->dentry&&fs->pwdmnt==old_nd->mnt)
+ set_fs_pwd(fs, new_nd->mnt, new_nd->dentry);
+ put_fs_struct(fs);
+ } else
+ task_unlock(p);
+ }
+ read_unlock(&tasklist_lock);
+}
+
+/*
+ * Moves the current root to put_root, and sets root/cwd of all processes
+ * which had them on the old root to new_root.
+ *
+ * Note:
+ * - we don't move root/cwd if they are not at the root (reason: if something
+ * cared enough to change them, it's probably wrong to force them elsewhere)
+ * - it's okay to pick a root that isn't the root of a file system, e.g.
+ * /nfs/my_root where /nfs is the mount point. It must be a mountpoint,
+ * though, so you may need to say mount --bind /nfs/my_root /nfs/my_root
+ * first.
+ */
+
+asmlinkage long sys_pivot_root(const char *new_root, const char *put_old)
+{
+ struct vfsmount *tmp;
+ struct nameidata new_nd, old_nd, parent_nd, root_parent, user_nd;
+ int error;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ lock_kernel();
+
+ error = __user_walk(new_root, LOOKUP_POSITIVE|LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &new_nd);
+ if (error)
+ goto out0;
+ error = -EINVAL;
+ if (!check_mnt(new_nd.mnt))
+ goto out1;
+
+ error = __user_walk(put_old, LOOKUP_POSITIVE|LOOKUP_FOLLOW|LOOKUP_DIRECTORY, &old_nd);
+ if (error)
+ goto out1;
+
+ read_lock(&current->fs->lock);
+ user_nd.mnt = mntget(current->fs->rootmnt);
+ user_nd.dentry = dget(current->fs->root);
+ read_unlock(&current->fs->lock);
+ down_write(&current->namespace->sem);
+ down(&old_nd.dentry->d_inode->i_zombie);
+ error = -EINVAL;
+ if (!check_mnt(user_nd.mnt))
+ goto out2;
+ error = -ENOENT;
+ if (IS_DEADDIR(new_nd.dentry->d_inode))
+ goto out2;
+ if (d_unhashed(new_nd.dentry) && !IS_ROOT(new_nd.dentry))
+ goto out2;
+ if (d_unhashed(old_nd.dentry) && !IS_ROOT(old_nd.dentry))
+ goto out2;
+ error = -EBUSY;
+ if (new_nd.mnt == user_nd.mnt || old_nd.mnt == user_nd.mnt)
+ goto out2; /* loop */
+ error = -EINVAL;
+ if (user_nd.mnt->mnt_root != user_nd.dentry)
+ goto out2;
+ if (new_nd.mnt->mnt_root != new_nd.dentry)
+ goto out2; /* not a mountpoint */
+ tmp = old_nd.mnt; /* make sure we can reach put_old from new_root */
+ spin_lock(&dcache_lock);
+ if (tmp != new_nd.mnt) {
+ for (;;) {
+ if (tmp->mnt_parent == tmp)
+ goto out3;
+ if (tmp->mnt_parent == new_nd.mnt)
+ break;
+ tmp = tmp->mnt_parent;
+ }
+ if (!is_subdir(tmp->mnt_mountpoint, new_nd.dentry))
+ goto out3;
+ } else if (!is_subdir(old_nd.dentry, new_nd.dentry))
+ goto out3;
+ detach_mnt(new_nd.mnt, &parent_nd);
+ detach_mnt(user_nd.mnt, &root_parent);
+ attach_mnt(user_nd.mnt, &old_nd);
+ attach_mnt(new_nd.mnt, &root_parent);
+ spin_unlock(&dcache_lock);
+ chroot_fs_refs(&user_nd, &new_nd);
+ error = 0;
+ path_release(&root_parent);
+ path_release(&parent_nd);
+out2:
+ up(&old_nd.dentry->d_inode->i_zombie);
+ up_write(&current->namespace->sem);
+ path_release(&user_nd);
+ path_release(&old_nd);
+out1:
+ path_release(&new_nd);
+out0:
+ unlock_kernel();
+ return error;
+out3:
+ spin_unlock(&dcache_lock);
+ goto out2;
+}
+
+static void __init init_mount_tree(void)
+{
+ struct vfsmount *mnt;
+ struct namespace *namespace;
+ struct task_struct *p;
+
+ mnt = do_kern_mount("rootfs", 0, "rootfs", NULL);
+ if (IS_ERR(mnt))
+ panic("Can't create rootfs");
+ namespace = kmalloc(sizeof(*namespace), GFP_KERNEL);
+ if (!namespace)
+ panic("Can't allocate initial namespace");
+ atomic_set(&namespace->count, 1);
+ INIT_LIST_HEAD(&namespace->list);
+ init_rwsem(&namespace->sem);
+ list_add(&mnt->mnt_list, &namespace->list);
+ namespace->root = mnt;
+
+ init_task.namespace = namespace;
+ read_lock(&tasklist_lock);
+ for_each_task(p) {
+ get_namespace(namespace);
+ p->namespace = namespace;
+ }
+ read_unlock(&tasklist_lock);
+
+ set_fs_pwd(current->fs, namespace->root, namespace->root->mnt_root);
+ set_fs_root(current->fs, namespace->root, namespace->root->mnt_root);
+}
+
+void __init mnt_init(unsigned long mempages)
+{
+ struct list_head *d;
+ unsigned long order;
+ unsigned int nr_hash;
+ int i;
+
+ mnt_cache = kmem_cache_create("mnt_cache", sizeof(struct vfsmount),
+ 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+ if (!mnt_cache)
+ panic("Cannot create vfsmount cache");
+
+ /* using single pointer list heads would save half of the hash table. */
+ order = 0;
+ mount_hashtable = (struct list_head *)
+ __get_free_pages(GFP_ATOMIC, order);
+
+ if (!mount_hashtable)
+ panic("Failed to allocate mount hash table\n");
+
+ /*
+ * Find the power-of-two list-heads that can fit into the allocation..
+ * We don't guarantee that "sizeof(struct list_head)" is necessarily
+ * a power-of-two.
+ */
+ nr_hash = (1UL << order) * PAGE_SIZE / sizeof(struct list_head);
+ hash_bits = 0;
+ do {
+ hash_bits++;
+ } while ((nr_hash >> hash_bits) != 0);
+ hash_bits--;
+
+ /*
+ * Re-calculate the actual number of entries and the mask
+ * from the number of bits we can fit.
+ */
+ nr_hash = 1UL << hash_bits;
+ hash_mask = nr_hash-1;
+
+ printk(KERN_INFO "Mount cache hash table entries: %d"
+ " (order: %ld, %ld bytes)\n",
+ nr_hash, order, (PAGE_SIZE << order));
+
+ /* And initialize the newly allocated array */
+ d = mount_hashtable;
+ i = nr_hash;
+ do {
+ INIT_LIST_HEAD(d);
+ d++;
+ i--;
+ } while (i);
+ init_rootfs();
+ init_mount_tree();
+}
diff --git a/fs/ncpfs/Config.in b/fs/ncpfs/Config.in
new file mode 100644
index 00000000000000..1f1eb12787f7c4
--- /dev/null
+++ b/fs/ncpfs/Config.in
@@ -0,0 +1,11 @@
+#
+# NCP Filesystem configuration
+#
+dep_mbool ' Packet signatures' CONFIG_NCPFS_PACKET_SIGNING $CONFIG_NCP_FS
+dep_mbool ' Proprietary file locking' CONFIG_NCPFS_IOCTL_LOCKING $CONFIG_NCP_FS
+dep_mbool ' Clear remove/delete inhibit when needed' CONFIG_NCPFS_STRONG $CONFIG_NCP_FS
+dep_mbool ' Use NFS namespace if available' CONFIG_NCPFS_NFS_NS $CONFIG_NCP_FS
+dep_mbool ' Use LONG (OS/2) namespace if available' CONFIG_NCPFS_OS2_NS $CONFIG_NCP_FS
+dep_mbool ' Lowercase DOS filenames' CONFIG_NCPFS_SMALLDOS $CONFIG_NCP_FS
+dep_mbool ' Use Native Language Support' CONFIG_NCPFS_NLS $CONFIG_NCP_FS
+dep_mbool ' Enable symbolic links and execute flags' CONFIG_NCPFS_EXTRAS $CONFIG_NCP_FS
diff --git a/fs/ncpfs/Makefile b/fs/ncpfs/Makefile
new file mode 100644
index 00000000000000..f30dfb67128d46
--- /dev/null
+++ b/fs/ncpfs/Makefile
@@ -0,0 +1,21 @@
+#
+# Makefile for the linux ncp filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile.
+
+O_TARGET := ncpfs.o
+
+obj-y := dir.o file.o inode.o ioctl.o mmap.o ncplib_kernel.o sock.o \
+ symlink.o ncpsign_kernel.o
+obj-m := $(O_TARGET)
+
+# If you want debugging output, please uncomment the following line
+# EXTRA_CFLAGS += -DDEBUG_NCP=1
+
+CFLAGS_ncplib_kernel.o := -finline-functions
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c
new file mode 100644
index 00000000000000..e6da885c0a8c70
--- /dev/null
+++ b/fs/ncpfs/dir.c
@@ -0,0 +1,1152 @@
+/*
+ * dir.c
+ *
+ * Copyright (C) 1995, 1996 by Volker Lendecke
+ * Modified for big endian by J.F. Chadima and David S. Miller
+ * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
+ * Modified 1998, 1999 Wolfram Pienkoss for NLS
+ * Modified 1999 Wolfram Pienkoss for directory caching
+ *
+ */
+
+#include <linux/config.h>
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/stat.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <asm/uaccess.h>
+#include <asm/byteorder.h>
+#include <linux/locks.h>
+#include <linux/smp_lock.h>
+
+#include <linux/ncp_fs.h>
+
+#include "ncplib_kernel.h"
+
+static void ncp_read_volume_list(struct file *, void *, filldir_t,
+ struct ncp_cache_control *);
+static void ncp_do_readdir(struct file *, void *, filldir_t,
+ struct ncp_cache_control *);
+
+static int ncp_readdir(struct file *, void *, filldir_t);
+
+static int ncp_create(struct inode *, struct dentry *, int);
+static struct dentry *ncp_lookup(struct inode *, struct dentry *);
+static int ncp_unlink(struct inode *, struct dentry *);
+static int ncp_mkdir(struct inode *, struct dentry *, int);
+static int ncp_rmdir(struct inode *, struct dentry *);
+static int ncp_rename(struct inode *, struct dentry *,
+ struct inode *, struct dentry *);
+#ifdef CONFIG_NCPFS_EXTRAS
+extern int ncp_symlink(struct inode *, struct dentry *, const char *);
+#endif
+
+struct file_operations ncp_dir_operations =
+{
+ read: generic_read_dir,
+ readdir: ncp_readdir,
+ ioctl: ncp_ioctl,
+};
+
+struct inode_operations ncp_dir_inode_operations =
+{
+ create: ncp_create,
+ lookup: ncp_lookup,
+ unlink: ncp_unlink,
+#ifdef CONFIG_NCPFS_EXTRAS
+ symlink: ncp_symlink,
+#endif
+ mkdir: ncp_mkdir,
+ rmdir: ncp_rmdir,
+ rename: ncp_rename,
+ setattr: ncp_notify_change,
+};
+
+/*
+ * Dentry operations routines
+ */
+static int ncp_lookup_validate(struct dentry *, int);
+static int ncp_hash_dentry(struct dentry *, struct qstr *);
+static int ncp_compare_dentry (struct dentry *, struct qstr *, struct qstr *);
+static int ncp_delete_dentry(struct dentry *);
+
+static struct dentry_operations ncp_dentry_operations =
+{
+ d_revalidate: ncp_lookup_validate,
+ d_hash: ncp_hash_dentry,
+ d_compare: ncp_compare_dentry,
+ d_delete: ncp_delete_dentry,
+};
+
+struct dentry_operations ncp_root_dentry_operations =
+{
+ d_hash: ncp_hash_dentry,
+ d_compare: ncp_compare_dentry,
+ d_delete: ncp_delete_dentry,
+};
+
+
+/*
+ * Note: leave the hash unchanged if the directory
+ * is case-sensitive.
+ */
+static int
+ncp_hash_dentry(struct dentry *dentry, struct qstr *this)
+{
+ struct nls_table *t;
+ unsigned long hash;
+ int i;
+
+ t = NCP_IO_TABLE(dentry);
+
+ if (!ncp_case_sensitive(dentry->d_inode)) {
+ hash = init_name_hash();
+ for (i=0; i<this->len ; i++)
+ hash = partial_name_hash(ncp_tolower(t, this->name[i]),
+ hash);
+ this->hash = end_name_hash(hash);
+ }
+ return 0;
+}
+
+static int
+ncp_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
+{
+ if (a->len != b->len)
+ return 1;
+
+ if (ncp_case_sensitive(dentry->d_inode))
+ return strncmp(a->name, b->name, a->len);
+
+ return ncp_strnicmp(NCP_IO_TABLE(dentry), a->name, b->name, a->len);
+}
+
+/*
+ * This is the callback from dput() when d_count is going to 0.
+ * We use this to unhash dentries with bad inodes.
+ * Closing files can be safely postponed until iput() - it's done there anyway.
+ */
+static int
+ncp_delete_dentry(struct dentry * dentry)
+{
+ struct inode *inode = dentry->d_inode;
+
+ if (inode) {
+ if (is_bad_inode(inode))
+ return 1;
+ } else
+ {
+ /* N.B. Unhash negative dentries? */
+ }
+ return 0;
+}
+
+static inline int
+ncp_single_volume(struct ncp_server *server)
+{
+ return (server->m.mounted_vol[0] != '\0');
+}
+
+static inline int ncp_is_server_root(struct inode *inode)
+{
+ return (!ncp_single_volume(NCP_SERVER(inode)) &&
+ inode == inode->i_sb->s_root->d_inode);
+}
+
+
+/*
+ * This is the callback when the dcache has a lookup hit.
+ */
+
+
+#ifdef CONFIG_NCPFS_STRONG
+/* try to delete a readonly file (NW R bit set) */
+
+static int
+ncp_force_unlink(struct inode *dir, struct dentry* dentry)
+{
+ int res=0x9c,res2;
+ struct nw_modify_dos_info info;
+ __u32 old_nwattr;
+ struct inode *inode;
+
+ memset(&info, 0, sizeof(info));
+
+ /* remove the Read-Only flag on the NW server */
+ inode = dentry->d_inode;
+
+ old_nwattr = NCP_FINFO(inode)->nwattr;
+ info.attributes = old_nwattr & ~(aRONLY|aDELETEINHIBIT|aRENAMEINHIBIT);
+ res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(inode), inode, NULL, DM_ATTRIBUTES, &info);
+ if (res2)
+ goto leave_me;
+
+ /* now try again the delete operation */
+ res = ncp_del_file_or_subdir2(NCP_SERVER(dir), dentry);
+
+ if (res) /* delete failed, set R bit again */
+ {
+ info.attributes = old_nwattr;
+ res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(inode), inode, NULL, DM_ATTRIBUTES, &info);
+ if (res2)
+ goto leave_me;
+ }
+leave_me:
+ return(res);
+}
+#endif /* CONFIG_NCPFS_STRONG */
+
+#ifdef CONFIG_NCPFS_STRONG
+static int
+ncp_force_rename(struct inode *old_dir, struct dentry* old_dentry, char *_old_name,
+ struct inode *new_dir, struct dentry* new_dentry, char *_new_name)
+{
+ struct nw_modify_dos_info info;
+ int res=0x90,res2;
+ struct inode *old_inode = old_dentry->d_inode;
+ __u32 old_nwattr = NCP_FINFO(old_inode)->nwattr;
+ __u32 new_nwattr = 0; /* shut compiler warning */
+ int old_nwattr_changed = 0;
+ int new_nwattr_changed = 0;
+
+ memset(&info, 0, sizeof(info));
+
+ /* remove the Read-Only flag on the NW server */
+
+ info.attributes = old_nwattr & ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
+ res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(old_inode), old_inode, NULL, DM_ATTRIBUTES, &info);
+ if (!res2)
+ old_nwattr_changed = 1;
+ if (new_dentry && new_dentry->d_inode) {
+ new_nwattr = NCP_FINFO(new_dentry->d_inode)->nwattr;
+ info.attributes = new_nwattr & ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
+ res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(new_dir), new_dir, _new_name, DM_ATTRIBUTES, &info);
+ if (!res2)
+ new_nwattr_changed = 1;
+ }
+ /* now try again the rename operation */
+ /* but only if something really happened */
+ if (new_nwattr_changed || old_nwattr_changed) {
+ res = ncp_ren_or_mov_file_or_subdir(NCP_SERVER(old_dir),
+ old_dir, _old_name,
+ new_dir, _new_name);
+ }
+ if (res)
+ goto leave_me;
+ /* file was successfully renamed, so:
+ do not set attributes on old file - it no longer exists
+ copy attributes from old file to new */
+ new_nwattr_changed = old_nwattr_changed;
+ new_nwattr = old_nwattr;
+ old_nwattr_changed = 0;
+
+leave_me:;
+ if (old_nwattr_changed) {
+ info.attributes = old_nwattr;
+ res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(old_inode), old_inode, NULL, DM_ATTRIBUTES, &info);
+ /* ignore errors */
+ }
+ if (new_nwattr_changed) {
+ info.attributes = new_nwattr;
+ res2 = ncp_modify_file_or_subdir_dos_info_path(NCP_SERVER(new_dir), new_dir, _new_name, DM_ATTRIBUTES, &info);
+ /* ignore errors */
+ }
+ return(res);
+}
+#endif /* CONFIG_NCPFS_STRONG */
+
+
+static int
+__ncp_lookup_validate(struct dentry * dentry, int flags)
+{
+ struct ncp_server *server;
+ struct inode *dir = dentry->d_parent->d_inode;
+ struct ncp_entry_info finfo;
+ int res, val = 0, len;
+ __u8 __name[NCP_MAXPATHLEN + 1];
+
+ if (!dentry->d_inode || !dir)
+ goto finished;
+
+ server = NCP_SERVER(dir);
+
+ if (!ncp_conn_valid(server))
+ goto finished;
+
+ /*
+ * Inspired by smbfs:
+ * The default validation is based on dentry age:
+ * We set the max age at mount time. (But each
+ * successful server lookup renews the timestamp.)
+ */
+ val = NCP_TEST_AGE(server, dentry);
+ if (val)
+ goto finished;
+
+ DDPRINTK("ncp_lookup_validate: %s/%s not valid, age=%ld, server lookup\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ NCP_GET_AGE(dentry));
+
+ len = sizeof(__name);
+ if (ncp_is_server_root(dir)) {
+ res = ncp_io2vol(server, __name, &len, dentry->d_name.name,
+ dentry->d_name.len, 1);
+ if (!res)
+ res = ncp_lookup_volume(server, __name, &(finfo.i));
+ } else {
+ res = ncp_io2vol(server, __name, &len, dentry->d_name.name,
+ dentry->d_name.len, !ncp_preserve_case(dir));
+ if (!res)
+ res = ncp_obtain_info(server, dir, __name, &(finfo.i));
+ }
+ DDPRINTK("ncp_lookup_validate: looked for %s/%s, res=%d\n",
+ dentry->d_parent->d_name.name, __name, res);
+ /*
+ * If we didn't find it, or if it has a different dirEntNum to
+ * what we remember, it's not valid any more.
+ */
+ if (!res) {
+ if (finfo.i.dirEntNum == NCP_FINFO(dentry->d_inode)->dirEntNum) {
+ ncp_new_dentry(dentry);
+ val=1;
+ } else
+ DDPRINTK("ncp_lookup_validate: found, but dirEntNum changed\n");
+
+ ncp_update_inode2(dentry->d_inode, &finfo);
+ }
+
+finished:
+ DDPRINTK("ncp_lookup_validate: result=%d\n", val);
+ return val;
+}
+
+static int
+ncp_lookup_validate(struct dentry * dentry, int flags)
+{
+ int res;
+ lock_kernel();
+ res = __ncp_lookup_validate(dentry, flags);
+ unlock_kernel();
+ return res;
+}
+
+static struct dentry *
+ncp_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos)
+{
+ struct dentry *dent = dentry;
+ struct list_head *next;
+
+ if (d_validate(dent, parent)) {
+ if (dent->d_name.len <= NCP_MAXPATHLEN &&
+ (unsigned long)dent->d_fsdata == fpos) {
+ if (!dent->d_inode) {
+ dput(dent);
+ dent = NULL;
+ }
+ return dent;
+ }
+ dput(dent);
+ }
+
+ /* If a pointer is invalid, we search the dentry. */
+ spin_lock(&dcache_lock);
+ next = parent->d_subdirs.next;
+ while (next != &parent->d_subdirs) {
+ dent = list_entry(next, struct dentry, d_child);
+ if ((unsigned long)dent->d_fsdata == fpos) {
+ if (dent->d_inode)
+ dget_locked(dent);
+ else
+ dent = NULL;
+ spin_unlock(&dcache_lock);
+ goto out;
+ }
+ next = next->next;
+ }
+ spin_unlock(&dcache_lock);
+ return NULL;
+
+out:
+ return dent;
+}
+
+static time_t ncp_obtain_mtime(struct dentry *dentry)
+{
+ struct inode *inode = dentry->d_inode;
+ struct ncp_server *server = NCP_SERVER(inode);
+ struct nw_info_struct i;
+
+ if (!ncp_conn_valid(server) || ncp_is_server_root(inode))
+ return 0;
+
+ if (ncp_obtain_info(server, inode, NULL, &i))
+ return 0;
+
+ return ncp_date_dos2unix(le16_to_cpu(i.modifyTime),
+ le16_to_cpu(i.modifyDate));
+}
+
+static int ncp_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+ struct dentry *dentry = filp->f_dentry;
+ struct inode *inode = dentry->d_inode;
+ struct page *page = NULL;
+ struct ncp_server *server = NCP_SERVER(inode);
+ union ncp_dir_cache *cache = NULL;
+ struct ncp_cache_control ctl;
+ int result, mtime_valid = 0;
+ time_t mtime = 0;
+
+ ctl.page = NULL;
+ ctl.cache = NULL;
+
+ DDPRINTK("ncp_readdir: reading %s/%s, pos=%d\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ (int) filp->f_pos);
+
+ result = -EIO;
+ if (!ncp_conn_valid(server))
+ goto out;
+
+ result = 0;
+ if (filp->f_pos == 0) {
+ if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR))
+ goto out;
+ filp->f_pos = 1;
+ }
+ if (filp->f_pos == 1) {
+ if (filldir(dirent, "..", 2, 1,
+ dentry->d_parent->d_inode->i_ino, DT_DIR))
+ goto out;
+ filp->f_pos = 2;
+ }
+
+ page = grab_cache_page(&inode->i_data, 0);
+ if (!page)
+ goto read_really;
+
+ ctl.cache = cache = kmap(page);
+ ctl.head = cache->head;
+
+ if (!Page_Uptodate(page) || !ctl.head.eof)
+ goto init_cache;
+
+ if (filp->f_pos == 2) {
+ if (jiffies - ctl.head.time >= NCP_MAX_AGE(server))
+ goto init_cache;
+
+ mtime = ncp_obtain_mtime(dentry);
+ mtime_valid = 1;
+ if ((!mtime) || (mtime != ctl.head.mtime))
+ goto init_cache;
+ }
+
+ if (filp->f_pos > ctl.head.end)
+ goto finished;
+
+ ctl.fpos = filp->f_pos + (NCP_DIRCACHE_START - 2);
+ ctl.ofs = ctl.fpos / NCP_DIRCACHE_SIZE;
+ ctl.idx = ctl.fpos % NCP_DIRCACHE_SIZE;
+
+ for (;;) {
+ if (ctl.ofs != 0) {
+ ctl.page = find_lock_page(&inode->i_data, ctl.ofs);
+ if (!ctl.page)
+ goto invalid_cache;
+ ctl.cache = kmap(ctl.page);
+ if (!Page_Uptodate(ctl.page))
+ goto invalid_cache;
+ }
+ while (ctl.idx < NCP_DIRCACHE_SIZE) {
+ struct dentry *dent;
+ int res;
+
+ dent = ncp_dget_fpos(ctl.cache->dentry[ctl.idx],
+ dentry, filp->f_pos);
+ if (!dent)
+ goto invalid_cache;
+ res = filldir(dirent, dent->d_name.name,
+ dent->d_name.len, filp->f_pos,
+ dent->d_inode->i_ino, DT_UNKNOWN);
+ dput(dent);
+ if (res)
+ goto finished;
+ filp->f_pos += 1;
+ ctl.idx += 1;
+ if (filp->f_pos > ctl.head.end)
+ goto finished;
+ }
+ if (ctl.page) {
+ kunmap(ctl.page);
+ SetPageUptodate(ctl.page);
+ UnlockPage(ctl.page);
+ page_cache_release(ctl.page);
+ ctl.page = NULL;
+ }
+ ctl.idx = 0;
+ ctl.ofs += 1;
+ }
+invalid_cache:
+ if (ctl.page) {
+ kunmap(ctl.page);
+ UnlockPage(ctl.page);
+ page_cache_release(ctl.page);
+ ctl.page = NULL;
+ }
+ ctl.cache = cache;
+init_cache:
+ ncp_invalidate_dircache_entries(dentry);
+ if (!mtime_valid) {
+ mtime = ncp_obtain_mtime(dentry);
+ mtime_valid = 1;
+ }
+ ctl.head.mtime = mtime;
+ ctl.head.time = jiffies;
+ ctl.head.eof = 0;
+ ctl.fpos = 2;
+ ctl.ofs = 0;
+ ctl.idx = NCP_DIRCACHE_START;
+ ctl.filled = 0;
+ ctl.valid = 1;
+read_really:
+ if (ncp_is_server_root(inode)) {
+ ncp_read_volume_list(filp, dirent, filldir, &ctl);
+ } else {
+ ncp_do_readdir(filp, dirent, filldir, &ctl);
+ }
+ ctl.head.end = ctl.fpos - 1;
+ ctl.head.eof = ctl.valid;
+finished:
+ if (page) {
+ cache->head = ctl.head;
+ kunmap(page);
+ SetPageUptodate(page);
+ UnlockPage(page);
+ page_cache_release(page);
+ }
+ if (ctl.page) {
+ kunmap(ctl.page);
+ SetPageUptodate(ctl.page);
+ UnlockPage(ctl.page);
+ page_cache_release(ctl.page);
+ }
+out:
+ return result;
+}
+
+static int
+ncp_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
+ struct ncp_cache_control *ctrl, struct ncp_entry_info *entry)
+{
+ struct dentry *newdent, *dentry = filp->f_dentry;
+ struct inode *newino, *inode = dentry->d_inode;
+ struct ncp_cache_control ctl = *ctrl;
+ struct qstr qname;
+ int valid = 0;
+ int hashed = 0;
+ ino_t ino = 0;
+ __u8 __name[NCP_MAXPATHLEN + 1];
+
+ qname.len = sizeof(__name);
+ if (ncp_vol2io(NCP_SERVER(inode), __name, &qname.len,
+ entry->i.entryName, entry->i.nameLen,
+ !ncp_preserve_entry_case(inode, entry->i.NSCreator)))
+ return 1; /* I'm not sure */
+
+ qname.name = __name;
+ qname.hash = full_name_hash(qname.name, qname.len);
+
+ if (dentry->d_op && dentry->d_op->d_hash)
+ if (dentry->d_op->d_hash(dentry, &qname) != 0)
+ goto end_advance;
+
+ newdent = d_lookup(dentry, &qname);
+
+ if (!newdent) {
+ newdent = d_alloc(dentry, &qname);
+ if (!newdent)
+ goto end_advance;
+ } else {
+ hashed = 1;
+ memcpy((char *) newdent->d_name.name, qname.name,
+ newdent->d_name.len);
+ }
+
+ if (!newdent->d_inode) {
+ entry->opened = 0;
+ entry->ino = iunique(inode->i_sb, 2);
+ newino = ncp_iget(inode->i_sb, entry);
+ if (newino) {
+ newdent->d_op = &ncp_dentry_operations;
+ d_instantiate(newdent, newino);
+ if (!hashed)
+ d_rehash(newdent);
+ }
+ } else
+ ncp_update_inode2(newdent->d_inode, entry);
+
+ if (newdent->d_inode) {
+ ino = newdent->d_inode->i_ino;
+ newdent->d_fsdata = (void *) ctl.fpos;
+ ncp_new_dentry(newdent);
+ }
+
+ if (ctl.idx >= NCP_DIRCACHE_SIZE) {
+ if (ctl.page) {
+ kunmap(ctl.page);
+ SetPageUptodate(ctl.page);
+ UnlockPage(ctl.page);
+ page_cache_release(ctl.page);
+ }
+ ctl.cache = NULL;
+ ctl.idx -= NCP_DIRCACHE_SIZE;
+ ctl.ofs += 1;
+ ctl.page = grab_cache_page(&inode->i_data, ctl.ofs);
+ if (ctl.page)
+ ctl.cache = kmap(ctl.page);
+ }
+ if (ctl.cache) {
+ ctl.cache->dentry[ctl.idx] = newdent;
+ valid = 1;
+ }
+ dput(newdent);
+end_advance:
+ if (!valid)
+ ctl.valid = 0;
+ if (!ctl.filled && (ctl.fpos == filp->f_pos)) {
+ if (!ino)
+ ino = find_inode_number(dentry, &qname);
+ if (!ino)
+ ino = iunique(inode->i_sb, 2);
+ ctl.filled = filldir(dirent, qname.name, qname.len,
+ filp->f_pos, ino, DT_UNKNOWN);
+ if (!ctl.filled)
+ filp->f_pos += 1;
+ }
+ ctl.fpos += 1;
+ ctl.idx += 1;
+ *ctrl = ctl;
+ return (ctl.valid || !ctl.filled);
+}
+
+static void
+ncp_read_volume_list(struct file *filp, void *dirent, filldir_t filldir,
+ struct ncp_cache_control *ctl)
+{
+ struct dentry *dentry = filp->f_dentry;
+ struct inode *inode = dentry->d_inode;
+ struct ncp_server *server = NCP_SERVER(inode);
+ struct ncp_volume_info info;
+ struct ncp_entry_info entry;
+ int i;
+
+ DPRINTK("ncp_read_volume_list: pos=%ld\n",
+ (unsigned long) filp->f_pos);
+
+ for (i = 0; i < NCP_NUMBER_OF_VOLUMES; i++) {
+
+ if (ncp_get_volume_info_with_number(server, i, &info) != 0)
+ return;
+ if (!strlen(info.volume_name))
+ continue;
+
+ DPRINTK("ncp_read_volume_list: found vol: %s\n",
+ info.volume_name);
+
+ if (ncp_lookup_volume(server, info.volume_name,
+ &entry.i)) {
+ DPRINTK("ncpfs: could not lookup vol %s\n",
+ info.volume_name);
+ continue;
+ }
+ if (!ncp_fill_cache(filp, dirent, filldir, ctl, &entry))
+ return;
+ }
+}
+
+static void
+ncp_do_readdir(struct file *filp, void *dirent, filldir_t filldir,
+ struct ncp_cache_control *ctl)
+{
+ struct dentry *dentry = filp->f_dentry;
+ struct inode *dir = dentry->d_inode;
+ struct ncp_server *server = NCP_SERVER(dir);
+ struct nw_search_sequence seq;
+ struct ncp_entry_info entry;
+ int err;
+
+ DPRINTK("ncp_do_readdir: %s/%s, fpos=%ld\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ (unsigned long) filp->f_pos);
+ PPRINTK("ncp_do_readdir: init %s, volnum=%d, dirent=%u\n",
+ dentry->d_name.name, NCP_FINFO(dir)->volNumber,
+ NCP_FINFO(dir)->dirEntNum);
+
+ err = ncp_initialize_search(server, dir, &seq);
+ if (err) {
+ DPRINTK("ncp_do_readdir: init failed, err=%d\n", err);
+ return;
+ }
+ for (;;) {
+ err = ncp_search_for_file_or_subdir(server, &seq, &entry.i);
+ if (err) {
+ DPRINTK("ncp_do_readdir: search failed, err=%d\n", err);
+ return;
+ }
+ if (!ncp_fill_cache(filp, dirent, filldir, ctl, &entry))
+ return;
+ }
+}
+
+int ncp_conn_logged_in(struct super_block *sb)
+{
+ struct ncp_server* server = NCP_SBP(sb);
+ struct nw_info_struct i;
+ int result;
+
+ if (ncp_single_volume(server)) {
+ int len;
+ struct dentry* dent;
+ __u8 __name[NCP_MAXPATHLEN + 1];
+
+ len = sizeof(__name);
+ result = ncp_io2vol(server, __name, &len, server->m.mounted_vol,
+ strlen(server->m.mounted_vol), 1);
+ if (result)
+ goto out;
+ result = -ENOENT;
+ if (ncp_lookup_volume(server, __name, &i)) {
+ PPRINTK("ncp_conn_logged_in: %s not found\n",
+ server->m.mounted_vol);
+ goto out;
+ }
+ dent = sb->s_root;
+ if (dent) {
+ struct inode* ino = dent->d_inode;
+ if (ino) {
+ NCP_FINFO(ino)->volNumber = i.volNumber;
+ NCP_FINFO(ino)->dirEntNum = i.dirEntNum;
+ NCP_FINFO(ino)->DosDirNum = i.DosDirNum;
+ } else {
+ DPRINTK("ncpfs: sb->s_root->d_inode == NULL!\n");
+ }
+ } else {
+ DPRINTK("ncpfs: sb->s_root == NULL!\n");
+ }
+ }
+ result = 0;
+
+out:
+ return result;
+}
+
+static struct dentry *ncp_lookup(struct inode *dir, struct dentry *dentry)
+{
+ struct ncp_server *server = NCP_SERVER(dir);
+ struct inode *inode = NULL;
+ struct ncp_entry_info finfo;
+ int error, res, len;
+ __u8 __name[NCP_MAXPATHLEN + 1];
+
+ error = -EIO;
+ if (!ncp_conn_valid(server))
+ goto finished;
+
+ PPRINTK("ncp_lookup: server lookup for %s/%s\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
+
+ len = sizeof(__name);
+ if (ncp_is_server_root(dir)) {
+ res = ncp_io2vol(server, __name, &len, dentry->d_name.name,
+ dentry->d_name.len, 1);
+ if (!res)
+ res = ncp_lookup_volume(server, __name, &(finfo.i));
+ } else {
+ res = ncp_io2vol(server, __name, &len, dentry->d_name.name,
+ dentry->d_name.len, !ncp_preserve_case(dir));
+ if (!res)
+ res = ncp_obtain_info(server, dir, __name, &(finfo.i));
+ }
+ PPRINTK("ncp_lookup: looked for %s/%s, res=%d\n",
+ dentry->d_parent->d_name.name, __name, res);
+ /*
+ * If we didn't find an entry, make a negative dentry.
+ */
+ if (res)
+ goto add_entry;
+
+ /*
+ * Create an inode for the entry.
+ */
+ finfo.opened = 0;
+ finfo.ino = iunique(dir->i_sb, 2);
+ error = -EACCES;
+ inode = ncp_iget(dir->i_sb, &finfo);
+
+ if (inode) {
+ ncp_new_dentry(dentry);
+add_entry:
+ dentry->d_op = &ncp_dentry_operations;
+ d_add(dentry, inode);
+ error = 0;
+ }
+
+finished:
+ PPRINTK("ncp_lookup: result=%d\n", error);
+ return ERR_PTR(error);
+}
+
+/*
+ * This code is common to create, mkdir, and mknod.
+ */
+static int ncp_instantiate(struct inode *dir, struct dentry *dentry,
+ struct ncp_entry_info *finfo)
+{
+ struct inode *inode;
+ int error = -EINVAL;
+
+ finfo->ino = iunique(dir->i_sb, 2);
+ inode = ncp_iget(dir->i_sb, finfo);
+ if (!inode)
+ goto out_close;
+ d_instantiate(dentry,inode);
+ error = 0;
+out:
+ return error;
+
+out_close:
+ PPRINTK("ncp_instantiate: %s/%s failed, closing file\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
+ ncp_close_file(NCP_SERVER(dir), finfo->file_handle);
+ goto out;
+}
+
+int ncp_create_new(struct inode *dir, struct dentry *dentry, int mode,
+ int attributes)
+{
+ struct ncp_server *server = NCP_SERVER(dir);
+ struct ncp_entry_info finfo;
+ int error, result, len;
+ int opmode;
+ __u8 __name[NCP_MAXPATHLEN + 1];
+
+ PPRINTK("ncp_create_new: creating %s/%s, mode=%x\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name, mode);
+ error = -EIO;
+ if (!ncp_conn_valid(server))
+ goto out;
+
+ ncp_age_dentry(server, dentry);
+ len = sizeof(__name);
+ error = ncp_io2vol(server, __name, &len, dentry->d_name.name,
+ dentry->d_name.len, !ncp_preserve_case(dir));
+ if (error)
+ goto out;
+
+ error = -EACCES;
+
+ if (S_ISREG(mode) &&
+ (server->m.flags & NCP_MOUNT_EXTRAS) &&
+ (mode & S_IXUGO))
+ attributes |= aSYSTEM | aSHARED;
+
+ result = ncp_open_create_file_or_subdir(server, dir, __name,
+ OC_MODE_CREATE | OC_MODE_OPEN | OC_MODE_REPLACE,
+ attributes, AR_READ | AR_WRITE, &finfo);
+ opmode = O_RDWR;
+ if (result) {
+ result = ncp_open_create_file_or_subdir(server, dir, __name,
+ OC_MODE_CREATE | OC_MODE_OPEN | OC_MODE_REPLACE,
+ attributes, AR_WRITE, &finfo);
+ if (result) {
+ if (result == 0x87)
+ error = -ENAMETOOLONG;
+ DPRINTK("ncp_create: %s/%s failed\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
+ goto out;
+ }
+ opmode = O_WRONLY;
+ }
+ finfo.access = opmode;
+ error = ncp_instantiate(dir, dentry, &finfo);
+out:
+ return error;
+}
+
+static int ncp_create(struct inode *dir, struct dentry *dentry, int mode)
+{
+ return ncp_create_new(dir, dentry, mode, 0);
+}
+
+static int ncp_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+ struct ncp_entry_info finfo;
+ struct ncp_server *server = NCP_SERVER(dir);
+ int error, len;
+ __u8 __name[NCP_MAXPATHLEN + 1];
+
+ DPRINTK("ncp_mkdir: making %s/%s\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
+ error = -EIO;
+ if (!ncp_conn_valid(server))
+ goto out;
+
+ ncp_age_dentry(server, dentry);
+ len = sizeof(__name);
+ error = ncp_io2vol(server, __name, &len, dentry->d_name.name,
+ dentry->d_name.len, !ncp_preserve_case(dir));
+ if (error)
+ goto out;
+
+ error = -EACCES;
+ if (ncp_open_create_file_or_subdir(server, dir, __name,
+ OC_MODE_CREATE, aDIR, 0xffff,
+ &finfo) == 0)
+ {
+ error = ncp_instantiate(dir, dentry, &finfo);
+ }
+out:
+ return error;
+}
+
+static int ncp_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ struct ncp_server *server = NCP_SERVER(dir);
+ int error, result, len;
+ __u8 __name[NCP_MAXPATHLEN + 1];
+
+ DPRINTK("ncp_rmdir: removing %s/%s\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
+
+ error = -EIO;
+ if (!ncp_conn_valid(server))
+ goto out;
+
+ error = -EBUSY;
+ if (!d_unhashed(dentry))
+ goto out;
+
+ len = sizeof(__name);
+ error = ncp_io2vol(server, __name, &len, dentry->d_name.name,
+ dentry->d_name.len, !ncp_preserve_case(dir));
+ if (error)
+ goto out;
+
+ result = ncp_del_file_or_subdir(server, dir, __name);
+ switch (result) {
+ case 0x00:
+ error = 0;
+ break;
+ case 0x85: /* unauthorized to delete file */
+ case 0x8A: /* unauthorized to delete file */
+ error = -EACCES;
+ break;
+ case 0x8F:
+ case 0x90: /* read only */
+ error = -EPERM;
+ break;
+ case 0x9F: /* in use by another client */
+ error = -EBUSY;
+ break;
+ case 0xA0: /* directory not empty */
+ error = -ENOTEMPTY;
+ break;
+ case 0xFF: /* someone deleted file */
+ error = -ENOENT;
+ break;
+ default:
+ error = -EACCES;
+ break;
+ }
+out:
+ return error;
+}
+
+static int ncp_unlink(struct inode *dir, struct dentry *dentry)
+{
+ struct inode *inode = dentry->d_inode;
+ struct ncp_server *server = NCP_SERVER(dir);
+ int error;
+
+ DPRINTK("ncp_unlink: unlinking %s/%s\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
+
+ error = -EIO;
+ if (!ncp_conn_valid(server))
+ goto out;
+
+ /*
+ * Check whether to close the file ...
+ */
+ if (inode) {
+ PPRINTK("ncp_unlink: closing file\n");
+ ncp_make_closed(inode);
+ }
+
+ error = ncp_del_file_or_subdir2(server, dentry);
+#ifdef CONFIG_NCPFS_STRONG
+ /* 9C is Invalid path.. It should be 8F, 90 - read only, but
+ it is not :-( */
+ if ((error == 0x9C || error == 0x90) && server->m.flags & NCP_MOUNT_STRONG) { /* R/O */
+ error = ncp_force_unlink(dir, dentry);
+ }
+#endif
+ switch (error) {
+ case 0x00:
+ DPRINTK("ncp: removed %s/%s\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
+ break;
+ case 0x85:
+ case 0x8A:
+ error = -EACCES;
+ break;
+ case 0x8D: /* some files in use */
+ case 0x8E: /* all files in use */
+ error = -EBUSY;
+ break;
+ case 0x8F: /* some read only */
+ case 0x90: /* all read only */
+ case 0x9C: /* !!! returned when in-use or read-only by NW4 */
+ error = -EPERM;
+ break;
+ case 0xFF:
+ error = -ENOENT;
+ break;
+ default:
+ error = -EACCES;
+ break;
+ }
+
+out:
+ return error;
+}
+
+static int ncp_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ struct ncp_server *server = NCP_SERVER(old_dir);
+ int error;
+ int old_len, new_len;
+ __u8 __old_name[NCP_MAXPATHLEN + 1], __new_name[NCP_MAXPATHLEN + 1];
+
+ DPRINTK("ncp_rename: %s/%s to %s/%s\n",
+ old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
+ new_dentry->d_parent->d_name.name, new_dentry->d_name.name);
+
+ error = -EIO;
+ if (!ncp_conn_valid(server))
+ goto out;
+
+ ncp_age_dentry(server, old_dentry);
+ ncp_age_dentry(server, new_dentry);
+
+ old_len = sizeof(__old_name);
+ error = ncp_io2vol(server, __old_name, &old_len,
+ old_dentry->d_name.name, old_dentry->d_name.len,
+ !ncp_preserve_case(old_dir));
+ if (error)
+ goto out;
+
+ new_len = sizeof(__new_name);
+ error = ncp_io2vol(server, __new_name, &new_len,
+ new_dentry->d_name.name, new_dentry->d_name.len,
+ !ncp_preserve_case(new_dir));
+ if (error)
+ goto out;
+
+ error = ncp_ren_or_mov_file_or_subdir(server, old_dir, __old_name,
+ new_dir, __new_name);
+#ifdef CONFIG_NCPFS_STRONG
+ if ((error == 0x90 || error == 0x8B || error == -EACCES) &&
+ server->m.flags & NCP_MOUNT_STRONG) { /* RO */
+ error = ncp_force_rename(old_dir, old_dentry, __old_name,
+ new_dir, new_dentry, __new_name);
+ }
+#endif
+ switch (error) {
+ case 0x00:
+ DPRINTK("ncp renamed %s -> %s.\n",
+ old_dentry->d_name.name,new_dentry->d_name.name);
+ break;
+ case 0x9E:
+ error = -ENAMETOOLONG;
+ break;
+ case 0xFF:
+ error = -ENOENT;
+ break;
+ default:
+ error = -EACCES;
+ break;
+ }
+out:
+ return error;
+}
+
+/* The following routines are taken directly from msdos-fs */
+
+/* Linear day numbers of the respective 1sts in non-leap years. */
+
+static int day_n[] =
+{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, 0};
+/* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */
+
+
+extern struct timezone sys_tz;
+
+static int utc2local(int time)
+{
+ return time - sys_tz.tz_minuteswest * 60;
+}
+
+static int local2utc(int time)
+{
+ return time + sys_tz.tz_minuteswest * 60;
+}
+
+/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
+int
+ncp_date_dos2unix(unsigned short time, unsigned short date)
+{
+ int month, year, secs;
+
+ /* first subtract and mask after that... Otherwise, if
+ date == 0, bad things happen */
+ month = ((date >> 5) - 1) & 15;
+ year = date >> 9;
+ secs = (time & 31) * 2 + 60 * ((time >> 5) & 63) + (time >> 11) * 3600 +
+ 86400 * ((date & 31) - 1 + day_n[month] + (year / 4) +
+ year * 365 - ((year & 3) == 0 && month < 2 ? 1 : 0) + 3653);
+ /* days since 1.1.70 plus 80's leap day */
+ return local2utc(secs);
+}
+
+
+/* Convert linear UNIX date to a MS-DOS time/date pair. */
+void
+ncp_date_unix2dos(int unix_date, unsigned short *time, unsigned short *date)
+{
+ int day, year, nl_day, month;
+
+ unix_date = utc2local(unix_date);
+ *time = (unix_date % 60) / 2 + (((unix_date / 60) % 60) << 5) +
+ (((unix_date / 3600) % 24) << 11);
+ day = unix_date / 86400 - 3652;
+ year = day / 365;
+ if ((year + 3) / 4 + 365 * year > day)
+ year--;
+ day -= (year + 3) / 4 + 365 * year;
+ if (day == 59 && !(year & 3)) {
+ nl_day = day;
+ month = 2;
+ } else {
+ nl_day = (year & 3) || day <= 59 ? day : day - 1;
+ for (month = 0; month < 12; month++)
+ if (day_n[month] > nl_day)
+ break;
+ }
+ *date = nl_day - day_n[month - 1] + 1 + (month << 5) + (year << 9);
+}
diff --git a/fs/ncpfs/file.c b/fs/ncpfs/file.c
new file mode 100644
index 00000000000000..1f70ec451ce709
--- /dev/null
+++ b/fs/ncpfs/file.c
@@ -0,0 +1,296 @@
+/*
+ * file.c
+ *
+ * Copyright (C) 1995, 1996 by Volker Lendecke
+ * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
+ *
+ */
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/locks.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#include <linux/ncp_fs.h>
+#include "ncplib_kernel.h"
+
+static int ncp_fsync(struct file *file, struct dentry *dentry, int datasync)
+{
+ return 0;
+}
+
+/*
+ * Open a file with the specified read/write mode.
+ */
+int ncp_make_open(struct inode *inode, int right)
+{
+ int error;
+ int access;
+
+ error = -EINVAL;
+ if (!inode) {
+ printk(KERN_ERR "ncp_make_open: got NULL inode\n");
+ goto out;
+ }
+
+ DPRINTK("ncp_make_open: opened=%d, volume # %u, dir entry # %u\n",
+ atomic_read(&NCP_FINFO(inode)->opened),
+ NCP_FINFO(inode)->volNumber,
+ NCP_FINFO(inode)->dirEntNum);
+ error = -EACCES;
+ down(&NCP_FINFO(inode)->open_sem);
+ if (!atomic_read(&NCP_FINFO(inode)->opened)) {
+ struct ncp_entry_info finfo;
+ int result;
+
+ finfo.i.dirEntNum = NCP_FINFO(inode)->dirEntNum;
+ finfo.i.volNumber = NCP_FINFO(inode)->volNumber;
+ /* tries max. rights */
+ finfo.access = O_RDWR;
+ result = ncp_open_create_file_or_subdir(NCP_SERVER(inode),
+ NULL, NULL, OC_MODE_OPEN,
+ 0, AR_READ | AR_WRITE, &finfo);
+ if (!result)
+ goto update;
+ /* RDWR did not succeeded, try readonly or writeonly as requested */
+ switch (right) {
+ case O_RDONLY:
+ finfo.access = O_RDONLY;
+ result = ncp_open_create_file_or_subdir(NCP_SERVER(inode),
+ NULL, NULL, OC_MODE_OPEN,
+ 0, AR_READ, &finfo);
+ break;
+ case O_WRONLY:
+ finfo.access = O_WRONLY;
+ result = ncp_open_create_file_or_subdir(NCP_SERVER(inode),
+ NULL, NULL, OC_MODE_OPEN,
+ 0, AR_WRITE, &finfo);
+ break;
+ }
+ if (result) {
+ PPRINTK("ncp_make_open: failed, result=%d\n", result);
+ goto out_unlock;
+ }
+ /*
+ * Update the inode information.
+ */
+ update:
+ ncp_update_inode(inode, &finfo);
+ atomic_set(&NCP_FINFO(inode)->opened, 1);
+ }
+
+ access = NCP_FINFO(inode)->access;
+ PPRINTK("ncp_make_open: file open, access=%x\n", access);
+ if (access == right || access == O_RDWR) {
+ atomic_inc(&NCP_FINFO(inode)->opened);
+ error = 0;
+ }
+
+out_unlock:
+ up(&NCP_FINFO(inode)->open_sem);
+out:
+ return error;
+}
+
+static ssize_t
+ncp_file_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+ struct dentry *dentry = file->f_dentry;
+ struct inode *inode = dentry->d_inode;
+ size_t already_read = 0;
+ off_t pos;
+ size_t bufsize;
+ int error;
+ void* freepage;
+ size_t freelen;
+
+ DPRINTK("ncp_file_read: enter %s/%s\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
+
+ error = -EIO;
+ if (!ncp_conn_valid(NCP_SERVER(inode)))
+ goto out;
+ error = -EINVAL;
+ if (!S_ISREG(inode->i_mode)) {
+ DPRINTK("ncp_file_read: read from non-file, mode %07o\n",
+ inode->i_mode);
+ goto out;
+ }
+
+ pos = *ppos;
+/* leave it out on server ...
+ if (pos + count > inode->i_size) {
+ count = inode->i_size - pos;
+ }
+*/
+ error = 0;
+ if (!count) /* size_t is never < 0 */
+ goto out;
+
+ error = ncp_make_open(inode, O_RDONLY);
+ if (error) {
+ DPRINTK(KERN_ERR "ncp_file_read: open failed, error=%d\n", error);
+ goto out;
+ }
+
+ bufsize = NCP_SERVER(inode)->buffer_size;
+
+ error = -EIO;
+ freelen = ncp_read_bounce_size(bufsize);
+ freepage = vmalloc(freelen);
+ if (!freepage)
+ goto outrel;
+ error = 0;
+ /* First read in as much as possible for each bufsize. */
+ while (already_read < count) {
+ int read_this_time;
+ size_t to_read = min_t(unsigned int,
+ bufsize - (pos % bufsize),
+ count - already_read);
+
+ error = ncp_read_bounce(NCP_SERVER(inode),
+ NCP_FINFO(inode)->file_handle,
+ pos, to_read, buf, &read_this_time,
+ freepage, freelen);
+ if (error) {
+ error = -EIO; /* NW errno -> Linux errno */
+ break;
+ }
+ pos += read_this_time;
+ buf += read_this_time;
+ already_read += read_this_time;
+
+ if (read_this_time != to_read) {
+ break;
+ }
+ }
+ vfree(freepage);
+
+ *ppos = pos;
+
+ if (!IS_RDONLY(inode)) {
+ inode->i_atime = CURRENT_TIME;
+ }
+
+ DPRINTK("ncp_file_read: exit %s/%s\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
+outrel:
+ ncp_inode_close(inode);
+out:
+ return already_read ? already_read : error;
+}
+
+static ssize_t
+ncp_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
+{
+ struct dentry *dentry = file->f_dentry;
+ struct inode *inode = dentry->d_inode;
+ size_t already_written = 0;
+ off_t pos;
+ size_t bufsize;
+ int errno;
+ void* bouncebuffer;
+
+ DPRINTK("ncp_file_write: enter %s/%s\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
+ errno = -EIO;
+ if (!ncp_conn_valid(NCP_SERVER(inode)))
+ goto out;
+ if (!S_ISREG(inode->i_mode)) {
+ DPRINTK("ncp_file_write: write to non-file, mode %07o\n",
+ inode->i_mode);
+ return -EINVAL;
+ }
+
+ errno = 0;
+ if (!count)
+ goto out;
+ errno = ncp_make_open(inode, O_WRONLY);
+ if (errno) {
+ DPRINTK(KERN_ERR "ncp_file_write: open failed, error=%d\n", errno);
+ return errno;
+ }
+ pos = *ppos;
+
+ if (file->f_flags & O_APPEND) {
+ pos = inode->i_size;
+ }
+ bufsize = NCP_SERVER(inode)->buffer_size;
+
+ already_written = 0;
+
+ bouncebuffer = vmalloc(bufsize);
+ if (!bouncebuffer) {
+ errno = -EIO; /* -ENOMEM */
+ goto outrel;
+ }
+ while (already_written < count) {
+ int written_this_time;
+ size_t to_write = min_t(unsigned int,
+ bufsize - (pos % bufsize),
+ count - already_written);
+
+ if (copy_from_user(bouncebuffer, buf, to_write)) {
+ errno = -EFAULT;
+ break;
+ }
+ if (ncp_write_kernel(NCP_SERVER(inode),
+ NCP_FINFO(inode)->file_handle,
+ pos, to_write, bouncebuffer, &written_this_time) != 0) {
+ errno = -EIO;
+ break;
+ }
+ pos += written_this_time;
+ buf += written_this_time;
+ already_written += written_this_time;
+
+ if (written_this_time != to_write) {
+ break;
+ }
+ }
+ vfree(bouncebuffer);
+ inode->i_mtime = inode->i_atime = CURRENT_TIME;
+
+ *ppos = pos;
+
+ if (pos > inode->i_size) {
+ inode->i_size = pos;
+ }
+ DPRINTK("ncp_file_write: exit %s/%s\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
+outrel:
+ ncp_inode_close(inode);
+out:
+ return already_written ? already_written : errno;
+}
+
+static int ncp_release(struct inode *inode, struct file *file) {
+ if (ncp_make_closed(inode)) {
+ DPRINTK("ncp_release: failed to close\n");
+ }
+ return 0;
+}
+
+struct file_operations ncp_file_operations =
+{
+ llseek: generic_file_llseek,
+ read: ncp_file_read,
+ write: ncp_file_write,
+ ioctl: ncp_ioctl,
+ mmap: ncp_mmap,
+ release: ncp_release,
+ fsync: ncp_fsync,
+};
+
+struct inode_operations ncp_file_inode_operations =
+{
+ setattr: ncp_notify_change,
+};
diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c
new file mode 100644
index 00000000000000..78d91a230d1859
--- /dev/null
+++ b/fs/ncpfs/inode.c
@@ -0,0 +1,733 @@
+/*
+ * inode.c
+ *
+ * Copyright (C) 1995, 1996 by Volker Lendecke
+ * Modified for big endian by J.F. Chadima and David S. Miller
+ * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
+ * Modified 1998 Wolfram Pienkoss for NLS
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/byteorder.h>
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <linux/file.h>
+#include <linux/fcntl.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+
+#include <linux/ncp_fs.h>
+
+#include "ncplib_kernel.h"
+
+static void ncp_delete_inode(struct inode *);
+static void ncp_put_super(struct super_block *);
+static int ncp_statfs(struct super_block *, struct statfs *);
+
+static struct super_operations ncp_sops =
+{
+ put_inode: force_delete,
+ delete_inode: ncp_delete_inode,
+ put_super: ncp_put_super,
+ statfs: ncp_statfs,
+};
+
+extern struct dentry_operations ncp_root_dentry_operations;
+#ifdef CONFIG_NCPFS_EXTRAS
+extern struct address_space_operations ncp_symlink_aops;
+extern int ncp_symlink(struct inode*, struct dentry*, const char*);
+#endif
+
+/*
+ * Fill in the ncpfs-specific information in the inode.
+ */
+void ncp_update_inode(struct inode *inode, struct ncp_entry_info *nwinfo)
+{
+ NCP_FINFO(inode)->DosDirNum = nwinfo->i.DosDirNum;
+ NCP_FINFO(inode)->dirEntNum = nwinfo->i.dirEntNum;
+ NCP_FINFO(inode)->volNumber = nwinfo->i.volNumber;
+
+#ifdef CONFIG_NCPFS_STRONG
+ NCP_FINFO(inode)->nwattr = nwinfo->i.attributes;
+#endif
+ NCP_FINFO(inode)->access = nwinfo->access;
+ NCP_FINFO(inode)->server_file_handle = nwinfo->server_file_handle;
+ memcpy(NCP_FINFO(inode)->file_handle, nwinfo->file_handle,
+ sizeof(nwinfo->file_handle));
+ DPRINTK("ncp_update_inode: updated %s, volnum=%d, dirent=%u\n",
+ nwinfo->i.entryName, NCP_FINFO(inode)->volNumber,
+ NCP_FINFO(inode)->dirEntNum);
+}
+
+void ncp_update_inode2(struct inode* inode, struct ncp_entry_info *nwinfo)
+{
+ struct nw_info_struct *nwi = &nwinfo->i;
+ struct ncp_server *server = NCP_SERVER(inode);
+
+ if (!atomic_read(&NCP_FINFO(inode)->opened)) {
+#ifdef CONFIG_NCPFS_STRONG
+ NCP_FINFO(inode)->nwattr = nwi->attributes;
+#endif
+ if (nwi->attributes & aDIR) {
+ inode->i_mode = server->m.dir_mode;
+ inode->i_size = NCP_BLOCK_SIZE;
+ } else {
+ inode->i_mode = server->m.file_mode;
+ inode->i_size = le32_to_cpu(nwi->dataStreamSize);
+#ifdef CONFIG_NCPFS_EXTRAS
+ if ((server->m.flags & (NCP_MOUNT_EXTRAS|NCP_MOUNT_SYMLINKS)) && (nwi->attributes & aSHARED)) {
+ switch (nwi->attributes & (aHIDDEN|aSYSTEM)) {
+ case aHIDDEN:
+ if (server->m.flags & NCP_MOUNT_SYMLINKS) {
+ if ( /* (inode->i_size >= NCP_MIN_SYMLINK_SIZE)
+ && */ (inode->i_size <= NCP_MAX_SYMLINK_SIZE)) {
+ inode->i_mode = (inode->i_mode & ~S_IFMT) | S_IFLNK;
+ break;
+ }
+ }
+ /* FALLTHROUGH */
+ case 0:
+ if (server->m.flags & NCP_MOUNT_EXTRAS)
+ inode->i_mode |= 0444;
+ break;
+ case aSYSTEM:
+ if (server->m.flags & NCP_MOUNT_EXTRAS)
+ inode->i_mode |= (inode->i_mode >> 2) & 0111;
+ break;
+ /* case aSYSTEM|aHIDDEN: */
+ default:
+ /* reserved combination */
+ break;
+ }
+ }
+#endif
+ }
+ if (nwi->attributes & aRONLY) inode->i_mode &= ~0222;
+ }
+ inode->i_blocks = (inode->i_size + NCP_BLOCK_SIZE - 1) >> NCP_BLOCK_SHIFT;
+
+ inode->i_mtime = ncp_date_dos2unix(le16_to_cpu(nwi->modifyTime),
+ le16_to_cpu(nwi->modifyDate));
+ inode->i_ctime = ncp_date_dos2unix(le16_to_cpu(nwi->creationTime),
+ le16_to_cpu(nwi->creationDate));
+ inode->i_atime = ncp_date_dos2unix(0, le16_to_cpu(nwi->lastAccessDate));
+
+ NCP_FINFO(inode)->DosDirNum = nwi->DosDirNum;
+ NCP_FINFO(inode)->dirEntNum = nwi->dirEntNum;
+ NCP_FINFO(inode)->volNumber = nwi->volNumber;
+}
+
+/*
+ * Fill in the inode based on the ncp_entry_info structure.
+ */
+static void ncp_set_attr(struct inode *inode, struct ncp_entry_info *nwinfo)
+{
+ struct nw_info_struct *nwi = &nwinfo->i;
+ struct ncp_server *server = NCP_SERVER(inode);
+
+ if (nwi->attributes & aDIR) {
+ inode->i_mode = server->m.dir_mode;
+ /* for directories dataStreamSize seems to be some
+ Object ID ??? */
+ inode->i_size = NCP_BLOCK_SIZE;
+ } else {
+ inode->i_mode = server->m.file_mode;
+ inode->i_size = le32_to_cpu(nwi->dataStreamSize);
+#ifdef CONFIG_NCPFS_EXTRAS
+ if ((server->m.flags & (NCP_MOUNT_EXTRAS|NCP_MOUNT_SYMLINKS))
+ && (nwi->attributes & aSHARED)) {
+ switch (nwi->attributes & (aHIDDEN|aSYSTEM)) {
+ case aHIDDEN:
+ if (server->m.flags & NCP_MOUNT_SYMLINKS) {
+ if (/* (inode->i_size >= NCP_MIN_SYMLINK_SIZE)
+ && */ (inode->i_size <= NCP_MAX_SYMLINK_SIZE)) {
+ inode->i_mode = (inode->i_mode & ~S_IFMT) | S_IFLNK;
+ break;
+ }
+ }
+ /* FALLTHROUGH */
+ case 0:
+ if (server->m.flags & NCP_MOUNT_EXTRAS)
+ inode->i_mode |= 0444;
+ break;
+ case aSYSTEM:
+ if (server->m.flags & NCP_MOUNT_EXTRAS)
+ inode->i_mode |= (inode->i_mode >> 2) & 0111;
+ break;
+ /* case aSYSTEM|aHIDDEN: */
+ default:
+ /* reserved combination */
+ break;
+ }
+ }
+#endif
+ }
+ if (nwi->attributes & aRONLY) inode->i_mode &= ~0222;
+
+ DDPRINTK("ncp_read_inode: inode->i_mode = %u\n", inode->i_mode);
+
+ inode->i_nlink = 1;
+ inode->i_uid = server->m.uid;
+ inode->i_gid = server->m.gid;
+ inode->i_rdev = 0;
+ inode->i_blksize = NCP_BLOCK_SIZE;
+
+ inode->i_blocks = (inode->i_size + NCP_BLOCK_SIZE - 1) >> NCP_BLOCK_SHIFT;
+
+ inode->i_mtime = ncp_date_dos2unix(le16_to_cpu(nwi->modifyTime),
+ le16_to_cpu(nwi->modifyDate));
+ inode->i_ctime = ncp_date_dos2unix(le16_to_cpu(nwi->creationTime),
+ le16_to_cpu(nwi->creationDate));
+ inode->i_atime = ncp_date_dos2unix(0,
+ le16_to_cpu(nwi->lastAccessDate));
+ ncp_update_inode(inode, nwinfo);
+}
+
+static struct inode_operations ncp_symlink_inode_operations = {
+ readlink: page_readlink,
+ follow_link: page_follow_link,
+ setattr: ncp_notify_change,
+};
+
+/*
+ * Get a new inode.
+ */
+struct inode *
+ncp_iget(struct super_block *sb, struct ncp_entry_info *info)
+{
+ struct inode *inode;
+
+ if (info == NULL) {
+ printk(KERN_ERR "ncp_iget: info is NULL\n");
+ return NULL;
+ }
+
+ inode = new_inode(sb);
+ if (inode) {
+ init_MUTEX(&NCP_FINFO(inode)->open_sem);
+ atomic_set(&NCP_FINFO(inode)->opened, info->opened);
+
+ inode->i_ino = info->ino;
+ ncp_set_attr(inode, info);
+ if (S_ISREG(inode->i_mode)) {
+ inode->i_op = &ncp_file_inode_operations;
+ inode->i_fop = &ncp_file_operations;
+ } else if (S_ISDIR(inode->i_mode)) {
+ inode->i_op = &ncp_dir_inode_operations;
+ inode->i_fop = &ncp_dir_operations;
+#ifdef CONFIG_NCPFS_EXTRAS
+ } else if (S_ISLNK(inode->i_mode)) {
+ inode->i_op = &ncp_symlink_inode_operations;
+ inode->i_data.a_ops = &ncp_symlink_aops;
+#endif
+ }
+ insert_inode_hash(inode);
+ } else
+ printk(KERN_ERR "ncp_iget: iget failed!\n");
+ return inode;
+}
+
+static void
+ncp_delete_inode(struct inode *inode)
+{
+ if (S_ISDIR(inode->i_mode)) {
+ DDPRINTK("ncp_delete_inode: put directory %ld\n", inode->i_ino);
+ }
+
+ if (ncp_make_closed(inode) != 0) {
+ /* We can't do anything but complain. */
+ printk(KERN_ERR "ncp_delete_inode: could not close\n");
+ }
+ clear_inode(inode);
+}
+
+struct super_block *
+ncp_read_super(struct super_block *sb, void *raw_data, int silent)
+{
+ struct ncp_mount_data_kernel data;
+ struct ncp_server *server;
+ struct file *ncp_filp;
+ struct inode *root_inode;
+ struct inode *sock_inode;
+ struct socket *sock;
+ int error;
+ int default_bufsize;
+#ifdef CONFIG_NCPFS_PACKET_SIGNING
+ int options;
+#endif
+ struct ncp_entry_info finfo;
+
+ if (raw_data == NULL)
+ goto out_no_data;
+ switch (*(int*)raw_data) {
+ case NCP_MOUNT_VERSION:
+ {
+ struct ncp_mount_data* md = (struct ncp_mount_data*)raw_data;
+
+ data.flags = md->flags;
+ data.int_flags = NCP_IMOUNT_LOGGEDIN_POSSIBLE;
+ data.mounted_uid = md->mounted_uid;
+ data.wdog_pid = md->wdog_pid;
+ data.ncp_fd = md->ncp_fd;
+ data.time_out = md->time_out;
+ data.retry_count = md->retry_count;
+ data.uid = md->uid;
+ data.gid = md->gid;
+ data.file_mode = md->file_mode;
+ data.dir_mode = md->dir_mode;
+ memcpy(data.mounted_vol, md->mounted_vol,
+ NCP_VOLNAME_LEN+1);
+ }
+ break;
+ case NCP_MOUNT_VERSION_V4:
+ {
+ struct ncp_mount_data_v4* md = (struct ncp_mount_data_v4*)raw_data;
+
+ data.flags = md->flags;
+ data.int_flags = 0;
+ data.mounted_uid = md->mounted_uid;
+ data.wdog_pid = md->wdog_pid;
+ data.ncp_fd = md->ncp_fd;
+ data.time_out = md->time_out;
+ data.retry_count = md->retry_count;
+ data.uid = md->uid;
+ data.gid = md->gid;
+ data.file_mode = md->file_mode;
+ data.dir_mode = md->dir_mode;
+ data.mounted_vol[0] = 0;
+ }
+ break;
+ default:
+ goto out_bad_mount;
+ }
+ ncp_filp = fget(data.ncp_fd);
+ if (!ncp_filp)
+ goto out_bad_file;
+ sock_inode = ncp_filp->f_dentry->d_inode;
+ if (!S_ISSOCK(sock_inode->i_mode))
+ goto out_bad_file2;
+ sock = &sock_inode->u.socket_i;
+ if (!sock)
+ goto out_bad_file2;
+
+ if (sock->type == SOCK_STREAM)
+ default_bufsize = 61440;
+ else
+ default_bufsize = 1024;
+
+ sb->s_blocksize = 1024; /* Eh... Is this correct? */
+ sb->s_blocksize_bits = 10;
+ sb->s_magic = NCP_SUPER_MAGIC;
+ sb->s_op = &ncp_sops;
+
+ server = NCP_SBP(sb);
+ memset(server, 0, sizeof(*server));
+
+ server->ncp_filp = ncp_filp;
+/* server->lock = 0; */
+ init_MUTEX(&server->sem);
+ server->packet = NULL;
+/* server->buffer_size = 0; */
+/* server->conn_status = 0; */
+/* server->root_dentry = NULL; */
+/* server->root_setuped = 0; */
+#ifdef CONFIG_NCPFS_PACKET_SIGNING
+/* server->sign_wanted = 0; */
+/* server->sign_active = 0; */
+#endif
+ server->auth.auth_type = NCP_AUTH_NONE;
+/* server->auth.object_name_len = 0; */
+/* server->auth.object_name = NULL; */
+/* server->auth.object_type = 0; */
+/* server->priv.len = 0; */
+/* server->priv.data = NULL; */
+
+ server->m = data;
+ /* Althought anything producing this is buggy, it happens
+ now because of PATH_MAX changes.. */
+ if (server->m.time_out < 1) {
+ server->m.time_out = 10;
+ printk(KERN_INFO "You need to recompile your ncpfs utils..\n");
+ }
+ server->m.time_out = server->m.time_out * HZ / 100;
+ server->m.file_mode = (server->m.file_mode &
+ (S_IRWXU | S_IRWXG | S_IRWXO)) | S_IFREG;
+ server->m.dir_mode = (server->m.dir_mode &
+ (S_IRWXU | S_IRWXG | S_IRWXO)) | S_IFDIR;
+
+#ifdef CONFIG_NCPFS_NLS
+ /* load the default NLS charsets */
+ server->nls_vol = load_nls_default();
+ server->nls_io = load_nls_default();
+#endif /* CONFIG_NCPFS_NLS */
+
+ server->dentry_ttl = 0; /* no caching */
+
+#undef NCP_PACKET_SIZE
+#define NCP_PACKET_SIZE 65536
+ server->packet_size = NCP_PACKET_SIZE;
+ server->packet = vmalloc(NCP_PACKET_SIZE);
+ if (server->packet == NULL)
+ goto out_no_packet;
+
+ ncp_lock_server(server);
+ error = ncp_connect(server);
+ ncp_unlock_server(server);
+ if (error < 0)
+ goto out_no_connect;
+ DPRINTK("ncp_read_super: NCP_SBP(sb) = %x\n", (int) NCP_SBP(sb));
+
+#ifdef CONFIG_NCPFS_PACKET_SIGNING
+ if (ncp_negotiate_size_and_options(server, default_bufsize,
+ NCP_DEFAULT_OPTIONS, &(server->buffer_size), &options) == 0)
+ {
+ if (options != NCP_DEFAULT_OPTIONS)
+ {
+ if (ncp_negotiate_size_and_options(server,
+ default_bufsize,
+ options & 2,
+ &(server->buffer_size), &options) != 0)
+
+ {
+ goto out_no_bufsize;
+ }
+ }
+ if (options & 2)
+ server->sign_wanted = 1;
+ }
+ else
+#endif /* CONFIG_NCPFS_PACKET_SIGNING */
+ if (ncp_negotiate_buffersize(server, default_bufsize,
+ &(server->buffer_size)) != 0)
+ goto out_no_bufsize;
+ DPRINTK("ncpfs: bufsize = %d\n", server->buffer_size);
+
+ memset(&finfo, 0, sizeof(finfo));
+ finfo.i.attributes = aDIR;
+ finfo.i.dataStreamSize = NCP_BLOCK_SIZE;
+ finfo.i.dirEntNum = 0;
+ finfo.i.DosDirNum = 0;
+#ifdef CONFIG_NCPFS_SMALLDOS
+ finfo.i.NSCreator = NW_NS_DOS;
+#endif
+ finfo.i.volNumber = NCP_NUMBER_OF_VOLUMES + 1; /* illegal volnum */
+ /* set dates of mountpoint to Jan 1, 1986; 00:00 */
+ finfo.i.creationTime = finfo.i.modifyTime
+ = cpu_to_le16(0x0000);
+ finfo.i.creationDate = finfo.i.modifyDate
+ = finfo.i.lastAccessDate
+ = cpu_to_le16(0x0C21);
+ finfo.i.nameLen = 0;
+ finfo.i.entryName[0] = '\0';
+
+ finfo.opened = 0;
+ finfo.ino = 2; /* tradition */
+
+ server->name_space[finfo.i.volNumber] = NW_NS_DOS;
+ root_inode = ncp_iget(sb, &finfo);
+ if (!root_inode)
+ goto out_no_root;
+ DPRINTK("ncp_read_super: root vol=%d\n", NCP_FINFO(root_inode)->volNumber);
+ sb->s_root = d_alloc_root(root_inode);
+ if (!sb->s_root)
+ goto out_no_root;
+ sb->s_root->d_op = &ncp_root_dentry_operations;
+ return sb;
+
+out_no_root:
+ printk(KERN_ERR "ncp_read_super: get root inode failed\n");
+ iput(root_inode);
+ goto out_disconnect;
+out_no_bufsize:
+ printk(KERN_ERR "ncp_read_super: could not get bufsize\n");
+out_disconnect:
+ ncp_lock_server(server);
+ ncp_disconnect(server);
+ ncp_unlock_server(server);
+ goto out_free_packet;
+out_no_connect:
+ printk(KERN_ERR "ncp_read_super: Failed connection, error=%d\n", error);
+out_free_packet:
+ vfree(server->packet);
+ goto out_free_server;
+out_no_packet:
+ printk(KERN_ERR "ncp_read_super: could not alloc packet\n");
+out_free_server:
+#ifdef CONFIG_NCPFS_NLS
+ unload_nls(server->nls_io);
+ unload_nls(server->nls_vol);
+#endif
+ /* 23/12/1998 Marcin Dalecki <dalecki@cs.net.pl>:
+ *
+ * The previously used put_filp(ncp_filp); was bogous, since
+ * it doesn't proper unlocking.
+ */
+ fput(ncp_filp);
+ goto out;
+
+out_bad_file2:
+ fput(ncp_filp);
+out_bad_file:
+ printk(KERN_ERR "ncp_read_super: invalid ncp socket\n");
+ goto out;
+out_bad_mount:
+ printk(KERN_INFO "ncp_read_super: kernel requires mount version %d\n",
+ NCP_MOUNT_VERSION);
+ goto out;
+out_no_data:
+ printk(KERN_ERR "ncp_read_super: missing data argument\n");
+out:
+ return NULL;
+}
+
+static void ncp_put_super(struct super_block *sb)
+{
+ struct ncp_server *server = NCP_SBP(sb);
+
+ ncp_lock_server(server);
+ ncp_disconnect(server);
+ ncp_unlock_server(server);
+
+#ifdef CONFIG_NCPFS_NLS
+ /* unload the NLS charsets */
+ if (server->nls_vol)
+ {
+ unload_nls(server->nls_vol);
+ server->nls_vol = NULL;
+ }
+ if (server->nls_io)
+ {
+ unload_nls(server->nls_io);
+ server->nls_io = NULL;
+ }
+#endif /* CONFIG_NCPFS_NLS */
+
+ fput(server->ncp_filp);
+ kill_proc(server->m.wdog_pid, SIGTERM, 1);
+
+ if (server->priv.data)
+ ncp_kfree_s(server->priv.data, server->priv.len);
+ if (server->auth.object_name)
+ ncp_kfree_s(server->auth.object_name, server->auth.object_name_len);
+ vfree(server->packet);
+
+}
+
+static int ncp_statfs(struct super_block *sb, struct statfs *buf)
+{
+ /* We cannot say how much disk space is left on a mounted
+ NetWare Server, because free space is distributed over
+ volumes, and the current user might have disk quotas. So
+ free space is not that simple to determine. Our decision
+ here is to err conservatively. */
+
+ buf->f_type = NCP_SUPER_MAGIC;
+ buf->f_bsize = NCP_BLOCK_SIZE;
+ buf->f_blocks = 0;
+ buf->f_bfree = 0;
+ buf->f_bavail = 0;
+ buf->f_namelen = 12;
+ return 0;
+}
+
+int ncp_notify_change(struct dentry *dentry, struct iattr *attr)
+{
+ struct inode *inode = dentry->d_inode;
+ int result = 0;
+ int info_mask;
+ struct nw_modify_dos_info info;
+ struct ncp_server *server;
+
+ result = -EIO;
+
+ server = NCP_SERVER(inode);
+ if ((!server) || !ncp_conn_valid(server))
+ goto out;
+
+ /* ageing the dentry to force validation */
+ ncp_age_dentry(server, dentry);
+
+ result = inode_change_ok(inode, attr);
+ if (result < 0)
+ goto out;
+
+ result = -EPERM;
+ if (((attr->ia_valid & ATTR_UID) &&
+ (attr->ia_uid != server->m.uid)))
+ goto out;
+
+ if (((attr->ia_valid & ATTR_GID) &&
+ (attr->ia_gid != server->m.gid)))
+ goto out;
+
+ if (((attr->ia_valid & ATTR_MODE) &&
+ (attr->ia_mode &
+ ~(S_IFREG | S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO))))
+ goto out;
+
+ info_mask = 0;
+ memset(&info, 0, sizeof(info));
+
+#if 1
+ if ((attr->ia_valid & ATTR_MODE) != 0)
+ {
+ if (S_ISDIR(inode->i_mode)) {
+ umode_t newmode;
+
+ info_mask |= DM_ATTRIBUTES;
+ newmode = attr->ia_mode;
+ newmode &= NCP_SERVER(inode)->m.dir_mode;
+
+ if (newmode & 0222)
+ info.attributes &= ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
+ else
+ info.attributes |= (aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
+ } else if (!S_ISREG(inode->i_mode))
+ {
+ return -EPERM;
+ }
+ else
+ {
+ umode_t newmode;
+#ifdef CONFIG_NCPFS_EXTRAS
+ int extras;
+
+ extras = server->m.flags & NCP_MOUNT_EXTRAS;
+#endif
+ info_mask |= DM_ATTRIBUTES;
+ newmode=attr->ia_mode;
+#ifdef CONFIG_NCPFS_EXTRAS
+ if (!extras)
+#endif
+ newmode &= server->m.file_mode;
+
+ if (newmode & 0222) /* any write bit set */
+ {
+ info.attributes &= ~(aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
+ }
+ else
+ {
+ info.attributes |= (aRONLY|aRENAMEINHIBIT|aDELETEINHIBIT);
+ }
+#ifdef CONFIG_NCPFS_EXTRAS
+ if (extras) {
+ if (newmode & 0111) /* any execute bit set */
+ info.attributes |= aSHARED | aSYSTEM;
+ /* read for group/world and not in default file_mode */
+ else if (newmode & ~server->m.file_mode & 0444)
+ info.attributes |= aSHARED;
+ }
+#endif
+ }
+ }
+#endif
+
+ if ((attr->ia_valid & ATTR_CTIME) != 0) {
+ info_mask |= (DM_CREATE_TIME | DM_CREATE_DATE);
+ ncp_date_unix2dos(attr->ia_ctime,
+ &(info.creationTime), &(info.creationDate));
+ info.creationTime = le16_to_cpu(info.creationTime);
+ info.creationDate = le16_to_cpu(info.creationDate);
+ }
+ if ((attr->ia_valid & ATTR_MTIME) != 0) {
+ info_mask |= (DM_MODIFY_TIME | DM_MODIFY_DATE);
+ ncp_date_unix2dos(attr->ia_mtime,
+ &(info.modifyTime), &(info.modifyDate));
+ info.modifyTime = le16_to_cpu(info.modifyTime);
+ info.modifyDate = le16_to_cpu(info.modifyDate);
+ }
+ if ((attr->ia_valid & ATTR_ATIME) != 0) {
+ __u16 dummy;
+ info_mask |= (DM_LAST_ACCESS_DATE);
+ ncp_date_unix2dos(attr->ia_atime,
+ &(dummy), &(info.lastAccessDate));
+ info.lastAccessDate = le16_to_cpu(info.lastAccessDate);
+ }
+ if (info_mask != 0) {
+ result = ncp_modify_file_or_subdir_dos_info(NCP_SERVER(inode),
+ inode, info_mask, &info);
+ if (result != 0) {
+ result = -EACCES;
+
+ if (info_mask == (DM_CREATE_TIME | DM_CREATE_DATE)) {
+ /* NetWare seems not to allow this. I
+ do not know why. So, just tell the
+ user everything went fine. This is
+ a terrible hack, but I do not know
+ how to do this correctly. */
+ result = 0;
+ }
+ }
+#ifdef CONFIG_NCPFS_STRONG
+ if ((!result) && (info_mask & DM_ATTRIBUTES))
+ NCP_FINFO(inode)->nwattr = info.attributes;
+#endif
+ }
+ if ((attr->ia_valid & ATTR_SIZE) != 0) {
+ int written;
+
+ DPRINTK("ncpfs: trying to change size to %ld\n",
+ attr->ia_size);
+
+ if ((result = ncp_make_open(inode, O_WRONLY)) < 0) {
+ return -EACCES;
+ }
+ ncp_write_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle,
+ attr->ia_size, 0, "", &written);
+
+ /* According to ndir, the changes only take effect after
+ closing the file */
+ ncp_inode_close(inode);
+ result = ncp_make_closed(inode);
+ if (!result)
+ result = vmtruncate(inode, attr->ia_size);
+ }
+out:
+ return result;
+}
+
+#ifdef DEBUG_NCP_MALLOC
+int ncp_malloced;
+int ncp_current_malloced;
+#endif
+
+static DECLARE_FSTYPE(ncp_fs_type, "ncpfs", ncp_read_super, 0);
+
+static int __init init_ncp_fs(void)
+{
+ DPRINTK("ncpfs: init_module called\n");
+
+#ifdef DEBUG_NCP_MALLOC
+ ncp_malloced = 0;
+ ncp_current_malloced = 0;
+#endif
+ return register_filesystem(&ncp_fs_type);
+}
+
+static void __exit exit_ncp_fs(void)
+{
+ DPRINTK("ncpfs: cleanup_module called\n");
+ unregister_filesystem(&ncp_fs_type);
+#ifdef DEBUG_NCP_MALLOC
+ PRINTK("ncp_malloced: %d\n", ncp_malloced);
+ PRINTK("ncp_current_malloced: %d\n", ncp_current_malloced);
+#endif
+}
+
+EXPORT_NO_SYMBOLS;
+
+module_init(init_ncp_fs)
+module_exit(exit_ncp_fs)
+MODULE_LICENSE("GPL");
diff --git a/fs/ncpfs/ioctl.c b/fs/ncpfs/ioctl.c
new file mode 100644
index 00000000000000..2d5f8c07a6b556
--- /dev/null
+++ b/fs/ncpfs/ioctl.c
@@ -0,0 +1,644 @@
+/*
+ * ioctl.c
+ *
+ * Copyright (C) 1995, 1996 by Volker Lendecke
+ * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
+ * Modified 1998, 1999 Wolfram Pienkoss for NLS
+ *
+ */
+
+#include <linux/config.h>
+
+#include <asm/uaccess.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/ioctl.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/highuid.h>
+#include <linux/vmalloc.h>
+
+#include <linux/ncp_fs.h>
+
+#include "ncplib_kernel.h"
+
+/* maximum limit for ncp_objectname_ioctl */
+#define NCP_OBJECT_NAME_MAX_LEN 4096
+/* maximum limit for ncp_privatedata_ioctl */
+#define NCP_PRIVATE_DATA_MAX_LEN 8192
+/* maximum negotiable packet size */
+#define NCP_PACKET_SIZE_INTERNAL 65536
+
+int ncp_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct ncp_server *server = NCP_SERVER(inode);
+ int result;
+ struct ncp_ioctl_request request;
+ char* bouncebuffer;
+
+ switch (cmd) {
+ case NCP_IOC_NCPREQUEST:
+
+ if ((permission(inode, MAY_WRITE) != 0)
+ && (current->uid != server->m.mounted_uid)) {
+ return -EACCES;
+ }
+ if (copy_from_user(&request, (struct ncp_ioctl_request *) arg,
+ sizeof(request)))
+ return -EFAULT;
+
+ if ((request.function > 255)
+ || (request.size >
+ NCP_PACKET_SIZE - sizeof(struct ncp_request_header))) {
+ return -EINVAL;
+ }
+ bouncebuffer = vmalloc(NCP_PACKET_SIZE_INTERNAL);
+ if (!bouncebuffer)
+ return -ENOMEM;
+ if (copy_from_user(bouncebuffer, request.data, request.size)) {
+ vfree(bouncebuffer);
+ return -EFAULT;
+ }
+ ncp_lock_server(server);
+
+ /* FIXME: We hack around in the server's structures
+ here to be able to use ncp_request */
+
+ server->has_subfunction = 0;
+ server->current_size = request.size;
+ memcpy(server->packet, bouncebuffer, request.size);
+
+ result = ncp_request2(server, request.function,
+ bouncebuffer, NCP_PACKET_SIZE_INTERNAL);
+ if (result < 0)
+ result = -EIO;
+ else
+ result = server->reply_size;
+ ncp_unlock_server(server);
+ DPRINTK("ncp_ioctl: copy %d bytes\n",
+ result);
+ if (result >= 0)
+ if (copy_to_user(request.data, bouncebuffer, result))
+ result = -EFAULT;
+ vfree(bouncebuffer);
+ return result;
+
+ case NCP_IOC_CONN_LOGGED_IN:
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+ if (!(server->m.int_flags & NCP_IMOUNT_LOGGEDIN_POSSIBLE))
+ return -EINVAL;
+ if (server->root_setuped)
+ return -EBUSY;
+ server->root_setuped = 1;
+ return ncp_conn_logged_in(inode->i_sb);
+
+ case NCP_IOC_GET_FS_INFO:
+ {
+ struct ncp_fs_info info;
+
+ if ((permission(inode, MAY_WRITE) != 0)
+ && (current->uid != server->m.mounted_uid)) {
+ return -EACCES;
+ }
+ if (copy_from_user(&info, (struct ncp_fs_info *) arg,
+ sizeof(info)))
+ return -EFAULT;
+
+ if (info.version != NCP_GET_FS_INFO_VERSION) {
+ DPRINTK("info.version invalid: %d\n", info.version);
+ return -EINVAL;
+ }
+ /* TODO: info.addr = server->m.serv_addr; */
+ info.mounted_uid = NEW_TO_OLD_UID(server->m.mounted_uid);
+ info.connection = server->connection;
+ info.buffer_size = server->buffer_size;
+ info.volume_number = NCP_FINFO(inode)->volNumber;
+ info.directory_id = NCP_FINFO(inode)->DosDirNum;
+
+ if (copy_to_user((struct ncp_fs_info *) arg, &info,
+ sizeof(info))) return -EFAULT;
+ return 0;
+ }
+
+ case NCP_IOC_GET_FS_INFO_V2:
+ {
+ struct ncp_fs_info_v2 info2;
+
+ if ((permission(inode, MAY_WRITE) != 0)
+ && (current->uid != server->m.mounted_uid)) {
+ return -EACCES;
+ }
+ if (copy_from_user(&info2, (struct ncp_fs_info_v2 *) arg,
+ sizeof(info2)))
+ return -EFAULT;
+
+ if (info2.version != NCP_GET_FS_INFO_VERSION_V2) {
+ DPRINTK("info.version invalid: %d\n", info2.version);
+ return -EINVAL;
+ }
+ info2.mounted_uid = server->m.mounted_uid;
+ info2.connection = server->connection;
+ info2.buffer_size = server->buffer_size;
+ info2.volume_number = NCP_FINFO(inode)->volNumber;
+ info2.directory_id = NCP_FINFO(inode)->DosDirNum;
+ info2.dummy1 = info2.dummy2 = info2.dummy3 = 0;
+
+ if (copy_to_user((struct ncp_fs_info_v2 *) arg, &info2,
+ sizeof(info2))) return -EFAULT;
+ return 0;
+ }
+
+ case NCP_IOC_GETMOUNTUID2:
+ {
+ unsigned long tmp = server->m.mounted_uid;
+
+ if ( (permission(inode, MAY_READ) != 0)
+ && (current->uid != server->m.mounted_uid))
+ {
+ return -EACCES;
+ }
+ if (put_user(tmp, (unsigned long*) arg))
+ return -EFAULT;
+ return 0;
+ }
+
+ case NCP_IOC_GETROOT:
+ {
+ struct ncp_setroot_ioctl sr;
+
+ if ( (permission(inode, MAY_READ) != 0)
+ && (current->uid != server->m.mounted_uid))
+ {
+ return -EACCES;
+ }
+ if (server->m.mounted_vol[0]) {
+ struct dentry* dentry = inode->i_sb->s_root;
+
+ if (dentry) {
+ struct inode* inode = dentry->d_inode;
+
+ if (inode) {
+ sr.volNumber = NCP_FINFO(inode)->volNumber;
+ sr.dirEntNum = NCP_FINFO(inode)->dirEntNum;
+ sr.namespace = server->name_space[sr.volNumber];
+ } else
+ DPRINTK("ncpfs: s_root->d_inode==NULL\n");
+ } else
+ DPRINTK("ncpfs: s_root==NULL\n");
+ } else {
+ sr.volNumber = -1;
+ sr.namespace = 0;
+ sr.dirEntNum = 0;
+ }
+ if (copy_to_user((struct ncp_setroot_ioctl*)arg,
+ &sr,
+ sizeof(sr))) return -EFAULT;
+ return 0;
+ }
+ case NCP_IOC_SETROOT:
+ {
+ struct ncp_setroot_ioctl sr;
+ struct nw_info_struct i;
+ struct dentry* dentry;
+
+ if (!capable(CAP_SYS_ADMIN))
+ {
+ return -EACCES;
+ }
+ if (server->root_setuped) return -EBUSY;
+ if (copy_from_user(&sr,
+ (struct ncp_setroot_ioctl*)arg,
+ sizeof(sr))) return -EFAULT;
+ if (sr.volNumber < 0) {
+ server->m.mounted_vol[0] = 0;
+ i.volNumber = NCP_NUMBER_OF_VOLUMES + 1;
+ i.dirEntNum = 0;
+ i.DosDirNum = 0;
+ } else if (sr.volNumber >= NCP_NUMBER_OF_VOLUMES) {
+ return -EINVAL;
+ } else
+ if (ncp_mount_subdir(server, &i, sr.volNumber,
+ sr.namespace, sr.dirEntNum))
+ return -ENOENT;
+
+ dentry = inode->i_sb->s_root;
+ server->root_setuped = 1;
+ if (dentry) {
+ struct inode* inode = dentry->d_inode;
+
+ if (inode) {
+ NCP_FINFO(inode)->volNumber = i.volNumber;
+ NCP_FINFO(inode)->dirEntNum = i.dirEntNum;
+ NCP_FINFO(inode)->DosDirNum = i.DosDirNum;
+ } else
+ DPRINTK("ncpfs: s_root->d_inode==NULL\n");
+ } else
+ DPRINTK("ncpfs: s_root==NULL\n");
+
+ return 0;
+ }
+
+#ifdef CONFIG_NCPFS_PACKET_SIGNING
+ case NCP_IOC_SIGN_INIT:
+ if ((permission(inode, MAY_WRITE) != 0)
+ && (current->uid != server->m.mounted_uid))
+ {
+ return -EACCES;
+ }
+ if (arg) {
+ if (server->sign_wanted)
+ {
+ struct ncp_sign_init sign;
+
+ if (copy_from_user(&sign, (struct ncp_sign_init *) arg,
+ sizeof(sign))) return -EFAULT;
+ memcpy(server->sign_root,sign.sign_root,8);
+ memcpy(server->sign_last,sign.sign_last,16);
+ server->sign_active = 1;
+ }
+ /* ignore when signatures not wanted */
+ } else {
+ server->sign_active = 0;
+ }
+ return 0;
+
+ case NCP_IOC_SIGN_WANTED:
+ if ( (permission(inode, MAY_READ) != 0)
+ && (current->uid != server->m.mounted_uid))
+ {
+ return -EACCES;
+ }
+
+ if (put_user(server->sign_wanted, (int*) arg))
+ return -EFAULT;
+ return 0;
+ case NCP_IOC_SET_SIGN_WANTED:
+ {
+ int newstate;
+
+ if ( (permission(inode, MAY_WRITE) != 0)
+ && (current->uid != server->m.mounted_uid))
+ {
+ return -EACCES;
+ }
+ /* get only low 8 bits... */
+ if (get_user(newstate, (unsigned char *) arg))
+ return -EFAULT;
+ if (server->sign_active) {
+ /* cannot turn signatures OFF when active */
+ if (!newstate) return -EINVAL;
+ } else {
+ server->sign_wanted = newstate != 0;
+ }
+ return 0;
+ }
+
+#endif /* CONFIG_NCPFS_PACKET_SIGNING */
+
+#ifdef CONFIG_NCPFS_IOCTL_LOCKING
+ case NCP_IOC_LOCKUNLOCK:
+ if ( (permission(inode, MAY_WRITE) != 0)
+ && (current->uid != server->m.mounted_uid))
+ {
+ return -EACCES;
+ }
+ {
+ struct ncp_lock_ioctl rqdata;
+ int result;
+
+ if (copy_from_user(&rqdata, (struct ncp_lock_ioctl*)arg,
+ sizeof(rqdata))) return -EFAULT;
+ if (rqdata.origin != 0)
+ return -EINVAL;
+ /* check for cmd */
+ switch (rqdata.cmd) {
+ case NCP_LOCK_EX:
+ case NCP_LOCK_SH:
+ if (rqdata.timeout == 0)
+ rqdata.timeout = NCP_LOCK_DEFAULT_TIMEOUT;
+ else if (rqdata.timeout > NCP_LOCK_MAX_TIMEOUT)
+ rqdata.timeout = NCP_LOCK_MAX_TIMEOUT;
+ break;
+ case NCP_LOCK_LOG:
+ rqdata.timeout = NCP_LOCK_DEFAULT_TIMEOUT; /* has no effect */
+ case NCP_LOCK_CLEAR:
+ break;
+ default:
+ return -EINVAL;
+ }
+ /* locking needs both read and write access */
+ if ((result = ncp_make_open(inode, O_RDWR)) != 0)
+ {
+ return result;
+ }
+ result = -EIO;
+ if (!ncp_conn_valid(server))
+ goto outrel;
+ result = -EISDIR;
+ if (!S_ISREG(inode->i_mode))
+ goto outrel;
+ if (rqdata.cmd == NCP_LOCK_CLEAR)
+ {
+ result = ncp_ClearPhysicalRecord(NCP_SERVER(inode),
+ NCP_FINFO(inode)->file_handle,
+ rqdata.offset,
+ rqdata.length);
+ if (result > 0) result = 0; /* no such lock */
+ }
+ else
+ {
+ int lockcmd;
+
+ switch (rqdata.cmd)
+ {
+ case NCP_LOCK_EX: lockcmd=1; break;
+ case NCP_LOCK_SH: lockcmd=3; break;
+ default: lockcmd=0; break;
+ }
+ result = ncp_LogPhysicalRecord(NCP_SERVER(inode),
+ NCP_FINFO(inode)->file_handle,
+ lockcmd,
+ rqdata.offset,
+ rqdata.length,
+ rqdata.timeout);
+ if (result > 0) result = -EAGAIN;
+ }
+outrel:
+ ncp_inode_close(inode);
+ return result;
+ }
+#endif /* CONFIG_NCPFS_IOCTL_LOCKING */
+
+ case NCP_IOC_GETOBJECTNAME:
+ if (current->uid != server->m.mounted_uid) {
+ return -EACCES;
+ }
+ {
+ struct ncp_objectname_ioctl user;
+ size_t outl;
+
+ if (copy_from_user(&user,
+ (struct ncp_objectname_ioctl*)arg,
+ sizeof(user))) return -EFAULT;
+ user.auth_type = server->auth.auth_type;
+ outl = user.object_name_len;
+ user.object_name_len = server->auth.object_name_len;
+ if (outl > user.object_name_len)
+ outl = user.object_name_len;
+ if (outl) {
+ if (copy_to_user(user.object_name,
+ server->auth.object_name,
+ outl)) return -EFAULT;
+ }
+ if (copy_to_user((struct ncp_objectname_ioctl*)arg,
+ &user,
+ sizeof(user))) return -EFAULT;
+ return 0;
+ }
+ case NCP_IOC_SETOBJECTNAME:
+ if (current->uid != server->m.mounted_uid) {
+ return -EACCES;
+ }
+ {
+ struct ncp_objectname_ioctl user;
+ void* newname;
+ void* oldname;
+ size_t oldnamelen;
+ void* oldprivate;
+ size_t oldprivatelen;
+
+ if (copy_from_user(&user,
+ (struct ncp_objectname_ioctl*)arg,
+ sizeof(user))) return -EFAULT;
+ if (user.object_name_len > NCP_OBJECT_NAME_MAX_LEN)
+ return -ENOMEM;
+ if (user.object_name_len) {
+ newname = ncp_kmalloc(user.object_name_len, GFP_USER);
+ if (!newname) return -ENOMEM;
+ if (copy_from_user(newname, user.object_name, user.object_name_len)) {
+ ncp_kfree_s(newname, user.object_name_len);
+ return -EFAULT;
+ }
+ } else {
+ newname = NULL;
+ }
+ /* enter critical section */
+ /* maybe that kfree can sleep so do that this way */
+ /* it is at least more SMP friendly (in future...) */
+ oldname = server->auth.object_name;
+ oldnamelen = server->auth.object_name_len;
+ oldprivate = server->priv.data;
+ oldprivatelen = server->priv.len;
+ server->auth.auth_type = user.auth_type;
+ server->auth.object_name_len = user.object_name_len;
+ server->auth.object_name = newname;
+ server->priv.len = 0;
+ server->priv.data = NULL;
+ /* leave critical section */
+ if (oldprivate) ncp_kfree_s(oldprivate, oldprivatelen);
+ if (oldname) ncp_kfree_s(oldname, oldnamelen);
+ return 0;
+ }
+ case NCP_IOC_GETPRIVATEDATA:
+ if (current->uid != server->m.mounted_uid) {
+ return -EACCES;
+ }
+ {
+ struct ncp_privatedata_ioctl user;
+ size_t outl;
+
+ if (copy_from_user(&user,
+ (struct ncp_privatedata_ioctl*)arg,
+ sizeof(user))) return -EFAULT;
+ outl = user.len;
+ user.len = server->priv.len;
+ if (outl > user.len) outl = user.len;
+ if (outl) {
+ if (copy_to_user(user.data,
+ server->priv.data,
+ outl)) return -EFAULT;
+ }
+ if (copy_to_user((struct ncp_privatedata_ioctl*)arg,
+ &user,
+ sizeof(user))) return -EFAULT;
+ return 0;
+ }
+ case NCP_IOC_SETPRIVATEDATA:
+ if (current->uid != server->m.mounted_uid) {
+ return -EACCES;
+ }
+ {
+ struct ncp_privatedata_ioctl user;
+ void* new;
+ void* old;
+ size_t oldlen;
+
+ if (copy_from_user(&user,
+ (struct ncp_privatedata_ioctl*)arg,
+ sizeof(user))) return -EFAULT;
+ if (user.len > NCP_PRIVATE_DATA_MAX_LEN)
+ return -ENOMEM;
+ if (user.len) {
+ new = ncp_kmalloc(user.len, GFP_USER);
+ if (!new) return -ENOMEM;
+ if (copy_from_user(new, user.data, user.len)) {
+ ncp_kfree_s(new, user.len);
+ return -EFAULT;
+ }
+ } else {
+ new = NULL;
+ }
+ /* enter critical section */
+ old = server->priv.data;
+ oldlen = server->priv.len;
+ server->priv.len = user.len;
+ server->priv.data = new;
+ /* leave critical section */
+ if (old) ncp_kfree_s(old, oldlen);
+ return 0;
+ }
+
+#ifdef CONFIG_NCPFS_NLS
+/* Here we are select the iocharset and the codepage for NLS.
+ * Thanks Petr Vandrovec for idea and many hints.
+ */
+ case NCP_IOC_SETCHARSETS:
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+ if (server->root_setuped)
+ return -EBUSY;
+
+ {
+ struct ncp_nls_ioctl user;
+ struct nls_table *codepage;
+ struct nls_table *iocharset;
+ struct nls_table *oldset_io;
+ struct nls_table *oldset_cp;
+
+ if (copy_from_user(&user, (struct ncp_nls_ioctl*)arg,
+ sizeof(user)))
+ return -EFAULT;
+
+ codepage = NULL;
+ user.codepage[NCP_IOCSNAME_LEN] = 0;
+ if (!user.codepage[0] ||
+ !strcmp(user.codepage, "default"))
+ codepage = load_nls_default();
+ else {
+ codepage = load_nls(user.codepage);
+ if (!codepage) {
+ return -EBADRQC;
+ }
+ }
+
+ iocharset = NULL;
+ user.iocharset[NCP_IOCSNAME_LEN] = 0;
+ if (!user.iocharset[0] ||
+ !strcmp(user.iocharset, "default")) {
+ iocharset = load_nls_default();
+ NCP_CLR_FLAG(server, NCP_FLAG_UTF8);
+ } else {
+ if (!strcmp(user.iocharset, "utf8")) {
+ iocharset = load_nls_default();
+ NCP_SET_FLAG(server, NCP_FLAG_UTF8);
+ } else {
+ iocharset = load_nls(user.iocharset);
+ if (!iocharset) {
+ unload_nls(codepage);
+ return -EBADRQC;
+ }
+ NCP_CLR_FLAG(server, NCP_FLAG_UTF8);
+ }
+ }
+
+ oldset_cp = server->nls_vol;
+ server->nls_vol = codepage;
+ oldset_io = server->nls_io;
+ server->nls_io = iocharset;
+
+ if (oldset_cp)
+ unload_nls(oldset_cp);
+ if (oldset_io)
+ unload_nls(oldset_io);
+
+ return 0;
+ }
+
+ case NCP_IOC_GETCHARSETS: /* not tested */
+ {
+ struct ncp_nls_ioctl user;
+ int len;
+
+ memset(&user, 0, sizeof(user));
+ if (server->nls_vol && server->nls_vol->charset) {
+ len = strlen(server->nls_vol->charset);
+ if (len > NCP_IOCSNAME_LEN)
+ len = NCP_IOCSNAME_LEN;
+ strncpy(user.codepage,
+ server->nls_vol->charset, len);
+ user.codepage[len] = 0;
+ }
+
+ if (NCP_IS_FLAG(server, NCP_FLAG_UTF8))
+ strcpy(user.iocharset, "utf8");
+ else
+ if (server->nls_io && server->nls_io->charset) {
+ len = strlen(server->nls_io->charset);
+ if (len > NCP_IOCSNAME_LEN)
+ len = NCP_IOCSNAME_LEN;
+ strncpy(user.iocharset,
+ server->nls_io->charset, len);
+ user.iocharset[len] = 0;
+ }
+
+ if (copy_to_user((struct ncp_nls_ioctl*)arg, &user,
+ sizeof(user)))
+ return -EFAULT;
+
+ return 0;
+ }
+#endif /* CONFIG_NCPFS_NLS */
+ case NCP_IOC_SETDENTRYTTL:
+ if ((permission(inode, MAY_WRITE) != 0) &&
+ (current->uid != server->m.mounted_uid))
+ return -EACCES;
+ {
+ u_int32_t user;
+
+ if (copy_from_user(&user, (u_int32_t*)arg, sizeof(user)))
+ return -EFAULT;
+ /* 20 secs at most... */
+ if (user > 20000)
+ return -EINVAL;
+ user = (user * HZ) / 1000;
+ server->dentry_ttl = user;
+ return 0;
+ }
+
+ case NCP_IOC_GETDENTRYTTL:
+ {
+ u_int32_t user = (server->dentry_ttl * 1000) / HZ;
+ if (copy_to_user((u_int32_t*)arg, &user, sizeof(user)))
+ return -EFAULT;
+ return 0;
+ }
+
+ }
+/* #ifdef CONFIG_UID16 */
+ /* NCP_IOC_GETMOUNTUID may be same as NCP_IOC_GETMOUNTUID2,
+ so we have this out of switch */
+ if (cmd == NCP_IOC_GETMOUNTUID) {
+ if ((permission(inode, MAY_READ) != 0)
+ && (current->uid != server->m.mounted_uid)) {
+ return -EACCES;
+ }
+ if (put_user(NEW_TO_OLD_UID(server->m.mounted_uid), (__kernel_uid_t *) arg))
+ return -EFAULT;
+ return 0;
+ }
+/* #endif */
+ return -EINVAL;
+}
diff --git a/fs/ncpfs/mmap.c b/fs/ncpfs/mmap.c
new file mode 100644
index 00000000000000..7d9bc345d4f0a5
--- /dev/null
+++ b/fs/ncpfs/mmap.c
@@ -0,0 +1,123 @@
+/*
+ * mmap.c
+ *
+ * Copyright (C) 1995, 1996 by Volker Lendecke
+ * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
+ *
+ */
+
+#include <linux/stat.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/shm.h>
+#include <linux/errno.h>
+#include <linux/mman.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/fcntl.h>
+#include <linux/ncp_fs.h>
+
+#include "ncplib_kernel.h"
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+/*
+ * Fill in the supplied page for mmap
+ */
+static struct page* ncp_file_mmap_nopage(struct vm_area_struct *area,
+ unsigned long address, int write_access)
+{
+ struct file *file = area->vm_file;
+ struct dentry *dentry = file->f_dentry;
+ struct inode *inode = dentry->d_inode;
+ struct page* page;
+ char *pg_addr;
+ unsigned int already_read;
+ unsigned int count;
+ int bufsize;
+ int pos;
+
+ page = alloc_page(GFP_HIGHUSER); /* ncpfs has nothing against high pages
+ as long as recvmsg and memset works on it */
+ if (!page)
+ return page;
+ pg_addr = kmap(page);
+ address &= PAGE_MASK;
+ pos = address - area->vm_start + (area->vm_pgoff << PAGE_SHIFT);
+
+ count = PAGE_SIZE;
+ if (address + PAGE_SIZE > area->vm_end) {
+ count = area->vm_end - address;
+ }
+ /* what we can read in one go */
+ bufsize = NCP_SERVER(inode)->buffer_size;
+
+ already_read = 0;
+ if (ncp_make_open(inode, O_RDONLY) >= 0) {
+ while (already_read < count) {
+ int read_this_time;
+ int to_read;
+
+ to_read = bufsize - (pos % bufsize);
+
+ to_read = min_t(unsigned int, to_read, count - already_read);
+
+ if (ncp_read_kernel(NCP_SERVER(inode),
+ NCP_FINFO(inode)->file_handle,
+ pos, to_read,
+ pg_addr + already_read,
+ &read_this_time) != 0) {
+ read_this_time = 0;
+ }
+ pos += read_this_time;
+ already_read += read_this_time;
+
+ if (read_this_time < to_read) {
+ break;
+ }
+ }
+ ncp_inode_close(inode);
+
+ }
+
+ if (already_read < PAGE_SIZE)
+ memset(pg_addr + already_read, 0, PAGE_SIZE - already_read);
+ flush_dcache_page(page);
+ kunmap(page);
+ return page;
+}
+
+static struct vm_operations_struct ncp_file_mmap =
+{
+ nopage: ncp_file_mmap_nopage,
+};
+
+
+/* This is used for a general mmap of a ncp file */
+int ncp_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct inode *inode = file->f_dentry->d_inode;
+
+ DPRINTK("ncp_mmap: called\n");
+
+ if (!ncp_conn_valid(NCP_SERVER(inode))) {
+ return -EIO;
+ }
+ /* only PAGE_COW or read-only supported now */
+ if (vma->vm_flags & VM_SHARED)
+ return -EINVAL;
+ if (!inode->i_sb || !S_ISREG(inode->i_mode))
+ return -EACCES;
+ /* we do not support files bigger than 4GB... We eventually
+ supports just 4GB... */
+ if (((vma->vm_end - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff
+ > (1U << (32 - PAGE_SHIFT)))
+ return -EFBIG;
+ if (!IS_RDONLY(inode)) {
+ inode->i_atime = CURRENT_TIME;
+ }
+
+ vma->vm_ops = &ncp_file_mmap;
+ return 0;
+}
diff --git a/fs/ncpfs/ncplib_kernel.c b/fs/ncpfs/ncplib_kernel.c
new file mode 100644
index 00000000000000..08110ebaf8cc85
--- /dev/null
+++ b/fs/ncpfs/ncplib_kernel.c
@@ -0,0 +1,1129 @@
+/*
+ * ncplib_kernel.c
+ *
+ * Copyright (C) 1995, 1996 by Volker Lendecke
+ * Modified for big endian by J.F. Chadima and David S. Miller
+ * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
+ * Modified 1999 Wolfram Pienkoss for NLS
+ *
+ */
+
+
+#include <linux/config.h>
+
+#include "ncplib_kernel.h"
+
+static inline void assert_server_locked(struct ncp_server *server)
+{
+ if (server->lock == 0) {
+ DPRINTK("ncpfs: server not locked!\n");
+ }
+}
+
+static void ncp_add_byte(struct ncp_server *server, __u8 x)
+{
+ assert_server_locked(server);
+ *(__u8 *) (&(server->packet[server->current_size])) = x;
+ server->current_size += 1;
+ return;
+}
+
+static void ncp_add_word(struct ncp_server *server, __u16 x)
+{
+ assert_server_locked(server);
+ put_unaligned(x, (__u16 *) (&(server->packet[server->current_size])));
+ server->current_size += 2;
+ return;
+}
+
+static void ncp_add_dword(struct ncp_server *server, __u32 x)
+{
+ assert_server_locked(server);
+ put_unaligned(x, (__u32 *) (&(server->packet[server->current_size])));
+ server->current_size += 4;
+ return;
+}
+
+static void ncp_add_mem(struct ncp_server *server, const void *source, int size)
+{
+ assert_server_locked(server);
+ memcpy(&(server->packet[server->current_size]), source, size);
+ server->current_size += size;
+ return;
+}
+
+static void ncp_add_pstring(struct ncp_server *server, const char *s)
+{
+ int len = strlen(s);
+ assert_server_locked(server);
+ if (len > 255) {
+ DPRINTK("ncpfs: string too long: %s\n", s);
+ len = 255;
+ }
+ ncp_add_byte(server, len);
+ ncp_add_mem(server, s, len);
+ return;
+}
+
+static inline void ncp_init_request(struct ncp_server *server)
+{
+ ncp_lock_server(server);
+
+ server->current_size = sizeof(struct ncp_request_header);
+ server->has_subfunction = 0;
+}
+
+static inline void ncp_init_request_s(struct ncp_server *server, int subfunction)
+{
+ ncp_lock_server(server);
+
+ server->current_size = sizeof(struct ncp_request_header) + 2;
+ ncp_add_byte(server, subfunction);
+
+ server->has_subfunction = 1;
+}
+
+static inline char *
+ ncp_reply_data(struct ncp_server *server, int offset)
+{
+ return &(server->packet[sizeof(struct ncp_reply_header) + offset]);
+}
+
+static __u8
+ ncp_reply_byte(struct ncp_server *server, int offset)
+{
+ return get_unaligned((__u8 *) ncp_reply_data(server, offset));
+}
+
+static __u16
+ ncp_reply_word(struct ncp_server *server, int offset)
+{
+ return get_unaligned((__u16 *) ncp_reply_data(server, offset));
+}
+
+static __u32
+ ncp_reply_dword(struct ncp_server *server, int offset)
+{
+ return get_unaligned((__u32 *) ncp_reply_data(server, offset));
+}
+
+int
+ncp_negotiate_buffersize(struct ncp_server *server, int size, int *target)
+{
+ int result;
+
+ ncp_init_request(server);
+ ncp_add_word(server, htons(size));
+
+ if ((result = ncp_request(server, 33)) != 0) {
+ ncp_unlock_server(server);
+ return result;
+ }
+ *target = min_t(unsigned int, ntohs(ncp_reply_word(server, 0)), size);
+
+ ncp_unlock_server(server);
+ return 0;
+}
+
+
+/* options:
+ * bit 0 ipx checksum
+ * bit 1 packet signing
+ */
+int
+ncp_negotiate_size_and_options(struct ncp_server *server,
+ int size, int options, int *ret_size, int *ret_options) {
+ int result;
+
+ /* there is minimum */
+ if (size < NCP_BLOCK_SIZE) size = NCP_BLOCK_SIZE;
+
+ ncp_init_request(server);
+ ncp_add_word(server, htons(size));
+ ncp_add_byte(server, options);
+
+ if ((result = ncp_request(server, 0x61)) != 0)
+ {
+ ncp_unlock_server(server);
+ return result;
+ }
+
+ /* NCP over UDP returns 0 (!!!) */
+ result = ntohs(ncp_reply_word(server, 0));
+ if (result >= NCP_BLOCK_SIZE)
+ size = min(result, size);
+ *ret_size = size;
+ *ret_options = ncp_reply_byte(server, 4);
+
+ ncp_unlock_server(server);
+ return 0;
+}
+
+int
+ncp_get_volume_info_with_number(struct ncp_server *server, int n,
+ struct ncp_volume_info *target)
+{
+ int result;
+ int len;
+
+ ncp_init_request_s(server, 44);
+ ncp_add_byte(server, n);
+
+ if ((result = ncp_request(server, 22)) != 0) {
+ goto out;
+ }
+ target->total_blocks = ncp_reply_dword(server, 0);
+ target->free_blocks = ncp_reply_dword(server, 4);
+ target->purgeable_blocks = ncp_reply_dword(server, 8);
+ target->not_yet_purgeable_blocks = ncp_reply_dword(server, 12);
+ target->total_dir_entries = ncp_reply_dword(server, 16);
+ target->available_dir_entries = ncp_reply_dword(server, 20);
+ target->sectors_per_block = ncp_reply_byte(server, 28);
+
+ memset(&(target->volume_name), 0, sizeof(target->volume_name));
+
+ result = -EIO;
+ len = ncp_reply_byte(server, 29);
+ if (len > NCP_VOLNAME_LEN) {
+ DPRINTK("ncpfs: volume name too long: %d\n", len);
+ goto out;
+ }
+ memcpy(&(target->volume_name), ncp_reply_data(server, 30), len);
+ result = 0;
+out:
+ ncp_unlock_server(server);
+ return result;
+}
+
+int
+ncp_close_file(struct ncp_server *server, const char *file_id)
+{
+ int result;
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 0);
+ ncp_add_mem(server, file_id, 6);
+
+ result = ncp_request(server, 66);
+ ncp_unlock_server(server);
+ return result;
+}
+
+int
+ncp_make_closed(struct inode *inode)
+{
+ int err;
+
+ err = 0;
+ down(&NCP_FINFO(inode)->open_sem);
+ if (atomic_read(&NCP_FINFO(inode)->opened) == 1) {
+ atomic_set(&NCP_FINFO(inode)->opened, 0);
+ err = ncp_close_file(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle);
+
+ if (!err)
+ PPRINTK("ncp_make_closed: volnum=%d, dirent=%u, error=%d\n",
+ NCP_FINFO(inode)->volNumber,
+ NCP_FINFO(inode)->dirEntNum, err);
+ }
+ up(&NCP_FINFO(inode)->open_sem);
+ return err;
+}
+
+static void ncp_add_handle_path(struct ncp_server *server, __u8 vol_num,
+ __u32 dir_base, int have_dir_base,
+ const char *path)
+{
+ ncp_add_byte(server, vol_num);
+ ncp_add_dword(server, dir_base);
+ if (have_dir_base != 0) {
+ ncp_add_byte(server, 1); /* dir_base */
+ } else {
+ ncp_add_byte(server, 0xff); /* no handle */
+ }
+ if (path != NULL) {
+ ncp_add_byte(server, 1); /* 1 component */
+ ncp_add_pstring(server, path);
+ } else {
+ ncp_add_byte(server, 0);
+ }
+}
+
+static void ncp_extract_file_info(void *structure, struct nw_info_struct *target)
+{
+ __u8 *name_len;
+ const int info_struct_size = sizeof(struct nw_info_struct) - 257;
+
+ memcpy(target, structure, info_struct_size);
+ name_len = structure + info_struct_size;
+ target->nameLen = *name_len;
+ memcpy(target->entryName, name_len + 1, *name_len);
+ target->entryName[*name_len] = '\0';
+ target->volNumber = le32_to_cpu(target->volNumber);
+ return;
+}
+
+/*
+ * Returns information for a (one-component) name relative to
+ * the specified directory.
+ */
+int ncp_obtain_info(struct ncp_server *server, struct inode *dir, char *path,
+ struct nw_info_struct *target)
+{
+ __u8 volnum = NCP_FINFO(dir)->volNumber;
+ __u32 dirent = NCP_FINFO(dir)->dirEntNum;
+ int result;
+
+ if (target == NULL) {
+ printk(KERN_ERR "ncp_obtain_info: invalid call\n");
+ return -EINVAL;
+ }
+ ncp_init_request(server);
+ ncp_add_byte(server, 6); /* subfunction */
+ ncp_add_byte(server, server->name_space[volnum]);
+ ncp_add_byte(server, server->name_space[volnum]); /* N.B. twice ?? */
+ ncp_add_word(server, htons(0x0680)); /* get all */
+ ncp_add_dword(server, RIM_ALL);
+ ncp_add_handle_path(server, volnum, dirent, 1, path);
+
+ if ((result = ncp_request(server, 87)) != 0)
+ goto out;
+ ncp_extract_file_info(ncp_reply_data(server, 0), target);
+
+out:
+ ncp_unlock_server(server);
+ return result;
+}
+
+#ifdef CONFIG_NCPFS_NFS_NS
+static int
+ncp_obtain_DOS_dir_base(struct ncp_server *server,
+ __u8 volnum, __u32 dirent,
+ char *path, /* At most 1 component */
+ __u32 *DOS_dir_base)
+{
+ int result;
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 6); /* subfunction */
+ ncp_add_byte(server, server->name_space[volnum]);
+ ncp_add_byte(server, server->name_space[volnum]);
+ ncp_add_word(server, htons(0x0680)); /* get all */
+ ncp_add_dword(server, RIM_DIRECTORY);
+ ncp_add_handle_path(server, volnum, dirent, 1, path);
+
+ if ((result = ncp_request(server, 87)) == 0)
+ {
+ if (DOS_dir_base) *DOS_dir_base=ncp_reply_dword(server, 0x34);
+ }
+ ncp_unlock_server(server);
+ return result;
+}
+#endif /* CONFIG_NCPFS_NFS_NS */
+
+static inline int
+ncp_get_known_namespace(struct ncp_server *server, __u8 volume)
+{
+#if defined(CONFIG_NCPFS_OS2_NS) || defined(CONFIG_NCPFS_NFS_NS)
+ int result;
+ __u8 *namespace;
+ __u16 no_namespaces;
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 24); /* Subfunction: Get Name Spaces Loaded */
+ ncp_add_word(server, 0);
+ ncp_add_byte(server, volume);
+
+ if ((result = ncp_request(server, 87)) != 0) {
+ ncp_unlock_server(server);
+ return NW_NS_DOS; /* not result ?? */
+ }
+
+ result = NW_NS_DOS;
+ no_namespaces = le16_to_cpu(ncp_reply_word(server, 0));
+ namespace = ncp_reply_data(server, 2);
+
+ while (no_namespaces > 0) {
+ DPRINTK("get_namespaces: found %d on %d\n", *namespace, volume);
+
+#ifdef CONFIG_NCPFS_NFS_NS
+ if ((*namespace == NW_NS_NFS) && !(server->m.flags&NCP_MOUNT_NO_NFS))
+ {
+ result = NW_NS_NFS;
+ break;
+ }
+#endif /* CONFIG_NCPFS_NFS_NS */
+#ifdef CONFIG_NCPFS_OS2_NS
+ if ((*namespace == NW_NS_OS2) && !(server->m.flags&NCP_MOUNT_NO_OS2))
+ {
+ result = NW_NS_OS2;
+ }
+#endif /* CONFIG_NCPFS_OS2_NS */
+ namespace += 1;
+ no_namespaces -= 1;
+ }
+ ncp_unlock_server(server);
+ return result;
+#else /* neither OS2 nor NFS - only DOS */
+ return NW_NS_DOS;
+#endif /* defined(CONFIG_NCPFS_OS2_NS) || defined(CONFIG_NCPFS_NFS_NS) */
+}
+
+static int
+ncp_ObtainSpecificDirBase(struct ncp_server *server,
+ __u8 nsSrc, __u8 nsDst, __u8 vol_num, __u32 dir_base,
+ char *path, /* At most 1 component */
+ __u32 *dirEntNum, __u32 *DosDirNum)
+{
+ int result;
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 6); /* subfunction */
+ ncp_add_byte(server, nsSrc);
+ ncp_add_byte(server, nsDst);
+ ncp_add_word(server, htons(0x0680)); /* get all */
+ ncp_add_dword(server, RIM_ALL);
+ ncp_add_handle_path(server, vol_num, dir_base, 1, path);
+
+ if ((result = ncp_request(server, 87)) != 0)
+ {
+ ncp_unlock_server(server);
+ return result;
+ }
+
+ if (dirEntNum)
+ *dirEntNum = ncp_reply_dword(server, 0x30);
+ if (DosDirNum)
+ *DosDirNum = ncp_reply_dword(server, 0x34);
+ ncp_unlock_server(server);
+ return 0;
+}
+
+int
+ncp_mount_subdir(struct ncp_server *server, struct nw_info_struct *i,
+ __u8 volNumber, __u8 srcNS, __u32 dirEntNum)
+{
+ int dstNS;
+ int result;
+ __u32 newDirEnt;
+ __u32 newDosEnt;
+
+ dstNS = ncp_get_known_namespace(server, volNumber);
+ if ((result = ncp_ObtainSpecificDirBase(server, srcNS, dstNS, volNumber,
+ dirEntNum, NULL, &newDirEnt, &newDosEnt)) != 0)
+ {
+ return result;
+ }
+ server->name_space[volNumber] = dstNS;
+ i->volNumber = volNumber;
+ i->dirEntNum = newDirEnt;
+ i->DosDirNum = newDosEnt;
+ server->m.mounted_vol[1] = 0;
+ server->m.mounted_vol[0] = 'X';
+ return 0;
+}
+
+int
+ncp_lookup_volume(struct ncp_server *server, char *volname,
+ struct nw_info_struct *target)
+{
+ int result;
+ int volnum;
+
+ DPRINTK("ncp_lookup_volume: looking up vol %s\n", volname);
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 22); /* Subfunction: Generate dir handle */
+ ncp_add_byte(server, 0); /* DOS namespace */
+ ncp_add_byte(server, 0); /* reserved */
+ ncp_add_byte(server, 0); /* reserved */
+ ncp_add_byte(server, 0); /* reserved */
+
+ ncp_add_byte(server, 0); /* faked volume number */
+ ncp_add_dword(server, 0); /* faked dir_base */
+ ncp_add_byte(server, 0xff); /* Don't have a dir_base */
+ ncp_add_byte(server, 1); /* 1 path component */
+ ncp_add_pstring(server, volname);
+
+ if ((result = ncp_request(server, 87)) != 0) {
+ ncp_unlock_server(server);
+ return result;
+ }
+ memset(target, 0, sizeof(*target));
+ target->DosDirNum = target->dirEntNum = ncp_reply_dword(server, 4);
+ target->volNumber = volnum = ncp_reply_byte(server, 8);
+ ncp_unlock_server(server);
+
+ server->name_space[volnum] = ncp_get_known_namespace(server, volnum);
+
+ DPRINTK("lookup_vol: namespace[%d] = %d\n",
+ volnum, server->name_space[volnum]);
+
+ target->nameLen = strlen(volname);
+ memcpy(target->entryName, volname, target->nameLen+1);
+ target->attributes = aDIR;
+ /* set dates to Jan 1, 1986 00:00 */
+ target->creationTime = target->modifyTime = cpu_to_le16(0x0000);
+ target->creationDate = target->modifyDate = target->lastAccessDate = cpu_to_le16(0x0C21);
+ return 0;
+}
+
+int ncp_modify_file_or_subdir_dos_info_path(struct ncp_server *server,
+ struct inode *dir,
+ const char *path,
+ __u32 info_mask,
+ const struct nw_modify_dos_info *info)
+{
+ __u8 volnum = NCP_FINFO(dir)->volNumber;
+ __u32 dirent = NCP_FINFO(dir)->dirEntNum;
+ int result;
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 7); /* subfunction */
+ ncp_add_byte(server, server->name_space[volnum]);
+ ncp_add_byte(server, 0); /* reserved */
+ ncp_add_word(server, htons(0x0680)); /* search attribs: all */
+
+ ncp_add_dword(server, info_mask);
+ ncp_add_mem(server, info, sizeof(*info));
+ ncp_add_handle_path(server, volnum, dirent, 1, path);
+
+ result = ncp_request(server, 87);
+ ncp_unlock_server(server);
+ return result;
+}
+
+int ncp_modify_file_or_subdir_dos_info(struct ncp_server *server,
+ struct inode *dir,
+ __u32 info_mask,
+ const struct nw_modify_dos_info *info)
+{
+ return ncp_modify_file_or_subdir_dos_info_path(server, dir, NULL,
+ info_mask, info);
+}
+
+static int
+ncp_DeleteNSEntry(struct ncp_server *server,
+ __u8 have_dir_base, __u8 volnum, __u32 dirent,
+ char* name, __u8 ns, int attr)
+{
+ int result;
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 8); /* subfunction */
+ ncp_add_byte(server, ns);
+ ncp_add_byte(server, 0); /* reserved */
+ ncp_add_word(server, attr); /* search attribs: all */
+ ncp_add_handle_path(server, volnum, dirent, have_dir_base, name);
+
+ result = ncp_request(server, 87);
+ ncp_unlock_server(server);
+ return result;
+}
+
+int
+ncp_del_file_or_subdir2(struct ncp_server *server,
+ struct dentry *dentry)
+{
+ struct inode *inode = dentry->d_inode;
+ __u8 volnum;
+ __u32 dirent;
+
+ if (!inode) {
+#if CONFIG_NCPFS_DEBUGDENTRY
+ PRINTK("ncpfs: ncpdel2: dentry->d_inode == NULL\n");
+#endif
+ return 0xFF; /* Any error */
+ }
+ volnum = NCP_FINFO(inode)->volNumber;
+ dirent = NCP_FINFO(inode)->DosDirNum;
+ return ncp_DeleteNSEntry(server, 1, volnum, dirent, NULL, NW_NS_DOS, htons(0x0680));
+}
+
+int
+ncp_del_file_or_subdir(struct ncp_server *server,
+ struct inode *dir, char *name)
+{
+ __u8 volnum = NCP_FINFO(dir)->volNumber;
+ __u32 dirent = NCP_FINFO(dir)->dirEntNum;
+
+#ifdef CONFIG_NCPFS_NFS_NS
+ if (server->name_space[volnum]==NW_NS_NFS)
+ {
+ int result;
+
+ result=ncp_obtain_DOS_dir_base(server, volnum, dirent, name, &dirent);
+ if (result) return result;
+ return ncp_DeleteNSEntry(server, 1, volnum, dirent, NULL, NW_NS_DOS, htons(0x0680));
+ }
+ else
+#endif /* CONFIG_NCPFS_NFS_NS */
+ return ncp_DeleteNSEntry(server, 1, volnum, dirent, name, server->name_space[volnum], htons(0x0680));
+}
+
+static inline void ConvertToNWfromDWORD(__u32 sfd, __u8 ret[6])
+{
+ __u16 *dest = (__u16 *) ret;
+ memcpy(ret + 2, &sfd, 4);
+ dest[0] = cpu_to_le16((le16_to_cpu(dest[1]) + le16_to_cpu(1)));
+ return;
+}
+
+/* If both dir and name are NULL, then in target there's already a
+ looked-up entry that wants to be opened. */
+int ncp_open_create_file_or_subdir(struct ncp_server *server,
+ struct inode *dir, char *name,
+ int open_create_mode,
+ __u32 create_attributes,
+ int desired_acc_rights,
+ struct ncp_entry_info *target)
+{
+ __u16 search_attribs = ntohs(0x0600);
+ __u8 volnum = target->i.volNumber;
+ __u32 dirent = target->i.dirEntNum;
+ int result;
+
+ if (dir)
+ {
+ volnum = NCP_FINFO(dir)->volNumber;
+ dirent = NCP_FINFO(dir)->dirEntNum;
+ }
+
+ if ((create_attributes & aDIR) != 0) {
+ search_attribs |= ntohs(0x0080);
+ }
+ ncp_init_request(server);
+ ncp_add_byte(server, 1); /* subfunction */
+ ncp_add_byte(server, server->name_space[volnum]);
+ ncp_add_byte(server, open_create_mode);
+ ncp_add_word(server, search_attribs);
+ ncp_add_dword(server, RIM_ALL);
+ ncp_add_dword(server, create_attributes);
+ /* The desired acc rights seem to be the inherited rights mask
+ for directories */
+ ncp_add_word(server, desired_acc_rights);
+ ncp_add_handle_path(server, volnum, dirent, 1, name);
+
+ if ((result = ncp_request(server, 87)) != 0)
+ goto out;
+ if (!(create_attributes & aDIR))
+ target->opened = 1;
+ target->server_file_handle = ncp_reply_dword(server, 0);
+ target->open_create_action = ncp_reply_byte(server, 4);
+
+ /* in target there's a new finfo to fill */
+ ncp_extract_file_info(ncp_reply_data(server, 6), &(target->i));
+ ConvertToNWfromDWORD(target->server_file_handle, target->file_handle);
+
+out:
+ ncp_unlock_server(server);
+ return result;
+}
+
+int
+ncp_initialize_search(struct ncp_server *server, struct inode *dir,
+ struct nw_search_sequence *target)
+{
+ __u8 volnum = NCP_FINFO(dir)->volNumber;
+ __u32 dirent = NCP_FINFO(dir)->dirEntNum;
+ int result;
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 2); /* subfunction */
+ ncp_add_byte(server, server->name_space[volnum]);
+ ncp_add_byte(server, 0); /* reserved */
+ ncp_add_handle_path(server, volnum, dirent, 1, NULL);
+
+ result = ncp_request(server, 87);
+ if (result)
+ goto out;
+ memcpy(target, ncp_reply_data(server, 0), sizeof(*target));
+
+out:
+ ncp_unlock_server(server);
+ return result;
+}
+
+/* Search for everything */
+int ncp_search_for_file_or_subdir(struct ncp_server *server,
+ struct nw_search_sequence *seq,
+ struct nw_info_struct *target)
+{
+ int result;
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 3); /* subfunction */
+ ncp_add_byte(server, server->name_space[seq->volNumber]);
+ ncp_add_byte(server, 0); /* data stream (???) */
+ ncp_add_word(server, htons(0x0680)); /* Search attribs */
+ ncp_add_dword(server, RIM_ALL); /* return info mask */
+ ncp_add_mem(server, seq, 9);
+#ifdef CONFIG_NCPFS_NFS_NS
+ if (server->name_space[seq->volNumber] == NW_NS_NFS) {
+ ncp_add_byte(server, 0); /* 0 byte pattern */
+ } else
+#endif
+ {
+ ncp_add_byte(server, 2); /* 2 byte pattern */
+ ncp_add_byte(server, 0xff); /* following is a wildcard */
+ ncp_add_byte(server, '*');
+ }
+
+ if ((result = ncp_request(server, 87)) != 0)
+ goto out;
+ memcpy(seq, ncp_reply_data(server, 0), sizeof(*seq));
+ ncp_extract_file_info(ncp_reply_data(server, 10), target);
+
+out:
+ ncp_unlock_server(server);
+ return result;
+}
+
+int
+ncp_RenameNSEntry(struct ncp_server *server,
+ struct inode *old_dir, char *old_name, int old_type,
+ struct inode *new_dir, char *new_name)
+{
+ int result = -EINVAL;
+
+ if ((old_dir == NULL) || (old_name == NULL) ||
+ (new_dir == NULL) || (new_name == NULL))
+ goto out;
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 4); /* subfunction */
+ ncp_add_byte(server, server->name_space[NCP_FINFO(old_dir)->volNumber]);
+ ncp_add_byte(server, 1); /* rename flag */
+ ncp_add_word(server, old_type); /* search attributes */
+
+ /* source Handle Path */
+ ncp_add_byte(server, NCP_FINFO(old_dir)->volNumber);
+ ncp_add_dword(server, NCP_FINFO(old_dir)->dirEntNum);
+ ncp_add_byte(server, 1);
+ ncp_add_byte(server, 1); /* 1 source component */
+
+ /* dest Handle Path */
+ ncp_add_byte(server, NCP_FINFO(new_dir)->volNumber);
+ ncp_add_dword(server, NCP_FINFO(new_dir)->dirEntNum);
+ ncp_add_byte(server, 1);
+ ncp_add_byte(server, 1); /* 1 destination component */
+
+ /* source path string */
+ ncp_add_pstring(server, old_name);
+ /* dest path string */
+ ncp_add_pstring(server, new_name);
+
+ result = ncp_request(server, 87);
+ ncp_unlock_server(server);
+out:
+ return result;
+}
+
+int ncp_ren_or_mov_file_or_subdir(struct ncp_server *server,
+ struct inode *old_dir, char *old_name,
+ struct inode *new_dir, char *new_name)
+{
+ int result;
+ int old_type = htons(0x0600);
+
+/* If somebody can do it atomic, call me... vandrove@vc.cvut.cz */
+ result = ncp_RenameNSEntry(server, old_dir, old_name, old_type,
+ new_dir, new_name);
+ if (result == 0xFF) /* File Not Found, try directory */
+ {
+ old_type = htons(0x1600);
+ result = ncp_RenameNSEntry(server, old_dir, old_name, old_type,
+ new_dir, new_name);
+ }
+ if (result != 0x92) return result; /* All except NO_FILES_RENAMED */
+ result = ncp_del_file_or_subdir(server, new_dir, new_name);
+ if (result != 0) return -EACCES;
+ result = ncp_RenameNSEntry(server, old_dir, old_name, old_type,
+ new_dir, new_name);
+ return result;
+}
+
+
+/* We have to transfer to/from user space */
+int
+ncp_read_kernel(struct ncp_server *server, const char *file_id,
+ __u32 offset, __u16 to_read, char *target, int *bytes_read)
+{
+ char *source;
+ int result;
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 0);
+ ncp_add_mem(server, file_id, 6);
+ ncp_add_dword(server, htonl(offset));
+ ncp_add_word(server, htons(to_read));
+
+ if ((result = ncp_request(server, 72)) != 0) {
+ goto out;
+ }
+ *bytes_read = ntohs(ncp_reply_word(server, 0));
+ source = ncp_reply_data(server, 2 + (offset & 1));
+
+ memcpy(target, source, *bytes_read);
+out:
+ ncp_unlock_server(server);
+ return result;
+}
+
+/* There is a problem... egrep and some other silly tools do:
+ x = mmap(NULL, MAP_PRIVATE, PROT_READ|PROT_WRITE, <ncpfs fd>, 32768);
+ read(<ncpfs fd>, x, 32768);
+ Now copying read result by copy_to_user causes pagefault. This pagefault
+ could not be handled because of server was locked due to read. So we have
+ to use temporary buffer. So ncp_unlock_server must be done before
+ copy_to_user (and for write, copy_from_user must be done before
+ ncp_init_request... same applies for send raw packet ioctl). Because of
+ file is normally read in bigger chunks, caller provides kmalloced
+ (vmalloced) chunk of memory with size >= to_read...
+ */
+int
+ncp_read_bounce(struct ncp_server *server, const char *file_id,
+ __u32 offset, __u16 to_read, char *target, int *bytes_read,
+ void* bounce, __u32 bufsize)
+{
+ int result;
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 0);
+ ncp_add_mem(server, file_id, 6);
+ ncp_add_dword(server, htonl(offset));
+ ncp_add_word(server, htons(to_read));
+ result = ncp_request2(server, 72, bounce, bufsize);
+ ncp_unlock_server(server);
+ if (!result) {
+ int len = be16_to_cpu(get_unaligned((__u16*)((char*)bounce +
+ sizeof(struct ncp_reply_header))));
+ result = -EIO;
+ if (len <= to_read) {
+ char* source;
+
+ source = (char*)bounce +
+ sizeof(struct ncp_reply_header) + 2 +
+ (offset & 1);
+ *bytes_read = len;
+ result = 0;
+ if (copy_to_user(target, source, len))
+ result = -EFAULT;
+ }
+ }
+ return result;
+}
+
+int
+ncp_write_kernel(struct ncp_server *server, const char *file_id,
+ __u32 offset, __u16 to_write,
+ const char *source, int *bytes_written)
+{
+ int result;
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 0);
+ ncp_add_mem(server, file_id, 6);
+ ncp_add_dword(server, htonl(offset));
+ ncp_add_word(server, htons(to_write));
+ ncp_add_mem(server, source, to_write);
+
+ if ((result = ncp_request(server, 73)) == 0)
+ *bytes_written = to_write;
+ ncp_unlock_server(server);
+ return result;
+}
+
+#ifdef CONFIG_NCPFS_IOCTL_LOCKING
+int
+ncp_LogPhysicalRecord(struct ncp_server *server, const char *file_id,
+ __u8 locktype, __u32 offset, __u32 length, __u16 timeout)
+{
+ int result;
+
+ ncp_init_request(server);
+ ncp_add_byte(server, locktype);
+ ncp_add_mem(server, file_id, 6);
+ ncp_add_dword(server, htonl(offset));
+ ncp_add_dword(server, htonl(length));
+ ncp_add_word(server, htons(timeout));
+
+ if ((result = ncp_request(server, 0x1A)) != 0)
+ {
+ ncp_unlock_server(server);
+ return result;
+ }
+ ncp_unlock_server(server);
+ return 0;
+}
+
+int
+ncp_ClearPhysicalRecord(struct ncp_server *server, const char *file_id,
+ __u32 offset, __u32 length)
+{
+ int result;
+
+ ncp_init_request(server);
+ ncp_add_byte(server, 0); /* who knows... lanalyzer says that */
+ ncp_add_mem(server, file_id, 6);
+ ncp_add_dword(server, htonl(offset));
+ ncp_add_dword(server, htonl(length));
+
+ if ((result = ncp_request(server, 0x1E)) != 0)
+ {
+ ncp_unlock_server(server);
+ return result;
+ }
+ ncp_unlock_server(server);
+ return 0;
+}
+#endif /* CONFIG_NCPFS_IOCTL_LOCKING */
+
+#ifdef CONFIG_NCPFS_NLS
+/* This are the NLS conversion routines with inspirations and code parts
+ * from the vfat file system and hints from Petr Vandrovec.
+ */
+
+inline unsigned char
+ncp__tolower(struct nls_table *t, unsigned char c)
+{
+ unsigned char nc = t->charset2lower[c];
+
+ return nc ? nc : c;
+}
+
+inline unsigned char
+ncp__toupper(struct nls_table *t, unsigned char c)
+{
+ unsigned char nc = t->charset2upper[c];
+
+ return nc ? nc : c;
+}
+
+int
+ncp__io2vol(struct ncp_server *server, unsigned char *vname, unsigned int *vlen,
+ const unsigned char *iname, unsigned int ilen, int cc)
+{
+ struct nls_table *in = server->nls_io;
+ struct nls_table *out = server->nls_vol;
+ unsigned char *vname_start;
+ unsigned char *vname_end;
+ const unsigned char *iname_end;
+
+ iname_end = iname + ilen;
+ vname_start = vname;
+ vname_end = vname + *vlen - 1;
+
+ while (iname < iname_end) {
+ int chl;
+ wchar_t ec;
+
+ if (NCP_IS_FLAG(server, NCP_FLAG_UTF8)) {
+ int k;
+
+ k = utf8_mbtowc(&ec, iname, iname_end - iname);
+ if (k < 0)
+ return -EINVAL;
+ iname += k;
+ } else {
+ if (*iname == NCP_ESC) {
+ int k;
+
+ if (iname_end - iname < 5)
+ goto nospec;
+
+ ec = 0;
+ for (k = 1; k < 5; k++) {
+ unsigned char nc;
+
+ nc = iname[k] - '0';
+ if (nc >= 10) {
+ nc -= 'A' - '0' - 10;
+ if ((nc < 10) || (nc > 15)) {
+ goto nospec;
+ }
+ }
+ ec = (ec << 4) | nc;
+ }
+ iname += 5;
+ } else {
+nospec:;
+ if ( (chl = in->char2uni(iname, iname_end - iname, &ec)) < 0)
+ return chl;
+ iname += chl;
+ }
+ }
+
+ /* unitoupper should be here! */
+
+ chl = out->uni2char(ec, vname, vname_end - vname);
+ if (chl < 0)
+ return chl;
+
+ /* this is wrong... */
+ if (cc) {
+ int chi;
+
+ for (chi = 0; chi < chl; chi++){
+ vname[chi] = ncp_toupper(out, vname[chi]);
+ }
+ }
+ vname += chl;
+ }
+
+ *vname = 0;
+ *vlen = vname - vname_start;
+ return 0;
+}
+
+int
+ncp__vol2io(struct ncp_server *server, unsigned char *iname, unsigned int *ilen,
+ const unsigned char *vname, unsigned int vlen, int cc)
+{
+ struct nls_table *in = server->nls_vol;
+ struct nls_table *out = server->nls_io;
+ const unsigned char *vname_end;
+ unsigned char *iname_start;
+ unsigned char *iname_end;
+ unsigned char *vname_cc;
+ int err;
+
+ vname_cc = NULL;
+
+ if (cc) {
+ int i;
+
+ /* this is wrong! */
+ vname_cc = kmalloc(vlen, GFP_KERNEL);
+ if (!vname_cc)
+ return -ENOMEM;
+ for (i = 0; i < vlen; i++)
+ vname_cc[i] = ncp_tolower(in, vname[i]);
+ vname = vname_cc;
+ }
+
+ iname_start = iname;
+ iname_end = iname + *ilen - 1;
+ vname_end = vname + vlen;
+
+ while (vname < vname_end) {
+ wchar_t ec;
+ int chl;
+
+ if ( (chl = in->char2uni(vname, vname_end - vname, &ec)) < 0) {
+ err = chl;
+ goto quit;
+ }
+ vname += chl;
+
+ /* unitolower should be here! */
+
+ if (NCP_IS_FLAG(server, NCP_FLAG_UTF8)) {
+ int k;
+
+ k = utf8_wctomb(iname, ec, iname_end - iname);
+ if (k < 0) {
+ err = -ENAMETOOLONG;
+ goto quit;
+ }
+ iname += k;
+ } else {
+ if ( (chl = out->uni2char(ec, iname, iname_end - iname)) >= 0) {
+ iname += chl;
+ } else {
+ int k;
+
+ if (iname_end - iname < 5) {
+ err = -ENAMETOOLONG;
+ goto quit;
+ }
+ *iname = NCP_ESC;
+ for (k = 4; k > 0; k--) {
+ unsigned char v;
+
+ v = (ec & 0xF) + '0';
+ if (v > '9') {
+ v += 'A' - '9' - 1;
+ }
+ iname[k] = v;
+ ec >>= 4;
+ }
+ iname += 5;
+ }
+ }
+ }
+
+ *iname = 0;
+ *ilen = iname - iname_start;
+ err = 0;
+quit:;
+ if (cc)
+ kfree(vname_cc);
+ return err;
+}
+
+#else
+
+int
+ncp__io2vol(unsigned char *vname, unsigned int *vlen,
+ const unsigned char *iname, unsigned int ilen, int cc)
+{
+ int i;
+
+ if (*vlen <= ilen)
+ return -ENAMETOOLONG;
+
+ if (cc)
+ for (i = 0; i < ilen; i++) {
+ *vname = toupper(*iname);
+ vname++;
+ iname++;
+ }
+ else {
+ memmove(vname, iname, ilen);
+ vname += ilen;
+ }
+
+ *vlen = ilen;
+ *vname = 0;
+ return 0;
+}
+
+int
+ncp__vol2io(unsigned char *iname, unsigned int *ilen,
+ const unsigned char *vname, unsigned int vlen, int cc)
+{
+ int i;
+
+ if (*ilen <= vlen)
+ return -ENAMETOOLONG;
+
+ if (cc)
+ for (i = 0; i < vlen; i++) {
+ *iname = tolower(*vname);
+ iname++;
+ vname++;
+ }
+ else {
+ memmove(iname, vname, vlen);
+ iname += vlen;
+ }
+
+ *ilen = vlen;
+ *iname = 0;
+ return 0;
+}
+
+#endif
+
+inline int
+ncp_strnicmp(struct nls_table *t, const unsigned char *s1,
+ const unsigned char *s2, int n)
+{
+ int i;
+
+ for (i=0; i<n; i++)
+ if (ncp_tolower(t, s1[i]) != ncp_tolower(t, s2[i]))
+ return 1;
+
+ return 0;
+}
diff --git a/fs/ncpfs/ncplib_kernel.h b/fs/ncpfs/ncplib_kernel.h
new file mode 100644
index 00000000000000..275dc6e5044be2
--- /dev/null
+++ b/fs/ncpfs/ncplib_kernel.h
@@ -0,0 +1,221 @@
+/*
+ * ncplib_kernel.h
+ *
+ * Copyright (C) 1995, 1996 by Volker Lendecke
+ * Modified for big endian by J.F. Chadima and David S. Miller
+ * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
+ * Modified 1998, 1999 Wolfram Pienkoss for NLS
+ * Modified 1999 Wolfram Pienkoss for directory caching
+ *
+ */
+
+#ifndef _NCPLIB_H
+#define _NCPLIB_H
+
+#include <linux/config.h>
+
+#include <linux/fs.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/pagemap.h>
+
+#include <asm/uaccess.h>
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
+#include <asm/string.h>
+
+#ifdef CONFIG_NCPFS_NLS
+#include <linux/nls.h>
+#else
+#include <linux/ctype.h>
+#endif /* CONFIG_NCPFS_NLS */
+
+#include <linux/ncp_fs.h>
+
+#define NCP_MIN_SYMLINK_SIZE 8
+#define NCP_MAX_SYMLINK_SIZE 512
+
+#define NCP_BLOCK_SHIFT 9
+#define NCP_BLOCK_SIZE (1 << (NCP_BLOCK_SHIFT))
+
+int ncp_negotiate_buffersize(struct ncp_server *, int, int *);
+int ncp_negotiate_size_and_options(struct ncp_server *server, int size,
+ int options, int *ret_size, int *ret_options);
+int ncp_get_volume_info_with_number(struct ncp_server *, int,
+ struct ncp_volume_info *);
+int ncp_close_file(struct ncp_server *, const char *);
+static inline int ncp_read_bounce_size(__u32 size) {
+ return sizeof(struct ncp_reply_header) + 2 + 2 + size + 8;
+};
+int ncp_read_bounce(struct ncp_server *, const char *, __u32, __u16,
+ char *, int *, void* bounce, __u32 bouncelen);
+int ncp_read_kernel(struct ncp_server *, const char *, __u32, __u16,
+ char *, int *);
+int ncp_write_kernel(struct ncp_server *, const char *, __u32, __u16,
+ const char *, int *);
+
+static inline void ncp_inode_close(struct inode *inode) {
+ atomic_dec(&NCP_FINFO(inode)->opened);
+}
+
+int ncp_obtain_info(struct ncp_server *server, struct inode *, char *,
+ struct nw_info_struct *target);
+int ncp_lookup_volume(struct ncp_server *, char *, struct nw_info_struct *);
+int ncp_modify_file_or_subdir_dos_info(struct ncp_server *, struct inode *,
+ __u32, const struct nw_modify_dos_info *info);
+int ncp_modify_file_or_subdir_dos_info_path(struct ncp_server *, struct inode *,
+ const char* path, __u32, const struct nw_modify_dos_info *info);
+
+int ncp_del_file_or_subdir2(struct ncp_server *, struct dentry*);
+int ncp_del_file_or_subdir(struct ncp_server *, struct inode *, char *);
+int ncp_open_create_file_or_subdir(struct ncp_server *, struct inode *, char *,
+ int, __u32, int, struct ncp_entry_info *);
+
+int ncp_initialize_search(struct ncp_server *, struct inode *,
+ struct nw_search_sequence *target);
+int ncp_search_for_file_or_subdir(struct ncp_server *server,
+ struct nw_search_sequence *seq,
+ struct nw_info_struct *target);
+
+int ncp_ren_or_mov_file_or_subdir(struct ncp_server *server,
+ struct inode *, char *, struct inode *, char *);
+
+
+int
+ncp_LogPhysicalRecord(struct ncp_server *server,
+ const char *file_id, __u8 locktype,
+ __u32 offset, __u32 length, __u16 timeout);
+
+#ifdef CONFIG_NCPFS_IOCTL_LOCKING
+int
+ncp_ClearPhysicalRecord(struct ncp_server *server,
+ const char *file_id,
+ __u32 offset, __u32 length);
+#endif /* CONFIG_NCPFS_IOCTL_LOCKING */
+
+int
+ncp_mount_subdir(struct ncp_server *, struct nw_info_struct *,
+ __u8, __u8, __u32);
+
+#ifdef CONFIG_NCPFS_NLS
+
+unsigned char ncp__tolower(struct nls_table *, unsigned char);
+unsigned char ncp__toupper(struct nls_table *, unsigned char);
+int ncp__io2vol(struct ncp_server *, unsigned char *, unsigned int *,
+ const unsigned char *, unsigned int, int);
+int ncp__vol2io(struct ncp_server *, unsigned char *, unsigned int *,
+ const unsigned char *, unsigned int, int);
+
+#define NCP_ESC ':'
+#define NCP_IO_TABLE(dentry) (NCP_SERVER((dentry)->d_inode)->nls_io)
+#define ncp_tolower(t, c) ncp__tolower(t, c)
+#define ncp_toupper(t, c) ncp__toupper(t, c)
+#define ncp_io2vol(S,m,i,n,k,U) ncp__io2vol(S,m,i,n,k,U)
+#define ncp_vol2io(S,m,i,n,k,U) ncp__vol2io(S,m,i,n,k,U)
+
+#else
+
+int ncp__io2vol(unsigned char *, unsigned int *,
+ const unsigned char *, unsigned int, int);
+int ncp__vol2io(unsigned char *, unsigned int *,
+ const unsigned char *, unsigned int, int);
+
+#define NCP_IO_TABLE(dentry) NULL
+#define ncp_tolower(t, c) tolower(c)
+#define ncp_toupper(t, c) toupper(c)
+#define ncp_io2vol(S,m,i,n,k,U) ncp__io2vol(m,i,n,k,U)
+#define ncp_vol2io(S,m,i,n,k,U) ncp__vol2io(m,i,n,k,U)
+
+#endif /* CONFIG_NCPFS_NLS */
+
+int
+ncp_strnicmp(struct nls_table *,
+ const unsigned char *, const unsigned char *, int);
+
+#define NCP_GET_AGE(dentry) (jiffies - (dentry)->d_time)
+#define NCP_MAX_AGE(server) ((server)->dentry_ttl)
+#define NCP_TEST_AGE(server,dentry) (NCP_GET_AGE(dentry) < NCP_MAX_AGE(server))
+
+static inline void
+ncp_age_dentry(struct ncp_server* server, struct dentry* dentry)
+{
+ dentry->d_time = jiffies - server->dentry_ttl;
+}
+
+static inline void
+ncp_new_dentry(struct dentry* dentry)
+{
+ dentry->d_time = jiffies;
+}
+
+static inline void
+ncp_renew_dentries(struct dentry *parent)
+{
+ struct ncp_server *server = NCP_SERVER(parent->d_inode);
+ struct list_head *next;
+ struct dentry *dentry;
+
+ spin_lock(&dcache_lock);
+ next = parent->d_subdirs.next;
+ while (next != &parent->d_subdirs) {
+ dentry = list_entry(next, struct dentry, d_child);
+
+ if (dentry->d_fsdata == NULL)
+ ncp_age_dentry(server, dentry);
+ else
+ ncp_new_dentry(dentry);
+
+ next = next->next;
+ }
+ spin_unlock(&dcache_lock);
+}
+
+static inline void
+ncp_invalidate_dircache_entries(struct dentry *parent)
+{
+ struct ncp_server *server = NCP_SERVER(parent->d_inode);
+ struct list_head *next;
+ struct dentry *dentry;
+
+ spin_lock(&dcache_lock);
+ next = parent->d_subdirs.next;
+ while (next != &parent->d_subdirs) {
+ dentry = list_entry(next, struct dentry, d_child);
+ dentry->d_fsdata = NULL;
+ ncp_age_dentry(server, dentry);
+ next = next->next;
+ }
+ spin_unlock(&dcache_lock);
+}
+
+struct ncp_cache_head {
+ time_t mtime;
+ unsigned long time; /* cache age */
+ unsigned long end; /* last valid fpos in cache */
+ int eof;
+};
+
+#define NCP_DIRCACHE_SIZE ((int)(PAGE_CACHE_SIZE/sizeof(struct dentry *)))
+union ncp_dir_cache {
+ struct ncp_cache_head head;
+ struct dentry *dentry[NCP_DIRCACHE_SIZE];
+};
+
+#define NCP_FIRSTCACHE_SIZE ((int)((NCP_DIRCACHE_SIZE * \
+ sizeof(struct dentry *) - sizeof(struct ncp_cache_head)) / \
+ sizeof(struct dentry *)))
+
+#define NCP_DIRCACHE_START (NCP_DIRCACHE_SIZE - NCP_FIRSTCACHE_SIZE)
+
+struct ncp_cache_control {
+ struct ncp_cache_head head;
+ struct page *page;
+ union ncp_dir_cache *cache;
+ unsigned long fpos, ofs;
+ int filled, valid, idx;
+};
+
+#endif /* _NCPLIB_H */
diff --git a/fs/ncpfs/ncpsign_kernel.c b/fs/ncpfs/ncpsign_kernel.c
new file mode 100644
index 00000000000000..7183ba4bcb92a9
--- /dev/null
+++ b/fs/ncpfs/ncpsign_kernel.c
@@ -0,0 +1,112 @@
+/*
+ * ncpsign_kernel.c
+ *
+ * Arne de Bruijn (arne@knoware.nl), 1997
+ *
+ */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_NCPFS_PACKET_SIGNING
+
+#include <linux/string.h>
+#include <linux/ncp.h>
+#include "ncpsign_kernel.h"
+
+#define rol32(i,c) (((((i)&0xffffffff)<<c)&0xffffffff)| \
+ (((i)&0xffffffff)>>(32-c)))
+/* i386: 32-bit, little endian, handles mis-alignment */
+#ifdef __i386__
+#define GET_LE32(p) (*(int *)(p))
+#define PUT_LE32(p,v) { *(int *)(p)=v; }
+#else
+/* from include/ncplib.h */
+#define BVAL(buf,pos) (((__u8 *)(buf))[pos])
+#define PVAL(buf,pos) ((unsigned)BVAL(buf,pos))
+#define BSET(buf,pos,val) (BVAL(buf,pos) = (val))
+
+static inline __u16
+WVAL_LH(__u8 * buf, int pos)
+{
+ return PVAL(buf, pos) | PVAL(buf, pos + 1) << 8;
+}
+static inline __u32
+DVAL_LH(__u8 * buf, int pos)
+{
+ return WVAL_LH(buf, pos) | WVAL_LH(buf, pos + 2) << 16;
+}
+static inline void
+WSET_LH(__u8 * buf, int pos, __u16 val)
+{
+ BSET(buf, pos, val & 0xff);
+ BSET(buf, pos + 1, val >> 8);
+}
+static inline void
+DSET_LH(__u8 * buf, int pos, __u32 val)
+{
+ WSET_LH(buf, pos, val & 0xffff);
+ WSET_LH(buf, pos + 2, val >> 16);
+}
+
+#define GET_LE32(p) DVAL_LH(p,0)
+#define PUT_LE32(p,v) DSET_LH(p,0,v)
+#endif
+
+static void nwsign(char *r_data1, char *r_data2, char *outdata) {
+ int i;
+ unsigned int w0,w1,w2,w3;
+ static int rbit[4]={0, 2, 1, 3};
+#ifdef __i386__
+ unsigned int *data2=(int *)r_data2;
+#else
+ unsigned int data2[16];
+ for (i=0;i<16;i++)
+ data2[i]=GET_LE32(r_data2+(i<<2));
+#endif
+ w0=GET_LE32(r_data1);
+ w1=GET_LE32(r_data1+4);
+ w2=GET_LE32(r_data1+8);
+ w3=GET_LE32(r_data1+12);
+ for (i=0;i<16;i+=4) {
+ w0=rol32(w0 + ((w1 & w2) | ((~w1) & w3)) + data2[i+0],3);
+ w3=rol32(w3 + ((w0 & w1) | ((~w0) & w2)) + data2[i+1],7);
+ w2=rol32(w2 + ((w3 & w0) | ((~w3) & w1)) + data2[i+2],11);
+ w1=rol32(w1 + ((w2 & w3) | ((~w2) & w0)) + data2[i+3],19);
+ }
+ for (i=0;i<4;i++) {
+ w0=rol32(w0 + (((w2 | w3) & w1) | (w2 & w3)) + 0x5a827999 + data2[i+0],3);
+ w3=rol32(w3 + (((w1 | w2) & w0) | (w1 & w2)) + 0x5a827999 + data2[i+4],5);
+ w2=rol32(w2 + (((w0 | w1) & w3) | (w0 & w1)) + 0x5a827999 + data2[i+8],9);
+ w1=rol32(w1 + (((w3 | w0) & w2) | (w3 & w0)) + 0x5a827999 + data2[i+12],13);
+ }
+ for (i=0;i<4;i++) {
+ w0=rol32(w0 + ((w1 ^ w2) ^ w3) + 0x6ed9eba1 + data2[rbit[i]+0],3);
+ w3=rol32(w3 + ((w0 ^ w1) ^ w2) + 0x6ed9eba1 + data2[rbit[i]+8],9);
+ w2=rol32(w2 + ((w3 ^ w0) ^ w1) + 0x6ed9eba1 + data2[rbit[i]+4],11);
+ w1=rol32(w1 + ((w2 ^ w3) ^ w0) + 0x6ed9eba1 + data2[rbit[i]+12],15);
+ }
+ PUT_LE32(outdata,(w0+GET_LE32(r_data1)) & 0xffffffff);
+ PUT_LE32(outdata+4,(w1+GET_LE32(r_data1+4)) & 0xffffffff);
+ PUT_LE32(outdata+8,(w2+GET_LE32(r_data1+8)) & 0xffffffff);
+ PUT_LE32(outdata+12,(w3+GET_LE32(r_data1+12)) & 0xffffffff);
+}
+
+/* Make a signature for the current packet and add it at the end of the */
+/* packet. */
+void sign_packet(struct ncp_server *server, int *size) {
+ char data[64];
+
+ memset(data,0,64);
+ memcpy(data,server->sign_root,8);
+ PUT_LE32(data+8,(*size));
+ memcpy(data+12,server->packet+sizeof(struct ncp_request_header)-1,
+ min_t(unsigned int,(*size)-sizeof(struct ncp_request_header)+1,52));
+
+ nwsign(server->sign_last,data,server->sign_last);
+
+ memcpy(server->packet+(*size),server->sign_last,8);
+ (*size)+=8;
+}
+
+#endif /* CONFIG_NCPFS_PACKET_SIGNING */
+
diff --git a/fs/ncpfs/ncpsign_kernel.h b/fs/ncpfs/ncpsign_kernel.h
new file mode 100644
index 00000000000000..141338fb5ad291
--- /dev/null
+++ b/fs/ncpfs/ncpsign_kernel.h
@@ -0,0 +1,15 @@
+/*
+ * ncpsign_kernel.h
+ *
+ * Arne de Bruijn (arne@knoware.nl), 1997
+ *
+ */
+
+#ifndef _NCPSIGN_KERNEL_H
+#define _NCPSIGN_KERNEL_H
+
+#include <linux/ncp_fs.h>
+
+void sign_packet(struct ncp_server *server, int *size);
+
+#endif
diff --git a/fs/ncpfs/sock.c b/fs/ncpfs/sock.c
new file mode 100644
index 00000000000000..470ea5e96ae346
--- /dev/null
+++ b/fs/ncpfs/sock.c
@@ -0,0 +1,610 @@
+/*
+ * linux/fs/ncpfs/sock.c
+ *
+ * Copyright (C) 1992, 1993 Rick Sladkey
+ *
+ * Modified 1995, 1996 by Volker Lendecke to be usable for ncp
+ * Modified 1997 Peter Waltenberg, Bill Hawes, David Woodhouse for 2.1 dcache
+ *
+ */
+
+#include <linux/config.h>
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/socket.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <asm/uaccess.h>
+#include <linux/in.h>
+#include <linux/net.h>
+#include <linux/mm.h>
+#include <linux/netdevice.h>
+#include <linux/signal.h>
+#include <net/scm.h>
+#include <net/sock.h>
+#include <linux/ipx.h>
+#include <linux/poll.h>
+#include <linux/file.h>
+
+#include <linux/ncp_fs.h>
+
+#ifdef CONFIG_NCPFS_PACKET_SIGNING
+#include "ncpsign_kernel.h"
+#endif
+
+static int _recv(struct socket *sock, unsigned char *ubuf, int size,
+ unsigned flags)
+{
+ struct iovec iov;
+ struct msghdr msg;
+ struct scm_cookie scm;
+
+ memset(&scm, 0, sizeof(scm));
+
+ iov.iov_base = ubuf;
+ iov.iov_len = size;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_control = NULL;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ return sock->ops->recvmsg(sock, &msg, size, flags, &scm);
+}
+
+static int _send(struct socket *sock, const void *buff, int len)
+{
+ struct iovec iov;
+ struct msghdr msg;
+ struct scm_cookie scm;
+ int err;
+
+ iov.iov_base = (void *) buff;
+ iov.iov_len = len;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_control = NULL;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_flags = 0;
+
+ err = scm_send(sock, &msg, &scm);
+ if (err < 0) {
+ return err;
+ }
+ err = sock->ops->sendmsg(sock, &msg, len, &scm);
+ scm_destroy(&scm);
+ return err;
+}
+
+static int do_ncp_rpc_call(struct ncp_server *server, int size,
+ struct ncp_reply_header* reply_buf, int max_reply_size)
+{
+ struct file *file;
+ struct socket *sock;
+ int result;
+ char *start = server->packet;
+ poll_table wait_table;
+ int init_timeout, max_timeout;
+ int timeout;
+ int retrans;
+ int major_timeout_seen;
+ int acknowledge_seen;
+ int n;
+
+ /* We have to check the result, so store the complete header */
+ struct ncp_request_header request =
+ *((struct ncp_request_header *) (server->packet));
+
+ struct ncp_reply_header reply;
+
+ file = server->ncp_filp;
+ sock = &file->f_dentry->d_inode->u.socket_i;
+
+ init_timeout = server->m.time_out;
+ max_timeout = NCP_MAX_RPC_TIMEOUT;
+ retrans = server->m.retry_count;
+ major_timeout_seen = 0;
+ acknowledge_seen = 0;
+
+ for (n = 0, timeout = init_timeout;; n++, timeout <<= 1) {
+ /*
+ DDPRINTK("ncpfs: %08lX:%02X%02X%02X%02X%02X%02X:%04X\n",
+ htonl(server->m.serv_addr.sipx_network),
+ server->m.serv_addr.sipx_node[0],
+ server->m.serv_addr.sipx_node[1],
+ server->m.serv_addr.sipx_node[2],
+ server->m.serv_addr.sipx_node[3],
+ server->m.serv_addr.sipx_node[4],
+ server->m.serv_addr.sipx_node[5],
+ ntohs(server->m.serv_addr.sipx_port));
+ */
+ DDPRINTK("ncpfs: req.typ: %04X, con: %d, "
+ "seq: %d",
+ request.type,
+ (request.conn_high << 8) + request.conn_low,
+ request.sequence);
+ DDPRINTK(" func: %d\n",
+ request.function);
+
+ result = _send(sock, (void *) start, size);
+ if (result < 0) {
+ printk(KERN_ERR "ncp_rpc_call: send error = %d\n", result);
+ break;
+ }
+ re_select:
+ poll_initwait(&wait_table);
+ /* mb() is not necessary because ->poll() will serialize
+ instructions adding the wait_table waitqueues in the
+ waitqueue-head before going to calculate the mask-retval. */
+ __set_current_state(TASK_INTERRUPTIBLE);
+ if (!(sock->ops->poll(file, sock, &wait_table) & POLLIN)) {
+ int timed_out;
+ if (timeout > max_timeout) {
+ /* JEJB/JSP 2/7/94
+ * This is useful to see if the system is
+ * hanging */
+ if (acknowledge_seen == 0) {
+ printk(KERN_WARNING "NCP max timeout\n");
+ }
+ timeout = max_timeout;
+ }
+ timed_out = !schedule_timeout(timeout);
+ poll_freewait(&wait_table);
+ current->state = TASK_RUNNING;
+ if (signal_pending(current)) {
+ result = -ERESTARTSYS;
+ break;
+ }
+ if(wait_table.error) {
+ result = wait_table.error;
+ break;
+ }
+ if (timed_out) {
+ if (n < retrans)
+ continue;
+ if (server->m.flags & NCP_MOUNT_SOFT) {
+ printk(KERN_WARNING "NCP server not responding\n");
+ result = -EIO;
+ break;
+ }
+ n = 0;
+ timeout = init_timeout;
+ if (init_timeout < max_timeout)
+ init_timeout <<= 1;
+ if (!major_timeout_seen) {
+ printk(KERN_WARNING "NCP server not responding\n");
+ }
+ major_timeout_seen = 1;
+ continue;
+ }
+ } else {
+ poll_freewait(&wait_table);
+ }
+ current->state = TASK_RUNNING;
+
+ /* Get the header from the next packet using a peek, so keep it
+ * on the recv queue. If it is wrong, it will be some reply
+ * we don't now need, so discard it */
+ result = _recv(sock, (void *) &reply, sizeof(reply),
+ MSG_PEEK | MSG_DONTWAIT);
+ if (result < 0) {
+ if (result == -EAGAIN) {
+ DDPRINTK("ncp_rpc_call: bad select ready\n");
+ goto re_select;
+ }
+ if (result == -ECONNREFUSED) {
+ DPRINTK("ncp_rpc_call: server playing coy\n");
+ goto re_select;
+ }
+ if (result != -ERESTARTSYS) {
+ printk(KERN_ERR "ncp_rpc_call: recv error = %d\n",
+ -result);
+ }
+ break;
+ }
+ if ((result == sizeof(reply))
+ && (reply.type == NCP_POSITIVE_ACK)) {
+ /* Throw away the packet */
+ DPRINTK("ncp_rpc_call: got positive acknowledge\n");
+ _recv(sock, (void *) &reply, sizeof(reply),
+ MSG_DONTWAIT);
+ n = 0;
+ timeout = max_timeout;
+ acknowledge_seen = 1;
+ goto re_select;
+ }
+ DDPRINTK("ncpfs: rep.typ: %04X, con: %d, tsk: %d,"
+ "seq: %d\n",
+ reply.type,
+ (reply.conn_high << 8) + reply.conn_low,
+ reply.task,
+ reply.sequence);
+
+ if ((result >= sizeof(reply))
+ && (reply.type == NCP_REPLY)
+ && ((request.type == NCP_ALLOC_SLOT_REQUEST)
+ || ((reply.sequence == request.sequence)
+ && (reply.conn_low == request.conn_low)
+/* seem to get wrong task from NW311 && (reply.task == request.task) */
+ && (reply.conn_high == request.conn_high)))) {
+ if (major_timeout_seen)
+ printk(KERN_NOTICE "NCP server OK\n");
+ break;
+ }
+ /* JEJB/JSP 2/7/94
+ * we have xid mismatch, so discard the packet and start
+ * again. What a hack! but I can't call recvfrom with
+ * a null buffer yet. */
+ _recv(sock, (void *) &reply, sizeof(reply), MSG_DONTWAIT);
+
+ DPRINTK("ncp_rpc_call: reply mismatch\n");
+ goto re_select;
+ }
+ /*
+ * we have the correct reply, so read into the correct place and
+ * return it
+ */
+ result = _recv(sock, (void *)reply_buf, max_reply_size, MSG_DONTWAIT);
+ if (result < 0) {
+ printk(KERN_WARNING "NCP: notice message: result=%d\n", result);
+ } else if (result < sizeof(struct ncp_reply_header)) {
+ printk(KERN_ERR "NCP: just caught a too small read memory size..., "
+ "email to NET channel\n");
+ printk(KERN_ERR "NCP: result=%d\n", result);
+ result = -EIO;
+ }
+
+ return result;
+}
+
+static int do_tcp_rcv(struct ncp_server *server, void *buffer, size_t len) {
+ poll_table wait_table;
+ struct file *file;
+ struct socket *sock;
+ int init_timeout;
+ size_t dataread;
+ int result = 0;
+
+ file = server->ncp_filp;
+ sock = &file->f_dentry->d_inode->u.socket_i;
+
+ dataread = 0;
+
+ init_timeout = server->m.time_out * 20;
+
+ /* hard-mounted volumes have no timeout, except connection close... */
+ if (!(server->m.flags & NCP_MOUNT_SOFT))
+ init_timeout = 0x7FFF0000;
+
+ while (len) {
+ poll_initwait(&wait_table);
+ /* mb() is not necessary because ->poll() will serialize
+ instructions adding the wait_table waitqueues in the
+ waitqueue-head before going to calculate the mask-retval. */
+ __set_current_state(TASK_INTERRUPTIBLE);
+ if (!(sock->ops->poll(file, sock, &wait_table) & POLLIN)) {
+ init_timeout = schedule_timeout(init_timeout);
+ poll_freewait(&wait_table);
+ current->state = TASK_RUNNING;
+ if (signal_pending(current)) {
+ return -ERESTARTSYS;
+ }
+ if (!init_timeout) {
+ return -EIO;
+ }
+ if(wait_table.error) {
+ return wait_table.error;
+ }
+ } else {
+ poll_freewait(&wait_table);
+ }
+ current->state = TASK_RUNNING;
+
+ result = _recv(sock, buffer, len, MSG_DONTWAIT);
+ if (result < 0) {
+ if (result == -EAGAIN) {
+ DDPRINTK("ncpfs: tcp: bad select ready\n");
+ continue;
+ }
+ return result;
+ }
+ if (result == 0) {
+ printk(KERN_ERR "ncpfs: tcp: EOF on socket\n");
+ return -EIO;
+ }
+ if (result > len) {
+ printk(KERN_ERR "ncpfs: tcp: bug in recvmsg\n");
+ return -EIO;
+ }
+ dataread += result;
+ buffer += result;
+ len -= result;
+ }
+ return 0;
+}
+
+#define NCP_TCP_XMIT_MAGIC (0x446D6454)
+#define NCP_TCP_XMIT_VERSION (1)
+#define NCP_TCP_RCVD_MAGIC (0x744E6350)
+
+static int do_ncp_tcp_rpc_call(struct ncp_server *server, int size,
+ struct ncp_reply_header* reply_buf, int max_reply_size)
+{
+ struct file *file;
+ struct socket *sock;
+ int result;
+ struct iovec iov[2];
+ struct msghdr msg;
+ struct scm_cookie scm;
+ __u32 ncptcp_rcvd_hdr[2];
+ __u32 ncptcp_xmit_hdr[4];
+ int datalen;
+
+ /* We have to check the result, so store the complete header */
+ struct ncp_request_header request =
+ *((struct ncp_request_header *) (server->packet));
+
+ file = server->ncp_filp;
+ sock = &file->f_dentry->d_inode->u.socket_i;
+
+ ncptcp_xmit_hdr[0] = htonl(NCP_TCP_XMIT_MAGIC);
+ ncptcp_xmit_hdr[1] = htonl(size + 16);
+ ncptcp_xmit_hdr[2] = htonl(NCP_TCP_XMIT_VERSION);
+ ncptcp_xmit_hdr[3] = htonl(max_reply_size + 8);
+
+ DDPRINTK("ncpfs: req.typ: %04X, con: %d, "
+ "seq: %d",
+ request.type,
+ (request.conn_high << 8) + request.conn_low,
+ request.sequence);
+ DDPRINTK(" func: %d\n",
+ request.function);
+
+ iov[1].iov_base = (void *) server->packet;
+ iov[1].iov_len = size;
+ iov[0].iov_base = ncptcp_xmit_hdr;
+ iov[0].iov_len = 16;
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_control = NULL;
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 2;
+ msg.msg_flags = MSG_NOSIGNAL;
+
+ result = scm_send(sock, &msg, &scm);
+ if (result < 0) {
+ return result;
+ }
+ result = sock->ops->sendmsg(sock, &msg, size + 16, &scm);
+ scm_destroy(&scm);
+ if (result < 0) {
+ printk(KERN_ERR "ncpfs: tcp: Send failed: %d\n", result);
+ return result;
+ }
+rstrcv:
+ result = do_tcp_rcv(server, ncptcp_rcvd_hdr, 8);
+ if (result)
+ return result;
+ if (ncptcp_rcvd_hdr[0] != htonl(NCP_TCP_RCVD_MAGIC)) {
+ printk(KERN_ERR "ncpfs: tcp: Unexpected reply type %08X\n", ntohl(ncptcp_rcvd_hdr[0]));
+ return -EIO;
+ }
+ datalen = ntohl(ncptcp_rcvd_hdr[1]);
+ if (datalen < 8 + sizeof(*reply_buf) || datalen > max_reply_size + 8) {
+ printk(KERN_ERR "ncpfs: tcp: Unexpected reply len %d\n", datalen);
+ return -EIO;
+ }
+ datalen -= 8;
+ result = do_tcp_rcv(server, reply_buf, datalen);
+ if (result)
+ return result;
+ if (reply_buf->type != NCP_REPLY) {
+ DDPRINTK("ncpfs: tcp: Unexpected NCP type %02X\n", reply_buf->type);
+ goto rstrcv;
+ }
+ if (request.type == NCP_ALLOC_SLOT_REQUEST)
+ return datalen;
+ if (reply_buf->sequence != request.sequence) {
+ printk(KERN_ERR "ncpfs: tcp: Bad sequence number\n");
+ return -EIO;
+ }
+ if ((reply_buf->conn_low != request.conn_low) ||
+ (reply_buf->conn_high != request.conn_high)) {
+ printk(KERN_ERR "ncpfs: tcp: Connection number mismatch\n");
+ return -EIO;
+ }
+ return datalen;
+}
+
+/*
+ * We need the server to be locked here, so check!
+ */
+
+static int ncp_do_request(struct ncp_server *server, int size,
+ void* reply, int max_reply_size)
+{
+ struct file *file;
+ struct socket *sock;
+ int result;
+
+ if (server->lock == 0) {
+ printk(KERN_ERR "ncpfs: Server not locked!\n");
+ return -EIO;
+ }
+ if (!ncp_conn_valid(server)) {
+ return -EIO;
+ }
+#ifdef CONFIG_NCPFS_PACKET_SIGNING
+ if (server->sign_active)
+ {
+ sign_packet(server, &size);
+ }
+#endif /* CONFIG_NCPFS_PACKET_SIGNING */
+ file = server->ncp_filp;
+ sock = &file->f_dentry->d_inode->u.socket_i;
+ /* N.B. this isn't needed ... check socket type? */
+ if (!sock) {
+ printk(KERN_ERR "ncp_rpc_call: socki_lookup failed\n");
+ result = -EBADF;
+ } else {
+ mm_segment_t fs;
+ sigset_t old_set;
+ unsigned long mask, flags;
+
+ spin_lock_irqsave(&current->sigmask_lock, flags);
+ old_set = current->blocked;
+ if (current->flags & PF_EXITING)
+ mask = 0;
+ else
+ mask = sigmask(SIGKILL);
+ if (server->m.flags & NCP_MOUNT_INTR) {
+ /* FIXME: This doesn't seem right at all. So, like,
+ we can't handle SIGINT and get whatever to stop?
+ What if we've blocked it ourselves? What about
+ alarms? Why, in fact, are we mucking with the
+ sigmask at all? -- r~ */
+ if (current->sig->action[SIGINT - 1].sa.sa_handler == SIG_DFL)
+ mask |= sigmask(SIGINT);
+ if (current->sig->action[SIGQUIT - 1].sa.sa_handler == SIG_DFL)
+ mask |= sigmask(SIGQUIT);
+ }
+ siginitsetinv(&current->blocked, mask);
+ recalc_sigpending(current);
+ spin_unlock_irqrestore(&current->sigmask_lock, flags);
+
+ fs = get_fs();
+ set_fs(get_ds());
+
+ if (sock->type == SOCK_STREAM)
+ result = do_ncp_tcp_rpc_call(server, size, reply, max_reply_size);
+ else
+ result = do_ncp_rpc_call(server, size, reply, max_reply_size);
+
+ set_fs(fs);
+
+ spin_lock_irqsave(&current->sigmask_lock, flags);
+ current->blocked = old_set;
+ recalc_sigpending(current);
+ spin_unlock_irqrestore(&current->sigmask_lock, flags);
+ }
+
+ DDPRINTK("do_ncp_rpc_call returned %d\n", result);
+
+ if (result < 0) {
+ /* There was a problem with I/O, so the connections is
+ * no longer usable. */
+ ncp_invalidate_conn(server);
+ }
+ return result;
+}
+
+/* ncp_do_request assures that at least a complete reply header is
+ * received. It assumes that server->current_size contains the ncp
+ * request size
+ */
+int ncp_request2(struct ncp_server *server, int function,
+ void* rpl, int size)
+{
+ struct ncp_request_header *h;
+ struct ncp_reply_header* reply = rpl;
+ int request_size = server->current_size
+ - sizeof(struct ncp_request_header);
+ int result;
+
+ h = (struct ncp_request_header *) (server->packet);
+ if (server->has_subfunction != 0) {
+ *(__u16 *) & (h->data[0]) = htons(request_size - 2);
+ }
+ h->type = NCP_REQUEST;
+
+ server->sequence += 1;
+ h->sequence = server->sequence;
+ h->conn_low = (server->connection) & 0xff;
+ h->conn_high = ((server->connection) & 0xff00) >> 8;
+ /*
+ * The server shouldn't know or care what task is making a
+ * request, so we always use the same task number.
+ */
+ h->task = 2; /* (current->pid) & 0xff; */
+ h->function = function;
+
+ result = ncp_do_request(server, request_size + sizeof(*h), reply, size);
+ if (result < 0) {
+ DPRINTK("ncp_request_error: %d\n", result);
+ goto out;
+ }
+ server->completion = reply->completion_code;
+ server->conn_status = reply->connection_state;
+ server->reply_size = result;
+ server->ncp_reply_size = result - sizeof(struct ncp_reply_header);
+
+ result = reply->completion_code;
+
+ if (result != 0)
+ PPRINTK("ncp_request: completion code=%x\n", result);
+out:
+ return result;
+}
+
+int ncp_connect(struct ncp_server *server)
+{
+ struct ncp_request_header *h;
+ int result;
+
+ h = (struct ncp_request_header *) (server->packet);
+ h->type = NCP_ALLOC_SLOT_REQUEST;
+
+ server->sequence = 0;
+ h->sequence = server->sequence;
+ h->conn_low = 0xff;
+ h->conn_high = 0xff;
+ h->task = 2; /* see above */
+ h->function = 0;
+
+ result = ncp_do_request(server, sizeof(*h), server->packet, server->packet_size);
+ if (result < 0)
+ goto out;
+ server->sequence = 0;
+ server->connection = h->conn_low + (h->conn_high * 256);
+ result = 0;
+out:
+ return result;
+}
+
+int ncp_disconnect(struct ncp_server *server)
+{
+ struct ncp_request_header *h;
+
+ h = (struct ncp_request_header *) (server->packet);
+ h->type = NCP_DEALLOC_SLOT_REQUEST;
+
+ server->sequence += 1;
+ h->sequence = server->sequence;
+ h->conn_low = (server->connection) & 0xff;
+ h->conn_high = ((server->connection) & 0xff00) >> 8;
+ h->task = 2; /* see above */
+ h->function = 0;
+
+ return ncp_do_request(server, sizeof(*h), server->packet, server->packet_size);
+}
+
+void ncp_lock_server(struct ncp_server *server)
+{
+ down(&server->sem);
+ if (server->lock)
+ printk(KERN_WARNING "ncp_lock_server: was locked!\n");
+ server->lock = 1;
+}
+
+void ncp_unlock_server(struct ncp_server *server)
+{
+ if (!server->lock) {
+ printk(KERN_WARNING "ncp_unlock_server: was not locked!\n");
+ return;
+ }
+ server->lock = 0;
+ up(&server->sem);
+}
diff --git a/fs/ncpfs/symlink.c b/fs/ncpfs/symlink.c
new file mode 100644
index 00000000000000..52f095848c3c87
--- /dev/null
+++ b/fs/ncpfs/symlink.c
@@ -0,0 +1,163 @@
+/*
+ * linux/fs/ncpfs/symlink.c
+ *
+ * Code for allowing symbolic links on NCPFS (i.e. NetWare)
+ * Symbolic links are not supported on native NetWare, so we use an
+ * infrequently-used flag (Sh) and store a two-word magic header in
+ * the file to make sure we don't accidentally use a non-link file
+ * as a link.
+ *
+ * from linux/fs/ext2/symlink.c
+ *
+ * Copyright (C) 1998-99, Frank A. Vorstenbosch
+ *
+ * ncpfs symlink handling code
+ * NLS support (c) 1999 Petr Vandrovec
+ *
+ */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_NCPFS_EXTRAS
+
+#include <asm/uaccess.h>
+#include <asm/segment.h>
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/ncp_fs.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/stat.h>
+#include "ncplib_kernel.h"
+
+
+/* these magic numbers must appear in the symlink file -- this makes it a bit
+ more resilient against the magic attributes being set on random files. */
+
+#define NCP_SYMLINK_MAGIC0 le32_to_cpu(0x6c6d7973) /* "symlnk->" */
+#define NCP_SYMLINK_MAGIC1 le32_to_cpu(0x3e2d6b6e)
+
+int ncp_create_new(struct inode *dir, struct dentry *dentry,
+ int mode,int attributes);
+
+/* ----- read a symbolic link ------------------------------------------ */
+
+static int ncp_symlink_readpage(struct file *file, struct page *page)
+{
+ struct inode *inode = page->mapping->host;
+ int error, length, len, cnt;
+ char *link;
+ char *buf = kmap(page);
+
+ error = -ENOMEM;
+ for (cnt = 0; (link=(char *)kmalloc(NCP_MAX_SYMLINK_SIZE, GFP_NFS))==NULL; cnt++) {
+ if (cnt > 10)
+ goto fail;
+ schedule();
+ }
+
+ if (ncp_make_open(inode,O_RDONLY))
+ goto failEIO;
+
+ error=ncp_read_kernel(NCP_SERVER(inode),NCP_FINFO(inode)->file_handle,
+ 0,NCP_MAX_SYMLINK_SIZE,link,&length);
+
+ ncp_inode_close(inode);
+ /* Close file handle if no other users... */
+ ncp_make_closed(inode);
+ if (error)
+ goto failEIO;
+
+ if (length<NCP_MIN_SYMLINK_SIZE ||
+ ((__u32 *)link)[0]!=NCP_SYMLINK_MAGIC0 ||
+ ((__u32 *)link)[1]!=NCP_SYMLINK_MAGIC1)
+ goto failEIO;
+
+ len = NCP_MAX_SYMLINK_SIZE;
+ error = ncp_vol2io(NCP_SERVER(inode), buf, &len, link+8, length-8, 0);
+ kfree(link);
+ if (error)
+ goto fail;
+ SetPageUptodate(page);
+ kunmap(page);
+ UnlockPage(page);
+ return 0;
+
+failEIO:
+ error = -EIO;
+ kfree(link);
+fail:
+ SetPageError(page);
+ kunmap(page);
+ UnlockPage(page);
+ return error;
+}
+
+/*
+ * symlinks can't do much...
+ */
+struct address_space_operations ncp_symlink_aops = {
+ readpage: ncp_symlink_readpage,
+};
+
+/* ----- create a new symbolic link -------------------------------------- */
+
+int ncp_symlink(struct inode *dir, struct dentry *dentry, const char *symname) {
+ struct inode *inode;
+ char *link;
+ int length, err, i;
+
+#ifdef DEBUG
+ PRINTK("ncp_symlink(dir=%p,dentry=%p,symname=%s)\n",dir,dentry,symname);
+#endif
+
+ if (!(NCP_SERVER(dir)->m.flags & NCP_MOUNT_SYMLINKS))
+ return -EPERM; /* EPERM is returned by VFS if symlink procedure does not exist */
+
+ if ((length=strlen(symname))>NCP_MAX_SYMLINK_SIZE-8)
+ return -EINVAL;
+
+ if ((link=(char *)kmalloc(length+9,GFP_NFS))==NULL)
+ return -ENOMEM;
+
+ err = -EIO;
+ if (ncp_create_new(dir,dentry,0,aSHARED|aHIDDEN))
+ goto failfree;
+
+ inode=dentry->d_inode;
+
+ if (ncp_make_open(inode, O_WRONLY))
+ goto failfree;
+
+ ((__u32 *)link)[0]=NCP_SYMLINK_MAGIC0;
+ ((__u32 *)link)[1]=NCP_SYMLINK_MAGIC1;
+
+ /* map to/from server charset, do not touch upper/lower case as
+ symlink can point out of ncp filesystem */
+ length += 1;
+ err = ncp_io2vol(NCP_SERVER(inode),link+8,&length,symname,length-1,0);
+ if (err)
+ goto fail;
+
+ if(ncp_write_kernel(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle,
+ 0, length+8, link, &i) || i!=length+8) {
+ err = -EIO;
+ goto fail;
+ }
+
+ ncp_inode_close(inode);
+ ncp_make_closed(inode);
+ kfree(link);
+ return 0;
+
+fail:
+ ncp_inode_close(inode);
+ ncp_make_closed(inode);
+failfree:
+ kfree(link);
+ return err;
+}
+#endif
+
+/* ----- EOF ----- */
diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile
new file mode 100644
index 00000000000000..2147af7f1742ce
--- /dev/null
+++ b/fs/nfs/Makefile
@@ -0,0 +1,21 @@
+#
+# Makefile for the Linux nfs filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile.
+
+O_TARGET := nfs.o
+
+obj-y := dir.o file.o flushd.o inode.o nfs2xdr.o pagelist.o proc.o \
+ read.o symlink.o unlink.o write.o
+
+obj-$(CONFIG_ROOT_NFS) += nfsroot.o mount_clnt.o
+obj-$(CONFIG_NFS_V3) += nfs3proc.o nfs3xdr.o
+obj-$(CONFIG_NFS_DIRECTIO) += direct.o
+
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
new file mode 100644
index 00000000000000..48ab5af19639cb
--- /dev/null
+++ b/fs/nfs/dir.c
@@ -0,0 +1,1126 @@
+/*
+ * linux/fs/nfs/dir.c
+ *
+ * Copyright (C) 1992 Rick Sladkey
+ *
+ * nfs directory handling functions
+ *
+ * 10 Apr 1996 Added silly rename for unlink --okir
+ * 28 Sep 1996 Improved directory cache --okir
+ * 23 Aug 1997 Claus Heine claus@momo.math.rwth-aachen.de
+ * Re-implemented silly rename for unlink, newly implemented
+ * silly rename for nfs_rename() following the suggestions
+ * of Olaf Kirch (okir) found in this file.
+ * Following Linus comments on my original hack, this version
+ * depends only on the dcache stuff and doesn't touch the inode
+ * layer (iput() and friends).
+ * 6 Jun 1999 Cache readdir lookups in the page cache. -DaveM
+ */
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/nfs_fs.h>
+#include <linux/nfs_mount.h>
+#include <linux/pagemap.h>
+#include <linux/smp_lock.h>
+
+#define NFS_PARANOIA 1
+/* #define NFS_DEBUG_VERBOSE 1 */
+
+static int nfs_readdir(struct file *, void *, filldir_t);
+static struct dentry *nfs_lookup(struct inode *, struct dentry *);
+static int nfs_create(struct inode *, struct dentry *, int);
+static int nfs_mkdir(struct inode *, struct dentry *, int);
+static int nfs_rmdir(struct inode *, struct dentry *);
+static int nfs_unlink(struct inode *, struct dentry *);
+static int nfs_symlink(struct inode *, struct dentry *, const char *);
+static int nfs_link(struct dentry *, struct inode *, struct dentry *);
+static int nfs_mknod(struct inode *, struct dentry *, int, int);
+static int nfs_rename(struct inode *, struct dentry *,
+ struct inode *, struct dentry *);
+static int nfs_fsync_dir(struct file *, struct dentry *, int);
+
+struct file_operations nfs_dir_operations = {
+ read: generic_read_dir,
+ readdir: nfs_readdir,
+ open: nfs_open,
+ release: nfs_release,
+ fsync: nfs_fsync_dir
+};
+
+struct inode_operations nfs_dir_inode_operations = {
+ create: nfs_create,
+ lookup: nfs_lookup,
+ link: nfs_link,
+ unlink: nfs_unlink,
+ symlink: nfs_symlink,
+ mkdir: nfs_mkdir,
+ rmdir: nfs_rmdir,
+ mknod: nfs_mknod,
+ rename: nfs_rename,
+ permission: nfs_permission,
+ revalidate: nfs_revalidate,
+ setattr: nfs_notify_change,
+};
+
+typedef u32 * (*decode_dirent_t)(u32 *, struct nfs_entry *, int);
+typedef struct {
+ struct file *file;
+ struct page *page;
+ unsigned long page_index;
+ u32 *ptr;
+ u64 target;
+ struct nfs_entry *entry;
+ decode_dirent_t decode;
+ int plus;
+ int error;
+} nfs_readdir_descriptor_t;
+
+/* Now we cache directories properly, by stuffing the dirent
+ * data directly in the page cache.
+ *
+ * Inode invalidation due to refresh etc. takes care of
+ * _everything_, no sloppy entry flushing logic, no extraneous
+ * copying, network direct to page cache, the way it was meant
+ * to be.
+ *
+ * NOTE: Dirent information verification is done always by the
+ * page-in of the RPC reply, nowhere else, this simplies
+ * things substantially.
+ */
+static
+int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page *page)
+{
+ struct file *file = desc->file;
+ struct inode *inode = file->f_dentry->d_inode;
+ struct rpc_cred *cred = nfs_file_cred(file);
+ int error;
+
+ dfprintk(VFS, "NFS: nfs_readdir_filler() reading cookie %Lu into page %lu.\n", (long long)desc->entry->cookie, page->index);
+
+ again:
+ error = NFS_PROTO(inode)->readdir(inode, cred, desc->entry->cookie, page,
+ NFS_SERVER(inode)->dtsize, desc->plus);
+ /* We requested READDIRPLUS, but the server doesn't grok it */
+ if (desc->plus && error == -ENOTSUPP) {
+ NFS_FLAGS(inode) &= ~NFS_INO_ADVISE_RDPLUS;
+ desc->plus = 0;
+ goto again;
+ }
+ if (error < 0)
+ goto error;
+ SetPageUptodate(page);
+ /* Ensure consistent page alignment of the data.
+ * Note: assumes we have exclusive access to this mapping either
+ * throught inode->i_sem or some other mechanism.
+ */
+ if (page->index == 0)
+ invalidate_inode_pages(inode);
+ UnlockPage(page);
+ return 0;
+ error:
+ SetPageError(page);
+ UnlockPage(page);
+ invalidate_inode_pages(inode);
+ desc->error = error;
+ return -EIO;
+}
+
+static inline
+int dir_decode(nfs_readdir_descriptor_t *desc)
+{
+ u32 *p = desc->ptr;
+ p = desc->decode(p, desc->entry, desc->plus);
+ if (IS_ERR(p))
+ return PTR_ERR(p);
+ desc->ptr = p;
+ return 0;
+}
+
+static inline
+void dir_page_release(nfs_readdir_descriptor_t *desc)
+{
+ kunmap(desc->page);
+ page_cache_release(desc->page);
+ desc->page = NULL;
+ desc->ptr = NULL;
+}
+
+/*
+ * Given a pointer to a buffer that has already been filled by a call
+ * to readdir, find the next entry.
+ *
+ * If the end of the buffer has been reached, return -EAGAIN, if not,
+ * return the offset within the buffer of the next entry to be
+ * read.
+ */
+static inline
+int find_dirent(nfs_readdir_descriptor_t *desc, struct page *page)
+{
+ struct nfs_entry *entry = desc->entry;
+ int loop_count = 0,
+ status;
+
+ while((status = dir_decode(desc)) == 0) {
+ dfprintk(VFS, "NFS: found cookie %Lu\n", (long long)entry->cookie);
+ if (entry->prev_cookie == desc->target)
+ break;
+ if (loop_count++ > 200) {
+ loop_count = 0;
+ schedule();
+ }
+ }
+ dfprintk(VFS, "NFS: find_dirent() returns %d\n", status);
+ return status;
+}
+
+/*
+ * Find the given page, and call find_dirent() in order to try to
+ * return the next entry.
+ */
+static inline
+int find_dirent_page(nfs_readdir_descriptor_t *desc)
+{
+ struct inode *inode = desc->file->f_dentry->d_inode;
+ struct page *page;
+ int status;
+
+ dfprintk(VFS, "NFS: find_dirent_page() searching directory page %ld\n", desc->page_index);
+
+ desc->plus = NFS_USE_READDIRPLUS(inode);
+ page = read_cache_page(&inode->i_data, desc->page_index,
+ (filler_t *)nfs_readdir_filler, desc);
+ if (IS_ERR(page)) {
+ status = PTR_ERR(page);
+ goto out;
+ }
+ if (!Page_Uptodate(page))
+ goto read_error;
+
+ /* NOTE: Someone else may have changed the READDIRPLUS flag */
+ desc->page = page;
+ desc->ptr = kmap(page);
+ status = find_dirent(desc, page);
+ if (status < 0)
+ dir_page_release(desc);
+ out:
+ dfprintk(VFS, "NFS: find_dirent_page() returns %d\n", status);
+ return status;
+ read_error:
+ page_cache_release(page);
+ return -EIO;
+}
+
+/*
+ * Recurse through the page cache pages, and return a
+ * filled nfs_entry structure of the next directory entry if possible.
+ *
+ * The target for the search is 'desc->target'.
+ */
+static inline
+int readdir_search_pagecache(nfs_readdir_descriptor_t *desc)
+{
+ int loop_count = 0;
+ int res;
+
+ dfprintk(VFS, "NFS: readdir_search_pagecache() searching for cookie %Lu\n", (long long)desc->target);
+ for (;;) {
+ res = find_dirent_page(desc);
+ if (res != -EAGAIN)
+ break;
+ /* Align to beginning of next page */
+ desc->page_index ++;
+ if (loop_count++ > 200) {
+ loop_count = 0;
+ schedule();
+ }
+ }
+ dfprintk(VFS, "NFS: readdir_search_pagecache() returned %d\n", res);
+ return res;
+}
+
+/*
+ * Once we've found the start of the dirent within a page: fill 'er up...
+ */
+static
+int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent,
+ filldir_t filldir)
+{
+ struct file *file = desc->file;
+ struct nfs_entry *entry = desc->entry;
+ unsigned long fileid;
+ int loop_count = 0,
+ res;
+
+ dfprintk(VFS, "NFS: nfs_do_filldir() filling starting @ cookie %Lu\n", (long long)desc->target);
+
+ for(;;) {
+ /* Note: entry->prev_cookie contains the cookie for
+ * retrieving the current dirent on the server */
+ fileid = nfs_fileid_to_ino_t(entry->ino);
+ res = filldir(dirent, entry->name, entry->len,
+ entry->prev_cookie, fileid, DT_UNKNOWN);
+ if (res < 0)
+ break;
+ file->f_pos = desc->target = entry->cookie;
+ if (dir_decode(desc) != 0) {
+ desc->page_index ++;
+ break;
+ }
+ if (loop_count++ > 200) {
+ loop_count = 0;
+ schedule();
+ }
+ }
+ dir_page_release(desc);
+
+ dfprintk(VFS, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n", (long long)desc->target, res);
+ return res;
+}
+
+/*
+ * If we cannot find a cookie in our cache, we suspect that this is
+ * because it points to a deleted file, so we ask the server to return
+ * whatever it thinks is the next entry. We then feed this to filldir.
+ * If all goes well, we should then be able to find our way round the
+ * cache on the next call to readdir_search_pagecache();
+ *
+ * NOTE: we cannot add the anonymous page to the pagecache because
+ * the data it contains might not be page aligned. Besides,
+ * we should already have a complete representation of the
+ * directory in the page cache by the time we get here.
+ */
+static inline
+int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent,
+ filldir_t filldir)
+{
+ struct file *file = desc->file;
+ struct inode *inode = file->f_dentry->d_inode;
+ struct rpc_cred *cred = nfs_file_cred(file);
+ struct page *page = NULL;
+ int status;
+
+ dfprintk(VFS, "NFS: uncached_readdir() searching for cookie %Lu\n", (long long)desc->target);
+
+ page = alloc_page(GFP_HIGHUSER);
+ if (!page) {
+ status = -ENOMEM;
+ goto out;
+ }
+ desc->error = NFS_PROTO(inode)->readdir(inode, cred, desc->target,
+ page,
+ NFS_SERVER(inode)->dtsize,
+ desc->plus);
+ desc->page = page;
+ desc->ptr = kmap(page);
+ if (desc->error >= 0) {
+ if ((status = dir_decode(desc)) == 0)
+ desc->entry->prev_cookie = desc->target;
+ } else
+ status = -EIO;
+ if (status < 0)
+ goto out_release;
+
+ status = nfs_do_filldir(desc, dirent, filldir);
+
+ /* Reset read descriptor so it searches the page cache from
+ * the start upon the next call to readdir_search_pagecache() */
+ desc->page_index = 0;
+ memset(desc->entry, 0, sizeof(*desc->entry));
+ out:
+ dfprintk(VFS, "NFS: uncached_readdir() returns %d\n", status);
+ return status;
+ out_release:
+ dir_page_release(desc);
+ goto out;
+}
+
+/* The file offset position is now represented as a true offset into the
+ * page cache as is the case in most of the other filesystems.
+ */
+static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+ struct dentry *dentry = filp->f_dentry;
+ struct inode *inode = dentry->d_inode;
+ nfs_readdir_descriptor_t my_desc,
+ *desc = &my_desc;
+ struct nfs_entry my_entry;
+ long res;
+
+ res = nfs_revalidate(dentry);
+ if (res < 0)
+ return res;
+
+ /*
+ * filp->f_pos points to the file offset in the page cache.
+ * but if the cache has meanwhile been zapped, we need to
+ * read from the last dirent to revalidate f_pos
+ * itself.
+ */
+ memset(desc, 0, sizeof(*desc));
+ memset(&my_entry, 0, sizeof(my_entry));
+
+ desc->file = filp;
+ desc->target = filp->f_pos;
+ desc->entry = &my_entry;
+ desc->decode = NFS_PROTO(inode)->decode_dirent;
+
+ while(!desc->entry->eof) {
+ res = readdir_search_pagecache(desc);
+ if (res == -EBADCOOKIE) {
+ /* This means either end of directory */
+ if (desc->entry->cookie != desc->target) {
+ /* Or that the server has 'lost' a cookie */
+ res = uncached_readdir(desc, dirent, filldir);
+ if (res >= 0)
+ continue;
+ }
+ res = 0;
+ break;
+ } else if (res < 0)
+ break;
+
+ res = nfs_do_filldir(desc, dirent, filldir);
+ if (res < 0) {
+ res = 0;
+ break;
+ }
+ }
+ if (desc->error < 0)
+ return desc->error;
+ if (res < 0)
+ return res;
+ return 0;
+}
+
+/*
+ * All directory operations under NFS are synchronous, so fsync()
+ * is a dummy operation.
+ */
+int nfs_fsync_dir(struct file *filp, struct dentry *dentry, int datasync)
+{
+ return 0;
+}
+
+/*
+ * A check for whether or not the parent directory has changed.
+ * In the case it has, we assume that the dentries are untrustworthy
+ * and may need to be looked up again.
+ */
+static inline
+int nfs_check_verifier(struct inode *dir, struct dentry *dentry)
+{
+ if (IS_ROOT(dentry))
+ return 1;
+ if (nfs_revalidate_inode(NFS_SERVER(dir), dir))
+ return 0;
+ return time_after(dentry->d_time, NFS_MTIME_UPDATE(dir));
+}
+
+/*
+ * Whenever an NFS operation succeeds, we know that the dentry
+ * is valid, so we update the revalidation timestamp.
+ */
+static inline void nfs_renew_times(struct dentry * dentry)
+{
+ dentry->d_time = jiffies;
+}
+
+static inline
+int nfs_lookup_verify_inode(struct inode *inode, int flags)
+{
+ struct nfs_server *server = NFS_SERVER(inode);
+ /*
+ * If we're interested in close-to-open cache consistency,
+ * then we revalidate the inode upon lookup.
+ */
+ if (!(server->flags & NFS_MOUNT_NOCTO) && !(flags & LOOKUP_CONTINUE))
+ NFS_CACHEINV(inode);
+ return nfs_revalidate_inode(server, inode);
+}
+
+/*
+ * We judge how long we want to trust negative
+ * dentries by looking at the parent inode mtime.
+ *
+ * If parent mtime has changed, we revalidate, else we wait for a
+ * period corresponding to the parent's attribute cache timeout value.
+ */
+static inline int nfs_neg_need_reval(struct inode *dir, struct dentry *dentry)
+{
+ if (!nfs_check_verifier(dir, dentry))
+ return 1;
+ return time_after(jiffies, dentry->d_time + NFS_ATTRTIMEO(dir));
+}
+
+/*
+ * This is called every time the dcache has a lookup hit,
+ * and we should check whether we can really trust that
+ * lookup.
+ *
+ * NOTE! The hit can be a negative hit too, don't assume
+ * we have an inode!
+ *
+ * If the parent directory is seen to have changed, we throw out the
+ * cached dentry and do a new lookup.
+ */
+static int nfs_lookup_revalidate(struct dentry * dentry, int flags)
+{
+ struct inode *dir;
+ struct inode *inode;
+ int error;
+ struct nfs_fh fhandle;
+ struct nfs_fattr fattr;
+
+ lock_kernel();
+ dir = dentry->d_parent->d_inode;
+ inode = dentry->d_inode;
+
+ if (!inode) {
+ if (nfs_neg_need_reval(dir, dentry))
+ goto out_bad;
+ goto out_valid;
+ }
+
+ if (is_bad_inode(inode)) {
+ dfprintk(VFS, "nfs_lookup_validate: %s/%s has dud inode\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
+ goto out_bad;
+ }
+
+ /* Force a full look up iff the parent directory has changed */
+ if (nfs_check_verifier(dir, dentry)) {
+ if (nfs_lookup_verify_inode(inode, flags))
+ goto out_bad;
+ goto out_valid;
+ }
+
+ if (NFS_STALE(inode))
+ goto out_bad;
+
+ error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr);
+ if (error)
+ goto out_bad;
+ if (memcmp(NFS_FH(inode), &fhandle, sizeof(struct nfs_fh))!= 0)
+ goto out_bad;
+ if ((error = nfs_refresh_inode(inode, &fattr)) != 0)
+ goto out_bad;
+
+ nfs_renew_times(dentry);
+ out_valid:
+ unlock_kernel();
+ return 1;
+ out_bad:
+ NFS_CACHEINV(dir);
+ if (inode && S_ISDIR(inode->i_mode)) {
+ /* Purge readdir caches. */
+ nfs_zap_caches(inode);
+ /* If we have submounts, don't unhash ! */
+ if (have_submounts(dentry))
+ goto out_valid;
+ shrink_dcache_parent(dentry);
+ }
+ d_drop(dentry);
+ unlock_kernel();
+ return 0;
+}
+
+/*
+ * This is called from dput() when d_count is going to 0.
+ */
+static int nfs_dentry_delete(struct dentry *dentry)
+{
+ dfprintk(VFS, "NFS: dentry_delete(%s/%s, %x)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ dentry->d_flags);
+
+ if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
+ /* Unhash it, so that ->d_iput() would be called */
+ return 1;
+ }
+ if (!(dentry->d_sb->s_flags & MS_ACTIVE)) {
+ /* Unhash it, so that ancestors of killed async unlink
+ * files will be cleaned up during umount */
+ return 1;
+ }
+ return 0;
+
+}
+
+/*
+ * Called when the dentry loses inode.
+ * We use it to clean up silly-renamed files.
+ */
+static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode)
+{
+ if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
+ lock_kernel();
+ nfs_complete_unlink(dentry);
+ unlock_kernel();
+ }
+ if (is_bad_inode(inode))
+ force_delete(inode);
+ iput(inode);
+}
+
+struct dentry_operations nfs_dentry_operations = {
+ d_revalidate: nfs_lookup_revalidate,
+ d_delete: nfs_dentry_delete,
+ d_iput: nfs_dentry_iput,
+};
+
+static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry)
+{
+ struct inode *inode;
+ int error;
+ struct nfs_fh fhandle;
+ struct nfs_fattr fattr;
+
+ dfprintk(VFS, "NFS: lookup(%s/%s)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
+
+ error = -ENAMETOOLONG;
+ if (dentry->d_name.len > NFS_SERVER(dir)->namelen)
+ goto out;
+
+ error = -ENOMEM;
+ dentry->d_op = &nfs_dentry_operations;
+
+ error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr);
+ inode = NULL;
+ if (error == -ENOENT)
+ goto no_entry;
+ if (!error) {
+ error = -EACCES;
+ inode = nfs_fhget(dentry, &fhandle, &fattr);
+ if (inode) {
+ no_entry:
+ d_add(dentry, inode);
+ error = 0;
+ }
+ nfs_renew_times(dentry);
+ }
+out:
+ return ERR_PTR(error);
+}
+
+/*
+ * Code common to create, mkdir, and mknod.
+ */
+static int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle,
+ struct nfs_fattr *fattr)
+{
+ struct inode *inode;
+ int error = -EACCES;
+
+ if (fhandle->size == 0 || !(fattr->valid & NFS_ATTR_FATTR)) {
+ struct inode *dir = dentry->d_parent->d_inode;
+ error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr);
+ if (error)
+ goto out_err;
+ }
+ inode = nfs_fhget(dentry, fhandle, fattr);
+ if (inode) {
+ d_instantiate(dentry, inode);
+ nfs_renew_times(dentry);
+ error = 0;
+ }
+ return error;
+out_err:
+ d_drop(dentry);
+ return error;
+}
+
+/*
+ * Following a failed create operation, we drop the dentry rather
+ * than retain a negative dentry. This avoids a problem in the event
+ * that the operation succeeded on the server, but an error in the
+ * reply path made it appear to have failed.
+ */
+static int nfs_create(struct inode *dir, struct dentry *dentry, int mode)
+{
+ struct iattr attr;
+ struct nfs_fattr fattr;
+ struct nfs_fh fhandle;
+ int error;
+
+ dfprintk(VFS, "NFS: create(%x/%ld, %s\n",
+ dir->i_dev, dir->i_ino, dentry->d_name.name);
+
+ attr.ia_mode = mode;
+ attr.ia_valid = ATTR_MODE;
+
+ /*
+ * The 0 argument passed into the create function should one day
+ * contain the O_EXCL flag if requested. This allows NFSv3 to
+ * select the appropriate create strategy. Currently open_namei
+ * does not pass the create flags.
+ */
+ nfs_zap_caches(dir);
+ error = NFS_PROTO(dir)->create(dir, &dentry->d_name,
+ &attr, 0, &fhandle, &fattr);
+ if (!error)
+ error = nfs_instantiate(dentry, &fhandle, &fattr);
+ else
+ d_drop(dentry);
+ return error;
+}
+
+/*
+ * See comments for nfs_proc_create regarding failed operations.
+ */
+static int nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev)
+{
+ struct iattr attr;
+ struct nfs_fattr fattr;
+ struct nfs_fh fhandle;
+ int error;
+
+ dfprintk(VFS, "NFS: mknod(%x/%ld, %s\n",
+ dir->i_dev, dir->i_ino, dentry->d_name.name);
+
+ attr.ia_mode = mode;
+ attr.ia_valid = ATTR_MODE;
+
+ nfs_zap_caches(dir);
+ error = NFS_PROTO(dir)->mknod(dir, &dentry->d_name, &attr, rdev,
+ &fhandle, &fattr);
+ if (!error)
+ error = nfs_instantiate(dentry, &fhandle, &fattr);
+ else
+ d_drop(dentry);
+ return error;
+}
+
+/*
+ * See comments for nfs_proc_create regarding failed operations.
+ */
+static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+ struct iattr attr;
+ struct nfs_fattr fattr;
+ struct nfs_fh fhandle;
+ int error;
+
+ dfprintk(VFS, "NFS: mkdir(%x/%ld, %s\n",
+ dir->i_dev, dir->i_ino, dentry->d_name.name);
+
+ attr.ia_valid = ATTR_MODE;
+ attr.ia_mode = mode | S_IFDIR;
+
+#if 0
+ /*
+ * Always drop the dentry, we can't always depend on
+ * the fattr returned by the server (AIX seems to be
+ * broken). We're better off doing another lookup than
+ * depending on potentially bogus information.
+ */
+ d_drop(dentry);
+#endif
+ nfs_zap_caches(dir);
+ error = NFS_PROTO(dir)->mkdir(dir, &dentry->d_name, &attr, &fhandle,
+ &fattr);
+ if (!error)
+ error = nfs_instantiate(dentry, &fhandle, &fattr);
+ else
+ d_drop(dentry);
+ return error;
+}
+
+static int nfs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ int error;
+
+ dfprintk(VFS, "NFS: rmdir(%x/%ld, %s\n",
+ dir->i_dev, dir->i_ino, dentry->d_name.name);
+
+ nfs_zap_caches(dir);
+ error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name);
+ if (!error)
+ dentry->d_inode->i_nlink = 0;
+
+ return error;
+}
+
+static int nfs_sillyrename(struct inode *dir, struct dentry *dentry)
+{
+ static unsigned int sillycounter;
+ const int i_inosize = sizeof(dir->i_ino)*2;
+ const int countersize = sizeof(sillycounter)*2;
+ const int slen = strlen(".nfs") + i_inosize + countersize;
+ char silly[slen+1];
+ struct qstr qsilly;
+ struct dentry *sdentry;
+ int error = -EIO;
+
+ dfprintk(VFS, "NFS: silly-rename(%s/%s, ct=%d)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ atomic_read(&dentry->d_count));
+
+ if (atomic_read(&dentry->d_count) == 1)
+ goto out; /* No need to silly rename. */
+
+
+#ifdef NFS_PARANOIA
+if (!dentry->d_inode)
+printk("NFS: silly-renaming %s/%s, negative dentry??\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+#endif
+ /*
+ * We don't allow a dentry to be silly-renamed twice.
+ */
+ error = -EBUSY;
+ if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
+ goto out;
+
+ sprintf(silly, ".nfs%*.*lx",
+ i_inosize, i_inosize, dentry->d_inode->i_ino);
+
+ sdentry = NULL;
+ do {
+ char *suffix = silly + slen - countersize;
+
+ dput(sdentry);
+ sillycounter++;
+ sprintf(suffix, "%*.*x", countersize, countersize, sillycounter);
+
+ dfprintk(VFS, "trying to rename %s to %s\n",
+ dentry->d_name.name, silly);
+
+ sdentry = lookup_one_len(silly, dentry->d_parent, slen);
+ /*
+ * N.B. Better to return EBUSY here ... it could be
+ * dangerous to delete the file while it's in use.
+ */
+ if (IS_ERR(sdentry))
+ goto out;
+ } while(sdentry->d_inode != NULL); /* need negative lookup */
+
+ nfs_zap_caches(dir);
+ qsilly.name = silly;
+ qsilly.len = strlen(silly);
+ error = NFS_PROTO(dir)->rename(dir, &dentry->d_name, dir, &qsilly);
+ if (!error) {
+ nfs_renew_times(dentry);
+ d_move(dentry, sdentry);
+ error = nfs_async_unlink(dentry);
+ /* If we return 0 we don't unlink */
+ }
+ dput(sdentry);
+out:
+ return error;
+}
+
+/*
+ * Remove a file after making sure there are no pending writes,
+ * and after checking that the file has only one user.
+ *
+ * We invalidate the attribute cache and free the inode prior to the operation
+ * to avoid possible races if the server reuses the inode.
+ */
+static int nfs_safe_remove(struct dentry *dentry)
+{
+ struct inode *dir = dentry->d_parent->d_inode;
+ struct inode *inode = dentry->d_inode;
+ int error = -EBUSY, rehash = 0;
+
+ dfprintk(VFS, "NFS: safe_remove(%s/%s)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
+
+ /*
+ * Unhash the dentry while we remove the file ...
+ */
+ if (!d_unhashed(dentry)) {
+ d_drop(dentry);
+ rehash = 1;
+ }
+ if (atomic_read(&dentry->d_count) > 1) {
+#ifdef NFS_PARANOIA
+ printk("nfs_safe_remove: %s/%s busy, d_count=%d\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ atomic_read(&dentry->d_count));
+#endif
+ goto out;
+ }
+
+ /* If the dentry was sillyrenamed, we simply call d_delete() */
+ if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
+ error = 0;
+ goto out_delete;
+ }
+
+ nfs_zap_caches(dir);
+ if (inode)
+ NFS_CACHEINV(inode);
+ error = NFS_PROTO(dir)->remove(dir, &dentry->d_name);
+ if (error < 0)
+ goto out;
+ if (inode)
+ inode->i_nlink--;
+
+ out_delete:
+ /*
+ * Free the inode
+ */
+ d_delete(dentry);
+out:
+ if (rehash)
+ d_rehash(dentry);
+ return error;
+}
+
+/* We do silly rename. In case sillyrename() returns -EBUSY, the inode
+ * belongs to an active ".nfs..." file and we return -EBUSY.
+ *
+ * If sillyrename() returns 0, we do nothing, otherwise we unlink.
+ */
+static int nfs_unlink(struct inode *dir, struct dentry *dentry)
+{
+ int error;
+
+ dfprintk(VFS, "NFS: unlink(%x/%ld, %s)\n",
+ dir->i_dev, dir->i_ino, dentry->d_name.name);
+
+ error = nfs_sillyrename(dir, dentry);
+ if (error && error != -EBUSY) {
+ error = nfs_safe_remove(dentry);
+ if (!error) {
+ nfs_renew_times(dentry);
+ }
+ }
+ return error;
+}
+
+static int
+nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
+{
+ struct iattr attr;
+ struct nfs_fattr sym_attr;
+ struct nfs_fh sym_fh;
+ struct qstr qsymname;
+ unsigned int maxlen;
+ int error;
+
+ dfprintk(VFS, "NFS: symlink(%x/%ld, %s, %s)\n",
+ dir->i_dev, dir->i_ino, dentry->d_name.name, symname);
+
+ error = -ENAMETOOLONG;
+ maxlen = (NFS_PROTO(dir)->version==2) ? NFS2_MAXPATHLEN : NFS3_MAXPATHLEN;
+ if (strlen(symname) > maxlen)
+ goto out;
+
+#ifdef NFS_PARANOIA
+if (dentry->d_inode)
+printk("nfs_proc_symlink: %s/%s not negative!\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+#endif
+ /*
+ * Fill in the sattr for the call.
+ * Note: SunOS 4.1.2 crashes if the mode isn't initialized!
+ */
+ attr.ia_valid = ATTR_MODE;
+ attr.ia_mode = S_IFLNK | S_IRWXUGO;
+
+ qsymname.name = symname;
+ qsymname.len = strlen(symname);
+
+ nfs_zap_caches(dir);
+ error = NFS_PROTO(dir)->symlink(dir, &dentry->d_name, &qsymname,
+ &attr, &sym_fh, &sym_attr);
+ if (!error) {
+ error = nfs_instantiate(dentry, &sym_fh, &sym_attr);
+ } else {
+ if (error == -EEXIST)
+ printk("nfs_proc_symlink: %s/%s already exists??\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
+ d_drop(dentry);
+ }
+
+out:
+ return error;
+}
+
+static int
+nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
+{
+ struct inode *inode = old_dentry->d_inode;
+ int error;
+
+ dfprintk(VFS, "NFS: link(%s/%s -> %s/%s)\n",
+ old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
+ dentry->d_parent->d_name.name, dentry->d_name.name);
+
+ /*
+ * Drop the dentry in advance to force a new lookup.
+ * Since nfs_proc_link doesn't return a file handle,
+ * we can't use the existing dentry.
+ */
+ d_drop(dentry);
+ nfs_zap_caches(dir);
+ NFS_CACHEINV(inode);
+ error = NFS_PROTO(dir)->link(inode, dir, &dentry->d_name);
+ return error;
+}
+
+/*
+ * RENAME
+ * FIXME: Some nfsds, like the Linux user space nfsd, may generate a
+ * different file handle for the same inode after a rename (e.g. when
+ * moving to a different directory). A fail-safe method to do so would
+ * be to look up old_dir/old_name, create a link to new_dir/new_name and
+ * rename the old file using the sillyrename stuff. This way, the original
+ * file in old_dir will go away when the last process iput()s the inode.
+ *
+ * FIXED.
+ *
+ * It actually works quite well. One needs to have the possibility for
+ * at least one ".nfs..." file in each directory the file ever gets
+ * moved or linked to which happens automagically with the new
+ * implementation that only depends on the dcache stuff instead of
+ * using the inode layer
+ *
+ * Unfortunately, things are a little more complicated than indicated
+ * above. For a cross-directory move, we want to make sure we can get
+ * rid of the old inode after the operation. This means there must be
+ * no pending writes (if it's a file), and the use count must be 1.
+ * If these conditions are met, we can drop the dentries before doing
+ * the rename.
+ */
+static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ struct inode *old_inode = old_dentry->d_inode;
+ struct inode *new_inode = new_dentry->d_inode;
+ struct dentry *dentry = NULL, *rehash = NULL;
+ int error = -EBUSY;
+
+ /*
+ * To prevent any new references to the target during the rename,
+ * we unhash the dentry and free the inode in advance.
+ */
+ if (!d_unhashed(new_dentry)) {
+ d_drop(new_dentry);
+ rehash = new_dentry;
+ }
+
+ dfprintk(VFS, "NFS: rename(%s/%s -> %s/%s, ct=%d)\n",
+ old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
+ new_dentry->d_parent->d_name.name, new_dentry->d_name.name,
+ atomic_read(&new_dentry->d_count));
+
+ /*
+ * First check whether the target is busy ... we can't
+ * safely do _any_ rename if the target is in use.
+ *
+ * For files, make a copy of the dentry and then do a
+ * silly-rename. If the silly-rename succeeds, the
+ * copied dentry is hashed and becomes the new target.
+ */
+ if (!new_inode)
+ goto go_ahead;
+ if (S_ISDIR(new_inode->i_mode))
+ goto out;
+ else if (atomic_read(&new_dentry->d_count) > 1) {
+ int err;
+ /* copy the target dentry's name */
+ dentry = d_alloc(new_dentry->d_parent,
+ &new_dentry->d_name);
+ if (!dentry)
+ goto out;
+
+ /* silly-rename the existing target ... */
+ err = nfs_sillyrename(new_dir, new_dentry);
+ if (!err) {
+ new_dentry = rehash = dentry;
+ new_inode = NULL;
+ /* instantiate the replacement target */
+ d_instantiate(new_dentry, NULL);
+ }
+
+ /* dentry still busy? */
+ if (atomic_read(&new_dentry->d_count) > 1) {
+#ifdef NFS_PARANOIA
+ printk("nfs_rename: target %s/%s busy, d_count=%d\n",
+ new_dentry->d_parent->d_name.name,
+ new_dentry->d_name.name,
+ atomic_read(&new_dentry->d_count));
+#endif
+ goto out;
+ }
+ }
+
+go_ahead:
+ /*
+ * ... prune child dentries and writebacks if needed.
+ */
+ if (atomic_read(&old_dentry->d_count) > 1) {
+ nfs_wb_all(old_inode);
+ shrink_dcache_parent(old_dentry);
+ }
+
+ if (new_inode)
+ d_delete(new_dentry);
+
+ nfs_zap_caches(new_dir);
+ nfs_zap_caches(old_dir);
+ NFS_CACHEINV(old_inode);
+ error = NFS_PROTO(old_dir)->rename(old_dir, &old_dentry->d_name,
+ new_dir, &new_dentry->d_name);
+out:
+ if (rehash)
+ d_rehash(rehash);
+ if (!error && !S_ISDIR(old_inode->i_mode))
+ d_move(old_dentry, new_dentry);
+
+ /* new dentry created? */
+ if (dentry)
+ dput(dentry);
+ return error;
+}
+
+int
+nfs_permission(struct inode *inode, int mask)
+{
+ int error = vfs_permission(inode, mask);
+
+ if (!NFS_PROTO(inode)->access)
+ goto out;
+
+ if (error == -EROFS)
+ goto out;
+
+ /*
+ * Trust UNIX mode bits except:
+ *
+ * 1) When override capabilities may have been invoked
+ * 2) When root squashing may be involved
+ * 3) When ACLs may overturn a negative answer */
+ if (!capable(CAP_DAC_OVERRIDE) && !capable(CAP_DAC_READ_SEARCH)
+ && (current->fsuid != 0) && (current->fsgid != 0)
+ && error != -EACCES)
+ goto out;
+
+ error = NFS_PROTO(inode)->access(inode, mask, 0);
+
+ if (error == -EACCES && NFS_CLIENT(inode)->cl_droppriv &&
+ current->uid != 0 && current->gid != 0 &&
+ (current->fsuid != current->uid || current->fsgid != current->gid))
+ error = NFS_PROTO(inode)->access(inode, mask, 1);
+
+ out:
+ return error;
+}
+
+/*
+ * Local variables:
+ * version-control: t
+ * kept-new-versions: 5
+ * End:
+ */
diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c
new file mode 100644
index 00000000000000..27aaea43f05525
--- /dev/null
+++ b/fs/nfs/direct.c
@@ -0,0 +1,382 @@
+/*
+ * linux/fs/nfs/direct.c
+ *
+ * High-performance direct I/O for the NFS client
+ *
+ * When an application requests uncached I/O, all read and write requests
+ * are made directly to the server; data stored or fetched via these
+ * requests is not cached in the Linux page cache. The client does not
+ * correct unaligned requests from applications. All requested bytes are
+ * held on permanent storage before a direct write system call returns to
+ * an application. Applications that manage their own data caching, such
+ * as databases, make very good use of direct I/O on local file systems.
+ *
+ * Solaris implements an uncached I/O facility called directio() that
+ * is used for backups and sequential I/O to very large files. Solaris
+ * also supports uncaching whole NFS partitions with "-o forcedirectio,"
+ * an undocumented mount option.
+ *
+ * Note that I/O to read in executables (e.g. kernel_read) cannot use
+ * direct (kiobuf) reads because there is no vma backing the passed-in
+ * data buffer.
+ *
+ * Designed by Jeff Kimmel, Chuck Lever, and Trond Myklebust.
+ *
+ * Initial implementation: 12/2001 by Chuck Lever <cel@netapp.com>
+ *
+ * TODO:
+ *
+ * 1. Use concurrent asynchronous network requests rather than
+ * serialized synchronous network requests for normal (non-sync)
+ * direct I/O.
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/file.h>
+#include <linux/errno.h>
+#include <linux/nfs_fs.h>
+#include <linux/smp_lock.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/iobuf.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#define NFSDBG_FACILITY (NFSDBG_PAGECACHE | NFSDBG_VFS)
+#define VERF_SIZE (2 * sizeof(__u32))
+
+static inline int
+nfs_direct_read_rpc(struct file *file, struct nfs_readargs *arg)
+{
+ int result;
+ struct inode * inode = file->f_dentry->d_inode;
+ struct nfs_fattr fattr;
+ struct rpc_message msg;
+ struct nfs_readres res = { &fattr, arg->count, 0 };
+
+#ifdef CONFIG_NFS_V3
+ msg.rpc_proc = (NFS_PROTO(inode)->version == 3) ?
+ NFS3PROC_READ : NFSPROC_READ;
+#else
+ msg.rpc_proc = NFSPROC_READ;
+#endif
+ msg.rpc_argp = arg;
+ msg.rpc_resp = &res;
+
+ lock_kernel();
+ msg.rpc_cred = nfs_file_cred(file);
+ fattr.valid = 0;
+ result = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
+ nfs_refresh_inode(inode, &fattr);
+ unlock_kernel();
+
+ return result;
+}
+
+static inline int
+nfs_direct_write_rpc(struct file *file, struct nfs_writeargs *arg,
+ struct nfs_writeverf *verf)
+{
+ int result;
+ struct inode *inode = file->f_dentry->d_inode;
+ struct nfs_fattr fattr;
+ struct rpc_message msg;
+ struct nfs_writeres res = { &fattr, verf, 0 };
+
+#ifdef CONFIG_NFS_V3
+ msg.rpc_proc = (NFS_PROTO(inode)->version == 3) ?
+ NFS3PROC_WRITE : NFSPROC_WRITE;
+#else
+ msg.rpc_proc = NFSPROC_WRITE;
+#endif
+ msg.rpc_argp = arg;
+ msg.rpc_resp = &res;
+
+ lock_kernel();
+ msg.rpc_cred = get_rpccred(nfs_file_cred(file));
+ fattr.valid = 0;
+ result = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
+ nfs_write_attributes(inode, &fattr);
+ put_rpccred(msg.rpc_cred);
+ unlock_kernel();
+
+#ifdef CONFIG_NFS_V3
+ if (NFS_PROTO(inode)->version == 3) {
+ if (result > 0) {
+ if ((arg->stable == NFS_FILE_SYNC) &&
+ (verf->committed != NFS_FILE_SYNC)) {
+ printk(KERN_ERR
+ "%s: server didn't sync stable write request\n",
+ __FUNCTION__);
+ return -EIO;
+ }
+
+ if (result != arg->count) {
+ printk(KERN_INFO
+ "%s: short write, count=%u, result=%d\n",
+ __FUNCTION__, arg->count, result);
+ }
+ }
+ return result;
+ } else {
+#endif
+ verf->committed = NFS_FILE_SYNC; /* NFSv2 always syncs data */
+ if (result == 0)
+ return arg->count;
+ return result;
+#ifdef CONFIG_NFS_V3
+ }
+#endif
+}
+
+#ifdef CONFIG_NFS_V3
+static inline int
+nfs_direct_commit_rpc(struct inode *inode, loff_t offset, size_t count,
+ struct nfs_writeverf *verf)
+{
+ int result;
+ struct nfs_fattr fattr;
+ struct nfs_writeargs arg = { NFS_FH(inode), offset, count, 0, 0,
+ NULL };
+ struct nfs_writeres res = { &fattr, verf, 0 };
+ struct rpc_message msg = { NFS3PROC_COMMIT, &arg, &res, NULL };
+
+ fattr.valid = 0;
+
+ lock_kernel();
+ result = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
+ nfs_write_attributes(inode, &fattr);
+ unlock_kernel();
+
+ return result;
+}
+#else
+static inline int
+nfs_direct_commit_rpc(struct inode *inode, loff_t offset, size_t count,
+ struct nfs_writeverf *verf)
+{
+ return 0;
+}
+#endif
+
+/*
+ * Walk through the iobuf and create an iovec for each "rsize" bytes.
+ */
+static int
+nfs_direct_read(struct file *file, struct kiobuf *iobuf, loff_t offset,
+ size_t count)
+{
+ int curpage, total;
+ int result = 0;
+ struct inode *inode = file->f_dentry->d_inode;
+ int rsize = NFS_SERVER(inode)->rsize;
+ struct page *pages[NFS_READ_MAXIOV];
+ struct nfs_readargs args = { NFS_FH(inode), offset, 0, iobuf->offset,
+ pages };
+
+ total = 0;
+ curpage = 0;
+ while (count) {
+ int len, request;
+ struct page **dest = pages;
+
+ request = count;
+ if (count > rsize)
+ request = rsize;
+ args.count = request;
+ args.offset = offset;
+ args.pgbase = (iobuf->offset + total) & ~PAGE_MASK;
+ len = PAGE_SIZE - args.pgbase;
+
+ do {
+ struct page *page = iobuf->maplist[curpage];
+
+ if (curpage >= iobuf->nr_pages || !page) {
+ result = -EFAULT;
+ goto out_err;
+ }
+
+ *dest++ = page;
+ /* zero after the first iov */
+ if (request < len)
+ break;
+ request -= len;
+ len = PAGE_SIZE;
+ curpage++;
+ } while (request != 0);
+
+ result = nfs_direct_read_rpc(file, &args);
+
+ if (result < 0)
+ break;
+
+ total += result;
+ if (result < args.count) /* NFSv2ism */
+ break;
+ count -= result;
+ offset += result;
+ };
+out_err:
+ if (!total)
+ return result;
+ return total;
+}
+
+/*
+ * Walk through the iobuf and create an iovec for each "wsize" bytes.
+ * If only one network write is necessary, or if the O_SYNC flag or
+ * 'sync' mount option are present, or if this is a V2 inode, use
+ * FILE_SYNC. Otherwise, use UNSTABLE and finish with a COMMIT.
+ *
+ * The mechanics of this function are much the same as nfs_direct_read,
+ * with the added complexity of committing unstable writes.
+ */
+static int
+nfs_direct_write(struct file *file, struct kiobuf *iobuf,
+ loff_t offset, size_t count)
+{
+ int curpage, total;
+ int need_commit = 0;
+ int result = 0;
+ loff_t save_offset = offset;
+ struct inode *inode = file->f_dentry->d_inode;
+ int wsize = NFS_SERVER(inode)->wsize;
+ struct nfs_writeverf first_verf, ret_verf;
+ struct page *pages[NFS_WRITE_MAXIOV];
+ struct nfs_writeargs args = { NFS_FH(inode), 0, 0, NFS_FILE_SYNC, 0,
+ pages };
+
+#ifdef CONFIG_NFS_V3
+ if ((NFS_PROTO(inode)->version == 3) && (count > wsize) &&
+ (!IS_SYNC(inode)))
+ args.stable = NFS_UNSTABLE;
+#endif
+
+retry:
+ total = 0;
+ curpage = 0;
+ while (count) {
+ int len, request;
+ struct page **dest = pages;
+
+ request = count;
+ if (count > wsize)
+ request = wsize;
+ args.count = request;
+ args.offset = offset;
+ args.pgbase = (iobuf->offset + total) & ~PAGE_MASK;
+ len = PAGE_SIZE - args.pgbase;
+
+ do {
+ struct page *page = iobuf->maplist[curpage];
+
+ if (curpage >= iobuf->nr_pages || !page) {
+ result = -EFAULT;
+ goto out_err;
+ }
+
+ *dest++ = page;
+ /* zero after the first iov */
+ if (request < len)
+ break;
+ request -= len;
+ len = PAGE_SIZE;
+ curpage++;
+ } while (request != 0);
+
+ result = nfs_direct_write_rpc(file, &args, &ret_verf);
+
+ if (result < 0)
+ break;
+
+ if (!total)
+ memcpy(&first_verf.verifier, &ret_verf.verifier,
+ VERF_SIZE);
+ if (ret_verf.committed != NFS_FILE_SYNC) {
+ need_commit = 1;
+ if (memcmp(&first_verf.verifier, &ret_verf.verifier,
+ VERF_SIZE))
+ goto print_retry;
+ }
+
+ total += result;
+ count -= result;
+ offset += result;
+ };
+
+out_err:
+ /*
+ * Commit data written so far, even in the event of an error
+ */
+ if (need_commit) {
+ if (nfs_direct_commit_rpc(inode, save_offset,
+ iobuf->length - count, &ret_verf))
+ goto print_retry;
+ if (memcmp(&first_verf.verifier, &ret_verf.verifier,
+ VERF_SIZE))
+ goto print_retry;
+ }
+
+ if (!total)
+ return result;
+ return total;
+
+print_retry:
+ printk(KERN_INFO "%s: detected server restart; retrying with FILE_SYNC\n",
+ __FUNCTION__);
+ args.stable = NFS_FILE_SYNC;
+ offset = save_offset;
+ count = iobuf->length;
+ goto retry;
+}
+
+/*
+ * Read or write data, moving the data directly to/from the
+ * application's buffer without caching in the page cache.
+ *
+ * Rules for direct I/O
+ *
+ * 1. block size = 512 bytes or more
+ * 2. file byte offset is block aligned
+ * 3. byte count is a multiple of block size
+ * 4. user buffer is not aligned
+ * 5. user buffer is faulted in and pinned
+ *
+ * These are verified before we get here.
+ */
+int
+nfs_direct_IO(int rw, struct file *file, struct kiobuf *iobuf,
+ unsigned long blocknr, int blocksize)
+{
+ int result = -EINVAL;
+ size_t count = iobuf->length;
+ struct dentry *dentry = file->f_dentry;
+ struct inode *inode = dentry->d_inode;
+ loff_t offset = (loff_t) blocknr << inode->i_blkbits;
+
+ switch (rw) {
+ case READ:
+ dfprintk(VFS,
+ "NFS: direct_IO(READ) (%s/%s) off/cnt(%Lu/%d)\n",
+ dentry->d_parent->d_name.name,
+ dentry->d_name.name, offset, count);
+
+ result = nfs_direct_read(file, iobuf, offset, count);
+ break;
+ case WRITE:
+ dfprintk(VFS,
+ "NFS: direct_IO(WRITE) (%s/%s) off/cnt(%Lu/%d)\n",
+ dentry->d_parent->d_name.name,
+ dentry->d_name.name, offset, count);
+
+ result = nfs_direct_write(file, iobuf, offset, count);
+ break;
+ default:
+ break;
+ }
+
+ dfprintk(VFS, "NFS: direct_IO result = %d\n", result);
+ return result;
+}
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
new file mode 100644
index 00000000000000..5c85bb9114497a
--- /dev/null
+++ b/fs/nfs/file.c
@@ -0,0 +1,373 @@
+/*
+ * linux/fs/nfs/file.c
+ *
+ * Copyright (C) 1992 Rick Sladkey
+ *
+ * Changes Copyright (C) 1994 by Florian La Roche
+ * - Do not copy data too often around in the kernel.
+ * - In nfs_file_read the return value of kmalloc wasn't checked.
+ * - Put in a better version of read look-ahead buffering. Original idea
+ * and implementation by Wai S Kok elekokws@ee.nus.sg.
+ *
+ * Expire cache on write to a file by Wai S Kok (Oct 1994).
+ *
+ * Total rewrite of read side for new NFS buffer cache.. Linus.
+ *
+ * nfs regular file handling functions
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/nfs_fs.h>
+#include <linux/nfs_mount.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/lockd/bind.h>
+#include <linux/smp_lock.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#define NFSDBG_FACILITY NFSDBG_FILE
+
+static int nfs_file_mmap(struct file *, struct vm_area_struct *);
+static ssize_t nfs_file_read(struct file *, char *, size_t, loff_t *);
+static ssize_t nfs_file_write(struct file *, const char *, size_t, loff_t *);
+static int nfs_file_flush(struct file *);
+static int nfs_fsync(struct file *, struct dentry *dentry, int datasync);
+
+struct file_operations nfs_file_operations = {
+ llseek: generic_file_llseek,
+ read: nfs_file_read,
+ write: nfs_file_write,
+ mmap: nfs_file_mmap,
+ open: nfs_open,
+ flush: nfs_file_flush,
+ release: nfs_release,
+ fsync: nfs_fsync,
+ lock: nfs_lock,
+};
+
+struct inode_operations nfs_file_inode_operations = {
+ permission: nfs_permission,
+ revalidate: nfs_revalidate,
+ setattr: nfs_notify_change,
+};
+
+/* Hack for future NFS swap support */
+#ifndef IS_SWAPFILE
+# define IS_SWAPFILE(inode) (0)
+#endif
+
+/*
+ * Flush all dirty pages, and check for write errors.
+ *
+ */
+static int
+nfs_file_flush(struct file *file)
+{
+ struct inode *inode = file->f_dentry->d_inode;
+ int status;
+
+ dfprintk(VFS, "nfs: flush(%x/%ld)\n", inode->i_dev, inode->i_ino);
+
+ /* Make sure all async reads have been sent off. We don't bother
+ * waiting on them though... */
+ if (file->f_mode & FMODE_READ)
+ nfs_pagein_inode(inode, 0, 0);
+
+ status = nfs_wb_all(inode);
+ if (!status) {
+ status = file->f_error;
+ file->f_error = 0;
+ }
+ return status;
+}
+
+static ssize_t
+nfs_file_read(struct file * file, char * buf, size_t count, loff_t *ppos)
+{
+ struct dentry * dentry = file->f_dentry;
+ struct inode * inode = dentry->d_inode;
+ ssize_t result;
+
+ dfprintk(VFS, "nfs: read(%s/%s, %lu@%lu)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ (unsigned long) count, (unsigned long) *ppos);
+
+ result = nfs_revalidate_inode(NFS_SERVER(inode), inode);
+ if (!result)
+ result = generic_file_read(file, buf, count, ppos);
+ return result;
+}
+
+static int
+nfs_file_mmap(struct file * file, struct vm_area_struct * vma)
+{
+ struct dentry *dentry = file->f_dentry;
+ struct inode *inode = dentry->d_inode;
+ int status;
+
+ dfprintk(VFS, "nfs: mmap(%s/%s)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
+
+ status = nfs_revalidate_inode(NFS_SERVER(inode), inode);
+ if (!status)
+ status = generic_file_mmap(file, vma);
+ return status;
+}
+
+/*
+ * Flush any dirty pages for this process, and check for write errors.
+ * The return status from this call provides a reliable indication of
+ * whether any write errors occurred for this process.
+ */
+static int
+nfs_fsync(struct file *file, struct dentry *dentry, int datasync)
+{
+ struct inode *inode = dentry->d_inode;
+ int status;
+
+ dfprintk(VFS, "nfs: fsync(%x/%ld)\n", inode->i_dev, inode->i_ino);
+
+ lock_kernel();
+ status = nfs_wb_all(inode);
+ if (!status) {
+ status = file->f_error;
+ file->f_error = 0;
+ }
+ unlock_kernel();
+ return status;
+}
+
+/*
+ * This does the "real" work of the write. The generic routine has
+ * allocated the page, locked it, done all the page alignment stuff
+ * calculations etc. Now we should just copy the data from user
+ * space and write it back to the real medium..
+ *
+ * If the writer ends up delaying the write, the writer needs to
+ * increment the page use counts until he is done with the page.
+ */
+static int nfs_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to)
+{
+ return nfs_flush_incompatible(file, page);
+}
+
+static int nfs_commit_write(struct file *file, struct page *page, unsigned offset, unsigned to)
+{
+ long status;
+
+ lock_kernel();
+ status = nfs_updatepage(file, page, offset, to-offset);
+ unlock_kernel();
+ return status;
+}
+
+/*
+ * The following is used by wait_on_page(), generic_file_readahead()
+ * to initiate the completion of any page readahead operations.
+ */
+static int nfs_sync_page(struct page *page)
+{
+ struct address_space *mapping;
+ struct inode *inode;
+ unsigned long index = page_index(page);
+ unsigned int rpages;
+ int result;
+
+ mapping = page->mapping;
+ if (!mapping)
+ return 0;
+ inode = mapping->host;
+ if (!inode)
+ return 0;
+
+ NFS_SetPageSync(page);
+ rpages = NFS_SERVER(inode)->rpages;
+ result = nfs_pagein_inode(inode, index, rpages);
+ if (result < 0)
+ return result;
+ return 0;
+}
+
+struct address_space_operations nfs_file_aops = {
+ readpage: nfs_readpage,
+ sync_page: nfs_sync_page,
+ writepage: nfs_writepage,
+#ifdef CONFIG_NFS_DIRECTIO
+ direct_fileIO: nfs_direct_IO,
+#endif
+ prepare_write: nfs_prepare_write,
+ commit_write: nfs_commit_write
+};
+
+/*
+ * Write to a file (through the page cache).
+ */
+static ssize_t
+nfs_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
+{
+ struct dentry * dentry = file->f_dentry;
+ struct inode * inode = dentry->d_inode;
+ ssize_t result;
+
+ dfprintk(VFS, "nfs: write(%s/%s(%ld), %lu@%lu)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ inode->i_ino, (unsigned long) count, (unsigned long) *ppos);
+
+ result = -EBUSY;
+ if (IS_SWAPFILE(inode))
+ goto out_swapfile;
+ result = nfs_revalidate_inode(NFS_SERVER(inode), inode);
+ if (result)
+ goto out;
+
+ result = count;
+ if (!count)
+ goto out;
+
+ result = generic_file_write(file, buf, count, ppos);
+out:
+ return result;
+
+out_swapfile:
+ printk(KERN_INFO "NFS: attempt to write to active swap file!\n");
+ goto out;
+}
+
+static int
+do_getlk(struct inode *inode, int cmd, struct file_lock *fl)
+{
+ int status;
+
+ lock_kernel();
+ status = nlmclnt_proc(inode, cmd, fl);
+ unlock_kernel();
+ return status;
+}
+
+static int
+do_unlk(struct inode *inode, int cmd, struct file_lock *fl)
+{
+ sigset_t oldset;
+ int status;
+
+ rpc_clnt_sigmask(NFS_CLIENT(inode), &oldset);
+ /*
+ * Flush all pending writes before doing anything
+ * with locks..
+ */
+ filemap_fdatasync(inode->i_mapping);
+ down(&inode->i_sem);
+ nfs_wb_all(inode);
+ up(&inode->i_sem);
+ filemap_fdatawait(inode->i_mapping);
+
+ /* NOTE: special case
+ * If we're signalled while cleaning up locks on process exit, we
+ * still need to complete the unlock.
+ */
+ lock_kernel();
+ status = nlmclnt_proc(inode, cmd, fl);
+ unlock_kernel();
+ rpc_clnt_sigunmask(NFS_CLIENT(inode), &oldset);
+ return status;
+}
+
+static int
+do_setlk(struct file *filp, int cmd, struct file_lock *fl)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ int status;
+
+ /*
+ * Flush all pending writes before doing anything
+ * with locks..
+ */
+ status = filemap_fdatasync(inode->i_mapping);
+ if (status == 0) {
+ down(&inode->i_sem);
+ status = nfs_wb_all(inode);
+ up(&inode->i_sem);
+ if (status == 0)
+ status = filemap_fdatawait(inode->i_mapping);
+ }
+ if (status < 0)
+ return status;
+
+ lock_kernel();
+ status = nlmclnt_proc(inode, cmd, fl);
+ /* If we were signalled we still need to ensure that
+ * we clean up any state on the server. We therefore
+ * record the lock call as having succeeded in order to
+ * ensure that locks_remove_posix() cleans it out when
+ * the process exits.
+ */
+ if (status == -EINTR || status == -ERESTARTSYS)
+ posix_lock_file(filp, fl, 0);
+ unlock_kernel();
+ if (status < 0)
+ return status;
+
+ /*
+ * Make sure we clear the cache whenever we try to get the lock.
+ * This makes locking act as a cache coherency point.
+ */
+ filemap_fdatasync(inode->i_mapping);
+ down(&inode->i_sem);
+ nfs_wb_all(inode); /* we may have slept */
+ up(&inode->i_sem);
+ filemap_fdatawait(inode->i_mapping);
+ nfs_zap_caches(inode);
+ return 0;
+}
+
+/*
+ * Lock a (portion of) a file
+ */
+int
+nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
+{
+ struct inode * inode = filp->f_dentry->d_inode;
+
+ dprintk("NFS: nfs_lock(f=%4x/%ld, t=%x, fl=%x, r=%Ld:%Ld)\n",
+ inode->i_dev, inode->i_ino,
+ fl->fl_type, fl->fl_flags,
+ (long long)fl->fl_start, (long long)fl->fl_end);
+
+ if (!inode)
+ return -EINVAL;
+
+ /* No mandatory locks over NFS */
+ if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
+ return -ENOLCK;
+
+ /* Fake OK code if mounted without NLM support */
+ if (NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM) {
+ if (IS_GETLK(cmd))
+ return LOCK_USE_CLNT;
+ return 0;
+ }
+
+ /*
+ * No BSD flocks over NFS allowed.
+ * Note: we could try to fake a POSIX lock request here by
+ * using ((u32) filp | 0x80000000) or some such as the pid.
+ * Not sure whether that would be unique, though, or whether
+ * that would break in other places.
+ */
+ if (!fl->fl_owner || (fl->fl_flags & (FL_POSIX|FL_BROKEN)) != FL_POSIX)
+ return -ENOLCK;
+
+ if (IS_GETLK(cmd))
+ return do_getlk(inode, cmd, fl);
+ if (fl->fl_type == F_UNLCK)
+ return do_unlk(inode, cmd, fl);
+ return do_setlk(filp, cmd, fl);
+}
diff --git a/fs/nfs/flushd.c b/fs/nfs/flushd.c
new file mode 100644
index 00000000000000..c218621e40c805
--- /dev/null
+++ b/fs/nfs/flushd.c
@@ -0,0 +1,205 @@
+/*
+ * linux/fs/nfs/flushd.c
+ *
+ * For each NFS mount, there is a separate cache object that contains
+ * a hash table of all clusters. With this cache, an async RPC task
+ * (`flushd') is associated, which wakes up occasionally to inspect
+ * its list of dirty buffers.
+ * (Note that RPC tasks aren't kernel threads. Take a look at the
+ * rpciod code to understand what they are).
+ *
+ * Inside the cache object, we also maintain a count of the current number
+ * of dirty pages, which may not exceed a certain threshold.
+ * (FIXME: This threshold should be configurable).
+ *
+ * The code is streamlined for what I think is the prevalent case for
+ * NFS traffic, which is sequential write access without concurrent
+ * access by different processes.
+ *
+ * Copyright (C) 1996, 1997, Olaf Kirch <okir@monad.swb.de>
+ *
+ * Rewritten 6/3/2000 by Trond Myklebust
+ * Copyright (C) 1999, 2000, Trond Myklebust <trond.myklebust@fys.uio.no>
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/file.h>
+
+#include <linux/sched.h>
+
+#include <linux/sunrpc/auth.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/sched.h>
+
+#include <linux/smp_lock.h>
+
+#include <linux/nfs.h>
+#include <linux/nfs_fs.h>
+#include <linux/nfs_page.h>
+#include <linux/nfs_fs_sb.h>
+#include <linux/nfs_flushd.h>
+
+/*
+ * Various constants
+ */
+#define NFSDBG_FACILITY NFSDBG_PAGECACHE
+
+/*
+ * This is the wait queue all cluster daemons sleep on
+ */
+static RPC_WAITQ(flushd_queue, "nfs_flushd");
+
+/*
+ * Local function declarations.
+ */
+static void nfs_flushd(struct rpc_task *);
+static void nfs_flushd_exit(struct rpc_task *);
+
+
+static int nfs_reqlist_init(struct nfs_server *server)
+{
+ struct nfs_reqlist *cache;
+ struct rpc_task *task;
+ int status;
+
+ dprintk("NFS: writecache_init\n");
+
+ lock_kernel();
+ status = -ENOMEM;
+ /* Create the RPC task */
+ if (!(task = rpc_new_task(server->client, NULL, RPC_TASK_ASYNC)))
+ goto out_unlock;
+
+ cache = server->rw_requests;
+
+ status = 0;
+ if (cache->task)
+ goto out_unlock;
+
+ task->tk_calldata = server;
+
+ cache->task = task;
+
+ /* Run the task */
+ cache->runat = jiffies;
+
+ cache->auth = server->client->cl_auth;
+ task->tk_action = nfs_flushd;
+ task->tk_exit = nfs_flushd_exit;
+
+ rpc_execute(task);
+ unlock_kernel();
+ return 0;
+ out_unlock:
+ if (task)
+ rpc_release_task(task);
+ unlock_kernel();
+ return status;
+}
+
+void nfs_reqlist_exit(struct nfs_server *server)
+{
+ struct nfs_reqlist *cache;
+
+ lock_kernel();
+ cache = server->rw_requests;
+ if (!cache)
+ goto out;
+
+ dprintk("NFS: reqlist_exit (ptr %p rpc %p)\n", cache, cache->task);
+
+ while (cache->task) {
+ rpc_exit(cache->task, 0);
+ rpc_wake_up_task(cache->task);
+
+ interruptible_sleep_on_timeout(&cache->request_wait, 1 * HZ);
+ }
+ out:
+ unlock_kernel();
+}
+
+int nfs_reqlist_alloc(struct nfs_server *server)
+{
+ struct nfs_reqlist *cache;
+ if (server->rw_requests)
+ return 0;
+
+ cache = (struct nfs_reqlist *)kmalloc(sizeof(*cache), GFP_KERNEL);
+ if (!cache)
+ return -ENOMEM;
+
+ memset(cache, 0, sizeof(*cache));
+ atomic_set(&cache->nr_requests, 0);
+ init_waitqueue_head(&cache->request_wait);
+ server->rw_requests = cache;
+
+ return nfs_reqlist_init(server);
+}
+
+void nfs_reqlist_free(struct nfs_server *server)
+{
+ if (server->rw_requests) {
+ kfree(server->rw_requests);
+ server->rw_requests = NULL;
+ }
+}
+
+#define NFS_FLUSHD_TIMEOUT (30*HZ)
+static void
+nfs_flushd(struct rpc_task *task)
+{
+ struct nfs_server *server;
+ struct nfs_reqlist *cache;
+ LIST_HEAD(head);
+
+ dprintk("NFS: %4d flushd starting\n", task->tk_pid);
+ server = (struct nfs_server *) task->tk_calldata;
+ cache = server->rw_requests;
+
+ for(;;) {
+ spin_lock(&nfs_wreq_lock);
+ if (nfs_scan_lru_dirty_timeout(server, &head)) {
+ spin_unlock(&nfs_wreq_lock);
+ nfs_flush_list(&head, server->wpages, FLUSH_AGING);
+ continue;
+ }
+ if (nfs_scan_lru_read_timeout(server, &head)) {
+ spin_unlock(&nfs_wreq_lock);
+ nfs_pagein_list(&head, server->rpages);
+ continue;
+ }
+#ifdef CONFIG_NFS_V3
+ if (nfs_scan_lru_commit_timeout(server, &head)) {
+ spin_unlock(&nfs_wreq_lock);
+ nfs_commit_list(&head, FLUSH_AGING);
+ continue;
+ }
+#endif
+ spin_unlock(&nfs_wreq_lock);
+ break;
+ }
+
+ dprintk("NFS: %4d flushd back to sleep\n", task->tk_pid);
+ if (task->tk_action) {
+ task->tk_timeout = NFS_FLUSHD_TIMEOUT;
+ cache->runat = jiffies + task->tk_timeout;
+ rpc_sleep_on(&flushd_queue, task, NULL, NULL);
+ }
+}
+
+static void
+nfs_flushd_exit(struct rpc_task *task)
+{
+ struct nfs_server *server;
+ struct nfs_reqlist *cache;
+ server = (struct nfs_server *) task->tk_calldata;
+ cache = server->rw_requests;
+
+ if (cache->task == task)
+ cache->task = NULL;
+ wake_up(&cache->request_wait);
+}
+
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
new file mode 100644
index 00000000000000..67f9d9cf35c60f
--- /dev/null
+++ b/fs/nfs/inode.c
@@ -0,0 +1,1179 @@
+/*
+ * linux/fs/nfs/inode.c
+ *
+ * Copyright (C) 1992 Rick Sladkey
+ *
+ * nfs inode and superblock handling functions
+ *
+ * Modularised by Alan Cox <Alan.Cox@linux.org>, while hacking some
+ * experimental NFS changes. Modularisation taken straight from SYS5 fs.
+ *
+ * Change to nfs_read_super() to permit NFS mounts to multi-homed hosts.
+ * J.S.Peatfield@damtp.cam.ac.uk
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <linux/unistd.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/stats.h>
+#include <linux/nfs_fs.h>
+#include <linux/nfs_mount.h>
+#include <linux/nfs_flushd.h>
+#include <linux/lockd/bind.h>
+#include <linux/smp_lock.h>
+#include <linux/seq_file.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#define CONFIG_NFS_SNAPSHOT 1
+#define NFSDBG_FACILITY NFSDBG_VFS
+#define NFS_PARANOIA 1
+
+static struct inode * __nfs_fhget(struct super_block *, struct nfs_fh *, struct nfs_fattr *);
+void nfs_zap_caches(struct inode *);
+static void nfs_invalidate_inode(struct inode *);
+
+static void nfs_read_inode(struct inode *);
+static void nfs_write_inode(struct inode *,int);
+static void nfs_delete_inode(struct inode *);
+static void nfs_put_super(struct super_block *);
+static void nfs_clear_inode(struct inode *);
+static void nfs_umount_begin(struct super_block *);
+static int nfs_statfs(struct super_block *, struct statfs *);
+static int nfs_show_options(struct seq_file *, struct vfsmount *);
+
+static struct super_operations nfs_sops = {
+ read_inode: nfs_read_inode,
+ write_inode: nfs_write_inode,
+ delete_inode: nfs_delete_inode,
+ put_super: nfs_put_super,
+ statfs: nfs_statfs,
+ clear_inode: nfs_clear_inode,
+ umount_begin: nfs_umount_begin,
+ show_options: nfs_show_options,
+};
+
+/*
+ * RPC cruft for NFS
+ */
+struct rpc_stat nfs_rpcstat = { &nfs_program };
+static struct rpc_version * nfs_version[] = {
+ NULL,
+ NULL,
+ &nfs_version2,
+#ifdef CONFIG_NFS_V3
+ &nfs_version3,
+#endif
+};
+
+struct rpc_program nfs_program = {
+ "nfs",
+ NFS_PROGRAM,
+ sizeof(nfs_version) / sizeof(nfs_version[0]),
+ nfs_version,
+ &nfs_rpcstat,
+};
+
+static inline unsigned long
+nfs_fattr_to_ino_t(struct nfs_fattr *fattr)
+{
+ return nfs_fileid_to_ino_t(fattr->fileid);
+}
+
+/*
+ * The "read_inode" function doesn't actually do anything:
+ * the real data is filled in later in nfs_fhget. Here we
+ * just mark the cache times invalid, and zero out i_mode
+ * (the latter makes "nfs_refresh_inode" do the right thing
+ * wrt pipe inodes)
+ */
+static void
+nfs_read_inode(struct inode * inode)
+{
+ inode->i_blksize = inode->i_sb->s_blocksize;
+ inode->i_mode = 0;
+ inode->i_rdev = 0;
+ /* We can't support UPDATE_ATIME(), since the server will reset it */
+ inode->i_flags |= S_NOATIME;
+ INIT_LIST_HEAD(&inode->u.nfs_i.read);
+ INIT_LIST_HEAD(&inode->u.nfs_i.dirty);
+ INIT_LIST_HEAD(&inode->u.nfs_i.commit);
+ INIT_LIST_HEAD(&inode->u.nfs_i.writeback);
+ NFS_CACHEINV(inode);
+ NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
+ NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
+}
+
+static void
+nfs_write_inode(struct inode *inode, int sync)
+{
+ int flags = sync ? FLUSH_WAIT : 0;
+
+ nfs_sync_file(inode, 0, 0, flags);
+}
+
+static void
+nfs_delete_inode(struct inode * inode)
+{
+ dprintk("NFS: delete_inode(%x/%ld)\n", inode->i_dev, inode->i_ino);
+
+ /*
+ * The following can never actually happen...
+ */
+ if (nfs_have_writebacks(inode) || nfs_have_read(inode)) {
+ printk(KERN_ERR "nfs_delete_inode: inode %ld has pending RPC requests\n", inode->i_ino);
+ }
+
+ clear_inode(inode);
+}
+
+/*
+ * For the moment, the only task for the NFS clear_inode method is to
+ * release the mmap credential
+ */
+static void
+nfs_clear_inode(struct inode *inode)
+{
+ struct rpc_cred *cred = NFS_I(inode)->mm_cred;
+
+ if (cred)
+ put_rpccred(cred);
+}
+
+void
+nfs_put_super(struct super_block *sb)
+{
+ struct nfs_server *server = &sb->u.nfs_sb.s_server;
+ struct rpc_clnt *rpc;
+
+ /*
+ * First get rid of the request flushing daemon.
+ * Relies on rpc_shutdown_client() waiting on all
+ * client tasks to finish.
+ */
+ nfs_reqlist_exit(server);
+
+ if ((rpc = server->client) != NULL)
+ rpc_shutdown_client(rpc);
+
+ nfs_reqlist_free(server);
+
+ if (!(server->flags & NFS_MOUNT_NONLM))
+ lockd_down(); /* release rpc.lockd */
+ rpciod_down(); /* release rpciod */
+
+ kfree(server->hostname);
+}
+
+void
+nfs_umount_begin(struct super_block *sb)
+{
+ struct nfs_server *server = &sb->u.nfs_sb.s_server;
+ struct rpc_clnt *rpc;
+
+ /* -EIO all pending I/O */
+ if ((rpc = server->client) != NULL)
+ rpc_killall_tasks(rpc);
+}
+
+
+static inline unsigned long
+nfs_block_bits(unsigned long bsize, unsigned char *nrbitsp)
+{
+ /* make sure blocksize is a power of two */
+ if ((bsize & (bsize - 1)) || nrbitsp) {
+ unsigned char nrbits;
+
+ for (nrbits = 31; nrbits && !(bsize & (1 << nrbits)); nrbits--)
+ ;
+ bsize = 1 << nrbits;
+ if (nrbitsp)
+ *nrbitsp = nrbits;
+ }
+
+ return bsize;
+}
+
+/*
+ * Calculate the number of 512byte blocks used.
+ */
+static inline unsigned long
+nfs_calc_block_size(u64 tsize)
+{
+ loff_t used = (tsize + 511) >> 9;
+ return (used > ULONG_MAX) ? ULONG_MAX : used;
+}
+
+/*
+ * Compute and set NFS server blocksize
+ */
+static inline unsigned long
+nfs_block_size(unsigned long bsize, unsigned char *nrbitsp)
+{
+ if (bsize < 1024)
+ bsize = NFS_DEF_FILE_IO_BUFFER_SIZE;
+ else if (bsize >= NFS_MAX_FILE_IO_BUFFER_SIZE)
+ bsize = NFS_MAX_FILE_IO_BUFFER_SIZE;
+
+ return nfs_block_bits(bsize, nrbitsp);
+}
+
+/*
+ * Obtain the root inode of the file system.
+ */
+static struct inode *
+nfs_get_root(struct super_block *sb, struct nfs_fh *rootfh)
+{
+ struct nfs_server *server = &sb->u.nfs_sb.s_server;
+ struct nfs_fattr fattr;
+ struct inode *inode;
+ int error;
+
+ if ((error = server->rpc_ops->getroot(server, rootfh, &fattr)) < 0) {
+ printk(KERN_NOTICE "nfs_get_root: getattr error = %d\n", -error);
+ return NULL;
+ }
+
+ inode = __nfs_fhget(sb, rootfh, &fattr);
+ return inode;
+}
+
+/*
+ * The way this works is that the mount process passes a structure
+ * in the data argument which contains the server's IP address
+ * and the root file handle obtained from the server's mount
+ * daemon. We stash these away in the private superblock fields.
+ */
+struct super_block *
+nfs_read_super(struct super_block *sb, void *raw_data, int silent)
+{
+ struct nfs_mount_data *data = (struct nfs_mount_data *) raw_data;
+ struct nfs_server *server;
+ struct rpc_xprt *xprt = NULL;
+ struct rpc_clnt *clnt = NULL;
+ struct nfs_fh *root = &data->root, fh;
+ struct inode *root_inode = NULL;
+ unsigned int authflavor;
+ struct sockaddr_in srvaddr;
+ struct rpc_timeout timeparms;
+ struct nfs_fsinfo fsinfo;
+ int tcp, version, maxlen;
+
+ memset(&sb->u.nfs_sb, 0, sizeof(sb->u.nfs_sb));
+ if (!data)
+ goto out_miss_args;
+
+ memset(&fh, 0, sizeof(fh));
+ if (data->version != NFS_MOUNT_VERSION) {
+ printk("nfs warning: mount version %s than kernel\n",
+ data->version < NFS_MOUNT_VERSION ? "older" : "newer");
+ if (data->version < 2)
+ data->namlen = 0;
+ if (data->version < 3)
+ data->bsize = 0;
+ if (data->version < 4) {
+ data->flags &= ~NFS_MOUNT_VER3;
+ root = &fh;
+ root->size = NFS2_FHSIZE;
+ memcpy(root->data, data->old_root.data, NFS2_FHSIZE);
+ }
+ }
+
+ /* We now require that the mount process passes the remote address */
+ memcpy(&srvaddr, &data->addr, sizeof(srvaddr));
+ if (srvaddr.sin_addr.s_addr == INADDR_ANY)
+ goto out_no_remote;
+
+ sb->s_magic = NFS_SUPER_MAGIC;
+ sb->s_op = &nfs_sops;
+ sb->s_blocksize_bits = 0;
+ sb->s_blocksize = nfs_block_size(data->bsize, &sb->s_blocksize_bits);
+ server = &sb->u.nfs_sb.s_server;
+ server->rsize = nfs_block_size(data->rsize, NULL);
+ server->wsize = nfs_block_size(data->wsize, NULL);
+ server->flags = data->flags & NFS_MOUNT_FLAGMASK;
+
+ if (data->flags & NFS_MOUNT_NOAC) {
+ data->acregmin = data->acregmax = 0;
+ data->acdirmin = data->acdirmax = 0;
+ sb->s_flags |= MS_SYNCHRONOUS;
+ }
+ server->acregmin = data->acregmin*HZ;
+ server->acregmax = data->acregmax*HZ;
+ server->acdirmin = data->acdirmin*HZ;
+ server->acdirmax = data->acdirmax*HZ;
+
+ server->namelen = data->namlen;
+ server->hostname = kmalloc(strlen(data->hostname) + 1, GFP_KERNEL);
+ if (!server->hostname)
+ goto out_unlock;
+ strcpy(server->hostname, data->hostname);
+ INIT_LIST_HEAD(&server->lru_read);
+ INIT_LIST_HEAD(&server->lru_dirty);
+ INIT_LIST_HEAD(&server->lru_commit);
+ INIT_LIST_HEAD(&server->lru_busy);
+
+ nfsv3_try_again:
+ /* Check NFS protocol revision and initialize RPC op vector
+ * and file handle pool. */
+ if (data->flags & NFS_MOUNT_VER3) {
+#ifdef CONFIG_NFS_V3
+ server->rpc_ops = &nfs_v3_clientops;
+ version = 3;
+ if (data->version < 4) {
+ printk(KERN_NOTICE "NFS: NFSv3 not supported by mount program.\n");
+ goto out_unlock;
+ }
+#else
+ printk(KERN_NOTICE "NFS: NFSv3 not supported.\n");
+ goto out_unlock;
+#endif
+ } else {
+ server->rpc_ops = &nfs_v2_clientops;
+ version = 2;
+ }
+
+ /* Which protocol do we use? */
+ tcp = (data->flags & NFS_MOUNT_TCP);
+
+ /* Initialize timeout values */
+ timeparms.to_initval = data->timeo * HZ / 10;
+ timeparms.to_retries = data->retrans;
+ timeparms.to_maxval = tcp? RPC_MAX_TCP_TIMEOUT : RPC_MAX_UDP_TIMEOUT;
+ timeparms.to_exponential = 1;
+
+ if (!timeparms.to_initval)
+ timeparms.to_initval = (tcp ? 600 : 11) * HZ / 10;
+ if (!timeparms.to_retries)
+ timeparms.to_retries = 5;
+
+ /* Now create transport and client */
+ xprt = xprt_create_proto(tcp? IPPROTO_TCP : IPPROTO_UDP,
+ &srvaddr, &timeparms);
+ if (xprt == NULL)
+ goto out_no_xprt;
+
+ /* Choose authentication flavor */
+ authflavor = RPC_AUTH_UNIX;
+ if (data->flags & NFS_MOUNT_SECURE)
+ authflavor = RPC_AUTH_DES;
+ else if (data->flags & NFS_MOUNT_KERBEROS)
+ authflavor = RPC_AUTH_KRB;
+
+ clnt = rpc_create_client(xprt, server->hostname, &nfs_program,
+ version, authflavor);
+ if (clnt == NULL)
+ goto out_no_client;
+
+ clnt->cl_intr = (data->flags & NFS_MOUNT_INTR)? 1 : 0;
+ clnt->cl_softrtry = (data->flags & NFS_MOUNT_SOFT)? 1 : 0;
+ clnt->cl_droppriv = (data->flags & NFS_MOUNT_BROKEN_SUID) ? 1 : 0;
+ clnt->cl_chatty = 1;
+ server->client = clnt;
+
+ /* Fire up rpciod if not yet running */
+ if (rpciod_up() != 0)
+ goto out_no_iod;
+
+ /*
+ * Keep the super block locked while we try to get
+ * the root fh attributes.
+ */
+ /* Did getting the root inode fail? */
+ if (!(root_inode = nfs_get_root(sb, root))
+ && (data->flags & NFS_MOUNT_VER3)) {
+ data->flags &= ~NFS_MOUNT_VER3;
+ rpciod_down();
+ rpc_shutdown_client(server->client);
+ goto nfsv3_try_again;
+ }
+
+ if (!root_inode)
+ goto out_no_root;
+ sb->s_root = d_alloc_root(root_inode);
+ if (!sb->s_root)
+ goto out_no_root;
+
+ sb->s_root->d_op = &nfs_dentry_operations;
+
+ /* Get some general file system info */
+ if (server->rpc_ops->statfs(server, root, &fsinfo) >= 0) {
+ if (server->namelen == 0)
+ server->namelen = fsinfo.namelen;
+ } else {
+ printk(KERN_NOTICE "NFS: cannot retrieve file system info.\n");
+ goto out_no_root;
+ }
+
+ /* Work out a lot of parameters */
+ if (data->rsize == 0)
+ server->rsize = nfs_block_size(fsinfo.rtpref, NULL);
+ if (data->wsize == 0)
+ server->wsize = nfs_block_size(fsinfo.wtpref, NULL);
+ /* NFSv3: we don't have bsize, but rather rtmult and wtmult... */
+ if (!fsinfo.bsize)
+ fsinfo.bsize = (fsinfo.rtmult>fsinfo.wtmult) ? fsinfo.rtmult : fsinfo.wtmult;
+ /* Also make sure we don't go below rsize/wsize since
+ * RPC calls are expensive */
+ if (fsinfo.bsize < server->rsize)
+ fsinfo.bsize = server->rsize;
+ if (fsinfo.bsize < server->wsize)
+ fsinfo.bsize = server->wsize;
+
+ if (data->bsize == 0)
+ sb->s_blocksize = nfs_block_bits(fsinfo.bsize, &sb->s_blocksize_bits);
+ if (server->rsize > fsinfo.rtmax)
+ server->rsize = fsinfo.rtmax;
+ if (server->wsize > fsinfo.wtmax)
+ server->wsize = fsinfo.wtmax;
+
+ server->rpages = (server->rsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+ if (server->rpages > NFS_READ_MAXIOV) {
+ server->rpages = NFS_READ_MAXIOV;
+ server->rsize = server->rpages << PAGE_CACHE_SHIFT;
+ }
+
+ server->wpages = (server->wsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+ if (server->wpages > NFS_WRITE_MAXIOV) {
+ server->wpages = NFS_WRITE_MAXIOV;
+ server->wsize = server->wpages << PAGE_CACHE_SHIFT;
+ }
+
+ server->dtsize = nfs_block_size(fsinfo.dtpref, NULL);
+ if (server->dtsize > PAGE_CACHE_SIZE)
+ server->dtsize = PAGE_CACHE_SIZE;
+ if (server->dtsize > server->rsize)
+ server->dtsize = server->rsize;
+
+ maxlen = (version == 2) ? NFS2_MAXNAMLEN : NFS3_MAXNAMLEN;
+
+ if (server->namelen == 0 || server->namelen > maxlen)
+ server->namelen = maxlen;
+
+ sb->s_maxbytes = fsinfo.maxfilesize;
+ if (sb->s_maxbytes > MAX_LFS_FILESIZE)
+ sb->s_maxbytes = MAX_LFS_FILESIZE;
+
+ /* Fire up the writeback cache */
+ if (nfs_reqlist_alloc(server) < 0) {
+ printk(KERN_NOTICE "NFS: cannot initialize writeback cache.\n");
+ goto failure_kill_reqlist;
+ }
+
+ /* We're airborne Set socket buffersize */
+ rpc_setbufsize(clnt, server->wsize + 100, server->rsize + 100);
+
+ /* Check whether to start the lockd process */
+ if (!(server->flags & NFS_MOUNT_NONLM))
+ lockd_up();
+ return sb;
+
+ /* Yargs. It didn't work out. */
+ failure_kill_reqlist:
+ nfs_reqlist_exit(server);
+out_no_root:
+ printk("nfs_read_super: get root inode failed\n");
+ iput(root_inode);
+ rpciod_down();
+ goto out_shutdown;
+
+out_no_iod:
+ printk(KERN_WARNING "NFS: couldn't start rpciod!\n");
+out_shutdown:
+ rpc_shutdown_client(server->client);
+ goto out_free_host;
+
+out_no_client:
+ printk(KERN_WARNING "NFS: cannot create RPC client.\n");
+ xprt_destroy(xprt);
+ goto out_free_host;
+
+out_no_xprt:
+ printk(KERN_WARNING "NFS: cannot create RPC transport.\n");
+
+out_free_host:
+ nfs_reqlist_free(server);
+ kfree(server->hostname);
+out_unlock:
+ goto out_fail;
+
+out_no_remote:
+ printk("NFS: mount program didn't pass remote address!\n");
+ goto out_fail;
+
+out_miss_args:
+ printk("nfs_read_super: missing data argument\n");
+
+out_fail:
+ return NULL;
+}
+
+static int
+nfs_statfs(struct super_block *sb, struct statfs *buf)
+{
+ struct nfs_server *server = &sb->u.nfs_sb.s_server;
+ unsigned char blockbits;
+ unsigned long blockres;
+ struct nfs_fsinfo res;
+ int error;
+
+ error = server->rpc_ops->statfs(server, NFS_FH(sb->s_root->d_inode), &res);
+ buf->f_type = NFS_SUPER_MAGIC;
+ if (error < 0)
+ goto out_err;
+
+ if (res.bsize == 0)
+ res.bsize = sb->s_blocksize;
+ buf->f_bsize = nfs_block_bits(res.bsize, &blockbits);
+ blockres = (1 << blockbits) - 1;
+ buf->f_blocks = (res.tbytes + blockres) >> blockbits;
+ buf->f_bfree = (res.fbytes + blockres) >> blockbits;
+ buf->f_bavail = (res.abytes + blockres) >> blockbits;
+ buf->f_files = res.tfiles;
+ buf->f_ffree = res.afiles;
+ if (res.namelen == 0 || res.namelen > server->namelen)
+ res.namelen = server->namelen;
+ buf->f_namelen = res.namelen;
+ return 0;
+ out_err:
+ printk("nfs_statfs: statfs error = %d\n", -error);
+ buf->f_bsize = buf->f_blocks = buf->f_bfree = buf->f_bavail = -1;
+ return 0;
+}
+
+static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt)
+{
+ static struct proc_nfs_info {
+ int flag;
+ char *str;
+ char *nostr;
+ } nfs_info[] = {
+ { NFS_MOUNT_SOFT, ",soft", ",hard" },
+ { NFS_MOUNT_INTR, ",intr", "" },
+ { NFS_MOUNT_POSIX, ",posix", "" },
+ { NFS_MOUNT_TCP, ",tcp", ",udp" },
+ { NFS_MOUNT_NOCTO, ",nocto", "" },
+ { NFS_MOUNT_NOAC, ",noac", "" },
+ { NFS_MOUNT_NONLM, ",nolock", ",lock" },
+ { NFS_MOUNT_BROKEN_SUID, ",broken_suid", "" },
+ { 0, NULL, NULL }
+ };
+ struct proc_nfs_info *nfs_infop;
+ struct nfs_server *nfss = &mnt->mnt_sb->u.nfs_sb.s_server;
+
+ seq_printf(m, ",v%d", nfss->rpc_ops->version);
+ seq_printf(m, ",rsize=%d", nfss->rsize);
+ seq_printf(m, ",wsize=%d", nfss->wsize);
+ if (nfss->acregmin != 3*HZ)
+ seq_printf(m, ",acregmin=%d", nfss->acregmin/HZ);
+ if (nfss->acregmax != 60*HZ)
+ seq_printf(m, ",acregmax=%d", nfss->acregmax/HZ);
+ if (nfss->acdirmin != 30*HZ)
+ seq_printf(m, ",acdirmin=%d", nfss->acdirmin/HZ);
+ if (nfss->acdirmax != 60*HZ)
+ seq_printf(m, ",acdirmax=%d", nfss->acdirmax/HZ);
+ for (nfs_infop = nfs_info; nfs_infop->flag; nfs_infop++) {
+ if (nfss->flags & nfs_infop->flag)
+ seq_puts(m, nfs_infop->str);
+ else
+ seq_puts(m, nfs_infop->nostr);
+ }
+ seq_puts(m, ",addr=");
+ seq_escape(m, nfss->hostname, " \t\n\\");
+ return 0;
+}
+
+/*
+ * Invalidate the local caches
+ */
+void
+nfs_zap_caches(struct inode *inode)
+{
+ NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
+ NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
+
+ invalidate_inode_pages(inode);
+
+ memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode)));
+ NFS_CACHEINV(inode);
+}
+
+/*
+ * Invalidate, but do not unhash, the inode
+ */
+static void
+nfs_invalidate_inode(struct inode *inode)
+{
+ umode_t save_mode = inode->i_mode;
+
+ make_bad_inode(inode);
+ inode->i_mode = save_mode;
+ nfs_zap_caches(inode);
+}
+
+/*
+ * Fill in inode information from the fattr.
+ */
+static void
+nfs_fill_inode(struct inode *inode, struct nfs_fh *fh, struct nfs_fattr *fattr)
+{
+ /*
+ * Check whether the mode has been set, as we only want to
+ * do this once. (We don't allow inodes to change types.)
+ */
+ if (inode->i_mode == 0) {
+ NFS_FILEID(inode) = fattr->fileid;
+ inode->i_mode = fattr->mode;
+ /* Why so? Because we want revalidate for devices/FIFOs, and
+ * that's precisely what we have in nfs_file_inode_operations.
+ */
+ inode->i_op = &nfs_file_inode_operations;
+ if (S_ISREG(inode->i_mode)) {
+ inode->i_fop = &nfs_file_operations;
+ inode->i_data.a_ops = &nfs_file_aops;
+ } else if (S_ISDIR(inode->i_mode)) {
+ inode->i_op = &nfs_dir_inode_operations;
+ inode->i_fop = &nfs_dir_operations;
+ } else if (S_ISLNK(inode->i_mode))
+ inode->i_op = &nfs_symlink_inode_operations;
+ else
+ init_special_inode(inode, inode->i_mode, fattr->rdev);
+ memcpy(&inode->u.nfs_i.fh, fh, sizeof(inode->u.nfs_i.fh));
+ }
+ nfs_refresh_inode(inode, fattr);
+}
+
+struct nfs_find_desc {
+ struct nfs_fh *fh;
+ struct nfs_fattr *fattr;
+};
+
+/*
+ * In NFSv3 we can have 64bit inode numbers. In order to support
+ * this, and re-exported directories (also seen in NFSv2)
+ * we are forced to allow 2 different inodes to have the same
+ * i_ino.
+ */
+static int
+nfs_find_actor(struct inode *inode, unsigned long ino, void *opaque)
+{
+ struct nfs_find_desc *desc = (struct nfs_find_desc *)opaque;
+ struct nfs_fh *fh = desc->fh;
+ struct nfs_fattr *fattr = desc->fattr;
+
+ if (NFS_FILEID(inode) != fattr->fileid)
+ return 0;
+ if (memcmp(&inode->u.nfs_i.fh, fh, sizeof(inode->u.nfs_i.fh)) != 0)
+ return 0;
+ if (is_bad_inode(inode))
+ return 0;
+ /* Force an attribute cache update if inode->i_count == 0 */
+ if (!atomic_read(&inode->i_count))
+ NFS_CACHEINV(inode);
+ return 1;
+}
+
+/*
+ * This is our own version of iget that looks up inodes by file handle
+ * instead of inode number. We use this technique instead of using
+ * the vfs read_inode function because there is no way to pass the
+ * file handle or current attributes into the read_inode function.
+ *
+ */
+struct inode *
+nfs_fhget(struct dentry *dentry, struct nfs_fh *fhandle,
+ struct nfs_fattr *fattr)
+{
+ struct super_block *sb = dentry->d_sb;
+
+ dprintk("NFS: nfs_fhget(%s/%s fileid=%Ld)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ (long long)fattr->fileid);
+ return __nfs_fhget(sb, fhandle, fattr);
+}
+
+/*
+ * Look up the inode by super block and fattr->fileid.
+ */
+static struct inode *
+__nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
+{
+ struct nfs_find_desc desc = { fh, fattr };
+ struct inode *inode = NULL;
+ unsigned long ino;
+
+ if ((fattr->valid & NFS_ATTR_FATTR) == 0)
+ goto out_no_inode;
+
+ if (!fattr->nlink) {
+ printk("NFS: Buggy server - nlink == 0!\n");
+ goto out_no_inode;
+ }
+
+ ino = nfs_fattr_to_ino_t(fattr);
+
+ if (!(inode = iget4(sb, ino, nfs_find_actor, &desc)))
+ goto out_no_inode;
+
+ nfs_fill_inode(inode, fh, fattr);
+ dprintk("NFS: __nfs_fhget(%x/%Ld ct=%d)\n",
+ inode->i_dev, (long long)NFS_FILEID(inode),
+ atomic_read(&inode->i_count));
+
+out:
+ return inode;
+
+out_no_inode:
+ printk("__nfs_fhget: iget failed\n");
+ goto out;
+}
+
+int
+nfs_notify_change(struct dentry *dentry, struct iattr *attr)
+{
+ struct inode *inode = dentry->d_inode;
+ struct nfs_fattr fattr;
+ int error;
+
+ /*
+ * Make sure the inode is up-to-date.
+ */
+ error = nfs_revalidate_inode(NFS_SERVER(inode),inode);
+ if (error) {
+#ifdef NFS_PARANOIA
+printk("nfs_notify_change: revalidate failed, error=%d\n", error);
+#endif
+ goto out;
+ }
+
+ if (!S_ISREG(inode->i_mode))
+ attr->ia_valid &= ~ATTR_SIZE;
+
+ filemap_fdatasync(inode->i_mapping);
+ error = nfs_wb_all(inode);
+ filemap_fdatawait(inode->i_mapping);
+ if (error)
+ goto out;
+
+ error = NFS_PROTO(inode)->setattr(inode, &fattr, attr);
+ if (error)
+ goto out;
+ /*
+ * If we changed the size or mtime, update the inode
+ * now to avoid invalidating the page cache.
+ */
+ if (attr->ia_valid & ATTR_SIZE) {
+ if (attr->ia_size != fattr.size)
+ printk("nfs_notify_change: attr=%Ld, fattr=%Ld??\n",
+ (long long) attr->ia_size, (long long)fattr.size);
+ vmtruncate(inode, attr->ia_size);
+ }
+
+ /*
+ * If we changed the size or mtime, update the inode
+ * now to avoid invalidating the page cache.
+ */
+ if (!(fattr.valid & NFS_ATTR_WCC)) {
+ fattr.pre_size = NFS_CACHE_ISIZE(inode);
+ fattr.pre_mtime = NFS_CACHE_MTIME(inode);
+ fattr.pre_ctime = NFS_CACHE_CTIME(inode);
+ fattr.valid |= NFS_ATTR_WCC;
+ }
+ /* Force an attribute cache update */
+ NFS_CACHEINV(inode);
+ error = nfs_refresh_inode(inode, &fattr);
+out:
+ return error;
+}
+
+/*
+ * Wait for the inode to get unlocked.
+ * (Used for NFS_INO_LOCKED and NFS_INO_REVALIDATING).
+ */
+int
+nfs_wait_on_inode(struct inode *inode, int flag)
+{
+ struct rpc_clnt *clnt = NFS_CLIENT(inode);
+ int error;
+ if (!(NFS_FLAGS(inode) & flag))
+ return 0;
+ atomic_inc(&inode->i_count);
+ error = nfs_wait_event(clnt, inode->i_wait, !(NFS_FLAGS(inode) & flag));
+ iput(inode);
+ return error;
+}
+
+/*
+ * Externally visible revalidation function
+ */
+int
+nfs_revalidate(struct dentry *dentry)
+{
+ struct inode *inode = dentry->d_inode;
+ return nfs_revalidate_inode(NFS_SERVER(inode), inode);
+}
+
+/*
+ * Ensure that mmap has a recent RPC credential for use when writing out
+ * shared pages
+ */
+static inline void
+nfs_set_mmcred(struct inode *inode, struct rpc_cred *cred)
+{
+ struct rpc_cred **p = &NFS_I(inode)->mm_cred,
+ *oldcred = *p;
+
+ *p = get_rpccred(cred);
+ if (oldcred)
+ put_rpccred(oldcred);
+}
+
+/*
+ * These are probably going to contain hooks for
+ * allocating and releasing RPC credentials for
+ * the file. I'll have to think about Tronds patch
+ * a bit more..
+ */
+int nfs_open(struct inode *inode, struct file *filp)
+{
+ struct rpc_auth *auth;
+ struct rpc_cred *cred;
+
+ lock_kernel();
+ auth = NFS_CLIENT(inode)->cl_auth;
+ cred = rpcauth_lookupcred(auth, 0);
+ filp->private_data = cred;
+ if (filp->f_mode & FMODE_WRITE)
+ nfs_set_mmcred(inode, cred);
+ unlock_kernel();
+ return 0;
+}
+
+int nfs_release(struct inode *inode, struct file *filp)
+{
+ struct rpc_cred *cred;
+
+ lock_kernel();
+ cred = nfs_file_cred(filp);
+ if (cred)
+ put_rpccred(cred);
+ unlock_kernel();
+ return 0;
+}
+
+/*
+ * This function is called whenever some part of NFS notices that
+ * the cached attributes have to be refreshed.
+ */
+int
+__nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
+{
+ int status = -ESTALE;
+ struct nfs_fattr fattr;
+
+ dfprintk(PAGECACHE, "NFS: revalidating (%x/%Ld)\n",
+ inode->i_dev, (long long)NFS_FILEID(inode));
+
+ lock_kernel();
+ if (!inode || is_bad_inode(inode))
+ goto out_nowait;
+ if (NFS_STALE(inode) && inode != inode->i_sb->s_root->d_inode)
+ goto out_nowait;
+
+ while (NFS_REVALIDATING(inode)) {
+ status = nfs_wait_on_inode(inode, NFS_INO_REVALIDATING);
+ if (status < 0)
+ goto out_nowait;
+ if (time_before(jiffies,NFS_READTIME(inode)+NFS_ATTRTIMEO(inode))) {
+ status = NFS_STALE(inode) ? -ESTALE : 0;
+ goto out_nowait;
+ }
+ }
+ NFS_FLAGS(inode) |= NFS_INO_REVALIDATING;
+
+ status = NFS_PROTO(inode)->getattr(inode, &fattr);
+ if (status) {
+ dfprintk(PAGECACHE, "nfs_revalidate_inode: (%x/%Ld) getattr failed, error=%d\n",
+ inode->i_dev, (long long)NFS_FILEID(inode), status);
+ if (status == -ESTALE) {
+ NFS_FLAGS(inode) |= NFS_INO_STALE;
+ if (inode != inode->i_sb->s_root->d_inode)
+ remove_inode_hash(inode);
+ }
+ goto out;
+ }
+
+ status = nfs_refresh_inode(inode, &fattr);
+ if (status) {
+ dfprintk(PAGECACHE, "nfs_revalidate_inode: (%x/%Ld) refresh failed, error=%d\n",
+ inode->i_dev, (long long)NFS_FILEID(inode), status);
+ goto out;
+ }
+ dfprintk(PAGECACHE, "NFS: (%x/%Ld) revalidation complete\n",
+ inode->i_dev, (long long)NFS_FILEID(inode));
+
+ NFS_FLAGS(inode) &= ~NFS_INO_STALE;
+out:
+ NFS_FLAGS(inode) &= ~NFS_INO_REVALIDATING;
+ wake_up(&inode->i_wait);
+ out_nowait:
+ unlock_kernel();
+ return status;
+}
+
+/*
+ * nfs_fattr_obsolete - Test if attribute data is newer than cached data
+ * @inode: inode
+ * @fattr: attributes to test
+ *
+ * Avoid stuffing the attribute cache with obsolete information.
+ * We always accept updates if the attribute cache timed out, or if
+ * fattr->ctime is newer than our cached value.
+ * If fattr->ctime matches the cached value, we still accept the update
+ * if it increases the file size.
+ */
+static inline
+int nfs_fattr_obsolete(struct inode *inode, struct nfs_fattr *fattr)
+{
+ s64 cdif;
+
+ if (time_after(jiffies, NFS_READTIME(inode)+NFS_ATTRTIMEO(inode)))
+ goto out_valid;
+ if ((cdif = (s64)fattr->ctime - (s64)NFS_CACHE_CTIME(inode)) > 0)
+ goto out_valid;
+ /* Ugh... */
+ if (cdif == 0 && fattr->size > NFS_CACHE_ISIZE(inode))
+ goto out_valid;
+ return -1;
+ out_valid:
+ return 0;
+}
+
+/*
+ * Many nfs protocol calls return the new file attributes after
+ * an operation. Here we update the inode to reflect the state
+ * of the server's inode.
+ *
+ * This is a bit tricky because we have to make sure all dirty pages
+ * have been sent off to the server before calling invalidate_inode_pages.
+ * To make sure no other process adds more write requests while we try
+ * our best to flush them, we make them sleep during the attribute refresh.
+ *
+ * A very similar scenario holds for the dir cache.
+ */
+int
+__nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
+{
+ __u64 new_size, new_mtime;
+ loff_t new_isize;
+ time_t new_atime;
+ int invalid = 0;
+
+ dfprintk(VFS, "NFS: refresh_inode(%x/%ld ct=%d info=0x%x)\n",
+ inode->i_dev, inode->i_ino,
+ atomic_read(&inode->i_count), fattr->valid);
+
+ if (NFS_FILEID(inode) != fattr->fileid) {
+ printk(KERN_ERR "nfs_refresh_inode: inode number mismatch\n"
+ "expected (0x%x/0x%Lx), got (0x%x/0x%Lx)\n",
+ inode->i_dev, (long long)NFS_FILEID(inode),
+ inode->i_dev, (long long)fattr->fileid);
+ goto out_err;
+ }
+
+ /*
+ * Make sure the inode's type hasn't changed.
+ */
+ if ((inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT))
+ goto out_changed;
+
+ new_mtime = fattr->mtime;
+ new_size = fattr->size;
+ new_isize = nfs_size_to_loff_t(fattr->size);
+
+ new_atime = nfs_time_to_secs(fattr->atime);
+ /* Avoid races */
+ if (nfs_fattr_obsolete(inode, fattr))
+ goto out_nochange;
+
+ /*
+ * Update the read time so we don't revalidate too often.
+ */
+ NFS_READTIME(inode) = jiffies;
+
+ /*
+ * Note: NFS_CACHE_ISIZE(inode) reflects the state of the cache.
+ * NOT inode->i_size!!!
+ */
+ if (NFS_CACHE_ISIZE(inode) != new_size) {
+#ifdef NFS_DEBUG_VERBOSE
+ printk(KERN_DEBUG "NFS: isize change on %x/%ld\n", inode->i_dev, inode->i_ino);
+#endif
+ invalid = 1;
+ }
+
+ /*
+ * Note: we don't check inode->i_mtime since pipes etc.
+ * can change this value in VFS without requiring a
+ * cache revalidation.
+ */
+ if (NFS_CACHE_MTIME(inode) != new_mtime) {
+#ifdef NFS_DEBUG_VERBOSE
+ printk(KERN_DEBUG "NFS: mtime change on %x/%ld\n", inode->i_dev, inode->i_ino);
+#endif
+ invalid = 1;
+ }
+
+ /* Check Weak Cache Consistency data.
+ * If size and mtime match the pre-operation values, we can
+ * assume that any attribute changes were caused by our NFS
+ * operation, so there's no need to invalidate the caches.
+ */
+ if ((fattr->valid & NFS_ATTR_WCC)
+ && NFS_CACHE_ISIZE(inode) == fattr->pre_size
+ && NFS_CACHE_MTIME(inode) == fattr->pre_mtime) {
+ invalid = 0;
+ }
+
+ /*
+ * If we have pending writebacks, things can get
+ * messy.
+ */
+ if (nfs_have_writebacks(inode) && new_isize < inode->i_size)
+ new_isize = inode->i_size;
+
+ NFS_CACHE_CTIME(inode) = fattr->ctime;
+ inode->i_ctime = nfs_time_to_secs(fattr->ctime);
+
+ inode->i_atime = new_atime;
+
+ if (NFS_CACHE_MTIME(inode) != new_mtime) {
+ NFS_MTIME_UPDATE(inode) = jiffies;
+ NFS_CACHE_MTIME(inode) = new_mtime;
+ inode->i_mtime = nfs_time_to_secs(new_mtime);
+ }
+
+ NFS_CACHE_ISIZE(inode) = new_size;
+ inode->i_size = new_isize;
+
+ inode->i_mode = fattr->mode;
+ inode->i_nlink = fattr->nlink;
+ inode->i_uid = fattr->uid;
+ inode->i_gid = fattr->gid;
+
+ if (fattr->valid & NFS_ATTR_FATTR_V3) {
+ /*
+ * report the blocks in 512byte units
+ */
+ inode->i_blocks = nfs_calc_block_size(fattr->du.nfs3.used);
+ inode->i_blksize = inode->i_sb->s_blocksize;
+ } else {
+ inode->i_blocks = fattr->du.nfs2.blocks;
+ inode->i_blksize = fattr->du.nfs2.blocksize;
+ }
+ inode->i_rdev = 0;
+ if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+ inode->i_rdev = to_kdev_t(fattr->rdev);
+
+ /* Update attrtimeo value */
+ if (invalid) {
+ NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
+ NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
+ invalidate_inode_pages(inode);
+ memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode)));
+ } else if (time_after(jiffies, NFS_ATTRTIMEO_UPDATE(inode)+NFS_ATTRTIMEO(inode))) {
+ if ((NFS_ATTRTIMEO(inode) <<= 1) > NFS_MAXATTRTIMEO(inode))
+ NFS_ATTRTIMEO(inode) = NFS_MAXATTRTIMEO(inode);
+ NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
+ }
+
+ return 0;
+ out_nochange:
+ if (new_atime - inode->i_atime > 0)
+ inode->i_atime = new_atime;
+ return 0;
+ out_changed:
+ /*
+ * Big trouble! The inode has become a different object.
+ */
+#ifdef NFS_PARANOIA
+ printk(KERN_DEBUG "nfs_refresh_inode: inode %ld mode changed, %07o to %07o\n",
+ inode->i_ino, inode->i_mode, fattr->mode);
+#endif
+ /*
+ * No need to worry about unhashing the dentry, as the
+ * lookup validation will know that the inode is bad.
+ * (But we fall through to invalidate the caches.)
+ */
+ nfs_invalidate_inode(inode);
+ out_err:
+ return -EIO;
+}
+
+/*
+ * File system information
+ */
+static DECLARE_FSTYPE(nfs_fs_type, "nfs", nfs_read_super, FS_ODD_RENAME);
+
+extern int nfs_init_nfspagecache(void);
+extern void nfs_destroy_nfspagecache(void);
+extern int nfs_init_readpagecache(void);
+extern int nfs_destroy_readpagecache(void);
+extern int nfs_init_writepagecache(void);
+extern int nfs_destroy_writepagecache(void);
+
+/*
+ * Initialize NFS
+ */
+static int __init init_nfs_fs(void)
+{
+ int err;
+
+ err = nfs_init_nfspagecache();
+ if (err)
+ return err;
+
+ err = nfs_init_readpagecache();
+ if (err)
+ return err;
+
+ err = nfs_init_writepagecache();
+ if (err)
+ return err;
+
+#ifdef CONFIG_PROC_FS
+ rpc_proc_register(&nfs_rpcstat);
+#endif
+ return register_filesystem(&nfs_fs_type);
+}
+
+static void __exit exit_nfs_fs(void)
+{
+ nfs_destroy_writepagecache();
+ nfs_destroy_readpagecache();
+ nfs_destroy_nfspagecache();
+#ifdef CONFIG_PROC_FS
+ rpc_proc_unregister("nfs");
+#endif
+ unregister_filesystem(&nfs_fs_type);
+}
+
+EXPORT_NO_SYMBOLS;
+/* Not quite true; I just maintain it */
+MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
+MODULE_LICENSE("GPL");
+
+module_init(init_nfs_fs)
+module_exit(exit_nfs_fs)
diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c
new file mode 100644
index 00000000000000..955bb1537ec64e
--- /dev/null
+++ b/fs/nfs/mount_clnt.c
@@ -0,0 +1,185 @@
+/*
+ * linux/fs/nfs/mount_clnt.c
+ *
+ * MOUNT client to support NFSroot.
+ *
+ * Copyright (C) 1997, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/uio.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/xprt.h>
+#include <linux/sunrpc/sched.h>
+#include <linux/nfs_fs.h>
+
+#ifdef RPC_DEBUG
+# define NFSDBG_FACILITY NFSDBG_ROOT
+#endif
+
+/*
+#define MOUNT_PROGRAM 100005
+#define MOUNT_VERSION 1
+#define MOUNT_MNT 1
+#define MOUNT_UMNT 3
+ */
+
+static struct rpc_clnt * mnt_create(char *, struct sockaddr_in *,
+ int, int);
+struct rpc_program mnt_program;
+
+struct mnt_fhstatus {
+ unsigned int status;
+ struct nfs_fh * fh;
+};
+
+/*
+ * Obtain an NFS file handle for the given host and path
+ */
+int
+nfsroot_mount(struct sockaddr_in *addr, char *path, struct nfs_fh *fh,
+ int version, int protocol)
+{
+ struct rpc_clnt *mnt_clnt;
+ struct mnt_fhstatus result = { 0, fh };
+ char hostname[32];
+ int status;
+ int call;
+
+ dprintk("NFS: nfs_mount(%08x:%s)\n",
+ (unsigned)ntohl(addr->sin_addr.s_addr), path);
+
+ sprintf(hostname, "%u.%u.%u.%u", NIPQUAD(addr->sin_addr.s_addr));
+ if (!(mnt_clnt = mnt_create(hostname, addr, version, protocol)))
+ return -EACCES;
+
+ call = (version == NFS_MNT3_VERSION)? MOUNTPROC3_MNT : MNTPROC_MNT;
+ status = rpc_call(mnt_clnt, call, path, &result, 0);
+ return status < 0? status : (result.status? -EACCES : 0);
+}
+
+static struct rpc_clnt *
+mnt_create(char *hostname, struct sockaddr_in *srvaddr, int version,
+ int protocol)
+{
+ struct rpc_xprt *xprt;
+ struct rpc_clnt *clnt;
+
+ if (!(xprt = xprt_create_proto(protocol, srvaddr, NULL)))
+ return NULL;
+
+ clnt = rpc_create_client(xprt, hostname,
+ &mnt_program, version,
+ RPC_AUTH_NULL);
+ if (!clnt) {
+ xprt_destroy(xprt);
+ } else {
+ clnt->cl_softrtry = 1;
+ clnt->cl_chatty = 1;
+ clnt->cl_oneshot = 1;
+ clnt->cl_intr = 1;
+ }
+ return clnt;
+}
+
+/*
+ * XDR encode/decode functions for MOUNT
+ */
+static int
+xdr_error(struct rpc_rqst *req, u32 *p, void *dummy)
+{
+ return -EIO;
+}
+
+static int
+xdr_encode_dirpath(struct rpc_rqst *req, u32 *p, const char *path)
+{
+ p = xdr_encode_string(p, path);
+
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+static int
+xdr_decode_fhstatus(struct rpc_rqst *req, u32 *p, struct mnt_fhstatus *res)
+{
+ struct nfs_fh *fh = res->fh;
+
+ memset((void *)fh, 0, sizeof(*fh));
+ if ((res->status = ntohl(*p++)) == 0) {
+ fh->size = NFS2_FHSIZE;
+ memcpy(fh->data, p, NFS2_FHSIZE);
+ }
+ return 0;
+}
+
+static int
+xdr_decode_fhstatus3(struct rpc_rqst *req, u32 *p, struct mnt_fhstatus *res)
+{
+ struct nfs_fh *fh = res->fh;
+
+ memset((void *)fh, 0, sizeof(*fh));
+ if ((res->status = ntohl(*p++)) == 0) {
+ int size = ntohl(*p++);
+ if (size <= NFS3_FHSIZE) {
+ fh->size = size;
+ memcpy(fh->data, p, size);
+ } else
+ res->status = -EBADHANDLE;
+ }
+ return 0;
+}
+
+#define MNT_dirpath_sz (1 + 256)
+#define MNT_fhstatus_sz (1 + 8)
+
+static struct rpc_procinfo mnt_procedures[2] = {
+ { "mnt_null",
+ (kxdrproc_t) xdr_error,
+ (kxdrproc_t) xdr_error, 0, 0 },
+ { "mnt_mount",
+ (kxdrproc_t) xdr_encode_dirpath,
+ (kxdrproc_t) xdr_decode_fhstatus,
+ MNT_dirpath_sz << 2, 0 },
+};
+
+static struct rpc_procinfo mnt3_procedures[2] = {
+ { "mnt3_null",
+ (kxdrproc_t) xdr_error,
+ (kxdrproc_t) xdr_error, 0, 0 },
+ { "mnt3_mount",
+ (kxdrproc_t) xdr_encode_dirpath,
+ (kxdrproc_t) xdr_decode_fhstatus3,
+ MNT_dirpath_sz << 2, 0 },
+};
+
+
+static struct rpc_version mnt_version1 = {
+ 1, 2, mnt_procedures
+};
+
+static struct rpc_version mnt_version3 = {
+ 3, 2, mnt3_procedures
+};
+
+static struct rpc_version * mnt_version[] = {
+ NULL,
+ &mnt_version1,
+ NULL,
+ &mnt_version3,
+};
+
+static struct rpc_stat mnt_stats;
+
+struct rpc_program mnt_program = {
+ "mount",
+ NFS_MNT_PROGRAM,
+ sizeof(mnt_version)/sizeof(mnt_version[0]),
+ mnt_version,
+ &mnt_stats,
+};
diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c
new file mode 100644
index 00000000000000..19f2f2885538b6
--- /dev/null
+++ b/fs/nfs/nfs2xdr.c
@@ -0,0 +1,729 @@
+/*
+ * linux/fs/nfs/nfs2xdr.c
+ *
+ * XDR functions to encode/decode NFS RPC arguments and results.
+ *
+ * Copyright (C) 1992, 1993, 1994 Rick Sladkey
+ * Copyright (C) 1996 Olaf Kirch
+ * 04 Aug 1998 Ion Badulescu <ionut@cs.columbia.edu>
+ * FIFO's need special handling in NFSv2
+ */
+
+#include <linux/param.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/utsname.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/in.h>
+#include <linux/pagemap.h>
+#include <linux/proc_fs.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/nfs.h>
+#include <linux/nfs2.h>
+#include <linux/nfs_fs.h>
+
+#define NFSDBG_FACILITY NFSDBG_XDR
+/* #define NFS_PARANOIA 1 */
+
+extern int nfs_stat_to_errno(int stat);
+
+/* Mapping from NFS error code to "errno" error code. */
+#define errno_NFSERR_IO EIO
+
+/*
+ * Declare the space requirements for NFS arguments and replies as
+ * number of 32bit-words
+ */
+#define NFS_fhandle_sz 8
+#define NFS_sattr_sz 8
+#define NFS_filename_sz 1+(NFS2_MAXNAMLEN>>2)
+#define NFS_path_sz 1+(NFS2_MAXPATHLEN>>2)
+#define NFS_fattr_sz 17
+#define NFS_info_sz 5
+#define NFS_entry_sz NFS_filename_sz+3
+
+#define NFS_enc_void_sz 0
+#define NFS_diropargs_sz NFS_fhandle_sz+NFS_filename_sz
+#define NFS_sattrargs_sz NFS_fhandle_sz+NFS_sattr_sz
+#define NFS_readlinkargs_sz NFS_fhandle_sz
+#define NFS_readargs_sz NFS_fhandle_sz+3
+#define NFS_writeargs_sz NFS_fhandle_sz+4
+#define NFS_createargs_sz NFS_diropargs_sz+NFS_sattr_sz
+#define NFS_renameargs_sz NFS_diropargs_sz+NFS_diropargs_sz
+#define NFS_linkargs_sz NFS_fhandle_sz+NFS_diropargs_sz
+#define NFS_symlinkargs_sz NFS_diropargs_sz+NFS_path_sz+NFS_sattr_sz
+#define NFS_readdirargs_sz NFS_fhandle_sz+2
+
+#define NFS_dec_void_sz 0
+#define NFS_attrstat_sz 1+NFS_fattr_sz
+#define NFS_diropres_sz 1+NFS_fhandle_sz+NFS_fattr_sz
+#define NFS_readlinkres_sz 1
+#define NFS_readres_sz 1+NFS_fattr_sz+1
+#define NFS_writeres_sz NFS_attrstat_sz
+#define NFS_stat_sz 1
+#define NFS_readdirres_sz 1
+#define NFS_statfsres_sz 1+NFS_info_sz
+
+/*
+ * Common NFS XDR functions as inlines
+ */
+static inline u32 *
+xdr_encode_fhandle(u32 *p, struct nfs_fh *fhandle)
+{
+ memcpy(p, fhandle->data, NFS2_FHSIZE);
+ return p + XDR_QUADLEN(NFS2_FHSIZE);
+}
+
+static inline u32 *
+xdr_decode_fhandle(u32 *p, struct nfs_fh *fhandle)
+{
+ /* Zero handle first to allow comparisons */
+ memset(fhandle, 0, sizeof(*fhandle));
+ /* NFSv2 handles have a fixed length */
+ fhandle->size = NFS2_FHSIZE;
+ memcpy(fhandle->data, p, NFS2_FHSIZE);
+ return p + XDR_QUADLEN(NFS2_FHSIZE);
+}
+
+static inline u32*
+xdr_decode_time(u32 *p, u64 *timep)
+{
+ u64 tmp = (u64)ntohl(*p++) << 32;
+ *timep = tmp + (u64)ntohl(*p++);
+ return p;
+}
+
+static u32 *
+xdr_decode_fattr(u32 *p, struct nfs_fattr *fattr)
+{
+ fattr->type = (enum nfs_ftype) ntohl(*p++);
+ fattr->mode = ntohl(*p++);
+ fattr->nlink = ntohl(*p++);
+ fattr->uid = ntohl(*p++);
+ fattr->gid = ntohl(*p++);
+ fattr->size = ntohl(*p++);
+ fattr->du.nfs2.blocksize = ntohl(*p++);
+ fattr->rdev = ntohl(*p++);
+ fattr->du.nfs2.blocks = ntohl(*p++);
+ fattr->fsid = ntohl(*p++);
+ fattr->fileid = ntohl(*p++);
+ p = xdr_decode_time(p, &fattr->atime);
+ p = xdr_decode_time(p, &fattr->mtime);
+ p = xdr_decode_time(p, &fattr->ctime);
+ fattr->valid |= NFS_ATTR_FATTR;
+ if (fattr->type == NFCHR && fattr->rdev == NFS2_FIFO_DEV) {
+ fattr->type = NFFIFO;
+ fattr->mode = (fattr->mode & ~S_IFMT) | S_IFIFO;
+ fattr->rdev = 0;
+ }
+ return p;
+}
+
+#define SATTR(p, attr, flag, field) \
+ *p++ = (attr->ia_valid & flag) ? htonl(attr->field) : ~(u32) 0
+static inline u32 *
+xdr_encode_sattr(u32 *p, struct iattr *attr)
+{
+ SATTR(p, attr, ATTR_MODE, ia_mode);
+ SATTR(p, attr, ATTR_UID, ia_uid);
+ SATTR(p, attr, ATTR_GID, ia_gid);
+ SATTR(p, attr, ATTR_SIZE, ia_size);
+
+ if (attr->ia_valid & (ATTR_ATIME|ATTR_ATIME_SET)) {
+ *p++ = htonl(attr->ia_atime);
+ *p++ = 0;
+ } else {
+ *p++ = ~(u32) 0;
+ *p++ = ~(u32) 0;
+ }
+
+ if (attr->ia_valid & (ATTR_MTIME|ATTR_MTIME_SET)) {
+ *p++ = htonl(attr->ia_mtime);
+ *p++ = 0;
+ } else {
+ *p++ = ~(u32) 0;
+ *p++ = ~(u32) 0;
+ }
+ return p;
+}
+#undef SATTR
+
+/*
+ * NFS encode functions
+ */
+/*
+ * Encode void argument
+ */
+static int
+nfs_xdr_enc_void(struct rpc_rqst *req, u32 *p, void *dummy)
+{
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+/*
+ * Encode file handle argument
+ * GETATTR, READLINK, STATFS
+ */
+static int
+nfs_xdr_fhandle(struct rpc_rqst *req, u32 *p, struct nfs_fh *fh)
+{
+ p = xdr_encode_fhandle(p, fh);
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+/*
+ * Encode SETATTR arguments
+ */
+static int
+nfs_xdr_sattrargs(struct rpc_rqst *req, u32 *p, struct nfs_sattrargs *args)
+{
+ p = xdr_encode_fhandle(p, args->fh);
+ p = xdr_encode_sattr(p, args->sattr);
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+/*
+ * Encode directory ops argument
+ * LOOKUP, REMOVE, RMDIR
+ */
+static int
+nfs_xdr_diropargs(struct rpc_rqst *req, u32 *p, struct nfs_diropargs *args)
+{
+ p = xdr_encode_fhandle(p, args->fh);
+ p = xdr_encode_array(p, args->name, args->len);
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+/*
+ * Arguments to a READ call. Since we read data directly into the page
+ * cache, we also set up the reply iovec here so that iov[1] points
+ * exactly to the page we want to fetch.
+ */
+static int
+nfs_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args)
+{
+ struct rpc_auth *auth = req->rq_task->tk_auth;
+ unsigned int replen;
+ u32 offset = (u32)args->offset;
+ u32 count = args->count;
+
+ p = xdr_encode_fhandle(p, args->fh);
+ *p++ = htonl(offset);
+ *p++ = htonl(count);
+ *p++ = htonl(count);
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+
+ /* Inline the page array */
+ replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2;
+ xdr_inline_pages(&req->rq_rcv_buf, replen,
+ args->pages, args->pgbase, count);
+ return 0;
+}
+
+/*
+ * Decode READ reply
+ */
+static int
+nfs_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res)
+{
+ struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
+ struct iovec *iov = req->rq_rvec;
+ int status, count, recvd, hdrlen;
+
+ if ((status = ntohl(*p++)))
+ return -nfs_stat_to_errno(status);
+ p = xdr_decode_fattr(p, res->fattr);
+
+ count = ntohl(*p++);
+ res->eof = 0;
+ if (rcvbuf->page_len) {
+ u32 end = page_offset(rcvbuf->pages[0]) + rcvbuf->page_base + count;
+ if (end >= res->fattr->size)
+ res->eof = 1;
+ }
+ hdrlen = (u8 *) p - (u8 *) iov->iov_base;
+ if (iov->iov_len < hdrlen) {
+ printk(KERN_WARNING "NFS: READ reply header overflowed:"
+ "length %d > %Zu\n", hdrlen, iov->iov_len);
+ return -errno_NFSERR_IO;
+ } else if (iov->iov_len != hdrlen) {
+ dprintk("NFS: READ header is short. iovec will be shifted.\n");
+ xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen);
+ }
+
+ recvd = req->rq_received - hdrlen;
+ if (count > recvd) {
+ printk(KERN_WARNING "NFS: server cheating in read reply: "
+ "count %d > recvd %d\n", count, recvd);
+ count = recvd;
+ res->eof = 0;
+ }
+
+ dprintk("RPC: readres OK count %d\n", count);
+ if (count < res->count)
+ res->count = count;
+
+ return count;
+}
+
+
+/*
+ * Write arguments. Splice the buffer to be written into the iovec.
+ */
+static int
+nfs_xdr_writeargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args)
+{
+ struct xdr_buf *sndbuf = &req->rq_snd_buf;
+ u32 offset = (u32)args->offset;
+ u32 count = args->count;
+
+ p = xdr_encode_fhandle(p, args->fh);
+ *p++ = htonl(offset);
+ *p++ = htonl(offset);
+ *p++ = htonl(count);
+ *p++ = htonl(count);
+ sndbuf->len = xdr_adjust_iovec(sndbuf->head, p);
+
+ /* Copy the page array */
+ xdr_encode_pages(sndbuf, args->pages, args->pgbase, count);
+ return 0;
+}
+
+/*
+ * Encode create arguments
+ * CREATE, MKDIR
+ */
+static int
+nfs_xdr_createargs(struct rpc_rqst *req, u32 *p, struct nfs_createargs *args)
+{
+ p = xdr_encode_fhandle(p, args->fh);
+ p = xdr_encode_array(p, args->name, args->len);
+ p = xdr_encode_sattr(p, args->sattr);
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+/*
+ * Encode RENAME arguments
+ */
+static int
+nfs_xdr_renameargs(struct rpc_rqst *req, u32 *p, struct nfs_renameargs *args)
+{
+ p = xdr_encode_fhandle(p, args->fromfh);
+ p = xdr_encode_array(p, args->fromname, args->fromlen);
+ p = xdr_encode_fhandle(p, args->tofh);
+ p = xdr_encode_array(p, args->toname, args->tolen);
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+/*
+ * Encode LINK arguments
+ */
+static int
+nfs_xdr_linkargs(struct rpc_rqst *req, u32 *p, struct nfs_linkargs *args)
+{
+ p = xdr_encode_fhandle(p, args->fromfh);
+ p = xdr_encode_fhandle(p, args->tofh);
+ p = xdr_encode_array(p, args->toname, args->tolen);
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+/*
+ * Encode SYMLINK arguments
+ */
+static int
+nfs_xdr_symlinkargs(struct rpc_rqst *req, u32 *p, struct nfs_symlinkargs *args)
+{
+ p = xdr_encode_fhandle(p, args->fromfh);
+ p = xdr_encode_array(p, args->fromname, args->fromlen);
+ p = xdr_encode_array(p, args->topath, args->tolen);
+ p = xdr_encode_sattr(p, args->sattr);
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+/*
+ * Encode arguments to readdir call
+ */
+static int
+nfs_xdr_readdirargs(struct rpc_rqst *req, u32 *p, struct nfs_readdirargs *args)
+{
+ struct rpc_task *task = req->rq_task;
+ struct rpc_auth *auth = task->tk_auth;
+ unsigned int replen;
+ u32 count = args->count;
+
+ /*
+ * Some servers (e.g. HP OS 9.5) seem to expect the buffer size
+ * to be in longwords ... check whether to convert the size.
+ */
+ if (task->tk_client->cl_flags & NFS_CLNTF_BUFSIZE)
+ count = count >> 2;
+
+ p = xdr_encode_fhandle(p, args->fh);
+ *p++ = htonl(args->cookie);
+ *p++ = htonl(count); /* see above */
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+
+ /* Inline the page array */
+ replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readdirres_sz) << 2;
+ xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0, count);
+ return 0;
+}
+
+/*
+ * Decode the result of a readdir call.
+ * We're not really decoding anymore, we just leave the buffer untouched
+ * and only check that it is syntactically correct.
+ * The real decoding happens in nfs_decode_entry below, called directly
+ * from nfs_readdir for each entry.
+ */
+static int
+nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, void *dummy)
+{
+ struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
+ struct iovec *iov = rcvbuf->head;
+ struct page **page;
+ int hdrlen, recvd;
+ int status, nr;
+ unsigned int len, pglen;
+ u32 *end, *entry;
+
+ if ((status = ntohl(*p++)))
+ return -nfs_stat_to_errno(status);
+
+ hdrlen = (u8 *) p - (u8 *) iov->iov_base;
+ if (iov->iov_len < hdrlen) {
+ printk(KERN_WARNING "NFS: READDIR reply header overflowed:"
+ "length %d > %Zu\n", hdrlen, iov->iov_len);
+ return -errno_NFSERR_IO;
+ } else if (iov->iov_len != hdrlen) {
+ dprintk("NFS: READDIR header is short. iovec will be shifted.\n");
+ xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
+ }
+
+ pglen = rcvbuf->page_len;
+ recvd = req->rq_received - hdrlen;
+ if (pglen > recvd)
+ pglen = recvd;
+ page = rcvbuf->pages;
+ p = kmap(*page);
+ entry = p;
+ end = (u32 *)((char *)p + pglen);
+ for (nr = 0; *p++; nr++) {
+ if (p + 2 > end)
+ goto short_pkt;
+ p++; /* fileid */
+ len = ntohl(*p++);
+ p += XDR_QUADLEN(len) + 1; /* name plus cookie */
+ if (len > NFS2_MAXNAMLEN) {
+ printk(KERN_WARNING "NFS: giant filename in readdir (len 0x%x)!\n",
+ len);
+ goto err_unmap;
+ }
+ if (p + 2 > end)
+ goto short_pkt;
+ entry = p;
+ }
+ if (!nr && (entry[0] != 0 || entry[1] == 0))
+ goto short_pkt;
+ out:
+ kunmap(*page);
+ return nr;
+ short_pkt:
+ entry[0] = entry[1] = 0;
+ /* truncate listing ? */
+ if (!nr) {
+ printk(KERN_NOTICE "NFS: readdir reply truncated!\n");
+ entry[1] = 1;
+ }
+ goto out;
+err_unmap:
+ kunmap(*page);
+ return -errno_NFSERR_IO;
+}
+
+u32 *
+nfs_decode_dirent(u32 *p, struct nfs_entry *entry, int plus)
+{
+ if (!*p++) {
+ if (!*p)
+ return ERR_PTR(-EAGAIN);
+ entry->eof = 1;
+ return ERR_PTR(-EBADCOOKIE);
+ }
+
+ entry->ino = ntohl(*p++);
+ entry->len = ntohl(*p++);
+ entry->name = (const char *) p;
+ p += XDR_QUADLEN(entry->len);
+ entry->prev_cookie = entry->cookie;
+ entry->cookie = ntohl(*p++);
+ entry->eof = !p[0] && p[1];
+
+ return p;
+}
+
+/*
+ * NFS XDR decode functions
+ */
+/*
+ * Decode void reply
+ */
+static int
+nfs_xdr_dec_void(struct rpc_rqst *req, u32 *p, void *dummy)
+{
+ return 0;
+}
+
+/*
+ * Decode simple status reply
+ */
+static int
+nfs_xdr_stat(struct rpc_rqst *req, u32 *p, void *dummy)
+{
+ int status;
+
+ if ((status = ntohl(*p++)) != 0)
+ status = -nfs_stat_to_errno(status);
+ return status;
+}
+
+/*
+ * Decode attrstat reply
+ * GETATTR, SETATTR, WRITE
+ */
+static int
+nfs_xdr_attrstat(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
+{
+ int status;
+
+ if ((status = ntohl(*p++)))
+ return -nfs_stat_to_errno(status);
+ xdr_decode_fattr(p, fattr);
+ return 0;
+}
+
+/*
+ * Decode diropres reply
+ * LOOKUP, CREATE, MKDIR
+ */
+static int
+nfs_xdr_diropres(struct rpc_rqst *req, u32 *p, struct nfs_diropok *res)
+{
+ int status;
+
+ if ((status = ntohl(*p++)))
+ return -nfs_stat_to_errno(status);
+ p = xdr_decode_fhandle(p, res->fh);
+ xdr_decode_fattr(p, res->fattr);
+ return 0;
+}
+
+/*
+ * Encode READLINK args
+ */
+static int
+nfs_xdr_readlinkargs(struct rpc_rqst *req, u32 *p, struct nfs_readlinkargs *args)
+{
+ struct rpc_auth *auth = req->rq_task->tk_auth;
+ unsigned int replen;
+ u32 count = args->count - 4;
+
+ p = xdr_encode_fhandle(p, args->fh);
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+
+ /* Inline the page array */
+ replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readlinkres_sz) << 2;
+ xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0, count);
+ return 0;
+}
+
+/*
+ * Decode READLINK reply
+ */
+static int
+nfs_xdr_readlinkres(struct rpc_rqst *req, u32 *p, void *dummy)
+{
+ struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
+ struct iovec *iov = rcvbuf->head;
+ unsigned int hdrlen;
+ u32 *strlen, len;
+ char *string;
+ int status;
+
+ if ((status = ntohl(*p++)))
+ return -nfs_stat_to_errno(status);
+ hdrlen = (u8 *) p - (u8 *) iov->iov_base;
+ if (iov->iov_len > hdrlen) {
+ dprintk("NFS: READLINK header is short. iovec will be shifted.\n");
+ xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
+ }
+
+ strlen = (u32*)kmap(rcvbuf->pages[0]);
+ /* Convert length of symlink */
+ len = ntohl(*strlen);
+ if (len > rcvbuf->page_len)
+ len = rcvbuf->page_len;
+ *strlen = len;
+ /* NULL terminate the string we got */
+ string = (char *)(strlen + 1);
+ string[len] = 0;
+ kunmap(rcvbuf->pages[0]);
+ return 0;
+}
+
+/*
+ * Decode WRITE reply
+ */
+static int
+nfs_xdr_writeres(struct rpc_rqst *req, u32 *p, struct nfs_writeres *res)
+{
+ res->verf->committed = NFS_FILE_SYNC;
+ return nfs_xdr_attrstat(req, p, res->fattr);
+}
+
+/*
+ * Decode STATFS reply
+ */
+static int
+nfs_xdr_statfsres(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res)
+{
+ int status;
+ u32 xfer_size;
+
+ if ((status = ntohl(*p++)))
+ return -nfs_stat_to_errno(status);
+
+ /* For NFSv2, we more or less have to guess the preferred
+ * read/write/readdir sizes from the single 'transfer size'
+ * value.
+ */
+ xfer_size = ntohl(*p++); /* tsize */
+ res->rtmax = 8 * 1024;
+ res->rtpref = xfer_size;
+ res->rtmult = xfer_size;
+ res->wtmax = 8 * 1024;
+ res->wtpref = xfer_size;
+ res->wtmult = xfer_size;
+ res->dtpref = PAGE_CACHE_SIZE;
+ res->maxfilesize = 0x7FFFFFFF; /* just a guess */
+ res->bsize = ntohl(*p++);
+
+ res->tbytes = ntohl(*p++) * res->bsize;
+ res->fbytes = ntohl(*p++) * res->bsize;
+ res->abytes = ntohl(*p++) * res->bsize;
+ res->tfiles = 0;
+ res->ffiles = 0;
+ res->afiles = 0;
+ res->namelen = 0;
+
+ return 0;
+}
+
+/*
+ * We need to translate between nfs status return values and
+ * the local errno values which may not be the same.
+ */
+static struct {
+ int stat;
+ int errno;
+} nfs_errtbl[] = {
+ { NFS_OK, 0 },
+ { NFSERR_PERM, EPERM },
+ { NFSERR_NOENT, ENOENT },
+ { NFSERR_IO, errno_NFSERR_IO },
+ { NFSERR_NXIO, ENXIO },
+/* { NFSERR_EAGAIN, EAGAIN }, */
+ { NFSERR_ACCES, EACCES },
+ { NFSERR_EXIST, EEXIST },
+ { NFSERR_XDEV, EXDEV },
+ { NFSERR_NODEV, ENODEV },
+ { NFSERR_NOTDIR, ENOTDIR },
+ { NFSERR_ISDIR, EISDIR },
+ { NFSERR_INVAL, EINVAL },
+ { NFSERR_FBIG, EFBIG },
+ { NFSERR_NOSPC, ENOSPC },
+ { NFSERR_ROFS, EROFS },
+ { NFSERR_MLINK, EMLINK },
+ { NFSERR_NAMETOOLONG, ENAMETOOLONG },
+ { NFSERR_NOTEMPTY, ENOTEMPTY },
+ { NFSERR_DQUOT, EDQUOT },
+ { NFSERR_STALE, ESTALE },
+ { NFSERR_REMOTE, EREMOTE },
+#ifdef EWFLUSH
+ { NFSERR_WFLUSH, EWFLUSH },
+#endif
+ { NFSERR_BADHANDLE, EBADHANDLE },
+ { NFSERR_NOT_SYNC, ENOTSYNC },
+ { NFSERR_BAD_COOKIE, EBADCOOKIE },
+ { NFSERR_NOTSUPP, ENOTSUPP },
+ { NFSERR_TOOSMALL, ETOOSMALL },
+ { NFSERR_SERVERFAULT, ESERVERFAULT },
+ { NFSERR_BADTYPE, EBADTYPE },
+ { NFSERR_JUKEBOX, EJUKEBOX },
+ { -1, EIO }
+};
+
+/*
+ * Convert an NFS error code to a local one.
+ * This one is used jointly by NFSv2 and NFSv3.
+ */
+int
+nfs_stat_to_errno(int stat)
+{
+ int i;
+
+ for (i = 0; nfs_errtbl[i].stat != -1; i++) {
+ if (nfs_errtbl[i].stat == stat)
+ return nfs_errtbl[i].errno;
+ }
+ printk(KERN_ERR "nfs_stat_to_errno: bad nfs status return value: %d\n", stat);
+ return nfs_errtbl[i].errno;
+}
+
+#ifndef MAX
+# define MAX(a, b) (((a) > (b))? (a) : (b))
+#endif
+
+#define PROC(proc, argtype, restype, timer) \
+ { .p_procname = "nfs_" #proc, \
+ .p_encode = (kxdrproc_t) nfs_xdr_##argtype, \
+ .p_decode = (kxdrproc_t) nfs_xdr_##restype, \
+ .p_bufsiz = MAX(NFS_##argtype##_sz,NFS_##restype##_sz) << 2, \
+ .p_timer = timer \
+ }
+static struct rpc_procinfo nfs_procedures[18] = {
+ PROC(null, enc_void, dec_void, 0),
+ PROC(getattr, fhandle, attrstat, 1),
+ PROC(setattr, sattrargs, attrstat, 0),
+ PROC(root, enc_void, dec_void, 0),
+ PROC(lookup, diropargs, diropres, 2),
+ PROC(readlink, readlinkargs, readlinkres, 3),
+ PROC(read, readargs, readres, 3),
+ PROC(writecache, enc_void, dec_void, 0),
+ PROC(write, writeargs, writeres, 4),
+ PROC(create, createargs, diropres, 0),
+ PROC(remove, diropargs, stat, 0),
+ PROC(rename, renameargs, stat, 0),
+ PROC(link, linkargs, stat, 0),
+ PROC(symlink, symlinkargs, stat, 0),
+ PROC(mkdir, createargs, diropres, 0),
+ PROC(rmdir, diropargs, stat, 0),
+ PROC(readdir, readdirargs, readdirres, 3),
+ PROC(statfs, fhandle, statfsres, 0),
+};
+
+struct rpc_version nfs_version2 = {
+ 2,
+ sizeof(nfs_procedures)/sizeof(nfs_procedures[0]),
+ nfs_procedures
+};
diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c
new file mode 100644
index 00000000000000..2519a62f05f53b
--- /dev/null
+++ b/fs/nfs/nfs3proc.c
@@ -0,0 +1,532 @@
+/*
+ * linux/fs/nfs/nfs3proc.c
+ *
+ * Client-side NFSv3 procedures stubs.
+ *
+ * Copyright (C) 1997, Olaf Kirch
+ */
+
+#include <linux/mm.h>
+#include <linux/utsname.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/nfs.h>
+#include <linux/nfs3.h>
+#include <linux/nfs_fs.h>
+
+#define NFSDBG_FACILITY NFSDBG_PROC
+
+/* A wrapper to handle the EJUKEBOX error message */
+static int
+nfs3_rpc_wrapper(struct rpc_clnt *clnt, struct rpc_message *msg, int flags)
+{
+ sigset_t oldset;
+ int res;
+ rpc_clnt_sigmask(clnt, &oldset);
+ do {
+ res = rpc_call_sync(clnt, msg, flags);
+ if (res != -EJUKEBOX)
+ break;
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(NFS_JUKEBOX_RETRY_TIME);
+ res = -ERESTARTSYS;
+ } while (!signalled());
+ rpc_clnt_sigunmask(clnt, &oldset);
+ return res;
+}
+
+static inline int
+nfs3_rpc_call_wrapper(struct rpc_clnt *clnt, u32 proc, void *argp, void *resp, int flags)
+{
+ struct rpc_message msg = { proc, argp, resp, NULL };
+ return nfs3_rpc_wrapper(clnt, &msg, flags);
+}
+
+#define rpc_call(clnt, proc, argp, resp, flags) \
+ nfs3_rpc_call_wrapper(clnt, proc, argp, resp, flags)
+#define rpc_call_sync(clnt, msg, flags) \
+ nfs3_rpc_wrapper(clnt, msg, flags)
+
+/*
+ * Bare-bones access to getattr: this is for nfs_read_super.
+ */
+static int
+nfs3_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
+ struct nfs_fattr *fattr)
+{
+ int status;
+
+ dprintk("NFS call getroot\n");
+ fattr->valid = 0;
+ status = rpc_call(server->client, NFS3PROC_GETATTR, fhandle, fattr, 0);
+ dprintk("NFS reply getroot\n");
+ return status;
+}
+
+/*
+ * One function for each procedure in the NFS protocol.
+ */
+static int
+nfs3_proc_getattr(struct inode *inode, struct nfs_fattr *fattr)
+{
+ int status;
+
+ dprintk("NFS call getattr\n");
+ fattr->valid = 0;
+ status = rpc_call(NFS_CLIENT(inode), NFS3PROC_GETATTR,
+ NFS_FH(inode), fattr, 0);
+ dprintk("NFS reply getattr\n");
+ return status;
+}
+
+static int
+nfs3_proc_setattr(struct inode *inode, struct nfs_fattr *fattr,
+ struct iattr *sattr)
+{
+ struct nfs3_sattrargs arg = { NFS_FH(inode), sattr, 0, 0 };
+ int status;
+
+ dprintk("NFS call setattr\n");
+ fattr->valid = 0;
+ status = rpc_call(NFS_CLIENT(inode), NFS3PROC_SETATTR, &arg, fattr, 0);
+ dprintk("NFS reply setattr\n");
+ return status;
+}
+
+static int
+nfs3_proc_lookup(struct inode *dir, struct qstr *name,
+ struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+{
+ struct nfs_fattr dir_attr;
+ struct nfs3_diropargs arg = { NFS_FH(dir), name->name, name->len };
+ struct nfs3_diropres res = { &dir_attr, fhandle, fattr };
+ int status;
+
+ dprintk("NFS call lookup %s\n", name->name);
+ dir_attr.valid = 0;
+ fattr->valid = 0;
+ status = rpc_call(NFS_CLIENT(dir), NFS3PROC_LOOKUP, &arg, &res, 0);
+ if (status >= 0 && !(fattr->valid & NFS_ATTR_FATTR))
+ status = rpc_call(NFS_CLIENT(dir), NFS3PROC_GETATTR,
+ fhandle, fattr, 0);
+ dprintk("NFS reply lookup: %d\n", status);
+ if (status >= 0)
+ status = nfs_refresh_inode(dir, &dir_attr);
+ return status;
+}
+
+static int
+nfs3_proc_access(struct inode *inode, int mode, int ruid)
+{
+ struct nfs_fattr fattr;
+ struct nfs3_accessargs arg = { NFS_FH(inode), 0 };
+ struct nfs3_accessres res = { &fattr, 0 };
+ int status, flags;
+
+ dprintk("NFS call access\n");
+ fattr.valid = 0;
+
+ if (mode & MAY_READ)
+ arg.access |= NFS3_ACCESS_READ;
+ if (S_ISDIR(inode->i_mode)) {
+ if (mode & MAY_WRITE)
+ arg.access |= NFS3_ACCESS_MODIFY | NFS3_ACCESS_EXTEND | NFS3_ACCESS_DELETE;
+ if (mode & MAY_EXEC)
+ arg.access |= NFS3_ACCESS_LOOKUP;
+ } else {
+ if (mode & MAY_WRITE)
+ arg.access |= NFS3_ACCESS_MODIFY | NFS3_ACCESS_EXTEND;
+ if (mode & MAY_EXEC)
+ arg.access |= NFS3_ACCESS_EXECUTE;
+ }
+ flags = (ruid) ? RPC_CALL_REALUID : 0;
+ status = rpc_call(NFS_CLIENT(inode), NFS3PROC_ACCESS, &arg, &res, flags);
+ nfs_refresh_inode(inode, &fattr);
+ dprintk("NFS reply access\n");
+
+ if (status == 0 && (arg.access & res.access) != arg.access)
+ status = -EACCES;
+ return status;
+}
+
+static int
+nfs3_proc_readlink(struct inode *inode, struct page *page)
+{
+ struct nfs_fattr fattr;
+ struct nfs3_readlinkargs args = { NFS_FH(inode), PAGE_CACHE_SIZE, &page };
+ int status;
+
+ dprintk("NFS call readlink\n");
+ fattr.valid = 0;
+ status = rpc_call(NFS_CLIENT(inode), NFS3PROC_READLINK,
+ &args, &fattr, 0);
+ nfs_refresh_inode(inode, &fattr);
+ dprintk("NFS reply readlink: %d\n", status);
+ return status;
+}
+
+static int
+nfs3_proc_read(struct inode *inode, struct rpc_cred *cred,
+ struct nfs_fattr *fattr, int flags,
+ unsigned int base, unsigned int count, struct page *page,
+ int *eofp)
+{
+ u64 offset = page_offset(page) + base;
+ struct nfs_readargs arg = { NFS_FH(inode), offset, count,
+ base, &page };
+ struct nfs_readres res = { fattr, count, 0 };
+ struct rpc_message msg = { NFS3PROC_READ, &arg, &res, cred };
+ int status;
+
+ dprintk("NFS call read %d @ %Ld\n", count, (long long)offset);
+ fattr->valid = 0;
+ status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags);
+ dprintk("NFS reply read: %d\n", status);
+ *eofp = res.eof;
+ return status;
+}
+
+static int
+nfs3_proc_write(struct inode *inode, struct rpc_cred *cred,
+ struct nfs_fattr *fattr, int flags,
+ unsigned int base, unsigned int count,
+ struct page *page, struct nfs_writeverf *verf)
+{
+ u64 offset = page_offset(page) + base;
+ struct nfs_writeargs arg = { NFS_FH(inode), offset, count,
+ NFS_FILE_SYNC, base, &page };
+ struct nfs_writeres res = { fattr, verf, 0 };
+ struct rpc_message msg = { NFS3PROC_WRITE, &arg, &res, cred };
+ int status, rpcflags = 0;
+
+ dprintk("NFS call write %d @ %Ld\n", count, (long long)offset);
+ fattr->valid = 0;
+ if (flags & NFS_RW_SWAP)
+ rpcflags |= NFS_RPC_SWAPFLAGS;
+ arg.stable = (flags & NFS_RW_SYNC) ? NFS_FILE_SYNC : NFS_UNSTABLE;
+
+ status = rpc_call_sync(NFS_CLIENT(inode), &msg, rpcflags);
+
+ dprintk("NFS reply read: %d\n", status);
+ return status < 0? status : res.count;
+}
+
+/*
+ * Create a regular file.
+ * For now, we don't implement O_EXCL.
+ */
+static int
+nfs3_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr,
+ int flags, struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+{
+ struct nfs_fattr dir_attr;
+ struct nfs3_createargs arg = { NFS_FH(dir), name->name, name->len,
+ sattr, 0, { 0, 0 } };
+ struct nfs3_diropres res = { &dir_attr, fhandle, fattr };
+ int status;
+
+ dprintk("NFS call create %s\n", name->name);
+ arg.createmode = NFS3_CREATE_UNCHECKED;
+ if (flags & O_EXCL) {
+ arg.createmode = NFS3_CREATE_EXCLUSIVE;
+ arg.verifier[0] = jiffies;
+ arg.verifier[1] = current->pid;
+ }
+
+again:
+ dir_attr.valid = 0;
+ fattr->valid = 0;
+ status = rpc_call(NFS_CLIENT(dir), NFS3PROC_CREATE, &arg, &res, 0);
+ nfs_refresh_inode(dir, &dir_attr);
+
+ /* If the server doesn't support the exclusive creation semantics,
+ * try again with simple 'guarded' mode. */
+ if (status == NFSERR_NOTSUPP) {
+ switch (arg.createmode) {
+ case NFS3_CREATE_EXCLUSIVE:
+ arg.createmode = NFS3_CREATE_GUARDED;
+ break;
+
+ case NFS3_CREATE_GUARDED:
+ arg.createmode = NFS3_CREATE_UNCHECKED;
+ break;
+
+ case NFS3_CREATE_UNCHECKED:
+ goto exit;
+ }
+ goto again;
+ }
+
+exit:
+ dprintk("NFS reply create: %d\n", status);
+
+ /* When we created the file with exclusive semantics, make
+ * sure we set the attributes afterwards. */
+ if (status == 0 && arg.createmode == NFS3_CREATE_EXCLUSIVE) {
+ struct nfs3_sattrargs arg = { fhandle, sattr, 0, 0 };
+ dprintk("NFS call setattr (post-create)\n");
+
+ /* Note: we could use a guarded setattr here, but I'm
+ * not sure this buys us anything (and I'd have
+ * to revamp the NFSv3 XDR code) */
+ fattr->valid = 0;
+ status = rpc_call(NFS_CLIENT(dir), NFS3PROC_SETATTR,
+ &arg, fattr, 0);
+ dprintk("NFS reply setattr (post-create): %d\n", status);
+ }
+
+ return status;
+}
+
+static int
+nfs3_proc_remove(struct inode *dir, struct qstr *name)
+{
+ struct nfs_fattr dir_attr;
+ struct nfs3_diropargs arg = { NFS_FH(dir), name->name, name->len };
+ struct rpc_message msg = { NFS3PROC_REMOVE, &arg, &dir_attr, NULL };
+ int status;
+
+ dprintk("NFS call remove %s\n", name->name);
+ dir_attr.valid = 0;
+ status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
+ nfs_refresh_inode(dir, &dir_attr);
+ dprintk("NFS reply remove: %d\n", status);
+ return status;
+}
+
+static int
+nfs3_proc_unlink_setup(struct rpc_message *msg, struct dentry *dir, struct qstr *name)
+{
+ struct nfs3_diropargs *arg;
+ struct nfs_fattr *res;
+ struct unlinkxdr {
+ struct nfs3_diropargs arg;
+ struct nfs_fattr res;
+ } *ptr;
+
+ ptr = (struct unlinkxdr *)kmalloc(sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return -ENOMEM;
+ arg = &ptr->arg;
+ res = &ptr->res;
+ arg->fh = NFS_FH(dir->d_inode);
+ arg->name = name->name;
+ arg->len = name->len;
+ res->valid = 0;
+ msg->rpc_proc = NFS3PROC_REMOVE;
+ msg->rpc_argp = arg;
+ msg->rpc_resp = res;
+ return 0;
+}
+
+static void
+nfs3_proc_unlink_done(struct dentry *dir, struct rpc_message *msg)
+{
+ struct nfs_fattr *dir_attr;
+
+ if (msg->rpc_argp) {
+ dir_attr = (struct nfs_fattr*)msg->rpc_resp;
+ nfs_refresh_inode(dir->d_inode, dir_attr);
+ kfree(msg->rpc_argp);
+ }
+}
+
+static int
+nfs3_proc_rename(struct inode *old_dir, struct qstr *old_name,
+ struct inode *new_dir, struct qstr *new_name)
+{
+ struct nfs_fattr old_dir_attr, new_dir_attr;
+ struct nfs3_renameargs arg = { NFS_FH(old_dir),
+ old_name->name, old_name->len,
+ NFS_FH(new_dir),
+ new_name->name, new_name->len };
+ struct nfs3_renameres res = { &old_dir_attr, &new_dir_attr };
+ int status;
+
+ dprintk("NFS call rename %s -> %s\n", old_name->name, new_name->name);
+ old_dir_attr.valid = 0;
+ new_dir_attr.valid = 0;
+ status = rpc_call(NFS_CLIENT(old_dir), NFS3PROC_RENAME, &arg, &res, 0);
+ nfs_refresh_inode(old_dir, &old_dir_attr);
+ nfs_refresh_inode(new_dir, &new_dir_attr);
+ dprintk("NFS reply rename: %d\n", status);
+ return status;
+}
+
+static int
+nfs3_proc_link(struct inode *inode, struct inode *dir, struct qstr *name)
+{
+ struct nfs_fattr dir_attr, fattr;
+ struct nfs3_linkargs arg = { NFS_FH(inode), NFS_FH(dir),
+ name->name, name->len };
+ struct nfs3_linkres res = { &dir_attr, &fattr };
+ int status;
+
+ dprintk("NFS call link %s\n", name->name);
+ dir_attr.valid = 0;
+ fattr.valid = 0;
+ status = rpc_call(NFS_CLIENT(inode), NFS3PROC_LINK, &arg, &res, 0);
+ nfs_refresh_inode(dir, &dir_attr);
+ nfs_refresh_inode(inode, &fattr);
+ dprintk("NFS reply link: %d\n", status);
+ return status;
+}
+
+static int
+nfs3_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path,
+ struct iattr *sattr, struct nfs_fh *fhandle,
+ struct nfs_fattr *fattr)
+{
+ struct nfs_fattr dir_attr;
+ struct nfs3_symlinkargs arg = { NFS_FH(dir), name->name, name->len,
+ path->name, path->len, sattr };
+ struct nfs3_diropres res = { &dir_attr, fhandle, fattr };
+ int status;
+
+ dprintk("NFS call symlink %s -> %s\n", name->name, path->name);
+ dir_attr.valid = 0;
+ fattr->valid = 0;
+ status = rpc_call(NFS_CLIENT(dir), NFS3PROC_SYMLINK, &arg, &res, 0);
+ nfs_refresh_inode(dir, &dir_attr);
+ dprintk("NFS reply symlink: %d\n", status);
+ return status;
+}
+
+static int
+nfs3_proc_mkdir(struct inode *dir, struct qstr *name, struct iattr *sattr,
+ struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+{
+ struct nfs_fattr dir_attr;
+ struct nfs3_mkdirargs arg = { NFS_FH(dir), name->name, name->len,
+ sattr };
+ struct nfs3_diropres res = { &dir_attr, fhandle, fattr };
+ int status;
+
+ dprintk("NFS call mkdir %s\n", name->name);
+ dir_attr.valid = 0;
+ fattr->valid = 0;
+ status = rpc_call(NFS_CLIENT(dir), NFS3PROC_MKDIR, &arg, &res, 0);
+ nfs_refresh_inode(dir, &dir_attr);
+ dprintk("NFS reply mkdir: %d\n", status);
+ return status;
+}
+
+static int
+nfs3_proc_rmdir(struct inode *dir, struct qstr *name)
+{
+ struct nfs_fattr dir_attr;
+ struct nfs3_diropargs arg = { NFS_FH(dir), name->name, name->len };
+ int status;
+
+ dprintk("NFS call rmdir %s\n", name->name);
+ dir_attr.valid = 0;
+ status = rpc_call(NFS_CLIENT(dir), NFS3PROC_RMDIR, &arg, &dir_attr, 0);
+ nfs_refresh_inode(dir, &dir_attr);
+ dprintk("NFS reply rmdir: %d\n", status);
+ return status;
+}
+
+/*
+ * The READDIR implementation is somewhat hackish - we pass the user buffer
+ * to the encode function, which installs it in the receive iovec.
+ * The decode function itself doesn't perform any decoding, it just makes
+ * sure the reply is syntactically correct.
+ *
+ */
+static int
+nfs3_proc_readdir(struct inode *dir, struct rpc_cred *cred,
+ u64 cookie, struct page *page, unsigned int count, int plus)
+{
+ struct nfs_fattr dir_attr;
+ u32 *verf = NFS_COOKIEVERF(dir);
+ struct nfs3_readdirargs arg = { NFS_FH(dir), cookie, {verf[0], verf[1]},
+ plus, count, &page };
+ struct nfs3_readdirres res = { &dir_attr, verf, plus };
+ struct rpc_message msg = { NFS3PROC_READDIR, &arg, &res, cred };
+ int status;
+
+ dprintk("NFS call readdir %d\n", (unsigned int) cookie);
+
+ dir_attr.valid = 0;
+ status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
+ nfs_refresh_inode(dir, &dir_attr);
+ dprintk("NFS reply readdir: %d\n", status);
+ return status;
+}
+
+static int
+nfs3_proc_mknod(struct inode *dir, struct qstr *name, struct iattr *sattr,
+ dev_t rdev, struct nfs_fh *fh, struct nfs_fattr *fattr)
+{
+ struct nfs_fattr dir_attr;
+ struct nfs3_mknodargs arg = { NFS_FH(dir), name->name, name->len, 0,
+ sattr, rdev };
+ struct nfs3_diropres res = { &dir_attr, fh, fattr };
+ int status;
+
+ switch (sattr->ia_mode & S_IFMT) {
+ case S_IFBLK: arg.type = NF3BLK; break;
+ case S_IFCHR: arg.type = NF3CHR; break;
+ case S_IFIFO: arg.type = NF3FIFO; break;
+ case S_IFSOCK: arg.type = NF3SOCK; break;
+ default: return -EINVAL;
+ }
+
+ dprintk("NFS call mknod %s %x\n", name->name, rdev);
+ dir_attr.valid = 0;
+ fattr->valid = 0;
+ status = rpc_call(NFS_CLIENT(dir), NFS3PROC_MKNOD, &arg, &res, 0);
+ nfs_refresh_inode(dir, &dir_attr);
+ dprintk("NFS reply mknod: %d\n", status);
+ return status;
+}
+
+/*
+ * This is a combo call of fsstat and fsinfo
+ */
+static int
+nfs3_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
+ struct nfs_fsinfo *info)
+{
+ int status;
+
+ dprintk("NFS call fsstat\n");
+ memset((char *)info, 0, sizeof(*info));
+ status = rpc_call(server->client, NFS3PROC_FSSTAT, fhandle, info, 0);
+ if (status < 0)
+ goto error;
+ status = rpc_call(server->client, NFS3PROC_FSINFO, fhandle, info, 0);
+
+error:
+ dprintk("NFS reply statfs: %d\n", status);
+ return status;
+}
+
+extern u32 *nfs3_decode_dirent(u32 *, struct nfs_entry *, int);
+
+struct nfs_rpc_ops nfs_v3_clientops = {
+ 3, /* protocol version */
+ nfs3_proc_get_root,
+ nfs3_proc_getattr,
+ nfs3_proc_setattr,
+ nfs3_proc_lookup,
+ nfs3_proc_access,
+ nfs3_proc_readlink,
+ nfs3_proc_read,
+ nfs3_proc_write,
+ NULL, /* commit */
+ nfs3_proc_create,
+ nfs3_proc_remove,
+ nfs3_proc_unlink_setup,
+ nfs3_proc_unlink_done,
+ nfs3_proc_rename,
+ nfs3_proc_link,
+ nfs3_proc_symlink,
+ nfs3_proc_mkdir,
+ nfs3_proc_rmdir,
+ nfs3_proc_readdir,
+ nfs3_proc_mknod,
+ nfs3_proc_statfs,
+ nfs3_decode_dirent,
+};
diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c
new file mode 100644
index 00000000000000..5606222f8f26b8
--- /dev/null
+++ b/fs/nfs/nfs3xdr.c
@@ -0,0 +1,1037 @@
+/*
+ * linux/fs/nfs/nfs3xdr.c
+ *
+ * XDR functions to encode/decode NFSv3 RPC arguments and results.
+ *
+ * Copyright (C) 1996, 1997 Olaf Kirch
+ */
+
+#include <linux/param.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/utsname.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/in.h>
+#include <linux/pagemap.h>
+#include <linux/proc_fs.h>
+#include <linux/kdev_t.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/nfs.h>
+#include <linux/nfs3.h>
+#include <linux/nfs_fs.h>
+
+#define NFSDBG_FACILITY NFSDBG_XDR
+
+/* Mapping from NFS error code to "errno" error code. */
+#define errno_NFSERR_IO EIO
+
+extern int nfs_stat_to_errno(int);
+
+/*
+ * Declare the space requirements for NFS arguments and replies as
+ * number of 32bit-words
+ */
+#define NFS3_fhandle_sz 1+16
+#define NFS3_fh_sz NFS3_fhandle_sz /* shorthand */
+#define NFS3_sattr_sz 15
+#define NFS3_filename_sz 1+(NFS3_MAXNAMLEN>>2)
+#define NFS3_path_sz 1+(NFS3_MAXPATHLEN>>2)
+#define NFS3_fattr_sz 21
+#define NFS3_wcc_attr_sz 6
+#define NFS3_pre_op_attr_sz 1+NFS3_wcc_attr_sz
+#define NFS3_post_op_attr_sz 1+NFS3_fattr_sz
+#define NFS3_wcc_data_sz NFS3_pre_op_attr_sz+NFS3_post_op_attr_sz
+#define NFS3_fsstat_sz
+#define NFS3_fsinfo_sz
+#define NFS3_pathconf_sz
+#define NFS3_entry_sz NFS3_filename_sz+3
+
+#define NFS3_enc_void_sz 0
+#define NFS3_sattrargs_sz NFS3_fh_sz+NFS3_sattr_sz+3
+#define NFS3_diropargs_sz NFS3_fh_sz+NFS3_filename_sz
+#define NFS3_accessargs_sz NFS3_fh_sz+1
+#define NFS3_readlinkargs_sz NFS3_fh_sz
+#define NFS3_readargs_sz NFS3_fh_sz+3
+#define NFS3_writeargs_sz NFS3_fh_sz+5
+#define NFS3_createargs_sz NFS3_diropargs_sz+NFS3_sattr_sz
+#define NFS3_mkdirargs_sz NFS3_diropargs_sz+NFS3_sattr_sz
+#define NFS3_symlinkargs_sz NFS3_diropargs_sz+NFS3_path_sz+NFS3_sattr_sz
+#define NFS3_mknodargs_sz NFS3_diropargs_sz+2+NFS3_sattr_sz
+#define NFS3_renameargs_sz NFS3_diropargs_sz+NFS3_diropargs_sz
+#define NFS3_linkargs_sz NFS3_fh_sz+NFS3_diropargs_sz
+#define NFS3_readdirargs_sz NFS3_fh_sz+2
+#define NFS3_commitargs_sz NFS3_fh_sz+3
+
+#define NFS3_dec_void_sz 0
+#define NFS3_attrstat_sz 1+NFS3_fattr_sz
+#define NFS3_wccstat_sz 1+NFS3_wcc_data_sz
+#define NFS3_lookupres_sz 1+NFS3_fh_sz+(2 * NFS3_post_op_attr_sz)
+#define NFS3_accessres_sz 1+NFS3_post_op_attr_sz+1
+#define NFS3_readlinkres_sz 1+NFS3_post_op_attr_sz
+#define NFS3_readres_sz 1+NFS3_post_op_attr_sz+3
+#define NFS3_writeres_sz 1+NFS3_wcc_data_sz+4
+#define NFS3_createres_sz 1+NFS3_fh_sz+NFS3_post_op_attr_sz+NFS3_wcc_data_sz
+#define NFS3_renameres_sz 1+(2 * NFS3_wcc_data_sz)
+#define NFS3_linkres_sz 1+NFS3_post_op_attr_sz+NFS3_wcc_data_sz
+#define NFS3_readdirres_sz 1+NFS3_post_op_attr_sz+2
+#define NFS3_fsstatres_sz 1+NFS3_post_op_attr_sz+13
+#define NFS3_fsinfores_sz 1+NFS3_post_op_attr_sz+12
+#define NFS3_pathconfres_sz 1+NFS3_post_op_attr_sz+6
+#define NFS3_commitres_sz 1+NFS3_wcc_data_sz+2
+
+/*
+ * Map file type to S_IFMT bits
+ */
+static struct {
+ unsigned int mode;
+ unsigned int nfs2type;
+} nfs_type2fmt[] = {
+ { 0, NFNON },
+ { S_IFREG, NFREG },
+ { S_IFDIR, NFDIR },
+ { S_IFBLK, NFBLK },
+ { S_IFCHR, NFCHR },
+ { S_IFLNK, NFLNK },
+ { S_IFSOCK, NFSOCK },
+ { S_IFIFO, NFFIFO },
+ { 0, NFBAD }
+};
+
+/*
+ * Common NFS XDR functions as inlines
+ */
+static inline u32 *
+xdr_encode_fhandle(u32 *p, struct nfs_fh *fh)
+{
+ *p++ = htonl(fh->size);
+ memcpy(p, fh->data, fh->size);
+ return p + XDR_QUADLEN(fh->size);
+}
+
+static inline u32 *
+xdr_decode_fhandle(u32 *p, struct nfs_fh *fh)
+{
+ /*
+ * Zero all nonused bytes
+ */
+ memset((u8 *)fh, 0, sizeof(*fh));
+ if ((fh->size = ntohl(*p++)) <= NFS3_FHSIZE) {
+ memcpy(fh->data, p, fh->size);
+ return p + XDR_QUADLEN(fh->size);
+ }
+ return NULL;
+}
+
+/*
+ * Encode/decode time.
+ * Since the VFS doesn't care for fractional times, we ignore the
+ * nanosecond field.
+ */
+static inline u32 *
+xdr_encode_time(u32 *p, time_t time)
+{
+ *p++ = htonl(time);
+ *p++ = 0;
+ return p;
+}
+
+static inline u32 *
+xdr_decode_time3(u32 *p, u64 *timep)
+{
+ u64 tmp = (u64)ntohl(*p++) << 32;
+ *timep = tmp + (u64)ntohl(*p++);
+ return p;
+}
+
+static inline u32 *
+xdr_encode_time3(u32 *p, u64 time)
+{
+ *p++ = htonl(time >> 32);
+ *p++ = htonl(time & 0xFFFFFFFF);
+ return p;
+}
+
+static u32 *
+xdr_decode_fattr(u32 *p, struct nfs_fattr *fattr)
+{
+ unsigned int type;
+ int fmode;
+
+ type = ntohl(*p++);
+ if (type >= NF3BAD)
+ type = NF3BAD;
+ fmode = nfs_type2fmt[type].mode;
+ fattr->type = nfs_type2fmt[type].nfs2type;
+ fattr->mode = (ntohl(*p++) & ~S_IFMT) | fmode;
+ fattr->nlink = ntohl(*p++);
+ fattr->uid = ntohl(*p++);
+ fattr->gid = ntohl(*p++);
+ p = xdr_decode_hyper(p, &fattr->size);
+ p = xdr_decode_hyper(p, &fattr->du.nfs3.used);
+ /* Turn remote device info into Linux-specific dev_t */
+ fattr->rdev = ntohl(*p++) << MINORBITS;
+ fattr->rdev |= ntohl(*p++) & MINORMASK;
+ p = xdr_decode_hyper(p, &fattr->fsid);
+ p = xdr_decode_hyper(p, &fattr->fileid);
+ p = xdr_decode_time3(p, &fattr->atime);
+ p = xdr_decode_time3(p, &fattr->mtime);
+ p = xdr_decode_time3(p, &fattr->ctime);
+
+ /* Update the mode bits */
+ fattr->valid |= (NFS_ATTR_FATTR | NFS_ATTR_FATTR_V3);
+ return p;
+}
+
+static inline u32 *
+xdr_encode_sattr(u32 *p, struct iattr *attr)
+{
+ if (attr->ia_valid & ATTR_MODE) {
+ *p++ = xdr_one;
+ *p++ = htonl(attr->ia_mode);
+ } else {
+ *p++ = xdr_zero;
+ }
+ if (attr->ia_valid & ATTR_UID) {
+ *p++ = xdr_one;
+ *p++ = htonl(attr->ia_uid);
+ } else {
+ *p++ = xdr_zero;
+ }
+ if (attr->ia_valid & ATTR_GID) {
+ *p++ = xdr_one;
+ *p++ = htonl(attr->ia_gid);
+ } else {
+ *p++ = xdr_zero;
+ }
+ if (attr->ia_valid & ATTR_SIZE) {
+ *p++ = xdr_one;
+ p = xdr_encode_hyper(p, (__u64) attr->ia_size);
+ } else {
+ *p++ = xdr_zero;
+ }
+ if (attr->ia_valid & ATTR_ATIME_SET) {
+ *p++ = xdr_two;
+ p = xdr_encode_time(p, attr->ia_atime);
+ } else if (attr->ia_valid & ATTR_ATIME) {
+ *p++ = xdr_one;
+ } else {
+ *p++ = xdr_zero;
+ }
+ if (attr->ia_valid & ATTR_MTIME_SET) {
+ *p++ = xdr_two;
+ p = xdr_encode_time(p, attr->ia_mtime);
+ } else if (attr->ia_valid & ATTR_MTIME) {
+ *p++ = xdr_one;
+ } else {
+ *p++ = xdr_zero;
+ }
+ return p;
+}
+
+static inline u32 *
+xdr_decode_wcc_attr(u32 *p, struct nfs_fattr *fattr)
+{
+ p = xdr_decode_hyper(p, &fattr->pre_size);
+ p = xdr_decode_time3(p, &fattr->pre_mtime);
+ p = xdr_decode_time3(p, &fattr->pre_ctime);
+ fattr->valid |= NFS_ATTR_WCC;
+ return p;
+}
+
+static inline u32 *
+xdr_decode_post_op_attr(u32 *p, struct nfs_fattr *fattr)
+{
+ if (*p++)
+ p = xdr_decode_fattr(p, fattr);
+ return p;
+}
+
+static inline u32 *
+xdr_decode_pre_op_attr(u32 *p, struct nfs_fattr *fattr)
+{
+ if (*p++)
+ return xdr_decode_wcc_attr(p, fattr);
+ return p;
+}
+
+
+static inline u32 *
+xdr_decode_wcc_data(u32 *p, struct nfs_fattr *fattr)
+{
+ p = xdr_decode_pre_op_attr(p, fattr);
+ return xdr_decode_post_op_attr(p, fattr);
+}
+
+/*
+ * NFS encode functions
+ */
+/*
+ * Encode void argument
+ */
+static int
+nfs3_xdr_enc_void(struct rpc_rqst *req, u32 *p, void *dummy)
+{
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+/*
+ * Encode file handle argument
+ */
+static int
+nfs3_xdr_fhandle(struct rpc_rqst *req, u32 *p, struct nfs_fh *fh)
+{
+ p = xdr_encode_fhandle(p, fh);
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+/*
+ * Encode SETATTR arguments
+ */
+static int
+nfs3_xdr_sattrargs(struct rpc_rqst *req, u32 *p, struct nfs3_sattrargs *args)
+{
+ p = xdr_encode_fhandle(p, args->fh);
+ p = xdr_encode_sattr(p, args->sattr);
+ *p++ = htonl(args->guard);
+ if (args->guard)
+ p = xdr_encode_time3(p, args->guardtime);
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+/*
+ * Encode directory ops argument
+ */
+static int
+nfs3_xdr_diropargs(struct rpc_rqst *req, u32 *p, struct nfs3_diropargs *args)
+{
+ p = xdr_encode_fhandle(p, args->fh);
+ p = xdr_encode_array(p, args->name, args->len);
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+/*
+ * Encode access() argument
+ */
+static int
+nfs3_xdr_accessargs(struct rpc_rqst *req, u32 *p, struct nfs3_accessargs *args)
+{
+ p = xdr_encode_fhandle(p, args->fh);
+ *p++ = htonl(args->access);
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+/*
+ * Arguments to a READ call. Since we read data directly into the page
+ * cache, we also set up the reply iovec here so that iov[1] points
+ * exactly to the page we want to fetch.
+ */
+static int
+nfs3_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args)
+{
+ struct rpc_auth *auth = req->rq_task->tk_auth;
+ unsigned int replen;
+ u32 count = args->count;
+
+ p = xdr_encode_fhandle(p, args->fh);
+ p = xdr_encode_hyper(p, args->offset);
+ *p++ = htonl(count);
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+
+ /* Inline the page array */
+ replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readres_sz) << 2;
+ xdr_inline_pages(&req->rq_rcv_buf, replen,
+ args->pages, args->pgbase, count);
+ return 0;
+}
+
+/*
+ * Write arguments. Splice the buffer to be written into the iovec.
+ */
+static int
+nfs3_xdr_writeargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args)
+{
+ struct xdr_buf *sndbuf = &req->rq_snd_buf;
+ u32 count = args->count;
+
+ p = xdr_encode_fhandle(p, args->fh);
+ p = xdr_encode_hyper(p, args->offset);
+ *p++ = htonl(count);
+ *p++ = htonl(args->stable);
+ *p++ = htonl(count);
+ sndbuf->len = xdr_adjust_iovec(sndbuf->head, p);
+
+ /* Copy the page array */
+ xdr_encode_pages(sndbuf, args->pages, args->pgbase, count);
+ return 0;
+}
+
+/*
+ * Encode CREATE arguments
+ */
+static int
+nfs3_xdr_createargs(struct rpc_rqst *req, u32 *p, struct nfs3_createargs *args)
+{
+ p = xdr_encode_fhandle(p, args->fh);
+ p = xdr_encode_array(p, args->name, args->len);
+
+ *p++ = htonl(args->createmode);
+ if (args->createmode == NFS3_CREATE_EXCLUSIVE) {
+ *p++ = args->verifier[0];
+ *p++ = args->verifier[1];
+ } else
+ p = xdr_encode_sattr(p, args->sattr);
+
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+/*
+ * Encode MKDIR arguments
+ */
+static int
+nfs3_xdr_mkdirargs(struct rpc_rqst *req, u32 *p, struct nfs3_mkdirargs *args)
+{
+ p = xdr_encode_fhandle(p, args->fh);
+ p = xdr_encode_array(p, args->name, args->len);
+ p = xdr_encode_sattr(p, args->sattr);
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+/*
+ * Encode SYMLINK arguments
+ */
+static int
+nfs3_xdr_symlinkargs(struct rpc_rqst *req, u32 *p, struct nfs3_symlinkargs *args)
+{
+ p = xdr_encode_fhandle(p, args->fromfh);
+ p = xdr_encode_array(p, args->fromname, args->fromlen);
+ p = xdr_encode_sattr(p, args->sattr);
+ p = xdr_encode_array(p, args->topath, args->tolen);
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+/*
+ * Encode MKNOD arguments
+ */
+static int
+nfs3_xdr_mknodargs(struct rpc_rqst *req, u32 *p, struct nfs3_mknodargs *args)
+{
+ p = xdr_encode_fhandle(p, args->fh);
+ p = xdr_encode_array(p, args->name, args->len);
+ *p++ = htonl(args->type);
+ p = xdr_encode_sattr(p, args->sattr);
+ if (args->type == NF3CHR || args->type == NF3BLK) {
+ *p++ = htonl(args->rdev >> MINORBITS);
+ *p++ = htonl(args->rdev & MINORMASK);
+ }
+
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+/*
+ * Encode RENAME arguments
+ */
+static int
+nfs3_xdr_renameargs(struct rpc_rqst *req, u32 *p, struct nfs3_renameargs *args)
+{
+ p = xdr_encode_fhandle(p, args->fromfh);
+ p = xdr_encode_array(p, args->fromname, args->fromlen);
+ p = xdr_encode_fhandle(p, args->tofh);
+ p = xdr_encode_array(p, args->toname, args->tolen);
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+/*
+ * Encode LINK arguments
+ */
+static int
+nfs3_xdr_linkargs(struct rpc_rqst *req, u32 *p, struct nfs3_linkargs *args)
+{
+ p = xdr_encode_fhandle(p, args->fromfh);
+ p = xdr_encode_fhandle(p, args->tofh);
+ p = xdr_encode_array(p, args->toname, args->tolen);
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+/*
+ * Encode arguments to readdir call
+ */
+static int
+nfs3_xdr_readdirargs(struct rpc_rqst *req, u32 *p, struct nfs3_readdirargs *args)
+{
+ struct rpc_auth *auth = req->rq_task->tk_auth;
+ unsigned int replen;
+ u32 count = args->count;
+
+ p = xdr_encode_fhandle(p, args->fh);
+ p = xdr_encode_hyper(p, args->cookie);
+ *p++ = args->verf[0];
+ *p++ = args->verf[1];
+ if (args->plus) {
+ /* readdirplus: need dircount + buffer size.
+ * We just make sure we make dircount big enough */
+ *p++ = htonl(count >> 3);
+ }
+ *p++ = htonl(count);
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+
+ /* Inline the page array */
+ replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readdirres_sz) << 2;
+ xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0, count);
+ return 0;
+}
+
+/*
+ * Decode the result of a readdir call.
+ * We just check for syntactical correctness.
+ */
+static int
+nfs3_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs3_readdirres *res)
+{
+ struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
+ struct iovec *iov = rcvbuf->head;
+ struct page **page;
+ int hdrlen, recvd;
+ int status, nr;
+ unsigned int len, pglen;
+ u32 *entry, *end;
+
+ status = ntohl(*p++);
+ /* Decode post_op_attrs */
+ p = xdr_decode_post_op_attr(p, res->dir_attr);
+ if (status)
+ return -nfs_stat_to_errno(status);
+ /* Decode verifier cookie */
+ if (res->verf) {
+ res->verf[0] = *p++;
+ res->verf[1] = *p++;
+ } else {
+ p += 2;
+ }
+
+ hdrlen = (u8 *) p - (u8 *) iov->iov_base;
+ if (iov->iov_len < hdrlen) {
+ printk(KERN_WARNING "NFS: READDIR reply header overflowed:"
+ "length %d > %Zu\n", hdrlen, iov->iov_len);
+ return -errno_NFSERR_IO;
+ } else if (iov->iov_len != hdrlen) {
+ dprintk("NFS: READDIR header is short. iovec will be shifted.\n");
+ xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
+ }
+
+ pglen = rcvbuf->page_len;
+ recvd = req->rq_received - hdrlen;
+ if (pglen > recvd)
+ pglen = recvd;
+ page = rcvbuf->pages;
+ p = kmap(*page);
+ end = (u32 *)((char *)p + pglen);
+ entry = p;
+ for (nr = 0; *p++; nr++) {
+ if (p + 3 > end)
+ goto short_pkt;
+ p += 2; /* inode # */
+ len = ntohl(*p++); /* string length */
+ p += XDR_QUADLEN(len) + 2; /* name + cookie */
+ if (len > NFS3_MAXNAMLEN) {
+ printk(KERN_WARNING "NFS: giant filename in readdir (len %x)!\n",
+ len);
+ goto err_unmap;
+ }
+
+ if (res->plus) {
+ /* post_op_attr */
+ if (p > end)
+ goto short_pkt;
+ if (*p++) {
+ p += 21;
+ if (p > end)
+ goto short_pkt;
+ }
+ /* post_op_fh3 */
+ if (*p++) {
+ if (p > end)
+ goto short_pkt;
+ len = ntohl(*p++);
+ if (len > NFS3_FHSIZE) {
+ printk(KERN_WARNING "NFS: giant filehandle in "
+ "readdir (len %x)!\n", len);
+ goto err_unmap;
+ }
+ p += XDR_QUADLEN(len);
+ }
+ }
+
+ if (p + 2 > end)
+ goto short_pkt;
+ entry = p;
+ }
+ if (!nr && (entry[0] != 0 || entry[1] == 0))
+ goto short_pkt;
+ out:
+ kunmap(*page);
+ return nr;
+ short_pkt:
+ entry[0] = entry[1] = 0;
+ /* truncate listing ? */
+ if (!nr) {
+ printk(KERN_NOTICE "NFS: readdir reply truncated!\n");
+ entry[1] = 1;
+ }
+ goto out;
+err_unmap:
+ kunmap(*page);
+ return -errno_NFSERR_IO;
+}
+
+u32 *
+nfs3_decode_dirent(u32 *p, struct nfs_entry *entry, int plus)
+{
+ if (!*p++) {
+ if (!*p)
+ return ERR_PTR(-EAGAIN);
+ entry->eof = 1;
+ return ERR_PTR(-EBADCOOKIE);
+ }
+
+ p = xdr_decode_hyper(p, &entry->ino);
+ entry->len = ntohl(*p++);
+ entry->name = (const char *) p;
+ p += XDR_QUADLEN(entry->len);
+ entry->prev_cookie = entry->cookie;
+ p = xdr_decode_hyper(p, &entry->cookie);
+
+ if (plus) {
+ struct nfs_fattr fattr;
+ p = xdr_decode_post_op_attr(p, &fattr);
+ /* In fact, a post_op_fh3: */
+ if (*p++) {
+ struct nfs_fh fh;
+ p = xdr_decode_fhandle(p, &fh);
+ }
+ }
+
+ entry->eof = !p[0] && p[1];
+ return p;
+}
+
+/*
+ * Encode COMMIT arguments
+ */
+static int
+nfs3_xdr_commitargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args)
+{
+ p = xdr_encode_fhandle(p, args->fh);
+ p = xdr_encode_hyper(p, args->offset);
+ *p++ = htonl(args->count);
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+/*
+ * NFS XDR decode functions
+ */
+/*
+ * Decode void reply
+ */
+static int
+nfs3_xdr_dec_void(struct rpc_rqst *req, u32 *p, void *dummy)
+{
+ return 0;
+}
+
+/*
+ * Decode attrstat reply.
+ */
+static int
+nfs3_xdr_attrstat(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
+{
+ int status;
+
+ if ((status = ntohl(*p++)))
+ return -nfs_stat_to_errno(status);
+ xdr_decode_fattr(p, fattr);
+ return 0;
+}
+
+/*
+ * Decode status+wcc_data reply
+ * SATTR, REMOVE, RMDIR
+ */
+static int
+nfs3_xdr_wccstat(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
+{
+ int status;
+
+ if ((status = ntohl(*p++)))
+ status = -nfs_stat_to_errno(status);
+ xdr_decode_wcc_data(p, fattr);
+ return status;
+}
+
+/*
+ * Decode LOOKUP reply
+ */
+static int
+nfs3_xdr_lookupres(struct rpc_rqst *req, u32 *p, struct nfs3_diropres *res)
+{
+ int status;
+
+ if ((status = ntohl(*p++))) {
+ status = -nfs_stat_to_errno(status);
+ } else {
+ if (!(p = xdr_decode_fhandle(p, res->fh)))
+ return -errno_NFSERR_IO;
+ p = xdr_decode_post_op_attr(p, res->fattr);
+ }
+ xdr_decode_post_op_attr(p, res->dir_attr);
+ return status;
+}
+
+/*
+ * Decode ACCESS reply
+ */
+static int
+nfs3_xdr_accessres(struct rpc_rqst *req, u32 *p, struct nfs3_accessres *res)
+{
+ int status = ntohl(*p++);
+
+ p = xdr_decode_post_op_attr(p, res->fattr);
+ if (status)
+ return -nfs_stat_to_errno(status);
+ res->access = ntohl(*p++);
+ return 0;
+}
+
+static int
+nfs3_xdr_readlinkargs(struct rpc_rqst *req, u32 *p, struct nfs3_readlinkargs *args)
+{
+ struct rpc_auth *auth = req->rq_task->tk_auth;
+ unsigned int replen;
+ u32 count = args->count - 4;
+
+ p = xdr_encode_fhandle(p, args->fh);
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+
+ /* Inline the page array */
+ replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readlinkres_sz) << 2;
+ xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0, count);
+ return 0;
+}
+
+/*
+ * Decode READLINK reply
+ */
+static int
+nfs3_xdr_readlinkres(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
+{
+ struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
+ struct iovec *iov = rcvbuf->head;
+ unsigned int hdrlen;
+ u32 *strlen, len;
+ char *string;
+ int status;
+
+ status = ntohl(*p++);
+ p = xdr_decode_post_op_attr(p, fattr);
+
+ if (status != 0)
+ return -nfs_stat_to_errno(status);
+
+ hdrlen = (u8 *) p - (u8 *) iov->iov_base;
+ if (iov->iov_len > hdrlen) {
+ dprintk("NFS: READLINK header is short. iovec will be shifted.\n");
+ xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
+ }
+
+ strlen = (u32*)kmap(rcvbuf->pages[0]);
+ /* Convert length of symlink */
+ len = ntohl(*strlen);
+ if (len > rcvbuf->page_len)
+ len = rcvbuf->page_len;
+ *strlen = len;
+ /* NULL terminate the string we got */
+ string = (char *)(strlen + 1);
+ string[len] = 0;
+ kunmap(rcvbuf->pages[0]);
+ return 0;
+}
+
+/*
+ * Decode READ reply
+ */
+static int
+nfs3_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res)
+{
+ struct iovec *iov = req->rq_rvec;
+ int status, count, ocount, recvd, hdrlen;
+
+ status = ntohl(*p++);
+ p = xdr_decode_post_op_attr(p, res->fattr);
+
+ if (status != 0)
+ return -nfs_stat_to_errno(status);
+
+ /* Decode reply could and EOF flag. NFSv3 is somewhat redundant
+ * in that it puts the count both in the res struct and in the
+ * opaque data count. */
+ count = ntohl(*p++);
+ res->eof = ntohl(*p++);
+ ocount = ntohl(*p++);
+
+ if (ocount != count) {
+ printk(KERN_WARNING "NFS: READ count doesn't match RPC opaque count.\n");
+ return -errno_NFSERR_IO;
+ }
+
+ hdrlen = (u8 *) p - (u8 *) iov->iov_base;
+ if (iov->iov_len < hdrlen) {
+ printk(KERN_WARNING "NFS: READ reply header overflowed:"
+ "length %d > %Zu\n", hdrlen, iov->iov_len);
+ return -errno_NFSERR_IO;
+ } else if (iov->iov_len != hdrlen) {
+ dprintk("NFS: READ header is short. iovec will be shifted.\n");
+ xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen);
+ }
+
+ recvd = req->rq_received - hdrlen;
+ if (count > recvd) {
+ printk(KERN_WARNING "NFS: server cheating in read reply: "
+ "count %d > recvd %d\n", count, recvd);
+ count = recvd;
+ res->eof = 0;
+ }
+
+ if (count < res->count)
+ res->count = count;
+
+ return count;
+}
+
+/*
+ * Decode WRITE response
+ */
+static int
+nfs3_xdr_writeres(struct rpc_rqst *req, u32 *p, struct nfs_writeres *res)
+{
+ int status;
+
+ status = ntohl(*p++);
+ p = xdr_decode_wcc_data(p, res->fattr);
+
+ if (status != 0)
+ return -nfs_stat_to_errno(status);
+
+ res->count = ntohl(*p++);
+ res->verf->committed = (enum nfs3_stable_how)ntohl(*p++);
+ res->verf->verifier[0] = *p++;
+ res->verf->verifier[1] = *p++;
+
+ return res->count;
+}
+
+/*
+ * Decode a CREATE response
+ */
+static int
+nfs3_xdr_createres(struct rpc_rqst *req, u32 *p, struct nfs3_diropres *res)
+{
+ int status;
+
+ status = ntohl(*p++);
+ if (status == 0) {
+ if (*p++) {
+ if (!(p = xdr_decode_fhandle(p, res->fh)))
+ return -errno_NFSERR_IO;
+ p = xdr_decode_post_op_attr(p, res->fattr);
+ } else {
+ memset(res->fh, 0, sizeof(*res->fh));
+ /* Do decode post_op_attr but set it to NULL */
+ p = xdr_decode_post_op_attr(p, res->fattr);
+ res->fattr->valid = 0;
+ }
+ } else {
+ status = -nfs_stat_to_errno(status);
+ }
+ p = xdr_decode_wcc_data(p, res->dir_attr);
+ return status;
+}
+
+/*
+ * Decode RENAME reply
+ */
+static int
+nfs3_xdr_renameres(struct rpc_rqst *req, u32 *p, struct nfs3_renameres *res)
+{
+ int status;
+
+ if ((status = ntohl(*p++)) != 0)
+ status = -nfs_stat_to_errno(status);
+ p = xdr_decode_wcc_data(p, res->fromattr);
+ p = xdr_decode_wcc_data(p, res->toattr);
+ return status;
+}
+
+/*
+ * Decode LINK reply
+ */
+static int
+nfs3_xdr_linkres(struct rpc_rqst *req, u32 *p, struct nfs3_linkres *res)
+{
+ int status;
+
+ if ((status = ntohl(*p++)) != 0)
+ status = -nfs_stat_to_errno(status);
+ p = xdr_decode_post_op_attr(p, res->fattr);
+ p = xdr_decode_wcc_data(p, res->dir_attr);
+ return status;
+}
+
+/*
+ * Decode FSSTAT reply
+ */
+static int
+nfs3_xdr_fsstatres(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res)
+{
+ struct nfs_fattr dummy;
+ int status;
+
+ status = ntohl(*p++);
+
+ p = xdr_decode_post_op_attr(p, &dummy);
+ if (status != 0)
+ return -nfs_stat_to_errno(status);
+
+ p = xdr_decode_hyper(p, &res->tbytes);
+ p = xdr_decode_hyper(p, &res->fbytes);
+ p = xdr_decode_hyper(p, &res->abytes);
+ p = xdr_decode_hyper(p, &res->tfiles);
+ p = xdr_decode_hyper(p, &res->ffiles);
+ p = xdr_decode_hyper(p, &res->afiles);
+
+ /* ignore invarsec */
+ return 0;
+}
+
+/*
+ * Decode FSINFO reply
+ */
+static int
+nfs3_xdr_fsinfores(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res)
+{
+ struct nfs_fattr dummy;
+ int status;
+
+ status = ntohl(*p++);
+
+ p = xdr_decode_post_op_attr(p, &dummy);
+ if (status != 0)
+ return -nfs_stat_to_errno(status);
+
+ res->rtmax = ntohl(*p++);
+ res->rtpref = ntohl(*p++);
+ res->rtmult = ntohl(*p++);
+ res->wtmax = ntohl(*p++);
+ res->wtpref = ntohl(*p++);
+ res->wtmult = ntohl(*p++);
+ res->dtpref = ntohl(*p++);
+ p = xdr_decode_hyper(p, &res->maxfilesize);
+
+ /* ignore time_delta and properties */
+ return 0;
+}
+
+/*
+ * Decode PATHCONF reply
+ */
+static int
+nfs3_xdr_pathconfres(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res)
+{
+ struct nfs_fattr dummy;
+ int status;
+
+ status = ntohl(*p++);
+
+ p = xdr_decode_post_op_attr(p, &dummy);
+ if (status != 0)
+ return -nfs_stat_to_errno(status);
+ res->linkmax = ntohl(*p++);
+ res->namelen = ntohl(*p++);
+
+ /* ignore remaining fields */
+ return 0;
+}
+
+/*
+ * Decode COMMIT reply
+ */
+static int
+nfs3_xdr_commitres(struct rpc_rqst *req, u32 *p, struct nfs_writeres *res)
+{
+ int status;
+
+ status = ntohl(*p++);
+ p = xdr_decode_wcc_data(p, res->fattr);
+ if (status != 0)
+ return -nfs_stat_to_errno(status);
+
+ res->verf->verifier[0] = *p++;
+ res->verf->verifier[1] = *p++;
+ return 0;
+}
+
+#ifndef MAX
+# define MAX(a, b) (((a) > (b))? (a) : (b))
+#endif
+
+#define PROC(proc, argtype, restype, timer) \
+ { .p_procname = "nfs3_" #proc, \
+ .p_encode = (kxdrproc_t) nfs3_xdr_##argtype, \
+ .p_decode = (kxdrproc_t) nfs3_xdr_##restype, \
+ .p_bufsiz = MAX(NFS3_##argtype##_sz,NFS3_##restype##_sz) << 2, \
+ .p_timer = timer \
+ }
+
+static struct rpc_procinfo nfs3_procedures[22] = {
+ PROC(null, enc_void, dec_void, 0),
+ PROC(getattr, fhandle, attrstat, 1),
+ PROC(setattr, sattrargs, wccstat, 0),
+ PROC(lookup, diropargs, lookupres, 2),
+ PROC(access, accessargs, accessres, 1),
+ PROC(readlink, readlinkargs, readlinkres, 3),
+ PROC(read, readargs, readres, 3),
+ PROC(write, writeargs, writeres, 4),
+ PROC(create, createargs, createres, 0),
+ PROC(mkdir, mkdirargs, createres, 0),
+ PROC(symlink, symlinkargs, createres, 0),
+ PROC(mknod, mknodargs, createres, 0),
+ PROC(remove, diropargs, wccstat, 0),
+ PROC(rmdir, diropargs, wccstat, 0),
+ PROC(rename, renameargs, renameres, 0),
+ PROC(link, linkargs, linkres, 0),
+ PROC(readdir, readdirargs, readdirres, 3),
+ PROC(readdirplus, readdirargs, readdirres, 3),
+ PROC(fsstat, fhandle, fsstatres, 0),
+ PROC(fsinfo, fhandle, fsinfores, 0),
+ PROC(pathconf, fhandle, pathconfres, 0),
+ PROC(commit, commitargs, commitres, 5),
+};
+
+struct rpc_version nfs_version3 = {
+ 3,
+ sizeof(nfs3_procedures)/sizeof(nfs3_procedures[0]),
+ nfs3_procedures
+};
+
diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c
new file mode 100644
index 00000000000000..ccd266bf37d891
--- /dev/null
+++ b/fs/nfs/nfsroot.c
@@ -0,0 +1,440 @@
+/*
+ * $Id: nfsroot.c,v 1.45 1998/03/07 10:44:46 mj Exp $
+ *
+ * Copyright (C) 1995, 1996 Gero Kuhlmann <gero@gkminix.han.de>
+ *
+ * Allow an NFS filesystem to be mounted as root. The way this works is:
+ * (1) Use the IP autoconfig mechanism to set local IP addresses and routes.
+ * (2) Handle RPC negotiation with the system which replied to RARP or
+ * was reported as a boot server by BOOTP or manually.
+ * (3) The actual mounting is done later, when init() is running.
+ *
+ *
+ * Changes:
+ *
+ * Alan Cox : Removed get_address name clash with FPU.
+ * Alan Cox : Reformatted a bit.
+ * Gero Kuhlmann : Code cleanup
+ * Michael Rausch : Fixed recognition of an incoming RARP answer.
+ * Martin Mares : (2.0) Auto-configuration via BOOTP supported.
+ * Martin Mares : Manual selection of interface & BOOTP/RARP.
+ * Martin Mares : Using network routes instead of host routes,
+ * allowing the default configuration to be used
+ * for normal operation of the host.
+ * Martin Mares : Randomized timer with exponential backoff
+ * installed to minimize network congestion.
+ * Martin Mares : Code cleanup.
+ * Martin Mares : (2.1) BOOTP and RARP made configuration options.
+ * Martin Mares : Server hostname generation fixed.
+ * Gerd Knorr : Fixed wired inode handling
+ * Martin Mares : (2.2) "0.0.0.0" addresses from command line ignored.
+ * Martin Mares : RARP replies not tested for server address.
+ * Gero Kuhlmann : (2.3) Some bug fixes and code cleanup again (please
+ * send me your new patches _before_ bothering
+ * Linus so that I don' always have to cleanup
+ * _afterwards_ - thanks)
+ * Gero Kuhlmann : Last changes of Martin Mares undone.
+ * Gero Kuhlmann : RARP replies are tested for specified server
+ * again. However, it's now possible to have
+ * different RARP and NFS servers.
+ * Gero Kuhlmann : "0.0.0.0" addresses from command line are
+ * now mapped to INADDR_NONE.
+ * Gero Kuhlmann : Fixed a bug which prevented BOOTP path name
+ * from being used (thanks to Leo Spiekman)
+ * Andy Walker : Allow to specify the NFS server in nfs_root
+ * without giving a path name
+ * Swen Thümmler : Allow to specify the NFS options in nfs_root
+ * without giving a path name. Fix BOOTP request
+ * for domainname (domainname is NIS domain, not
+ * DNS domain!). Skip dummy devices for BOOTP.
+ * Jacek Zapala : Fixed a bug which prevented server-ip address
+ * from nfsroot parameter from being used.
+ * Olaf Kirch : Adapted to new NFS code.
+ * Jakub Jelinek : Free used code segment.
+ * Marko Kohtala : Fixed some bugs.
+ * Martin Mares : Debug message cleanup
+ * Martin Mares : Changed to use the new generic IP layer autoconfig
+ * code. BOOTP and RARP moved there.
+ * Martin Mares : Default path now contains host name instead of
+ * host IP address (but host name defaults to IP
+ * address anyway).
+ * Martin Mares : Use root_server_addr appropriately during setup.
+ * Martin Mares : Rewrote parameter parsing, now hopefully giving
+ * correct overriding.
+ * Trond Myklebust : Add in preliminary support for NFSv3 and TCP.
+ * Fix bug in root_nfs_addr(). nfs_data.namlen
+ * is NOT for the length of the hostname.
+ * Hua Qin : Finish support for mounting root file system
+ * via NFS over TCP.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/nfs.h>
+#include <linux/nfs_fs.h>
+#include <linux/nfs_mount.h>
+#include <linux/in.h>
+#include <linux/major.h>
+#include <linux/utsname.h>
+#include <net/ipconfig.h>
+
+/* Define this to allow debugging output */
+#undef NFSROOT_DEBUG
+#define NFSDBG_FACILITY NFSDBG_ROOT
+
+/* Default path we try to mount. "%s" gets replaced by our IP address */
+#define NFS_ROOT "/tftpboot/%s"
+
+/* Parameters passed from the kernel command line */
+static char nfs_root_name[256] __initdata = "";
+
+/* Address of NFS server */
+static __u32 servaddr __initdata = 0;
+
+/* Name of directory to mount */
+static char nfs_path[NFS_MAXPATHLEN] __initdata = { 0, };
+
+/* NFS-related data */
+static struct nfs_mount_data nfs_data __initdata = { 0, };/* NFS mount info */
+static int nfs_port __initdata = 0; /* Port to connect to for NFS */
+static int mount_port __initdata = 0; /* Mount daemon port number */
+
+
+/***************************************************************************
+
+ Parsing of options
+
+ ***************************************************************************/
+
+/*
+ * The following integer options are recognized
+ */
+static struct nfs_int_opts {
+ char *name;
+ int *val;
+} root_int_opts[] __initdata = {
+ { "port", &nfs_port },
+ { "rsize", &nfs_data.rsize },
+ { "wsize", &nfs_data.wsize },
+ { "timeo", &nfs_data.timeo },
+ { "retrans", &nfs_data.retrans },
+ { "acregmin", &nfs_data.acregmin },
+ { "acregmax", &nfs_data.acregmax },
+ { "acdirmin", &nfs_data.acdirmin },
+ { "acdirmax", &nfs_data.acdirmax },
+ { NULL, NULL }
+};
+
+
+/*
+ * And now the flag options
+ */
+static struct nfs_bool_opts {
+ char *name;
+ int and_mask;
+ int or_mask;
+} root_bool_opts[] __initdata = {
+ { "soft", ~NFS_MOUNT_SOFT, NFS_MOUNT_SOFT },
+ { "hard", ~NFS_MOUNT_SOFT, 0 },
+ { "intr", ~NFS_MOUNT_INTR, NFS_MOUNT_INTR },
+ { "nointr", ~NFS_MOUNT_INTR, 0 },
+ { "posix", ~NFS_MOUNT_POSIX, NFS_MOUNT_POSIX },
+ { "noposix", ~NFS_MOUNT_POSIX, 0 },
+ { "cto", ~NFS_MOUNT_NOCTO, 0 },
+ { "nocto", ~NFS_MOUNT_NOCTO, NFS_MOUNT_NOCTO },
+ { "ac", ~NFS_MOUNT_NOAC, 0 },
+ { "noac", ~NFS_MOUNT_NOAC, NFS_MOUNT_NOAC },
+ { "lock", ~NFS_MOUNT_NONLM, 0 },
+ { "nolock", ~NFS_MOUNT_NONLM, NFS_MOUNT_NONLM },
+#ifdef CONFIG_NFS_V3
+ { "v2", ~NFS_MOUNT_VER3, 0 },
+ { "v3", ~NFS_MOUNT_VER3, NFS_MOUNT_VER3 },
+#endif
+ { "udp", ~NFS_MOUNT_TCP, 0 },
+ { "tcp", ~NFS_MOUNT_TCP, NFS_MOUNT_TCP },
+ { "broken_suid",~NFS_MOUNT_BROKEN_SUID, NFS_MOUNT_BROKEN_SUID },
+ { NULL, 0, 0 }
+};
+
+
+/*
+ * Parse option string.
+ */
+static void __init root_nfs_parse(char *name, char *buf)
+{
+ char *options, *val, *cp;
+
+ if ((options = strchr(name, ','))) {
+ *options++ = 0;
+ cp = strtok(options, ",");
+ while (cp) {
+ if ((val = strchr(cp, '='))) {
+ struct nfs_int_opts *opts = root_int_opts;
+ *val++ = '\0';
+ while (opts->name && strcmp(opts->name, cp))
+ opts++;
+ if (opts->name)
+ *(opts->val) = (int) simple_strtoul(val, NULL, 10);
+ } else {
+ struct nfs_bool_opts *opts = root_bool_opts;
+ while (opts->name && strcmp(opts->name, cp))
+ opts++;
+ if (opts->name) {
+ nfs_data.flags &= opts->and_mask;
+ nfs_data.flags |= opts->or_mask;
+ }
+ }
+ cp = strtok(NULL, ",");
+ }
+ }
+ if (name[0] && strcmp(name, "default")) {
+ strncpy(buf, name, NFS_MAXPATHLEN-1);
+ buf[NFS_MAXPATHLEN-1] = 0;
+ }
+}
+
+
+/*
+ * Prepare the NFS data structure and parse all options.
+ */
+static int __init root_nfs_name(char *name)
+{
+ char buf[NFS_MAXPATHLEN];
+ char *cp;
+
+ /* Set some default values */
+ memset(&nfs_data, 0, sizeof(nfs_data));
+ nfs_port = -1;
+ nfs_data.version = NFS_MOUNT_VERSION;
+ nfs_data.flags = NFS_MOUNT_NONLM; /* No lockd in nfs root yet */
+ nfs_data.rsize = NFS_DEF_FILE_IO_BUFFER_SIZE;
+ nfs_data.wsize = NFS_DEF_FILE_IO_BUFFER_SIZE;
+ nfs_data.bsize = 0;
+ nfs_data.timeo = 7;
+ nfs_data.retrans = 3;
+ nfs_data.acregmin = 3;
+ nfs_data.acregmax = 60;
+ nfs_data.acdirmin = 30;
+ nfs_data.acdirmax = 60;
+ strcpy(buf, NFS_ROOT);
+
+ /* Process options received from the remote server */
+ root_nfs_parse(root_server_path, buf);
+
+ /* Override them by options set on kernel command-line */
+ root_nfs_parse(name, buf);
+
+ cp = system_utsname.nodename;
+ if (strlen(buf) + strlen(cp) > NFS_MAXPATHLEN) {
+ printk(KERN_ERR "Root-NFS: Pathname for remote directory too long.\n");
+ return -1;
+ }
+ sprintf(nfs_path, buf, cp);
+
+ return 1;
+}
+
+
+/*
+ * Get NFS server address.
+ */
+static int __init root_nfs_addr(void)
+{
+ if ((servaddr = root_server_addr) == INADDR_NONE) {
+ printk(KERN_ERR "Root-NFS: No NFS server available, giving up.\n");
+ return -1;
+ }
+
+ snprintf(nfs_data.hostname, sizeof(nfs_data.hostname),
+ "%u.%u.%u.%u", NIPQUAD(servaddr));
+ return 0;
+}
+
+/*
+ * Tell the user what's going on.
+ */
+#ifdef NFSROOT_DEBUG
+static void __init root_nfs_print(void)
+{
+ printk(KERN_NOTICE "Root-NFS: Mounting %s on server %s as root\n",
+ nfs_path, nfs_data.hostname);
+ printk(KERN_NOTICE "Root-NFS: rsize = %d, wsize = %d, timeo = %d, retrans = %d\n",
+ nfs_data.rsize, nfs_data.wsize, nfs_data.timeo, nfs_data.retrans);
+ printk(KERN_NOTICE "Root-NFS: acreg (min,max) = (%d,%d), acdir (min,max) = (%d,%d)\n",
+ nfs_data.acregmin, nfs_data.acregmax,
+ nfs_data.acdirmin, nfs_data.acdirmax);
+ printk(KERN_NOTICE "Root-NFS: nfsd port = %d, mountd port = %d, flags = %08x\n",
+ nfs_port, mount_port, nfs_data.flags);
+}
+#endif
+
+
+int __init root_nfs_init(void)
+{
+#ifdef NFSROOT_DEBUG
+ nfs_debug |= NFSDBG_ROOT;
+#endif
+
+ /*
+ * Decode the root directory path name and NFS options from
+ * the kernel command line. This has to go here in order to
+ * be able to use the client IP address for the remote root
+ * directory (necessary for pure RARP booting).
+ */
+ if (root_nfs_name(nfs_root_name) < 0 ||
+ root_nfs_addr() < 0)
+ return -1;
+
+#ifdef NFSROOT_DEBUG
+ root_nfs_print();
+#endif
+
+ return 0;
+}
+
+
+/*
+ * Parse NFS server and directory information passed on the kernel
+ * command line.
+ */
+int __init nfs_root_setup(char *line)
+{
+ ROOT_DEV = MKDEV(NFS_MAJOR, NFS_MINOR);
+ if (line[0] == '/' || line[0] == ',' || (line[0] >= '0' && line[0] <= '9')) {
+ strncpy(nfs_root_name, line, sizeof(nfs_root_name));
+ nfs_root_name[sizeof(nfs_root_name)-1] = '\0';
+ } else {
+ int n = strlen(line) + strlen(NFS_ROOT);
+ if (n >= sizeof(nfs_root_name))
+ line[sizeof(nfs_root_name) - strlen(NFS_ROOT) - 1] = '\0';
+ sprintf(nfs_root_name, NFS_ROOT, line);
+ }
+ root_server_addr = root_nfs_parse_addr(nfs_root_name);
+ return 1;
+}
+
+__setup("nfsroot=", nfs_root_setup);
+
+/***************************************************************************
+
+ Routines to actually mount the root directory
+
+ ***************************************************************************/
+
+/*
+ * Construct sockaddr_in from address and port number.
+ */
+static inline void
+set_sockaddr(struct sockaddr_in *sin, __u32 addr, __u16 port)
+{
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = addr;
+ sin->sin_port = port;
+}
+
+/*
+ * Query server portmapper for the port of a daemon program.
+ */
+static int __init root_nfs_getport(int program, int version, int proto)
+{
+ struct sockaddr_in sin;
+
+ printk(KERN_NOTICE "Looking up port of RPC %d/%d on %u.%u.%u.%u\n",
+ program, version, NIPQUAD(servaddr));
+ set_sockaddr(&sin, servaddr, 0);
+ return rpc_getport_external(&sin, program, version, proto);
+}
+
+
+/*
+ * Use portmapper to find mountd and nfsd port numbers if not overriden
+ * by the user. Use defaults if portmapper is not available.
+ * XXX: Is there any nfs server with no portmapper?
+ */
+static int __init root_nfs_ports(void)
+{
+ int port;
+ int nfsd_ver, mountd_ver;
+ int nfsd_port, mountd_port;
+ int proto;
+
+ if (nfs_data.flags & NFS_MOUNT_VER3) {
+ nfsd_ver = NFS3_VERSION;
+ mountd_ver = NFS_MNT3_VERSION;
+ nfsd_port = NFS_PORT;
+ mountd_port = NFS_MNT_PORT;
+ } else {
+ nfsd_ver = NFS2_VERSION;
+ mountd_ver = NFS_MNT_VERSION;
+ nfsd_port = NFS_PORT;
+ mountd_port = NFS_MNT_PORT;
+ }
+
+ proto = (nfs_data.flags & NFS_MOUNT_TCP) ? IPPROTO_TCP : IPPROTO_UDP;
+
+ if (nfs_port < 0) {
+ if ((port = root_nfs_getport(NFS_PROGRAM, nfsd_ver, proto)) < 0) {
+ printk(KERN_ERR "Root-NFS: Unable to get nfsd port "
+ "number from server, using default\n");
+ port = nfsd_port;
+ }
+ nfs_port = port;
+ dprintk("Root-NFS: Portmapper on server returned %d "
+ "as nfsd port\n", port);
+ }
+ nfs_port = htons(nfs_port);
+
+ if ((port = root_nfs_getport(NFS_MNT_PROGRAM, mountd_ver, proto)) < 0) {
+ printk(KERN_ERR "Root-NFS: Unable to get mountd port "
+ "number from server, using default\n");
+ port = mountd_port;
+ }
+ mount_port = htons(port);
+ dprintk("Root-NFS: mountd port is %d\n", port);
+
+ return 0;
+}
+
+
+/*
+ * Get a file handle from the server for the directory which is to be
+ * mounted.
+ */
+static int __init root_nfs_get_handle(void)
+{
+ struct sockaddr_in sin;
+ int status;
+ int protocol = (nfs_data.flags & NFS_MOUNT_TCP) ?
+ IPPROTO_TCP : IPPROTO_UDP;
+ int version = (nfs_data.flags & NFS_MOUNT_VER3) ?
+ NFS_MNT3_VERSION : NFS_MNT_VERSION;
+
+ set_sockaddr(&sin, servaddr, mount_port);
+ status = nfsroot_mount(&sin, nfs_path, &nfs_data.root,
+ version, protocol);
+ if (status < 0)
+ printk(KERN_ERR "Root-NFS: Server returned error %d "
+ "while mounting %s\n", status, nfs_path);
+
+ return status;
+}
+
+/*
+ * Get the NFS port numbers and file handle, and return the prepared 'data'
+ * argument for ->read_super() if everything went OK. Return NULL otherwise.
+ */
+void * __init nfs_root_data(void)
+{
+ if (root_nfs_init() < 0
+ || root_nfs_ports() < 0
+ || root_nfs_get_handle() < 0)
+ return NULL;
+ set_sockaddr((struct sockaddr_in *) &nfs_data.addr, servaddr, nfs_port);
+ return (void*)&nfs_data;
+}
diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c
new file mode 100644
index 00000000000000..4a7cd20663cf3b
--- /dev/null
+++ b/fs/nfs/pagelist.c
@@ -0,0 +1,500 @@
+/*
+ * linux/fs/nfs/pagelist.c
+ *
+ * A set of helper functions for managing NFS read and write requests.
+ * The main purpose of these routines is to provide support for the
+ * coalescing of several requests into a single RPC call.
+ *
+ * Copyright 2000, 2001 (c) Trond Myklebust <trond.myklebust@fys.uio.no>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/slab.h>
+#include <linux/file.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/nfs3.h>
+#include <linux/nfs_page.h>
+#include <linux/nfs_fs.h>
+#include <linux/nfs_flushd.h>
+#include <linux/nfs_mount.h>
+
+#define NFS_PARANOIA 1
+
+/*
+ * Spinlock
+ */
+spinlock_t nfs_wreq_lock = SPIN_LOCK_UNLOCKED;
+
+static kmem_cache_t *nfs_page_cachep;
+
+static inline struct nfs_page *
+nfs_page_alloc(void)
+{
+ struct nfs_page *p;
+ p = kmem_cache_alloc(nfs_page_cachep, SLAB_NOFS);
+ if (p) {
+ memset(p, 0, sizeof(*p));
+ INIT_LIST_HEAD(&p->wb_hash);
+ INIT_LIST_HEAD(&p->wb_list);
+ INIT_LIST_HEAD(&p->wb_lru);
+ init_waitqueue_head(&p->wb_wait);
+ }
+ return p;
+}
+
+static inline void
+nfs_page_free(struct nfs_page *p)
+{
+ kmem_cache_free(nfs_page_cachep, p);
+}
+
+static int nfs_try_to_free_pages(struct nfs_server *);
+
+/**
+ * nfs_create_request - Create an NFS read/write request.
+ * @cred: RPC credential to use
+ * @inode: inode to which the request is attached
+ * @page: page to write
+ * @offset: starting offset within the page for the write
+ * @count: number of bytes to read/write
+ *
+ * The page must be locked by the caller. This makes sure we never
+ * create two different requests for the same page, and avoids
+ * a possible deadlock when we reach the hard limit on the number
+ * of dirty pages.
+ * User should ensure it is safe to sleep in this function.
+ */
+struct nfs_page *
+nfs_create_request(struct rpc_cred *cred, struct inode *inode,
+ struct page *page,
+ unsigned int offset, unsigned int count)
+{
+ struct nfs_server *server = NFS_SERVER(inode);
+ struct nfs_reqlist *cache = NFS_REQUESTLIST(inode);
+ struct nfs_page *req;
+
+ /* Deal with hard limits. */
+ for (;;) {
+ /* Prevent races by incrementing *before* we test */
+ atomic_inc(&cache->nr_requests);
+
+ /* If we haven't reached the local hard limit yet,
+ * try to allocate the request struct */
+ if (atomic_read(&cache->nr_requests) <= MAX_REQUEST_HARD) {
+ req = nfs_page_alloc();
+ if (req != NULL)
+ break;
+ }
+
+ atomic_dec(&cache->nr_requests);
+
+ /* Try to free up at least one request in order to stay
+ * below the hard limit
+ */
+ if (nfs_try_to_free_pages(server))
+ continue;
+ if (signalled() && (server->flags & NFS_MOUNT_INTR))
+ return ERR_PTR(-ERESTARTSYS);
+ yield();
+ }
+
+ /* Initialize the request struct. Initially, we assume a
+ * long write-back delay. This will be adjusted in
+ * update_nfs_request below if the region is not locked. */
+ req->wb_page = page;
+ page_cache_get(page);
+ req->wb_offset = offset;
+ req->wb_bytes = count;
+
+ if (cred)
+ req->wb_cred = get_rpccred(cred);
+ req->wb_inode = inode;
+ req->wb_count = 1;
+
+ return req;
+}
+
+/**
+ * nfs_clear_request - Free up all resources allocated to the request
+ * @req:
+ *
+ * Release all resources associated with a write request after it
+ * has completed.
+ */
+void nfs_clear_request(struct nfs_page *req)
+{
+ /* Release struct file or cached credential */
+ if (req->wb_file) {
+ fput(req->wb_file);
+ req->wb_file = NULL;
+ }
+ if (req->wb_cred) {
+ put_rpccred(req->wb_cred);
+ req->wb_cred = NULL;
+ }
+ if (req->wb_page) {
+ atomic_dec(&NFS_REQUESTLIST(req->wb_inode)->nr_requests);
+#ifdef NFS_PARANOIA
+ BUG_ON(atomic_read(&NFS_REQUESTLIST(req->wb_inode)->nr_requests) < 0);
+#endif
+ page_cache_release(req->wb_page);
+ req->wb_page = NULL;
+ }
+}
+
+
+/**
+ * nfs_release_request - Release the count on an NFS read/write request
+ * @req: request to release
+ *
+ * Note: Should never be called with the spinlock held!
+ */
+void
+nfs_release_request(struct nfs_page *req)
+{
+ spin_lock(&nfs_wreq_lock);
+ if (--req->wb_count) {
+ spin_unlock(&nfs_wreq_lock);
+ return;
+ }
+ __nfs_del_lru(req);
+ spin_unlock(&nfs_wreq_lock);
+
+#ifdef NFS_PARANOIA
+ BUG_ON(!list_empty(&req->wb_list));
+ BUG_ON(!list_empty(&req->wb_hash));
+ BUG_ON(NFS_WBACK_BUSY(req));
+#endif
+
+ /* Release struct file or cached credential */
+ nfs_clear_request(req);
+ nfs_page_free(req);
+}
+
+/**
+ * nfs_list_add_request - Insert a request into a sorted list
+ * @req: request
+ * @head: head of list into which to insert the request.
+ *
+ * Note that the wb_list is sorted by page index in order to facilitate
+ * coalescing of requests.
+ * We use an insertion sort that is optimized for the case of appended
+ * writes.
+ */
+void
+nfs_list_add_request(struct nfs_page *req, struct list_head *head)
+{
+ struct list_head *pos;
+ unsigned long pg_idx = page_index(req->wb_page);
+
+#ifdef NFS_PARANOIA
+ if (!list_empty(&req->wb_list)) {
+ printk(KERN_ERR "NFS: Add to list failed!\n");
+ BUG();
+ }
+#endif
+ list_for_each_prev(pos, head) {
+ struct nfs_page *p = nfs_list_entry(pos);
+ if (page_index(p->wb_page) < pg_idx)
+ break;
+ }
+ list_add(&req->wb_list, pos);
+ req->wb_list_head = head;
+}
+
+/**
+ * nfs_wait_on_request - Wait for a request to complete.
+ * @req: request to wait upon.
+ *
+ * Interruptible by signals only if mounted with intr flag.
+ * The user is responsible for holding a count on the request.
+ */
+int
+nfs_wait_on_request(struct nfs_page *req)
+{
+ struct inode *inode = req->wb_inode;
+ struct rpc_clnt *clnt = NFS_CLIENT(inode);
+
+ if (!NFS_WBACK_BUSY(req))
+ return 0;
+ return nfs_wait_event(clnt, req->wb_wait, !NFS_WBACK_BUSY(req));
+}
+
+/**
+ * nfs_coalesce_requests - Split coalesced requests out from a list.
+ * @head: source list
+ * @dst: destination list
+ * @nmax: maximum number of requests to coalesce
+ *
+ * Moves a maximum of 'nmax' elements from one list to another.
+ * The elements are checked to ensure that they form a contiguous set
+ * of pages, and that they originated from the same file.
+ */
+int
+nfs_coalesce_requests(struct list_head *head, struct list_head *dst,
+ unsigned int nmax)
+{
+ struct nfs_page *req = NULL;
+ unsigned int npages = 0;
+
+ while (!list_empty(head)) {
+ struct nfs_page *prev = req;
+
+ req = nfs_list_entry(head->next);
+ if (prev) {
+ if (req->wb_cred != prev->wb_cred)
+ break;
+ if (page_index(req->wb_page) != page_index(prev->wb_page)+1)
+ break;
+
+ if (req->wb_offset != 0)
+ break;
+ }
+ nfs_list_remove_request(req);
+ nfs_list_add_request(req, dst);
+ npages++;
+ if (req->wb_offset + req->wb_bytes != PAGE_CACHE_SIZE)
+ break;
+ if (npages >= nmax)
+ break;
+ }
+ return npages;
+}
+
+/*
+ * nfs_scan_forward - Coalesce more requests
+ * @req: First request to add
+ * @dst: destination list
+ * @nmax: maximum number of requests to coalesce
+ *
+ * Tries to coalesce more requests by traversing the request's wb_list.
+ * Moves the resulting list into dst. Requests are guaranteed to be
+ * contiguous, and to originate from the same file.
+ */
+static int
+nfs_scan_forward(struct nfs_page *req, struct list_head *dst, int nmax)
+{
+ struct nfs_server *server = NFS_SERVER(req->wb_inode);
+ struct list_head *pos, *head = req->wb_list_head;
+ struct rpc_cred *cred = req->wb_cred;
+ unsigned long idx = page_index(req->wb_page) + 1;
+ int npages = 0;
+
+ for (pos = req->wb_list.next; nfs_lock_request(req); pos = pos->next) {
+ nfs_list_remove_request(req);
+ nfs_list_add_request(req, dst);
+ __nfs_del_lru(req);
+ __nfs_add_lru(&server->lru_busy, req);
+ npages++;
+ if (npages == nmax)
+ break;
+ if (pos == head)
+ break;
+ if (req->wb_offset + req->wb_bytes != PAGE_CACHE_SIZE)
+ break;
+ req = nfs_list_entry(pos);
+ if (page_index(req->wb_page) != idx++)
+ break;
+ if (req->wb_offset != 0)
+ break;
+ if (req->wb_cred != cred)
+ break;
+ }
+ return npages;
+}
+
+/**
+ * nfs_scan_lru - Scan one of the least recently used list
+ * @head: One of the NFS superblock lru lists
+ * @dst: Destination list
+ * @nmax: maximum number of requests to coalesce
+ *
+ * Scans one of the NFS superblock lru lists for upto nmax requests
+ * and returns them on a list. The requests are all guaranteed to be
+ * contiguous, originating from the same inode and the same file.
+ */
+int
+nfs_scan_lru(struct list_head *head, struct list_head *dst, int nmax)
+{
+ struct list_head *pos;
+ struct nfs_page *req;
+ int npages = 0;
+
+ list_for_each(pos, head) {
+ req = nfs_lru_entry(pos);
+ npages = nfs_scan_forward(req, dst, nmax);
+ if (npages)
+ break;
+ }
+ return npages;
+}
+
+/**
+ * nfs_scan_lru_timeout - Scan one of the superblock lru lists for timed out requests
+ * @head: One of the NFS superblock lru lists
+ * @dst: Destination list
+ * @nmax: maximum number of requests to coalesce
+ *
+ * Scans one of the NFS superblock lru lists for upto nmax requests
+ * and returns them on a list. The requests are all guaranteed to be
+ * contiguous, originating from the same inode and the same file.
+ * The first request on the destination list will be timed out, the
+ * others are not guaranteed to be so.
+ */
+int
+nfs_scan_lru_timeout(struct list_head *head, struct list_head *dst, int nmax)
+{
+ struct list_head *pos;
+ struct nfs_page *req;
+ int npages = 0;
+
+ list_for_each(pos, head) {
+ req = nfs_lru_entry(pos);
+ if (time_after(req->wb_timeout, jiffies))
+ break;
+ npages = nfs_scan_forward(req, dst, nmax);
+ if (npages)
+ break;
+ }
+ return npages;
+}
+
+/**
+ * nfs_scan_list - Scan a list for matching requests
+ * @head: One of the NFS inode request lists
+ * @dst: Destination list
+ * @idx_start: lower bound of page->index to scan
+ * @npages: idx_start + npages sets the upper bound to scan.
+ *
+ * Moves elements from one of the inode request lists.
+ * If the number of requests is set to 0, the entire address_space
+ * starting at index idx_start, is scanned.
+ * The requests are *not* checked to ensure that they form a contiguous set.
+ * You must be holding the nfs_wreq_lock when calling this function
+ */
+int
+nfs_scan_list(struct list_head *head, struct list_head *dst,
+ unsigned long idx_start, unsigned int npages)
+{
+ struct list_head *pos, *tmp;
+ struct nfs_page *req;
+ unsigned long idx_end;
+ int res;
+
+ res = 0;
+ if (npages == 0)
+ idx_end = ~0;
+ else
+ idx_end = idx_start + npages - 1;
+
+ list_for_each_safe(pos, tmp, head) {
+ unsigned long pg_idx;
+
+ req = nfs_list_entry(pos);
+
+ pg_idx = page_index(req->wb_page);
+ if (pg_idx < idx_start)
+ continue;
+ if (pg_idx > idx_end)
+ break;
+
+ if (!nfs_lock_request(req))
+ continue;
+ nfs_list_remove_request(req);
+ nfs_list_add_request(req, dst);
+ __nfs_del_lru(req);
+ __nfs_add_lru(&NFS_SERVER(req->wb_inode)->lru_busy, req);
+ res++;
+ }
+ return res;
+}
+
+/*
+ * nfs_try_to_free_pages - Free up NFS read/write requests
+ * @server: The NFS superblock
+ *
+ * This function attempts to flush out NFS reads and writes in order
+ * to keep the hard limit on the total number of pending requests
+ * on a given NFS partition.
+ * Note: we first try to commit unstable writes, then flush out pending
+ * reads, then finally the dirty pages.
+ * The assumption is that this reflects the ordering from the fastest
+ * to the slowest method for reclaiming requests.
+ */
+static int
+nfs_try_to_free_pages(struct nfs_server *server)
+{
+ LIST_HEAD(head);
+ struct nfs_page *req = NULL;
+ int nreq;
+
+ for (;;) {
+ if (req) {
+ int status = nfs_wait_on_request(req);
+ nfs_release_request(req);
+ if (status)
+ break;
+ req = NULL;
+ }
+ nreq = atomic_read(&server->rw_requests->nr_requests);
+ if (nreq < MAX_REQUEST_HARD)
+ return 1;
+ spin_lock(&nfs_wreq_lock);
+ /* Are there any busy RPC calls that might free up requests? */
+ if (!list_empty(&server->lru_busy)) {
+ req = nfs_lru_entry(server->lru_busy.next);
+ req->wb_count++;
+ __nfs_del_lru(req);
+ spin_unlock(&nfs_wreq_lock);
+ continue;
+ }
+
+#ifdef CONFIG_NFS_V3
+ /* Let's try to free up some completed NFSv3 unstable writes */
+ nfs_scan_lru_commit(server, &head);
+ if (!list_empty(&head)) {
+ spin_unlock(&nfs_wreq_lock);
+ nfs_commit_list(&head, 0);
+ continue;
+ }
+#endif
+ /* OK, so we try to free up some pending readaheads */
+ nfs_scan_lru_read(server, &head);
+ if (!list_empty(&head)) {
+ spin_unlock(&nfs_wreq_lock);
+ nfs_pagein_list(&head, server->rpages);
+ continue;
+ }
+ /* Last resort: we try to flush out single requests */
+ nfs_scan_lru_dirty(server, &head);
+ if (!list_empty(&head)) {
+ spin_unlock(&nfs_wreq_lock);
+ nfs_flush_list(&head, server->wpages, FLUSH_STABLE);
+ continue;
+ }
+ spin_unlock(&nfs_wreq_lock);
+ break;
+ }
+ /* We failed to free up requests */
+ return 0;
+}
+
+int nfs_init_nfspagecache(void)
+{
+ nfs_page_cachep = kmem_cache_create("nfs_page",
+ sizeof(struct nfs_page),
+ 0, SLAB_HWCACHE_ALIGN,
+ NULL, NULL);
+ if (nfs_page_cachep == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+
+void nfs_destroy_nfspagecache(void)
+{
+ if (kmem_cache_destroy(nfs_page_cachep))
+ printk(KERN_INFO "nfs_page: not all structures were freed\n");
+}
+
diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c
new file mode 100644
index 00000000000000..e7abac6be74a88
--- /dev/null
+++ b/fs/nfs/proc.c
@@ -0,0 +1,391 @@
+/*
+ * linux/fs/nfs/proc.c
+ *
+ * Copyright (C) 1992, 1993, 1994 Rick Sladkey
+ *
+ * OS-independent nfs remote procedure call functions
+ *
+ * Tuned by Alan Cox <A.Cox@swansea.ac.uk> for >3K buffers
+ * so at last we can have decent(ish) throughput off a
+ * Sun server.
+ *
+ * Coding optimized and cleaned up by Florian La Roche.
+ * Note: Error returns are optimized for NFS_OK, which isn't translated via
+ * nfs_stat_to_errno(), but happens to be already the right return code.
+ *
+ * Also, the code currently doesn't check the size of the packet, when
+ * it decodes the packet.
+ *
+ * Feel free to fix it and mail me the diffs if it worries you.
+ *
+ * Completely rewritten to support the new RPC call interface;
+ * rewrote and moved the entire XDR stuff to xdr.c
+ * --Olaf Kirch June 1996
+ *
+ * The code below initializes all auto variables explicitly, otherwise
+ * it will fail to work as a module (gcc generates a memset call for an
+ * incomplete struct).
+ */
+
+#include <linux/types.h>
+#include <linux/param.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/utsname.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/in.h>
+#include <linux/pagemap.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/nfs.h>
+#include <linux/nfs2.h>
+#include <linux/nfs_fs.h>
+
+#define NFSDBG_FACILITY NFSDBG_PROC
+
+/*
+ * Bare-bones access to getattr: this is for nfs_read_super.
+ */
+static int
+nfs_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
+ struct nfs_fattr *fattr)
+{
+ int status;
+
+ dprintk("NFS call getroot\n");
+ fattr->valid = 0;
+ status = rpc_call(server->client, NFSPROC_GETATTR, fhandle, fattr, 0);
+ dprintk("NFS reply getroot\n");
+ return status;
+}
+
+/*
+ * One function for each procedure in the NFS protocol.
+ */
+static int
+nfs_proc_getattr(struct inode *inode, struct nfs_fattr *fattr)
+{
+ int status;
+
+ dprintk("NFS call getattr\n");
+ fattr->valid = 0;
+ status = rpc_call(NFS_CLIENT(inode), NFSPROC_GETATTR,
+ NFS_FH(inode), fattr, 0);
+ dprintk("NFS reply getattr\n");
+ return status;
+}
+
+static int
+nfs_proc_setattr(struct inode *inode, struct nfs_fattr *fattr,
+ struct iattr *sattr)
+{
+ struct nfs_sattrargs arg = { NFS_FH(inode), sattr };
+ int status;
+
+ dprintk("NFS call setattr\n");
+ fattr->valid = 0;
+ status = rpc_call(NFS_CLIENT(inode), NFSPROC_SETATTR, &arg, fattr, 0);
+ dprintk("NFS reply setattr\n");
+ return status;
+}
+
+static int
+nfs_proc_lookup(struct inode *dir, struct qstr *name,
+ struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+{
+ struct nfs_diropargs arg = { NFS_FH(dir), name->name, name->len };
+ struct nfs_diropok res = { fhandle, fattr };
+ int status;
+
+ dprintk("NFS call lookup %s\n", name->name);
+ fattr->valid = 0;
+ status = rpc_call(NFS_CLIENT(dir), NFSPROC_LOOKUP, &arg, &res, 0);
+ dprintk("NFS reply lookup: %d\n", status);
+ return status;
+}
+
+static int
+nfs_proc_readlink(struct inode *inode, struct page *page)
+{
+ struct nfs_readlinkargs args = { NFS_FH(inode), PAGE_CACHE_SIZE, &page };
+ int status;
+
+ dprintk("NFS call readlink\n");
+ status = rpc_call(NFS_CLIENT(inode), NFSPROC_READLINK, &args, NULL, 0);
+ dprintk("NFS reply readlink: %d\n", status);
+ return status;
+}
+
+static int
+nfs_proc_read(struct inode *inode, struct rpc_cred *cred,
+ struct nfs_fattr *fattr, int flags,
+ unsigned int base, unsigned int count,
+ struct page *page, int *eofp)
+{
+ u64 offset = page_offset(page) + base;
+ struct nfs_readargs arg = { NFS_FH(inode), offset, count,
+ base, &page };
+ struct nfs_readres res = { fattr, count, 0};
+ struct rpc_message msg = { NFSPROC_READ, &arg, &res, cred };
+ int status;
+
+ dprintk("NFS call read %d @ %Ld\n", count, (long long)offset);
+ fattr->valid = 0;
+ status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags);
+
+ dprintk("NFS reply read: %d\n", status);
+ *eofp = res.eof;
+ return status;
+}
+
+static int
+nfs_proc_write(struct inode *inode, struct rpc_cred *cred,
+ struct nfs_fattr *fattr, int how,
+ unsigned int base, unsigned int count,
+ struct page *page, struct nfs_writeverf *verf)
+{
+ u64 offset = page_offset(page) + base;
+ struct nfs_writeargs arg = { NFS_FH(inode), offset, count,
+ NFS_FILE_SYNC, base, &page };
+ struct nfs_writeres res = {fattr, verf, count};
+ struct rpc_message msg = { NFSPROC_WRITE, &arg, &res, cred };
+ int status, flags = 0;
+
+ dprintk("NFS call write %d @ %Ld\n", count, (long long)offset);
+ fattr->valid = 0;
+ if (how & NFS_RW_SWAP)
+ flags |= NFS_RPC_SWAPFLAGS;
+ status = rpc_call_sync(NFS_CLIENT(inode), &msg, flags);
+
+ dprintk("NFS reply write: %d\n", status);
+ verf->committed = NFS_FILE_SYNC; /* NFSv2 always syncs data */
+ return status < 0? status : count;
+}
+
+static int
+nfs_proc_create(struct inode *dir, struct qstr *name, struct iattr *sattr,
+ int flags, struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+{
+ struct nfs_createargs arg = { NFS_FH(dir), name->name,
+ name->len, sattr };
+ struct nfs_diropok res = { fhandle, fattr };
+ int status;
+
+ fattr->valid = 0;
+ dprintk("NFS call create %s\n", name->name);
+ status = rpc_call(NFS_CLIENT(dir), NFSPROC_CREATE, &arg, &res, 0);
+ dprintk("NFS reply create: %d\n", status);
+ return status;
+}
+
+/*
+ * In NFSv2, mknod is grafted onto the create call.
+ */
+static int
+nfs_proc_mknod(struct inode *dir, struct qstr *name, struct iattr *sattr,
+ dev_t rdev, struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+{
+ struct nfs_createargs arg = { NFS_FH(dir), name->name,
+ name->len, sattr };
+ struct nfs_diropok res = { fhandle, fattr };
+ int status, mode;
+
+ dprintk("NFS call mknod %s\n", name->name);
+
+ mode = sattr->ia_mode;
+ if (S_ISFIFO(mode)) {
+ sattr->ia_mode = (mode & ~S_IFMT) | S_IFCHR;
+ sattr->ia_valid &= ~ATTR_SIZE;
+ } else if (S_ISCHR(mode) || S_ISBLK(mode)) {
+ sattr->ia_valid |= ATTR_SIZE;
+ sattr->ia_size = rdev; /* get out your barf bag */
+ }
+
+ fattr->valid = 0;
+ status = rpc_call(NFS_CLIENT(dir), NFSPROC_CREATE, &arg, &res, 0);
+
+ if (status == -EINVAL && S_ISFIFO(mode)) {
+ sattr->ia_mode = mode;
+ fattr->valid = 0;
+ status = rpc_call(NFS_CLIENT(dir), NFSPROC_CREATE, &arg, &res, 0);
+ }
+ dprintk("NFS reply mknod: %d\n", status);
+ return status;
+}
+
+static int
+nfs_proc_remove(struct inode *dir, struct qstr *name)
+{
+ struct nfs_diropargs arg = { NFS_FH(dir), name->name, name->len };
+ struct rpc_message msg = { NFSPROC_REMOVE, &arg, NULL, NULL };
+ int status;
+
+ dprintk("NFS call remove %s\n", name->name);
+ status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
+
+ dprintk("NFS reply remove: %d\n", status);
+ return status;
+}
+
+static int
+nfs_proc_unlink_setup(struct rpc_message *msg, struct dentry *dir, struct qstr *name)
+{
+ struct nfs_diropargs *arg;
+
+ arg = (struct nfs_diropargs *)kmalloc(sizeof(*arg), GFP_KERNEL);
+ if (!arg)
+ return -ENOMEM;
+ arg->fh = NFS_FH(dir->d_inode);
+ arg->name = name->name;
+ arg->len = name->len;
+ msg->rpc_proc = NFSPROC_REMOVE;
+ msg->rpc_argp = arg;
+ return 0;
+}
+
+static void
+nfs_proc_unlink_done(struct dentry *dir, struct rpc_message *msg)
+{
+ if (msg->rpc_argp) {
+ NFS_CACHEINV(dir->d_inode);
+ kfree(msg->rpc_argp);
+ }
+}
+
+static int
+nfs_proc_rename(struct inode *old_dir, struct qstr *old_name,
+ struct inode *new_dir, struct qstr *new_name)
+{
+ struct nfs_renameargs arg = { NFS_FH(old_dir), old_name->name,
+ old_name->len,
+ NFS_FH(new_dir), new_name->name,
+ new_name->len};
+ int status;
+
+ dprintk("NFS call rename %s -> %s\n", old_name->name, new_name->name);
+ status = rpc_call(NFS_CLIENT(old_dir), NFSPROC_RENAME, &arg, NULL, 0);
+ dprintk("NFS reply rename: %d\n", status);
+ return status;
+}
+
+static int
+nfs_proc_link(struct inode *inode, struct inode *dir, struct qstr *name)
+{
+ struct nfs_linkargs arg = { NFS_FH(inode), NFS_FH(dir),
+ name->name, name->len };
+ int status;
+
+ dprintk("NFS call link %s\n", name->name);
+ status = rpc_call(NFS_CLIENT(inode), NFSPROC_LINK, &arg, NULL, 0);
+ dprintk("NFS reply link: %d\n", status);
+ return status;
+}
+
+static int
+nfs_proc_symlink(struct inode *dir, struct qstr *name, struct qstr *path,
+ struct iattr *sattr, struct nfs_fh *fhandle,
+ struct nfs_fattr *fattr)
+{
+ struct nfs_symlinkargs arg = { NFS_FH(dir), name->name, name->len,
+ path->name, path->len, sattr };
+ int status;
+
+ dprintk("NFS call symlink %s -> %s\n", name->name, path->name);
+ fattr->valid = 0;
+ status = rpc_call(NFS_CLIENT(dir), NFSPROC_SYMLINK, &arg, NULL, 0);
+ dprintk("NFS reply symlink: %d\n", status);
+ return status;
+}
+
+static int
+nfs_proc_mkdir(struct inode *dir, struct qstr *name, struct iattr *sattr,
+ struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+{
+ struct nfs_createargs arg = { NFS_FH(dir), name->name, name->len,
+ sattr };
+ struct nfs_diropok res = { fhandle, fattr };
+ int status;
+
+ dprintk("NFS call mkdir %s\n", name->name);
+ fattr->valid = 0;
+ status = rpc_call(NFS_CLIENT(dir), NFSPROC_MKDIR, &arg, &res, 0);
+ dprintk("NFS reply mkdir: %d\n", status);
+ return status;
+}
+
+static int
+nfs_proc_rmdir(struct inode *dir, struct qstr *name)
+{
+ struct nfs_diropargs arg = { NFS_FH(dir), name->name, name->len };
+ int status;
+
+ dprintk("NFS call rmdir %s\n", name->name);
+ status = rpc_call(NFS_CLIENT(dir), NFSPROC_RMDIR, &arg, NULL, 0);
+ dprintk("NFS reply rmdir: %d\n", status);
+ return status;
+}
+
+/*
+ * The READDIR implementation is somewhat hackish - we pass a temporary
+ * buffer to the encode function, which installs it in the receive
+ * the receive iovec. The decode function just parses the reply to make
+ * sure it is syntactically correct; the entries itself are decoded
+ * from nfs_readdir by calling the decode_entry function directly.
+ */
+static int
+nfs_proc_readdir(struct inode *dir, struct rpc_cred *cred,
+ __u64 cookie, struct page *page,
+ unsigned int count, int plus)
+{
+ struct nfs_readdirargs arg = { NFS_FH(dir), cookie, count, &page };
+ struct rpc_message msg = { NFSPROC_READDIR, &arg, NULL, cred };
+ int status;
+
+ dprintk("NFS call readdir %d\n", (unsigned int)cookie);
+ status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
+
+ dprintk("NFS reply readdir: %d\n", status);
+ return status;
+}
+
+static int
+nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
+ struct nfs_fsinfo *info)
+{
+ int status;
+
+ dprintk("NFS call statfs\n");
+ memset((char *)info, 0, sizeof(*info));
+ status = rpc_call(server->client, NFSPROC_STATFS, fhandle, info, 0);
+ dprintk("NFS reply statfs: %d\n", status);
+ return status;
+}
+
+extern u32 * nfs_decode_dirent(u32 *, struct nfs_entry *, int);
+
+struct nfs_rpc_ops nfs_v2_clientops = {
+ 2, /* protocol version */
+ nfs_proc_get_root,
+ nfs_proc_getattr,
+ nfs_proc_setattr,
+ nfs_proc_lookup,
+ NULL, /* access */
+ nfs_proc_readlink,
+ nfs_proc_read,
+ nfs_proc_write,
+ NULL, /* commit */
+ nfs_proc_create,
+ nfs_proc_remove,
+ nfs_proc_unlink_setup,
+ nfs_proc_unlink_done,
+ nfs_proc_rename,
+ nfs_proc_link,
+ nfs_proc_symlink,
+ nfs_proc_mkdir,
+ nfs_proc_rmdir,
+ nfs_proc_readdir,
+ nfs_proc_mknod,
+ nfs_proc_statfs,
+ nfs_decode_dirent,
+};
diff --git a/fs/nfs/read.c b/fs/nfs/read.c
new file mode 100644
index 00000000000000..1e0024b3d07242
--- /dev/null
+++ b/fs/nfs/read.c
@@ -0,0 +1,508 @@
+/*
+ * linux/fs/nfs/read.c
+ *
+ * Block I/O for NFS
+ *
+ * Partial copy of Linus' read cache modifications to fs/nfs/file.c
+ * modified for async RPC by okir@monad.swb.de
+ *
+ * We do an ugly hack here in order to return proper error codes to the
+ * user program when a read request failed: since generic_file_read
+ * only checks the return value of inode->i_op->readpage() which is always 0
+ * for async RPC, we set the error bit of the page to 1 when an error occurs,
+ * and make nfs_readpage transmit requests synchronously when encountering this.
+ * This is only a small problem, though, since we now retry all operations
+ * within the RPC code when root squashing is suspected.
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/nfs_fs.h>
+#include <linux/nfs_page.h>
+#include <linux/nfs_flushd.h>
+#include <linux/smp_lock.h>
+
+#include <asm/system.h>
+
+#define NFSDBG_FACILITY NFSDBG_PAGECACHE
+
+struct nfs_read_data {
+ struct rpc_task task;
+ struct inode *inode;
+ struct rpc_cred *cred;
+ struct nfs_readargs args; /* XDR argument struct */
+ struct nfs_readres res; /* ... and result struct */
+ struct nfs_fattr fattr; /* fattr storage */
+ struct list_head pages; /* Coalesced read requests */
+ struct page *pagevec[NFS_READ_MAXIOV];
+};
+
+/*
+ * Local function declarations
+ */
+static void nfs_readpage_result(struct rpc_task *task);
+
+/* Hack for future NFS swap support */
+#ifndef IS_SWAPFILE
+# define IS_SWAPFILE(inode) (0)
+#endif
+
+static kmem_cache_t *nfs_rdata_cachep;
+
+static __inline__ struct nfs_read_data *nfs_readdata_alloc(void)
+{
+ struct nfs_read_data *p;
+ p = kmem_cache_alloc(nfs_rdata_cachep, SLAB_NOFS);
+ if (p) {
+ memset(p, 0, sizeof(*p));
+ INIT_LIST_HEAD(&p->pages);
+ p->args.pages = p->pagevec;
+ }
+ return p;
+}
+
+static __inline__ void nfs_readdata_free(struct nfs_read_data *p)
+{
+ kmem_cache_free(nfs_rdata_cachep, p);
+}
+
+static void nfs_readdata_release(struct rpc_task *task)
+{
+ struct nfs_read_data *data = (struct nfs_read_data *)task->tk_calldata;
+ nfs_readdata_free(data);
+}
+
+/*
+ * Read a page synchronously.
+ */
+static int
+nfs_readpage_sync(struct file *file, struct inode *inode, struct page *page)
+{
+ struct rpc_cred *cred = NULL;
+ struct nfs_fattr fattr;
+ unsigned int offset = 0;
+ int rsize = NFS_SERVER(inode)->rsize;
+ int result;
+ int count = PAGE_CACHE_SIZE;
+ int flags = IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0;
+ int eof;
+
+ dprintk("NFS: nfs_readpage_sync(%p)\n", page);
+
+ if (file)
+ cred = nfs_file_cred(file);
+
+ /*
+ * This works now because the socket layer never tries to DMA
+ * into this buffer directly.
+ */
+ do {
+ if (count < rsize)
+ rsize = count;
+
+ dprintk("NFS: nfs_proc_read(%s, (%x/%Ld), %u, %u, %p)\n",
+ NFS_SERVER(inode)->hostname,
+ inode->i_dev, (long long)NFS_FILEID(inode),
+ offset, rsize, page);
+
+ lock_kernel();
+ result = NFS_PROTO(inode)->read(inode, cred, &fattr, flags,
+ offset, rsize, page, &eof);
+ nfs_refresh_inode(inode, &fattr);
+ unlock_kernel();
+
+ /*
+ * Even if we had a partial success we can't mark the page
+ * cache valid.
+ */
+ if (result < 0) {
+ if (result == -EISDIR)
+ result = -EINVAL;
+ goto io_error;
+ }
+ count -= result;
+ offset += result;
+ if (result < rsize) /* NFSv2ism */
+ break;
+ } while (count);
+
+ if (count) {
+ char *kaddr = kmap(page);
+ memset(kaddr + offset, 0, count);
+ kunmap(page);
+ }
+ flush_dcache_page(page);
+ SetPageUptodate(page);
+ if (PageError(page))
+ ClearPageError(page);
+ result = 0;
+
+io_error:
+ UnlockPage(page);
+ return result;
+}
+
+/*
+ * Add a request to the inode's asynchronous read list.
+ */
+static inline void
+nfs_mark_request_read(struct nfs_page *req)
+{
+ struct inode *inode = req->wb_inode;
+
+ spin_lock(&nfs_wreq_lock);
+ nfs_list_add_request(req, &inode->u.nfs_i.read);
+ inode->u.nfs_i.nread++;
+ __nfs_add_lru(&NFS_SERVER(inode)->lru_read, req);
+ spin_unlock(&nfs_wreq_lock);
+}
+
+static int
+nfs_readpage_async(struct file *file, struct inode *inode, struct page *page)
+{
+ struct nfs_page *new;
+
+ new = nfs_create_request(nfs_file_cred(file), inode, page, 0, PAGE_CACHE_SIZE);
+ if (IS_ERR(new)) {
+ SetPageError(page);
+ NFS_ClearPageSync(page);
+ UnlockPage(page);
+ return PTR_ERR(new);
+ }
+ nfs_mark_request_read(new);
+
+ if (NFS_TestClearPageSync(page) ||
+ inode->u.nfs_i.nread >= NFS_SERVER(inode)->rpages ||
+ page_index(page) == (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT)
+ nfs_pagein_inode(inode, 0, 0);
+ return 0;
+}
+
+/*
+ * Set up the NFS read request struct
+ */
+static void
+nfs_read_rpcsetup(struct list_head *head, struct nfs_read_data *data)
+{
+ struct nfs_page *req;
+ struct page **pages;
+ unsigned int count;
+
+ pages = data->args.pages;
+ count = 0;
+ while (!list_empty(head)) {
+ struct nfs_page *req = nfs_list_entry(head->next);
+ nfs_list_remove_request(req);
+ nfs_list_add_request(req, &data->pages);
+ *pages++ = req->wb_page;
+ count += req->wb_bytes;
+ }
+ req = nfs_list_entry(data->pages.next);
+ data->inode = req->wb_inode;
+ data->cred = req->wb_cred;
+ data->args.fh = NFS_FH(req->wb_inode);
+ data->args.offset = page_offset(req->wb_page) + req->wb_offset;
+ data->args.pgbase = req->wb_offset;
+ data->args.count = count;
+ data->res.fattr = &data->fattr;
+ data->res.count = count;
+ data->res.eof = 0;
+}
+
+static void
+nfs_async_read_error(struct list_head *head)
+{
+ struct nfs_page *req;
+ struct page *page;
+
+ while (!list_empty(head)) {
+ req = nfs_list_entry(head->next);
+ page = req->wb_page;
+ nfs_list_remove_request(req);
+ NFS_ClearPageSync(page);
+ SetPageError(page);
+ UnlockPage(page);
+ nfs_clear_request(req);
+ nfs_release_request(req);
+ nfs_unlock_request(req);
+ }
+}
+
+static int
+nfs_pagein_one(struct list_head *head, struct inode *inode)
+{
+ struct rpc_task *task;
+ struct rpc_clnt *clnt = NFS_CLIENT(inode);
+ struct nfs_read_data *data;
+ struct rpc_message msg;
+ int flags;
+ sigset_t oldset;
+
+ data = nfs_readdata_alloc();
+ if (!data)
+ goto out_bad;
+ task = &data->task;
+
+ /* N.B. Do we need to test? Never called for swapfile inode */
+ flags = RPC_TASK_ASYNC | (IS_SWAPFILE(inode)? NFS_RPC_SWAPFLAGS : 0);
+
+ nfs_read_rpcsetup(head, data);
+
+ /* Finalize the task. */
+ rpc_init_task(task, clnt, nfs_readpage_result, flags);
+ task->tk_calldata = data;
+ /* Release requests */
+ task->tk_release = nfs_readdata_release;
+
+#ifdef CONFIG_NFS_V3
+ msg.rpc_proc = (NFS_PROTO(inode)->version == 3) ? NFS3PROC_READ : NFSPROC_READ;
+#else
+ msg.rpc_proc = NFSPROC_READ;
+#endif
+ msg.rpc_argp = &data->args;
+ msg.rpc_resp = &data->res;
+ msg.rpc_cred = data->cred;
+
+ /* Start the async call */
+ dprintk("NFS: %4d initiated read call (req %x/%Ld count %u.\n",
+ task->tk_pid,
+ inode->i_dev, (long long)NFS_FILEID(inode),
+ data->args.count);
+
+ rpc_clnt_sigmask(clnt, &oldset);
+ rpc_call_setup(task, &msg, 0);
+ lock_kernel();
+ rpc_execute(task);
+ unlock_kernel();
+ rpc_clnt_sigunmask(clnt, &oldset);
+ return 0;
+out_bad:
+ nfs_async_read_error(head);
+ return -ENOMEM;
+}
+
+int
+nfs_pagein_list(struct list_head *head, int rpages)
+{
+ LIST_HEAD(one_request);
+ struct nfs_page *req;
+ int error = 0;
+ unsigned int pages = 0;
+
+ while (!list_empty(head)) {
+ pages += nfs_coalesce_requests(head, &one_request, rpages);
+ req = nfs_list_entry(one_request.next);
+ error = nfs_pagein_one(&one_request, req->wb_inode);
+ if (error < 0)
+ break;
+ }
+ if (error >= 0)
+ return pages;
+
+ nfs_async_read_error(head);
+ return error;
+}
+
+/**
+ * nfs_scan_lru_read_timeout - Scan LRU list for timed out read requests
+ * @server: NFS superblock data
+ * @dst: destination list
+ *
+ * Moves a maximum of 'rpages' timed out requests from the NFS read LRU list.
+ * The elements are checked to ensure that they form a contiguous set
+ * of pages, and that they originated from the same file.
+ */
+int
+nfs_scan_lru_read_timeout(struct nfs_server *server, struct list_head *dst)
+{
+ struct inode *inode;
+ int npages;
+
+ npages = nfs_scan_lru_timeout(&server->lru_read, dst, server->rpages);
+ if (npages) {
+ inode = nfs_list_entry(dst->next)->wb_inode;
+ inode->u.nfs_i.nread -= npages;
+ }
+ return npages;
+}
+
+/**
+ * nfs_scan_lru_read - Scan LRU list for read requests
+ * @server: NFS superblock data
+ * @dst: destination list
+ *
+ * Moves a maximum of 'rpages' requests from the NFS read LRU list.
+ * The elements are checked to ensure that they form a contiguous set
+ * of pages, and that they originated from the same file.
+ */
+int
+nfs_scan_lru_read(struct nfs_server *server, struct list_head *dst)
+{
+ struct inode *inode;
+ int npages;
+
+ npages = nfs_scan_lru(&server->lru_read, dst, server->rpages);
+ if (npages) {
+ inode = nfs_list_entry(dst->next)->wb_inode;
+ inode->u.nfs_i.nread -= npages;
+ }
+ return npages;
+}
+
+/*
+ * nfs_scan_read - Scan an inode for read requests
+ * @inode: NFS inode to scan
+ * @dst: destination list
+ * @idx_start: lower bound of page->index to scan
+ * @npages: idx_start + npages sets the upper bound to scan
+ *
+ * Moves requests from the inode's read list.
+ * The requests are *not* checked to ensure that they form a contiguous set.
+ */
+static int
+nfs_scan_read(struct inode *inode, struct list_head *dst, unsigned long idx_start, unsigned int npages)
+{
+ int res;
+ res = nfs_scan_list(&inode->u.nfs_i.read, dst, idx_start, npages);
+ inode->u.nfs_i.nread -= res;
+ if ((inode->u.nfs_i.nread == 0) != list_empty(&inode->u.nfs_i.read))
+ printk(KERN_ERR "NFS: desynchronized value of nfs_i.nread.\n");
+ return res;
+}
+
+int nfs_pagein_inode(struct inode *inode, unsigned long idx_start,
+ unsigned int npages)
+{
+ LIST_HEAD(head);
+ int res,
+ error = 0;
+
+ spin_lock(&nfs_wreq_lock);
+ res = nfs_scan_read(inode, &head, idx_start, npages);
+ spin_unlock(&nfs_wreq_lock);
+ if (res)
+ error = nfs_pagein_list(&head, NFS_SERVER(inode)->rpages);
+ if (error < 0)
+ return error;
+ return res;
+}
+
+/*
+ * This is the callback from RPC telling us whether a reply was
+ * received or some error occurred (timeout or socket shutdown).
+ */
+static void
+nfs_readpage_result(struct rpc_task *task)
+{
+ struct nfs_read_data *data = (struct nfs_read_data *) task->tk_calldata;
+ struct inode *inode = data->inode;
+ unsigned int count = data->res.count;
+
+ dprintk("NFS: %4d nfs_readpage_result, (status %d)\n",
+ task->tk_pid, task->tk_status);
+
+ if (nfs_async_handle_jukebox(task))
+ return;
+
+ nfs_refresh_inode(inode, &data->fattr);
+ while (!list_empty(&data->pages)) {
+ struct nfs_page *req = nfs_list_entry(data->pages.next);
+ struct page *page = req->wb_page;
+ nfs_list_remove_request(req);
+
+ if (task->tk_status >= 0) {
+ if (count < PAGE_CACHE_SIZE) {
+ char *p = kmap(page);
+ memset(p + count, 0, PAGE_CACHE_SIZE - count);
+ kunmap(page);
+ count = 0;
+ } else
+ count -= PAGE_CACHE_SIZE;
+ SetPageUptodate(page);
+ } else
+ SetPageError(page);
+ flush_dcache_page(page);
+ NFS_ClearPageSync(page);
+ UnlockPage(page);
+
+ dprintk("NFS: read (%x/%Ld %d@%Ld)\n",
+ req->wb_inode->i_dev,
+ (long long)NFS_FILEID(req->wb_inode),
+ req->wb_bytes,
+ (long long)(page_offset(page) + req->wb_offset));
+ nfs_clear_request(req);
+ nfs_release_request(req);
+ nfs_unlock_request(req);
+ }
+}
+
+/*
+ * Read a page over NFS.
+ * We read the page synchronously in the following cases:
+ * - The NFS rsize is smaller than PAGE_CACHE_SIZE. We could kludge our way
+ * around this by creating several consecutive read requests, but
+ * that's hardly worth it.
+ * - The error flag is set for this page. This happens only when a
+ * previous async read operation failed.
+ */
+int
+nfs_readpage(struct file *file, struct page *page)
+{
+ struct inode *inode = page->mapping->host;
+ int error;
+
+ dprintk("NFS: nfs_readpage (%p %ld@%lu)\n",
+ page, PAGE_CACHE_SIZE, page->index);
+ /*
+ * Try to flush any pending writes to the file..
+ *
+ * NOTE! Because we own the page lock, there cannot
+ * be any new pending writes generated at this point
+ * for this page (other pages can be written to).
+ */
+ error = nfs_wb_page(inode, page);
+ if (error)
+ goto out_error;
+
+ if (!PageError(page) && NFS_SERVER(inode)->rsize >= PAGE_CACHE_SIZE) {
+ error = nfs_readpage_async(file, inode, page);
+ goto out;
+ }
+
+ error = nfs_readpage_sync(file, inode, page);
+ if (error < 0 && IS_SWAPFILE(inode))
+ printk("Aiee.. nfs swap-in of page failed!\n");
+out:
+ return error;
+
+out_error:
+ NFS_ClearPageSync(page);
+ UnlockPage(page);
+ goto out;
+}
+
+int nfs_init_readpagecache(void)
+{
+ nfs_rdata_cachep = kmem_cache_create("nfs_read_data",
+ sizeof(struct nfs_read_data),
+ 0, SLAB_HWCACHE_ALIGN,
+ NULL, NULL);
+ if (nfs_rdata_cachep == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+
+void nfs_destroy_readpagecache(void)
+{
+ if (kmem_cache_destroy(nfs_rdata_cachep))
+ printk(KERN_INFO "nfs_read_data: not all structures were freed\n");
+}
diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c
new file mode 100644
index 00000000000000..ec80009246dfb5
--- /dev/null
+++ b/fs/nfs/symlink.c
@@ -0,0 +1,108 @@
+/*
+ * linux/fs/nfs/symlink.c
+ *
+ * Copyright (C) 1992 Rick Sladkey
+ *
+ * Optimization changes Copyright (C) 1994 Florian La Roche
+ *
+ * Jun 7 1999, cache symlink lookups in the page cache. -DaveM
+ *
+ * nfs symlink handling code
+ */
+
+#define NFS_NEED_XDR_TYPES
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/nfs.h>
+#include <linux/nfs2.h>
+#include <linux/nfs_fs.h>
+#include <linux/pagemap.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/smp_lock.h>
+
+/* Symlink caching in the page cache is even more simplistic
+ * and straight-forward than readdir caching.
+ */
+static int nfs_symlink_filler(struct inode *inode, struct page *page)
+{
+ int error;
+
+ /* We place the length at the beginning of the page,
+ * in host byte order, followed by the string. The
+ * XDR response verification will NULL terminate it.
+ */
+ lock_kernel();
+ error = NFS_PROTO(inode)->readlink(inode, page);
+ unlock_kernel();
+ if (error < 0)
+ goto error;
+ SetPageUptodate(page);
+ UnlockPage(page);
+ return 0;
+
+error:
+ SetPageError(page);
+ UnlockPage(page);
+ return -EIO;
+}
+
+static char *nfs_getlink(struct inode *inode, struct page **ppage)
+{
+ struct page *page;
+ u32 *p;
+
+ /* Caller revalidated the directory inode already. */
+ page = read_cache_page(&inode->i_data, 0,
+ (filler_t *)nfs_symlink_filler, inode);
+ if (IS_ERR(page))
+ goto read_failed;
+ if (!Page_Uptodate(page))
+ goto getlink_read_error;
+ *ppage = page;
+ p = kmap(page);
+ return (char*)(p+1);
+
+getlink_read_error:
+ page_cache_release(page);
+ return ERR_PTR(-EIO);
+read_failed:
+ return (char*)page;
+}
+
+static int nfs_readlink(struct dentry *dentry, char *buffer, int buflen)
+{
+ struct inode *inode = dentry->d_inode;
+ struct page *page = NULL;
+ int res = vfs_readlink(dentry,buffer,buflen,nfs_getlink(inode,&page));
+ if (page) {
+ kunmap(page);
+ page_cache_release(page);
+ }
+ return res;
+}
+
+static int nfs_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+ struct inode *inode = dentry->d_inode;
+ struct page *page = NULL;
+ int res = vfs_follow_link(nd, nfs_getlink(inode,&page));
+ if (page) {
+ kunmap(page);
+ page_cache_release(page);
+ }
+ return res;
+}
+
+/*
+ * symlinks can't do much...
+ */
+struct inode_operations nfs_symlink_inode_operations = {
+ readlink: nfs_readlink,
+ follow_link: nfs_follow_link,
+ revalidate: nfs_revalidate,
+ setattr: nfs_notify_change,
+};
diff --git a/fs/nfs/unlink.c b/fs/nfs/unlink.c
new file mode 100644
index 00000000000000..9697bedad53c1b
--- /dev/null
+++ b/fs/nfs/unlink.c
@@ -0,0 +1,218 @@
+/*
+ * linux/fs/nfs/unlink.c
+ *
+ * nfs sillydelete handling
+ *
+ * NOTE: we rely on holding the BKL for list manipulation protection.
+ */
+
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/dcache.h>
+#include <linux/sunrpc/sched.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/nfs_fs.h>
+
+
+struct nfs_unlinkdata {
+ struct nfs_unlinkdata *next;
+ struct dentry *dir, *dentry;
+ struct qstr name;
+ struct rpc_task task;
+ struct rpc_cred *cred;
+ unsigned int count;
+};
+
+static struct nfs_unlinkdata *nfs_deletes;
+static RPC_WAITQ(nfs_delete_queue, "nfs_delete_queue");
+
+/**
+ * nfs_detach_unlinkdata - Remove asynchronous unlink from global list
+ * @data: pointer to descriptor
+ */
+static inline void
+nfs_detach_unlinkdata(struct nfs_unlinkdata *data)
+{
+ struct nfs_unlinkdata **q;
+
+ for (q = &nfs_deletes; *q != NULL; q = &((*q)->next)) {
+ if (*q == data) {
+ *q = data->next;
+ break;
+ }
+ }
+}
+
+/**
+ * nfs_put_unlinkdata - release data from a sillydelete operation.
+ * @data: pointer to unlink structure.
+ */
+static void
+nfs_put_unlinkdata(struct nfs_unlinkdata *data)
+{
+ if (--data->count == 0) {
+ nfs_detach_unlinkdata(data);
+ if (data->name.name != NULL)
+ kfree(data->name.name);
+ kfree(data);
+ }
+}
+
+#define NAME_ALLOC_LEN(len) ((len+16) & ~15)
+/**
+ * nfs_copy_dname - copy dentry name to data structure
+ * @dentry: pointer to dentry
+ * @data: nfs_unlinkdata
+ */
+static inline void
+nfs_copy_dname(struct dentry *dentry, struct nfs_unlinkdata *data)
+{
+ char *str;
+ int len = dentry->d_name.len;
+
+ str = kmalloc(NAME_ALLOC_LEN(len), GFP_KERNEL);
+ if (!str)
+ return;
+ memcpy(str, dentry->d_name.name, len);
+ if (!data->name.len) {
+ data->name.len = len;
+ data->name.name = str;
+ } else
+ kfree(str);
+}
+
+/**
+ * nfs_async_unlink_init - Initialize the RPC info
+ * @task: rpc_task of the sillydelete
+ *
+ * We delay initializing RPC info until after the call to dentry_iput()
+ * in order to minimize races against rename().
+ */
+static void
+nfs_async_unlink_init(struct rpc_task *task)
+{
+ struct nfs_unlinkdata *data = (struct nfs_unlinkdata *)task->tk_calldata;
+ struct dentry *dir = data->dir;
+ struct rpc_message msg;
+ int status = -ENOENT;
+
+ if (!data->name.len)
+ goto out_err;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.rpc_cred = data->cred;
+ status = NFS_PROTO(dir->d_inode)->unlink_setup(&msg, dir, &data->name);
+ if (status < 0)
+ goto out_err;
+ rpc_call_setup(task, &msg, 0);
+ return;
+ out_err:
+ rpc_exit(task, status);
+}
+
+/**
+ * nfs_async_unlink_done - Sillydelete post-processing
+ * @task: rpc_task of the sillydelete
+ *
+ * Do the directory attribute update.
+ */
+static void
+nfs_async_unlink_done(struct rpc_task *task)
+{
+ struct nfs_unlinkdata *data = (struct nfs_unlinkdata *)task->tk_calldata;
+ struct dentry *dir = data->dir;
+ struct inode *dir_i;
+
+ if (nfs_async_handle_jukebox(task))
+ return;
+ if (!dir)
+ return;
+ dir_i = dir->d_inode;
+ nfs_zap_caches(dir_i);
+ NFS_PROTO(dir_i)->unlink_done(dir, &task->tk_msg);
+ put_rpccred(data->cred);
+ data->cred = NULL;
+ dput(dir);
+}
+
+/**
+ * nfs_async_unlink_release - Release the sillydelete data.
+ * @task: rpc_task of the sillydelete
+ *
+ * We need to call nfs_put_unlinkdata as a 'tk_release' task since the
+ * rpc_task would be freed too.
+ */
+static void
+nfs_async_unlink_release(struct rpc_task *task)
+{
+ struct nfs_unlinkdata *data = (struct nfs_unlinkdata *)task->tk_calldata;
+ nfs_put_unlinkdata(data);
+}
+
+/**
+ * nfs_async_unlink - asynchronous unlinking of a file
+ * @dir: directory in which the file resides.
+ * @name: name of the file to unlink.
+ */
+int
+nfs_async_unlink(struct dentry *dentry)
+{
+ struct dentry *dir = dentry->d_parent;
+ struct nfs_unlinkdata *data;
+ struct rpc_task *task;
+ struct rpc_clnt *clnt = NFS_CLIENT(dir->d_inode);
+ int status = -ENOMEM;
+
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ goto out;
+ memset(data, 0, sizeof(*data));
+
+ data->dir = dget(dir);
+ data->dentry = dentry;
+
+ data->next = nfs_deletes;
+ nfs_deletes = data;
+ data->count = 1;
+
+ task = &data->task;
+ rpc_init_task(task, clnt, nfs_async_unlink_done , RPC_TASK_ASYNC);
+ task->tk_calldata = data;
+ task->tk_action = nfs_async_unlink_init;
+ task->tk_release = nfs_async_unlink_release;
+
+ dentry->d_flags |= DCACHE_NFSFS_RENAMED;
+ data->cred = rpcauth_lookupcred(clnt->cl_auth, 0);
+
+ rpc_sleep_on(&nfs_delete_queue, task, NULL, NULL);
+ status = 0;
+ out:
+ return status;
+}
+
+/**
+ * nfs_complete_remove - Initialize completion of the sillydelete
+ * @dentry: dentry to delete
+ *
+ * Since we're most likely to be called by dentry_iput(), we
+ * only use the dentry to find the sillydelete. We then copy the name
+ * into the qstr.
+ */
+void
+nfs_complete_unlink(struct dentry *dentry)
+{
+ struct nfs_unlinkdata *data;
+
+ for(data = nfs_deletes; data != NULL; data = data->next) {
+ if (dentry == data->dentry)
+ break;
+ }
+ if (!data)
+ return;
+ data->count++;
+ nfs_copy_dname(dentry, data);
+ dentry->d_flags &= ~DCACHE_NFSFS_RENAMED;
+ if (data->task.tk_rpcwait == &nfs_delete_queue)
+ rpc_wake_up_task(&data->task);
+ nfs_put_unlinkdata(data);
+}
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
new file mode 100644
index 00000000000000..01e84fc443b514
--- /dev/null
+++ b/fs/nfs/write.c
@@ -0,0 +1,1323 @@
+/*
+ * linux/fs/nfs/write.c
+ *
+ * Writing file data over NFS.
+ *
+ * We do it like this: When a (user) process wishes to write data to an
+ * NFS file, a write request is allocated that contains the RPC task data
+ * plus some info on the page to be written, and added to the inode's
+ * write chain. If the process writes past the end of the page, an async
+ * RPC call to write the page is scheduled immediately; otherwise, the call
+ * is delayed for a few seconds.
+ *
+ * Just like readahead, no async I/O is performed if wsize < PAGE_SIZE.
+ *
+ * Write requests are kept on the inode's writeback list. Each entry in
+ * that list references the page (portion) to be written. When the
+ * cache timeout has expired, the RPC task is woken up, and tries to
+ * lock the page. As soon as it manages to do so, the request is moved
+ * from the writeback list to the writelock list.
+ *
+ * Note: we must make sure never to confuse the inode passed in the
+ * write_page request with the one in page->inode. As far as I understand
+ * it, these are different when doing a swap-out.
+ *
+ * To understand everything that goes on here and in the NFS read code,
+ * one should be aware that a page is locked in exactly one of the following
+ * cases:
+ *
+ * - A write request is in progress.
+ * - A user process is in generic_file_write/nfs_update_page
+ * - A user process is in generic_file_read
+ *
+ * Also note that because of the way pages are invalidated in
+ * nfs_revalidate_inode, the following assertions hold:
+ *
+ * - If a page is dirty, there will be no read requests (a page will
+ * not be re-read unless invalidated by nfs_revalidate_inode).
+ * - If the page is not uptodate, there will be no pending write
+ * requests, and no process will be in nfs_update_page.
+ *
+ * FIXME: Interaction with the vmscan routines is not optimal yet.
+ * Either vmscan must be made nfs-savvy, or we need a different page
+ * reclaim concept that supports something like FS-independent
+ * buffer_heads with a b_ops-> field.
+ *
+ * Copyright (C) 1996, 1997, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/swap.h>
+#include <linux/pagemap.h>
+#include <linux/file.h>
+
+#include <linux/sunrpc/clnt.h>
+#include <linux/nfs_fs.h>
+#include <linux/nfs_mount.h>
+#include <linux/nfs_flushd.h>
+#include <linux/nfs_page.h>
+#include <asm/uaccess.h>
+#include <linux/smp_lock.h>
+
+#define NFSDBG_FACILITY NFSDBG_PAGECACHE
+
+/*
+ * Local structures
+ *
+ * This is the struct where the WRITE/COMMIT arguments go.
+ */
+struct nfs_write_data {
+ struct rpc_task task;
+ struct inode *inode;
+ struct rpc_cred *cred;
+ struct nfs_writeargs args; /* argument struct */
+ struct nfs_writeres res; /* result struct */
+ struct nfs_fattr fattr;
+ struct nfs_writeverf verf;
+ struct list_head pages; /* Coalesced requests we wish to flush */
+ struct page *pagevec[NFS_WRITE_MAXIOV];
+};
+
+/*
+ * Local function declarations
+ */
+static struct nfs_page * nfs_update_request(struct file*, struct inode *,
+ struct page *,
+ unsigned int, unsigned int);
+static void nfs_strategy(struct inode *inode);
+static void nfs_writeback_done(struct rpc_task *);
+#ifdef CONFIG_NFS_V3
+static void nfs_commit_done(struct rpc_task *);
+#endif
+
+/* Hack for future NFS swap support */
+#ifndef IS_SWAPFILE
+# define IS_SWAPFILE(inode) (0)
+#endif
+
+static kmem_cache_t *nfs_wdata_cachep;
+
+static __inline__ struct nfs_write_data *nfs_writedata_alloc(void)
+{
+ struct nfs_write_data *p;
+ p = kmem_cache_alloc(nfs_wdata_cachep, SLAB_NOFS);
+ if (p) {
+ memset(p, 0, sizeof(*p));
+ INIT_LIST_HEAD(&p->pages);
+ p->args.pages = p->pagevec;
+ }
+ return p;
+}
+
+static __inline__ void nfs_writedata_free(struct nfs_write_data *p)
+{
+ kmem_cache_free(nfs_wdata_cachep, p);
+}
+
+static void nfs_writedata_release(struct rpc_task *task)
+{
+ struct nfs_write_data *wdata = (struct nfs_write_data *)task->tk_calldata;
+ nfs_writedata_free(wdata);
+}
+
+/*
+ * Write a page synchronously.
+ * Offset is the data offset within the page.
+ */
+static int
+nfs_writepage_sync(struct file *file, struct inode *inode, struct page *page,
+ unsigned int offset, unsigned int count)
+{
+ struct rpc_cred *cred = NULL;
+ loff_t base;
+ unsigned int wsize = NFS_SERVER(inode)->wsize;
+ int result, refresh = 0, written = 0, flags;
+ u8 *buffer;
+ struct nfs_fattr fattr;
+ struct nfs_writeverf verf;
+
+
+ if (file)
+ cred = get_rpccred(nfs_file_cred(file));
+ if (!cred)
+ cred = get_rpccred(NFS_I(inode)->mm_cred);
+
+ dprintk("NFS: nfs_writepage_sync(%x/%Ld %d@%Ld)\n",
+ inode->i_dev, (long long)NFS_FILEID(inode),
+ count, (long long)(page_offset(page) + offset));
+
+ base = page_offset(page) + offset;
+
+ flags = ((IS_SWAPFILE(inode)) ? NFS_RW_SWAP : 0) | NFS_RW_SYNC;
+
+ do {
+ if (count < wsize && !IS_SWAPFILE(inode))
+ wsize = count;
+
+ result = NFS_PROTO(inode)->write(inode, cred, &fattr, flags,
+ offset, wsize, page, &verf);
+ nfs_write_attributes(inode, &fattr);
+
+ if (result < 0) {
+ /* Must mark the page invalid after I/O error */
+ ClearPageUptodate(page);
+ goto io_error;
+ }
+ if (result != wsize)
+ printk("NFS: short write, wsize=%u, result=%d\n",
+ wsize, result);
+ refresh = 1;
+ buffer += wsize;
+ base += wsize;
+ offset += wsize;
+ written += wsize;
+ count -= wsize;
+ /*
+ * If we've extended the file, update the inode
+ * now so we don't invalidate the cache.
+ */
+ if (base > inode->i_size)
+ inode->i_size = base;
+ } while (count);
+
+ if (PageError(page))
+ ClearPageError(page);
+
+io_error:
+ if (cred)
+ put_rpccred(cred);
+
+ return written? written : result;
+}
+
+static int
+nfs_writepage_async(struct file *file, struct inode *inode, struct page *page,
+ unsigned int offset, unsigned int count)
+{
+ struct nfs_page *req;
+ loff_t end;
+ int status;
+
+ req = nfs_update_request(file, inode, page, offset, count);
+ status = (IS_ERR(req)) ? PTR_ERR(req) : 0;
+ if (status < 0)
+ goto out;
+ if (!req->wb_cred)
+ req->wb_cred = get_rpccred(NFS_I(inode)->mm_cred);
+ nfs_unlock_request(req);
+ nfs_strategy(inode);
+ end = ((loff_t)page->index<<PAGE_CACHE_SHIFT) + (loff_t)(offset + count);
+ if (inode->i_size < end)
+ inode->i_size = end;
+
+ out:
+ return status;
+}
+
+/*
+ * Write an mmapped page to the server.
+ */
+int
+nfs_writepage(struct page *page)
+{
+ struct inode *inode = page->mapping->host;
+ unsigned long end_index;
+ unsigned offset = PAGE_CACHE_SIZE;
+ int inode_referenced = 0;
+ int err;
+
+ /*
+ * Note: We need to ensure that we have a reference to the inode
+ * if we are to do asynchronous writes. If not, waiting
+ * in nfs_wait_on_request() may deadlock with clear_inode().
+ *
+ * If igrab() fails here, then it is in any case safe to
+ * call nfs_wb_page(), since there will be no pending writes.
+ */
+ if (igrab(inode) != 0)
+ inode_referenced = 1;
+ end_index = inode->i_size >> PAGE_CACHE_SHIFT;
+
+ /* Ensure we've flushed out any previous writes */
+ nfs_wb_page(inode,page);
+
+ /* easy case */
+ if (page->index < end_index)
+ goto do_it;
+ /* things got complicated... */
+ offset = inode->i_size & (PAGE_CACHE_SIZE-1);
+
+ /* OK, are we completely out? */
+ err = -EIO;
+ if (page->index >= end_index+1 || !offset)
+ goto out;
+do_it:
+ lock_kernel();
+ if (NFS_SERVER(inode)->wsize >= PAGE_CACHE_SIZE && !IS_SYNC(inode) &&
+ inode_referenced) {
+ err = nfs_writepage_async(NULL, inode, page, 0, offset);
+ if (err >= 0)
+ err = 0;
+ } else {
+ err = nfs_writepage_sync(NULL, inode, page, 0, offset);
+ if (err == offset)
+ err = 0;
+ }
+ unlock_kernel();
+out:
+ UnlockPage(page);
+ if (inode_referenced)
+ iput(inode);
+ return err;
+}
+
+/*
+ * Check whether the file range we want to write to is locked by
+ * us.
+ */
+static int
+region_locked(struct inode *inode, struct nfs_page *req)
+{
+ struct file_lock *fl;
+ loff_t rqstart, rqend;
+
+ /* Don't optimize writes if we don't use NLM */
+ if (NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM)
+ return 0;
+
+ rqstart = page_offset(req->wb_page) + req->wb_offset;
+ rqend = rqstart + req->wb_bytes;
+ for (fl = inode->i_flock; fl; fl = fl->fl_next) {
+ if (fl->fl_owner == current->files && (fl->fl_flags & FL_POSIX)
+ && fl->fl_type == F_WRLCK
+ && fl->fl_start <= rqstart && rqend <= fl->fl_end) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Insert a write request into an inode
+ * Note: we sort the list in order to be able to optimize nfs_find_request()
+ * & co. for the 'write append' case. For 2.5 we may want to consider
+ * some form of hashing so as to perform well on random writes.
+ */
+static inline void
+nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
+{
+ struct list_head *pos, *head;
+ unsigned long pg_idx = page_index(req->wb_page);
+
+ if (!list_empty(&req->wb_hash))
+ return;
+ if (!NFS_WBACK_BUSY(req))
+ printk(KERN_ERR "NFS: unlocked request attempted hashed!\n");
+ head = &inode->u.nfs_i.writeback;
+ if (list_empty(head))
+ igrab(inode);
+ list_for_each_prev(pos, head) {
+ struct nfs_page *entry = nfs_inode_wb_entry(pos);
+ if (page_index(entry->wb_page) < pg_idx)
+ break;
+ }
+ inode->u.nfs_i.npages++;
+ list_add(&req->wb_hash, pos);
+ req->wb_count++;
+}
+
+/*
+ * Insert a write request into an inode
+ */
+static inline void
+nfs_inode_remove_request(struct nfs_page *req)
+{
+ struct inode *inode;
+ spin_lock(&nfs_wreq_lock);
+ if (list_empty(&req->wb_hash)) {
+ spin_unlock(&nfs_wreq_lock);
+ return;
+ }
+ if (!NFS_WBACK_BUSY(req))
+ printk(KERN_ERR "NFS: unlocked request attempted unhashed!\n");
+ inode = req->wb_inode;
+ list_del(&req->wb_hash);
+ INIT_LIST_HEAD(&req->wb_hash);
+ inode->u.nfs_i.npages--;
+ if ((inode->u.nfs_i.npages == 0) != list_empty(&inode->u.nfs_i.writeback))
+ printk(KERN_ERR "NFS: desynchronized value of nfs_i.npages.\n");
+ if (list_empty(&inode->u.nfs_i.writeback)) {
+ spin_unlock(&nfs_wreq_lock);
+ iput(inode);
+ } else
+ spin_unlock(&nfs_wreq_lock);
+ nfs_clear_request(req);
+ nfs_release_request(req);
+}
+
+/*
+ * Find a request
+ */
+static inline struct nfs_page *
+_nfs_find_request(struct inode *inode, struct page *page)
+{
+ struct list_head *head, *pos;
+ unsigned long pg_idx = page_index(page);
+
+ head = &inode->u.nfs_i.writeback;
+ list_for_each_prev(pos, head) {
+ struct nfs_page *req = nfs_inode_wb_entry(pos);
+ unsigned long found_idx = page_index(req->wb_page);
+
+ if (pg_idx < found_idx)
+ continue;
+ if (pg_idx != found_idx)
+ break;
+ req->wb_count++;
+ return req;
+ }
+ return NULL;
+}
+
+static struct nfs_page *
+nfs_find_request(struct inode *inode, struct page *page)
+{
+ struct nfs_page *req;
+
+ spin_lock(&nfs_wreq_lock);
+ req = _nfs_find_request(inode, page);
+ spin_unlock(&nfs_wreq_lock);
+ return req;
+}
+
+/*
+ * Add a request to the inode's dirty list.
+ */
+static inline void
+nfs_mark_request_dirty(struct nfs_page *req)
+{
+ struct inode *inode = req->wb_inode;
+
+ spin_lock(&nfs_wreq_lock);
+ nfs_list_add_request(req, &inode->u.nfs_i.dirty);
+ inode->u.nfs_i.ndirty++;
+ __nfs_del_lru(req);
+ __nfs_add_lru(&NFS_SERVER(inode)->lru_dirty, req);
+ spin_unlock(&nfs_wreq_lock);
+ mark_inode_dirty(inode);
+}
+
+/*
+ * Check if a request is dirty
+ */
+static inline int
+nfs_dirty_request(struct nfs_page *req)
+{
+ struct inode *inode = req->wb_inode;
+ return !list_empty(&req->wb_list) && req->wb_list_head == &inode->u.nfs_i.dirty;
+}
+
+#ifdef CONFIG_NFS_V3
+/*
+ * Add a request to the inode's commit list.
+ */
+static inline void
+nfs_mark_request_commit(struct nfs_page *req)
+{
+ struct inode *inode = req->wb_inode;
+
+ spin_lock(&nfs_wreq_lock);
+ nfs_list_add_request(req, &inode->u.nfs_i.commit);
+ inode->u.nfs_i.ncommit++;
+ __nfs_del_lru(req);
+ __nfs_add_lru(&NFS_SERVER(inode)->lru_commit, req);
+ spin_unlock(&nfs_wreq_lock);
+ mark_inode_dirty(inode);
+}
+#endif
+
+/*
+ * Wait for a request to complete.
+ *
+ * Interruptible by signals only if mounted with intr flag.
+ */
+static int
+nfs_wait_on_requests(struct inode *inode, unsigned long idx_start, unsigned int npages)
+{
+ struct list_head *p, *head;
+ unsigned long idx_end;
+ unsigned int res = 0;
+ int error;
+
+ if (npages == 0)
+ idx_end = ~0;
+ else
+ idx_end = idx_start + npages - 1;
+
+ head = &inode->u.nfs_i.writeback;
+ restart:
+ spin_lock(&nfs_wreq_lock);
+ list_for_each_prev(p, head) {
+ unsigned long pg_idx;
+ struct nfs_page *req = nfs_inode_wb_entry(p);
+
+ pg_idx = page_index(req->wb_page);
+ if (pg_idx < idx_start)
+ break;
+ if (pg_idx > idx_end)
+ continue;
+
+ if (!NFS_WBACK_BUSY(req))
+ continue;
+ req->wb_count++;
+ spin_unlock(&nfs_wreq_lock);
+ error = nfs_wait_on_request(req);
+ nfs_release_request(req);
+ if (error < 0)
+ return error;
+ res++;
+ goto restart;
+ }
+ spin_unlock(&nfs_wreq_lock);
+ return res;
+}
+
+/**
+ * nfs_scan_lru_dirty_timeout - Scan LRU list for timed out dirty requests
+ * @server: NFS superblock data
+ * @dst: destination list
+ *
+ * Moves a maximum of 'wpages' requests from the NFS dirty page LRU list.
+ * The elements are checked to ensure that they form a contiguous set
+ * of pages, and that they originated from the same file.
+ */
+int
+nfs_scan_lru_dirty_timeout(struct nfs_server *server, struct list_head *dst)
+{
+ struct inode *inode;
+ int npages;
+
+ npages = nfs_scan_lru_timeout(&server->lru_dirty, dst, server->wpages);
+ if (npages) {
+ inode = nfs_list_entry(dst->next)->wb_inode;
+ inode->u.nfs_i.ndirty -= npages;
+ }
+ return npages;
+}
+
+/**
+ * nfs_scan_lru_dirty - Scan LRU list for dirty requests
+ * @server: NFS superblock data
+ * @dst: destination list
+ *
+ * Moves a maximum of 'wpages' requests from the NFS dirty page LRU list.
+ * The elements are checked to ensure that they form a contiguous set
+ * of pages, and that they originated from the same file.
+ */
+int
+nfs_scan_lru_dirty(struct nfs_server *server, struct list_head *dst)
+{
+ struct inode *inode;
+ int npages;
+
+ npages = nfs_scan_lru(&server->lru_dirty, dst, server->wpages);
+ if (npages) {
+ inode = nfs_list_entry(dst->next)->wb_inode;
+ inode->u.nfs_i.ndirty -= npages;
+ }
+ return npages;
+}
+
+/*
+ * nfs_scan_dirty - Scan an inode for dirty requests
+ * @inode: NFS inode to scan
+ * @dst: destination list
+ * @idx_start: lower bound of page->index to scan.
+ * @npages: idx_start + npages sets the upper bound to scan.
+ *
+ * Moves requests from the inode's dirty page list.
+ * The requests are *not* checked to ensure that they form a contiguous set.
+ */
+static int
+nfs_scan_dirty(struct inode *inode, struct list_head *dst, unsigned long idx_start, unsigned int npages)
+{
+ int res;
+ res = nfs_scan_list(&inode->u.nfs_i.dirty, dst, idx_start, npages);
+ inode->u.nfs_i.ndirty -= res;
+ if ((inode->u.nfs_i.ndirty == 0) != list_empty(&inode->u.nfs_i.dirty))
+ printk(KERN_ERR "NFS: desynchronized value of nfs_i.ndirty.\n");
+ return res;
+}
+
+#ifdef CONFIG_NFS_V3
+/**
+ * nfs_scan_lru_commit_timeout - Scan LRU list for timed out commit requests
+ * @server: NFS superblock data
+ * @dst: destination list
+ *
+ * Finds the first a timed out request in the NFS commit LRU list and moves it
+ * to the list dst. If such an element is found, we move all other commit
+ * requests that apply to the same inode.
+ * The assumption is that doing everything in a single commit-to-disk is
+ * the cheaper alternative.
+ */
+int
+nfs_scan_lru_commit_timeout(struct nfs_server *server, struct list_head *dst)
+{
+ struct inode *inode;
+ int npages;
+
+ npages = nfs_scan_lru_timeout(&server->lru_commit, dst, 1);
+ if (npages) {
+ inode = nfs_list_entry(dst->next)->wb_inode;
+ npages += nfs_scan_list(&inode->u.nfs_i.commit, dst, 0, 0);
+ inode->u.nfs_i.ncommit -= npages;
+ }
+ return npages;
+}
+
+
+/**
+ * nfs_scan_lru_commit_timeout - Scan LRU list for timed out commit requests
+ * @server: NFS superblock data
+ * @dst: destination list
+ *
+ * Finds the first request in the NFS commit LRU list and moves it
+ * to the list dst. If such an element is found, we move all other commit
+ * requests that apply to the same inode.
+ * The assumption is that doing everything in a single commit-to-disk is
+ * the cheaper alternative.
+ */
+int
+nfs_scan_lru_commit(struct nfs_server *server, struct list_head *dst)
+{
+ struct inode *inode;
+ int npages;
+
+ npages = nfs_scan_lru(&server->lru_commit, dst, 1);
+ if (npages) {
+ inode = nfs_list_entry(dst->next)->wb_inode;
+ npages += nfs_scan_list(&inode->u.nfs_i.commit, dst, 0, 0);
+ inode->u.nfs_i.ncommit -= npages;
+ }
+ return npages;
+}
+
+/*
+ * nfs_scan_commit - Scan an inode for commit requests
+ * @inode: NFS inode to scan
+ * @dst: destination list
+ * @idx_start: lower bound of page->index to scan.
+ * @npages: idx_start + npages sets the upper bound to scan.
+ *
+ * Moves requests from the inode's 'commit' request list.
+ * The requests are *not* checked to ensure that they form a contiguous set.
+ */
+static int
+nfs_scan_commit(struct inode *inode, struct list_head *dst, unsigned long idx_start, unsigned int npages)
+{
+ int res;
+ res = nfs_scan_list(&inode->u.nfs_i.commit, dst, idx_start, npages);
+ inode->u.nfs_i.ncommit -= res;
+ if ((inode->u.nfs_i.ncommit == 0) != list_empty(&inode->u.nfs_i.commit))
+ printk(KERN_ERR "NFS: desynchronized value of nfs_i.ncommit.\n");
+ return res;
+}
+#endif
+
+
+/*
+ * Try to update any existing write request, or create one if there is none.
+ * In order to match, the request's credentials must match those of
+ * the calling process.
+ *
+ * Note: Should always be called with the Page Lock held!
+ */
+static struct nfs_page *
+nfs_update_request(struct file* file, struct inode *inode, struct page *page,
+ unsigned int offset, unsigned int bytes)
+{
+ struct nfs_page *req, *new = NULL;
+ unsigned long rqend, end;
+
+ end = offset + bytes;
+
+ for (;;) {
+ /* Loop over all inode entries and see if we find
+ * A request for the page we wish to update
+ */
+ spin_lock(&nfs_wreq_lock);
+ req = _nfs_find_request(inode, page);
+ if (req) {
+ if (!nfs_lock_request_dontget(req)) {
+ int error;
+ spin_unlock(&nfs_wreq_lock);
+ error = nfs_wait_on_request(req);
+ nfs_release_request(req);
+ if (error < 0)
+ return ERR_PTR(error);
+ continue;
+ }
+ spin_unlock(&nfs_wreq_lock);
+ if (new)
+ nfs_release_request(new);
+ break;
+ }
+
+ if (new) {
+ nfs_lock_request_dontget(new);
+ nfs_inode_add_request(inode, new);
+ spin_unlock(&nfs_wreq_lock);
+ nfs_mark_request_dirty(new);
+ return new;
+ }
+ spin_unlock(&nfs_wreq_lock);
+
+ new = nfs_create_request(nfs_file_cred(file), inode, page, offset, bytes);
+ if (IS_ERR(new))
+ return new;
+ if (file) {
+ new->wb_file = file;
+ get_file(file);
+ }
+ /* If the region is locked, adjust the timeout */
+ if (region_locked(inode, new))
+ new->wb_timeout = jiffies + NFS_WRITEBACK_LOCKDELAY;
+ else
+ new->wb_timeout = jiffies + NFS_WRITEBACK_DELAY;
+ }
+
+ /* We have a request for our page.
+ * If the creds don't match, or the
+ * page addresses don't match,
+ * tell the caller to wait on the conflicting
+ * request.
+ */
+ rqend = req->wb_offset + req->wb_bytes;
+ if (req->wb_file != file
+ || req->wb_page != page
+ || !nfs_dirty_request(req)
+ || offset > rqend || end < req->wb_offset) {
+ nfs_unlock_request(req);
+ return ERR_PTR(-EBUSY);
+ }
+
+ /* Okay, the request matches. Update the region */
+ if (offset < req->wb_offset) {
+ req->wb_offset = offset;
+ req->wb_bytes = rqend - req->wb_offset;
+ }
+
+ if (end > rqend)
+ req->wb_bytes = end - req->wb_offset;
+
+ return req;
+}
+
+/*
+ * This is the strategy routine for NFS.
+ * It is called by nfs_updatepage whenever the user wrote up to the end
+ * of a page.
+ *
+ * We always try to submit a set of requests in parallel so that the
+ * server's write code can gather writes. This is mainly for the benefit
+ * of NFSv2.
+ *
+ * We never submit more requests than we think the remote can handle.
+ * For UDP sockets, we make sure we don't exceed the congestion window;
+ * for TCP, we limit the number of requests to 8.
+ *
+ * NFS_STRATEGY_PAGES gives the minimum number of requests for NFSv2 that
+ * should be sent out in one go. This is for the benefit of NFSv2 servers
+ * that perform write gathering.
+ *
+ * FIXME: Different servers may have different sweet spots.
+ * Record the average congestion window in server struct?
+ */
+#define NFS_STRATEGY_PAGES 8
+static void
+nfs_strategy(struct inode *inode)
+{
+ unsigned int dirty, wpages;
+
+ dirty = inode->u.nfs_i.ndirty;
+ wpages = NFS_SERVER(inode)->wpages;
+#ifdef CONFIG_NFS_V3
+ if (NFS_PROTO(inode)->version == 2) {
+ if (dirty >= NFS_STRATEGY_PAGES * wpages)
+ nfs_flush_file(inode, 0, 0, 0);
+ } else if (dirty >= wpages)
+ nfs_flush_file(inode, 0, 0, 0);
+#else
+ if (dirty >= NFS_STRATEGY_PAGES * wpages)
+ nfs_flush_file(inode, 0, 0, 0);
+#endif
+}
+
+int
+nfs_flush_incompatible(struct file *file, struct page *page)
+{
+ struct rpc_cred *cred = nfs_file_cred(file);
+ struct inode *inode = page->mapping->host;
+ struct nfs_page *req;
+ int status = 0;
+ /*
+ * Look for a request corresponding to this page. If there
+ * is one, and it belongs to another file, we flush it out
+ * before we try to copy anything into the page. Do this
+ * due to the lack of an ACCESS-type call in NFSv2.
+ * Also do the same if we find a request from an existing
+ * dropped page.
+ */
+ req = nfs_find_request(inode,page);
+ if (req) {
+ if (req->wb_file != file || req->wb_cred != cred || req->wb_page != page)
+ status = nfs_wb_page(inode, page);
+ nfs_release_request(req);
+ }
+ return (status < 0) ? status : 0;
+}
+
+/*
+ * Update and possibly write a cached page of an NFS file.
+ *
+ * XXX: Keep an eye on generic_file_read to make sure it doesn't do bad
+ * things with a page scheduled for an RPC call (e.g. invalidate it).
+ */
+int
+nfs_updatepage(struct file *file, struct page *page, unsigned int offset, unsigned int count)
+{
+ struct dentry *dentry = file->f_dentry;
+ struct inode *inode = page->mapping->host;
+ struct nfs_page *req;
+ loff_t end;
+ int status = 0;
+
+ dprintk("NFS: nfs_updatepage(%s/%s %d@%Ld)\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ count, (long long)(page_offset(page) +offset));
+
+ /*
+ * If wsize is smaller than page size, update and write
+ * page synchronously.
+ */
+ if (NFS_SERVER(inode)->wsize < PAGE_CACHE_SIZE || IS_SYNC(inode)) {
+ status = nfs_writepage_sync(file, inode, page, offset, count);
+ if (status > 0) {
+ if (offset == 0 && status == PAGE_CACHE_SIZE)
+ SetPageUptodate(page);
+ return 0;
+ }
+ return status;
+ }
+
+ /*
+ * Try to find an NFS request corresponding to this page
+ * and update it.
+ * If the existing request cannot be updated, we must flush
+ * it out now.
+ */
+ do {
+ req = nfs_update_request(file, inode, page, offset, count);
+ status = (IS_ERR(req)) ? PTR_ERR(req) : 0;
+ if (status != -EBUSY)
+ break;
+ /* Request could not be updated. Flush it out and try again */
+ status = nfs_wb_page(inode, page);
+ } while (status >= 0);
+ if (status < 0)
+ goto done;
+
+ status = 0;
+ end = ((loff_t)page->index<<PAGE_CACHE_SHIFT) + (loff_t)(offset + count);
+ if (inode->i_size < end)
+ inode->i_size = end;
+
+ /* If we wrote past the end of the page.
+ * Call the strategy routine so it can send out a bunch
+ * of requests.
+ */
+ if (req->wb_offset == 0 && req->wb_bytes == PAGE_CACHE_SIZE) {
+ SetPageUptodate(page);
+ nfs_unlock_request(req);
+ nfs_strategy(inode);
+ } else
+ nfs_unlock_request(req);
+done:
+ dprintk("NFS: nfs_updatepage returns %d (isize %Ld)\n",
+ status, (long long)inode->i_size);
+ if (status < 0)
+ ClearPageUptodate(page);
+ return status;
+}
+
+/*
+ * Set up the argument/result storage required for the RPC call.
+ */
+static void
+nfs_write_rpcsetup(struct list_head *head, struct nfs_write_data *data)
+{
+ struct nfs_page *req;
+ struct page **pages;
+ unsigned int count;
+
+ /* Set up the RPC argument and reply structs
+ * NB: take care not to mess about with data->commit et al. */
+
+ pages = data->args.pages;
+ count = 0;
+ while (!list_empty(head)) {
+ struct nfs_page *req = nfs_list_entry(head->next);
+ nfs_list_remove_request(req);
+ nfs_list_add_request(req, &data->pages);
+ *pages++ = req->wb_page;
+ count += req->wb_bytes;
+ }
+ req = nfs_list_entry(data->pages.next);
+ data->inode = req->wb_inode;
+ data->cred = req->wb_cred;
+ data->args.fh = NFS_FH(req->wb_inode);
+ data->args.offset = page_offset(req->wb_page) + req->wb_offset;
+ data->args.pgbase = req->wb_offset;
+ data->args.count = count;
+ data->res.fattr = &data->fattr;
+ data->res.count = count;
+ data->res.verf = &data->verf;
+}
+
+
+/*
+ * Create an RPC task for the given write request and kick it.
+ * The page must have been locked by the caller.
+ *
+ * It may happen that the page we're passed is not marked dirty.
+ * This is the case if nfs_updatepage detects a conflicting request
+ * that has been written but not committed.
+ */
+static int
+nfs_flush_one(struct list_head *head, struct inode *inode, int how)
+{
+ struct rpc_clnt *clnt = NFS_CLIENT(inode);
+ struct nfs_write_data *data;
+ struct rpc_task *task;
+ struct rpc_message msg;
+ int flags,
+ nfsvers = NFS_PROTO(inode)->version,
+ async = !(how & FLUSH_SYNC),
+ stable = (how & FLUSH_STABLE);
+ sigset_t oldset;
+
+
+ data = nfs_writedata_alloc();
+ if (!data)
+ goto out_bad;
+ task = &data->task;
+
+ /* Set the initial flags for the task. */
+ flags = (async) ? RPC_TASK_ASYNC : 0;
+
+ /* Set up the argument struct */
+ nfs_write_rpcsetup(head, data);
+ if (nfsvers < 3)
+ data->args.stable = NFS_FILE_SYNC;
+ else if (stable) {
+ if (!inode->u.nfs_i.ncommit)
+ data->args.stable = NFS_FILE_SYNC;
+ else
+ data->args.stable = NFS_DATA_SYNC;
+ } else
+ data->args.stable = NFS_UNSTABLE;
+
+ /* Finalize the task. */
+ rpc_init_task(task, clnt, nfs_writeback_done, flags);
+ task->tk_calldata = data;
+ /* Release requests */
+ task->tk_release = nfs_writedata_release;
+
+#ifdef CONFIG_NFS_V3
+ msg.rpc_proc = (nfsvers == 3) ? NFS3PROC_WRITE : NFSPROC_WRITE;
+#else
+ msg.rpc_proc = NFSPROC_WRITE;
+#endif
+ msg.rpc_argp = &data->args;
+ msg.rpc_resp = &data->res;
+ msg.rpc_cred = data->cred;
+
+ dprintk("NFS: %4d initiated write call (req %x/%Ld count %u)\n",
+ task->tk_pid,
+ inode->i_dev,
+ (long long)NFS_FILEID(inode),
+ data->args.count);
+
+ rpc_clnt_sigmask(clnt, &oldset);
+ rpc_call_setup(task, &msg, 0);
+ lock_kernel();
+ rpc_execute(task);
+ unlock_kernel();
+ rpc_clnt_sigunmask(clnt, &oldset);
+ return 0;
+ out_bad:
+ while (!list_empty(head)) {
+ struct nfs_page *req = nfs_list_entry(head->next);
+ nfs_list_remove_request(req);
+ nfs_mark_request_dirty(req);
+ nfs_unlock_request(req);
+ }
+ return -ENOMEM;
+}
+
+int
+nfs_flush_list(struct list_head *head, int wpages, int how)
+{
+ LIST_HEAD(one_request);
+ struct nfs_page *req;
+ int error = 0;
+ unsigned int pages = 0;
+
+ while (!list_empty(head)) {
+ pages += nfs_coalesce_requests(head, &one_request, wpages);
+ req = nfs_list_entry(one_request.next);
+ error = nfs_flush_one(&one_request, req->wb_inode, how);
+ if (error < 0)
+ break;
+ }
+ if (error >= 0)
+ return pages;
+
+ while (!list_empty(head)) {
+ req = nfs_list_entry(head->next);
+ nfs_list_remove_request(req);
+ nfs_mark_request_dirty(req);
+ nfs_unlock_request(req);
+ }
+ return error;
+}
+
+
+/*
+ * This function is called when the WRITE call is complete.
+ */
+static void
+nfs_writeback_done(struct rpc_task *task)
+{
+ struct nfs_write_data *data = (struct nfs_write_data *) task->tk_calldata;
+ struct nfs_writeargs *argp = &data->args;
+ struct nfs_writeres *resp = &data->res;
+ struct inode *inode = data->inode;
+ struct nfs_page *req;
+ struct page *page;
+
+ dprintk("NFS: %4d nfs_writeback_done (status %d)\n",
+ task->tk_pid, task->tk_status);
+
+ if (nfs_async_handle_jukebox(task))
+ return;
+
+ /* We can't handle that yet but we check for it nevertheless */
+ if (resp->count < argp->count && task->tk_status >= 0) {
+ static unsigned long complain;
+ if (time_before(complain, jiffies)) {
+ printk(KERN_WARNING
+ "NFS: Server wrote less than requested.\n");
+ complain = jiffies + 300 * HZ;
+ }
+ /* Can't do anything about it right now except throw
+ * an error. */
+ task->tk_status = -EIO;
+ }
+#ifdef CONFIG_NFS_V3
+ if (resp->verf->committed < argp->stable && task->tk_status >= 0) {
+ /* We tried a write call, but the server did not
+ * commit data to stable storage even though we
+ * requested it.
+ * Note: There is a known bug in Tru64 < 5.0 in which
+ * the server reports NFS_DATA_SYNC, but performs
+ * NFS_FILE_SYNC. We therefore implement this checking
+ * as a dprintk() in order to avoid filling syslog.
+ */
+ static unsigned long complain;
+
+ if (time_before(complain, jiffies)) {
+ dprintk("NFS: faulty NFSv3 server %s:"
+ " (committed = %d) != (stable = %d)\n",
+ NFS_SERVER(inode)->hostname,
+ resp->verf->committed, argp->stable);
+ complain = jiffies + 300 * HZ;
+ }
+ }
+#endif
+
+ /*
+ * Update attributes as result of writeback.
+ * FIXME: There is an inherent race with invalidate_inode_pages and
+ * writebacks since the page->count is kept > 1 for as long
+ * as the page has a write request pending.
+ */
+ nfs_write_attributes(inode, resp->fattr);
+ while (!list_empty(&data->pages)) {
+ req = nfs_list_entry(data->pages.next);
+ nfs_list_remove_request(req);
+ page = req->wb_page;
+
+ dprintk("NFS: write (%x/%Ld %d@%Ld)",
+ req->wb_inode->i_dev,
+ (long long)NFS_FILEID(req->wb_inode),
+ req->wb_bytes,
+ (long long)(page_offset(page) + req->wb_offset));
+
+ if (task->tk_status < 0) {
+ ClearPageUptodate(page);
+ SetPageError(page);
+ if (req->wb_file)
+ req->wb_file->f_error = task->tk_status;
+ nfs_inode_remove_request(req);
+ dprintk(", error = %d\n", task->tk_status);
+ goto next;
+ }
+
+#ifdef CONFIG_NFS_V3
+ if (argp->stable != NFS_UNSTABLE || resp->verf->committed == NFS_FILE_SYNC) {
+ nfs_inode_remove_request(req);
+ dprintk(" OK\n");
+ goto next;
+ }
+ memcpy(&req->wb_verf, resp->verf, sizeof(req->wb_verf));
+ req->wb_timeout = jiffies + NFS_COMMIT_DELAY;
+ nfs_mark_request_commit(req);
+ dprintk(" marked for commit\n");
+#else
+ nfs_inode_remove_request(req);
+#endif
+ next:
+ nfs_unlock_request(req);
+ }
+}
+
+
+#ifdef CONFIG_NFS_V3
+/*
+ * Set up the argument/result storage required for the RPC call.
+ */
+static void
+nfs_commit_rpcsetup(struct list_head *head, struct nfs_write_data *data)
+{
+ struct nfs_page *first, *last;
+ struct inode *inode;
+ loff_t start, end, len;
+
+ /* Set up the RPC argument and reply structs
+ * NB: take care not to mess about with data->commit et al. */
+
+ list_splice(head, &data->pages);
+ INIT_LIST_HEAD(head);
+ first = nfs_list_entry(data->pages.next);
+ last = nfs_list_entry(data->pages.prev);
+ inode = first->wb_inode;
+
+ /*
+ * Determine the offset range of requests in the COMMIT call.
+ * We rely on the fact that data->pages is an ordered list...
+ */
+ start = page_offset(first->wb_page) + first->wb_offset;
+ end = page_offset(last->wb_page) + (last->wb_offset + last->wb_bytes);
+ len = end - start;
+ /* If 'len' is not a 32-bit quantity, pass '0' in the COMMIT call */
+ if (end >= inode->i_size || len < 0 || len > (~((u32)0) >> 1))
+ len = 0;
+
+ data->inode = inode;
+ data->cred = first->wb_cred;
+ data->args.fh = NFS_FH(inode);
+ data->args.offset = start;
+ data->res.count = data->args.count = (u32)len;
+ data->res.fattr = &data->fattr;
+ data->res.verf = &data->verf;
+}
+
+/*
+ * Commit dirty pages
+ */
+int
+nfs_commit_list(struct list_head *head, int how)
+{
+ struct rpc_message msg;
+ struct rpc_clnt *clnt;
+ struct nfs_write_data *data;
+ struct rpc_task *task;
+ struct nfs_page *req;
+ int flags,
+ async = !(how & FLUSH_SYNC);
+ sigset_t oldset;
+
+ data = nfs_writedata_alloc();
+
+ if (!data)
+ goto out_bad;
+ task = &data->task;
+
+ flags = (async) ? RPC_TASK_ASYNC : 0;
+
+ /* Set up the argument struct */
+ nfs_commit_rpcsetup(head, data);
+ req = nfs_list_entry(data->pages.next);
+ clnt = NFS_CLIENT(req->wb_inode);
+
+ rpc_init_task(task, clnt, nfs_commit_done, flags);
+ task->tk_calldata = data;
+ /* Release requests */
+ task->tk_release = nfs_writedata_release;
+
+ msg.rpc_proc = NFS3PROC_COMMIT;
+ msg.rpc_argp = &data->args;
+ msg.rpc_resp = &data->res;
+ msg.rpc_cred = data->cred;
+
+ dprintk("NFS: %4d initiated commit call\n", task->tk_pid);
+ rpc_clnt_sigmask(clnt, &oldset);
+ rpc_call_setup(task, &msg, 0);
+ lock_kernel();
+ rpc_execute(task);
+ unlock_kernel();
+ rpc_clnt_sigunmask(clnt, &oldset);
+ return 0;
+ out_bad:
+ while (!list_empty(head)) {
+ req = nfs_list_entry(head->next);
+ nfs_list_remove_request(req);
+ nfs_mark_request_commit(req);
+ nfs_unlock_request(req);
+ }
+ return -ENOMEM;
+}
+
+/*
+ * COMMIT call returned
+ */
+static void
+nfs_commit_done(struct rpc_task *task)
+{
+ struct nfs_write_data *data = (struct nfs_write_data *)task->tk_calldata;
+ struct nfs_writeres *resp = &data->res;
+ struct nfs_page *req;
+ struct inode *inode = data->inode;
+
+ dprintk("NFS: %4d nfs_commit_done (status %d)\n",
+ task->tk_pid, task->tk_status);
+
+ if (nfs_async_handle_jukebox(task))
+ return;
+
+ nfs_write_attributes(inode, resp->fattr);
+ while (!list_empty(&data->pages)) {
+ req = nfs_list_entry(data->pages.next);
+ nfs_list_remove_request(req);
+
+ dprintk("NFS: commit (%x/%Ld %d@%Ld)",
+ req->wb_inode->i_dev,
+ (long long)NFS_FILEID(req->wb_inode),
+ req->wb_bytes,
+ (long long)(page_offset(req->wb_page) + req->wb_offset));
+ if (task->tk_status < 0) {
+ if (req->wb_file)
+ req->wb_file->f_error = task->tk_status;
+ nfs_inode_remove_request(req);
+ dprintk(", error = %d\n", task->tk_status);
+ goto next;
+ }
+
+ /* Okay, COMMIT succeeded, apparently. Check the verifier
+ * returned by the server against all stored verfs. */
+ if (!memcmp(req->wb_verf.verifier, data->verf.verifier, sizeof(data->verf.verifier))) {
+ /* We have a match */
+ nfs_inode_remove_request(req);
+ dprintk(" OK\n");
+ goto next;
+ }
+ /* We have a mismatch. Write the page again */
+ dprintk(" mismatch\n");
+ nfs_mark_request_dirty(req);
+ next:
+ nfs_unlock_request(req);
+ }
+}
+#endif
+
+int nfs_flush_file(struct inode *inode, unsigned long idx_start,
+ unsigned int npages, int how)
+{
+ LIST_HEAD(head);
+ int res,
+ error = 0;
+
+ spin_lock(&nfs_wreq_lock);
+ res = nfs_scan_dirty(inode, &head, idx_start, npages);
+ spin_unlock(&nfs_wreq_lock);
+ if (res)
+ error = nfs_flush_list(&head, NFS_SERVER(inode)->wpages, how);
+ if (error < 0)
+ return error;
+ return res;
+}
+
+#ifdef CONFIG_NFS_V3
+int nfs_commit_file(struct inode *inode, int how)
+{
+ LIST_HEAD(head);
+ int res,
+ error = 0;
+
+ spin_lock(&nfs_wreq_lock);
+ res = nfs_scan_commit(inode, &head, 0, 0);
+ spin_unlock(&nfs_wreq_lock);
+ if (res)
+ error = nfs_commit_list(&head, how);
+ if (error < 0)
+ return error;
+ return res;
+}
+#endif
+
+int nfs_sync_file(struct inode *inode, unsigned long idx_start,
+ unsigned int npages, int how)
+{
+ int error,
+ wait;
+
+ wait = how & FLUSH_WAIT;
+ how &= ~FLUSH_WAIT;
+
+ do {
+ error = 0;
+ if (wait)
+ error = nfs_wait_on_requests(inode, idx_start, npages);
+ if (error == 0)
+ error = nfs_flush_file(inode, idx_start, npages, how);
+#ifdef CONFIG_NFS_V3
+ if (error == 0)
+ error = nfs_commit_file(inode, how);
+#endif
+ } while (error > 0);
+ return error;
+}
+
+int nfs_init_writepagecache(void)
+{
+ nfs_wdata_cachep = kmem_cache_create("nfs_write_data",
+ sizeof(struct nfs_write_data),
+ 0, SLAB_HWCACHE_ALIGN,
+ NULL, NULL);
+ if (nfs_wdata_cachep == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+
+void nfs_destroy_writepagecache(void)
+{
+ if (kmem_cache_destroy(nfs_wdata_cachep))
+ printk(KERN_INFO "nfs_write_data: not all structures were freed\n");
+}
+
diff --git a/fs/nfsd/Makefile b/fs/nfsd/Makefile
new file mode 100644
index 00000000000000..17775d0fbf16d0
--- /dev/null
+++ b/fs/nfsd/Makefile
@@ -0,0 +1,20 @@
+#
+# Makefile for the Linux nfs filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile.
+
+O_TARGET := nfsd.o
+
+obj-y := nfssvc.o nfsctl.o nfsproc.o nfsfh.o vfs.o \
+ export.o auth.o lockd.o nfscache.o nfsxdr.o \
+ stats.o
+
+obj-$(CONFIG_NFSD_V3) += nfs3proc.o nfs3xdr.o
+
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/nfsd/auth.c b/fs/nfsd/auth.c
new file mode 100644
index 00000000000000..12fa97fe1bfaff
--- /dev/null
+++ b/fs/nfsd/auth.c
@@ -0,0 +1,61 @@
+/*
+ * linux/fs/nfsd/auth.c
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/sunrpc/svcauth.h>
+#include <linux/nfsd/nfsd.h>
+
+#define CAP_NFSD_MASK (CAP_FS_MASK|CAP_TO_MASK(CAP_SYS_RESOURCE))
+void
+nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp)
+{
+ struct svc_cred *cred = &rqstp->rq_cred;
+ int i;
+
+ if (rqstp->rq_userset)
+ return;
+
+ if (exp->ex_flags & NFSEXP_ALLSQUASH) {
+ cred->cr_uid = exp->ex_anon_uid;
+ cred->cr_gid = exp->ex_anon_gid;
+ cred->cr_groups[0] = NOGROUP;
+ } else if (exp->ex_flags & NFSEXP_ROOTSQUASH) {
+ if (!cred->cr_uid)
+ cred->cr_uid = exp->ex_anon_uid;
+ if (!cred->cr_gid)
+ cred->cr_gid = exp->ex_anon_gid;
+ for (i = 0; i < NGROUPS; i++)
+ if (!cred->cr_groups[i])
+ cred->cr_groups[i] = exp->ex_anon_gid;
+ }
+
+ if (cred->cr_uid != (uid_t) -1)
+ current->fsuid = cred->cr_uid;
+ else
+ current->fsuid = exp->ex_anon_uid;
+ if (cred->cr_gid != (gid_t) -1)
+ current->fsgid = cred->cr_gid;
+ else
+ current->fsgid = exp->ex_anon_gid;
+ for (i = 0; i < NGROUPS; i++) {
+ gid_t group = cred->cr_groups[i];
+ if (group == (gid_t) NOGROUP)
+ break;
+ current->groups[i] = group;
+ }
+ current->ngroups = i;
+
+ if ((cred->cr_uid)) {
+ cap_t(current->cap_effective) &= ~CAP_NFSD_MASK;
+ } else {
+ cap_t(current->cap_effective) |= (CAP_NFSD_MASK &
+ current->cap_permitted);
+ }
+
+ rqstp->rq_userset = 1;
+}
diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c
new file mode 100644
index 00000000000000..c024bcb2cd8a05
--- /dev/null
+++ b/fs/nfsd/export.c
@@ -0,0 +1,966 @@
+#define MSNFS /* HACK HACK */
+/*
+ * linux/fs/nfsd/export.c
+ *
+ * NFS exporting and validation.
+ *
+ * We maintain a list of clients, each of which has a list of
+ * exports. To export an fs to a given client, you first have
+ * to create the client entry with NFSCTL_ADDCLIENT, which
+ * creates a client control block and adds it to the hash
+ * table. Then, you call NFSCTL_EXPORT for each fs.
+ *
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch, <okir@monad.swb.de>
+ */
+
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/in.h>
+#include <linux/seq_file.h>
+#include <linux/smp_lock.h>
+
+#include <linux/sunrpc/svc.h>
+#include <linux/nfsd/nfsd.h>
+#include <linux/nfsd/nfsfh.h>
+#include <linux/nfsd/syscall.h>
+#include <linux/lockd/bind.h>
+
+#define NFSDDBG_FACILITY NFSDDBG_EXPORT
+#define NFSD_PARANOIA 1
+
+typedef struct svc_client svc_client;
+typedef struct svc_export svc_export;
+
+static svc_export * exp_parent(svc_client *clp, kdev_t dev,
+ struct dentry *dentry);
+static svc_export * exp_child(svc_client *clp, kdev_t dev,
+ struct dentry *dentry);
+static void exp_unexport_all(svc_client *clp);
+static void exp_do_unexport(svc_export *unexp);
+static svc_client * exp_getclientbyname(char *name);
+static void exp_freeclient(svc_client *clp);
+static void exp_unhashclient(svc_client *clp);
+static int exp_verify_string(char *cp, int max);
+
+#define CLIENT_HASHBITS 6
+#define CLIENT_HASHMAX (1 << CLIENT_HASHBITS)
+#define CLIENT_HASHMASK (CLIENT_HASHMAX - 1)
+#define CLIENT_HASH(a) \
+ ((((a)>>24) ^ ((a)>>16) ^ ((a)>>8) ^(a)) & CLIENT_HASHMASK)
+/* XXX: is this adequate for 32bit kdev_t ? */
+#define EXPORT_HASH(dev) ((dev) & (NFSCLNT_EXPMAX - 1))
+#define EXPORT_FSID_HASH(fsid) ((fsid) & (NFSCLNT_EXPMAX - 1))
+
+struct svc_clnthash {
+ struct svc_clnthash * h_next;
+ struct in_addr h_addr;
+ struct svc_client * h_client;
+};
+static struct svc_clnthash * clnt_hash[CLIENT_HASHMAX];
+static svc_client * clients;
+
+static int hash_lock;
+static int want_lock;
+static int hash_count;
+static DECLARE_WAIT_QUEUE_HEAD( hash_wait );
+
+/*
+ * Find the client's export entry matching xdev/xino.
+ */
+svc_export *
+exp_get(svc_client *clp, kdev_t dev, ino_t ino)
+{
+ struct list_head *head, *p;
+
+ if (!clp)
+ return NULL;
+
+ head = &clp->cl_export[EXPORT_HASH(dev)];
+ list_for_each(p, head) {
+ svc_export *exp = list_entry(p, svc_export, ex_hash);
+ if (exp->ex_ino == ino && exp->ex_dev == dev)
+ return exp;
+ }
+
+ return NULL;
+}
+
+/*
+ * Find the client's export entry matching fsid
+ */
+svc_export *
+exp_get_fsid(svc_client *clp, int fsid)
+{
+ struct list_head *head, *p;
+
+ if (!clp)
+ return NULL;
+
+ head = &clp->cl_expfsid[EXPORT_FSID_HASH(fsid)];
+ list_for_each(p, head) {
+ svc_export *exp = list_entry(p, svc_export, ex_fsid_hash);
+ if (exp->ex_fsid == fsid)
+ return exp;
+ }
+ return NULL;
+}
+
+/*
+ * Find the export entry for a given dentry. <gam3@acm.org>
+ */
+static svc_export *
+exp_parent(svc_client *clp, kdev_t dev, struct dentry *dentry)
+{
+ struct list_head *head = &clp->cl_export[EXPORT_HASH(dev)];
+ struct list_head *p;
+
+ list_for_each(p,head) {
+ svc_export *exp = list_entry(p, svc_export, ex_hash);
+ if (is_subdir(dentry, exp->ex_dentry))
+ return exp;
+ }
+ return NULL;
+}
+
+/*
+ * Find the child export entry for a given fs. This function is used
+ * only by the export syscall to keep the export tree consistent.
+ * <gam3@acm.org>
+ */
+static svc_export *
+exp_child(svc_client *clp, kdev_t dev, struct dentry *dentry)
+{
+ struct list_head *head = &clp->cl_export[EXPORT_HASH(dev)];
+ struct list_head *p;
+
+
+ list_for_each(p, head) {
+ svc_export *exp = list_entry(p, svc_export, ex_hash);
+ struct dentry *ndentry = exp->ex_dentry;
+
+ if (ndentry && is_subdir(ndentry->d_parent, dentry))
+ return exp;
+ }
+ return NULL;
+}
+
+/* Update parent pointers of all exports */
+static void exp_change_parents(svc_client *clp, svc_export *old, svc_export *new)
+{
+ struct list_head *head = &clp->cl_list;
+ struct list_head *p;
+
+ list_for_each(p, head) {
+ svc_export *exp = list_entry(p, svc_export, ex_list);
+ if (exp->ex_parent == old)
+ exp->ex_parent = new;
+ }
+}
+
+static void exp_fsid_unhash(struct svc_export *exp)
+{
+
+ if ((exp->ex_flags & NFSEXP_FSID) == 0)
+ return;
+
+ list_del_init(&exp->ex_fsid_hash);
+}
+
+static void exp_fsid_hash(struct svc_client *clp, struct svc_export *exp)
+{
+ struct list_head *head;
+
+ if ((exp->ex_flags & NFSEXP_FSID) == 0)
+ return;
+ head = clp->cl_expfsid + EXPORT_FSID_HASH(exp->ex_fsid);
+ list_add(&exp->ex_fsid_hash, head);
+}
+
+/*
+ * Export a file system.
+ */
+int
+exp_export(struct nfsctl_export *nxp)
+{
+ svc_client *clp;
+ svc_export *exp = NULL, *parent;
+ svc_export *fsid_exp;
+ struct nameidata nd;
+ struct inode *inode = NULL;
+ int err;
+ kdev_t dev;
+ ino_t ino;
+
+ /* Consistency check */
+ err = -EINVAL;
+ if (!exp_verify_string(nxp->ex_path, NFS_MAXPATHLEN) ||
+ !exp_verify_string(nxp->ex_client, NFSCLNT_IDMAX))
+ goto out;
+
+ dprintk("exp_export called for %s:%s (%x/%ld fl %x).\n",
+ nxp->ex_client, nxp->ex_path,
+ nxp->ex_dev, (long) nxp->ex_ino, nxp->ex_flags);
+
+ /* Try to lock the export table for update */
+ if ((err = exp_writelock()) < 0)
+ goto out;
+
+ /* Look up client info */
+ err = -EINVAL;
+ if (!(clp = exp_getclientbyname(nxp->ex_client)))
+ goto out_unlock;
+
+
+ /* Look up the dentry */
+ err = 0;
+ if (path_init(nxp->ex_path, LOOKUP_POSITIVE, &nd))
+ err = path_walk(nxp->ex_path, &nd);
+ if (err)
+ goto out_unlock;
+
+ inode = nd.dentry->d_inode;
+ dev = inode->i_dev;
+ ino = inode->i_ino;
+ err = -EINVAL;
+
+ exp = exp_get(clp, dev, ino);
+
+ /* must make sure there wont be an ex_fsid clash */
+ if ((nxp->ex_flags & NFSEXP_FSID) &&
+ (fsid_exp = exp_get_fsid(clp, nxp->ex_dev)) &&
+ fsid_exp != exp)
+ goto finish;
+
+ if (exp != NULL) {
+ /* just a flags/id/fsid update */
+
+ exp_fsid_unhash(exp);
+ exp->ex_flags = nxp->ex_flags;
+ exp->ex_anon_uid = nxp->ex_anon_uid;
+ exp->ex_anon_gid = nxp->ex_anon_gid;
+ exp->ex_fsid = nxp->ex_dev;
+ exp_fsid_hash(clp, exp);
+ err = 0;
+ goto finish;
+ }
+
+ /* We currently export only dirs and regular files.
+ * This is what umountd does.
+ */
+ err = -ENOTDIR;
+ if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode))
+ goto finish;
+
+ err = -EINVAL;
+ /* There are two requirements on a filesystem to be exportable.
+ * 1: We must be able to identify the filesystem from a number.
+ * either a device number (so FS_REQUIRES_DEV needed)
+ * or an FSID number (so NFSEXP_FSID needed).
+ * 2: We must be able to find an inode from a filehandle.
+ * either using fh_to_dentry (prefered)
+ * or using read_inode (the hack).
+ */
+ if (!((inode->i_sb->s_type->fs_flags & FS_REQUIRES_DEV)
+ || (nxp->ex_flags & NFSEXP_FSID))
+ ||
+ (inode->i_sb->s_op->read_inode == NULL
+ && inode->i_sb->s_op->fh_to_dentry == NULL)) {
+ dprintk("exp_export: export of invalid fs type.\n");
+ goto finish;
+ }
+
+ if ((parent = exp_child(clp, dev, nd.dentry)) != NULL) {
+ dprintk("exp_export: export not valid (Rule 3).\n");
+ goto finish;
+ }
+ /* Is this is a sub-export, must be a proper subset of FS */
+ if ((parent = exp_parent(clp, dev, nd.dentry)) != NULL) {
+ dprintk("exp_export: sub-export not valid (Rule 2).\n");
+ goto finish;
+ }
+
+ err = -ENOMEM;
+ if (!(exp = kmalloc(sizeof(*exp), GFP_USER)))
+ goto finish;
+ dprintk("nfsd: created export entry %p for client %p\n", exp, clp);
+
+ strcpy(exp->ex_path, nxp->ex_path);
+ exp->ex_client = clp;
+ exp->ex_parent = parent;
+ exp->ex_dentry = dget(nd.dentry);
+ exp->ex_mnt = mntget(nd.mnt);
+ exp->ex_flags = nxp->ex_flags;
+ exp->ex_dev = dev;
+ exp->ex_ino = ino;
+ exp->ex_anon_uid = nxp->ex_anon_uid;
+ exp->ex_anon_gid = nxp->ex_anon_gid;
+ exp->ex_fsid = nxp->ex_dev;
+
+
+ /* Update parent pointers of all exports */
+ if (parent)
+ exp_change_parents(clp, parent, exp);
+
+ list_add(&exp->ex_hash, clp->cl_export + EXPORT_HASH(dev));
+ list_add_tail(&exp->ex_list, &clp->cl_list);
+
+ exp_fsid_hash(clp, exp);
+
+ err = 0;
+
+finish:
+ path_release(&nd);
+out_unlock:
+ exp_unlock();
+out:
+ return err;
+}
+
+/*
+ * Unexport a file system. The export entry has already
+ * been removed from the client's list of exported fs's.
+ */
+static void
+exp_do_unexport(svc_export *unexp)
+{
+ struct dentry *dentry;
+ struct vfsmount *mnt;
+ struct inode *inode;
+
+ list_del(&unexp->ex_hash);
+ list_del(&unexp->ex_list);
+ exp_fsid_unhash(unexp);
+
+ exp_change_parents(unexp->ex_client, unexp, unexp->ex_parent);
+
+ dentry = unexp->ex_dentry;
+ mnt = unexp->ex_mnt;
+ inode = dentry->d_inode;
+ if (unexp->ex_dev != inode->i_dev || unexp->ex_ino != inode->i_ino)
+ printk(KERN_WARNING "nfsd: bad dentry in unexport!\n");
+ dput(dentry);
+ mntput(mnt);
+
+ kfree(unexp);
+}
+
+/*
+ * Revoke all exports for a given client.
+ * This may look very awkward, but we have to do it this way in order
+ * to avoid race conditions (aka mind the parent pointer).
+ */
+static void
+exp_unexport_all(svc_client *clp)
+{
+ struct list_head *p = &clp->cl_list;
+
+ dprintk("unexporting all fs's for clnt %p\n", clp);
+
+ while (!list_empty(p)) {
+ svc_export *exp = list_entry(p->next, svc_export, ex_list);
+ exp_do_unexport(exp);
+ }
+}
+
+/*
+ * unexport syscall.
+ */
+int
+exp_unexport(struct nfsctl_export *nxp)
+{
+ svc_client *clp;
+ int err;
+
+ /* Consistency check */
+ if (!exp_verify_string(nxp->ex_client, NFSCLNT_IDMAX))
+ return -EINVAL;
+
+ if ((err = exp_writelock()) < 0)
+ goto out;
+
+ err = -EINVAL;
+ clp = exp_getclientbyname(nxp->ex_client);
+ if (clp) {
+ svc_export *exp = exp_get(clp, nxp->ex_dev, nxp->ex_ino);
+ if (exp) {
+ exp_do_unexport(exp);
+ err = 0;
+ }
+ }
+
+ exp_unlock();
+out:
+ return err;
+}
+
+/*
+ * Obtain the root fh on behalf of a client.
+ * This could be done in user space, but I feel that it adds some safety
+ * since its harder to fool a kernel module than a user space program.
+ */
+int
+exp_rootfh(struct svc_client *clp, kdev_t dev, ino_t ino,
+ char *path, struct knfsd_fh *f, int maxsize)
+{
+ struct svc_export *exp;
+ struct nameidata nd;
+ struct inode *inode;
+ struct svc_fh fh;
+ int err;
+
+ err = -EPERM;
+ if (path) {
+ if (path_init(path, LOOKUP_POSITIVE, &nd) &&
+ path_walk(path, &nd)) {
+ printk("nfsd: exp_rootfh path not found %s", path);
+ return err;
+ }
+ dev = nd.dentry->d_inode->i_dev;
+ ino = nd.dentry->d_inode->i_ino;
+
+ dprintk("nfsd: exp_rootfh(%s [%p] %s:%x/%ld)\n",
+ path, nd.dentry, clp->cl_ident, dev, (long) ino);
+ exp = exp_parent(clp, dev, nd.dentry);
+ } else {
+ dprintk("nfsd: exp_rootfh(%s:%x/%ld)\n",
+ clp->cl_ident, dev, (long) ino);
+ if ((exp = exp_get(clp, dev, ino))) {
+ nd.mnt = mntget(exp->ex_mnt);
+ nd.dentry = dget(exp->ex_dentry);
+ }
+ }
+ if (!exp) {
+ dprintk("nfsd: exp_rootfh export not found.\n");
+ goto out;
+ }
+
+ inode = nd.dentry->d_inode;
+ if (!inode) {
+ printk("exp_rootfh: Aieee, NULL d_inode\n");
+ goto out;
+ }
+ if (inode->i_dev != dev || inode->i_ino != ino) {
+ printk("exp_rootfh: Aieee, ino/dev mismatch\n");
+ printk("exp_rootfh: arg[dev(%x):ino(%ld)]"
+ " inode[dev(%x):ino(%ld)]\n",
+ dev, (long) ino, inode->i_dev, (long) inode->i_ino);
+ }
+
+ /*
+ * fh must be initialized before calling fh_compose
+ */
+ fh_init(&fh, maxsize);
+ if (fh_compose(&fh, exp, dget(nd.dentry), NULL))
+ err = -EINVAL;
+ else
+ err = 0;
+ memcpy(f, &fh.fh_handle, sizeof(struct knfsd_fh));
+ fh_put(&fh);
+
+out:
+ if (path)
+ path_release(&nd);
+ return err;
+}
+
+/*
+ * Hashtable locking. Write locks are placed only by user processes
+ * wanting to modify export information.
+ */
+void
+exp_readlock(void)
+{
+ while (hash_lock || want_lock)
+ sleep_on(&hash_wait);
+ hash_count++;
+}
+
+int
+exp_writelock(void)
+{
+ /* fast track */
+ if (!hash_count && !hash_lock) {
+ lock_it:
+ hash_lock = 1;
+ return 0;
+ }
+
+ current->sigpending = 0;
+ want_lock++;
+ while (hash_count || hash_lock) {
+ interruptible_sleep_on(&hash_wait);
+ if (signal_pending(current))
+ break;
+ }
+ want_lock--;
+
+ /* restore the task's signals */
+ spin_lock_irq(&current->sigmask_lock);
+ recalc_sigpending(current);
+ spin_unlock_irq(&current->sigmask_lock);
+
+ if (!hash_count && !hash_lock)
+ goto lock_it;
+ return -EINTR;
+}
+
+void
+exp_unlock(void)
+{
+ if (!hash_count && !hash_lock)
+ printk(KERN_WARNING "exp_unlock: not locked!\n");
+ if (hash_count)
+ hash_count--;
+ else
+ hash_lock = 0;
+ wake_up(&hash_wait);
+}
+
+/*
+ * Find a valid client given an inet address. We always move the most
+ * recently used client to the front of the hash chain to speed up
+ * future lookups.
+ * Locking against other processes is the responsibility of the caller.
+ */
+struct svc_client *
+exp_getclient(struct sockaddr_in *sin)
+{
+ struct svc_clnthash **hp, **head, *tmp;
+ unsigned long addr = sin->sin_addr.s_addr;
+
+ head = &clnt_hash[CLIENT_HASH(addr)];
+
+ for (hp = head; (tmp = *hp) != NULL; hp = &(tmp->h_next)) {
+ if (tmp->h_addr.s_addr == addr) {
+ /* Move client to the front */
+ if (head != hp) {
+ *hp = tmp->h_next;
+ tmp->h_next = *head;
+ *head = tmp;
+ }
+
+ return tmp->h_client;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Find a client given its identifier.
+ */
+static svc_client *
+exp_getclientbyname(char *ident)
+{
+ svc_client * clp;
+
+ for (clp = clients; clp; clp = clp->cl_next) {
+ if (!strcmp(clp->cl_ident, ident))
+ return clp;
+ }
+ return NULL;
+}
+
+/* Iterator */
+
+static void *e_start(struct seq_file *m, loff_t *pos)
+{
+ loff_t n = *pos;
+ unsigned client, export;
+ svc_client *clp;
+ struct list_head *p;
+
+ lock_kernel();
+ exp_readlock();
+ if (!n--)
+ return (void *)1;
+ client = n >> 32;
+ export = n & ((1LL<<32) - 1);
+ for (clp = clients; client && clp; clp = clp->cl_next, client--)
+ ;
+ if (!clp)
+ return NULL;
+ list_for_each(p, &clp->cl_list)
+ if (!export--)
+ return list_entry(p, svc_export, ex_list);
+ n &= ~((1LL<<32) - 1);
+ do {
+ clp = clp->cl_next;
+ n += 1LL<<32;
+ } while(clp && list_empty(&clp->cl_list));
+ if (!clp)
+ return NULL;
+ *pos = n+1;
+ return list_entry(clp->cl_list.next, svc_export, ex_list);
+}
+
+static void *e_next(struct seq_file *m, void *p, loff_t *pos)
+{
+ svc_export *exp = p;
+ svc_client *clp;
+
+ if (p == (void *)1)
+ clp = clients;
+ else if (exp->ex_list.next == &exp->ex_client->cl_list) {
+ clp = exp->ex_client->cl_next;
+ *pos += 1LL<<32;
+ } else {
+ ++*pos;
+ return list_entry(exp->ex_list.next, svc_export, ex_list);
+ }
+ *pos &= ~((1LL<<32) - 1);
+ while (clp && list_empty(&clp->cl_list)) {
+ clp = clp->cl_next;
+ *pos += 1LL<<32;
+ }
+ if (!clp)
+ return NULL;
+ ++*pos;
+ return list_entry(clp->cl_list.next, svc_export, ex_list);
+}
+
+static void e_stop(struct seq_file *m, void *p)
+{
+ exp_unlock();
+ unlock_kernel();
+}
+
+struct flags {
+ int flag;
+ char *name[2];
+} expflags[] = {
+ { NFSEXP_READONLY, {"ro", "rw"}},
+ { NFSEXP_INSECURE_PORT, {"insecure", ""}},
+ { NFSEXP_ROOTSQUASH, {"root_squash", "no_root_squash"}},
+ { NFSEXP_ALLSQUASH, {"all_squash", ""}},
+ { NFSEXP_ASYNC, {"async", "sync"}},
+ { NFSEXP_GATHERED_WRITES, {"wdelay", "no_wdelay"}},
+ { NFSEXP_UIDMAP, {"uidmap", ""}},
+ { NFSEXP_KERBEROS, { "kerberos", ""}},
+ { NFSEXP_SUNSECURE, { "sunsecure", ""}},
+ { NFSEXP_NOHIDE, {"nohide", ""}},
+ { NFSEXP_NOSUBTREECHECK, {"no_subtree_check", ""}},
+ { NFSEXP_NOAUTHNLM, {"insecure_locks", ""}},
+#ifdef MSNFS
+ { NFSEXP_MSNFS, {"msnfs", ""}},
+#endif
+ { 0, {"", ""}}
+};
+
+static void exp_flags(struct seq_file *m, int flag, int fsid)
+{
+ int first = 0;
+ struct flags *flg;
+
+ for (flg = expflags; flg->flag; flg++) {
+ int state = (flg->flag & flag)?0:1;
+ if (*flg->name[state])
+ seq_printf(m, "%s%s", first++?",":"", flg->name[state]);
+ }
+ if (flag & NFSEXP_FSID)
+ seq_printf(m, "%sfsid=%d", first++?",":"", fsid);
+}
+
+static inline void mangle(struct seq_file *m, const char *s)
+{
+ seq_escape(m, s, " \t\n\\");
+}
+
+static int e_show(struct seq_file *m, void *p)
+{
+ struct svc_export *exp = p;
+ struct svc_client *clp;
+ int j, first = 0;
+
+ if (p == (void *)1) {
+ seq_puts(m, "# Version 1.1\n");
+ seq_puts(m, "# Path Client(Flags) # IPs\n");
+ return 0;
+ }
+
+ clp = exp->ex_client;
+
+ mangle(m, exp->ex_path);
+ seq_putc(m, '\t');
+ mangle(m, clp->cl_ident);
+ seq_putc(m, '(');
+ exp_flags(m, exp->ex_flags, exp->ex_fsid);
+ seq_puts(m, ") # ");
+ for (j = 0; j < clp->cl_naddr; j++) {
+ struct svc_clnthash **hp, **head, *tmp;
+ struct in_addr addr = clp->cl_addr[j];
+
+ head = &clnt_hash[CLIENT_HASH(addr.s_addr)];
+ for (hp = head; (tmp = *hp) != NULL; hp = &(tmp->h_next)) {
+ if (tmp->h_addr.s_addr == addr.s_addr)
+ break;
+ }
+ if (tmp) {
+ if (first++)
+ seq_putc(m, ' ');
+ if (tmp->h_client != clp)
+ seq_putc(m, '(');
+ seq_printf(m, "%d.%d.%d.%d",
+ htonl(addr.s_addr) >> 24 & 0xff,
+ htonl(addr.s_addr) >> 16 & 0xff,
+ htonl(addr.s_addr) >> 8 & 0xff,
+ htonl(addr.s_addr) >> 0 & 0xff);
+ if (tmp->h_client != clp)
+ seq_putc(m, ')');
+ }
+ }
+ seq_putc(m, '\n');
+ return 0;
+}
+
+struct seq_operations nfs_exports_op = {
+ start: e_start,
+ next: e_next,
+ stop: e_stop,
+ show: e_show,
+};
+
+/*
+ * Add or modify a client.
+ * Change requests may involve the list of host addresses. The list of
+ * exports and possibly existing uid maps are left untouched.
+ */
+int
+exp_addclient(struct nfsctl_client *ncp)
+{
+ struct svc_clnthash * ch[NFSCLNT_ADDRMAX];
+ svc_client * clp;
+ int i, err, change = 0, ilen;
+
+ /* First, consistency check. */
+ err = -EINVAL;
+ if (!(ilen = exp_verify_string(ncp->cl_ident, NFSCLNT_IDMAX)))
+ goto out;
+ if (ncp->cl_naddr > NFSCLNT_ADDRMAX)
+ goto out;
+
+ /* Lock the hashtable */
+ if ((err = exp_writelock()) < 0)
+ goto out;
+
+ /* First check if this is a change request for a client. */
+ for (clp = clients; clp; clp = clp->cl_next)
+ if (!strcmp(clp->cl_ident, ncp->cl_ident))
+ break;
+
+ err = -ENOMEM;
+ if (clp) {
+ change = 1;
+ } else {
+ if (!(clp = kmalloc(sizeof(*clp), GFP_KERNEL)))
+ goto out_unlock;
+ memset(clp, 0, sizeof(*clp));
+ for (i = 0; i < NFSCLNT_EXPMAX; i++) {
+ INIT_LIST_HEAD(&clp->cl_export[i]);
+ INIT_LIST_HEAD(&clp->cl_expfsid[i]);
+ }
+ INIT_LIST_HEAD(&clp->cl_list);
+
+ dprintk("created client %s (%p)\n", ncp->cl_ident, clp);
+
+ strcpy(clp->cl_ident, ncp->cl_ident);
+ clp->cl_idlen = ilen;
+ }
+
+ /* Allocate hash buckets */
+ for (i = 0; i < ncp->cl_naddr; i++) {
+ ch[i] = kmalloc(sizeof(struct svc_clnthash), GFP_KERNEL);
+ if (!ch[i]) {
+ while (i--)
+ kfree(ch[i]);
+ if (!change)
+ kfree(clp);
+ goto out_unlock;
+ }
+ }
+
+ /* Copy addresses. */
+ for (i = 0; i < ncp->cl_naddr; i++) {
+ clp->cl_addr[i] = ncp->cl_addrlist[i];
+ }
+ clp->cl_naddr = ncp->cl_naddr;
+
+ /* Remove old client hash entries. */
+ if (change)
+ exp_unhashclient(clp);
+
+ /* Insert client into hashtable. */
+ for (i = 0; i < ncp->cl_naddr; i++) {
+ struct in_addr addr = clp->cl_addr[i];
+ int hash;
+
+ hash = CLIENT_HASH(addr.s_addr);
+ ch[i]->h_client = clp;
+ ch[i]->h_addr = addr;
+ ch[i]->h_next = clnt_hash[hash];
+ clnt_hash[hash] = ch[i];
+ }
+
+ if (!change) {
+ clp->cl_next = clients;
+ clients = clp;
+ }
+ err = 0;
+
+out_unlock:
+ exp_unlock();
+out:
+ return err;
+}
+
+/*
+ * Delete a client given an identifier.
+ */
+int
+exp_delclient(struct nfsctl_client *ncp)
+{
+ svc_client **clpp, *clp;
+ int err;
+
+ err = -EINVAL;
+ if (!exp_verify_string(ncp->cl_ident, NFSCLNT_IDMAX))
+ goto out;
+
+ /* Lock the hashtable */
+ if ((err = exp_writelock()) < 0)
+ goto out;
+
+ err = -EINVAL;
+ for (clpp = &clients; (clp = *clpp); clpp = &(clp->cl_next))
+ if (!strcmp(ncp->cl_ident, clp->cl_ident))
+ break;
+
+ if (clp) {
+ *clpp = clp->cl_next;
+ exp_freeclient(clp);
+ err = 0;
+ }
+
+ exp_unlock();
+out:
+ return err;
+}
+
+/*
+ * Free a client. The caller has already removed it from the client list.
+ */
+static void
+exp_freeclient(svc_client *clp)
+{
+ exp_unhashclient(clp);
+
+ /* umap_free(&(clp->cl_umap)); */
+ exp_unexport_all(clp);
+ nfsd_lockd_unexport(clp);
+ kfree (clp);
+}
+
+/*
+ * Remove client from hashtable. We first collect all hashtable
+ * entries and free them in one go.
+ * The hash table must be writelocked by the caller.
+ */
+static void
+exp_unhashclient(svc_client *clp)
+{
+ struct svc_clnthash **hpp, *hp, *ch[NFSCLNT_ADDRMAX];
+ int i, count, err;
+
+again:
+ err = 0;
+ for (i = 0, count = 0; i < CLIENT_HASHMAX && !err; i++) {
+ hpp = clnt_hash + i;
+ while ((hp = *hpp) && !err) {
+ if (hp->h_client == clp) {
+ *hpp = hp->h_next;
+ ch[count++] = hp;
+ err = (count >= NFSCLNT_ADDRMAX);
+ } else {
+ hpp = &(hp->h_next);
+ }
+ }
+ }
+ if (count != clp->cl_naddr)
+ printk(KERN_WARNING "nfsd: bad address count in freeclient!\n");
+ if (err)
+ goto again;
+ for (i = 0; i < count; i++)
+ kfree (ch[i]);
+}
+
+/*
+ * Lockd is shutting down and tells us to unregister all clients
+ */
+void
+exp_nlmdetach(void)
+{
+ struct svc_client *clp;
+
+ exp_readlock();
+ for (clp = clients; clp; clp = clp->cl_next)
+ nfsd_lockd_unexport(clp);
+ exp_unlock();
+}
+
+/*
+ * Verify that string is non-empty and does not exceed max length.
+ */
+static int
+exp_verify_string(char *cp, int max)
+{
+ int i;
+
+ for (i = 0; i < max; i++)
+ if (!cp[i])
+ return i;
+ cp[i] = 0;
+ printk(KERN_NOTICE "nfsd: couldn't validate string %s\n", cp);
+ return 0;
+}
+
+/*
+ * Initialize the exports module.
+ */
+void
+nfsd_export_init(void)
+{
+ int i;
+
+ dprintk("nfsd: initializing export module.\n");
+
+ for (i = 0; i < CLIENT_HASHMAX; i++)
+ clnt_hash[i] = NULL;
+ clients = NULL;
+
+}
+
+/*
+ * Shutdown the exports module.
+ */
+void
+nfsd_export_shutdown(void)
+{
+ int i;
+
+ dprintk("nfsd: shutting down export module.\n");
+
+ if (exp_writelock() < 0) {
+ printk(KERN_WARNING "Weird: hashtable locked in exp_shutdown");
+ return;
+ }
+ for (i = 0; i < CLIENT_HASHMAX; i++) {
+ while (clnt_hash[i])
+ exp_freeclient(clnt_hash[i]->h_client);
+ }
+ clients = NULL; /* we may be restarted before the module unloads */
+
+ exp_unlock();
+ dprintk("nfsd: export shutdown complete.\n");
+}
diff --git a/fs/nfsd/lockd.c b/fs/nfsd/lockd.c
new file mode 100644
index 00000000000000..5bed6b8ae32080
--- /dev/null
+++ b/fs/nfsd/lockd.c
@@ -0,0 +1,93 @@
+/*
+ * linux/fs/nfsd/lockd.c
+ *
+ * This file contains all the stubs needed when communicating with lockd.
+ * This level of indirection is necessary so we can run nfsd+lockd without
+ * requiring the nfs client to be compiled in/loaded, and vice versa.
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/nfsd/nfsd.h>
+#include <linux/lockd/bind.h>
+
+#define NFSDDBG_FACILITY NFSDDBG_LOCKD
+
+/*
+ * Note: we hold the dentry use count while the file is open.
+ */
+static u32
+nlm_fopen(struct svc_rqst *rqstp, struct nfs_fh *f, struct file *filp)
+{
+ u32 nfserr;
+ struct svc_fh fh;
+
+ /* must initialize before using! but maxsize doesn't matter */
+ fh_init(&fh,0);
+ fh.fh_handle.fh_size = f->size;
+ memcpy((char*)&fh.fh_handle.fh_base, f->data, f->size);
+ fh.fh_export = NULL;
+
+ nfserr = nfsd_open(rqstp, &fh, S_IFREG, MAY_LOCK, filp);
+ if (!nfserr) {
+ dget(filp->f_dentry);
+ mntget(filp->f_vfsmnt);
+ }
+ fh_put(&fh);
+ /* nlm and nfsd don't share error codes.
+ * we invent: 0 = no error
+ * 1 = stale file handle
+ * 2 = other error
+ */
+ switch (nfserr) {
+ case nfs_ok:
+ return 0;
+ case nfserr_stale:
+ return 1;
+ default:
+ return 2;
+ }
+}
+
+static void
+nlm_fclose(struct file *filp)
+{
+ nfsd_close(filp);
+ dput(filp->f_dentry);
+ mntput(filp->f_vfsmnt);
+}
+
+struct nlmsvc_binding nfsd_nlm_ops = {
+ exp_readlock, /* lock export table for reading */
+ exp_unlock, /* unlock export table */
+ exp_getclient, /* look up NFS client */
+ nlm_fopen, /* open file for locking */
+ nlm_fclose, /* close file */
+ exp_nlmdetach, /* lockd shutdown notification */
+};
+
+/*
+ * When removing an NFS client entry, notify lockd that it is gone.
+ * FIXME: We should do the same when unexporting an NFS volume.
+ */
+void
+nfsd_lockd_unexport(struct svc_client *clnt)
+{
+ nlmsvc_invalidate_client(clnt);
+}
+
+void
+nfsd_lockd_init(void)
+{
+ dprintk("nfsd: initializing lockd\n");
+ nlmsvc_ops = &nfsd_nlm_ops;
+}
+
+void
+nfsd_lockd_shutdown(void)
+{
+ nlmsvc_ops = NULL;
+}
diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c
new file mode 100644
index 00000000000000..5fc2f6480ef87f
--- /dev/null
+++ b/fs/nfsd/nfs3proc.c
@@ -0,0 +1,692 @@
+/*
+ * linux/fs/nfsd/nfs3proc.c
+ *
+ * Process version 3 NFS requests.
+ *
+ * Copyright (C) 1996, 1997, 1998 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/linkage.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <linux/fs.h>
+#include <linux/ext2_fs.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/version.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/major.h>
+
+#include <linux/sunrpc/svc.h>
+#include <linux/nfsd/nfsd.h>
+#include <linux/nfsd/cache.h>
+#include <linux/nfsd/xdr3.h>
+#include <linux/nfs3.h>
+
+#define NFSDDBG_FACILITY NFSDDBG_PROC
+
+#define RETURN_STATUS(st) { resp->status = (st); return (st); }
+
+static int nfs3_ftypes[] = {
+ 0, /* NF3NON */
+ S_IFREG, /* NF3REG */
+ S_IFDIR, /* NF3DIR */
+ S_IFBLK, /* NF3BLK */
+ S_IFCHR, /* NF3CHR */
+ S_IFLNK, /* NF3LNK */
+ S_IFSOCK, /* NF3SOCK */
+ S_IFIFO, /* NF3FIFO */
+};
+
+/*
+ * Reserve room in the send buffer
+ */
+static void
+svcbuf_reserve(struct svc_buf *buf, u32 **ptr, int *len, int nr)
+{
+ *ptr = buf->buf + nr;
+ *len = buf->buflen - buf->len - nr;
+}
+
+/*
+ * NULL call.
+ */
+static int
+nfsd3_proc_null(struct svc_rqst *rqstp, void *argp, void *resp)
+{
+ return nfs_ok;
+}
+
+/*
+ * Get a file's attributes
+ */
+static int
+nfsd3_proc_getattr(struct svc_rqst *rqstp, struct nfsd_fhandle *argp,
+ struct nfsd3_attrstat *resp)
+{
+ int nfserr;
+
+ dprintk("nfsd: GETATTR(3) %s\n",
+ SVCFH_fmt(&argp->fh));
+
+ fh_copy(&resp->fh, &argp->fh);
+ nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_NOP);
+ RETURN_STATUS(nfserr);
+}
+
+/*
+ * Set a file's attributes
+ */
+static int
+nfsd3_proc_setattr(struct svc_rqst *rqstp, struct nfsd3_sattrargs *argp,
+ struct nfsd3_attrstat *resp)
+{
+ int nfserr;
+
+ dprintk("nfsd: SETATTR(3) %s\n",
+ SVCFH_fmt(&argp->fh));
+
+ fh_copy(&resp->fh, &argp->fh);
+ nfserr = nfsd_setattr(rqstp, &resp->fh, &argp->attrs,
+ argp->check_guard, argp->guardtime);
+ RETURN_STATUS(nfserr);
+}
+
+/*
+ * Look up a path name component
+ */
+static int
+nfsd3_proc_lookup(struct svc_rqst *rqstp, struct nfsd3_diropargs *argp,
+ struct nfsd3_diropres *resp)
+{
+ int nfserr;
+
+ dprintk("nfsd: LOOKUP(3) %s %.*s\n",
+ SVCFH_fmt(&argp->fh),
+ argp->len,
+ argp->name);
+
+ fh_copy(&resp->dirfh, &argp->fh);
+ fh_init(&resp->fh, NFS3_FHSIZE);
+
+ nfserr = nfsd_lookup(rqstp, &resp->dirfh,
+ argp->name,
+ argp->len,
+ &resp->fh);
+ RETURN_STATUS(nfserr);
+}
+
+/*
+ * Check file access
+ */
+static int
+nfsd3_proc_access(struct svc_rqst *rqstp, struct nfsd3_accessargs *argp,
+ struct nfsd3_accessres *resp)
+{
+ int nfserr;
+
+ dprintk("nfsd: ACCESS(3) %s 0x%x\n",
+ SVCFH_fmt(&argp->fh),
+ argp->access);
+
+ fh_copy(&resp->fh, &argp->fh);
+ resp->access = argp->access;
+ nfserr = nfsd_access(rqstp, &resp->fh, &resp->access);
+ RETURN_STATUS(nfserr);
+}
+
+/*
+ * Read a symlink.
+ */
+static int
+nfsd3_proc_readlink(struct svc_rqst *rqstp, struct nfsd_fhandle *argp,
+ struct nfsd3_readlinkres *resp)
+{
+ u32 *path;
+ int dummy, nfserr;
+
+ dprintk("nfsd: READLINK(3) %s\n", SVCFH_fmt(&argp->fh));
+
+ /* Reserve room for status, post_op_attr, and path length */
+ svcbuf_reserve(&rqstp->rq_resbuf, &path, &dummy,
+ 1 + NFS3_POST_OP_ATTR_WORDS + 1);
+
+ /* Read the symlink. */
+ fh_copy(&resp->fh, &argp->fh);
+ resp->len = NFS3_MAXPATHLEN;
+ nfserr = nfsd_readlink(rqstp, &resp->fh, (char *) path, &resp->len);
+ RETURN_STATUS(nfserr);
+}
+
+/*
+ * Read a portion of a file.
+ */
+static int
+nfsd3_proc_read(struct svc_rqst *rqstp, struct nfsd3_readargs *argp,
+ struct nfsd3_readres *resp)
+{
+ u32 * buffer;
+ int nfserr, avail;
+
+ dprintk("nfsd: READ(3) %s %lu bytes at %lu\n",
+ SVCFH_fmt(&argp->fh),
+ (unsigned long) argp->count,
+ (unsigned long) argp->offset);
+
+ /* Obtain buffer pointer for payload.
+ * 1 (status) + 22 (post_op_attr) + 1 (count) + 1 (eof)
+ * + 1 (xdr opaque byte count) = 26
+ */
+ svcbuf_reserve(&rqstp->rq_resbuf, &buffer, &avail,
+ 1 + NFS3_POST_OP_ATTR_WORDS + 3);
+
+ resp->count = argp->count;
+ if ((avail << 2) < resp->count)
+ resp->count = avail << 2;
+
+ svc_reserve(rqstp, ((1 + NFS3_POST_OP_ATTR_WORDS + 3)<<2) + argp->count +4);
+
+ fh_copy(&resp->fh, &argp->fh);
+ nfserr = nfsd_read(rqstp, &resp->fh,
+ argp->offset,
+ (char *) buffer,
+ &resp->count);
+ if (nfserr == 0) {
+ struct inode *inode = resp->fh.fh_dentry->d_inode;
+
+ resp->eof = (argp->offset + resp->count) >= inode->i_size;
+ }
+
+ RETURN_STATUS(nfserr);
+}
+
+/*
+ * Write data to a file
+ */
+static int
+nfsd3_proc_write(struct svc_rqst *rqstp, struct nfsd3_writeargs *argp,
+ struct nfsd3_writeres *resp)
+{
+ int nfserr;
+
+ dprintk("nfsd: WRITE(3) %s %d bytes at %ld%s\n",
+ SVCFH_fmt(&argp->fh),
+ argp->len,
+ (unsigned long) argp->offset,
+ argp->stable? " stable" : "");
+
+ fh_copy(&resp->fh, &argp->fh);
+ resp->committed = argp->stable;
+ nfserr = nfsd_write(rqstp, &resp->fh,
+ argp->offset,
+ argp->data,
+ argp->len,
+ &resp->committed);
+ resp->count = argp->count;
+ RETURN_STATUS(nfserr);
+}
+
+/*
+ * With NFSv3, CREATE processing is a lot easier than with NFSv2.
+ * At least in theory; we'll see how it fares in practice when the
+ * first reports about SunOS compatibility problems start to pour in...
+ */
+static int
+nfsd3_proc_create(struct svc_rqst *rqstp, struct nfsd3_createargs *argp,
+ struct nfsd3_diropres *resp)
+{
+ svc_fh *dirfhp, *newfhp = NULL;
+ struct iattr *attr;
+ u32 nfserr;
+
+ dprintk("nfsd: CREATE(3) %s %.*s\n",
+ SVCFH_fmt(&argp->fh),
+ argp->len,
+ argp->name);
+
+ dirfhp = fh_copy(&resp->dirfh, &argp->fh);
+ newfhp = fh_init(&resp->fh, NFS3_FHSIZE);
+ attr = &argp->attrs;
+
+ /* Get the directory inode */
+ nfserr = fh_verify(rqstp, dirfhp, S_IFDIR, MAY_CREATE);
+ if (nfserr)
+ RETURN_STATUS(nfserr);
+
+ /* Unfudge the mode bits */
+ attr->ia_mode &= ~S_IFMT;
+ if (!(attr->ia_valid & ATTR_MODE)) {
+ attr->ia_valid |= ATTR_MODE;
+ attr->ia_mode = S_IFREG;
+ } else {
+ attr->ia_mode = (attr->ia_mode & ~S_IFMT) | S_IFREG;
+ }
+
+ /* Now create the file and set attributes */
+ nfserr = nfsd_create_v3(rqstp, dirfhp, argp->name, argp->len,
+ attr, newfhp,
+ argp->createmode, argp->verf);
+
+ RETURN_STATUS(nfserr);
+}
+
+/*
+ * Make directory. This operation is not idempotent.
+ */
+static int
+nfsd3_proc_mkdir(struct svc_rqst *rqstp, struct nfsd3_createargs *argp,
+ struct nfsd3_diropres *resp)
+{
+ int nfserr;
+
+ dprintk("nfsd: MKDIR(3) %s %.*s\n",
+ SVCFH_fmt(&argp->fh),
+ argp->len,
+ argp->name);
+
+ argp->attrs.ia_valid &= ~ATTR_SIZE;
+ fh_copy(&resp->dirfh, &argp->fh);
+ fh_init(&resp->fh, NFS3_FHSIZE);
+ nfserr = nfsd_create(rqstp, &resp->dirfh, argp->name, argp->len,
+ &argp->attrs, S_IFDIR, 0, &resp->fh);
+
+ RETURN_STATUS(nfserr);
+}
+
+static int
+nfsd3_proc_symlink(struct svc_rqst *rqstp, struct nfsd3_symlinkargs *argp,
+ struct nfsd3_diropres *resp)
+{
+ int nfserr;
+
+ dprintk("nfsd: SYMLINK(3) %s %.*s -> %.*s\n",
+ SVCFH_fmt(&argp->ffh),
+ argp->flen, argp->fname,
+ argp->tlen, argp->tname);
+
+ fh_copy(&resp->dirfh, &argp->ffh);
+ fh_init(&resp->fh, NFS3_FHSIZE);
+ nfserr = nfsd_symlink(rqstp, &resp->dirfh, argp->fname, argp->flen,
+ argp->tname, argp->tlen,
+ &resp->fh, &argp->attrs);
+ RETURN_STATUS(nfserr);
+}
+
+/*
+ * Make socket/fifo/device.
+ */
+static int
+nfsd3_proc_mknod(struct svc_rqst *rqstp, struct nfsd3_mknodargs *argp,
+ struct nfsd3_diropres *resp)
+{
+ int nfserr, type;
+ dev_t rdev = 0;
+
+ dprintk("nfsd: MKNOD(3) %s %.*s\n",
+ SVCFH_fmt(&argp->fh),
+ argp->len,
+ argp->name);
+
+ fh_copy(&resp->dirfh, &argp->fh);
+ fh_init(&resp->fh, NFS3_FHSIZE);
+
+ if (argp->ftype == 0 || argp->ftype >= NF3BAD)
+ RETURN_STATUS(nfserr_inval);
+ if (argp->ftype == NF3CHR || argp->ftype == NF3BLK) {
+ if ((argp->ftype == NF3CHR && argp->major >= MAX_CHRDEV)
+ || (argp->ftype == NF3BLK && argp->major >= MAX_BLKDEV)
+ || argp->minor > 0xFF)
+ RETURN_STATUS(nfserr_inval);
+ rdev = MKDEV(argp->major, argp->minor);
+ } else
+ if (argp->ftype != NF3SOCK && argp->ftype != NF3FIFO)
+ RETURN_STATUS(nfserr_inval);
+
+ type = nfs3_ftypes[argp->ftype];
+ nfserr = nfsd_create(rqstp, &resp->dirfh, argp->name, argp->len,
+ &argp->attrs, type, rdev, &resp->fh);
+
+ RETURN_STATUS(nfserr);
+}
+
+/*
+ * Remove file/fifo/socket etc.
+ */
+static int
+nfsd3_proc_remove(struct svc_rqst *rqstp, struct nfsd3_diropargs *argp,
+ struct nfsd3_attrstat *resp)
+{
+ int nfserr;
+
+ dprintk("nfsd: REMOVE(3) %s %.*s\n",
+ SVCFH_fmt(&argp->fh),
+ argp->len,
+ argp->name);
+
+ /* Unlink. -S_IFDIR means file must not be a directory */
+ fh_copy(&resp->fh, &argp->fh);
+ nfserr = nfsd_unlink(rqstp, &resp->fh, -S_IFDIR, argp->name, argp->len);
+ RETURN_STATUS(nfserr);
+}
+
+/*
+ * Remove a directory
+ */
+static int
+nfsd3_proc_rmdir(struct svc_rqst *rqstp, struct nfsd3_diropargs *argp,
+ struct nfsd3_attrstat *resp)
+{
+ int nfserr;
+
+ dprintk("nfsd: RMDIR(3) %s %.*s\n",
+ SVCFH_fmt(&argp->fh),
+ argp->len,
+ argp->name);
+
+ fh_copy(&resp->fh, &argp->fh);
+ nfserr = nfsd_unlink(rqstp, &resp->fh, S_IFDIR, argp->name, argp->len);
+ RETURN_STATUS(nfserr);
+}
+
+static int
+nfsd3_proc_rename(struct svc_rqst *rqstp, struct nfsd3_renameargs *argp,
+ struct nfsd3_renameres *resp)
+{
+ int nfserr;
+
+ dprintk("nfsd: RENAME(3) %s %.*s ->\n",
+ SVCFH_fmt(&argp->ffh),
+ argp->flen,
+ argp->fname);
+ dprintk("nfsd: -> %s %.*s\n",
+ SVCFH_fmt(&argp->tfh),
+ argp->tlen,
+ argp->tname);
+
+ fh_copy(&resp->ffh, &argp->ffh);
+ fh_copy(&resp->tfh, &argp->tfh);
+ nfserr = nfsd_rename(rqstp, &resp->ffh, argp->fname, argp->flen,
+ &resp->tfh, argp->tname, argp->tlen);
+ RETURN_STATUS(nfserr);
+}
+
+static int
+nfsd3_proc_link(struct svc_rqst *rqstp, struct nfsd3_linkargs *argp,
+ struct nfsd3_linkres *resp)
+{
+ int nfserr;
+
+ dprintk("nfsd: LINK(3) %s ->\n",
+ SVCFH_fmt(&argp->ffh));
+ dprintk("nfsd: -> %s %.*s\n",
+ SVCFH_fmt(&argp->tfh),
+ argp->tlen,
+ argp->tname);
+
+ fh_copy(&resp->fh, &argp->ffh);
+ fh_copy(&resp->tfh, &argp->tfh);
+ nfserr = nfsd_link(rqstp, &resp->tfh, argp->tname, argp->tlen,
+ &resp->fh);
+ RETURN_STATUS(nfserr);
+}
+
+/*
+ * Read a portion of a directory.
+ */
+static int
+nfsd3_proc_readdir(struct svc_rqst *rqstp, struct nfsd3_readdirargs *argp,
+ struct nfsd3_readdirres *resp)
+{
+ u32 * buffer;
+ int nfserr, count;
+ unsigned int want;
+
+ dprintk("nfsd: READDIR(3) %s %d bytes at %d\n",
+ SVCFH_fmt(&argp->fh),
+ argp->count, (u32) argp->cookie);
+
+ /* Reserve buffer space for status, attributes and verifier */
+ svcbuf_reserve(&rqstp->rq_resbuf, &buffer, &count,
+ 1 + NFS3_POST_OP_ATTR_WORDS + 2);
+
+ /* Make sure we've room for the NULL ptr & eof flag, and shrink to
+ * client read size */
+ if ((count -= 2) > (want = (argp->count >> 2) - 2))
+ count = want;
+
+ /* Read directory and encode entries on the fly */
+ fh_copy(&resp->fh, &argp->fh);
+ nfserr = nfsd_readdir(rqstp, &resp->fh, (loff_t) argp->cookie,
+ nfs3svc_encode_entry,
+ buffer, &count, argp->verf);
+ memcpy(resp->verf, argp->verf, 8);
+ resp->count = count;
+
+ RETURN_STATUS(nfserr);
+}
+
+/*
+ * Read a portion of a directory, including file handles and attrs.
+ * For now, we choose to ignore the dircount parameter.
+ */
+static int
+nfsd3_proc_readdirplus(struct svc_rqst *rqstp, struct nfsd3_readdirargs *argp,
+ struct nfsd3_readdirres *resp)
+{
+ u32 * buffer;
+ int nfserr, count, want;
+
+ dprintk("nfsd: READDIR+(3) %s %d bytes at %d\n",
+ SVCFH_fmt(&argp->fh),
+ argp->count, (u32) argp->cookie);
+
+ /* Reserve buffer space for status, attributes and verifier */
+ svcbuf_reserve(&rqstp->rq_resbuf, &buffer, &count,
+ 1 + NFS3_POST_OP_ATTR_WORDS + 2);
+
+ /* Make sure we've room for the NULL ptr & eof flag, and shrink to
+ * client read size */
+ if ((count -= 2) > (want = argp->count >> 2))
+ count = want;
+
+ /* Read directory and encode entries on the fly */
+ fh_copy(&resp->fh, &argp->fh);
+ nfserr = nfsd_readdir(rqstp, &resp->fh, (loff_t) argp->cookie,
+ nfs3svc_encode_entry_plus,
+ buffer, &count, argp->verf);
+ memcpy(resp->verf, argp->verf, 8);
+ resp->count = count;
+
+ RETURN_STATUS(nfserr);
+}
+
+/*
+ * Get file system stats
+ */
+static int
+nfsd3_proc_fsstat(struct svc_rqst * rqstp, struct nfsd_fhandle *argp,
+ struct nfsd3_fsstatres *resp)
+{
+ int nfserr;
+
+ dprintk("nfsd: FSSTAT(3) %s\n",
+ SVCFH_fmt(&argp->fh));
+
+ nfserr = nfsd_statfs(rqstp, &argp->fh, &resp->stats);
+ fh_put(&argp->fh);
+ RETURN_STATUS(nfserr);
+}
+
+/*
+ * Get file system info
+ */
+static int
+nfsd3_proc_fsinfo(struct svc_rqst * rqstp, struct nfsd_fhandle *argp,
+ struct nfsd3_fsinfores *resp)
+{
+ int nfserr;
+
+ dprintk("nfsd: FSINFO(3) %s\n",
+ SVCFH_fmt(&argp->fh));
+
+ resp->f_rtmax = NFSSVC_MAXBLKSIZE;
+ resp->f_rtpref = NFSSVC_MAXBLKSIZE;
+ resp->f_rtmult = PAGE_SIZE;
+ resp->f_wtmax = NFSSVC_MAXBLKSIZE;
+ resp->f_wtpref = NFSSVC_MAXBLKSIZE;
+ resp->f_wtmult = PAGE_SIZE;
+ resp->f_dtpref = PAGE_SIZE;
+ resp->f_maxfilesize = ~(u32) 0;
+ resp->f_properties = NFS3_FSF_DEFAULT;
+
+ nfserr = fh_verify(rqstp, &argp->fh, 0, MAY_NOP);
+
+ /* Check special features of the file system. May request
+ * different read/write sizes for file systems known to have
+ * problems with large blocks */
+ if (nfserr == 0) {
+ struct super_block *sb = argp->fh.fh_dentry->d_inode->i_sb;
+
+ /* Note that we don't care for remote fs's here */
+ if (sb->s_magic == 0x4d44 /* MSDOS_SUPER_MAGIC */) {
+ resp->f_properties = NFS3_FSF_BILLYBOY;
+ }
+ resp->f_maxfilesize = sb->s_maxbytes;
+ }
+
+ fh_put(&argp->fh);
+ RETURN_STATUS(nfserr);
+}
+
+/*
+ * Get pathconf info for the specified file
+ */
+static int
+nfsd3_proc_pathconf(struct svc_rqst * rqstp, struct nfsd_fhandle *argp,
+ struct nfsd3_pathconfres *resp)
+{
+ int nfserr;
+
+ dprintk("nfsd: PATHCONF(3) %s\n",
+ SVCFH_fmt(&argp->fh));
+
+ /* Set default pathconf */
+ resp->p_link_max = 255; /* at least */
+ resp->p_name_max = 255; /* at least */
+ resp->p_no_trunc = 0;
+ resp->p_chown_restricted = 1;
+ resp->p_case_insensitive = 0;
+ resp->p_case_preserving = 1;
+
+ nfserr = fh_verify(rqstp, &argp->fh, 0, MAY_NOP);
+
+ if (nfserr == 0) {
+ struct super_block *sb = argp->fh.fh_dentry->d_inode->i_sb;
+
+ /* Note that we don't care for remote fs's here */
+ switch (sb->s_magic) {
+ case EXT2_SUPER_MAGIC:
+ resp->p_link_max = EXT2_LINK_MAX;
+ resp->p_name_max = EXT2_NAME_LEN;
+ break;
+ case 0x4d44: /* MSDOS_SUPER_MAGIC */
+ resp->p_case_insensitive = 1;
+ resp->p_case_preserving = 0;
+ break;
+ }
+ }
+
+ fh_put(&argp->fh);
+ RETURN_STATUS(nfserr);
+}
+
+
+/*
+ * Commit a file (range) to stable storage.
+ */
+static int
+nfsd3_proc_commit(struct svc_rqst * rqstp, struct nfsd3_commitargs *argp,
+ struct nfsd3_commitres *resp)
+{
+ int nfserr;
+
+ dprintk("nfsd: COMMIT(3) %s %d@%ld\n",
+ SVCFH_fmt(&argp->fh),
+ argp->count,
+ (unsigned long) argp->offset);
+
+ if (argp->offset > NFS_OFFSET_MAX)
+ RETURN_STATUS(nfserr_inval);
+
+ fh_copy(&resp->fh, &argp->fh);
+ nfserr = nfsd_commit(rqstp, &resp->fh, argp->offset, argp->count);
+
+ RETURN_STATUS(nfserr);
+}
+
+
+/*
+ * NFSv3 Server procedures.
+ * Only the results of non-idempotent operations are cached.
+ */
+#define nfs3svc_decode_voidargs NULL
+#define nfs3svc_release_void NULL
+#define nfs3svc_decode_fhandleargs nfs3svc_decode_fhandle
+#define nfs3svc_encode_attrstatres nfs3svc_encode_attrstat
+#define nfs3svc_encode_wccstatres nfs3svc_encode_wccstat
+#define nfsd3_mkdirargs nfsd3_createargs
+#define nfsd3_readdirplusargs nfsd3_readdirargs
+#define nfsd3_fhandleargs nfsd_fhandle
+#define nfsd3_fhandleres nfsd3_attrstat
+#define nfsd3_attrstatres nfsd3_attrstat
+#define nfsd3_wccstatres nfsd3_attrstat
+#define nfsd3_createres nfsd3_diropres
+#define nfsd3_voidres nfsd3_voidargs
+struct nfsd3_voidargs { int dummy; };
+
+#define PROC(name, argt, rest, relt, cache, respsize) \
+ { (svc_procfunc) nfsd3_proc_##name, \
+ (kxdrproc_t) nfs3svc_decode_##argt##args, \
+ (kxdrproc_t) nfs3svc_encode_##rest##res, \
+ (kxdrproc_t) nfs3svc_release_##relt, \
+ sizeof(struct nfsd3_##argt##args), \
+ sizeof(struct nfsd3_##rest##res), \
+ 0, \
+ cache, \
+ respsize, \
+ }
+
+#define ST 1 /* status*/
+#define FH 17 /* filehandle with length */
+#define AT 21 /* attributes */
+#define pAT (1+AT) /* post attributes - conditional */
+#define WC (7+pAT) /* WCC attributes */
+
+struct svc_procedure nfsd_procedures3[22] = {
+ PROC(null, void, void, void, RC_NOCACHE, ST),
+ PROC(getattr, fhandle, attrstat, fhandle, RC_NOCACHE, ST+AT),
+ PROC(setattr, sattr, wccstat, fhandle, RC_REPLBUFF, ST+WC),
+ PROC(lookup, dirop, dirop, fhandle2, RC_NOCACHE, ST+FH+pAT+pAT),
+ PROC(access, access, access, fhandle, RC_NOCACHE, ST+pAT+1),
+ PROC(readlink, fhandle, readlink, fhandle, RC_NOCACHE, ST+pAT+1+NFS3_MAXPATHLEN/4),
+ PROC(read, read, read, fhandle, RC_NOCACHE, ST+pAT+4+NFSSVC_MAXBLKSIZE),
+ PROC(write, write, write, fhandle, RC_REPLBUFF, ST+WC+4),
+ PROC(create, create, create, fhandle2, RC_REPLBUFF, ST+(1+FH+pAT)+WC),
+ PROC(mkdir, mkdir, create, fhandle2, RC_REPLBUFF, ST+(1+FH+pAT)+WC),
+ PROC(symlink, symlink, create, fhandle2, RC_REPLBUFF, ST+(1+FH+pAT)+WC),
+ PROC(mknod, mknod, create, fhandle2, RC_REPLBUFF, ST+(1+FH+pAT)+WC),
+ PROC(remove, dirop, wccstat, fhandle, RC_REPLBUFF, ST+WC),
+ PROC(rmdir, dirop, wccstat, fhandle, RC_REPLBUFF, ST+WC),
+ PROC(rename, rename, rename, fhandle2, RC_REPLBUFF, ST+WC+WC),
+ PROC(link, link, link, fhandle2, RC_REPLBUFF, ST+pAT+WC),
+ PROC(readdir, readdir, readdir, fhandle, RC_NOCACHE, 0),
+ PROC(readdirplus,readdirplus, readdir, fhandle, RC_NOCACHE, 0),
+ PROC(fsstat, fhandle, fsstat, void, RC_NOCACHE, ST+pAT+2*6+1),
+ PROC(fsinfo, fhandle, fsinfo, void, RC_NOCACHE, ST+pAT+12),
+ PROC(pathconf, fhandle, pathconf, void, RC_NOCACHE, ST+pAT+6),
+ PROC(commit, commit, commit, fhandle, RC_NOCACHE, ST+WC+2),
+};
diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c
new file mode 100644
index 00000000000000..2348b1993f7c4f
--- /dev/null
+++ b/fs/nfsd/nfs3xdr.c
@@ -0,0 +1,851 @@
+/*
+ * linux/fs/nfsd/nfs3xdr.c
+ *
+ * XDR support for nfsd/protocol version 3.
+ *
+ * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/nfs3.h>
+
+#include <linux/sunrpc/xdr.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/nfsd/nfsd.h>
+#include <linux/nfsd/xdr3.h>
+
+#define NFSDDBG_FACILITY NFSDDBG_XDR
+
+#ifdef NFSD_OPTIMIZE_SPACE
+# define inline
+#endif
+
+
+/*
+ * Mapping of S_IF* types to NFS file types
+ */
+static u32 nfs3_ftypes[] = {
+ NF3NON, NF3FIFO, NF3CHR, NF3BAD,
+ NF3DIR, NF3BAD, NF3BLK, NF3BAD,
+ NF3REG, NF3BAD, NF3LNK, NF3BAD,
+ NF3SOCK, NF3BAD, NF3LNK, NF3BAD,
+};
+
+/*
+ * XDR functions for basic NFS types
+ */
+static inline u32 *
+encode_time3(u32 *p, time_t secs)
+{
+ *p++ = htonl((u32) secs); *p++ = 0;
+ return p;
+}
+
+static inline u32 *
+decode_time3(u32 *p, time_t *secp)
+{
+ *secp = ntohl(*p++);
+ return p + 1;
+}
+
+static inline u32 *
+decode_fh(u32 *p, struct svc_fh *fhp)
+{
+ unsigned int size;
+ fh_init(fhp, NFS3_FHSIZE);
+ size = ntohl(*p++);
+ if (size > NFS3_FHSIZE)
+ return NULL;
+
+ memcpy(&fhp->fh_handle.fh_base, p, size);
+ fhp->fh_handle.fh_size = size;
+ return p + XDR_QUADLEN(size);
+}
+
+static inline u32 *
+encode_fh(u32 *p, struct svc_fh *fhp)
+{
+ int size = fhp->fh_handle.fh_size;
+ *p++ = htonl(size);
+ if (size) p[XDR_QUADLEN(size)-1]=0;
+ memcpy(p, &fhp->fh_handle.fh_base, size);
+ return p + XDR_QUADLEN(size);
+}
+
+/*
+ * Decode a file name and make sure that the path contains
+ * no slashes or null bytes.
+ */
+static inline u32 *
+decode_filename(u32 *p, char **namp, int *lenp)
+{
+ char *name;
+ int i;
+
+ if ((p = xdr_decode_string_inplace(p, namp, lenp, NFS3_MAXNAMLEN)) != NULL) {
+ for (i = 0, name = *namp; i < *lenp; i++, name++) {
+ if (*name == '\0' || *name == '/')
+ return NULL;
+ }
+ }
+
+ return p;
+}
+
+static inline u32 *
+decode_pathname(u32 *p, char **namp, int *lenp)
+{
+ char *name;
+ int i;
+
+ if ((p = xdr_decode_string(p, namp, lenp, NFS3_MAXPATHLEN)) != NULL) {
+ for (i = 0, name = *namp; i < *lenp; i++, name++) {
+ if (*name == '\0')
+ return NULL;
+ }
+ }
+
+ return p;
+}
+
+static inline u32 *
+decode_sattr3(u32 *p, struct iattr *iap)
+{
+ u32 tmp;
+
+ iap->ia_valid = 0;
+
+ if (*p++) {
+ iap->ia_valid |= ATTR_MODE;
+ iap->ia_mode = ntohl(*p++);
+ }
+ if (*p++) {
+ iap->ia_valid |= ATTR_UID;
+ iap->ia_uid = ntohl(*p++);
+ }
+ if (*p++) {
+ iap->ia_valid |= ATTR_GID;
+ iap->ia_gid = ntohl(*p++);
+ }
+ if (*p++) {
+ u64 newsize;
+
+ iap->ia_valid |= ATTR_SIZE;
+ p = xdr_decode_hyper(p, &newsize);
+ if (newsize <= NFS_OFFSET_MAX)
+ iap->ia_size = newsize;
+ else
+ iap->ia_size = NFS_OFFSET_MAX;
+ }
+ if ((tmp = ntohl(*p++)) == 1) { /* set to server time */
+ iap->ia_valid |= ATTR_ATIME;
+ } else if (tmp == 2) { /* set to client time */
+ iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET;
+ iap->ia_atime = ntohl(*p++), p++;
+ }
+ if ((tmp = ntohl(*p++)) == 1) { /* set to server time */
+ iap->ia_valid |= ATTR_MTIME;
+ } else if (tmp == 2) { /* set to client time */
+ iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET;
+ iap->ia_mtime = ntohl(*p++), p++;
+ }
+ return p;
+}
+
+static inline u32 *
+encode_fattr3(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
+{
+ struct inode *inode = fhp->fh_dentry->d_inode;
+
+ *p++ = htonl(nfs3_ftypes[(inode->i_mode & S_IFMT) >> 12]);
+ *p++ = htonl((u32) inode->i_mode);
+ *p++ = htonl((u32) inode->i_nlink);
+ *p++ = htonl((u32) nfsd_ruid(rqstp, inode->i_uid));
+ *p++ = htonl((u32) nfsd_rgid(rqstp, inode->i_gid));
+ if (S_ISLNK(inode->i_mode) && inode->i_size > NFS3_MAXPATHLEN) {
+ p = xdr_encode_hyper(p, (u64) NFS3_MAXPATHLEN);
+ } else {
+ p = xdr_encode_hyper(p, (u64) inode->i_size);
+ }
+ if (inode->i_blksize == 0 && inode->i_blocks == 0)
+ /* Minix file system(?) i_size is (hopefully) close enough */
+ p = xdr_encode_hyper(p, (u64)(inode->i_size +511)& ~511);
+ else
+ p = xdr_encode_hyper(p, ((u64)inode->i_blocks) << 9);
+ *p++ = htonl((u32) MAJOR(inode->i_rdev));
+ *p++ = htonl((u32) MINOR(inode->i_rdev));
+ if (rqstp->rq_reffh->fh_version == 1
+ && rqstp->rq_reffh->fh_fsid_type == 1
+ && (fhp->fh_export->ex_flags & NFSEXP_FSID))
+ p = xdr_encode_hyper(p, (u64) fhp->fh_export->ex_fsid);
+ else
+ p = xdr_encode_hyper(p, (u64) inode->i_dev);
+ p = xdr_encode_hyper(p, (u64) inode->i_ino);
+ p = encode_time3(p, inode->i_atime);
+ p = encode_time3(p, lease_get_mtime(inode));
+ p = encode_time3(p, inode->i_ctime);
+
+ return p;
+}
+
+static inline u32 *
+encode_saved_post_attr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
+{
+ struct inode *inode = fhp->fh_dentry->d_inode;
+
+ /* Attributes to follow */
+ *p++ = xdr_one;
+
+ *p++ = htonl(nfs3_ftypes[(fhp->fh_post_mode & S_IFMT) >> 12]);
+ *p++ = htonl((u32) fhp->fh_post_mode);
+ *p++ = htonl((u32) fhp->fh_post_nlink);
+ *p++ = htonl((u32) nfsd_ruid(rqstp, fhp->fh_post_uid));
+ *p++ = htonl((u32) nfsd_rgid(rqstp, fhp->fh_post_gid));
+ if (S_ISLNK(fhp->fh_post_mode) && fhp->fh_post_size > NFS3_MAXPATHLEN) {
+ p = xdr_encode_hyper(p, (u64) NFS3_MAXPATHLEN);
+ } else {
+ p = xdr_encode_hyper(p, (u64) fhp->fh_post_size);
+ }
+ p = xdr_encode_hyper(p, ((u64)fhp->fh_post_blocks) << 9);
+ *p++ = htonl((u32) MAJOR(fhp->fh_post_rdev));
+ *p++ = htonl((u32) MINOR(fhp->fh_post_rdev));
+ if (rqstp->rq_reffh->fh_version == 1
+ && rqstp->rq_reffh->fh_fsid_type == 1
+ && (fhp->fh_export->ex_flags & NFSEXP_FSID))
+ p = xdr_encode_hyper(p, (u64) fhp->fh_export->ex_fsid);
+ else
+ p = xdr_encode_hyper(p, (u64) inode->i_dev);
+ p = xdr_encode_hyper(p, (u64) inode->i_ino);
+ p = encode_time3(p, fhp->fh_post_atime);
+ p = encode_time3(p, fhp->fh_post_mtime);
+ p = encode_time3(p, fhp->fh_post_ctime);
+
+ return p;
+}
+
+/*
+ * Encode post-operation attributes.
+ * The inode may be NULL if the call failed because of a stale file
+ * handle. In this case, no attributes are returned.
+ */
+static u32 *
+encode_post_op_attr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
+{
+ struct dentry *dentry = fhp->fh_dentry;
+ if (dentry && dentry->d_inode != NULL) {
+ *p++ = xdr_one; /* attributes follow */
+ return encode_fattr3(rqstp, p, fhp);
+ }
+ *p++ = xdr_zero;
+ return p;
+}
+
+/*
+ * Enocde weak cache consistency data
+ */
+static u32 *
+encode_wcc_data(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
+{
+ struct dentry *dentry = fhp->fh_dentry;
+
+ if (dentry && dentry->d_inode && fhp->fh_post_saved) {
+ if (fhp->fh_pre_saved) {
+ *p++ = xdr_one;
+ p = xdr_encode_hyper(p, (u64) fhp->fh_pre_size);
+ p = encode_time3(p, fhp->fh_pre_mtime);
+ p = encode_time3(p, fhp->fh_pre_ctime);
+ } else {
+ *p++ = xdr_zero;
+ }
+ return encode_saved_post_attr(rqstp, p, fhp);
+ }
+ /* no pre- or post-attrs */
+ *p++ = xdr_zero;
+ return encode_post_op_attr(rqstp, p, fhp);
+}
+
+/*
+ * Check buffer bounds after decoding arguments
+ */
+static inline int
+xdr_argsize_check(struct svc_rqst *rqstp, u32 *p)
+{
+ struct svc_buf *buf = &rqstp->rq_argbuf;
+
+ return p >= buf->base && p <= buf->base + buf->buflen ;
+}
+
+static inline int
+xdr_ressize_check(struct svc_rqst *rqstp, u32 *p)
+{
+ struct svc_buf *buf = &rqstp->rq_resbuf;
+
+ buf->len = p - buf->base;
+ dprintk("nfsd: ressize_check p %p base %p len %d\n",
+ p, buf->base, buf->buflen);
+ return (buf->len <= buf->buflen);
+}
+
+/*
+ * XDR decode functions
+ */
+int
+nfs3svc_decode_fhandle(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
+{
+ if (!(p = decode_fh(p, fhp)))
+ return 0;
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfs3svc_decode_sattrargs(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_sattrargs *args)
+{
+ if (!(p = decode_fh(p, &args->fh))
+ || !(p = decode_sattr3(p, &args->attrs)))
+ return 0;
+
+ if ((args->check_guard = ntohl(*p++)) != 0)
+ p = decode_time3(p, &args->guardtime);
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfs3svc_decode_diropargs(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_diropargs *args)
+{
+ if (!(p = decode_fh(p, &args->fh))
+ || !(p = decode_filename(p, &args->name, &args->len)))
+ return 0;
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfs3svc_decode_accessargs(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_accessargs *args)
+{
+ if (!(p = decode_fh(p, &args->fh)))
+ return 0;
+ args->access = ntohl(*p++);
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfs3svc_decode_readargs(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_readargs *args)
+{
+ if (!(p = decode_fh(p, &args->fh))
+ || !(p = xdr_decode_hyper(p, &args->offset)))
+ return 0;
+
+ args->count = ntohl(*p++);
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfs3svc_decode_writeargs(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_writeargs *args)
+{
+ if (!(p = decode_fh(p, &args->fh))
+ || !(p = xdr_decode_hyper(p, &args->offset)))
+ return 0;
+
+ args->count = ntohl(*p++);
+ args->stable = ntohl(*p++);
+ args->len = ntohl(*p++);
+ args->data = (char *) p;
+ p += XDR_QUADLEN(args->len);
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfs3svc_decode_createargs(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_createargs *args)
+{
+ if (!(p = decode_fh(p, &args->fh))
+ || !(p = decode_filename(p, &args->name, &args->len)))
+ return 0;
+
+ switch (args->createmode = ntohl(*p++)) {
+ case NFS3_CREATE_UNCHECKED:
+ case NFS3_CREATE_GUARDED:
+ if (!(p = decode_sattr3(p, &args->attrs)))
+ return 0;
+ break;
+ case NFS3_CREATE_EXCLUSIVE:
+ args->verf = p;
+ p += 2;
+ break;
+ default:
+ return 0;
+ }
+
+ return xdr_argsize_check(rqstp, p);
+}
+int
+nfs3svc_decode_mkdirargs(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_createargs *args)
+{
+ if (!(p = decode_fh(p, &args->fh))
+ || !(p = decode_filename(p, &args->name, &args->len))
+ || !(p = decode_sattr3(p, &args->attrs)))
+ return 0;
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfs3svc_decode_symlinkargs(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_symlinkargs *args)
+{
+ if (!(p = decode_fh(p, &args->ffh))
+ || !(p = decode_filename(p, &args->fname, &args->flen))
+ || !(p = decode_sattr3(p, &args->attrs))
+ || !(p = decode_pathname(p, &args->tname, &args->tlen)))
+ return 0;
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfs3svc_decode_mknodargs(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_mknodargs *args)
+{
+ if (!(p = decode_fh(p, &args->fh))
+ || !(p = decode_filename(p, &args->name, &args->len)))
+ return 0;
+
+ args->ftype = ntohl(*p++);
+
+ if (args->ftype == NF3BLK || args->ftype == NF3CHR
+ || args->ftype == NF3SOCK || args->ftype == NF3FIFO) {
+ if (!(p = decode_sattr3(p, &args->attrs)))
+ return 0;
+ }
+
+ if (args->ftype == NF3BLK || args->ftype == NF3CHR) {
+ args->major = ntohl(*p++);
+ args->minor = ntohl(*p++);
+ }
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfs3svc_decode_renameargs(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_renameargs *args)
+{
+ if (!(p = decode_fh(p, &args->ffh))
+ || !(p = decode_filename(p, &args->fname, &args->flen))
+ || !(p = decode_fh(p, &args->tfh))
+ || !(p = decode_filename(p, &args->tname, &args->tlen)))
+ return 0;
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfs3svc_decode_linkargs(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_linkargs *args)
+{
+ if (!(p = decode_fh(p, &args->ffh))
+ || !(p = decode_fh(p, &args->tfh))
+ || !(p = decode_filename(p, &args->tname, &args->tlen)))
+ return 0;
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfs3svc_decode_readdirargs(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_readdirargs *args)
+{
+ if (!(p = decode_fh(p, &args->fh)))
+ return 0;
+ p = xdr_decode_hyper(p, &args->cookie);
+ args->verf = p; p += 2;
+ args->dircount = ~0;
+ args->count = ntohl(*p++);
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_readdirargs *args)
+{
+ if (!(p = decode_fh(p, &args->fh)))
+ return 0;
+ p = xdr_decode_hyper(p, &args->cookie);
+ args->verf = p; p += 2;
+ args->dircount = ntohl(*p++);
+ args->count = ntohl(*p++);
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfs3svc_decode_commitargs(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_commitargs *args)
+{
+ if (!(p = decode_fh(p, &args->fh)))
+ return 0;
+ p = xdr_decode_hyper(p, &args->offset);
+ args->count = ntohl(*p++);
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+/*
+ * XDR encode functions
+ */
+/*
+ * There must be an encoding function for void results so svc_process
+ * will work properly.
+ */
+int
+nfs3svc_encode_voidres(struct svc_rqst *rqstp, u32 *p, void *dummy)
+{
+ return xdr_ressize_check(rqstp, p);
+}
+
+/* GETATTR */
+int
+nfs3svc_encode_attrstat(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_attrstat *resp)
+{
+ if (resp->status == 0)
+ p = encode_fattr3(rqstp, p, &resp->fh);
+ return xdr_ressize_check(rqstp, p);
+}
+
+/* SETATTR, REMOVE, RMDIR */
+int
+nfs3svc_encode_wccstat(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_attrstat *resp)
+{
+ p = encode_wcc_data(rqstp, p, &resp->fh);
+ return xdr_ressize_check(rqstp, p);
+}
+
+/* LOOKUP */
+int
+nfs3svc_encode_diropres(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_diropres *resp)
+{
+ if (resp->status == 0) {
+ p = encode_fh(p, &resp->fh);
+ p = encode_post_op_attr(rqstp, p, &resp->fh);
+ }
+ p = encode_post_op_attr(rqstp, p, &resp->dirfh);
+ return xdr_ressize_check(rqstp, p);
+}
+
+/* ACCESS */
+int
+nfs3svc_encode_accessres(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_accessres *resp)
+{
+ p = encode_post_op_attr(rqstp, p, &resp->fh);
+ if (resp->status == 0)
+ *p++ = htonl(resp->access);
+ return xdr_ressize_check(rqstp, p);
+}
+
+/* READLINK */
+int
+nfs3svc_encode_readlinkres(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_readlinkres *resp)
+{
+ p = encode_post_op_attr(rqstp, p, &resp->fh);
+ if (resp->status == 0) {
+ *p++ = htonl(resp->len);
+ p += XDR_QUADLEN(resp->len);
+ }
+ return xdr_ressize_check(rqstp, p);
+}
+
+/* READ */
+int
+nfs3svc_encode_readres(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_readres *resp)
+{
+ p = encode_post_op_attr(rqstp, p, &resp->fh);
+ if (resp->status == 0) {
+ *p++ = htonl(resp->count);
+ *p++ = htonl(resp->eof);
+ *p++ = htonl(resp->count); /* xdr opaque count */
+ p += XDR_QUADLEN(resp->count);
+ }
+ return xdr_ressize_check(rqstp, p);
+}
+
+/* WRITE */
+int
+nfs3svc_encode_writeres(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_writeres *resp)
+{
+ p = encode_wcc_data(rqstp, p, &resp->fh);
+ if (resp->status == 0) {
+ *p++ = htonl(resp->count);
+ *p++ = htonl(resp->committed);
+ *p++ = htonl(nfssvc_boot.tv_sec);
+ *p++ = htonl(nfssvc_boot.tv_usec);
+ }
+ return xdr_ressize_check(rqstp, p);
+}
+
+/* CREATE, MKDIR, SYMLINK, MKNOD */
+int
+nfs3svc_encode_createres(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_diropres *resp)
+{
+ if (resp->status == 0) {
+ *p++ = xdr_one;
+ p = encode_fh(p, &resp->fh);
+ p = encode_post_op_attr(rqstp, p, &resp->fh);
+ }
+ p = encode_wcc_data(rqstp, p, &resp->dirfh);
+ return xdr_ressize_check(rqstp, p);
+}
+
+/* RENAME */
+int
+nfs3svc_encode_renameres(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_renameres *resp)
+{
+ p = encode_wcc_data(rqstp, p, &resp->ffh);
+ p = encode_wcc_data(rqstp, p, &resp->tfh);
+ return xdr_ressize_check(rqstp, p);
+}
+
+/* LINK */
+int
+nfs3svc_encode_linkres(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_linkres *resp)
+{
+ p = encode_post_op_attr(rqstp, p, &resp->fh);
+ p = encode_wcc_data(rqstp, p, &resp->tfh);
+ return xdr_ressize_check(rqstp, p);
+}
+
+/* READDIR */
+int
+nfs3svc_encode_readdirres(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_readdirres *resp)
+{
+ p = encode_post_op_attr(rqstp, p, &resp->fh);
+ if (resp->status == 0) {
+ /* stupid readdir cookie */
+ memcpy(p, resp->verf, 8); p += 2;
+ p += XDR_QUADLEN(resp->count);
+ }
+
+ return xdr_ressize_check(rqstp, p);
+}
+
+/*
+ * Encode a directory entry. This one works for both normal readdir
+ * and readdirplus.
+ * The normal readdir reply requires 2 (fileid) + 1 (stringlen)
+ * + string + 2 (cookie) + 1 (next) words, i.e. 6 + strlen.
+ *
+ * The readdirplus baggage is 1+21 words for post_op_attr, plus the
+ * file handle.
+ */
+
+#define NFS3_ENTRY_BAGGAGE (2 + 1 + 2 + 1)
+#define NFS3_ENTRYPLUS_BAGGAGE (1 + 21 + 1 + (NFS3_FHSIZE >> 2))
+static int
+encode_entry(struct readdir_cd *cd, const char *name,
+ int namlen, off_t offset, ino_t ino, unsigned int d_type, int plus)
+{
+ u32 *p = cd->buffer;
+ int buflen, slen, elen;
+
+ if (cd->offset)
+ xdr_encode_hyper(cd->offset, (u64) offset);
+
+ /* nfsd_readdir calls us with name == 0 when it wants us to
+ * set the last offset entry. */
+ if (name == 0)
+ return 0;
+
+ /*
+ dprintk("encode_entry(%.*s @%ld%s)\n",
+ namlen, name, (long) offset, plus? " plus" : "");
+ */
+
+ /* truncate filename if too long */
+ if (namlen > NFS3_MAXNAMLEN)
+ namlen = NFS3_MAXNAMLEN;
+
+ slen = XDR_QUADLEN(namlen);
+ elen = slen + NFS3_ENTRY_BAGGAGE
+ + (plus? NFS3_ENTRYPLUS_BAGGAGE : 0);
+ if ((buflen = cd->buflen - elen) < 0) {
+ cd->eob = 1;
+ return -EINVAL;
+ }
+ *p++ = xdr_one; /* mark entry present */
+ p = xdr_encode_hyper(p, ino); /* file id */
+ p = xdr_encode_array(p, name, namlen);/* name length & name */
+
+ cd->offset = p; /* remember pointer */
+ p = xdr_encode_hyper(p, NFS_OFFSET_MAX); /* offset of next entry */
+
+ /* throw in readdirplus baggage */
+ if (plus) {
+ struct svc_fh fh;
+ struct svc_export *exp;
+ struct dentry *dparent, *dchild;
+
+ dparent = cd->dirfh->fh_dentry;
+ exp = cd->dirfh->fh_export;
+
+ fh_init(&fh, NFS3_FHSIZE);
+ if (isdotent(name, namlen)) {
+ dchild = dparent;
+ if (namlen == 2)
+ dchild = dchild->d_parent;
+ dchild = dget(dchild);
+ } else
+ dchild = lookup_one_len(name, dparent,namlen);
+ if (IS_ERR(dchild))
+ goto noexec;
+ if (fh_compose(&fh, exp, dchild, cd->dirfh) != 0 || !dchild->d_inode)
+ goto noexec;
+ p = encode_post_op_attr(cd->rqstp, p, &fh);
+ *p++ = xdr_one; /* yes, a file handle follows */
+ p = encode_fh(p, &fh);
+ fh_put(&fh);
+ }
+
+out:
+ cd->buflen = buflen;
+ cd->buffer = p;
+ return 0;
+
+noexec:
+ *p++ = 0;
+ *p++ = 0;
+ goto out;
+}
+
+int
+nfs3svc_encode_entry(struct readdir_cd *cd, const char *name,
+ int namlen, loff_t offset, ino_t ino, unsigned int d_type)
+{
+ return encode_entry(cd, name, namlen, offset, ino, d_type, 0);
+}
+
+int
+nfs3svc_encode_entry_plus(struct readdir_cd *cd, const char *name,
+ int namlen, loff_t offset, ino_t ino, unsigned int d_type)
+{
+ return encode_entry(cd, name, namlen, offset, ino, d_type, 1);
+}
+
+/* FSSTAT */
+int
+nfs3svc_encode_fsstatres(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_fsstatres *resp)
+{
+ struct statfs *s = &resp->stats;
+ u64 bs = s->f_bsize;
+
+ *p++ = xdr_zero; /* no post_op_attr */
+
+ if (resp->status == 0) {
+ p = xdr_encode_hyper(p, bs * s->f_blocks); /* total bytes */
+ p = xdr_encode_hyper(p, bs * s->f_bfree); /* free bytes */
+ p = xdr_encode_hyper(p, bs * s->f_bavail); /* user available bytes */
+ p = xdr_encode_hyper(p, s->f_files); /* total inodes */
+ p = xdr_encode_hyper(p, s->f_ffree); /* free inodes */
+ p = xdr_encode_hyper(p, s->f_ffree); /* user available inodes */
+ *p++ = htonl(resp->invarsec); /* mean unchanged time */
+ }
+ return xdr_ressize_check(rqstp, p);
+}
+
+/* FSINFO */
+int
+nfs3svc_encode_fsinfores(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_fsinfores *resp)
+{
+ *p++ = xdr_zero; /* no post_op_attr */
+
+ if (resp->status == 0) {
+ *p++ = htonl(resp->f_rtmax);
+ *p++ = htonl(resp->f_rtpref);
+ *p++ = htonl(resp->f_rtmult);
+ *p++ = htonl(resp->f_wtmax);
+ *p++ = htonl(resp->f_wtpref);
+ *p++ = htonl(resp->f_wtmult);
+ *p++ = htonl(resp->f_dtpref);
+ p = xdr_encode_hyper(p, resp->f_maxfilesize);
+ *p++ = xdr_one;
+ *p++ = xdr_zero;
+ *p++ = htonl(resp->f_properties);
+ }
+
+ return xdr_ressize_check(rqstp, p);
+}
+
+/* PATHCONF */
+int
+nfs3svc_encode_pathconfres(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_pathconfres *resp)
+{
+ *p++ = xdr_zero; /* no post_op_attr */
+
+ if (resp->status == 0) {
+ *p++ = htonl(resp->p_link_max);
+ *p++ = htonl(resp->p_name_max);
+ *p++ = htonl(resp->p_no_trunc);
+ *p++ = htonl(resp->p_chown_restricted);
+ *p++ = htonl(resp->p_case_insensitive);
+ *p++ = htonl(resp->p_case_preserving);
+ }
+
+ return xdr_ressize_check(rqstp, p);
+}
+
+/* COMMIT */
+int
+nfs3svc_encode_commitres(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_commitres *resp)
+{
+ p = encode_wcc_data(rqstp, p, &resp->fh);
+ /* Write verifier */
+ if (resp->status == 0) {
+ *p++ = htonl(nfssvc_boot.tv_sec);
+ *p++ = htonl(nfssvc_boot.tv_usec);
+ }
+ return xdr_ressize_check(rqstp, p);
+}
+
+/*
+ * XDR release functions
+ */
+int
+nfs3svc_release_fhandle(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_attrstat *resp)
+{
+ fh_put(&resp->fh);
+ return 1;
+}
+
+int
+nfs3svc_release_fhandle2(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd3_fhandle_pair *resp)
+{
+ fh_put(&resp->fh1);
+ fh_put(&resp->fh2);
+ return 1;
+}
diff --git a/fs/nfsd/nfscache.c b/fs/nfsd/nfscache.c
new file mode 100644
index 00000000000000..c556efb9755ea4
--- /dev/null
+++ b/fs/nfsd/nfscache.c
@@ -0,0 +1,353 @@
+/*
+ * linux/fs/nfsd/nfscache.c
+ *
+ * Request reply cache. This is currently a global cache, but this may
+ * change in the future and be a per-client cache.
+ *
+ * This code is heavily inspired by the 44BSD implementation, although
+ * it does things a bit differently.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include <linux/sunrpc/svc.h>
+#include <linux/nfsd/nfsd.h>
+#include <linux/nfsd/cache.h>
+
+/* Size of reply cache. Common values are:
+ * 4.3BSD: 128
+ * 4.4BSD: 256
+ * Solaris2: 1024
+ * DEC Unix: 512-4096
+ */
+#define CACHESIZE 1024
+#define HASHSIZE 64
+#define REQHASH(xid) ((((xid) >> 24) ^ (xid)) & (HASHSIZE-1))
+
+struct nfscache_head {
+ struct svc_cacherep * next;
+ struct svc_cacherep * prev;
+};
+
+static struct nfscache_head * hash_list;
+static struct svc_cacherep * lru_head;
+static struct svc_cacherep * lru_tail;
+static struct svc_cacherep * nfscache;
+static int cache_disabled = 1;
+
+static int nfsd_cache_append(struct svc_rqst *rqstp, struct svc_buf *data);
+
+void
+nfsd_cache_init(void)
+{
+ struct svc_cacherep *rp;
+ struct nfscache_head *rh;
+ size_t i;
+ unsigned long order;
+
+
+ i = CACHESIZE * sizeof (struct svc_cacherep);
+ for (order = 0; (PAGE_SIZE << order) < i; order++)
+ ;
+ nfscache = (struct svc_cacherep *)
+ __get_free_pages(GFP_KERNEL, order);
+ if (!nfscache) {
+ printk (KERN_ERR "nfsd: cannot allocate %Zd bytes for reply cache\n", i);
+ return;
+ }
+ memset(nfscache, 0, i);
+
+ i = HASHSIZE * sizeof (struct nfscache_head);
+ hash_list = kmalloc (i, GFP_KERNEL);
+ if (!hash_list) {
+ free_pages ((unsigned long)nfscache, order);
+ nfscache = NULL;
+ printk (KERN_ERR "nfsd: cannot allocate %Zd bytes for hash list\n", i);
+ return;
+ }
+
+ for (i = 0, rh = hash_list; i < HASHSIZE; i++, rh++)
+ rh->next = rh->prev = (struct svc_cacherep *) rh;
+
+ for (i = 0, rp = nfscache; i < CACHESIZE; i++, rp++) {
+ rp->c_state = RC_UNUSED;
+ rp->c_type = RC_NOCACHE;
+ rp->c_hash_next =
+ rp->c_hash_prev = rp;
+ rp->c_lru_next = rp + 1;
+ rp->c_lru_prev = rp - 1;
+ }
+ lru_head = nfscache;
+ lru_tail = nfscache + CACHESIZE - 1;
+ lru_head->c_lru_prev = NULL;
+ lru_tail->c_lru_next = NULL;
+
+ cache_disabled = 0;
+}
+
+void
+nfsd_cache_shutdown(void)
+{
+ struct svc_cacherep *rp;
+ size_t i;
+ unsigned long order;
+
+ for (rp = lru_head; rp; rp = rp->c_lru_next) {
+ if (rp->c_state == RC_DONE && rp->c_type == RC_REPLBUFF)
+ kfree(rp->c_replbuf.buf);
+ }
+
+ cache_disabled = 1;
+
+ i = CACHESIZE * sizeof (struct svc_cacherep);
+ for (order = 0; (PAGE_SIZE << order) < i; order++)
+ ;
+ free_pages ((unsigned long)nfscache, order);
+ nfscache = NULL;
+ kfree (hash_list);
+ hash_list = NULL;
+}
+
+/*
+ * Move cache entry to front of LRU list
+ */
+static void
+lru_put_front(struct svc_cacherep *rp)
+{
+ struct svc_cacherep *prev = rp->c_lru_prev,
+ *next = rp->c_lru_next;
+
+ if (prev)
+ prev->c_lru_next = next;
+ else
+ lru_head = next;
+ if (next)
+ next->c_lru_prev = prev;
+ else
+ lru_tail = prev;
+
+ rp->c_lru_next = lru_head;
+ rp->c_lru_prev = NULL;
+ if (lru_head)
+ lru_head->c_lru_prev = rp;
+ lru_head = rp;
+}
+
+/*
+ * Move a cache entry from one hash list to another
+ */
+static void
+hash_refile(struct svc_cacherep *rp)
+{
+ struct svc_cacherep *prev = rp->c_hash_prev,
+ *next = rp->c_hash_next;
+ struct nfscache_head *head = hash_list + REQHASH(rp->c_xid);
+
+ prev->c_hash_next = next;
+ next->c_hash_prev = prev;
+
+ rp->c_hash_next = head->next;
+ rp->c_hash_prev = (struct svc_cacherep *) head;
+ head->next->c_hash_prev = rp;
+ head->next = rp;
+}
+
+/*
+ * Try to find an entry matching the current call in the cache. When none
+ * is found, we grab the oldest unlocked entry off the LRU list.
+ * Note that no operation within the loop may sleep.
+ */
+int
+nfsd_cache_lookup(struct svc_rqst *rqstp, int type)
+{
+ struct svc_cacherep *rh, *rp;
+ u32 xid = rqstp->rq_xid,
+ proto = rqstp->rq_prot,
+ vers = rqstp->rq_vers,
+ proc = rqstp->rq_proc;
+ unsigned long age;
+
+ rqstp->rq_cacherep = NULL;
+ if (cache_disabled || type == RC_NOCACHE) {
+ nfsdstats.rcnocache++;
+ return RC_DOIT;
+ }
+
+ rp = rh = (struct svc_cacherep *) &hash_list[REQHASH(xid)];
+ while ((rp = rp->c_hash_next) != rh) {
+ if (rp->c_state != RC_UNUSED &&
+ xid == rp->c_xid && proc == rp->c_proc &&
+ proto == rp->c_prot && vers == rp->c_vers &&
+ time_before(jiffies, rp->c_timestamp + 120*HZ) &&
+ memcmp((char*)&rqstp->rq_addr, (char*)&rp->c_addr, sizeof(rp->c_addr))==0) {
+ nfsdstats.rchits++;
+ goto found_entry;
+ }
+ }
+ nfsdstats.rcmisses++;
+
+ /* This loop shouldn't take more than a few iterations normally */
+ {
+ int safe = 0;
+ for (rp = lru_tail; rp; rp = rp->c_lru_prev) {
+ if (rp->c_state != RC_INPROG)
+ break;
+ if (safe++ > CACHESIZE) {
+ printk("nfsd: loop in repcache LRU list\n");
+ cache_disabled = 1;
+ return RC_DOIT;
+ }
+ }
+ }
+
+ /* This should not happen */
+ if (rp == NULL) {
+ static int complaints;
+
+ printk(KERN_WARNING "nfsd: all repcache entries locked!\n");
+ if (++complaints > 5) {
+ printk(KERN_WARNING "nfsd: disabling repcache.\n");
+ cache_disabled = 1;
+ }
+ return RC_DOIT;
+ }
+
+ rqstp->rq_cacherep = rp;
+ rp->c_state = RC_INPROG;
+ rp->c_xid = xid;
+ rp->c_proc = proc;
+ rp->c_addr = rqstp->rq_addr;
+ rp->c_prot = proto;
+ rp->c_vers = vers;
+ rp->c_timestamp = jiffies;
+
+ hash_refile(rp);
+
+ /* release any buffer */
+ if (rp->c_type == RC_REPLBUFF) {
+ kfree(rp->c_replbuf.buf);
+ rp->c_replbuf.buf = NULL;
+ }
+ rp->c_type = RC_NOCACHE;
+
+ return RC_DOIT;
+
+found_entry:
+ /* We found a matching entry which is either in progress or done. */
+ age = jiffies - rp->c_timestamp;
+ rp->c_timestamp = jiffies;
+ lru_put_front(rp);
+
+ /* Request being processed or excessive rexmits */
+ if (rp->c_state == RC_INPROG || age < RC_DELAY)
+ return RC_DROPIT;
+
+ /* From the hall of fame of impractical attacks:
+ * Is this a user who tries to snoop on the cache? */
+ if (!rqstp->rq_secure && rp->c_secure)
+ return RC_DOIT;
+
+ /* Compose RPC reply header */
+ switch (rp->c_type) {
+ case RC_NOCACHE:
+ return RC_DOIT;
+ case RC_REPLSTAT:
+ svc_putlong(&rqstp->rq_resbuf, rp->c_replstat);
+ break;
+ case RC_REPLBUFF:
+ if (!nfsd_cache_append(rqstp, &rp->c_replbuf))
+ return RC_DOIT; /* should not happen */
+ break;
+ default:
+ printk(KERN_WARNING "nfsd: bad repcache type %d\n", rp->c_type);
+ rp->c_state = RC_UNUSED;
+ return RC_DOIT;
+ }
+
+ return RC_REPLY;
+}
+
+/*
+ * Update a cache entry. This is called from nfsd_dispatch when
+ * the procedure has been executed and the complete reply is in
+ * rqstp->rq_res.
+ *
+ * We're copying around data here rather than swapping buffers because
+ * the toplevel loop requires max-sized buffers, which would be a waste
+ * of memory for a cache with a max reply size of 100 bytes (diropokres).
+ *
+ * If we should start to use different types of cache entries tailored
+ * specifically for attrstat and fh's, we may save even more space.
+ *
+ * Also note that a cachetype of RC_NOCACHE can legally be passed when
+ * nfsd failed to encode a reply that otherwise would have been cached.
+ * In this case, nfsd_cache_update is called with statp == NULL.
+ */
+void
+nfsd_cache_update(struct svc_rqst *rqstp, int cachetype, u32 *statp)
+{
+ struct svc_cacherep *rp;
+ struct svc_buf *resp = &rqstp->rq_resbuf, *cachp;
+ int len;
+
+ if (!(rp = rqstp->rq_cacherep) || cache_disabled)
+ return;
+
+ len = resp->len - (statp - resp->base);
+
+ /* Don't cache excessive amounts of data and XDR failures */
+ if (!statp || len > (256 >> 2)) {
+ rp->c_state = RC_UNUSED;
+ return;
+ }
+
+ switch (cachetype) {
+ case RC_REPLSTAT:
+ if (len != 1)
+ printk("nfsd: RC_REPLSTAT/reply len %d!\n",len);
+ rp->c_replstat = *statp;
+ break;
+ case RC_REPLBUFF:
+ cachp = &rp->c_replbuf;
+ cachp->buf = (u32 *) kmalloc(len << 2, GFP_KERNEL);
+ if (!cachp->buf) {
+ rp->c_state = RC_UNUSED;
+ return;
+ }
+ cachp->len = len;
+ memcpy(cachp->buf, statp, len << 2);
+ break;
+ }
+
+ lru_put_front(rp);
+ rp->c_secure = rqstp->rq_secure;
+ rp->c_type = cachetype;
+ rp->c_state = RC_DONE;
+ rp->c_timestamp = jiffies;
+
+ return;
+}
+
+/*
+ * Copy cached reply to current reply buffer. Should always fit.
+ */
+static int
+nfsd_cache_append(struct svc_rqst *rqstp, struct svc_buf *data)
+{
+ struct svc_buf *resp = &rqstp->rq_resbuf;
+
+ if (resp->len + data->len > resp->buflen) {
+ printk(KERN_WARNING "nfsd: cached reply too large (%d).\n",
+ data->len);
+ return 0;
+ }
+ memcpy(resp->buf, data->buf, data->len << 2);
+ resp->buf += data->len;
+ resp->len += data->len;
+ return 1;
+}
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
new file mode 100644
index 00000000000000..59d65f500e8091
--- /dev/null
+++ b/fs/nfsd/nfsctl.c
@@ -0,0 +1,349 @@
+/*
+ * linux/fs/nfsd/nfsctl.c
+ *
+ * Syscall interface to knfsd.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+
+#include <linux/linkage.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/fcntl.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+#include <linux/nfs.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/nfsd/nfsd.h>
+#include <linux/nfsd/cache.h>
+#include <linux/nfsd/xdr.h>
+#include <linux/nfsd/syscall.h>
+
+#include <asm/uaccess.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/init.h>
+
+static int nfsctl_svc(struct nfsctl_svc *data);
+static int nfsctl_addclient(struct nfsctl_client *data);
+static int nfsctl_delclient(struct nfsctl_client *data);
+static int nfsctl_export(struct nfsctl_export *data);
+static int nfsctl_unexport(struct nfsctl_export *data);
+static int nfsctl_getfh(struct nfsctl_fhparm *, __u8 *);
+static int nfsctl_getfd(struct nfsctl_fdparm *, __u8 *);
+static int nfsctl_getfs(struct nfsctl_fsparm *, struct knfsd_fh *);
+#ifdef notyet
+static int nfsctl_ugidupdate(struct nfsctl_ugidmap *data);
+#endif
+
+extern struct seq_operations nfs_exports_op;
+static int exports_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &nfs_exports_op);
+}
+static struct file_operations exports_operations = {
+ open: exports_open,
+ read: seq_read,
+ llseek: seq_lseek,
+ release: seq_release,
+};
+
+void proc_export_init(void)
+{
+ struct proc_dir_entry *entry;
+ if (!proc_mkdir("fs/nfs", 0))
+ return;
+ entry = create_proc_entry("fs/nfs/exports", 0, NULL);
+ if (entry)
+ entry->proc_fops = &exports_operations;
+}
+
+static inline int
+nfsctl_svc(struct nfsctl_svc *data)
+{
+ return nfsd_svc(data->svc_port, data->svc_nthreads);
+}
+
+static inline int
+nfsctl_addclient(struct nfsctl_client *data)
+{
+ return exp_addclient(data);
+}
+
+static inline int
+nfsctl_delclient(struct nfsctl_client *data)
+{
+ return exp_delclient(data);
+}
+
+static inline int
+nfsctl_export(struct nfsctl_export *data)
+{
+ return exp_export(data);
+}
+
+static inline int
+nfsctl_unexport(struct nfsctl_export *data)
+{
+ return exp_unexport(data);
+}
+
+#ifdef notyet
+static inline int
+nfsctl_ugidupdate(nfs_ugidmap *data)
+{
+ return -EINVAL;
+}
+#endif
+
+static inline int
+nfsctl_getfs(struct nfsctl_fsparm *data, struct knfsd_fh *res)
+{
+ struct sockaddr_in *sin;
+ struct svc_client *clp;
+ int err = 0;
+
+ if (data->gd_addr.sa_family != AF_INET)
+ return -EPROTONOSUPPORT;
+ sin = (struct sockaddr_in *)&data->gd_addr;
+ if (data->gd_maxlen > NFS3_FHSIZE)
+ data->gd_maxlen = NFS3_FHSIZE;
+ exp_readlock();
+ if (!(clp = exp_getclient(sin)))
+ err = -EPERM;
+ else
+ err = exp_rootfh(clp, 0, 0, data->gd_path, res, data->gd_maxlen);
+ exp_unlock();
+ return err;
+}
+
+static inline int
+nfsctl_getfd(struct nfsctl_fdparm *data, __u8 *res)
+{
+ struct sockaddr_in *sin;
+ struct svc_client *clp;
+ int err = 0;
+ struct knfsd_fh fh;
+
+ if (data->gd_addr.sa_family != AF_INET)
+ return -EPROTONOSUPPORT;
+ if (data->gd_version < 2 || data->gd_version > NFSSVC_MAXVERS)
+ return -EINVAL;
+ sin = (struct sockaddr_in *)&data->gd_addr;
+
+ exp_readlock();
+ if (!(clp = exp_getclient(sin)))
+ err = -EPERM;
+ else
+ err = exp_rootfh(clp, 0, 0, data->gd_path, &fh, NFS_FHSIZE);
+ exp_unlock();
+
+ if (err == 0) {
+ if (fh.fh_size > NFS_FHSIZE)
+ err = -EINVAL;
+ else {
+ memset(res,0, NFS_FHSIZE);
+ memcpy(res, &fh.fh_base, fh.fh_size);
+ }
+ }
+
+ return err;
+}
+
+static inline int
+nfsctl_getfh(struct nfsctl_fhparm *data, __u8 *res)
+{
+ struct sockaddr_in *sin;
+ struct svc_client *clp;
+ int err = 0;
+ struct knfsd_fh fh;
+
+ if (data->gf_addr.sa_family != AF_INET)
+ return -EPROTONOSUPPORT;
+ if (data->gf_version < 2 || data->gf_version > NFSSVC_MAXVERS)
+ return -EINVAL;
+ sin = (struct sockaddr_in *)&data->gf_addr;
+
+ exp_readlock();
+ if (!(clp = exp_getclient(sin)))
+ err = -EPERM;
+ else
+ err = exp_rootfh(clp, to_kdev_t(data->gf_dev), data->gf_ino, NULL, &fh, NFS_FHSIZE);
+ exp_unlock();
+
+ if (err == 0) {
+ if (fh.fh_size > NFS_FHSIZE)
+ err = -EINVAL;
+ else {
+ memset(res,0, NFS_FHSIZE);
+ memcpy(res, &fh.fh_base, fh.fh_size);
+ }
+ }
+
+ return err;
+}
+
+#ifdef CONFIG_NFSD
+#define handle_sys_nfsservctl sys_nfsservctl
+#endif
+
+static struct {
+ int argsize, respsize;
+} sizes[] = {
+ /* NFSCTL_SVC */ { sizeof(struct nfsctl_svc), 0 },
+ /* NFSCTL_ADDCLIENT */ { sizeof(struct nfsctl_client), 0},
+ /* NFSCTL_DELCLIENT */ { sizeof(struct nfsctl_client), 0},
+ /* NFSCTL_EXPORT */ { sizeof(struct nfsctl_export), 0},
+ /* NFSCTL_UNEXPORT */ { sizeof(struct nfsctl_export), 0},
+ /* NFSCTL_UGIDUPDATE */ { sizeof(struct nfsctl_uidmap), 0},
+ /* NFSCTL_GETFH */ { sizeof(struct nfsctl_fhparm), NFS_FHSIZE},
+ /* NFSCTL_GETFD */ { sizeof(struct nfsctl_fdparm), NFS_FHSIZE},
+ /* NFSCTL_GETFS */ { sizeof(struct nfsctl_fsparm), sizeof(struct knfsd_fh)},
+};
+#define CMD_MAX (sizeof(sizes)/sizeof(sizes[0])-1)
+
+long
+asmlinkage handle_sys_nfsservctl(int cmd, void *opaque_argp, void *opaque_resp)
+{
+ struct nfsctl_arg * argp = opaque_argp;
+ union nfsctl_res * resp = opaque_resp;
+ struct nfsctl_arg * arg = NULL;
+ union nfsctl_res * res = NULL;
+ int err;
+ int argsize, respsize;
+
+ lock_kernel ();
+
+ err = -EPERM;
+ if (!capable(CAP_SYS_ADMIN)) {
+ goto done;
+ }
+ err = -EINVAL;
+ if (cmd<0 || cmd > CMD_MAX)
+ goto done;
+ err = -EFAULT;
+ argsize = sizes[cmd].argsize + (int)&((struct nfsctl_arg *)0)->u;
+ respsize = sizes[cmd].respsize; /* maximum */
+ if (!access_ok(VERIFY_READ, argp, argsize)
+ || (resp && !access_ok(VERIFY_WRITE, resp, respsize))) {
+ goto done;
+ }
+ err = -ENOMEM; /* ??? */
+ if (!(arg = kmalloc(sizeof(*arg), GFP_USER)) ||
+ (resp && !(res = kmalloc(sizeof(*res), GFP_USER)))) {
+ goto done;
+ }
+
+ err = -EINVAL;
+ copy_from_user(arg, argp, argsize);
+ if (arg->ca_version != NFSCTL_VERSION) {
+ printk(KERN_WARNING "nfsd: incompatible version in syscall.\n");
+ goto done;
+ }
+
+ switch(cmd) {
+ case NFSCTL_SVC:
+ err = nfsctl_svc(&arg->ca_svc);
+ break;
+ case NFSCTL_ADDCLIENT:
+ err = nfsctl_addclient(&arg->ca_client);
+ break;
+ case NFSCTL_DELCLIENT:
+ err = nfsctl_delclient(&arg->ca_client);
+ break;
+ case NFSCTL_EXPORT:
+ err = nfsctl_export(&arg->ca_export);
+ break;
+ case NFSCTL_UNEXPORT:
+ err = nfsctl_unexport(&arg->ca_export);
+ break;
+#ifdef notyet
+ case NFSCTL_UGIDUPDATE:
+ err = nfsctl_ugidupdate(&arg->ca_umap);
+ break;
+#endif
+ case NFSCTL_GETFH:
+ err = nfsctl_getfh(&arg->ca_getfh, res->cr_getfh);
+ break;
+ case NFSCTL_GETFD:
+ err = nfsctl_getfd(&arg->ca_getfd, res->cr_getfh);
+ break;
+ case NFSCTL_GETFS:
+ err = nfsctl_getfs(&arg->ca_getfs, &res->cr_getfs);
+ respsize = res->cr_getfs.fh_size+ (int)&((struct knfsd_fh*)0)->fh_base;
+ break;
+ default:
+ err = -EINVAL;
+ }
+
+ if (!err && resp && respsize)
+ copy_to_user(resp, res, respsize);
+
+done:
+ if (arg)
+ kfree(arg);
+ if (res)
+ kfree(res);
+
+ unlock_kernel ();
+ return err;
+}
+
+EXPORT_NO_SYMBOLS;
+MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
+MODULE_LICENSE("GPL");
+
+#ifdef MODULE
+struct nfsd_linkage nfsd_linkage_s = {
+ do_nfsservctl: handle_sys_nfsservctl,
+ owner: THIS_MODULE,
+};
+#endif
+
+/*
+ * Initialize the module
+ */
+static int __init
+nfsd_init(void)
+{
+ printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n");
+#ifdef MODULE
+ nfsd_linkage = &nfsd_linkage_s;
+#endif
+ nfsd_stat_init(); /* Statistics */
+ nfsd_cache_init(); /* RPC reply cache */
+ nfsd_export_init(); /* Exports table */
+ nfsd_lockd_init(); /* lockd->nfsd callbacks */
+ proc_export_init();
+ return 0;
+}
+
+/*
+ * Clean up the mess before unloading the module
+ */
+static void __exit
+nfsd_exit(void)
+{
+#ifdef MODULE
+ nfsd_linkage = NULL;
+#endif
+ nfsd_export_shutdown();
+ nfsd_cache_shutdown();
+ remove_proc_entry("fs/nfs/exports", NULL);
+ remove_proc_entry("fs/nfs", NULL);
+ nfsd_stat_shutdown();
+ nfsd_lockd_shutdown();
+}
+
+module_init(nfsd_init);
+module_exit(nfsd_exit);
diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c
new file mode 100644
index 00000000000000..c500f1002d3e57
--- /dev/null
+++ b/fs/nfsd/nfsfh.c
@@ -0,0 +1,925 @@
+/*
+ * linux/fs/nfsd/nfsfh.c
+ *
+ * NFS server file handle treatment.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ * Portions Copyright (C) 1999 G. Allen Morris III <gam3@acm.org>
+ * Extensive rewrite by Neil Brown <neilb@cse.unsw.edu.au> Southern-Spring 1999
+ */
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/unistd.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/dcache.h>
+#include <asm/pgtable.h>
+
+#include <linux/sunrpc/svc.h>
+#include <linux/nfsd/nfsd.h>
+
+#define NFSDDBG_FACILITY NFSDDBG_FH
+#define NFSD_PARANOIA 1
+/* #define NFSD_DEBUG_VERBOSE 1 */
+
+
+static int nfsd_nr_verified;
+static int nfsd_nr_put;
+
+
+struct nfsd_getdents_callback {
+ char *name; /* name that was found. It already points to a buffer NAME_MAX+1 is size */
+ unsigned long ino; /* the inum we are looking for */
+ int found; /* inode matched? */
+ int sequence; /* sequence counter */
+};
+
+/*
+ * A rather strange filldir function to capture
+ * the name matching the specified inode number.
+ */
+static int filldir_one(void * __buf, const char * name, int len,
+ loff_t pos, ino_t ino, unsigned int d_type)
+{
+ struct nfsd_getdents_callback *buf = __buf;
+ int result = 0;
+
+ buf->sequence++;
+#ifdef NFSD_DEBUG_VERBOSE
+dprintk("filldir_one: seq=%d, ino=%ld, name=%s\n", buf->sequence, ino, name);
+#endif
+ if (buf->ino == ino) {
+ memcpy(buf->name, name, len);
+ buf->name[len] = '\0';
+ buf->found = 1;
+ result = -1;
+ }
+ return result;
+}
+
+/**
+ * nfsd_get_name - default nfsd_operations->get_name function
+ * @dentry: the directory in which to find a name
+ * @name: a pointer to a %NAME_MAX+1 char buffer to store the name
+ * @child: the dentry for the child directory.
+ *
+ * calls readdir on the parent until it finds an entry with
+ * the same inode number as the child, and returns that.
+ */
+static int nfsd_get_name(struct dentry *dentry, char *name,
+ struct dentry *child)
+{
+ struct inode *dir = dentry->d_inode;
+ int error;
+ struct file file;
+ struct nfsd_getdents_callback buffer;
+
+ error = -ENOTDIR;
+ if (!dir || !S_ISDIR(dir->i_mode))
+ goto out;
+ error = -EINVAL;
+ if (!dir->i_fop)
+ goto out;
+ /*
+ * Open the directory ...
+ */
+ error = init_private_file(&file, dentry, FMODE_READ);
+ if (error)
+ goto out;
+ error = -EINVAL;
+ if (!file.f_op->readdir)
+ goto out_close;
+
+ buffer.name = name;
+ buffer.ino = child->d_inode->i_ino;
+ buffer.found = 0;
+ buffer.sequence = 0;
+ while (1) {
+ int old_seq = buffer.sequence;
+
+ error = vfs_readdir(&file, filldir_one, &buffer);
+
+ if (error < 0)
+ break;
+
+ error = 0;
+ if (buffer.found)
+ break;
+ error = -ENOENT;
+ if (old_seq == buffer.sequence)
+ break;
+ }
+
+out_close:
+ if (file.f_op->release)
+ file.f_op->release(dir, &file);
+out:
+ return error;
+}
+
+/* this should be provided by each filesystem in an nfsd_operations interface as
+ * iget isn't really the right interface
+ */
+static struct dentry *nfsd_iget(struct super_block *sb, unsigned long ino, __u32 generation)
+{
+
+ /* iget isn't really right if the inode is currently unallocated!!
+ * This should really all be done inside each filesystem
+ *
+ * ext2fs' read_inode has been strengthed to return a bad_inode if the inode
+ * had been deleted.
+ *
+ * Currently we don't know the generation for parent directory, so a generation
+ * of 0 means "accept any"
+ */
+ struct inode *inode;
+ struct list_head *lp;
+ struct dentry *result;
+ if (ino == 0)
+ return ERR_PTR(-ESTALE);
+ inode = iget(sb, ino);
+ if (inode == NULL)
+ return ERR_PTR(-ENOMEM);
+ if (is_bad_inode(inode)
+ || (generation && inode->i_generation != generation)
+ ) {
+ /* we didn't find the right inode.. */
+ dprintk("fh_verify: Inode %lu, Bad count: %d %d or version %u %u\n",
+ inode->i_ino,
+ inode->i_nlink, atomic_read(&inode->i_count),
+ inode->i_generation,
+ generation);
+
+ iput(inode);
+ return ERR_PTR(-ESTALE);
+ }
+ /* now to find a dentry.
+ * If possible, get a well-connected one
+ */
+ spin_lock(&dcache_lock);
+ for (lp = inode->i_dentry.next; lp != &inode->i_dentry ; lp=lp->next) {
+ result = list_entry(lp,struct dentry, d_alias);
+ if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED)) {
+ dget_locked(result);
+ result->d_vfs_flags |= DCACHE_REFERENCED;
+ spin_unlock(&dcache_lock);
+ iput(inode);
+ return result;
+ }
+ }
+ spin_unlock(&dcache_lock);
+ result = d_alloc_root(inode);
+ if (result == NULL) {
+ iput(inode);
+ return ERR_PTR(-ENOMEM);
+ }
+ result->d_flags |= DCACHE_NFSD_DISCONNECTED;
+ return result;
+}
+
+static struct dentry *nfsd_get_dentry(struct super_block *sb, __u32 *fh,
+ int len, int fhtype, int parent)
+{
+ if (sb->s_op->fh_to_dentry)
+ return sb->s_op->fh_to_dentry(sb, fh, len, fhtype, parent);
+ switch (fhtype) {
+ case 1:
+ if (len < 2)
+ break;
+ if (parent)
+ break;
+ return nfsd_iget(sb, fh[0], fh[1]);
+
+ case 2:
+ if (len < 3)
+ break;
+ if (parent)
+ return nfsd_iget(sb,fh[2],0);
+ return nfsd_iget(sb,fh[0],fh[1]);
+ default: break;
+ }
+ return ERR_PTR(-EINVAL);
+}
+
+
+/* this routine links an IS_ROOT dentry into the dcache tree. It gains "parent"
+ * as a parent and "name" as a name
+ * It should possibly go in dcache.c
+ */
+int d_splice(struct dentry *target, struct dentry *parent, struct qstr *name)
+{
+ struct dentry *tdentry;
+#ifdef NFSD_PARANOIA
+ if (!IS_ROOT(target))
+ printk("nfsd: d_splice with no-root target: %s/%s\n", parent->d_name.name, name->name);
+ if (!(target->d_flags & DCACHE_NFSD_DISCONNECTED))
+ printk("nfsd: d_splice with non-DISCONNECTED target: %s/%s\n", parent->d_name.name, name->name);
+#endif
+ tdentry = d_alloc(parent, name);
+ if (tdentry == NULL)
+ return -ENOMEM;
+ d_move(target, tdentry);
+
+ /* tdentry will have been made a "child" of target (the parent of target)
+ * make it an IS_ROOT instead
+ */
+ spin_lock(&dcache_lock);
+ list_del_init(&tdentry->d_child);
+ tdentry->d_parent = tdentry;
+ spin_unlock(&dcache_lock);
+ d_rehash(target);
+ dput(tdentry);
+
+ /* if parent is properly connected, then we can assert that
+ * the children are connected, but it must be a singluar (non-forking)
+ * branch
+ */
+ if (!(parent->d_flags & DCACHE_NFSD_DISCONNECTED)) {
+ while (target) {
+ target->d_flags &= ~DCACHE_NFSD_DISCONNECTED;
+ parent = target;
+ spin_lock(&dcache_lock);
+ if (list_empty(&parent->d_subdirs))
+ target = NULL;
+ else {
+ target = list_entry(parent->d_subdirs.next, struct dentry, d_child);
+#ifdef NFSD_PARANOIA
+ /* must be only child */
+ if (target->d_child.next != &parent->d_subdirs
+ || target->d_child.prev != &parent->d_subdirs)
+ printk("nfsd: d_splice found non-singular disconnected branch: %s/%s\n",
+ parent->d_name.name, target->d_name.name);
+#endif
+ }
+ spin_unlock(&dcache_lock);
+ }
+ }
+ return 0;
+}
+
+/* this routine finds the dentry of the parent of a given directory
+ * it should be in the filesystem accessed by nfsd_operations
+ * it assumes lookup("..") works.
+ */
+struct dentry *nfsd_findparent(struct dentry *child)
+{
+ struct dentry *tdentry, *pdentry;
+ tdentry = d_alloc(child, &(const struct qstr) {"..", 2, 0});
+ if (!tdentry)
+ return ERR_PTR(-ENOMEM);
+
+ /* I'm going to assume that if the returned dentry is different, then
+ * it is well connected. But nobody returns different dentrys do they?
+ */
+ down(&child->d_inode->i_sem);
+ pdentry = child->d_inode->i_op->lookup(child->d_inode, tdentry);
+ up(&child->d_inode->i_sem);
+ d_drop(tdentry); /* we never want ".." hashed */
+ if (!pdentry && tdentry->d_inode == NULL) {
+ /* File system cannot find ".." ... sad but possible */
+ pdentry = ERR_PTR(-EINVAL);
+ }
+ if (!pdentry) {
+ /* I don't want to return a ".." dentry.
+ * I would prefer to return an unconnected "IS_ROOT" dentry,
+ * though a properly connected dentry is even better
+ */
+ /* if first or last of alias list is not tdentry, use that
+ * else make a root dentry
+ */
+ struct list_head *aliases = &tdentry->d_inode->i_dentry;
+ spin_lock(&dcache_lock);
+ if (aliases->next != aliases) {
+ pdentry = list_entry(aliases->next, struct dentry, d_alias);
+ if (pdentry == tdentry)
+ pdentry = list_entry(aliases->prev, struct dentry, d_alias);
+ if (pdentry == tdentry)
+ pdentry = NULL;
+ if (pdentry) dget_locked(pdentry);
+ }
+ spin_unlock(&dcache_lock);
+ if (pdentry == NULL) {
+ pdentry = d_alloc_root(tdentry->d_inode);
+ if (pdentry) {
+ igrab(tdentry->d_inode);
+ pdentry->d_flags |= DCACHE_NFSD_DISCONNECTED;
+ pdentry->d_op = child->d_op;
+ }
+ }
+ if (pdentry == NULL)
+ pdentry = ERR_PTR(-ENOMEM);
+ }
+ dput(tdentry); /* it is not hashed, it will be discarded */
+ return pdentry;
+}
+
+static struct dentry *splice(struct dentry *child, struct dentry *parent)
+{
+ int err = 0, nerr;
+ struct qstr qs;
+ char namebuf[256];
+ struct list_head *lp;
+ /* child is an IS_ROOT (anonymous) dentry, but it is hypothesised that
+ * it should be a child of parent.
+ * We see if we can find a name and, if we can - splice it in.
+ * We lookup the name before locking (i_sem) the directory as namelookup
+ * also claims i_sem. If the name gets changed then we will loop around
+ * and try again in find_fh_dentry.
+ */
+
+ nerr = nfsd_get_name(parent, namebuf, child);
+
+ /*
+ * We now claim the parent i_sem so that no-one else tries to create
+ * a dentry in the parent while we are.
+ */
+
+ down(&parent->d_inode->i_sem);
+
+ /* Now, things might have changed while we waited.
+ * Possibly a friendly filesystem found child and spliced it in in response
+ * to a lookup (though nobody does this yet). In this case, just succeed.
+ */
+ if (child->d_parent == parent) goto out;
+
+ /* Possibly a new dentry has been made for this child->d_inode in
+ * parent by a lookup. In this case return that dentry. Caller must
+ * notice and act accordingly
+ */
+ spin_lock(&dcache_lock);
+ list_for_each(lp, &child->d_inode->i_dentry) {
+ struct dentry *tmp = list_entry(lp,struct dentry, d_alias);
+ if (!list_empty(&tmp->d_hash) &&
+ tmp->d_parent == parent) {
+ child = dget_locked(tmp);
+ spin_unlock(&dcache_lock);
+ goto out;
+ }
+ }
+ spin_unlock(&dcache_lock);
+
+ /* now we need that name. If there was an error getting it, now is th
+ * time to bail out.
+ */
+ if ((err = nerr))
+ goto out;
+ qs.name = namebuf;
+ qs.len = strlen(namebuf);
+ if (find_inode_number(parent, &qs) != 0) {
+ /* Now that IS odd. I wonder what it means... */
+ err = -EEXIST;
+ printk("nfsd-fh: found a name that I didn't expect: %s/%s\n", parent->d_name.name, qs.name);
+ goto out;
+ }
+ err = d_splice(child, parent, &qs);
+ dprintk("nfsd_fh: found name %s for ino %ld\n", child->d_name.name, child->d_inode->i_ino);
+ out:
+ up(&parent->d_inode->i_sem);
+ if (err)
+ return ERR_PTR(err);
+ else
+ return child;
+}
+
+/*
+ * This is the basic lookup mechanism for turning an NFS file handle
+ * into a dentry.
+ * We use nfsd_iget and if that doesn't return a suitably connected dentry,
+ * we try to find the parent, and the parent of that and so-on until a
+ * connection if made.
+ */
+static struct dentry *
+find_fh_dentry(struct super_block *sb, __u32 *datap, int len, int fhtype, int needpath)
+{
+ struct dentry *dentry, *result = NULL;
+ struct dentry *tmp;
+ int err = -ESTALE;
+ /* the sb->s_nfsd_free_path_sem semaphore is needed to make sure that only one unconnected (free)
+ * dcache path ever exists, as otherwise two partial paths might get
+ * joined together, which would be very confusing.
+ * If there is ever an unconnected non-root directory, then this lock
+ * must be held.
+ */
+
+
+ nfsdstats.fh_lookup++;
+ /*
+ * Attempt to find the inode.
+ */
+ retry:
+ down(&sb->s_nfsd_free_path_sem);
+ result = nfsd_get_dentry(sb, datap, len, fhtype, 0);
+ if (IS_ERR(result)
+ || !(result->d_flags & DCACHE_NFSD_DISCONNECTED)
+ || (!S_ISDIR(result->d_inode->i_mode) && ! needpath)) {
+ up(&sb->s_nfsd_free_path_sem);
+
+ err = PTR_ERR(result);
+ if (IS_ERR(result))
+ goto err_out;
+ if ((result->d_flags & DCACHE_NFSD_DISCONNECTED))
+ nfsdstats.fh_anon++;
+ return result;
+ }
+
+ /* It's a directory, or we are required to confirm the file's
+ * location in the tree.
+ */
+ dprintk("nfs_fh: need to look harder for %d/%d\n",sb->s_dev,datap[0]);
+
+ if (!S_ISDIR(result->d_inode->i_mode)) {
+ nfsdstats.fh_nocache_nondir++;
+ /* need to iget dirino and make sure this inode is in that directory */
+ dentry = nfsd_get_dentry(sb, datap, len, fhtype, 1);
+ err = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
+ goto err_result;
+ err = -ESTALE;
+ if (!dentry->d_inode
+ || !S_ISDIR(dentry->d_inode->i_mode)) {
+ goto err_dentry;
+ }
+ tmp = splice(result, dentry);
+ err = PTR_ERR(tmp);
+ if (IS_ERR(tmp))
+ goto err_dentry;
+ if (tmp != result) {
+ /* it is safe to just use tmp instead, but we must discard result first */
+ d_drop(result);
+ dput(result);
+ result = tmp;
+ }
+ } else {
+ nfsdstats.fh_nocache_dir++;
+ dentry = dget(result);
+ }
+
+ while(dentry->d_flags & DCACHE_NFSD_DISCONNECTED) {
+ /* LOOP INVARIANT */
+ /* haven't found a place in the tree yet, but we do have a free path
+ * from dentry down to result, and dentry is a directory.
+ * Have a hold on dentry and result */
+ struct dentry *pdentry;
+ struct inode *parent;
+
+ pdentry = nfsd_findparent(dentry);
+ err = PTR_ERR(pdentry);
+ if (IS_ERR(pdentry))
+ goto err_dentry;
+ parent = pdentry->d_inode;
+ err = -EACCES;
+ if (!parent) {
+ dput(pdentry);
+ goto err_dentry;
+ }
+
+ tmp = splice(dentry, pdentry);
+ if (tmp != dentry) {
+ /* Something wrong. We need to drop the whole dentry->result path
+ * whatever it was
+ */
+ struct dentry *d;
+ for (d=result ; d ; d=(d->d_parent == d)?NULL:d->d_parent)
+ d_drop(d);
+ }
+ if (IS_ERR(tmp)) {
+ err = PTR_ERR(tmp);
+ dput(pdentry);
+ goto err_dentry;
+ }
+ if (tmp != dentry) {
+ /* we lost a race, try again
+ */
+ dput(pdentry);
+ dput(tmp);
+ dput(dentry);
+ dput(result); /* this will discard the whole free path, so we can up the semaphore */
+ up(&sb->s_nfsd_free_path_sem);
+ goto retry;
+ }
+ dput(dentry);
+ dentry = pdentry;
+ }
+ dput(dentry);
+ up(&sb->s_nfsd_free_path_sem);
+ return result;
+
+err_dentry:
+ dput(dentry);
+err_result:
+ dput(result);
+ up(&sb->s_nfsd_free_path_sem);
+err_out:
+ if (err == -ESTALE)
+ nfsdstats.fh_stale++;
+ return ERR_PTR(err);
+}
+
+/*
+ * Perform sanity checks on the dentry in a client's file handle.
+ *
+ * Note that the file handle dentry may need to be freed even after
+ * an error return.
+ *
+ * This is only called at the start of an nfsproc call, so fhp points to
+ * a svc_fh which is all 0 except for the over-the-wire file handle.
+ */
+u32
+fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
+{
+ struct knfsd_fh *fh = &fhp->fh_handle;
+ struct svc_export *exp;
+ struct dentry *dentry;
+ struct inode *inode;
+ u32 error = 0;
+
+ dprintk("nfsd: fh_verify(%s)\n", SVCFH_fmt(fhp));
+
+ /* keep this filehandle for possible reference when encoding attributes */
+ rqstp->rq_reffh = fh;
+
+ if (!fhp->fh_dentry) {
+ kdev_t xdev = NODEV;
+ ino_t xino = 0;
+ __u32 *datap=NULL;
+ int data_left = fh->fh_size/4;
+ int nfsdev;
+ int fsid = 0;
+
+ error = nfserr_stale;
+ if (rqstp->rq_vers == 3)
+ error = nfserr_badhandle;
+ if (fh->fh_version == 1) {
+
+ datap = fh->fh_auth;
+ if (--data_left<0) goto out;
+ switch (fh->fh_auth_type) {
+ case 0: break;
+ default: goto out;
+ }
+
+ switch (fh->fh_fsid_type) {
+ case 0:
+ if ((data_left-=2)<0) goto out;
+ nfsdev = ntohl(*datap++);
+ xdev = MKDEV(nfsdev>>16, nfsdev&0xFFFF);
+ xino = *datap++;
+ break;
+ case 1:
+ if ((data_left-=1)<0) goto out;
+ fsid = *datap++;
+ break;
+ default:
+ goto out;
+ }
+ } else {
+ if (fh->fh_size != NFS_FHSIZE)
+ goto out;
+ /* assume old filehandle format */
+ xdev = u32_to_kdev_t(fh->ofh_xdev);
+ xino = u32_to_ino_t(fh->ofh_xino);
+ }
+
+ /*
+ * Look up the export entry.
+ */
+ error = nfserr_stale;
+ if (fh->fh_version == 1 && fh->fh_fsid_type == 1)
+ exp = exp_get_fsid(rqstp->rq_client, fsid);
+ else
+ exp = exp_get(rqstp->rq_client, xdev, xino);
+
+ if (!exp)
+ /* export entry revoked */
+ goto out;
+
+ /* Check if the request originated from a secure port. */
+ error = nfserr_perm;
+ if (!rqstp->rq_secure && EX_SECURE(exp)) {
+ printk(KERN_WARNING
+ "nfsd: request from insecure port (%08x:%d)!\n",
+ ntohl(rqstp->rq_addr.sin_addr.s_addr),
+ ntohs(rqstp->rq_addr.sin_port));
+ goto out;
+ }
+
+ /* Set user creds if we haven't done so already. */
+ nfsd_setuser(rqstp, exp);
+
+ /*
+ * Look up the dentry using the NFS file handle.
+ */
+ error = nfserr_stale;
+ if (rqstp->rq_vers == 3)
+ error = nfserr_badhandle;
+
+ if (fh->fh_version == 1) {
+ /* if fileid_type != 0, and super_operations provide fh_to_dentry lookup,
+ * then should use that */
+ switch (fh->fh_fileid_type) {
+ case 0:
+ dentry = dget(exp->ex_dentry);
+ /* need to revalidate the inode */
+ inode = dentry->d_inode;
+ if (inode->i_op && inode->i_op->revalidate)
+ if (inode->i_op->revalidate(dentry)) {
+ dput(dentry);
+ dentry = ERR_PTR(-ESTALE);
+ }
+ break;
+ default:
+ dentry = find_fh_dentry(exp->ex_dentry->d_inode->i_sb,
+ datap, data_left, fh->fh_fileid_type,
+ !(exp->ex_flags & NFSEXP_NOSUBTREECHECK));
+ }
+ } else {
+ __u32 tfh[3];
+ tfh[0] = fh->ofh_ino;
+ tfh[1] = fh->ofh_generation;
+ tfh[2] = fh->ofh_dirino;
+ dentry = find_fh_dentry(exp->ex_dentry->d_inode->i_sb,
+ tfh, 3, fh->ofh_dirino?2:1,
+ !(exp->ex_flags & NFSEXP_NOSUBTREECHECK));
+ }
+ if (IS_ERR(dentry)) {
+ if (PTR_ERR(dentry) != -EINVAL)
+ error = nfserrno(PTR_ERR(dentry));
+ goto out;
+ }
+#ifdef NFSD_PARANOIA
+ if (S_ISDIR(dentry->d_inode->i_mode) &&
+ (dentry->d_flags & DCACHE_NFSD_DISCONNECTED)) {
+ printk("nfsd: find_fh_dentry returned a DISCONNECTED directory: %s/%s\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
+ }
+#endif
+
+ fhp->fh_dentry = dentry;
+ fhp->fh_export = exp;
+ nfsd_nr_verified++;
+ } else {
+ /* just rechecking permissions
+ * (e.g. nfsproc_create calls fh_verify, then nfsd_create does as well)
+ */
+ dprintk("nfsd: fh_verify - just checking\n");
+ dentry = fhp->fh_dentry;
+ exp = fhp->fh_export;
+ }
+
+ inode = dentry->d_inode;
+
+ /* Type check. The correct error return for type mismatches
+ * does not seem to be generally agreed upon. SunOS seems to
+ * use EISDIR if file isn't S_IFREG; a comment in the NFSv3
+ * spec says this is incorrect (implementation notes for the
+ * write call).
+ */
+
+ /* Type can be negative to e.g. exclude directories from linking */
+ if (type > 0 && (inode->i_mode & S_IFMT) != type) {
+ if (type == S_IFDIR)
+ error = nfserr_notdir;
+ else if ((inode->i_mode & S_IFMT) == S_IFDIR)
+ error = nfserr_isdir;
+ else
+ error = nfserr_inval;
+ goto out;
+ }
+ if (type < 0 && (inode->i_mode & S_IFMT) == -type) {
+ error = (type == -S_IFDIR)? nfserr_isdir : nfserr_notdir;
+ goto out;
+ }
+
+ /*
+ * Security: Check that the export is valid for dentry <gam3@acm.org>
+ */
+ error = 0;
+
+ if (!(exp->ex_flags & NFSEXP_NOSUBTREECHECK)) {
+ struct dentry *tdentry = dentry;
+
+ while (tdentry != exp->ex_dentry && !IS_ROOT(tdentry)) {
+ struct dentry *parent = tdentry->d_parent;
+
+ /* make sure parents give x permission to user */
+ error = permission(parent->d_inode, MAY_EXEC);
+ if (error)
+ break;
+ tdentry = parent;
+ }
+ if (exp->ex_dentry != tdentry) {
+ error = nfserr_stale;
+ printk("fh_verify: no root_squashed access at %s/%s.\n",
+ dentry->d_parent->d_name.name,
+ dentry->d_name.name);
+ goto out;
+ }
+ }
+
+ /* Finally, check access permissions. */
+ if (!error) {
+ error = nfsd_permission(exp, dentry, access);
+ }
+#ifdef NFSD_PARANOIA_EXTREME
+ if (error) {
+ printk("fh_verify: %s/%s permission failure, acc=%x, error=%d\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name, access, (error >> 24));
+ }
+#endif
+out:
+ if (error == nfserr_stale)
+ nfsdstats.fh_stale++;
+ return error;
+}
+
+/*
+ * Compose a file handle for an NFS reply.
+ *
+ * Note that when first composed, the dentry may not yet have
+ * an inode. In this case a call to fh_update should be made
+ * before the fh goes out on the wire ...
+ */
+inline int _fh_update(struct dentry *dentry, struct svc_export *exp,
+ __u32 *datap, int *maxsize)
+{
+ struct super_block *sb = dentry->d_inode->i_sb;
+
+ if (dentry == exp->ex_dentry) {
+ *maxsize = 0;
+ return 0;
+ }
+
+ if (sb->s_op->dentry_to_fh) {
+ int need_parent = !S_ISDIR(dentry->d_inode->i_mode) &&
+ !(exp->ex_flags & NFSEXP_NOSUBTREECHECK);
+
+ int type = sb->s_op->dentry_to_fh(dentry, datap, maxsize, need_parent);
+ return type;
+ }
+
+ if (*maxsize < 2)
+ return 255;
+ *datap++ = ino_t_to_u32(dentry->d_inode->i_ino);
+ *datap++ = dentry->d_inode->i_generation;
+ if (*maxsize ==2 ||
+ S_ISDIR(dentry->d_inode->i_mode) ||
+ (exp->ex_flags & NFSEXP_NOSUBTREECHECK)) {
+ *maxsize = 2;
+ return 1;
+ }
+ *datap++ = ino_t_to_u32(dentry->d_parent->d_inode->i_ino);
+ *maxsize = 3;
+ return 2;
+}
+
+/*
+ * for composing old style file handles
+ */
+inline void _fh_update_old(struct dentry *dentry, struct svc_export *exp,
+ struct knfsd_fh *fh)
+{
+ fh->ofh_ino = ino_t_to_u32(dentry->d_inode->i_ino);
+ fh->ofh_generation = dentry->d_inode->i_generation;
+ if (S_ISDIR(dentry->d_inode->i_mode) ||
+ (exp->ex_flags & NFSEXP_NOSUBTREECHECK))
+ fh->ofh_dirino = 0;
+}
+
+int
+fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry, struct svc_fh *ref_fh)
+{
+ /* ref_fh is a reference file handle.
+ * if it is non-null, then we should compose a filehandle which is
+ * of the same version, where possible.
+ * Currently, that means that if ref_fh->fh_handle.fh_version == 0xca
+ * Then create a 32byte filehandle using nfs_fhbase_old
+ * But only do this if dentry_to_fh is not available
+ *
+ */
+
+ struct inode * inode = dentry->d_inode;
+ struct dentry *parent = dentry->d_parent;
+ __u32 *datap;
+
+ dprintk("nfsd: fh_compose(exp %x/%ld %s/%s, ino=%ld)\n",
+ exp->ex_dev, (long) exp->ex_ino,
+ parent->d_name.name, dentry->d_name.name,
+ (inode ? inode->i_ino : 0));
+
+ if (fhp->fh_locked || fhp->fh_dentry) {
+ printk(KERN_ERR "fh_compose: fh %s/%s not initialized!\n",
+ parent->d_name.name, dentry->d_name.name);
+ }
+ if (fhp->fh_maxsize < NFS_FHSIZE)
+ printk(KERN_ERR "fh_compose: called with maxsize %d! %s/%s\n",
+ fhp->fh_maxsize, parent->d_name.name, dentry->d_name.name);
+
+ fhp->fh_dentry = dentry; /* our internal copy */
+ fhp->fh_export = exp;
+
+ if (ref_fh &&
+ ref_fh->fh_handle.fh_version == 0xca &&
+ parent->d_inode->i_sb->s_op->dentry_to_fh == NULL) {
+ /* old style filehandle please */
+ memset(&fhp->fh_handle.fh_base, 0, NFS_FHSIZE);
+ fhp->fh_handle.fh_size = NFS_FHSIZE;
+ fhp->fh_handle.ofh_dcookie = 0xfeebbaca;
+ fhp->fh_handle.ofh_dev = htonl((MAJOR(exp->ex_dev)<<16)| MINOR(exp->ex_dev));
+ fhp->fh_handle.ofh_xdev = fhp->fh_handle.ofh_dev;
+ fhp->fh_handle.ofh_xino = ino_t_to_u32(exp->ex_ino);
+ fhp->fh_handle.ofh_dirino = ino_t_to_u32(dentry->d_parent->d_inode->i_ino);
+ if (inode)
+ _fh_update_old(dentry, exp, &fhp->fh_handle);
+ } else {
+ fhp->fh_handle.fh_version = 1;
+ fhp->fh_handle.fh_auth_type = 0;
+ datap = fhp->fh_handle.fh_auth+0;
+ if ((exp->ex_flags & NFSEXP_FSID) &&
+ (!ref_fh || ref_fh->fh_handle.fh_fsid_type == 1)) {
+ fhp->fh_handle.fh_fsid_type = 1;
+ /* fsid_type 1 == 4 bytes filesystem id */
+ *datap++ = exp->ex_fsid;
+ fhp->fh_handle.fh_size = 2*4;
+ } else {
+ fhp->fh_handle.fh_fsid_type = 0;
+ /* fsid_type 0 == 2byte major, 2byte minor, 4byte inode */
+ *datap++ = htonl((MAJOR(exp->ex_dev)<<16)| MINOR(exp->ex_dev));
+ *datap++ = ino_t_to_u32(exp->ex_ino);
+ fhp->fh_handle.fh_size = 3*4;
+ }
+ if (inode) {
+ int size = fhp->fh_maxsize/4 - 3;
+ fhp->fh_handle.fh_fileid_type =
+ _fh_update(dentry, exp, datap, &size);
+ fhp->fh_handle.fh_size += size*4;
+ }
+ }
+
+ nfsd_nr_verified++;
+ if (fhp->fh_handle.fh_fileid_type == 255)
+ return nfserr_opnotsupp;
+ return 0;
+}
+
+/*
+ * Update file handle information after changing a dentry.
+ * This is only called by nfsd_create, nfsd_create_v3 and nfsd_proc_create
+ */
+int
+fh_update(struct svc_fh *fhp)
+{
+ struct dentry *dentry;
+ __u32 *datap;
+
+ if (!fhp->fh_dentry)
+ goto out_bad;
+
+ dentry = fhp->fh_dentry;
+ if (!dentry->d_inode)
+ goto out_negative;
+ if (fhp->fh_handle.fh_version != 1) {
+ _fh_update_old(dentry, fhp->fh_export, &fhp->fh_handle);
+ } else {
+ int size;
+ if (fhp->fh_handle.fh_fileid_type != 0)
+ goto out_uptodate;
+ datap = fhp->fh_handle.fh_auth+
+ fhp->fh_handle.fh_size/4 -1;
+ size = (fhp->fh_maxsize - fhp->fh_handle.fh_size)/4;
+ fhp->fh_handle.fh_fileid_type =
+ _fh_update(dentry, fhp->fh_export, datap, &size);
+ fhp->fh_handle.fh_size += size*4;
+ }
+out:
+ return 0;
+
+out_bad:
+ printk(KERN_ERR "fh_update: fh not verified!\n");
+ goto out;
+out_negative:
+ printk(KERN_ERR "fh_update: %s/%s still negative!\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
+ goto out;
+out_uptodate:
+ printk(KERN_ERR "fh_update: %s/%s already up-to-date!\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
+ goto out;
+}
+
+/*
+ * Release a file handle.
+ */
+void
+fh_put(struct svc_fh *fhp)
+{
+ struct dentry * dentry = fhp->fh_dentry;
+ if (dentry) {
+ fh_unlock(fhp);
+ fhp->fh_dentry = NULL;
+ dput(dentry);
+ nfsd_nr_put++;
+ }
+ return;
+}
diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c
new file mode 100644
index 00000000000000..fefab47a8c1baf
--- /dev/null
+++ b/fs/nfsd/nfsproc.c
@@ -0,0 +1,609 @@
+/*
+ * nfsproc2.c Process version 2 NFS requests.
+ * linux/fs/nfsd/nfs2proc.c
+ *
+ * Process version 2 NFS requests.
+ *
+ * Copyright (C) 1995-1997 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/linkage.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/version.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+
+#include <linux/sunrpc/svc.h>
+#include <linux/nfsd/nfsd.h>
+#include <linux/nfsd/cache.h>
+#include <linux/nfsd/xdr.h>
+
+typedef struct svc_rqst svc_rqst;
+typedef struct svc_buf svc_buf;
+
+#define NFSDDBG_FACILITY NFSDDBG_PROC
+
+
+static void
+svcbuf_reserve(struct svc_buf *buf, u32 **ptr, int *len, int nr)
+{
+ *ptr = buf->buf + nr;
+ *len = buf->buflen - buf->len - nr;
+}
+
+static int
+nfsd_proc_null(struct svc_rqst *rqstp, void *argp, void *resp)
+{
+ return nfs_ok;
+}
+
+/*
+ * Get a file's attributes
+ * N.B. After this call resp->fh needs an fh_put
+ */
+static int
+nfsd_proc_getattr(struct svc_rqst *rqstp, struct nfsd_fhandle *argp,
+ struct nfsd_attrstat *resp)
+{
+ dprintk("nfsd: GETATTR %s\n", SVCFH_fmt(&argp->fh));
+
+ fh_copy(&resp->fh, &argp->fh);
+ return fh_verify(rqstp, &resp->fh, 0, MAY_NOP);
+}
+
+/*
+ * Set a file's attributes
+ * N.B. After this call resp->fh needs an fh_put
+ */
+static int
+nfsd_proc_setattr(struct svc_rqst *rqstp, struct nfsd_sattrargs *argp,
+ struct nfsd_attrstat *resp)
+{
+ dprintk("nfsd: SETATTR %s, valid=%x, size=%ld\n",
+ SVCFH_fmt(&argp->fh),
+ argp->attrs.ia_valid, (long) argp->attrs.ia_size);
+
+ fh_copy(&resp->fh, &argp->fh);
+ return nfsd_setattr(rqstp, &resp->fh, &argp->attrs,0, (time_t)0);
+}
+
+/*
+ * Look up a path name component
+ * Note: the dentry in the resp->fh may be negative if the file
+ * doesn't exist yet.
+ * N.B. After this call resp->fh needs an fh_put
+ */
+static int
+nfsd_proc_lookup(struct svc_rqst *rqstp, struct nfsd_diropargs *argp,
+ struct nfsd_diropres *resp)
+{
+ int nfserr;
+
+ dprintk("nfsd: LOOKUP %s %.*s\n",
+ SVCFH_fmt(&argp->fh), argp->len, argp->name);
+
+ fh_init(&resp->fh, NFS_FHSIZE);
+ nfserr = nfsd_lookup(rqstp, &argp->fh, argp->name, argp->len,
+ &resp->fh);
+
+ fh_put(&argp->fh);
+ return nfserr;
+}
+
+/*
+ * Read a symlink.
+ */
+static int
+nfsd_proc_readlink(struct svc_rqst *rqstp, struct nfsd_fhandle *argp,
+ struct nfsd_readlinkres *resp)
+{
+ u32 *path;
+ int dummy, nfserr;
+
+ dprintk("nfsd: READLINK %s\n", SVCFH_fmt(&argp->fh));
+
+ /* Reserve room for status and path length */
+ svcbuf_reserve(&rqstp->rq_resbuf, &path, &dummy, 2);
+
+ /* Read the symlink. */
+ resp->len = NFS_MAXPATHLEN;
+ nfserr = nfsd_readlink(rqstp, &argp->fh, (char *) path, &resp->len);
+
+ fh_put(&argp->fh);
+ return nfserr;
+}
+
+/*
+ * Read a portion of a file.
+ * N.B. After this call resp->fh needs an fh_put
+ */
+static int
+nfsd_proc_read(struct svc_rqst *rqstp, struct nfsd_readargs *argp,
+ struct nfsd_readres *resp)
+{
+ u32 * buffer;
+ int nfserr, avail;
+
+ dprintk("nfsd: READ %s %d bytes at %d\n",
+ SVCFH_fmt(&argp->fh),
+ argp->count, argp->offset);
+
+ /* Obtain buffer pointer for payload. 19 is 1 word for
+ * status, 17 words for fattr, and 1 word for the byte count.
+ */
+ svcbuf_reserve(&rqstp->rq_resbuf, &buffer, &avail, 19);
+
+ if ((avail << 2) < argp->count) {
+ printk(KERN_NOTICE
+ "oversized read request from %08x:%d (%d bytes)\n",
+ ntohl(rqstp->rq_addr.sin_addr.s_addr),
+ ntohs(rqstp->rq_addr.sin_port),
+ argp->count);
+ argp->count = avail << 2;
+ }
+ svc_reserve(rqstp, (19<<2) + argp->count + 4);
+
+ resp->count = argp->count;
+ nfserr = nfsd_read(rqstp, fh_copy(&resp->fh, &argp->fh),
+ argp->offset,
+ (char *) buffer,
+ &resp->count);
+
+ return nfserr;
+}
+
+/*
+ * Write data to a file
+ * N.B. After this call resp->fh needs an fh_put
+ */
+static int
+nfsd_proc_write(struct svc_rqst *rqstp, struct nfsd_writeargs *argp,
+ struct nfsd_attrstat *resp)
+{
+ int nfserr;
+ int stable = 1;
+
+ dprintk("nfsd: WRITE %s %d bytes at %d\n",
+ SVCFH_fmt(&argp->fh),
+ argp->len, argp->offset);
+
+ nfserr = nfsd_write(rqstp, fh_copy(&resp->fh, &argp->fh),
+ argp->offset,
+ argp->data,
+ argp->len,
+ &stable);
+ return nfserr;
+}
+
+/*
+ * CREATE processing is complicated. The keyword here is `overloaded.'
+ * The parent directory is kept locked between the check for existence
+ * and the actual create() call in compliance with VFS protocols.
+ * N.B. After this call _both_ argp->fh and resp->fh need an fh_put
+ */
+static int
+nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
+ struct nfsd_diropres *resp)
+{
+ svc_fh *dirfhp = &argp->fh;
+ svc_fh *newfhp = &resp->fh;
+ struct iattr *attr = &argp->attrs;
+ struct inode *inode;
+ struct dentry *dchild;
+ int nfserr, type, mode;
+ dev_t rdev = NODEV;
+
+ dprintk("nfsd: CREATE %s %.*s\n",
+ SVCFH_fmt(dirfhp), argp->len, argp->name);
+
+ /* First verify the parent file handle */
+ nfserr = fh_verify(rqstp, dirfhp, S_IFDIR, MAY_EXEC);
+ if (nfserr)
+ goto done; /* must fh_put dirfhp even on error */
+
+ /* Check for MAY_WRITE in nfsd_create if necessary */
+
+ nfserr = nfserr_acces;
+ if (!argp->len)
+ goto done;
+ nfserr = nfserr_exist;
+ if (isdotent(argp->name, argp->len))
+ goto done;
+ fh_lock(dirfhp);
+ dchild = lookup_one_len(argp->name, dirfhp->fh_dentry, argp->len);
+ if (IS_ERR(dchild)) {
+ nfserr = nfserrno(PTR_ERR(dchild));
+ goto out_unlock;
+ }
+ fh_init(newfhp, NFS_FHSIZE);
+ nfserr = fh_compose(newfhp, dirfhp->fh_export, dchild, dirfhp);
+ if (!nfserr && !dchild->d_inode)
+ nfserr = nfserr_noent;
+ if (nfserr) {
+ if (nfserr != nfserr_noent)
+ goto out_unlock;
+ /*
+ * If the new file handle wasn't verified, we can't tell
+ * whether the file exists or not. Time to bail ...
+ */
+ nfserr = nfserr_acces;
+ if (!newfhp->fh_dentry) {
+ printk(KERN_WARNING
+ "nfsd_proc_create: file handle not verified\n");
+ goto out_unlock;
+ }
+ }
+
+ inode = newfhp->fh_dentry->d_inode;
+
+ /* Unfudge the mode bits */
+ if (attr->ia_valid & ATTR_MODE) {
+ type = attr->ia_mode & S_IFMT;
+ mode = attr->ia_mode & ~S_IFMT;
+ if (!type) {
+ /* no type, so if target exists, assume same as that,
+ * else assume a file */
+ if (inode) {
+ type = inode->i_mode & S_IFMT;
+ switch(type) {
+ case S_IFCHR:
+ case S_IFBLK:
+ /* reserve rdev for later checking */
+ attr->ia_size = inode->i_rdev;
+ attr->ia_valid |= ATTR_SIZE;
+
+ /* FALLTHROUGH */
+ case S_IFIFO:
+ /* this is probably a permission check..
+ * at least IRIX implements perm checking on
+ * echo thing > device-special-file-or-pipe
+ * by doing a CREATE with type==0
+ */
+ nfserr = nfsd_permission(newfhp->fh_export,
+ newfhp->fh_dentry,
+ MAY_WRITE|_NFSD_IRIX_BOGOSITY);
+ if (nfserr && nfserr != nfserr_rofs)
+ goto out_unlock;
+ }
+ } else
+ type = S_IFREG;
+ }
+ } else if (inode) {
+ type = inode->i_mode & S_IFMT;
+ mode = inode->i_mode & ~S_IFMT;
+ } else {
+ type = S_IFREG;
+ mode = 0; /* ??? */
+ }
+
+ attr->ia_valid |= ATTR_MODE;
+ attr->ia_mode = mode;
+
+ /* Special treatment for non-regular files according to the
+ * gospel of sun micro
+ */
+ if (type != S_IFREG) {
+ int is_borc = 0;
+ u32 size = attr->ia_size;
+
+ rdev = (dev_t) size;
+ if (type != S_IFBLK && type != S_IFCHR) {
+ rdev = 0;
+ } else if (type == S_IFCHR && !(attr->ia_valid & ATTR_SIZE)) {
+ /* If you think you've seen the worst, grok this. */
+ type = S_IFIFO;
+ } else if (size != rdev) {
+ /* dev got truncated because of 16bit Linux dev_t */
+ nfserr = nfserr_inval;
+ goto out_unlock;
+ } else {
+ /* Okay, char or block special */
+ is_borc = 1;
+ }
+
+ /* we've used the SIZE information, so discard it */
+ attr->ia_valid &= ~ATTR_SIZE;
+
+ /* Make sure the type and device matches */
+ nfserr = nfserr_exist;
+ if (inode && (type != (inode->i_mode & S_IFMT) ||
+ (is_borc && inode->i_rdev != rdev)))
+ goto out_unlock;
+ }
+
+ nfserr = 0;
+ if (!inode) {
+ /* File doesn't exist. Create it and set attrs */
+ nfserr = nfsd_create(rqstp, dirfhp, argp->name, argp->len,
+ attr, type, rdev, newfhp);
+ } else if (type == S_IFREG) {
+ dprintk("nfsd: existing %s, valid=%x, size=%ld\n",
+ argp->name, attr->ia_valid, (long) attr->ia_size);
+ /* File already exists. We ignore all attributes except
+ * size, so that creat() behaves exactly like
+ * open(..., O_CREAT|O_TRUNC|O_WRONLY).
+ */
+ attr->ia_valid &= ATTR_SIZE;
+ if (attr->ia_valid)
+ nfserr = nfsd_setattr(rqstp, newfhp, attr, 0, (time_t)0);
+ }
+
+out_unlock:
+ /* We don't really need to unlock, as fh_put does it. */
+ fh_unlock(dirfhp);
+
+done:
+ fh_put(dirfhp);
+ return nfserr;
+}
+
+static int
+nfsd_proc_remove(struct svc_rqst *rqstp, struct nfsd_diropargs *argp,
+ void *resp)
+{
+ int nfserr;
+
+ dprintk("nfsd: REMOVE %s %.*s\n", SVCFH_fmt(&argp->fh),
+ argp->len, argp->name);
+
+ /* Unlink. -SIFDIR means file must not be a directory */
+ nfserr = nfsd_unlink(rqstp, &argp->fh, -S_IFDIR, argp->name, argp->len);
+ fh_put(&argp->fh);
+ return nfserr;
+}
+
+static int
+nfsd_proc_rename(struct svc_rqst *rqstp, struct nfsd_renameargs *argp,
+ void *resp)
+{
+ int nfserr;
+
+ dprintk("nfsd: RENAME %s %.*s -> \n",
+ SVCFH_fmt(&argp->ffh), argp->flen, argp->fname);
+ dprintk("nfsd: -> %s %.*s\n",
+ SVCFH_fmt(&argp->tfh), argp->tlen, argp->tname);
+
+ nfserr = nfsd_rename(rqstp, &argp->ffh, argp->fname, argp->flen,
+ &argp->tfh, argp->tname, argp->tlen);
+ fh_put(&argp->ffh);
+ fh_put(&argp->tfh);
+ return nfserr;
+}
+
+static int
+nfsd_proc_link(struct svc_rqst *rqstp, struct nfsd_linkargs *argp,
+ void *resp)
+{
+ int nfserr;
+
+ dprintk("nfsd: LINK %s ->\n",
+ SVCFH_fmt(&argp->ffh));
+ dprintk("nfsd: %s %.*s\n",
+ SVCFH_fmt(&argp->tfh),
+ argp->tlen,
+ argp->tname);
+
+ nfserr = nfsd_link(rqstp, &argp->tfh, argp->tname, argp->tlen,
+ &argp->ffh);
+ fh_put(&argp->ffh);
+ fh_put(&argp->tfh);
+ return nfserr;
+}
+
+static int
+nfsd_proc_symlink(struct svc_rqst *rqstp, struct nfsd_symlinkargs *argp,
+ void *resp)
+{
+ struct svc_fh newfh;
+ int nfserr;
+
+ dprintk("nfsd: SYMLINK %s %.*s -> %.*s\n",
+ SVCFH_fmt(&argp->ffh), argp->flen, argp->fname,
+ argp->tlen, argp->tname);
+
+ fh_init(&newfh, NFS_FHSIZE);
+ /*
+ * Create the link, look up new file and set attrs.
+ */
+ nfserr = nfsd_symlink(rqstp, &argp->ffh, argp->fname, argp->flen,
+ argp->tname, argp->tlen,
+ &newfh, &argp->attrs);
+
+
+ fh_put(&argp->ffh);
+ fh_put(&newfh);
+ return nfserr;
+}
+
+/*
+ * Make directory. This operation is not idempotent.
+ * N.B. After this call resp->fh needs an fh_put
+ */
+static int
+nfsd_proc_mkdir(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
+ struct nfsd_diropres *resp)
+{
+ int nfserr;
+
+ dprintk("nfsd: MKDIR %s %.*s\n", SVCFH_fmt(&argp->fh), argp->len, argp->name);
+
+ if (resp->fh.fh_dentry) {
+ printk(KERN_WARNING
+ "nfsd_proc_mkdir: response already verified??\n");
+ }
+
+ argp->attrs.ia_valid &= ~ATTR_SIZE;
+ fh_init(&resp->fh, NFS_FHSIZE);
+ nfserr = nfsd_create(rqstp, &argp->fh, argp->name, argp->len,
+ &argp->attrs, S_IFDIR, 0, &resp->fh);
+ fh_put(&argp->fh);
+ return nfserr;
+}
+
+/*
+ * Remove a directory
+ */
+static int
+nfsd_proc_rmdir(struct svc_rqst *rqstp, struct nfsd_diropargs *argp,
+ void *resp)
+{
+ int nfserr;
+
+ dprintk("nfsd: RMDIR %s %.*s\n", SVCFH_fmt(&argp->fh), argp->len, argp->name);
+
+ nfserr = nfsd_unlink(rqstp, &argp->fh, S_IFDIR, argp->name, argp->len);
+ fh_put(&argp->fh);
+ return nfserr;
+}
+
+/*
+ * Read a portion of a directory.
+ */
+static int
+nfsd_proc_readdir(struct svc_rqst *rqstp, struct nfsd_readdirargs *argp,
+ struct nfsd_readdirres *resp)
+{
+ u32 * buffer;
+ int nfserr, count;
+
+ dprintk("nfsd: READDIR %s %d bytes at %d\n",
+ SVCFH_fmt(&argp->fh),
+ argp->count, argp->cookie);
+
+ /* Reserve buffer space for status */
+ svcbuf_reserve(&rqstp->rq_resbuf, &buffer, &count, 1);
+
+ /* Shrink to the client read size */
+ if (count > (argp->count >> 2))
+ count = argp->count >> 2;
+
+ /* Make sure we've room for the NULL ptr & eof flag */
+ count -= 2;
+ if (count < 0)
+ count = 0;
+
+ /* Read directory and encode entries on the fly */
+ nfserr = nfsd_readdir(rqstp, &argp->fh, (loff_t) argp->cookie,
+ nfssvc_encode_entry,
+ buffer, &count, NULL);
+ resp->count = count;
+
+ fh_put(&argp->fh);
+ return nfserr;
+}
+
+/*
+ * Get file system info
+ */
+static int
+nfsd_proc_statfs(struct svc_rqst * rqstp, struct nfsd_fhandle *argp,
+ struct nfsd_statfsres *resp)
+{
+ int nfserr;
+
+ dprintk("nfsd: STATFS %s\n", SVCFH_fmt(&argp->fh));
+
+ nfserr = nfsd_statfs(rqstp, &argp->fh, &resp->stats);
+ fh_put(&argp->fh);
+ return nfserr;
+}
+
+/*
+ * NFSv2 Server procedures.
+ * Only the results of non-idempotent operations are cached.
+ */
+#define nfsd_proc_none NULL
+#define nfssvc_release_none NULL
+struct nfsd_void { int dummy; };
+
+#define PROC(name, argt, rest, relt, cache, respsize) \
+ { (svc_procfunc) nfsd_proc_##name, \
+ (kxdrproc_t) nfssvc_decode_##argt, \
+ (kxdrproc_t) nfssvc_encode_##rest, \
+ (kxdrproc_t) nfssvc_release_##relt, \
+ sizeof(struct nfsd_##argt), \
+ sizeof(struct nfsd_##rest), \
+ 0, \
+ cache, \
+ respsize, \
+ }
+
+#define ST 1 /* status */
+#define FH 8 /* filehandle */
+#define AT 18 /* attributes */
+
+struct svc_procedure nfsd_procedures2[18] = {
+ PROC(null, void, void, none, RC_NOCACHE, ST),
+ PROC(getattr, fhandle, attrstat, fhandle, RC_NOCACHE, ST+AT),
+ PROC(setattr, sattrargs, attrstat, fhandle, RC_REPLBUFF, ST+AT),
+ PROC(none, void, void, none, RC_NOCACHE, ST),
+ PROC(lookup, diropargs, diropres, fhandle, RC_NOCACHE, ST+FH+AT),
+ PROC(readlink, fhandle, readlinkres, none, RC_NOCACHE, ST+1+NFS_MAXPATHLEN/4),
+ PROC(read, readargs, readres, fhandle, RC_NOCACHE, ST+AT+1+NFSSVC_MAXBLKSIZE),
+ PROC(none, void, void, none, RC_NOCACHE, ST),
+ PROC(write, writeargs, attrstat, fhandle, RC_REPLBUFF, ST+AT),
+ PROC(create, createargs, diropres, fhandle, RC_REPLBUFF, ST+FH+AT),
+ PROC(remove, diropargs, void, none, RC_REPLSTAT, ST),
+ PROC(rename, renameargs, void, none, RC_REPLSTAT, ST),
+ PROC(link, linkargs, void, none, RC_REPLSTAT, ST),
+ PROC(symlink, symlinkargs, void, none, RC_REPLSTAT, ST),
+ PROC(mkdir, createargs, diropres, fhandle, RC_REPLBUFF, ST+FH+AT),
+ PROC(rmdir, diropargs, void, none, RC_REPLSTAT, ST),
+ PROC(readdir, readdirargs, readdirres, none, RC_REPLBUFF, 0),
+ PROC(statfs, fhandle, statfsres, none, RC_NOCACHE, ST+5),
+};
+
+
+/*
+ * Map errnos to NFS errnos.
+ */
+int
+nfserrno (int errno)
+{
+ static struct {
+ int nfserr;
+ int syserr;
+ } nfs_errtbl[] = {
+ { nfs_ok, 0 },
+ { nfserr_perm, -EPERM },
+ { nfserr_noent, -ENOENT },
+ { nfserr_io, -EIO },
+ { nfserr_nxio, -ENXIO },
+ { nfserr_acces, -EACCES },
+ { nfserr_exist, -EEXIST },
+ { nfserr_xdev, -EXDEV },
+ { nfserr_mlink, -EMLINK },
+ { nfserr_nodev, -ENODEV },
+ { nfserr_notdir, -ENOTDIR },
+ { nfserr_isdir, -EISDIR },
+ { nfserr_inval, -EINVAL },
+ { nfserr_fbig, -EFBIG },
+ { nfserr_nospc, -ENOSPC },
+ { nfserr_rofs, -EROFS },
+ { nfserr_mlink, -EMLINK },
+ { nfserr_nametoolong, -ENAMETOOLONG },
+ { nfserr_notempty, -ENOTEMPTY },
+#ifdef EDQUOT
+ { nfserr_dquot, -EDQUOT },
+#endif
+ { nfserr_stale, -ESTALE },
+ { nfserr_dropit, -ENOMEM },
+ { -1, -EIO }
+ };
+ int i;
+
+ for (i = 0; nfs_errtbl[i].nfserr != -1; i++) {
+ if (nfs_errtbl[i].syserr == errno)
+ return nfs_errtbl[i].nfserr;
+ }
+ printk (KERN_INFO "nfsd: non-standard errno: %d\n", errno);
+ return nfserr_io;
+}
+
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
new file mode 100644
index 00000000000000..6d48dd27754b66
--- /dev/null
+++ b/fs/nfsd/nfssvc.c
@@ -0,0 +1,342 @@
+/*
+ * linux/fs/nfsd/nfssvc.c
+ *
+ * Central processing for nfsd.
+ *
+ * Authors: Olaf Kirch (okir@monad.swb.de)
+ *
+ * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/nfs.h>
+#include <linux/in.h>
+#include <linux/uio.h>
+#include <linux/version.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+
+#include <linux/sunrpc/types.h>
+#include <linux/sunrpc/stats.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/sunrpc/svcsock.h>
+#include <linux/nfsd/nfsd.h>
+#include <linux/nfsd/stats.h>
+#include <linux/nfsd/cache.h>
+#include <linux/nfsd/xdr.h>
+#include <linux/lockd/bind.h>
+
+#define NFSDDBG_FACILITY NFSDDBG_SVC
+#define NFSD_BUFSIZE (1024 + NFSSVC_MAXBLKSIZE)
+
+/* these signals will be delivered to an nfsd thread
+ * when handling a request
+ */
+#define ALLOWED_SIGS (sigmask(SIGKILL))
+/* these signals will be delivered to an nfsd thread
+ * when not handling a request. i.e. when waiting
+ */
+#define SHUTDOWN_SIGS (sigmask(SIGKILL) | sigmask(SIGHUP) | sigmask(SIGINT) | sigmask(SIGQUIT))
+/* if the last thread dies with SIGHUP, then the exports table is
+ * left unchanged ( like 2.4-{0-9} ). Any other signal will clear
+ * the exports table (like 2.2).
+ */
+#define SIG_NOCLEAN SIGHUP
+
+extern struct svc_program nfsd_program;
+static void nfsd(struct svc_rqst *rqstp);
+struct timeval nfssvc_boot;
+static struct svc_serv *nfsd_serv;
+static int nfsd_busy;
+static unsigned long nfsd_last_call;
+
+struct nfsd_list {
+ struct list_head list;
+ struct task_struct *task;
+};
+struct list_head nfsd_list = LIST_HEAD_INIT(nfsd_list);
+
+/*
+ * Maximum number of nfsd processes
+ */
+#define NFSD_MAXSERVS 8192
+
+int
+nfsd_svc(unsigned short port, int nrservs)
+{
+ int error;
+ int none_left;
+ struct list_head *victim;
+
+ dprintk("nfsd: creating service\n");
+ error = -EINVAL;
+ if (nrservs <= 0)
+ nrservs = 0;
+ if (nrservs > NFSD_MAXSERVS)
+ nrservs = NFSD_MAXSERVS;
+
+ /* Readahead param cache - will no-op if it already exists */
+ error = nfsd_racache_init(2*nrservs);
+ if (error<0)
+ goto out;
+ if (!nfsd_serv) {
+ error = -ENOMEM;
+ nfsd_serv = svc_create(&nfsd_program, NFSD_BUFSIZE, NFSSVC_XDRSIZE);
+ if (nfsd_serv == NULL)
+ goto out;
+ error = svc_makesock(nfsd_serv, IPPROTO_UDP, port);
+ if (error < 0)
+ goto failure;
+
+#if CONFIG_NFSD_TCP
+ error = svc_makesock(nfsd_serv, IPPROTO_TCP, port);
+ if (error < 0)
+ goto failure;
+#endif
+ do_gettimeofday(&nfssvc_boot); /* record boot time */
+ } else
+ nfsd_serv->sv_nrthreads++;
+ nrservs -= (nfsd_serv->sv_nrthreads-1);
+ while (nrservs > 0) {
+ nrservs--;
+ error = svc_create_thread(nfsd, nfsd_serv);
+ if (error < 0)
+ break;
+ }
+ victim = nfsd_list.next;
+ while (nrservs < 0 && victim != &nfsd_list) {
+ struct nfsd_list *nl =
+ list_entry(victim,struct nfsd_list, list);
+ victim = victim->next;
+ send_sig(SIG_NOCLEAN, nl->task, 1);
+ nrservs++;
+ }
+ failure:
+ none_left = (nfsd_serv->sv_nrthreads == 1);
+ svc_destroy(nfsd_serv); /* Release server */
+ if (none_left) {
+ nfsd_serv = NULL;
+ nfsd_racache_shutdown();
+ }
+ out:
+ return error;
+}
+
+static inline void
+update_thread_usage(int busy_threads)
+{
+ unsigned long prev_call;
+ unsigned long diff;
+ int decile;
+
+ prev_call = nfsd_last_call;
+ nfsd_last_call = jiffies;
+ decile = busy_threads*10/nfsdstats.th_cnt;
+ if (decile>0 && decile <= 10) {
+ diff = nfsd_last_call - prev_call;
+ if ( (nfsdstats.th_usage[decile-1] += diff) >= NFSD_USAGE_WRAP)
+ nfsdstats.th_usage[decile-1] -= NFSD_USAGE_WRAP;
+ if (decile == 10)
+ nfsdstats.th_fullcnt++;
+ }
+}
+
+/*
+ * This is the NFS server kernel thread
+ */
+static void
+nfsd(struct svc_rqst *rqstp)
+{
+ struct svc_serv *serv = rqstp->rq_server;
+ int err;
+ struct nfsd_list me;
+
+ /* Lock module and set up kernel thread */
+ MOD_INC_USE_COUNT;
+ lock_kernel();
+ daemonize();
+ sprintf(current->comm, "nfsd");
+ current->rlim[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY;
+
+ nfsdstats.th_cnt++;
+ /* Let svc_process check client's authentication. */
+ rqstp->rq_auth = 1;
+
+ lockd_up(); /* start lockd */
+
+ me.task = current;
+ list_add(&me.list, &nfsd_list);
+
+ /*
+ * The main request loop
+ */
+ for (;;) {
+ /* Block all but the shutdown signals */
+ spin_lock_irq(&current->sigmask_lock);
+ siginitsetinv(&current->blocked, SHUTDOWN_SIGS);
+ recalc_sigpending(current);
+ spin_unlock_irq(&current->sigmask_lock);
+
+ /*
+ * Find a socket with data available and call its
+ * recvfrom routine.
+ */
+ while ((err = svc_recv(serv, rqstp,
+ 5*60*HZ)) == -EAGAIN)
+ ;
+ if (err < 0)
+ break;
+ update_thread_usage(nfsd_busy);
+ nfsd_busy++;
+
+ /* Lock the export hash tables for reading. */
+ exp_readlock();
+
+ /* Validate the client's address. This will also defeat
+ * port probes on port 2049 by unauthorized clients.
+ */
+ rqstp->rq_client = exp_getclient(&rqstp->rq_addr);
+ /* Process request with signals blocked. */
+ spin_lock_irq(&current->sigmask_lock);
+ siginitsetinv(&current->blocked, ALLOWED_SIGS);
+ recalc_sigpending(current);
+ spin_unlock_irq(&current->sigmask_lock);
+
+ svc_process(serv, rqstp);
+
+ /* Unlock export hash tables */
+ exp_unlock();
+ update_thread_usage(nfsd_busy);
+ nfsd_busy--;
+ }
+
+ if (err != -EINTR) {
+ printk(KERN_WARNING "nfsd: terminating on error %d\n", -err);
+ } else {
+ unsigned int signo;
+
+ for (signo = 1; signo <= _NSIG; signo++)
+ if (sigismember(&current->pending.signal, signo) &&
+ !sigismember(&current->blocked, signo))
+ break;
+ err = signo;
+ }
+
+ /* Release lockd */
+ lockd_down();
+
+ /* Check if this is last thread */
+ if (serv->sv_nrthreads==1) {
+
+ printk(KERN_WARNING "nfsd: last server has exited\n");
+ if (err != SIG_NOCLEAN) {
+ printk(KERN_WARNING "nfsd: unexporting all filesystems\n");
+ nfsd_export_shutdown();
+ }
+ nfsd_serv = NULL;
+ nfsd_racache_shutdown(); /* release read-ahead cache */
+ }
+ list_del(&me.list);
+ nfsdstats.th_cnt --;
+
+ /* Release the thread */
+ svc_exit_thread(rqstp);
+
+ /* Release module */
+ MOD_DEC_USE_COUNT;
+}
+
+static int
+nfsd_dispatch(struct svc_rqst *rqstp, u32 *statp)
+{
+ struct svc_procedure *proc;
+ kxdrproc_t xdr;
+ u32 nfserr;
+
+ dprintk("nfsd_dispatch: vers %d proc %d\n",
+ rqstp->rq_vers, rqstp->rq_proc);
+ proc = rqstp->rq_procinfo;
+
+ /* Check whether we have this call in the cache. */
+ switch (nfsd_cache_lookup(rqstp, proc->pc_cachetype)) {
+ case RC_INTR:
+ case RC_DROPIT:
+ return 0;
+ case RC_REPLY:
+ return 1;
+ case RC_DOIT:;
+ /* do it */
+ }
+
+ /* Decode arguments */
+ xdr = proc->pc_decode;
+ if (xdr && !xdr(rqstp, rqstp->rq_argbuf.buf, rqstp->rq_argp)) {
+ dprintk("nfsd: failed to decode arguments!\n");
+ nfsd_cache_update(rqstp, RC_NOCACHE, NULL);
+ *statp = rpc_garbage_args;
+ return 1;
+ }
+
+ /* Now call the procedure handler, and encode NFS status. */
+ nfserr = proc->pc_func(rqstp, rqstp->rq_argp, rqstp->rq_resp);
+ if (nfserr == nfserr_dropit) {
+ dprintk("nfsd: Dropping request due to malloc failure!\n");
+ nfsd_cache_update(rqstp, RC_NOCACHE, NULL);
+ return 0;
+ }
+
+ if (rqstp->rq_proc != 0)
+ svc_putlong(&rqstp->rq_resbuf, nfserr);
+
+ /* Encode result.
+ * For NFSv2, additional info is never returned in case of an error.
+ */
+ if (!(nfserr && rqstp->rq_vers == 2)) {
+ xdr = proc->pc_encode;
+ if (xdr && !xdr(rqstp, rqstp->rq_resbuf.buf, rqstp->rq_resp)) {
+ /* Failed to encode result. Release cache entry */
+ dprintk("nfsd: failed to encode result!\n");
+ nfsd_cache_update(rqstp, RC_NOCACHE, NULL);
+ *statp = rpc_system_err;
+ return 1;
+ }
+ }
+
+ /* Store reply in cache. */
+ nfsd_cache_update(rqstp, proc->pc_cachetype, statp + 1);
+ return 1;
+}
+
+static struct svc_version nfsd_version2 = {
+ 2, 18, nfsd_procedures2, nfsd_dispatch
+};
+#ifdef CONFIG_NFSD_V3
+static struct svc_version nfsd_version3 = {
+ 3, 22, nfsd_procedures3, nfsd_dispatch
+};
+#endif
+static struct svc_version * nfsd_version[] = {
+ NULL,
+ NULL,
+ &nfsd_version2,
+#ifdef CONFIG_NFSD_V3
+ &nfsd_version3,
+#endif
+};
+
+#define NFSD_NRVERS (sizeof(nfsd_version)/sizeof(nfsd_version[0]))
+struct svc_program nfsd_program = {
+ NFS_PROGRAM, /* program number */
+ 2, NFSD_NRVERS-1, /* version range */
+ NFSD_NRVERS, /* nr of entries in nfsd_version */
+ nfsd_version, /* version table */
+ "nfsd", /* program name */
+ &nfsd_svcstats, /* version table */
+};
diff --git a/fs/nfsd/nfsxdr.c b/fs/nfsd/nfsxdr.c
new file mode 100644
index 00000000000000..b5e9a2aee8fe69
--- /dev/null
+++ b/fs/nfsd/nfsxdr.c
@@ -0,0 +1,451 @@
+/*
+ * linux/fs/nfsd/xdr.c
+ *
+ * XDR support for nfsd
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/nfs.h>
+
+#include <linux/sunrpc/xdr.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/nfsd/nfsd.h>
+#include <linux/nfsd/xdr.h>
+
+#define NFSDDBG_FACILITY NFSDDBG_XDR
+
+
+#ifdef NFSD_OPTIMIZE_SPACE
+# define inline
+#endif
+
+/*
+ * Mapping of S_IF* types to NFS file types
+ */
+static u32 nfs_ftypes[] = {
+ NFNON, NFCHR, NFCHR, NFBAD,
+ NFDIR, NFBAD, NFBLK, NFBAD,
+ NFREG, NFBAD, NFLNK, NFBAD,
+ NFSOCK, NFBAD, NFLNK, NFBAD,
+};
+
+
+/*
+ * XDR functions for basic NFS types
+ */
+static inline u32 *
+decode_fh(u32 *p, struct svc_fh *fhp)
+{
+ fh_init(fhp, NFS_FHSIZE);
+ memcpy(&fhp->fh_handle.fh_base, p, NFS_FHSIZE);
+ fhp->fh_handle.fh_size = NFS_FHSIZE;
+
+ /* FIXME: Look up export pointer here and verify
+ * Sun Secure RPC if requested */
+ return p + (NFS_FHSIZE >> 2);
+}
+
+static inline u32 *
+encode_fh(u32 *p, struct svc_fh *fhp)
+{
+ memcpy(p, &fhp->fh_handle.fh_base, NFS_FHSIZE);
+ return p + (NFS_FHSIZE>> 2);
+}
+
+/*
+ * Decode a file name and make sure that the path contains
+ * no slashes or null bytes.
+ */
+static inline u32 *
+decode_filename(u32 *p, char **namp, int *lenp)
+{
+ char *name;
+ int i;
+
+ if ((p = xdr_decode_string_inplace(p, namp, lenp, NFS_MAXNAMLEN)) != NULL) {
+ for (i = 0, name = *namp; i < *lenp; i++, name++) {
+ if (*name == '\0' || *name == '/')
+ return NULL;
+ }
+ }
+
+ return p;
+}
+
+static inline u32 *
+decode_pathname(u32 *p, char **namp, int *lenp)
+{
+ char *name;
+ int i;
+
+ if ((p = xdr_decode_string(p, namp, lenp, NFS_MAXPATHLEN)) != NULL) {
+ for (i = 0, name = *namp; i < *lenp; i++, name++) {
+ if (*name == '\0')
+ return NULL;
+ }
+ }
+
+ return p;
+}
+
+static inline u32 *
+decode_sattr(u32 *p, struct iattr *iap)
+{
+ u32 tmp, tmp1;
+
+ iap->ia_valid = 0;
+
+ /* Sun client bug compatibility check: some sun clients seem to
+ * put 0xffff in the mode field when they mean 0xffffffff.
+ * Quoting the 4.4BSD nfs server code: Nah nah nah nah na nah.
+ */
+ if ((tmp = ntohl(*p++)) != (u32)-1 && tmp != 0xffff) {
+ iap->ia_valid |= ATTR_MODE;
+ iap->ia_mode = tmp;
+ }
+ if ((tmp = ntohl(*p++)) != (u32)-1) {
+ iap->ia_valid |= ATTR_UID;
+ iap->ia_uid = tmp;
+ }
+ if ((tmp = ntohl(*p++)) != (u32)-1) {
+ iap->ia_valid |= ATTR_GID;
+ iap->ia_gid = tmp;
+ }
+ if ((tmp = ntohl(*p++)) != (u32)-1) {
+ iap->ia_valid |= ATTR_SIZE;
+ iap->ia_size = tmp;
+ }
+ tmp = ntohl(*p++); tmp1 = ntohl(*p++);
+ if (tmp != (u32)-1 && tmp1 != (u32)-1) {
+ iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET;
+ iap->ia_atime = tmp;
+ }
+ tmp = ntohl(*p++); tmp1 = ntohl(*p++);
+ if (tmp != (u32)-1 && tmp1 != (u32)-1) {
+ iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET;
+ iap->ia_mtime = tmp;
+ /*
+ * Passing the invalid value useconds=1000000 for mtime
+ * is a Sun convention for "set both mtime and atime to
+ * current server time". It's needed to make permissions
+ * checks for the "touch" program across v2 mounts to
+ * Solaris and Irix boxes work correctly. See description of
+ * sattr in section 6.1 of "NFS Illustrated" by
+ * Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5
+ */
+ if (tmp1 == 1000000)
+ iap->ia_valid &= ~(ATTR_ATIME_SET|ATTR_MTIME_SET);
+ }
+ return p;
+}
+
+static inline u32 *
+encode_fattr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
+{
+ struct inode *inode = fhp->fh_dentry->d_inode;
+ int type = (inode->i_mode & S_IFMT);
+
+ *p++ = htonl(nfs_ftypes[type >> 12]);
+ *p++ = htonl((u32) inode->i_mode);
+ *p++ = htonl((u32) inode->i_nlink);
+ *p++ = htonl((u32) nfsd_ruid(rqstp, inode->i_uid));
+ *p++ = htonl((u32) nfsd_rgid(rqstp, inode->i_gid));
+
+ if (S_ISLNK(type) && inode->i_size > NFS_MAXPATHLEN) {
+ *p++ = htonl(NFS_MAXPATHLEN);
+ } else {
+ *p++ = htonl((u32) inode->i_size);
+ }
+ *p++ = htonl((u32) inode->i_blksize);
+ if (S_ISCHR(type) || S_ISBLK(type))
+ *p++ = htonl((u32) inode->i_rdev);
+ else
+ *p++ = htonl(0xffffffff);
+ *p++ = htonl((u32) inode->i_blocks);
+ if (rqstp->rq_reffh->fh_version == 1
+ && rqstp->rq_reffh->fh_fsid_type == 1
+ && (fhp->fh_export->ex_flags & NFSEXP_FSID))
+ *p++ = htonl((u32) fhp->fh_export->ex_fsid);
+ else
+ *p++ = htonl((u32) inode->i_dev);
+ *p++ = htonl((u32) inode->i_ino);
+ *p++ = htonl((u32) inode->i_atime);
+ *p++ = 0;
+ *p++ = htonl((u32) lease_get_mtime(inode));
+ *p++ = 0;
+ *p++ = htonl((u32) inode->i_ctime);
+ *p++ = 0;
+
+ return p;
+}
+
+/*
+ * Check buffer bounds after decoding arguments
+ */
+static inline int
+xdr_argsize_check(struct svc_rqst *rqstp, u32 *p)
+{
+ struct svc_buf *buf = &rqstp->rq_argbuf;
+
+ return p - buf->base <= buf->buflen;
+}
+
+static inline int
+xdr_ressize_check(struct svc_rqst *rqstp, u32 *p)
+{
+ struct svc_buf *buf = &rqstp->rq_resbuf;
+
+ buf->len = p - buf->base;
+ dprintk("nfsd: ressize_check p %p base %p len %d\n",
+ p, buf->base, buf->buflen);
+ return (buf->len <= buf->buflen);
+}
+
+/*
+ * XDR decode functions
+ */
+int
+nfssvc_decode_void(struct svc_rqst *rqstp, u32 *p, void *dummy)
+{
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfssvc_decode_fhandle(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
+{
+ if (!(p = decode_fh(p, fhp)))
+ return 0;
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfssvc_decode_sattrargs(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd_sattrargs *args)
+{
+ if (!(p = decode_fh(p, &args->fh))
+ || !(p = decode_sattr(p, &args->attrs)))
+ return 0;
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfssvc_decode_diropargs(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd_diropargs *args)
+{
+ if (!(p = decode_fh(p, &args->fh))
+ || !(p = decode_filename(p, &args->name, &args->len)))
+ return 0;
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfssvc_decode_readargs(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd_readargs *args)
+{
+ if (!(p = decode_fh(p, &args->fh)))
+ return 0;
+
+ args->offset = ntohl(*p++);
+ args->count = ntohl(*p++);
+ args->totalsize = ntohl(*p++);
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfssvc_decode_writeargs(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd_writeargs *args)
+{
+ if (!(p = decode_fh(p, &args->fh)))
+ return 0;
+
+ p++; /* beginoffset */
+ args->offset = ntohl(*p++); /* offset */
+ p++; /* totalcount */
+ args->len = ntohl(*p++);
+ args->data = (char *) p;
+ p += XDR_QUADLEN(args->len);
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfssvc_decode_createargs(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd_createargs *args)
+{
+ if (!(p = decode_fh(p, &args->fh))
+ || !(p = decode_filename(p, &args->name, &args->len))
+ || !(p = decode_sattr(p, &args->attrs)))
+ return 0;
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfssvc_decode_renameargs(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd_renameargs *args)
+{
+ if (!(p = decode_fh(p, &args->ffh))
+ || !(p = decode_filename(p, &args->fname, &args->flen))
+ || !(p = decode_fh(p, &args->tfh))
+ || !(p = decode_filename(p, &args->tname, &args->tlen)))
+ return 0;
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfssvc_decode_linkargs(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd_linkargs *args)
+{
+ if (!(p = decode_fh(p, &args->ffh))
+ || !(p = decode_fh(p, &args->tfh))
+ || !(p = decode_filename(p, &args->tname, &args->tlen)))
+ return 0;
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfssvc_decode_symlinkargs(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd_symlinkargs *args)
+{
+ if (!(p = decode_fh(p, &args->ffh))
+ || !(p = decode_filename(p, &args->fname, &args->flen))
+ || !(p = decode_pathname(p, &args->tname, &args->tlen))
+ || !(p = decode_sattr(p, &args->attrs)))
+ return 0;
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+int
+nfssvc_decode_readdirargs(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd_readdirargs *args)
+{
+ if (!(p = decode_fh(p, &args->fh)))
+ return 0;
+ args->cookie = ntohl(*p++);
+ args->count = ntohl(*p++);
+
+ return xdr_argsize_check(rqstp, p);
+}
+
+/*
+ * XDR encode functions
+ */
+int
+nfssvc_encode_void(struct svc_rqst *rqstp, u32 *p, void *dummy)
+{
+ return xdr_ressize_check(rqstp, p);
+}
+
+int
+nfssvc_encode_attrstat(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd_attrstat *resp)
+{
+ p = encode_fattr(rqstp, p, &resp->fh);
+ return xdr_ressize_check(rqstp, p);
+}
+
+int
+nfssvc_encode_diropres(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd_diropres *resp)
+{
+ p = encode_fh(p, &resp->fh);
+ p = encode_fattr(rqstp, p, &resp->fh);
+ return xdr_ressize_check(rqstp, p);
+}
+
+int
+nfssvc_encode_readlinkres(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd_readlinkres *resp)
+{
+ *p++ = htonl(resp->len);
+ p += XDR_QUADLEN(resp->len);
+ return xdr_ressize_check(rqstp, p);
+}
+
+int
+nfssvc_encode_readres(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd_readres *resp)
+{
+ p = encode_fattr(rqstp, p, &resp->fh);
+ *p++ = htonl(resp->count);
+ p += XDR_QUADLEN(resp->count);
+
+ return xdr_ressize_check(rqstp, p);
+}
+
+int
+nfssvc_encode_readdirres(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd_readdirres *resp)
+{
+ p += XDR_QUADLEN(resp->count);
+ return xdr_ressize_check(rqstp, p);
+}
+
+int
+nfssvc_encode_statfsres(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd_statfsres *resp)
+{
+ struct statfs *stat = &resp->stats;
+
+ *p++ = htonl(NFSSVC_MAXBLKSIZE); /* max transfer size */
+ *p++ = htonl(stat->f_bsize);
+ *p++ = htonl(stat->f_blocks);
+ *p++ = htonl(stat->f_bfree);
+ *p++ = htonl(stat->f_bavail);
+ return xdr_ressize_check(rqstp, p);
+}
+
+int
+nfssvc_encode_entry(struct readdir_cd *cd, const char *name,
+ int namlen, loff_t offset, ino_t ino, unsigned int d_type)
+{
+ u32 *p = cd->buffer;
+ int buflen, slen;
+
+ /*
+ dprintk("nfsd: entry(%.*s off %ld ino %ld)\n",
+ namlen, name, offset, ino);
+ */
+
+ if (offset > ~((u32) 0))
+ return -EINVAL;
+ if (cd->offset)
+ *cd->offset = htonl(offset);
+ if (namlen > NFS2_MAXNAMLEN)
+ namlen = NFS2_MAXNAMLEN;/* truncate filename */
+
+ slen = XDR_QUADLEN(namlen);
+ if ((buflen = cd->buflen - slen - 4) < 0) {
+ cd->eob = 1;
+ return -EINVAL;
+ }
+ *p++ = xdr_one; /* mark entry present */
+ *p++ = htonl((u32) ino); /* file id */
+ p = xdr_encode_array(p, name, namlen);/* name length & name */
+ cd->offset = p; /* remember pointer */
+ *p++ = ~(u32) 0; /* offset of next entry */
+
+ cd->buflen = buflen;
+ cd->buffer = p;
+ return 0;
+}
+
+/*
+ * XDR release functions
+ */
+int
+nfssvc_release_fhandle(struct svc_rqst *rqstp, u32 *p,
+ struct nfsd_fhandle *resp)
+{
+ fh_put(&resp->fh);
+ return 1;
+}
diff --git a/fs/nfsd/stats.c b/fs/nfsd/stats.c
new file mode 100644
index 00000000000000..2bcffc4ee81102
--- /dev/null
+++ b/fs/nfsd/stats.c
@@ -0,0 +1,117 @@
+/*
+ * linux/fs/nfsd/stats.c
+ *
+ * procfs-based user access to knfsd statistics
+ *
+ * /proc/net/rpc/nfsd
+ *
+ * Format:
+ * rc <hits> <misses> <nocache>
+ * Statistsics for the reply cache
+ * fh <stale> <total-lookups> <anonlookups> <dir-not-in-dcache> <nondir-not-in-dcache>
+ * statistics for filehandle lookup
+ * io <bytes-read> <bytes-writtten>
+ * statistics for IO throughput
+ * th <threads> <fullcnt> <10%-20%> <20%-30%> ... <90%-100%> <100%>
+ * time (seconds) when nfsd thread usage above thresholds
+ * and number of times that all threads were in use
+ * ra cache-size <10% <20% <30% ... <100% not-found
+ * number of times that read-ahead entry was found that deep in
+ * the cache.
+ * plus generic RPC stats (see net/sunrpc/stats.c)
+ *
+ * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#define __NO_VERSION__
+#include <linux/module.h>
+
+#include <linux/sunrpc/svc.h>
+#include <linux/sunrpc/stats.h>
+#include <linux/nfsd/nfsd.h>
+#include <linux/nfsd/stats.h>
+
+struct nfsd_stats nfsdstats;
+struct svc_stat nfsd_svcstats = { &nfsd_program, };
+
+static int
+nfsd_proc_read(char *buffer, char **start, off_t offset, int count,
+ int *eof, void *data)
+{
+ int len;
+ int i;
+
+ len = sprintf(buffer, "rc %u %u %u\nfh %u %u %u %u %u\nio %u %u\n",
+ nfsdstats.rchits,
+ nfsdstats.rcmisses,
+ nfsdstats.rcnocache,
+ nfsdstats.fh_stale,
+ nfsdstats.fh_lookup,
+ nfsdstats.fh_anon,
+ nfsdstats.fh_nocache_dir,
+ nfsdstats.fh_nocache_nondir,
+ nfsdstats.io_read,
+ nfsdstats.io_write);
+ /* thread usage: */
+ len += sprintf(buffer+len, "th %u %u", nfsdstats.th_cnt, nfsdstats.th_fullcnt);
+ for (i=0; i<10; i++) {
+ unsigned int jifs = nfsdstats.th_usage[i];
+ unsigned int sec = jifs / HZ, msec = (jifs % HZ)*1000/HZ;
+ len += sprintf(buffer+len, " %u.%03u", sec, msec);
+ }
+
+ /* newline and ra-cache */
+ len += sprintf(buffer+len, "\nra %u", nfsdstats.ra_size);
+ for (i=0; i<11; i++)
+ len += sprintf(buffer+len, " %u", nfsdstats.ra_depth[i]);
+ len += sprintf(buffer+len, "\n");
+
+
+ /* Assume we haven't hit EOF yet. Will be set by svc_proc_read. */
+ *eof = 0;
+
+ /*
+ * Append generic nfsd RPC statistics if there's room for it.
+ */
+ if (len <= offset) {
+ len = svc_proc_read(buffer, start, offset - len, count,
+ eof, data);
+ return len;
+ }
+
+ if (len < count) {
+ len += svc_proc_read(buffer + len, start, 0, count - len,
+ eof, data);
+ }
+
+ if (offset >= len) {
+ *start = buffer;
+ return 0;
+ }
+
+ *start = buffer + offset;
+ if ((len -= offset) > count)
+ return count;
+ return len;
+}
+
+void
+nfsd_stat_init(void)
+{
+ struct proc_dir_entry *ent;
+
+ if ((ent = svc_proc_register(&nfsd_svcstats)) != 0) {
+ ent->read_proc = nfsd_proc_read;
+ ent->owner = THIS_MODULE;
+ }
+}
+
+void
+nfsd_stat_shutdown(void)
+{
+ svc_proc_unregister("nfsd");
+}
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
new file mode 100644
index 00000000000000..59d4b91e42eaf9
--- /dev/null
+++ b/fs/nfsd/vfs.c
@@ -0,0 +1,1615 @@
+#define MSNFS /* HACK HACK */
+/*
+ * linux/fs/nfsd/vfs.c
+ *
+ * File operations used by nfsd. Some of these have been ripped from
+ * other parts of the kernel because they weren't in ksyms.c, others
+ * are partial duplicates with added or changed functionality.
+ *
+ * Note that several functions dget() the dentry upon which they want
+ * to act, most notably those that create directory entries. Response
+ * dentry's are dput()'d if necessary in the release callback.
+ * So if you notice code paths that apparently fail to dput() the
+ * dentry, don't worry--they have been taken care of.
+ *
+ * Copyright (C) 1995-1999 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <linux/ext2_fs.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/net.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/in.h>
+#define __NO_VERSION__
+#include <linux/module.h>
+
+#include <linux/sunrpc/svc.h>
+#include <linux/nfsd/nfsd.h>
+#ifdef CONFIG_NFSD_V3
+#include <linux/nfs3.h>
+#include <linux/nfsd/xdr3.h>
+#endif /* CONFIG_NFSD_V3 */
+#include <linux/nfsd/nfsfh.h>
+#include <linux/quotaops.h>
+
+#include <asm/uaccess.h>
+
+#define NFSDDBG_FACILITY NFSDDBG_FILEOP
+#define NFSD_PARANOIA
+
+
+/* We must ignore files (but only files) which might have mandatory
+ * locks on them because there is no way to know if the accesser has
+ * the lock.
+ */
+#define IS_ISMNDLK(i) (S_ISREG((i)->i_mode) && MANDATORY_LOCK(i))
+
+/*
+ * This is a cache of readahead params that help us choose the proper
+ * readahead strategy. Initially, we set all readahead parameters to 0
+ * and let the VFS handle things.
+ * If you increase the number of cached files very much, you'll need to
+ * add a hash table here.
+ */
+struct raparms {
+ struct raparms *p_next;
+ unsigned int p_count;
+ ino_t p_ino;
+ dev_t p_dev;
+ unsigned long p_reada,
+ p_ramax,
+ p_raend,
+ p_ralen,
+ p_rawin;
+};
+
+static struct raparms * raparml;
+static struct raparms * raparm_cache;
+
+/*
+ * Look up one component of a pathname.
+ * N.B. After this call _both_ fhp and resfh need an fh_put
+ *
+ * If the lookup would cross a mountpoint, and the mounted filesystem
+ * is exported to the client with NFSEXP_NOHIDE, then the lookup is
+ * accepted as it stands and the mounted directory is
+ * returned. Otherwise the covered directory is returned.
+ * NOTE: this mountpoint crossing is not supported properly by all
+ * clients and is explicitly disallowed for NFSv3
+ * NeilBrown <neilb@cse.unsw.edu.au>
+ */
+int
+nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
+ int len, struct svc_fh *resfh)
+{
+ struct svc_export *exp;
+ struct dentry *dparent;
+ struct dentry *dentry;
+ int err;
+
+ dprintk("nfsd: nfsd_lookup(fh %s, %.*s)\n", SVCFH_fmt(fhp), len,name);
+
+ /* Obtain dentry and export. */
+ err = fh_verify(rqstp, fhp, S_IFDIR, MAY_EXEC);
+ if (err)
+ goto out;
+
+ dparent = fhp->fh_dentry;
+ exp = fhp->fh_export;
+
+ err = nfserr_acces;
+
+ /* Lookup the name, but don't follow links */
+ if (isdotent(name, len)) {
+ if (len==1)
+ dentry = dget(dparent);
+ else if (dparent != exp->ex_dentry)
+ dentry = dget(dparent->d_parent);
+ else if (!EX_NOHIDE(exp))
+ dentry = dget(dparent); /* .. == . just like at / */
+ else {
+ /* checking mountpoint crossing is very different when stepping up */
+ struct svc_export *exp2 = NULL;
+ struct dentry *dp;
+ struct vfsmount *mnt = mntget(exp->ex_mnt);
+ dentry = dget(dparent);
+ while(follow_up(&mnt, &dentry))
+ ;
+ dp = dget(dentry->d_parent);
+ dput(dentry);
+ dentry = dp;
+ for ( ; exp2 == NULL && dp->d_parent != dp;
+ dp=dp->d_parent)
+ exp2 = exp_get(exp->ex_client, dp->d_inode->i_dev, dp->d_inode->i_ino);
+ if (exp2==NULL) {
+ dput(dentry);
+ dentry = dget(dparent);
+ } else {
+ exp = exp2;
+ }
+ mntput(mnt);
+ }
+ } else {
+ fh_lock(fhp);
+ dentry = lookup_one_len(name, dparent, len);
+ err = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
+ goto out_nfserr;
+ /*
+ * check if we have crossed a mount point ...
+ */
+ if (d_mountpoint(dentry)) {
+ struct svc_export *exp2 = NULL;
+ struct vfsmount *mnt = mntget(exp->ex_mnt);
+ struct dentry *mounts = dget(dentry);
+ while (follow_down(&mnt,&mounts)&&d_mountpoint(mounts))
+ ;
+ exp2 = exp_get(rqstp->rq_client,
+ mounts->d_inode->i_dev,
+ mounts->d_inode->i_ino);
+ if (exp2 && EX_NOHIDE(exp2)) {
+ /* successfully crossed mount point */
+ exp = exp2;
+ dput(dentry);
+ dentry = mounts;
+ } else
+ dput(mounts);
+ mntput(mnt);
+ }
+ }
+
+ if (dentry->d_inode && dentry->d_inode->i_op &&
+ dentry->d_inode->i_op->revalidate &&
+ dentry->d_inode->i_op->revalidate(dentry))
+ err = nfserr_noent;
+ else
+ err = fh_compose(resfh, exp, dentry, fhp);
+ if (!err && !dentry->d_inode)
+ err = nfserr_noent;
+out:
+ return err;
+
+out_nfserr:
+ err = nfserrno(err);
+ goto out;
+}
+
+/*
+ * Set various file attributes.
+ * N.B. After this call fhp needs an fh_put
+ */
+int
+nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
+ int check_guard, time_t guardtime)
+{
+ struct dentry *dentry;
+ struct inode *inode;
+ int accmode = MAY_SATTR;
+ int ftype = 0;
+ int imode;
+ int err;
+ int size_change = 0;
+
+ if (iap->ia_valid & (ATTR_ATIME | ATTR_MTIME | ATTR_SIZE))
+ accmode |= MAY_WRITE|MAY_OWNER_OVERRIDE;
+ if (iap->ia_valid & ATTR_SIZE)
+ ftype = S_IFREG;
+
+ /* Get inode */
+ err = fh_verify(rqstp, fhp, ftype, accmode);
+ if (err || !iap->ia_valid)
+ goto out;
+
+ dentry = fhp->fh_dentry;
+ inode = dentry->d_inode;
+
+ /* NFSv2 does not differentiate between "set-[ac]time-to-now"
+ * which only requires access, and "set-[ac]time-to-X" which
+ * requires ownership.
+ * So if it looks like it might be "set both to the same time which
+ * is close to now", and if inode_change_ok fails, then we
+ * convert to "set to now" instead of "set to explicit time"
+ *
+ * We only call inode_change_ok as the last test as technically
+ * it is not an interface that we should be using. It is only
+ * valid if the filesystem does not define it's own i_op->setattr.
+ */
+#define BOTH_TIME_SET (ATTR_ATIME_SET | ATTR_MTIME_SET)
+#define MAX_TOUCH_TIME_ERROR (30*60)
+ if ((iap->ia_valid & BOTH_TIME_SET) == BOTH_TIME_SET
+ && iap->ia_mtime == iap->ia_atime
+ ) {
+ /* Looks probable. Now just make sure time is in the right ballpark.
+ * Solaris, at least, doesn't seem to care what the time request is.
+ * We require it be within 30 minutes of now.
+ */
+ time_t delta = iap->ia_atime - CURRENT_TIME;
+ if (delta<0) delta = -delta;
+ if (delta < MAX_TOUCH_TIME_ERROR &&
+ inode_change_ok(inode, iap) != 0) {
+ /* turn off ATTR_[AM]TIME_SET but leave ATTR_[AM]TIME
+ * this will cause notify_change to set these times to "now"
+ */
+ iap->ia_valid &= ~BOTH_TIME_SET;
+ }
+ }
+
+ /* The size case is special. It changes the file as well as the attributes. */
+ if (iap->ia_valid & ATTR_SIZE) {
+ if (iap->ia_size < inode->i_size) {
+ err = nfsd_permission(fhp->fh_export, dentry, MAY_TRUNC|MAY_OWNER_OVERRIDE);
+ if (err)
+ goto out;
+ }
+
+ /*
+ * If we are changing the size of the file, then
+ * we need to break all leases.
+ */
+ err = get_lease(inode, FMODE_WRITE);
+ if (err)
+ goto out_nfserr;
+
+ err = get_write_access(inode);
+ if (err)
+ goto out_nfserr;
+
+ err = locks_verify_truncate(inode, NULL, iap->ia_size);
+ if (err) {
+ put_write_access(inode);
+ goto out_nfserr;
+ }
+ DQUOT_INIT(inode);
+ }
+
+ imode = inode->i_mode;
+ if (iap->ia_valid & ATTR_MODE) {
+ iap->ia_mode &= S_IALLUGO;
+ imode = iap->ia_mode |= (imode & ~S_IALLUGO);
+ }
+
+ /* Revoke setuid/setgid bit on chown/chgrp */
+ if ((iap->ia_valid & ATTR_UID) && (imode & S_ISUID)
+ && iap->ia_uid != inode->i_uid) {
+ iap->ia_valid |= ATTR_MODE;
+ iap->ia_mode = imode &= ~S_ISUID;
+ }
+ if ((iap->ia_valid & ATTR_GID) && (imode & S_ISGID)
+ && iap->ia_gid != inode->i_gid) {
+ iap->ia_valid |= ATTR_MODE;
+ iap->ia_mode = imode &= ~S_ISGID;
+ }
+
+ /* Change the attributes. */
+
+
+ iap->ia_valid |= ATTR_CTIME;
+
+ if (iap->ia_valid & ATTR_SIZE) {
+ fh_lock(fhp);
+ size_change = 1;
+ }
+ err = nfserr_notsync;
+ if (!check_guard || guardtime == inode->i_ctime) {
+ err = notify_change(dentry, iap);
+ err = nfserrno(err);
+ }
+ if (size_change) {
+ fh_unlock(fhp);
+ put_write_access(inode);
+ }
+ if (!err)
+ if (EX_ISSYNC(fhp->fh_export))
+ write_inode_now(inode, 1);
+out:
+ return err;
+
+out_nfserr:
+ err = nfserrno(err);
+ goto out;
+}
+
+#ifdef CONFIG_NFSD_V3
+/*
+ * Check server access rights to a file system object
+ */
+struct accessmap {
+ u32 access;
+ int how;
+};
+static struct accessmap nfs3_regaccess[] = {
+ { NFS3_ACCESS_READ, MAY_READ },
+ { NFS3_ACCESS_EXECUTE, MAY_EXEC },
+ { NFS3_ACCESS_MODIFY, MAY_WRITE|MAY_TRUNC },
+ { NFS3_ACCESS_EXTEND, MAY_WRITE },
+
+ { 0, 0 }
+};
+
+static struct accessmap nfs3_diraccess[] = {
+ { NFS3_ACCESS_READ, MAY_READ },
+ { NFS3_ACCESS_LOOKUP, MAY_EXEC },
+ { NFS3_ACCESS_MODIFY, MAY_EXEC|MAY_WRITE|MAY_TRUNC },
+ { NFS3_ACCESS_EXTEND, MAY_EXEC|MAY_WRITE },
+ { NFS3_ACCESS_DELETE, MAY_REMOVE },
+
+ { 0, 0 }
+};
+
+static struct accessmap nfs3_anyaccess[] = {
+ /* Some clients - Solaris 2.6 at least, make an access call
+ * to the server to check for access for things like /dev/null
+ * (which really, the server doesn't care about). So
+ * We provide simple access checking for them, looking
+ * mainly at mode bits
+ */
+ { NFS3_ACCESS_READ, MAY_READ },
+ { NFS3_ACCESS_EXECUTE, MAY_EXEC },
+ { NFS3_ACCESS_MODIFY, MAY_WRITE },
+ { NFS3_ACCESS_EXTEND, MAY_WRITE },
+
+ { 0, 0 }
+};
+
+int
+nfsd_access(struct svc_rqst *rqstp, struct svc_fh *fhp, u32 *access)
+{
+ struct accessmap *map;
+ struct svc_export *export;
+ struct dentry *dentry;
+ u32 query, result = 0;
+ unsigned int error;
+
+ error = fh_verify(rqstp, fhp, 0, MAY_NOP);
+ if (error)
+ goto out;
+
+ export = fhp->fh_export;
+ dentry = fhp->fh_dentry;
+
+ if (S_ISREG(dentry->d_inode->i_mode))
+ map = nfs3_regaccess;
+ else if (S_ISDIR(dentry->d_inode->i_mode))
+ map = nfs3_diraccess;
+ else
+ map = nfs3_anyaccess;
+
+
+ query = *access;
+ for (; map->access; map++) {
+ if (map->access & query) {
+ unsigned int err2;
+ err2 = nfsd_permission(export, dentry, map->how);
+ switch (err2) {
+ case nfs_ok:
+ result |= map->access;
+ break;
+
+ /* the following error codes just mean the access was not allowed,
+ * rather than an error occurred */
+ case nfserr_rofs:
+ case nfserr_acces:
+ case nfserr_perm:
+ /* simply don't "or" in the access bit. */
+ break;
+ default:
+ error = err2;
+ goto out;
+ }
+ }
+ }
+ *access = result;
+
+ out:
+ return error;
+}
+#endif /* CONFIG_NFSD_V3 */
+
+
+
+/*
+ * Open an existing file or directory.
+ * The access argument indicates the type of open (read/write/lock)
+ * N.B. After this call fhp needs an fh_put
+ */
+int
+nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
+ int access, struct file *filp)
+{
+ struct dentry *dentry;
+ struct inode *inode;
+ int err;
+
+ /* If we get here, then the client has already done an "open", and (hopefully)
+ * checked permission - so allow OWNER_OVERRIDE in case a chmod has now revoked
+ * permission */
+ err = fh_verify(rqstp, fhp, type, access | MAY_OWNER_OVERRIDE);
+ if (err)
+ goto out;
+
+ dentry = fhp->fh_dentry;
+ inode = dentry->d_inode;
+
+ /* Disallow access to files with the append-only bit set or
+ * with mandatory locking enabled
+ */
+ err = nfserr_perm;
+ if (IS_APPEND(inode) || IS_ISMNDLK(inode))
+ goto out;
+ if (!inode->i_fop)
+ goto out;
+
+ /*
+ * Check to see if there are any leases on this file.
+ * This may block while leases are broken.
+ */
+ err = get_lease(inode, (access & MAY_WRITE) ? FMODE_WRITE : 0);
+ if (err)
+ goto out_nfserr;
+
+ if ((access & MAY_WRITE) && (err = get_write_access(inode)) != 0)
+ goto out_nfserr;
+
+ memset(filp, 0, sizeof(*filp));
+ filp->f_op = fops_get(inode->i_fop);
+ atomic_set(&filp->f_count, 1);
+ filp->f_dentry = dentry;
+ filp->f_vfsmnt = fhp->fh_export->ex_mnt;
+ filp->f_maxcount = INT_MAX;
+
+ if (access & MAY_WRITE) {
+ filp->f_flags = O_WRONLY|O_LARGEFILE;
+ filp->f_mode = FMODE_WRITE;
+ DQUOT_INIT(inode);
+ } else {
+ filp->f_flags = O_RDONLY|O_LARGEFILE;
+ filp->f_mode = FMODE_READ;
+ }
+
+ err = 0;
+ if (filp->f_op && filp->f_op->open) {
+ err = filp->f_op->open(inode, filp);
+ if (err) {
+ fops_put(filp->f_op);
+ if (access & MAY_WRITE)
+ put_write_access(inode);
+
+ /* I nearly added put_filp() call here, but this filp
+ * is really on callers stack frame. -DaveM
+ */
+ atomic_dec(&filp->f_count);
+ }
+ }
+out_nfserr:
+ if (err)
+ err = nfserrno(err);
+out:
+ return err;
+}
+
+/*
+ * Close a file.
+ */
+void
+nfsd_close(struct file *filp)
+{
+ struct dentry *dentry = filp->f_dentry;
+ struct inode *inode = dentry->d_inode;
+
+ if (filp->f_op && filp->f_op->release)
+ filp->f_op->release(inode, filp);
+ fops_put(filp->f_op);
+ if (filp->f_mode & FMODE_WRITE)
+ put_write_access(inode);
+}
+
+/*
+ * Sync a file
+ * As this calls fsync (not fdatasync) there is no need for a write_inode
+ * after it.
+ */
+inline void nfsd_dosync(struct file *filp, struct dentry *dp,
+ struct file_operations *fop)
+{
+ struct inode *inode = dp->d_inode;
+ int (*fsync) (struct file *, struct dentry *, int);
+
+ filemap_fdatasync(inode->i_mapping);
+ if (fop && (fsync = fop->fsync))
+ fsync(filp, dp, 0);
+ filemap_fdatawait(inode->i_mapping);
+}
+
+
+void
+nfsd_sync(struct file *filp)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ dprintk("nfsd: sync file %s\n", filp->f_dentry->d_name.name);
+ down(&inode->i_sem);
+ nfsd_dosync(filp, filp->f_dentry, filp->f_op);
+ up(&inode->i_sem);
+}
+
+void
+nfsd_sync_dir(struct dentry *dp)
+{
+ nfsd_dosync(NULL, dp, dp->d_inode->i_fop);
+}
+
+/*
+ * Obtain the readahead parameters for the file
+ * specified by (dev, ino).
+ */
+static inline struct raparms *
+nfsd_get_raparms(dev_t dev, ino_t ino)
+{
+ struct raparms *ra, **rap, **frap = NULL;
+ int depth = 0;
+
+ for (rap = &raparm_cache; (ra = *rap); rap = &ra->p_next) {
+ if (ra->p_ino == ino && ra->p_dev == dev)
+ goto found;
+ depth++;
+ if (ra->p_count == 0)
+ frap = rap;
+ }
+ depth = nfsdstats.ra_size*11/10;
+ if (!frap)
+ return NULL;
+ rap = frap;
+ ra = *frap;
+ ra->p_dev = dev;
+ ra->p_ino = ino;
+ ra->p_reada = 0;
+ ra->p_ramax = 0;
+ ra->p_raend = 0;
+ ra->p_ralen = 0;
+ ra->p_rawin = 0;
+found:
+ if (rap != &raparm_cache) {
+ *rap = ra->p_next;
+ ra->p_next = raparm_cache;
+ raparm_cache = ra;
+ }
+ ra->p_count++;
+ nfsdstats.ra_depth[depth*10/nfsdstats.ra_size]++;
+ return ra;
+}
+
+/* copied from fs/read_write.c */
+static inline loff_t llseek(struct file *file, loff_t offset, int origin)
+{
+ loff_t (*fn)(struct file *, loff_t, int);
+ loff_t retval;
+
+ fn = default_llseek;
+ if (file->f_op && file->f_op->llseek)
+ fn = file->f_op->llseek;
+ lock_kernel();
+ retval = fn(file, offset, origin);
+ unlock_kernel();
+ return retval;
+}
+
+
+/*
+ * Read data from a file. count must contain the requested read count
+ * on entry. On return, *count contains the number of bytes actually read.
+ * N.B. After this call fhp needs an fh_put
+ */
+int
+nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
+ char *buf, unsigned long *count)
+{
+ struct raparms *ra;
+ mm_segment_t oldfs;
+ int err;
+ struct file file;
+
+ err = nfsd_open(rqstp, fhp, S_IFREG, MAY_READ, &file);
+ if (err)
+ goto out;
+ err = nfserr_perm;
+ if (!file.f_op->read)
+ goto out_close;
+#ifdef MSNFS
+ if ((fhp->fh_export->ex_flags & NFSEXP_MSNFS) &&
+ (!lock_may_read(file.f_dentry->d_inode, offset, *count)))
+ goto out_close;
+#endif
+
+ /* Get readahead parameters */
+ ra = nfsd_get_raparms(fhp->fh_export->ex_dev, fhp->fh_dentry->d_inode->i_ino);
+ if (ra) {
+ file.f_reada = ra->p_reada;
+ file.f_ramax = ra->p_ramax;
+ file.f_raend = ra->p_raend;
+ file.f_ralen = ra->p_ralen;
+ file.f_rawin = ra->p_rawin;
+ }
+ llseek(&file, offset, 0);
+
+ oldfs = get_fs(); set_fs(KERNEL_DS);
+ err = file.f_op->read(&file, buf, *count, &file.f_pos);
+ set_fs(oldfs);
+
+ /* Write back readahead params */
+ if (ra != NULL) {
+ dprintk("nfsd: raparms %ld %ld %ld %ld %ld\n",
+ file.f_reada, file.f_ramax, file.f_raend,
+ file.f_ralen, file.f_rawin);
+ ra->p_reada = file.f_reada;
+ ra->p_ramax = file.f_ramax;
+ ra->p_raend = file.f_raend;
+ ra->p_ralen = file.f_ralen;
+ ra->p_rawin = file.f_rawin;
+ ra->p_count -= 1;
+ }
+
+ if (err >= 0) {
+ nfsdstats.io_read += err;
+ *count = err;
+ err = 0;
+ } else
+ err = nfserrno(err);
+out_close:
+ nfsd_close(&file);
+out:
+ return err;
+}
+
+/*
+ * Write data to a file.
+ * The stable flag requests synchronous writes.
+ * N.B. After this call fhp needs an fh_put
+ */
+int
+nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
+ char *buf, unsigned long cnt, int *stablep)
+{
+ struct svc_export *exp;
+ struct file file;
+ struct dentry *dentry;
+ struct inode *inode;
+ mm_segment_t oldfs;
+ int err = 0;
+ int stable = *stablep;
+
+ err = nfsd_open(rqstp, fhp, S_IFREG, MAY_WRITE, &file);
+ if (err)
+ goto out;
+ if (!cnt)
+ goto out_close;
+ err = nfserr_perm;
+ if (!file.f_op->write)
+ goto out_close;
+#ifdef MSNFS
+ if ((fhp->fh_export->ex_flags & NFSEXP_MSNFS) &&
+ (!lock_may_write(file.f_dentry->d_inode, offset, cnt)))
+ goto out_close;
+#endif
+
+ dentry = file.f_dentry;
+ inode = dentry->d_inode;
+ exp = fhp->fh_export;
+
+ /*
+ * Request sync writes if
+ * - the sync export option has been set, or
+ * - the client requested O_SYNC behavior (NFSv3 feature).
+ * - The file system doesn't support fsync().
+ * When gathered writes have been configured for this volume,
+ * flushing the data to disk is handled separately below.
+ */
+
+ if (file.f_op->fsync == 0) {/* COMMIT3 cannot work */
+ stable = 2;
+ *stablep = 2; /* FILE_SYNC */
+ }
+
+ if (!EX_ISSYNC(exp))
+ stable = 0;
+ if (stable && !EX_WGATHER(exp))
+ file.f_flags |= O_SYNC;
+
+
+ llseek(&file, offset, 0);
+
+ /* Write the data. */
+ oldfs = get_fs(); set_fs(KERNEL_DS);
+ err = file.f_op->write(&file, buf, cnt, &file.f_pos);
+ if (err >= 0)
+ nfsdstats.io_write += cnt;
+ set_fs(oldfs);
+
+ /* clear setuid/setgid flag after write */
+ if (err >= 0 && (inode->i_mode & (S_ISUID | S_ISGID))) {
+ struct iattr ia;
+
+ ia.ia_valid = ATTR_MODE;
+ ia.ia_mode = inode->i_mode & ~(S_ISUID | S_ISGID);
+ notify_change(dentry, &ia);
+ }
+
+ if (err >= 0 && stable) {
+ static unsigned long last_ino;
+ static kdev_t last_dev = NODEV;
+
+ /*
+ * Gathered writes: If another process is currently
+ * writing to the file, there's a high chance
+ * this is another nfsd (triggered by a bulk write
+ * from a client's biod). Rather than syncing the
+ * file with each write request, we sleep for 10 msec.
+ *
+ * I don't know if this roughly approximates
+ * C. Juszak's idea of gathered writes, but it's a
+ * nice and simple solution (IMHO), and it seems to
+ * work:-)
+ */
+ if (EX_WGATHER(exp)) {
+ if (atomic_read(&inode->i_writecount) > 1
+ || (last_ino == inode->i_ino && last_dev == inode->i_dev)) {
+ dprintk("nfsd: write defer %d\n", current->pid);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout((HZ+99)/100);
+ current->state = TASK_RUNNING;
+ dprintk("nfsd: write resume %d\n", current->pid);
+ }
+
+ if (inode->i_state & I_DIRTY) {
+ dprintk("nfsd: write sync %d\n", current->pid);
+ nfsd_sync(&file);
+ }
+#if 0
+ wake_up(&inode->i_wait);
+#endif
+ }
+ last_ino = inode->i_ino;
+ last_dev = inode->i_dev;
+ }
+
+ dprintk("nfsd: write complete err=%d\n", err);
+ if (err >= 0)
+ err = 0;
+ else
+ err = nfserrno(err);
+out_close:
+ nfsd_close(&file);
+out:
+ return err;
+}
+
+
+#ifdef CONFIG_NFSD_V3
+/*
+ * Commit all pending writes to stable storage.
+ * Strictly speaking, we could sync just the indicated file region here,
+ * but there's currently no way we can ask the VFS to do so.
+ *
+ * Unfortunately we cannot lock the file to make sure we return full WCC
+ * data to the client, as locking happens lower down in the filesystem.
+ */
+int
+nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp,
+ off_t offset, unsigned long count)
+{
+ struct file file;
+ int err;
+
+ if ((err = nfsd_open(rqstp, fhp, S_IFREG, MAY_WRITE, &file)) != 0)
+ return err;
+ if (EX_ISSYNC(fhp->fh_export)) {
+ if (file.f_op && file.f_op->fsync) {
+ nfsd_sync(&file);
+ } else {
+ err = nfserr_notsupp;
+ }
+ }
+
+ nfsd_close(&file);
+ return err;
+}
+#endif /* CONFIG_NFSD_V3 */
+
+/*
+ * Create a file (regular, directory, device, fifo); UNIX sockets
+ * not yet implemented.
+ * If the response fh has been verified, the parent directory should
+ * already be locked. Note that the parent directory is left locked.
+ *
+ * N.B. Every call to nfsd_create needs an fh_put for _both_ fhp and resfhp
+ */
+int
+nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
+ char *fname, int flen, struct iattr *iap,
+ int type, dev_t rdev, struct svc_fh *resfhp)
+{
+ struct dentry *dentry, *dchild;
+ struct inode *dirp;
+ int err;
+
+ err = nfserr_perm;
+ if (!flen)
+ goto out;
+ err = nfserr_exist;
+ if (isdotent(fname, flen))
+ goto out;
+
+ err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE);
+ if (err)
+ goto out;
+
+ dentry = fhp->fh_dentry;
+ dirp = dentry->d_inode;
+
+ err = nfserr_notdir;
+ if(!dirp->i_op || !dirp->i_op->lookup)
+ goto out;
+ /*
+ * Check whether the response file handle has been verified yet.
+ * If it has, the parent directory should already be locked.
+ */
+ if (!resfhp->fh_dentry) {
+ /* called from nfsd_proc_mkdir, or possibly nfsd3_proc_create */
+ fh_lock(fhp);
+ dchild = lookup_one_len(fname, dentry, flen);
+ err = PTR_ERR(dchild);
+ if (IS_ERR(dchild))
+ goto out_nfserr;
+ err = fh_compose(resfhp, fhp->fh_export, dchild, fhp);
+ if (err)
+ goto out;
+ } else {
+ /* called from nfsd_proc_create */
+ dchild = resfhp->fh_dentry;
+ if (!fhp->fh_locked) {
+ /* not actually possible */
+ printk(KERN_ERR
+ "nfsd_create: parent %s/%s not locked!\n",
+ dentry->d_parent->d_name.name,
+ dentry->d_name.name);
+ err = -EIO;
+ goto out;
+ }
+ }
+ /*
+ * Make sure the child dentry is still negative ...
+ */
+ err = nfserr_exist;
+ if (dchild->d_inode) {
+ dprintk("nfsd_create: dentry %s/%s not negative!\n",
+ dentry->d_name.name, dchild->d_name.name);
+ goto out;
+ }
+
+ if (!(iap->ia_valid & ATTR_MODE))
+ iap->ia_mode = 0;
+ iap->ia_mode = (iap->ia_mode & S_IALLUGO) | type;
+
+ /*
+ * Get the dir op function pointer.
+ */
+ err = nfserr_perm;
+ switch (type) {
+ case S_IFREG:
+ err = vfs_create(dirp, dchild, iap->ia_mode);
+ break;
+ case S_IFDIR:
+ err = vfs_mkdir(dirp, dchild, iap->ia_mode);
+ break;
+ case S_IFCHR:
+ case S_IFBLK:
+ case S_IFIFO:
+ case S_IFSOCK:
+ err = vfs_mknod(dirp, dchild, iap->ia_mode, rdev);
+ break;
+ default:
+ printk("nfsd: bad file type %o in nfsd_create\n", type);
+ err = -EINVAL;
+ }
+ if (err < 0)
+ goto out_nfserr;
+
+ if (EX_ISSYNC(fhp->fh_export)) {
+ nfsd_sync_dir(dentry);
+ write_inode_now(dchild->d_inode, 1);
+ }
+
+
+ /* Set file attributes. Mode has already been set and
+ * setting uid/gid works only for root. Irix appears to
+ * send along the gid when it tries to implement setgid
+ * directories via NFS.
+ */
+ err = 0;
+ if ((iap->ia_valid &= ~(ATTR_UID|ATTR_GID|ATTR_MODE)) != 0)
+ err = nfsd_setattr(rqstp, resfhp, iap, 0, (time_t)0);
+ /*
+ * Update the file handle to get the new inode info.
+ */
+ if (!err)
+ err = fh_update(resfhp);
+out:
+ return err;
+
+out_nfserr:
+ err = nfserrno(err);
+ goto out;
+}
+
+#ifdef CONFIG_NFSD_V3
+/*
+ * NFSv3 version of nfsd_create
+ */
+int
+nfsd_create_v3(struct svc_rqst *rqstp, struct svc_fh *fhp,
+ char *fname, int flen, struct iattr *iap,
+ struct svc_fh *resfhp, int createmode, u32 *verifier)
+{
+ struct dentry *dentry, *dchild;
+ struct inode *dirp;
+ int err;
+ __u32 v_mtime=0, v_atime=0;
+ int v_mode=0;
+
+ err = nfserr_perm;
+ if (!flen)
+ goto out;
+ err = nfserr_exist;
+ if (isdotent(fname, flen))
+ goto out;
+ if (!(iap->ia_valid & ATTR_MODE))
+ iap->ia_mode = 0;
+ err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE);
+ if (err)
+ goto out;
+
+ dentry = fhp->fh_dentry;
+ dirp = dentry->d_inode;
+
+ /* Get all the sanity checks out of the way before
+ * we lock the parent. */
+ err = nfserr_notdir;
+ if(!dirp->i_op || !dirp->i_op->lookup)
+ goto out;
+ fh_lock(fhp);
+
+ /*
+ * Compose the response file handle.
+ */
+ dchild = lookup_one_len(fname, dentry, flen);
+ err = PTR_ERR(dchild);
+ if (IS_ERR(dchild))
+ goto out_nfserr;
+
+ err = fh_compose(resfhp, fhp->fh_export, dchild, fhp);
+ if (err)
+ goto out;
+
+ if (createmode == NFS3_CREATE_EXCLUSIVE) {
+ /* while the verifier would fit in mtime+atime,
+ * solaris7 gets confused (bugid 4218508) if these have
+ * the high bit set, so we use the mode as well
+ */
+ v_mtime = verifier[0]&0x7fffffff;
+ v_atime = verifier[1]&0x7fffffff;
+ v_mode = S_IFREG
+ | ((verifier[0]&0x80000000) >> (32-7)) /* u+x */
+ | ((verifier[1]&0x80000000) >> (32-9)) /* u+r */
+ ;
+ }
+
+ if (dchild->d_inode) {
+ err = 0;
+
+ switch (createmode) {
+ case NFS3_CREATE_UNCHECKED:
+ if (! S_ISREG(dchild->d_inode->i_mode))
+ err = nfserr_exist;
+ else {
+ iap->ia_valid &= ATTR_SIZE;
+ goto set_attr;
+ }
+ break;
+ case NFS3_CREATE_EXCLUSIVE:
+ if ( dchild->d_inode->i_mtime == v_mtime
+ && dchild->d_inode->i_atime == v_atime
+ && dchild->d_inode->i_mode == v_mode
+ && dchild->d_inode->i_size == 0 )
+ break;
+ /* fallthru */
+ case NFS3_CREATE_GUARDED:
+ err = nfserr_exist;
+ }
+ goto out;
+ }
+
+ err = vfs_create(dirp, dchild, iap->ia_mode);
+ if (err < 0)
+ goto out_nfserr;
+
+ if (EX_ISSYNC(fhp->fh_export)) {
+ nfsd_sync_dir(dentry);
+ /* setattr will sync the child (or not) */
+ }
+
+ /*
+ * Update the filehandle to get the new inode info.
+ */
+ err = fh_update(resfhp);
+ if (err)
+ goto out;
+
+ if (createmode == NFS3_CREATE_EXCLUSIVE) {
+ /* Cram the verifier into atime/mtime/mode */
+ iap->ia_valid = ATTR_MTIME|ATTR_ATIME
+ | ATTR_MTIME_SET|ATTR_ATIME_SET
+ | ATTR_MODE;
+ iap->ia_mtime = v_mtime;
+ iap->ia_atime = v_atime;
+ iap->ia_mode = v_mode;
+ }
+
+ /* Set file attributes.
+ * Mode has already been set but we might need to reset it
+ * for CREATE_EXCLUSIVE
+ * Irix appears to send along the gid when it tries to
+ * implement setgid directories via NFS. Clear out all that cruft.
+ */
+ set_attr:
+ if ((iap->ia_valid &= ~(ATTR_UID|ATTR_GID)) != 0)
+ err = nfsd_setattr(rqstp, resfhp, iap, 0, (time_t)0);
+
+ out:
+ fh_unlock(fhp);
+ return err;
+
+ out_nfserr:
+ err = nfserrno(err);
+ goto out;
+}
+#endif /* CONFIG_NFSD_V3 */
+
+/*
+ * Read a symlink. On entry, *lenp must contain the maximum path length that
+ * fits into the buffer. On return, it contains the true length.
+ * N.B. After this call fhp needs an fh_put
+ */
+int
+nfsd_readlink(struct svc_rqst *rqstp, struct svc_fh *fhp, char *buf, int *lenp)
+{
+ struct dentry *dentry;
+ struct inode *inode;
+ mm_segment_t oldfs;
+ int err;
+
+ err = fh_verify(rqstp, fhp, S_IFLNK, MAY_NOP);
+ if (err)
+ goto out;
+
+ dentry = fhp->fh_dentry;
+ inode = dentry->d_inode;
+
+ err = nfserr_inval;
+ if (!inode->i_op || !inode->i_op->readlink)
+ goto out;
+
+ UPDATE_ATIME(inode);
+ /* N.B. Why does this call need a get_fs()??
+ * Remove the set_fs and watch the fireworks:-) --okir
+ */
+
+ oldfs = get_fs(); set_fs(KERNEL_DS);
+ err = inode->i_op->readlink(dentry, buf, *lenp);
+ set_fs(oldfs);
+
+ if (err < 0)
+ goto out_nfserr;
+ *lenp = err;
+ err = 0;
+out:
+ return err;
+
+out_nfserr:
+ err = nfserrno(err);
+ goto out;
+}
+
+/*
+ * Create a symlink and look up its inode
+ * N.B. After this call _both_ fhp and resfhp need an fh_put
+ */
+int
+nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
+ char *fname, int flen,
+ char *path, int plen,
+ struct svc_fh *resfhp,
+ struct iattr *iap)
+{
+ struct dentry *dentry, *dnew;
+ int err, cerr;
+
+ err = nfserr_noent;
+ if (!flen || !plen)
+ goto out;
+ err = nfserr_exist;
+ if (isdotent(fname, flen))
+ goto out;
+
+ err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE);
+ if (err)
+ goto out;
+ fh_lock(fhp);
+ dentry = fhp->fh_dentry;
+ dnew = lookup_one_len(fname, dentry, flen);
+ err = PTR_ERR(dnew);
+ if (IS_ERR(dnew))
+ goto out_nfserr;
+
+ err = vfs_symlink(dentry->d_inode, dnew, path);
+ if (!err) {
+ if (EX_ISSYNC(fhp->fh_export))
+ nfsd_sync_dir(dentry);
+ if (iap) {
+ iap->ia_valid &= ATTR_MODE /* ~(ATTR_MODE|ATTR_UID|ATTR_GID)*/;
+ if (iap->ia_valid) {
+ iap->ia_valid |= ATTR_CTIME;
+ iap->ia_mode = (iap->ia_mode&S_IALLUGO)
+ | S_IFLNK;
+ err = notify_change(dnew, iap);
+ if (err)
+ err = nfserrno(err);
+ else if (EX_ISSYNC(fhp->fh_export))
+ write_inode_now(dentry->d_inode, 1);
+ }
+ }
+ } else
+ err = nfserrno(err);
+ fh_unlock(fhp);
+
+ /* Compose the fh so the dentry will be freed ... */
+ cerr = fh_compose(resfhp, fhp->fh_export, dnew, fhp);
+ if (err==0) err = cerr;
+out:
+ return err;
+
+out_nfserr:
+ err = nfserrno(err);
+ goto out;
+}
+
+/*
+ * Create a hardlink
+ * N.B. After this call _both_ ffhp and tfhp need an fh_put
+ */
+int
+nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
+ char *name, int len, struct svc_fh *tfhp)
+{
+ struct dentry *ddir, *dnew, *dold;
+ struct inode *dirp, *dest;
+ int err;
+
+ err = fh_verify(rqstp, ffhp, S_IFDIR, MAY_CREATE);
+ if (err)
+ goto out;
+ err = fh_verify(rqstp, tfhp, -S_IFDIR, MAY_NOP);
+ if (err)
+ goto out;
+
+ err = nfserr_perm;
+ if (!len)
+ goto out;
+ err = nfserr_exist;
+ if (isdotent(name, len))
+ goto out;
+
+ fh_lock(ffhp);
+ ddir = ffhp->fh_dentry;
+ dirp = ddir->d_inode;
+
+ dnew = lookup_one_len(name, ddir, len);
+ err = PTR_ERR(dnew);
+ if (IS_ERR(dnew))
+ goto out_nfserr;
+
+ dold = tfhp->fh_dentry;
+ dest = dold->d_inode;
+
+ err = vfs_link(dold, dirp, dnew);
+ if (!err) {
+ if (EX_ISSYNC(ffhp->fh_export)) {
+ nfsd_sync_dir(ddir);
+ write_inode_now(dest, 1);
+ }
+ } else {
+ if (err == -EXDEV && rqstp->rq_vers == 2)
+ err = nfserr_acces;
+ else
+ err = nfserrno(err);
+ }
+
+ fh_unlock(ffhp);
+ dput(dnew);
+out:
+ return err;
+
+out_nfserr:
+ err = nfserrno(err);
+ goto out;
+}
+
+/*
+ * Rename a file
+ * N.B. After this call _both_ ffhp and tfhp need an fh_put
+ */
+int
+nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
+ struct svc_fh *tfhp, char *tname, int tlen)
+{
+ struct dentry *fdentry, *tdentry, *odentry, *ndentry;
+ struct inode *fdir, *tdir;
+ int err;
+
+ err = fh_verify(rqstp, ffhp, S_IFDIR, MAY_REMOVE);
+ if (err)
+ goto out;
+ err = fh_verify(rqstp, tfhp, S_IFDIR, MAY_CREATE);
+ if (err)
+ goto out;
+
+ fdentry = ffhp->fh_dentry;
+ fdir = fdentry->d_inode;
+
+ tdentry = tfhp->fh_dentry;
+ tdir = tdentry->d_inode;
+
+ err = (rqstp->rq_vers == 2) ? nfserr_acces : nfserr_xdev;
+ if (fdir->i_dev != tdir->i_dev)
+ goto out;
+
+ err = nfserr_perm;
+ if (!flen || isdotent(fname, flen) || !tlen || isdotent(tname, tlen))
+ goto out;
+
+ /* cannot use fh_lock as we need deadlock protective ordering
+ * so do it by hand */
+ double_down(&tdir->i_sem, &fdir->i_sem);
+ ffhp->fh_locked = tfhp->fh_locked = 1;
+ fill_pre_wcc(ffhp);
+ fill_pre_wcc(tfhp);
+
+ odentry = lookup_one_len(fname, fdentry, flen);
+ err = PTR_ERR(odentry);
+ if (IS_ERR(odentry))
+ goto out_nfserr;
+
+ err = -ENOENT;
+ if (!odentry->d_inode)
+ goto out_dput_old;
+
+ ndentry = lookup_one_len(tname, tdentry, tlen);
+ err = PTR_ERR(ndentry);
+ if (IS_ERR(ndentry))
+ goto out_dput_old;
+
+
+#ifdef MSNFS
+ if ((ffhp->fh_export->ex_flags & NFSEXP_MSNFS) &&
+ ((atomic_read(&odentry->d_count) > 1)
+ || (atomic_read(&ndentry->d_count) > 1))) {
+ err = nfserr_perm;
+ } else
+#endif
+ err = vfs_rename(fdir, odentry, tdir, ndentry);
+ if (!err && EX_ISSYNC(tfhp->fh_export)) {
+ nfsd_sync_dir(tdentry);
+ nfsd_sync_dir(fdentry);
+ }
+ dput(ndentry);
+
+ out_dput_old:
+ dput(odentry);
+ out_nfserr:
+ if (err)
+ err = nfserrno(err);
+
+ /* we cannot reply on fh_unlock on the two filehandles,
+ * as that would do the wrong thing if the two directories
+ * were the same, so again we do it by hand
+ */
+ fill_post_wcc(ffhp);
+ fill_post_wcc(tfhp);
+ double_up(&tdir->i_sem, &fdir->i_sem);
+ ffhp->fh_locked = tfhp->fh_locked = 0;
+
+out:
+ return err;
+}
+
+/*
+ * Unlink a file or directory
+ * N.B. After this call fhp needs an fh_put
+ */
+int
+nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
+ char *fname, int flen)
+{
+ struct dentry *dentry, *rdentry;
+ struct inode *dirp;
+ int err;
+
+ err = nfserr_acces;
+ if (!flen || isdotent(fname, flen))
+ goto out;
+ err = fh_verify(rqstp, fhp, S_IFDIR, MAY_REMOVE);
+ if (err)
+ goto out;
+
+ fh_lock(fhp);
+ dentry = fhp->fh_dentry;
+ dirp = dentry->d_inode;
+
+ rdentry = lookup_one_len(fname, dentry, flen);
+ err = PTR_ERR(rdentry);
+ if (IS_ERR(rdentry))
+ goto out_nfserr;
+
+ if (!rdentry->d_inode) {
+ dput(rdentry);
+ err = nfserr_noent;
+ goto out;
+ }
+
+ if (type != S_IFDIR) { /* It's UNLINK */
+#ifdef MSNFS
+ if ((fhp->fh_export->ex_flags & NFSEXP_MSNFS) &&
+ (atomic_read(&rdentry->d_count) > 1)) {
+ err = nfserr_perm;
+ } else
+#endif
+ err = vfs_unlink(dirp, rdentry);
+ } else { /* It's RMDIR */
+ err = vfs_rmdir(dirp, rdentry);
+ }
+
+ dput(rdentry);
+
+ if (err)
+ goto out_nfserr;
+ if (EX_ISSYNC(fhp->fh_export))
+ nfsd_sync_dir(dentry);
+
+out:
+ return err;
+
+out_nfserr:
+ err = nfserrno(err);
+ goto out;
+}
+
+/*
+ * Read entries from a directory.
+ * The verifier is an NFSv3 thing we ignore for now.
+ */
+int
+nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
+ encode_dent_fn func, u32 *buffer, int *countp, u32 *verf)
+{
+ u32 *p;
+ int oldlen, eof, err;
+ struct file file;
+ struct readdir_cd cd;
+
+ err = nfsd_open(rqstp, fhp, S_IFDIR, MAY_READ, &file);
+ if (err)
+ goto out;
+
+ offset = llseek(&file, offset, 0);
+ if (offset < 0) {
+ err = nfserrno((int)offset);
+ goto out_close;
+ }
+
+ /* Set up the readdir context */
+ memset(&cd, 0, sizeof(cd));
+ cd.rqstp = rqstp;
+ cd.buffer = buffer;
+ cd.buflen = *countp; /* count of words */
+ cd.dirfh = fhp;
+
+ /*
+ * Read the directory entries. This silly loop is necessary because
+ * readdir() is not guaranteed to fill up the entire buffer, but
+ * may choose to do less.
+ */
+
+ do {
+ oldlen = cd.buflen;
+
+ err = vfs_readdir(&file, (filldir_t) func, &cd);
+
+ if (err < 0)
+ goto out_nfserr;
+
+ } while (oldlen != cd.buflen && !cd.eob);
+
+ /* If we didn't fill the buffer completely, we're at EOF */
+ eof = !cd.eob;
+
+
+ offset = llseek(&file, 0LL, 1);
+ if (cd.offset) {
+ if (rqstp->rq_vers == 3)
+ (void)xdr_encode_hyper(cd.offset, offset);
+ else
+ *cd.offset = htonl(offset);
+ }
+
+ p = cd.buffer;
+ *p++ = 0; /* no more entries */
+ *p++ = htonl(eof); /* end of directory */
+ *countp = (caddr_t) p - (caddr_t) buffer;
+
+ dprintk("nfsd: readdir result %d bytes, eof %d offset %d\n",
+ *countp, eof,
+ cd.offset? ntohl(*cd.offset) : -1);
+ err = 0;
+out_close:
+ nfsd_close(&file);
+out:
+ return err;
+
+out_nfserr:
+ err = nfserrno(err);
+ goto out_close;
+}
+
+/*
+ * Get file system stats
+ * N.B. After this call fhp needs an fh_put
+ */
+int
+nfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct statfs *stat)
+{
+ int err = fh_verify(rqstp, fhp, 0, MAY_NOP);
+ if (!err && vfs_statfs(fhp->fh_dentry->d_inode->i_sb,stat))
+ err = nfserr_io;
+ return err;
+}
+
+/*
+ * Check for a user's access permissions to this inode.
+ */
+int
+nfsd_permission(struct svc_export *exp, struct dentry *dentry, int acc)
+{
+ struct inode *inode = dentry->d_inode;
+ int err;
+
+ if (acc == MAY_NOP)
+ return 0;
+#if 0
+ dprintk("nfsd: permission 0x%x%s%s%s%s%s%s%s mode 0%o%s%s%s\n",
+ acc,
+ (acc & MAY_READ)? " read" : "",
+ (acc & MAY_WRITE)? " write" : "",
+ (acc & MAY_EXEC)? " exec" : "",
+ (acc & MAY_SATTR)? " sattr" : "",
+ (acc & MAY_TRUNC)? " trunc" : "",
+ (acc & MAY_LOCK)? " lock" : "",
+ (acc & MAY_OWNER_OVERRIDE)? " owneroverride" : "",
+ inode->i_mode,
+ IS_IMMUTABLE(inode)? " immut" : "",
+ IS_APPEND(inode)? " append" : "",
+ IS_RDONLY(inode)? " ro" : "");
+ dprintk(" owner %d/%d user %d/%d\n",
+ inode->i_uid, inode->i_gid, current->fsuid, current->fsgid);
+#endif
+
+ /* The following code is here to make IRIX happy, which
+ * does a permission check every time a user does
+ * echo yaddayadda > special-file
+ * by sending a CREATE request.
+ * The original code would check read-only export status
+ * only for regular files and directories, allowing
+ * clients to chown/chmod device files and fifos even
+ * on volumes exported read-only. */
+ if (!(acc & _NFSD_IRIX_BOGOSITY)
+ && (acc & (MAY_WRITE | MAY_SATTR | MAY_TRUNC))) {
+ if (EX_RDONLY(exp) || IS_RDONLY(inode))
+ return nfserr_rofs;
+ if (/* (acc & MAY_WRITE) && */ IS_IMMUTABLE(inode))
+ return nfserr_perm;
+ }
+ if ((acc & MAY_TRUNC) && IS_APPEND(inode))
+ return nfserr_perm;
+
+ if (acc & MAY_LOCK) {
+ /* If we cannot rely on authentication in NLM requests,
+ * just allow locks, otherwise require read permission, or
+ * ownership
+ */
+ if (exp->ex_flags & NFSEXP_NOAUTHNLM)
+ return 0;
+ else
+ acc = MAY_READ | MAY_OWNER_OVERRIDE;
+ }
+ /*
+ * The file owner always gets access permission for accesses that
+ * would normally be checked at open time. This is to make
+ * file access work even when the client has done a fchmod(fd, 0).
+ *
+ * However, `cp foo bar' should fail nevertheless when bar is
+ * readonly. A sensible way to do this might be to reject all
+ * attempts to truncate a read-only file, because a creat() call
+ * always implies file truncation.
+ * ... but this isn't really fair. A process may reasonably call
+ * ftruncate on an open file descriptor on a file with perm 000.
+ * We must trust the client to do permission checking - using "ACCESS"
+ * with NFSv3.
+ */
+ if ((acc & MAY_OWNER_OVERRIDE) &&
+ inode->i_uid == current->fsuid)
+ return 0;
+
+ err = permission(inode, acc & (MAY_READ|MAY_WRITE|MAY_EXEC));
+
+ /* Allow read access to binaries even when mode 111 */
+ if (err == -EACCES && S_ISREG(inode->i_mode) &&
+ acc == (MAY_READ | MAY_OWNER_OVERRIDE))
+ err = permission(inode, MAY_EXEC);
+
+ return err? nfserrno(err) : 0;
+}
+
+void
+nfsd_racache_shutdown(void)
+{
+ if (!raparm_cache)
+ return;
+ dprintk("nfsd: freeing readahead buffers.\n");
+ kfree(raparml);
+ raparm_cache = raparml = NULL;
+}
+/*
+ * Initialize readahead param cache
+ */
+int
+nfsd_racache_init(int cache_size)
+{
+ int i;
+
+ if (raparm_cache)
+ return 0;
+ raparml = kmalloc(sizeof(struct raparms) * cache_size, GFP_KERNEL);
+
+ if (raparml != NULL) {
+ dprintk("nfsd: allocating %d readahead buffers.\n",
+ cache_size);
+ memset(raparml, 0, sizeof(struct raparms) * cache_size);
+ for (i = 0; i < cache_size - 1; i++) {
+ raparml[i].p_next = raparml + i + 1;
+ }
+ raparm_cache = raparml;
+ } else {
+ printk(KERN_WARNING
+ "nfsd: Could not allocate memory read-ahead cache.\n");
+ return -ENOMEM;
+ }
+ nfsdstats.ra_size = cache_size;
+ return 0;
+}
diff --git a/fs/nls/Config.in b/fs/nls/Config.in
new file mode 100644
index 00000000000000..262f75c5df0635
--- /dev/null
+++ b/fs/nls/Config.in
@@ -0,0 +1,64 @@
+#
+# Native language support configuration
+#
+
+# smb wants NLS
+if [ "$CONFIG_SMB_FS" = "m" -o "$CONFIG_SMB_FS" = "y" ]; then
+ define_bool CONFIG_SMB_NLS y
+else
+ define_bool CONFIG_SMB_NLS n
+fi
+
+# msdos and Joliet want NLS
+if [ "$CONFIG_JOLIET" = "y" -o "$CONFIG_FAT_FS" != "n" \
+ -o "$CONFIG_NTFS_FS" != "n" -o "$CONFIG_NCPFS_NLS" = "y" \
+ -o "$CONFIG_SMB_NLS" = "y" -o "$CONFIG_JFS_FS" != "n" \
+ -o "$CONFIG_BEFS_FS" != "n" -o "$CONFIG_HFSPLUS_FS" != "n" ]; then
+ define_bool CONFIG_NLS y
+else
+ define_bool CONFIG_NLS n
+fi
+
+if [ "$CONFIG_NLS" = "y" ]; then
+ mainmenu_option next_comment
+ comment 'Native Language Support'
+ string 'Default NLS Option' CONFIG_NLS_DEFAULT "iso8859-1"
+ tristate 'Codepage 437 (United States, Canada)' CONFIG_NLS_CODEPAGE_437
+ tristate 'Codepage 737 (Greek)' CONFIG_NLS_CODEPAGE_737
+ tristate 'Codepage 775 (Baltic Rim)' CONFIG_NLS_CODEPAGE_775
+ tristate 'Codepage 850 (Europe)' CONFIG_NLS_CODEPAGE_850
+ tristate 'Codepage 852 (Central/Eastern Europe)' CONFIG_NLS_CODEPAGE_852
+ tristate 'Codepage 855 (Cyrillic)' CONFIG_NLS_CODEPAGE_855
+ tristate 'Codepage 857 (Turkish)' CONFIG_NLS_CODEPAGE_857
+ tristate 'Codepage 860 (Portuguese)' CONFIG_NLS_CODEPAGE_860
+ tristate 'Codepage 861 (Icelandic)' CONFIG_NLS_CODEPAGE_861
+ tristate 'Codepage 862 (Hebrew)' CONFIG_NLS_CODEPAGE_862
+ tristate 'Codepage 863 (Canadian French)' CONFIG_NLS_CODEPAGE_863
+ tristate 'Codepage 864 (Arabic)' CONFIG_NLS_CODEPAGE_864
+ tristate 'Codepage 865 (Norwegian, Danish)' CONFIG_NLS_CODEPAGE_865
+ tristate 'Codepage 866 (Cyrillic/Russian)' CONFIG_NLS_CODEPAGE_866
+ tristate 'Codepage 869 (Greek)' CONFIG_NLS_CODEPAGE_869
+ tristate 'Simplified Chinese charset (CP936, GB2312)' CONFIG_NLS_CODEPAGE_936
+ tristate 'Traditional Chinese charset (Big5)' CONFIG_NLS_CODEPAGE_950
+ tristate 'Japanese charsets (Shift-JIS, EUC-JP)' CONFIG_NLS_CODEPAGE_932
+ tristate 'Korean charset (CP949, EUC-KR)' CONFIG_NLS_CODEPAGE_949
+ tristate 'Thai charset (CP874, TIS-620)' CONFIG_NLS_CODEPAGE_874
+ tristate 'Hebrew charsets (ISO-8859-8, CP1255)' CONFIG_NLS_ISO8859_8
+ tristate 'Windows CP1250 (Slavic/Central European Languages)' CONFIG_NLS_CODEPAGE_1250
+ tristate 'Windows CP1251 (Bulgarian, Belarusian)' CONFIG_NLS_CODEPAGE_1251
+ tristate 'NLS ISO 8859-1 (Latin 1; Western European Languages)' CONFIG_NLS_ISO8859_1
+ tristate 'NLS ISO 8859-2 (Latin 2; Slavic/Central European Languages)' CONFIG_NLS_ISO8859_2
+ tristate 'NLS ISO 8859-3 (Latin 3; Esperanto, Galician, Maltese, Turkish)' CONFIG_NLS_ISO8859_3
+ tristate 'NLS ISO 8859-4 (Latin 4; old Baltic charset)' CONFIG_NLS_ISO8859_4
+ tristate 'NLS ISO 8859-5 (Cyrillic)' CONFIG_NLS_ISO8859_5
+ tristate 'NLS ISO 8859-6 (Arabic)' CONFIG_NLS_ISO8859_6
+ tristate 'NLS ISO 8859-7 (Modern Greek)' CONFIG_NLS_ISO8859_7
+ tristate 'NLS ISO 8859-9 (Latin 5; Turkish)' CONFIG_NLS_ISO8859_9
+ tristate 'NLS ISO 8859-13 (Latin 7; Baltic)' CONFIG_NLS_ISO8859_13
+ tristate 'NLS ISO 8859-14 (Latin 8; Celtic)' CONFIG_NLS_ISO8859_14
+ tristate 'NLS ISO 8859-15 (Latin 9; Western European Languages with Euro)' CONFIG_NLS_ISO8859_15
+ tristate 'NLS KOI8-R (Russian)' CONFIG_NLS_KOI8_R
+ tristate 'NLS KOI8-U/RU (Ukrainian, Belarusian)' CONFIG_NLS_KOI8_U
+ tristate 'NLS UTF8' CONFIG_NLS_UTF8
+ endmenu
+fi
diff --git a/fs/nls/Makefile b/fs/nls/Makefile
new file mode 100644
index 00000000000000..d3d4ab272c202a
--- /dev/null
+++ b/fs/nls/Makefile
@@ -0,0 +1,52 @@
+#
+# Makefile for native language support
+#
+
+obj-y := nls_base.o
+obj-m :=
+obj-n :=
+obj- :=
+
+obj-$(CONFIG_NLS_CODEPAGE_437) += nls_cp437.o
+obj-$(CONFIG_NLS_CODEPAGE_737) += nls_cp737.o
+obj-$(CONFIG_NLS_CODEPAGE_775) += nls_cp775.o
+obj-$(CONFIG_NLS_CODEPAGE_850) += nls_cp850.o
+obj-$(CONFIG_NLS_CODEPAGE_852) += nls_cp852.o
+obj-$(CONFIG_NLS_CODEPAGE_855) += nls_cp855.o
+obj-$(CONFIG_NLS_CODEPAGE_857) += nls_cp857.o
+obj-$(CONFIG_NLS_CODEPAGE_860) += nls_cp860.o
+obj-$(CONFIG_NLS_CODEPAGE_861) += nls_cp861.o
+obj-$(CONFIG_NLS_CODEPAGE_862) += nls_cp862.o
+obj-$(CONFIG_NLS_CODEPAGE_863) += nls_cp863.o
+obj-$(CONFIG_NLS_CODEPAGE_864) += nls_cp864.o
+obj-$(CONFIG_NLS_CODEPAGE_865) += nls_cp865.o
+obj-$(CONFIG_NLS_CODEPAGE_866) += nls_cp866.o
+obj-$(CONFIG_NLS_CODEPAGE_869) += nls_cp869.o
+obj-$(CONFIG_NLS_CODEPAGE_874) += nls_cp874.o nls_tis-620.o
+obj-$(CONFIG_NLS_CODEPAGE_932) += nls_cp932.o nls_sjis.o nls_euc-jp.o
+obj-$(CONFIG_NLS_CODEPAGE_936) += nls_cp936.o nls_gb2312.o
+obj-$(CONFIG_NLS_CODEPAGE_949) += nls_cp949.o nls_euc-kr.o
+obj-$(CONFIG_NLS_CODEPAGE_950) += nls_cp950.o nls_big5.o
+obj-$(CONFIG_NLS_CODEPAGE_1250) += nls_cp1250.o
+obj-$(CONFIG_NLS_CODEPAGE_1251) += nls_cp1251.o
+obj-$(CONFIG_NLS_ISO8859_1) += nls_iso8859-1.o
+obj-$(CONFIG_NLS_ISO8859_2) += nls_iso8859-2.o
+obj-$(CONFIG_NLS_ISO8859_3) += nls_iso8859-3.o
+obj-$(CONFIG_NLS_ISO8859_4) += nls_iso8859-4.o
+obj-$(CONFIG_NLS_ISO8859_5) += nls_iso8859-5.o
+obj-$(CONFIG_NLS_ISO8859_6) += nls_iso8859-6.o
+obj-$(CONFIG_NLS_ISO8859_7) += nls_iso8859-7.o
+obj-$(CONFIG_NLS_ISO8859_8) += nls_cp1255.o nls_iso8859-8.o
+obj-$(CONFIG_NLS_ISO8859_9) += nls_iso8859-9.o
+obj-$(CONFIG_NLS_ISO8859_13) += nls_iso8859-13.o
+obj-$(CONFIG_NLS_ISO8859_14) += nls_iso8859-14.o
+obj-$(CONFIG_NLS_ISO8859_15) += nls_iso8859-15.o
+obj-$(CONFIG_NLS_KOI8_R) += nls_koi8-r.o
+obj-$(CONFIG_NLS_KOI8_U) += nls_koi8-u.o nls_koi8-ru.o
+obj-$(CONFIG_NLS_UTF8) += nls_utf8.o
+
+export-objs = $(obj-y)
+
+O_TARGET = nls.o
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/nls/nls_base.c b/fs/nls/nls_base.c
new file mode 100644
index 00000000000000..6c5e7883e675f3
--- /dev/null
+++ b/fs/nls/nls_base.c
@@ -0,0 +1,501 @@
+/*
+ * linux/fs/nls_base.c
+ *
+ * Native language support--charsets and unicode translations.
+ * By Gordon Chaffee 1996, 1997
+ *
+ * Unicode based case conversion 1999 by Wolfram Pienkoss
+ *
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/config.h>
+#include <linux/nls.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+#include <linux/spinlock.h>
+
+static struct nls_table *tables;
+static spinlock_t nls_lock = SPIN_LOCK_UNLOCKED;
+
+/*
+ * Sample implementation from Unicode home page.
+ * http://www.stonehand.com/unicode/standard/fss-utf.html
+ */
+struct utf8_table {
+ int cmask;
+ int cval;
+ int shift;
+ long lmask;
+ long lval;
+};
+
+static struct utf8_table utf8_table[] =
+{
+ {0x80, 0x00, 0*6, 0x7F, 0, /* 1 byte sequence */},
+ {0xE0, 0xC0, 1*6, 0x7FF, 0x80, /* 2 byte sequence */},
+ {0xF0, 0xE0, 2*6, 0xFFFF, 0x800, /* 3 byte sequence */},
+ {0xF8, 0xF0, 3*6, 0x1FFFFF, 0x10000, /* 4 byte sequence */},
+ {0xFC, 0xF8, 4*6, 0x3FFFFFF, 0x200000, /* 5 byte sequence */},
+ {0xFE, 0xFC, 5*6, 0x7FFFFFFF, 0x4000000, /* 6 byte sequence */},
+ {0, /* end of table */}
+};
+
+int
+utf8_mbtowc(wchar_t *p, const __u8 *s, int n)
+{
+ long l;
+ int c0, c, nc;
+ struct utf8_table *t;
+
+ nc = 0;
+ c0 = *s;
+ l = c0;
+ for (t = utf8_table; t->cmask; t++) {
+ nc++;
+ if ((c0 & t->cmask) == t->cval) {
+ l &= t->lmask;
+ if (l < t->lval)
+ return -1;
+ *p = l;
+ return nc;
+ }
+ if (n <= nc)
+ return -1;
+ s++;
+ c = (*s ^ 0x80) & 0xFF;
+ if (c & 0xC0)
+ return -1;
+ l = (l << 6) | c;
+ }
+ return -1;
+}
+
+int
+utf8_mbstowcs(wchar_t *pwcs, const __u8 *s, int n)
+{
+ __u16 *op;
+ const __u8 *ip;
+ int size;
+
+ op = pwcs;
+ ip = s;
+ while (*ip && n > 0) {
+ if (*ip & 0x80) {
+ size = utf8_mbtowc(op, ip, n);
+ if (size == -1) {
+ /* Ignore character and move on */
+ ip++;
+ n--;
+ } else {
+ op++;
+ ip += size;
+ n -= size;
+ }
+ } else {
+ *op++ = *ip++;
+ }
+ }
+ return (op - pwcs);
+}
+
+int
+utf8_wctomb(__u8 *s, wchar_t wc, int maxlen)
+{
+ long l;
+ int c, nc;
+ struct utf8_table *t;
+
+ if (s == 0)
+ return 0;
+
+ l = wc;
+ nc = 0;
+ for (t = utf8_table; t->cmask && maxlen; t++, maxlen--) {
+ nc++;
+ if (l <= t->lmask) {
+ c = t->shift;
+ *s = t->cval | (l >> c);
+ while (c > 0) {
+ c -= 6;
+ s++;
+ *s = 0x80 | ((l >> c) & 0x3F);
+ }
+ return nc;
+ }
+ }
+ return -1;
+}
+
+int
+utf8_wcstombs(__u8 *s, const wchar_t *pwcs, int maxlen)
+{
+ const __u16 *ip;
+ __u8 *op;
+ int size;
+
+ op = s;
+ ip = pwcs;
+ while (*ip && maxlen > 0) {
+ if (*ip > 0x7f) {
+ size = utf8_wctomb(op, *ip, maxlen);
+ if (size == -1) {
+ /* Ignore character and move on */
+ maxlen--;
+ } else {
+ op += size;
+ maxlen -= size;
+ }
+ } else {
+ *op++ = (__u8) *ip;
+ }
+ ip++;
+ }
+ return (op - s);
+}
+
+int register_nls(struct nls_table * nls)
+{
+ struct nls_table ** tmp = &tables;
+
+ if (!nls)
+ return -EINVAL;
+ if (nls->next)
+ return -EBUSY;
+
+ spin_lock(&nls_lock);
+ while (*tmp) {
+ if (nls == *tmp) {
+ spin_unlock(&nls_lock);
+ return -EBUSY;
+ }
+ tmp = &(*tmp)->next;
+ }
+ nls->next = tables;
+ tables = nls;
+ spin_unlock(&nls_lock);
+ return 0;
+}
+
+int unregister_nls(struct nls_table * nls)
+{
+ struct nls_table ** tmp = &tables;
+
+ spin_lock(&nls_lock);
+ while (*tmp) {
+ if (nls == *tmp) {
+ *tmp = nls->next;
+ spin_unlock(&nls_lock);
+ return 0;
+ }
+ tmp = &(*tmp)->next;
+ }
+ spin_unlock(&nls_lock);
+ return -EINVAL;
+}
+
+static struct nls_table *find_nls(char *charset)
+{
+ struct nls_table *nls;
+ spin_lock(&nls_lock);
+ for (nls = tables; nls; nls = nls->next)
+ if (! strcmp(nls->charset, charset))
+ break;
+ if (nls && !try_inc_mod_count(nls->owner))
+ nls = NULL;
+ spin_unlock(&nls_lock);
+ return nls;
+}
+
+struct nls_table *load_nls(char *charset)
+{
+ struct nls_table *nls;
+#ifdef CONFIG_KMOD
+ char buf[40];
+ int ret;
+#endif
+
+ nls = find_nls(charset);
+ if (nls)
+ return nls;
+
+#ifdef CONFIG_KMOD
+ if (strlen(charset) > sizeof(buf) - sizeof("nls_")) {
+ printk("Unable to load NLS charset %s: name too long\n",
+ charset);
+ return NULL;
+ }
+
+ sprintf(buf, "nls_%s", charset);
+ ret = request_module(buf);
+ if (ret != 0) {
+ printk("Unable to load NLS charset %s\n", charset);
+ return NULL;
+ }
+ nls = find_nls(charset);
+#endif
+ return nls;
+}
+
+void unload_nls(struct nls_table *nls)
+{
+ if (nls->owner)
+ __MOD_DEC_USE_COUNT(nls->owner);
+}
+
+wchar_t charset2uni[256] = {
+ /* 0x00*/
+ 0x0000, 0x0001, 0x0002, 0x0003,
+ 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b,
+ 0x000c, 0x000d, 0x000e, 0x000f,
+ /* 0x10*/
+ 0x0010, 0x0011, 0x0012, 0x0013,
+ 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b,
+ 0x001c, 0x001d, 0x001e, 0x001f,
+ /* 0x20*/
+ 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b,
+ 0x002c, 0x002d, 0x002e, 0x002f,
+ /* 0x30*/
+ 0x0030, 0x0031, 0x0032, 0x0033,
+ 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b,
+ 0x003c, 0x003d, 0x003e, 0x003f,
+ /* 0x40*/
+ 0x0040, 0x0041, 0x0042, 0x0043,
+ 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b,
+ 0x004c, 0x004d, 0x004e, 0x004f,
+ /* 0x50*/
+ 0x0050, 0x0051, 0x0052, 0x0053,
+ 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b,
+ 0x005c, 0x005d, 0x005e, 0x005f,
+ /* 0x60*/
+ 0x0060, 0x0061, 0x0062, 0x0063,
+ 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b,
+ 0x006c, 0x006d, 0x006e, 0x006f,
+ /* 0x70*/
+ 0x0070, 0x0071, 0x0072, 0x0073,
+ 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b,
+ 0x007c, 0x007d, 0x007e, 0x007f,
+ /* 0x80*/
+ 0x0080, 0x0081, 0x0082, 0x0083,
+ 0x0084, 0x0085, 0x0086, 0x0087,
+ 0x0088, 0x0089, 0x008a, 0x008b,
+ 0x008c, 0x008d, 0x008e, 0x008f,
+ /* 0x90*/
+ 0x0090, 0x0091, 0x0092, 0x0093,
+ 0x0094, 0x0095, 0x0096, 0x0097,
+ 0x0098, 0x0099, 0x009a, 0x009b,
+ 0x009c, 0x009d, 0x009e, 0x009f,
+ /* 0xa0*/
+ 0x00a0, 0x00a1, 0x00a2, 0x00a3,
+ 0x00a4, 0x00a5, 0x00a6, 0x00a7,
+ 0x00a8, 0x00a9, 0x00aa, 0x00ab,
+ 0x00ac, 0x00ad, 0x00ae, 0x00af,
+ /* 0xb0*/
+ 0x00b0, 0x00b1, 0x00b2, 0x00b3,
+ 0x00b4, 0x00b5, 0x00b6, 0x00b7,
+ 0x00b8, 0x00b9, 0x00ba, 0x00bb,
+ 0x00bc, 0x00bd, 0x00be, 0x00bf,
+ /* 0xc0*/
+ 0x00c0, 0x00c1, 0x00c2, 0x00c3,
+ 0x00c4, 0x00c5, 0x00c6, 0x00c7,
+ 0x00c8, 0x00c9, 0x00ca, 0x00cb,
+ 0x00cc, 0x00cd, 0x00ce, 0x00cf,
+ /* 0xd0*/
+ 0x00d0, 0x00d1, 0x00d2, 0x00d3,
+ 0x00d4, 0x00d5, 0x00d6, 0x00d7,
+ 0x00d8, 0x00d9, 0x00da, 0x00db,
+ 0x00dc, 0x00dd, 0x00de, 0x00df,
+ /* 0xe0*/
+ 0x00e0, 0x00e1, 0x00e2, 0x00e3,
+ 0x00e4, 0x00e5, 0x00e6, 0x00e7,
+ 0x00e8, 0x00e9, 0x00ea, 0x00eb,
+ 0x00ec, 0x00ed, 0x00ee, 0x00ef,
+ /* 0xf0*/
+ 0x00f0, 0x00f1, 0x00f2, 0x00f3,
+ 0x00f4, 0x00f5, 0x00f6, 0x00f7,
+ 0x00f8, 0x00f9, 0x00fa, 0x00fb,
+ 0x00fc, 0x00fd, 0x00fe, 0x00ff,
+};
+
+static unsigned char page00[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0x88-0x8f */
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static unsigned char *page_uni2charset[256] = {
+ page00
+};
+
+static unsigned char charset2lower[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40-0x47 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x48-0x4f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50-0x57 */
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0x88-0x8f */
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static unsigned char charset2upper[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0x88-0x8f */
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+ unsigned char *uni2charset;
+ unsigned char cl = uni & 0x00ff;
+ unsigned char ch = (uni & 0xff00) >> 8;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ uni2charset = page_uni2charset[ch];
+ if (uni2charset && uni2charset[cl])
+ out[0] = uni2charset[cl];
+ else
+ return -EINVAL;
+ return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+ *uni = charset2uni[*rawstring];
+ if (*uni == 0x0000)
+ return -EINVAL;
+ return 1;
+}
+
+static struct nls_table default_table = {
+ "default",
+ uni2char,
+ char2uni,
+ charset2lower,
+ charset2upper,
+ NULL,
+};
+
+/* Returns a simple default translation table */
+struct nls_table *load_nls_default(void)
+{
+ struct nls_table *default_nls;
+
+ default_nls = load_nls(CONFIG_NLS_DEFAULT);
+ if (default_nls != NULL)
+ return default_nls;
+ else
+ return &default_table;
+}
+
+EXPORT_SYMBOL(register_nls);
+EXPORT_SYMBOL(unregister_nls);
+EXPORT_SYMBOL(unload_nls);
+EXPORT_SYMBOL(load_nls);
+EXPORT_SYMBOL(load_nls_default);
+EXPORT_SYMBOL(utf8_mbtowc);
+EXPORT_SYMBOL(utf8_mbstowcs);
+EXPORT_SYMBOL(utf8_wctomb);
+EXPORT_SYMBOL(utf8_wcstombs);
diff --git a/fs/nls/nls_big5.c b/fs/nls/nls_big5.c
new file mode 100644
index 00000000000000..04849382a58085
--- /dev/null
+++ b/fs/nls/nls_big5.c
@@ -0,0 +1,63 @@
+/*
+ * linux/fs/nls_big5.c
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static struct nls_table *p_nls;
+
+static struct nls_table table = {
+ "big5",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ THIS_MODULE,
+};
+
+static int __init init_nls_big5(void)
+{
+ p_nls = load_nls("cp950");
+
+ if (p_nls) {
+ table.uni2char = p_nls->uni2char;
+ table.char2uni = p_nls->char2uni;
+ table.charset2upper = p_nls->charset2upper;
+ table.charset2lower = p_nls->charset2lower;
+ return register_nls(&table);
+ }
+
+ return -EINVAL;
+}
+
+static void __exit exit_nls_big5(void)
+{
+ unregister_nls(&table);
+ unload_nls(p_nls);
+}
+
+module_init(init_nls_big5)
+module_exit(exit_nls_big5)
+MODULE_LICENSE("Dual BSD/GPL");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ *
+---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/fs/nls/nls_cp1250.c b/fs/nls/nls_cp1250.c
new file mode 100644
index 00000000000000..95a977f68b99d7
--- /dev/null
+++ b/fs/nls/nls_cp1250.c
@@ -0,0 +1,365 @@
+/*
+ * linux/fs/nls_cp1250.c
+ *
+ * Charset cp1250 translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static wchar_t charset2uni[256] = {
+ /* 0x00*/
+ 0x0000, 0x0001, 0x0002, 0x0003,
+ 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b,
+ 0x000c, 0x000d, 0x000e, 0x000f,
+ /* 0x10*/
+ 0x0010, 0x0011, 0x0012, 0x0013,
+ 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b,
+ 0x001c, 0x001d, 0x001e, 0x001f,
+ /* 0x20*/
+ 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b,
+ 0x002c, 0x002d, 0x002e, 0x002f,
+ /* 0x30*/
+ 0x0030, 0x0031, 0x0032, 0x0033,
+ 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b,
+ 0x003c, 0x003d, 0x003e, 0x003f,
+ /* 0x40*/
+ 0x0040, 0x0041, 0x0042, 0x0043,
+ 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b,
+ 0x004c, 0x004d, 0x004e, 0x004f,
+ /* 0x50*/
+ 0x0050, 0x0051, 0x0052, 0x0053,
+ 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b,
+ 0x005c, 0x005d, 0x005e, 0x005f,
+ /* 0x60*/
+ 0x0060, 0x0061, 0x0062, 0x0063,
+ 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b,
+ 0x006c, 0x006d, 0x006e, 0x006f,
+ /* 0x70*/
+ 0x0070, 0x0071, 0x0072, 0x0073,
+ 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b,
+ 0x007c, 0x007d, 0x007e, 0x007f,
+ /* 0x80*/
+ 0x20ac, 0x0000, 0x201a, 0x0000,
+ 0x201e, 0x2026, 0x2020, 0x2021,
+ 0x0000, 0x2030, 0x0160, 0x2039,
+ 0x015a, 0x0164, 0x017d, 0x0179,
+ /* 0x90*/
+ 0x0000, 0x2018, 0x2019, 0x201c,
+ 0x201d, 0x2022, 0x2013, 0x2014,
+ 0x0000, 0x2122, 0x0161, 0x203a,
+ 0x015b, 0x0165, 0x017e, 0x017a,
+ /* 0xa0*/
+ 0x00a0, 0x02c7, 0x02d8, 0x0141,
+ 0x00a4, 0x0104, 0x00a6, 0x00a7,
+ 0x00a8, 0x00a9, 0x015e, 0x00ab,
+ 0x00ac, 0x00ad, 0x00ae, 0x017b,
+ /* 0xb0*/
+ 0x00b0, 0x00b1, 0x02db, 0x0142,
+ 0x00b4, 0x00b5, 0x00b6, 0x00b7,
+ 0x00b8, 0x0105, 0x015f, 0x00bb,
+ 0x013d, 0x02dd, 0x013e, 0x017c,
+ /* 0xc0*/
+ 0x0154, 0x00c1, 0x00c2, 0x0102,
+ 0x00c4, 0x0139, 0x0106, 0x00c7,
+ 0x010c, 0x00c9, 0x0118, 0x00cb,
+ 0x011a, 0x00cd, 0x00ce, 0x010e,
+ /* 0xd0*/
+ 0x0110, 0x0143, 0x0147, 0x00d3,
+ 0x00d4, 0x0150, 0x00d6, 0x00d7,
+ 0x0158, 0x016e, 0x00da, 0x0170,
+ 0x00dc, 0x00dd, 0x0162, 0x00df,
+ /* 0xe0*/
+ 0x0155, 0x00e1, 0x00e2, 0x0103,
+ 0x00e4, 0x013a, 0x0107, 0x00e7,
+ 0x010d, 0x00e9, 0x0119, 0x00eb,
+ 0x011b, 0x00ed, 0x00ee, 0x010f,
+ /* 0xf0*/
+ 0x0111, 0x0144, 0x0148, 0x00f3,
+ 0x00f4, 0x0151, 0x00f6, 0x00f7,
+ 0x0159, 0x016f, 0x00fa, 0x0171,
+ 0x00fc, 0x00fd, 0x0163, 0x02d9,
+ };
+
+static unsigned char page00[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0x00, 0x00, 0x00, 0xa4, 0x00, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0x00, 0xab, 0xac, 0xad, 0xae, 0x00, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0x00, 0x00, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0x00, 0x00, 0xbb, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+ 0x00, 0xc1, 0xc2, 0x00, 0xc4, 0x00, 0x00, 0xc7, /* 0xc0-0xc7 */
+ 0x00, 0xc9, 0x00, 0xcb, 0x00, 0xcd, 0xce, 0x00, /* 0xc8-0xcf */
+ 0x00, 0x00, 0x00, 0xd3, 0xd4, 0x00, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0x00, 0x00, 0xda, 0x00, 0xdc, 0xdd, 0x00, 0xdf, /* 0xd8-0xdf */
+ 0x00, 0xe1, 0xe2, 0x00, 0xe4, 0x00, 0x00, 0xe7, /* 0xe0-0xe7 */
+ 0x00, 0xe9, 0x00, 0xeb, 0x00, 0xed, 0xee, 0x00, /* 0xe8-0xef */
+ 0x00, 0x00, 0x00, 0xf3, 0xf4, 0x00, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0x00, 0x00, 0xfa, 0x00, 0xfc, 0xfd, 0x00, 0x00, /* 0xf8-0xff */
+ };
+
+static unsigned char page01[256] = {
+ 0x00, 0x00, 0xc3, 0xe3, 0xa5, 0xb9, 0xc6, 0xe6, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0xc8, 0xe8, 0xcf, 0xef, /* 0x08-0x0f */
+ 0xd0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0xca, 0xea, 0xcc, 0xec, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0xc5, 0xe5, 0x00, 0x00, 0xbc, 0xbe, 0x00, /* 0x38-0x3f */
+ 0x00, 0xa3, 0xb3, 0xd1, 0xf1, 0x00, 0x00, 0xd2, /* 0x40-0x47 */
+ 0xf2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0xd5, 0xf5, 0x00, 0x00, 0xc0, 0xe0, 0x00, 0x00, /* 0x50-0x57 */
+ 0xd8, 0xf8, 0x8c, 0x9c, 0x00, 0x00, 0xaa, 0xba, /* 0x58-0x5f */
+ 0x8a, 0x9a, 0xde, 0xfe, 0x8d, 0x9d, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd9, 0xf9, /* 0x68-0x6f */
+ 0xdb, 0xfb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x8f, 0x9f, 0xaf, 0xbf, 0x8e, 0x9e, 0x00, /* 0x78-0x7f */
+
+ };
+
+static unsigned char page02[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa1, /* 0xc0-0xc7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+ 0xa2, 0xff, 0x00, 0xb2, 0x00, 0xbd, 0x00, 0x00, /* 0xd8-0xdf */
+ };
+
+static unsigned char page20[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x96, 0x97, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x91, 0x92, 0x82, 0x00, 0x93, 0x94, 0x84, 0x00, /* 0x18-0x1f */
+ 0x86, 0x87, 0x95, 0x00, 0x00, 0x00, 0x85, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x8b, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+ };
+
+static unsigned char page21[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ };
+
+static unsigned char *page_uni2charset[256] = {
+ page00, page01, page02, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ page20, page21, NULL, NULL, NULL, NULL, NULL, NULL,
+ };
+
+static unsigned char charset2lower[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40-0x47 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x48-0x4f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50-0x57 */
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x80, 0x00, 0x82, 0x00, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */
+ 0x00, 0x89, 0x9a, 0x8b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x88-0x8f */
+ 0x00, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */
+ 0x00, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xb3, 0xa4, 0xb9, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xba, 0xab, 0xac, 0xad, 0xae, 0xbf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbe, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xc0-0xc7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xc8-0xcf */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xd7, /* 0xd0-0xd7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+ };
+
+static unsigned char charset2upper[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x80, 0x00, 0x82, 0x00, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */
+ 0x00, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0x88-0x8f */
+ 0x00, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */
+ 0x00, 0x99, 0x8a, 0x9b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xa3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xa5, 0xaa, 0xbb, 0xbc, 0xbd, 0xbc, 0xaf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0x00, /* 0xd8-0xdf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xe0-0xe7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xe8-0xef */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xf7, /* 0xf0-0xf7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xff, /* 0xf8-0xff */
+ };
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+ unsigned char *uni2charset;
+ unsigned char cl = uni & 0x00ff;
+ unsigned char ch = (uni & 0xff00) >> 8;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ uni2charset = page_uni2charset[ch];
+ if (uni2charset && uni2charset[cl])
+ out[0] = uni2charset[cl];
+ else
+ return -EINVAL;
+ return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+ *uni = charset2uni[*rawstring];
+ if (*uni == 0x0000)
+ return -EINVAL;
+ return 1;
+}
+
+static struct nls_table table = {
+ "cp1250",
+ uni2char,
+ char2uni,
+ charset2lower,
+ charset2upper,
+ THIS_MODULE,
+};
+
+static int __init init_nls_cp1250(void)
+{
+ return register_nls(&table);
+}
+static void __exit exit_nls_cp1250(void)
+{
+ unregister_nls(&table);
+}
+
+module_init(init_nls_cp1250)
+module_exit(exit_nls_cp1250)
+
+MODULE_LICENSE("Dual BSD/GPL");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
+
diff --git a/fs/nls/nls_cp1251.c b/fs/nls/nls_cp1251.c
new file mode 100644
index 00000000000000..e432312e03a3d2
--- /dev/null
+++ b/fs/nls/nls_cp1251.c
@@ -0,0 +1,318 @@
+/*
+ * linux/fs/nls_cp1251.c
+ *
+ * Charset cp1251 translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static wchar_t charset2uni[256] = {
+ /* 0x00*/
+ 0x0000, 0x0001, 0x0002, 0x0003,
+ 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b,
+ 0x000c, 0x000d, 0x000e, 0x000f,
+ /* 0x10*/
+ 0x0010, 0x0011, 0x0012, 0x0013,
+ 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b,
+ 0x001c, 0x001d, 0x001e, 0x001f,
+ /* 0x20*/
+ 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b,
+ 0x002c, 0x002d, 0x002e, 0x002f,
+ /* 0x30*/
+ 0x0030, 0x0031, 0x0032, 0x0033,
+ 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b,
+ 0x003c, 0x003d, 0x003e, 0x003f,
+ /* 0x40*/
+ 0x0040, 0x0041, 0x0042, 0x0043,
+ 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b,
+ 0x004c, 0x004d, 0x004e, 0x004f,
+ /* 0x50*/
+ 0x0050, 0x0051, 0x0052, 0x0053,
+ 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b,
+ 0x005c, 0x005d, 0x005e, 0x005f,
+ /* 0x60*/
+ 0x0060, 0x0061, 0x0062, 0x0063,
+ 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b,
+ 0x006c, 0x006d, 0x006e, 0x006f,
+ /* 0x70*/
+ 0x0070, 0x0071, 0x0072, 0x0073,
+ 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b,
+ 0x007c, 0x007d, 0x007e, 0x007f,
+ /* 0x80*/
+ 0x0402, 0x0403, 0x201a, 0x0453,
+ 0x201e, 0x2026, 0x2020, 0x2021,
+ 0x20ac, 0x2030, 0x0409, 0x2039,
+ 0x040a, 0x040c, 0x040b, 0x040f,
+ /* 0x90*/
+ 0x0452, 0x2018, 0x2019, 0x201c,
+ 0x201d, 0x2022, 0x2013, 0x2014,
+ 0x0000, 0x2122, 0x0459, 0x203a,
+ 0x045a, 0x045c, 0x045b, 0x045f,
+ /* 0xa0*/
+ 0x00a0, 0x040e, 0x045e, 0x0408,
+ 0x00a4, 0x0490, 0x00a6, 0x00a7,
+ 0x0401, 0x00a9, 0x0404, 0x00ab,
+ 0x00ac, 0x00ad, 0x00ae, 0x0407,
+ /* 0xb0*/
+ 0x00b0, 0x00b1, 0x0406, 0x0456,
+ 0x0491, 0x00b5, 0x00b6, 0x00b7,
+ 0x0451, 0x2116, 0x0454, 0x00bb,
+ 0x0458, 0x0405, 0x0455, 0x0457,
+ /* 0xc0*/
+ 0x0410, 0x0411, 0x0412, 0x0413,
+ 0x0414, 0x0415, 0x0416, 0x0417,
+ 0x0418, 0x0419, 0x041a, 0x041b,
+ 0x041c, 0x041d, 0x041e, 0x041f,
+ /* 0xd0*/
+ 0x0420, 0x0421, 0x0422, 0x0423,
+ 0x0424, 0x0425, 0x0426, 0x0427,
+ 0x0428, 0x0429, 0x042a, 0x042b,
+ 0x042c, 0x042d, 0x042e, 0x042f,
+ /* 0xe0*/
+ 0x0430, 0x0431, 0x0432, 0x0433,
+ 0x0434, 0x0435, 0x0436, 0x0437,
+ 0x0438, 0x0439, 0x043a, 0x043b,
+ 0x043c, 0x043d, 0x043e, 0x043f,
+ /* 0xf0*/
+ 0x0440, 0x0441, 0x0442, 0x0443,
+ 0x0444, 0x0445, 0x0446, 0x0447,
+ 0x0448, 0x0449, 0x044a, 0x044b,
+ 0x044c, 0x044d, 0x044e, 0x044f,
+};
+
+static unsigned char page00[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0x00, 0x00, 0x00, 0xa4, 0x00, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0x00, 0xa9, 0x00, 0xab, 0xac, 0xad, 0xae, 0x00, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0x00, 0x00, 0x00, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0x00, 0x00, 0x00, 0xbb, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+ 0x00, 0x00, 0x00, 0xbb, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+ 0x00, 0x00, 0x00, 0xbb, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+ 0x00, 0x00, 0x00, 0xbb, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+ 0x00, 0x00, 0x00, 0xbb, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+ 0x00, 0x00, 0x00, 0xbb, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+ 0x00, 0x00, 0x00, 0xbb, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+ 0x00, 0x00, 0x00, 0xbb, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+ 0x00, 0x00, 0x00, 0xbb, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static unsigned char page04[256] = {
+ 0x00, 0xa8, 0x80, 0x81, 0xaa, 0xbd, 0xb2, 0xaf, /* 0x00-0x07 */
+ 0xa3, 0x8a, 0x8c, 0x8e, 0x8d, 0x00, 0xa1, 0x8f, /* 0x08-0x0f */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0x10-0x17 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0x18-0x1f */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0x20-0x27 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0x28-0x2f */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0x30-0x37 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0x38-0x3f */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0x40-0x47 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0x48-0x4f */
+ 0x00, 0xb8, 0x90, 0x83, 0xba, 0xbe, 0xb3, 0xbf, /* 0x50-0x57 */
+ 0xbc, 0x9a, 0x9c, 0x9e, 0x9d, 0x00, 0xa2, 0x9f, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0xa5, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+};
+
+static unsigned char page20[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x96, 0x97, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x91, 0x92, 0x82, 0x00, 0x93, 0x94, 0x84, 0x00, /* 0x18-0x1f */
+ 0x86, 0x87, 0x95, 0x00, 0x00, 0x00, 0x85, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x8b, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+};
+
+static unsigned char page21[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+};
+
+static unsigned char *page_uni2charset[256] = {
+ page00, NULL, NULL, NULL, page04, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ page20, page21, NULL, NULL, NULL, NULL, NULL, NULL,
+};
+
+static unsigned char charset2lower[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+ 0x90, 0x83, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */
+ 0x88, 0x89, 0x9a, 0x8b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x88-0x8f */
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x98-0x9f */
+ 0xa0, 0xa2, 0xa2, 0xbc, 0xa4, 0xb4, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xb8, 0xa9, 0xba, 0xab, 0xac, 0xad, 0xae, 0xbf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb3, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbe, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xc0-0xc7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xc8-0xcf */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xd0-0xd7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static unsigned char charset2upper[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+ 0x80, 0x81, 0x82, 0x81, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0x88-0x8f */
+ 0x80, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */
+ 0x98, 0x99, 0x8a, 0x9b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa1, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb2, 0xa5, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xa8, 0xb9, 0xaa, 0xbb, 0xa3, 0xbd, 0xbd, 0xaf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xe0-0xe7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xe8-0xef */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xf0-0xf7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+ unsigned char *uni2charset;
+ unsigned char cl = uni & 0x00ff;
+ unsigned char ch = (uni & 0xff00) >> 8;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ uni2charset = page_uni2charset[ch];
+ if (uni2charset && uni2charset[cl])
+ out[0] = uni2charset[cl];
+ else
+ return -EINVAL;
+ return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+ *uni = charset2uni[*rawstring];
+ if (*uni == 0x0000)
+ return -EINVAL;
+ return 1;
+}
+
+static struct nls_table table = {
+ "cp1251",
+ uni2char,
+ char2uni,
+ charset2lower,
+ charset2upper,
+ THIS_MODULE,
+};
+
+static int __init init_nls_cp1251(void)
+{
+ return register_nls(&table);
+}
+
+static void __exit exit_nls_cp1251(void)
+{
+ unregister_nls(&table);
+}
+
+module_init(init_nls_cp1251)
+module_exit(exit_nls_cp1251)
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/fs/nls/nls_cp1255.c b/fs/nls/nls_cp1255.c
new file mode 100644
index 00000000000000..d6751422ecfc41
--- /dev/null
+++ b/fs/nls/nls_cp1255.c
@@ -0,0 +1,399 @@
+/*
+ * linux/fs/nls_cp1255.c
+ *
+ * Charset cp1255 translation tables.
+ * The Unicode to charset table has only exact mappings.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static wchar_t charset2uni[256] = {
+ /* 0x00*/
+ 0x0000, 0x0001, 0x0002, 0x0003,
+ 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b,
+ 0x000c, 0x000d, 0x000e, 0x000f,
+ /* 0x10*/
+ 0x0010, 0x0011, 0x0012, 0x0013,
+ 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b,
+ 0x001c, 0x001d, 0x001e, 0x001f,
+ /* 0x20*/
+ 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b,
+ 0x002c, 0x002d, 0x002e, 0x002f,
+ /* 0x30*/
+ 0x0030, 0x0031, 0x0032, 0x0033,
+ 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b,
+ 0x003c, 0x003d, 0x003e, 0x003f,
+ /* 0x40*/
+ 0x0040, 0x0041, 0x0042, 0x0043,
+ 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b,
+ 0x004c, 0x004d, 0x004e, 0x004f,
+ /* 0x50*/
+ 0x0050, 0x0051, 0x0052, 0x0053,
+ 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b,
+ 0x005c, 0x005d, 0x005e, 0x005f,
+ /* 0x60*/
+ 0x0060, 0x0061, 0x0062, 0x0063,
+ 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b,
+ 0x006c, 0x006d, 0x006e, 0x006f,
+ /* 0x70*/
+ 0x0070, 0x0071, 0x0072, 0x0073,
+ 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b,
+ 0x007c, 0x007d, 0x007e, 0x007f,
+ /* 0x80*/
+ 0x20ac, 0x0000, 0x201a, 0x0192,
+ 0x201e, 0x2026, 0x2020, 0x2021,
+ 0x02c6, 0x2030, 0x0000, 0x2039,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0x90*/
+ 0x0000, 0x2018, 0x2019, 0x201c,
+ 0x201d, 0x2022, 0x2013, 0x2014,
+ 0x02dc, 0x2122, 0x0000, 0x203a,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0xa0*/
+ 0x00a0, 0x00a1, 0x00a2, 0x00a3,
+ 0x20aa, 0x00a5, 0x00a6, 0x00a7,
+ 0x00a8, 0x00a9, 0x00d7, 0x00ab,
+ 0x00ac, 0x00ad, 0x00ae, 0x203e,
+ /* 0xb0*/
+ 0x00b0, 0x00b1, 0x00b2, 0x00b3,
+ 0x00b4, 0x00b5, 0x00b6, 0x00b7,
+ 0x00b8, 0x00b9, 0x00f7, 0x00bb,
+ 0x00bc, 0x00bd, 0x00be, 0x00bf,
+ /* 0xc0*/
+ 0x05b0, 0x05b1, 0x05b2, 0x05b3,
+ 0x05b4, 0x05b5, 0x05b6, 0x05b7,
+ 0x05b8, 0x05b9, 0x0000, 0x05bb,
+ 0x05bc, 0x05bd, 0x05be, 0x05bf,
+ /* 0xd0*/
+ 0x05c0, 0x05c1, 0x05c2, 0x05c3,
+ 0x05f0, 0x05f1, 0x05f2, 0x05f3,
+ 0x05f4, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x2017,
+ /* 0xe0*/
+ 0x05d0, 0x05d1, 0x05d2, 0x05d3,
+ 0x05d4, 0x05d5, 0x05d6, 0x05d7,
+ 0x05d8, 0x05d9, 0x05da, 0x05db,
+ 0x05dc, 0x05dd, 0x05de, 0x05df,
+ /* 0xf0*/
+ 0x05e0, 0x05e1, 0x05e2, 0x05e3,
+ 0x05e4, 0x05e5, 0x05e6, 0x05e7,
+ 0x05e8, 0x05e9, 0x05ea, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+};
+
+static unsigned char page00[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0x00, 0xa2, 0xa3, 0x00, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0x00, 0xab, 0xac, 0xad, 0xae, 0x00, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0x00, 0xbb, 0xbc, 0xbd, 0xbe, 0x00, /* 0xb8-0xbf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, /* 0xd0-0xd7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, /* 0xf0-0xf7 */
+};
+
+static unsigned char page01[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+};
+
+static unsigned char page02[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x00, /* 0xc0-0xc7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+ 0x00, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+};
+
+static unsigned char page05[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xb0-0xb7 */
+ 0xc8, 0xc9, 0x00, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xb8-0xbf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xd0-0xd7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xd8-0xdf */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xe0-0xe7 */
+ 0xf8, 0xf9, 0xfa, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+ 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+};
+
+static unsigned char page20[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0xfe, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x96, 0x97, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x91, 0x92, 0x82, 0x00, 0x93, 0x94, 0x84, 0x00, /* 0x18-0x1f */
+ 0x86, 0x87, 0x95, 0x00, 0x00, 0x00, 0x85, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x8b, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+ 0x00, 0x00, 0xa4, 0x00, 0x80, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+};
+
+static unsigned char page21[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+};
+
+static unsigned char *page_uni2charset[256] = {
+ page00, page01, page02, NULL, NULL, page05, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ page20, page21, NULL, NULL, NULL, NULL, NULL, NULL,
+};
+
+static unsigned char charset2lower[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40-0x47 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x48-0x4f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50-0x57 */
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0x00, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0x00, /* 0xb8-0xbf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static unsigned char charset2upper[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0x00, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0x00, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0x00, /* 0xb8-0xbf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+ unsigned char *uni2charset;
+ unsigned char cl = uni & 0x00ff;
+ unsigned char ch = (uni & 0xff00) >> 8;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ uni2charset = page_uni2charset[ch];
+ if (uni2charset && uni2charset[cl])
+ out[0] = uni2charset[cl];
+ else
+ return -EINVAL;
+ return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+ *uni = charset2uni[*rawstring];
+ if (*uni == 0x0000)
+ return -EINVAL;
+ return 1;
+}
+
+static struct nls_table table = {
+ "cp1255",
+ uni2char,
+ char2uni,
+ charset2lower,
+ charset2upper,
+ THIS_MODULE,
+};
+
+static int __init init_nls_cp1255(void)
+{
+ return register_nls(&table);
+}
+
+static void __exit exit_nls_cp1255(void)
+{
+ unregister_nls(&table);
+}
+
+module_init(init_nls_cp1255)
+module_exit(exit_nls_cp1255)
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/fs/nls/nls_cp437.c b/fs/nls/nls_cp437.c
new file mode 100644
index 00000000000000..bf433fee369c49
--- /dev/null
+++ b/fs/nls/nls_cp437.c
@@ -0,0 +1,404 @@
+/*
+ * linux/fs/nls_cp437.c
+ *
+ * Charset cp437 translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static wchar_t charset2uni[256] = {
+ /* 0x00*/
+ 0x0000, 0x0001, 0x0002, 0x0003,
+ 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b,
+ 0x000c, 0x000d, 0x000e, 0x000f,
+ /* 0x10*/
+ 0x0010, 0x0011, 0x0012, 0x0013,
+ 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b,
+ 0x001c, 0x001d, 0x001e, 0x001f,
+ /* 0x20*/
+ 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b,
+ 0x002c, 0x002d, 0x002e, 0x002f,
+ /* 0x30*/
+ 0x0030, 0x0031, 0x0032, 0x0033,
+ 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b,
+ 0x003c, 0x003d, 0x003e, 0x003f,
+ /* 0x40*/
+ 0x0040, 0x0041, 0x0042, 0x0043,
+ 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b,
+ 0x004c, 0x004d, 0x004e, 0x004f,
+ /* 0x50*/
+ 0x0050, 0x0051, 0x0052, 0x0053,
+ 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b,
+ 0x005c, 0x005d, 0x005e, 0x005f,
+ /* 0x60*/
+ 0x0060, 0x0061, 0x0062, 0x0063,
+ 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b,
+ 0x006c, 0x006d, 0x006e, 0x006f,
+ /* 0x70*/
+ 0x0070, 0x0071, 0x0072, 0x0073,
+ 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b,
+ 0x007c, 0x007d, 0x007e, 0x007f,
+ /* 0x80*/
+ 0x00c7, 0x00fc, 0x00e9, 0x00e2,
+ 0x00e4, 0x00e0, 0x00e5, 0x00e7,
+ 0x00ea, 0x00eb, 0x00e8, 0x00ef,
+ 0x00ee, 0x00ec, 0x00c4, 0x00c5,
+ /* 0x90*/
+ 0x00c9, 0x00e6, 0x00c6, 0x00f4,
+ 0x00f6, 0x00f2, 0x00fb, 0x00f9,
+ 0x00ff, 0x00d6, 0x00dc, 0x00a2,
+ 0x00a3, 0x00a5, 0x20a7, 0x0192,
+ /* 0xa0*/
+ 0x00e1, 0x00ed, 0x00f3, 0x00fa,
+ 0x00f1, 0x00d1, 0x00aa, 0x00ba,
+ 0x00bf, 0x2310, 0x00ac, 0x00bd,
+ 0x00bc, 0x00a1, 0x00ab, 0x00bb,
+ /* 0xb0*/
+ 0x2591, 0x2592, 0x2593, 0x2502,
+ 0x2524, 0x2561, 0x2562, 0x2556,
+ 0x2555, 0x2563, 0x2551, 0x2557,
+ 0x255d, 0x255c, 0x255b, 0x2510,
+ /* 0xc0*/
+ 0x2514, 0x2534, 0x252c, 0x251c,
+ 0x2500, 0x253c, 0x255e, 0x255f,
+ 0x255a, 0x2554, 0x2569, 0x2566,
+ 0x2560, 0x2550, 0x256c, 0x2567,
+ /* 0xd0*/
+ 0x2568, 0x2564, 0x2565, 0x2559,
+ 0x2558, 0x2552, 0x2553, 0x256b,
+ 0x256a, 0x2518, 0x250c, 0x2588,
+ 0x2584, 0x258c, 0x2590, 0x2580,
+ /* 0xe0*/
+ 0x03b1, 0x00df, 0x0393, 0x03c0,
+ 0x03a3, 0x03c3, 0x00b5, 0x03c4,
+ 0x03a6, 0x0398, 0x03a9, 0x03b4,
+ 0x221e, 0x03c6, 0x03b5, 0x2229,
+ /* 0xf0*/
+ 0x2261, 0x00b1, 0x2265, 0x2264,
+ 0x2320, 0x2321, 0x00f7, 0x2248,
+ 0x00b0, 0x2219, 0x00b7, 0x221a,
+ 0x207f, 0x00b2, 0x25a0, 0x00a0,
+};
+
+static unsigned char page00[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xff, 0xad, 0x9b, 0x9c, 0x00, 0x9d, 0x00, 0x00, /* 0xa0-0xa7 */
+ 0x00, 0x00, 0xa6, 0xae, 0xaa, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+ 0xf8, 0xf1, 0xfd, 0x00, 0x00, 0xe6, 0x00, 0xfa, /* 0xb0-0xb7 */
+ 0x00, 0x00, 0xa7, 0xaf, 0xac, 0xab, 0x00, 0xa8, /* 0xb8-0xbf */
+ 0x00, 0x00, 0x00, 0x00, 0x8e, 0x8f, 0x92, 0x80, /* 0xc0-0xc7 */
+ 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+ 0x00, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x99, 0x00, /* 0xd0-0xd7 */
+ 0x00, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0xe1, /* 0xd8-0xdf */
+ 0x85, 0xa0, 0x83, 0x00, 0x84, 0x86, 0x91, 0x87, /* 0xe0-0xe7 */
+ 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b, /* 0xe8-0xef */
+ 0x00, 0xa4, 0x95, 0xa2, 0x93, 0x00, 0x94, 0xf6, /* 0xf0-0xf7 */
+ 0x00, 0x97, 0xa3, 0x96, 0x81, 0x00, 0x00, 0x98, /* 0xf8-0xff */
+};
+
+static unsigned char page01[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+};
+
+static unsigned char page03[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0xe2, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0xe8, 0x00, /* 0xa0-0xa7 */
+ 0x00, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+ 0x00, 0xe0, 0x00, 0x00, 0xeb, 0xee, 0x00, 0x00, /* 0xb0-0xb7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+ 0xe3, 0x00, 0x00, 0xe5, 0xe7, 0x00, 0xed, 0x00, /* 0xc0-0xc7 */
+};
+
+static unsigned char page20[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, /* 0xa0-0xa7 */
+};
+
+static unsigned char page22[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0xf9, 0xfb, 0x00, 0x00, 0x00, 0xec, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0xf0, 0x00, 0x00, 0xf3, 0xf2, 0x00, 0x00, /* 0x60-0x67 */
+};
+
+static unsigned char page23[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0xa9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0xf4, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+};
+
+static unsigned char page25[256] = {
+ 0xc4, 0x00, 0xb3, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0xbf, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0xd9, 0x00, 0x00, 0x00, 0xc3, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0xcd, 0xba, 0xd5, 0xd6, 0xc9, 0xb8, 0xb7, 0xbb, /* 0x50-0x57 */
+ 0xd4, 0xd3, 0xc8, 0xbe, 0xbd, 0xbc, 0xc6, 0xc7, /* 0x58-0x5f */
+ 0xcc, 0xb5, 0xb6, 0xb9, 0xd1, 0xd2, 0xcb, 0xcf, /* 0x60-0x67 */
+ 0xd0, 0xca, 0xd8, 0xd7, 0xce, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0xdf, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0xdb, 0x00, 0x00, 0x00, 0xdd, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0xde, 0xb0, 0xb1, 0xb2, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+};
+
+static unsigned char *page_uni2charset[256] = {
+ page00, page01, NULL, page03, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ page20, NULL, page22, page23, NULL, page25, NULL, NULL,
+};
+
+static unsigned char charset2lower[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40-0x47 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x48-0x4f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50-0x57 */
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x87, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x84, 0x86, /* 0x88-0x8f */
+ 0x82, 0x91, 0x91, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */
+ 0x98, 0x94, 0x81, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa4, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0x00, 0xe3, 0xe5, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xed, 0x00, 0x00, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static unsigned char charset2upper[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x80, 0x9a, 0x90, 0x00, 0x8e, 0x00, 0x8f, 0x80, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8e, 0x8f, /* 0x88-0x8f */
+ 0x90, 0x92, 0x92, 0x00, 0x99, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x00, /* 0x98-0x9f */
+ 0x00, 0x00, 0x00, 0x00, 0xa5, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0x00, 0xe1, 0xe2, 0x00, 0xe4, 0xe4, 0x00, 0x00, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0x00, 0xec, 0xe8, 0x00, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+ unsigned char *uni2charset;
+ unsigned char cl = uni & 0x00ff;
+ unsigned char ch = (uni & 0xff00) >> 8;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ uni2charset = page_uni2charset[ch];
+ if (uni2charset && uni2charset[cl])
+ out[0] = uni2charset[cl];
+ else
+ return -EINVAL;
+ return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+ *uni = charset2uni[*rawstring];
+ if (*uni == 0x0000)
+ return -EINVAL;
+ return 1;
+}
+
+static struct nls_table table = {
+ "cp437",
+ uni2char,
+ char2uni,
+ charset2lower,
+ charset2upper,
+ THIS_MODULE,
+};
+
+static int __init init_nls_cp437(void)
+{
+ return register_nls(&table);
+}
+
+static void __exit exit_nls_cp437(void)
+{
+ unregister_nls(&table);
+}
+
+module_init(init_nls_cp437)
+module_exit(exit_nls_cp437)
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/fs/nls/nls_cp737.c b/fs/nls/nls_cp737.c
new file mode 100644
index 00000000000000..7ef8871b7d5250
--- /dev/null
+++ b/fs/nls/nls_cp737.c
@@ -0,0 +1,367 @@
+/*
+ * linux/fs/nls_cp737.c
+ *
+ * Charset cp737 translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static wchar_t charset2uni[256] = {
+ /* 0x00*/
+ 0x0000, 0x0001, 0x0002, 0x0003,
+ 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b,
+ 0x000c, 0x000d, 0x000e, 0x000f,
+ /* 0x10*/
+ 0x0010, 0x0011, 0x0012, 0x0013,
+ 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b,
+ 0x001c, 0x001d, 0x001e, 0x001f,
+ /* 0x20*/
+ 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b,
+ 0x002c, 0x002d, 0x002e, 0x002f,
+ /* 0x30*/
+ 0x0030, 0x0031, 0x0032, 0x0033,
+ 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b,
+ 0x003c, 0x003d, 0x003e, 0x003f,
+ /* 0x40*/
+ 0x0040, 0x0041, 0x0042, 0x0043,
+ 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b,
+ 0x004c, 0x004d, 0x004e, 0x004f,
+ /* 0x50*/
+ 0x0050, 0x0051, 0x0052, 0x0053,
+ 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b,
+ 0x005c, 0x005d, 0x005e, 0x005f,
+ /* 0x60*/
+ 0x0060, 0x0061, 0x0062, 0x0063,
+ 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b,
+ 0x006c, 0x006d, 0x006e, 0x006f,
+ /* 0x70*/
+ 0x0070, 0x0071, 0x0072, 0x0073,
+ 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b,
+ 0x007c, 0x007d, 0x007e, 0x007f,
+ /* 0x80*/
+ 0x0391, 0x0392, 0x0393, 0x0394,
+ 0x0395, 0x0396, 0x0397, 0x0398,
+ 0x0399, 0x039a, 0x039b, 0x039c,
+ 0x039d, 0x039e, 0x039f, 0x03a0,
+ /* 0x90*/
+ 0x03a1, 0x03a3, 0x03a4, 0x03a5,
+ 0x03a6, 0x03a7, 0x03a8, 0x03a9,
+ 0x03b1, 0x03b2, 0x03b3, 0x03b4,
+ 0x03b5, 0x03b6, 0x03b7, 0x03b8,
+ /* 0xa0*/
+ 0x03b9, 0x03ba, 0x03bb, 0x03bc,
+ 0x03bd, 0x03be, 0x03bf, 0x03c0,
+ 0x03c1, 0x03c3, 0x03c2, 0x03c4,
+ 0x03c5, 0x03c6, 0x03c7, 0x03c8,
+ /* 0xb0*/
+ 0x2591, 0x2592, 0x2593, 0x2502,
+ 0x2524, 0x2561, 0x2562, 0x2556,
+ 0x2555, 0x2563, 0x2551, 0x2557,
+ 0x255d, 0x255c, 0x255b, 0x2510,
+ /* 0xc0*/
+ 0x2514, 0x2534, 0x252c, 0x251c,
+ 0x2500, 0x253c, 0x255e, 0x255f,
+ 0x255a, 0x2554, 0x2569, 0x2566,
+ 0x2560, 0x2550, 0x256c, 0x2567,
+ /* 0xd0*/
+ 0x2568, 0x2564, 0x2565, 0x2559,
+ 0x2558, 0x2552, 0x2553, 0x256b,
+ 0x256a, 0x2518, 0x250c, 0x2588,
+ 0x2584, 0x258c, 0x2590, 0x2580,
+ /* 0xe0*/
+ 0x03c9, 0x03ac, 0x03ad, 0x03ae,
+ 0x03ca, 0x03af, 0x03cc, 0x03cd,
+ 0x03cb, 0x03ce, 0x0386, 0x0388,
+ 0x0389, 0x038a, 0x038c, 0x038e,
+ /* 0xf0*/
+ 0x038f, 0x00b1, 0x2265, 0x2264,
+ 0x03aa, 0x03ab, 0x00f7, 0x2248,
+ 0x00b0, 0x2219, 0x00b7, 0x221a,
+ 0x207f, 0x00b2, 0x25a0, 0x00a0,
+};
+
+static unsigned char page00[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+ 0xf8, 0xf1, 0xfd, 0x00, 0x00, 0x00, 0x00, 0xfa, /* 0xb0-0xb7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, /* 0xf0-0xf7 */
+};
+
+static unsigned char page03[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xea, 0x00, /* 0x80-0x87 */
+ 0xeb, 0xec, 0xed, 0x00, 0xee, 0x00, 0xef, 0xf0, /* 0x88-0x8f */
+ 0x00, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, /* 0x90-0x97 */
+ 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, /* 0x98-0x9f */
+ 0x8f, 0x90, 0x00, 0x91, 0x92, 0x93, 0x94, 0x95, /* 0xa0-0xa7 */
+ 0x96, 0x97, 0xf4, 0xf5, 0xe1, 0xe2, 0xe3, 0xe5, /* 0xa8-0xaf */
+ 0x00, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, /* 0xb0-0xb7 */
+ 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, /* 0xb8-0xbf */
+ 0xa7, 0xa8, 0xaa, 0xa9, 0xab, 0xac, 0xad, 0xae, /* 0xc0-0xc7 */
+ 0xaf, 0xe0, 0xe4, 0xe8, 0xe6, 0xe7, 0xe9, 0x00, /* 0xc8-0xcf */
+};
+
+static unsigned char page20[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, /* 0x78-0x7f */
+};
+
+static unsigned char page22[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0xf9, 0xfb, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0xf3, 0xf2, 0x00, 0x00, /* 0x60-0x67 */
+};
+
+static unsigned char page25[256] = {
+ 0xc4, 0x00, 0xb3, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0xbf, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0xd9, 0x00, 0x00, 0x00, 0xc3, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0xcd, 0xba, 0xd5, 0xd6, 0xc9, 0xb8, 0xb7, 0xbb, /* 0x50-0x57 */
+ 0xd4, 0xd3, 0xc8, 0xbe, 0xbd, 0xbc, 0xc6, 0xc7, /* 0x58-0x5f */
+ 0xcc, 0xb5, 0xb6, 0xb9, 0xd1, 0xd2, 0xcb, 0xcf, /* 0x60-0x67 */
+ 0xd0, 0xca, 0xd8, 0xd7, 0xce, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0xdf, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0xdb, 0x00, 0x00, 0x00, 0xdd, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0xde, 0xb0, 0xb1, 0xb2, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+};
+
+static unsigned char *page_uni2charset[256] = {
+ page00, NULL, NULL, page03, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ page20, NULL, page22, NULL, NULL, page25, NULL, NULL,
+};
+
+static unsigned char charset2lower[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40-0x47 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x48-0x4f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50-0x57 */
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x80-0x87 */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0x88-0x8f */
+ 0xa8, 0xa9, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xe0, /* 0x90-0x97 */
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xe1, 0xe2, 0xe3, 0xe5, 0xe6, 0xe7, /* 0xe8-0xef */
+ 0xe9, 0xf1, 0xf2, 0xf3, 0xe4, 0xe8, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static unsigned char charset2upper[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0x88-0x8f */
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x98-0x9f */
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0xa0-0xa7 */
+ 0x90, 0x91, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0x97, 0xea, 0xeb, 0xec, 0xf4, 0xed, 0xee, 0xef, /* 0xe0-0xe7 */
+ 0xf5, 0xf0, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+ unsigned char *uni2charset;
+ unsigned char cl = uni & 0x00ff;
+ unsigned char ch = (uni & 0xff00) >> 8;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ uni2charset = page_uni2charset[ch];
+ if (uni2charset && uni2charset[cl])
+ out[0] = uni2charset[cl];
+ else
+ return -EINVAL;
+ return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+ *uni = charset2uni[*rawstring];
+ if (*uni == 0x0000)
+ return -EINVAL;
+ return 1;
+}
+
+static struct nls_table table = {
+ "cp737",
+ uni2char,
+ char2uni,
+ charset2lower,
+ charset2upper,
+ THIS_MODULE,
+};
+
+static int __init init_nls_cp737(void)
+{
+ return register_nls(&table);
+}
+
+static void __exit exit_nls_cp737(void)
+{
+ unregister_nls(&table);
+}
+
+module_init(init_nls_cp737)
+module_exit(exit_nls_cp737)
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/fs/nls/nls_cp775.c b/fs/nls/nls_cp775.c
new file mode 100644
index 00000000000000..f1742f12497b03
--- /dev/null
+++ b/fs/nls/nls_cp775.c
@@ -0,0 +1,336 @@
+/*
+ * linux/fs/nls_cp775.c
+ *
+ * Charset cp775 translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static wchar_t charset2uni[256] = {
+ /* 0x00*/
+ 0x0000, 0x0001, 0x0002, 0x0003,
+ 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b,
+ 0x000c, 0x000d, 0x000e, 0x000f,
+ /* 0x10*/
+ 0x0010, 0x0011, 0x0012, 0x0013,
+ 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b,
+ 0x001c, 0x001d, 0x001e, 0x001f,
+ /* 0x20*/
+ 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b,
+ 0x002c, 0x002d, 0x002e, 0x002f,
+ /* 0x30*/
+ 0x0030, 0x0031, 0x0032, 0x0033,
+ 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b,
+ 0x003c, 0x003d, 0x003e, 0x003f,
+ /* 0x40*/
+ 0x0040, 0x0041, 0x0042, 0x0043,
+ 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b,
+ 0x004c, 0x004d, 0x004e, 0x004f,
+ /* 0x50*/
+ 0x0050, 0x0051, 0x0052, 0x0053,
+ 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b,
+ 0x005c, 0x005d, 0x005e, 0x005f,
+ /* 0x60*/
+ 0x0060, 0x0061, 0x0062, 0x0063,
+ 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b,
+ 0x006c, 0x006d, 0x006e, 0x006f,
+ /* 0x70*/
+ 0x0070, 0x0071, 0x0072, 0x0073,
+ 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b,
+ 0x007c, 0x007d, 0x007e, 0x007f,
+ /* 0x80*/
+ 0x0106, 0x00fc, 0x00e9, 0x0101,
+ 0x00e4, 0x0123, 0x00e5, 0x0107,
+ 0x0142, 0x0113, 0x0156, 0x0157,
+ 0x012b, 0x0179, 0x00c4, 0x00c5,
+ /* 0x90*/
+ 0x00c9, 0x00e6, 0x00c6, 0x014d,
+ 0x00f6, 0x0122, 0x00a2, 0x015a,
+ 0x015b, 0x00d6, 0x00dc, 0x00f8,
+ 0x00a3, 0x00d8, 0x00d7, 0x00a4,
+ /* 0xa0*/
+ 0x0100, 0x012a, 0x00f3, 0x017b,
+ 0x017c, 0x017a, 0x201d, 0x00a6,
+ 0x00a9, 0x00ae, 0x00ac, 0x00bd,
+ 0x00bc, 0x0141, 0x00ab, 0x00bb,
+ /* 0xb0*/
+ 0x2591, 0x2592, 0x2593, 0x2502,
+ 0x2524, 0x0104, 0x010c, 0x0118,
+ 0x0116, 0x2563, 0x2551, 0x2557,
+ 0x255d, 0x012e, 0x0160, 0x2510,
+ /* 0xc0*/
+ 0x2514, 0x2534, 0x252c, 0x251c,
+ 0x2500, 0x253c, 0x0172, 0x016a,
+ 0x255a, 0x2554, 0x2569, 0x2566,
+ 0x2560, 0x2550, 0x256c, 0x017d,
+ /* 0xd0*/
+ 0x0105, 0x010d, 0x0119, 0x0117,
+ 0x012f, 0x0161, 0x0173, 0x016b,
+ 0x017e, 0x2518, 0x250c, 0x2588,
+ 0x2584, 0x258c, 0x2590, 0x2580,
+ /* 0xe0*/
+ 0x00d3, 0x00df, 0x014c, 0x0143,
+ 0x00f5, 0x00d5, 0x00b5, 0x0144,
+ 0x0136, 0x0137, 0x013b, 0x013c,
+ 0x0146, 0x0112, 0x0145, 0x2019,
+ /* 0xf0*/
+ 0x00ad, 0x00b1, 0x201c, 0x00be,
+ 0x00b6, 0x00a7, 0x00f7, 0x201e,
+ 0x00b0, 0x2219, 0x00b7, 0x00b9,
+ 0x00b3, 0x00b2, 0x25a0, 0x00a0,
+};
+
+static unsigned char page00[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xff, 0x00, 0x96, 0x9c, 0x9f, 0x00, 0xa7, 0xf5, /* 0xa0-0xa7 */
+ 0x00, 0xa8, 0x00, 0xae, 0xaa, 0xf0, 0xa9, 0x00, /* 0xa8-0xaf */
+ 0xf8, 0xf1, 0xfd, 0xfc, 0x00, 0xe6, 0xf4, 0xfa, /* 0xb0-0xb7 */
+ 0x00, 0xfb, 0x00, 0xaf, 0xac, 0xab, 0xf3, 0x00, /* 0xb8-0xbf */
+ 0x00, 0x00, 0x00, 0x00, 0x8e, 0x8f, 0x92, 0x00, /* 0xc0-0xc7 */
+ 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+ 0x00, 0x00, 0x00, 0xe0, 0x00, 0xe5, 0x99, 0x9e, /* 0xd0-0xd7 */
+ 0x9d, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0xe1, /* 0xd8-0xdf */
+ 0x00, 0x00, 0x00, 0x00, 0x84, 0x86, 0x91, 0x00, /* 0xe0-0xe7 */
+ 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+ 0x00, 0x00, 0x00, 0xa2, 0x00, 0xe4, 0x94, 0xf6, /* 0xf0-0xf7 */
+ 0x9b, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static unsigned char page01[256] = {
+ 0xa0, 0x83, 0x00, 0x00, 0xb5, 0xd0, 0x80, 0x87, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0xb6, 0xd1, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0xed, 0x89, 0x00, 0x00, 0xb8, 0xd3, /* 0x10-0x17 */
+ 0xb7, 0xd2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x95, 0x85, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0xa1, 0x8c, 0x00, 0x00, 0xbd, 0xd4, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe8, 0xe9, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0xea, 0xeb, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0xad, 0x88, 0xe3, 0xe7, 0xee, 0xec, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0xe2, 0x93, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x8b, /* 0x50-0x57 */
+ 0x00, 0x00, 0x97, 0x98, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0xbe, 0xd5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0xc7, 0xd7, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0xc6, 0xd6, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x8d, 0xa5, 0xa3, 0xa4, 0xcf, 0xd8, 0x00, /* 0x78-0x7f */
+};
+
+static unsigned char page20[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0xef, 0x00, 0x00, 0xf2, 0xa6, 0xf7, 0x00, /* 0x18-0x1f */
+};
+
+static unsigned char page22[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+};
+
+static unsigned char page25[256] = {
+ 0xc4, 0x00, 0xb3, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0xbf, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0xd9, 0x00, 0x00, 0x00, 0xc3, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0xcd, 0xba, 0x00, 0x00, 0xc9, 0x00, 0x00, 0xbb, /* 0x50-0x57 */
+ 0x00, 0x00, 0xc8, 0x00, 0x00, 0xbc, 0x00, 0x00, /* 0x58-0x5f */
+ 0xcc, 0x00, 0x00, 0xb9, 0x00, 0x00, 0xcb, 0x00, /* 0x60-0x67 */
+ 0x00, 0xca, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0xdf, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0xdb, 0x00, 0x00, 0x00, 0xdd, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0xde, 0xb0, 0xb1, 0xb2, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+};
+
+static unsigned char *page_uni2charset[256] = {
+ page00, page01, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ page20, NULL, page22, NULL, NULL, page25, NULL, NULL,
+};
+
+static unsigned char charset2lower[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40-0x47 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x48-0x4f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50-0x57 */
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x87, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */
+ 0x88, 0x89, 0x8b, 0x8b, 0x8c, 0xa5, 0x84, 0x86, /* 0x88-0x8f */
+ 0x82, 0x91, 0x91, 0x93, 0x94, 0x85, 0x96, 0x98, /* 0x90-0x97 */
+ 0x98, 0x94, 0x81, 0x9b, 0x9c, 0x9b, 0x9e, 0x9f, /* 0x98-0x9f */
+ 0x83, 0x8c, 0xa2, 0xa4, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0x88, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xd0, 0xd1, 0xd2, /* 0xb0-0xb7 */
+ 0xd3, 0xb9, 0xba, 0xbb, 0xbc, 0xd4, 0xd5, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xd6, 0xd7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xd8, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xa2, 0xe1, 0x93, 0xe7, 0xe4, 0xe4, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe9, 0xe9, 0xeb, 0xeb, 0xec, 0x89, 0xec, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static unsigned char charset2upper[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x80, 0x9a, 0x90, 0xa0, 0x8e, 0x95, 0x8f, 0x80, /* 0x80-0x87 */
+ 0xad, 0xed, 0x8a, 0x8a, 0xa1, 0x8d, 0x8e, 0x8f, /* 0x88-0x8f */
+ 0x90, 0x92, 0x92, 0xe2, 0x99, 0x95, 0x96, 0x97, /* 0x90-0x97 */
+ 0x97, 0x99, 0x9a, 0x9d, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xe0, 0xa3, 0xa3, 0x8d, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xb5, 0xb6, 0xb7, 0xb8, 0xbd, 0xbe, 0xc6, 0xc7, /* 0xd0-0xd7 */
+ 0xcf, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe5, 0xe5, 0x00, 0xe3, /* 0xe0-0xe7 */
+ 0xe8, 0xe8, 0xea, 0xea, 0xee, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+ unsigned char *uni2charset;
+ unsigned char cl = uni & 0x00ff;
+ unsigned char ch = (uni & 0xff00) >> 8;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ uni2charset = page_uni2charset[ch];
+ if (uni2charset && uni2charset[cl])
+ out[0] = uni2charset[cl];
+ else
+ return -EINVAL;
+ return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+ *uni = charset2uni[*rawstring];
+ if (*uni == 0x0000)
+ return -EINVAL;
+ return 1;
+}
+
+static struct nls_table table = {
+ "cp775",
+ uni2char,
+ char2uni,
+ charset2lower,
+ charset2upper,
+ THIS_MODULE,
+};
+
+static int __init init_nls_cp775(void)
+{
+ return register_nls(&table);
+}
+
+static void __exit exit_nls_cp775(void)
+{
+ unregister_nls(&table);
+}
+
+module_init(init_nls_cp775)
+module_exit(exit_nls_cp775)
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/fs/nls/nls_cp850.c b/fs/nls/nls_cp850.c
new file mode 100644
index 00000000000000..772e324daa5cc8
--- /dev/null
+++ b/fs/nls/nls_cp850.c
@@ -0,0 +1,332 @@
+/*
+ * linux/fs/nls_cp850.c
+ *
+ * Charset cp850 translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static wchar_t charset2uni[256] = {
+ /* 0x00*/
+ 0x0000, 0x0001, 0x0002, 0x0003,
+ 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b,
+ 0x000c, 0x000d, 0x000e, 0x000f,
+ /* 0x10*/
+ 0x0010, 0x0011, 0x0012, 0x0013,
+ 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b,
+ 0x001c, 0x001d, 0x001e, 0x001f,
+ /* 0x20*/
+ 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b,
+ 0x002c, 0x002d, 0x002e, 0x002f,
+ /* 0x30*/
+ 0x0030, 0x0031, 0x0032, 0x0033,
+ 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b,
+ 0x003c, 0x003d, 0x003e, 0x003f,
+ /* 0x40*/
+ 0x0040, 0x0041, 0x0042, 0x0043,
+ 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b,
+ 0x004c, 0x004d, 0x004e, 0x004f,
+ /* 0x50*/
+ 0x0050, 0x0051, 0x0052, 0x0053,
+ 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b,
+ 0x005c, 0x005d, 0x005e, 0x005f,
+ /* 0x60*/
+ 0x0060, 0x0061, 0x0062, 0x0063,
+ 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b,
+ 0x006c, 0x006d, 0x006e, 0x006f,
+ /* 0x70*/
+ 0x0070, 0x0071, 0x0072, 0x0073,
+ 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b,
+ 0x007c, 0x007d, 0x007e, 0x007f,
+ /* 0x80*/
+ 0x00c7, 0x00fc, 0x00e9, 0x00e2,
+ 0x00e4, 0x00e0, 0x00e5, 0x00e7,
+ 0x00ea, 0x00eb, 0x00e8, 0x00ef,
+ 0x00ee, 0x00ec, 0x00c4, 0x00c5,
+ /* 0x90*/
+ 0x00c9, 0x00e6, 0x00c6, 0x00f4,
+ 0x00f6, 0x00f2, 0x00fb, 0x00f9,
+ 0x00ff, 0x00d6, 0x00dc, 0x00f8,
+ 0x00a3, 0x00d8, 0x00d7, 0x0192,
+ /* 0xa0*/
+ 0x00e1, 0x00ed, 0x00f3, 0x00fa,
+ 0x00f1, 0x00d1, 0x00aa, 0x00ba,
+ 0x00bf, 0x00ae, 0x00ac, 0x00bd,
+ 0x00bc, 0x00a1, 0x00ab, 0x00bb,
+ /* 0xb0*/
+ 0x2591, 0x2592, 0x2593, 0x2502,
+ 0x2524, 0x00c1, 0x00c2, 0x00c0,
+ 0x00a9, 0x2563, 0x2551, 0x2557,
+ 0x255d, 0x00a2, 0x00a5, 0x2510,
+ /* 0xc0*/
+ 0x2514, 0x2534, 0x252c, 0x251c,
+ 0x2500, 0x253c, 0x00e3, 0x00c3,
+ 0x255a, 0x2554, 0x2569, 0x2566,
+ 0x2560, 0x2550, 0x256c, 0x00a4,
+ /* 0xd0*/
+ 0x00f0, 0x00d0, 0x00ca, 0x00cb,
+ 0x00c8, 0x0131, 0x00cd, 0x00ce,
+ 0x00cf, 0x2518, 0x250c, 0x2588,
+ 0x2584, 0x00a6, 0x00cc, 0x2580,
+ /* 0xe0*/
+ 0x00d3, 0x00df, 0x00d4, 0x00d2,
+ 0x00f5, 0x00d5, 0x00b5, 0x00fe,
+ 0x00de, 0x00da, 0x00db, 0x00d9,
+ 0x00fd, 0x00dd, 0x00af, 0x00b4,
+ /* 0xf0*/
+ 0x00ad, 0x00b1, 0x2017, 0x00be,
+ 0x00b6, 0x00a7, 0x00f7, 0x00b8,
+ 0x00b0, 0x00a8, 0x00b7, 0x00b9,
+ 0x00b3, 0x00b2, 0x25a0, 0x00a0,
+};
+
+static unsigned char page00[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xff, 0xad, 0xbd, 0x9c, 0xcf, 0xbe, 0xdd, 0xf5, /* 0xa0-0xa7 */
+ 0xf9, 0xb8, 0xa6, 0xae, 0xaa, 0xf0, 0xa9, 0xee, /* 0xa8-0xaf */
+ 0xf8, 0xf1, 0xfd, 0xfc, 0xef, 0xe6, 0xf4, 0xfa, /* 0xb0-0xb7 */
+ 0xf7, 0xfb, 0xa7, 0xaf, 0xac, 0xab, 0xf3, 0xa8, /* 0xb8-0xbf */
+ 0xb7, 0xb5, 0xb6, 0xc7, 0x8e, 0x8f, 0x92, 0x80, /* 0xc0-0xc7 */
+ 0xd4, 0x90, 0xd2, 0xd3, 0xde, 0xd6, 0xd7, 0xd8, /* 0xc8-0xcf */
+ 0xd1, 0xa5, 0xe3, 0xe0, 0xe2, 0xe5, 0x99, 0x9e, /* 0xd0-0xd7 */
+ 0x9d, 0xeb, 0xe9, 0xea, 0x9a, 0xed, 0xe8, 0xe1, /* 0xd8-0xdf */
+ 0x85, 0xa0, 0x83, 0xc6, 0x84, 0x86, 0x91, 0x87, /* 0xe0-0xe7 */
+ 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b, /* 0xe8-0xef */
+ 0xd0, 0xa4, 0x95, 0xa2, 0x93, 0xe4, 0x94, 0xf6, /* 0xf0-0xf7 */
+ 0x9b, 0x97, 0xa3, 0x96, 0x81, 0xec, 0xe7, 0x98, /* 0xf8-0xff */
+};
+
+static unsigned char page01[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0xd5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+};
+
+static unsigned char page20[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf2, /* 0x10-0x17 */
+};
+
+static unsigned char page25[256] = {
+ 0xc4, 0x00, 0xb3, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0xbf, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0xd9, 0x00, 0x00, 0x00, 0xc3, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0xcd, 0xba, 0x00, 0x00, 0xc9, 0x00, 0x00, 0xbb, /* 0x50-0x57 */
+ 0x00, 0x00, 0xc8, 0x00, 0x00, 0xbc, 0x00, 0x00, /* 0x58-0x5f */
+ 0xcc, 0x00, 0x00, 0xb9, 0x00, 0x00, 0xcb, 0x00, /* 0x60-0x67 */
+ 0x00, 0xca, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0xdf, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0xdb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0xb0, 0xb1, 0xb2, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+};
+
+static unsigned char *page_uni2charset[256] = {
+ page00, page01, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ page20, NULL, NULL, NULL, NULL, page25, NULL, NULL,
+};
+
+static unsigned char charset2lower[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40-0x47 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x48-0x4f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50-0x57 */
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x87, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x84, 0x86, /* 0x88-0x8f */
+ 0x82, 0x91, 0x91, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */
+ 0x98, 0x94, 0x81, 0x9b, 0x9c, 0x9b, 0x9e, 0x9f, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa4, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xa0, 0x83, 0x85, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc6, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd0, 0x88, 0x89, 0x8a, 0xd5, 0xa1, 0x8c, /* 0xd0-0xd7 */
+ 0x8b, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0x8d, 0xdf, /* 0xd8-0xdf */
+ 0xa2, 0xe1, 0x93, 0x95, 0xe4, 0xe4, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe7, 0xa3, 0x96, 0x97, 0xec, 0xec, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static unsigned char charset2upper[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x80, 0x9a, 0x90, 0xb6, 0x8e, 0xb7, 0x8f, 0x80, /* 0x80-0x87 */
+ 0xd2, 0xd3, 0xd4, 0xd8, 0xd7, 0xde, 0x8e, 0x8f, /* 0x88-0x8f */
+ 0x90, 0x92, 0x92, 0xe2, 0x99, 0xe3, 0xea, 0xeb, /* 0x90-0x97 */
+ 0x00, 0x99, 0x9a, 0x9d, 0x9c, 0x9d, 0x9e, 0x00, /* 0x98-0x9f */
+ 0xb5, 0xd6, 0xe0, 0xe9, 0xa5, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc7, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd1, 0xd1, 0xd2, 0xd3, 0xd4, 0x49, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe5, 0xe5, 0x00, 0xe8, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xed, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+ unsigned char *uni2charset;
+ unsigned char cl = uni & 0x00ff;
+ unsigned char ch = (uni & 0xff00) >> 8;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ uni2charset = page_uni2charset[ch];
+ if (uni2charset && uni2charset[cl])
+ out[0] = uni2charset[cl];
+ else
+ return -EINVAL;
+ return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+ *uni = charset2uni[*rawstring];
+ if (*uni == 0x0000)
+ return -EINVAL;
+ return 1;
+}
+
+static struct nls_table table = {
+ "cp850",
+ uni2char,
+ char2uni,
+ charset2lower,
+ charset2upper,
+ THIS_MODULE,
+};
+
+static int __init init_nls_cp850(void)
+{
+ return register_nls(&table);
+}
+
+static void __exit exit_nls_cp850(void)
+{
+ unregister_nls(&table);
+}
+
+module_init(init_nls_cp850)
+module_exit(exit_nls_cp850)
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/fs/nls/nls_cp852.c b/fs/nls/nls_cp852.c
new file mode 100644
index 00000000000000..48366f26b3c802
--- /dev/null
+++ b/fs/nls/nls_cp852.c
@@ -0,0 +1,354 @@
+/*
+ * linux/fs/nls_cp852.c
+ *
+ * Charset cp852 translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static wchar_t charset2uni[256] = {
+ /* 0x00*/
+ 0x0000, 0x0001, 0x0002, 0x0003,
+ 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b,
+ 0x000c, 0x000d, 0x000e, 0x000f,
+ /* 0x10*/
+ 0x0010, 0x0011, 0x0012, 0x0013,
+ 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b,
+ 0x001c, 0x001d, 0x001e, 0x001f,
+ /* 0x20*/
+ 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b,
+ 0x002c, 0x002d, 0x002e, 0x002f,
+ /* 0x30*/
+ 0x0030, 0x0031, 0x0032, 0x0033,
+ 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b,
+ 0x003c, 0x003d, 0x003e, 0x003f,
+ /* 0x40*/
+ 0x0040, 0x0041, 0x0042, 0x0043,
+ 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b,
+ 0x004c, 0x004d, 0x004e, 0x004f,
+ /* 0x50*/
+ 0x0050, 0x0051, 0x0052, 0x0053,
+ 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b,
+ 0x005c, 0x005d, 0x005e, 0x005f,
+ /* 0x60*/
+ 0x0060, 0x0061, 0x0062, 0x0063,
+ 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b,
+ 0x006c, 0x006d, 0x006e, 0x006f,
+ /* 0x70*/
+ 0x0070, 0x0071, 0x0072, 0x0073,
+ 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b,
+ 0x007c, 0x007d, 0x007e, 0x007f,
+ /* 0x80*/
+ 0x00c7, 0x00fc, 0x00e9, 0x00e2,
+ 0x00e4, 0x016f, 0x0107, 0x00e7,
+ 0x0142, 0x00eb, 0x0150, 0x0151,
+ 0x00ee, 0x0179, 0x00c4, 0x0106,
+ /* 0x90*/
+ 0x00c9, 0x0139, 0x013a, 0x00f4,
+ 0x00f6, 0x013d, 0x013e, 0x015a,
+ 0x015b, 0x00d6, 0x00dc, 0x0164,
+ 0x0165, 0x0141, 0x00d7, 0x010d,
+ /* 0xa0*/
+ 0x00e1, 0x00ed, 0x00f3, 0x00fa,
+ 0x0104, 0x0105, 0x017d, 0x017e,
+ 0x0118, 0x0119, 0x00ac, 0x017a,
+ 0x010c, 0x015f, 0x00ab, 0x00bb,
+ /* 0xb0*/
+ 0x2591, 0x2592, 0x2593, 0x2502,
+ 0x2524, 0x00c1, 0x00c2, 0x011a,
+ 0x015e, 0x2563, 0x2551, 0x2557,
+ 0x255d, 0x017b, 0x017c, 0x2510,
+ /* 0xc0*/
+ 0x2514, 0x2534, 0x252c, 0x251c,
+ 0x2500, 0x253c, 0x0102, 0x0103,
+ 0x255a, 0x2554, 0x2569, 0x2566,
+ 0x2560, 0x2550, 0x256c, 0x00a4,
+ /* 0xd0*/
+ 0x0111, 0x0110, 0x010e, 0x00cb,
+ 0x010f, 0x0147, 0x00cd, 0x00ce,
+ 0x011b, 0x2518, 0x250c, 0x2588,
+ 0x2584, 0x0162, 0x016e, 0x2580,
+ /* 0xe0*/
+ 0x00d3, 0x00df, 0x00d4, 0x0143,
+ 0x0144, 0x0148, 0x0160, 0x0161,
+ 0x0154, 0x00da, 0x0155, 0x0170,
+ 0x00fd, 0x00dd, 0x0163, 0x00b4,
+ /* 0xf0*/
+ 0x00ad, 0x02dd, 0x02db, 0x02c7,
+ 0x02d8, 0x00a7, 0x00f7, 0x00b8,
+ 0x00b0, 0x00a8, 0x02d9, 0x0171,
+ 0x0158, 0x0159, 0x25a0, 0x00a0,
+};
+
+static unsigned char page00[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xff, 0x00, 0x00, 0x00, 0xcf, 0x00, 0x00, 0xf5, /* 0xa0-0xa7 */
+ 0xf9, 0x00, 0x00, 0xae, 0xaa, 0xf0, 0x00, 0x00, /* 0xa8-0xaf */
+ 0xf8, 0x00, 0x00, 0x00, 0xef, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+ 0xf7, 0x00, 0x00, 0xaf, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+ 0x00, 0xb5, 0xb6, 0x00, 0x8e, 0x00, 0x00, 0x80, /* 0xc0-0xc7 */
+ 0x00, 0x90, 0x00, 0xd3, 0x00, 0xd6, 0xd7, 0x00, /* 0xc8-0xcf */
+ 0x00, 0x00, 0x00, 0xe0, 0xe2, 0x00, 0x99, 0x9e, /* 0xd0-0xd7 */
+ 0x00, 0x00, 0xe9, 0x00, 0x9a, 0xed, 0x00, 0xe1, /* 0xd8-0xdf */
+ 0x00, 0xa0, 0x83, 0x00, 0x84, 0x00, 0x00, 0x87, /* 0xe0-0xe7 */
+ 0x00, 0x82, 0x00, 0x89, 0x00, 0xa1, 0x8c, 0x00, /* 0xe8-0xef */
+ 0x00, 0x00, 0x00, 0xa2, 0x93, 0x00, 0x94, 0xf6, /* 0xf0-0xf7 */
+ 0x00, 0x00, 0xa3, 0x00, 0x81, 0xec, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static unsigned char page01[256] = {
+ 0x00, 0x00, 0xc6, 0xc7, 0xa4, 0xa5, 0x8f, 0x86, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0xac, 0x9f, 0xd2, 0xd4, /* 0x08-0x0f */
+ 0xd1, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0xa8, 0xa9, 0xb7, 0xd8, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x91, 0x92, 0x00, 0x00, 0x95, 0x96, 0x00, /* 0x38-0x3f */
+ 0x00, 0x9d, 0x88, 0xe3, 0xe4, 0x00, 0x00, 0xd5, /* 0x40-0x47 */
+ 0xe5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x8a, 0x8b, 0x00, 0x00, 0xe8, 0xea, 0x00, 0x00, /* 0x50-0x57 */
+ 0xfc, 0xfd, 0x97, 0x98, 0x00, 0x00, 0xb8, 0xad, /* 0x58-0x5f */
+ 0xe6, 0xe7, 0xdd, 0xee, 0x9b, 0x9c, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x85, /* 0x68-0x6f */
+ 0xeb, 0xfb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x8d, 0xab, 0xbd, 0xbe, 0xa6, 0xa7, 0x00, /* 0x78-0x7f */
+};
+
+static unsigned char page02[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf3, /* 0xc0-0xc7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+ 0xf4, 0xfa, 0x00, 0xf2, 0x00, 0xf1, 0x00, 0x00, /* 0xd8-0xdf */
+};
+
+static unsigned char page25[256] = {
+ 0xc4, 0x00, 0xb3, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0xbf, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0xd9, 0x00, 0x00, 0x00, 0xc3, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0xcd, 0xba, 0x00, 0x00, 0xc9, 0x00, 0x00, 0xbb, /* 0x50-0x57 */
+ 0x00, 0x00, 0xc8, 0x00, 0x00, 0xbc, 0x00, 0x00, /* 0x58-0x5f */
+ 0xcc, 0x00, 0x00, 0xb9, 0x00, 0x00, 0xcb, 0x00, /* 0x60-0x67 */
+ 0x00, 0xca, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0xdf, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0xdb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0xb0, 0xb1, 0xb2, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+};
+
+static unsigned char *page_uni2charset[256] = {
+ page00, page01, page02, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, page25, NULL, NULL,
+};
+
+static unsigned char charset2lower[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40-0x47 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x48-0x4f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50-0x57 */
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x87, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */
+ 0x88, 0x89, 0x8b, 0x8b, 0x8c, 0xab, 0x84, 0x86, /* 0x88-0x8f */
+ 0x82, 0x92, 0x92, 0x93, 0x94, 0x96, 0x96, 0x98, /* 0x90-0x97 */
+ 0x98, 0x94, 0x81, 0x9c, 0x9c, 0x88, 0x9e, 0x9f, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa5, 0xa5, 0xa7, 0xa7, /* 0xa0-0xa7 */
+ 0xa9, 0xa9, 0xaa, 0xab, 0x9f, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xa0, 0x83, 0xd8, /* 0xb0-0xb7 */
+ 0xad, 0xb9, 0xba, 0xbb, 0xbc, 0xbe, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc7, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd0, 0xd4, 0x89, 0xd4, 0xe5, 0xa1, 0x8c, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xee, 0x85, 0xdf, /* 0xd8-0xdf */
+ 0xa2, 0xe1, 0x93, 0xe4, 0xe4, 0xe5, 0xe7, 0xe7, /* 0xe0-0xe7 */
+ 0xea, 0xa3, 0xea, 0xfb, 0xec, 0xec, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfd, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static unsigned char charset2upper[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x80, 0x9a, 0x90, 0xb6, 0x8e, 0xde, 0x8f, 0x80, /* 0x80-0x87 */
+ 0x9d, 0xd3, 0x8a, 0x8a, 0xd7, 0x8d, 0x8e, 0x8f, /* 0x88-0x8f */
+ 0x90, 0x91, 0x91, 0xe2, 0x99, 0x95, 0x95, 0x97, /* 0x90-0x97 */
+ 0x97, 0x99, 0x9a, 0x9b, 0x9b, 0x9d, 0x9e, 0xac, /* 0x98-0x9f */
+ 0xb5, 0xd6, 0xe0, 0xe9, 0xa4, 0xa4, 0xa6, 0xa6, /* 0xa0-0xa7 */
+ 0xa8, 0xa8, 0xaa, 0x8d, 0xac, 0xb8, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbd, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc6, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd1, 0xd1, 0xd2, 0xd3, 0xd2, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xb7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe3, 0xd5, 0xe6, 0xe6, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xe8, 0xeb, 0xed, 0xed, 0xdd, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xeb, 0xfc, 0xfc, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+ unsigned char *uni2charset;
+ unsigned char cl = uni & 0x00ff;
+ unsigned char ch = (uni & 0xff00) >> 8;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ uni2charset = page_uni2charset[ch];
+ if (uni2charset && uni2charset[cl])
+ out[0] = uni2charset[cl];
+ else
+ return -EINVAL;
+ return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+ *uni = charset2uni[*rawstring];
+ if (*uni == 0x0000)
+ return -EINVAL;
+ return 1;
+}
+
+static struct nls_table table = {
+ "cp852",
+ uni2char,
+ char2uni,
+ charset2lower,
+ charset2upper,
+ THIS_MODULE,
+};
+
+static int __init init_nls_cp852(void)
+{
+ return register_nls(&table);
+}
+
+static void __exit exit_nls_cp852(void)
+{
+ unregister_nls(&table);
+}
+
+module_init(init_nls_cp852)
+module_exit(exit_nls_cp852)
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/fs/nls/nls_cp855.c b/fs/nls/nls_cp855.c
new file mode 100644
index 00000000000000..e95e89b464fe05
--- /dev/null
+++ b/fs/nls/nls_cp855.c
@@ -0,0 +1,316 @@
+/*
+ * linux/fs/nls_cp855.c
+ *
+ * Charset cp855 translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static wchar_t charset2uni[256] = {
+ /* 0x00*/
+ 0x0000, 0x0001, 0x0002, 0x0003,
+ 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b,
+ 0x000c, 0x000d, 0x000e, 0x000f,
+ /* 0x10*/
+ 0x0010, 0x0011, 0x0012, 0x0013,
+ 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b,
+ 0x001c, 0x001d, 0x001e, 0x001f,
+ /* 0x20*/
+ 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b,
+ 0x002c, 0x002d, 0x002e, 0x002f,
+ /* 0x30*/
+ 0x0030, 0x0031, 0x0032, 0x0033,
+ 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b,
+ 0x003c, 0x003d, 0x003e, 0x003f,
+ /* 0x40*/
+ 0x0040, 0x0041, 0x0042, 0x0043,
+ 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b,
+ 0x004c, 0x004d, 0x004e, 0x004f,
+ /* 0x50*/
+ 0x0050, 0x0051, 0x0052, 0x0053,
+ 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b,
+ 0x005c, 0x005d, 0x005e, 0x005f,
+ /* 0x60*/
+ 0x0060, 0x0061, 0x0062, 0x0063,
+ 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b,
+ 0x006c, 0x006d, 0x006e, 0x006f,
+ /* 0x70*/
+ 0x0070, 0x0071, 0x0072, 0x0073,
+ 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b,
+ 0x007c, 0x007d, 0x007e, 0x007f,
+ /* 0x80*/
+ 0x0452, 0x0402, 0x0453, 0x0403,
+ 0x0451, 0x0401, 0x0454, 0x0404,
+ 0x0455, 0x0405, 0x0456, 0x0406,
+ 0x0457, 0x0407, 0x0458, 0x0408,
+ /* 0x90*/
+ 0x0459, 0x0409, 0x045a, 0x040a,
+ 0x045b, 0x040b, 0x045c, 0x040c,
+ 0x045e, 0x040e, 0x045f, 0x040f,
+ 0x044e, 0x042e, 0x044a, 0x042a,
+ /* 0xa0*/
+ 0x0430, 0x0410, 0x0431, 0x0411,
+ 0x0446, 0x0426, 0x0434, 0x0414,
+ 0x0435, 0x0415, 0x0444, 0x0424,
+ 0x0433, 0x0413, 0x00ab, 0x00bb,
+ /* 0xb0*/
+ 0x2591, 0x2592, 0x2593, 0x2502,
+ 0x2524, 0x0445, 0x0425, 0x0438,
+ 0x0418, 0x2563, 0x2551, 0x2557,
+ 0x255d, 0x0439, 0x0419, 0x2510,
+ /* 0xc0*/
+ 0x2514, 0x2534, 0x252c, 0x251c,
+ 0x2500, 0x253c, 0x043a, 0x041a,
+ 0x255a, 0x2554, 0x2569, 0x2566,
+ 0x2560, 0x2550, 0x256c, 0x00a4,
+ /* 0xd0*/
+ 0x043b, 0x041b, 0x043c, 0x041c,
+ 0x043d, 0x041d, 0x043e, 0x041e,
+ 0x043f, 0x2518, 0x250c, 0x2588,
+ 0x2584, 0x041f, 0x044f, 0x2580,
+ /* 0xe0*/
+ 0x042f, 0x0440, 0x0420, 0x0441,
+ 0x0421, 0x0442, 0x0422, 0x0443,
+ 0x0423, 0x0436, 0x0416, 0x0432,
+ 0x0412, 0x044c, 0x042c, 0x2116,
+ /* 0xf0*/
+ 0x00ad, 0x044b, 0x042b, 0x0437,
+ 0x0417, 0x0448, 0x0428, 0x044d,
+ 0x042d, 0x0449, 0x0429, 0x0447,
+ 0x0427, 0x00a7, 0x25a0, 0x00a0,
+};
+
+static unsigned char page00[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xff, 0x00, 0x00, 0x00, 0xcf, 0x00, 0x00, 0xfd, /* 0xa0-0xa7 */
+ 0x00, 0x00, 0x00, 0xae, 0x00, 0xf0, 0x00, 0x00, /* 0xa8-0xaf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+ 0x00, 0x00, 0x00, 0xaf, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+};
+
+static unsigned char page04[256] = {
+ 0x00, 0x85, 0x81, 0x83, 0x87, 0x89, 0x8b, 0x8d, /* 0x00-0x07 */
+ 0x8f, 0x91, 0x93, 0x95, 0x97, 0x00, 0x99, 0x9b, /* 0x08-0x0f */
+ 0xa1, 0xa3, 0xec, 0xad, 0xa7, 0xa9, 0xea, 0xf4, /* 0x10-0x17 */
+ 0xb8, 0xbe, 0xc7, 0xd1, 0xd3, 0xd5, 0xd7, 0xdd, /* 0x18-0x1f */
+ 0xe2, 0xe4, 0xe6, 0xe8, 0xab, 0xb6, 0xa5, 0xfc, /* 0x20-0x27 */
+ 0xf6, 0xfa, 0x9f, 0xf2, 0xee, 0xf8, 0x9d, 0xe0, /* 0x28-0x2f */
+ 0xa0, 0xa2, 0xeb, 0xac, 0xa6, 0xa8, 0xe9, 0xf3, /* 0x30-0x37 */
+ 0xb7, 0xbd, 0xc6, 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, /* 0x38-0x3f */
+ 0xe1, 0xe3, 0xe5, 0xe7, 0xaa, 0xb5, 0xa4, 0xfb, /* 0x40-0x47 */
+ 0xf5, 0xf9, 0x9e, 0xf1, 0xed, 0xf7, 0x9c, 0xde, /* 0x48-0x4f */
+ 0x00, 0x84, 0x80, 0x82, 0x86, 0x88, 0x8a, 0x8c, /* 0x50-0x57 */
+ 0x8e, 0x90, 0x92, 0x94, 0x96, 0x00, 0x98, 0x9a, /* 0x58-0x5f */
+};
+
+static unsigned char page21[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0x00, /* 0x10-0x17 */
+};
+
+static unsigned char page25[256] = {
+ 0xc4, 0x00, 0xb3, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0xbf, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0xd9, 0x00, 0x00, 0x00, 0xc3, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0xcd, 0xba, 0x00, 0x00, 0xc9, 0x00, 0x00, 0xbb, /* 0x50-0x57 */
+ 0x00, 0x00, 0xc8, 0x00, 0x00, 0xbc, 0x00, 0x00, /* 0x58-0x5f */
+ 0xcc, 0x00, 0x00, 0xb9, 0x00, 0x00, 0xcb, 0x00, /* 0x60-0x67 */
+ 0x00, 0xca, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0xdf, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0xdb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0xb0, 0xb1, 0xb2, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+};
+
+static unsigned char *page_uni2charset[256] = {
+ page00, NULL, NULL, NULL, page04, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, page21, NULL, NULL, NULL, page25, NULL, NULL,
+};
+
+static unsigned char charset2lower[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40-0x47 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x48-0x4f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50-0x57 */
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x80, 0x80, 0x82, 0x82, 0x84, 0x84, 0x86, 0x86, /* 0x80-0x87 */
+ 0x88, 0x88, 0x8a, 0x8a, 0x8c, 0x8c, 0x8e, 0x8e, /* 0x88-0x8f */
+ 0x90, 0x90, 0x92, 0x92, 0x94, 0x94, 0x96, 0x96, /* 0x90-0x97 */
+ 0x98, 0x98, 0x9a, 0x9a, 0x9c, 0x9c, 0x9e, 0x9e, /* 0x98-0x9f */
+ 0xa0, 0xa0, 0xa2, 0xa2, 0xa4, 0xa4, 0xa6, 0xa6, /* 0xa0-0xa7 */
+ 0xa8, 0xa8, 0xaa, 0xaa, 0xac, 0xac, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb5, 0xb7, /* 0xb0-0xb7 */
+ 0xb7, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbd, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc6, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd0, 0xd2, 0xd2, 0xd4, 0xd4, 0xd6, 0xd6, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xd8, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xde, 0xe1, 0xe1, 0xe3, 0xe3, 0xe5, 0xe5, 0xe7, /* 0xe0-0xe7 */
+ 0xe7, 0xe9, 0xe9, 0xeb, 0xeb, 0xed, 0xed, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf1, 0xf3, 0xf3, 0xf5, 0xf5, 0xf7, /* 0xf0-0xf7 */
+ 0xf7, 0xf9, 0xf9, 0xfb, 0xfb, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static unsigned char charset2upper[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x81, 0x81, 0x83, 0x83, 0x85, 0x85, 0x87, 0x87, /* 0x80-0x87 */
+ 0x89, 0x89, 0x8b, 0x8b, 0x8d, 0x8d, 0x8f, 0x8f, /* 0x88-0x8f */
+ 0x91, 0x91, 0x93, 0x93, 0x95, 0x95, 0x97, 0x97, /* 0x90-0x97 */
+ 0x99, 0x99, 0x9b, 0x9b, 0x9d, 0x9d, 0x9f, 0x9f, /* 0x98-0x9f */
+ 0xa1, 0xa1, 0xa3, 0xa3, 0xa5, 0xa5, 0xa7, 0xa7, /* 0xa0-0xa7 */
+ 0xa9, 0xa9, 0xab, 0xab, 0xad, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb6, 0xb6, 0xb8, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbe, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc7, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd1, 0xd1, 0xd3, 0xd3, 0xd5, 0xd5, 0xd7, 0xd7, /* 0xd0-0xd7 */
+ 0xdd, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xe0, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe2, 0xe2, 0xe4, 0xe4, 0xe6, 0xe6, 0xe8, /* 0xe0-0xe7 */
+ 0xe8, 0xea, 0xea, 0xec, 0xec, 0xee, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf2, 0xf2, 0xf4, 0xf4, 0xf6, 0xf6, 0xf8, /* 0xf0-0xf7 */
+ 0xf8, 0xfa, 0xfa, 0xfc, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+ unsigned char *uni2charset;
+ unsigned char cl = uni & 0x00ff;
+ unsigned char ch = (uni & 0xff00) >> 8;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ uni2charset = page_uni2charset[ch];
+ if (uni2charset && uni2charset[cl])
+ out[0] = uni2charset[cl];
+ else
+ return -EINVAL;
+ return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+ *uni = charset2uni[*rawstring];
+ if (*uni == 0x0000)
+ return -EINVAL;
+ return 1;
+}
+
+static struct nls_table table = {
+ "cp855",
+ uni2char,
+ char2uni,
+ charset2lower,
+ charset2upper,
+ THIS_MODULE,
+};
+
+static int __init init_nls_cp855(void)
+{
+ return register_nls(&table);
+}
+
+static void __exit exit_nls_cp855(void)
+{
+ unregister_nls(&table);
+}
+
+module_init(init_nls_cp855)
+module_exit(exit_nls_cp855)
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/fs/nls/nls_cp857.c b/fs/nls/nls_cp857.c
new file mode 100644
index 00000000000000..dc849b33bad164
--- /dev/null
+++ b/fs/nls/nls_cp857.c
@@ -0,0 +1,318 @@
+/*
+ * linux/fs/nls_cp857.c
+ *
+ * Charset cp857 translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static wchar_t charset2uni[256] = {
+ /* 0x00*/
+ 0x0000, 0x0001, 0x0002, 0x0003,
+ 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b,
+ 0x000c, 0x000d, 0x000e, 0x000f,
+ /* 0x10*/
+ 0x0010, 0x0011, 0x0012, 0x0013,
+ 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b,
+ 0x001c, 0x001d, 0x001e, 0x001f,
+ /* 0x20*/
+ 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b,
+ 0x002c, 0x002d, 0x002e, 0x002f,
+ /* 0x30*/
+ 0x0030, 0x0031, 0x0032, 0x0033,
+ 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b,
+ 0x003c, 0x003d, 0x003e, 0x003f,
+ /* 0x40*/
+ 0x0040, 0x0041, 0x0042, 0x0043,
+ 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b,
+ 0x004c, 0x004d, 0x004e, 0x004f,
+ /* 0x50*/
+ 0x0050, 0x0051, 0x0052, 0x0053,
+ 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b,
+ 0x005c, 0x005d, 0x005e, 0x005f,
+ /* 0x60*/
+ 0x0060, 0x0061, 0x0062, 0x0063,
+ 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b,
+ 0x006c, 0x006d, 0x006e, 0x006f,
+ /* 0x70*/
+ 0x0070, 0x0071, 0x0072, 0x0073,
+ 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b,
+ 0x007c, 0x007d, 0x007e, 0x007f,
+ /* 0x80*/
+ 0x00c7, 0x00fc, 0x00e9, 0x00e2,
+ 0x00e4, 0x00e0, 0x00e5, 0x00e7,
+ 0x00ea, 0x00eb, 0x00e8, 0x00ef,
+ 0x00ee, 0x0131, 0x00c4, 0x00c5,
+ /* 0x90*/
+ 0x00c9, 0x00e6, 0x00c6, 0x00f4,
+ 0x00f6, 0x00f2, 0x00fb, 0x00f9,
+ 0x0130, 0x00d6, 0x00dc, 0x00f8,
+ 0x00a3, 0x00d8, 0x015e, 0x015f,
+ /* 0xa0*/
+ 0x00e1, 0x00ed, 0x00f3, 0x00fa,
+ 0x00f1, 0x00d1, 0x011e, 0x011f,
+ 0x00bf, 0x00ae, 0x00ac, 0x00bd,
+ 0x00bc, 0x00a1, 0x00ab, 0x00bb,
+ /* 0xb0*/
+ 0x2591, 0x2592, 0x2593, 0x2502,
+ 0x2524, 0x00c1, 0x00c2, 0x00c0,
+ 0x00a9, 0x2563, 0x2551, 0x2557,
+ 0x255d, 0x00a2, 0x00a5, 0x2510,
+ /* 0xc0*/
+ 0x2514, 0x2534, 0x252c, 0x251c,
+ 0x2500, 0x253c, 0x00e3, 0x00c3,
+ 0x255a, 0x2554, 0x2569, 0x2566,
+ 0x2560, 0x2550, 0x256c, 0x00a4,
+ /* 0xd0*/
+ 0x00ba, 0x00aa, 0x00ca, 0x00cb,
+ 0x00c8, 0x0000, 0x00cd, 0x00ce,
+ 0x00cf, 0x2518, 0x250c, 0x2588,
+ 0x2584, 0x00a6, 0x00cc, 0x2580,
+ /* 0xe0*/
+ 0x00d3, 0x00df, 0x00d4, 0x00d2,
+ 0x00f5, 0x00d5, 0x00b5, 0x0000,
+ 0x00d7, 0x00da, 0x00db, 0x00d9,
+ 0x00ec, 0x00ff, 0x00af, 0x00b4,
+ /* 0xf0*/
+ 0x00ad, 0x00b1, 0x0000, 0x00be,
+ 0x00b6, 0x00a7, 0x00f7, 0x00b8,
+ 0x00b0, 0x00a8, 0x00b7, 0x00b9,
+ 0x00b3, 0x00b2, 0x25a0, 0x00a0,
+};
+
+static unsigned char page00[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xff, 0xad, 0xbd, 0x9c, 0xcf, 0xbe, 0xdd, 0xf5, /* 0xa0-0xa7 */
+ 0xf9, 0xb8, 0xd1, 0xae, 0xaa, 0xf0, 0xa9, 0xee, /* 0xa8-0xaf */
+ 0xf8, 0xf1, 0xfd, 0xfc, 0xef, 0xe6, 0xf4, 0xfa, /* 0xb0-0xb7 */
+ 0xf7, 0xfb, 0xd0, 0xaf, 0xac, 0xab, 0xf3, 0xa8, /* 0xb8-0xbf */
+ 0xb7, 0xb5, 0xb6, 0xc7, 0x8e, 0x8f, 0x92, 0x80, /* 0xc0-0xc7 */
+ 0xd4, 0x90, 0xd2, 0xd3, 0xde, 0xd6, 0xd7, 0xd8, /* 0xc8-0xcf */
+ 0x00, 0xa5, 0xe3, 0xe0, 0xe2, 0xe5, 0x99, 0xe8, /* 0xd0-0xd7 */
+ 0x9d, 0xeb, 0xe9, 0xea, 0x9a, 0x00, 0x00, 0xe1, /* 0xd8-0xdf */
+ 0x85, 0xa0, 0x83, 0xc6, 0x84, 0x86, 0x91, 0x87, /* 0xe0-0xe7 */
+ 0x8a, 0x82, 0x88, 0x89, 0xec, 0xa1, 0x8c, 0x8b, /* 0xe8-0xef */
+ 0x00, 0xa4, 0x95, 0xa2, 0x93, 0xe4, 0x94, 0xf6, /* 0xf0-0xf7 */
+ 0x9b, 0x97, 0xa3, 0x96, 0x81, 0x00, 0x00, 0xed, /* 0xf8-0xff */
+};
+
+static unsigned char page01[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa6, 0xa7, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x98, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x9f, /* 0x58-0x5f */
+};
+
+static unsigned char page25[256] = {
+ 0xc4, 0x00, 0xb3, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0xbf, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0xd9, 0x00, 0x00, 0x00, 0xc3, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0xcd, 0xba, 0x00, 0x00, 0xc9, 0x00, 0x00, 0xbb, /* 0x50-0x57 */
+ 0x00, 0x00, 0xc8, 0x00, 0x00, 0xbc, 0x00, 0x00, /* 0x58-0x5f */
+ 0xcc, 0x00, 0x00, 0xb9, 0x00, 0x00, 0xcb, 0x00, /* 0x60-0x67 */
+ 0x00, 0xca, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0xdf, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0xdb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0xb0, 0xb1, 0xb2, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+};
+
+static unsigned char *page_uni2charset[256] = {
+ page00, page01, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, page25, NULL, NULL,
+};
+
+static unsigned char charset2lower[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40-0x47 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x48-0x4f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50-0x57 */
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x87, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x84, 0x86, /* 0x88-0x8f */
+ 0x82, 0x91, 0x91, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */
+ 0x69, 0x94, 0x81, 0x9b, 0x9c, 0x9b, 0x9f, 0x9f, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa4, 0xa7, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xa0, 0x83, 0x85, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc6, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0x88, 0x89, 0x8a, 0x00, 0xa1, 0x8c, /* 0xd0-0xd7 */
+ 0x8b, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xec, 0xdf, /* 0xd8-0xdf */
+ 0xa2, 0xe1, 0x93, 0x95, 0xe4, 0xe4, 0xe6, 0x00, /* 0xe0-0xe7 */
+ 0xe8, 0xa3, 0x96, 0x97, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0x00, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static unsigned char charset2upper[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x80, 0x9a, 0x90, 0xb6, 0x8e, 0xb7, 0x8f, 0x80, /* 0x80-0x87 */
+ 0xd2, 0xd3, 0xd4, 0xd8, 0xd7, 0x49, 0x8e, 0x8f, /* 0x88-0x8f */
+ 0x90, 0x92, 0x92, 0xe2, 0x99, 0xe3, 0xea, 0xeb, /* 0x90-0x97 */
+ 0x98, 0x99, 0x9a, 0x9d, 0x9c, 0x9d, 0x9e, 0x9e, /* 0x98-0x9f */
+ 0xb5, 0xd6, 0xe0, 0xe9, 0xa5, 0xa5, 0xa6, 0xa6, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc7, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0x00, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe5, 0xe5, 0x00, 0x00, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xde, 0x00, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0x00, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+ unsigned char *uni2charset;
+ unsigned char cl = uni & 0x00ff;
+ unsigned char ch = (uni & 0xff00) >> 8;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ uni2charset = page_uni2charset[ch];
+ if (uni2charset && uni2charset[cl])
+ out[0] = uni2charset[cl];
+ else
+ return -EINVAL;
+ return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+ *uni = charset2uni[*rawstring];
+ if (*uni == 0x0000)
+ return -EINVAL;
+ return 1;
+}
+
+static struct nls_table table = {
+ "cp857",
+ uni2char,
+ char2uni,
+ charset2lower,
+ charset2upper,
+ THIS_MODULE,
+};
+
+static int __init init_nls_cp857(void)
+{
+ return register_nls(&table);
+}
+
+static void __exit exit_nls_cp857(void)
+{
+ unregister_nls(&table);
+}
+
+module_init(init_nls_cp857)
+module_exit(exit_nls_cp857)
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/fs/nls/nls_cp860.c b/fs/nls/nls_cp860.c
new file mode 100644
index 00000000000000..b9a219341314e3
--- /dev/null
+++ b/fs/nls/nls_cp860.c
@@ -0,0 +1,381 @@
+/*
+ * linux/fs/nls_cp860.c
+ *
+ * Charset cp860 translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static wchar_t charset2uni[256] = {
+ /* 0x00*/
+ 0x0000, 0x0001, 0x0002, 0x0003,
+ 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b,
+ 0x000c, 0x000d, 0x000e, 0x000f,
+ /* 0x10*/
+ 0x0010, 0x0011, 0x0012, 0x0013,
+ 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b,
+ 0x001c, 0x001d, 0x001e, 0x001f,
+ /* 0x20*/
+ 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b,
+ 0x002c, 0x002d, 0x002e, 0x002f,
+ /* 0x30*/
+ 0x0030, 0x0031, 0x0032, 0x0033,
+ 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b,
+ 0x003c, 0x003d, 0x003e, 0x003f,
+ /* 0x40*/
+ 0x0040, 0x0041, 0x0042, 0x0043,
+ 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b,
+ 0x004c, 0x004d, 0x004e, 0x004f,
+ /* 0x50*/
+ 0x0050, 0x0051, 0x0052, 0x0053,
+ 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b,
+ 0x005c, 0x005d, 0x005e, 0x005f,
+ /* 0x60*/
+ 0x0060, 0x0061, 0x0062, 0x0063,
+ 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b,
+ 0x006c, 0x006d, 0x006e, 0x006f,
+ /* 0x70*/
+ 0x0070, 0x0071, 0x0072, 0x0073,
+ 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b,
+ 0x007c, 0x007d, 0x007e, 0x007f,
+ /* 0x80*/
+ 0x00c7, 0x00fc, 0x00e9, 0x00e2,
+ 0x00e3, 0x00e0, 0x00c1, 0x00e7,
+ 0x00ea, 0x00ca, 0x00e8, 0x00cd,
+ 0x00d4, 0x00ec, 0x00c3, 0x00c2,
+ /* 0x90*/
+ 0x00c9, 0x00c0, 0x00c8, 0x00f4,
+ 0x00f5, 0x00f2, 0x00da, 0x00f9,
+ 0x00cc, 0x00d5, 0x00dc, 0x00a2,
+ 0x00a3, 0x00d9, 0x20a7, 0x00d3,
+ /* 0xa0*/
+ 0x00e1, 0x00ed, 0x00f3, 0x00fa,
+ 0x00f1, 0x00d1, 0x00aa, 0x00ba,
+ 0x00bf, 0x00d2, 0x00ac, 0x00bd,
+ 0x00bc, 0x00a1, 0x00ab, 0x00bb,
+ /* 0xb0*/
+ 0x2591, 0x2592, 0x2593, 0x2502,
+ 0x2524, 0x2561, 0x2562, 0x2556,
+ 0x2555, 0x2563, 0x2551, 0x2557,
+ 0x255d, 0x255c, 0x255b, 0x2510,
+ /* 0xc0*/
+ 0x2514, 0x2534, 0x252c, 0x251c,
+ 0x2500, 0x253c, 0x255e, 0x255f,
+ 0x255a, 0x2554, 0x2569, 0x2566,
+ 0x2560, 0x2550, 0x256c, 0x2567,
+ /* 0xd0*/
+ 0x2568, 0x2564, 0x2565, 0x2559,
+ 0x2558, 0x2552, 0x2553, 0x256b,
+ 0x256a, 0x2518, 0x250c, 0x2588,
+ 0x2584, 0x258c, 0x2590, 0x2580,
+ /* 0xe0*/
+ 0x03b1, 0x00df, 0x0393, 0x03c0,
+ 0x03a3, 0x03c3, 0x00b5, 0x03c4,
+ 0x03a6, 0x0398, 0x03a9, 0x03b4,
+ 0x221e, 0x03c6, 0x03b5, 0x2229,
+ /* 0xf0*/
+ 0x2261, 0x00b1, 0x2265, 0x2264,
+ 0x2320, 0x2321, 0x00f7, 0x2248,
+ 0x00b0, 0x2219, 0x00b7, 0x221a,
+ 0x207f, 0x00b2, 0x25a0, 0x00a0,
+};
+
+static unsigned char page00[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xff, 0xad, 0x9b, 0x9c, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+ 0x00, 0x00, 0xa6, 0xae, 0xaa, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+ 0xf8, 0xf1, 0xfd, 0x00, 0x00, 0xe6, 0x00, 0xfa, /* 0xb0-0xb7 */
+ 0x00, 0x00, 0xa7, 0xaf, 0xac, 0xab, 0x00, 0xa8, /* 0xb8-0xbf */
+ 0x91, 0x86, 0x8f, 0x8e, 0x00, 0x00, 0x00, 0x80, /* 0xc0-0xc7 */
+ 0x92, 0x90, 0x89, 0x00, 0x98, 0x8b, 0x00, 0x00, /* 0xc8-0xcf */
+ 0x00, 0xa5, 0xa9, 0x9f, 0x8c, 0x99, 0x00, 0x00, /* 0xd0-0xd7 */
+ 0x00, 0x9d, 0x96, 0x00, 0x9a, 0x00, 0x00, 0xe1, /* 0xd8-0xdf */
+ 0x85, 0xa0, 0x83, 0x84, 0x00, 0x00, 0x00, 0x87, /* 0xe0-0xe7 */
+ 0x8a, 0x82, 0x88, 0x00, 0x8d, 0xa1, 0x00, 0x00, /* 0xe8-0xef */
+ 0x00, 0xa4, 0x95, 0xa2, 0x93, 0x94, 0x00, 0xf6, /* 0xf0-0xf7 */
+ 0x00, 0x97, 0xa3, 0x00, 0x81, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static unsigned char page03[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0xe2, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0xe8, 0x00, /* 0xa0-0xa7 */
+ 0x00, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+ 0x00, 0xe0, 0x00, 0x00, 0xeb, 0xee, 0x00, 0x00, /* 0xb0-0xb7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+ 0xe3, 0x00, 0x00, 0xe5, 0xe7, 0x00, 0xed, 0x00, /* 0xc0-0xc7 */
+};
+
+static unsigned char page20[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, /* 0xa0-0xa7 */
+};
+
+static unsigned char page22[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0xf9, 0xfb, 0x00, 0x00, 0x00, 0xec, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0xf0, 0x00, 0x00, 0xf3, 0xf2, 0x00, 0x00, /* 0x60-0x67 */
+};
+
+static unsigned char page23[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0xf4, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+};
+
+static unsigned char page25[256] = {
+ 0xc4, 0x00, 0xb3, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0xbf, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0xd9, 0x00, 0x00, 0x00, 0xc3, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0xcd, 0xba, 0xd5, 0xd6, 0xc9, 0xb8, 0xb7, 0xbb, /* 0x50-0x57 */
+ 0xd4, 0xd3, 0xc8, 0xbe, 0xbd, 0xbc, 0xc6, 0xc7, /* 0x58-0x5f */
+ 0xcc, 0xb5, 0xb6, 0xb9, 0xd1, 0xd2, 0xcb, 0xcf, /* 0x60-0x67 */
+ 0xd0, 0xca, 0xd8, 0xd7, 0xce, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0xdf, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0xdb, 0x00, 0x00, 0x00, 0xdd, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0xde, 0xb0, 0xb1, 0xb2, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+};
+
+static unsigned char *page_uni2charset[256] = {
+ page00, NULL, NULL, page03, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ page20, NULL, page22, page23, NULL, page25, NULL, NULL,
+};
+
+static unsigned char charset2lower[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40-0x47 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x48-0x4f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50-0x57 */
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x87, 0x81, 0x82, 0x83, 0x84, 0x85, 0xa0, 0x87, /* 0x80-0x87 */
+ 0x88, 0x88, 0x8a, 0xa1, 0x93, 0x8d, 0x84, 0x83, /* 0x88-0x8f */
+ 0x82, 0x85, 0x8a, 0x93, 0x94, 0x95, 0xa3, 0x97, /* 0x90-0x97 */
+ 0x8d, 0x94, 0x81, 0x9b, 0x9c, 0x97, 0x9e, 0xa2, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa4, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0x95, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0x00, 0xe3, 0xe5, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xed, 0x00, 0x00, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static unsigned char charset2upper[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x80, 0x9a, 0x90, 0x8f, 0x8e, 0x91, 0x86, 0x80, /* 0x80-0x87 */
+ 0x89, 0x89, 0x92, 0x8b, 0x8c, 0x98, 0x8e, 0x8f, /* 0x88-0x8f */
+ 0x90, 0x91, 0x92, 0x8c, 0x99, 0xa9, 0x96, 0x9d, /* 0x90-0x97 */
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x98-0x9f */
+ 0x86, 0x8b, 0x9f, 0x96, 0xa5, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0x00, 0xe1, 0xe2, 0x00, 0xe4, 0xe4, 0x00, 0x00, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0x00, 0xec, 0xe8, 0x00, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+ unsigned char *uni2charset;
+ unsigned char cl = uni & 0x00ff;
+ unsigned char ch = (uni & 0xff00) >> 8;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ uni2charset = page_uni2charset[ch];
+ if (uni2charset && uni2charset[cl])
+ out[0] = uni2charset[cl];
+ else
+ return -EINVAL;
+ return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+ *uni = charset2uni[*rawstring];
+ if (*uni == 0x0000)
+ return -EINVAL;
+ return 1;
+}
+
+static struct nls_table table = {
+ "cp860",
+ uni2char,
+ char2uni,
+ charset2lower,
+ charset2upper,
+ THIS_MODULE,
+};
+
+static int __init init_nls_cp860(void)
+{
+ return register_nls(&table);
+}
+
+static void __exit exit_nls_cp860(void)
+{
+ unregister_nls(&table);
+}
+
+module_init(init_nls_cp860)
+module_exit(exit_nls_cp860)
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/fs/nls/nls_cp861.c b/fs/nls/nls_cp861.c
new file mode 100644
index 00000000000000..91c839b8949637
--- /dev/null
+++ b/fs/nls/nls_cp861.c
@@ -0,0 +1,404 @@
+/*
+ * linux/fs/nls_cp861.c
+ *
+ * Charset cp861 translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static wchar_t charset2uni[256] = {
+ /* 0x00*/
+ 0x0000, 0x0001, 0x0002, 0x0003,
+ 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b,
+ 0x000c, 0x000d, 0x000e, 0x000f,
+ /* 0x10*/
+ 0x0010, 0x0011, 0x0012, 0x0013,
+ 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b,
+ 0x001c, 0x001d, 0x001e, 0x001f,
+ /* 0x20*/
+ 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b,
+ 0x002c, 0x002d, 0x002e, 0x002f,
+ /* 0x30*/
+ 0x0030, 0x0031, 0x0032, 0x0033,
+ 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b,
+ 0x003c, 0x003d, 0x003e, 0x003f,
+ /* 0x40*/
+ 0x0040, 0x0041, 0x0042, 0x0043,
+ 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b,
+ 0x004c, 0x004d, 0x004e, 0x004f,
+ /* 0x50*/
+ 0x0050, 0x0051, 0x0052, 0x0053,
+ 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b,
+ 0x005c, 0x005d, 0x005e, 0x005f,
+ /* 0x60*/
+ 0x0060, 0x0061, 0x0062, 0x0063,
+ 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b,
+ 0x006c, 0x006d, 0x006e, 0x006f,
+ /* 0x70*/
+ 0x0070, 0x0071, 0x0072, 0x0073,
+ 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b,
+ 0x007c, 0x007d, 0x007e, 0x007f,
+ /* 0x80*/
+ 0x00c7, 0x00fc, 0x00e9, 0x00e2,
+ 0x00e4, 0x00e0, 0x00e5, 0x00e7,
+ 0x00ea, 0x00eb, 0x00e8, 0x00d0,
+ 0x00f0, 0x00de, 0x00c4, 0x00c5,
+ /* 0x90*/
+ 0x00c9, 0x00e6, 0x00c6, 0x00f4,
+ 0x00f6, 0x00fe, 0x00fb, 0x00dd,
+ 0x00fd, 0x00d6, 0x00dc, 0x00f8,
+ 0x00a3, 0x00d8, 0x20a7, 0x0192,
+ /* 0xa0*/
+ 0x00e1, 0x00ed, 0x00f3, 0x00fa,
+ 0x00c1, 0x00cd, 0x00d3, 0x00da,
+ 0x00bf, 0x2310, 0x00ac, 0x00bd,
+ 0x00bc, 0x00a1, 0x00ab, 0x00bb,
+ /* 0xb0*/
+ 0x2591, 0x2592, 0x2593, 0x2502,
+ 0x2524, 0x2561, 0x2562, 0x2556,
+ 0x2555, 0x2563, 0x2551, 0x2557,
+ 0x255d, 0x255c, 0x255b, 0x2510,
+ /* 0xc0*/
+ 0x2514, 0x2534, 0x252c, 0x251c,
+ 0x2500, 0x253c, 0x255e, 0x255f,
+ 0x255a, 0x2554, 0x2569, 0x2566,
+ 0x2560, 0x2550, 0x256c, 0x2567,
+ /* 0xd0*/
+ 0x2568, 0x2564, 0x2565, 0x2559,
+ 0x2558, 0x2552, 0x2553, 0x256b,
+ 0x256a, 0x2518, 0x250c, 0x2588,
+ 0x2584, 0x258c, 0x2590, 0x2580,
+ /* 0xe0*/
+ 0x03b1, 0x00df, 0x0393, 0x03c0,
+ 0x03a3, 0x03c3, 0x00b5, 0x03c4,
+ 0x03a6, 0x0398, 0x03a9, 0x03b4,
+ 0x221e, 0x03c6, 0x03b5, 0x2229,
+ /* 0xf0*/
+ 0x2261, 0x00b1, 0x2265, 0x2264,
+ 0x2320, 0x2321, 0x00f7, 0x2248,
+ 0x00b0, 0x2219, 0x00b7, 0x221a,
+ 0x207f, 0x00b2, 0x25a0, 0x00a0,
+};
+
+static unsigned char page00[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xff, 0xad, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+ 0x00, 0x00, 0x00, 0xae, 0xaa, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+ 0xf8, 0xf1, 0xfd, 0x00, 0x00, 0xe6, 0x00, 0xfa, /* 0xb0-0xb7 */
+ 0x00, 0x00, 0x00, 0xaf, 0xac, 0xab, 0x00, 0xa8, /* 0xb8-0xbf */
+ 0x00, 0xa4, 0x00, 0x00, 0x8e, 0x8f, 0x92, 0x80, /* 0xc0-0xc7 */
+ 0x00, 0x90, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, /* 0xc8-0xcf */
+ 0x8b, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x99, 0x00, /* 0xd0-0xd7 */
+ 0x9d, 0x00, 0xa7, 0x00, 0x9a, 0x97, 0x8d, 0xe1, /* 0xd8-0xdf */
+ 0x85, 0xa0, 0x83, 0x00, 0x84, 0x86, 0x91, 0x87, /* 0xe0-0xe7 */
+ 0x8a, 0x82, 0x88, 0x89, 0x00, 0xa1, 0x00, 0x00, /* 0xe8-0xef */
+ 0x8c, 0x00, 0x00, 0xa2, 0x93, 0x00, 0x94, 0xf6, /* 0xf0-0xf7 */
+ 0x9b, 0x00, 0xa3, 0x96, 0x81, 0x98, 0x95, 0x00, /* 0xf8-0xff */
+};
+
+static unsigned char page01[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+};
+
+static unsigned char page03[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0xe2, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0xe8, 0x00, /* 0xa0-0xa7 */
+ 0x00, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+ 0x00, 0xe0, 0x00, 0x00, 0xeb, 0xee, 0x00, 0x00, /* 0xb0-0xb7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+ 0xe3, 0x00, 0x00, 0xe5, 0xe7, 0x00, 0xed, 0x00, /* 0xc0-0xc7 */
+};
+
+static unsigned char page20[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, /* 0xa0-0xa7 */
+};
+
+static unsigned char page22[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0xf9, 0xfb, 0x00, 0x00, 0x00, 0xec, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0xf0, 0x00, 0x00, 0xf3, 0xf2, 0x00, 0x00, /* 0x60-0x67 */
+};
+
+static unsigned char page23[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0xa9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0xf4, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+};
+
+static unsigned char page25[256] = {
+ 0xc4, 0x00, 0xb3, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0xbf, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0xd9, 0x00, 0x00, 0x00, 0xc3, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0xcd, 0xba, 0xd5, 0xd6, 0xc9, 0xb8, 0xb7, 0xbb, /* 0x50-0x57 */
+ 0xd4, 0xd3, 0xc8, 0xbe, 0xbd, 0xbc, 0xc6, 0xc7, /* 0x58-0x5f */
+ 0xcc, 0xb5, 0xb6, 0xb9, 0xd1, 0xd2, 0xcb, 0xcf, /* 0x60-0x67 */
+ 0xd0, 0xca, 0xd8, 0xd7, 0xce, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0xdf, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0xdb, 0x00, 0x00, 0x00, 0xdd, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0xde, 0xb0, 0xb1, 0xb2, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+};
+
+static unsigned char *page_uni2charset[256] = {
+ page00, page01, NULL, page03, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ page20, NULL, page22, page23, NULL, page25, NULL, NULL,
+};
+
+static unsigned char charset2lower[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40-0x47 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x48-0x4f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50-0x57 */
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x87, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */
+ 0x88, 0x89, 0x8a, 0x8c, 0x8c, 0x95, 0x84, 0x86, /* 0x88-0x8f */
+ 0x82, 0x91, 0x91, 0x93, 0x94, 0x95, 0x96, 0x98, /* 0x90-0x97 */
+ 0x98, 0x94, 0x81, 0x9b, 0x9c, 0x9b, 0x9e, 0x9f, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa0, 0xa1, 0xa2, 0xa3, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0x00, 0xe3, 0xe5, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xed, 0x00, 0x00, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static unsigned char charset2upper[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x80, 0x9a, 0x90, 0x00, 0x8e, 0x00, 0x8f, 0x80, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x8b, 0x8b, 0x8d, 0x8e, 0x8f, /* 0x88-0x8f */
+ 0x90, 0x92, 0x92, 0x00, 0x99, 0x8d, 0x00, 0x97, /* 0x90-0x97 */
+ 0x97, 0x99, 0x9a, 0x9d, 0x9c, 0x9d, 0x9e, 0x00, /* 0x98-0x9f */
+ 0xa4, 0xa5, 0xa6, 0xa7, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0x00, 0xe1, 0xe2, 0x00, 0xe4, 0xe4, 0x00, 0x00, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0x00, 0xec, 0xe8, 0x00, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+ unsigned char *uni2charset;
+ unsigned char cl = uni & 0x00ff;
+ unsigned char ch = (uni & 0xff00) >> 8;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ uni2charset = page_uni2charset[ch];
+ if (uni2charset && uni2charset[cl])
+ out[0] = uni2charset[cl];
+ else
+ return -EINVAL;
+ return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+ *uni = charset2uni[*rawstring];
+ if (*uni == 0x0000)
+ return -EINVAL;
+ return 1;
+}
+
+static struct nls_table table = {
+ "cp861",
+ uni2char,
+ char2uni,
+ charset2lower,
+ charset2upper,
+ THIS_MODULE,
+};
+
+static int __init init_nls_cp861(void)
+{
+ return register_nls(&table);
+}
+
+static void __exit exit_nls_cp861(void)
+{
+ unregister_nls(&table);
+}
+
+module_init(init_nls_cp861)
+module_exit(exit_nls_cp861)
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/fs/nls/nls_cp862.c b/fs/nls/nls_cp862.c
new file mode 100644
index 00000000000000..ef2999a0d1dcad
--- /dev/null
+++ b/fs/nls/nls_cp862.c
@@ -0,0 +1,438 @@
+/*
+ * linux/fs/nls_cp862.c
+ *
+ * Charset cp862 translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static wchar_t charset2uni[256] = {
+ /* 0x00*/
+ 0x0000, 0x0001, 0x0002, 0x0003,
+ 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b,
+ 0x000c, 0x000d, 0x000e, 0x000f,
+ /* 0x10*/
+ 0x0010, 0x0011, 0x0012, 0x0013,
+ 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b,
+ 0x001c, 0x001d, 0x001e, 0x001f,
+ /* 0x20*/
+ 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b,
+ 0x002c, 0x002d, 0x002e, 0x002f,
+ /* 0x30*/
+ 0x0030, 0x0031, 0x0032, 0x0033,
+ 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b,
+ 0x003c, 0x003d, 0x003e, 0x003f,
+ /* 0x40*/
+ 0x0040, 0x0041, 0x0042, 0x0043,
+ 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b,
+ 0x004c, 0x004d, 0x004e, 0x004f,
+ /* 0x50*/
+ 0x0050, 0x0051, 0x0052, 0x0053,
+ 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b,
+ 0x005c, 0x005d, 0x005e, 0x005f,
+ /* 0x60*/
+ 0x0060, 0x0061, 0x0062, 0x0063,
+ 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b,
+ 0x006c, 0x006d, 0x006e, 0x006f,
+ /* 0x70*/
+ 0x0070, 0x0071, 0x0072, 0x0073,
+ 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b,
+ 0x007c, 0x007d, 0x007e, 0x007f,
+ /* 0x80*/
+ 0x05d0, 0x05d1, 0x05d2, 0x05d3,
+ 0x05d4, 0x05d5, 0x05d6, 0x05d7,
+ 0x05d8, 0x05d9, 0x05da, 0x05db,
+ 0x05dc, 0x05dd, 0x05de, 0x05df,
+ /* 0x90*/
+ 0x05e0, 0x05e1, 0x05e2, 0x05e3,
+ 0x05e4, 0x05e5, 0x05e6, 0x05e7,
+ 0x05e8, 0x05e9, 0x05ea, 0x00a2,
+ 0x00a3, 0x00a5, 0x20a7, 0x0192,
+ /* 0xa0*/
+ 0x00e1, 0x00ed, 0x00f3, 0x00fa,
+ 0x00f1, 0x00d1, 0x00aa, 0x00ba,
+ 0x00bf, 0x2310, 0x00ac, 0x00bd,
+ 0x00bc, 0x00a1, 0x00ab, 0x00bb,
+ /* 0xb0*/
+ 0x2591, 0x2592, 0x2593, 0x2502,
+ 0x2524, 0x2561, 0x2562, 0x2556,
+ 0x2555, 0x2563, 0x2551, 0x2557,
+ 0x255d, 0x255c, 0x255b, 0x2510,
+ /* 0xc0*/
+ 0x2514, 0x2534, 0x252c, 0x251c,
+ 0x2500, 0x253c, 0x255e, 0x255f,
+ 0x255a, 0x2554, 0x2569, 0x2566,
+ 0x2560, 0x2550, 0x256c, 0x2567,
+ /* 0xd0*/
+ 0x2568, 0x2564, 0x2565, 0x2559,
+ 0x2558, 0x2552, 0x2553, 0x256b,
+ 0x256a, 0x2518, 0x250c, 0x2588,
+ 0x2584, 0x258c, 0x2590, 0x2580,
+ /* 0xe0*/
+ 0x03b1, 0x00df, 0x0393, 0x03c0,
+ 0x03a3, 0x03c3, 0x00b5, 0x03c4,
+ 0x03a6, 0x0398, 0x03a9, 0x03b4,
+ 0x221e, 0x03c6, 0x03b5, 0x2229,
+ /* 0xf0*/
+ 0x2261, 0x00b1, 0x2265, 0x2264,
+ 0x2320, 0x2321, 0x00f7, 0x2248,
+ 0x00b0, 0x2219, 0x00b7, 0x221a,
+ 0x207f, 0x00b2, 0x25a0, 0x00a0,
+};
+
+static unsigned char page00[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xff, 0xad, 0x9b, 0x9c, 0x00, 0x9d, 0x00, 0x00, /* 0xa0-0xa7 */
+ 0x00, 0x00, 0xa6, 0xae, 0xaa, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+ 0xf8, 0xf1, 0xfd, 0x00, 0x00, 0xe6, 0x00, 0xfa, /* 0xb0-0xb7 */
+ 0x00, 0x00, 0xa7, 0xaf, 0xac, 0xab, 0x00, 0xa8, /* 0xb8-0xbf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+ 0x00, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe1, /* 0xd8-0xdf */
+ 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xa1, 0x00, 0x00, /* 0xe8-0xef */
+ 0x00, 0xa4, 0x00, 0xa2, 0x00, 0x00, 0x00, 0xf6, /* 0xf0-0xf7 */
+ 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static unsigned char page01[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+};
+
+static unsigned char page03[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0xe2, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0xe8, 0x00, /* 0xa0-0xa7 */
+ 0x00, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+ 0x00, 0xe0, 0x00, 0x00, 0xeb, 0xee, 0x00, 0x00, /* 0xb0-0xb7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+ 0xe3, 0x00, 0x00, 0xe5, 0xe7, 0x00, 0xed, 0x00, /* 0xc0-0xc7 */
+};
+
+static unsigned char page05[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0xd0-0xd7 */
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0xd8-0xdf */
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0xe0-0xe7 */
+ 0x98, 0x99, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+};
+
+static unsigned char page20[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, /* 0xa0-0xa7 */
+};
+
+static unsigned char page22[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0xf9, 0xfb, 0x00, 0x00, 0x00, 0xec, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0xf0, 0x00, 0x00, 0xf3, 0xf2, 0x00, 0x00, /* 0x60-0x67 */
+};
+
+static unsigned char page23[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0xa9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0xf4, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+};
+
+static unsigned char page25[256] = {
+ 0xc4, 0x00, 0xb3, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0xbf, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0xd9, 0x00, 0x00, 0x00, 0xc3, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0xcd, 0xba, 0xd5, 0xd6, 0xc9, 0xb8, 0xb7, 0xbb, /* 0x50-0x57 */
+ 0xd4, 0xd3, 0xc8, 0xbe, 0xbd, 0xbc, 0xc6, 0xc7, /* 0x58-0x5f */
+ 0xcc, 0xb5, 0xb6, 0xb9, 0xd1, 0xd2, 0xcb, 0xcf, /* 0x60-0x67 */
+ 0xd0, 0xca, 0xd8, 0xd7, 0xce, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0xdf, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0xdb, 0x00, 0x00, 0x00, 0xdd, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0xde, 0xb0, 0xb1, 0xb2, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+};
+
+static unsigned char *page_uni2charset[256] = {
+ page00, page01, NULL, page03, NULL, page05, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ page20, NULL, page22, page23, NULL, page25, NULL, NULL,
+};
+
+static unsigned char charset2lower[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40-0x47 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x48-0x4f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50-0x57 */
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0x88-0x8f */
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa4, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0x00, 0xe3, 0xe5, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xed, 0x00, 0x00, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static unsigned char charset2upper[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0x88-0x8f */
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x00, /* 0x98-0x9f */
+ 0x00, 0x00, 0x00, 0x00, 0xa5, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0x00, 0xe1, 0xe2, 0x00, 0xe4, 0xe4, 0x00, 0x00, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0x00, 0xec, 0xe8, 0x00, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+ unsigned char *uni2charset;
+ unsigned char cl = uni & 0x00ff;
+ unsigned char ch = (uni & 0xff00) >> 8;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ uni2charset = page_uni2charset[ch];
+ if (uni2charset && uni2charset[cl])
+ out[0] = uni2charset[cl];
+ else
+ return -EINVAL;
+ return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+ *uni = charset2uni[*rawstring];
+ if (*uni == 0x0000)
+ return -EINVAL;
+ return 1;
+}
+
+static struct nls_table table = {
+ "cp862",
+ uni2char,
+ char2uni,
+ charset2lower,
+ charset2upper,
+ THIS_MODULE,
+};
+
+static int __init init_nls_cp862(void)
+{
+ return register_nls(&table);
+}
+
+static void __exit exit_nls_cp862(void)
+{
+ unregister_nls(&table);
+}
+
+module_init(init_nls_cp862)
+module_exit(exit_nls_cp862)
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/fs/nls/nls_cp863.c b/fs/nls/nls_cp863.c
new file mode 100644
index 00000000000000..1b4c6c04b1db06
--- /dev/null
+++ b/fs/nls/nls_cp863.c
@@ -0,0 +1,398 @@
+/*
+ * linux/fs/nls_cp863.c
+ *
+ * Charset cp863 translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static wchar_t charset2uni[256] = {
+ /* 0x00*/
+ 0x0000, 0x0001, 0x0002, 0x0003,
+ 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b,
+ 0x000c, 0x000d, 0x000e, 0x000f,
+ /* 0x10*/
+ 0x0010, 0x0011, 0x0012, 0x0013,
+ 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b,
+ 0x001c, 0x001d, 0x001e, 0x001f,
+ /* 0x20*/
+ 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b,
+ 0x002c, 0x002d, 0x002e, 0x002f,
+ /* 0x30*/
+ 0x0030, 0x0031, 0x0032, 0x0033,
+ 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b,
+ 0x003c, 0x003d, 0x003e, 0x003f,
+ /* 0x40*/
+ 0x0040, 0x0041, 0x0042, 0x0043,
+ 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b,
+ 0x004c, 0x004d, 0x004e, 0x004f,
+ /* 0x50*/
+ 0x0050, 0x0051, 0x0052, 0x0053,
+ 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b,
+ 0x005c, 0x005d, 0x005e, 0x005f,
+ /* 0x60*/
+ 0x0060, 0x0061, 0x0062, 0x0063,
+ 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b,
+ 0x006c, 0x006d, 0x006e, 0x006f,
+ /* 0x70*/
+ 0x0070, 0x0071, 0x0072, 0x0073,
+ 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b,
+ 0x007c, 0x007d, 0x007e, 0x007f,
+ /* 0x80*/
+ 0x00c7, 0x00fc, 0x00e9, 0x00e2,
+ 0x00c2, 0x00e0, 0x00b6, 0x00e7,
+ 0x00ea, 0x00eb, 0x00e8, 0x00ef,
+ 0x00ee, 0x2017, 0x00c0, 0x00a7,
+ /* 0x90*/
+ 0x00c9, 0x00c8, 0x00ca, 0x00f4,
+ 0x00cb, 0x00cf, 0x00fb, 0x00f9,
+ 0x00a4, 0x00d4, 0x00dc, 0x00a2,
+ 0x00a3, 0x00d9, 0x00db, 0x0192,
+ /* 0xa0*/
+ 0x00a6, 0x00b4, 0x00f3, 0x00fa,
+ 0x00a8, 0x00b8, 0x00b3, 0x00af,
+ 0x00ce, 0x2310, 0x00ac, 0x00bd,
+ 0x00bc, 0x00be, 0x00ab, 0x00bb,
+ /* 0xb0*/
+ 0x2591, 0x2592, 0x2593, 0x2502,
+ 0x2524, 0x2561, 0x2562, 0x2556,
+ 0x2555, 0x2563, 0x2551, 0x2557,
+ 0x255d, 0x255c, 0x255b, 0x2510,
+ /* 0xc0*/
+ 0x2514, 0x2534, 0x252c, 0x251c,
+ 0x2500, 0x253c, 0x255e, 0x255f,
+ 0x255a, 0x2554, 0x2569, 0x2566,
+ 0x2560, 0x2550, 0x256c, 0x2567,
+ /* 0xd0*/
+ 0x2568, 0x2564, 0x2565, 0x2559,
+ 0x2558, 0x2552, 0x2553, 0x256b,
+ 0x256a, 0x2518, 0x250c, 0x2588,
+ 0x2584, 0x258c, 0x2590, 0x2580,
+ /* 0xe0*/
+ 0x03b1, 0x00df, 0x0393, 0x03c0,
+ 0x03a3, 0x03c3, 0x00b5, 0x03c4,
+ 0x03a6, 0x0398, 0x03a9, 0x03b4,
+ 0x221e, 0x03c6, 0x03b5, 0x2229,
+ /* 0xf0*/
+ 0x2261, 0x00b1, 0x2265, 0x2264,
+ 0x2320, 0x2321, 0x00f7, 0x2248,
+ 0x00b0, 0x2219, 0x00b7, 0x221a,
+ 0x207f, 0x00b2, 0x25a0, 0x00a0,
+};
+
+static unsigned char page00[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xff, 0x00, 0x9b, 0x9c, 0x98, 0x00, 0xa0, 0x8f, /* 0xa0-0xa7 */
+ 0xa4, 0x00, 0x00, 0xae, 0xaa, 0x00, 0x00, 0xa7, /* 0xa8-0xaf */
+ 0xf8, 0xf1, 0xfd, 0xa6, 0xa1, 0xe6, 0x86, 0xfa, /* 0xb0-0xb7 */
+ 0xa5, 0x00, 0x00, 0xaf, 0xac, 0xab, 0xad, 0x00, /* 0xb8-0xbf */
+ 0x8e, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x80, /* 0xc0-0xc7 */
+ 0x91, 0x90, 0x92, 0x94, 0x00, 0x00, 0xa8, 0x95, /* 0xc8-0xcf */
+ 0x00, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+ 0x00, 0x9d, 0x00, 0x9e, 0x9a, 0x00, 0x00, 0xe1, /* 0xd8-0xdf */
+ 0x85, 0x00, 0x83, 0x00, 0x00, 0x00, 0x00, 0x87, /* 0xe0-0xe7 */
+ 0x8a, 0x82, 0x88, 0x89, 0x00, 0x00, 0x8c, 0x8b, /* 0xe8-0xef */
+ 0x00, 0x00, 0x00, 0xa2, 0x93, 0x00, 0x00, 0xf6, /* 0xf0-0xf7 */
+ 0x00, 0x97, 0xa3, 0x96, 0x81, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static unsigned char page01[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+};
+
+static unsigned char page03[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0xe2, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0xe8, 0x00, /* 0xa0-0xa7 */
+ 0x00, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+ 0x00, 0xe0, 0x00, 0x00, 0xeb, 0xee, 0x00, 0x00, /* 0xb0-0xb7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+ 0xe3, 0x00, 0x00, 0xe5, 0xe7, 0x00, 0xed, 0x00, /* 0xc0-0xc7 */
+};
+
+static unsigned char page20[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8d, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, /* 0x78-0x7f */
+};
+
+static unsigned char page22[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0xf9, 0xfb, 0x00, 0x00, 0x00, 0xec, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0xf0, 0x00, 0x00, 0xf3, 0xf2, 0x00, 0x00, /* 0x60-0x67 */
+};
+
+static unsigned char page23[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0xa9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0xf4, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+};
+
+static unsigned char page25[256] = {
+ 0xc4, 0x00, 0xb3, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0xbf, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0xd9, 0x00, 0x00, 0x00, 0xc3, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0xcd, 0xba, 0xd5, 0xd6, 0xc9, 0xb8, 0xb7, 0xbb, /* 0x50-0x57 */
+ 0xd4, 0xd3, 0xc8, 0xbe, 0xbd, 0xbc, 0xc6, 0xc7, /* 0x58-0x5f */
+ 0xcc, 0xb5, 0xb6, 0xb9, 0xd1, 0xd2, 0xcb, 0xcf, /* 0x60-0x67 */
+ 0xd0, 0xca, 0xd8, 0xd7, 0xce, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0xdf, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0xdb, 0x00, 0x00, 0x00, 0xdd, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0xde, 0xb0, 0xb1, 0xb2, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+};
+
+static unsigned char *page_uni2charset[256] = {
+ page00, page01, NULL, page03, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ page20, NULL, page22, page23, NULL, page25, NULL, NULL,
+};
+
+static unsigned char charset2lower[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40-0x47 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x48-0x4f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50-0x57 */
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x87, 0x81, 0x82, 0x83, 0x83, 0x85, 0x86, 0x87, /* 0x80-0x87 */
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x85, 0x8f, /* 0x88-0x8f */
+ 0x82, 0x8a, 0x88, 0x93, 0x89, 0x8b, 0x96, 0x97, /* 0x90-0x97 */
+ 0x98, 0x93, 0x81, 0x9b, 0x9c, 0x97, 0x96, 0x9f, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0x8c, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0x00, 0xe3, 0xe5, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xed, 0x00, 0x00, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static unsigned char charset2upper[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x80, 0x9a, 0x90, 0x84, 0x84, 0x8e, 0x86, 0x80, /* 0x80-0x87 */
+ 0x92, 0x94, 0x91, 0x95, 0xa8, 0x8d, 0x8e, 0x8f, /* 0x88-0x8f */
+ 0x90, 0x91, 0x92, 0x99, 0x94, 0x95, 0x9e, 0x9d, /* 0x90-0x97 */
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x00, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0x00, 0x00, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0x00, 0xe1, 0xe2, 0x00, 0xe4, 0xe4, 0x00, 0x00, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0x00, 0xec, 0xe8, 0x00, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+ unsigned char *uni2charset;
+ unsigned char cl = uni & 0x00ff;
+ unsigned char ch = (uni & 0xff00) >> 8;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ uni2charset = page_uni2charset[ch];
+ if (uni2charset && uni2charset[cl])
+ out[0] = uni2charset[cl];
+ else
+ return -EINVAL;
+ return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+ *uni = charset2uni[*rawstring];
+ if (*uni == 0x0000)
+ return -EINVAL;
+ return 1;
+}
+
+static struct nls_table table = {
+ "cp863",
+ uni2char,
+ char2uni,
+ charset2lower,
+ charset2upper,
+ THIS_MODULE,
+};
+
+static int __init init_nls_cp863(void)
+{
+ return register_nls(&table);
+}
+
+static void __exit exit_nls_cp863(void)
+{
+ unregister_nls(&table);
+}
+
+module_init(init_nls_cp863)
+module_exit(exit_nls_cp863)
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/fs/nls/nls_cp864.c b/fs/nls/nls_cp864.c
new file mode 100644
index 00000000000000..de5eac3d3de64a
--- /dev/null
+++ b/fs/nls/nls_cp864.c
@@ -0,0 +1,424 @@
+/*
+ * linux/fs/nls_cp864.c
+ *
+ * Charset cp864 translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static wchar_t charset2uni[256] = {
+ /* 0x00*/
+ 0x0000, 0x0001, 0x0002, 0x0003,
+ 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b,
+ 0x000c, 0x000d, 0x000e, 0x000f,
+ /* 0x10*/
+ 0x0010, 0x0011, 0x0012, 0x0013,
+ 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b,
+ 0x001c, 0x001d, 0x001e, 0x001f,
+ /* 0x20*/
+ 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x066a, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b,
+ 0x002c, 0x002d, 0x002e, 0x002f,
+ /* 0x30*/
+ 0x0030, 0x0031, 0x0032, 0x0033,
+ 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b,
+ 0x003c, 0x003d, 0x003e, 0x003f,
+ /* 0x40*/
+ 0x0040, 0x0041, 0x0042, 0x0043,
+ 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b,
+ 0x004c, 0x004d, 0x004e, 0x004f,
+ /* 0x50*/
+ 0x0050, 0x0051, 0x0052, 0x0053,
+ 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b,
+ 0x005c, 0x005d, 0x005e, 0x005f,
+ /* 0x60*/
+ 0x0060, 0x0061, 0x0062, 0x0063,
+ 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b,
+ 0x006c, 0x006d, 0x006e, 0x006f,
+ /* 0x70*/
+ 0x0070, 0x0071, 0x0072, 0x0073,
+ 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b,
+ 0x007c, 0x007d, 0x007e, 0x007f,
+ /* 0x80*/
+ 0x00b0, 0x00b7, 0x2219, 0x221a,
+ 0x2592, 0x2500, 0x2502, 0x253c,
+ 0x2524, 0x252c, 0x251c, 0x2534,
+ 0x2510, 0x250c, 0x2514, 0x2518,
+ /* 0x90*/
+ 0x03b2, 0x221e, 0x03c6, 0x00b1,
+ 0x00bd, 0x00bc, 0x2248, 0x00ab,
+ 0x00bb, 0xfef7, 0xfef8, 0x0000,
+ 0x0000, 0xfefb, 0xfefc, 0x0000,
+ /* 0xa0*/
+ 0x00a0, 0x00ad, 0xfe82, 0x00a3,
+ 0x00a4, 0xfe84, 0x0000, 0x0000,
+ 0xfe8e, 0xfe8f, 0xfe95, 0xfe99,
+ 0x060c, 0xfe9d, 0xfea1, 0xfea5,
+ /* 0xb0*/
+ 0x0660, 0x0661, 0x0662, 0x0663,
+ 0x0664, 0x0665, 0x0666, 0x0667,
+ 0x0668, 0x0669, 0xfed1, 0x061b,
+ 0xfeb1, 0xfeb5, 0xfeb9, 0x061f,
+ /* 0xc0*/
+ 0x00a2, 0xfe80, 0xfe81, 0xfe83,
+ 0xfe85, 0xfeca, 0xfe8b, 0xfe8d,
+ 0xfe91, 0xfe93, 0xfe97, 0xfe9b,
+ 0xfe9f, 0xfea3, 0xfea7, 0xfea9,
+ /* 0xd0*/
+ 0xfeab, 0xfead, 0xfeaf, 0xfeb3,
+ 0xfeb7, 0xfebb, 0xfebf, 0xfec1,
+ 0xfec5, 0xfecb, 0xfecf, 0x00a6,
+ 0x00ac, 0x00f7, 0x00d7, 0xfec9,
+ /* 0xe0*/
+ 0x0640, 0xfed3, 0xfed7, 0xfedb,
+ 0xfedf, 0xfee3, 0xfee7, 0xfeeb,
+ 0xfeed, 0xfeef, 0xfef3, 0xfebd,
+ 0xfecc, 0xfece, 0xfecd, 0xfee1,
+ /* 0xf0*/
+ 0xfe7d, 0x0651, 0xfee5, 0xfee9,
+ 0xfeec, 0xfef0, 0xfef2, 0xfed0,
+ 0xfed5, 0xfef5, 0xfef6, 0xfedd,
+ 0xfed9, 0xfef1, 0x25a0, 0x0000,
+};
+
+static unsigned char page00[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x00, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0x00, 0xc0, 0xa3, 0xa4, 0x00, 0xdb, 0x00, /* 0xa0-0xa7 */
+ 0x00, 0x00, 0x00, 0x97, 0xdc, 0xa1, 0x00, 0x00, /* 0xa8-0xaf */
+ 0x80, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, /* 0xb0-0xb7 */
+ 0x00, 0x00, 0x00, 0x98, 0x95, 0x94, 0x00, 0x00, /* 0xb8-0xbf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, /* 0xd0-0xd7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, /* 0xf0-0xf7 */
+};
+
+static unsigned char page03[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+ 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x00, /* 0xc0-0xc7 */
+};
+
+static unsigned char page06[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0xac, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0xbb, 0x00, 0x00, 0x00, 0xbf, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0xf1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0x60-0x67 */
+ 0xb8, 0xb9, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+};
+
+static unsigned char page22[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x82, 0x83, 0x00, 0x00, 0x00, 0x91, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+};
+
+static unsigned char page25[256] = {
+ 0x85, 0x00, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x8d, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x8c, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x8f, 0x00, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+};
+
+static unsigned char pagefe[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0xc1, 0xc2, 0xa2, 0xc3, 0xa5, 0xc4, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0xc6, 0x00, 0xc7, 0xa8, 0xa9, /* 0x88-0x8f */
+ 0x00, 0xc8, 0x00, 0xc9, 0x00, 0xaa, 0x00, 0xca, /* 0x90-0x97 */
+ 0x00, 0xab, 0x00, 0xcb, 0x00, 0xad, 0x00, 0xcc, /* 0x98-0x9f */
+ 0x00, 0xae, 0x00, 0xcd, 0x00, 0xaf, 0x00, 0xce, /* 0xa0-0xa7 */
+ 0x00, 0xcf, 0x00, 0xd0, 0x00, 0xd1, 0x00, 0xd2, /* 0xa8-0xaf */
+ 0x00, 0xbc, 0x00, 0xd3, 0x00, 0xbd, 0x00, 0xd4, /* 0xb0-0xb7 */
+ 0x00, 0xbe, 0x00, 0xd5, 0x00, 0xeb, 0x00, 0xd6, /* 0xb8-0xbf */
+ 0x00, 0xd7, 0x00, 0x00, 0x00, 0xd8, 0x00, 0x00, /* 0xc0-0xc7 */
+ 0x00, 0xdf, 0xc5, 0xd9, 0xec, 0xee, 0xed, 0xda, /* 0xc8-0xcf */
+ 0xf7, 0xba, 0x00, 0xe1, 0x00, 0xf8, 0x00, 0xe2, /* 0xd0-0xd7 */
+ 0x00, 0xfc, 0x00, 0xe3, 0x00, 0xfb, 0x00, 0xe4, /* 0xd8-0xdf */
+ 0x00, 0xef, 0x00, 0xe5, 0x00, 0xf2, 0x00, 0xe6, /* 0xe0-0xe7 */
+ 0x00, 0xf3, 0x00, 0xe7, 0xf4, 0xe8, 0x00, 0xe9, /* 0xe8-0xef */
+ 0xf5, 0xfd, 0xf6, 0xea, 0x00, 0xf9, 0xfa, 0x99, /* 0xf0-0xf7 */
+ 0x9a, 0x00, 0x00, 0x9d, 0x9e, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static unsigned char *page_uni2charset[256] = {
+ page00, NULL, NULL, page03, NULL, NULL, page06, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, page22, NULL, NULL, page25, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, pagefe, NULL,
+};
+
+static unsigned char charset2lower[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40-0x47 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x48-0x4f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50-0x57 */
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0x88-0x8f */
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */
+ 0x98, 0x99, 0x9a, 0x00, 0x00, 0x9d, 0x9e, 0x00, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0x00, 0x00, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0x00, /* 0xf8-0xff */
+};
+
+static unsigned char charset2upper[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0x88-0x8f */
+ 0x00, 0x91, 0x00, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */
+ 0x98, 0x99, 0x9a, 0x00, 0x00, 0x9d, 0x9e, 0x00, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0x00, 0x00, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0x00, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+ unsigned char *uni2charset;
+ unsigned char cl = uni & 0x00ff;
+ unsigned char ch = (uni & 0xff00) >> 8;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ uni2charset = page_uni2charset[ch];
+ if (uni2charset && uni2charset[cl])
+ out[0] = uni2charset[cl];
+ else
+ return -EINVAL;
+ return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+ *uni = charset2uni[*rawstring];
+ if (*uni == 0x0000)
+ return -EINVAL;
+ return 1;
+}
+
+static struct nls_table table = {
+ "cp864",
+ uni2char,
+ char2uni,
+ charset2lower,
+ charset2upper,
+ THIS_MODULE,
+};
+
+static int __init init_nls_cp864(void)
+{
+ return register_nls(&table);
+}
+
+static void __exit exit_nls_cp864(void)
+{
+ unregister_nls(&table);
+}
+
+module_init(init_nls_cp864)
+module_exit(exit_nls_cp864)
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/fs/nls/nls_cp865.c b/fs/nls/nls_cp865.c
new file mode 100644
index 00000000000000..68d9e567fd0c57
--- /dev/null
+++ b/fs/nls/nls_cp865.c
@@ -0,0 +1,404 @@
+/*
+ * linux/fs/nls_cp865.c
+ *
+ * Charset cp865 translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static wchar_t charset2uni[256] = {
+ /* 0x00*/
+ 0x0000, 0x0001, 0x0002, 0x0003,
+ 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b,
+ 0x000c, 0x000d, 0x000e, 0x000f,
+ /* 0x10*/
+ 0x0010, 0x0011, 0x0012, 0x0013,
+ 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b,
+ 0x001c, 0x001d, 0x001e, 0x001f,
+ /* 0x20*/
+ 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b,
+ 0x002c, 0x002d, 0x002e, 0x002f,
+ /* 0x30*/
+ 0x0030, 0x0031, 0x0032, 0x0033,
+ 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b,
+ 0x003c, 0x003d, 0x003e, 0x003f,
+ /* 0x40*/
+ 0x0040, 0x0041, 0x0042, 0x0043,
+ 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b,
+ 0x004c, 0x004d, 0x004e, 0x004f,
+ /* 0x50*/
+ 0x0050, 0x0051, 0x0052, 0x0053,
+ 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b,
+ 0x005c, 0x005d, 0x005e, 0x005f,
+ /* 0x60*/
+ 0x0060, 0x0061, 0x0062, 0x0063,
+ 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b,
+ 0x006c, 0x006d, 0x006e, 0x006f,
+ /* 0x70*/
+ 0x0070, 0x0071, 0x0072, 0x0073,
+ 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b,
+ 0x007c, 0x007d, 0x007e, 0x007f,
+ /* 0x80*/
+ 0x00c7, 0x00fc, 0x00e9, 0x00e2,
+ 0x00e4, 0x00e0, 0x00e5, 0x00e7,
+ 0x00ea, 0x00eb, 0x00e8, 0x00ef,
+ 0x00ee, 0x00ec, 0x00c4, 0x00c5,
+ /* 0x90*/
+ 0x00c9, 0x00e6, 0x00c6, 0x00f4,
+ 0x00f6, 0x00f2, 0x00fb, 0x00f9,
+ 0x00ff, 0x00d6, 0x00dc, 0x00f8,
+ 0x00a3, 0x00d8, 0x20a7, 0x0192,
+ /* 0xa0*/
+ 0x00e1, 0x00ed, 0x00f3, 0x00fa,
+ 0x00f1, 0x00d1, 0x00aa, 0x00ba,
+ 0x00bf, 0x2310, 0x00ac, 0x00bd,
+ 0x00bc, 0x00a1, 0x00ab, 0x00a4,
+ /* 0xb0*/
+ 0x2591, 0x2592, 0x2593, 0x2502,
+ 0x2524, 0x2561, 0x2562, 0x2556,
+ 0x2555, 0x2563, 0x2551, 0x2557,
+ 0x255d, 0x255c, 0x255b, 0x2510,
+ /* 0xc0*/
+ 0x2514, 0x2534, 0x252c, 0x251c,
+ 0x2500, 0x253c, 0x255e, 0x255f,
+ 0x255a, 0x2554, 0x2569, 0x2566,
+ 0x2560, 0x2550, 0x256c, 0x2567,
+ /* 0xd0*/
+ 0x2568, 0x2564, 0x2565, 0x2559,
+ 0x2558, 0x2552, 0x2553, 0x256b,
+ 0x256a, 0x2518, 0x250c, 0x2588,
+ 0x2584, 0x258c, 0x2590, 0x2580,
+ /* 0xe0*/
+ 0x03b1, 0x00df, 0x0393, 0x03c0,
+ 0x03a3, 0x03c3, 0x00b5, 0x03c4,
+ 0x03a6, 0x0398, 0x03a9, 0x03b4,
+ 0x221e, 0x03c6, 0x03b5, 0x2229,
+ /* 0xf0*/
+ 0x2261, 0x00b1, 0x2265, 0x2264,
+ 0x2320, 0x2321, 0x00f7, 0x2248,
+ 0x00b0, 0x2219, 0x00b7, 0x221a,
+ 0x207f, 0x00b2, 0x25a0, 0x00a0,
+};
+
+static unsigned char page00[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xff, 0xad, 0x00, 0x9c, 0xaf, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+ 0x00, 0x00, 0xa6, 0xae, 0xaa, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+ 0xf8, 0xf1, 0xfd, 0x00, 0x00, 0xe6, 0x00, 0xfa, /* 0xb0-0xb7 */
+ 0x00, 0x00, 0xa7, 0x00, 0xac, 0xab, 0x00, 0xa8, /* 0xb8-0xbf */
+ 0x00, 0x00, 0x00, 0x00, 0x8e, 0x8f, 0x92, 0x80, /* 0xc0-0xc7 */
+ 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+ 0x00, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x99, 0x00, /* 0xd0-0xd7 */
+ 0x9d, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0xe1, /* 0xd8-0xdf */
+ 0x85, 0xa0, 0x83, 0x00, 0x84, 0x86, 0x91, 0x87, /* 0xe0-0xe7 */
+ 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b, /* 0xe8-0xef */
+ 0x00, 0xa4, 0x95, 0xa2, 0x93, 0x00, 0x94, 0xf6, /* 0xf0-0xf7 */
+ 0x9b, 0x97, 0xa3, 0x96, 0x81, 0x00, 0x00, 0x98, /* 0xf8-0xff */
+};
+
+static unsigned char page01[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+};
+
+static unsigned char page03[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0xe2, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0xe8, 0x00, /* 0xa0-0xa7 */
+ 0x00, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+ 0x00, 0xe0, 0x00, 0x00, 0xeb, 0xee, 0x00, 0x00, /* 0xb0-0xb7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+ 0xe3, 0x00, 0x00, 0xe5, 0xe7, 0x00, 0xed, 0x00, /* 0xc0-0xc7 */
+};
+
+static unsigned char page20[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, /* 0xa0-0xa7 */
+};
+
+static unsigned char page22[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0xf9, 0xfb, 0x00, 0x00, 0x00, 0xec, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0xf0, 0x00, 0x00, 0xf3, 0xf2, 0x00, 0x00, /* 0x60-0x67 */
+};
+
+static unsigned char page23[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0xa9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0xf4, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+};
+
+static unsigned char page25[256] = {
+ 0xc4, 0x00, 0xb3, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0xbf, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0xd9, 0x00, 0x00, 0x00, 0xc3, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0xcd, 0xba, 0xd5, 0xd6, 0xc9, 0xb8, 0xb7, 0xbb, /* 0x50-0x57 */
+ 0xd4, 0xd3, 0xc8, 0xbe, 0xbd, 0xbc, 0xc6, 0xc7, /* 0x58-0x5f */
+ 0xcc, 0xb5, 0xb6, 0xb9, 0xd1, 0xd2, 0xcb, 0xcf, /* 0x60-0x67 */
+ 0xd0, 0xca, 0xd8, 0xd7, 0xce, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0xdf, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0xdb, 0x00, 0x00, 0x00, 0xdd, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0xde, 0xb0, 0xb1, 0xb2, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+};
+
+static unsigned char *page_uni2charset[256] = {
+ page00, page01, NULL, page03, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ page20, NULL, page22, page23, NULL, page25, NULL, NULL,
+};
+
+static unsigned char charset2lower[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40-0x47 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x48-0x4f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50-0x57 */
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x87, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x84, 0x86, /* 0x88-0x8f */
+ 0x82, 0x91, 0x91, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */
+ 0x98, 0x94, 0x81, 0x9b, 0x9c, 0x9b, 0x9e, 0x9f, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa4, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0x00, 0xe3, 0xe5, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xed, 0x00, 0x00, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static unsigned char charset2upper[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x80, 0x9a, 0x90, 0x00, 0x8e, 0x00, 0x8f, 0x80, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8e, 0x8f, /* 0x88-0x8f */
+ 0x90, 0x92, 0x92, 0x00, 0x99, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x99, 0x9a, 0x9d, 0x9c, 0x9d, 0x9e, 0x00, /* 0x98-0x9f */
+ 0x00, 0x00, 0x00, 0x00, 0xa5, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0x00, 0xe1, 0xe2, 0x00, 0xe4, 0xe4, 0x00, 0x00, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0x00, 0xec, 0xe8, 0x00, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+ unsigned char *uni2charset;
+ unsigned char cl = uni & 0x00ff;
+ unsigned char ch = (uni & 0xff00) >> 8;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ uni2charset = page_uni2charset[ch];
+ if (uni2charset && uni2charset[cl])
+ out[0] = uni2charset[cl];
+ else
+ return -EINVAL;
+ return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+ *uni = charset2uni[*rawstring];
+ if (*uni == 0x0000)
+ return -EINVAL;
+ return 1;
+}
+
+static struct nls_table table = {
+ "cp865",
+ uni2char,
+ char2uni,
+ charset2lower,
+ charset2upper,
+ THIS_MODULE,
+};
+
+static int __init init_nls_cp865(void)
+{
+ return register_nls(&table);
+}
+
+static void __exit exit_nls_cp865(void)
+{
+ unregister_nls(&table);
+}
+
+module_init(init_nls_cp865)
+module_exit(exit_nls_cp865)
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/fs/nls/nls_cp866.c b/fs/nls/nls_cp866.c
new file mode 100644
index 00000000000000..8860b2510e1383
--- /dev/null
+++ b/fs/nls/nls_cp866.c
@@ -0,0 +1,322 @@
+/*
+ * linux/fs/nls_cp866.c
+ *
+ * Charset cp866 translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static wchar_t charset2uni[256] = {
+ /* 0x00*/
+ 0x0000, 0x0001, 0x0002, 0x0003,
+ 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b,
+ 0x000c, 0x000d, 0x000e, 0x000f,
+ /* 0x10*/
+ 0x0010, 0x0011, 0x0012, 0x0013,
+ 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b,
+ 0x001c, 0x001d, 0x001e, 0x001f,
+ /* 0x20*/
+ 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b,
+ 0x002c, 0x002d, 0x002e, 0x002f,
+ /* 0x30*/
+ 0x0030, 0x0031, 0x0032, 0x0033,
+ 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b,
+ 0x003c, 0x003d, 0x003e, 0x003f,
+ /* 0x40*/
+ 0x0040, 0x0041, 0x0042, 0x0043,
+ 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b,
+ 0x004c, 0x004d, 0x004e, 0x004f,
+ /* 0x50*/
+ 0x0050, 0x0051, 0x0052, 0x0053,
+ 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b,
+ 0x005c, 0x005d, 0x005e, 0x005f,
+ /* 0x60*/
+ 0x0060, 0x0061, 0x0062, 0x0063,
+ 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b,
+ 0x006c, 0x006d, 0x006e, 0x006f,
+ /* 0x70*/
+ 0x0070, 0x0071, 0x0072, 0x0073,
+ 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b,
+ 0x007c, 0x007d, 0x007e, 0x007f,
+ /* 0x80*/
+ 0x0410, 0x0411, 0x0412, 0x0413,
+ 0x0414, 0x0415, 0x0416, 0x0417,
+ 0x0418, 0x0419, 0x041a, 0x041b,
+ 0x041c, 0x041d, 0x041e, 0x041f,
+ /* 0x90*/
+ 0x0420, 0x0421, 0x0422, 0x0423,
+ 0x0424, 0x0425, 0x0426, 0x0427,
+ 0x0428, 0x0429, 0x042a, 0x042b,
+ 0x042c, 0x042d, 0x042e, 0x042f,
+ /* 0xa0*/
+ 0x0430, 0x0431, 0x0432, 0x0433,
+ 0x0434, 0x0435, 0x0436, 0x0437,
+ 0x0438, 0x0439, 0x043a, 0x043b,
+ 0x043c, 0x043d, 0x043e, 0x043f,
+ /* 0xb0*/
+ 0x2591, 0x2592, 0x2593, 0x2502,
+ 0x2524, 0x2561, 0x2562, 0x2556,
+ 0x2555, 0x2563, 0x2551, 0x2557,
+ 0x255d, 0x255c, 0x255b, 0x2510,
+ /* 0xc0*/
+ 0x2514, 0x2534, 0x252c, 0x251c,
+ 0x2500, 0x253c, 0x255e, 0x255f,
+ 0x255a, 0x2554, 0x2569, 0x2566,
+ 0x2560, 0x2550, 0x256c, 0x2567,
+ /* 0xd0*/
+ 0x2568, 0x2564, 0x2565, 0x2559,
+ 0x2558, 0x2552, 0x2553, 0x256b,
+ 0x256a, 0x2518, 0x250c, 0x2588,
+ 0x2584, 0x258c, 0x2590, 0x2580,
+ /* 0xe0*/
+ 0x0440, 0x0441, 0x0442, 0x0443,
+ 0x0444, 0x0445, 0x0446, 0x0447,
+ 0x0448, 0x0449, 0x044a, 0x044b,
+ 0x044c, 0x044d, 0x044e, 0x044f,
+ /* 0xf0*/
+ 0x0401, 0x0451, 0x0404, 0x0454,
+ 0x0407, 0x0457, 0x040e, 0x045e,
+ 0x00b0, 0x2219, 0x00b7, 0x221a,
+ 0x2116, 0x00a4, 0x25a0, 0x00a0,
+};
+
+static unsigned char page00[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xff, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+ 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, /* 0xb0-0xb7 */
+};
+
+static unsigned char page04[256] = {
+ 0x00, 0xf0, 0x00, 0x00, 0xf2, 0x00, 0x00, 0xf4, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0x00, /* 0x08-0x0f */
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x10-0x17 */
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0x18-0x1f */
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x20-0x27 */
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x28-0x2f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0x30-0x37 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0x38-0x3f */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0x40-0x47 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0x48-0x4f */
+ 0x00, 0xf1, 0x00, 0x00, 0xf3, 0x00, 0x00, 0xf5, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf7, 0x00, /* 0x58-0x5f */
+};
+
+static unsigned char page21[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, /* 0x10-0x17 */
+};
+
+static unsigned char page22[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0xf9, 0xfb, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+};
+
+static unsigned char page25[256] = {
+ 0xc4, 0x00, 0xb3, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0xbf, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0xd9, 0x00, 0x00, 0x00, 0xc3, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0xcd, 0xba, 0xd5, 0xd6, 0xc9, 0xb8, 0xb7, 0xbb, /* 0x50-0x57 */
+ 0xd4, 0xd3, 0xc8, 0xbe, 0xbd, 0xbc, 0xc6, 0xc7, /* 0x58-0x5f */
+ 0xcc, 0xb5, 0xb6, 0xb9, 0xd1, 0xd2, 0xcb, 0xcf, /* 0x60-0x67 */
+ 0xd0, 0xca, 0xd8, 0xd7, 0xce, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0xdf, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0xdb, 0x00, 0x00, 0x00, 0xdd, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0xde, 0xb0, 0xb1, 0xb2, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+};
+
+static unsigned char *page_uni2charset[256] = {
+ page00, NULL, NULL, NULL, page04, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, page21, page22, NULL, NULL, page25, NULL, NULL,
+};
+
+static unsigned char charset2lower[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40-0x47 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x48-0x4f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50-0x57 */
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0x80-0x87 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0x88-0x8f */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0x90-0x97 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf1, 0xf1, 0xf3, 0xf3, 0xf5, 0xf5, 0xf7, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static unsigned char charset2upper[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0x88-0x8f */
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x98-0x9f */
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0xa0-0xa7 */
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0xe0-0xe7 */
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0xe8-0xef */
+ 0xf0, 0xf0, 0xf2, 0xf2, 0xf4, 0xf4, 0xf6, 0xf6, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+ unsigned char *uni2charset;
+ unsigned char cl = uni & 0x00ff;
+ unsigned char ch = (uni & 0xff00) >> 8;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ uni2charset = page_uni2charset[ch];
+ if (uni2charset && uni2charset[cl])
+ out[0] = uni2charset[cl];
+ else
+ return -EINVAL;
+ return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+ *uni = charset2uni[*rawstring];
+ if (*uni == 0x0000)
+ return -EINVAL;
+ return 1;
+}
+
+static struct nls_table table = {
+ "cp866",
+ uni2char,
+ char2uni,
+ charset2lower,
+ charset2upper,
+ THIS_MODULE,
+};
+
+static int __init init_nls_cp866(void)
+{
+ return register_nls(&table);
+}
+
+static void __exit exit_nls_cp866(void)
+{
+ unregister_nls(&table);
+}
+
+module_init(init_nls_cp866)
+module_exit(exit_nls_cp866)
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/fs/nls/nls_cp869.c b/fs/nls/nls_cp869.c
new file mode 100644
index 00000000000000..dfa2bf5f37db8b
--- /dev/null
+++ b/fs/nls/nls_cp869.c
@@ -0,0 +1,332 @@
+/*
+ * linux/fs/nls_cp869.c
+ *
+ * Charset cp869 translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static wchar_t charset2uni[256] = {
+ /* 0x00*/
+ 0x0000, 0x0001, 0x0002, 0x0003,
+ 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b,
+ 0x000c, 0x000d, 0x000e, 0x000f,
+ /* 0x10*/
+ 0x0010, 0x0011, 0x0012, 0x0013,
+ 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b,
+ 0x001c, 0x001d, 0x001e, 0x001f,
+ /* 0x20*/
+ 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b,
+ 0x002c, 0x002d, 0x002e, 0x002f,
+ /* 0x30*/
+ 0x0030, 0x0031, 0x0032, 0x0033,
+ 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b,
+ 0x003c, 0x003d, 0x003e, 0x003f,
+ /* 0x40*/
+ 0x0040, 0x0041, 0x0042, 0x0043,
+ 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b,
+ 0x004c, 0x004d, 0x004e, 0x004f,
+ /* 0x50*/
+ 0x0050, 0x0051, 0x0052, 0x0053,
+ 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b,
+ 0x005c, 0x005d, 0x005e, 0x005f,
+ /* 0x60*/
+ 0x0060, 0x0061, 0x0062, 0x0063,
+ 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b,
+ 0x006c, 0x006d, 0x006e, 0x006f,
+ /* 0x70*/
+ 0x0070, 0x0071, 0x0072, 0x0073,
+ 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b,
+ 0x007c, 0x007d, 0x007e, 0x007f,
+ /* 0x80*/
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0386, 0x0000,
+ 0x00b7, 0x00ac, 0x00a6, 0x2018,
+ 0x2019, 0x0388, 0x2015, 0x0389,
+ /* 0x90*/
+ 0x038a, 0x03aa, 0x038c, 0x0000,
+ 0x0000, 0x038e, 0x03ab, 0x00a9,
+ 0x038f, 0x00b2, 0x00b3, 0x03ac,
+ 0x00a3, 0x03ad, 0x03ae, 0x03af,
+ /* 0xa0*/
+ 0x03ca, 0x0390, 0x03cc, 0x03cd,
+ 0x0391, 0x0392, 0x0393, 0x0394,
+ 0x0395, 0x0396, 0x0397, 0x00bd,
+ 0x0398, 0x0399, 0x00ab, 0x00bb,
+ /* 0xb0*/
+ 0x2591, 0x2592, 0x2593, 0x2502,
+ 0x2524, 0x039a, 0x039b, 0x039c,
+ 0x039d, 0x2563, 0x2551, 0x2557,
+ 0x255d, 0x039e, 0x039f, 0x2510,
+ /* 0xc0*/
+ 0x2514, 0x2534, 0x252c, 0x251c,
+ 0x2500, 0x253c, 0x03a0, 0x03a1,
+ 0x255a, 0x2554, 0x2569, 0x2566,
+ 0x2560, 0x2550, 0x256c, 0x03a3,
+ /* 0xd0*/
+ 0x03a4, 0x03a5, 0x03a6, 0x03a7,
+ 0x03a8, 0x03a9, 0x03b1, 0x03b2,
+ 0x03b3, 0x2518, 0x250c, 0x2588,
+ 0x2584, 0x03b4, 0x03b5, 0x2580,
+ /* 0xe0*/
+ 0x03b6, 0x03b7, 0x03b8, 0x03b9,
+ 0x03ba, 0x03bb, 0x03bc, 0x03bd,
+ 0x03be, 0x03bf, 0x03c0, 0x03c1,
+ 0x03c3, 0x03c2, 0x03c4, 0x0384,
+ /* 0xf0*/
+ 0x00ad, 0x00b1, 0x03c5, 0x03c6,
+ 0x03c7, 0x00a7, 0x03c8, 0x0385,
+ 0x00b0, 0x00a8, 0x03c9, 0x03cb,
+ 0x03b0, 0x03ce, 0x25a0, 0x00a0,
+};
+
+static unsigned char page00[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xff, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x8a, 0xf5, /* 0xa0-0xa7 */
+ 0xf9, 0x97, 0x00, 0xae, 0x89, 0xf0, 0x00, 0x00, /* 0xa8-0xaf */
+ 0xf8, 0xf1, 0x99, 0x9a, 0x00, 0x00, 0x00, 0x88, /* 0xb0-0xb7 */
+ 0x00, 0x00, 0x00, 0xaf, 0x00, 0xab, 0x00, 0x00, /* 0xb8-0xbf */
+};
+
+static unsigned char page03[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0xef, 0xf7, 0x86, 0x00, /* 0x80-0x87 */
+ 0x8d, 0x8f, 0x90, 0x00, 0x92, 0x00, 0x95, 0x98, /* 0x88-0x8f */
+ 0xa1, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, /* 0x90-0x97 */
+ 0xac, 0xad, 0xb5, 0xb6, 0xb7, 0xb8, 0xbd, 0xbe, /* 0x98-0x9f */
+ 0xc6, 0xc7, 0x00, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, /* 0xa0-0xa7 */
+ 0xd4, 0xd5, 0x91, 0x96, 0x9b, 0x9d, 0x9e, 0x9f, /* 0xa8-0xaf */
+ 0xfc, 0xd6, 0xd7, 0xd8, 0xdd, 0xde, 0xe0, 0xe1, /* 0xb0-0xb7 */
+ 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, /* 0xb8-0xbf */
+ 0xea, 0xeb, 0xed, 0xec, 0xee, 0xf2, 0xf3, 0xf4, /* 0xc0-0xc7 */
+ 0xf6, 0xfa, 0xa0, 0xfb, 0xa2, 0xa3, 0xfd, 0x00, /* 0xc8-0xcf */
+};
+
+static unsigned char page20[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, /* 0x10-0x17 */
+ 0x8b, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+};
+
+static unsigned char page25[256] = {
+ 0xc4, 0x00, 0xb3, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0xbf, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0xd9, 0x00, 0x00, 0x00, 0xc3, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0xc1, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0xcd, 0xba, 0x00, 0x00, 0xc9, 0x00, 0x00, 0xbb, /* 0x50-0x57 */
+ 0x00, 0x00, 0xc8, 0x00, 0x00, 0xbc, 0x00, 0x00, /* 0x58-0x5f */
+ 0xcc, 0x00, 0x00, 0xb9, 0x00, 0x00, 0xcb, 0x00, /* 0x60-0x67 */
+ 0x00, 0xca, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0xdf, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0xdb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0xb0, 0xb1, 0xb2, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+};
+
+static unsigned char *page_uni2charset[256] = {
+ page00, NULL, NULL, page03, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ page20, NULL, NULL, NULL, NULL, page25, NULL, NULL,
+};
+
+static unsigned char charset2lower[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40-0x47 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x48-0x4f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50-0x57 */
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9b, 0x00, /* 0x80-0x87 */
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x9d, 0x8e, 0x9e, /* 0x88-0x8f */
+ 0x9f, 0xa0, 0xa2, 0x00, 0x00, 0xa3, 0xfb, 0x97, /* 0x90-0x97 */
+ 0xfd, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xd6, 0xd7, 0xd8, 0xdd, /* 0xa0-0xa7 */
+ 0xde, 0xe0, 0xe1, 0xab, 0xe2, 0xe3, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xe4, 0xe5, 0xe6, /* 0xb0-0xb7 */
+ 0xe7, 0xb9, 0xba, 0xbb, 0xbc, 0xe8, 0xe9, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xea, 0xeb, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xec, /* 0xc8-0xcf */
+ 0xee, 0xf2, 0xf3, 0xf4, 0xf6, 0xfa, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static unsigned char charset2upper[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x00, /* 0x80-0x87 */
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0x88-0x8f */
+ 0x90, 0x91, 0x92, 0x00, 0x00, 0x95, 0x96, 0x97, /* 0x90-0x97 */
+ 0x98, 0x99, 0x9a, 0x86, 0x9c, 0x8d, 0x8f, 0x90, /* 0x98-0x9f */
+ 0x91, 0xa1, 0x92, 0x95, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xa4, 0xa5, /* 0xd0-0xd7 */
+ 0xa6, 0xd9, 0xda, 0xdb, 0xdc, 0xa7, 0xa8, 0xdf, /* 0xd8-0xdf */
+ 0xa9, 0xaa, 0xac, 0xad, 0xb5, 0xb6, 0xb7, 0xb8, /* 0xe0-0xe7 */
+ 0xbd, 0xbe, 0xc6, 0xc7, 0xcf, 0xcf, 0xd0, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xd1, 0xd2, 0xd3, 0xf5, 0xd4, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xd5, 0x96, 0xfc, 0x98, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+ unsigned char *uni2charset;
+ unsigned char cl = uni & 0x00ff;
+ unsigned char ch = (uni & 0xff00) >> 8;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ uni2charset = page_uni2charset[ch];
+ if (uni2charset && uni2charset[cl])
+ out[0] = uni2charset[cl];
+ else
+ return -EINVAL;
+ return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+ *uni = charset2uni[*rawstring];
+ if (*uni == 0x0000)
+ return -EINVAL;
+ return 1;
+}
+
+static struct nls_table table = {
+ "cp869",
+ uni2char,
+ char2uni,
+ charset2lower,
+ charset2upper,
+ THIS_MODULE,
+};
+
+static int __init init_nls_cp869(void)
+{
+ return register_nls(&table);
+}
+
+static void __exit exit_nls_cp869(void)
+{
+ unregister_nls(&table);
+}
+
+module_init(init_nls_cp869)
+module_exit(exit_nls_cp869)
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/fs/nls/nls_cp874.c b/fs/nls/nls_cp874.c
new file mode 100644
index 00000000000000..a40cf7b9a2138e
--- /dev/null
+++ b/fs/nls/nls_cp874.c
@@ -0,0 +1,290 @@
+/*
+ * linux/fs/nls_cp874.c
+ *
+ * Charset cp874 translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static wchar_t charset2uni[256] = {
+ /* 0x00*/
+ 0x0000, 0x0001, 0x0002, 0x0003,
+ 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b,
+ 0x000c, 0x000d, 0x000e, 0x000f,
+ /* 0x10*/
+ 0x0010, 0x0011, 0x0012, 0x0013,
+ 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b,
+ 0x001c, 0x001d, 0x001e, 0x001f,
+ /* 0x20*/
+ 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b,
+ 0x002c, 0x002d, 0x002e, 0x002f,
+ /* 0x30*/
+ 0x0030, 0x0031, 0x0032, 0x0033,
+ 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b,
+ 0x003c, 0x003d, 0x003e, 0x003f,
+ /* 0x40*/
+ 0x0040, 0x0041, 0x0042, 0x0043,
+ 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b,
+ 0x004c, 0x004d, 0x004e, 0x004f,
+ /* 0x50*/
+ 0x0050, 0x0051, 0x0052, 0x0053,
+ 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b,
+ 0x005c, 0x005d, 0x005e, 0x005f,
+ /* 0x60*/
+ 0x0060, 0x0061, 0x0062, 0x0063,
+ 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b,
+ 0x006c, 0x006d, 0x006e, 0x006f,
+ /* 0x70*/
+ 0x0070, 0x0071, 0x0072, 0x0073,
+ 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b,
+ 0x007c, 0x007d, 0x007e, 0x007f,
+ /* 0x80*/
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x2026, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0x90*/
+ 0x0000, 0x2018, 0x2019, 0x201c,
+ 0x201d, 0x2022, 0x2013, 0x2014,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0xa0*/
+ 0x00a0, 0x0e01, 0x0e02, 0x0e03,
+ 0x0e04, 0x0e05, 0x0e06, 0x0e07,
+ 0x0e08, 0x0e09, 0x0e0a, 0x0e0b,
+ 0x0e0c, 0x0e0d, 0x0e0e, 0x0e0f,
+ /* 0xb0*/
+ 0x0e10, 0x0e11, 0x0e12, 0x0e13,
+ 0x0e14, 0x0e15, 0x0e16, 0x0e17,
+ 0x0e18, 0x0e19, 0x0e1a, 0x0e1b,
+ 0x0e1c, 0x0e1d, 0x0e1e, 0x0e1f,
+ /* 0xc0*/
+ 0x0e20, 0x0e21, 0x0e22, 0x0e23,
+ 0x0e24, 0x0e25, 0x0e26, 0x0e27,
+ 0x0e28, 0x0e29, 0x0e2a, 0x0e2b,
+ 0x0e2c, 0x0e2d, 0x0e2e, 0x0e2f,
+ /* 0xd0*/
+ 0x0e30, 0x0e31, 0x0e32, 0x0e33,
+ 0x0e34, 0x0e35, 0x0e36, 0x0e37,
+ 0x0e38, 0x0e39, 0x0e3a, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0e3f,
+ /* 0xe0*/
+ 0x0e40, 0x0e41, 0x0e42, 0x0e43,
+ 0x0e44, 0x0e45, 0x0e46, 0x0e47,
+ 0x0e48, 0x0e49, 0x0e4a, 0x0e4b,
+ 0x0e4c, 0x0e4d, 0x0e4e, 0x0e4f,
+ /* 0xf0*/
+ 0x0e50, 0x0e51, 0x0e52, 0x0e53,
+ 0x0e54, 0x0e55, 0x0e56, 0x0e57,
+ 0x0e58, 0x0e59, 0x0e5a, 0x0e5b,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+};
+
+static unsigned char page00[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+};
+
+static unsigned char page0e[256] = {
+ 0x00, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0x00-0x07 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0x08-0x0f */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0x10-0x17 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0x18-0x1f */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0x20-0x27 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0x28-0x2f */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0x30-0x37 */
+ 0xd8, 0xd9, 0xda, 0x00, 0x00, 0x00, 0x00, 0xdf, /* 0x38-0x3f */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0x40-0x47 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0x48-0x4f */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0x50-0x57 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+};
+
+static unsigned char page20[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x96, 0x97, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x91, 0x92, 0x00, 0x00, 0x93, 0x94, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x85, 0x00, /* 0x20-0x27 */
+};
+
+static unsigned char *page_uni2charset[256] = {
+ page00, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, page0e, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ page20, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+};
+
+static unsigned char charset2lower[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40-0x47 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x48-0x4f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50-0x57 */
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0x00, 0x00, 0x00, 0x00, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static unsigned char charset2upper[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0x00, 0x00, 0x00, 0x00, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+ unsigned char *uni2charset;
+ unsigned char cl = uni & 0x00ff;
+ unsigned char ch = (uni & 0xff00) >> 8;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ uni2charset = page_uni2charset[ch];
+ if (uni2charset && uni2charset[cl])
+ out[0] = uni2charset[cl];
+ else
+ return -EINVAL;
+ return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+ *uni = charset2uni[*rawstring];
+ if (*uni == 0x0000)
+ return -EINVAL;
+ return 1;
+}
+
+static struct nls_table table = {
+ "cp874",
+ uni2char,
+ char2uni,
+ charset2lower,
+ charset2upper,
+ THIS_MODULE,
+};
+
+static int __init init_nls_cp874(void)
+{
+ return register_nls(&table);
+}
+
+static void __exit exit_nls_cp874(void)
+{
+ unregister_nls(&table);
+}
+
+module_init(init_nls_cp874)
+module_exit(exit_nls_cp874)
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/fs/nls/nls_cp932.c b/fs/nls/nls_cp932.c
new file mode 100644
index 00000000000000..72c4875cc88367
--- /dev/null
+++ b/fs/nls/nls_cp932.c
@@ -0,0 +1,7925 @@
+/*
+ * linux/fs/nls_cp932.c
+ *
+ * Charset cp932 translation tables.
+ * This translation table was generated automatically, the
+ * original table can be download from the Microsoft website.
+ * (http://www.microsoft.com/typography/unicode/unicodecp.htm)
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static wchar_t c2u_81[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x3000,0x3001,0x3002,0xFF0C,0xFF0E,0x30FB,0xFF1A,0xFF1B,/* 0x40-0x47 */
+ 0xFF1F,0xFF01,0x309B,0x309C,0x00B4,0xFF40,0x00A8,0xFF3E,/* 0x48-0x4F */
+ 0xFFE3,0xFF3F,0x30FD,0x30FE,0x309D,0x309E,0x3003,0x4EDD,/* 0x50-0x57 */
+ 0x3005,0x3006,0x3007,0x30FC,0x2015,0x2010,0xFF0F,0xFF3C,/* 0x58-0x5F */
+ 0xFF5E,0x2225,0xFF5C,0x2026,0x2025,0x2018,0x2019,0x201C,/* 0x60-0x67 */
+ 0x201D,0xFF08,0xFF09,0x3014,0x3015,0xFF3B,0xFF3D,0xFF5B,/* 0x68-0x6F */
+ 0xFF5D,0x3008,0x3009,0x300A,0x300B,0x300C,0x300D,0x300E,/* 0x70-0x77 */
+ 0x300F,0x3010,0x3011,0xFF0B,0xFF0D,0x00B1,0x00D7,0x0000,/* 0x78-0x7F */
+
+ 0x00F7,0xFF1D,0x2260,0xFF1C,0xFF1E,0x2266,0x2267,0x221E,/* 0x80-0x87 */
+ 0x2234,0x2642,0x2640,0x00B0,0x2032,0x2033,0x2103,0xFFE5,/* 0x88-0x8F */
+ 0xFF04,0xFFE0,0xFFE1,0xFF05,0xFF03,0xFF06,0xFF0A,0xFF20,/* 0x90-0x97 */
+ 0x00A7,0x2606,0x2605,0x25CB,0x25CF,0x25CE,0x25C7,0x25C6,/* 0x98-0x9F */
+ 0x25A1,0x25A0,0x25B3,0x25B2,0x25BD,0x25BC,0x203B,0x3012,/* 0xA0-0xA7 */
+ 0x2192,0x2190,0x2191,0x2193,0x3013,0x0000,0x0000,0x0000,/* 0xA8-0xAF */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xB0-0xB7 */
+ 0x2208,0x220B,0x2286,0x2287,0x2282,0x2283,0x222A,0x2229,/* 0xB8-0xBF */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xC0-0xC7 */
+ 0x2227,0x2228,0xFFE2,0x21D2,0x21D4,0x2200,0x2203,0x0000,/* 0xC8-0xCF */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xD0-0xD7 */
+ 0x0000,0x0000,0x2220,0x22A5,0x2312,0x2202,0x2207,0x2261,/* 0xD8-0xDF */
+ 0x2252,0x226A,0x226B,0x221A,0x223D,0x221D,0x2235,0x222B,/* 0xE0-0xE7 */
+ 0x222C,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xE8-0xEF */
+ 0x212B,0x2030,0x266F,0x266D,0x266A,0x2020,0x2021,0x00B6,/* 0xF0-0xF7 */
+ 0x0000,0x0000,0x0000,0x0000,0x25EF,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_82[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xFF10,/* 0x48-0x4F */
+ 0xFF11,0xFF12,0xFF13,0xFF14,0xFF15,0xFF16,0xFF17,0xFF18,/* 0x50-0x57 */
+ 0xFF19,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0xFF21,0xFF22,0xFF23,0xFF24,0xFF25,0xFF26,0xFF27,0xFF28,/* 0x60-0x67 */
+ 0xFF29,0xFF2A,0xFF2B,0xFF2C,0xFF2D,0xFF2E,0xFF2F,0xFF30,/* 0x68-0x6F */
+ 0xFF31,0xFF32,0xFF33,0xFF34,0xFF35,0xFF36,0xFF37,0xFF38,/* 0x70-0x77 */
+ 0xFF39,0xFF3A,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xFF41,0xFF42,0xFF43,0xFF44,0xFF45,0xFF46,0xFF47,/* 0x80-0x87 */
+ 0xFF48,0xFF49,0xFF4A,0xFF4B,0xFF4C,0xFF4D,0xFF4E,0xFF4F,/* 0x88-0x8F */
+ 0xFF50,0xFF51,0xFF52,0xFF53,0xFF54,0xFF55,0xFF56,0xFF57,/* 0x90-0x97 */
+ 0xFF58,0xFF59,0xFF5A,0x0000,0x0000,0x0000,0x0000,0x3041,/* 0x98-0x9F */
+ 0x3042,0x3043,0x3044,0x3045,0x3046,0x3047,0x3048,0x3049,/* 0xA0-0xA7 */
+ 0x304A,0x304B,0x304C,0x304D,0x304E,0x304F,0x3050,0x3051,/* 0xA8-0xAF */
+ 0x3052,0x3053,0x3054,0x3055,0x3056,0x3057,0x3058,0x3059,/* 0xB0-0xB7 */
+ 0x305A,0x305B,0x305C,0x305D,0x305E,0x305F,0x3060,0x3061,/* 0xB8-0xBF */
+ 0x3062,0x3063,0x3064,0x3065,0x3066,0x3067,0x3068,0x3069,/* 0xC0-0xC7 */
+ 0x306A,0x306B,0x306C,0x306D,0x306E,0x306F,0x3070,0x3071,/* 0xC8-0xCF */
+ 0x3072,0x3073,0x3074,0x3075,0x3076,0x3077,0x3078,0x3079,/* 0xD0-0xD7 */
+ 0x307A,0x307B,0x307C,0x307D,0x307E,0x307F,0x3080,0x3081,/* 0xD8-0xDF */
+ 0x3082,0x3083,0x3084,0x3085,0x3086,0x3087,0x3088,0x3089,/* 0xE0-0xE7 */
+ 0x308A,0x308B,0x308C,0x308D,0x308E,0x308F,0x3090,0x3091,/* 0xE8-0xEF */
+ 0x3092,0x3093,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xF0-0xF7 */
+};
+
+static wchar_t c2u_83[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x30A1,0x30A2,0x30A3,0x30A4,0x30A5,0x30A6,0x30A7,0x30A8,/* 0x40-0x47 */
+ 0x30A9,0x30AA,0x30AB,0x30AC,0x30AD,0x30AE,0x30AF,0x30B0,/* 0x48-0x4F */
+ 0x30B1,0x30B2,0x30B3,0x30B4,0x30B5,0x30B6,0x30B7,0x30B8,/* 0x50-0x57 */
+ 0x30B9,0x30BA,0x30BB,0x30BC,0x30BD,0x30BE,0x30BF,0x30C0,/* 0x58-0x5F */
+ 0x30C1,0x30C2,0x30C3,0x30C4,0x30C5,0x30C6,0x30C7,0x30C8,/* 0x60-0x67 */
+ 0x30C9,0x30CA,0x30CB,0x30CC,0x30CD,0x30CE,0x30CF,0x30D0,/* 0x68-0x6F */
+ 0x30D1,0x30D2,0x30D3,0x30D4,0x30D5,0x30D6,0x30D7,0x30D8,/* 0x70-0x77 */
+ 0x30D9,0x30DA,0x30DB,0x30DC,0x30DD,0x30DE,0x30DF,0x0000,/* 0x78-0x7F */
+
+ 0x30E0,0x30E1,0x30E2,0x30E3,0x30E4,0x30E5,0x30E6,0x30E7,/* 0x80-0x87 */
+ 0x30E8,0x30E9,0x30EA,0x30EB,0x30EC,0x30ED,0x30EE,0x30EF,/* 0x88-0x8F */
+ 0x30F0,0x30F1,0x30F2,0x30F3,0x30F4,0x30F5,0x30F6,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0391,/* 0x98-0x9F */
+ 0x0392,0x0393,0x0394,0x0395,0x0396,0x0397,0x0398,0x0399,/* 0xA0-0xA7 */
+ 0x039A,0x039B,0x039C,0x039D,0x039E,0x039F,0x03A0,0x03A1,/* 0xA8-0xAF */
+ 0x03A3,0x03A4,0x03A5,0x03A6,0x03A7,0x03A8,0x03A9,0x0000,/* 0xB0-0xB7 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x03B1,/* 0xB8-0xBF */
+ 0x03B2,0x03B3,0x03B4,0x03B5,0x03B6,0x03B7,0x03B8,0x03B9,/* 0xC0-0xC7 */
+ 0x03BA,0x03BB,0x03BC,0x03BD,0x03BE,0x03BF,0x03C0,0x03C1,/* 0xC8-0xCF */
+ 0x03C3,0x03C4,0x03C5,0x03C6,0x03C7,0x03C8,0x03C9,0x0000,/* 0xD0-0xD7 */
+};
+
+static wchar_t c2u_84[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0410,0x0411,0x0412,0x0413,0x0414,0x0415,0x0401,0x0416,/* 0x40-0x47 */
+ 0x0417,0x0418,0x0419,0x041A,0x041B,0x041C,0x041D,0x041E,/* 0x48-0x4F */
+ 0x041F,0x0420,0x0421,0x0422,0x0423,0x0424,0x0425,0x0426,/* 0x50-0x57 */
+ 0x0427,0x0428,0x0429,0x042A,0x042B,0x042C,0x042D,0x042E,/* 0x58-0x5F */
+ 0x042F,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0430,0x0431,0x0432,0x0433,0x0434,0x0435,0x0451,0x0436,/* 0x70-0x77 */
+ 0x0437,0x0438,0x0439,0x043A,0x043B,0x043C,0x043D,0x0000,/* 0x78-0x7F */
+
+ 0x043E,0x043F,0x0440,0x0441,0x0442,0x0443,0x0444,0x0445,/* 0x80-0x87 */
+ 0x0446,0x0447,0x0448,0x0449,0x044A,0x044B,0x044C,0x044D,/* 0x88-0x8F */
+ 0x044E,0x044F,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x2500,/* 0x98-0x9F */
+ 0x2502,0x250C,0x2510,0x2518,0x2514,0x251C,0x252C,0x2524,/* 0xA0-0xA7 */
+ 0x2534,0x253C,0x2501,0x2503,0x250F,0x2513,0x251B,0x2517,/* 0xA8-0xAF */
+ 0x2523,0x2533,0x252B,0x253B,0x254B,0x2520,0x252F,0x2528,/* 0xB0-0xB7 */
+ 0x2537,0x253F,0x251D,0x2530,0x2525,0x2538,0x2542,0x0000,/* 0xB8-0xBF */
+};
+
+static wchar_t c2u_87[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x2460,0x2461,0x2462,0x2463,0x2464,0x2465,0x2466,0x2467,/* 0x40-0x47 */
+ 0x2468,0x2469,0x246A,0x246B,0x246C,0x246D,0x246E,0x246F,/* 0x48-0x4F */
+ 0x2470,0x2471,0x2472,0x2473,0x2160,0x2161,0x2162,0x2163,/* 0x50-0x57 */
+ 0x2164,0x2165,0x2166,0x2167,0x2168,0x2169,0x0000,0x3349,/* 0x58-0x5F */
+ 0x3314,0x3322,0x334D,0x3318,0x3327,0x3303,0x3336,0x3351,/* 0x60-0x67 */
+ 0x3357,0x330D,0x3326,0x3323,0x332B,0x334A,0x333B,0x339C,/* 0x68-0x6F */
+ 0x339D,0x339E,0x338E,0x338F,0x33C4,0x33A1,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x337B,0x0000,/* 0x78-0x7F */
+
+ 0x301D,0x301F,0x2116,0x33CD,0x2121,0x32A4,0x32A5,0x32A6,/* 0x80-0x87 */
+ 0x32A7,0x32A8,0x3231,0x3232,0x3239,0x337E,0x337D,0x337C,/* 0x88-0x8F */
+ 0x2252,0x2261,0x222B,0x222E,0x2211,0x221A,0x22A5,0x2220,/* 0x90-0x97 */
+ 0x221F,0x22BF,0x2235,0x2229,0x222A,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+};
+
+static wchar_t c2u_88[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x4E9C,/* 0x98-0x9F */
+ 0x5516,0x5A03,0x963F,0x54C0,0x611B,0x6328,0x59F6,0x9022,/* 0xA0-0xA7 */
+ 0x8475,0x831C,0x7A50,0x60AA,0x63E1,0x6E25,0x65ED,0x8466,/* 0xA8-0xAF */
+ 0x82A6,0x9BF5,0x6893,0x5727,0x65A1,0x6271,0x5B9B,0x59D0,/* 0xB0-0xB7 */
+ 0x867B,0x98F4,0x7D62,0x7DBE,0x9B8E,0x6216,0x7C9F,0x88B7,/* 0xB8-0xBF */
+ 0x5B89,0x5EB5,0x6309,0x6697,0x6848,0x95C7,0x978D,0x674F,/* 0xC0-0xC7 */
+ 0x4EE5,0x4F0A,0x4F4D,0x4F9D,0x5049,0x56F2,0x5937,0x59D4,/* 0xC8-0xCF */
+ 0x5A01,0x5C09,0x60DF,0x610F,0x6170,0x6613,0x6905,0x70BA,/* 0xD0-0xD7 */
+ 0x754F,0x7570,0x79FB,0x7DAD,0x7DEF,0x80C3,0x840E,0x8863,/* 0xD8-0xDF */
+ 0x8B02,0x9055,0x907A,0x533B,0x4E95,0x4EA5,0x57DF,0x80B2,/* 0xE0-0xE7 */
+ 0x90C1,0x78EF,0x4E00,0x58F1,0x6EA2,0x9038,0x7A32,0x8328,/* 0xE8-0xEF */
+ 0x828B,0x9C2F,0x5141,0x5370,0x54BD,0x54E1,0x56E0,0x59FB,/* 0xF0-0xF7 */
+ 0x5F15,0x98F2,0x6DEB,0x80E4,0x852D,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_89[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x9662,0x9670,0x96A0,0x97FB,0x540B,0x53F3,0x5B87,0x70CF,/* 0x40-0x47 */
+ 0x7FBD,0x8FC2,0x96E8,0x536F,0x9D5C,0x7ABA,0x4E11,0x7893,/* 0x48-0x4F */
+ 0x81FC,0x6E26,0x5618,0x5504,0x6B1D,0x851A,0x9C3B,0x59E5,/* 0x50-0x57 */
+ 0x53A9,0x6D66,0x74DC,0x958F,0x5642,0x4E91,0x904B,0x96F2,/* 0x58-0x5F */
+ 0x834F,0x990C,0x53E1,0x55B6,0x5B30,0x5F71,0x6620,0x66F3,/* 0x60-0x67 */
+ 0x6804,0x6C38,0x6CF3,0x6D29,0x745B,0x76C8,0x7A4E,0x9834,/* 0x68-0x6F */
+ 0x82F1,0x885B,0x8A60,0x92ED,0x6DB2,0x75AB,0x76CA,0x99C5,/* 0x70-0x77 */
+ 0x60A6,0x8B01,0x8D8A,0x95B2,0x698E,0x53AD,0x5186,0x0000,/* 0x78-0x7F */
+
+ 0x5712,0x5830,0x5944,0x5BB4,0x5EF6,0x6028,0x63A9,0x63F4,/* 0x80-0x87 */
+ 0x6CBF,0x6F14,0x708E,0x7114,0x7159,0x71D5,0x733F,0x7E01,/* 0x88-0x8F */
+ 0x8276,0x82D1,0x8597,0x9060,0x925B,0x9D1B,0x5869,0x65BC,/* 0x90-0x97 */
+ 0x6C5A,0x7525,0x51F9,0x592E,0x5965,0x5F80,0x5FDC,0x62BC,/* 0x98-0x9F */
+ 0x65FA,0x6A2A,0x6B27,0x6BB4,0x738B,0x7FC1,0x8956,0x9D2C,/* 0xA0-0xA7 */
+ 0x9D0E,0x9EC4,0x5CA1,0x6C96,0x837B,0x5104,0x5C4B,0x61B6,/* 0xA8-0xAF */
+ 0x81C6,0x6876,0x7261,0x4E59,0x4FFA,0x5378,0x6069,0x6E29,/* 0xB0-0xB7 */
+ 0x7A4F,0x97F3,0x4E0B,0x5316,0x4EEE,0x4F55,0x4F3D,0x4FA1,/* 0xB8-0xBF */
+ 0x4F73,0x52A0,0x53EF,0x5609,0x590F,0x5AC1,0x5BB6,0x5BE1,/* 0xC0-0xC7 */
+ 0x79D1,0x6687,0x679C,0x67B6,0x6B4C,0x6CB3,0x706B,0x73C2,/* 0xC8-0xCF */
+ 0x798D,0x79BE,0x7A3C,0x7B87,0x82B1,0x82DB,0x8304,0x8377,/* 0xD0-0xD7 */
+ 0x83EF,0x83D3,0x8766,0x8AB2,0x5629,0x8CA8,0x8FE6,0x904E,/* 0xD8-0xDF */
+ 0x971E,0x868A,0x4FC4,0x5CE8,0x6211,0x7259,0x753B,0x81E5,/* 0xE0-0xE7 */
+ 0x82BD,0x86FE,0x8CC0,0x96C5,0x9913,0x99D5,0x4ECB,0x4F1A,/* 0xE8-0xEF */
+ 0x89E3,0x56DE,0x584A,0x58CA,0x5EFB,0x5FEB,0x602A,0x6094,/* 0xF0-0xF7 */
+ 0x6062,0x61D0,0x6212,0x62D0,0x6539,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_8A[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x9B41,0x6666,0x68B0,0x6D77,0x7070,0x754C,0x7686,0x7D75,/* 0x40-0x47 */
+ 0x82A5,0x87F9,0x958B,0x968E,0x8C9D,0x51F1,0x52BE,0x5916,/* 0x48-0x4F */
+ 0x54B3,0x5BB3,0x5D16,0x6168,0x6982,0x6DAF,0x788D,0x84CB,/* 0x50-0x57 */
+ 0x8857,0x8A72,0x93A7,0x9AB8,0x6D6C,0x99A8,0x86D9,0x57A3,/* 0x58-0x5F */
+ 0x67FF,0x86CE,0x920E,0x5283,0x5687,0x5404,0x5ED3,0x62E1,/* 0x60-0x67 */
+ 0x64B9,0x683C,0x6838,0x6BBB,0x7372,0x78BA,0x7A6B,0x899A,/* 0x68-0x6F */
+ 0x89D2,0x8D6B,0x8F03,0x90ED,0x95A3,0x9694,0x9769,0x5B66,/* 0x70-0x77 */
+ 0x5CB3,0x697D,0x984D,0x984E,0x639B,0x7B20,0x6A2B,0x0000,/* 0x78-0x7F */
+
+ 0x6A7F,0x68B6,0x9C0D,0x6F5F,0x5272,0x559D,0x6070,0x62EC,/* 0x80-0x87 */
+ 0x6D3B,0x6E07,0x6ED1,0x845B,0x8910,0x8F44,0x4E14,0x9C39,/* 0x88-0x8F */
+ 0x53F6,0x691B,0x6A3A,0x9784,0x682A,0x515C,0x7AC3,0x84B2,/* 0x90-0x97 */
+ 0x91DC,0x938C,0x565B,0x9D28,0x6822,0x8305,0x8431,0x7CA5,/* 0x98-0x9F */
+ 0x5208,0x82C5,0x74E6,0x4E7E,0x4F83,0x51A0,0x5BD2,0x520A,/* 0xA0-0xA7 */
+ 0x52D8,0x52E7,0x5DFB,0x559A,0x582A,0x59E6,0x5B8C,0x5B98,/* 0xA8-0xAF */
+ 0x5BDB,0x5E72,0x5E79,0x60A3,0x611F,0x6163,0x61BE,0x63DB,/* 0xB0-0xB7 */
+ 0x6562,0x67D1,0x6853,0x68FA,0x6B3E,0x6B53,0x6C57,0x6F22,/* 0xB8-0xBF */
+ 0x6F97,0x6F45,0x74B0,0x7518,0x76E3,0x770B,0x7AFF,0x7BA1,/* 0xC0-0xC7 */
+ 0x7C21,0x7DE9,0x7F36,0x7FF0,0x809D,0x8266,0x839E,0x89B3,/* 0xC8-0xCF */
+ 0x8ACC,0x8CAB,0x9084,0x9451,0x9593,0x9591,0x95A2,0x9665,/* 0xD0-0xD7 */
+ 0x97D3,0x9928,0x8218,0x4E38,0x542B,0x5CB8,0x5DCC,0x73A9,/* 0xD8-0xDF */
+ 0x764C,0x773C,0x5CA9,0x7FEB,0x8D0B,0x96C1,0x9811,0x9854,/* 0xE0-0xE7 */
+ 0x9858,0x4F01,0x4F0E,0x5371,0x559C,0x5668,0x57FA,0x5947,/* 0xE8-0xEF */
+ 0x5B09,0x5BC4,0x5C90,0x5E0C,0x5E7E,0x5FCC,0x63EE,0x673A,/* 0xF0-0xF7 */
+ 0x65D7,0x65E2,0x671F,0x68CB,0x68C4,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_8B[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x6A5F,0x5E30,0x6BC5,0x6C17,0x6C7D,0x757F,0x7948,0x5B63,/* 0x40-0x47 */
+ 0x7A00,0x7D00,0x5FBD,0x898F,0x8A18,0x8CB4,0x8D77,0x8ECC,/* 0x48-0x4F */
+ 0x8F1D,0x98E2,0x9A0E,0x9B3C,0x4E80,0x507D,0x5100,0x5993,/* 0x50-0x57 */
+ 0x5B9C,0x622F,0x6280,0x64EC,0x6B3A,0x72A0,0x7591,0x7947,/* 0x58-0x5F */
+ 0x7FA9,0x87FB,0x8ABC,0x8B70,0x63AC,0x83CA,0x97A0,0x5409,/* 0x60-0x67 */
+ 0x5403,0x55AB,0x6854,0x6A58,0x8A70,0x7827,0x6775,0x9ECD,/* 0x68-0x6F */
+ 0x5374,0x5BA2,0x811A,0x8650,0x9006,0x4E18,0x4E45,0x4EC7,/* 0x70-0x77 */
+ 0x4F11,0x53CA,0x5438,0x5BAE,0x5F13,0x6025,0x6551,0x0000,/* 0x78-0x7F */
+
+ 0x673D,0x6C42,0x6C72,0x6CE3,0x7078,0x7403,0x7A76,0x7AAE,/* 0x80-0x87 */
+ 0x7B08,0x7D1A,0x7CFE,0x7D66,0x65E7,0x725B,0x53BB,0x5C45,/* 0x88-0x8F */
+ 0x5DE8,0x62D2,0x62E0,0x6319,0x6E20,0x865A,0x8A31,0x8DDD,/* 0x90-0x97 */
+ 0x92F8,0x6F01,0x79A6,0x9B5A,0x4EA8,0x4EAB,0x4EAC,0x4F9B,/* 0x98-0x9F */
+ 0x4FA0,0x50D1,0x5147,0x7AF6,0x5171,0x51F6,0x5354,0x5321,/* 0xA0-0xA7 */
+ 0x537F,0x53EB,0x55AC,0x5883,0x5CE1,0x5F37,0x5F4A,0x602F,/* 0xA8-0xAF */
+ 0x6050,0x606D,0x631F,0x6559,0x6A4B,0x6CC1,0x72C2,0x72ED,/* 0xB0-0xB7 */
+ 0x77EF,0x80F8,0x8105,0x8208,0x854E,0x90F7,0x93E1,0x97FF,/* 0xB8-0xBF */
+ 0x9957,0x9A5A,0x4EF0,0x51DD,0x5C2D,0x6681,0x696D,0x5C40,/* 0xC0-0xC7 */
+ 0x66F2,0x6975,0x7389,0x6850,0x7C81,0x50C5,0x52E4,0x5747,/* 0xC8-0xCF */
+ 0x5DFE,0x9326,0x65A4,0x6B23,0x6B3D,0x7434,0x7981,0x79BD,/* 0xD0-0xD7 */
+ 0x7B4B,0x7DCA,0x82B9,0x83CC,0x887F,0x895F,0x8B39,0x8FD1,/* 0xD8-0xDF */
+ 0x91D1,0x541F,0x9280,0x4E5D,0x5036,0x53E5,0x533A,0x72D7,/* 0xE0-0xE7 */
+ 0x7396,0x77E9,0x82E6,0x8EAF,0x99C6,0x99C8,0x99D2,0x5177,/* 0xE8-0xEF */
+ 0x611A,0x865E,0x55B0,0x7A7A,0x5076,0x5BD3,0x9047,0x9685,/* 0xF0-0xF7 */
+ 0x4E32,0x6ADB,0x91E7,0x5C51,0x5C48,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_8C[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x6398,0x7A9F,0x6C93,0x9774,0x8F61,0x7AAA,0x718A,0x9688,/* 0x40-0x47 */
+ 0x7C82,0x6817,0x7E70,0x6851,0x936C,0x52F2,0x541B,0x85AB,/* 0x48-0x4F */
+ 0x8A13,0x7FA4,0x8ECD,0x90E1,0x5366,0x8888,0x7941,0x4FC2,/* 0x50-0x57 */
+ 0x50BE,0x5211,0x5144,0x5553,0x572D,0x73EA,0x578B,0x5951,/* 0x58-0x5F */
+ 0x5F62,0x5F84,0x6075,0x6176,0x6167,0x61A9,0x63B2,0x643A,/* 0x60-0x67 */
+ 0x656C,0x666F,0x6842,0x6E13,0x7566,0x7A3D,0x7CFB,0x7D4C,/* 0x68-0x6F */
+ 0x7D99,0x7E4B,0x7F6B,0x830E,0x834A,0x86CD,0x8A08,0x8A63,/* 0x70-0x77 */
+ 0x8B66,0x8EFD,0x981A,0x9D8F,0x82B8,0x8FCE,0x9BE8,0x0000,/* 0x78-0x7F */
+
+ 0x5287,0x621F,0x6483,0x6FC0,0x9699,0x6841,0x5091,0x6B20,/* 0x80-0x87 */
+ 0x6C7A,0x6F54,0x7A74,0x7D50,0x8840,0x8A23,0x6708,0x4EF6,/* 0x88-0x8F */
+ 0x5039,0x5026,0x5065,0x517C,0x5238,0x5263,0x55A7,0x570F,/* 0x90-0x97 */
+ 0x5805,0x5ACC,0x5EFA,0x61B2,0x61F8,0x62F3,0x6372,0x691C,/* 0x98-0x9F */
+ 0x6A29,0x727D,0x72AC,0x732E,0x7814,0x786F,0x7D79,0x770C,/* 0xA0-0xA7 */
+ 0x80A9,0x898B,0x8B19,0x8CE2,0x8ED2,0x9063,0x9375,0x967A,/* 0xA8-0xAF */
+ 0x9855,0x9A13,0x9E78,0x5143,0x539F,0x53B3,0x5E7B,0x5F26,/* 0xB0-0xB7 */
+ 0x6E1B,0x6E90,0x7384,0x73FE,0x7D43,0x8237,0x8A00,0x8AFA,/* 0xB8-0xBF */
+ 0x9650,0x4E4E,0x500B,0x53E4,0x547C,0x56FA,0x59D1,0x5B64,/* 0xC0-0xC7 */
+ 0x5DF1,0x5EAB,0x5F27,0x6238,0x6545,0x67AF,0x6E56,0x72D0,/* 0xC8-0xCF */
+ 0x7CCA,0x88B4,0x80A1,0x80E1,0x83F0,0x864E,0x8A87,0x8DE8,/* 0xD0-0xD7 */
+ 0x9237,0x96C7,0x9867,0x9F13,0x4E94,0x4E92,0x4F0D,0x5348,/* 0xD8-0xDF */
+ 0x5449,0x543E,0x5A2F,0x5F8C,0x5FA1,0x609F,0x68A7,0x6A8E,/* 0xE0-0xE7 */
+ 0x745A,0x7881,0x8A9E,0x8AA4,0x8B77,0x9190,0x4E5E,0x9BC9,/* 0xE8-0xEF */
+ 0x4EA4,0x4F7C,0x4FAF,0x5019,0x5016,0x5149,0x516C,0x529F,/* 0xF0-0xF7 */
+ 0x52B9,0x52FE,0x539A,0x53E3,0x5411,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_8D[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x540E,0x5589,0x5751,0x57A2,0x597D,0x5B54,0x5B5D,0x5B8F,/* 0x40-0x47 */
+ 0x5DE5,0x5DE7,0x5DF7,0x5E78,0x5E83,0x5E9A,0x5EB7,0x5F18,/* 0x48-0x4F */
+ 0x6052,0x614C,0x6297,0x62D8,0x63A7,0x653B,0x6602,0x6643,/* 0x50-0x57 */
+ 0x66F4,0x676D,0x6821,0x6897,0x69CB,0x6C5F,0x6D2A,0x6D69,/* 0x58-0x5F */
+ 0x6E2F,0x6E9D,0x7532,0x7687,0x786C,0x7A3F,0x7CE0,0x7D05,/* 0x60-0x67 */
+ 0x7D18,0x7D5E,0x7DB1,0x8015,0x8003,0x80AF,0x80B1,0x8154,/* 0x68-0x6F */
+ 0x818F,0x822A,0x8352,0x884C,0x8861,0x8B1B,0x8CA2,0x8CFC,/* 0x70-0x77 */
+ 0x90CA,0x9175,0x9271,0x783F,0x92FC,0x95A4,0x964D,0x0000,/* 0x78-0x7F */
+
+ 0x9805,0x9999,0x9AD8,0x9D3B,0x525B,0x52AB,0x53F7,0x5408,/* 0x80-0x87 */
+ 0x58D5,0x62F7,0x6FE0,0x8C6A,0x8F5F,0x9EB9,0x514B,0x523B,/* 0x88-0x8F */
+ 0x544A,0x56FD,0x7A40,0x9177,0x9D60,0x9ED2,0x7344,0x6F09,/* 0x90-0x97 */
+ 0x8170,0x7511,0x5FFD,0x60DA,0x9AA8,0x72DB,0x8FBC,0x6B64,/* 0x98-0x9F */
+ 0x9803,0x4ECA,0x56F0,0x5764,0x58BE,0x5A5A,0x6068,0x61C7,/* 0xA0-0xA7 */
+ 0x660F,0x6606,0x6839,0x68B1,0x6DF7,0x75D5,0x7D3A,0x826E,/* 0xA8-0xAF */
+ 0x9B42,0x4E9B,0x4F50,0x53C9,0x5506,0x5D6F,0x5DE6,0x5DEE,/* 0xB0-0xB7 */
+ 0x67FB,0x6C99,0x7473,0x7802,0x8A50,0x9396,0x88DF,0x5750,/* 0xB8-0xBF */
+ 0x5EA7,0x632B,0x50B5,0x50AC,0x518D,0x6700,0x54C9,0x585E,/* 0xC0-0xC7 */
+ 0x59BB,0x5BB0,0x5F69,0x624D,0x63A1,0x683D,0x6B73,0x6E08,/* 0xC8-0xCF */
+ 0x707D,0x91C7,0x7280,0x7815,0x7826,0x796D,0x658E,0x7D30,/* 0xD0-0xD7 */
+ 0x83DC,0x88C1,0x8F09,0x969B,0x5264,0x5728,0x6750,0x7F6A,/* 0xD8-0xDF */
+ 0x8CA1,0x51B4,0x5742,0x962A,0x583A,0x698A,0x80B4,0x54B2,/* 0xE0-0xE7 */
+ 0x5D0E,0x57FC,0x7895,0x9DFA,0x4F5C,0x524A,0x548B,0x643E,/* 0xE8-0xEF */
+ 0x6628,0x6714,0x67F5,0x7A84,0x7B56,0x7D22,0x932F,0x685C,/* 0xF0-0xF7 */
+ 0x9BAD,0x7B39,0x5319,0x518A,0x5237,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_8E[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x5BDF,0x62F6,0x64AE,0x64E6,0x672D,0x6BBA,0x85A9,0x96D1,/* 0x40-0x47 */
+ 0x7690,0x9BD6,0x634C,0x9306,0x9BAB,0x76BF,0x6652,0x4E09,/* 0x48-0x4F */
+ 0x5098,0x53C2,0x5C71,0x60E8,0x6492,0x6563,0x685F,0x71E6,/* 0x50-0x57 */
+ 0x73CA,0x7523,0x7B97,0x7E82,0x8695,0x8B83,0x8CDB,0x9178,/* 0x58-0x5F */
+ 0x9910,0x65AC,0x66AB,0x6B8B,0x4ED5,0x4ED4,0x4F3A,0x4F7F,/* 0x60-0x67 */
+ 0x523A,0x53F8,0x53F2,0x55E3,0x56DB,0x58EB,0x59CB,0x59C9,/* 0x68-0x6F */
+ 0x59FF,0x5B50,0x5C4D,0x5E02,0x5E2B,0x5FD7,0x601D,0x6307,/* 0x70-0x77 */
+ 0x652F,0x5B5C,0x65AF,0x65BD,0x65E8,0x679D,0x6B62,0x0000,/* 0x78-0x7F */
+
+ 0x6B7B,0x6C0F,0x7345,0x7949,0x79C1,0x7CF8,0x7D19,0x7D2B,/* 0x80-0x87 */
+ 0x80A2,0x8102,0x81F3,0x8996,0x8A5E,0x8A69,0x8A66,0x8A8C,/* 0x88-0x8F */
+ 0x8AEE,0x8CC7,0x8CDC,0x96CC,0x98FC,0x6B6F,0x4E8B,0x4F3C,/* 0x90-0x97 */
+ 0x4F8D,0x5150,0x5B57,0x5BFA,0x6148,0x6301,0x6642,0x6B21,/* 0x98-0x9F */
+ 0x6ECB,0x6CBB,0x723E,0x74BD,0x75D4,0x78C1,0x793A,0x800C,/* 0xA0-0xA7 */
+ 0x8033,0x81EA,0x8494,0x8F9E,0x6C50,0x9E7F,0x5F0F,0x8B58,/* 0xA8-0xAF */
+ 0x9D2B,0x7AFA,0x8EF8,0x5B8D,0x96EB,0x4E03,0x53F1,0x57F7,/* 0xB0-0xB7 */
+ 0x5931,0x5AC9,0x5BA4,0x6089,0x6E7F,0x6F06,0x75BE,0x8CEA,/* 0xB8-0xBF */
+ 0x5B9F,0x8500,0x7BE0,0x5072,0x67F4,0x829D,0x5C61,0x854A,/* 0xC0-0xC7 */
+ 0x7E1E,0x820E,0x5199,0x5C04,0x6368,0x8D66,0x659C,0x716E,/* 0xC8-0xCF */
+ 0x793E,0x7D17,0x8005,0x8B1D,0x8ECA,0x906E,0x86C7,0x90AA,/* 0xD0-0xD7 */
+ 0x501F,0x52FA,0x5C3A,0x6753,0x707C,0x7235,0x914C,0x91C8,/* 0xD8-0xDF */
+ 0x932B,0x82E5,0x5BC2,0x5F31,0x60F9,0x4E3B,0x53D6,0x5B88,/* 0xE0-0xE7 */
+ 0x624B,0x6731,0x6B8A,0x72E9,0x73E0,0x7A2E,0x816B,0x8DA3,/* 0xE8-0xEF */
+ 0x9152,0x9996,0x5112,0x53D7,0x546A,0x5BFF,0x6388,0x6A39,/* 0xF0-0xF7 */
+ 0x7DAC,0x9700,0x56DA,0x53CE,0x5468,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_8F[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x5B97,0x5C31,0x5DDE,0x4FEE,0x6101,0x62FE,0x6D32,0x79C0,/* 0x40-0x47 */
+ 0x79CB,0x7D42,0x7E4D,0x7FD2,0x81ED,0x821F,0x8490,0x8846,/* 0x48-0x4F */
+ 0x8972,0x8B90,0x8E74,0x8F2F,0x9031,0x914B,0x916C,0x96C6,/* 0x50-0x57 */
+ 0x919C,0x4EC0,0x4F4F,0x5145,0x5341,0x5F93,0x620E,0x67D4,/* 0x58-0x5F */
+ 0x6C41,0x6E0B,0x7363,0x7E26,0x91CD,0x9283,0x53D4,0x5919,/* 0x60-0x67 */
+ 0x5BBF,0x6DD1,0x795D,0x7E2E,0x7C9B,0x587E,0x719F,0x51FA,/* 0x68-0x6F */
+ 0x8853,0x8FF0,0x4FCA,0x5CFB,0x6625,0x77AC,0x7AE3,0x821C,/* 0x70-0x77 */
+ 0x99FF,0x51C6,0x5FAA,0x65EC,0x696F,0x6B89,0x6DF3,0x0000,/* 0x78-0x7F */
+
+ 0x6E96,0x6F64,0x76FE,0x7D14,0x5DE1,0x9075,0x9187,0x9806,/* 0x80-0x87 */
+ 0x51E6,0x521D,0x6240,0x6691,0x66D9,0x6E1A,0x5EB6,0x7DD2,/* 0x88-0x8F */
+ 0x7F72,0x66F8,0x85AF,0x85F7,0x8AF8,0x52A9,0x53D9,0x5973,/* 0x90-0x97 */
+ 0x5E8F,0x5F90,0x6055,0x92E4,0x9664,0x50B7,0x511F,0x52DD,/* 0x98-0x9F */
+ 0x5320,0x5347,0x53EC,0x54E8,0x5546,0x5531,0x5617,0x5968,/* 0xA0-0xA7 */
+ 0x59BE,0x5A3C,0x5BB5,0x5C06,0x5C0F,0x5C11,0x5C1A,0x5E84,/* 0xA8-0xAF */
+ 0x5E8A,0x5EE0,0x5F70,0x627F,0x6284,0x62DB,0x638C,0x6377,/* 0xB0-0xB7 */
+ 0x6607,0x660C,0x662D,0x6676,0x677E,0x68A2,0x6A1F,0x6A35,/* 0xB8-0xBF */
+ 0x6CBC,0x6D88,0x6E09,0x6E58,0x713C,0x7126,0x7167,0x75C7,/* 0xC0-0xC7 */
+ 0x7701,0x785D,0x7901,0x7965,0x79F0,0x7AE0,0x7B11,0x7CA7,/* 0xC8-0xCF */
+ 0x7D39,0x8096,0x83D6,0x848B,0x8549,0x885D,0x88F3,0x8A1F,/* 0xD0-0xD7 */
+ 0x8A3C,0x8A54,0x8A73,0x8C61,0x8CDE,0x91A4,0x9266,0x937E,/* 0xD8-0xDF */
+ 0x9418,0x969C,0x9798,0x4E0A,0x4E08,0x4E1E,0x4E57,0x5197,/* 0xE0-0xE7 */
+ 0x5270,0x57CE,0x5834,0x58CC,0x5B22,0x5E38,0x60C5,0x64FE,/* 0xE8-0xEF */
+ 0x6761,0x6756,0x6D44,0x72B6,0x7573,0x7A63,0x84B8,0x8B72,/* 0xF0-0xF7 */
+ 0x91B8,0x9320,0x5631,0x57F4,0x98FE,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_90[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x62ED,0x690D,0x6B96,0x71ED,0x7E54,0x8077,0x8272,0x89E6,/* 0x40-0x47 */
+ 0x98DF,0x8755,0x8FB1,0x5C3B,0x4F38,0x4FE1,0x4FB5,0x5507,/* 0x48-0x4F */
+ 0x5A20,0x5BDD,0x5BE9,0x5FC3,0x614E,0x632F,0x65B0,0x664B,/* 0x50-0x57 */
+ 0x68EE,0x699B,0x6D78,0x6DF1,0x7533,0x75B9,0x771F,0x795E,/* 0x58-0x5F */
+ 0x79E6,0x7D33,0x81E3,0x82AF,0x85AA,0x89AA,0x8A3A,0x8EAB,/* 0x60-0x67 */
+ 0x8F9B,0x9032,0x91DD,0x9707,0x4EBA,0x4EC1,0x5203,0x5875,/* 0x68-0x6F */
+ 0x58EC,0x5C0B,0x751A,0x5C3D,0x814E,0x8A0A,0x8FC5,0x9663,/* 0x70-0x77 */
+ 0x976D,0x7B25,0x8ACF,0x9808,0x9162,0x56F3,0x53A8,0x0000,/* 0x78-0x7F */
+
+ 0x9017,0x5439,0x5782,0x5E25,0x63A8,0x6C34,0x708A,0x7761,/* 0x80-0x87 */
+ 0x7C8B,0x7FE0,0x8870,0x9042,0x9154,0x9310,0x9318,0x968F,/* 0x88-0x8F */
+ 0x745E,0x9AC4,0x5D07,0x5D69,0x6570,0x67A2,0x8DA8,0x96DB,/* 0x90-0x97 */
+ 0x636E,0x6749,0x6919,0x83C5,0x9817,0x96C0,0x88FE,0x6F84,/* 0x98-0x9F */
+ 0x647A,0x5BF8,0x4E16,0x702C,0x755D,0x662F,0x51C4,0x5236,/* 0xA0-0xA7 */
+ 0x52E2,0x59D3,0x5F81,0x6027,0x6210,0x653F,0x6574,0x661F,/* 0xA8-0xAF */
+ 0x6674,0x68F2,0x6816,0x6B63,0x6E05,0x7272,0x751F,0x76DB,/* 0xB0-0xB7 */
+ 0x7CBE,0x8056,0x58F0,0x88FD,0x897F,0x8AA0,0x8A93,0x8ACB,/* 0xB8-0xBF */
+ 0x901D,0x9192,0x9752,0x9759,0x6589,0x7A0E,0x8106,0x96BB,/* 0xC0-0xC7 */
+ 0x5E2D,0x60DC,0x621A,0x65A5,0x6614,0x6790,0x77F3,0x7A4D,/* 0xC8-0xCF */
+ 0x7C4D,0x7E3E,0x810A,0x8CAC,0x8D64,0x8DE1,0x8E5F,0x78A9,/* 0xD0-0xD7 */
+ 0x5207,0x62D9,0x63A5,0x6442,0x6298,0x8A2D,0x7A83,0x7BC0,/* 0xD8-0xDF */
+ 0x8AAC,0x96EA,0x7D76,0x820C,0x8749,0x4ED9,0x5148,0x5343,/* 0xE0-0xE7 */
+ 0x5360,0x5BA3,0x5C02,0x5C16,0x5DDD,0x6226,0x6247,0x64B0,/* 0xE8-0xEF */
+ 0x6813,0x6834,0x6CC9,0x6D45,0x6D17,0x67D3,0x6F5C,0x714E,/* 0xF0-0xF7 */
+ 0x717D,0x65CB,0x7A7F,0x7BAD,0x7DDA,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_91[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x7E4A,0x7FA8,0x817A,0x821B,0x8239,0x85A6,0x8A6E,0x8CCE,/* 0x40-0x47 */
+ 0x8DF5,0x9078,0x9077,0x92AD,0x9291,0x9583,0x9BAE,0x524D,/* 0x48-0x4F */
+ 0x5584,0x6F38,0x7136,0x5168,0x7985,0x7E55,0x81B3,0x7CCE,/* 0x50-0x57 */
+ 0x564C,0x5851,0x5CA8,0x63AA,0x66FE,0x66FD,0x695A,0x72D9,/* 0x58-0x5F */
+ 0x758F,0x758E,0x790E,0x7956,0x79DF,0x7C97,0x7D20,0x7D44,/* 0x60-0x67 */
+ 0x8607,0x8A34,0x963B,0x9061,0x9F20,0x50E7,0x5275,0x53CC,/* 0x68-0x6F */
+ 0x53E2,0x5009,0x55AA,0x58EE,0x594F,0x723D,0x5B8B,0x5C64,/* 0x70-0x77 */
+ 0x531D,0x60E3,0x60F3,0x635C,0x6383,0x633F,0x63BB,0x0000,/* 0x78-0x7F */
+
+ 0x64CD,0x65E9,0x66F9,0x5DE3,0x69CD,0x69FD,0x6F15,0x71E5,/* 0x80-0x87 */
+ 0x4E89,0x75E9,0x76F8,0x7A93,0x7CDF,0x7DCF,0x7D9C,0x8061,/* 0x88-0x8F */
+ 0x8349,0x8358,0x846C,0x84BC,0x85FB,0x88C5,0x8D70,0x9001,/* 0x90-0x97 */
+ 0x906D,0x9397,0x971C,0x9A12,0x50CF,0x5897,0x618E,0x81D3,/* 0x98-0x9F */
+ 0x8535,0x8D08,0x9020,0x4FC3,0x5074,0x5247,0x5373,0x606F,/* 0xA0-0xA7 */
+ 0x6349,0x675F,0x6E2C,0x8DB3,0x901F,0x4FD7,0x5C5E,0x8CCA,/* 0xA8-0xAF */
+ 0x65CF,0x7D9A,0x5352,0x8896,0x5176,0x63C3,0x5B58,0x5B6B,/* 0xB0-0xB7 */
+ 0x5C0A,0x640D,0x6751,0x905C,0x4ED6,0x591A,0x592A,0x6C70,/* 0xB8-0xBF */
+ 0x8A51,0x553E,0x5815,0x59A5,0x60F0,0x6253,0x67C1,0x8235,/* 0xC0-0xC7 */
+ 0x6955,0x9640,0x99C4,0x9A28,0x4F53,0x5806,0x5BFE,0x8010,/* 0xC8-0xCF */
+ 0x5CB1,0x5E2F,0x5F85,0x6020,0x614B,0x6234,0x66FF,0x6CF0,/* 0xD0-0xD7 */
+ 0x6EDE,0x80CE,0x817F,0x82D4,0x888B,0x8CB8,0x9000,0x902E,/* 0xD8-0xDF */
+ 0x968A,0x9EDB,0x9BDB,0x4EE3,0x53F0,0x5927,0x7B2C,0x918D,/* 0xE0-0xE7 */
+ 0x984C,0x9DF9,0x6EDD,0x7027,0x5353,0x5544,0x5B85,0x6258,/* 0xE8-0xEF */
+ 0x629E,0x62D3,0x6CA2,0x6FEF,0x7422,0x8A17,0x9438,0x6FC1,/* 0xF0-0xF7 */
+ 0x8AFE,0x8338,0x51E7,0x86F8,0x53EA,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_92[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x53E9,0x4F46,0x9054,0x8FB0,0x596A,0x8131,0x5DFD,0x7AEA,/* 0x40-0x47 */
+ 0x8FBF,0x68DA,0x8C37,0x72F8,0x9C48,0x6A3D,0x8AB0,0x4E39,/* 0x48-0x4F */
+ 0x5358,0x5606,0x5766,0x62C5,0x63A2,0x65E6,0x6B4E,0x6DE1,/* 0x50-0x57 */
+ 0x6E5B,0x70AD,0x77ED,0x7AEF,0x7BAA,0x7DBB,0x803D,0x80C6,/* 0x58-0x5F */
+ 0x86CB,0x8A95,0x935B,0x56E3,0x58C7,0x5F3E,0x65AD,0x6696,/* 0x60-0x67 */
+ 0x6A80,0x6BB5,0x7537,0x8AC7,0x5024,0x77E5,0x5730,0x5F1B,/* 0x68-0x6F */
+ 0x6065,0x667A,0x6C60,0x75F4,0x7A1A,0x7F6E,0x81F4,0x8718,/* 0x70-0x77 */
+ 0x9045,0x99B3,0x7BC9,0x755C,0x7AF9,0x7B51,0x84C4,0x0000,/* 0x78-0x7F */
+
+ 0x9010,0x79E9,0x7A92,0x8336,0x5AE1,0x7740,0x4E2D,0x4EF2,/* 0x80-0x87 */
+ 0x5B99,0x5FE0,0x62BD,0x663C,0x67F1,0x6CE8,0x866B,0x8877,/* 0x88-0x8F */
+ 0x8A3B,0x914E,0x92F3,0x99D0,0x6A17,0x7026,0x732A,0x82E7,/* 0x90-0x97 */
+ 0x8457,0x8CAF,0x4E01,0x5146,0x51CB,0x558B,0x5BF5,0x5E16,/* 0x98-0x9F */
+ 0x5E33,0x5E81,0x5F14,0x5F35,0x5F6B,0x5FB4,0x61F2,0x6311,/* 0xA0-0xA7 */
+ 0x66A2,0x671D,0x6F6E,0x7252,0x753A,0x773A,0x8074,0x8139,/* 0xA8-0xAF */
+ 0x8178,0x8776,0x8ABF,0x8ADC,0x8D85,0x8DF3,0x929A,0x9577,/* 0xB0-0xB7 */
+ 0x9802,0x9CE5,0x52C5,0x6357,0x76F4,0x6715,0x6C88,0x73CD,/* 0xB8-0xBF */
+ 0x8CC3,0x93AE,0x9673,0x6D25,0x589C,0x690E,0x69CC,0x8FFD,/* 0xC0-0xC7 */
+ 0x939A,0x75DB,0x901A,0x585A,0x6802,0x63B4,0x69FB,0x4F43,/* 0xC8-0xCF */
+ 0x6F2C,0x67D8,0x8FBB,0x8526,0x7DB4,0x9354,0x693F,0x6F70,/* 0xD0-0xD7 */
+ 0x576A,0x58F7,0x5B2C,0x7D2C,0x722A,0x540A,0x91E3,0x9DB4,/* 0xD8-0xDF */
+ 0x4EAD,0x4F4E,0x505C,0x5075,0x5243,0x8C9E,0x5448,0x5824,/* 0xE0-0xE7 */
+ 0x5B9A,0x5E1D,0x5E95,0x5EAD,0x5EF7,0x5F1F,0x608C,0x62B5,/* 0xE8-0xEF */
+ 0x633A,0x63D0,0x68AF,0x6C40,0x7887,0x798E,0x7A0B,0x7DE0,/* 0xF0-0xF7 */
+ 0x8247,0x8A02,0x8AE6,0x8E44,0x9013,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_93[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x90B8,0x912D,0x91D8,0x9F0E,0x6CE5,0x6458,0x64E2,0x6575,/* 0x40-0x47 */
+ 0x6EF4,0x7684,0x7B1B,0x9069,0x93D1,0x6EBA,0x54F2,0x5FB9,/* 0x48-0x4F */
+ 0x64A4,0x8F4D,0x8FED,0x9244,0x5178,0x586B,0x5929,0x5C55,/* 0x50-0x57 */
+ 0x5E97,0x6DFB,0x7E8F,0x751C,0x8CBC,0x8EE2,0x985B,0x70B9,/* 0x58-0x5F */
+ 0x4F1D,0x6BBF,0x6FB1,0x7530,0x96FB,0x514E,0x5410,0x5835,/* 0x60-0x67 */
+ 0x5857,0x59AC,0x5C60,0x5F92,0x6597,0x675C,0x6E21,0x767B,/* 0x68-0x6F */
+ 0x83DF,0x8CED,0x9014,0x90FD,0x934D,0x7825,0x783A,0x52AA,/* 0x70-0x77 */
+ 0x5EA6,0x571F,0x5974,0x6012,0x5012,0x515A,0x51AC,0x0000,/* 0x78-0x7F */
+
+ 0x51CD,0x5200,0x5510,0x5854,0x5858,0x5957,0x5B95,0x5CF6,/* 0x80-0x87 */
+ 0x5D8B,0x60BC,0x6295,0x642D,0x6771,0x6843,0x68BC,0x68DF,/* 0x88-0x8F */
+ 0x76D7,0x6DD8,0x6E6F,0x6D9B,0x706F,0x71C8,0x5F53,0x75D8,/* 0x90-0x97 */
+ 0x7977,0x7B49,0x7B54,0x7B52,0x7CD6,0x7D71,0x5230,0x8463,/* 0x98-0x9F */
+ 0x8569,0x85E4,0x8A0E,0x8B04,0x8C46,0x8E0F,0x9003,0x900F,/* 0xA0-0xA7 */
+ 0x9419,0x9676,0x982D,0x9A30,0x95D8,0x50CD,0x52D5,0x540C,/* 0xA8-0xAF */
+ 0x5802,0x5C0E,0x61A7,0x649E,0x6D1E,0x77B3,0x7AE5,0x80F4,/* 0xB0-0xB7 */
+ 0x8404,0x9053,0x9285,0x5CE0,0x9D07,0x533F,0x5F97,0x5FB3,/* 0xB8-0xBF */
+ 0x6D9C,0x7279,0x7763,0x79BF,0x7BE4,0x6BD2,0x72EC,0x8AAD,/* 0xC0-0xC7 */
+ 0x6803,0x6A61,0x51F8,0x7A81,0x6934,0x5C4A,0x9CF6,0x82EB,/* 0xC8-0xCF */
+ 0x5BC5,0x9149,0x701E,0x5678,0x5C6F,0x60C7,0x6566,0x6C8C,/* 0xD0-0xD7 */
+ 0x8C5A,0x9041,0x9813,0x5451,0x66C7,0x920D,0x5948,0x90A3,/* 0xD8-0xDF */
+ 0x5185,0x4E4D,0x51EA,0x8599,0x8B0E,0x7058,0x637A,0x934B,/* 0xE0-0xE7 */
+ 0x6962,0x99B4,0x7E04,0x7577,0x5357,0x6960,0x8EDF,0x96E3,/* 0xE8-0xEF */
+ 0x6C5D,0x4E8C,0x5C3C,0x5F10,0x8FE9,0x5302,0x8CD1,0x8089,/* 0xF0-0xF7 */
+ 0x8679,0x5EFF,0x65E5,0x4E73,0x5165,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_94[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x5982,0x5C3F,0x97EE,0x4EFB,0x598A,0x5FCD,0x8A8D,0x6FE1,/* 0x40-0x47 */
+ 0x79B0,0x7962,0x5BE7,0x8471,0x732B,0x71B1,0x5E74,0x5FF5,/* 0x48-0x4F */
+ 0x637B,0x649A,0x71C3,0x7C98,0x4E43,0x5EFC,0x4E4B,0x57DC,/* 0x50-0x57 */
+ 0x56A2,0x60A9,0x6FC3,0x7D0D,0x80FD,0x8133,0x81BF,0x8FB2,/* 0x58-0x5F */
+ 0x8997,0x86A4,0x5DF4,0x628A,0x64AD,0x8987,0x6777,0x6CE2,/* 0x60-0x67 */
+ 0x6D3E,0x7436,0x7834,0x5A46,0x7F75,0x82AD,0x99AC,0x4FF3,/* 0x68-0x6F */
+ 0x5EC3,0x62DD,0x6392,0x6557,0x676F,0x76C3,0x724C,0x80CC,/* 0x70-0x77 */
+ 0x80BA,0x8F29,0x914D,0x500D,0x57F9,0x5A92,0x6885,0x0000,/* 0x78-0x7F */
+
+ 0x6973,0x7164,0x72FD,0x8CB7,0x58F2,0x8CE0,0x966A,0x9019,/* 0x80-0x87 */
+ 0x877F,0x79E4,0x77E7,0x8429,0x4F2F,0x5265,0x535A,0x62CD,/* 0x88-0x8F */
+ 0x67CF,0x6CCA,0x767D,0x7B94,0x7C95,0x8236,0x8584,0x8FEB,/* 0x90-0x97 */
+ 0x66DD,0x6F20,0x7206,0x7E1B,0x83AB,0x99C1,0x9EA6,0x51FD,/* 0x98-0x9F */
+ 0x7BB1,0x7872,0x7BB8,0x8087,0x7B48,0x6AE8,0x5E61,0x808C,/* 0xA0-0xA7 */
+ 0x7551,0x7560,0x516B,0x9262,0x6E8C,0x767A,0x9197,0x9AEA,/* 0xA8-0xAF */
+ 0x4F10,0x7F70,0x629C,0x7B4F,0x95A5,0x9CE9,0x567A,0x5859,/* 0xB0-0xB7 */
+ 0x86E4,0x96BC,0x4F34,0x5224,0x534A,0x53CD,0x53DB,0x5E06,/* 0xB8-0xBF */
+ 0x642C,0x6591,0x677F,0x6C3E,0x6C4E,0x7248,0x72AF,0x73ED,/* 0xC0-0xC7 */
+ 0x7554,0x7E41,0x822C,0x85E9,0x8CA9,0x7BC4,0x91C6,0x7169,/* 0xC8-0xCF */
+ 0x9812,0x98EF,0x633D,0x6669,0x756A,0x76E4,0x78D0,0x8543,/* 0xD0-0xD7 */
+ 0x86EE,0x532A,0x5351,0x5426,0x5983,0x5E87,0x5F7C,0x60B2,/* 0xD8-0xDF */
+ 0x6249,0x6279,0x62AB,0x6590,0x6BD4,0x6CCC,0x75B2,0x76AE,/* 0xE0-0xE7 */
+ 0x7891,0x79D8,0x7DCB,0x7F77,0x80A5,0x88AB,0x8AB9,0x8CBB,/* 0xE8-0xEF */
+ 0x907F,0x975E,0x98DB,0x6A0B,0x7C38,0x5099,0x5C3E,0x5FAE,/* 0xF0-0xF7 */
+ 0x6787,0x6BD8,0x7435,0x7709,0x7F8E,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_95[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x9F3B,0x67CA,0x7A17,0x5339,0x758B,0x9AED,0x5F66,0x819D,/* 0x40-0x47 */
+ 0x83F1,0x8098,0x5F3C,0x5FC5,0x7562,0x7B46,0x903C,0x6867,/* 0x48-0x4F */
+ 0x59EB,0x5A9B,0x7D10,0x767E,0x8B2C,0x4FF5,0x5F6A,0x6A19,/* 0x50-0x57 */
+ 0x6C37,0x6F02,0x74E2,0x7968,0x8868,0x8A55,0x8C79,0x5EDF,/* 0x58-0x5F */
+ 0x63CF,0x75C5,0x79D2,0x82D7,0x9328,0x92F2,0x849C,0x86ED,/* 0x60-0x67 */
+ 0x9C2D,0x54C1,0x5F6C,0x658C,0x6D5C,0x7015,0x8CA7,0x8CD3,/* 0x68-0x6F */
+ 0x983B,0x654F,0x74F6,0x4E0D,0x4ED8,0x57E0,0x592B,0x5A66,/* 0x70-0x77 */
+ 0x5BCC,0x51A8,0x5E03,0x5E9C,0x6016,0x6276,0x6577,0x0000,/* 0x78-0x7F */
+
+ 0x65A7,0x666E,0x6D6E,0x7236,0x7B26,0x8150,0x819A,0x8299,/* 0x80-0x87 */
+ 0x8B5C,0x8CA0,0x8CE6,0x8D74,0x961C,0x9644,0x4FAE,0x64AB,/* 0x88-0x8F */
+ 0x6B66,0x821E,0x8461,0x856A,0x90E8,0x5C01,0x6953,0x98A8,/* 0x90-0x97 */
+ 0x847A,0x8557,0x4F0F,0x526F,0x5FA9,0x5E45,0x670D,0x798F,/* 0x98-0x9F */
+ 0x8179,0x8907,0x8986,0x6DF5,0x5F17,0x6255,0x6CB8,0x4ECF,/* 0xA0-0xA7 */
+ 0x7269,0x9B92,0x5206,0x543B,0x5674,0x58B3,0x61A4,0x626E,/* 0xA8-0xAF */
+ 0x711A,0x596E,0x7C89,0x7CDE,0x7D1B,0x96F0,0x6587,0x805E,/* 0xB0-0xB7 */
+ 0x4E19,0x4F75,0x5175,0x5840,0x5E63,0x5E73,0x5F0A,0x67C4,/* 0xB8-0xBF */
+ 0x4E26,0x853D,0x9589,0x965B,0x7C73,0x9801,0x50FB,0x58C1,/* 0xC0-0xC7 */
+ 0x7656,0x78A7,0x5225,0x77A5,0x8511,0x7B86,0x504F,0x5909,/* 0xC8-0xCF */
+ 0x7247,0x7BC7,0x7DE8,0x8FBA,0x8FD4,0x904D,0x4FBF,0x52C9,/* 0xD0-0xD7 */
+ 0x5A29,0x5F01,0x97AD,0x4FDD,0x8217,0x92EA,0x5703,0x6355,/* 0xD8-0xDF */
+ 0x6B69,0x752B,0x88DC,0x8F14,0x7A42,0x52DF,0x5893,0x6155,/* 0xE0-0xE7 */
+ 0x620A,0x66AE,0x6BCD,0x7C3F,0x83E9,0x5023,0x4FF8,0x5305,/* 0xE8-0xEF */
+ 0x5446,0x5831,0x5949,0x5B9D,0x5CF0,0x5CEF,0x5D29,0x5E96,/* 0xF0-0xF7 */
+ 0x62B1,0x6367,0x653E,0x65B9,0x670B,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_96[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x6CD5,0x6CE1,0x70F9,0x7832,0x7E2B,0x80DE,0x82B3,0x840C,/* 0x40-0x47 */
+ 0x84EC,0x8702,0x8912,0x8A2A,0x8C4A,0x90A6,0x92D2,0x98FD,/* 0x48-0x4F */
+ 0x9CF3,0x9D6C,0x4E4F,0x4EA1,0x508D,0x5256,0x574A,0x59A8,/* 0x50-0x57 */
+ 0x5E3D,0x5FD8,0x5FD9,0x623F,0x66B4,0x671B,0x67D0,0x68D2,/* 0x58-0x5F */
+ 0x5192,0x7D21,0x80AA,0x81A8,0x8B00,0x8C8C,0x8CBF,0x927E,/* 0x60-0x67 */
+ 0x9632,0x5420,0x982C,0x5317,0x50D5,0x535C,0x58A8,0x64B2,/* 0x68-0x6F */
+ 0x6734,0x7267,0x7766,0x7A46,0x91E6,0x52C3,0x6CA1,0x6B86,/* 0x70-0x77 */
+ 0x5800,0x5E4C,0x5954,0x672C,0x7FFB,0x51E1,0x76C6,0x0000,/* 0x78-0x7F */
+
+ 0x6469,0x78E8,0x9B54,0x9EBB,0x57CB,0x59B9,0x6627,0x679A,/* 0x80-0x87 */
+ 0x6BCE,0x54E9,0x69D9,0x5E55,0x819C,0x6795,0x9BAA,0x67FE,/* 0x88-0x8F */
+ 0x9C52,0x685D,0x4EA6,0x4FE3,0x53C8,0x62B9,0x672B,0x6CAB,/* 0x90-0x97 */
+ 0x8FC4,0x4FAD,0x7E6D,0x9EBF,0x4E07,0x6162,0x6E80,0x6F2B,/* 0x98-0x9F */
+ 0x8513,0x5473,0x672A,0x9B45,0x5DF3,0x7B95,0x5CAC,0x5BC6,/* 0xA0-0xA7 */
+ 0x871C,0x6E4A,0x84D1,0x7A14,0x8108,0x5999,0x7C8D,0x6C11,/* 0xA8-0xAF */
+ 0x7720,0x52D9,0x5922,0x7121,0x725F,0x77DB,0x9727,0x9D61,/* 0xB0-0xB7 */
+ 0x690B,0x5A7F,0x5A18,0x51A5,0x540D,0x547D,0x660E,0x76DF,/* 0xB8-0xBF */
+ 0x8FF7,0x9298,0x9CF4,0x59EA,0x725D,0x6EC5,0x514D,0x68C9,/* 0xC0-0xC7 */
+ 0x7DBF,0x7DEC,0x9762,0x9EBA,0x6478,0x6A21,0x8302,0x5984,/* 0xC8-0xCF */
+ 0x5B5F,0x6BDB,0x731B,0x76F2,0x7DB2,0x8017,0x8499,0x5132,/* 0xD0-0xD7 */
+ 0x6728,0x9ED9,0x76EE,0x6762,0x52FF,0x9905,0x5C24,0x623B,/* 0xD8-0xDF */
+ 0x7C7E,0x8CB0,0x554F,0x60B6,0x7D0B,0x9580,0x5301,0x4E5F,/* 0xE0-0xE7 */
+ 0x51B6,0x591C,0x723A,0x8036,0x91CE,0x5F25,0x77E2,0x5384,/* 0xE8-0xEF */
+ 0x5F79,0x7D04,0x85AC,0x8A33,0x8E8D,0x9756,0x67F3,0x85AE,/* 0xF0-0xF7 */
+ 0x9453,0x6109,0x6108,0x6CB9,0x7652,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_97[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x8AED,0x8F38,0x552F,0x4F51,0x512A,0x52C7,0x53CB,0x5BA5,/* 0x40-0x47 */
+ 0x5E7D,0x60A0,0x6182,0x63D6,0x6709,0x67DA,0x6E67,0x6D8C,/* 0x48-0x4F */
+ 0x7336,0x7337,0x7531,0x7950,0x88D5,0x8A98,0x904A,0x9091,/* 0x50-0x57 */
+ 0x90F5,0x96C4,0x878D,0x5915,0x4E88,0x4F59,0x4E0E,0x8A89,/* 0x58-0x5F */
+ 0x8F3F,0x9810,0x50AD,0x5E7C,0x5996,0x5BB9,0x5EB8,0x63DA,/* 0x60-0x67 */
+ 0x63FA,0x64C1,0x66DC,0x694A,0x69D8,0x6D0B,0x6EB6,0x7194,/* 0x68-0x6F */
+ 0x7528,0x7AAF,0x7F8A,0x8000,0x8449,0x84C9,0x8981,0x8B21,/* 0x70-0x77 */
+ 0x8E0A,0x9065,0x967D,0x990A,0x617E,0x6291,0x6B32,0x0000,/* 0x78-0x7F */
+
+ 0x6C83,0x6D74,0x7FCC,0x7FFC,0x6DC0,0x7F85,0x87BA,0x88F8,/* 0x80-0x87 */
+ 0x6765,0x83B1,0x983C,0x96F7,0x6D1B,0x7D61,0x843D,0x916A,/* 0x88-0x8F */
+ 0x4E71,0x5375,0x5D50,0x6B04,0x6FEB,0x85CD,0x862D,0x89A7,/* 0x90-0x97 */
+ 0x5229,0x540F,0x5C65,0x674E,0x68A8,0x7406,0x7483,0x75E2,/* 0x98-0x9F */
+ 0x88CF,0x88E1,0x91CC,0x96E2,0x9678,0x5F8B,0x7387,0x7ACB,/* 0xA0-0xA7 */
+ 0x844E,0x63A0,0x7565,0x5289,0x6D41,0x6E9C,0x7409,0x7559,/* 0xA8-0xAF */
+ 0x786B,0x7C92,0x9686,0x7ADC,0x9F8D,0x4FB6,0x616E,0x65C5,/* 0xB0-0xB7 */
+ 0x865C,0x4E86,0x4EAE,0x50DA,0x4E21,0x51CC,0x5BEE,0x6599,/* 0xB8-0xBF */
+ 0x6881,0x6DBC,0x731F,0x7642,0x77AD,0x7A1C,0x7CE7,0x826F,/* 0xC0-0xC7 */
+ 0x8AD2,0x907C,0x91CF,0x9675,0x9818,0x529B,0x7DD1,0x502B,/* 0xC8-0xCF */
+ 0x5398,0x6797,0x6DCB,0x71D0,0x7433,0x81E8,0x8F2A,0x96A3,/* 0xD0-0xD7 */
+ 0x9C57,0x9E9F,0x7460,0x5841,0x6D99,0x7D2F,0x985E,0x4EE4,/* 0xD8-0xDF */
+ 0x4F36,0x4F8B,0x51B7,0x52B1,0x5DBA,0x601C,0x73B2,0x793C,/* 0xE0-0xE7 */
+ 0x82D3,0x9234,0x96B7,0x96F6,0x970A,0x9E97,0x9F62,0x66A6,/* 0xE8-0xEF */
+ 0x6B74,0x5217,0x52A3,0x70C8,0x88C2,0x5EC9,0x604B,0x6190,/* 0xF0-0xF7 */
+ 0x6F23,0x7149,0x7C3E,0x7DF4,0x806F,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_98[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x84EE,0x9023,0x932C,0x5442,0x9B6F,0x6AD3,0x7089,0x8CC2,/* 0x40-0x47 */
+ 0x8DEF,0x9732,0x52B4,0x5A41,0x5ECA,0x5F04,0x6717,0x697C,/* 0x48-0x4F */
+ 0x6994,0x6D6A,0x6F0F,0x7262,0x72FC,0x7BED,0x8001,0x807E,/* 0x50-0x57 */
+ 0x874B,0x90CE,0x516D,0x9E93,0x7984,0x808B,0x9332,0x8AD6,/* 0x58-0x5F */
+ 0x502D,0x548C,0x8A71,0x6B6A,0x8CC4,0x8107,0x60D1,0x67A0,/* 0x60-0x67 */
+ 0x9DF2,0x4E99,0x4E98,0x9C10,0x8A6B,0x85C1,0x8568,0x6900,/* 0x68-0x6F */
+ 0x6E7E,0x7897,0x8155,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x5F0C,/* 0x98-0x9F */
+ 0x4E10,0x4E15,0x4E2A,0x4E31,0x4E36,0x4E3C,0x4E3F,0x4E42,/* 0xA0-0xA7 */
+ 0x4E56,0x4E58,0x4E82,0x4E85,0x8C6B,0x4E8A,0x8212,0x5F0D,/* 0xA8-0xAF */
+ 0x4E8E,0x4E9E,0x4E9F,0x4EA0,0x4EA2,0x4EB0,0x4EB3,0x4EB6,/* 0xB0-0xB7 */
+ 0x4ECE,0x4ECD,0x4EC4,0x4EC6,0x4EC2,0x4ED7,0x4EDE,0x4EED,/* 0xB8-0xBF */
+ 0x4EDF,0x4EF7,0x4F09,0x4F5A,0x4F30,0x4F5B,0x4F5D,0x4F57,/* 0xC0-0xC7 */
+ 0x4F47,0x4F76,0x4F88,0x4F8F,0x4F98,0x4F7B,0x4F69,0x4F70,/* 0xC8-0xCF */
+ 0x4F91,0x4F6F,0x4F86,0x4F96,0x5118,0x4FD4,0x4FDF,0x4FCE,/* 0xD0-0xD7 */
+ 0x4FD8,0x4FDB,0x4FD1,0x4FDA,0x4FD0,0x4FE4,0x4FE5,0x501A,/* 0xD8-0xDF */
+ 0x5028,0x5014,0x502A,0x5025,0x5005,0x4F1C,0x4FF6,0x5021,/* 0xE0-0xE7 */
+ 0x5029,0x502C,0x4FFE,0x4FEF,0x5011,0x5006,0x5043,0x5047,/* 0xE8-0xEF */
+ 0x6703,0x5055,0x5050,0x5048,0x505A,0x5056,0x506C,0x5078,/* 0xF0-0xF7 */
+ 0x5080,0x509A,0x5085,0x50B4,0x50B2,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_99[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x50C9,0x50CA,0x50B3,0x50C2,0x50D6,0x50DE,0x50E5,0x50ED,/* 0x40-0x47 */
+ 0x50E3,0x50EE,0x50F9,0x50F5,0x5109,0x5101,0x5102,0x5116,/* 0x48-0x4F */
+ 0x5115,0x5114,0x511A,0x5121,0x513A,0x5137,0x513C,0x513B,/* 0x50-0x57 */
+ 0x513F,0x5140,0x5152,0x514C,0x5154,0x5162,0x7AF8,0x5169,/* 0x58-0x5F */
+ 0x516A,0x516E,0x5180,0x5182,0x56D8,0x518C,0x5189,0x518F,/* 0x60-0x67 */
+ 0x5191,0x5193,0x5195,0x5196,0x51A4,0x51A6,0x51A2,0x51A9,/* 0x68-0x6F */
+ 0x51AA,0x51AB,0x51B3,0x51B1,0x51B2,0x51B0,0x51B5,0x51BD,/* 0x70-0x77 */
+ 0x51C5,0x51C9,0x51DB,0x51E0,0x8655,0x51E9,0x51ED,0x0000,/* 0x78-0x7F */
+
+ 0x51F0,0x51F5,0x51FE,0x5204,0x520B,0x5214,0x520E,0x5227,/* 0x80-0x87 */
+ 0x522A,0x522E,0x5233,0x5239,0x524F,0x5244,0x524B,0x524C,/* 0x88-0x8F */
+ 0x525E,0x5254,0x526A,0x5274,0x5269,0x5273,0x527F,0x527D,/* 0x90-0x97 */
+ 0x528D,0x5294,0x5292,0x5271,0x5288,0x5291,0x8FA8,0x8FA7,/* 0x98-0x9F */
+ 0x52AC,0x52AD,0x52BC,0x52B5,0x52C1,0x52CD,0x52D7,0x52DE,/* 0xA0-0xA7 */
+ 0x52E3,0x52E6,0x98ED,0x52E0,0x52F3,0x52F5,0x52F8,0x52F9,/* 0xA8-0xAF */
+ 0x5306,0x5308,0x7538,0x530D,0x5310,0x530F,0x5315,0x531A,/* 0xB0-0xB7 */
+ 0x5323,0x532F,0x5331,0x5333,0x5338,0x5340,0x5346,0x5345,/* 0xB8-0xBF */
+ 0x4E17,0x5349,0x534D,0x51D6,0x535E,0x5369,0x536E,0x5918,/* 0xC0-0xC7 */
+ 0x537B,0x5377,0x5382,0x5396,0x53A0,0x53A6,0x53A5,0x53AE,/* 0xC8-0xCF */
+ 0x53B0,0x53B6,0x53C3,0x7C12,0x96D9,0x53DF,0x66FC,0x71EE,/* 0xD0-0xD7 */
+ 0x53EE,0x53E8,0x53ED,0x53FA,0x5401,0x543D,0x5440,0x542C,/* 0xD8-0xDF */
+ 0x542D,0x543C,0x542E,0x5436,0x5429,0x541D,0x544E,0x548F,/* 0xE0-0xE7 */
+ 0x5475,0x548E,0x545F,0x5471,0x5477,0x5470,0x5492,0x547B,/* 0xE8-0xEF */
+ 0x5480,0x5476,0x5484,0x5490,0x5486,0x54C7,0x54A2,0x54B8,/* 0xF0-0xF7 */
+ 0x54A5,0x54AC,0x54C4,0x54C8,0x54A8,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_9A[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x54AB,0x54C2,0x54A4,0x54BE,0x54BC,0x54D8,0x54E5,0x54E6,/* 0x40-0x47 */
+ 0x550F,0x5514,0x54FD,0x54EE,0x54ED,0x54FA,0x54E2,0x5539,/* 0x48-0x4F */
+ 0x5540,0x5563,0x554C,0x552E,0x555C,0x5545,0x5556,0x5557,/* 0x50-0x57 */
+ 0x5538,0x5533,0x555D,0x5599,0x5580,0x54AF,0x558A,0x559F,/* 0x58-0x5F */
+ 0x557B,0x557E,0x5598,0x559E,0x55AE,0x557C,0x5583,0x55A9,/* 0x60-0x67 */
+ 0x5587,0x55A8,0x55DA,0x55C5,0x55DF,0x55C4,0x55DC,0x55E4,/* 0x68-0x6F */
+ 0x55D4,0x5614,0x55F7,0x5616,0x55FE,0x55FD,0x561B,0x55F9,/* 0x70-0x77 */
+ 0x564E,0x5650,0x71DF,0x5634,0x5636,0x5632,0x5638,0x0000,/* 0x78-0x7F */
+
+ 0x566B,0x5664,0x562F,0x566C,0x566A,0x5686,0x5680,0x568A,/* 0x80-0x87 */
+ 0x56A0,0x5694,0x568F,0x56A5,0x56AE,0x56B6,0x56B4,0x56C2,/* 0x88-0x8F */
+ 0x56BC,0x56C1,0x56C3,0x56C0,0x56C8,0x56CE,0x56D1,0x56D3,/* 0x90-0x97 */
+ 0x56D7,0x56EE,0x56F9,0x5700,0x56FF,0x5704,0x5709,0x5708,/* 0x98-0x9F */
+ 0x570B,0x570D,0x5713,0x5718,0x5716,0x55C7,0x571C,0x5726,/* 0xA0-0xA7 */
+ 0x5737,0x5738,0x574E,0x573B,0x5740,0x574F,0x5769,0x57C0,/* 0xA8-0xAF */
+ 0x5788,0x5761,0x577F,0x5789,0x5793,0x57A0,0x57B3,0x57A4,/* 0xB0-0xB7 */
+ 0x57AA,0x57B0,0x57C3,0x57C6,0x57D4,0x57D2,0x57D3,0x580A,/* 0xB8-0xBF */
+ 0x57D6,0x57E3,0x580B,0x5819,0x581D,0x5872,0x5821,0x5862,/* 0xC0-0xC7 */
+ 0x584B,0x5870,0x6BC0,0x5852,0x583D,0x5879,0x5885,0x58B9,/* 0xC8-0xCF */
+ 0x589F,0x58AB,0x58BA,0x58DE,0x58BB,0x58B8,0x58AE,0x58C5,/* 0xD0-0xD7 */
+ 0x58D3,0x58D1,0x58D7,0x58D9,0x58D8,0x58E5,0x58DC,0x58E4,/* 0xD8-0xDF */
+ 0x58DF,0x58EF,0x58FA,0x58F9,0x58FB,0x58FC,0x58FD,0x5902,/* 0xE0-0xE7 */
+ 0x590A,0x5910,0x591B,0x68A6,0x5925,0x592C,0x592D,0x5932,/* 0xE8-0xEF */
+ 0x5938,0x593E,0x7AD2,0x5955,0x5950,0x594E,0x595A,0x5958,/* 0xF0-0xF7 */
+ 0x5962,0x5960,0x5967,0x596C,0x5969,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_9B[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x5978,0x5981,0x599D,0x4F5E,0x4FAB,0x59A3,0x59B2,0x59C6,/* 0x40-0x47 */
+ 0x59E8,0x59DC,0x598D,0x59D9,0x59DA,0x5A25,0x5A1F,0x5A11,/* 0x48-0x4F */
+ 0x5A1C,0x5A09,0x5A1A,0x5A40,0x5A6C,0x5A49,0x5A35,0x5A36,/* 0x50-0x57 */
+ 0x5A62,0x5A6A,0x5A9A,0x5ABC,0x5ABE,0x5ACB,0x5AC2,0x5ABD,/* 0x58-0x5F */
+ 0x5AE3,0x5AD7,0x5AE6,0x5AE9,0x5AD6,0x5AFA,0x5AFB,0x5B0C,/* 0x60-0x67 */
+ 0x5B0B,0x5B16,0x5B32,0x5AD0,0x5B2A,0x5B36,0x5B3E,0x5B43,/* 0x68-0x6F */
+ 0x5B45,0x5B40,0x5B51,0x5B55,0x5B5A,0x5B5B,0x5B65,0x5B69,/* 0x70-0x77 */
+ 0x5B70,0x5B73,0x5B75,0x5B78,0x6588,0x5B7A,0x5B80,0x0000,/* 0x78-0x7F */
+
+ 0x5B83,0x5BA6,0x5BB8,0x5BC3,0x5BC7,0x5BC9,0x5BD4,0x5BD0,/* 0x80-0x87 */
+ 0x5BE4,0x5BE6,0x5BE2,0x5BDE,0x5BE5,0x5BEB,0x5BF0,0x5BF6,/* 0x88-0x8F */
+ 0x5BF3,0x5C05,0x5C07,0x5C08,0x5C0D,0x5C13,0x5C20,0x5C22,/* 0x90-0x97 */
+ 0x5C28,0x5C38,0x5C39,0x5C41,0x5C46,0x5C4E,0x5C53,0x5C50,/* 0x98-0x9F */
+ 0x5C4F,0x5B71,0x5C6C,0x5C6E,0x4E62,0x5C76,0x5C79,0x5C8C,/* 0xA0-0xA7 */
+ 0x5C91,0x5C94,0x599B,0x5CAB,0x5CBB,0x5CB6,0x5CBC,0x5CB7,/* 0xA8-0xAF */
+ 0x5CC5,0x5CBE,0x5CC7,0x5CD9,0x5CE9,0x5CFD,0x5CFA,0x5CED,/* 0xB0-0xB7 */
+ 0x5D8C,0x5CEA,0x5D0B,0x5D15,0x5D17,0x5D5C,0x5D1F,0x5D1B,/* 0xB8-0xBF */
+ 0x5D11,0x5D14,0x5D22,0x5D1A,0x5D19,0x5D18,0x5D4C,0x5D52,/* 0xC0-0xC7 */
+ 0x5D4E,0x5D4B,0x5D6C,0x5D73,0x5D76,0x5D87,0x5D84,0x5D82,/* 0xC8-0xCF */
+ 0x5DA2,0x5D9D,0x5DAC,0x5DAE,0x5DBD,0x5D90,0x5DB7,0x5DBC,/* 0xD0-0xD7 */
+ 0x5DC9,0x5DCD,0x5DD3,0x5DD2,0x5DD6,0x5DDB,0x5DEB,0x5DF2,/* 0xD8-0xDF */
+ 0x5DF5,0x5E0B,0x5E1A,0x5E19,0x5E11,0x5E1B,0x5E36,0x5E37,/* 0xE0-0xE7 */
+ 0x5E44,0x5E43,0x5E40,0x5E4E,0x5E57,0x5E54,0x5E5F,0x5E62,/* 0xE8-0xEF */
+ 0x5E64,0x5E47,0x5E75,0x5E76,0x5E7A,0x9EBC,0x5E7F,0x5EA0,/* 0xF0-0xF7 */
+ 0x5EC1,0x5EC2,0x5EC8,0x5ED0,0x5ECF,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_9C[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x5ED6,0x5EE3,0x5EDD,0x5EDA,0x5EDB,0x5EE2,0x5EE1,0x5EE8,/* 0x40-0x47 */
+ 0x5EE9,0x5EEC,0x5EF1,0x5EF3,0x5EF0,0x5EF4,0x5EF8,0x5EFE,/* 0x48-0x4F */
+ 0x5F03,0x5F09,0x5F5D,0x5F5C,0x5F0B,0x5F11,0x5F16,0x5F29,/* 0x50-0x57 */
+ 0x5F2D,0x5F38,0x5F41,0x5F48,0x5F4C,0x5F4E,0x5F2F,0x5F51,/* 0x58-0x5F */
+ 0x5F56,0x5F57,0x5F59,0x5F61,0x5F6D,0x5F73,0x5F77,0x5F83,/* 0x60-0x67 */
+ 0x5F82,0x5F7F,0x5F8A,0x5F88,0x5F91,0x5F87,0x5F9E,0x5F99,/* 0x68-0x6F */
+ 0x5F98,0x5FA0,0x5FA8,0x5FAD,0x5FBC,0x5FD6,0x5FFB,0x5FE4,/* 0x70-0x77 */
+ 0x5FF8,0x5FF1,0x5FDD,0x60B3,0x5FFF,0x6021,0x6060,0x0000,/* 0x78-0x7F */
+
+ 0x6019,0x6010,0x6029,0x600E,0x6031,0x601B,0x6015,0x602B,/* 0x80-0x87 */
+ 0x6026,0x600F,0x603A,0x605A,0x6041,0x606A,0x6077,0x605F,/* 0x88-0x8F */
+ 0x604A,0x6046,0x604D,0x6063,0x6043,0x6064,0x6042,0x606C,/* 0x90-0x97 */
+ 0x606B,0x6059,0x6081,0x608D,0x60E7,0x6083,0x609A,0x6084,/* 0x98-0x9F */
+ 0x609B,0x6096,0x6097,0x6092,0x60A7,0x608B,0x60E1,0x60B8,/* 0xA0-0xA7 */
+ 0x60E0,0x60D3,0x60B4,0x5FF0,0x60BD,0x60C6,0x60B5,0x60D8,/* 0xA8-0xAF */
+ 0x614D,0x6115,0x6106,0x60F6,0x60F7,0x6100,0x60F4,0x60FA,/* 0xB0-0xB7 */
+ 0x6103,0x6121,0x60FB,0x60F1,0x610D,0x610E,0x6147,0x613E,/* 0xB8-0xBF */
+ 0x6128,0x6127,0x614A,0x613F,0x613C,0x612C,0x6134,0x613D,/* 0xC0-0xC7 */
+ 0x6142,0x6144,0x6173,0x6177,0x6158,0x6159,0x615A,0x616B,/* 0xC8-0xCF */
+ 0x6174,0x616F,0x6165,0x6171,0x615F,0x615D,0x6153,0x6175,/* 0xD0-0xD7 */
+ 0x6199,0x6196,0x6187,0x61AC,0x6194,0x619A,0x618A,0x6191,/* 0xD8-0xDF */
+ 0x61AB,0x61AE,0x61CC,0x61CA,0x61C9,0x61F7,0x61C8,0x61C3,/* 0xE0-0xE7 */
+ 0x61C6,0x61BA,0x61CB,0x7F79,0x61CD,0x61E6,0x61E3,0x61F6,/* 0xE8-0xEF */
+ 0x61FA,0x61F4,0x61FF,0x61FD,0x61FC,0x61FE,0x6200,0x6208,/* 0xF0-0xF7 */
+ 0x6209,0x620D,0x620C,0x6214,0x621B,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_9D[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x621E,0x6221,0x622A,0x622E,0x6230,0x6232,0x6233,0x6241,/* 0x40-0x47 */
+ 0x624E,0x625E,0x6263,0x625B,0x6260,0x6268,0x627C,0x6282,/* 0x48-0x4F */
+ 0x6289,0x627E,0x6292,0x6293,0x6296,0x62D4,0x6283,0x6294,/* 0x50-0x57 */
+ 0x62D7,0x62D1,0x62BB,0x62CF,0x62FF,0x62C6,0x64D4,0x62C8,/* 0x58-0x5F */
+ 0x62DC,0x62CC,0x62CA,0x62C2,0x62C7,0x629B,0x62C9,0x630C,/* 0x60-0x67 */
+ 0x62EE,0x62F1,0x6327,0x6302,0x6308,0x62EF,0x62F5,0x6350,/* 0x68-0x6F */
+ 0x633E,0x634D,0x641C,0x634F,0x6396,0x638E,0x6380,0x63AB,/* 0x70-0x77 */
+ 0x6376,0x63A3,0x638F,0x6389,0x639F,0x63B5,0x636B,0x0000,/* 0x78-0x7F */
+
+ 0x6369,0x63BE,0x63E9,0x63C0,0x63C6,0x63E3,0x63C9,0x63D2,/* 0x80-0x87 */
+ 0x63F6,0x63C4,0x6416,0x6434,0x6406,0x6413,0x6426,0x6436,/* 0x88-0x8F */
+ 0x651D,0x6417,0x6428,0x640F,0x6467,0x646F,0x6476,0x644E,/* 0x90-0x97 */
+ 0x652A,0x6495,0x6493,0x64A5,0x64A9,0x6488,0x64BC,0x64DA,/* 0x98-0x9F */
+ 0x64D2,0x64C5,0x64C7,0x64BB,0x64D8,0x64C2,0x64F1,0x64E7,/* 0xA0-0xA7 */
+ 0x8209,0x64E0,0x64E1,0x62AC,0x64E3,0x64EF,0x652C,0x64F6,/* 0xA8-0xAF */
+ 0x64F4,0x64F2,0x64FA,0x6500,0x64FD,0x6518,0x651C,0x6505,/* 0xB0-0xB7 */
+ 0x6524,0x6523,0x652B,0x6534,0x6535,0x6537,0x6536,0x6538,/* 0xB8-0xBF */
+ 0x754B,0x6548,0x6556,0x6555,0x654D,0x6558,0x655E,0x655D,/* 0xC0-0xC7 */
+ 0x6572,0x6578,0x6582,0x6583,0x8B8A,0x659B,0x659F,0x65AB,/* 0xC8-0xCF */
+ 0x65B7,0x65C3,0x65C6,0x65C1,0x65C4,0x65CC,0x65D2,0x65DB,/* 0xD0-0xD7 */
+ 0x65D9,0x65E0,0x65E1,0x65F1,0x6772,0x660A,0x6603,0x65FB,/* 0xD8-0xDF */
+ 0x6773,0x6635,0x6636,0x6634,0x661C,0x664F,0x6644,0x6649,/* 0xE0-0xE7 */
+ 0x6641,0x665E,0x665D,0x6664,0x6667,0x6668,0x665F,0x6662,/* 0xE8-0xEF */
+ 0x6670,0x6683,0x6688,0x668E,0x6689,0x6684,0x6698,0x669D,/* 0xF0-0xF7 */
+ 0x66C1,0x66B9,0x66C9,0x66BE,0x66BC,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_9E[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x66C4,0x66B8,0x66D6,0x66DA,0x66E0,0x663F,0x66E6,0x66E9,/* 0x40-0x47 */
+ 0x66F0,0x66F5,0x66F7,0x670F,0x6716,0x671E,0x6726,0x6727,/* 0x48-0x4F */
+ 0x9738,0x672E,0x673F,0x6736,0x6741,0x6738,0x6737,0x6746,/* 0x50-0x57 */
+ 0x675E,0x6760,0x6759,0x6763,0x6764,0x6789,0x6770,0x67A9,/* 0x58-0x5F */
+ 0x677C,0x676A,0x678C,0x678B,0x67A6,0x67A1,0x6785,0x67B7,/* 0x60-0x67 */
+ 0x67EF,0x67B4,0x67EC,0x67B3,0x67E9,0x67B8,0x67E4,0x67DE,/* 0x68-0x6F */
+ 0x67DD,0x67E2,0x67EE,0x67B9,0x67CE,0x67C6,0x67E7,0x6A9C,/* 0x70-0x77 */
+ 0x681E,0x6846,0x6829,0x6840,0x684D,0x6832,0x684E,0x0000,/* 0x78-0x7F */
+
+ 0x68B3,0x682B,0x6859,0x6863,0x6877,0x687F,0x689F,0x688F,/* 0x80-0x87 */
+ 0x68AD,0x6894,0x689D,0x689B,0x6883,0x6AAE,0x68B9,0x6874,/* 0x88-0x8F */
+ 0x68B5,0x68A0,0x68BA,0x690F,0x688D,0x687E,0x6901,0x68CA,/* 0x90-0x97 */
+ 0x6908,0x68D8,0x6922,0x6926,0x68E1,0x690C,0x68CD,0x68D4,/* 0x98-0x9F */
+ 0x68E7,0x68D5,0x6936,0x6912,0x6904,0x68D7,0x68E3,0x6925,/* 0xA0-0xA7 */
+ 0x68F9,0x68E0,0x68EF,0x6928,0x692A,0x691A,0x6923,0x6921,/* 0xA8-0xAF */
+ 0x68C6,0x6979,0x6977,0x695C,0x6978,0x696B,0x6954,0x697E,/* 0xB0-0xB7 */
+ 0x696E,0x6939,0x6974,0x693D,0x6959,0x6930,0x6961,0x695E,/* 0xB8-0xBF */
+ 0x695D,0x6981,0x696A,0x69B2,0x69AE,0x69D0,0x69BF,0x69C1,/* 0xC0-0xC7 */
+ 0x69D3,0x69BE,0x69CE,0x5BE8,0x69CA,0x69DD,0x69BB,0x69C3,/* 0xC8-0xCF */
+ 0x69A7,0x6A2E,0x6991,0x69A0,0x699C,0x6995,0x69B4,0x69DE,/* 0xD0-0xD7 */
+ 0x69E8,0x6A02,0x6A1B,0x69FF,0x6B0A,0x69F9,0x69F2,0x69E7,/* 0xD8-0xDF */
+ 0x6A05,0x69B1,0x6A1E,0x69ED,0x6A14,0x69EB,0x6A0A,0x6A12,/* 0xE0-0xE7 */
+ 0x6AC1,0x6A23,0x6A13,0x6A44,0x6A0C,0x6A72,0x6A36,0x6A78,/* 0xE8-0xEF */
+ 0x6A47,0x6A62,0x6A59,0x6A66,0x6A48,0x6A38,0x6A22,0x6A90,/* 0xF0-0xF7 */
+ 0x6A8D,0x6AA0,0x6A84,0x6AA2,0x6AA3,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_9F[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x6A97,0x8617,0x6ABB,0x6AC3,0x6AC2,0x6AB8,0x6AB3,0x6AAC,/* 0x40-0x47 */
+ 0x6ADE,0x6AD1,0x6ADF,0x6AAA,0x6ADA,0x6AEA,0x6AFB,0x6B05,/* 0x48-0x4F */
+ 0x8616,0x6AFA,0x6B12,0x6B16,0x9B31,0x6B1F,0x6B38,0x6B37,/* 0x50-0x57 */
+ 0x76DC,0x6B39,0x98EE,0x6B47,0x6B43,0x6B49,0x6B50,0x6B59,/* 0x58-0x5F */
+ 0x6B54,0x6B5B,0x6B5F,0x6B61,0x6B78,0x6B79,0x6B7F,0x6B80,/* 0x60-0x67 */
+ 0x6B84,0x6B83,0x6B8D,0x6B98,0x6B95,0x6B9E,0x6BA4,0x6BAA,/* 0x68-0x6F */
+ 0x6BAB,0x6BAF,0x6BB2,0x6BB1,0x6BB3,0x6BB7,0x6BBC,0x6BC6,/* 0x70-0x77 */
+ 0x6BCB,0x6BD3,0x6BDF,0x6BEC,0x6BEB,0x6BF3,0x6BEF,0x0000,/* 0x78-0x7F */
+
+ 0x9EBE,0x6C08,0x6C13,0x6C14,0x6C1B,0x6C24,0x6C23,0x6C5E,/* 0x80-0x87 */
+ 0x6C55,0x6C62,0x6C6A,0x6C82,0x6C8D,0x6C9A,0x6C81,0x6C9B,/* 0x88-0x8F */
+ 0x6C7E,0x6C68,0x6C73,0x6C92,0x6C90,0x6CC4,0x6CF1,0x6CD3,/* 0x90-0x97 */
+ 0x6CBD,0x6CD7,0x6CC5,0x6CDD,0x6CAE,0x6CB1,0x6CBE,0x6CBA,/* 0x98-0x9F */
+ 0x6CDB,0x6CEF,0x6CD9,0x6CEA,0x6D1F,0x884D,0x6D36,0x6D2B,/* 0xA0-0xA7 */
+ 0x6D3D,0x6D38,0x6D19,0x6D35,0x6D33,0x6D12,0x6D0C,0x6D63,/* 0xA8-0xAF */
+ 0x6D93,0x6D64,0x6D5A,0x6D79,0x6D59,0x6D8E,0x6D95,0x6FE4,/* 0xB0-0xB7 */
+ 0x6D85,0x6DF9,0x6E15,0x6E0A,0x6DB5,0x6DC7,0x6DE6,0x6DB8,/* 0xB8-0xBF */
+ 0x6DC6,0x6DEC,0x6DDE,0x6DCC,0x6DE8,0x6DD2,0x6DC5,0x6DFA,/* 0xC0-0xC7 */
+ 0x6DD9,0x6DE4,0x6DD5,0x6DEA,0x6DEE,0x6E2D,0x6E6E,0x6E2E,/* 0xC8-0xCF */
+ 0x6E19,0x6E72,0x6E5F,0x6E3E,0x6E23,0x6E6B,0x6E2B,0x6E76,/* 0xD0-0xD7 */
+ 0x6E4D,0x6E1F,0x6E43,0x6E3A,0x6E4E,0x6E24,0x6EFF,0x6E1D,/* 0xD8-0xDF */
+ 0x6E38,0x6E82,0x6EAA,0x6E98,0x6EC9,0x6EB7,0x6ED3,0x6EBD,/* 0xE0-0xE7 */
+ 0x6EAF,0x6EC4,0x6EB2,0x6ED4,0x6ED5,0x6E8F,0x6EA5,0x6EC2,/* 0xE8-0xEF */
+ 0x6E9F,0x6F41,0x6F11,0x704C,0x6EEC,0x6EF8,0x6EFE,0x6F3F,/* 0xF0-0xF7 */
+ 0x6EF2,0x6F31,0x6EEF,0x6F32,0x6ECC,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E0[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x6F3E,0x6F13,0x6EF7,0x6F86,0x6F7A,0x6F78,0x6F81,0x6F80,/* 0x40-0x47 */
+ 0x6F6F,0x6F5B,0x6FF3,0x6F6D,0x6F82,0x6F7C,0x6F58,0x6F8E,/* 0x48-0x4F */
+ 0x6F91,0x6FC2,0x6F66,0x6FB3,0x6FA3,0x6FA1,0x6FA4,0x6FB9,/* 0x50-0x57 */
+ 0x6FC6,0x6FAA,0x6FDF,0x6FD5,0x6FEC,0x6FD4,0x6FD8,0x6FF1,/* 0x58-0x5F */
+ 0x6FEE,0x6FDB,0x7009,0x700B,0x6FFA,0x7011,0x7001,0x700F,/* 0x60-0x67 */
+ 0x6FFE,0x701B,0x701A,0x6F74,0x701D,0x7018,0x701F,0x7030,/* 0x68-0x6F */
+ 0x703E,0x7032,0x7051,0x7063,0x7099,0x7092,0x70AF,0x70F1,/* 0x70-0x77 */
+ 0x70AC,0x70B8,0x70B3,0x70AE,0x70DF,0x70CB,0x70DD,0x0000,/* 0x78-0x7F */
+
+ 0x70D9,0x7109,0x70FD,0x711C,0x7119,0x7165,0x7155,0x7188,/* 0x80-0x87 */
+ 0x7166,0x7162,0x714C,0x7156,0x716C,0x718F,0x71FB,0x7184,/* 0x88-0x8F */
+ 0x7195,0x71A8,0x71AC,0x71D7,0x71B9,0x71BE,0x71D2,0x71C9,/* 0x90-0x97 */
+ 0x71D4,0x71CE,0x71E0,0x71EC,0x71E7,0x71F5,0x71FC,0x71F9,/* 0x98-0x9F */
+ 0x71FF,0x720D,0x7210,0x721B,0x7228,0x722D,0x722C,0x7230,/* 0xA0-0xA7 */
+ 0x7232,0x723B,0x723C,0x723F,0x7240,0x7246,0x724B,0x7258,/* 0xA8-0xAF */
+ 0x7274,0x727E,0x7282,0x7281,0x7287,0x7292,0x7296,0x72A2,/* 0xB0-0xB7 */
+ 0x72A7,0x72B9,0x72B2,0x72C3,0x72C6,0x72C4,0x72CE,0x72D2,/* 0xB8-0xBF */
+ 0x72E2,0x72E0,0x72E1,0x72F9,0x72F7,0x500F,0x7317,0x730A,/* 0xC0-0xC7 */
+ 0x731C,0x7316,0x731D,0x7334,0x732F,0x7329,0x7325,0x733E,/* 0xC8-0xCF */
+ 0x734E,0x734F,0x9ED8,0x7357,0x736A,0x7368,0x7370,0x7378,/* 0xD0-0xD7 */
+ 0x7375,0x737B,0x737A,0x73C8,0x73B3,0x73CE,0x73BB,0x73C0,/* 0xD8-0xDF */
+ 0x73E5,0x73EE,0x73DE,0x74A2,0x7405,0x746F,0x7425,0x73F8,/* 0xE0-0xE7 */
+ 0x7432,0x743A,0x7455,0x743F,0x745F,0x7459,0x7441,0x745C,/* 0xE8-0xEF */
+ 0x7469,0x7470,0x7463,0x746A,0x7476,0x747E,0x748B,0x749E,/* 0xF0-0xF7 */
+ 0x74A7,0x74CA,0x74CF,0x74D4,0x73F1,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E1[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x74E0,0x74E3,0x74E7,0x74E9,0x74EE,0x74F2,0x74F0,0x74F1,/* 0x40-0x47 */
+ 0x74F8,0x74F7,0x7504,0x7503,0x7505,0x750C,0x750E,0x750D,/* 0x48-0x4F */
+ 0x7515,0x7513,0x751E,0x7526,0x752C,0x753C,0x7544,0x754D,/* 0x50-0x57 */
+ 0x754A,0x7549,0x755B,0x7546,0x755A,0x7569,0x7564,0x7567,/* 0x58-0x5F */
+ 0x756B,0x756D,0x7578,0x7576,0x7586,0x7587,0x7574,0x758A,/* 0x60-0x67 */
+ 0x7589,0x7582,0x7594,0x759A,0x759D,0x75A5,0x75A3,0x75C2,/* 0x68-0x6F */
+ 0x75B3,0x75C3,0x75B5,0x75BD,0x75B8,0x75BC,0x75B1,0x75CD,/* 0x70-0x77 */
+ 0x75CA,0x75D2,0x75D9,0x75E3,0x75DE,0x75FE,0x75FF,0x0000,/* 0x78-0x7F */
+
+ 0x75FC,0x7601,0x75F0,0x75FA,0x75F2,0x75F3,0x760B,0x760D,/* 0x80-0x87 */
+ 0x7609,0x761F,0x7627,0x7620,0x7621,0x7622,0x7624,0x7634,/* 0x88-0x8F */
+ 0x7630,0x763B,0x7647,0x7648,0x7646,0x765C,0x7658,0x7661,/* 0x90-0x97 */
+ 0x7662,0x7668,0x7669,0x766A,0x7667,0x766C,0x7670,0x7672,/* 0x98-0x9F */
+ 0x7676,0x7678,0x767C,0x7680,0x7683,0x7688,0x768B,0x768E,/* 0xA0-0xA7 */
+ 0x7696,0x7693,0x7699,0x769A,0x76B0,0x76B4,0x76B8,0x76B9,/* 0xA8-0xAF */
+ 0x76BA,0x76C2,0x76CD,0x76D6,0x76D2,0x76DE,0x76E1,0x76E5,/* 0xB0-0xB7 */
+ 0x76E7,0x76EA,0x862F,0x76FB,0x7708,0x7707,0x7704,0x7729,/* 0xB8-0xBF */
+ 0x7724,0x771E,0x7725,0x7726,0x771B,0x7737,0x7738,0x7747,/* 0xC0-0xC7 */
+ 0x775A,0x7768,0x776B,0x775B,0x7765,0x777F,0x777E,0x7779,/* 0xC8-0xCF */
+ 0x778E,0x778B,0x7791,0x77A0,0x779E,0x77B0,0x77B6,0x77B9,/* 0xD0-0xD7 */
+ 0x77BF,0x77BC,0x77BD,0x77BB,0x77C7,0x77CD,0x77D7,0x77DA,/* 0xD8-0xDF */
+ 0x77DC,0x77E3,0x77EE,0x77FC,0x780C,0x7812,0x7926,0x7820,/* 0xE0-0xE7 */
+ 0x792A,0x7845,0x788E,0x7874,0x7886,0x787C,0x789A,0x788C,/* 0xE8-0xEF */
+ 0x78A3,0x78B5,0x78AA,0x78AF,0x78D1,0x78C6,0x78CB,0x78D4,/* 0xF0-0xF7 */
+ 0x78BE,0x78BC,0x78C5,0x78CA,0x78EC,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E2[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x78E7,0x78DA,0x78FD,0x78F4,0x7907,0x7912,0x7911,0x7919,/* 0x40-0x47 */
+ 0x792C,0x792B,0x7940,0x7960,0x7957,0x795F,0x795A,0x7955,/* 0x48-0x4F */
+ 0x7953,0x797A,0x797F,0x798A,0x799D,0x79A7,0x9F4B,0x79AA,/* 0x50-0x57 */
+ 0x79AE,0x79B3,0x79B9,0x79BA,0x79C9,0x79D5,0x79E7,0x79EC,/* 0x58-0x5F */
+ 0x79E1,0x79E3,0x7A08,0x7A0D,0x7A18,0x7A19,0x7A20,0x7A1F,/* 0x60-0x67 */
+ 0x7980,0x7A31,0x7A3B,0x7A3E,0x7A37,0x7A43,0x7A57,0x7A49,/* 0x68-0x6F */
+ 0x7A61,0x7A62,0x7A69,0x9F9D,0x7A70,0x7A79,0x7A7D,0x7A88,/* 0x70-0x77 */
+ 0x7A97,0x7A95,0x7A98,0x7A96,0x7AA9,0x7AC8,0x7AB0,0x0000,/* 0x78-0x7F */
+
+ 0x7AB6,0x7AC5,0x7AC4,0x7ABF,0x9083,0x7AC7,0x7ACA,0x7ACD,/* 0x80-0x87 */
+ 0x7ACF,0x7AD5,0x7AD3,0x7AD9,0x7ADA,0x7ADD,0x7AE1,0x7AE2,/* 0x88-0x8F */
+ 0x7AE6,0x7AED,0x7AF0,0x7B02,0x7B0F,0x7B0A,0x7B06,0x7B33,/* 0x90-0x97 */
+ 0x7B18,0x7B19,0x7B1E,0x7B35,0x7B28,0x7B36,0x7B50,0x7B7A,/* 0x98-0x9F */
+ 0x7B04,0x7B4D,0x7B0B,0x7B4C,0x7B45,0x7B75,0x7B65,0x7B74,/* 0xA0-0xA7 */
+ 0x7B67,0x7B70,0x7B71,0x7B6C,0x7B6E,0x7B9D,0x7B98,0x7B9F,/* 0xA8-0xAF */
+ 0x7B8D,0x7B9C,0x7B9A,0x7B8B,0x7B92,0x7B8F,0x7B5D,0x7B99,/* 0xB0-0xB7 */
+ 0x7BCB,0x7BC1,0x7BCC,0x7BCF,0x7BB4,0x7BC6,0x7BDD,0x7BE9,/* 0xB8-0xBF */
+ 0x7C11,0x7C14,0x7BE6,0x7BE5,0x7C60,0x7C00,0x7C07,0x7C13,/* 0xC0-0xC7 */
+ 0x7BF3,0x7BF7,0x7C17,0x7C0D,0x7BF6,0x7C23,0x7C27,0x7C2A,/* 0xC8-0xCF */
+ 0x7C1F,0x7C37,0x7C2B,0x7C3D,0x7C4C,0x7C43,0x7C54,0x7C4F,/* 0xD0-0xD7 */
+ 0x7C40,0x7C50,0x7C58,0x7C5F,0x7C64,0x7C56,0x7C65,0x7C6C,/* 0xD8-0xDF */
+ 0x7C75,0x7C83,0x7C90,0x7CA4,0x7CAD,0x7CA2,0x7CAB,0x7CA1,/* 0xE0-0xE7 */
+ 0x7CA8,0x7CB3,0x7CB2,0x7CB1,0x7CAE,0x7CB9,0x7CBD,0x7CC0,/* 0xE8-0xEF */
+ 0x7CC5,0x7CC2,0x7CD8,0x7CD2,0x7CDC,0x7CE2,0x9B3B,0x7CEF,/* 0xF0-0xF7 */
+ 0x7CF2,0x7CF4,0x7CF6,0x7CFA,0x7D06,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E3[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x7D02,0x7D1C,0x7D15,0x7D0A,0x7D45,0x7D4B,0x7D2E,0x7D32,/* 0x40-0x47 */
+ 0x7D3F,0x7D35,0x7D46,0x7D73,0x7D56,0x7D4E,0x7D72,0x7D68,/* 0x48-0x4F */
+ 0x7D6E,0x7D4F,0x7D63,0x7D93,0x7D89,0x7D5B,0x7D8F,0x7D7D,/* 0x50-0x57 */
+ 0x7D9B,0x7DBA,0x7DAE,0x7DA3,0x7DB5,0x7DC7,0x7DBD,0x7DAB,/* 0x58-0x5F */
+ 0x7E3D,0x7DA2,0x7DAF,0x7DDC,0x7DB8,0x7D9F,0x7DB0,0x7DD8,/* 0x60-0x67 */
+ 0x7DDD,0x7DE4,0x7DDE,0x7DFB,0x7DF2,0x7DE1,0x7E05,0x7E0A,/* 0x68-0x6F */
+ 0x7E23,0x7E21,0x7E12,0x7E31,0x7E1F,0x7E09,0x7E0B,0x7E22,/* 0x70-0x77 */
+ 0x7E46,0x7E66,0x7E3B,0x7E35,0x7E39,0x7E43,0x7E37,0x0000,/* 0x78-0x7F */
+
+ 0x7E32,0x7E3A,0x7E67,0x7E5D,0x7E56,0x7E5E,0x7E59,0x7E5A,/* 0x80-0x87 */
+ 0x7E79,0x7E6A,0x7E69,0x7E7C,0x7E7B,0x7E83,0x7DD5,0x7E7D,/* 0x88-0x8F */
+ 0x8FAE,0x7E7F,0x7E88,0x7E89,0x7E8C,0x7E92,0x7E90,0x7E93,/* 0x90-0x97 */
+ 0x7E94,0x7E96,0x7E8E,0x7E9B,0x7E9C,0x7F38,0x7F3A,0x7F45,/* 0x98-0x9F */
+ 0x7F4C,0x7F4D,0x7F4E,0x7F50,0x7F51,0x7F55,0x7F54,0x7F58,/* 0xA0-0xA7 */
+ 0x7F5F,0x7F60,0x7F68,0x7F69,0x7F67,0x7F78,0x7F82,0x7F86,/* 0xA8-0xAF */
+ 0x7F83,0x7F88,0x7F87,0x7F8C,0x7F94,0x7F9E,0x7F9D,0x7F9A,/* 0xB0-0xB7 */
+ 0x7FA3,0x7FAF,0x7FB2,0x7FB9,0x7FAE,0x7FB6,0x7FB8,0x8B71,/* 0xB8-0xBF */
+ 0x7FC5,0x7FC6,0x7FCA,0x7FD5,0x7FD4,0x7FE1,0x7FE6,0x7FE9,/* 0xC0-0xC7 */
+ 0x7FF3,0x7FF9,0x98DC,0x8006,0x8004,0x800B,0x8012,0x8018,/* 0xC8-0xCF */
+ 0x8019,0x801C,0x8021,0x8028,0x803F,0x803B,0x804A,0x8046,/* 0xD0-0xD7 */
+ 0x8052,0x8058,0x805A,0x805F,0x8062,0x8068,0x8073,0x8072,/* 0xD8-0xDF */
+ 0x8070,0x8076,0x8079,0x807D,0x807F,0x8084,0x8086,0x8085,/* 0xE0-0xE7 */
+ 0x809B,0x8093,0x809A,0x80AD,0x5190,0x80AC,0x80DB,0x80E5,/* 0xE8-0xEF */
+ 0x80D9,0x80DD,0x80C4,0x80DA,0x80D6,0x8109,0x80EF,0x80F1,/* 0xF0-0xF7 */
+ 0x811B,0x8129,0x8123,0x812F,0x814B,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E4[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x968B,0x8146,0x813E,0x8153,0x8151,0x80FC,0x8171,0x816E,/* 0x40-0x47 */
+ 0x8165,0x8166,0x8174,0x8183,0x8188,0x818A,0x8180,0x8182,/* 0x48-0x4F */
+ 0x81A0,0x8195,0x81A4,0x81A3,0x815F,0x8193,0x81A9,0x81B0,/* 0x50-0x57 */
+ 0x81B5,0x81BE,0x81B8,0x81BD,0x81C0,0x81C2,0x81BA,0x81C9,/* 0x58-0x5F */
+ 0x81CD,0x81D1,0x81D9,0x81D8,0x81C8,0x81DA,0x81DF,0x81E0,/* 0x60-0x67 */
+ 0x81E7,0x81FA,0x81FB,0x81FE,0x8201,0x8202,0x8205,0x8207,/* 0x68-0x6F */
+ 0x820A,0x820D,0x8210,0x8216,0x8229,0x822B,0x8238,0x8233,/* 0x70-0x77 */
+ 0x8240,0x8259,0x8258,0x825D,0x825A,0x825F,0x8264,0x0000,/* 0x78-0x7F */
+
+ 0x8262,0x8268,0x826A,0x826B,0x822E,0x8271,0x8277,0x8278,/* 0x80-0x87 */
+ 0x827E,0x828D,0x8292,0x82AB,0x829F,0x82BB,0x82AC,0x82E1,/* 0x88-0x8F */
+ 0x82E3,0x82DF,0x82D2,0x82F4,0x82F3,0x82FA,0x8393,0x8303,/* 0x90-0x97 */
+ 0x82FB,0x82F9,0x82DE,0x8306,0x82DC,0x8309,0x82D9,0x8335,/* 0x98-0x9F */
+ 0x8334,0x8316,0x8332,0x8331,0x8340,0x8339,0x8350,0x8345,/* 0xA0-0xA7 */
+ 0x832F,0x832B,0x8317,0x8318,0x8385,0x839A,0x83AA,0x839F,/* 0xA8-0xAF */
+ 0x83A2,0x8396,0x8323,0x838E,0x8387,0x838A,0x837C,0x83B5,/* 0xB0-0xB7 */
+ 0x8373,0x8375,0x83A0,0x8389,0x83A8,0x83F4,0x8413,0x83EB,/* 0xB8-0xBF */
+ 0x83CE,0x83FD,0x8403,0x83D8,0x840B,0x83C1,0x83F7,0x8407,/* 0xC0-0xC7 */
+ 0x83E0,0x83F2,0x840D,0x8422,0x8420,0x83BD,0x8438,0x8506,/* 0xC8-0xCF */
+ 0x83FB,0x846D,0x842A,0x843C,0x855A,0x8484,0x8477,0x846B,/* 0xD0-0xD7 */
+ 0x84AD,0x846E,0x8482,0x8469,0x8446,0x842C,0x846F,0x8479,/* 0xD8-0xDF */
+ 0x8435,0x84CA,0x8462,0x84B9,0x84BF,0x849F,0x84D9,0x84CD,/* 0xE0-0xE7 */
+ 0x84BB,0x84DA,0x84D0,0x84C1,0x84C6,0x84D6,0x84A1,0x8521,/* 0xE8-0xEF */
+ 0x84FF,0x84F4,0x8517,0x8518,0x852C,0x851F,0x8515,0x8514,/* 0xF0-0xF7 */
+ 0x84FC,0x8540,0x8563,0x8558,0x8548,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E5[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x8541,0x8602,0x854B,0x8555,0x8580,0x85A4,0x8588,0x8591,/* 0x40-0x47 */
+ 0x858A,0x85A8,0x856D,0x8594,0x859B,0x85EA,0x8587,0x859C,/* 0x48-0x4F */
+ 0x8577,0x857E,0x8590,0x85C9,0x85BA,0x85CF,0x85B9,0x85D0,/* 0x50-0x57 */
+ 0x85D5,0x85DD,0x85E5,0x85DC,0x85F9,0x860A,0x8613,0x860B,/* 0x58-0x5F */
+ 0x85FE,0x85FA,0x8606,0x8622,0x861A,0x8630,0x863F,0x864D,/* 0x60-0x67 */
+ 0x4E55,0x8654,0x865F,0x8667,0x8671,0x8693,0x86A3,0x86A9,/* 0x68-0x6F */
+ 0x86AA,0x868B,0x868C,0x86B6,0x86AF,0x86C4,0x86C6,0x86B0,/* 0x70-0x77 */
+ 0x86C9,0x8823,0x86AB,0x86D4,0x86DE,0x86E9,0x86EC,0x0000,/* 0x78-0x7F */
+
+ 0x86DF,0x86DB,0x86EF,0x8712,0x8706,0x8708,0x8700,0x8703,/* 0x80-0x87 */
+ 0x86FB,0x8711,0x8709,0x870D,0x86F9,0x870A,0x8734,0x873F,/* 0x88-0x8F */
+ 0x8737,0x873B,0x8725,0x8729,0x871A,0x8760,0x875F,0x8778,/* 0x90-0x97 */
+ 0x874C,0x874E,0x8774,0x8757,0x8768,0x876E,0x8759,0x8753,/* 0x98-0x9F */
+ 0x8763,0x876A,0x8805,0x87A2,0x879F,0x8782,0x87AF,0x87CB,/* 0xA0-0xA7 */
+ 0x87BD,0x87C0,0x87D0,0x96D6,0x87AB,0x87C4,0x87B3,0x87C7,/* 0xA8-0xAF */
+ 0x87C6,0x87BB,0x87EF,0x87F2,0x87E0,0x880F,0x880D,0x87FE,/* 0xB0-0xB7 */
+ 0x87F6,0x87F7,0x880E,0x87D2,0x8811,0x8816,0x8815,0x8822,/* 0xB8-0xBF */
+ 0x8821,0x8831,0x8836,0x8839,0x8827,0x883B,0x8844,0x8842,/* 0xC0-0xC7 */
+ 0x8852,0x8859,0x885E,0x8862,0x886B,0x8881,0x887E,0x889E,/* 0xC8-0xCF */
+ 0x8875,0x887D,0x88B5,0x8872,0x8882,0x8897,0x8892,0x88AE,/* 0xD0-0xD7 */
+ 0x8899,0x88A2,0x888D,0x88A4,0x88B0,0x88BF,0x88B1,0x88C3,/* 0xD8-0xDF */
+ 0x88C4,0x88D4,0x88D8,0x88D9,0x88DD,0x88F9,0x8902,0x88FC,/* 0xE0-0xE7 */
+ 0x88F4,0x88E8,0x88F2,0x8904,0x890C,0x890A,0x8913,0x8943,/* 0xE8-0xEF */
+ 0x891E,0x8925,0x892A,0x892B,0x8941,0x8944,0x893B,0x8936,/* 0xF0-0xF7 */
+ 0x8938,0x894C,0x891D,0x8960,0x895E,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E6[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x8966,0x8964,0x896D,0x896A,0x896F,0x8974,0x8977,0x897E,/* 0x40-0x47 */
+ 0x8983,0x8988,0x898A,0x8993,0x8998,0x89A1,0x89A9,0x89A6,/* 0x48-0x4F */
+ 0x89AC,0x89AF,0x89B2,0x89BA,0x89BD,0x89BF,0x89C0,0x89DA,/* 0x50-0x57 */
+ 0x89DC,0x89DD,0x89E7,0x89F4,0x89F8,0x8A03,0x8A16,0x8A10,/* 0x58-0x5F */
+ 0x8A0C,0x8A1B,0x8A1D,0x8A25,0x8A36,0x8A41,0x8A5B,0x8A52,/* 0x60-0x67 */
+ 0x8A46,0x8A48,0x8A7C,0x8A6D,0x8A6C,0x8A62,0x8A85,0x8A82,/* 0x68-0x6F */
+ 0x8A84,0x8AA8,0x8AA1,0x8A91,0x8AA5,0x8AA6,0x8A9A,0x8AA3,/* 0x70-0x77 */
+ 0x8AC4,0x8ACD,0x8AC2,0x8ADA,0x8AEB,0x8AF3,0x8AE7,0x0000,/* 0x78-0x7F */
+
+ 0x8AE4,0x8AF1,0x8B14,0x8AE0,0x8AE2,0x8AF7,0x8ADE,0x8ADB,/* 0x80-0x87 */
+ 0x8B0C,0x8B07,0x8B1A,0x8AE1,0x8B16,0x8B10,0x8B17,0x8B20,/* 0x88-0x8F */
+ 0x8B33,0x97AB,0x8B26,0x8B2B,0x8B3E,0x8B28,0x8B41,0x8B4C,/* 0x90-0x97 */
+ 0x8B4F,0x8B4E,0x8B49,0x8B56,0x8B5B,0x8B5A,0x8B6B,0x8B5F,/* 0x98-0x9F */
+ 0x8B6C,0x8B6F,0x8B74,0x8B7D,0x8B80,0x8B8C,0x8B8E,0x8B92,/* 0xA0-0xA7 */
+ 0x8B93,0x8B96,0x8B99,0x8B9A,0x8C3A,0x8C41,0x8C3F,0x8C48,/* 0xA8-0xAF */
+ 0x8C4C,0x8C4E,0x8C50,0x8C55,0x8C62,0x8C6C,0x8C78,0x8C7A,/* 0xB0-0xB7 */
+ 0x8C82,0x8C89,0x8C85,0x8C8A,0x8C8D,0x8C8E,0x8C94,0x8C7C,/* 0xB8-0xBF */
+ 0x8C98,0x621D,0x8CAD,0x8CAA,0x8CBD,0x8CB2,0x8CB3,0x8CAE,/* 0xC0-0xC7 */
+ 0x8CB6,0x8CC8,0x8CC1,0x8CE4,0x8CE3,0x8CDA,0x8CFD,0x8CFA,/* 0xC8-0xCF */
+ 0x8CFB,0x8D04,0x8D05,0x8D0A,0x8D07,0x8D0F,0x8D0D,0x8D10,/* 0xD0-0xD7 */
+ 0x9F4E,0x8D13,0x8CCD,0x8D14,0x8D16,0x8D67,0x8D6D,0x8D71,/* 0xD8-0xDF */
+ 0x8D73,0x8D81,0x8D99,0x8DC2,0x8DBE,0x8DBA,0x8DCF,0x8DDA,/* 0xE0-0xE7 */
+ 0x8DD6,0x8DCC,0x8DDB,0x8DCB,0x8DEA,0x8DEB,0x8DDF,0x8DE3,/* 0xE8-0xEF */
+ 0x8DFC,0x8E08,0x8E09,0x8DFF,0x8E1D,0x8E1E,0x8E10,0x8E1F,/* 0xF0-0xF7 */
+ 0x8E42,0x8E35,0x8E30,0x8E34,0x8E4A,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E7[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x8E47,0x8E49,0x8E4C,0x8E50,0x8E48,0x8E59,0x8E64,0x8E60,/* 0x40-0x47 */
+ 0x8E2A,0x8E63,0x8E55,0x8E76,0x8E72,0x8E7C,0x8E81,0x8E87,/* 0x48-0x4F */
+ 0x8E85,0x8E84,0x8E8B,0x8E8A,0x8E93,0x8E91,0x8E94,0x8E99,/* 0x50-0x57 */
+ 0x8EAA,0x8EA1,0x8EAC,0x8EB0,0x8EC6,0x8EB1,0x8EBE,0x8EC5,/* 0x58-0x5F */
+ 0x8EC8,0x8ECB,0x8EDB,0x8EE3,0x8EFC,0x8EFB,0x8EEB,0x8EFE,/* 0x60-0x67 */
+ 0x8F0A,0x8F05,0x8F15,0x8F12,0x8F19,0x8F13,0x8F1C,0x8F1F,/* 0x68-0x6F */
+ 0x8F1B,0x8F0C,0x8F26,0x8F33,0x8F3B,0x8F39,0x8F45,0x8F42,/* 0x70-0x77 */
+ 0x8F3E,0x8F4C,0x8F49,0x8F46,0x8F4E,0x8F57,0x8F5C,0x0000,/* 0x78-0x7F */
+
+ 0x8F62,0x8F63,0x8F64,0x8F9C,0x8F9F,0x8FA3,0x8FAD,0x8FAF,/* 0x80-0x87 */
+ 0x8FB7,0x8FDA,0x8FE5,0x8FE2,0x8FEA,0x8FEF,0x9087,0x8FF4,/* 0x88-0x8F */
+ 0x9005,0x8FF9,0x8FFA,0x9011,0x9015,0x9021,0x900D,0x901E,/* 0x90-0x97 */
+ 0x9016,0x900B,0x9027,0x9036,0x9035,0x9039,0x8FF8,0x904F,/* 0x98-0x9F */
+ 0x9050,0x9051,0x9052,0x900E,0x9049,0x903E,0x9056,0x9058,/* 0xA0-0xA7 */
+ 0x905E,0x9068,0x906F,0x9076,0x96A8,0x9072,0x9082,0x907D,/* 0xA8-0xAF */
+ 0x9081,0x9080,0x908A,0x9089,0x908F,0x90A8,0x90AF,0x90B1,/* 0xB0-0xB7 */
+ 0x90B5,0x90E2,0x90E4,0x6248,0x90DB,0x9102,0x9112,0x9119,/* 0xB8-0xBF */
+ 0x9132,0x9130,0x914A,0x9156,0x9158,0x9163,0x9165,0x9169,/* 0xC0-0xC7 */
+ 0x9173,0x9172,0x918B,0x9189,0x9182,0x91A2,0x91AB,0x91AF,/* 0xC8-0xCF */
+ 0x91AA,0x91B5,0x91B4,0x91BA,0x91C0,0x91C1,0x91C9,0x91CB,/* 0xD0-0xD7 */
+ 0x91D0,0x91D6,0x91DF,0x91E1,0x91DB,0x91FC,0x91F5,0x91F6,/* 0xD8-0xDF */
+ 0x921E,0x91FF,0x9214,0x922C,0x9215,0x9211,0x925E,0x9257,/* 0xE0-0xE7 */
+ 0x9245,0x9249,0x9264,0x9248,0x9295,0x923F,0x924B,0x9250,/* 0xE8-0xEF */
+ 0x929C,0x9296,0x9293,0x929B,0x925A,0x92CF,0x92B9,0x92B7,/* 0xF0-0xF7 */
+ 0x92E9,0x930F,0x92FA,0x9344,0x932E,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E8[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x9319,0x9322,0x931A,0x9323,0x933A,0x9335,0x933B,0x935C,/* 0x40-0x47 */
+ 0x9360,0x937C,0x936E,0x9356,0x93B0,0x93AC,0x93AD,0x9394,/* 0x48-0x4F */
+ 0x93B9,0x93D6,0x93D7,0x93E8,0x93E5,0x93D8,0x93C3,0x93DD,/* 0x50-0x57 */
+ 0x93D0,0x93C8,0x93E4,0x941A,0x9414,0x9413,0x9403,0x9407,/* 0x58-0x5F */
+ 0x9410,0x9436,0x942B,0x9435,0x9421,0x943A,0x9441,0x9452,/* 0x60-0x67 */
+ 0x9444,0x945B,0x9460,0x9462,0x945E,0x946A,0x9229,0x9470,/* 0x68-0x6F */
+ 0x9475,0x9477,0x947D,0x945A,0x947C,0x947E,0x9481,0x947F,/* 0x70-0x77 */
+ 0x9582,0x9587,0x958A,0x9594,0x9596,0x9598,0x9599,0x0000,/* 0x78-0x7F */
+
+ 0x95A0,0x95A8,0x95A7,0x95AD,0x95BC,0x95BB,0x95B9,0x95BE,/* 0x80-0x87 */
+ 0x95CA,0x6FF6,0x95C3,0x95CD,0x95CC,0x95D5,0x95D4,0x95D6,/* 0x88-0x8F */
+ 0x95DC,0x95E1,0x95E5,0x95E2,0x9621,0x9628,0x962E,0x962F,/* 0x90-0x97 */
+ 0x9642,0x964C,0x964F,0x964B,0x9677,0x965C,0x965E,0x965D,/* 0x98-0x9F */
+ 0x965F,0x9666,0x9672,0x966C,0x968D,0x9698,0x9695,0x9697,/* 0xA0-0xA7 */
+ 0x96AA,0x96A7,0x96B1,0x96B2,0x96B0,0x96B4,0x96B6,0x96B8,/* 0xA8-0xAF */
+ 0x96B9,0x96CE,0x96CB,0x96C9,0x96CD,0x894D,0x96DC,0x970D,/* 0xB0-0xB7 */
+ 0x96D5,0x96F9,0x9704,0x9706,0x9708,0x9713,0x970E,0x9711,/* 0xB8-0xBF */
+ 0x970F,0x9716,0x9719,0x9724,0x972A,0x9730,0x9739,0x973D,/* 0xC0-0xC7 */
+ 0x973E,0x9744,0x9746,0x9748,0x9742,0x9749,0x975C,0x9760,/* 0xC8-0xCF */
+ 0x9764,0x9766,0x9768,0x52D2,0x976B,0x9771,0x9779,0x9785,/* 0xD0-0xD7 */
+ 0x977C,0x9781,0x977A,0x9786,0x978B,0x978F,0x9790,0x979C,/* 0xD8-0xDF */
+ 0x97A8,0x97A6,0x97A3,0x97B3,0x97B4,0x97C3,0x97C6,0x97C8,/* 0xE0-0xE7 */
+ 0x97CB,0x97DC,0x97ED,0x9F4F,0x97F2,0x7ADF,0x97F6,0x97F5,/* 0xE8-0xEF */
+ 0x980F,0x980C,0x9838,0x9824,0x9821,0x9837,0x983D,0x9846,/* 0xF0-0xF7 */
+ 0x984F,0x984B,0x986B,0x986F,0x9870,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E9[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x9871,0x9874,0x9873,0x98AA,0x98AF,0x98B1,0x98B6,0x98C4,/* 0x40-0x47 */
+ 0x98C3,0x98C6,0x98E9,0x98EB,0x9903,0x9909,0x9912,0x9914,/* 0x48-0x4F */
+ 0x9918,0x9921,0x991D,0x991E,0x9924,0x9920,0x992C,0x992E,/* 0x50-0x57 */
+ 0x993D,0x993E,0x9942,0x9949,0x9945,0x9950,0x994B,0x9951,/* 0x58-0x5F */
+ 0x9952,0x994C,0x9955,0x9997,0x9998,0x99A5,0x99AD,0x99AE,/* 0x60-0x67 */
+ 0x99BC,0x99DF,0x99DB,0x99DD,0x99D8,0x99D1,0x99ED,0x99EE,/* 0x68-0x6F */
+ 0x99F1,0x99F2,0x99FB,0x99F8,0x9A01,0x9A0F,0x9A05,0x99E2,/* 0x70-0x77 */
+ 0x9A19,0x9A2B,0x9A37,0x9A45,0x9A42,0x9A40,0x9A43,0x0000,/* 0x78-0x7F */
+
+ 0x9A3E,0x9A55,0x9A4D,0x9A5B,0x9A57,0x9A5F,0x9A62,0x9A65,/* 0x80-0x87 */
+ 0x9A64,0x9A69,0x9A6B,0x9A6A,0x9AAD,0x9AB0,0x9ABC,0x9AC0,/* 0x88-0x8F */
+ 0x9ACF,0x9AD1,0x9AD3,0x9AD4,0x9ADE,0x9ADF,0x9AE2,0x9AE3,/* 0x90-0x97 */
+ 0x9AE6,0x9AEF,0x9AEB,0x9AEE,0x9AF4,0x9AF1,0x9AF7,0x9AFB,/* 0x98-0x9F */
+ 0x9B06,0x9B18,0x9B1A,0x9B1F,0x9B22,0x9B23,0x9B25,0x9B27,/* 0xA0-0xA7 */
+ 0x9B28,0x9B29,0x9B2A,0x9B2E,0x9B2F,0x9B32,0x9B44,0x9B43,/* 0xA8-0xAF */
+ 0x9B4F,0x9B4D,0x9B4E,0x9B51,0x9B58,0x9B74,0x9B93,0x9B83,/* 0xB0-0xB7 */
+ 0x9B91,0x9B96,0x9B97,0x9B9F,0x9BA0,0x9BA8,0x9BB4,0x9BC0,/* 0xB8-0xBF */
+ 0x9BCA,0x9BB9,0x9BC6,0x9BCF,0x9BD1,0x9BD2,0x9BE3,0x9BE2,/* 0xC0-0xC7 */
+ 0x9BE4,0x9BD4,0x9BE1,0x9C3A,0x9BF2,0x9BF1,0x9BF0,0x9C15,/* 0xC8-0xCF */
+ 0x9C14,0x9C09,0x9C13,0x9C0C,0x9C06,0x9C08,0x9C12,0x9C0A,/* 0xD0-0xD7 */
+ 0x9C04,0x9C2E,0x9C1B,0x9C25,0x9C24,0x9C21,0x9C30,0x9C47,/* 0xD8-0xDF */
+ 0x9C32,0x9C46,0x9C3E,0x9C5A,0x9C60,0x9C67,0x9C76,0x9C78,/* 0xE0-0xE7 */
+ 0x9CE7,0x9CEC,0x9CF0,0x9D09,0x9D08,0x9CEB,0x9D03,0x9D06,/* 0xE8-0xEF */
+ 0x9D2A,0x9D26,0x9DAF,0x9D23,0x9D1F,0x9D44,0x9D15,0x9D12,/* 0xF0-0xF7 */
+ 0x9D41,0x9D3F,0x9D3E,0x9D46,0x9D48,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_EA[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x9D5D,0x9D5E,0x9D64,0x9D51,0x9D50,0x9D59,0x9D72,0x9D89,/* 0x40-0x47 */
+ 0x9D87,0x9DAB,0x9D6F,0x9D7A,0x9D9A,0x9DA4,0x9DA9,0x9DB2,/* 0x48-0x4F */
+ 0x9DC4,0x9DC1,0x9DBB,0x9DB8,0x9DBA,0x9DC6,0x9DCF,0x9DC2,/* 0x50-0x57 */
+ 0x9DD9,0x9DD3,0x9DF8,0x9DE6,0x9DED,0x9DEF,0x9DFD,0x9E1A,/* 0x58-0x5F */
+ 0x9E1B,0x9E1E,0x9E75,0x9E79,0x9E7D,0x9E81,0x9E88,0x9E8B,/* 0x60-0x67 */
+ 0x9E8C,0x9E92,0x9E95,0x9E91,0x9E9D,0x9EA5,0x9EA9,0x9EB8,/* 0x68-0x6F */
+ 0x9EAA,0x9EAD,0x9761,0x9ECC,0x9ECE,0x9ECF,0x9ED0,0x9ED4,/* 0x70-0x77 */
+ 0x9EDC,0x9EDE,0x9EDD,0x9EE0,0x9EE5,0x9EE8,0x9EEF,0x0000,/* 0x78-0x7F */
+
+ 0x9EF4,0x9EF6,0x9EF7,0x9EF9,0x9EFB,0x9EFC,0x9EFD,0x9F07,/* 0x80-0x87 */
+ 0x9F08,0x76B7,0x9F15,0x9F21,0x9F2C,0x9F3E,0x9F4A,0x9F52,/* 0x88-0x8F */
+ 0x9F54,0x9F63,0x9F5F,0x9F60,0x9F61,0x9F66,0x9F67,0x9F6C,/* 0x90-0x97 */
+ 0x9F6A,0x9F77,0x9F72,0x9F76,0x9F95,0x9F9C,0x9FA0,0x582F,/* 0x98-0x9F */
+ 0x69C7,0x9059,0x7464,0x51DC,0x7199,0x0000,0x0000,0x0000,/* 0xA0-0xA7 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xA8-0xAF */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xB0-0xB7 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xB8-0xBF */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xC0-0xC7 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xC8-0xCF */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xD0-0xD7 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xD8-0xDF */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xE0-0xE7 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xE8-0xEF */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xF0-0xF7 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_ED[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x7E8A,0x891C,0x9348,0x9288,0x84DC,0x4FC9,0x70BB,0x6631,/* 0x40-0x47 */
+ 0x68C8,0x92F9,0x66FB,0x5F45,0x4E28,0x4EE1,0x4EFC,0x4F00,/* 0x48-0x4F */
+ 0x4F03,0x4F39,0x4F56,0x4F92,0x4F8A,0x4F9A,0x4F94,0x4FCD,/* 0x50-0x57 */
+ 0x5040,0x5022,0x4FFF,0x501E,0x5046,0x5070,0x5042,0x5094,/* 0x58-0x5F */
+ 0x50F4,0x50D8,0x514A,0x5164,0x519D,0x51BE,0x51EC,0x5215,/* 0x60-0x67 */
+ 0x529C,0x52A6,0x52C0,0x52DB,0x5300,0x5307,0x5324,0x5372,/* 0x68-0x6F */
+ 0x5393,0x53B2,0x53DD,0xFA0E,0x549C,0x548A,0x54A9,0x54FF,/* 0x70-0x77 */
+ 0x5586,0x5759,0x5765,0x57AC,0x57C8,0x57C7,0xFA0F,0x0000,/* 0x78-0x7F */
+
+ 0xFA10,0x589E,0x58B2,0x590B,0x5953,0x595B,0x595D,0x5963,/* 0x80-0x87 */
+ 0x59A4,0x59BA,0x5B56,0x5BC0,0x752F,0x5BD8,0x5BEC,0x5C1E,/* 0x88-0x8F */
+ 0x5CA6,0x5CBA,0x5CF5,0x5D27,0x5D53,0xFA11,0x5D42,0x5D6D,/* 0x90-0x97 */
+ 0x5DB8,0x5DB9,0x5DD0,0x5F21,0x5F34,0x5F67,0x5FB7,0x5FDE,/* 0x98-0x9F */
+ 0x605D,0x6085,0x608A,0x60DE,0x60D5,0x6120,0x60F2,0x6111,/* 0xA0-0xA7 */
+ 0x6137,0x6130,0x6198,0x6213,0x62A6,0x63F5,0x6460,0x649D,/* 0xA8-0xAF */
+ 0x64CE,0x654E,0x6600,0x6615,0x663B,0x6609,0x662E,0x661E,/* 0xB0-0xB7 */
+ 0x6624,0x6665,0x6657,0x6659,0xFA12,0x6673,0x6699,0x66A0,/* 0xB8-0xBF */
+ 0x66B2,0x66BF,0x66FA,0x670E,0xF929,0x6766,0x67BB,0x6852,/* 0xC0-0xC7 */
+ 0x67C0,0x6801,0x6844,0x68CF,0xFA13,0x6968,0xFA14,0x6998,/* 0xC8-0xCF */
+ 0x69E2,0x6A30,0x6A6B,0x6A46,0x6A73,0x6A7E,0x6AE2,0x6AE4,/* 0xD0-0xD7 */
+ 0x6BD6,0x6C3F,0x6C5C,0x6C86,0x6C6F,0x6CDA,0x6D04,0x6D87,/* 0xD8-0xDF */
+ 0x6D6F,0x6D96,0x6DAC,0x6DCF,0x6DF8,0x6DF2,0x6DFC,0x6E39,/* 0xE0-0xE7 */
+ 0x6E5C,0x6E27,0x6E3C,0x6EBF,0x6F88,0x6FB5,0x6FF5,0x7005,/* 0xE8-0xEF */
+ 0x7007,0x7028,0x7085,0x70AB,0x710F,0x7104,0x715C,0x7146,/* 0xF0-0xF7 */
+ 0x7147,0xFA15,0x71C1,0x71FE,0x72B1,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_EE[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x72BE,0x7324,0xFA16,0x7377,0x73BD,0x73C9,0x73D6,0x73E3,/* 0x40-0x47 */
+ 0x73D2,0x7407,0x73F5,0x7426,0x742A,0x7429,0x742E,0x7462,/* 0x48-0x4F */
+ 0x7489,0x749F,0x7501,0x756F,0x7682,0x769C,0x769E,0x769B,/* 0x50-0x57 */
+ 0x76A6,0xFA17,0x7746,0x52AF,0x7821,0x784E,0x7864,0x787A,/* 0x58-0x5F */
+ 0x7930,0xFA18,0xFA19,0xFA1A,0x7994,0xFA1B,0x799B,0x7AD1,/* 0x60-0x67 */
+ 0x7AE7,0xFA1C,0x7AEB,0x7B9E,0xFA1D,0x7D48,0x7D5C,0x7DB7,/* 0x68-0x6F */
+ 0x7DA0,0x7DD6,0x7E52,0x7F47,0x7FA1,0xFA1E,0x8301,0x8362,/* 0x70-0x77 */
+ 0x837F,0x83C7,0x83F6,0x8448,0x84B4,0x8553,0x8559,0x0000,/* 0x78-0x7F */
+
+ 0x856B,0xFA1F,0x85B0,0xFA20,0xFA21,0x8807,0x88F5,0x8A12,/* 0x80-0x87 */
+ 0x8A37,0x8A79,0x8AA7,0x8ABE,0x8ADF,0xFA22,0x8AF6,0x8B53,/* 0x88-0x8F */
+ 0x8B7F,0x8CF0,0x8CF4,0x8D12,0x8D76,0xFA23,0x8ECF,0xFA24,/* 0x90-0x97 */
+ 0xFA25,0x9067,0x90DE,0xFA26,0x9115,0x9127,0x91DA,0x91D7,/* 0x98-0x9F */
+ 0x91DE,0x91ED,0x91EE,0x91E4,0x91E5,0x9206,0x9210,0x920A,/* 0xA0-0xA7 */
+ 0x923A,0x9240,0x923C,0x924E,0x9259,0x9251,0x9239,0x9267,/* 0xA8-0xAF */
+ 0x92A7,0x9277,0x9278,0x92E7,0x92D7,0x92D9,0x92D0,0xFA27,/* 0xB0-0xB7 */
+ 0x92D5,0x92E0,0x92D3,0x9325,0x9321,0x92FB,0xFA28,0x931E,/* 0xB8-0xBF */
+ 0x92FF,0x931D,0x9302,0x9370,0x9357,0x93A4,0x93C6,0x93DE,/* 0xC0-0xC7 */
+ 0x93F8,0x9431,0x9445,0x9448,0x9592,0xF9DC,0xFA29,0x969D,/* 0xC8-0xCF */
+ 0x96AF,0x9733,0x973B,0x9743,0x974D,0x974F,0x9751,0x9755,/* 0xD0-0xD7 */
+ 0x9857,0x9865,0xFA2A,0xFA2B,0x9927,0xFA2C,0x999E,0x9A4E,/* 0xD8-0xDF */
+ 0x9AD9,0x9ADC,0x9B75,0x9B72,0x9B8F,0x9BB1,0x9BBB,0x9C00,/* 0xE0-0xE7 */
+ 0x9D70,0x9D6B,0xFA2D,0x9E19,0x9ED1,0x0000,0x0000,0x2170,/* 0xE8-0xEF */
+ 0x2171,0x2172,0x2173,0x2174,0x2175,0x2176,0x2177,0x2178,/* 0xF0-0xF7 */
+ 0x2179,0xFFE2,0xFFE4,0xFF07,0xFF02,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_FA[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x2170,0x2171,0x2172,0x2173,0x2174,0x2175,0x2176,0x2177,/* 0x40-0x47 */
+ 0x2178,0x2179,0x2160,0x2161,0x2162,0x2163,0x2164,0x2165,/* 0x48-0x4F */
+ 0x2166,0x2167,0x2168,0x2169,0x0000,0xFFE4,0xFF07,0xFF02,/* 0x50-0x57 */
+ 0x3231,0x2116,0x2121,0x2235,0x7E8A,0x891C,0x9348,0x9288,/* 0x58-0x5F */
+ 0x84DC,0x4FC9,0x70BB,0x6631,0x68C8,0x92F9,0x66FB,0x5F45,/* 0x60-0x67 */
+ 0x4E28,0x4EE1,0x4EFC,0x4F00,0x4F03,0x4F39,0x4F56,0x4F92,/* 0x68-0x6F */
+ 0x4F8A,0x4F9A,0x4F94,0x4FCD,0x5040,0x5022,0x4FFF,0x501E,/* 0x70-0x77 */
+ 0x5046,0x5070,0x5042,0x5094,0x50F4,0x50D8,0x514A,0x0000,/* 0x78-0x7F */
+
+ 0x5164,0x519D,0x51BE,0x51EC,0x5215,0x529C,0x52A6,0x52C0,/* 0x80-0x87 */
+ 0x52DB,0x5300,0x5307,0x5324,0x5372,0x5393,0x53B2,0x53DD,/* 0x88-0x8F */
+ 0xFA0E,0x549C,0x548A,0x54A9,0x54FF,0x5586,0x5759,0x5765,/* 0x90-0x97 */
+ 0x57AC,0x57C8,0x57C7,0xFA0F,0xFA10,0x589E,0x58B2,0x590B,/* 0x98-0x9F */
+ 0x5953,0x595B,0x595D,0x5963,0x59A4,0x59BA,0x5B56,0x5BC0,/* 0xA0-0xA7 */
+ 0x752F,0x5BD8,0x5BEC,0x5C1E,0x5CA6,0x5CBA,0x5CF5,0x5D27,/* 0xA8-0xAF */
+ 0x5D53,0xFA11,0x5D42,0x5D6D,0x5DB8,0x5DB9,0x5DD0,0x5F21,/* 0xB0-0xB7 */
+ 0x5F34,0x5F67,0x5FB7,0x5FDE,0x605D,0x6085,0x608A,0x60DE,/* 0xB8-0xBF */
+ 0x60D5,0x6120,0x60F2,0x6111,0x6137,0x6130,0x6198,0x6213,/* 0xC0-0xC7 */
+ 0x62A6,0x63F5,0x6460,0x649D,0x64CE,0x654E,0x6600,0x6615,/* 0xC8-0xCF */
+ 0x663B,0x6609,0x662E,0x661E,0x6624,0x6665,0x6657,0x6659,/* 0xD0-0xD7 */
+ 0xFA12,0x6673,0x6699,0x66A0,0x66B2,0x66BF,0x66FA,0x670E,/* 0xD8-0xDF */
+ 0xF929,0x6766,0x67BB,0x6852,0x67C0,0x6801,0x6844,0x68CF,/* 0xE0-0xE7 */
+ 0xFA13,0x6968,0xFA14,0x6998,0x69E2,0x6A30,0x6A6B,0x6A46,/* 0xE8-0xEF */
+ 0x6A73,0x6A7E,0x6AE2,0x6AE4,0x6BD6,0x6C3F,0x6C5C,0x6C86,/* 0xF0-0xF7 */
+ 0x6C6F,0x6CDA,0x6D04,0x6D87,0x6D6F,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_FB[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x6D96,0x6DAC,0x6DCF,0x6DF8,0x6DF2,0x6DFC,0x6E39,0x6E5C,/* 0x40-0x47 */
+ 0x6E27,0x6E3C,0x6EBF,0x6F88,0x6FB5,0x6FF5,0x7005,0x7007,/* 0x48-0x4F */
+ 0x7028,0x7085,0x70AB,0x710F,0x7104,0x715C,0x7146,0x7147,/* 0x50-0x57 */
+ 0xFA15,0x71C1,0x71FE,0x72B1,0x72BE,0x7324,0xFA16,0x7377,/* 0x58-0x5F */
+ 0x73BD,0x73C9,0x73D6,0x73E3,0x73D2,0x7407,0x73F5,0x7426,/* 0x60-0x67 */
+ 0x742A,0x7429,0x742E,0x7462,0x7489,0x749F,0x7501,0x756F,/* 0x68-0x6F */
+ 0x7682,0x769C,0x769E,0x769B,0x76A6,0xFA17,0x7746,0x52AF,/* 0x70-0x77 */
+ 0x7821,0x784E,0x7864,0x787A,0x7930,0xFA18,0xFA19,0x0000,/* 0x78-0x7F */
+
+ 0xFA1A,0x7994,0xFA1B,0x799B,0x7AD1,0x7AE7,0xFA1C,0x7AEB,/* 0x80-0x87 */
+ 0x7B9E,0xFA1D,0x7D48,0x7D5C,0x7DB7,0x7DA0,0x7DD6,0x7E52,/* 0x88-0x8F */
+ 0x7F47,0x7FA1,0xFA1E,0x8301,0x8362,0x837F,0x83C7,0x83F6,/* 0x90-0x97 */
+ 0x8448,0x84B4,0x8553,0x8559,0x856B,0xFA1F,0x85B0,0xFA20,/* 0x98-0x9F */
+ 0xFA21,0x8807,0x88F5,0x8A12,0x8A37,0x8A79,0x8AA7,0x8ABE,/* 0xA0-0xA7 */
+ 0x8ADF,0xFA22,0x8AF6,0x8B53,0x8B7F,0x8CF0,0x8CF4,0x8D12,/* 0xA8-0xAF */
+ 0x8D76,0xFA23,0x8ECF,0xFA24,0xFA25,0x9067,0x90DE,0xFA26,/* 0xB0-0xB7 */
+ 0x9115,0x9127,0x91DA,0x91D7,0x91DE,0x91ED,0x91EE,0x91E4,/* 0xB8-0xBF */
+ 0x91E5,0x9206,0x9210,0x920A,0x923A,0x9240,0x923C,0x924E,/* 0xC0-0xC7 */
+ 0x9259,0x9251,0x9239,0x9267,0x92A7,0x9277,0x9278,0x92E7,/* 0xC8-0xCF */
+ 0x92D7,0x92D9,0x92D0,0xFA27,0x92D5,0x92E0,0x92D3,0x9325,/* 0xD0-0xD7 */
+ 0x9321,0x92FB,0xFA28,0x931E,0x92FF,0x931D,0x9302,0x9370,/* 0xD8-0xDF */
+ 0x9357,0x93A4,0x93C6,0x93DE,0x93F8,0x9431,0x9445,0x9448,/* 0xE0-0xE7 */
+ 0x9592,0xF9DC,0xFA29,0x969D,0x96AF,0x9733,0x973B,0x9743,/* 0xE8-0xEF */
+ 0x974D,0x974F,0x9751,0x9755,0x9857,0x9865,0xFA2A,0xFA2B,/* 0xF0-0xF7 */
+ 0x9927,0xFA2C,0x999E,0x9A4E,0x9AD9,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_FC[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x9ADC,0x9B75,0x9B72,0x9B8F,0x9BB1,0x9BBB,0x9C00,0x9D70,/* 0x40-0x47 */
+ 0x9D6B,0xFA2D,0x9E19,0x9ED1,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+};
+
+static wchar_t *page_charset2uni[256] = {
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, c2u_81, c2u_82, c2u_83, c2u_84, NULL, NULL, c2u_87,
+ c2u_88, c2u_89, c2u_8A, c2u_8B, c2u_8C, c2u_8D, c2u_8E, c2u_8F,
+ c2u_90, c2u_91, c2u_92, c2u_93, c2u_94, c2u_95, c2u_96, c2u_97,
+ c2u_98, c2u_99, c2u_9A, c2u_9B, c2u_9C, c2u_9D, c2u_9E, c2u_9F,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ c2u_E0, c2u_E1, c2u_E2, c2u_E3, c2u_E4, c2u_E5, c2u_E6, c2u_E7,
+ c2u_E8, c2u_E9, c2u_EA, NULL, NULL, c2u_ED, c2u_EE, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, c2u_FA, c2u_FB, c2u_FC, NULL, NULL, NULL,
+};
+
+static unsigned char u2c_03[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x83, 0x9F, 0x83, 0xA0, 0x83, 0xA1, /* 0x90-0x93 */
+ 0x83, 0xA2, 0x83, 0xA3, 0x83, 0xA4, 0x83, 0xA5, /* 0x94-0x97 */
+ 0x83, 0xA6, 0x83, 0xA7, 0x83, 0xA8, 0x83, 0xA9, /* 0x98-0x9B */
+ 0x83, 0xAA, 0x83, 0xAB, 0x83, 0xAC, 0x83, 0xAD, /* 0x9C-0x9F */
+ 0x83, 0xAE, 0x83, 0xAF, 0x00, 0x00, 0x83, 0xB0, /* 0xA0-0xA3 */
+ 0x83, 0xB1, 0x83, 0xB2, 0x83, 0xB3, 0x83, 0xB4, /* 0xA4-0xA7 */
+ 0x83, 0xB5, 0x83, 0xB6, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x83, 0xBF, 0x83, 0xC0, 0x83, 0xC1, /* 0xB0-0xB3 */
+ 0x83, 0xC2, 0x83, 0xC3, 0x83, 0xC4, 0x83, 0xC5, /* 0xB4-0xB7 */
+ 0x83, 0xC6, 0x83, 0xC7, 0x83, 0xC8, 0x83, 0xC9, /* 0xB8-0xBB */
+ 0x83, 0xCA, 0x83, 0xCB, 0x83, 0xCC, 0x83, 0xCD, /* 0xBC-0xBF */
+ 0x83, 0xCE, 0x83, 0xCF, 0x00, 0x00, 0x83, 0xD0, /* 0xC0-0xC3 */
+ 0x83, 0xD1, 0x83, 0xD2, 0x83, 0xD3, 0x83, 0xD4, /* 0xC4-0xC7 */
+ 0x83, 0xD5, 0x83, 0xD6, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+};
+
+static unsigned char u2c_04[512] = {
+ 0x00, 0x00, 0x84, 0x46, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x84, 0x40, 0x84, 0x41, 0x84, 0x42, 0x84, 0x43, /* 0x10-0x13 */
+ 0x84, 0x44, 0x84, 0x45, 0x84, 0x47, 0x84, 0x48, /* 0x14-0x17 */
+ 0x84, 0x49, 0x84, 0x4A, 0x84, 0x4B, 0x84, 0x4C, /* 0x18-0x1B */
+ 0x84, 0x4D, 0x84, 0x4E, 0x84, 0x4F, 0x84, 0x50, /* 0x1C-0x1F */
+ 0x84, 0x51, 0x84, 0x52, 0x84, 0x53, 0x84, 0x54, /* 0x20-0x23 */
+ 0x84, 0x55, 0x84, 0x56, 0x84, 0x57, 0x84, 0x58, /* 0x24-0x27 */
+ 0x84, 0x59, 0x84, 0x5A, 0x84, 0x5B, 0x84, 0x5C, /* 0x28-0x2B */
+ 0x84, 0x5D, 0x84, 0x5E, 0x84, 0x5F, 0x84, 0x60, /* 0x2C-0x2F */
+ 0x84, 0x70, 0x84, 0x71, 0x84, 0x72, 0x84, 0x73, /* 0x30-0x33 */
+ 0x84, 0x74, 0x84, 0x75, 0x84, 0x77, 0x84, 0x78, /* 0x34-0x37 */
+ 0x84, 0x79, 0x84, 0x7A, 0x84, 0x7B, 0x84, 0x7C, /* 0x38-0x3B */
+ 0x84, 0x7D, 0x84, 0x7E, 0x84, 0x80, 0x84, 0x81, /* 0x3C-0x3F */
+ 0x84, 0x82, 0x84, 0x83, 0x84, 0x84, 0x84, 0x85, /* 0x40-0x43 */
+ 0x84, 0x86, 0x84, 0x87, 0x84, 0x88, 0x84, 0x89, /* 0x44-0x47 */
+ 0x84, 0x8A, 0x84, 0x8B, 0x84, 0x8C, 0x84, 0x8D, /* 0x48-0x4B */
+ 0x84, 0x8E, 0x84, 0x8F, 0x84, 0x90, 0x84, 0x91, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x84, 0x76, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+};
+
+static unsigned char u2c_20[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x81, 0x5D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x81, 0x5C, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x81, 0x65, 0x81, 0x66, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x81, 0x67, 0x81, 0x68, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x81, 0xF5, 0x81, 0xF6, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x81, 0x64, 0x81, 0x63, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x81, 0xF1, 0x00, 0x00, 0x81, 0x8C, 0x81, 0x8D, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0xA6, /* 0x38-0x3B */
+};
+
+static unsigned char u2c_21[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x8E, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0xFA, 0x59, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0xFA, 0x5A, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0xF0, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xFA, 0x4A, 0xFA, 0x4B, 0xFA, 0x4C, 0xFA, 0x4D, /* 0x60-0x63 */
+ 0xFA, 0x4E, 0xFA, 0x4F, 0xFA, 0x50, 0xFA, 0x51, /* 0x64-0x67 */
+ 0xFA, 0x52, 0xFA, 0x53, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xEE, 0xEF, 0xEE, 0xF0, 0xEE, 0xF1, 0xEE, 0xF2, /* 0x70-0x73 */
+ 0xEE, 0xF3, 0xEE, 0xF4, 0xEE, 0xF5, 0xEE, 0xF6, /* 0x74-0x77 */
+ 0xEE, 0xF7, 0xEE, 0xF8, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x81, 0xA9, 0x81, 0xAA, 0x81, 0xA8, 0x81, 0xAB, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x81, 0xCB, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x81, 0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+};
+
+static unsigned char u2c_22[512] = {
+ 0x81, 0xCD, 0x00, 0x00, 0x81, 0xDD, 0x81, 0xCE, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0xDE, /* 0x04-0x07 */
+ 0x81, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x81, 0xB9, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x87, 0x94, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x87, 0x95, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x81, 0xE5, 0x81, 0x87, 0x87, 0x98, /* 0x1C-0x1F */
+ 0x87, 0x97, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x81, 0x61, 0x00, 0x00, 0x81, 0xC8, /* 0x24-0x27 */
+ 0x81, 0xC9, 0x87, 0x9B, 0x87, 0x9C, 0x87, 0x92, /* 0x28-0x2B */
+ 0x81, 0xE8, 0x00, 0x00, 0x87, 0x93, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x81, 0x88, 0xFA, 0x5B, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x81, 0xE4, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x87, 0x90, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x81, 0x82, 0x87, 0x91, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x81, 0x85, 0x81, 0x86, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x81, 0xE1, 0x81, 0xE2, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x81, 0xBC, 0x81, 0xBD, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x81, 0xBA, 0x81, 0xBB, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x87, 0x96, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x99, /* 0xBC-0xBF */
+};
+
+static unsigned char u2c_23[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x81, 0xDC, 0x00, 0x00, /* 0x10-0x13 */
+};
+
+static unsigned char u2c_24[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x87, 0x40, 0x87, 0x41, 0x87, 0x42, 0x87, 0x43, /* 0x60-0x63 */
+ 0x87, 0x44, 0x87, 0x45, 0x87, 0x46, 0x87, 0x47, /* 0x64-0x67 */
+ 0x87, 0x48, 0x87, 0x49, 0x87, 0x4A, 0x87, 0x4B, /* 0x68-0x6B */
+ 0x87, 0x4C, 0x87, 0x4D, 0x87, 0x4E, 0x87, 0x4F, /* 0x6C-0x6F */
+ 0x87, 0x50, 0x87, 0x51, 0x87, 0x52, 0x87, 0x53, /* 0x70-0x73 */
+};
+
+static unsigned char u2c_25[512] = {
+ 0x84, 0x9F, 0x84, 0xAA, 0x84, 0xA0, 0x84, 0xAB, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x84, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x84, 0xAC, /* 0x0C-0x0F */
+ 0x84, 0xA2, 0x00, 0x00, 0x00, 0x00, 0x84, 0xAD, /* 0x10-0x13 */
+ 0x84, 0xA4, 0x00, 0x00, 0x00, 0x00, 0x84, 0xAF, /* 0x14-0x17 */
+ 0x84, 0xA3, 0x00, 0x00, 0x00, 0x00, 0x84, 0xAE, /* 0x18-0x1B */
+ 0x84, 0xA5, 0x84, 0xBA, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x84, 0xB5, 0x00, 0x00, 0x00, 0x00, 0x84, 0xB0, /* 0x20-0x23 */
+ 0x84, 0xA7, 0x84, 0xBC, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x84, 0xB7, 0x00, 0x00, 0x00, 0x00, 0x84, 0xB2, /* 0x28-0x2B */
+ 0x84, 0xA6, 0x00, 0x00, 0x00, 0x00, 0x84, 0xB6, /* 0x2C-0x2F */
+ 0x84, 0xBB, 0x00, 0x00, 0x00, 0x00, 0x84, 0xB1, /* 0x30-0x33 */
+ 0x84, 0xA8, 0x00, 0x00, 0x00, 0x00, 0x84, 0xB8, /* 0x34-0x37 */
+ 0x84, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x84, 0xB3, /* 0x38-0x3B */
+ 0x84, 0xA9, 0x00, 0x00, 0x00, 0x00, 0x84, 0xB9, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x84, 0xBE, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0xB4, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x81, 0xA1, 0x81, 0xA0, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x81, 0xA3, 0x81, 0xA2, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x81, 0xA5, 0x81, 0xA4, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x81, 0x9F, 0x81, 0x9E, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x9B, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x81, 0x9D, 0x81, 0x9C, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0xFC, /* 0xEC-0xEF */
+};
+
+static unsigned char u2c_26[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x81, 0x9A, 0x81, 0x99, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x81, 0x8A, 0x00, 0x00, 0x81, 0x89, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x81, 0xF4, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x81, 0xF3, 0x00, 0x00, 0x81, 0xF2, /* 0x6C-0x6F */
+};
+
+static unsigned char u2c_30[512] = {
+ 0x81, 0x40, 0x81, 0x41, 0x81, 0x42, 0x81, 0x56, /* 0x00-0x03 */
+ 0x00, 0x00, 0x81, 0x58, 0x81, 0x59, 0x81, 0x5A, /* 0x04-0x07 */
+ 0x81, 0x71, 0x81, 0x72, 0x81, 0x73, 0x81, 0x74, /* 0x08-0x0B */
+ 0x81, 0x75, 0x81, 0x76, 0x81, 0x77, 0x81, 0x78, /* 0x0C-0x0F */
+ 0x81, 0x79, 0x81, 0x7A, 0x81, 0xA7, 0x81, 0xAC, /* 0x10-0x13 */
+ 0x81, 0x6B, 0x81, 0x6C, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x87, 0x80, 0x00, 0x00, 0x87, 0x81, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x82, 0x9F, 0x82, 0xA0, 0x82, 0xA1, /* 0x40-0x43 */
+ 0x82, 0xA2, 0x82, 0xA3, 0x82, 0xA4, 0x82, 0xA5, /* 0x44-0x47 */
+ 0x82, 0xA6, 0x82, 0xA7, 0x82, 0xA8, 0x82, 0xA9, /* 0x48-0x4B */
+ 0x82, 0xAA, 0x82, 0xAB, 0x82, 0xAC, 0x82, 0xAD, /* 0x4C-0x4F */
+ 0x82, 0xAE, 0x82, 0xAF, 0x82, 0xB0, 0x82, 0xB1, /* 0x50-0x53 */
+ 0x82, 0xB2, 0x82, 0xB3, 0x82, 0xB4, 0x82, 0xB5, /* 0x54-0x57 */
+ 0x82, 0xB6, 0x82, 0xB7, 0x82, 0xB8, 0x82, 0xB9, /* 0x58-0x5B */
+ 0x82, 0xBA, 0x82, 0xBB, 0x82, 0xBC, 0x82, 0xBD, /* 0x5C-0x5F */
+ 0x82, 0xBE, 0x82, 0xBF, 0x82, 0xC0, 0x82, 0xC1, /* 0x60-0x63 */
+ 0x82, 0xC2, 0x82, 0xC3, 0x82, 0xC4, 0x82, 0xC5, /* 0x64-0x67 */
+ 0x82, 0xC6, 0x82, 0xC7, 0x82, 0xC8, 0x82, 0xC9, /* 0x68-0x6B */
+ 0x82, 0xCA, 0x82, 0xCB, 0x82, 0xCC, 0x82, 0xCD, /* 0x6C-0x6F */
+ 0x82, 0xCE, 0x82, 0xCF, 0x82, 0xD0, 0x82, 0xD1, /* 0x70-0x73 */
+ 0x82, 0xD2, 0x82, 0xD3, 0x82, 0xD4, 0x82, 0xD5, /* 0x74-0x77 */
+ 0x82, 0xD6, 0x82, 0xD7, 0x82, 0xD8, 0x82, 0xD9, /* 0x78-0x7B */
+ 0x82, 0xDA, 0x82, 0xDB, 0x82, 0xDC, 0x82, 0xDD, /* 0x7C-0x7F */
+
+ 0x82, 0xDE, 0x82, 0xDF, 0x82, 0xE0, 0x82, 0xE1, /* 0x80-0x83 */
+ 0x82, 0xE2, 0x82, 0xE3, 0x82, 0xE4, 0x82, 0xE5, /* 0x84-0x87 */
+ 0x82, 0xE6, 0x82, 0xE7, 0x82, 0xE8, 0x82, 0xE9, /* 0x88-0x8B */
+ 0x82, 0xEA, 0x82, 0xEB, 0x82, 0xEC, 0x82, 0xED, /* 0x8C-0x8F */
+ 0x82, 0xEE, 0x82, 0xEF, 0x82, 0xF0, 0x82, 0xF1, /* 0x90-0x93 */
+ 0x83, 0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x4A, /* 0x98-0x9B */
+ 0x81, 0x4B, 0x81, 0x54, 0x81, 0x55, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x83, 0x40, 0x83, 0x41, 0x83, 0x42, /* 0xA0-0xA3 */
+ 0x83, 0x43, 0x83, 0x44, 0x83, 0x45, 0x83, 0x46, /* 0xA4-0xA7 */
+ 0x83, 0x47, 0x83, 0x48, 0x83, 0x49, 0x83, 0x4A, /* 0xA8-0xAB */
+ 0x83, 0x4B, 0x83, 0x4C, 0x83, 0x4D, 0x83, 0x4E, /* 0xAC-0xAF */
+ 0x83, 0x4F, 0x83, 0x50, 0x83, 0x51, 0x83, 0x52, /* 0xB0-0xB3 */
+ 0x83, 0x53, 0x83, 0x54, 0x83, 0x55, 0x83, 0x56, /* 0xB4-0xB7 */
+ 0x83, 0x57, 0x83, 0x58, 0x83, 0x59, 0x83, 0x5A, /* 0xB8-0xBB */
+ 0x83, 0x5B, 0x83, 0x5C, 0x83, 0x5D, 0x83, 0x5E, /* 0xBC-0xBF */
+ 0x83, 0x5F, 0x83, 0x60, 0x83, 0x61, 0x83, 0x62, /* 0xC0-0xC3 */
+ 0x83, 0x63, 0x83, 0x64, 0x83, 0x65, 0x83, 0x66, /* 0xC4-0xC7 */
+ 0x83, 0x67, 0x83, 0x68, 0x83, 0x69, 0x83, 0x6A, /* 0xC8-0xCB */
+ 0x83, 0x6B, 0x83, 0x6C, 0x83, 0x6D, 0x83, 0x6E, /* 0xCC-0xCF */
+ 0x83, 0x6F, 0x83, 0x70, 0x83, 0x71, 0x83, 0x72, /* 0xD0-0xD3 */
+ 0x83, 0x73, 0x83, 0x74, 0x83, 0x75, 0x83, 0x76, /* 0xD4-0xD7 */
+ 0x83, 0x77, 0x83, 0x78, 0x83, 0x79, 0x83, 0x7A, /* 0xD8-0xDB */
+ 0x83, 0x7B, 0x83, 0x7C, 0x83, 0x7D, 0x83, 0x7E, /* 0xDC-0xDF */
+ 0x83, 0x80, 0x83, 0x81, 0x83, 0x82, 0x83, 0x83, /* 0xE0-0xE3 */
+ 0x83, 0x84, 0x83, 0x85, 0x83, 0x86, 0x83, 0x87, /* 0xE4-0xE7 */
+ 0x83, 0x88, 0x83, 0x89, 0x83, 0x8A, 0x83, 0x8B, /* 0xE8-0xEB */
+ 0x83, 0x8C, 0x83, 0x8D, 0x83, 0x8E, 0x83, 0x8F, /* 0xEC-0xEF */
+ 0x83, 0x90, 0x83, 0x91, 0x83, 0x92, 0x83, 0x93, /* 0xF0-0xF3 */
+ 0x83, 0x94, 0x83, 0x95, 0x83, 0x96, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x45, /* 0xF8-0xFB */
+ 0x81, 0x5B, 0x81, 0x52, 0x81, 0x53, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_32[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0xFA, 0x58, 0x87, 0x8B, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x87, 0x8C, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x87, 0x85, 0x87, 0x86, 0x87, 0x87, 0x87, 0x88, /* 0xA4-0xA7 */
+ 0x87, 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+};
+
+static unsigned char u2c_33[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x65, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x87, 0x69, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x87, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x87, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x87, 0x61, 0x87, 0x6B, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x87, 0x6A, 0x87, 0x64, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x6C, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x87, 0x66, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x6E, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x87, 0x5F, 0x87, 0x6D, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x87, 0x62, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x87, 0x67, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x68, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x7E, /* 0x78-0x7B */
+ 0x87, 0x8F, 0x87, 0x8E, 0x87, 0x8D, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x87, 0x72, 0x87, 0x73, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x87, 0x6F, 0x87, 0x70, 0x87, 0x71, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x87, 0x75, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x87, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x87, 0x83, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+};
+
+static unsigned char u2c_4E[512] = {
+ 0x88, 0xEA, 0x92, 0x9A, 0x00, 0x00, 0x8E, 0xB5, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x9C, /* 0x04-0x07 */
+ 0x8F, 0xE4, 0x8E, 0x4F, 0x8F, 0xE3, 0x89, 0xBA, /* 0x08-0x0B */
+ 0x00, 0x00, 0x95, 0x73, 0x97, 0x5E, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x98, 0xA0, 0x89, 0x4E, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x8A, 0x8E, 0x98, 0xA1, 0x90, 0xA2, 0x99, 0xC0, /* 0x14-0x17 */
+ 0x8B, 0x75, 0x95, 0xB8, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x8F, 0xE5, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x97, 0xBC, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x95, 0xC0, 0x00, 0x00, /* 0x24-0x27 */
+ 0xED, 0x4C, 0x00, 0x00, 0x98, 0xA2, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x92, 0x86, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x98, 0xA3, 0x8B, 0xF8, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x98, 0xA4, 0x00, 0x00, /* 0x34-0x37 */
+ 0x8A, 0xDB, 0x92, 0x4F, 0x00, 0x00, 0x8E, 0xE5, /* 0x38-0x3B */
+ 0x98, 0xA5, 0x00, 0x00, 0x00, 0x00, 0x98, 0xA6, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x98, 0xA7, 0x94, 0x54, /* 0x40-0x43 */
+ 0x00, 0x00, 0x8B, 0x76, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x56, /* 0x48-0x4B */
+ 0x00, 0x00, 0x93, 0xE1, 0x8C, 0xC1, 0x96, 0x52, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0xE5, 0x68, 0x98, 0xA8, 0x8F, 0xE6, /* 0x54-0x57 */
+ 0x98, 0xA9, 0x89, 0xB3, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x8B, 0xE3, 0x8C, 0xEE, 0x96, 0xE7, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x9B, 0xA4, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x97, 0x90, 0x00, 0x00, 0x93, 0xFB, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x8A, 0xA3, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x8B, 0x54, 0x00, 0x00, 0x98, 0xAA, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x98, 0xAB, 0x97, 0xB9, 0x00, 0x00, /* 0x84-0x87 */
+ 0x97, 0x5C, 0x91, 0x88, 0x98, 0xAD, 0x8E, 0x96, /* 0x88-0x8B */
+ 0x93, 0xF1, 0x00, 0x00, 0x98, 0xB0, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x89, 0x5D, 0x8C, 0xDD, 0x00, 0x00, /* 0x90-0x93 */
+ 0x8C, 0xDC, 0x88, 0xE4, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x98, 0x6A, 0x98, 0x69, 0x00, 0x00, 0x8D, 0xB1, /* 0x98-0x9B */
+ 0x88, 0x9F, 0x00, 0x00, 0x98, 0xB1, 0x98, 0xB2, /* 0x9C-0x9F */
+ 0x98, 0xB3, 0x96, 0x53, 0x98, 0xB4, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x8C, 0xF0, 0x88, 0xE5, 0x96, 0x92, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x8B, 0x9C, 0x00, 0x00, 0x00, 0x00, 0x8B, 0x9D, /* 0xA8-0xAB */
+ 0x8B, 0x9E, 0x92, 0xE0, 0x97, 0xBA, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x98, 0xB5, 0x00, 0x00, 0x00, 0x00, 0x98, 0xB6, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x98, 0xB7, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x90, 0x6C, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x8F, 0x59, 0x90, 0x6D, 0x98, 0xBC, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x98, 0xBA, 0x00, 0x00, 0x98, 0xBB, 0x8B, 0x77, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x8D, 0xA1, 0x89, 0xEE, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x98, 0xB9, 0x98, 0xB8, 0x95, 0xA7, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x8E, 0x65, 0x8E, 0x64, 0x91, 0xBC, 0x98, 0xBD, /* 0xD4-0xD7 */
+ 0x95, 0x74, 0x90, 0xE5, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x81, 0x57, 0x98, 0xBE, 0x98, 0xC0, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xED, 0x4D, 0x00, 0x00, 0x91, 0xE3, /* 0xE0-0xE3 */
+ 0x97, 0xDF, 0x88, 0xC8, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x98, 0xBF, 0x89, 0xBC, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x8B, 0xC2, 0x00, 0x00, 0x92, 0x87, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x8C, 0x8F, 0x98, 0xC1, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x43, /* 0xF8-0xFB */
+ 0xED, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_4F[512] = {
+ 0xED, 0x4F, 0x8A, 0xE9, 0x00, 0x00, 0xED, 0x50, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x98, 0xC2, 0x88, 0xC9, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x8C, 0xDE, 0x8A, 0xEA, 0x95, 0x9A, /* 0x0C-0x0F */
+ 0x94, 0xB0, 0x8B, 0x78, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x89, 0xEF, 0x00, 0x00, /* 0x18-0x1B */
+ 0x98, 0xE5, 0x93, 0x60, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x8C, /* 0x2C-0x2F */
+ 0x98, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x94, 0xBA, 0x00, 0x00, 0x97, 0xE0, 0x00, 0x00, /* 0x34-0x37 */
+ 0x90, 0x4C, 0xED, 0x51, 0x8E, 0x66, 0x00, 0x00, /* 0x38-0x3B */
+ 0x8E, 0x97, 0x89, 0xBE, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0xCF, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x92, 0x41, 0x98, 0xC8, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x88, 0xCA, 0x92, 0xE1, 0x8F, 0x5A, /* 0x4C-0x4F */
+ 0x8D, 0xB2, 0x97, 0x43, 0x00, 0x00, 0x91, 0xCC, /* 0x50-0x53 */
+ 0x00, 0x00, 0x89, 0xBD, 0xED, 0x52, 0x98, 0xC7, /* 0x54-0x57 */
+ 0x00, 0x00, 0x97, 0x5D, 0x98, 0xC3, 0x98, 0xC5, /* 0x58-0x5B */
+ 0x8D, 0xEC, 0x98, 0xC6, 0x9B, 0x43, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x98, 0xCE, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0xD1, /* 0x6C-0x6F */
+ 0x98, 0xCF, 0x00, 0x00, 0x00, 0x00, 0x89, 0xC0, /* 0x70-0x73 */
+ 0x00, 0x00, 0x95, 0xB9, 0x98, 0xC9, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0xCD, /* 0x78-0x7B */
+ 0x8C, 0xF1, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x67, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8A, 0xA4, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x98, 0xD2, 0x00, 0x00, /* 0x84-0x87 */
+ 0x98, 0xCA, 0x00, 0x00, 0xED, 0x54, 0x97, 0xE1, /* 0x88-0x8B */
+ 0x00, 0x00, 0x8E, 0x98, 0x00, 0x00, 0x98, 0xCB, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x98, 0xD0, 0xED, 0x53, 0x00, 0x00, /* 0x90-0x93 */
+ 0xED, 0x56, 0x00, 0x00, 0x98, 0xD3, 0x00, 0x00, /* 0x94-0x97 */
+ 0x98, 0xCC, 0x00, 0x00, 0xED, 0x55, 0x8B, 0x9F, /* 0x98-0x9B */
+ 0x00, 0x00, 0x88, 0xCB, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x8B, 0xA0, 0x89, 0xBF, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9B, 0x44, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x96, 0x99, 0x95, 0x8E, 0x8C, 0xF2, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x90, 0x4E, 0x97, 0xB5, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0xD6, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x8C, 0x57, 0x91, 0xA3, /* 0xC0-0xC3 */
+ 0x89, 0xE2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xED, 0x45, 0x8F, 0x72, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0xED, 0x57, 0x98, 0xD7, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x98, 0xDC, 0x98, 0xDA, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x98, 0xD5, 0x00, 0x00, 0x00, 0x00, 0x91, 0xAD, /* 0xD4-0xD7 */
+ 0x98, 0xD8, 0x00, 0x00, 0x98, 0xDB, 0x98, 0xD9, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x95, 0xDB, 0x00, 0x00, 0x98, 0xD6, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x90, 0x4D, 0x00, 0x00, 0x96, 0x93, /* 0xE0-0xE3 */
+ 0x98, 0xDD, 0x98, 0xDE, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x8F, 0x43, 0x98, 0xEB, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x6F, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x95, 0x55, 0x98, 0xE6, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x95, 0xEE, 0x00, 0x00, 0x89, 0xB4, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0x98, 0xEA, 0xED, 0x5A, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_50[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x98, 0xE4, 0x98, 0xED, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x91, 0x71, 0x00, 0x00, 0x8C, 0xC2, /* 0x08-0x0B */
+ 0x00, 0x00, 0x94, 0x7B, 0x00, 0x00, 0xE0, 0xC5, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x98, 0xEC, 0x93, 0x7C, 0x00, 0x00, /* 0x10-0x13 */
+ 0x98, 0xE1, 0x00, 0x00, 0x8C, 0xF4, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x8C, 0xF3, 0x98, 0xDF, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0xED, 0x5B, 0x8E, 0xD8, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x98, 0xE7, 0xED, 0x59, 0x95, 0xED, /* 0x20-0x23 */
+ 0x92, 0x6C, 0x98, 0xE3, 0x8C, 0x91, 0x00, 0x00, /* 0x24-0x27 */
+ 0x98, 0xE0, 0x98, 0xE8, 0x98, 0xE2, 0x97, 0xCF, /* 0x28-0x2B */
+ 0x98, 0xE9, 0x98, 0x60, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x8B, 0xE4, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x8C, 0x90, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0xED, 0x58, 0x00, 0x00, 0xED, 0x5E, 0x98, 0xEE, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0xED, 0x5C, 0x98, 0xEF, /* 0x44-0x47 */
+ 0x98, 0xF3, 0x88, 0xCC, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0xCE, /* 0x4C-0x4F */
+ 0x98, 0xF2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x98, 0xF1, 0x98, 0xF5, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x98, 0xF4, 0x00, 0x00, /* 0x58-0x5B */
+ 0x92, 0xE2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x8C, 0x92, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x98, 0xF6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xED, 0x5D, 0x00, 0x00, 0x8E, 0xC3, 0x00, 0x00, /* 0x70-0x73 */
+ 0x91, 0xA4, 0x92, 0xE3, 0x8B, 0xF4, 0x00, 0x00, /* 0x74-0x77 */
+ 0x98, 0xF7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x8B, 0x55, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x98, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x98, 0xFA, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x96, 0x54, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x8C, 0x86, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0xED, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x8E, 0x50, 0x94, 0xF5, 0x98, 0xF9, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x8D, 0xC3, 0x97, 0x62, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x98, 0xFC, 0x99, 0x42, /* 0xB0-0xB3 */
+ 0x98, 0xFB, 0x8D, 0xC2, 0x00, 0x00, 0x8F, 0x9D, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x8C, 0x58, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x99, 0x43, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x8B, 0xCD, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x99, 0x40, 0x99, 0x41, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x93, 0xAD, 0x00, 0x00, 0x91, 0x9C, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x8B, 0xA1, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x96, 0x6C, 0x99, 0x44, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xED, 0x61, 0x00, 0x00, 0x97, 0xBB, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x99, 0x45, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0x48, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x99, 0x46, 0x00, 0x00, 0x91, 0x6D, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x99, 0x47, 0x99, 0x49, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xED, 0x60, 0x99, 0x4B, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x99, 0x4A, 0x00, 0x00, 0x95, 0xC6, /* 0xF8-0xFB */
+};
+
+static unsigned char u2c_51[512] = {
+ 0x8B, 0x56, 0x99, 0x4D, 0x99, 0x4E, 0x00, 0x00, /* 0x00-0x03 */
+ 0x89, 0xAD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x99, 0x4C, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x8E, 0xF2, 0x00, 0x00, /* 0x10-0x13 */
+ 0x99, 0x51, 0x99, 0x50, 0x99, 0x4F, 0x00, 0x00, /* 0x14-0x17 */
+ 0x98, 0xD4, 0x00, 0x00, 0x99, 0x52, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8F, 0x9E, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x99, 0x53, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x97, 0x44, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x96, 0xD7, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0x55, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x99, 0x54, 0x99, 0x57, /* 0x38-0x3B */
+ 0x99, 0x56, 0x00, 0x00, 0x00, 0x00, 0x99, 0x58, /* 0x3C-0x3F */
+ 0x99, 0x59, 0x88, 0xF2, 0x00, 0x00, 0x8C, 0xB3, /* 0x40-0x43 */
+ 0x8C, 0x5A, 0x8F, 0x5B, 0x92, 0x9B, 0x8B, 0xA2, /* 0x44-0x47 */
+ 0x90, 0xE6, 0x8C, 0xF5, 0xED, 0x62, 0x8D, 0x8E, /* 0x48-0x4B */
+ 0x99, 0x5B, 0x96, 0xC6, 0x93, 0x65, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x8E, 0x99, 0x00, 0x00, 0x99, 0x5A, 0x00, 0x00, /* 0x50-0x53 */
+ 0x99, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x93, 0x7D, 0x00, 0x00, /* 0x58-0x5B */
+ 0x8A, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x99, 0x5D, 0x00, 0x00, /* 0x60-0x63 */
+ 0xED, 0x63, 0x93, 0xFC, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x91, 0x53, 0x99, 0x5F, 0x99, 0x60, 0x94, 0xAA, /* 0x68-0x6B */
+ 0x8C, 0xF6, 0x98, 0x5A, 0x99, 0x61, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x8B, 0xA4, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x95, 0xBA, 0x91, 0xB4, 0x8B, 0xEF, /* 0x74-0x77 */
+ 0x93, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x8C, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x99, 0x62, 0x00, 0x00, 0x99, 0x63, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x93, 0xE0, 0x89, 0x7E, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x99, 0x66, 0x8D, 0xFB, 0x00, 0x00, /* 0x88-0x8B */
+ 0x99, 0x65, 0x8D, 0xC4, 0x00, 0x00, 0x99, 0x67, /* 0x8C-0x8F */
+ 0xE3, 0xEC, 0x99, 0x68, 0x96, 0x60, 0x99, 0x69, /* 0x90-0x93 */
+ 0x00, 0x00, 0x99, 0x6A, 0x99, 0x6B, 0x8F, 0xE7, /* 0x94-0x97 */
+ 0x00, 0x00, 0x8E, 0xCA, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0xED, 0x64, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x8A, 0xA5, 0x00, 0x00, 0x99, 0x6E, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x99, 0x6C, 0x96, 0xBB, 0x99, 0x6D, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x95, 0x79, 0x99, 0x6F, 0x99, 0x70, 0x99, 0x71, /* 0xA8-0xAB */
+ 0x93, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x99, 0x75, 0x99, 0x73, 0x99, 0x74, 0x99, 0x72, /* 0xB0-0xB3 */
+ 0x8D, 0xE1, 0x99, 0x76, 0x96, 0xE8, 0x97, 0xE2, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x99, 0x77, 0xED, 0x65, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x90, 0xA6, 0x99, 0x78, 0x8F, 0x79, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x99, 0x79, 0x00, 0x00, 0x92, 0x9C, /* 0xC8-0xCB */
+ 0x97, 0xBD, 0x93, 0x80, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x99, 0xC3, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0x7A, /* 0xD8-0xDB */
+ 0xEA, 0xA3, 0x8B, 0xC3, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x99, 0x7B, 0x96, 0x7D, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x8F, 0x88, 0x91, 0xFA, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x99, 0x7D, 0x93, 0xE2, 0x00, 0x00, /* 0xE8-0xEB */
+ 0xED, 0x66, 0x99, 0x7E, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x99, 0x80, 0x8A, 0x4D, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x99, 0x81, 0x8B, 0xA5, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x93, 0xCA, 0x89, 0x9A, 0x8F, 0x6F, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x94, 0x9F, 0x99, 0x82, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_52[512] = {
+ 0x93, 0x81, 0x00, 0x00, 0x00, 0x00, 0x90, 0x6E, /* 0x00-0x03 */
+ 0x99, 0x83, 0x00, 0x00, 0x95, 0xAA, 0x90, 0xD8, /* 0x04-0x07 */
+ 0x8A, 0xA0, 0x00, 0x00, 0x8A, 0xA7, 0x99, 0x84, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x99, 0x86, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x8C, 0x59, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x99, 0x85, 0xED, 0x67, 0x00, 0x00, 0x97, 0xF1, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x8F, 0x89, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x94, 0xBB, 0x95, 0xCA, 0x00, 0x00, 0x99, 0x87, /* 0x24-0x27 */
+ 0x00, 0x00, 0x97, 0x98, 0x99, 0x88, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x99, 0x89, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x93, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x99, 0x8A, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x90, 0xA7, 0x8D, 0xFC, /* 0x34-0x37 */
+ 0x8C, 0x94, 0x99, 0x8B, 0x8E, 0x68, 0x8D, 0x8F, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0xE4, /* 0x40-0x43 */
+ 0x99, 0x8D, 0x00, 0x00, 0x00, 0x00, 0x91, 0xA5, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x8D, 0xED, 0x99, 0x8E, /* 0x48-0x4B */
+ 0x99, 0x8F, 0x91, 0x4F, 0x00, 0x00, 0x99, 0x8C, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x99, 0x91, 0x00, 0x00, 0x96, 0x55, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8D, 0x84, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x99, 0x90, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8C, 0x95, /* 0x60-0x63 */
+ 0x8D, 0xDC, 0x94, 0x8D, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x99, 0x94, 0x99, 0x92, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0x9B, /* 0x6C-0x6F */
+ 0x8F, 0xE8, 0x99, 0x9B, 0x8A, 0x84, 0x99, 0x95, /* 0x70-0x73 */
+ 0x99, 0x93, 0x91, 0x6E, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x99, 0x97, 0x00, 0x00, 0x99, 0x96, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8A, 0x63, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8C, 0x80, /* 0x84-0x87 */
+ 0x99, 0x9C, 0x97, 0xAB, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x99, 0x98, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x99, 0x9D, 0x99, 0x9A, 0x00, 0x00, /* 0x90-0x93 */
+ 0x99, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0xCD, /* 0x98-0x9B */
+ 0xED, 0x68, 0x00, 0x00, 0x00, 0x00, 0x8C, 0xF7, /* 0x9C-0x9F */
+ 0x89, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x97, 0xF2, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0xED, 0x69, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x8F, 0x95, 0x93, 0x77, 0x8D, 0x85, /* 0xA8-0xAB */
+ 0x99, 0xA0, 0x99, 0xA1, 0x00, 0x00, 0xEE, 0x5B, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x97, 0xE3, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x98, 0x4A, 0x99, 0xA3, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x8C, 0xF8, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x99, 0xA2, 0x00, 0x00, 0x8A, 0x4E, 0x00, 0x00, /* 0xBC-0xBF */
+ 0xED, 0x6A, 0x99, 0xA4, 0x00, 0x00, 0x96, 0x75, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x92, 0xBA, 0x00, 0x00, 0x97, 0x45, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x95, 0xD7, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x99, 0xA5, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0xE8, 0xD3, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x93, 0xAE, 0x00, 0x00, 0x99, 0xA6, /* 0xD4-0xD7 */
+ 0x8A, 0xA8, 0x96, 0xB1, 0x00, 0x00, 0xED, 0x6B, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x8F, 0x9F, 0x99, 0xA7, 0x95, 0xE5, /* 0xDC-0xDF */
+ 0x99, 0xAB, 0x00, 0x00, 0x90, 0xA8, 0x99, 0xA8, /* 0xE0-0xE3 */
+ 0x8B, 0xCE, 0x00, 0x00, 0x99, 0xA9, 0x8A, 0xA9, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x8C, 0x4D, 0x99, 0xAC, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x99, 0xAD, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x99, 0xAE, 0x99, 0xAF, 0x8E, 0xD9, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0x8C, 0xF9, 0x96, 0xDC, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_53[512] = {
+ 0xED, 0x6C, 0x96, 0xE6, 0x93, 0xF5, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x95, 0xEF, 0x99, 0xB0, 0xED, 0x6D, /* 0x04-0x07 */
+ 0x99, 0xB1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x99, 0xB3, 0x00, 0x00, 0x99, 0xB5, /* 0x0C-0x0F */
+ 0x99, 0xB4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x99, 0xB6, 0x89, 0xBB, 0x96, 0x6B, /* 0x14-0x17 */
+ 0x00, 0x00, 0x8D, 0xFA, 0x99, 0xB7, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x91, 0x78, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x8F, 0xA0, 0x8B, 0xA7, 0x00, 0x00, 0x99, 0xB8, /* 0x20-0x23 */
+ 0xED, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x94, 0xD9, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0xB9, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x99, 0xBA, 0x00, 0x00, 0x99, 0xBB, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x99, 0xBC, 0x95, 0x43, 0x8B, 0xE6, 0x88, 0xE3, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0xBD, /* 0x3C-0x3F */
+ 0x99, 0xBD, 0x8F, 0x5C, 0x00, 0x00, 0x90, 0xE7, /* 0x40-0x43 */
+ 0x00, 0x00, 0x99, 0xBF, 0x99, 0xBE, 0x8F, 0xA1, /* 0x44-0x47 */
+ 0x8C, 0xDF, 0x99, 0xC1, 0x94, 0xBC, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x99, 0xC2, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x94, 0xDA, 0x91, 0xB2, 0x91, 0xEC, /* 0x50-0x53 */
+ 0x8B, 0xA6, 0x00, 0x00, 0x00, 0x00, 0x93, 0xEC, /* 0x54-0x57 */
+ 0x92, 0x50, 0x00, 0x00, 0x94, 0x8E, 0x00, 0x00, /* 0x58-0x5B */
+ 0x96, 0x6D, 0x00, 0x00, 0x99, 0xC4, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x90, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x8C, 0x54, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x99, 0xC5, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x99, 0xC6, 0x89, 0x4B, /* 0x6C-0x6F */
+ 0x88, 0xF3, 0x8A, 0xEB, 0xED, 0x6F, 0x91, 0xA6, /* 0x70-0x73 */
+ 0x8B, 0x70, 0x97, 0x91, 0x00, 0x00, 0x99, 0xC9, /* 0x74-0x77 */
+ 0x89, 0xB5, 0x00, 0x00, 0x00, 0x00, 0x99, 0xC8, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8B, 0xA8, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x99, 0xCA, 0x00, 0x00, /* 0x80-0x83 */
+ 0x96, 0xEF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xED, 0x70, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x99, 0xCB, 0x00, 0x00, /* 0x94-0x97 */
+ 0x97, 0xD0, 0x00, 0x00, 0x8C, 0xFA, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8C, 0xB4, /* 0x9C-0x9F */
+ 0x99, 0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x99, 0xCE, 0x99, 0xCD, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x90, 0x7E, 0x89, 0x58, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x89, 0x7D, 0x99, 0xCF, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x99, 0xD0, 0x00, 0x00, 0xED, 0x71, 0x8C, 0xB5, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x99, 0xD1, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8B, 0x8E, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x8E, 0x51, 0x99, 0xD2, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x96, 0x94, 0x8D, 0xB3, 0x8B, 0x79, 0x97, 0x46, /* 0xC8-0xCB */
+ 0x91, 0x6F, 0x94, 0xBD, 0x8E, 0xFB, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x8F, 0x66, 0x00, 0x00, 0x8E, 0xE6, 0x8E, 0xF3, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x8F, 0x96, 0x00, 0x00, 0x94, 0xBE, /* 0xD8-0xDB */
+ 0x00, 0x00, 0xED, 0x72, 0x00, 0x00, 0x99, 0xD5, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x89, 0x62, 0x91, 0x70, 0x8C, 0xFB, /* 0xE0-0xE3 */
+ 0x8C, 0xC3, 0x8B, 0xE5, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x99, 0xD9, 0x92, 0x40, 0x91, 0xFC, 0x8B, 0xA9, /* 0xE8-0xEB */
+ 0x8F, 0xA2, 0x99, 0xDA, 0x99, 0xD8, 0x89, 0xC2, /* 0xEC-0xEF */
+ 0x91, 0xE4, 0x8E, 0xB6, 0x8E, 0x6A, 0x89, 0x45, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x8A, 0x90, 0x8D, 0x86, /* 0xF4-0xF7 */
+ 0x8E, 0x69, 0x00, 0x00, 0x99, 0xDB, 0x00, 0x00, /* 0xF8-0xFB */
+};
+
+static unsigned char u2c_54[512] = {
+ 0x00, 0x00, 0x99, 0xDC, 0x00, 0x00, 0x8B, 0x68, /* 0x00-0x03 */
+ 0x8A, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x8D, 0x87, 0x8B, 0x67, 0x92, 0xDD, 0x89, 0x44, /* 0x08-0x0B */
+ 0x93, 0xAF, 0x96, 0xBC, 0x8D, 0x40, 0x97, 0x99, /* 0x0C-0x0F */
+ 0x93, 0x66, 0x8C, 0xFC, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8C, 0x4E, /* 0x18-0x1B */
+ 0x00, 0x00, 0x99, 0xE5, 0x00, 0x00, 0x8B, 0xE1, /* 0x1C-0x1F */
+ 0x96, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x94, 0xDB, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x99, 0xE4, 0x00, 0x00, 0x8A, 0xDC, /* 0x28-0x2B */
+ 0x99, 0xDF, 0x99, 0xE0, 0x99, 0xE2, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x99, 0xE3, 0x00, 0x00, /* 0x34-0x37 */
+ 0x8B, 0x7A, 0x90, 0x81, 0x00, 0x00, 0x95, 0xAB, /* 0x38-0x3B */
+ 0x99, 0xE1, 0x99, 0xDD, 0x8C, 0xE1, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x99, 0xDE, 0x00, 0x00, 0x98, 0x43, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x95, 0xF0, 0x00, 0x00, /* 0x44-0x47 */
+ 0x92, 0xE6, 0x8C, 0xE0, 0x8D, 0x90, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x99, 0xE6, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x93, 0xDB, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0xEA, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x8E, 0xFC, 0x00, 0x00, 0x8E, 0xF4, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x99, 0xED, 0x99, 0xEB, 0x00, 0x00, 0x96, 0xA1, /* 0x70-0x73 */
+ 0x00, 0x00, 0x99, 0xE8, 0x99, 0xF1, 0x99, 0xEC, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0xEF, /* 0x78-0x7B */
+ 0x8C, 0xC4, 0x96, 0xBD, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x99, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x99, 0xF2, 0x00, 0x00, 0x99, 0xF4, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0xED, 0x75, 0x8D, 0xEE, /* 0x88-0x8B */
+ 0x98, 0x61, 0x00, 0x00, 0x99, 0xE9, 0x99, 0xE7, /* 0x8C-0x8F */
+ 0x99, 0xF3, 0x00, 0x00, 0x99, 0xEE, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0xED, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x99, 0xF6, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x9A, 0x42, 0x99, 0xF8, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x99, 0xFC, 0xED, 0x76, 0x00, 0x00, 0x9A, 0x40, /* 0xA8-0xAB */
+ 0x99, 0xF9, 0x00, 0x00, 0x00, 0x00, 0x9A, 0x5D, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x8D, 0xE7, 0x8A, 0x50, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x99, 0xF7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x9A, 0x44, 0x88, 0xF4, 0x9A, 0x43, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x88, 0xA3, 0x95, 0x69, 0x9A, 0x41, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x99, 0xFA, 0x00, 0x00, 0x00, 0x00, 0x99, 0xF5, /* 0xC4-0xC7 */
+ 0x99, 0xFB, 0x8D, 0xC6, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x9A, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x88, 0xF5, 0x9A, 0x4E, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x9A, 0x46, 0x9A, 0x47, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x8F, 0xA3, 0x96, 0x89, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x9A, 0x4C, 0x9A, 0x4B, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x93, 0x4E, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x9A, 0x4D, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x9A, 0x4A, 0x00, 0x00, 0xED, 0x77, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_55[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x89, 0x53, 0x00, 0x00, 0x8D, 0xB4, 0x90, 0x4F, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9A, 0x48, /* 0x0C-0x0F */
+ 0x93, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x9A, 0x49, 0x00, 0x00, 0x88, 0xA0, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x9A, 0x53, 0x97, 0x42, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x8F, 0xA5, 0x00, 0x00, 0x9A, 0x59, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x9A, 0x58, 0x9A, 0x4F, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x91, 0xC1, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x9A, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x91, 0xED, 0x9A, 0x55, 0x8F, 0xA4, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x9A, 0x52, 0x00, 0x00, 0x00, 0x00, 0x96, 0xE2, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8C, 0x5B, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x9A, 0x56, 0x9A, 0x57, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x9A, 0x54, 0x9A, 0x5A, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9A, 0x51, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9A, 0x60, /* 0x78-0x7B */
+ 0x9A, 0x65, 0x00, 0x00, 0x9A, 0x61, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x9A, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x9A, 0x66, /* 0x80-0x83 */
+ 0x91, 0x50, 0x00, 0x00, 0xED, 0x78, 0x9A, 0x68, /* 0x84-0x87 */
+ 0x00, 0x00, 0x8D, 0x41, 0x9A, 0x5E, 0x92, 0x9D, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x9A, 0x62, 0x9A, 0x5B, 0x8A, 0xAB, 0x00, 0x00, /* 0x98-0x9B */
+ 0x8A, 0xEC, 0x8A, 0x85, 0x9A, 0x63, 0x9A, 0x5F, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8C, 0x96, /* 0xA4-0xA7 */
+ 0x9A, 0x69, 0x9A, 0x67, 0x91, 0x72, 0x8B, 0x69, /* 0xA8-0xAB */
+ 0x8B, 0xAA, 0x00, 0x00, 0x9A, 0x64, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x8B, 0xF2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x89, 0x63, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x9A, 0x6D, 0x9A, 0x6B, 0x00, 0x00, 0x9A, 0xA5, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x9A, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x9A, 0x6A, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x9A, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x9A, 0x6C, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x6B, /* 0xE0-0xE3 */
+ 0x9A, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9A, 0x72, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x9A, 0x77, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x9A, 0x75, 0x9A, 0x74, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_56[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x92, 0x51, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x89, 0xC3, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x9A, 0x71, 0x00, 0x00, 0x9A, 0x73, 0x8F, 0xA6, /* 0x14-0x17 */
+ 0x89, 0x52, 0x00, 0x00, 0x00, 0x00, 0x9A, 0x76, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x89, 0xDC, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9A, 0x82, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x8F, 0xFA, 0x9A, 0x7D, 0x00, 0x00, /* 0x30-0x33 */
+ 0x9A, 0x7B, 0x00, 0x00, 0x9A, 0x7C, 0x00, 0x00, /* 0x34-0x37 */
+ 0x9A, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x89, 0x5C, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x91, 0x58, 0x00, 0x00, 0x9A, 0x78, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x9A, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8A, 0x9A, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x9A, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x8A, 0xED, 0x00, 0x00, 0x9A, 0x84, 0x9A, 0x80, /* 0x68-0x6B */
+ 0x9A, 0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x95, 0xAC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x93, 0xD3, 0x00, 0x00, 0x94, 0xB6, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x9A, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x9A, 0x85, 0x8A, 0x64, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x9A, 0x87, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9A, 0x8A, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x9A, 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x9A, 0x88, 0x00, 0x00, 0x94, 0x58, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x9A, 0x8B, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x9A, 0x8C, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x9A, 0x8E, 0x00, 0x00, 0x9A, 0x8D, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x9A, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x9A, 0x93, 0x9A, 0x91, 0x9A, 0x8F, 0x9A, 0x92, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x9A, 0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x9A, 0x95, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x9A, 0x96, 0x00, 0x00, 0x9A, 0x97, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9A, 0x98, /* 0xD4-0xD7 */
+ 0x99, 0x64, 0x00, 0x00, 0x8E, 0xFA, 0x8E, 0x6C, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x89, 0xF1, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x88, 0xF6, 0x00, 0x00, 0x00, 0x00, 0x92, 0x63, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x9A, 0x99, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x8D, 0xA2, 0x00, 0x00, 0x88, 0xCD, 0x90, 0x7D, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x9A, 0x9A, 0x8C, 0xC5, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x8D, 0x91, 0x00, 0x00, 0x9A, 0x9C, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_57[512] = {
+ 0x9A, 0x9B, 0x00, 0x00, 0x00, 0x00, 0x95, 0xDE, /* 0x00-0x03 */
+ 0x9A, 0x9D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x9A, 0x9F, 0x9A, 0x9E, 0x00, 0x00, 0x9A, 0xA0, /* 0x08-0x0B */
+ 0x00, 0x00, 0x9A, 0xA1, 0x00, 0x00, 0x8C, 0x97, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x89, 0x80, 0x9A, 0xA2, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x9A, 0xA4, 0x00, 0x00, /* 0x14-0x17 */
+ 0x9A, 0xA3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x9A, 0xA6, 0x00, 0x00, 0x00, 0x00, 0x93, 0x79, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x9A, 0xA7, 0x88, 0xB3, /* 0x24-0x27 */
+ 0x8D, 0xDD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x8C, 0x5C, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x92, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9A, 0xA8, /* 0x34-0x37 */
+ 0x9A, 0xA9, 0x00, 0x00, 0x00, 0x00, 0x9A, 0xAB, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x9A, 0xAC, 0x00, 0x00, 0x8D, 0xE2, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8B, 0xCF, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x96, 0x56, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x9A, 0xAA, 0x9A, 0xAD, /* 0x4C-0x4F */
+ 0x8D, 0xBF, 0x8D, 0x42, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0xED, 0x79, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x9A, 0xB1, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x8D, 0xA3, 0xED, 0x7A, 0x92, 0x52, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x9A, 0xAE, 0x92, 0xD8, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9A, 0xB2, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x90, 0x82, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x9A, 0xB0, 0x9A, 0xB3, 0x00, 0x00, 0x8C, 0x5E, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9A, 0xB4, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x9A, 0xB5, 0x00, 0x00, 0x8D, 0x43, 0x8A, 0x5F, /* 0xA0-0xA3 */
+ 0x9A, 0xB7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x9A, 0xB8, 0x00, 0x00, /* 0xA8-0xAB */
+ 0xED, 0x7B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x9A, 0xB9, 0x00, 0x00, 0x00, 0x00, 0x9A, 0xB6, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x9A, 0xAF, 0x00, 0x00, 0x00, 0x00, 0x9A, 0xBA, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x9A, 0xBB, 0xED, 0x7D, /* 0xC4-0xC7 */
+ 0xED, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x96, 0x84, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x8F, 0xE9, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x9A, 0xBD, 0x9A, 0xBE, /* 0xD0-0xD3 */
+ 0x9A, 0xBC, 0x00, 0x00, 0x9A, 0xC0, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x94, 0x57, 0x00, 0x00, 0x00, 0x00, 0x88, 0xE6, /* 0xDC-0xDF */
+ 0x95, 0x75, 0x00, 0x00, 0x00, 0x00, 0x9A, 0xC1, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x8F, 0xFB, 0x00, 0x00, 0x00, 0x00, 0x8E, 0xB7, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x94, 0x7C, 0x8A, 0xEE, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x8D, 0xE9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_58[512] = {
+ 0x96, 0x78, 0x00, 0x00, 0x93, 0xB0, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x8C, 0x98, 0x91, 0xCD, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x9A, 0xBF, 0x9A, 0xC2, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x91, 0xC2, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x9A, 0xC3, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x9A, 0xC4, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x9A, 0xC6, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x92, 0xE7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x8A, 0xAC, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEA, 0x9F, /* 0x2C-0x2F */
+ 0x89, 0x81, 0x95, 0xF1, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x8F, 0xEA, 0x93, 0x67, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x8D, 0xE4, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x9A, 0xCC, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x95, 0xBB, 0x97, 0xDB, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x89, 0xF2, 0x9A, 0xC8, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x91, 0x59, 0x9A, 0xCB, 0x00, 0x00, /* 0x50-0x53 */
+ 0x93, 0x83, 0x00, 0x00, 0x00, 0x00, 0x93, 0x68, /* 0x54-0x57 */
+ 0x93, 0x84, 0x94, 0xB7, 0x92, 0xCB, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x8D, 0xC7, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x9A, 0xC7, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x89, 0x96, 0x00, 0x00, 0x93, 0x55, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x9A, 0xC9, 0x00, 0x00, 0x9A, 0xC5, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x90, 0x6F, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x9A, 0xCD, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x8F, 0x6D, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8B, 0xAB, /* 0x80-0x83 */
+ 0x00, 0x00, 0x9A, 0xCE, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0xE6, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0x9D, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x92, 0xC4, 0x00, 0x00, 0xED, 0x81, 0x9A, 0xD0, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x96, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x9A, 0xD1, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x9A, 0xD6, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0xED, 0x82, 0x95, 0xAD, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x9A, 0xD5, 0x9A, 0xCF, 0x9A, 0xD2, 0x9A, 0xD4, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x8D, 0xA4, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x95, 0xC7, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x9A, 0xD7, 0x00, 0x00, 0x92, 0x64, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x89, 0xF3, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x8F, 0xEB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x9A, 0xD9, 0x00, 0x00, 0x9A, 0xD8, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x8D, 0x88, 0x00, 0x00, 0x9A, 0xDA, /* 0xD4-0xD7 */
+ 0x9A, 0xDC, 0x9A, 0xDB, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x9A, 0xDE, 0x00, 0x00, 0x9A, 0xD3, 0x9A, 0xE0, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x9A, 0xDF, 0x9A, 0xDD, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x6D, /* 0xE8-0xEB */
+ 0x90, 0x70, 0x00, 0x00, 0x91, 0x73, 0x9A, 0xE1, /* 0xEC-0xEF */
+ 0x90, 0xBA, 0x88, 0xEB, 0x94, 0x84, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0xD9, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x9A, 0xE3, 0x9A, 0xE2, 0x9A, 0xE4, /* 0xF8-0xFB */
+ 0x9A, 0xE5, 0x9A, 0xE6, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_59[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x9A, 0xE7, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x95, 0xCF, 0x9A, 0xE8, 0xED, 0x83, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0xC4, /* 0x0C-0x0F */
+ 0x9A, 0xE9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x97, 0x5B, 0x8A, 0x4F, 0x00, 0x00, /* 0x14-0x17 */
+ 0x99, 0xC7, 0x8F, 0x67, 0x91, 0xBD, 0x9A, 0xEA, /* 0x18-0x1B */
+ 0x96, 0xE9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x96, 0xB2, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x9A, 0xEC, 0x00, 0x00, 0x91, 0xE5, /* 0x24-0x27 */
+ 0x00, 0x00, 0x93, 0x56, 0x91, 0xBE, 0x95, 0x76, /* 0x28-0x2B */
+ 0x9A, 0xED, 0x9A, 0xEE, 0x89, 0x9B, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x8E, 0xB8, 0x9A, 0xEF, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0xCE, /* 0x34-0x37 */
+ 0x9A, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x9A, 0xF1, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x89, 0x82, 0x00, 0x00, 0x00, 0x00, 0x8A, 0xEF, /* 0x44-0x47 */
+ 0x93, 0xDE, 0x95, 0xF2, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x9A, 0xF5, 0x91, 0x74, /* 0x4C-0x4F */
+ 0x9A, 0xF4, 0x8C, 0x5F, 0x00, 0x00, 0xED, 0x84, /* 0x50-0x53 */
+ 0x96, 0x7A, 0x9A, 0xF3, 0x00, 0x00, 0x93, 0x85, /* 0x54-0x57 */
+ 0x9A, 0xF7, 0x00, 0x00, 0x9A, 0xF6, 0xED, 0x85, /* 0x58-0x5B */
+ 0x00, 0x00, 0xED, 0x86, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x9A, 0xF9, 0x00, 0x00, 0x9A, 0xF8, 0xED, 0x87, /* 0x60-0x63 */
+ 0x00, 0x00, 0x89, 0x9C, 0x00, 0x00, 0x9A, 0xFA, /* 0x64-0x67 */
+ 0x8F, 0xA7, 0x9A, 0xFC, 0x92, 0x44, 0x00, 0x00, /* 0x68-0x6B */
+ 0x9A, 0xFB, 0x00, 0x00, 0x95, 0xB1, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8F, 0x97, /* 0x70-0x73 */
+ 0x93, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x9B, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x8D, 0x44, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x9B, 0x41, 0x94, 0x40, 0x94, 0xDC, /* 0x80-0x83 */
+ 0x96, 0xCF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x94, 0x44, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x9B, 0x4A, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8B, 0x57, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x97, 0x64, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x96, 0xAD, 0x00, 0x00, 0x9B, 0xAA, /* 0x98-0x9B */
+ 0x00, 0x00, 0x9B, 0x42, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9B, 0x45, /* 0xA0-0xA3 */
+ 0xED, 0x88, 0x91, 0xC3, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x96, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x93, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x9B, 0x46, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x96, 0x85, 0xED, 0x89, 0x8D, 0xC8, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x8F, 0xA8, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x9B, 0x47, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x8E, 0x6F, 0x00, 0x00, 0x8E, 0x6E, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x88, 0xB7, 0x8C, 0xC6, 0x00, 0x00, 0x90, 0xA9, /* 0xD0-0xD3 */
+ 0x88, 0xCF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x9B, 0x4B, 0x9B, 0x4C, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x9B, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x89, 0x57, 0x8A, 0xAD, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x9B, 0x48, 0x00, 0x00, 0x96, 0xC3, 0x95, 0x50, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x88, 0xA6, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0xF7, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x70, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_5A[512] = {
+ 0x00, 0x00, 0x88, 0xD0, 0x00, 0x00, 0x88, 0xA1, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x9B, 0x51, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x9B, 0x4F, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x96, 0xBA, 0x00, 0x00, 0x9B, 0x52, 0x00, 0x00, /* 0x18-0x1B */
+ 0x9B, 0x50, 0x00, 0x00, 0x00, 0x00, 0x9B, 0x4E, /* 0x1C-0x1F */
+ 0x90, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x9B, 0x4D, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x95, 0xD8, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8C, 0xE2, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x9B, 0x56, 0x9B, 0x57, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x8F, 0xA9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x9B, 0x53, 0x98, 0x4B, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x94, 0x6B, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x9B, 0x55, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x8D, 0xA5, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x9B, 0x58, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x95, 0x77, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x9B, 0x59, 0x00, 0x00, /* 0x68-0x6B */
+ 0x9B, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0xB9, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x94, 0x7D, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x9B, 0x5A, 0x95, 0x51, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x9B, 0x5B, 0x9B, 0x5F, 0x9B, 0x5C, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x89, 0xC5, 0x9B, 0x5E, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x8E, 0xB9, 0x00, 0x00, 0x9B, 0x5D, /* 0xC8-0xCB */
+ 0x8C, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x9B, 0x6B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x9B, 0x64, 0x9B, 0x61, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x92, 0x84, 0x00, 0x00, 0x9B, 0x60, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x9B, 0x62, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x9B, 0x63, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x9B, 0x65, 0x9B, 0x66, /* 0xF8-0xFB */
+};
+
+static unsigned char u2c_5B[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x8A, 0xF0, 0x00, 0x00, 0x9B, 0x68, /* 0x08-0x0B */
+ 0x9B, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x9B, 0x69, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x8F, 0xEC, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x9B, 0x6C, 0x00, 0x00, /* 0x28-0x2B */
+ 0x92, 0xDA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x89, 0x64, 0x00, 0x00, 0x9B, 0x6A, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x9B, 0x6D, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x9B, 0x6E, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x9B, 0x71, 0x00, 0x00, 0x00, 0x00, 0x9B, 0x6F, /* 0x40-0x43 */
+ 0x00, 0x00, 0x9B, 0x70, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x8E, 0x71, 0x9B, 0x72, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x8D, 0x45, 0x9B, 0x73, 0xED, 0x8A, 0x8E, 0x9A, /* 0x54-0x57 */
+ 0x91, 0xB6, 0x00, 0x00, 0x9B, 0x74, 0x9B, 0x75, /* 0x58-0x5B */
+ 0x8E, 0x79, 0x8D, 0x46, 0x00, 0x00, 0x96, 0xD0, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8B, 0x47, /* 0x60-0x63 */
+ 0x8C, 0xC7, 0x9B, 0x76, 0x8A, 0x77, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x9B, 0x77, 0x00, 0x00, 0x91, 0xB7, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x9B, 0x78, 0x9B, 0xA1, 0x00, 0x00, 0x9B, 0x79, /* 0x70-0x73 */
+ 0x00, 0x00, 0x9B, 0x7A, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x9B, 0x7B, 0x00, 0x00, 0x9B, 0x7D, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x9B, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x9B, 0x80, /* 0x80-0x83 */
+ 0x00, 0x00, 0x91, 0xEE, 0x00, 0x00, 0x89, 0x46, /* 0x84-0x87 */
+ 0x8E, 0xE7, 0x88, 0xC0, 0x00, 0x00, 0x91, 0x76, /* 0x88-0x8B */
+ 0x8A, 0xAE, 0x8E, 0xB3, 0x00, 0x00, 0x8D, 0x47, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x93, 0x86, 0x00, 0x00, 0x8F, 0x40, /* 0x94-0x97 */
+ 0x8A, 0xAF, 0x92, 0x88, 0x92, 0xE8, 0x88, 0xB6, /* 0x98-0x9B */
+ 0x8B, 0x58, 0x95, 0xF3, 0x00, 0x00, 0x8E, 0xC0, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x8B, 0x71, 0x90, 0xE9, /* 0xA0-0xA3 */
+ 0x8E, 0xBA, 0x97, 0x47, 0x9B, 0x81, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x8B, 0x7B, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x8D, 0xC9, 0x00, 0x00, 0x00, 0x00, 0x8A, 0x51, /* 0xB0-0xB3 */
+ 0x89, 0x83, 0x8F, 0xAA, 0x89, 0xC6, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x9B, 0x82, 0x97, 0x65, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8F, 0x68, /* 0xBC-0xBF */
+ 0xED, 0x8B, 0x00, 0x00, 0x8E, 0xE2, 0x9B, 0x83, /* 0xC0-0xC3 */
+ 0x8A, 0xF1, 0x93, 0xD0, 0x96, 0xA7, 0x9B, 0x84, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x9B, 0x85, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x95, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x9B, 0x87, 0x00, 0x00, 0x8A, 0xA6, 0x8B, 0xF5, /* 0xD0-0xD3 */
+ 0x9B, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xED, 0x8D, 0x00, 0x00, 0x00, 0x00, 0x8A, 0xB0, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x90, 0x51, 0x9B, 0x8B, 0x8E, 0x40, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x89, 0xC7, 0x9B, 0x8A, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x9B, 0x88, 0x9B, 0x8C, 0x9B, 0x89, 0x94, 0x4A, /* 0xE4-0xE7 */
+ 0x9E, 0xCB, 0x90, 0x52, 0x00, 0x00, 0x9B, 0x8D, /* 0xE8-0xEB */
+ 0xED, 0x8E, 0x00, 0x00, 0x97, 0xBE, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x9B, 0x8E, 0x00, 0x00, 0x00, 0x00, 0x9B, 0x90, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x92, 0x9E, 0x9B, 0x8F, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x90, 0xA1, 0x00, 0x00, 0x8E, 0x9B, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0x91, 0xCE, 0x8E, 0xF5, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_5C[512] = {
+ 0x00, 0x00, 0x95, 0x95, 0x90, 0xEA, 0x00, 0x00, /* 0x00-0x03 */
+ 0x8E, 0xCB, 0x9B, 0x91, 0x8F, 0xAB, 0x9B, 0x92, /* 0x04-0x07 */
+ 0x9B, 0x93, 0x88, 0xD1, 0x91, 0xB8, 0x90, 0x71, /* 0x08-0x0B */
+ 0x00, 0x00, 0x9B, 0x94, 0x93, 0xB1, 0x8F, 0xAC, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x8F, 0xAD, 0x00, 0x00, 0x9B, 0x95, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x90, 0xEB, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x8F, 0xAE, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0xED, 0x8F, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x9B, 0x96, 0x00, 0x00, 0x9B, 0x97, 0x00, 0x00, /* 0x20-0x23 */
+ 0x96, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x9B, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x8B, 0xC4, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x8F, 0x41, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x9B, 0x99, 0x9B, 0x9A, 0x8E, 0xDA, 0x90, 0x4B, /* 0x38-0x3B */
+ 0x93, 0xF2, 0x90, 0x73, 0x94, 0xF6, 0x94, 0x41, /* 0x3C-0x3F */
+ 0x8B, 0xC7, 0x9B, 0x9B, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x8B, 0x8F, 0x9B, 0x9C, 0x00, 0x00, /* 0x44-0x47 */
+ 0x8B, 0xFC, 0x00, 0x00, 0x93, 0xCD, 0x89, 0xAE, /* 0x48-0x4B */
+ 0x00, 0x00, 0x8E, 0x72, 0x9B, 0x9D, 0x9B, 0xA0, /* 0x4C-0x4F */
+ 0x9B, 0x9F, 0x8B, 0xFB, 0x00, 0x00, 0x9B, 0x9E, /* 0x50-0x53 */
+ 0x00, 0x00, 0x93, 0x57, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x91, 0xAE, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x93, 0x6A, 0x8E, 0xC6, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x91, 0x77, 0x97, 0x9A, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x9B, 0xA2, 0x00, 0x00, 0x9B, 0xA3, 0x93, 0xD4, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x8E, 0x52, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x9B, 0xA5, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x9B, 0xA6, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x9B, 0xA7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x8A, 0xF2, 0x9B, 0xA8, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x9B, 0xA9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x89, 0xAA, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0xED, 0x90, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x91, 0x5A, 0x8A, 0xE2, 0x00, 0x00, 0x9B, 0xAB, /* 0xA8-0xAB */
+ 0x96, 0xA6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x91, 0xD0, 0x00, 0x00, 0x8A, 0x78, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x9B, 0xAD, 0x9B, 0xAF, /* 0xB4-0xB7 */
+ 0x8A, 0xDD, 0x00, 0x00, 0xED, 0x91, 0x9B, 0xAC, /* 0xB8-0xBB */
+ 0x9B, 0xAE, 0x00, 0x00, 0x9B, 0xB1, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x9B, 0xB0, 0x00, 0x00, 0x9B, 0xB2, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x9B, 0xB3, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x93, 0xBB, 0x8B, 0xAC, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x89, 0xE3, 0x9B, 0xB4, 0x9B, 0xB9, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x9B, 0xB7, 0x00, 0x00, 0x95, 0xF5, /* 0xEC-0xEF */
+ 0x95, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0xED, 0x92, 0x93, 0x87, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x9B, 0xB6, 0x8F, 0x73, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x9B, 0xB5, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_5D[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x92, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9B, 0xBA, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x8D, 0xE8, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x9B, 0xC0, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x9B, 0xC1, 0x9B, 0xBB, 0x8A, 0x52, 0x9B, 0xBC, /* 0x14-0x17 */
+ 0x9B, 0xC5, 0x9B, 0xC4, 0x9B, 0xC3, 0x9B, 0xBF, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9B, 0xBE, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x9B, 0xC2, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xED, 0x93, /* 0x24-0x27 */
+ 0x00, 0x00, 0x95, 0xF6, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0xED, 0x96, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9B, 0xC9, /* 0x48-0x4B */
+ 0x9B, 0xC6, 0x00, 0x00, 0x9B, 0xC8, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x97, 0x92, 0x00, 0x00, 0x9B, 0xC7, 0xED, 0x94, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x9B, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x90, 0x93, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x9B, 0xCA, 0xED, 0x97, 0x00, 0x00, 0x8D, 0xB5, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9B, 0xCB, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x9B, 0xCC, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x9B, 0xCF, 0x00, 0x00, /* 0x80-0x83 */
+ 0x9B, 0xCE, 0x00, 0x00, 0x00, 0x00, 0x9B, 0xCD, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x88, /* 0x88-0x8B */
+ 0x9B, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x9B, 0xD5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x9B, 0xD1, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x9B, 0xD0, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x9B, 0xD2, 0x00, 0x00, 0x9B, 0xD3, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9B, 0xD6, /* 0xB4-0xB7 */
+ 0xED, 0x98, 0xED, 0x99, 0x97, 0xE4, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x9B, 0xD7, 0x9B, 0xD4, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x9B, 0xD8, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x8A, 0xDE, 0x9B, 0xD9, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0xED, 0x9A, 0x00, 0x00, 0x9B, 0xDB, 0x9B, 0xDA, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x9B, 0xDC, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9B, 0xDD, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x90, 0xEC, 0x8F, 0x42, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x8F, 0x84, 0x00, 0x00, 0x91, 0x83, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x8D, 0x48, 0x8D, 0xB6, 0x8D, 0x49, /* 0xE4-0xE7 */
+ 0x8B, 0x90, 0x00, 0x00, 0x00, 0x00, 0x9B, 0xDE, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x8D, 0xB7, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x8C, 0xC8, 0x9B, 0xDF, 0x96, 0xA4, /* 0xF0-0xF3 */
+ 0x94, 0x62, 0x9B, 0xE0, 0x00, 0x00, 0x8D, 0x4A, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8A, 0xAA, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x92, 0x46, 0x8B, 0xD0, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_5E[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x8E, 0x73, 0x95, 0x7A, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x94, 0xBF, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9B, 0xE1, /* 0x08-0x0B */
+ 0x8A, 0xF3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x9B, 0xE4, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x92, 0x9F, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x9B, 0xE3, 0x9B, 0xE2, 0x9B, 0xE5, /* 0x18-0x1B */
+ 0x00, 0x00, 0x92, 0xE9, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x90, 0x83, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x74, /* 0x28-0x2B */
+ 0x00, 0x00, 0x90, 0xC8, 0x00, 0x00, 0x91, 0xD1, /* 0x2C-0x2F */
+ 0x8B, 0x41, 0x00, 0x00, 0x00, 0x00, 0x92, 0xA0, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x9B, 0xE6, 0x9B, 0xE7, /* 0x34-0x37 */
+ 0x8F, 0xED, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x96, 0x58, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x9B, 0xEA, 0x00, 0x00, 0x00, 0x00, 0x9B, 0xE9, /* 0x40-0x43 */
+ 0x9B, 0xE8, 0x95, 0x9D, 0x00, 0x00, 0x9B, 0xF1, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x96, 0x79, 0x00, 0x00, 0x9B, 0xEB, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x9B, 0xED, 0x96, 0x8B, 0x00, 0x00, 0x9B, 0xEC, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9B, 0xEE, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x94, 0xA6, 0x9B, 0xEF, 0x95, 0xBC, /* 0x60-0x63 */
+ 0x9B, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x8A, 0xB1, 0x95, 0xBD, /* 0x70-0x73 */
+ 0x94, 0x4E, 0x9B, 0xF2, 0x9B, 0xF3, 0x00, 0x00, /* 0x74-0x77 */
+ 0x8D, 0x4B, 0x8A, 0xB2, 0x9B, 0xF4, 0x8C, 0xB6, /* 0x78-0x7B */
+ 0x97, 0x63, 0x97, 0x48, 0x8A, 0xF4, 0x9B, 0xF6, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x92, 0xA1, 0x00, 0x00, 0x8D, 0x4C, /* 0x80-0x83 */
+ 0x8F, 0xAF, 0x00, 0x00, 0x00, 0x00, 0x94, 0xDD, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x8F, 0xB0, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8F, 0x98, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x92, 0xEA, 0x95, 0xF7, 0x93, 0x58, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x8D, 0x4D, 0x00, 0x00, /* 0x98-0x9B */
+ 0x95, 0x7B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x9B, 0xF7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x93, 0x78, 0x8D, 0xC0, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8C, 0xC9, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x92, 0xEB, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x88, 0xC1, 0x8F, 0x8E, 0x8D, 0x4E, /* 0xB4-0xB7 */
+ 0x97, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x9B, 0xF8, 0x9B, 0xF9, 0x94, 0x70, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x9B, 0xFA, 0x97, 0xF5, 0x98, 0x4C, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9B, 0xFC, /* 0xCC-0xCF */
+ 0x9B, 0xFB, 0x00, 0x00, 0x00, 0x00, 0x8A, 0x66, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x9C, 0x40, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x9C, 0x43, 0x9C, 0x44, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x9C, 0x42, 0x00, 0x00, 0x95, 0x5F, /* 0xDC-0xDF */
+ 0x8F, 0xB1, 0x9C, 0x46, 0x9C, 0x45, 0x9C, 0x41, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x9C, 0x47, 0x9C, 0x48, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x9C, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x9C, 0x4C, 0x9C, 0x4A, 0x00, 0x00, 0x9C, 0x4B, /* 0xF0-0xF3 */
+ 0x9C, 0x4D, 0x00, 0x00, 0x89, 0x84, 0x92, 0xEC, /* 0xF4-0xF7 */
+ 0x9C, 0x4E, 0x00, 0x00, 0x8C, 0x9A, 0x89, 0xF4, /* 0xF8-0xFB */
+ 0x94, 0x55, 0x00, 0x00, 0x9C, 0x4F, 0x93, 0xF9, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_5F[512] = {
+ 0x00, 0x00, 0x95, 0xD9, 0x00, 0x00, 0x9C, 0x50, /* 0x00-0x03 */
+ 0x98, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x9C, 0x51, 0x95, 0xBE, 0x9C, 0x54, /* 0x08-0x0B */
+ 0x98, 0x9F, 0x98, 0xAF, 0x00, 0x00, 0x8E, 0xAE, /* 0x0C-0x0F */
+ 0x93, 0xF3, 0x9C, 0x55, 0x00, 0x00, 0x8B, 0x7C, /* 0x10-0x13 */
+ 0x92, 0xA2, 0x88, 0xF8, 0x9C, 0x56, 0x95, 0xA4, /* 0x14-0x17 */
+ 0x8D, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x92, 0x6F, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0xED, /* 0x1C-0x1F */
+ 0x00, 0x00, 0xED, 0x9B, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x96, 0xED, 0x8C, 0xB7, 0x8C, 0xCA, /* 0x24-0x27 */
+ 0x00, 0x00, 0x9C, 0x57, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x9C, 0x58, 0x00, 0x00, 0x9C, 0x5E, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x8E, 0xE3, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0xED, 0x9C, 0x92, 0xA3, 0x00, 0x00, 0x8B, 0xAD, /* 0x34-0x37 */
+ 0x9C, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x95, 0x4A, 0x00, 0x00, 0x92, 0x65, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x9C, 0x5A, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0xED, 0x4B, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x9C, 0x5B, 0x00, 0x00, 0x8B, 0xAE, 0x00, 0x00, /* 0x48-0x4B */
+ 0x9C, 0x5C, 0x00, 0x00, 0x9C, 0x5D, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x9C, 0x5F, 0x00, 0x00, 0x93, 0x96, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x9C, 0x60, 0x9C, 0x61, /* 0x54-0x57 */
+ 0x00, 0x00, 0x9C, 0x62, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x9C, 0x53, 0x9C, 0x52, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x9C, 0x63, 0x8C, 0x60, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x95, 0x46, 0xED, 0x9D, /* 0x64-0x67 */
+ 0x00, 0x00, 0x8D, 0xCA, 0x95, 0x56, 0x92, 0xA4, /* 0x68-0x6B */
+ 0x95, 0x6A, 0x9C, 0x64, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x8F, 0xB2, 0x89, 0x65, 0x00, 0x00, 0x9C, 0x65, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9C, 0x66, /* 0x74-0x77 */
+ 0x00, 0x00, 0x96, 0xF0, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x94, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x9C, 0x69, /* 0x7C-0x7F */
+
+ 0x89, 0x9D, 0x90, 0xAA, 0x9C, 0x68, 0x9C, 0x67, /* 0x80-0x83 */
+ 0x8C, 0x61, 0x91, 0xD2, 0x00, 0x00, 0x9C, 0x6D, /* 0x84-0x87 */
+ 0x9C, 0x6B, 0x00, 0x00, 0x9C, 0x6A, 0x97, 0xA5, /* 0x88-0x8B */
+ 0x8C, 0xE3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x8F, 0x99, 0x9C, 0x6C, 0x93, 0x6B, 0x8F, 0x5D, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0xBE, /* 0x94-0x97 */
+ 0x9C, 0x70, 0x9C, 0x6F, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x9C, 0x6E, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x9C, 0x71, 0x8C, 0xE4, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x9C, 0x72, 0x95, 0x9C, 0x8F, 0x7A, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x9C, 0x73, 0x94, 0xF7, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0xBF, /* 0xB0-0xB3 */
+ 0x92, 0xA5, 0x00, 0x00, 0x00, 0x00, 0xED, 0x9E, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x93, 0x4F, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x9C, 0x74, 0x8B, 0x4A, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x53, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x95, 0x4B, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x8A, 0xF5, 0x94, 0x45, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x9C, 0x75, 0x8E, 0x75, /* 0xD4-0xD7 */
+ 0x96, 0x59, 0x96, 0x5A, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x89, 0x9E, 0x9C, 0x7A, 0xED, 0x9F, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x92, 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x9C, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0xF5, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x9C, 0xAB, 0x9C, 0x79, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x94, 0x4F, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x9C, 0x78, 0x00, 0x00, 0x00, 0x00, 0x9C, 0x76, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x8D, 0x9A, 0x00, 0x00, 0x9C, 0x7C, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_60[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x9C, 0x83, 0x9C, 0x89, /* 0x0C-0x0F */
+ 0x9C, 0x81, 0x00, 0x00, 0x93, 0x7B, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x9C, 0x86, 0x95, 0x7C, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x9C, 0x80, 0x00, 0x00, 0x9C, 0x85, /* 0x18-0x1B */
+ 0x97, 0xE5, 0x8E, 0x76, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x91, 0xD3, 0x9C, 0x7D, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x8B, 0x7D, 0x9C, 0x88, 0x90, 0xAB, /* 0x24-0x27 */
+ 0x89, 0x85, 0x9C, 0x82, 0x89, 0xF6, 0x9C, 0x87, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8B, 0xAF, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x9C, 0x84, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x9C, 0x8A, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x9C, 0x8C, 0x9C, 0x96, 0x9C, 0x94, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x9C, 0x91, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x9C, 0x90, 0x97, 0xF6, /* 0x48-0x4B */
+ 0x00, 0x00, 0x9C, 0x92, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x8B, 0xB0, 0x00, 0x00, 0x8D, 0x50, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x8F, 0x9A, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x9C, 0x99, 0x9C, 0x8B, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0xED, 0xA0, 0x00, 0x00, 0x9C, 0x8F, /* 0x5C-0x5F */
+ 0x9C, 0x7E, 0x00, 0x00, 0x89, 0xF8, 0x9C, 0x93, /* 0x60-0x63 */
+ 0x9C, 0x95, 0x92, 0x70, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x8D, 0xA6, 0x89, 0xB6, 0x9C, 0x8D, 0x9C, 0x98, /* 0x68-0x6B */
+ 0x9C, 0x97, 0x8B, 0xB1, 0x00, 0x00, 0x91, 0xA7, /* 0x6C-0x6F */
+ 0x8A, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x8C, 0x62, 0x00, 0x00, 0x9C, 0x8E, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x9C, 0x9A, 0x00, 0x00, 0x9C, 0x9D, /* 0x80-0x83 */
+ 0x9C, 0x9F, 0xED, 0xA1, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x8E, 0xBB, 0xED, 0xA2, 0x9C, 0xA5, /* 0x88-0x8B */
+ 0x92, 0xEE, 0x9C, 0x9B, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x9C, 0xA3, 0x00, 0x00, /* 0x90-0x93 */
+ 0x89, 0xF7, 0x00, 0x00, 0x9C, 0xA1, 0x9C, 0xA2, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x9C, 0x9E, 0x9C, 0xA0, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8C, 0xE5, /* 0x9C-0x9F */
+ 0x97, 0x49, 0x00, 0x00, 0x00, 0x00, 0x8A, 0xB3, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x89, 0x78, 0x9C, 0xA4, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x94, 0x59, 0x88, 0xAB, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x94, 0xDF, 0x9C, 0x7B, /* 0xB0-0xB3 */
+ 0x9C, 0xAA, 0x9C, 0xAE, 0x96, 0xE3, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x9C, 0xA7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x93, 0x89, 0x9C, 0xAC, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x8F, 0xEE, 0x9C, 0xAD, 0x93, 0xD5, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x98, 0x66, 0x00, 0x00, 0x9C, 0xA9, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0xED, 0xA4, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x9C, 0xAF, 0x00, 0x00, 0x8D, 0x9B, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x90, 0xC9, 0x00, 0x00, 0xED, 0xA3, 0x88, 0xD2, /* 0xDC-0xDF */
+ 0x9C, 0xA8, 0x9C, 0xA6, 0x00, 0x00, 0x91, 0x79, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9C, 0x9C, /* 0xE4-0xE7 */
+ 0x8E, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x91, 0xC4, 0x9C, 0xBB, 0xED, 0xA6, 0x91, 0x7A, /* 0xF0-0xF3 */
+ 0x9C, 0xB6, 0x00, 0x00, 0x9C, 0xB3, 0x9C, 0xB4, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x8E, 0xE4, 0x9C, 0xB7, 0x9C, 0xBA, /* 0xF8-0xFB */
+};
+
+static unsigned char u2c_61[512] = {
+ 0x9C, 0xB5, 0x8F, 0x44, 0x00, 0x00, 0x9C, 0xB8, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x9C, 0xB2, 0x00, 0x00, /* 0x04-0x07 */
+ 0x96, 0xFA, 0x96, 0xF9, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x9C, 0xBC, 0x9C, 0xBD, 0x88, 0xD3, /* 0x0C-0x0F */
+ 0x00, 0x00, 0xED, 0xA7, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x9C, 0xB1, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x8B, 0xF0, 0x88, 0xA4, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8A, 0xB4, /* 0x1C-0x1F */
+ 0xED, 0xA5, 0x9C, 0xB9, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9C, 0xC1, /* 0x24-0x27 */
+ 0x9C, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x9C, 0xC5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xED, 0xA9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x9C, 0xC6, 0x00, 0x00, 0x00, 0x00, 0xED, 0xA8, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x9C, 0xC4, 0x9C, 0xC7, 0x9C, 0xBF, 0x9C, 0xC3, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x9C, 0xC8, 0x00, 0x00, /* 0x40-0x43 */
+ 0x9C, 0xC9, 0x00, 0x00, 0x00, 0x00, 0x9C, 0xBE, /* 0x44-0x47 */
+ 0x8E, 0x9C, 0x00, 0x00, 0x9C, 0xC2, 0x91, 0xD4, /* 0x48-0x4B */
+ 0x8D, 0x51, 0x9C, 0xB0, 0x90, 0x54, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9C, 0xD6, /* 0x50-0x53 */
+ 0x00, 0x00, 0x95, 0xE7, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x9C, 0xCC, 0x9C, 0xCD, 0x9C, 0xCE, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x9C, 0xD5, 0x00, 0x00, 0x9C, 0xD4, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x96, 0x9D, 0x8A, 0xB5, /* 0x60-0x63 */
+ 0x00, 0x00, 0x9C, 0xD2, 0x00, 0x00, 0x8C, 0x64, /* 0x64-0x67 */
+ 0x8A, 0x53, 0x00, 0x00, 0x00, 0x00, 0x9C, 0xCF, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x97, 0xB6, 0x9C, 0xD1, /* 0x6C-0x6F */
+ 0x88, 0xD4, 0x9C, 0xD3, 0x00, 0x00, 0x9C, 0xCA, /* 0x70-0x73 */
+ 0x9C, 0xD0, 0x9C, 0xD7, 0x8C, 0x63, 0x9C, 0xCB, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x97, 0x7C, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x97, 0x4A, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9C, 0xDA, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x9C, 0xDE, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x91, 0x9E, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x97, 0xF7, 0x9C, 0xDF, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x9C, 0xDC, 0x00, 0x00, 0x9C, 0xD9, 0x00, 0x00, /* 0x94-0x97 */
+ 0xED, 0xAA, 0x9C, 0xD8, 0x9C, 0xDD, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x95, 0xAE, 0x00, 0x00, 0x00, 0x00, 0x93, 0xB2, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x8C, 0x65, 0x00, 0x00, 0x9C, 0xE0, /* 0xA8-0xAB */
+ 0x9C, 0xDB, 0x00, 0x00, 0x9C, 0xE1, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x8C, 0x9B, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x89, 0xAF, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x9C, 0xE9, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x8A, 0xB6, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9C, 0xE7, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x9C, 0xE8, 0x8D, 0xA7, /* 0xC4-0xC7 */
+ 0x9C, 0xE6, 0x9C, 0xE4, 0x9C, 0xE3, 0x9C, 0xEA, /* 0xC8-0xCB */
+ 0x9C, 0xE2, 0x9C, 0xEC, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x89, 0xF9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9C, 0xEE, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x9C, 0xED, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x92, 0xA6, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x9C, 0xF1, 0x00, 0x00, 0x9C, 0xEF, 0x9C, 0xE5, /* 0xF4-0xF7 */
+ 0x8C, 0x9C, 0x00, 0x00, 0x9C, 0xF0, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x9C, 0xF4, 0x9C, 0xF3, 0x9C, 0xF5, 0x9C, 0xF2, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_62[512] = {
+ 0x9C, 0xF6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x9C, 0xF7, 0x9C, 0xF8, 0x95, 0xE8, 0x00, 0x00, /* 0x08-0x0B */
+ 0x9C, 0xFA, 0x9C, 0xF9, 0x8F, 0x5E, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x90, 0xAC, 0x89, 0xE4, 0x89, 0xFA, 0xED, 0xAB, /* 0x10-0x13 */
+ 0x9C, 0xFB, 0x00, 0x00, 0x88, 0xBD, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x90, 0xCA, 0x9C, 0xFC, /* 0x18-0x1B */
+ 0x00, 0x00, 0xE6, 0xC1, 0x9D, 0x40, 0x8C, 0x81, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x9D, 0x41, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x90, 0xED, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x9D, 0x42, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x9D, 0x43, 0x8B, 0x59, /* 0x2C-0x2F */
+ 0x9D, 0x44, 0x00, 0x00, 0x9D, 0x45, 0x9D, 0x46, /* 0x30-0x33 */
+ 0x91, 0xD5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x8C, 0xCB, 0x00, 0x00, 0x00, 0x00, 0x96, 0xDF, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x5B, /* 0x3C-0x3F */
+ 0x8F, 0x8A, 0x9D, 0x47, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0xEE, /* 0x44-0x47 */
+ 0xE7, 0xBB, 0x94, 0xE0, 0x00, 0x00, 0x8E, 0xE8, /* 0x48-0x4B */
+ 0x00, 0x00, 0x8D, 0xCB, 0x9D, 0x48, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0xC5, /* 0x50-0x53 */
+ 0x00, 0x00, 0x95, 0xA5, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x91, 0xEF, 0x00, 0x00, 0x00, 0x00, 0x9D, 0x4B, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x9D, 0x49, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x9D, 0x4C, 0x00, 0x00, 0x00, 0x00, 0x9D, 0x4A, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x9D, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x95, 0xAF, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x88, 0xB5, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x95, 0x7D, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x94, 0xE1, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x9D, 0x4E, 0x00, 0x00, 0x9D, 0x51, 0x8F, 0xB3, /* 0x7C-0x7F */
+
+ 0x8B, 0x5A, 0x00, 0x00, 0x9D, 0x4F, 0x9D, 0x56, /* 0x80-0x83 */
+ 0x8F, 0xB4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x9D, 0x50, 0x94, 0x63, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x97, 0x7D, 0x9D, 0x52, 0x9D, 0x53, /* 0x90-0x93 */
+ 0x9D, 0x57, 0x93, 0x8A, 0x9D, 0x54, 0x8D, 0x52, /* 0x94-0x97 */
+ 0x90, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x9D, 0x65, /* 0x98-0x9B */
+ 0x94, 0xB2, 0x00, 0x00, 0x91, 0xF0, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0xED, 0xAC, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0xE2, /* 0xA8-0xAB */
+ 0x9D, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x95, 0xF8, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x92, 0xEF, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x96, 0x95, 0x00, 0x00, 0x9D, 0x5A, /* 0xB8-0xBB */
+ 0x89, 0x9F, 0x92, 0x8A, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x9D, 0x63, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x92, 0x53, 0x9D, 0x5D, 0x9D, 0x64, /* 0xC4-0xC7 */
+ 0x9D, 0x5F, 0x9D, 0x66, 0x9D, 0x62, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x9D, 0x61, 0x94, 0x8F, 0x00, 0x00, 0x9D, 0x5B, /* 0xCC-0xCF */
+ 0x89, 0xFB, 0x9D, 0x59, 0x8B, 0x91, 0x91, 0xF1, /* 0xD0-0xD3 */
+ 0x9D, 0x55, 0x00, 0x00, 0x00, 0x00, 0x9D, 0x58, /* 0xD4-0xD7 */
+ 0x8D, 0x53, 0x90, 0xD9, 0x00, 0x00, 0x8F, 0xB5, /* 0xD8-0xDB */
+ 0x9D, 0x60, 0x94, 0x71, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x8B, 0x92, 0x8A, 0x67, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x8A, 0x87, 0x90, 0x40, 0x9D, 0x68, 0x9D, 0x6D, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x9D, 0x69, 0x00, 0x00, 0x8C, 0x9D, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x9D, 0x6E, 0x8E, 0x41, 0x8D, 0x89, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0x8F, 0x45, 0x9D, 0x5C, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_63[512] = {
+ 0x00, 0x00, 0x8E, 0x9D, 0x9D, 0x6B, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x77, /* 0x04-0x07 */
+ 0x9D, 0x6C, 0x88, 0xC2, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x9D, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x92, 0xA7, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x8B, 0x93, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8B, 0xB2, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9D, 0x6A, /* 0x24-0x27 */
+ 0x88, 0xA5, 0x00, 0x00, 0x00, 0x00, 0x8D, 0xC1, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x55, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x92, 0xF0, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x94, 0xD2, 0x9D, 0x70, 0x91, 0x7D, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x91, 0xA8, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x8E, 0x4A, 0x9D, 0x71, 0x00, 0x00, 0x9D, 0x73, /* 0x4C-0x4F */
+ 0x9D, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x95, 0xDF, 0x00, 0x00, 0x92, 0xBB, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x91, 0x7B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0xF9, /* 0x64-0x67 */
+ 0x8E, 0xCC, 0x9D, 0x80, 0x00, 0x00, 0x9D, 0x7E, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x90, 0x98, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x8C, 0x9E, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x9D, 0x78, 0x8F, 0xB7, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x93, 0xE6, 0x94, 0x50, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x9D, 0x76, 0x00, 0x00, 0x00, 0x00, 0x91, 0x7C, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x8E, 0xF6, 0x9D, 0x7B, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x8F, 0xB6, 0x00, 0x00, 0x9D, 0x75, 0x9D, 0x7A, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x94, 0x72, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x9D, 0x74, 0x00, 0x00, /* 0x94-0x97 */
+ 0x8C, 0x40, 0x00, 0x00, 0x00, 0x00, 0x8A, 0x7C, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9D, 0x7C, /* 0x9C-0x9F */
+ 0x97, 0xA9, 0x8D, 0xCC, 0x92, 0x54, 0x9D, 0x79, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x90, 0xDA, 0x00, 0x00, 0x8D, 0x54, /* 0xA4-0xA7 */
+ 0x90, 0x84, 0x89, 0x86, 0x91, 0x5B, 0x9D, 0x77, /* 0xA8-0xAB */
+ 0x8B, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x8C, 0x66, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x92, 0xCD, 0x9D, 0x7D, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0x7E, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x9D, 0x81, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x9D, 0x83, 0x00, 0x00, 0x00, 0x00, 0x91, 0xB5, /* 0xC0-0xC3 */
+ 0x9D, 0x89, 0x00, 0x00, 0x9D, 0x84, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x9D, 0x86, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0x60, /* 0xCC-0xCF */
+ 0x92, 0xF1, 0x00, 0x00, 0x9D, 0x87, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x97, 0x4B, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x97, 0x67, 0x8A, 0xB7, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x88, 0xAC, 0x00, 0x00, 0x9D, 0x85, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x9D, 0x82, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x8A, 0xF6, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x89, 0x87, 0xED, 0xAD, 0x9D, 0x88, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x97, 0x68, 0x00, 0x00, /* 0xF8-0xFB */
+};
+
+static unsigned char u2c_64[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x9D, 0x8C, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x91, 0xB9, 0x00, 0x00, 0x9D, 0x93, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9D, 0x8D, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x9D, 0x8A, 0x9D, 0x91, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x9D, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x9D, 0x8E, 0x00, 0x00, /* 0x24-0x27 */
+ 0x9D, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x94, 0xC0, 0x93, 0x8B, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x9D, 0x8B, 0x00, 0x00, 0x9D, 0x8F, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x8C, 0x67, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x8D, 0xEF, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x90, 0xDB, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x9D, 0x97, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x93, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xED, 0xAE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9D, 0x94, /* 0x64-0x67 */
+ 0x00, 0x00, 0x96, 0x80, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9D, 0x95, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x9D, 0x96, 0x00, 0x00, /* 0x74-0x77 */
+ 0x96, 0xCC, 0x00, 0x00, 0x90, 0xA0, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8C, 0x82, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x9D, 0x9D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x8E, 0x54, 0x9D, 0x9A, /* 0x90-0x93 */
+ 0x00, 0x00, 0x9D, 0x99, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x94, 0x51, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0xED, 0xAF, 0x93, 0xB3, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x93, 0x50, 0x9D, 0x9B, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x9D, 0x9C, 0x00, 0x00, 0x95, 0x8F, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x94, 0x64, 0x8E, 0x42, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x90, 0xEF, 0x00, 0x00, 0x96, 0x6F, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x8A, 0x68, 0x00, 0x00, 0x9D, 0xA3, /* 0xB8-0xBB */
+ 0x9D, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x97, 0x69, 0x9D, 0xA5, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x9D, 0xA1, 0x00, 0x00, 0x9D, 0xA2, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x91, 0x80, 0xED, 0xB0, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x9D, 0xA0, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x9D, 0x5E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x9D, 0xA4, 0x00, 0x00, 0x9D, 0x9F, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x9D, 0xA9, 0x9D, 0xAA, 0x93, 0x46, 0x9D, 0xAC, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x8E, 0x43, 0x9D, 0xA7, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x8B, 0x5B, 0x00, 0x00, 0x00, 0x00, 0x9D, 0xAD, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x9D, 0xA6, 0x9D, 0xB1, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x9D, 0xB0, 0x00, 0x00, 0x9D, 0xAF, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x9D, 0xB2, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x9D, 0xB4, 0x8F, 0xEF, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_65[512] = {
+ 0x9D, 0xB3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x9D, 0xB7, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x9D, 0xB5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x9D, 0xB6, 0x9D, 0x90, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9D, 0xB9, /* 0x20-0x23 */
+ 0x9D, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x9D, 0x98, 0x9D, 0xBA, /* 0x28-0x2B */
+ 0x9D, 0xAE, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x78, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x9D, 0xBB, 0x9D, 0xBC, 0x9D, 0xBE, 0x9D, 0xBD, /* 0x34-0x37 */
+ 0x9D, 0xBF, 0x89, 0xFC, 0x00, 0x00, 0x8D, 0x55, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x95, 0xFA, 0x90, 0xAD, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x8C, 0xCC, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x9D, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x9D, 0xC4, 0xED, 0xB1, 0x95, 0x71, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x8B, 0x7E, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x9D, 0xC3, 0x9D, 0xC2, 0x94, 0x73, /* 0x54-0x57 */
+ 0x9D, 0xC5, 0x8B, 0xB3, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x9D, 0xC7, 0x9D, 0xC6, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x8A, 0xB8, 0x8E, 0x55, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x93, 0xD6, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x8C, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x90, 0x94, 0x00, 0x00, 0x9D, 0xC8, 0x00, 0x00, /* 0x70-0x73 */
+ 0x90, 0xAE, 0x93, 0x47, 0x00, 0x00, 0x95, 0x7E, /* 0x74-0x77 */
+ 0x9D, 0xC9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x9D, 0xCA, 0x9D, 0xCB, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0xB6, /* 0x84-0x87 */
+ 0x9B, 0x7C, 0x90, 0xC4, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x95, 0x6B, 0x00, 0x00, 0x8D, 0xD6, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x94, 0xE3, 0x94, 0xC1, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x6C, /* 0x94-0x97 */
+ 0x00, 0x00, 0x97, 0xBF, 0x00, 0x00, 0x9D, 0xCD, /* 0x98-0x9B */
+ 0x8E, 0xCE, 0x00, 0x00, 0x00, 0x00, 0x9D, 0xCE, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x88, 0xB4, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x8B, 0xD2, 0x90, 0xCB, 0x00, 0x00, 0x95, 0x80, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9D, 0xCF, /* 0xA8-0xAB */
+ 0x8E, 0x61, 0x92, 0x66, 0x00, 0x00, 0x8E, 0x7A, /* 0xAC-0xAF */
+ 0x90, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9D, 0xD0, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x95, 0xFB, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x89, 0x97, 0x8E, 0x7B, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x9D, 0xD3, 0x00, 0x00, 0x9D, 0xD1, /* 0xC0-0xC3 */
+ 0x9D, 0xD4, 0x97, 0xB7, 0x9D, 0xD2, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0xF9, /* 0xC8-0xCB */
+ 0x9D, 0xD5, 0x00, 0x00, 0x00, 0x00, 0x91, 0xB0, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x9D, 0xD6, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8A, 0xF8, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x9D, 0xD8, 0x00, 0x00, 0x9D, 0xD7, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x9D, 0xD9, 0x9D, 0xDA, 0x8A, 0xF9, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x93, 0xFA, 0x92, 0x55, 0x8B, 0x8C, /* 0xE4-0xE7 */
+ 0x8E, 0x7C, 0x91, 0x81, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x8F, 0x7B, 0x88, 0xAE, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x9D, 0xDB, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x89, 0xA0, 0x9D, 0xDF, /* 0xF8-0xFB */
+};
+
+static unsigned char u2c_66[512] = {
+ 0xED, 0xB2, 0x00, 0x00, 0x8D, 0x56, 0x9D, 0xDE, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x8D, 0xA9, 0x8F, 0xB8, /* 0x04-0x07 */
+ 0x00, 0x00, 0xED, 0xB5, 0x9D, 0xDD, 0x00, 0x00, /* 0x08-0x0B */
+ 0x8F, 0xB9, 0x00, 0x00, 0x96, 0xBE, 0x8D, 0xA8, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0xD5, /* 0x10-0x13 */
+ 0x90, 0xCC, 0xED, 0xB3, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x9D, 0xE4, 0x00, 0x00, 0xED, 0xB7, 0x90, 0xAF, /* 0x1C-0x1F */
+ 0x89, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0xED, 0xB8, 0x8F, 0x74, 0x00, 0x00, 0x96, 0x86, /* 0x24-0x27 */
+ 0x8D, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x8F, 0xBA, 0xED, 0xB6, 0x90, 0xA5, /* 0x2C-0x2F */
+ 0x00, 0x00, 0xED, 0x47, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x9D, 0xE3, 0x9D, 0xE1, 0x9D, 0xE2, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xED, 0xB4, /* 0x38-0x3B */
+ 0x92, 0x8B, 0x00, 0x00, 0x00, 0x00, 0x9E, 0x45, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x9D, 0xE8, 0x8E, 0x9E, 0x8D, 0x57, /* 0x40-0x43 */
+ 0x9D, 0xE6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x9D, 0xE7, 0x00, 0x00, 0x90, 0x57, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9D, 0xE5, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x8E, 0x4E, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xED, 0xBA, /* 0x54-0x57 */
+ 0x00, 0x00, 0xED, 0xBB, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x9D, 0xEA, 0x9D, 0xE9, 0x9D, 0xEE, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x9D, 0xEF, 0x00, 0x00, /* 0x60-0x63 */
+ 0x9D, 0xEB, 0xED, 0xB9, 0x8A, 0x41, 0x9D, 0xEC, /* 0x64-0x67 */
+ 0x9D, 0xED, 0x94, 0xD3, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x95, 0x81, 0x8C, 0x69, /* 0x6C-0x6F */
+ 0x9D, 0xF0, 0x00, 0x00, 0x00, 0x00, 0xED, 0xBD, /* 0x70-0x73 */
+ 0x90, 0xB0, 0x00, 0x00, 0x8F, 0xBB, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x92, 0x71, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x8B, 0xC5, 0x00, 0x00, 0x9D, 0xF1, /* 0x80-0x83 */
+ 0x9D, 0xF5, 0x00, 0x00, 0x00, 0x00, 0x89, 0xC9, /* 0x84-0x87 */
+ 0x9D, 0xF2, 0x9D, 0xF4, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x9D, 0xF3, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x8F, 0x8B, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x92, 0x67, 0x88, 0xC3, /* 0x94-0x97 */
+ 0x9D, 0xF6, 0xED, 0xBE, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x9D, 0xF7, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0xED, 0xBF, 0x00, 0x00, 0x92, 0xA8, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x97, 0xEF, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x62, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x95, 0xE9, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0xED, 0xC0, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x96, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x9E, 0x41, 0x9D, 0xF9, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x9D, 0xFC, 0x00, 0x00, 0x9D, 0xFB, 0xED, 0xC1, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x9D, 0xF8, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x9E, 0x40, 0x00, 0x00, 0x00, 0x00, 0x93, 0xDC, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x9D, 0xFA, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x9E, 0x42, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x8F, 0x8C, 0x9E, 0x43, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x97, 0x6A, 0x94, 0x98, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x9E, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x9E, 0x46, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x9E, 0x47, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x9E, 0x48, 0x00, 0x00, 0x8B, 0xC8, 0x89, 0x67, /* 0xF0-0xF3 */
+ 0x8D, 0x58, 0x9E, 0x49, 0x00, 0x00, 0x9E, 0x4A, /* 0xF4-0xF7 */
+ 0x8F, 0x91, 0x91, 0x82, 0xED, 0xC2, 0xED, 0x4A, /* 0xF8-0xFB */
+ 0x99, 0xD6, 0x91, 0x5D, 0x91, 0x5C, 0x91, 0xD6, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_67[512] = {
+ 0x8D, 0xC5, 0x00, 0x00, 0x00, 0x00, 0x98, 0xF0, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x8C, 0x8E, 0x97, 0x4C, 0x00, 0x00, 0x95, 0xFC, /* 0x08-0x0B */
+ 0x00, 0x00, 0x95, 0x9E, 0xED, 0xC3, 0x9E, 0x4B, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x8D, 0xF1, 0x92, 0xBD, 0x9E, 0x4C, 0x98, 0x4E, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x5D, /* 0x18-0x1B */
+ 0x00, 0x00, 0x92, 0xA9, 0x9E, 0x4D, 0x8A, 0xFA, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x9E, 0x4E, 0x9E, 0x4F, /* 0x24-0x27 */
+ 0x96, 0xD8, 0x00, 0x00, 0x96, 0xA2, 0x96, 0x96, /* 0x28-0x2B */
+ 0x96, 0x7B, 0x8E, 0x44, 0x9E, 0x51, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x8E, 0xE9, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x96, 0x70, 0x00, 0x00, 0x9E, 0x53, 0x9E, 0x56, /* 0x34-0x37 */
+ 0x9E, 0x55, 0x00, 0x00, 0x8A, 0xF7, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x8B, 0x80, 0x00, 0x00, 0x9E, 0x52, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x9E, 0x54, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x9E, 0x57, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x90, 0x99, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x97, 0x9B, 0x88, 0xC7, /* 0x4C-0x4F */
+ 0x8D, 0xDE, 0x91, 0xBA, 0x00, 0x00, 0x8E, 0xDB, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x8F, 0xF1, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x9E, 0x5A, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x93, 0x6D, 0x00, 0x00, 0x9E, 0x58, 0x91, 0xA9, /* 0x5C-0x5F */
+ 0x9E, 0x59, 0x8F, 0xF0, 0x96, 0xDB, 0x9E, 0x5B, /* 0x60-0x63 */
+ 0x9E, 0x5C, 0x97, 0x88, 0xED, 0xC5, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x9E, 0x61, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x8D, 0x59, 0x00, 0x00, 0x94, 0x74, /* 0x6C-0x6F */
+ 0x9E, 0x5E, 0x93, 0x8C, 0x9D, 0xDC, 0x9D, 0xE0, /* 0x70-0x73 */
+ 0x00, 0x00, 0x8B, 0x6E, 0x00, 0x00, 0x94, 0x66, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x9E, 0x60, 0x00, 0x00, 0x8F, 0xBC, 0x94, 0xC2, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x9E, 0x66, 0x00, 0x00, 0x94, 0xF8, /* 0x84-0x87 */
+ 0x00, 0x00, 0x9E, 0x5D, 0x00, 0x00, 0x9E, 0x63, /* 0x88-0x8B */
+ 0x9E, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x90, 0xCD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x96, 0x8D, 0x00, 0x00, 0x97, 0xD1, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x96, 0x87, 0x00, 0x00, /* 0x98-0x9B */
+ 0x89, 0xCA, 0x8E, 0x7D, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x98, 0x67, 0x9E, 0x65, 0x90, 0x95, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x9E, 0x64, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x9E, 0x5F, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8C, 0xCD, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9E, 0x6B, /* 0xB0-0xB3 */
+ 0x9E, 0x69, 0x00, 0x00, 0x89, 0xCB, 0x9E, 0x67, /* 0xB4-0xB7 */
+ 0x9E, 0x6D, 0x9E, 0x73, 0x00, 0x00, 0xED, 0xC6, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0xED, 0xC8, 0x91, 0xC6, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x95, 0xBF, 0x00, 0x00, 0x9E, 0x75, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x95, 0x41, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x9E, 0x74, 0x94, 0x90, /* 0xCC-0xCF */
+ 0x96, 0x5E, 0x8A, 0xB9, 0x00, 0x00, 0x90, 0xF5, /* 0xD0-0xD3 */
+ 0x8F, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x92, 0xD1, 0x00, 0x00, 0x97, 0x4D, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x9E, 0x70, 0x9E, 0x6F, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x9E, 0x71, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x9E, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x9E, 0x76, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x9E, 0x6C, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x9E, 0x6A, 0x00, 0x00, 0x9E, 0x72, 0x9E, 0x68, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x92, 0x8C, 0x00, 0x00, 0x96, 0xF6, /* 0xF0-0xF3 */
+ 0x8E, 0xC4, 0x8D, 0xF2, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8D, 0xB8, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0x96, 0x8F, 0x8A, 0x60, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_68[512] = {
+ 0x00, 0x00, 0xED, 0xC9, 0x92, 0xCC, 0x93, 0xC8, /* 0x00-0x03 */
+ 0x89, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0xF0, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x90, 0xB2, 0x8C, 0x49, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x9E, 0x78, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x8D, 0x5A, 0x8A, 0x9C, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x9E, 0x7A, 0x8A, 0x94, 0x9E, 0x81, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x9E, 0x7D, 0x00, 0x00, /* 0x30-0x33 */
+ 0x90, 0xF1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x8A, 0x6A, 0x8D, 0xAA, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x8A, 0x69, 0x8D, 0xCD, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x9E, 0x7B, 0x8C, 0x85, 0x8C, 0x6A, 0x93, 0x8D, /* 0x40-0x43 */
+ 0xED, 0xCA, 0x00, 0x00, 0x9E, 0x79, 0x00, 0x00, /* 0x44-0x47 */
+ 0x88, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x9E, 0x7C, 0x9E, 0x7E, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x8B, 0xCB, 0x8C, 0x4B, 0xED, 0xC7, 0x8A, 0xBA, /* 0x50-0x53 */
+ 0x8B, 0x6A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x9E, 0x82, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x8D, 0xF7, 0x96, 0x91, 0x00, 0x00, 0x8E, 0x56, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9E, 0x83, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0x4F, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x9E, 0x8F, 0x00, 0x00, 0x89, 0xB1, 0x9E, 0x84, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x9E, 0x95, 0x9E, 0x85, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x97, 0xC0, 0x00, 0x00, 0x9E, 0x8C, /* 0x80-0x83 */
+ 0x00, 0x00, 0x94, 0x7E, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x9E, 0x94, 0x00, 0x00, 0x9E, 0x87, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0xB2, /* 0x90-0x93 */
+ 0x9E, 0x89, 0x00, 0x00, 0x00, 0x00, 0x8D, 0x5B, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9E, 0x8B, /* 0x98-0x9B */
+ 0x00, 0x00, 0x9E, 0x8A, 0x00, 0x00, 0x9E, 0x86, /* 0x9C-0x9F */
+ 0x9E, 0x91, 0x00, 0x00, 0x8F, 0xBD, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x9A, 0xEB, 0x8C, 0xE6, /* 0xA4-0xA7 */
+ 0x97, 0x9C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x9E, 0x88, 0x00, 0x00, 0x92, 0xF2, /* 0xAC-0xAF */
+ 0x8A, 0x42, 0x8D, 0xAB, 0x00, 0x00, 0x9E, 0x80, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x9E, 0x90, 0x8A, 0x81, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x9E, 0x8E, 0x9E, 0x92, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x93, 0x8E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x8A, 0xFC, 0x00, 0x00, 0x9E, 0xB0, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0xED, 0x48, 0x96, 0xC7, 0x9E, 0x97, 0x8A, 0xFB, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x9E, 0x9E, 0x00, 0x00, 0xED, 0xCB, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x96, 0x5F, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x9E, 0x9F, 0x9E, 0xA1, 0x00, 0x00, 0x9E, 0xA5, /* 0xD4-0xD7 */
+ 0x9E, 0x99, 0x00, 0x00, 0x92, 0x49, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x8F, /* 0xDC-0xDF */
+ 0x9E, 0xA9, 0x9E, 0x9C, 0x00, 0x00, 0x9E, 0xA6, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9E, 0xA0, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x90, 0x58, 0x9E, 0xAA, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x90, 0xB1, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x9E, 0xA8, 0x8A, 0xBB, 0x00, 0x00, /* 0xF8-0xFB */
+};
+
+static unsigned char u2c_69[512] = {
+ 0x98, 0x6F, 0x9E, 0x96, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x9E, 0xA4, 0x88, 0xD6, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x9E, 0x98, 0x00, 0x00, 0x00, 0x00, 0x96, 0xB8, /* 0x08-0x0B */
+ 0x9E, 0x9D, 0x90, 0x41, 0x92, 0xC5, 0x9E, 0x93, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x9E, 0xA3, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x90, 0x9A, 0x9E, 0xAD, 0x8A, 0x91, /* 0x18-0x1B */
+ 0x8C, 0x9F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x9E, 0xAF, 0x9E, 0x9A, 0x9E, 0xAE, /* 0x20-0x23 */
+ 0x00, 0x00, 0x9E, 0xA7, 0x9E, 0x9B, 0x00, 0x00, /* 0x24-0x27 */
+ 0x9E, 0xAB, 0x00, 0x00, 0x9E, 0xAC, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x9E, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x93, 0xCC, 0x00, 0x00, 0x9E, 0xA2, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x9E, 0xB9, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x9E, 0xBB, 0x00, 0x00, 0x92, 0xD6, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x97, 0x6B, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0x96, /* 0x50-0x53 */
+ 0x9E, 0xB6, 0x91, 0xC8, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x9E, 0xBC, 0x91, 0x5E, 0x00, 0x00, /* 0x58-0x5B */
+ 0x9E, 0xB3, 0x9E, 0xC0, 0x9E, 0xBF, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x93, 0xED, 0x9E, 0xBE, 0x93, 0xE8, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0xED, 0xCD, 0x00, 0x00, 0x9E, 0xC2, 0x9E, 0xB5, /* 0x68-0x6B */
+ 0x00, 0x00, 0x8B, 0xC6, 0x9E, 0xB8, 0x8F, 0x7C, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x80, /* 0x70-0x73 */
+ 0x9E, 0xBA, 0x8B, 0xC9, 0x00, 0x00, 0x9E, 0xB2, /* 0x74-0x77 */
+ 0x9E, 0xB4, 0x9E, 0xB1, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x98, 0x4F, 0x8A, 0x79, 0x9E, 0xB7, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x9E, 0xC1, 0x8A, 0x54, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x8D, 0xE5, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x89, 0x7C, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x9E, 0xD2, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x98, 0x50, 0x9E, 0xD5, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0xED, 0xCF, 0x00, 0x00, 0x00, 0x00, 0x90, 0x59, /* 0x98-0x9B */
+ 0x9E, 0xD4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x9E, 0xD3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9E, 0xD0, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x9E, 0xC4, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x9E, 0xE1, 0x9E, 0xC3, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x9E, 0xD6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9E, 0xCE, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x9E, 0xC9, 0x9E, 0xC6, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x9E, 0xC7, 0x00, 0x00, 0x9E, 0xCF, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEA, 0xA0, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x9E, 0xCC, 0x8D, 0x5C, /* 0xC8-0xCB */
+ 0x92, 0xC6, 0x91, 0x84, 0x9E, 0xCA, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x9E, 0xC5, 0x00, 0x00, 0x00, 0x00, 0x9E, 0xC8, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x97, 0x6C, 0x96, 0x8A, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x9E, 0xCD, 0x9E, 0xD7, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0xED, 0xD0, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9E, 0xDF, /* 0xE4-0xE7 */
+ 0x9E, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x9E, 0xE5, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x9E, 0xE3, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x9E, 0xDE, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x9E, 0xDD, 0x00, 0x00, 0x92, 0xCE, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x91, 0x85, 0x00, 0x00, 0x9E, 0xDB, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_6A[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x9E, 0xD9, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x9E, 0xE0, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x9E, 0xE6, 0x94, 0xF3, /* 0x08-0x0B */
+ 0x9E, 0xEC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x9E, 0xE7, 0x9E, 0xEA, /* 0x10-0x13 */
+ 0x9E, 0xE4, 0x00, 0x00, 0x00, 0x00, 0x92, 0x94, /* 0x14-0x17 */
+ 0x00, 0x00, 0x95, 0x57, 0x00, 0x00, 0x9E, 0xDA, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x9E, 0xE2, 0x8F, 0xBE, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x96, 0xCD, 0x9E, 0xF6, 0x9E, 0xE9, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x8C, 0xA0, 0x89, 0xA1, 0x8A, 0x7E, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x9E, 0xD1, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xED, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x8F, 0xBF, 0x9E, 0xEE, 0x00, 0x00, /* 0x34-0x37 */
+ 0x9E, 0xF5, 0x8E, 0xF7, 0x8A, 0x92, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x92, 0x4D, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x9E, 0xEB, 0x00, 0x00, 0xED, 0xD3, 0x9E, 0xF0, /* 0x44-0x47 */
+ 0x9E, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x8B, 0xB4, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x8B, 0x6B, 0x9E, 0xF2, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8B, 0x40, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x93, 0xC9, 0x9E, 0xF1, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x9E, 0xF3, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xED, 0xD2, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x9E, 0xED, 0xED, 0xD4, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x9E, 0xEF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0xED, 0xD5, 0x8A, 0x80, /* 0x7C-0x7F */
+
+ 0x92, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x9E, 0xFA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x9E, 0xF8, 0x8C, 0xE7, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x9E, 0xF7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9F, 0x40, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x9E, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x9E, 0xF9, 0x00, 0x00, 0x9E, 0xFB, 0x9E, 0xFC, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x9F, 0x4B, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x9F, 0x47, 0x00, 0x00, 0x9E, 0x8D, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9F, 0x46, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x9F, 0x45, 0x00, 0x00, 0x00, 0x00, 0x9F, 0x42, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x9E, 0xE8, 0x9F, 0x44, 0x9F, 0x43, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x9F, 0x49, 0x00, 0x00, 0x98, 0x45, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x9F, 0x4C, 0x8B, 0xF9, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x9F, 0x48, 0x9F, 0x4A, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0xED, 0xD6, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0xED, 0xD7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x94, 0xA5, 0x00, 0x00, 0x9F, 0x4D, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x9F, 0x51, 0x9F, 0x4E, /* 0xF8-0xFB */
+};
+
+static unsigned char u2c_6B[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x97, 0x93, 0x9F, 0x4F, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x9E, 0xDC, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x9F, 0x52, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x9F, 0x53, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x89, 0x54, 0x00, 0x00, 0x9F, 0x55, /* 0x1C-0x1F */
+ 0x8C, 0x87, 0x8E, 0x9F, 0x00, 0x00, 0x8B, 0xD3, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0xA2, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x97, 0x7E, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9F, 0x57, /* 0x34-0x37 */
+ 0x9F, 0x56, 0x9F, 0x59, 0x8B, 0x5C, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x8B, 0xD4, 0x8A, 0xBC, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9F, 0x5C, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9F, 0x5B, /* 0x44-0x47 */
+ 0x00, 0x00, 0x9F, 0x5D, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x89, 0xCC, 0x00, 0x00, 0x92, 0x56, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x9F, 0x5E, 0x00, 0x00, 0x00, 0x00, 0x8A, 0xBD, /* 0x50-0x53 */
+ 0x9F, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x9F, 0x5F, 0x00, 0x00, 0x9F, 0x61, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9F, 0x62, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x9F, 0x63, 0x8E, 0x7E, 0x90, 0xB3, /* 0x60-0x63 */
+ 0x8D, 0x9F, 0x00, 0x00, 0x95, 0x90, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x95, 0xE0, 0x98, 0x63, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x95, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8D, 0xCE, /* 0x70-0x73 */
+ 0x97, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x9F, 0x64, 0x9F, 0x65, 0x00, 0x00, 0x8E, 0x80, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9F, 0x66, /* 0x7C-0x7F */
+
+ 0x9F, 0x67, 0x00, 0x00, 0x00, 0x00, 0x9F, 0x69, /* 0x80-0x83 */
+ 0x9F, 0x68, 0x00, 0x00, 0x96, 0x77, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x8F, 0x7D, 0x8E, 0xEA, 0x8E, 0x63, /* 0x88-0x8B */
+ 0x00, 0x00, 0x9F, 0x6A, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x9F, 0x6C, 0x90, 0x42, 0x00, 0x00, /* 0x94-0x97 */
+ 0x9F, 0x6B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x9F, 0x6D, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x9F, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x9F, 0x6F, 0x9F, 0x70, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9F, 0x71, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x9F, 0x73, 0x9F, 0x72, 0x9F, 0x74, /* 0xB0-0xB3 */
+ 0x89, 0xA3, 0x92, 0x69, 0x00, 0x00, 0x9F, 0x75, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x8E, 0x45, 0x8A, 0x6B, /* 0xB8-0xBB */
+ 0x9F, 0x76, 0x00, 0x00, 0x00, 0x00, 0x93, 0x61, /* 0xBC-0xBF */
+ 0x9A, 0xCA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x8B, 0x42, 0x9F, 0x77, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9F, 0x78, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x95, 0xEA, 0x96, 0x88, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x93, 0xC5, 0x9F, 0x79, /* 0xD0-0xD3 */
+ 0x94, 0xE4, 0x00, 0x00, 0xED, 0xD8, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x94, 0xF9, 0x00, 0x00, 0x00, 0x00, 0x96, 0xD1, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9F, 0x7A, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9F, 0x7C, /* 0xE8-0xEB */
+ 0x9F, 0x7B, 0x00, 0x00, 0x00, 0x00, 0x9F, 0x7E, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9F, 0x7D, /* 0xF0-0xF3 */
+};
+
+static unsigned char u2c_6C[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x9F, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x81, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x96, 0xAF, 0x00, 0x00, 0x9F, 0x82, /* 0x10-0x13 */
+ 0x9F, 0x83, 0x00, 0x00, 0x00, 0x00, 0x8B, 0x43, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9F, 0x84, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9F, 0x86, /* 0x20-0x23 */
+ 0x9F, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x90, 0x85, 0x00, 0x00, 0x00, 0x00, 0x95, 0x58, /* 0x34-0x37 */
+ 0x89, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x94, 0xC3, 0xED, 0xD9, /* 0x3C-0x3F */
+ 0x92, 0xF3, 0x8F, 0x60, 0x8B, 0x81, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x94, 0xC4, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x8E, 0xAC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x9F, 0x88, 0x00, 0x00, 0x8A, 0xBE, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x89, 0x98, 0x00, 0x00, /* 0x58-0x5B */
+ 0xED, 0xDA, 0x93, 0xF0, 0x9F, 0x87, 0x8D, 0x5D, /* 0x5C-0x5F */
+ 0x92, 0x72, 0x00, 0x00, 0x9F, 0x89, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x9F, 0x91, 0x00, 0x00, 0x9F, 0x8A, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xED, 0xDC, /* 0x6C-0x6F */
+ 0x91, 0xBF, 0x00, 0x00, 0x8B, 0x82, 0x9F, 0x92, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x8C, 0x88, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x8B, 0x44, 0x9F, 0x90, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x9F, 0x8E, 0x9F, 0x8B, 0x97, 0x80, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0xED, 0xDB, 0x00, 0x00, /* 0x84-0x87 */
+ 0x92, 0xBE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x93, 0xD7, 0x9F, 0x8C, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x9F, 0x94, 0x00, 0x00, 0x9F, 0x93, 0x8C, 0x42, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x89, 0xAB, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x8D, 0xB9, 0x9F, 0x8D, 0x9F, 0x8F, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x96, 0x76, 0x91, 0xF2, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x97, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x9F, 0x9C, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x9F, 0x9D, 0x00, 0x00, 0x89, 0xCD, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x95, 0xA6, 0x96, 0xFB, 0x9F, 0x9F, 0x8E, 0xA1, /* 0xB8-0xBB */
+ 0x8F, 0xC0, 0x9F, 0x98, 0x9F, 0x9E, 0x89, 0x88, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x8B, 0xB5, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x9F, 0x95, 0x9F, 0x9A, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x90, 0xF2, 0x94, 0x91, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x94, 0xE5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9F, 0x97, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x96, 0x40, 0x00, 0x00, 0x9F, 0x99, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x9F, 0xA2, 0xED, 0xDD, 0x9F, 0xA0, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x9F, 0x9B, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x96, 0x41, 0x94, 0x67, 0x8B, 0x83, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x93, 0x44, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x92, 0x8D, 0x00, 0x00, 0x9F, 0xA3, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9F, 0xA1, /* 0xEC-0xEF */
+ 0x91, 0xD7, 0x9F, 0x96, 0x00, 0x00, 0x89, 0x6A, /* 0xF0-0xF3 */
+};
+
+static unsigned char u2c_6D[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0xED, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x6D, /* 0x08-0x0B */
+ 0x9F, 0xAE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x9F, 0xAD, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0xF4, /* 0x14-0x17 */
+ 0x00, 0x00, 0x9F, 0xAA, 0x00, 0x00, 0x97, 0x8C, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x93, 0xB4, 0x9F, 0xA4, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x92, 0xC3, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x89, 0x6B, 0x8D, 0x5E, 0x9F, 0xA7, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x8F, 0x46, 0x9F, 0xAC, /* 0x30-0x33 */
+ 0x00, 0x00, 0x9F, 0xAB, 0x9F, 0xA6, 0x00, 0x00, /* 0x34-0x37 */
+ 0x9F, 0xA9, 0x00, 0x00, 0x00, 0x00, 0x8A, 0x88, /* 0x38-0x3B */
+ 0x00, 0x00, 0x9F, 0xA8, 0x94, 0x68, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x97, 0xAC, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x8F, 0xF2, 0x90, 0xF3, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x9F, 0xB4, 0x9F, 0xB2, 0x00, 0x00, /* 0x58-0x5B */
+ 0x95, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9F, 0xAF, /* 0x60-0x63 */
+ 0x9F, 0xB1, 0x00, 0x00, 0x89, 0x59, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x8D, 0x5F, 0x98, 0x51, 0x00, 0x00, /* 0x68-0x6B */
+ 0x8A, 0x5C, 0x00, 0x00, 0x95, 0x82, 0xED, 0xE0, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x97, 0x81, 0x00, 0x00, 0x00, 0x00, 0x8A, 0x43, /* 0x74-0x77 */
+ 0x90, 0x5A, 0x9F, 0xB3, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x9F, 0xB8, 0x00, 0x00, 0xED, 0xDF, /* 0x84-0x87 */
+ 0x8F, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x97, 0x4F, 0x00, 0x00, 0x9F, 0xB5, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9F, 0xB0, /* 0x90-0x93 */
+ 0x00, 0x00, 0x9F, 0xB6, 0xED, 0xE1, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x97, 0xDC, 0x00, 0x00, 0x93, 0x93, /* 0x98-0x9B */
+ 0x93, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0xED, 0xE2, 0x00, 0x00, 0x00, 0x00, 0x8A, 0x55, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x89, 0x74, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x9F, 0xBC, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x9F, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x97, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x97, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x9F, 0xC6, 0x9F, 0xC0, 0x9F, 0xBD, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0xD2, /* 0xC8-0xCB */
+ 0x9F, 0xC3, 0x00, 0x00, 0x00, 0x00, 0xED, 0xE3, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x8F, 0x69, 0x9F, 0xC5, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x9F, 0xCA, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x93, 0x91, 0x9F, 0xC8, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x9F, 0xC2, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x92, 0x57, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x9F, 0xC9, 0x00, 0x00, 0x9F, 0xBE, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x9F, 0xC4, 0x00, 0x00, 0x9F, 0xCB, 0x88, 0xFA, /* 0xE8-0xEB */
+ 0x9F, 0xC1, 0x00, 0x00, 0x9F, 0xCC, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x90, 0x5B, 0xED, 0xE5, 0x8F, 0x7E, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x95, 0xA3, 0x00, 0x00, 0x8D, 0xAC, /* 0xF4-0xF7 */
+ 0xED, 0xE4, 0x9F, 0xB9, 0x9F, 0xC7, 0x93, 0x59, /* 0xF8-0xFB */
+ 0xED, 0xE6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_6E[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x90, 0xB4, 0x00, 0x00, 0x8A, 0x89, /* 0x04-0x07 */
+ 0x8D, 0xCF, 0x8F, 0xC2, 0x9F, 0xBB, 0x8F, 0x61, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8C, 0x6B, /* 0x10-0x13 */
+ 0x00, 0x00, 0x9F, 0xBA, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x9F, 0xD0, 0x8F, 0x8D, 0x8C, 0xB8, /* 0x18-0x1B */
+ 0x00, 0x00, 0x9F, 0xDF, 0x00, 0x00, 0x9F, 0xD9, /* 0x1C-0x1F */
+ 0x8B, 0x94, 0x93, 0x6E, 0x00, 0x00, 0x9F, 0xD4, /* 0x20-0x23 */
+ 0x9F, 0xDD, 0x88, 0xAD, 0x89, 0x51, 0xED, 0xE9, /* 0x24-0x27 */
+ 0x00, 0x00, 0x89, 0xB7, 0x00, 0x00, 0x9F, 0xD6, /* 0x28-0x2B */
+ 0x91, 0xAA, 0x9F, 0xCD, 0x9F, 0xCF, 0x8D, 0x60, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x9F, 0xE0, 0xED, 0xE7, 0x9F, 0xDB, 0x00, 0x00, /* 0x38-0x3B */
+ 0xED, 0xEA, 0x00, 0x00, 0x9F, 0xD3, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9F, 0xDA, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x96, 0xA9, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x9F, 0xD8, 0x9F, 0xDC, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x8C, 0xCE, 0x00, 0x00, /* 0x54-0x57 */
+ 0x8F, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x92, 0x58, /* 0x58-0x5B */
+ 0xED, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x9F, 0xD2, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x4E, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9F, 0xD5, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x9F, 0xCE, 0x93, 0x92, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x9F, 0xD1, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x9F, 0xD7, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x98, 0x70, 0x8E, 0xBC, /* 0x7C-0x7F */
+
+ 0x96, 0x9E, 0x00, 0x00, 0x9F, 0xE1, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x94, 0xAC, 0x00, 0x00, 0x00, 0x00, 0x9F, 0xED, /* 0x8C-0x8F */
+ 0x8C, 0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x8F, 0x80, 0x00, 0x00, /* 0x94-0x97 */
+ 0x9F, 0xE3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x97, 0xAD, 0x8D, 0x61, 0x00, 0x00, 0x9F, 0xF0, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x88, 0xEC, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x9F, 0xEE, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x9F, 0xE2, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9F, 0xE8, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x9F, 0xEA, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x97, 0x6E, 0x9F, 0xE5, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x93, 0x4D, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x9F, 0xE7, 0x00, 0x00, 0xED, 0xEB, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x9F, 0xEF, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x9F, 0xE9, 0x96, 0xC5, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x9F, 0xE4, 0x00, 0x00, 0x8E, 0xA0, /* 0xC8-0xCB */
+ 0x9F, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x8A, 0x8A, 0x00, 0x00, 0x9F, 0xE6, /* 0xD0-0xD3 */
+ 0x9F, 0xEB, 0x9F, 0xEC, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x91, 0xEA, 0x91, 0xD8, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x9F, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x9F, 0xFA, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x9F, 0xF8, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x93, 0x48, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x42, /* 0xF4-0xF7 */
+ 0x9F, 0xF5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0x9F, 0xF6, 0x9F, 0xDE, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_6F[512] = {
+ 0x00, 0x00, 0x8B, 0x99, 0x95, 0x59, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x8E, 0xBD, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x8D, 0x97, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0x52, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x9F, 0xF2, 0x00, 0x00, 0xE0, 0x41, /* 0x10-0x13 */
+ 0x89, 0x89, 0x91, 0x86, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x94, 0x99, 0x00, 0x00, 0x8A, 0xBF, 0x97, 0xF8, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x9F, /* 0x28-0x2B */
+ 0x92, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x9F, 0xF9, 0x9F, 0xFB, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x91, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0x40, 0x9F, 0xF7, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x9F, 0xF1, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x8A, 0xC1, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x8C, 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0xE0, 0x4E, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x49, /* 0x58-0x5B */
+ 0x90, 0xF6, 0x00, 0x00, 0x00, 0x00, 0x8A, 0x83, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x8F, 0x81, 0x00, 0x00, 0xE0, 0x52, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0xE0, 0x4B, 0x92, 0xAA, 0xE0, 0x48, /* 0x6C-0x6F */
+ 0x92, 0xD7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0xE0, 0x6B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0xE0, 0x45, 0x00, 0x00, 0xE0, 0x44, 0x00, 0x00, /* 0x78-0x7B */
+ 0xE0, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0xE0, 0x47, 0xE0, 0x46, 0xE0, 0x4C, 0x00, 0x00, /* 0x80-0x83 */
+ 0x90, 0x9F, 0x00, 0x00, 0xE0, 0x43, 0x00, 0x00, /* 0x84-0x87 */
+ 0xED, 0xEC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0x4F, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xE0, 0x50, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8A, 0xC0, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0xE0, 0x55, 0x00, 0x00, 0xE0, 0x54, /* 0xA0-0xA3 */
+ 0xE0, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0x59, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x93, 0x62, 0x00, 0x00, 0xE0, 0x53, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0xED, 0xED, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xE0, 0x57, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x8C, 0x83, 0x91, 0xF7, 0xE0, 0x51, 0x94, 0x5A, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0x58, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xE0, 0x5D, 0xE0, 0x5B, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xE0, 0x5E, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x61, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x5A, /* 0xDC-0xDF */
+ 0x8D, 0x8A, 0x94, 0x47, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x9F, 0xB7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x94, /* 0xE8-0xEB */
+ 0xE0, 0x5C, 0x00, 0x00, 0xE0, 0x60, 0x91, 0xF3, /* 0xEC-0xEF */
+ 0x00, 0x00, 0xE0, 0x5F, 0x00, 0x00, 0xE0, 0x4A, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0xED, 0xEE, 0xE8, 0x89, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0x64, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0x68, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_70[512] = {
+ 0x00, 0x00, 0xE0, 0x66, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0xED, 0xEF, 0x00, 0x00, 0xED, 0xF0, /* 0x04-0x07 */
+ 0x00, 0x00, 0xE0, 0x62, 0x00, 0x00, 0xE0, 0x63, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x67, /* 0x0C-0x0F */
+ 0x00, 0x00, 0xE0, 0x65, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x95, 0x6D, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0xE0, 0x6D, 0x00, 0x00, 0xE0, 0x6A, 0xE0, 0x69, /* 0x18-0x1B */
+ 0x00, 0x00, 0xE0, 0x6C, 0x93, 0xD2, 0xE0, 0x6E, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x92, 0x95, 0x91, 0xEB, /* 0x24-0x27 */
+ 0xED, 0xF1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x90, 0xA3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xE0, 0x6F, 0x00, 0x00, 0xE0, 0x71, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0x70, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x9F, 0xF3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0xE0, 0x72, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x93, 0xE5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x73, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0xCE, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x94, /* 0x6C-0x6F */
+ 0x8A, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x8B, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x8E, 0xDC, 0x8D, 0xD0, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0xED, 0xF2, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x98, 0x46, 0x90, 0x86, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x89, 0x8A, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0x75, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0xE0, 0x74, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xED, 0xF3, /* 0xA8-0xAB */
+ 0xE0, 0x78, 0x92, 0x59, 0xE0, 0x7B, 0xE0, 0x76, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x7A, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0xE0, 0x79, 0x93, 0x5F, 0x88, 0xD7, 0xED, 0x46, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x97, 0xF3, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x7D, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x47, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0xE0, 0x80, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0xE0, 0x7E, 0x00, 0x00, 0xE0, 0x7C, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0xE0, 0x77, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x96, 0x42, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0xE0, 0x82, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_71[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0xED, 0xF5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0xE0, 0x81, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xED, 0xF4, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x89, 0x8B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0xE0, 0x84, 0x95, 0xB0, 0x00, 0x00, /* 0x18-0x1B */
+ 0xE0, 0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x96, 0xB3, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x8F, 0xC5, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x91, 0x52, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x8F, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0xED, 0xF7, 0xED, 0xF8, /* 0x44-0x47 */
+ 0x00, 0x00, 0x97, 0xF9, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0xE0, 0x8A, 0x00, 0x00, 0x90, 0xF7, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0xE0, 0x86, 0xE0, 0x8B, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x89, 0x8C, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0xED, 0xF6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0x89, 0x00, 0x00, /* 0x60-0x63 */
+ 0x94, 0x81, 0xE0, 0x85, 0xE0, 0x88, 0x8F, 0xC6, /* 0x64-0x67 */
+ 0x00, 0x00, 0x94, 0xCF, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0xE0, 0x8C, 0x00, 0x00, 0x8E, 0xCF, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x90, 0xF8, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0xE0, 0x8F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0xE0, 0x87, 0x00, 0x00, 0x8C, 0x46, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x8D, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x97, 0x6F, 0xE0, 0x90, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0xEA, 0xA4, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8F, 0x6E, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xE0, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0xE0, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x94, 0x4D, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xE0, 0x94, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0x95, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0xED, 0xFA, 0x00, 0x00, 0x94, 0x52, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x93, 0x95, 0xE0, 0x97, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0x99, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x97, 0xD3, 0x00, 0x00, 0xE0, 0x96, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xE0, 0x98, 0x89, 0x8D, 0x00, 0x00, 0xE0, 0x93, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9A, 0x7A, /* 0xDC-0xDF */
+ 0xE0, 0x9A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x91, 0x87, 0x8E, 0x57, 0xE0, 0x9C, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0xE0, 0x9B, 0x90, 0x43, 0x99, 0xD7, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0xE0, 0x9D, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xE0, 0x9F, 0x00, 0x00, 0xE0, 0x8E, /* 0xF8-0xFB */
+ 0xE0, 0x9E, 0x00, 0x00, 0xED, 0xFB, 0xE0, 0xA0, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_72[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x94, 0x9A, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0xE0, 0xA1, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xE0, 0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xA3, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0xE0, 0xA4, 0x00, 0x00, 0x92, 0xDC, 0x00, 0x00, /* 0x28-0x2B */
+ 0xE0, 0xA6, 0xE0, 0xA5, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xE0, 0xA7, 0x00, 0x00, 0xE0, 0xA8, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x8E, 0xDD, 0x95, 0x83, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x96, 0xEA, 0xE0, 0xA9, /* 0x38-0x3B */
+ 0xE0, 0xAA, 0x91, 0x75, 0x8E, 0xA2, 0xE0, 0xAB, /* 0x3C-0x3F */
+ 0xE0, 0xAC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0xAD, 0x95, 0xD0, /* 0x44-0x47 */
+ 0x94, 0xC5, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xAE, /* 0x48-0x4B */
+ 0x94, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x92, 0xAB, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0xE0, 0xAF, 0x89, 0xE5, 0x00, 0x00, 0x8B, 0x8D, /* 0x58-0x5B */
+ 0x00, 0x00, 0x96, 0xC4, 0x00, 0x00, 0x96, 0xB4, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x89, 0xB2, 0x98, 0x53, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x71, /* 0x64-0x67 */
+ 0x00, 0x00, 0x95, 0xA8, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x90, 0xB5, 0x00, 0x00, /* 0x70-0x73 */
+ 0xE0, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x93, 0xC1, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x8C, 0xA1, 0xE0, 0xB1, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x8D, 0xD2, 0xE0, 0xB3, 0xE0, 0xB2, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xB4, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0xB5, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0xB6, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x8B, 0x5D, 0x00, 0x00, 0xE0, 0xB7, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xB8, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x8C, 0xA2, 0x00, 0x00, 0x00, 0x00, 0x94, 0xC6, /* 0xAC-0xAF */
+ 0x00, 0x00, 0xED, 0xFC, 0xE0, 0xBA, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x8F, 0xF3, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xE0, 0xB9, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0xEE, 0x40, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x8B, 0xB6, 0xE0, 0xBB, /* 0xC0-0xC3 */
+ 0xE0, 0xBD, 0x00, 0x00, 0xE0, 0xBC, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0xBE, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x8C, 0xCF, 0x00, 0x00, 0xE0, 0xBF, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8B, 0xE7, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x91, 0x5F, 0x00, 0x00, 0x8D, 0x9D, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0xE0, 0xC1, 0xE0, 0xC2, 0xE0, 0xC0, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x8E, 0xEB, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x93, 0xC6, 0x8B, 0xB7, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xC4, /* 0xF4-0xF7 */
+ 0x92, 0x4B, 0xE0, 0xC3, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x98, 0x54, 0x94, 0x82, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_73[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0xC7, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0xC9, 0xE0, 0xC6, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0xD2, /* 0x18-0x1B */
+ 0xE0, 0xC8, 0xE0, 0xCA, 0x00, 0x00, 0x97, 0xC2, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0xEE, 0x41, 0xE0, 0xCE, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0xE0, 0xCD, 0x92, 0x96, 0x94, 0x4C, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x8C, 0xA3, 0xE0, 0xCC, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0xE0, 0xCB, 0x00, 0x00, 0x97, 0x50, 0x97, 0x51, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0xCF, 0x89, 0x8E, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x8D, 0x96, 0x8E, 0x82, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0xD0, 0xE0, 0xD1, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xD3, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8F, 0x62, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0xE0, 0xD5, 0x00, 0x00, 0xE0, 0xD4, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xE0, 0xD6, 0x00, 0x00, 0x8A, 0x6C, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0xE0, 0xD8, 0x00, 0x00, 0xEE, 0x43, /* 0x74-0x77 */
+ 0xE0, 0xD7, 0x00, 0x00, 0xE0, 0xDA, 0xE0, 0xD9, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x8C, 0xBA, 0x00, 0x00, 0x00, 0x00, 0x97, 0xA6, /* 0x84-0x87 */
+ 0x00, 0x00, 0x8B, 0xCA, 0x00, 0x00, 0x89, 0xA4, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x8B, 0xE8, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x8A, 0xDF, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x97, 0xE6, 0xE0, 0xDC, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xDE, /* 0xB8-0xBB */
+ 0x00, 0x00, 0xEE, 0x44, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0xE0, 0xDF, 0x00, 0x00, 0x89, 0xCF, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0xE0, 0xDB, 0xEE, 0x45, 0x8E, 0x58, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x92, 0xBF, 0xE0, 0xDD, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0xEE, 0x48, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0xEE, 0x46, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0xE2, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x8E, 0xEC, 0x00, 0x00, 0x00, 0x00, 0xEE, 0x47, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0xE0, 0xE0, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x8C, 0x5D, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x94, 0xC7, 0xE0, 0xE1, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0xE0, 0xFC, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0xEE, 0x4A, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0xE0, 0xE7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0x8C, 0xBB, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_74[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8B, 0x85, /* 0x00-0x03 */
+ 0x00, 0x00, 0xE0, 0xE4, 0x97, 0x9D, 0xEE, 0x49, /* 0x04-0x07 */
+ 0x00, 0x00, 0x97, 0xAE, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x91, 0xF4, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0xE0, 0xE6, 0xEE, 0x4B, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0xEE, 0x4D, 0xEE, 0x4C, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0xEE, 0x4E, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0xE8, 0x97, 0xD4, /* 0x30-0x33 */
+ 0x8B, 0xD5, 0x94, 0xFA, 0x94, 0x69, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0xE9, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xEB, /* 0x3C-0x3F */
+ 0x00, 0x00, 0xE0, 0xEE, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0xE0, 0xEA, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0xE0, 0xED, 0x8C, 0xE8, 0x89, 0x6C, /* 0x58-0x5B */
+ 0xE0, 0xEF, 0x00, 0x00, 0x90, 0x90, 0xE0, 0xEC, /* 0x5C-0x5F */
+ 0x97, 0xDA, 0x00, 0x00, 0xEE, 0x4F, 0xE0, 0xF2, /* 0x60-0x63 */
+ 0xEA, 0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0xE0, 0xF0, 0xE0, 0xF3, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xE5, /* 0x6C-0x6F */
+ 0xE0, 0xF1, 0x00, 0x00, 0x00, 0x00, 0x8D, 0xBA, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0xF4, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0xF5, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x9E, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0xEE, 0x50, 0x00, 0x00, 0xE0, 0xF6, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0xF7, 0xEE, 0x51, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0xE3, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xF8, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x8A, 0xC2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x8E, 0xA3, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0xF9, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFA, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xE0, 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x89, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0xE1, 0x40, 0x00, 0x00, 0x95, 0x5A, 0xE1, 0x41, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x8A, 0xA2, 0xE1, 0x42, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0xE1, 0x43, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0x44, 0x00, 0x00, /* 0xEC-0xEF */
+ 0xE1, 0x46, 0xE1, 0x47, 0xE1, 0x45, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x95, 0x72, 0xE1, 0x49, /* 0xF4-0xF7 */
+ 0xE1, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+};
+
+static unsigned char u2c_75[512] = {
+ 0x00, 0x00, 0xEE, 0x52, 0x00, 0x00, 0xE1, 0x4B, /* 0x00-0x03 */
+ 0xE1, 0x4A, 0xE1, 0x4C, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0xE1, 0x4D, 0xE1, 0x4F, 0xE1, 0x4E, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x8D, 0x99, 0x00, 0x00, 0xE1, 0x51, /* 0x10-0x13 */
+ 0x00, 0x00, 0xE1, 0x50, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x8A, 0xC3, 0x00, 0x00, 0x90, 0x72, 0x00, 0x00, /* 0x18-0x1B */
+ 0x93, 0x5B, 0x00, 0x00, 0xE1, 0x52, 0x90, 0xB6, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x59, /* 0x20-0x23 */
+ 0x00, 0x00, 0x89, 0x99, 0xE1, 0x53, 0x00, 0x00, /* 0x24-0x27 */
+ 0x97, 0x70, 0x00, 0x00, 0x00, 0x00, 0x95, 0xE1, /* 0x28-0x2B */
+ 0xE1, 0x54, 0x00, 0x00, 0x00, 0x00, 0xED, 0x8C, /* 0x2C-0x2F */
+ 0x93, 0x63, 0x97, 0x52, 0x8D, 0x62, 0x90, 0x5C, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x6A, /* 0x34-0x37 */
+ 0x99, 0xB2, 0x00, 0x00, 0x92, 0xAC, 0x89, 0xE6, /* 0x38-0x3B */
+ 0xE1, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0xE1, 0x56, 0x00, 0x00, 0xE1, 0x5B, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0xE1, 0x59, 0xE1, 0x58, 0x9D, 0xC0, /* 0x48-0x4B */
+ 0x8A, 0x45, 0xE1, 0x57, 0x00, 0x00, 0x88, 0xD8, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x94, 0xA8, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x94, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x97, 0xAF, 0xE1, 0x5C, 0xE1, 0x5A, /* 0x58-0x5B */
+ 0x92, 0x7B, 0x90, 0xA4, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x94, 0xA9, 0x00, 0x00, 0x95, 0x4C, 0x00, 0x00, /* 0x60-0x63 */
+ 0xE1, 0x5E, 0x97, 0xAA, 0x8C, 0x6C, 0xE1, 0x5F, /* 0x64-0x67 */
+ 0x00, 0x00, 0xE1, 0x5D, 0x94, 0xD4, 0xE1, 0x60, /* 0x68-0x6B */
+ 0x00, 0x00, 0xE1, 0x61, 0x00, 0x00, 0xEE, 0x53, /* 0x6C-0x6F */
+ 0x88, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x8F, 0xF4, /* 0x70-0x73 */
+ 0xE1, 0x66, 0x00, 0x00, 0xE1, 0x63, 0x93, 0xEB, /* 0x74-0x77 */
+ 0xE1, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8B, 0x45, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0x69, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0x64, 0xE1, 0x65, /* 0x84-0x87 */
+ 0x00, 0x00, 0xE1, 0x68, 0xE1, 0x67, 0x95, 0x44, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x91, 0x61, 0x91, 0x60, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x8B, 0x5E, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0xE1, 0x6A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0x6B, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0xE1, 0x6C, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE1, 0x6E, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0xE1, 0x6D, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x75, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0xE1, 0x76, 0x94, 0xE6, 0xE1, 0x70, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0xE1, 0x72, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0xE1, 0x74, 0x90, 0x5D, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0xE1, 0x75, 0xE1, 0x73, 0x8E, 0xBE, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0x6F, 0xE1, 0x71, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x95, 0x61, 0x00, 0x00, 0x8F, 0xC7, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0x78, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0xE1, 0x77, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0x79, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x8E, 0xA4, 0x8D, 0xAD, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x93, 0x97, 0xE1, 0x7A, 0x00, 0x00, 0x92, 0xC9, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0x7C, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x97, 0x9F, 0xE1, 0x7B, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x91, 0x89, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0xE1, 0x82, 0x00, 0x00, 0xE1, 0x84, 0xE1, 0x85, /* 0xF0-0xF3 */
+ 0x92, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0x83, 0x00, 0x00, /* 0xF8-0xFB */
+ 0xE1, 0x80, 0x00, 0x00, 0xE1, 0x7D, 0xE1, 0x7E, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_76[512] = {
+ 0x00, 0x00, 0xE1, 0x81, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0xE1, 0x88, 0x00, 0x00, 0xE1, 0x86, /* 0x08-0x0B */
+ 0x00, 0x00, 0xE1, 0x87, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE1, 0x89, /* 0x1C-0x1F */
+ 0xE1, 0x8B, 0xE1, 0x8C, 0xE1, 0x8D, 0x00, 0x00, /* 0x20-0x23 */
+ 0xE1, 0x8E, 0x00, 0x00, 0x00, 0x00, 0xE1, 0x8A, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xE1, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0xE1, 0x8F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE1, 0x91, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x97, 0xC3, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0x94, 0xE1, 0x92, /* 0x44-0x47 */
+ 0xE1, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x8A, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x96, 0xFC, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x95, 0xC8, 0x00, 0x00, /* 0x54-0x57 */
+ 0xE1, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0xE1, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0xE1, 0x97, 0xE1, 0x98, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE1, 0x9C, /* 0x64-0x67 */
+ 0xE1, 0x99, 0xE1, 0x9A, 0xE1, 0x9B, 0x00, 0x00, /* 0x68-0x6B */
+ 0xE1, 0x9D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xE1, 0x9E, 0x00, 0x00, 0xE1, 0x9F, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0xA0, 0x00, 0x00, /* 0x74-0x77 */
+ 0xE1, 0xA1, 0x00, 0x00, 0x94, 0xAD, 0x93, 0x6F, /* 0x78-0x7B */
+ 0xE1, 0xA2, 0x94, 0x92, 0x95, 0x53, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0xE1, 0xA3, 0x00, 0x00, 0xEE, 0x54, 0xE1, 0xA4, /* 0x80-0x83 */
+ 0x93, 0x49, 0x00, 0x00, 0x8A, 0x46, 0x8D, 0x63, /* 0x84-0x87 */
+ 0xE1, 0xA5, 0x00, 0x00, 0x00, 0x00, 0xE1, 0xA6, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0xA7, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x8E, 0x48, 0x00, 0x00, 0x00, 0x00, 0xE1, 0xA9, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0xA8, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0xE1, 0xAA, 0xE1, 0xAB, 0xEE, 0x57, /* 0x98-0x9B */
+ 0xEE, 0x55, 0x00, 0x00, 0xEE, 0x56, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0xEE, 0x58, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x94, 0xE7, 0x00, 0x00, /* 0xAC-0xAF */
+ 0xE1, 0xAC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0xE1, 0xAD, 0x00, 0x00, 0x00, 0x00, 0xEA, 0x89, /* 0xB4-0xB7 */
+ 0xE1, 0xAE, 0xE1, 0xAF, 0xE1, 0xB0, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x4D, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0xB1, 0x94, 0x75, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x96, 0x7E, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x89, 0x6D, 0x00, 0x00, 0x89, 0x76, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0xE1, 0xB2, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0xB4, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0xB3, 0x93, 0x90, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0xB7, /* 0xD8-0xDB */
+ 0x9F, 0x58, 0x00, 0x00, 0xE1, 0xB5, 0x96, 0xBF, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xE1, 0xB6, 0x00, 0x00, 0x8A, 0xC4, /* 0xE0-0xE3 */
+ 0x94, 0xD5, 0xE1, 0xB7, 0x00, 0x00, 0xE1, 0xB8, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0xB9, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x96, 0xDA, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x96, 0xD3, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x92, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x91, 0x8A, 0x00, 0x00, 0x00, 0x00, 0xE1, 0xBB, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0x8F, 0x82, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_77[512] = {
+ 0x00, 0x00, 0x8F, 0xC8, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0xE1, 0xBE, 0x00, 0x00, 0x00, 0x00, 0xE1, 0xBD, /* 0x04-0x07 */
+ 0xE1, 0xBC, 0x94, 0xFB, 0x00, 0x00, 0x8A, 0xC5, /* 0x08-0x0B */
+ 0x8C, 0xA7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE1, 0xC4, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0xC1, 0x90, 0x5E, /* 0x1C-0x1F */
+ 0x96, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0xE1, 0xC0, 0xE1, 0xC2, 0xE1, 0xC3, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0xE1, 0xBF, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE1, 0xC5, /* 0x34-0x37 */
+ 0xE1, 0xC6, 0x00, 0x00, 0x92, 0xAD, 0x00, 0x00, /* 0x38-0x3B */
+ 0x8A, 0xE1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x92, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0xEE, 0x5A, 0xE1, 0xC7, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0xC8, 0xE1, 0xCB, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x90, 0x87, 0x00, 0x00, 0x93, 0xC2, /* 0x60-0x63 */
+ 0x00, 0x00, 0xE1, 0xCC, 0x96, 0x72, 0x00, 0x00, /* 0x64-0x67 */
+ 0xE1, 0xC9, 0x00, 0x00, 0x00, 0x00, 0xE1, 0xCA, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0xE1, 0xCF, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0xCE, 0xE1, 0xCD, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE1, 0xD1, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0xD0, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xE1, 0xD2, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0xD4, 0x00, 0x00, /* 0x9C-0x9F */
+ 0xE1, 0xD3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x95, 0xCB, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x8F, 0x75, 0x97, 0xC4, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0xE1, 0xD5, 0x00, 0x00, 0x00, 0x00, 0x93, 0xB5, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0xD6, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xE1, 0xD7, 0x00, 0x00, 0xE1, 0xDB, /* 0xB8-0xBB */
+ 0xE1, 0xD9, 0xE1, 0xDA, 0x00, 0x00, 0xE1, 0xD8, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE1, 0xDC, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0xE1, 0xDD, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE1, 0xDE, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0xDF, 0x96, 0xB5, /* 0xD8-0xDB */
+ 0xE1, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x96, 0xEE, 0xE1, 0xE1, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x92, 0x6D, 0x00, 0x00, 0x94, 0x8A, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x8B, 0xE9, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x92, 0x5A, 0xE1, 0xE2, 0x8B, 0xB8, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0xCE, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0xE1, 0xE3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_78[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x8D, 0xBB, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0xE1, 0xE4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0xE5, 0x00, 0x00, /* 0x10-0x13 */
+ 0x8C, 0xA4, 0x8D, 0xD3, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0xE1, 0xE7, 0xEE, 0x5C, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x93, 0x75, 0x8D, 0xD4, 0x8B, 0x6D, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x96, 0x43, 0x00, 0x00, /* 0x30-0x33 */
+ 0x94, 0x6A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x93, 0x76, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8D, 0x7B, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0xE1, 0xE9, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0xEE, 0x5D, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x8F, 0xC9, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0xEE, 0x5E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0xB0, /* 0x68-0x6B */
+ 0x8D, 0x64, 0x00, 0x00, 0x00, 0x00, 0x8C, 0xA5, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x94, 0xA1, 0x00, 0x00, /* 0x70-0x73 */
+ 0xE1, 0xEB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0xEE, 0x5F, 0x00, 0x00, /* 0x78-0x7B */
+ 0xE1, 0xED, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x8C, 0xE9, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0xEC, 0x92, 0xF4, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0xE1, 0xEF, 0x8A, 0x56, 0xE1, 0xEA, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x94, 0xE8, 0x00, 0x00, 0x89, 0x4F, /* 0x90-0x93 */
+ 0x00, 0x00, 0x8D, 0xEA, 0x00, 0x00, 0x98, 0x71, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0xEE, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE1, 0xF0, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0xC9, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x90, 0xD7, 0xE1, 0xF2, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE1, 0xF3, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0xE1, 0xF1, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x8A, 0x6D, 0x00, 0x00, /* 0xB8-0xBB */
+ 0xE1, 0xF9, 0x00, 0x00, 0xE1, 0xF8, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x8E, 0xA5, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0xE1, 0xFA, 0xE1, 0xF5, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0xFB, 0xE1, 0xF6, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x94, 0xD6, 0xE1, 0xF4, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xE1, 0xF7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0xE2, 0x41, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2, 0x40, /* 0xE4-0xE7 */
+ 0x96, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0xE1, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x88, 0xE9, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xE2, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0xE2, 0x42, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_79[512] = {
+ 0x00, 0x00, 0x8F, 0xCA, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2, 0x44, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x91, 0x62, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0xE2, 0x46, 0xE2, 0x45, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0xE2, 0x47, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0xE6, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0xE8, 0xE2, 0x49, /* 0x28-0x2B */
+ 0xE2, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xEE, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x8E, 0xA6, 0x00, 0x00, /* 0x38-0x3B */
+ 0x97, 0xE7, 0x00, 0x00, 0x8E, 0xD0, 0x00, 0x00, /* 0x3C-0x3F */
+ 0xE2, 0x4A, 0x8C, 0x56, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8B, 0x5F, /* 0x44-0x47 */
+ 0x8B, 0x46, 0x8E, 0x83, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x97, 0x53, 0x00, 0x00, 0x00, 0x00, 0xE2, 0x50, /* 0x50-0x53 */
+ 0x00, 0x00, 0xE2, 0x4F, 0x91, 0x63, 0xE2, 0x4C, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0xE2, 0x4E, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x8F, 0x6A, 0x90, 0x5F, 0xE2, 0x4D, /* 0x5C-0x5F */
+ 0xE2, 0x4B, 0x00, 0x00, 0x94, 0x49, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x8F, 0xCB, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x95, 0x5B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x8D, 0xD5, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x98, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0xE2, 0x51, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2, 0x52, /* 0x7C-0x7F */
+
+ 0xE2, 0x68, 0x8B, 0xD6, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x98, 0x5C, 0x91, 0x54, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0xE2, 0x53, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x89, 0xD0, 0x92, 0xF5, 0x95, 0x9F, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0xEE, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEE, 0x66, /* 0x98-0x9B */
+ 0x00, 0x00, 0xE2, 0x54, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x8B, 0x9A, 0xE2, 0x55, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0xE2, 0x57, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0xE2, 0x58, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x94, 0x48, 0x00, 0x00, 0x00, 0x00, 0xE2, 0x59, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xE2, 0x5A, 0xE2, 0x5B, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x8B, 0xD7, 0x89, 0xD1, 0x93, 0xC3, /* 0xBC-0xBF */
+ 0x8F, 0x47, 0x8E, 0x84, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xE2, 0x5C, 0x00, 0x00, 0x8F, 0x48, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x89, 0xC8, 0x95, 0x62, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0xE2, 0x5D, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x94, 0xE9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0x64, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xE2, 0x60, 0x00, 0x00, 0xE2, 0x61, /* 0xE0-0xE3 */
+ 0x94, 0x89, 0x00, 0x00, 0x90, 0x60, 0xE2, 0x5E, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x92, 0x81, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0xE2, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x8F, 0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0xDA, /* 0xF8-0xFB */
+};
+
+static unsigned char u2c_7A[512] = {
+ 0x8B, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0xE2, 0x62, 0x00, 0x00, 0x00, 0x00, 0x92, 0xF6, /* 0x08-0x0B */
+ 0x00, 0x00, 0xE2, 0x63, 0x90, 0xC5, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x96, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x95, 0x42, /* 0x14-0x17 */
+ 0xE2, 0x64, 0xE2, 0x65, 0x92, 0x74, 0x00, 0x00, /* 0x18-0x1B */
+ 0x97, 0xC5, 0x00, 0x00, 0x00, 0x00, 0xE2, 0x67, /* 0x1C-0x1F */
+ 0xE2, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x8E, 0xED, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0xE2, 0x69, 0x88, 0xEE, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2, 0x6C, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2, 0x6A, /* 0x38-0x3B */
+ 0x89, 0xD2, 0x8C, 0x6D, 0xE2, 0x6B, 0x8D, 0x65, /* 0x3C-0x3F */
+ 0x8D, 0x92, 0x00, 0x00, 0x95, 0xE4, 0xE2, 0x6D, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x96, 0x73, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0xE2, 0x6F, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x90, 0xCF, 0x89, 0x6E, 0x89, 0xB8, /* 0x4C-0x4F */
+ 0x88, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2, 0x6E, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0xE2, 0x70, 0xE2, 0x71, 0x8F, 0xF5, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0xE2, 0x72, 0x00, 0x00, 0x8A, 0x6E, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xE2, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x8C, 0x8A, 0x00, 0x00, 0x8B, 0x86, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0xE2, 0x75, 0x8B, 0xF3, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0xE2, 0x76, 0x00, 0x00, 0x90, 0xFA, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x93, 0xCB, 0x00, 0x00, 0x90, 0xDE, /* 0x80-0x83 */
+ 0x8D, 0xF3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0xE2, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x92, 0x82, 0x91, 0x8B, /* 0x90-0x93 */
+ 0x00, 0x00, 0xE2, 0x79, 0xE2, 0x7B, 0xE2, 0x78, /* 0x94-0x97 */
+ 0xE2, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8C, 0x41, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0xE2, 0x7C, 0x8C, 0x45, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x8B, 0x87, 0x97, 0x71, /* 0xAC-0xAF */
+ 0xE2, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0xE2, 0x80, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x89, 0x4D, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2, 0x83, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8A, 0x96, /* 0xC0-0xC3 */
+ 0xE2, 0x82, 0xE2, 0x81, 0x00, 0x00, 0xE2, 0x85, /* 0xC4-0xC7 */
+ 0xE2, 0x7D, 0x00, 0x00, 0xE2, 0x86, 0x97, 0xA7, /* 0xC8-0xCB */
+ 0x00, 0x00, 0xE2, 0x87, 0x00, 0x00, 0xE2, 0x88, /* 0xCC-0xCF */
+ 0x00, 0x00, 0xEE, 0x67, 0x9A, 0xF2, 0xE2, 0x8A, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0xE2, 0x89, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0xE2, 0x8B, 0xE2, 0x8C, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x97, 0xB3, 0xE2, 0x8D, 0x00, 0x00, 0xE8, 0xED, /* 0xDC-0xDF */
+ 0x8F, 0xCD, 0xE2, 0x8E, 0xE2, 0x8F, 0x8F, 0x76, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x93, 0xB6, 0xE2, 0x90, 0xEE, 0x68, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x92, 0x47, 0xEE, 0x6A, /* 0xE8-0xEB */
+ 0x00, 0x00, 0xE2, 0x91, 0x00, 0x00, 0x92, 0x5B, /* 0xEC-0xEF */
+ 0xE2, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x8B, 0xA3, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x99, 0x5E, 0x92, 0x7C, 0x8E, 0xB1, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8A, 0xC6, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_7B[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0xE2, 0x93, 0x00, 0x00, /* 0x00-0x03 */
+ 0xE2, 0xA0, 0x00, 0x00, 0xE2, 0x96, 0x00, 0x00, /* 0x04-0x07 */
+ 0x8B, 0x88, 0x00, 0x00, 0xE2, 0x95, 0xE2, 0xA2, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2, 0x94, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x8F, 0xCE, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0xE2, 0x98, 0xE2, 0x99, 0x00, 0x00, 0x93, 0x4A, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0xE2, 0x9A, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x8A, 0x7D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x90, 0x79, 0x95, 0x84, 0x00, 0x00, /* 0x24-0x27 */
+ 0xE2, 0x9C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x91, 0xE6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2, 0x97, /* 0x30-0x33 */
+ 0x00, 0x00, 0xE2, 0x9B, 0xE2, 0x9D, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x8D, 0xF9, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0xE2, 0xA4, 0x95, 0x4D, 0x00, 0x00, /* 0x44-0x47 */
+ 0x94, 0xA4, 0x93, 0x99, 0x00, 0x00, 0x8B, 0xD8, /* 0x48-0x4B */
+ 0xE2, 0xA3, 0xE2, 0xA1, 0x00, 0x00, 0x94, 0xB3, /* 0x4C-0x4F */
+ 0xE2, 0x9E, 0x92, 0x7D, 0x93, 0x9B, 0x00, 0x00, /* 0x50-0x53 */
+ 0x93, 0x9A, 0x00, 0x00, 0x8D, 0xF4, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0xE2, 0xB6, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0xE2, 0xA6, 0x00, 0x00, 0xE2, 0xA8, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0xE2, 0xAB, 0x00, 0x00, 0xE2, 0xAC, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xE2, 0xA9, 0xE2, 0xAA, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0xE2, 0xA7, 0xE2, 0xA5, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0xE2, 0x9F, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x95, 0xCD, 0x89, 0xD3, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2, 0xB3, /* 0x88-0x8B */
+ 0x00, 0x00, 0xE2, 0xB0, 0x00, 0x00, 0xE2, 0xB5, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0xE2, 0xB4, 0x00, 0x00, /* 0x90-0x93 */
+ 0x94, 0x93, 0x96, 0xA5, 0x00, 0x00, 0x8E, 0x5A, /* 0x94-0x97 */
+ 0xE2, 0xAE, 0xE2, 0xB7, 0xE2, 0xB2, 0x00, 0x00, /* 0x98-0x9B */
+ 0xE2, 0xB1, 0xE2, 0xAD, 0xEE, 0x6B, 0xE2, 0xAF, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x8A, 0xC7, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x92, 0x5C, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x90, 0xFB, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x94, 0xA0, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0xE2, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x94, 0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x90, 0xDF, 0xE2, 0xB9, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x94, 0xCD, 0x00, 0x00, 0xE2, 0xBD, 0x95, 0xD1, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x92, 0x7A, 0x00, 0x00, 0xE2, 0xB8, /* 0xC8-0xCB */
+ 0xE2, 0xBA, 0x00, 0x00, 0x00, 0x00, 0xE2, 0xBB, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0xE2, 0xBE, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x8E, 0xC2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x93, 0xC4, 0xE2, 0xC3, 0xE2, 0xC2, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0xE2, 0xBF, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x98, 0x55, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2, 0xC8, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0xE2, 0xCC, 0xE2, 0xC9, /* 0xF4-0xF7 */
+};
+
+static unsigned char u2c_7C[512] = {
+ 0xE2, 0xC5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2, 0xC6, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0xE2, 0xCB, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0xE2, 0xC0, 0x99, 0xD3, 0xE2, 0xC7, /* 0x10-0x13 */
+ 0xE2, 0xC1, 0x00, 0x00, 0x00, 0x00, 0xE2, 0xCA, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2, 0xD0, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x8A, 0xC8, 0x00, 0x00, 0xE2, 0xCD, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2, 0xCE, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xE2, 0xCF, 0xE2, 0xD2, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2, 0xD1, /* 0x34-0x37 */
+ 0x94, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0xE2, 0xD3, 0x97, 0xFA, 0x95, 0xEB, /* 0x3C-0x3F */
+ 0xE2, 0xD8, 0x00, 0x00, 0x00, 0x00, 0xE2, 0xD5, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0xE2, 0xD4, 0x90, 0xD0, 0x00, 0x00, 0xE2, 0xD7, /* 0x4C-0x4F */
+ 0xE2, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0xE2, 0xD6, 0x00, 0x00, 0xE2, 0xDD, 0x00, 0x00, /* 0x54-0x57 */
+ 0xE2, 0xDA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2, 0xDB, /* 0x5C-0x5F */
+ 0xE2, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0xE2, 0xDC, 0xE2, 0xDE, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0xE2, 0xDF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0xC4, /* 0x70-0x73 */
+ 0x00, 0x00, 0xE2, 0xE0, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x96, 0xE0, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x8B, 0xCC, 0x8C, 0x48, 0xE2, 0xE1, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x95, 0xB2, 0x00, 0x00, 0x90, 0x88, /* 0x88-0x8B */
+ 0x00, 0x00, 0x96, 0xAE, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0xE2, 0xE2, 0x00, 0x00, 0x97, 0xB1, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x94, 0x94, 0x00, 0x00, 0x91, 0x65, /* 0x94-0x97 */
+ 0x94, 0x53, 0x00, 0x00, 0x00, 0x00, 0x8F, 0x6C, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0xBE, /* 0x9C-0x9F */
+ 0x00, 0x00, 0xE2, 0xE7, 0xE2, 0xE5, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0xE2, 0xE3, 0x8A, 0x9F, 0x00, 0x00, 0x8F, 0xCF, /* 0xA4-0xA7 */
+ 0xE2, 0xE8, 0x00, 0x00, 0x00, 0x00, 0xE2, 0xE6, /* 0xA8-0xAB */
+ 0x00, 0x00, 0xE2, 0xE4, 0xE2, 0xEC, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0xE2, 0xEB, 0xE2, 0xEA, 0xE2, 0xE9, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xE2, 0xED, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0xE2, 0xEE, 0x90, 0xB8, 0x00, 0x00, /* 0xBC-0xBF */
+ 0xE2, 0xEF, 0x00, 0x00, 0xE2, 0xF1, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0xE2, 0xF0, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x8C, 0xD0, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x91, 0x57, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0xE2, 0xF3, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x93, 0x9C, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xE2, 0xF2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0xE2, 0xF4, 0x00, 0x00, 0x95, 0xB3, 0x91, 0x8C, /* 0xDC-0xDF */
+ 0x8D, 0x66, 0x00, 0x00, 0xE2, 0xF5, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0xC6, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2, 0xF7, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0xE2, 0xF8, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xE2, 0xF9, 0x00, 0x00, 0xE2, 0xFA, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x8E, 0x85, 0x00, 0x00, 0xE2, 0xFB, 0x8C, 0x6E, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0x8B, 0x8A, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_7D[512] = {
+ 0x8B, 0x49, 0x00, 0x00, 0xE3, 0x40, 0x00, 0x00, /* 0x00-0x03 */
+ 0x96, 0xF1, 0x8D, 0x67, 0xE2, 0xFC, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0xE3, 0x43, 0x96, 0xE4, /* 0x08-0x0B */
+ 0x00, 0x00, 0x94, 0x5B, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x95, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x8F, 0x83, 0xE3, 0x42, 0x00, 0x00, 0x8E, 0xD1, /* 0x14-0x17 */
+ 0x8D, 0x68, 0x8E, 0x86, 0x8B, 0x89, 0x95, 0xB4, /* 0x18-0x1B */
+ 0xE3, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x91, 0x66, 0x96, 0x61, 0x8D, 0xF5, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x87, /* 0x28-0x2B */
+ 0x92, 0xDB, 0x00, 0x00, 0xE3, 0x46, 0x97, 0xDD, /* 0x2C-0x2F */
+ 0x8D, 0xD7, 0x00, 0x00, 0xE3, 0x47, 0x90, 0x61, /* 0x30-0x33 */
+ 0x00, 0x00, 0xE3, 0x49, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x8F, 0xD0, 0x8D, 0xAE, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE3, 0x48, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x8F, 0x49, 0x8C, 0xBC, /* 0x40-0x43 */
+ 0x91, 0x67, 0xE3, 0x44, 0xE3, 0x4A, 0x00, 0x00, /* 0x44-0x47 */
+ 0xEE, 0x6D, 0x00, 0x00, 0x00, 0x00, 0xE3, 0x45, /* 0x48-0x4B */
+ 0x8C, 0x6F, 0x00, 0x00, 0xE3, 0x4D, 0xE3, 0x51, /* 0x4C-0x4F */
+ 0x8C, 0x8B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0xE3, 0x4C, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE3, 0x55, /* 0x58-0x5B */
+ 0xEE, 0x6E, 0x00, 0x00, 0x8D, 0x69, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x97, 0x8D, 0x88, 0xBA, 0xE3, 0x52, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x8B, 0x8B, 0x00, 0x00, /* 0x64-0x67 */
+ 0xE3, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0xE3, 0x50, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x93, 0x9D, 0xE3, 0x4E, 0xE3, 0x4B, /* 0x70-0x73 */
+ 0x00, 0x00, 0x8A, 0x47, 0x90, 0xE2, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x8C, 0xA6, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0xE3, 0x57, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0xE3, 0x54, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE3, 0x56, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE3, 0x53, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x8C, 0x70, 0x91, 0xB1, 0xE3, 0x58, /* 0x98-0x9B */
+ 0x91, 0x8E, 0x00, 0x00, 0x00, 0x00, 0xE3, 0x65, /* 0x9C-0x9F */
+ 0xEE, 0x70, 0x00, 0x00, 0xE3, 0x61, 0xE3, 0x5B, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE3, 0x5F, /* 0xA8-0xAB */
+ 0x8E, 0xF8, 0x88, 0xDB, 0xE3, 0x5A, 0xE3, 0x62, /* 0xAC-0xAF */
+ 0xE3, 0x66, 0x8D, 0x6A, 0x96, 0xD4, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x92, 0xD4, 0xE3, 0x5C, 0x00, 0x00, 0xEE, 0x6F, /* 0xB4-0xB7 */
+ 0xE3, 0x64, 0x00, 0x00, 0xE3, 0x59, 0x92, 0x5D, /* 0xB8-0xBB */
+ 0x00, 0x00, 0xE3, 0x5E, 0x88, 0xBB, 0x96, 0xC8, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE3, 0x5D, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x8B, 0xD9, 0x94, 0xEA, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0x8D, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x97, 0xCE, 0x8F, 0x8F, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0xE3, 0x8E, 0xEE, 0x71, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xE3, 0x67, 0x00, 0x00, 0x90, 0xFC, 0x00, 0x00, /* 0xD8-0xDB */
+ 0xE3, 0x63, 0xE3, 0x68, 0xE3, 0x6A, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x92, 0xF7, 0xE3, 0x6D, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0xE3, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x95, 0xD2, 0x8A, 0xC9, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x96, 0xC9, 0x00, 0x00, 0x00, 0x00, 0x88, 0xDC, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0xE3, 0x6C, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x97, 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE3, 0x6B, /* 0xF8-0xFB */
+};
+
+static unsigned char u2c_7E[512] = {
+ 0x00, 0x00, 0x89, 0x8F, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x93, 0xEA, 0xE3, 0x6E, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0xE3, 0x75, 0xE3, 0x6F, 0xE3, 0x76, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0xE3, 0x72, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x9B, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x8E, 0xC8, 0xE3, 0x74, /* 0x1C-0x1F */
+ 0x00, 0x00, 0xE3, 0x71, 0xE3, 0x77, 0xE3, 0x70, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x8F, 0x63, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x44, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x8F, 0x6B, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0xE3, 0x73, 0xE3, 0x80, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0xE3, 0x7B, 0x00, 0x00, 0xE3, 0x7E, /* 0x34-0x37 */
+ 0x00, 0x00, 0xE3, 0x7C, 0xE3, 0x81, 0xE3, 0x7A, /* 0x38-0x3B */
+ 0x00, 0x00, 0xE3, 0x60, 0x90, 0xD1, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x94, 0xC9, 0x00, 0x00, 0xE3, 0x7D, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0xE3, 0x78, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x91, 0x40, 0x8C, 0x71, /* 0x48-0x4B */
+ 0x00, 0x00, 0x8F, 0x4A, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0xEE, 0x72, 0x00, 0x00, /* 0x50-0x53 */
+ 0x90, 0x44, 0x91, 0x55, 0xE3, 0x84, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0xE3, 0x86, 0xE3, 0x87, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0xE3, 0x83, 0xE3, 0x85, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0xE3, 0x79, 0xE3, 0x82, /* 0x64-0x67 */
+ 0x00, 0x00, 0xE3, 0x8A, 0xE3, 0x89, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x96, 0x9A, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x8C, 0x4A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0xE3, 0x88, 0x00, 0x00, 0xE3, 0x8C, /* 0x78-0x7B */
+ 0xE3, 0x8B, 0xE3, 0x8F, 0x00, 0x00, 0xE3, 0x91, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x8E, 0x5B, 0xE3, 0x8D, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0xE3, 0x92, 0xE3, 0x93, 0xED, 0x40, 0x00, 0x00, /* 0x88-0x8B */
+ 0xE3, 0x94, 0x00, 0x00, 0xE3, 0x9A, 0x93, 0x5A, /* 0x8C-0x8F */
+ 0xE3, 0x96, 0x00, 0x00, 0xE3, 0x95, 0xE3, 0x97, /* 0x90-0x93 */
+ 0xE3, 0x98, 0x00, 0x00, 0xE3, 0x99, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE3, 0x9B, /* 0x98-0x9B */
+ 0xE3, 0x9C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+};
+
+static unsigned char u2c_7F[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x8A, 0xCA, 0x00, 0x00, /* 0x34-0x37 */
+ 0xE3, 0x9D, 0x00, 0x00, 0xE3, 0x9E, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0xE3, 0x9F, 0x00, 0x00, 0xEE, 0x73, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0xE3, 0xA0, 0xE3, 0xA1, 0xE3, 0xA2, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xE3, 0xA3, 0xE3, 0xA4, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0xE3, 0xA6, 0xE3, 0xA5, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0xE3, 0xA7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE3, 0xA8, /* 0x5C-0x5F */
+ 0xE3, 0xA9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE3, 0xAC, /* 0x64-0x67 */
+ 0xE3, 0xAA, 0xE3, 0xAB, 0x8D, 0xDF, 0x8C, 0x72, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x92, 0x75, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x94, 0xB1, 0x00, 0x00, 0x8F, 0x90, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x94, 0x6C, 0x00, 0x00, 0x94, 0xEB, /* 0x74-0x77 */
+ 0xE3, 0xAD, 0x9C, 0xEB, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0xE3, 0xAE, 0xE3, 0xB0, /* 0x80-0x83 */
+ 0x00, 0x00, 0x97, 0x85, 0xE3, 0xAF, 0xE3, 0xB2, /* 0x84-0x87 */
+ 0xE3, 0xB1, 0x00, 0x00, 0x97, 0x72, 0x00, 0x00, /* 0x88-0x8B */
+ 0xE3, 0xB3, 0x00, 0x00, 0x94, 0xFC, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0xE3, 0xB4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0xE3, 0xB7, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0xE3, 0xB6, 0xE3, 0xB5, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0xEE, 0x74, 0x00, 0x00, 0xE3, 0xB8, /* 0xA0-0xA3 */
+ 0x8C, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x91, 0x41, 0x8B, 0x60, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0xE3, 0xBC, 0xE3, 0xB9, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0xE3, 0xBA, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0xE3, 0xBD, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0xE3, 0xBE, 0xE3, 0xBB, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x89, 0x48, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x89, 0xA5, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0xE3, 0xC0, 0xE3, 0xC1, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0xE3, 0xC2, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x97, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x8F, 0x4B, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xE3, 0xC4, 0xE3, 0xC3, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x90, 0x89, 0xE3, 0xC5, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0xE3, 0xC6, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0xE3, 0xC7, 0x00, 0x00, 0x8A, 0xE3, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x8A, 0xCB, 0x00, 0x00, 0x00, 0x00, 0xE3, 0xC8, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xE3, 0xC9, 0x00, 0x00, 0x96, 0x7C, /* 0xF8-0xFB */
+ 0x97, 0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_80[512] = {
+ 0x97, 0x73, 0x98, 0x56, 0x00, 0x00, 0x8D, 0x6C, /* 0x00-0x03 */
+ 0xE3, 0xCC, 0x8E, 0xD2, 0xE3, 0xCB, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE3, 0xCD, /* 0x08-0x0B */
+ 0x8E, 0xA7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x91, 0xCF, 0x00, 0x00, 0xE3, 0xCE, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x8D, 0x6B, 0x00, 0x00, 0x96, 0xD5, /* 0x14-0x17 */
+ 0xE3, 0xCF, 0xE3, 0xD0, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0xE3, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0xE3, 0xD2, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0xE3, 0xD3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8E, 0xA8, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x96, 0xEB, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE3, 0xD5, /* 0x38-0x3B */
+ 0x00, 0x00, 0x92, 0x5E, 0x00, 0x00, 0xE3, 0xD4, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0xE3, 0xD7, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0xE3, 0xD6, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0xE3, 0xD8, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x90, 0xB9, 0x00, 0x00, /* 0x54-0x57 */
+ 0xE3, 0xD9, 0x00, 0x00, 0xE3, 0xDA, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x95, 0xB7, 0xE3, 0xDB, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x91, 0x8F, 0xE3, 0xDC, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0xE3, 0xDD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0xFC, /* 0x6C-0x6F */
+ 0xE3, 0xE0, 0x00, 0x00, 0xE3, 0xDF, 0xE3, 0xDE, /* 0x70-0x73 */
+ 0x92, 0xAE, 0x00, 0x00, 0xE3, 0xE1, 0x90, 0x45, /* 0x74-0x77 */
+ 0x00, 0x00, 0xE3, 0xE2, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0xE3, 0xE3, 0x98, 0x57, 0xE3, 0xE4, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0xE3, 0xE5, 0xE3, 0xE7, 0xE3, 0xE6, 0x94, 0xA3, /* 0x84-0x87 */
+ 0x00, 0x00, 0x93, 0xF7, 0x00, 0x00, 0x98, 0x5D, /* 0x88-0x8B */
+ 0x94, 0xA7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE3, 0xE9, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x8F, 0xD1, 0x00, 0x00, /* 0x94-0x97 */
+ 0x95, 0x49, 0x00, 0x00, 0xE3, 0xEA, 0xE3, 0xE8, /* 0x98-0x9B */
+ 0x00, 0x00, 0x8A, 0xCC, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x8C, 0xD2, 0x8E, 0x88, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x94, 0xEC, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x8C, 0xA8, 0x96, 0x62, 0x00, 0x00, /* 0xA8-0xAB */
+ 0xE3, 0xED, 0xE3, 0xEB, 0x00, 0x00, 0x8D, 0x6D, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x8D, 0x6E, 0x88, 0xE7, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x8D, 0xE6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x94, 0x78, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0xDD, /* 0xC0-0xC3 */
+ 0xE3, 0xF2, 0x00, 0x00, 0x92, 0x5F, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x94, 0x77, 0x00, 0x00, 0x91, 0xD9, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0xE3, 0xF4, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0xE3, 0xF0, 0xE3, 0xF3, 0xE3, 0xEE, /* 0xD8-0xDB */
+ 0x00, 0x00, 0xE3, 0xF1, 0x96, 0x45, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x8C, 0xD3, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x88, 0xFB, 0xE3, 0xEF, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE3, 0xF6, /* 0xEC-0xEF */
+ 0x00, 0x00, 0xE3, 0xF7, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x93, 0xB7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x8B, 0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0xE4, 0x45, 0x94, 0x5C, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_81[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x8E, 0x89, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x8B, 0xBA, 0x90, 0xC6, 0x98, 0x65, /* 0x04-0x07 */
+ 0x96, 0xAC, 0xE3, 0xF5, 0x90, 0xD2, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x8B, 0x72, 0xE3, 0xF8, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE3, 0xFA, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0xE3, 0xF9, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE3, 0xFB, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x92, 0x45, 0x00, 0x00, 0x94, 0x5D, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x92, 0xAF, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0xE4, 0x42, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0xE4, 0x41, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE3, 0xFC, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x90, 0x74, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x95, 0x85, 0xE4, 0x44, 0x00, 0x00, 0xE4, 0x43, /* 0x50-0x53 */
+ 0x8D, 0x6F, 0x98, 0x72, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0x54, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0xE4, 0x48, 0xE4, 0x49, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8E, 0xEE, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0xE4, 0x47, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x8D, 0x98, 0xE4, 0x46, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0xE4, 0x4A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x92, 0xB0, 0x95, 0xA0, 0x91, 0x42, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0xDA, /* 0x7C-0x7F */
+
+ 0xE4, 0x4E, 0x00, 0x00, 0xE4, 0x4F, 0xE4, 0x4B, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0xE4, 0x4C, 0x00, 0x00, 0xE4, 0x4D, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8D, 0x70, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0x55, /* 0x90-0x93 */
+ 0x00, 0x00, 0xE4, 0x51, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x95, 0x86, 0x00, 0x00, /* 0x98-0x9B */
+ 0x96, 0x8C, 0x95, 0x47, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0xE4, 0x50, 0x00, 0x00, 0x00, 0x00, 0xE4, 0x53, /* 0xA0-0xA3 */
+ 0xE4, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x96, 0x63, 0xE4, 0x56, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0xE4, 0x57, 0x00, 0x00, 0x00, 0x00, 0x91, 0x56, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0xE4, 0x58, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0xE4, 0x5A, 0x00, 0x00, 0xE4, 0x5E, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0xE4, 0x5B, 0xE4, 0x59, 0x94, 0x5E, /* 0xBC-0xBF */
+ 0xE4, 0x5C, 0x00, 0x00, 0xE4, 0x5D, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x89, 0xB0, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0xE4, 0x64, 0xE4, 0x5F, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0xE4, 0x60, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0xE4, 0x61, 0x00, 0x00, 0x91, 0x9F, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xE4, 0x63, 0xE4, 0x62, 0xE4, 0x65, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0x66, /* 0xDC-0xDF */
+ 0xE4, 0x67, 0x00, 0x00, 0x00, 0x00, 0x90, 0x62, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x89, 0xE7, 0x00, 0x00, 0xE4, 0x68, /* 0xE4-0xE7 */
+ 0x97, 0xD5, 0x00, 0x00, 0x8E, 0xA9, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x8F, 0x4C, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x8A, /* 0xF0-0xF3 */
+ 0x92, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0xE4, 0x69, 0xE4, 0x6A, /* 0xF8-0xFB */
+ 0x89, 0x50, 0x00, 0x00, 0xE4, 0x6B, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_82[512] = {
+ 0x00, 0x00, 0xE4, 0x6C, 0xE4, 0x6D, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0xE4, 0x6E, 0x00, 0x00, 0xE4, 0x6F, /* 0x04-0x07 */
+ 0x8B, 0xBB, 0x9D, 0xA8, 0xE4, 0x70, 0x00, 0x00, /* 0x08-0x0B */
+ 0x90, 0xE3, 0xE4, 0x71, 0x8E, 0xC9, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xE4, 0x72, 0x00, 0x00, 0x98, 0xAE, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0xE4, 0x73, 0x95, 0xDC, /* 0x14-0x17 */
+ 0x8A, 0xDA, 0x00, 0x00, 0x00, 0x00, 0x91, 0x43, /* 0x18-0x1B */
+ 0x8F, 0x77, 0x00, 0x00, 0x95, 0x91, 0x8F, 0x4D, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0xE4, 0x74, 0x8D, 0x71, 0xE4, 0x75, /* 0x28-0x2B */
+ 0x94, 0xCA, 0x00, 0x00, 0xE4, 0x84, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0x77, /* 0x30-0x33 */
+ 0x00, 0x00, 0x91, 0xC7, 0x94, 0x95, 0x8C, 0xBD, /* 0x34-0x37 */
+ 0xE4, 0x76, 0x91, 0x44, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0xE4, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0xF8, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0xE4, 0x7A, 0xE4, 0x79, 0xE4, 0x7C, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0xE4, 0x7B, 0x00, 0x00, 0xE4, 0x7D, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0xE4, 0x80, 0x00, 0x00, /* 0x60-0x63 */
+ 0xE4, 0x7E, 0x00, 0x00, 0x8A, 0xCD, 0x00, 0x00, /* 0x64-0x67 */
+ 0xE4, 0x81, 0x00, 0x00, 0xE4, 0x82, 0xE4, 0x83, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x8D, 0xAF, 0x97, 0xC7, /* 0x6C-0x6F */
+ 0x00, 0x00, 0xE4, 0x85, 0x90, 0x46, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x89, 0x90, 0xE4, 0x86, /* 0x74-0x77 */
+ 0xE4, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0xE4, 0x88, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0xF0, /* 0x88-0x8B */
+ 0x00, 0x00, 0xE4, 0x89, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0xE4, 0x8A, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x95, 0x87, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x8E, 0xC5, 0x00, 0x00, 0xE4, 0x8C, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x8A, 0x48, 0x88, 0xB0, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0x8B, /* 0xA8-0xAB */
+ 0xE4, 0x8E, 0x94, 0x6D, 0x00, 0x00, 0x90, 0x63, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x89, 0xD4, 0x00, 0x00, 0x96, 0x46, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x8C, 0x7C, 0x8B, 0xDA, 0x00, 0x00, 0xE4, 0x8D, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x89, 0xE8, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x8A, 0xA1, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x89, 0x91, 0xE4, 0x92, 0x97, 0xE8, /* 0xD0-0xD3 */
+ 0x91, 0xDB, 0x00, 0x00, 0x00, 0x00, 0x95, 0x63, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0xE4, 0x9E, 0x00, 0x00, 0x89, 0xD5, /* 0xD8-0xDB */
+ 0xE4, 0x9C, 0x00, 0x00, 0xE4, 0x9A, 0xE4, 0x91, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xE4, 0x8F, 0x00, 0x00, 0xE4, 0x90, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x8E, 0xE1, 0x8B, 0xEA, 0x92, 0x97, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0xCF, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x89, 0x70, 0x00, 0x00, 0xE4, 0x94, /* 0xF0-0xF3 */
+ 0xE4, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xE4, 0x99, 0xE4, 0x95, 0xE4, 0x98, /* 0xF8-0xFB */
+};
+
+static unsigned char u2c_83[512] = {
+ 0x00, 0x00, 0xEE, 0x76, 0x96, 0xCE, 0xE4, 0x97, /* 0x00-0x03 */
+ 0x89, 0xD6, 0x8A, 0x9D, 0xE4, 0x9B, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0xE4, 0x9D, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x8C, 0x73, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0xE4, 0xA1, 0xE4, 0xAA, /* 0x14-0x17 */
+ 0xE4, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x88, 0xA9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0xB2, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x88, 0xEF, 0x00, 0x00, 0x00, 0x00, 0xE4, 0xA9, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0xA8, /* 0x2C-0x2F */
+ 0x00, 0x00, 0xE4, 0xA3, 0xE4, 0xA2, 0x00, 0x00, /* 0x30-0x33 */
+ 0xE4, 0xA0, 0xE4, 0x9F, 0x92, 0x83, 0x00, 0x00, /* 0x34-0x37 */
+ 0x91, 0xF9, 0xE4, 0xA5, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0xE4, 0xA4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0xE4, 0xA7, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x91, 0x90, 0x8C, 0x74, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x60, /* 0x4C-0x4F */
+ 0xE4, 0xA6, 0x00, 0x00, 0x8D, 0x72, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x91, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0xEE, 0x77, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0xB8, /* 0x70-0x73 */
+ 0x00, 0x00, 0xE4, 0xB9, 0x00, 0x00, 0x89, 0xD7, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0xAC, /* 0x78-0x7B */
+ 0xE4, 0xB6, 0x00, 0x00, 0x00, 0x00, 0xEE, 0x78, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0xE4, 0xAC, 0x00, 0x00, 0xE4, 0xB4, /* 0x84-0x87 */
+ 0x00, 0x00, 0xE4, 0xBB, 0xE4, 0xB5, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0xE4, 0xB3, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0x96, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0xE4, 0xB1, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0xE4, 0xAD, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x8A, 0xCE, 0xE4, 0xAF, /* 0x9C-0x9F */
+ 0xE4, 0xBA, 0x00, 0x00, 0xE4, 0xB0, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xE4, 0xBC, 0x00, 0x00, 0xE4, 0xAE, 0x94, 0x9C, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x97, 0x89, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0xE4, 0xB7, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0xE4, 0xCD, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0xE4, 0xC5, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x90, 0x9B, 0x00, 0x00, 0xEE, 0x79, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x8B, 0x65, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x8B, 0xDB, 0x00, 0x00, 0xE4, 0xC0, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0xD9, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x8F, 0xD2, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xE4, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x8D, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x93, 0x70, /* 0xDC-0xDF */
+ 0xE4, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x95, 0xEC, 0x00, 0x00, 0xE4, 0xBF, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0xD8, /* 0xEC-0xEF */
+ 0x8C, 0xD4, 0x95, 0x48, 0xE4, 0xC9, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xE4, 0xBD, 0x00, 0x00, 0xEE, 0x7A, 0xE4, 0xC6, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0xD0, /* 0xF8-0xFB */
+ 0x00, 0x00, 0xE4, 0xC1, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_84[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0xC2, /* 0x00-0x03 */
+ 0x93, 0xB8, 0x00, 0x00, 0x00, 0x00, 0xE4, 0xC7, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0xC4, /* 0x08-0x0B */
+ 0x96, 0x47, 0xE4, 0xCA, 0x88, 0xDE, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0xBE, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0xE4, 0xCC, 0x00, 0x00, 0xE4, 0xCB, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x94, 0x8B, 0xE4, 0xD2, 0x00, 0x00, /* 0x28-0x2B */
+ 0xE4, 0xDD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x8A, 0x9E, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0xE4, 0xE0, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0xE4, 0xCE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0xE4, 0xD3, 0x97, 0x8E, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0xE4, 0xDC, 0x00, 0x00, /* 0x44-0x47 */
+ 0xEE, 0x7B, 0x97, 0x74, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x97, 0xA8, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x98, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8A, 0x8B, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x95, 0x92, 0xE4, 0xE2, 0x93, 0x9F, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x88, 0xAF, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0xE4, 0xDB, 0x00, 0x00, 0xE4, 0xD7, /* 0x68-0x6B */
+ 0x91, 0x92, 0xE4, 0xD1, 0xE4, 0xD9, 0xE4, 0xDE, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x94, 0x4B, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x88, 0xA8, 0x00, 0x00, 0xE4, 0xD6, /* 0x74-0x77 */
+ 0x00, 0x00, 0xE4, 0xDF, 0x95, 0x98, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0xE4, 0xDA, 0x00, 0x00, /* 0x80-0x83 */
+ 0xE4, 0xD5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8F, 0xD3, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x8F, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x8E, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x96, 0xD6, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x95, 0x66, 0x00, 0x00, 0x00, 0x00, 0xE4, 0xE5, /* 0x9C-0x9F */
+ 0x00, 0x00, 0xE4, 0xEE, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0xE4, 0xD8, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x8A, 0x97, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0xEE, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x8F, 0xF6, 0xE4, 0xE3, 0x00, 0x00, 0xE4, 0xE8, /* 0xB8-0xBB */
+ 0x91, 0x93, 0x00, 0x00, 0x00, 0x00, 0xE4, 0xE4, /* 0xBC-0xBF */
+ 0x00, 0x00, 0xE4, 0xEB, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x92, 0x7E, 0x00, 0x00, 0xE4, 0xEC, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x97, 0x75, 0xE4, 0xE1, 0x8A, 0x57, /* 0xC8-0xCB */
+ 0x00, 0x00, 0xE4, 0xE7, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0xE4, 0xEA, 0x96, 0xAA, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0xE4, 0xED, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0xE4, 0xE6, 0xE4, 0xE9, 0x00, 0x00, /* 0xD8-0xDB */
+ 0xED, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x96, 0x48, 0x00, 0x00, 0x98, 0x40, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xE4, 0xF1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0xE4, 0xF8, 0x00, 0x00, 0x00, 0x00, 0xE4, 0xF0, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_85[512] = {
+ 0x8E, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0xE4, 0xCF, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x95, 0xCC, 0x00, 0x00, 0x96, 0xA0, /* 0x10-0x13 */
+ 0xE4, 0xF7, 0xE4, 0xF6, 0x00, 0x00, 0xE4, 0xF2, /* 0x14-0x17 */
+ 0xE4, 0xF3, 0x00, 0x00, 0x89, 0x55, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0xF5, /* 0x1C-0x1F */
+ 0x00, 0x00, 0xE4, 0xEF, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x92, 0xD3, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0xE4, 0xF4, 0x88, 0xFC, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x91, 0xA0, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x95, 0xC1, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0xE4, 0xF9, 0xE5, 0x40, 0x00, 0x00, 0x94, 0xD7, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0xE4, 0xFC, 0x8F, 0xD4, 0x8E, 0xC7, 0xE5, 0x42, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x8B, 0xBC, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEE, 0x7D, /* 0x50-0x53 */
+ 0x00, 0x00, 0xE5, 0x43, 0x00, 0x00, 0x95, 0x99, /* 0x54-0x57 */
+ 0xE4, 0xFB, 0xEE, 0x7E, 0xE4, 0xD4, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0xFA, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x98, 0x6E, 0x93, 0xA0, 0x95, 0x93, 0xEE, 0x80, /* 0x68-0x6B */
+ 0x00, 0x00, 0xE5, 0x4A, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0x50, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0x51, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0xE5, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x94, 0x96, 0x00, 0x00, 0x00, 0x00, 0xE5, 0x4E, /* 0x84-0x87 */
+ 0xE5, 0x46, 0x00, 0x00, 0xE5, 0x48, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0xE5, 0x52, 0xE5, 0x47, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0xE5, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x89, 0x92, /* 0x94-0x97 */
+ 0x00, 0x00, 0x93, 0xE3, 0x00, 0x00, 0xE5, 0x4C, /* 0x98-0x9B */
+ 0xE5, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0xE5, 0x45, 0x00, 0x00, 0x91, 0x45, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xE5, 0x49, 0x8E, 0x46, 0x90, 0x64, 0x8C, 0x4F, /* 0xA8-0xAB */
+ 0x96, 0xF2, 0x00, 0x00, 0x96, 0xF7, 0x8F, 0x92, /* 0xAC-0xAF */
+ 0xEE, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xE5, 0x56, 0xE5, 0x54, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x98, 0x6D, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xE5, 0x53, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x97, 0x95, 0x00, 0x00, 0xE5, 0x55, /* 0xCC-0xCF */
+ 0xE5, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0xE5, 0x58, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0xE5, 0x5B, 0xE5, 0x59, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x93, 0xA1, 0xE5, 0x5A, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x94, 0xCB, 0xE5, 0x4D, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8F, 0x93, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xE5, 0x5C, 0xE5, 0x61, 0x91, 0x94, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0x60, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_86[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0x41, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0x62, 0x91, 0x68, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0x5D, 0xE5, 0x5F, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0x5E, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x9F, 0x50, 0x9F, 0x41, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0x64, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0x63, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x97, 0x96, 0x00, 0x00, 0xE1, 0xBA, /* 0x2C-0x2F */
+ 0xE5, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0x66, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0xE5, 0x67, 0x8C, 0xD5, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x8B, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0xE5, 0x69, 0x99, 0x7C, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x8B, 0x95, 0x00, 0x00, /* 0x58-0x5B */
+ 0x97, 0xB8, 0x00, 0x00, 0x8B, 0xF1, 0xE5, 0x6A, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0x6B, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x8E, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0xE5, 0x6C, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x93, 0xF8, 0x00, 0x00, 0x88, 0xB8, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x89, 0xE1, 0xE5, 0x71, /* 0x88-0x8B */
+ 0xE5, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0x6D, /* 0x90-0x93 */
+ 0x00, 0x00, 0x8E, 0x5C, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0x6E, /* 0xA0-0xA3 */
+ 0x94, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0xE5, 0x6F, 0xE5, 0x70, 0xE5, 0x7A, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0x74, /* 0xAC-0xAF */
+ 0xE5, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0x73, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xE5, 0x75, 0x00, 0x00, 0xE5, 0x76, 0x8E, 0xD6, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xE5, 0x78, 0x00, 0x00, 0x92, 0x60, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x8C, 0x75, 0x8A, 0x61, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xE5, 0x7B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x8A, 0x5E, 0x00, 0x00, 0xE5, 0x81, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0x7C, 0xE5, 0x80, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x94, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0xE5, 0x7D, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0xE5, 0x7E, 0x95, 0x67, 0x94, 0xD8, 0xE5, 0x82, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x91, 0xFB, 0xE5, 0x8C, 0x00, 0x00, 0xE5, 0x88, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0x89, 0xE9, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_87[512] = {
+ 0xE5, 0x86, 0x00, 0x00, 0x96, 0x49, 0xE5, 0x87, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0x84, 0x00, 0x00, /* 0x04-0x07 */
+ 0xE5, 0x85, 0xE5, 0x8A, 0xE5, 0x8D, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0xE5, 0x8B, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0xE5, 0x89, 0xE5, 0x83, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x92, 0x77, 0x00, 0x00, 0xE5, 0x94, 0x00, 0x00, /* 0x18-0x1B */
+ 0x96, 0xA8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0xE5, 0x92, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0xE5, 0x93, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0xE5, 0x8E, 0x00, 0x00, 0x00, 0x00, 0xE5, 0x90, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0x91, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0x8F, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x90, 0xE4, 0x00, 0x00, 0x98, 0x58, /* 0x48-0x4B */
+ 0xE5, 0x98, 0x00, 0x00, 0xE5, 0x99, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0x9F, /* 0x50-0x53 */
+ 0x00, 0x00, 0x90, 0x49, 0x00, 0x00, 0xE5, 0x9B, /* 0x54-0x57 */
+ 0x00, 0x00, 0xE5, 0x9E, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0x96, /* 0x5C-0x5F */
+ 0xE5, 0x95, 0x00, 0x00, 0x00, 0x00, 0xE5, 0xA0, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x89, 0xDA, 0x00, 0x00, /* 0x64-0x67 */
+ 0xE5, 0x9C, 0x00, 0x00, 0xE5, 0xA1, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0x9D, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0xE5, 0x9A, 0x00, 0x00, 0x92, 0xB1, 0x00, 0x00, /* 0x74-0x77 */
+ 0xE5, 0x97, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x88, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0xA5, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x97, 0x5A, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0xA4, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0xA3, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0xAC, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0xA6, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0xAE, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x97, 0x86, 0xE5, 0xB1, /* 0xB8-0xBB */
+ 0x00, 0x00, 0xE5, 0xA8, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0xE5, 0xA9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xE5, 0xAD, 0x00, 0x00, 0xE5, 0xB0, 0xE5, 0xAF, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0xA7, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0xE5, 0xAA, 0x00, 0x00, 0xE5, 0xBB, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0xE5, 0xB4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0xB2, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0xB3, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0xB8, 0xE5, 0xB9, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x8A, 0x49, 0x00, 0x00, 0x8B, 0x61, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0xB7, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_88[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0xE5, 0xA2, 0x00, 0x00, 0xEE, 0x85, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0xE5, 0xB6, 0xE5, 0xBA, 0xE5, 0xB5, /* 0x0C-0x0F */
+ 0x00, 0x00, 0xE5, 0xBC, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0xE5, 0xBE, 0xE5, 0xBD, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0xE5, 0xC0, 0xE5, 0xBF, 0xE5, 0x79, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0xC4, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0xE5, 0xC1, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0xC2, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0xE5, 0xC3, 0x00, 0x00, 0xE5, 0xC5, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x8C, 0x8C, 0x00, 0x00, 0xE5, 0xC7, 0x00, 0x00, /* 0x40-0x43 */
+ 0xE5, 0xC6, 0x00, 0x00, 0x8F, 0x4F, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x8D, 0x73, 0x9F, 0xA5, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0xC8, 0x8F, 0x70, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8A, 0x58, /* 0x54-0x57 */
+ 0x00, 0x00, 0xE5, 0xC9, 0x00, 0x00, 0x89, 0x71, /* 0x58-0x5B */
+ 0x00, 0x00, 0x8F, 0xD5, 0xE5, 0xCA, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x8D, 0x74, 0xE5, 0xCB, 0x88, 0xDF, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x95, 0x5C, 0x00, 0x00, 0x00, 0x00, 0xE5, 0xCC, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x90, 0x8A, 0x00, 0x00, 0xE5, 0xD3, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0xE5, 0xD0, 0x00, 0x00, 0x92, 0x8F, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0xE5, 0xD1, 0xE5, 0xCE, 0x8B, 0xDC, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0xE5, 0xCD, 0xE5, 0xD4, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x8C, 0x55, 0x00, 0x00, 0x00, 0x00, 0x91, 0xDC, /* 0x88-0x8B */
+ 0x00, 0x00, 0xE5, 0xDA, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0xD6, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x91, 0xB3, 0xE5, 0xD5, /* 0x94-0x97 */
+ 0x00, 0x00, 0xE5, 0xD8, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0xCF, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0xD9, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0xE5, 0xDB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0xED, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0xD7, 0x00, 0x00, /* 0xAC-0xAF */
+ 0xE5, 0xDC, 0xE5, 0xDE, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x8C, 0xD1, 0xE5, 0xD2, 0x00, 0x00, 0x88, 0xBF, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0xDD, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x8D, 0xD9, 0x97, 0xF4, 0xE5, 0xDF, /* 0xC0-0xC3 */
+ 0xE5, 0xE0, 0x91, 0x95, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0xA0, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xE5, 0xE1, 0x97, 0x54, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xE5, 0xE2, 0xE5, 0xE3, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x95, 0xE2, 0xE5, 0xE4, 0x00, 0x00, 0x8D, 0xBE, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x97, 0xA1, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xE5, 0xE9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0xEA, 0x8F, 0xD6, /* 0xF0-0xF3 */
+ 0xE5, 0xE8, 0xEE, 0x86, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x97, 0x87, 0xE5, 0xE5, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0xE5, 0xE7, 0x90, 0xBB, 0x90, 0x9E, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_89[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0xE6, 0x00, 0x00, /* 0x00-0x03 */
+ 0xE5, 0xEB, 0x00, 0x00, 0x00, 0x00, 0x95, 0xA1, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0xED, 0x00, 0x00, /* 0x08-0x0B */
+ 0xE5, 0xEC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x8A, 0x8C, 0x00, 0x00, 0x96, 0x4A, 0xE5, 0xEE, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0xED, 0x41, 0xE5, 0xFA, 0xE5, 0xF0, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0xE5, 0xF1, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0xF2, 0xE5, 0xF3, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0xF7, 0x00, 0x00, /* 0x34-0x37 */
+ 0xE5, 0xF8, 0x00, 0x00, 0x00, 0x00, 0xE5, 0xF6, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0xE5, 0xF4, 0x00, 0x00, 0xE5, 0xEF, /* 0x40-0x43 */
+ 0xE5, 0xF5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0xE5, 0xF9, 0xE8, 0xB5, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x89, 0xA6, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0xFC, 0x8B, 0xDD, /* 0x5C-0x5F */
+ 0xE5, 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0xE6, 0x41, 0x00, 0x00, 0xE6, 0x40, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0xE6, 0x43, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0xE6, 0x42, 0x00, 0x00, 0xE6, 0x44, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x8F, 0x50, 0x00, 0x00, /* 0x70-0x73 */
+ 0xE6, 0x45, 0x00, 0x00, 0x00, 0x00, 0xE6, 0x46, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0xE6, 0x47, 0x90, 0xBC, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x97, 0x76, 0x00, 0x00, 0xE6, 0x48, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x95, 0xA2, 0x94, 0x65, /* 0x84-0x87 */
+ 0xE6, 0x49, 0x00, 0x00, 0xE6, 0x4A, 0x8C, 0xA9, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8B, 0x4B, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE6, 0x4B, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x8E, 0x8B, 0x94, 0x60, /* 0x94-0x97 */
+ 0xE6, 0x4C, 0x00, 0x00, 0x8A, 0x6F, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0xE6, 0x4D, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0xE6, 0x4F, 0x97, 0x97, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0xE6, 0x4E, 0x90, 0x65, 0x00, 0x00, /* 0xA8-0xAB */
+ 0xE6, 0x50, 0x00, 0x00, 0x00, 0x00, 0xE6, 0x51, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0xE6, 0x52, 0x8A, 0xCF, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0xE6, 0x53, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0xE6, 0x54, 0x00, 0x00, 0xE6, 0x55, /* 0xBC-0xBF */
+ 0xE6, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x8A, 0x70, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0xE6, 0x57, 0x00, 0x00, /* 0xD8-0xDB */
+ 0xE6, 0x58, 0xE6, 0x59, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0xF0, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x90, 0x47, 0xE6, 0x5A, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xE6, 0x5B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0xE6, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+};
+
+static unsigned char u2c_8A[512] = {
+ 0x8C, 0xBE, 0x00, 0x00, 0x92, 0xF9, 0xE6, 0x5D, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x8C, 0x76, 0x00, 0x00, 0x90, 0x75, 0x00, 0x00, /* 0x08-0x0B */
+ 0xE6, 0x60, 0x00, 0x00, 0x93, 0xA2, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xE6, 0x5F, 0x00, 0x00, 0xEE, 0x87, 0x8C, 0x50, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0xE6, 0x5E, 0x91, 0xF5, /* 0x14-0x17 */
+ 0x8B, 0x4C, 0x00, 0x00, 0x00, 0x00, 0xE6, 0x61, /* 0x18-0x1B */
+ 0x00, 0x00, 0xE6, 0x62, 0x00, 0x00, 0x8F, 0xD7, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8C, 0x8D, /* 0x20-0x23 */
+ 0x00, 0x00, 0xE6, 0x63, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x96, 0x4B, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x90, 0xDD, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x8B, 0x96, 0x00, 0x00, 0x96, 0xF3, /* 0x30-0x33 */
+ 0x91, 0x69, 0x00, 0x00, 0xE6, 0x64, 0xEE, 0x88, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x90, 0x66, 0x92, 0x90, /* 0x38-0x3B */
+ 0x8F, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0xE6, 0x65, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0xE6, 0x68, 0x00, 0x00, /* 0x44-0x47 */
+ 0xE6, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x8D, 0xBC, 0x91, 0xC0, 0xE6, 0x67, 0x00, 0x00, /* 0x50-0x53 */
+ 0x8F, 0xD9, 0x95, 0x5D, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE6, 0x66, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x8E, 0x8C, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x89, 0x72, 0x00, 0x00, 0xE6, 0x6D, 0x8C, 0x77, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x8E, 0x8E, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x8E, 0x8D, 0x00, 0x00, 0x98, 0x6C, /* 0x68-0x6B */
+ 0xE6, 0x6C, 0xE6, 0x6B, 0x91, 0x46, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x8B, 0x6C, 0x98, 0x62, 0x8A, 0x59, 0x8F, 0xDA, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0xEE, 0x89, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0xE6, 0x6A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0xE6, 0x6F, 0x00, 0x00, /* 0x80-0x83 */
+ 0xE6, 0x70, 0xE6, 0x6E, 0x00, 0x00, 0x8C, 0xD6, /* 0x84-0x87 */
+ 0x00, 0x00, 0x97, 0x5F, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x8E, 0x8F, 0x94, 0x46, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xE6, 0x73, 0x00, 0x00, 0x90, 0xBE, /* 0x90-0x93 */
+ 0x00, 0x00, 0x92, 0x61, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x97, 0x55, 0x00, 0x00, 0xE6, 0x76, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x8C, 0xEA, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x90, 0xBD, 0xE6, 0x72, 0x00, 0x00, 0xE6, 0x77, /* 0xA0-0xA3 */
+ 0x8C, 0xEB, 0xE6, 0x74, 0xE6, 0x75, 0xEE, 0x8A, /* 0xA4-0xA7 */
+ 0xE6, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x90, 0xE0, 0x93, 0xC7, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x92, 0x4E, 0x00, 0x00, 0x89, 0xDB, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x94, 0xEE, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x8B, 0x62, 0x00, 0x00, 0xEE, 0x8B, 0x92, 0xB2, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0xE6, 0x7A, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xE6, 0x78, 0x00, 0x00, 0x00, 0x00, 0x92, 0x6B, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0xBF, /* 0xC8-0xCB */
+ 0x8A, 0xD0, 0xE6, 0x79, 0x00, 0x00, 0x90, 0x7A, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x97, 0xC8, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x98, 0x5F, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0xE6, 0x7B, 0xE6, 0x87, /* 0xD8-0xDB */
+ 0x92, 0xB3, 0x00, 0x00, 0xE6, 0x86, 0xEE, 0x8C, /* 0xDC-0xDF */
+ 0xE6, 0x83, 0xE6, 0x8B, 0xE6, 0x84, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0xE6, 0x80, 0x00, 0x00, 0x92, 0xFA, 0xE6, 0x7E, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE6, 0x7C, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x97, 0x40, 0x8E, 0x90, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0xE6, 0x81, 0x00, 0x00, 0xE6, 0x7D, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0xEE, 0x8E, 0xE6, 0x85, /* 0xF4-0xF7 */
+ 0x8F, 0x94, 0x00, 0x00, 0x8C, 0xBF, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0x91, 0xF8, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_8B[512] = {
+ 0x96, 0x64, 0x89, 0x79, 0x88, 0xE0, 0x00, 0x00, /* 0x00-0x03 */
+ 0x93, 0xA3, 0x00, 0x00, 0x00, 0x00, 0xE6, 0x89, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0xE6, 0x88, 0x00, 0x00, 0x93, 0xE4, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xE6, 0x8D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0xE6, 0x82, 0x00, 0x00, 0xE6, 0x8C, 0xE6, 0x8E, /* 0x14-0x17 */
+ 0x00, 0x00, 0x8C, 0xAA, 0xE6, 0x8A, 0x8D, 0x75, /* 0x18-0x1B */
+ 0x00, 0x00, 0x8E, 0xD3, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0xE6, 0x8F, 0x97, 0x77, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0xE6, 0x92, 0x00, 0x00, /* 0x24-0x27 */
+ 0xE6, 0x95, 0x00, 0x00, 0x00, 0x00, 0xE6, 0x93, /* 0x28-0x2B */
+ 0x95, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE6, 0x90, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x8B, 0xDE, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0xE6, 0x94, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0xE6, 0x96, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0xE6, 0x9A, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0xE6, 0x97, 0x00, 0x00, 0xE6, 0x99, 0xE6, 0x98, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEE, 0x8F, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0xE6, 0x9B, 0x00, 0x00, /* 0x54-0x57 */
+ 0x8E, 0xAF, 0x00, 0x00, 0xE6, 0x9D, 0xE6, 0x9C, /* 0x58-0x5B */
+ 0x95, 0x88, 0x00, 0x00, 0x00, 0x00, 0xE6, 0x9F, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x8C, 0x78, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE6, 0x9E, /* 0x68-0x6B */
+ 0xE6, 0xA0, 0x00, 0x00, 0x00, 0x00, 0xE6, 0xA1, /* 0x6C-0x6F */
+ 0x8B, 0x63, 0xE3, 0xBF, 0x8F, 0xF7, 0x00, 0x00, /* 0x70-0x73 */
+ 0xE6, 0xA2, 0x00, 0x00, 0x00, 0x00, 0x8C, 0xEC, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0xE6, 0xA3, 0x00, 0x00, 0xEE, 0x90, /* 0x7C-0x7F */
+
+ 0xE6, 0xA4, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x5D, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x9D, 0xCC, 0x00, 0x00, /* 0x88-0x8B */
+ 0xE6, 0xA5, 0x00, 0x00, 0xE6, 0xA6, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x8F, 0x51, 0x00, 0x00, 0xE6, 0xA7, 0xE6, 0xA8, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0xE6, 0xA9, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0xE6, 0xAA, 0xE6, 0xAB, 0x00, 0x00, /* 0x98-0x9B */
+};
+
+static unsigned char u2c_8C[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x4A, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0xE6, 0xAC, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE6, 0xAE, /* 0x3C-0x3F */
+ 0x00, 0x00, 0xE6, 0xAD, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x93, 0xA4, 0x00, 0x00, /* 0x44-0x47 */
+ 0xE6, 0xAF, 0x00, 0x00, 0x96, 0x4C, 0x00, 0x00, /* 0x48-0x4B */
+ 0xE6, 0xB0, 0x00, 0x00, 0xE6, 0xB1, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xE6, 0xB2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0xE6, 0xB3, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x93, 0xD8, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x8F, 0xDB, 0xE6, 0xB4, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x8D, 0x8B, 0x98, 0xAC, /* 0x68-0x6B */
+ 0xE6, 0xB5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0xE6, 0xB6, 0x95, 0x5E, 0xE6, 0xB7, 0x00, 0x00, /* 0x78-0x7B */
+ 0xE6, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0xE6, 0xB8, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0xE6, 0xBA, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0xE6, 0xB9, 0xE6, 0xBB, 0x00, 0x00, /* 0x88-0x8B */
+ 0x96, 0x65, 0xE6, 0xBC, 0xE6, 0xBD, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0xE6, 0xBE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0xE6, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x8A, 0x4C, 0x92, 0xE5, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x95, 0x89, 0x8D, 0xE0, 0x8D, 0x76, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0x6E, /* 0xA4-0xA7 */
+ 0x89, 0xDD, 0x94, 0xCC, 0xE6, 0xC3, 0x8A, 0xD1, /* 0xA8-0xAB */
+ 0x90, 0xD3, 0xE6, 0xC2, 0xE6, 0xC7, 0x92, 0x99, /* 0xAC-0xAF */
+ 0x96, 0xE1, 0x00, 0x00, 0xE6, 0xC5, 0xE6, 0xC6, /* 0xB0-0xB3 */
+ 0x8B, 0x4D, 0x00, 0x00, 0xE6, 0xC8, 0x94, 0x83, /* 0xB4-0xB7 */
+ 0x91, 0xDD, 0x00, 0x00, 0x00, 0x00, 0x94, 0xEF, /* 0xB8-0xBB */
+ 0x93, 0x5C, 0xE6, 0xC4, 0x00, 0x00, 0x96, 0x66, /* 0xBC-0xBF */
+ 0x89, 0xEA, 0xE6, 0xCA, 0x98, 0x47, 0x92, 0xC0, /* 0xC0-0xC3 */
+ 0x98, 0x64, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x91, /* 0xC4-0xC7 */
+ 0xE6, 0xC9, 0x00, 0x00, 0x91, 0xAF, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0xE6, 0xDA, 0x91, 0x47, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x93, 0xF6, 0x00, 0x00, 0x95, 0x6F, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0xE6, 0xCD, 0x8E, 0x5E, /* 0xD8-0xDB */
+ 0x8E, 0x92, 0x00, 0x00, 0x8F, 0xDC, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x94, 0x85, 0x00, 0x00, 0x8C, 0xAB, 0xE6, 0xCC, /* 0xE0-0xE3 */
+ 0xE6, 0xCB, 0x00, 0x00, 0x95, 0x8A, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x8E, 0xBF, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x93, 0x71, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0xEE, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xEE, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0xE6, 0xCF, 0xE6, 0xD0, /* 0xF8-0xFB */
+ 0x8D, 0x77, 0xE6, 0xCE, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_8D[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0xE6, 0xD1, 0xE6, 0xD2, 0x00, 0x00, 0xE6, 0xD4, /* 0x04-0x07 */
+ 0x91, 0xA1, 0x00, 0x00, 0xE6, 0xD3, 0x8A, 0xE4, /* 0x08-0x0B */
+ 0x00, 0x00, 0xE6, 0xD6, 0x00, 0x00, 0xE6, 0xD5, /* 0x0C-0x0F */
+ 0xE6, 0xD7, 0x00, 0x00, 0xEE, 0x93, 0xE6, 0xD9, /* 0x10-0x13 */
+ 0xE6, 0xDB, 0x00, 0x00, 0xE6, 0xDC, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x90, 0xD4, 0x00, 0x00, 0x8E, 0xCD, 0xE6, 0xDD, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8A, 0x71, /* 0x68-0x6B */
+ 0x00, 0x00, 0xE6, 0xDE, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x91, 0x96, 0xE6, 0xDF, 0x00, 0x00, 0xE6, 0xE0, /* 0x70-0x73 */
+ 0x95, 0x8B, 0x00, 0x00, 0xEE, 0x94, 0x8B, 0x4E, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0xE6, 0xE1, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x92, 0xB4, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x89, 0x7A, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0xE6, 0xE2, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8E, 0xEF, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x90, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0xAB, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0xE6, 0xE5, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0xE6, 0xE4, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0xE6, 0xE3, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE6, 0xEB, /* 0xC8-0xCB */
+ 0xE6, 0xE9, 0x00, 0x00, 0x00, 0x00, 0xE6, 0xE6, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0xE6, 0xE8, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0xE6, 0xE7, 0xE6, 0xEA, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x8B, 0x97, 0x00, 0x00, 0xE6, 0xEE, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x90, 0xD5, 0x00, 0x00, 0xE6, 0xEF, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x8C, 0xD7, 0x00, 0x00, 0xE6, 0xEC, 0xE6, 0xED, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0x48, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0xB5, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x91, 0x48, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0xE6, 0xF0, 0x00, 0x00, 0x00, 0x00, 0xE6, 0xF3, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_8E[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0xE6, 0xF1, 0xE6, 0xF2, 0x97, 0x78, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0xA5, /* 0x0C-0x0F */
+ 0xE6, 0xF6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0xE6, 0xF4, 0xE6, 0xF5, 0xE6, 0xF7, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xE7, 0x48, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xE6, 0xFA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0xE6, 0xFB, 0xE6, 0xF9, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0xE6, 0xF8, 0x00, 0x00, /* 0x40-0x43 */
+ 0x92, 0xFB, 0x00, 0x00, 0x00, 0x00, 0xE7, 0x40, /* 0x44-0x47 */
+ 0xE7, 0x44, 0xE7, 0x41, 0xE6, 0xFC, 0x00, 0x00, /* 0x48-0x4B */
+ 0xE7, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xE7, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0xE7, 0x4A, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0xE7, 0x45, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0xD6, /* 0x5C-0x5F */
+ 0xE7, 0x47, 0x00, 0x00, 0x00, 0x00, 0xE7, 0x49, /* 0x60-0x63 */
+ 0xE7, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0xE7, 0x4C, 0x00, 0x00, /* 0x70-0x73 */
+ 0x8F, 0x52, 0x00, 0x00, 0xE7, 0x4B, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0xE7, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0xE7, 0x4E, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0xE7, 0x51, 0xE7, 0x50, 0x00, 0x00, 0xE7, 0x4F, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0xE7, 0x53, 0xE7, 0x52, /* 0x88-0x8B */
+ 0x00, 0x00, 0x96, 0xF4, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xE7, 0x55, 0x00, 0x00, 0xE7, 0x54, /* 0x90-0x93 */
+ 0xE7, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0xE7, 0x57, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0xE7, 0x59, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0xE7, 0x58, 0x90, 0x67, /* 0xA8-0xAB */
+ 0xE7, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x8B, 0xEB, /* 0xAC-0xAF */
+ 0xE7, 0x5B, 0xE7, 0x5D, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0xE7, 0x5E, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0xE7, 0x5F, 0xE7, 0x5C, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0xE7, 0x60, 0x00, 0x00, 0x8E, 0xD4, 0xE7, 0x61, /* 0xC8-0xCB */
+ 0x8B, 0x4F, 0x8C, 0x52, 0x00, 0x00, 0xEE, 0x96, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x8C, 0xAC, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE7, 0x62, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0xEE, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x93, 0x5D, 0xE7, 0x63, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE7, 0x66, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x8E, 0xB2, 0x00, 0x00, 0x00, 0x00, 0xE7, 0x65, /* 0xF8-0xFB */
+ 0xE7, 0x64, 0x8C, 0x79, 0xE7, 0x67, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_8F[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8A, 0x72, /* 0x00-0x03 */
+ 0x00, 0x00, 0xE7, 0x69, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x8D, 0xDA, 0xE7, 0x68, 0x00, 0x00, /* 0x08-0x0B */
+ 0xE7, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0xE7, 0x6B, 0xE7, 0x6D, /* 0x10-0x13 */
+ 0x95, 0xE3, 0xE7, 0x6A, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0xE7, 0x6C, 0x00, 0x00, 0xE7, 0x70, /* 0x18-0x1B */
+ 0xE7, 0x6E, 0x8B, 0x50, 0x00, 0x00, 0xE7, 0x6F, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0xE7, 0x72, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x94, 0x79, 0x97, 0xD6, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8F, 0x53, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE7, 0x73, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x97, 0x41, 0xE7, 0x75, 0x00, 0x00, 0xE7, 0x74, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0xE7, 0x78, 0x97, 0x60, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0xE7, 0x77, 0x00, 0x00, /* 0x40-0x43 */
+ 0x8A, 0x8D, 0xE7, 0x76, 0xE7, 0x7B, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0xE7, 0x7A, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0xE7, 0x79, 0x93, 0x51, 0xE7, 0x7C, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE7, 0x7D, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0xE7, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x8D, 0x8C, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x8C, 0x44, 0xE7, 0x80, 0xE7, 0x81, /* 0x60-0x63 */
+ 0xE7, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x68, /* 0x98-0x9B */
+ 0xE7, 0x83, 0x00, 0x00, 0x8E, 0xAB, 0xE7, 0x84, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE7, 0x85, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0x9F, /* 0xA4-0xA7 */
+ 0x99, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0xE7, 0x86, 0xE3, 0x90, 0xE7, 0x87, /* 0xAC-0xAF */
+ 0x92, 0x43, 0x90, 0x4A, 0x94, 0x5F, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE7, 0x88, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x95, 0xD3, 0x92, 0xD2, /* 0xB8-0xBB */
+ 0x8D, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x92, 0x48, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x89, 0x49, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x96, 0x98, 0x90, 0x76, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x8C, 0x7D, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x8B, 0xDF, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x95, 0xD4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0xE7, 0x89, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0xE7, 0x8B, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0xE7, 0x8A, 0x89, 0xDE, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x93, 0xF4, 0xE7, 0x8C, 0x94, 0x97, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x93, 0x52, 0x00, 0x00, 0xE7, 0x8D, /* 0xEC-0xEF */
+ 0x8F, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xE7, 0x8F, 0x00, 0x00, 0x00, 0x00, 0x96, 0xC0, /* 0xF4-0xF7 */
+ 0xE7, 0x9E, 0xE7, 0x91, 0xE7, 0x92, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x92, 0xC7, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_90[512] = {
+ 0x91, 0xDE, 0x91, 0x97, 0x00, 0x00, 0x93, 0xA6, /* 0x00-0x03 */
+ 0x00, 0x00, 0xE7, 0x90, 0x8B, 0x74, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE7, 0x99, /* 0x08-0x0B */
+ 0x00, 0x00, 0xE7, 0x96, 0xE7, 0xA3, 0x93, 0xA7, /* 0x0C-0x0F */
+ 0x92, 0x80, 0xE7, 0x93, 0x00, 0x00, 0x92, 0xFC, /* 0x10-0x13 */
+ 0x93, 0x72, 0xE7, 0x94, 0xE7, 0x98, 0x90, 0x80, /* 0x14-0x17 */
+ 0x00, 0x00, 0x94, 0x87, 0x92, 0xCA, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x90, 0xC0, 0xE7, 0x97, 0x91, 0xAC, /* 0x1C-0x1F */
+ 0x91, 0xA2, 0xE7, 0x95, 0x88, 0xA7, 0x98, 0x41, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE7, 0x9A, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x91, 0xDF, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x8F, 0x54, 0x90, 0x69, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0xE7, 0x9C, 0xE7, 0x9B, 0x00, 0x00, /* 0x34-0x37 */
+ 0x88, 0xED, 0xE7, 0x9D, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x95, 0x4E, 0x00, 0x00, 0xE7, 0xA5, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x93, 0xD9, 0x90, 0x8B, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x92, 0x78, 0x00, 0x00, 0x8B, 0xF6, /* 0x44-0x47 */
+ 0x00, 0x00, 0xE7, 0xA4, 0x97, 0x56, 0x89, 0x5E, /* 0x48-0x4B */
+ 0x00, 0x00, 0x95, 0xD5, 0x89, 0xDF, 0xE7, 0x9F, /* 0x4C-0x4F */
+ 0xE7, 0xA0, 0xE7, 0xA1, 0xE7, 0xA2, 0x93, 0xB9, /* 0x50-0x53 */
+ 0x92, 0x42, 0x88, 0xE1, 0xE7, 0xA6, 0x00, 0x00, /* 0x54-0x57 */
+ 0xE7, 0xA7, 0xEA, 0xA1, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x91, 0xBB, 0x00, 0x00, 0xE7, 0xA8, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x89, 0x93, 0x91, 0x6B, 0x00, 0x00, 0x8C, 0xAD, /* 0x60-0x63 */
+ 0x00, 0x00, 0x97, 0x79, 0x00, 0x00, 0xEE, 0x99, /* 0x64-0x67 */
+ 0xE7, 0xA9, 0x93, 0x4B, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x91, 0x98, 0x8E, 0xD5, 0xE7, 0xAA, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0xE7, 0xAD, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x8F, 0x85, 0xE7, 0xAB, 0x91, 0x4A, /* 0x74-0x77 */
+ 0x91, 0x49, 0x00, 0x00, 0x88, 0xE2, 0x00, 0x00, /* 0x78-0x7B */
+ 0x97, 0xC9, 0xE7, 0xAF, 0x00, 0x00, 0x94, 0xF0, /* 0x7C-0x7F */
+
+ 0xE7, 0xB1, 0xE7, 0xB0, 0xE7, 0xAE, 0xE2, 0x84, /* 0x80-0x83 */
+ 0x8A, 0xD2, 0x00, 0x00, 0x00, 0x00, 0xE7, 0x8E, /* 0x84-0x87 */
+ 0x00, 0x00, 0xE7, 0xB3, 0xE7, 0xB2, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE7, 0xB4, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x97, 0x57, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0xDF, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x96, 0x4D, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xE7, 0xB5, 0x00, 0x00, 0x8E, 0xD7, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE7, 0xB6, /* 0xAC-0xAF */
+ 0x00, 0x00, 0xE7, 0xB7, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0xE7, 0xB8, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x93, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x88, 0xE8, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x8D, 0x78, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x98, 0x59, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE7, 0xBC, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0xEE, 0x9A, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x8C, 0x53, 0xE7, 0xB9, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0xE7, 0xBA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x95, 0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x8A, 0x73, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x97, 0x58, 0x00, 0x00, 0x8B, 0xBD, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x93, 0x73, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_91[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0xE7, 0xBD, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0xE7, 0xBE, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0xEE, 0x9C, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0xE7, 0xBF, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEE, 0x9D, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x93, 0x41, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xE7, 0xC1, 0x00, 0x00, 0xE7, 0xC0, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x93, 0xD1, 0xE7, 0xC2, 0x8F, 0x55, /* 0x48-0x4B */
+ 0x8E, 0xDE, 0x94, 0x7A, 0x92, 0x91, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x8E, 0xF0, 0x00, 0x00, /* 0x50-0x53 */
+ 0x90, 0x8C, 0x00, 0x00, 0xE7, 0xC3, 0x00, 0x00, /* 0x54-0x57 */
+ 0xE7, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x90, 0x7C, 0xE7, 0xC5, /* 0x60-0x63 */
+ 0x00, 0x00, 0xE7, 0xC6, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0xE7, 0xC7, 0x97, 0x8F, 0x00, 0x00, /* 0x68-0x6B */
+ 0x8F, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0xE7, 0xC9, 0xE7, 0xC8, /* 0x70-0x73 */
+ 0x00, 0x00, 0x8D, 0x79, 0x00, 0x00, 0x8D, 0x93, /* 0x74-0x77 */
+ 0x8E, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0xE7, 0xCC, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8F, 0x86, /* 0x84-0x87 */
+ 0x00, 0x00, 0xE7, 0xCB, 0x00, 0x00, 0xE7, 0xCA, /* 0x88-0x8B */
+ 0x00, 0x00, 0x91, 0xE7, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x8C, 0xED, 0x00, 0x00, 0x90, 0xC1, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0xAE, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x8F, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0xE7, 0xCD, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x8F, 0xDD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0xE7, 0xD0, 0xE7, 0xCE, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE7, 0xCF, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0xE7, 0xD2, 0xE7, 0xD1, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x8F, 0xF8, 0x00, 0x00, 0xE7, 0xD3, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0xE7, 0xD4, 0xE7, 0xD5, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x94, 0xCE, 0x8D, 0xD1, /* 0xC4-0xC7 */
+ 0x8E, 0xDF, 0xE7, 0xD6, 0x00, 0x00, 0xE7, 0xD7, /* 0xC8-0xCB */
+ 0x97, 0xA2, 0x8F, 0x64, 0x96, 0xEC, 0x97, 0xCA, /* 0xCC-0xCF */
+ 0xE7, 0xD8, 0x8B, 0xE0, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0xE7, 0xD9, 0xEE, 0x9F, /* 0xD4-0xD7 */
+ 0x93, 0x42, 0x00, 0x00, 0xEE, 0x9E, 0xE7, 0xDC, /* 0xD8-0xDB */
+ 0x8A, 0x98, 0x90, 0x6A, 0xEE, 0xA0, 0xE7, 0xDA, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xE7, 0xDB, 0x00, 0x00, 0x92, 0xDE, /* 0xE0-0xE3 */
+ 0xEE, 0xA3, 0xEE, 0xA4, 0x96, 0x74, 0x8B, 0xFA, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0xEE, 0xA1, 0xEE, 0xA2, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0xE7, 0xDE, 0xE7, 0xDF, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0xE7, 0xDD, 0x00, 0x00, 0x00, 0x00, 0xE7, 0xE1, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_92[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0xEE, 0xA5, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0xEE, 0xA7, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x93, 0xDD, 0x8A, 0x62, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xEE, 0xA6, 0xE7, 0xE5, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0xE7, 0xE2, 0xE7, 0xE4, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0xE7, 0xE0, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0xE8, 0x6E, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0xE7, 0xE3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x97, 0xE9, 0x00, 0x00, 0x00, 0x00, 0x8C, 0xD8, /* 0x34-0x37 */
+ 0x00, 0x00, 0xEE, 0xAE, 0xEE, 0xA8, 0x00, 0x00, /* 0x38-0x3B */
+ 0xEE, 0xAA, 0x00, 0x00, 0x00, 0x00, 0xE7, 0xED, /* 0x3C-0x3F */
+ 0xEE, 0xA9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x93, 0x53, 0xE7, 0xE8, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0xE7, 0xEB, 0xE7, 0xE9, 0x00, 0x00, 0xE7, 0xEE, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0xEE, 0xAB, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xE7, 0xEF, 0xEE, 0xAD, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE7, 0xE7, /* 0x54-0x57 */
+ 0x00, 0x00, 0xEE, 0xAC, 0xE7, 0xF4, 0x89, 0x94, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0xE7, 0xE6, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x94, 0xAB, 0x00, 0x00, /* 0x60-0x63 */
+ 0xE7, 0xEA, 0x00, 0x00, 0x8F, 0xDE, 0xEE, 0xAF, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x8D, 0x7A, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEE, 0xB1, /* 0x74-0x77 */
+ 0xEE, 0xB2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x96, 0x67, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x8B, 0xE2, 0x00, 0x00, 0x00, 0x00, 0x8F, 0x65, /* 0x80-0x83 */
+ 0x00, 0x00, 0x93, 0xBA, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0xED, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x91, 0x4C, 0x00, 0x00, 0xE7, 0xF2, /* 0x90-0x93 */
+ 0x00, 0x00, 0xE7, 0xEC, 0xE7, 0xF1, 0x00, 0x00, /* 0x94-0x97 */
+ 0x96, 0xC1, 0x00, 0x00, 0x92, 0xB6, 0xE7, 0xF3, /* 0x98-0x9B */
+ 0xE7, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEE, 0xB0, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x91, 0x4B, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE7, 0xF7, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xE7, 0xF6, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE7, 0xF5, /* 0xCC-0xCF */
+ 0xEE, 0xB6, 0x00, 0x00, 0x96, 0x4E, 0xEE, 0xBA, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0xEE, 0xB8, 0x00, 0x00, 0xEE, 0xB4, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0xEE, 0xB5, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0xEE, 0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x8F, 0x9B, 0x00, 0x00, 0x00, 0x00, 0xEE, 0xB3, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0xE7, 0xF8, 0x95, 0xDD, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x89, 0x73, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x95, 0x65, 0x92, 0x92, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x8B, 0x98, 0xED, 0x49, 0xE7, 0xFA, 0xEE, 0xBD, /* 0xF8-0xFB */
+ 0x8D, 0x7C, 0x00, 0x00, 0x00, 0x00, 0xEE, 0xC0, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_93[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0xEE, 0xC2, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x8E, 0x4B, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE7, 0xF9, /* 0x0C-0x0F */
+ 0x90, 0x8D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x90, 0x8E, 0xE8, 0x40, 0xE8, 0x42, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0xEE, 0xC1, 0xEE, 0xBF, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x8F, 0xF9, 0xEE, 0xBC, 0xE8, 0x41, 0xE8, 0x43, /* 0x20-0x23 */
+ 0x00, 0x00, 0xEE, 0xBB, 0x8B, 0xD1, 0x00, 0x00, /* 0x24-0x27 */
+ 0x95, 0x64, 0x00, 0x00, 0x00, 0x00, 0x8E, 0xE0, /* 0x28-0x2B */
+ 0x98, 0x42, 0x00, 0x00, 0xE7, 0xFC, 0x8D, 0xF6, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x98, 0x5E, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0xE8, 0x45, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0xE8, 0x44, 0xE8, 0x46, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0xE7, 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0xED, 0x42, 0x00, 0x00, 0x00, 0x00, 0x93, 0xE7, /* 0x48-0x4B */
+ 0x00, 0x00, 0x93, 0x74, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x92, 0xD5, 0x00, 0x00, 0xE8, 0x4B, 0xEE, 0xC4, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x62, /* 0x58-0x5B */
+ 0xE8, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xE8, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x8C, 0x4C, 0x00, 0x00, 0xE8, 0x4A, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xEE, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x8C, 0xAE, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0xE8, 0x49, 0x00, 0x00, 0x8F, 0xDF, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x8A, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0xE8, 0x4F, 0x00, 0x00, 0x8D, 0xBD, 0x91, 0x99, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x92, 0xC8, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0xEE, 0xC5, 0x00, 0x00, 0x00, 0x00, 0x8A, 0x5A, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0xE8, 0x4D, 0xE8, 0x4E, 0x92, 0xC1, 0x00, 0x00, /* 0xAC-0xAF */
+ 0xE8, 0x4C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xE8, 0x50, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x56, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0xEE, 0xC6, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0xE8, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0xE8, 0x58, 0x93, 0x4C, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0xE8, 0x51, 0xE8, 0x52, /* 0xD4-0xD7 */
+ 0xE8, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0xE8, 0x57, 0xEE, 0xC7, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x8B, 0xBE, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0xE8, 0x5A, 0xE8, 0x54, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xE8, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0xEE, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+};
+
+static unsigned char u2c_94[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x5E, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x5F, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xE8, 0x60, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x5D, /* 0x10-0x13 */
+ 0xE8, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x8F, 0xE0, 0x93, 0xA8, 0xE8, 0x5B, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0xE8, 0x64, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x62, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0xEE, 0xC9, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0xE8, 0x63, 0xE8, 0x61, 0x00, 0x00, /* 0x34-0x37 */
+ 0x91, 0xF6, 0x00, 0x00, 0xE8, 0x65, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0xE8, 0x66, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0xE8, 0x68, 0xEE, 0xCA, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0xEE, 0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x8A, 0xD3, 0xE8, 0x67, 0x96, 0xF8, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0xE8, 0x73, 0xE8, 0x69, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0xE8, 0x6C, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xE8, 0x6A, 0x00, 0x00, 0xE8, 0x6B, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0xE8, 0x6D, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xE8, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0xE8, 0x70, 0x00, 0x00, 0xE8, 0x71, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0xE8, 0x74, 0xE8, 0x72, 0xE8, 0x75, 0xE8, 0x77, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0xE8, 0x76, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+};
+
+static unsigned char u2c_95[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0xB7, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x96, 0xE5, 0x00, 0x00, 0xE8, 0x78, 0x91, 0x4D, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x79, /* 0x84-0x87 */
+ 0x00, 0x00, 0x95, 0xC2, 0xE8, 0x7A, 0x8A, 0x4A, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x5B, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x8A, 0xD5, 0xEE, 0xCC, 0x8A, 0xD4, /* 0x90-0x93 */
+ 0xE8, 0x7B, 0x00, 0x00, 0xE8, 0x7C, 0x00, 0x00, /* 0x94-0x97 */
+ 0xE8, 0x7D, 0xE8, 0x7E, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0xE8, 0x80, 0x00, 0x00, 0x8A, 0xD6, 0x8A, 0x74, /* 0xA0-0xA3 */
+ 0x8D, 0x7D, 0x94, 0xB4, 0x00, 0x00, 0xE8, 0x82, /* 0xA4-0xA7 */
+ 0xE8, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0xE8, 0x83, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x89, 0x7B, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xE8, 0x86, 0x00, 0x00, 0xE8, 0x85, /* 0xB8-0xBB */
+ 0xE8, 0x84, 0x00, 0x00, 0xE8, 0x87, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x8A, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0xC5, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0xE8, 0x88, 0x00, 0x00, /* 0xC8-0xCB */
+ 0xE8, 0x8C, 0xE8, 0x8B, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xE8, 0x8E, 0xE8, 0x8D, 0xE8, 0x8F, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x93, 0xAC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0xE8, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xE8, 0x91, 0xE8, 0x93, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0xE8, 0x92, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+};
+
+static unsigned char u2c_96[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x95, 0x8C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0xE8, 0x94, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0xE8, 0x95, 0x00, 0x00, 0x8D, 0xE3, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0xE8, 0x96, 0xE8, 0x97, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x96, 0x68, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0x6A, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0xA2, /* 0x3C-0x3F */
+ 0x91, 0xC9, 0x00, 0x00, 0xE8, 0x98, 0x00, 0x00, /* 0x40-0x43 */
+ 0x95, 0x8D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x9B, /* 0x48-0x4B */
+ 0xE8, 0x99, 0x8D, 0x7E, 0x00, 0x00, 0xE8, 0x9A, /* 0x4C-0x4F */
+ 0x8C, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0xC3, /* 0x58-0x5B */
+ 0xE8, 0x9D, 0xE8, 0x9F, 0xE8, 0x9E, 0xE8, 0xA0, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x89, 0x40, 0x90, 0x77, /* 0x60-0x63 */
+ 0x8F, 0x9C, 0x8A, 0xD7, 0xE8, 0xA1, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x94, 0x86, 0x00, 0x00, /* 0x68-0x6B */
+ 0xE8, 0xA3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x89, 0x41, 0x00, 0x00, 0xE8, 0xA2, 0x92, 0xC2, /* 0x70-0x73 */
+ 0x00, 0x00, 0x97, 0xCB, 0x93, 0xA9, 0xE8, 0x9C, /* 0x74-0x77 */
+ 0x97, 0xA4, 0x00, 0x00, 0x8C, 0xAF, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x97, 0x7A, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x8B, 0xF7, 0x97, 0xB2, 0x00, 0x00, /* 0x84-0x87 */
+ 0x8C, 0x47, 0x00, 0x00, 0x91, 0xE0, 0xE4, 0x40, /* 0x88-0x8B */
+ 0x00, 0x00, 0xE8, 0xA4, 0x8A, 0x4B, 0x90, 0x8F, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x8A, 0x75, 0xE8, 0xA6, 0x00, 0x00, 0xE8, 0xA7, /* 0x94-0x97 */
+ 0xE8, 0xA5, 0x8C, 0x84, 0x00, 0x00, 0x8D, 0xDB, /* 0x98-0x9B */
+ 0x8F, 0xE1, 0xEE, 0xCF, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x89, 0x42, 0x00, 0x00, 0x00, 0x00, 0x97, 0xD7, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0xA9, /* 0xA4-0xA7 */
+ 0xE7, 0xAC, 0x00, 0x00, 0xE8, 0xA8, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEE, 0xD0, /* 0xAC-0xAF */
+ 0xE8, 0xAC, 0xE8, 0xAA, 0xE8, 0xAB, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0xE8, 0xAD, 0x00, 0x00, 0xE8, 0xAE, 0x97, 0xEA, /* 0xB4-0xB7 */
+ 0xE8, 0xAF, 0xE8, 0xB0, 0x00, 0x00, 0x90, 0xC7, /* 0xB8-0xBB */
+ 0x94, 0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x90, 0x9D, 0x8A, 0xE5, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x97, 0x59, 0x89, 0xEB, 0x8F, 0x57, 0x8C, 0xD9, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xE8, 0xB3, 0x00, 0x00, 0xE8, 0xB2, /* 0xC8-0xCB */
+ 0x8E, 0x93, 0xE8, 0xB4, 0xE8, 0xB1, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x8E, 0x47, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0xE8, 0xB8, 0xE5, 0xAB, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x99, 0xD4, 0x00, 0x00, 0x90, 0x97, /* 0xD8-0xDB */
+ 0xE8, 0xB6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x97, 0xA3, 0x93, 0xEF, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x89, 0x4A, 0x00, 0x00, 0x90, 0xE1, 0x8E, 0xB4, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x95, 0xB5, 0x00, 0x00, 0x89, 0x5F, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x97, 0xEB, 0x97, 0x8B, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xE8, 0xB9, 0x00, 0x00, 0x93, 0x64, /* 0xF8-0xFB */
+};
+
+static unsigned char u2c_97[512] = {
+ 0x8E, 0xF9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0xE8, 0xBA, 0x00, 0x00, 0xE8, 0xBB, 0x90, 0x6B, /* 0x04-0x07 */
+ 0xE8, 0xBC, 0x00, 0x00, 0x97, 0xEC, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0xE8, 0xB7, 0xE8, 0xBE, 0xE8, 0xC0, /* 0x0C-0x0F */
+ 0x00, 0x00, 0xE8, 0xBF, 0x00, 0x00, 0xE8, 0xBD, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0xE8, 0xC1, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0xE8, 0xC2, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x91, 0x9A, 0x00, 0x00, 0x89, 0xE0, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0xE8, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x96, 0xB6, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xE8, 0xC4, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xE8, 0xC5, 0x00, 0x00, 0x98, 0x49, 0xEE, 0xD1, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x9E, 0x50, 0xE8, 0xC6, 0x00, 0x00, 0xEE, 0xD2, /* 0x38-0x3B */
+ 0x00, 0x00, 0xE8, 0xC7, 0xE8, 0xC8, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0xE8, 0xCC, 0xEE, 0xD3, /* 0x40-0x43 */
+ 0xE8, 0xC9, 0x00, 0x00, 0xE8, 0xCA, 0x00, 0x00, /* 0x44-0x47 */
+ 0xE8, 0xCB, 0xE8, 0xCD, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0xEE, 0xD4, 0x00, 0x00, 0xEE, 0xD5, /* 0x4C-0x4F */
+ 0x00, 0x00, 0xEE, 0xD6, 0x90, 0xC2, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0xEE, 0xD7, 0x96, 0xF5, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x90, 0xC3, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0xE8, 0xCE, 0x00, 0x00, 0x94, 0xF1, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xE8, 0xCF, 0xEA, 0x72, 0x96, 0xCA, 0x00, 0x00, /* 0x60-0x63 */
+ 0xE8, 0xD0, 0x00, 0x00, 0xE8, 0xD1, 0x00, 0x00, /* 0x64-0x67 */
+ 0xE8, 0xD2, 0x8A, 0x76, 0x00, 0x00, 0xE8, 0xD4, /* 0x68-0x6B */
+ 0x00, 0x00, 0x90, 0x78, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0xE8, 0xD5, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x8C, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0xE8, 0xD6, 0xE8, 0xDA, 0x00, 0x00, /* 0x78-0x7B */
+ 0xE8, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0xE8, 0xD9, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x8A, 0x93, 0xE8, 0xD7, 0xE8, 0xDB, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0xDC, /* 0x88-0x8B */
+ 0x00, 0x00, 0x88, 0xC6, 0x00, 0x00, 0xE8, 0xDD, /* 0x8C-0x8F */
+ 0xE8, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x8F, 0xE2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0xE8, 0xDF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x8B, 0x66, 0x00, 0x00, 0x00, 0x00, 0xE8, 0xE2, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0xE8, 0xE1, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xE8, 0xE0, 0x00, 0x00, 0x00, 0x00, 0xE6, 0x91, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x95, 0xDA, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0xE3, /* 0xB0-0xB3 */
+ 0xE8, 0xE4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0xE5, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0xE8, 0xE6, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0xE8, 0xE7, 0x00, 0x00, 0x00, 0x00, 0xE8, 0xE8, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8A, 0xD8, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0xE8, 0xE9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0xE8, 0xEA, 0x94, 0x42, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0xE8, 0xEC, 0x89, 0xB9, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0xE8, 0xEF, 0xE8, 0xEE, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x43, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8B, 0xBF, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_98[512] = {
+ 0x00, 0x00, 0x95, 0xC5, 0x92, 0xB8, 0x8D, 0xA0, /* 0x00-0x03 */
+ 0x00, 0x00, 0x8D, 0x80, 0x8F, 0x87, 0x00, 0x00, /* 0x04-0x07 */
+ 0x90, 0x7B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0xE8, 0xF1, 0x00, 0x00, 0x00, 0x00, 0xE8, 0xF0, /* 0x0C-0x0F */
+ 0x97, 0x61, 0x8A, 0xE6, 0x94, 0xD0, 0x93, 0xDA, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x9C, /* 0x14-0x17 */
+ 0x97, 0xCC, 0x00, 0x00, 0x8C, 0x7A, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0xE8, 0xF4, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0xE8, 0xF3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x96, 0x6A, 0x93, 0xAA, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x89, 0x6F, 0x00, 0x00, 0x00, 0x00, 0xE8, 0xF5, /* 0x34-0x37 */
+ 0xE8, 0xF2, 0x00, 0x00, 0x00, 0x00, 0x95, 0x70, /* 0x38-0x3B */
+ 0x97, 0x8A, 0xE8, 0xF6, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0xE8, 0xF7, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0xF9, /* 0x48-0x4B */
+ 0x91, 0xE8, 0x8A, 0x7A, 0x8A, 0x7B, 0xE8, 0xF8, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x8A, 0xE7, 0x8C, 0xB0, 0x00, 0x00, 0xEE, 0xD8, /* 0x54-0x57 */
+ 0x8A, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x93, 0x5E, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x97, 0xDE, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0xEE, 0xD9, 0x00, 0x00, 0x8C, 0xDA, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0xFA, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0xFB, /* 0x6C-0x6F */
+ 0xE8, 0xFC, 0xE9, 0x40, 0x00, 0x00, 0xE9, 0x42, /* 0x70-0x73 */
+ 0xE9, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x95, 0x97, 0x00, 0x00, 0xE9, 0x43, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE9, 0x44, /* 0xAC-0xAF */
+ 0x00, 0x00, 0xE9, 0x45, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0x46, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE9, 0x48, /* 0xC0-0xC3 */
+ 0xE9, 0x47, 0x00, 0x00, 0xE9, 0x49, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0xF2, /* 0xD8-0xDB */
+ 0xE3, 0xCA, 0x00, 0x00, 0x00, 0x00, 0x90, 0x48, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x8B, 0x51, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0xE9, 0x4A, 0x00, 0x00, 0xE9, 0x4B, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x99, 0xAA, 0x9F, 0x5A, 0x94, 0xD1, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x88, 0xF9, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x88, 0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x8E, 0x94, 0x96, 0x4F, 0x8F, 0xFC, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_99[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE9, 0x4C, /* 0x00-0x03 */
+ 0x00, 0x00, 0x96, 0xDD, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0xE9, 0x4D, 0x97, 0x7B, 0x00, 0x00, /* 0x08-0x0B */
+ 0x89, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x8E, 0x60, 0x00, 0x00, 0xE9, 0x4E, 0x89, 0xEC, /* 0x10-0x13 */
+ 0xE9, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0xE9, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0xE9, 0x52, 0xE9, 0x53, 0x00, 0x00, /* 0x1C-0x1F */
+ 0xE9, 0x55, 0xE9, 0x51, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0xE9, 0x54, 0x00, 0x00, 0x00, 0x00, 0xEE, 0xDC, /* 0x24-0x27 */
+ 0x8A, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0xE9, 0x56, 0x00, 0x00, 0xE9, 0x57, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0xE9, 0x58, 0xE9, 0x59, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0x5A, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0xE9, 0x5C, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0xE9, 0x5B, 0x00, 0x00, 0xE9, 0x5E, /* 0x48-0x4B */
+ 0xE9, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xE9, 0x5D, 0xE9, 0x5F, 0xE9, 0x60, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0xE9, 0x62, 0x00, 0x00, 0x8B, 0xC0, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x8E, 0xF1, 0xE9, 0x63, /* 0x94-0x97 */
+ 0xE9, 0x64, 0x8D, 0x81, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0xEE, 0xDE, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0xE9, 0x65, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x8A, 0x5D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x94, 0x6E, 0xE9, 0x66, 0xE9, 0x67, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x79, /* 0xB0-0xB3 */
+ 0x93, 0xE9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0xE9, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x94, 0x9D, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x91, 0xCA, 0x89, 0x77, 0x8B, 0xEC, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x8B, 0xED, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x92, 0x93, 0xE9, 0x6D, 0x8B, 0xEE, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x89, 0xED, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xE9, 0x6C, 0x00, 0x00, 0x00, 0x00, 0xE9, 0x6A, /* 0xD8-0xDB */
+ 0x00, 0x00, 0xE9, 0x6B, 0x00, 0x00, 0xE9, 0x69, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0x77, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0xE9, 0x6E, 0xE9, 0x6F, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0xE9, 0x70, 0xE9, 0x71, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0xE9, 0x73, 0x00, 0x00, 0x00, 0x00, 0xE9, 0x72, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8F, 0x78, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_9A[512] = {
+ 0x00, 0x00, 0xE9, 0x74, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0xE9, 0x76, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x8B, 0x52, 0xE9, 0x75, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x91, 0x9B, 0x8C, 0xB1, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0xE9, 0x78, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x91, 0xCB, 0x00, 0x00, 0x00, 0x00, 0xE9, 0x79, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x93, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE9, 0x7A, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0x80, 0x00, 0x00, /* 0x3C-0x3F */
+ 0xE9, 0x7D, 0x00, 0x00, 0xE9, 0x7C, 0xE9, 0x7E, /* 0x40-0x43 */
+ 0x00, 0x00, 0xE9, 0x7B, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0xE9, 0x82, 0xEE, 0xDF, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0xE9, 0x81, 0x00, 0x00, 0xE9, 0x84, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x8B, 0xC1, 0xE9, 0x83, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE9, 0x85, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0x86, 0x00, 0x00, /* 0x60-0x63 */
+ 0xE9, 0x88, 0xE9, 0x87, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0xE9, 0x89, 0xE9, 0x8B, 0xE9, 0x8A, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x8D, 0x9C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0xE9, 0x8C, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0xE9, 0x8D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x8A, 0x5B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0xE9, 0x8E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0xE9, 0x8F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x90, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE9, 0x90, /* 0xCC-0xCF */
+ 0x00, 0x00, 0xE9, 0x91, 0x00, 0x00, 0xE9, 0x92, /* 0xD0-0xD3 */
+ 0xE9, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x8D, 0x82, 0xEE, 0xE0, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0xEE, 0xE1, 0x00, 0x00, 0xE9, 0x94, 0xE9, 0x95, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0x96, 0xE9, 0x97, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0x98, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x94, 0xAF, 0xE9, 0x9A, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x95, 0x45, 0xE9, 0x9B, 0xE9, 0x99, /* 0xEC-0xEF */
+ 0x00, 0x00, 0xE9, 0x9D, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xE9, 0x9C, 0x00, 0x00, 0x00, 0x00, 0xE9, 0x9E, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE9, 0x9F, /* 0xF8-0xFB */
+};
+
+static unsigned char u2c_9B[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0xA0, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0xE9, 0xA1, 0x00, 0x00, 0xE9, 0xA2, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE9, 0xA3, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0xA4, 0xE9, 0xA5, /* 0x20-0x23 */
+ 0x00, 0x00, 0xE9, 0xA6, 0x00, 0x00, 0xE9, 0xA7, /* 0x24-0x27 */
+ 0xE9, 0xA8, 0xE9, 0xA9, 0xE9, 0xAA, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0xAB, 0xE9, 0xAC, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x9F, 0x54, 0xE9, 0xAD, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2, 0xF6, /* 0x38-0x3B */
+ 0x8B, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x8A, 0x40, 0x8D, 0xB0, 0xE9, 0xAF, /* 0x40-0x43 */
+ 0xE9, 0xAE, 0x96, 0xA3, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0xE9, 0xB1, 0xE9, 0xB2, 0xE9, 0xB0, /* 0x4C-0x4F */
+ 0x00, 0x00, 0xE9, 0xB3, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x96, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0xE9, 0xB4, 0x00, 0x00, 0x8B, 0x9B, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0x44, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0xEE, 0xE3, 0x00, 0x00, /* 0x70-0x73 */
+ 0xE9, 0xB5, 0xEE, 0xE2, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE9, 0xB7, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x88, 0xBC, 0xEE, 0xE4, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xE9, 0xB8, 0x95, 0xA9, 0xE9, 0xB6, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0xB9, 0xE9, 0xBA, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE9, 0xBB, /* 0x9C-0x9F */
+ 0xE9, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xE9, 0xBD, 0x00, 0x00, 0x96, 0x8E, 0x8E, 0x4C, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x8D, 0xF8, 0x91, 0x4E, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0xEE, 0xE5, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0xE9, 0xBE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xE9, 0xC1, 0x00, 0x00, 0xEE, 0xE6, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0xE9, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0xC2, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x8C, 0xEF, 0xE9, 0xC0, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE9, 0xC3, /* 0xCC-0xCF */
+ 0x00, 0x00, 0xE9, 0xC4, 0xE9, 0xC5, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xE9, 0xC9, 0x00, 0x00, 0x8E, 0x49, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0xE2, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xE9, 0xCA, 0xE9, 0xC7, 0xE9, 0xC6, /* 0xE0-0xE3 */
+ 0xE9, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x8C, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0xE9, 0xCE, 0xE9, 0xCD, 0xE9, 0xCC, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x88, 0xB1, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+};
+
+static unsigned char u2c_9C[512] = {
+ 0xEE, 0xE7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0xE9, 0xD8, 0x00, 0x00, 0xE9, 0xD4, 0x00, 0x00, /* 0x04-0x07 */
+ 0xE9, 0xD5, 0xE9, 0xD1, 0xE9, 0xD7, 0x00, 0x00, /* 0x08-0x0B */
+ 0xE9, 0xD3, 0x8A, 0x82, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x98, 0x6B, 0x00, 0x00, 0xE9, 0xD6, 0xE9, 0xD2, /* 0x10-0x13 */
+ 0xE9, 0xD0, 0xE9, 0xCF, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE9, 0xDA, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0xE9, 0xDD, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0xE9, 0xDC, 0xE9, 0xDB, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x95, 0x68, 0xE9, 0xD9, 0x88, 0xF1, /* 0x2C-0x2F */
+ 0xE9, 0xDE, 0x00, 0x00, 0xE9, 0xE0, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x8A, 0x8F, 0xE9, 0xCB, 0x89, 0x56, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0xE2, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0xE1, 0xE9, 0xDF, /* 0x44-0x47 */
+ 0x92, 0x4C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x96, 0x90, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0xD8, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0xE3, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xE9, 0xE4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE9, 0xE5, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0xE6, 0x00, 0x00, /* 0x74-0x77 */
+ 0xE9, 0xE7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x92, 0xB9, 0x00, 0x00, 0xE9, 0xE8, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x94, 0xB5, 0x00, 0x00, 0xE9, 0xED, /* 0xE8-0xEB */
+ 0xE9, 0xE9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0xE9, 0xEA, 0x00, 0x00, 0x00, 0x00, 0x96, 0x50, /* 0xF0-0xF3 */
+ 0x96, 0xC2, 0x00, 0x00, 0x93, 0xCE, 0x00, 0x00, /* 0xF4-0xF7 */
+};
+
+static unsigned char u2c_9D[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE9, 0xEE, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0xEF, 0x93, 0xBC, /* 0x04-0x07 */
+ 0xE9, 0xEC, 0xE9, 0xEB, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x89, 0xA8, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0xF7, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0xE9, 0xF6, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x95, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE9, 0xF4, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE9, 0xF3, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0xF1, 0x00, 0x00, /* 0x24-0x27 */
+ 0x8A, 0x9B, 0x00, 0x00, 0xE9, 0xF0, 0x8E, 0xB0, /* 0x28-0x2B */
+ 0x89, 0xA7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8D, 0x83, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0xFA, 0xE9, 0xF9, /* 0x3C-0x3F */
+ 0x00, 0x00, 0xE9, 0xF8, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0xE9, 0xF5, 0x00, 0x00, 0xE9, 0xFB, 0x00, 0x00, /* 0x44-0x47 */
+ 0xE9, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xEA, 0x44, 0xEA, 0x43, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0xEA, 0x45, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x89, 0x4C, 0xEA, 0x40, 0xEA, 0x41, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x8D, 0x94, 0x96, 0xB7, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0xEA, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEE, 0xE9, /* 0x68-0x6B */
+ 0x96, 0x51, 0x00, 0x00, 0x00, 0x00, 0xEA, 0x4A, /* 0x6C-0x6F */
+ 0xEE, 0xE8, 0x00, 0x00, 0xEA, 0x46, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0xEA, 0x4B, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEA, 0x48, /* 0x84-0x87 */
+ 0x00, 0x00, 0xEA, 0x47, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8C, 0x7B, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0xEA, 0x4C, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0xEA, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0xEA, 0x4E, 0x00, 0x00, 0xEA, 0x49, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE9, 0xF2, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0xEA, 0x4F, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x92, 0xDF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0xEA, 0x53, 0x00, 0x00, 0xEA, 0x54, 0xEA, 0x52, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0xEA, 0x51, 0xEA, 0x57, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xEA, 0x50, 0x00, 0x00, 0xEA, 0x55, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEA, 0x56, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEA, 0x59, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0xEA, 0x58, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0xEA, 0x5B, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0xEA, 0x5C, 0x00, 0x00, 0xEA, 0x5D, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x98, 0x68, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0xEA, 0x5A, 0x91, 0xE9, 0x8D, 0xEB, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0xEA, 0x5E, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_9E[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0xEE, 0xEB, 0xEA, 0x5F, 0xEA, 0x60, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0xEA, 0x61, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0xEA, 0x62, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x8C, 0xB2, 0xEA, 0x63, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0xEA, 0x64, 0x00, 0x00, 0x8E, 0xAD, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0xEA, 0x65, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0xEA, 0x66, 0x00, 0x00, 0x00, 0x00, 0xEA, 0x67, /* 0x88-0x8B */
+ 0xEA, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xEA, 0x6B, 0xEA, 0x69, 0x98, 0x5B, /* 0x90-0x93 */
+ 0x00, 0x00, 0xEA, 0x6A, 0x00, 0x00, 0x97, 0xED, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0xEA, 0x6C, 0x00, 0x00, 0x97, 0xD9, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0xEA, 0x6D, 0x94, 0x9E, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0xEA, 0x6E, 0xEA, 0x70, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0xEA, 0x71, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0xEA, 0x6F, 0x8D, 0x8D, 0x96, 0xCB, 0x96, 0x83, /* 0xB8-0xBB */
+ 0x9B, 0xF5, 0x00, 0x00, 0x9F, 0x80, 0x96, 0x9B, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x89, 0xA9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0xEA, 0x73, 0x8B, 0x6F, 0xEA, 0x74, 0xEA, 0x75, /* 0xCC-0xCF */
+ 0xEA, 0x76, 0xEE, 0xEC, 0x8D, 0x95, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xEA, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xE0, 0xD2, 0x96, 0xD9, 0x00, 0x00, 0x91, 0xE1, /* 0xD8-0xDB */
+ 0xEA, 0x78, 0xEA, 0x7A, 0xEA, 0x79, 0x00, 0x00, /* 0xDC-0xDF */
+ 0xEA, 0x7B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0xEA, 0x7C, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xEA, 0x7D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEA, 0x7E, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xEA, 0x80, 0x00, 0x00, 0xEA, 0x81, 0xEA, 0x82, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xEA, 0x83, 0x00, 0x00, 0xEA, 0x84, /* 0xF8-0xFB */
+ 0xEA, 0x85, 0xEA, 0x86, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_9F[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEA, 0x87, /* 0x04-0x07 */
+ 0xEA, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x93, 0x43, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8C, 0xDB, /* 0x10-0x13 */
+ 0x00, 0x00, 0xEA, 0x8A, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x91, 0x6C, 0xEA, 0x8B, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0xEA, 0x8C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0x40, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0xEA, 0x8D, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0xEA, 0x8E, 0xE2, 0x56, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0xE6, 0xD8, 0xE8, 0xEB, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0xEA, 0x8F, 0x00, 0x00, /* 0x50-0x53 */
+ 0xEA, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEA, 0x92, /* 0x5C-0x5F */
+ 0xEA, 0x93, 0xEA, 0x94, 0x97, 0xEE, 0xEA, 0x91, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0xEA, 0x95, 0xEA, 0x96, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0xEA, 0x98, 0x00, 0x00, /* 0x68-0x6B */
+ 0xEA, 0x97, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0xEA, 0x9A, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0xEA, 0x9B, 0xEA, 0x99, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x97, 0xB4, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0xEA, 0x9C, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0xEA, 0x9D, 0xE2, 0x73, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0xEA, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+};
+
+static unsigned char u2c_DC[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+};
+
+static unsigned char u2c_F9[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0xED, 0xC4, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0xEE, 0xCD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+};
+
+static unsigned char u2c_FA[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0xED, 0x73, 0xED, 0x7E, /* 0x0C-0x0F */
+ 0xED, 0x80, 0xED, 0x95, 0xED, 0xBC, 0xED, 0xCC, /* 0x10-0x13 */
+ 0xED, 0xCE, 0xED, 0xF9, 0xEE, 0x42, 0xEE, 0x59, /* 0x14-0x17 */
+ 0xEE, 0x61, 0xEE, 0x62, 0xEE, 0x63, 0xEE, 0x65, /* 0x18-0x1B */
+ 0xEE, 0x69, 0xEE, 0x6C, 0xEE, 0x75, 0xEE, 0x81, /* 0x1C-0x1F */
+ 0xEE, 0x83, 0xEE, 0x84, 0xEE, 0x8D, 0xEE, 0x95, /* 0x20-0x23 */
+ 0xEE, 0x97, 0xEE, 0x98, 0xEE, 0x9B, 0xEE, 0xB7, /* 0x24-0x27 */
+ 0xEE, 0xBE, 0xEE, 0xCE, 0xEE, 0xDA, 0xEE, 0xDB, /* 0x28-0x2B */
+ 0xEE, 0xDD, 0xEE, 0xEA, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+};
+
+static unsigned char u2c_FF[512] = {
+ 0x00, 0x00, 0x81, 0x49, 0xEE, 0xFC, 0x81, 0x94, /* 0x00-0x03 */
+ 0x81, 0x90, 0x81, 0x93, 0x81, 0x95, 0xEE, 0xFB, /* 0x04-0x07 */
+ 0x81, 0x69, 0x81, 0x6A, 0x81, 0x96, 0x81, 0x7B, /* 0x08-0x0B */
+ 0x81, 0x43, 0x81, 0x7C, 0x81, 0x44, 0x81, 0x5E, /* 0x0C-0x0F */
+ 0x82, 0x4F, 0x82, 0x50, 0x82, 0x51, 0x82, 0x52, /* 0x10-0x13 */
+ 0x82, 0x53, 0x82, 0x54, 0x82, 0x55, 0x82, 0x56, /* 0x14-0x17 */
+ 0x82, 0x57, 0x82, 0x58, 0x81, 0x46, 0x81, 0x47, /* 0x18-0x1B */
+ 0x81, 0x83, 0x81, 0x81, 0x81, 0x84, 0x81, 0x48, /* 0x1C-0x1F */
+ 0x81, 0x97, 0x82, 0x60, 0x82, 0x61, 0x82, 0x62, /* 0x20-0x23 */
+ 0x82, 0x63, 0x82, 0x64, 0x82, 0x65, 0x82, 0x66, /* 0x24-0x27 */
+ 0x82, 0x67, 0x82, 0x68, 0x82, 0x69, 0x82, 0x6A, /* 0x28-0x2B */
+ 0x82, 0x6B, 0x82, 0x6C, 0x82, 0x6D, 0x82, 0x6E, /* 0x2C-0x2F */
+ 0x82, 0x6F, 0x82, 0x70, 0x82, 0x71, 0x82, 0x72, /* 0x30-0x33 */
+ 0x82, 0x73, 0x82, 0x74, 0x82, 0x75, 0x82, 0x76, /* 0x34-0x37 */
+ 0x82, 0x77, 0x82, 0x78, 0x82, 0x79, 0x81, 0x6D, /* 0x38-0x3B */
+ 0x81, 0x5F, 0x81, 0x6E, 0x81, 0x4F, 0x81, 0x51, /* 0x3C-0x3F */
+ 0x81, 0x4D, 0x82, 0x81, 0x82, 0x82, 0x82, 0x83, /* 0x40-0x43 */
+ 0x82, 0x84, 0x82, 0x85, 0x82, 0x86, 0x82, 0x87, /* 0x44-0x47 */
+ 0x82, 0x88, 0x82, 0x89, 0x82, 0x8A, 0x82, 0x8B, /* 0x48-0x4B */
+ 0x82, 0x8C, 0x82, 0x8D, 0x82, 0x8E, 0x82, 0x8F, /* 0x4C-0x4F */
+ 0x82, 0x90, 0x82, 0x91, 0x82, 0x92, 0x82, 0x93, /* 0x50-0x53 */
+ 0x82, 0x94, 0x82, 0x95, 0x82, 0x96, 0x82, 0x97, /* 0x54-0x57 */
+ 0x82, 0x98, 0x82, 0x99, 0x82, 0x9A, 0x81, 0x6F, /* 0x58-0x5B */
+ 0x81, 0x62, 0x81, 0x70, 0x81, 0x60, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0xA1, 0x00, 0xA2, 0x00, 0xA3, /* 0x60-0x63 */
+ 0x00, 0xA4, 0x00, 0xA5, 0x00, 0xA6, 0x00, 0xA7, /* 0x64-0x67 */
+ 0x00, 0xA8, 0x00, 0xA9, 0x00, 0xAA, 0x00, 0xAB, /* 0x68-0x6B */
+ 0x00, 0xAC, 0x00, 0xAD, 0x00, 0xAE, 0x00, 0xAF, /* 0x6C-0x6F */
+ 0x00, 0xB0, 0x00, 0xB1, 0x00, 0xB2, 0x00, 0xB3, /* 0x70-0x73 */
+ 0x00, 0xB4, 0x00, 0xB5, 0x00, 0xB6, 0x00, 0xB7, /* 0x74-0x77 */
+ 0x00, 0xB8, 0x00, 0xB9, 0x00, 0xBA, 0x00, 0xBB, /* 0x78-0x7B */
+ 0x00, 0xBC, 0x00, 0xBD, 0x00, 0xBE, 0x00, 0xBF, /* 0x7C-0x7F */
+
+ 0x00, 0xC0, 0x00, 0xC1, 0x00, 0xC2, 0x00, 0xC3, /* 0x80-0x83 */
+ 0x00, 0xC4, 0x00, 0xC5, 0x00, 0xC6, 0x00, 0xC7, /* 0x84-0x87 */
+ 0x00, 0xC8, 0x00, 0xC9, 0x00, 0xCA, 0x00, 0xCB, /* 0x88-0x8B */
+ 0x00, 0xCC, 0x00, 0xCD, 0x00, 0xCE, 0x00, 0xCF, /* 0x8C-0x8F */
+ 0x00, 0xD0, 0x00, 0xD1, 0x00, 0xD2, 0x00, 0xD3, /* 0x90-0x93 */
+ 0x00, 0xD4, 0x00, 0xD5, 0x00, 0xD6, 0x00, 0xD7, /* 0x94-0x97 */
+ 0x00, 0xD8, 0x00, 0xD9, 0x00, 0xDA, 0x00, 0xDB, /* 0x98-0x9B */
+ 0x00, 0xDC, 0x00, 0xDD, 0x00, 0xDE, 0x00, 0xDF, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x81, 0x91, 0x81, 0x92, 0xEE, 0xF9, 0x81, 0x50, /* 0xE0-0xE3 */
+ 0xEE, 0xFA, 0x81, 0x8F, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+};
+
+static unsigned char *page_uni2charset[256] = {
+ NULL, NULL, NULL, u2c_03, u2c_04, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ u2c_20, u2c_21, u2c_22, u2c_23, u2c_24, u2c_25, u2c_26, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ u2c_30, NULL, u2c_32, u2c_33, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, u2c_4E, u2c_4F,
+ u2c_50, u2c_51, u2c_52, u2c_53, u2c_54, u2c_55, u2c_56, u2c_57,
+ u2c_58, u2c_59, u2c_5A, u2c_5B, u2c_5C, u2c_5D, u2c_5E, u2c_5F,
+ u2c_60, u2c_61, u2c_62, u2c_63, u2c_64, u2c_65, u2c_66, u2c_67,
+ u2c_68, u2c_69, u2c_6A, u2c_6B, u2c_6C, u2c_6D, u2c_6E, u2c_6F,
+ u2c_70, u2c_71, u2c_72, u2c_73, u2c_74, u2c_75, u2c_76, u2c_77,
+ u2c_78, u2c_79, u2c_7A, u2c_7B, u2c_7C, u2c_7D, u2c_7E, u2c_7F,
+ u2c_80, u2c_81, u2c_82, u2c_83, u2c_84, u2c_85, u2c_86, u2c_87,
+ u2c_88, u2c_89, u2c_8A, u2c_8B, u2c_8C, u2c_8D, u2c_8E, u2c_8F,
+ u2c_90, u2c_91, u2c_92, u2c_93, u2c_94, u2c_95, u2c_96, u2c_97,
+ u2c_98, u2c_99, u2c_9A, u2c_9B, u2c_9C, u2c_9D, u2c_9E, u2c_9F,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, u2c_DC, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, u2c_F9, u2c_FA, NULL, NULL, NULL, NULL, u2c_FF, };
+
+static unsigned char charset2lower[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40-0x47 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x48-0x4f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50-0x57 */
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0x88-0x8f */
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static unsigned char charset2upper[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0x88-0x8f */
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static int uni2char(const wchar_t uni,
+ unsigned char *out, int boundlen)
+{
+ unsigned char *uni2charset;
+ unsigned char cl = uni&0xFF;
+ unsigned char ch = (uni>>8)&0xFF;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ if (ch == 0xFF && 0x61 <= cl && cl <= 0x9F) {
+ out[0] = cl + 0x40;
+ return 1;
+ }
+ uni2charset = page_uni2charset[ch];
+ if (uni2charset) {
+ if (boundlen < 2)
+ return -ENAMETOOLONG;
+
+ out[0] = uni2charset[cl*2];
+ out[1] = uni2charset[cl*2+1];
+ if (out[0] == 0x00 && out[1] == 0x00)
+ return -EINVAL;
+ return 2;
+ } else if ((ch == 0) && (cl <= 0x7F)) {
+ out[0] = cl;
+ return 1;
+ }
+ else
+ return -EINVAL;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen,
+ wchar_t *uni)
+{
+ unsigned char ch, cl;
+ wchar_t *charset2uni;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ if (rawstring[0] <= 0x7F) {
+ *uni = rawstring[0];
+ return 1;
+ }
+ if (0xA1 <= rawstring[0] && rawstring[0] <= 0xDF) {
+ *uni = 0xFF00 | (rawstring[0] - 0x40);
+ return 1;
+ }
+
+ if (boundlen < 2)
+ return -ENAMETOOLONG;
+ ch = rawstring[0];
+ cl = rawstring[1];
+ charset2uni = page_charset2uni[ch];
+ if (charset2uni && cl) {
+ *uni = charset2uni[cl];
+ if (*uni == 0x0000)
+ return -EINVAL;
+ return 2;
+ }
+ else
+ return -EINVAL;
+}
+
+static struct nls_table table = {
+ "cp932",
+ uni2char,
+ char2uni,
+ charset2lower,
+ charset2upper,
+ THIS_MODULE,
+};
+
+static int __init init_nls_cp932(void)
+{
+ return register_nls(&table);
+}
+
+static void __exit exit_nls_cp932(void)
+{
+ unregister_nls(&table);
+}
+
+module_init(init_nls_cp932)
+module_exit(exit_nls_cp932)
+MODULE_LICENSE("Dual BSD/GPL");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ *
+---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/fs/nls/nls_cp936.c b/fs/nls/nls_cp936.c
new file mode 100644
index 00000000000000..906a8baf386638
--- /dev/null
+++ b/fs/nls/nls_cp936.c
@@ -0,0 +1,11045 @@
+/*
+ * linux/fs/nls_cp936.c
+ *
+ * Charset cp936 translation tables.
+ * This translation table was generated automatically, the
+ * original table can be download from the Microsoft website.
+ * (http://www.microsoft.com/typography/unicode/unicodecp.htm)
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static wchar_t c2u_81[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x4E02,0x4E04,0x4E05,0x4E06,0x4E0F,0x4E12,0x4E17,0x4E1F,/* 0x40-0x47 */
+ 0x4E20,0x4E21,0x4E23,0x4E26,0x4E29,0x4E2E,0x4E2F,0x4E31,/* 0x48-0x4F */
+ 0x4E33,0x4E35,0x4E37,0x4E3C,0x4E40,0x4E41,0x4E42,0x4E44,/* 0x50-0x57 */
+ 0x4E46,0x4E4A,0x4E51,0x4E55,0x4E57,0x4E5A,0x4E5B,0x4E62,/* 0x58-0x5F */
+ 0x4E63,0x4E64,0x4E65,0x4E67,0x4E68,0x4E6A,0x4E6B,0x4E6C,/* 0x60-0x67 */
+ 0x4E6D,0x4E6E,0x4E6F,0x4E72,0x4E74,0x4E75,0x4E76,0x4E77,/* 0x68-0x6F */
+ 0x4E78,0x4E79,0x4E7A,0x4E7B,0x4E7C,0x4E7D,0x4E7F,0x4E80,/* 0x70-0x77 */
+ 0x4E81,0xF91B,0x4E83,0x4E84,0x4E85,0x4E87,0x4E8A,0x0000,/* 0x78-0x7F */
+
+ 0x4E90,0x4E96,0x4E97,0x4E99,0x4E9C,0x4E9D,0x4E9E,0x4EA3,/* 0x80-0x87 */
+ 0x4EAA,0x4EAF,0x4EB0,0x4EB1,0x4EB4,0x4EB6,0x4EB7,0x4EB8,/* 0x88-0x8F */
+ 0x4EB9,0x4EBC,0x4EBD,0x4EBE,0x4EC8,0x4ECC,0x4ECF,0x4ED0,/* 0x90-0x97 */
+ 0x4ED2,0x4EDA,0x4EDB,0x4EDC,0x4EE0,0x4EE2,0x4EE6,0x4EE7,/* 0x98-0x9F */
+ 0x4EE9,0x4EED,0x4EEE,0x4EEF,0x4EF1,0x4EF4,0x4EF8,0x4EF9,/* 0xA0-0xA7 */
+ 0x4EFA,0x4EFC,0x4EFE,0x4F00,0x4F02,0x4F03,0x4F04,0x4F05,/* 0xA8-0xAF */
+ 0x4F06,0x4F07,0x4F08,0x4F0B,0x4F0C,0x4F12,0x4F13,0x4F14,/* 0xB0-0xB7 */
+ 0x4F15,0x4F16,0x4F1C,0x4F1D,0x4F21,0x4F23,0x4F28,0x4F29,/* 0xB8-0xBF */
+ 0x4F2C,0x4F2D,0x4F2E,0x4F31,0x4F33,0x4F35,0x4F37,0x4F39,/* 0xC0-0xC7 */
+ 0x4F3B,0x4F3E,0x4F3F,0x4F40,0x4F41,0x4F42,0x4F44,0x4F45,/* 0xC8-0xCF */
+ 0x4F47,0x4F48,0x4F49,0x4F4A,0x4F4B,0x4F4C,0x4F52,0x4F54,/* 0xD0-0xD7 */
+ 0x4F56,0x4F61,0x4F62,0x4F66,0x4F68,0x4F6A,0x4F6B,0x4F6D,/* 0xD8-0xDF */
+ 0x4F6E,0x4F71,0x4F72,0x4F75,0x4F77,0x4F78,0x4F79,0x4F7A,/* 0xE0-0xE7 */
+ 0x4F7D,0x4F80,0x4F81,0x4F82,0x4F85,0xF92D,0x4F87,0x4F8A,/* 0xE8-0xEF */
+ 0x4F8C,0x4F8E,0x4F90,0x4F92,0x4F93,0x4F95,0x4F96,0x4F98,/* 0xF0-0xF7 */
+ 0x4F99,0x4F9A,0x4F9C,0x4F9E,0x4F9F,0x4FA1,0x4FA2,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_82[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x4FA4,0x4FAB,0x4FAD,0x4FB0,0x4FB1,0x4FB2,0x4FB3,0x4FB4,/* 0x40-0x47 */
+ 0x4FB6,0x4FB7,0x4FB8,0x4FB9,0x4FBA,0x4FBB,0x4FBC,0x4FBD,/* 0x48-0x4F */
+ 0x4FBE,0x4FC0,0x4FC1,0x4FC2,0x4FC6,0x4FC7,0x4FC8,0x4FC9,/* 0x50-0x57 */
+ 0x4FCB,0x4FCC,0x4FCD,0x4FD2,0x4FD3,0x4FD4,0x4FD5,0x4FD6,/* 0x58-0x5F */
+ 0x4FD9,0x4FDB,0x4FE0,0x4FE2,0x4FE4,0x4FE5,0x4FE7,0x4FEB,/* 0x60-0x67 */
+ 0x4FEC,0x4FF0,0x4FF2,0x4FF4,0x4FF5,0x4FF6,0x4FF7,0x4FF9,/* 0x68-0x6F */
+ 0x4FFB,0x4FFC,0x4FFD,0x4FFF,0x5000,0x5001,0x5002,0x5003,/* 0x70-0x77 */
+ 0x5004,0x5005,0x5006,0x5007,0x5008,0x5009,0x500A,0x0000,/* 0x78-0x7F */
+
+ 0x500B,0x500E,0x5010,0x5011,0x5013,0x5015,0x5016,0x5017,/* 0x80-0x87 */
+ 0x501B,0x501D,0x501E,0x5020,0x5022,0x5023,0x5024,0x5027,/* 0x88-0x8F */
+ 0xF9D4,0x502F,0x5030,0x5031,0x5032,0x5033,0x5034,0x5035,/* 0x90-0x97 */
+ 0x5036,0x5037,0x5038,0x5039,0x503B,0x503D,0x503F,0x5040,/* 0x98-0x9F */
+ 0x5041,0x5042,0x5044,0x5045,0x5046,0x5049,0x504A,0x504B,/* 0xA0-0xA7 */
+ 0x504D,0x5050,0x5051,0x5052,0x5053,0x5054,0x5056,0x5057,/* 0xA8-0xAF */
+ 0x5058,0x5059,0x505B,0x505D,0x505E,0x505F,0x5060,0x5061,/* 0xB0-0xB7 */
+ 0x5062,0x5063,0x5064,0x5066,0x5067,0x5068,0x5069,0x506A,/* 0xB8-0xBF */
+ 0x506B,0x506D,0x506E,0x506F,0x5070,0x5071,0x5072,0x5073,/* 0xC0-0xC7 */
+ 0x5074,0x5075,0x5078,0x5079,0x507A,0x507C,0x507D,0x5081,/* 0xC8-0xCF */
+ 0x5082,0x5083,0x5084,0x5086,0x5087,0x5089,0x508A,0x508B,/* 0xD0-0xD7 */
+ 0x508C,0x508E,0x508F,0x5090,0x5091,0x5092,0x5093,0x5094,/* 0xD8-0xDF */
+ 0x5095,0x5096,0x5097,0x5098,0x5099,0x509A,0x509B,0x509C,/* 0xE0-0xE7 */
+ 0x509D,0x509E,0x509F,0x50A0,0x50A1,0x50A2,0x50A4,0x50A6,/* 0xE8-0xEF */
+ 0x50AA,0x50AB,0x50AD,0x50AE,0x50AF,0x50B0,0x50B1,0x50B3,/* 0xF0-0xF7 */
+ 0x50B4,0x50B5,0x50B6,0x50B7,0x50B8,0x50B9,0x50BC,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_83[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x50BD,0x50BE,0x50BF,0x50C0,0x50C1,0x50C2,0x50C3,0x50C4,/* 0x40-0x47 */
+ 0x50C5,0x50C6,0x50C7,0x50C8,0x50C9,0x50CA,0x50CB,0x50CC,/* 0x48-0x4F */
+ 0x50CD,0x50CE,0x50D0,0x50D1,0x50D2,0x50D3,0x50D4,0x50D5,/* 0x50-0x57 */
+ 0x50D7,0x50D8,0x50D9,0x50DB,0x50DC,0x50DD,0x50DE,0x50DF,/* 0x58-0x5F */
+ 0x50E0,0x50E1,0x50E2,0x50E3,0x50E4,0x50E5,0x50E8,0x50E9,/* 0x60-0x67 */
+ 0x50EA,0x50EB,0x50EF,0x50F0,0x50F1,0x50F2,0x50F4,0x50F6,/* 0x68-0x6F */
+ 0x50F7,0x50F8,0x50F9,0x50FA,0x50FC,0x50FD,0x50FE,0x50FF,/* 0x70-0x77 */
+ 0x5100,0x5101,0x5102,0x5103,0x5104,0x5105,0x5108,0x0000,/* 0x78-0x7F */
+
+ 0x5109,0x510A,0x510C,0x510D,0x510E,0x510F,0x5110,0x5111,/* 0x80-0x87 */
+ 0x5113,0x5114,0x5115,0x5116,0x5117,0x5118,0x5119,0x511A,/* 0x88-0x8F */
+ 0x511B,0x511C,0x511D,0x511E,0x511F,0x5120,0x5122,0x5123,/* 0x90-0x97 */
+ 0x5124,0x5125,0x5126,0x5127,0x5128,0x5129,0x512A,0x512B,/* 0x98-0x9F */
+ 0x512C,0x512D,0x512E,0x512F,0x5130,0x5131,0x5132,0x5133,/* 0xA0-0xA7 */
+ 0x5134,0x5135,0x5136,0x5137,0x5138,0x5139,0x513A,0x513B,/* 0xA8-0xAF */
+ 0x513C,0x513D,0x513E,0x5142,0x5147,0x514A,0x514C,0x514E,/* 0xB0-0xB7 */
+ 0x514F,0x5150,0x5152,0x5153,0x5157,0x5158,0x5159,0x515B,/* 0xB8-0xBF */
+ 0x515D,0x515E,0x515F,0x5160,0x5161,0x5163,0x5164,0x5166,/* 0xC0-0xC7 */
+ 0x5167,0xF978,0x516A,0x516F,0x5172,0x517A,0x517E,0x517F,/* 0xC8-0xCF */
+ 0x5183,0x5184,0x5186,0x5187,0x518A,0x518B,0x518E,0x518F,/* 0xD0-0xD7 */
+ 0x5190,0x5191,0x5193,0x5194,0x5198,0x519A,0x519D,0x519E,/* 0xD8-0xDF */
+ 0x519F,0x51A1,0x51A3,0x51A6,0x51A7,0x51A8,0x51A9,0x51AA,/* 0xE0-0xE7 */
+ 0x51AD,0x51AE,0x51B4,0x51B8,0x51B9,0x51BA,0x51BE,0x51BF,/* 0xE8-0xEF */
+ 0x51C1,0x51C2,0x51C3,0x51C5,0x51C8,0x51CA,0x51CD,0x51CE,/* 0xF0-0xF7 */
+ 0x51D0,0x51D2,0x51D3,0x51D4,0x51D5,0x51D6,0x51D7,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_84[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x51D8,0x51D9,0x51DA,0xF954,0xFA15,0x51DF,0x51E2,0x51E3,/* 0x40-0x47 */
+ 0x51E5,0x51E6,0x51E7,0x51E8,0x51E9,0x51EA,0x51EC,0x51EE,/* 0x48-0x4F */
+ 0x51F1,0x51F2,0x51F4,0x51F7,0x51FE,0x5204,0x5205,0x5209,/* 0x50-0x57 */
+ 0x520B,0x520C,0x520F,0x5210,0x5213,0x5214,0x5215,0x521C,/* 0x58-0x5F */
+ 0x521E,0x521F,0x5221,0x5222,0x5223,0x5225,0x5226,0x5227,/* 0x60-0x67 */
+ 0x522A,0x522C,0x522F,0x5231,0x5232,0x5234,0x5235,0x523C,/* 0x68-0x6F */
+ 0x523E,0x5244,0x5245,0x5246,0x5247,0x5248,0x5249,0x524B,/* 0x70-0x77 */
+ 0x524E,0x524F,0x5252,0x5253,0x5255,0x5257,0x5258,0x0000,/* 0x78-0x7F */
+
+ 0x5259,0x525A,0x525B,0x525D,0x525F,0x5260,0x5262,0x5263,/* 0x80-0x87 */
+ 0x5264,0x5266,0x5268,0x526B,0x526C,0x526D,0x526E,0x5270,/* 0x88-0x8F */
+ 0x5271,0x5273,0x5274,0x5275,0x5276,0x5277,0x5278,0x5279,/* 0x90-0x97 */
+ 0x527A,0x527B,0x527C,0x527E,0x5280,0x5283,0x5284,0x5285,/* 0x98-0x9F */
+ 0x5286,0x5287,0xF9C7,0x528A,0x528B,0x528C,0x528D,0x528E,/* 0xA0-0xA7 */
+ 0x528F,0x5291,0x5292,0x5294,0x5295,0x5296,0x5297,0x5298,/* 0xA8-0xAF */
+ 0x5299,0x529A,0x529C,0x52A4,0x52A5,0x52A6,0x52A7,0x52AE,/* 0xB0-0xB7 */
+ 0x52AF,0x52B0,0x52B4,0x52B5,0x52B6,0x52B7,0x52B8,0x52B9,/* 0xB8-0xBF */
+ 0x52BA,0x52BB,0x52BC,0x52BD,0x52C0,0x52C1,0x52C2,0x52C4,/* 0xC0-0xC7 */
+ 0x52C5,0x52C6,0x52C8,0x52CA,0x52CC,0x52CD,0x52CE,0x52CF,/* 0xC8-0xCF */
+ 0x52D1,0x52D3,0x52D4,0x52D5,0x52D7,0x52D9,0x52DA,0x52DB,/* 0xD0-0xD7 */
+ 0x52DC,0x52DD,0xF92F,0x52E0,0x52E1,0x52E2,0x52E3,0x52E5,/* 0xD8-0xDF */
+ 0x52E6,0x52E7,0x52E8,0x52E9,0x52EA,0x52EB,0x52EC,0x52ED,/* 0xE0-0xE7 */
+ 0x52EE,0x52EF,0x52F1,0x52F2,0x52F3,0x52F4,0xF97F,0x52F6,/* 0xE8-0xEF */
+ 0x52F7,0x52F8,0x52FB,0x52FC,0x52FD,0x5301,0x5302,0x5303,/* 0xF0-0xF7 */
+ 0x5304,0x5307,0x5309,0x530A,0x530B,0x530C,0x530E,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_85[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x5311,0x5312,0x5313,0x5314,0x5318,0x531B,0x531C,0x531E,/* 0x40-0x47 */
+ 0x531F,0x5322,0x5324,0x5325,0x5327,0x5328,0x5329,0x532B,/* 0x48-0x4F */
+ 0x532C,0x532D,0x532F,0x5330,0x5331,0x5332,0x5333,0x5334,/* 0x50-0x57 */
+ 0x5335,0x5336,0x5337,0x5338,0x533C,0x533D,0x5340,0x5342,/* 0x58-0x5F */
+ 0x5344,0x5346,0x534B,0x534C,0x534D,0x5350,0x5354,0x5358,/* 0x60-0x67 */
+ 0x5359,0x535B,0x535D,0x5365,0x5368,0x536A,0x536C,0x536D,/* 0x68-0x6F */
+ 0x5372,0x5376,0x5379,0x537B,0x537C,0x537D,0x537E,0x5380,/* 0x70-0x77 */
+ 0x5381,0x5383,0x5387,0x5388,0x538A,0x538E,0x538F,0x0000,/* 0x78-0x7F */
+
+ 0x5390,0x5391,0x5392,0x5393,0x5394,0x5396,0x5397,0x5399,/* 0x80-0x87 */
+ 0x539B,0x539C,0x539E,0x53A0,0x53A1,0x53A4,0x53A7,0x53AA,/* 0x88-0x8F */
+ 0x53AB,0x53AC,0x53AD,0x53AF,0x53B0,0x53B1,0x53B2,0x53B3,/* 0x90-0x97 */
+ 0x53B4,0x53B5,0x53B7,0x53B8,0x53B9,0x53BA,0x53BC,0x53BD,/* 0x98-0x9F */
+ 0x53BE,0x53C0,0xF96B,0x53C4,0x53C5,0x53C6,0x53C7,0x53CE,/* 0xA0-0xA7 */
+ 0x53CF,0x53D0,0x53D2,0x53D3,0x53D5,0x53DA,0x53DC,0x53DD,/* 0xA8-0xAF */
+ 0x53DE,0x53E1,0x53E2,0x53E7,0x53F4,0x53FA,0x53FE,0x53FF,/* 0xB0-0xB7 */
+ 0x5400,0x5402,0x5405,0x5407,0x540B,0x5414,0x5418,0x5419,/* 0xB8-0xBF */
+ 0x541A,0x541C,0x5422,0x5424,0x5425,0x542A,0x5430,0x5433,/* 0xC0-0xC7 */
+ 0x5436,0x5437,0x543A,0x543D,0x543F,0x5441,0xF980,0x5444,/* 0xC8-0xCF */
+ 0x5445,0x5447,0x5449,0x544C,0x544D,0x544E,0x544F,0x5451,/* 0xD0-0xD7 */
+ 0x545A,0x545D,0x545E,0x545F,0x5460,0x5461,0x5463,0x5465,/* 0xD8-0xDF */
+ 0x5467,0x5469,0x546A,0x546B,0x546C,0x546D,0x546E,0x546F,/* 0xE0-0xE7 */
+ 0x5470,0x5474,0x5479,0x547A,0x547E,0x547F,0x5481,0x5483,/* 0xE8-0xEF */
+ 0x5485,0x5487,0x5488,0x5489,0x548A,0x548D,0x5491,0x5493,/* 0xF0-0xF7 */
+ 0x5497,0x5498,0x549C,0x549E,0x549F,0x54A0,0x54A1,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_86[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x54A2,0x54A5,0x54AE,0x54B0,0x54B2,0x54B5,0x54B6,0x54B7,/* 0x40-0x47 */
+ 0x54B9,0x54BA,0x54BC,0x54BE,0x54C3,0x54C5,0x54CA,0x54CB,/* 0x48-0x4F */
+ 0x54D6,0x54D8,0x54DB,0x54E0,0x54E1,0x54E2,0x54E3,0x54E4,/* 0x50-0x57 */
+ 0x54EB,0x54EC,0x54EF,0x54F0,0x54F1,0x54F4,0x54F5,0x54F6,/* 0x58-0x5F */
+ 0x54F7,0x54F8,0x54F9,0x54FB,0x54FE,0x5500,0x5502,0x5503,/* 0x60-0x67 */
+ 0x5504,0x5505,0x5508,0x550A,0x550B,0x550C,0x550D,0x550E,/* 0x68-0x6F */
+ 0x5512,0x5513,0x5515,0x5516,0x5517,0x5518,0x5519,0x551A,/* 0x70-0x77 */
+ 0x551C,0x551D,0x551E,0x551F,0x5521,0x5525,0x5526,0x0000,/* 0x78-0x7F */
+
+ 0x5528,0x5529,0x552B,0x552D,0x5532,0x5534,0x5535,0x5536,/* 0x80-0x87 */
+ 0x5538,0x5539,0x553A,0x553B,0x553D,0x5540,0x5542,0x5545,/* 0x88-0x8F */
+ 0x5547,0x5548,0x554B,0x554C,0x554D,0x554E,0x554F,0x5551,/* 0x90-0x97 */
+ 0x5552,0x5553,0x5554,0x5557,0x5558,0x5559,0x555A,0x555B,/* 0x98-0x9F */
+ 0x555D,0x555E,0x555F,0x5560,0x5562,0x5563,0x5568,0x5569,/* 0xA0-0xA7 */
+ 0x556B,0x556F,0x5570,0x5571,0x5572,0x5573,0x5574,0x5579,/* 0xA8-0xAF */
+ 0x557A,0x557D,0x557F,0x5585,0x5586,0x558C,0x558D,0x558E,/* 0xB0-0xB7 */
+ 0x5590,0x5592,0x5593,0x5595,0x5596,0x5597,0x559A,0x559B,/* 0xB8-0xBF */
+ 0x559E,0x55A0,0x55A1,0x55A2,0x55A3,0x55A4,0x55A5,0x55A6,/* 0xC0-0xC7 */
+ 0x55A8,0x55A9,0x55AA,0x55AB,0x55AC,0x55AD,0x55AE,0x55AF,/* 0xC8-0xCF */
+ 0x55B0,0x55B2,0x55B4,0x55B6,0x55B8,0x55BA,0x55BC,0x55BF,/* 0xD0-0xD7 */
+ 0x55C0,0x55C1,0x55C2,0x55C3,0x55C6,0x55C7,0x55C8,0x55CA,/* 0xD8-0xDF */
+ 0x55CB,0x55CE,0x55CF,0x55D0,0x55D5,0x55D7,0x55D8,0x55D9,/* 0xE0-0xE7 */
+ 0x55DA,0x55DB,0x55DE,0x55E0,0x55E2,0x55E7,0x55E9,0x55ED,/* 0xE8-0xEF */
+ 0x55EE,0x55F0,0x55F1,0x55F4,0x55F6,0x55F8,0x55F9,0x55FA,/* 0xF0-0xF7 */
+ 0x55FB,0x55FC,0x55FF,0x5602,0x5603,0x5604,0x5605,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_87[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x5606,0x5607,0x560A,0x560B,0x560D,0x5610,0x5611,0x5612,/* 0x40-0x47 */
+ 0x5613,0x5614,0x5615,0x5616,0x5617,0x5619,0x561A,0x561C,/* 0x48-0x4F */
+ 0x561D,0x5620,0x5621,0x5622,0x5625,0x5626,0x5628,0x5629,/* 0x50-0x57 */
+ 0x562A,0x562B,0x562E,0x562F,0x5630,0x5633,0x5635,0x5637,/* 0x58-0x5F */
+ 0x5638,0x563A,0x563C,0x563D,0x563E,0x5640,0x5641,0x5642,/* 0x60-0x67 */
+ 0x5643,0x5644,0x5645,0x5646,0x5647,0x5648,0x5649,0x564A,/* 0x68-0x6F */
+ 0x564B,0x564F,0x5650,0x5651,0x5652,0x5653,0x5655,0x5656,/* 0x70-0x77 */
+ 0x565A,0x565B,0x565D,0x565E,0x565F,0x5660,0x5661,0x0000,/* 0x78-0x7F */
+
+ 0x5663,0x5665,0x5666,0x5667,0x566D,0x566E,0x566F,0x5670,/* 0x80-0x87 */
+ 0x5672,0x5673,0x5674,0x5675,0x5677,0x5678,0x5679,0x567A,/* 0x88-0x8F */
+ 0x567D,0x567E,0x567F,0x5680,0x5681,0x5682,0x5683,0x5684,/* 0x90-0x97 */
+ 0x5687,0x5688,0x5689,0x568A,0x568B,0x568C,0x568D,0x5690,/* 0x98-0x9F */
+ 0x5691,0x5692,0x5694,0x5695,0x5696,0x5697,0x5698,0x5699,/* 0xA0-0xA7 */
+ 0x569A,0x569B,0x569C,0x569D,0x569E,0x569F,0x56A0,0x56A1,/* 0xA8-0xAF */
+ 0x56A2,0x56A4,0x56A5,0x56A6,0x56A7,0x56A8,0x56A9,0x56AA,/* 0xB0-0xB7 */
+ 0x56AB,0x56AC,0x56AD,0x56AE,0x56B0,0x56B1,0x56B2,0x56B3,/* 0xB8-0xBF */
+ 0x56B4,0x56B5,0x56B6,0x56B8,0x56B9,0x56BA,0x56BB,0x56BD,/* 0xC0-0xC7 */
+ 0x56BE,0x56BF,0x56C0,0x56C1,0x56C2,0x56C3,0x56C4,0x56C5,/* 0xC8-0xCF */
+ 0x56C6,0x56C7,0x56C8,0x56C9,0x56CB,0x56CC,0x56CD,0x56CE,/* 0xD0-0xD7 */
+ 0x56CF,0x56D0,0x56D1,0x56D2,0x56D3,0x56D5,0x56D6,0x56D8,/* 0xD8-0xDF */
+ 0x56D9,0x56DC,0x56E3,0x56E5,0x56E6,0x56E7,0x56E8,0x56E9,/* 0xE0-0xE7 */
+ 0x56EA,0x56EC,0x56EE,0x56EF,0x56F2,0x56F3,0x56F6,0x56F7,/* 0xE8-0xEF */
+ 0x56F8,0x56FB,0x56FC,0x5700,0x5701,0x5702,0x5705,0x5707,/* 0xF0-0xF7 */
+ 0x570B,0x570C,0x570D,0x570E,0x570F,0x5710,0x5711,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_88[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x5712,0x5713,0x5714,0x5715,0x5716,0x5717,0x5718,0x5719,/* 0x40-0x47 */
+ 0x571A,0x571B,0x571D,0x571E,0x5720,0x5721,0x5722,0x5724,/* 0x48-0x4F */
+ 0x5725,0x5726,0x5727,0x572B,0x5731,0x5732,0x5734,0x5735,/* 0x50-0x57 */
+ 0x5736,0x5737,0x5738,0x573C,0x573D,0x573F,0x5741,0x5743,/* 0x58-0x5F */
+ 0x5744,0x5745,0x5746,0x5748,0x5749,0x574B,0x5752,0x5753,/* 0x60-0x67 */
+ 0x5754,0x5755,0x5756,0x5758,0x5759,0x5762,0x5763,0x5765,/* 0x68-0x6F */
+ 0x5767,0x576C,0x576E,0x5770,0x5771,0x5772,0x5774,0x5775,/* 0x70-0x77 */
+ 0x5778,0x5779,0x577A,0x577D,0x577E,0x577F,0x5780,0x0000,/* 0x78-0x7F */
+
+ 0x5781,0x5787,0x5788,0x5789,0x578A,0x578D,0x578E,0x578F,/* 0x80-0x87 */
+ 0x5790,0x5791,0x5794,0x5795,0x5796,0x5797,0x5798,0x5799,/* 0x88-0x8F */
+ 0x579A,0x579C,0x579D,0x579E,0x579F,0x57A5,0x57A8,0x57AA,/* 0x90-0x97 */
+ 0x57AC,0x57AF,0x57B0,0x57B1,0x57B3,0x57B5,0x57B6,0x57B7,/* 0x98-0x9F */
+ 0x57B9,0x57BA,0x57BB,0x57BC,0x57BD,0x57BE,0x57BF,0x57C0,/* 0xA0-0xA7 */
+ 0x57C1,0x57C4,0x57C5,0x57C6,0x57C7,0x57C8,0x57C9,0x57CA,/* 0xA8-0xAF */
+ 0x57CC,0x57CD,0x57D0,0x57D1,0x57D3,0x57D6,0x57D7,0x57DB,/* 0xB0-0xB7 */
+ 0x57DC,0x57DE,0x57E1,0x57E2,0x57E3,0x57E5,0x57E6,0x57E7,/* 0xB8-0xBF */
+ 0x57E8,0x57E9,0x57EA,0x57EB,0x57EC,0x57EE,0x57F0,0x57F1,/* 0xC0-0xC7 */
+ 0x57F2,0x57F3,0x57F5,0x57F6,0x57F7,0x57FB,0x57FC,0x57FE,/* 0xC8-0xCF */
+ 0x57FF,0x5801,0x5803,0x5804,0x5805,0x5808,0x5809,0x580A,/* 0xD0-0xD7 */
+ 0x580C,0x580E,0x580F,0x5810,0x5812,0x5813,0x5814,0x5816,/* 0xD8-0xDF */
+ 0x5817,0x5818,0x581A,0x581B,0x581C,0x581D,0x581F,0x5822,/* 0xE0-0xE7 */
+ 0x5823,0x5825,0x5826,0x5827,0x5828,0x5829,0x582B,0x582C,/* 0xE8-0xEF */
+ 0x582D,0x582E,0x582F,0x5831,0x5832,0x5833,0x5834,0x5836,/* 0xF0-0xF7 */
+ 0x5837,0x5838,0x5839,0x583A,0x583B,0x583C,0x583D,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_89[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x583E,0x583F,0x5840,0x5841,0x5842,0x5843,0x5845,0x5846,/* 0x40-0x47 */
+ 0x5847,0x5848,0x5849,0x584A,0x584B,0x584E,0x584F,0x5850,/* 0x48-0x4F */
+ 0x5852,0x5853,0x5855,0x5856,0x5857,0x5859,0xFA10,0x585B,/* 0x50-0x57 */
+ 0x585C,0x585D,0x585F,0x5860,0x5861,0x5862,0x5863,0x5864,/* 0x58-0x5F */
+ 0x5866,0x5867,0x5868,0x5869,0x586A,0x586D,0x586E,0x586F,/* 0x60-0x67 */
+ 0x5870,0x5871,0x5872,0x5873,0x5874,0x5875,0x5876,0x5877,/* 0x68-0x6F */
+ 0x5878,0x5879,0x587A,0x587B,0x587C,0x587D,0x587F,0x5882,/* 0x70-0x77 */
+ 0x5884,0x5886,0x5887,0x5888,0x588A,0x588B,0x588C,0x0000,/* 0x78-0x7F */
+
+ 0x588D,0x588E,0x588F,0x5890,0x5891,0x5894,0x5895,0x5896,/* 0x80-0x87 */
+ 0x5897,0x5898,0x589B,0x589C,0x589D,0x58A0,0x58A1,0x58A2,/* 0x88-0x8F */
+ 0x58A3,0x58A4,0x58A5,0x58A6,0x58A7,0x58AA,0x58AB,0x58AC,/* 0x90-0x97 */
+ 0x58AD,0x58AE,0x58AF,0x58B0,0x58B1,0x58B2,0x58B3,0x58B4,/* 0x98-0x9F */
+ 0x58B5,0x58B6,0x58B7,0x58B8,0x58B9,0x58BA,0x58BB,0x58BD,/* 0xA0-0xA7 */
+ 0x58BE,0x58BF,0x58C0,0x58C2,0x58C3,0x58C4,0x58C6,0x58C7,/* 0xA8-0xAF */
+ 0x58C8,0x58C9,0x58CA,0x58CB,0x58CC,0x58CD,0x58CE,0x58CF,/* 0xB0-0xB7 */
+ 0x58D0,0x58D2,0x58D3,0x58D4,0x58D6,0x58D7,0xF94A,0x58D9,/* 0xB8-0xBF */
+ 0x58DA,0x58DB,0x58DC,0x58DD,0x58DE,0xF942,0x58E0,0x58E1,/* 0xC0-0xC7 */
+ 0x58E2,0x58E3,0x58E5,0x58E6,0x58E7,0x58E8,0x58E9,0x58EA,/* 0xC8-0xCF */
+ 0x58ED,0x58EF,0x58F1,0x58F2,0x58F4,0x58F5,0x58F7,0x58F8,/* 0xD0-0xD7 */
+ 0x58FA,0x58FB,0x58FC,0x58FD,0x58FE,0x58FF,0x5900,0x5901,/* 0xD8-0xDF */
+ 0x5903,0x5905,0x5906,0x5908,0x5909,0x590A,0x590B,0x590C,/* 0xE0-0xE7 */
+ 0x590E,0x5910,0x5911,0x5912,0x5913,0x5917,0x5918,0x591B,/* 0xE8-0xEF */
+ 0x591D,0x591E,0x5920,0x5921,0x5922,0x5923,0x5926,0x5928,/* 0xF0-0xF7 */
+ 0x592C,0x5930,0x5932,0x5933,0x5935,0x5936,0x593B,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_8A[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x593D,0x593E,0x593F,0x5940,0x5943,0x5945,0x5946,0x594A,/* 0x40-0x47 */
+ 0x594C,0x594D,0x5950,0x5952,0x5953,0x5959,0x595B,0x595C,/* 0x48-0x4F */
+ 0x595D,0x595E,0x595F,0x5961,0x5963,0x5964,0x5966,0x5967,/* 0x50-0x57 */
+ 0x5968,0x5969,0x596A,0x596B,0x596C,0x596D,0x596E,0x596F,/* 0x58-0x5F */
+ 0x5970,0x5971,0x5972,0x5975,0x5977,0x597A,0x597B,0x597C,/* 0x60-0x67 */
+ 0x597E,0x597F,0x5980,0x5985,0x5989,0x598B,0x598C,0x598E,/* 0x68-0x6F */
+ 0x598F,0x5990,0x5991,0x5994,0x5995,0x5998,0x599A,0x599B,/* 0x70-0x77 */
+ 0x599C,0x599D,0x599F,0x59A0,0x59A1,0x59A2,0x59A6,0x0000,/* 0x78-0x7F */
+
+ 0x59A7,0x59AC,0x59AD,0x59B0,0x59B1,0x59B3,0x59B4,0x59B5,/* 0x80-0x87 */
+ 0x59B6,0x59B7,0x59B8,0x59BA,0x59BC,0x59BD,0x59BF,0x59C0,/* 0x88-0x8F */
+ 0x59C1,0x59C2,0x59C3,0x59C4,0x59C5,0x59C7,0x59C8,0x59C9,/* 0x90-0x97 */
+ 0x59CC,0x59CD,0x59CE,0x59CF,0x59D5,0x59D6,0x59D9,0x59DB,/* 0x98-0x9F */
+ 0x59DE,0x59DF,0x59E0,0x59E1,0x59E2,0x59E4,0x59E6,0x59E7,/* 0xA0-0xA7 */
+ 0x59E9,0x59EA,0x59EB,0x59ED,0x59EE,0x59EF,0x59F0,0x59F1,/* 0xA8-0xAF */
+ 0x59F2,0x59F3,0x59F4,0x59F5,0x59F6,0x59F7,0x59F8,0x59FA,/* 0xB0-0xB7 */
+ 0x59FC,0x59FD,0x59FE,0x5A00,0x5A02,0x5A0A,0x5A0B,0x5A0D,/* 0xB8-0xBF */
+ 0x5A0E,0x5A0F,0x5A10,0x5A12,0x5A14,0x5A15,0x5A16,0x5A17,/* 0xC0-0xC7 */
+ 0x5A19,0x5A1A,0x5A1B,0x5A1D,0x5A1E,0x5A21,0x5A22,0x5A24,/* 0xC8-0xCF */
+ 0x5A26,0x5A27,0x5A28,0x5A2A,0x5A2B,0x5A2C,0x5A2D,0x5A2E,/* 0xD0-0xD7 */
+ 0x5A2F,0x5A30,0x5A33,0x5A35,0x5A37,0x5A38,0x5A39,0x5A3A,/* 0xD8-0xDF */
+ 0x5A3B,0x5A3D,0x5A3E,0x5A3F,0x5A41,0x5A42,0x5A43,0x5A44,/* 0xE0-0xE7 */
+ 0x5A45,0x5A47,0x5A48,0x5A4B,0x5A4C,0x5A4D,0x5A4E,0x5A4F,/* 0xE8-0xEF */
+ 0x5A50,0x5A51,0x5A52,0x5A53,0x5A54,0x5A56,0x5A57,0x5A58,/* 0xF0-0xF7 */
+ 0x5A59,0x5A5B,0x5A5C,0x5A5D,0x5A5E,0x5A5F,0x5A60,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_8B[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x5A61,0x5A63,0x5A64,0x5A65,0x5A66,0x5A68,0x5A69,0x5A6B,/* 0x40-0x47 */
+ 0x5A6C,0x5A6D,0x5A6E,0x5A6F,0x5A70,0x5A71,0x5A72,0x5A73,/* 0x48-0x4F */
+ 0x5A78,0x5A79,0x5A7B,0x5A7C,0x5A7D,0x5A7E,0x5A80,0x5A81,/* 0x50-0x57 */
+ 0x5A82,0x5A83,0x5A84,0x5A85,0x5A86,0x5A87,0x5A88,0x5A89,/* 0x58-0x5F */
+ 0x5A8A,0x5A8B,0x5A8C,0x5A8D,0x5A8E,0x5A8F,0x5A90,0x5A91,/* 0x60-0x67 */
+ 0x5A93,0x5A94,0x5A95,0x5A96,0x5A97,0x5A98,0x5A99,0x5A9C,/* 0x68-0x6F */
+ 0x5A9D,0x5A9E,0x5A9F,0x5AA0,0x5AA1,0x5AA2,0x5AA3,0x5AA4,/* 0x70-0x77 */
+ 0x5AA5,0x5AA6,0x5AA7,0x5AA8,0x5AA9,0x5AAB,0x5AAC,0x0000,/* 0x78-0x7F */
+
+ 0x5AAD,0x5AAE,0x5AAF,0x5AB0,0x5AB1,0x5AB4,0x5AB6,0x5AB7,/* 0x80-0x87 */
+ 0x5AB9,0x5ABA,0x5ABB,0x5ABC,0x5ABD,0x5ABF,0x5AC0,0x5AC3,/* 0x88-0x8F */
+ 0x5AC4,0x5AC5,0x5AC6,0x5AC7,0x5AC8,0x5ACA,0x5ACB,0x5ACD,/* 0x90-0x97 */
+ 0x5ACE,0x5ACF,0x5AD0,0x5AD1,0x5AD3,0x5AD5,0x5AD7,0x5AD9,/* 0x98-0x9F */
+ 0x5ADA,0x5ADB,0x5ADD,0x5ADE,0x5ADF,0x5AE2,0x5AE4,0x5AE5,/* 0xA0-0xA7 */
+ 0x5AE7,0x5AE8,0x5AEA,0x5AEC,0x5AED,0x5AEE,0x5AEF,0x5AF0,/* 0xA8-0xAF */
+ 0x5AF2,0x5AF3,0x5AF4,0x5AF5,0x5AF6,0x5AF7,0x5AF8,0x5AF9,/* 0xB0-0xB7 */
+ 0x5AFA,0x5AFB,0x5AFC,0x5AFD,0x5AFE,0x5AFF,0x5B00,0x5B01,/* 0xB8-0xBF */
+ 0x5B02,0x5B03,0x5B04,0x5B05,0x5B06,0x5B07,0x5B08,0x5B0A,/* 0xC0-0xC7 */
+ 0x5B0B,0x5B0C,0x5B0D,0x5B0E,0x5B0F,0x5B10,0x5B11,0x5B12,/* 0xC8-0xCF */
+ 0x5B13,0x5B14,0x5B15,0x5B18,0x5B19,0x5B1A,0x5B1B,0x5B1C,/* 0xD0-0xD7 */
+ 0x5B1D,0x5B1E,0x5B1F,0x5B20,0x5B21,0x5B22,0x5B23,0x5B24,/* 0xD8-0xDF */
+ 0x5B25,0x5B26,0x5B27,0x5B28,0x5B29,0x5B2A,0x5B2B,0x5B2C,/* 0xE0-0xE7 */
+ 0x5B2D,0x5B2E,0x5B2F,0x5B30,0x5B31,0x5B33,0x5B35,0x5B36,/* 0xE8-0xEF */
+ 0x5B38,0x5B39,0x5B3A,0x5B3B,0x5B3C,0x5B3D,0x5B3E,0x5B3F,/* 0xF0-0xF7 */
+ 0x5B41,0x5B42,0x5B43,0x5B44,0x5B45,0x5B46,0x5B47,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_8C[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x5B48,0x5B49,0x5B4A,0x5B4B,0x5B4C,0x5B4D,0x5B4E,0x5B4F,/* 0x40-0x47 */
+ 0x5B52,0x5B56,0x5B5E,0x5B60,0x5B61,0x5B67,0x5B68,0x5B6B,/* 0x48-0x4F */
+ 0x5B6D,0x5B6E,0x5B6F,0x5B72,0x5B74,0x5B76,0x5B77,0x5B78,/* 0x50-0x57 */
+ 0x5B79,0x5B7B,0x5B7C,0x5B7E,0x5B7F,0x5B82,0x5B86,0x5B8A,/* 0x58-0x5F */
+ 0x5B8D,0x5B8E,0x5B90,0x5B91,0x5B92,0x5B94,0x5B96,0x5B9F,/* 0x60-0x67 */
+ 0x5BA7,0x5BA8,0x5BA9,0x5BAC,0x5BAD,0x5BAE,0x5BAF,0x5BB1,/* 0x68-0x6F */
+ 0x5BB2,0x5BB7,0x5BBA,0x5BBB,0x5BBC,0x5BC0,0x5BC1,0x5BC3,/* 0x70-0x77 */
+ 0x5BC8,0x5BC9,0x5BCA,0x5BCB,0x5BCD,0x5BCE,0x5BCF,0x0000,/* 0x78-0x7F */
+
+ 0x5BD1,0x5BD4,0x5BD5,0x5BD6,0x5BD7,0x5BD8,0x5BD9,0x5BDA,/* 0x80-0x87 */
+ 0x5BDB,0x5BDC,0x5BE0,0x5BE2,0x5BE3,0x5BE6,0xF9AA,0x5BE9,/* 0x88-0x8F */
+ 0x5BEA,0x5BEB,0x5BEC,0x5BED,0x5BEF,0x5BF1,0x5BF2,0x5BF3,/* 0x90-0x97 */
+ 0x5BF4,0x5BF5,0x5BF6,0x5BF7,0x5BFD,0x5BFE,0x5C00,0x5C02,/* 0x98-0x9F */
+ 0x5C03,0x5C05,0x5C07,0x5C08,0x5C0B,0x5C0C,0x5C0D,0x5C0E,/* 0xA0-0xA7 */
+ 0x5C10,0x5C12,0x5C13,0x5C17,0x5C19,0x5C1B,0x5C1E,0x5C1F,/* 0xA8-0xAF */
+ 0x5C20,0x5C21,0x5C23,0x5C26,0x5C28,0x5C29,0x5C2A,0x5C2B,/* 0xB0-0xB7 */
+ 0x5C2D,0x5C2E,0x5C2F,0x5C30,0x5C32,0x5C33,0x5C35,0x5C36,/* 0xB8-0xBF */
+ 0x5C37,0x5C43,0x5C44,0x5C46,0x5C47,0x5C4C,0x5C4D,0x5C52,/* 0xC0-0xC7 */
+ 0x5C53,0x5C54,0x5C56,0x5C57,0x5C58,0x5C5A,0x5C5B,0x5C5C,/* 0xC8-0xCF */
+ 0x5C5D,0x5C5F,0xF94B,0x5C64,0x5C67,0x5C68,0x5C69,0x5C6A,/* 0xD0-0xD7 */
+ 0x5C6B,0x5C6C,0x5C6D,0x5C70,0x5C72,0x5C73,0x5C74,0x5C75,/* 0xD8-0xDF */
+ 0x5C76,0x5C77,0x5C78,0x5C7B,0x5C7C,0x5C7D,0x5C7E,0x5C80,/* 0xE0-0xE7 */
+ 0x5C83,0x5C84,0x5C85,0x5C86,0x5C87,0x5C89,0x5C8A,0x5C8B,/* 0xE8-0xEF */
+ 0x5C8E,0x5C8F,0x5C92,0x5C93,0x5C95,0x5C9D,0x5C9E,0x5C9F,/* 0xF0-0xF7 */
+ 0x5CA0,0x5CA1,0x5CA4,0x5CA5,0x5CA6,0x5CA7,0x5CA8,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_8D[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x5CAA,0x5CAE,0x5CAF,0x5CB0,0x5CB2,0x5CB4,0x5CB6,0x5CB9,/* 0x40-0x47 */
+ 0x5CBA,0x5CBB,0x5CBC,0x5CBE,0x5CC0,0x5CC2,0x5CC3,0x5CC5,/* 0x48-0x4F */
+ 0x5CC6,0x5CC7,0x5CC8,0x5CC9,0x5CCA,0x5CCC,0x5CCD,0x5CCE,/* 0x50-0x57 */
+ 0x5CCF,0x5CD0,0x5CD1,0x5CD3,0x5CD4,0x5CD5,0x5CD6,0x5CD7,/* 0x58-0x5F */
+ 0x5CD8,0x5CDA,0x5CDB,0x5CDC,0x5CDD,0x5CDE,0x5CDF,0x5CE0,/* 0x60-0x67 */
+ 0x5CE2,0x5CE3,0x5CE7,0x5CE9,0x5CEB,0x5CEC,0x5CEE,0x5CEF,/* 0x68-0x6F */
+ 0x5CF1,0x5CF2,0x5CF3,0x5CF4,0x5CF5,0x5CF6,0x5CF7,0x5CF8,/* 0x70-0x77 */
+ 0x5CF9,0x5CFA,0x5CFC,0x5CFD,0x5CFE,0x5CFF,0x5D00,0x0000,/* 0x78-0x7F */
+
+ 0x5D01,0x5D04,0x5D05,0x5D08,0x5D09,0x5D0A,0x5D0B,0x5D0C,/* 0x80-0x87 */
+ 0x5D0D,0x5D0F,0x5D10,0x5D11,0x5D12,0x5D13,0x5D15,0x5D17,/* 0x88-0x8F */
+ 0x5D18,0xF9D5,0x5D1A,0x5D1C,0x5D1D,0x5D1F,0x5D20,0x5D21,/* 0x90-0x97 */
+ 0x5D22,0x5D23,0x5D25,0x5D28,0x5D2A,0x5D2B,0x5D2C,0x5D2F,/* 0x98-0x9F */
+ 0x5D30,0x5D31,0x5D32,0x5D33,0x5D35,0x5D36,0x5D37,0x5D38,/* 0xA0-0xA7 */
+ 0x5D39,0x5D3A,0x5D3B,0x5D3C,0x5D3F,0x5D40,0x5D41,0x5D42,/* 0xA8-0xAF */
+ 0x5D43,0x5D44,0x5D45,0x5D46,0x5D48,0x5D49,0x5D4D,0x5D4E,/* 0xB0-0xB7 */
+ 0x5D4F,0xF921,0x5D51,0x5D52,0x5D53,0x5D54,0x5D55,0x5D56,/* 0xB8-0xBF */
+ 0x5D57,0x5D59,0x5D5A,0x5D5C,0x5D5E,0x5D5F,0x5D60,0x5D61,/* 0xC0-0xC7 */
+ 0x5D62,0x5D63,0x5D64,0x5D65,0x5D66,0x5D67,0x5D68,0x5D6A,/* 0xC8-0xCF */
+ 0x5D6D,0x5D6E,0x5D70,0x5D71,0x5D72,0x5D73,0x5D75,0x5D76,/* 0xD0-0xD7 */
+ 0x5D77,0x5D78,0x5D79,0x5D7A,0x5D7B,0x5D7C,0x5D7D,0x5D7E,/* 0xD8-0xDF */
+ 0x5D7F,0x5D80,0x5D81,0x5D83,0x5D84,0x5D85,0x5D86,0x5D87,/* 0xE0-0xE7 */
+ 0x5D88,0x5D89,0x5D8A,0x5D8B,0x5D8C,0x5D8D,0x5D8E,0x5D8F,/* 0xE8-0xEF */
+ 0x5D90,0x5D91,0x5D92,0x5D93,0x5D94,0x5D95,0x5D96,0x5D97,/* 0xF0-0xF7 */
+ 0x5D98,0x5D9A,0x5D9B,0x5D9C,0x5D9E,0x5D9F,0x5DA0,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_8E[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x5DA1,0x5DA2,0x5DA3,0x5DA4,0x5DA5,0x5DA6,0x5DA7,0x5DA8,/* 0x40-0x47 */
+ 0x5DA9,0x5DAA,0x5DAB,0x5DAC,0x5DAD,0x5DAE,0x5DAF,0x5DB0,/* 0x48-0x4F */
+ 0x5DB1,0x5DB2,0x5DB3,0x5DB4,0x5DB5,0x5DB6,0x5DB8,0x5DB9,/* 0x50-0x57 */
+ 0xF9AB,0x5DBB,0x5DBC,0x5DBD,0x5DBE,0x5DBF,0x5DC0,0x5DC1,/* 0x58-0x5F */
+ 0x5DC2,0x5DC3,0x5DC4,0x5DC6,0x5DC7,0x5DC8,0x5DC9,0x5DCA,/* 0x60-0x67 */
+ 0x5DCB,0x5DCC,0x5DCE,0x5DCF,0x5DD0,0x5DD1,0x5DD2,0x5DD3,/* 0x68-0x6F */
+ 0x5DD4,0x5DD5,0x5DD6,0x5DD7,0x5DD8,0x5DD9,0x5DDA,0x5DDC,/* 0x70-0x77 */
+ 0x5DDF,0x5DE0,0x5DE3,0x5DE4,0x5DEA,0x5DEC,0x5DED,0x0000,/* 0x78-0x7F */
+
+ 0x5DF0,0x5DF5,0x5DF6,0x5DF8,0x5DF9,0x5DFA,0x5DFB,0x5DFC,/* 0x80-0x87 */
+ 0x5DFF,0x5E00,0x5E04,0x5E07,0x5E09,0x5E0A,0x5E0B,0x5E0D,/* 0x88-0x8F */
+ 0x5E0E,0x5E12,0x5E13,0x5E17,0x5E1E,0x5E1F,0x5E20,0x5E21,/* 0x90-0x97 */
+ 0x5E22,0x5E23,0x5E24,0x5E25,0x5E28,0x5E29,0x5E2A,0x5E2B,/* 0x98-0x9F */
+ 0x5E2C,0x5E2F,0x5E30,0x5E32,0x5E33,0x5E34,0x5E35,0x5E36,/* 0xA0-0xA7 */
+ 0x5E39,0x5E3A,0x5E3E,0x5E3F,0x5E40,0x5E41,0x5E43,0x5E46,/* 0xA8-0xAF */
+ 0x5E47,0x5E48,0x5E49,0x5E4A,0x5E4B,0x5E4D,0x5E4E,0x5E4F,/* 0xB0-0xB7 */
+ 0x5E50,0x5E51,0x5E52,0x5E53,0x5E56,0x5E57,0x5E58,0x5E59,/* 0xB8-0xBF */
+ 0x5E5A,0x5E5C,0x5E5D,0x5E5F,0x5E60,0x5E63,0x5E64,0x5E65,/* 0xC0-0xC7 */
+ 0x5E66,0x5E67,0x5E68,0x5E69,0x5E6A,0x5E6B,0x5E6C,0x5E6D,/* 0xC8-0xCF */
+ 0x5E6E,0x5E6F,0x5E70,0x5E71,0x5E75,0x5E77,0x5E79,0x5E7E,/* 0xD0-0xD7 */
+ 0x5E81,0x5E82,0x5E83,0x5E85,0x5E88,0x5E89,0x5E8C,0x5E8D,/* 0xD8-0xDF */
+ 0x5E8E,0x5E92,0x5E98,0x5E9B,0x5E9D,0x5EA1,0x5EA2,0x5EA3,/* 0xE0-0xE7 */
+ 0x5EA4,0x5EA8,0x5EA9,0x5EAA,0x5EAB,0x5EAC,0x5EAE,0x5EAF,/* 0xE8-0xEF */
+ 0x5EB0,0x5EB1,0x5EB2,0x5EB4,0x5EBA,0x5EBB,0x5EBC,0x5EBD,/* 0xF0-0xF7 */
+ 0x5EBF,0x5EC0,0x5EC1,0x5EC2,0x5EC3,0x5EC4,0x5EC5,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_8F[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x5EC6,0x5EC7,0x5EC8,0x5ECB,0x5ECC,0x5ECD,0x5ECE,0x5ECF,/* 0x40-0x47 */
+ 0x5ED0,0x5ED4,0x5ED5,0x5ED7,0x5ED8,0x5ED9,0x5EDA,0x5EDC,/* 0x48-0x4F */
+ 0x5EDD,0x5EDE,0x5EDF,0x5EE0,0x5EE1,0x5EE2,0x5EE3,0x5EE4,/* 0x50-0x57 */
+ 0x5EE5,0x5EE6,0x5EE7,0x5EE9,0x5EEB,0xF982,0x5EED,0x5EEE,/* 0x58-0x5F */
+ 0x5EEF,0x5EF0,0x5EF1,0x5EF2,0x5EF3,0x5EF5,0x5EF8,0x5EF9,/* 0x60-0x67 */
+ 0x5EFB,0x5EFC,0x5EFD,0x5F05,0x5F06,0x5F07,0x5F09,0x5F0C,/* 0x68-0x6F */
+ 0x5F0D,0x5F0E,0x5F10,0x5F12,0x5F14,0x5F16,0x5F19,0x5F1A,/* 0x70-0x77 */
+ 0x5F1C,0x5F1D,0x5F1E,0x5F21,0x5F22,0x5F23,0x5F24,0x0000,/* 0x78-0x7F */
+
+ 0x5F28,0x5F2B,0x5F2C,0x5F2E,0x5F30,0x5F32,0x5F33,0x5F34,/* 0x80-0x87 */
+ 0x5F35,0x5F36,0x5F37,0x5F38,0x5F3B,0x5F3D,0x5F3E,0x5F3F,/* 0x88-0x8F */
+ 0x5F41,0x5F42,0x5F43,0x5F44,0x5F45,0x5F46,0x5F47,0x5F48,/* 0x90-0x97 */
+ 0x5F49,0x5F4A,0x5F4B,0x5F4C,0x5F4D,0x5F4E,0x5F4F,0x5F51,/* 0x98-0x9F */
+ 0x5F54,0x5F59,0x5F5A,0x5F5B,0x5F5C,0x5F5E,0x5F5F,0x5F60,/* 0xA0-0xA7 */
+ 0x5F63,0x5F65,0x5F67,0x5F68,0x5F6B,0x5F6E,0x5F6F,0x5F72,/* 0xA8-0xAF */
+ 0x5F74,0x5F75,0x5F76,0x5F78,0x5F7A,0x5F7D,0x5F7E,0x5F7F,/* 0xB0-0xB7 */
+ 0x5F83,0x5F86,0x5F8D,0x5F8E,0x5F8F,0x5F91,0x5F93,0x5F94,/* 0xB8-0xBF */
+ 0x5F96,0x5F9A,0x5F9B,0x5F9D,0x5F9E,0x5F9F,0x5FA0,0x5FA2,/* 0xC0-0xC7 */
+ 0x5FA3,0x5FA4,0x5FA5,0x5FA6,0x5FA7,0xF966,0x5FAB,0x5FAC,/* 0xC8-0xCF */
+ 0x5FAF,0x5FB0,0x5FB1,0x5FB2,0x5FB3,0x5FB4,0x5FB6,0x5FB8,/* 0xD0-0xD7 */
+ 0x5FB9,0x5FBA,0x5FBB,0x5FBE,0x5FBF,0x5FC0,0x5FC1,0x5FC2,/* 0xD8-0xDF */
+ 0x5FC7,0x5FC8,0x5FCA,0x5FCB,0x5FCE,0x5FD3,0x5FD4,0x5FD5,/* 0xE0-0xE7 */
+ 0x5FDA,0x5FDB,0x5FDC,0x5FDE,0x5FDF,0x5FE2,0x5FE3,0x5FE5,/* 0xE8-0xEF */
+ 0x5FE6,0x5FE8,0x5FE9,0x5FEC,0x5FEF,0x5FF0,0x5FF2,0x5FF3,/* 0xF0-0xF7 */
+ 0x5FF4,0x5FF6,0x5FF7,0x5FF9,0x5FFA,0x5FFC,0x6007,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_90[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x6008,0x6009,0x600B,0x600C,0x6010,0x6011,0x6013,0x6017,/* 0x40-0x47 */
+ 0x6018,0x601A,0x601E,0x601F,0x6022,0x6023,0x6024,0x602C,/* 0x48-0x4F */
+ 0x602D,0x602E,0x6030,0x6031,0x6032,0x6033,0x6034,0x6036,/* 0x50-0x57 */
+ 0x6037,0x6038,0x6039,0x603A,0x603D,0x603E,0x6040,0x6044,/* 0x58-0x5F */
+ 0x6045,0x6046,0x6047,0x6048,0x6049,0x604A,0x604C,0x604E,/* 0x60-0x67 */
+ 0x604F,0x6051,0x6053,0x6054,0x6056,0x6057,0x6058,0x605B,/* 0x68-0x6F */
+ 0x605C,0x605E,0x605F,0x6060,0x6061,0x6065,0x6066,0x606E,/* 0x70-0x77 */
+ 0x6071,0x6072,0x6074,0x6075,0x6077,0x607E,0x6080,0x0000,/* 0x78-0x7F */
+
+ 0x6081,0x6082,0x6085,0x6086,0x6087,0x6088,0x608A,0x608B,/* 0x80-0x87 */
+ 0x608E,0x608F,0x6090,0x6091,0x6093,0x6095,0x6097,0x6098,/* 0x88-0x8F */
+ 0x6099,0x609C,0x609E,0x60A1,0x60A2,0x60A4,0x60A5,0x60A7,/* 0x90-0x97 */
+ 0x60A9,0x60AA,0x60AE,0x60B0,0x60B3,0x60B5,0x60B6,0x60B7,/* 0x98-0x9F */
+ 0x60B9,0x60BA,0x60BD,0x60BE,0x60BF,0x60C0,0x60C1,0x60C2,/* 0xA0-0xA7 */
+ 0x60C3,0x60C4,0x60C7,0x60C8,0x60C9,0x60CC,0x60CD,0x60CE,/* 0xA8-0xAF */
+ 0x60CF,0x60D0,0x60D2,0x60D3,0x60D4,0x60D6,0x60D7,0x60D9,/* 0xB0-0xB7 */
+ 0x60DB,0x60DE,0xF9B9,0x60E2,0x60E3,0x60E4,0x60E5,0x60EA,/* 0xB8-0xBF */
+ 0x60F1,0x60F2,0x60F5,0x60F7,0x60F8,0x60FB,0x60FC,0x60FD,/* 0xC0-0xC7 */
+ 0x60FE,0x60FF,0x6102,0x6103,0x6104,0x6105,0x6107,0x610A,/* 0xC8-0xCF */
+ 0x610B,0x610C,0x6110,0x6111,0x6112,0x6113,0x6114,0x6116,/* 0xD0-0xD7 */
+ 0x6117,0x6118,0x6119,0x611B,0x611C,0x611D,0x611E,0x6121,/* 0xD8-0xDF */
+ 0x6122,0x6125,0x6128,0x6129,0x612A,0x612C,0x612D,0x612E,/* 0xE0-0xE7 */
+ 0x612F,0x6130,0x6131,0x6132,0x6133,0x6134,0x6135,0x6136,/* 0xE8-0xEF */
+ 0x6137,0x6138,0x6139,0x613A,0x613B,0x613C,0x613D,0x613E,/* 0xF0-0xF7 */
+ 0x6140,0x6141,0x6142,0x6143,0xF9D9,0x6145,0x6146,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_91[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x6147,0x6149,0x614B,0x614D,0x614F,0x6150,0x6152,0x6153,/* 0x40-0x47 */
+ 0x6154,0x6156,0x6157,0x6158,0x6159,0x615A,0x615B,0x615C,/* 0x48-0x4F */
+ 0x615E,0x615F,0x6160,0x6161,0x6163,0x6164,0x6165,0x6166,/* 0x50-0x57 */
+ 0x6169,0x616A,0x616B,0x616C,0x616D,0x616E,0x616F,0x6171,/* 0x58-0x5F */
+ 0x6172,0x6173,0x6174,0x6176,0x6178,0x6179,0x617A,0x617B,/* 0x60-0x67 */
+ 0x617C,0x617D,0x617E,0x617F,0x6180,0x6181,0x6182,0x6183,/* 0x68-0x6F */
+ 0x6184,0x6185,0x6186,0x6187,0x6188,0x6189,0x618A,0x618C,/* 0x70-0x77 */
+ 0x618D,0x618F,0xF98F,0x6191,0x6192,0x6193,0x6195,0x0000,/* 0x78-0x7F */
+
+ 0x6196,0x6197,0x6198,0x6199,0x619A,0x619B,0x619C,0x619E,/* 0x80-0x87 */
+ 0x619F,0x61A0,0x61A1,0x61A2,0x61A3,0x61A4,0x61A5,0x61A6,/* 0x88-0x8F */
+ 0x61AA,0x61AB,0x61AD,0x61AE,0x61AF,0x61B0,0x61B1,0x61B2,/* 0x90-0x97 */
+ 0x61B3,0x61B4,0x61B5,0x61B6,0x61B8,0x61B9,0x61BA,0x61BB,/* 0x98-0x9F */
+ 0x61BC,0x61BD,0x61BF,0x61C0,0x61C1,0x61C3,0x61C4,0x61C5,/* 0xA0-0xA7 */
+ 0x61C6,0x61C7,0x61C9,0x61CC,0x61CD,0x61CE,0x61CF,0x61D0,/* 0xA8-0xAF */
+ 0x61D3,0x61D5,0x61D6,0x61D7,0x61D8,0x61D9,0x61DA,0x61DB,/* 0xB0-0xB7 */
+ 0x61DC,0x61DD,0x61DE,0x61DF,0x61E0,0x61E1,0x61E2,0x61E3,/* 0xB8-0xBF */
+ 0x61E4,0x61E5,0x61E7,0x61E8,0x61E9,0x61EA,0x61EB,0x61EC,/* 0xC0-0xC7 */
+ 0x61ED,0x61EE,0x61EF,0x61F0,0x61F1,0x61F2,0x61F3,0x61F4,/* 0xC8-0xCF */
+ 0xF90D,0x61F7,0x61F8,0x61F9,0x61FA,0x61FB,0x61FC,0x61FD,/* 0xD0-0xD7 */
+ 0x61FE,0xF990,0x6201,0x6202,0x6203,0x6204,0x6205,0x6207,/* 0xD8-0xDF */
+ 0x6209,0x6213,0x6214,0x6219,0x621C,0x621D,0x621E,0x6220,/* 0xE0-0xE7 */
+ 0x6223,0x6226,0x6227,0x6228,0x6229,0x622B,0x622D,0x622F,/* 0xE8-0xEF */
+ 0x6230,0x6231,0x6232,0x6235,0x6236,0x6238,0x6239,0x623A,/* 0xF0-0xF7 */
+ 0x623B,0x623C,0x6242,0x6244,0x6245,0x6246,0x624A,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_92[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x624F,0x6250,0x6255,0x6256,0x6257,0x6259,0x625A,0x625C,/* 0x40-0x47 */
+ 0x625D,0x625E,0x625F,0x6260,0x6261,0x6262,0x6264,0x6265,/* 0x48-0x4F */
+ 0x6268,0x6271,0x6272,0x6274,0x6275,0x6277,0x6278,0x627A,/* 0x50-0x57 */
+ 0x627B,0x627D,0x6281,0x6282,0x6283,0x6285,0x6286,0x6287,/* 0x58-0x5F */
+ 0x6288,0x628B,0x628C,0x628D,0x628E,0x628F,0x6290,0x6294,/* 0x60-0x67 */
+ 0x6299,0x629C,0x629D,0x629E,0x62A3,0x62A6,0x62A7,0x62A9,/* 0x68-0x6F */
+ 0x62AA,0x62AD,0x62AE,0x62AF,0x62B0,0x62B2,0x62B3,0x62B4,/* 0x70-0x77 */
+ 0x62B6,0x62B7,0x62B8,0x62BA,0x62BE,0x62C0,0x62C1,0x0000,/* 0x78-0x7F */
+
+ 0x62C3,0x62CB,0xF95B,0x62D1,0x62D5,0x62DD,0x62DE,0x62E0,/* 0x80-0x87 */
+ 0x62E1,0x62E4,0x62EA,0x62EB,0x62F0,0x62F2,0x62F5,0x62F8,/* 0x88-0x8F */
+ 0x62F9,0x62FA,0x62FB,0x6300,0x6303,0x6304,0x6305,0x6306,/* 0x90-0x97 */
+ 0x630A,0x630B,0x630C,0x630D,0x630F,0x6310,0x6312,0x6313,/* 0x98-0x9F */
+ 0x6314,0x6315,0x6317,0x6318,0x6319,0x631C,0x6326,0x6327,/* 0xA0-0xA7 */
+ 0x6329,0x632C,0x632D,0x632E,0x6330,0x6331,0x6333,0x6334,/* 0xA8-0xAF */
+ 0x6335,0x6336,0x6337,0x6338,0x633B,0x633C,0x633E,0x633F,/* 0xB0-0xB7 */
+ 0x6340,0x6341,0x6344,0x6347,0x6348,0x634A,0x6351,0x6352,/* 0xB8-0xBF */
+ 0x6353,0x6354,0x6356,0x6357,0x6358,0x6359,0x635A,0x635B,/* 0xC0-0xC7 */
+ 0x635C,0x635D,0x6360,0x6364,0x6365,0x6366,0x6368,0x636A,/* 0xC8-0xCF */
+ 0x636B,0x636C,0x636F,0x6370,0x6372,0x6373,0x6374,0x6375,/* 0xD0-0xD7 */
+ 0x6378,0x6379,0x637C,0x637D,0x637E,0x637F,0x6381,0x6383,/* 0xD8-0xDF */
+ 0x6384,0x6385,0x6386,0x638B,0x638D,0x6391,0x6393,0x6394,/* 0xE0-0xE7 */
+ 0x6395,0x6397,0x6399,0x639A,0x639B,0x639C,0x639D,0x639E,/* 0xE8-0xEF */
+ 0x639F,0x63A1,0x63A4,0x63A6,0x63AB,0x63AF,0x63B1,0x63B2,/* 0xF0-0xF7 */
+ 0x63B5,0x63B6,0x63B9,0x63BB,0x63BD,0x63BF,0x63C0,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_93[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x63C1,0x63C2,0x63C3,0x63C5,0x63C7,0x63C8,0x63CA,0x63CB,/* 0x40-0x47 */
+ 0x63CC,0x63D1,0x63D3,0x63D4,0x63D5,0x63D7,0x63D8,0x63D9,/* 0x48-0x4F */
+ 0x63DA,0x63DB,0x63DC,0x63DD,0x63DF,0x63E2,0x63E4,0x63E5,/* 0x50-0x57 */
+ 0x63E6,0x63E7,0x63E8,0x63EB,0x63EC,0x63EE,0x63EF,0x63F0,/* 0x58-0x5F */
+ 0x63F1,0x63F3,0x63F5,0x63F7,0x63F9,0x63FA,0x63FB,0x63FC,/* 0x60-0x67 */
+ 0x63FE,0x6403,0x6404,0x6406,0x6407,0x6408,0x6409,0x640A,/* 0x68-0x6F */
+ 0x640D,0x640E,0x6411,0x6412,0x6415,0x6416,0x6417,0x6418,/* 0x70-0x77 */
+ 0x6419,0x641A,0x641D,0x641F,0x6422,0x6423,0x6424,0x0000,/* 0x78-0x7F */
+
+ 0x6425,0x6427,0x6428,0x6429,0x642B,0x642E,0x642F,0x6430,/* 0x80-0x87 */
+ 0x6431,0x6432,0x6433,0x6435,0x6436,0x6437,0x6438,0x6439,/* 0x88-0x8F */
+ 0x643B,0x643C,0x643E,0x6440,0x6442,0x6443,0x6449,0x644B,/* 0x90-0x97 */
+ 0x644C,0x644D,0x644E,0x644F,0x6450,0x6451,0x6453,0x6455,/* 0x98-0x9F */
+ 0x6456,0x6457,0x6459,0x645A,0x645B,0x645C,0x645D,0x645F,/* 0xA0-0xA7 */
+ 0x6460,0x6461,0x6462,0x6463,0x6464,0x6465,0x6466,0x6468,/* 0xA8-0xAF */
+ 0x646A,0x646B,0x646C,0x646E,0x646F,0x6470,0x6471,0x6472,/* 0xB0-0xB7 */
+ 0x6473,0x6474,0x6475,0x6476,0x6477,0x647B,0x647C,0x647D,/* 0xB8-0xBF */
+ 0x647E,0x647F,0x6480,0x6481,0x6483,0x6486,0x6488,0x6489,/* 0xC0-0xC7 */
+ 0x648A,0x648B,0x648C,0x648D,0x648E,0x648F,0x6490,0x6493,/* 0xC8-0xCF */
+ 0x6494,0x6497,0x6498,0xF991,0x649B,0x649C,0x649D,0x649F,/* 0xD0-0xD7 */
+ 0x64A0,0x64A1,0x64A2,0x64A3,0x64A5,0x64A6,0x64A7,0x64A8,/* 0xD8-0xDF */
+ 0x64AA,0x64AB,0x64AF,0x64B1,0x64B2,0x64B3,0x64B4,0x64B6,/* 0xE0-0xE7 */
+ 0x64B9,0x64BB,0x64BD,0x64BE,0x64BF,0x64C1,0x64C3,0xF930,/* 0xE8-0xEF */
+ 0x64C6,0x64C7,0x64C8,0x64C9,0x64CA,0x64CB,0x64CC,0x64CF,/* 0xF0-0xF7 */
+ 0x64D1,0x64D3,0x64D4,0x64D5,0x64D6,0x64D9,0x64DA,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_94[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x64DB,0x64DC,0x64DD,0x64DF,0x64E0,0x64E1,0x64E3,0x64E5,/* 0x40-0x47 */
+ 0x64E7,0x64E8,0x64E9,0x64EA,0x64EB,0x64EC,0x64ED,0x64EE,/* 0x48-0x4F */
+ 0x64EF,0x64F0,0x64F1,0x64F2,0x64F3,0x64F4,0x64F5,0x64F6,/* 0x50-0x57 */
+ 0x64F7,0x64F8,0x64F9,0x64FA,0x64FB,0x64FC,0x64FD,0x64FE,/* 0x58-0x5F */
+ 0x64FF,0x6501,0x6502,0x6503,0x6504,0x6505,0x6506,0x6507,/* 0x60-0x67 */
+ 0x6508,0x650A,0x650B,0x650C,0x650D,0x650E,0x650F,0x6510,/* 0x68-0x6F */
+ 0x6511,0x6513,0x6514,0x6515,0x6516,0x6517,0x6519,0x651A,/* 0x70-0x77 */
+ 0x651B,0x651C,0x651D,0x651E,0x651F,0x6520,0x6521,0x0000,/* 0x78-0x7F */
+
+ 0x6522,0x6523,0x6524,0x6526,0x6527,0x6528,0x6529,0x652A,/* 0x80-0x87 */
+ 0x652C,0x652D,0x6530,0x6531,0x6532,0x6533,0x6537,0x653A,/* 0x88-0x8F */
+ 0x653C,0x653D,0x6540,0x6541,0x6542,0x6543,0x6544,0x6546,/* 0x90-0x97 */
+ 0x6547,0x654A,0x654B,0x654D,0x654E,0x6550,0x6552,0x6553,/* 0x98-0x9F */
+ 0x6554,0x6557,0x6558,0x655A,0x655C,0x655F,0x6560,0x6561,/* 0xA0-0xA7 */
+ 0x6564,0x6565,0x6567,0x6568,0x6569,0x656A,0x656D,0x656E,/* 0xA8-0xAF */
+ 0x656F,0x6571,0x6573,0x6575,0x6576,0xF969,0x6579,0x657A,/* 0xB0-0xB7 */
+ 0x657B,0x657C,0x657D,0x657E,0x657F,0x6580,0x6581,0x6582,/* 0xB8-0xBF */
+ 0x6583,0x6584,0x6585,0x6586,0x6588,0x6589,0x658A,0x658D,/* 0xC0-0xC7 */
+ 0x658E,0x658F,0x6592,0x6594,0x6595,0x6596,0x6598,0x659A,/* 0xC8-0xCF */
+ 0x659D,0x659E,0x65A0,0x65A2,0x65A3,0x65A6,0x65A8,0x65AA,/* 0xD0-0xD7 */
+ 0x65AC,0x65AE,0x65B1,0x65B2,0x65B3,0x65B4,0x65B5,0x65B6,/* 0xD8-0xDF */
+ 0x65B7,0x65B8,0x65BA,0x65BB,0x65BE,0x65BF,0x65C0,0x65C2,/* 0xE0-0xE7 */
+ 0x65C7,0x65C8,0x65C9,0x65CA,0x65CD,0x65D0,0x65D1,0x65D3,/* 0xE8-0xEF */
+ 0x65D4,0x65D5,0x65D8,0x65D9,0x65DA,0x65DB,0x65DC,0x65DD,/* 0xF0-0xF7 */
+ 0x65DE,0x65DF,0x65E1,0x65E3,0x65E4,0x65EA,0x65EB,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_95[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x65F2,0x65F3,0x65F4,0x65F5,0x65F8,0x65F9,0x65FB,0x65FC,/* 0x40-0x47 */
+ 0x65FD,0x65FE,0x65FF,0x6601,0x6604,0x6605,0x6607,0x6608,/* 0x48-0x4F */
+ 0x6609,0x660B,0x660D,0x6610,0x6611,0x6612,0x6616,0x6617,/* 0x50-0x57 */
+ 0x6618,0x661A,0x661B,0x661C,0x661E,0x6621,0x6622,0x6623,/* 0x58-0x5F */
+ 0x6624,0x6626,0x6629,0x662A,0x662B,0x662C,0x662E,0x6630,/* 0x60-0x67 */
+ 0x6632,0x6633,0x6637,0x6638,0x6639,0x663A,0x663B,0x663D,/* 0x68-0x6F */
+ 0x663F,0x6640,0x6642,0x6644,0x6645,0x6646,0x6647,0x6648,/* 0x70-0x77 */
+ 0x6649,0x664A,0x664D,0x664E,0x6650,0x6651,0x6658,0x0000,/* 0x78-0x7F */
+
+ 0x6659,0x665B,0x665C,0x665D,0x665E,0x6660,0x6662,0x6663,/* 0x80-0x87 */
+ 0x6665,0x6667,0x6669,0x666A,0x666B,0x666C,0x666D,0x6671,/* 0x88-0x8F */
+ 0x6672,0x6673,0x6675,0x6678,0x6679,0x667B,0x667C,0x667D,/* 0x90-0x97 */
+ 0x667F,0x6680,0x6681,0x6683,0x6685,0x6686,0xF9C5,0x6689,/* 0x98-0x9F */
+ 0x668A,0x668B,0x668D,0x668E,0x668F,0x6690,0x6692,0x6693,/* 0xA0-0xA7 */
+ 0x6694,0x6695,0x6698,0x6699,0x669A,0x669B,0x669C,0x669E,/* 0xA8-0xAF */
+ 0x669F,0x66A0,0x66A1,0x66A2,0x66A3,0x66A4,0x66A5,0x66A6,/* 0xB0-0xB7 */
+ 0x66A9,0x66AA,0x66AB,0x66AC,0x66AD,0x66AF,0x66B0,0x66B1,/* 0xB8-0xBF */
+ 0x66B2,0x66B3,0x66B5,0x66B6,0x66B7,0x66B8,0x66BA,0x66BB,/* 0xC0-0xC7 */
+ 0x66BC,0x66BD,0x66BF,0x66C0,0x66C1,0x66C2,0x66C3,0x66C4,/* 0xC8-0xCF */
+ 0x66C5,0xF98B,0x66C7,0x66C8,0x66C9,0x66CA,0x66CB,0x66CC,/* 0xD0-0xD7 */
+ 0x66CD,0x66CE,0x66CF,0x66D0,0x66D1,0x66D2,0x66D3,0x66D4,/* 0xD8-0xDF */
+ 0x66D5,0x66D6,0x66D7,0x66D8,0x66DA,0x66DE,0x66DF,0x66E0,/* 0xE0-0xE7 */
+ 0x66E1,0x66E2,0x66E3,0x66E4,0x66E5,0x66E7,0x66E8,0x66EA,/* 0xE8-0xEF */
+ 0x66EB,0x66EC,0x66ED,0x66EE,0x66EF,0x66F1,0x66F5,0x66F6,/* 0xF0-0xF7 */
+ 0x66F8,0x66FA,0x66FB,0x66FD,0x6701,0x6702,0x6703,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_96[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x6704,0x6705,0x6706,0x6707,0x670C,0x670E,0x670F,0x6711,/* 0x40-0x47 */
+ 0x6712,0x6713,0x6716,0x6718,0x6719,0x671A,0x671C,0x671E,/* 0x48-0x4F */
+ 0x6720,0x6721,0x6722,0x6723,0x6724,0x6725,0x6727,0x6729,/* 0x50-0x57 */
+ 0x672E,0x6730,0x6732,0x6733,0x6736,0x6737,0x6738,0x6739,/* 0x58-0x5F */
+ 0x673B,0x673C,0x673E,0x673F,0x6741,0x6744,0x6745,0x6747,/* 0x60-0x67 */
+ 0x674A,0x674B,0x674D,0x6752,0x6754,0x6755,0x6757,0x6758,/* 0x68-0x6F */
+ 0x6759,0x675A,0x675B,0x675D,0x6762,0x6763,0x6764,0x6766,/* 0x70-0x77 */
+ 0x6767,0x676B,0x676C,0x676E,0x6771,0x6774,0x6776,0x0000,/* 0x78-0x7F */
+
+ 0x6778,0x6779,0x677A,0xF9C8,0x677D,0x6780,0x6782,0x6783,/* 0x80-0x87 */
+ 0x6785,0x6786,0x6788,0x678A,0x678C,0x678D,0x678E,0x678F,/* 0x88-0x8F */
+ 0x6791,0x6792,0x6793,0x6794,0x6796,0x6799,0x679B,0x679F,/* 0x90-0x97 */
+ 0x67A0,0x67A1,0x67A4,0x67A6,0x67A9,0x67AC,0x67AE,0x67B1,/* 0x98-0x9F */
+ 0x67B2,0x67B4,0x67B9,0x67BA,0x67BB,0x67BC,0x67BD,0x67BE,/* 0xA0-0xA7 */
+ 0x67BF,0x67C0,0x67C2,0x67C5,0x67C6,0x67C7,0x67C8,0x67C9,/* 0xA8-0xAF */
+ 0x67CA,0x67CB,0x67CC,0x67CD,0x67CE,0x67D5,0x67D6,0x67D7,/* 0xB0-0xB7 */
+ 0x67DB,0x67DF,0x67E1,0x67E3,0x67E4,0x67E6,0x67E7,0x67E8,/* 0xB8-0xBF */
+ 0x67EA,0x67EB,0x67ED,0x67EE,0x67F2,0x67F5,0x67F6,0x67F7,/* 0xC0-0xC7 */
+ 0x67F8,0x67F9,0x67FA,0x67FB,0x67FC,0x67FE,0x6801,0x6802,/* 0xC8-0xCF */
+ 0x6803,0x6804,0x6806,0x680D,0x6810,0x6812,0x6814,0x6815,/* 0xD0-0xD7 */
+ 0x6818,0x6819,0x681A,0x681B,0x681C,0x681E,0x681F,0x6820,/* 0xD8-0xDF */
+ 0x6822,0x6823,0x6824,0x6825,0x6826,0x6827,0x6828,0x682B,/* 0xE0-0xE7 */
+ 0x682C,0x682D,0x682E,0x682F,0x6830,0x6831,0x6834,0x6835,/* 0xE8-0xEF */
+ 0x6836,0x683A,0x683B,0x683F,0x6847,0x684B,0x684D,0x684F,/* 0xF0-0xF7 */
+ 0x6852,0x6856,0x6857,0x6858,0x6859,0x685A,0x685B,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_97[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x685C,0x685D,0x685E,0x685F,0x686A,0x686C,0x686D,0x686E,/* 0x40-0x47 */
+ 0x686F,0x6870,0x6871,0x6872,0x6873,0x6875,0x6878,0x6879,/* 0x48-0x4F */
+ 0x687A,0x687B,0x687C,0x687D,0x687E,0x687F,0x6880,0x6882,/* 0x50-0x57 */
+ 0x6884,0x6887,0x6888,0x6889,0x688A,0x688B,0x688C,0x688D,/* 0x58-0x5F */
+ 0x688E,0x6890,0x6891,0x6892,0x6894,0x6895,0x6896,0x6898,/* 0x60-0x67 */
+ 0x6899,0x689A,0x689B,0x689C,0x689D,0x689E,0x689F,0x68A0,/* 0x68-0x6F */
+ 0x68A1,0x68A3,0x68A4,0x68A5,0x68A9,0x68AA,0x68AB,0x68AC,/* 0x70-0x77 */
+ 0x68AE,0x68B1,0x68B2,0x68B4,0x68B6,0x68B7,0x68B8,0x0000,/* 0x78-0x7F */
+
+ 0x68B9,0x68BA,0x68BB,0x68BC,0x68BD,0x68BE,0x68BF,0x68C1,/* 0x80-0x87 */
+ 0x68C3,0x68C4,0x68C5,0x68C6,0x68C7,0x68C8,0x68CA,0x68CC,/* 0x88-0x8F */
+ 0x68CE,0x68CF,0x68D0,0x68D1,0x68D3,0x68D4,0x68D6,0x68D7,/* 0x90-0x97 */
+ 0x68D9,0x68DB,0x68DC,0x68DD,0x68DE,0x68DF,0x68E1,0x68E2,/* 0x98-0x9F */
+ 0x68E4,0x68E5,0x68E6,0x68E7,0x68E8,0x68E9,0x68EA,0x68EB,/* 0xA0-0xA7 */
+ 0x68EC,0x68ED,0x68EF,0x68F2,0x68F3,0x68F4,0x68F6,0x68F7,/* 0xA8-0xAF */
+ 0x68F8,0x68FB,0x68FD,0x68FE,0x68FF,0x6900,0x6902,0x6903,/* 0xB0-0xB7 */
+ 0x6904,0x6906,0x6907,0x6908,0x6909,0x690A,0x690C,0x690F,/* 0xB8-0xBF */
+ 0x6911,0x6913,0x6914,0x6915,0x6916,0x6917,0x6918,0x6919,/* 0xC0-0xC7 */
+ 0x691A,0x691B,0x691C,0x691D,0x691E,0x6921,0x6922,0x6923,/* 0xC8-0xCF */
+ 0x6925,0x6926,0x6927,0x6928,0x6929,0x692A,0x692B,0x692C,/* 0xD0-0xD7 */
+ 0x692E,0x692F,0x6931,0x6932,0x6933,0x6935,0x6936,0x6937,/* 0xD8-0xDF */
+ 0x6938,0x693A,0x693B,0x693C,0x693E,0x6940,0x6941,0x6943,/* 0xE0-0xE7 */
+ 0x6944,0x6945,0x6946,0x6947,0x6948,0x6949,0x694A,0x694B,/* 0xE8-0xEF */
+ 0x694C,0x694D,0x694E,0x694F,0x6950,0x6951,0x6952,0x6953,/* 0xF0-0xF7 */
+ 0x6955,0x6956,0x6958,0x6959,0x695B,0x695C,0x695F,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_98[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x6961,0x6962,0x6964,0x6965,0x6967,0x6968,0x6969,0x696A,/* 0x40-0x47 */
+ 0x696C,0x696D,0x696F,0x6970,0x6972,0x6973,0x6974,0x6975,/* 0x48-0x4F */
+ 0x6976,0x697A,0x697B,0x697D,0x697E,0x697F,0x6981,0x6983,/* 0x50-0x57 */
+ 0x6985,0x698A,0x698B,0x698C,0x698E,0x698F,0x6990,0x6991,/* 0x58-0x5F */
+ 0x6992,0x6993,0x6996,0x6997,0x6999,0x699A,0x699D,0x699E,/* 0x60-0x67 */
+ 0x699F,0x69A0,0x69A1,0x69A2,0x69A3,0x69A4,0x69A5,0x69A6,/* 0x68-0x6F */
+ 0x69A9,0x69AA,0x69AC,0x69AE,0x69AF,0x69B0,0x69B2,0x69B3,/* 0x70-0x77 */
+ 0x69B5,0x69B6,0x69B8,0x69B9,0x69BA,0x69BC,0x69BD,0x0000,/* 0x78-0x7F */
+
+ 0x69BE,0x69BF,0x69C0,0x69C2,0x69C3,0x69C4,0x69C5,0x69C6,/* 0x80-0x87 */
+ 0x69C7,0x69C8,0x69C9,0x69CB,0x69CD,0x69CF,0x69D1,0x69D2,/* 0x88-0x8F */
+ 0x69D3,0x69D5,0x69D6,0x69D7,0x69D8,0x69D9,0x69DA,0x69DC,/* 0x90-0x97 */
+ 0x69DD,0x69DE,0x69E1,0x69E2,0x69E3,0x69E4,0x69E5,0x69E6,/* 0x98-0x9F */
+ 0x69E7,0x69E8,0x69E9,0x69EA,0x69EB,0x69EC,0x69EE,0x69EF,/* 0xA0-0xA7 */
+ 0x69F0,0x69F1,0x69F3,0x69F4,0x69F5,0x69F6,0x69F7,0x69F8,/* 0xA8-0xAF */
+ 0x69F9,0x69FA,0x69FB,0x69FC,0x69FE,0x6A00,0x6A01,0xF9BF,/* 0xB0-0xB7 */
+ 0x6A03,0x6A04,0x6A05,0x6A06,0x6A07,0x6A08,0x6A09,0x6A0B,/* 0xB8-0xBF */
+ 0x6A0C,0x6A0D,0x6A0E,0x6A0F,0x6A10,0x6A11,0x6A12,0xF94C,/* 0xC0-0xC7 */
+ 0x6A14,0x6A15,0x6A16,0x6A19,0x6A1A,0x6A1B,0x6A1C,0x6A1D,/* 0xC8-0xCF */
+ 0x6A1E,0x6A20,0x6A22,0x6A23,0x6A24,0x6A25,0x6A26,0x6A27,/* 0xD0-0xD7 */
+ 0x6A29,0x6A2B,0x6A2C,0x6A2D,0x6A2E,0x6A30,0x6A32,0x6A33,/* 0xD8-0xDF */
+ 0x6A34,0x6A36,0x6A37,0x6A38,0x6A39,0x6A3A,0x6A3B,0x6A3C,/* 0xE0-0xE7 */
+ 0x6A3F,0x6A40,0x6A41,0x6A42,0x6A43,0x6A45,0x6A46,0x6A48,/* 0xE8-0xEF */
+ 0x6A49,0x6A4A,0x6A4B,0x6A4C,0x6A4D,0x6A4E,0x6A4F,0x6A51,/* 0xF0-0xF7 */
+ 0x6A52,0x6A53,0x6A54,0x6A55,0x6A56,0x6A57,0x6A5A,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_99[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x6A5C,0x6A5D,0x6A5E,0x6A5F,0x6A60,0x6A62,0x6A63,0x6A64,/* 0x40-0x47 */
+ 0x6A66,0x6A67,0x6A68,0x6A69,0x6A6A,0x6A6B,0x6A6C,0x6A6D,/* 0x48-0x4F */
+ 0x6A6E,0x6A6F,0x6A70,0x6A72,0x6A73,0x6A74,0x6A75,0x6A76,/* 0x50-0x57 */
+ 0x6A77,0x6A78,0x6A7A,0x6A7B,0x6A7D,0x6A7E,0x6A7F,0x6A81,/* 0x58-0x5F */
+ 0x6A82,0x6A83,0x6A85,0x6A86,0x6A87,0x6A88,0x6A89,0x6A8A,/* 0x60-0x67 */
+ 0x6A8B,0x6A8C,0x6A8D,0x6A8F,0x6A92,0x6A93,0x6A94,0x6A95,/* 0x68-0x6F */
+ 0x6A96,0x6A98,0x6A99,0x6A9A,0x6A9B,0x6A9C,0x6A9D,0x6A9E,/* 0x70-0x77 */
+ 0x6A9F,0x6AA1,0x6AA2,0x6AA3,0x6AA4,0x6AA5,0x6AA6,0x0000,/* 0x78-0x7F */
+
+ 0x6AA7,0x6AA8,0x6AAA,0x6AAD,0x6AAE,0x6AAF,0x6AB0,0x6AB1,/* 0x80-0x87 */
+ 0x6AB2,0x6AB3,0x6AB4,0x6AB5,0x6AB6,0x6AB7,0x6AB8,0x6AB9,/* 0x88-0x8F */
+ 0x6ABA,0x6ABB,0x6ABC,0x6ABD,0x6ABE,0x6ABF,0x6AC0,0x6AC1,/* 0x90-0x97 */
+ 0x6AC2,0x6AC3,0x6AC4,0x6AC5,0x6AC6,0x6AC7,0x6AC8,0x6AC9,/* 0x98-0x9F */
+ 0x6ACA,0x6ACB,0x6ACC,0x6ACD,0x6ACE,0x6ACF,0x6AD0,0x6AD1,/* 0xA0-0xA7 */
+ 0x6AD2,0xF931,0x6AD4,0x6AD5,0x6AD6,0x6AD7,0x6AD8,0x6AD9,/* 0xA8-0xAF */
+ 0x6ADA,0x6ADB,0x6ADC,0x6ADD,0x6ADE,0x6ADF,0x6AE0,0x6AE1,/* 0xB0-0xB7 */
+ 0x6AE2,0x6AE3,0x6AE4,0x6AE5,0x6AE6,0x6AE7,0x6AE8,0x6AE9,/* 0xB8-0xBF */
+ 0x6AEA,0x6AEB,0x6AEC,0x6AED,0x6AEE,0x6AEF,0x6AF0,0x6AF1,/* 0xC0-0xC7 */
+ 0x6AF2,0x6AF3,0x6AF4,0x6AF5,0x6AF6,0x6AF7,0x6AF8,0x6AF9,/* 0xC8-0xCF */
+ 0x6AFA,0x6AFB,0x6AFC,0x6AFD,0x6AFE,0x6AFF,0x6B00,0x6B01,/* 0xD0-0xD7 */
+ 0x6B02,0x6B03,0xF91D,0x6B05,0x6B06,0x6B07,0x6B08,0x6B09,/* 0xD8-0xDF */
+ 0x6B0A,0x6B0B,0x6B0C,0x6B0D,0x6B0E,0x6B0F,0x6B10,0x6B11,/* 0xE0-0xE7 */
+ 0x6B12,0x6B13,0x6B14,0x6B15,0x6B16,0x6B17,0x6B18,0x6B19,/* 0xE8-0xEF */
+ 0x6B1A,0x6B1B,0x6B1C,0x6B1D,0x6B1E,0x6B1F,0x6B25,0x6B26,/* 0xF0-0xF7 */
+ 0x6B28,0x6B29,0x6B2A,0x6B2B,0x6B2C,0x6B2D,0x6B2E,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_9A[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x6B2F,0x6B30,0x6B31,0x6B33,0x6B34,0x6B35,0x6B36,0x6B38,/* 0x40-0x47 */
+ 0x6B3B,0x6B3C,0x6B3D,0x6B3F,0x6B40,0x6B41,0x6B42,0x6B44,/* 0x48-0x4F */
+ 0x6B45,0x6B48,0x6B4A,0x6B4B,0x6B4D,0x6B4E,0x6B4F,0x6B50,/* 0x50-0x57 */
+ 0x6B51,0x6B52,0x6B53,0x6B54,0x6B55,0x6B56,0x6B57,0x6B58,/* 0x58-0x5F */
+ 0x6B5A,0x6B5B,0x6B5C,0x6B5D,0x6B5E,0x6B5F,0x6B60,0x6B61,/* 0x60-0x67 */
+ 0x6B68,0x6B69,0x6B6B,0x6B6C,0x6B6D,0x6B6E,0x6B6F,0x6B70,/* 0x68-0x6F */
+ 0x6B71,0x6B72,0x6B73,0x6B74,0x6B75,0x6B76,0xF98C,0x6B78,/* 0x70-0x77 */
+ 0x6B7A,0x6B7D,0x6B7E,0x6B7F,0x6B80,0x6B85,0x6B88,0x0000,/* 0x78-0x7F */
+
+ 0x6B8C,0x6B8E,0x6B8F,0x6B90,0x6B91,0x6B94,0x6B95,0x6B97,/* 0x80-0x87 */
+ 0x6B98,0x6B99,0x6B9C,0x6B9D,0x6B9E,0x6B9F,0x6BA0,0x6BA2,/* 0x88-0x8F */
+ 0x6BA3,0x6BA4,0x6BA5,0x6BA6,0x6BA7,0x6BA8,0x6BA9,0x6BAB,/* 0x90-0x97 */
+ 0x6BAC,0x6BAD,0xF9A5,0x6BAF,0x6BB0,0x6BB1,0x6BB2,0x6BB6,/* 0x98-0x9F */
+ 0x6BB8,0x6BB9,0xF970,0x6BBB,0x6BBC,0x6BBD,0x6BBE,0x6BC0,/* 0xA0-0xA7 */
+ 0x6BC3,0x6BC4,0x6BC6,0x6BC7,0x6BC8,0x6BC9,0x6BCA,0x6BCC,/* 0xA8-0xAF */
+ 0x6BCE,0x6BD0,0x6BD1,0x6BD8,0x6BDA,0x6BDC,0x6BDD,0x6BDE,/* 0xB0-0xB7 */
+ 0x6BDF,0x6BE0,0x6BE2,0x6BE3,0x6BE4,0x6BE5,0x6BE6,0x6BE7,/* 0xB8-0xBF */
+ 0x6BE8,0x6BE9,0x6BEC,0x6BED,0x6BEE,0x6BF0,0x6BF1,0x6BF2,/* 0xC0-0xC7 */
+ 0x6BF4,0x6BF6,0x6BF7,0x6BF8,0x6BFA,0x6BFB,0x6BFC,0x6BFE,/* 0xC8-0xCF */
+ 0x6BFF,0x6C00,0x6C01,0x6C02,0x6C03,0x6C04,0x6C08,0x6C09,/* 0xD0-0xD7 */
+ 0x6C0A,0x6C0B,0x6C0C,0x6C0E,0x6C12,0x6C17,0x6C1C,0x6C1D,/* 0xD8-0xDF */
+ 0x6C1E,0x6C20,0x6C23,0x6C25,0x6C2B,0x6C2C,0x6C2D,0x6C31,/* 0xE0-0xE7 */
+ 0x6C33,0x6C36,0x6C37,0x6C39,0x6C3A,0x6C3B,0x6C3C,0x6C3E,/* 0xE8-0xEF */
+ 0x6C3F,0x6C43,0x6C44,0x6C45,0x6C48,0x6C4B,0x6C4C,0x6C4D,/* 0xF0-0xF7 */
+ 0x6C4E,0x6C4F,0x6C51,0x6C52,0x6C53,0x6C56,0x6C58,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_9B[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x6C59,0x6C5A,0x6C62,0x6C63,0x6C65,0x6C66,0x6C67,0x6C6B,/* 0x40-0x47 */
+ 0x6C6C,0x6C6D,0x6C6E,0x6C6F,0x6C71,0x6C73,0x6C75,0x6C77,/* 0x48-0x4F */
+ 0x6C78,0x6C7A,0x6C7B,0x6C7C,0x6C7F,0x6C80,0x6C84,0x6C87,/* 0x50-0x57 */
+ 0x6C8A,0x6C8B,0x6C8D,0x6C8E,0x6C91,0x6C92,0x6C95,0x6C96,/* 0x58-0x5F */
+ 0x6C97,0x6C98,0x6C9A,0x6C9C,0x6C9D,0x6C9E,0x6CA0,0x6CA2,/* 0x60-0x67 */
+ 0x6CA8,0x6CAC,0x6CAF,0x6CB0,0x6CB4,0x6CB5,0x6CB6,0x6CB7,/* 0x68-0x6F */
+ 0x6CBA,0x6CC0,0x6CC1,0x6CC2,0x6CC3,0x6CC6,0x6CC7,0x6CC8,/* 0x70-0x77 */
+ 0x6CCB,0x6CCD,0x6CCE,0x6CCF,0x6CD1,0x6CD2,0x6CD8,0x0000,/* 0x78-0x7F */
+
+ 0x6CD9,0x6CDA,0x6CDC,0x6CDD,0x6CDF,0x6CE4,0x6CE6,0x6CE7,/* 0x80-0x87 */
+ 0x6CE9,0x6CEC,0x6CED,0x6CF2,0x6CF4,0x6CF9,0x6CFF,0x6D00,/* 0x88-0x8F */
+ 0x6D02,0x6D03,0x6D05,0x6D06,0x6D08,0x6D09,0x6D0A,0x6D0D,/* 0x90-0x97 */
+ 0x6D0F,0x6D10,0x6D11,0x6D13,0x6D14,0x6D15,0x6D16,0x6D18,/* 0x98-0x9F */
+ 0x6D1C,0x6D1D,0x6D1F,0x6D20,0x6D21,0x6D22,0x6D23,0x6D24,/* 0xA0-0xA7 */
+ 0x6D26,0x6D28,0x6D29,0x6D2C,0x6D2D,0x6D2F,0x6D30,0x6D34,/* 0xA8-0xAF */
+ 0x6D36,0x6D37,0x6D38,0x6D3A,0x6D3F,0x6D40,0x6D42,0x6D44,/* 0xB0-0xB7 */
+ 0x6D49,0x6D4C,0x6D50,0x6D55,0x6D56,0x6D57,0x6D58,0x6D5B,/* 0xB8-0xBF */
+ 0x6D5D,0x6D5F,0x6D61,0x6D62,0x6D64,0x6D65,0x6D67,0x6D68,/* 0xC0-0xC7 */
+ 0x6D6B,0x6D6C,0x6D6D,0x6D70,0x6D71,0x6D72,0x6D73,0x6D75,/* 0xC8-0xCF */
+ 0x6D76,0x6D79,0x6D7A,0x6D7B,0x6D7D,0x6D7E,0x6D7F,0x6D80,/* 0xD0-0xD7 */
+ 0x6D81,0x6D83,0x6D84,0x6D86,0x6D87,0x6D8A,0x6D8B,0x6D8D,/* 0xD8-0xDF */
+ 0x6D8F,0x6D90,0x6D92,0x6D96,0x6D97,0x6D98,0x6D99,0x6D9A,/* 0xE0-0xE7 */
+ 0x6D9C,0x6DA2,0x6DA5,0x6DAC,0x6DAD,0x6DB0,0x6DB1,0x6DB3,/* 0xE8-0xEF */
+ 0x6DB4,0x6DB6,0x6DB7,0x6DB9,0x6DBA,0x6DBB,0x6DBC,0x6DBD,/* 0xF0-0xF7 */
+ 0x6DBE,0x6DC1,0x6DC2,0x6DC3,0x6DC8,0x6DC9,0x6DCA,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_9C[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x6DCD,0x6DCE,0x6DCF,0x6DD0,0x6DD2,0x6DD3,0x6DD4,0x6DD5,/* 0x40-0x47 */
+ 0x6DD7,0xF94D,0x6DDB,0x6DDC,0x6DDF,0x6DE2,0x6DE3,0x6DE5,/* 0x48-0x4F */
+ 0x6DE7,0x6DE8,0x6DE9,0xF9D6,0x6DED,0x6DEF,0x6DF0,0x6DF2,/* 0x50-0x57 */
+ 0x6DF4,0x6DF5,0x6DF6,0x6DF8,0x6DFA,0x6DFD,0x6DFE,0x6DFF,/* 0x58-0x5F */
+ 0x6E00,0x6E01,0x6E02,0x6E03,0x6E04,0x6E06,0x6E07,0x6E08,/* 0x60-0x67 */
+ 0x6E09,0x6E0B,0x6E0F,0x6E12,0x6E13,0x6E15,0x6E18,0x6E19,/* 0x68-0x6F */
+ 0x6E1B,0x6E1C,0x6E1E,0x6E1F,0x6E22,0x6E26,0x6E27,0x6E28,/* 0x70-0x77 */
+ 0x6E2A,0x6E2C,0x6E2E,0x6E30,0x6E31,0x6E33,0x6E35,0x0000,/* 0x78-0x7F */
+
+ 0x6E36,0x6E37,0x6E39,0x6E3B,0x6E3C,0x6E3D,0x6E3E,0x6E3F,/* 0x80-0x87 */
+ 0x6E40,0x6E41,0x6E42,0x6E45,0x6E46,0x6E47,0x6E48,0x6E49,/* 0x88-0x8F */
+ 0x6E4A,0x6E4B,0x6E4C,0x6E4F,0x6E50,0x6E51,0x6E52,0x6E55,/* 0x90-0x97 */
+ 0x6E57,0x6E59,0x6E5A,0x6E5C,0x6E5D,0x6E5E,0x6E60,0x6E61,/* 0x98-0x9F */
+ 0x6E62,0x6E63,0x6E64,0x6E65,0x6E66,0x6E67,0x6E68,0x6E69,/* 0xA0-0xA7 */
+ 0x6E6A,0x6E6C,0x6E6D,0x6E6F,0x6E70,0x6E71,0x6E72,0x6E73,/* 0xA8-0xAF */
+ 0x6E74,0x6E75,0x6E76,0x6E77,0x6E78,0x6E79,0x6E7A,0x6E7B,/* 0xB0-0xB7 */
+ 0x6E7C,0x6E7D,0x6E80,0x6E81,0x6E82,0x6E84,0x6E87,0x6E88,/* 0xB8-0xBF */
+ 0x6E8A,0x6E8B,0x6E8C,0x6E8D,0x6E8E,0x6E91,0x6E92,0x6E93,/* 0xC0-0xC7 */
+ 0x6E94,0x6E95,0x6E96,0x6E97,0x6E99,0x6E9A,0x6E9B,0x6E9D,/* 0xC8-0xCF */
+ 0x6E9E,0x6EA0,0x6EA1,0x6EA3,0x6EA4,0x6EA6,0x6EA8,0x6EA9,/* 0xD0-0xD7 */
+ 0x6EAB,0x6EAC,0x6EAD,0x6EAE,0x6EB0,0x6EB3,0x6EB5,0x6EB8,/* 0xD8-0xDF */
+ 0x6EB9,0x6EBC,0x6EBE,0x6EBF,0x6EC0,0x6EC3,0x6EC4,0x6EC5,/* 0xE0-0xE7 */
+ 0x6EC6,0x6EC8,0x6EC9,0x6ECA,0x6ECC,0x6ECD,0x6ECE,0x6ED0,/* 0xE8-0xEF */
+ 0x6ED2,0x6ED6,0x6ED8,0x6ED9,0x6EDB,0x6EDC,0x6EDD,0x6EE3,/* 0xF0-0xF7 */
+ 0x6EE7,0x6EEA,0x6EEB,0x6EEC,0x6EED,0x6EEE,0x6EEF,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_9D[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x6EF0,0x6EF1,0x6EF2,0x6EF3,0x6EF5,0x6EF6,0x6EF7,0x6EF8,/* 0x40-0x47 */
+ 0x6EFA,0x6EFB,0x6EFC,0x6EFD,0x6EFE,0x6EFF,0x6F00,0x6F01,/* 0x48-0x4F */
+ 0x6F03,0x6F04,0x6F05,0x6F07,0x6F08,0x6F0A,0x6F0B,0x6F0C,/* 0x50-0x57 */
+ 0x6F0D,0x6F0E,0x6F10,0x6F11,0x6F12,0x6F16,0x6F17,0x6F18,/* 0x58-0x5F */
+ 0x6F19,0x6F1A,0x6F1B,0x6F1C,0x6F1D,0x6F1E,0x6F1F,0x6F21,/* 0x60-0x67 */
+ 0x6F22,0xF992,0x6F25,0x6F26,0x6F27,0x6F28,0x6F2C,0x6F2E,/* 0x68-0x6F */
+ 0x6F30,0x6F32,0x6F34,0x6F35,0x6F37,0x6F38,0x6F39,0x6F3A,/* 0x70-0x77 */
+ 0x6F3B,0x6F3C,0x6F3D,0x6F3F,0x6F40,0x6F41,0x6F42,0x0000,/* 0x78-0x7F */
+
+ 0x6F43,0x6F44,0x6F45,0x6F48,0x6F49,0x6F4A,0x6F4C,0x6F4E,/* 0x80-0x87 */
+ 0x6F4F,0x6F50,0x6F51,0x6F52,0x6F53,0x6F54,0x6F55,0x6F56,/* 0x88-0x8F */
+ 0x6F57,0x6F59,0x6F5A,0x6F5B,0x6F5D,0x6F5F,0x6F60,0x6F61,/* 0x90-0x97 */
+ 0x6F63,0x6F64,0x6F65,0x6F67,0x6F68,0x6F69,0x6F6A,0x6F6B,/* 0x98-0x9F */
+ 0x6F6C,0x6F6F,0x6F70,0x6F71,0x6F73,0x6F75,0x6F76,0x6F77,/* 0xA0-0xA7 */
+ 0x6F79,0x6F7B,0x6F7D,0x6F7E,0x6F7F,0x6F80,0x6F81,0x6F82,/* 0xA8-0xAF */
+ 0x6F83,0x6F85,0x6F86,0x6F87,0x6F8A,0x6F8B,0x6F8F,0x6F90,/* 0xB0-0xB7 */
+ 0x6F91,0x6F92,0x6F93,0x6F94,0x6F95,0x6F96,0x6F97,0x6F98,/* 0xB8-0xBF */
+ 0x6F99,0x6F9A,0x6F9B,0x6F9D,0x6F9E,0x6F9F,0x6FA0,0x6FA2,/* 0xC0-0xC7 */
+ 0x6FA3,0x6FA4,0x6FA5,0x6FA6,0x6FA8,0x6FA9,0x6FAA,0x6FAB,/* 0xC8-0xCF */
+ 0x6FAC,0x6FAD,0x6FAE,0x6FAF,0x6FB0,0x6FB1,0x6FB2,0x6FB4,/* 0xD0-0xD7 */
+ 0x6FB5,0x6FB7,0x6FB8,0x6FBA,0x6FBB,0x6FBC,0x6FBD,0x6FBE,/* 0xD8-0xDF */
+ 0x6FBF,0x6FC1,0x6FC3,0x6FC4,0x6FC5,0x6FC6,0x6FC7,0x6FC8,/* 0xE0-0xE7 */
+ 0x6FCA,0x6FCB,0x6FCC,0x6FCD,0x6FCE,0x6FCF,0x6FD0,0x6FD3,/* 0xE8-0xEF */
+ 0x6FD4,0x6FD5,0x6FD6,0x6FD7,0x6FD8,0x6FD9,0x6FDA,0x6FDB,/* 0xF0-0xF7 */
+ 0x6FDC,0x6FDD,0x6FDF,0x6FE2,0x6FE3,0x6FE4,0x6FE5,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_9E[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x6FE6,0x6FE7,0x6FE8,0x6FE9,0x6FEA,0xF922,0x6FEC,0x6FED,/* 0x40-0x47 */
+ 0x6FF0,0x6FF1,0x6FF2,0x6FF3,0x6FF4,0x6FF5,0x6FF6,0x6FF7,/* 0x48-0x4F */
+ 0x6FF8,0x6FF9,0x6FFA,0x6FFB,0x6FFC,0x6FFD,0xF984,0x6FFF,/* 0x50-0x57 */
+ 0x7000,0x7001,0x7002,0x7003,0x7004,0x7005,0x7006,0x7007,/* 0x58-0x5F */
+ 0x7008,0x7009,0x700A,0x700B,0x700C,0x700D,0x700E,0x700F,/* 0x60-0x67 */
+ 0x7010,0x7012,0x7013,0x7014,0x7015,0x7016,0x7017,0x7018,/* 0x68-0x6F */
+ 0x7019,0x701C,0x701D,0x701E,0x701F,0x7020,0x7021,0x7022,/* 0x70-0x77 */
+ 0x7024,0x7025,0x7026,0x7027,0x7028,0x7029,0x702A,0x0000,/* 0x78-0x7F */
+
+ 0x702B,0x702C,0x702D,0x702E,0x702F,0x7030,0x7031,0x7032,/* 0x80-0x87 */
+ 0x7033,0x7034,0x7036,0x7037,0x7038,0x703A,0x703B,0x703C,/* 0x88-0x8F */
+ 0x703D,0x703E,0x703F,0x7040,0x7041,0x7042,0x7043,0x7044,/* 0x90-0x97 */
+ 0x7045,0x7046,0x7047,0x7048,0x7049,0x704A,0x704B,0x704D,/* 0x98-0x9F */
+ 0x704E,0x7050,0x7051,0x7052,0x7053,0x7054,0x7055,0x7056,/* 0xA0-0xA7 */
+ 0x7057,0x7058,0x7059,0x705A,0x705B,0x705C,0x705D,0x705F,/* 0xA8-0xAF */
+ 0x7060,0x7061,0x7062,0x7063,0x7064,0x7065,0x7066,0x7067,/* 0xB0-0xB7 */
+ 0x7068,0x7069,0x706A,0x706E,0x7071,0x7072,0x7073,0x7074,/* 0xB8-0xBF */
+ 0x7077,0x7079,0x707A,0x707B,0x707D,0x7081,0x7082,0x7083,/* 0xC0-0xC7 */
+ 0x7084,0x7086,0x7087,0x7088,0x708B,0x708C,0x708D,0x708F,/* 0xC8-0xCF */
+ 0x7090,0x7091,0x7093,0x7097,0x7098,0x709A,0x709B,0x709E,/* 0xD0-0xD7 */
+ 0x709F,0x70A0,0x70A1,0x70A2,0x70A3,0x70A4,0x70A5,0x70A6,/* 0xD8-0xDF */
+ 0x70A7,0x70A8,0x70A9,0x70AA,0x70B0,0x70B2,0x70B4,0x70B5,/* 0xE0-0xE7 */
+ 0x70B6,0x70BA,0x70BE,0x70BF,0x70C4,0x70C5,0x70C6,0x70C7,/* 0xE8-0xEF */
+ 0x70C9,0x70CB,0x70CC,0x70CD,0x70CE,0x70CF,0x70D0,0x70D1,/* 0xF0-0xF7 */
+ 0x70D2,0x70D3,0x70D4,0x70D5,0x70D6,0x70D7,0x70DA,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_9F[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x70DC,0x70DD,0x70DE,0x70E0,0x70E1,0x70E2,0x70E3,0x70E5,/* 0x40-0x47 */
+ 0x70EA,0x70EE,0x70F0,0x70F1,0x70F2,0x70F3,0x70F4,0x70F5,/* 0x48-0x4F */
+ 0x70F6,0x70F8,0x70FA,0x70FB,0x70FC,0x70FE,0x70FF,0x7100,/* 0x50-0x57 */
+ 0x7101,0x7102,0x7103,0x7104,0x7105,0x7106,0x7107,0x7108,/* 0x58-0x5F */
+ 0x710B,0x710C,0x710D,0x710E,0x710F,0x7111,0x7112,0x7114,/* 0x60-0x67 */
+ 0x7117,0x711B,0x711C,0x711D,0x711E,0x711F,0x7120,0x7121,/* 0x68-0x6F */
+ 0x7122,0x7123,0x7124,0x7125,0x7127,0x7128,0x7129,0x712A,/* 0x70-0x77 */
+ 0x712B,0x712C,0x712D,0x712E,0x7132,0x7133,0x7134,0x0000,/* 0x78-0x7F */
+
+ 0x7135,0x7137,0x7138,0x7139,0x713A,0x713B,0x713C,0x713D,/* 0x80-0x87 */
+ 0x713E,0x713F,0x7140,0x7141,0x7142,0x7143,0x7144,0x7146,/* 0x88-0x8F */
+ 0x7147,0x7148,0xF993,0x714B,0x714D,0x714F,0x7150,0x7151,/* 0x90-0x97 */
+ 0x7152,0x7153,0x7154,0x7155,0x7156,0x7157,0x7158,0x7159,/* 0x98-0x9F */
+ 0x715A,0x715B,0x715D,0x715F,0x7160,0x7161,0x7162,0x7163,/* 0xA0-0xA7 */
+ 0x7165,0x7169,0x716A,0x716B,0x716C,0x716D,0x716F,0x7170,/* 0xA8-0xAF */
+ 0x7171,0x7174,0x7175,0x7176,0x7177,0x7179,0x717B,0x717C,/* 0xB0-0xB7 */
+ 0x717E,0x717F,0x7180,0x7181,0x7182,0x7183,0x7185,0x7186,/* 0xB8-0xBF */
+ 0x7187,0x7188,0x7189,0x718B,0x718C,0x718D,0x718E,0x7190,/* 0xC0-0xC7 */
+ 0x7191,0x7192,0x7193,0x7195,0x7196,0x7197,0x719A,0x719B,/* 0xC8-0xCF */
+ 0x719C,0x719D,0x719E,0x71A1,0x71A2,0x71A3,0x71A4,0x71A5,/* 0xD0-0xD7 */
+ 0x71A6,0x71A7,0x71A9,0x71AA,0x71AB,0x71AD,0x71AE,0x71AF,/* 0xD8-0xDF */
+ 0x71B0,0x71B1,0x71B2,0x71B4,0x71B6,0x71B7,0x71B8,0x71BA,/* 0xE0-0xE7 */
+ 0x71BB,0x71BC,0x71BD,0x71BE,0x71BF,0x71C0,0x71C1,0x71C2,/* 0xE8-0xEF */
+ 0x71C4,0x71C5,0x71C6,0x71C7,0x71C8,0x71C9,0x71CA,0x71CB,/* 0xF0-0xF7 */
+ 0x71CC,0x71CD,0x71CF,0xF9EE,0x71D1,0x71D2,0x71D3,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_A0[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x71D6,0x71D7,0x71D8,0x71D9,0x71DA,0x71DB,0x71DC,0x71DD,/* 0x40-0x47 */
+ 0x71DE,0x71DF,0x71E1,0x71E2,0x71E3,0x71E4,0x71E6,0x71E8,/* 0x48-0x4F */
+ 0x71E9,0x71EA,0x71EB,0x71EC,0x71ED,0x71EF,0x71F0,0x71F1,/* 0x50-0x57 */
+ 0x71F2,0x71F3,0x71F4,0x71F5,0x71F6,0x71F7,0x71F8,0x71FA,/* 0x58-0x5F */
+ 0x71FB,0x71FC,0x71FD,0x71FE,0x71FF,0x7200,0x7201,0x7202,/* 0x60-0x67 */
+ 0x7203,0x7204,0x7205,0x7207,0x7208,0x7209,0x720A,0x720B,/* 0x68-0x6F */
+ 0x720C,0x720D,0x720E,0x720F,0xF932,0x7211,0x7212,0x7213,/* 0x70-0x77 */
+ 0x7214,0x7215,0x7216,0x7217,0x7218,0x7219,0x721A,0x0000,/* 0x78-0x7F */
+
+ 0xF91E,0x721C,0x721E,0x721F,0x7220,0x7221,0x7222,0x7223,/* 0x80-0x87 */
+ 0x7224,0x7225,0x7226,0x7227,0x7229,0x722B,0x722D,0x722E,/* 0x88-0x8F */
+ 0x722F,0x7232,0x7233,0x7234,0x723A,0x723C,0x723E,0x7240,/* 0x90-0x97 */
+ 0x7241,0x7242,0x7243,0x7244,0x7245,0x7246,0x7249,0x724A,/* 0x98-0x9F */
+ 0x724B,0x724E,0x724F,0x7250,0x7251,0x7253,0x7254,0x7255,/* 0xA0-0xA7 */
+ 0x7257,0x7258,0x725A,0x725C,0x725E,0x7260,0x7263,0x7264,/* 0xA8-0xAF */
+ 0x7265,0x7268,0x726A,0x726B,0x726C,0x726D,0x7270,0x7271,/* 0xB0-0xB7 */
+ 0x7273,0x7274,0x7276,0x7277,0x7278,0x727B,0x727C,0x727D,/* 0xB8-0xBF */
+ 0x7282,0x7283,0x7285,0x7286,0x7287,0x7288,0x7289,0x728C,/* 0xC0-0xC7 */
+ 0x728E,0x7290,0x7291,0x7293,0x7294,0x7295,0x7296,0x7297,/* 0xC8-0xCF */
+ 0x7298,0x7299,0x729A,0x729B,0x729C,0x729D,0x729E,0x72A0,/* 0xD0-0xD7 */
+ 0x72A1,0x72A2,0x72A3,0x72A4,0x72A5,0x72A6,0x72A7,0x72A8,/* 0xD8-0xDF */
+ 0x72A9,0x72AA,0x72AB,0x72AE,0x72B1,0x72B2,0x72B3,0x72B5,/* 0xE0-0xE7 */
+ 0x72BA,0x72BB,0x72BC,0x72BD,0x72BE,0x72BF,0xF9FA,0x72C5,/* 0xE8-0xEF */
+ 0x72C6,0x72C7,0x72C9,0x72CA,0x72CB,0x72CC,0x72CF,0x72D1,/* 0xF0-0xF7 */
+ 0x72D3,0x72D4,0x72D5,0x72D6,0x72D8,0x72DA,0x72DB,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_A1[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x3000,0x3001,0x3002,0x00B7,0x02C9,0x02C7,0x00A8,/* 0xA0-0xA7 */
+ 0x3003,0x3005,0x2014,0xFF5E,0x2016,0x2026,0x2018,0x2019,/* 0xA8-0xAF */
+ 0x201C,0x201D,0x3014,0x3015,0x3008,0x3009,0x300A,0x300B,/* 0xB0-0xB7 */
+ 0x300C,0x300D,0x300E,0x300F,0x3016,0x3017,0x3010,0x3011,/* 0xB8-0xBF */
+ 0x00B1,0x00D7,0x00F7,0x2236,0x2227,0x2228,0x2211,0x220F,/* 0xC0-0xC7 */
+ 0x222A,0x2229,0x2208,0x2237,0x221A,0x22A5,0x2225,0x2220,/* 0xC8-0xCF */
+ 0x2312,0x2299,0x222B,0x222E,0x2261,0x224C,0x2248,0x223D,/* 0xD0-0xD7 */
+ 0x221D,0x2260,0x226E,0x226F,0x2264,0x2265,0x221E,0x2235,/* 0xD8-0xDF */
+ 0x2234,0x2642,0x2640,0x2218,0x2032,0x2033,0x2103,0xFF04,/* 0xE0-0xE7 */
+ 0x00A4,0xFFE0,0xFFE1,0x2030,0x00A7,0x2116,0x2606,0x2605,/* 0xE8-0xEF */
+ 0x25CB,0x25CF,0x25CE,0x25C7,0x25C6,0x25A1,0x25A0,0x25B3,/* 0xF0-0xF7 */
+ 0x25B2,0x203B,0x2192,0x2190,0x2191,0x2193,0x3013,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_A2[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x2170,0x2171,0x2172,0x2173,0x2174,0x2175,0x2176,/* 0xA0-0xA7 */
+ 0x2177,0x2178,0x2179,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xA8-0xAF */
+ 0x0000,0x2488,0x2489,0x248A,0x248B,0x248C,0x248D,0x248E,/* 0xB0-0xB7 */
+ 0x248F,0x2490,0x2491,0x2492,0x2493,0x2494,0x2495,0x2496,/* 0xB8-0xBF */
+ 0x2497,0x2498,0x2499,0x249A,0x249B,0x2474,0x2475,0x2476,/* 0xC0-0xC7 */
+ 0x2477,0x2478,0x2479,0x247A,0x247B,0x247C,0x247D,0x247E,/* 0xC8-0xCF */
+ 0x247F,0x2480,0x2481,0x2482,0x2483,0x2484,0x2485,0x2486,/* 0xD0-0xD7 */
+ 0x2487,0x2460,0x2461,0x2462,0x2463,0x2464,0x2465,0x2466,/* 0xD8-0xDF */
+ 0x2467,0x2468,0x2469,0x0000,0x0000,0x3220,0x3221,0x3222,/* 0xE0-0xE7 */
+ 0x3223,0x3224,0x3225,0x3226,0x3227,0x3228,0x3229,0x0000,/* 0xE8-0xEF */
+ 0x0000,0x2160,0x2161,0x2162,0x2163,0x2164,0x2165,0x2166,/* 0xF0-0xF7 */
+ 0x2167,0x2168,0x2169,0x216A,0x216B,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_A3[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0xFF01,0xFF02,0xFF03,0xFFE5,0xFF05,0xFF06,0xFF07,/* 0xA0-0xA7 */
+ 0xFF08,0xFF09,0xFF0A,0xFF0B,0xFF0C,0xFF0D,0xFF0E,0xFF0F,/* 0xA8-0xAF */
+ 0xFF10,0xFF11,0xFF12,0xFF13,0xFF14,0xFF15,0xFF16,0xFF17,/* 0xB0-0xB7 */
+ 0xFF18,0xFF19,0xFF1A,0xFF1B,0xFF1C,0xFF1D,0xFF1E,0xFF1F,/* 0xB8-0xBF */
+ 0xFF20,0xFF21,0xFF22,0xFF23,0xFF24,0xFF25,0xFF26,0xFF27,/* 0xC0-0xC7 */
+ 0xFF28,0xFF29,0xFF2A,0xFF2B,0xFF2C,0xFF2D,0xFF2E,0xFF2F,/* 0xC8-0xCF */
+ 0xFF30,0xFF31,0xFF32,0xFF33,0xFF34,0xFF35,0xFF36,0xFF37,/* 0xD0-0xD7 */
+ 0xFF38,0xFF39,0xFF3A,0xFF3B,0xFF3C,0xFF3D,0xFF3E,0xFF3F,/* 0xD8-0xDF */
+ 0xFF40,0xFF41,0xFF42,0xFF43,0xFF44,0xFF45,0xFF46,0xFF47,/* 0xE0-0xE7 */
+ 0xFF48,0xFF49,0xFF4A,0xFF4B,0xFF4C,0xFF4D,0xFF4E,0xFF4F,/* 0xE8-0xEF */
+ 0xFF50,0xFF51,0xFF52,0xFF53,0xFF54,0xFF55,0xFF56,0xFF57,/* 0xF0-0xF7 */
+ 0xFF58,0xFF59,0xFF5A,0xFF5B,0xFF5C,0xFF5D,0xFFE3,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_A4[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x3041,0x3042,0x3043,0x3044,0x3045,0x3046,0x3047,/* 0xA0-0xA7 */
+ 0x3048,0x3049,0x304A,0x304B,0x304C,0x304D,0x304E,0x304F,/* 0xA8-0xAF */
+ 0x3050,0x3051,0x3052,0x3053,0x3054,0x3055,0x3056,0x3057,/* 0xB0-0xB7 */
+ 0x3058,0x3059,0x305A,0x305B,0x305C,0x305D,0x305E,0x305F,/* 0xB8-0xBF */
+ 0x3060,0x3061,0x3062,0x3063,0x3064,0x3065,0x3066,0x3067,/* 0xC0-0xC7 */
+ 0x3068,0x3069,0x306A,0x306B,0x306C,0x306D,0x306E,0x306F,/* 0xC8-0xCF */
+ 0x3070,0x3071,0x3072,0x3073,0x3074,0x3075,0x3076,0x3077,/* 0xD0-0xD7 */
+ 0x3078,0x3079,0x307A,0x307B,0x307C,0x307D,0x307E,0x307F,/* 0xD8-0xDF */
+ 0x3080,0x3081,0x3082,0x3083,0x3084,0x3085,0x3086,0x3087,/* 0xE0-0xE7 */
+ 0x3088,0x3089,0x308A,0x308B,0x308C,0x308D,0x308E,0x308F,/* 0xE8-0xEF */
+ 0x3090,0x3091,0x3092,0x3093,0x0000,0x0000,0x0000,0x0000,/* 0xF0-0xF7 */
+};
+
+static wchar_t c2u_A5[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x30A1,0x30A2,0x30A3,0x30A4,0x30A5,0x30A6,0x30A7,/* 0xA0-0xA7 */
+ 0x30A8,0x30A9,0x30AA,0x30AB,0x30AC,0x30AD,0x30AE,0x30AF,/* 0xA8-0xAF */
+ 0x30B0,0x30B1,0x30B2,0x30B3,0x30B4,0x30B5,0x30B6,0x30B7,/* 0xB0-0xB7 */
+ 0x30B8,0x30B9,0x30BA,0x30BB,0x30BC,0x30BD,0x30BE,0x30BF,/* 0xB8-0xBF */
+ 0x30C0,0x30C1,0x30C2,0x30C3,0x30C4,0x30C5,0x30C6,0x30C7,/* 0xC0-0xC7 */
+ 0x30C8,0x30C9,0x30CA,0x30CB,0x30CC,0x30CD,0x30CE,0x30CF,/* 0xC8-0xCF */
+ 0x30D0,0x30D1,0x30D2,0x30D3,0x30D4,0x30D5,0x30D6,0x30D7,/* 0xD0-0xD7 */
+ 0x30D8,0x30D9,0x30DA,0x30DB,0x30DC,0x30DD,0x30DE,0x30DF,/* 0xD8-0xDF */
+ 0x30E0,0x30E1,0x30E2,0x30E3,0x30E4,0x30E5,0x30E6,0x30E7,/* 0xE0-0xE7 */
+ 0x30E8,0x30E9,0x30EA,0x30EB,0x30EC,0x30ED,0x30EE,0x30EF,/* 0xE8-0xEF */
+ 0x30F0,0x30F1,0x30F2,0x30F3,0x30F4,0x30F5,0x30F6,0x0000,/* 0xF0-0xF7 */
+};
+
+static wchar_t c2u_A6[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x0391,0x0392,0x0393,0x0394,0x0395,0x0396,0x0397,/* 0xA0-0xA7 */
+ 0x0398,0x0399,0x039A,0x039B,0x039C,0x039D,0x039E,0x039F,/* 0xA8-0xAF */
+ 0x03A0,0x03A1,0x03A3,0x03A4,0x03A5,0x03A6,0x03A7,0x03A8,/* 0xB0-0xB7 */
+ 0x03A9,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xB8-0xBF */
+ 0x0000,0x03B1,0x03B2,0x03B3,0x03B4,0x03B5,0x03B6,0x03B7,/* 0xC0-0xC7 */
+ 0x03B8,0x03B9,0x03BA,0x03BB,0x03BC,0x03BD,0x03BE,0x03BF,/* 0xC8-0xCF */
+ 0x03C0,0x03C1,0x03C3,0x03C4,0x03C5,0x03C6,0x03C7,0x03C8,/* 0xD0-0xD7 */
+ 0x03C9,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xD8-0xDF */
+ 0xFE35,0xFE36,0xFE39,0xFE3A,0xFE3F,0xFE40,0xFE3D,0xFE3E,/* 0xE0-0xE7 */
+ 0xFE41,0xFE42,0xFE43,0xFE44,0x0000,0x0000,0xFE3B,0xFE3C,/* 0xE8-0xEF */
+ 0xFE37,0xFE38,0xFE31,0x0000,0xFE33,0xFE34,0x0000,0x0000,/* 0xF0-0xF7 */
+};
+
+static wchar_t c2u_A7[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x0410,0x0411,0x0412,0x0413,0x0414,0x0415,0x0401,/* 0xA0-0xA7 */
+ 0x0416,0x0417,0x0418,0x0419,0x041A,0x041B,0x041C,0x041D,/* 0xA8-0xAF */
+ 0x041E,0x041F,0x0420,0x0421,0x0422,0x0423,0x0424,0x0425,/* 0xB0-0xB7 */
+ 0x0426,0x0427,0x0428,0x0429,0x042A,0x042B,0x042C,0x042D,/* 0xB8-0xBF */
+ 0x042E,0x042F,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xC0-0xC7 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xC8-0xCF */
+ 0x0000,0x0430,0x0431,0x0432,0x0433,0x0434,0x0435,0x0451,/* 0xD0-0xD7 */
+ 0x0436,0x0437,0x0438,0x0439,0x043A,0x043B,0x043C,0x043D,/* 0xD8-0xDF */
+ 0x043E,0x043F,0x0440,0x0441,0x0442,0x0443,0x0444,0x0445,/* 0xE0-0xE7 */
+ 0x0446,0x0447,0x0448,0x0449,0x044A,0x044B,0x044C,0x044D,/* 0xE8-0xEF */
+ 0x044E,0x044F,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xF0-0xF7 */
+};
+
+static wchar_t c2u_A8[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x02CA,0x02CB,0x02D9,0x2013,0x2015,0x2025,0x2035,0x2105,/* 0x40-0x47 */
+ 0x2109,0x2196,0x2197,0x2198,0x2199,0x2215,0x221F,0x2223,/* 0x48-0x4F */
+ 0x2252,0x2266,0x2267,0x22BF,0x2550,0x2551,0x2552,0x2553,/* 0x50-0x57 */
+ 0x2554,0x2555,0x2556,0x2557,0x2558,0x2559,0x255A,0x255B,/* 0x58-0x5F */
+ 0x255C,0x255D,0x255E,0x255F,0x2560,0x2561,0x2562,0x2563,/* 0x60-0x67 */
+ 0x2564,0x2565,0x2566,0x2567,0x2568,0x2569,0x256A,0x256B,/* 0x68-0x6F */
+ 0x256C,0x256D,0x256E,0x256F,0x2570,0x2571,0x2572,0x2573,/* 0x70-0x77 */
+ 0x2581,0x2582,0x2583,0x2584,0x2585,0x2586,0x2587,0x0000,/* 0x78-0x7F */
+
+ 0x2588,0x2589,0x258A,0x258B,0x258C,0x258D,0x258E,0x258F,/* 0x80-0x87 */
+ 0x2593,0x2594,0x2595,0x25BC,0x25BD,0x25E2,0x25E3,0x25E4,/* 0x88-0x8F */
+ 0x25E5,0x2609,0x2295,0x3012,0x301D,0x301E,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x0101,0x00E1,0x01CE,0x00E0,0x0113,0x00E9,0x011B,/* 0xA0-0xA7 */
+ 0x00E8,0x012B,0x00ED,0x01D0,0x00EC,0x014D,0x00F3,0x01D2,/* 0xA8-0xAF */
+ 0x00F2,0x016B,0x00FA,0x01D4,0x00F9,0x01D6,0x01D8,0x01DA,/* 0xB0-0xB7 */
+ 0x01DC,0x00FC,0x00EA,0x0251,0x0000,0x0144,0x0148,0x0000,/* 0xB8-0xBF */
+ 0x0261,0x0000,0x0000,0x0000,0x0000,0x3105,0x3106,0x3107,/* 0xC0-0xC7 */
+ 0x3108,0x3109,0x310A,0x310B,0x310C,0x310D,0x310E,0x310F,/* 0xC8-0xCF */
+ 0x3110,0x3111,0x3112,0x3113,0x3114,0x3115,0x3116,0x3117,/* 0xD0-0xD7 */
+ 0x3118,0x3119,0x311A,0x311B,0x311C,0x311D,0x311E,0x311F,/* 0xD8-0xDF */
+ 0x3120,0x3121,0x3122,0x3123,0x3124,0x3125,0x3126,0x3127,/* 0xE0-0xE7 */
+ 0x3128,0x3129,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xE8-0xEF */
+};
+
+static wchar_t c2u_A9[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x3021,0x3022,0x3023,0x3024,0x3025,0x3026,0x3027,0x3028,/* 0x40-0x47 */
+ 0x3029,0x32A3,0x338E,0x338F,0x339C,0x339D,0x339E,0x33A1,/* 0x48-0x4F */
+ 0x33C4,0x33CE,0x33D1,0x33D2,0x33D5,0xFE30,0xFFE2,0xFFE4,/* 0x50-0x57 */
+ 0x0000,0x2121,0x3231,0x0000,0x2010,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x30FC,0x309B,0x309C,0x30FD,0x30FE,0x3006,0x309D,0x309E,/* 0x60-0x67 */
+ 0xFE49,0xFE4A,0xFE4B,0xFE4C,0xFE4D,0xFE4E,0xFE4F,0xFE50,/* 0x68-0x6F */
+ 0xFE51,0xFE52,0xFE54,0xFE55,0xFE56,0xFE57,0xFE59,0xFE5A,/* 0x70-0x77 */
+ 0xFE5B,0xFE5C,0xFE5D,0xFE5E,0xFE5F,0xFE60,0xFE61,0x0000,/* 0x78-0x7F */
+
+ 0xFE62,0xFE63,0xFE64,0xFE65,0xFE66,0xFE68,0xFE69,0xFE6A,/* 0x80-0x87 */
+ 0xFE6B,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x3007,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x0000,0x0000,0x0000,0x2500,0x2501,0x2502,0x2503,/* 0xA0-0xA7 */
+ 0x2504,0x2505,0x2506,0x2507,0x2508,0x2509,0x250A,0x250B,/* 0xA8-0xAF */
+ 0x250C,0x250D,0x250E,0x250F,0x2510,0x2511,0x2512,0x2513,/* 0xB0-0xB7 */
+ 0x2514,0x2515,0x2516,0x2517,0x2518,0x2519,0x251A,0x251B,/* 0xB8-0xBF */
+ 0x251C,0x251D,0x251E,0x251F,0x2520,0x2521,0x2522,0x2523,/* 0xC0-0xC7 */
+ 0x2524,0x2525,0x2526,0x2527,0x2528,0x2529,0x252A,0x252B,/* 0xC8-0xCF */
+ 0x252C,0x252D,0x252E,0x252F,0x2530,0x2531,0x2532,0x2533,/* 0xD0-0xD7 */
+ 0x2534,0x2535,0x2536,0x2537,0x2538,0x2539,0x253A,0x253B,/* 0xD8-0xDF */
+ 0x253C,0x253D,0x253E,0x253F,0x2540,0x2541,0x2542,0x2543,/* 0xE0-0xE7 */
+ 0x2544,0x2545,0x2546,0x2547,0x2548,0x2549,0x254A,0x254B,/* 0xE8-0xEF */
+};
+
+static wchar_t c2u_AA[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x72DC,0x72DD,0x72DF,0x72E2,0x72E3,0x72E4,0x72E5,0x72E6,/* 0x40-0x47 */
+ 0x72E7,0x72EA,0x72EB,0x72F5,0x72F6,0x72F9,0x72FD,0x72FE,/* 0x48-0x4F */
+ 0x72FF,0x7300,0x7302,0x7304,0x7305,0x7306,0x7307,0x7308,/* 0x50-0x57 */
+ 0x7309,0x730B,0x730C,0x730D,0x730F,0x7310,0x7311,0x7312,/* 0x58-0x5F */
+ 0x7314,0x7318,0x7319,0x731A,0x731F,0x7320,0x7323,0x7324,/* 0x60-0x67 */
+ 0x7326,0x7327,0x7328,0x732D,0x732F,0x7330,0x7332,0x7333,/* 0x68-0x6F */
+ 0x7335,0x7336,0x733A,0x733B,0x733C,0x733D,0x7340,0x7341,/* 0x70-0x77 */
+ 0x7342,0x7343,0x7344,0x7345,0x7346,0x7347,0x7348,0x0000,/* 0x78-0x7F */
+
+ 0x7349,0x734A,0x734B,0x734C,0x734E,0x734F,0x7351,0x7353,/* 0x80-0x87 */
+ 0x7354,0x7355,0x7356,0x7358,0x7359,0x735A,0x735B,0x735C,/* 0x88-0x8F */
+ 0x735D,0x735E,0x735F,0x7361,0x7362,0x7363,0x7364,0x7365,/* 0x90-0x97 */
+ 0x7366,0x7367,0x7368,0x7369,0x736A,0x736B,0x736E,0x7370,/* 0x98-0x9F */
+ 0x7371,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xA0-0xA7 */
+};
+
+static wchar_t c2u_AB[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x7372,0x7373,0x7374,0xF9A7,0x7376,0x7377,0x7378,0x7379,/* 0x40-0x47 */
+ 0x737A,0x737B,0x737C,0x737D,0x737F,0x7380,0x7381,0x7382,/* 0x48-0x4F */
+ 0x7383,0x7385,0x7386,0x7388,0x738A,0x738C,0x738D,0x738F,/* 0x50-0x57 */
+ 0x7390,0x7392,0x7393,0x7394,0x7395,0x7397,0x7398,0x7399,/* 0x58-0x5F */
+ 0x739A,0x739C,0x739D,0x739E,0x73A0,0x73A1,0x73A3,0x73A4,/* 0x60-0x67 */
+ 0x73A5,0x73A6,0x73A7,0x73A8,0x73AA,0x73AC,0x73AD,0x73B1,/* 0x68-0x6F */
+ 0x73B4,0x73B5,0x73B6,0x73B8,0x73B9,0x73BC,0x73BD,0x73BE,/* 0x70-0x77 */
+ 0x73BF,0x73C1,0x73C3,0x73C4,0x73C5,0x73C6,0x73C7,0x0000,/* 0x78-0x7F */
+
+ 0x73CB,0x73CC,0x73CE,0x73D2,0x73D3,0x73D4,0x73D5,0x73D6,/* 0x80-0x87 */
+ 0x73D7,0x73D8,0x73DA,0x73DB,0x73DC,0x73DD,0x73DF,0x73E1,/* 0x88-0x8F */
+ 0x73E2,0x73E3,0x73E4,0x73E6,0x73E8,0x73EA,0x73EB,0x73EC,/* 0x90-0x97 */
+ 0x73EE,0x73EF,0x73F0,0x73F1,0x73F3,0x73F4,0x73F5,0x73F6,/* 0x98-0x9F */
+ 0x73F7,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xA0-0xA7 */
+};
+
+static wchar_t c2u_AC[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x73F8,0x73F9,0x73FA,0x73FB,0x73FC,0x73FD,0x73FE,0x73FF,/* 0x40-0x47 */
+ 0x7400,0x7401,0x7402,0x7404,0x7407,0x7408,0x740B,0x740C,/* 0x48-0x4F */
+ 0x740D,0x740E,0x7411,0x7412,0x7413,0x7414,0x7415,0x7416,/* 0x50-0x57 */
+ 0x7417,0x7418,0x7419,0x741C,0x741D,0x741E,0x741F,0x7420,/* 0x58-0x5F */
+ 0x7421,0x7423,0x7424,0x7427,0x7429,0x742B,0x742D,0x742F,/* 0x60-0x67 */
+ 0x7431,0x7432,0x7437,0x7438,0x7439,0x743A,0x743B,0x743D,/* 0x68-0x6F */
+ 0x743E,0x743F,0x7440,0x7442,0x7443,0x7444,0x7445,0x7446,/* 0x70-0x77 */
+ 0x7447,0x7448,0x7449,0x744A,0x744B,0x744C,0x744D,0x0000,/* 0x78-0x7F */
+
+ 0x744E,0x744F,0x7450,0x7451,0x7452,0x7453,0x7454,0x7456,/* 0x80-0x87 */
+ 0x7458,0x745D,0x7460,0x7461,0x7462,0x7463,0x7464,0x7465,/* 0x88-0x8F */
+ 0x7466,0x7467,0x7468,0xF9AE,0x746A,0x746B,0x746C,0x746E,/* 0x90-0x97 */
+ 0x746F,0x7471,0x7472,0x7473,0x7474,0x7475,0x7478,0x7479,/* 0x98-0x9F */
+ 0x747A,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xA0-0xA7 */
+};
+
+static wchar_t c2u_AD[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x747B,0x747C,0x747D,0x747F,0x7482,0x7484,0x7485,0x7486,/* 0x40-0x47 */
+ 0x7488,0xF994,0x748A,0x748C,0x748D,0x748F,0x7491,0x7492,/* 0x48-0x4F */
+ 0x7493,0x7494,0x7495,0x7496,0x7497,0xF9EF,0x7499,0x749A,/* 0x50-0x57 */
+ 0x749B,0x749D,0x749F,0x74A0,0x74A1,0x74A2,0x74A3,0x74A4,/* 0x58-0x5F */
+ 0x74A5,0x74A6,0x74AA,0x74AB,0x74AC,0x74AD,0x74AE,0x74AF,/* 0x60-0x67 */
+ 0x74B0,0x74B1,0x74B2,0x74B3,0x74B4,0x74B5,0x74B6,0x74B7,/* 0x68-0x6F */
+ 0x74B8,0x74B9,0x74BB,0x74BC,0x74BD,0x74BE,0x74BF,0x74C0,/* 0x70-0x77 */
+ 0x74C1,0x74C2,0x74C3,0x74C4,0x74C5,0x74C6,0x74C7,0x0000,/* 0x78-0x7F */
+
+ 0x74C8,0x74C9,0x74CA,0x74CB,0x74CC,0x74CD,0x74CE,0x74CF,/* 0x80-0x87 */
+ 0x74D0,0x74D1,0x74D3,0x74D4,0x74D5,0x74D6,0x74D7,0x74D8,/* 0x88-0x8F */
+ 0x74D9,0x74DA,0x74DB,0x74DD,0x74DF,0x74E1,0x74E5,0x74E7,/* 0x90-0x97 */
+ 0x74E8,0x74E9,0x74EA,0x74EB,0x74EC,0x74ED,0x74F0,0x74F1,/* 0x98-0x9F */
+ 0x74F2,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xA0-0xA7 */
+};
+
+static wchar_t c2u_AE[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x74F3,0x74F5,0x74F8,0x74F9,0x74FA,0x74FB,0x74FC,0x74FD,/* 0x40-0x47 */
+ 0x74FE,0x7500,0x7501,0x7502,0x7503,0x7505,0x7506,0x7507,/* 0x48-0x4F */
+ 0x7508,0x7509,0x750A,0x750B,0x750C,0x750E,0x7510,0x7512,/* 0x50-0x57 */
+ 0x7514,0x7515,0x7516,0x7517,0x751B,0x751D,0x751E,0x7520,/* 0x58-0x5F */
+ 0x7521,0x7522,0x7523,0x7524,0x7526,0x7527,0x752A,0x752E,/* 0x60-0x67 */
+ 0x7534,0x7536,0x7539,0x753C,0x753D,0x753F,0x7541,0x7542,/* 0x68-0x6F */
+ 0x7543,0x7544,0x7546,0x7547,0x7549,0x754A,0x754D,0x7550,/* 0x70-0x77 */
+ 0x7551,0x7552,0x7553,0x7555,0x7556,0x7557,0x7558,0x0000,/* 0x78-0x7F */
+
+ 0x755D,0x755E,0x755F,0x7560,0x7561,0x7562,0x7563,0x7564,/* 0x80-0x87 */
+ 0x7567,0x7568,0x7569,0x756B,0x756C,0x756D,0x756E,0x756F,/* 0x88-0x8F */
+ 0xF962,0x7571,0x7573,0x7575,0x7576,0x7577,0x757A,0x757B,/* 0x90-0x97 */
+ 0x757C,0x757D,0x757E,0x7580,0x7581,0x7582,0x7584,0x7585,/* 0x98-0x9F */
+ 0x7587,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xA0-0xA7 */
+};
+
+static wchar_t c2u_AF[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x7588,0x7589,0x758A,0x758C,0x758D,0x758E,0x7590,0x7593,/* 0x40-0x47 */
+ 0x7595,0x7598,0x759B,0x759C,0x759E,0x75A2,0x75A6,0x75A7,/* 0x48-0x4F */
+ 0x75A8,0x75A9,0x75AA,0x75AD,0x75B6,0x75B7,0x75BA,0x75BB,/* 0x50-0x57 */
+ 0x75BF,0x75C0,0x75C1,0x75C6,0x75CB,0x75CC,0x75CE,0x75CF,/* 0x58-0x5F */
+ 0x75D0,0x75D1,0x75D3,0x75D7,0x75D9,0x75DA,0x75DC,0x75DD,/* 0x60-0x67 */
+ 0x75DF,0x75E0,0x75E1,0x75E5,0x75E9,0x75EC,0x75ED,0x75EE,/* 0x68-0x6F */
+ 0x75EF,0x75F2,0x75F3,0x75F5,0x75F6,0x75F7,0x75F8,0x75FA,/* 0x70-0x77 */
+ 0x75FB,0x75FD,0x75FE,0x7602,0x7604,0x7606,0x7607,0x0000,/* 0x78-0x7F */
+
+ 0x7608,0x7609,0x760B,0x760D,0x760E,0x760F,0x7611,0x7612,/* 0x80-0x87 */
+ 0x7613,0x7614,0x7616,0x761A,0x761C,0x761D,0x761E,0x7621,/* 0x88-0x8F */
+ 0x7623,0x7627,0x7628,0x762C,0x762E,0x762F,0x7631,0x7632,/* 0x90-0x97 */
+ 0x7636,0x7637,0x7639,0x763A,0x763B,0x763D,0x7641,0xF9C1,/* 0x98-0x9F */
+ 0x7644,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xA0-0xA7 */
+};
+
+static wchar_t c2u_B0[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x7645,0x7646,0x7647,0x7648,0x7649,0x764A,0x764B,0x764E,/* 0x40-0x47 */
+ 0x764F,0x7650,0x7651,0x7652,0x7653,0x7655,0x7657,0x7658,/* 0x48-0x4F */
+ 0x7659,0x765A,0x765B,0x765D,0x765F,0x7660,0x7661,0x7662,/* 0x50-0x57 */
+ 0x7664,0x7665,0x7666,0x7667,0x7668,0xF90E,0x766A,0x766C,/* 0x58-0x5F */
+ 0x766D,0x766E,0x7670,0x7671,0x7672,0x7673,0x7674,0x7675,/* 0x60-0x67 */
+ 0x7676,0x7677,0x7679,0x767A,0x767C,0x767F,0x7680,0x7681,/* 0x68-0x6F */
+ 0x7683,0x7685,0x7689,0x768A,0x768C,0x768D,0x768F,0x7690,/* 0x70-0x77 */
+ 0x7692,0x7694,0x7695,0x7697,0x7698,0x769A,0x769B,0x0000,/* 0x78-0x7F */
+
+ 0x769C,0x769D,0x769E,0x769F,0x76A0,0x76A1,0x76A2,0x76A3,/* 0x80-0x87 */
+ 0x76A5,0x76A6,0x76A7,0x76A8,0x76A9,0x76AA,0x76AB,0x76AC,/* 0x88-0x8F */
+ 0x76AD,0x76AF,0x76B0,0x76B3,0x76B5,0x76B6,0x76B7,0x76B8,/* 0x90-0x97 */
+ 0x76B9,0x76BA,0x76BB,0x76BC,0x76BD,0x76BE,0x76C0,0x76C1,/* 0x98-0x9F */
+ 0x76C3,0x554A,0x963F,0x57C3,0x6328,0x54CE,0x5509,0x54C0,/* 0xA0-0xA7 */
+ 0x7691,0x764C,0x853C,0x77EE,0x827E,0x788D,0x7231,0x9698,/* 0xA8-0xAF */
+ 0x978D,0x6C28,0x5B89,0x4FFA,0x6309,0x6697,0x5CB8,0x80FA,/* 0xB0-0xB7 */
+ 0x6848,0x80AE,0x6602,0x76CE,0x51F9,0x6556,0x71AC,0x7FF1,/* 0xB8-0xBF */
+ 0x8884,0x50B2,0x5965,0x61CA,0x6FB3,0x82AD,0x634C,0x6252,/* 0xC0-0xC7 */
+ 0x53ED,0x5427,0x7B06,0x516B,0x75A4,0x5DF4,0x62D4,0x8DCB,/* 0xC8-0xCF */
+ 0x9776,0x628A,0x8019,0x575D,0x9738,0x7F62,0x7238,0x767D,/* 0xD0-0xD7 */
+ 0x67CF,0x767E,0x6446,0x4F70,0x8D25,0x62DC,0x7A17,0x6591,/* 0xD8-0xDF */
+ 0x73ED,0x642C,0x6273,0x822C,0x9881,0x677F,0x7248,0x626E,/* 0xE0-0xE7 */
+ 0x62CC,0x4F34,0x74E3,0x534A,0x529E,0x7ECA,0x90A6,0x5E2E,/* 0xE8-0xEF */
+ 0x6886,0x699C,0x8180,0x7ED1,0x68D2,0x78C5,0x868C,0x9551,/* 0xF0-0xF7 */
+ 0x508D,0x8C24,0x82DE,0x80DE,0x5305,0x8912,0x5265,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_B1[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x76C4,0x76C7,0x76C9,0x76CB,0x76CC,0x76D3,0x76D5,0x76D9,/* 0x40-0x47 */
+ 0x76DA,0x76DC,0x76DD,0x76DE,0x76E0,0x76E1,0x76E2,0x76E3,/* 0x48-0x4F */
+ 0x76E4,0x76E6,0xF933,0x76E8,0x76E9,0x76EA,0x76EB,0x76EC,/* 0x50-0x57 */
+ 0x76ED,0x76F0,0x76F3,0x76F5,0x76F6,0x76F7,0x76FA,0x76FB,/* 0x58-0x5F */
+ 0x76FD,0x76FF,0x7700,0x7702,0x7703,0x7705,0x7706,0x770A,/* 0x60-0x67 */
+ 0x770C,0x770E,0x770F,0x7710,0x7711,0x7712,0x7713,0x7714,/* 0x68-0x6F */
+ 0x7715,0x7716,0x7717,0x7718,0x771B,0x771C,0x771D,0x771E,/* 0x70-0x77 */
+ 0x7721,0x7723,0x7724,0x7725,0x7727,0x772A,0x772B,0x0000,/* 0x78-0x7F */
+
+ 0x772C,0x772E,0x7730,0x7731,0x7732,0x7733,0x7734,0x7739,/* 0x80-0x87 */
+ 0x773B,0x773D,0x773E,0x773F,0x7742,0x7744,0x7745,0x7746,/* 0x88-0x8F */
+ 0x7748,0x7749,0x774A,0x774B,0x774C,0x774D,0x774E,0x774F,/* 0x90-0x97 */
+ 0x7752,0x7753,0x7754,0x7755,0x7756,0x7757,0x7758,0x7759,/* 0x98-0x9F */
+ 0x775C,0x8584,0x96F9,0x4FDD,0x5821,0x9971,0x5B9D,0x62B1,/* 0xA0-0xA7 */
+ 0x62A5,0xFA06,0x8C79,0x9C8D,0x7206,0x676F,0x7891,0x60B2,/* 0xA8-0xAF */
+ 0x5351,0xF963,0x8F88,0x80CC,0x8D1D,0x94A1,0x500D,0x72C8,/* 0xB0-0xB7 */
+ 0x5907,0x60EB,0x7119,0x88AB,0x5954,0x82EF,0x672C,0x7B28,/* 0xB8-0xBF */
+ 0x5D29,0x7EF7,0x752D,0x6CF5,0x8E66,0x8FF8,0x903C,0x9F3B,/* 0xC0-0xC7 */
+ 0x6BD4,0x9119,0x7B14,0x5F7C,0x78A7,0x84D6,0x853D,0x6BD5,/* 0xC8-0xCF */
+ 0x6BD9,0x6BD6,0x5E01,0x5E87,0x75F9,0x95ED,0x655D,0x5F0A,/* 0xD0-0xD7 */
+ 0x5FC5,0x8F9F,0x58C1,0x81C2,0x907F,0x965B,0x97AD,0x8FB9,/* 0xD8-0xDF */
+ 0x7F16,0x8D2C,0x6241,0xF965,0x53D8,0x535E,0x8FA8,0x8FA9,/* 0xE0-0xE7 */
+ 0x8FAB,0x904D,0x6807,0x5F6A,0x8198,0x8868,0x9CD6,0x618B,/* 0xE8-0xEF */
+ 0x522B,0x762A,0x5F6C,0x658C,0x6FD2,0x6EE8,0x5BBE,0x6448,/* 0xF0-0xF7 */
+ 0x5175,0x51B0,0x67C4,0x4E19,0x79C9,0x997C,0x70B3,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_B2[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x775D,0x775E,0x775F,0x7760,0x7764,0x7767,0x7769,0x776A,/* 0x40-0x47 */
+ 0x776D,0x776E,0x776F,0x7770,0x7771,0x7772,0x7773,0x7774,/* 0x48-0x4F */
+ 0x7775,0x7776,0x7777,0x7778,0x777A,0x777B,0x777C,0x7781,/* 0x50-0x57 */
+ 0x7782,0x7783,0x7786,0x7787,0x7788,0x7789,0x778A,0x778B,/* 0x58-0x5F */
+ 0x778F,0x7790,0x7793,0x7794,0x7795,0x7796,0x7797,0x7798,/* 0x60-0x67 */
+ 0x7799,0x779A,0x779B,0x779C,0x779D,0x779E,0x77A1,0x77A3,/* 0x68-0x6F */
+ 0x77A4,0x77A6,0x77A8,0x77AB,0x77AD,0x77AE,0x77AF,0x77B1,/* 0x70-0x77 */
+ 0x77B2,0x77B4,0x77B6,0x77B7,0x77B8,0x77B9,0x77BA,0x0000,/* 0x78-0x7F */
+
+ 0x77BC,0x77BE,0x77C0,0x77C1,0x77C2,0x77C3,0x77C4,0x77C5,/* 0x80-0x87 */
+ 0x77C6,0x77C7,0x77C8,0x77C9,0x77CA,0x77CB,0x77CC,0x77CE,/* 0x88-0x8F */
+ 0x77CF,0x77D0,0x77D1,0x77D2,0x77D3,0x77D4,0x77D5,0x77D6,/* 0x90-0x97 */
+ 0x77D8,0x77D9,0x77DA,0x77DD,0x77DE,0x77DF,0x77E0,0x77E1,/* 0x98-0x9F */
+ 0x77E4,0x75C5,0x5E76,0x73BB,0x83E0,0x64AD,0x62E8,0x94B5,/* 0xA0-0xA7 */
+ 0x6CE2,0x535A,0x52C3,0x640F,0x94C2,0x7B94,0x4F2F,0x5E1B,/* 0xA8-0xAF */
+ 0x8236,0x8116,0x818A,0x6E24,0x6CCA,0x9A73,0x6355,0x535C,/* 0xB0-0xB7 */
+ 0x54FA,0x8865,0x57E0,0xF967,0x5E03,0x6B65,0x7C3F,0x90E8,/* 0xB8-0xBF */
+ 0x6016,0x64E6,0x731C,0x88C1,0x6750,0x624D,0x8D22,0x776C,/* 0xC0-0xC7 */
+ 0x8E29,0x91C7,0x5F69,0x83DC,0x8521,0x9910,0x53C2,0x8695,/* 0xC8-0xCF */
+ 0x6B8B,0x60ED,0x60E8,0x707F,0x82CD,0x8231,0x4ED3,0x6CA7,/* 0xD0-0xD7 */
+ 0x85CF,0x64CD,0x7CD9,0x69FD,0x66F9,0x8349,0x5395,0x7B56,/* 0xD8-0xDF */
+ 0x4FA7,0x518C,0x6D4B,0x5C42,0x8E6D,0x63D2,0x53C9,0x832C,/* 0xE0-0xE7 */
+ 0xF9FE,0x67E5,0x78B4,0x643D,0x5BDF,0x5C94,0x5DEE,0x8BE7,/* 0xE8-0xEF */
+ 0x62C6,0x67F4,0x8C7A,0x6400,0x63BA,0x8749,0x998B,0x8C17,/* 0xF0-0xF7 */
+ 0x7F20,0x94F2,0x4EA7,0x9610,0x98A4,0x660C,0x7316,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_B3[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x77E6,0x77E8,0x77EA,0x77EF,0x77F0,0x77F1,0x77F2,0x77F4,/* 0x40-0x47 */
+ 0x77F5,0x77F7,0x77F9,0x77FA,0x77FB,0x77FC,0x7803,0x7804,/* 0x48-0x4F */
+ 0x7805,0x7806,0x7807,0x7808,0x780A,0x780B,0x780E,0x780F,/* 0x50-0x57 */
+ 0x7810,0x7813,0x7815,0x7819,0x781B,0x781E,0x7820,0x7821,/* 0x58-0x5F */
+ 0x7822,0x7824,0x7828,0x782A,0x782B,0x782E,0x782F,0x7831,/* 0x60-0x67 */
+ 0x7832,0x7833,0x7835,0x7836,0x783D,0x783F,0x7841,0x7842,/* 0x68-0x6F */
+ 0x7843,0x7844,0x7846,0x7848,0x7849,0x784A,0x784B,0x784D,/* 0x70-0x77 */
+ 0x784F,0x7851,0x7853,0x7854,0x7858,0x7859,0x785A,0x0000,/* 0x78-0x7F */
+
+ 0x785B,0x785C,0x785E,0x785F,0x7860,0x7861,0x7862,0x7863,/* 0x80-0x87 */
+ 0x7864,0x7865,0x7866,0x7867,0x7868,0x7869,0x786F,0x7870,/* 0x88-0x8F */
+ 0x7871,0x7872,0x7873,0x7874,0x7875,0x7876,0x7878,0x7879,/* 0x90-0x97 */
+ 0x787A,0x787B,0x787D,0x787E,0x787F,0x7880,0x7881,0x7882,/* 0x98-0x9F */
+ 0x7883,0x573A,0x5C1D,0x5E38,0x957F,0x507F,0x80A0,0x5382,/* 0xA0-0xA7 */
+ 0x655E,0x7545,0x5531,0x5021,0x8D85,0x6284,0x949E,0x671D,/* 0xA8-0xAF */
+ 0x5632,0x6F6E,0x5DE2,0x5435,0x7092,0x8F66,0x626F,0x64A4,/* 0xB0-0xB7 */
+ 0x63A3,0x5F7B,0x6F88,0x90F4,0x81E3,0xF971,0x5C18,0x6668,/* 0xB8-0xBF */
+ 0x5FF1,0x6C89,0x9648,0x8D81,0x886C,0x6491,0x79F0,0x57CE,/* 0xC0-0xC7 */
+ 0x6A59,0x6210,0x5448,0x4E58,0x7A0B,0x60E9,0x6F84,0x8BDA,/* 0xC8-0xCF */
+ 0x627F,0x901E,0x9A8B,0x79E4,0x5403,0x75F4,0x6301,0x5319,/* 0xD0-0xD7 */
+ 0x6C60,0x8FDF,0x5F1B,0x9A70,0x803B,0x9F7F,0x4F88,0x5C3A,/* 0xD8-0xDF */
+ 0x8D64,0x7FC5,0x65A5,0x70BD,0x5145,0x51B2,0x866B,0x5D07,/* 0xE0-0xE7 */
+ 0x5BA0,0x62BD,0x916C,0x7574,0x8E0C,0x7A20,0x6101,0x7B79,/* 0xE8-0xEF */
+ 0x4EC7,0x7EF8,0x7785,0x4E11,0x81ED,0x521D,0x51FA,0x6A71,/* 0xF0-0xF7 */
+ 0x53A8,0x8E87,0x9504,0x96CF,0x6EC1,0x9664,0x695A,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_B4[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x7884,0x7885,0x7886,0x7888,0x788A,0x788B,0x788F,0x7890,/* 0x40-0x47 */
+ 0x7892,0x7894,0x7895,0x7896,0x7899,0x789D,0x789E,0x78A0,/* 0x48-0x4F */
+ 0x78A2,0x78A4,0x78A6,0x78A8,0x78A9,0x78AA,0x78AB,0x78AC,/* 0x50-0x57 */
+ 0x78AD,0x78AE,0x78AF,0x78B5,0x78B6,0x78B7,0x78B8,0x78BA,/* 0x58-0x5F */
+ 0x78BB,0x78BC,0x78BD,0x78BF,0x78C0,0x78C2,0x78C3,0x78C4,/* 0x60-0x67 */
+ 0x78C6,0x78C7,0x78C8,0x78CC,0x78CD,0x78CE,0x78CF,0x78D1,/* 0x68-0x6F */
+ 0x78D2,0x78D3,0x78D6,0x78D7,0x78D8,0x78DA,0x78DB,0x78DC,/* 0x70-0x77 */
+ 0x78DD,0x78DE,0x78DF,0x78E0,0x78E1,0x78E2,0x78E3,0x0000,/* 0x78-0x7F */
+
+ 0x78E4,0x78E5,0x78E6,0x78E7,0x78E9,0x78EA,0x78EB,0x78ED,/* 0x80-0x87 */
+ 0x78EE,0x78EF,0x78F0,0x78F1,0x78F3,0x78F5,0x78F6,0x78F8,/* 0x88-0x8F */
+ 0x78F9,0xF964,0x78FC,0x78FD,0x78FE,0x78FF,0x7900,0x7902,/* 0x90-0x97 */
+ 0x7903,0x7904,0x7906,0x7907,0x7908,0x7909,0x790A,0x790B,/* 0x98-0x9F */
+ 0x790C,0x7840,0x50A8,0x77D7,0x6410,0x89E6,0x5904,0x63E3,/* 0xA0-0xA7 */
+ 0x5DDD,0x7A7F,0x693D,0x4F20,0x8239,0x5598,0xF905,0x75AE,/* 0xA8-0xAF */
+ 0x7A97,0x5E62,0x5E8A,0x95EF,0x521B,0x5439,0x708A,0x6376,/* 0xB0-0xB7 */
+ 0x9524,0x5782,0x6625,0x693F,0x9187,0x5507,0x6DF3,0x7EAF,/* 0xB8-0xBF */
+ 0x8822,0x6233,0x7EF0,0x75B5,0x8328,0x78C1,0x96CC,0x8F9E,/* 0xC0-0xC7 */
+ 0x6148,0x74F7,0x8BCD,0x6B64,0xF9FF,0x8D50,0x6B21,0x806A,/* 0xC8-0xCF */
+ 0x8471,0x56F1,0x5306,0x4ECE,0x4E1B,0x51D1,0x7C97,0x918B,/* 0xD0-0xD7 */
+ 0x7C07,0x4FC3,0x8E7F,0x7BE1,0x7A9C,0x6467,0x5D14,0x50AC,/* 0xD8-0xDF */
+ 0x8106,0x7601,0x7CB9,0x6DEC,0x7FE0,0x6751,0x5B58,0x5BF8,/* 0xE0-0xE7 */
+ 0x78CB,0x64AE,0x6413,0x63AA,0x632B,0x9519,0x642D,0x8FBE,/* 0xE8-0xEF */
+ 0x7B54,0x7629,0x6253,0x5927,0x5446,0x6B79,0x50A3,0x6234,/* 0xF0-0xF7 */
+ 0x5E26,0x6B86,0x4EE3,0x8D37,0x888B,0x5F85,0x902E,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_B5[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x790D,0x790E,0x790F,0x7910,0x7911,0x7912,0x7914,0x7915,/* 0x40-0x47 */
+ 0x7916,0x7917,0x7918,0x7919,0x791A,0x791B,0x791C,0x791D,/* 0x48-0x4F */
+ 0x791F,0x7920,0x7921,0x7922,0x7923,0x7925,0x7926,0x7927,/* 0x50-0x57 */
+ 0x7928,0x7929,0xF985,0x792B,0x792C,0x792D,0x792E,0x792F,/* 0x58-0x5F */
+ 0x7930,0x7931,0x7932,0x7933,0x7935,0x7936,0x7937,0x7938,/* 0x60-0x67 */
+ 0x7939,0x793D,0x793F,0x7942,0x7943,0x7944,0x7945,0x7947,/* 0x68-0x6F */
+ 0x794A,0x794B,0x794C,0x794D,0x794E,0x794F,0x7950,0x7951,/* 0x70-0x77 */
+ 0x7952,0x7954,0x7955,0x7958,0x7959,0x7961,0x7963,0x0000,/* 0x78-0x7F */
+
+ 0x7964,0x7966,0x7969,0x796A,0x796B,0x796C,0x796E,0x7970,/* 0x80-0x87 */
+ 0x7971,0x7972,0x7973,0x7974,0x7975,0x7976,0x7979,0x797B,/* 0x88-0x8F */
+ 0x797C,0x797D,0x797E,0xF93C,0x7982,0x7983,0x7986,0x7987,/* 0x90-0x97 */
+ 0x7988,0x7989,0x798B,0x798C,0x798D,0x798E,0x7990,0x7991,/* 0x98-0x9F */
+ 0x7992,0x6020,0x803D,0x62C5,0xF95E,0x5355,0x90F8,0x63B8,/* 0xA0-0xA7 */
+ 0x80C6,0x65E6,0x6C2E,0x4F46,0x60EE,0x6DE1,0x8BDE,0x5F39,/* 0xA8-0xAF */
+ 0x86CB,0x5F53,0x6321,0x515A,0x8361,0x6863,0x5200,0x6363,/* 0xB0-0xB7 */
+ 0x8E48,0x5012,0x5C9B,0x7977,0x5BFC,0x5230,0x7A3B,0x60BC,/* 0xB8-0xBF */
+ 0x9053,0x76D7,0x5FB7,0x5F97,0x7684,0x8E6C,0x706F,0x767B,/* 0xC0-0xC7 */
+ 0x7B49,0x77AA,0x51F3,0x9093,0x5824,0x4F4E,0x6EF4,0x8FEA,/* 0xC8-0xCF */
+ 0x654C,0x7B1B,0x72C4,0x6DA4,0x7FDF,0x5AE1,0x62B5,0x5E95,/* 0xD0-0xD7 */
+ 0x5730,0x8482,0x7B2C,0x5E1D,0x5F1F,0x9012,0x7F14,0x98A0,/* 0xD8-0xDF */
+ 0x6382,0x6EC7,0x7898,0x70B9,0x5178,0x975B,0x57AB,0x7535,/* 0xE0-0xE7 */
+ 0x4F43,0x7538,0x5E97,0x60E6,0x5960,0x6DC0,0x6BBF,0x7889,/* 0xE8-0xEF */
+ 0x53FC,0x96D5,0x51CB,0x5201,0x6389,0x540A,0x9493,0x8C03,/* 0xF0-0xF7 */
+ 0x8DCC,0x7239,0x789F,0x8776,0x8FED,0x8C0D,0x53E0,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_B6[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x7993,0x7994,0x7995,0x7996,0x7997,0x7998,0x7999,0x799B,/* 0x40-0x47 */
+ 0x799C,0x799D,0x799E,0x799F,0x79A0,0x79A1,0x79A2,0x79A3,/* 0x48-0x4F */
+ 0x79A4,0x79A5,0x79A6,0x79A8,0x79A9,0x79AA,0x79AB,0x79AC,/* 0x50-0x57 */
+ 0x79AD,0xF9B6,0x79AF,0x79B0,0x79B1,0x79B2,0x79B4,0x79B5,/* 0x58-0x5F */
+ 0x79B6,0x79B7,0x79B8,0x79BC,0x79BF,0x79C2,0x79C4,0x79C5,/* 0x60-0x67 */
+ 0x79C7,0x79C8,0x79CA,0x79CC,0x79CE,0x79CF,0x79D0,0x79D3,/* 0x68-0x6F */
+ 0x79D4,0x79D6,0x79D7,0x79D9,0x79DA,0x79DB,0x79DC,0x79DD,/* 0x70-0x77 */
+ 0x79DE,0x79E0,0x79E1,0x79E2,0x79E5,0x79E8,0x79EA,0x0000,/* 0x78-0x7F */
+
+ 0x79EC,0x79EE,0x79F1,0x79F2,0x79F3,0x79F4,0x79F5,0x79F6,/* 0x80-0x87 */
+ 0x79F7,0x79F9,0x79FA,0x79FC,0x79FE,0x79FF,0x7A01,0x7A04,/* 0x88-0x8F */
+ 0x7A05,0x7A07,0x7A08,0x7A09,0x7A0A,0x7A0C,0x7A0F,0x7A10,/* 0x90-0x97 */
+ 0x7A11,0x7A12,0x7A13,0x7A15,0x7A16,0x7A18,0x7A19,0x7A1B,/* 0x98-0x9F */
+ 0xF956,0x4E01,0x76EF,0x53EE,0x9489,0x9876,0x9F0E,0x952D,/* 0xA0-0xA7 */
+ 0x5B9A,0x8BA2,0x4E22,0x4E1C,0x51AC,0x8463,0x61C2,0x52A8,/* 0xA8-0xAF */
+ 0x680B,0x4F97,0x606B,0x51BB,0xFA05,0x515C,0x6296,0x6597,/* 0xB0-0xB7 */
+ 0x9661,0x8C46,0x9017,0x75D8,0xFA26,0x7763,0x6BD2,0x728A,/* 0xB8-0xBF */
+ 0x72EC,0x8BFB,0x5835,0x7779,0x8D4C,0x675C,0x9540,0x809A,/* 0xC0-0xC7 */
+ 0xFA01,0x6E21,0x5992,0x7AEF,0x77ED,0x953B,0x6BB5,0x65AD,/* 0xC8-0xCF */
+ 0x7F0E,0x5806,0x5151,0x961F,0x5BF9,0x58A9,0x5428,0x8E72,/* 0xD0-0xD7 */
+ 0x6566,0x987F,0x56E4,0x949D,0x76FE,0x9041,0x6387,0x54C6,/* 0xD8-0xDF */
+ 0x591A,0x593A,0x579B,0x8EB2,0x6735,0x8DFA,0x8235,0x5241,/* 0xE0-0xE7 */
+ 0x60F0,0x5815,0x86FE,0x5CE8,0x9E45,0x4FC4,0x989D,0x8BB9,/* 0xE8-0xEF */
+ 0x5A25,0x6076,0x5384,0x627C,0x904F,0x9102,0x997F,0x6069,/* 0xF0-0xF7 */
+ 0x800C,0x513F,0x8033,0x5C14,0x9975,0x6D31,0x4E8C,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_B7[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x7A1D,0x7A1F,0x7A21,0x7A22,0x7A24,0x7A25,0x7A26,0x7A27,/* 0x40-0x47 */
+ 0x7A28,0x7A29,0x7A2A,0x7A2B,0x7A2C,0x7A2D,0x7A2E,0x7A2F,/* 0x48-0x4F */
+ 0x7A30,0x7A31,0x7A32,0x7A34,0x7A35,0x7A36,0x7A38,0x7A3A,/* 0x50-0x57 */
+ 0x7A3E,0x7A40,0x7A41,0x7A42,0x7A43,0x7A44,0x7A45,0x7A47,/* 0x58-0x5F */
+ 0x7A48,0x7A49,0x7A4A,0x7A4B,0x7A4C,0x7A4D,0x7A4E,0x7A4F,/* 0x60-0x67 */
+ 0x7A50,0x7A52,0x7A53,0x7A54,0x7A55,0x7A56,0x7A58,0x7A59,/* 0x68-0x6F */
+ 0x7A5A,0x7A5B,0x7A5C,0x7A5D,0x7A5E,0x7A5F,0x7A60,0x7A61,/* 0x70-0x77 */
+ 0x7A62,0x7A63,0x7A64,0x7A65,0x7A66,0x7A67,0x7A68,0x0000,/* 0x78-0x7F */
+
+ 0x7A69,0x7A6A,0x7A6B,0x7A6C,0x7A6D,0x7A6E,0x7A6F,0x7A71,/* 0x80-0x87 */
+ 0x7A72,0x7A73,0x7A75,0x7A7B,0x7A7C,0x7A7D,0x7A7E,0x7A82,/* 0x88-0x8F */
+ 0x7A85,0x7A87,0x7A89,0x7A8A,0x7A8B,0x7A8C,0x7A8E,0x7A8F,/* 0x90-0x97 */
+ 0x7A90,0x7A93,0x7A94,0x7A99,0x7A9A,0x7A9B,0x7A9E,0x7AA1,/* 0x98-0x9F */
+ 0x7AA2,0x8D30,0x53D1,0x7F5A,0x7B4F,0x4F10,0x4E4F,0x9600,/* 0xA0-0xA7 */
+ 0x6CD5,0x73D0,0x85E9,0x5E06,0x756A,0x7FFB,0x6A0A,0x77FE,/* 0xA8-0xAF */
+ 0x9492,0x7E41,0x51E1,0x70E6,0x53CD,0x8FD4,0x8303,0x8D29,/* 0xB0-0xB7 */
+ 0x72AF,0x996D,0x6CDB,0x574A,0x82B3,0x65B9,0x80AA,0x623F,/* 0xB8-0xBF */
+ 0x9632,0x59A8,0x4EFF,0x8BBF,0x7EBA,0x653E,0x83F2,0x975E,/* 0xC0-0xC7 */
+ 0x5561,0x98DE,0x80A5,0x532A,0x8BFD,0x5420,0x80BA,0x5E9F,/* 0xC8-0xCF */
+ 0x6CB8,0x8D39,0x82AC,0x915A,0x5429,0x6C1B,0x5206,0x7EB7,/* 0xD0-0xD7 */
+ 0x575F,0x711A,0x6C7E,0x7C89,0x594B,0x4EFD,0x5FFF,0x6124,/* 0xD8-0xDF */
+ 0x7CAA,0x4E30,0x5C01,0x67AB,0x8702,0x5CF0,0x950B,0x98CE,/* 0xE0-0xE7 */
+ 0x75AF,0x70FD,0x9022,0x51AF,0x7F1D,0x8BBD,0x5949,0x51E4,/* 0xE8-0xEF */
+ 0x4F5B,0x5426,0x592B,0x6577,0x80A4,0x5B75,0x6276,0x62C2,/* 0xF0-0xF7 */
+ 0x8F90,0x5E45,0x6C1F,0x7B26,0x4F0F,0x4FD8,0x670D,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_B8[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x7AA3,0x7AA4,0x7AA7,0x7AA9,0x7AAA,0x7AAB,0x7AAE,0x7AAF,/* 0x40-0x47 */
+ 0x7AB0,0x7AB1,0x7AB2,0x7AB4,0x7AB5,0x7AB6,0x7AB7,0x7AB8,/* 0x48-0x4F */
+ 0x7AB9,0x7ABA,0x7ABB,0x7ABC,0x7ABD,0x7ABE,0x7AC0,0x7AC1,/* 0x50-0x57 */
+ 0x7AC2,0x7AC3,0x7AC4,0x7AC5,0x7AC6,0x7AC7,0x7AC8,0x7AC9,/* 0x58-0x5F */
+ 0x7ACA,0x7ACC,0x7ACD,0x7ACE,0x7ACF,0x7AD0,0x7AD1,0x7AD2,/* 0x60-0x67 */
+ 0x7AD3,0x7AD4,0x7AD5,0x7AD7,0x7AD8,0x7ADA,0x7ADB,0x7ADC,/* 0x68-0x6F */
+ 0x7ADD,0x7AE1,0x7AE2,0x7AE4,0x7AE7,0x7AE8,0x7AE9,0x7AEA,/* 0x70-0x77 */
+ 0x7AEB,0x7AEC,0x7AEE,0x7AF0,0x7AF1,0x7AF2,0x7AF3,0x0000,/* 0x78-0x7F */
+
+ 0x7AF4,0x7AF5,0x7AF6,0x7AF7,0x7AF8,0x7AFB,0x7AFC,0x7AFE,/* 0x80-0x87 */
+ 0x7B00,0x7B01,0x7B02,0x7B05,0x7B07,0x7B09,0x7B0C,0x7B0D,/* 0x88-0x8F */
+ 0x7B0E,0x7B10,0x7B12,0x7B13,0x7B16,0x7B17,0x7B18,0x7B1A,/* 0x90-0x97 */
+ 0x7B1C,0x7B1D,0x7B1F,0x7B21,0x7B22,0x7B23,0x7B27,0x7B29,/* 0x98-0x9F */
+ 0x7B2D,0x6D6E,0x6DAA,0xFA1B,0x88B1,0x5F17,0x752B,0x629A,/* 0xA0-0xA7 */
+ 0x8F85,0x4FEF,0x91DC,0x65A7,0x812F,0x8151,0x5E9C,0x8150,/* 0xA8-0xAF */
+ 0x8D74,0x526F,0x8986,0x8D4B,0x590D,0x5085,0x4ED8,0x961C,/* 0xB0-0xB7 */
+ 0x7236,0x8179,0x8D1F,0x5BCC,0x8BA3,0x9644,0x5987,0x7F1A,/* 0xB8-0xBF */
+ 0x5490,0x5676,0x560E,0x8BE5,0x6539,0x6982,0x9499,0x76D6,/* 0xC0-0xC7 */
+ 0x6E89,0x5E72,0x7518,0x6746,0x67D1,0x7AFF,0x809D,0x8D76,/* 0xC8-0xCF */
+ 0x611F,0x79C6,0x6562,0x8D63,0x5188,0x521A,0x94A2,0x7F38,/* 0xD0-0xD7 */
+ 0x809B,0x7EB2,0x5C97,0x6E2F,0x6760,0x7BD9,0x768B,0x9AD8,/* 0xD8-0xDF */
+ 0x818F,0x7F94,0x7CD5,0x641E,0x9550,0x7A3F,0x544A,0x54E5,/* 0xE0-0xE7 */
+ 0x6B4C,0x6401,0x6208,0x9E3D,0x80F3,0x7599,0x5272,0x9769,/* 0xE8-0xEF */
+ 0x845B,0x683C,0x86E4,0x9601,0x9694,0x94EC,0x4E2A,0x5404,/* 0xF0-0xF7 */
+ 0x7ED9,0x6839,0x8DDF,0x8015,0xF901,0x5E9A,0x7FB9,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_B9[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x7B2F,0x7B30,0x7B32,0x7B34,0x7B35,0x7B36,0x7B37,0x7B39,/* 0x40-0x47 */
+ 0x7B3B,0x7B3D,0x7B3F,0x7B40,0x7B41,0x7B42,0x7B43,0x7B44,/* 0x48-0x4F */
+ 0x7B46,0x7B48,0x7B4A,0x7B4D,0x7B4E,0x7B53,0x7B55,0x7B57,/* 0x50-0x57 */
+ 0x7B59,0x7B5C,0x7B5E,0x7B5F,0x7B61,0x7B63,0x7B64,0x7B65,/* 0x58-0x5F */
+ 0x7B66,0x7B67,0x7B68,0x7B69,0x7B6A,0x7B6B,0x7B6C,0x7B6D,/* 0x60-0x67 */
+ 0x7B6F,0x7B70,0x7B73,0x7B74,0x7B76,0x7B78,0x7B7A,0x7B7C,/* 0x68-0x6F */
+ 0x7B7D,0x7B7F,0x7B81,0x7B82,0x7B83,0x7B84,0x7B86,0x7B87,/* 0x70-0x77 */
+ 0x7B88,0x7B89,0x7B8A,0x7B8B,0x7B8C,0x7B8E,0x7B8F,0x0000,/* 0x78-0x7F */
+
+ 0x7B91,0x7B92,0x7B93,0x7B96,0x7B98,0x7B99,0x7B9A,0x7B9B,/* 0x80-0x87 */
+ 0x7B9E,0x7B9F,0x7BA0,0x7BA3,0x7BA4,0x7BA5,0x7BAE,0x7BAF,/* 0x88-0x8F */
+ 0x7BB0,0x7BB2,0x7BB3,0x7BB5,0x7BB6,0x7BB7,0x7BB9,0x7BBA,/* 0x90-0x97 */
+ 0x7BBB,0x7BBC,0x7BBD,0x7BBE,0x7BBF,0x7BC0,0x7BC2,0x7BC3,/* 0x98-0x9F */
+ 0x7BC4,0x57C2,0x803F,0x6897,0x5DE5,0x653B,0x529F,0x606D,/* 0xA0-0xA7 */
+ 0x9F9A,0x4F9B,0x8EAC,0x516C,0x5BAB,0x5F13,0x5DE9,0x6C5E,/* 0xA8-0xAF */
+ 0x62F1,0x8D21,0x5171,0x94A9,0x52FE,0x6C9F,0x82DF,0x72D7,/* 0xB0-0xB7 */
+ 0x57A2,0x6784,0x8D2D,0x591F,0x8F9C,0x83C7,0x5495,0x7B8D,/* 0xB8-0xBF */
+ 0x4F30,0x6CBD,0x5B64,0x59D1,0x9F13,0x53E4,0x86CA,0x9AA8,/* 0xC0-0xC7 */
+ 0x8C37,0x80A1,0x6545,0x987E,0x56FA,0x96C7,0x522E,0x74DC,/* 0xC8-0xCF */
+ 0x5250,0x5BE1,0x6302,0x8902,0x4E56,0x62D0,0x602A,0x68FA,/* 0xD0-0xD7 */
+ 0x5173,0x5B98,0x51A0,0x89C2,0x7BA1,0x9986,0x7F50,0x60EF,/* 0xD8-0xDF */
+ 0x704C,0x8D2F,0x5149,0x5E7F,0x901B,0x7470,0x89C4,0x572D,/* 0xE0-0xE7 */
+ 0x7845,0x5F52,0x9F9F,0x95FA,0x8F68,0x9B3C,0x8BE1,0x7678,/* 0xE8-0xEF */
+ 0x6842,0x67DC,0x8DEA,0x8D35,0x523D,0x8F8A,0x6EDA,0x68CD,/* 0xF0-0xF7 */
+ 0x9505,0x90ED,0x56FD,0x679C,0x88F9,0x8FC7,0x54C8,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_BA[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x7BC5,0x7BC8,0x7BC9,0x7BCA,0x7BCB,0x7BCD,0x7BCE,0x7BCF,/* 0x40-0x47 */
+ 0x7BD0,0x7BD2,0x7BD4,0x7BD5,0x7BD6,0x7BD7,0x7BD8,0x7BDB,/* 0x48-0x4F */
+ 0x7BDC,0x7BDE,0x7BDF,0x7BE0,0x7BE2,0x7BE3,0x7BE4,0x7BE7,/* 0x50-0x57 */
+ 0x7BE8,0x7BE9,0x7BEB,0x7BEC,0x7BED,0x7BEF,0x7BF0,0x7BF2,/* 0x58-0x5F */
+ 0x7BF3,0x7BF4,0x7BF5,0x7BF6,0x7BF8,0x7BF9,0x7BFA,0x7BFB,/* 0x60-0x67 */
+ 0x7BFD,0x7BFF,0x7C00,0x7C01,0x7C02,0x7C03,0x7C04,0x7C05,/* 0x68-0x6F */
+ 0x7C06,0x7C08,0x7C09,0x7C0A,0x7C0D,0x7C0E,0x7C10,0x7C11,/* 0x70-0x77 */
+ 0x7C12,0x7C13,0x7C14,0x7C15,0x7C17,0x7C18,0x7C19,0x0000,/* 0x78-0x7F */
+
+ 0x7C1A,0x7C1B,0x7C1C,0x7C1D,0x7C1E,0x7C20,0x7C21,0x7C22,/* 0x80-0x87 */
+ 0x7C23,0x7C24,0x7C25,0x7C28,0x7C29,0x7C2B,0x7C2C,0x7C2D,/* 0x88-0x8F */
+ 0x7C2E,0x7C2F,0x7C30,0x7C31,0x7C32,0x7C33,0x7C34,0x7C35,/* 0x90-0x97 */
+ 0x7C36,0x7C37,0x7C39,0x7C3A,0x7C3B,0x7C3C,0x7C3D,0xF9A6,/* 0x98-0x9F */
+ 0x7C42,0x9AB8,0x5B69,0x6D77,0x6C26,0x4EA5,0x5BB3,0x9A87,/* 0xA0-0xA7 */
+ 0x9163,0x61A8,0x90AF,0x97E9,0x542B,0x6DB5,0x5BD2,0x51FD,/* 0xA8-0xAF */
+ 0x558A,0x7F55,0x7FF0,0x64BC,0x634D,0x65F1,0x61BE,0x608D,/* 0xB0-0xB7 */
+ 0x710A,0x6C57,0x6C49,0x592F,0x676D,0x822A,0x58D5,0x568E,/* 0xB8-0xBF */
+ 0x8C6A,0x6BEB,0x90DD,0x597D,0x8017,0x53F7,0x6D69,0x5475,/* 0xC0-0xC7 */
+ 0x559D,0x8377,0x83CF,0x6838,0x79BE,0x548C,0x4F55,0x5408,/* 0xC8-0xCF */
+ 0x76D2,0x8C89,0x9602,0x6CB3,0x6DB8,0x8D6B,0x8910,0x9E64,/* 0xD0-0xD7 */
+ 0x8D3A,0x563F,0x9ED1,0x75D5,0x5F88,0x72E0,0x6068,0x54FC,/* 0xD8-0xDF */
+ 0x4EA8,0x6A2A,0x8861,0x6052,0x8F70,0x54C4,0x70D8,0x8679,/* 0xE0-0xE7 */
+ 0x9E3F,0x6D2A,0x5B8F,0x5F18,0x7EA2,0x5589,0x4FAF,0x7334,/* 0xE8-0xEF */
+ 0x543C,0x539A,0x5019,0x540E,0x547C,0x4E4E,0x5FFD,0x745A,/* 0xF0-0xF7 */
+ 0x58F6,0x846B,0x80E1,0x8774,0x72D0,0x7CCA,0x6E56,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_BB[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x7C43,0x7C44,0x7C45,0x7C46,0x7C47,0x7C48,0x7C49,0x7C4A,/* 0x40-0x47 */
+ 0x7C4B,0x7C4C,0x7C4E,0x7C4F,0x7C50,0x7C51,0x7C52,0x7C53,/* 0x48-0x4F */
+ 0x7C54,0x7C55,0x7C56,0x7C57,0x7C58,0x7C59,0x7C5A,0x7C5B,/* 0x50-0x57 */
+ 0x7C5C,0x7C5D,0x7C5E,0x7C5F,0xF944,0x7C61,0x7C62,0x7C63,/* 0x58-0x5F */
+ 0x7C64,0x7C65,0x7C66,0x7C67,0x7C68,0x7C69,0x7C6A,0x7C6B,/* 0x60-0x67 */
+ 0x7C6C,0x7C6D,0x7C6E,0x7C6F,0x7C70,0x7C71,0x7C72,0x7C75,/* 0x68-0x6F */
+ 0x7C76,0x7C77,0x7C78,0x7C79,0x7C7A,0x7C7E,0x7C7F,0x7C80,/* 0x70-0x77 */
+ 0x7C81,0x7C82,0x7C83,0x7C84,0x7C85,0x7C86,0x7C87,0x0000,/* 0x78-0x7F */
+
+ 0x7C88,0x7C8A,0x7C8B,0x7C8C,0x7C8D,0x7C8E,0x7C8F,0x7C90,/* 0x80-0x87 */
+ 0x7C93,0x7C94,0x7C96,0x7C99,0x7C9A,0x7C9B,0x7CA0,0x7CA1,/* 0x88-0x8F */
+ 0x7CA3,0x7CA6,0x7CA7,0x7CA8,0x7CA9,0x7CAB,0x7CAC,0x7CAD,/* 0x90-0x97 */
+ 0x7CAF,0x7CB0,0x7CB4,0x7CB5,0x7CB6,0x7CB7,0x7CB8,0x7CBA,/* 0x98-0x9F */
+ 0x7CBB,0x5F27,0x864E,0x552C,0x62A4,0x4E92,0x6CAA,0x6237,/* 0xA0-0xA7 */
+ 0x82B1,0x54D7,0x534E,0x733E,0xF904,0x753B,0x5212,0x5316,/* 0xA8-0xAF */
+ 0x8BDD,0x69D0,0x5F8A,0x6000,0x6DEE,0x574F,0x6B22,0x73AF,/* 0xB0-0xB7 */
+ 0x6853,0x8FD8,0x7F13,0x6362,0x60A3,0x5524,0x75EA,0x8C62,/* 0xB8-0xBF */
+ 0x7115,0x6DA3,0x5BA6,0x5E7B,0x8352,0x614C,0x9EC4,0x78FA,/* 0xC0-0xC7 */
+ 0x8757,0x7C27,0x7687,0x51F0,0x60F6,0x714C,0x6643,0x5E4C,/* 0xC8-0xCF */
+ 0x604D,0x8C0E,0x7070,0x6325,0x8F89,0x5FBD,0x6062,0x86D4,/* 0xD0-0xD7 */
+ 0x56DE,0x6BC1,0x6094,0x6167,0x5349,0x60E0,0x6666,0x8D3F,/* 0xD8-0xDF */
+ 0x79FD,0x4F1A,0x70E9,0x6C47,0x8BB3,0x8BF2,0x7ED8,0x8364,/* 0xE0-0xE7 */
+ 0x660F,0x5A5A,0x9B42,0x6D51,0x6DF7,0x8C41,0x6D3B,0x4F19,/* 0xE8-0xEF */
+ 0x706B,0x83B7,0x6216,0x60D1,0x970D,0x8D27,0x7978,0x51FB,/* 0xF0-0xF7 */
+ 0x573E,0x57FA,0x673A,0x7578,0x7A3D,0x79EF,0x7B95,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_BC[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x7CBF,0x7CC0,0x7CC2,0x7CC3,0x7CC4,0x7CC6,0x7CC9,0x7CCB,/* 0x40-0x47 */
+ 0x7CCE,0x7CCF,0x7CD0,0x7CD1,0x7CD2,0x7CD3,0x7CD4,0x7CD8,/* 0x48-0x4F */
+ 0x7CDA,0x7CDB,0x7CDD,0x7CDE,0x7CE1,0x7CE2,0x7CE3,0x7CE4,/* 0x50-0x57 */
+ 0x7CE5,0x7CE6,0xF97B,0x7CE9,0x7CEA,0x7CEB,0x7CEC,0x7CED,/* 0x58-0x5F */
+ 0x7CEE,0x7CF0,0x7CF1,0x7CF2,0x7CF3,0x7CF4,0x7CF5,0x7CF6,/* 0x60-0x67 */
+ 0x7CF7,0x7CF9,0x7CFA,0x7CFC,0x7CFD,0x7CFE,0x7CFF,0x7D00,/* 0x68-0x6F */
+ 0x7D01,0x7D02,0x7D03,0x7D04,0x7D05,0x7D06,0x7D07,0x7D08,/* 0x70-0x77 */
+ 0x7D09,0x7D0B,0x7D0C,0x7D0D,0x7D0E,0x7D0F,0xF9CF,0x0000,/* 0x78-0x7F */
+
+ 0x7D11,0x7D12,0x7D13,0x7D14,0x7D15,0x7D16,0x7D17,0x7D18,/* 0x80-0x87 */
+ 0x7D19,0x7D1A,0x7D1B,0x7D1C,0x7D1D,0x7D1E,0x7D1F,0x7D21,/* 0x88-0x8F */
+ 0x7D23,0x7D24,0x7D25,0x7D26,0x7D28,0x7D29,0x7D2A,0x7D2C,/* 0x90-0x97 */
+ 0x7D2D,0x7D2E,0x7D30,0x7D31,0x7D32,0x7D33,0x7D34,0x7D35,/* 0x98-0x9F */
+ 0x7D36,0x808C,0x9965,0x8FF9,0x6FC0,0x8BA5,0x9E21,0x59EC,/* 0xA0-0xA7 */
+ 0x7EE9,0x7F09,0x5409,0x6781,0x68D8,0x8F91,0x7C4D,0x96C6,/* 0xA8-0xAF */
+ 0x53CA,0x6025,0x75BE,0x6C72,0x5373,0x5AC9,0x7EA7,0x6324,/* 0xB0-0xB7 */
+ 0x51E0,0x810A,0x5DF1,0x84DF,0x6280,0x5180,0x5B63,0x4F0E,/* 0xB8-0xBF */
+ 0x796D,0x5242,0x60B8,0x6D4E,0x5BC4,0x5BC2,0x8BA1,0x8BB0,/* 0xC0-0xC7 */
+ 0x65E2,0x5FCC,0x9645,0x5993,0x7EE7,0x7EAA,0x5609,0x67B7,/* 0xC8-0xCF */
+ 0x5939,0x4F73,0x5BB6,0x52A0,0x835A,0x988A,0x8D3E,0x7532,/* 0xD0-0xD7 */
+ 0x94BE,0x5047,0x7A3C,0x4EF7,0x67B6,0x9A7E,0x5AC1,0x6B7C,/* 0xD8-0xDF */
+ 0x76D1,0x575A,0x5C16,0x7B3A,0x95F4,0x714E,0x517C,0x80A9,/* 0xE0-0xE7 */
+ 0x8270,0x5978,0x7F04,0x8327,0x68C0,0x67EC,0x78B1,0x7877,/* 0xE8-0xEF */
+ 0x62E3,0x6361,0x7B80,0x4FED,0x526A,0x51CF,0x8350,0x69DB,/* 0xF0-0xF7 */
+ 0x9274,0x8DF5,0x8D31,0x89C1,0x952E,0x7BAD,0x4EF6,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_BD[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x7D37,0x7D38,0x7D39,0x7D3A,0x7D3B,0x7D3C,0x7D3D,0x7D3E,/* 0x40-0x47 */
+ 0x7D3F,0x7D40,0x7D41,0x7D42,0x7D43,0x7D44,0x7D45,0x7D46,/* 0x48-0x4F */
+ 0x7D47,0x7D48,0x7D49,0x7D4A,0x7D4B,0x7D4C,0x7D4D,0x7D4E,/* 0x50-0x57 */
+ 0x7D4F,0x7D50,0x7D51,0x7D52,0x7D53,0x7D54,0x7D55,0x7D56,/* 0x58-0x5F */
+ 0x7D57,0x7D58,0x7D59,0x7D5A,0x7D5B,0x7D5C,0x7D5D,0x7D5E,/* 0x60-0x67 */
+ 0x7D5F,0x7D60,0x7D61,0x7D62,0x7D63,0x7D64,0x7D65,0x7D66,/* 0x68-0x6F */
+ 0x7D67,0x7D68,0x7D69,0x7D6A,0x7D6B,0x7D6C,0x7D6D,0x7D6F,/* 0x70-0x77 */
+ 0x7D70,0x7D71,0x7D72,0x7D73,0x7D74,0x7D75,0x7D76,0x0000,/* 0x78-0x7F */
+
+ 0x7D78,0x7D79,0x7D7A,0x7D7B,0x7D7C,0x7D7D,0x7D7E,0x7D7F,/* 0x80-0x87 */
+ 0x7D80,0x7D81,0x7D82,0x7D83,0x7D84,0x7D85,0x7D86,0x7D87,/* 0x88-0x8F */
+ 0x7D88,0x7D89,0x7D8A,0x7D8B,0x7D8C,0x7D8D,0x7D8E,0x7D8F,/* 0x90-0x97 */
+ 0x7D90,0x7D91,0x7D92,0x7D93,0x7D94,0x7D95,0x7D96,0x7D97,/* 0x98-0x9F */
+ 0x7D98,0x5065,0x8230,0x5251,0x996F,0x6E10,0x6E85,0x6DA7,/* 0xA0-0xA7 */
+ 0x5EFA,0x50F5,0x59DC,0x5C06,0x6D46,0x6C5F,0x7586,0x848B,/* 0xA8-0xAF */
+ 0x6868,0x5956,0x8BB2,0x5320,0x9171,0xFA09,0x8549,0x6912,/* 0xB0-0xB7 */
+ 0x7901,0x7126,0x80F6,0x4EA4,0x90CA,0x6D47,0x9A84,0x5A07,/* 0xB8-0xBF */
+ 0x56BC,0x6405,0x94F0,0x77EB,0x4FA5,0x811A,0x72E1,0x89D2,/* 0xC0-0xC7 */
+ 0x997A,0x7F34,0x7EDE,0x527F,0x6559,0x9175,0x8F7F,0x8F83,/* 0xC8-0xCF */
+ 0x53EB,0x7A96,0x63ED,0x63A5,0x7686,0x79F8,0x8857,0x9636,/* 0xD0-0xD7 */
+ 0x622A,0x52AB,0x8282,0x6854,0x6770,0x6377,0x776B,0x7AED,/* 0xD8-0xDF */
+ 0x6D01,0x7ED3,0x89E3,0x59D0,0x6212,0x85C9,0x82A5,0x754C,/* 0xE0-0xE7 */
+ 0x501F,0x4ECB,0x75A5,0x8BEB,0x5C4A,0x5DFE,0x7B4B,0x65A4,/* 0xE8-0xEF */
+ 0xF90A,0x4ECA,0x6D25,0x895F,0x7D27,0x9526,0x4EC5,0x8C28,/* 0xF0-0xF7 */
+ 0x8FDB,0x9773,0x664B,0x7981,0x8FD1,0x70EC,0x6D78,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_BE[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x7D99,0x7D9A,0x7D9B,0x7D9C,0x7D9D,0x7D9E,0x7D9F,0xF93D,/* 0x40-0x47 */
+ 0x7DA1,0x7DA2,0x7DA3,0x7DA4,0x7DA5,0x7DA7,0x7DA8,0x7DA9,/* 0x48-0x4F */
+ 0x7DAA,0x7DAB,0x7DAC,0x7DAD,0x7DAF,0x7DB0,0x7DB1,0x7DB2,/* 0x50-0x57 */
+ 0x7DB3,0x7DB4,0x7DB5,0x7DB6,0x7DB7,0x7DB8,0x7DB9,0x7DBA,/* 0x58-0x5F */
+ 0x7DBB,0x7DBC,0x7DBD,0xF957,0x7DBF,0x7DC0,0x7DC1,0x7DC2,/* 0x60-0x67 */
+ 0x7DC3,0x7DC4,0x7DC5,0x7DC6,0x7DC7,0x7DC8,0x7DC9,0x7DCA,/* 0x68-0x6F */
+ 0x7DCB,0x7DCC,0x7DCD,0x7DCE,0x7DCF,0x7DD0,0x7DD1,0x7DD2,/* 0x70-0x77 */
+ 0x7DD3,0x7DD4,0x7DD5,0x7DD6,0x7DD7,0x7DD8,0x7DD9,0x0000,/* 0x78-0x7F */
+
+ 0x7DDA,0x7DDB,0x7DDC,0x7DDD,0x7DDE,0x7DDF,0x7DE0,0x7DE1,/* 0x80-0x87 */
+ 0x7DE2,0x7DE3,0x7DE4,0x7DE5,0x7DE6,0x7DE7,0x7DE8,0x7DE9,/* 0x88-0x8F */
+ 0x7DEA,0x7DEB,0x7DEC,0x7DED,0x7DEE,0x7DEF,0x7DF0,0x7DF1,/* 0x90-0x97 */
+ 0x7DF2,0x7DF3,0xF996,0x7DF5,0x7DF6,0x7DF7,0x7DF8,0x7DF9,/* 0x98-0x9F */
+ 0x7DFA,0x5C3D,0x52B2,0x8346,0x5162,0x830E,0x775B,0x6676,/* 0xA0-0xA7 */
+ 0x9CB8,0x4EAC,0x60CA,0xFA1D,0x7CB3,0x7ECF,0x4E95,0x8B66,/* 0xA8-0xAF */
+ 0x666F,0x9888,0x9759,0x5883,0x656C,0x955C,0x5F84,0x75C9,/* 0xB0-0xB7 */
+ 0xFA1C,0x7ADF,0x7ADE,0x51C0,0x70AF,0x7A98,0x63EA,0x7A76,/* 0xB8-0xBF */
+ 0x7EA0,0x7396,0x97ED,0x4E45,0x7078,0x4E5D,0x9152,0x53A9,/* 0xC0-0xC7 */
+ 0x6551,0x65E7,0x81FC,0x8205,0x548E,0x5C31,0x759A,0x97A0,/* 0xC8-0xCF */
+ 0x62D8,0x72D9,0x75BD,0x5C45,0x9A79,0x83CA,0x5C40,0x5480,/* 0xD0-0xD7 */
+ 0x77E9,0x4E3E,0x6CAE,0x805A,0x62D2,0x636E,0x5DE8,0x5177,/* 0xD8-0xDF */
+ 0x8DDD,0x8E1E,0x952F,0x4FF1,0xF906,0x60E7,0x70AC,0x5267,/* 0xE0-0xE7 */
+ 0x6350,0x9E43,0x5A1F,0x5026,0x7737,0x5377,0x7EE2,0x6485,/* 0xE8-0xEF */
+ 0x652B,0x6289,0x6398,0x5014,0x7235,0x89C9,0x51B3,0x8BC0,/* 0xF0-0xF7 */
+ 0x7EDD,0x5747,0x83CC,0x94A7,0x519B,0x541B,0x5CFB,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_BF[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x7DFB,0x7DFC,0x7DFD,0x7DFE,0x7DFF,0x7E00,0x7E01,0x7E02,/* 0x40-0x47 */
+ 0x7E03,0x7E04,0x7E05,0x7E06,0x7E07,0x7E08,0x7E09,0x7E0A,/* 0x48-0x4F */
+ 0x7E0B,0x7E0C,0x7E0D,0x7E0E,0x7E0F,0x7E10,0x7E11,0x7E12,/* 0x50-0x57 */
+ 0x7E13,0x7E14,0x7E15,0x7E16,0x7E17,0x7E18,0x7E19,0x7E1A,/* 0x58-0x5F */
+ 0x7E1B,0x7E1C,0x7E1D,0x7E1E,0x7E1F,0x7E20,0x7E21,0x7E22,/* 0x60-0x67 */
+ 0x7E23,0x7E24,0x7E25,0x7E26,0x7E27,0x7E28,0x7E29,0x7E2A,/* 0x68-0x6F */
+ 0x7E2B,0x7E2C,0x7E2D,0x7E2E,0x7E2F,0x7E30,0x7E31,0x7E32,/* 0x70-0x77 */
+ 0x7E33,0x7E34,0x7E35,0x7E36,0xF950,0x7E38,0x7E39,0x0000,/* 0x78-0x7F */
+
+ 0x7E3A,0x7E3C,0x7E3D,0x7E3E,0x7E3F,0x7E40,0x7E42,0x7E43,/* 0x80-0x87 */
+ 0x7E44,0x7E45,0x7E46,0x7E48,0x7E49,0x7E4A,0x7E4B,0x7E4C,/* 0x88-0x8F */
+ 0x7E4D,0x7E4E,0x7E4F,0x7E50,0x7E51,0x7E52,0x7E53,0x7E54,/* 0x90-0x97 */
+ 0x7E55,0x7E56,0x7E57,0x7E58,0x7E59,0x7E5A,0x7E5B,0x7E5C,/* 0x98-0x9F */
+ 0x7E5D,0x4FCA,0x7AE3,0x6D5A,0x90E1,0x9A8F,0x5580,0x5496,/* 0xA0-0xA7 */
+ 0x5361,0x54AF,0x5F00,0x63E9,0x6977,0x51EF,0x6168,0x520A,/* 0xA8-0xAF */
+ 0x582A,0x52D8,0x574E,0x780D,0x770B,0x5EB7,0x6177,0x7CE0,/* 0xB0-0xB7 */
+ 0x625B,0x6297,0x4EA2,0x7095,0x8003,0x62F7,0x70E4,0x9760,/* 0xB8-0xBF */
+ 0x5777,0x82DB,0x67EF,0x68F5,0x78D5,0x9897,0x79D1,0x58F3,/* 0xC0-0xC7 */
+ 0x54B3,0x53EF,0x6E34,0x514B,0x523B,0x5BA2,0x8BFE,0x80AF,/* 0xC8-0xCF */
+ 0x5543,0x57A6,0x6073,0x5751,0x542D,0x7A7A,0x6050,0x5B54,/* 0xD0-0xD7 */
+ 0x63A7,0x62A0,0x53E3,0x6263,0x5BC7,0x67AF,0x54ED,0x7A9F,/* 0xD8-0xDF */
+ 0x82E6,0x9177,0x5E93,0x88E4,0x5938,0x57AE,0x630E,0x8DE8,/* 0xE0-0xE7 */
+ 0x80EF,0x5757,0x7B77,0x4FA9,0x5FEB,0x5BBD,0x6B3E,0x5321,/* 0xE8-0xEF */
+ 0x7B50,0x72C2,0x6846,0x77FF,0x7736,0x65F7,0x51B5,0x4E8F,/* 0xF0-0xF7 */
+ 0x76D4,0x5CBF,0x7AA5,0x8475,0x594E,0x9B41,0x5080,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_C0[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x7E5E,0x7E5F,0x7E60,0x7E61,0x7E62,0x7E63,0x7E64,0x7E65,/* 0x40-0x47 */
+ 0x7E66,0x7E67,0x7E68,0x7E69,0x7E6A,0x7E6B,0x7E6C,0x7E6D,/* 0x48-0x4F */
+ 0x7E6E,0x7E6F,0x7E70,0x7E71,0x7E72,0x7E73,0x7E74,0x7E75,/* 0x50-0x57 */
+ 0x7E76,0x7E77,0x7E78,0x7E79,0x7E7A,0x7E7B,0x7E7C,0x7E7D,/* 0x58-0x5F */
+ 0x7E7E,0x7E7F,0x7E80,0x7E81,0x7E83,0x7E84,0x7E85,0x7E86,/* 0x60-0x67 */
+ 0x7E87,0x7E88,0x7E89,0x7E8A,0x7E8B,0x7E8C,0x7E8D,0x7E8E,/* 0x68-0x6F */
+ 0x7E8F,0x7E90,0x7E91,0x7E92,0x7E93,0x7E94,0x7E95,0x7E96,/* 0x70-0x77 */
+ 0x7E97,0x7E98,0x7E99,0x7E9A,0x7E9C,0x7E9D,0x7E9E,0x0000,/* 0x78-0x7F */
+
+ 0x7EAE,0x7EB4,0x7EBB,0x7EBC,0x7ED6,0x7EE4,0x7EEC,0x7EF9,/* 0x80-0x87 */
+ 0x7F0A,0x7F10,0x7F1E,0x7F37,0x7F39,0x7F3B,0x7F3C,0x7F3D,/* 0x88-0x8F */
+ 0x7F3E,0x7F3F,0x7F40,0x7F41,0x7F43,0x7F46,0x7F47,0x7F48,/* 0x90-0x97 */
+ 0x7F49,0x7F4A,0x7F4B,0x7F4C,0x7F4D,0x7F4E,0x7F4F,0x7F52,/* 0x98-0x9F */
+ 0x7F53,0x9988,0x6127,0x6E83,0x5764,0x6606,0x6346,0x56F0,/* 0xA0-0xA7 */
+ 0x62EC,0x6269,0xFA0B,0x9614,0x5783,0xF925,0xF90B,0x8721,/* 0xA8-0xAF */
+ 0x814A,0x8FA3,0x5566,0x83B1,0x6765,0x8D56,0x84DD,0x5A6A,/* 0xB0-0xB7 */
+ 0x680F,0x62E6,0x7BEE,0x9611,0x5170,0x6F9C,0x8C30,0x63FD,/* 0xB8-0xBF */
+ 0x89C8,0x61D2,0x7F06,0x70C2,0x6EE5,0x7405,0x6994,0xF92B,/* 0xC0-0xC7 */
+ 0xF928,0x90CE,0xF929,0xF92A,0x635E,0x52B3,0xF946,0xF934,/* 0xC8-0xCF */
+ 0x4F6C,0x59E5,0xF919,0xF916,0x6D9D,0xF952,0x4E50,0xF949,/* 0xD0-0xD7 */
+ 0x956D,0x857E,0xF947,0xF94F,0x5121,0x5792,0x64C2,0xF953,/* 0xD8-0xDF */
+ 0x7C7B,0x6CEA,0x68F1,0x695E,0xF92E,0x5398,0xF9E2,0x7281,/* 0xE0-0xE7 */
+ 0xF989,0x7BF1,0x72F8,0x79BB,0x6F13,0xF9E4,0xF9E1,0xF9E9,/* 0xE8-0xEF */
+ 0x9CA4,0x793C,0x8389,0x8354,0xF9DE,0xF9DA,0x4E3D,0x5389,/* 0xF0-0xF7 */
+ 0x52B1,0x783E,0x5386,0xF9DD,0x5088,0xF9B5,0x4FD0,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_C1[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x7F56,0x7F59,0x7F5B,0x7F5C,0x7F5D,0x7F5E,0x7F60,0x7F63,/* 0x40-0x47 */
+ 0x7F64,0x7F65,0x7F66,0x7F67,0x7F6B,0x7F6C,0x7F6D,0x7F6F,/* 0x48-0x4F */
+ 0x7F70,0x7F73,0x7F75,0x7F76,0x7F77,0x7F78,0x7F7A,0x7F7B,/* 0x50-0x57 */
+ 0x7F7C,0x7F7D,0x7F7F,0x7F80,0x7F82,0x7F83,0x7F84,0xF90F,/* 0x58-0x5F */
+ 0x7F86,0x7F87,0x7F88,0x7F89,0x7F8B,0x7F8D,0x7F8F,0x7F90,/* 0x60-0x67 */
+ 0x7F91,0x7F92,0x7F93,0x7F95,0x7F96,0x7F97,0x7F98,0x7F99,/* 0x68-0x6F */
+ 0x7F9B,0x7F9C,0x7FA0,0x7FA2,0x7FA3,0x7FA5,0x7FA6,0x7FA8,/* 0x70-0x77 */
+ 0x7FA9,0x7FAA,0x7FAB,0x7FAC,0x7FAD,0x7FAE,0x7FB1,0x0000,/* 0x78-0x7F */
+
+ 0x7FB3,0x7FB4,0x7FB5,0x7FB6,0x7FB7,0x7FBA,0x7FBB,0x7FBE,/* 0x80-0x87 */
+ 0x7FC0,0x7FC2,0x7FC3,0x7FC4,0x7FC6,0x7FC7,0x7FC8,0x7FC9,/* 0x88-0x8F */
+ 0x7FCB,0x7FCD,0x7FCF,0x7FD0,0x7FD1,0x7FD2,0x7FD3,0x7FD6,/* 0x90-0x97 */
+ 0x7FD7,0x7FD9,0x7FDA,0x7FDB,0x7FDC,0x7FDD,0x7FDE,0x7FE2,/* 0x98-0x9F */
+ 0x7FE3,0xF9E5,0xF9F7,0xF9F9,0x6CA5,0x96B6,0xF98A,0x7483,/* 0xA0-0xA7 */
+ 0x54E9,0x4FE9,0x8054,0x83B2,0x8FDE,0x9570,0xF9A2,0xF9AC,/* 0xA8-0xAF */
+ 0x6D9F,0x5E18,0x655B,0x8138,0x94FE,0x604B,0x70BC,0x7EC3,/* 0xB0-0xB7 */
+ 0x7CAE,0x51C9,0xF97A,0x7CB1,0xF97C,0x4E24,0x8F86,0xF97E,/* 0xB8-0xBF */
+ 0x667E,0xF977,0x8C05,0x64A9,0x804A,0xF9BB,0x7597,0xF9C0,/* 0xC0-0xC7 */
+ 0x5BE5,0x8FBD,0x6F66,0xF9BA,0x6482,0x9563,0x5ED6,0xF9BE,/* 0xC8-0xCF */
+ 0xF99C,0xF9A0,0xF99F,0xF99D,0x730E,0x7433,0xF9F4,0x78F7,/* 0xD0-0xD7 */
+ 0x9716,0x4E34,0x90BB,0x9CDE,0xF9F5,0x51DB,0x8D41,0xF9ED,/* 0xD8-0xDF */
+ 0x62CE,0xF9AD,0xF958,0xF9B2,0x9F84,0x94C3,0x4F36,0xF9AF,/* 0xE0-0xE7 */
+ 0xF955,0x7075,0xF959,0x5CAD,0x9886,0x53E6,0xF9A8,0xF9CB,/* 0xE8-0xEF */
+ 0xF9CC,0x69B4,0xF9CE,0x998F,0xF9CD,0x5218,0x7624,0xF9CA,/* 0xF0-0xF7 */
+ 0xF9C9,0xF9D1,0x9F99,0x804B,0x5499,0x7B3C,0x7ABF,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_C2[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x7FE4,0x7FE7,0x7FE8,0x7FEA,0x7FEB,0x7FEC,0x7FED,0x7FEF,/* 0x40-0x47 */
+ 0x7FF2,0x7FF4,0x7FF5,0x7FF6,0x7FF7,0x7FF8,0x7FF9,0x7FFA,/* 0x48-0x4F */
+ 0x7FFD,0x7FFE,0x7FFF,0x8002,0x8007,0x8008,0x8009,0x800A,/* 0x50-0x57 */
+ 0x800E,0x800F,0x8011,0x8013,0x801A,0x801B,0x801D,0x801E,/* 0x58-0x5F */
+ 0x801F,0x8021,0x8023,0x8024,0x802B,0x802C,0x802D,0x802E,/* 0x60-0x67 */
+ 0x802F,0x8030,0x8032,0x8034,0x8039,0x803A,0x803C,0x803E,/* 0x68-0x6F */
+ 0x8040,0x8041,0x8044,0x8045,0x8047,0x8048,0x8049,0x804E,/* 0x70-0x77 */
+ 0x804F,0x8050,0x8051,0x8053,0x8055,0x8056,0x8057,0x0000,/* 0x78-0x7F */
+
+ 0x8059,0x805B,0x805C,0x805D,0x805E,0x805F,0x8060,0x8061,/* 0x80-0x87 */
+ 0x8062,0x8063,0x8064,0x8065,0x8066,0x8067,0x8068,0x806B,/* 0x88-0x8F */
+ 0x806C,0x806D,0x806E,0xF997,0x8070,0x8072,0x8073,0x8074,/* 0x90-0x97 */
+ 0x8075,0x8076,0x8077,0x8078,0x8079,0x807A,0x807B,0x807C,/* 0x98-0x9F */
+ 0x807D,0xF9DC,0x5784,0x62E2,0x9647,0x697C,0x5A04,0x6402,/* 0xA0-0xA7 */
+ 0x7BD3,0xF94E,0xF951,0x82A6,0x5362,0x9885,0x5E90,0x7089,/* 0xA8-0xAF */
+ 0x63B3,0x5364,0x864F,0x9C81,0x9E93,0xF93B,0xF938,0xF937,/* 0xB0-0xB7 */
+ 0x8D42,0xF940,0x6F5E,0x7984,0x5F55,0x9646,0xF9D2,0x9A74,/* 0xB8-0xBF */
+ 0x5415,0x94DD,0x4FA3,0xF983,0xF9DF,0x5C61,0x7F15,0x8651,/* 0xC0-0xC7 */
+ 0x6C2F,0xF9D8,0xF9DB,0x6EE4,0x7EFF,0x5CE6,0x631B,0x5B6A,/* 0xC8-0xCF */
+ 0x6EE6,0xF91C,0x4E71,0xF975,0xF976,0x62A1,0x8F6E,0x4F26,/* 0xD0-0xD7 */
+ 0x4ED1,0x6CA6,0x7EB6,0x8BBA,0x841D,0xF911,0x7F57,0x903B,/* 0xD8-0xDF */
+ 0x9523,0x7BA9,0x9AA1,0xF912,0xF918,0xF915,0x9A86,0x7EDC,/* 0xE0-0xE7 */
+ 0x5988,0x9EBB,0x739B,0x7801,0x8682,0x9A6C,0x9A82,0x561B,/* 0xE8-0xEF */
+ 0x5417,0x57CB,0x4E70,0x9EA6,0x5356,0x8FC8,0x8109,0x7792,/* 0xF0-0xF7 */
+ 0x9992,0x86EE,0x6EE1,0x8513,0x66FC,0x6162,0x6F2B,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_C3[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0xF945,0x8081,0x8082,0x8085,0x8088,0x808A,0x808D,0x808E,/* 0x40-0x47 */
+ 0x808F,0x8090,0x8091,0x8092,0x8094,0x8095,0x8097,0x8099,/* 0x48-0x4F */
+ 0x809E,0x80A3,0x80A6,0x80A7,0x80A8,0x80AC,0x80B0,0x80B3,/* 0x50-0x57 */
+ 0x80B5,0x80B6,0x80B8,0x80B9,0x80BB,0x80C5,0x80C7,0x80C8,/* 0x58-0x5F */
+ 0x80C9,0x80CA,0x80CB,0x80CF,0x80D0,0x80D1,0x80D2,0x80D3,/* 0x60-0x67 */
+ 0x80D4,0x80D5,0x80D8,0x80DF,0x80E0,0x80E2,0x80E3,0x80E6,/* 0x68-0x6F */
+ 0x80EE,0x80F5,0x80F7,0x80F9,0x80FB,0x80FE,0x80FF,0x8100,/* 0x70-0x77 */
+ 0x8101,0x8103,0x8104,0x8105,0x8107,0x8108,0x810B,0x0000,/* 0x78-0x7F */
+
+ 0x810C,0x8115,0x8117,0x8119,0x811B,0x811C,0x811D,0x811F,/* 0x80-0x87 */
+ 0x8120,0x8121,0x8122,0x8123,0x8124,0x8125,0x8126,0x8127,/* 0x88-0x8F */
+ 0x8128,0x8129,0x812A,0x812B,0x812D,0x812E,0x8130,0x8133,/* 0x90-0x97 */
+ 0x8134,0x8135,0x8137,0x8139,0x813A,0x813B,0x813C,0x813D,/* 0x98-0x9F */
+ 0x813F,0x8C29,0x8292,0x832B,0x76F2,0x6C13,0x5FD9,0x83BD,/* 0xA0-0xA7 */
+ 0x732B,0x8305,0x951A,0x6BDB,0x77DB,0x94C6,0x536F,0x8302,/* 0xA8-0xAF */
+ 0x5192,0x5E3D,0x8C8C,0x8D38,0x4E48,0x73AB,0x679A,0x6885,/* 0xB0-0xB7 */
+ 0x9176,0x9709,0x7164,0x6CA1,0x7709,0x5A92,0x9541,0x6BCF,/* 0xB8-0xBF */
+ 0x7F8E,0x6627,0x5BD0,0x59B9,0x5A9A,0x95E8,0x95F7,0x4EEC,/* 0xC0-0xC7 */
+ 0x840C,0x8499,0x6AAC,0x76DF,0x9530,0x731B,0x68A6,0x5B5F,/* 0xC8-0xCF */
+ 0x772F,0x919A,0x9761,0x7CDC,0x8FF7,0x8C1C,0x5F25,0x7C73,/* 0xD0-0xD7 */
+ 0x79D8,0x89C5,0xF968,0x871C,0x5BC6,0x5E42,0x68C9,0x7720,/* 0xD8-0xDF */
+ 0x7EF5,0x5195,0x514D,0x52C9,0x5A29,0x7F05,0x9762,0x82D7,/* 0xE0-0xE7 */
+ 0x63CF,0x7784,0x85D0,0x79D2,0x6E3A,0x5E99,0x5999,0x8511,/* 0xE8-0xEF */
+ 0x706D,0x6C11,0x62BF,0x76BF,0x654F,0x60AF,0x95FD,0x660E,/* 0xF0-0xF7 */
+ 0x879F,0x9E23,0x94ED,0x540D,0x547D,0x8C2C,0x6478,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_C4[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x8140,0x8141,0x8142,0x8143,0x8144,0x8145,0x8147,0x8149,/* 0x40-0x47 */
+ 0x814D,0x814E,0x814F,0x8152,0x8156,0x8157,0x8158,0x815B,/* 0x48-0x4F */
+ 0x815C,0x815D,0x815E,0x815F,0x8161,0x8162,0x8163,0x8164,/* 0x50-0x57 */
+ 0x8166,0x8168,0x816A,0x816B,0x816C,0x816F,0x8172,0x8173,/* 0x58-0x5F */
+ 0x8175,0x8176,0x8177,0x8178,0x8181,0x8183,0x8184,0x8185,/* 0x60-0x67 */
+ 0x8186,0x8187,0x8189,0x818B,0x818C,0x818D,0x818E,0x8190,/* 0x68-0x6F */
+ 0x8192,0x8193,0x8194,0x8195,0x8196,0x8197,0x8199,0x819A,/* 0x70-0x77 */
+ 0x819E,0x819F,0x81A0,0x81A1,0x81A2,0x81A4,0x81A5,0x0000,/* 0x78-0x7F */
+
+ 0x81A7,0x81A9,0x81AB,0x81AC,0x81AD,0x81AE,0x81AF,0x81B0,/* 0x80-0x87 */
+ 0x81B1,0x81B2,0x81B4,0x81B5,0x81B6,0x81B7,0x81B8,0x81B9,/* 0x88-0x8F */
+ 0x81BC,0x81BD,0x81BE,0x81BF,0x81C4,0x81C5,0x81C7,0x81C8,/* 0x90-0x97 */
+ 0x81C9,0x81CB,0x81CD,0x81CE,0x81CF,0x81D0,0x81D1,0x81D2,/* 0x98-0x9F */
+ 0x81D3,0x6479,0x8611,0x6A21,0x819C,0x78E8,0x6469,0x9B54,/* 0xA0-0xA7 */
+ 0x62B9,0x672B,0x83AB,0x58A8,0x9ED8,0x6CAB,0x6F20,0x5BDE,/* 0xA8-0xAF */
+ 0x964C,0x8C0B,0x725F,0x67D0,0x62C7,0x7261,0x4EA9,0x59C6,/* 0xB0-0xB7 */
+ 0x6BCD,0x5893,0x66AE,0x5E55,0x52DF,0x6155,0x6728,0x76EE,/* 0xB8-0xBF */
+ 0x7766,0x7267,0x7A46,0x62FF,0x54EA,0x5450,0x94A0,0x90A3,/* 0xC0-0xC7 */
+ 0x5A1C,0x7EB3,0x6C16,0x4E43,0x5976,0x8010,0xF90C,0x5357,/* 0xC8-0xCF */
+ 0x7537,0x96BE,0x56CA,0x6320,0x8111,0x607C,0x95F9,0x6DD6,/* 0xD0-0xD7 */
+ 0x5462,0x9981,0x5185,0x5AE9,0x80FD,0x59AE,0x9713,0x502A,/* 0xD8-0xDF */
+ 0xF9E3,0x5C3C,0x62DF,0x4F60,0xF9EB,0x817B,0x9006,0xF9EC,/* 0xE0-0xE7 */
+ 0x852B,0x62C8,0xF98E,0x78BE,0x64B5,0xF9A4,0xF9A3,0x5A18,/* 0xE8-0xEF */
+ 0x917F,0x9E1F,0xF9BD,0x634F,0x8042,0x5B7D,0x556E,0x954A,/* 0xF0-0xF7 */
+ 0x954D,0x6D85,0x60A8,0x67E0,0x72DE,0x51DD,0x5B81,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_C5[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x81D4,0x81D5,0x81D6,0x81D7,0xF926,0x81D9,0x81DA,0x81DB,/* 0x40-0x47 */
+ 0x81DC,0x81DD,0x81DE,0x81DF,0x81E0,0x81E1,0x81E2,0x81E4,/* 0x48-0x4F */
+ 0x81E5,0x81E6,0xF9F6,0x81E9,0x81EB,0x81EE,0x81EF,0x81F0,/* 0x50-0x57 */
+ 0x81F1,0x81F2,0x81F5,0x81F6,0x81F7,0x81F8,0x81F9,0x81FA,/* 0x58-0x5F */
+ 0x81FD,0x81FF,0x8203,0x8207,0x8208,0x8209,0x820A,0x820B,/* 0x60-0x67 */
+ 0x820E,0x820F,0x8211,0x8213,0x8215,0x8216,0x8217,0x8218,/* 0x68-0x6F */
+ 0x8219,0x821A,0x821D,0x8220,0x8224,0x8225,0x8226,0x8227,/* 0x70-0x77 */
+ 0x8229,0x822E,0x8232,0x823A,0x823C,0x823D,0x823F,0x0000,/* 0x78-0x7F */
+
+ 0x8240,0x8241,0x8242,0x8243,0x8245,0x8246,0x8248,0x824A,/* 0x80-0x87 */
+ 0x824C,0x824D,0x824E,0x8250,0x8251,0x8252,0x8253,0x8254,/* 0x88-0x8F */
+ 0x8255,0x8256,0x8257,0x8259,0x825B,0x825C,0x825D,0x825E,/* 0x90-0x97 */
+ 0x8260,0x8261,0x8262,0x8263,0x8264,0x8265,0x8266,0x8267,/* 0x98-0x9F */
+ 0x8269,0x62E7,0x6CDE,0x725B,0x626D,0x94AE,0x7EBD,0x8113,/* 0xA0-0xA7 */
+ 0x6D53,0x519C,0xF943,0x5974,0x52AA,0xF960,0xF981,0x6696,/* 0xA8-0xAF */
+ 0x8650,0x759F,0x632A,0x61E6,0x7CEF,0x8BFA,0x54E6,0x6B27,/* 0xB0-0xB7 */
+ 0x9E25,0x6BB4,0x85D5,0x5455,0x5076,0x6CA4,0x556A,0x8DB4,/* 0xB8-0xBF */
+ 0x722C,0x5E15,0x6015,0x7436,0x62CD,0x6392,0x724C,0x5F98,/* 0xC0-0xC7 */
+ 0x6E43,0x6D3E,0x6500,0x6F58,0x76D8,0x78D0,0x76FC,0x7554,/* 0xC8-0xCF */
+ 0x5224,0x53DB,0x4E53,0x5E9E,0x65C1,0x802A,0x80D6,0x629B,/* 0xD0-0xD7 */
+ 0x5486,0x5228,0x70AE,0x888D,0x8DD1,0x6CE1,0x5478,0x80DA,/* 0xD8-0xDF */
+ 0x57F9,0x88F4,0x8D54,0x966A,0x914D,0x4F69,0x6C9B,0x55B7,/* 0xE0-0xE7 */
+ 0x76C6,0x7830,0x62A8,0x70F9,0x6F8E,0x5F6D,0x84EC,0x68DA,/* 0xE8-0xEF */
+ 0x787C,0x7BF7,0x81A8,0x670B,0x9E4F,0x6367,0x78B0,0x576F,/* 0xF0-0xF7 */
+ 0x7812,0x9739,0x6279,0x62AB,0x5288,0x7435,0x6BD7,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_C6[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x826A,0x826B,0x826C,0x826D,0x8271,0x8275,0x8276,0x8277,/* 0x40-0x47 */
+ 0x8278,0x827B,0x827C,0x8280,0x8281,0x8283,0x8285,0x8286,/* 0x48-0x4F */
+ 0x8287,0x8289,0x828C,0x8290,0x8293,0x8294,0x8295,0x8296,/* 0x50-0x57 */
+ 0x829A,0x829B,0x829E,0x82A0,0x82A2,0x82A3,0x82A7,0x82B2,/* 0x58-0x5F */
+ 0x82B5,0x82B6,0x82BA,0x82BB,0x82BC,0x82BF,0x82C0,0x82C2,/* 0x60-0x67 */
+ 0x82C3,0x82C5,0x82C6,0x82C9,0x82D0,0x82D6,0x82D9,0x82DA,/* 0x68-0x6F */
+ 0x82DD,0x82E2,0x82E7,0x82E8,0x82E9,0x82EA,0x82EC,0x82ED,/* 0x70-0x77 */
+ 0x82EE,0x82F0,0x82F2,0x82F3,0x82F5,0x82F6,0x82F8,0x0000,/* 0x78-0x7F */
+
+ 0x82FA,0x82FC,0x82FD,0x82FE,0x82FF,0x8300,0x830A,0x830B,/* 0x80-0x87 */
+ 0x830D,0x8310,0x8312,0x8313,0x8316,0x8318,0x8319,0x831D,/* 0x88-0x8F */
+ 0x831E,0x831F,0x8320,0x8321,0x8322,0x8323,0x8324,0x8325,/* 0x90-0x97 */
+ 0x8326,0x8329,0x832A,0x832E,0x8330,0x8332,0x8337,0x833B,/* 0x98-0x9F */
+ 0x833D,0x5564,0x813E,0x75B2,0x76AE,0x5339,0x75DE,0x50FB,/* 0xA0-0xA7 */
+ 0x5C41,0x8B6C,0x7BC7,0x504F,0x7247,0x9A97,0x98D8,0x6F02,/* 0xA8-0xAF */
+ 0x74E2,0x7968,0x6487,0x77A5,0x62FC,0x9891,0x8D2B,0x54C1,/* 0xB0-0xB7 */
+ 0x8058,0x4E52,0x576A,0x82F9,0x840D,0x5E73,0x51ED,0x74F6,/* 0xB8-0xBF */
+ 0x8BC4,0x5C4F,0x5761,0x6CFC,0x9887,0x5A46,0x7834,0x9B44,/* 0xC0-0xC7 */
+ 0x8FEB,0x7C95,0x5256,0x6251,0x94FA,0x4EC6,0x8386,0x8461,/* 0xC8-0xCF */
+ 0x83E9,0x84B2,0x57D4,0x6734,0x5703,0x666E,0x6D66,0x8C31,/* 0xD0-0xD7 */
+ 0x66DD,0x7011,0x671F,0x6B3A,0x6816,0x621A,0x59BB,0x4E03,/* 0xD8-0xDF */
+ 0x51C4,0x6F06,0x67D2,0x6C8F,0x5176,0x68CB,0x5947,0x6B67,/* 0xE0-0xE7 */
+ 0x7566,0x5D0E,0x8110,0x9F50,0x65D7,0x7948,0x7941,0x9A91,/* 0xE8-0xEF */
+ 0x8D77,0x5C82,0x4E5E,0x4F01,0x542F,0xF909,0x780C,0x5668,/* 0xF0-0xF7 */
+ 0x6C14,0x8FC4,0x5F03,0x6C7D,0x6CE3,0x8BAB,0x6390,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_C7[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x833E,0x833F,0x8341,0x8342,0x8344,0x8345,0x8348,0x834A,/* 0x40-0x47 */
+ 0x834B,0x834C,0x834D,0x834E,0x8353,0x8355,0x8356,0x8357,/* 0x48-0x4F */
+ 0x8358,0x8359,0x835D,0x8362,0x8370,0x8371,0x8372,0x8373,/* 0x50-0x57 */
+ 0x8374,0x8375,0x8376,0x8379,0x837A,0x837E,0x837F,0x8380,/* 0x58-0x5F */
+ 0x8381,0x8382,0x8383,0x8384,0x8387,0x8388,0x838A,0x838B,/* 0x60-0x67 */
+ 0x838C,0x838D,0x838F,0x8390,0x8391,0x8394,0x8395,0x8396,/* 0x68-0x6F */
+ 0x8397,0x8399,0x839A,0x839D,0x839F,0x83A1,0x83A2,0x83A3,/* 0x70-0x77 */
+ 0x83A4,0x83A5,0x83A6,0x83A7,0x83AC,0x83AD,0x83AE,0x0000,/* 0x78-0x7F */
+
+ 0x83AF,0x83B5,0x83BB,0x83BE,0x83BF,0x83C2,0x83C3,0x83C4,/* 0x80-0x87 */
+ 0x83C6,0x83C8,0xF93E,0x83CB,0x83CD,0x83CE,0x83D0,0x83D1,/* 0x88-0x8F */
+ 0x83D2,0x83D3,0x83D5,0x83D7,0x83D9,0x83DA,0x83DB,0x83DE,/* 0x90-0x97 */
+ 0x83E2,0x83E3,0x83E4,0x83E6,0x83E7,0x83E8,0x83EB,0x83EC,/* 0x98-0x9F */
+ 0x83ED,0x6070,0x6D3D,0x7275,0x6266,0x948E,0x94C5,0x5343,/* 0xA0-0xA7 */
+ 0x8FC1,0x7B7E,0x4EDF,0x8C26,0x4E7E,0x9ED4,0x94B1,0x94B3,/* 0xA8-0xAF */
+ 0x524D,0x6F5C,0x9063,0x6D45,0x8C34,0x5811,0x5D4C,0x6B20,/* 0xB0-0xB7 */
+ 0x6B49,0x67AA,0x545B,0x8154,0x7F8C,0x5899,0x8537,0x5F3A,/* 0xB8-0xBF */
+ 0x62A2,0x6A47,0x9539,0x6572,0x6084,0x6865,0x77A7,0x4E54,/* 0xC0-0xC7 */
+ 0x4FA8,0x5DE7,0x9798,0x64AC,0x7FD8,0x5CED,0x4FCF,0x7A8D,/* 0xC8-0xCF */
+ 0xFA00,0x8304,0x4E14,0x602F,0x7A83,0x94A6,0x4FB5,0x4EB2,/* 0xD0-0xD7 */
+ 0x79E6,0x7434,0x52E4,0x82B9,0x64D2,0x79BD,0x5BDD,0x6C81,/* 0xD8-0xDF */
+ 0x9752,0x8F7B,0x6C22,0x503E,0x537F,0x6E05,0x64CE,0xFA12,/* 0xE0-0xE7 */
+ 0x6C30,0x60C5,0x9877,0x8BF7,0x5E86,0x743C,0x7A77,0x79CB,/* 0xE8-0xEF */
+ 0x4E18,0x90B1,0x7403,0x6C42,0x56DA,0x914B,0x6CC5,0x8D8B,/* 0xF0-0xF7 */
+ 0x533A,0x86C6,0x66F2,0x8EAF,0x5C48,0x9A71,0x6E20,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_C8[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x83EE,0x83EF,0x83F3,0x83F4,0x83F5,0x83F6,0x83F7,0x83FA,/* 0x40-0x47 */
+ 0x83FB,0x83FC,0x83FE,0x83FF,0x8400,0x8402,0x8405,0x8407,/* 0x48-0x4F */
+ 0x8408,0x8409,0x840A,0x8410,0x8412,0x8413,0x8414,0x8415,/* 0x50-0x57 */
+ 0x8416,0x8417,0x8419,0x841A,0x841B,0x841E,0x841F,0x8420,/* 0x58-0x5F */
+ 0x8421,0x8422,0x8423,0x8429,0x842A,0x842B,0x842C,0x842D,/* 0x60-0x67 */
+ 0x842E,0x842F,0x8430,0x8432,0x8433,0x8434,0x8435,0x8436,/* 0x68-0x6F */
+ 0x8437,0x8439,0x843A,0x843B,0x843E,0x843F,0x8440,0x8441,/* 0x70-0x77 */
+ 0x8442,0x8443,0x8444,0x8445,0x8447,0x8448,0xF96E,0x0000,/* 0x78-0x7F */
+
+ 0x844A,0x844B,0x844C,0x844D,0x844E,0x844F,0x8450,0x8452,/* 0x80-0x87 */
+ 0x8453,0x8454,0x8455,0x8456,0x8458,0x845D,0x845E,0x845F,/* 0x88-0x8F */
+ 0x8460,0x8462,0x8464,0x8465,0x8466,0x8467,0x8468,0x846A,/* 0x90-0x97 */
+ 0x846E,0x846F,0x8470,0x8472,0x8474,0x8477,0x8479,0x847B,/* 0x98-0x9F */
+ 0x847C,0x53D6,0x5A36,0x9F8B,0x8DA3,0x53BB,0x5708,0x98A7,/* 0xA0-0xA7 */
+ 0x6743,0x919B,0x6CC9,0x5168,0x75CA,0x62F3,0x72AC,0x5238,/* 0xA8-0xAF */
+ 0x529D,0x7F3A,0x7094,0x7638,0x5374,0x9E4A,0x69B7,0x786E,/* 0xB0-0xB7 */
+ 0x96C0,0x88D9,0x7FA4,0x7136,0x71C3,0x5189,0x67D3,0x74E4,/* 0xB8-0xBF */
+ 0x58E4,0x6518,0x56B7,0x8BA9,0x9976,0x6270,0x7ED5,0x60F9,/* 0xC0-0xC7 */
+ 0x70ED,0x58EC,0x4EC1,0x4EBA,0x5FCD,0x97E7,0x4EFB,0x8BA4,/* 0xC8-0xCF */
+ 0x5203,0x598A,0x7EAB,0x6254,0x4ECD,0x65E5,0x620E,0x8338,/* 0xD0-0xD7 */
+ 0x84C9,0x8363,0x878D,0x7194,0x6EB6,0x5BB9,0x7ED2,0x5197,/* 0xD8-0xDF */
+ 0x63C9,0x67D4,0x8089,0x8339,0x8815,0x5112,0x5B7A,0x5982,/* 0xE0-0xE7 */
+ 0x8FB1,0x4E73,0x6C5D,0x5165,0x8925,0x8F6F,0xF9C6,0x854A,/* 0xE8-0xEF */
+ 0x745E,0x9510,0x95F0,0x6DA6,0xF974,0x5F31,0x6492,0x6D12,/* 0xF0-0xF7 */
+ 0x8428,0x816E,0x9CC3,0xF96C,0x8D5B,0x4E09,0x53C1,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_C9[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x847D,0x847E,0x847F,0x8480,0x8481,0x8483,0x8484,0x8485,/* 0x40-0x47 */
+ 0x8486,0x848A,0x848D,0x848F,0x8490,0x8491,0x8492,0x8493,/* 0x48-0x4F */
+ 0x8494,0x8495,0x8496,0x8498,0x849A,0x849B,0x849D,0x849E,/* 0x50-0x57 */
+ 0x849F,0x84A0,0x84A2,0x84A3,0x84A4,0x84A5,0x84A6,0x84A7,/* 0x58-0x5F */
+ 0x84A8,0x84A9,0x84AA,0x84AB,0x84AC,0x84AD,0x84AE,0x84B0,/* 0x60-0x67 */
+ 0x84B1,0x84B3,0x84B5,0x84B6,0x84B7,0x84BB,0x84BC,0x84BE,/* 0x68-0x6F */
+ 0x84C0,0x84C2,0x84C3,0x84C5,0x84C6,0x84C7,0x84C8,0x84CB,/* 0x70-0x77 */
+ 0x84CC,0x84CE,0x84CF,0x84D2,0x84D4,0x84D5,0x84D7,0x0000,/* 0x78-0x7F */
+
+ 0x84D8,0x84D9,0x84DA,0x84DB,0x84DC,0x84DE,0x84E1,0x84E2,/* 0x80-0x87 */
+ 0x84E4,0x84E7,0x84E8,0x84E9,0x84EA,0x84EB,0x84ED,0xF999,/* 0x88-0x8F */
+ 0x84EF,0x84F1,0x84F2,0x84F3,0x84F4,0x84F5,0x84F6,0x84F7,/* 0x90-0x97 */
+ 0x84F8,0x84F9,0x84FA,0x84FB,0x84FD,0x84FE,0x8500,0x8501,/* 0x98-0x9F */
+ 0x8502,0x4F1E,0x6563,0x6851,0x55D3,0x4E27,0x6414,0x9A9A,/* 0xA0-0xA7 */
+ 0x626B,0x5AC2,0x745F,0x8272,0x6DA9,0x68EE,0x50E7,0x838E,/* 0xA8-0xAF */
+ 0x7802,0x6740,0x5239,0x6C99,0x7EB1,0x50BB,0x5565,0x715E,/* 0xB0-0xB7 */
+ 0x7B5B,0x6652,0x73CA,0x82EB,0x6749,0x5C71,0x5220,0x717D,/* 0xB8-0xBF */
+ 0x886B,0x95EA,0x9655,0x64C5,0x8D61,0x81B3,0x5584,0x6C55,/* 0xC0-0xC7 */
+ 0x6247,0x7F2E,0x5892,0x4F24,0x5546,0x8D4F,0x664C,0x4E0A,/* 0xC8-0xCF */
+ 0x5C1A,0x88F3,0x68A2,0x634E,0x7A0D,0x70E7,0x828D,0x52FA,/* 0xD0-0xD7 */
+ 0x97F6,0x5C11,0x54E8,0x90B5,0x7ECD,0x5962,0x8D4A,0x86C7,/* 0xD8-0xDF */
+ 0x820C,0x820D,0x8D66,0x6444,0x5C04,0x6151,0x6D89,0x793E,/* 0xE0-0xE7 */
+ 0x8BBE,0x7837,0x7533,0x547B,0x4F38,0x8EAB,0x6DF1,0x5A20,/* 0xE8-0xEF */
+ 0x7EC5,0xFA19,0xF972,0x5BA1,0x5A76,0x751A,0x80BE,0x614E,/* 0xF0-0xF7 */
+ 0x6E17,0x58F0,0x751F,0x7525,0x7272,0x5347,0x7EF3,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_CA[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x8503,0x8504,0x8505,0x8506,0x8507,0x8508,0x8509,0x850A,/* 0x40-0x47 */
+ 0x850B,0x850D,0x850E,0x850F,0x8510,0x8512,0x8514,0x8515,/* 0x48-0x4F */
+ 0x8516,0x8518,0x8519,0x851B,0x851C,0x851D,0x851E,0x8520,/* 0x50-0x57 */
+ 0x8522,0x8523,0x8524,0x8525,0x8526,0x8527,0x8528,0x8529,/* 0x58-0x5F */
+ 0x852A,0x852D,0x852E,0x852F,0x8530,0x8531,0x8532,0x8533,/* 0x60-0x67 */
+ 0x8534,0x8535,0x8536,0x853E,0x853F,0x8540,0x8541,0x8542,/* 0x68-0x6F */
+ 0x8544,0x8545,0x8546,0x8547,0x854B,0x854C,0x854D,0x854E,/* 0x70-0x77 */
+ 0x854F,0x8550,0x8551,0x8552,0x8553,0x8554,0x8555,0x0000,/* 0x78-0x7F */
+
+ 0x8557,0x8558,0x855A,0x855B,0x855C,0x855D,0x855F,0x8560,/* 0x80-0x87 */
+ 0x8561,0x8562,0x8563,0x8565,0x8566,0x8567,0x8569,0x856A,/* 0x88-0x8F */
+ 0x856B,0x856C,0x856D,0x856E,0x856F,0x8570,0x8571,0x8573,/* 0x90-0x97 */
+ 0x8575,0x8576,0x8577,0x8578,0x857C,0x857D,0x857F,0x8580,/* 0x98-0x9F */
+ 0x8581,0xF96D,0x76DB,0x5269,0x80DC,0x5723,0x5E08,0x5931,/* 0xA0-0xA7 */
+ 0x72EE,0x65BD,0x6E7F,0x8BD7,0x5C38,0x8671,0x5341,0x77F3,/* 0xA8-0xAF */
+ 0xF973,0x65F6,0xF9FD,0x98DF,0x8680,0x5B9E,0x8BC6,0x53F2,/* 0xB0-0xB7 */
+ 0x77E2,0x4F7F,0x5C4E,0x9A76,0x59CB,0x5F0F,0x793A,0x58EB,/* 0xB8-0xBF */
+ 0x4E16,0x67FF,0x4E8B,0x62ED,0x8A93,0x901D,0x52BF,0x662F,/* 0xC0-0xC7 */
+ 0x55DC,0x566C,0x9002,0x4ED5,0x4F8D,0x91CA,0x9970,0x6C0F,/* 0xC8-0xCF */
+ 0x5E02,0x6043,0x5BA4,0x89C6,0x8BD5,0x6536,0x624B,0x9996,/* 0xD0-0xD7 */
+ 0x5B88,0x5BFF,0x6388,0x552E,0x53D7,0x7626,0x517D,0x852C,/* 0xD8-0xDF */
+ 0x67A2,0x68B3,0x6B8A,0x6292,0x8F93,0x53D4,0x8212,0x6DD1,/* 0xE0-0xE7 */
+ 0x758F,0x4E66,0x8D4E,0x5B70,0x719F,0x85AF,0x6691,0x66D9,/* 0xE8-0xEF */
+ 0x7F72,0x8700,0x9ECD,0x9F20,0x5C5E,0x672F,0x8FF0,0x6811,/* 0xF0-0xF7 */
+ 0x675F,0x620D,0x7AD6,0x5885,0x5EB6,0x6570,0x6F31,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_CB[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x8582,0x8583,0x8586,0x8588,0x8589,0x858A,0x858B,0x858C,/* 0x40-0x47 */
+ 0x858D,0x858E,0x8590,0x8591,0x8592,0x8593,0x8594,0x8595,/* 0x48-0x4F */
+ 0x8596,0x8597,0x8598,0x8599,0x859A,0x859D,0x859E,0x859F,/* 0x50-0x57 */
+ 0x85A0,0x85A1,0x85A2,0x85A3,0x85A5,0x85A6,0x85A7,0x85A9,/* 0x58-0x5F */
+ 0x85AB,0x85AC,0x85AD,0x85B1,0x85B2,0x85B3,0x85B4,0x85B5,/* 0x60-0x67 */
+ 0x85B6,0x85B8,0x85BA,0x85BB,0x85BC,0x85BD,0x85BE,0x85BF,/* 0x68-0x6F */
+ 0x85C0,0x85C2,0x85C3,0x85C4,0x85C5,0x85C6,0x85C7,0x85C8,/* 0x70-0x77 */
+ 0x85CA,0x85CB,0x85CC,0xF923,0x85CE,0x85D1,0x85D2,0x0000,/* 0x78-0x7F */
+
+ 0x85D4,0x85D6,0x85D7,0x85D8,0x85D9,0x85DA,0x85DB,0x85DD,/* 0x80-0x87 */
+ 0x85DE,0x85DF,0x85E0,0x85E1,0x85E2,0x85E3,0x85E5,0x85E6,/* 0x88-0x8F */
+ 0x85E7,0x85E8,0x85EA,0x85EB,0x85EC,0x85ED,0x85EE,0x85EF,/* 0x90-0x97 */
+ 0x85F0,0x85F1,0x85F2,0x85F3,0x85F4,0x85F5,0x85F6,0x85F7,/* 0x98-0x9F */
+ 0x85F8,0x6055,0x5237,0x800D,0x6454,0x8870,0x7529,0x5E05,/* 0xA0-0xA7 */
+ 0x6813,0x62F4,0x971C,0x53CC,0x723D,0x8C01,0x6C34,0x7761,/* 0xA8-0xAF */
+ 0x7A0E,0x542E,0x77AC,0x987A,0x821C,0x8BF4,0x7855,0x6714,/* 0xB0-0xB7 */
+ 0x70C1,0x65AF,0x6495,0x5636,0x601D,0x79C1,0x53F8,0x4E1D,/* 0xB8-0xBF */
+ 0x6B7B,0x8086,0x5BFA,0x55E3,0x56DB,0x4F3A,0x4F3C,0x9972,/* 0xC0-0xC7 */
+ 0x5DF3,0x677E,0x8038,0x6002,0x9882,0x9001,0x5B8B,0x8BBC,/* 0xC8-0xCF */
+ 0x8BF5,0x641C,0x8258,0x64DE,0x55FD,0x82CF,0x9165,0x4FD7,/* 0xD0-0xD7 */
+ 0x7D20,0x901F,0x7C9F,0x50F3,0x5851,0x6EAF,0x5BBF,0x8BC9,/* 0xD8-0xDF */
+ 0x8083,0x9178,0x849C,0x7B97,0x867D,0x968B,0x968F,0x7EE5,/* 0xE0-0xE7 */
+ 0x9AD3,0x788E,0x5C81,0x7A57,0x9042,0x96A7,0x795F,0x5B59,/* 0xE8-0xEF */
+ 0x635F,0x7B0B,0x84D1,0x68AD,0x5506,0x7F29,0x7410,0xF96A,/* 0xF0-0xF7 */
+ 0x9501,0x6240,0x584C,0x4ED6,0x5B83,0x5979,0x5854,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_CC[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x85F9,0xF9F0,0x85FC,0x85FD,0x85FE,0x8600,0x8601,0x8602,/* 0x40-0x47 */
+ 0x8603,0x8604,0xF935,0x8607,0x8608,0x8609,0x860A,0x860B,/* 0x48-0x4F */
+ 0x860C,0x860D,0x860E,0x860F,0x8610,0x8612,0x8613,0x8614,/* 0x50-0x57 */
+ 0x8615,0x8617,0x8618,0x8619,0x861A,0x861B,0x861C,0x861D,/* 0x58-0x5F */
+ 0x861E,0x861F,0x8620,0x8621,0x8622,0x8623,0x8624,0x8625,/* 0x60-0x67 */
+ 0x8626,0x8628,0x862A,0x862B,0x862C,0xF91F,0x862E,0x862F,/* 0x68-0x6F */
+ 0x8630,0x8631,0x8632,0x8633,0x8634,0x8635,0x8636,0x8637,/* 0x70-0x77 */
+ 0x8639,0x863A,0x863B,0x863D,0x863E,0xF910,0x8640,0x0000,/* 0x78-0x7F */
+
+ 0x8641,0x8642,0x8643,0x8644,0x8645,0x8646,0x8647,0x8648,/* 0x80-0x87 */
+ 0x8649,0x864A,0x864B,0x864C,0x8652,0x8653,0x8655,0x8656,/* 0x88-0x8F */
+ 0x8657,0x8658,0x8659,0x865B,0xF936,0x865D,0x865F,0x8660,/* 0x90-0x97 */
+ 0x8661,0x8663,0x8664,0x8665,0x8666,0x8667,0x8668,0x8669,/* 0x98-0x9F */
+ 0x866A,0x736D,0x631E,0x8E4B,0x8E0F,0x80CE,0x82D4,0x62AC,/* 0xA0-0xA7 */
+ 0x53F0,0x6CF0,0x915E,0x592A,0x6001,0x6C70,0x574D,0x644A,/* 0xA8-0xAF */
+ 0x8D2A,0x762B,0x6EE9,0x575B,0x6A80,0x75F0,0x6F6D,0x8C2D,/* 0xB0-0xB7 */
+ 0x8C08,0x5766,0x6BEF,0x8892,0x78B3,0x63A2,0x53F9,0x70AD,/* 0xB8-0xBF */
+ 0x6C64,0x5858,0x642A,0x5802,0x68E0,0x819B,0x5510,0xFA03,/* 0xC0-0xC7 */
+ 0x5018,0x8EBA,0x6DCC,0x8D9F,0x70EB,0x638F,0x6D9B,0x6ED4,/* 0xC8-0xCF */
+ 0x7EE6,0x8404,0x6843,0x9003,0x6DD8,0x9676,0x8BA8,0x5957,/* 0xD0-0xD7 */
+ 0x7279,0x85E4,0x817E,0x75BC,0x8A8A,0x68AF,0x5254,0x8E22,/* 0xD8-0xDF */
+ 0x9511,0x63D0,0x9898,0x8E44,0x557C,0x4F53,0x66FF,0x568F,/* 0xE0-0xE7 */
+ 0x60D5,0x6D95,0x5243,0x5C49,0x5929,0x6DFB,0x586B,0x7530,/* 0xE8-0xEF */
+ 0x751C,0x606C,0x8214,0x8146,0x6311,0x6761,0x8FE2,0x773A,/* 0xF0-0xF7 */
+ 0x8DF3,0x8D34,0x94C1,0x5E16,0x5385,0x542C,0x70C3,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_CD[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x866D,0x866F,0x8670,0x8672,0x8673,0x8674,0x8675,0x8676,/* 0x40-0x47 */
+ 0x8677,0x8678,0x8683,0x8684,0x8685,0x8686,0x8687,0x8688,/* 0x48-0x4F */
+ 0x8689,0x868E,0x868F,0x8690,0x8691,0x8692,0x8694,0x8696,/* 0x50-0x57 */
+ 0x8697,0x8698,0x8699,0x869A,0x869B,0x869E,0x869F,0x86A0,/* 0x58-0x5F */
+ 0x86A1,0x86A2,0x86A5,0x86A6,0x86AB,0x86AD,0x86AE,0x86B2,/* 0x60-0x67 */
+ 0x86B3,0x86B7,0x86B8,0x86B9,0x86BB,0x86BC,0x86BD,0x86BE,/* 0x68-0x6F */
+ 0x86BF,0x86C1,0x86C2,0x86C3,0x86C5,0x86C8,0x86CC,0x86CD,/* 0x70-0x77 */
+ 0x86D2,0x86D3,0x86D5,0x86D6,0x86D7,0x86DA,0x86DC,0x0000,/* 0x78-0x7F */
+
+ 0x86DD,0x86E0,0x86E1,0x86E2,0x86E3,0x86E5,0x86E6,0x86E7,/* 0x80-0x87 */
+ 0x86E8,0x86EA,0x86EB,0x86EC,0x86EF,0x86F5,0x86F6,0x86F7,/* 0x88-0x8F */
+ 0x86FA,0x86FB,0x86FC,0x86FD,0x86FF,0x8701,0x8704,0x8705,/* 0x90-0x97 */
+ 0x8706,0x870B,0x870C,0x870E,0x870F,0x8710,0x8711,0x8714,/* 0x98-0x9F */
+ 0x8716,0x6C40,0x5EF7,0x505C,0x4EAD,0x5EAD,0x633A,0x8247,/* 0xA0-0xA7 */
+ 0x901A,0x6850,0x916E,0x77B3,0x540C,0x94DC,0x5F64,0x7AE5,/* 0xA8-0xAF */
+ 0x6876,0x6345,0x7B52,0x7EDF,0x75DB,0x5077,0x6295,0x5934,/* 0xB0-0xB7 */
+ 0x900F,0x51F8,0x79C3,0x7A81,0x56FE,0x5F92,0x9014,0x6D82,/* 0xB8-0xBF */
+ 0x5C60,0x571F,0x5410,0x5154,0x6E4D,0x56E2,0x63A8,0x9893,/* 0xC0-0xC7 */
+ 0x817F,0x8715,0x892A,0x9000,0x541E,0x5C6F,0x81C0,0x62D6,/* 0xC8-0xCF */
+ 0x6258,0x8131,0x9E35,0x9640,0x9A6E,0x9A7C,0x692D,0x59A5,/* 0xD0-0xD7 */
+ 0xFA02,0x553E,0x6316,0x54C7,0x86D9,0x6D3C,0x5A03,0x74E6,/* 0xD8-0xDF */
+ 0x889C,0x6B6A,0x5916,0x8C4C,0x5F2F,0x6E7E,0x73A9,0x987D,/* 0xE0-0xE7 */
+ 0x4E38,0x70F7,0x5B8C,0x7897,0x633D,0x665A,0x7696,0x60CB,/* 0xE8-0xEF */
+ 0x5B9B,0x5A49,0x4E07,0x8155,0x6C6A,0x738B,0x4EA1,0x6789,/* 0xF0-0xF7 */
+ 0x7F51,0x5F80,0x65FA,0x671B,0x5FD8,0x5984,0x5A01,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_CE[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x8719,0x871B,0x871D,0x871F,0x8720,0x8724,0x8726,0x8727,/* 0x40-0x47 */
+ 0x8728,0x872A,0x872B,0x872C,0x872D,0x872F,0x8730,0x8732,/* 0x48-0x4F */
+ 0x8733,0x8735,0x8736,0x8738,0x8739,0x873A,0x873C,0x873D,/* 0x50-0x57 */
+ 0x8740,0x8741,0x8742,0x8743,0x8744,0x8745,0x8746,0x874A,/* 0x58-0x5F */
+ 0x874B,0x874D,0x874F,0x8750,0x8751,0x8752,0x8754,0x8755,/* 0x60-0x67 */
+ 0x8756,0x8758,0x875A,0x875B,0x875C,0x875D,0x875E,0x875F,/* 0x68-0x6F */
+ 0x8761,0x8762,0x8766,0x8767,0x8768,0x8769,0x876A,0x876B,/* 0x70-0x77 */
+ 0x876C,0x876D,0x876F,0x8771,0x8772,0x8773,0x8775,0x0000,/* 0x78-0x7F */
+
+ 0x8777,0x8778,0x8779,0x877A,0x877F,0x8780,0x8781,0x8784,/* 0x80-0x87 */
+ 0x8786,0x8787,0x8789,0x878A,0x878C,0x878E,0x878F,0x8790,/* 0x88-0x8F */
+ 0x8791,0x8792,0x8794,0x8795,0x8796,0x8798,0x8799,0x879A,/* 0x90-0x97 */
+ 0x879B,0x879C,0x879D,0x879E,0x87A0,0x87A1,0x87A2,0x87A3,/* 0x98-0x9F */
+ 0x87A4,0x5DCD,0x5FAE,0x5371,0x97E6,0x8FDD,0x6845,0x56F4,/* 0xA0-0xA7 */
+ 0x552F,0x60DF,0x4E3A,0x6F4D,0x7EF4,0x82C7,0x840E,0x59D4,/* 0xA8-0xAF */
+ 0x4F1F,0x4F2A,0x5C3E,0x7EAC,0x672A,0x851A,0x5473,0x754F,/* 0xB0-0xB7 */
+ 0x80C3,0x5582,0x9B4F,0x4F4D,0x6E2D,0x8C13,0x5C09,0x6170,/* 0xB8-0xBF */
+ 0x536B,0x761F,0x6E29,0x868A,0x6587,0x95FB,0x7EB9,0x543B,/* 0xC0-0xC7 */
+ 0x7A33,0x7D0A,0x95EE,0x55E1,0x7FC1,0x74EE,0x631D,0x8717,/* 0xC8-0xCF */
+ 0x6DA1,0x7A9D,0x6211,0x65A1,0x5367,0x63E1,0x6C83,0x5DEB,/* 0xD0-0xD7 */
+ 0x545C,0x94A8,0x4E4C,0x6C61,0x8BEC,0x5C4B,0x65E0,0x829C,/* 0xD8-0xDF */
+ 0x68A7,0x543E,0x5434,0x6BCB,0x6B66,0x4E94,0x6342,0x5348,/* 0xE0-0xE7 */
+ 0x821E,0x4F0D,0x4FAE,0x575E,0x620A,0x96FE,0x6664,0x7269,/* 0xE8-0xEF */
+ 0x52FF,0x52A1,0x609F,0x8BEF,0x6614,0x7199,0x6790,0x897F,/* 0xF0-0xF7 */
+ 0x7852,0x77FD,0x6670,0x563B,0x5438,0x9521,0x727A,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_CF[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x87A5,0x87A6,0x87A7,0x87A9,0x87AA,0x87AE,0x87B0,0x87B1,/* 0x40-0x47 */
+ 0x87B2,0x87B4,0x87B6,0x87B7,0x87B8,0x87B9,0x87BB,0x87BC,/* 0x48-0x4F */
+ 0x87BE,0x87BF,0x87C1,0x87C2,0x87C3,0x87C4,0x87C5,0x87C7,/* 0x50-0x57 */
+ 0x87C8,0x87C9,0x87CC,0x87CD,0x87CE,0x87CF,0x87D0,0x87D4,/* 0x58-0x5F */
+ 0x87D5,0x87D6,0x87D7,0x87D8,0x87D9,0x87DA,0x87DC,0x87DD,/* 0x60-0x67 */
+ 0x87DE,0x87DF,0x87E1,0x87E2,0x87E3,0x87E4,0x87E6,0x87E7,/* 0x68-0x6F */
+ 0x87E8,0x87E9,0x87EB,0x87EC,0x87ED,0x87EF,0x87F0,0x87F1,/* 0x70-0x77 */
+ 0x87F2,0x87F3,0x87F4,0x87F5,0x87F6,0x87F7,0x87F8,0x0000,/* 0x78-0x7F */
+
+ 0x87FA,0x87FB,0x87FC,0x87FD,0x87FF,0x8800,0x8801,0x8802,/* 0x80-0x87 */
+ 0x8804,0x8805,0x8806,0x8807,0x8808,0x8809,0x880B,0x880C,/* 0x88-0x8F */
+ 0x880D,0x880E,0x880F,0x8810,0x8811,0x8812,0x8814,0x8817,/* 0x90-0x97 */
+ 0x8818,0x8819,0x881A,0x881C,0x881D,0x881E,0xF927,0x8820,/* 0x98-0x9F */
+ 0x8823,0x7A00,0x606F,0x5E0C,0x6089,0x819D,0x5915,0x60DC,/* 0xA0-0xA7 */
+ 0x7184,0x70EF,0x6EAA,0x6C50,0x7280,0x6A84,0x88AD,0x5E2D,/* 0xA8-0xAF */
+ 0x4E60,0x5AB3,0x559C,0x94E3,0x6D17,0x7CFB,0x9699,0x620F,/* 0xB0-0xB7 */
+ 0x7EC6,0x778E,0x867E,0x5323,0x971E,0x8F96,0x6687,0x5CE1,/* 0xB8-0xBF */
+ 0x4FA0,0x72ED,0x4E0B,0x53A6,0x590F,0x5413,0x6380,0x9528,/* 0xC0-0xC7 */
+ 0x5148,0x4ED9,0x9C9C,0x7EA4,0x54B8,0x8D24,0x8854,0x8237,/* 0xC8-0xCF */
+ 0x95F2,0x6D8E,0x5F26,0x5ACC,0x663E,0x9669,0x73B0,0x732E,/* 0xD0-0xD7 */
+ 0x53BF,0x817A,0x9985,0x7FA1,0x5BAA,0x9677,0x9650,0x7EBF,/* 0xD8-0xDF */
+ 0x76F8,0x53A2,0x9576,0x9999,0x7BB1,0x8944,0x6E58,0x4E61,/* 0xE0-0xE7 */
+ 0x7FD4,0xFA1A,0x8BE6,0x60F3,0x54CD,0x4EAB,0x9879,0x5DF7,/* 0xE8-0xEF */
+ 0x6A61,0x50CF,0x5411,0x8C61,0x8427,0x785D,0x9704,0x524A,/* 0xF0-0xF7 */
+ 0x54EE,0x56A3,0x9500,0x6D88,0x5BB5,0x6DC6,0x6653,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_D0[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x8824,0x8825,0x8826,0x8827,0x8828,0x8829,0x882A,0x882B,/* 0x40-0x47 */
+ 0x882C,0x882D,0x882E,0x882F,0x8830,0x8831,0x8833,0x8834,/* 0x48-0x4F */
+ 0x8835,0x8836,0x8837,0x8838,0x883A,0x883B,0x883D,0x883E,/* 0x50-0x57 */
+ 0x883F,0x8841,0x8842,0x8843,0x8846,0x8847,0x8848,0x8849,/* 0x58-0x5F */
+ 0x884A,0x884B,0x884E,0x884F,0x8850,0x8851,0x8852,0x8853,/* 0x60-0x67 */
+ 0x8855,0x8856,0x8858,0x885A,0x885B,0x885C,0x885D,0x885E,/* 0x68-0x6F */
+ 0x885F,0x8860,0x8866,0x8867,0x886A,0x886D,0x886F,0x8871,/* 0x70-0x77 */
+ 0x8873,0x8874,0x8875,0x8876,0x8878,0x8879,0x887A,0x0000,/* 0x78-0x7F */
+
+ 0x887B,0x887C,0x8880,0x8883,0x8886,0x8887,0x8889,0x888A,/* 0x80-0x87 */
+ 0x888C,0x888E,0x888F,0x8890,0x8891,0x8893,0x8894,0x8895,/* 0x88-0x8F */
+ 0x8897,0x8898,0x8899,0x889A,0x889B,0x889D,0x889E,0x889F,/* 0x90-0x97 */
+ 0x88A0,0x88A1,0x88A3,0x88A5,0x88A6,0x88A7,0x88A8,0x88A9,/* 0x98-0x9F */
+ 0x88AA,0x5C0F,0x5B5D,0x6821,0x8096,0x5578,0x7B11,0x6548,/* 0xA0-0xA7 */
+ 0x6954,0x4E9B,0x6B47,0x874E,0x978B,0x534F,0x631F,0x643A,/* 0xA8-0xAF */
+ 0x90AA,0x659C,0x80C1,0x8C10,0x5199,0x68B0,0x5378,0x87F9,/* 0xB0-0xB7 */
+ 0x61C8,0x6CC4,0x6CFB,0x8C22,0x5C51,0x85AA,0x82AF,0x950C,/* 0xB8-0xBF */
+ 0x6B23,0x8F9B,0x65B0,0x5FFB,0x5FC3,0x4FE1,0x8845,0x661F,/* 0xC0-0xC7 */
+ 0x8165,0x7329,0x60FA,0x5174,0x5211,0x578B,0x5F62,0x90A2,/* 0xC8-0xCF */
+ 0xFA08,0x9192,0x5E78,0x674F,0x6027,0x59D3,0x5144,0x51F6,/* 0xD0-0xD7 */
+ 0x80F8,0x5308,0x6C79,0x96C4,0x718A,0x4F11,0x4FEE,0x7F9E,/* 0xD8-0xDF */
+ 0x673D,0x55C5,0x9508,0x79C0,0x8896,0x7EE3,0x589F,0x620C,/* 0xE0-0xE7 */
+ 0x9700,0x865A,0x5618,0x987B,0x5F90,0x8BB8,0x84C4,0x9157,/* 0xE8-0xEF */
+ 0x53D9,0x65ED,0x5E8F,0x755C,0x6064,0x7D6E,0x5A7F,0x7EEA,/* 0xF0-0xF7 */
+ 0x7EED,0x8F69,0x55A7,0x5BA3,0x60AC,0x65CB,0x7384,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_D1[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x88AC,0x88AE,0x88AF,0x88B0,0x88B2,0x88B3,0x88B4,0x88B5,/* 0x40-0x47 */
+ 0x88B6,0x88B8,0x88B9,0x88BA,0x88BB,0x88BD,0x88BE,0x88BF,/* 0x48-0x4F */
+ 0x88C0,0x88C3,0x88C4,0x88C7,0x88C8,0x88CA,0x88CB,0x88CC,/* 0x50-0x57 */
+ 0x88CD,0x88CF,0x88D0,0x88D1,0x88D3,0x88D6,0x88D7,0x88DA,/* 0x58-0x5F */
+ 0x88DB,0x88DC,0x88DD,0x88DE,0x88E0,0xF9E8,0x88E6,0x88E7,/* 0x60-0x67 */
+ 0x88E9,0x88EA,0x88EB,0x88EC,0x88ED,0x88EE,0x88EF,0x88F2,/* 0x68-0x6F */
+ 0x88F5,0x88F6,0x88F7,0x88FA,0x88FB,0x88FD,0x88FF,0x8900,/* 0x70-0x77 */
+ 0x8901,0x8903,0x8904,0x8905,0x8906,0x8907,0x8908,0x0000,/* 0x78-0x7F */
+
+ 0x8909,0x890B,0x890C,0x890D,0x890E,0x890F,0x8911,0x8914,/* 0x80-0x87 */
+ 0x8915,0x8916,0x8917,0x8918,0x891C,0x891D,0x891E,0x891F,/* 0x88-0x8F */
+ 0x8920,0x8922,0x8923,0x8924,0x8926,0x8927,0x8928,0x8929,/* 0x90-0x97 */
+ 0x892C,0x892D,0x892E,0x892F,0x8931,0x8932,0x8933,0x8935,/* 0x98-0x9F */
+ 0x8937,0x9009,0x7663,0x7729,0x7EDA,0x9774,0x859B,0x5B66,/* 0xA0-0xA7 */
+ 0x7A74,0x96EA,0x8840,0x52CB,0x718F,0x5FAA,0x65EC,0x8BE2,/* 0xA8-0xAF */
+ 0x5BFB,0x9A6F,0x5DE1,0x6B89,0x6C5B,0x8BAD,0x8BAF,0x900A,/* 0xB0-0xB7 */
+ 0x8FC5,0x538B,0x62BC,0x9E26,0x9E2D,0x5440,0x4E2B,0x82BD,/* 0xB8-0xBF */
+ 0x7259,0x869C,0x5D16,0x8859,0x6DAF,0x96C5,0x54D1,0x4E9A,/* 0xC0-0xC7 */
+ 0x8BB6,0x7109,0xF99E,0x9609,0x70DF,0x6DF9,0x76D0,0x4E25,/* 0xC8-0xCF */
+ 0x7814,0x8712,0x5CA9,0x5EF6,0x8A00,0x989C,0x960E,0x708E,/* 0xD0-0xD7 */
+ 0x6CBF,0x5944,0x63A9,0x773C,0x884D,0x6F14,0x8273,0x5830,/* 0xD8-0xDF */
+ 0x71D5,0x538C,0x781A,0x96C1,0x5501,0x5F66,0x7130,0x5BB4,/* 0xE0-0xE7 */
+ 0x8C1A,0x9A8C,0x6B83,0x592E,0x9E2F,0x79E7,0x6768,0x626C,/* 0xE8-0xEF */
+ 0x4F6F,0x75A1,0x7F8A,0x6D0B,0x9633,0x6C27,0x4EF0,0x75D2,/* 0xF0-0xF7 */
+ 0x517B,0x6837,0x6F3E,0x9080,0x8170,0x5996,0x7476,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_D2[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x8938,0x8939,0x893A,0x893B,0x893C,0x893D,0x893E,0x893F,/* 0x40-0x47 */
+ 0x8940,0x8942,0x8943,0x8945,0x8946,0x8947,0x8948,0x8949,/* 0x48-0x4F */
+ 0x894A,0x894B,0x894C,0x894D,0x894E,0x894F,0x8950,0x8951,/* 0x50-0x57 */
+ 0x8952,0x8953,0x8954,0x8955,0x8956,0x8957,0x8958,0x8959,/* 0x58-0x5F */
+ 0x895A,0x895B,0x895C,0x895D,0x8960,0x8961,0x8962,0x8963,/* 0x60-0x67 */
+ 0xF924,0x8965,0x8967,0x8968,0x8969,0x896A,0x896B,0x896C,/* 0x68-0x6F */
+ 0x896D,0x896E,0x896F,0x8970,0x8971,0x8972,0x8973,0x8974,/* 0x70-0x77 */
+ 0x8975,0x8976,0x8977,0x8978,0x8979,0x897A,0x897C,0x0000,/* 0x78-0x7F */
+
+ 0x897D,0x897E,0x8980,0x8982,0x8984,0x8985,0x8987,0x8988,/* 0x80-0x87 */
+ 0x8989,0x898A,0xFA0A,0x898C,0x898D,0x898E,0x898F,0x8990,/* 0x88-0x8F */
+ 0x8991,0x8992,0x8993,0x8994,0x8995,0x8996,0x8997,0x8998,/* 0x90-0x97 */
+ 0x8999,0x899A,0x899B,0x899C,0x899D,0x899E,0x899F,0x89A0,/* 0x98-0x9F */
+ 0x89A1,0x6447,0x5C27,0x9065,0x7A91,0x8C23,0x59DA,0x54AC,/* 0xA0-0xA7 */
+ 0x8200,0x836F,0x8981,0x8000,0x6930,0x564E,0x8036,0x7237,/* 0xA8-0xAF */
+ 0x91CE,0x51B6,0x4E5F,0x9875,0x6396,0x4E1A,0x53F6,0x66F3,/* 0xB0-0xB7 */
+ 0x814B,0x591C,0x6DB2,0x4E00,0x58F9,0x533B,0x63D6,0x94F1,/* 0xB8-0xBF */
+ 0x4F9D,0x4F0A,0x8863,0x9890,0x5937,0x9057,0x79FB,0x4EEA,/* 0xC0-0xC7 */
+ 0x80F0,0x7591,0x6C82,0x5B9C,0x59E8,0x5F5D,0x6905,0x8681,/* 0xC8-0xCF */
+ 0x501A,0x5DF2,0x4E59,0x77E3,0x4EE5,0x827A,0x6291,0xF9E0,/* 0xD0-0xD7 */
+ 0x9091,0x5C79,0x4EBF,0x5F79,0x81C6,0xFA25,0x8084,0x75AB,/* 0xD8-0xDF */
+ 0x4EA6,0x88D4,0x610F,0x6BC5,0x5FC6,0x4E49,0xFA17,0x6EA2,/* 0xE0-0xE7 */
+ 0x8BE3,0x8BAE,0x8C0A,0x8BD1,0x5F02,0x7FFC,0x7FCC,0x7ECE,/* 0xE8-0xEF */
+ 0x8335,0x836B,0x56E0,0x6BB7,0x97F3,0x9634,0x59FB,0x541F,/* 0xF0-0xF7 */
+ 0x94F6,0x6DEB,0x5BC5,0x996E,0x5C39,0x5F15,0x9690,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_D3[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x89A2,0x89A3,0x89A4,0x89A5,0x89A6,0x89A7,0x89A8,0x89A9,/* 0x40-0x47 */
+ 0x89AA,0x89AB,0x89AC,0x89AD,0x89AE,0x89AF,0x89B0,0x89B1,/* 0x48-0x4F */
+ 0x89B2,0x89B3,0x89B4,0x89B5,0x89B6,0x89B7,0x89B8,0x89B9,/* 0x50-0x57 */
+ 0x89BA,0x89BB,0x89BC,0x89BD,0x89BE,0x89BF,0x89C0,0x89C3,/* 0x58-0x5F */
+ 0x89CD,0x89D3,0x89D4,0x89D5,0x89D7,0x89D8,0x89D9,0x89DB,/* 0x60-0x67 */
+ 0x89DD,0x89DF,0x89E0,0x89E1,0x89E2,0x89E4,0x89E7,0x89E8,/* 0x68-0x6F */
+ 0x89E9,0x89EA,0x89EC,0x89ED,0x89EE,0x89F0,0x89F1,0x89F2,/* 0x70-0x77 */
+ 0x89F4,0x89F5,0x89F6,0x89F7,0x89F8,0x89F9,0x89FA,0x0000,/* 0x78-0x7F */
+
+ 0x89FB,0x89FC,0x89FD,0x89FE,0x89FF,0x8A01,0x8A02,0x8A03,/* 0x80-0x87 */
+ 0x8A04,0x8A05,0x8A06,0x8A08,0x8A09,0x8A0A,0x8A0B,0x8A0C,/* 0x88-0x8F */
+ 0x8A0D,0x8A0E,0x8A0F,0x8A10,0x8A11,0x8A12,0x8A13,0x8A14,/* 0x90-0x97 */
+ 0x8A15,0x8A16,0x8A17,0x8A18,0x8A19,0x8A1A,0x8A1B,0x8A1C,/* 0x98-0x9F */
+ 0x8A1D,0x5370,0x82F1,0x6A31,0x5A74,0x9E70,0x5E94,0x7F28,/* 0xA0-0xA7 */
+ 0x83B9,0x8424,0x8425,0x8367,0x8747,0x8FCE,0x8D62,0x76C8,/* 0xA8-0xAF */
+ 0x5F71,0x9896,0x786C,0x6620,0x54DF,0x62E5,0x4F63,0x81C3,/* 0xB0-0xB7 */
+ 0x75C8,0x5EB8,0x96CD,0x8E0A,0x86F9,0x548F,0x6CF3,0x6D8C,/* 0xB8-0xBF */
+ 0x6C38,0x607F,0x52C7,0x7528,0x5E7D,0x4F18,0x60A0,0x5FE7,/* 0xC0-0xC7 */
+ 0x5C24,0x7531,0x90AE,0x94C0,0x72B9,0x6CB9,0x6E38,0x9149,/* 0xC8-0xCF */
+ 0x6709,0x53CB,0x53F3,0x4F51,0x91C9,0x8BF1,0x53C8,0x5E7C,/* 0xD0-0xD7 */
+ 0x8FC2,0x6DE4,0x4E8E,0x76C2,0x6986,0x865E,0x611A,0x8206,/* 0xD8-0xDF */
+ 0x4F59,0x4FDE,0x903E,0x9C7C,0x6109,0x6E1D,0x6E14,0x9685,/* 0xE0-0xE7 */
+ 0x4E88,0x5A31,0x96E8,0x4E0E,0x5C7F,0x79B9,0x5B87,0x8BED,/* 0xE8-0xEF */
+ 0xFA1E,0x7389,0x57DF,0x828B,0x90C1,0x5401,0x9047,0x55BB,/* 0xF0-0xF7 */
+ 0x5CEA,0x5FA1,0x6108,0x6B32,0x72F1,0x80B2,0x8A89,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_D4[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x8A1E,0x8A1F,0x8A20,0x8A21,0x8A22,0x8A23,0x8A24,0x8A25,/* 0x40-0x47 */
+ 0x8A26,0x8A27,0x8A28,0x8A29,0x8A2A,0x8A2B,0x8A2C,0x8A2D,/* 0x48-0x4F */
+ 0x8A2E,0x8A2F,0x8A30,0x8A31,0x8A32,0x8A33,0x8A34,0x8A35,/* 0x50-0x57 */
+ 0x8A36,0x8A37,0x8A38,0x8A39,0x8A3A,0x8A3B,0x8A3C,0x8A3D,/* 0x58-0x5F */
+ 0x8A3F,0x8A40,0x8A41,0x8A42,0x8A43,0x8A44,0x8A45,0x8A46,/* 0x60-0x67 */
+ 0x8A47,0x8A49,0x8A4A,0x8A4B,0x8A4C,0x8A4D,0x8A4E,0x8A4F,/* 0x68-0x6F */
+ 0x8A50,0x8A51,0x8A52,0x8A53,0x8A54,0x8A55,0x8A56,0x8A57,/* 0x70-0x77 */
+ 0x8A58,0x8A59,0x8A5A,0x8A5B,0x8A5C,0x8A5D,0x8A5E,0x0000,/* 0x78-0x7F */
+
+ 0x8A5F,0x8A60,0x8A61,0x8A62,0x8A63,0x8A64,0x8A65,0x8A66,/* 0x80-0x87 */
+ 0x8A67,0x8A68,0x8A69,0x8A6A,0x8A6B,0x8A6C,0x8A6D,0x8A6E,/* 0x88-0x8F */
+ 0x8A6F,0x8A70,0x8A71,0x8A72,0x8A73,0x8A74,0x8A75,0x8A76,/* 0x90-0x97 */
+ 0x8A77,0x8A78,0x8A7A,0x8A7B,0x8A7C,0x8A7D,0x8A7E,0x8A7F,/* 0x98-0x9F */
+ 0x8A80,0x6D74,0x5BD3,0x88D5,0x9884,0x8C6B,0x9A6D,0x9E33,/* 0xA0-0xA7 */
+ 0x6E0A,0x51A4,0x5143,0x57A3,0x8881,0x539F,0x63F4,0x8F95,/* 0xA8-0xAF */
+ 0x56ED,0x5458,0x5706,0x733F,0x6E90,0x7F18,0x8FDC,0x82D1,/* 0xB0-0xB7 */
+ 0x613F,0x6028,0x9662,0x66F0,0x7EA6,0x8D8A,0x8DC3,0x94A5,/* 0xB8-0xBF */
+ 0x5CB3,0x7CA4,0x6708,0x60A6,0x9605,0x8018,0x4E91,0x90E7,/* 0xC0-0xC7 */
+ 0x5300,0x9668,0x5141,0x8FD0,0x8574,0x915D,0x6655,0x97F5,/* 0xC8-0xCF */
+ 0x5B55,0x531D,0x7838,0x6742,0x683D,0x54C9,0x707E,0x5BB0,/* 0xD0-0xD7 */
+ 0x8F7D,0x518D,0x5728,0x54B1,0x6512,0x6682,0x8D5E,0x8D43,/* 0xD8-0xDF */
+ 0x810F,0x846C,0x906D,0x7CDF,0x51FF,0x85FB,0x67A3,0x65E9,/* 0xE0-0xE7 */
+ 0x6FA1,0x86A4,0x8E81,0x566A,0x9020,0x7682,0x7076,0x71E5,/* 0xE8-0xEF */
+ 0x8D23,0x62E9,0x5219,0x6CFD,0x8D3C,0x600E,0x589E,0x618E,/* 0xF0-0xF7 */
+ 0x66FE,0x8D60,0x624E,0x55B3,0x6E23,0x672D,0x8F67,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_D5[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x8A81,0x8A82,0x8A83,0x8A84,0x8A85,0x8A86,0x8A87,0x8A88,/* 0x40-0x47 */
+ 0x8A8B,0x8A8C,0x8A8D,0x8A8E,0x8A8F,0x8A90,0x8A91,0x8A92,/* 0x48-0x4F */
+ 0x8A94,0x8A95,0x8A96,0x8A97,0x8A98,0x8A99,0x8A9A,0x8A9B,/* 0x50-0x57 */
+ 0x8A9C,0x8A9D,0x8A9E,0x8A9F,0x8AA0,0x8AA1,0x8AA2,0x8AA3,/* 0x58-0x5F */
+ 0x8AA4,0x8AA5,0x8AA6,0x8AA7,0x8AA8,0x8AA9,0xF9A1,0x8AAB,/* 0x60-0x67 */
+ 0x8AAC,0x8AAD,0x8AAE,0x8AAF,0x8AB0,0x8AB1,0x8AB2,0x8AB3,/* 0x68-0x6F */
+ 0x8AB4,0x8AB5,0x8AB6,0x8AB7,0x8AB8,0x8AB9,0x8ABA,0x8ABB,/* 0x70-0x77 */
+ 0x8ABC,0x8ABD,0x8ABE,0x8ABF,0x8AC0,0x8AC1,0x8AC2,0x0000,/* 0x78-0x7F */
+
+ 0x8AC3,0x8AC4,0x8AC5,0x8AC6,0x8AC7,0x8AC8,0x8AC9,0x8ACA,/* 0x80-0x87 */
+ 0x8ACB,0x8ACC,0x8ACD,0x8ACE,0x8ACF,0x8AD0,0x8AD1,0xF97D,/* 0x88-0x8F */
+ 0x8AD3,0x8AD4,0x8AD5,0xF941,0x8AD7,0x8AD8,0x8AD9,0x8ADA,/* 0x90-0x97 */
+ 0x8ADB,0x8ADC,0x8ADD,0x8ADE,0x8ADF,0x8AE0,0x8AE1,0x8AE2,/* 0x98-0x9F */
+ 0x8AE3,0x94E1,0x95F8,0x7728,0x6805,0x69A8,0x548B,0x4E4D,/* 0xA0-0xA7 */
+ 0x70B8,0x8BC8,0x6458,0x658B,0xFA04,0x7A84,0x503A,0x5BE8,/* 0xA8-0xAF */
+ 0x77BB,0x6BE1,0x8A79,0x7C98,0x6CBE,0x76CF,0x65A9,0x8F97,/* 0xB0-0xB7 */
+ 0x5D2D,0x5C55,0x8638,0x6808,0x5360,0x6218,0x7AD9,0x6E5B,/* 0xB8-0xBF */
+ 0x7EFD,0x6A1F,0x7AE0,0x5F70,0x6F33,0x5F20,0x638C,0x6DA8,/* 0xC0-0xC7 */
+ 0x6756,0x4E08,0x5E10,0x8D26,0x4ED7,0x80C0,0x7634,0x969C,/* 0xC8-0xCF */
+ 0x62DB,0x662D,0x627E,0x6CBC,0x8D75,0x7167,0x7F69,0x5146,/* 0xD0-0xD7 */
+ 0x8087,0x53EC,0x906E,0x6298,0x54F2,0x86F0,0x8F99,0x8005,/* 0xD8-0xDF */
+ 0x9517,0x8517,0x8FD9,0x6D59,0x73CD,0x659F,0x771F,0x7504,/* 0xE0-0xE7 */
+ 0x7827,0x81FB,0x8D1E,0x9488,0x4FA6,0x6795,0x75B9,0x8BCA,/* 0xE8-0xEF */
+ 0x9707,0x632F,0x9547,0x9635,0x84B8,0x6323,0x7741,0x5F81,/* 0xF0-0xF7 */
+ 0x72F0,0x4E89,0x6014,0x6574,0x62EF,0x6B63,0x653F,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_D6[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x8AE4,0x8AE5,0x8AE6,0x8AE7,0x8AE8,0x8AE9,0x8AEA,0x8AEB,/* 0x40-0x47 */
+ 0x8AEC,0x8AED,0x8AEE,0x8AEF,0x8AF0,0x8AF1,0x8AF2,0x8AF3,/* 0x48-0x4F */
+ 0x8AF4,0x8AF5,0x8AF6,0x8AF7,0xFA22,0x8AF9,0x8AFA,0x8AFB,/* 0x50-0x57 */
+ 0x8AFC,0x8AFD,0xF95D,0x8AFF,0x8B00,0x8B01,0x8B02,0x8B03,/* 0x58-0x5F */
+ 0x8B04,0x8B05,0x8B06,0x8B08,0x8B09,0x8B0A,0x8B0B,0x8B0C,/* 0x60-0x67 */
+ 0x8B0D,0x8B0E,0x8B0F,0x8B10,0x8B11,0x8B12,0x8B13,0x8B14,/* 0x68-0x6F */
+ 0x8B15,0x8B16,0x8B17,0x8B18,0x8B19,0x8B1A,0x8B1B,0x8B1C,/* 0x70-0x77 */
+ 0x8B1D,0x8B1E,0x8B1F,0x8B20,0x8B21,0x8B22,0x8B23,0x0000,/* 0x78-0x7F */
+
+ 0x8B24,0x8B25,0x8B27,0x8B28,0x8B29,0x8B2A,0x8B2B,0x8B2C,/* 0x80-0x87 */
+ 0x8B2D,0x8B2E,0x8B2F,0x8B30,0x8B31,0x8B32,0x8B33,0x8B34,/* 0x88-0x8F */
+ 0x8B35,0x8B36,0x8B37,0x8B38,0x8B39,0x8B3A,0x8B3B,0x8B3C,/* 0x90-0x97 */
+ 0x8B3D,0x8B3E,0x8B3F,0x8B40,0x8B41,0x8B42,0x8B43,0x8B44,/* 0x98-0x9F */
+ 0x8B45,0x5E27,0x75C7,0x90D1,0x8BC1,0x829D,0x679D,0x652F,/* 0xA0-0xA7 */
+ 0x5431,0x8718,0x77E5,0x80A2,0x8102,0x6C41,0x4E4B,0x7EC7,/* 0xA8-0xAF */
+ 0x804C,0x76F4,0x690D,0x6B96,0x6267,0x503C,0x4F84,0x5740,/* 0xB0-0xB7 */
+ 0x6307,0x6B62,0x8DBE,0x53EA,0x65E8,0x7EB8,0x5FD7,0x631A,/* 0xB8-0xBF */
+ 0x63B7,0x81F3,0x81F4,0x7F6E,0x5E1C,0x5CD9,0x5236,0x667A,/* 0xC0-0xC7 */
+ 0x79E9,0x7A1A,0x8D28,0xF9FB,0x75D4,0x6EDE,0x6CBB,0x7A92,/* 0xC8-0xCF */
+ 0x4E2D,0x76C5,0x5FE0,0x949F,0x8877,0x7EC8,0x79CD,0x80BF,/* 0xD0-0xD7 */
+ 0x91CD,0x4EF2,0x4F17,0x821F,0x5468,0x5DDE,0x6D32,0x8BCC,/* 0xD8-0xDF */
+ 0x7CA5,0x8F74,0x8098,0x5E1A,0x5492,0x76B1,0x5B99,0x663C,/* 0xE0-0xE7 */
+ 0x9AA4,0x73E0,0x682A,0x86DB,0x6731,0x732A,0x8BF8,0x8BDB,/* 0xE8-0xEF */
+ 0x9010,0x7AF9,0x70DB,0x716E,0x62C4,0x77A9,0x5631,0x4E3B,/* 0xF0-0xF7 */
+ 0x8457,0x67F1,0x52A9,0x86C0,0x8D2E,0x94F8,0x7B51,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_D7[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x8B46,0x8B47,0x8B48,0x8B49,0x8B4A,0x8B4B,0x8B4C,0x8B4D,/* 0x40-0x47 */
+ 0x8B4E,0x8B4F,0x8B50,0x8B51,0x8B52,0x8B53,0x8B54,0x8B55,/* 0x48-0x4F */
+ 0x8B56,0x8B57,0xF9FC,0x8B59,0x8B5A,0x8B5B,0x8B5C,0x8B5D,/* 0x50-0x57 */
+ 0x8B5E,0x8B5F,0x8B60,0x8B61,0x8B62,0x8B63,0x8B64,0x8B65,/* 0x58-0x5F */
+ 0x8B67,0x8B68,0x8B69,0x8B6A,0x8B6B,0x8B6D,0x8B6E,0x8B6F,/* 0x60-0x67 */
+ 0x8B70,0x8B71,0x8B72,0x8B73,0x8B74,0x8B75,0x8B76,0x8B77,/* 0x68-0x6F */
+ 0x8B78,0x8B79,0x8B7A,0x8B7B,0x8B7C,0x8B7D,0x8B7E,0x8B7F,/* 0x70-0x77 */
+ 0xF95A,0x8B81,0x8B82,0x8B83,0x8B84,0x8B85,0x8B86,0x0000,/* 0x78-0x7F */
+
+ 0x8B87,0x8B88,0x8B89,0x8B8A,0x8B8B,0x8B8C,0x8B8D,0x8B8E,/* 0x80-0x87 */
+ 0x8B8F,0x8B90,0x8B91,0x8B92,0x8B93,0x8B94,0x8B95,0x8B96,/* 0x88-0x8F */
+ 0x8B97,0x8B98,0x8B99,0x8B9A,0x8B9B,0x8B9C,0x8B9D,0x8B9E,/* 0x90-0x97 */
+ 0x8B9F,0x8BAC,0x8BB1,0x8BBB,0x8BC7,0x8BD0,0x8BEA,0x8C09,/* 0x98-0x9F */
+ 0x8C1E,0x4F4F,0x6CE8,0x795D,0x9A7B,0x6293,0x722A,0x62FD,/* 0xA0-0xA7 */
+ 0x4E13,0x7816,0x8F6C,0x64B0,0x8D5A,0x7BC6,0x6869,0x5E84,/* 0xA8-0xAF */
+ 0x88C5,0x5986,0x649E,0x58EE,0x72B6,0x690E,0x9525,0x8FFD,/* 0xB0-0xB7 */
+ 0x8D58,0x5760,0x7F00,0x8C06,0x51C6,0x6349,0x62D9,0x5353,/* 0xB8-0xBF */
+ 0x684C,0x7422,0x8301,0x914C,0x5544,0x7740,0x707C,0x6D4A,/* 0xC0-0xC7 */
+ 0x5179,0x54A8,0x8D44,0x59FF,0x6ECB,0x6DC4,0x5B5C,0x7D2B,/* 0xC8-0xCF */
+ 0x4ED4,0x7C7D,0x6ED3,0x5B50,0x81EA,0x6E0D,0x5B57,0x9B03,/* 0xD0-0xD7 */
+ 0x68D5,0x8E2A,0x5B97,0x7EFC,0x603B,0x7EB5,0x90B9,0x8D70,/* 0xD8-0xDF */
+ 0x594F,0x63CD,0x79DF,0x8DB3,0x5352,0x65CF,0x7956,0x8BC5,/* 0xE0-0xE7 */
+ 0x963B,0x7EC4,0x94BB,0x7E82,0x5634,0x9189,0x6700,0x7F6A,/* 0xE8-0xEF */
+ 0x5C0A,0x9075,0x6628,0x5DE6,0x4F50,0x67DE,0x505A,0x4F5C,/* 0xF0-0xF7 */
+ 0x5750,0x5EA7,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_D8[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x8C38,0x8C39,0x8C3A,0x8C3B,0x8C3C,0x8C3D,0x8C3E,0x8C3F,/* 0x40-0x47 */
+ 0x8C40,0x8C42,0x8C43,0x8C44,0x8C45,0xF900,0x8C4A,0x8C4B,/* 0x48-0x4F */
+ 0x8C4D,0x8C4E,0x8C4F,0x8C50,0x8C51,0x8C52,0x8C53,0x8C54,/* 0x50-0x57 */
+ 0x8C56,0x8C57,0x8C58,0x8C59,0x8C5B,0x8C5C,0x8C5D,0x8C5E,/* 0x58-0x5F */
+ 0x8C5F,0x8C60,0x8C63,0x8C64,0x8C65,0x8C66,0x8C67,0x8C68,/* 0x60-0x67 */
+ 0x8C69,0xFA16,0x8C6D,0x8C6E,0x8C6F,0x8C70,0x8C71,0x8C72,/* 0x68-0x6F */
+ 0x8C74,0x8C75,0x8C76,0x8C77,0x8C7B,0x8C7C,0x8C7D,0x8C7E,/* 0x70-0x77 */
+ 0x8C7F,0x8C80,0x8C81,0x8C83,0x8C84,0x8C86,0x8C87,0x0000,/* 0x78-0x7F */
+
+ 0x8C88,0x8C8B,0x8C8D,0x8C8E,0x8C8F,0x8C90,0x8C91,0x8C92,/* 0x80-0x87 */
+ 0x8C93,0x8C95,0x8C96,0x8C97,0x8C99,0x8C9A,0x8C9B,0x8C9C,/* 0x88-0x8F */
+ 0x8C9D,0x8C9E,0x8C9F,0x8CA0,0x8CA1,0x8CA2,0x8CA3,0x8CA4,/* 0x90-0x97 */
+ 0x8CA5,0x8CA6,0x8CA7,0x8CA8,0x8CA9,0x8CAA,0x8CAB,0x8CAC,/* 0x98-0x9F */
+ 0x8CAD,0x4E8D,0x4E0C,0x5140,0x4E10,0x5EFF,0x5345,0x4E15,/* 0xA0-0xA7 */
+ 0x4E98,0x4E1E,0x9B32,0x5B6C,0x5669,0x4E28,0x79BA,0x4E3F,/* 0xA8-0xAF */
+ 0x5315,0x4E47,0x592D,0x723B,0x536E,0x6C10,0x56DF,0x80E4,/* 0xB0-0xB7 */
+ 0x9997,0x6BD3,0x777E,0x9F17,0x4E36,0x4E9F,0x9F10,0x4E5C,/* 0xB8-0xBF */
+ 0x4E69,0x4E93,0x8288,0x5B5B,0x556C,0x560F,0x4EC4,0x538D,/* 0xC0-0xC7 */
+ 0x539D,0x53A3,0x53A5,0x53AE,0x9765,0x8D5D,0x531A,0x53F5,/* 0xC8-0xCF */
+ 0x5326,0x532E,0x533E,0x8D5C,0x5366,0x5363,0x5202,0x5208,/* 0xD0-0xD7 */
+ 0x520E,0x522D,0x5233,0x523F,0x5240,0x524C,0x525E,0x5261,/* 0xD8-0xDF */
+ 0x525C,0x84AF,0x527D,0x5282,0x5281,0x5290,0x5293,0x5182,/* 0xE0-0xE7 */
+ 0x7F54,0x4EBB,0x4EC3,0x4EC9,0x4EC2,0x4EE8,0x4EE1,0x4EEB,/* 0xE8-0xEF */
+ 0x4EDE,0x4F1B,0x4EF3,0x4F22,0x4F64,0x4EF5,0x4F25,0x4F27,/* 0xF0-0xF7 */
+ 0x4F09,0x4F2B,0x4F5E,0x4F67,0x6538,0x4F5A,0x4F5D,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_D9[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x8CAE,0x8CAF,0x8CB0,0x8CB1,0x8CB2,0x8CB3,0x8CB4,0x8CB5,/* 0x40-0x47 */
+ 0x8CB6,0x8CB7,0x8CB8,0x8CB9,0x8CBA,0x8CBB,0x8CBC,0x8CBD,/* 0x48-0x4F */
+ 0x8CBE,0x8CBF,0x8CC0,0x8CC1,0xF948,0x8CC3,0x8CC4,0x8CC5,/* 0x50-0x57 */
+ 0x8CC6,0x8CC7,0xF903,0x8CC9,0x8CCA,0x8CCB,0x8CCC,0x8CCD,/* 0x58-0x5F */
+ 0x8CCE,0x8CCF,0x8CD0,0x8CD1,0x8CD2,0x8CD3,0x8CD4,0x8CD5,/* 0x60-0x67 */
+ 0x8CD6,0x8CD7,0x8CD8,0x8CD9,0x8CDA,0x8CDB,0x8CDC,0x8CDD,/* 0x68-0x6F */
+ 0x8CDE,0x8CDF,0x8CE0,0x8CE1,0x8CE2,0x8CE3,0x8CE4,0x8CE5,/* 0x70-0x77 */
+ 0x8CE6,0x8CE7,0x8CE8,0x8CE9,0x8CEA,0x8CEB,0x8CEC,0x0000,/* 0x78-0x7F */
+
+ 0x8CED,0x8CEE,0x8CEF,0x8CF0,0x8CF1,0x8CF2,0x8CF3,0x8CF4,/* 0x80-0x87 */
+ 0x8CF5,0x8CF6,0x8CF7,0x8CF8,0x8CF9,0x8CFA,0x8CFB,0x8CFC,/* 0x88-0x8F */
+ 0x8CFD,0x8CFE,0x8CFF,0x8D00,0x8D01,0x8D02,0x8D03,0x8D04,/* 0x90-0x97 */
+ 0x8D05,0x8D06,0x8D07,0x8D08,0x8D09,0x8D0A,0x8D0B,0x8D0C,/* 0x98-0x9F */
+ 0x8D0D,0x4F5F,0x4F57,0x4F32,0x4F3D,0x4F76,0x4F74,0x4F91,/* 0xA0-0xA7 */
+ 0x4F89,0x4F83,0x4F8F,0x4F7E,0x4F7B,0x4FAA,0x4F7C,0x4FAC,/* 0xA8-0xAF */
+ 0x4F94,0x4FE6,0x4FE8,0x4FEA,0x4FC5,0x4FDA,0x4FE3,0x4FDC,/* 0xB0-0xB7 */
+ 0x4FD1,0x4FDF,0x4FF8,0x5029,0x504C,0x4FF3,0x502C,0x500F,/* 0xB8-0xBF */
+ 0x502E,0x502D,0x4FFE,0x501C,0x500C,0x5025,0x5028,0x507E,/* 0xC0-0xC7 */
+ 0x5043,0x5055,0x5048,0x504E,0x506C,0x507B,0x50A5,0x50A7,/* 0xC8-0xCF */
+ 0x50A9,0x50BA,0x50D6,0x5106,0x50ED,0x50EC,0x50E6,0x50EE,/* 0xD0-0xD7 */
+ 0x5107,0x510B,0x4EDD,0x6C3D,0x4F58,0x4F65,0x4FCE,0x9FA0,/* 0xD8-0xDF */
+ 0x6C46,0x7C74,0x516E,0x5DFD,0x9EC9,0x9998,0x5181,0x5914,/* 0xE0-0xE7 */
+ 0x52F9,0x530D,0x8A07,0x5310,0x51EB,0x5919,0x5155,0x4EA0,/* 0xE8-0xEF */
+ 0x5156,0x4EB3,0x886E,0x88A4,0x4EB5,0x8114,0x88D2,0x7980,/* 0xF0-0xF7 */
+ 0x5B34,0x8803,0x7FB8,0x51AB,0x51B1,0x51BD,0x51BC,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_DA[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x8D0E,0x8D0F,0x8D10,0x8D11,0x8D12,0x8D13,0x8D14,0x8D15,/* 0x40-0x47 */
+ 0x8D16,0x8D17,0x8D18,0x8D19,0x8D1A,0x8D1B,0x8D1C,0x8D20,/* 0x48-0x4F */
+ 0x8D51,0x8D52,0x8D57,0x8D5F,0x8D65,0x8D68,0x8D69,0x8D6A,/* 0x50-0x57 */
+ 0x8D6C,0x8D6E,0x8D6F,0x8D71,0x8D72,0x8D78,0x8D79,0x8D7A,/* 0x58-0x5F */
+ 0x8D7B,0x8D7C,0x8D7D,0x8D7E,0x8D7F,0x8D80,0x8D82,0x8D83,/* 0x60-0x67 */
+ 0x8D86,0x8D87,0x8D88,0x8D89,0x8D8C,0x8D8D,0x8D8E,0x8D8F,/* 0x68-0x6F */
+ 0x8D90,0x8D92,0x8D93,0x8D95,0x8D96,0x8D97,0x8D98,0x8D99,/* 0x70-0x77 */
+ 0x8D9A,0x8D9B,0x8D9C,0x8D9D,0x8D9E,0x8DA0,0x8DA1,0x0000,/* 0x78-0x7F */
+
+ 0x8DA2,0x8DA4,0x8DA5,0x8DA6,0x8DA7,0x8DA8,0x8DA9,0x8DAA,/* 0x80-0x87 */
+ 0x8DAB,0x8DAC,0x8DAD,0x8DAE,0x8DAF,0x8DB0,0x8DB2,0x8DB6,/* 0x88-0x8F */
+ 0x8DB7,0x8DB9,0x8DBB,0x8DBD,0x8DC0,0x8DC1,0x8DC2,0x8DC5,/* 0x90-0x97 */
+ 0x8DC7,0x8DC8,0x8DC9,0x8DCA,0x8DCD,0x8DD0,0x8DD2,0x8DD3,/* 0x98-0x9F */
+ 0x8DD4,0x51C7,0x5196,0x51A2,0x51A5,0x8BA0,0x8BA6,0x8BA7,/* 0xA0-0xA7 */
+ 0x8BAA,0x8BB4,0x8BB5,0x8BB7,0x8BC2,0x8BC3,0x8BCB,0x8BCF,/* 0xA8-0xAF */
+ 0x8BCE,0x8BD2,0x8BD3,0x8BD4,0x8BD6,0x8BD8,0x8BD9,0x8BDC,/* 0xB0-0xB7 */
+ 0x8BDF,0x8BE0,0x8BE4,0x8BE8,0x8BE9,0x8BEE,0x8BF0,0x8BF3,/* 0xB8-0xBF */
+ 0x8BF6,0x8BF9,0x8BFC,0x8BFF,0x8C00,0x8C02,0x8C04,0x8C07,/* 0xC0-0xC7 */
+ 0x8C0C,0x8C0F,0x8C11,0x8C12,0x8C14,0x8C15,0x8C16,0x8C19,/* 0xC8-0xCF */
+ 0x8C1B,0x8C18,0x8C1D,0x8C1F,0x8C20,0x8C21,0x8C25,0x8C27,/* 0xD0-0xD7 */
+ 0x8C2A,0x8C2B,0x8C2E,0x8C2F,0x8C32,0x8C33,0x8C35,0x8C36,/* 0xD8-0xDF */
+ 0x5369,0x537A,0x961D,0x9622,0x9621,0x9631,0x962A,0x963D,/* 0xE0-0xE7 */
+ 0x963C,0x9642,0x9649,0x9654,0x965F,0x9667,0x966C,0x9672,/* 0xE8-0xEF */
+ 0x9674,0x9688,0x968D,0x9697,0x96B0,0x9097,0x909B,0x909D,/* 0xF0-0xF7 */
+ 0x9099,0x90AC,0x90A1,0x90B4,0x90B3,0x90B6,0x90BA,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_DB[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x8DD5,0x8DD8,0x8DD9,0x8DDC,0x8DE0,0x8DE1,0x8DE2,0x8DE5,/* 0x40-0x47 */
+ 0x8DE6,0x8DE7,0x8DE9,0x8DED,0x8DEE,0x8DF0,0x8DF1,0x8DF2,/* 0x48-0x4F */
+ 0x8DF4,0x8DF6,0x8DFC,0x8DFE,0x8DFF,0x8E00,0x8E01,0x8E02,/* 0x50-0x57 */
+ 0x8E03,0x8E04,0x8E06,0x8E07,0x8E08,0x8E0B,0x8E0D,0x8E0E,/* 0x58-0x5F */
+ 0x8E10,0x8E11,0x8E12,0x8E13,0x8E15,0x8E16,0x8E17,0x8E18,/* 0x60-0x67 */
+ 0x8E19,0x8E1A,0x8E1B,0x8E1C,0x8E20,0x8E21,0x8E24,0x8E25,/* 0x68-0x6F */
+ 0x8E26,0x8E27,0x8E28,0x8E2B,0x8E2D,0x8E30,0x8E32,0x8E33,/* 0x70-0x77 */
+ 0x8E34,0x8E36,0x8E37,0x8E38,0x8E3B,0x8E3C,0x8E3E,0x0000,/* 0x78-0x7F */
+
+ 0x8E3F,0x8E43,0x8E45,0x8E46,0x8E4C,0x8E4D,0x8E4E,0x8E4F,/* 0x80-0x87 */
+ 0x8E50,0x8E53,0x8E54,0x8E55,0x8E56,0x8E57,0x8E58,0x8E5A,/* 0x88-0x8F */
+ 0x8E5B,0x8E5C,0x8E5D,0x8E5E,0x8E5F,0x8E60,0x8E61,0x8E62,/* 0x90-0x97 */
+ 0x8E63,0x8E64,0x8E65,0x8E67,0x8E68,0x8E6A,0x8E6B,0x8E6E,/* 0x98-0x9F */
+ 0x8E71,0x90B8,0x90B0,0x90CF,0x90C5,0x90BE,0x90D0,0x90C4,/* 0xA0-0xA7 */
+ 0x90C7,0x90D3,0x90E6,0x90E2,0x90DC,0x90D7,0x90DB,0x90EB,/* 0xA8-0xAF */
+ 0x90EF,0x90FE,0x9104,0x9122,0x911E,0x9123,0x9131,0x912F,/* 0xB0-0xB7 */
+ 0x9139,0x9143,0x9146,0x520D,0x5942,0x52A2,0x52AC,0x52AD,/* 0xB8-0xBF */
+ 0x52BE,0x54FF,0x52D0,0x52D6,0x52F0,0x53DF,0x71EE,0x77CD,/* 0xC0-0xC7 */
+ 0x5EF4,0x51F5,0x51FC,0x9B2F,0x53B6,0x5F01,0x755A,0x5DEF,/* 0xC8-0xCF */
+ 0x574C,0x57A9,0x57A1,0x587E,0x58BC,0x58C5,0x58D1,0x5729,/* 0xD0-0xD7 */
+ 0x572C,0x572A,0x5733,0x5739,0x572E,0x572F,0x575C,0x573B,/* 0xD8-0xDF */
+ 0x5742,0x5769,0x5785,0x576B,0x5786,0x577C,0x577B,0x5768,/* 0xE0-0xE7 */
+ 0x576D,0x5776,0x5773,0x57AD,0x57A4,0x578C,0x57B2,0x57CF,/* 0xE8-0xEF */
+ 0x57A7,0x57B4,0x5793,0x57A0,0x57D5,0x57D8,0x57DA,0x57D9,/* 0xF0-0xF7 */
+ 0x57D2,0x57B8,0x57F4,0x57EF,0x57F8,0x57E4,0x57DD,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_DC[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x8E73,0x8E75,0x8E77,0x8E78,0x8E79,0x8E7A,0x8E7B,0x8E7D,/* 0x40-0x47 */
+ 0x8E7E,0x8E80,0x8E82,0x8E83,0x8E84,0x8E86,0x8E88,0x8E89,/* 0x48-0x4F */
+ 0x8E8A,0x8E8B,0x8E8C,0x8E8D,0x8E8E,0x8E91,0x8E92,0x8E93,/* 0x50-0x57 */
+ 0x8E95,0x8E96,0x8E97,0x8E98,0x8E99,0x8E9A,0x8E9B,0x8E9D,/* 0x58-0x5F */
+ 0x8E9F,0x8EA0,0x8EA1,0x8EA2,0x8EA3,0x8EA4,0x8EA5,0x8EA6,/* 0x60-0x67 */
+ 0x8EA7,0x8EA8,0x8EA9,0x8EAA,0x8EAD,0x8EAE,0x8EB0,0x8EB1,/* 0x68-0x6F */
+ 0x8EB3,0x8EB4,0x8EB5,0x8EB6,0x8EB7,0x8EB8,0x8EB9,0x8EBB,/* 0x70-0x77 */
+ 0x8EBC,0x8EBD,0x8EBE,0x8EBF,0x8EC0,0x8EC1,0x8EC2,0x0000,/* 0x78-0x7F */
+
+ 0x8EC3,0x8EC4,0x8EC5,0x8EC6,0x8EC7,0x8EC8,0x8EC9,0xF902,/* 0x80-0x87 */
+ 0x8ECB,0x8ECC,0x8ECD,0x8ECF,0x8ED0,0x8ED1,0x8ED2,0x8ED3,/* 0x88-0x8F */
+ 0x8ED4,0x8ED5,0x8ED6,0x8ED7,0x8ED8,0x8ED9,0x8EDA,0x8EDB,/* 0x90-0x97 */
+ 0x8EDC,0x8EDD,0x8EDE,0x8EDF,0x8EE0,0x8EE1,0x8EE2,0x8EE3,/* 0x98-0x9F */
+ 0x8EE4,0x580B,0x580D,0x57FD,0x57ED,0x5800,0x581E,0x5819,/* 0xA0-0xA7 */
+ 0x5844,0x5820,0x5865,0x586C,0x5881,0x5889,0x589A,0x5880,/* 0xA8-0xAF */
+ 0x99A8,0x9F19,0x61FF,0x8279,0x827D,0x827F,0x828F,0x828A,/* 0xB0-0xB7 */
+ 0x82A8,0x8284,0x828E,0x8291,0x8297,0x8299,0x82AB,0x82B8,/* 0xB8-0xBF */
+ 0x82BE,0x82B0,0x82C8,0x82CA,0x82E3,0x8298,0x82B7,0x82AE,/* 0xC0-0xC7 */
+ 0x82CB,0x82CC,0x82C1,0x82A9,0x82B4,0x82A1,0x82AA,0x829F,/* 0xC8-0xCF */
+ 0x82C4,0x82CE,0x82A4,0x82E1,0x8309,0x82F7,0x82E4,0x830F,/* 0xD0-0xD7 */
+ 0x8307,0x82DC,0x82F4,0x82D2,0x82D8,0x830C,0x82FB,0x82D3,/* 0xD8-0xDF */
+ 0x8311,0x831A,0x8306,0x8314,0x8315,0x82E0,0x82D5,0x831C,/* 0xE0-0xE7 */
+ 0x8351,0x835B,0x835C,0x8308,0x8392,0x833C,0x8334,0x8331,/* 0xE8-0xEF */
+ 0x839B,0x835E,0x832F,0x834F,0x8347,0x8343,0x835F,0x8340,/* 0xF0-0xF7 */
+ 0x8317,0x8360,0x832D,0x833A,0x8333,0x8366,0x8365,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_DD[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x8EE5,0x8EE6,0x8EE7,0x8EE8,0x8EE9,0x8EEA,0x8EEB,0x8EEC,/* 0x40-0x47 */
+ 0x8EED,0x8EEE,0x8EEF,0x8EF0,0x8EF1,0x8EF2,0x8EF3,0x8EF4,/* 0x48-0x4F */
+ 0x8EF5,0x8EF6,0x8EF7,0x8EF8,0x8EF9,0x8EFA,0x8EFB,0x8EFC,/* 0x50-0x57 */
+ 0x8EFD,0x8EFE,0x8EFF,0x8F00,0x8F01,0x8F02,0x8F03,0x8F04,/* 0x58-0x5F */
+ 0x8F05,0x8F06,0x8F07,0x8F08,0x8F09,0x8F0A,0x8F0B,0x8F0C,/* 0x60-0x67 */
+ 0x8F0D,0x8F0E,0x8F0F,0x8F10,0x8F11,0x8F12,0x8F13,0x8F14,/* 0x68-0x6F */
+ 0x8F15,0x8F16,0x8F17,0x8F18,0x8F19,0x8F1A,0x8F1B,0x8F1C,/* 0x70-0x77 */
+ 0x8F1D,0x8F1E,0x8F1F,0x8F20,0x8F21,0x8F22,0x8F23,0x0000,/* 0x78-0x7F */
+
+ 0x8F24,0x8F25,0xF998,0x8F27,0x8F28,0x8F29,0xF9D7,0x8F2B,/* 0x80-0x87 */
+ 0x8F2C,0x8F2D,0x8F2E,0x8F2F,0x8F30,0x8F31,0x8F32,0x8F33,/* 0x88-0x8F */
+ 0x8F34,0x8F35,0x8F36,0x8F37,0x8F38,0x8F39,0x8F3A,0xFA07,/* 0x90-0x97 */
+ 0x8F3C,0x8F3D,0x8F3E,0x8F3F,0x8F40,0x8F41,0x8F42,0x8F43,/* 0x98-0x9F */
+ 0x8F44,0x8368,0x831B,0x8369,0x836C,0x836A,0x836D,0x836E,/* 0xA0-0xA7 */
+ 0x83B0,0x8378,0x83B3,0x83B4,0x83A0,0x83AA,0x8393,0x839C,/* 0xA8-0xAF */
+ 0x8385,0x837C,0x83B6,0x83A9,0x837D,0x83B8,0x837B,0x8398,/* 0xB0-0xB7 */
+ 0x839E,0x83A8,0x83BA,0x83BC,0x83C1,0x8401,0x83E5,0x83D8,/* 0xB8-0xBF */
+ 0x5807,0x8418,0x840B,0x83DD,0x83FD,0x83D6,0x841C,0x8438,/* 0xC0-0xC7 */
+ 0x8411,0x8406,0x83D4,0x83DF,0x840F,0x8403,0x83F8,0x83F9,/* 0xC8-0xCF */
+ 0x83EA,0x83C5,0x83C0,0x8426,0x83F0,0x83E1,0x845C,0x8451,/* 0xD0-0xD7 */
+ 0x845A,0x8459,0x8473,0x8487,0x8488,0x847A,0x8489,0x8478,/* 0xD8-0xDF */
+ 0x843C,0x8446,0x8469,0x8476,0x848C,0x848E,0x8431,0x846D,/* 0xE0-0xE7 */
+ 0x84C1,0x84CD,0x84D0,0x84E6,0x84BD,0x84D3,0x84CA,0x84BF,/* 0xE8-0xEF */
+ 0x84BA,0x84E0,0x84A1,0x84B9,0x84B4,0x8497,0x84E5,0x84E3,/* 0xF0-0xF7 */
+ 0x850C,0x750D,0x8538,0x84F0,0x8539,0x851F,0x853A,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_DE[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x8F45,0x8F46,0x8F47,0x8F48,0x8F49,0x8F4A,0x8F4B,0x8F4C,/* 0x40-0x47 */
+ 0x8F4D,0x8F4E,0x8F4F,0x8F50,0x8F51,0x8F52,0x8F53,0x8F54,/* 0x48-0x4F */
+ 0x8F55,0x8F56,0x8F57,0x8F58,0x8F59,0x8F5A,0x8F5B,0x8F5C,/* 0x50-0x57 */
+ 0x8F5D,0x8F5E,0x8F5F,0x8F60,0x8F61,0xF98D,0x8F63,0x8F64,/* 0x58-0x5F */
+ 0x8F65,0x8F6A,0x8F80,0x8F8C,0x8F92,0x8F9D,0x8FA0,0x8FA1,/* 0x60-0x67 */
+ 0x8FA2,0x8FA4,0x8FA5,0x8FA6,0x8FA7,0x8FAA,0x8FAC,0x8FAD,/* 0x68-0x6F */
+ 0x8FAE,0x8FAF,0x8FB2,0x8FB3,0x8FB4,0x8FB5,0x8FB7,0x8FB8,/* 0x70-0x77 */
+ 0x8FBA,0x8FBB,0x8FBC,0x8FBF,0x8FC0,0x8FC3,0x8FC6,0x0000,/* 0x78-0x7F */
+
+ 0x8FC9,0x8FCA,0x8FCB,0x8FCC,0x8FCD,0x8FCF,0x8FD2,0x8FD6,/* 0x80-0x87 */
+ 0x8FD7,0x8FDA,0x8FE0,0x8FE1,0x8FE3,0x8FE7,0x8FEC,0x8FEF,/* 0x88-0x8F */
+ 0x8FF1,0x8FF2,0x8FF4,0x8FF5,0x8FF6,0x8FFA,0x8FFB,0x8FFC,/* 0x90-0x97 */
+ 0x8FFE,0x8FFF,0x9007,0x9008,0x900C,0x900E,0x9013,0x9015,/* 0x98-0x9F */
+ 0x9018,0x8556,0x853B,0x84FF,0xF9C2,0x8559,0x8548,0x8568,/* 0xA0-0xA7 */
+ 0x8564,0x855E,0x857A,0x77A2,0x8543,0x8572,0x857B,0x85A4,/* 0xA8-0xAF */
+ 0x85A8,0x8587,0x858F,0x8579,0x85AE,0x859C,0x8585,0x85B9,/* 0xB0-0xB7 */
+ 0x85B7,0x85B0,0x85D3,0x85C1,0x85DC,0x85FF,0x8627,0x8605,/* 0xB8-0xBF */
+ 0x8629,0x8616,0x863C,0x5EFE,0x5F08,0x593C,0x5941,0x8037,/* 0xC0-0xC7 */
+ 0x5955,0x595A,0x5958,0x530F,0x5C22,0x5C25,0x5C2C,0x5C34,/* 0xC8-0xCF */
+ 0x624C,0x626A,0x629F,0x62BB,0x62CA,0x62DA,0x62D7,0x62EE,/* 0xD0-0xD7 */
+ 0x6322,0x62F6,0x6339,0x634B,0x6343,0x63AD,0x63F6,0x6371,/* 0xD8-0xDF */
+ 0x637A,0x638E,0x63B4,0x636D,0x63AC,0x638A,0x6369,0x63AE,/* 0xE0-0xE7 */
+ 0x63BC,0x63F2,0x63F8,0x63E0,0x63FF,0x63C4,0x63DE,0x63CE,/* 0xE8-0xEF */
+ 0x6452,0x63C6,0x63BE,0x6445,0x6441,0x640B,0x641B,0x6420,/* 0xF0-0xF7 */
+ 0x640C,0x6426,0x6421,0x645E,0x6484,0x646D,0x6496,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_DF[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x9019,0x901C,0xF99A,0x9024,0x9025,0x9027,0x9028,0x9029,/* 0x40-0x47 */
+ 0x902A,0x902B,0x902C,0x9030,0x9031,0x9032,0x9033,0x9034,/* 0x48-0x4F */
+ 0x9037,0x9039,0x903A,0x903D,0x903F,0x9040,0x9043,0x9045,/* 0x50-0x57 */
+ 0x9046,0x9048,0x9049,0x904A,0x904B,0x904C,0x904E,0x9054,/* 0x58-0x5F */
+ 0x9055,0x9056,0x9059,0x905A,0x905C,0x905D,0x905E,0x905F,/* 0x60-0x67 */
+ 0x9060,0x9061,0x9064,0x9066,0x9067,0x9069,0x906A,0x906B,/* 0x68-0x6F */
+ 0x906C,0x906F,0x9070,0x9071,0x9072,0x9073,0x9076,0x9077,/* 0x70-0x77 */
+ 0x9078,0x9079,0x907A,0x907B,0xF9C3,0x907E,0x9081,0x0000,/* 0x78-0x7F */
+
+ 0x9084,0x9085,0x9086,0x9087,0x9089,0x908A,0x908C,0x908D,/* 0x80-0x87 */
+ 0x908E,0xF913,0x9090,0x9092,0x9094,0x9096,0x9098,0x909A,/* 0x88-0x8F */
+ 0x909C,0x909E,0x909F,0x90A0,0x90A4,0x90A5,0x90A7,0x90A8,/* 0x90-0x97 */
+ 0x90A9,0x90AB,0x90AD,0x90B2,0x90B7,0x90BC,0x90BD,0x90BF,/* 0x98-0x9F */
+ 0x90C0,0x647A,0x64B7,0x64B8,0x6499,0x64BA,0x64C0,0x64D0,/* 0xA0-0xA7 */
+ 0x64D7,0x64E4,0x64E2,0x6509,0x6525,0x652E,0x5F0B,0x5FD2,/* 0xA8-0xAF */
+ 0x7519,0x5F11,0x535F,0x53F1,0x53FD,0x53E9,0x53E8,0x53FB,/* 0xB0-0xB7 */
+ 0x5412,0x5416,0x5406,0x544B,0x5452,0x5453,0x5454,0x5456,/* 0xB8-0xBF */
+ 0x5443,0x5421,0x5457,0x5459,0x5423,0x5432,0x5482,0x5494,/* 0xC0-0xC7 */
+ 0x5477,0x5471,0x5464,0x549A,0x549B,0x5484,0x5476,0x5466,/* 0xC8-0xCF */
+ 0x549D,0x54D0,0x54AD,0x54C2,0x54B4,0x54D2,0x54A7,0x54A6,/* 0xD0-0xD7 */
+ 0x54D3,0x54D4,0x5472,0x54A3,0x54D5,0x54BB,0x54BF,0x54CC,/* 0xD8-0xDF */
+ 0x54D9,0x54DA,0x54DC,0x54A9,0x54AA,0x54A4,0x54DD,0x54CF,/* 0xE0-0xE7 */
+ 0x54DE,0x551B,0x54E7,0x5520,0x54FD,0x5514,0x54F3,0x5522,/* 0xE8-0xEF */
+ 0x5523,0x550F,0x5511,0x5527,0x552A,0x5567,0x558F,0x55B5,/* 0xF0-0xF7 */
+ 0x5549,0x556D,0x5541,0x5555,0x553F,0x5550,0x553C,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E0[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x90C2,0x90C3,0x90C6,0x90C8,0x90C9,0x90CB,0x90CC,0x90CD,/* 0x40-0x47 */
+ 0x90D2,0x90D4,0x90D5,0x90D6,0x90D8,0x90D9,0x90DA,0x90DE,/* 0x48-0x4F */
+ 0x90DF,0x90E0,0x90E3,0x90E4,0x90E5,0x90E9,0x90EA,0x90EC,/* 0x50-0x57 */
+ 0x90EE,0x90F0,0x90F1,0x90F2,0x90F3,0x90F5,0x90F6,0x90F7,/* 0x58-0x5F */
+ 0x90F9,0x90FA,0x90FB,0x90FC,0x90FF,0x9100,0x9101,0x9103,/* 0x60-0x67 */
+ 0x9105,0x9106,0x9107,0x9108,0x9109,0x910A,0x910B,0x910C,/* 0x68-0x6F */
+ 0x910D,0x910E,0x910F,0x9110,0x9111,0x9112,0x9113,0x9114,/* 0x70-0x77 */
+ 0x9115,0x9116,0x9117,0x9118,0x911A,0x911B,0x911C,0x0000,/* 0x78-0x7F */
+
+ 0x911D,0x911F,0x9120,0x9121,0x9124,0x9125,0x9126,0x9127,/* 0x80-0x87 */
+ 0x9128,0x9129,0x912A,0x912B,0x912C,0x912D,0x912E,0x9130,/* 0x88-0x8F */
+ 0x9132,0x9133,0x9134,0x9135,0x9136,0x9137,0x9138,0x913A,/* 0x90-0x97 */
+ 0x913B,0x913C,0x913D,0x913E,0x913F,0x9140,0x9141,0x9142,/* 0x98-0x9F */
+ 0x9144,0x5537,0x5556,0x5575,0x5576,0x5577,0x5533,0x5530,/* 0xA0-0xA7 */
+ 0x555C,0x558B,0x55D2,0x5583,0x55B1,0x55B9,0x5588,0x5581,/* 0xA8-0xAF */
+ 0x559F,0x557E,0x55D6,0x5591,0x557B,0x55DF,0x55BD,0x55BE,/* 0xB0-0xB7 */
+ 0x5594,0x5599,0x55EA,0x55F7,0x55C9,0x561F,0x55D1,0x55EB,/* 0xB8-0xBF */
+ 0x55EC,0x55D4,0x55E6,0x55DD,0x55C4,0x55EF,0x55E5,0x55F2,/* 0xC0-0xC7 */
+ 0x55F3,0x55CC,0x55CD,0x55E8,0x55F5,0x55E4,0x8F94,0x561E,/* 0xC8-0xCF */
+ 0x5608,0x560C,0x5601,0x5624,0x5623,0x55FE,0x5600,0x5627,/* 0xD0-0xD7 */
+ 0x562D,0x5658,0x5639,0x5657,0x562C,0x564D,0x5662,0x5659,/* 0xD8-0xDF */
+ 0x565C,0x564C,0x5654,0x5686,0x5664,0x5671,0x566B,0x567B,/* 0xE0-0xE7 */
+ 0x567C,0x5685,0x5693,0x56AF,0x56D4,0x56D7,0x56DD,0x56E1,/* 0xE8-0xEF */
+ 0x56F5,0x56EB,0xF9A9,0x56FF,0x5704,0x570A,0x5709,0x571C,/* 0xF0-0xF7 */
+ 0x5E0F,0x5E19,0x5E14,0x5E11,0x5E31,0x5E3B,0x5E3C,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E1[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x9145,0x9147,0x9148,0x9151,0x9153,0x9154,0x9155,0x9156,/* 0x40-0x47 */
+ 0x9158,0x9159,0x915B,0x915C,0x915F,0x9160,0x9166,0x9167,/* 0x48-0x4F */
+ 0x9168,0x916B,0x916D,0x9173,0x917A,0x917B,0x917C,0x9180,/* 0x50-0x57 */
+ 0x9181,0x9182,0x9183,0x9184,0x9186,0x9188,0x918A,0x918E,/* 0x58-0x5F */
+ 0x918F,0x9193,0x9194,0x9195,0x9196,0x9197,0x9198,0x9199,/* 0x60-0x67 */
+ 0x919C,0x919D,0x919E,0x919F,0x91A0,0x91A1,0x91A4,0x91A5,/* 0x68-0x6F */
+ 0x91A6,0x91A7,0x91A8,0x91A9,0x91AB,0x91AC,0x91B0,0x91B1,/* 0x70-0x77 */
+ 0x91B2,0x91B3,0x91B6,0x91B7,0x91B8,0x91B9,0x91BB,0x0000,/* 0x78-0x7F */
+
+ 0x91BC,0x91BD,0x91BE,0x91BF,0x91C0,0x91C1,0x91C2,0x91C3,/* 0x80-0x87 */
+ 0x91C4,0x91C5,0x91C6,0x91C8,0x91CB,0x91D0,0x91D2,0x91D3,/* 0x88-0x8F */
+ 0x91D4,0x91D5,0x91D6,0x91D7,0x91D8,0x91D9,0x91DA,0x91DB,/* 0x90-0x97 */
+ 0x91DD,0x91DE,0x91DF,0x91E0,0x91E1,0x91E2,0x91E3,0x91E4,/* 0x98-0x9F */
+ 0x91E5,0x5E37,0x5E44,0x5E54,0x5E5B,0x5E5E,0x5E61,0x5C8C,/* 0xA0-0xA7 */
+ 0x5C7A,0x5C8D,0x5C90,0x5C96,0x5C88,0x5C98,0x5C99,0x5C91,/* 0xA8-0xAF */
+ 0x5C9A,0x5C9C,0x5CB5,0x5CA2,0x5CBD,0x5CAC,0x5CAB,0x5CB1,/* 0xB0-0xB7 */
+ 0x5CA3,0x5CC1,0x5CB7,0x5CC4,0x5CD2,0x5CE4,0x5CCB,0x5CE5,/* 0xB8-0xBF */
+ 0x5D02,0x5D03,0x5D27,0x5D26,0x5D2E,0x5D24,0x5D1E,0x5D06,/* 0xC0-0xC7 */
+ 0x5D1B,0x5D58,0x5D3E,0x5D34,0x5D3D,0x5D6C,0x5D5B,0x5D6F,/* 0xC8-0xCF */
+ 0x5D5D,0x5D6B,0x5D4B,0x5D4A,0x5D69,0x5D74,0x5D82,0x5D99,/* 0xD0-0xD7 */
+ 0x5D9D,0x8C73,0x5DB7,0x5DC5,0x5F73,0x5F77,0x5F82,0x5F87,/* 0xD8-0xDF */
+ 0x5F89,0x5F8C,0x5F95,0x5F99,0x5F9C,0x5FA8,0x5FAD,0x5FB5,/* 0xE0-0xE7 */
+ 0x5FBC,0x8862,0x5F61,0x72AD,0x72B0,0x72B4,0x72B7,0x72B8,/* 0xE8-0xEF */
+ 0x72C3,0x72C1,0x72CE,0x72CD,0x72D2,0x72E8,0x72EF,0x72E9,/* 0xF0-0xF7 */
+ 0x72F2,0x72F4,0x72F7,0x7301,0x72F3,0x7303,0x72FA,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E2[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x91E6,0x91E7,0x91E8,0x91E9,0x91EA,0x91EB,0x91EC,0x91ED,/* 0x40-0x47 */
+ 0x91EE,0x91EF,0x91F0,0x91F1,0x91F2,0x91F3,0x91F4,0x91F5,/* 0x48-0x4F */
+ 0x91F6,0x91F7,0x91F8,0x91F9,0x91FA,0x91FB,0x91FC,0x91FD,/* 0x50-0x57 */
+ 0x91FE,0x91FF,0x9200,0x9201,0x9202,0x9203,0x9204,0x9205,/* 0x58-0x5F */
+ 0x9206,0x9207,0x9208,0x9209,0x920A,0x920B,0x920C,0x920D,/* 0x60-0x67 */
+ 0x920E,0x920F,0x9210,0x9211,0x9212,0x9213,0x9214,0x9215,/* 0x68-0x6F */
+ 0x9216,0x9217,0x9218,0x9219,0x921A,0x921B,0x921C,0x921D,/* 0x70-0x77 */
+ 0x921E,0x921F,0x9220,0x9221,0x9222,0x9223,0x9224,0x0000,/* 0x78-0x7F */
+
+ 0x9225,0x9226,0x9227,0x9228,0x9229,0x922A,0x922B,0x922C,/* 0x80-0x87 */
+ 0x922D,0x922E,0x922F,0x9230,0x9231,0x9232,0x9233,0xF9B1,/* 0x88-0x8F */
+ 0x9235,0x9236,0x9237,0x9238,0x9239,0x923A,0x923B,0x923C,/* 0x90-0x97 */
+ 0x923D,0x923E,0x923F,0x9240,0x9241,0x9242,0x9243,0x9244,/* 0x98-0x9F */
+ 0x9245,0x72FB,0x7317,0x7313,0x7321,0x730A,0x731E,0x731D,/* 0xA0-0xA7 */
+ 0x7315,0x7322,0x7339,0x7325,0x732C,0x7338,0x7331,0x7350,/* 0xA8-0xAF */
+ 0x734D,0x7357,0x7360,0x736C,0x736F,0x737E,0x821B,0x5925,/* 0xB0-0xB7 */
+ 0x98E7,0x5924,0x5902,0x9963,0x9967,0x9968,0x9969,0x996A,/* 0xB8-0xBF */
+ 0x996B,0x996C,0x9974,0x9977,0x997D,0x9980,0x9984,0x9987,/* 0xC0-0xC7 */
+ 0x998A,0x998D,0x9990,0x9991,0x9993,0x9994,0x9995,0x5E80,/* 0xC8-0xCF */
+ 0x5E91,0x5E8B,0x5E96,0x5EA5,0x5EA0,0x5EB9,0x5EB5,0x5EBE,/* 0xD0-0xD7 */
+ 0x5EB3,0x8D53,0x5ED2,0x5ED1,0x5EDB,0x5EE8,0x5EEA,0x81BA,/* 0xD8-0xDF */
+ 0x5FC4,0x5FC9,0x5FD6,0x5FCF,0x6003,0x5FEE,0x6004,0x5FE1,/* 0xE0-0xE7 */
+ 0x5FE4,0x5FFE,0x6005,0x6006,0x5FEA,0x5FED,0x5FF8,0x6019,/* 0xE8-0xEF */
+ 0x6035,0x6026,0x601B,0x600F,0x600D,0x6029,0x602B,0x600A,/* 0xF0-0xF7 */
+ 0x603F,0x6021,0x6078,0x6079,0x607B,0x607A,0x6042,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E3[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x9246,0x9247,0x9248,0x9249,0x924A,0x924B,0x924C,0x924D,/* 0x40-0x47 */
+ 0x924E,0x924F,0x9250,0x9251,0x9252,0x9253,0x9254,0x9255,/* 0x48-0x4F */
+ 0x9256,0x9257,0x9258,0x9259,0x925A,0x925B,0x925C,0x925D,/* 0x50-0x57 */
+ 0x925E,0x925F,0x9260,0x9261,0x9262,0x9263,0x9264,0x9265,/* 0x58-0x5F */
+ 0x9266,0x9267,0x9268,0x9269,0x926A,0x926B,0x926C,0x926D,/* 0x60-0x67 */
+ 0x926E,0x926F,0x9270,0x9271,0x9272,0x9273,0x9275,0x9276,/* 0x68-0x6F */
+ 0x9277,0x9278,0x9279,0x927A,0x927B,0x927C,0x927D,0x927E,/* 0x70-0x77 */
+ 0x927F,0x9280,0x9281,0x9282,0x9283,0x9284,0x9285,0x0000,/* 0x78-0x7F */
+
+ 0x9286,0x9287,0x9288,0x9289,0x928A,0x928B,0x928C,0x928D,/* 0x80-0x87 */
+ 0x928F,0x9290,0x9291,0x9292,0x9293,0x9294,0x9295,0x9296,/* 0x88-0x8F */
+ 0x9297,0x9298,0x9299,0x929A,0x929B,0x929C,0x929D,0x929E,/* 0x90-0x97 */
+ 0x929F,0x92A0,0x92A1,0x92A2,0x92A3,0x92A4,0x92A5,0x92A6,/* 0x98-0x9F */
+ 0x92A7,0x606A,0x607D,0x6096,0x609A,0x60AD,0x609D,0x6083,/* 0xA0-0xA7 */
+ 0x6092,0x608C,0x609B,0x60EC,0x60BB,0x60B1,0x60DD,0x60D8,/* 0xA8-0xAF */
+ 0x60C6,0x60DA,0x60B4,0x6120,0x6126,0x6115,0x6123,0x60F4,/* 0xB0-0xB7 */
+ 0x6100,0x610E,0x612B,0x614A,0x6175,0x61AC,0x6194,0x61A7,/* 0xB8-0xBF */
+ 0x61B7,0x61D4,0x61F5,0x5FDD,0x96B3,0x95E9,0x95EB,0x95F1,/* 0xC0-0xC7 */
+ 0x95F3,0x95F5,0x95F6,0x95FC,0x95FE,0x9603,0x9604,0x9606,/* 0xC8-0xCF */
+ 0x9608,0x960A,0x960B,0x960C,0x960D,0x960F,0x9612,0x9615,/* 0xD0-0xD7 */
+ 0x9616,0x9617,0x9619,0x961A,0x4E2C,0x723F,0x6215,0x6C35,/* 0xD8-0xDF */
+ 0x6C54,0x6C5C,0x6C4A,0x6CA3,0x6C85,0x6C90,0x6C94,0x6C8C,/* 0xE0-0xE7 */
+ 0x6C68,0x6C69,0x6C74,0x6C76,0x6C86,0x6CA9,0x6CD0,0x6CD4,/* 0xE8-0xEF */
+ 0x6CAD,0x6CF7,0x6CF8,0x6CF1,0x6CD7,0x6CB2,0x6CE0,0x6CD6,/* 0xF0-0xF7 */
+ 0x6CFA,0x6CEB,0x6CEE,0x6CB1,0x6CD3,0x6CEF,0x6CFE,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E4[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x92A8,0x92A9,0x92AA,0x92AB,0x92AC,0x92AD,0x92AF,0x92B0,/* 0x40-0x47 */
+ 0x92B1,0x92B2,0x92B3,0x92B4,0x92B5,0x92B6,0x92B7,0x92B8,/* 0x48-0x4F */
+ 0x92B9,0x92BA,0x92BB,0x92BC,0x92BD,0x92BE,0x92BF,0x92C0,/* 0x50-0x57 */
+ 0x92C1,0x92C2,0x92C3,0x92C4,0x92C5,0x92C6,0x92C7,0x92C9,/* 0x58-0x5F */
+ 0x92CA,0x92CB,0x92CC,0x92CD,0x92CE,0x92CF,0x92D0,0x92D1,/* 0x60-0x67 */
+ 0x92D2,0x92D3,0x92D4,0x92D5,0x92D6,0x92D7,0x92D8,0x92D9,/* 0x68-0x6F */
+ 0x92DA,0x92DB,0x92DC,0x92DD,0x92DE,0x92DF,0x92E0,0x92E1,/* 0x70-0x77 */
+ 0x92E2,0x92E3,0x92E4,0x92E5,0x92E6,0x92E7,0x92E8,0x0000,/* 0x78-0x7F */
+
+ 0x92E9,0x92EA,0x92EB,0x92EC,0x92ED,0x92EE,0x92EF,0x92F0,/* 0x80-0x87 */
+ 0x92F1,0x92F2,0x92F3,0x92F4,0x92F5,0x92F6,0x92F7,0x92F8,/* 0x88-0x8F */
+ 0x92F9,0x92FA,0x92FB,0x92FC,0x92FD,0x92FE,0x92FF,0x9300,/* 0x90-0x97 */
+ 0x9301,0x9302,0x9303,0xF93F,0x9305,0x9306,0x9307,0x9308,/* 0x98-0x9F */
+ 0x9309,0x6D39,0x6D27,0x6D0C,0x6D43,0x6D48,0x6D07,0x6D04,/* 0xA0-0xA7 */
+ 0x6D19,0x6D0E,0x6D2B,0x6D4D,0x6D2E,0x6D35,0x6D1A,0x6D4F,/* 0xA8-0xAF */
+ 0x6D52,0x6D54,0x6D33,0x6D91,0x6D6F,0x6D9E,0x6DA0,0x6D5E,/* 0xB0-0xB7 */
+ 0x6D93,0x6D94,0x6D5C,0x6D60,0x6D7C,0x6D63,0x6E1A,0x6DC7,/* 0xB8-0xBF */
+ 0x6DC5,0x6DDE,0x6E0E,0x6DBF,0x6DE0,0x6E11,0x6DE6,0x6DDD,/* 0xC0-0xC7 */
+ 0x6DD9,0x6E16,0x6DAB,0x6E0C,0x6DAE,0x6E2B,0x6E6E,0x6E4E,/* 0xC8-0xCF */
+ 0x6E6B,0x6EB2,0x6E5F,0x6E86,0x6E53,0x6E54,0x6E32,0x6E25,/* 0xD0-0xD7 */
+ 0x6E44,0x6EDF,0x6EB1,0x6E98,0x6EE0,0x6F2D,0x6EE2,0x6EA5,/* 0xD8-0xDF */
+ 0x6EA7,0x6EBD,0x6EBB,0x6EB7,0x6ED7,0x6EB4,0x6ECF,0x6E8F,/* 0xE0-0xE7 */
+ 0x6EC2,0x6E9F,0x6F62,0x6F46,0x6F47,0x6F24,0x6F15,0x6EF9,/* 0xE8-0xEF */
+ 0x6F2F,0x6F36,0x6F4B,0x6F74,0x6F2A,0x6F09,0x6F29,0x6F89,/* 0xF0-0xF7 */
+ 0x6F8D,0x6F8C,0x6F78,0x6F72,0x6F7C,0x6F7A,0x6FD1,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E5[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x930A,0x930B,0x930C,0x930D,0x930E,0x930F,0x9310,0x9311,/* 0x40-0x47 */
+ 0x9312,0x9313,0x9314,0x9315,0x9316,0x9317,0x9318,0x9319,/* 0x48-0x4F */
+ 0x931A,0x931B,0x931C,0x931D,0x931E,0x931F,0x9320,0x9321,/* 0x50-0x57 */
+ 0x9322,0x9323,0x9324,0x9325,0x9326,0x9327,0x9328,0x9329,/* 0x58-0x5F */
+ 0x932A,0x932B,0x932C,0x932D,0x932E,0x932F,0x9330,0x9331,/* 0x60-0x67 */
+ 0x9332,0x9333,0x9334,0x9335,0x9336,0x9337,0x9338,0x9339,/* 0x68-0x6F */
+ 0x933A,0x933B,0x933C,0x933D,0x933F,0x9340,0x9341,0x9342,/* 0x70-0x77 */
+ 0x9343,0x9344,0x9345,0x9346,0x9347,0x9348,0x9349,0x0000,/* 0x78-0x7F */
+
+ 0xF99B,0x934B,0x934C,0x934D,0x934E,0x934F,0x9350,0x9351,/* 0x80-0x87 */
+ 0x9352,0x9353,0x9354,0x9355,0x9356,0x9357,0x9358,0x9359,/* 0x88-0x8F */
+ 0x935A,0x935B,0x935C,0x935D,0x935E,0x935F,0x9360,0x9361,/* 0x90-0x97 */
+ 0x9362,0x9363,0x9364,0x9365,0x9366,0x9367,0x9368,0x9369,/* 0x98-0x9F */
+ 0x936B,0x6FC9,0x6FA7,0x6FB9,0x6FB6,0x6FC2,0x6FE1,0x6FEE,/* 0xA0-0xA7 */
+ 0x6FDE,0x6FE0,0x6FEF,0x701A,0x7023,0x701B,0x7039,0x7035,/* 0xA8-0xAF */
+ 0x704F,0x705E,0x5B80,0x5B84,0x5B95,0x5B93,0x5BA5,0x5BB8,/* 0xB0-0xB7 */
+ 0x752F,0x9A9E,0x6434,0x5BE4,0xF9BC,0x8930,0x5BF0,0x8E47,/* 0xB8-0xBF */
+ 0x8B07,0x8FB6,0x8FD3,0x8FD5,0x8FE5,0x8FEE,0x8FE4,0x8FE9,/* 0xC0-0xC7 */
+ 0x8FE6,0x8FF3,0x8FE8,0x9005,0x9004,0x900B,0x9026,0x9011,/* 0xC8-0xCF */
+ 0x900D,0x9016,0x9021,0x9035,0x9036,0x902D,0x902F,0x9044,/* 0xD0-0xD7 */
+ 0x9051,0x9052,0x9050,0x9068,0x9058,0x9062,0x905B,0x66B9,/* 0xD8-0xDF */
+ 0x9074,0x907D,0x9082,0x9088,0x9083,0x908B,0x5F50,0x5F57,/* 0xE0-0xE7 */
+ 0x5F56,0x5F58,0x5C3B,0x54AB,0x5C50,0x5C59,0x5B71,0x5C63,/* 0xE8-0xEF */
+ 0x5C66,0x7FBC,0x5F2A,0x5F29,0x5F2D,0x8274,0x5F3C,0x9B3B,/* 0xF0-0xF7 */
+ 0x5C6E,0x5981,0x5983,0x598D,0x59A9,0x59AA,0x59A3,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E6[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x936C,0x936D,0x936E,0x936F,0x9370,0x9371,0x9372,0x9373,/* 0x40-0x47 */
+ 0x9374,0x9375,0x9376,0x9377,0x9378,0x9379,0x937A,0x937B,/* 0x48-0x4F */
+ 0x937C,0x937D,0x937E,0x937F,0x9380,0x9381,0x9382,0x9383,/* 0x50-0x57 */
+ 0x9384,0x9385,0x9386,0x9387,0x9388,0x9389,0x938A,0x938B,/* 0x58-0x5F */
+ 0x938C,0x938D,0x938E,0x9390,0x9391,0x9392,0x9393,0x9394,/* 0x60-0x67 */
+ 0x9395,0x9396,0x9397,0x9398,0x9399,0x939A,0x939B,0x939C,/* 0x68-0x6F */
+ 0x939D,0x939E,0x939F,0x93A0,0x93A1,0x93A2,0x93A3,0x93A4,/* 0x70-0x77 */
+ 0x93A5,0x93A6,0x93A7,0x93A8,0x93A9,0x93AA,0x93AB,0x0000,/* 0x78-0x7F */
+
+ 0x93AC,0x93AD,0x93AE,0x93AF,0x93B0,0x93B1,0x93B2,0x93B3,/* 0x80-0x87 */
+ 0x93B4,0x93B5,0x93B6,0x93B7,0x93B8,0x93B9,0x93BA,0x93BB,/* 0x88-0x8F */
+ 0x93BC,0x93BD,0x93BE,0x93BF,0x93C0,0x93C1,0x93C2,0x93C3,/* 0x90-0x97 */
+ 0x93C4,0x93C5,0x93C6,0x93C7,0x93C8,0x93C9,0x93CB,0x93CC,/* 0x98-0x9F */
+ 0x93CD,0x5997,0x59CA,0x59AB,0x599E,0x59A4,0x59D2,0x59B2,/* 0xA0-0xA7 */
+ 0x59AF,0x59D7,0x59BE,0x5A05,0x5A06,0x59DD,0x5A08,0x59E3,/* 0xA8-0xAF */
+ 0x59D8,0x59F9,0x5A0C,0x5A09,0x5A32,0x5A34,0x5A11,0x5A23,/* 0xB0-0xB7 */
+ 0x5A13,0x5A40,0x5A67,0x5A4A,0x5A55,0x5A3C,0x5A62,0x5A75,/* 0xB8-0xBF */
+ 0x80EC,0x5AAA,0x5A9B,0x5A77,0x5A7A,0x5ABE,0x5AEB,0x5AB2,/* 0xC0-0xC7 */
+ 0x5AD2,0x5AD4,0x5AB8,0x5AE0,0x5AE3,0x5AF1,0x5AD6,0x5AE6,/* 0xC8-0xCF */
+ 0x5AD8,0x5ADC,0x5B09,0x5B17,0x5B16,0x5B32,0x5B37,0x5B40,/* 0xD0-0xD7 */
+ 0x5C15,0x5C1C,0x5B5A,0x5B65,0x5B73,0x5B51,0x5B53,0x5B62,/* 0xD8-0xDF */
+ 0x9A75,0x9A77,0x9A78,0x9A7A,0x9A7F,0x9A7D,0x9A80,0x9A81,/* 0xE0-0xE7 */
+ 0x9A85,0x9A88,0x9A8A,0x9A90,0x9A92,0x9A93,0x9A96,0x9A98,/* 0xE8-0xEF */
+ 0x9A9B,0x9A9C,0x9A9D,0x9A9F,0x9AA0,0x9AA2,0x9AA3,0x9AA5,/* 0xF0-0xF7 */
+ 0x9AA7,0x7E9F,0x7EA1,0x7EA3,0x7EA5,0x7EA8,0x7EA9,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E7[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x93CE,0x93CF,0x93D0,0x93D1,0x93D2,0x93D3,0x93D4,0x93D5,/* 0x40-0x47 */
+ 0x93D7,0x93D8,0x93D9,0x93DA,0x93DB,0x93DC,0x93DD,0x93DE,/* 0x48-0x4F */
+ 0x93DF,0x93E0,0x93E1,0x93E2,0x93E3,0x93E4,0x93E5,0x93E6,/* 0x50-0x57 */
+ 0x93E7,0x93E8,0x93E9,0x93EA,0x93EB,0x93EC,0x93ED,0x93EE,/* 0x58-0x5F */
+ 0x93EF,0x93F0,0x93F1,0x93F2,0x93F3,0x93F4,0x93F5,0x93F6,/* 0x60-0x67 */
+ 0x93F7,0x93F8,0x93F9,0x93FA,0x93FB,0x93FC,0x93FD,0x93FE,/* 0x68-0x6F */
+ 0x93FF,0x9400,0x9401,0x9402,0x9403,0x9404,0x9405,0x9406,/* 0x70-0x77 */
+ 0x9407,0x9408,0x9409,0x940A,0x940B,0x940C,0x940D,0x0000,/* 0x78-0x7F */
+
+ 0x940E,0x940F,0x9410,0x9411,0x9412,0x9413,0x9414,0x9415,/* 0x80-0x87 */
+ 0x9416,0x9417,0x9418,0x9419,0x941A,0x941B,0x941C,0x941D,/* 0x88-0x8F */
+ 0x941E,0x941F,0x9420,0x9421,0x9422,0x9423,0x9424,0x9425,/* 0x90-0x97 */
+ 0x9426,0x9427,0x9428,0x9429,0x942A,0x942B,0x942C,0x942D,/* 0x98-0x9F */
+ 0x942E,0x7EAD,0x7EB0,0x7EBE,0x7EC0,0x7EC1,0x7EC2,0x7EC9,/* 0xA0-0xA7 */
+ 0x7ECB,0x7ECC,0x7ED0,0x7ED4,0x7ED7,0x7EDB,0x7EE0,0x7EE1,/* 0xA8-0xAF */
+ 0x7EE8,0x7EEB,0x7EEE,0x7EEF,0x7EF1,0x7EF2,0x7F0D,0x7EF6,/* 0xB0-0xB7 */
+ 0x7EFA,0x7EFB,0x7EFE,0x7F01,0x7F02,0x7F03,0x7F07,0x7F08,/* 0xB8-0xBF */
+ 0x7F0B,0x7F0C,0x7F0F,0x7F11,0x7F12,0x7F17,0x7F19,0x7F1C,/* 0xC0-0xC7 */
+ 0x7F1B,0x7F1F,0x7F21,0x7F22,0x7F23,0x7F24,0x7F25,0x7F26,/* 0xC8-0xCF */
+ 0x7F27,0x7F2A,0x7F2B,0x7F2C,0x7F2D,0x7F2F,0x7F30,0x7F31,/* 0xD0-0xD7 */
+ 0x7F32,0x7F33,0x7F35,0x5E7A,0x757F,0x5DDB,0x753E,0x9095,/* 0xD8-0xDF */
+ 0x738E,0x7391,0x73AE,0x73A2,0x739F,0x73CF,0x73C2,0x73D1,/* 0xE0-0xE7 */
+ 0x73B7,0x73B3,0x73C0,0x73C9,0x73C8,0x73E5,0x73D9,0x987C,/* 0xE8-0xEF */
+ 0x740A,0x73E9,0x73E7,0xF917,0x73BA,0x73F2,0x740F,0x742A,/* 0xF0-0xF7 */
+ 0x745B,0x7426,0x7425,0x7428,0x7430,0x742E,0x742C,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E8[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x942F,0x9430,0x9431,0x9432,0x9433,0x9434,0x9435,0x9436,/* 0x40-0x47 */
+ 0x9437,0x9438,0x9439,0x943A,0x943B,0x943C,0x943D,0x943F,/* 0x48-0x4F */
+ 0x9440,0x9441,0x9442,0x9443,0x9444,0x9445,0x9446,0x9447,/* 0x50-0x57 */
+ 0x9448,0x9449,0x944A,0x944B,0x944C,0x944D,0x944E,0x944F,/* 0x58-0x5F */
+ 0x9450,0x9451,0x9452,0x9453,0x9454,0x9455,0x9456,0x9457,/* 0x60-0x67 */
+ 0x9458,0x9459,0x945A,0x945B,0x945C,0x945D,0x945E,0x945F,/* 0x68-0x6F */
+ 0x9460,0x9461,0x9462,0x9463,0x9464,0x9465,0x9466,0x9467,/* 0x70-0x77 */
+ 0x9468,0x9469,0x946A,0x946C,0x946D,0x946E,0x946F,0x0000,/* 0x78-0x7F */
+
+ 0x9470,0x9471,0x9472,0x9473,0x9474,0x9475,0x9476,0x9477,/* 0x80-0x87 */
+ 0x9478,0x9479,0x947A,0x947B,0x947C,0x947D,0x947E,0x947F,/* 0x88-0x8F */
+ 0x9480,0x9481,0x9482,0x9483,0x9484,0x9491,0x9496,0x9498,/* 0x90-0x97 */
+ 0x94C7,0x94CF,0x94D3,0x94D4,0x94DA,0x94E6,0x94FB,0x951C,/* 0x98-0x9F */
+ 0x9520,0x741B,0x741A,0x7441,0x745C,0x7457,0x7455,0x7459,/* 0xA0-0xA7 */
+ 0x7477,0x746D,0x747E,0x749C,0x748E,0x7480,0x7481,0x7487,/* 0xA8-0xAF */
+ 0x748B,0x749E,0x74A8,0x74A9,0x7490,0x74A7,0x74D2,0x74BA,/* 0xB0-0xB7 */
+ 0x97EA,0x97EB,0x97EC,0x674C,0x6753,0x675E,0x6748,0x6769,/* 0xB8-0xBF */
+ 0x67A5,0x6787,0x676A,0x6773,0x6798,0x67A7,0x6775,0x67A8,/* 0xC0-0xC7 */
+ 0x679E,0x67AD,0x678B,0x6777,0x677C,0x67F0,0x6809,0x67D8,/* 0xC8-0xCF */
+ 0x680A,0x67E9,0x67B0,0x680C,0x67D9,0x67B5,0x67DA,0x67B3,/* 0xD0-0xD7 */
+ 0x67DD,0x6800,0x67C3,0x67B8,0x67E2,0x680E,0x67C1,0x67FD,/* 0xD8-0xDF */
+ 0x6832,0x6833,0x6860,0x6861,0x684E,0x6862,0x6844,0x6864,/* 0xE0-0xE7 */
+ 0x6883,0x681D,0x6855,0x6866,0x6841,0x6867,0x6840,0x683E,/* 0xE8-0xEF */
+ 0x684A,0x6849,0x6829,0x68B5,0x688F,0x6874,0x6877,0x6893,/* 0xF0-0xF7 */
+ 0x686B,0x68C2,0x696E,0x68FC,0x691F,0x6920,0x68F9,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E9[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x9527,0x9533,0x953D,0x9543,0x9548,0x954B,0x9555,0x955A,/* 0x40-0x47 */
+ 0x9560,0x956E,0x9574,0x9575,0x9577,0x9578,0x9579,0x957A,/* 0x48-0x4F */
+ 0x957B,0x957C,0x957D,0x957E,0x9580,0x9581,0x9582,0x9583,/* 0x50-0x57 */
+ 0x9584,0x9585,0x9586,0x9587,0x9588,0x9589,0x958A,0x958B,/* 0x58-0x5F */
+ 0x958C,0x958D,0x958E,0x958F,0x9590,0x9591,0x9592,0x9593,/* 0x60-0x67 */
+ 0x9594,0x9595,0x9596,0x9597,0x9598,0x9599,0x959A,0x959B,/* 0x68-0x6F */
+ 0x959C,0x959D,0x959E,0x959F,0x95A0,0x95A1,0x95A2,0x95A3,/* 0x70-0x77 */
+ 0x95A4,0x95A5,0x95A6,0x95A7,0x95A8,0x95A9,0x95AA,0x0000,/* 0x78-0x7F */
+
+ 0x95AB,0x95AC,0xF986,0x95AE,0x95AF,0x95B0,0x95B1,0x95B2,/* 0x80-0x87 */
+ 0x95B3,0x95B4,0x95B5,0x95B6,0x95B7,0x95B8,0x95B9,0x95BA,/* 0x88-0x8F */
+ 0x95BB,0x95BC,0x95BD,0x95BE,0x95BF,0x95C0,0x95C1,0x95C2,/* 0x90-0x97 */
+ 0x95C3,0x95C4,0x95C5,0x95C6,0x95C7,0x95C8,0x95C9,0x95CA,/* 0x98-0x9F */
+ 0x95CB,0x6924,0x68F0,0x690B,0x6901,0x6957,0x68E3,0x6910,/* 0xA0-0xA7 */
+ 0x6971,0x6939,0x6960,0x6942,0x695D,0x6984,0x696B,0x6980,/* 0xA8-0xAF */
+ 0x6998,0x6978,0x6934,0x69CC,0x6987,0x6988,0x69CE,0x6989,/* 0xB0-0xB7 */
+ 0x6966,0x6963,0x6979,0x699B,0x69A7,0x69BB,0x69AB,0x69AD,/* 0xB8-0xBF */
+ 0x69D4,0x69B1,0x69C1,0x69CA,0x69DF,0x6995,0x69E0,0x698D,/* 0xC0-0xC7 */
+ 0x69FF,0x6A2F,0x69ED,0x6A17,0x6A18,0x6A65,0x69F2,0x6A44,/* 0xC8-0xCF */
+ 0x6A3E,0x6AA0,0x6A50,0x6A5B,0x6A35,0x6A8E,0x6A79,0x6A3D,/* 0xD0-0xD7 */
+ 0x6A28,0x6A58,0x6A7C,0x6A91,0x6A90,0x6AA9,0x6A97,0x6AAB,/* 0xD8-0xDF */
+ 0x7337,0x7352,0x6B81,0x6B82,0x6B87,0x6B84,0x6B92,0x6B93,/* 0xE0-0xE7 */
+ 0x6B8D,0x6B9A,0x6B9B,0x6BA1,0x6BAA,0x8F6B,0x8F6D,0x8F71,/* 0xE8-0xEF */
+ 0x8F72,0x8F73,0x8F75,0x8F76,0x8F78,0x8F77,0x8F79,0x8F7A,/* 0xF0-0xF7 */
+ 0x8F7C,0x8F7E,0x8F81,0x8F82,0x8F84,0x8F87,0x8F8B,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_EA[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x95CC,0x95CD,0x95CE,0x95CF,0x95D0,0x95D1,0x95D2,0x95D3,/* 0x40-0x47 */
+ 0x95D4,0x95D5,0x95D6,0x95D7,0x95D8,0x95D9,0x95DA,0x95DB,/* 0x48-0x4F */
+ 0x95DC,0x95DD,0x95DE,0x95DF,0x95E0,0x95E1,0x95E2,0x95E3,/* 0x50-0x57 */
+ 0x95E4,0x95E5,0x95E6,0x95E7,0x95EC,0x95FF,0x9607,0x9613,/* 0x58-0x5F */
+ 0x9618,0x961B,0x961E,0x9620,0x9623,0x9624,0x9625,0x9626,/* 0x60-0x67 */
+ 0x9627,0x9628,0x9629,0x962B,0x962C,0x962D,0x962F,0x9630,/* 0x68-0x6F */
+ 0x9637,0x9638,0x9639,0x963A,0x963E,0x9641,0x9643,0x964A,/* 0x70-0x77 */
+ 0x964E,0x964F,0x9651,0x9652,0x9653,0x9656,0x9657,0x0000,/* 0x78-0x7F */
+
+ 0x9658,0x9659,0x965A,0x965C,0x965D,0x965E,0x9660,0x9663,/* 0x80-0x87 */
+ 0x9665,0x9666,0x966B,0x966D,0x966E,0x966F,0x9670,0x9671,/* 0x88-0x8F */
+ 0x9673,0xF9D3,0x9679,0x967A,0x967B,0x967C,0x967D,0x967E,/* 0x90-0x97 */
+ 0x967F,0x9680,0x9681,0x9682,0x9683,0x9684,0x9687,0x9689,/* 0x98-0x9F */
+ 0x968A,0x8F8D,0x8F8E,0x8F8F,0x8F98,0x8F9A,0x8ECE,0x620B,/* 0xA0-0xA7 */
+ 0x6217,0x621B,0x621F,0x6222,0x6221,0x6225,0x6224,0x622C,/* 0xA8-0xAF */
+ 0x81E7,0x74EF,0x74F4,0x74FF,0x750F,0x7511,0x7513,0x6534,/* 0xB0-0xB7 */
+ 0x65EE,0x65EF,0x65F0,0x660A,0x6619,0x6772,0x6603,0x6615,/* 0xB8-0xBF */
+ 0x6600,0x7085,0x66F7,0x661D,0x6634,0x6631,0x6636,0x6635,/* 0xC0-0xC7 */
+ 0x8006,0x665F,0x6654,0x6641,0x664F,0x6656,0x6661,0x6657,/* 0xC8-0xCF */
+ 0x6677,0x6684,0x668C,0x66A7,0x669D,0x66BE,0x66DB,0x66DC,/* 0xD0-0xD7 */
+ 0x66E6,0x66E9,0x8D32,0x8D33,0x8D36,0x8D3B,0x8D3D,0x8D40,/* 0xD8-0xDF */
+ 0x8D45,0x8D46,0x8D48,0x8D49,0x8D47,0x8D4D,0x8D55,0x8D59,/* 0xE0-0xE7 */
+ 0x89C7,0x89CA,0x89CB,0x89CC,0x89CE,0x89CF,0x89D0,0x89D1,/* 0xE8-0xEF */
+ 0x726E,0x729F,0x725D,0x7266,0x726F,0x727E,0x727F,0x7284,/* 0xF0-0xF7 */
+ 0x728B,0x728D,0x728F,0x7292,0x6308,0x6332,0x63B0,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_EB[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x968C,0x968E,0x9691,0x9692,0x9693,0x9695,0x9696,0x969A,/* 0x40-0x47 */
+ 0x969B,0x969D,0x969E,0x969F,0x96A0,0x96A1,0x96A2,0x96A3,/* 0x48-0x4F */
+ 0x96A4,0x96A5,0x96A6,0x96A8,0x96A9,0x96AA,0x96AB,0x96AC,/* 0x50-0x57 */
+ 0x96AD,0x96AE,0x96AF,0x96B1,0x96B2,0x96B4,0x96B5,0x96B7,/* 0x58-0x5F */
+ 0xF9B8,0x96BA,0x96BB,0x96BF,0x96C2,0x96C3,0x96C8,0x96CA,/* 0x60-0x67 */
+ 0x96CB,0x96D0,0x96D1,0x96D3,0x96D4,0x96D6,0x96D7,0x96D8,/* 0x68-0x6F */
+ 0x96D9,0x96DA,0x96DB,0x96DC,0x96DD,0x96DE,0x96DF,0x96E1,/* 0x70-0x77 */
+ 0xF9EA,0x96E3,0x96E4,0x96E5,0x96E6,0x96E7,0x96EB,0x0000,/* 0x78-0x7F */
+
+ 0x96EC,0x96ED,0x96EE,0x96F0,0x96F1,0x96F2,0x96F4,0x96F5,/* 0x80-0x87 */
+ 0x96F8,0x96FA,0x96FB,0x96FC,0x96FD,0x96FF,0x9702,0x9703,/* 0x88-0x8F */
+ 0x9705,0x970A,0x970B,0x970C,0x9710,0x9711,0x9712,0x9714,/* 0x90-0x97 */
+ 0x9715,0x9717,0x9718,0x9719,0x971A,0x971B,0x971D,0x971F,/* 0x98-0x9F */
+ 0x9720,0x643F,0x64D8,0x8004,0x6BEA,0x6BF3,0x6BFD,0x6BF5,/* 0xA0-0xA7 */
+ 0x6BF9,0x6C05,0x6C07,0x6C06,0x6C0D,0x6C15,0x6C18,0x6C19,/* 0xA8-0xAF */
+ 0x6C1A,0x6C21,0x6C29,0x6C24,0x6C2A,0x6C32,0x6535,0x6555,/* 0xB0-0xB7 */
+ 0x656B,0x724D,0x7252,0x7256,0x7230,0x8662,0x5216,0x809F,/* 0xB8-0xBF */
+ 0x809C,0x8093,0x80BC,0x670A,0x80BD,0x80B1,0x80AB,0x80AD,/* 0xC0-0xC7 */
+ 0x80B4,0x80B7,0x80E7,0x80E8,0x80E9,0x80EA,0x80DB,0x80C2,/* 0xC8-0xCF */
+ 0x80C4,0x80D9,0x80CD,0x80D7,0x6710,0x80DD,0x80EB,0x80F1,/* 0xD0-0xD7 */
+ 0x80F4,0x80ED,0x810D,0x810E,0x80F2,0x80FC,0x6715,0x8112,/* 0xD8-0xDF */
+ 0x8C5A,0x8136,0x811E,0x812C,0x8118,0x8132,0x8148,0x814C,/* 0xE0-0xE7 */
+ 0x8153,0x8174,0x8159,0x815A,0x8171,0x8160,0x8169,0x817C,/* 0xE8-0xEF */
+ 0x817D,0x816D,0x8167,0x584D,0x5AB5,0x8188,0x8182,0x8191,/* 0xF0-0xF7 */
+ 0x6ED5,0x81A3,0x81AA,0x81CC,0x6726,0x81CA,0x81BB,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_EC[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x9721,0x9722,0x9723,0x9724,0x9725,0x9726,0x9727,0x9728,/* 0x40-0x47 */
+ 0x9729,0x972B,0x972C,0x972E,0x972F,0x9731,0x9733,0x9734,/* 0x48-0x4F */
+ 0x9735,0x9736,0x9737,0x973A,0x973B,0x973C,0x973D,0x973F,/* 0x50-0x57 */
+ 0x9740,0x9741,0x9742,0x9743,0x9744,0x9745,0x9746,0x9747,/* 0x58-0x5F */
+ 0xF9B3,0x9749,0x974A,0x974B,0x974C,0x974D,0x974E,0x974F,/* 0x60-0x67 */
+ 0x9750,0x9751,0x9754,0x9755,0x9757,0x9758,0x975A,0x975C,/* 0x68-0x6F */
+ 0x975D,0x975F,0x9763,0x9764,0x9766,0x9767,0x9768,0x976A,/* 0x70-0x77 */
+ 0x976B,0x976C,0x976D,0x976E,0x976F,0x9770,0x9771,0x0000,/* 0x78-0x7F */
+
+ 0x9772,0x9775,0x9777,0x9778,0x9779,0x977A,0x977B,0x977D,/* 0x80-0x87 */
+ 0x977E,0x977F,0x9780,0x9781,0x9782,0x9783,0x9784,0x9786,/* 0x88-0x8F */
+ 0x9787,0x9788,0x9789,0x978A,0x978C,0x978E,0x978F,0x9790,/* 0x90-0x97 */
+ 0x9793,0x9795,0x9796,0x9797,0x9799,0x979A,0x979B,0x979C,/* 0x98-0x9F */
+ 0x979D,0x81C1,0x81A6,0x6B24,0x6B37,0x6B39,0x6B43,0x6B46,/* 0xA0-0xA7 */
+ 0x6B59,0x98D1,0x98D2,0x98D3,0x98D5,0x98D9,0x98DA,0x6BB3,/* 0xA8-0xAF */
+ 0x5F40,0x6BC2,0x89F3,0x6590,0x9F51,0x6593,0x65BC,0x65C6,/* 0xB0-0xB7 */
+ 0x65C4,0x65C3,0x65CC,0x65CE,0x65D2,0x65D6,0x7080,0x709C,/* 0xB8-0xBF */
+ 0x7096,0x709D,0x70BB,0x70C0,0x70B7,0x70AB,0x70B1,0x70E8,/* 0xC0-0xC7 */
+ 0x70CA,0x7110,0x7113,0x7116,0x712F,0x7131,0x7173,0x715C,/* 0xC8-0xCF */
+ 0x7168,0x7145,0x7172,0x714A,0x7178,0x717A,0x7198,0x71B3,/* 0xD0-0xD7 */
+ 0x71B5,0x71A8,0x71A0,0x71E0,0x71D4,0x71E7,0x71F9,0x721D,/* 0xD8-0xDF */
+ 0x7228,0x706C,0x7118,0x7166,0x71B9,0x623E,0x623D,0x6243,/* 0xE0-0xE7 */
+ 0x6248,0x6249,0x793B,0x7940,0x7946,0x7949,0x795B,0x795C,/* 0xE8-0xEF */
+ 0x7953,0x795A,0x7962,0x7957,0x7960,0x796F,0x7967,0x797A,/* 0xF0-0xF7 */
+ 0x7985,0x798A,0x799A,0x79A7,0x79B3,0x5FD1,0x5FD0,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_ED[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x979E,0x979F,0x97A1,0x97A2,0x97A4,0x97A5,0x97A6,0x97A7,/* 0x40-0x47 */
+ 0x97A8,0x97A9,0x97AA,0x97AC,0x97AE,0x97B0,0x97B1,0x97B3,/* 0x48-0x4F */
+ 0x97B5,0x97B6,0x97B7,0x97B8,0x97B9,0x97BA,0x97BB,0x97BC,/* 0x50-0x57 */
+ 0x97BD,0x97BE,0x97BF,0x97C0,0x97C1,0x97C2,0x97C3,0x97C4,/* 0x58-0x5F */
+ 0x97C5,0x97C6,0x97C7,0x97C8,0x97C9,0x97CA,0x97CB,0x97CC,/* 0x60-0x67 */
+ 0x97CD,0x97CE,0x97CF,0x97D0,0x97D1,0x97D2,0x97D3,0x97D4,/* 0x68-0x6F */
+ 0x97D5,0x97D6,0x97D7,0x97D8,0x97D9,0x97DA,0x97DB,0x97DC,/* 0x70-0x77 */
+ 0x97DD,0x97DE,0x97DF,0x97E0,0x97E1,0x97E2,0x97E3,0x0000,/* 0x78-0x7F */
+
+ 0x97E4,0x97E5,0x97E8,0x97EE,0x97EF,0x97F0,0x97F1,0x97F2,/* 0x80-0x87 */
+ 0x97F4,0x97F7,0x97F8,0x97F9,0x97FA,0x97FB,0x97FC,0x97FD,/* 0x88-0x8F */
+ 0x97FE,0x97FF,0x9800,0x9801,0x9802,0x9803,0x9804,0x9805,/* 0x90-0x97 */
+ 0x9806,0x9807,0x9808,0x9809,0x980A,0x980B,0x980C,0x980D,/* 0x98-0x9F */
+ 0x980E,0x603C,0x605D,0x605A,0x6067,0x6041,0x6059,0x6063,/* 0xA0-0xA7 */
+ 0x60AB,0x6106,0x610D,0x615D,0x61A9,0x619D,0x61CB,0x61D1,/* 0xA8-0xAF */
+ 0x6206,0x8080,0x807F,0x6C93,0x6CF6,0x6DFC,0x77F6,0x77F8,/* 0xB0-0xB7 */
+ 0x7800,0x7809,0x7817,0x7818,0x7811,0x65AB,0x782D,0x781C,/* 0xB8-0xBF */
+ 0x781D,0x7839,0x783A,0x783B,0x781F,0x783C,0x7825,0x782C,/* 0xC0-0xC7 */
+ 0x7823,0x7829,0x784E,0x786D,0x7856,0x7857,0x7826,0x7850,/* 0xC8-0xCF */
+ 0x7847,0x784C,0x786A,0x789B,0x7893,0x789A,0x7887,0x789C,/* 0xD0-0xD7 */
+ 0x78A1,0x78A3,0x78B2,0x78B9,0x78A5,0x78D4,0x78D9,0x78C9,/* 0xD8-0xDF */
+ 0x78EC,0x78F2,0x7905,0x78F4,0x7913,0x7924,0x791E,0x7934,/* 0xE0-0xE7 */
+ 0x9F9B,0x9EF9,0x9EFB,0x9EFC,0x76F1,0x7704,0x770D,0x76F9,/* 0xE8-0xEF */
+ 0x7707,0x7708,0x771A,0x7722,0x7719,0x772D,0x7726,0x7735,/* 0xF0-0xF7 */
+ 0x7738,0x7750,0x7751,0x7747,0x7743,0x775A,0x7768,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_EE[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x980F,0x9810,0x9811,0x9812,0x9813,0x9814,0x9815,0x9816,/* 0x40-0x47 */
+ 0x9817,0xF9B4,0x9819,0x981A,0x981B,0x981C,0x981D,0x981E,/* 0x48-0x4F */
+ 0x981F,0x9820,0x9821,0x9822,0x9823,0x9824,0x9825,0x9826,/* 0x50-0x57 */
+ 0x9827,0x9828,0x9829,0x982A,0x982B,0x982C,0x982D,0x982E,/* 0x58-0x5F */
+ 0x982F,0x9830,0x9831,0x9832,0x9833,0x9834,0x9835,0x9836,/* 0x60-0x67 */
+ 0x9837,0x9838,0x9839,0x983A,0x983B,0x983C,0x983D,0x983E,/* 0x68-0x6F */
+ 0x983F,0x9840,0x9841,0x9842,0x9843,0x9844,0x9845,0x9846,/* 0x70-0x77 */
+ 0x9847,0x9848,0x9849,0x984A,0x984B,0x984C,0x984D,0x0000,/* 0x78-0x7F */
+
+ 0x984E,0x984F,0x9850,0x9851,0x9852,0x9853,0x9854,0x9855,/* 0x80-0x87 */
+ 0x9856,0x9857,0x9858,0x9859,0x985A,0x985B,0x985C,0x985D,/* 0x88-0x8F */
+ 0xF9D0,0x985F,0x9860,0x9861,0x9862,0x9863,0x9864,0x9865,/* 0x90-0x97 */
+ 0x9866,0x9867,0x9868,0x9869,0x986A,0x986B,0x986C,0x986D,/* 0x98-0x9F */
+ 0x986E,0x7762,0x7765,0x777F,0x778D,0x777D,0x7780,0x778C,/* 0xA0-0xA7 */
+ 0x7791,0x779F,0x77A0,0x77B0,0x77B5,0x77BD,0x753A,0x7540,/* 0xA8-0xAF */
+ 0x754E,0x754B,0x7548,0x755B,0x7572,0x7579,0x7583,0x7F58,/* 0xB0-0xB7 */
+ 0x7F61,0x7F5F,0x8A48,0x7F68,0x7F74,0x7F71,0xF9E6,0x7F81,/* 0xB8-0xBF */
+ 0x7F7E,0x76CD,0x76E5,0x8832,0x9485,0x9486,0x9487,0x948B,/* 0xC0-0xC7 */
+ 0x948A,0x948C,0x948D,0x948F,0x9490,0x9494,0x9497,0x9495,/* 0xC8-0xCF */
+ 0x949A,0x949B,0x949C,0x94A3,0x94A4,0x94AB,0x94AA,0x94AD,/* 0xD0-0xD7 */
+ 0x94AC,0x94AF,0x94B0,0x94B2,0x94B4,0x94B6,0x94B7,0x94B8,/* 0xD8-0xDF */
+ 0x94B9,0x94BA,0x94BC,0x94BD,0x94BF,0x94C4,0x94C8,0x94C9,/* 0xE0-0xE7 */
+ 0x94CA,0x94CB,0x94CC,0x94CD,0x94CE,0x94D0,0x94D1,0x94D2,/* 0xE8-0xEF */
+ 0x94D5,0x94D6,0x94D7,0x94D9,0x94D8,0x94DB,0x94DE,0x94DF,/* 0xF0-0xF7 */
+ 0x94E0,0x94E2,0x94E4,0x94E5,0x94E7,0x94E8,0x94EA,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_EF[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x986F,0x9870,0x9871,0x9872,0x9873,0x9874,0x988B,0x988E,/* 0x40-0x47 */
+ 0x9892,0x9895,0x9899,0x98A3,0x98A8,0x98A9,0x98AA,0x98AB,/* 0x48-0x4F */
+ 0x98AC,0x98AD,0x98AE,0x98AF,0x98B0,0x98B1,0x98B2,0x98B3,/* 0x50-0x57 */
+ 0x98B4,0x98B5,0x98B6,0x98B7,0x98B8,0x98B9,0x98BA,0x98BB,/* 0x58-0x5F */
+ 0x98BC,0x98BD,0x98BE,0x98BF,0x98C0,0x98C1,0x98C2,0x98C3,/* 0x60-0x67 */
+ 0x98C4,0x98C5,0x98C6,0x98C7,0x98C8,0x98C9,0x98CA,0x98CB,/* 0x68-0x6F */
+ 0x98CC,0x98CD,0x98CF,0x98D0,0x98D4,0x98D6,0x98D7,0x98DB,/* 0x70-0x77 */
+ 0x98DC,0x98DD,0x98E0,0x98E1,0x98E2,0x98E3,0x98E4,0x0000,/* 0x78-0x7F */
+
+ 0x98E5,0x98E6,0x98E9,0x98EA,0x98EB,0x98EC,0x98ED,0x98EE,/* 0x80-0x87 */
+ 0xFA2A,0x98F0,0x98F1,0x98F2,0x98F3,0x98F4,0x98F5,0x98F6,/* 0x88-0x8F */
+ 0x98F7,0x98F8,0x98F9,0x98FA,0x98FB,0xFA2B,0x98FD,0x98FE,/* 0x90-0x97 */
+ 0x98FF,0x9900,0x9901,0x9902,0x9903,0x9904,0x9905,0x9906,/* 0x98-0x9F */
+ 0x9907,0x94E9,0x94EB,0x94EE,0x94EF,0x94F3,0x94F4,0x94F5,/* 0xA0-0xA7 */
+ 0x94F7,0x94F9,0x94FC,0x94FD,0x94FF,0x9503,0x9502,0x9506,/* 0xA8-0xAF */
+ 0x9507,0x9509,0x950A,0x950D,0x950E,0x950F,0x9512,0x9513,/* 0xB0-0xB7 */
+ 0x9514,0x9515,0x9516,0x9518,0x951B,0x951D,0x951E,0x951F,/* 0xB8-0xBF */
+ 0x9522,0x952A,0x952B,0x9529,0x952C,0x9531,0x9532,0x9534,/* 0xC0-0xC7 */
+ 0x9536,0x9537,0x9538,0x953C,0x953E,0x953F,0x9542,0x9535,/* 0xC8-0xCF */
+ 0x9544,0x9545,0x9546,0x9549,0x954C,0x954E,0x954F,0x9552,/* 0xD0-0xD7 */
+ 0x9553,0x9554,0x9556,0x9557,0x9558,0x9559,0x955B,0x955E,/* 0xD8-0xDF */
+ 0x955F,0x955D,0x9561,0x9562,0x9564,0x9565,0x9566,0x9567,/* 0xE0-0xE7 */
+ 0x9568,0x9569,0x956A,0x956B,0x956C,0x956F,0x9571,0x9572,/* 0xE8-0xEF */
+ 0x9573,0x953A,0x77E7,0x77EC,0x96C9,0x79D5,0x79ED,0x79E3,/* 0xF0-0xF7 */
+ 0x79EB,0x7A06,0x5D47,0x7A03,0x7A02,0x7A1E,0x7A14,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_F0[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x9908,0x9909,0x990A,0x990B,0x990C,0x990E,0x990F,0x9911,/* 0x40-0x47 */
+ 0x9912,0x9913,0x9914,0x9915,0x9916,0x9917,0x9918,0x9919,/* 0x48-0x4F */
+ 0x991A,0x991B,0x991C,0x991D,0x991E,0x991F,0x9920,0x9921,/* 0x50-0x57 */
+ 0x9922,0x9923,0x9924,0x9925,0x9926,0x9927,0xFA2C,0x9929,/* 0x58-0x5F */
+ 0x992A,0x992B,0x992C,0x992D,0x992F,0x9930,0x9931,0x9932,/* 0x60-0x67 */
+ 0x9933,0x9934,0x9935,0x9936,0x9937,0x9938,0x9939,0x993A,/* 0x68-0x6F */
+ 0x993B,0x993C,0x993D,0x993E,0x993F,0x9940,0x9941,0x9942,/* 0x70-0x77 */
+ 0x9943,0x9944,0x9945,0x9946,0x9947,0x9948,0x9949,0x0000,/* 0x78-0x7F */
+
+ 0x994A,0x994B,0x994C,0x994D,0x994E,0x994F,0x9950,0x9951,/* 0x80-0x87 */
+ 0x9952,0x9953,0x9956,0x9957,0x9958,0x9959,0x995A,0x995B,/* 0x88-0x8F */
+ 0x995C,0x995D,0x995E,0x995F,0x9960,0x9961,0x9962,0x9964,/* 0x90-0x97 */
+ 0x9966,0x9973,0x9978,0x9979,0x997B,0x997E,0x9982,0x9983,/* 0x98-0x9F */
+ 0x9989,0x7A39,0x7A37,0x7A51,0x9ECF,0x99A5,0x7A70,0x7688,/* 0xA0-0xA7 */
+ 0x768E,0x7693,0x7699,0x76A4,0x74DE,0x74E0,0x752C,0x9E20,/* 0xA8-0xAF */
+ 0x9E22,0x9E28,0x9E29,0x9E2A,0x9E2B,0x9E2C,0x9E32,0x9E31,/* 0xB0-0xB7 */
+ 0x9E36,0x9E38,0x9E37,0x9E39,0x9E3A,0x9E3E,0x9E41,0x9E42,/* 0xB8-0xBF */
+ 0x9E44,0x9E46,0x9E47,0x9E48,0x9E49,0x9E4B,0x9E4C,0x9E4E,/* 0xC0-0xC7 */
+ 0x9E51,0x9E55,0x9E57,0x9E5A,0x9E5B,0x9E5C,0x9E5E,0x9E63,/* 0xC8-0xCF */
+ 0x9E66,0x9E67,0x9E68,0x9E69,0x9E6A,0x9E6B,0x9E6C,0x9E71,/* 0xD0-0xD7 */
+ 0x9E6D,0x9E73,0x7592,0x7594,0x7596,0x75A0,0x759D,0x75AC,/* 0xD8-0xDF */
+ 0x75A3,0x75B3,0x75B4,0x75B8,0x75C4,0x75B1,0x75B0,0x75C3,/* 0xE0-0xE7 */
+ 0x75C2,0x75D6,0x75CD,0x75E3,0x75E8,0x75E6,0x75E4,0x75EB,/* 0xE8-0xEF */
+ 0x75E7,0x7603,0x75F1,0x75FC,0x75FF,0x7610,0x7600,0x7605,/* 0xF0-0xF7 */
+ 0x760C,0x7617,0x760A,0x7625,0x7618,0x7615,0x7619,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_F1[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x998C,0x998E,0x999A,0x999B,0x999C,0x999D,0x999E,0x999F,/* 0x40-0x47 */
+ 0x99A0,0x99A1,0x99A2,0x99A3,0x99A4,0x99A6,0x99A7,0x99A9,/* 0x48-0x4F */
+ 0x99AA,0x99AB,0x99AC,0x99AD,0x99AE,0x99AF,0x99B0,0x99B1,/* 0x50-0x57 */
+ 0x99B2,0x99B3,0x99B4,0x99B5,0x99B6,0x99B7,0x99B8,0x99B9,/* 0x58-0x5F */
+ 0x99BA,0x99BB,0x99BC,0x99BD,0x99BE,0x99BF,0x99C0,0x99C1,/* 0x60-0x67 */
+ 0x99C2,0x99C3,0x99C4,0x99C5,0x99C6,0x99C7,0x99C8,0x99C9,/* 0x68-0x6F */
+ 0x99CA,0x99CB,0x99CC,0x99CD,0x99CE,0x99CF,0x99D0,0x99D1,/* 0x70-0x77 */
+ 0x99D2,0x99D3,0x99D4,0x99D5,0x99D6,0x99D7,0x99D8,0x0000,/* 0x78-0x7F */
+
+ 0x99D9,0x99DA,0x99DB,0x99DC,0x99DD,0x99DE,0x99DF,0x99E0,/* 0x80-0x87 */
+ 0x99E1,0x99E2,0x99E3,0x99E4,0x99E5,0x99E6,0x99E7,0x99E8,/* 0x88-0x8F */
+ 0x99E9,0x99EA,0x99EB,0x99EC,0x99ED,0x99EE,0x99EF,0x99F0,/* 0x90-0x97 */
+ 0xF91A,0x99F2,0x99F3,0x99F4,0x99F5,0x99F6,0x99F7,0x99F8,/* 0x98-0x9F */
+ 0x99F9,0x761B,0x763C,0x7622,0x7620,0x7640,0x762D,0x7630,/* 0xA0-0xA7 */
+ 0x763F,0x7635,0x7643,0x763E,0x7633,0x764D,0x765E,0x7654,/* 0xA8-0xAF */
+ 0x765C,0x7656,0x766B,0x766F,0x7FCA,0x7AE6,0x7A78,0x7A79,/* 0xB0-0xB7 */
+ 0x7A80,0x7A86,0x7A88,0x7A95,0x7AA6,0x7AA0,0x7AAC,0x7AA8,/* 0xB8-0xBF */
+ 0x7AAD,0x7AB3,0x8864,0x8869,0x8872,0x887D,0x887F,0x8882,/* 0xC0-0xC7 */
+ 0x88A2,0x88C6,0x88B7,0x88BC,0x88C9,0x88E2,0x88CE,0x88E3,/* 0xC8-0xCF */
+ 0x88E5,0x88F1,0x891A,0x88FC,0x88E8,0x88FE,0x88F0,0x8921,/* 0xD0-0xD7 */
+ 0x8919,0x8913,0x891B,0x890A,0x8934,0x892B,0x8936,0x8941,/* 0xD8-0xDF */
+ 0x8966,0x897B,0x758B,0x80E5,0x76B2,0x76B4,0x77DC,0x8012,/* 0xE0-0xE7 */
+ 0x8014,0x8016,0x801C,0x8020,0x8022,0x8025,0x8026,0x8027,/* 0xE8-0xEF */
+ 0x8029,0x8028,0x8031,0x800B,0x8035,0x8043,0xF9B0,0x804D,/* 0xF0-0xF7 */
+ 0x8052,0x8069,0x8071,0x8983,0x9878,0x9880,0x9883,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_F2[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x99FA,0x99FB,0x99FC,0x99FD,0x99FE,0x99FF,0x9A00,0x9A01,/* 0x40-0x47 */
+ 0x9A02,0x9A03,0x9A04,0x9A05,0x9A06,0x9A07,0x9A08,0x9A09,/* 0x48-0x4F */
+ 0x9A0A,0x9A0B,0x9A0C,0x9A0D,0x9A0E,0x9A0F,0x9A10,0x9A11,/* 0x50-0x57 */
+ 0x9A12,0x9A13,0x9A14,0x9A15,0x9A16,0x9A17,0x9A18,0x9A19,/* 0x58-0x5F */
+ 0x9A1A,0x9A1B,0x9A1C,0x9A1D,0x9A1E,0x9A1F,0x9A20,0x9A21,/* 0x60-0x67 */
+ 0x9A22,0x9A23,0x9A24,0x9A25,0x9A26,0x9A27,0x9A28,0x9A29,/* 0x68-0x6F */
+ 0x9A2A,0x9A2B,0x9A2C,0x9A2D,0x9A2E,0x9A2F,0x9A30,0x9A31,/* 0x70-0x77 */
+ 0x9A32,0x9A33,0x9A34,0x9A35,0x9A36,0x9A37,0x9A38,0x0000,/* 0x78-0x7F */
+
+ 0x9A39,0x9A3A,0x9A3B,0x9A3C,0x9A3D,0x9A3E,0x9A3F,0x9A40,/* 0x80-0x87 */
+ 0x9A41,0x9A42,0x9A43,0x9A44,0x9A45,0x9A46,0x9A47,0x9A48,/* 0x88-0x8F */
+ 0x9A49,0x9A4A,0x9A4B,0x9A4C,0x9A4D,0x9A4E,0x9A4F,0x9A50,/* 0x90-0x97 */
+ 0x9A51,0x9A52,0x9A53,0x9A54,0x9A55,0x9A56,0x9A57,0x9A58,/* 0x98-0x9F */
+ 0x9A59,0x9889,0x988C,0x988D,0x988F,0x9894,0x989A,0x989B,/* 0xA0-0xA7 */
+ 0x989E,0x989F,0x98A1,0x98A2,0x98A5,0x98A6,0x864D,0x8654,/* 0xA8-0xAF */
+ 0x866C,0x866E,0x867F,0x867A,0x867C,0x867B,0x86A8,0x868D,/* 0xB0-0xB7 */
+ 0x868B,0x86AC,0x869D,0x86A7,0x86A3,0x86AA,0x8693,0x86A9,/* 0xB8-0xBF */
+ 0x86B6,0x86C4,0x86B5,0x86CE,0x86B0,0x86BA,0x86B1,0x86AF,/* 0xC0-0xC7 */
+ 0x86C9,0x86CF,0x86B4,0x86E9,0x86F1,0x86F2,0x86ED,0x86F3,/* 0xC8-0xCF */
+ 0x86D0,0x8713,0x86DE,0x86F4,0x86DF,0x86D8,0x86D1,0x8703,/* 0xD0-0xD7 */
+ 0x8707,0x86F8,0x8708,0x870A,0x870D,0x8709,0x8723,0x873B,/* 0xD8-0xDF */
+ 0x871E,0x8725,0x872E,0x871A,0x873E,0x8748,0x8734,0x8731,/* 0xE0-0xE7 */
+ 0x8729,0x8737,0x873F,0x8782,0x8722,0x877D,0x877E,0x877B,/* 0xE8-0xEF */
+ 0x8760,0x8770,0x874C,0x876E,0x878B,0x8753,0x8763,0x877C,/* 0xF0-0xF7 */
+ 0x8764,0x8759,0x8765,0x8793,0x87AF,0x87A8,0x87D2,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_F3[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x9A5A,0x9A5B,0x9A5C,0x9A5D,0x9A5E,0x9A5F,0x9A60,0x9A61,/* 0x40-0x47 */
+ 0x9A62,0x9A63,0x9A64,0x9A65,0x9A66,0x9A67,0x9A68,0x9A69,/* 0x48-0x4F */
+ 0xF987,0x9A6B,0x9A72,0x9A83,0x9A89,0x9A8D,0x9A8E,0x9A94,/* 0x50-0x57 */
+ 0x9A95,0x9A99,0x9AA6,0x9AA9,0x9AAA,0x9AAB,0x9AAC,0x9AAD,/* 0x58-0x5F */
+ 0x9AAE,0x9AAF,0x9AB2,0x9AB3,0x9AB4,0x9AB5,0x9AB9,0x9ABB,/* 0x60-0x67 */
+ 0x9ABD,0x9ABE,0x9ABF,0x9AC3,0x9AC4,0x9AC6,0x9AC7,0x9AC8,/* 0x68-0x6F */
+ 0x9AC9,0x9ACA,0x9ACD,0x9ACE,0x9ACF,0x9AD0,0x9AD2,0x9AD4,/* 0x70-0x77 */
+ 0x9AD5,0x9AD6,0x9AD7,0x9AD9,0x9ADA,0x9ADB,0x9ADC,0x0000,/* 0x78-0x7F */
+
+ 0x9ADD,0x9ADE,0x9AE0,0x9AE2,0x9AE3,0x9AE4,0x9AE5,0x9AE7,/* 0x80-0x87 */
+ 0x9AE8,0x9AE9,0x9AEA,0x9AEC,0x9AEE,0x9AF0,0x9AF1,0x9AF2,/* 0x88-0x8F */
+ 0x9AF3,0x9AF4,0x9AF5,0x9AF6,0x9AF7,0x9AF8,0x9AFA,0x9AFC,/* 0x90-0x97 */
+ 0x9AFD,0x9AFE,0x9AFF,0x9B00,0x9B01,0x9B02,0x9B04,0x9B05,/* 0x98-0x9F */
+ 0x9B06,0x87C6,0x8788,0x8785,0x87AD,0x8797,0x8783,0x87AB,/* 0xA0-0xA7 */
+ 0x87E5,0x87AC,0x87B5,0x87B3,0x87CB,0x87D3,0x87BD,0x87D1,/* 0xA8-0xAF */
+ 0x87C0,0x87CA,0x87DB,0x87EA,0x87E0,0x87EE,0x8816,0x8813,/* 0xB0-0xB7 */
+ 0x87FE,0x880A,0x881B,0x8821,0x8839,0x883C,0x7F36,0x7F42,/* 0xB8-0xBF */
+ 0x7F44,0x7F45,0x8210,0x7AFA,0x7AFD,0x7B08,0x7B03,0x7B04,/* 0xC0-0xC7 */
+ 0x7B15,0x7B0A,0x7B2B,0x7B0F,0x7B47,0x7B38,0x7B2A,0x7B19,/* 0xC8-0xCF */
+ 0x7B2E,0x7B31,0xF9F8,0x7B25,0x7B24,0x7B33,0x7B3E,0x7B1E,/* 0xD0-0xD7 */
+ 0x7B58,0x7B5A,0x7B45,0x7B75,0x7B4C,0x7B5D,0x7B60,0x7B6E,/* 0xD8-0xDF */
+ 0x7B7B,0x7B62,0x7B72,0x7B71,0x7B90,0x7BA6,0x7BA7,0x7BB8,/* 0xE0-0xE7 */
+ 0x7BAC,0x7B9D,0x7BA8,0x7B85,0x7BAA,0x7B9C,0x7BA2,0x7BAB,/* 0xE8-0xEF */
+ 0x7BB4,0x7BD1,0x7BC1,0x7BCC,0x7BDD,0x7BDA,0x7BE5,0x7BE6,/* 0xF0-0xF7 */
+ 0x7BEA,0x7C0C,0x7BFE,0x7BFC,0x7C0F,0x7C16,0x7C0B,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_F4[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x9B07,0x9B09,0x9B0A,0x9B0B,0x9B0C,0x9B0D,0x9B0E,0x9B10,/* 0x40-0x47 */
+ 0x9B11,0x9B12,0x9B14,0x9B15,0x9B16,0x9B17,0x9B18,0x9B19,/* 0x48-0x4F */
+ 0x9B1A,0x9B1B,0x9B1C,0x9B1D,0x9B1E,0x9B20,0x9B21,0x9B22,/* 0x50-0x57 */
+ 0x9B24,0x9B25,0x9B26,0x9B27,0x9B28,0x9B29,0x9B2A,0x9B2B,/* 0x58-0x5F */
+ 0x9B2C,0x9B2D,0x9B2E,0x9B30,0x9B31,0x9B33,0x9B34,0x9B35,/* 0x60-0x67 */
+ 0x9B36,0x9B37,0x9B38,0x9B39,0x9B3A,0x9B3D,0x9B3E,0x9B3F,/* 0x68-0x6F */
+ 0x9B40,0x9B46,0x9B4A,0x9B4B,0x9B4C,0x9B4E,0x9B50,0x9B52,/* 0x70-0x77 */
+ 0x9B53,0x9B55,0x9B56,0x9B57,0x9B58,0x9B59,0x9B5A,0x0000,/* 0x78-0x7F */
+
+ 0x9B5B,0x9B5C,0x9B5D,0x9B5E,0x9B5F,0x9B60,0x9B61,0x9B62,/* 0x80-0x87 */
+ 0x9B63,0x9B64,0x9B65,0x9B66,0x9B67,0x9B68,0x9B69,0x9B6A,/* 0x88-0x8F */
+ 0x9B6B,0x9B6C,0x9B6D,0x9B6E,0xF939,0x9B70,0x9B71,0x9B72,/* 0x90-0x97 */
+ 0x9B73,0x9B74,0x9B75,0x9B76,0x9B77,0x9B78,0x9B79,0x9B7A,/* 0x98-0x9F */
+ 0x9B7B,0x7C1F,0x7C2A,0x7C26,0x7C38,0x7C41,0x7C40,0x81FE,/* 0xA0-0xA7 */
+ 0x8201,0x8202,0x8204,0x81EC,0x8844,0x8221,0x8222,0x8223,/* 0xA8-0xAF */
+ 0x822D,0x822F,0x8228,0x822B,0x8238,0x823B,0x8233,0x8234,/* 0xB0-0xB7 */
+ 0x823E,0x8244,0x8249,0x824B,0x824F,0x825A,0x825F,0x8268,/* 0xB8-0xBF */
+ 0x887E,0x8885,0x8888,0x88D8,0x88DF,0x895E,0x7F9D,0x7F9F,/* 0xC0-0xC7 */
+ 0x7FA7,0x7FAF,0x7FB0,0x7FB2,0x7C7C,0x6549,0x7C91,0x7C9D,/* 0xC8-0xCF */
+ 0x7C9C,0x7C9E,0x7CA2,0x7CB2,0x7CBC,0x7CBD,0x7CC1,0x7CC7,/* 0xD0-0xD7 */
+ 0x7CCC,0x7CCD,0x7CC8,0x7CC5,0x7CD7,0x7CE8,0x826E,0x66A8,/* 0xD8-0xDF */
+ 0x7FBF,0x7FCE,0x7FD5,0x7FE5,0x7FE1,0x7FE6,0x7FE9,0x7FEE,/* 0xE0-0xE7 */
+ 0x7FF3,0x7CF8,0x7D77,0x7DA6,0x7DAE,0x7E47,0x7E9B,0x9EB8,/* 0xE8-0xEF */
+ 0x9EB4,0x8D73,0x8D84,0x8D94,0x8D91,0x8DB1,0x8D67,0x8D6D,/* 0xF0-0xF7 */
+ 0x8C47,0x8C49,0x914A,0x9150,0x914E,0x914F,0x9164,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_F5[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x9B7C,0x9B7D,0x9B7E,0x9B7F,0x9B80,0x9B81,0x9B82,0x9B83,/* 0x40-0x47 */
+ 0x9B84,0x9B85,0x9B86,0x9B87,0x9B88,0x9B89,0x9B8A,0x9B8B,/* 0x48-0x4F */
+ 0x9B8C,0x9B8D,0x9B8E,0x9B8F,0x9B90,0x9B91,0x9B92,0x9B93,/* 0x50-0x57 */
+ 0x9B94,0x9B95,0x9B96,0x9B97,0x9B98,0x9B99,0x9B9A,0x9B9B,/* 0x58-0x5F */
+ 0x9B9C,0x9B9D,0x9B9E,0x9B9F,0x9BA0,0x9BA1,0x9BA2,0x9BA3,/* 0x60-0x67 */
+ 0x9BA4,0x9BA5,0x9BA6,0x9BA7,0x9BA8,0x9BA9,0x9BAA,0x9BAB,/* 0x68-0x6F */
+ 0x9BAC,0x9BAD,0x9BAE,0x9BAF,0x9BB0,0x9BB1,0x9BB2,0x9BB3,/* 0x70-0x77 */
+ 0x9BB4,0x9BB5,0x9BB6,0x9BB7,0x9BB8,0x9BB9,0x9BBA,0x0000,/* 0x78-0x7F */
+
+ 0x9BBB,0x9BBC,0x9BBD,0x9BBE,0x9BBF,0x9BC0,0x9BC1,0x9BC2,/* 0x80-0x87 */
+ 0x9BC3,0x9BC4,0x9BC5,0x9BC6,0x9BC7,0x9BC8,0x9BC9,0x9BCA,/* 0x88-0x8F */
+ 0x9BCB,0x9BCC,0x9BCD,0x9BCE,0x9BCF,0x9BD0,0x9BD1,0x9BD2,/* 0x90-0x97 */
+ 0x9BD3,0x9BD4,0x9BD5,0x9BD6,0x9BD7,0x9BD8,0x9BD9,0x9BDA,/* 0x98-0x9F */
+ 0x9BDB,0x9162,0x9161,0x9170,0x9169,0x916F,0x917D,0x917E,/* 0xA0-0xA7 */
+ 0x9172,0x9174,0x9179,0x918C,0x9185,0x9190,0x918D,0x9191,/* 0xA8-0xAF */
+ 0x91A2,0x91A3,0x91AA,0x91AD,0x91AE,0x91AF,0x91B5,0xF9B7,/* 0xB0-0xB7 */
+ 0x91BA,0x8C55,0x9E7E,0x8DB8,0x8DEB,0x8E05,0x8E59,0x8E69,/* 0xB8-0xBF */
+ 0x8DB5,0x8DBF,0x8DBC,0x8DBA,0x8DC4,0x8DD6,0x8DD7,0x8DDA,/* 0xC0-0xC7 */
+ 0x8DDE,0x8DCE,0x8DCF,0x8DDB,0x8DC6,0x8DEC,0x8DF7,0x8DF8,/* 0xC8-0xCF */
+ 0x8DE3,0x8DF9,0x8DFB,0x8DE4,0x8E09,0x8DFD,0x8E14,0x8E1D,/* 0xD0-0xD7 */
+ 0x8E1F,0x8E2C,0x8E2E,0x8E23,0x8E2F,0x8E3A,0x8E40,0x8E39,/* 0xD8-0xDF */
+ 0x8E35,0x8E3D,0x8E31,0x8E49,0x8E41,0x8E42,0x8E51,0x8E52,/* 0xE0-0xE7 */
+ 0x8E4A,0x8E70,0x8E76,0x8E7C,0x8E6F,0x8E74,0x8E85,0x8E8F,/* 0xE8-0xEF */
+ 0x8E94,0x8E90,0x8E9C,0x8E9E,0x8C78,0x8C82,0x8C8A,0x8C85,/* 0xF0-0xF7 */
+ 0x8C98,0x8C94,0x659B,0x89D6,0x89DE,0x89DA,0x89DC,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_F6[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x9BDC,0x9BDD,0x9BDE,0x9BDF,0x9BE0,0x9BE1,0x9BE2,0x9BE3,/* 0x40-0x47 */
+ 0x9BE4,0x9BE5,0x9BE6,0x9BE7,0x9BE8,0x9BE9,0x9BEA,0x9BEB,/* 0x48-0x4F */
+ 0x9BEC,0x9BED,0x9BEE,0x9BEF,0x9BF0,0x9BF1,0x9BF2,0x9BF3,/* 0x50-0x57 */
+ 0x9BF4,0x9BF5,0x9BF6,0x9BF7,0x9BF8,0x9BF9,0x9BFA,0x9BFB,/* 0x58-0x5F */
+ 0x9BFC,0x9BFD,0x9BFE,0x9BFF,0x9C00,0x9C01,0x9C02,0x9C03,/* 0x60-0x67 */
+ 0x9C04,0x9C05,0x9C06,0x9C07,0x9C08,0x9C09,0x9C0A,0x9C0B,/* 0x68-0x6F */
+ 0x9C0C,0x9C0D,0x9C0E,0x9C0F,0x9C10,0x9C11,0x9C12,0x9C13,/* 0x70-0x77 */
+ 0x9C14,0x9C15,0x9C16,0x9C17,0x9C18,0x9C19,0x9C1A,0x0000,/* 0x78-0x7F */
+
+ 0x9C1B,0x9C1C,0x9C1D,0x9C1E,0x9C1F,0x9C20,0x9C21,0x9C22,/* 0x80-0x87 */
+ 0x9C23,0x9C24,0x9C25,0x9C26,0x9C27,0x9C28,0x9C29,0x9C2A,/* 0x88-0x8F */
+ 0x9C2B,0x9C2C,0x9C2D,0x9C2E,0x9C2F,0x9C30,0x9C31,0x9C32,/* 0x90-0x97 */
+ 0x9C33,0x9C34,0x9C35,0x9C36,0x9C37,0x9C38,0x9C39,0x9C3A,/* 0x98-0x9F */
+ 0x9C3B,0x89E5,0x89EB,0x89EF,0x8A3E,0x8B26,0x9753,0x96E9,/* 0xA0-0xA7 */
+ 0x96F3,0x96EF,0x9706,0x9701,0x9708,0x970F,0x970E,0x972A,/* 0xA8-0xAF */
+ 0x972D,0x9730,0x973E,0x9F80,0x9F83,0x9F85,0x9F86,0x9F87,/* 0xB0-0xB7 */
+ 0x9F88,0x9F89,0x9F8A,0x9F8C,0x9EFE,0x9F0B,0x9F0D,0x96B9,/* 0xB8-0xBF */
+ 0x96BC,0x96BD,0x96CE,0x96D2,0x77BF,0x96E0,0x928E,0x92AE,/* 0xC0-0xC7 */
+ 0x92C8,0x933E,0x936A,0x93CA,0x938F,0x943E,0x946B,0x9C7F,/* 0xC8-0xCF */
+ 0x9C82,0x9C85,0x9C86,0x9C87,0x9C88,0x7A23,0x9C8B,0x9C8E,/* 0xD0-0xD7 */
+ 0x9C90,0x9C91,0x9C92,0x9C94,0x9C95,0x9C9A,0x9C9B,0x9C9E,/* 0xD8-0xDF */
+ 0x9C9F,0x9CA0,0x9CA1,0x9CA2,0x9CA3,0x9CA5,0x9CA6,0x9CA7,/* 0xE0-0xE7 */
+ 0x9CA8,0x9CA9,0x9CAB,0x9CAD,0x9CAE,0x9CB0,0x9CB1,0x9CB2,/* 0xE8-0xEF */
+ 0x9CB3,0x9CB4,0x9CB5,0x9CB6,0x9CB7,0x9CBA,0x9CBB,0x9CBC,/* 0xF0-0xF7 */
+ 0x9CBD,0x9CC4,0x9CC5,0x9CC6,0x9CC7,0x9CCA,0x9CCB,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_F7[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x9C3C,0x9C3D,0x9C3E,0x9C3F,0x9C40,0x9C41,0x9C42,0x9C43,/* 0x40-0x47 */
+ 0x9C44,0x9C45,0x9C46,0x9C47,0x9C48,0x9C49,0x9C4A,0x9C4B,/* 0x48-0x4F */
+ 0x9C4C,0x9C4D,0x9C4E,0x9C4F,0x9C50,0x9C51,0x9C52,0x9C53,/* 0x50-0x57 */
+ 0x9C54,0x9C55,0x9C56,0xF9F2,0x9C58,0x9C59,0x9C5A,0x9C5B,/* 0x58-0x5F */
+ 0x9C5C,0x9C5D,0x9C5E,0x9C5F,0x9C60,0x9C61,0x9C62,0x9C63,/* 0x60-0x67 */
+ 0x9C64,0x9C65,0x9C66,0x9C67,0x9C68,0x9C69,0x9C6A,0x9C6B,/* 0x68-0x6F */
+ 0x9C6C,0x9C6D,0x9C6E,0x9C6F,0x9C70,0x9C71,0x9C72,0x9C73,/* 0x70-0x77 */
+ 0x9C74,0x9C75,0x9C76,0x9C77,0x9C78,0x9C79,0x9C7A,0x0000,/* 0x78-0x7F */
+
+ 0x9C7B,0x9C7D,0x9C7E,0x9C80,0x9C83,0x9C84,0x9C89,0x9C8A,/* 0x80-0x87 */
+ 0x9C8C,0x9C8F,0x9C93,0x9C96,0x9C97,0x9C98,0x9C99,0x9C9D,/* 0x88-0x8F */
+ 0x9CAA,0x9CAC,0x9CAF,0x9CB9,0x9CBE,0x9CBF,0x9CC0,0x9CC1,/* 0x90-0x97 */
+ 0x9CC2,0x9CC8,0x9CC9,0x9CD1,0x9CD2,0x9CDA,0x9CDB,0x9CE0,/* 0x98-0x9F */
+ 0x9CE1,0x9CCC,0x9CCD,0x9CCE,0x9CCF,0x9CD0,0x9CD3,0x9CD4,/* 0xA0-0xA7 */
+ 0x9CD5,0x9CD7,0x9CD8,0x9CD9,0x9CDC,0x9CDD,0x9CDF,0x9CE2,/* 0xA8-0xAF */
+ 0x977C,0x9785,0x9791,0x9792,0x9794,0x97AF,0x97AB,0x97A3,/* 0xB0-0xB7 */
+ 0x97B2,0x97B4,0x9AB1,0x9AB0,0x9AB7,0x9E58,0x9AB6,0x9ABA,/* 0xB8-0xBF */
+ 0x9ABC,0x9AC1,0x9AC0,0x9AC5,0x9AC2,0x9ACB,0x9ACC,0x9AD1,/* 0xC0-0xC7 */
+ 0x9B45,0x9B43,0x9B47,0x9B49,0x9B48,0x9B4D,0x9B51,0x98E8,/* 0xC8-0xCF */
+ 0x990D,0x992E,0x9955,0x9954,0x9ADF,0x9AE1,0x9AE6,0x9AEF,/* 0xD0-0xD7 */
+ 0x9AEB,0x9AFB,0x9AED,0x9AF9,0x9B08,0x9B0F,0x9B13,0x9B1F,/* 0xD8-0xDF */
+ 0x9B23,0x9EBD,0x9EBE,0x7E3B,0x9E82,0x9E87,0x9E88,0x9E8B,/* 0xE0-0xE7 */
+ 0x9E92,0x93D6,0x9E9D,0xF9F3,0x9EDB,0x9EDC,0x9EDD,0x9EE0,/* 0xE8-0xEF */
+ 0x9EDF,0x9EE2,0x9EE9,0x9EE7,0x9EE5,0x9EEA,0x9EEF,0x9F22,/* 0xF0-0xF7 */
+ 0x9F2C,0x9F2F,0x9F39,0x9F37,0x9F3D,0x9F3E,0x9F44,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_F8[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x9CE3,0x9CE4,0x9CE5,0x9CE6,0x9CE7,0x9CE8,0x9CE9,0x9CEA,/* 0x40-0x47 */
+ 0x9CEB,0x9CEC,0x9CED,0x9CEE,0x9CEF,0x9CF0,0x9CF1,0x9CF2,/* 0x48-0x4F */
+ 0x9CF3,0x9CF4,0x9CF5,0x9CF6,0x9CF7,0x9CF8,0x9CF9,0x9CFA,/* 0x50-0x57 */
+ 0x9CFB,0x9CFC,0x9CFD,0x9CFE,0x9CFF,0x9D00,0x9D01,0x9D02,/* 0x58-0x5F */
+ 0x9D03,0x9D04,0x9D05,0x9D06,0x9D07,0x9D08,0x9D09,0x9D0A,/* 0x60-0x67 */
+ 0x9D0B,0x9D0C,0x9D0D,0x9D0E,0x9D0F,0x9D10,0x9D11,0x9D12,/* 0x68-0x6F */
+ 0x9D13,0x9D14,0x9D15,0x9D16,0x9D17,0x9D18,0x9D19,0x9D1A,/* 0x70-0x77 */
+ 0x9D1B,0x9D1C,0x9D1D,0x9D1E,0x9D1F,0x9D20,0x9D21,0x0000,/* 0x78-0x7F */
+
+ 0x9D22,0x9D23,0x9D24,0x9D25,0x9D26,0x9D27,0x9D28,0x9D29,/* 0x80-0x87 */
+ 0x9D2A,0x9D2B,0x9D2C,0x9D2D,0x9D2E,0x9D2F,0x9D30,0x9D31,/* 0x88-0x8F */
+ 0x9D32,0x9D33,0x9D34,0x9D35,0x9D36,0x9D37,0x9D38,0x9D39,/* 0x90-0x97 */
+ 0x9D3A,0x9D3B,0x9D3C,0x9D3D,0x9D3E,0x9D3F,0x9D40,0x9D41,/* 0x98-0x9F */
+ 0x9D42,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xA0-0xA7 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xA8-0xAF */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xB0-0xB7 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xB8-0xBF */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xC0-0xC7 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xC8-0xCF */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xD0-0xD7 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xD8-0xDF */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xE0-0xE7 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xE8-0xEF */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xF0-0xF7 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_F9[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x9D43,0x9D44,0x9D45,0x9D46,0x9D47,0x9D48,0x9D49,0x9D4A,/* 0x40-0x47 */
+ 0x9D4B,0x9D4C,0x9D4D,0x9D4E,0x9D4F,0x9D50,0x9D51,0x9D52,/* 0x48-0x4F */
+ 0x9D53,0x9D54,0x9D55,0x9D56,0x9D57,0x9D58,0x9D59,0x9D5A,/* 0x50-0x57 */
+ 0x9D5B,0x9D5C,0x9D5D,0x9D5E,0x9D5F,0x9D60,0x9D61,0x9D62,/* 0x58-0x5F */
+ 0x9D63,0x9D64,0x9D65,0x9D66,0x9D67,0x9D68,0x9D69,0x9D6A,/* 0x60-0x67 */
+ 0x9D6B,0x9D6C,0x9D6D,0x9D6E,0x9D6F,0x9D70,0x9D71,0x9D72,/* 0x68-0x6F */
+ 0x9D73,0x9D74,0x9D75,0x9D76,0x9D77,0x9D78,0x9D79,0x9D7A,/* 0x70-0x77 */
+ 0x9D7B,0x9D7C,0x9D7D,0x9D7E,0x9D7F,0x9D80,0x9D81,0x0000,/* 0x78-0x7F */
+
+ 0x9D82,0x9D83,0x9D84,0x9D85,0x9D86,0x9D87,0x9D88,0x9D89,/* 0x80-0x87 */
+ 0x9D8A,0x9D8B,0x9D8C,0x9D8D,0x9D8E,0x9D8F,0x9D90,0x9D91,/* 0x88-0x8F */
+ 0x9D92,0x9D93,0x9D94,0x9D95,0x9D96,0x9D97,0x9D98,0x9D99,/* 0x90-0x97 */
+ 0x9D9A,0x9D9B,0x9D9C,0x9D9D,0x9D9E,0x9D9F,0x9DA0,0x9DA1,/* 0x98-0x9F */
+ 0x9DA2,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xA0-0xA7 */
+};
+
+static wchar_t c2u_FA[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x9DA3,0x9DA4,0x9DA5,0x9DA6,0x9DA7,0x9DA8,0x9DA9,0x9DAA,/* 0x40-0x47 */
+ 0x9DAB,0x9DAC,0x9DAD,0x9DAE,0x9DAF,0x9DB0,0x9DB1,0x9DB2,/* 0x48-0x4F */
+ 0x9DB3,0xFA2D,0x9DB5,0x9DB6,0x9DB7,0x9DB8,0x9DB9,0x9DBA,/* 0x50-0x57 */
+ 0x9DBB,0x9DBC,0x9DBD,0x9DBE,0x9DBF,0x9DC0,0x9DC1,0x9DC2,/* 0x58-0x5F */
+ 0x9DC3,0x9DC4,0x9DC5,0x9DC6,0x9DC7,0x9DC8,0x9DC9,0x9DCA,/* 0x60-0x67 */
+ 0x9DCB,0x9DCC,0x9DCD,0x9DCE,0x9DCF,0x9DD0,0x9DD1,0x9DD2,/* 0x68-0x6F */
+ 0x9DD3,0x9DD4,0x9DD5,0x9DD6,0x9DD7,0x9DD8,0x9DD9,0x9DDA,/* 0x70-0x77 */
+ 0x9DDB,0x9DDC,0x9DDD,0x9DDE,0x9DDF,0x9DE0,0x9DE1,0x0000,/* 0x78-0x7F */
+
+ 0x9DE2,0x9DE3,0x9DE4,0x9DE5,0x9DE6,0x9DE7,0x9DE8,0x9DE9,/* 0x80-0x87 */
+ 0x9DEA,0x9DEB,0x9DEC,0x9DED,0x9DEE,0x9DEF,0x9DF0,0x9DF1,/* 0x88-0x8F */
+ 0x9DF2,0x9DF3,0x9DF4,0x9DF5,0x9DF6,0x9DF7,0x9DF8,0x9DF9,/* 0x90-0x97 */
+ 0xF93A,0x9DFB,0x9DFC,0x9DFD,0x9DFE,0x9DFF,0x9E00,0x9E01,/* 0x98-0x9F */
+ 0x9E02,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xA0-0xA7 */
+};
+
+static wchar_t c2u_FB[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x9E03,0x9E04,0x9E05,0x9E06,0x9E07,0x9E08,0x9E09,0x9E0A,/* 0x40-0x47 */
+ 0x9E0B,0x9E0C,0x9E0D,0x9E0E,0x9E0F,0x9E10,0x9E11,0x9E12,/* 0x48-0x4F */
+ 0x9E13,0x9E14,0x9E15,0x9E16,0x9E17,0x9E18,0x9E19,0x9E1A,/* 0x50-0x57 */
+ 0x9E1B,0x9E1C,0x9E1D,0xF920,0x9E24,0x9E27,0x9E2E,0x9E30,/* 0x58-0x5F */
+ 0x9E34,0x9E3B,0x9E3C,0x9E40,0x9E4D,0x9E50,0x9E52,0x9E53,/* 0x60-0x67 */
+ 0x9E54,0x9E56,0x9E59,0x9E5D,0x9E5F,0x9E60,0x9E61,0x9E62,/* 0x68-0x6F */
+ 0x9E65,0x9E6E,0x9E6F,0x9E72,0x9E74,0x9E75,0x9E76,0x9E77,/* 0x70-0x77 */
+ 0x9E78,0x9E79,0x9E7A,0x9E7B,0x9E7C,0x9E7D,0x9E80,0x0000,/* 0x78-0x7F */
+
+ 0x9E81,0x9E83,0x9E84,0x9E85,0x9E86,0x9E89,0x9E8A,0x9E8C,/* 0x80-0x87 */
+ 0x9E8D,0x9E8E,0x9E8F,0x9E90,0x9E91,0x9E94,0x9E95,0x9E96,/* 0x88-0x8F */
+ 0xF988,0x9E98,0x9E99,0x9E9A,0x9E9B,0x9E9C,0x9E9E,0x9EA0,/* 0x90-0x97 */
+ 0x9EA1,0x9EA2,0x9EA3,0x9EA4,0x9EA5,0x9EA7,0x9EA8,0x9EA9,/* 0x98-0x9F */
+ 0x9EAA,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xA0-0xA7 */
+};
+
+static wchar_t c2u_FC[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x9EAB,0x9EAC,0x9EAD,0x9EAE,0x9EAF,0x9EB0,0x9EB1,0x9EB2,/* 0x40-0x47 */
+ 0x9EB3,0x9EB5,0x9EB6,0x9EB7,0x9EB9,0x9EBA,0x9EBC,0x9EBF,/* 0x48-0x4F */
+ 0x9EC0,0x9EC1,0x9EC2,0x9EC3,0x9EC5,0x9EC6,0x9EC7,0x9EC8,/* 0x50-0x57 */
+ 0x9ECA,0x9ECB,0x9ECC,0x9ED0,0x9ED2,0x9ED3,0x9ED5,0x9ED6,/* 0x58-0x5F */
+ 0x9ED7,0x9ED9,0x9EDA,0x9EDE,0x9EE1,0x9EE3,0x9EE4,0x9EE6,/* 0x60-0x67 */
+ 0x9EE8,0x9EEB,0x9EEC,0x9EED,0x9EEE,0x9EF0,0x9EF1,0x9EF2,/* 0x68-0x6F */
+ 0x9EF3,0x9EF4,0x9EF5,0x9EF6,0x9EF7,0x9EF8,0x9EFA,0x9EFD,/* 0x70-0x77 */
+ 0x9EFF,0x9F00,0x9F01,0x9F02,0x9F03,0x9F04,0x9F05,0x0000,/* 0x78-0x7F */
+
+ 0x9F06,0x9F07,0x9F08,0x9F09,0x9F0A,0x9F0C,0x9F0F,0x9F11,/* 0x80-0x87 */
+ 0x9F12,0x9F14,0x9F15,0x9F16,0x9F18,0x9F1A,0x9F1B,0x9F1C,/* 0x88-0x8F */
+ 0x9F1D,0x9F1E,0x9F1F,0x9F21,0x9F23,0x9F24,0x9F25,0x9F26,/* 0x90-0x97 */
+ 0x9F27,0x9F28,0x9F29,0x9F2A,0x9F2B,0x9F2D,0x9F2E,0x9F30,/* 0x98-0x9F */
+ 0x9F31,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xA0-0xA7 */
+};
+
+static wchar_t c2u_FD[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x9F32,0x9F33,0x9F34,0x9F35,0x9F36,0x9F38,0x9F3A,0x9F3C,/* 0x40-0x47 */
+ 0x9F3F,0x9F40,0x9F41,0x9F42,0x9F43,0x9F45,0x9F46,0x9F47,/* 0x48-0x4F */
+ 0x9F48,0x9F49,0x9F4A,0x9F4B,0x9F4C,0x9F4D,0x9F4E,0x9F4F,/* 0x50-0x57 */
+ 0x9F52,0x9F53,0x9F54,0x9F55,0x9F56,0x9F57,0x9F58,0x9F59,/* 0x58-0x5F */
+ 0x9F5A,0x9F5B,0x9F5C,0x9F5D,0x9F5E,0x9F5F,0x9F60,0x9F61,/* 0x60-0x67 */
+ 0x9F62,0x9F63,0x9F64,0x9F65,0x9F66,0x9F67,0x9F68,0x9F69,/* 0x68-0x6F */
+ 0x9F6A,0x9F6B,0x9F6C,0x9F6D,0x9F6E,0x9F6F,0x9F70,0x9F71,/* 0x70-0x77 */
+ 0x9F72,0x9F73,0x9F74,0x9F75,0x9F76,0x9F77,0x9F78,0x0000,/* 0x78-0x7F */
+
+ 0x9F79,0x9F7A,0x9F7B,0x9F7C,0x9F7D,0x9F7E,0x9F81,0x9F82,/* 0x80-0x87 */
+ 0xF9C4,0x9F8E,0x9F8F,0x9F90,0x9F91,0x9F92,0x9F93,0x9F94,/* 0x88-0x8F */
+ 0x9F95,0x9F96,0x9F97,0x9F98,0xF908,0x9F9D,0x9F9E,0x9FA1,/* 0x90-0x97 */
+ 0x9FA2,0x9FA3,0x9FA4,0x9FA5,0xF92C,0xF979,0xF995,0xF9E7,/* 0x98-0x9F */
+ 0xF9F1,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xA0-0xA7 */
+};
+
+static wchar_t c2u_FE[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0xFA0C,0xFA0D,0xFA0E,0xFA0F,0xFA11,0xFA13,0xFA14,0xFA18,/* 0x40-0x47 */
+ 0xFA1F,0xFA20,0xFA21,0xFA23,0xFA24,0xFA27,0xFA28,0xFA29,/* 0x48-0x4F */
+};
+
+static wchar_t *page_charset2uni[256] = {
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, c2u_81, c2u_82, c2u_83, c2u_84, c2u_85, c2u_86, c2u_87,
+ c2u_88, c2u_89, c2u_8A, c2u_8B, c2u_8C, c2u_8D, c2u_8E, c2u_8F,
+ c2u_90, c2u_91, c2u_92, c2u_93, c2u_94, c2u_95, c2u_96, c2u_97,
+ c2u_98, c2u_99, c2u_9A, c2u_9B, c2u_9C, c2u_9D, c2u_9E, c2u_9F,
+ c2u_A0, c2u_A1, c2u_A2, c2u_A3, c2u_A4, c2u_A5, c2u_A6, c2u_A7,
+ c2u_A8, c2u_A9, c2u_AA, c2u_AB, c2u_AC, c2u_AD, c2u_AE, c2u_AF,
+ c2u_B0, c2u_B1, c2u_B2, c2u_B3, c2u_B4, c2u_B5, c2u_B6, c2u_B7,
+ c2u_B8, c2u_B9, c2u_BA, c2u_BB, c2u_BC, c2u_BD, c2u_BE, c2u_BF,
+ c2u_C0, c2u_C1, c2u_C2, c2u_C3, c2u_C4, c2u_C5, c2u_C6, c2u_C7,
+ c2u_C8, c2u_C9, c2u_CA, c2u_CB, c2u_CC, c2u_CD, c2u_CE, c2u_CF,
+ c2u_D0, c2u_D1, c2u_D2, c2u_D3, c2u_D4, c2u_D5, c2u_D6, c2u_D7,
+ c2u_D8, c2u_D9, c2u_DA, c2u_DB, c2u_DC, c2u_DD, c2u_DE, c2u_DF,
+ c2u_E0, c2u_E1, c2u_E2, c2u_E3, c2u_E4, c2u_E5, c2u_E6, c2u_E7,
+ c2u_E8, c2u_E9, c2u_EA, c2u_EB, c2u_EC, c2u_ED, c2u_EE, c2u_EF,
+ c2u_F0, c2u_F1, c2u_F2, c2u_F3, c2u_F4, c2u_F5, c2u_F6, c2u_F7,
+ c2u_F8, c2u_F9, c2u_FA, c2u_FB, c2u_FC, c2u_FD, c2u_FE, NULL,
+};
+
+static unsigned char u2c_01[512] = {
+ 0xA8, 0xA1, 0xA8, 0xA1, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0xA8, 0xA5, 0xA8, 0xA5, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0xA8, 0xA7, 0xA8, 0xA7, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xA8, 0xA9, 0xA8, 0xA9, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0xA8, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0xA8, 0xBE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0xA8, 0xAD, 0xA8, 0xAD, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0xA8, 0xB1, 0xA8, 0xB1, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0xA1, 0xCE, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0xA8, 0xA3, 0xA8, 0xA3, 0xA8, 0xAB, /* 0xCC-0xCF */
+ 0xA8, 0xAB, 0xA8, 0xAF, 0xA8, 0xAF, 0xA8, 0xB3, /* 0xD0-0xD3 */
+ 0xA8, 0xB3, 0xA8, 0xB5, 0xA8, 0xB5, 0xA8, 0xB6, /* 0xD4-0xD7 */
+ 0xA8, 0xB6, 0xA8, 0xB7, 0xA8, 0xB7, 0xA8, 0xB8, /* 0xD8-0xDB */
+ 0xA8, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+};
+
+static unsigned char u2c_02[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0xA8, 0xBB, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0xA8, 0xC0, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA1, 0xA6, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xA1, 0xA5, 0xA8, 0x40, 0xA8, 0x41, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0xA8, 0x42, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+};
+
+static unsigned char u2c_03[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xA6, 0xA1, 0xA6, 0xA2, 0xA6, 0xA3, /* 0x90-0x93 */
+ 0xA6, 0xA4, 0xA6, 0xA5, 0xA6, 0xA6, 0xA6, 0xA7, /* 0x94-0x97 */
+ 0xA6, 0xA8, 0xA6, 0xA9, 0xA6, 0xAA, 0xA6, 0xAB, /* 0x98-0x9B */
+ 0xA6, 0xAC, 0xA6, 0xAD, 0xA6, 0xAE, 0xA6, 0xAF, /* 0x9C-0x9F */
+ 0xA6, 0xB0, 0xA6, 0xB1, 0x00, 0x00, 0xA6, 0xB2, /* 0xA0-0xA3 */
+ 0xA6, 0xB3, 0xA6, 0xB4, 0xA6, 0xB5, 0xA6, 0xB6, /* 0xA4-0xA7 */
+ 0xA6, 0xB7, 0xA6, 0xB8, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0xA6, 0xC1, 0xA6, 0xC2, 0xA6, 0xC3, /* 0xB0-0xB3 */
+ 0xA6, 0xC4, 0xA6, 0xC5, 0xA6, 0xC6, 0xA6, 0xC7, /* 0xB4-0xB7 */
+ 0xA6, 0xC8, 0xA6, 0xC9, 0xA6, 0xCA, 0xA6, 0xCB, /* 0xB8-0xBB */
+ 0xA6, 0xCC, 0xA6, 0xCD, 0xA6, 0xCE, 0xA6, 0xCF, /* 0xBC-0xBF */
+ 0xA6, 0xD0, 0xA6, 0xD1, 0x00, 0x00, 0xA6, 0xD2, /* 0xC0-0xC3 */
+ 0xA6, 0xD3, 0xA6, 0xD4, 0xA6, 0xD5, 0xA6, 0xD6, /* 0xC4-0xC7 */
+ 0xA6, 0xD7, 0xA6, 0xD8, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+};
+
+static unsigned char u2c_04[512] = {
+ 0x00, 0x00, 0xA7, 0xA7, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xA7, 0xA1, 0xA7, 0xA2, 0xA7, 0xA3, 0xA7, 0xA4, /* 0x10-0x13 */
+ 0xA7, 0xA5, 0xA7, 0xA6, 0xA7, 0xA8, 0xA7, 0xA9, /* 0x14-0x17 */
+ 0xA7, 0xAA, 0xA7, 0xAB, 0xA7, 0xAC, 0xA7, 0xAD, /* 0x18-0x1B */
+ 0xA7, 0xAE, 0xA7, 0xAF, 0xA7, 0xB0, 0xA7, 0xB1, /* 0x1C-0x1F */
+ 0xA7, 0xB2, 0xA7, 0xB3, 0xA7, 0xB4, 0xA7, 0xB5, /* 0x20-0x23 */
+ 0xA7, 0xB6, 0xA7, 0xB7, 0xA7, 0xB8, 0xA7, 0xB9, /* 0x24-0x27 */
+ 0xA7, 0xBA, 0xA7, 0xBB, 0xA7, 0xBC, 0xA7, 0xBD, /* 0x28-0x2B */
+ 0xA7, 0xBE, 0xA7, 0xBF, 0xA7, 0xC0, 0xA7, 0xC1, /* 0x2C-0x2F */
+ 0xA7, 0xD1, 0xA7, 0xD2, 0xA7, 0xD3, 0xA7, 0xD4, /* 0x30-0x33 */
+ 0xA7, 0xD5, 0xA7, 0xD6, 0xA7, 0xD8, 0xA7, 0xD9, /* 0x34-0x37 */
+ 0xA7, 0xDA, 0xA7, 0xDB, 0xA7, 0xDC, 0xA7, 0xDD, /* 0x38-0x3B */
+ 0xA7, 0xDE, 0xA7, 0xDF, 0xA7, 0xE0, 0xA7, 0xE1, /* 0x3C-0x3F */
+ 0xA7, 0xE2, 0xA7, 0xE3, 0xA7, 0xE4, 0xA7, 0xE5, /* 0x40-0x43 */
+ 0xA7, 0xE6, 0xA7, 0xE7, 0xA7, 0xE8, 0xA7, 0xE9, /* 0x44-0x47 */
+ 0xA7, 0xEA, 0xA7, 0xEB, 0xA7, 0xEC, 0xA7, 0xED, /* 0x48-0x4B */
+ 0xA7, 0xEE, 0xA7, 0xEF, 0xA7, 0xF0, 0xA7, 0xF1, /* 0x4C-0x4F */
+ 0x00, 0x00, 0xA7, 0xD7, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+};
+
+static unsigned char u2c_20[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xA9, 0x5C, 0x00, 0x00, 0x00, 0x00, 0xA8, 0x43, /* 0x10-0x13 */
+ 0xA1, 0xAA, 0xA8, 0x44, 0xA1, 0xAC, 0x00, 0x00, /* 0x14-0x17 */
+ 0xA1, 0xAE, 0xA1, 0xAF, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0xA1, 0xB0, 0xA1, 0xB1, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0xA8, 0x45, 0xA1, 0xAD, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xA1, 0xEB, 0x00, 0x00, 0xA1, 0xE4, 0xA1, 0xE5, /* 0x30-0x33 */
+ 0x00, 0x00, 0xA8, 0x46, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA1, 0xF9, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0xA3, 0xFE, 0x00, 0x00, /* 0x3C-0x3F */
+};
+
+static unsigned char u2c_21[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA1, 0xE6, /* 0x00-0x03 */
+ 0x00, 0x00, 0xA8, 0x47, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0xA8, 0x48, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0xA1, 0xED, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0xA9, 0x59, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xA2, 0xF1, 0xA2, 0xF2, 0xA2, 0xF3, 0xA2, 0xF4, /* 0x60-0x63 */
+ 0xA2, 0xF5, 0xA2, 0xF6, 0xA2, 0xF7, 0xA2, 0xF8, /* 0x64-0x67 */
+ 0xA2, 0xF9, 0xA2, 0xFA, 0xA2, 0xFB, 0xA2, 0xFC, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xA2, 0xA1, 0xA2, 0xA2, 0xA2, 0xA3, 0xA2, 0xA4, /* 0x70-0x73 */
+ 0xA2, 0xA5, 0xA2, 0xA6, 0xA2, 0xA7, 0xA2, 0xA8, /* 0x74-0x77 */
+ 0xA2, 0xA9, 0xA2, 0xAA, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0xA1, 0xFB, 0xA1, 0xFC, 0xA1, 0xFA, 0xA1, 0xFD, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0xA8, 0x49, 0xA8, 0x4A, /* 0x94-0x97 */
+ 0xA8, 0x4B, 0xA8, 0x4C, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+};
+
+static unsigned char u2c_22[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0xA1, 0xCA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA1, 0xC7, /* 0x0C-0x0F */
+ 0x00, 0x00, 0xA1, 0xC6, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0xA8, 0x4D, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0xA1, 0xE3, 0x00, 0x00, 0xA1, 0xCC, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0xA1, 0xD8, 0xA1, 0xDE, 0xA8, 0x4E, /* 0x1C-0x1F */
+ 0xA1, 0xCF, 0x00, 0x00, 0x00, 0x00, 0xA8, 0x4F, /* 0x20-0x23 */
+ 0x00, 0x00, 0xA1, 0xCE, 0x00, 0x00, 0xA1, 0xC4, /* 0x24-0x27 */
+ 0xA1, 0xC5, 0xA1, 0xC9, 0xA1, 0xC8, 0xA1, 0xD2, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0xA1, 0xD3, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0xA1, 0xE0, 0xA1, 0xDF, 0xA1, 0xC3, 0xA1, 0xCB, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0xA1, 0xAB, 0xA1, 0xD7, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0xA1, 0xD6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0xA1, 0xD5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0xA8, 0x50, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xA1, 0xD9, 0xA1, 0xD4, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0xA1, 0xDC, 0xA1, 0xDD, 0xA8, 0x51, 0xA8, 0x52, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0xA1, 0xDA, 0xA1, 0xDB, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0xA8, 0x92, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0xA1, 0xD1, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0xA1, 0xCD, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA8, 0x53, /* 0xBC-0xBF */
+};
+
+static unsigned char u2c_23[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0xA1, 0xD0, 0x00, 0x00, /* 0x10-0x13 */
+};
+
+static unsigned char u2c_24[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xA2, 0xD9, 0xA2, 0xDA, 0xA2, 0xDB, 0xA2, 0xDC, /* 0x60-0x63 */
+ 0xA2, 0xDD, 0xA2, 0xDE, 0xA2, 0xDF, 0xA2, 0xE0, /* 0x64-0x67 */
+ 0xA2, 0xE1, 0xA2, 0xE2, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0xA2, 0xC5, 0xA2, 0xC6, 0xA2, 0xC7, 0xA2, 0xC8, /* 0x74-0x77 */
+ 0xA2, 0xC9, 0xA2, 0xCA, 0xA2, 0xCB, 0xA2, 0xCC, /* 0x78-0x7B */
+ 0xA2, 0xCD, 0xA2, 0xCE, 0xA2, 0xCF, 0xA2, 0xD0, /* 0x7C-0x7F */
+
+ 0xA2, 0xD1, 0xA2, 0xD2, 0xA2, 0xD3, 0xA2, 0xD4, /* 0x80-0x83 */
+ 0xA2, 0xD5, 0xA2, 0xD6, 0xA2, 0xD7, 0xA2, 0xD8, /* 0x84-0x87 */
+ 0xA2, 0xB1, 0xA2, 0xB2, 0xA2, 0xB3, 0xA2, 0xB4, /* 0x88-0x8B */
+ 0xA2, 0xB5, 0xA2, 0xB6, 0xA2, 0xB7, 0xA2, 0xB8, /* 0x8C-0x8F */
+ 0xA2, 0xB9, 0xA2, 0xBA, 0xA2, 0xBB, 0xA2, 0xBC, /* 0x90-0x93 */
+ 0xA2, 0xBD, 0xA2, 0xBE, 0xA2, 0xBF, 0xA2, 0xC0, /* 0x94-0x97 */
+ 0xA2, 0xC1, 0xA2, 0xC2, 0xA2, 0xC3, 0xA2, 0xC4, /* 0x98-0x9B */
+};
+
+static unsigned char u2c_25[512] = {
+ 0xA9, 0xA4, 0xA9, 0xA5, 0xA9, 0xA6, 0xA9, 0xA7, /* 0x00-0x03 */
+ 0xA9, 0xA8, 0xA9, 0xA9, 0xA9, 0xAA, 0xA9, 0xAB, /* 0x04-0x07 */
+ 0xA9, 0xAC, 0xA9, 0xAD, 0xA9, 0xAE, 0xA9, 0xAF, /* 0x08-0x0B */
+ 0xA9, 0xB0, 0xA9, 0xB1, 0xA9, 0xB2, 0xA9, 0xB3, /* 0x0C-0x0F */
+ 0xA9, 0xB4, 0xA9, 0xB5, 0xA9, 0xB6, 0xA9, 0xB7, /* 0x10-0x13 */
+ 0xA9, 0xB8, 0xA9, 0xB9, 0xA9, 0xBA, 0xA9, 0xBB, /* 0x14-0x17 */
+ 0xA9, 0xBC, 0xA9, 0xBD, 0xA9, 0xBE, 0xA9, 0xBF, /* 0x18-0x1B */
+ 0xA9, 0xC0, 0xA9, 0xC1, 0xA9, 0xC2, 0xA9, 0xC3, /* 0x1C-0x1F */
+ 0xA9, 0xC4, 0xA9, 0xC5, 0xA9, 0xC6, 0xA9, 0xC7, /* 0x20-0x23 */
+ 0xA9, 0xC8, 0xA9, 0xC9, 0xA9, 0xCA, 0xA9, 0xCB, /* 0x24-0x27 */
+ 0xA9, 0xCC, 0xA9, 0xCD, 0xA9, 0xCE, 0xA9, 0xCF, /* 0x28-0x2B */
+ 0xA9, 0xD0, 0xA9, 0xD1, 0xA9, 0xD2, 0xA9, 0xD3, /* 0x2C-0x2F */
+ 0xA9, 0xD4, 0xA9, 0xD5, 0xA9, 0xD6, 0xA9, 0xD7, /* 0x30-0x33 */
+ 0xA9, 0xD8, 0xA9, 0xD9, 0xA9, 0xDA, 0xA9, 0xDB, /* 0x34-0x37 */
+ 0xA9, 0xDC, 0xA9, 0xDD, 0xA9, 0xDE, 0xA9, 0xDF, /* 0x38-0x3B */
+ 0xA9, 0xE0, 0xA9, 0xE1, 0xA9, 0xE2, 0xA9, 0xE3, /* 0x3C-0x3F */
+ 0xA9, 0xE4, 0xA9, 0xE5, 0xA9, 0xE6, 0xA9, 0xE7, /* 0x40-0x43 */
+ 0xA9, 0xE8, 0xA9, 0xE9, 0xA9, 0xEA, 0xA9, 0xEB, /* 0x44-0x47 */
+ 0xA9, 0xEC, 0xA9, 0xED, 0xA9, 0xEE, 0xA9, 0xEF, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xA8, 0x54, 0xA8, 0x55, 0xA8, 0x56, 0xA8, 0x57, /* 0x50-0x53 */
+ 0xA8, 0x58, 0xA8, 0x59, 0xA8, 0x5A, 0xA8, 0x5B, /* 0x54-0x57 */
+ 0xA8, 0x5C, 0xA8, 0x5D, 0xA8, 0x5E, 0xA8, 0x5F, /* 0x58-0x5B */
+ 0xA8, 0x60, 0xA8, 0x61, 0xA8, 0x62, 0xA8, 0x63, /* 0x5C-0x5F */
+ 0xA8, 0x64, 0xA8, 0x65, 0xA8, 0x66, 0xA8, 0x67, /* 0x60-0x63 */
+ 0xA8, 0x68, 0xA8, 0x69, 0xA8, 0x6A, 0xA8, 0x6B, /* 0x64-0x67 */
+ 0xA8, 0x6C, 0xA8, 0x6D, 0xA8, 0x6E, 0xA8, 0x6F, /* 0x68-0x6B */
+ 0xA8, 0x70, 0xA8, 0x71, 0xA8, 0x72, 0xA8, 0x73, /* 0x6C-0x6F */
+ 0xA8, 0x74, 0xA8, 0x75, 0xA8, 0x76, 0xA8, 0x77, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0xA8, 0x78, 0xA8, 0x79, 0xA8, 0x7A, /* 0x80-0x83 */
+ 0xA8, 0x7B, 0xA8, 0x7C, 0xA8, 0x7D, 0xA8, 0x7E, /* 0x84-0x87 */
+ 0xA8, 0x80, 0xA8, 0x81, 0xA8, 0x82, 0xA8, 0x83, /* 0x88-0x8B */
+ 0xA8, 0x84, 0xA8, 0x85, 0xA8, 0x86, 0xA8, 0x87, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA8, 0x88, /* 0x90-0x93 */
+ 0xA8, 0x89, 0xA8, 0x8A, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0xA1, 0xF6, 0xA1, 0xF5, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0xA1, 0xF8, 0xA1, 0xF7, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0xA8, 0x8B, 0xA8, 0x8C, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0xA1, 0xF4, 0xA1, 0xF3, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA1, 0xF0, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0xA1, 0xF2, 0xA1, 0xF1, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0xA8, 0x8D, 0xA8, 0x8E, /* 0xE0-0xE3 */
+ 0xA8, 0x8F, 0xA8, 0x90, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_26[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0xA1, 0xEF, 0xA1, 0xEE, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0xA8, 0x91, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0xA1, 0xE2, 0x00, 0x00, 0xA1, 0xE1, 0x00, 0x00, /* 0x40-0x43 */
+};
+
+static unsigned char u2c_30[512] = {
+ 0xA1, 0xA1, 0xA1, 0xA2, 0xA1, 0xA3, 0xA1, 0xA8, /* 0x00-0x03 */
+ 0x00, 0x00, 0xA1, 0xA9, 0xA9, 0x65, 0xA9, 0x96, /* 0x04-0x07 */
+ 0xA1, 0xB4, 0xA1, 0xB5, 0xA1, 0xB6, 0xA1, 0xB7, /* 0x08-0x0B */
+ 0xA1, 0xB8, 0xA1, 0xB9, 0xA1, 0xBA, 0xA1, 0xBB, /* 0x0C-0x0F */
+ 0xA1, 0xBE, 0xA1, 0xBF, 0xA8, 0x93, 0xA1, 0xFE, /* 0x10-0x13 */
+ 0xA1, 0xB2, 0xA1, 0xB3, 0xA1, 0xBC, 0xA1, 0xBD, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0xA8, 0x94, 0xA8, 0x95, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0xA9, 0x40, 0xA9, 0x41, 0xA9, 0x42, /* 0x20-0x23 */
+ 0xA9, 0x43, 0xA9, 0x44, 0xA9, 0x45, 0xA9, 0x46, /* 0x24-0x27 */
+ 0xA9, 0x47, 0xA9, 0x48, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0xA4, 0xA1, 0xA4, 0xA2, 0xA4, 0xA3, /* 0x40-0x43 */
+ 0xA4, 0xA4, 0xA4, 0xA5, 0xA4, 0xA6, 0xA4, 0xA7, /* 0x44-0x47 */
+ 0xA4, 0xA8, 0xA4, 0xA9, 0xA4, 0xAA, 0xA4, 0xAB, /* 0x48-0x4B */
+ 0xA4, 0xAC, 0xA4, 0xAD, 0xA4, 0xAE, 0xA4, 0xAF, /* 0x4C-0x4F */
+ 0xA4, 0xB0, 0xA4, 0xB1, 0xA4, 0xB2, 0xA4, 0xB3, /* 0x50-0x53 */
+ 0xA4, 0xB4, 0xA4, 0xB5, 0xA4, 0xB6, 0xA4, 0xB7, /* 0x54-0x57 */
+ 0xA4, 0xB8, 0xA4, 0xB9, 0xA4, 0xBA, 0xA4, 0xBB, /* 0x58-0x5B */
+ 0xA4, 0xBC, 0xA4, 0xBD, 0xA4, 0xBE, 0xA4, 0xBF, /* 0x5C-0x5F */
+ 0xA4, 0xC0, 0xA4, 0xC1, 0xA4, 0xC2, 0xA4, 0xC3, /* 0x60-0x63 */
+ 0xA4, 0xC4, 0xA4, 0xC5, 0xA4, 0xC6, 0xA4, 0xC7, /* 0x64-0x67 */
+ 0xA4, 0xC8, 0xA4, 0xC9, 0xA4, 0xCA, 0xA4, 0xCB, /* 0x68-0x6B */
+ 0xA4, 0xCC, 0xA4, 0xCD, 0xA4, 0xCE, 0xA4, 0xCF, /* 0x6C-0x6F */
+ 0xA4, 0xD0, 0xA4, 0xD1, 0xA4, 0xD2, 0xA4, 0xD3, /* 0x70-0x73 */
+ 0xA4, 0xD4, 0xA4, 0xD5, 0xA4, 0xD6, 0xA4, 0xD7, /* 0x74-0x77 */
+ 0xA4, 0xD8, 0xA4, 0xD9, 0xA4, 0xDA, 0xA4, 0xDB, /* 0x78-0x7B */
+ 0xA4, 0xDC, 0xA4, 0xDD, 0xA4, 0xDE, 0xA4, 0xDF, /* 0x7C-0x7F */
+
+ 0xA4, 0xE0, 0xA4, 0xE1, 0xA4, 0xE2, 0xA4, 0xE3, /* 0x80-0x83 */
+ 0xA4, 0xE4, 0xA4, 0xE5, 0xA4, 0xE6, 0xA4, 0xE7, /* 0x84-0x87 */
+ 0xA4, 0xE8, 0xA4, 0xE9, 0xA4, 0xEA, 0xA4, 0xEB, /* 0x88-0x8B */
+ 0xA4, 0xEC, 0xA4, 0xED, 0xA4, 0xEE, 0xA4, 0xEF, /* 0x8C-0x8F */
+ 0xA4, 0xF0, 0xA4, 0xF1, 0xA4, 0xF2, 0xA4, 0xF3, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA9, 0x61, /* 0x98-0x9B */
+ 0xA9, 0x62, 0xA9, 0x66, 0xA9, 0x67, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0xA5, 0xA1, 0xA5, 0xA2, 0xA5, 0xA3, /* 0xA0-0xA3 */
+ 0xA5, 0xA4, 0xA5, 0xA5, 0xA5, 0xA6, 0xA5, 0xA7, /* 0xA4-0xA7 */
+ 0xA5, 0xA8, 0xA5, 0xA9, 0xA5, 0xAA, 0xA5, 0xAB, /* 0xA8-0xAB */
+ 0xA5, 0xAC, 0xA5, 0xAD, 0xA5, 0xAE, 0xA5, 0xAF, /* 0xAC-0xAF */
+ 0xA5, 0xB0, 0xA5, 0xB1, 0xA5, 0xB2, 0xA5, 0xB3, /* 0xB0-0xB3 */
+ 0xA5, 0xB4, 0xA5, 0xB5, 0xA5, 0xB6, 0xA5, 0xB7, /* 0xB4-0xB7 */
+ 0xA5, 0xB8, 0xA5, 0xB9, 0xA5, 0xBA, 0xA5, 0xBB, /* 0xB8-0xBB */
+ 0xA5, 0xBC, 0xA5, 0xBD, 0xA5, 0xBE, 0xA5, 0xBF, /* 0xBC-0xBF */
+ 0xA5, 0xC0, 0xA5, 0xC1, 0xA5, 0xC2, 0xA5, 0xC3, /* 0xC0-0xC3 */
+ 0xA5, 0xC4, 0xA5, 0xC5, 0xA5, 0xC6, 0xA5, 0xC7, /* 0xC4-0xC7 */
+ 0xA5, 0xC8, 0xA5, 0xC9, 0xA5, 0xCA, 0xA5, 0xCB, /* 0xC8-0xCB */
+ 0xA5, 0xCC, 0xA5, 0xCD, 0xA5, 0xCE, 0xA5, 0xCF, /* 0xCC-0xCF */
+ 0xA5, 0xD0, 0xA5, 0xD1, 0xA5, 0xD2, 0xA5, 0xD3, /* 0xD0-0xD3 */
+ 0xA5, 0xD4, 0xA5, 0xD5, 0xA5, 0xD6, 0xA5, 0xD7, /* 0xD4-0xD7 */
+ 0xA5, 0xD8, 0xA5, 0xD9, 0xA5, 0xDA, 0xA5, 0xDB, /* 0xD8-0xDB */
+ 0xA5, 0xDC, 0xA5, 0xDD, 0xA5, 0xDE, 0xA5, 0xDF, /* 0xDC-0xDF */
+ 0xA5, 0xE0, 0xA5, 0xE1, 0xA5, 0xE2, 0xA5, 0xE3, /* 0xE0-0xE3 */
+ 0xA5, 0xE4, 0xA5, 0xE5, 0xA5, 0xE6, 0xA5, 0xE7, /* 0xE4-0xE7 */
+ 0xA5, 0xE8, 0xA5, 0xE9, 0xA5, 0xEA, 0xA5, 0xEB, /* 0xE8-0xEB */
+ 0xA5, 0xEC, 0xA5, 0xED, 0xA5, 0xEE, 0xA5, 0xEF, /* 0xEC-0xEF */
+ 0xA5, 0xF0, 0xA5, 0xF1, 0xA5, 0xF2, 0xA5, 0xF3, /* 0xF0-0xF3 */
+ 0xA5, 0xF4, 0xA5, 0xF5, 0xA5, 0xF6, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0xA9, 0x60, 0xA9, 0x63, 0xA9, 0x64, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_31[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0xA8, 0xC5, 0xA8, 0xC6, 0xA8, 0xC7, /* 0x04-0x07 */
+ 0xA8, 0xC8, 0xA8, 0xC9, 0xA8, 0xCA, 0xA8, 0xCB, /* 0x08-0x0B */
+ 0xA8, 0xCC, 0xA8, 0xCD, 0xA8, 0xCE, 0xA8, 0xCF, /* 0x0C-0x0F */
+ 0xA8, 0xD0, 0xA8, 0xD1, 0xA8, 0xD2, 0xA8, 0xD3, /* 0x10-0x13 */
+ 0xA8, 0xD4, 0xA8, 0xD5, 0xA8, 0xD6, 0xA8, 0xD7, /* 0x14-0x17 */
+ 0xA8, 0xD8, 0xA8, 0xD9, 0xA8, 0xDA, 0xA8, 0xDB, /* 0x18-0x1B */
+ 0xA8, 0xDC, 0xA8, 0xDD, 0xA8, 0xDE, 0xA8, 0xDF, /* 0x1C-0x1F */
+ 0xA8, 0xE0, 0xA8, 0xE1, 0xA8, 0xE2, 0xA8, 0xE3, /* 0x20-0x23 */
+ 0xA8, 0xE4, 0xA8, 0xE5, 0xA8, 0xE6, 0xA8, 0xE7, /* 0x24-0x27 */
+ 0xA8, 0xE8, 0xA8, 0xE9, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0xD2, 0xBB, 0xB6, 0xFE, /* 0x90-0x93 */
+ 0xC8, 0xFD, 0xCB, 0xC4, 0xC9, 0xCF, 0xD6, 0xD0, /* 0x94-0x97 */
+ 0xCF, 0xC2, 0xBC, 0xD7, 0xD2, 0xD2, 0xB1, 0xFB, /* 0x98-0x9B */
+ 0xB6, 0xA1, 0xCC, 0xEC, 0xB5, 0xD8, 0xC8, 0xCB, /* 0x9C-0x9F */
+};
+
+static unsigned char u2c_32[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0xA2, 0xE5, 0xA2, 0xE6, 0xA2, 0xE7, 0xA2, 0xE8, /* 0x20-0x23 */
+ 0xA2, 0xE9, 0xA2, 0xEA, 0xA2, 0xEB, 0xA2, 0xEC, /* 0x24-0x27 */
+ 0xA2, 0xED, 0xA2, 0xEE, 0xD4, 0xC2, 0xBB, 0xF0, /* 0x28-0x2B */
+ 0xCB, 0xAE, 0xC4, 0xBE, 0xBD, 0xF0, 0xCD, 0xC1, /* 0x2C-0x2F */
+ 0xC8, 0xD5, 0xA9, 0x5A, 0xD3, 0xD0, 0xC9, 0xE7, /* 0x30-0x33 */
+ 0xC3, 0xFB, 0xCC, 0xD8, 0xB2, 0xC6, 0xD7, 0xA3, /* 0x34-0x37 */
+ 0xC0, 0xCD, 0xB4, 0xFA, 0xBA, 0xF4, 0xD1, 0xA7, /* 0x38-0x3B */
+ 0xBC, 0xE0, 0xC6, 0xF3, 0xD7, 0xCA, 0xD0, 0xAD, /* 0x3C-0x3F */
+ 0xBC, 0xC0, 0xD0, 0xDD, 0xD7, 0xD4, 0xD6, 0xC1, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0xD2, 0xBB, 0xB6, 0xFE, 0xC8, 0xFD, 0xCB, 0xC4, /* 0x80-0x83 */
+ 0xCE, 0xE5, 0xC1, 0xF9, 0xC6, 0xDF, 0xB0, 0xCB, /* 0x84-0x87 */
+ 0xBE, 0xC5, 0xCA, 0xAE, 0xD4, 0xC2, 0xBB, 0xF0, /* 0x88-0x8B */
+ 0xCB, 0xAE, 0xC4, 0xBE, 0xBD, 0xF0, 0xCD, 0xC1, /* 0x8C-0x8F */
+ 0xC8, 0xD5, 0xD6, 0xEA, 0xD3, 0xD0, 0xC9, 0xE7, /* 0x90-0x93 */
+ 0xC3, 0xFB, 0xCC, 0xD8, 0xB2, 0xC6, 0xD7, 0xA3, /* 0x94-0x97 */
+ 0xC0, 0xCD, 0xC3, 0xD8, 0xC4, 0xD0, 0xC5, 0xAE, /* 0x98-0x9B */
+ 0xCA, 0xCA, 0xD3, 0xC5, 0x00, 0x00, 0xD7, 0xA2, /* 0x9C-0x9F */
+ 0xCF, 0xEE, 0xD0, 0xDD, 0xD0, 0xB4, 0xA9, 0x49, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0xD2, 0xBD, 0xD7, 0xDA, 0xD1, 0xA7, /* 0xA8-0xAB */
+ 0xBC, 0xE0, 0xC6, 0xF3, 0xD7, 0xCA, 0xD0, 0xAD, /* 0xAC-0xAF */
+ 0xD2, 0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+};
+
+static unsigned char u2c_33[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0xA9, 0x4A, 0xA9, 0x4B, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0xA9, 0x4C, 0xA9, 0x4D, 0xA9, 0x4E, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0xA9, 0x4F, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xA9, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0xA9, 0x51, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0xA9, 0x52, 0xA9, 0x53, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0xA9, 0x54, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+};
+
+static unsigned char u2c_4E[512] = {
+ 0xD2, 0xBB, 0xB6, 0xA1, 0x81, 0x40, 0xC6, 0xDF, /* 0x00-0x03 */
+ 0x81, 0x41, 0x81, 0x42, 0x81, 0x43, 0xCD, 0xF2, /* 0x04-0x07 */
+ 0xD5, 0xC9, 0xC8, 0xFD, 0xC9, 0xCF, 0xCF, 0xC2, /* 0x08-0x0B */
+ 0xD8, 0xA2, 0xB2, 0xBB, 0xD3, 0xEB, 0x81, 0x44, /* 0x0C-0x0F */
+ 0xD8, 0xA4, 0xB3, 0xF3, 0x81, 0x45, 0xD7, 0xA8, /* 0x10-0x13 */
+ 0xC7, 0xD2, 0xD8, 0xA7, 0xCA, 0xC0, 0x81, 0x46, /* 0x14-0x17 */
+ 0xC7, 0xF0, 0xB1, 0xFB, 0xD2, 0xB5, 0xB4, 0xD4, /* 0x18-0x1B */
+ 0xB6, 0xAB, 0xCB, 0xBF, 0xD8, 0xA9, 0x81, 0x47, /* 0x1C-0x1F */
+ 0x81, 0x48, 0x81, 0x49, 0xB6, 0xAA, 0x81, 0x4A, /* 0x20-0x23 */
+ 0xC1, 0xBD, 0xD1, 0xCF, 0x81, 0x4B, 0xC9, 0xA5, /* 0x24-0x27 */
+ 0xD8, 0xAD, 0x81, 0x4C, 0xB8, 0xF6, 0xD1, 0xBE, /* 0x28-0x2B */
+ 0xE3, 0xDC, 0xD6, 0xD0, 0x81, 0x4D, 0x81, 0x4E, /* 0x2C-0x2F */
+ 0xB7, 0xE1, 0x81, 0x4F, 0xB4, 0xAE, 0x81, 0x50, /* 0x30-0x33 */
+ 0xC1, 0xD9, 0x81, 0x51, 0xD8, 0xBC, 0x81, 0x52, /* 0x34-0x37 */
+ 0xCD, 0xE8, 0xB5, 0xA4, 0xCE, 0xAA, 0xD6, 0xF7, /* 0x38-0x3B */
+ 0x81, 0x53, 0xC0, 0xF6, 0xBE, 0xD9, 0xD8, 0xAF, /* 0x3C-0x3F */
+ 0x81, 0x54, 0x81, 0x55, 0x81, 0x56, 0xC4, 0xCB, /* 0x40-0x43 */
+ 0x81, 0x57, 0xBE, 0xC3, 0x81, 0x58, 0xD8, 0xB1, /* 0x44-0x47 */
+ 0xC3, 0xB4, 0xD2, 0xE5, 0x81, 0x59, 0xD6, 0xAE, /* 0x48-0x4B */
+ 0xCE, 0xDA, 0xD5, 0xA7, 0xBA, 0xF5, 0xB7, 0xA6, /* 0x4C-0x4F */
+ 0xC0, 0xD6, 0x81, 0x5A, 0xC6, 0xB9, 0xC5, 0xD2, /* 0x50-0x53 */
+ 0xC7, 0xC7, 0x81, 0x5B, 0xB9, 0xD4, 0x81, 0x5C, /* 0x54-0x57 */
+ 0xB3, 0xCB, 0xD2, 0xD2, 0x81, 0x5D, 0x81, 0x5E, /* 0x58-0x5B */
+ 0xD8, 0xBF, 0xBE, 0xC5, 0xC6, 0xF2, 0xD2, 0xB2, /* 0x5C-0x5F */
+ 0xCF, 0xB0, 0xCF, 0xE7, 0x81, 0x5F, 0x81, 0x60, /* 0x60-0x63 */
+ 0x81, 0x61, 0x81, 0x62, 0xCA, 0xE9, 0x81, 0x63, /* 0x64-0x67 */
+ 0x81, 0x64, 0xD8, 0xC0, 0x81, 0x65, 0x81, 0x66, /* 0x68-0x6B */
+ 0x81, 0x67, 0x81, 0x68, 0x81, 0x69, 0x81, 0x6A, /* 0x6C-0x6F */
+ 0xC2, 0xF2, 0xC2, 0xD2, 0x81, 0x6B, 0xC8, 0xE9, /* 0x70-0x73 */
+ 0x81, 0x6C, 0x81, 0x6D, 0x81, 0x6E, 0x81, 0x6F, /* 0x74-0x77 */
+ 0x81, 0x70, 0x81, 0x71, 0x81, 0x72, 0x81, 0x73, /* 0x78-0x7B */
+ 0x81, 0x74, 0x81, 0x75, 0xC7, 0xAC, 0x81, 0x76, /* 0x7C-0x7F */
+
+ 0x81, 0x77, 0x81, 0x78, 0x81, 0x79, 0x81, 0x7A, /* 0x80-0x83 */
+ 0x81, 0x7B, 0x81, 0x7C, 0xC1, 0xCB, 0x81, 0x7D, /* 0x84-0x87 */
+ 0xD3, 0xE8, 0xD5, 0xF9, 0x81, 0x7E, 0xCA, 0xC2, /* 0x88-0x8B */
+ 0xB6, 0xFE, 0xD8, 0xA1, 0xD3, 0xDA, 0xBF, 0xF7, /* 0x8C-0x8F */
+ 0x81, 0x80, 0xD4, 0xC6, 0xBB, 0xA5, 0xD8, 0xC1, /* 0x90-0x93 */
+ 0xCE, 0xE5, 0xBE, 0xAE, 0x81, 0x81, 0x81, 0x82, /* 0x94-0x97 */
+ 0xD8, 0xA8, 0x81, 0x83, 0xD1, 0xC7, 0xD0, 0xA9, /* 0x98-0x9B */
+ 0x81, 0x84, 0x81, 0x85, 0x81, 0x86, 0xD8, 0xBD, /* 0x9C-0x9F */
+ 0xD9, 0xEF, 0xCD, 0xF6, 0xBF, 0xBA, 0x81, 0x87, /* 0xA0-0xA3 */
+ 0xBD, 0xBB, 0xBA, 0xA5, 0xD2, 0xE0, 0xB2, 0xFA, /* 0xA4-0xA7 */
+ 0xBA, 0xE0, 0xC4, 0xB6, 0x81, 0x88, 0xCF, 0xED, /* 0xA8-0xAB */
+ 0xBE, 0xA9, 0xCD, 0xA4, 0xC1, 0xC1, 0x81, 0x89, /* 0xAC-0xAF */
+ 0x81, 0x8A, 0x81, 0x8B, 0xC7, 0xD7, 0xD9, 0xF1, /* 0xB0-0xB3 */
+ 0x81, 0x8C, 0xD9, 0xF4, 0x81, 0x8D, 0x81, 0x8E, /* 0xB4-0xB7 */
+ 0x81, 0x8F, 0x81, 0x90, 0xC8, 0xCB, 0xD8, 0xE9, /* 0xB8-0xBB */
+ 0x81, 0x91, 0x81, 0x92, 0x81, 0x93, 0xD2, 0xDA, /* 0xBC-0xBF */
+ 0xCA, 0xB2, 0xC8, 0xCA, 0xD8, 0xEC, 0xD8, 0xEA, /* 0xC0-0xC3 */
+ 0xD8, 0xC6, 0xBD, 0xF6, 0xC6, 0xCD, 0xB3, 0xF0, /* 0xC4-0xC7 */
+ 0x81, 0x94, 0xD8, 0xEB, 0xBD, 0xF1, 0xBD, 0xE9, /* 0xC8-0xCB */
+ 0x81, 0x95, 0xC8, 0xD4, 0xB4, 0xD3, 0x81, 0x96, /* 0xCC-0xCF */
+ 0x81, 0x97, 0xC2, 0xD8, 0x81, 0x98, 0xB2, 0xD6, /* 0xD0-0xD3 */
+ 0xD7, 0xD0, 0xCA, 0xCB, 0xCB, 0xFB, 0xD5, 0xCC, /* 0xD4-0xD7 */
+ 0xB8, 0xB6, 0xCF, 0xC9, 0x81, 0x99, 0x81, 0x9A, /* 0xD8-0xDB */
+ 0x81, 0x9B, 0xD9, 0xDA, 0xD8, 0xF0, 0xC7, 0xAA, /* 0xDC-0xDF */
+ 0x81, 0x9C, 0xD8, 0xEE, 0x81, 0x9D, 0xB4, 0xFA, /* 0xE0-0xE3 */
+ 0xC1, 0xEE, 0xD2, 0xD4, 0x81, 0x9E, 0x81, 0x9F, /* 0xE4-0xE7 */
+ 0xD8, 0xED, 0x81, 0xA0, 0xD2, 0xC7, 0xD8, 0xEF, /* 0xE8-0xEB */
+ 0xC3, 0xC7, 0x81, 0xA1, 0x81, 0xA2, 0x81, 0xA3, /* 0xEC-0xEF */
+ 0xD1, 0xF6, 0x81, 0xA4, 0xD6, 0xD9, 0xD8, 0xF2, /* 0xF0-0xF3 */
+ 0x81, 0xA5, 0xD8, 0xF5, 0xBC, 0xFE, 0xBC, 0xDB, /* 0xF4-0xF7 */
+ 0x81, 0xA6, 0x81, 0xA7, 0x81, 0xA8, 0xC8, 0xCE, /* 0xF8-0xFB */
+ 0x81, 0xA9, 0xB7, 0xDD, 0x81, 0xAA, 0xB7, 0xC2, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_4F[512] = {
+ 0x81, 0xAB, 0xC6, 0xF3, 0x81, 0xAC, 0x81, 0xAD, /* 0x00-0x03 */
+ 0x81, 0xAE, 0x81, 0xAF, 0x81, 0xB0, 0x81, 0xB1, /* 0x04-0x07 */
+ 0x81, 0xB2, 0xD8, 0xF8, 0xD2, 0xC1, 0x81, 0xB3, /* 0x08-0x0B */
+ 0x81, 0xB4, 0xCE, 0xE9, 0xBC, 0xBF, 0xB7, 0xFC, /* 0x0C-0x0F */
+ 0xB7, 0xA5, 0xD0, 0xDD, 0x81, 0xB5, 0x81, 0xB6, /* 0x10-0x13 */
+ 0x81, 0xB7, 0x81, 0xB8, 0x81, 0xB9, 0xD6, 0xDA, /* 0x14-0x17 */
+ 0xD3, 0xC5, 0xBB, 0xEF, 0xBB, 0xE1, 0xD8, 0xF1, /* 0x18-0x1B */
+ 0x81, 0xBA, 0x81, 0xBB, 0xC9, 0xA1, 0xCE, 0xB0, /* 0x1C-0x1F */
+ 0xB4, 0xAB, 0x81, 0xBC, 0xD8, 0xF3, 0x81, 0xBD, /* 0x20-0x23 */
+ 0xC9, 0xCB, 0xD8, 0xF6, 0xC2, 0xD7, 0xD8, 0xF7, /* 0x24-0x27 */
+ 0x81, 0xBE, 0x81, 0xBF, 0xCE, 0xB1, 0xD8, 0xF9, /* 0x28-0x2B */
+ 0x81, 0xC0, 0x81, 0xC1, 0x81, 0xC2, 0xB2, 0xAE, /* 0x2C-0x2F */
+ 0xB9, 0xC0, 0x81, 0xC3, 0xD9, 0xA3, 0x81, 0xC4, /* 0x30-0x33 */
+ 0xB0, 0xE9, 0x81, 0xC5, 0xC1, 0xE6, 0x81, 0xC6, /* 0x34-0x37 */
+ 0xC9, 0xEC, 0x81, 0xC7, 0xCB, 0xC5, 0x81, 0xC8, /* 0x38-0x3B */
+ 0xCB, 0xC6, 0xD9, 0xA4, 0x81, 0xC9, 0x81, 0xCA, /* 0x3C-0x3F */
+ 0x81, 0xCB, 0x81, 0xCC, 0x81, 0xCD, 0xB5, 0xE8, /* 0x40-0x43 */
+ 0x81, 0xCE, 0x81, 0xCF, 0xB5, 0xAB, 0x81, 0xD0, /* 0x44-0x47 */
+ 0x81, 0xD1, 0x81, 0xD2, 0x81, 0xD3, 0x81, 0xD4, /* 0x48-0x4B */
+ 0x81, 0xD5, 0xCE, 0xBB, 0xB5, 0xCD, 0xD7, 0xA1, /* 0x4C-0x4F */
+ 0xD7, 0xF4, 0xD3, 0xD3, 0x81, 0xD6, 0xCC, 0xE5, /* 0x50-0x53 */
+ 0x81, 0xD7, 0xBA, 0xCE, 0x81, 0xD8, 0xD9, 0xA2, /* 0x54-0x57 */
+ 0xD9, 0xDC, 0xD3, 0xE0, 0xD8, 0xFD, 0xB7, 0xF0, /* 0x58-0x5B */
+ 0xD7, 0xF7, 0xD8, 0xFE, 0xD8, 0xFA, 0xD9, 0xA1, /* 0x5C-0x5F */
+ 0xC4, 0xE3, 0x81, 0xD9, 0x81, 0xDA, 0xD3, 0xB6, /* 0x60-0x63 */
+ 0xD8, 0xF4, 0xD9, 0xDD, 0x81, 0xDB, 0xD8, 0xFB, /* 0x64-0x67 */
+ 0x81, 0xDC, 0xC5, 0xE5, 0x81, 0xDD, 0x81, 0xDE, /* 0x68-0x6B */
+ 0xC0, 0xD0, 0x81, 0xDF, 0x81, 0xE0, 0xD1, 0xF0, /* 0x6C-0x6F */
+ 0xB0, 0xDB, 0x81, 0xE1, 0x81, 0xE2, 0xBC, 0xD1, /* 0x70-0x73 */
+ 0xD9, 0xA6, 0x81, 0xE3, 0xD9, 0xA5, 0x81, 0xE4, /* 0x74-0x77 */
+ 0x81, 0xE5, 0x81, 0xE6, 0x81, 0xE7, 0xD9, 0xAC, /* 0x78-0x7B */
+ 0xD9, 0xAE, 0x81, 0xE8, 0xD9, 0xAB, 0xCA, 0xB9, /* 0x7C-0x7F */
+
+ 0x81, 0xE9, 0x81, 0xEA, 0x81, 0xEB, 0xD9, 0xA9, /* 0x80-0x83 */
+ 0xD6, 0xB6, 0x81, 0xEC, 0x81, 0xED, 0x81, 0xEE, /* 0x84-0x87 */
+ 0xB3, 0xDE, 0xD9, 0xA8, 0x81, 0xEF, 0xC0, 0xFD, /* 0x88-0x8B */
+ 0x81, 0xF0, 0xCA, 0xCC, 0x81, 0xF1, 0xD9, 0xAA, /* 0x8C-0x8F */
+ 0x81, 0xF2, 0xD9, 0xA7, 0x81, 0xF3, 0x81, 0xF4, /* 0x90-0x93 */
+ 0xD9, 0xB0, 0x81, 0xF5, 0x81, 0xF6, 0xB6, 0xB1, /* 0x94-0x97 */
+ 0x81, 0xF7, 0x81, 0xF8, 0x81, 0xF9, 0xB9, 0xA9, /* 0x98-0x9B */
+ 0x81, 0xFA, 0xD2, 0xC0, 0x81, 0xFB, 0x81, 0xFC, /* 0x9C-0x9F */
+ 0xCF, 0xC0, 0x81, 0xFD, 0x81, 0xFE, 0xC2, 0xC2, /* 0xA0-0xA3 */
+ 0x82, 0x40, 0xBD, 0xC4, 0xD5, 0xEC, 0xB2, 0xE0, /* 0xA4-0xA7 */
+ 0xC7, 0xC8, 0xBF, 0xEB, 0xD9, 0xAD, 0x82, 0x41, /* 0xA8-0xAB */
+ 0xD9, 0xAF, 0x82, 0x42, 0xCE, 0xEA, 0xBA, 0xEE, /* 0xAC-0xAF */
+ 0x82, 0x43, 0x82, 0x44, 0x82, 0x45, 0x82, 0x46, /* 0xB0-0xB3 */
+ 0x82, 0x47, 0xC7, 0xD6, 0x82, 0x48, 0x82, 0x49, /* 0xB4-0xB7 */
+ 0x82, 0x4A, 0x82, 0x4B, 0x82, 0x4C, 0x82, 0x4D, /* 0xB8-0xBB */
+ 0x82, 0x4E, 0x82, 0x4F, 0x82, 0x50, 0xB1, 0xE3, /* 0xBC-0xBF */
+ 0x82, 0x51, 0x82, 0x52, 0x82, 0x53, 0xB4, 0xD9, /* 0xC0-0xC3 */
+ 0xB6, 0xED, 0xD9, 0xB4, 0x82, 0x54, 0x82, 0x55, /* 0xC4-0xC7 */
+ 0x82, 0x56, 0x82, 0x57, 0xBF, 0xA1, 0x82, 0x58, /* 0xC8-0xCB */
+ 0x82, 0x59, 0x82, 0x5A, 0xD9, 0xDE, 0xC7, 0xCE, /* 0xCC-0xCF */
+ 0xC0, 0xFE, 0xD9, 0xB8, 0x82, 0x5B, 0x82, 0x5C, /* 0xD0-0xD3 */
+ 0x82, 0x5D, 0x82, 0x5E, 0x82, 0x5F, 0xCB, 0xD7, /* 0xD4-0xD7 */
+ 0xB7, 0xFD, 0x82, 0x60, 0xD9, 0xB5, 0x82, 0x61, /* 0xD8-0xDB */
+ 0xD9, 0xB7, 0xB1, 0xA3, 0xD3, 0xE1, 0xD9, 0xB9, /* 0xDC-0xDF */
+ 0x82, 0x62, 0xD0, 0xC5, 0x82, 0x63, 0xD9, 0xB6, /* 0xE0-0xE3 */
+ 0x82, 0x64, 0x82, 0x65, 0xD9, 0xB1, 0x82, 0x66, /* 0xE4-0xE7 */
+ 0xD9, 0xB2, 0xC1, 0xA9, 0xD9, 0xB3, 0x82, 0x67, /* 0xE8-0xEB */
+ 0x82, 0x68, 0xBC, 0xF3, 0xD0, 0xDE, 0xB8, 0xA9, /* 0xEC-0xEF */
+ 0x82, 0x69, 0xBE, 0xE3, 0x82, 0x6A, 0xD9, 0xBD, /* 0xF0-0xF3 */
+ 0x82, 0x6B, 0x82, 0x6C, 0x82, 0x6D, 0x82, 0x6E, /* 0xF4-0xF7 */
+ 0xD9, 0xBA, 0x82, 0x6F, 0xB0, 0xB3, 0x82, 0x70, /* 0xF8-0xFB */
+ 0x82, 0x71, 0x82, 0x72, 0xD9, 0xC2, 0x82, 0x73, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_50[512] = {
+ 0x82, 0x74, 0x82, 0x75, 0x82, 0x76, 0x82, 0x77, /* 0x00-0x03 */
+ 0x82, 0x78, 0x82, 0x79, 0x82, 0x7A, 0x82, 0x7B, /* 0x04-0x07 */
+ 0x82, 0x7C, 0x82, 0x7D, 0x82, 0x7E, 0x82, 0x80, /* 0x08-0x0B */
+ 0xD9, 0xC4, 0xB1, 0xB6, 0x82, 0x81, 0xD9, 0xBF, /* 0x0C-0x0F */
+ 0x82, 0x82, 0x82, 0x83, 0xB5, 0xB9, 0x82, 0x84, /* 0x10-0x13 */
+ 0xBE, 0xF3, 0x82, 0x85, 0x82, 0x86, 0x82, 0x87, /* 0x14-0x17 */
+ 0xCC, 0xC8, 0xBA, 0xF2, 0xD2, 0xD0, 0x82, 0x88, /* 0x18-0x1B */
+ 0xD9, 0xC3, 0x82, 0x89, 0x82, 0x8A, 0xBD, 0xE8, /* 0x1C-0x1F */
+ 0x82, 0x8B, 0xB3, 0xAB, 0x82, 0x8C, 0x82, 0x8D, /* 0x20-0x23 */
+ 0x82, 0x8E, 0xD9, 0xC5, 0xBE, 0xEB, 0x82, 0x8F, /* 0x24-0x27 */
+ 0xD9, 0xC6, 0xD9, 0xBB, 0xC4, 0xDF, 0x82, 0x90, /* 0x28-0x2B */
+ 0xD9, 0xBE, 0xD9, 0xC1, 0xD9, 0xC0, 0x82, 0x91, /* 0x2C-0x2F */
+ 0x82, 0x92, 0x82, 0x93, 0x82, 0x94, 0x82, 0x95, /* 0x30-0x33 */
+ 0x82, 0x96, 0x82, 0x97, 0x82, 0x98, 0x82, 0x99, /* 0x34-0x37 */
+ 0x82, 0x9A, 0x82, 0x9B, 0xD5, 0xAE, 0x82, 0x9C, /* 0x38-0x3B */
+ 0xD6, 0xB5, 0x82, 0x9D, 0xC7, 0xE3, 0x82, 0x9E, /* 0x3C-0x3F */
+ 0x82, 0x9F, 0x82, 0xA0, 0x82, 0xA1, 0xD9, 0xC8, /* 0x40-0x43 */
+ 0x82, 0xA2, 0x82, 0xA3, 0x82, 0xA4, 0xBC, 0xD9, /* 0x44-0x47 */
+ 0xD9, 0xCA, 0x82, 0xA5, 0x82, 0xA6, 0x82, 0xA7, /* 0x48-0x4B */
+ 0xD9, 0xBC, 0x82, 0xA8, 0xD9, 0xCB, 0xC6, 0xAB, /* 0x4C-0x4F */
+ 0x82, 0xA9, 0x82, 0xAA, 0x82, 0xAB, 0x82, 0xAC, /* 0x50-0x53 */
+ 0x82, 0xAD, 0xD9, 0xC9, 0x82, 0xAE, 0x82, 0xAF, /* 0x54-0x57 */
+ 0x82, 0xB0, 0x82, 0xB1, 0xD7, 0xF6, 0x82, 0xB2, /* 0x58-0x5B */
+ 0xCD, 0xA3, 0x82, 0xB3, 0x82, 0xB4, 0x82, 0xB5, /* 0x5C-0x5F */
+ 0x82, 0xB6, 0x82, 0xB7, 0x82, 0xB8, 0x82, 0xB9, /* 0x60-0x63 */
+ 0x82, 0xBA, 0xBD, 0xA1, 0x82, 0xBB, 0x82, 0xBC, /* 0x64-0x67 */
+ 0x82, 0xBD, 0x82, 0xBE, 0x82, 0xBF, 0x82, 0xC0, /* 0x68-0x6B */
+ 0xD9, 0xCC, 0x82, 0xC1, 0x82, 0xC2, 0x82, 0xC3, /* 0x6C-0x6F */
+ 0x82, 0xC4, 0x82, 0xC5, 0x82, 0xC6, 0x82, 0xC7, /* 0x70-0x73 */
+ 0x82, 0xC8, 0x82, 0xC9, 0xC5, 0xBC, 0xCD, 0xB5, /* 0x74-0x77 */
+ 0x82, 0xCA, 0x82, 0xCB, 0x82, 0xCC, 0xD9, 0xCD, /* 0x78-0x7B */
+ 0x82, 0xCD, 0x82, 0xCE, 0xD9, 0xC7, 0xB3, 0xA5, /* 0x7C-0x7F */
+
+ 0xBF, 0xFE, 0x82, 0xCF, 0x82, 0xD0, 0x82, 0xD1, /* 0x80-0x83 */
+ 0x82, 0xD2, 0xB8, 0xB5, 0x82, 0xD3, 0x82, 0xD4, /* 0x84-0x87 */
+ 0xC0, 0xFC, 0x82, 0xD5, 0x82, 0xD6, 0x82, 0xD7, /* 0x88-0x8B */
+ 0x82, 0xD8, 0xB0, 0xF8, 0x82, 0xD9, 0x82, 0xDA, /* 0x8C-0x8F */
+ 0x82, 0xDB, 0x82, 0xDC, 0x82, 0xDD, 0x82, 0xDE, /* 0x90-0x93 */
+ 0x82, 0xDF, 0x82, 0xE0, 0x82, 0xE1, 0x82, 0xE2, /* 0x94-0x97 */
+ 0x82, 0xE3, 0x82, 0xE4, 0x82, 0xE5, 0x82, 0xE6, /* 0x98-0x9B */
+ 0x82, 0xE7, 0x82, 0xE8, 0x82, 0xE9, 0x82, 0xEA, /* 0x9C-0x9F */
+ 0x82, 0xEB, 0x82, 0xEC, 0x82, 0xED, 0xB4, 0xF6, /* 0xA0-0xA3 */
+ 0x82, 0xEE, 0xD9, 0xCE, 0x82, 0xEF, 0xD9, 0xCF, /* 0xA4-0xA7 */
+ 0xB4, 0xA2, 0xD9, 0xD0, 0x82, 0xF0, 0x82, 0xF1, /* 0xA8-0xAB */
+ 0xB4, 0xDF, 0x82, 0xF2, 0x82, 0xF3, 0x82, 0xF4, /* 0xAC-0xAF */
+ 0x82, 0xF5, 0x82, 0xF6, 0xB0, 0xC1, 0x82, 0xF7, /* 0xB0-0xB3 */
+ 0x82, 0xF8, 0x82, 0xF9, 0x82, 0xFA, 0x82, 0xFB, /* 0xB4-0xB7 */
+ 0x82, 0xFC, 0x82, 0xFD, 0xD9, 0xD1, 0xC9, 0xB5, /* 0xB8-0xBB */
+ 0x82, 0xFE, 0x83, 0x40, 0x83, 0x41, 0x83, 0x42, /* 0xBC-0xBF */
+ 0x83, 0x43, 0x83, 0x44, 0x83, 0x45, 0x83, 0x46, /* 0xC0-0xC3 */
+ 0x83, 0x47, 0x83, 0x48, 0x83, 0x49, 0x83, 0x4A, /* 0xC4-0xC7 */
+ 0x83, 0x4B, 0x83, 0x4C, 0x83, 0x4D, 0x83, 0x4E, /* 0xC8-0xCB */
+ 0x83, 0x4F, 0x83, 0x50, 0x83, 0x51, 0xCF, 0xF1, /* 0xCC-0xCF */
+ 0x83, 0x52, 0x83, 0x53, 0x83, 0x54, 0x83, 0x55, /* 0xD0-0xD3 */
+ 0x83, 0x56, 0x83, 0x57, 0xD9, 0xD2, 0x83, 0x58, /* 0xD4-0xD7 */
+ 0x83, 0x59, 0x83, 0x5A, 0xC1, 0xC5, 0x83, 0x5B, /* 0xD8-0xDB */
+ 0x83, 0x5C, 0x83, 0x5D, 0x83, 0x5E, 0x83, 0x5F, /* 0xDC-0xDF */
+ 0x83, 0x60, 0x83, 0x61, 0x83, 0x62, 0x83, 0x63, /* 0xE0-0xE3 */
+ 0x83, 0x64, 0x83, 0x65, 0xD9, 0xD6, 0xC9, 0xAE, /* 0xE4-0xE7 */
+ 0x83, 0x66, 0x83, 0x67, 0x83, 0x68, 0x83, 0x69, /* 0xE8-0xEB */
+ 0xD9, 0xD5, 0xD9, 0xD4, 0xD9, 0xD7, 0x83, 0x6A, /* 0xEC-0xEF */
+ 0x83, 0x6B, 0x83, 0x6C, 0x83, 0x6D, 0xCB, 0xDB, /* 0xF0-0xF3 */
+ 0x83, 0x6E, 0xBD, 0xA9, 0x83, 0x6F, 0x83, 0x70, /* 0xF4-0xF7 */
+ 0x83, 0x71, 0x83, 0x72, 0x83, 0x73, 0xC6, 0xA7, /* 0xF8-0xFB */
+ 0x83, 0x74, 0x83, 0x75, 0x83, 0x76, 0x83, 0x77, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_51[512] = {
+ 0x83, 0x78, 0x83, 0x79, 0x83, 0x7A, 0x83, 0x7B, /* 0x00-0x03 */
+ 0x83, 0x7C, 0x83, 0x7D, 0xD9, 0xD3, 0xD9, 0xD8, /* 0x04-0x07 */
+ 0x83, 0x7E, 0x83, 0x80, 0x83, 0x81, 0xD9, 0xD9, /* 0x08-0x0B */
+ 0x83, 0x82, 0x83, 0x83, 0x83, 0x84, 0x83, 0x85, /* 0x0C-0x0F */
+ 0x83, 0x86, 0x83, 0x87, 0xC8, 0xE5, 0x83, 0x88, /* 0x10-0x13 */
+ 0x83, 0x89, 0x83, 0x8A, 0x83, 0x8B, 0x83, 0x8C, /* 0x14-0x17 */
+ 0x83, 0x8D, 0x83, 0x8E, 0x83, 0x8F, 0x83, 0x90, /* 0x18-0x1B */
+ 0x83, 0x91, 0x83, 0x92, 0x83, 0x93, 0x83, 0x94, /* 0x1C-0x1F */
+ 0x83, 0x95, 0xC0, 0xDC, 0x83, 0x96, 0x83, 0x97, /* 0x20-0x23 */
+ 0x83, 0x98, 0x83, 0x99, 0x83, 0x9A, 0x83, 0x9B, /* 0x24-0x27 */
+ 0x83, 0x9C, 0x83, 0x9D, 0x83, 0x9E, 0x83, 0x9F, /* 0x28-0x2B */
+ 0x83, 0xA0, 0x83, 0xA1, 0x83, 0xA2, 0x83, 0xA3, /* 0x2C-0x2F */
+ 0x83, 0xA4, 0x83, 0xA5, 0x83, 0xA6, 0x83, 0xA7, /* 0x30-0x33 */
+ 0x83, 0xA8, 0x83, 0xA9, 0x83, 0xAA, 0x83, 0xAB, /* 0x34-0x37 */
+ 0x83, 0xAC, 0x83, 0xAD, 0x83, 0xAE, 0x83, 0xAF, /* 0x38-0x3B */
+ 0x83, 0xB0, 0x83, 0xB1, 0x83, 0xB2, 0xB6, 0xF9, /* 0x3C-0x3F */
+ 0xD8, 0xA3, 0xD4, 0xCA, 0x83, 0xB3, 0xD4, 0xAA, /* 0x40-0x43 */
+ 0xD0, 0xD6, 0xB3, 0xE4, 0xD5, 0xD7, 0x83, 0xB4, /* 0x44-0x47 */
+ 0xCF, 0xC8, 0xB9, 0xE2, 0x83, 0xB5, 0xBF, 0xCB, /* 0x48-0x4B */
+ 0x83, 0xB6, 0xC3, 0xE2, 0x83, 0xB7, 0x83, 0xB8, /* 0x4C-0x4F */
+ 0x83, 0xB9, 0xB6, 0xD2, 0x83, 0xBA, 0x83, 0xBB, /* 0x50-0x53 */
+ 0xCD, 0xC3, 0xD9, 0xEE, 0xD9, 0xF0, 0x83, 0xBC, /* 0x54-0x57 */
+ 0x83, 0xBD, 0x83, 0xBE, 0xB5, 0xB3, 0x83, 0xBF, /* 0x58-0x5B */
+ 0xB6, 0xB5, 0x83, 0xC0, 0x83, 0xC1, 0x83, 0xC2, /* 0x5C-0x5F */
+ 0x83, 0xC3, 0x83, 0xC4, 0xBE, 0xA4, 0x83, 0xC5, /* 0x60-0x63 */
+ 0x83, 0xC6, 0xC8, 0xEB, 0x83, 0xC7, 0x83, 0xC8, /* 0x64-0x67 */
+ 0xC8, 0xAB, 0x83, 0xC9, 0x83, 0xCA, 0xB0, 0xCB, /* 0x68-0x6B */
+ 0xB9, 0xAB, 0xC1, 0xF9, 0xD9, 0xE2, 0x83, 0xCB, /* 0x6C-0x6F */
+ 0xC0, 0xBC, 0xB9, 0xB2, 0x83, 0xCC, 0xB9, 0xD8, /* 0x70-0x73 */
+ 0xD0, 0xCB, 0xB1, 0xF8, 0xC6, 0xE4, 0xBE, 0xDF, /* 0x74-0x77 */
+ 0xB5, 0xE4, 0xD7, 0xC8, 0x83, 0xCD, 0xD1, 0xF8, /* 0x78-0x7B */
+ 0xBC, 0xE6, 0xCA, 0xDE, 0x83, 0xCE, 0x83, 0xCF, /* 0x7C-0x7F */
+
+ 0xBC, 0xBD, 0xD9, 0xE6, 0xD8, 0xE7, 0x83, 0xD0, /* 0x80-0x83 */
+ 0x83, 0xD1, 0xC4, 0xDA, 0x83, 0xD2, 0x83, 0xD3, /* 0x84-0x87 */
+ 0xB8, 0xD4, 0xC8, 0xBD, 0x83, 0xD4, 0x83, 0xD5, /* 0x88-0x8B */
+ 0xB2, 0xE1, 0xD4, 0xD9, 0x83, 0xD6, 0x83, 0xD7, /* 0x8C-0x8F */
+ 0x83, 0xD8, 0x83, 0xD9, 0xC3, 0xB0, 0x83, 0xDA, /* 0x90-0x93 */
+ 0x83, 0xDB, 0xC3, 0xE1, 0xDA, 0xA2, 0xC8, 0xDF, /* 0x94-0x97 */
+ 0x83, 0xDC, 0xD0, 0xB4, 0x83, 0xDD, 0xBE, 0xFC, /* 0x98-0x9B */
+ 0xC5, 0xA9, 0x83, 0xDE, 0x83, 0xDF, 0x83, 0xE0, /* 0x9C-0x9F */
+ 0xB9, 0xDA, 0x83, 0xE1, 0xDA, 0xA3, 0x83, 0xE2, /* 0xA0-0xA3 */
+ 0xD4, 0xA9, 0xDA, 0xA4, 0x83, 0xE3, 0x83, 0xE4, /* 0xA4-0xA7 */
+ 0x83, 0xE5, 0x83, 0xE6, 0x83, 0xE7, 0xD9, 0xFB, /* 0xA8-0xAB */
+ 0xB6, 0xAC, 0x83, 0xE8, 0x83, 0xE9, 0xB7, 0xEB, /* 0xAC-0xAF */
+ 0xB1, 0xF9, 0xD9, 0xFC, 0xB3, 0xE5, 0xBE, 0xF6, /* 0xB0-0xB3 */
+ 0x83, 0xEA, 0xBF, 0xF6, 0xD2, 0xB1, 0xC0, 0xE4, /* 0xB4-0xB7 */
+ 0x83, 0xEB, 0x83, 0xEC, 0x83, 0xED, 0xB6, 0xB3, /* 0xB8-0xBB */
+ 0xD9, 0xFE, 0xD9, 0xFD, 0x83, 0xEE, 0x83, 0xEF, /* 0xBC-0xBF */
+ 0xBE, 0xBB, 0x83, 0xF0, 0x83, 0xF1, 0x83, 0xF2, /* 0xC0-0xC3 */
+ 0xC6, 0xE0, 0x83, 0xF3, 0xD7, 0xBC, 0xDA, 0xA1, /* 0xC4-0xC7 */
+ 0x83, 0xF4, 0xC1, 0xB9, 0x83, 0xF5, 0xB5, 0xF2, /* 0xC8-0xCB */
+ 0xC1, 0xE8, 0x83, 0xF6, 0x83, 0xF7, 0xBC, 0xF5, /* 0xCC-0xCF */
+ 0x83, 0xF8, 0xB4, 0xD5, 0x83, 0xF9, 0x83, 0xFA, /* 0xD0-0xD3 */
+ 0x83, 0xFB, 0x83, 0xFC, 0x83, 0xFD, 0x83, 0xFE, /* 0xD4-0xD7 */
+ 0x84, 0x40, 0x84, 0x41, 0x84, 0x42, 0xC1, 0xDD, /* 0xD8-0xDB */
+ 0x84, 0x43, 0xC4, 0xFD, 0x84, 0x44, 0x84, 0x45, /* 0xDC-0xDF */
+ 0xBC, 0xB8, 0xB7, 0xB2, 0x84, 0x46, 0x84, 0x47, /* 0xE0-0xE3 */
+ 0xB7, 0xEF, 0x84, 0x48, 0x84, 0x49, 0x84, 0x4A, /* 0xE4-0xE7 */
+ 0x84, 0x4B, 0x84, 0x4C, 0x84, 0x4D, 0xD9, 0xEC, /* 0xE8-0xEB */
+ 0x84, 0x4E, 0xC6, 0xBE, 0x84, 0x4F, 0xBF, 0xAD, /* 0xEC-0xEF */
+ 0xBB, 0xCB, 0x84, 0x50, 0x84, 0x51, 0xB5, 0xCA, /* 0xF0-0xF3 */
+ 0x84, 0x52, 0xDB, 0xC9, 0xD0, 0xD7, 0x84, 0x53, /* 0xF4-0xF7 */
+ 0xCD, 0xB9, 0xB0, 0xBC, 0xB3, 0xF6, 0xBB, 0xF7, /* 0xF8-0xFB */
+ 0xDB, 0xCA, 0xBA, 0xAF, 0x84, 0x54, 0xD4, 0xE4, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_52[512] = {
+ 0xB5, 0xB6, 0xB5, 0xF3, 0xD8, 0xD6, 0xC8, 0xD0, /* 0x00-0x03 */
+ 0x84, 0x55, 0x84, 0x56, 0xB7, 0xD6, 0xC7, 0xD0, /* 0x04-0x07 */
+ 0xD8, 0xD7, 0x84, 0x57, 0xBF, 0xAF, 0x84, 0x58, /* 0x08-0x0B */
+ 0x84, 0x59, 0xDB, 0xBB, 0xD8, 0xD8, 0x84, 0x5A, /* 0x0C-0x0F */
+ 0x84, 0x5B, 0xD0, 0xCC, 0xBB, 0xAE, 0x84, 0x5C, /* 0x10-0x13 */
+ 0x84, 0x5D, 0x84, 0x5E, 0xEB, 0xBE, 0xC1, 0xD0, /* 0x14-0x17 */
+ 0xC1, 0xF5, 0xD4, 0xF2, 0xB8, 0xD5, 0xB4, 0xB4, /* 0x18-0x1B */
+ 0x84, 0x5F, 0xB3, 0xF5, 0x84, 0x60, 0x84, 0x61, /* 0x1C-0x1F */
+ 0xC9, 0xBE, 0x84, 0x62, 0x84, 0x63, 0x84, 0x64, /* 0x20-0x23 */
+ 0xC5, 0xD0, 0x84, 0x65, 0x84, 0x66, 0x84, 0x67, /* 0x24-0x27 */
+ 0xC5, 0xD9, 0xC0, 0xFB, 0x84, 0x68, 0xB1, 0xF0, /* 0x28-0x2B */
+ 0x84, 0x69, 0xD8, 0xD9, 0xB9, 0xCE, 0x84, 0x6A, /* 0x2C-0x2F */
+ 0xB5, 0xBD, 0x84, 0x6B, 0x84, 0x6C, 0xD8, 0xDA, /* 0x30-0x33 */
+ 0x84, 0x6D, 0x84, 0x6E, 0xD6, 0xC6, 0xCB, 0xA2, /* 0x34-0x37 */
+ 0xC8, 0xAF, 0xC9, 0xB2, 0xB4, 0xCC, 0xBF, 0xCC, /* 0x38-0x3B */
+ 0x84, 0x6F, 0xB9, 0xF4, 0x84, 0x70, 0xD8, 0xDB, /* 0x3C-0x3F */
+ 0xD8, 0xDC, 0xB6, 0xE7, 0xBC, 0xC1, 0xCC, 0xEA, /* 0x40-0x43 */
+ 0x84, 0x71, 0x84, 0x72, 0x84, 0x73, 0x84, 0x74, /* 0x44-0x47 */
+ 0x84, 0x75, 0x84, 0x76, 0xCF, 0xF7, 0x84, 0x77, /* 0x48-0x4B */
+ 0xD8, 0xDD, 0xC7, 0xB0, 0x84, 0x78, 0x84, 0x79, /* 0x4C-0x4F */
+ 0xB9, 0xD0, 0xBD, 0xA3, 0x84, 0x7A, 0x84, 0x7B, /* 0x50-0x53 */
+ 0xCC, 0xDE, 0x84, 0x7C, 0xC6, 0xCA, 0x84, 0x7D, /* 0x54-0x57 */
+ 0x84, 0x7E, 0x84, 0x80, 0x84, 0x81, 0x84, 0x82, /* 0x58-0x5B */
+ 0xD8, 0xE0, 0x84, 0x83, 0xD8, 0xDE, 0x84, 0x84, /* 0x5C-0x5F */
+ 0x84, 0x85, 0xD8, 0xDF, 0x84, 0x86, 0x84, 0x87, /* 0x60-0x63 */
+ 0x84, 0x88, 0xB0, 0xFE, 0x84, 0x89, 0xBE, 0xE7, /* 0x64-0x67 */
+ 0x84, 0x8A, 0xCA, 0xA3, 0xBC, 0xF4, 0x84, 0x8B, /* 0x68-0x6B */
+ 0x84, 0x8C, 0x84, 0x8D, 0x84, 0x8E, 0xB8, 0xB1, /* 0x6C-0x6F */
+ 0x84, 0x8F, 0x84, 0x90, 0xB8, 0xEE, 0x84, 0x91, /* 0x70-0x73 */
+ 0x84, 0x92, 0x84, 0x93, 0x84, 0x94, 0x84, 0x95, /* 0x74-0x77 */
+ 0x84, 0x96, 0x84, 0x97, 0x84, 0x98, 0x84, 0x99, /* 0x78-0x7B */
+ 0x84, 0x9A, 0xD8, 0xE2, 0x84, 0x9B, 0xBD, 0xCB, /* 0x7C-0x7F */
+
+ 0x84, 0x9C, 0xD8, 0xE4, 0xD8, 0xE3, 0x84, 0x9D, /* 0x80-0x83 */
+ 0x84, 0x9E, 0x84, 0x9F, 0x84, 0xA0, 0x84, 0xA1, /* 0x84-0x87 */
+ 0xC5, 0xFC, 0x84, 0xA2, 0x84, 0xA3, 0x84, 0xA4, /* 0x88-0x8B */
+ 0x84, 0xA5, 0x84, 0xA6, 0x84, 0xA7, 0x84, 0xA8, /* 0x8C-0x8F */
+ 0xD8, 0xE5, 0x84, 0xA9, 0x84, 0xAA, 0xD8, 0xE6, /* 0x90-0x93 */
+ 0x84, 0xAB, 0x84, 0xAC, 0x84, 0xAD, 0x84, 0xAE, /* 0x94-0x97 */
+ 0x84, 0xAF, 0x84, 0xB0, 0x84, 0xB1, 0xC1, 0xA6, /* 0x98-0x9B */
+ 0x84, 0xB2, 0xC8, 0xB0, 0xB0, 0xEC, 0xB9, 0xA6, /* 0x9C-0x9F */
+ 0xBC, 0xD3, 0xCE, 0xF1, 0xDB, 0xBD, 0xC1, 0xD3, /* 0xA0-0xA3 */
+ 0x84, 0xB3, 0x84, 0xB4, 0x84, 0xB5, 0x84, 0xB6, /* 0xA4-0xA7 */
+ 0xB6, 0xAF, 0xD6, 0xFA, 0xC5, 0xAC, 0xBD, 0xD9, /* 0xA8-0xAB */
+ 0xDB, 0xBE, 0xDB, 0xBF, 0x84, 0xB7, 0x84, 0xB8, /* 0xAC-0xAF */
+ 0x84, 0xB9, 0xC0, 0xF8, 0xBE, 0xA2, 0xC0, 0xCD, /* 0xB0-0xB3 */
+ 0x84, 0xBA, 0x84, 0xBB, 0x84, 0xBC, 0x84, 0xBD, /* 0xB4-0xB7 */
+ 0x84, 0xBE, 0x84, 0xBF, 0x84, 0xC0, 0x84, 0xC1, /* 0xB8-0xBB */
+ 0x84, 0xC2, 0x84, 0xC3, 0xDB, 0xC0, 0xCA, 0xC6, /* 0xBC-0xBF */
+ 0x84, 0xC4, 0x84, 0xC5, 0x84, 0xC6, 0xB2, 0xAA, /* 0xC0-0xC3 */
+ 0x84, 0xC7, 0x84, 0xC8, 0x84, 0xC9, 0xD3, 0xC2, /* 0xC4-0xC7 */
+ 0x84, 0xCA, 0xC3, 0xE3, 0x84, 0xCB, 0xD1, 0xAB, /* 0xC8-0xCB */
+ 0x84, 0xCC, 0x84, 0xCD, 0x84, 0xCE, 0x84, 0xCF, /* 0xCC-0xCF */
+ 0xDB, 0xC2, 0x84, 0xD0, 0xC0, 0xD5, 0x84, 0xD1, /* 0xD0-0xD3 */
+ 0x84, 0xD2, 0x84, 0xD3, 0xDB, 0xC3, 0x84, 0xD4, /* 0xD4-0xD7 */
+ 0xBF, 0xB1, 0x84, 0xD5, 0x84, 0xD6, 0x84, 0xD7, /* 0xD8-0xDB */
+ 0x84, 0xD8, 0x84, 0xD9, 0x84, 0xDA, 0xC4, 0xBC, /* 0xDC-0xDF */
+ 0x84, 0xDB, 0x84, 0xDC, 0x84, 0xDD, 0x84, 0xDE, /* 0xE0-0xE3 */
+ 0xC7, 0xDA, 0x84, 0xDF, 0x84, 0xE0, 0x84, 0xE1, /* 0xE4-0xE7 */
+ 0x84, 0xE2, 0x84, 0xE3, 0x84, 0xE4, 0x84, 0xE5, /* 0xE8-0xEB */
+ 0x84, 0xE6, 0x84, 0xE7, 0x84, 0xE8, 0x84, 0xE9, /* 0xEC-0xEF */
+ 0xDB, 0xC4, 0x84, 0xEA, 0x84, 0xEB, 0x84, 0xEC, /* 0xF0-0xF3 */
+ 0x84, 0xED, 0x84, 0xEE, 0x84, 0xEF, 0x84, 0xF0, /* 0xF4-0xF7 */
+ 0x84, 0xF1, 0xD9, 0xE8, 0xC9, 0xD7, 0x84, 0xF2, /* 0xF8-0xFB */
+ 0x84, 0xF3, 0x84, 0xF4, 0xB9, 0xB4, 0xCE, 0xF0, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_53[512] = {
+ 0xD4, 0xC8, 0x84, 0xF5, 0x84, 0xF6, 0x84, 0xF7, /* 0x00-0x03 */
+ 0x84, 0xF8, 0xB0, 0xFC, 0xB4, 0xD2, 0x84, 0xF9, /* 0x04-0x07 */
+ 0xD0, 0xD9, 0x84, 0xFA, 0x84, 0xFB, 0x84, 0xFC, /* 0x08-0x0B */
+ 0x84, 0xFD, 0xD9, 0xE9, 0x84, 0xFE, 0xDE, 0xCB, /* 0x0C-0x0F */
+ 0xD9, 0xEB, 0x85, 0x40, 0x85, 0x41, 0x85, 0x42, /* 0x10-0x13 */
+ 0x85, 0x43, 0xD8, 0xB0, 0xBB, 0xAF, 0xB1, 0xB1, /* 0x14-0x17 */
+ 0x85, 0x44, 0xB3, 0xD7, 0xD8, 0xCE, 0x85, 0x45, /* 0x18-0x1B */
+ 0x85, 0x46, 0xD4, 0xD1, 0x85, 0x47, 0x85, 0x48, /* 0x1C-0x1F */
+ 0xBD, 0xB3, 0xBF, 0xEF, 0x85, 0x49, 0xCF, 0xBB, /* 0x20-0x23 */
+ 0x85, 0x4A, 0x85, 0x4B, 0xD8, 0xD0, 0x85, 0x4C, /* 0x24-0x27 */
+ 0x85, 0x4D, 0x85, 0x4E, 0xB7, 0xCB, 0x85, 0x4F, /* 0x28-0x2B */
+ 0x85, 0x50, 0x85, 0x51, 0xD8, 0xD1, 0x85, 0x52, /* 0x2C-0x2F */
+ 0x85, 0x53, 0x85, 0x54, 0x85, 0x55, 0x85, 0x56, /* 0x30-0x33 */
+ 0x85, 0x57, 0x85, 0x58, 0x85, 0x59, 0x85, 0x5A, /* 0x34-0x37 */
+ 0x85, 0x5B, 0xC6, 0xA5, 0xC7, 0xF8, 0xD2, 0xBD, /* 0x38-0x3B */
+ 0x85, 0x5C, 0x85, 0x5D, 0xD8, 0xD2, 0xC4, 0xE4, /* 0x3C-0x3F */
+ 0x85, 0x5E, 0xCA, 0xAE, 0x85, 0x5F, 0xC7, 0xA7, /* 0x40-0x43 */
+ 0x85, 0x60, 0xD8, 0xA6, 0x85, 0x61, 0xC9, 0xFD, /* 0x44-0x47 */
+ 0xCE, 0xE7, 0xBB, 0xDC, 0xB0, 0xEB, 0x85, 0x62, /* 0x48-0x4B */
+ 0x85, 0x63, 0x85, 0x64, 0xBB, 0xAA, 0xD0, 0xAD, /* 0x4C-0x4F */
+ 0x85, 0x65, 0xB1, 0xB0, 0xD7, 0xE4, 0xD7, 0xBF, /* 0x50-0x53 */
+ 0x85, 0x66, 0xB5, 0xA5, 0xC2, 0xF4, 0xC4, 0xCF, /* 0x54-0x57 */
+ 0x85, 0x67, 0x85, 0x68, 0xB2, 0xA9, 0x85, 0x69, /* 0x58-0x5B */
+ 0xB2, 0xB7, 0x85, 0x6A, 0xB1, 0xE5, 0xDF, 0xB2, /* 0x5C-0x5F */
+ 0xD5, 0xBC, 0xBF, 0xA8, 0xC2, 0xAC, 0xD8, 0xD5, /* 0x60-0x63 */
+ 0xC2, 0xB1, 0x85, 0x6B, 0xD8, 0xD4, 0xCE, 0xD4, /* 0x64-0x67 */
+ 0x85, 0x6C, 0xDA, 0xE0, 0x85, 0x6D, 0xCE, 0xC0, /* 0x68-0x6B */
+ 0x85, 0x6E, 0x85, 0x6F, 0xD8, 0xB4, 0xC3, 0xAE, /* 0x6C-0x6F */
+ 0xD3, 0xA1, 0xCE, 0xA3, 0x85, 0x70, 0xBC, 0xB4, /* 0x70-0x73 */
+ 0xC8, 0xB4, 0xC2, 0xD1, 0x85, 0x71, 0xBE, 0xED, /* 0x74-0x77 */
+ 0xD0, 0xB6, 0x85, 0x72, 0xDA, 0xE1, 0x85, 0x73, /* 0x78-0x7B */
+ 0x85, 0x74, 0x85, 0x75, 0x85, 0x76, 0xC7, 0xE4, /* 0x7C-0x7F */
+
+ 0x85, 0x77, 0x85, 0x78, 0xB3, 0xA7, 0x85, 0x79, /* 0x80-0x83 */
+ 0xB6, 0xF2, 0xCC, 0xFC, 0xC0, 0xFA, 0x85, 0x7A, /* 0x84-0x87 */
+ 0x85, 0x7B, 0xC0, 0xF7, 0x85, 0x7C, 0xD1, 0xB9, /* 0x88-0x8B */
+ 0xD1, 0xE1, 0xD8, 0xC7, 0x85, 0x7D, 0x85, 0x7E, /* 0x8C-0x8F */
+ 0x85, 0x80, 0x85, 0x81, 0x85, 0x82, 0x85, 0x83, /* 0x90-0x93 */
+ 0x85, 0x84, 0xB2, 0xDE, 0x85, 0x85, 0x85, 0x86, /* 0x94-0x97 */
+ 0xC0, 0xE5, 0x85, 0x87, 0xBA, 0xF1, 0x85, 0x88, /* 0x98-0x9B */
+ 0x85, 0x89, 0xD8, 0xC8, 0x85, 0x8A, 0xD4, 0xAD, /* 0x9C-0x9F */
+ 0x85, 0x8B, 0x85, 0x8C, 0xCF, 0xE1, 0xD8, 0xC9, /* 0xA0-0xA3 */
+ 0x85, 0x8D, 0xD8, 0xCA, 0xCF, 0xC3, 0x85, 0x8E, /* 0xA4-0xA7 */
+ 0xB3, 0xF8, 0xBE, 0xC7, 0x85, 0x8F, 0x85, 0x90, /* 0xA8-0xAB */
+ 0x85, 0x91, 0x85, 0x92, 0xD8, 0xCB, 0x85, 0x93, /* 0xAC-0xAF */
+ 0x85, 0x94, 0x85, 0x95, 0x85, 0x96, 0x85, 0x97, /* 0xB0-0xB3 */
+ 0x85, 0x98, 0x85, 0x99, 0xDB, 0xCC, 0x85, 0x9A, /* 0xB4-0xB7 */
+ 0x85, 0x9B, 0x85, 0x9C, 0x85, 0x9D, 0xC8, 0xA5, /* 0xB8-0xBB */
+ 0x85, 0x9E, 0x85, 0x9F, 0x85, 0xA0, 0xCF, 0xD8, /* 0xBC-0xBF */
+ 0x85, 0xA1, 0xC8, 0xFE, 0xB2, 0xCE, 0x85, 0xA2, /* 0xC0-0xC3 */
+ 0x85, 0xA3, 0x85, 0xA4, 0x85, 0xA5, 0x85, 0xA6, /* 0xC4-0xC7 */
+ 0xD3, 0xD6, 0xB2, 0xE6, 0xBC, 0xB0, 0xD3, 0xD1, /* 0xC8-0xCB */
+ 0xCB, 0xAB, 0xB7, 0xB4, 0x85, 0xA7, 0x85, 0xA8, /* 0xCC-0xCF */
+ 0x85, 0xA9, 0xB7, 0xA2, 0x85, 0xAA, 0x85, 0xAB, /* 0xD0-0xD3 */
+ 0xCA, 0xE5, 0x85, 0xAC, 0xC8, 0xA1, 0xCA, 0xDC, /* 0xD4-0xD7 */
+ 0xB1, 0xE4, 0xD0, 0xF0, 0x85, 0xAD, 0xC5, 0xD1, /* 0xD8-0xDB */
+ 0x85, 0xAE, 0x85, 0xAF, 0x85, 0xB0, 0xDB, 0xC5, /* 0xDC-0xDF */
+ 0xB5, 0xFE, 0x85, 0xB1, 0x85, 0xB2, 0xBF, 0xDA, /* 0xE0-0xE3 */
+ 0xB9, 0xC5, 0xBE, 0xE4, 0xC1, 0xED, 0x85, 0xB3, /* 0xE4-0xE7 */
+ 0xDF, 0xB6, 0xDF, 0xB5, 0xD6, 0xBB, 0xBD, 0xD0, /* 0xE8-0xEB */
+ 0xD5, 0xD9, 0xB0, 0xC8, 0xB6, 0xA3, 0xBF, 0xC9, /* 0xEC-0xEF */
+ 0xCC, 0xA8, 0xDF, 0xB3, 0xCA, 0xB7, 0xD3, 0xD2, /* 0xF0-0xF3 */
+ 0x85, 0xB4, 0xD8, 0xCF, 0xD2, 0xB6, 0xBA, 0xC5, /* 0xF4-0xF7 */
+ 0xCB, 0xBE, 0xCC, 0xBE, 0x85, 0xB5, 0xDF, 0xB7, /* 0xF8-0xFB */
+ 0xB5, 0xF0, 0xDF, 0xB4, 0x85, 0xB6, 0x85, 0xB7, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_54[512] = {
+ 0x85, 0xB8, 0xD3, 0xF5, 0x85, 0xB9, 0xB3, 0xD4, /* 0x00-0x03 */
+ 0xB8, 0xF7, 0x85, 0xBA, 0xDF, 0xBA, 0x85, 0xBB, /* 0x04-0x07 */
+ 0xBA, 0xCF, 0xBC, 0xAA, 0xB5, 0xF5, 0x85, 0xBC, /* 0x08-0x0B */
+ 0xCD, 0xAC, 0xC3, 0xFB, 0xBA, 0xF3, 0xC0, 0xF4, /* 0x0C-0x0F */
+ 0xCD, 0xC2, 0xCF, 0xF2, 0xDF, 0xB8, 0xCF, 0xC5, /* 0x10-0x13 */
+ 0x85, 0xBD, 0xC2, 0xC0, 0xDF, 0xB9, 0xC2, 0xF0, /* 0x14-0x17 */
+ 0x85, 0xBE, 0x85, 0xBF, 0x85, 0xC0, 0xBE, 0xFD, /* 0x18-0x1B */
+ 0x85, 0xC1, 0xC1, 0xDF, 0xCD, 0xCC, 0xD2, 0xF7, /* 0x1C-0x1F */
+ 0xB7, 0xCD, 0xDF, 0xC1, 0x85, 0xC2, 0xDF, 0xC4, /* 0x20-0x23 */
+ 0x85, 0xC3, 0x85, 0xC4, 0xB7, 0xF1, 0xB0, 0xC9, /* 0x24-0x27 */
+ 0xB6, 0xD6, 0xB7, 0xD4, 0x85, 0xC5, 0xBA, 0xAC, /* 0x28-0x2B */
+ 0xCC, 0xFD, 0xBF, 0xD4, 0xCB, 0xB1, 0xC6, 0xF4, /* 0x2C-0x2F */
+ 0x85, 0xC6, 0xD6, 0xA8, 0xDF, 0xC5, 0x85, 0xC7, /* 0x30-0x33 */
+ 0xCE, 0xE2, 0xB3, 0xB3, 0x85, 0xC8, 0x85, 0xC9, /* 0x34-0x37 */
+ 0xCE, 0xFC, 0xB4, 0xB5, 0x85, 0xCA, 0xCE, 0xC7, /* 0x38-0x3B */
+ 0xBA, 0xF0, 0x85, 0xCB, 0xCE, 0xE1, 0x85, 0xCC, /* 0x3C-0x3F */
+ 0xD1, 0xBD, 0x85, 0xCD, 0x85, 0xCE, 0xDF, 0xC0, /* 0x40-0x43 */
+ 0x85, 0xCF, 0x85, 0xD0, 0xB4, 0xF4, 0x85, 0xD1, /* 0x44-0x47 */
+ 0xB3, 0xCA, 0x85, 0xD2, 0xB8, 0xE6, 0xDF, 0xBB, /* 0x48-0x4B */
+ 0x85, 0xD3, 0x85, 0xD4, 0x85, 0xD5, 0x85, 0xD6, /* 0x4C-0x4F */
+ 0xC4, 0xC5, 0x85, 0xD7, 0xDF, 0xBC, 0xDF, 0xBD, /* 0x50-0x53 */
+ 0xDF, 0xBE, 0xC5, 0xBB, 0xDF, 0xBF, 0xDF, 0xC2, /* 0x54-0x57 */
+ 0xD4, 0xB1, 0xDF, 0xC3, 0x85, 0xD8, 0xC7, 0xBA, /* 0x58-0x5B */
+ 0xCE, 0xD8, 0x85, 0xD9, 0x85, 0xDA, 0x85, 0xDB, /* 0x5C-0x5F */
+ 0x85, 0xDC, 0x85, 0xDD, 0xC4, 0xD8, 0x85, 0xDE, /* 0x60-0x63 */
+ 0xDF, 0xCA, 0x85, 0xDF, 0xDF, 0xCF, 0x85, 0xE0, /* 0x64-0x67 */
+ 0xD6, 0xDC, 0x85, 0xE1, 0x85, 0xE2, 0x85, 0xE3, /* 0x68-0x6B */
+ 0x85, 0xE4, 0x85, 0xE5, 0x85, 0xE6, 0x85, 0xE7, /* 0x6C-0x6F */
+ 0x85, 0xE8, 0xDF, 0xC9, 0xDF, 0xDA, 0xCE, 0xB6, /* 0x70-0x73 */
+ 0x85, 0xE9, 0xBA, 0xC7, 0xDF, 0xCE, 0xDF, 0xC8, /* 0x74-0x77 */
+ 0xC5, 0xDE, 0x85, 0xEA, 0x85, 0xEB, 0xC9, 0xEB, /* 0x78-0x7B */
+ 0xBA, 0xF4, 0xC3, 0xFC, 0x85, 0xEC, 0x85, 0xED, /* 0x7C-0x7F */
+
+ 0xBE, 0xD7, 0x85, 0xEE, 0xDF, 0xC6, 0x85, 0xEF, /* 0x80-0x83 */
+ 0xDF, 0xCD, 0x85, 0xF0, 0xC5, 0xD8, 0x85, 0xF1, /* 0x84-0x87 */
+ 0x85, 0xF2, 0x85, 0xF3, 0x85, 0xF4, 0xD5, 0xA6, /* 0x88-0x8B */
+ 0xBA, 0xCD, 0x85, 0xF5, 0xBE, 0xCC, 0xD3, 0xBD, /* 0x8C-0x8F */
+ 0xB8, 0xC0, 0x85, 0xF6, 0xD6, 0xE4, 0x85, 0xF7, /* 0x90-0x93 */
+ 0xDF, 0xC7, 0xB9, 0xBE, 0xBF, 0xA7, 0x85, 0xF8, /* 0x94-0x97 */
+ 0x85, 0xF9, 0xC1, 0xFC, 0xDF, 0xCB, 0xDF, 0xCC, /* 0x98-0x9B */
+ 0x85, 0xFA, 0xDF, 0xD0, 0x85, 0xFB, 0x85, 0xFC, /* 0x9C-0x9F */
+ 0x85, 0xFD, 0x85, 0xFE, 0x86, 0x40, 0xDF, 0xDB, /* 0xA0-0xA3 */
+ 0xDF, 0xE5, 0x86, 0x41, 0xDF, 0xD7, 0xDF, 0xD6, /* 0xA4-0xA7 */
+ 0xD7, 0xC9, 0xDF, 0xE3, 0xDF, 0xE4, 0xE5, 0xEB, /* 0xA8-0xAB */
+ 0xD2, 0xA7, 0xDF, 0xD2, 0x86, 0x42, 0xBF, 0xA9, /* 0xAC-0xAF */
+ 0x86, 0x43, 0xD4, 0xDB, 0x86, 0x44, 0xBF, 0xC8, /* 0xB0-0xB3 */
+ 0xDF, 0xD4, 0x86, 0x45, 0x86, 0x46, 0x86, 0x47, /* 0xB4-0xB7 */
+ 0xCF, 0xCC, 0x86, 0x48, 0x86, 0x49, 0xDF, 0xDD, /* 0xB8-0xBB */
+ 0x86, 0x4A, 0xD1, 0xCA, 0x86, 0x4B, 0xDF, 0xDE, /* 0xBC-0xBF */
+ 0xB0, 0xA7, 0xC6, 0xB7, 0xDF, 0xD3, 0x86, 0x4C, /* 0xC0-0xC3 */
+ 0xBA, 0xE5, 0x86, 0x4D, 0xB6, 0xDF, 0xCD, 0xDB, /* 0xC4-0xC7 */
+ 0xB9, 0xFE, 0xD4, 0xD5, 0x86, 0x4E, 0x86, 0x4F, /* 0xC8-0xCB */
+ 0xDF, 0xDF, 0xCF, 0xEC, 0xB0, 0xA5, 0xDF, 0xE7, /* 0xCC-0xCF */
+ 0xDF, 0xD1, 0xD1, 0xC6, 0xDF, 0xD5, 0xDF, 0xD8, /* 0xD0-0xD3 */
+ 0xDF, 0xD9, 0xDF, 0xDC, 0x86, 0x50, 0xBB, 0xA9, /* 0xD4-0xD7 */
+ 0x86, 0x51, 0xDF, 0xE0, 0xDF, 0xE1, 0x86, 0x52, /* 0xD8-0xDB */
+ 0xDF, 0xE2, 0xDF, 0xE6, 0xDF, 0xE8, 0xD3, 0xB4, /* 0xDC-0xDF */
+ 0x86, 0x53, 0x86, 0x54, 0x86, 0x55, 0x86, 0x56, /* 0xE0-0xE3 */
+ 0x86, 0x57, 0xB8, 0xE7, 0xC5, 0xB6, 0xDF, 0xEA, /* 0xE4-0xE7 */
+ 0xC9, 0xDA, 0xC1, 0xA8, 0xC4, 0xC4, 0x86, 0x58, /* 0xE8-0xEB */
+ 0x86, 0x59, 0xBF, 0xDE, 0xCF, 0xF8, 0x86, 0x5A, /* 0xEC-0xEF */
+ 0x86, 0x5B, 0x86, 0x5C, 0xD5, 0xDC, 0xDF, 0xEE, /* 0xF0-0xF3 */
+ 0x86, 0x5D, 0x86, 0x5E, 0x86, 0x5F, 0x86, 0x60, /* 0xF4-0xF7 */
+ 0x86, 0x61, 0x86, 0x62, 0xB2, 0xB8, 0x86, 0x63, /* 0xF8-0xFB */
+ 0xBA, 0xDF, 0xDF, 0xEC, 0x86, 0x64, 0xDB, 0xC1, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_55[512] = {
+ 0x86, 0x65, 0xD1, 0xE4, 0x86, 0x66, 0x86, 0x67, /* 0x00-0x03 */
+ 0x86, 0x68, 0x86, 0x69, 0xCB, 0xF4, 0xB4, 0xBD, /* 0x04-0x07 */
+ 0x86, 0x6A, 0xB0, 0xA6, 0x86, 0x6B, 0x86, 0x6C, /* 0x08-0x0B */
+ 0x86, 0x6D, 0x86, 0x6E, 0x86, 0x6F, 0xDF, 0xF1, /* 0x0C-0x0F */
+ 0xCC, 0xC6, 0xDF, 0xF2, 0x86, 0x70, 0x86, 0x71, /* 0x10-0x13 */
+ 0xDF, 0xED, 0x86, 0x72, 0x86, 0x73, 0x86, 0x74, /* 0x14-0x17 */
+ 0x86, 0x75, 0x86, 0x76, 0x86, 0x77, 0xDF, 0xE9, /* 0x18-0x1B */
+ 0x86, 0x78, 0x86, 0x79, 0x86, 0x7A, 0x86, 0x7B, /* 0x1C-0x1F */
+ 0xDF, 0xEB, 0x86, 0x7C, 0xDF, 0xEF, 0xDF, 0xF0, /* 0x20-0x23 */
+ 0xBB, 0xBD, 0x86, 0x7D, 0x86, 0x7E, 0xDF, 0xF3, /* 0x24-0x27 */
+ 0x86, 0x80, 0x86, 0x81, 0xDF, 0xF4, 0x86, 0x82, /* 0x28-0x2B */
+ 0xBB, 0xA3, 0x86, 0x83, 0xCA, 0xDB, 0xCE, 0xA8, /* 0x2C-0x2F */
+ 0xE0, 0xA7, 0xB3, 0xAA, 0x86, 0x84, 0xE0, 0xA6, /* 0x30-0x33 */
+ 0x86, 0x85, 0x86, 0x86, 0x86, 0x87, 0xE0, 0xA1, /* 0x34-0x37 */
+ 0x86, 0x88, 0x86, 0x89, 0x86, 0x8A, 0x86, 0x8B, /* 0x38-0x3B */
+ 0xDF, 0xFE, 0x86, 0x8C, 0xCD, 0xD9, 0xDF, 0xFC, /* 0x3C-0x3F */
+ 0x86, 0x8D, 0xDF, 0xFA, 0x86, 0x8E, 0xBF, 0xD0, /* 0x40-0x43 */
+ 0xD7, 0xC4, 0x86, 0x8F, 0xC9, 0xCC, 0x86, 0x90, /* 0x44-0x47 */
+ 0x86, 0x91, 0xDF, 0xF8, 0xB0, 0xA1, 0x86, 0x92, /* 0x48-0x4B */
+ 0x86, 0x93, 0x86, 0x94, 0x86, 0x95, 0x86, 0x96, /* 0x4C-0x4F */
+ 0xDF, 0xFD, 0x86, 0x97, 0x86, 0x98, 0x86, 0x99, /* 0x50-0x53 */
+ 0x86, 0x9A, 0xDF, 0xFB, 0xE0, 0xA2, 0x86, 0x9B, /* 0x54-0x57 */
+ 0x86, 0x9C, 0x86, 0x9D, 0x86, 0x9E, 0x86, 0x9F, /* 0x58-0x5B */
+ 0xE0, 0xA8, 0x86, 0xA0, 0x86, 0xA1, 0x86, 0xA2, /* 0x5C-0x5F */
+ 0x86, 0xA3, 0xB7, 0xC8, 0x86, 0xA4, 0x86, 0xA5, /* 0x60-0x63 */
+ 0xC6, 0xA1, 0xC9, 0xB6, 0xC0, 0xB2, 0xDF, 0xF5, /* 0x64-0x67 */
+ 0x86, 0xA6, 0x86, 0xA7, 0xC5, 0xBE, 0x86, 0xA8, /* 0x68-0x6B */
+ 0xD8, 0xC4, 0xDF, 0xF9, 0xC4, 0xF6, 0x86, 0xA9, /* 0x6C-0x6F */
+ 0x86, 0xAA, 0x86, 0xAB, 0x86, 0xAC, 0x86, 0xAD, /* 0x70-0x73 */
+ 0x86, 0xAE, 0xE0, 0xA3, 0xE0, 0xA4, 0xE0, 0xA5, /* 0x74-0x77 */
+ 0xD0, 0xA5, 0x86, 0xAF, 0x86, 0xB0, 0xE0, 0xB4, /* 0x78-0x7B */
+ 0xCC, 0xE4, 0x86, 0xB1, 0xE0, 0xB1, 0x86, 0xB2, /* 0x7C-0x7F */
+
+ 0xBF, 0xA6, 0xE0, 0xAF, 0xCE, 0xB9, 0xE0, 0xAB, /* 0x80-0x83 */
+ 0xC9, 0xC6, 0x86, 0xB3, 0x86, 0xB4, 0xC0, 0xAE, /* 0x84-0x87 */
+ 0xE0, 0xAE, 0xBA, 0xED, 0xBA, 0xB0, 0xE0, 0xA9, /* 0x88-0x8B */
+ 0x86, 0xB5, 0x86, 0xB6, 0x86, 0xB7, 0xDF, 0xF6, /* 0x8C-0x8F */
+ 0x86, 0xB8, 0xE0, 0xB3, 0x86, 0xB9, 0x86, 0xBA, /* 0x90-0x93 */
+ 0xE0, 0xB8, 0x86, 0xBB, 0x86, 0xBC, 0x86, 0xBD, /* 0x94-0x97 */
+ 0xB4, 0xAD, 0xE0, 0xB9, 0x86, 0xBE, 0x86, 0xBF, /* 0x98-0x9B */
+ 0xCF, 0xB2, 0xBA, 0xC8, 0x86, 0xC0, 0xE0, 0xB0, /* 0x9C-0x9F */
+ 0x86, 0xC1, 0x86, 0xC2, 0x86, 0xC3, 0x86, 0xC4, /* 0xA0-0xA3 */
+ 0x86, 0xC5, 0x86, 0xC6, 0x86, 0xC7, 0xD0, 0xFA, /* 0xA4-0xA7 */
+ 0x86, 0xC8, 0x86, 0xC9, 0x86, 0xCA, 0x86, 0xCB, /* 0xA8-0xAB */
+ 0x86, 0xCC, 0x86, 0xCD, 0x86, 0xCE, 0x86, 0xCF, /* 0xAC-0xAF */
+ 0x86, 0xD0, 0xE0, 0xAC, 0x86, 0xD1, 0xD4, 0xFB, /* 0xB0-0xB3 */
+ 0x86, 0xD2, 0xDF, 0xF7, 0x86, 0xD3, 0xC5, 0xE7, /* 0xB4-0xB7 */
+ 0x86, 0xD4, 0xE0, 0xAD, 0x86, 0xD5, 0xD3, 0xF7, /* 0xB8-0xBB */
+ 0x86, 0xD6, 0xE0, 0xB6, 0xE0, 0xB7, 0x86, 0xD7, /* 0xBC-0xBF */
+ 0x86, 0xD8, 0x86, 0xD9, 0x86, 0xDA, 0x86, 0xDB, /* 0xC0-0xC3 */
+ 0xE0, 0xC4, 0xD0, 0xE1, 0x86, 0xDC, 0x86, 0xDD, /* 0xC4-0xC7 */
+ 0x86, 0xDE, 0xE0, 0xBC, 0x86, 0xDF, 0x86, 0xE0, /* 0xC8-0xCB */
+ 0xE0, 0xC9, 0xE0, 0xCA, 0x86, 0xE1, 0x86, 0xE2, /* 0xCC-0xCF */
+ 0x86, 0xE3, 0xE0, 0xBE, 0xE0, 0xAA, 0xC9, 0xA4, /* 0xD0-0xD3 */
+ 0xE0, 0xC1, 0x86, 0xE4, 0xE0, 0xB2, 0x86, 0xE5, /* 0xD4-0xD7 */
+ 0x86, 0xE6, 0x86, 0xE7, 0x86, 0xE8, 0x86, 0xE9, /* 0xD8-0xDB */
+ 0xCA, 0xC8, 0xE0, 0xC3, 0x86, 0xEA, 0xE0, 0xB5, /* 0xDC-0xDF */
+ 0x86, 0xEB, 0xCE, 0xCB, 0x86, 0xEC, 0xCB, 0xC3, /* 0xE0-0xE3 */
+ 0xE0, 0xCD, 0xE0, 0xC6, 0xE0, 0xC2, 0x86, 0xED, /* 0xE4-0xE7 */
+ 0xE0, 0xCB, 0x86, 0xEE, 0xE0, 0xBA, 0xE0, 0xBF, /* 0xE8-0xEB */
+ 0xE0, 0xC0, 0x86, 0xEF, 0x86, 0xF0, 0xE0, 0xC5, /* 0xEC-0xEF */
+ 0x86, 0xF1, 0x86, 0xF2, 0xE0, 0xC7, 0xE0, 0xC8, /* 0xF0-0xF3 */
+ 0x86, 0xF3, 0xE0, 0xCC, 0x86, 0xF4, 0xE0, 0xBB, /* 0xF4-0xF7 */
+ 0x86, 0xF5, 0x86, 0xF6, 0x86, 0xF7, 0x86, 0xF8, /* 0xF8-0xFB */
+ 0x86, 0xF9, 0xCB, 0xD4, 0xE0, 0xD5, 0x86, 0xFA, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_56[512] = {
+ 0xE0, 0xD6, 0xE0, 0xD2, 0x86, 0xFB, 0x86, 0xFC, /* 0x00-0x03 */
+ 0x86, 0xFD, 0x86, 0xFE, 0x87, 0x40, 0x87, 0x41, /* 0x04-0x07 */
+ 0xE0, 0xD0, 0xBC, 0xCE, 0x87, 0x42, 0x87, 0x43, /* 0x08-0x0B */
+ 0xE0, 0xD1, 0x87, 0x44, 0xB8, 0xC2, 0xD8, 0xC5, /* 0x0C-0x0F */
+ 0x87, 0x45, 0x87, 0x46, 0x87, 0x47, 0x87, 0x48, /* 0x10-0x13 */
+ 0x87, 0x49, 0x87, 0x4A, 0x87, 0x4B, 0x87, 0x4C, /* 0x14-0x17 */
+ 0xD0, 0xEA, 0x87, 0x4D, 0x87, 0x4E, 0xC2, 0xEF, /* 0x18-0x1B */
+ 0x87, 0x4F, 0x87, 0x50, 0xE0, 0xCF, 0xE0, 0xBD, /* 0x1C-0x1F */
+ 0x87, 0x51, 0x87, 0x52, 0x87, 0x53, 0xE0, 0xD4, /* 0x20-0x23 */
+ 0xE0, 0xD3, 0x87, 0x54, 0x87, 0x55, 0xE0, 0xD7, /* 0x24-0x27 */
+ 0x87, 0x56, 0x87, 0x57, 0x87, 0x58, 0x87, 0x59, /* 0x28-0x2B */
+ 0xE0, 0xDC, 0xE0, 0xD8, 0x87, 0x5A, 0x87, 0x5B, /* 0x2C-0x2F */
+ 0x87, 0x5C, 0xD6, 0xF6, 0xB3, 0xB0, 0x87, 0x5D, /* 0x30-0x33 */
+ 0xD7, 0xEC, 0x87, 0x5E, 0xCB, 0xBB, 0x87, 0x5F, /* 0x34-0x37 */
+ 0x87, 0x60, 0xE0, 0xDA, 0x87, 0x61, 0xCE, 0xFB, /* 0x38-0x3B */
+ 0x87, 0x62, 0x87, 0x63, 0x87, 0x64, 0xBA, 0xD9, /* 0x3C-0x3F */
+ 0x87, 0x65, 0x87, 0x66, 0x87, 0x67, 0x87, 0x68, /* 0x40-0x43 */
+ 0x87, 0x69, 0x87, 0x6A, 0x87, 0x6B, 0x87, 0x6C, /* 0x44-0x47 */
+ 0x87, 0x6D, 0x87, 0x6E, 0x87, 0x6F, 0x87, 0x70, /* 0x48-0x4B */
+ 0xE0, 0xE1, 0xE0, 0xDD, 0xD2, 0xAD, 0x87, 0x71, /* 0x4C-0x4F */
+ 0x87, 0x72, 0x87, 0x73, 0x87, 0x74, 0x87, 0x75, /* 0x50-0x53 */
+ 0xE0, 0xE2, 0x87, 0x76, 0x87, 0x77, 0xE0, 0xDB, /* 0x54-0x57 */
+ 0xE0, 0xD9, 0xE0, 0xDF, 0x87, 0x78, 0x87, 0x79, /* 0x58-0x5B */
+ 0xE0, 0xE0, 0x87, 0x7A, 0x87, 0x7B, 0x87, 0x7C, /* 0x5C-0x5F */
+ 0x87, 0x7D, 0x87, 0x7E, 0xE0, 0xDE, 0x87, 0x80, /* 0x60-0x63 */
+ 0xE0, 0xE4, 0x87, 0x81, 0x87, 0x82, 0x87, 0x83, /* 0x64-0x67 */
+ 0xC6, 0xF7, 0xD8, 0xAC, 0xD4, 0xEB, 0xE0, 0xE6, /* 0x68-0x6B */
+ 0xCA, 0xC9, 0x87, 0x84, 0x87, 0x85, 0x87, 0x86, /* 0x6C-0x6F */
+ 0x87, 0x87, 0xE0, 0xE5, 0x87, 0x88, 0x87, 0x89, /* 0x70-0x73 */
+ 0x87, 0x8A, 0x87, 0x8B, 0xB8, 0xC1, 0x87, 0x8C, /* 0x74-0x77 */
+ 0x87, 0x8D, 0x87, 0x8E, 0x87, 0x8F, 0xE0, 0xE7, /* 0x78-0x7B */
+ 0xE0, 0xE8, 0x87, 0x90, 0x87, 0x91, 0x87, 0x92, /* 0x7C-0x7F */
+
+ 0x87, 0x93, 0x87, 0x94, 0x87, 0x95, 0x87, 0x96, /* 0x80-0x83 */
+ 0x87, 0x97, 0xE0, 0xE9, 0xE0, 0xE3, 0x87, 0x98, /* 0x84-0x87 */
+ 0x87, 0x99, 0x87, 0x9A, 0x87, 0x9B, 0x87, 0x9C, /* 0x88-0x8B */
+ 0x87, 0x9D, 0x87, 0x9E, 0xBA, 0xBF, 0xCC, 0xE7, /* 0x8C-0x8F */
+ 0x87, 0x9F, 0x87, 0xA0, 0x87, 0xA1, 0xE0, 0xEA, /* 0x90-0x93 */
+ 0x87, 0xA2, 0x87, 0xA3, 0x87, 0xA4, 0x87, 0xA5, /* 0x94-0x97 */
+ 0x87, 0xA6, 0x87, 0xA7, 0x87, 0xA8, 0x87, 0xA9, /* 0x98-0x9B */
+ 0x87, 0xAA, 0x87, 0xAB, 0x87, 0xAC, 0x87, 0xAD, /* 0x9C-0x9F */
+ 0x87, 0xAE, 0x87, 0xAF, 0x87, 0xB0, 0xCF, 0xF9, /* 0xA0-0xA3 */
+ 0x87, 0xB1, 0x87, 0xB2, 0x87, 0xB3, 0x87, 0xB4, /* 0xA4-0xA7 */
+ 0x87, 0xB5, 0x87, 0xB6, 0x87, 0xB7, 0x87, 0xB8, /* 0xA8-0xAB */
+ 0x87, 0xB9, 0x87, 0xBA, 0x87, 0xBB, 0xE0, 0xEB, /* 0xAC-0xAF */
+ 0x87, 0xBC, 0x87, 0xBD, 0x87, 0xBE, 0x87, 0xBF, /* 0xB0-0xB3 */
+ 0x87, 0xC0, 0x87, 0xC1, 0x87, 0xC2, 0xC8, 0xC2, /* 0xB4-0xB7 */
+ 0x87, 0xC3, 0x87, 0xC4, 0x87, 0xC5, 0x87, 0xC6, /* 0xB8-0xBB */
+ 0xBD, 0xC0, 0x87, 0xC7, 0x87, 0xC8, 0x87, 0xC9, /* 0xBC-0xBF */
+ 0x87, 0xCA, 0x87, 0xCB, 0x87, 0xCC, 0x87, 0xCD, /* 0xC0-0xC3 */
+ 0x87, 0xCE, 0x87, 0xCF, 0x87, 0xD0, 0x87, 0xD1, /* 0xC4-0xC7 */
+ 0x87, 0xD2, 0x87, 0xD3, 0xC4, 0xD2, 0x87, 0xD4, /* 0xC8-0xCB */
+ 0x87, 0xD5, 0x87, 0xD6, 0x87, 0xD7, 0x87, 0xD8, /* 0xCC-0xCF */
+ 0x87, 0xD9, 0x87, 0xDA, 0x87, 0xDB, 0x87, 0xDC, /* 0xD0-0xD3 */
+ 0xE0, 0xEC, 0x87, 0xDD, 0x87, 0xDE, 0xE0, 0xED, /* 0xD4-0xD7 */
+ 0x87, 0xDF, 0x87, 0xE0, 0xC7, 0xF4, 0xCB, 0xC4, /* 0xD8-0xDB */
+ 0x87, 0xE1, 0xE0, 0xEE, 0xBB, 0xD8, 0xD8, 0xB6, /* 0xDC-0xDF */
+ 0xD2, 0xF2, 0xE0, 0xEF, 0xCD, 0xC5, 0x87, 0xE2, /* 0xE0-0xE3 */
+ 0xB6, 0xDA, 0x87, 0xE3, 0x87, 0xE4, 0x87, 0xE5, /* 0xE4-0xE7 */
+ 0x87, 0xE6, 0x87, 0xE7, 0x87, 0xE8, 0xE0, 0xF1, /* 0xE8-0xEB */
+ 0x87, 0xE9, 0xD4, 0xB0, 0x87, 0xEA, 0x87, 0xEB, /* 0xEC-0xEF */
+ 0xC0, 0xA7, 0xB4, 0xD1, 0x87, 0xEC, 0x87, 0xED, /* 0xF0-0xF3 */
+ 0xCE, 0xA7, 0xE0, 0xF0, 0x87, 0xEE, 0x87, 0xEF, /* 0xF4-0xF7 */
+ 0x87, 0xF0, 0xE0, 0xF2, 0xB9, 0xCC, 0x87, 0xF1, /* 0xF8-0xFB */
+ 0x87, 0xF2, 0xB9, 0xFA, 0xCD, 0xBC, 0xE0, 0xF3, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_57[512] = {
+ 0x87, 0xF3, 0x87, 0xF4, 0x87, 0xF5, 0xC6, 0xD4, /* 0x00-0x03 */
+ 0xE0, 0xF4, 0x87, 0xF6, 0xD4, 0xB2, 0x87, 0xF7, /* 0x04-0x07 */
+ 0xC8, 0xA6, 0xE0, 0xF6, 0xE0, 0xF5, 0x87, 0xF8, /* 0x08-0x0B */
+ 0x87, 0xF9, 0x87, 0xFA, 0x87, 0xFB, 0x87, 0xFC, /* 0x0C-0x0F */
+ 0x87, 0xFD, 0x87, 0xFE, 0x88, 0x40, 0x88, 0x41, /* 0x10-0x13 */
+ 0x88, 0x42, 0x88, 0x43, 0x88, 0x44, 0x88, 0x45, /* 0x14-0x17 */
+ 0x88, 0x46, 0x88, 0x47, 0x88, 0x48, 0x88, 0x49, /* 0x18-0x1B */
+ 0xE0, 0xF7, 0x88, 0x4A, 0x88, 0x4B, 0xCD, 0xC1, /* 0x1C-0x1F */
+ 0x88, 0x4C, 0x88, 0x4D, 0x88, 0x4E, 0xCA, 0xA5, /* 0x20-0x23 */
+ 0x88, 0x4F, 0x88, 0x50, 0x88, 0x51, 0x88, 0x52, /* 0x24-0x27 */
+ 0xD4, 0xDA, 0xDB, 0xD7, 0xDB, 0xD9, 0x88, 0x53, /* 0x28-0x2B */
+ 0xDB, 0xD8, 0xB9, 0xE7, 0xDB, 0xDC, 0xDB, 0xDD, /* 0x2C-0x2F */
+ 0xB5, 0xD8, 0x88, 0x54, 0x88, 0x55, 0xDB, 0xDA, /* 0x30-0x33 */
+ 0x88, 0x56, 0x88, 0x57, 0x88, 0x58, 0x88, 0x59, /* 0x34-0x37 */
+ 0x88, 0x5A, 0xDB, 0xDB, 0xB3, 0xA1, 0xDB, 0xDF, /* 0x38-0x3B */
+ 0x88, 0x5B, 0x88, 0x5C, 0xBB, 0xF8, 0x88, 0x5D, /* 0x3C-0x3F */
+ 0xD6, 0xB7, 0x88, 0x5E, 0xDB, 0xE0, 0x88, 0x5F, /* 0x40-0x43 */
+ 0x88, 0x60, 0x88, 0x61, 0x88, 0x62, 0xBE, 0xF9, /* 0x44-0x47 */
+ 0x88, 0x63, 0x88, 0x64, 0xB7, 0xBB, 0x88, 0x65, /* 0x48-0x4B */
+ 0xDB, 0xD0, 0xCC, 0xAE, 0xBF, 0xB2, 0xBB, 0xB5, /* 0x4C-0x4F */
+ 0xD7, 0xF8, 0xBF, 0xD3, 0x88, 0x66, 0x88, 0x67, /* 0x50-0x53 */
+ 0x88, 0x68, 0x88, 0x69, 0x88, 0x6A, 0xBF, 0xE9, /* 0x54-0x57 */
+ 0x88, 0x6B, 0x88, 0x6C, 0xBC, 0xE1, 0xCC, 0xB3, /* 0x58-0x5B */
+ 0xDB, 0xDE, 0xB0, 0xD3, 0xCE, 0xEB, 0xB7, 0xD8, /* 0x5C-0x5F */
+ 0xD7, 0xB9, 0xC6, 0xC2, 0x88, 0x6D, 0x88, 0x6E, /* 0x60-0x63 */
+ 0xC0, 0xA4, 0x88, 0x6F, 0xCC, 0xB9, 0x88, 0x70, /* 0x64-0x67 */
+ 0xDB, 0xE7, 0xDB, 0xE1, 0xC6, 0xBA, 0xDB, 0xE3, /* 0x68-0x6B */
+ 0x88, 0x71, 0xDB, 0xE8, 0x88, 0x72, 0xC5, 0xF7, /* 0x6C-0x6F */
+ 0x88, 0x73, 0x88, 0x74, 0x88, 0x75, 0xDB, 0xEA, /* 0x70-0x73 */
+ 0x88, 0x76, 0x88, 0x77, 0xDB, 0xE9, 0xBF, 0xC0, /* 0x74-0x77 */
+ 0x88, 0x78, 0x88, 0x79, 0x88, 0x7A, 0xDB, 0xE6, /* 0x78-0x7B */
+ 0xDB, 0xE5, 0x88, 0x7B, 0x88, 0x7C, 0x88, 0x7D, /* 0x7C-0x7F */
+
+ 0x88, 0x7E, 0x88, 0x80, 0xB4, 0xB9, 0xC0, 0xAC, /* 0x80-0x83 */
+ 0xC2, 0xA2, 0xDB, 0xE2, 0xDB, 0xE4, 0x88, 0x81, /* 0x84-0x87 */
+ 0x88, 0x82, 0x88, 0x83, 0x88, 0x84, 0xD0, 0xCD, /* 0x88-0x8B */
+ 0xDB, 0xED, 0x88, 0x85, 0x88, 0x86, 0x88, 0x87, /* 0x8C-0x8F */
+ 0x88, 0x88, 0x88, 0x89, 0xC0, 0xDD, 0xDB, 0xF2, /* 0x90-0x93 */
+ 0x88, 0x8A, 0x88, 0x8B, 0x88, 0x8C, 0x88, 0x8D, /* 0x94-0x97 */
+ 0x88, 0x8E, 0x88, 0x8F, 0x88, 0x90, 0xB6, 0xE2, /* 0x98-0x9B */
+ 0x88, 0x91, 0x88, 0x92, 0x88, 0x93, 0x88, 0x94, /* 0x9C-0x9F */
+ 0xDB, 0xF3, 0xDB, 0xD2, 0xB9, 0xB8, 0xD4, 0xAB, /* 0xA0-0xA3 */
+ 0xDB, 0xEC, 0x88, 0x95, 0xBF, 0xD1, 0xDB, 0xF0, /* 0xA4-0xA7 */
+ 0x88, 0x96, 0xDB, 0xD1, 0x88, 0x97, 0xB5, 0xE6, /* 0xA8-0xAB */
+ 0x88, 0x98, 0xDB, 0xEB, 0xBF, 0xE5, 0x88, 0x99, /* 0xAC-0xAF */
+ 0x88, 0x9A, 0x88, 0x9B, 0xDB, 0xEE, 0x88, 0x9C, /* 0xB0-0xB3 */
+ 0xDB, 0xF1, 0x88, 0x9D, 0x88, 0x9E, 0x88, 0x9F, /* 0xB4-0xB7 */
+ 0xDB, 0xF9, 0x88, 0xA0, 0x88, 0xA1, 0x88, 0xA2, /* 0xB8-0xBB */
+ 0x88, 0xA3, 0x88, 0xA4, 0x88, 0xA5, 0x88, 0xA6, /* 0xBC-0xBF */
+ 0x88, 0xA7, 0x88, 0xA8, 0xB9, 0xA1, 0xB0, 0xA3, /* 0xC0-0xC3 */
+ 0x88, 0xA9, 0x88, 0xAA, 0x88, 0xAB, 0x88, 0xAC, /* 0xC4-0xC7 */
+ 0x88, 0xAD, 0x88, 0xAE, 0x88, 0xAF, 0xC2, 0xF1, /* 0xC8-0xCB */
+ 0x88, 0xB0, 0x88, 0xB1, 0xB3, 0xC7, 0xDB, 0xEF, /* 0xCC-0xCF */
+ 0x88, 0xB2, 0x88, 0xB3, 0xDB, 0xF8, 0x88, 0xB4, /* 0xD0-0xD3 */
+ 0xC6, 0xD2, 0xDB, 0xF4, 0x88, 0xB5, 0x88, 0xB6, /* 0xD4-0xD7 */
+ 0xDB, 0xF5, 0xDB, 0xF7, 0xDB, 0xF6, 0x88, 0xB7, /* 0xD8-0xDB */
+ 0x88, 0xB8, 0xDB, 0xFE, 0x88, 0xB9, 0xD3, 0xF2, /* 0xDC-0xDF */
+ 0xB2, 0xBA, 0x88, 0xBA, 0x88, 0xBB, 0x88, 0xBC, /* 0xE0-0xE3 */
+ 0xDB, 0xFD, 0x88, 0xBD, 0x88, 0xBE, 0x88, 0xBF, /* 0xE4-0xE7 */
+ 0x88, 0xC0, 0x88, 0xC1, 0x88, 0xC2, 0x88, 0xC3, /* 0xE8-0xEB */
+ 0x88, 0xC4, 0xDC, 0xA4, 0x88, 0xC5, 0xDB, 0xFB, /* 0xEC-0xEF */
+ 0x88, 0xC6, 0x88, 0xC7, 0x88, 0xC8, 0x88, 0xC9, /* 0xF0-0xF3 */
+ 0xDB, 0xFA, 0x88, 0xCA, 0x88, 0xCB, 0x88, 0xCC, /* 0xF4-0xF7 */
+ 0xDB, 0xFC, 0xC5, 0xE0, 0xBB, 0xF9, 0x88, 0xCD, /* 0xF8-0xFB */
+ 0x88, 0xCE, 0xDC, 0xA3, 0x88, 0xCF, 0x88, 0xD0, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_58[512] = {
+ 0xDC, 0xA5, 0x88, 0xD1, 0xCC, 0xC3, 0x88, 0xD2, /* 0x00-0x03 */
+ 0x88, 0xD3, 0x88, 0xD4, 0xB6, 0xD1, 0xDD, 0xC0, /* 0x04-0x07 */
+ 0x88, 0xD5, 0x88, 0xD6, 0x88, 0xD7, 0xDC, 0xA1, /* 0x08-0x0B */
+ 0x88, 0xD8, 0xDC, 0xA2, 0x88, 0xD9, 0x88, 0xDA, /* 0x0C-0x0F */
+ 0x88, 0xDB, 0xC7, 0xB5, 0x88, 0xDC, 0x88, 0xDD, /* 0x10-0x13 */
+ 0x88, 0xDE, 0xB6, 0xE9, 0x88, 0xDF, 0x88, 0xE0, /* 0x14-0x17 */
+ 0x88, 0xE1, 0xDC, 0xA7, 0x88, 0xE2, 0x88, 0xE3, /* 0x18-0x1B */
+ 0x88, 0xE4, 0x88, 0xE5, 0xDC, 0xA6, 0x88, 0xE6, /* 0x1C-0x1F */
+ 0xDC, 0xA9, 0xB1, 0xA4, 0x88, 0xE7, 0x88, 0xE8, /* 0x20-0x23 */
+ 0xB5, 0xCC, 0x88, 0xE9, 0x88, 0xEA, 0x88, 0xEB, /* 0x24-0x27 */
+ 0x88, 0xEC, 0x88, 0xED, 0xBF, 0xB0, 0x88, 0xEE, /* 0x28-0x2B */
+ 0x88, 0xEF, 0x88, 0xF0, 0x88, 0xF1, 0x88, 0xF2, /* 0x2C-0x2F */
+ 0xD1, 0xDF, 0x88, 0xF3, 0x88, 0xF4, 0x88, 0xF5, /* 0x30-0x33 */
+ 0x88, 0xF6, 0xB6, 0xC2, 0x88, 0xF7, 0x88, 0xF8, /* 0x34-0x37 */
+ 0x88, 0xF9, 0x88, 0xFA, 0x88, 0xFB, 0x88, 0xFC, /* 0x38-0x3B */
+ 0x88, 0xFD, 0x88, 0xFE, 0x89, 0x40, 0x89, 0x41, /* 0x3C-0x3F */
+ 0x89, 0x42, 0x89, 0x43, 0x89, 0x44, 0x89, 0x45, /* 0x40-0x43 */
+ 0xDC, 0xA8, 0x89, 0x46, 0x89, 0x47, 0x89, 0x48, /* 0x44-0x47 */
+ 0x89, 0x49, 0x89, 0x4A, 0x89, 0x4B, 0x89, 0x4C, /* 0x48-0x4B */
+ 0xCB, 0xFA, 0xEB, 0xF3, 0x89, 0x4D, 0x89, 0x4E, /* 0x4C-0x4F */
+ 0x89, 0x4F, 0xCB, 0xDC, 0x89, 0x50, 0x89, 0x51, /* 0x50-0x53 */
+ 0xCB, 0xFE, 0x89, 0x52, 0x89, 0x53, 0x89, 0x54, /* 0x54-0x57 */
+ 0xCC, 0xC1, 0x89, 0x55, 0x89, 0x56, 0x89, 0x57, /* 0x58-0x5B */
+ 0x89, 0x58, 0x89, 0x59, 0xC8, 0xFB, 0x89, 0x5A, /* 0x5C-0x5F */
+ 0x89, 0x5B, 0x89, 0x5C, 0x89, 0x5D, 0x89, 0x5E, /* 0x60-0x63 */
+ 0x89, 0x5F, 0xDC, 0xAA, 0x89, 0x60, 0x89, 0x61, /* 0x64-0x67 */
+ 0x89, 0x62, 0x89, 0x63, 0x89, 0x64, 0xCC, 0xEE, /* 0x68-0x6B */
+ 0xDC, 0xAB, 0x89, 0x65, 0x89, 0x66, 0x89, 0x67, /* 0x6C-0x6F */
+ 0x89, 0x68, 0x89, 0x69, 0x89, 0x6A, 0x89, 0x6B, /* 0x70-0x73 */
+ 0x89, 0x6C, 0x89, 0x6D, 0x89, 0x6E, 0x89, 0x6F, /* 0x74-0x77 */
+ 0x89, 0x70, 0x89, 0x71, 0x89, 0x72, 0x89, 0x73, /* 0x78-0x7B */
+ 0x89, 0x74, 0x89, 0x75, 0xDB, 0xD3, 0x89, 0x76, /* 0x7C-0x7F */
+
+ 0xDC, 0xAF, 0xDC, 0xAC, 0x89, 0x77, 0xBE, 0xB3, /* 0x80-0x83 */
+ 0x89, 0x78, 0xCA, 0xFB, 0x89, 0x79, 0x89, 0x7A, /* 0x84-0x87 */
+ 0x89, 0x7B, 0xDC, 0xAD, 0x89, 0x7C, 0x89, 0x7D, /* 0x88-0x8B */
+ 0x89, 0x7E, 0x89, 0x80, 0x89, 0x81, 0x89, 0x82, /* 0x8C-0x8F */
+ 0x89, 0x83, 0x89, 0x84, 0xC9, 0xCA, 0xC4, 0xB9, /* 0x90-0x93 */
+ 0x89, 0x85, 0x89, 0x86, 0x89, 0x87, 0x89, 0x88, /* 0x94-0x97 */
+ 0x89, 0x89, 0xC7, 0xBD, 0xDC, 0xAE, 0x89, 0x8A, /* 0x98-0x9B */
+ 0x89, 0x8B, 0x89, 0x8C, 0xD4, 0xF6, 0xD0, 0xE6, /* 0x9C-0x9F */
+ 0x89, 0x8D, 0x89, 0x8E, 0x89, 0x8F, 0x89, 0x90, /* 0xA0-0xA3 */
+ 0x89, 0x91, 0x89, 0x92, 0x89, 0x93, 0x89, 0x94, /* 0xA4-0xA7 */
+ 0xC4, 0xAB, 0xB6, 0xD5, 0x89, 0x95, 0x89, 0x96, /* 0xA8-0xAB */
+ 0x89, 0x97, 0x89, 0x98, 0x89, 0x99, 0x89, 0x9A, /* 0xAC-0xAF */
+ 0x89, 0x9B, 0x89, 0x9C, 0x89, 0x9D, 0x89, 0x9E, /* 0xB0-0xB3 */
+ 0x89, 0x9F, 0x89, 0xA0, 0x89, 0xA1, 0x89, 0xA2, /* 0xB4-0xB7 */
+ 0x89, 0xA3, 0x89, 0xA4, 0x89, 0xA5, 0x89, 0xA6, /* 0xB8-0xBB */
+ 0xDB, 0xD4, 0x89, 0xA7, 0x89, 0xA8, 0x89, 0xA9, /* 0xBC-0xBF */
+ 0x89, 0xAA, 0xB1, 0xDA, 0x89, 0xAB, 0x89, 0xAC, /* 0xC0-0xC3 */
+ 0x89, 0xAD, 0xDB, 0xD5, 0x89, 0xAE, 0x89, 0xAF, /* 0xC4-0xC7 */
+ 0x89, 0xB0, 0x89, 0xB1, 0x89, 0xB2, 0x89, 0xB3, /* 0xC8-0xCB */
+ 0x89, 0xB4, 0x89, 0xB5, 0x89, 0xB6, 0x89, 0xB7, /* 0xCC-0xCF */
+ 0x89, 0xB8, 0xDB, 0xD6, 0x89, 0xB9, 0x89, 0xBA, /* 0xD0-0xD3 */
+ 0x89, 0xBB, 0xBA, 0xBE, 0x89, 0xBC, 0x89, 0xBD, /* 0xD4-0xD7 */
+ 0x89, 0xBE, 0x89, 0xBF, 0x89, 0xC0, 0x89, 0xC1, /* 0xD8-0xDB */
+ 0x89, 0xC2, 0x89, 0xC3, 0x89, 0xC4, 0x89, 0xC5, /* 0xDC-0xDF */
+ 0x89, 0xC6, 0x89, 0xC7, 0x89, 0xC8, 0x89, 0xC9, /* 0xE0-0xE3 */
+ 0xC8, 0xC0, 0x89, 0xCA, 0x89, 0xCB, 0x89, 0xCC, /* 0xE4-0xE7 */
+ 0x89, 0xCD, 0x89, 0xCE, 0x89, 0xCF, 0xCA, 0xBF, /* 0xE8-0xEB */
+ 0xC8, 0xC9, 0x89, 0xD0, 0xD7, 0xB3, 0x89, 0xD1, /* 0xEC-0xEF */
+ 0xC9, 0xF9, 0x89, 0xD2, 0x89, 0xD3, 0xBF, 0xC7, /* 0xF0-0xF3 */
+ 0x89, 0xD4, 0x89, 0xD5, 0xBA, 0xF8, 0x89, 0xD6, /* 0xF4-0xF7 */
+ 0x89, 0xD7, 0xD2, 0xBC, 0x89, 0xD8, 0x89, 0xD9, /* 0xF8-0xFB */
+ 0x89, 0xDA, 0x89, 0xDB, 0x89, 0xDC, 0x89, 0xDD, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_59[512] = {
+ 0x89, 0xDE, 0x89, 0xDF, 0xE2, 0xBA, 0x89, 0xE0, /* 0x00-0x03 */
+ 0xB4, 0xA6, 0x89, 0xE1, 0x89, 0xE2, 0xB1, 0xB8, /* 0x04-0x07 */
+ 0x89, 0xE3, 0x89, 0xE4, 0x89, 0xE5, 0x89, 0xE6, /* 0x08-0x0B */
+ 0x89, 0xE7, 0xB8, 0xB4, 0x89, 0xE8, 0xCF, 0xC4, /* 0x0C-0x0F */
+ 0x89, 0xE9, 0x89, 0xEA, 0x89, 0xEB, 0x89, 0xEC, /* 0x10-0x13 */
+ 0xD9, 0xE7, 0xCF, 0xA6, 0xCD, 0xE2, 0x89, 0xED, /* 0x14-0x17 */
+ 0x89, 0xEE, 0xD9, 0xED, 0xB6, 0xE0, 0x89, 0xEF, /* 0x18-0x1B */
+ 0xD2, 0xB9, 0x89, 0xF0, 0x89, 0xF1, 0xB9, 0xBB, /* 0x1C-0x1F */
+ 0x89, 0xF2, 0x89, 0xF3, 0x89, 0xF4, 0x89, 0xF5, /* 0x20-0x23 */
+ 0xE2, 0xB9, 0xE2, 0xB7, 0x89, 0xF6, 0xB4, 0xF3, /* 0x24-0x27 */
+ 0x89, 0xF7, 0xCC, 0xEC, 0xCC, 0xAB, 0xB7, 0xF2, /* 0x28-0x2B */
+ 0x89, 0xF8, 0xD8, 0xB2, 0xD1, 0xEB, 0xBA, 0xBB, /* 0x2C-0x2F */
+ 0x89, 0xF9, 0xCA, 0xA7, 0x89, 0xFA, 0x89, 0xFB, /* 0x30-0x33 */
+ 0xCD, 0xB7, 0x89, 0xFC, 0x89, 0xFD, 0xD2, 0xC4, /* 0x34-0x37 */
+ 0xBF, 0xE4, 0xBC, 0xD0, 0xB6, 0xE1, 0x89, 0xFE, /* 0x38-0x3B */
+ 0xDE, 0xC5, 0x8A, 0x40, 0x8A, 0x41, 0x8A, 0x42, /* 0x3C-0x3F */
+ 0x8A, 0x43, 0xDE, 0xC6, 0xDB, 0xBC, 0x8A, 0x44, /* 0x40-0x43 */
+ 0xD1, 0xD9, 0x8A, 0x45, 0x8A, 0x46, 0xC6, 0xE6, /* 0x44-0x47 */
+ 0xC4, 0xCE, 0xB7, 0xEE, 0x8A, 0x47, 0xB7, 0xDC, /* 0x48-0x4B */
+ 0x8A, 0x48, 0x8A, 0x49, 0xBF, 0xFC, 0xD7, 0xE0, /* 0x4C-0x4F */
+ 0x8A, 0x4A, 0xC6, 0xF5, 0x8A, 0x4B, 0x8A, 0x4C, /* 0x50-0x53 */
+ 0xB1, 0xBC, 0xDE, 0xC8, 0xBD, 0xB1, 0xCC, 0xD7, /* 0x54-0x57 */
+ 0xDE, 0xCA, 0x8A, 0x4D, 0xDE, 0xC9, 0x8A, 0x4E, /* 0x58-0x5B */
+ 0x8A, 0x4F, 0x8A, 0x50, 0x8A, 0x51, 0x8A, 0x52, /* 0x5C-0x5F */
+ 0xB5, 0xEC, 0x8A, 0x53, 0xC9, 0xDD, 0x8A, 0x54, /* 0x60-0x63 */
+ 0x8A, 0x55, 0xB0, 0xC2, 0x8A, 0x56, 0x8A, 0x57, /* 0x64-0x67 */
+ 0x8A, 0x58, 0x8A, 0x59, 0x8A, 0x5A, 0x8A, 0x5B, /* 0x68-0x6B */
+ 0x8A, 0x5C, 0x8A, 0x5D, 0x8A, 0x5E, 0x8A, 0x5F, /* 0x6C-0x6F */
+ 0x8A, 0x60, 0x8A, 0x61, 0x8A, 0x62, 0xC5, 0xAE, /* 0x70-0x73 */
+ 0xC5, 0xAB, 0x8A, 0x63, 0xC4, 0xCC, 0x8A, 0x64, /* 0x74-0x77 */
+ 0xBC, 0xE9, 0xCB, 0xFD, 0x8A, 0x65, 0x8A, 0x66, /* 0x78-0x7B */
+ 0x8A, 0x67, 0xBA, 0xC3, 0x8A, 0x68, 0x8A, 0x69, /* 0x7C-0x7F */
+
+ 0x8A, 0x6A, 0xE5, 0xF9, 0xC8, 0xE7, 0xE5, 0xFA, /* 0x80-0x83 */
+ 0xCD, 0xFD, 0x8A, 0x6B, 0xD7, 0xB1, 0xB8, 0xBE, /* 0x84-0x87 */
+ 0xC2, 0xE8, 0x8A, 0x6C, 0xC8, 0xD1, 0x8A, 0x6D, /* 0x88-0x8B */
+ 0x8A, 0x6E, 0xE5, 0xFB, 0x8A, 0x6F, 0x8A, 0x70, /* 0x8C-0x8F */
+ 0x8A, 0x71, 0x8A, 0x72, 0xB6, 0xCA, 0xBC, 0xCB, /* 0x90-0x93 */
+ 0x8A, 0x73, 0x8A, 0x74, 0xD1, 0xFD, 0xE6, 0xA1, /* 0x94-0x97 */
+ 0x8A, 0x75, 0xC3, 0xEE, 0x8A, 0x76, 0x8A, 0x77, /* 0x98-0x9B */
+ 0x8A, 0x78, 0x8A, 0x79, 0xE6, 0xA4, 0x8A, 0x7A, /* 0x9C-0x9F */
+ 0x8A, 0x7B, 0x8A, 0x7C, 0x8A, 0x7D, 0xE5, 0xFE, /* 0xA0-0xA3 */
+ 0xE6, 0xA5, 0xCD, 0xD7, 0x8A, 0x7E, 0x8A, 0x80, /* 0xA4-0xA7 */
+ 0xB7, 0xC1, 0xE5, 0xFC, 0xE5, 0xFD, 0xE6, 0xA3, /* 0xA8-0xAB */
+ 0x8A, 0x81, 0x8A, 0x82, 0xC4, 0xDD, 0xE6, 0xA8, /* 0xAC-0xAF */
+ 0x8A, 0x83, 0x8A, 0x84, 0xE6, 0xA7, 0x8A, 0x85, /* 0xB0-0xB3 */
+ 0x8A, 0x86, 0x8A, 0x87, 0x8A, 0x88, 0x8A, 0x89, /* 0xB4-0xB7 */
+ 0x8A, 0x8A, 0xC3, 0xC3, 0x8A, 0x8B, 0xC6, 0xDE, /* 0xB8-0xBB */
+ 0x8A, 0x8C, 0x8A, 0x8D, 0xE6, 0xAA, 0x8A, 0x8E, /* 0xBC-0xBF */
+ 0x8A, 0x8F, 0x8A, 0x90, 0x8A, 0x91, 0x8A, 0x92, /* 0xC0-0xC3 */
+ 0x8A, 0x93, 0x8A, 0x94, 0xC4, 0xB7, 0x8A, 0x95, /* 0xC4-0xC7 */
+ 0x8A, 0x96, 0x8A, 0x97, 0xE6, 0xA2, 0xCA, 0xBC, /* 0xC8-0xCB */
+ 0x8A, 0x98, 0x8A, 0x99, 0x8A, 0x9A, 0x8A, 0x9B, /* 0xCC-0xCF */
+ 0xBD, 0xE3, 0xB9, 0xC3, 0xE6, 0xA6, 0xD0, 0xD5, /* 0xD0-0xD3 */
+ 0xCE, 0xAF, 0x8A, 0x9C, 0x8A, 0x9D, 0xE6, 0xA9, /* 0xD4-0xD7 */
+ 0xE6, 0xB0, 0x8A, 0x9E, 0xD2, 0xA6, 0x8A, 0x9F, /* 0xD8-0xDB */
+ 0xBD, 0xAA, 0xE6, 0xAD, 0x8A, 0xA0, 0x8A, 0xA1, /* 0xDC-0xDF */
+ 0x8A, 0xA2, 0x8A, 0xA3, 0x8A, 0xA4, 0xE6, 0xAF, /* 0xE0-0xE3 */
+ 0x8A, 0xA5, 0xC0, 0xD1, 0x8A, 0xA6, 0x8A, 0xA7, /* 0xE4-0xE7 */
+ 0xD2, 0xCC, 0x8A, 0xA8, 0x8A, 0xA9, 0x8A, 0xAA, /* 0xE8-0xEB */
+ 0xBC, 0xA7, 0x8A, 0xAB, 0x8A, 0xAC, 0x8A, 0xAD, /* 0xEC-0xEF */
+ 0x8A, 0xAE, 0x8A, 0xAF, 0x8A, 0xB0, 0x8A, 0xB1, /* 0xF0-0xF3 */
+ 0x8A, 0xB2, 0x8A, 0xB3, 0x8A, 0xB4, 0x8A, 0xB5, /* 0xF4-0xF7 */
+ 0x8A, 0xB6, 0xE6, 0xB1, 0x8A, 0xB7, 0xD2, 0xF6, /* 0xF8-0xFB */
+ 0x8A, 0xB8, 0x8A, 0xB9, 0x8A, 0xBA, 0xD7, 0xCB, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_5A[512] = {
+ 0x8A, 0xBB, 0xCD, 0xFE, 0x8A, 0xBC, 0xCD, 0xDE, /* 0x00-0x03 */
+ 0xC2, 0xA6, 0xE6, 0xAB, 0xE6, 0xAC, 0xBD, 0xBF, /* 0x04-0x07 */
+ 0xE6, 0xAE, 0xE6, 0xB3, 0x8A, 0xBD, 0x8A, 0xBE, /* 0x08-0x0B */
+ 0xE6, 0xB2, 0x8A, 0xBF, 0x8A, 0xC0, 0x8A, 0xC1, /* 0x0C-0x0F */
+ 0x8A, 0xC2, 0xE6, 0xB6, 0x8A, 0xC3, 0xE6, 0xB8, /* 0x10-0x13 */
+ 0x8A, 0xC4, 0x8A, 0xC5, 0x8A, 0xC6, 0x8A, 0xC7, /* 0x14-0x17 */
+ 0xC4, 0xEF, 0x8A, 0xC8, 0x8A, 0xC9, 0x8A, 0xCA, /* 0x18-0x1B */
+ 0xC4, 0xC8, 0x8A, 0xCB, 0x8A, 0xCC, 0xBE, 0xEA, /* 0x1C-0x1F */
+ 0xC9, 0xEF, 0x8A, 0xCD, 0x8A, 0xCE, 0xE6, 0xB7, /* 0x20-0x23 */
+ 0x8A, 0xCF, 0xB6, 0xF0, 0x8A, 0xD0, 0x8A, 0xD1, /* 0x24-0x27 */
+ 0x8A, 0xD2, 0xC3, 0xE4, 0x8A, 0xD3, 0x8A, 0xD4, /* 0x28-0x2B */
+ 0x8A, 0xD5, 0x8A, 0xD6, 0x8A, 0xD7, 0x8A, 0xD8, /* 0x2C-0x2F */
+ 0x8A, 0xD9, 0xD3, 0xE9, 0xE6, 0xB4, 0x8A, 0xDA, /* 0x30-0x33 */
+ 0xE6, 0xB5, 0x8A, 0xDB, 0xC8, 0xA2, 0x8A, 0xDC, /* 0x34-0x37 */
+ 0x8A, 0xDD, 0x8A, 0xDE, 0x8A, 0xDF, 0x8A, 0xE0, /* 0x38-0x3B */
+ 0xE6, 0xBD, 0x8A, 0xE1, 0x8A, 0xE2, 0x8A, 0xE3, /* 0x3C-0x3F */
+ 0xE6, 0xB9, 0x8A, 0xE4, 0x8A, 0xE5, 0x8A, 0xE6, /* 0x40-0x43 */
+ 0x8A, 0xE7, 0x8A, 0xE8, 0xC6, 0xC5, 0x8A, 0xE9, /* 0x44-0x47 */
+ 0x8A, 0xEA, 0xCD, 0xF1, 0xE6, 0xBB, 0x8A, 0xEB, /* 0x48-0x4B */
+ 0x8A, 0xEC, 0x8A, 0xED, 0x8A, 0xEE, 0x8A, 0xEF, /* 0x4C-0x4F */
+ 0x8A, 0xF0, 0x8A, 0xF1, 0x8A, 0xF2, 0x8A, 0xF3, /* 0x50-0x53 */
+ 0x8A, 0xF4, 0xE6, 0xBC, 0x8A, 0xF5, 0x8A, 0xF6, /* 0x54-0x57 */
+ 0x8A, 0xF7, 0x8A, 0xF8, 0xBB, 0xE9, 0x8A, 0xF9, /* 0x58-0x5B */
+ 0x8A, 0xFA, 0x8A, 0xFB, 0x8A, 0xFC, 0x8A, 0xFD, /* 0x5C-0x5F */
+ 0x8A, 0xFE, 0x8B, 0x40, 0xE6, 0xBE, 0x8B, 0x41, /* 0x60-0x63 */
+ 0x8B, 0x42, 0x8B, 0x43, 0x8B, 0x44, 0xE6, 0xBA, /* 0x64-0x67 */
+ 0x8B, 0x45, 0x8B, 0x46, 0xC0, 0xB7, 0x8B, 0x47, /* 0x68-0x6B */
+ 0x8B, 0x48, 0x8B, 0x49, 0x8B, 0x4A, 0x8B, 0x4B, /* 0x6C-0x6F */
+ 0x8B, 0x4C, 0x8B, 0x4D, 0x8B, 0x4E, 0x8B, 0x4F, /* 0x70-0x73 */
+ 0xD3, 0xA4, 0xE6, 0xBF, 0xC9, 0xF4, 0xE6, 0xC3, /* 0x74-0x77 */
+ 0x8B, 0x50, 0x8B, 0x51, 0xE6, 0xC4, 0x8B, 0x52, /* 0x78-0x7B */
+ 0x8B, 0x53, 0x8B, 0x54, 0x8B, 0x55, 0xD0, 0xF6, /* 0x7C-0x7F */
+
+ 0x8B, 0x56, 0x8B, 0x57, 0x8B, 0x58, 0x8B, 0x59, /* 0x80-0x83 */
+ 0x8B, 0x5A, 0x8B, 0x5B, 0x8B, 0x5C, 0x8B, 0x5D, /* 0x84-0x87 */
+ 0x8B, 0x5E, 0x8B, 0x5F, 0x8B, 0x60, 0x8B, 0x61, /* 0x88-0x8B */
+ 0x8B, 0x62, 0x8B, 0x63, 0x8B, 0x64, 0x8B, 0x65, /* 0x8C-0x8F */
+ 0x8B, 0x66, 0x8B, 0x67, 0xC3, 0xBD, 0x8B, 0x68, /* 0x90-0x93 */
+ 0x8B, 0x69, 0x8B, 0x6A, 0x8B, 0x6B, 0x8B, 0x6C, /* 0x94-0x97 */
+ 0x8B, 0x6D, 0x8B, 0x6E, 0xC3, 0xC4, 0xE6, 0xC2, /* 0x98-0x9B */
+ 0x8B, 0x6F, 0x8B, 0x70, 0x8B, 0x71, 0x8B, 0x72, /* 0x9C-0x9F */
+ 0x8B, 0x73, 0x8B, 0x74, 0x8B, 0x75, 0x8B, 0x76, /* 0xA0-0xA3 */
+ 0x8B, 0x77, 0x8B, 0x78, 0x8B, 0x79, 0x8B, 0x7A, /* 0xA4-0xA7 */
+ 0x8B, 0x7B, 0x8B, 0x7C, 0xE6, 0xC1, 0x8B, 0x7D, /* 0xA8-0xAB */
+ 0x8B, 0x7E, 0x8B, 0x80, 0x8B, 0x81, 0x8B, 0x82, /* 0xAC-0xAF */
+ 0x8B, 0x83, 0x8B, 0x84, 0xE6, 0xC7, 0xCF, 0xB1, /* 0xB0-0xB3 */
+ 0x8B, 0x85, 0xEB, 0xF4, 0x8B, 0x86, 0x8B, 0x87, /* 0xB4-0xB7 */
+ 0xE6, 0xCA, 0x8B, 0x88, 0x8B, 0x89, 0x8B, 0x8A, /* 0xB8-0xBB */
+ 0x8B, 0x8B, 0x8B, 0x8C, 0xE6, 0xC5, 0x8B, 0x8D, /* 0xBC-0xBF */
+ 0x8B, 0x8E, 0xBC, 0xDE, 0xC9, 0xA9, 0x8B, 0x8F, /* 0xC0-0xC3 */
+ 0x8B, 0x90, 0x8B, 0x91, 0x8B, 0x92, 0x8B, 0x93, /* 0xC4-0xC7 */
+ 0x8B, 0x94, 0xBC, 0xB5, 0x8B, 0x95, 0x8B, 0x96, /* 0xC8-0xCB */
+ 0xCF, 0xD3, 0x8B, 0x97, 0x8B, 0x98, 0x8B, 0x99, /* 0xCC-0xCF */
+ 0x8B, 0x9A, 0x8B, 0x9B, 0xE6, 0xC8, 0x8B, 0x9C, /* 0xD0-0xD3 */
+ 0xE6, 0xC9, 0x8B, 0x9D, 0xE6, 0xCE, 0x8B, 0x9E, /* 0xD4-0xD7 */
+ 0xE6, 0xD0, 0x8B, 0x9F, 0x8B, 0xA0, 0x8B, 0xA1, /* 0xD8-0xDB */
+ 0xE6, 0xD1, 0x8B, 0xA2, 0x8B, 0xA3, 0x8B, 0xA4, /* 0xDC-0xDF */
+ 0xE6, 0xCB, 0xB5, 0xD5, 0x8B, 0xA5, 0xE6, 0xCC, /* 0xE0-0xE3 */
+ 0x8B, 0xA6, 0x8B, 0xA7, 0xE6, 0xCF, 0x8B, 0xA8, /* 0xE4-0xE7 */
+ 0x8B, 0xA9, 0xC4, 0xDB, 0x8B, 0xAA, 0xE6, 0xC6, /* 0xE8-0xEB */
+ 0x8B, 0xAB, 0x8B, 0xAC, 0x8B, 0xAD, 0x8B, 0xAE, /* 0xEC-0xEF */
+ 0x8B, 0xAF, 0xE6, 0xCD, 0x8B, 0xB0, 0x8B, 0xB1, /* 0xF0-0xF3 */
+ 0x8B, 0xB2, 0x8B, 0xB3, 0x8B, 0xB4, 0x8B, 0xB5, /* 0xF4-0xF7 */
+ 0x8B, 0xB6, 0x8B, 0xB7, 0x8B, 0xB8, 0x8B, 0xB9, /* 0xF8-0xFB */
+ 0x8B, 0xBA, 0x8B, 0xBB, 0x8B, 0xBC, 0x8B, 0xBD, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_5B[512] = {
+ 0x8B, 0xBE, 0x8B, 0xBF, 0x8B, 0xC0, 0x8B, 0xC1, /* 0x00-0x03 */
+ 0x8B, 0xC2, 0x8B, 0xC3, 0x8B, 0xC4, 0x8B, 0xC5, /* 0x04-0x07 */
+ 0x8B, 0xC6, 0xE6, 0xD2, 0x8B, 0xC7, 0x8B, 0xC8, /* 0x08-0x0B */
+ 0x8B, 0xC9, 0x8B, 0xCA, 0x8B, 0xCB, 0x8B, 0xCC, /* 0x0C-0x0F */
+ 0x8B, 0xCD, 0x8B, 0xCE, 0x8B, 0xCF, 0x8B, 0xD0, /* 0x10-0x13 */
+ 0x8B, 0xD1, 0x8B, 0xD2, 0xE6, 0xD4, 0xE6, 0xD3, /* 0x14-0x17 */
+ 0x8B, 0xD3, 0x8B, 0xD4, 0x8B, 0xD5, 0x8B, 0xD6, /* 0x18-0x1B */
+ 0x8B, 0xD7, 0x8B, 0xD8, 0x8B, 0xD9, 0x8B, 0xDA, /* 0x1C-0x1F */
+ 0x8B, 0xDB, 0x8B, 0xDC, 0x8B, 0xDD, 0x8B, 0xDE, /* 0x20-0x23 */
+ 0x8B, 0xDF, 0x8B, 0xE0, 0x8B, 0xE1, 0x8B, 0xE2, /* 0x24-0x27 */
+ 0x8B, 0xE3, 0x8B, 0xE4, 0x8B, 0xE5, 0x8B, 0xE6, /* 0x28-0x2B */
+ 0x8B, 0xE7, 0x8B, 0xE8, 0x8B, 0xE9, 0x8B, 0xEA, /* 0x2C-0x2F */
+ 0x8B, 0xEB, 0x8B, 0xEC, 0xE6, 0xD5, 0x8B, 0xED, /* 0x30-0x33 */
+ 0xD9, 0xF8, 0x8B, 0xEE, 0x8B, 0xEF, 0xE6, 0xD6, /* 0x34-0x37 */
+ 0x8B, 0xF0, 0x8B, 0xF1, 0x8B, 0xF2, 0x8B, 0xF3, /* 0x38-0x3B */
+ 0x8B, 0xF4, 0x8B, 0xF5, 0x8B, 0xF6, 0x8B, 0xF7, /* 0x3C-0x3F */
+ 0xE6, 0xD7, 0x8B, 0xF8, 0x8B, 0xF9, 0x8B, 0xFA, /* 0x40-0x43 */
+ 0x8B, 0xFB, 0x8B, 0xFC, 0x8B, 0xFD, 0x8B, 0xFE, /* 0x44-0x47 */
+ 0x8C, 0x40, 0x8C, 0x41, 0x8C, 0x42, 0x8C, 0x43, /* 0x48-0x4B */
+ 0x8C, 0x44, 0x8C, 0x45, 0x8C, 0x46, 0x8C, 0x47, /* 0x4C-0x4F */
+ 0xD7, 0xD3, 0xE6, 0xDD, 0x8C, 0x48, 0xE6, 0xDE, /* 0x50-0x53 */
+ 0xBF, 0xD7, 0xD4, 0xD0, 0x8C, 0x49, 0xD7, 0xD6, /* 0x54-0x57 */
+ 0xB4, 0xE6, 0xCB, 0xEF, 0xE6, 0xDA, 0xD8, 0xC3, /* 0x58-0x5B */
+ 0xD7, 0xCE, 0xD0, 0xA2, 0x8C, 0x4A, 0xC3, 0xCF, /* 0x5C-0x5F */
+ 0x8C, 0x4B, 0x8C, 0x4C, 0xE6, 0xDF, 0xBC, 0xBE, /* 0x60-0x63 */
+ 0xB9, 0xC2, 0xE6, 0xDB, 0xD1, 0xA7, 0x8C, 0x4D, /* 0x64-0x67 */
+ 0x8C, 0x4E, 0xBA, 0xA2, 0xC2, 0xCF, 0x8C, 0x4F, /* 0x68-0x6B */
+ 0xD8, 0xAB, 0x8C, 0x50, 0x8C, 0x51, 0x8C, 0x52, /* 0x6C-0x6F */
+ 0xCA, 0xEB, 0xE5, 0xEE, 0x8C, 0x53, 0xE6, 0xDC, /* 0x70-0x73 */
+ 0x8C, 0x54, 0xB7, 0xF5, 0x8C, 0x55, 0x8C, 0x56, /* 0x74-0x77 */
+ 0x8C, 0x57, 0x8C, 0x58, 0xC8, 0xE6, 0x8C, 0x59, /* 0x78-0x7B */
+ 0x8C, 0x5A, 0xC4, 0xF5, 0x8C, 0x5B, 0x8C, 0x5C, /* 0x7C-0x7F */
+
+ 0xE5, 0xB2, 0xC4, 0xFE, 0x8C, 0x5D, 0xCB, 0xFC, /* 0x80-0x83 */
+ 0xE5, 0xB3, 0xD5, 0xAC, 0x8C, 0x5E, 0xD3, 0xEE, /* 0x84-0x87 */
+ 0xCA, 0xD8, 0xB0, 0xB2, 0x8C, 0x5F, 0xCB, 0xCE, /* 0x88-0x8B */
+ 0xCD, 0xEA, 0x8C, 0x60, 0x8C, 0x61, 0xBA, 0xEA, /* 0x8C-0x8F */
+ 0x8C, 0x62, 0x8C, 0x63, 0x8C, 0x64, 0xE5, 0xB5, /* 0x90-0x93 */
+ 0x8C, 0x65, 0xE5, 0xB4, 0x8C, 0x66, 0xD7, 0xDA, /* 0x94-0x97 */
+ 0xB9, 0xD9, 0xD6, 0xE6, 0xB6, 0xA8, 0xCD, 0xF0, /* 0x98-0x9B */
+ 0xD2, 0xCB, 0xB1, 0xA6, 0xCA, 0xB5, 0x8C, 0x67, /* 0x9C-0x9F */
+ 0xB3, 0xE8, 0xC9, 0xF3, 0xBF, 0xCD, 0xD0, 0xFB, /* 0xA0-0xA3 */
+ 0xCA, 0xD2, 0xE5, 0xB6, 0xBB, 0xC2, 0x8C, 0x68, /* 0xA4-0xA7 */
+ 0x8C, 0x69, 0x8C, 0x6A, 0xCF, 0xDC, 0xB9, 0xAC, /* 0xA8-0xAB */
+ 0x8C, 0x6B, 0x8C, 0x6C, 0x8C, 0x6D, 0x8C, 0x6E, /* 0xAC-0xAF */
+ 0xD4, 0xD7, 0x8C, 0x6F, 0x8C, 0x70, 0xBA, 0xA6, /* 0xB0-0xB3 */
+ 0xD1, 0xE7, 0xCF, 0xFC, 0xBC, 0xD2, 0x8C, 0x71, /* 0xB4-0xB7 */
+ 0xE5, 0xB7, 0xC8, 0xDD, 0x8C, 0x72, 0x8C, 0x73, /* 0xB8-0xBB */
+ 0x8C, 0x74, 0xBF, 0xED, 0xB1, 0xF6, 0xCB, 0xDE, /* 0xBC-0xBF */
+ 0x8C, 0x75, 0x8C, 0x76, 0xBC, 0xC5, 0x8C, 0x77, /* 0xC0-0xC3 */
+ 0xBC, 0xC4, 0xD2, 0xFA, 0xC3, 0xDC, 0xBF, 0xDC, /* 0xC4-0xC7 */
+ 0x8C, 0x78, 0x8C, 0x79, 0x8C, 0x7A, 0x8C, 0x7B, /* 0xC8-0xCB */
+ 0xB8, 0xBB, 0x8C, 0x7C, 0x8C, 0x7D, 0x8C, 0x7E, /* 0xCC-0xCF */
+ 0xC3, 0xC2, 0x8C, 0x80, 0xBA, 0xAE, 0xD4, 0xA2, /* 0xD0-0xD3 */
+ 0x8C, 0x81, 0x8C, 0x82, 0x8C, 0x83, 0x8C, 0x84, /* 0xD4-0xD7 */
+ 0x8C, 0x85, 0x8C, 0x86, 0x8C, 0x87, 0x8C, 0x88, /* 0xD8-0xDB */
+ 0x8C, 0x89, 0xC7, 0xDE, 0xC4, 0xAF, 0xB2, 0xEC, /* 0xDC-0xDF */
+ 0x8C, 0x8A, 0xB9, 0xD1, 0x8C, 0x8B, 0x8C, 0x8C, /* 0xE0-0xE3 */
+ 0xE5, 0xBB, 0xC1, 0xC8, 0x8C, 0x8D, 0x8C, 0x8E, /* 0xE4-0xE7 */
+ 0xD5, 0xAF, 0x8C, 0x8F, 0x8C, 0x90, 0x8C, 0x91, /* 0xE8-0xEB */
+ 0x8C, 0x92, 0x8C, 0x93, 0xE5, 0xBC, 0x8C, 0x94, /* 0xEC-0xEF */
+ 0xE5, 0xBE, 0x8C, 0x95, 0x8C, 0x96, 0x8C, 0x97, /* 0xF0-0xF3 */
+ 0x8C, 0x98, 0x8C, 0x99, 0x8C, 0x9A, 0x8C, 0x9B, /* 0xF4-0xF7 */
+ 0xB4, 0xE7, 0xB6, 0xD4, 0xCB, 0xC2, 0xD1, 0xB0, /* 0xF8-0xFB */
+ 0xB5, 0xBC, 0x8C, 0x9C, 0x8C, 0x9D, 0xCA, 0xD9, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_5C[512] = {
+ 0x8C, 0x9E, 0xB7, 0xE2, 0x8C, 0x9F, 0x8C, 0xA0, /* 0x00-0x03 */
+ 0xC9, 0xE4, 0x8C, 0xA1, 0xBD, 0xAB, 0x8C, 0xA2, /* 0x04-0x07 */
+ 0x8C, 0xA3, 0xCE, 0xBE, 0xD7, 0xF0, 0x8C, 0xA4, /* 0x08-0x0B */
+ 0x8C, 0xA5, 0x8C, 0xA6, 0x8C, 0xA7, 0xD0, 0xA1, /* 0x0C-0x0F */
+ 0x8C, 0xA8, 0xC9, 0xD9, 0x8C, 0xA9, 0x8C, 0xAA, /* 0x10-0x13 */
+ 0xB6, 0xFB, 0xE6, 0xD8, 0xBC, 0xE2, 0x8C, 0xAB, /* 0x14-0x17 */
+ 0xB3, 0xBE, 0x8C, 0xAC, 0xC9, 0xD0, 0x8C, 0xAD, /* 0x18-0x1B */
+ 0xE6, 0xD9, 0xB3, 0xA2, 0x8C, 0xAE, 0x8C, 0xAF, /* 0x1C-0x1F */
+ 0x8C, 0xB0, 0x8C, 0xB1, 0xDE, 0xCC, 0x8C, 0xB2, /* 0x20-0x23 */
+ 0xD3, 0xC8, 0xDE, 0xCD, 0x8C, 0xB3, 0xD2, 0xA2, /* 0x24-0x27 */
+ 0x8C, 0xB4, 0x8C, 0xB5, 0x8C, 0xB6, 0x8C, 0xB7, /* 0x28-0x2B */
+ 0xDE, 0xCE, 0x8C, 0xB8, 0x8C, 0xB9, 0x8C, 0xBA, /* 0x2C-0x2F */
+ 0x8C, 0xBB, 0xBE, 0xCD, 0x8C, 0xBC, 0x8C, 0xBD, /* 0x30-0x33 */
+ 0xDE, 0xCF, 0x8C, 0xBE, 0x8C, 0xBF, 0x8C, 0xC0, /* 0x34-0x37 */
+ 0xCA, 0xAC, 0xD2, 0xFC, 0xB3, 0xDF, 0xE5, 0xEA, /* 0x38-0x3B */
+ 0xC4, 0xE1, 0xBE, 0xA1, 0xCE, 0xB2, 0xC4, 0xF2, /* 0x3C-0x3F */
+ 0xBE, 0xD6, 0xC6, 0xA8, 0xB2, 0xE3, 0x8C, 0xC1, /* 0x40-0x43 */
+ 0x8C, 0xC2, 0xBE, 0xD3, 0x8C, 0xC3, 0x8C, 0xC4, /* 0x44-0x47 */
+ 0xC7, 0xFC, 0xCC, 0xEB, 0xBD, 0xEC, 0xCE, 0xDD, /* 0x48-0x4B */
+ 0x8C, 0xC5, 0x8C, 0xC6, 0xCA, 0xBA, 0xC6, 0xC1, /* 0x4C-0x4F */
+ 0xE5, 0xEC, 0xD0, 0xBC, 0x8C, 0xC7, 0x8C, 0xC8, /* 0x50-0x53 */
+ 0x8C, 0xC9, 0xD5, 0xB9, 0x8C, 0xCA, 0x8C, 0xCB, /* 0x54-0x57 */
+ 0x8C, 0xCC, 0xE5, 0xED, 0x8C, 0xCD, 0x8C, 0xCE, /* 0x58-0x5B */
+ 0x8C, 0xCF, 0x8C, 0xD0, 0xCA, 0xF4, 0x8C, 0xD1, /* 0x5C-0x5F */
+ 0xCD, 0xC0, 0xC2, 0xC5, 0x8C, 0xD2, 0xE5, 0xEF, /* 0x60-0x63 */
+ 0x8C, 0xD3, 0xC2, 0xC4, 0xE5, 0xF0, 0x8C, 0xD4, /* 0x64-0x67 */
+ 0x8C, 0xD5, 0x8C, 0xD6, 0x8C, 0xD7, 0x8C, 0xD8, /* 0x68-0x6B */
+ 0x8C, 0xD9, 0x8C, 0xDA, 0xE5, 0xF8, 0xCD, 0xCD, /* 0x6C-0x6F */
+ 0x8C, 0xDB, 0xC9, 0xBD, 0x8C, 0xDC, 0x8C, 0xDD, /* 0x70-0x73 */
+ 0x8C, 0xDE, 0x8C, 0xDF, 0x8C, 0xE0, 0x8C, 0xE1, /* 0x74-0x77 */
+ 0x8C, 0xE2, 0xD2, 0xD9, 0xE1, 0xA8, 0x8C, 0xE3, /* 0x78-0x7B */
+ 0x8C, 0xE4, 0x8C, 0xE5, 0x8C, 0xE6, 0xD3, 0xEC, /* 0x7C-0x7F */
+
+ 0x8C, 0xE7, 0xCB, 0xEA, 0xC6, 0xF1, 0x8C, 0xE8, /* 0x80-0x83 */
+ 0x8C, 0xE9, 0x8C, 0xEA, 0x8C, 0xEB, 0x8C, 0xEC, /* 0x84-0x87 */
+ 0xE1, 0xAC, 0x8C, 0xED, 0x8C, 0xEE, 0x8C, 0xEF, /* 0x88-0x8B */
+ 0xE1, 0xA7, 0xE1, 0xA9, 0x8C, 0xF0, 0x8C, 0xF1, /* 0x8C-0x8F */
+ 0xE1, 0xAA, 0xE1, 0xAF, 0x8C, 0xF2, 0x8C, 0xF3, /* 0x90-0x93 */
+ 0xB2, 0xED, 0x8C, 0xF4, 0xE1, 0xAB, 0xB8, 0xDA, /* 0x94-0x97 */
+ 0xE1, 0xAD, 0xE1, 0xAE, 0xE1, 0xB0, 0xB5, 0xBA, /* 0x98-0x9B */
+ 0xE1, 0xB1, 0x8C, 0xF5, 0x8C, 0xF6, 0x8C, 0xF7, /* 0x9C-0x9F */
+ 0x8C, 0xF8, 0x8C, 0xF9, 0xE1, 0xB3, 0xE1, 0xB8, /* 0xA0-0xA3 */
+ 0x8C, 0xFA, 0x8C, 0xFB, 0x8C, 0xFC, 0x8C, 0xFD, /* 0xA4-0xA7 */
+ 0x8C, 0xFE, 0xD1, 0xD2, 0x8D, 0x40, 0xE1, 0xB6, /* 0xA8-0xAB */
+ 0xE1, 0xB5, 0xC1, 0xEB, 0x8D, 0x41, 0x8D, 0x42, /* 0xAC-0xAF */
+ 0x8D, 0x43, 0xE1, 0xB7, 0x8D, 0x44, 0xD4, 0xC0, /* 0xB0-0xB3 */
+ 0x8D, 0x45, 0xE1, 0xB2, 0x8D, 0x46, 0xE1, 0xBA, /* 0xB4-0xB7 */
+ 0xB0, 0xB6, 0x8D, 0x47, 0x8D, 0x48, 0x8D, 0x49, /* 0xB8-0xBB */
+ 0x8D, 0x4A, 0xE1, 0xB4, 0x8D, 0x4B, 0xBF, 0xF9, /* 0xBC-0xBF */
+ 0x8D, 0x4C, 0xE1, 0xB9, 0x8D, 0x4D, 0x8D, 0x4E, /* 0xC0-0xC3 */
+ 0xE1, 0xBB, 0x8D, 0x4F, 0x8D, 0x50, 0x8D, 0x51, /* 0xC4-0xC7 */
+ 0x8D, 0x52, 0x8D, 0x53, 0x8D, 0x54, 0xE1, 0xBE, /* 0xC8-0xCB */
+ 0x8D, 0x55, 0x8D, 0x56, 0x8D, 0x57, 0x8D, 0x58, /* 0xCC-0xCF */
+ 0x8D, 0x59, 0x8D, 0x5A, 0xE1, 0xBC, 0x8D, 0x5B, /* 0xD0-0xD3 */
+ 0x8D, 0x5C, 0x8D, 0x5D, 0x8D, 0x5E, 0x8D, 0x5F, /* 0xD4-0xD7 */
+ 0x8D, 0x60, 0xD6, 0xC5, 0x8D, 0x61, 0x8D, 0x62, /* 0xD8-0xDB */
+ 0x8D, 0x63, 0x8D, 0x64, 0x8D, 0x65, 0x8D, 0x66, /* 0xDC-0xDF */
+ 0x8D, 0x67, 0xCF, 0xBF, 0x8D, 0x68, 0x8D, 0x69, /* 0xE0-0xE3 */
+ 0xE1, 0xBD, 0xE1, 0xBF, 0xC2, 0xCD, 0x8D, 0x6A, /* 0xE4-0xE7 */
+ 0xB6, 0xEB, 0x8D, 0x6B, 0xD3, 0xF8, 0x8D, 0x6C, /* 0xE8-0xEB */
+ 0x8D, 0x6D, 0xC7, 0xCD, 0x8D, 0x6E, 0x8D, 0x6F, /* 0xEC-0xEF */
+ 0xB7, 0xE5, 0x8D, 0x70, 0x8D, 0x71, 0x8D, 0x72, /* 0xF0-0xF3 */
+ 0x8D, 0x73, 0x8D, 0x74, 0x8D, 0x75, 0x8D, 0x76, /* 0xF4-0xF7 */
+ 0x8D, 0x77, 0x8D, 0x78, 0x8D, 0x79, 0xBE, 0xFE, /* 0xF8-0xFB */
+ 0x8D, 0x7A, 0x8D, 0x7B, 0x8D, 0x7C, 0x8D, 0x7D, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_5D[512] = {
+ 0x8D, 0x7E, 0x8D, 0x80, 0xE1, 0xC0, 0xE1, 0xC1, /* 0x00-0x03 */
+ 0x8D, 0x81, 0x8D, 0x82, 0xE1, 0xC7, 0xB3, 0xE7, /* 0x04-0x07 */
+ 0x8D, 0x83, 0x8D, 0x84, 0x8D, 0x85, 0x8D, 0x86, /* 0x08-0x0B */
+ 0x8D, 0x87, 0x8D, 0x88, 0xC6, 0xE9, 0x8D, 0x89, /* 0x0C-0x0F */
+ 0x8D, 0x8A, 0x8D, 0x8B, 0x8D, 0x8C, 0x8D, 0x8D, /* 0x10-0x13 */
+ 0xB4, 0xDE, 0x8D, 0x8E, 0xD1, 0xC2, 0x8D, 0x8F, /* 0x14-0x17 */
+ 0x8D, 0x90, 0x8D, 0x91, 0x8D, 0x92, 0xE1, 0xC8, /* 0x18-0x1B */
+ 0x8D, 0x93, 0x8D, 0x94, 0xE1, 0xC6, 0x8D, 0x95, /* 0x1C-0x1F */
+ 0x8D, 0x96, 0x8D, 0x97, 0x8D, 0x98, 0x8D, 0x99, /* 0x20-0x23 */
+ 0xE1, 0xC5, 0x8D, 0x9A, 0xE1, 0xC3, 0xE1, 0xC2, /* 0x24-0x27 */
+ 0x8D, 0x9B, 0xB1, 0xC0, 0x8D, 0x9C, 0x8D, 0x9D, /* 0x28-0x2B */
+ 0x8D, 0x9E, 0xD5, 0xB8, 0xE1, 0xC4, 0x8D, 0x9F, /* 0x2C-0x2F */
+ 0x8D, 0xA0, 0x8D, 0xA1, 0x8D, 0xA2, 0x8D, 0xA3, /* 0x30-0x33 */
+ 0xE1, 0xCB, 0x8D, 0xA4, 0x8D, 0xA5, 0x8D, 0xA6, /* 0x34-0x37 */
+ 0x8D, 0xA7, 0x8D, 0xA8, 0x8D, 0xA9, 0x8D, 0xAA, /* 0x38-0x3B */
+ 0x8D, 0xAB, 0xE1, 0xCC, 0xE1, 0xCA, 0x8D, 0xAC, /* 0x3C-0x3F */
+ 0x8D, 0xAD, 0x8D, 0xAE, 0x8D, 0xAF, 0x8D, 0xB0, /* 0x40-0x43 */
+ 0x8D, 0xB1, 0x8D, 0xB2, 0x8D, 0xB3, 0xEF, 0xFA, /* 0x44-0x47 */
+ 0x8D, 0xB4, 0x8D, 0xB5, 0xE1, 0xD3, 0xE1, 0xD2, /* 0x48-0x4B */
+ 0xC7, 0xB6, 0x8D, 0xB6, 0x8D, 0xB7, 0x8D, 0xB8, /* 0x4C-0x4F */
+ 0x8D, 0xB9, 0x8D, 0xBA, 0x8D, 0xBB, 0x8D, 0xBC, /* 0x50-0x53 */
+ 0x8D, 0xBD, 0x8D, 0xBE, 0x8D, 0xBF, 0x8D, 0xC0, /* 0x54-0x57 */
+ 0xE1, 0xC9, 0x8D, 0xC1, 0x8D, 0xC2, 0xE1, 0xCE, /* 0x58-0x5B */
+ 0x8D, 0xC3, 0xE1, 0xD0, 0x8D, 0xC4, 0x8D, 0xC5, /* 0x5C-0x5F */
+ 0x8D, 0xC6, 0x8D, 0xC7, 0x8D, 0xC8, 0x8D, 0xC9, /* 0x60-0x63 */
+ 0x8D, 0xCA, 0x8D, 0xCB, 0x8D, 0xCC, 0x8D, 0xCD, /* 0x64-0x67 */
+ 0x8D, 0xCE, 0xE1, 0xD4, 0x8D, 0xCF, 0xE1, 0xD1, /* 0x68-0x6B */
+ 0xE1, 0xCD, 0x8D, 0xD0, 0x8D, 0xD1, 0xE1, 0xCF, /* 0x6C-0x6F */
+ 0x8D, 0xD2, 0x8D, 0xD3, 0x8D, 0xD4, 0x8D, 0xD5, /* 0x70-0x73 */
+ 0xE1, 0xD5, 0x8D, 0xD6, 0x8D, 0xD7, 0x8D, 0xD8, /* 0x74-0x77 */
+ 0x8D, 0xD9, 0x8D, 0xDA, 0x8D, 0xDB, 0x8D, 0xDC, /* 0x78-0x7B */
+ 0x8D, 0xDD, 0x8D, 0xDE, 0x8D, 0xDF, 0x8D, 0xE0, /* 0x7C-0x7F */
+
+ 0x8D, 0xE1, 0x8D, 0xE2, 0xE1, 0xD6, 0x8D, 0xE3, /* 0x80-0x83 */
+ 0x8D, 0xE4, 0x8D, 0xE5, 0x8D, 0xE6, 0x8D, 0xE7, /* 0x84-0x87 */
+ 0x8D, 0xE8, 0x8D, 0xE9, 0x8D, 0xEA, 0x8D, 0xEB, /* 0x88-0x8B */
+ 0x8D, 0xEC, 0x8D, 0xED, 0x8D, 0xEE, 0x8D, 0xEF, /* 0x8C-0x8F */
+ 0x8D, 0xF0, 0x8D, 0xF1, 0x8D, 0xF2, 0x8D, 0xF3, /* 0x90-0x93 */
+ 0x8D, 0xF4, 0x8D, 0xF5, 0x8D, 0xF6, 0x8D, 0xF7, /* 0x94-0x97 */
+ 0x8D, 0xF8, 0xE1, 0xD7, 0x8D, 0xF9, 0x8D, 0xFA, /* 0x98-0x9B */
+ 0x8D, 0xFB, 0xE1, 0xD8, 0x8D, 0xFC, 0x8D, 0xFD, /* 0x9C-0x9F */
+ 0x8D, 0xFE, 0x8E, 0x40, 0x8E, 0x41, 0x8E, 0x42, /* 0xA0-0xA3 */
+ 0x8E, 0x43, 0x8E, 0x44, 0x8E, 0x45, 0x8E, 0x46, /* 0xA4-0xA7 */
+ 0x8E, 0x47, 0x8E, 0x48, 0x8E, 0x49, 0x8E, 0x4A, /* 0xA8-0xAB */
+ 0x8E, 0x4B, 0x8E, 0x4C, 0x8E, 0x4D, 0x8E, 0x4E, /* 0xAC-0xAF */
+ 0x8E, 0x4F, 0x8E, 0x50, 0x8E, 0x51, 0x8E, 0x52, /* 0xB0-0xB3 */
+ 0x8E, 0x53, 0x8E, 0x54, 0x8E, 0x55, 0xE1, 0xDA, /* 0xB4-0xB7 */
+ 0x8E, 0x56, 0x8E, 0x57, 0x8E, 0x58, 0x8E, 0x59, /* 0xB8-0xBB */
+ 0x8E, 0x5A, 0x8E, 0x5B, 0x8E, 0x5C, 0x8E, 0x5D, /* 0xBC-0xBF */
+ 0x8E, 0x5E, 0x8E, 0x5F, 0x8E, 0x60, 0x8E, 0x61, /* 0xC0-0xC3 */
+ 0x8E, 0x62, 0xE1, 0xDB, 0x8E, 0x63, 0x8E, 0x64, /* 0xC4-0xC7 */
+ 0x8E, 0x65, 0x8E, 0x66, 0x8E, 0x67, 0x8E, 0x68, /* 0xC8-0xCB */
+ 0x8E, 0x69, 0xCE, 0xA1, 0x8E, 0x6A, 0x8E, 0x6B, /* 0xCC-0xCF */
+ 0x8E, 0x6C, 0x8E, 0x6D, 0x8E, 0x6E, 0x8E, 0x6F, /* 0xD0-0xD3 */
+ 0x8E, 0x70, 0x8E, 0x71, 0x8E, 0x72, 0x8E, 0x73, /* 0xD4-0xD7 */
+ 0x8E, 0x74, 0x8E, 0x75, 0x8E, 0x76, 0xE7, 0xDD, /* 0xD8-0xDB */
+ 0x8E, 0x77, 0xB4, 0xA8, 0xD6, 0xDD, 0x8E, 0x78, /* 0xDC-0xDF */
+ 0x8E, 0x79, 0xD1, 0xB2, 0xB3, 0xB2, 0x8E, 0x7A, /* 0xE0-0xE3 */
+ 0x8E, 0x7B, 0xB9, 0xA4, 0xD7, 0xF3, 0xC7, 0xC9, /* 0xE4-0xE7 */
+ 0xBE, 0xDE, 0xB9, 0xAE, 0x8E, 0x7C, 0xCE, 0xD7, /* 0xE8-0xEB */
+ 0x8E, 0x7D, 0x8E, 0x7E, 0xB2, 0xEE, 0xDB, 0xCF, /* 0xEC-0xEF */
+ 0x8E, 0x80, 0xBC, 0xBA, 0xD2, 0xD1, 0xCB, 0xC8, /* 0xF0-0xF3 */
+ 0xB0, 0xCD, 0x8E, 0x81, 0x8E, 0x82, 0xCF, 0xEF, /* 0xF4-0xF7 */
+ 0x8E, 0x83, 0x8E, 0x84, 0x8E, 0x85, 0x8E, 0x86, /* 0xF8-0xFB */
+ 0x8E, 0x87, 0xD9, 0xE3, 0xBD, 0xED, 0x8E, 0x88, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_5E[512] = {
+ 0x8E, 0x89, 0xB1, 0xD2, 0xCA, 0xD0, 0xB2, 0xBC, /* 0x00-0x03 */
+ 0x8E, 0x8A, 0xCB, 0xA7, 0xB7, 0xAB, 0x8E, 0x8B, /* 0x04-0x07 */
+ 0xCA, 0xA6, 0x8E, 0x8C, 0x8E, 0x8D, 0x8E, 0x8E, /* 0x08-0x0B */
+ 0xCF, 0xA3, 0x8E, 0x8F, 0x8E, 0x90, 0xE0, 0xF8, /* 0x0C-0x0F */
+ 0xD5, 0xCA, 0xE0, 0xFB, 0x8E, 0x91, 0x8E, 0x92, /* 0x10-0x13 */
+ 0xE0, 0xFA, 0xC5, 0xC1, 0xCC, 0xFB, 0x8E, 0x93, /* 0x14-0x17 */
+ 0xC1, 0xB1, 0xE0, 0xF9, 0xD6, 0xE3, 0xB2, 0xAF, /* 0x18-0x1B */
+ 0xD6, 0xC4, 0xB5, 0xDB, 0x8E, 0x94, 0x8E, 0x95, /* 0x1C-0x1F */
+ 0x8E, 0x96, 0x8E, 0x97, 0x8E, 0x98, 0x8E, 0x99, /* 0x20-0x23 */
+ 0x8E, 0x9A, 0x8E, 0x9B, 0xB4, 0xF8, 0xD6, 0xA1, /* 0x24-0x27 */
+ 0x8E, 0x9C, 0x8E, 0x9D, 0x8E, 0x9E, 0x8E, 0x9F, /* 0x28-0x2B */
+ 0x8E, 0xA0, 0xCF, 0xAF, 0xB0, 0xEF, 0x8E, 0xA1, /* 0x2C-0x2F */
+ 0x8E, 0xA2, 0xE0, 0xFC, 0x8E, 0xA3, 0x8E, 0xA4, /* 0x30-0x33 */
+ 0x8E, 0xA5, 0x8E, 0xA6, 0x8E, 0xA7, 0xE1, 0xA1, /* 0x34-0x37 */
+ 0xB3, 0xA3, 0x8E, 0xA8, 0x8E, 0xA9, 0xE0, 0xFD, /* 0x38-0x3B */
+ 0xE0, 0xFE, 0xC3, 0xB1, 0x8E, 0xAA, 0x8E, 0xAB, /* 0x3C-0x3F */
+ 0x8E, 0xAC, 0x8E, 0xAD, 0xC3, 0xDD, 0x8E, 0xAE, /* 0x40-0x43 */
+ 0xE1, 0xA2, 0xB7, 0xF9, 0x8E, 0xAF, 0x8E, 0xB0, /* 0x44-0x47 */
+ 0x8E, 0xB1, 0x8E, 0xB2, 0x8E, 0xB3, 0x8E, 0xB4, /* 0x48-0x4B */
+ 0xBB, 0xCF, 0x8E, 0xB5, 0x8E, 0xB6, 0x8E, 0xB7, /* 0x4C-0x4F */
+ 0x8E, 0xB8, 0x8E, 0xB9, 0x8E, 0xBA, 0x8E, 0xBB, /* 0x50-0x53 */
+ 0xE1, 0xA3, 0xC4, 0xBB, 0x8E, 0xBC, 0x8E, 0xBD, /* 0x54-0x57 */
+ 0x8E, 0xBE, 0x8E, 0xBF, 0x8E, 0xC0, 0xE1, 0xA4, /* 0x58-0x5B */
+ 0x8E, 0xC1, 0x8E, 0xC2, 0xE1, 0xA5, 0x8E, 0xC3, /* 0x5C-0x5F */
+ 0x8E, 0xC4, 0xE1, 0xA6, 0xB4, 0xB1, 0x8E, 0xC5, /* 0x60-0x63 */
+ 0x8E, 0xC6, 0x8E, 0xC7, 0x8E, 0xC8, 0x8E, 0xC9, /* 0x64-0x67 */
+ 0x8E, 0xCA, 0x8E, 0xCB, 0x8E, 0xCC, 0x8E, 0xCD, /* 0x68-0x6B */
+ 0x8E, 0xCE, 0x8E, 0xCF, 0x8E, 0xD0, 0x8E, 0xD1, /* 0x6C-0x6F */
+ 0x8E, 0xD2, 0x8E, 0xD3, 0xB8, 0xC9, 0xC6, 0xBD, /* 0x70-0x73 */
+ 0xC4, 0xEA, 0x8E, 0xD4, 0xB2, 0xA2, 0x8E, 0xD5, /* 0x74-0x77 */
+ 0xD0, 0xD2, 0x8E, 0xD6, 0xE7, 0xDB, 0xBB, 0xC3, /* 0x78-0x7B */
+ 0xD3, 0xD7, 0xD3, 0xC4, 0x8E, 0xD7, 0xB9, 0xE3, /* 0x7C-0x7F */
+
+ 0xE2, 0xCF, 0x8E, 0xD8, 0x8E, 0xD9, 0x8E, 0xDA, /* 0x80-0x83 */
+ 0xD7, 0xAF, 0x8E, 0xDB, 0xC7, 0xEC, 0xB1, 0xD3, /* 0x84-0x87 */
+ 0x8E, 0xDC, 0x8E, 0xDD, 0xB4, 0xB2, 0xE2, 0xD1, /* 0x88-0x8B */
+ 0x8E, 0xDE, 0x8E, 0xDF, 0x8E, 0xE0, 0xD0, 0xF2, /* 0x8C-0x8F */
+ 0xC2, 0xAE, 0xE2, 0xD0, 0x8E, 0xE1, 0xBF, 0xE2, /* 0x90-0x93 */
+ 0xD3, 0xA6, 0xB5, 0xD7, 0xE2, 0xD2, 0xB5, 0xEA, /* 0x94-0x97 */
+ 0x8E, 0xE2, 0xC3, 0xED, 0xB8, 0xFD, 0x8E, 0xE3, /* 0x98-0x9B */
+ 0xB8, 0xAE, 0x8E, 0xE4, 0xC5, 0xD3, 0xB7, 0xCF, /* 0x9C-0x9F */
+ 0xE2, 0xD4, 0x8E, 0xE5, 0x8E, 0xE6, 0x8E, 0xE7, /* 0xA0-0xA3 */
+ 0x8E, 0xE8, 0xE2, 0xD3, 0xB6, 0xC8, 0xD7, 0xF9, /* 0xA4-0xA7 */
+ 0x8E, 0xE9, 0x8E, 0xEA, 0x8E, 0xEB, 0x8E, 0xEC, /* 0xA8-0xAB */
+ 0x8E, 0xED, 0xCD, 0xA5, 0x8E, 0xEE, 0x8E, 0xEF, /* 0xAC-0xAF */
+ 0x8E, 0xF0, 0x8E, 0xF1, 0x8E, 0xF2, 0xE2, 0xD8, /* 0xB0-0xB3 */
+ 0x8E, 0xF3, 0xE2, 0xD6, 0xCA, 0xFC, 0xBF, 0xB5, /* 0xB4-0xB7 */
+ 0xD3, 0xB9, 0xE2, 0xD5, 0x8E, 0xF4, 0x8E, 0xF5, /* 0xB8-0xBB */
+ 0x8E, 0xF6, 0x8E, 0xF7, 0xE2, 0xD7, 0x8E, 0xF8, /* 0xBC-0xBF */
+ 0x8E, 0xF9, 0x8E, 0xFA, 0x8E, 0xFB, 0x8E, 0xFC, /* 0xC0-0xC3 */
+ 0x8E, 0xFD, 0x8E, 0xFE, 0x8F, 0x40, 0x8F, 0x41, /* 0xC4-0xC7 */
+ 0x8F, 0x42, 0xC1, 0xAE, 0xC0, 0xC8, 0x8F, 0x43, /* 0xC8-0xCB */
+ 0x8F, 0x44, 0x8F, 0x45, 0x8F, 0x46, 0x8F, 0x47, /* 0xCC-0xCF */
+ 0x8F, 0x48, 0xE2, 0xDB, 0xE2, 0xDA, 0xC0, 0xAA, /* 0xD0-0xD3 */
+ 0x8F, 0x49, 0x8F, 0x4A, 0xC1, 0xCE, 0x8F, 0x4B, /* 0xD4-0xD7 */
+ 0x8F, 0x4C, 0x8F, 0x4D, 0x8F, 0x4E, 0xE2, 0xDC, /* 0xD8-0xDB */
+ 0x8F, 0x4F, 0x8F, 0x50, 0x8F, 0x51, 0x8F, 0x52, /* 0xDC-0xDF */
+ 0x8F, 0x53, 0x8F, 0x54, 0x8F, 0x55, 0x8F, 0x56, /* 0xE0-0xE3 */
+ 0x8F, 0x57, 0x8F, 0x58, 0x8F, 0x59, 0x8F, 0x5A, /* 0xE4-0xE7 */
+ 0xE2, 0xDD, 0x8F, 0x5B, 0xE2, 0xDE, 0x8F, 0x5C, /* 0xE8-0xEB */
+ 0x8F, 0x5D, 0x8F, 0x5E, 0x8F, 0x5F, 0x8F, 0x60, /* 0xEC-0xEF */
+ 0x8F, 0x61, 0x8F, 0x62, 0x8F, 0x63, 0x8F, 0x64, /* 0xF0-0xF3 */
+ 0xDB, 0xC8, 0x8F, 0x65, 0xD1, 0xD3, 0xCD, 0xA2, /* 0xF4-0xF7 */
+ 0x8F, 0x66, 0x8F, 0x67, 0xBD, 0xA8, 0x8F, 0x68, /* 0xF8-0xFB */
+ 0x8F, 0x69, 0x8F, 0x6A, 0xDE, 0xC3, 0xD8, 0xA5, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_5F[512] = {
+ 0xBF, 0xAA, 0xDB, 0xCD, 0xD2, 0xEC, 0xC6, 0xFA, /* 0x00-0x03 */
+ 0xC5, 0xAA, 0x8F, 0x6B, 0x8F, 0x6C, 0x8F, 0x6D, /* 0x04-0x07 */
+ 0xDE, 0xC4, 0x8F, 0x6E, 0xB1, 0xD7, 0xDF, 0xAE, /* 0x08-0x0B */
+ 0x8F, 0x6F, 0x8F, 0x70, 0x8F, 0x71, 0xCA, 0xBD, /* 0x0C-0x0F */
+ 0x8F, 0x72, 0xDF, 0xB1, 0x8F, 0x73, 0xB9, 0xAD, /* 0x10-0x13 */
+ 0x8F, 0x74, 0xD2, 0xFD, 0x8F, 0x75, 0xB8, 0xA5, /* 0x14-0x17 */
+ 0xBA, 0xEB, 0x8F, 0x76, 0x8F, 0x77, 0xB3, 0xDA, /* 0x18-0x1B */
+ 0x8F, 0x78, 0x8F, 0x79, 0x8F, 0x7A, 0xB5, 0xDC, /* 0x1C-0x1F */
+ 0xD5, 0xC5, 0x8F, 0x7B, 0x8F, 0x7C, 0x8F, 0x7D, /* 0x20-0x23 */
+ 0x8F, 0x7E, 0xC3, 0xD6, 0xCF, 0xD2, 0xBB, 0xA1, /* 0x24-0x27 */
+ 0x8F, 0x80, 0xE5, 0xF3, 0xE5, 0xF2, 0x8F, 0x81, /* 0x28-0x2B */
+ 0x8F, 0x82, 0xE5, 0xF4, 0x8F, 0x83, 0xCD, 0xE4, /* 0x2C-0x2F */
+ 0x8F, 0x84, 0xC8, 0xF5, 0x8F, 0x85, 0x8F, 0x86, /* 0x30-0x33 */
+ 0x8F, 0x87, 0x8F, 0x88, 0x8F, 0x89, 0x8F, 0x8A, /* 0x34-0x37 */
+ 0x8F, 0x8B, 0xB5, 0xAF, 0xC7, 0xBF, 0x8F, 0x8C, /* 0x38-0x3B */
+ 0xE5, 0xF6, 0x8F, 0x8D, 0x8F, 0x8E, 0x8F, 0x8F, /* 0x3C-0x3F */
+ 0xEC, 0xB0, 0x8F, 0x90, 0x8F, 0x91, 0x8F, 0x92, /* 0x40-0x43 */
+ 0x8F, 0x93, 0x8F, 0x94, 0x8F, 0x95, 0x8F, 0x96, /* 0x44-0x47 */
+ 0x8F, 0x97, 0x8F, 0x98, 0x8F, 0x99, 0x8F, 0x9A, /* 0x48-0x4B */
+ 0x8F, 0x9B, 0x8F, 0x9C, 0x8F, 0x9D, 0x8F, 0x9E, /* 0x4C-0x4F */
+ 0xE5, 0xE6, 0x8F, 0x9F, 0xB9, 0xE9, 0xB5, 0xB1, /* 0x50-0x53 */
+ 0x8F, 0xA0, 0xC2, 0xBC, 0xE5, 0xE8, 0xE5, 0xE7, /* 0x54-0x57 */
+ 0xE5, 0xE9, 0x8F, 0xA1, 0x8F, 0xA2, 0x8F, 0xA3, /* 0x58-0x5B */
+ 0x8F, 0xA4, 0xD2, 0xCD, 0x8F, 0xA5, 0x8F, 0xA6, /* 0x5C-0x5F */
+ 0x8F, 0xA7, 0xE1, 0xEA, 0xD0, 0xCE, 0x8F, 0xA8, /* 0x60-0x63 */
+ 0xCD, 0xAE, 0x8F, 0xA9, 0xD1, 0xE5, 0x8F, 0xAA, /* 0x64-0x67 */
+ 0x8F, 0xAB, 0xB2, 0xCA, 0xB1, 0xEB, 0x8F, 0xAC, /* 0x68-0x6B */
+ 0xB1, 0xF2, 0xC5, 0xED, 0x8F, 0xAD, 0x8F, 0xAE, /* 0x6C-0x6F */
+ 0xD5, 0xC3, 0xD3, 0xB0, 0x8F, 0xAF, 0xE1, 0xDC, /* 0x70-0x73 */
+ 0x8F, 0xB0, 0x8F, 0xB1, 0x8F, 0xB2, 0xE1, 0xDD, /* 0x74-0x77 */
+ 0x8F, 0xB3, 0xD2, 0xDB, 0x8F, 0xB4, 0xB3, 0xB9, /* 0x78-0x7B */
+ 0xB1, 0xCB, 0x8F, 0xB5, 0x8F, 0xB6, 0x8F, 0xB7, /* 0x7C-0x7F */
+
+ 0xCD, 0xF9, 0xD5, 0xF7, 0xE1, 0xDE, 0x8F, 0xB8, /* 0x80-0x83 */
+ 0xBE, 0xB6, 0xB4, 0xFD, 0x8F, 0xB9, 0xE1, 0xDF, /* 0x84-0x87 */
+ 0xBA, 0xDC, 0xE1, 0xE0, 0xBB, 0xB2, 0xC2, 0xC9, /* 0x88-0x8B */
+ 0xE1, 0xE1, 0x8F, 0xBA, 0x8F, 0xBB, 0x8F, 0xBC, /* 0x8C-0x8F */
+ 0xD0, 0xEC, 0x8F, 0xBD, 0xCD, 0xBD, 0x8F, 0xBE, /* 0x90-0x93 */
+ 0x8F, 0xBF, 0xE1, 0xE2, 0x8F, 0xC0, 0xB5, 0xC3, /* 0x94-0x97 */
+ 0xC5, 0xC7, 0xE1, 0xE3, 0x8F, 0xC1, 0x8F, 0xC2, /* 0x98-0x9B */
+ 0xE1, 0xE4, 0x8F, 0xC3, 0x8F, 0xC4, 0x8F, 0xC5, /* 0x9C-0x9F */
+ 0x8F, 0xC6, 0xD3, 0xF9, 0x8F, 0xC7, 0x8F, 0xC8, /* 0xA0-0xA3 */
+ 0x8F, 0xC9, 0x8F, 0xCA, 0x8F, 0xCB, 0x8F, 0xCC, /* 0xA4-0xA7 */
+ 0xE1, 0xE5, 0x8F, 0xCD, 0xD1, 0xAD, 0x8F, 0xCE, /* 0xA8-0xAB */
+ 0x8F, 0xCF, 0xE1, 0xE6, 0xCE, 0xA2, 0x8F, 0xD0, /* 0xAC-0xAF */
+ 0x8F, 0xD1, 0x8F, 0xD2, 0x8F, 0xD3, 0x8F, 0xD4, /* 0xB0-0xB3 */
+ 0x8F, 0xD5, 0xE1, 0xE7, 0x8F, 0xD6, 0xB5, 0xC2, /* 0xB4-0xB7 */
+ 0x8F, 0xD7, 0x8F, 0xD8, 0x8F, 0xD9, 0x8F, 0xDA, /* 0xB8-0xBB */
+ 0xE1, 0xE8, 0xBB, 0xD5, 0x8F, 0xDB, 0x8F, 0xDC, /* 0xBC-0xBF */
+ 0x8F, 0xDD, 0x8F, 0xDE, 0x8F, 0xDF, 0xD0, 0xC4, /* 0xC0-0xC3 */
+ 0xE2, 0xE0, 0xB1, 0xD8, 0xD2, 0xE4, 0x8F, 0xE0, /* 0xC4-0xC7 */
+ 0x8F, 0xE1, 0xE2, 0xE1, 0x8F, 0xE2, 0x8F, 0xE3, /* 0xC8-0xCB */
+ 0xBC, 0xC9, 0xC8, 0xCC, 0x8F, 0xE4, 0xE2, 0xE3, /* 0xCC-0xCF */
+ 0xEC, 0xFE, 0xEC, 0xFD, 0xDF, 0xAF, 0x8F, 0xE5, /* 0xD0-0xD3 */
+ 0x8F, 0xE6, 0x8F, 0xE7, 0xE2, 0xE2, 0xD6, 0xBE, /* 0xD4-0xD7 */
+ 0xCD, 0xFC, 0xC3, 0xA6, 0x8F, 0xE8, 0x8F, 0xE9, /* 0xD8-0xDB */
+ 0x8F, 0xEA, 0xE3, 0xC3, 0x8F, 0xEB, 0x8F, 0xEC, /* 0xDC-0xDF */
+ 0xD6, 0xD2, 0xE2, 0xE7, 0x8F, 0xED, 0x8F, 0xEE, /* 0xE0-0xE3 */
+ 0xE2, 0xE8, 0x8F, 0xEF, 0x8F, 0xF0, 0xD3, 0xC7, /* 0xE4-0xE7 */
+ 0x8F, 0xF1, 0x8F, 0xF2, 0xE2, 0xEC, 0xBF, 0xEC, /* 0xE8-0xEB */
+ 0x8F, 0xF3, 0xE2, 0xED, 0xE2, 0xE5, 0x8F, 0xF4, /* 0xEC-0xEF */
+ 0x8F, 0xF5, 0xB3, 0xC0, 0x8F, 0xF6, 0x8F, 0xF7, /* 0xF0-0xF3 */
+ 0x8F, 0xF8, 0xC4, 0xEE, 0x8F, 0xF9, 0x8F, 0xFA, /* 0xF4-0xF7 */
+ 0xE2, 0xEE, 0x8F, 0xFB, 0x8F, 0xFC, 0xD0, 0xC3, /* 0xF8-0xFB */
+ 0x8F, 0xFD, 0xBA, 0xF6, 0xE2, 0xE9, 0xB7, 0xDE, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_60[512] = {
+ 0xBB, 0xB3, 0xCC, 0xAC, 0xCB, 0xCB, 0xE2, 0xE4, /* 0x00-0x03 */
+ 0xE2, 0xE6, 0xE2, 0xEA, 0xE2, 0xEB, 0x8F, 0xFE, /* 0x04-0x07 */
+ 0x90, 0x40, 0x90, 0x41, 0xE2, 0xF7, 0x90, 0x42, /* 0x08-0x0B */
+ 0x90, 0x43, 0xE2, 0xF4, 0xD4, 0xF5, 0xE2, 0xF3, /* 0x0C-0x0F */
+ 0x90, 0x44, 0x90, 0x45, 0xC5, 0xAD, 0x90, 0x46, /* 0x10-0x13 */
+ 0xD5, 0xFA, 0xC5, 0xC2, 0xB2, 0xC0, 0x90, 0x47, /* 0x14-0x17 */
+ 0x90, 0x48, 0xE2, 0xEF, 0x90, 0x49, 0xE2, 0xF2, /* 0x18-0x1B */
+ 0xC1, 0xAF, 0xCB, 0xBC, 0x90, 0x4A, 0x90, 0x4B, /* 0x1C-0x1F */
+ 0xB5, 0xA1, 0xE2, 0xF9, 0x90, 0x4C, 0x90, 0x4D, /* 0x20-0x23 */
+ 0x90, 0x4E, 0xBC, 0xB1, 0xE2, 0xF1, 0xD0, 0xD4, /* 0x24-0x27 */
+ 0xD4, 0xB9, 0xE2, 0xF5, 0xB9, 0xD6, 0xE2, 0xF6, /* 0x28-0x2B */
+ 0x90, 0x4F, 0x90, 0x50, 0x90, 0x51, 0xC7, 0xD3, /* 0x2C-0x2F */
+ 0x90, 0x52, 0x90, 0x53, 0x90, 0x54, 0x90, 0x55, /* 0x30-0x33 */
+ 0x90, 0x56, 0xE2, 0xF0, 0x90, 0x57, 0x90, 0x58, /* 0x34-0x37 */
+ 0x90, 0x59, 0x90, 0x5A, 0x90, 0x5B, 0xD7, 0xDC, /* 0x38-0x3B */
+ 0xED, 0xA1, 0x90, 0x5C, 0x90, 0x5D, 0xE2, 0xF8, /* 0x3C-0x3F */
+ 0x90, 0x5E, 0xED, 0xA5, 0xE2, 0xFE, 0xCA, 0xD1, /* 0x40-0x43 */
+ 0x90, 0x5F, 0x90, 0x60, 0x90, 0x61, 0x90, 0x62, /* 0x44-0x47 */
+ 0x90, 0x63, 0x90, 0x64, 0x90, 0x65, 0xC1, 0xB5, /* 0x48-0x4B */
+ 0x90, 0x66, 0xBB, 0xD0, 0x90, 0x67, 0x90, 0x68, /* 0x4C-0x4F */
+ 0xBF, 0xD6, 0x90, 0x69, 0xBA, 0xE3, 0x90, 0x6A, /* 0x50-0x53 */
+ 0x90, 0x6B, 0xCB, 0xA1, 0x90, 0x6C, 0x90, 0x6D, /* 0x54-0x57 */
+ 0x90, 0x6E, 0xED, 0xA6, 0xED, 0xA3, 0x90, 0x6F, /* 0x58-0x5B */
+ 0x90, 0x70, 0xED, 0xA2, 0x90, 0x71, 0x90, 0x72, /* 0x5C-0x5F */
+ 0x90, 0x73, 0x90, 0x74, 0xBB, 0xD6, 0xED, 0xA7, /* 0x60-0x63 */
+ 0xD0, 0xF4, 0x90, 0x75, 0x90, 0x76, 0xED, 0xA4, /* 0x64-0x67 */
+ 0xBA, 0xDE, 0xB6, 0xF7, 0xE3, 0xA1, 0xB6, 0xB2, /* 0x68-0x6B */
+ 0xCC, 0xF1, 0xB9, 0xA7, 0x90, 0x77, 0xCF, 0xA2, /* 0x6C-0x6F */
+ 0xC7, 0xA1, 0x90, 0x78, 0x90, 0x79, 0xBF, 0xD2, /* 0x70-0x73 */
+ 0x90, 0x7A, 0x90, 0x7B, 0xB6, 0xF1, 0x90, 0x7C, /* 0x74-0x77 */
+ 0xE2, 0xFA, 0xE2, 0xFB, 0xE2, 0xFD, 0xE2, 0xFC, /* 0x78-0x7B */
+ 0xC4, 0xD5, 0xE3, 0xA2, 0x90, 0x7D, 0xD3, 0xC1, /* 0x7C-0x7F */
+
+ 0x90, 0x7E, 0x90, 0x80, 0x90, 0x81, 0xE3, 0xA7, /* 0x80-0x83 */
+ 0xC7, 0xC4, 0x90, 0x82, 0x90, 0x83, 0x90, 0x84, /* 0x84-0x87 */
+ 0x90, 0x85, 0xCF, 0xA4, 0x90, 0x86, 0x90, 0x87, /* 0x88-0x8B */
+ 0xE3, 0xA9, 0xBA, 0xB7, 0x90, 0x88, 0x90, 0x89, /* 0x8C-0x8F */
+ 0x90, 0x8A, 0x90, 0x8B, 0xE3, 0xA8, 0x90, 0x8C, /* 0x90-0x93 */
+ 0xBB, 0xDA, 0x90, 0x8D, 0xE3, 0xA3, 0x90, 0x8E, /* 0x94-0x97 */
+ 0x90, 0x8F, 0x90, 0x90, 0xE3, 0xA4, 0xE3, 0xAA, /* 0x98-0x9B */
+ 0x90, 0x91, 0xE3, 0xA6, 0x90, 0x92, 0xCE, 0xF2, /* 0x9C-0x9F */
+ 0xD3, 0xC6, 0x90, 0x93, 0x90, 0x94, 0xBB, 0xBC, /* 0xA0-0xA3 */
+ 0x90, 0x95, 0x90, 0x96, 0xD4, 0xC3, 0x90, 0x97, /* 0xA4-0xA7 */
+ 0xC4, 0xFA, 0x90, 0x98, 0x90, 0x99, 0xED, 0xA8, /* 0xA8-0xAB */
+ 0xD0, 0xFC, 0xE3, 0xA5, 0x90, 0x9A, 0xC3, 0xF5, /* 0xAC-0xAF */
+ 0x90, 0x9B, 0xE3, 0xAD, 0xB1, 0xAF, 0x90, 0x9C, /* 0xB0-0xB3 */
+ 0xE3, 0xB2, 0x90, 0x9D, 0x90, 0x9E, 0x90, 0x9F, /* 0xB4-0xB7 */
+ 0xBC, 0xC2, 0x90, 0xA0, 0x90, 0xA1, 0xE3, 0xAC, /* 0xB8-0xBB */
+ 0xB5, 0xBF, 0x90, 0xA2, 0x90, 0xA3, 0x90, 0xA4, /* 0xBC-0xBF */
+ 0x90, 0xA5, 0x90, 0xA6, 0x90, 0xA7, 0x90, 0xA8, /* 0xC0-0xC3 */
+ 0x90, 0xA9, 0xC7, 0xE9, 0xE3, 0xB0, 0x90, 0xAA, /* 0xC4-0xC7 */
+ 0x90, 0xAB, 0x90, 0xAC, 0xBE, 0xAA, 0xCD, 0xEF, /* 0xC8-0xCB */
+ 0x90, 0xAD, 0x90, 0xAE, 0x90, 0xAF, 0x90, 0xB0, /* 0xCC-0xCF */
+ 0x90, 0xB1, 0xBB, 0xF3, 0x90, 0xB2, 0x90, 0xB3, /* 0xD0-0xD3 */
+ 0x90, 0xB4, 0xCC, 0xE8, 0x90, 0xB5, 0x90, 0xB6, /* 0xD4-0xD7 */
+ 0xE3, 0xAF, 0x90, 0xB7, 0xE3, 0xB1, 0x90, 0xB8, /* 0xD8-0xDB */
+ 0xCF, 0xA7, 0xE3, 0xAE, 0x90, 0xB9, 0xCE, 0xA9, /* 0xDC-0xDF */
+ 0xBB, 0xDD, 0x90, 0xBA, 0x90, 0xBB, 0x90, 0xBC, /* 0xE0-0xE3 */
+ 0x90, 0xBD, 0x90, 0xBE, 0xB5, 0xEB, 0xBE, 0xE5, /* 0xE4-0xE7 */
+ 0xB2, 0xD2, 0xB3, 0xCD, 0x90, 0xBF, 0xB1, 0xB9, /* 0xE8-0xEB */
+ 0xE3, 0xAB, 0xB2, 0xD1, 0xB5, 0xAC, 0xB9, 0xDF, /* 0xEC-0xEF */
+ 0xB6, 0xE8, 0x90, 0xC0, 0x90, 0xC1, 0xCF, 0xEB, /* 0xF0-0xF3 */
+ 0xE3, 0xB7, 0x90, 0xC2, 0xBB, 0xCC, 0x90, 0xC3, /* 0xF4-0xF7 */
+ 0x90, 0xC4, 0xC8, 0xC7, 0xD0, 0xCA, 0x90, 0xC5, /* 0xF8-0xFB */
+ 0x90, 0xC6, 0x90, 0xC7, 0x90, 0xC8, 0x90, 0xC9, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_61[512] = {
+ 0xE3, 0xB8, 0xB3, 0xEE, 0x90, 0xCA, 0x90, 0xCB, /* 0x00-0x03 */
+ 0x90, 0xCC, 0x90, 0xCD, 0xED, 0xA9, 0x90, 0xCE, /* 0x04-0x07 */
+ 0xD3, 0xFA, 0xD3, 0xE4, 0x90, 0xCF, 0x90, 0xD0, /* 0x08-0x0B */
+ 0x90, 0xD1, 0xED, 0xAA, 0xE3, 0xB9, 0xD2, 0xE2, /* 0x0C-0x0F */
+ 0x90, 0xD2, 0x90, 0xD3, 0x90, 0xD4, 0x90, 0xD5, /* 0x10-0x13 */
+ 0x90, 0xD6, 0xE3, 0xB5, 0x90, 0xD7, 0x90, 0xD8, /* 0x14-0x17 */
+ 0x90, 0xD9, 0x90, 0xDA, 0xD3, 0xDE, 0x90, 0xDB, /* 0x18-0x1B */
+ 0x90, 0xDC, 0x90, 0xDD, 0x90, 0xDE, 0xB8, 0xD0, /* 0x1C-0x1F */
+ 0xE3, 0xB3, 0x90, 0xDF, 0x90, 0xE0, 0xE3, 0xB6, /* 0x20-0x23 */
+ 0xB7, 0xDF, 0x90, 0xE1, 0xE3, 0xB4, 0xC0, 0xA2, /* 0x24-0x27 */
+ 0x90, 0xE2, 0x90, 0xE3, 0x90, 0xE4, 0xE3, 0xBA, /* 0x28-0x2B */
+ 0x90, 0xE5, 0x90, 0xE6, 0x90, 0xE7, 0x90, 0xE8, /* 0x2C-0x2F */
+ 0x90, 0xE9, 0x90, 0xEA, 0x90, 0xEB, 0x90, 0xEC, /* 0x30-0x33 */
+ 0x90, 0xED, 0x90, 0xEE, 0x90, 0xEF, 0x90, 0xF0, /* 0x34-0x37 */
+ 0x90, 0xF1, 0x90, 0xF2, 0x90, 0xF3, 0x90, 0xF4, /* 0x38-0x3B */
+ 0x90, 0xF5, 0x90, 0xF6, 0x90, 0xF7, 0xD4, 0xB8, /* 0x3C-0x3F */
+ 0x90, 0xF8, 0x90, 0xF9, 0x90, 0xFA, 0x90, 0xFB, /* 0x40-0x43 */
+ 0x90, 0xFC, 0x90, 0xFD, 0x90, 0xFE, 0x91, 0x40, /* 0x44-0x47 */
+ 0xB4, 0xC8, 0x91, 0x41, 0xE3, 0xBB, 0x91, 0x42, /* 0x48-0x4B */
+ 0xBB, 0xC5, 0x91, 0x43, 0xC9, 0xF7, 0x91, 0x44, /* 0x4C-0x4F */
+ 0x91, 0x45, 0xC9, 0xE5, 0x91, 0x46, 0x91, 0x47, /* 0x50-0x53 */
+ 0x91, 0x48, 0xC4, 0xBD, 0x91, 0x49, 0x91, 0x4A, /* 0x54-0x57 */
+ 0x91, 0x4B, 0x91, 0x4C, 0x91, 0x4D, 0x91, 0x4E, /* 0x58-0x5B */
+ 0x91, 0x4F, 0xED, 0xAB, 0x91, 0x50, 0x91, 0x51, /* 0x5C-0x5F */
+ 0x91, 0x52, 0x91, 0x53, 0xC2, 0xFD, 0x91, 0x54, /* 0x60-0x63 */
+ 0x91, 0x55, 0x91, 0x56, 0x91, 0x57, 0xBB, 0xDB, /* 0x64-0x67 */
+ 0xBF, 0xAE, 0x91, 0x58, 0x91, 0x59, 0x91, 0x5A, /* 0x68-0x6B */
+ 0x91, 0x5B, 0x91, 0x5C, 0x91, 0x5D, 0x91, 0x5E, /* 0x6C-0x6F */
+ 0xCE, 0xBF, 0x91, 0x5F, 0x91, 0x60, 0x91, 0x61, /* 0x70-0x73 */
+ 0x91, 0x62, 0xE3, 0xBC, 0x91, 0x63, 0xBF, 0xB6, /* 0x74-0x77 */
+ 0x91, 0x64, 0x91, 0x65, 0x91, 0x66, 0x91, 0x67, /* 0x78-0x7B */
+ 0x91, 0x68, 0x91, 0x69, 0x91, 0x6A, 0x91, 0x6B, /* 0x7C-0x7F */
+
+ 0x91, 0x6C, 0x91, 0x6D, 0x91, 0x6E, 0x91, 0x6F, /* 0x80-0x83 */
+ 0x91, 0x70, 0x91, 0x71, 0x91, 0x72, 0x91, 0x73, /* 0x84-0x87 */
+ 0x91, 0x74, 0x91, 0x75, 0x91, 0x76, 0xB1, 0xEF, /* 0x88-0x8B */
+ 0x91, 0x77, 0x91, 0x78, 0xD4, 0xF7, 0x91, 0x79, /* 0x8C-0x8F */
+ 0x91, 0x7A, 0x91, 0x7B, 0x91, 0x7C, 0x91, 0x7D, /* 0x90-0x93 */
+ 0xE3, 0xBE, 0x91, 0x7E, 0x91, 0x80, 0x91, 0x81, /* 0x94-0x97 */
+ 0x91, 0x82, 0x91, 0x83, 0x91, 0x84, 0x91, 0x85, /* 0x98-0x9B */
+ 0x91, 0x86, 0xED, 0xAD, 0x91, 0x87, 0x91, 0x88, /* 0x9C-0x9F */
+ 0x91, 0x89, 0x91, 0x8A, 0x91, 0x8B, 0x91, 0x8C, /* 0xA0-0xA3 */
+ 0x91, 0x8D, 0x91, 0x8E, 0x91, 0x8F, 0xE3, 0xBF, /* 0xA4-0xA7 */
+ 0xBA, 0xA9, 0xED, 0xAC, 0x91, 0x90, 0x91, 0x91, /* 0xA8-0xAB */
+ 0xE3, 0xBD, 0x91, 0x92, 0x91, 0x93, 0x91, 0x94, /* 0xAC-0xAF */
+ 0x91, 0x95, 0x91, 0x96, 0x91, 0x97, 0x91, 0x98, /* 0xB0-0xB3 */
+ 0x91, 0x99, 0x91, 0x9A, 0x91, 0x9B, 0xE3, 0xC0, /* 0xB4-0xB7 */
+ 0x91, 0x9C, 0x91, 0x9D, 0x91, 0x9E, 0x91, 0x9F, /* 0xB8-0xBB */
+ 0x91, 0xA0, 0x91, 0xA1, 0xBA, 0xB6, 0x91, 0xA2, /* 0xBC-0xBF */
+ 0x91, 0xA3, 0x91, 0xA4, 0xB6, 0xAE, 0x91, 0xA5, /* 0xC0-0xC3 */
+ 0x91, 0xA6, 0x91, 0xA7, 0x91, 0xA8, 0x91, 0xA9, /* 0xC4-0xC7 */
+ 0xD0, 0xB8, 0x91, 0xAA, 0xB0, 0xC3, 0xED, 0xAE, /* 0xC8-0xCB */
+ 0x91, 0xAB, 0x91, 0xAC, 0x91, 0xAD, 0x91, 0xAE, /* 0xCC-0xCF */
+ 0x91, 0xAF, 0xED, 0xAF, 0xC0, 0xC1, 0x91, 0xB0, /* 0xD0-0xD3 */
+ 0xE3, 0xC1, 0x91, 0xB1, 0x91, 0xB2, 0x91, 0xB3, /* 0xD4-0xD7 */
+ 0x91, 0xB4, 0x91, 0xB5, 0x91, 0xB6, 0x91, 0xB7, /* 0xD8-0xDB */
+ 0x91, 0xB8, 0x91, 0xB9, 0x91, 0xBA, 0x91, 0xBB, /* 0xDC-0xDF */
+ 0x91, 0xBC, 0x91, 0xBD, 0x91, 0xBE, 0x91, 0xBF, /* 0xE0-0xE3 */
+ 0x91, 0xC0, 0x91, 0xC1, 0xC5, 0xB3, 0x91, 0xC2, /* 0xE4-0xE7 */
+ 0x91, 0xC3, 0x91, 0xC4, 0x91, 0xC5, 0x91, 0xC6, /* 0xE8-0xEB */
+ 0x91, 0xC7, 0x91, 0xC8, 0x91, 0xC9, 0x91, 0xCA, /* 0xEC-0xEF */
+ 0x91, 0xCB, 0x91, 0xCC, 0x91, 0xCD, 0x91, 0xCE, /* 0xF0-0xF3 */
+ 0x91, 0xCF, 0xE3, 0xC2, 0x91, 0xD0, 0x91, 0xD1, /* 0xF4-0xF7 */
+ 0x91, 0xD2, 0x91, 0xD3, 0x91, 0xD4, 0x91, 0xD5, /* 0xF8-0xFB */
+ 0x91, 0xD6, 0x91, 0xD7, 0x91, 0xD8, 0xDC, 0xB2, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_62[512] = {
+ 0x91, 0xD9, 0x91, 0xDA, 0x91, 0xDB, 0x91, 0xDC, /* 0x00-0x03 */
+ 0x91, 0xDD, 0x91, 0xDE, 0xED, 0xB0, 0x91, 0xDF, /* 0x04-0x07 */
+ 0xB8, 0xEA, 0x91, 0xE0, 0xCE, 0xEC, 0xEA, 0xA7, /* 0x08-0x0B */
+ 0xD0, 0xE7, 0xCA, 0xF9, 0xC8, 0xD6, 0xCF, 0xB7, /* 0x0C-0x0F */
+ 0xB3, 0xC9, 0xCE, 0xD2, 0xBD, 0xE4, 0x91, 0xE1, /* 0x10-0x13 */
+ 0x91, 0xE2, 0xE3, 0xDE, 0xBB, 0xF2, 0xEA, 0xA8, /* 0x14-0x17 */
+ 0xD5, 0xBD, 0x91, 0xE3, 0xC6, 0xDD, 0xEA, 0xA9, /* 0x18-0x1B */
+ 0x91, 0xE4, 0x91, 0xE5, 0x91, 0xE6, 0xEA, 0xAA, /* 0x1C-0x1F */
+ 0x91, 0xE7, 0xEA, 0xAC, 0xEA, 0xAB, 0x91, 0xE8, /* 0x20-0x23 */
+ 0xEA, 0xAE, 0xEA, 0xAD, 0x91, 0xE9, 0x91, 0xEA, /* 0x24-0x27 */
+ 0x91, 0xEB, 0x91, 0xEC, 0xBD, 0xD8, 0x91, 0xED, /* 0x28-0x2B */
+ 0xEA, 0xAF, 0x91, 0xEE, 0xC2, 0xBE, 0x91, 0xEF, /* 0x2C-0x2F */
+ 0x91, 0xF0, 0x91, 0xF1, 0x91, 0xF2, 0xB4, 0xC1, /* 0x30-0x33 */
+ 0xB4, 0xF7, 0x91, 0xF3, 0x91, 0xF4, 0xBB, 0xA7, /* 0x34-0x37 */
+ 0x91, 0xF5, 0x91, 0xF6, 0x91, 0xF7, 0x91, 0xF8, /* 0x38-0x3B */
+ 0x91, 0xF9, 0xEC, 0xE6, 0xEC, 0xE5, 0xB7, 0xBF, /* 0x3C-0x3F */
+ 0xCB, 0xF9, 0xB1, 0xE2, 0x91, 0xFA, 0xEC, 0xE7, /* 0x40-0x43 */
+ 0x91, 0xFB, 0x91, 0xFC, 0x91, 0xFD, 0xC9, 0xC8, /* 0x44-0x47 */
+ 0xEC, 0xE8, 0xEC, 0xE9, 0x91, 0xFE, 0xCA, 0xD6, /* 0x48-0x4B */
+ 0xDE, 0xD0, 0xB2, 0xC5, 0xD4, 0xFA, 0x92, 0x40, /* 0x4C-0x4F */
+ 0x92, 0x41, 0xC6, 0xCB, 0xB0, 0xC7, 0xB4, 0xF2, /* 0x50-0x53 */
+ 0xC8, 0xD3, 0x92, 0x42, 0x92, 0x43, 0x92, 0x44, /* 0x54-0x57 */
+ 0xCD, 0xD0, 0x92, 0x45, 0x92, 0x46, 0xBF, 0xB8, /* 0x58-0x5B */
+ 0x92, 0x47, 0x92, 0x48, 0x92, 0x49, 0x92, 0x4A, /* 0x5C-0x5F */
+ 0x92, 0x4B, 0x92, 0x4C, 0x92, 0x4D, 0xBF, 0xDB, /* 0x60-0x63 */
+ 0x92, 0x4E, 0x92, 0x4F, 0xC7, 0xA4, 0xD6, 0xB4, /* 0x64-0x67 */
+ 0x92, 0x50, 0xC0, 0xA9, 0xDE, 0xD1, 0xC9, 0xA8, /* 0x68-0x6B */
+ 0xD1, 0xEF, 0xC5, 0xA4, 0xB0, 0xE7, 0xB3, 0xB6, /* 0x6C-0x6F */
+ 0xC8, 0xC5, 0x92, 0x51, 0x92, 0x52, 0xB0, 0xE2, /* 0x70-0x73 */
+ 0x92, 0x53, 0x92, 0x54, 0xB7, 0xF6, 0x92, 0x55, /* 0x74-0x77 */
+ 0x92, 0x56, 0xC5, 0xFA, 0x92, 0x57, 0x92, 0x58, /* 0x78-0x7B */
+ 0xB6, 0xF3, 0x92, 0x59, 0xD5, 0xD2, 0xB3, 0xD0, /* 0x7C-0x7F */
+
+ 0xBC, 0xBC, 0x92, 0x5A, 0x92, 0x5B, 0x92, 0x5C, /* 0x80-0x83 */
+ 0xB3, 0xAD, 0x92, 0x5D, 0x92, 0x5E, 0x92, 0x5F, /* 0x84-0x87 */
+ 0x92, 0x60, 0xBE, 0xF1, 0xB0, 0xD1, 0x92, 0x61, /* 0x88-0x8B */
+ 0x92, 0x62, 0x92, 0x63, 0x92, 0x64, 0x92, 0x65, /* 0x8C-0x8F */
+ 0x92, 0x66, 0xD2, 0xD6, 0xCA, 0xE3, 0xD7, 0xA5, /* 0x90-0x93 */
+ 0x92, 0x67, 0xCD, 0xB6, 0xB6, 0xB6, 0xBF, 0xB9, /* 0x94-0x97 */
+ 0xD5, 0xDB, 0x92, 0x68, 0xB8, 0xA7, 0xC5, 0xD7, /* 0x98-0x9B */
+ 0x92, 0x69, 0x92, 0x6A, 0x92, 0x6B, 0xDE, 0xD2, /* 0x9C-0x9F */
+ 0xBF, 0xD9, 0xC2, 0xD5, 0xC7, 0xC0, 0x92, 0x6C, /* 0xA0-0xA3 */
+ 0xBB, 0xA4, 0xB1, 0xA8, 0x92, 0x6D, 0x92, 0x6E, /* 0xA4-0xA7 */
+ 0xC5, 0xEA, 0x92, 0x6F, 0x92, 0x70, 0xC5, 0xFB, /* 0xA8-0xAB */
+ 0xCC, 0xA7, 0x92, 0x71, 0x92, 0x72, 0x92, 0x73, /* 0xAC-0xAF */
+ 0x92, 0x74, 0xB1, 0xA7, 0x92, 0x75, 0x92, 0x76, /* 0xB0-0xB3 */
+ 0x92, 0x77, 0xB5, 0xD6, 0x92, 0x78, 0x92, 0x79, /* 0xB4-0xB7 */
+ 0x92, 0x7A, 0xC4, 0xA8, 0x92, 0x7B, 0xDE, 0xD3, /* 0xB8-0xBB */
+ 0xD1, 0xBA, 0xB3, 0xE9, 0x92, 0x7C, 0xC3, 0xF2, /* 0xBC-0xBF */
+ 0x92, 0x7D, 0x92, 0x7E, 0xB7, 0xF7, 0x92, 0x80, /* 0xC0-0xC3 */
+ 0xD6, 0xF4, 0xB5, 0xA3, 0xB2, 0xF0, 0xC4, 0xB4, /* 0xC4-0xC7 */
+ 0xC4, 0xE9, 0xC0, 0xAD, 0xDE, 0xD4, 0x92, 0x81, /* 0xC8-0xCB */
+ 0xB0, 0xE8, 0xC5, 0xC4, 0xC1, 0xE0, 0x92, 0x82, /* 0xCC-0xCF */
+ 0xB9, 0xD5, 0x92, 0x83, 0xBE, 0xDC, 0xCD, 0xD8, /* 0xD0-0xD3 */
+ 0xB0, 0xCE, 0x92, 0x84, 0xCD, 0xCF, 0xDE, 0xD6, /* 0xD4-0xD7 */
+ 0xBE, 0xD0, 0xD7, 0xBE, 0xDE, 0xD5, 0xD5, 0xD0, /* 0xD8-0xDB */
+ 0xB0, 0xDD, 0x92, 0x85, 0x92, 0x86, 0xC4, 0xE2, /* 0xDC-0xDF */
+ 0x92, 0x87, 0x92, 0x88, 0xC2, 0xA3, 0xBC, 0xF0, /* 0xE0-0xE3 */
+ 0x92, 0x89, 0xD3, 0xB5, 0xC0, 0xB9, 0xC5, 0xA1, /* 0xE4-0xE7 */
+ 0xB2, 0xA6, 0xD4, 0xF1, 0x92, 0x8A, 0x92, 0x8B, /* 0xE8-0xEB */
+ 0xC0, 0xA8, 0xCA, 0xC3, 0xDE, 0xD7, 0xD5, 0xFC, /* 0xEC-0xEF */
+ 0x92, 0x8C, 0xB9, 0xB0, 0x92, 0x8D, 0xC8, 0xAD, /* 0xF0-0xF3 */
+ 0xCB, 0xA9, 0x92, 0x8E, 0xDE, 0xD9, 0xBF, 0xBD, /* 0xF4-0xF7 */
+ 0x92, 0x8F, 0x92, 0x90, 0x92, 0x91, 0x92, 0x92, /* 0xF8-0xFB */
+ 0xC6, 0xB4, 0xD7, 0xA7, 0xCA, 0xB0, 0xC4, 0xC3, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_63[512] = {
+ 0x92, 0x93, 0xB3, 0xD6, 0xB9, 0xD2, 0x92, 0x94, /* 0x00-0x03 */
+ 0x92, 0x95, 0x92, 0x96, 0x92, 0x97, 0xD6, 0xB8, /* 0x04-0x07 */
+ 0xEA, 0xFC, 0xB0, 0xB4, 0x92, 0x98, 0x92, 0x99, /* 0x08-0x0B */
+ 0x92, 0x9A, 0x92, 0x9B, 0xBF, 0xE6, 0x92, 0x9C, /* 0x0C-0x0F */
+ 0x92, 0x9D, 0xCC, 0xF4, 0x92, 0x9E, 0x92, 0x9F, /* 0x10-0x13 */
+ 0x92, 0xA0, 0x92, 0xA1, 0xCD, 0xDA, 0x92, 0xA2, /* 0x14-0x17 */
+ 0x92, 0xA3, 0x92, 0xA4, 0xD6, 0xBF, 0xC2, 0xCE, /* 0x18-0x1B */
+ 0x92, 0xA5, 0xCE, 0xCE, 0xCC, 0xA2, 0xD0, 0xAE, /* 0x1C-0x1F */
+ 0xC4, 0xD3, 0xB5, 0xB2, 0xDE, 0xD8, 0xD5, 0xF5, /* 0x20-0x23 */
+ 0xBC, 0xB7, 0xBB, 0xD3, 0x92, 0xA6, 0x92, 0xA7, /* 0x24-0x27 */
+ 0xB0, 0xA4, 0x92, 0xA8, 0xC5, 0xB2, 0xB4, 0xEC, /* 0x28-0x2B */
+ 0x92, 0xA9, 0x92, 0xAA, 0x92, 0xAB, 0xD5, 0xF1, /* 0x2C-0x2F */
+ 0x92, 0xAC, 0x92, 0xAD, 0xEA, 0xFD, 0x92, 0xAE, /* 0x30-0x33 */
+ 0x92, 0xAF, 0x92, 0xB0, 0x92, 0xB1, 0x92, 0xB2, /* 0x34-0x37 */
+ 0x92, 0xB3, 0xDE, 0xDA, 0xCD, 0xA6, 0x92, 0xB4, /* 0x38-0x3B */
+ 0x92, 0xB5, 0xCD, 0xEC, 0x92, 0xB6, 0x92, 0xB7, /* 0x3C-0x3F */
+ 0x92, 0xB8, 0x92, 0xB9, 0xCE, 0xE6, 0xDE, 0xDC, /* 0x40-0x43 */
+ 0x92, 0xBA, 0xCD, 0xB1, 0xC0, 0xA6, 0x92, 0xBB, /* 0x44-0x47 */
+ 0x92, 0xBC, 0xD7, 0xBD, 0x92, 0xBD, 0xDE, 0xDB, /* 0x48-0x4B */
+ 0xB0, 0xC6, 0xBA, 0xB4, 0xC9, 0xD3, 0xC4, 0xF3, /* 0x4C-0x4F */
+ 0xBE, 0xE8, 0x92, 0xBE, 0x92, 0xBF, 0x92, 0xC0, /* 0x50-0x53 */
+ 0x92, 0xC1, 0xB2, 0xB6, 0x92, 0xC2, 0x92, 0xC3, /* 0x54-0x57 */
+ 0x92, 0xC4, 0x92, 0xC5, 0x92, 0xC6, 0x92, 0xC7, /* 0x58-0x5B */
+ 0x92, 0xC8, 0x92, 0xC9, 0xC0, 0xCC, 0xCB, 0xF0, /* 0x5C-0x5F */
+ 0x92, 0xCA, 0xBC, 0xF1, 0xBB, 0xBB, 0xB5, 0xB7, /* 0x60-0x63 */
+ 0x92, 0xCB, 0x92, 0xCC, 0x92, 0xCD, 0xC5, 0xF5, /* 0x64-0x67 */
+ 0x92, 0xCE, 0xDE, 0xE6, 0x92, 0xCF, 0x92, 0xD0, /* 0x68-0x6B */
+ 0x92, 0xD1, 0xDE, 0xE3, 0xBE, 0xDD, 0x92, 0xD2, /* 0x6C-0x6F */
+ 0x92, 0xD3, 0xDE, 0xDF, 0x92, 0xD4, 0x92, 0xD5, /* 0x70-0x73 */
+ 0x92, 0xD6, 0x92, 0xD7, 0xB4, 0xB7, 0xBD, 0xDD, /* 0x74-0x77 */
+ 0x92, 0xD8, 0x92, 0xD9, 0xDE, 0xE0, 0xC4, 0xED, /* 0x78-0x7B */
+ 0x92, 0xDA, 0x92, 0xDB, 0x92, 0xDC, 0x92, 0xDD, /* 0x7C-0x7F */
+
+ 0xCF, 0xC6, 0x92, 0xDE, 0xB5, 0xE0, 0x92, 0xDF, /* 0x80-0x83 */
+ 0x92, 0xE0, 0x92, 0xE1, 0x92, 0xE2, 0xB6, 0xDE, /* 0x84-0x87 */
+ 0xCA, 0xDA, 0xB5, 0xF4, 0xDE, 0xE5, 0x92, 0xE3, /* 0x88-0x8B */
+ 0xD5, 0xC6, 0x92, 0xE4, 0xDE, 0xE1, 0xCC, 0xCD, /* 0x8C-0x8F */
+ 0xC6, 0xFE, 0x92, 0xE5, 0xC5, 0xC5, 0x92, 0xE6, /* 0x90-0x93 */
+ 0x92, 0xE7, 0x92, 0xE8, 0xD2, 0xB4, 0x92, 0xE9, /* 0x94-0x97 */
+ 0xBE, 0xF2, 0x92, 0xEA, 0x92, 0xEB, 0x92, 0xEC, /* 0x98-0x9B */
+ 0x92, 0xED, 0x92, 0xEE, 0x92, 0xEF, 0x92, 0xF0, /* 0x9C-0x9F */
+ 0xC2, 0xD3, 0x92, 0xF1, 0xCC, 0xBD, 0xB3, 0xB8, /* 0xA0-0xA3 */
+ 0x92, 0xF2, 0xBD, 0xD3, 0x92, 0xF3, 0xBF, 0xD8, /* 0xA4-0xA7 */
+ 0xCD, 0xC6, 0xD1, 0xDA, 0xB4, 0xEB, 0x92, 0xF4, /* 0xA8-0xAB */
+ 0xDE, 0xE4, 0xDE, 0xDD, 0xDE, 0xE7, 0x92, 0xF5, /* 0xAC-0xAF */
+ 0xEA, 0xFE, 0x92, 0xF6, 0x92, 0xF7, 0xC2, 0xB0, /* 0xB0-0xB3 */
+ 0xDE, 0xE2, 0x92, 0xF8, 0x92, 0xF9, 0xD6, 0xC0, /* 0xB4-0xB7 */
+ 0xB5, 0xA7, 0x92, 0xFA, 0xB2, 0xF4, 0x92, 0xFB, /* 0xB8-0xBB */
+ 0xDE, 0xE8, 0x92, 0xFC, 0xDE, 0xF2, 0x92, 0xFD, /* 0xBC-0xBF */
+ 0x92, 0xFE, 0x93, 0x40, 0x93, 0x41, 0x93, 0x42, /* 0xC0-0xC3 */
+ 0xDE, 0xED, 0x93, 0x43, 0xDE, 0xF1, 0x93, 0x44, /* 0xC4-0xC7 */
+ 0x93, 0x45, 0xC8, 0xE0, 0x93, 0x46, 0x93, 0x47, /* 0xC8-0xCB */
+ 0x93, 0x48, 0xD7, 0xE1, 0xDE, 0xEF, 0xC3, 0xE8, /* 0xCC-0xCF */
+ 0xCC, 0xE1, 0x93, 0x49, 0xB2, 0xE5, 0x93, 0x4A, /* 0xD0-0xD3 */
+ 0x93, 0x4B, 0x93, 0x4C, 0xD2, 0xBE, 0x93, 0x4D, /* 0xD4-0xD7 */
+ 0x93, 0x4E, 0x93, 0x4F, 0x93, 0x50, 0x93, 0x51, /* 0xD8-0xDB */
+ 0x93, 0x52, 0x93, 0x53, 0xDE, 0xEE, 0x93, 0x54, /* 0xDC-0xDF */
+ 0xDE, 0xEB, 0xCE, 0xD5, 0x93, 0x55, 0xB4, 0xA7, /* 0xE0-0xE3 */
+ 0x93, 0x56, 0x93, 0x57, 0x93, 0x58, 0x93, 0x59, /* 0xE4-0xE7 */
+ 0x93, 0x5A, 0xBF, 0xAB, 0xBE, 0xBE, 0x93, 0x5B, /* 0xE8-0xEB */
+ 0x93, 0x5C, 0xBD, 0xD2, 0x93, 0x5D, 0x93, 0x5E, /* 0xEC-0xEF */
+ 0x93, 0x5F, 0x93, 0x60, 0xDE, 0xE9, 0x93, 0x61, /* 0xF0-0xF3 */
+ 0xD4, 0xAE, 0x93, 0x62, 0xDE, 0xDE, 0x93, 0x63, /* 0xF4-0xF7 */
+ 0xDE, 0xEA, 0x93, 0x64, 0x93, 0x65, 0x93, 0x66, /* 0xF8-0xFB */
+ 0x93, 0x67, 0xC0, 0xBF, 0x93, 0x68, 0xDE, 0xEC, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_64[512] = {
+ 0xB2, 0xF3, 0xB8, 0xE9, 0xC2, 0xA7, 0x93, 0x69, /* 0x00-0x03 */
+ 0x93, 0x6A, 0xBD, 0xC1, 0x93, 0x6B, 0x93, 0x6C, /* 0x04-0x07 */
+ 0x93, 0x6D, 0x93, 0x6E, 0x93, 0x6F, 0xDE, 0xF5, /* 0x08-0x0B */
+ 0xDE, 0xF8, 0x93, 0x70, 0x93, 0x71, 0xB2, 0xAB, /* 0x0C-0x0F */
+ 0xB4, 0xA4, 0x93, 0x72, 0x93, 0x73, 0xB4, 0xEA, /* 0x10-0x13 */
+ 0xC9, 0xA6, 0x93, 0x74, 0x93, 0x75, 0x93, 0x76, /* 0x14-0x17 */
+ 0x93, 0x77, 0x93, 0x78, 0x93, 0x79, 0xDE, 0xF6, /* 0x18-0x1B */
+ 0xCB, 0xD1, 0x93, 0x7A, 0xB8, 0xE3, 0x93, 0x7B, /* 0x1C-0x1F */
+ 0xDE, 0xF7, 0xDE, 0xFA, 0x93, 0x7C, 0x93, 0x7D, /* 0x20-0x23 */
+ 0x93, 0x7E, 0x93, 0x80, 0xDE, 0xF9, 0x93, 0x81, /* 0x24-0x27 */
+ 0x93, 0x82, 0x93, 0x83, 0xCC, 0xC2, 0x93, 0x84, /* 0x28-0x2B */
+ 0xB0, 0xE1, 0xB4, 0xEE, 0x93, 0x85, 0x93, 0x86, /* 0x2C-0x2F */
+ 0x93, 0x87, 0x93, 0x88, 0x93, 0x89, 0x93, 0x8A, /* 0x30-0x33 */
+ 0xE5, 0xBA, 0x93, 0x8B, 0x93, 0x8C, 0x93, 0x8D, /* 0x34-0x37 */
+ 0x93, 0x8E, 0x93, 0x8F, 0xD0, 0xAF, 0x93, 0x90, /* 0x38-0x3B */
+ 0x93, 0x91, 0xB2, 0xEB, 0x93, 0x92, 0xEB, 0xA1, /* 0x3C-0x3F */
+ 0x93, 0x93, 0xDE, 0xF4, 0x93, 0x94, 0x93, 0x95, /* 0x40-0x43 */
+ 0xC9, 0xE3, 0xDE, 0xF3, 0xB0, 0xDA, 0xD2, 0xA1, /* 0x44-0x47 */
+ 0xB1, 0xF7, 0x93, 0x96, 0xCC, 0xAF, 0x93, 0x97, /* 0x48-0x4B */
+ 0x93, 0x98, 0x93, 0x99, 0x93, 0x9A, 0x93, 0x9B, /* 0x4C-0x4F */
+ 0x93, 0x9C, 0x93, 0x9D, 0xDE, 0xF0, 0x93, 0x9E, /* 0x50-0x53 */
+ 0xCB, 0xA4, 0x93, 0x9F, 0x93, 0xA0, 0x93, 0xA1, /* 0x54-0x57 */
+ 0xD5, 0xAA, 0x93, 0xA2, 0x93, 0xA3, 0x93, 0xA4, /* 0x58-0x5B */
+ 0x93, 0xA5, 0x93, 0xA6, 0xDE, 0xFB, 0x93, 0xA7, /* 0x5C-0x5F */
+ 0x93, 0xA8, 0x93, 0xA9, 0x93, 0xAA, 0x93, 0xAB, /* 0x60-0x63 */
+ 0x93, 0xAC, 0x93, 0xAD, 0x93, 0xAE, 0xB4, 0xDD, /* 0x64-0x67 */
+ 0x93, 0xAF, 0xC4, 0xA6, 0x93, 0xB0, 0x93, 0xB1, /* 0x68-0x6B */
+ 0x93, 0xB2, 0xDE, 0xFD, 0x93, 0xB3, 0x93, 0xB4, /* 0x6C-0x6F */
+ 0x93, 0xB5, 0x93, 0xB6, 0x93, 0xB7, 0x93, 0xB8, /* 0x70-0x73 */
+ 0x93, 0xB9, 0x93, 0xBA, 0x93, 0xBB, 0x93, 0xBC, /* 0x74-0x77 */
+ 0xC3, 0xFE, 0xC4, 0xA1, 0xDF, 0xA1, 0x93, 0xBD, /* 0x78-0x7B */
+ 0x93, 0xBE, 0x93, 0xBF, 0x93, 0xC0, 0x93, 0xC1, /* 0x7C-0x7F */
+
+ 0x93, 0xC2, 0x93, 0xC3, 0xC1, 0xCC, 0x93, 0xC4, /* 0x80-0x83 */
+ 0xDE, 0xFC, 0xBE, 0xEF, 0x93, 0xC5, 0xC6, 0xB2, /* 0x84-0x87 */
+ 0x93, 0xC6, 0x93, 0xC7, 0x93, 0xC8, 0x93, 0xC9, /* 0x88-0x8B */
+ 0x93, 0xCA, 0x93, 0xCB, 0x93, 0xCC, 0x93, 0xCD, /* 0x8C-0x8F */
+ 0x93, 0xCE, 0xB3, 0xC5, 0xC8, 0xF6, 0x93, 0xCF, /* 0x90-0x93 */
+ 0x93, 0xD0, 0xCB, 0xBA, 0xDE, 0xFE, 0x93, 0xD1, /* 0x94-0x97 */
+ 0x93, 0xD2, 0xDF, 0xA4, 0x93, 0xD3, 0x93, 0xD4, /* 0x98-0x9B */
+ 0x93, 0xD5, 0x93, 0xD6, 0xD7, 0xB2, 0x93, 0xD7, /* 0x9C-0x9F */
+ 0x93, 0xD8, 0x93, 0xD9, 0x93, 0xDA, 0x93, 0xDB, /* 0xA0-0xA3 */
+ 0xB3, 0xB7, 0x93, 0xDC, 0x93, 0xDD, 0x93, 0xDE, /* 0xA4-0xA7 */
+ 0x93, 0xDF, 0xC1, 0xC3, 0x93, 0xE0, 0x93, 0xE1, /* 0xA8-0xAB */
+ 0xC7, 0xCB, 0xB2, 0xA5, 0xB4, 0xE9, 0x93, 0xE2, /* 0xAC-0xAF */
+ 0xD7, 0xAB, 0x93, 0xE3, 0x93, 0xE4, 0x93, 0xE5, /* 0xB0-0xB3 */
+ 0x93, 0xE6, 0xC4, 0xEC, 0x93, 0xE7, 0xDF, 0xA2, /* 0xB4-0xB7 */
+ 0xDF, 0xA3, 0x93, 0xE8, 0xDF, 0xA5, 0x93, 0xE9, /* 0xB8-0xBB */
+ 0xBA, 0xB3, 0x93, 0xEA, 0x93, 0xEB, 0x93, 0xEC, /* 0xBC-0xBF */
+ 0xDF, 0xA6, 0x93, 0xED, 0xC0, 0xDE, 0x93, 0xEE, /* 0xC0-0xC3 */
+ 0x93, 0xEF, 0xC9, 0xC3, 0x93, 0xF0, 0x93, 0xF1, /* 0xC4-0xC7 */
+ 0x93, 0xF2, 0x93, 0xF3, 0x93, 0xF4, 0x93, 0xF5, /* 0xC8-0xCB */
+ 0x93, 0xF6, 0xB2, 0xD9, 0xC7, 0xE6, 0x93, 0xF7, /* 0xCC-0xCF */
+ 0xDF, 0xA7, 0x93, 0xF8, 0xC7, 0xDC, 0x93, 0xF9, /* 0xD0-0xD3 */
+ 0x93, 0xFA, 0x93, 0xFB, 0x93, 0xFC, 0xDF, 0xA8, /* 0xD4-0xD7 */
+ 0xEB, 0xA2, 0x93, 0xFD, 0x93, 0xFE, 0x94, 0x40, /* 0xD8-0xDB */
+ 0x94, 0x41, 0x94, 0x42, 0xCB, 0xD3, 0x94, 0x43, /* 0xDC-0xDF */
+ 0x94, 0x44, 0x94, 0x45, 0xDF, 0xAA, 0x94, 0x46, /* 0xE0-0xE3 */
+ 0xDF, 0xA9, 0x94, 0x47, 0xB2, 0xC1, 0x94, 0x48, /* 0xE4-0xE7 */
+ 0x94, 0x49, 0x94, 0x4A, 0x94, 0x4B, 0x94, 0x4C, /* 0xE8-0xEB */
+ 0x94, 0x4D, 0x94, 0x4E, 0x94, 0x4F, 0x94, 0x50, /* 0xEC-0xEF */
+ 0x94, 0x51, 0x94, 0x52, 0x94, 0x53, 0x94, 0x54, /* 0xF0-0xF3 */
+ 0x94, 0x55, 0x94, 0x56, 0x94, 0x57, 0x94, 0x58, /* 0xF4-0xF7 */
+ 0x94, 0x59, 0x94, 0x5A, 0x94, 0x5B, 0x94, 0x5C, /* 0xF8-0xFB */
+ 0x94, 0x5D, 0x94, 0x5E, 0x94, 0x5F, 0x94, 0x60, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_65[512] = {
+ 0xC5, 0xCA, 0x94, 0x61, 0x94, 0x62, 0x94, 0x63, /* 0x00-0x03 */
+ 0x94, 0x64, 0x94, 0x65, 0x94, 0x66, 0x94, 0x67, /* 0x04-0x07 */
+ 0x94, 0x68, 0xDF, 0xAB, 0x94, 0x69, 0x94, 0x6A, /* 0x08-0x0B */
+ 0x94, 0x6B, 0x94, 0x6C, 0x94, 0x6D, 0x94, 0x6E, /* 0x0C-0x0F */
+ 0x94, 0x6F, 0x94, 0x70, 0xD4, 0xDC, 0x94, 0x71, /* 0x10-0x13 */
+ 0x94, 0x72, 0x94, 0x73, 0x94, 0x74, 0x94, 0x75, /* 0x14-0x17 */
+ 0xC8, 0xC1, 0x94, 0x76, 0x94, 0x77, 0x94, 0x78, /* 0x18-0x1B */
+ 0x94, 0x79, 0x94, 0x7A, 0x94, 0x7B, 0x94, 0x7C, /* 0x1C-0x1F */
+ 0x94, 0x7D, 0x94, 0x7E, 0x94, 0x80, 0x94, 0x81, /* 0x20-0x23 */
+ 0x94, 0x82, 0xDF, 0xAC, 0x94, 0x83, 0x94, 0x84, /* 0x24-0x27 */
+ 0x94, 0x85, 0x94, 0x86, 0x94, 0x87, 0xBE, 0xF0, /* 0x28-0x2B */
+ 0x94, 0x88, 0x94, 0x89, 0xDF, 0xAD, 0xD6, 0xA7, /* 0x2C-0x2F */
+ 0x94, 0x8A, 0x94, 0x8B, 0x94, 0x8C, 0x94, 0x8D, /* 0x30-0x33 */
+ 0xEA, 0xB7, 0xEB, 0xB6, 0xCA, 0xD5, 0x94, 0x8E, /* 0x34-0x37 */
+ 0xD8, 0xFC, 0xB8, 0xC4, 0x94, 0x8F, 0xB9, 0xA5, /* 0x38-0x3B */
+ 0x94, 0x90, 0x94, 0x91, 0xB7, 0xC5, 0xD5, 0xFE, /* 0x3C-0x3F */
+ 0x94, 0x92, 0x94, 0x93, 0x94, 0x94, 0x94, 0x95, /* 0x40-0x43 */
+ 0x94, 0x96, 0xB9, 0xCA, 0x94, 0x97, 0x94, 0x98, /* 0x44-0x47 */
+ 0xD0, 0xA7, 0xF4, 0xCD, 0x94, 0x99, 0x94, 0x9A, /* 0x48-0x4B */
+ 0xB5, 0xD0, 0x94, 0x9B, 0x94, 0x9C, 0xC3, 0xF4, /* 0x4C-0x4F */
+ 0x94, 0x9D, 0xBE, 0xC8, 0x94, 0x9E, 0x94, 0x9F, /* 0x50-0x53 */
+ 0x94, 0xA0, 0xEB, 0xB7, 0xB0, 0xBD, 0x94, 0xA1, /* 0x54-0x57 */
+ 0x94, 0xA2, 0xBD, 0xCC, 0x94, 0xA3, 0xC1, 0xB2, /* 0x58-0x5B */
+ 0x94, 0xA4, 0xB1, 0xD6, 0xB3, 0xA8, 0x94, 0xA5, /* 0x5C-0x5F */
+ 0x94, 0xA6, 0x94, 0xA7, 0xB8, 0xD2, 0xC9, 0xA2, /* 0x60-0x63 */
+ 0x94, 0xA8, 0x94, 0xA9, 0xB6, 0xD8, 0x94, 0xAA, /* 0x64-0x67 */
+ 0x94, 0xAB, 0x94, 0xAC, 0x94, 0xAD, 0xEB, 0xB8, /* 0x68-0x6B */
+ 0xBE, 0xB4, 0x94, 0xAE, 0x94, 0xAF, 0x94, 0xB0, /* 0x6C-0x6F */
+ 0xCA, 0xFD, 0x94, 0xB1, 0xC7, 0xC3, 0x94, 0xB2, /* 0x70-0x73 */
+ 0xD5, 0xFB, 0x94, 0xB3, 0x94, 0xB4, 0xB7, 0xF3, /* 0x74-0x77 */
+ 0x94, 0xB5, 0x94, 0xB6, 0x94, 0xB7, 0x94, 0xB8, /* 0x78-0x7B */
+ 0x94, 0xB9, 0x94, 0xBA, 0x94, 0xBB, 0x94, 0xBC, /* 0x7C-0x7F */
+
+ 0x94, 0xBD, 0x94, 0xBE, 0x94, 0xBF, 0x94, 0xC0, /* 0x80-0x83 */
+ 0x94, 0xC1, 0x94, 0xC2, 0x94, 0xC3, 0xCE, 0xC4, /* 0x84-0x87 */
+ 0x94, 0xC4, 0x94, 0xC5, 0x94, 0xC6, 0xD5, 0xAB, /* 0x88-0x8B */
+ 0xB1, 0xF3, 0x94, 0xC7, 0x94, 0xC8, 0x94, 0xC9, /* 0x8C-0x8F */
+ 0xEC, 0xB3, 0xB0, 0xDF, 0x94, 0xCA, 0xEC, 0xB5, /* 0x90-0x93 */
+ 0x94, 0xCB, 0x94, 0xCC, 0x94, 0xCD, 0xB6, 0xB7, /* 0x94-0x97 */
+ 0x94, 0xCE, 0xC1, 0xCF, 0x94, 0xCF, 0xF5, 0xFA, /* 0x98-0x9B */
+ 0xD0, 0xB1, 0x94, 0xD0, 0x94, 0xD1, 0xD5, 0xE5, /* 0x9C-0x9F */
+ 0x94, 0xD2, 0xCE, 0xD3, 0x94, 0xD3, 0x94, 0xD4, /* 0xA0-0xA3 */
+ 0xBD, 0xEF, 0xB3, 0xE2, 0x94, 0xD5, 0xB8, 0xAB, /* 0xA4-0xA7 */
+ 0x94, 0xD6, 0xD5, 0xB6, 0x94, 0xD7, 0xED, 0xBD, /* 0xA8-0xAB */
+ 0x94, 0xD8, 0xB6, 0xCF, 0x94, 0xD9, 0xCB, 0xB9, /* 0xAC-0xAF */
+ 0xD0, 0xC2, 0x94, 0xDA, 0x94, 0xDB, 0x94, 0xDC, /* 0xB0-0xB3 */
+ 0x94, 0xDD, 0x94, 0xDE, 0x94, 0xDF, 0x94, 0xE0, /* 0xB4-0xB7 */
+ 0x94, 0xE1, 0xB7, 0xBD, 0x94, 0xE2, 0x94, 0xE3, /* 0xB8-0xBB */
+ 0xEC, 0xB6, 0xCA, 0xA9, 0x94, 0xE4, 0x94, 0xE5, /* 0xBC-0xBF */
+ 0x94, 0xE6, 0xC5, 0xD4, 0x94, 0xE7, 0xEC, 0xB9, /* 0xC0-0xC3 */
+ 0xEC, 0xB8, 0xC2, 0xC3, 0xEC, 0xB7, 0x94, 0xE8, /* 0xC4-0xC7 */
+ 0x94, 0xE9, 0x94, 0xEA, 0x94, 0xEB, 0xD0, 0xFD, /* 0xC8-0xCB */
+ 0xEC, 0xBA, 0x94, 0xEC, 0xEC, 0xBB, 0xD7, 0xE5, /* 0xCC-0xCF */
+ 0x94, 0xED, 0x94, 0xEE, 0xEC, 0xBC, 0x94, 0xEF, /* 0xD0-0xD3 */
+ 0x94, 0xF0, 0x94, 0xF1, 0xEC, 0xBD, 0xC6, 0xEC, /* 0xD4-0xD7 */
+ 0x94, 0xF2, 0x94, 0xF3, 0x94, 0xF4, 0x94, 0xF5, /* 0xD8-0xDB */
+ 0x94, 0xF6, 0x94, 0xF7, 0x94, 0xF8, 0x94, 0xF9, /* 0xDC-0xDF */
+ 0xCE, 0xDE, 0x94, 0xFA, 0xBC, 0xC8, 0x94, 0xFB, /* 0xE0-0xE3 */
+ 0x94, 0xFC, 0xC8, 0xD5, 0xB5, 0xA9, 0xBE, 0xC9, /* 0xE4-0xE7 */
+ 0xD6, 0xBC, 0xD4, 0xE7, 0x94, 0xFD, 0x94, 0xFE, /* 0xE8-0xEB */
+ 0xD1, 0xAE, 0xD0, 0xF1, 0xEA, 0xB8, 0xEA, 0xB9, /* 0xEC-0xEF */
+ 0xEA, 0xBA, 0xBA, 0xB5, 0x95, 0x40, 0x95, 0x41, /* 0xF0-0xF3 */
+ 0x95, 0x42, 0x95, 0x43, 0xCA, 0xB1, 0xBF, 0xF5, /* 0xF4-0xF7 */
+ 0x95, 0x44, 0x95, 0x45, 0xCD, 0xFA, 0x95, 0x46, /* 0xF8-0xFB */
+ 0x95, 0x47, 0x95, 0x48, 0x95, 0x49, 0x95, 0x4A, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_66[512] = {
+ 0xEA, 0xC0, 0x95, 0x4B, 0xB0, 0xBA, 0xEA, 0xBE, /* 0x00-0x03 */
+ 0x95, 0x4C, 0x95, 0x4D, 0xC0, 0xA5, 0x95, 0x4E, /* 0x04-0x07 */
+ 0x95, 0x4F, 0x95, 0x50, 0xEA, 0xBB, 0x95, 0x51, /* 0x08-0x0B */
+ 0xB2, 0xFD, 0x95, 0x52, 0xC3, 0xF7, 0xBB, 0xE8, /* 0x0C-0x0F */
+ 0x95, 0x53, 0x95, 0x54, 0x95, 0x55, 0xD2, 0xD7, /* 0x10-0x13 */
+ 0xCE, 0xF4, 0xEA, 0xBF, 0x95, 0x56, 0x95, 0x57, /* 0x14-0x17 */
+ 0x95, 0x58, 0xEA, 0xBC, 0x95, 0x59, 0x95, 0x5A, /* 0x18-0x1B */
+ 0x95, 0x5B, 0xEA, 0xC3, 0x95, 0x5C, 0xD0, 0xC7, /* 0x1C-0x1F */
+ 0xD3, 0xB3, 0x95, 0x5D, 0x95, 0x5E, 0x95, 0x5F, /* 0x20-0x23 */
+ 0x95, 0x60, 0xB4, 0xBA, 0x95, 0x61, 0xC3, 0xC1, /* 0x24-0x27 */
+ 0xD7, 0xF2, 0x95, 0x62, 0x95, 0x63, 0x95, 0x64, /* 0x28-0x2B */
+ 0x95, 0x65, 0xD5, 0xD1, 0x95, 0x66, 0xCA, 0xC7, /* 0x2C-0x2F */
+ 0x95, 0x67, 0xEA, 0xC5, 0x95, 0x68, 0x95, 0x69, /* 0x30-0x33 */
+ 0xEA, 0xC4, 0xEA, 0xC7, 0xEA, 0xC6, 0x95, 0x6A, /* 0x34-0x37 */
+ 0x95, 0x6B, 0x95, 0x6C, 0x95, 0x6D, 0x95, 0x6E, /* 0x38-0x3B */
+ 0xD6, 0xE7, 0x95, 0x6F, 0xCF, 0xD4, 0x95, 0x70, /* 0x3C-0x3F */
+ 0x95, 0x71, 0xEA, 0xCB, 0x95, 0x72, 0xBB, 0xCE, /* 0x40-0x43 */
+ 0x95, 0x73, 0x95, 0x74, 0x95, 0x75, 0x95, 0x76, /* 0x44-0x47 */
+ 0x95, 0x77, 0x95, 0x78, 0x95, 0x79, 0xBD, 0xFA, /* 0x48-0x4B */
+ 0xC9, 0xCE, 0x95, 0x7A, 0x95, 0x7B, 0xEA, 0xCC, /* 0x4C-0x4F */
+ 0x95, 0x7C, 0x95, 0x7D, 0xC9, 0xB9, 0xCF, 0xFE, /* 0x50-0x53 */
+ 0xEA, 0xCA, 0xD4, 0xCE, 0xEA, 0xCD, 0xEA, 0xCF, /* 0x54-0x57 */
+ 0x95, 0x7E, 0x95, 0x80, 0xCD, 0xED, 0x95, 0x81, /* 0x58-0x5B */
+ 0x95, 0x82, 0x95, 0x83, 0x95, 0x84, 0xEA, 0xC9, /* 0x5C-0x5F */
+ 0x95, 0x85, 0xEA, 0xCE, 0x95, 0x86, 0x95, 0x87, /* 0x60-0x63 */
+ 0xCE, 0xEE, 0x95, 0x88, 0xBB, 0xDE, 0x95, 0x89, /* 0x64-0x67 */
+ 0xB3, 0xBF, 0x95, 0x8A, 0x95, 0x8B, 0x95, 0x8C, /* 0x68-0x6B */
+ 0x95, 0x8D, 0x95, 0x8E, 0xC6, 0xD5, 0xBE, 0xB0, /* 0x6C-0x6F */
+ 0xCE, 0xFA, 0x95, 0x8F, 0x95, 0x90, 0x95, 0x91, /* 0x70-0x73 */
+ 0xC7, 0xE7, 0x95, 0x92, 0xBE, 0xA7, 0xEA, 0xD0, /* 0x74-0x77 */
+ 0x95, 0x93, 0x95, 0x94, 0xD6, 0xC7, 0x95, 0x95, /* 0x78-0x7B */
+ 0x95, 0x96, 0x95, 0x97, 0xC1, 0xC0, 0x95, 0x98, /* 0x7C-0x7F */
+
+ 0x95, 0x99, 0x95, 0x9A, 0xD4, 0xDD, 0x95, 0x9B, /* 0x80-0x83 */
+ 0xEA, 0xD1, 0x95, 0x9C, 0x95, 0x9D, 0xCF, 0xBE, /* 0x84-0x87 */
+ 0x95, 0x9E, 0x95, 0x9F, 0x95, 0xA0, 0x95, 0xA1, /* 0x88-0x8B */
+ 0xEA, 0xD2, 0x95, 0xA2, 0x95, 0xA3, 0x95, 0xA4, /* 0x8C-0x8F */
+ 0x95, 0xA5, 0xCA, 0xEE, 0x95, 0xA6, 0x95, 0xA7, /* 0x90-0x93 */
+ 0x95, 0xA8, 0x95, 0xA9, 0xC5, 0xAF, 0xB0, 0xB5, /* 0x94-0x97 */
+ 0x95, 0xAA, 0x95, 0xAB, 0x95, 0xAC, 0x95, 0xAD, /* 0x98-0x9B */
+ 0x95, 0xAE, 0xEA, 0xD4, 0x95, 0xAF, 0x95, 0xB0, /* 0x9C-0x9F */
+ 0x95, 0xB1, 0x95, 0xB2, 0x95, 0xB3, 0x95, 0xB4, /* 0xA0-0xA3 */
+ 0x95, 0xB5, 0x95, 0xB6, 0x95, 0xB7, 0xEA, 0xD3, /* 0xA4-0xA7 */
+ 0xF4, 0xDF, 0x95, 0xB8, 0x95, 0xB9, 0x95, 0xBA, /* 0xA8-0xAB */
+ 0x95, 0xBB, 0x95, 0xBC, 0xC4, 0xBA, 0x95, 0xBD, /* 0xAC-0xAF */
+ 0x95, 0xBE, 0x95, 0xBF, 0x95, 0xC0, 0x95, 0xC1, /* 0xB0-0xB3 */
+ 0xB1, 0xA9, 0x95, 0xC2, 0x95, 0xC3, 0x95, 0xC4, /* 0xB4-0xB7 */
+ 0x95, 0xC5, 0xE5, 0xDF, 0x95, 0xC6, 0x95, 0xC7, /* 0xB8-0xBB */
+ 0x95, 0xC8, 0x95, 0xC9, 0xEA, 0xD5, 0x95, 0xCA, /* 0xBC-0xBF */
+ 0x95, 0xCB, 0x95, 0xCC, 0x95, 0xCD, 0x95, 0xCE, /* 0xC0-0xC3 */
+ 0x95, 0xCF, 0x95, 0xD0, 0x95, 0xD1, 0x95, 0xD2, /* 0xC4-0xC7 */
+ 0x95, 0xD3, 0x95, 0xD4, 0x95, 0xD5, 0x95, 0xD6, /* 0xC8-0xCB */
+ 0x95, 0xD7, 0x95, 0xD8, 0x95, 0xD9, 0x95, 0xDA, /* 0xCC-0xCF */
+ 0x95, 0xDB, 0x95, 0xDC, 0x95, 0xDD, 0x95, 0xDE, /* 0xD0-0xD3 */
+ 0x95, 0xDF, 0x95, 0xE0, 0x95, 0xE1, 0x95, 0xE2, /* 0xD4-0xD7 */
+ 0x95, 0xE3, 0xCA, 0xEF, 0x95, 0xE4, 0xEA, 0xD6, /* 0xD8-0xDB */
+ 0xEA, 0xD7, 0xC6, 0xD8, 0x95, 0xE5, 0x95, 0xE6, /* 0xDC-0xDF */
+ 0x95, 0xE7, 0x95, 0xE8, 0x95, 0xE9, 0x95, 0xEA, /* 0xE0-0xE3 */
+ 0x95, 0xEB, 0x95, 0xEC, 0xEA, 0xD8, 0x95, 0xED, /* 0xE4-0xE7 */
+ 0x95, 0xEE, 0xEA, 0xD9, 0x95, 0xEF, 0x95, 0xF0, /* 0xE8-0xEB */
+ 0x95, 0xF1, 0x95, 0xF2, 0x95, 0xF3, 0x95, 0xF4, /* 0xEC-0xEF */
+ 0xD4, 0xBB, 0x95, 0xF5, 0xC7, 0xFA, 0xD2, 0xB7, /* 0xF0-0xF3 */
+ 0xB8, 0xFC, 0x95, 0xF6, 0x95, 0xF7, 0xEA, 0xC2, /* 0xF4-0xF7 */
+ 0x95, 0xF8, 0xB2, 0xDC, 0x95, 0xF9, 0x95, 0xFA, /* 0xF8-0xFB */
+ 0xC2, 0xFC, 0x95, 0xFB, 0xD4, 0xF8, 0xCC, 0xE6, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_67[512] = {
+ 0xD7, 0xEE, 0x95, 0xFC, 0x95, 0xFD, 0x95, 0xFE, /* 0x00-0x03 */
+ 0x96, 0x40, 0x96, 0x41, 0x96, 0x42, 0x96, 0x43, /* 0x04-0x07 */
+ 0xD4, 0xC2, 0xD3, 0xD0, 0xEB, 0xC3, 0xC5, 0xF3, /* 0x08-0x0B */
+ 0x96, 0x44, 0xB7, 0xFE, 0x96, 0x45, 0x96, 0x46, /* 0x0C-0x0F */
+ 0xEB, 0xD4, 0x96, 0x47, 0x96, 0x48, 0x96, 0x49, /* 0x10-0x13 */
+ 0xCB, 0xB7, 0xEB, 0xDE, 0x96, 0x4A, 0xC0, 0xCA, /* 0x14-0x17 */
+ 0x96, 0x4B, 0x96, 0x4C, 0x96, 0x4D, 0xCD, 0xFB, /* 0x18-0x1B */
+ 0x96, 0x4E, 0xB3, 0xAF, 0x96, 0x4F, 0xC6, 0xDA, /* 0x1C-0x1F */
+ 0x96, 0x50, 0x96, 0x51, 0x96, 0x52, 0x96, 0x53, /* 0x20-0x23 */
+ 0x96, 0x54, 0x96, 0x55, 0xEB, 0xFC, 0x96, 0x56, /* 0x24-0x27 */
+ 0xC4, 0xBE, 0x96, 0x57, 0xCE, 0xB4, 0xC4, 0xA9, /* 0x28-0x2B */
+ 0xB1, 0xBE, 0xD4, 0xFD, 0x96, 0x58, 0xCA, 0xF5, /* 0x2C-0x2F */
+ 0x96, 0x59, 0xD6, 0xEC, 0x96, 0x5A, 0x96, 0x5B, /* 0x30-0x33 */
+ 0xC6, 0xD3, 0xB6, 0xE4, 0x96, 0x5C, 0x96, 0x5D, /* 0x34-0x37 */
+ 0x96, 0x5E, 0x96, 0x5F, 0xBB, 0xFA, 0x96, 0x60, /* 0x38-0x3B */
+ 0x96, 0x61, 0xD0, 0xE0, 0x96, 0x62, 0x96, 0x63, /* 0x3C-0x3F */
+ 0xC9, 0xB1, 0x96, 0x64, 0xD4, 0xD3, 0xC8, 0xA8, /* 0x40-0x43 */
+ 0x96, 0x65, 0x96, 0x66, 0xB8, 0xCB, 0x96, 0x67, /* 0x44-0x47 */
+ 0xE8, 0xBE, 0xC9, 0xBC, 0x96, 0x68, 0x96, 0x69, /* 0x48-0x4B */
+ 0xE8, 0xBB, 0x96, 0x6A, 0xC0, 0xEE, 0xD0, 0xD3, /* 0x4C-0x4F */
+ 0xB2, 0xC4, 0xB4, 0xE5, 0x96, 0x6B, 0xE8, 0xBC, /* 0x50-0x53 */
+ 0x96, 0x6C, 0x96, 0x6D, 0xD5, 0xC8, 0x96, 0x6E, /* 0x54-0x57 */
+ 0x96, 0x6F, 0x96, 0x70, 0x96, 0x71, 0x96, 0x72, /* 0x58-0x5B */
+ 0xB6, 0xC5, 0x96, 0x73, 0xE8, 0xBD, 0xCA, 0xF8, /* 0x5C-0x5F */
+ 0xB8, 0xDC, 0xCC, 0xF5, 0x96, 0x74, 0x96, 0x75, /* 0x60-0x63 */
+ 0x96, 0x76, 0xC0, 0xB4, 0x96, 0x77, 0x96, 0x78, /* 0x64-0x67 */
+ 0xD1, 0xEE, 0xE8, 0xBF, 0xE8, 0xC2, 0x96, 0x79, /* 0x68-0x6B */
+ 0x96, 0x7A, 0xBA, 0xBC, 0x96, 0x7B, 0xB1, 0xAD, /* 0x6C-0x6F */
+ 0xBD, 0xDC, 0x96, 0x7C, 0xEA, 0xBD, 0xE8, 0xC3, /* 0x70-0x73 */
+ 0x96, 0x7D, 0xE8, 0xC6, 0x96, 0x7E, 0xE8, 0xCB, /* 0x74-0x77 */
+ 0x96, 0x80, 0x96, 0x81, 0x96, 0x82, 0x96, 0x83, /* 0x78-0x7B */
+ 0xE8, 0xCC, 0x96, 0x84, 0xCB, 0xC9, 0xB0, 0xE5, /* 0x7C-0x7F */
+
+ 0x96, 0x85, 0xBC, 0xAB, 0x96, 0x86, 0x96, 0x87, /* 0x80-0x83 */
+ 0xB9, 0xB9, 0x96, 0x88, 0x96, 0x89, 0xE8, 0xC1, /* 0x84-0x87 */
+ 0x96, 0x8A, 0xCD, 0xF7, 0x96, 0x8B, 0xE8, 0xCA, /* 0x88-0x8B */
+ 0x96, 0x8C, 0x96, 0x8D, 0x96, 0x8E, 0x96, 0x8F, /* 0x8C-0x8F */
+ 0xCE, 0xF6, 0x96, 0x90, 0x96, 0x91, 0x96, 0x92, /* 0x90-0x93 */
+ 0x96, 0x93, 0xD5, 0xED, 0x96, 0x94, 0xC1, 0xD6, /* 0x94-0x97 */
+ 0xE8, 0xC4, 0x96, 0x95, 0xC3, 0xB6, 0x96, 0x96, /* 0x98-0x9B */
+ 0xB9, 0xFB, 0xD6, 0xA6, 0xE8, 0xC8, 0x96, 0x97, /* 0x9C-0x9F */
+ 0x96, 0x98, 0x96, 0x99, 0xCA, 0xE0, 0xD4, 0xE6, /* 0xA0-0xA3 */
+ 0x96, 0x9A, 0xE8, 0xC0, 0x96, 0x9B, 0xE8, 0xC5, /* 0xA4-0xA7 */
+ 0xE8, 0xC7, 0x96, 0x9C, 0xC7, 0xB9, 0xB7, 0xE3, /* 0xA8-0xAB */
+ 0x96, 0x9D, 0xE8, 0xC9, 0x96, 0x9E, 0xBF, 0xDD, /* 0xAC-0xAF */
+ 0xE8, 0xD2, 0x96, 0x9F, 0x96, 0xA0, 0xE8, 0xD7, /* 0xB0-0xB3 */
+ 0x96, 0xA1, 0xE8, 0xD5, 0xBC, 0xDC, 0xBC, 0xCF, /* 0xB4-0xB7 */
+ 0xE8, 0xDB, 0x96, 0xA2, 0x96, 0xA3, 0x96, 0xA4, /* 0xB8-0xBB */
+ 0x96, 0xA5, 0x96, 0xA6, 0x96, 0xA7, 0x96, 0xA8, /* 0xBC-0xBF */
+ 0x96, 0xA9, 0xE8, 0xDE, 0x96, 0xAA, 0xE8, 0xDA, /* 0xC0-0xC3 */
+ 0xB1, 0xFA, 0x96, 0xAB, 0x96, 0xAC, 0x96, 0xAD, /* 0xC4-0xC7 */
+ 0x96, 0xAE, 0x96, 0xAF, 0x96, 0xB0, 0x96, 0xB1, /* 0xC8-0xCB */
+ 0x96, 0xB2, 0x96, 0xB3, 0x96, 0xB4, 0xB0, 0xD8, /* 0xCC-0xCF */
+ 0xC4, 0xB3, 0xB8, 0xCC, 0xC6, 0xE2, 0xC8, 0xBE, /* 0xD0-0xD3 */
+ 0xC8, 0xE1, 0x96, 0xB5, 0x96, 0xB6, 0x96, 0xB7, /* 0xD4-0xD7 */
+ 0xE8, 0xCF, 0xE8, 0xD4, 0xE8, 0xD6, 0x96, 0xB8, /* 0xD8-0xDB */
+ 0xB9, 0xF1, 0xE8, 0xD8, 0xD7, 0xF5, 0x96, 0xB9, /* 0xDC-0xDF */
+ 0xC4, 0xFB, 0x96, 0xBA, 0xE8, 0xDC, 0x96, 0xBB, /* 0xE0-0xE3 */
+ 0x96, 0xBC, 0xB2, 0xE9, 0x96, 0xBD, 0x96, 0xBE, /* 0xE4-0xE7 */
+ 0x96, 0xBF, 0xE8, 0xD1, 0x96, 0xC0, 0x96, 0xC1, /* 0xE8-0xEB */
+ 0xBC, 0xED, 0x96, 0xC2, 0x96, 0xC3, 0xBF, 0xC2, /* 0xEC-0xEF */
+ 0xE8, 0xCD, 0xD6, 0xF9, 0x96, 0xC4, 0xC1, 0xF8, /* 0xF0-0xF3 */
+ 0xB2, 0xF1, 0x96, 0xC5, 0x96, 0xC6, 0x96, 0xC7, /* 0xF4-0xF7 */
+ 0x96, 0xC8, 0x96, 0xC9, 0x96, 0xCA, 0x96, 0xCB, /* 0xF8-0xFB */
+ 0x96, 0xCC, 0xE8, 0xDF, 0x96, 0xCD, 0xCA, 0xC1, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_68[512] = {
+ 0xE8, 0xD9, 0x96, 0xCE, 0x96, 0xCF, 0x96, 0xD0, /* 0x00-0x03 */
+ 0x96, 0xD1, 0xD5, 0xA4, 0x96, 0xD2, 0xB1, 0xEA, /* 0x04-0x07 */
+ 0xD5, 0xBB, 0xE8, 0xCE, 0xE8, 0xD0, 0xB6, 0xB0, /* 0x08-0x0B */
+ 0xE8, 0xD3, 0x96, 0xD3, 0xE8, 0xDD, 0xC0, 0xB8, /* 0x0C-0x0F */
+ 0x96, 0xD4, 0xCA, 0xF7, 0x96, 0xD5, 0xCB, 0xA8, /* 0x10-0x13 */
+ 0x96, 0xD6, 0x96, 0xD7, 0xC6, 0xDC, 0xC0, 0xF5, /* 0x14-0x17 */
+ 0x96, 0xD8, 0x96, 0xD9, 0x96, 0xDA, 0x96, 0xDB, /* 0x18-0x1B */
+ 0x96, 0xDC, 0xE8, 0xE9, 0x96, 0xDD, 0x96, 0xDE, /* 0x1C-0x1F */
+ 0x96, 0xDF, 0xD0, 0xA3, 0x96, 0xE0, 0x96, 0xE1, /* 0x20-0x23 */
+ 0x96, 0xE2, 0x96, 0xE3, 0x96, 0xE4, 0x96, 0xE5, /* 0x24-0x27 */
+ 0x96, 0xE6, 0xE8, 0xF2, 0xD6, 0xEA, 0x96, 0xE7, /* 0x28-0x2B */
+ 0x96, 0xE8, 0x96, 0xE9, 0x96, 0xEA, 0x96, 0xEB, /* 0x2C-0x2F */
+ 0x96, 0xEC, 0x96, 0xED, 0xE8, 0xE0, 0xE8, 0xE1, /* 0x30-0x33 */
+ 0x96, 0xEE, 0x96, 0xEF, 0x96, 0xF0, 0xD1, 0xF9, /* 0x34-0x37 */
+ 0xBA, 0xCB, 0xB8, 0xF9, 0x96, 0xF1, 0x96, 0xF2, /* 0x38-0x3B */
+ 0xB8, 0xF1, 0xD4, 0xD4, 0xE8, 0xEF, 0x96, 0xF3, /* 0x3C-0x3F */
+ 0xE8, 0xEE, 0xE8, 0xEC, 0xB9, 0xF0, 0xCC, 0xD2, /* 0x40-0x43 */
+ 0xE8, 0xE6, 0xCE, 0xA6, 0xBF, 0xF2, 0x96, 0xF4, /* 0x44-0x47 */
+ 0xB0, 0xB8, 0xE8, 0xF1, 0xE8, 0xF0, 0x96, 0xF5, /* 0x48-0x4B */
+ 0xD7, 0xC0, 0x96, 0xF6, 0xE8, 0xE4, 0x96, 0xF7, /* 0x4C-0x4F */
+ 0xCD, 0xA9, 0xC9, 0xA3, 0x96, 0xF8, 0xBB, 0xB8, /* 0x50-0x53 */
+ 0xBD, 0xDB, 0xE8, 0xEA, 0x96, 0xF9, 0x96, 0xFA, /* 0x54-0x57 */
+ 0x96, 0xFB, 0x96, 0xFC, 0x96, 0xFD, 0x96, 0xFE, /* 0x58-0x5B */
+ 0x97, 0x40, 0x97, 0x41, 0x97, 0x42, 0x97, 0x43, /* 0x5C-0x5F */
+ 0xE8, 0xE2, 0xE8, 0xE3, 0xE8, 0xE5, 0xB5, 0xB5, /* 0x60-0x63 */
+ 0xE8, 0xE7, 0xC7, 0xC5, 0xE8, 0xEB, 0xE8, 0xED, /* 0x64-0x67 */
+ 0xBD, 0xB0, 0xD7, 0xAE, 0x97, 0x44, 0xE8, 0xF8, /* 0x68-0x6B */
+ 0x97, 0x45, 0x97, 0x46, 0x97, 0x47, 0x97, 0x48, /* 0x6C-0x6F */
+ 0x97, 0x49, 0x97, 0x4A, 0x97, 0x4B, 0x97, 0x4C, /* 0x70-0x73 */
+ 0xE8, 0xF5, 0x97, 0x4D, 0xCD, 0xB0, 0xE8, 0xF6, /* 0x74-0x77 */
+ 0x97, 0x4E, 0x97, 0x4F, 0x97, 0x50, 0x97, 0x51, /* 0x78-0x7B */
+ 0x97, 0x52, 0x97, 0x53, 0x97, 0x54, 0x97, 0x55, /* 0x7C-0x7F */
+
+ 0x97, 0x56, 0xC1, 0xBA, 0x97, 0x57, 0xE8, 0xE8, /* 0x80-0x83 */
+ 0x97, 0x58, 0xC3, 0xB7, 0xB0, 0xF0, 0x97, 0x59, /* 0x84-0x87 */
+ 0x97, 0x5A, 0x97, 0x5B, 0x97, 0x5C, 0x97, 0x5D, /* 0x88-0x8B */
+ 0x97, 0x5E, 0x97, 0x5F, 0x97, 0x60, 0xE8, 0xF4, /* 0x8C-0x8F */
+ 0x97, 0x61, 0x97, 0x62, 0x97, 0x63, 0xE8, 0xF7, /* 0x90-0x93 */
+ 0x97, 0x64, 0x97, 0x65, 0x97, 0x66, 0xB9, 0xA3, /* 0x94-0x97 */
+ 0x97, 0x67, 0x97, 0x68, 0x97, 0x69, 0x97, 0x6A, /* 0x98-0x9B */
+ 0x97, 0x6B, 0x97, 0x6C, 0x97, 0x6D, 0x97, 0x6E, /* 0x9C-0x9F */
+ 0x97, 0x6F, 0x97, 0x70, 0xC9, 0xD2, 0x97, 0x71, /* 0xA0-0xA3 */
+ 0x97, 0x72, 0x97, 0x73, 0xC3, 0xCE, 0xCE, 0xE0, /* 0xA4-0xA7 */
+ 0xC0, 0xE6, 0x97, 0x74, 0x97, 0x75, 0x97, 0x76, /* 0xA8-0xAB */
+ 0x97, 0x77, 0xCB, 0xF3, 0x97, 0x78, 0xCC, 0xDD, /* 0xAC-0xAF */
+ 0xD0, 0xB5, 0x97, 0x79, 0x97, 0x7A, 0xCA, 0xE1, /* 0xB0-0xB3 */
+ 0x97, 0x7B, 0xE8, 0xF3, 0x97, 0x7C, 0x97, 0x7D, /* 0xB4-0xB7 */
+ 0x97, 0x7E, 0x97, 0x80, 0x97, 0x81, 0x97, 0x82, /* 0xB8-0xBB */
+ 0x97, 0x83, 0x97, 0x84, 0x97, 0x85, 0x97, 0x86, /* 0xBC-0xBF */
+ 0xBC, 0xEC, 0x97, 0x87, 0xE8, 0xF9, 0x97, 0x88, /* 0xC0-0xC3 */
+ 0x97, 0x89, 0x97, 0x8A, 0x97, 0x8B, 0x97, 0x8C, /* 0xC4-0xC7 */
+ 0x97, 0x8D, 0xC3, 0xDE, 0x97, 0x8E, 0xC6, 0xE5, /* 0xC8-0xCB */
+ 0x97, 0x8F, 0xB9, 0xF7, 0x97, 0x90, 0x97, 0x91, /* 0xCC-0xCF */
+ 0x97, 0x92, 0x97, 0x93, 0xB0, 0xF4, 0x97, 0x94, /* 0xD0-0xD3 */
+ 0x97, 0x95, 0xD7, 0xD8, 0x97, 0x96, 0x97, 0x97, /* 0xD4-0xD7 */
+ 0xBC, 0xAC, 0x97, 0x98, 0xC5, 0xEF, 0x97, 0x99, /* 0xD8-0xDB */
+ 0x97, 0x9A, 0x97, 0x9B, 0x97, 0x9C, 0x97, 0x9D, /* 0xDC-0xDF */
+ 0xCC, 0xC4, 0x97, 0x9E, 0x97, 0x9F, 0xE9, 0xA6, /* 0xE0-0xE3 */
+ 0x97, 0xA0, 0x97, 0xA1, 0x97, 0xA2, 0x97, 0xA3, /* 0xE4-0xE7 */
+ 0x97, 0xA4, 0x97, 0xA5, 0x97, 0xA6, 0x97, 0xA7, /* 0xE8-0xEB */
+ 0x97, 0xA8, 0x97, 0xA9, 0xC9, 0xAD, 0x97, 0xAA, /* 0xEC-0xEF */
+ 0xE9, 0xA2, 0xC0, 0xE2, 0x97, 0xAB, 0x97, 0xAC, /* 0xF0-0xF3 */
+ 0x97, 0xAD, 0xBF, 0xC3, 0x97, 0xAE, 0x97, 0xAF, /* 0xF4-0xF7 */
+ 0x97, 0xB0, 0xE8, 0xFE, 0xB9, 0xD7, 0x97, 0xB1, /* 0xF8-0xFB */
+ 0xE8, 0xFB, 0x97, 0xB2, 0x97, 0xB3, 0x97, 0xB4, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_69[512] = {
+ 0x97, 0xB5, 0xE9, 0xA4, 0x97, 0xB6, 0x97, 0xB7, /* 0x00-0x03 */
+ 0x97, 0xB8, 0xD2, 0xCE, 0x97, 0xB9, 0x97, 0xBA, /* 0x04-0x07 */
+ 0x97, 0xBB, 0x97, 0xBC, 0x97, 0xBD, 0xE9, 0xA3, /* 0x08-0x0B */
+ 0x97, 0xBE, 0xD6, 0xB2, 0xD7, 0xB5, 0x97, 0xBF, /* 0x0C-0x0F */
+ 0xE9, 0xA7, 0x97, 0xC0, 0xBD, 0xB7, 0x97, 0xC1, /* 0x10-0x13 */
+ 0x97, 0xC2, 0x97, 0xC3, 0x97, 0xC4, 0x97, 0xC5, /* 0x14-0x17 */
+ 0x97, 0xC6, 0x97, 0xC7, 0x97, 0xC8, 0x97, 0xC9, /* 0x18-0x1B */
+ 0x97, 0xCA, 0x97, 0xCB, 0x97, 0xCC, 0xE8, 0xFC, /* 0x1C-0x1F */
+ 0xE8, 0xFD, 0x97, 0xCD, 0x97, 0xCE, 0x97, 0xCF, /* 0x20-0x23 */
+ 0xE9, 0xA1, 0x97, 0xD0, 0x97, 0xD1, 0x97, 0xD2, /* 0x24-0x27 */
+ 0x97, 0xD3, 0x97, 0xD4, 0x97, 0xD5, 0x97, 0xD6, /* 0x28-0x2B */
+ 0x97, 0xD7, 0xCD, 0xD6, 0x97, 0xD8, 0x97, 0xD9, /* 0x2C-0x2F */
+ 0xD2, 0xAC, 0x97, 0xDA, 0x97, 0xDB, 0x97, 0xDC, /* 0x30-0x33 */
+ 0xE9, 0xB2, 0x97, 0xDD, 0x97, 0xDE, 0x97, 0xDF, /* 0x34-0x37 */
+ 0x97, 0xE0, 0xE9, 0xA9, 0x97, 0xE1, 0x97, 0xE2, /* 0x38-0x3B */
+ 0x97, 0xE3, 0xB4, 0xAA, 0x97, 0xE4, 0xB4, 0xBB, /* 0x3C-0x3F */
+ 0x97, 0xE5, 0x97, 0xE6, 0xE9, 0xAB, 0x97, 0xE7, /* 0x40-0x43 */
+ 0x97, 0xE8, 0x97, 0xE9, 0x97, 0xEA, 0x97, 0xEB, /* 0x44-0x47 */
+ 0x97, 0xEC, 0x97, 0xED, 0x97, 0xEE, 0x97, 0xEF, /* 0x48-0x4B */
+ 0x97, 0xF0, 0x97, 0xF1, 0x97, 0xF2, 0x97, 0xF3, /* 0x4C-0x4F */
+ 0x97, 0xF4, 0x97, 0xF5, 0x97, 0xF6, 0x97, 0xF7, /* 0x50-0x53 */
+ 0xD0, 0xA8, 0x97, 0xF8, 0x97, 0xF9, 0xE9, 0xA5, /* 0x54-0x57 */
+ 0x97, 0xFA, 0x97, 0xFB, 0xB3, 0xFE, 0x97, 0xFC, /* 0x58-0x5B */
+ 0x97, 0xFD, 0xE9, 0xAC, 0xC0, 0xE3, 0x97, 0xFE, /* 0x5C-0x5F */
+ 0xE9, 0xAA, 0x98, 0x40, 0x98, 0x41, 0xE9, 0xB9, /* 0x60-0x63 */
+ 0x98, 0x42, 0x98, 0x43, 0xE9, 0xB8, 0x98, 0x44, /* 0x64-0x67 */
+ 0x98, 0x45, 0x98, 0x46, 0x98, 0x47, 0xE9, 0xAE, /* 0x68-0x6B */
+ 0x98, 0x48, 0x98, 0x49, 0xE8, 0xFA, 0x98, 0x4A, /* 0x6C-0x6F */
+ 0x98, 0x4B, 0xE9, 0xA8, 0x98, 0x4C, 0x98, 0x4D, /* 0x70-0x73 */
+ 0x98, 0x4E, 0x98, 0x4F, 0x98, 0x50, 0xBF, 0xAC, /* 0x74-0x77 */
+ 0xE9, 0xB1, 0xE9, 0xBA, 0x98, 0x51, 0x98, 0x52, /* 0x78-0x7B */
+ 0xC2, 0xA5, 0x98, 0x53, 0x98, 0x54, 0x98, 0x55, /* 0x7C-0x7F */
+
+ 0xE9, 0xAF, 0x98, 0x56, 0xB8, 0xC5, 0x98, 0x57, /* 0x80-0x83 */
+ 0xE9, 0xAD, 0x98, 0x58, 0xD3, 0xDC, 0xE9, 0xB4, /* 0x84-0x87 */
+ 0xE9, 0xB5, 0xE9, 0xB7, 0x98, 0x59, 0x98, 0x5A, /* 0x88-0x8B */
+ 0x98, 0x5B, 0xE9, 0xC7, 0x98, 0x5C, 0x98, 0x5D, /* 0x8C-0x8F */
+ 0x98, 0x5E, 0x98, 0x5F, 0x98, 0x60, 0x98, 0x61, /* 0x90-0x93 */
+ 0xC0, 0xC6, 0xE9, 0xC5, 0x98, 0x62, 0x98, 0x63, /* 0x94-0x97 */
+ 0xE9, 0xB0, 0x98, 0x64, 0x98, 0x65, 0xE9, 0xBB, /* 0x98-0x9B */
+ 0xB0, 0xF1, 0x98, 0x66, 0x98, 0x67, 0x98, 0x68, /* 0x9C-0x9F */
+ 0x98, 0x69, 0x98, 0x6A, 0x98, 0x6B, 0x98, 0x6C, /* 0xA0-0xA3 */
+ 0x98, 0x6D, 0x98, 0x6E, 0x98, 0x6F, 0xE9, 0xBC, /* 0xA4-0xA7 */
+ 0xD5, 0xA5, 0x98, 0x70, 0x98, 0x71, 0xE9, 0xBE, /* 0xA8-0xAB */
+ 0x98, 0x72, 0xE9, 0xBF, 0x98, 0x73, 0x98, 0x74, /* 0xAC-0xAF */
+ 0x98, 0x75, 0xE9, 0xC1, 0x98, 0x76, 0x98, 0x77, /* 0xB0-0xB3 */
+ 0xC1, 0xF1, 0x98, 0x78, 0x98, 0x79, 0xC8, 0xB6, /* 0xB4-0xB7 */
+ 0x98, 0x7A, 0x98, 0x7B, 0x98, 0x7C, 0xE9, 0xBD, /* 0xB8-0xBB */
+ 0x98, 0x7D, 0x98, 0x7E, 0x98, 0x80, 0x98, 0x81, /* 0xBC-0xBF */
+ 0x98, 0x82, 0xE9, 0xC2, 0x98, 0x83, 0x98, 0x84, /* 0xC0-0xC3 */
+ 0x98, 0x85, 0x98, 0x86, 0x98, 0x87, 0x98, 0x88, /* 0xC4-0xC7 */
+ 0x98, 0x89, 0x98, 0x8A, 0xE9, 0xC3, 0x98, 0x8B, /* 0xC8-0xCB */
+ 0xE9, 0xB3, 0x98, 0x8C, 0xE9, 0xB6, 0x98, 0x8D, /* 0xCC-0xCF */
+ 0xBB, 0xB1, 0x98, 0x8E, 0x98, 0x8F, 0x98, 0x90, /* 0xD0-0xD3 */
+ 0xE9, 0xC0, 0x98, 0x91, 0x98, 0x92, 0x98, 0x93, /* 0xD4-0xD7 */
+ 0x98, 0x94, 0x98, 0x95, 0x98, 0x96, 0xBC, 0xF7, /* 0xD8-0xDB */
+ 0x98, 0x97, 0x98, 0x98, 0x98, 0x99, 0xE9, 0xC4, /* 0xDC-0xDF */
+ 0xE9, 0xC6, 0x98, 0x9A, 0x98, 0x9B, 0x98, 0x9C, /* 0xE0-0xE3 */
+ 0x98, 0x9D, 0x98, 0x9E, 0x98, 0x9F, 0x98, 0xA0, /* 0xE4-0xE7 */
+ 0x98, 0xA1, 0x98, 0xA2, 0x98, 0xA3, 0x98, 0xA4, /* 0xE8-0xEB */
+ 0x98, 0xA5, 0xE9, 0xCA, 0x98, 0xA6, 0x98, 0xA7, /* 0xEC-0xEF */
+ 0x98, 0xA8, 0x98, 0xA9, 0xE9, 0xCE, 0x98, 0xAA, /* 0xF0-0xF3 */
+ 0x98, 0xAB, 0x98, 0xAC, 0x98, 0xAD, 0x98, 0xAE, /* 0xF4-0xF7 */
+ 0x98, 0xAF, 0x98, 0xB0, 0x98, 0xB1, 0x98, 0xB2, /* 0xF8-0xFB */
+ 0x98, 0xB3, 0xB2, 0xDB, 0x98, 0xB4, 0xE9, 0xC8, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_6A[512] = {
+ 0x98, 0xB5, 0x98, 0xB6, 0x98, 0xB7, 0x98, 0xB8, /* 0x00-0x03 */
+ 0x98, 0xB9, 0x98, 0xBA, 0x98, 0xBB, 0x98, 0xBC, /* 0x04-0x07 */
+ 0x98, 0xBD, 0x98, 0xBE, 0xB7, 0xAE, 0x98, 0xBF, /* 0x08-0x0B */
+ 0x98, 0xC0, 0x98, 0xC1, 0x98, 0xC2, 0x98, 0xC3, /* 0x0C-0x0F */
+ 0x98, 0xC4, 0x98, 0xC5, 0x98, 0xC6, 0x98, 0xC7, /* 0x10-0x13 */
+ 0x98, 0xC8, 0x98, 0xC9, 0x98, 0xCA, 0xE9, 0xCB, /* 0x14-0x17 */
+ 0xE9, 0xCC, 0x98, 0xCB, 0x98, 0xCC, 0x98, 0xCD, /* 0x18-0x1B */
+ 0x98, 0xCE, 0x98, 0xCF, 0x98, 0xD0, 0xD5, 0xC1, /* 0x1C-0x1F */
+ 0x98, 0xD1, 0xC4, 0xA3, 0x98, 0xD2, 0x98, 0xD3, /* 0x20-0x23 */
+ 0x98, 0xD4, 0x98, 0xD5, 0x98, 0xD6, 0x98, 0xD7, /* 0x24-0x27 */
+ 0xE9, 0xD8, 0x98, 0xD8, 0xBA, 0xE1, 0x98, 0xD9, /* 0x28-0x2B */
+ 0x98, 0xDA, 0x98, 0xDB, 0x98, 0xDC, 0xE9, 0xC9, /* 0x2C-0x2F */
+ 0x98, 0xDD, 0xD3, 0xA3, 0x98, 0xDE, 0x98, 0xDF, /* 0x30-0x33 */
+ 0x98, 0xE0, 0xE9, 0xD4, 0x98, 0xE1, 0x98, 0xE2, /* 0x34-0x37 */
+ 0x98, 0xE3, 0x98, 0xE4, 0x98, 0xE5, 0x98, 0xE6, /* 0x38-0x3B */
+ 0x98, 0xE7, 0xE9, 0xD7, 0xE9, 0xD0, 0x98, 0xE8, /* 0x3C-0x3F */
+ 0x98, 0xE9, 0x98, 0xEA, 0x98, 0xEB, 0x98, 0xEC, /* 0x40-0x43 */
+ 0xE9, 0xCF, 0x98, 0xED, 0x98, 0xEE, 0xC7, 0xC1, /* 0x44-0x47 */
+ 0x98, 0xEF, 0x98, 0xF0, 0x98, 0xF1, 0x98, 0xF2, /* 0x48-0x4B */
+ 0x98, 0xF3, 0x98, 0xF4, 0x98, 0xF5, 0x98, 0xF6, /* 0x4C-0x4F */
+ 0xE9, 0xD2, 0x98, 0xF7, 0x98, 0xF8, 0x98, 0xF9, /* 0x50-0x53 */
+ 0x98, 0xFA, 0x98, 0xFB, 0x98, 0xFC, 0x98, 0xFD, /* 0x54-0x57 */
+ 0xE9, 0xD9, 0xB3, 0xC8, 0x98, 0xFE, 0xE9, 0xD3, /* 0x58-0x5B */
+ 0x99, 0x40, 0x99, 0x41, 0x99, 0x42, 0x99, 0x43, /* 0x5C-0x5F */
+ 0x99, 0x44, 0xCF, 0xF0, 0x99, 0x45, 0x99, 0x46, /* 0x60-0x63 */
+ 0x99, 0x47, 0xE9, 0xCD, 0x99, 0x48, 0x99, 0x49, /* 0x64-0x67 */
+ 0x99, 0x4A, 0x99, 0x4B, 0x99, 0x4C, 0x99, 0x4D, /* 0x68-0x6B */
+ 0x99, 0x4E, 0x99, 0x4F, 0x99, 0x50, 0x99, 0x51, /* 0x6C-0x6F */
+ 0x99, 0x52, 0xB3, 0xF7, 0x99, 0x53, 0x99, 0x54, /* 0x70-0x73 */
+ 0x99, 0x55, 0x99, 0x56, 0x99, 0x57, 0x99, 0x58, /* 0x74-0x77 */
+ 0x99, 0x59, 0xE9, 0xD6, 0x99, 0x5A, 0x99, 0x5B, /* 0x78-0x7B */
+ 0xE9, 0xDA, 0x99, 0x5C, 0x99, 0x5D, 0x99, 0x5E, /* 0x7C-0x7F */
+
+ 0xCC, 0xB4, 0x99, 0x5F, 0x99, 0x60, 0x99, 0x61, /* 0x80-0x83 */
+ 0xCF, 0xAD, 0x99, 0x62, 0x99, 0x63, 0x99, 0x64, /* 0x84-0x87 */
+ 0x99, 0x65, 0x99, 0x66, 0x99, 0x67, 0x99, 0x68, /* 0x88-0x8B */
+ 0x99, 0x69, 0x99, 0x6A, 0xE9, 0xD5, 0x99, 0x6B, /* 0x8C-0x8F */
+ 0xE9, 0xDC, 0xE9, 0xDB, 0x99, 0x6C, 0x99, 0x6D, /* 0x90-0x93 */
+ 0x99, 0x6E, 0x99, 0x6F, 0x99, 0x70, 0xE9, 0xDE, /* 0x94-0x97 */
+ 0x99, 0x71, 0x99, 0x72, 0x99, 0x73, 0x99, 0x74, /* 0x98-0x9B */
+ 0x99, 0x75, 0x99, 0x76, 0x99, 0x77, 0x99, 0x78, /* 0x9C-0x9F */
+ 0xE9, 0xD1, 0x99, 0x79, 0x99, 0x7A, 0x99, 0x7B, /* 0xA0-0xA3 */
+ 0x99, 0x7C, 0x99, 0x7D, 0x99, 0x7E, 0x99, 0x80, /* 0xA4-0xA7 */
+ 0x99, 0x81, 0xE9, 0xDD, 0x99, 0x82, 0xE9, 0xDF, /* 0xA8-0xAB */
+ 0xC3, 0xCA, 0x99, 0x83, 0x99, 0x84, 0x99, 0x85, /* 0xAC-0xAF */
+ 0x99, 0x86, 0x99, 0x87, 0x99, 0x88, 0x99, 0x89, /* 0xB0-0xB3 */
+ 0x99, 0x8A, 0x99, 0x8B, 0x99, 0x8C, 0x99, 0x8D, /* 0xB4-0xB7 */
+ 0x99, 0x8E, 0x99, 0x8F, 0x99, 0x90, 0x99, 0x91, /* 0xB8-0xBB */
+ 0x99, 0x92, 0x99, 0x93, 0x99, 0x94, 0x99, 0x95, /* 0xBC-0xBF */
+ 0x99, 0x96, 0x99, 0x97, 0x99, 0x98, 0x99, 0x99, /* 0xC0-0xC3 */
+ 0x99, 0x9A, 0x99, 0x9B, 0x99, 0x9C, 0x99, 0x9D, /* 0xC4-0xC7 */
+ 0x99, 0x9E, 0x99, 0x9F, 0x99, 0xA0, 0x99, 0xA1, /* 0xC8-0xCB */
+ 0x99, 0xA2, 0x99, 0xA3, 0x99, 0xA4, 0x99, 0xA5, /* 0xCC-0xCF */
+ 0x99, 0xA6, 0x99, 0xA7, 0x99, 0xA8, 0x99, 0xA9, /* 0xD0-0xD3 */
+ 0x99, 0xAA, 0x99, 0xAB, 0x99, 0xAC, 0x99, 0xAD, /* 0xD4-0xD7 */
+ 0x99, 0xAE, 0x99, 0xAF, 0x99, 0xB0, 0x99, 0xB1, /* 0xD8-0xDB */
+ 0x99, 0xB2, 0x99, 0xB3, 0x99, 0xB4, 0x99, 0xB5, /* 0xDC-0xDF */
+ 0x99, 0xB6, 0x99, 0xB7, 0x99, 0xB8, 0x99, 0xB9, /* 0xE0-0xE3 */
+ 0x99, 0xBA, 0x99, 0xBB, 0x99, 0xBC, 0x99, 0xBD, /* 0xE4-0xE7 */
+ 0x99, 0xBE, 0x99, 0xBF, 0x99, 0xC0, 0x99, 0xC1, /* 0xE8-0xEB */
+ 0x99, 0xC2, 0x99, 0xC3, 0x99, 0xC4, 0x99, 0xC5, /* 0xEC-0xEF */
+ 0x99, 0xC6, 0x99, 0xC7, 0x99, 0xC8, 0x99, 0xC9, /* 0xF0-0xF3 */
+ 0x99, 0xCA, 0x99, 0xCB, 0x99, 0xCC, 0x99, 0xCD, /* 0xF4-0xF7 */
+ 0x99, 0xCE, 0x99, 0xCF, 0x99, 0xD0, 0x99, 0xD1, /* 0xF8-0xFB */
+ 0x99, 0xD2, 0x99, 0xD3, 0x99, 0xD4, 0x99, 0xD5, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_6B[512] = {
+ 0x99, 0xD6, 0x99, 0xD7, 0x99, 0xD8, 0x99, 0xD9, /* 0x00-0x03 */
+ 0x99, 0xDA, 0x99, 0xDB, 0x99, 0xDC, 0x99, 0xDD, /* 0x04-0x07 */
+ 0x99, 0xDE, 0x99, 0xDF, 0x99, 0xE0, 0x99, 0xE1, /* 0x08-0x0B */
+ 0x99, 0xE2, 0x99, 0xE3, 0x99, 0xE4, 0x99, 0xE5, /* 0x0C-0x0F */
+ 0x99, 0xE6, 0x99, 0xE7, 0x99, 0xE8, 0x99, 0xE9, /* 0x10-0x13 */
+ 0x99, 0xEA, 0x99, 0xEB, 0x99, 0xEC, 0x99, 0xED, /* 0x14-0x17 */
+ 0x99, 0xEE, 0x99, 0xEF, 0x99, 0xF0, 0x99, 0xF1, /* 0x18-0x1B */
+ 0x99, 0xF2, 0x99, 0xF3, 0x99, 0xF4, 0x99, 0xF5, /* 0x1C-0x1F */
+ 0xC7, 0xB7, 0xB4, 0xCE, 0xBB, 0xB6, 0xD0, 0xC0, /* 0x20-0x23 */
+ 0xEC, 0xA3, 0x99, 0xF6, 0x99, 0xF7, 0xC5, 0xB7, /* 0x24-0x27 */
+ 0x99, 0xF8, 0x99, 0xF9, 0x99, 0xFA, 0x99, 0xFB, /* 0x28-0x2B */
+ 0x99, 0xFC, 0x99, 0xFD, 0x99, 0xFE, 0x9A, 0x40, /* 0x2C-0x2F */
+ 0x9A, 0x41, 0x9A, 0x42, 0xD3, 0xFB, 0x9A, 0x43, /* 0x30-0x33 */
+ 0x9A, 0x44, 0x9A, 0x45, 0x9A, 0x46, 0xEC, 0xA4, /* 0x34-0x37 */
+ 0x9A, 0x47, 0xEC, 0xA5, 0xC6, 0xDB, 0x9A, 0x48, /* 0x38-0x3B */
+ 0x9A, 0x49, 0x9A, 0x4A, 0xBF, 0xEE, 0x9A, 0x4B, /* 0x3C-0x3F */
+ 0x9A, 0x4C, 0x9A, 0x4D, 0x9A, 0x4E, 0xEC, 0xA6, /* 0x40-0x43 */
+ 0x9A, 0x4F, 0x9A, 0x50, 0xEC, 0xA7, 0xD0, 0xAA, /* 0x44-0x47 */
+ 0x9A, 0x51, 0xC7, 0xB8, 0x9A, 0x52, 0x9A, 0x53, /* 0x48-0x4B */
+ 0xB8, 0xE8, 0x9A, 0x54, 0x9A, 0x55, 0x9A, 0x56, /* 0x4C-0x4F */
+ 0x9A, 0x57, 0x9A, 0x58, 0x9A, 0x59, 0x9A, 0x5A, /* 0x50-0x53 */
+ 0x9A, 0x5B, 0x9A, 0x5C, 0x9A, 0x5D, 0x9A, 0x5E, /* 0x54-0x57 */
+ 0x9A, 0x5F, 0xEC, 0xA8, 0x9A, 0x60, 0x9A, 0x61, /* 0x58-0x5B */
+ 0x9A, 0x62, 0x9A, 0x63, 0x9A, 0x64, 0x9A, 0x65, /* 0x5C-0x5F */
+ 0x9A, 0x66, 0x9A, 0x67, 0xD6, 0xB9, 0xD5, 0xFD, /* 0x60-0x63 */
+ 0xB4, 0xCB, 0xB2, 0xBD, 0xCE, 0xE4, 0xC6, 0xE7, /* 0x64-0x67 */
+ 0x9A, 0x68, 0x9A, 0x69, 0xCD, 0xE1, 0x9A, 0x6A, /* 0x68-0x6B */
+ 0x9A, 0x6B, 0x9A, 0x6C, 0x9A, 0x6D, 0x9A, 0x6E, /* 0x6C-0x6F */
+ 0x9A, 0x6F, 0x9A, 0x70, 0x9A, 0x71, 0x9A, 0x72, /* 0x70-0x73 */
+ 0x9A, 0x73, 0x9A, 0x74, 0x9A, 0x75, 0x9A, 0x76, /* 0x74-0x77 */
+ 0x9A, 0x77, 0xB4, 0xF5, 0x9A, 0x78, 0xCB, 0xC0, /* 0x78-0x7B */
+ 0xBC, 0xDF, 0x9A, 0x79, 0x9A, 0x7A, 0x9A, 0x7B, /* 0x7C-0x7F */
+
+ 0x9A, 0x7C, 0xE9, 0xE2, 0xE9, 0xE3, 0xD1, 0xEA, /* 0x80-0x83 */
+ 0xE9, 0xE5, 0x9A, 0x7D, 0xB4, 0xF9, 0xE9, 0xE4, /* 0x84-0x87 */
+ 0x9A, 0x7E, 0xD1, 0xB3, 0xCA, 0xE2, 0xB2, 0xD0, /* 0x88-0x8B */
+ 0x9A, 0x80, 0xE9, 0xE8, 0x9A, 0x81, 0x9A, 0x82, /* 0x8C-0x8F */
+ 0x9A, 0x83, 0x9A, 0x84, 0xE9, 0xE6, 0xE9, 0xE7, /* 0x90-0x93 */
+ 0x9A, 0x85, 0x9A, 0x86, 0xD6, 0xB3, 0x9A, 0x87, /* 0x94-0x97 */
+ 0x9A, 0x88, 0x9A, 0x89, 0xE9, 0xE9, 0xE9, 0xEA, /* 0x98-0x9B */
+ 0x9A, 0x8A, 0x9A, 0x8B, 0x9A, 0x8C, 0x9A, 0x8D, /* 0x9C-0x9F */
+ 0x9A, 0x8E, 0xE9, 0xEB, 0x9A, 0x8F, 0x9A, 0x90, /* 0xA0-0xA3 */
+ 0x9A, 0x91, 0x9A, 0x92, 0x9A, 0x93, 0x9A, 0x94, /* 0xA4-0xA7 */
+ 0x9A, 0x95, 0x9A, 0x96, 0xE9, 0xEC, 0x9A, 0x97, /* 0xA8-0xAB */
+ 0x9A, 0x98, 0x9A, 0x99, 0x9A, 0x9A, 0x9A, 0x9B, /* 0xAC-0xAF */
+ 0x9A, 0x9C, 0x9A, 0x9D, 0x9A, 0x9E, 0xEC, 0xAF, /* 0xB0-0xB3 */
+ 0xC5, 0xB9, 0xB6, 0xCE, 0x9A, 0x9F, 0xD2, 0xF3, /* 0xB4-0xB7 */
+ 0x9A, 0xA0, 0x9A, 0xA1, 0x9A, 0xA2, 0x9A, 0xA3, /* 0xB8-0xBB */
+ 0x9A, 0xA4, 0x9A, 0xA5, 0x9A, 0xA6, 0xB5, 0xEE, /* 0xBC-0xBF */
+ 0x9A, 0xA7, 0xBB, 0xD9, 0xEC, 0xB1, 0x9A, 0xA8, /* 0xC0-0xC3 */
+ 0x9A, 0xA9, 0xD2, 0xE3, 0x9A, 0xAA, 0x9A, 0xAB, /* 0xC4-0xC7 */
+ 0x9A, 0xAC, 0x9A, 0xAD, 0x9A, 0xAE, 0xCE, 0xE3, /* 0xC8-0xCB */
+ 0x9A, 0xAF, 0xC4, 0xB8, 0x9A, 0xB0, 0xC3, 0xBF, /* 0xCC-0xCF */
+ 0x9A, 0xB1, 0x9A, 0xB2, 0xB6, 0xBE, 0xD8, 0xB9, /* 0xD0-0xD3 */
+ 0xB1, 0xC8, 0xB1, 0xCF, 0xB1, 0xD1, 0xC5, 0xFE, /* 0xD4-0xD7 */
+ 0x9A, 0xB3, 0xB1, 0xD0, 0x9A, 0xB4, 0xC3, 0xAB, /* 0xD8-0xDB */
+ 0x9A, 0xB5, 0x9A, 0xB6, 0x9A, 0xB7, 0x9A, 0xB8, /* 0xDC-0xDF */
+ 0x9A, 0xB9, 0xD5, 0xB1, 0x9A, 0xBA, 0x9A, 0xBB, /* 0xE0-0xE3 */
+ 0x9A, 0xBC, 0x9A, 0xBD, 0x9A, 0xBE, 0x9A, 0xBF, /* 0xE4-0xE7 */
+ 0x9A, 0xC0, 0x9A, 0xC1, 0xEB, 0xA4, 0xBA, 0xC1, /* 0xE8-0xEB */
+ 0x9A, 0xC2, 0x9A, 0xC3, 0x9A, 0xC4, 0xCC, 0xBA, /* 0xEC-0xEF */
+ 0x9A, 0xC5, 0x9A, 0xC6, 0x9A, 0xC7, 0xEB, 0xA5, /* 0xF0-0xF3 */
+ 0x9A, 0xC8, 0xEB, 0xA7, 0x9A, 0xC9, 0x9A, 0xCA, /* 0xF4-0xF7 */
+ 0x9A, 0xCB, 0xEB, 0xA8, 0x9A, 0xCC, 0x9A, 0xCD, /* 0xF8-0xFB */
+ 0x9A, 0xCE, 0xEB, 0xA6, 0x9A, 0xCF, 0x9A, 0xD0, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_6C[512] = {
+ 0x9A, 0xD1, 0x9A, 0xD2, 0x9A, 0xD3, 0x9A, 0xD4, /* 0x00-0x03 */
+ 0x9A, 0xD5, 0xEB, 0xA9, 0xEB, 0xAB, 0xEB, 0xAA, /* 0x04-0x07 */
+ 0x9A, 0xD6, 0x9A, 0xD7, 0x9A, 0xD8, 0x9A, 0xD9, /* 0x08-0x0B */
+ 0x9A, 0xDA, 0xEB, 0xAC, 0x9A, 0xDB, 0xCA, 0xCF, /* 0x0C-0x0F */
+ 0xD8, 0xB5, 0xC3, 0xF1, 0x9A, 0xDC, 0xC3, 0xA5, /* 0x10-0x13 */
+ 0xC6, 0xF8, 0xEB, 0xAD, 0xC4, 0xCA, 0x9A, 0xDD, /* 0x14-0x17 */
+ 0xEB, 0xAE, 0xEB, 0xAF, 0xEB, 0xB0, 0xB7, 0xD5, /* 0x18-0x1B */
+ 0x9A, 0xDE, 0x9A, 0xDF, 0x9A, 0xE0, 0xB7, 0xFA, /* 0x1C-0x1F */
+ 0x9A, 0xE1, 0xEB, 0xB1, 0xC7, 0xE2, 0x9A, 0xE2, /* 0x20-0x23 */
+ 0xEB, 0xB3, 0x9A, 0xE3, 0xBA, 0xA4, 0xD1, 0xF5, /* 0x24-0x27 */
+ 0xB0, 0xB1, 0xEB, 0xB2, 0xEB, 0xB4, 0x9A, 0xE4, /* 0x28-0x2B */
+ 0x9A, 0xE5, 0x9A, 0xE6, 0xB5, 0xAA, 0xC2, 0xC8, /* 0x2C-0x2F */
+ 0xC7, 0xE8, 0x9A, 0xE7, 0xEB, 0xB5, 0x9A, 0xE8, /* 0x30-0x33 */
+ 0xCB, 0xAE, 0xE3, 0xDF, 0x9A, 0xE9, 0x9A, 0xEA, /* 0x34-0x37 */
+ 0xD3, 0xC0, 0x9A, 0xEB, 0x9A, 0xEC, 0x9A, 0xED, /* 0x38-0x3B */
+ 0x9A, 0xEE, 0xD9, 0xDB, 0x9A, 0xEF, 0x9A, 0xF0, /* 0x3C-0x3F */
+ 0xCD, 0xA1, 0xD6, 0xAD, 0xC7, 0xF3, 0x9A, 0xF1, /* 0x40-0x43 */
+ 0x9A, 0xF2, 0x9A, 0xF3, 0xD9, 0xE0, 0xBB, 0xE3, /* 0x44-0x47 */
+ 0x9A, 0xF4, 0xBA, 0xBA, 0xE3, 0xE2, 0x9A, 0xF5, /* 0x48-0x4B */
+ 0x9A, 0xF6, 0x9A, 0xF7, 0x9A, 0xF8, 0x9A, 0xF9, /* 0x4C-0x4F */
+ 0xCF, 0xAB, 0x9A, 0xFA, 0x9A, 0xFB, 0x9A, 0xFC, /* 0x50-0x53 */
+ 0xE3, 0xE0, 0xC9, 0xC7, 0x9A, 0xFD, 0xBA, 0xB9, /* 0x54-0x57 */
+ 0x9A, 0xFE, 0x9B, 0x40, 0x9B, 0x41, 0xD1, 0xB4, /* 0x58-0x5B */
+ 0xE3, 0xE1, 0xC8, 0xEA, 0xB9, 0xAF, 0xBD, 0xAD, /* 0x5C-0x5F */
+ 0xB3, 0xD8, 0xCE, 0xDB, 0x9B, 0x42, 0x9B, 0x43, /* 0x60-0x63 */
+ 0xCC, 0xC0, 0x9B, 0x44, 0x9B, 0x45, 0x9B, 0x46, /* 0x64-0x67 */
+ 0xE3, 0xE8, 0xE3, 0xE9, 0xCD, 0xF4, 0x9B, 0x47, /* 0x68-0x6B */
+ 0x9B, 0x48, 0x9B, 0x49, 0x9B, 0x4A, 0x9B, 0x4B, /* 0x6C-0x6F */
+ 0xCC, 0xAD, 0x9B, 0x4C, 0xBC, 0xB3, 0x9B, 0x4D, /* 0x70-0x73 */
+ 0xE3, 0xEA, 0x9B, 0x4E, 0xE3, 0xEB, 0x9B, 0x4F, /* 0x74-0x77 */
+ 0x9B, 0x50, 0xD0, 0xDA, 0x9B, 0x51, 0x9B, 0x52, /* 0x78-0x7B */
+ 0x9B, 0x53, 0xC6, 0xFB, 0xB7, 0xDA, 0x9B, 0x54, /* 0x7C-0x7F */
+
+ 0x9B, 0x55, 0xC7, 0xDF, 0xD2, 0xCA, 0xCE, 0xD6, /* 0x80-0x83 */
+ 0x9B, 0x56, 0xE3, 0xE4, 0xE3, 0xEC, 0x9B, 0x57, /* 0x84-0x87 */
+ 0xC9, 0xF2, 0xB3, 0xC1, 0x9B, 0x58, 0x9B, 0x59, /* 0x88-0x8B */
+ 0xE3, 0xE7, 0x9B, 0x5A, 0x9B, 0x5B, 0xC6, 0xE3, /* 0x8C-0x8F */
+ 0xE3, 0xE5, 0x9B, 0x5C, 0x9B, 0x5D, 0xED, 0xB3, /* 0x90-0x93 */
+ 0xE3, 0xE6, 0x9B, 0x5E, 0x9B, 0x5F, 0x9B, 0x60, /* 0x94-0x97 */
+ 0x9B, 0x61, 0xC9, 0xB3, 0x9B, 0x62, 0xC5, 0xE6, /* 0x98-0x9B */
+ 0x9B, 0x63, 0x9B, 0x64, 0x9B, 0x65, 0xB9, 0xB5, /* 0x9C-0x9F */
+ 0x9B, 0x66, 0xC3, 0xBB, 0x9B, 0x67, 0xE3, 0xE3, /* 0xA0-0xA3 */
+ 0xC5, 0xBD, 0xC1, 0xA4, 0xC2, 0xD9, 0xB2, 0xD7, /* 0xA4-0xA7 */
+ 0x9B, 0x68, 0xE3, 0xED, 0xBB, 0xA6, 0xC4, 0xAD, /* 0xA8-0xAB */
+ 0x9B, 0x69, 0xE3, 0xF0, 0xBE, 0xDA, 0x9B, 0x6A, /* 0xAC-0xAF */
+ 0x9B, 0x6B, 0xE3, 0xFB, 0xE3, 0xF5, 0xBA, 0xD3, /* 0xB0-0xB3 */
+ 0x9B, 0x6C, 0x9B, 0x6D, 0x9B, 0x6E, 0x9B, 0x6F, /* 0xB4-0xB7 */
+ 0xB7, 0xD0, 0xD3, 0xCD, 0x9B, 0x70, 0xD6, 0xCE, /* 0xB8-0xBB */
+ 0xD5, 0xD3, 0xB9, 0xC1, 0xD5, 0xB4, 0xD1, 0xD8, /* 0xBC-0xBF */
+ 0x9B, 0x71, 0x9B, 0x72, 0x9B, 0x73, 0x9B, 0x74, /* 0xC0-0xC3 */
+ 0xD0, 0xB9, 0xC7, 0xF6, 0x9B, 0x75, 0x9B, 0x76, /* 0xC4-0xC7 */
+ 0x9B, 0x77, 0xC8, 0xAA, 0xB2, 0xB4, 0x9B, 0x78, /* 0xC8-0xCB */
+ 0xC3, 0xDA, 0x9B, 0x79, 0x9B, 0x7A, 0x9B, 0x7B, /* 0xCC-0xCF */
+ 0xE3, 0xEE, 0x9B, 0x7C, 0x9B, 0x7D, 0xE3, 0xFC, /* 0xD0-0xD3 */
+ 0xE3, 0xEF, 0xB7, 0xA8, 0xE3, 0xF7, 0xE3, 0xF4, /* 0xD4-0xD7 */
+ 0x9B, 0x7E, 0x9B, 0x80, 0x9B, 0x81, 0xB7, 0xBA, /* 0xD8-0xDB */
+ 0x9B, 0x82, 0x9B, 0x83, 0xC5, 0xA2, 0x9B, 0x84, /* 0xDC-0xDF */
+ 0xE3, 0xF6, 0xC5, 0xDD, 0xB2, 0xA8, 0xC6, 0xFC, /* 0xE0-0xE3 */
+ 0x9B, 0x85, 0xC4, 0xE0, 0x9B, 0x86, 0x9B, 0x87, /* 0xE4-0xE7 */
+ 0xD7, 0xA2, 0x9B, 0x88, 0xC0, 0xE1, 0xE3, 0xF9, /* 0xE8-0xEB */
+ 0x9B, 0x89, 0x9B, 0x8A, 0xE3, 0xFA, 0xE3, 0xFD, /* 0xEC-0xEF */
+ 0xCC, 0xA9, 0xE3, 0xF3, 0x9B, 0x8B, 0xD3, 0xBE, /* 0xF0-0xF3 */
+ 0x9B, 0x8C, 0xB1, 0xC3, 0xED, 0xB4, 0xE3, 0xF1, /* 0xF4-0xF7 */
+ 0xE3, 0xF2, 0x9B, 0x8D, 0xE3, 0xF8, 0xD0, 0xBA, /* 0xF8-0xFB */
+ 0xC6, 0xC3, 0xD4, 0xF3, 0xE3, 0xFE, 0x9B, 0x8E, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_6D[512] = {
+ 0x9B, 0x8F, 0xBD, 0xE0, 0x9B, 0x90, 0x9B, 0x91, /* 0x00-0x03 */
+ 0xE4, 0xA7, 0x9B, 0x92, 0x9B, 0x93, 0xE4, 0xA6, /* 0x04-0x07 */
+ 0x9B, 0x94, 0x9B, 0x95, 0x9B, 0x96, 0xD1, 0xF3, /* 0x08-0x0B */
+ 0xE4, 0xA3, 0x9B, 0x97, 0xE4, 0xA9, 0x9B, 0x98, /* 0x0C-0x0F */
+ 0x9B, 0x99, 0x9B, 0x9A, 0xC8, 0xF7, 0x9B, 0x9B, /* 0x10-0x13 */
+ 0x9B, 0x9C, 0x9B, 0x9D, 0x9B, 0x9E, 0xCF, 0xB4, /* 0x14-0x17 */
+ 0x9B, 0x9F, 0xE4, 0xA8, 0xE4, 0xAE, 0xC2, 0xE5, /* 0x18-0x1B */
+ 0x9B, 0xA0, 0x9B, 0xA1, 0xB6, 0xB4, 0x9B, 0xA2, /* 0x1C-0x1F */
+ 0x9B, 0xA3, 0x9B, 0xA4, 0x9B, 0xA5, 0x9B, 0xA6, /* 0x20-0x23 */
+ 0x9B, 0xA7, 0xBD, 0xF2, 0x9B, 0xA8, 0xE4, 0xA2, /* 0x24-0x27 */
+ 0x9B, 0xA9, 0x9B, 0xAA, 0xBA, 0xE9, 0xE4, 0xAA, /* 0x28-0x2B */
+ 0x9B, 0xAB, 0x9B, 0xAC, 0xE4, 0xAC, 0x9B, 0xAD, /* 0x2C-0x2F */
+ 0x9B, 0xAE, 0xB6, 0xFD, 0xD6, 0xDE, 0xE4, 0xB2, /* 0x30-0x33 */
+ 0x9B, 0xAF, 0xE4, 0xAD, 0x9B, 0xB0, 0x9B, 0xB1, /* 0x34-0x37 */
+ 0x9B, 0xB2, 0xE4, 0xA1, 0x9B, 0xB3, 0xBB, 0xEE, /* 0x38-0x3B */
+ 0xCD, 0xDD, 0xC7, 0xA2, 0xC5, 0xC9, 0x9B, 0xB4, /* 0x3C-0x3F */
+ 0x9B, 0xB5, 0xC1, 0xF7, 0x9B, 0xB6, 0xE4, 0xA4, /* 0x40-0x43 */
+ 0x9B, 0xB7, 0xC7, 0xB3, 0xBD, 0xAC, 0xBD, 0xBD, /* 0x44-0x47 */
+ 0xE4, 0xA5, 0x9B, 0xB8, 0xD7, 0xC7, 0xB2, 0xE2, /* 0x48-0x4B */
+ 0x9B, 0xB9, 0xE4, 0xAB, 0xBC, 0xC3, 0xE4, 0xAF, /* 0x4C-0x4F */
+ 0x9B, 0xBA, 0xBB, 0xEB, 0xE4, 0xB0, 0xC5, 0xA8, /* 0x50-0x53 */
+ 0xE4, 0xB1, 0x9B, 0xBB, 0x9B, 0xBC, 0x9B, 0xBD, /* 0x54-0x57 */
+ 0x9B, 0xBE, 0xD5, 0xE3, 0xBF, 0xA3, 0x9B, 0xBF, /* 0x58-0x5B */
+ 0xE4, 0xBA, 0x9B, 0xC0, 0xE4, 0xB7, 0x9B, 0xC1, /* 0x5C-0x5F */
+ 0xE4, 0xBB, 0x9B, 0xC2, 0x9B, 0xC3, 0xE4, 0xBD, /* 0x60-0x63 */
+ 0x9B, 0xC4, 0x9B, 0xC5, 0xC6, 0xD6, 0x9B, 0xC6, /* 0x64-0x67 */
+ 0x9B, 0xC7, 0xBA, 0xC6, 0xC0, 0xCB, 0x9B, 0xC8, /* 0x68-0x6B */
+ 0x9B, 0xC9, 0x9B, 0xCA, 0xB8, 0xA1, 0xE4, 0xB4, /* 0x6C-0x6F */
+ 0x9B, 0xCB, 0x9B, 0xCC, 0x9B, 0xCD, 0x9B, 0xCE, /* 0x70-0x73 */
+ 0xD4, 0xA1, 0x9B, 0xCF, 0x9B, 0xD0, 0xBA, 0xA3, /* 0x74-0x77 */
+ 0xBD, 0xFE, 0x9B, 0xD1, 0x9B, 0xD2, 0x9B, 0xD3, /* 0x78-0x7B */
+ 0xE4, 0xBC, 0x9B, 0xD4, 0x9B, 0xD5, 0x9B, 0xD6, /* 0x7C-0x7F */
+
+ 0x9B, 0xD7, 0x9B, 0xD8, 0xCD, 0xBF, 0x9B, 0xD9, /* 0x80-0x83 */
+ 0x9B, 0xDA, 0xC4, 0xF9, 0x9B, 0xDB, 0x9B, 0xDC, /* 0x84-0x87 */
+ 0xCF, 0xFB, 0xC9, 0xE6, 0x9B, 0xDD, 0x9B, 0xDE, /* 0x88-0x8B */
+ 0xD3, 0xBF, 0x9B, 0xDF, 0xCF, 0xD1, 0x9B, 0xE0, /* 0x8C-0x8F */
+ 0x9B, 0xE1, 0xE4, 0xB3, 0x9B, 0xE2, 0xE4, 0xB8, /* 0x90-0x93 */
+ 0xE4, 0xB9, 0xCC, 0xE9, 0x9B, 0xE3, 0x9B, 0xE4, /* 0x94-0x97 */
+ 0x9B, 0xE5, 0x9B, 0xE6, 0x9B, 0xE7, 0xCC, 0xCE, /* 0x98-0x9B */
+ 0x9B, 0xE8, 0xC0, 0xD4, 0xE4, 0xB5, 0xC1, 0xB0, /* 0x9C-0x9F */
+ 0xE4, 0xB6, 0xCE, 0xD0, 0x9B, 0xE9, 0xBB, 0xC1, /* 0xA0-0xA3 */
+ 0xB5, 0xD3, 0x9B, 0xEA, 0xC8, 0xF3, 0xBD, 0xA7, /* 0xA4-0xA7 */
+ 0xD5, 0xC7, 0xC9, 0xAC, 0xB8, 0xA2, 0xE4, 0xCA, /* 0xA8-0xAB */
+ 0x9B, 0xEB, 0x9B, 0xEC, 0xE4, 0xCC, 0xD1, 0xC4, /* 0xAC-0xAF */
+ 0x9B, 0xED, 0x9B, 0xEE, 0xD2, 0xBA, 0x9B, 0xEF, /* 0xB0-0xB3 */
+ 0x9B, 0xF0, 0xBA, 0xAD, 0x9B, 0xF1, 0x9B, 0xF2, /* 0xB4-0xB7 */
+ 0xBA, 0xD4, 0x9B, 0xF3, 0x9B, 0xF4, 0x9B, 0xF5, /* 0xB8-0xBB */
+ 0x9B, 0xF6, 0x9B, 0xF7, 0x9B, 0xF8, 0xE4, 0xC3, /* 0xBC-0xBF */
+ 0xB5, 0xED, 0x9B, 0xF9, 0x9B, 0xFA, 0x9B, 0xFB, /* 0xC0-0xC3 */
+ 0xD7, 0xCD, 0xE4, 0xC0, 0xCF, 0xFD, 0xE4, 0xBF, /* 0xC4-0xC7 */
+ 0x9B, 0xFC, 0x9B, 0xFD, 0x9B, 0xFE, 0xC1, 0xDC, /* 0xC8-0xCB */
+ 0xCC, 0xCA, 0x9C, 0x40, 0x9C, 0x41, 0x9C, 0x42, /* 0xCC-0xCF */
+ 0x9C, 0x43, 0xCA, 0xE7, 0x9C, 0x44, 0x9C, 0x45, /* 0xD0-0xD3 */
+ 0x9C, 0x46, 0x9C, 0x47, 0xC4, 0xD7, 0x9C, 0x48, /* 0xD4-0xD7 */
+ 0xCC, 0xD4, 0xE4, 0xC8, 0x9C, 0x49, 0x9C, 0x4A, /* 0xD8-0xDB */
+ 0x9C, 0x4B, 0xE4, 0xC7, 0xE4, 0xC1, 0x9C, 0x4C, /* 0xDC-0xDF */
+ 0xE4, 0xC4, 0xB5, 0xAD, 0x9C, 0x4D, 0x9C, 0x4E, /* 0xE0-0xE3 */
+ 0xD3, 0xD9, 0x9C, 0x4F, 0xE4, 0xC6, 0x9C, 0x50, /* 0xE4-0xE7 */
+ 0x9C, 0x51, 0x9C, 0x52, 0x9C, 0x53, 0xD2, 0xF9, /* 0xE8-0xEB */
+ 0xB4, 0xE3, 0x9C, 0x54, 0xBB, 0xB4, 0x9C, 0x55, /* 0xEC-0xEF */
+ 0x9C, 0x56, 0xC9, 0xEE, 0x9C, 0x57, 0xB4, 0xBE, /* 0xF0-0xF3 */
+ 0x9C, 0x58, 0x9C, 0x59, 0x9C, 0x5A, 0xBB, 0xEC, /* 0xF4-0xF7 */
+ 0x9C, 0x5B, 0xD1, 0xCD, 0x9C, 0x5C, 0xCC, 0xED, /* 0xF8-0xFB */
+ 0xED, 0xB5, 0x9C, 0x5D, 0x9C, 0x5E, 0x9C, 0x5F, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_6E[512] = {
+ 0x9C, 0x60, 0x9C, 0x61, 0x9C, 0x62, 0x9C, 0x63, /* 0x00-0x03 */
+ 0x9C, 0x64, 0xC7, 0xE5, 0x9C, 0x65, 0x9C, 0x66, /* 0x04-0x07 */
+ 0x9C, 0x67, 0x9C, 0x68, 0xD4, 0xA8, 0x9C, 0x69, /* 0x08-0x0B */
+ 0xE4, 0xCB, 0xD7, 0xD5, 0xE4, 0xC2, 0x9C, 0x6A, /* 0x0C-0x0F */
+ 0xBD, 0xA5, 0xE4, 0xC5, 0x9C, 0x6B, 0x9C, 0x6C, /* 0x10-0x13 */
+ 0xD3, 0xE6, 0x9C, 0x6D, 0xE4, 0xC9, 0xC9, 0xF8, /* 0x14-0x17 */
+ 0x9C, 0x6E, 0x9C, 0x6F, 0xE4, 0xBE, 0x9C, 0x70, /* 0x18-0x1B */
+ 0x9C, 0x71, 0xD3, 0xE5, 0x9C, 0x72, 0x9C, 0x73, /* 0x1C-0x1F */
+ 0xC7, 0xFE, 0xB6, 0xC9, 0x9C, 0x74, 0xD4, 0xFC, /* 0x20-0x23 */
+ 0xB2, 0xB3, 0xE4, 0xD7, 0x9C, 0x75, 0x9C, 0x76, /* 0x24-0x27 */
+ 0x9C, 0x77, 0xCE, 0xC2, 0x9C, 0x78, 0xE4, 0xCD, /* 0x28-0x2B */
+ 0x9C, 0x79, 0xCE, 0xBC, 0x9C, 0x7A, 0xB8, 0xDB, /* 0x2C-0x2F */
+ 0x9C, 0x7B, 0x9C, 0x7C, 0xE4, 0xD6, 0x9C, 0x7D, /* 0x30-0x33 */
+ 0xBF, 0xCA, 0x9C, 0x7E, 0x9C, 0x80, 0x9C, 0x81, /* 0x34-0x37 */
+ 0xD3, 0xCE, 0x9C, 0x82, 0xC3, 0xEC, 0x9C, 0x83, /* 0x38-0x3B */
+ 0x9C, 0x84, 0x9C, 0x85, 0x9C, 0x86, 0x9C, 0x87, /* 0x3C-0x3F */
+ 0x9C, 0x88, 0x9C, 0x89, 0x9C, 0x8A, 0xC5, 0xC8, /* 0x40-0x43 */
+ 0xE4, 0xD8, 0x9C, 0x8B, 0x9C, 0x8C, 0x9C, 0x8D, /* 0x44-0x47 */
+ 0x9C, 0x8E, 0x9C, 0x8F, 0x9C, 0x90, 0x9C, 0x91, /* 0x48-0x4B */
+ 0x9C, 0x92, 0xCD, 0xC4, 0xE4, 0xCF, 0x9C, 0x93, /* 0x4C-0x4F */
+ 0x9C, 0x94, 0x9C, 0x95, 0x9C, 0x96, 0xE4, 0xD4, /* 0x50-0x53 */
+ 0xE4, 0xD5, 0x9C, 0x97, 0xBA, 0xFE, 0x9C, 0x98, /* 0x54-0x57 */
+ 0xCF, 0xE6, 0x9C, 0x99, 0x9C, 0x9A, 0xD5, 0xBF, /* 0x58-0x5B */
+ 0x9C, 0x9B, 0x9C, 0x9C, 0x9C, 0x9D, 0xE4, 0xD2, /* 0x5C-0x5F */
+ 0x9C, 0x9E, 0x9C, 0x9F, 0x9C, 0xA0, 0x9C, 0xA1, /* 0x60-0x63 */
+ 0x9C, 0xA2, 0x9C, 0xA3, 0x9C, 0xA4, 0x9C, 0xA5, /* 0x64-0x67 */
+ 0x9C, 0xA6, 0x9C, 0xA7, 0x9C, 0xA8, 0xE4, 0xD0, /* 0x68-0x6B */
+ 0x9C, 0xA9, 0x9C, 0xAA, 0xE4, 0xCE, 0x9C, 0xAB, /* 0x6C-0x6F */
+ 0x9C, 0xAC, 0x9C, 0xAD, 0x9C, 0xAE, 0x9C, 0xAF, /* 0x70-0x73 */
+ 0x9C, 0xB0, 0x9C, 0xB1, 0x9C, 0xB2, 0x9C, 0xB3, /* 0x74-0x77 */
+ 0x9C, 0xB4, 0x9C, 0xB5, 0x9C, 0xB6, 0x9C, 0xB7, /* 0x78-0x7B */
+ 0x9C, 0xB8, 0x9C, 0xB9, 0xCD, 0xE5, 0xCA, 0xAA, /* 0x7C-0x7F */
+
+ 0x9C, 0xBA, 0x9C, 0xBB, 0x9C, 0xBC, 0xC0, 0xA3, /* 0x80-0x83 */
+ 0x9C, 0xBD, 0xBD, 0xA6, 0xE4, 0xD3, 0x9C, 0xBE, /* 0x84-0x87 */
+ 0x9C, 0xBF, 0xB8, 0xC8, 0x9C, 0xC0, 0x9C, 0xC1, /* 0x88-0x8B */
+ 0x9C, 0xC2, 0x9C, 0xC3, 0x9C, 0xC4, 0xE4, 0xE7, /* 0x8C-0x8F */
+ 0xD4, 0xB4, 0x9C, 0xC5, 0x9C, 0xC6, 0x9C, 0xC7, /* 0x90-0x93 */
+ 0x9C, 0xC8, 0x9C, 0xC9, 0x9C, 0xCA, 0x9C, 0xCB, /* 0x94-0x97 */
+ 0xE4, 0xDB, 0x9C, 0xCC, 0x9C, 0xCD, 0x9C, 0xCE, /* 0x98-0x9B */
+ 0xC1, 0xEF, 0x9C, 0xCF, 0x9C, 0xD0, 0xE4, 0xE9, /* 0x9C-0x9F */
+ 0x9C, 0xD1, 0x9C, 0xD2, 0xD2, 0xE7, 0x9C, 0xD3, /* 0xA0-0xA3 */
+ 0x9C, 0xD4, 0xE4, 0xDF, 0x9C, 0xD5, 0xE4, 0xE0, /* 0xA4-0xA7 */
+ 0x9C, 0xD6, 0x9C, 0xD7, 0xCF, 0xAA, 0x9C, 0xD8, /* 0xA8-0xAB */
+ 0x9C, 0xD9, 0x9C, 0xDA, 0x9C, 0xDB, 0xCB, 0xDD, /* 0xAC-0xAF */
+ 0x9C, 0xDC, 0xE4, 0xDA, 0xE4, 0xD1, 0x9C, 0xDD, /* 0xB0-0xB3 */
+ 0xE4, 0xE5, 0x9C, 0xDE, 0xC8, 0xDC, 0xE4, 0xE3, /* 0xB4-0xB7 */
+ 0x9C, 0xDF, 0x9C, 0xE0, 0xC4, 0xE7, 0xE4, 0xE2, /* 0xB8-0xBB */
+ 0x9C, 0xE1, 0xE4, 0xE1, 0x9C, 0xE2, 0x9C, 0xE3, /* 0xBC-0xBF */
+ 0x9C, 0xE4, 0xB3, 0xFC, 0xE4, 0xE8, 0x9C, 0xE5, /* 0xC0-0xC3 */
+ 0x9C, 0xE6, 0x9C, 0xE7, 0x9C, 0xE8, 0xB5, 0xE1, /* 0xC4-0xC7 */
+ 0x9C, 0xE9, 0x9C, 0xEA, 0x9C, 0xEB, 0xD7, 0xCC, /* 0xC8-0xCB */
+ 0x9C, 0xEC, 0x9C, 0xED, 0x9C, 0xEE, 0xE4, 0xE6, /* 0xCC-0xCF */
+ 0x9C, 0xEF, 0xBB, 0xAC, 0x9C, 0xF0, 0xD7, 0xD2, /* 0xD0-0xD3 */
+ 0xCC, 0xCF, 0xEB, 0xF8, 0x9C, 0xF1, 0xE4, 0xE4, /* 0xD4-0xD7 */
+ 0x9C, 0xF2, 0x9C, 0xF3, 0xB9, 0xF6, 0x9C, 0xF4, /* 0xD8-0xDB */
+ 0x9C, 0xF5, 0x9C, 0xF6, 0xD6, 0xCD, 0xE4, 0xD9, /* 0xDC-0xDF */
+ 0xE4, 0xDC, 0xC2, 0xFA, 0xE4, 0xDE, 0x9C, 0xF7, /* 0xE0-0xE3 */
+ 0xC2, 0xCB, 0xC0, 0xC4, 0xC2, 0xD0, 0x9C, 0xF8, /* 0xE4-0xE7 */
+ 0xB1, 0xF5, 0xCC, 0xB2, 0x9C, 0xF9, 0x9C, 0xFA, /* 0xE8-0xEB */
+ 0x9C, 0xFB, 0x9C, 0xFC, 0x9C, 0xFD, 0x9C, 0xFE, /* 0xEC-0xEF */
+ 0x9D, 0x40, 0x9D, 0x41, 0x9D, 0x42, 0x9D, 0x43, /* 0xF0-0xF3 */
+ 0xB5, 0xCE, 0x9D, 0x44, 0x9D, 0x45, 0x9D, 0x46, /* 0xF4-0xF7 */
+ 0x9D, 0x47, 0xE4, 0xEF, 0x9D, 0x48, 0x9D, 0x49, /* 0xF8-0xFB */
+ 0x9D, 0x4A, 0x9D, 0x4B, 0x9D, 0x4C, 0x9D, 0x4D, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_6F[512] = {
+ 0x9D, 0x4E, 0x9D, 0x4F, 0xC6, 0xAF, 0x9D, 0x50, /* 0x00-0x03 */
+ 0x9D, 0x51, 0x9D, 0x52, 0xC6, 0xE1, 0x9D, 0x53, /* 0x04-0x07 */
+ 0x9D, 0x54, 0xE4, 0xF5, 0x9D, 0x55, 0x9D, 0x56, /* 0x08-0x0B */
+ 0x9D, 0x57, 0x9D, 0x58, 0x9D, 0x59, 0xC2, 0xA9, /* 0x0C-0x0F */
+ 0x9D, 0x5A, 0x9D, 0x5B, 0x9D, 0x5C, 0xC0, 0xEC, /* 0x10-0x13 */
+ 0xD1, 0xDD, 0xE4, 0xEE, 0x9D, 0x5D, 0x9D, 0x5E, /* 0x14-0x17 */
+ 0x9D, 0x5F, 0x9D, 0x60, 0x9D, 0x61, 0x9D, 0x62, /* 0x18-0x1B */
+ 0x9D, 0x63, 0x9D, 0x64, 0x9D, 0x65, 0x9D, 0x66, /* 0x1C-0x1F */
+ 0xC4, 0xAE, 0x9D, 0x67, 0x9D, 0x68, 0x9D, 0x69, /* 0x20-0x23 */
+ 0xE4, 0xED, 0x9D, 0x6A, 0x9D, 0x6B, 0x9D, 0x6C, /* 0x24-0x27 */
+ 0x9D, 0x6D, 0xE4, 0xF6, 0xE4, 0xF4, 0xC2, 0xFE, /* 0x28-0x2B */
+ 0x9D, 0x6E, 0xE4, 0xDD, 0x9D, 0x6F, 0xE4, 0xF0, /* 0x2C-0x2F */
+ 0x9D, 0x70, 0xCA, 0xFE, 0x9D, 0x71, 0xD5, 0xC4, /* 0x30-0x33 */
+ 0x9D, 0x72, 0x9D, 0x73, 0xE4, 0xF1, 0x9D, 0x74, /* 0x34-0x37 */
+ 0x9D, 0x75, 0x9D, 0x76, 0x9D, 0x77, 0x9D, 0x78, /* 0x38-0x3B */
+ 0x9D, 0x79, 0x9D, 0x7A, 0xD1, 0xFA, 0x9D, 0x7B, /* 0x3C-0x3F */
+ 0x9D, 0x7C, 0x9D, 0x7D, 0x9D, 0x7E, 0x9D, 0x80, /* 0x40-0x43 */
+ 0x9D, 0x81, 0x9D, 0x82, 0xE4, 0xEB, 0xE4, 0xEC, /* 0x44-0x47 */
+ 0x9D, 0x83, 0x9D, 0x84, 0x9D, 0x85, 0xE4, 0xF2, /* 0x48-0x4B */
+ 0x9D, 0x86, 0xCE, 0xAB, 0x9D, 0x87, 0x9D, 0x88, /* 0x4C-0x4F */
+ 0x9D, 0x89, 0x9D, 0x8A, 0x9D, 0x8B, 0x9D, 0x8C, /* 0x50-0x53 */
+ 0x9D, 0x8D, 0x9D, 0x8E, 0x9D, 0x8F, 0x9D, 0x90, /* 0x54-0x57 */
+ 0xC5, 0xCB, 0x9D, 0x91, 0x9D, 0x92, 0x9D, 0x93, /* 0x58-0x5B */
+ 0xC7, 0xB1, 0x9D, 0x94, 0xC2, 0xBA, 0x9D, 0x95, /* 0x5C-0x5F */
+ 0x9D, 0x96, 0x9D, 0x97, 0xE4, 0xEA, 0x9D, 0x98, /* 0x60-0x63 */
+ 0x9D, 0x99, 0x9D, 0x9A, 0xC1, 0xCA, 0x9D, 0x9B, /* 0x64-0x67 */
+ 0x9D, 0x9C, 0x9D, 0x9D, 0x9D, 0x9E, 0x9D, 0x9F, /* 0x68-0x6B */
+ 0x9D, 0xA0, 0xCC, 0xB6, 0xB3, 0xB1, 0x9D, 0xA1, /* 0x6C-0x6F */
+ 0x9D, 0xA2, 0x9D, 0xA3, 0xE4, 0xFB, 0x9D, 0xA4, /* 0x70-0x73 */
+ 0xE4, 0xF3, 0x9D, 0xA5, 0x9D, 0xA6, 0x9D, 0xA7, /* 0x74-0x77 */
+ 0xE4, 0xFA, 0x9D, 0xA8, 0xE4, 0xFD, 0x9D, 0xA9, /* 0x78-0x7B */
+ 0xE4, 0xFC, 0x9D, 0xAA, 0x9D, 0xAB, 0x9D, 0xAC, /* 0x7C-0x7F */
+
+ 0x9D, 0xAD, 0x9D, 0xAE, 0x9D, 0xAF, 0x9D, 0xB0, /* 0x80-0x83 */
+ 0xB3, 0xCE, 0x9D, 0xB1, 0x9D, 0xB2, 0x9D, 0xB3, /* 0x84-0x87 */
+ 0xB3, 0xBA, 0xE4, 0xF7, 0x9D, 0xB4, 0x9D, 0xB5, /* 0x88-0x8B */
+ 0xE4, 0xF9, 0xE4, 0xF8, 0xC5, 0xEC, 0x9D, 0xB6, /* 0x8C-0x8F */
+ 0x9D, 0xB7, 0x9D, 0xB8, 0x9D, 0xB9, 0x9D, 0xBA, /* 0x90-0x93 */
+ 0x9D, 0xBB, 0x9D, 0xBC, 0x9D, 0xBD, 0x9D, 0xBE, /* 0x94-0x97 */
+ 0x9D, 0xBF, 0x9D, 0xC0, 0x9D, 0xC1, 0x9D, 0xC2, /* 0x98-0x9B */
+ 0xC0, 0xBD, 0x9D, 0xC3, 0x9D, 0xC4, 0x9D, 0xC5, /* 0x9C-0x9F */
+ 0x9D, 0xC6, 0xD4, 0xE8, 0x9D, 0xC7, 0x9D, 0xC8, /* 0xA0-0xA3 */
+ 0x9D, 0xC9, 0x9D, 0xCA, 0x9D, 0xCB, 0xE5, 0xA2, /* 0xA4-0xA7 */
+ 0x9D, 0xCC, 0x9D, 0xCD, 0x9D, 0xCE, 0x9D, 0xCF, /* 0xA8-0xAB */
+ 0x9D, 0xD0, 0x9D, 0xD1, 0x9D, 0xD2, 0x9D, 0xD3, /* 0xAC-0xAF */
+ 0x9D, 0xD4, 0x9D, 0xD5, 0x9D, 0xD6, 0xB0, 0xC4, /* 0xB0-0xB3 */
+ 0x9D, 0xD7, 0x9D, 0xD8, 0xE5, 0xA4, 0x9D, 0xD9, /* 0xB4-0xB7 */
+ 0x9D, 0xDA, 0xE5, 0xA3, 0x9D, 0xDB, 0x9D, 0xDC, /* 0xB8-0xBB */
+ 0x9D, 0xDD, 0x9D, 0xDE, 0x9D, 0xDF, 0x9D, 0xE0, /* 0xBC-0xBF */
+ 0xBC, 0xA4, 0x9D, 0xE1, 0xE5, 0xA5, 0x9D, 0xE2, /* 0xC0-0xC3 */
+ 0x9D, 0xE3, 0x9D, 0xE4, 0x9D, 0xE5, 0x9D, 0xE6, /* 0xC4-0xC7 */
+ 0x9D, 0xE7, 0xE5, 0xA1, 0x9D, 0xE8, 0x9D, 0xE9, /* 0xC8-0xCB */
+ 0x9D, 0xEA, 0x9D, 0xEB, 0x9D, 0xEC, 0x9D, 0xED, /* 0xCC-0xCF */
+ 0x9D, 0xEE, 0xE4, 0xFE, 0xB1, 0xF4, 0x9D, 0xEF, /* 0xD0-0xD3 */
+ 0x9D, 0xF0, 0x9D, 0xF1, 0x9D, 0xF2, 0x9D, 0xF3, /* 0xD4-0xD7 */
+ 0x9D, 0xF4, 0x9D, 0xF5, 0x9D, 0xF6, 0x9D, 0xF7, /* 0xD8-0xDB */
+ 0x9D, 0xF8, 0x9D, 0xF9, 0xE5, 0xA8, 0x9D, 0xFA, /* 0xDC-0xDF */
+ 0xE5, 0xA9, 0xE5, 0xA6, 0x9D, 0xFB, 0x9D, 0xFC, /* 0xE0-0xE3 */
+ 0x9D, 0xFD, 0x9D, 0xFE, 0x9E, 0x40, 0x9E, 0x41, /* 0xE4-0xE7 */
+ 0x9E, 0x42, 0x9E, 0x43, 0x9E, 0x44, 0x9E, 0x45, /* 0xE8-0xEB */
+ 0x9E, 0x46, 0x9E, 0x47, 0xE5, 0xA7, 0xE5, 0xAA, /* 0xEC-0xEF */
+ 0x9E, 0x48, 0x9E, 0x49, 0x9E, 0x4A, 0x9E, 0x4B, /* 0xF0-0xF3 */
+ 0x9E, 0x4C, 0x9E, 0x4D, 0x9E, 0x4E, 0x9E, 0x4F, /* 0xF4-0xF7 */
+ 0x9E, 0x50, 0x9E, 0x51, 0x9E, 0x52, 0x9E, 0x53, /* 0xF8-0xFB */
+ 0x9E, 0x54, 0x9E, 0x55, 0x9E, 0x56, 0x9E, 0x57, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_70[512] = {
+ 0x9E, 0x58, 0x9E, 0x59, 0x9E, 0x5A, 0x9E, 0x5B, /* 0x00-0x03 */
+ 0x9E, 0x5C, 0x9E, 0x5D, 0x9E, 0x5E, 0x9E, 0x5F, /* 0x04-0x07 */
+ 0x9E, 0x60, 0x9E, 0x61, 0x9E, 0x62, 0x9E, 0x63, /* 0x08-0x0B */
+ 0x9E, 0x64, 0x9E, 0x65, 0x9E, 0x66, 0x9E, 0x67, /* 0x0C-0x0F */
+ 0x9E, 0x68, 0xC6, 0xD9, 0x9E, 0x69, 0x9E, 0x6A, /* 0x10-0x13 */
+ 0x9E, 0x6B, 0x9E, 0x6C, 0x9E, 0x6D, 0x9E, 0x6E, /* 0x14-0x17 */
+ 0x9E, 0x6F, 0x9E, 0x70, 0xE5, 0xAB, 0xE5, 0xAD, /* 0x18-0x1B */
+ 0x9E, 0x71, 0x9E, 0x72, 0x9E, 0x73, 0x9E, 0x74, /* 0x1C-0x1F */
+ 0x9E, 0x75, 0x9E, 0x76, 0x9E, 0x77, 0xE5, 0xAC, /* 0x20-0x23 */
+ 0x9E, 0x78, 0x9E, 0x79, 0x9E, 0x7A, 0x9E, 0x7B, /* 0x24-0x27 */
+ 0x9E, 0x7C, 0x9E, 0x7D, 0x9E, 0x7E, 0x9E, 0x80, /* 0x28-0x2B */
+ 0x9E, 0x81, 0x9E, 0x82, 0x9E, 0x83, 0x9E, 0x84, /* 0x2C-0x2F */
+ 0x9E, 0x85, 0x9E, 0x86, 0x9E, 0x87, 0x9E, 0x88, /* 0x30-0x33 */
+ 0x9E, 0x89, 0xE5, 0xAF, 0x9E, 0x8A, 0x9E, 0x8B, /* 0x34-0x37 */
+ 0x9E, 0x8C, 0xE5, 0xAE, 0x9E, 0x8D, 0x9E, 0x8E, /* 0x38-0x3B */
+ 0x9E, 0x8F, 0x9E, 0x90, 0x9E, 0x91, 0x9E, 0x92, /* 0x3C-0x3F */
+ 0x9E, 0x93, 0x9E, 0x94, 0x9E, 0x95, 0x9E, 0x96, /* 0x40-0x43 */
+ 0x9E, 0x97, 0x9E, 0x98, 0x9E, 0x99, 0x9E, 0x9A, /* 0x44-0x47 */
+ 0x9E, 0x9B, 0x9E, 0x9C, 0x9E, 0x9D, 0x9E, 0x9E, /* 0x48-0x4B */
+ 0xB9, 0xE0, 0x9E, 0x9F, 0x9E, 0xA0, 0xE5, 0xB0, /* 0x4C-0x4F */
+ 0x9E, 0xA1, 0x9E, 0xA2, 0x9E, 0xA3, 0x9E, 0xA4, /* 0x50-0x53 */
+ 0x9E, 0xA5, 0x9E, 0xA6, 0x9E, 0xA7, 0x9E, 0xA8, /* 0x54-0x57 */
+ 0x9E, 0xA9, 0x9E, 0xAA, 0x9E, 0xAB, 0x9E, 0xAC, /* 0x58-0x5B */
+ 0x9E, 0xAD, 0x9E, 0xAE, 0xE5, 0xB1, 0x9E, 0xAF, /* 0x5C-0x5F */
+ 0x9E, 0xB0, 0x9E, 0xB1, 0x9E, 0xB2, 0x9E, 0xB3, /* 0x60-0x63 */
+ 0x9E, 0xB4, 0x9E, 0xB5, 0x9E, 0xB6, 0x9E, 0xB7, /* 0x64-0x67 */
+ 0x9E, 0xB8, 0x9E, 0xB9, 0x9E, 0xBA, 0xBB, 0xF0, /* 0x68-0x6B */
+ 0xEC, 0xE1, 0xC3, 0xF0, 0x9E, 0xBB, 0xB5, 0xC6, /* 0x6C-0x6F */
+ 0xBB, 0xD2, 0x9E, 0xBC, 0x9E, 0xBD, 0x9E, 0xBE, /* 0x70-0x73 */
+ 0x9E, 0xBF, 0xC1, 0xE9, 0xD4, 0xEE, 0x9E, 0xC0, /* 0x74-0x77 */
+ 0xBE, 0xC4, 0x9E, 0xC1, 0x9E, 0xC2, 0x9E, 0xC3, /* 0x78-0x7B */
+ 0xD7, 0xC6, 0x9E, 0xC4, 0xD4, 0xD6, 0xB2, 0xD3, /* 0x7C-0x7F */
+
+ 0xEC, 0xBE, 0x9E, 0xC5, 0x9E, 0xC6, 0x9E, 0xC7, /* 0x80-0x83 */
+ 0x9E, 0xC8, 0xEA, 0xC1, 0x9E, 0xC9, 0x9E, 0xCA, /* 0x84-0x87 */
+ 0x9E, 0xCB, 0xC2, 0xAF, 0xB4, 0xB6, 0x9E, 0xCC, /* 0x88-0x8B */
+ 0x9E, 0xCD, 0x9E, 0xCE, 0xD1, 0xD7, 0x9E, 0xCF, /* 0x8C-0x8F */
+ 0x9E, 0xD0, 0x9E, 0xD1, 0xB3, 0xB4, 0x9E, 0xD2, /* 0x90-0x93 */
+ 0xC8, 0xB2, 0xBF, 0xBB, 0xEC, 0xC0, 0x9E, 0xD3, /* 0x94-0x97 */
+ 0x9E, 0xD4, 0xD6, 0xCB, 0x9E, 0xD5, 0x9E, 0xD6, /* 0x98-0x9B */
+ 0xEC, 0xBF, 0xEC, 0xC1, 0x9E, 0xD7, 0x9E, 0xD8, /* 0x9C-0x9F */
+ 0x9E, 0xD9, 0x9E, 0xDA, 0x9E, 0xDB, 0x9E, 0xDC, /* 0xA0-0xA3 */
+ 0x9E, 0xDD, 0x9E, 0xDE, 0x9E, 0xDF, 0x9E, 0xE0, /* 0xA4-0xA7 */
+ 0x9E, 0xE1, 0x9E, 0xE2, 0x9E, 0xE3, 0xEC, 0xC5, /* 0xA8-0xAB */
+ 0xBE, 0xE6, 0xCC, 0xBF, 0xC5, 0xDA, 0xBE, 0xBC, /* 0xAC-0xAF */
+ 0x9E, 0xE4, 0xEC, 0xC6, 0x9E, 0xE5, 0xB1, 0xFE, /* 0xB0-0xB3 */
+ 0x9E, 0xE6, 0x9E, 0xE7, 0x9E, 0xE8, 0xEC, 0xC4, /* 0xB4-0xB7 */
+ 0xD5, 0xA8, 0xB5, 0xE3, 0x9E, 0xE9, 0xEC, 0xC2, /* 0xB8-0xBB */
+ 0xC1, 0xB6, 0xB3, 0xE3, 0x9E, 0xEA, 0x9E, 0xEB, /* 0xBC-0xBF */
+ 0xEC, 0xC3, 0xCB, 0xB8, 0xC0, 0xC3, 0xCC, 0xFE, /* 0xC0-0xC3 */
+ 0x9E, 0xEC, 0x9E, 0xED, 0x9E, 0xEE, 0x9E, 0xEF, /* 0xC4-0xC7 */
+ 0xC1, 0xD2, 0x9E, 0xF0, 0xEC, 0xC8, 0x9E, 0xF1, /* 0xC8-0xCB */
+ 0x9E, 0xF2, 0x9E, 0xF3, 0x9E, 0xF4, 0x9E, 0xF5, /* 0xCC-0xCF */
+ 0x9E, 0xF6, 0x9E, 0xF7, 0x9E, 0xF8, 0x9E, 0xF9, /* 0xD0-0xD3 */
+ 0x9E, 0xFA, 0x9E, 0xFB, 0x9E, 0xFC, 0x9E, 0xFD, /* 0xD4-0xD7 */
+ 0xBA, 0xE6, 0xC0, 0xD3, 0x9E, 0xFE, 0xD6, 0xF2, /* 0xD8-0xDB */
+ 0x9F, 0x40, 0x9F, 0x41, 0x9F, 0x42, 0xD1, 0xCC, /* 0xDC-0xDF */
+ 0x9F, 0x43, 0x9F, 0x44, 0x9F, 0x45, 0x9F, 0x46, /* 0xE0-0xE3 */
+ 0xBF, 0xBE, 0x9F, 0x47, 0xB7, 0xB3, 0xC9, 0xD5, /* 0xE4-0xE7 */
+ 0xEC, 0xC7, 0xBB, 0xE2, 0x9F, 0x48, 0xCC, 0xCC, /* 0xE8-0xEB */
+ 0xBD, 0xFD, 0xC8, 0xC8, 0x9F, 0x49, 0xCF, 0xA9, /* 0xEC-0xEF */
+ 0x9F, 0x4A, 0x9F, 0x4B, 0x9F, 0x4C, 0x9F, 0x4D, /* 0xF0-0xF3 */
+ 0x9F, 0x4E, 0x9F, 0x4F, 0x9F, 0x50, 0xCD, 0xE9, /* 0xF4-0xF7 */
+ 0x9F, 0x51, 0xC5, 0xEB, 0x9F, 0x52, 0x9F, 0x53, /* 0xF8-0xFB */
+ 0x9F, 0x54, 0xB7, 0xE9, 0x9F, 0x55, 0x9F, 0x56, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_71[512] = {
+ 0x9F, 0x57, 0x9F, 0x58, 0x9F, 0x59, 0x9F, 0x5A, /* 0x00-0x03 */
+ 0x9F, 0x5B, 0x9F, 0x5C, 0x9F, 0x5D, 0x9F, 0x5E, /* 0x04-0x07 */
+ 0x9F, 0x5F, 0xD1, 0xC9, 0xBA, 0xB8, 0x9F, 0x60, /* 0x08-0x0B */
+ 0x9F, 0x61, 0x9F, 0x62, 0x9F, 0x63, 0x9F, 0x64, /* 0x0C-0x0F */
+ 0xEC, 0xC9, 0x9F, 0x65, 0x9F, 0x66, 0xEC, 0xCA, /* 0x10-0x13 */
+ 0x9F, 0x67, 0xBB, 0xC0, 0xEC, 0xCB, 0x9F, 0x68, /* 0x14-0x17 */
+ 0xEC, 0xE2, 0xB1, 0xBA, 0xB7, 0xD9, 0x9F, 0x69, /* 0x18-0x1B */
+ 0x9F, 0x6A, 0x9F, 0x6B, 0x9F, 0x6C, 0x9F, 0x6D, /* 0x1C-0x1F */
+ 0x9F, 0x6E, 0x9F, 0x6F, 0x9F, 0x70, 0x9F, 0x71, /* 0x20-0x23 */
+ 0x9F, 0x72, 0x9F, 0x73, 0xBD, 0xB9, 0x9F, 0x74, /* 0x24-0x27 */
+ 0x9F, 0x75, 0x9F, 0x76, 0x9F, 0x77, 0x9F, 0x78, /* 0x28-0x2B */
+ 0x9F, 0x79, 0x9F, 0x7A, 0x9F, 0x7B, 0xEC, 0xCC, /* 0x2C-0x2F */
+ 0xD1, 0xE6, 0xEC, 0xCD, 0x9F, 0x7C, 0x9F, 0x7D, /* 0x30-0x33 */
+ 0x9F, 0x7E, 0x9F, 0x80, 0xC8, 0xBB, 0x9F, 0x81, /* 0x34-0x37 */
+ 0x9F, 0x82, 0x9F, 0x83, 0x9F, 0x84, 0x9F, 0x85, /* 0x38-0x3B */
+ 0x9F, 0x86, 0x9F, 0x87, 0x9F, 0x88, 0x9F, 0x89, /* 0x3C-0x3F */
+ 0x9F, 0x8A, 0x9F, 0x8B, 0x9F, 0x8C, 0x9F, 0x8D, /* 0x40-0x43 */
+ 0x9F, 0x8E, 0xEC, 0xD1, 0x9F, 0x8F, 0x9F, 0x90, /* 0x44-0x47 */
+ 0x9F, 0x91, 0x9F, 0x92, 0xEC, 0xD3, 0x9F, 0x93, /* 0x48-0x4B */
+ 0xBB, 0xCD, 0x9F, 0x94, 0xBC, 0xE5, 0x9F, 0x95, /* 0x4C-0x4F */
+ 0x9F, 0x96, 0x9F, 0x97, 0x9F, 0x98, 0x9F, 0x99, /* 0x50-0x53 */
+ 0x9F, 0x9A, 0x9F, 0x9B, 0x9F, 0x9C, 0x9F, 0x9D, /* 0x54-0x57 */
+ 0x9F, 0x9E, 0x9F, 0x9F, 0x9F, 0xA0, 0x9F, 0xA1, /* 0x58-0x5B */
+ 0xEC, 0xCF, 0x9F, 0xA2, 0xC9, 0xB7, 0x9F, 0xA3, /* 0x5C-0x5F */
+ 0x9F, 0xA4, 0x9F, 0xA5, 0x9F, 0xA6, 0x9F, 0xA7, /* 0x60-0x63 */
+ 0xC3, 0xBA, 0x9F, 0xA8, 0xEC, 0xE3, 0xD5, 0xD5, /* 0x64-0x67 */
+ 0xEC, 0xD0, 0x9F, 0xA9, 0x9F, 0xAA, 0x9F, 0xAB, /* 0x68-0x6B */
+ 0x9F, 0xAC, 0x9F, 0xAD, 0xD6, 0xF3, 0x9F, 0xAE, /* 0x6C-0x6F */
+ 0x9F, 0xAF, 0x9F, 0xB0, 0xEC, 0xD2, 0xEC, 0xCE, /* 0x70-0x73 */
+ 0x9F, 0xB1, 0x9F, 0xB2, 0x9F, 0xB3, 0x9F, 0xB4, /* 0x74-0x77 */
+ 0xEC, 0xD4, 0x9F, 0xB5, 0xEC, 0xD5, 0x9F, 0xB6, /* 0x78-0x7B */
+ 0x9F, 0xB7, 0xC9, 0xBF, 0x9F, 0xB8, 0x9F, 0xB9, /* 0x7C-0x7F */
+
+ 0x9F, 0xBA, 0x9F, 0xBB, 0x9F, 0xBC, 0x9F, 0xBD, /* 0x80-0x83 */
+ 0xCF, 0xA8, 0x9F, 0xBE, 0x9F, 0xBF, 0x9F, 0xC0, /* 0x84-0x87 */
+ 0x9F, 0xC1, 0x9F, 0xC2, 0xD0, 0xDC, 0x9F, 0xC3, /* 0x88-0x8B */
+ 0x9F, 0xC4, 0x9F, 0xC5, 0x9F, 0xC6, 0xD1, 0xAC, /* 0x8C-0x8F */
+ 0x9F, 0xC7, 0x9F, 0xC8, 0x9F, 0xC9, 0x9F, 0xCA, /* 0x90-0x93 */
+ 0xC8, 0xDB, 0x9F, 0xCB, 0x9F, 0xCC, 0x9F, 0xCD, /* 0x94-0x97 */
+ 0xEC, 0xD6, 0xCE, 0xF5, 0x9F, 0xCE, 0x9F, 0xCF, /* 0x98-0x9B */
+ 0x9F, 0xD0, 0x9F, 0xD1, 0x9F, 0xD2, 0xCA, 0xEC, /* 0x9C-0x9F */
+ 0xEC, 0xDA, 0x9F, 0xD3, 0x9F, 0xD4, 0x9F, 0xD5, /* 0xA0-0xA3 */
+ 0x9F, 0xD6, 0x9F, 0xD7, 0x9F, 0xD8, 0x9F, 0xD9, /* 0xA4-0xA7 */
+ 0xEC, 0xD9, 0x9F, 0xDA, 0x9F, 0xDB, 0x9F, 0xDC, /* 0xA8-0xAB */
+ 0xB0, 0xBE, 0x9F, 0xDD, 0x9F, 0xDE, 0x9F, 0xDF, /* 0xAC-0xAF */
+ 0x9F, 0xE0, 0x9F, 0xE1, 0x9F, 0xE2, 0xEC, 0xD7, /* 0xB0-0xB3 */
+ 0x9F, 0xE3, 0xEC, 0xD8, 0x9F, 0xE4, 0x9F, 0xE5, /* 0xB4-0xB7 */
+ 0x9F, 0xE6, 0xEC, 0xE4, 0x9F, 0xE7, 0x9F, 0xE8, /* 0xB8-0xBB */
+ 0x9F, 0xE9, 0x9F, 0xEA, 0x9F, 0xEB, 0x9F, 0xEC, /* 0xBC-0xBF */
+ 0x9F, 0xED, 0x9F, 0xEE, 0x9F, 0xEF, 0xC8, 0xBC, /* 0xC0-0xC3 */
+ 0x9F, 0xF0, 0x9F, 0xF1, 0x9F, 0xF2, 0x9F, 0xF3, /* 0xC4-0xC7 */
+ 0x9F, 0xF4, 0x9F, 0xF5, 0x9F, 0xF6, 0x9F, 0xF7, /* 0xC8-0xCB */
+ 0x9F, 0xF8, 0x9F, 0xF9, 0xC1, 0xC7, 0x9F, 0xFA, /* 0xCC-0xCF */
+ 0x9F, 0xFB, 0x9F, 0xFC, 0x9F, 0xFD, 0x9F, 0xFE, /* 0xD0-0xD3 */
+ 0xEC, 0xDC, 0xD1, 0xE0, 0xA0, 0x40, 0xA0, 0x41, /* 0xD4-0xD7 */
+ 0xA0, 0x42, 0xA0, 0x43, 0xA0, 0x44, 0xA0, 0x45, /* 0xD8-0xDB */
+ 0xA0, 0x46, 0xA0, 0x47, 0xA0, 0x48, 0xA0, 0x49, /* 0xDC-0xDF */
+ 0xEC, 0xDB, 0xA0, 0x4A, 0xA0, 0x4B, 0xA0, 0x4C, /* 0xE0-0xE3 */
+ 0xA0, 0x4D, 0xD4, 0xEF, 0xA0, 0x4E, 0xEC, 0xDD, /* 0xE4-0xE7 */
+ 0xA0, 0x4F, 0xA0, 0x50, 0xA0, 0x51, 0xA0, 0x52, /* 0xE8-0xEB */
+ 0xA0, 0x53, 0xA0, 0x54, 0xDB, 0xC6, 0xA0, 0x55, /* 0xEC-0xEF */
+ 0xA0, 0x56, 0xA0, 0x57, 0xA0, 0x58, 0xA0, 0x59, /* 0xF0-0xF3 */
+ 0xA0, 0x5A, 0xA0, 0x5B, 0xA0, 0x5C, 0xA0, 0x5D, /* 0xF4-0xF7 */
+ 0xA0, 0x5E, 0xEC, 0xDE, 0xA0, 0x5F, 0xA0, 0x60, /* 0xF8-0xFB */
+ 0xA0, 0x61, 0xA0, 0x62, 0xA0, 0x63, 0xA0, 0x64, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_72[512] = {
+ 0xA0, 0x65, 0xA0, 0x66, 0xA0, 0x67, 0xA0, 0x68, /* 0x00-0x03 */
+ 0xA0, 0x69, 0xA0, 0x6A, 0xB1, 0xAC, 0xA0, 0x6B, /* 0x04-0x07 */
+ 0xA0, 0x6C, 0xA0, 0x6D, 0xA0, 0x6E, 0xA0, 0x6F, /* 0x08-0x0B */
+ 0xA0, 0x70, 0xA0, 0x71, 0xA0, 0x72, 0xA0, 0x73, /* 0x0C-0x0F */
+ 0xA0, 0x74, 0xA0, 0x75, 0xA0, 0x76, 0xA0, 0x77, /* 0x10-0x13 */
+ 0xA0, 0x78, 0xA0, 0x79, 0xA0, 0x7A, 0xA0, 0x7B, /* 0x14-0x17 */
+ 0xA0, 0x7C, 0xA0, 0x7D, 0xA0, 0x7E, 0xA0, 0x80, /* 0x18-0x1B */
+ 0xA0, 0x81, 0xEC, 0xDF, 0xA0, 0x82, 0xA0, 0x83, /* 0x1C-0x1F */
+ 0xA0, 0x84, 0xA0, 0x85, 0xA0, 0x86, 0xA0, 0x87, /* 0x20-0x23 */
+ 0xA0, 0x88, 0xA0, 0x89, 0xA0, 0x8A, 0xA0, 0x8B, /* 0x24-0x27 */
+ 0xEC, 0xE0, 0xA0, 0x8C, 0xD7, 0xA6, 0xA0, 0x8D, /* 0x28-0x2B */
+ 0xC5, 0xC0, 0xA0, 0x8E, 0xA0, 0x8F, 0xA0, 0x90, /* 0x2C-0x2F */
+ 0xEB, 0xBC, 0xB0, 0xAE, 0xA0, 0x91, 0xA0, 0x92, /* 0x30-0x33 */
+ 0xA0, 0x93, 0xBE, 0xF4, 0xB8, 0xB8, 0xD2, 0xAF, /* 0x34-0x37 */
+ 0xB0, 0xD6, 0xB5, 0xF9, 0xA0, 0x94, 0xD8, 0xB3, /* 0x38-0x3B */
+ 0xA0, 0x95, 0xCB, 0xAC, 0xA0, 0x96, 0xE3, 0xDD, /* 0x3C-0x3F */
+ 0xA0, 0x97, 0xA0, 0x98, 0xA0, 0x99, 0xA0, 0x9A, /* 0x40-0x43 */
+ 0xA0, 0x9B, 0xA0, 0x9C, 0xA0, 0x9D, 0xC6, 0xAC, /* 0x44-0x47 */
+ 0xB0, 0xE6, 0xA0, 0x9E, 0xA0, 0x9F, 0xA0, 0xA0, /* 0x48-0x4B */
+ 0xC5, 0xC6, 0xEB, 0xB9, 0xA0, 0xA1, 0xA0, 0xA2, /* 0x4C-0x4F */
+ 0xA0, 0xA3, 0xA0, 0xA4, 0xEB, 0xBA, 0xA0, 0xA5, /* 0x50-0x53 */
+ 0xA0, 0xA6, 0xA0, 0xA7, 0xEB, 0xBB, 0xA0, 0xA8, /* 0x54-0x57 */
+ 0xA0, 0xA9, 0xD1, 0xC0, 0xA0, 0xAA, 0xC5, 0xA3, /* 0x58-0x5B */
+ 0xA0, 0xAB, 0xEA, 0xF2, 0xA0, 0xAC, 0xC4, 0xB2, /* 0x5C-0x5F */
+ 0xA0, 0xAD, 0xC4, 0xB5, 0xC0, 0xCE, 0xA0, 0xAE, /* 0x60-0x63 */
+ 0xA0, 0xAF, 0xA0, 0xB0, 0xEA, 0xF3, 0xC4, 0xC1, /* 0x64-0x67 */
+ 0xA0, 0xB1, 0xCE, 0xEF, 0xA0, 0xB2, 0xA0, 0xB3, /* 0x68-0x6B */
+ 0xA0, 0xB4, 0xA0, 0xB5, 0xEA, 0xF0, 0xEA, 0xF4, /* 0x6C-0x6F */
+ 0xA0, 0xB6, 0xA0, 0xB7, 0xC9, 0xFC, 0xA0, 0xB8, /* 0x70-0x73 */
+ 0xA0, 0xB9, 0xC7, 0xA3, 0xA0, 0xBA, 0xA0, 0xBB, /* 0x74-0x77 */
+ 0xA0, 0xBC, 0xCC, 0xD8, 0xCE, 0xFE, 0xA0, 0xBD, /* 0x78-0x7B */
+ 0xA0, 0xBE, 0xA0, 0xBF, 0xEA, 0xF5, 0xEA, 0xF6, /* 0x7C-0x7F */
+
+ 0xCF, 0xAC, 0xC0, 0xE7, 0xA0, 0xC0, 0xA0, 0xC1, /* 0x80-0x83 */
+ 0xEA, 0xF7, 0xA0, 0xC2, 0xA0, 0xC3, 0xA0, 0xC4, /* 0x84-0x87 */
+ 0xA0, 0xC5, 0xA0, 0xC6, 0xB6, 0xBF, 0xEA, 0xF8, /* 0x88-0x8B */
+ 0xA0, 0xC7, 0xEA, 0xF9, 0xA0, 0xC8, 0xEA, 0xFA, /* 0x8C-0x8F */
+ 0xA0, 0xC9, 0xA0, 0xCA, 0xEA, 0xFB, 0xA0, 0xCB, /* 0x90-0x93 */
+ 0xA0, 0xCC, 0xA0, 0xCD, 0xA0, 0xCE, 0xA0, 0xCF, /* 0x94-0x97 */
+ 0xA0, 0xD0, 0xA0, 0xD1, 0xA0, 0xD2, 0xA0, 0xD3, /* 0x98-0x9B */
+ 0xA0, 0xD4, 0xA0, 0xD5, 0xA0, 0xD6, 0xEA, 0xF1, /* 0x9C-0x9F */
+ 0xA0, 0xD7, 0xA0, 0xD8, 0xA0, 0xD9, 0xA0, 0xDA, /* 0xA0-0xA3 */
+ 0xA0, 0xDB, 0xA0, 0xDC, 0xA0, 0xDD, 0xA0, 0xDE, /* 0xA4-0xA7 */
+ 0xA0, 0xDF, 0xA0, 0xE0, 0xA0, 0xE1, 0xA0, 0xE2, /* 0xA8-0xAB */
+ 0xC8, 0xAE, 0xE1, 0xEB, 0xA0, 0xE3, 0xB7, 0xB8, /* 0xAC-0xAF */
+ 0xE1, 0xEC, 0xA0, 0xE4, 0xA0, 0xE5, 0xA0, 0xE6, /* 0xB0-0xB3 */
+ 0xE1, 0xED, 0xA0, 0xE7, 0xD7, 0xB4, 0xE1, 0xEE, /* 0xB4-0xB7 */
+ 0xE1, 0xEF, 0xD3, 0xCC, 0xA0, 0xE8, 0xA0, 0xE9, /* 0xB8-0xBB */
+ 0xA0, 0xEA, 0xA0, 0xEB, 0xA0, 0xEC, 0xA0, 0xED, /* 0xBC-0xBF */
+ 0xA0, 0xEE, 0xE1, 0xF1, 0xBF, 0xF1, 0xE1, 0xF0, /* 0xC0-0xC3 */
+ 0xB5, 0xD2, 0xA0, 0xEF, 0xA0, 0xF0, 0xA0, 0xF1, /* 0xC4-0xC7 */
+ 0xB1, 0xB7, 0xA0, 0xF2, 0xA0, 0xF3, 0xA0, 0xF4, /* 0xC8-0xCB */
+ 0xA0, 0xF5, 0xE1, 0xF3, 0xE1, 0xF2, 0xA0, 0xF6, /* 0xCC-0xCF */
+ 0xBA, 0xFC, 0xA0, 0xF7, 0xE1, 0xF4, 0xA0, 0xF8, /* 0xD0-0xD3 */
+ 0xA0, 0xF9, 0xA0, 0xFA, 0xA0, 0xFB, 0xB9, 0xB7, /* 0xD4-0xD7 */
+ 0xA0, 0xFC, 0xBE, 0xD1, 0xA0, 0xFD, 0xA0, 0xFE, /* 0xD8-0xDB */
+ 0xAA, 0x40, 0xAA, 0x41, 0xC4, 0xFC, 0xAA, 0x42, /* 0xDC-0xDF */
+ 0xBA, 0xDD, 0xBD, 0xC6, 0xAA, 0x43, 0xAA, 0x44, /* 0xE0-0xE3 */
+ 0xAA, 0x45, 0xAA, 0x46, 0xAA, 0x47, 0xAA, 0x48, /* 0xE4-0xE7 */
+ 0xE1, 0xF5, 0xE1, 0xF7, 0xAA, 0x49, 0xAA, 0x4A, /* 0xE8-0xEB */
+ 0xB6, 0xC0, 0xCF, 0xC1, 0xCA, 0xA8, 0xE1, 0xF6, /* 0xEC-0xEF */
+ 0xD5, 0xF8, 0xD3, 0xFC, 0xE1, 0xF8, 0xE1, 0xFC, /* 0xF0-0xF3 */
+ 0xE1, 0xF9, 0xAA, 0x4B, 0xAA, 0x4C, 0xE1, 0xFA, /* 0xF4-0xF7 */
+ 0xC0, 0xEA, 0xAA, 0x4D, 0xE1, 0xFE, 0xE2, 0xA1, /* 0xF8-0xFB */
+ 0xC0, 0xC7, 0xAA, 0x4E, 0xAA, 0x4F, 0xAA, 0x50, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_73[512] = {
+ 0xAA, 0x51, 0xE1, 0xFB, 0xAA, 0x52, 0xE1, 0xFD, /* 0x00-0x03 */
+ 0xAA, 0x53, 0xAA, 0x54, 0xAA, 0x55, 0xAA, 0x56, /* 0x04-0x07 */
+ 0xAA, 0x57, 0xAA, 0x58, 0xE2, 0xA5, 0xAA, 0x59, /* 0x08-0x0B */
+ 0xAA, 0x5A, 0xAA, 0x5B, 0xC1, 0xD4, 0xAA, 0x5C, /* 0x0C-0x0F */
+ 0xAA, 0x5D, 0xAA, 0x5E, 0xAA, 0x5F, 0xE2, 0xA3, /* 0x10-0x13 */
+ 0xAA, 0x60, 0xE2, 0xA8, 0xB2, 0xFE, 0xE2, 0xA2, /* 0x14-0x17 */
+ 0xAA, 0x61, 0xAA, 0x62, 0xAA, 0x63, 0xC3, 0xCD, /* 0x18-0x1B */
+ 0xB2, 0xC2, 0xE2, 0xA7, 0xE2, 0xA6, 0xAA, 0x64, /* 0x1C-0x1F */
+ 0xAA, 0x65, 0xE2, 0xA4, 0xE2, 0xA9, 0xAA, 0x66, /* 0x20-0x23 */
+ 0xAA, 0x67, 0xE2, 0xAB, 0xAA, 0x68, 0xAA, 0x69, /* 0x24-0x27 */
+ 0xAA, 0x6A, 0xD0, 0xC9, 0xD6, 0xED, 0xC3, 0xA8, /* 0x28-0x2B */
+ 0xE2, 0xAC, 0xAA, 0x6B, 0xCF, 0xD7, 0xAA, 0x6C, /* 0x2C-0x2F */
+ 0xAA, 0x6D, 0xE2, 0xAE, 0xAA, 0x6E, 0xAA, 0x6F, /* 0x30-0x33 */
+ 0xBA, 0xEF, 0xAA, 0x70, 0xAA, 0x71, 0xE9, 0xE0, /* 0x34-0x37 */
+ 0xE2, 0xAD, 0xE2, 0xAA, 0xAA, 0x72, 0xAA, 0x73, /* 0x38-0x3B */
+ 0xAA, 0x74, 0xAA, 0x75, 0xBB, 0xAB, 0xD4, 0xB3, /* 0x3C-0x3F */
+ 0xAA, 0x76, 0xAA, 0x77, 0xAA, 0x78, 0xAA, 0x79, /* 0x40-0x43 */
+ 0xAA, 0x7A, 0xAA, 0x7B, 0xAA, 0x7C, 0xAA, 0x7D, /* 0x44-0x47 */
+ 0xAA, 0x7E, 0xAA, 0x80, 0xAA, 0x81, 0xAA, 0x82, /* 0x48-0x4B */
+ 0xAA, 0x83, 0xE2, 0xB0, 0xAA, 0x84, 0xAA, 0x85, /* 0x4C-0x4F */
+ 0xE2, 0xAF, 0xAA, 0x86, 0xE9, 0xE1, 0xAA, 0x87, /* 0x50-0x53 */
+ 0xAA, 0x88, 0xAA, 0x89, 0xAA, 0x8A, 0xE2, 0xB1, /* 0x54-0x57 */
+ 0xAA, 0x8B, 0xAA, 0x8C, 0xAA, 0x8D, 0xAA, 0x8E, /* 0x58-0x5B */
+ 0xAA, 0x8F, 0xAA, 0x90, 0xAA, 0x91, 0xAA, 0x92, /* 0x5C-0x5F */
+ 0xE2, 0xB2, 0xAA, 0x93, 0xAA, 0x94, 0xAA, 0x95, /* 0x60-0x63 */
+ 0xAA, 0x96, 0xAA, 0x97, 0xAA, 0x98, 0xAA, 0x99, /* 0x64-0x67 */
+ 0xAA, 0x9A, 0xAA, 0x9B, 0xAA, 0x9C, 0xAA, 0x9D, /* 0x68-0x6B */
+ 0xE2, 0xB3, 0xCC, 0xA1, 0xAA, 0x9E, 0xE2, 0xB4, /* 0x6C-0x6F */
+ 0xAA, 0x9F, 0xAA, 0xA0, 0xAB, 0x40, 0xAB, 0x41, /* 0x70-0x73 */
+ 0xAB, 0x42, 0xAB, 0x43, 0xAB, 0x44, 0xAB, 0x45, /* 0x74-0x77 */
+ 0xAB, 0x46, 0xAB, 0x47, 0xAB, 0x48, 0xAB, 0x49, /* 0x78-0x7B */
+ 0xAB, 0x4A, 0xAB, 0x4B, 0xE2, 0xB5, 0xAB, 0x4C, /* 0x7C-0x7F */
+
+ 0xAB, 0x4D, 0xAB, 0x4E, 0xAB, 0x4F, 0xAB, 0x50, /* 0x80-0x83 */
+ 0xD0, 0xFE, 0xAB, 0x51, 0xAB, 0x52, 0xC2, 0xCA, /* 0x84-0x87 */
+ 0xAB, 0x53, 0xD3, 0xF1, 0xAB, 0x54, 0xCD, 0xF5, /* 0x88-0x8B */
+ 0xAB, 0x55, 0xAB, 0x56, 0xE7, 0xE0, 0xAB, 0x57, /* 0x8C-0x8F */
+ 0xAB, 0x58, 0xE7, 0xE1, 0xAB, 0x59, 0xAB, 0x5A, /* 0x90-0x93 */
+ 0xAB, 0x5B, 0xAB, 0x5C, 0xBE, 0xC1, 0xAB, 0x5D, /* 0x94-0x97 */
+ 0xAB, 0x5E, 0xAB, 0x5F, 0xAB, 0x60, 0xC2, 0xEA, /* 0x98-0x9B */
+ 0xAB, 0x61, 0xAB, 0x62, 0xAB, 0x63, 0xE7, 0xE4, /* 0x9C-0x9F */
+ 0xAB, 0x64, 0xAB, 0x65, 0xE7, 0xE3, 0xAB, 0x66, /* 0xA0-0xA3 */
+ 0xAB, 0x67, 0xAB, 0x68, 0xAB, 0x69, 0xAB, 0x6A, /* 0xA4-0xA7 */
+ 0xAB, 0x6B, 0xCD, 0xE6, 0xAB, 0x6C, 0xC3, 0xB5, /* 0xA8-0xAB */
+ 0xAB, 0x6D, 0xAB, 0x6E, 0xE7, 0xE2, 0xBB, 0xB7, /* 0xAC-0xAF */
+ 0xCF, 0xD6, 0xAB, 0x6F, 0xC1, 0xE1, 0xE7, 0xE9, /* 0xB0-0xB3 */
+ 0xAB, 0x70, 0xAB, 0x71, 0xAB, 0x72, 0xE7, 0xE8, /* 0xB4-0xB7 */
+ 0xAB, 0x73, 0xAB, 0x74, 0xE7, 0xF4, 0xB2, 0xA3, /* 0xB8-0xBB */
+ 0xAB, 0x75, 0xAB, 0x76, 0xAB, 0x77, 0xAB, 0x78, /* 0xBC-0xBF */
+ 0xE7, 0xEA, 0xAB, 0x79, 0xE7, 0xE6, 0xAB, 0x7A, /* 0xC0-0xC3 */
+ 0xAB, 0x7B, 0xAB, 0x7C, 0xAB, 0x7D, 0xAB, 0x7E, /* 0xC4-0xC7 */
+ 0xE7, 0xEC, 0xE7, 0xEB, 0xC9, 0xBA, 0xAB, 0x80, /* 0xC8-0xCB */
+ 0xAB, 0x81, 0xD5, 0xE4, 0xAB, 0x82, 0xE7, 0xE5, /* 0xCC-0xCF */
+ 0xB7, 0xA9, 0xE7, 0xE7, 0xAB, 0x83, 0xAB, 0x84, /* 0xD0-0xD3 */
+ 0xAB, 0x85, 0xAB, 0x86, 0xAB, 0x87, 0xAB, 0x88, /* 0xD4-0xD7 */
+ 0xAB, 0x89, 0xE7, 0xEE, 0xAB, 0x8A, 0xAB, 0x8B, /* 0xD8-0xDB */
+ 0xAB, 0x8C, 0xAB, 0x8D, 0xE7, 0xF3, 0xAB, 0x8E, /* 0xDC-0xDF */
+ 0xD6, 0xE9, 0xAB, 0x8F, 0xAB, 0x90, 0xAB, 0x91, /* 0xE0-0xE3 */
+ 0xAB, 0x92, 0xE7, 0xED, 0xAB, 0x93, 0xE7, 0xF2, /* 0xE4-0xE7 */
+ 0xAB, 0x94, 0xE7, 0xF1, 0xAB, 0x95, 0xAB, 0x96, /* 0xE8-0xEB */
+ 0xAB, 0x97, 0xB0, 0xE0, 0xAB, 0x98, 0xAB, 0x99, /* 0xEC-0xEF */
+ 0xAB, 0x9A, 0xAB, 0x9B, 0xE7, 0xF5, 0xAB, 0x9C, /* 0xF0-0xF3 */
+ 0xAB, 0x9D, 0xAB, 0x9E, 0xAB, 0x9F, 0xAB, 0xA0, /* 0xF4-0xF7 */
+ 0xAC, 0x40, 0xAC, 0x41, 0xAC, 0x42, 0xAC, 0x43, /* 0xF8-0xFB */
+ 0xAC, 0x44, 0xAC, 0x45, 0xAC, 0x46, 0xAC, 0x47, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_74[512] = {
+ 0xAC, 0x48, 0xAC, 0x49, 0xAC, 0x4A, 0xC7, 0xF2, /* 0x00-0x03 */
+ 0xAC, 0x4B, 0xC0, 0xC5, 0xC0, 0xED, 0xAC, 0x4C, /* 0x04-0x07 */
+ 0xAC, 0x4D, 0xC1, 0xF0, 0xE7, 0xF0, 0xAC, 0x4E, /* 0x08-0x0B */
+ 0xAC, 0x4F, 0xAC, 0x50, 0xAC, 0x51, 0xE7, 0xF6, /* 0x0C-0x0F */
+ 0xCB, 0xF6, 0xAC, 0x52, 0xAC, 0x53, 0xAC, 0x54, /* 0x10-0x13 */
+ 0xAC, 0x55, 0xAC, 0x56, 0xAC, 0x57, 0xAC, 0x58, /* 0x14-0x17 */
+ 0xAC, 0x59, 0xAC, 0x5A, 0xE8, 0xA2, 0xE8, 0xA1, /* 0x18-0x1B */
+ 0xAC, 0x5B, 0xAC, 0x5C, 0xAC, 0x5D, 0xAC, 0x5E, /* 0x1C-0x1F */
+ 0xAC, 0x5F, 0xAC, 0x60, 0xD7, 0xC1, 0xAC, 0x61, /* 0x20-0x23 */
+ 0xAC, 0x62, 0xE7, 0xFA, 0xE7, 0xF9, 0xAC, 0x63, /* 0x24-0x27 */
+ 0xE7, 0xFB, 0xAC, 0x64, 0xE7, 0xF7, 0xAC, 0x65, /* 0x28-0x2B */
+ 0xE7, 0xFE, 0xAC, 0x66, 0xE7, 0xFD, 0xAC, 0x67, /* 0x2C-0x2F */
+ 0xE7, 0xFC, 0xAC, 0x68, 0xAC, 0x69, 0xC1, 0xD5, /* 0x30-0x33 */
+ 0xC7, 0xD9, 0xC5, 0xFD, 0xC5, 0xC3, 0xAC, 0x6A, /* 0x34-0x37 */
+ 0xAC, 0x6B, 0xAC, 0x6C, 0xAC, 0x6D, 0xAC, 0x6E, /* 0x38-0x3B */
+ 0xC7, 0xED, 0xAC, 0x6F, 0xAC, 0x70, 0xAC, 0x71, /* 0x3C-0x3F */
+ 0xAC, 0x72, 0xE8, 0xA3, 0xAC, 0x73, 0xAC, 0x74, /* 0x40-0x43 */
+ 0xAC, 0x75, 0xAC, 0x76, 0xAC, 0x77, 0xAC, 0x78, /* 0x44-0x47 */
+ 0xAC, 0x79, 0xAC, 0x7A, 0xAC, 0x7B, 0xAC, 0x7C, /* 0x48-0x4B */
+ 0xAC, 0x7D, 0xAC, 0x7E, 0xAC, 0x80, 0xAC, 0x81, /* 0x4C-0x4F */
+ 0xAC, 0x82, 0xAC, 0x83, 0xAC, 0x84, 0xAC, 0x85, /* 0x50-0x53 */
+ 0xAC, 0x86, 0xE8, 0xA6, 0xAC, 0x87, 0xE8, 0xA5, /* 0x54-0x57 */
+ 0xAC, 0x88, 0xE8, 0xA7, 0xBA, 0xF7, 0xE7, 0xF8, /* 0x58-0x5B */
+ 0xE8, 0xA4, 0xAC, 0x89, 0xC8, 0xF0, 0xC9, 0xAA, /* 0x5C-0x5F */
+ 0xAC, 0x8A, 0xAC, 0x8B, 0xAC, 0x8C, 0xAC, 0x8D, /* 0x60-0x63 */
+ 0xAC, 0x8E, 0xAC, 0x8F, 0xAC, 0x90, 0xAC, 0x91, /* 0x64-0x67 */
+ 0xAC, 0x92, 0xAC, 0x93, 0xAC, 0x94, 0xAC, 0x95, /* 0x68-0x6B */
+ 0xAC, 0x96, 0xE8, 0xA9, 0xAC, 0x97, 0xAC, 0x98, /* 0x6C-0x6F */
+ 0xB9, 0xE5, 0xAC, 0x99, 0xAC, 0x9A, 0xAC, 0x9B, /* 0x70-0x73 */
+ 0xAC, 0x9C, 0xAC, 0x9D, 0xD1, 0xFE, 0xE8, 0xA8, /* 0x74-0x77 */
+ 0xAC, 0x9E, 0xAC, 0x9F, 0xAC, 0xA0, 0xAD, 0x40, /* 0x78-0x7B */
+ 0xAD, 0x41, 0xAD, 0x42, 0xE8, 0xAA, 0xAD, 0x43, /* 0x7C-0x7F */
+
+ 0xE8, 0xAD, 0xE8, 0xAE, 0xAD, 0x44, 0xC1, 0xA7, /* 0x80-0x83 */
+ 0xAD, 0x45, 0xAD, 0x46, 0xAD, 0x47, 0xE8, 0xAF, /* 0x84-0x87 */
+ 0xAD, 0x48, 0xAD, 0x49, 0xAD, 0x4A, 0xE8, 0xB0, /* 0x88-0x8B */
+ 0xAD, 0x4B, 0xAD, 0x4C, 0xE8, 0xAC, 0xAD, 0x4D, /* 0x8C-0x8F */
+ 0xE8, 0xB4, 0xAD, 0x4E, 0xAD, 0x4F, 0xAD, 0x50, /* 0x90-0x93 */
+ 0xAD, 0x51, 0xAD, 0x52, 0xAD, 0x53, 0xAD, 0x54, /* 0x94-0x97 */
+ 0xAD, 0x55, 0xAD, 0x56, 0xAD, 0x57, 0xAD, 0x58, /* 0x98-0x9B */
+ 0xE8, 0xAB, 0xAD, 0x59, 0xE8, 0xB1, 0xAD, 0x5A, /* 0x9C-0x9F */
+ 0xAD, 0x5B, 0xAD, 0x5C, 0xAD, 0x5D, 0xAD, 0x5E, /* 0xA0-0xA3 */
+ 0xAD, 0x5F, 0xAD, 0x60, 0xAD, 0x61, 0xE8, 0xB5, /* 0xA4-0xA7 */
+ 0xE8, 0xB2, 0xE8, 0xB3, 0xAD, 0x62, 0xAD, 0x63, /* 0xA8-0xAB */
+ 0xAD, 0x64, 0xAD, 0x65, 0xAD, 0x66, 0xAD, 0x67, /* 0xAC-0xAF */
+ 0xAD, 0x68, 0xAD, 0x69, 0xAD, 0x6A, 0xAD, 0x6B, /* 0xB0-0xB3 */
+ 0xAD, 0x6C, 0xAD, 0x6D, 0xAD, 0x6E, 0xAD, 0x6F, /* 0xB4-0xB7 */
+ 0xAD, 0x70, 0xAD, 0x71, 0xE8, 0xB7, 0xAD, 0x72, /* 0xB8-0xBB */
+ 0xAD, 0x73, 0xAD, 0x74, 0xAD, 0x75, 0xAD, 0x76, /* 0xBC-0xBF */
+ 0xAD, 0x77, 0xAD, 0x78, 0xAD, 0x79, 0xAD, 0x7A, /* 0xC0-0xC3 */
+ 0xAD, 0x7B, 0xAD, 0x7C, 0xAD, 0x7D, 0xAD, 0x7E, /* 0xC4-0xC7 */
+ 0xAD, 0x80, 0xAD, 0x81, 0xAD, 0x82, 0xAD, 0x83, /* 0xC8-0xCB */
+ 0xAD, 0x84, 0xAD, 0x85, 0xAD, 0x86, 0xAD, 0x87, /* 0xCC-0xCF */
+ 0xAD, 0x88, 0xAD, 0x89, 0xE8, 0xB6, 0xAD, 0x8A, /* 0xD0-0xD3 */
+ 0xAD, 0x8B, 0xAD, 0x8C, 0xAD, 0x8D, 0xAD, 0x8E, /* 0xD4-0xD7 */
+ 0xAD, 0x8F, 0xAD, 0x90, 0xAD, 0x91, 0xAD, 0x92, /* 0xD8-0xDB */
+ 0xB9, 0xCF, 0xAD, 0x93, 0xF0, 0xAC, 0xAD, 0x94, /* 0xDC-0xDF */
+ 0xF0, 0xAD, 0xAD, 0x95, 0xC6, 0xB0, 0xB0, 0xEA, /* 0xE0-0xE3 */
+ 0xC8, 0xBF, 0xAD, 0x96, 0xCD, 0xDF, 0xAD, 0x97, /* 0xE4-0xE7 */
+ 0xAD, 0x98, 0xAD, 0x99, 0xAD, 0x9A, 0xAD, 0x9B, /* 0xE8-0xEB */
+ 0xAD, 0x9C, 0xAD, 0x9D, 0xCE, 0xCD, 0xEA, 0xB1, /* 0xEC-0xEF */
+ 0xAD, 0x9E, 0xAD, 0x9F, 0xAD, 0xA0, 0xAE, 0x40, /* 0xF0-0xF3 */
+ 0xEA, 0xB2, 0xAE, 0x41, 0xC6, 0xBF, 0xB4, 0xC9, /* 0xF4-0xF7 */
+ 0xAE, 0x42, 0xAE, 0x43, 0xAE, 0x44, 0xAE, 0x45, /* 0xF8-0xFB */
+ 0xAE, 0x46, 0xAE, 0x47, 0xAE, 0x48, 0xEA, 0xB3, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_75[512] = {
+ 0xAE, 0x49, 0xAE, 0x4A, 0xAE, 0x4B, 0xAE, 0x4C, /* 0x00-0x03 */
+ 0xD5, 0xE7, 0xAE, 0x4D, 0xAE, 0x4E, 0xAE, 0x4F, /* 0x04-0x07 */
+ 0xAE, 0x50, 0xAE, 0x51, 0xAE, 0x52, 0xAE, 0x53, /* 0x08-0x0B */
+ 0xAE, 0x54, 0xDD, 0xF9, 0xAE, 0x55, 0xEA, 0xB4, /* 0x0C-0x0F */
+ 0xAE, 0x56, 0xEA, 0xB5, 0xAE, 0x57, 0xEA, 0xB6, /* 0x10-0x13 */
+ 0xAE, 0x58, 0xAE, 0x59, 0xAE, 0x5A, 0xAE, 0x5B, /* 0x14-0x17 */
+ 0xB8, 0xCA, 0xDF, 0xB0, 0xC9, 0xF5, 0xAE, 0x5C, /* 0x18-0x1B */
+ 0xCC, 0xF0, 0xAE, 0x5D, 0xAE, 0x5E, 0xC9, 0xFA, /* 0x1C-0x1F */
+ 0xAE, 0x5F, 0xAE, 0x60, 0xAE, 0x61, 0xAE, 0x62, /* 0x20-0x23 */
+ 0xAE, 0x63, 0xC9, 0xFB, 0xAE, 0x64, 0xAE, 0x65, /* 0x24-0x27 */
+ 0xD3, 0xC3, 0xCB, 0xA6, 0xAE, 0x66, 0xB8, 0xA6, /* 0x28-0x2B */
+ 0xF0, 0xAE, 0xB1, 0xC2, 0xAE, 0x67, 0xE5, 0xB8, /* 0x2C-0x2F */
+ 0xCC, 0xEF, 0xD3, 0xC9, 0xBC, 0xD7, 0xC9, 0xEA, /* 0x30-0x33 */
+ 0xAE, 0x68, 0xB5, 0xE7, 0xAE, 0x69, 0xC4, 0xD0, /* 0x34-0x37 */
+ 0xB5, 0xE9, 0xAE, 0x6A, 0xEE, 0xAE, 0xBB, 0xAD, /* 0x38-0x3B */
+ 0xAE, 0x6B, 0xAE, 0x6C, 0xE7, 0xDE, 0xAE, 0x6D, /* 0x3C-0x3F */
+ 0xEE, 0xAF, 0xAE, 0x6E, 0xAE, 0x6F, 0xAE, 0x70, /* 0x40-0x43 */
+ 0xAE, 0x71, 0xB3, 0xA9, 0xAE, 0x72, 0xAE, 0x73, /* 0x44-0x47 */
+ 0xEE, 0xB2, 0xAE, 0x74, 0xAE, 0x75, 0xEE, 0xB1, /* 0x48-0x4B */
+ 0xBD, 0xE7, 0xAE, 0x76, 0xEE, 0xB0, 0xCE, 0xB7, /* 0x4C-0x4F */
+ 0xAE, 0x77, 0xAE, 0x78, 0xAE, 0x79, 0xAE, 0x7A, /* 0x50-0x53 */
+ 0xC5, 0xCF, 0xAE, 0x7B, 0xAE, 0x7C, 0xAE, 0x7D, /* 0x54-0x57 */
+ 0xAE, 0x7E, 0xC1, 0xF4, 0xDB, 0xCE, 0xEE, 0xB3, /* 0x58-0x5B */
+ 0xD0, 0xF3, 0xAE, 0x80, 0xAE, 0x81, 0xAE, 0x82, /* 0x5C-0x5F */
+ 0xAE, 0x83, 0xAE, 0x84, 0xAE, 0x85, 0xAE, 0x86, /* 0x60-0x63 */
+ 0xAE, 0x87, 0xC2, 0xD4, 0xC6, 0xE8, 0xAE, 0x88, /* 0x64-0x67 */
+ 0xAE, 0x89, 0xAE, 0x8A, 0xB7, 0xAC, 0xAE, 0x8B, /* 0x68-0x6B */
+ 0xAE, 0x8C, 0xAE, 0x8D, 0xAE, 0x8E, 0xAE, 0x8F, /* 0x6C-0x6F */
+ 0xAE, 0x90, 0xAE, 0x91, 0xEE, 0xB4, 0xAE, 0x92, /* 0x70-0x73 */
+ 0xB3, 0xEB, 0xAE, 0x93, 0xAE, 0x94, 0xAE, 0x95, /* 0x74-0x77 */
+ 0xBB, 0xFB, 0xEE, 0xB5, 0xAE, 0x96, 0xAE, 0x97, /* 0x78-0x7B */
+ 0xAE, 0x98, 0xAE, 0x99, 0xAE, 0x9A, 0xE7, 0xDC, /* 0x7C-0x7F */
+
+ 0xAE, 0x9B, 0xAE, 0x9C, 0xAE, 0x9D, 0xEE, 0xB6, /* 0x80-0x83 */
+ 0xAE, 0x9E, 0xAE, 0x9F, 0xBD, 0xAE, 0xAE, 0xA0, /* 0x84-0x87 */
+ 0xAF, 0x40, 0xAF, 0x41, 0xAF, 0x42, 0xF1, 0xE2, /* 0x88-0x8B */
+ 0xAF, 0x43, 0xAF, 0x44, 0xAF, 0x45, 0xCA, 0xE8, /* 0x8C-0x8F */
+ 0xAF, 0x46, 0xD2, 0xC9, 0xF0, 0xDA, 0xAF, 0x47, /* 0x90-0x93 */
+ 0xF0, 0xDB, 0xAF, 0x48, 0xF0, 0xDC, 0xC1, 0xC6, /* 0x94-0x97 */
+ 0xAF, 0x49, 0xB8, 0xED, 0xBE, 0xCE, 0xAF, 0x4A, /* 0x98-0x9B */
+ 0xAF, 0x4B, 0xF0, 0xDE, 0xAF, 0x4C, 0xC5, 0xB1, /* 0x9C-0x9F */
+ 0xF0, 0xDD, 0xD1, 0xF1, 0xAF, 0x4D, 0xF0, 0xE0, /* 0xA0-0xA3 */
+ 0xB0, 0xCC, 0xBD, 0xEA, 0xAF, 0x4E, 0xAF, 0x4F, /* 0xA4-0xA7 */
+ 0xAF, 0x50, 0xAF, 0x51, 0xAF, 0x52, 0xD2, 0xDF, /* 0xA8-0xAB */
+ 0xF0, 0xDF, 0xAF, 0x53, 0xB4, 0xAF, 0xB7, 0xE8, /* 0xAC-0xAF */
+ 0xF0, 0xE6, 0xF0, 0xE5, 0xC6, 0xA3, 0xF0, 0xE1, /* 0xB0-0xB3 */
+ 0xF0, 0xE2, 0xB4, 0xC3, 0xAF, 0x54, 0xAF, 0x55, /* 0xB4-0xB7 */
+ 0xF0, 0xE3, 0xD5, 0xEE, 0xAF, 0x56, 0xAF, 0x57, /* 0xB8-0xBB */
+ 0xCC, 0xDB, 0xBE, 0xD2, 0xBC, 0xB2, 0xAF, 0x58, /* 0xBC-0xBF */
+ 0xAF, 0x59, 0xAF, 0x5A, 0xF0, 0xE8, 0xF0, 0xE7, /* 0xC0-0xC3 */
+ 0xF0, 0xE4, 0xB2, 0xA1, 0xAF, 0x5B, 0xD6, 0xA2, /* 0xC4-0xC7 */
+ 0xD3, 0xB8, 0xBE, 0xB7, 0xC8, 0xAC, 0xAF, 0x5C, /* 0xC8-0xCB */
+ 0xAF, 0x5D, 0xF0, 0xEA, 0xAF, 0x5E, 0xAF, 0x5F, /* 0xCC-0xCF */
+ 0xAF, 0x60, 0xAF, 0x61, 0xD1, 0xF7, 0xAF, 0x62, /* 0xD0-0xD3 */
+ 0xD6, 0xCC, 0xBA, 0xDB, 0xF0, 0xE9, 0xAF, 0x63, /* 0xD4-0xD7 */
+ 0xB6, 0xBB, 0xAF, 0x64, 0xAF, 0x65, 0xCD, 0xB4, /* 0xD8-0xDB */
+ 0xAF, 0x66, 0xAF, 0x67, 0xC6, 0xA6, 0xAF, 0x68, /* 0xDC-0xDF */
+ 0xAF, 0x69, 0xAF, 0x6A, 0xC1, 0xA1, 0xF0, 0xEB, /* 0xE0-0xE3 */
+ 0xF0, 0xEE, 0xAF, 0x6B, 0xF0, 0xED, 0xF0, 0xF0, /* 0xE4-0xE7 */
+ 0xF0, 0xEC, 0xAF, 0x6C, 0xBB, 0xBE, 0xF0, 0xEF, /* 0xE8-0xEB */
+ 0xAF, 0x6D, 0xAF, 0x6E, 0xAF, 0x6F, 0xAF, 0x70, /* 0xEC-0xEF */
+ 0xCC, 0xB5, 0xF0, 0xF2, 0xAF, 0x71, 0xAF, 0x72, /* 0xF0-0xF3 */
+ 0xB3, 0xD5, 0xAF, 0x73, 0xAF, 0x74, 0xAF, 0x75, /* 0xF4-0xF7 */
+ 0xAF, 0x76, 0xB1, 0xD4, 0xAF, 0x77, 0xAF, 0x78, /* 0xF8-0xFB */
+ 0xF0, 0xF3, 0xAF, 0x79, 0xAF, 0x7A, 0xF0, 0xF4, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_76[512] = {
+ 0xF0, 0xF6, 0xB4, 0xE1, 0xAF, 0x7B, 0xF0, 0xF1, /* 0x00-0x03 */
+ 0xAF, 0x7C, 0xF0, 0xF7, 0xAF, 0x7D, 0xAF, 0x7E, /* 0x04-0x07 */
+ 0xAF, 0x80, 0xAF, 0x81, 0xF0, 0xFA, 0xAF, 0x82, /* 0x08-0x0B */
+ 0xF0, 0xF8, 0xAF, 0x83, 0xAF, 0x84, 0xAF, 0x85, /* 0x0C-0x0F */
+ 0xF0, 0xF5, 0xAF, 0x86, 0xAF, 0x87, 0xAF, 0x88, /* 0x10-0x13 */
+ 0xAF, 0x89, 0xF0, 0xFD, 0xAF, 0x8A, 0xF0, 0xF9, /* 0x14-0x17 */
+ 0xF0, 0xFC, 0xF0, 0xFE, 0xAF, 0x8B, 0xF1, 0xA1, /* 0x18-0x1B */
+ 0xAF, 0x8C, 0xAF, 0x8D, 0xAF, 0x8E, 0xCE, 0xC1, /* 0x1C-0x1F */
+ 0xF1, 0xA4, 0xAF, 0x8F, 0xF1, 0xA3, 0xAF, 0x90, /* 0x20-0x23 */
+ 0xC1, 0xF6, 0xF0, 0xFB, 0xCA, 0xDD, 0xAF, 0x91, /* 0x24-0x27 */
+ 0xAF, 0x92, 0xB4, 0xF1, 0xB1, 0xF1, 0xCC, 0xB1, /* 0x28-0x2B */
+ 0xAF, 0x93, 0xF1, 0xA6, 0xAF, 0x94, 0xAF, 0x95, /* 0x2C-0x2F */
+ 0xF1, 0xA7, 0xAF, 0x96, 0xAF, 0x97, 0xF1, 0xAC, /* 0x30-0x33 */
+ 0xD5, 0xCE, 0xF1, 0xA9, 0xAF, 0x98, 0xAF, 0x99, /* 0x34-0x37 */
+ 0xC8, 0xB3, 0xAF, 0x9A, 0xAF, 0x9B, 0xAF, 0x9C, /* 0x38-0x3B */
+ 0xF1, 0xA2, 0xAF, 0x9D, 0xF1, 0xAB, 0xF1, 0xA8, /* 0x3C-0x3F */
+ 0xF1, 0xA5, 0xAF, 0x9E, 0xAF, 0x9F, 0xF1, 0xAA, /* 0x40-0x43 */
+ 0xAF, 0xA0, 0xB0, 0x40, 0xB0, 0x41, 0xB0, 0x42, /* 0x44-0x47 */
+ 0xB0, 0x43, 0xB0, 0x44, 0xB0, 0x45, 0xB0, 0x46, /* 0x48-0x4B */
+ 0xB0, 0xA9, 0xF1, 0xAD, 0xB0, 0x47, 0xB0, 0x48, /* 0x4C-0x4F */
+ 0xB0, 0x49, 0xB0, 0x4A, 0xB0, 0x4B, 0xB0, 0x4C, /* 0x50-0x53 */
+ 0xF1, 0xAF, 0xB0, 0x4D, 0xF1, 0xB1, 0xB0, 0x4E, /* 0x54-0x57 */
+ 0xB0, 0x4F, 0xB0, 0x50, 0xB0, 0x51, 0xB0, 0x52, /* 0x58-0x5B */
+ 0xF1, 0xB0, 0xB0, 0x53, 0xF1, 0xAE, 0xB0, 0x54, /* 0x5C-0x5F */
+ 0xB0, 0x55, 0xB0, 0x56, 0xB0, 0x57, 0xD1, 0xA2, /* 0x60-0x63 */
+ 0xB0, 0x58, 0xB0, 0x59, 0xB0, 0x5A, 0xB0, 0x5B, /* 0x64-0x67 */
+ 0xB0, 0x5C, 0xB0, 0x5D, 0xB0, 0x5E, 0xF1, 0xB2, /* 0x68-0x6B */
+ 0xB0, 0x5F, 0xB0, 0x60, 0xB0, 0x61, 0xF1, 0xB3, /* 0x6C-0x6F */
+ 0xB0, 0x62, 0xB0, 0x63, 0xB0, 0x64, 0xB0, 0x65, /* 0x70-0x73 */
+ 0xB0, 0x66, 0xB0, 0x67, 0xB0, 0x68, 0xB0, 0x69, /* 0x74-0x77 */
+ 0xB9, 0xEF, 0xB0, 0x6A, 0xB0, 0x6B, 0xB5, 0xC7, /* 0x78-0x7B */
+ 0xB0, 0x6C, 0xB0, 0xD7, 0xB0, 0xD9, 0xB0, 0x6D, /* 0x7C-0x7F */
+
+ 0xB0, 0x6E, 0xB0, 0x6F, 0xD4, 0xED, 0xB0, 0x70, /* 0x80-0x83 */
+ 0xB5, 0xC4, 0xB0, 0x71, 0xBD, 0xD4, 0xBB, 0xCA, /* 0x84-0x87 */
+ 0xF0, 0xA7, 0xB0, 0x72, 0xB0, 0x73, 0xB8, 0xDE, /* 0x88-0x8B */
+ 0xB0, 0x74, 0xB0, 0x75, 0xF0, 0xA8, 0xB0, 0x76, /* 0x8C-0x8F */
+ 0xB0, 0x77, 0xB0, 0xA8, 0xB0, 0x78, 0xF0, 0xA9, /* 0x90-0x93 */
+ 0xB0, 0x79, 0xB0, 0x7A, 0xCD, 0xEE, 0xB0, 0x7B, /* 0x94-0x97 */
+ 0xB0, 0x7C, 0xF0, 0xAA, 0xB0, 0x7D, 0xB0, 0x7E, /* 0x98-0x9B */
+ 0xB0, 0x80, 0xB0, 0x81, 0xB0, 0x82, 0xB0, 0x83, /* 0x9C-0x9F */
+ 0xB0, 0x84, 0xB0, 0x85, 0xB0, 0x86, 0xB0, 0x87, /* 0xA0-0xA3 */
+ 0xF0, 0xAB, 0xB0, 0x88, 0xB0, 0x89, 0xB0, 0x8A, /* 0xA4-0xA7 */
+ 0xB0, 0x8B, 0xB0, 0x8C, 0xB0, 0x8D, 0xB0, 0x8E, /* 0xA8-0xAB */
+ 0xB0, 0x8F, 0xB0, 0x90, 0xC6, 0xA4, 0xB0, 0x91, /* 0xAC-0xAF */
+ 0xB0, 0x92, 0xD6, 0xE5, 0xF1, 0xE4, 0xB0, 0x93, /* 0xB0-0xB3 */
+ 0xF1, 0xE5, 0xB0, 0x94, 0xB0, 0x95, 0xB0, 0x96, /* 0xB4-0xB7 */
+ 0xB0, 0x97, 0xB0, 0x98, 0xB0, 0x99, 0xB0, 0x9A, /* 0xB8-0xBB */
+ 0xB0, 0x9B, 0xB0, 0x9C, 0xB0, 0x9D, 0xC3, 0xF3, /* 0xBC-0xBF */
+ 0xB0, 0x9E, 0xB0, 0x9F, 0xD3, 0xDB, 0xB0, 0xA0, /* 0xC0-0xC3 */
+ 0xB1, 0x40, 0xD6, 0xD1, 0xC5, 0xE8, 0xB1, 0x41, /* 0xC4-0xC7 */
+ 0xD3, 0xAF, 0xB1, 0x42, 0xD2, 0xE6, 0xB1, 0x43, /* 0xC8-0xCB */
+ 0xB1, 0x44, 0xEE, 0xC1, 0xB0, 0xBB, 0xD5, 0xB5, /* 0xCC-0xCF */
+ 0xD1, 0xCE, 0xBC, 0xE0, 0xBA, 0xD0, 0xB1, 0x45, /* 0xD0-0xD3 */
+ 0xBF, 0xF8, 0xB1, 0x46, 0xB8, 0xC7, 0xB5, 0xC1, /* 0xD4-0xD7 */
+ 0xC5, 0xCC, 0xB1, 0x47, 0xB1, 0x48, 0xCA, 0xA2, /* 0xD8-0xDB */
+ 0xB1, 0x49, 0xB1, 0x4A, 0xB1, 0x4B, 0xC3, 0xCB, /* 0xDC-0xDF */
+ 0xB1, 0x4C, 0xB1, 0x4D, 0xB1, 0x4E, 0xB1, 0x4F, /* 0xE0-0xE3 */
+ 0xB1, 0x50, 0xEE, 0xC2, 0xB1, 0x51, 0xB1, 0x52, /* 0xE4-0xE7 */
+ 0xB1, 0x53, 0xB1, 0x54, 0xB1, 0x55, 0xB1, 0x56, /* 0xE8-0xEB */
+ 0xB1, 0x57, 0xB1, 0x58, 0xC4, 0xBF, 0xB6, 0xA2, /* 0xEC-0xEF */
+ 0xB1, 0x59, 0xED, 0xEC, 0xC3, 0xA4, 0xB1, 0x5A, /* 0xF0-0xF3 */
+ 0xD6, 0xB1, 0xB1, 0x5B, 0xB1, 0x5C, 0xB1, 0x5D, /* 0xF4-0xF7 */
+ 0xCF, 0xE0, 0xED, 0xEF, 0xB1, 0x5E, 0xB1, 0x5F, /* 0xF8-0xFB */
+ 0xC5, 0xCE, 0xB1, 0x60, 0xB6, 0xDC, 0xB1, 0x61, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_77[512] = {
+ 0xB1, 0x62, 0xCA, 0xA1, 0xB1, 0x63, 0xB1, 0x64, /* 0x00-0x03 */
+ 0xED, 0xED, 0xB1, 0x65, 0xB1, 0x66, 0xED, 0xF0, /* 0x04-0x07 */
+ 0xED, 0xF1, 0xC3, 0xBC, 0xB1, 0x67, 0xBF, 0xB4, /* 0x08-0x0B */
+ 0xB1, 0x68, 0xED, 0xEE, 0xB1, 0x69, 0xB1, 0x6A, /* 0x0C-0x0F */
+ 0xB1, 0x6B, 0xB1, 0x6C, 0xB1, 0x6D, 0xB1, 0x6E, /* 0x10-0x13 */
+ 0xB1, 0x6F, 0xB1, 0x70, 0xB1, 0x71, 0xB1, 0x72, /* 0x14-0x17 */
+ 0xB1, 0x73, 0xED, 0xF4, 0xED, 0xF2, 0xB1, 0x74, /* 0x18-0x1B */
+ 0xB1, 0x75, 0xB1, 0x76, 0xB1, 0x77, 0xD5, 0xE6, /* 0x1C-0x1F */
+ 0xC3, 0xDF, 0xB1, 0x78, 0xED, 0xF3, 0xB1, 0x79, /* 0x20-0x23 */
+ 0xB1, 0x7A, 0xB1, 0x7B, 0xED, 0xF6, 0xB1, 0x7C, /* 0x24-0x27 */
+ 0xD5, 0xA3, 0xD1, 0xA3, 0xB1, 0x7D, 0xB1, 0x7E, /* 0x28-0x2B */
+ 0xB1, 0x80, 0xED, 0xF5, 0xB1, 0x81, 0xC3, 0xD0, /* 0x2C-0x2F */
+ 0xB1, 0x82, 0xB1, 0x83, 0xB1, 0x84, 0xB1, 0x85, /* 0x30-0x33 */
+ 0xB1, 0x86, 0xED, 0xF7, 0xBF, 0xF4, 0xBE, 0xEC, /* 0x34-0x37 */
+ 0xED, 0xF8, 0xB1, 0x87, 0xCC, 0xF7, 0xB1, 0x88, /* 0x38-0x3B */
+ 0xD1, 0xDB, 0xB1, 0x89, 0xB1, 0x8A, 0xB1, 0x8B, /* 0x3C-0x3F */
+ 0xD7, 0xC5, 0xD5, 0xF6, 0xB1, 0x8C, 0xED, 0xFC, /* 0x40-0x43 */
+ 0xB1, 0x8D, 0xB1, 0x8E, 0xB1, 0x8F, 0xED, 0xFB, /* 0x44-0x47 */
+ 0xB1, 0x90, 0xB1, 0x91, 0xB1, 0x92, 0xB1, 0x93, /* 0x48-0x4B */
+ 0xB1, 0x94, 0xB1, 0x95, 0xB1, 0x96, 0xB1, 0x97, /* 0x4C-0x4F */
+ 0xED, 0xF9, 0xED, 0xFA, 0xB1, 0x98, 0xB1, 0x99, /* 0x50-0x53 */
+ 0xB1, 0x9A, 0xB1, 0x9B, 0xB1, 0x9C, 0xB1, 0x9D, /* 0x54-0x57 */
+ 0xB1, 0x9E, 0xB1, 0x9F, 0xED, 0xFD, 0xBE, 0xA6, /* 0x58-0x5B */
+ 0xB1, 0xA0, 0xB2, 0x40, 0xB2, 0x41, 0xB2, 0x42, /* 0x5C-0x5F */
+ 0xB2, 0x43, 0xCB, 0xAF, 0xEE, 0xA1, 0xB6, 0xBD, /* 0x60-0x63 */
+ 0xB2, 0x44, 0xEE, 0xA2, 0xC4, 0xC0, 0xB2, 0x45, /* 0x64-0x67 */
+ 0xED, 0xFE, 0xB2, 0x46, 0xB2, 0x47, 0xBD, 0xDE, /* 0x68-0x6B */
+ 0xB2, 0xC7, 0xB2, 0x48, 0xB2, 0x49, 0xB2, 0x4A, /* 0x6C-0x6F */
+ 0xB2, 0x4B, 0xB2, 0x4C, 0xB2, 0x4D, 0xB2, 0x4E, /* 0x70-0x73 */
+ 0xB2, 0x4F, 0xB2, 0x50, 0xB2, 0x51, 0xB2, 0x52, /* 0x74-0x77 */
+ 0xB2, 0x53, 0xB6, 0xC3, 0xB2, 0x54, 0xB2, 0x55, /* 0x78-0x7B */
+ 0xB2, 0x56, 0xEE, 0xA5, 0xD8, 0xBA, 0xEE, 0xA3, /* 0x7C-0x7F */
+
+ 0xEE, 0xA6, 0xB2, 0x57, 0xB2, 0x58, 0xB2, 0x59, /* 0x80-0x83 */
+ 0xC3, 0xE9, 0xB3, 0xF2, 0xB2, 0x5A, 0xB2, 0x5B, /* 0x84-0x87 */
+ 0xB2, 0x5C, 0xB2, 0x5D, 0xB2, 0x5E, 0xB2, 0x5F, /* 0x88-0x8B */
+ 0xEE, 0xA7, 0xEE, 0xA4, 0xCF, 0xB9, 0xB2, 0x60, /* 0x8C-0x8F */
+ 0xB2, 0x61, 0xEE, 0xA8, 0xC2, 0xF7, 0xB2, 0x62, /* 0x90-0x93 */
+ 0xB2, 0x63, 0xB2, 0x64, 0xB2, 0x65, 0xB2, 0x66, /* 0x94-0x97 */
+ 0xB2, 0x67, 0xB2, 0x68, 0xB2, 0x69, 0xB2, 0x6A, /* 0x98-0x9B */
+ 0xB2, 0x6B, 0xB2, 0x6C, 0xB2, 0x6D, 0xEE, 0xA9, /* 0x9C-0x9F */
+ 0xEE, 0xAA, 0xB2, 0x6E, 0xDE, 0xAB, 0xB2, 0x6F, /* 0xA0-0xA3 */
+ 0xB2, 0x70, 0xC6, 0xB3, 0xB2, 0x71, 0xC7, 0xC6, /* 0xA4-0xA7 */
+ 0xB2, 0x72, 0xD6, 0xF5, 0xB5, 0xC9, 0xB2, 0x73, /* 0xA8-0xAB */
+ 0xCB, 0xB2, 0xB2, 0x74, 0xB2, 0x75, 0xB2, 0x76, /* 0xAC-0xAF */
+ 0xEE, 0xAB, 0xB2, 0x77, 0xB2, 0x78, 0xCD, 0xAB, /* 0xB0-0xB3 */
+ 0xB2, 0x79, 0xEE, 0xAC, 0xB2, 0x7A, 0xB2, 0x7B, /* 0xB4-0xB7 */
+ 0xB2, 0x7C, 0xB2, 0x7D, 0xB2, 0x7E, 0xD5, 0xB0, /* 0xB8-0xBB */
+ 0xB2, 0x80, 0xEE, 0xAD, 0xB2, 0x81, 0xF6, 0xC4, /* 0xBC-0xBF */
+ 0xB2, 0x82, 0xB2, 0x83, 0xB2, 0x84, 0xB2, 0x85, /* 0xC0-0xC3 */
+ 0xB2, 0x86, 0xB2, 0x87, 0xB2, 0x88, 0xB2, 0x89, /* 0xC4-0xC7 */
+ 0xB2, 0x8A, 0xB2, 0x8B, 0xB2, 0x8C, 0xB2, 0x8D, /* 0xC8-0xCB */
+ 0xB2, 0x8E, 0xDB, 0xC7, 0xB2, 0x8F, 0xB2, 0x90, /* 0xCC-0xCF */
+ 0xB2, 0x91, 0xB2, 0x92, 0xB2, 0x93, 0xB2, 0x94, /* 0xD0-0xD3 */
+ 0xB2, 0x95, 0xB2, 0x96, 0xB2, 0x97, 0xB4, 0xA3, /* 0xD4-0xD7 */
+ 0xB2, 0x98, 0xB2, 0x99, 0xB2, 0x9A, 0xC3, 0xAC, /* 0xD8-0xDB */
+ 0xF1, 0xE6, 0xB2, 0x9B, 0xB2, 0x9C, 0xB2, 0x9D, /* 0xDC-0xDF */
+ 0xB2, 0x9E, 0xB2, 0x9F, 0xCA, 0xB8, 0xD2, 0xD3, /* 0xE0-0xE3 */
+ 0xB2, 0xA0, 0xD6, 0xAA, 0xB3, 0x40, 0xEF, 0xF2, /* 0xE4-0xE7 */
+ 0xB3, 0x41, 0xBE, 0xD8, 0xB3, 0x42, 0xBD, 0xC3, /* 0xE8-0xEB */
+ 0xEF, 0xF3, 0xB6, 0xCC, 0xB0, 0xAB, 0xB3, 0x43, /* 0xEC-0xEF */
+ 0xB3, 0x44, 0xB3, 0x45, 0xB3, 0x46, 0xCA, 0xAF, /* 0xF0-0xF3 */
+ 0xB3, 0x47, 0xB3, 0x48, 0xED, 0xB6, 0xB3, 0x49, /* 0xF4-0xF7 */
+ 0xED, 0xB7, 0xB3, 0x4A, 0xB3, 0x4B, 0xB3, 0x4C, /* 0xF8-0xFB */
+ 0xB3, 0x4D, 0xCE, 0xF9, 0xB7, 0xAF, 0xBF, 0xF3, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_78[512] = {
+ 0xED, 0xB8, 0xC2, 0xEB, 0xC9, 0xB0, 0xB3, 0x4E, /* 0x00-0x03 */
+ 0xB3, 0x4F, 0xB3, 0x50, 0xB3, 0x51, 0xB3, 0x52, /* 0x04-0x07 */
+ 0xB3, 0x53, 0xED, 0xB9, 0xB3, 0x54, 0xB3, 0x55, /* 0x08-0x0B */
+ 0xC6, 0xF6, 0xBF, 0xB3, 0xB3, 0x56, 0xB3, 0x57, /* 0x0C-0x0F */
+ 0xB3, 0x58, 0xED, 0xBC, 0xC5, 0xF8, 0xB3, 0x59, /* 0x10-0x13 */
+ 0xD1, 0xD0, 0xB3, 0x5A, 0xD7, 0xA9, 0xED, 0xBA, /* 0x14-0x17 */
+ 0xED, 0xBB, 0xB3, 0x5B, 0xD1, 0xE2, 0xB3, 0x5C, /* 0x18-0x1B */
+ 0xED, 0xBF, 0xED, 0xC0, 0xB3, 0x5D, 0xED, 0xC4, /* 0x1C-0x1F */
+ 0xB3, 0x5E, 0xB3, 0x5F, 0xB3, 0x60, 0xED, 0xC8, /* 0x20-0x23 */
+ 0xB3, 0x61, 0xED, 0xC6, 0xED, 0xCE, 0xD5, 0xE8, /* 0x24-0x27 */
+ 0xB3, 0x62, 0xED, 0xC9, 0xB3, 0x63, 0xB3, 0x64, /* 0x28-0x2B */
+ 0xED, 0xC7, 0xED, 0xBE, 0xB3, 0x65, 0xB3, 0x66, /* 0x2C-0x2F */
+ 0xC5, 0xE9, 0xB3, 0x67, 0xB3, 0x68, 0xB3, 0x69, /* 0x30-0x33 */
+ 0xC6, 0xC6, 0xB3, 0x6A, 0xB3, 0x6B, 0xC9, 0xE9, /* 0x34-0x37 */
+ 0xD4, 0xD2, 0xED, 0xC1, 0xED, 0xC2, 0xED, 0xC3, /* 0x38-0x3B */
+ 0xED, 0xC5, 0xB3, 0x6C, 0xC0, 0xF9, 0xB3, 0x6D, /* 0x3C-0x3F */
+ 0xB4, 0xA1, 0xB3, 0x6E, 0xB3, 0x6F, 0xB3, 0x70, /* 0x40-0x43 */
+ 0xB3, 0x71, 0xB9, 0xE8, 0xB3, 0x72, 0xED, 0xD0, /* 0x44-0x47 */
+ 0xB3, 0x73, 0xB3, 0x74, 0xB3, 0x75, 0xB3, 0x76, /* 0x48-0x4B */
+ 0xED, 0xD1, 0xB3, 0x77, 0xED, 0xCA, 0xB3, 0x78, /* 0x4C-0x4F */
+ 0xED, 0xCF, 0xB3, 0x79, 0xCE, 0xF8, 0xB3, 0x7A, /* 0x50-0x53 */
+ 0xB3, 0x7B, 0xCB, 0xB6, 0xED, 0xCC, 0xED, 0xCD, /* 0x54-0x57 */
+ 0xB3, 0x7C, 0xB3, 0x7D, 0xB3, 0x7E, 0xB3, 0x80, /* 0x58-0x5B */
+ 0xB3, 0x81, 0xCF, 0xF5, 0xB3, 0x82, 0xB3, 0x83, /* 0x5C-0x5F */
+ 0xB3, 0x84, 0xB3, 0x85, 0xB3, 0x86, 0xB3, 0x87, /* 0x60-0x63 */
+ 0xB3, 0x88, 0xB3, 0x89, 0xB3, 0x8A, 0xB3, 0x8B, /* 0x64-0x67 */
+ 0xB3, 0x8C, 0xB3, 0x8D, 0xED, 0xD2, 0xC1, 0xF2, /* 0x68-0x6B */
+ 0xD3, 0xB2, 0xED, 0xCB, 0xC8, 0xB7, 0xB3, 0x8E, /* 0x6C-0x6F */
+ 0xB3, 0x8F, 0xB3, 0x90, 0xB3, 0x91, 0xB3, 0x92, /* 0x70-0x73 */
+ 0xB3, 0x93, 0xB3, 0x94, 0xB3, 0x95, 0xBC, 0xEF, /* 0x74-0x77 */
+ 0xB3, 0x96, 0xB3, 0x97, 0xB3, 0x98, 0xB3, 0x99, /* 0x78-0x7B */
+ 0xC5, 0xF0, 0xB3, 0x9A, 0xB3, 0x9B, 0xB3, 0x9C, /* 0x7C-0x7F */
+
+ 0xB3, 0x9D, 0xB3, 0x9E, 0xB3, 0x9F, 0xB3, 0xA0, /* 0x80-0x83 */
+ 0xB4, 0x40, 0xB4, 0x41, 0xB4, 0x42, 0xED, 0xD6, /* 0x84-0x87 */
+ 0xB4, 0x43, 0xB5, 0xEF, 0xB4, 0x44, 0xB4, 0x45, /* 0x88-0x8B */
+ 0xC2, 0xB5, 0xB0, 0xAD, 0xCB, 0xE9, 0xB4, 0x46, /* 0x8C-0x8F */
+ 0xB4, 0x47, 0xB1, 0xAE, 0xB4, 0x48, 0xED, 0xD4, /* 0x90-0x93 */
+ 0xB4, 0x49, 0xB4, 0x4A, 0xB4, 0x4B, 0xCD, 0xEB, /* 0x94-0x97 */
+ 0xB5, 0xE2, 0xB4, 0x4C, 0xED, 0xD5, 0xED, 0xD3, /* 0x98-0x9B */
+ 0xED, 0xD7, 0xB4, 0x4D, 0xB4, 0x4E, 0xB5, 0xFA, /* 0x9C-0x9F */
+ 0xB4, 0x4F, 0xED, 0xD8, 0xB4, 0x50, 0xED, 0xD9, /* 0xA0-0xA3 */
+ 0xB4, 0x51, 0xED, 0xDC, 0xB4, 0x52, 0xB1, 0xCC, /* 0xA4-0xA7 */
+ 0xB4, 0x53, 0xB4, 0x54, 0xB4, 0x55, 0xB4, 0x56, /* 0xA8-0xAB */
+ 0xB4, 0x57, 0xB4, 0x58, 0xB4, 0x59, 0xB4, 0x5A, /* 0xAC-0xAF */
+ 0xC5, 0xF6, 0xBC, 0xEE, 0xED, 0xDA, 0xCC, 0xBC, /* 0xB0-0xB3 */
+ 0xB2, 0xEA, 0xB4, 0x5B, 0xB4, 0x5C, 0xB4, 0x5D, /* 0xB4-0xB7 */
+ 0xB4, 0x5E, 0xED, 0xDB, 0xB4, 0x5F, 0xB4, 0x60, /* 0xB8-0xBB */
+ 0xB4, 0x61, 0xB4, 0x62, 0xC4, 0xEB, 0xB4, 0x63, /* 0xBC-0xBF */
+ 0xB4, 0x64, 0xB4, 0xC5, 0xB4, 0x65, 0xB4, 0x66, /* 0xC0-0xC3 */
+ 0xB4, 0x67, 0xB0, 0xF5, 0xB4, 0x68, 0xB4, 0x69, /* 0xC4-0xC7 */
+ 0xB4, 0x6A, 0xED, 0xDF, 0xC0, 0xDA, 0xB4, 0xE8, /* 0xC8-0xCB */
+ 0xB4, 0x6B, 0xB4, 0x6C, 0xB4, 0x6D, 0xB4, 0x6E, /* 0xCC-0xCF */
+ 0xC5, 0xCD, 0xB4, 0x6F, 0xB4, 0x70, 0xB4, 0x71, /* 0xD0-0xD3 */
+ 0xED, 0xDD, 0xBF, 0xC4, 0xB4, 0x72, 0xB4, 0x73, /* 0xD4-0xD7 */
+ 0xB4, 0x74, 0xED, 0xDE, 0xB4, 0x75, 0xB4, 0x76, /* 0xD8-0xDB */
+ 0xB4, 0x77, 0xB4, 0x78, 0xB4, 0x79, 0xB4, 0x7A, /* 0xDC-0xDF */
+ 0xB4, 0x7B, 0xB4, 0x7C, 0xB4, 0x7D, 0xB4, 0x7E, /* 0xE0-0xE3 */
+ 0xB4, 0x80, 0xB4, 0x81, 0xB4, 0x82, 0xB4, 0x83, /* 0xE4-0xE7 */
+ 0xC4, 0xA5, 0xB4, 0x84, 0xB4, 0x85, 0xB4, 0x86, /* 0xE8-0xEB */
+ 0xED, 0xE0, 0xB4, 0x87, 0xB4, 0x88, 0xB4, 0x89, /* 0xEC-0xEF */
+ 0xB4, 0x8A, 0xB4, 0x8B, 0xED, 0xE1, 0xB4, 0x8C, /* 0xF0-0xF3 */
+ 0xED, 0xE3, 0xB4, 0x8D, 0xB4, 0x8E, 0xC1, 0xD7, /* 0xF4-0xF7 */
+ 0xB4, 0x8F, 0xB4, 0x90, 0xBB, 0xC7, 0xB4, 0x91, /* 0xF8-0xFB */
+ 0xB4, 0x92, 0xB4, 0x93, 0xB4, 0x94, 0xB4, 0x95, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_79[512] = {
+ 0xB4, 0x96, 0xBD, 0xB8, 0xB4, 0x97, 0xB4, 0x98, /* 0x00-0x03 */
+ 0xB4, 0x99, 0xED, 0xE2, 0xB4, 0x9A, 0xB4, 0x9B, /* 0x04-0x07 */
+ 0xB4, 0x9C, 0xB4, 0x9D, 0xB4, 0x9E, 0xB4, 0x9F, /* 0x08-0x0B */
+ 0xB4, 0xA0, 0xB5, 0x40, 0xB5, 0x41, 0xB5, 0x42, /* 0x0C-0x0F */
+ 0xB5, 0x43, 0xB5, 0x44, 0xB5, 0x45, 0xED, 0xE4, /* 0x10-0x13 */
+ 0xB5, 0x46, 0xB5, 0x47, 0xB5, 0x48, 0xB5, 0x49, /* 0x14-0x17 */
+ 0xB5, 0x4A, 0xB5, 0x4B, 0xB5, 0x4C, 0xB5, 0x4D, /* 0x18-0x1B */
+ 0xB5, 0x4E, 0xB5, 0x4F, 0xED, 0xE6, 0xB5, 0x50, /* 0x1C-0x1F */
+ 0xB5, 0x51, 0xB5, 0x52, 0xB5, 0x53, 0xB5, 0x54, /* 0x20-0x23 */
+ 0xED, 0xE5, 0xB5, 0x55, 0xB5, 0x56, 0xB5, 0x57, /* 0x24-0x27 */
+ 0xB5, 0x58, 0xB5, 0x59, 0xB5, 0x5A, 0xB5, 0x5B, /* 0x28-0x2B */
+ 0xB5, 0x5C, 0xB5, 0x5D, 0xB5, 0x5E, 0xB5, 0x5F, /* 0x2C-0x2F */
+ 0xB5, 0x60, 0xB5, 0x61, 0xB5, 0x62, 0xB5, 0x63, /* 0x30-0x33 */
+ 0xED, 0xE7, 0xB5, 0x64, 0xB5, 0x65, 0xB5, 0x66, /* 0x34-0x37 */
+ 0xB5, 0x67, 0xB5, 0x68, 0xCA, 0xBE, 0xEC, 0xEA, /* 0x38-0x3B */
+ 0xC0, 0xF1, 0xB5, 0x69, 0xC9, 0xE7, 0xB5, 0x6A, /* 0x3C-0x3F */
+ 0xEC, 0xEB, 0xC6, 0xEE, 0xB5, 0x6B, 0xB5, 0x6C, /* 0x40-0x43 */
+ 0xB5, 0x6D, 0xB5, 0x6E, 0xEC, 0xEC, 0xB5, 0x6F, /* 0x44-0x47 */
+ 0xC6, 0xED, 0xEC, 0xED, 0xB5, 0x70, 0xB5, 0x71, /* 0x48-0x4B */
+ 0xB5, 0x72, 0xB5, 0x73, 0xB5, 0x74, 0xB5, 0x75, /* 0x4C-0x4F */
+ 0xB5, 0x76, 0xB5, 0x77, 0xB5, 0x78, 0xEC, 0xF0, /* 0x50-0x53 */
+ 0xB5, 0x79, 0xB5, 0x7A, 0xD7, 0xE6, 0xEC, 0xF3, /* 0x54-0x57 */
+ 0xB5, 0x7B, 0xB5, 0x7C, 0xEC, 0xF1, 0xEC, 0xEE, /* 0x58-0x5B */
+ 0xEC, 0xEF, 0xD7, 0xA3, 0xC9, 0xF1, 0xCB, 0xEE, /* 0x5C-0x5F */
+ 0xEC, 0xF4, 0xB5, 0x7D, 0xEC, 0xF2, 0xB5, 0x7E, /* 0x60-0x63 */
+ 0xB5, 0x80, 0xCF, 0xE9, 0xB5, 0x81, 0xEC, 0xF6, /* 0x64-0x67 */
+ 0xC6, 0xB1, 0xB5, 0x82, 0xB5, 0x83, 0xB5, 0x84, /* 0x68-0x6B */
+ 0xB5, 0x85, 0xBC, 0xC0, 0xB5, 0x86, 0xEC, 0xF5, /* 0x6C-0x6F */
+ 0xB5, 0x87, 0xB5, 0x88, 0xB5, 0x89, 0xB5, 0x8A, /* 0x70-0x73 */
+ 0xB5, 0x8B, 0xB5, 0x8C, 0xB5, 0x8D, 0xB5, 0xBB, /* 0x74-0x77 */
+ 0xBB, 0xF6, 0xB5, 0x8E, 0xEC, 0xF7, 0xB5, 0x8F, /* 0x78-0x7B */
+ 0xB5, 0x90, 0xB5, 0x91, 0xB5, 0x92, 0xB5, 0x93, /* 0x7C-0x7F */
+
+ 0xD9, 0xF7, 0xBD, 0xFB, 0xB5, 0x94, 0xB5, 0x95, /* 0x80-0x83 */
+ 0xC2, 0xBB, 0xEC, 0xF8, 0xB5, 0x96, 0xB5, 0x97, /* 0x84-0x87 */
+ 0xB5, 0x98, 0xB5, 0x99, 0xEC, 0xF9, 0xB5, 0x9A, /* 0x88-0x8B */
+ 0xB5, 0x9B, 0xB5, 0x9C, 0xB5, 0x9D, 0xB8, 0xA3, /* 0x8C-0x8F */
+ 0xB5, 0x9E, 0xB5, 0x9F, 0xB5, 0xA0, 0xB6, 0x40, /* 0x90-0x93 */
+ 0xB6, 0x41, 0xB6, 0x42, 0xB6, 0x43, 0xB6, 0x44, /* 0x94-0x97 */
+ 0xB6, 0x45, 0xB6, 0x46, 0xEC, 0xFA, 0xB6, 0x47, /* 0x98-0x9B */
+ 0xB6, 0x48, 0xB6, 0x49, 0xB6, 0x4A, 0xB6, 0x4B, /* 0x9C-0x9F */
+ 0xB6, 0x4C, 0xB6, 0x4D, 0xB6, 0x4E, 0xB6, 0x4F, /* 0xA0-0xA3 */
+ 0xB6, 0x50, 0xB6, 0x51, 0xB6, 0x52, 0xEC, 0xFB, /* 0xA4-0xA7 */
+ 0xB6, 0x53, 0xB6, 0x54, 0xB6, 0x55, 0xB6, 0x56, /* 0xA8-0xAB */
+ 0xB6, 0x57, 0xB6, 0x58, 0xB6, 0x59, 0xB6, 0x5A, /* 0xAC-0xAF */
+ 0xB6, 0x5B, 0xB6, 0x5C, 0xB6, 0x5D, 0xEC, 0xFC, /* 0xB0-0xB3 */
+ 0xB6, 0x5E, 0xB6, 0x5F, 0xB6, 0x60, 0xB6, 0x61, /* 0xB4-0xB7 */
+ 0xB6, 0x62, 0xD3, 0xED, 0xD8, 0xAE, 0xC0, 0xEB, /* 0xB8-0xBB */
+ 0xB6, 0x63, 0xC7, 0xDD, 0xBA, 0xCC, 0xB6, 0x64, /* 0xBC-0xBF */
+ 0xD0, 0xE3, 0xCB, 0xBD, 0xB6, 0x65, 0xCD, 0xBA, /* 0xC0-0xC3 */
+ 0xB6, 0x66, 0xB6, 0x67, 0xB8, 0xD1, 0xB6, 0x68, /* 0xC4-0xC7 */
+ 0xB6, 0x69, 0xB1, 0xFC, 0xB6, 0x6A, 0xC7, 0xEF, /* 0xC8-0xCB */
+ 0xB6, 0x6B, 0xD6, 0xD6, 0xB6, 0x6C, 0xB6, 0x6D, /* 0xCC-0xCF */
+ 0xB6, 0x6E, 0xBF, 0xC6, 0xC3, 0xEB, 0xB6, 0x6F, /* 0xD0-0xD3 */
+ 0xB6, 0x70, 0xEF, 0xF5, 0xB6, 0x71, 0xB6, 0x72, /* 0xD4-0xD7 */
+ 0xC3, 0xD8, 0xB6, 0x73, 0xB6, 0x74, 0xB6, 0x75, /* 0xD8-0xDB */
+ 0xB6, 0x76, 0xB6, 0x77, 0xB6, 0x78, 0xD7, 0xE2, /* 0xDC-0xDF */
+ 0xB6, 0x79, 0xB6, 0x7A, 0xB6, 0x7B, 0xEF, 0xF7, /* 0xE0-0xE3 */
+ 0xB3, 0xD3, 0xB6, 0x7C, 0xC7, 0xD8, 0xD1, 0xED, /* 0xE4-0xE7 */
+ 0xB6, 0x7D, 0xD6, 0xC8, 0xB6, 0x7E, 0xEF, 0xF8, /* 0xE8-0xEB */
+ 0xB6, 0x80, 0xEF, 0xF6, 0xB6, 0x81, 0xBB, 0xFD, /* 0xEC-0xEF */
+ 0xB3, 0xC6, 0xB6, 0x82, 0xB6, 0x83, 0xB6, 0x84, /* 0xF0-0xF3 */
+ 0xB6, 0x85, 0xB6, 0x86, 0xB6, 0x87, 0xB6, 0x88, /* 0xF4-0xF7 */
+ 0xBD, 0xD5, 0xB6, 0x89, 0xB6, 0x8A, 0xD2, 0xC6, /* 0xF8-0xFB */
+ 0xB6, 0x8B, 0xBB, 0xE0, 0xB6, 0x8C, 0xB6, 0x8D, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_7A[512] = {
+ 0xCF, 0xA1, 0xB6, 0x8E, 0xEF, 0xFC, 0xEF, 0xFB, /* 0x00-0x03 */
+ 0xB6, 0x8F, 0xB6, 0x90, 0xEF, 0xF9, 0xB6, 0x91, /* 0x04-0x07 */
+ 0xB6, 0x92, 0xB6, 0x93, 0xB6, 0x94, 0xB3, 0xCC, /* 0x08-0x0B */
+ 0xB6, 0x95, 0xC9, 0xD4, 0xCB, 0xB0, 0xB6, 0x96, /* 0x0C-0x0F */
+ 0xB6, 0x97, 0xB6, 0x98, 0xB6, 0x99, 0xB6, 0x9A, /* 0x10-0x13 */
+ 0xEF, 0xFE, 0xB6, 0x9B, 0xB6, 0x9C, 0xB0, 0xDE, /* 0x14-0x17 */
+ 0xB6, 0x9D, 0xB6, 0x9E, 0xD6, 0xC9, 0xB6, 0x9F, /* 0x18-0x1B */
+ 0xB6, 0xA0, 0xB7, 0x40, 0xEF, 0xFD, 0xB7, 0x41, /* 0x1C-0x1F */
+ 0xB3, 0xED, 0xB7, 0x42, 0xB7, 0x43, 0xF6, 0xD5, /* 0x20-0x23 */
+ 0xB7, 0x44, 0xB7, 0x45, 0xB7, 0x46, 0xB7, 0x47, /* 0x24-0x27 */
+ 0xB7, 0x48, 0xB7, 0x49, 0xB7, 0x4A, 0xB7, 0x4B, /* 0x28-0x2B */
+ 0xB7, 0x4C, 0xB7, 0x4D, 0xB7, 0x4E, 0xB7, 0x4F, /* 0x2C-0x2F */
+ 0xB7, 0x50, 0xB7, 0x51, 0xB7, 0x52, 0xCE, 0xC8, /* 0x30-0x33 */
+ 0xB7, 0x53, 0xB7, 0x54, 0xB7, 0x55, 0xF0, 0xA2, /* 0x34-0x37 */
+ 0xB7, 0x56, 0xF0, 0xA1, 0xB7, 0x57, 0xB5, 0xBE, /* 0x38-0x3B */
+ 0xBC, 0xDA, 0xBB, 0xFC, 0xB7, 0x58, 0xB8, 0xE5, /* 0x3C-0x3F */
+ 0xB7, 0x59, 0xB7, 0x5A, 0xB7, 0x5B, 0xB7, 0x5C, /* 0x40-0x43 */
+ 0xB7, 0x5D, 0xB7, 0x5E, 0xC4, 0xC2, 0xB7, 0x5F, /* 0x44-0x47 */
+ 0xB7, 0x60, 0xB7, 0x61, 0xB7, 0x62, 0xB7, 0x63, /* 0x48-0x4B */
+ 0xB7, 0x64, 0xB7, 0x65, 0xB7, 0x66, 0xB7, 0x67, /* 0x4C-0x4F */
+ 0xB7, 0x68, 0xF0, 0xA3, 0xB7, 0x69, 0xB7, 0x6A, /* 0x50-0x53 */
+ 0xB7, 0x6B, 0xB7, 0x6C, 0xB7, 0x6D, 0xCB, 0xEB, /* 0x54-0x57 */
+ 0xB7, 0x6E, 0xB7, 0x6F, 0xB7, 0x70, 0xB7, 0x71, /* 0x58-0x5B */
+ 0xB7, 0x72, 0xB7, 0x73, 0xB7, 0x74, 0xB7, 0x75, /* 0x5C-0x5F */
+ 0xB7, 0x76, 0xB7, 0x77, 0xB7, 0x78, 0xB7, 0x79, /* 0x60-0x63 */
+ 0xB7, 0x7A, 0xB7, 0x7B, 0xB7, 0x7C, 0xB7, 0x7D, /* 0x64-0x67 */
+ 0xB7, 0x7E, 0xB7, 0x80, 0xB7, 0x81, 0xB7, 0x82, /* 0x68-0x6B */
+ 0xB7, 0x83, 0xB7, 0x84, 0xB7, 0x85, 0xB7, 0x86, /* 0x6C-0x6F */
+ 0xF0, 0xA6, 0xB7, 0x87, 0xB7, 0x88, 0xB7, 0x89, /* 0x70-0x73 */
+ 0xD1, 0xA8, 0xB7, 0x8A, 0xBE, 0xBF, 0xC7, 0xEE, /* 0x74-0x77 */
+ 0xF1, 0xB6, 0xF1, 0xB7, 0xBF, 0xD5, 0xB7, 0x8B, /* 0x78-0x7B */
+ 0xB7, 0x8C, 0xB7, 0x8D, 0xB7, 0x8E, 0xB4, 0xA9, /* 0x7C-0x7F */
+
+ 0xF1, 0xB8, 0xCD, 0xBB, 0xB7, 0x8F, 0xC7, 0xD4, /* 0x80-0x83 */
+ 0xD5, 0xAD, 0xB7, 0x90, 0xF1, 0xB9, 0xB7, 0x91, /* 0x84-0x87 */
+ 0xF1, 0xBA, 0xB7, 0x92, 0xB7, 0x93, 0xB7, 0x94, /* 0x88-0x8B */
+ 0xB7, 0x95, 0xC7, 0xCF, 0xB7, 0x96, 0xB7, 0x97, /* 0x8C-0x8F */
+ 0xB7, 0x98, 0xD2, 0xA4, 0xD6, 0xCF, 0xB7, 0x99, /* 0x90-0x93 */
+ 0xB7, 0x9A, 0xF1, 0xBB, 0xBD, 0xD1, 0xB4, 0xB0, /* 0x94-0x97 */
+ 0xBE, 0xBD, 0xB7, 0x9B, 0xB7, 0x9C, 0xB7, 0x9D, /* 0x98-0x9B */
+ 0xB4, 0xDC, 0xCE, 0xD1, 0xB7, 0x9E, 0xBF, 0xDF, /* 0x9C-0x9F */
+ 0xF1, 0xBD, 0xB7, 0x9F, 0xB7, 0xA0, 0xB8, 0x40, /* 0xA0-0xA3 */
+ 0xB8, 0x41, 0xBF, 0xFA, 0xF1, 0xBC, 0xB8, 0x42, /* 0xA4-0xA7 */
+ 0xF1, 0xBF, 0xB8, 0x43, 0xB8, 0x44, 0xB8, 0x45, /* 0xA8-0xAB */
+ 0xF1, 0xBE, 0xF1, 0xC0, 0xB8, 0x46, 0xB8, 0x47, /* 0xAC-0xAF */
+ 0xB8, 0x48, 0xB8, 0x49, 0xB8, 0x4A, 0xF1, 0xC1, /* 0xB0-0xB3 */
+ 0xB8, 0x4B, 0xB8, 0x4C, 0xB8, 0x4D, 0xB8, 0x4E, /* 0xB4-0xB7 */
+ 0xB8, 0x4F, 0xB8, 0x50, 0xB8, 0x51, 0xB8, 0x52, /* 0xB8-0xBB */
+ 0xB8, 0x53, 0xB8, 0x54, 0xB8, 0x55, 0xC1, 0xFE, /* 0xBC-0xBF */
+ 0xB8, 0x56, 0xB8, 0x57, 0xB8, 0x58, 0xB8, 0x59, /* 0xC0-0xC3 */
+ 0xB8, 0x5A, 0xB8, 0x5B, 0xB8, 0x5C, 0xB8, 0x5D, /* 0xC4-0xC7 */
+ 0xB8, 0x5E, 0xB8, 0x5F, 0xB8, 0x60, 0xC1, 0xA2, /* 0xC8-0xCB */
+ 0xB8, 0x61, 0xB8, 0x62, 0xB8, 0x63, 0xB8, 0x64, /* 0xCC-0xCF */
+ 0xB8, 0x65, 0xB8, 0x66, 0xB8, 0x67, 0xB8, 0x68, /* 0xD0-0xD3 */
+ 0xB8, 0x69, 0xB8, 0x6A, 0xCA, 0xFA, 0xB8, 0x6B, /* 0xD4-0xD7 */
+ 0xB8, 0x6C, 0xD5, 0xBE, 0xB8, 0x6D, 0xB8, 0x6E, /* 0xD8-0xDB */
+ 0xB8, 0x6F, 0xB8, 0x70, 0xBE, 0xBA, 0xBE, 0xB9, /* 0xDC-0xDF */
+ 0xD5, 0xC2, 0xB8, 0x71, 0xB8, 0x72, 0xBF, 0xA2, /* 0xE0-0xE3 */
+ 0xB8, 0x73, 0xCD, 0xAF, 0xF1, 0xB5, 0xB8, 0x74, /* 0xE4-0xE7 */
+ 0xB8, 0x75, 0xB8, 0x76, 0xB8, 0x77, 0xB8, 0x78, /* 0xE8-0xEB */
+ 0xB8, 0x79, 0xBD, 0xDF, 0xB8, 0x7A, 0xB6, 0xCB, /* 0xEC-0xEF */
+ 0xB8, 0x7B, 0xB8, 0x7C, 0xB8, 0x7D, 0xB8, 0x7E, /* 0xF0-0xF3 */
+ 0xB8, 0x80, 0xB8, 0x81, 0xB8, 0x82, 0xB8, 0x83, /* 0xF4-0xF7 */
+ 0xB8, 0x84, 0xD6, 0xF1, 0xF3, 0xC3, 0xB8, 0x85, /* 0xF8-0xFB */
+ 0xB8, 0x86, 0xF3, 0xC4, 0xB8, 0x87, 0xB8, 0xCD, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_7B[512] = {
+ 0xB8, 0x88, 0xB8, 0x89, 0xB8, 0x8A, 0xF3, 0xC6, /* 0x00-0x03 */
+ 0xF3, 0xC7, 0xB8, 0x8B, 0xB0, 0xCA, 0xB8, 0x8C, /* 0x04-0x07 */
+ 0xF3, 0xC5, 0xB8, 0x8D, 0xF3, 0xC9, 0xCB, 0xF1, /* 0x08-0x0B */
+ 0xB8, 0x8E, 0xB8, 0x8F, 0xB8, 0x90, 0xF3, 0xCB, /* 0x0C-0x0F */
+ 0xB8, 0x91, 0xD0, 0xA6, 0xB8, 0x92, 0xB8, 0x93, /* 0x10-0x13 */
+ 0xB1, 0xCA, 0xF3, 0xC8, 0xB8, 0x94, 0xB8, 0x95, /* 0x14-0x17 */
+ 0xB8, 0x96, 0xF3, 0xCF, 0xB8, 0x97, 0xB5, 0xD1, /* 0x18-0x1B */
+ 0xB8, 0x98, 0xB8, 0x99, 0xF3, 0xD7, 0xB8, 0x9A, /* 0x1C-0x1F */
+ 0xF3, 0xD2, 0xB8, 0x9B, 0xB8, 0x9C, 0xB8, 0x9D, /* 0x20-0x23 */
+ 0xF3, 0xD4, 0xF3, 0xD3, 0xB7, 0xFB, 0xB8, 0x9E, /* 0x24-0x27 */
+ 0xB1, 0xBF, 0xB8, 0x9F, 0xF3, 0xCE, 0xF3, 0xCA, /* 0x28-0x2B */
+ 0xB5, 0xDA, 0xB8, 0xA0, 0xF3, 0xD0, 0xB9, 0x40, /* 0x2C-0x2F */
+ 0xB9, 0x41, 0xF3, 0xD1, 0xB9, 0x42, 0xF3, 0xD5, /* 0x30-0x33 */
+ 0xB9, 0x43, 0xB9, 0x44, 0xB9, 0x45, 0xB9, 0x46, /* 0x34-0x37 */
+ 0xF3, 0xCD, 0xB9, 0x47, 0xBC, 0xE3, 0xB9, 0x48, /* 0x38-0x3B */
+ 0xC1, 0xFD, 0xB9, 0x49, 0xF3, 0xD6, 0xB9, 0x4A, /* 0x3C-0x3F */
+ 0xB9, 0x4B, 0xB9, 0x4C, 0xB9, 0x4D, 0xB9, 0x4E, /* 0x40-0x43 */
+ 0xB9, 0x4F, 0xF3, 0xDA, 0xB9, 0x50, 0xF3, 0xCC, /* 0x44-0x47 */
+ 0xB9, 0x51, 0xB5, 0xC8, 0xB9, 0x52, 0xBD, 0xEE, /* 0x48-0x4B */
+ 0xF3, 0xDC, 0xB9, 0x53, 0xB9, 0x54, 0xB7, 0xA4, /* 0x4C-0x4F */
+ 0xBF, 0xF0, 0xD6, 0xFE, 0xCD, 0xB2, 0xB9, 0x55, /* 0x50-0x53 */
+ 0xB4, 0xF0, 0xB9, 0x56, 0xB2, 0xDF, 0xB9, 0x57, /* 0x54-0x57 */
+ 0xF3, 0xD8, 0xB9, 0x58, 0xF3, 0xD9, 0xC9, 0xB8, /* 0x58-0x5B */
+ 0xB9, 0x59, 0xF3, 0xDD, 0xB9, 0x5A, 0xB9, 0x5B, /* 0x5C-0x5F */
+ 0xF3, 0xDE, 0xB9, 0x5C, 0xF3, 0xE1, 0xB9, 0x5D, /* 0x60-0x63 */
+ 0xB9, 0x5E, 0xB9, 0x5F, 0xB9, 0x60, 0xB9, 0x61, /* 0x64-0x67 */
+ 0xB9, 0x62, 0xB9, 0x63, 0xB9, 0x64, 0xB9, 0x65, /* 0x68-0x6B */
+ 0xB9, 0x66, 0xB9, 0x67, 0xF3, 0xDF, 0xB9, 0x68, /* 0x6C-0x6F */
+ 0xB9, 0x69, 0xF3, 0xE3, 0xF3, 0xE2, 0xB9, 0x6A, /* 0x70-0x73 */
+ 0xB9, 0x6B, 0xF3, 0xDB, 0xB9, 0x6C, 0xBF, 0xEA, /* 0x74-0x77 */
+ 0xB9, 0x6D, 0xB3, 0xEF, 0xB9, 0x6E, 0xF3, 0xE0, /* 0x78-0x7B */
+ 0xB9, 0x6F, 0xB9, 0x70, 0xC7, 0xA9, 0xB9, 0x71, /* 0x7C-0x7F */
+
+ 0xBC, 0xF2, 0xB9, 0x72, 0xB9, 0x73, 0xB9, 0x74, /* 0x80-0x83 */
+ 0xB9, 0x75, 0xF3, 0xEB, 0xB9, 0x76, 0xB9, 0x77, /* 0x84-0x87 */
+ 0xB9, 0x78, 0xB9, 0x79, 0xB9, 0x7A, 0xB9, 0x7B, /* 0x88-0x8B */
+ 0xB9, 0x7C, 0xB9, 0xBF, 0xB9, 0x7D, 0xB9, 0x7E, /* 0x8C-0x8F */
+ 0xF3, 0xE4, 0xB9, 0x80, 0xB9, 0x81, 0xB9, 0x82, /* 0x90-0x93 */
+ 0xB2, 0xAD, 0xBB, 0xFE, 0xB9, 0x83, 0xCB, 0xE3, /* 0x94-0x97 */
+ 0xB9, 0x84, 0xB9, 0x85, 0xB9, 0x86, 0xB9, 0x87, /* 0x98-0x9B */
+ 0xF3, 0xED, 0xF3, 0xE9, 0xB9, 0x88, 0xB9, 0x89, /* 0x9C-0x9F */
+ 0xB9, 0x8A, 0xB9, 0xDC, 0xF3, 0xEE, 0xB9, 0x8B, /* 0xA0-0xA3 */
+ 0xB9, 0x8C, 0xB9, 0x8D, 0xF3, 0xE5, 0xF3, 0xE6, /* 0xA4-0xA7 */
+ 0xF3, 0xEA, 0xC2, 0xE1, 0xF3, 0xEC, 0xF3, 0xEF, /* 0xA8-0xAB */
+ 0xF3, 0xE8, 0xBC, 0xFD, 0xB9, 0x8E, 0xB9, 0x8F, /* 0xAC-0xAF */
+ 0xB9, 0x90, 0xCF, 0xE4, 0xB9, 0x91, 0xB9, 0x92, /* 0xB0-0xB3 */
+ 0xF3, 0xF0, 0xB9, 0x93, 0xB9, 0x94, 0xB9, 0x95, /* 0xB4-0xB7 */
+ 0xF3, 0xE7, 0xB9, 0x96, 0xB9, 0x97, 0xB9, 0x98, /* 0xB8-0xBB */
+ 0xB9, 0x99, 0xB9, 0x9A, 0xB9, 0x9B, 0xB9, 0x9C, /* 0xBC-0xBF */
+ 0xB9, 0x9D, 0xF3, 0xF2, 0xB9, 0x9E, 0xB9, 0x9F, /* 0xC0-0xC3 */
+ 0xB9, 0xA0, 0xBA, 0x40, 0xD7, 0xAD, 0xC6, 0xAA, /* 0xC4-0xC7 */
+ 0xBA, 0x41, 0xBA, 0x42, 0xBA, 0x43, 0xBA, 0x44, /* 0xC8-0xCB */
+ 0xF3, 0xF3, 0xBA, 0x45, 0xBA, 0x46, 0xBA, 0x47, /* 0xCC-0xCF */
+ 0xBA, 0x48, 0xF3, 0xF1, 0xBA, 0x49, 0xC2, 0xA8, /* 0xD0-0xD3 */
+ 0xBA, 0x4A, 0xBA, 0x4B, 0xBA, 0x4C, 0xBA, 0x4D, /* 0xD4-0xD7 */
+ 0xBA, 0x4E, 0xB8, 0xDD, 0xF3, 0xF5, 0xBA, 0x4F, /* 0xD8-0xDB */
+ 0xBA, 0x50, 0xF3, 0xF4, 0xBA, 0x51, 0xBA, 0x52, /* 0xDC-0xDF */
+ 0xBA, 0x53, 0xB4, 0xDB, 0xBA, 0x54, 0xBA, 0x55, /* 0xE0-0xE3 */
+ 0xBA, 0x56, 0xF3, 0xF6, 0xF3, 0xF7, 0xBA, 0x57, /* 0xE4-0xE7 */
+ 0xBA, 0x58, 0xBA, 0x59, 0xF3, 0xF8, 0xBA, 0x5A, /* 0xE8-0xEB */
+ 0xBA, 0x5B, 0xBA, 0x5C, 0xC0, 0xBA, 0xBA, 0x5D, /* 0xEC-0xEF */
+ 0xBA, 0x5E, 0xC0, 0xE9, 0xBA, 0x5F, 0xBA, 0x60, /* 0xF0-0xF3 */
+ 0xBA, 0x61, 0xBA, 0x62, 0xBA, 0x63, 0xC5, 0xF1, /* 0xF4-0xF7 */
+ 0xBA, 0x64, 0xBA, 0x65, 0xBA, 0x66, 0xBA, 0x67, /* 0xF8-0xFB */
+ 0xF3, 0xFB, 0xBA, 0x68, 0xF3, 0xFA, 0xBA, 0x69, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_7C[512] = {
+ 0xBA, 0x6A, 0xBA, 0x6B, 0xBA, 0x6C, 0xBA, 0x6D, /* 0x00-0x03 */
+ 0xBA, 0x6E, 0xBA, 0x6F, 0xBA, 0x70, 0xB4, 0xD8, /* 0x04-0x07 */
+ 0xBA, 0x71, 0xBA, 0x72, 0xBA, 0x73, 0xF3, 0xFE, /* 0x08-0x0B */
+ 0xF3, 0xF9, 0xBA, 0x74, 0xBA, 0x75, 0xF3, 0xFC, /* 0x0C-0x0F */
+ 0xBA, 0x76, 0xBA, 0x77, 0xBA, 0x78, 0xBA, 0x79, /* 0x10-0x13 */
+ 0xBA, 0x7A, 0xBA, 0x7B, 0xF3, 0xFD, 0xBA, 0x7C, /* 0x14-0x17 */
+ 0xBA, 0x7D, 0xBA, 0x7E, 0xBA, 0x80, 0xBA, 0x81, /* 0x18-0x1B */
+ 0xBA, 0x82, 0xBA, 0x83, 0xBA, 0x84, 0xF4, 0xA1, /* 0x1C-0x1F */
+ 0xBA, 0x85, 0xBA, 0x86, 0xBA, 0x87, 0xBA, 0x88, /* 0x20-0x23 */
+ 0xBA, 0x89, 0xBA, 0x8A, 0xF4, 0xA3, 0xBB, 0xC9, /* 0x24-0x27 */
+ 0xBA, 0x8B, 0xBA, 0x8C, 0xF4, 0xA2, 0xBA, 0x8D, /* 0x28-0x2B */
+ 0xBA, 0x8E, 0xBA, 0x8F, 0xBA, 0x90, 0xBA, 0x91, /* 0x2C-0x2F */
+ 0xBA, 0x92, 0xBA, 0x93, 0xBA, 0x94, 0xBA, 0x95, /* 0x30-0x33 */
+ 0xBA, 0x96, 0xBA, 0x97, 0xBA, 0x98, 0xBA, 0x99, /* 0x34-0x37 */
+ 0xF4, 0xA4, 0xBA, 0x9A, 0xBA, 0x9B, 0xBA, 0x9C, /* 0x38-0x3B */
+ 0xBA, 0x9D, 0xBA, 0x9E, 0xBA, 0x9F, 0xB2, 0xBE, /* 0x3C-0x3F */
+ 0xF4, 0xA6, 0xF4, 0xA5, 0xBA, 0xA0, 0xBB, 0x40, /* 0x40-0x43 */
+ 0xBB, 0x41, 0xBB, 0x42, 0xBB, 0x43, 0xBB, 0x44, /* 0x44-0x47 */
+ 0xBB, 0x45, 0xBB, 0x46, 0xBB, 0x47, 0xBB, 0x48, /* 0x48-0x4B */
+ 0xBB, 0x49, 0xBC, 0xAE, 0xBB, 0x4A, 0xBB, 0x4B, /* 0x4C-0x4F */
+ 0xBB, 0x4C, 0xBB, 0x4D, 0xBB, 0x4E, 0xBB, 0x4F, /* 0x50-0x53 */
+ 0xBB, 0x50, 0xBB, 0x51, 0xBB, 0x52, 0xBB, 0x53, /* 0x54-0x57 */
+ 0xBB, 0x54, 0xBB, 0x55, 0xBB, 0x56, 0xBB, 0x57, /* 0x58-0x5B */
+ 0xBB, 0x58, 0xBB, 0x59, 0xBB, 0x5A, 0xBB, 0x5B, /* 0x5C-0x5F */
+ 0xBB, 0x5C, 0xBB, 0x5D, 0xBB, 0x5E, 0xBB, 0x5F, /* 0x60-0x63 */
+ 0xBB, 0x60, 0xBB, 0x61, 0xBB, 0x62, 0xBB, 0x63, /* 0x64-0x67 */
+ 0xBB, 0x64, 0xBB, 0x65, 0xBB, 0x66, 0xBB, 0x67, /* 0x68-0x6B */
+ 0xBB, 0x68, 0xBB, 0x69, 0xBB, 0x6A, 0xBB, 0x6B, /* 0x6C-0x6F */
+ 0xBB, 0x6C, 0xBB, 0x6D, 0xBB, 0x6E, 0xC3, 0xD7, /* 0x70-0x73 */
+ 0xD9, 0xE1, 0xBB, 0x6F, 0xBB, 0x70, 0xBB, 0x71, /* 0x74-0x77 */
+ 0xBB, 0x72, 0xBB, 0x73, 0xBB, 0x74, 0xC0, 0xE0, /* 0x78-0x7B */
+ 0xF4, 0xCC, 0xD7, 0xD1, 0xBB, 0x75, 0xBB, 0x76, /* 0x7C-0x7F */
+
+ 0xBB, 0x77, 0xBB, 0x78, 0xBB, 0x79, 0xBB, 0x7A, /* 0x80-0x83 */
+ 0xBB, 0x7B, 0xBB, 0x7C, 0xBB, 0x7D, 0xBB, 0x7E, /* 0x84-0x87 */
+ 0xBB, 0x80, 0xB7, 0xDB, 0xBB, 0x81, 0xBB, 0x82, /* 0x88-0x8B */
+ 0xBB, 0x83, 0xBB, 0x84, 0xBB, 0x85, 0xBB, 0x86, /* 0x8C-0x8F */
+ 0xBB, 0x87, 0xF4, 0xCE, 0xC1, 0xA3, 0xBB, 0x88, /* 0x90-0x93 */
+ 0xBB, 0x89, 0xC6, 0xC9, 0xBB, 0x8A, 0xB4, 0xD6, /* 0x94-0x97 */
+ 0xD5, 0xB3, 0xBB, 0x8B, 0xBB, 0x8C, 0xBB, 0x8D, /* 0x98-0x9B */
+ 0xF4, 0xD0, 0xF4, 0xCF, 0xF4, 0xD1, 0xCB, 0xDA, /* 0x9C-0x9F */
+ 0xBB, 0x8E, 0xBB, 0x8F, 0xF4, 0xD2, 0xBB, 0x90, /* 0xA0-0xA3 */
+ 0xD4, 0xC1, 0xD6, 0xE0, 0xBB, 0x91, 0xBB, 0x92, /* 0xA4-0xA7 */
+ 0xBB, 0x93, 0xBB, 0x94, 0xB7, 0xE0, 0xBB, 0x95, /* 0xA8-0xAB */
+ 0xBB, 0x96, 0xBB, 0x97, 0xC1, 0xB8, 0xBB, 0x98, /* 0xAC-0xAF */
+ 0xBB, 0x99, 0xC1, 0xBB, 0xF4, 0xD3, 0xBE, 0xAC, /* 0xB0-0xB3 */
+ 0xBB, 0x9A, 0xBB, 0x9B, 0xBB, 0x9C, 0xBB, 0x9D, /* 0xB4-0xB7 */
+ 0xBB, 0x9E, 0xB4, 0xE2, 0xBB, 0x9F, 0xBB, 0xA0, /* 0xB8-0xBB */
+ 0xF4, 0xD4, 0xF4, 0xD5, 0xBE, 0xAB, 0xBC, 0x40, /* 0xBC-0xBF */
+ 0xBC, 0x41, 0xF4, 0xD6, 0xBC, 0x42, 0xBC, 0x43, /* 0xC0-0xC3 */
+ 0xBC, 0x44, 0xF4, 0xDB, 0xBC, 0x45, 0xF4, 0xD7, /* 0xC4-0xC7 */
+ 0xF4, 0xDA, 0xBC, 0x46, 0xBA, 0xFD, 0xBC, 0x47, /* 0xC8-0xCB */
+ 0xF4, 0xD8, 0xF4, 0xD9, 0xBC, 0x48, 0xBC, 0x49, /* 0xCC-0xCF */
+ 0xBC, 0x4A, 0xBC, 0x4B, 0xBC, 0x4C, 0xBC, 0x4D, /* 0xD0-0xD3 */
+ 0xBC, 0x4E, 0xB8, 0xE2, 0xCC, 0xC7, 0xF4, 0xDC, /* 0xD4-0xD7 */
+ 0xBC, 0x4F, 0xB2, 0xDA, 0xBC, 0x50, 0xBC, 0x51, /* 0xD8-0xDB */
+ 0xC3, 0xD3, 0xBC, 0x52, 0xBC, 0x53, 0xD4, 0xE3, /* 0xDC-0xDF */
+ 0xBF, 0xB7, 0xBC, 0x54, 0xBC, 0x55, 0xBC, 0x56, /* 0xE0-0xE3 */
+ 0xBC, 0x57, 0xBC, 0x58, 0xBC, 0x59, 0xBC, 0x5A, /* 0xE4-0xE7 */
+ 0xF4, 0xDD, 0xBC, 0x5B, 0xBC, 0x5C, 0xBC, 0x5D, /* 0xE8-0xEB */
+ 0xBC, 0x5E, 0xBC, 0x5F, 0xBC, 0x60, 0xC5, 0xB4, /* 0xEC-0xEF */
+ 0xBC, 0x61, 0xBC, 0x62, 0xBC, 0x63, 0xBC, 0x64, /* 0xF0-0xF3 */
+ 0xBC, 0x65, 0xBC, 0x66, 0xBC, 0x67, 0xBC, 0x68, /* 0xF4-0xF7 */
+ 0xF4, 0xE9, 0xBC, 0x69, 0xBC, 0x6A, 0xCF, 0xB5, /* 0xF8-0xFB */
+ 0xBC, 0x6B, 0xBC, 0x6C, 0xBC, 0x6D, 0xBC, 0x6E, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_7D[512] = {
+ 0xBC, 0x6F, 0xBC, 0x70, 0xBC, 0x71, 0xBC, 0x72, /* 0x00-0x03 */
+ 0xBC, 0x73, 0xBC, 0x74, 0xBC, 0x75, 0xBC, 0x76, /* 0x04-0x07 */
+ 0xBC, 0x77, 0xBC, 0x78, 0xCE, 0xC9, 0xBC, 0x79, /* 0x08-0x0B */
+ 0xBC, 0x7A, 0xBC, 0x7B, 0xBC, 0x7C, 0xBC, 0x7D, /* 0x0C-0x0F */
+ 0xBC, 0x7E, 0xBC, 0x80, 0xBC, 0x81, 0xBC, 0x82, /* 0x10-0x13 */
+ 0xBC, 0x83, 0xBC, 0x84, 0xBC, 0x85, 0xBC, 0x86, /* 0x14-0x17 */
+ 0xBC, 0x87, 0xBC, 0x88, 0xBC, 0x89, 0xBC, 0x8A, /* 0x18-0x1B */
+ 0xBC, 0x8B, 0xBC, 0x8C, 0xBC, 0x8D, 0xBC, 0x8E, /* 0x1C-0x1F */
+ 0xCB, 0xD8, 0xBC, 0x8F, 0xCB, 0xF7, 0xBC, 0x90, /* 0x20-0x23 */
+ 0xBC, 0x91, 0xBC, 0x92, 0xBC, 0x93, 0xBD, 0xF4, /* 0x24-0x27 */
+ 0xBC, 0x94, 0xBC, 0x95, 0xBC, 0x96, 0xD7, 0xCF, /* 0x28-0x2B */
+ 0xBC, 0x97, 0xBC, 0x98, 0xBC, 0x99, 0xC0, 0xDB, /* 0x2C-0x2F */
+ 0xBC, 0x9A, 0xBC, 0x9B, 0xBC, 0x9C, 0xBC, 0x9D, /* 0x30-0x33 */
+ 0xBC, 0x9E, 0xBC, 0x9F, 0xBC, 0xA0, 0xBD, 0x40, /* 0x34-0x37 */
+ 0xBD, 0x41, 0xBD, 0x42, 0xBD, 0x43, 0xBD, 0x44, /* 0x38-0x3B */
+ 0xBD, 0x45, 0xBD, 0x46, 0xBD, 0x47, 0xBD, 0x48, /* 0x3C-0x3F */
+ 0xBD, 0x49, 0xBD, 0x4A, 0xBD, 0x4B, 0xBD, 0x4C, /* 0x40-0x43 */
+ 0xBD, 0x4D, 0xBD, 0x4E, 0xBD, 0x4F, 0xBD, 0x50, /* 0x44-0x47 */
+ 0xBD, 0x51, 0xBD, 0x52, 0xBD, 0x53, 0xBD, 0x54, /* 0x48-0x4B */
+ 0xBD, 0x55, 0xBD, 0x56, 0xBD, 0x57, 0xBD, 0x58, /* 0x4C-0x4F */
+ 0xBD, 0x59, 0xBD, 0x5A, 0xBD, 0x5B, 0xBD, 0x5C, /* 0x50-0x53 */
+ 0xBD, 0x5D, 0xBD, 0x5E, 0xBD, 0x5F, 0xBD, 0x60, /* 0x54-0x57 */
+ 0xBD, 0x61, 0xBD, 0x62, 0xBD, 0x63, 0xBD, 0x64, /* 0x58-0x5B */
+ 0xBD, 0x65, 0xBD, 0x66, 0xBD, 0x67, 0xBD, 0x68, /* 0x5C-0x5F */
+ 0xBD, 0x69, 0xBD, 0x6A, 0xBD, 0x6B, 0xBD, 0x6C, /* 0x60-0x63 */
+ 0xBD, 0x6D, 0xBD, 0x6E, 0xBD, 0x6F, 0xBD, 0x70, /* 0x64-0x67 */
+ 0xBD, 0x71, 0xBD, 0x72, 0xBD, 0x73, 0xBD, 0x74, /* 0x68-0x6B */
+ 0xBD, 0x75, 0xBD, 0x76, 0xD0, 0xF5, 0xBD, 0x77, /* 0x6C-0x6F */
+ 0xBD, 0x78, 0xBD, 0x79, 0xBD, 0x7A, 0xBD, 0x7B, /* 0x70-0x73 */
+ 0xBD, 0x7C, 0xBD, 0x7D, 0xBD, 0x7E, 0xF4, 0xEA, /* 0x74-0x77 */
+ 0xBD, 0x80, 0xBD, 0x81, 0xBD, 0x82, 0xBD, 0x83, /* 0x78-0x7B */
+ 0xBD, 0x84, 0xBD, 0x85, 0xBD, 0x86, 0xBD, 0x87, /* 0x7C-0x7F */
+
+ 0xBD, 0x88, 0xBD, 0x89, 0xBD, 0x8A, 0xBD, 0x8B, /* 0x80-0x83 */
+ 0xBD, 0x8C, 0xBD, 0x8D, 0xBD, 0x8E, 0xBD, 0x8F, /* 0x84-0x87 */
+ 0xBD, 0x90, 0xBD, 0x91, 0xBD, 0x92, 0xBD, 0x93, /* 0x88-0x8B */
+ 0xBD, 0x94, 0xBD, 0x95, 0xBD, 0x96, 0xBD, 0x97, /* 0x8C-0x8F */
+ 0xBD, 0x98, 0xBD, 0x99, 0xBD, 0x9A, 0xBD, 0x9B, /* 0x90-0x93 */
+ 0xBD, 0x9C, 0xBD, 0x9D, 0xBD, 0x9E, 0xBD, 0x9F, /* 0x94-0x97 */
+ 0xBD, 0xA0, 0xBE, 0x40, 0xBE, 0x41, 0xBE, 0x42, /* 0x98-0x9B */
+ 0xBE, 0x43, 0xBE, 0x44, 0xBE, 0x45, 0xBE, 0x46, /* 0x9C-0x9F */
+ 0xBE, 0x47, 0xBE, 0x48, 0xBE, 0x49, 0xBE, 0x4A, /* 0xA0-0xA3 */
+ 0xBE, 0x4B, 0xBE, 0x4C, 0xF4, 0xEB, 0xBE, 0x4D, /* 0xA4-0xA7 */
+ 0xBE, 0x4E, 0xBE, 0x4F, 0xBE, 0x50, 0xBE, 0x51, /* 0xA8-0xAB */
+ 0xBE, 0x52, 0xBE, 0x53, 0xF4, 0xEC, 0xBE, 0x54, /* 0xAC-0xAF */
+ 0xBE, 0x55, 0xBE, 0x56, 0xBE, 0x57, 0xBE, 0x58, /* 0xB0-0xB3 */
+ 0xBE, 0x59, 0xBE, 0x5A, 0xBE, 0x5B, 0xBE, 0x5C, /* 0xB4-0xB7 */
+ 0xBE, 0x5D, 0xBE, 0x5E, 0xBE, 0x5F, 0xBE, 0x60, /* 0xB8-0xBB */
+ 0xBE, 0x61, 0xBE, 0x62, 0xBE, 0x63, 0xBE, 0x64, /* 0xBC-0xBF */
+ 0xBE, 0x65, 0xBE, 0x66, 0xBE, 0x67, 0xBE, 0x68, /* 0xC0-0xC3 */
+ 0xBE, 0x69, 0xBE, 0x6A, 0xBE, 0x6B, 0xBE, 0x6C, /* 0xC4-0xC7 */
+ 0xBE, 0x6D, 0xBE, 0x6E, 0xBE, 0x6F, 0xBE, 0x70, /* 0xC8-0xCB */
+ 0xBE, 0x71, 0xBE, 0x72, 0xBE, 0x73, 0xBE, 0x74, /* 0xCC-0xCF */
+ 0xBE, 0x75, 0xBE, 0x76, 0xBE, 0x77, 0xBE, 0x78, /* 0xD0-0xD3 */
+ 0xBE, 0x79, 0xBE, 0x7A, 0xBE, 0x7B, 0xBE, 0x7C, /* 0xD4-0xD7 */
+ 0xBE, 0x7D, 0xBE, 0x7E, 0xBE, 0x80, 0xBE, 0x81, /* 0xD8-0xDB */
+ 0xBE, 0x82, 0xBE, 0x83, 0xBE, 0x84, 0xBE, 0x85, /* 0xDC-0xDF */
+ 0xBE, 0x86, 0xBE, 0x87, 0xBE, 0x88, 0xBE, 0x89, /* 0xE0-0xE3 */
+ 0xBE, 0x8A, 0xBE, 0x8B, 0xBE, 0x8C, 0xBE, 0x8D, /* 0xE4-0xE7 */
+ 0xBE, 0x8E, 0xBE, 0x8F, 0xBE, 0x90, 0xBE, 0x91, /* 0xE8-0xEB */
+ 0xBE, 0x92, 0xBE, 0x93, 0xBE, 0x94, 0xBE, 0x95, /* 0xEC-0xEF */
+ 0xBE, 0x96, 0xBE, 0x97, 0xBE, 0x98, 0xBE, 0x99, /* 0xF0-0xF3 */
+ 0xBE, 0x9A, 0xBE, 0x9B, 0xBE, 0x9C, 0xBE, 0x9D, /* 0xF4-0xF7 */
+ 0xBE, 0x9E, 0xBE, 0x9F, 0xBE, 0xA0, 0xBF, 0x40, /* 0xF8-0xFB */
+ 0xBF, 0x41, 0xBF, 0x42, 0xBF, 0x43, 0xBF, 0x44, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_7E[512] = {
+ 0xBF, 0x45, 0xBF, 0x46, 0xBF, 0x47, 0xBF, 0x48, /* 0x00-0x03 */
+ 0xBF, 0x49, 0xBF, 0x4A, 0xBF, 0x4B, 0xBF, 0x4C, /* 0x04-0x07 */
+ 0xBF, 0x4D, 0xBF, 0x4E, 0xBF, 0x4F, 0xBF, 0x50, /* 0x08-0x0B */
+ 0xBF, 0x51, 0xBF, 0x52, 0xBF, 0x53, 0xBF, 0x54, /* 0x0C-0x0F */
+ 0xBF, 0x55, 0xBF, 0x56, 0xBF, 0x57, 0xBF, 0x58, /* 0x10-0x13 */
+ 0xBF, 0x59, 0xBF, 0x5A, 0xBF, 0x5B, 0xBF, 0x5C, /* 0x14-0x17 */
+ 0xBF, 0x5D, 0xBF, 0x5E, 0xBF, 0x5F, 0xBF, 0x60, /* 0x18-0x1B */
+ 0xBF, 0x61, 0xBF, 0x62, 0xBF, 0x63, 0xBF, 0x64, /* 0x1C-0x1F */
+ 0xBF, 0x65, 0xBF, 0x66, 0xBF, 0x67, 0xBF, 0x68, /* 0x20-0x23 */
+ 0xBF, 0x69, 0xBF, 0x6A, 0xBF, 0x6B, 0xBF, 0x6C, /* 0x24-0x27 */
+ 0xBF, 0x6D, 0xBF, 0x6E, 0xBF, 0x6F, 0xBF, 0x70, /* 0x28-0x2B */
+ 0xBF, 0x71, 0xBF, 0x72, 0xBF, 0x73, 0xBF, 0x74, /* 0x2C-0x2F */
+ 0xBF, 0x75, 0xBF, 0x76, 0xBF, 0x77, 0xBF, 0x78, /* 0x30-0x33 */
+ 0xBF, 0x79, 0xBF, 0x7A, 0xBF, 0x7B, 0xBF, 0x7C, /* 0x34-0x37 */
+ 0xBF, 0x7D, 0xBF, 0x7E, 0xBF, 0x80, 0xF7, 0xE3, /* 0x38-0x3B */
+ 0xBF, 0x81, 0xBF, 0x82, 0xBF, 0x83, 0xBF, 0x84, /* 0x3C-0x3F */
+ 0xBF, 0x85, 0xB7, 0xB1, 0xBF, 0x86, 0xBF, 0x87, /* 0x40-0x43 */
+ 0xBF, 0x88, 0xBF, 0x89, 0xBF, 0x8A, 0xF4, 0xED, /* 0x44-0x47 */
+ 0xBF, 0x8B, 0xBF, 0x8C, 0xBF, 0x8D, 0xBF, 0x8E, /* 0x48-0x4B */
+ 0xBF, 0x8F, 0xBF, 0x90, 0xBF, 0x91, 0xBF, 0x92, /* 0x4C-0x4F */
+ 0xBF, 0x93, 0xBF, 0x94, 0xBF, 0x95, 0xBF, 0x96, /* 0x50-0x53 */
+ 0xBF, 0x97, 0xBF, 0x98, 0xBF, 0x99, 0xBF, 0x9A, /* 0x54-0x57 */
+ 0xBF, 0x9B, 0xBF, 0x9C, 0xBF, 0x9D, 0xBF, 0x9E, /* 0x58-0x5B */
+ 0xBF, 0x9F, 0xBF, 0xA0, 0xC0, 0x40, 0xC0, 0x41, /* 0x5C-0x5F */
+ 0xC0, 0x42, 0xC0, 0x43, 0xC0, 0x44, 0xC0, 0x45, /* 0x60-0x63 */
+ 0xC0, 0x46, 0xC0, 0x47, 0xC0, 0x48, 0xC0, 0x49, /* 0x64-0x67 */
+ 0xC0, 0x4A, 0xC0, 0x4B, 0xC0, 0x4C, 0xC0, 0x4D, /* 0x68-0x6B */
+ 0xC0, 0x4E, 0xC0, 0x4F, 0xC0, 0x50, 0xC0, 0x51, /* 0x6C-0x6F */
+ 0xC0, 0x52, 0xC0, 0x53, 0xC0, 0x54, 0xC0, 0x55, /* 0x70-0x73 */
+ 0xC0, 0x56, 0xC0, 0x57, 0xC0, 0x58, 0xC0, 0x59, /* 0x74-0x77 */
+ 0xC0, 0x5A, 0xC0, 0x5B, 0xC0, 0x5C, 0xC0, 0x5D, /* 0x78-0x7B */
+ 0xC0, 0x5E, 0xC0, 0x5F, 0xC0, 0x60, 0xC0, 0x61, /* 0x7C-0x7F */
+
+ 0xC0, 0x62, 0xC0, 0x63, 0xD7, 0xEB, 0xC0, 0x64, /* 0x80-0x83 */
+ 0xC0, 0x65, 0xC0, 0x66, 0xC0, 0x67, 0xC0, 0x68, /* 0x84-0x87 */
+ 0xC0, 0x69, 0xC0, 0x6A, 0xC0, 0x6B, 0xC0, 0x6C, /* 0x88-0x8B */
+ 0xC0, 0x6D, 0xC0, 0x6E, 0xC0, 0x6F, 0xC0, 0x70, /* 0x8C-0x8F */
+ 0xC0, 0x71, 0xC0, 0x72, 0xC0, 0x73, 0xC0, 0x74, /* 0x90-0x93 */
+ 0xC0, 0x75, 0xC0, 0x76, 0xC0, 0x77, 0xC0, 0x78, /* 0x94-0x97 */
+ 0xC0, 0x79, 0xC0, 0x7A, 0xC0, 0x7B, 0xF4, 0xEE, /* 0x98-0x9B */
+ 0xC0, 0x7C, 0xC0, 0x7D, 0xC0, 0x7E, 0xE6, 0xF9, /* 0x9C-0x9F */
+ 0xBE, 0xC0, 0xE6, 0xFA, 0xBA, 0xEC, 0xE6, 0xFB, /* 0xA0-0xA3 */
+ 0xCF, 0xCB, 0xE6, 0xFC, 0xD4, 0xBC, 0xBC, 0xB6, /* 0xA4-0xA7 */
+ 0xE6, 0xFD, 0xE6, 0xFE, 0xBC, 0xCD, 0xC8, 0xD2, /* 0xA8-0xAB */
+ 0xCE, 0xB3, 0xE7, 0xA1, 0xC0, 0x80, 0xB4, 0xBF, /* 0xAC-0xAF */
+ 0xE7, 0xA2, 0xC9, 0xB4, 0xB8, 0xD9, 0xC4, 0xC9, /* 0xB0-0xB3 */
+ 0xC0, 0x81, 0xD7, 0xDD, 0xC2, 0xDA, 0xB7, 0xD7, /* 0xB4-0xB7 */
+ 0xD6, 0xBD, 0xCE, 0xC6, 0xB7, 0xC4, 0xC0, 0x82, /* 0xB8-0xBB */
+ 0xC0, 0x83, 0xC5, 0xA6, 0xE7, 0xA3, 0xCF, 0xDF, /* 0xBC-0xBF */
+ 0xE7, 0xA4, 0xE7, 0xA5, 0xE7, 0xA6, 0xC1, 0xB7, /* 0xC0-0xC3 */
+ 0xD7, 0xE9, 0xC9, 0xF0, 0xCF, 0xB8, 0xD6, 0xAF, /* 0xC4-0xC7 */
+ 0xD6, 0xD5, 0xE7, 0xA7, 0xB0, 0xED, 0xE7, 0xA8, /* 0xC8-0xCB */
+ 0xE7, 0xA9, 0xC9, 0xDC, 0xD2, 0xEF, 0xBE, 0xAD, /* 0xCC-0xCF */
+ 0xE7, 0xAA, 0xB0, 0xF3, 0xC8, 0xDE, 0xBD, 0xE1, /* 0xD0-0xD3 */
+ 0xE7, 0xAB, 0xC8, 0xC6, 0xC0, 0x84, 0xE7, 0xAC, /* 0xD4-0xD7 */
+ 0xBB, 0xE6, 0xB8, 0xF8, 0xD1, 0xA4, 0xE7, 0xAD, /* 0xD8-0xDB */
+ 0xC2, 0xE7, 0xBE, 0xF8, 0xBD, 0xCA, 0xCD, 0xB3, /* 0xDC-0xDF */
+ 0xE7, 0xAE, 0xE7, 0xAF, 0xBE, 0xEE, 0xD0, 0xE5, /* 0xE0-0xE3 */
+ 0xC0, 0x85, 0xCB, 0xE7, 0xCC, 0xD0, 0xBC, 0xCC, /* 0xE4-0xE7 */
+ 0xE7, 0xB0, 0xBC, 0xA8, 0xD0, 0xF7, 0xE7, 0xB1, /* 0xE8-0xEB */
+ 0xC0, 0x86, 0xD0, 0xF8, 0xE7, 0xB2, 0xE7, 0xB3, /* 0xEC-0xEF */
+ 0xB4, 0xC2, 0xE7, 0xB4, 0xE7, 0xB5, 0xC9, 0xFE, /* 0xF0-0xF3 */
+ 0xCE, 0xAC, 0xC3, 0xE0, 0xE7, 0xB7, 0xB1, 0xC1, /* 0xF4-0xF7 */
+ 0xB3, 0xF1, 0xC0, 0x87, 0xE7, 0xB8, 0xE7, 0xB9, /* 0xF8-0xFB */
+ 0xD7, 0xDB, 0xD5, 0xC0, 0xE7, 0xBA, 0xC2, 0xCC, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_7F[512] = {
+ 0xD7, 0xBA, 0xE7, 0xBB, 0xE7, 0xBC, 0xE7, 0xBD, /* 0x00-0x03 */
+ 0xBC, 0xEA, 0xC3, 0xE5, 0xC0, 0xC2, 0xE7, 0xBE, /* 0x04-0x07 */
+ 0xE7, 0xBF, 0xBC, 0xA9, 0xC0, 0x88, 0xE7, 0xC0, /* 0x08-0x0B */
+ 0xE7, 0xC1, 0xE7, 0xB6, 0xB6, 0xD0, 0xE7, 0xC2, /* 0x0C-0x0F */
+ 0xC0, 0x89, 0xE7, 0xC3, 0xE7, 0xC4, 0xBB, 0xBA, /* 0x10-0x13 */
+ 0xB5, 0xDE, 0xC2, 0xC6, 0xB1, 0xE0, 0xE7, 0xC5, /* 0x14-0x17 */
+ 0xD4, 0xB5, 0xE7, 0xC6, 0xB8, 0xBF, 0xE7, 0xC8, /* 0x18-0x1B */
+ 0xE7, 0xC7, 0xB7, 0xEC, 0xC0, 0x8A, 0xE7, 0xC9, /* 0x1C-0x1F */
+ 0xB2, 0xF8, 0xE7, 0xCA, 0xE7, 0xCB, 0xE7, 0xCC, /* 0x20-0x23 */
+ 0xE7, 0xCD, 0xE7, 0xCE, 0xE7, 0xCF, 0xE7, 0xD0, /* 0x24-0x27 */
+ 0xD3, 0xA7, 0xCB, 0xF5, 0xE7, 0xD1, 0xE7, 0xD2, /* 0x28-0x2B */
+ 0xE7, 0xD3, 0xE7, 0xD4, 0xC9, 0xC9, 0xE7, 0xD5, /* 0x2C-0x2F */
+ 0xE7, 0xD6, 0xE7, 0xD7, 0xE7, 0xD8, 0xE7, 0xD9, /* 0x30-0x33 */
+ 0xBD, 0xC9, 0xE7, 0xDA, 0xF3, 0xBE, 0xC0, 0x8B, /* 0x34-0x37 */
+ 0xB8, 0xD7, 0xC0, 0x8C, 0xC8, 0xB1, 0xC0, 0x8D, /* 0x38-0x3B */
+ 0xC0, 0x8E, 0xC0, 0x8F, 0xC0, 0x90, 0xC0, 0x91, /* 0x3C-0x3F */
+ 0xC0, 0x92, 0xC0, 0x93, 0xF3, 0xBF, 0xC0, 0x94, /* 0x40-0x43 */
+ 0xF3, 0xC0, 0xF3, 0xC1, 0xC0, 0x95, 0xC0, 0x96, /* 0x44-0x47 */
+ 0xC0, 0x97, 0xC0, 0x98, 0xC0, 0x99, 0xC0, 0x9A, /* 0x48-0x4B */
+ 0xC0, 0x9B, 0xC0, 0x9C, 0xC0, 0x9D, 0xC0, 0x9E, /* 0x4C-0x4F */
+ 0xB9, 0xDE, 0xCD, 0xF8, 0xC0, 0x9F, 0xC0, 0xA0, /* 0x50-0x53 */
+ 0xD8, 0xE8, 0xBA, 0xB1, 0xC1, 0x40, 0xC2, 0xDE, /* 0x54-0x57 */
+ 0xEE, 0xB7, 0xC1, 0x41, 0xB7, 0xA3, 0xC1, 0x42, /* 0x58-0x5B */
+ 0xC1, 0x43, 0xC1, 0x44, 0xC1, 0x45, 0xEE, 0xB9, /* 0x5C-0x5F */
+ 0xC1, 0x46, 0xEE, 0xB8, 0xB0, 0xD5, 0xC1, 0x47, /* 0x60-0x63 */
+ 0xC1, 0x48, 0xC1, 0x49, 0xC1, 0x4A, 0xC1, 0x4B, /* 0x64-0x67 */
+ 0xEE, 0xBB, 0xD5, 0xD6, 0xD7, 0xEF, 0xC1, 0x4C, /* 0x68-0x6B */
+ 0xC1, 0x4D, 0xC1, 0x4E, 0xD6, 0xC3, 0xC1, 0x4F, /* 0x6C-0x6F */
+ 0xC1, 0x50, 0xEE, 0xBD, 0xCA, 0xF0, 0xC1, 0x51, /* 0x70-0x73 */
+ 0xEE, 0xBC, 0xC1, 0x52, 0xC1, 0x53, 0xC1, 0x54, /* 0x74-0x77 */
+ 0xC1, 0x55, 0xEE, 0xBE, 0xC1, 0x56, 0xC1, 0x57, /* 0x78-0x7B */
+ 0xC1, 0x58, 0xC1, 0x59, 0xEE, 0xC0, 0xC1, 0x5A, /* 0x7C-0x7F */
+
+ 0xC1, 0x5B, 0xEE, 0xBF, 0xC1, 0x5C, 0xC1, 0x5D, /* 0x80-0x83 */
+ 0xC1, 0x5E, 0xC1, 0x5F, 0xC1, 0x60, 0xC1, 0x61, /* 0x84-0x87 */
+ 0xC1, 0x62, 0xC1, 0x63, 0xD1, 0xF2, 0xC1, 0x64, /* 0x88-0x8B */
+ 0xC7, 0xBC, 0xC1, 0x65, 0xC3, 0xC0, 0xC1, 0x66, /* 0x8C-0x8F */
+ 0xC1, 0x67, 0xC1, 0x68, 0xC1, 0x69, 0xC1, 0x6A, /* 0x90-0x93 */
+ 0xB8, 0xE1, 0xC1, 0x6B, 0xC1, 0x6C, 0xC1, 0x6D, /* 0x94-0x97 */
+ 0xC1, 0x6E, 0xC1, 0x6F, 0xC1, 0xE7, 0xC1, 0x70, /* 0x98-0x9B */
+ 0xC1, 0x71, 0xF4, 0xC6, 0xD0, 0xDF, 0xF4, 0xC7, /* 0x9C-0x9F */
+ 0xC1, 0x72, 0xCF, 0xDB, 0xC1, 0x73, 0xC1, 0x74, /* 0xA0-0xA3 */
+ 0xC8, 0xBA, 0xC1, 0x75, 0xC1, 0x76, 0xF4, 0xC8, /* 0xA4-0xA7 */
+ 0xC1, 0x77, 0xC1, 0x78, 0xC1, 0x79, 0xC1, 0x7A, /* 0xA8-0xAB */
+ 0xC1, 0x7B, 0xC1, 0x7C, 0xC1, 0x7D, 0xF4, 0xC9, /* 0xAC-0xAF */
+ 0xF4, 0xCA, 0xC1, 0x7E, 0xF4, 0xCB, 0xC1, 0x80, /* 0xB0-0xB3 */
+ 0xC1, 0x81, 0xC1, 0x82, 0xC1, 0x83, 0xC1, 0x84, /* 0xB4-0xB7 */
+ 0xD9, 0xFA, 0xB8, 0xFE, 0xC1, 0x85, 0xC1, 0x86, /* 0xB8-0xBB */
+ 0xE5, 0xF1, 0xD3, 0xF0, 0xC1, 0x87, 0xF4, 0xE0, /* 0xBC-0xBF */
+ 0xC1, 0x88, 0xCE, 0xCC, 0xC1, 0x89, 0xC1, 0x8A, /* 0xC0-0xC3 */
+ 0xC1, 0x8B, 0xB3, 0xE1, 0xC1, 0x8C, 0xC1, 0x8D, /* 0xC4-0xC7 */
+ 0xC1, 0x8E, 0xC1, 0x8F, 0xF1, 0xB4, 0xC1, 0x90, /* 0xC8-0xCB */
+ 0xD2, 0xEE, 0xC1, 0x91, 0xF4, 0xE1, 0xC1, 0x92, /* 0xCC-0xCF */
+ 0xC1, 0x93, 0xC1, 0x94, 0xC1, 0x95, 0xC1, 0x96, /* 0xD0-0xD3 */
+ 0xCF, 0xE8, 0xF4, 0xE2, 0xC1, 0x97, 0xC1, 0x98, /* 0xD4-0xD7 */
+ 0xC7, 0xCC, 0xC1, 0x99, 0xC1, 0x9A, 0xC1, 0x9B, /* 0xD8-0xDB */
+ 0xC1, 0x9C, 0xC1, 0x9D, 0xC1, 0x9E, 0xB5, 0xD4, /* 0xDC-0xDF */
+ 0xB4, 0xE4, 0xF4, 0xE4, 0xC1, 0x9F, 0xC1, 0xA0, /* 0xE0-0xE3 */
+ 0xC2, 0x40, 0xF4, 0xE3, 0xF4, 0xE5, 0xC2, 0x41, /* 0xE4-0xE7 */
+ 0xC2, 0x42, 0xF4, 0xE6, 0xC2, 0x43, 0xC2, 0x44, /* 0xE8-0xEB */
+ 0xC2, 0x45, 0xC2, 0x46, 0xF4, 0xE7, 0xC2, 0x47, /* 0xEC-0xEF */
+ 0xBA, 0xB2, 0xB0, 0xBF, 0xC2, 0x48, 0xF4, 0xE8, /* 0xF0-0xF3 */
+ 0xC2, 0x49, 0xC2, 0x4A, 0xC2, 0x4B, 0xC2, 0x4C, /* 0xF4-0xF7 */
+ 0xC2, 0x4D, 0xC2, 0x4E, 0xC2, 0x4F, 0xB7, 0xAD, /* 0xF8-0xFB */
+ 0xD2, 0xED, 0xC2, 0x50, 0xC2, 0x51, 0xC2, 0x52, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_80[512] = {
+ 0xD2, 0xAB, 0xC0, 0xCF, 0xC2, 0x53, 0xBF, 0xBC, /* 0x00-0x03 */
+ 0xEB, 0xA3, 0xD5, 0xDF, 0xEA, 0xC8, 0xC2, 0x54, /* 0x04-0x07 */
+ 0xC2, 0x55, 0xC2, 0x56, 0xC2, 0x57, 0xF1, 0xF3, /* 0x08-0x0B */
+ 0xB6, 0xF8, 0xCB, 0xA3, 0xC2, 0x58, 0xC2, 0x59, /* 0x0C-0x0F */
+ 0xC4, 0xCD, 0xC2, 0x5A, 0xF1, 0xE7, 0xC2, 0x5B, /* 0x10-0x13 */
+ 0xF1, 0xE8, 0xB8, 0xFB, 0xF1, 0xE9, 0xBA, 0xC4, /* 0x14-0x17 */
+ 0xD4, 0xC5, 0xB0, 0xD2, 0xC2, 0x5C, 0xC2, 0x5D, /* 0x18-0x1B */
+ 0xF1, 0xEA, 0xC2, 0x5E, 0xC2, 0x5F, 0xC2, 0x60, /* 0x1C-0x1F */
+ 0xF1, 0xEB, 0xC2, 0x61, 0xF1, 0xEC, 0xC2, 0x62, /* 0x20-0x23 */
+ 0xC2, 0x63, 0xF1, 0xED, 0xF1, 0xEE, 0xF1, 0xEF, /* 0x24-0x27 */
+ 0xF1, 0xF1, 0xF1, 0xF0, 0xC5, 0xD5, 0xC2, 0x64, /* 0x28-0x2B */
+ 0xC2, 0x65, 0xC2, 0x66, 0xC2, 0x67, 0xC2, 0x68, /* 0x2C-0x2F */
+ 0xC2, 0x69, 0xF1, 0xF2, 0xC2, 0x6A, 0xB6, 0xFA, /* 0x30-0x33 */
+ 0xC2, 0x6B, 0xF1, 0xF4, 0xD2, 0xAE, 0xDE, 0xC7, /* 0x34-0x37 */
+ 0xCB, 0xCA, 0xC2, 0x6C, 0xC2, 0x6D, 0xB3, 0xDC, /* 0x38-0x3B */
+ 0xC2, 0x6E, 0xB5, 0xA2, 0xC2, 0x6F, 0xB9, 0xA2, /* 0x3C-0x3F */
+ 0xC2, 0x70, 0xC2, 0x71, 0xC4, 0xF4, 0xF1, 0xF5, /* 0x40-0x43 */
+ 0xC2, 0x72, 0xC2, 0x73, 0xF1, 0xF6, 0xC2, 0x74, /* 0x44-0x47 */
+ 0xC2, 0x75, 0xC2, 0x76, 0xC1, 0xC4, 0xC1, 0xFB, /* 0x48-0x4B */
+ 0xD6, 0xB0, 0xF1, 0xF7, 0xC2, 0x77, 0xC2, 0x78, /* 0x4C-0x4F */
+ 0xC2, 0x79, 0xC2, 0x7A, 0xF1, 0xF8, 0xC2, 0x7B, /* 0x50-0x53 */
+ 0xC1, 0xAA, 0xC2, 0x7C, 0xC2, 0x7D, 0xC2, 0x7E, /* 0x54-0x57 */
+ 0xC6, 0xB8, 0xC2, 0x80, 0xBE, 0xDB, 0xC2, 0x81, /* 0x58-0x5B */
+ 0xC2, 0x82, 0xC2, 0x83, 0xC2, 0x84, 0xC2, 0x85, /* 0x5C-0x5F */
+ 0xC2, 0x86, 0xC2, 0x87, 0xC2, 0x88, 0xC2, 0x89, /* 0x60-0x63 */
+ 0xC2, 0x8A, 0xC2, 0x8B, 0xC2, 0x8C, 0xC2, 0x8D, /* 0x64-0x67 */
+ 0xC2, 0x8E, 0xF1, 0xF9, 0xB4, 0xCF, 0xC2, 0x8F, /* 0x68-0x6B */
+ 0xC2, 0x90, 0xC2, 0x91, 0xC2, 0x92, 0xC2, 0x93, /* 0x6C-0x6F */
+ 0xC2, 0x94, 0xF1, 0xFA, 0xC2, 0x95, 0xC2, 0x96, /* 0x70-0x73 */
+ 0xC2, 0x97, 0xC2, 0x98, 0xC2, 0x99, 0xC2, 0x9A, /* 0x74-0x77 */
+ 0xC2, 0x9B, 0xC2, 0x9C, 0xC2, 0x9D, 0xC2, 0x9E, /* 0x78-0x7B */
+ 0xC2, 0x9F, 0xC2, 0xA0, 0xC3, 0x40, 0xED, 0xB2, /* 0x7C-0x7F */
+
+ 0xED, 0xB1, 0xC3, 0x41, 0xC3, 0x42, 0xCB, 0xE0, /* 0x80-0x83 */
+ 0xD2, 0xDE, 0xC3, 0x43, 0xCB, 0xC1, 0xD5, 0xD8, /* 0x84-0x87 */
+ 0xC3, 0x44, 0xC8, 0xE2, 0xC3, 0x45, 0xC0, 0xDF, /* 0x88-0x8B */
+ 0xBC, 0xA1, 0xC3, 0x46, 0xC3, 0x47, 0xC3, 0x48, /* 0x8C-0x8F */
+ 0xC3, 0x49, 0xC3, 0x4A, 0xC3, 0x4B, 0xEB, 0xC1, /* 0x90-0x93 */
+ 0xC3, 0x4C, 0xC3, 0x4D, 0xD0, 0xA4, 0xC3, 0x4E, /* 0x94-0x97 */
+ 0xD6, 0xE2, 0xC3, 0x4F, 0xB6, 0xC7, 0xB8, 0xD8, /* 0x98-0x9B */
+ 0xEB, 0xC0, 0xB8, 0xCE, 0xC3, 0x50, 0xEB, 0xBF, /* 0x9C-0x9F */
+ 0xB3, 0xA6, 0xB9, 0xC9, 0xD6, 0xAB, 0xC3, 0x51, /* 0xA0-0xA3 */
+ 0xB7, 0xF4, 0xB7, 0xCA, 0xC3, 0x52, 0xC3, 0x53, /* 0xA4-0xA7 */
+ 0xC3, 0x54, 0xBC, 0xE7, 0xB7, 0xBE, 0xEB, 0xC6, /* 0xA8-0xAB */
+ 0xC3, 0x55, 0xEB, 0xC7, 0xB0, 0xB9, 0xBF, 0xCF, /* 0xAC-0xAF */
+ 0xC3, 0x56, 0xEB, 0xC5, 0xD3, 0xFD, 0xC3, 0x57, /* 0xB0-0xB3 */
+ 0xEB, 0xC8, 0xC3, 0x58, 0xC3, 0x59, 0xEB, 0xC9, /* 0xB4-0xB7 */
+ 0xC3, 0x5A, 0xC3, 0x5B, 0xB7, 0xCE, 0xC3, 0x5C, /* 0xB8-0xBB */
+ 0xEB, 0xC2, 0xEB, 0xC4, 0xC9, 0xF6, 0xD6, 0xD7, /* 0xBC-0xBF */
+ 0xD5, 0xCD, 0xD0, 0xB2, 0xEB, 0xCF, 0xCE, 0xB8, /* 0xC0-0xC3 */
+ 0xEB, 0xD0, 0xC3, 0x5D, 0xB5, 0xA8, 0xC3, 0x5E, /* 0xC4-0xC7 */
+ 0xC3, 0x5F, 0xC3, 0x60, 0xC3, 0x61, 0xC3, 0x62, /* 0xC8-0xCB */
+ 0xB1, 0xB3, 0xEB, 0xD2, 0xCC, 0xA5, 0xC3, 0x63, /* 0xCC-0xCF */
+ 0xC3, 0x64, 0xC3, 0x65, 0xC3, 0x66, 0xC3, 0x67, /* 0xD0-0xD3 */
+ 0xC3, 0x68, 0xC3, 0x69, 0xC5, 0xD6, 0xEB, 0xD3, /* 0xD4-0xD7 */
+ 0xC3, 0x6A, 0xEB, 0xD1, 0xC5, 0xDF, 0xEB, 0xCE, /* 0xD8-0xDB */
+ 0xCA, 0xA4, 0xEB, 0xD5, 0xB0, 0xFB, 0xC3, 0x6B, /* 0xDC-0xDF */
+ 0xC3, 0x6C, 0xBA, 0xFA, 0xC3, 0x6D, 0xC3, 0x6E, /* 0xE0-0xE3 */
+ 0xD8, 0xB7, 0xF1, 0xE3, 0xC3, 0x6F, 0xEB, 0xCA, /* 0xE4-0xE7 */
+ 0xEB, 0xCB, 0xEB, 0xCC, 0xEB, 0xCD, 0xEB, 0xD6, /* 0xE8-0xEB */
+ 0xE6, 0xC0, 0xEB, 0xD9, 0xC3, 0x70, 0xBF, 0xE8, /* 0xEC-0xEF */
+ 0xD2, 0xC8, 0xEB, 0xD7, 0xEB, 0xDC, 0xB8, 0xEC, /* 0xF0-0xF3 */
+ 0xEB, 0xD8, 0xC3, 0x71, 0xBD, 0xBA, 0xC3, 0x72, /* 0xF4-0xF7 */
+ 0xD0, 0xD8, 0xC3, 0x73, 0xB0, 0xB7, 0xC3, 0x74, /* 0xF8-0xFB */
+ 0xEB, 0xDD, 0xC4, 0xDC, 0xC3, 0x75, 0xC3, 0x76, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_81[512] = {
+ 0xC3, 0x77, 0xC3, 0x78, 0xD6, 0xAC, 0xC3, 0x79, /* 0x00-0x03 */
+ 0xC3, 0x7A, 0xC3, 0x7B, 0xB4, 0xE0, 0xC3, 0x7C, /* 0x04-0x07 */
+ 0xC3, 0x7D, 0xC2, 0xF6, 0xBC, 0xB9, 0xC3, 0x7E, /* 0x08-0x0B */
+ 0xC3, 0x80, 0xEB, 0xDA, 0xEB, 0xDB, 0xD4, 0xE0, /* 0x0C-0x0F */
+ 0xC6, 0xEA, 0xC4, 0xD4, 0xEB, 0xDF, 0xC5, 0xA7, /* 0x10-0x13 */
+ 0xD9, 0xF5, 0xC3, 0x81, 0xB2, 0xB1, 0xC3, 0x82, /* 0x14-0x17 */
+ 0xEB, 0xE4, 0xC3, 0x83, 0xBD, 0xC5, 0xC3, 0x84, /* 0x18-0x1B */
+ 0xC3, 0x85, 0xC3, 0x86, 0xEB, 0xE2, 0xC3, 0x87, /* 0x1C-0x1F */
+ 0xC3, 0x88, 0xC3, 0x89, 0xC3, 0x8A, 0xC3, 0x8B, /* 0x20-0x23 */
+ 0xC3, 0x8C, 0xC3, 0x8D, 0xC3, 0x8E, 0xC3, 0x8F, /* 0x24-0x27 */
+ 0xC3, 0x90, 0xC3, 0x91, 0xC3, 0x92, 0xC3, 0x93, /* 0x28-0x2B */
+ 0xEB, 0xE3, 0xC3, 0x94, 0xC3, 0x95, 0xB8, 0xAC, /* 0x2C-0x2F */
+ 0xC3, 0x96, 0xCD, 0xD1, 0xEB, 0xE5, 0xC3, 0x97, /* 0x30-0x33 */
+ 0xC3, 0x98, 0xC3, 0x99, 0xEB, 0xE1, 0xC3, 0x9A, /* 0x34-0x37 */
+ 0xC1, 0xB3, 0xC3, 0x9B, 0xC3, 0x9C, 0xC3, 0x9D, /* 0x38-0x3B */
+ 0xC3, 0x9E, 0xC3, 0x9F, 0xC6, 0xA2, 0xC3, 0xA0, /* 0x3C-0x3F */
+ 0xC4, 0x40, 0xC4, 0x41, 0xC4, 0x42, 0xC4, 0x43, /* 0x40-0x43 */
+ 0xC4, 0x44, 0xC4, 0x45, 0xCC, 0xF3, 0xC4, 0x46, /* 0x44-0x47 */
+ 0xEB, 0xE6, 0xC4, 0x47, 0xC0, 0xB0, 0xD2, 0xB8, /* 0x48-0x4B */
+ 0xEB, 0xE7, 0xC4, 0x48, 0xC4, 0x49, 0xC4, 0x4A, /* 0x4C-0x4F */
+ 0xB8, 0xAF, 0xB8, 0xAD, 0xC4, 0x4B, 0xEB, 0xE8, /* 0x50-0x53 */
+ 0xC7, 0xBB, 0xCD, 0xF3, 0xC4, 0x4C, 0xC4, 0x4D, /* 0x54-0x57 */
+ 0xC4, 0x4E, 0xEB, 0xEA, 0xEB, 0xEB, 0xC4, 0x4F, /* 0x58-0x5B */
+ 0xC4, 0x50, 0xC4, 0x51, 0xC4, 0x52, 0xC4, 0x53, /* 0x5C-0x5F */
+ 0xEB, 0xED, 0xC4, 0x54, 0xC4, 0x55, 0xC4, 0x56, /* 0x60-0x63 */
+ 0xC4, 0x57, 0xD0, 0xC8, 0xC4, 0x58, 0xEB, 0xF2, /* 0x64-0x67 */
+ 0xC4, 0x59, 0xEB, 0xEE, 0xC4, 0x5A, 0xC4, 0x5B, /* 0x68-0x6B */
+ 0xC4, 0x5C, 0xEB, 0xF1, 0xC8, 0xF9, 0xC4, 0x5D, /* 0x6C-0x6F */
+ 0xD1, 0xFC, 0xEB, 0xEC, 0xC4, 0x5E, 0xC4, 0x5F, /* 0x70-0x73 */
+ 0xEB, 0xE9, 0xC4, 0x60, 0xC4, 0x61, 0xC4, 0x62, /* 0x74-0x77 */
+ 0xC4, 0x63, 0xB8, 0xB9, 0xCF, 0xD9, 0xC4, 0xE5, /* 0x78-0x7B */
+ 0xEB, 0xEF, 0xEB, 0xF0, 0xCC, 0xDA, 0xCD, 0xC8, /* 0x7C-0x7F */
+
+ 0xB0, 0xF2, 0xC4, 0x64, 0xEB, 0xF6, 0xC4, 0x65, /* 0x80-0x83 */
+ 0xC4, 0x66, 0xC4, 0x67, 0xC4, 0x68, 0xC4, 0x69, /* 0x84-0x87 */
+ 0xEB, 0xF5, 0xC4, 0x6A, 0xB2, 0xB2, 0xC4, 0x6B, /* 0x88-0x8B */
+ 0xC4, 0x6C, 0xC4, 0x6D, 0xC4, 0x6E, 0xB8, 0xE0, /* 0x8C-0x8F */
+ 0xC4, 0x6F, 0xEB, 0xF7, 0xC4, 0x70, 0xC4, 0x71, /* 0x90-0x93 */
+ 0xC4, 0x72, 0xC4, 0x73, 0xC4, 0x74, 0xC4, 0x75, /* 0x94-0x97 */
+ 0xB1, 0xEC, 0xC4, 0x76, 0xC4, 0x77, 0xCC, 0xC5, /* 0x98-0x9B */
+ 0xC4, 0xA4, 0xCF, 0xA5, 0xC4, 0x78, 0xC4, 0x79, /* 0x9C-0x9F */
+ 0xC4, 0x7A, 0xC4, 0x7B, 0xC4, 0x7C, 0xEB, 0xF9, /* 0xA0-0xA3 */
+ 0xC4, 0x7D, 0xC4, 0x7E, 0xEC, 0xA2, 0xC4, 0x80, /* 0xA4-0xA7 */
+ 0xC5, 0xF2, 0xC4, 0x81, 0xEB, 0xFA, 0xC4, 0x82, /* 0xA8-0xAB */
+ 0xC4, 0x83, 0xC4, 0x84, 0xC4, 0x85, 0xC4, 0x86, /* 0xAC-0xAF */
+ 0xC4, 0x87, 0xC4, 0x88, 0xC4, 0x89, 0xC9, 0xC5, /* 0xB0-0xB3 */
+ 0xC4, 0x8A, 0xC4, 0x8B, 0xC4, 0x8C, 0xC4, 0x8D, /* 0xB4-0xB7 */
+ 0xC4, 0x8E, 0xC4, 0x8F, 0xE2, 0xDF, 0xEB, 0xFE, /* 0xB8-0xBB */
+ 0xC4, 0x90, 0xC4, 0x91, 0xC4, 0x92, 0xC4, 0x93, /* 0xBC-0xBF */
+ 0xCD, 0xCE, 0xEC, 0xA1, 0xB1, 0xDB, 0xD3, 0xB7, /* 0xC0-0xC3 */
+ 0xC4, 0x94, 0xC4, 0x95, 0xD2, 0xDC, 0xC4, 0x96, /* 0xC4-0xC7 */
+ 0xC4, 0x97, 0xC4, 0x98, 0xEB, 0xFD, 0xC4, 0x99, /* 0xC8-0xCB */
+ 0xEB, 0xFB, 0xC4, 0x9A, 0xC4, 0x9B, 0xC4, 0x9C, /* 0xCC-0xCF */
+ 0xC4, 0x9D, 0xC4, 0x9E, 0xC4, 0x9F, 0xC4, 0xA0, /* 0xD0-0xD3 */
+ 0xC5, 0x40, 0xC5, 0x41, 0xC5, 0x42, 0xC5, 0x43, /* 0xD4-0xD7 */
+ 0xC5, 0x44, 0xC5, 0x45, 0xC5, 0x46, 0xC5, 0x47, /* 0xD8-0xDB */
+ 0xC5, 0x48, 0xC5, 0x49, 0xC5, 0x4A, 0xC5, 0x4B, /* 0xDC-0xDF */
+ 0xC5, 0x4C, 0xC5, 0x4D, 0xC5, 0x4E, 0xB3, 0xBC, /* 0xE0-0xE3 */
+ 0xC5, 0x4F, 0xC5, 0x50, 0xC5, 0x51, 0xEA, 0xB0, /* 0xE4-0xE7 */
+ 0xC5, 0x52, 0xC5, 0x53, 0xD7, 0xD4, 0xC5, 0x54, /* 0xE8-0xEB */
+ 0xF4, 0xAB, 0xB3, 0xF4, 0xC5, 0x55, 0xC5, 0x56, /* 0xEC-0xEF */
+ 0xC5, 0x57, 0xC5, 0x58, 0xC5, 0x59, 0xD6, 0xC1, /* 0xF0-0xF3 */
+ 0xD6, 0xC2, 0xC5, 0x5A, 0xC5, 0x5B, 0xC5, 0x5C, /* 0xF4-0xF7 */
+ 0xC5, 0x5D, 0xC5, 0x5E, 0xC5, 0x5F, 0xD5, 0xE9, /* 0xF8-0xFB */
+ 0xBE, 0xCA, 0xC5, 0x60, 0xF4, 0xA7, 0xC5, 0x61, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_82[512] = {
+ 0xD2, 0xA8, 0xF4, 0xA8, 0xF4, 0xA9, 0xC5, 0x62, /* 0x00-0x03 */
+ 0xF4, 0xAA, 0xBE, 0xCB, 0xD3, 0xDF, 0xC5, 0x63, /* 0x04-0x07 */
+ 0xC5, 0x64, 0xC5, 0x65, 0xC5, 0x66, 0xC5, 0x67, /* 0x08-0x0B */
+ 0xC9, 0xE0, 0xC9, 0xE1, 0xC5, 0x68, 0xC5, 0x69, /* 0x0C-0x0F */
+ 0xF3, 0xC2, 0xC5, 0x6A, 0xCA, 0xE6, 0xC5, 0x6B, /* 0x10-0x13 */
+ 0xCC, 0xF2, 0xC5, 0x6C, 0xC5, 0x6D, 0xC5, 0x6E, /* 0x14-0x17 */
+ 0xC5, 0x6F, 0xC5, 0x70, 0xC5, 0x71, 0xE2, 0xB6, /* 0x18-0x1B */
+ 0xCB, 0xB4, 0xC5, 0x72, 0xCE, 0xE8, 0xD6, 0xDB, /* 0x1C-0x1F */
+ 0xC5, 0x73, 0xF4, 0xAD, 0xF4, 0xAE, 0xF4, 0xAF, /* 0x20-0x23 */
+ 0xC5, 0x74, 0xC5, 0x75, 0xC5, 0x76, 0xC5, 0x77, /* 0x24-0x27 */
+ 0xF4, 0xB2, 0xC5, 0x78, 0xBA, 0xBD, 0xF4, 0xB3, /* 0x28-0x2B */
+ 0xB0, 0xE3, 0xF4, 0xB0, 0xC5, 0x79, 0xF4, 0xB1, /* 0x2C-0x2F */
+ 0xBD, 0xA2, 0xB2, 0xD5, 0xC5, 0x7A, 0xF4, 0xB6, /* 0x30-0x33 */
+ 0xF4, 0xB7, 0xB6, 0xE6, 0xB2, 0xB0, 0xCF, 0xCF, /* 0x34-0x37 */
+ 0xF4, 0xB4, 0xB4, 0xAC, 0xC5, 0x7B, 0xF4, 0xB5, /* 0x38-0x3B */
+ 0xC5, 0x7C, 0xC5, 0x7D, 0xF4, 0xB8, 0xC5, 0x7E, /* 0x3C-0x3F */
+ 0xC5, 0x80, 0xC5, 0x81, 0xC5, 0x82, 0xC5, 0x83, /* 0x40-0x43 */
+ 0xF4, 0xB9, 0xC5, 0x84, 0xC5, 0x85, 0xCD, 0xA7, /* 0x44-0x47 */
+ 0xC5, 0x86, 0xF4, 0xBA, 0xC5, 0x87, 0xF4, 0xBB, /* 0x48-0x4B */
+ 0xC5, 0x88, 0xC5, 0x89, 0xC5, 0x8A, 0xF4, 0xBC, /* 0x4C-0x4F */
+ 0xC5, 0x8B, 0xC5, 0x8C, 0xC5, 0x8D, 0xC5, 0x8E, /* 0x50-0x53 */
+ 0xC5, 0x8F, 0xC5, 0x90, 0xC5, 0x91, 0xC5, 0x92, /* 0x54-0x57 */
+ 0xCB, 0xD2, 0xC5, 0x93, 0xF4, 0xBD, 0xC5, 0x94, /* 0x58-0x5B */
+ 0xC5, 0x95, 0xC5, 0x96, 0xC5, 0x97, 0xF4, 0xBE, /* 0x5C-0x5F */
+ 0xC5, 0x98, 0xC5, 0x99, 0xC5, 0x9A, 0xC5, 0x9B, /* 0x60-0x63 */
+ 0xC5, 0x9C, 0xC5, 0x9D, 0xC5, 0x9E, 0xC5, 0x9F, /* 0x64-0x67 */
+ 0xF4, 0xBF, 0xC5, 0xA0, 0xC6, 0x40, 0xC6, 0x41, /* 0x68-0x6B */
+ 0xC6, 0x42, 0xC6, 0x43, 0xF4, 0xDE, 0xC1, 0xBC, /* 0x6C-0x6F */
+ 0xBC, 0xE8, 0xC6, 0x44, 0xC9, 0xAB, 0xD1, 0xDE, /* 0x70-0x73 */
+ 0xE5, 0xF5, 0xC6, 0x45, 0xC6, 0x46, 0xC6, 0x47, /* 0x74-0x77 */
+ 0xC6, 0x48, 0xDC, 0xB3, 0xD2, 0xD5, 0xC6, 0x49, /* 0x78-0x7B */
+ 0xC6, 0x4A, 0xDC, 0xB4, 0xB0, 0xAC, 0xDC, 0xB5, /* 0x7C-0x7F */
+
+ 0xC6, 0x4B, 0xC6, 0x4C, 0xBD, 0xDA, 0xC6, 0x4D, /* 0x80-0x83 */
+ 0xDC, 0xB9, 0xC6, 0x4E, 0xC6, 0x4F, 0xC6, 0x50, /* 0x84-0x87 */
+ 0xD8, 0xC2, 0xC6, 0x51, 0xDC, 0xB7, 0xD3, 0xF3, /* 0x88-0x8B */
+ 0xC6, 0x52, 0xC9, 0xD6, 0xDC, 0xBA, 0xDC, 0xB6, /* 0x8C-0x8F */
+ 0xC6, 0x53, 0xDC, 0xBB, 0xC3, 0xA2, 0xC6, 0x54, /* 0x90-0x93 */
+ 0xC6, 0x55, 0xC6, 0x56, 0xC6, 0x57, 0xDC, 0xBC, /* 0x94-0x97 */
+ 0xDC, 0xC5, 0xDC, 0xBD, 0xC6, 0x58, 0xC6, 0x59, /* 0x98-0x9B */
+ 0xCE, 0xDF, 0xD6, 0xA5, 0xC6, 0x5A, 0xDC, 0xCF, /* 0x9C-0x9F */
+ 0xC6, 0x5B, 0xDC, 0xCD, 0xC6, 0x5C, 0xC6, 0x5D, /* 0xA0-0xA3 */
+ 0xDC, 0xD2, 0xBD, 0xE6, 0xC2, 0xAB, 0xC6, 0x5E, /* 0xA4-0xA7 */
+ 0xDC, 0xB8, 0xDC, 0xCB, 0xDC, 0xCE, 0xDC, 0xBE, /* 0xA8-0xAB */
+ 0xB7, 0xD2, 0xB0, 0xC5, 0xDC, 0xC7, 0xD0, 0xBE, /* 0xAC-0xAF */
+ 0xDC, 0xC1, 0xBB, 0xA8, 0xC6, 0x5F, 0xB7, 0xBC, /* 0xB0-0xB3 */
+ 0xDC, 0xCC, 0xC6, 0x60, 0xC6, 0x61, 0xDC, 0xC6, /* 0xB4-0xB7 */
+ 0xDC, 0xBF, 0xC7, 0xDB, 0xC6, 0x62, 0xC6, 0x63, /* 0xB8-0xBB */
+ 0xC6, 0x64, 0xD1, 0xBF, 0xDC, 0xC0, 0xC6, 0x65, /* 0xBC-0xBF */
+ 0xC6, 0x66, 0xDC, 0xCA, 0xC6, 0x67, 0xC6, 0x68, /* 0xC0-0xC3 */
+ 0xDC, 0xD0, 0xC6, 0x69, 0xC6, 0x6A, 0xCE, 0xAD, /* 0xC4-0xC7 */
+ 0xDC, 0xC2, 0xC6, 0x6B, 0xDC, 0xC3, 0xDC, 0xC8, /* 0xC8-0xCB */
+ 0xDC, 0xC9, 0xB2, 0xD4, 0xDC, 0xD1, 0xCB, 0xD5, /* 0xCC-0xCF */
+ 0xC6, 0x6C, 0xD4, 0xB7, 0xDC, 0xDB, 0xDC, 0xDF, /* 0xD0-0xD3 */
+ 0xCC, 0xA6, 0xDC, 0xE6, 0xC6, 0x6D, 0xC3, 0xE7, /* 0xD4-0xD7 */
+ 0xDC, 0xDC, 0xC6, 0x6E, 0xC6, 0x6F, 0xBF, 0xC1, /* 0xD8-0xDB */
+ 0xDC, 0xD9, 0xC6, 0x70, 0xB0, 0xFA, 0xB9, 0xB6, /* 0xDC-0xDF */
+ 0xDC, 0xE5, 0xDC, 0xD3, 0xC6, 0x71, 0xDC, 0xC4, /* 0xE0-0xE3 */
+ 0xDC, 0xD6, 0xC8, 0xF4, 0xBF, 0xE0, 0xC6, 0x72, /* 0xE4-0xE7 */
+ 0xC6, 0x73, 0xC6, 0x74, 0xC6, 0x75, 0xC9, 0xBB, /* 0xE8-0xEB */
+ 0xC6, 0x76, 0xC6, 0x77, 0xC6, 0x78, 0xB1, 0xBD, /* 0xEC-0xEF */
+ 0xC6, 0x79, 0xD3, 0xA2, 0xC6, 0x7A, 0xC6, 0x7B, /* 0xF0-0xF3 */
+ 0xDC, 0xDA, 0xC6, 0x7C, 0xC6, 0x7D, 0xDC, 0xD5, /* 0xF4-0xF7 */
+ 0xC6, 0x7E, 0xC6, 0xBB, 0xC6, 0x80, 0xDC, 0xDE, /* 0xF8-0xFB */
+ 0xC6, 0x81, 0xC6, 0x82, 0xC6, 0x83, 0xC6, 0x84, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_83[512] = {
+ 0xC6, 0x85, 0xD7, 0xC2, 0xC3, 0xAF, 0xB7, 0xB6, /* 0x00-0x03 */
+ 0xC7, 0xD1, 0xC3, 0xA9, 0xDC, 0xE2, 0xDC, 0xD8, /* 0x04-0x07 */
+ 0xDC, 0xEB, 0xDC, 0xD4, 0xC6, 0x86, 0xC6, 0x87, /* 0x08-0x0B */
+ 0xDC, 0xDD, 0xC6, 0x88, 0xBE, 0xA5, 0xDC, 0xD7, /* 0x0C-0x0F */
+ 0xC6, 0x89, 0xDC, 0xE0, 0xC6, 0x8A, 0xC6, 0x8B, /* 0x10-0x13 */
+ 0xDC, 0xE3, 0xDC, 0xE4, 0xC6, 0x8C, 0xDC, 0xF8, /* 0x14-0x17 */
+ 0xC6, 0x8D, 0xC6, 0x8E, 0xDC, 0xE1, 0xDD, 0xA2, /* 0x18-0x1B */
+ 0xDC, 0xE7, 0xC6, 0x8F, 0xC6, 0x90, 0xC6, 0x91, /* 0x1C-0x1F */
+ 0xC6, 0x92, 0xC6, 0x93, 0xC6, 0x94, 0xC6, 0x95, /* 0x20-0x23 */
+ 0xC6, 0x96, 0xC6, 0x97, 0xC6, 0x98, 0xBC, 0xEB, /* 0x24-0x27 */
+ 0xB4, 0xC4, 0xC6, 0x99, 0xC6, 0x9A, 0xC3, 0xA3, /* 0x28-0x2B */
+ 0xB2, 0xE7, 0xDC, 0xFA, 0xC6, 0x9B, 0xDC, 0xF2, /* 0x2C-0x2F */
+ 0xC6, 0x9C, 0xDC, 0xEF, 0xC6, 0x9D, 0xDC, 0xFC, /* 0x30-0x33 */
+ 0xDC, 0xEE, 0xD2, 0xF0, 0xB2, 0xE8, 0xC6, 0x9E, /* 0x34-0x37 */
+ 0xC8, 0xD7, 0xC8, 0xE3, 0xDC, 0xFB, 0xC6, 0x9F, /* 0x38-0x3B */
+ 0xDC, 0xED, 0xC6, 0xA0, 0xC7, 0x40, 0xC7, 0x41, /* 0x3C-0x3F */
+ 0xDC, 0xF7, 0xC7, 0x42, 0xC7, 0x43, 0xDC, 0xF5, /* 0x40-0x43 */
+ 0xC7, 0x44, 0xC7, 0x45, 0xBE, 0xA3, 0xDC, 0xF4, /* 0x44-0x47 */
+ 0xC7, 0x46, 0xB2, 0xDD, 0xC7, 0x47, 0xC7, 0x48, /* 0x48-0x4B */
+ 0xC7, 0x49, 0xC7, 0x4A, 0xC7, 0x4B, 0xDC, 0xF3, /* 0x4C-0x4F */
+ 0xBC, 0xF6, 0xDC, 0xE8, 0xBB, 0xC4, 0xC7, 0x4C, /* 0x50-0x53 */
+ 0xC0, 0xF3, 0xC7, 0x4D, 0xC7, 0x4E, 0xC7, 0x4F, /* 0x54-0x57 */
+ 0xC7, 0x50, 0xC7, 0x51, 0xBC, 0xD4, 0xDC, 0xE9, /* 0x58-0x5B */
+ 0xDC, 0xEA, 0xC7, 0x52, 0xDC, 0xF1, 0xDC, 0xF6, /* 0x5C-0x5F */
+ 0xDC, 0xF9, 0xB5, 0xB4, 0xC7, 0x53, 0xC8, 0xD9, /* 0x60-0x63 */
+ 0xBB, 0xE7, 0xDC, 0xFE, 0xDC, 0xFD, 0xD3, 0xAB, /* 0x64-0x67 */
+ 0xDD, 0xA1, 0xDD, 0xA3, 0xDD, 0xA5, 0xD2, 0xF1, /* 0x68-0x6B */
+ 0xDD, 0xA4, 0xDD, 0xA6, 0xDD, 0xA7, 0xD2, 0xA9, /* 0x6C-0x6F */
+ 0xC7, 0x54, 0xC7, 0x55, 0xC7, 0x56, 0xC7, 0x57, /* 0x70-0x73 */
+ 0xC7, 0x58, 0xC7, 0x59, 0xC7, 0x5A, 0xBA, 0xC9, /* 0x74-0x77 */
+ 0xDD, 0xA9, 0xC7, 0x5B, 0xC7, 0x5C, 0xDD, 0xB6, /* 0x78-0x7B */
+ 0xDD, 0xB1, 0xDD, 0xB4, 0xC7, 0x5D, 0xC7, 0x5E, /* 0x7C-0x7F */
+
+ 0xC7, 0x5F, 0xC7, 0x60, 0xC7, 0x61, 0xC7, 0x62, /* 0x80-0x83 */
+ 0xC7, 0x63, 0xDD, 0xB0, 0xC6, 0xCE, 0xC7, 0x64, /* 0x84-0x87 */
+ 0xC7, 0x65, 0xC0, 0xF2, 0xC7, 0x66, 0xC7, 0x67, /* 0x88-0x8B */
+ 0xC7, 0x68, 0xC7, 0x69, 0xC9, 0xAF, 0xC7, 0x6A, /* 0x8C-0x8F */
+ 0xC7, 0x6B, 0xC7, 0x6C, 0xDC, 0xEC, 0xDD, 0xAE, /* 0x90-0x93 */
+ 0xC7, 0x6D, 0xC7, 0x6E, 0xC7, 0x6F, 0xC7, 0x70, /* 0x94-0x97 */
+ 0xDD, 0xB7, 0xC7, 0x71, 0xC7, 0x72, 0xDC, 0xF0, /* 0x98-0x9B */
+ 0xDD, 0xAF, 0xC7, 0x73, 0xDD, 0xB8, 0xC7, 0x74, /* 0x9C-0x9F */
+ 0xDD, 0xAC, 0xC7, 0x75, 0xC7, 0x76, 0xC7, 0x77, /* 0xA0-0xA3 */
+ 0xC7, 0x78, 0xC7, 0x79, 0xC7, 0x7A, 0xC7, 0x7B, /* 0xA4-0xA7 */
+ 0xDD, 0xB9, 0xDD, 0xB3, 0xDD, 0xAD, 0xC4, 0xAA, /* 0xA8-0xAB */
+ 0xC7, 0x7C, 0xC7, 0x7D, 0xC7, 0x7E, 0xC7, 0x80, /* 0xAC-0xAF */
+ 0xDD, 0xA8, 0xC0, 0xB3, 0xC1, 0xAB, 0xDD, 0xAA, /* 0xB0-0xB3 */
+ 0xDD, 0xAB, 0xC7, 0x81, 0xDD, 0xB2, 0xBB, 0xF1, /* 0xB4-0xB7 */
+ 0xDD, 0xB5, 0xD3, 0xA8, 0xDD, 0xBA, 0xC7, 0x82, /* 0xB8-0xBB */
+ 0xDD, 0xBB, 0xC3, 0xA7, 0xC7, 0x83, 0xC7, 0x84, /* 0xBC-0xBF */
+ 0xDD, 0xD2, 0xDD, 0xBC, 0xC7, 0x85, 0xC7, 0x86, /* 0xC0-0xC3 */
+ 0xC7, 0x87, 0xDD, 0xD1, 0xC7, 0x88, 0xB9, 0xBD, /* 0xC4-0xC7 */
+ 0xC7, 0x89, 0xC7, 0x8A, 0xBE, 0xD5, 0xC7, 0x8B, /* 0xC8-0xCB */
+ 0xBE, 0xFA, 0xC7, 0x8C, 0xC7, 0x8D, 0xBA, 0xCA, /* 0xCC-0xCF */
+ 0xC7, 0x8E, 0xC7, 0x8F, 0xC7, 0x90, 0xC7, 0x91, /* 0xD0-0xD3 */
+ 0xDD, 0xCA, 0xC7, 0x92, 0xDD, 0xC5, 0xC7, 0x93, /* 0xD4-0xD7 */
+ 0xDD, 0xBF, 0xC7, 0x94, 0xC7, 0x95, 0xC7, 0x96, /* 0xD8-0xDB */
+ 0xB2, 0xCB, 0xDD, 0xC3, 0xC7, 0x97, 0xDD, 0xCB, /* 0xDC-0xDF */
+ 0xB2, 0xA4, 0xDD, 0xD5, 0xC7, 0x98, 0xC7, 0x99, /* 0xE0-0xE3 */
+ 0xC7, 0x9A, 0xDD, 0xBE, 0xC7, 0x9B, 0xC7, 0x9C, /* 0xE4-0xE7 */
+ 0xC7, 0x9D, 0xC6, 0xD0, 0xDD, 0xD0, 0xC7, 0x9E, /* 0xE8-0xEB */
+ 0xC7, 0x9F, 0xC7, 0xA0, 0xC8, 0x40, 0xC8, 0x41, /* 0xEC-0xEF */
+ 0xDD, 0xD4, 0xC1, 0xE2, 0xB7, 0xC6, 0xC8, 0x42, /* 0xF0-0xF3 */
+ 0xC8, 0x43, 0xC8, 0x44, 0xC8, 0x45, 0xC8, 0x46, /* 0xF4-0xF7 */
+ 0xDD, 0xCE, 0xDD, 0xCF, 0xC8, 0x47, 0xC8, 0x48, /* 0xF8-0xFB */
+ 0xC8, 0x49, 0xDD, 0xC4, 0xC8, 0x4A, 0xC8, 0x4B, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_84[512] = {
+ 0xC8, 0x4C, 0xDD, 0xBD, 0xC8, 0x4D, 0xDD, 0xCD, /* 0x00-0x03 */
+ 0xCC, 0xD1, 0xC8, 0x4E, 0xDD, 0xC9, 0xC8, 0x4F, /* 0x04-0x07 */
+ 0xC8, 0x50, 0xC8, 0x51, 0xC8, 0x52, 0xDD, 0xC2, /* 0x08-0x0B */
+ 0xC3, 0xC8, 0xC6, 0xBC, 0xCE, 0xAE, 0xDD, 0xCC, /* 0x0C-0x0F */
+ 0xC8, 0x53, 0xDD, 0xC8, 0xC8, 0x54, 0xC8, 0x55, /* 0x10-0x13 */
+ 0xC8, 0x56, 0xC8, 0x57, 0xC8, 0x58, 0xC8, 0x59, /* 0x14-0x17 */
+ 0xDD, 0xC1, 0xC8, 0x5A, 0xC8, 0x5B, 0xC8, 0x5C, /* 0x18-0x1B */
+ 0xDD, 0xC6, 0xC2, 0xDC, 0xC8, 0x5D, 0xC8, 0x5E, /* 0x1C-0x1F */
+ 0xC8, 0x5F, 0xC8, 0x60, 0xC8, 0x61, 0xC8, 0x62, /* 0x20-0x23 */
+ 0xD3, 0xA9, 0xD3, 0xAA, 0xDD, 0xD3, 0xCF, 0xF4, /* 0x24-0x27 */
+ 0xC8, 0xF8, 0xC8, 0x63, 0xC8, 0x64, 0xC8, 0x65, /* 0x28-0x2B */
+ 0xC8, 0x66, 0xC8, 0x67, 0xC8, 0x68, 0xC8, 0x69, /* 0x2C-0x2F */
+ 0xC8, 0x6A, 0xDD, 0xE6, 0xC8, 0x6B, 0xC8, 0x6C, /* 0x30-0x33 */
+ 0xC8, 0x6D, 0xC8, 0x6E, 0xC8, 0x6F, 0xC8, 0x70, /* 0x34-0x37 */
+ 0xDD, 0xC7, 0xC8, 0x71, 0xC8, 0x72, 0xC8, 0x73, /* 0x38-0x3B */
+ 0xDD, 0xE0, 0xC2, 0xE4, 0xC8, 0x74, 0xC8, 0x75, /* 0x3C-0x3F */
+ 0xC8, 0x76, 0xC8, 0x77, 0xC8, 0x78, 0xC8, 0x79, /* 0x40-0x43 */
+ 0xC8, 0x7A, 0xC8, 0x7B, 0xDD, 0xE1, 0xC8, 0x7C, /* 0x44-0x47 */
+ 0xC8, 0x7D, 0xC8, 0x7E, 0xC8, 0x80, 0xC8, 0x81, /* 0x48-0x4B */
+ 0xC8, 0x82, 0xC8, 0x83, 0xC8, 0x84, 0xC8, 0x85, /* 0x4C-0x4F */
+ 0xC8, 0x86, 0xDD, 0xD7, 0xC8, 0x87, 0xC8, 0x88, /* 0x50-0x53 */
+ 0xC8, 0x89, 0xC8, 0x8A, 0xC8, 0x8B, 0xD6, 0xF8, /* 0x54-0x57 */
+ 0xC8, 0x8C, 0xDD, 0xD9, 0xDD, 0xD8, 0xB8, 0xF0, /* 0x58-0x5B */
+ 0xDD, 0xD6, 0xC8, 0x8D, 0xC8, 0x8E, 0xC8, 0x8F, /* 0x5C-0x5F */
+ 0xC8, 0x90, 0xC6, 0xCF, 0xC8, 0x91, 0xB6, 0xAD, /* 0x60-0x63 */
+ 0xC8, 0x92, 0xC8, 0x93, 0xC8, 0x94, 0xC8, 0x95, /* 0x64-0x67 */
+ 0xC8, 0x96, 0xDD, 0xE2, 0xC8, 0x97, 0xBA, 0xF9, /* 0x68-0x6B */
+ 0xD4, 0xE1, 0xDD, 0xE7, 0xC8, 0x98, 0xC8, 0x99, /* 0x6C-0x6F */
+ 0xC8, 0x9A, 0xB4, 0xD0, 0xC8, 0x9B, 0xDD, 0xDA, /* 0x70-0x73 */
+ 0xC8, 0x9C, 0xBF, 0xFB, 0xDD, 0xE3, 0xC8, 0x9D, /* 0x74-0x77 */
+ 0xDD, 0xDF, 0xC8, 0x9E, 0xDD, 0xDD, 0xC8, 0x9F, /* 0x78-0x7B */
+ 0xC8, 0xA0, 0xC9, 0x40, 0xC9, 0x41, 0xC9, 0x42, /* 0x7C-0x7F */
+
+ 0xC9, 0x43, 0xC9, 0x44, 0xB5, 0xD9, 0xC9, 0x45, /* 0x80-0x83 */
+ 0xC9, 0x46, 0xC9, 0x47, 0xC9, 0x48, 0xDD, 0xDB, /* 0x84-0x87 */
+ 0xDD, 0xDC, 0xDD, 0xDE, 0xC9, 0x49, 0xBD, 0xAF, /* 0x88-0x8B */
+ 0xDD, 0xE4, 0xC9, 0x4A, 0xDD, 0xE5, 0xC9, 0x4B, /* 0x8C-0x8F */
+ 0xC9, 0x4C, 0xC9, 0x4D, 0xC9, 0x4E, 0xC9, 0x4F, /* 0x90-0x93 */
+ 0xC9, 0x50, 0xC9, 0x51, 0xC9, 0x52, 0xDD, 0xF5, /* 0x94-0x97 */
+ 0xC9, 0x53, 0xC3, 0xC9, 0xC9, 0x54, 0xC9, 0x55, /* 0x98-0x9B */
+ 0xCB, 0xE2, 0xC9, 0x56, 0xC9, 0x57, 0xC9, 0x58, /* 0x9C-0x9F */
+ 0xC9, 0x59, 0xDD, 0xF2, 0xC9, 0x5A, 0xC9, 0x5B, /* 0xA0-0xA3 */
+ 0xC9, 0x5C, 0xC9, 0x5D, 0xC9, 0x5E, 0xC9, 0x5F, /* 0xA4-0xA7 */
+ 0xC9, 0x60, 0xC9, 0x61, 0xC9, 0x62, 0xC9, 0x63, /* 0xA8-0xAB */
+ 0xC9, 0x64, 0xC9, 0x65, 0xC9, 0x66, 0xD8, 0xE1, /* 0xAC-0xAF */
+ 0xC9, 0x67, 0xC9, 0x68, 0xC6, 0xD1, 0xC9, 0x69, /* 0xB0-0xB3 */
+ 0xDD, 0xF4, 0xC9, 0x6A, 0xC9, 0x6B, 0xC9, 0x6C, /* 0xB4-0xB7 */
+ 0xD5, 0xF4, 0xDD, 0xF3, 0xDD, 0xF0, 0xC9, 0x6D, /* 0xB8-0xBB */
+ 0xC9, 0x6E, 0xDD, 0xEC, 0xC9, 0x6F, 0xDD, 0xEF, /* 0xBC-0xBF */
+ 0xC9, 0x70, 0xDD, 0xE8, 0xC9, 0x71, 0xC9, 0x72, /* 0xC0-0xC3 */
+ 0xD0, 0xEE, 0xC9, 0x73, 0xC9, 0x74, 0xC9, 0x75, /* 0xC4-0xC7 */
+ 0xC9, 0x76, 0xC8, 0xD8, 0xDD, 0xEE, 0xC9, 0x77, /* 0xC8-0xCB */
+ 0xC9, 0x78, 0xDD, 0xE9, 0xC9, 0x79, 0xC9, 0x7A, /* 0xCC-0xCF */
+ 0xDD, 0xEA, 0xCB, 0xF2, 0xC9, 0x7B, 0xDD, 0xED, /* 0xD0-0xD3 */
+ 0xC9, 0x7C, 0xC9, 0x7D, 0xB1, 0xCD, 0xC9, 0x7E, /* 0xD4-0xD7 */
+ 0xC9, 0x80, 0xC9, 0x81, 0xC9, 0x82, 0xC9, 0x83, /* 0xD8-0xDB */
+ 0xC9, 0x84, 0xC0, 0xB6, 0xC9, 0x85, 0xBC, 0xBB, /* 0xDC-0xDF */
+ 0xDD, 0xF1, 0xC9, 0x86, 0xC9, 0x87, 0xDD, 0xF7, /* 0xE0-0xE3 */
+ 0xC9, 0x88, 0xDD, 0xF6, 0xDD, 0xEB, 0xC9, 0x89, /* 0xE4-0xE7 */
+ 0xC9, 0x8A, 0xC9, 0x8B, 0xC9, 0x8C, 0xC9, 0x8D, /* 0xE8-0xEB */
+ 0xC5, 0xEE, 0xC9, 0x8E, 0xC9, 0x8F, 0xC9, 0x90, /* 0xEC-0xEF */
+ 0xDD, 0xFB, 0xC9, 0x91, 0xC9, 0x92, 0xC9, 0x93, /* 0xF0-0xF3 */
+ 0xC9, 0x94, 0xC9, 0x95, 0xC9, 0x96, 0xC9, 0x97, /* 0xF4-0xF7 */
+ 0xC9, 0x98, 0xC9, 0x99, 0xC9, 0x9A, 0xC9, 0x9B, /* 0xF8-0xFB */
+ 0xDE, 0xA4, 0xC9, 0x9C, 0xC9, 0x9D, 0xDE, 0xA3, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_85[512] = {
+ 0xC9, 0x9E, 0xC9, 0x9F, 0xC9, 0xA0, 0xCA, 0x40, /* 0x00-0x03 */
+ 0xCA, 0x41, 0xCA, 0x42, 0xCA, 0x43, 0xCA, 0x44, /* 0x04-0x07 */
+ 0xCA, 0x45, 0xCA, 0x46, 0xCA, 0x47, 0xCA, 0x48, /* 0x08-0x0B */
+ 0xDD, 0xF8, 0xCA, 0x49, 0xCA, 0x4A, 0xCA, 0x4B, /* 0x0C-0x0F */
+ 0xCA, 0x4C, 0xC3, 0xEF, 0xCA, 0x4D, 0xC2, 0xFB, /* 0x10-0x13 */
+ 0xCA, 0x4E, 0xCA, 0x4F, 0xCA, 0x50, 0xD5, 0xE1, /* 0x14-0x17 */
+ 0xCA, 0x51, 0xCA, 0x52, 0xCE, 0xB5, 0xCA, 0x53, /* 0x18-0x1B */
+ 0xCA, 0x54, 0xCA, 0x55, 0xCA, 0x56, 0xDD, 0xFD, /* 0x1C-0x1F */
+ 0xCA, 0x57, 0xB2, 0xCC, 0xCA, 0x58, 0xCA, 0x59, /* 0x20-0x23 */
+ 0xCA, 0x5A, 0xCA, 0x5B, 0xCA, 0x5C, 0xCA, 0x5D, /* 0x24-0x27 */
+ 0xCA, 0x5E, 0xCA, 0x5F, 0xCA, 0x60, 0xC4, 0xE8, /* 0x28-0x2B */
+ 0xCA, 0xDF, 0xCA, 0x61, 0xCA, 0x62, 0xCA, 0x63, /* 0x2C-0x2F */
+ 0xCA, 0x64, 0xCA, 0x65, 0xCA, 0x66, 0xCA, 0x67, /* 0x30-0x33 */
+ 0xCA, 0x68, 0xCA, 0x69, 0xCA, 0x6A, 0xC7, 0xBE, /* 0x34-0x37 */
+ 0xDD, 0xFA, 0xDD, 0xFC, 0xDD, 0xFE, 0xDE, 0xA2, /* 0x38-0x3B */
+ 0xB0, 0xAA, 0xB1, 0xCE, 0xCA, 0x6B, 0xCA, 0x6C, /* 0x3C-0x3F */
+ 0xCA, 0x6D, 0xCA, 0x6E, 0xCA, 0x6F, 0xDE, 0xAC, /* 0x40-0x43 */
+ 0xCA, 0x70, 0xCA, 0x71, 0xCA, 0x72, 0xCA, 0x73, /* 0x44-0x47 */
+ 0xDE, 0xA6, 0xBD, 0xB6, 0xC8, 0xEF, 0xCA, 0x74, /* 0x48-0x4B */
+ 0xCA, 0x75, 0xCA, 0x76, 0xCA, 0x77, 0xCA, 0x78, /* 0x4C-0x4F */
+ 0xCA, 0x79, 0xCA, 0x7A, 0xCA, 0x7B, 0xCA, 0x7C, /* 0x50-0x53 */
+ 0xCA, 0x7D, 0xCA, 0x7E, 0xDE, 0xA1, 0xCA, 0x80, /* 0x54-0x57 */
+ 0xCA, 0x81, 0xDE, 0xA5, 0xCA, 0x82, 0xCA, 0x83, /* 0x58-0x5B */
+ 0xCA, 0x84, 0xCA, 0x85, 0xDE, 0xA9, 0xCA, 0x86, /* 0x5C-0x5F */
+ 0xCA, 0x87, 0xCA, 0x88, 0xCA, 0x89, 0xCA, 0x8A, /* 0x60-0x63 */
+ 0xDE, 0xA8, 0xCA, 0x8B, 0xCA, 0x8C, 0xCA, 0x8D, /* 0x64-0x67 */
+ 0xDE, 0xA7, 0xCA, 0x8E, 0xCA, 0x8F, 0xCA, 0x90, /* 0x68-0x6B */
+ 0xCA, 0x91, 0xCA, 0x92, 0xCA, 0x93, 0xCA, 0x94, /* 0x6C-0x6F */
+ 0xCA, 0x95, 0xCA, 0x96, 0xDE, 0xAD, 0xCA, 0x97, /* 0x70-0x73 */
+ 0xD4, 0xCC, 0xCA, 0x98, 0xCA, 0x99, 0xCA, 0x9A, /* 0x74-0x77 */
+ 0xCA, 0x9B, 0xDE, 0xB3, 0xDE, 0xAA, 0xDE, 0xAE, /* 0x78-0x7B */
+ 0xCA, 0x9C, 0xCA, 0x9D, 0xC0, 0xD9, 0xCA, 0x9E, /* 0x7C-0x7F */
+
+ 0xCA, 0x9F, 0xCA, 0xA0, 0xCB, 0x40, 0xCB, 0x41, /* 0x80-0x83 */
+ 0xB1, 0xA1, 0xDE, 0xB6, 0xCB, 0x42, 0xDE, 0xB1, /* 0x84-0x87 */
+ 0xCB, 0x43, 0xCB, 0x44, 0xCB, 0x45, 0xCB, 0x46, /* 0x88-0x8B */
+ 0xCB, 0x47, 0xCB, 0x48, 0xCB, 0x49, 0xDE, 0xB2, /* 0x8C-0x8F */
+ 0xCB, 0x4A, 0xCB, 0x4B, 0xCB, 0x4C, 0xCB, 0x4D, /* 0x90-0x93 */
+ 0xCB, 0x4E, 0xCB, 0x4F, 0xCB, 0x50, 0xCB, 0x51, /* 0x94-0x97 */
+ 0xCB, 0x52, 0xCB, 0x53, 0xCB, 0x54, 0xD1, 0xA6, /* 0x98-0x9B */
+ 0xDE, 0xB5, 0xCB, 0x55, 0xCB, 0x56, 0xCB, 0x57, /* 0x9C-0x9F */
+ 0xCB, 0x58, 0xCB, 0x59, 0xCB, 0x5A, 0xCB, 0x5B, /* 0xA0-0xA3 */
+ 0xDE, 0xAF, 0xCB, 0x5C, 0xCB, 0x5D, 0xCB, 0x5E, /* 0xA4-0xA7 */
+ 0xDE, 0xB0, 0xCB, 0x5F, 0xD0, 0xBD, 0xCB, 0x60, /* 0xA8-0xAB */
+ 0xCB, 0x61, 0xCB, 0x62, 0xDE, 0xB4, 0xCA, 0xED, /* 0xAC-0xAF */
+ 0xDE, 0xB9, 0xCB, 0x63, 0xCB, 0x64, 0xCB, 0x65, /* 0xB0-0xB3 */
+ 0xCB, 0x66, 0xCB, 0x67, 0xCB, 0x68, 0xDE, 0xB8, /* 0xB4-0xB7 */
+ 0xCB, 0x69, 0xDE, 0xB7, 0xCB, 0x6A, 0xCB, 0x6B, /* 0xB8-0xBB */
+ 0xCB, 0x6C, 0xCB, 0x6D, 0xCB, 0x6E, 0xCB, 0x6F, /* 0xBC-0xBF */
+ 0xCB, 0x70, 0xDE, 0xBB, 0xCB, 0x71, 0xCB, 0x72, /* 0xC0-0xC3 */
+ 0xCB, 0x73, 0xCB, 0x74, 0xCB, 0x75, 0xCB, 0x76, /* 0xC4-0xC7 */
+ 0xCB, 0x77, 0xBD, 0xE5, 0xCB, 0x78, 0xCB, 0x79, /* 0xC8-0xCB */
+ 0xCB, 0x7A, 0xCB, 0x7B, 0xCB, 0x7C, 0xB2, 0xD8, /* 0xCC-0xCF */
+ 0xC3, 0xEA, 0xCB, 0x7D, 0xCB, 0x7E, 0xDE, 0xBA, /* 0xD0-0xD3 */
+ 0xCB, 0x80, 0xC5, 0xBA, 0xCB, 0x81, 0xCB, 0x82, /* 0xD4-0xD7 */
+ 0xCB, 0x83, 0xCB, 0x84, 0xCB, 0x85, 0xCB, 0x86, /* 0xD8-0xDB */
+ 0xDE, 0xBC, 0xCB, 0x87, 0xCB, 0x88, 0xCB, 0x89, /* 0xDC-0xDF */
+ 0xCB, 0x8A, 0xCB, 0x8B, 0xCB, 0x8C, 0xCB, 0x8D, /* 0xE0-0xE3 */
+ 0xCC, 0xD9, 0xCB, 0x8E, 0xCB, 0x8F, 0xCB, 0x90, /* 0xE4-0xE7 */
+ 0xCB, 0x91, 0xB7, 0xAA, 0xCB, 0x92, 0xCB, 0x93, /* 0xE8-0xEB */
+ 0xCB, 0x94, 0xCB, 0x95, 0xCB, 0x96, 0xCB, 0x97, /* 0xEC-0xEF */
+ 0xCB, 0x98, 0xCB, 0x99, 0xCB, 0x9A, 0xCB, 0x9B, /* 0xF0-0xF3 */
+ 0xCB, 0x9C, 0xCB, 0x9D, 0xCB, 0x9E, 0xCB, 0x9F, /* 0xF4-0xF7 */
+ 0xCB, 0xA0, 0xCC, 0x40, 0xCC, 0x41, 0xD4, 0xE5, /* 0xF8-0xFB */
+ 0xCC, 0x42, 0xCC, 0x43, 0xCC, 0x44, 0xDE, 0xBD, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_86[512] = {
+ 0xCC, 0x45, 0xCC, 0x46, 0xCC, 0x47, 0xCC, 0x48, /* 0x00-0x03 */
+ 0xCC, 0x49, 0xDE, 0xBF, 0xCC, 0x4A, 0xCC, 0x4B, /* 0x04-0x07 */
+ 0xCC, 0x4C, 0xCC, 0x4D, 0xCC, 0x4E, 0xCC, 0x4F, /* 0x08-0x0B */
+ 0xCC, 0x50, 0xCC, 0x51, 0xCC, 0x52, 0xCC, 0x53, /* 0x0C-0x0F */
+ 0xCC, 0x54, 0xC4, 0xA2, 0xCC, 0x55, 0xCC, 0x56, /* 0x10-0x13 */
+ 0xCC, 0x57, 0xCC, 0x58, 0xDE, 0xC1, 0xCC, 0x59, /* 0x14-0x17 */
+ 0xCC, 0x5A, 0xCC, 0x5B, 0xCC, 0x5C, 0xCC, 0x5D, /* 0x18-0x1B */
+ 0xCC, 0x5E, 0xCC, 0x5F, 0xCC, 0x60, 0xCC, 0x61, /* 0x1C-0x1F */
+ 0xCC, 0x62, 0xCC, 0x63, 0xCC, 0x64, 0xCC, 0x65, /* 0x20-0x23 */
+ 0xCC, 0x66, 0xCC, 0x67, 0xCC, 0x68, 0xDE, 0xBE, /* 0x24-0x27 */
+ 0xCC, 0x69, 0xDE, 0xC0, 0xCC, 0x6A, 0xCC, 0x6B, /* 0x28-0x2B */
+ 0xCC, 0x6C, 0xCC, 0x6D, 0xCC, 0x6E, 0xCC, 0x6F, /* 0x2C-0x2F */
+ 0xCC, 0x70, 0xCC, 0x71, 0xCC, 0x72, 0xCC, 0x73, /* 0x30-0x33 */
+ 0xCC, 0x74, 0xCC, 0x75, 0xCC, 0x76, 0xCC, 0x77, /* 0x34-0x37 */
+ 0xD5, 0xBA, 0xCC, 0x78, 0xCC, 0x79, 0xCC, 0x7A, /* 0x38-0x3B */
+ 0xDE, 0xC2, 0xCC, 0x7B, 0xCC, 0x7C, 0xCC, 0x7D, /* 0x3C-0x3F */
+ 0xCC, 0x7E, 0xCC, 0x80, 0xCC, 0x81, 0xCC, 0x82, /* 0x40-0x43 */
+ 0xCC, 0x83, 0xCC, 0x84, 0xCC, 0x85, 0xCC, 0x86, /* 0x44-0x47 */
+ 0xCC, 0x87, 0xCC, 0x88, 0xCC, 0x89, 0xCC, 0x8A, /* 0x48-0x4B */
+ 0xCC, 0x8B, 0xF2, 0xAE, 0xBB, 0xA2, 0xC2, 0xB2, /* 0x4C-0x4F */
+ 0xC5, 0xB0, 0xC2, 0xC7, 0xCC, 0x8C, 0xCC, 0x8D, /* 0x50-0x53 */
+ 0xF2, 0xAF, 0xCC, 0x8E, 0xCC, 0x8F, 0xCC, 0x90, /* 0x54-0x57 */
+ 0xCC, 0x91, 0xCC, 0x92, 0xD0, 0xE9, 0xCC, 0x93, /* 0x58-0x5B */
+ 0xCC, 0x94, 0xCC, 0x95, 0xD3, 0xDD, 0xCC, 0x96, /* 0x5C-0x5F */
+ 0xCC, 0x97, 0xCC, 0x98, 0xEB, 0xBD, 0xCC, 0x99, /* 0x60-0x63 */
+ 0xCC, 0x9A, 0xCC, 0x9B, 0xCC, 0x9C, 0xCC, 0x9D, /* 0x64-0x67 */
+ 0xCC, 0x9E, 0xCC, 0x9F, 0xCC, 0xA0, 0xB3, 0xE6, /* 0x68-0x6B */
+ 0xF2, 0xB0, 0xCD, 0x40, 0xF2, 0xB1, 0xCD, 0x41, /* 0x6C-0x6F */
+ 0xCD, 0x42, 0xCA, 0xAD, 0xCD, 0x43, 0xCD, 0x44, /* 0x70-0x73 */
+ 0xCD, 0x45, 0xCD, 0x46, 0xCD, 0x47, 0xCD, 0x48, /* 0x74-0x77 */
+ 0xCD, 0x49, 0xBA, 0xE7, 0xF2, 0xB3, 0xF2, 0xB5, /* 0x78-0x7B */
+ 0xF2, 0xB4, 0xCB, 0xE4, 0xCF, 0xBA, 0xF2, 0xB2, /* 0x7C-0x7F */
+
+ 0xCA, 0xB4, 0xD2, 0xCF, 0xC2, 0xEC, 0xCD, 0x4A, /* 0x80-0x83 */
+ 0xCD, 0x4B, 0xCD, 0x4C, 0xCD, 0x4D, 0xCD, 0x4E, /* 0x84-0x87 */
+ 0xCD, 0x4F, 0xCD, 0x50, 0xCE, 0xC3, 0xF2, 0xB8, /* 0x88-0x8B */
+ 0xB0, 0xF6, 0xF2, 0xB7, 0xCD, 0x51, 0xCD, 0x52, /* 0x8C-0x8F */
+ 0xCD, 0x53, 0xCD, 0x54, 0xCD, 0x55, 0xF2, 0xBE, /* 0x90-0x93 */
+ 0xCD, 0x56, 0xB2, 0xCF, 0xCD, 0x57, 0xCD, 0x58, /* 0x94-0x97 */
+ 0xCD, 0x59, 0xCD, 0x5A, 0xCD, 0x5B, 0xCD, 0x5C, /* 0x98-0x9B */
+ 0xD1, 0xC1, 0xF2, 0xBA, 0xCD, 0x5D, 0xCD, 0x5E, /* 0x9C-0x9F */
+ 0xCD, 0x5F, 0xCD, 0x60, 0xCD, 0x61, 0xF2, 0xBC, /* 0xA0-0xA3 */
+ 0xD4, 0xE9, 0xCD, 0x62, 0xCD, 0x63, 0xF2, 0xBB, /* 0xA4-0xA7 */
+ 0xF2, 0xB6, 0xF2, 0xBF, 0xF2, 0xBD, 0xCD, 0x64, /* 0xA8-0xAB */
+ 0xF2, 0xB9, 0xCD, 0x65, 0xCD, 0x66, 0xF2, 0xC7, /* 0xAC-0xAF */
+ 0xF2, 0xC4, 0xF2, 0xC6, 0xCD, 0x67, 0xCD, 0x68, /* 0xB0-0xB3 */
+ 0xF2, 0xCA, 0xF2, 0xC2, 0xF2, 0xC0, 0xCD, 0x69, /* 0xB4-0xB7 */
+ 0xCD, 0x6A, 0xCD, 0x6B, 0xF2, 0xC5, 0xCD, 0x6C, /* 0xB8-0xBB */
+ 0xCD, 0x6D, 0xCD, 0x6E, 0xCD, 0x6F, 0xCD, 0x70, /* 0xBC-0xBF */
+ 0xD6, 0xFB, 0xCD, 0x71, 0xCD, 0x72, 0xCD, 0x73, /* 0xC0-0xC3 */
+ 0xF2, 0xC1, 0xCD, 0x74, 0xC7, 0xF9, 0xC9, 0xDF, /* 0xC4-0xC7 */
+ 0xCD, 0x75, 0xF2, 0xC8, 0xB9, 0xC6, 0xB5, 0xB0, /* 0xC8-0xCB */
+ 0xCD, 0x76, 0xCD, 0x77, 0xF2, 0xC3, 0xF2, 0xC9, /* 0xCC-0xCF */
+ 0xF2, 0xD0, 0xF2, 0xD6, 0xCD, 0x78, 0xCD, 0x79, /* 0xD0-0xD3 */
+ 0xBB, 0xD7, 0xCD, 0x7A, 0xCD, 0x7B, 0xCD, 0x7C, /* 0xD4-0xD7 */
+ 0xF2, 0xD5, 0xCD, 0xDC, 0xCD, 0x7D, 0xD6, 0xEB, /* 0xD8-0xDB */
+ 0xCD, 0x7E, 0xCD, 0x80, 0xF2, 0xD2, 0xF2, 0xD4, /* 0xDC-0xDF */
+ 0xCD, 0x81, 0xCD, 0x82, 0xCD, 0x83, 0xCD, 0x84, /* 0xE0-0xE3 */
+ 0xB8, 0xF2, 0xCD, 0x85, 0xCD, 0x86, 0xCD, 0x87, /* 0xE4-0xE7 */
+ 0xCD, 0x88, 0xF2, 0xCB, 0xCD, 0x89, 0xCD, 0x8A, /* 0xE8-0xEB */
+ 0xCD, 0x8B, 0xF2, 0xCE, 0xC2, 0xF9, 0xCD, 0x8C, /* 0xEC-0xEF */
+ 0xD5, 0xDD, 0xF2, 0xCC, 0xF2, 0xCD, 0xF2, 0xCF, /* 0xF0-0xF3 */
+ 0xF2, 0xD3, 0xCD, 0x8D, 0xCD, 0x8E, 0xCD, 0x8F, /* 0xF4-0xF7 */
+ 0xF2, 0xD9, 0xD3, 0xBC, 0xCD, 0x90, 0xCD, 0x91, /* 0xF8-0xFB */
+ 0xCD, 0x92, 0xCD, 0x93, 0xB6, 0xEA, 0xCD, 0x94, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_87[512] = {
+ 0xCA, 0xF1, 0xCD, 0x95, 0xB7, 0xE4, 0xF2, 0xD7, /* 0x00-0x03 */
+ 0xCD, 0x96, 0xCD, 0x97, 0xCD, 0x98, 0xF2, 0xD8, /* 0x04-0x07 */
+ 0xF2, 0xDA, 0xF2, 0xDD, 0xF2, 0xDB, 0xCD, 0x99, /* 0x08-0x0B */
+ 0xCD, 0x9A, 0xF2, 0xDC, 0xCD, 0x9B, 0xCD, 0x9C, /* 0x0C-0x0F */
+ 0xCD, 0x9D, 0xCD, 0x9E, 0xD1, 0xD1, 0xF2, 0xD1, /* 0x10-0x13 */
+ 0xCD, 0x9F, 0xCD, 0xC9, 0xCD, 0xA0, 0xCE, 0xCF, /* 0x14-0x17 */
+ 0xD6, 0xA9, 0xCE, 0x40, 0xF2, 0xE3, 0xCE, 0x41, /* 0x18-0x1B */
+ 0xC3, 0xDB, 0xCE, 0x42, 0xF2, 0xE0, 0xCE, 0x43, /* 0x1C-0x1F */
+ 0xCE, 0x44, 0xC0, 0xAF, 0xF2, 0xEC, 0xF2, 0xDE, /* 0x20-0x23 */
+ 0xCE, 0x45, 0xF2, 0xE1, 0xCE, 0x46, 0xCE, 0x47, /* 0x24-0x27 */
+ 0xCE, 0x48, 0xF2, 0xE8, 0xCE, 0x49, 0xCE, 0x4A, /* 0x28-0x2B */
+ 0xCE, 0x4B, 0xCE, 0x4C, 0xF2, 0xE2, 0xCE, 0x4D, /* 0x2C-0x2F */
+ 0xCE, 0x4E, 0xF2, 0xE7, 0xCE, 0x4F, 0xCE, 0x50, /* 0x30-0x33 */
+ 0xF2, 0xE6, 0xCE, 0x51, 0xCE, 0x52, 0xF2, 0xE9, /* 0x34-0x37 */
+ 0xCE, 0x53, 0xCE, 0x54, 0xCE, 0x55, 0xF2, 0xDF, /* 0x38-0x3B */
+ 0xCE, 0x56, 0xCE, 0x57, 0xF2, 0xE4, 0xF2, 0xEA, /* 0x3C-0x3F */
+ 0xCE, 0x58, 0xCE, 0x59, 0xCE, 0x5A, 0xCE, 0x5B, /* 0x40-0x43 */
+ 0xCE, 0x5C, 0xCE, 0x5D, 0xCE, 0x5E, 0xD3, 0xAC, /* 0x44-0x47 */
+ 0xF2, 0xE5, 0xB2, 0xF5, 0xCE, 0x5F, 0xCE, 0x60, /* 0x48-0x4B */
+ 0xF2, 0xF2, 0xCE, 0x61, 0xD0, 0xAB, 0xCE, 0x62, /* 0x4C-0x4F */
+ 0xCE, 0x63, 0xCE, 0x64, 0xCE, 0x65, 0xF2, 0xF5, /* 0x50-0x53 */
+ 0xCE, 0x66, 0xCE, 0x67, 0xCE, 0x68, 0xBB, 0xC8, /* 0x54-0x57 */
+ 0xCE, 0x69, 0xF2, 0xF9, 0xCE, 0x6A, 0xCE, 0x6B, /* 0x58-0x5B */
+ 0xCE, 0x6C, 0xCE, 0x6D, 0xCE, 0x6E, 0xCE, 0x6F, /* 0x5C-0x5F */
+ 0xF2, 0xF0, 0xCE, 0x70, 0xCE, 0x71, 0xF2, 0xF6, /* 0x60-0x63 */
+ 0xF2, 0xF8, 0xF2, 0xFA, 0xCE, 0x72, 0xCE, 0x73, /* 0x64-0x67 */
+ 0xCE, 0x74, 0xCE, 0x75, 0xCE, 0x76, 0xCE, 0x77, /* 0x68-0x6B */
+ 0xCE, 0x78, 0xCE, 0x79, 0xF2, 0xF3, 0xCE, 0x7A, /* 0x6C-0x6F */
+ 0xF2, 0xF1, 0xCE, 0x7B, 0xCE, 0x7C, 0xCE, 0x7D, /* 0x70-0x73 */
+ 0xBA, 0xFB, 0xCE, 0x7E, 0xB5, 0xFB, 0xCE, 0x80, /* 0x74-0x77 */
+ 0xCE, 0x81, 0xCE, 0x82, 0xCE, 0x83, 0xF2, 0xEF, /* 0x78-0x7B */
+ 0xF2, 0xF7, 0xF2, 0xED, 0xF2, 0xEE, 0xCE, 0x84, /* 0x7C-0x7F */
+
+ 0xCE, 0x85, 0xCE, 0x86, 0xF2, 0xEB, 0xF3, 0xA6, /* 0x80-0x83 */
+ 0xCE, 0x87, 0xF3, 0xA3, 0xCE, 0x88, 0xCE, 0x89, /* 0x84-0x87 */
+ 0xF3, 0xA2, 0xCE, 0x8A, 0xCE, 0x8B, 0xF2, 0xF4, /* 0x88-0x8B */
+ 0xCE, 0x8C, 0xC8, 0xDA, 0xCE, 0x8D, 0xCE, 0x8E, /* 0x8C-0x8F */
+ 0xCE, 0x8F, 0xCE, 0x90, 0xCE, 0x91, 0xF2, 0xFB, /* 0x90-0x93 */
+ 0xCE, 0x92, 0xCE, 0x93, 0xCE, 0x94, 0xF3, 0xA5, /* 0x94-0x97 */
+ 0xCE, 0x95, 0xCE, 0x96, 0xCE, 0x97, 0xCE, 0x98, /* 0x98-0x9B */
+ 0xCE, 0x99, 0xCE, 0x9A, 0xCE, 0x9B, 0xC3, 0xF8, /* 0x9C-0x9F */
+ 0xCE, 0x9C, 0xCE, 0x9D, 0xCE, 0x9E, 0xCE, 0x9F, /* 0xA0-0xA3 */
+ 0xCE, 0xA0, 0xCF, 0x40, 0xCF, 0x41, 0xCF, 0x42, /* 0xA4-0xA7 */
+ 0xF2, 0xFD, 0xCF, 0x43, 0xCF, 0x44, 0xF3, 0xA7, /* 0xA8-0xAB */
+ 0xF3, 0xA9, 0xF3, 0xA4, 0xCF, 0x45, 0xF2, 0xFC, /* 0xAC-0xAF */
+ 0xCF, 0x46, 0xCF, 0x47, 0xCF, 0x48, 0xF3, 0xAB, /* 0xB0-0xB3 */
+ 0xCF, 0x49, 0xF3, 0xAA, 0xCF, 0x4A, 0xCF, 0x4B, /* 0xB4-0xB7 */
+ 0xCF, 0x4C, 0xCF, 0x4D, 0xC2, 0xDD, 0xCF, 0x4E, /* 0xB8-0xBB */
+ 0xCF, 0x4F, 0xF3, 0xAE, 0xCF, 0x50, 0xCF, 0x51, /* 0xBC-0xBF */
+ 0xF3, 0xB0, 0xCF, 0x52, 0xCF, 0x53, 0xCF, 0x54, /* 0xC0-0xC3 */
+ 0xCF, 0x55, 0xCF, 0x56, 0xF3, 0xA1, 0xCF, 0x57, /* 0xC4-0xC7 */
+ 0xCF, 0x58, 0xCF, 0x59, 0xF3, 0xB1, 0xF3, 0xAC, /* 0xC8-0xCB */
+ 0xCF, 0x5A, 0xCF, 0x5B, 0xCF, 0x5C, 0xCF, 0x5D, /* 0xCC-0xCF */
+ 0xCF, 0x5E, 0xF3, 0xAF, 0xF2, 0xFE, 0xF3, 0xAD, /* 0xD0-0xD3 */
+ 0xCF, 0x5F, 0xCF, 0x60, 0xCF, 0x61, 0xCF, 0x62, /* 0xD4-0xD7 */
+ 0xCF, 0x63, 0xCF, 0x64, 0xCF, 0x65, 0xF3, 0xB2, /* 0xD8-0xDB */
+ 0xCF, 0x66, 0xCF, 0x67, 0xCF, 0x68, 0xCF, 0x69, /* 0xDC-0xDF */
+ 0xF3, 0xB4, 0xCF, 0x6A, 0xCF, 0x6B, 0xCF, 0x6C, /* 0xE0-0xE3 */
+ 0xCF, 0x6D, 0xF3, 0xA8, 0xCF, 0x6E, 0xCF, 0x6F, /* 0xE4-0xE7 */
+ 0xCF, 0x70, 0xCF, 0x71, 0xF3, 0xB3, 0xCF, 0x72, /* 0xE8-0xEB */
+ 0xCF, 0x73, 0xCF, 0x74, 0xF3, 0xB5, 0xCF, 0x75, /* 0xEC-0xEF */
+ 0xCF, 0x76, 0xCF, 0x77, 0xCF, 0x78, 0xCF, 0x79, /* 0xF0-0xF3 */
+ 0xCF, 0x7A, 0xCF, 0x7B, 0xCF, 0x7C, 0xCF, 0x7D, /* 0xF4-0xF7 */
+ 0xCF, 0x7E, 0xD0, 0xB7, 0xCF, 0x80, 0xCF, 0x81, /* 0xF8-0xFB */
+ 0xCF, 0x82, 0xCF, 0x83, 0xF3, 0xB8, 0xCF, 0x84, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_88[512] = {
+ 0xCF, 0x85, 0xCF, 0x86, 0xCF, 0x87, 0xD9, 0xF9, /* 0x00-0x03 */
+ 0xCF, 0x88, 0xCF, 0x89, 0xCF, 0x8A, 0xCF, 0x8B, /* 0x04-0x07 */
+ 0xCF, 0x8C, 0xCF, 0x8D, 0xF3, 0xB9, 0xCF, 0x8E, /* 0x08-0x0B */
+ 0xCF, 0x8F, 0xCF, 0x90, 0xCF, 0x91, 0xCF, 0x92, /* 0x0C-0x0F */
+ 0xCF, 0x93, 0xCF, 0x94, 0xCF, 0x95, 0xF3, 0xB7, /* 0x10-0x13 */
+ 0xCF, 0x96, 0xC8, 0xE4, 0xF3, 0xB6, 0xCF, 0x97, /* 0x14-0x17 */
+ 0xCF, 0x98, 0xCF, 0x99, 0xCF, 0x9A, 0xF3, 0xBA, /* 0x18-0x1B */
+ 0xCF, 0x9B, 0xCF, 0x9C, 0xCF, 0x9D, 0xCF, 0x9E, /* 0x1C-0x1F */
+ 0xCF, 0x9F, 0xF3, 0xBB, 0xB4, 0xC0, 0xCF, 0xA0, /* 0x20-0x23 */
+ 0xD0, 0x40, 0xD0, 0x41, 0xD0, 0x42, 0xD0, 0x43, /* 0x24-0x27 */
+ 0xD0, 0x44, 0xD0, 0x45, 0xD0, 0x46, 0xD0, 0x47, /* 0x28-0x2B */
+ 0xD0, 0x48, 0xD0, 0x49, 0xD0, 0x4A, 0xD0, 0x4B, /* 0x2C-0x2F */
+ 0xD0, 0x4C, 0xD0, 0x4D, 0xEE, 0xC3, 0xD0, 0x4E, /* 0x30-0x33 */
+ 0xD0, 0x4F, 0xD0, 0x50, 0xD0, 0x51, 0xD0, 0x52, /* 0x34-0x37 */
+ 0xD0, 0x53, 0xF3, 0xBC, 0xD0, 0x54, 0xD0, 0x55, /* 0x38-0x3B */
+ 0xF3, 0xBD, 0xD0, 0x56, 0xD0, 0x57, 0xD0, 0x58, /* 0x3C-0x3F */
+ 0xD1, 0xAA, 0xD0, 0x59, 0xD0, 0x5A, 0xD0, 0x5B, /* 0x40-0x43 */
+ 0xF4, 0xAC, 0xD0, 0xC6, 0xD0, 0x5C, 0xD0, 0x5D, /* 0x44-0x47 */
+ 0xD0, 0x5E, 0xD0, 0x5F, 0xD0, 0x60, 0xD0, 0x61, /* 0x48-0x4B */
+ 0xD0, 0xD0, 0xD1, 0xDC, 0xD0, 0x62, 0xD0, 0x63, /* 0x4C-0x4F */
+ 0xD0, 0x64, 0xD0, 0x65, 0xD0, 0x66, 0xD0, 0x67, /* 0x50-0x53 */
+ 0xCF, 0xCE, 0xD0, 0x68, 0xD0, 0x69, 0xBD, 0xD6, /* 0x54-0x57 */
+ 0xD0, 0x6A, 0xD1, 0xC3, 0xD0, 0x6B, 0xD0, 0x6C, /* 0x58-0x5B */
+ 0xD0, 0x6D, 0xD0, 0x6E, 0xD0, 0x6F, 0xD0, 0x70, /* 0x5C-0x5F */
+ 0xD0, 0x71, 0xBA, 0xE2, 0xE1, 0xE9, 0xD2, 0xC2, /* 0x60-0x63 */
+ 0xF1, 0xC2, 0xB2, 0xB9, 0xD0, 0x72, 0xD0, 0x73, /* 0x64-0x67 */
+ 0xB1, 0xED, 0xF1, 0xC3, 0xD0, 0x74, 0xC9, 0xC0, /* 0x68-0x6B */
+ 0xB3, 0xC4, 0xD0, 0x75, 0xD9, 0xF2, 0xD0, 0x76, /* 0x6C-0x6F */
+ 0xCB, 0xA5, 0xD0, 0x77, 0xF1, 0xC4, 0xD0, 0x78, /* 0x70-0x73 */
+ 0xD0, 0x79, 0xD0, 0x7A, 0xD0, 0x7B, 0xD6, 0xD4, /* 0x74-0x77 */
+ 0xD0, 0x7C, 0xD0, 0x7D, 0xD0, 0x7E, 0xD0, 0x80, /* 0x78-0x7B */
+ 0xD0, 0x81, 0xF1, 0xC5, 0xF4, 0xC0, 0xF1, 0xC6, /* 0x7C-0x7F */
+
+ 0xD0, 0x82, 0xD4, 0xAC, 0xF1, 0xC7, 0xD0, 0x83, /* 0x80-0x83 */
+ 0xB0, 0xC0, 0xF4, 0xC1, 0xD0, 0x84, 0xD0, 0x85, /* 0x84-0x87 */
+ 0xF4, 0xC2, 0xD0, 0x86, 0xD0, 0x87, 0xB4, 0xFC, /* 0x88-0x8B */
+ 0xD0, 0x88, 0xC5, 0xDB, 0xD0, 0x89, 0xD0, 0x8A, /* 0x8C-0x8F */
+ 0xD0, 0x8B, 0xD0, 0x8C, 0xCC, 0xBB, 0xD0, 0x8D, /* 0x90-0x93 */
+ 0xD0, 0x8E, 0xD0, 0x8F, 0xD0, 0xE4, 0xD0, 0x90, /* 0x94-0x97 */
+ 0xD0, 0x91, 0xD0, 0x92, 0xD0, 0x93, 0xD0, 0x94, /* 0x98-0x9B */
+ 0xCD, 0xE0, 0xD0, 0x95, 0xD0, 0x96, 0xD0, 0x97, /* 0x9C-0x9F */
+ 0xD0, 0x98, 0xD0, 0x99, 0xF1, 0xC8, 0xD0, 0x9A, /* 0xA0-0xA3 */
+ 0xD9, 0xF3, 0xD0, 0x9B, 0xD0, 0x9C, 0xD0, 0x9D, /* 0xA4-0xA7 */
+ 0xD0, 0x9E, 0xD0, 0x9F, 0xD0, 0xA0, 0xB1, 0xBB, /* 0xA8-0xAB */
+ 0xD1, 0x40, 0xCF, 0xAE, 0xD1, 0x41, 0xD1, 0x42, /* 0xAC-0xAF */
+ 0xD1, 0x43, 0xB8, 0xA4, 0xD1, 0x44, 0xD1, 0x45, /* 0xB0-0xB3 */
+ 0xD1, 0x46, 0xD1, 0x47, 0xD1, 0x48, 0xF1, 0xCA, /* 0xB4-0xB7 */
+ 0xD1, 0x49, 0xD1, 0x4A, 0xD1, 0x4B, 0xD1, 0x4C, /* 0xB8-0xBB */
+ 0xF1, 0xCB, 0xD1, 0x4D, 0xD1, 0x4E, 0xD1, 0x4F, /* 0xBC-0xBF */
+ 0xD1, 0x50, 0xB2, 0xC3, 0xC1, 0xD1, 0xD1, 0x51, /* 0xC0-0xC3 */
+ 0xD1, 0x52, 0xD7, 0xB0, 0xF1, 0xC9, 0xD1, 0x53, /* 0xC4-0xC7 */
+ 0xD1, 0x54, 0xF1, 0xCC, 0xD1, 0x55, 0xD1, 0x56, /* 0xC8-0xCB */
+ 0xD1, 0x57, 0xD1, 0x58, 0xF1, 0xCE, 0xD1, 0x59, /* 0xCC-0xCF */
+ 0xD1, 0x5A, 0xD1, 0x5B, 0xD9, 0xF6, 0xD1, 0x5C, /* 0xD0-0xD3 */
+ 0xD2, 0xE1, 0xD4, 0xA3, 0xD1, 0x5D, 0xD1, 0x5E, /* 0xD4-0xD7 */
+ 0xF4, 0xC3, 0xC8, 0xB9, 0xD1, 0x5F, 0xD1, 0x60, /* 0xD8-0xDB */
+ 0xD1, 0x61, 0xD1, 0x62, 0xD1, 0x63, 0xF4, 0xC4, /* 0xDC-0xDF */
+ 0xD1, 0x64, 0xD1, 0x65, 0xF1, 0xCD, 0xF1, 0xCF, /* 0xE0-0xE3 */
+ 0xBF, 0xE3, 0xF1, 0xD0, 0xD1, 0x66, 0xD1, 0x67, /* 0xE4-0xE7 */
+ 0xF1, 0xD4, 0xD1, 0x68, 0xD1, 0x69, 0xD1, 0x6A, /* 0xE8-0xEB */
+ 0xD1, 0x6B, 0xD1, 0x6C, 0xD1, 0x6D, 0xD1, 0x6E, /* 0xEC-0xEF */
+ 0xF1, 0xD6, 0xF1, 0xD1, 0xD1, 0x6F, 0xC9, 0xD1, /* 0xF0-0xF3 */
+ 0xC5, 0xE1, 0xD1, 0x70, 0xD1, 0x71, 0xD1, 0x72, /* 0xF4-0xF7 */
+ 0xC2, 0xE3, 0xB9, 0xFC, 0xD1, 0x73, 0xD1, 0x74, /* 0xF8-0xFB */
+ 0xF1, 0xD3, 0xD1, 0x75, 0xF1, 0xD5, 0xD1, 0x76, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_89[512] = {
+ 0xD1, 0x77, 0xD1, 0x78, 0xB9, 0xD3, 0xD1, 0x79, /* 0x00-0x03 */
+ 0xD1, 0x7A, 0xD1, 0x7B, 0xD1, 0x7C, 0xD1, 0x7D, /* 0x04-0x07 */
+ 0xD1, 0x7E, 0xD1, 0x80, 0xF1, 0xDB, 0xD1, 0x81, /* 0x08-0x0B */
+ 0xD1, 0x82, 0xD1, 0x83, 0xD1, 0x84, 0xD1, 0x85, /* 0x0C-0x0F */
+ 0xBA, 0xD6, 0xD1, 0x86, 0xB0, 0xFD, 0xF1, 0xD9, /* 0x10-0x13 */
+ 0xD1, 0x87, 0xD1, 0x88, 0xD1, 0x89, 0xD1, 0x8A, /* 0x14-0x17 */
+ 0xD1, 0x8B, 0xF1, 0xD8, 0xF1, 0xD2, 0xF1, 0xDA, /* 0x18-0x1B */
+ 0xD1, 0x8C, 0xD1, 0x8D, 0xD1, 0x8E, 0xD1, 0x8F, /* 0x1C-0x1F */
+ 0xD1, 0x90, 0xF1, 0xD7, 0xD1, 0x91, 0xD1, 0x92, /* 0x20-0x23 */
+ 0xD1, 0x93, 0xC8, 0xEC, 0xD1, 0x94, 0xD1, 0x95, /* 0x24-0x27 */
+ 0xD1, 0x96, 0xD1, 0x97, 0xCD, 0xCA, 0xF1, 0xDD, /* 0x28-0x2B */
+ 0xD1, 0x98, 0xD1, 0x99, 0xD1, 0x9A, 0xD1, 0x9B, /* 0x2C-0x2F */
+ 0xE5, 0xBD, 0xD1, 0x9C, 0xD1, 0x9D, 0xD1, 0x9E, /* 0x30-0x33 */
+ 0xF1, 0xDC, 0xD1, 0x9F, 0xF1, 0xDE, 0xD1, 0xA0, /* 0x34-0x37 */
+ 0xD2, 0x40, 0xD2, 0x41, 0xD2, 0x42, 0xD2, 0x43, /* 0x38-0x3B */
+ 0xD2, 0x44, 0xD2, 0x45, 0xD2, 0x46, 0xD2, 0x47, /* 0x3C-0x3F */
+ 0xD2, 0x48, 0xF1, 0xDF, 0xD2, 0x49, 0xD2, 0x4A, /* 0x40-0x43 */
+ 0xCF, 0xE5, 0xD2, 0x4B, 0xD2, 0x4C, 0xD2, 0x4D, /* 0x44-0x47 */
+ 0xD2, 0x4E, 0xD2, 0x4F, 0xD2, 0x50, 0xD2, 0x51, /* 0x48-0x4B */
+ 0xD2, 0x52, 0xD2, 0x53, 0xD2, 0x54, 0xD2, 0x55, /* 0x4C-0x4F */
+ 0xD2, 0x56, 0xD2, 0x57, 0xD2, 0x58, 0xD2, 0x59, /* 0x50-0x53 */
+ 0xD2, 0x5A, 0xD2, 0x5B, 0xD2, 0x5C, 0xD2, 0x5D, /* 0x54-0x57 */
+ 0xD2, 0x5E, 0xD2, 0x5F, 0xD2, 0x60, 0xD2, 0x61, /* 0x58-0x5B */
+ 0xD2, 0x62, 0xD2, 0x63, 0xF4, 0xC5, 0xBD, 0xF3, /* 0x5C-0x5F */
+ 0xD2, 0x64, 0xD2, 0x65, 0xD2, 0x66, 0xD2, 0x67, /* 0x60-0x63 */
+ 0xD2, 0x68, 0xD2, 0x69, 0xF1, 0xE0, 0xD2, 0x6A, /* 0x64-0x67 */
+ 0xD2, 0x6B, 0xD2, 0x6C, 0xD2, 0x6D, 0xD2, 0x6E, /* 0x68-0x6B */
+ 0xD2, 0x6F, 0xD2, 0x70, 0xD2, 0x71, 0xD2, 0x72, /* 0x6C-0x6F */
+ 0xD2, 0x73, 0xD2, 0x74, 0xD2, 0x75, 0xD2, 0x76, /* 0x70-0x73 */
+ 0xD2, 0x77, 0xD2, 0x78, 0xD2, 0x79, 0xD2, 0x7A, /* 0x74-0x77 */
+ 0xD2, 0x7B, 0xD2, 0x7C, 0xD2, 0x7D, 0xF1, 0xE1, /* 0x78-0x7B */
+ 0xD2, 0x7E, 0xD2, 0x80, 0xD2, 0x81, 0xCE, 0xF7, /* 0x7C-0x7F */
+
+ 0xD2, 0x82, 0xD2, 0xAA, 0xD2, 0x83, 0xF1, 0xFB, /* 0x80-0x83 */
+ 0xD2, 0x84, 0xD2, 0x85, 0xB8, 0xB2, 0xD2, 0x86, /* 0x84-0x87 */
+ 0xD2, 0x87, 0xD2, 0x88, 0xD2, 0x89, 0xD2, 0x8A, /* 0x88-0x8B */
+ 0xD2, 0x8B, 0xD2, 0x8C, 0xD2, 0x8D, 0xD2, 0x8E, /* 0x8C-0x8F */
+ 0xD2, 0x8F, 0xD2, 0x90, 0xD2, 0x91, 0xD2, 0x92, /* 0x90-0x93 */
+ 0xD2, 0x93, 0xD2, 0x94, 0xD2, 0x95, 0xD2, 0x96, /* 0x94-0x97 */
+ 0xD2, 0x97, 0xD2, 0x98, 0xD2, 0x99, 0xD2, 0x9A, /* 0x98-0x9B */
+ 0xD2, 0x9B, 0xD2, 0x9C, 0xD2, 0x9D, 0xD2, 0x9E, /* 0x9C-0x9F */
+ 0xD2, 0x9F, 0xD2, 0xA0, 0xD3, 0x40, 0xD3, 0x41, /* 0xA0-0xA3 */
+ 0xD3, 0x42, 0xD3, 0x43, 0xD3, 0x44, 0xD3, 0x45, /* 0xA4-0xA7 */
+ 0xD3, 0x46, 0xD3, 0x47, 0xD3, 0x48, 0xD3, 0x49, /* 0xA8-0xAB */
+ 0xD3, 0x4A, 0xD3, 0x4B, 0xD3, 0x4C, 0xD3, 0x4D, /* 0xAC-0xAF */
+ 0xD3, 0x4E, 0xD3, 0x4F, 0xD3, 0x50, 0xD3, 0x51, /* 0xB0-0xB3 */
+ 0xD3, 0x52, 0xD3, 0x53, 0xD3, 0x54, 0xD3, 0x55, /* 0xB4-0xB7 */
+ 0xD3, 0x56, 0xD3, 0x57, 0xD3, 0x58, 0xD3, 0x59, /* 0xB8-0xBB */
+ 0xD3, 0x5A, 0xD3, 0x5B, 0xD3, 0x5C, 0xD3, 0x5D, /* 0xBC-0xBF */
+ 0xD3, 0x5E, 0xBC, 0xFB, 0xB9, 0xDB, 0xD3, 0x5F, /* 0xC0-0xC3 */
+ 0xB9, 0xE6, 0xC3, 0xD9, 0xCA, 0xD3, 0xEA, 0xE8, /* 0xC4-0xC7 */
+ 0xC0, 0xC0, 0xBE, 0xF5, 0xEA, 0xE9, 0xEA, 0xEA, /* 0xC8-0xCB */
+ 0xEA, 0xEB, 0xD3, 0x60, 0xEA, 0xEC, 0xEA, 0xED, /* 0xCC-0xCF */
+ 0xEA, 0xEE, 0xEA, 0xEF, 0xBD, 0xC7, 0xD3, 0x61, /* 0xD0-0xD3 */
+ 0xD3, 0x62, 0xD3, 0x63, 0xF5, 0xFB, 0xD3, 0x64, /* 0xD4-0xD7 */
+ 0xD3, 0x65, 0xD3, 0x66, 0xF5, 0xFD, 0xD3, 0x67, /* 0xD8-0xDB */
+ 0xF5, 0xFE, 0xD3, 0x68, 0xF5, 0xFC, 0xD3, 0x69, /* 0xDC-0xDF */
+ 0xD3, 0x6A, 0xD3, 0x6B, 0xD3, 0x6C, 0xBD, 0xE2, /* 0xE0-0xE3 */
+ 0xD3, 0x6D, 0xF6, 0xA1, 0xB4, 0xA5, 0xD3, 0x6E, /* 0xE4-0xE7 */
+ 0xD3, 0x6F, 0xD3, 0x70, 0xD3, 0x71, 0xF6, 0xA2, /* 0xE8-0xEB */
+ 0xD3, 0x72, 0xD3, 0x73, 0xD3, 0x74, 0xF6, 0xA3, /* 0xEC-0xEF */
+ 0xD3, 0x75, 0xD3, 0x76, 0xD3, 0x77, 0xEC, 0xB2, /* 0xF0-0xF3 */
+ 0xD3, 0x78, 0xD3, 0x79, 0xD3, 0x7A, 0xD3, 0x7B, /* 0xF4-0xF7 */
+ 0xD3, 0x7C, 0xD3, 0x7D, 0xD3, 0x7E, 0xD3, 0x80, /* 0xF8-0xFB */
+ 0xD3, 0x81, 0xD3, 0x82, 0xD3, 0x83, 0xD3, 0x84, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_8A[512] = {
+ 0xD1, 0xD4, 0xD3, 0x85, 0xD3, 0x86, 0xD3, 0x87, /* 0x00-0x03 */
+ 0xD3, 0x88, 0xD3, 0x89, 0xD3, 0x8A, 0xD9, 0xEA, /* 0x04-0x07 */
+ 0xD3, 0x8B, 0xD3, 0x8C, 0xD3, 0x8D, 0xD3, 0x8E, /* 0x08-0x0B */
+ 0xD3, 0x8F, 0xD3, 0x90, 0xD3, 0x91, 0xD3, 0x92, /* 0x0C-0x0F */
+ 0xD3, 0x93, 0xD3, 0x94, 0xD3, 0x95, 0xD3, 0x96, /* 0x10-0x13 */
+ 0xD3, 0x97, 0xD3, 0x98, 0xD3, 0x99, 0xD3, 0x9A, /* 0x14-0x17 */
+ 0xD3, 0x9B, 0xD3, 0x9C, 0xD3, 0x9D, 0xD3, 0x9E, /* 0x18-0x1B */
+ 0xD3, 0x9F, 0xD3, 0xA0, 0xD4, 0x40, 0xD4, 0x41, /* 0x1C-0x1F */
+ 0xD4, 0x42, 0xD4, 0x43, 0xD4, 0x44, 0xD4, 0x45, /* 0x20-0x23 */
+ 0xD4, 0x46, 0xD4, 0x47, 0xD4, 0x48, 0xD4, 0x49, /* 0x24-0x27 */
+ 0xD4, 0x4A, 0xD4, 0x4B, 0xD4, 0x4C, 0xD4, 0x4D, /* 0x28-0x2B */
+ 0xD4, 0x4E, 0xD4, 0x4F, 0xD4, 0x50, 0xD4, 0x51, /* 0x2C-0x2F */
+ 0xD4, 0x52, 0xD4, 0x53, 0xD4, 0x54, 0xD4, 0x55, /* 0x30-0x33 */
+ 0xD4, 0x56, 0xD4, 0x57, 0xD4, 0x58, 0xD4, 0x59, /* 0x34-0x37 */
+ 0xD4, 0x5A, 0xD4, 0x5B, 0xD4, 0x5C, 0xD4, 0x5D, /* 0x38-0x3B */
+ 0xD4, 0x5E, 0xD4, 0x5F, 0xF6, 0xA4, 0xD4, 0x60, /* 0x3C-0x3F */
+ 0xD4, 0x61, 0xD4, 0x62, 0xD4, 0x63, 0xD4, 0x64, /* 0x40-0x43 */
+ 0xD4, 0x65, 0xD4, 0x66, 0xD4, 0x67, 0xD4, 0x68, /* 0x44-0x47 */
+ 0xEE, 0xBA, 0xD4, 0x69, 0xD4, 0x6A, 0xD4, 0x6B, /* 0x48-0x4B */
+ 0xD4, 0x6C, 0xD4, 0x6D, 0xD4, 0x6E, 0xD4, 0x6F, /* 0x4C-0x4F */
+ 0xD4, 0x70, 0xD4, 0x71, 0xD4, 0x72, 0xD4, 0x73, /* 0x50-0x53 */
+ 0xD4, 0x74, 0xD4, 0x75, 0xD4, 0x76, 0xD4, 0x77, /* 0x54-0x57 */
+ 0xD4, 0x78, 0xD4, 0x79, 0xD4, 0x7A, 0xD4, 0x7B, /* 0x58-0x5B */
+ 0xD4, 0x7C, 0xD4, 0x7D, 0xD4, 0x7E, 0xD4, 0x80, /* 0x5C-0x5F */
+ 0xD4, 0x81, 0xD4, 0x82, 0xD4, 0x83, 0xD4, 0x84, /* 0x60-0x63 */
+ 0xD4, 0x85, 0xD4, 0x86, 0xD4, 0x87, 0xD4, 0x88, /* 0x64-0x67 */
+ 0xD4, 0x89, 0xD4, 0x8A, 0xD4, 0x8B, 0xD4, 0x8C, /* 0x68-0x6B */
+ 0xD4, 0x8D, 0xD4, 0x8E, 0xD4, 0x8F, 0xD4, 0x90, /* 0x6C-0x6F */
+ 0xD4, 0x91, 0xD4, 0x92, 0xD4, 0x93, 0xD4, 0x94, /* 0x70-0x73 */
+ 0xD4, 0x95, 0xD4, 0x96, 0xD4, 0x97, 0xD4, 0x98, /* 0x74-0x77 */
+ 0xD4, 0x99, 0xD5, 0xB2, 0xD4, 0x9A, 0xD4, 0x9B, /* 0x78-0x7B */
+ 0xD4, 0x9C, 0xD4, 0x9D, 0xD4, 0x9E, 0xD4, 0x9F, /* 0x7C-0x7F */
+
+ 0xD4, 0xA0, 0xD5, 0x40, 0xD5, 0x41, 0xD5, 0x42, /* 0x80-0x83 */
+ 0xD5, 0x43, 0xD5, 0x44, 0xD5, 0x45, 0xD5, 0x46, /* 0x84-0x87 */
+ 0xD5, 0x47, 0xD3, 0xFE, 0xCC, 0xDC, 0xD5, 0x48, /* 0x88-0x8B */
+ 0xD5, 0x49, 0xD5, 0x4A, 0xD5, 0x4B, 0xD5, 0x4C, /* 0x8C-0x8F */
+ 0xD5, 0x4D, 0xD5, 0x4E, 0xD5, 0x4F, 0xCA, 0xC4, /* 0x90-0x93 */
+ 0xD5, 0x50, 0xD5, 0x51, 0xD5, 0x52, 0xD5, 0x53, /* 0x94-0x97 */
+ 0xD5, 0x54, 0xD5, 0x55, 0xD5, 0x56, 0xD5, 0x57, /* 0x98-0x9B */
+ 0xD5, 0x58, 0xD5, 0x59, 0xD5, 0x5A, 0xD5, 0x5B, /* 0x9C-0x9F */
+ 0xD5, 0x5C, 0xD5, 0x5D, 0xD5, 0x5E, 0xD5, 0x5F, /* 0xA0-0xA3 */
+ 0xD5, 0x60, 0xD5, 0x61, 0xD5, 0x62, 0xD5, 0x63, /* 0xA4-0xA7 */
+ 0xD5, 0x64, 0xD5, 0x65, 0xD5, 0x66, 0xD5, 0x67, /* 0xA8-0xAB */
+ 0xD5, 0x68, 0xD5, 0x69, 0xD5, 0x6A, 0xD5, 0x6B, /* 0xAC-0xAF */
+ 0xD5, 0x6C, 0xD5, 0x6D, 0xD5, 0x6E, 0xD5, 0x6F, /* 0xB0-0xB3 */
+ 0xD5, 0x70, 0xD5, 0x71, 0xD5, 0x72, 0xD5, 0x73, /* 0xB4-0xB7 */
+ 0xD5, 0x74, 0xD5, 0x75, 0xD5, 0x76, 0xD5, 0x77, /* 0xB8-0xBB */
+ 0xD5, 0x78, 0xD5, 0x79, 0xD5, 0x7A, 0xD5, 0x7B, /* 0xBC-0xBF */
+ 0xD5, 0x7C, 0xD5, 0x7D, 0xD5, 0x7E, 0xD5, 0x80, /* 0xC0-0xC3 */
+ 0xD5, 0x81, 0xD5, 0x82, 0xD5, 0x83, 0xD5, 0x84, /* 0xC4-0xC7 */
+ 0xD5, 0x85, 0xD5, 0x86, 0xD5, 0x87, 0xD5, 0x88, /* 0xC8-0xCB */
+ 0xD5, 0x89, 0xD5, 0x8A, 0xD5, 0x8B, 0xD5, 0x8C, /* 0xCC-0xCF */
+ 0xD5, 0x8D, 0xD5, 0x8E, 0xD5, 0x8F, 0xD5, 0x90, /* 0xD0-0xD3 */
+ 0xD5, 0x91, 0xD5, 0x92, 0xD5, 0x93, 0xD5, 0x94, /* 0xD4-0xD7 */
+ 0xD5, 0x95, 0xD5, 0x96, 0xD5, 0x97, 0xD5, 0x98, /* 0xD8-0xDB */
+ 0xD5, 0x99, 0xD5, 0x9A, 0xD5, 0x9B, 0xD5, 0x9C, /* 0xDC-0xDF */
+ 0xD5, 0x9D, 0xD5, 0x9E, 0xD5, 0x9F, 0xD5, 0xA0, /* 0xE0-0xE3 */
+ 0xD6, 0x40, 0xD6, 0x41, 0xD6, 0x42, 0xD6, 0x43, /* 0xE4-0xE7 */
+ 0xD6, 0x44, 0xD6, 0x45, 0xD6, 0x46, 0xD6, 0x47, /* 0xE8-0xEB */
+ 0xD6, 0x48, 0xD6, 0x49, 0xD6, 0x4A, 0xD6, 0x4B, /* 0xEC-0xEF */
+ 0xD6, 0x4C, 0xD6, 0x4D, 0xD6, 0x4E, 0xD6, 0x4F, /* 0xF0-0xF3 */
+ 0xD6, 0x50, 0xD6, 0x51, 0xD6, 0x52, 0xD6, 0x53, /* 0xF4-0xF7 */
+ 0xD6, 0x54, 0xD6, 0x55, 0xD6, 0x56, 0xD6, 0x57, /* 0xF8-0xFB */
+ 0xD6, 0x58, 0xD6, 0x59, 0xD6, 0x5A, 0xD6, 0x5B, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_8B[512] = {
+ 0xD6, 0x5C, 0xD6, 0x5D, 0xD6, 0x5E, 0xD6, 0x5F, /* 0x00-0x03 */
+ 0xD6, 0x60, 0xD6, 0x61, 0xD6, 0x62, 0xE5, 0xC0, /* 0x04-0x07 */
+ 0xD6, 0x63, 0xD6, 0x64, 0xD6, 0x65, 0xD6, 0x66, /* 0x08-0x0B */
+ 0xD6, 0x67, 0xD6, 0x68, 0xD6, 0x69, 0xD6, 0x6A, /* 0x0C-0x0F */
+ 0xD6, 0x6B, 0xD6, 0x6C, 0xD6, 0x6D, 0xD6, 0x6E, /* 0x10-0x13 */
+ 0xD6, 0x6F, 0xD6, 0x70, 0xD6, 0x71, 0xD6, 0x72, /* 0x14-0x17 */
+ 0xD6, 0x73, 0xD6, 0x74, 0xD6, 0x75, 0xD6, 0x76, /* 0x18-0x1B */
+ 0xD6, 0x77, 0xD6, 0x78, 0xD6, 0x79, 0xD6, 0x7A, /* 0x1C-0x1F */
+ 0xD6, 0x7B, 0xD6, 0x7C, 0xD6, 0x7D, 0xD6, 0x7E, /* 0x20-0x23 */
+ 0xD6, 0x80, 0xD6, 0x81, 0xF6, 0xA5, 0xD6, 0x82, /* 0x24-0x27 */
+ 0xD6, 0x83, 0xD6, 0x84, 0xD6, 0x85, 0xD6, 0x86, /* 0x28-0x2B */
+ 0xD6, 0x87, 0xD6, 0x88, 0xD6, 0x89, 0xD6, 0x8A, /* 0x2C-0x2F */
+ 0xD6, 0x8B, 0xD6, 0x8C, 0xD6, 0x8D, 0xD6, 0x8E, /* 0x30-0x33 */
+ 0xD6, 0x8F, 0xD6, 0x90, 0xD6, 0x91, 0xD6, 0x92, /* 0x34-0x37 */
+ 0xD6, 0x93, 0xD6, 0x94, 0xD6, 0x95, 0xD6, 0x96, /* 0x38-0x3B */
+ 0xD6, 0x97, 0xD6, 0x98, 0xD6, 0x99, 0xD6, 0x9A, /* 0x3C-0x3F */
+ 0xD6, 0x9B, 0xD6, 0x9C, 0xD6, 0x9D, 0xD6, 0x9E, /* 0x40-0x43 */
+ 0xD6, 0x9F, 0xD6, 0xA0, 0xD7, 0x40, 0xD7, 0x41, /* 0x44-0x47 */
+ 0xD7, 0x42, 0xD7, 0x43, 0xD7, 0x44, 0xD7, 0x45, /* 0x48-0x4B */
+ 0xD7, 0x46, 0xD7, 0x47, 0xD7, 0x48, 0xD7, 0x49, /* 0x4C-0x4F */
+ 0xD7, 0x4A, 0xD7, 0x4B, 0xD7, 0x4C, 0xD7, 0x4D, /* 0x50-0x53 */
+ 0xD7, 0x4E, 0xD7, 0x4F, 0xD7, 0x50, 0xD7, 0x51, /* 0x54-0x57 */
+ 0xD7, 0x52, 0xD7, 0x53, 0xD7, 0x54, 0xD7, 0x55, /* 0x58-0x5B */
+ 0xD7, 0x56, 0xD7, 0x57, 0xD7, 0x58, 0xD7, 0x59, /* 0x5C-0x5F */
+ 0xD7, 0x5A, 0xD7, 0x5B, 0xD7, 0x5C, 0xD7, 0x5D, /* 0x60-0x63 */
+ 0xD7, 0x5E, 0xD7, 0x5F, 0xBE, 0xAF, 0xD7, 0x60, /* 0x64-0x67 */
+ 0xD7, 0x61, 0xD7, 0x62, 0xD7, 0x63, 0xD7, 0x64, /* 0x68-0x6B */
+ 0xC6, 0xA9, 0xD7, 0x65, 0xD7, 0x66, 0xD7, 0x67, /* 0x6C-0x6F */
+ 0xD7, 0x68, 0xD7, 0x69, 0xD7, 0x6A, 0xD7, 0x6B, /* 0x70-0x73 */
+ 0xD7, 0x6C, 0xD7, 0x6D, 0xD7, 0x6E, 0xD7, 0x6F, /* 0x74-0x77 */
+ 0xD7, 0x70, 0xD7, 0x71, 0xD7, 0x72, 0xD7, 0x73, /* 0x78-0x7B */
+ 0xD7, 0x74, 0xD7, 0x75, 0xD7, 0x76, 0xD7, 0x77, /* 0x7C-0x7F */
+
+ 0xD7, 0x78, 0xD7, 0x79, 0xD7, 0x7A, 0xD7, 0x7B, /* 0x80-0x83 */
+ 0xD7, 0x7C, 0xD7, 0x7D, 0xD7, 0x7E, 0xD7, 0x80, /* 0x84-0x87 */
+ 0xD7, 0x81, 0xD7, 0x82, 0xD7, 0x83, 0xD7, 0x84, /* 0x88-0x8B */
+ 0xD7, 0x85, 0xD7, 0x86, 0xD7, 0x87, 0xD7, 0x88, /* 0x8C-0x8F */
+ 0xD7, 0x89, 0xD7, 0x8A, 0xD7, 0x8B, 0xD7, 0x8C, /* 0x90-0x93 */
+ 0xD7, 0x8D, 0xD7, 0x8E, 0xD7, 0x8F, 0xD7, 0x90, /* 0x94-0x97 */
+ 0xD7, 0x91, 0xD7, 0x92, 0xD7, 0x93, 0xD7, 0x94, /* 0x98-0x9B */
+ 0xD7, 0x95, 0xD7, 0x96, 0xD7, 0x97, 0xD7, 0x98, /* 0x9C-0x9F */
+ 0xDA, 0xA5, 0xBC, 0xC6, 0xB6, 0xA9, 0xB8, 0xBC, /* 0xA0-0xA3 */
+ 0xC8, 0xCF, 0xBC, 0xA5, 0xDA, 0xA6, 0xDA, 0xA7, /* 0xA4-0xA7 */
+ 0xCC, 0xD6, 0xC8, 0xC3, 0xDA, 0xA8, 0xC6, 0xFD, /* 0xA8-0xAB */
+ 0xD7, 0x99, 0xD1, 0xB5, 0xD2, 0xE9, 0xD1, 0xB6, /* 0xAC-0xAF */
+ 0xBC, 0xC7, 0xD7, 0x9A, 0xBD, 0xB2, 0xBB, 0xE4, /* 0xB0-0xB3 */
+ 0xDA, 0xA9, 0xDA, 0xAA, 0xD1, 0xC8, 0xDA, 0xAB, /* 0xB4-0xB7 */
+ 0xD0, 0xED, 0xB6, 0xEF, 0xC2, 0xDB, 0xD7, 0x9B, /* 0xB8-0xBB */
+ 0xCB, 0xCF, 0xB7, 0xED, 0xC9, 0xE8, 0xB7, 0xC3, /* 0xBC-0xBF */
+ 0xBE, 0xF7, 0xD6, 0xA4, 0xDA, 0xAC, 0xDA, 0xAD, /* 0xC0-0xC3 */
+ 0xC6, 0xC0, 0xD7, 0xE7, 0xCA, 0xB6, 0xD7, 0x9C, /* 0xC4-0xC7 */
+ 0xD5, 0xA9, 0xCB, 0xDF, 0xD5, 0xEF, 0xDA, 0xAE, /* 0xC8-0xCB */
+ 0xD6, 0xDF, 0xB4, 0xCA, 0xDA, 0xB0, 0xDA, 0xAF, /* 0xCC-0xCF */
+ 0xD7, 0x9D, 0xD2, 0xEB, 0xDA, 0xB1, 0xDA, 0xB2, /* 0xD0-0xD3 */
+ 0xDA, 0xB3, 0xCA, 0xD4, 0xDA, 0xB4, 0xCA, 0xAB, /* 0xD4-0xD7 */
+ 0xDA, 0xB5, 0xDA, 0xB6, 0xB3, 0xCF, 0xD6, 0xEF, /* 0xD8-0xDB */
+ 0xDA, 0xB7, 0xBB, 0xB0, 0xB5, 0xAE, 0xDA, 0xB8, /* 0xDC-0xDF */
+ 0xDA, 0xB9, 0xB9, 0xEE, 0xD1, 0xAF, 0xD2, 0xE8, /* 0xE0-0xE3 */
+ 0xDA, 0xBA, 0xB8, 0xC3, 0xCF, 0xEA, 0xB2, 0xEF, /* 0xE4-0xE7 */
+ 0xDA, 0xBB, 0xDA, 0xBC, 0xD7, 0x9E, 0xBD, 0xEB, /* 0xE8-0xEB */
+ 0xCE, 0xDC, 0xD3, 0xEF, 0xDA, 0xBD, 0xCE, 0xF3, /* 0xEC-0xEF */
+ 0xDA, 0xBE, 0xD3, 0xD5, 0xBB, 0xE5, 0xDA, 0xBF, /* 0xF0-0xF3 */
+ 0xCB, 0xB5, 0xCB, 0xD0, 0xDA, 0xC0, 0xC7, 0xEB, /* 0xF4-0xF7 */
+ 0xD6, 0xEE, 0xDA, 0xC1, 0xC5, 0xB5, 0xB6, 0xC1, /* 0xF8-0xFB */
+ 0xDA, 0xC2, 0xB7, 0xCC, 0xBF, 0xCE, 0xDA, 0xC3, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_8C[512] = {
+ 0xDA, 0xC4, 0xCB, 0xAD, 0xDA, 0xC5, 0xB5, 0xF7, /* 0x00-0x03 */
+ 0xDA, 0xC6, 0xC1, 0xC2, 0xD7, 0xBB, 0xDA, 0xC7, /* 0x04-0x07 */
+ 0xCC, 0xB8, 0xD7, 0x9F, 0xD2, 0xEA, 0xC4, 0xB1, /* 0x08-0x0B */
+ 0xDA, 0xC8, 0xB5, 0xFD, 0xBB, 0xD1, 0xDA, 0xC9, /* 0x0C-0x0F */
+ 0xD0, 0xB3, 0xDA, 0xCA, 0xDA, 0xCB, 0xCE, 0xBD, /* 0x10-0x13 */
+ 0xDA, 0xCC, 0xDA, 0xCD, 0xDA, 0xCE, 0xB2, 0xF7, /* 0x14-0x17 */
+ 0xDA, 0xD1, 0xDA, 0xCF, 0xD1, 0xE8, 0xDA, 0xD0, /* 0x18-0x1B */
+ 0xC3, 0xD5, 0xDA, 0xD2, 0xD7, 0xA0, 0xDA, 0xD3, /* 0x1C-0x1F */
+ 0xDA, 0xD4, 0xDA, 0xD5, 0xD0, 0xBB, 0xD2, 0xA5, /* 0x20-0x23 */
+ 0xB0, 0xF9, 0xDA, 0xD6, 0xC7, 0xAB, 0xDA, 0xD7, /* 0x24-0x27 */
+ 0xBD, 0xF7, 0xC3, 0xA1, 0xDA, 0xD8, 0xDA, 0xD9, /* 0x28-0x2B */
+ 0xC3, 0xFD, 0xCC, 0xB7, 0xDA, 0xDA, 0xDA, 0xDB, /* 0x2C-0x2F */
+ 0xC0, 0xBE, 0xC6, 0xD7, 0xDA, 0xDC, 0xDA, 0xDD, /* 0x30-0x33 */
+ 0xC7, 0xB4, 0xDA, 0xDE, 0xDA, 0xDF, 0xB9, 0xC8, /* 0x34-0x37 */
+ 0xD8, 0x40, 0xD8, 0x41, 0xD8, 0x42, 0xD8, 0x43, /* 0x38-0x3B */
+ 0xD8, 0x44, 0xD8, 0x45, 0xD8, 0x46, 0xD8, 0x47, /* 0x3C-0x3F */
+ 0xD8, 0x48, 0xBB, 0xED, 0xD8, 0x49, 0xD8, 0x4A, /* 0x40-0x43 */
+ 0xD8, 0x4B, 0xD8, 0x4C, 0xB6, 0xB9, 0xF4, 0xF8, /* 0x44-0x47 */
+ 0xD8, 0x4D, 0xF4, 0xF9, 0xD8, 0x4E, 0xD8, 0x4F, /* 0x48-0x4B */
+ 0xCD, 0xE3, 0xD8, 0x50, 0xD8, 0x51, 0xD8, 0x52, /* 0x4C-0x4F */
+ 0xD8, 0x53, 0xD8, 0x54, 0xD8, 0x55, 0xD8, 0x56, /* 0x50-0x53 */
+ 0xD8, 0x57, 0xF5, 0xB9, 0xD8, 0x58, 0xD8, 0x59, /* 0x54-0x57 */
+ 0xD8, 0x5A, 0xD8, 0x5B, 0xEB, 0xE0, 0xD8, 0x5C, /* 0x58-0x5B */
+ 0xD8, 0x5D, 0xD8, 0x5E, 0xD8, 0x5F, 0xD8, 0x60, /* 0x5C-0x5F */
+ 0xD8, 0x61, 0xCF, 0xF3, 0xBB, 0xBF, 0xD8, 0x62, /* 0x60-0x63 */
+ 0xD8, 0x63, 0xD8, 0x64, 0xD8, 0x65, 0xD8, 0x66, /* 0x64-0x67 */
+ 0xD8, 0x67, 0xD8, 0x68, 0xBA, 0xC0, 0xD4, 0xA5, /* 0x68-0x6B */
+ 0xD8, 0x69, 0xD8, 0x6A, 0xD8, 0x6B, 0xD8, 0x6C, /* 0x6C-0x6F */
+ 0xD8, 0x6D, 0xD8, 0x6E, 0xD8, 0x6F, 0xE1, 0xD9, /* 0x70-0x73 */
+ 0xD8, 0x70, 0xD8, 0x71, 0xD8, 0x72, 0xD8, 0x73, /* 0x74-0x77 */
+ 0xF5, 0xF4, 0xB1, 0xAA, 0xB2, 0xF2, 0xD8, 0x74, /* 0x78-0x7B */
+ 0xD8, 0x75, 0xD8, 0x76, 0xD8, 0x77, 0xD8, 0x78, /* 0x7C-0x7F */
+
+ 0xD8, 0x79, 0xD8, 0x7A, 0xF5, 0xF5, 0xD8, 0x7B, /* 0x80-0x83 */
+ 0xD8, 0x7C, 0xF5, 0xF7, 0xD8, 0x7D, 0xD8, 0x7E, /* 0x84-0x87 */
+ 0xD8, 0x80, 0xBA, 0xD1, 0xF5, 0xF6, 0xD8, 0x81, /* 0x88-0x8B */
+ 0xC3, 0xB2, 0xD8, 0x82, 0xD8, 0x83, 0xD8, 0x84, /* 0x8C-0x8F */
+ 0xD8, 0x85, 0xD8, 0x86, 0xD8, 0x87, 0xD8, 0x88, /* 0x90-0x93 */
+ 0xF5, 0xF9, 0xD8, 0x89, 0xD8, 0x8A, 0xD8, 0x8B, /* 0x94-0x97 */
+ 0xF5, 0xF8, 0xD8, 0x8C, 0xD8, 0x8D, 0xD8, 0x8E, /* 0x98-0x9B */
+ 0xD8, 0x8F, 0xD8, 0x90, 0xD8, 0x91, 0xD8, 0x92, /* 0x9C-0x9F */
+ 0xD8, 0x93, 0xD8, 0x94, 0xD8, 0x95, 0xD8, 0x96, /* 0xA0-0xA3 */
+ 0xD8, 0x97, 0xD8, 0x98, 0xD8, 0x99, 0xD8, 0x9A, /* 0xA4-0xA7 */
+ 0xD8, 0x9B, 0xD8, 0x9C, 0xD8, 0x9D, 0xD8, 0x9E, /* 0xA8-0xAB */
+ 0xD8, 0x9F, 0xD8, 0xA0, 0xD9, 0x40, 0xD9, 0x41, /* 0xAC-0xAF */
+ 0xD9, 0x42, 0xD9, 0x43, 0xD9, 0x44, 0xD9, 0x45, /* 0xB0-0xB3 */
+ 0xD9, 0x46, 0xD9, 0x47, 0xD9, 0x48, 0xD9, 0x49, /* 0xB4-0xB7 */
+ 0xD9, 0x4A, 0xD9, 0x4B, 0xD9, 0x4C, 0xD9, 0x4D, /* 0xB8-0xBB */
+ 0xD9, 0x4E, 0xD9, 0x4F, 0xD9, 0x50, 0xD9, 0x51, /* 0xBC-0xBF */
+ 0xD9, 0x52, 0xD9, 0x53, 0xD9, 0x54, 0xD9, 0x55, /* 0xC0-0xC3 */
+ 0xD9, 0x56, 0xD9, 0x57, 0xD9, 0x58, 0xD9, 0x59, /* 0xC4-0xC7 */
+ 0xD9, 0x5A, 0xD9, 0x5B, 0xD9, 0x5C, 0xD9, 0x5D, /* 0xC8-0xCB */
+ 0xD9, 0x5E, 0xD9, 0x5F, 0xD9, 0x60, 0xD9, 0x61, /* 0xCC-0xCF */
+ 0xD9, 0x62, 0xD9, 0x63, 0xD9, 0x64, 0xD9, 0x65, /* 0xD0-0xD3 */
+ 0xD9, 0x66, 0xD9, 0x67, 0xD9, 0x68, 0xD9, 0x69, /* 0xD4-0xD7 */
+ 0xD9, 0x6A, 0xD9, 0x6B, 0xD9, 0x6C, 0xD9, 0x6D, /* 0xD8-0xDB */
+ 0xD9, 0x6E, 0xD9, 0x6F, 0xD9, 0x70, 0xD9, 0x71, /* 0xDC-0xDF */
+ 0xD9, 0x72, 0xD9, 0x73, 0xD9, 0x74, 0xD9, 0x75, /* 0xE0-0xE3 */
+ 0xD9, 0x76, 0xD9, 0x77, 0xD9, 0x78, 0xD9, 0x79, /* 0xE4-0xE7 */
+ 0xD9, 0x7A, 0xD9, 0x7B, 0xD9, 0x7C, 0xD9, 0x7D, /* 0xE8-0xEB */
+ 0xD9, 0x7E, 0xD9, 0x80, 0xD9, 0x81, 0xD9, 0x82, /* 0xEC-0xEF */
+ 0xD9, 0x83, 0xD9, 0x84, 0xD9, 0x85, 0xD9, 0x86, /* 0xF0-0xF3 */
+ 0xD9, 0x87, 0xD9, 0x88, 0xD9, 0x89, 0xD9, 0x8A, /* 0xF4-0xF7 */
+ 0xD9, 0x8B, 0xD9, 0x8C, 0xD9, 0x8D, 0xD9, 0x8E, /* 0xF8-0xFB */
+ 0xD9, 0x8F, 0xD9, 0x90, 0xD9, 0x91, 0xD9, 0x92, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_8D[512] = {
+ 0xD9, 0x93, 0xD9, 0x94, 0xD9, 0x95, 0xD9, 0x96, /* 0x00-0x03 */
+ 0xD9, 0x97, 0xD9, 0x98, 0xD9, 0x99, 0xD9, 0x9A, /* 0x04-0x07 */
+ 0xD9, 0x9B, 0xD9, 0x9C, 0xD9, 0x9D, 0xD9, 0x9E, /* 0x08-0x0B */
+ 0xD9, 0x9F, 0xD9, 0xA0, 0xDA, 0x40, 0xDA, 0x41, /* 0x0C-0x0F */
+ 0xDA, 0x42, 0xDA, 0x43, 0xDA, 0x44, 0xDA, 0x45, /* 0x10-0x13 */
+ 0xDA, 0x46, 0xDA, 0x47, 0xDA, 0x48, 0xDA, 0x49, /* 0x14-0x17 */
+ 0xDA, 0x4A, 0xDA, 0x4B, 0xDA, 0x4C, 0xDA, 0x4D, /* 0x18-0x1B */
+ 0xDA, 0x4E, 0xB1, 0xB4, 0xD5, 0xEA, 0xB8, 0xBA, /* 0x1C-0x1F */
+ 0xDA, 0x4F, 0xB9, 0xB1, 0xB2, 0xC6, 0xD4, 0xF0, /* 0x20-0x23 */
+ 0xCF, 0xCD, 0xB0, 0xDC, 0xD5, 0xCB, 0xBB, 0xF5, /* 0x24-0x27 */
+ 0xD6, 0xCA, 0xB7, 0xB7, 0xCC, 0xB0, 0xC6, 0xB6, /* 0x28-0x2B */
+ 0xB1, 0xE1, 0xB9, 0xBA, 0xD6, 0xFC, 0xB9, 0xE1, /* 0x2C-0x2F */
+ 0xB7, 0xA1, 0xBC, 0xFA, 0xEA, 0xDA, 0xEA, 0xDB, /* 0x30-0x33 */
+ 0xCC, 0xF9, 0xB9, 0xF3, 0xEA, 0xDC, 0xB4, 0xFB, /* 0x34-0x37 */
+ 0xC3, 0xB3, 0xB7, 0xD1, 0xBA, 0xD8, 0xEA, 0xDD, /* 0x38-0x3B */
+ 0xD4, 0xF4, 0xEA, 0xDE, 0xBC, 0xD6, 0xBB, 0xDF, /* 0x3C-0x3F */
+ 0xEA, 0xDF, 0xC1, 0xDE, 0xC2, 0xB8, 0xD4, 0xDF, /* 0x40-0x43 */
+ 0xD7, 0xCA, 0xEA, 0xE0, 0xEA, 0xE1, 0xEA, 0xE4, /* 0x44-0x47 */
+ 0xEA, 0xE2, 0xEA, 0xE3, 0xC9, 0xDE, 0xB8, 0xB3, /* 0x48-0x4B */
+ 0xB6, 0xC4, 0xEA, 0xE5, 0xCA, 0xEA, 0xC9, 0xCD, /* 0x4C-0x4F */
+ 0xB4, 0xCD, 0xDA, 0x50, 0xDA, 0x51, 0xE2, 0xD9, /* 0x50-0x53 */
+ 0xC5, 0xE2, 0xEA, 0xE6, 0xC0, 0xB5, 0xDA, 0x52, /* 0x54-0x57 */
+ 0xD7, 0xB8, 0xEA, 0xE7, 0xD7, 0xAC, 0xC8, 0xFC, /* 0x58-0x5B */
+ 0xD8, 0xD3, 0xD8, 0xCD, 0xD4, 0xDE, 0xDA, 0x53, /* 0x5C-0x5F */
+ 0xD4, 0xF9, 0xC9, 0xC4, 0xD3, 0xAE, 0xB8, 0xD3, /* 0x60-0x63 */
+ 0xB3, 0xE0, 0xDA, 0x54, 0xC9, 0xE2, 0xF4, 0xF6, /* 0x64-0x67 */
+ 0xDA, 0x55, 0xDA, 0x56, 0xDA, 0x57, 0xBA, 0xD5, /* 0x68-0x6B */
+ 0xDA, 0x58, 0xF4, 0xF7, 0xDA, 0x59, 0xDA, 0x5A, /* 0x6C-0x6F */
+ 0xD7, 0xDF, 0xDA, 0x5B, 0xDA, 0x5C, 0xF4, 0xF1, /* 0x70-0x73 */
+ 0xB8, 0xB0, 0xD5, 0xD4, 0xB8, 0xCF, 0xC6, 0xF0, /* 0x74-0x77 */
+ 0xDA, 0x5D, 0xDA, 0x5E, 0xDA, 0x5F, 0xDA, 0x60, /* 0x78-0x7B */
+ 0xDA, 0x61, 0xDA, 0x62, 0xDA, 0x63, 0xDA, 0x64, /* 0x7C-0x7F */
+
+ 0xDA, 0x65, 0xB3, 0xC3, 0xDA, 0x66, 0xDA, 0x67, /* 0x80-0x83 */
+ 0xF4, 0xF2, 0xB3, 0xAC, 0xDA, 0x68, 0xDA, 0x69, /* 0x84-0x87 */
+ 0xDA, 0x6A, 0xDA, 0x6B, 0xD4, 0xBD, 0xC7, 0xF7, /* 0x88-0x8B */
+ 0xDA, 0x6C, 0xDA, 0x6D, 0xDA, 0x6E, 0xDA, 0x6F, /* 0x8C-0x8F */
+ 0xDA, 0x70, 0xF4, 0xF4, 0xDA, 0x71, 0xDA, 0x72, /* 0x90-0x93 */
+ 0xF4, 0xF3, 0xDA, 0x73, 0xDA, 0x74, 0xDA, 0x75, /* 0x94-0x97 */
+ 0xDA, 0x76, 0xDA, 0x77, 0xDA, 0x78, 0xDA, 0x79, /* 0x98-0x9B */
+ 0xDA, 0x7A, 0xDA, 0x7B, 0xDA, 0x7C, 0xCC, 0xCB, /* 0x9C-0x9F */
+ 0xDA, 0x7D, 0xDA, 0x7E, 0xDA, 0x80, 0xC8, 0xA4, /* 0xA0-0xA3 */
+ 0xDA, 0x81, 0xDA, 0x82, 0xDA, 0x83, 0xDA, 0x84, /* 0xA4-0xA7 */
+ 0xDA, 0x85, 0xDA, 0x86, 0xDA, 0x87, 0xDA, 0x88, /* 0xA8-0xAB */
+ 0xDA, 0x89, 0xDA, 0x8A, 0xDA, 0x8B, 0xDA, 0x8C, /* 0xAC-0xAF */
+ 0xDA, 0x8D, 0xF4, 0xF5, 0xDA, 0x8E, 0xD7, 0xE3, /* 0xB0-0xB3 */
+ 0xC5, 0xBF, 0xF5, 0xC0, 0xDA, 0x8F, 0xDA, 0x90, /* 0xB4-0xB7 */
+ 0xF5, 0xBB, 0xDA, 0x91, 0xF5, 0xC3, 0xDA, 0x92, /* 0xB8-0xBB */
+ 0xF5, 0xC2, 0xDA, 0x93, 0xD6, 0xBA, 0xF5, 0xC1, /* 0xBC-0xBF */
+ 0xDA, 0x94, 0xDA, 0x95, 0xDA, 0x96, 0xD4, 0xBE, /* 0xC0-0xC3 */
+ 0xF5, 0xC4, 0xDA, 0x97, 0xF5, 0xCC, 0xDA, 0x98, /* 0xC4-0xC7 */
+ 0xDA, 0x99, 0xDA, 0x9A, 0xDA, 0x9B, 0xB0, 0xCF, /* 0xC8-0xCB */
+ 0xB5, 0xF8, 0xDA, 0x9C, 0xF5, 0xC9, 0xF5, 0xCA, /* 0xCC-0xCF */
+ 0xDA, 0x9D, 0xC5, 0xDC, 0xDA, 0x9E, 0xDA, 0x9F, /* 0xD0-0xD3 */
+ 0xDA, 0xA0, 0xDB, 0x40, 0xF5, 0xC5, 0xF5, 0xC6, /* 0xD4-0xD7 */
+ 0xDB, 0x41, 0xDB, 0x42, 0xF5, 0xC7, 0xF5, 0xCB, /* 0xD8-0xDB */
+ 0xDB, 0x43, 0xBE, 0xE0, 0xF5, 0xC8, 0xB8, 0xFA, /* 0xDC-0xDF */
+ 0xDB, 0x44, 0xDB, 0x45, 0xDB, 0x46, 0xF5, 0xD0, /* 0xE0-0xE3 */
+ 0xF5, 0xD3, 0xDB, 0x47, 0xDB, 0x48, 0xDB, 0x49, /* 0xE4-0xE7 */
+ 0xBF, 0xE7, 0xDB, 0x4A, 0xB9, 0xF2, 0xF5, 0xBC, /* 0xE8-0xEB */
+ 0xF5, 0xCD, 0xDB, 0x4B, 0xDB, 0x4C, 0xC2, 0xB7, /* 0xEC-0xEF */
+ 0xDB, 0x4D, 0xDB, 0x4E, 0xDB, 0x4F, 0xCC, 0xF8, /* 0xF0-0xF3 */
+ 0xDB, 0x50, 0xBC, 0xF9, 0xDB, 0x51, 0xF5, 0xCE, /* 0xF4-0xF7 */
+ 0xF5, 0xCF, 0xF5, 0xD1, 0xB6, 0xE5, 0xF5, 0xD2, /* 0xF8-0xFB */
+ 0xDB, 0x52, 0xF5, 0xD5, 0xDB, 0x53, 0xDB, 0x54, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_8E[512] = {
+ 0xDB, 0x55, 0xDB, 0x56, 0xDB, 0x57, 0xDB, 0x58, /* 0x00-0x03 */
+ 0xDB, 0x59, 0xF5, 0xBD, 0xDB, 0x5A, 0xDB, 0x5B, /* 0x04-0x07 */
+ 0xDB, 0x5C, 0xF5, 0xD4, 0xD3, 0xBB, 0xDB, 0x5D, /* 0x08-0x0B */
+ 0xB3, 0xEC, 0xDB, 0x5E, 0xDB, 0x5F, 0xCC, 0xA4, /* 0x0C-0x0F */
+ 0xDB, 0x60, 0xDB, 0x61, 0xDB, 0x62, 0xDB, 0x63, /* 0x10-0x13 */
+ 0xF5, 0xD6, 0xDB, 0x64, 0xDB, 0x65, 0xDB, 0x66, /* 0x14-0x17 */
+ 0xDB, 0x67, 0xDB, 0x68, 0xDB, 0x69, 0xDB, 0x6A, /* 0x18-0x1B */
+ 0xDB, 0x6B, 0xF5, 0xD7, 0xBE, 0xE1, 0xF5, 0xD8, /* 0x1C-0x1F */
+ 0xDB, 0x6C, 0xDB, 0x6D, 0xCC, 0xDF, 0xF5, 0xDB, /* 0x20-0x23 */
+ 0xDB, 0x6E, 0xDB, 0x6F, 0xDB, 0x70, 0xDB, 0x71, /* 0x24-0x27 */
+ 0xDB, 0x72, 0xB2, 0xC8, 0xD7, 0xD9, 0xDB, 0x73, /* 0x28-0x2B */
+ 0xF5, 0xD9, 0xDB, 0x74, 0xF5, 0xDA, 0xF5, 0xDC, /* 0x2C-0x2F */
+ 0xDB, 0x75, 0xF5, 0xE2, 0xDB, 0x76, 0xDB, 0x77, /* 0x30-0x33 */
+ 0xDB, 0x78, 0xF5, 0xE0, 0xDB, 0x79, 0xDB, 0x7A, /* 0x34-0x37 */
+ 0xDB, 0x7B, 0xF5, 0xDF, 0xF5, 0xDD, 0xDB, 0x7C, /* 0x38-0x3B */
+ 0xDB, 0x7D, 0xF5, 0xE1, 0xDB, 0x7E, 0xDB, 0x80, /* 0x3C-0x3F */
+ 0xF5, 0xDE, 0xF5, 0xE4, 0xF5, 0xE5, 0xDB, 0x81, /* 0x40-0x43 */
+ 0xCC, 0xE3, 0xDB, 0x82, 0xDB, 0x83, 0xE5, 0xBF, /* 0x44-0x47 */
+ 0xB5, 0xB8, 0xF5, 0xE3, 0xF5, 0xE8, 0xCC, 0xA3, /* 0x48-0x4B */
+ 0xDB, 0x84, 0xDB, 0x85, 0xDB, 0x86, 0xDB, 0x87, /* 0x4C-0x4F */
+ 0xDB, 0x88, 0xF5, 0xE6, 0xF5, 0xE7, 0xDB, 0x89, /* 0x50-0x53 */
+ 0xDB, 0x8A, 0xDB, 0x8B, 0xDB, 0x8C, 0xDB, 0x8D, /* 0x54-0x57 */
+ 0xDB, 0x8E, 0xF5, 0xBE, 0xDB, 0x8F, 0xDB, 0x90, /* 0x58-0x5B */
+ 0xDB, 0x91, 0xDB, 0x92, 0xDB, 0x93, 0xDB, 0x94, /* 0x5C-0x5F */
+ 0xDB, 0x95, 0xDB, 0x96, 0xDB, 0x97, 0xDB, 0x98, /* 0x60-0x63 */
+ 0xDB, 0x99, 0xDB, 0x9A, 0xB1, 0xC4, 0xDB, 0x9B, /* 0x64-0x67 */
+ 0xDB, 0x9C, 0xF5, 0xBF, 0xDB, 0x9D, 0xDB, 0x9E, /* 0x68-0x6B */
+ 0xB5, 0xC5, 0xB2, 0xE4, 0xDB, 0x9F, 0xF5, 0xEC, /* 0x6C-0x6F */
+ 0xF5, 0xE9, 0xDB, 0xA0, 0xB6, 0xD7, 0xDC, 0x40, /* 0x70-0x73 */
+ 0xF5, 0xED, 0xDC, 0x41, 0xF5, 0xEA, 0xDC, 0x42, /* 0x74-0x77 */
+ 0xDC, 0x43, 0xDC, 0x44, 0xDC, 0x45, 0xDC, 0x46, /* 0x78-0x7B */
+ 0xF5, 0xEB, 0xDC, 0x47, 0xDC, 0x48, 0xB4, 0xDA, /* 0x7C-0x7F */
+
+ 0xDC, 0x49, 0xD4, 0xEA, 0xDC, 0x4A, 0xDC, 0x4B, /* 0x80-0x83 */
+ 0xDC, 0x4C, 0xF5, 0xEE, 0xDC, 0x4D, 0xB3, 0xF9, /* 0x84-0x87 */
+ 0xDC, 0x4E, 0xDC, 0x4F, 0xDC, 0x50, 0xDC, 0x51, /* 0x88-0x8B */
+ 0xDC, 0x52, 0xDC, 0x53, 0xDC, 0x54, 0xF5, 0xEF, /* 0x8C-0x8F */
+ 0xF5, 0xF1, 0xDC, 0x55, 0xDC, 0x56, 0xDC, 0x57, /* 0x90-0x93 */
+ 0xF5, 0xF0, 0xDC, 0x58, 0xDC, 0x59, 0xDC, 0x5A, /* 0x94-0x97 */
+ 0xDC, 0x5B, 0xDC, 0x5C, 0xDC, 0x5D, 0xDC, 0x5E, /* 0x98-0x9B */
+ 0xF5, 0xF2, 0xDC, 0x5F, 0xF5, 0xF3, 0xDC, 0x60, /* 0x9C-0x9F */
+ 0xDC, 0x61, 0xDC, 0x62, 0xDC, 0x63, 0xDC, 0x64, /* 0xA0-0xA3 */
+ 0xDC, 0x65, 0xDC, 0x66, 0xDC, 0x67, 0xDC, 0x68, /* 0xA4-0xA7 */
+ 0xDC, 0x69, 0xDC, 0x6A, 0xDC, 0x6B, 0xC9, 0xED, /* 0xA8-0xAB */
+ 0xB9, 0xAA, 0xDC, 0x6C, 0xDC, 0x6D, 0xC7, 0xFB, /* 0xAC-0xAF */
+ 0xDC, 0x6E, 0xDC, 0x6F, 0xB6, 0xE3, 0xDC, 0x70, /* 0xB0-0xB3 */
+ 0xDC, 0x71, 0xDC, 0x72, 0xDC, 0x73, 0xDC, 0x74, /* 0xB4-0xB7 */
+ 0xDC, 0x75, 0xDC, 0x76, 0xCC, 0xC9, 0xDC, 0x77, /* 0xB8-0xBB */
+ 0xDC, 0x78, 0xDC, 0x79, 0xDC, 0x7A, 0xDC, 0x7B, /* 0xBC-0xBF */
+ 0xDC, 0x7C, 0xDC, 0x7D, 0xDC, 0x7E, 0xDC, 0x80, /* 0xC0-0xC3 */
+ 0xDC, 0x81, 0xDC, 0x82, 0xDC, 0x83, 0xDC, 0x84, /* 0xC4-0xC7 */
+ 0xDC, 0x85, 0xDC, 0x86, 0xDC, 0x87, 0xDC, 0x88, /* 0xC8-0xCB */
+ 0xDC, 0x89, 0xDC, 0x8A, 0xEA, 0xA6, 0xDC, 0x8B, /* 0xCC-0xCF */
+ 0xDC, 0x8C, 0xDC, 0x8D, 0xDC, 0x8E, 0xDC, 0x8F, /* 0xD0-0xD3 */
+ 0xDC, 0x90, 0xDC, 0x91, 0xDC, 0x92, 0xDC, 0x93, /* 0xD4-0xD7 */
+ 0xDC, 0x94, 0xDC, 0x95, 0xDC, 0x96, 0xDC, 0x97, /* 0xD8-0xDB */
+ 0xDC, 0x98, 0xDC, 0x99, 0xDC, 0x9A, 0xDC, 0x9B, /* 0xDC-0xDF */
+ 0xDC, 0x9C, 0xDC, 0x9D, 0xDC, 0x9E, 0xDC, 0x9F, /* 0xE0-0xE3 */
+ 0xDC, 0xA0, 0xDD, 0x40, 0xDD, 0x41, 0xDD, 0x42, /* 0xE4-0xE7 */
+ 0xDD, 0x43, 0xDD, 0x44, 0xDD, 0x45, 0xDD, 0x46, /* 0xE8-0xEB */
+ 0xDD, 0x47, 0xDD, 0x48, 0xDD, 0x49, 0xDD, 0x4A, /* 0xEC-0xEF */
+ 0xDD, 0x4B, 0xDD, 0x4C, 0xDD, 0x4D, 0xDD, 0x4E, /* 0xF0-0xF3 */
+ 0xDD, 0x4F, 0xDD, 0x50, 0xDD, 0x51, 0xDD, 0x52, /* 0xF4-0xF7 */
+ 0xDD, 0x53, 0xDD, 0x54, 0xDD, 0x55, 0xDD, 0x56, /* 0xF8-0xFB */
+ 0xDD, 0x57, 0xDD, 0x58, 0xDD, 0x59, 0xDD, 0x5A, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_8F[512] = {
+ 0xDD, 0x5B, 0xDD, 0x5C, 0xDD, 0x5D, 0xDD, 0x5E, /* 0x00-0x03 */
+ 0xDD, 0x5F, 0xDD, 0x60, 0xDD, 0x61, 0xDD, 0x62, /* 0x04-0x07 */
+ 0xDD, 0x63, 0xDD, 0x64, 0xDD, 0x65, 0xDD, 0x66, /* 0x08-0x0B */
+ 0xDD, 0x67, 0xDD, 0x68, 0xDD, 0x69, 0xDD, 0x6A, /* 0x0C-0x0F */
+ 0xDD, 0x6B, 0xDD, 0x6C, 0xDD, 0x6D, 0xDD, 0x6E, /* 0x10-0x13 */
+ 0xDD, 0x6F, 0xDD, 0x70, 0xDD, 0x71, 0xDD, 0x72, /* 0x14-0x17 */
+ 0xDD, 0x73, 0xDD, 0x74, 0xDD, 0x75, 0xDD, 0x76, /* 0x18-0x1B */
+ 0xDD, 0x77, 0xDD, 0x78, 0xDD, 0x79, 0xDD, 0x7A, /* 0x1C-0x1F */
+ 0xDD, 0x7B, 0xDD, 0x7C, 0xDD, 0x7D, 0xDD, 0x7E, /* 0x20-0x23 */
+ 0xDD, 0x80, 0xDD, 0x81, 0xDD, 0x82, 0xDD, 0x83, /* 0x24-0x27 */
+ 0xDD, 0x84, 0xDD, 0x85, 0xDD, 0x86, 0xDD, 0x87, /* 0x28-0x2B */
+ 0xDD, 0x88, 0xDD, 0x89, 0xDD, 0x8A, 0xDD, 0x8B, /* 0x2C-0x2F */
+ 0xDD, 0x8C, 0xDD, 0x8D, 0xDD, 0x8E, 0xDD, 0x8F, /* 0x30-0x33 */
+ 0xDD, 0x90, 0xDD, 0x91, 0xDD, 0x92, 0xDD, 0x93, /* 0x34-0x37 */
+ 0xDD, 0x94, 0xDD, 0x95, 0xDD, 0x96, 0xDD, 0x97, /* 0x38-0x3B */
+ 0xDD, 0x98, 0xDD, 0x99, 0xDD, 0x9A, 0xDD, 0x9B, /* 0x3C-0x3F */
+ 0xDD, 0x9C, 0xDD, 0x9D, 0xDD, 0x9E, 0xDD, 0x9F, /* 0x40-0x43 */
+ 0xDD, 0xA0, 0xDE, 0x40, 0xDE, 0x41, 0xDE, 0x42, /* 0x44-0x47 */
+ 0xDE, 0x43, 0xDE, 0x44, 0xDE, 0x45, 0xDE, 0x46, /* 0x48-0x4B */
+ 0xDE, 0x47, 0xDE, 0x48, 0xDE, 0x49, 0xDE, 0x4A, /* 0x4C-0x4F */
+ 0xDE, 0x4B, 0xDE, 0x4C, 0xDE, 0x4D, 0xDE, 0x4E, /* 0x50-0x53 */
+ 0xDE, 0x4F, 0xDE, 0x50, 0xDE, 0x51, 0xDE, 0x52, /* 0x54-0x57 */
+ 0xDE, 0x53, 0xDE, 0x54, 0xDE, 0x55, 0xDE, 0x56, /* 0x58-0x5B */
+ 0xDE, 0x57, 0xDE, 0x58, 0xDE, 0x59, 0xDE, 0x5A, /* 0x5C-0x5F */
+ 0xDE, 0x5B, 0xDE, 0x5C, 0xDE, 0x5D, 0xDE, 0x5E, /* 0x60-0x63 */
+ 0xDE, 0x5F, 0xDE, 0x60, 0xB3, 0xB5, 0xD4, 0xFE, /* 0x64-0x67 */
+ 0xB9, 0xEC, 0xD0, 0xF9, 0xDE, 0x61, 0xE9, 0xED, /* 0x68-0x6B */
+ 0xD7, 0xAA, 0xE9, 0xEE, 0xC2, 0xD6, 0xC8, 0xED, /* 0x6C-0x6F */
+ 0xBA, 0xE4, 0xE9, 0xEF, 0xE9, 0xF0, 0xE9, 0xF1, /* 0x70-0x73 */
+ 0xD6, 0xE1, 0xE9, 0xF2, 0xE9, 0xF3, 0xE9, 0xF5, /* 0x74-0x77 */
+ 0xE9, 0xF4, 0xE9, 0xF6, 0xE9, 0xF7, 0xC7, 0xE1, /* 0x78-0x7B */
+ 0xE9, 0xF8, 0xD4, 0xD8, 0xE9, 0xF9, 0xBD, 0xCE, /* 0x7C-0x7F */
+
+ 0xDE, 0x62, 0xE9, 0xFA, 0xE9, 0xFB, 0xBD, 0xCF, /* 0x80-0x83 */
+ 0xE9, 0xFC, 0xB8, 0xA8, 0xC1, 0xBE, 0xE9, 0xFD, /* 0x84-0x87 */
+ 0xB1, 0xB2, 0xBB, 0xD4, 0xB9, 0xF5, 0xE9, 0xFE, /* 0x88-0x8B */
+ 0xDE, 0x63, 0xEA, 0xA1, 0xEA, 0xA2, 0xEA, 0xA3, /* 0x8C-0x8F */
+ 0xB7, 0xF8, 0xBC, 0xAD, 0xDE, 0x64, 0xCA, 0xE4, /* 0x90-0x93 */
+ 0xE0, 0xCE, 0xD4, 0xAF, 0xCF, 0xBD, 0xD5, 0xB7, /* 0x94-0x97 */
+ 0xEA, 0xA4, 0xD5, 0xDE, 0xEA, 0xA5, 0xD0, 0xC1, /* 0x98-0x9B */
+ 0xB9, 0xBC, 0xDE, 0x65, 0xB4, 0xC7, 0xB1, 0xD9, /* 0x9C-0x9F */
+ 0xDE, 0x66, 0xDE, 0x67, 0xDE, 0x68, 0xC0, 0xB1, /* 0xA0-0xA3 */
+ 0xDE, 0x69, 0xDE, 0x6A, 0xDE, 0x6B, 0xDE, 0x6C, /* 0xA4-0xA7 */
+ 0xB1, 0xE6, 0xB1, 0xE7, 0xDE, 0x6D, 0xB1, 0xE8, /* 0xA8-0xAB */
+ 0xDE, 0x6E, 0xDE, 0x6F, 0xDE, 0x70, 0xDE, 0x71, /* 0xAC-0xAF */
+ 0xB3, 0xBD, 0xC8, 0xE8, 0xDE, 0x72, 0xDE, 0x73, /* 0xB0-0xB3 */
+ 0xDE, 0x74, 0xDE, 0x75, 0xE5, 0xC1, 0xDE, 0x76, /* 0xB4-0xB7 */
+ 0xDE, 0x77, 0xB1, 0xDF, 0xDE, 0x78, 0xDE, 0x79, /* 0xB8-0xBB */
+ 0xDE, 0x7A, 0xC1, 0xC9, 0xB4, 0xEF, 0xDE, 0x7B, /* 0xBC-0xBF */
+ 0xDE, 0x7C, 0xC7, 0xA8, 0xD3, 0xD8, 0xDE, 0x7D, /* 0xC0-0xC3 */
+ 0xC6, 0xF9, 0xD1, 0xB8, 0xDE, 0x7E, 0xB9, 0xFD, /* 0xC4-0xC7 */
+ 0xC2, 0xF5, 0xDE, 0x80, 0xDE, 0x81, 0xDE, 0x82, /* 0xC8-0xCB */
+ 0xDE, 0x83, 0xDE, 0x84, 0xD3, 0xAD, 0xDE, 0x85, /* 0xCC-0xCF */
+ 0xD4, 0xCB, 0xBD, 0xFC, 0xDE, 0x86, 0xE5, 0xC2, /* 0xD0-0xD3 */
+ 0xB7, 0xB5, 0xE5, 0xC3, 0xDE, 0x87, 0xDE, 0x88, /* 0xD4-0xD7 */
+ 0xBB, 0xB9, 0xD5, 0xE2, 0xDE, 0x89, 0xBD, 0xF8, /* 0xD8-0xDB */
+ 0xD4, 0xB6, 0xCE, 0xA5, 0xC1, 0xAC, 0xB3, 0xD9, /* 0xDC-0xDF */
+ 0xDE, 0x8A, 0xDE, 0x8B, 0xCC, 0xF6, 0xDE, 0x8C, /* 0xE0-0xE3 */
+ 0xE5, 0xC6, 0xE5, 0xC4, 0xE5, 0xC8, 0xDE, 0x8D, /* 0xE4-0xE7 */
+ 0xE5, 0xCA, 0xE5, 0xC7, 0xB5, 0xCF, 0xC6, 0xC8, /* 0xE8-0xEB */
+ 0xDE, 0x8E, 0xB5, 0xFC, 0xE5, 0xC5, 0xDE, 0x8F, /* 0xEC-0xEF */
+ 0xCA, 0xF6, 0xDE, 0x90, 0xDE, 0x91, 0xE5, 0xC9, /* 0xF0-0xF3 */
+ 0xDE, 0x92, 0xDE, 0x93, 0xDE, 0x94, 0xC3, 0xD4, /* 0xF4-0xF7 */
+ 0xB1, 0xC5, 0xBC, 0xA3, 0xDE, 0x95, 0xDE, 0x96, /* 0xF8-0xFB */
+ 0xDE, 0x97, 0xD7, 0xB7, 0xDE, 0x98, 0xDE, 0x99, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_90[512] = {
+ 0xCD, 0xCB, 0xCB, 0xCD, 0xCA, 0xCA, 0xCC, 0xD3, /* 0x00-0x03 */
+ 0xE5, 0xCC, 0xE5, 0xCB, 0xC4, 0xE6, 0xDE, 0x9A, /* 0x04-0x07 */
+ 0xDE, 0x9B, 0xD1, 0xA1, 0xD1, 0xB7, 0xE5, 0xCD, /* 0x08-0x0B */
+ 0xDE, 0x9C, 0xE5, 0xD0, 0xDE, 0x9D, 0xCD, 0xB8, /* 0x0C-0x0F */
+ 0xD6, 0xF0, 0xE5, 0xCF, 0xB5, 0xDD, 0xDE, 0x9E, /* 0x10-0x13 */
+ 0xCD, 0xBE, 0xDE, 0x9F, 0xE5, 0xD1, 0xB6, 0xBA, /* 0x14-0x17 */
+ 0xDE, 0xA0, 0xDF, 0x40, 0xCD, 0xA8, 0xB9, 0xE4, /* 0x18-0x1B */
+ 0xDF, 0x41, 0xCA, 0xC5, 0xB3, 0xD1, 0xCB, 0xD9, /* 0x1C-0x1F */
+ 0xD4, 0xEC, 0xE5, 0xD2, 0xB7, 0xEA, 0xDF, 0x42, /* 0x20-0x23 */
+ 0xDF, 0x43, 0xDF, 0x44, 0xE5, 0xCE, 0xDF, 0x45, /* 0x24-0x27 */
+ 0xDF, 0x46, 0xDF, 0x47, 0xDF, 0x48, 0xDF, 0x49, /* 0x28-0x2B */
+ 0xDF, 0x4A, 0xE5, 0xD5, 0xB4, 0xFE, 0xE5, 0xD6, /* 0x2C-0x2F */
+ 0xDF, 0x4B, 0xDF, 0x4C, 0xDF, 0x4D, 0xDF, 0x4E, /* 0x30-0x33 */
+ 0xDF, 0x4F, 0xE5, 0xD3, 0xE5, 0xD4, 0xDF, 0x50, /* 0x34-0x37 */
+ 0xD2, 0xDD, 0xDF, 0x51, 0xDF, 0x52, 0xC2, 0xDF, /* 0x38-0x3B */
+ 0xB1, 0xC6, 0xDF, 0x53, 0xD3, 0xE2, 0xDF, 0x54, /* 0x3C-0x3F */
+ 0xDF, 0x55, 0xB6, 0xDD, 0xCB, 0xEC, 0xDF, 0x56, /* 0x40-0x43 */
+ 0xE5, 0xD7, 0xDF, 0x57, 0xDF, 0x58, 0xD3, 0xF6, /* 0x44-0x47 */
+ 0xDF, 0x59, 0xDF, 0x5A, 0xDF, 0x5B, 0xDF, 0x5C, /* 0x48-0x4B */
+ 0xDF, 0x5D, 0xB1, 0xE9, 0xDF, 0x5E, 0xB6, 0xF4, /* 0x4C-0x4F */
+ 0xE5, 0xDA, 0xE5, 0xD8, 0xE5, 0xD9, 0xB5, 0xC0, /* 0x50-0x53 */
+ 0xDF, 0x5F, 0xDF, 0x60, 0xDF, 0x61, 0xD2, 0xC5, /* 0x54-0x57 */
+ 0xE5, 0xDC, 0xDF, 0x62, 0xDF, 0x63, 0xE5, 0xDE, /* 0x58-0x5B */
+ 0xDF, 0x64, 0xDF, 0x65, 0xDF, 0x66, 0xDF, 0x67, /* 0x5C-0x5F */
+ 0xDF, 0x68, 0xDF, 0x69, 0xE5, 0xDD, 0xC7, 0xB2, /* 0x60-0x63 */
+ 0xDF, 0x6A, 0xD2, 0xA3, 0xDF, 0x6B, 0xDF, 0x6C, /* 0x64-0x67 */
+ 0xE5, 0xDB, 0xDF, 0x6D, 0xDF, 0x6E, 0xDF, 0x6F, /* 0x68-0x6B */
+ 0xDF, 0x70, 0xD4, 0xE2, 0xD5, 0xDA, 0xDF, 0x71, /* 0x6C-0x6F */
+ 0xDF, 0x72, 0xDF, 0x73, 0xDF, 0x74, 0xDF, 0x75, /* 0x70-0x73 */
+ 0xE5, 0xE0, 0xD7, 0xF1, 0xDF, 0x76, 0xDF, 0x77, /* 0x74-0x77 */
+ 0xDF, 0x78, 0xDF, 0x79, 0xDF, 0x7A, 0xDF, 0x7B, /* 0x78-0x7B */
+ 0xDF, 0x7C, 0xE5, 0xE1, 0xDF, 0x7D, 0xB1, 0xDC, /* 0x7C-0x7F */
+
+ 0xD1, 0xFB, 0xDF, 0x7E, 0xE5, 0xE2, 0xE5, 0xE4, /* 0x80-0x83 */
+ 0xDF, 0x80, 0xDF, 0x81, 0xDF, 0x82, 0xDF, 0x83, /* 0x84-0x87 */
+ 0xE5, 0xE3, 0xDF, 0x84, 0xDF, 0x85, 0xE5, 0xE5, /* 0x88-0x8B */
+ 0xDF, 0x86, 0xDF, 0x87, 0xDF, 0x88, 0xDF, 0x89, /* 0x8C-0x8F */
+ 0xDF, 0x8A, 0xD2, 0xD8, 0xDF, 0x8B, 0xB5, 0xCB, /* 0x90-0x93 */
+ 0xDF, 0x8C, 0xE7, 0xDF, 0xDF, 0x8D, 0xDA, 0xF5, /* 0x94-0x97 */
+ 0xDF, 0x8E, 0xDA, 0xF8, 0xDF, 0x8F, 0xDA, 0xF6, /* 0x98-0x9B */
+ 0xDF, 0x90, 0xDA, 0xF7, 0xDF, 0x91, 0xDF, 0x92, /* 0x9C-0x9F */
+ 0xDF, 0x93, 0xDA, 0xFA, 0xD0, 0xCF, 0xC4, 0xC7, /* 0xA0-0xA3 */
+ 0xDF, 0x94, 0xDF, 0x95, 0xB0, 0xEE, 0xDF, 0x96, /* 0xA4-0xA7 */
+ 0xDF, 0x97, 0xDF, 0x98, 0xD0, 0xB0, 0xDF, 0x99, /* 0xA8-0xAB */
+ 0xDA, 0xF9, 0xDF, 0x9A, 0xD3, 0xCA, 0xBA, 0xAA, /* 0xAC-0xAF */
+ 0xDB, 0xA2, 0xC7, 0xF1, 0xDF, 0x9B, 0xDA, 0xFC, /* 0xB0-0xB3 */
+ 0xDA, 0xFB, 0xC9, 0xDB, 0xDA, 0xFD, 0xDF, 0x9C, /* 0xB4-0xB7 */
+ 0xDB, 0xA1, 0xD7, 0xDE, 0xDA, 0xFE, 0xC1, 0xDA, /* 0xB8-0xBB */
+ 0xDF, 0x9D, 0xDF, 0x9E, 0xDB, 0xA5, 0xDF, 0x9F, /* 0xBC-0xBF */
+ 0xDF, 0xA0, 0xD3, 0xF4, 0xE0, 0x40, 0xE0, 0x41, /* 0xC0-0xC3 */
+ 0xDB, 0xA7, 0xDB, 0xA4, 0xE0, 0x42, 0xDB, 0xA8, /* 0xC4-0xC7 */
+ 0xE0, 0x43, 0xE0, 0x44, 0xBD, 0xBC, 0xE0, 0x45, /* 0xC8-0xCB */
+ 0xE0, 0x46, 0xE0, 0x47, 0xC0, 0xC9, 0xDB, 0xA3, /* 0xCC-0xCF */
+ 0xDB, 0xA6, 0xD6, 0xA3, 0xE0, 0x48, 0xDB, 0xA9, /* 0xD0-0xD3 */
+ 0xE0, 0x49, 0xE0, 0x4A, 0xE0, 0x4B, 0xDB, 0xAD, /* 0xD4-0xD7 */
+ 0xE0, 0x4C, 0xE0, 0x4D, 0xE0, 0x4E, 0xDB, 0xAE, /* 0xD8-0xDB */
+ 0xDB, 0xAC, 0xBA, 0xC2, 0xE0, 0x4F, 0xE0, 0x50, /* 0xDC-0xDF */
+ 0xE0, 0x51, 0xBF, 0xA4, 0xDB, 0xAB, 0xE0, 0x52, /* 0xE0-0xE3 */
+ 0xE0, 0x53, 0xE0, 0x54, 0xDB, 0xAA, 0xD4, 0xC7, /* 0xE4-0xE7 */
+ 0xB2, 0xBF, 0xE0, 0x55, 0xE0, 0x56, 0xDB, 0xAF, /* 0xE8-0xEB */
+ 0xE0, 0x57, 0xB9, 0xF9, 0xE0, 0x58, 0xDB, 0xB0, /* 0xEC-0xEF */
+ 0xE0, 0x59, 0xE0, 0x5A, 0xE0, 0x5B, 0xE0, 0x5C, /* 0xF0-0xF3 */
+ 0xB3, 0xBB, 0xE0, 0x5D, 0xE0, 0x5E, 0xE0, 0x5F, /* 0xF4-0xF7 */
+ 0xB5, 0xA6, 0xE0, 0x60, 0xE0, 0x61, 0xE0, 0x62, /* 0xF8-0xFB */
+ 0xE0, 0x63, 0xB6, 0xBC, 0xDB, 0xB1, 0xE0, 0x64, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_91[512] = {
+ 0xE0, 0x65, 0xE0, 0x66, 0xB6, 0xF5, 0xE0, 0x67, /* 0x00-0x03 */
+ 0xDB, 0xB2, 0xE0, 0x68, 0xE0, 0x69, 0xE0, 0x6A, /* 0x04-0x07 */
+ 0xE0, 0x6B, 0xE0, 0x6C, 0xE0, 0x6D, 0xE0, 0x6E, /* 0x08-0x0B */
+ 0xE0, 0x6F, 0xE0, 0x70, 0xE0, 0x71, 0xE0, 0x72, /* 0x0C-0x0F */
+ 0xE0, 0x73, 0xE0, 0x74, 0xE0, 0x75, 0xE0, 0x76, /* 0x10-0x13 */
+ 0xE0, 0x77, 0xE0, 0x78, 0xE0, 0x79, 0xE0, 0x7A, /* 0x14-0x17 */
+ 0xE0, 0x7B, 0xB1, 0xC9, 0xE0, 0x7C, 0xE0, 0x7D, /* 0x18-0x1B */
+ 0xE0, 0x7E, 0xE0, 0x80, 0xDB, 0xB4, 0xE0, 0x81, /* 0x1C-0x1F */
+ 0xE0, 0x82, 0xE0, 0x83, 0xDB, 0xB3, 0xDB, 0xB5, /* 0x20-0x23 */
+ 0xE0, 0x84, 0xE0, 0x85, 0xE0, 0x86, 0xE0, 0x87, /* 0x24-0x27 */
+ 0xE0, 0x88, 0xE0, 0x89, 0xE0, 0x8A, 0xE0, 0x8B, /* 0x28-0x2B */
+ 0xE0, 0x8C, 0xE0, 0x8D, 0xE0, 0x8E, 0xDB, 0xB7, /* 0x2C-0x2F */
+ 0xE0, 0x8F, 0xDB, 0xB6, 0xE0, 0x90, 0xE0, 0x91, /* 0x30-0x33 */
+ 0xE0, 0x92, 0xE0, 0x93, 0xE0, 0x94, 0xE0, 0x95, /* 0x34-0x37 */
+ 0xE0, 0x96, 0xDB, 0xB8, 0xE0, 0x97, 0xE0, 0x98, /* 0x38-0x3B */
+ 0xE0, 0x99, 0xE0, 0x9A, 0xE0, 0x9B, 0xE0, 0x9C, /* 0x3C-0x3F */
+ 0xE0, 0x9D, 0xE0, 0x9E, 0xE0, 0x9F, 0xDB, 0xB9, /* 0x40-0x43 */
+ 0xE0, 0xA0, 0xE1, 0x40, 0xDB, 0xBA, 0xE1, 0x41, /* 0x44-0x47 */
+ 0xE1, 0x42, 0xD3, 0xCF, 0xF4, 0xFA, 0xC7, 0xF5, /* 0x48-0x4B */
+ 0xD7, 0xC3, 0xC5, 0xE4, 0xF4, 0xFC, 0xF4, 0xFD, /* 0x4C-0x4F */
+ 0xF4, 0xFB, 0xE1, 0x43, 0xBE, 0xC6, 0xE1, 0x44, /* 0x50-0x53 */
+ 0xE1, 0x45, 0xE1, 0x46, 0xE1, 0x47, 0xD0, 0xEF, /* 0x54-0x57 */
+ 0xE1, 0x48, 0xE1, 0x49, 0xB7, 0xD3, 0xE1, 0x4A, /* 0x58-0x5B */
+ 0xE1, 0x4B, 0xD4, 0xCD, 0xCC, 0xAA, 0xE1, 0x4C, /* 0x5C-0x5F */
+ 0xE1, 0x4D, 0xF5, 0xA2, 0xF5, 0xA1, 0xBA, 0xA8, /* 0x60-0x63 */
+ 0xF4, 0xFE, 0xCB, 0xD6, 0xE1, 0x4E, 0xE1, 0x4F, /* 0x64-0x67 */
+ 0xE1, 0x50, 0xF5, 0xA4, 0xC0, 0xD2, 0xE1, 0x51, /* 0x68-0x6B */
+ 0xB3, 0xEA, 0xE1, 0x52, 0xCD, 0xAA, 0xF5, 0xA5, /* 0x6C-0x6F */
+ 0xF5, 0xA3, 0xBD, 0xB4, 0xF5, 0xA8, 0xE1, 0x53, /* 0x70-0x73 */
+ 0xF5, 0xA9, 0xBD, 0xCD, 0xC3, 0xB8, 0xBF, 0xE1, /* 0x74-0x77 */
+ 0xCB, 0xE1, 0xF5, 0xAA, 0xE1, 0x54, 0xE1, 0x55, /* 0x78-0x7B */
+ 0xE1, 0x56, 0xF5, 0xA6, 0xF5, 0xA7, 0xC4, 0xF0, /* 0x7C-0x7F */
+
+ 0xE1, 0x57, 0xE1, 0x58, 0xE1, 0x59, 0xE1, 0x5A, /* 0x80-0x83 */
+ 0xE1, 0x5B, 0xF5, 0xAC, 0xE1, 0x5C, 0xB4, 0xBC, /* 0x84-0x87 */
+ 0xE1, 0x5D, 0xD7, 0xED, 0xE1, 0x5E, 0xB4, 0xD7, /* 0x88-0x8B */
+ 0xF5, 0xAB, 0xF5, 0xAE, 0xE1, 0x5F, 0xE1, 0x60, /* 0x8C-0x8F */
+ 0xF5, 0xAD, 0xF5, 0xAF, 0xD0, 0xD1, 0xE1, 0x61, /* 0x90-0x93 */
+ 0xE1, 0x62, 0xE1, 0x63, 0xE1, 0x64, 0xE1, 0x65, /* 0x94-0x97 */
+ 0xE1, 0x66, 0xE1, 0x67, 0xC3, 0xD1, 0xC8, 0xA9, /* 0x98-0x9B */
+ 0xE1, 0x68, 0xE1, 0x69, 0xE1, 0x6A, 0xE1, 0x6B, /* 0x9C-0x9F */
+ 0xE1, 0x6C, 0xE1, 0x6D, 0xF5, 0xB0, 0xF5, 0xB1, /* 0xA0-0xA3 */
+ 0xE1, 0x6E, 0xE1, 0x6F, 0xE1, 0x70, 0xE1, 0x71, /* 0xA4-0xA7 */
+ 0xE1, 0x72, 0xE1, 0x73, 0xF5, 0xB2, 0xE1, 0x74, /* 0xA8-0xAB */
+ 0xE1, 0x75, 0xF5, 0xB3, 0xF5, 0xB4, 0xF5, 0xB5, /* 0xAC-0xAF */
+ 0xE1, 0x76, 0xE1, 0x77, 0xE1, 0x78, 0xE1, 0x79, /* 0xB0-0xB3 */
+ 0xF5, 0xB7, 0xF5, 0xB6, 0xE1, 0x7A, 0xE1, 0x7B, /* 0xB4-0xB7 */
+ 0xE1, 0x7C, 0xE1, 0x7D, 0xF5, 0xB8, 0xE1, 0x7E, /* 0xB8-0xBB */
+ 0xE1, 0x80, 0xE1, 0x81, 0xE1, 0x82, 0xE1, 0x83, /* 0xBC-0xBF */
+ 0xE1, 0x84, 0xE1, 0x85, 0xE1, 0x86, 0xE1, 0x87, /* 0xC0-0xC3 */
+ 0xE1, 0x88, 0xE1, 0x89, 0xE1, 0x8A, 0xB2, 0xC9, /* 0xC4-0xC7 */
+ 0xE1, 0x8B, 0xD3, 0xD4, 0xCA, 0xCD, 0xE1, 0x8C, /* 0xC8-0xCB */
+ 0xC0, 0xEF, 0xD6, 0xD8, 0xD2, 0xB0, 0xC1, 0xBF, /* 0xCC-0xCF */
+ 0xE1, 0x8D, 0xBD, 0xF0, 0xE1, 0x8E, 0xE1, 0x8F, /* 0xD0-0xD3 */
+ 0xE1, 0x90, 0xE1, 0x91, 0xE1, 0x92, 0xE1, 0x93, /* 0xD4-0xD7 */
+ 0xE1, 0x94, 0xE1, 0x95, 0xE1, 0x96, 0xE1, 0x97, /* 0xD8-0xDB */
+ 0xB8, 0xAA, 0xE1, 0x98, 0xE1, 0x99, 0xE1, 0x9A, /* 0xDC-0xDF */
+ 0xE1, 0x9B, 0xE1, 0x9C, 0xE1, 0x9D, 0xE1, 0x9E, /* 0xE0-0xE3 */
+ 0xE1, 0x9F, 0xE1, 0xA0, 0xE2, 0x40, 0xE2, 0x41, /* 0xE4-0xE7 */
+ 0xE2, 0x42, 0xE2, 0x43, 0xE2, 0x44, 0xE2, 0x45, /* 0xE8-0xEB */
+ 0xE2, 0x46, 0xE2, 0x47, 0xE2, 0x48, 0xE2, 0x49, /* 0xEC-0xEF */
+ 0xE2, 0x4A, 0xE2, 0x4B, 0xE2, 0x4C, 0xE2, 0x4D, /* 0xF0-0xF3 */
+ 0xE2, 0x4E, 0xE2, 0x4F, 0xE2, 0x50, 0xE2, 0x51, /* 0xF4-0xF7 */
+ 0xE2, 0x52, 0xE2, 0x53, 0xE2, 0x54, 0xE2, 0x55, /* 0xF8-0xFB */
+ 0xE2, 0x56, 0xE2, 0x57, 0xE2, 0x58, 0xE2, 0x59, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_92[512] = {
+ 0xE2, 0x5A, 0xE2, 0x5B, 0xE2, 0x5C, 0xE2, 0x5D, /* 0x00-0x03 */
+ 0xE2, 0x5E, 0xE2, 0x5F, 0xE2, 0x60, 0xE2, 0x61, /* 0x04-0x07 */
+ 0xE2, 0x62, 0xE2, 0x63, 0xE2, 0x64, 0xE2, 0x65, /* 0x08-0x0B */
+ 0xE2, 0x66, 0xE2, 0x67, 0xE2, 0x68, 0xE2, 0x69, /* 0x0C-0x0F */
+ 0xE2, 0x6A, 0xE2, 0x6B, 0xE2, 0x6C, 0xE2, 0x6D, /* 0x10-0x13 */
+ 0xE2, 0x6E, 0xE2, 0x6F, 0xE2, 0x70, 0xE2, 0x71, /* 0x14-0x17 */
+ 0xE2, 0x72, 0xE2, 0x73, 0xE2, 0x74, 0xE2, 0x75, /* 0x18-0x1B */
+ 0xE2, 0x76, 0xE2, 0x77, 0xE2, 0x78, 0xE2, 0x79, /* 0x1C-0x1F */
+ 0xE2, 0x7A, 0xE2, 0x7B, 0xE2, 0x7C, 0xE2, 0x7D, /* 0x20-0x23 */
+ 0xE2, 0x7E, 0xE2, 0x80, 0xE2, 0x81, 0xE2, 0x82, /* 0x24-0x27 */
+ 0xE2, 0x83, 0xE2, 0x84, 0xE2, 0x85, 0xE2, 0x86, /* 0x28-0x2B */
+ 0xE2, 0x87, 0xE2, 0x88, 0xE2, 0x89, 0xE2, 0x8A, /* 0x2C-0x2F */
+ 0xE2, 0x8B, 0xE2, 0x8C, 0xE2, 0x8D, 0xE2, 0x8E, /* 0x30-0x33 */
+ 0xE2, 0x8F, 0xE2, 0x90, 0xE2, 0x91, 0xE2, 0x92, /* 0x34-0x37 */
+ 0xE2, 0x93, 0xE2, 0x94, 0xE2, 0x95, 0xE2, 0x96, /* 0x38-0x3B */
+ 0xE2, 0x97, 0xE2, 0x98, 0xE2, 0x99, 0xE2, 0x9A, /* 0x3C-0x3F */
+ 0xE2, 0x9B, 0xE2, 0x9C, 0xE2, 0x9D, 0xE2, 0x9E, /* 0x40-0x43 */
+ 0xE2, 0x9F, 0xE2, 0xA0, 0xE3, 0x40, 0xE3, 0x41, /* 0x44-0x47 */
+ 0xE3, 0x42, 0xE3, 0x43, 0xE3, 0x44, 0xE3, 0x45, /* 0x48-0x4B */
+ 0xE3, 0x46, 0xE3, 0x47, 0xE3, 0x48, 0xE3, 0x49, /* 0x4C-0x4F */
+ 0xE3, 0x4A, 0xE3, 0x4B, 0xE3, 0x4C, 0xE3, 0x4D, /* 0x50-0x53 */
+ 0xE3, 0x4E, 0xE3, 0x4F, 0xE3, 0x50, 0xE3, 0x51, /* 0x54-0x57 */
+ 0xE3, 0x52, 0xE3, 0x53, 0xE3, 0x54, 0xE3, 0x55, /* 0x58-0x5B */
+ 0xE3, 0x56, 0xE3, 0x57, 0xE3, 0x58, 0xE3, 0x59, /* 0x5C-0x5F */
+ 0xE3, 0x5A, 0xE3, 0x5B, 0xE3, 0x5C, 0xE3, 0x5D, /* 0x60-0x63 */
+ 0xE3, 0x5E, 0xE3, 0x5F, 0xE3, 0x60, 0xE3, 0x61, /* 0x64-0x67 */
+ 0xE3, 0x62, 0xE3, 0x63, 0xE3, 0x64, 0xE3, 0x65, /* 0x68-0x6B */
+ 0xE3, 0x66, 0xE3, 0x67, 0xE3, 0x68, 0xE3, 0x69, /* 0x6C-0x6F */
+ 0xE3, 0x6A, 0xE3, 0x6B, 0xE3, 0x6C, 0xE3, 0x6D, /* 0x70-0x73 */
+ 0xBC, 0xF8, 0xE3, 0x6E, 0xE3, 0x6F, 0xE3, 0x70, /* 0x74-0x77 */
+ 0xE3, 0x71, 0xE3, 0x72, 0xE3, 0x73, 0xE3, 0x74, /* 0x78-0x7B */
+ 0xE3, 0x75, 0xE3, 0x76, 0xE3, 0x77, 0xE3, 0x78, /* 0x7C-0x7F */
+
+ 0xE3, 0x79, 0xE3, 0x7A, 0xE3, 0x7B, 0xE3, 0x7C, /* 0x80-0x83 */
+ 0xE3, 0x7D, 0xE3, 0x7E, 0xE3, 0x80, 0xE3, 0x81, /* 0x84-0x87 */
+ 0xE3, 0x82, 0xE3, 0x83, 0xE3, 0x84, 0xE3, 0x85, /* 0x88-0x8B */
+ 0xE3, 0x86, 0xE3, 0x87, 0xF6, 0xC6, 0xE3, 0x88, /* 0x8C-0x8F */
+ 0xE3, 0x89, 0xE3, 0x8A, 0xE3, 0x8B, 0xE3, 0x8C, /* 0x90-0x93 */
+ 0xE3, 0x8D, 0xE3, 0x8E, 0xE3, 0x8F, 0xE3, 0x90, /* 0x94-0x97 */
+ 0xE3, 0x91, 0xE3, 0x92, 0xE3, 0x93, 0xE3, 0x94, /* 0x98-0x9B */
+ 0xE3, 0x95, 0xE3, 0x96, 0xE3, 0x97, 0xE3, 0x98, /* 0x9C-0x9F */
+ 0xE3, 0x99, 0xE3, 0x9A, 0xE3, 0x9B, 0xE3, 0x9C, /* 0xA0-0xA3 */
+ 0xE3, 0x9D, 0xE3, 0x9E, 0xE3, 0x9F, 0xE3, 0xA0, /* 0xA4-0xA7 */
+ 0xE4, 0x40, 0xE4, 0x41, 0xE4, 0x42, 0xE4, 0x43, /* 0xA8-0xAB */
+ 0xE4, 0x44, 0xE4, 0x45, 0xF6, 0xC7, 0xE4, 0x46, /* 0xAC-0xAF */
+ 0xE4, 0x47, 0xE4, 0x48, 0xE4, 0x49, 0xE4, 0x4A, /* 0xB0-0xB3 */
+ 0xE4, 0x4B, 0xE4, 0x4C, 0xE4, 0x4D, 0xE4, 0x4E, /* 0xB4-0xB7 */
+ 0xE4, 0x4F, 0xE4, 0x50, 0xE4, 0x51, 0xE4, 0x52, /* 0xB8-0xBB */
+ 0xE4, 0x53, 0xE4, 0x54, 0xE4, 0x55, 0xE4, 0x56, /* 0xBC-0xBF */
+ 0xE4, 0x57, 0xE4, 0x58, 0xE4, 0x59, 0xE4, 0x5A, /* 0xC0-0xC3 */
+ 0xE4, 0x5B, 0xE4, 0x5C, 0xE4, 0x5D, 0xE4, 0x5E, /* 0xC4-0xC7 */
+ 0xF6, 0xC8, 0xE4, 0x5F, 0xE4, 0x60, 0xE4, 0x61, /* 0xC8-0xCB */
+ 0xE4, 0x62, 0xE4, 0x63, 0xE4, 0x64, 0xE4, 0x65, /* 0xCC-0xCF */
+ 0xE4, 0x66, 0xE4, 0x67, 0xE4, 0x68, 0xE4, 0x69, /* 0xD0-0xD3 */
+ 0xE4, 0x6A, 0xE4, 0x6B, 0xE4, 0x6C, 0xE4, 0x6D, /* 0xD4-0xD7 */
+ 0xE4, 0x6E, 0xE4, 0x6F, 0xE4, 0x70, 0xE4, 0x71, /* 0xD8-0xDB */
+ 0xE4, 0x72, 0xE4, 0x73, 0xE4, 0x74, 0xE4, 0x75, /* 0xDC-0xDF */
+ 0xE4, 0x76, 0xE4, 0x77, 0xE4, 0x78, 0xE4, 0x79, /* 0xE0-0xE3 */
+ 0xE4, 0x7A, 0xE4, 0x7B, 0xE4, 0x7C, 0xE4, 0x7D, /* 0xE4-0xE7 */
+ 0xE4, 0x7E, 0xE4, 0x80, 0xE4, 0x81, 0xE4, 0x82, /* 0xE8-0xEB */
+ 0xE4, 0x83, 0xE4, 0x84, 0xE4, 0x85, 0xE4, 0x86, /* 0xEC-0xEF */
+ 0xE4, 0x87, 0xE4, 0x88, 0xE4, 0x89, 0xE4, 0x8A, /* 0xF0-0xF3 */
+ 0xE4, 0x8B, 0xE4, 0x8C, 0xE4, 0x8D, 0xE4, 0x8E, /* 0xF4-0xF7 */
+ 0xE4, 0x8F, 0xE4, 0x90, 0xE4, 0x91, 0xE4, 0x92, /* 0xF8-0xFB */
+ 0xE4, 0x93, 0xE4, 0x94, 0xE4, 0x95, 0xE4, 0x96, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_93[512] = {
+ 0xE4, 0x97, 0xE4, 0x98, 0xE4, 0x99, 0xE4, 0x9A, /* 0x00-0x03 */
+ 0xE4, 0x9B, 0xE4, 0x9C, 0xE4, 0x9D, 0xE4, 0x9E, /* 0x04-0x07 */
+ 0xE4, 0x9F, 0xE4, 0xA0, 0xE5, 0x40, 0xE5, 0x41, /* 0x08-0x0B */
+ 0xE5, 0x42, 0xE5, 0x43, 0xE5, 0x44, 0xE5, 0x45, /* 0x0C-0x0F */
+ 0xE5, 0x46, 0xE5, 0x47, 0xE5, 0x48, 0xE5, 0x49, /* 0x10-0x13 */
+ 0xE5, 0x4A, 0xE5, 0x4B, 0xE5, 0x4C, 0xE5, 0x4D, /* 0x14-0x17 */
+ 0xE5, 0x4E, 0xE5, 0x4F, 0xE5, 0x50, 0xE5, 0x51, /* 0x18-0x1B */
+ 0xE5, 0x52, 0xE5, 0x53, 0xE5, 0x54, 0xE5, 0x55, /* 0x1C-0x1F */
+ 0xE5, 0x56, 0xE5, 0x57, 0xE5, 0x58, 0xE5, 0x59, /* 0x20-0x23 */
+ 0xE5, 0x5A, 0xE5, 0x5B, 0xE5, 0x5C, 0xE5, 0x5D, /* 0x24-0x27 */
+ 0xE5, 0x5E, 0xE5, 0x5F, 0xE5, 0x60, 0xE5, 0x61, /* 0x28-0x2B */
+ 0xE5, 0x62, 0xE5, 0x63, 0xE5, 0x64, 0xE5, 0x65, /* 0x2C-0x2F */
+ 0xE5, 0x66, 0xE5, 0x67, 0xE5, 0x68, 0xE5, 0x69, /* 0x30-0x33 */
+ 0xE5, 0x6A, 0xE5, 0x6B, 0xE5, 0x6C, 0xE5, 0x6D, /* 0x34-0x37 */
+ 0xE5, 0x6E, 0xE5, 0x6F, 0xE5, 0x70, 0xE5, 0x71, /* 0x38-0x3B */
+ 0xE5, 0x72, 0xE5, 0x73, 0xF6, 0xC9, 0xE5, 0x74, /* 0x3C-0x3F */
+ 0xE5, 0x75, 0xE5, 0x76, 0xE5, 0x77, 0xE5, 0x78, /* 0x40-0x43 */
+ 0xE5, 0x79, 0xE5, 0x7A, 0xE5, 0x7B, 0xE5, 0x7C, /* 0x44-0x47 */
+ 0xE5, 0x7D, 0xE5, 0x7E, 0xE5, 0x80, 0xE5, 0x81, /* 0x48-0x4B */
+ 0xE5, 0x82, 0xE5, 0x83, 0xE5, 0x84, 0xE5, 0x85, /* 0x4C-0x4F */
+ 0xE5, 0x86, 0xE5, 0x87, 0xE5, 0x88, 0xE5, 0x89, /* 0x50-0x53 */
+ 0xE5, 0x8A, 0xE5, 0x8B, 0xE5, 0x8C, 0xE5, 0x8D, /* 0x54-0x57 */
+ 0xE5, 0x8E, 0xE5, 0x8F, 0xE5, 0x90, 0xE5, 0x91, /* 0x58-0x5B */
+ 0xE5, 0x92, 0xE5, 0x93, 0xE5, 0x94, 0xE5, 0x95, /* 0x5C-0x5F */
+ 0xE5, 0x96, 0xE5, 0x97, 0xE5, 0x98, 0xE5, 0x99, /* 0x60-0x63 */
+ 0xE5, 0x9A, 0xE5, 0x9B, 0xE5, 0x9C, 0xE5, 0x9D, /* 0x64-0x67 */
+ 0xE5, 0x9E, 0xE5, 0x9F, 0xF6, 0xCA, 0xE5, 0xA0, /* 0x68-0x6B */
+ 0xE6, 0x40, 0xE6, 0x41, 0xE6, 0x42, 0xE6, 0x43, /* 0x6C-0x6F */
+ 0xE6, 0x44, 0xE6, 0x45, 0xE6, 0x46, 0xE6, 0x47, /* 0x70-0x73 */
+ 0xE6, 0x48, 0xE6, 0x49, 0xE6, 0x4A, 0xE6, 0x4B, /* 0x74-0x77 */
+ 0xE6, 0x4C, 0xE6, 0x4D, 0xE6, 0x4E, 0xE6, 0x4F, /* 0x78-0x7B */
+ 0xE6, 0x50, 0xE6, 0x51, 0xE6, 0x52, 0xE6, 0x53, /* 0x7C-0x7F */
+
+ 0xE6, 0x54, 0xE6, 0x55, 0xE6, 0x56, 0xE6, 0x57, /* 0x80-0x83 */
+ 0xE6, 0x58, 0xE6, 0x59, 0xE6, 0x5A, 0xE6, 0x5B, /* 0x84-0x87 */
+ 0xE6, 0x5C, 0xE6, 0x5D, 0xE6, 0x5E, 0xE6, 0x5F, /* 0x88-0x8B */
+ 0xE6, 0x60, 0xE6, 0x61, 0xE6, 0x62, 0xF6, 0xCC, /* 0x8C-0x8F */
+ 0xE6, 0x63, 0xE6, 0x64, 0xE6, 0x65, 0xE6, 0x66, /* 0x90-0x93 */
+ 0xE6, 0x67, 0xE6, 0x68, 0xE6, 0x69, 0xE6, 0x6A, /* 0x94-0x97 */
+ 0xE6, 0x6B, 0xE6, 0x6C, 0xE6, 0x6D, 0xE6, 0x6E, /* 0x98-0x9B */
+ 0xE6, 0x6F, 0xE6, 0x70, 0xE6, 0x71, 0xE6, 0x72, /* 0x9C-0x9F */
+ 0xE6, 0x73, 0xE6, 0x74, 0xE6, 0x75, 0xE6, 0x76, /* 0xA0-0xA3 */
+ 0xE6, 0x77, 0xE6, 0x78, 0xE6, 0x79, 0xE6, 0x7A, /* 0xA4-0xA7 */
+ 0xE6, 0x7B, 0xE6, 0x7C, 0xE6, 0x7D, 0xE6, 0x7E, /* 0xA8-0xAB */
+ 0xE6, 0x80, 0xE6, 0x81, 0xE6, 0x82, 0xE6, 0x83, /* 0xAC-0xAF */
+ 0xE6, 0x84, 0xE6, 0x85, 0xE6, 0x86, 0xE6, 0x87, /* 0xB0-0xB3 */
+ 0xE6, 0x88, 0xE6, 0x89, 0xE6, 0x8A, 0xE6, 0x8B, /* 0xB4-0xB7 */
+ 0xE6, 0x8C, 0xE6, 0x8D, 0xE6, 0x8E, 0xE6, 0x8F, /* 0xB8-0xBB */
+ 0xE6, 0x90, 0xE6, 0x91, 0xE6, 0x92, 0xE6, 0x93, /* 0xBC-0xBF */
+ 0xE6, 0x94, 0xE6, 0x95, 0xE6, 0x96, 0xE6, 0x97, /* 0xC0-0xC3 */
+ 0xE6, 0x98, 0xE6, 0x99, 0xE6, 0x9A, 0xE6, 0x9B, /* 0xC4-0xC7 */
+ 0xE6, 0x9C, 0xE6, 0x9D, 0xF6, 0xCB, 0xE6, 0x9E, /* 0xC8-0xCB */
+ 0xE6, 0x9F, 0xE6, 0xA0, 0xE7, 0x40, 0xE7, 0x41, /* 0xCC-0xCF */
+ 0xE7, 0x42, 0xE7, 0x43, 0xE7, 0x44, 0xE7, 0x45, /* 0xD0-0xD3 */
+ 0xE7, 0x46, 0xE7, 0x47, 0xF7, 0xE9, 0xE7, 0x48, /* 0xD4-0xD7 */
+ 0xE7, 0x49, 0xE7, 0x4A, 0xE7, 0x4B, 0xE7, 0x4C, /* 0xD8-0xDB */
+ 0xE7, 0x4D, 0xE7, 0x4E, 0xE7, 0x4F, 0xE7, 0x50, /* 0xDC-0xDF */
+ 0xE7, 0x51, 0xE7, 0x52, 0xE7, 0x53, 0xE7, 0x54, /* 0xE0-0xE3 */
+ 0xE7, 0x55, 0xE7, 0x56, 0xE7, 0x57, 0xE7, 0x58, /* 0xE4-0xE7 */
+ 0xE7, 0x59, 0xE7, 0x5A, 0xE7, 0x5B, 0xE7, 0x5C, /* 0xE8-0xEB */
+ 0xE7, 0x5D, 0xE7, 0x5E, 0xE7, 0x5F, 0xE7, 0x60, /* 0xEC-0xEF */
+ 0xE7, 0x61, 0xE7, 0x62, 0xE7, 0x63, 0xE7, 0x64, /* 0xF0-0xF3 */
+ 0xE7, 0x65, 0xE7, 0x66, 0xE7, 0x67, 0xE7, 0x68, /* 0xF4-0xF7 */
+ 0xE7, 0x69, 0xE7, 0x6A, 0xE7, 0x6B, 0xE7, 0x6C, /* 0xF8-0xFB */
+ 0xE7, 0x6D, 0xE7, 0x6E, 0xE7, 0x6F, 0xE7, 0x70, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_94[512] = {
+ 0xE7, 0x71, 0xE7, 0x72, 0xE7, 0x73, 0xE7, 0x74, /* 0x00-0x03 */
+ 0xE7, 0x75, 0xE7, 0x76, 0xE7, 0x77, 0xE7, 0x78, /* 0x04-0x07 */
+ 0xE7, 0x79, 0xE7, 0x7A, 0xE7, 0x7B, 0xE7, 0x7C, /* 0x08-0x0B */
+ 0xE7, 0x7D, 0xE7, 0x7E, 0xE7, 0x80, 0xE7, 0x81, /* 0x0C-0x0F */
+ 0xE7, 0x82, 0xE7, 0x83, 0xE7, 0x84, 0xE7, 0x85, /* 0x10-0x13 */
+ 0xE7, 0x86, 0xE7, 0x87, 0xE7, 0x88, 0xE7, 0x89, /* 0x14-0x17 */
+ 0xE7, 0x8A, 0xE7, 0x8B, 0xE7, 0x8C, 0xE7, 0x8D, /* 0x18-0x1B */
+ 0xE7, 0x8E, 0xE7, 0x8F, 0xE7, 0x90, 0xE7, 0x91, /* 0x1C-0x1F */
+ 0xE7, 0x92, 0xE7, 0x93, 0xE7, 0x94, 0xE7, 0x95, /* 0x20-0x23 */
+ 0xE7, 0x96, 0xE7, 0x97, 0xE7, 0x98, 0xE7, 0x99, /* 0x24-0x27 */
+ 0xE7, 0x9A, 0xE7, 0x9B, 0xE7, 0x9C, 0xE7, 0x9D, /* 0x28-0x2B */
+ 0xE7, 0x9E, 0xE7, 0x9F, 0xE7, 0xA0, 0xE8, 0x40, /* 0x2C-0x2F */
+ 0xE8, 0x41, 0xE8, 0x42, 0xE8, 0x43, 0xE8, 0x44, /* 0x30-0x33 */
+ 0xE8, 0x45, 0xE8, 0x46, 0xE8, 0x47, 0xE8, 0x48, /* 0x34-0x37 */
+ 0xE8, 0x49, 0xE8, 0x4A, 0xE8, 0x4B, 0xE8, 0x4C, /* 0x38-0x3B */
+ 0xE8, 0x4D, 0xE8, 0x4E, 0xF6, 0xCD, 0xE8, 0x4F, /* 0x3C-0x3F */
+ 0xE8, 0x50, 0xE8, 0x51, 0xE8, 0x52, 0xE8, 0x53, /* 0x40-0x43 */
+ 0xE8, 0x54, 0xE8, 0x55, 0xE8, 0x56, 0xE8, 0x57, /* 0x44-0x47 */
+ 0xE8, 0x58, 0xE8, 0x59, 0xE8, 0x5A, 0xE8, 0x5B, /* 0x48-0x4B */
+ 0xE8, 0x5C, 0xE8, 0x5D, 0xE8, 0x5E, 0xE8, 0x5F, /* 0x4C-0x4F */
+ 0xE8, 0x60, 0xE8, 0x61, 0xE8, 0x62, 0xE8, 0x63, /* 0x50-0x53 */
+ 0xE8, 0x64, 0xE8, 0x65, 0xE8, 0x66, 0xE8, 0x67, /* 0x54-0x57 */
+ 0xE8, 0x68, 0xE8, 0x69, 0xE8, 0x6A, 0xE8, 0x6B, /* 0x58-0x5B */
+ 0xE8, 0x6C, 0xE8, 0x6D, 0xE8, 0x6E, 0xE8, 0x6F, /* 0x5C-0x5F */
+ 0xE8, 0x70, 0xE8, 0x71, 0xE8, 0x72, 0xE8, 0x73, /* 0x60-0x63 */
+ 0xE8, 0x74, 0xE8, 0x75, 0xE8, 0x76, 0xE8, 0x77, /* 0x64-0x67 */
+ 0xE8, 0x78, 0xE8, 0x79, 0xE8, 0x7A, 0xF6, 0xCE, /* 0x68-0x6B */
+ 0xE8, 0x7B, 0xE8, 0x7C, 0xE8, 0x7D, 0xE8, 0x7E, /* 0x6C-0x6F */
+ 0xE8, 0x80, 0xE8, 0x81, 0xE8, 0x82, 0xE8, 0x83, /* 0x70-0x73 */
+ 0xE8, 0x84, 0xE8, 0x85, 0xE8, 0x86, 0xE8, 0x87, /* 0x74-0x77 */
+ 0xE8, 0x88, 0xE8, 0x89, 0xE8, 0x8A, 0xE8, 0x8B, /* 0x78-0x7B */
+ 0xE8, 0x8C, 0xE8, 0x8D, 0xE8, 0x8E, 0xE8, 0x8F, /* 0x7C-0x7F */
+
+ 0xE8, 0x90, 0xE8, 0x91, 0xE8, 0x92, 0xE8, 0x93, /* 0x80-0x83 */
+ 0xE8, 0x94, 0xEE, 0xC4, 0xEE, 0xC5, 0xEE, 0xC6, /* 0x84-0x87 */
+ 0xD5, 0xEB, 0xB6, 0xA4, 0xEE, 0xC8, 0xEE, 0xC7, /* 0x88-0x8B */
+ 0xEE, 0xC9, 0xEE, 0xCA, 0xC7, 0xA5, 0xEE, 0xCB, /* 0x8C-0x8F */
+ 0xEE, 0xCC, 0xE8, 0x95, 0xB7, 0xB0, 0xB5, 0xF6, /* 0x90-0x93 */
+ 0xEE, 0xCD, 0xEE, 0xCF, 0xE8, 0x96, 0xEE, 0xCE, /* 0x94-0x97 */
+ 0xE8, 0x97, 0xB8, 0xC6, 0xEE, 0xD0, 0xEE, 0xD1, /* 0x98-0x9B */
+ 0xEE, 0xD2, 0xB6, 0xDB, 0xB3, 0xAE, 0xD6, 0xD3, /* 0x9C-0x9F */
+ 0xC4, 0xC6, 0xB1, 0xB5, 0xB8, 0xD6, 0xEE, 0xD3, /* 0xA0-0xA3 */
+ 0xEE, 0xD4, 0xD4, 0xBF, 0xC7, 0xD5, 0xBE, 0xFB, /* 0xA4-0xA7 */
+ 0xCE, 0xD9, 0xB9, 0xB3, 0xEE, 0xD6, 0xEE, 0xD5, /* 0xA8-0xAB */
+ 0xEE, 0xD8, 0xEE, 0xD7, 0xC5, 0xA5, 0xEE, 0xD9, /* 0xAC-0xAF */
+ 0xEE, 0xDA, 0xC7, 0xAE, 0xEE, 0xDB, 0xC7, 0xAF, /* 0xB0-0xB3 */
+ 0xEE, 0xDC, 0xB2, 0xA7, 0xEE, 0xDD, 0xEE, 0xDE, /* 0xB4-0xB7 */
+ 0xEE, 0xDF, 0xEE, 0xE0, 0xEE, 0xE1, 0xD7, 0xEA, /* 0xB8-0xBB */
+ 0xEE, 0xE2, 0xEE, 0xE3, 0xBC, 0xD8, 0xEE, 0xE4, /* 0xBC-0xBF */
+ 0xD3, 0xCB, 0xCC, 0xFA, 0xB2, 0xAC, 0xC1, 0xE5, /* 0xC0-0xC3 */
+ 0xEE, 0xE5, 0xC7, 0xA6, 0xC3, 0xAD, 0xE8, 0x98, /* 0xC4-0xC7 */
+ 0xEE, 0xE6, 0xEE, 0xE7, 0xEE, 0xE8, 0xEE, 0xE9, /* 0xC8-0xCB */
+ 0xEE, 0xEA, 0xEE, 0xEB, 0xEE, 0xEC, 0xE8, 0x99, /* 0xCC-0xCF */
+ 0xEE, 0xED, 0xEE, 0xEE, 0xEE, 0xEF, 0xE8, 0x9A, /* 0xD0-0xD3 */
+ 0xE8, 0x9B, 0xEE, 0xF0, 0xEE, 0xF1, 0xEE, 0xF2, /* 0xD4-0xD7 */
+ 0xEE, 0xF4, 0xEE, 0xF3, 0xE8, 0x9C, 0xEE, 0xF5, /* 0xD8-0xDB */
+ 0xCD, 0xAD, 0xC2, 0xC1, 0xEE, 0xF6, 0xEE, 0xF7, /* 0xDC-0xDF */
+ 0xEE, 0xF8, 0xD5, 0xA1, 0xEE, 0xF9, 0xCF, 0xB3, /* 0xE0-0xE3 */
+ 0xEE, 0xFA, 0xEE, 0xFB, 0xE8, 0x9D, 0xEE, 0xFC, /* 0xE4-0xE7 */
+ 0xEE, 0xFD, 0xEF, 0xA1, 0xEE, 0xFE, 0xEF, 0xA2, /* 0xE8-0xEB */
+ 0xB8, 0xF5, 0xC3, 0xFA, 0xEF, 0xA3, 0xEF, 0xA4, /* 0xEC-0xEF */
+ 0xBD, 0xC2, 0xD2, 0xBF, 0xB2, 0xF9, 0xEF, 0xA5, /* 0xF0-0xF3 */
+ 0xEF, 0xA6, 0xEF, 0xA7, 0xD2, 0xF8, 0xEF, 0xA8, /* 0xF4-0xF7 */
+ 0xD6, 0xFD, 0xEF, 0xA9, 0xC6, 0xCC, 0xE8, 0x9E, /* 0xF8-0xFB */
+ 0xEF, 0xAA, 0xEF, 0xAB, 0xC1, 0xB4, 0xEF, 0xAC, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_95[512] = {
+ 0xCF, 0xFA, 0xCB, 0xF8, 0xEF, 0xAE, 0xEF, 0xAD, /* 0x00-0x03 */
+ 0xB3, 0xFA, 0xB9, 0xF8, 0xEF, 0xAF, 0xEF, 0xB0, /* 0x04-0x07 */
+ 0xD0, 0xE2, 0xEF, 0xB1, 0xEF, 0xB2, 0xB7, 0xE6, /* 0x08-0x0B */
+ 0xD0, 0xBF, 0xEF, 0xB3, 0xEF, 0xB4, 0xEF, 0xB5, /* 0x0C-0x0F */
+ 0xC8, 0xF1, 0xCC, 0xE0, 0xEF, 0xB6, 0xEF, 0xB7, /* 0x10-0x13 */
+ 0xEF, 0xB8, 0xEF, 0xB9, 0xEF, 0xBA, 0xD5, 0xE0, /* 0x14-0x17 */
+ 0xEF, 0xBB, 0xB4, 0xED, 0xC3, 0xAA, 0xEF, 0xBC, /* 0x18-0x1B */
+ 0xE8, 0x9F, 0xEF, 0xBD, 0xEF, 0xBE, 0xEF, 0xBF, /* 0x1C-0x1F */
+ 0xE8, 0xA0, 0xCE, 0xFD, 0xEF, 0xC0, 0xC2, 0xE0, /* 0x20-0x23 */
+ 0xB4, 0xB8, 0xD7, 0xB6, 0xBD, 0xF5, 0xE9, 0x40, /* 0x24-0x27 */
+ 0xCF, 0xC7, 0xEF, 0xC3, 0xEF, 0xC1, 0xEF, 0xC2, /* 0x28-0x2B */
+ 0xEF, 0xC4, 0xB6, 0xA7, 0xBC, 0xFC, 0xBE, 0xE2, /* 0x2C-0x2F */
+ 0xC3, 0xCC, 0xEF, 0xC5, 0xEF, 0xC6, 0xE9, 0x41, /* 0x30-0x33 */
+ 0xEF, 0xC7, 0xEF, 0xCF, 0xEF, 0xC8, 0xEF, 0xC9, /* 0x34-0x37 */
+ 0xEF, 0xCA, 0xC7, 0xC2, 0xEF, 0xF1, 0xB6, 0xCD, /* 0x38-0x3B */
+ 0xEF, 0xCB, 0xE9, 0x42, 0xEF, 0xCC, 0xEF, 0xCD, /* 0x3C-0x3F */
+ 0xB6, 0xC6, 0xC3, 0xBE, 0xEF, 0xCE, 0xE9, 0x43, /* 0x40-0x43 */
+ 0xEF, 0xD0, 0xEF, 0xD1, 0xEF, 0xD2, 0xD5, 0xF2, /* 0x44-0x47 */
+ 0xE9, 0x44, 0xEF, 0xD3, 0xC4, 0xF7, 0xE9, 0x45, /* 0x48-0x4B */
+ 0xEF, 0xD4, 0xC4, 0xF8, 0xEF, 0xD5, 0xEF, 0xD6, /* 0x4C-0x4F */
+ 0xB8, 0xE4, 0xB0, 0xF7, 0xEF, 0xD7, 0xEF, 0xD8, /* 0x50-0x53 */
+ 0xEF, 0xD9, 0xE9, 0x46, 0xEF, 0xDA, 0xEF, 0xDB, /* 0x54-0x57 */
+ 0xEF, 0xDC, 0xEF, 0xDD, 0xE9, 0x47, 0xEF, 0xDE, /* 0x58-0x5B */
+ 0xBE, 0xB5, 0xEF, 0xE1, 0xEF, 0xDF, 0xEF, 0xE0, /* 0x5C-0x5F */
+ 0xE9, 0x48, 0xEF, 0xE2, 0xEF, 0xE3, 0xC1, 0xCD, /* 0x60-0x63 */
+ 0xEF, 0xE4, 0xEF, 0xE5, 0xEF, 0xE6, 0xEF, 0xE7, /* 0x64-0x67 */
+ 0xEF, 0xE8, 0xEF, 0xE9, 0xEF, 0xEA, 0xEF, 0xEB, /* 0x68-0x6B */
+ 0xEF, 0xEC, 0xC0, 0xD8, 0xE9, 0x49, 0xEF, 0xED, /* 0x6C-0x6F */
+ 0xC1, 0xAD, 0xEF, 0xEE, 0xEF, 0xEF, 0xEF, 0xF0, /* 0x70-0x73 */
+ 0xE9, 0x4A, 0xE9, 0x4B, 0xCF, 0xE2, 0xE9, 0x4C, /* 0x74-0x77 */
+ 0xE9, 0x4D, 0xE9, 0x4E, 0xE9, 0x4F, 0xE9, 0x50, /* 0x78-0x7B */
+ 0xE9, 0x51, 0xE9, 0x52, 0xE9, 0x53, 0xB3, 0xA4, /* 0x7C-0x7F */
+
+ 0xE9, 0x54, 0xE9, 0x55, 0xE9, 0x56, 0xE9, 0x57, /* 0x80-0x83 */
+ 0xE9, 0x58, 0xE9, 0x59, 0xE9, 0x5A, 0xE9, 0x5B, /* 0x84-0x87 */
+ 0xE9, 0x5C, 0xE9, 0x5D, 0xE9, 0x5E, 0xE9, 0x5F, /* 0x88-0x8B */
+ 0xE9, 0x60, 0xE9, 0x61, 0xE9, 0x62, 0xE9, 0x63, /* 0x8C-0x8F */
+ 0xE9, 0x64, 0xE9, 0x65, 0xE9, 0x66, 0xE9, 0x67, /* 0x90-0x93 */
+ 0xE9, 0x68, 0xE9, 0x69, 0xE9, 0x6A, 0xE9, 0x6B, /* 0x94-0x97 */
+ 0xE9, 0x6C, 0xE9, 0x6D, 0xE9, 0x6E, 0xE9, 0x6F, /* 0x98-0x9B */
+ 0xE9, 0x70, 0xE9, 0x71, 0xE9, 0x72, 0xE9, 0x73, /* 0x9C-0x9F */
+ 0xE9, 0x74, 0xE9, 0x75, 0xE9, 0x76, 0xE9, 0x77, /* 0xA0-0xA3 */
+ 0xE9, 0x78, 0xE9, 0x79, 0xE9, 0x7A, 0xE9, 0x7B, /* 0xA4-0xA7 */
+ 0xE9, 0x7C, 0xE9, 0x7D, 0xE9, 0x7E, 0xE9, 0x80, /* 0xA8-0xAB */
+ 0xE9, 0x81, 0xE9, 0x82, 0xE9, 0x83, 0xE9, 0x84, /* 0xAC-0xAF */
+ 0xE9, 0x85, 0xE9, 0x86, 0xE9, 0x87, 0xE9, 0x88, /* 0xB0-0xB3 */
+ 0xE9, 0x89, 0xE9, 0x8A, 0xE9, 0x8B, 0xE9, 0x8C, /* 0xB4-0xB7 */
+ 0xE9, 0x8D, 0xE9, 0x8E, 0xE9, 0x8F, 0xE9, 0x90, /* 0xB8-0xBB */
+ 0xE9, 0x91, 0xE9, 0x92, 0xE9, 0x93, 0xE9, 0x94, /* 0xBC-0xBF */
+ 0xE9, 0x95, 0xE9, 0x96, 0xE9, 0x97, 0xE9, 0x98, /* 0xC0-0xC3 */
+ 0xE9, 0x99, 0xE9, 0x9A, 0xE9, 0x9B, 0xE9, 0x9C, /* 0xC4-0xC7 */
+ 0xE9, 0x9D, 0xE9, 0x9E, 0xE9, 0x9F, 0xE9, 0xA0, /* 0xC8-0xCB */
+ 0xEA, 0x40, 0xEA, 0x41, 0xEA, 0x42, 0xEA, 0x43, /* 0xCC-0xCF */
+ 0xEA, 0x44, 0xEA, 0x45, 0xEA, 0x46, 0xEA, 0x47, /* 0xD0-0xD3 */
+ 0xEA, 0x48, 0xEA, 0x49, 0xEA, 0x4A, 0xEA, 0x4B, /* 0xD4-0xD7 */
+ 0xEA, 0x4C, 0xEA, 0x4D, 0xEA, 0x4E, 0xEA, 0x4F, /* 0xD8-0xDB */
+ 0xEA, 0x50, 0xEA, 0x51, 0xEA, 0x52, 0xEA, 0x53, /* 0xDC-0xDF */
+ 0xEA, 0x54, 0xEA, 0x55, 0xEA, 0x56, 0xEA, 0x57, /* 0xE0-0xE3 */
+ 0xEA, 0x58, 0xEA, 0x59, 0xEA, 0x5A, 0xEA, 0x5B, /* 0xE4-0xE7 */
+ 0xC3, 0xC5, 0xE3, 0xC5, 0xC9, 0xC1, 0xE3, 0xC6, /* 0xE8-0xEB */
+ 0xEA, 0x5C, 0xB1, 0xD5, 0xCE, 0xCA, 0xB4, 0xB3, /* 0xEC-0xEF */
+ 0xC8, 0xF2, 0xE3, 0xC7, 0xCF, 0xD0, 0xE3, 0xC8, /* 0xF0-0xF3 */
+ 0xBC, 0xE4, 0xE3, 0xC9, 0xE3, 0xCA, 0xC3, 0xC6, /* 0xF4-0xF7 */
+ 0xD5, 0xA2, 0xC4, 0xD6, 0xB9, 0xEB, 0xCE, 0xC5, /* 0xF8-0xFB */
+ 0xE3, 0xCB, 0xC3, 0xF6, 0xE3, 0xCC, 0xEA, 0x5D, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_96[512] = {
+ 0xB7, 0xA7, 0xB8, 0xF3, 0xBA, 0xD2, 0xE3, 0xCD, /* 0x00-0x03 */
+ 0xE3, 0xCE, 0xD4, 0xC4, 0xE3, 0xCF, 0xEA, 0x5E, /* 0x04-0x07 */
+ 0xE3, 0xD0, 0xD1, 0xCB, 0xE3, 0xD1, 0xE3, 0xD2, /* 0x08-0x0B */
+ 0xE3, 0xD3, 0xE3, 0xD4, 0xD1, 0xD6, 0xE3, 0xD5, /* 0x0C-0x0F */
+ 0xB2, 0xFB, 0xC0, 0xBB, 0xE3, 0xD6, 0xEA, 0x5F, /* 0x10-0x13 */
+ 0xC0, 0xAB, 0xE3, 0xD7, 0xE3, 0xD8, 0xE3, 0xD9, /* 0x14-0x17 */
+ 0xEA, 0x60, 0xE3, 0xDA, 0xE3, 0xDB, 0xEA, 0x61, /* 0x18-0x1B */
+ 0xB8, 0xB7, 0xDA, 0xE2, 0xEA, 0x62, 0xB6, 0xD3, /* 0x1C-0x1F */
+ 0xEA, 0x63, 0xDA, 0xE4, 0xDA, 0xE3, 0xEA, 0x64, /* 0x20-0x23 */
+ 0xEA, 0x65, 0xEA, 0x66, 0xEA, 0x67, 0xEA, 0x68, /* 0x24-0x27 */
+ 0xEA, 0x69, 0xEA, 0x6A, 0xDA, 0xE6, 0xEA, 0x6B, /* 0x28-0x2B */
+ 0xEA, 0x6C, 0xEA, 0x6D, 0xC8, 0xEE, 0xEA, 0x6E, /* 0x2C-0x2F */
+ 0xEA, 0x6F, 0xDA, 0xE5, 0xB7, 0xC0, 0xD1, 0xF4, /* 0x30-0x33 */
+ 0xD2, 0xF5, 0xD5, 0xF3, 0xBD, 0xD7, 0xEA, 0x70, /* 0x34-0x37 */
+ 0xEA, 0x71, 0xEA, 0x72, 0xEA, 0x73, 0xD7, 0xE8, /* 0x38-0x3B */
+ 0xDA, 0xE8, 0xDA, 0xE7, 0xEA, 0x74, 0xB0, 0xA2, /* 0x3C-0x3F */
+ 0xCD, 0xD3, 0xEA, 0x75, 0xDA, 0xE9, 0xEA, 0x76, /* 0x40-0x43 */
+ 0xB8, 0xBD, 0xBC, 0xCA, 0xC2, 0xBD, 0xC2, 0xA4, /* 0x44-0x47 */
+ 0xB3, 0xC2, 0xDA, 0xEA, 0xEA, 0x77, 0xC2, 0xAA, /* 0x48-0x4B */
+ 0xC4, 0xB0, 0xBD, 0xB5, 0xEA, 0x78, 0xEA, 0x79, /* 0x4C-0x4F */
+ 0xCF, 0xDE, 0xEA, 0x7A, 0xEA, 0x7B, 0xEA, 0x7C, /* 0x50-0x53 */
+ 0xDA, 0xEB, 0xC9, 0xC2, 0xEA, 0x7D, 0xEA, 0x7E, /* 0x54-0x57 */
+ 0xEA, 0x80, 0xEA, 0x81, 0xEA, 0x82, 0xB1, 0xDD, /* 0x58-0x5B */
+ 0xEA, 0x83, 0xEA, 0x84, 0xEA, 0x85, 0xDA, 0xEC, /* 0x5C-0x5F */
+ 0xEA, 0x86, 0xB6, 0xB8, 0xD4, 0xBA, 0xEA, 0x87, /* 0x60-0x63 */
+ 0xB3, 0xFD, 0xEA, 0x88, 0xEA, 0x89, 0xDA, 0xED, /* 0x64-0x67 */
+ 0xD4, 0xC9, 0xCF, 0xD5, 0xC5, 0xE3, 0xEA, 0x8A, /* 0x68-0x6B */
+ 0xDA, 0xEE, 0xEA, 0x8B, 0xEA, 0x8C, 0xEA, 0x8D, /* 0x6C-0x6F */
+ 0xEA, 0x8E, 0xEA, 0x8F, 0xDA, 0xEF, 0xEA, 0x90, /* 0x70-0x73 */
+ 0xDA, 0xF0, 0xC1, 0xEA, 0xCC, 0xD5, 0xCF, 0xDD, /* 0x74-0x77 */
+ 0xEA, 0x91, 0xEA, 0x92, 0xEA, 0x93, 0xEA, 0x94, /* 0x78-0x7B */
+ 0xEA, 0x95, 0xEA, 0x96, 0xEA, 0x97, 0xEA, 0x98, /* 0x7C-0x7F */
+
+ 0xEA, 0x99, 0xEA, 0x9A, 0xEA, 0x9B, 0xEA, 0x9C, /* 0x80-0x83 */
+ 0xEA, 0x9D, 0xD3, 0xE7, 0xC2, 0xA1, 0xEA, 0x9E, /* 0x84-0x87 */
+ 0xDA, 0xF1, 0xEA, 0x9F, 0xEA, 0xA0, 0xCB, 0xE5, /* 0x88-0x8B */
+ 0xEB, 0x40, 0xDA, 0xF2, 0xEB, 0x41, 0xCB, 0xE6, /* 0x8C-0x8F */
+ 0xD2, 0xFE, 0xEB, 0x42, 0xEB, 0x43, 0xEB, 0x44, /* 0x90-0x93 */
+ 0xB8, 0xF4, 0xEB, 0x45, 0xEB, 0x46, 0xDA, 0xF3, /* 0x94-0x97 */
+ 0xB0, 0xAF, 0xCF, 0xB6, 0xEB, 0x47, 0xEB, 0x48, /* 0x98-0x9B */
+ 0xD5, 0xCF, 0xEB, 0x49, 0xEB, 0x4A, 0xEB, 0x4B, /* 0x9C-0x9F */
+ 0xEB, 0x4C, 0xEB, 0x4D, 0xEB, 0x4E, 0xEB, 0x4F, /* 0xA0-0xA3 */
+ 0xEB, 0x50, 0xEB, 0x51, 0xEB, 0x52, 0xCB, 0xED, /* 0xA4-0xA7 */
+ 0xEB, 0x53, 0xEB, 0x54, 0xEB, 0x55, 0xEB, 0x56, /* 0xA8-0xAB */
+ 0xEB, 0x57, 0xEB, 0x58, 0xEB, 0x59, 0xEB, 0x5A, /* 0xAC-0xAF */
+ 0xDA, 0xF4, 0xEB, 0x5B, 0xEB, 0x5C, 0xE3, 0xC4, /* 0xB0-0xB3 */
+ 0xEB, 0x5D, 0xEB, 0x5E, 0xC1, 0xA5, 0xEB, 0x5F, /* 0xB4-0xB7 */
+ 0xEB, 0x60, 0xF6, 0xBF, 0xEB, 0x61, 0xEB, 0x62, /* 0xB8-0xBB */
+ 0xF6, 0xC0, 0xF6, 0xC1, 0xC4, 0xD1, 0xEB, 0x63, /* 0xBC-0xBF */
+ 0xC8, 0xB8, 0xD1, 0xE3, 0xEB, 0x64, 0xEB, 0x65, /* 0xC0-0xC3 */
+ 0xD0, 0xDB, 0xD1, 0xC5, 0xBC, 0xAF, 0xB9, 0xCD, /* 0xC4-0xC7 */
+ 0xEB, 0x66, 0xEF, 0xF4, 0xEB, 0x67, 0xEB, 0x68, /* 0xC8-0xCB */
+ 0xB4, 0xC6, 0xD3, 0xBA, 0xF6, 0xC2, 0xB3, 0xFB, /* 0xCC-0xCF */
+ 0xEB, 0x69, 0xEB, 0x6A, 0xF6, 0xC3, 0xEB, 0x6B, /* 0xD0-0xD3 */
+ 0xEB, 0x6C, 0xB5, 0xF1, 0xEB, 0x6D, 0xEB, 0x6E, /* 0xD4-0xD7 */
+ 0xEB, 0x6F, 0xEB, 0x70, 0xEB, 0x71, 0xEB, 0x72, /* 0xD8-0xDB */
+ 0xEB, 0x73, 0xEB, 0x74, 0xEB, 0x75, 0xEB, 0x76, /* 0xDC-0xDF */
+ 0xF6, 0xC5, 0xEB, 0x77, 0xEB, 0x78, 0xEB, 0x79, /* 0xE0-0xE3 */
+ 0xEB, 0x7A, 0xEB, 0x7B, 0xEB, 0x7C, 0xEB, 0x7D, /* 0xE4-0xE7 */
+ 0xD3, 0xEA, 0xF6, 0xA7, 0xD1, 0xA9, 0xEB, 0x7E, /* 0xE8-0xEB */
+ 0xEB, 0x80, 0xEB, 0x81, 0xEB, 0x82, 0xF6, 0xA9, /* 0xEC-0xEF */
+ 0xEB, 0x83, 0xEB, 0x84, 0xEB, 0x85, 0xF6, 0xA8, /* 0xF0-0xF3 */
+ 0xEB, 0x86, 0xEB, 0x87, 0xC1, 0xE3, 0xC0, 0xD7, /* 0xF4-0xF7 */
+ 0xEB, 0x88, 0xB1, 0xA2, 0xEB, 0x89, 0xEB, 0x8A, /* 0xF8-0xFB */
+ 0xEB, 0x8B, 0xEB, 0x8C, 0xCE, 0xED, 0xEB, 0x8D, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_97[512] = {
+ 0xD0, 0xE8, 0xF6, 0xAB, 0xEB, 0x8E, 0xEB, 0x8F, /* 0x00-0x03 */
+ 0xCF, 0xF6, 0xEB, 0x90, 0xF6, 0xAA, 0xD5, 0xF0, /* 0x04-0x07 */
+ 0xF6, 0xAC, 0xC3, 0xB9, 0xEB, 0x91, 0xEB, 0x92, /* 0x08-0x0B */
+ 0xEB, 0x93, 0xBB, 0xF4, 0xF6, 0xAE, 0xF6, 0xAD, /* 0x0C-0x0F */
+ 0xEB, 0x94, 0xEB, 0x95, 0xEB, 0x96, 0xC4, 0xDE, /* 0x10-0x13 */
+ 0xEB, 0x97, 0xEB, 0x98, 0xC1, 0xD8, 0xEB, 0x99, /* 0x14-0x17 */
+ 0xEB, 0x9A, 0xEB, 0x9B, 0xEB, 0x9C, 0xEB, 0x9D, /* 0x18-0x1B */
+ 0xCB, 0xAA, 0xEB, 0x9E, 0xCF, 0xBC, 0xEB, 0x9F, /* 0x1C-0x1F */
+ 0xEB, 0xA0, 0xEC, 0x40, 0xEC, 0x41, 0xEC, 0x42, /* 0x20-0x23 */
+ 0xEC, 0x43, 0xEC, 0x44, 0xEC, 0x45, 0xEC, 0x46, /* 0x24-0x27 */
+ 0xEC, 0x47, 0xEC, 0x48, 0xF6, 0xAF, 0xEC, 0x49, /* 0x28-0x2B */
+ 0xEC, 0x4A, 0xF6, 0xB0, 0xEC, 0x4B, 0xEC, 0x4C, /* 0x2C-0x2F */
+ 0xF6, 0xB1, 0xEC, 0x4D, 0xC2, 0xB6, 0xEC, 0x4E, /* 0x30-0x33 */
+ 0xEC, 0x4F, 0xEC, 0x50, 0xEC, 0x51, 0xEC, 0x52, /* 0x34-0x37 */
+ 0xB0, 0xD4, 0xC5, 0xF9, 0xEC, 0x53, 0xEC, 0x54, /* 0x38-0x3B */
+ 0xEC, 0x55, 0xEC, 0x56, 0xF6, 0xB2, 0xEC, 0x57, /* 0x3C-0x3F */
+ 0xEC, 0x58, 0xEC, 0x59, 0xEC, 0x5A, 0xEC, 0x5B, /* 0x40-0x43 */
+ 0xEC, 0x5C, 0xEC, 0x5D, 0xEC, 0x5E, 0xEC, 0x5F, /* 0x44-0x47 */
+ 0xEC, 0x60, 0xEC, 0x61, 0xEC, 0x62, 0xEC, 0x63, /* 0x48-0x4B */
+ 0xEC, 0x64, 0xEC, 0x65, 0xEC, 0x66, 0xEC, 0x67, /* 0x4C-0x4F */
+ 0xEC, 0x68, 0xEC, 0x69, 0xC7, 0xE0, 0xF6, 0xA6, /* 0x50-0x53 */
+ 0xEC, 0x6A, 0xEC, 0x6B, 0xBE, 0xB8, 0xEC, 0x6C, /* 0x54-0x57 */
+ 0xEC, 0x6D, 0xBE, 0xB2, 0xEC, 0x6E, 0xB5, 0xE5, /* 0x58-0x5B */
+ 0xEC, 0x6F, 0xEC, 0x70, 0xB7, 0xC7, 0xEC, 0x71, /* 0x5C-0x5F */
+ 0xBF, 0xBF, 0xC3, 0xD2, 0xC3, 0xE6, 0xEC, 0x72, /* 0x60-0x63 */
+ 0xEC, 0x73, 0xD8, 0xCC, 0xEC, 0x74, 0xEC, 0x75, /* 0x64-0x67 */
+ 0xEC, 0x76, 0xB8, 0xEF, 0xEC, 0x77, 0xEC, 0x78, /* 0x68-0x6B */
+ 0xEC, 0x79, 0xEC, 0x7A, 0xEC, 0x7B, 0xEC, 0x7C, /* 0x6C-0x6F */
+ 0xEC, 0x7D, 0xEC, 0x7E, 0xEC, 0x80, 0xBD, 0xF9, /* 0x70-0x73 */
+ 0xD1, 0xA5, 0xEC, 0x81, 0xB0, 0xD0, 0xEC, 0x82, /* 0x74-0x77 */
+ 0xEC, 0x83, 0xEC, 0x84, 0xEC, 0x85, 0xEC, 0x86, /* 0x78-0x7B */
+ 0xF7, 0xB0, 0xEC, 0x87, 0xEC, 0x88, 0xEC, 0x89, /* 0x7C-0x7F */
+
+ 0xEC, 0x8A, 0xEC, 0x8B, 0xEC, 0x8C, 0xEC, 0x8D, /* 0x80-0x83 */
+ 0xEC, 0x8E, 0xF7, 0xB1, 0xEC, 0x8F, 0xEC, 0x90, /* 0x84-0x87 */
+ 0xEC, 0x91, 0xEC, 0x92, 0xEC, 0x93, 0xD0, 0xAC, /* 0x88-0x8B */
+ 0xEC, 0x94, 0xB0, 0xB0, 0xEC, 0x95, 0xEC, 0x96, /* 0x8C-0x8F */
+ 0xEC, 0x97, 0xF7, 0xB2, 0xF7, 0xB3, 0xEC, 0x98, /* 0x90-0x93 */
+ 0xF7, 0xB4, 0xEC, 0x99, 0xEC, 0x9A, 0xEC, 0x9B, /* 0x94-0x97 */
+ 0xC7, 0xCA, 0xEC, 0x9C, 0xEC, 0x9D, 0xEC, 0x9E, /* 0x98-0x9B */
+ 0xEC, 0x9F, 0xEC, 0xA0, 0xED, 0x40, 0xED, 0x41, /* 0x9C-0x9F */
+ 0xBE, 0xCF, 0xED, 0x42, 0xED, 0x43, 0xF7, 0xB7, /* 0xA0-0xA3 */
+ 0xED, 0x44, 0xED, 0x45, 0xED, 0x46, 0xED, 0x47, /* 0xA4-0xA7 */
+ 0xED, 0x48, 0xED, 0x49, 0xED, 0x4A, 0xF7, 0xB6, /* 0xA8-0xAB */
+ 0xED, 0x4B, 0xB1, 0xDE, 0xED, 0x4C, 0xF7, 0xB5, /* 0xAC-0xAF */
+ 0xED, 0x4D, 0xED, 0x4E, 0xF7, 0xB8, 0xED, 0x4F, /* 0xB0-0xB3 */
+ 0xF7, 0xB9, 0xED, 0x50, 0xED, 0x51, 0xED, 0x52, /* 0xB4-0xB7 */
+ 0xED, 0x53, 0xED, 0x54, 0xED, 0x55, 0xED, 0x56, /* 0xB8-0xBB */
+ 0xED, 0x57, 0xED, 0x58, 0xED, 0x59, 0xED, 0x5A, /* 0xBC-0xBF */
+ 0xED, 0x5B, 0xED, 0x5C, 0xED, 0x5D, 0xED, 0x5E, /* 0xC0-0xC3 */
+ 0xED, 0x5F, 0xED, 0x60, 0xED, 0x61, 0xED, 0x62, /* 0xC4-0xC7 */
+ 0xED, 0x63, 0xED, 0x64, 0xED, 0x65, 0xED, 0x66, /* 0xC8-0xCB */
+ 0xED, 0x67, 0xED, 0x68, 0xED, 0x69, 0xED, 0x6A, /* 0xCC-0xCF */
+ 0xED, 0x6B, 0xED, 0x6C, 0xED, 0x6D, 0xED, 0x6E, /* 0xD0-0xD3 */
+ 0xED, 0x6F, 0xED, 0x70, 0xED, 0x71, 0xED, 0x72, /* 0xD4-0xD7 */
+ 0xED, 0x73, 0xED, 0x74, 0xED, 0x75, 0xED, 0x76, /* 0xD8-0xDB */
+ 0xED, 0x77, 0xED, 0x78, 0xED, 0x79, 0xED, 0x7A, /* 0xDC-0xDF */
+ 0xED, 0x7B, 0xED, 0x7C, 0xED, 0x7D, 0xED, 0x7E, /* 0xE0-0xE3 */
+ 0xED, 0x80, 0xED, 0x81, 0xCE, 0xA4, 0xC8, 0xCD, /* 0xE4-0xE7 */
+ 0xED, 0x82, 0xBA, 0xAB, 0xE8, 0xB8, 0xE8, 0xB9, /* 0xE8-0xEB */
+ 0xE8, 0xBA, 0xBE, 0xC2, 0xED, 0x83, 0xED, 0x84, /* 0xEC-0xEF */
+ 0xED, 0x85, 0xED, 0x86, 0xED, 0x87, 0xD2, 0xF4, /* 0xF0-0xF3 */
+ 0xED, 0x88, 0xD4, 0xCF, 0xC9, 0xD8, 0xED, 0x89, /* 0xF4-0xF7 */
+ 0xED, 0x8A, 0xED, 0x8B, 0xED, 0x8C, 0xED, 0x8D, /* 0xF8-0xFB */
+ 0xED, 0x8E, 0xED, 0x8F, 0xED, 0x90, 0xED, 0x91, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_98[512] = {
+ 0xED, 0x92, 0xED, 0x93, 0xED, 0x94, 0xED, 0x95, /* 0x00-0x03 */
+ 0xED, 0x96, 0xED, 0x97, 0xED, 0x98, 0xED, 0x99, /* 0x04-0x07 */
+ 0xED, 0x9A, 0xED, 0x9B, 0xED, 0x9C, 0xED, 0x9D, /* 0x08-0x0B */
+ 0xED, 0x9E, 0xED, 0x9F, 0xED, 0xA0, 0xEE, 0x40, /* 0x0C-0x0F */
+ 0xEE, 0x41, 0xEE, 0x42, 0xEE, 0x43, 0xEE, 0x44, /* 0x10-0x13 */
+ 0xEE, 0x45, 0xEE, 0x46, 0xEE, 0x47, 0xEE, 0x48, /* 0x14-0x17 */
+ 0xEE, 0x49, 0xEE, 0x4A, 0xEE, 0x4B, 0xEE, 0x4C, /* 0x18-0x1B */
+ 0xEE, 0x4D, 0xEE, 0x4E, 0xEE, 0x4F, 0xEE, 0x50, /* 0x1C-0x1F */
+ 0xEE, 0x51, 0xEE, 0x52, 0xEE, 0x53, 0xEE, 0x54, /* 0x20-0x23 */
+ 0xEE, 0x55, 0xEE, 0x56, 0xEE, 0x57, 0xEE, 0x58, /* 0x24-0x27 */
+ 0xEE, 0x59, 0xEE, 0x5A, 0xEE, 0x5B, 0xEE, 0x5C, /* 0x28-0x2B */
+ 0xEE, 0x5D, 0xEE, 0x5E, 0xEE, 0x5F, 0xEE, 0x60, /* 0x2C-0x2F */
+ 0xEE, 0x61, 0xEE, 0x62, 0xEE, 0x63, 0xEE, 0x64, /* 0x30-0x33 */
+ 0xEE, 0x65, 0xEE, 0x66, 0xEE, 0x67, 0xEE, 0x68, /* 0x34-0x37 */
+ 0xEE, 0x69, 0xEE, 0x6A, 0xEE, 0x6B, 0xEE, 0x6C, /* 0x38-0x3B */
+ 0xEE, 0x6D, 0xEE, 0x6E, 0xEE, 0x6F, 0xEE, 0x70, /* 0x3C-0x3F */
+ 0xEE, 0x71, 0xEE, 0x72, 0xEE, 0x73, 0xEE, 0x74, /* 0x40-0x43 */
+ 0xEE, 0x75, 0xEE, 0x76, 0xEE, 0x77, 0xEE, 0x78, /* 0x44-0x47 */
+ 0xEE, 0x79, 0xEE, 0x7A, 0xEE, 0x7B, 0xEE, 0x7C, /* 0x48-0x4B */
+ 0xEE, 0x7D, 0xEE, 0x7E, 0xEE, 0x80, 0xEE, 0x81, /* 0x4C-0x4F */
+ 0xEE, 0x82, 0xEE, 0x83, 0xEE, 0x84, 0xEE, 0x85, /* 0x50-0x53 */
+ 0xEE, 0x86, 0xEE, 0x87, 0xEE, 0x88, 0xEE, 0x89, /* 0x54-0x57 */
+ 0xEE, 0x8A, 0xEE, 0x8B, 0xEE, 0x8C, 0xEE, 0x8D, /* 0x58-0x5B */
+ 0xEE, 0x8E, 0xEE, 0x8F, 0xEE, 0x90, 0xEE, 0x91, /* 0x5C-0x5F */
+ 0xEE, 0x92, 0xEE, 0x93, 0xEE, 0x94, 0xEE, 0x95, /* 0x60-0x63 */
+ 0xEE, 0x96, 0xEE, 0x97, 0xEE, 0x98, 0xEE, 0x99, /* 0x64-0x67 */
+ 0xEE, 0x9A, 0xEE, 0x9B, 0xEE, 0x9C, 0xEE, 0x9D, /* 0x68-0x6B */
+ 0xEE, 0x9E, 0xEE, 0x9F, 0xEE, 0xA0, 0xEF, 0x40, /* 0x6C-0x6F */
+ 0xEF, 0x41, 0xEF, 0x42, 0xEF, 0x43, 0xEF, 0x44, /* 0x70-0x73 */
+ 0xEF, 0x45, 0xD2, 0xB3, 0xB6, 0xA5, 0xC7, 0xEA, /* 0x74-0x77 */
+ 0xF1, 0xFC, 0xCF, 0xEE, 0xCB, 0xB3, 0xD0, 0xEB, /* 0x78-0x7B */
+ 0xE7, 0xEF, 0xCD, 0xE7, 0xB9, 0xCB, 0xB6, 0xD9, /* 0x7C-0x7F */
+
+ 0xF1, 0xFD, 0xB0, 0xE4, 0xCB, 0xCC, 0xF1, 0xFE, /* 0x80-0x83 */
+ 0xD4, 0xA4, 0xC2, 0xAD, 0xC1, 0xEC, 0xC6, 0xC4, /* 0x84-0x87 */
+ 0xBE, 0xB1, 0xF2, 0xA1, 0xBC, 0xD5, 0xEF, 0x46, /* 0x88-0x8B */
+ 0xF2, 0xA2, 0xF2, 0xA3, 0xEF, 0x47, 0xF2, 0xA4, /* 0x8C-0x8F */
+ 0xD2, 0xC3, 0xC6, 0xB5, 0xEF, 0x48, 0xCD, 0xC7, /* 0x90-0x93 */
+ 0xF2, 0xA5, 0xEF, 0x49, 0xD3, 0xB1, 0xBF, 0xC5, /* 0x94-0x97 */
+ 0xCC, 0xE2, 0xEF, 0x4A, 0xF2, 0xA6, 0xF2, 0xA7, /* 0x98-0x9B */
+ 0xD1, 0xD5, 0xB6, 0xEE, 0xF2, 0xA8, 0xF2, 0xA9, /* 0x9C-0x9F */
+ 0xB5, 0xDF, 0xF2, 0xAA, 0xF2, 0xAB, 0xEF, 0x4B, /* 0xA0-0xA3 */
+ 0xB2, 0xFC, 0xF2, 0xAC, 0xF2, 0xAD, 0xC8, 0xA7, /* 0xA4-0xA7 */
+ 0xEF, 0x4C, 0xEF, 0x4D, 0xEF, 0x4E, 0xEF, 0x4F, /* 0xA8-0xAB */
+ 0xEF, 0x50, 0xEF, 0x51, 0xEF, 0x52, 0xEF, 0x53, /* 0xAC-0xAF */
+ 0xEF, 0x54, 0xEF, 0x55, 0xEF, 0x56, 0xEF, 0x57, /* 0xB0-0xB3 */
+ 0xEF, 0x58, 0xEF, 0x59, 0xEF, 0x5A, 0xEF, 0x5B, /* 0xB4-0xB7 */
+ 0xEF, 0x5C, 0xEF, 0x5D, 0xEF, 0x5E, 0xEF, 0x5F, /* 0xB8-0xBB */
+ 0xEF, 0x60, 0xEF, 0x61, 0xEF, 0x62, 0xEF, 0x63, /* 0xBC-0xBF */
+ 0xEF, 0x64, 0xEF, 0x65, 0xEF, 0x66, 0xEF, 0x67, /* 0xC0-0xC3 */
+ 0xEF, 0x68, 0xEF, 0x69, 0xEF, 0x6A, 0xEF, 0x6B, /* 0xC4-0xC7 */
+ 0xEF, 0x6C, 0xEF, 0x6D, 0xEF, 0x6E, 0xEF, 0x6F, /* 0xC8-0xCB */
+ 0xEF, 0x70, 0xEF, 0x71, 0xB7, 0xE7, 0xEF, 0x72, /* 0xCC-0xCF */
+ 0xEF, 0x73, 0xEC, 0xA9, 0xEC, 0xAA, 0xEC, 0xAB, /* 0xD0-0xD3 */
+ 0xEF, 0x74, 0xEC, 0xAC, 0xEF, 0x75, 0xEF, 0x76, /* 0xD4-0xD7 */
+ 0xC6, 0xAE, 0xEC, 0xAD, 0xEC, 0xAE, 0xEF, 0x77, /* 0xD8-0xDB */
+ 0xEF, 0x78, 0xEF, 0x79, 0xB7, 0xC9, 0xCA, 0xB3, /* 0xDC-0xDF */
+ 0xEF, 0x7A, 0xEF, 0x7B, 0xEF, 0x7C, 0xEF, 0x7D, /* 0xE0-0xE3 */
+ 0xEF, 0x7E, 0xEF, 0x80, 0xEF, 0x81, 0xE2, 0xB8, /* 0xE4-0xE7 */
+ 0xF7, 0xCF, 0xEF, 0x82, 0xEF, 0x83, 0xEF, 0x84, /* 0xE8-0xEB */
+ 0xEF, 0x85, 0xEF, 0x86, 0xEF, 0x87, 0xEF, 0x88, /* 0xEC-0xEF */
+ 0xEF, 0x89, 0xEF, 0x8A, 0xEF, 0x8B, 0xEF, 0x8C, /* 0xF0-0xF3 */
+ 0xEF, 0x8D, 0xEF, 0x8E, 0xEF, 0x8F, 0xEF, 0x90, /* 0xF4-0xF7 */
+ 0xEF, 0x91, 0xEF, 0x92, 0xEF, 0x93, 0xEF, 0x94, /* 0xF8-0xFB */
+ 0xEF, 0x95, 0xEF, 0x96, 0xEF, 0x97, 0xEF, 0x98, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_99[512] = {
+ 0xEF, 0x99, 0xEF, 0x9A, 0xEF, 0x9B, 0xEF, 0x9C, /* 0x00-0x03 */
+ 0xEF, 0x9D, 0xEF, 0x9E, 0xEF, 0x9F, 0xEF, 0xA0, /* 0x04-0x07 */
+ 0xF0, 0x40, 0xF0, 0x41, 0xF0, 0x42, 0xF0, 0x43, /* 0x08-0x0B */
+ 0xF0, 0x44, 0xF7, 0xD0, 0xF0, 0x45, 0xF0, 0x46, /* 0x0C-0x0F */
+ 0xB2, 0xCD, 0xF0, 0x47, 0xF0, 0x48, 0xF0, 0x49, /* 0x10-0x13 */
+ 0xF0, 0x4A, 0xF0, 0x4B, 0xF0, 0x4C, 0xF0, 0x4D, /* 0x14-0x17 */
+ 0xF0, 0x4E, 0xF0, 0x4F, 0xF0, 0x50, 0xF0, 0x51, /* 0x18-0x1B */
+ 0xF0, 0x52, 0xF0, 0x53, 0xF0, 0x54, 0xF0, 0x55, /* 0x1C-0x1F */
+ 0xF0, 0x56, 0xF0, 0x57, 0xF0, 0x58, 0xF0, 0x59, /* 0x20-0x23 */
+ 0xF0, 0x5A, 0xF0, 0x5B, 0xF0, 0x5C, 0xF0, 0x5D, /* 0x24-0x27 */
+ 0xF0, 0x5E, 0xF0, 0x5F, 0xF0, 0x60, 0xF0, 0x61, /* 0x28-0x2B */
+ 0xF0, 0x62, 0xF0, 0x63, 0xF7, 0xD1, 0xF0, 0x64, /* 0x2C-0x2F */
+ 0xF0, 0x65, 0xF0, 0x66, 0xF0, 0x67, 0xF0, 0x68, /* 0x30-0x33 */
+ 0xF0, 0x69, 0xF0, 0x6A, 0xF0, 0x6B, 0xF0, 0x6C, /* 0x34-0x37 */
+ 0xF0, 0x6D, 0xF0, 0x6E, 0xF0, 0x6F, 0xF0, 0x70, /* 0x38-0x3B */
+ 0xF0, 0x71, 0xF0, 0x72, 0xF0, 0x73, 0xF0, 0x74, /* 0x3C-0x3F */
+ 0xF0, 0x75, 0xF0, 0x76, 0xF0, 0x77, 0xF0, 0x78, /* 0x40-0x43 */
+ 0xF0, 0x79, 0xF0, 0x7A, 0xF0, 0x7B, 0xF0, 0x7C, /* 0x44-0x47 */
+ 0xF0, 0x7D, 0xF0, 0x7E, 0xF0, 0x80, 0xF0, 0x81, /* 0x48-0x4B */
+ 0xF0, 0x82, 0xF0, 0x83, 0xF0, 0x84, 0xF0, 0x85, /* 0x4C-0x4F */
+ 0xF0, 0x86, 0xF0, 0x87, 0xF0, 0x88, 0xF0, 0x89, /* 0x50-0x53 */
+ 0xF7, 0xD3, 0xF7, 0xD2, 0xF0, 0x8A, 0xF0, 0x8B, /* 0x54-0x57 */
+ 0xF0, 0x8C, 0xF0, 0x8D, 0xF0, 0x8E, 0xF0, 0x8F, /* 0x58-0x5B */
+ 0xF0, 0x90, 0xF0, 0x91, 0xF0, 0x92, 0xF0, 0x93, /* 0x5C-0x5F */
+ 0xF0, 0x94, 0xF0, 0x95, 0xF0, 0x96, 0xE2, 0xBB, /* 0x60-0x63 */
+ 0xF0, 0x97, 0xBC, 0xA2, 0xF0, 0x98, 0xE2, 0xBC, /* 0x64-0x67 */
+ 0xE2, 0xBD, 0xE2, 0xBE, 0xE2, 0xBF, 0xE2, 0xC0, /* 0x68-0x6B */
+ 0xE2, 0xC1, 0xB7, 0xB9, 0xD2, 0xFB, 0xBD, 0xA4, /* 0x6C-0x6F */
+ 0xCA, 0xCE, 0xB1, 0xA5, 0xCB, 0xC7, 0xF0, 0x99, /* 0x70-0x73 */
+ 0xE2, 0xC2, 0xB6, 0xFC, 0xC8, 0xC4, 0xE2, 0xC3, /* 0x74-0x77 */
+ 0xF0, 0x9A, 0xF0, 0x9B, 0xBD, 0xC8, 0xF0, 0x9C, /* 0x78-0x7B */
+ 0xB1, 0xFD, 0xE2, 0xC4, 0xF0, 0x9D, 0xB6, 0xF6, /* 0x7C-0x7F */
+
+ 0xE2, 0xC5, 0xC4, 0xD9, 0xF0, 0x9E, 0xF0, 0x9F, /* 0x80-0x83 */
+ 0xE2, 0xC6, 0xCF, 0xDA, 0xB9, 0xDD, 0xE2, 0xC7, /* 0x84-0x87 */
+ 0xC0, 0xA1, 0xF0, 0xA0, 0xE2, 0xC8, 0xB2, 0xF6, /* 0x88-0x8B */
+ 0xF1, 0x40, 0xE2, 0xC9, 0xF1, 0x41, 0xC1, 0xF3, /* 0x8C-0x8F */
+ 0xE2, 0xCA, 0xE2, 0xCB, 0xC2, 0xF8, 0xE2, 0xCC, /* 0x90-0x93 */
+ 0xE2, 0xCD, 0xE2, 0xCE, 0xCA, 0xD7, 0xD8, 0xB8, /* 0x94-0x97 */
+ 0xD9, 0xE5, 0xCF, 0xE3, 0xF1, 0x42, 0xF1, 0x43, /* 0x98-0x9B */
+ 0xF1, 0x44, 0xF1, 0x45, 0xF1, 0x46, 0xF1, 0x47, /* 0x9C-0x9F */
+ 0xF1, 0x48, 0xF1, 0x49, 0xF1, 0x4A, 0xF1, 0x4B, /* 0xA0-0xA3 */
+ 0xF1, 0x4C, 0xF0, 0xA5, 0xF1, 0x4D, 0xF1, 0x4E, /* 0xA4-0xA7 */
+ 0xDC, 0xB0, 0xF1, 0x4F, 0xF1, 0x50, 0xF1, 0x51, /* 0xA8-0xAB */
+ 0xF1, 0x52, 0xF1, 0x53, 0xF1, 0x54, 0xF1, 0x55, /* 0xAC-0xAF */
+ 0xF1, 0x56, 0xF1, 0x57, 0xF1, 0x58, 0xF1, 0x59, /* 0xB0-0xB3 */
+ 0xF1, 0x5A, 0xF1, 0x5B, 0xF1, 0x5C, 0xF1, 0x5D, /* 0xB4-0xB7 */
+ 0xF1, 0x5E, 0xF1, 0x5F, 0xF1, 0x60, 0xF1, 0x61, /* 0xB8-0xBB */
+ 0xF1, 0x62, 0xF1, 0x63, 0xF1, 0x64, 0xF1, 0x65, /* 0xBC-0xBF */
+ 0xF1, 0x66, 0xF1, 0x67, 0xF1, 0x68, 0xF1, 0x69, /* 0xC0-0xC3 */
+ 0xF1, 0x6A, 0xF1, 0x6B, 0xF1, 0x6C, 0xF1, 0x6D, /* 0xC4-0xC7 */
+ 0xF1, 0x6E, 0xF1, 0x6F, 0xF1, 0x70, 0xF1, 0x71, /* 0xC8-0xCB */
+ 0xF1, 0x72, 0xF1, 0x73, 0xF1, 0x74, 0xF1, 0x75, /* 0xCC-0xCF */
+ 0xF1, 0x76, 0xF1, 0x77, 0xF1, 0x78, 0xF1, 0x79, /* 0xD0-0xD3 */
+ 0xF1, 0x7A, 0xF1, 0x7B, 0xF1, 0x7C, 0xF1, 0x7D, /* 0xD4-0xD7 */
+ 0xF1, 0x7E, 0xF1, 0x80, 0xF1, 0x81, 0xF1, 0x82, /* 0xD8-0xDB */
+ 0xF1, 0x83, 0xF1, 0x84, 0xF1, 0x85, 0xF1, 0x86, /* 0xDC-0xDF */
+ 0xF1, 0x87, 0xF1, 0x88, 0xF1, 0x89, 0xF1, 0x8A, /* 0xE0-0xE3 */
+ 0xF1, 0x8B, 0xF1, 0x8C, 0xF1, 0x8D, 0xF1, 0x8E, /* 0xE4-0xE7 */
+ 0xF1, 0x8F, 0xF1, 0x90, 0xF1, 0x91, 0xF1, 0x92, /* 0xE8-0xEB */
+ 0xF1, 0x93, 0xF1, 0x94, 0xF1, 0x95, 0xF1, 0x96, /* 0xEC-0xEF */
+ 0xF1, 0x97, 0xF1, 0x98, 0xF1, 0x99, 0xF1, 0x9A, /* 0xF0-0xF3 */
+ 0xF1, 0x9B, 0xF1, 0x9C, 0xF1, 0x9D, 0xF1, 0x9E, /* 0xF4-0xF7 */
+ 0xF1, 0x9F, 0xF1, 0xA0, 0xF2, 0x40, 0xF2, 0x41, /* 0xF8-0xFB */
+ 0xF2, 0x42, 0xF2, 0x43, 0xF2, 0x44, 0xF2, 0x45, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_9A[512] = {
+ 0xF2, 0x46, 0xF2, 0x47, 0xF2, 0x48, 0xF2, 0x49, /* 0x00-0x03 */
+ 0xF2, 0x4A, 0xF2, 0x4B, 0xF2, 0x4C, 0xF2, 0x4D, /* 0x04-0x07 */
+ 0xF2, 0x4E, 0xF2, 0x4F, 0xF2, 0x50, 0xF2, 0x51, /* 0x08-0x0B */
+ 0xF2, 0x52, 0xF2, 0x53, 0xF2, 0x54, 0xF2, 0x55, /* 0x0C-0x0F */
+ 0xF2, 0x56, 0xF2, 0x57, 0xF2, 0x58, 0xF2, 0x59, /* 0x10-0x13 */
+ 0xF2, 0x5A, 0xF2, 0x5B, 0xF2, 0x5C, 0xF2, 0x5D, /* 0x14-0x17 */
+ 0xF2, 0x5E, 0xF2, 0x5F, 0xF2, 0x60, 0xF2, 0x61, /* 0x18-0x1B */
+ 0xF2, 0x62, 0xF2, 0x63, 0xF2, 0x64, 0xF2, 0x65, /* 0x1C-0x1F */
+ 0xF2, 0x66, 0xF2, 0x67, 0xF2, 0x68, 0xF2, 0x69, /* 0x20-0x23 */
+ 0xF2, 0x6A, 0xF2, 0x6B, 0xF2, 0x6C, 0xF2, 0x6D, /* 0x24-0x27 */
+ 0xF2, 0x6E, 0xF2, 0x6F, 0xF2, 0x70, 0xF2, 0x71, /* 0x28-0x2B */
+ 0xF2, 0x72, 0xF2, 0x73, 0xF2, 0x74, 0xF2, 0x75, /* 0x2C-0x2F */
+ 0xF2, 0x76, 0xF2, 0x77, 0xF2, 0x78, 0xF2, 0x79, /* 0x30-0x33 */
+ 0xF2, 0x7A, 0xF2, 0x7B, 0xF2, 0x7C, 0xF2, 0x7D, /* 0x34-0x37 */
+ 0xF2, 0x7E, 0xF2, 0x80, 0xF2, 0x81, 0xF2, 0x82, /* 0x38-0x3B */
+ 0xF2, 0x83, 0xF2, 0x84, 0xF2, 0x85, 0xF2, 0x86, /* 0x3C-0x3F */
+ 0xF2, 0x87, 0xF2, 0x88, 0xF2, 0x89, 0xF2, 0x8A, /* 0x40-0x43 */
+ 0xF2, 0x8B, 0xF2, 0x8C, 0xF2, 0x8D, 0xF2, 0x8E, /* 0x44-0x47 */
+ 0xF2, 0x8F, 0xF2, 0x90, 0xF2, 0x91, 0xF2, 0x92, /* 0x48-0x4B */
+ 0xF2, 0x93, 0xF2, 0x94, 0xF2, 0x95, 0xF2, 0x96, /* 0x4C-0x4F */
+ 0xF2, 0x97, 0xF2, 0x98, 0xF2, 0x99, 0xF2, 0x9A, /* 0x50-0x53 */
+ 0xF2, 0x9B, 0xF2, 0x9C, 0xF2, 0x9D, 0xF2, 0x9E, /* 0x54-0x57 */
+ 0xF2, 0x9F, 0xF2, 0xA0, 0xF3, 0x40, 0xF3, 0x41, /* 0x58-0x5B */
+ 0xF3, 0x42, 0xF3, 0x43, 0xF3, 0x44, 0xF3, 0x45, /* 0x5C-0x5F */
+ 0xF3, 0x46, 0xF3, 0x47, 0xF3, 0x48, 0xF3, 0x49, /* 0x60-0x63 */
+ 0xF3, 0x4A, 0xF3, 0x4B, 0xF3, 0x4C, 0xF3, 0x4D, /* 0x64-0x67 */
+ 0xF3, 0x4E, 0xF3, 0x4F, 0xF3, 0x50, 0xF3, 0x51, /* 0x68-0x6B */
+ 0xC2, 0xED, 0xD4, 0xA6, 0xCD, 0xD4, 0xD1, 0xB1, /* 0x6C-0x6F */
+ 0xB3, 0xDB, 0xC7, 0xFD, 0xF3, 0x52, 0xB2, 0xB5, /* 0x70-0x73 */
+ 0xC2, 0xBF, 0xE6, 0xE0, 0xCA, 0xBB, 0xE6, 0xE1, /* 0x74-0x77 */
+ 0xE6, 0xE2, 0xBE, 0xD4, 0xE6, 0xE3, 0xD7, 0xA4, /* 0x78-0x7B */
+ 0xCD, 0xD5, 0xE6, 0xE5, 0xBC, 0xDD, 0xE6, 0xE4, /* 0x7C-0x7F */
+
+ 0xE6, 0xE6, 0xE6, 0xE7, 0xC2, 0xEE, 0xF3, 0x53, /* 0x80-0x83 */
+ 0xBD, 0xBE, 0xE6, 0xE8, 0xC2, 0xE6, 0xBA, 0xA7, /* 0x84-0x87 */
+ 0xE6, 0xE9, 0xF3, 0x54, 0xE6, 0xEA, 0xB3, 0xD2, /* 0x88-0x8B */
+ 0xD1, 0xE9, 0xF3, 0x55, 0xF3, 0x56, 0xBF, 0xA5, /* 0x8C-0x8F */
+ 0xE6, 0xEB, 0xC6, 0xEF, 0xE6, 0xEC, 0xE6, 0xED, /* 0x90-0x93 */
+ 0xF3, 0x57, 0xF3, 0x58, 0xE6, 0xEE, 0xC6, 0xAD, /* 0x94-0x97 */
+ 0xE6, 0xEF, 0xF3, 0x59, 0xC9, 0xA7, 0xE6, 0xF0, /* 0x98-0x9B */
+ 0xE6, 0xF1, 0xE6, 0xF2, 0xE5, 0xB9, 0xE6, 0xF3, /* 0x9C-0x9F */
+ 0xE6, 0xF4, 0xC2, 0xE2, 0xE6, 0xF5, 0xE6, 0xF6, /* 0xA0-0xA3 */
+ 0xD6, 0xE8, 0xE6, 0xF7, 0xF3, 0x5A, 0xE6, 0xF8, /* 0xA4-0xA7 */
+ 0xB9, 0xC7, 0xF3, 0x5B, 0xF3, 0x5C, 0xF3, 0x5D, /* 0xA8-0xAB */
+ 0xF3, 0x5E, 0xF3, 0x5F, 0xF3, 0x60, 0xF3, 0x61, /* 0xAC-0xAF */
+ 0xF7, 0xBB, 0xF7, 0xBA, 0xF3, 0x62, 0xF3, 0x63, /* 0xB0-0xB3 */
+ 0xF3, 0x64, 0xF3, 0x65, 0xF7, 0xBE, 0xF7, 0xBC, /* 0xB4-0xB7 */
+ 0xBA, 0xA1, 0xF3, 0x66, 0xF7, 0xBF, 0xF3, 0x67, /* 0xB8-0xBB */
+ 0xF7, 0xC0, 0xF3, 0x68, 0xF3, 0x69, 0xF3, 0x6A, /* 0xBC-0xBF */
+ 0xF7, 0xC2, 0xF7, 0xC1, 0xF7, 0xC4, 0xF3, 0x6B, /* 0xC0-0xC3 */
+ 0xF3, 0x6C, 0xF7, 0xC3, 0xF3, 0x6D, 0xF3, 0x6E, /* 0xC4-0xC7 */
+ 0xF3, 0x6F, 0xF3, 0x70, 0xF3, 0x71, 0xF7, 0xC5, /* 0xC8-0xCB */
+ 0xF7, 0xC6, 0xF3, 0x72, 0xF3, 0x73, 0xF3, 0x74, /* 0xCC-0xCF */
+ 0xF3, 0x75, 0xF7, 0xC7, 0xF3, 0x76, 0xCB, 0xE8, /* 0xD0-0xD3 */
+ 0xF3, 0x77, 0xF3, 0x78, 0xF3, 0x79, 0xF3, 0x7A, /* 0xD4-0xD7 */
+ 0xB8, 0xDF, 0xF3, 0x7B, 0xF3, 0x7C, 0xF3, 0x7D, /* 0xD8-0xDB */
+ 0xF3, 0x7E, 0xF3, 0x80, 0xF3, 0x81, 0xF7, 0xD4, /* 0xDC-0xDF */
+ 0xF3, 0x82, 0xF7, 0xD5, 0xF3, 0x83, 0xF3, 0x84, /* 0xE0-0xE3 */
+ 0xF3, 0x85, 0xF3, 0x86, 0xF7, 0xD6, 0xF3, 0x87, /* 0xE4-0xE7 */
+ 0xF3, 0x88, 0xF3, 0x89, 0xF3, 0x8A, 0xF7, 0xD8, /* 0xE8-0xEB */
+ 0xF3, 0x8B, 0xF7, 0xDA, 0xF3, 0x8C, 0xF7, 0xD7, /* 0xEC-0xEF */
+ 0xF3, 0x8D, 0xF3, 0x8E, 0xF3, 0x8F, 0xF3, 0x90, /* 0xF0-0xF3 */
+ 0xF3, 0x91, 0xF3, 0x92, 0xF3, 0x93, 0xF3, 0x94, /* 0xF4-0xF7 */
+ 0xF3, 0x95, 0xF7, 0xDB, 0xF3, 0x96, 0xF7, 0xD9, /* 0xF8-0xFB */
+ 0xF3, 0x97, 0xF3, 0x98, 0xF3, 0x99, 0xF3, 0x9A, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_9B[512] = {
+ 0xF3, 0x9B, 0xF3, 0x9C, 0xF3, 0x9D, 0xD7, 0xD7, /* 0x00-0x03 */
+ 0xF3, 0x9E, 0xF3, 0x9F, 0xF3, 0xA0, 0xF4, 0x40, /* 0x04-0x07 */
+ 0xF7, 0xDC, 0xF4, 0x41, 0xF4, 0x42, 0xF4, 0x43, /* 0x08-0x0B */
+ 0xF4, 0x44, 0xF4, 0x45, 0xF4, 0x46, 0xF7, 0xDD, /* 0x0C-0x0F */
+ 0xF4, 0x47, 0xF4, 0x48, 0xF4, 0x49, 0xF7, 0xDE, /* 0x10-0x13 */
+ 0xF4, 0x4A, 0xF4, 0x4B, 0xF4, 0x4C, 0xF4, 0x4D, /* 0x14-0x17 */
+ 0xF4, 0x4E, 0xF4, 0x4F, 0xF4, 0x50, 0xF4, 0x51, /* 0x18-0x1B */
+ 0xF4, 0x52, 0xF4, 0x53, 0xF4, 0x54, 0xF7, 0xDF, /* 0x1C-0x1F */
+ 0xF4, 0x55, 0xF4, 0x56, 0xF4, 0x57, 0xF7, 0xE0, /* 0x20-0x23 */
+ 0xF4, 0x58, 0xF4, 0x59, 0xF4, 0x5A, 0xF4, 0x5B, /* 0x24-0x27 */
+ 0xF4, 0x5C, 0xF4, 0x5D, 0xF4, 0x5E, 0xF4, 0x5F, /* 0x28-0x2B */
+ 0xF4, 0x60, 0xF4, 0x61, 0xF4, 0x62, 0xDB, 0xCB, /* 0x2C-0x2F */
+ 0xF4, 0x63, 0xF4, 0x64, 0xD8, 0xAA, 0xF4, 0x65, /* 0x30-0x33 */
+ 0xF4, 0x66, 0xF4, 0x67, 0xF4, 0x68, 0xF4, 0x69, /* 0x34-0x37 */
+ 0xF4, 0x6A, 0xF4, 0x6B, 0xF4, 0x6C, 0xE5, 0xF7, /* 0x38-0x3B */
+ 0xB9, 0xED, 0xF4, 0x6D, 0xF4, 0x6E, 0xF4, 0x6F, /* 0x3C-0x3F */
+ 0xF4, 0x70, 0xBF, 0xFD, 0xBB, 0xEA, 0xF7, 0xC9, /* 0x40-0x43 */
+ 0xC6, 0xC7, 0xF7, 0xC8, 0xF4, 0x71, 0xF7, 0xCA, /* 0x44-0x47 */
+ 0xF7, 0xCC, 0xF7, 0xCB, 0xF4, 0x72, 0xF4, 0x73, /* 0x48-0x4B */
+ 0xF4, 0x74, 0xF7, 0xCD, 0xF4, 0x75, 0xCE, 0xBA, /* 0x4C-0x4F */
+ 0xF4, 0x76, 0xF7, 0xCE, 0xF4, 0x77, 0xF4, 0x78, /* 0x50-0x53 */
+ 0xC4, 0xA7, 0xF4, 0x79, 0xF4, 0x7A, 0xF4, 0x7B, /* 0x54-0x57 */
+ 0xF4, 0x7C, 0xF4, 0x7D, 0xF4, 0x7E, 0xF4, 0x80, /* 0x58-0x5B */
+ 0xF4, 0x81, 0xF4, 0x82, 0xF4, 0x83, 0xF4, 0x84, /* 0x5C-0x5F */
+ 0xF4, 0x85, 0xF4, 0x86, 0xF4, 0x87, 0xF4, 0x88, /* 0x60-0x63 */
+ 0xF4, 0x89, 0xF4, 0x8A, 0xF4, 0x8B, 0xF4, 0x8C, /* 0x64-0x67 */
+ 0xF4, 0x8D, 0xF4, 0x8E, 0xF4, 0x8F, 0xF4, 0x90, /* 0x68-0x6B */
+ 0xF4, 0x91, 0xF4, 0x92, 0xF4, 0x93, 0xF4, 0x94, /* 0x6C-0x6F */
+ 0xF4, 0x95, 0xF4, 0x96, 0xF4, 0x97, 0xF4, 0x98, /* 0x70-0x73 */
+ 0xF4, 0x99, 0xF4, 0x9A, 0xF4, 0x9B, 0xF4, 0x9C, /* 0x74-0x77 */
+ 0xF4, 0x9D, 0xF4, 0x9E, 0xF4, 0x9F, 0xF4, 0xA0, /* 0x78-0x7B */
+ 0xF5, 0x40, 0xF5, 0x41, 0xF5, 0x42, 0xF5, 0x43, /* 0x7C-0x7F */
+
+ 0xF5, 0x44, 0xF5, 0x45, 0xF5, 0x46, 0xF5, 0x47, /* 0x80-0x83 */
+ 0xF5, 0x48, 0xF5, 0x49, 0xF5, 0x4A, 0xF5, 0x4B, /* 0x84-0x87 */
+ 0xF5, 0x4C, 0xF5, 0x4D, 0xF5, 0x4E, 0xF5, 0x4F, /* 0x88-0x8B */
+ 0xF5, 0x50, 0xF5, 0x51, 0xF5, 0x52, 0xF5, 0x53, /* 0x8C-0x8F */
+ 0xF5, 0x54, 0xF5, 0x55, 0xF5, 0x56, 0xF5, 0x57, /* 0x90-0x93 */
+ 0xF5, 0x58, 0xF5, 0x59, 0xF5, 0x5A, 0xF5, 0x5B, /* 0x94-0x97 */
+ 0xF5, 0x5C, 0xF5, 0x5D, 0xF5, 0x5E, 0xF5, 0x5F, /* 0x98-0x9B */
+ 0xF5, 0x60, 0xF5, 0x61, 0xF5, 0x62, 0xF5, 0x63, /* 0x9C-0x9F */
+ 0xF5, 0x64, 0xF5, 0x65, 0xF5, 0x66, 0xF5, 0x67, /* 0xA0-0xA3 */
+ 0xF5, 0x68, 0xF5, 0x69, 0xF5, 0x6A, 0xF5, 0x6B, /* 0xA4-0xA7 */
+ 0xF5, 0x6C, 0xF5, 0x6D, 0xF5, 0x6E, 0xF5, 0x6F, /* 0xA8-0xAB */
+ 0xF5, 0x70, 0xF5, 0x71, 0xF5, 0x72, 0xF5, 0x73, /* 0xAC-0xAF */
+ 0xF5, 0x74, 0xF5, 0x75, 0xF5, 0x76, 0xF5, 0x77, /* 0xB0-0xB3 */
+ 0xF5, 0x78, 0xF5, 0x79, 0xF5, 0x7A, 0xF5, 0x7B, /* 0xB4-0xB7 */
+ 0xF5, 0x7C, 0xF5, 0x7D, 0xF5, 0x7E, 0xF5, 0x80, /* 0xB8-0xBB */
+ 0xF5, 0x81, 0xF5, 0x82, 0xF5, 0x83, 0xF5, 0x84, /* 0xBC-0xBF */
+ 0xF5, 0x85, 0xF5, 0x86, 0xF5, 0x87, 0xF5, 0x88, /* 0xC0-0xC3 */
+ 0xF5, 0x89, 0xF5, 0x8A, 0xF5, 0x8B, 0xF5, 0x8C, /* 0xC4-0xC7 */
+ 0xF5, 0x8D, 0xF5, 0x8E, 0xF5, 0x8F, 0xF5, 0x90, /* 0xC8-0xCB */
+ 0xF5, 0x91, 0xF5, 0x92, 0xF5, 0x93, 0xF5, 0x94, /* 0xCC-0xCF */
+ 0xF5, 0x95, 0xF5, 0x96, 0xF5, 0x97, 0xF5, 0x98, /* 0xD0-0xD3 */
+ 0xF5, 0x99, 0xF5, 0x9A, 0xF5, 0x9B, 0xF5, 0x9C, /* 0xD4-0xD7 */
+ 0xF5, 0x9D, 0xF5, 0x9E, 0xF5, 0x9F, 0xF5, 0xA0, /* 0xD8-0xDB */
+ 0xF6, 0x40, 0xF6, 0x41, 0xF6, 0x42, 0xF6, 0x43, /* 0xDC-0xDF */
+ 0xF6, 0x44, 0xF6, 0x45, 0xF6, 0x46, 0xF6, 0x47, /* 0xE0-0xE3 */
+ 0xF6, 0x48, 0xF6, 0x49, 0xF6, 0x4A, 0xF6, 0x4B, /* 0xE4-0xE7 */
+ 0xF6, 0x4C, 0xF6, 0x4D, 0xF6, 0x4E, 0xF6, 0x4F, /* 0xE8-0xEB */
+ 0xF6, 0x50, 0xF6, 0x51, 0xF6, 0x52, 0xF6, 0x53, /* 0xEC-0xEF */
+ 0xF6, 0x54, 0xF6, 0x55, 0xF6, 0x56, 0xF6, 0x57, /* 0xF0-0xF3 */
+ 0xF6, 0x58, 0xF6, 0x59, 0xF6, 0x5A, 0xF6, 0x5B, /* 0xF4-0xF7 */
+ 0xF6, 0x5C, 0xF6, 0x5D, 0xF6, 0x5E, 0xF6, 0x5F, /* 0xF8-0xFB */
+ 0xF6, 0x60, 0xF6, 0x61, 0xF6, 0x62, 0xF6, 0x63, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_9C[512] = {
+ 0xF6, 0x64, 0xF6, 0x65, 0xF6, 0x66, 0xF6, 0x67, /* 0x00-0x03 */
+ 0xF6, 0x68, 0xF6, 0x69, 0xF6, 0x6A, 0xF6, 0x6B, /* 0x04-0x07 */
+ 0xF6, 0x6C, 0xF6, 0x6D, 0xF6, 0x6E, 0xF6, 0x6F, /* 0x08-0x0B */
+ 0xF6, 0x70, 0xF6, 0x71, 0xF6, 0x72, 0xF6, 0x73, /* 0x0C-0x0F */
+ 0xF6, 0x74, 0xF6, 0x75, 0xF6, 0x76, 0xF6, 0x77, /* 0x10-0x13 */
+ 0xF6, 0x78, 0xF6, 0x79, 0xF6, 0x7A, 0xF6, 0x7B, /* 0x14-0x17 */
+ 0xF6, 0x7C, 0xF6, 0x7D, 0xF6, 0x7E, 0xF6, 0x80, /* 0x18-0x1B */
+ 0xF6, 0x81, 0xF6, 0x82, 0xF6, 0x83, 0xF6, 0x84, /* 0x1C-0x1F */
+ 0xF6, 0x85, 0xF6, 0x86, 0xF6, 0x87, 0xF6, 0x88, /* 0x20-0x23 */
+ 0xF6, 0x89, 0xF6, 0x8A, 0xF6, 0x8B, 0xF6, 0x8C, /* 0x24-0x27 */
+ 0xF6, 0x8D, 0xF6, 0x8E, 0xF6, 0x8F, 0xF6, 0x90, /* 0x28-0x2B */
+ 0xF6, 0x91, 0xF6, 0x92, 0xF6, 0x93, 0xF6, 0x94, /* 0x2C-0x2F */
+ 0xF6, 0x95, 0xF6, 0x96, 0xF6, 0x97, 0xF6, 0x98, /* 0x30-0x33 */
+ 0xF6, 0x99, 0xF6, 0x9A, 0xF6, 0x9B, 0xF6, 0x9C, /* 0x34-0x37 */
+ 0xF6, 0x9D, 0xF6, 0x9E, 0xF6, 0x9F, 0xF6, 0xA0, /* 0x38-0x3B */
+ 0xF7, 0x40, 0xF7, 0x41, 0xF7, 0x42, 0xF7, 0x43, /* 0x3C-0x3F */
+ 0xF7, 0x44, 0xF7, 0x45, 0xF7, 0x46, 0xF7, 0x47, /* 0x40-0x43 */
+ 0xF7, 0x48, 0xF7, 0x49, 0xF7, 0x4A, 0xF7, 0x4B, /* 0x44-0x47 */
+ 0xF7, 0x4C, 0xF7, 0x4D, 0xF7, 0x4E, 0xF7, 0x4F, /* 0x48-0x4B */
+ 0xF7, 0x50, 0xF7, 0x51, 0xF7, 0x52, 0xF7, 0x53, /* 0x4C-0x4F */
+ 0xF7, 0x54, 0xF7, 0x55, 0xF7, 0x56, 0xF7, 0x57, /* 0x50-0x53 */
+ 0xF7, 0x58, 0xF7, 0x59, 0xF7, 0x5A, 0xF7, 0x5B, /* 0x54-0x57 */
+ 0xF7, 0x5C, 0xF7, 0x5D, 0xF7, 0x5E, 0xF7, 0x5F, /* 0x58-0x5B */
+ 0xF7, 0x60, 0xF7, 0x61, 0xF7, 0x62, 0xF7, 0x63, /* 0x5C-0x5F */
+ 0xF7, 0x64, 0xF7, 0x65, 0xF7, 0x66, 0xF7, 0x67, /* 0x60-0x63 */
+ 0xF7, 0x68, 0xF7, 0x69, 0xF7, 0x6A, 0xF7, 0x6B, /* 0x64-0x67 */
+ 0xF7, 0x6C, 0xF7, 0x6D, 0xF7, 0x6E, 0xF7, 0x6F, /* 0x68-0x6B */
+ 0xF7, 0x70, 0xF7, 0x71, 0xF7, 0x72, 0xF7, 0x73, /* 0x6C-0x6F */
+ 0xF7, 0x74, 0xF7, 0x75, 0xF7, 0x76, 0xF7, 0x77, /* 0x70-0x73 */
+ 0xF7, 0x78, 0xF7, 0x79, 0xF7, 0x7A, 0xF7, 0x7B, /* 0x74-0x77 */
+ 0xF7, 0x7C, 0xF7, 0x7D, 0xF7, 0x7E, 0xF7, 0x80, /* 0x78-0x7B */
+ 0xD3, 0xE3, 0xF7, 0x81, 0xF7, 0x82, 0xF6, 0xCF, /* 0x7C-0x7F */
+
+ 0xF7, 0x83, 0xC2, 0xB3, 0xF6, 0xD0, 0xF7, 0x84, /* 0x80-0x83 */
+ 0xF7, 0x85, 0xF6, 0xD1, 0xF6, 0xD2, 0xF6, 0xD3, /* 0x84-0x87 */
+ 0xF6, 0xD4, 0xF7, 0x86, 0xF7, 0x87, 0xF6, 0xD6, /* 0x88-0x8B */
+ 0xF7, 0x88, 0xB1, 0xAB, 0xF6, 0xD7, 0xF7, 0x89, /* 0x8C-0x8F */
+ 0xF6, 0xD8, 0xF6, 0xD9, 0xF6, 0xDA, 0xF7, 0x8A, /* 0x90-0x93 */
+ 0xF6, 0xDB, 0xF6, 0xDC, 0xF7, 0x8B, 0xF7, 0x8C, /* 0x94-0x97 */
+ 0xF7, 0x8D, 0xF7, 0x8E, 0xF6, 0xDD, 0xF6, 0xDE, /* 0x98-0x9B */
+ 0xCF, 0xCA, 0xF7, 0x8F, 0xF6, 0xDF, 0xF6, 0xE0, /* 0x9C-0x9F */
+ 0xF6, 0xE1, 0xF6, 0xE2, 0xF6, 0xE3, 0xF6, 0xE4, /* 0xA0-0xA3 */
+ 0xC0, 0xF0, 0xF6, 0xE5, 0xF6, 0xE6, 0xF6, 0xE7, /* 0xA4-0xA7 */
+ 0xF6, 0xE8, 0xF6, 0xE9, 0xF7, 0x90, 0xF6, 0xEA, /* 0xA8-0xAB */
+ 0xF7, 0x91, 0xF6, 0xEB, 0xF6, 0xEC, 0xF7, 0x92, /* 0xAC-0xAF */
+ 0xF6, 0xED, 0xF6, 0xEE, 0xF6, 0xEF, 0xF6, 0xF0, /* 0xB0-0xB3 */
+ 0xF6, 0xF1, 0xF6, 0xF2, 0xF6, 0xF3, 0xF6, 0xF4, /* 0xB4-0xB7 */
+ 0xBE, 0xA8, 0xF7, 0x93, 0xF6, 0xF5, 0xF6, 0xF6, /* 0xB8-0xBB */
+ 0xF6, 0xF7, 0xF6, 0xF8, 0xF7, 0x94, 0xF7, 0x95, /* 0xBC-0xBF */
+ 0xF7, 0x96, 0xF7, 0x97, 0xF7, 0x98, 0xC8, 0xFA, /* 0xC0-0xC3 */
+ 0xF6, 0xF9, 0xF6, 0xFA, 0xF6, 0xFB, 0xF6, 0xFC, /* 0xC4-0xC7 */
+ 0xF7, 0x99, 0xF7, 0x9A, 0xF6, 0xFD, 0xF6, 0xFE, /* 0xC8-0xCB */
+ 0xF7, 0xA1, 0xF7, 0xA2, 0xF7, 0xA3, 0xF7, 0xA4, /* 0xCC-0xCF */
+ 0xF7, 0xA5, 0xF7, 0x9B, 0xF7, 0x9C, 0xF7, 0xA6, /* 0xD0-0xD3 */
+ 0xF7, 0xA7, 0xF7, 0xA8, 0xB1, 0xEE, 0xF7, 0xA9, /* 0xD4-0xD7 */
+ 0xF7, 0xAA, 0xF7, 0xAB, 0xF7, 0x9D, 0xF7, 0x9E, /* 0xD8-0xDB */
+ 0xF7, 0xAC, 0xF7, 0xAD, 0xC1, 0xDB, 0xF7, 0xAE, /* 0xDC-0xDF */
+ 0xF7, 0x9F, 0xF7, 0xA0, 0xF7, 0xAF, 0xF8, 0x40, /* 0xE0-0xE3 */
+ 0xF8, 0x41, 0xF8, 0x42, 0xF8, 0x43, 0xF8, 0x44, /* 0xE4-0xE7 */
+ 0xF8, 0x45, 0xF8, 0x46, 0xF8, 0x47, 0xF8, 0x48, /* 0xE8-0xEB */
+ 0xF8, 0x49, 0xF8, 0x4A, 0xF8, 0x4B, 0xF8, 0x4C, /* 0xEC-0xEF */
+ 0xF8, 0x4D, 0xF8, 0x4E, 0xF8, 0x4F, 0xF8, 0x50, /* 0xF0-0xF3 */
+ 0xF8, 0x51, 0xF8, 0x52, 0xF8, 0x53, 0xF8, 0x54, /* 0xF4-0xF7 */
+ 0xF8, 0x55, 0xF8, 0x56, 0xF8, 0x57, 0xF8, 0x58, /* 0xF8-0xFB */
+ 0xF8, 0x59, 0xF8, 0x5A, 0xF8, 0x5B, 0xF8, 0x5C, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_9D[512] = {
+ 0xF8, 0x5D, 0xF8, 0x5E, 0xF8, 0x5F, 0xF8, 0x60, /* 0x00-0x03 */
+ 0xF8, 0x61, 0xF8, 0x62, 0xF8, 0x63, 0xF8, 0x64, /* 0x04-0x07 */
+ 0xF8, 0x65, 0xF8, 0x66, 0xF8, 0x67, 0xF8, 0x68, /* 0x08-0x0B */
+ 0xF8, 0x69, 0xF8, 0x6A, 0xF8, 0x6B, 0xF8, 0x6C, /* 0x0C-0x0F */
+ 0xF8, 0x6D, 0xF8, 0x6E, 0xF8, 0x6F, 0xF8, 0x70, /* 0x10-0x13 */
+ 0xF8, 0x71, 0xF8, 0x72, 0xF8, 0x73, 0xF8, 0x74, /* 0x14-0x17 */
+ 0xF8, 0x75, 0xF8, 0x76, 0xF8, 0x77, 0xF8, 0x78, /* 0x18-0x1B */
+ 0xF8, 0x79, 0xF8, 0x7A, 0xF8, 0x7B, 0xF8, 0x7C, /* 0x1C-0x1F */
+ 0xF8, 0x7D, 0xF8, 0x7E, 0xF8, 0x80, 0xF8, 0x81, /* 0x20-0x23 */
+ 0xF8, 0x82, 0xF8, 0x83, 0xF8, 0x84, 0xF8, 0x85, /* 0x24-0x27 */
+ 0xF8, 0x86, 0xF8, 0x87, 0xF8, 0x88, 0xF8, 0x89, /* 0x28-0x2B */
+ 0xF8, 0x8A, 0xF8, 0x8B, 0xF8, 0x8C, 0xF8, 0x8D, /* 0x2C-0x2F */
+ 0xF8, 0x8E, 0xF8, 0x8F, 0xF8, 0x90, 0xF8, 0x91, /* 0x30-0x33 */
+ 0xF8, 0x92, 0xF8, 0x93, 0xF8, 0x94, 0xF8, 0x95, /* 0x34-0x37 */
+ 0xF8, 0x96, 0xF8, 0x97, 0xF8, 0x98, 0xF8, 0x99, /* 0x38-0x3B */
+ 0xF8, 0x9A, 0xF8, 0x9B, 0xF8, 0x9C, 0xF8, 0x9D, /* 0x3C-0x3F */
+ 0xF8, 0x9E, 0xF8, 0x9F, 0xF8, 0xA0, 0xF9, 0x40, /* 0x40-0x43 */
+ 0xF9, 0x41, 0xF9, 0x42, 0xF9, 0x43, 0xF9, 0x44, /* 0x44-0x47 */
+ 0xF9, 0x45, 0xF9, 0x46, 0xF9, 0x47, 0xF9, 0x48, /* 0x48-0x4B */
+ 0xF9, 0x49, 0xF9, 0x4A, 0xF9, 0x4B, 0xF9, 0x4C, /* 0x4C-0x4F */
+ 0xF9, 0x4D, 0xF9, 0x4E, 0xF9, 0x4F, 0xF9, 0x50, /* 0x50-0x53 */
+ 0xF9, 0x51, 0xF9, 0x52, 0xF9, 0x53, 0xF9, 0x54, /* 0x54-0x57 */
+ 0xF9, 0x55, 0xF9, 0x56, 0xF9, 0x57, 0xF9, 0x58, /* 0x58-0x5B */
+ 0xF9, 0x59, 0xF9, 0x5A, 0xF9, 0x5B, 0xF9, 0x5C, /* 0x5C-0x5F */
+ 0xF9, 0x5D, 0xF9, 0x5E, 0xF9, 0x5F, 0xF9, 0x60, /* 0x60-0x63 */
+ 0xF9, 0x61, 0xF9, 0x62, 0xF9, 0x63, 0xF9, 0x64, /* 0x64-0x67 */
+ 0xF9, 0x65, 0xF9, 0x66, 0xF9, 0x67, 0xF9, 0x68, /* 0x68-0x6B */
+ 0xF9, 0x69, 0xF9, 0x6A, 0xF9, 0x6B, 0xF9, 0x6C, /* 0x6C-0x6F */
+ 0xF9, 0x6D, 0xF9, 0x6E, 0xF9, 0x6F, 0xF9, 0x70, /* 0x70-0x73 */
+ 0xF9, 0x71, 0xF9, 0x72, 0xF9, 0x73, 0xF9, 0x74, /* 0x74-0x77 */
+ 0xF9, 0x75, 0xF9, 0x76, 0xF9, 0x77, 0xF9, 0x78, /* 0x78-0x7B */
+ 0xF9, 0x79, 0xF9, 0x7A, 0xF9, 0x7B, 0xF9, 0x7C, /* 0x7C-0x7F */
+
+ 0xF9, 0x7D, 0xF9, 0x7E, 0xF9, 0x80, 0xF9, 0x81, /* 0x80-0x83 */
+ 0xF9, 0x82, 0xF9, 0x83, 0xF9, 0x84, 0xF9, 0x85, /* 0x84-0x87 */
+ 0xF9, 0x86, 0xF9, 0x87, 0xF9, 0x88, 0xF9, 0x89, /* 0x88-0x8B */
+ 0xF9, 0x8A, 0xF9, 0x8B, 0xF9, 0x8C, 0xF9, 0x8D, /* 0x8C-0x8F */
+ 0xF9, 0x8E, 0xF9, 0x8F, 0xF9, 0x90, 0xF9, 0x91, /* 0x90-0x93 */
+ 0xF9, 0x92, 0xF9, 0x93, 0xF9, 0x94, 0xF9, 0x95, /* 0x94-0x97 */
+ 0xF9, 0x96, 0xF9, 0x97, 0xF9, 0x98, 0xF9, 0x99, /* 0x98-0x9B */
+ 0xF9, 0x9A, 0xF9, 0x9B, 0xF9, 0x9C, 0xF9, 0x9D, /* 0x9C-0x9F */
+ 0xF9, 0x9E, 0xF9, 0x9F, 0xF9, 0xA0, 0xFA, 0x40, /* 0xA0-0xA3 */
+ 0xFA, 0x41, 0xFA, 0x42, 0xFA, 0x43, 0xFA, 0x44, /* 0xA4-0xA7 */
+ 0xFA, 0x45, 0xFA, 0x46, 0xFA, 0x47, 0xFA, 0x48, /* 0xA8-0xAB */
+ 0xFA, 0x49, 0xFA, 0x4A, 0xFA, 0x4B, 0xFA, 0x4C, /* 0xAC-0xAF */
+ 0xFA, 0x4D, 0xFA, 0x4E, 0xFA, 0x4F, 0xFA, 0x50, /* 0xB0-0xB3 */
+ 0xFA, 0x51, 0xFA, 0x52, 0xFA, 0x53, 0xFA, 0x54, /* 0xB4-0xB7 */
+ 0xFA, 0x55, 0xFA, 0x56, 0xFA, 0x57, 0xFA, 0x58, /* 0xB8-0xBB */
+ 0xFA, 0x59, 0xFA, 0x5A, 0xFA, 0x5B, 0xFA, 0x5C, /* 0xBC-0xBF */
+ 0xFA, 0x5D, 0xFA, 0x5E, 0xFA, 0x5F, 0xFA, 0x60, /* 0xC0-0xC3 */
+ 0xFA, 0x61, 0xFA, 0x62, 0xFA, 0x63, 0xFA, 0x64, /* 0xC4-0xC7 */
+ 0xFA, 0x65, 0xFA, 0x66, 0xFA, 0x67, 0xFA, 0x68, /* 0xC8-0xCB */
+ 0xFA, 0x69, 0xFA, 0x6A, 0xFA, 0x6B, 0xFA, 0x6C, /* 0xCC-0xCF */
+ 0xFA, 0x6D, 0xFA, 0x6E, 0xFA, 0x6F, 0xFA, 0x70, /* 0xD0-0xD3 */
+ 0xFA, 0x71, 0xFA, 0x72, 0xFA, 0x73, 0xFA, 0x74, /* 0xD4-0xD7 */
+ 0xFA, 0x75, 0xFA, 0x76, 0xFA, 0x77, 0xFA, 0x78, /* 0xD8-0xDB */
+ 0xFA, 0x79, 0xFA, 0x7A, 0xFA, 0x7B, 0xFA, 0x7C, /* 0xDC-0xDF */
+ 0xFA, 0x7D, 0xFA, 0x7E, 0xFA, 0x80, 0xFA, 0x81, /* 0xE0-0xE3 */
+ 0xFA, 0x82, 0xFA, 0x83, 0xFA, 0x84, 0xFA, 0x85, /* 0xE4-0xE7 */
+ 0xFA, 0x86, 0xFA, 0x87, 0xFA, 0x88, 0xFA, 0x89, /* 0xE8-0xEB */
+ 0xFA, 0x8A, 0xFA, 0x8B, 0xFA, 0x8C, 0xFA, 0x8D, /* 0xEC-0xEF */
+ 0xFA, 0x8E, 0xFA, 0x8F, 0xFA, 0x90, 0xFA, 0x91, /* 0xF0-0xF3 */
+ 0xFA, 0x92, 0xFA, 0x93, 0xFA, 0x94, 0xFA, 0x95, /* 0xF4-0xF7 */
+ 0xFA, 0x96, 0xFA, 0x97, 0xFA, 0x98, 0xFA, 0x99, /* 0xF8-0xFB */
+ 0xFA, 0x9A, 0xFA, 0x9B, 0xFA, 0x9C, 0xFA, 0x9D, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_9E[512] = {
+ 0xFA, 0x9E, 0xFA, 0x9F, 0xFA, 0xA0, 0xFB, 0x40, /* 0x00-0x03 */
+ 0xFB, 0x41, 0xFB, 0x42, 0xFB, 0x43, 0xFB, 0x44, /* 0x04-0x07 */
+ 0xFB, 0x45, 0xFB, 0x46, 0xFB, 0x47, 0xFB, 0x48, /* 0x08-0x0B */
+ 0xFB, 0x49, 0xFB, 0x4A, 0xFB, 0x4B, 0xFB, 0x4C, /* 0x0C-0x0F */
+ 0xFB, 0x4D, 0xFB, 0x4E, 0xFB, 0x4F, 0xFB, 0x50, /* 0x10-0x13 */
+ 0xFB, 0x51, 0xFB, 0x52, 0xFB, 0x53, 0xFB, 0x54, /* 0x14-0x17 */
+ 0xFB, 0x55, 0xFB, 0x56, 0xFB, 0x57, 0xFB, 0x58, /* 0x18-0x1B */
+ 0xFB, 0x59, 0xFB, 0x5A, 0xFB, 0x5B, 0xC4, 0xF1, /* 0x1C-0x1F */
+ 0xF0, 0xAF, 0xBC, 0xA6, 0xF0, 0xB0, 0xC3, 0xF9, /* 0x20-0x23 */
+ 0xFB, 0x5C, 0xC5, 0xB8, 0xD1, 0xBB, 0xFB, 0x5D, /* 0x24-0x27 */
+ 0xF0, 0xB1, 0xF0, 0xB2, 0xF0, 0xB3, 0xF0, 0xB4, /* 0x28-0x2B */
+ 0xF0, 0xB5, 0xD1, 0xBC, 0xFB, 0x5E, 0xD1, 0xEC, /* 0x2C-0x2F */
+ 0xFB, 0x5F, 0xF0, 0xB7, 0xF0, 0xB6, 0xD4, 0xA7, /* 0x30-0x33 */
+ 0xFB, 0x60, 0xCD, 0xD2, 0xF0, 0xB8, 0xF0, 0xBA, /* 0x34-0x37 */
+ 0xF0, 0xB9, 0xF0, 0xBB, 0xF0, 0xBC, 0xFB, 0x61, /* 0x38-0x3B */
+ 0xFB, 0x62, 0xB8, 0xEB, 0xF0, 0xBD, 0xBA, 0xE8, /* 0x3C-0x3F */
+ 0xFB, 0x63, 0xF0, 0xBE, 0xF0, 0xBF, 0xBE, 0xE9, /* 0x40-0x43 */
+ 0xF0, 0xC0, 0xB6, 0xEC, 0xF0, 0xC1, 0xF0, 0xC2, /* 0x44-0x47 */
+ 0xF0, 0xC3, 0xF0, 0xC4, 0xC8, 0xB5, 0xF0, 0xC5, /* 0x48-0x4B */
+ 0xF0, 0xC6, 0xFB, 0x64, 0xF0, 0xC7, 0xC5, 0xF4, /* 0x4C-0x4F */
+ 0xFB, 0x65, 0xF0, 0xC8, 0xFB, 0x66, 0xFB, 0x67, /* 0x50-0x53 */
+ 0xFB, 0x68, 0xF0, 0xC9, 0xFB, 0x69, 0xF0, 0xCA, /* 0x54-0x57 */
+ 0xF7, 0xBD, 0xFB, 0x6A, 0xF0, 0xCB, 0xF0, 0xCC, /* 0x58-0x5B */
+ 0xF0, 0xCD, 0xFB, 0x6B, 0xF0, 0xCE, 0xFB, 0x6C, /* 0x5C-0x5F */
+ 0xFB, 0x6D, 0xFB, 0x6E, 0xFB, 0x6F, 0xF0, 0xCF, /* 0x60-0x63 */
+ 0xBA, 0xD7, 0xFB, 0x70, 0xF0, 0xD0, 0xF0, 0xD1, /* 0x64-0x67 */
+ 0xF0, 0xD2, 0xF0, 0xD3, 0xF0, 0xD4, 0xF0, 0xD5, /* 0x68-0x6B */
+ 0xF0, 0xD6, 0xF0, 0xD8, 0xFB, 0x71, 0xFB, 0x72, /* 0x6C-0x6F */
+ 0xD3, 0xA5, 0xF0, 0xD7, 0xFB, 0x73, 0xF0, 0xD9, /* 0x70-0x73 */
+ 0xFB, 0x74, 0xFB, 0x75, 0xFB, 0x76, 0xFB, 0x77, /* 0x74-0x77 */
+ 0xFB, 0x78, 0xFB, 0x79, 0xFB, 0x7A, 0xFB, 0x7B, /* 0x78-0x7B */
+ 0xFB, 0x7C, 0xFB, 0x7D, 0xF5, 0xBA, 0xC2, 0xB9, /* 0x7C-0x7F */
+
+ 0xFB, 0x7E, 0xFB, 0x80, 0xF7, 0xE4, 0xFB, 0x81, /* 0x80-0x83 */
+ 0xFB, 0x82, 0xFB, 0x83, 0xFB, 0x84, 0xF7, 0xE5, /* 0x84-0x87 */
+ 0xF7, 0xE6, 0xFB, 0x85, 0xFB, 0x86, 0xF7, 0xE7, /* 0x88-0x8B */
+ 0xFB, 0x87, 0xFB, 0x88, 0xFB, 0x89, 0xFB, 0x8A, /* 0x8C-0x8F */
+ 0xFB, 0x8B, 0xFB, 0x8C, 0xF7, 0xE8, 0xC2, 0xB4, /* 0x90-0x93 */
+ 0xFB, 0x8D, 0xFB, 0x8E, 0xFB, 0x8F, 0xFB, 0x90, /* 0x94-0x97 */
+ 0xFB, 0x91, 0xFB, 0x92, 0xFB, 0x93, 0xFB, 0x94, /* 0x98-0x9B */
+ 0xFB, 0x95, 0xF7, 0xEA, 0xFB, 0x96, 0xF7, 0xEB, /* 0x9C-0x9F */
+ 0xFB, 0x97, 0xFB, 0x98, 0xFB, 0x99, 0xFB, 0x9A, /* 0xA0-0xA3 */
+ 0xFB, 0x9B, 0xFB, 0x9C, 0xC2, 0xF3, 0xFB, 0x9D, /* 0xA4-0xA7 */
+ 0xFB, 0x9E, 0xFB, 0x9F, 0xFB, 0xA0, 0xFC, 0x40, /* 0xA8-0xAB */
+ 0xFC, 0x41, 0xFC, 0x42, 0xFC, 0x43, 0xFC, 0x44, /* 0xAC-0xAF */
+ 0xFC, 0x45, 0xFC, 0x46, 0xFC, 0x47, 0xFC, 0x48, /* 0xB0-0xB3 */
+ 0xF4, 0xF0, 0xFC, 0x49, 0xFC, 0x4A, 0xFC, 0x4B, /* 0xB4-0xB7 */
+ 0xF4, 0xEF, 0xFC, 0x4C, 0xFC, 0x4D, 0xC2, 0xE9, /* 0xB8-0xBB */
+ 0xFC, 0x4E, 0xF7, 0xE1, 0xF7, 0xE2, 0xFC, 0x4F, /* 0xBC-0xBF */
+ 0xFC, 0x50, 0xFC, 0x51, 0xFC, 0x52, 0xFC, 0x53, /* 0xC0-0xC3 */
+ 0xBB, 0xC6, 0xFC, 0x54, 0xFC, 0x55, 0xFC, 0x56, /* 0xC4-0xC7 */
+ 0xFC, 0x57, 0xD9, 0xE4, 0xFC, 0x58, 0xFC, 0x59, /* 0xC8-0xCB */
+ 0xFC, 0x5A, 0xCA, 0xF2, 0xC0, 0xE8, 0xF0, 0xA4, /* 0xCC-0xCF */
+ 0xFC, 0x5B, 0xBA, 0xDA, 0xFC, 0x5C, 0xFC, 0x5D, /* 0xD0-0xD3 */
+ 0xC7, 0xAD, 0xFC, 0x5E, 0xFC, 0x5F, 0xFC, 0x60, /* 0xD4-0xD7 */
+ 0xC4, 0xAC, 0xFC, 0x61, 0xFC, 0x62, 0xF7, 0xEC, /* 0xD8-0xDB */
+ 0xF7, 0xED, 0xF7, 0xEE, 0xFC, 0x63, 0xF7, 0xF0, /* 0xDC-0xDF */
+ 0xF7, 0xEF, 0xFC, 0x64, 0xF7, 0xF1, 0xFC, 0x65, /* 0xE0-0xE3 */
+ 0xFC, 0x66, 0xF7, 0xF4, 0xFC, 0x67, 0xF7, 0xF3, /* 0xE4-0xE7 */
+ 0xFC, 0x68, 0xF7, 0xF2, 0xF7, 0xF5, 0xFC, 0x69, /* 0xE8-0xEB */
+ 0xFC, 0x6A, 0xFC, 0x6B, 0xFC, 0x6C, 0xF7, 0xF6, /* 0xEC-0xEF */
+ 0xFC, 0x6D, 0xFC, 0x6E, 0xFC, 0x6F, 0xFC, 0x70, /* 0xF0-0xF3 */
+ 0xFC, 0x71, 0xFC, 0x72, 0xFC, 0x73, 0xFC, 0x74, /* 0xF4-0xF7 */
+ 0xFC, 0x75, 0xED, 0xE9, 0xFC, 0x76, 0xED, 0xEA, /* 0xF8-0xFB */
+ 0xED, 0xEB, 0xFC, 0x77, 0xF6, 0xBC, 0xFC, 0x78, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_9F[512] = {
+ 0xFC, 0x79, 0xFC, 0x7A, 0xFC, 0x7B, 0xFC, 0x7C, /* 0x00-0x03 */
+ 0xFC, 0x7D, 0xFC, 0x7E, 0xFC, 0x80, 0xFC, 0x81, /* 0x04-0x07 */
+ 0xFC, 0x82, 0xFC, 0x83, 0xFC, 0x84, 0xF6, 0xBD, /* 0x08-0x0B */
+ 0xFC, 0x85, 0xF6, 0xBE, 0xB6, 0xA6, 0xFC, 0x86, /* 0x0C-0x0F */
+ 0xD8, 0xBE, 0xFC, 0x87, 0xFC, 0x88, 0xB9, 0xC4, /* 0x10-0x13 */
+ 0xFC, 0x89, 0xFC, 0x8A, 0xFC, 0x8B, 0xD8, 0xBB, /* 0x14-0x17 */
+ 0xFC, 0x8C, 0xDC, 0xB1, 0xFC, 0x8D, 0xFC, 0x8E, /* 0x18-0x1B */
+ 0xFC, 0x8F, 0xFC, 0x90, 0xFC, 0x91, 0xFC, 0x92, /* 0x1C-0x1F */
+ 0xCA, 0xF3, 0xFC, 0x93, 0xF7, 0xF7, 0xFC, 0x94, /* 0x20-0x23 */
+ 0xFC, 0x95, 0xFC, 0x96, 0xFC, 0x97, 0xFC, 0x98, /* 0x24-0x27 */
+ 0xFC, 0x99, 0xFC, 0x9A, 0xFC, 0x9B, 0xFC, 0x9C, /* 0x28-0x2B */
+ 0xF7, 0xF8, 0xFC, 0x9D, 0xFC, 0x9E, 0xF7, 0xF9, /* 0x2C-0x2F */
+ 0xFC, 0x9F, 0xFC, 0xA0, 0xFD, 0x40, 0xFD, 0x41, /* 0x30-0x33 */
+ 0xFD, 0x42, 0xFD, 0x43, 0xFD, 0x44, 0xF7, 0xFB, /* 0x34-0x37 */
+ 0xFD, 0x45, 0xF7, 0xFA, 0xFD, 0x46, 0xB1, 0xC7, /* 0x38-0x3B */
+ 0xFD, 0x47, 0xF7, 0xFC, 0xF7, 0xFD, 0xFD, 0x48, /* 0x3C-0x3F */
+ 0xFD, 0x49, 0xFD, 0x4A, 0xFD, 0x4B, 0xFD, 0x4C, /* 0x40-0x43 */
+ 0xF7, 0xFE, 0xFD, 0x4D, 0xFD, 0x4E, 0xFD, 0x4F, /* 0x44-0x47 */
+ 0xFD, 0x50, 0xFD, 0x51, 0xFD, 0x52, 0xFD, 0x53, /* 0x48-0x4B */
+ 0xFD, 0x54, 0xFD, 0x55, 0xFD, 0x56, 0xFD, 0x57, /* 0x4C-0x4F */
+ 0xC6, 0xEB, 0xEC, 0xB4, 0xFD, 0x58, 0xFD, 0x59, /* 0x50-0x53 */
+ 0xFD, 0x5A, 0xFD, 0x5B, 0xFD, 0x5C, 0xFD, 0x5D, /* 0x54-0x57 */
+ 0xFD, 0x5E, 0xFD, 0x5F, 0xFD, 0x60, 0xFD, 0x61, /* 0x58-0x5B */
+ 0xFD, 0x62, 0xFD, 0x63, 0xFD, 0x64, 0xFD, 0x65, /* 0x5C-0x5F */
+ 0xFD, 0x66, 0xFD, 0x67, 0xFD, 0x68, 0xFD, 0x69, /* 0x60-0x63 */
+ 0xFD, 0x6A, 0xFD, 0x6B, 0xFD, 0x6C, 0xFD, 0x6D, /* 0x64-0x67 */
+ 0xFD, 0x6E, 0xFD, 0x6F, 0xFD, 0x70, 0xFD, 0x71, /* 0x68-0x6B */
+ 0xFD, 0x72, 0xFD, 0x73, 0xFD, 0x74, 0xFD, 0x75, /* 0x6C-0x6F */
+ 0xFD, 0x76, 0xFD, 0x77, 0xFD, 0x78, 0xFD, 0x79, /* 0x70-0x73 */
+ 0xFD, 0x7A, 0xFD, 0x7B, 0xFD, 0x7C, 0xFD, 0x7D, /* 0x74-0x77 */
+ 0xFD, 0x7E, 0xFD, 0x80, 0xFD, 0x81, 0xFD, 0x82, /* 0x78-0x7B */
+ 0xFD, 0x83, 0xFD, 0x84, 0xFD, 0x85, 0xB3, 0xDD, /* 0x7C-0x7F */
+
+ 0xF6, 0xB3, 0xFD, 0x86, 0xFD, 0x87, 0xF6, 0xB4, /* 0x80-0x83 */
+ 0xC1, 0xE4, 0xF6, 0xB5, 0xF6, 0xB6, 0xF6, 0xB7, /* 0x84-0x87 */
+ 0xF6, 0xB8, 0xF6, 0xB9, 0xF6, 0xBA, 0xC8, 0xA3, /* 0x88-0x8B */
+ 0xF6, 0xBB, 0xFD, 0x88, 0xFD, 0x89, 0xFD, 0x8A, /* 0x8C-0x8F */
+ 0xFD, 0x8B, 0xFD, 0x8C, 0xFD, 0x8D, 0xFD, 0x8E, /* 0x90-0x93 */
+ 0xFD, 0x8F, 0xFD, 0x90, 0xFD, 0x91, 0xFD, 0x92, /* 0x94-0x97 */
+ 0xFD, 0x93, 0xC1, 0xFA, 0xB9, 0xA8, 0xED, 0xE8, /* 0x98-0x9B */
+ 0xFD, 0x94, 0xFD, 0x95, 0xFD, 0x96, 0xB9, 0xEA, /* 0x9C-0x9F */
+ 0xD9, 0xDF, 0xFD, 0x97, 0xFD, 0x98, 0xFD, 0x99, /* 0xA0-0xA3 */
+ 0xFD, 0x9A, 0xFD, 0x9B, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+};
+
+static unsigned char u2c_DC[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+};
+
+static unsigned char u2c_F9[512] = {
+ 0xD8, 0x4D, 0xB8, 0xFC, 0xDC, 0x87, 0xD9, 0x5A, /* 0x00-0x03 */
+ 0xBB, 0xAC, 0xB4, 0xAE, 0xBE, 0xE4, 0xFD, 0x94, /* 0x04-0x07 */
+ 0xFD, 0x94, 0xC6, 0xF5, 0xBD, 0xF0, 0xC0, 0xAE, /* 0x08-0x0B */
+ 0xC4, 0xCE, 0x91, 0xD0, 0xB0, 0x5D, 0xC1, 0x5F, /* 0x0C-0x0F */
+ 0xCC, 0x7D, 0xC2, 0xDD, 0xC2, 0xE3, 0xDF, 0x89, /* 0x10-0x13 */
+ 0x98, 0xB7, 0xC2, 0xE5, 0xC0, 0xD3, 0xE7, 0xF3, /* 0x14-0x17 */
+ 0xC2, 0xE4, 0xC0, 0xD2, 0xF1, 0x98, 0x81, 0x79, /* 0x18-0x1B */
+ 0xC2, 0xD1, 0x99, 0xDA, 0xA0, 0x80, 0xCC, 0x6D, /* 0x1C-0x1F */
+ 0xFB, 0x5B, 0x8D, 0xB9, 0x9E, 0x45, 0xCB, 0x7B, /* 0x20-0x23 */
+ 0xD2, 0x68, 0xC0, 0xAD, 0xC5, 0x44, 0xCF, 0x9E, /* 0x24-0x27 */
+ 0xC0, 0xC8, 0xC0, 0xCA, 0xC0, 0xCB, 0xC0, 0xC7, /* 0x28-0x2B */
+ 0xFD, 0x9C, 0x81, 0xED, 0xC0, 0xE4, 0x84, 0xDA, /* 0x2C-0x2F */
+ 0x93, 0xEF, 0x99, 0xA9, 0xA0, 0x74, 0xB1, 0x52, /* 0x30-0x33 */
+ 0xC0, 0xCF, 0xCC, 0x4A, 0xCC, 0x94, 0xC2, 0xB7, /* 0x34-0x37 */
+ 0xC2, 0xB6, 0xF4, 0x94, 0xFA, 0x98, 0xC2, 0xB5, /* 0x38-0x3B */
+ 0xB5, 0x93, 0xBE, 0x47, 0xC7, 0x8A, 0xE4, 0x9B, /* 0x3C-0x3F */
+ 0xC2, 0xB9, 0xD5, 0x93, 0x89, 0xC5, 0xC5, 0xAA, /* 0x40-0x43 */
+ 0xBB, 0x5C, 0xC3, 0x40, 0xC0, 0xCE, 0xC0, 0xDA, /* 0x44-0x47 */
+ 0xD9, 0x54, 0xC0, 0xD7, 0x89, 0xBE, 0x8C, 0xD2, /* 0x48-0x4B */
+ 0x98, 0xC7, 0x9C, 0x49, 0xC2, 0xA9, 0xC0, 0xDB, /* 0x4C-0x4F */
+ 0xBF, 0x7C, 0xC2, 0xAA, 0xC0, 0xD5, 0xC0, 0xDF, /* 0x50-0x53 */
+ 0x84, 0x43, 0xC1, 0xE8, 0xB6, 0xA0, 0xBE, 0x63, /* 0x54-0x57 */
+ 0xC1, 0xE2, 0xC1, 0xEA, 0xD7, 0x78, 0x92, 0x82, /* 0x58-0x5B */
+ 0x98, 0xB7, 0xD6, 0x5A, 0xB5, 0xA4, 0x8C, 0x8E, /* 0x5C-0x5F */
+ 0xC5, 0xAD, 0xC2, 0xCA, 0xAE, 0x90, 0xB1, 0xB1, /* 0x60-0x63 */
+ 0xB4, 0x91, 0xB1, 0xE3, 0x8F, 0xCD, 0xB2, 0xBB, /* 0x64-0x67 */
+ 0xC3, 0xDA, 0x94, 0xB5, 0xCB, 0xF7, 0x85, 0xA2, /* 0x68-0x6B */
+ 0xC8, 0xFB, 0xCA, 0xA1, 0xC8, 0x7E, 0xD5, 0x66, /* 0x6C-0x6F */
+ 0x9A, 0xA2, 0xB3, 0xBD, 0xC9, 0xF2, 0xCA, 0xB0, /* 0x70-0x73 */
+ 0xC8, 0xF4, 0xC2, 0xD3, 0xC2, 0xD4, 0xC1, 0xC1, /* 0x74-0x77 */
+ 0x83, 0xC9, 0xFD, 0x9D, 0xC1, 0xBA, 0xBC, 0x5A, /* 0x78-0x7B */
+ 0xC1, 0xBC, 0xD5, 0x8F, 0xC1, 0xBF, 0x84, 0xEE, /* 0x7C-0x7F */
+
+ 0x85, 0xCE, 0xC5, 0xAE, 0x8F, 0x5D, 0xC2, 0xC3, /* 0x80-0x83 */
+ 0x9E, 0x56, 0xB5, 0x5A, 0xE9, 0x82, 0xF3, 0x50, /* 0x84-0x87 */
+ 0xFB, 0x90, 0xC0, 0xE8, 0xC1, 0xA6, 0x95, 0xD1, /* 0x88-0x8B */
+ 0x9A, 0x76, 0xDE, 0x5D, 0xC4, 0xEA, 0x91, 0x7A, /* 0x8C-0x8F */
+ 0x91, 0xD9, 0x93, 0xD3, 0x9D, 0x69, 0x9F, 0x92, /* 0x90-0x93 */
+ 0xAD, 0x49, 0xFD, 0x9E, 0xBE, 0x9A, 0xC2, 0x93, /* 0x94-0x97 */
+ 0xDD, 0x82, 0xC9, 0x8F, 0xDF, 0x42, 0xE5, 0x80, /* 0x98-0x9B */
+ 0xC1, 0xD0, 0xC1, 0xD3, 0xD1, 0xCA, 0xC1, 0xD2, /* 0x9C-0x9F */
+ 0xC1, 0xD1, 0xD5, 0x66, 0xC1, 0xAE, 0xC4, 0xEE, /* 0xA0-0xA3 */
+ 0xC4, 0xED, 0x9A, 0x9A, 0xBA, 0x9F, 0xAB, 0x43, /* 0xA4-0xA7 */
+ 0xC1, 0xEE, 0xE0, 0xF2, 0x8C, 0x8E, 0x8E, 0x58, /* 0xA8-0xAB */
+ 0xC1, 0xAF, 0xC1, 0xE1, 0xAC, 0x93, 0xC1, 0xE7, /* 0xAC-0xAF */
+ 0xF1, 0xF6, 0xE2, 0x8F, 0xC1, 0xE3, 0xEC, 0x60, /* 0xB0-0xB3 */
+ 0xEE, 0x49, 0xC0, 0xFD, 0xB6, 0x59, 0xF5, 0xB7, /* 0xB4-0xB7 */
+ 0xEB, 0x60, 0x90, 0xBA, 0xC1, 0xCB, 0xC1, 0xC5, /* 0xB8-0xBB */
+ 0xE5, 0xBC, 0xC4, 0xF2, 0xC1, 0xCF, 0x98, 0xB7, /* 0xBC-0xBF */
+ 0xC1, 0xC7, 0xAF, 0x9F, 0xDE, 0xA4, 0xDF, 0x7C, /* 0xC0-0xC3 */
+ 0xFD, 0x88, 0x95, 0x9E, 0xC8, 0xEE, 0x84, 0xA2, /* 0xC4-0xC7 */
+ 0x96, 0x83, 0xC1, 0xF8, 0xC1, 0xF7, 0xC1, 0xEF, /* 0xC8-0xCB */
+ 0xC1, 0xF0, 0xC1, 0xF4, 0xC1, 0xF2, 0xBC, 0x7E, /* 0xCC-0xCF */
+ 0xEE, 0x90, 0xC1, 0xF9, 0xC2, 0xBE, 0xEA, 0x91, /* 0xD0-0xD3 */
+ 0x82, 0x90, 0x8D, 0x91, 0x9C, 0x53, 0xDD, 0x86, /* 0xD4-0xD7 */
+ 0xC2, 0xC9, 0x90, 0xFC, 0xC0, 0xF5, 0xC2, 0xCA, /* 0xD8-0xDB */
+ 0xC2, 0xA1, 0xC0, 0xFB, 0xC0, 0xF4, 0xC2, 0xC4, /* 0xDC-0xDF */
+ 0xD2, 0xD7, 0xC0, 0xEE, 0xC0, 0xE6, 0xC4, 0xE0, /* 0xE0-0xE3 */
+ 0xC0, 0xED, 0xC1, 0xA1, 0xEE, 0xBE, 0xFD, 0x9F, /* 0xE4-0xE7 */
+ 0xD1, 0x65, 0xC0, 0xEF, 0xEB, 0x78, 0xC4, 0xE4, /* 0xE8-0xEB */
+ 0xC4, 0xE7, 0xC1, 0xDF, 0x9F, 0xFB, 0xAD, 0x55, /* 0xEC-0xEF */
+ 0xCC, 0x41, 0xFD, 0xA0, 0xF7, 0x5B, 0xF7, 0xEB, /* 0xF0-0xF3 */
+ 0xC1, 0xD6, 0xC1, 0xDC, 0xC5, 0x52, 0xC1, 0xA2, /* 0xF4-0xF7 */
+ 0xF3, 0xD2, 0xC1, 0xA3, 0xA0, 0xEE, 0xD6, 0xCB, /* 0xF8-0xFB */
+ 0xD7, 0x52, 0xCA, 0xB2, 0xB2, 0xE8, 0xB4, 0xCC, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_FA[512] = {
+ 0xC7, 0xD0, 0xB6, 0xC8, 0xCD, 0xD8, 0xCC, 0xC7, /* 0x00-0x03 */
+ 0xD5, 0xAC, 0xB6, 0xB4, 0xB1, 0xA9, 0xDD, 0x97, /* 0x04-0x07 */
+ 0xD0, 0xD0, 0xBD, 0xB5, 0xD2, 0x8A, 0xC0, 0xAA, /* 0x08-0x0B */
+ 0xFE, 0x40, 0xFE, 0x41, 0xFE, 0x42, 0xFE, 0x43, /* 0x0C-0x0F */
+ 0x89, 0x56, 0xFE, 0x44, 0xC7, 0xE7, 0xFE, 0x45, /* 0x10-0x13 */
+ 0xFE, 0x46, 0x84, 0x44, 0xD8, 0x69, 0xD2, 0xE6, /* 0x14-0x17 */
+ 0xFE, 0x47, 0xC9, 0xF1, 0xCF, 0xE9, 0xB8, 0xA3, /* 0x18-0x1B */
+ 0xBE, 0xB8, 0xBE, 0xAB, 0xD3, 0xF0, 0xFE, 0x48, /* 0x1C-0x1F */
+ 0xFE, 0x49, 0xFE, 0x4A, 0xD6, 0x54, 0xFE, 0x4B, /* 0x20-0x23 */
+ 0xFE, 0x4C, 0xD2, 0xDD, 0xB6, 0xBC, 0xFE, 0x4D, /* 0x24-0x27 */
+ 0xFE, 0x4E, 0xFE, 0x4F, 0xEF, 0x88, 0xEF, 0x95, /* 0x28-0x2B */
+ 0xF0, 0x5E, 0xFA, 0x51, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+};
+
+static unsigned char u2c_FE[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xA9, 0x55, 0xA6, 0xF2, 0x00, 0x00, 0xA6, 0xF4, /* 0x30-0x33 */
+ 0xA6, 0xF5, 0xA6, 0xE0, 0xA6, 0xE1, 0xA6, 0xF0, /* 0x34-0x37 */
+ 0xA6, 0xF1, 0xA6, 0xE2, 0xA6, 0xE3, 0xA6, 0xEE, /* 0x38-0x3B */
+ 0xA6, 0xEF, 0xA6, 0xE6, 0xA6, 0xE7, 0xA6, 0xE4, /* 0x3C-0x3F */
+ 0xA6, 0xE5, 0xA6, 0xE8, 0xA6, 0xE9, 0xA6, 0xEA, /* 0x40-0x43 */
+ 0xA6, 0xEB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0xA9, 0x68, 0xA9, 0x69, 0xA9, 0x6A, /* 0x48-0x4B */
+ 0xA9, 0x6B, 0xA9, 0x6C, 0xA9, 0x6D, 0xA9, 0x6E, /* 0x4C-0x4F */
+ 0xA9, 0x6F, 0xA9, 0x70, 0xA9, 0x71, 0x00, 0x00, /* 0x50-0x53 */
+ 0xA9, 0x72, 0xA9, 0x73, 0xA9, 0x74, 0xA9, 0x75, /* 0x54-0x57 */
+ 0x00, 0x00, 0xA9, 0x76, 0xA9, 0x77, 0xA9, 0x78, /* 0x58-0x5B */
+ 0xA9, 0x79, 0xA9, 0x7A, 0xA9, 0x7B, 0xA9, 0x7C, /* 0x5C-0x5F */
+ 0xA9, 0x7D, 0xA9, 0x7E, 0xA9, 0x80, 0xA9, 0x81, /* 0x60-0x63 */
+ 0xA9, 0x82, 0xA9, 0x83, 0xA9, 0x84, 0x00, 0x00, /* 0x64-0x67 */
+ 0xA9, 0x85, 0xA9, 0x86, 0xA9, 0x87, 0xA9, 0x88, /* 0x68-0x6B */
+};
+
+static unsigned char u2c_FF[512] = {
+ 0x00, 0x00, 0xA3, 0xA1, 0xA3, 0xA2, 0xA3, 0xA3, /* 0x00-0x03 */
+ 0xA1, 0xE7, 0xA3, 0xA5, 0xA3, 0xA6, 0xA3, 0xA7, /* 0x04-0x07 */
+ 0xA3, 0xA8, 0xA3, 0xA9, 0xA3, 0xAA, 0xA3, 0xAB, /* 0x08-0x0B */
+ 0xA3, 0xAC, 0xA3, 0xAD, 0xA3, 0xAE, 0xA3, 0xAF, /* 0x0C-0x0F */
+ 0xA3, 0xB0, 0xA3, 0xB1, 0xA3, 0xB2, 0xA3, 0xB3, /* 0x10-0x13 */
+ 0xA3, 0xB4, 0xA3, 0xB5, 0xA3, 0xB6, 0xA3, 0xB7, /* 0x14-0x17 */
+ 0xA3, 0xB8, 0xA3, 0xB9, 0xA3, 0xBA, 0xA3, 0xBB, /* 0x18-0x1B */
+ 0xA3, 0xBC, 0xA3, 0xBD, 0xA3, 0xBE, 0xA3, 0xBF, /* 0x1C-0x1F */
+ 0xA3, 0xC0, 0xA3, 0xC1, 0xA3, 0xC2, 0xA3, 0xC3, /* 0x20-0x23 */
+ 0xA3, 0xC4, 0xA3, 0xC5, 0xA3, 0xC6, 0xA3, 0xC7, /* 0x24-0x27 */
+ 0xA3, 0xC8, 0xA3, 0xC9, 0xA3, 0xCA, 0xA3, 0xCB, /* 0x28-0x2B */
+ 0xA3, 0xCC, 0xA3, 0xCD, 0xA3, 0xCE, 0xA3, 0xCF, /* 0x2C-0x2F */
+ 0xA3, 0xD0, 0xA3, 0xD1, 0xA3, 0xD2, 0xA3, 0xD3, /* 0x30-0x33 */
+ 0xA3, 0xD4, 0xA3, 0xD5, 0xA3, 0xD6, 0xA3, 0xD7, /* 0x34-0x37 */
+ 0xA3, 0xD8, 0xA3, 0xD9, 0xA3, 0xDA, 0xA3, 0xDB, /* 0x38-0x3B */
+ 0xA3, 0xDC, 0xA3, 0xDD, 0xA3, 0xDE, 0xA3, 0xDF, /* 0x3C-0x3F */
+ 0xA3, 0xE0, 0xA3, 0xE1, 0xA3, 0xE2, 0xA3, 0xE3, /* 0x40-0x43 */
+ 0xA3, 0xE4, 0xA3, 0xE5, 0xA3, 0xE6, 0xA3, 0xE7, /* 0x44-0x47 */
+ 0xA3, 0xE8, 0xA3, 0xE9, 0xA3, 0xEA, 0xA3, 0xEB, /* 0x48-0x4B */
+ 0xA3, 0xEC, 0xA3, 0xED, 0xA3, 0xEE, 0xA3, 0xEF, /* 0x4C-0x4F */
+ 0xA3, 0xF0, 0xA3, 0xF1, 0xA3, 0xF2, 0xA3, 0xF3, /* 0x50-0x53 */
+ 0xA3, 0xF4, 0xA3, 0xF5, 0xA3, 0xF6, 0xA3, 0xF7, /* 0x54-0x57 */
+ 0xA3, 0xF8, 0xA3, 0xF9, 0xA3, 0xFA, 0xA3, 0xFB, /* 0x58-0x5B */
+ 0xA3, 0xFC, 0xA3, 0xFD, 0xA1, 0xAB, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0xA1, 0xE9, 0xA1, 0xEA, 0xA9, 0x56, 0xA3, 0xFE, /* 0xE0-0xE3 */
+ 0xA9, 0x57, 0xA3, 0xA4, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+};
+
+static unsigned char *page_uni2charset[256] = {
+ NULL, u2c_01, u2c_02, u2c_03, u2c_04, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ u2c_20, u2c_21, u2c_22, u2c_23, u2c_24, u2c_25, u2c_26, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ u2c_30, u2c_31, u2c_32, u2c_33, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, u2c_4E, u2c_4F,
+ u2c_50, u2c_51, u2c_52, u2c_53, u2c_54, u2c_55, u2c_56, u2c_57,
+ u2c_58, u2c_59, u2c_5A, u2c_5B, u2c_5C, u2c_5D, u2c_5E, u2c_5F,
+ u2c_60, u2c_61, u2c_62, u2c_63, u2c_64, u2c_65, u2c_66, u2c_67,
+ u2c_68, u2c_69, u2c_6A, u2c_6B, u2c_6C, u2c_6D, u2c_6E, u2c_6F,
+ u2c_70, u2c_71, u2c_72, u2c_73, u2c_74, u2c_75, u2c_76, u2c_77,
+ u2c_78, u2c_79, u2c_7A, u2c_7B, u2c_7C, u2c_7D, u2c_7E, u2c_7F,
+ u2c_80, u2c_81, u2c_82, u2c_83, u2c_84, u2c_85, u2c_86, u2c_87,
+ u2c_88, u2c_89, u2c_8A, u2c_8B, u2c_8C, u2c_8D, u2c_8E, u2c_8F,
+ u2c_90, u2c_91, u2c_92, u2c_93, u2c_94, u2c_95, u2c_96, u2c_97,
+ u2c_98, u2c_99, u2c_9A, u2c_9B, u2c_9C, u2c_9D, u2c_9E, u2c_9F,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, u2c_DC, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, u2c_F9, u2c_FA, NULL, NULL, NULL, u2c_FE, u2c_FF, };
+
+static unsigned char charset2lower[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40-0x47 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x48-0x4f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50-0x57 */
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0x88-0x8f */
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static unsigned char charset2upper[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0x88-0x8f */
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static int uni2char(const wchar_t uni,
+ unsigned char *out, int boundlen)
+{
+ unsigned char *uni2charset;
+ unsigned char cl = uni&0xFF;
+ unsigned char ch = (uni>>8)&0xFF;
+ int n;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+
+ uni2charset = page_uni2charset[ch];
+ if (uni2charset) {
+ if (boundlen <= 1)
+ return -ENAMETOOLONG;
+ out[0] = uni2charset[cl*2];
+ out[1] = uni2charset[cl*2+1];
+ if (out[0] == 0x00 && out[1] == 0x00)
+ return -EINVAL;
+ n = 2;
+ } else if (ch==0 && cl) {
+ out[0] = cl;
+ n = 1;
+ }
+ else
+ return -EINVAL;
+
+ return n;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen,
+ wchar_t *uni)
+{
+ unsigned char ch, cl;
+ wchar_t *charset2uni;
+ int n;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ if (boundlen == 1) {
+ *uni = rawstring[0];
+ return 1;
+ }
+
+ ch = rawstring[0];
+ cl = rawstring[1];
+
+ charset2uni = page_charset2uni[ch];
+ if (charset2uni && cl) {
+ *uni = charset2uni[cl];
+ if (*uni == 0x0000)
+ return -EINVAL;
+ n = 2;
+ } else{
+ *uni = ch;
+ n = 1;
+ }
+ return n;
+}
+
+static struct nls_table table = {
+ "cp936",
+ uni2char,
+ char2uni,
+ charset2lower,
+ charset2upper,
+ THIS_MODULE,
+};
+
+static int __init init_nls_cp936(void)
+{
+ return register_nls(&table);
+}
+
+static void __exit exit_nls_cp936(void)
+{
+ unregister_nls(&table);
+}
+
+module_init(init_nls_cp936)
+module_exit(exit_nls_cp936)
+MODULE_LICENSE("Dual BSD/GPL");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ *
+---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/fs/nls/nls_cp949.c b/fs/nls/nls_cp949.c
new file mode 100644
index 00000000000000..5e06469f8f4390
--- /dev/null
+++ b/fs/nls/nls_cp949.c
@@ -0,0 +1,13962 @@
+/*
+ * linux/fs/nls_cp949.c
+ *
+ * Charset cp949 translation tables.
+ * This translation table was generated automatically, the
+ * original table can be download from the Microsoft website.
+ * (http://www.microsoft.com/typography/unicode/unicodecp.htm)
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static wchar_t c2u_81[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xAC02,0xAC03,0xAC05,0xAC06,0xAC0B,0xAC0C,0xAC0D,/* 0x40-0x47 */
+ 0xAC0E,0xAC0F,0xAC18,0xAC1E,0xAC1F,0xAC21,0xAC22,0xAC23,/* 0x48-0x4F */
+ 0xAC25,0xAC26,0xAC27,0xAC28,0xAC29,0xAC2A,0xAC2B,0xAC2E,/* 0x50-0x57 */
+ 0xAC32,0xAC33,0xAC34,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xAC35,0xAC36,0xAC37,0xAC3A,0xAC3B,0xAC3D,0xAC3E,/* 0x60-0x67 */
+ 0xAC3F,0xAC41,0xAC42,0xAC43,0xAC44,0xAC45,0xAC46,0xAC47,/* 0x68-0x6F */
+ 0xAC48,0xAC49,0xAC4A,0xAC4C,0xAC4E,0xAC4F,0xAC50,0xAC51,/* 0x70-0x77 */
+ 0xAC52,0xAC53,0xAC55,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xAC56,0xAC57,0xAC59,0xAC5A,0xAC5B,0xAC5D,0xAC5E,/* 0x80-0x87 */
+ 0xAC5F,0xAC60,0xAC61,0xAC62,0xAC63,0xAC64,0xAC65,0xAC66,/* 0x88-0x8F */
+ 0xAC67,0xAC68,0xAC69,0xAC6A,0xAC6B,0xAC6C,0xAC6D,0xAC6E,/* 0x90-0x97 */
+ 0xAC6F,0xAC72,0xAC73,0xAC75,0xAC76,0xAC79,0xAC7B,0xAC7C,/* 0x98-0x9F */
+ 0xAC7D,0xAC7E,0xAC7F,0xAC82,0xAC87,0xAC88,0xAC8D,0xAC8E,/* 0xA0-0xA7 */
+ 0xAC8F,0xAC91,0xAC92,0xAC93,0xAC95,0xAC96,0xAC97,0xAC98,/* 0xA8-0xAF */
+ 0xAC99,0xAC9A,0xAC9B,0xAC9E,0xACA2,0xACA3,0xACA4,0xACA5,/* 0xB0-0xB7 */
+ 0xACA6,0xACA7,0xACAB,0xACAD,0xACAE,0xACB1,0xACB2,0xACB3,/* 0xB8-0xBF */
+ 0xACB4,0xACB5,0xACB6,0xACB7,0xACBA,0xACBE,0xACBF,0xACC0,/* 0xC0-0xC7 */
+ 0xACC2,0xACC3,0xACC5,0xACC6,0xACC7,0xACC9,0xACCA,0xACCB,/* 0xC8-0xCF */
+ 0xACCD,0xACCE,0xACCF,0xACD0,0xACD1,0xACD2,0xACD3,0xACD4,/* 0xD0-0xD7 */
+ 0xACD6,0xACD8,0xACD9,0xACDA,0xACDB,0xACDC,0xACDD,0xACDE,/* 0xD8-0xDF */
+ 0xACDF,0xACE2,0xACE3,0xACE5,0xACE6,0xACE9,0xACEB,0xACED,/* 0xE0-0xE7 */
+ 0xACEE,0xACF2,0xACF4,0xACF7,0xACF8,0xACF9,0xACFA,0xACFB,/* 0xE8-0xEF */
+ 0xACFE,0xACFF,0xAD01,0xAD02,0xAD03,0xAD05,0xAD07,0xAD08,/* 0xF0-0xF7 */
+ 0xAD09,0xAD0A,0xAD0B,0xAD0E,0xAD10,0xAD12,0xAD13,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_82[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xAD14,0xAD15,0xAD16,0xAD17,0xAD19,0xAD1A,0xAD1B,/* 0x40-0x47 */
+ 0xAD1D,0xAD1E,0xAD1F,0xAD21,0xAD22,0xAD23,0xAD24,0xAD25,/* 0x48-0x4F */
+ 0xAD26,0xAD27,0xAD28,0xAD2A,0xAD2B,0xAD2E,0xAD2F,0xAD30,/* 0x50-0x57 */
+ 0xAD31,0xAD32,0xAD33,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xAD36,0xAD37,0xAD39,0xAD3A,0xAD3B,0xAD3D,0xAD3E,/* 0x60-0x67 */
+ 0xAD3F,0xAD40,0xAD41,0xAD42,0xAD43,0xAD46,0xAD48,0xAD4A,/* 0x68-0x6F */
+ 0xAD4B,0xAD4C,0xAD4D,0xAD4E,0xAD4F,0xAD51,0xAD52,0xAD53,/* 0x70-0x77 */
+ 0xAD55,0xAD56,0xAD57,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xAD59,0xAD5A,0xAD5B,0xAD5C,0xAD5D,0xAD5E,0xAD5F,/* 0x80-0x87 */
+ 0xAD60,0xAD62,0xAD64,0xAD65,0xAD66,0xAD67,0xAD68,0xAD69,/* 0x88-0x8F */
+ 0xAD6A,0xAD6B,0xAD6E,0xAD6F,0xAD71,0xAD72,0xAD77,0xAD78,/* 0x90-0x97 */
+ 0xAD79,0xAD7A,0xAD7E,0xAD80,0xAD83,0xAD84,0xAD85,0xAD86,/* 0x98-0x9F */
+ 0xAD87,0xAD8A,0xAD8B,0xAD8D,0xAD8E,0xAD8F,0xAD91,0xAD92,/* 0xA0-0xA7 */
+ 0xAD93,0xAD94,0xAD95,0xAD96,0xAD97,0xAD98,0xAD99,0xAD9A,/* 0xA8-0xAF */
+ 0xAD9B,0xAD9E,0xAD9F,0xADA0,0xADA1,0xADA2,0xADA3,0xADA5,/* 0xB0-0xB7 */
+ 0xADA6,0xADA7,0xADA8,0xADA9,0xADAA,0xADAB,0xADAC,0xADAD,/* 0xB8-0xBF */
+ 0xADAE,0xADAF,0xADB0,0xADB1,0xADB2,0xADB3,0xADB4,0xADB5,/* 0xC0-0xC7 */
+ 0xADB6,0xADB8,0xADB9,0xADBA,0xADBB,0xADBC,0xADBD,0xADBE,/* 0xC8-0xCF */
+ 0xADBF,0xADC2,0xADC3,0xADC5,0xADC6,0xADC7,0xADC9,0xADCA,/* 0xD0-0xD7 */
+ 0xADCB,0xADCC,0xADCD,0xADCE,0xADCF,0xADD2,0xADD4,0xADD5,/* 0xD8-0xDF */
+ 0xADD6,0xADD7,0xADD8,0xADD9,0xADDA,0xADDB,0xADDD,0xADDE,/* 0xE0-0xE7 */
+ 0xADDF,0xADE1,0xADE2,0xADE3,0xADE5,0xADE6,0xADE7,0xADE8,/* 0xE8-0xEF */
+ 0xADE9,0xADEA,0xADEB,0xADEC,0xADED,0xADEE,0xADEF,0xADF0,/* 0xF0-0xF7 */
+ 0xADF1,0xADF2,0xADF3,0xADF4,0xADF5,0xADF6,0xADF7,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_83[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xADFA,0xADFB,0xADFD,0xADFE,0xAE02,0xAE03,0xAE04,/* 0x40-0x47 */
+ 0xAE05,0xAE06,0xAE07,0xAE0A,0xAE0C,0xAE0E,0xAE0F,0xAE10,/* 0x48-0x4F */
+ 0xAE11,0xAE12,0xAE13,0xAE15,0xAE16,0xAE17,0xAE18,0xAE19,/* 0x50-0x57 */
+ 0xAE1A,0xAE1B,0xAE1C,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xAE1D,0xAE1E,0xAE1F,0xAE20,0xAE21,0xAE22,0xAE23,/* 0x60-0x67 */
+ 0xAE24,0xAE25,0xAE26,0xAE27,0xAE28,0xAE29,0xAE2A,0xAE2B,/* 0x68-0x6F */
+ 0xAE2C,0xAE2D,0xAE2E,0xAE2F,0xAE32,0xAE33,0xAE35,0xAE36,/* 0x70-0x77 */
+ 0xAE39,0xAE3B,0xAE3C,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xAE3D,0xAE3E,0xAE3F,0xAE42,0xAE44,0xAE47,0xAE48,/* 0x80-0x87 */
+ 0xAE49,0xAE4B,0xAE4F,0xAE51,0xAE52,0xAE53,0xAE55,0xAE57,/* 0x88-0x8F */
+ 0xAE58,0xAE59,0xAE5A,0xAE5B,0xAE5E,0xAE62,0xAE63,0xAE64,/* 0x90-0x97 */
+ 0xAE66,0xAE67,0xAE6A,0xAE6B,0xAE6D,0xAE6E,0xAE6F,0xAE71,/* 0x98-0x9F */
+ 0xAE72,0xAE73,0xAE74,0xAE75,0xAE76,0xAE77,0xAE7A,0xAE7E,/* 0xA0-0xA7 */
+ 0xAE7F,0xAE80,0xAE81,0xAE82,0xAE83,0xAE86,0xAE87,0xAE88,/* 0xA8-0xAF */
+ 0xAE89,0xAE8A,0xAE8B,0xAE8D,0xAE8E,0xAE8F,0xAE90,0xAE91,/* 0xB0-0xB7 */
+ 0xAE92,0xAE93,0xAE94,0xAE95,0xAE96,0xAE97,0xAE98,0xAE99,/* 0xB8-0xBF */
+ 0xAE9A,0xAE9B,0xAE9C,0xAE9D,0xAE9E,0xAE9F,0xAEA0,0xAEA1,/* 0xC0-0xC7 */
+ 0xAEA2,0xAEA3,0xAEA4,0xAEA5,0xAEA6,0xAEA7,0xAEA8,0xAEA9,/* 0xC8-0xCF */
+ 0xAEAA,0xAEAB,0xAEAC,0xAEAD,0xAEAE,0xAEAF,0xAEB0,0xAEB1,/* 0xD0-0xD7 */
+ 0xAEB2,0xAEB3,0xAEB4,0xAEB5,0xAEB6,0xAEB7,0xAEB8,0xAEB9,/* 0xD8-0xDF */
+ 0xAEBA,0xAEBB,0xAEBF,0xAEC1,0xAEC2,0xAEC3,0xAEC5,0xAEC6,/* 0xE0-0xE7 */
+ 0xAEC7,0xAEC8,0xAEC9,0xAECA,0xAECB,0xAECE,0xAED2,0xAED3,/* 0xE8-0xEF */
+ 0xAED4,0xAED5,0xAED6,0xAED7,0xAEDA,0xAEDB,0xAEDD,0xAEDE,/* 0xF0-0xF7 */
+ 0xAEDF,0xAEE0,0xAEE1,0xAEE2,0xAEE3,0xAEE4,0xAEE5,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_84[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xAEE6,0xAEE7,0xAEE9,0xAEEA,0xAEEC,0xAEEE,0xAEEF,/* 0x40-0x47 */
+ 0xAEF0,0xAEF1,0xAEF2,0xAEF3,0xAEF5,0xAEF6,0xAEF7,0xAEF9,/* 0x48-0x4F */
+ 0xAEFA,0xAEFB,0xAEFD,0xAEFE,0xAEFF,0xAF00,0xAF01,0xAF02,/* 0x50-0x57 */
+ 0xAF03,0xAF04,0xAF05,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xAF06,0xAF09,0xAF0A,0xAF0B,0xAF0C,0xAF0E,0xAF0F,/* 0x60-0x67 */
+ 0xAF11,0xAF12,0xAF13,0xAF14,0xAF15,0xAF16,0xAF17,0xAF18,/* 0x68-0x6F */
+ 0xAF19,0xAF1A,0xAF1B,0xAF1C,0xAF1D,0xAF1E,0xAF1F,0xAF20,/* 0x70-0x77 */
+ 0xAF21,0xAF22,0xAF23,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xAF24,0xAF25,0xAF26,0xAF27,0xAF28,0xAF29,0xAF2A,/* 0x80-0x87 */
+ 0xAF2B,0xAF2E,0xAF2F,0xAF31,0xAF33,0xAF35,0xAF36,0xAF37,/* 0x88-0x8F */
+ 0xAF38,0xAF39,0xAF3A,0xAF3B,0xAF3E,0xAF40,0xAF44,0xAF45,/* 0x90-0x97 */
+ 0xAF46,0xAF47,0xAF4A,0xAF4B,0xAF4C,0xAF4D,0xAF4E,0xAF4F,/* 0x98-0x9F */
+ 0xAF51,0xAF52,0xAF53,0xAF54,0xAF55,0xAF56,0xAF57,0xAF58,/* 0xA0-0xA7 */
+ 0xAF59,0xAF5A,0xAF5B,0xAF5E,0xAF5F,0xAF60,0xAF61,0xAF62,/* 0xA8-0xAF */
+ 0xAF63,0xAF66,0xAF67,0xAF68,0xAF69,0xAF6A,0xAF6B,0xAF6C,/* 0xB0-0xB7 */
+ 0xAF6D,0xAF6E,0xAF6F,0xAF70,0xAF71,0xAF72,0xAF73,0xAF74,/* 0xB8-0xBF */
+ 0xAF75,0xAF76,0xAF77,0xAF78,0xAF7A,0xAF7B,0xAF7C,0xAF7D,/* 0xC0-0xC7 */
+ 0xAF7E,0xAF7F,0xAF81,0xAF82,0xAF83,0xAF85,0xAF86,0xAF87,/* 0xC8-0xCF */
+ 0xAF89,0xAF8A,0xAF8B,0xAF8C,0xAF8D,0xAF8E,0xAF8F,0xAF92,/* 0xD0-0xD7 */
+ 0xAF93,0xAF94,0xAF96,0xAF97,0xAF98,0xAF99,0xAF9A,0xAF9B,/* 0xD8-0xDF */
+ 0xAF9D,0xAF9E,0xAF9F,0xAFA0,0xAFA1,0xAFA2,0xAFA3,0xAFA4,/* 0xE0-0xE7 */
+ 0xAFA5,0xAFA6,0xAFA7,0xAFA8,0xAFA9,0xAFAA,0xAFAB,0xAFAC,/* 0xE8-0xEF */
+ 0xAFAD,0xAFAE,0xAFAF,0xAFB0,0xAFB1,0xAFB2,0xAFB3,0xAFB4,/* 0xF0-0xF7 */
+ 0xAFB5,0xAFB6,0xAFB7,0xAFBA,0xAFBB,0xAFBD,0xAFBE,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_85[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xAFBF,0xAFC1,0xAFC2,0xAFC3,0xAFC4,0xAFC5,0xAFC6,/* 0x40-0x47 */
+ 0xAFCA,0xAFCC,0xAFCF,0xAFD0,0xAFD1,0xAFD2,0xAFD3,0xAFD5,/* 0x48-0x4F */
+ 0xAFD6,0xAFD7,0xAFD8,0xAFD9,0xAFDA,0xAFDB,0xAFDD,0xAFDE,/* 0x50-0x57 */
+ 0xAFDF,0xAFE0,0xAFE1,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xAFE2,0xAFE3,0xAFE4,0xAFE5,0xAFE6,0xAFE7,0xAFEA,/* 0x60-0x67 */
+ 0xAFEB,0xAFEC,0xAFED,0xAFEE,0xAFEF,0xAFF2,0xAFF3,0xAFF5,/* 0x68-0x6F */
+ 0xAFF6,0xAFF7,0xAFF9,0xAFFA,0xAFFB,0xAFFC,0xAFFD,0xAFFE,/* 0x70-0x77 */
+ 0xAFFF,0xB002,0xB003,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xB005,0xB006,0xB007,0xB008,0xB009,0xB00A,0xB00B,/* 0x80-0x87 */
+ 0xB00D,0xB00E,0xB00F,0xB011,0xB012,0xB013,0xB015,0xB016,/* 0x88-0x8F */
+ 0xB017,0xB018,0xB019,0xB01A,0xB01B,0xB01E,0xB01F,0xB020,/* 0x90-0x97 */
+ 0xB021,0xB022,0xB023,0xB024,0xB025,0xB026,0xB027,0xB029,/* 0x98-0x9F */
+ 0xB02A,0xB02B,0xB02C,0xB02D,0xB02E,0xB02F,0xB030,0xB031,/* 0xA0-0xA7 */
+ 0xB032,0xB033,0xB034,0xB035,0xB036,0xB037,0xB038,0xB039,/* 0xA8-0xAF */
+ 0xB03A,0xB03B,0xB03C,0xB03D,0xB03E,0xB03F,0xB040,0xB041,/* 0xB0-0xB7 */
+ 0xB042,0xB043,0xB046,0xB047,0xB049,0xB04B,0xB04D,0xB04F,/* 0xB8-0xBF */
+ 0xB050,0xB051,0xB052,0xB056,0xB058,0xB05A,0xB05B,0xB05C,/* 0xC0-0xC7 */
+ 0xB05E,0xB05F,0xB060,0xB061,0xB062,0xB063,0xB064,0xB065,/* 0xC8-0xCF */
+ 0xB066,0xB067,0xB068,0xB069,0xB06A,0xB06B,0xB06C,0xB06D,/* 0xD0-0xD7 */
+ 0xB06E,0xB06F,0xB070,0xB071,0xB072,0xB073,0xB074,0xB075,/* 0xD8-0xDF */
+ 0xB076,0xB077,0xB078,0xB079,0xB07A,0xB07B,0xB07E,0xB07F,/* 0xE0-0xE7 */
+ 0xB081,0xB082,0xB083,0xB085,0xB086,0xB087,0xB088,0xB089,/* 0xE8-0xEF */
+ 0xB08A,0xB08B,0xB08E,0xB090,0xB092,0xB093,0xB094,0xB095,/* 0xF0-0xF7 */
+ 0xB096,0xB097,0xB09B,0xB09D,0xB09E,0xB0A3,0xB0A4,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_86[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xB0A5,0xB0A6,0xB0A7,0xB0AA,0xB0B0,0xB0B2,0xB0B6,/* 0x40-0x47 */
+ 0xB0B7,0xB0B9,0xB0BA,0xB0BB,0xB0BD,0xB0BE,0xB0BF,0xB0C0,/* 0x48-0x4F */
+ 0xB0C1,0xB0C2,0xB0C3,0xB0C6,0xB0CA,0xB0CB,0xB0CC,0xB0CD,/* 0x50-0x57 */
+ 0xB0CE,0xB0CF,0xB0D2,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xB0D3,0xB0D5,0xB0D6,0xB0D7,0xB0D9,0xB0DA,0xB0DB,/* 0x60-0x67 */
+ 0xB0DC,0xB0DD,0xB0DE,0xB0DF,0xB0E1,0xB0E2,0xB0E3,0xB0E4,/* 0x68-0x6F */
+ 0xB0E6,0xB0E7,0xB0E8,0xB0E9,0xB0EA,0xB0EB,0xB0EC,0xB0ED,/* 0x70-0x77 */
+ 0xB0EE,0xB0EF,0xB0F0,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xB0F1,0xB0F2,0xB0F3,0xB0F4,0xB0F5,0xB0F6,0xB0F7,/* 0x80-0x87 */
+ 0xB0F8,0xB0F9,0xB0FA,0xB0FB,0xB0FC,0xB0FD,0xB0FE,0xB0FF,/* 0x88-0x8F */
+ 0xB100,0xB101,0xB102,0xB103,0xB104,0xB105,0xB106,0xB107,/* 0x90-0x97 */
+ 0xB10A,0xB10D,0xB10E,0xB10F,0xB111,0xB114,0xB115,0xB116,/* 0x98-0x9F */
+ 0xB117,0xB11A,0xB11E,0xB11F,0xB120,0xB121,0xB122,0xB126,/* 0xA0-0xA7 */
+ 0xB127,0xB129,0xB12A,0xB12B,0xB12D,0xB12E,0xB12F,0xB130,/* 0xA8-0xAF */
+ 0xB131,0xB132,0xB133,0xB136,0xB13A,0xB13B,0xB13C,0xB13D,/* 0xB0-0xB7 */
+ 0xB13E,0xB13F,0xB142,0xB143,0xB145,0xB146,0xB147,0xB149,/* 0xB8-0xBF */
+ 0xB14A,0xB14B,0xB14C,0xB14D,0xB14E,0xB14F,0xB152,0xB153,/* 0xC0-0xC7 */
+ 0xB156,0xB157,0xB159,0xB15A,0xB15B,0xB15D,0xB15E,0xB15F,/* 0xC8-0xCF */
+ 0xB161,0xB162,0xB163,0xB164,0xB165,0xB166,0xB167,0xB168,/* 0xD0-0xD7 */
+ 0xB169,0xB16A,0xB16B,0xB16C,0xB16D,0xB16E,0xB16F,0xB170,/* 0xD8-0xDF */
+ 0xB171,0xB172,0xB173,0xB174,0xB175,0xB176,0xB177,0xB17A,/* 0xE0-0xE7 */
+ 0xB17B,0xB17D,0xB17E,0xB17F,0xB181,0xB183,0xB184,0xB185,/* 0xE8-0xEF */
+ 0xB186,0xB187,0xB18A,0xB18C,0xB18E,0xB18F,0xB190,0xB191,/* 0xF0-0xF7 */
+ 0xB195,0xB196,0xB197,0xB199,0xB19A,0xB19B,0xB19D,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_87[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xB19E,0xB19F,0xB1A0,0xB1A1,0xB1A2,0xB1A3,0xB1A4,/* 0x40-0x47 */
+ 0xB1A5,0xB1A6,0xB1A7,0xB1A9,0xB1AA,0xB1AB,0xB1AC,0xB1AD,/* 0x48-0x4F */
+ 0xB1AE,0xB1AF,0xB1B0,0xB1B1,0xB1B2,0xB1B3,0xB1B4,0xB1B5,/* 0x50-0x57 */
+ 0xB1B6,0xB1B7,0xB1B8,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xB1B9,0xB1BA,0xB1BB,0xB1BC,0xB1BD,0xB1BE,0xB1BF,/* 0x60-0x67 */
+ 0xB1C0,0xB1C1,0xB1C2,0xB1C3,0xB1C4,0xB1C5,0xB1C6,0xB1C7,/* 0x68-0x6F */
+ 0xB1C8,0xB1C9,0xB1CA,0xB1CB,0xB1CD,0xB1CE,0xB1CF,0xB1D1,/* 0x70-0x77 */
+ 0xB1D2,0xB1D3,0xB1D5,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xB1D6,0xB1D7,0xB1D8,0xB1D9,0xB1DA,0xB1DB,0xB1DE,/* 0x80-0x87 */
+ 0xB1E0,0xB1E1,0xB1E2,0xB1E3,0xB1E4,0xB1E5,0xB1E6,0xB1E7,/* 0x88-0x8F */
+ 0xB1EA,0xB1EB,0xB1ED,0xB1EE,0xB1EF,0xB1F1,0xB1F2,0xB1F3,/* 0x90-0x97 */
+ 0xB1F4,0xB1F5,0xB1F6,0xB1F7,0xB1F8,0xB1FA,0xB1FC,0xB1FE,/* 0x98-0x9F */
+ 0xB1FF,0xB200,0xB201,0xB202,0xB203,0xB206,0xB207,0xB209,/* 0xA0-0xA7 */
+ 0xB20A,0xB20D,0xB20E,0xB20F,0xB210,0xB211,0xB212,0xB213,/* 0xA8-0xAF */
+ 0xB216,0xB218,0xB21A,0xB21B,0xB21C,0xB21D,0xB21E,0xB21F,/* 0xB0-0xB7 */
+ 0xB221,0xB222,0xB223,0xB224,0xB225,0xB226,0xB227,0xB228,/* 0xB8-0xBF */
+ 0xB229,0xB22A,0xB22B,0xB22C,0xB22D,0xB22E,0xB22F,0xB230,/* 0xC0-0xC7 */
+ 0xB231,0xB232,0xB233,0xB235,0xB236,0xB237,0xB238,0xB239,/* 0xC8-0xCF */
+ 0xB23A,0xB23B,0xB23D,0xB23E,0xB23F,0xB240,0xB241,0xB242,/* 0xD0-0xD7 */
+ 0xB243,0xB244,0xB245,0xB246,0xB247,0xB248,0xB249,0xB24A,/* 0xD8-0xDF */
+ 0xB24B,0xB24C,0xB24D,0xB24E,0xB24F,0xB250,0xB251,0xB252,/* 0xE0-0xE7 */
+ 0xB253,0xB254,0xB255,0xB256,0xB257,0xB259,0xB25A,0xB25B,/* 0xE8-0xEF */
+ 0xB25D,0xB25E,0xB25F,0xB261,0xB262,0xB263,0xB264,0xB265,/* 0xF0-0xF7 */
+ 0xB266,0xB267,0xB26A,0xB26B,0xB26C,0xB26D,0xB26E,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_88[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xB26F,0xB270,0xB271,0xB272,0xB273,0xB276,0xB277,/* 0x40-0x47 */
+ 0xB278,0xB279,0xB27A,0xB27B,0xB27D,0xB27E,0xB27F,0xB280,/* 0x48-0x4F */
+ 0xB281,0xB282,0xB283,0xB286,0xB287,0xB288,0xB28A,0xB28B,/* 0x50-0x57 */
+ 0xB28C,0xB28D,0xB28E,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xB28F,0xB292,0xB293,0xB295,0xB296,0xB297,0xB29B,/* 0x60-0x67 */
+ 0xB29C,0xB29D,0xB29E,0xB29F,0xB2A2,0xB2A4,0xB2A7,0xB2A8,/* 0x68-0x6F */
+ 0xB2A9,0xB2AB,0xB2AD,0xB2AE,0xB2AF,0xB2B1,0xB2B2,0xB2B3,/* 0x70-0x77 */
+ 0xB2B5,0xB2B6,0xB2B7,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xB2B8,0xB2B9,0xB2BA,0xB2BB,0xB2BC,0xB2BD,0xB2BE,/* 0x80-0x87 */
+ 0xB2BF,0xB2C0,0xB2C1,0xB2C2,0xB2C3,0xB2C4,0xB2C5,0xB2C6,/* 0x88-0x8F */
+ 0xB2C7,0xB2CA,0xB2CB,0xB2CD,0xB2CE,0xB2CF,0xB2D1,0xB2D3,/* 0x90-0x97 */
+ 0xB2D4,0xB2D5,0xB2D6,0xB2D7,0xB2DA,0xB2DC,0xB2DE,0xB2DF,/* 0x98-0x9F */
+ 0xB2E0,0xB2E1,0xB2E3,0xB2E7,0xB2E9,0xB2EA,0xB2F0,0xB2F1,/* 0xA0-0xA7 */
+ 0xB2F2,0xB2F6,0xB2FC,0xB2FD,0xB2FE,0xB302,0xB303,0xB305,/* 0xA8-0xAF */
+ 0xB306,0xB307,0xB309,0xB30A,0xB30B,0xB30C,0xB30D,0xB30E,/* 0xB0-0xB7 */
+ 0xB30F,0xB312,0xB316,0xB317,0xB318,0xB319,0xB31A,0xB31B,/* 0xB8-0xBF */
+ 0xB31D,0xB31E,0xB31F,0xB320,0xB321,0xB322,0xB323,0xB324,/* 0xC0-0xC7 */
+ 0xB325,0xB326,0xB327,0xB328,0xB329,0xB32A,0xB32B,0xB32C,/* 0xC8-0xCF */
+ 0xB32D,0xB32E,0xB32F,0xB330,0xB331,0xB332,0xB333,0xB334,/* 0xD0-0xD7 */
+ 0xB335,0xB336,0xB337,0xB338,0xB339,0xB33A,0xB33B,0xB33C,/* 0xD8-0xDF */
+ 0xB33D,0xB33E,0xB33F,0xB340,0xB341,0xB342,0xB343,0xB344,/* 0xE0-0xE7 */
+ 0xB345,0xB346,0xB347,0xB348,0xB349,0xB34A,0xB34B,0xB34C,/* 0xE8-0xEF */
+ 0xB34D,0xB34E,0xB34F,0xB350,0xB351,0xB352,0xB353,0xB357,/* 0xF0-0xF7 */
+ 0xB359,0xB35A,0xB35D,0xB360,0xB361,0xB362,0xB363,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_89[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xB366,0xB368,0xB36A,0xB36C,0xB36D,0xB36F,0xB372,/* 0x40-0x47 */
+ 0xB373,0xB375,0xB376,0xB377,0xB379,0xB37A,0xB37B,0xB37C,/* 0x48-0x4F */
+ 0xB37D,0xB37E,0xB37F,0xB382,0xB386,0xB387,0xB388,0xB389,/* 0x50-0x57 */
+ 0xB38A,0xB38B,0xB38D,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xB38E,0xB38F,0xB391,0xB392,0xB393,0xB395,0xB396,/* 0x60-0x67 */
+ 0xB397,0xB398,0xB399,0xB39A,0xB39B,0xB39C,0xB39D,0xB39E,/* 0x68-0x6F */
+ 0xB39F,0xB3A2,0xB3A3,0xB3A4,0xB3A5,0xB3A6,0xB3A7,0xB3A9,/* 0x70-0x77 */
+ 0xB3AA,0xB3AB,0xB3AD,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xB3AE,0xB3AF,0xB3B0,0xB3B1,0xB3B2,0xB3B3,0xB3B4,/* 0x80-0x87 */
+ 0xB3B5,0xB3B6,0xB3B7,0xB3B8,0xB3B9,0xB3BA,0xB3BB,0xB3BC,/* 0x88-0x8F */
+ 0xB3BD,0xB3BE,0xB3BF,0xB3C0,0xB3C1,0xB3C2,0xB3C3,0xB3C6,/* 0x90-0x97 */
+ 0xB3C7,0xB3C9,0xB3CA,0xB3CD,0xB3CF,0xB3D1,0xB3D2,0xB3D3,/* 0x98-0x9F */
+ 0xB3D6,0xB3D8,0xB3DA,0xB3DC,0xB3DE,0xB3DF,0xB3E1,0xB3E2,/* 0xA0-0xA7 */
+ 0xB3E3,0xB3E5,0xB3E6,0xB3E7,0xB3E9,0xB3EA,0xB3EB,0xB3EC,/* 0xA8-0xAF */
+ 0xB3ED,0xB3EE,0xB3EF,0xB3F0,0xB3F1,0xB3F2,0xB3F3,0xB3F4,/* 0xB0-0xB7 */
+ 0xB3F5,0xB3F6,0xB3F7,0xB3F8,0xB3F9,0xB3FA,0xB3FB,0xB3FD,/* 0xB8-0xBF */
+ 0xB3FE,0xB3FF,0xB400,0xB401,0xB402,0xB403,0xB404,0xB405,/* 0xC0-0xC7 */
+ 0xB406,0xB407,0xB408,0xB409,0xB40A,0xB40B,0xB40C,0xB40D,/* 0xC8-0xCF */
+ 0xB40E,0xB40F,0xB411,0xB412,0xB413,0xB414,0xB415,0xB416,/* 0xD0-0xD7 */
+ 0xB417,0xB419,0xB41A,0xB41B,0xB41D,0xB41E,0xB41F,0xB421,/* 0xD8-0xDF */
+ 0xB422,0xB423,0xB424,0xB425,0xB426,0xB427,0xB42A,0xB42C,/* 0xE0-0xE7 */
+ 0xB42D,0xB42E,0xB42F,0xB430,0xB431,0xB432,0xB433,0xB435,/* 0xE8-0xEF */
+ 0xB436,0xB437,0xB438,0xB439,0xB43A,0xB43B,0xB43C,0xB43D,/* 0xF0-0xF7 */
+ 0xB43E,0xB43F,0xB440,0xB441,0xB442,0xB443,0xB444,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_8A[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xB445,0xB446,0xB447,0xB448,0xB449,0xB44A,0xB44B,/* 0x40-0x47 */
+ 0xB44C,0xB44D,0xB44E,0xB44F,0xB452,0xB453,0xB455,0xB456,/* 0x48-0x4F */
+ 0xB457,0xB459,0xB45A,0xB45B,0xB45C,0xB45D,0xB45E,0xB45F,/* 0x50-0x57 */
+ 0xB462,0xB464,0xB466,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xB467,0xB468,0xB469,0xB46A,0xB46B,0xB46D,0xB46E,/* 0x60-0x67 */
+ 0xB46F,0xB470,0xB471,0xB472,0xB473,0xB474,0xB475,0xB476,/* 0x68-0x6F */
+ 0xB477,0xB478,0xB479,0xB47A,0xB47B,0xB47C,0xB47D,0xB47E,/* 0x70-0x77 */
+ 0xB47F,0xB481,0xB482,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xB483,0xB484,0xB485,0xB486,0xB487,0xB489,0xB48A,/* 0x80-0x87 */
+ 0xB48B,0xB48C,0xB48D,0xB48E,0xB48F,0xB490,0xB491,0xB492,/* 0x88-0x8F */
+ 0xB493,0xB494,0xB495,0xB496,0xB497,0xB498,0xB499,0xB49A,/* 0x90-0x97 */
+ 0xB49B,0xB49C,0xB49E,0xB49F,0xB4A0,0xB4A1,0xB4A2,0xB4A3,/* 0x98-0x9F */
+ 0xB4A5,0xB4A6,0xB4A7,0xB4A9,0xB4AA,0xB4AB,0xB4AD,0xB4AE,/* 0xA0-0xA7 */
+ 0xB4AF,0xB4B0,0xB4B1,0xB4B2,0xB4B3,0xB4B4,0xB4B6,0xB4B8,/* 0xA8-0xAF */
+ 0xB4BA,0xB4BB,0xB4BC,0xB4BD,0xB4BE,0xB4BF,0xB4C1,0xB4C2,/* 0xB0-0xB7 */
+ 0xB4C3,0xB4C5,0xB4C6,0xB4C7,0xB4C9,0xB4CA,0xB4CB,0xB4CC,/* 0xB8-0xBF */
+ 0xB4CD,0xB4CE,0xB4CF,0xB4D1,0xB4D2,0xB4D3,0xB4D4,0xB4D6,/* 0xC0-0xC7 */
+ 0xB4D7,0xB4D8,0xB4D9,0xB4DA,0xB4DB,0xB4DE,0xB4DF,0xB4E1,/* 0xC8-0xCF */
+ 0xB4E2,0xB4E5,0xB4E7,0xB4E8,0xB4E9,0xB4EA,0xB4EB,0xB4EE,/* 0xD0-0xD7 */
+ 0xB4F0,0xB4F2,0xB4F3,0xB4F4,0xB4F5,0xB4F6,0xB4F7,0xB4F9,/* 0xD8-0xDF */
+ 0xB4FA,0xB4FB,0xB4FC,0xB4FD,0xB4FE,0xB4FF,0xB500,0xB501,/* 0xE0-0xE7 */
+ 0xB502,0xB503,0xB504,0xB505,0xB506,0xB507,0xB508,0xB509,/* 0xE8-0xEF */
+ 0xB50A,0xB50B,0xB50C,0xB50D,0xB50E,0xB50F,0xB510,0xB511,/* 0xF0-0xF7 */
+ 0xB512,0xB513,0xB516,0xB517,0xB519,0xB51A,0xB51D,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_8B[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xB51E,0xB51F,0xB520,0xB521,0xB522,0xB523,0xB526,/* 0x40-0x47 */
+ 0xB52B,0xB52C,0xB52D,0xB52E,0xB52F,0xB532,0xB533,0xB535,/* 0x48-0x4F */
+ 0xB536,0xB537,0xB539,0xB53A,0xB53B,0xB53C,0xB53D,0xB53E,/* 0x50-0x57 */
+ 0xB53F,0xB542,0xB546,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xB547,0xB548,0xB549,0xB54A,0xB54E,0xB54F,0xB551,/* 0x60-0x67 */
+ 0xB552,0xB553,0xB555,0xB556,0xB557,0xB558,0xB559,0xB55A,/* 0x68-0x6F */
+ 0xB55B,0xB55E,0xB562,0xB563,0xB564,0xB565,0xB566,0xB567,/* 0x70-0x77 */
+ 0xB568,0xB569,0xB56A,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xB56B,0xB56C,0xB56D,0xB56E,0xB56F,0xB570,0xB571,/* 0x80-0x87 */
+ 0xB572,0xB573,0xB574,0xB575,0xB576,0xB577,0xB578,0xB579,/* 0x88-0x8F */
+ 0xB57A,0xB57B,0xB57C,0xB57D,0xB57E,0xB57F,0xB580,0xB581,/* 0x90-0x97 */
+ 0xB582,0xB583,0xB584,0xB585,0xB586,0xB587,0xB588,0xB589,/* 0x98-0x9F */
+ 0xB58A,0xB58B,0xB58C,0xB58D,0xB58E,0xB58F,0xB590,0xB591,/* 0xA0-0xA7 */
+ 0xB592,0xB593,0xB594,0xB595,0xB596,0xB597,0xB598,0xB599,/* 0xA8-0xAF */
+ 0xB59A,0xB59B,0xB59C,0xB59D,0xB59E,0xB59F,0xB5A2,0xB5A3,/* 0xB0-0xB7 */
+ 0xB5A5,0xB5A6,0xB5A7,0xB5A9,0xB5AC,0xB5AD,0xB5AE,0xB5AF,/* 0xB8-0xBF */
+ 0xB5B2,0xB5B6,0xB5B7,0xB5B8,0xB5B9,0xB5BA,0xB5BE,0xB5BF,/* 0xC0-0xC7 */
+ 0xB5C1,0xB5C2,0xB5C3,0xB5C5,0xB5C6,0xB5C7,0xB5C8,0xB5C9,/* 0xC8-0xCF */
+ 0xB5CA,0xB5CB,0xB5CE,0xB5D2,0xB5D3,0xB5D4,0xB5D5,0xB5D6,/* 0xD0-0xD7 */
+ 0xB5D7,0xB5D9,0xB5DA,0xB5DB,0xB5DC,0xB5DD,0xB5DE,0xB5DF,/* 0xD8-0xDF */
+ 0xB5E0,0xB5E1,0xB5E2,0xB5E3,0xB5E4,0xB5E5,0xB5E6,0xB5E7,/* 0xE0-0xE7 */
+ 0xB5E8,0xB5E9,0xB5EA,0xB5EB,0xB5ED,0xB5EE,0xB5EF,0xB5F0,/* 0xE8-0xEF */
+ 0xB5F1,0xB5F2,0xB5F3,0xB5F4,0xB5F5,0xB5F6,0xB5F7,0xB5F8,/* 0xF0-0xF7 */
+ 0xB5F9,0xB5FA,0xB5FB,0xB5FC,0xB5FD,0xB5FE,0xB5FF,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_8C[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xB600,0xB601,0xB602,0xB603,0xB604,0xB605,0xB606,/* 0x40-0x47 */
+ 0xB607,0xB608,0xB609,0xB60A,0xB60B,0xB60C,0xB60D,0xB60E,/* 0x48-0x4F */
+ 0xB60F,0xB612,0xB613,0xB615,0xB616,0xB617,0xB619,0xB61A,/* 0x50-0x57 */
+ 0xB61B,0xB61C,0xB61D,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xB61E,0xB61F,0xB620,0xB621,0xB622,0xB623,0xB624,/* 0x60-0x67 */
+ 0xB626,0xB627,0xB628,0xB629,0xB62A,0xB62B,0xB62D,0xB62E,/* 0x68-0x6F */
+ 0xB62F,0xB630,0xB631,0xB632,0xB633,0xB635,0xB636,0xB637,/* 0x70-0x77 */
+ 0xB638,0xB639,0xB63A,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xB63B,0xB63C,0xB63D,0xB63E,0xB63F,0xB640,0xB641,/* 0x80-0x87 */
+ 0xB642,0xB643,0xB644,0xB645,0xB646,0xB647,0xB649,0xB64A,/* 0x88-0x8F */
+ 0xB64B,0xB64C,0xB64D,0xB64E,0xB64F,0xB650,0xB651,0xB652,/* 0x90-0x97 */
+ 0xB653,0xB654,0xB655,0xB656,0xB657,0xB658,0xB659,0xB65A,/* 0x98-0x9F */
+ 0xB65B,0xB65C,0xB65D,0xB65E,0xB65F,0xB660,0xB661,0xB662,/* 0xA0-0xA7 */
+ 0xB663,0xB665,0xB666,0xB667,0xB669,0xB66A,0xB66B,0xB66C,/* 0xA8-0xAF */
+ 0xB66D,0xB66E,0xB66F,0xB670,0xB671,0xB672,0xB673,0xB674,/* 0xB0-0xB7 */
+ 0xB675,0xB676,0xB677,0xB678,0xB679,0xB67A,0xB67B,0xB67C,/* 0xB8-0xBF */
+ 0xB67D,0xB67E,0xB67F,0xB680,0xB681,0xB682,0xB683,0xB684,/* 0xC0-0xC7 */
+ 0xB685,0xB686,0xB687,0xB688,0xB689,0xB68A,0xB68B,0xB68C,/* 0xC8-0xCF */
+ 0xB68D,0xB68E,0xB68F,0xB690,0xB691,0xB692,0xB693,0xB694,/* 0xD0-0xD7 */
+ 0xB695,0xB696,0xB697,0xB698,0xB699,0xB69A,0xB69B,0xB69E,/* 0xD8-0xDF */
+ 0xB69F,0xB6A1,0xB6A2,0xB6A3,0xB6A5,0xB6A6,0xB6A7,0xB6A8,/* 0xE0-0xE7 */
+ 0xB6A9,0xB6AA,0xB6AD,0xB6AE,0xB6AF,0xB6B0,0xB6B2,0xB6B3,/* 0xE8-0xEF */
+ 0xB6B4,0xB6B5,0xB6B6,0xB6B7,0xB6B8,0xB6B9,0xB6BA,0xB6BB,/* 0xF0-0xF7 */
+ 0xB6BC,0xB6BD,0xB6BE,0xB6BF,0xB6C0,0xB6C1,0xB6C2,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_8D[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xB6C3,0xB6C4,0xB6C5,0xB6C6,0xB6C7,0xB6C8,0xB6C9,/* 0x40-0x47 */
+ 0xB6CA,0xB6CB,0xB6CC,0xB6CD,0xB6CE,0xB6CF,0xB6D0,0xB6D1,/* 0x48-0x4F */
+ 0xB6D2,0xB6D3,0xB6D5,0xB6D6,0xB6D7,0xB6D8,0xB6D9,0xB6DA,/* 0x50-0x57 */
+ 0xB6DB,0xB6DC,0xB6DD,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xB6DE,0xB6DF,0xB6E0,0xB6E1,0xB6E2,0xB6E3,0xB6E4,/* 0x60-0x67 */
+ 0xB6E5,0xB6E6,0xB6E7,0xB6E8,0xB6E9,0xB6EA,0xB6EB,0xB6EC,/* 0x68-0x6F */
+ 0xB6ED,0xB6EE,0xB6EF,0xB6F1,0xB6F2,0xB6F3,0xB6F5,0xB6F6,/* 0x70-0x77 */
+ 0xB6F7,0xB6F9,0xB6FA,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xB6FB,0xB6FC,0xB6FD,0xB6FE,0xB6FF,0xB702,0xB703,/* 0x80-0x87 */
+ 0xB704,0xB706,0xB707,0xB708,0xB709,0xB70A,0xB70B,0xB70C,/* 0x88-0x8F */
+ 0xB70D,0xB70E,0xB70F,0xB710,0xB711,0xB712,0xB713,0xB714,/* 0x90-0x97 */
+ 0xB715,0xB716,0xB717,0xB718,0xB719,0xB71A,0xB71B,0xB71C,/* 0x98-0x9F */
+ 0xB71D,0xB71E,0xB71F,0xB720,0xB721,0xB722,0xB723,0xB724,/* 0xA0-0xA7 */
+ 0xB725,0xB726,0xB727,0xB72A,0xB72B,0xB72D,0xB72E,0xB731,/* 0xA8-0xAF */
+ 0xB732,0xB733,0xB734,0xB735,0xB736,0xB737,0xB73A,0xB73C,/* 0xB0-0xB7 */
+ 0xB73D,0xB73E,0xB73F,0xB740,0xB741,0xB742,0xB743,0xB745,/* 0xB8-0xBF */
+ 0xB746,0xB747,0xB749,0xB74A,0xB74B,0xB74D,0xB74E,0xB74F,/* 0xC0-0xC7 */
+ 0xB750,0xB751,0xB752,0xB753,0xB756,0xB757,0xB758,0xB759,/* 0xC8-0xCF */
+ 0xB75A,0xB75B,0xB75C,0xB75D,0xB75E,0xB75F,0xB761,0xB762,/* 0xD0-0xD7 */
+ 0xB763,0xB765,0xB766,0xB767,0xB769,0xB76A,0xB76B,0xB76C,/* 0xD8-0xDF */
+ 0xB76D,0xB76E,0xB76F,0xB772,0xB774,0xB776,0xB777,0xB778,/* 0xE0-0xE7 */
+ 0xB779,0xB77A,0xB77B,0xB77E,0xB77F,0xB781,0xB782,0xB783,/* 0xE8-0xEF */
+ 0xB785,0xB786,0xB787,0xB788,0xB789,0xB78A,0xB78B,0xB78E,/* 0xF0-0xF7 */
+ 0xB793,0xB794,0xB795,0xB79A,0xB79B,0xB79D,0xB79E,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_8E[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xB79F,0xB7A1,0xB7A2,0xB7A3,0xB7A4,0xB7A5,0xB7A6,/* 0x40-0x47 */
+ 0xB7A7,0xB7AA,0xB7AE,0xB7AF,0xB7B0,0xB7B1,0xB7B2,0xB7B3,/* 0x48-0x4F */
+ 0xB7B6,0xB7B7,0xB7B9,0xB7BA,0xB7BB,0xB7BC,0xB7BD,0xB7BE,/* 0x50-0x57 */
+ 0xB7BF,0xB7C0,0xB7C1,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xB7C2,0xB7C3,0xB7C4,0xB7C5,0xB7C6,0xB7C8,0xB7CA,/* 0x60-0x67 */
+ 0xB7CB,0xB7CC,0xB7CD,0xB7CE,0xB7CF,0xB7D0,0xB7D1,0xB7D2,/* 0x68-0x6F */
+ 0xB7D3,0xB7D4,0xB7D5,0xB7D6,0xB7D7,0xB7D8,0xB7D9,0xB7DA,/* 0x70-0x77 */
+ 0xB7DB,0xB7DC,0xB7DD,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xB7DE,0xB7DF,0xB7E0,0xB7E1,0xB7E2,0xB7E3,0xB7E4,/* 0x80-0x87 */
+ 0xB7E5,0xB7E6,0xB7E7,0xB7E8,0xB7E9,0xB7EA,0xB7EB,0xB7EE,/* 0x88-0x8F */
+ 0xB7EF,0xB7F1,0xB7F2,0xB7F3,0xB7F5,0xB7F6,0xB7F7,0xB7F8,/* 0x90-0x97 */
+ 0xB7F9,0xB7FA,0xB7FB,0xB7FE,0xB802,0xB803,0xB804,0xB805,/* 0x98-0x9F */
+ 0xB806,0xB80A,0xB80B,0xB80D,0xB80E,0xB80F,0xB811,0xB812,/* 0xA0-0xA7 */
+ 0xB813,0xB814,0xB815,0xB816,0xB817,0xB81A,0xB81C,0xB81E,/* 0xA8-0xAF */
+ 0xB81F,0xB820,0xB821,0xB822,0xB823,0xB826,0xB827,0xB829,/* 0xB0-0xB7 */
+ 0xB82A,0xB82B,0xB82D,0xB82E,0xB82F,0xB830,0xB831,0xB832,/* 0xB8-0xBF */
+ 0xB833,0xB836,0xB83A,0xB83B,0xB83C,0xB83D,0xB83E,0xB83F,/* 0xC0-0xC7 */
+ 0xB841,0xB842,0xB843,0xB845,0xB846,0xB847,0xB848,0xB849,/* 0xC8-0xCF */
+ 0xB84A,0xB84B,0xB84C,0xB84D,0xB84E,0xB84F,0xB850,0xB852,/* 0xD0-0xD7 */
+ 0xB854,0xB855,0xB856,0xB857,0xB858,0xB859,0xB85A,0xB85B,/* 0xD8-0xDF */
+ 0xB85E,0xB85F,0xB861,0xB862,0xB863,0xB865,0xB866,0xB867,/* 0xE0-0xE7 */
+ 0xB868,0xB869,0xB86A,0xB86B,0xB86E,0xB870,0xB872,0xB873,/* 0xE8-0xEF */
+ 0xB874,0xB875,0xB876,0xB877,0xB879,0xB87A,0xB87B,0xB87D,/* 0xF0-0xF7 */
+ 0xB87E,0xB87F,0xB880,0xB881,0xB882,0xB883,0xB884,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_8F[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xB885,0xB886,0xB887,0xB888,0xB889,0xB88A,0xB88B,/* 0x40-0x47 */
+ 0xB88C,0xB88E,0xB88F,0xB890,0xB891,0xB892,0xB893,0xB894,/* 0x48-0x4F */
+ 0xB895,0xB896,0xB897,0xB898,0xB899,0xB89A,0xB89B,0xB89C,/* 0x50-0x57 */
+ 0xB89D,0xB89E,0xB89F,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xB8A0,0xB8A1,0xB8A2,0xB8A3,0xB8A4,0xB8A5,0xB8A6,/* 0x60-0x67 */
+ 0xB8A7,0xB8A9,0xB8AA,0xB8AB,0xB8AC,0xB8AD,0xB8AE,0xB8AF,/* 0x68-0x6F */
+ 0xB8B1,0xB8B2,0xB8B3,0xB8B5,0xB8B6,0xB8B7,0xB8B9,0xB8BA,/* 0x70-0x77 */
+ 0xB8BB,0xB8BC,0xB8BD,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xB8BE,0xB8BF,0xB8C2,0xB8C4,0xB8C6,0xB8C7,0xB8C8,/* 0x80-0x87 */
+ 0xB8C9,0xB8CA,0xB8CB,0xB8CD,0xB8CE,0xB8CF,0xB8D1,0xB8D2,/* 0x88-0x8F */
+ 0xB8D3,0xB8D5,0xB8D6,0xB8D7,0xB8D8,0xB8D9,0xB8DA,0xB8DB,/* 0x90-0x97 */
+ 0xB8DC,0xB8DE,0xB8E0,0xB8E2,0xB8E3,0xB8E4,0xB8E5,0xB8E6,/* 0x98-0x9F */
+ 0xB8E7,0xB8EA,0xB8EB,0xB8ED,0xB8EE,0xB8EF,0xB8F1,0xB8F2,/* 0xA0-0xA7 */
+ 0xB8F3,0xB8F4,0xB8F5,0xB8F6,0xB8F7,0xB8FA,0xB8FC,0xB8FE,/* 0xA8-0xAF */
+ 0xB8FF,0xB900,0xB901,0xB902,0xB903,0xB905,0xB906,0xB907,/* 0xB0-0xB7 */
+ 0xB908,0xB909,0xB90A,0xB90B,0xB90C,0xB90D,0xB90E,0xB90F,/* 0xB8-0xBF */
+ 0xB910,0xB911,0xB912,0xB913,0xB914,0xB915,0xB916,0xB917,/* 0xC0-0xC7 */
+ 0xB919,0xB91A,0xB91B,0xB91C,0xB91D,0xB91E,0xB91F,0xB921,/* 0xC8-0xCF */
+ 0xB922,0xB923,0xB924,0xB925,0xB926,0xB927,0xB928,0xB929,/* 0xD0-0xD7 */
+ 0xB92A,0xB92B,0xB92C,0xB92D,0xB92E,0xB92F,0xB930,0xB931,/* 0xD8-0xDF */
+ 0xB932,0xB933,0xB934,0xB935,0xB936,0xB937,0xB938,0xB939,/* 0xE0-0xE7 */
+ 0xB93A,0xB93B,0xB93E,0xB93F,0xB941,0xB942,0xB943,0xB945,/* 0xE8-0xEF */
+ 0xB946,0xB947,0xB948,0xB949,0xB94A,0xB94B,0xB94D,0xB94E,/* 0xF0-0xF7 */
+ 0xB950,0xB952,0xB953,0xB954,0xB955,0xB956,0xB957,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_90[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xB95A,0xB95B,0xB95D,0xB95E,0xB95F,0xB961,0xB962,/* 0x40-0x47 */
+ 0xB963,0xB964,0xB965,0xB966,0xB967,0xB96A,0xB96C,0xB96E,/* 0x48-0x4F */
+ 0xB96F,0xB970,0xB971,0xB972,0xB973,0xB976,0xB977,0xB979,/* 0x50-0x57 */
+ 0xB97A,0xB97B,0xB97D,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xB97E,0xB97F,0xB980,0xB981,0xB982,0xB983,0xB986,/* 0x60-0x67 */
+ 0xB988,0xB98B,0xB98C,0xB98F,0xB990,0xB991,0xB992,0xB993,/* 0x68-0x6F */
+ 0xB994,0xB995,0xB996,0xB997,0xB998,0xB999,0xB99A,0xB99B,/* 0x70-0x77 */
+ 0xB99C,0xB99D,0xB99E,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xB99F,0xB9A0,0xB9A1,0xB9A2,0xB9A3,0xB9A4,0xB9A5,/* 0x80-0x87 */
+ 0xB9A6,0xB9A7,0xB9A8,0xB9A9,0xB9AA,0xB9AB,0xB9AE,0xB9AF,/* 0x88-0x8F */
+ 0xB9B1,0xB9B2,0xB9B3,0xB9B5,0xB9B6,0xB9B7,0xB9B8,0xB9B9,/* 0x90-0x97 */
+ 0xB9BA,0xB9BB,0xB9BE,0xB9C0,0xB9C2,0xB9C3,0xB9C4,0xB9C5,/* 0x98-0x9F */
+ 0xB9C6,0xB9C7,0xB9CA,0xB9CB,0xB9CD,0xB9D3,0xB9D4,0xB9D5,/* 0xA0-0xA7 */
+ 0xB9D6,0xB9D7,0xB9DA,0xB9DC,0xB9DF,0xB9E0,0xB9E2,0xB9E6,/* 0xA8-0xAF */
+ 0xB9E7,0xB9E9,0xB9EA,0xB9EB,0xB9ED,0xB9EE,0xB9EF,0xB9F0,/* 0xB0-0xB7 */
+ 0xB9F1,0xB9F2,0xB9F3,0xB9F6,0xB9FB,0xB9FC,0xB9FD,0xB9FE,/* 0xB8-0xBF */
+ 0xB9FF,0xBA02,0xBA03,0xBA04,0xBA05,0xBA06,0xBA07,0xBA09,/* 0xC0-0xC7 */
+ 0xBA0A,0xBA0B,0xBA0C,0xBA0D,0xBA0E,0xBA0F,0xBA10,0xBA11,/* 0xC8-0xCF */
+ 0xBA12,0xBA13,0xBA14,0xBA16,0xBA17,0xBA18,0xBA19,0xBA1A,/* 0xD0-0xD7 */
+ 0xBA1B,0xBA1C,0xBA1D,0xBA1E,0xBA1F,0xBA20,0xBA21,0xBA22,/* 0xD8-0xDF */
+ 0xBA23,0xBA24,0xBA25,0xBA26,0xBA27,0xBA28,0xBA29,0xBA2A,/* 0xE0-0xE7 */
+ 0xBA2B,0xBA2C,0xBA2D,0xBA2E,0xBA2F,0xBA30,0xBA31,0xBA32,/* 0xE8-0xEF */
+ 0xBA33,0xBA34,0xBA35,0xBA36,0xBA37,0xBA3A,0xBA3B,0xBA3D,/* 0xF0-0xF7 */
+ 0xBA3E,0xBA3F,0xBA41,0xBA43,0xBA44,0xBA45,0xBA46,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_91[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xBA47,0xBA4A,0xBA4C,0xBA4F,0xBA50,0xBA51,0xBA52,/* 0x40-0x47 */
+ 0xBA56,0xBA57,0xBA59,0xBA5A,0xBA5B,0xBA5D,0xBA5E,0xBA5F,/* 0x48-0x4F */
+ 0xBA60,0xBA61,0xBA62,0xBA63,0xBA66,0xBA6A,0xBA6B,0xBA6C,/* 0x50-0x57 */
+ 0xBA6D,0xBA6E,0xBA6F,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xBA72,0xBA73,0xBA75,0xBA76,0xBA77,0xBA79,0xBA7A,/* 0x60-0x67 */
+ 0xBA7B,0xBA7C,0xBA7D,0xBA7E,0xBA7F,0xBA80,0xBA81,0xBA82,/* 0x68-0x6F */
+ 0xBA86,0xBA88,0xBA89,0xBA8A,0xBA8B,0xBA8D,0xBA8E,0xBA8F,/* 0x70-0x77 */
+ 0xBA90,0xBA91,0xBA92,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xBA93,0xBA94,0xBA95,0xBA96,0xBA97,0xBA98,0xBA99,/* 0x80-0x87 */
+ 0xBA9A,0xBA9B,0xBA9C,0xBA9D,0xBA9E,0xBA9F,0xBAA0,0xBAA1,/* 0x88-0x8F */
+ 0xBAA2,0xBAA3,0xBAA4,0xBAA5,0xBAA6,0xBAA7,0xBAAA,0xBAAD,/* 0x90-0x97 */
+ 0xBAAE,0xBAAF,0xBAB1,0xBAB3,0xBAB4,0xBAB5,0xBAB6,0xBAB7,/* 0x98-0x9F */
+ 0xBABA,0xBABC,0xBABE,0xBABF,0xBAC0,0xBAC1,0xBAC2,0xBAC3,/* 0xA0-0xA7 */
+ 0xBAC5,0xBAC6,0xBAC7,0xBAC9,0xBACA,0xBACB,0xBACC,0xBACD,/* 0xA8-0xAF */
+ 0xBACE,0xBACF,0xBAD0,0xBAD1,0xBAD2,0xBAD3,0xBAD4,0xBAD5,/* 0xB0-0xB7 */
+ 0xBAD6,0xBAD7,0xBADA,0xBADB,0xBADC,0xBADD,0xBADE,0xBADF,/* 0xB8-0xBF */
+ 0xBAE0,0xBAE1,0xBAE2,0xBAE3,0xBAE4,0xBAE5,0xBAE6,0xBAE7,/* 0xC0-0xC7 */
+ 0xBAE8,0xBAE9,0xBAEA,0xBAEB,0xBAEC,0xBAED,0xBAEE,0xBAEF,/* 0xC8-0xCF */
+ 0xBAF0,0xBAF1,0xBAF2,0xBAF3,0xBAF4,0xBAF5,0xBAF6,0xBAF7,/* 0xD0-0xD7 */
+ 0xBAF8,0xBAF9,0xBAFA,0xBAFB,0xBAFD,0xBAFE,0xBAFF,0xBB01,/* 0xD8-0xDF */
+ 0xBB02,0xBB03,0xBB05,0xBB06,0xBB07,0xBB08,0xBB09,0xBB0A,/* 0xE0-0xE7 */
+ 0xBB0B,0xBB0C,0xBB0E,0xBB10,0xBB12,0xBB13,0xBB14,0xBB15,/* 0xE8-0xEF */
+ 0xBB16,0xBB17,0xBB19,0xBB1A,0xBB1B,0xBB1D,0xBB1E,0xBB1F,/* 0xF0-0xF7 */
+ 0xBB21,0xBB22,0xBB23,0xBB24,0xBB25,0xBB26,0xBB27,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_92[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xBB28,0xBB2A,0xBB2C,0xBB2D,0xBB2E,0xBB2F,0xBB30,/* 0x40-0x47 */
+ 0xBB31,0xBB32,0xBB33,0xBB37,0xBB39,0xBB3A,0xBB3F,0xBB40,/* 0x48-0x4F */
+ 0xBB41,0xBB42,0xBB43,0xBB46,0xBB48,0xBB4A,0xBB4B,0xBB4C,/* 0x50-0x57 */
+ 0xBB4E,0xBB51,0xBB52,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xBB53,0xBB55,0xBB56,0xBB57,0xBB59,0xBB5A,0xBB5B,/* 0x60-0x67 */
+ 0xBB5C,0xBB5D,0xBB5E,0xBB5F,0xBB60,0xBB62,0xBB64,0xBB65,/* 0x68-0x6F */
+ 0xBB66,0xBB67,0xBB68,0xBB69,0xBB6A,0xBB6B,0xBB6D,0xBB6E,/* 0x70-0x77 */
+ 0xBB6F,0xBB70,0xBB71,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xBB72,0xBB73,0xBB74,0xBB75,0xBB76,0xBB77,0xBB78,/* 0x80-0x87 */
+ 0xBB79,0xBB7A,0xBB7B,0xBB7C,0xBB7D,0xBB7E,0xBB7F,0xBB80,/* 0x88-0x8F */
+ 0xBB81,0xBB82,0xBB83,0xBB84,0xBB85,0xBB86,0xBB87,0xBB89,/* 0x90-0x97 */
+ 0xBB8A,0xBB8B,0xBB8D,0xBB8E,0xBB8F,0xBB91,0xBB92,0xBB93,/* 0x98-0x9F */
+ 0xBB94,0xBB95,0xBB96,0xBB97,0xBB98,0xBB99,0xBB9A,0xBB9B,/* 0xA0-0xA7 */
+ 0xBB9C,0xBB9D,0xBB9E,0xBB9F,0xBBA0,0xBBA1,0xBBA2,0xBBA3,/* 0xA8-0xAF */
+ 0xBBA5,0xBBA6,0xBBA7,0xBBA9,0xBBAA,0xBBAB,0xBBAD,0xBBAE,/* 0xB0-0xB7 */
+ 0xBBAF,0xBBB0,0xBBB1,0xBBB2,0xBBB3,0xBBB5,0xBBB6,0xBBB8,/* 0xB8-0xBF */
+ 0xBBB9,0xBBBA,0xBBBB,0xBBBC,0xBBBD,0xBBBE,0xBBBF,0xBBC1,/* 0xC0-0xC7 */
+ 0xBBC2,0xBBC3,0xBBC5,0xBBC6,0xBBC7,0xBBC9,0xBBCA,0xBBCB,/* 0xC8-0xCF */
+ 0xBBCC,0xBBCD,0xBBCE,0xBBCF,0xBBD1,0xBBD2,0xBBD4,0xBBD5,/* 0xD0-0xD7 */
+ 0xBBD6,0xBBD7,0xBBD8,0xBBD9,0xBBDA,0xBBDB,0xBBDC,0xBBDD,/* 0xD8-0xDF */
+ 0xBBDE,0xBBDF,0xBBE0,0xBBE1,0xBBE2,0xBBE3,0xBBE4,0xBBE5,/* 0xE0-0xE7 */
+ 0xBBE6,0xBBE7,0xBBE8,0xBBE9,0xBBEA,0xBBEB,0xBBEC,0xBBED,/* 0xE8-0xEF */
+ 0xBBEE,0xBBEF,0xBBF0,0xBBF1,0xBBF2,0xBBF3,0xBBF4,0xBBF5,/* 0xF0-0xF7 */
+ 0xBBF6,0xBBF7,0xBBFA,0xBBFB,0xBBFD,0xBBFE,0xBC01,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_93[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xBC03,0xBC04,0xBC05,0xBC06,0xBC07,0xBC0A,0xBC0E,/* 0x40-0x47 */
+ 0xBC10,0xBC12,0xBC13,0xBC19,0xBC1A,0xBC20,0xBC21,0xBC22,/* 0x48-0x4F */
+ 0xBC23,0xBC26,0xBC28,0xBC2A,0xBC2B,0xBC2C,0xBC2E,0xBC2F,/* 0x50-0x57 */
+ 0xBC32,0xBC33,0xBC35,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xBC36,0xBC37,0xBC39,0xBC3A,0xBC3B,0xBC3C,0xBC3D,/* 0x60-0x67 */
+ 0xBC3E,0xBC3F,0xBC42,0xBC46,0xBC47,0xBC48,0xBC4A,0xBC4B,/* 0x68-0x6F */
+ 0xBC4E,0xBC4F,0xBC51,0xBC52,0xBC53,0xBC54,0xBC55,0xBC56,/* 0x70-0x77 */
+ 0xBC57,0xBC58,0xBC59,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xBC5A,0xBC5B,0xBC5C,0xBC5E,0xBC5F,0xBC60,0xBC61,/* 0x80-0x87 */
+ 0xBC62,0xBC63,0xBC64,0xBC65,0xBC66,0xBC67,0xBC68,0xBC69,/* 0x88-0x8F */
+ 0xBC6A,0xBC6B,0xBC6C,0xBC6D,0xBC6E,0xBC6F,0xBC70,0xBC71,/* 0x90-0x97 */
+ 0xBC72,0xBC73,0xBC74,0xBC75,0xBC76,0xBC77,0xBC78,0xBC79,/* 0x98-0x9F */
+ 0xBC7A,0xBC7B,0xBC7C,0xBC7D,0xBC7E,0xBC7F,0xBC80,0xBC81,/* 0xA0-0xA7 */
+ 0xBC82,0xBC83,0xBC86,0xBC87,0xBC89,0xBC8A,0xBC8D,0xBC8F,/* 0xA8-0xAF */
+ 0xBC90,0xBC91,0xBC92,0xBC93,0xBC96,0xBC98,0xBC9B,0xBC9C,/* 0xB0-0xB7 */
+ 0xBC9D,0xBC9E,0xBC9F,0xBCA2,0xBCA3,0xBCA5,0xBCA6,0xBCA9,/* 0xB8-0xBF */
+ 0xBCAA,0xBCAB,0xBCAC,0xBCAD,0xBCAE,0xBCAF,0xBCB2,0xBCB6,/* 0xC0-0xC7 */
+ 0xBCB7,0xBCB8,0xBCB9,0xBCBA,0xBCBB,0xBCBE,0xBCBF,0xBCC1,/* 0xC8-0xCF */
+ 0xBCC2,0xBCC3,0xBCC5,0xBCC6,0xBCC7,0xBCC8,0xBCC9,0xBCCA,/* 0xD0-0xD7 */
+ 0xBCCB,0xBCCC,0xBCCE,0xBCD2,0xBCD3,0xBCD4,0xBCD6,0xBCD7,/* 0xD8-0xDF */
+ 0xBCD9,0xBCDA,0xBCDB,0xBCDD,0xBCDE,0xBCDF,0xBCE0,0xBCE1,/* 0xE0-0xE7 */
+ 0xBCE2,0xBCE3,0xBCE4,0xBCE5,0xBCE6,0xBCE7,0xBCE8,0xBCE9,/* 0xE8-0xEF */
+ 0xBCEA,0xBCEB,0xBCEC,0xBCED,0xBCEE,0xBCEF,0xBCF0,0xBCF1,/* 0xF0-0xF7 */
+ 0xBCF2,0xBCF3,0xBCF7,0xBCF9,0xBCFA,0xBCFB,0xBCFD,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_94[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xBCFE,0xBCFF,0xBD00,0xBD01,0xBD02,0xBD03,0xBD06,/* 0x40-0x47 */
+ 0xBD08,0xBD0A,0xBD0B,0xBD0C,0xBD0D,0xBD0E,0xBD0F,0xBD11,/* 0x48-0x4F */
+ 0xBD12,0xBD13,0xBD15,0xBD16,0xBD17,0xBD18,0xBD19,0xBD1A,/* 0x50-0x57 */
+ 0xBD1B,0xBD1C,0xBD1D,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xBD1E,0xBD1F,0xBD20,0xBD21,0xBD22,0xBD23,0xBD25,/* 0x60-0x67 */
+ 0xBD26,0xBD27,0xBD28,0xBD29,0xBD2A,0xBD2B,0xBD2D,0xBD2E,/* 0x68-0x6F */
+ 0xBD2F,0xBD30,0xBD31,0xBD32,0xBD33,0xBD34,0xBD35,0xBD36,/* 0x70-0x77 */
+ 0xBD37,0xBD38,0xBD39,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xBD3A,0xBD3B,0xBD3C,0xBD3D,0xBD3E,0xBD3F,0xBD41,/* 0x80-0x87 */
+ 0xBD42,0xBD43,0xBD44,0xBD45,0xBD46,0xBD47,0xBD4A,0xBD4B,/* 0x88-0x8F */
+ 0xBD4D,0xBD4E,0xBD4F,0xBD51,0xBD52,0xBD53,0xBD54,0xBD55,/* 0x90-0x97 */
+ 0xBD56,0xBD57,0xBD5A,0xBD5B,0xBD5C,0xBD5D,0xBD5E,0xBD5F,/* 0x98-0x9F */
+ 0xBD60,0xBD61,0xBD62,0xBD63,0xBD65,0xBD66,0xBD67,0xBD69,/* 0xA0-0xA7 */
+ 0xBD6A,0xBD6B,0xBD6C,0xBD6D,0xBD6E,0xBD6F,0xBD70,0xBD71,/* 0xA8-0xAF */
+ 0xBD72,0xBD73,0xBD74,0xBD75,0xBD76,0xBD77,0xBD78,0xBD79,/* 0xB0-0xB7 */
+ 0xBD7A,0xBD7B,0xBD7C,0xBD7D,0xBD7E,0xBD7F,0xBD82,0xBD83,/* 0xB8-0xBF */
+ 0xBD85,0xBD86,0xBD8B,0xBD8C,0xBD8D,0xBD8E,0xBD8F,0xBD92,/* 0xC0-0xC7 */
+ 0xBD94,0xBD96,0xBD97,0xBD98,0xBD9B,0xBD9D,0xBD9E,0xBD9F,/* 0xC8-0xCF */
+ 0xBDA0,0xBDA1,0xBDA2,0xBDA3,0xBDA5,0xBDA6,0xBDA7,0xBDA8,/* 0xD0-0xD7 */
+ 0xBDA9,0xBDAA,0xBDAB,0xBDAC,0xBDAD,0xBDAE,0xBDAF,0xBDB1,/* 0xD8-0xDF */
+ 0xBDB2,0xBDB3,0xBDB4,0xBDB5,0xBDB6,0xBDB7,0xBDB9,0xBDBA,/* 0xE0-0xE7 */
+ 0xBDBB,0xBDBC,0xBDBD,0xBDBE,0xBDBF,0xBDC0,0xBDC1,0xBDC2,/* 0xE8-0xEF */
+ 0xBDC3,0xBDC4,0xBDC5,0xBDC6,0xBDC7,0xBDC8,0xBDC9,0xBDCA,/* 0xF0-0xF7 */
+ 0xBDCB,0xBDCC,0xBDCD,0xBDCE,0xBDCF,0xBDD0,0xBDD1,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_95[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xBDD2,0xBDD3,0xBDD6,0xBDD7,0xBDD9,0xBDDA,0xBDDB,/* 0x40-0x47 */
+ 0xBDDD,0xBDDE,0xBDDF,0xBDE0,0xBDE1,0xBDE2,0xBDE3,0xBDE4,/* 0x48-0x4F */
+ 0xBDE5,0xBDE6,0xBDE7,0xBDE8,0xBDEA,0xBDEB,0xBDEC,0xBDED,/* 0x50-0x57 */
+ 0xBDEE,0xBDEF,0xBDF1,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xBDF2,0xBDF3,0xBDF5,0xBDF6,0xBDF7,0xBDF9,0xBDFA,/* 0x60-0x67 */
+ 0xBDFB,0xBDFC,0xBDFD,0xBDFE,0xBDFF,0xBE01,0xBE02,0xBE04,/* 0x68-0x6F */
+ 0xBE06,0xBE07,0xBE08,0xBE09,0xBE0A,0xBE0B,0xBE0E,0xBE0F,/* 0x70-0x77 */
+ 0xBE11,0xBE12,0xBE13,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xBE15,0xBE16,0xBE17,0xBE18,0xBE19,0xBE1A,0xBE1B,/* 0x80-0x87 */
+ 0xBE1E,0xBE20,0xBE21,0xBE22,0xBE23,0xBE24,0xBE25,0xBE26,/* 0x88-0x8F */
+ 0xBE27,0xBE28,0xBE29,0xBE2A,0xBE2B,0xBE2C,0xBE2D,0xBE2E,/* 0x90-0x97 */
+ 0xBE2F,0xBE30,0xBE31,0xBE32,0xBE33,0xBE34,0xBE35,0xBE36,/* 0x98-0x9F */
+ 0xBE37,0xBE38,0xBE39,0xBE3A,0xBE3B,0xBE3C,0xBE3D,0xBE3E,/* 0xA0-0xA7 */
+ 0xBE3F,0xBE40,0xBE41,0xBE42,0xBE43,0xBE46,0xBE47,0xBE49,/* 0xA8-0xAF */
+ 0xBE4A,0xBE4B,0xBE4D,0xBE4F,0xBE50,0xBE51,0xBE52,0xBE53,/* 0xB0-0xB7 */
+ 0xBE56,0xBE58,0xBE5C,0xBE5D,0xBE5E,0xBE5F,0xBE62,0xBE63,/* 0xB8-0xBF */
+ 0xBE65,0xBE66,0xBE67,0xBE69,0xBE6B,0xBE6C,0xBE6D,0xBE6E,/* 0xC0-0xC7 */
+ 0xBE6F,0xBE72,0xBE76,0xBE77,0xBE78,0xBE79,0xBE7A,0xBE7E,/* 0xC8-0xCF */
+ 0xBE7F,0xBE81,0xBE82,0xBE83,0xBE85,0xBE86,0xBE87,0xBE88,/* 0xD0-0xD7 */
+ 0xBE89,0xBE8A,0xBE8B,0xBE8E,0xBE92,0xBE93,0xBE94,0xBE95,/* 0xD8-0xDF */
+ 0xBE96,0xBE97,0xBE9A,0xBE9B,0xBE9C,0xBE9D,0xBE9E,0xBE9F,/* 0xE0-0xE7 */
+ 0xBEA0,0xBEA1,0xBEA2,0xBEA3,0xBEA4,0xBEA5,0xBEA6,0xBEA7,/* 0xE8-0xEF */
+ 0xBEA9,0xBEAA,0xBEAB,0xBEAC,0xBEAD,0xBEAE,0xBEAF,0xBEB0,/* 0xF0-0xF7 */
+ 0xBEB1,0xBEB2,0xBEB3,0xBEB4,0xBEB5,0xBEB6,0xBEB7,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_96[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xBEB8,0xBEB9,0xBEBA,0xBEBB,0xBEBC,0xBEBD,0xBEBE,/* 0x40-0x47 */
+ 0xBEBF,0xBEC0,0xBEC1,0xBEC2,0xBEC3,0xBEC4,0xBEC5,0xBEC6,/* 0x48-0x4F */
+ 0xBEC7,0xBEC8,0xBEC9,0xBECA,0xBECB,0xBECC,0xBECD,0xBECE,/* 0x50-0x57 */
+ 0xBECF,0xBED2,0xBED3,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xBED5,0xBED6,0xBED9,0xBEDA,0xBEDB,0xBEDC,0xBEDD,/* 0x60-0x67 */
+ 0xBEDE,0xBEDF,0xBEE1,0xBEE2,0xBEE6,0xBEE7,0xBEE8,0xBEE9,/* 0x68-0x6F */
+ 0xBEEA,0xBEEB,0xBEED,0xBEEE,0xBEEF,0xBEF0,0xBEF1,0xBEF2,/* 0x70-0x77 */
+ 0xBEF3,0xBEF4,0xBEF5,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xBEF6,0xBEF7,0xBEF8,0xBEF9,0xBEFA,0xBEFB,0xBEFC,/* 0x80-0x87 */
+ 0xBEFD,0xBEFE,0xBEFF,0xBF00,0xBF02,0xBF03,0xBF04,0xBF05,/* 0x88-0x8F */
+ 0xBF06,0xBF07,0xBF0A,0xBF0B,0xBF0C,0xBF0D,0xBF0E,0xBF0F,/* 0x90-0x97 */
+ 0xBF10,0xBF11,0xBF12,0xBF13,0xBF14,0xBF15,0xBF16,0xBF17,/* 0x98-0x9F */
+ 0xBF1A,0xBF1E,0xBF1F,0xBF20,0xBF21,0xBF22,0xBF23,0xBF24,/* 0xA0-0xA7 */
+ 0xBF25,0xBF26,0xBF27,0xBF28,0xBF29,0xBF2A,0xBF2B,0xBF2C,/* 0xA8-0xAF */
+ 0xBF2D,0xBF2E,0xBF2F,0xBF30,0xBF31,0xBF32,0xBF33,0xBF34,/* 0xB0-0xB7 */
+ 0xBF35,0xBF36,0xBF37,0xBF38,0xBF39,0xBF3A,0xBF3B,0xBF3C,/* 0xB8-0xBF */
+ 0xBF3D,0xBF3E,0xBF3F,0xBF42,0xBF43,0xBF45,0xBF46,0xBF47,/* 0xC0-0xC7 */
+ 0xBF49,0xBF4A,0xBF4B,0xBF4C,0xBF4D,0xBF4E,0xBF4F,0xBF52,/* 0xC8-0xCF */
+ 0xBF53,0xBF54,0xBF56,0xBF57,0xBF58,0xBF59,0xBF5A,0xBF5B,/* 0xD0-0xD7 */
+ 0xBF5C,0xBF5D,0xBF5E,0xBF5F,0xBF60,0xBF61,0xBF62,0xBF63,/* 0xD8-0xDF */
+ 0xBF64,0xBF65,0xBF66,0xBF67,0xBF68,0xBF69,0xBF6A,0xBF6B,/* 0xE0-0xE7 */
+ 0xBF6C,0xBF6D,0xBF6E,0xBF6F,0xBF70,0xBF71,0xBF72,0xBF73,/* 0xE8-0xEF */
+ 0xBF74,0xBF75,0xBF76,0xBF77,0xBF78,0xBF79,0xBF7A,0xBF7B,/* 0xF0-0xF7 */
+ 0xBF7C,0xBF7D,0xBF7E,0xBF7F,0xBF80,0xBF81,0xBF82,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_97[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xBF83,0xBF84,0xBF85,0xBF86,0xBF87,0xBF88,0xBF89,/* 0x40-0x47 */
+ 0xBF8A,0xBF8B,0xBF8C,0xBF8D,0xBF8E,0xBF8F,0xBF90,0xBF91,/* 0x48-0x4F */
+ 0xBF92,0xBF93,0xBF95,0xBF96,0xBF97,0xBF98,0xBF99,0xBF9A,/* 0x50-0x57 */
+ 0xBF9B,0xBF9C,0xBF9D,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xBF9E,0xBF9F,0xBFA0,0xBFA1,0xBFA2,0xBFA3,0xBFA4,/* 0x60-0x67 */
+ 0xBFA5,0xBFA6,0xBFA7,0xBFA8,0xBFA9,0xBFAA,0xBFAB,0xBFAC,/* 0x68-0x6F */
+ 0xBFAD,0xBFAE,0xBFAF,0xBFB1,0xBFB2,0xBFB3,0xBFB4,0xBFB5,/* 0x70-0x77 */
+ 0xBFB6,0xBFB7,0xBFB8,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xBFB9,0xBFBA,0xBFBB,0xBFBC,0xBFBD,0xBFBE,0xBFBF,/* 0x80-0x87 */
+ 0xBFC0,0xBFC1,0xBFC2,0xBFC3,0xBFC4,0xBFC6,0xBFC7,0xBFC8,/* 0x88-0x8F */
+ 0xBFC9,0xBFCA,0xBFCB,0xBFCE,0xBFCF,0xBFD1,0xBFD2,0xBFD3,/* 0x90-0x97 */
+ 0xBFD5,0xBFD6,0xBFD7,0xBFD8,0xBFD9,0xBFDA,0xBFDB,0xBFDD,/* 0x98-0x9F */
+ 0xBFDE,0xBFE0,0xBFE2,0xBFE3,0xBFE4,0xBFE5,0xBFE6,0xBFE7,/* 0xA0-0xA7 */
+ 0xBFE8,0xBFE9,0xBFEA,0xBFEB,0xBFEC,0xBFED,0xBFEE,0xBFEF,/* 0xA8-0xAF */
+ 0xBFF0,0xBFF1,0xBFF2,0xBFF3,0xBFF4,0xBFF5,0xBFF6,0xBFF7,/* 0xB0-0xB7 */
+ 0xBFF8,0xBFF9,0xBFFA,0xBFFB,0xBFFC,0xBFFD,0xBFFE,0xBFFF,/* 0xB8-0xBF */
+ 0xC000,0xC001,0xC002,0xC003,0xC004,0xC005,0xC006,0xC007,/* 0xC0-0xC7 */
+ 0xC008,0xC009,0xC00A,0xC00B,0xC00C,0xC00D,0xC00E,0xC00F,/* 0xC8-0xCF */
+ 0xC010,0xC011,0xC012,0xC013,0xC014,0xC015,0xC016,0xC017,/* 0xD0-0xD7 */
+ 0xC018,0xC019,0xC01A,0xC01B,0xC01C,0xC01D,0xC01E,0xC01F,/* 0xD8-0xDF */
+ 0xC020,0xC021,0xC022,0xC023,0xC024,0xC025,0xC026,0xC027,/* 0xE0-0xE7 */
+ 0xC028,0xC029,0xC02A,0xC02B,0xC02C,0xC02D,0xC02E,0xC02F,/* 0xE8-0xEF */
+ 0xC030,0xC031,0xC032,0xC033,0xC034,0xC035,0xC036,0xC037,/* 0xF0-0xF7 */
+ 0xC038,0xC039,0xC03A,0xC03B,0xC03D,0xC03E,0xC03F,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_98[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xC040,0xC041,0xC042,0xC043,0xC044,0xC045,0xC046,/* 0x40-0x47 */
+ 0xC047,0xC048,0xC049,0xC04A,0xC04B,0xC04C,0xC04D,0xC04E,/* 0x48-0x4F */
+ 0xC04F,0xC050,0xC052,0xC053,0xC054,0xC055,0xC056,0xC057,/* 0x50-0x57 */
+ 0xC059,0xC05A,0xC05B,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xC05D,0xC05E,0xC05F,0xC061,0xC062,0xC063,0xC064,/* 0x60-0x67 */
+ 0xC065,0xC066,0xC067,0xC06A,0xC06B,0xC06C,0xC06D,0xC06E,/* 0x68-0x6F */
+ 0xC06F,0xC070,0xC071,0xC072,0xC073,0xC074,0xC075,0xC076,/* 0x70-0x77 */
+ 0xC077,0xC078,0xC079,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xC07A,0xC07B,0xC07C,0xC07D,0xC07E,0xC07F,0xC080,/* 0x80-0x87 */
+ 0xC081,0xC082,0xC083,0xC084,0xC085,0xC086,0xC087,0xC088,/* 0x88-0x8F */
+ 0xC089,0xC08A,0xC08B,0xC08C,0xC08D,0xC08E,0xC08F,0xC092,/* 0x90-0x97 */
+ 0xC093,0xC095,0xC096,0xC097,0xC099,0xC09A,0xC09B,0xC09C,/* 0x98-0x9F */
+ 0xC09D,0xC09E,0xC09F,0xC0A2,0xC0A4,0xC0A6,0xC0A7,0xC0A8,/* 0xA0-0xA7 */
+ 0xC0A9,0xC0AA,0xC0AB,0xC0AE,0xC0B1,0xC0B2,0xC0B7,0xC0B8,/* 0xA8-0xAF */
+ 0xC0B9,0xC0BA,0xC0BB,0xC0BE,0xC0C2,0xC0C3,0xC0C4,0xC0C6,/* 0xB0-0xB7 */
+ 0xC0C7,0xC0CA,0xC0CB,0xC0CD,0xC0CE,0xC0CF,0xC0D1,0xC0D2,/* 0xB8-0xBF */
+ 0xC0D3,0xC0D4,0xC0D5,0xC0D6,0xC0D7,0xC0DA,0xC0DE,0xC0DF,/* 0xC0-0xC7 */
+ 0xC0E0,0xC0E1,0xC0E2,0xC0E3,0xC0E6,0xC0E7,0xC0E9,0xC0EA,/* 0xC8-0xCF */
+ 0xC0EB,0xC0ED,0xC0EE,0xC0EF,0xC0F0,0xC0F1,0xC0F2,0xC0F3,/* 0xD0-0xD7 */
+ 0xC0F6,0xC0F8,0xC0FA,0xC0FB,0xC0FC,0xC0FD,0xC0FE,0xC0FF,/* 0xD8-0xDF */
+ 0xC101,0xC102,0xC103,0xC105,0xC106,0xC107,0xC109,0xC10A,/* 0xE0-0xE7 */
+ 0xC10B,0xC10C,0xC10D,0xC10E,0xC10F,0xC111,0xC112,0xC113,/* 0xE8-0xEF */
+ 0xC114,0xC116,0xC117,0xC118,0xC119,0xC11A,0xC11B,0xC121,/* 0xF0-0xF7 */
+ 0xC122,0xC125,0xC128,0xC129,0xC12A,0xC12B,0xC12E,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_99[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xC132,0xC133,0xC134,0xC135,0xC137,0xC13A,0xC13B,/* 0x40-0x47 */
+ 0xC13D,0xC13E,0xC13F,0xC141,0xC142,0xC143,0xC144,0xC145,/* 0x48-0x4F */
+ 0xC146,0xC147,0xC14A,0xC14E,0xC14F,0xC150,0xC151,0xC152,/* 0x50-0x57 */
+ 0xC153,0xC156,0xC157,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xC159,0xC15A,0xC15B,0xC15D,0xC15E,0xC15F,0xC160,/* 0x60-0x67 */
+ 0xC161,0xC162,0xC163,0xC166,0xC16A,0xC16B,0xC16C,0xC16D,/* 0x68-0x6F */
+ 0xC16E,0xC16F,0xC171,0xC172,0xC173,0xC175,0xC176,0xC177,/* 0x70-0x77 */
+ 0xC179,0xC17A,0xC17B,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xC17C,0xC17D,0xC17E,0xC17F,0xC180,0xC181,0xC182,/* 0x80-0x87 */
+ 0xC183,0xC184,0xC186,0xC187,0xC188,0xC189,0xC18A,0xC18B,/* 0x88-0x8F */
+ 0xC18F,0xC191,0xC192,0xC193,0xC195,0xC197,0xC198,0xC199,/* 0x90-0x97 */
+ 0xC19A,0xC19B,0xC19E,0xC1A0,0xC1A2,0xC1A3,0xC1A4,0xC1A6,/* 0x98-0x9F */
+ 0xC1A7,0xC1AA,0xC1AB,0xC1AD,0xC1AE,0xC1AF,0xC1B1,0xC1B2,/* 0xA0-0xA7 */
+ 0xC1B3,0xC1B4,0xC1B5,0xC1B6,0xC1B7,0xC1B8,0xC1B9,0xC1BA,/* 0xA8-0xAF */
+ 0xC1BB,0xC1BC,0xC1BE,0xC1BF,0xC1C0,0xC1C1,0xC1C2,0xC1C3,/* 0xB0-0xB7 */
+ 0xC1C5,0xC1C6,0xC1C7,0xC1C9,0xC1CA,0xC1CB,0xC1CD,0xC1CE,/* 0xB8-0xBF */
+ 0xC1CF,0xC1D0,0xC1D1,0xC1D2,0xC1D3,0xC1D5,0xC1D6,0xC1D9,/* 0xC0-0xC7 */
+ 0xC1DA,0xC1DB,0xC1DC,0xC1DD,0xC1DE,0xC1DF,0xC1E1,0xC1E2,/* 0xC8-0xCF */
+ 0xC1E3,0xC1E5,0xC1E6,0xC1E7,0xC1E9,0xC1EA,0xC1EB,0xC1EC,/* 0xD0-0xD7 */
+ 0xC1ED,0xC1EE,0xC1EF,0xC1F2,0xC1F4,0xC1F5,0xC1F6,0xC1F7,/* 0xD8-0xDF */
+ 0xC1F8,0xC1F9,0xC1FA,0xC1FB,0xC1FE,0xC1FF,0xC201,0xC202,/* 0xE0-0xE7 */
+ 0xC203,0xC205,0xC206,0xC207,0xC208,0xC209,0xC20A,0xC20B,/* 0xE8-0xEF */
+ 0xC20E,0xC210,0xC212,0xC213,0xC214,0xC215,0xC216,0xC217,/* 0xF0-0xF7 */
+ 0xC21A,0xC21B,0xC21D,0xC21E,0xC221,0xC222,0xC223,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_9A[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xC224,0xC225,0xC226,0xC227,0xC22A,0xC22C,0xC22E,/* 0x40-0x47 */
+ 0xC230,0xC233,0xC235,0xC236,0xC237,0xC238,0xC239,0xC23A,/* 0x48-0x4F */
+ 0xC23B,0xC23C,0xC23D,0xC23E,0xC23F,0xC240,0xC241,0xC242,/* 0x50-0x57 */
+ 0xC243,0xC244,0xC245,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xC246,0xC247,0xC249,0xC24A,0xC24B,0xC24C,0xC24D,/* 0x60-0x67 */
+ 0xC24E,0xC24F,0xC252,0xC253,0xC255,0xC256,0xC257,0xC259,/* 0x68-0x6F */
+ 0xC25A,0xC25B,0xC25C,0xC25D,0xC25E,0xC25F,0xC261,0xC262,/* 0x70-0x77 */
+ 0xC263,0xC264,0xC266,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xC267,0xC268,0xC269,0xC26A,0xC26B,0xC26E,0xC26F,/* 0x80-0x87 */
+ 0xC271,0xC272,0xC273,0xC275,0xC276,0xC277,0xC278,0xC279,/* 0x88-0x8F */
+ 0xC27A,0xC27B,0xC27E,0xC280,0xC282,0xC283,0xC284,0xC285,/* 0x90-0x97 */
+ 0xC286,0xC287,0xC28A,0xC28B,0xC28C,0xC28D,0xC28E,0xC28F,/* 0x98-0x9F */
+ 0xC291,0xC292,0xC293,0xC294,0xC295,0xC296,0xC297,0xC299,/* 0xA0-0xA7 */
+ 0xC29A,0xC29C,0xC29E,0xC29F,0xC2A0,0xC2A1,0xC2A2,0xC2A3,/* 0xA8-0xAF */
+ 0xC2A6,0xC2A7,0xC2A9,0xC2AA,0xC2AB,0xC2AE,0xC2AF,0xC2B0,/* 0xB0-0xB7 */
+ 0xC2B1,0xC2B2,0xC2B3,0xC2B6,0xC2B8,0xC2BA,0xC2BB,0xC2BC,/* 0xB8-0xBF */
+ 0xC2BD,0xC2BE,0xC2BF,0xC2C0,0xC2C1,0xC2C2,0xC2C3,0xC2C4,/* 0xC0-0xC7 */
+ 0xC2C5,0xC2C6,0xC2C7,0xC2C8,0xC2C9,0xC2CA,0xC2CB,0xC2CC,/* 0xC8-0xCF */
+ 0xC2CD,0xC2CE,0xC2CF,0xC2D0,0xC2D1,0xC2D2,0xC2D3,0xC2D4,/* 0xD0-0xD7 */
+ 0xC2D5,0xC2D6,0xC2D7,0xC2D8,0xC2D9,0xC2DA,0xC2DB,0xC2DE,/* 0xD8-0xDF */
+ 0xC2DF,0xC2E1,0xC2E2,0xC2E5,0xC2E6,0xC2E7,0xC2E8,0xC2E9,/* 0xE0-0xE7 */
+ 0xC2EA,0xC2EE,0xC2F0,0xC2F2,0xC2F3,0xC2F4,0xC2F5,0xC2F7,/* 0xE8-0xEF */
+ 0xC2FA,0xC2FD,0xC2FE,0xC2FF,0xC301,0xC302,0xC303,0xC304,/* 0xF0-0xF7 */
+ 0xC305,0xC306,0xC307,0xC30A,0xC30B,0xC30E,0xC30F,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_9B[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xC310,0xC311,0xC312,0xC316,0xC317,0xC319,0xC31A,/* 0x40-0x47 */
+ 0xC31B,0xC31D,0xC31E,0xC31F,0xC320,0xC321,0xC322,0xC323,/* 0x48-0x4F */
+ 0xC326,0xC327,0xC32A,0xC32B,0xC32C,0xC32D,0xC32E,0xC32F,/* 0x50-0x57 */
+ 0xC330,0xC331,0xC332,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xC333,0xC334,0xC335,0xC336,0xC337,0xC338,0xC339,/* 0x60-0x67 */
+ 0xC33A,0xC33B,0xC33C,0xC33D,0xC33E,0xC33F,0xC340,0xC341,/* 0x68-0x6F */
+ 0xC342,0xC343,0xC344,0xC346,0xC347,0xC348,0xC349,0xC34A,/* 0x70-0x77 */
+ 0xC34B,0xC34C,0xC34D,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xC34E,0xC34F,0xC350,0xC351,0xC352,0xC353,0xC354,/* 0x80-0x87 */
+ 0xC355,0xC356,0xC357,0xC358,0xC359,0xC35A,0xC35B,0xC35C,/* 0x88-0x8F */
+ 0xC35D,0xC35E,0xC35F,0xC360,0xC361,0xC362,0xC363,0xC364,/* 0x90-0x97 */
+ 0xC365,0xC366,0xC367,0xC36A,0xC36B,0xC36D,0xC36E,0xC36F,/* 0x98-0x9F */
+ 0xC371,0xC373,0xC374,0xC375,0xC376,0xC377,0xC37A,0xC37B,/* 0xA0-0xA7 */
+ 0xC37E,0xC37F,0xC380,0xC381,0xC382,0xC383,0xC385,0xC386,/* 0xA8-0xAF */
+ 0xC387,0xC389,0xC38A,0xC38B,0xC38D,0xC38E,0xC38F,0xC390,/* 0xB0-0xB7 */
+ 0xC391,0xC392,0xC393,0xC394,0xC395,0xC396,0xC397,0xC398,/* 0xB8-0xBF */
+ 0xC399,0xC39A,0xC39B,0xC39C,0xC39D,0xC39E,0xC39F,0xC3A0,/* 0xC0-0xC7 */
+ 0xC3A1,0xC3A2,0xC3A3,0xC3A4,0xC3A5,0xC3A6,0xC3A7,0xC3A8,/* 0xC8-0xCF */
+ 0xC3A9,0xC3AA,0xC3AB,0xC3AC,0xC3AD,0xC3AE,0xC3AF,0xC3B0,/* 0xD0-0xD7 */
+ 0xC3B1,0xC3B2,0xC3B3,0xC3B4,0xC3B5,0xC3B6,0xC3B7,0xC3B8,/* 0xD8-0xDF */
+ 0xC3B9,0xC3BA,0xC3BB,0xC3BC,0xC3BD,0xC3BE,0xC3BF,0xC3C1,/* 0xE0-0xE7 */
+ 0xC3C2,0xC3C3,0xC3C4,0xC3C5,0xC3C6,0xC3C7,0xC3C8,0xC3C9,/* 0xE8-0xEF */
+ 0xC3CA,0xC3CB,0xC3CC,0xC3CD,0xC3CE,0xC3CF,0xC3D0,0xC3D1,/* 0xF0-0xF7 */
+ 0xC3D2,0xC3D3,0xC3D4,0xC3D5,0xC3D6,0xC3D7,0xC3DA,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_9C[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xC3DB,0xC3DD,0xC3DE,0xC3E1,0xC3E3,0xC3E4,0xC3E5,/* 0x40-0x47 */
+ 0xC3E6,0xC3E7,0xC3EA,0xC3EB,0xC3EC,0xC3EE,0xC3EF,0xC3F0,/* 0x48-0x4F */
+ 0xC3F1,0xC3F2,0xC3F3,0xC3F6,0xC3F7,0xC3F9,0xC3FA,0xC3FB,/* 0x50-0x57 */
+ 0xC3FC,0xC3FD,0xC3FE,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xC3FF,0xC400,0xC401,0xC402,0xC403,0xC404,0xC405,/* 0x60-0x67 */
+ 0xC406,0xC407,0xC409,0xC40A,0xC40B,0xC40C,0xC40D,0xC40E,/* 0x68-0x6F */
+ 0xC40F,0xC411,0xC412,0xC413,0xC414,0xC415,0xC416,0xC417,/* 0x70-0x77 */
+ 0xC418,0xC419,0xC41A,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xC41B,0xC41C,0xC41D,0xC41E,0xC41F,0xC420,0xC421,/* 0x80-0x87 */
+ 0xC422,0xC423,0xC425,0xC426,0xC427,0xC428,0xC429,0xC42A,/* 0x88-0x8F */
+ 0xC42B,0xC42D,0xC42E,0xC42F,0xC431,0xC432,0xC433,0xC435,/* 0x90-0x97 */
+ 0xC436,0xC437,0xC438,0xC439,0xC43A,0xC43B,0xC43E,0xC43F,/* 0x98-0x9F */
+ 0xC440,0xC441,0xC442,0xC443,0xC444,0xC445,0xC446,0xC447,/* 0xA0-0xA7 */
+ 0xC449,0xC44A,0xC44B,0xC44C,0xC44D,0xC44E,0xC44F,0xC450,/* 0xA8-0xAF */
+ 0xC451,0xC452,0xC453,0xC454,0xC455,0xC456,0xC457,0xC458,/* 0xB0-0xB7 */
+ 0xC459,0xC45A,0xC45B,0xC45C,0xC45D,0xC45E,0xC45F,0xC460,/* 0xB8-0xBF */
+ 0xC461,0xC462,0xC463,0xC466,0xC467,0xC469,0xC46A,0xC46B,/* 0xC0-0xC7 */
+ 0xC46D,0xC46E,0xC46F,0xC470,0xC471,0xC472,0xC473,0xC476,/* 0xC8-0xCF */
+ 0xC477,0xC478,0xC47A,0xC47B,0xC47C,0xC47D,0xC47E,0xC47F,/* 0xD0-0xD7 */
+ 0xC481,0xC482,0xC483,0xC484,0xC485,0xC486,0xC487,0xC488,/* 0xD8-0xDF */
+ 0xC489,0xC48A,0xC48B,0xC48C,0xC48D,0xC48E,0xC48F,0xC490,/* 0xE0-0xE7 */
+ 0xC491,0xC492,0xC493,0xC495,0xC496,0xC497,0xC498,0xC499,/* 0xE8-0xEF */
+ 0xC49A,0xC49B,0xC49D,0xC49E,0xC49F,0xC4A0,0xC4A1,0xC4A2,/* 0xF0-0xF7 */
+ 0xC4A3,0xC4A4,0xC4A5,0xC4A6,0xC4A7,0xC4A8,0xC4A9,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_9D[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xC4AA,0xC4AB,0xC4AC,0xC4AD,0xC4AE,0xC4AF,0xC4B0,/* 0x40-0x47 */
+ 0xC4B1,0xC4B2,0xC4B3,0xC4B4,0xC4B5,0xC4B6,0xC4B7,0xC4B9,/* 0x48-0x4F */
+ 0xC4BA,0xC4BB,0xC4BD,0xC4BE,0xC4BF,0xC4C0,0xC4C1,0xC4C2,/* 0x50-0x57 */
+ 0xC4C3,0xC4C4,0xC4C5,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xC4C6,0xC4C7,0xC4C8,0xC4C9,0xC4CA,0xC4CB,0xC4CC,/* 0x60-0x67 */
+ 0xC4CD,0xC4CE,0xC4CF,0xC4D0,0xC4D1,0xC4D2,0xC4D3,0xC4D4,/* 0x68-0x6F */
+ 0xC4D5,0xC4D6,0xC4D7,0xC4D8,0xC4D9,0xC4DA,0xC4DB,0xC4DC,/* 0x70-0x77 */
+ 0xC4DD,0xC4DE,0xC4DF,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xC4E0,0xC4E1,0xC4E2,0xC4E3,0xC4E4,0xC4E5,0xC4E6,/* 0x80-0x87 */
+ 0xC4E7,0xC4E8,0xC4EA,0xC4EB,0xC4EC,0xC4ED,0xC4EE,0xC4EF,/* 0x88-0x8F */
+ 0xC4F2,0xC4F3,0xC4F5,0xC4F6,0xC4F7,0xC4F9,0xC4FB,0xC4FC,/* 0x90-0x97 */
+ 0xC4FD,0xC4FE,0xC502,0xC503,0xC504,0xC505,0xC506,0xC507,/* 0x98-0x9F */
+ 0xC508,0xC509,0xC50A,0xC50B,0xC50D,0xC50E,0xC50F,0xC511,/* 0xA0-0xA7 */
+ 0xC512,0xC513,0xC515,0xC516,0xC517,0xC518,0xC519,0xC51A,/* 0xA8-0xAF */
+ 0xC51B,0xC51D,0xC51E,0xC51F,0xC520,0xC521,0xC522,0xC523,/* 0xB0-0xB7 */
+ 0xC524,0xC525,0xC526,0xC527,0xC52A,0xC52B,0xC52D,0xC52E,/* 0xB8-0xBF */
+ 0xC52F,0xC531,0xC532,0xC533,0xC534,0xC535,0xC536,0xC537,/* 0xC0-0xC7 */
+ 0xC53A,0xC53C,0xC53E,0xC53F,0xC540,0xC541,0xC542,0xC543,/* 0xC8-0xCF */
+ 0xC546,0xC547,0xC54B,0xC54F,0xC550,0xC551,0xC552,0xC556,/* 0xD0-0xD7 */
+ 0xC55A,0xC55B,0xC55C,0xC55F,0xC562,0xC563,0xC565,0xC566,/* 0xD8-0xDF */
+ 0xC567,0xC569,0xC56A,0xC56B,0xC56C,0xC56D,0xC56E,0xC56F,/* 0xE0-0xE7 */
+ 0xC572,0xC576,0xC577,0xC578,0xC579,0xC57A,0xC57B,0xC57E,/* 0xE8-0xEF */
+ 0xC57F,0xC581,0xC582,0xC583,0xC585,0xC586,0xC588,0xC589,/* 0xF0-0xF7 */
+ 0xC58A,0xC58B,0xC58E,0xC590,0xC592,0xC593,0xC594,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_9E[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xC596,0xC599,0xC59A,0xC59B,0xC59D,0xC59E,0xC59F,/* 0x40-0x47 */
+ 0xC5A1,0xC5A2,0xC5A3,0xC5A4,0xC5A5,0xC5A6,0xC5A7,0xC5A8,/* 0x48-0x4F */
+ 0xC5AA,0xC5AB,0xC5AC,0xC5AD,0xC5AE,0xC5AF,0xC5B0,0xC5B1,/* 0x50-0x57 */
+ 0xC5B2,0xC5B3,0xC5B6,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xC5B7,0xC5BA,0xC5BF,0xC5C0,0xC5C1,0xC5C2,0xC5C3,/* 0x60-0x67 */
+ 0xC5CB,0xC5CD,0xC5CF,0xC5D2,0xC5D3,0xC5D5,0xC5D6,0xC5D7,/* 0x68-0x6F */
+ 0xC5D9,0xC5DA,0xC5DB,0xC5DC,0xC5DD,0xC5DE,0xC5DF,0xC5E2,/* 0x70-0x77 */
+ 0xC5E4,0xC5E6,0xC5E7,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xC5E8,0xC5E9,0xC5EA,0xC5EB,0xC5EF,0xC5F1,0xC5F2,/* 0x80-0x87 */
+ 0xC5F3,0xC5F5,0xC5F8,0xC5F9,0xC5FA,0xC5FB,0xC602,0xC603,/* 0x88-0x8F */
+ 0xC604,0xC609,0xC60A,0xC60B,0xC60D,0xC60E,0xC60F,0xC611,/* 0x90-0x97 */
+ 0xC612,0xC613,0xC614,0xC615,0xC616,0xC617,0xC61A,0xC61D,/* 0x98-0x9F */
+ 0xC61E,0xC61F,0xC620,0xC621,0xC622,0xC623,0xC626,0xC627,/* 0xA0-0xA7 */
+ 0xC629,0xC62A,0xC62B,0xC62F,0xC631,0xC632,0xC636,0xC638,/* 0xA8-0xAF */
+ 0xC63A,0xC63C,0xC63D,0xC63E,0xC63F,0xC642,0xC643,0xC645,/* 0xB0-0xB7 */
+ 0xC646,0xC647,0xC649,0xC64A,0xC64B,0xC64C,0xC64D,0xC64E,/* 0xB8-0xBF */
+ 0xC64F,0xC652,0xC656,0xC657,0xC658,0xC659,0xC65A,0xC65B,/* 0xC0-0xC7 */
+ 0xC65E,0xC65F,0xC661,0xC662,0xC663,0xC664,0xC665,0xC666,/* 0xC8-0xCF */
+ 0xC667,0xC668,0xC669,0xC66A,0xC66B,0xC66D,0xC66E,0xC670,/* 0xD0-0xD7 */
+ 0xC672,0xC673,0xC674,0xC675,0xC676,0xC677,0xC67A,0xC67B,/* 0xD8-0xDF */
+ 0xC67D,0xC67E,0xC67F,0xC681,0xC682,0xC683,0xC684,0xC685,/* 0xE0-0xE7 */
+ 0xC686,0xC687,0xC68A,0xC68C,0xC68E,0xC68F,0xC690,0xC691,/* 0xE8-0xEF */
+ 0xC692,0xC693,0xC696,0xC697,0xC699,0xC69A,0xC69B,0xC69D,/* 0xF0-0xF7 */
+ 0xC69E,0xC69F,0xC6A0,0xC6A1,0xC6A2,0xC6A3,0xC6A6,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_9F[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xC6A8,0xC6AA,0xC6AB,0xC6AC,0xC6AD,0xC6AE,0xC6AF,/* 0x40-0x47 */
+ 0xC6B2,0xC6B3,0xC6B5,0xC6B6,0xC6B7,0xC6BB,0xC6BC,0xC6BD,/* 0x48-0x4F */
+ 0xC6BE,0xC6BF,0xC6C2,0xC6C4,0xC6C6,0xC6C7,0xC6C8,0xC6C9,/* 0x50-0x57 */
+ 0xC6CA,0xC6CB,0xC6CE,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xC6CF,0xC6D1,0xC6D2,0xC6D3,0xC6D5,0xC6D6,0xC6D7,/* 0x60-0x67 */
+ 0xC6D8,0xC6D9,0xC6DA,0xC6DB,0xC6DE,0xC6DF,0xC6E2,0xC6E3,/* 0x68-0x6F */
+ 0xC6E4,0xC6E5,0xC6E6,0xC6E7,0xC6EA,0xC6EB,0xC6ED,0xC6EE,/* 0x70-0x77 */
+ 0xC6EF,0xC6F1,0xC6F2,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xC6F3,0xC6F4,0xC6F5,0xC6F6,0xC6F7,0xC6FA,0xC6FB,/* 0x80-0x87 */
+ 0xC6FC,0xC6FE,0xC6FF,0xC700,0xC701,0xC702,0xC703,0xC706,/* 0x88-0x8F */
+ 0xC707,0xC709,0xC70A,0xC70B,0xC70D,0xC70E,0xC70F,0xC710,/* 0x90-0x97 */
+ 0xC711,0xC712,0xC713,0xC716,0xC718,0xC71A,0xC71B,0xC71C,/* 0x98-0x9F */
+ 0xC71D,0xC71E,0xC71F,0xC722,0xC723,0xC725,0xC726,0xC727,/* 0xA0-0xA7 */
+ 0xC729,0xC72A,0xC72B,0xC72C,0xC72D,0xC72E,0xC72F,0xC732,/* 0xA8-0xAF */
+ 0xC734,0xC736,0xC738,0xC739,0xC73A,0xC73B,0xC73E,0xC73F,/* 0xB0-0xB7 */
+ 0xC741,0xC742,0xC743,0xC745,0xC746,0xC747,0xC748,0xC749,/* 0xB8-0xBF */
+ 0xC74B,0xC74E,0xC750,0xC759,0xC75A,0xC75B,0xC75D,0xC75E,/* 0xC0-0xC7 */
+ 0xC75F,0xC761,0xC762,0xC763,0xC764,0xC765,0xC766,0xC767,/* 0xC8-0xCF */
+ 0xC769,0xC76A,0xC76C,0xC76D,0xC76E,0xC76F,0xC770,0xC771,/* 0xD0-0xD7 */
+ 0xC772,0xC773,0xC776,0xC777,0xC779,0xC77A,0xC77B,0xC77F,/* 0xD8-0xDF */
+ 0xC780,0xC781,0xC782,0xC786,0xC78B,0xC78C,0xC78D,0xC78F,/* 0xE0-0xE7 */
+ 0xC792,0xC793,0xC795,0xC799,0xC79B,0xC79C,0xC79D,0xC79E,/* 0xE8-0xEF */
+ 0xC79F,0xC7A2,0xC7A7,0xC7A8,0xC7A9,0xC7AA,0xC7AB,0xC7AE,/* 0xF0-0xF7 */
+ 0xC7AF,0xC7B1,0xC7B2,0xC7B3,0xC7B5,0xC7B6,0xC7B7,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_A0[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xC7B8,0xC7B9,0xC7BA,0xC7BB,0xC7BE,0xC7C2,0xC7C3,/* 0x40-0x47 */
+ 0xC7C4,0xC7C5,0xC7C6,0xC7C7,0xC7CA,0xC7CB,0xC7CD,0xC7CF,/* 0x48-0x4F */
+ 0xC7D1,0xC7D2,0xC7D3,0xC7D4,0xC7D5,0xC7D6,0xC7D7,0xC7D9,/* 0x50-0x57 */
+ 0xC7DA,0xC7DB,0xC7DC,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xC7DE,0xC7DF,0xC7E0,0xC7E1,0xC7E2,0xC7E3,0xC7E5,/* 0x60-0x67 */
+ 0xC7E6,0xC7E7,0xC7E9,0xC7EA,0xC7EB,0xC7ED,0xC7EE,0xC7EF,/* 0x68-0x6F */
+ 0xC7F0,0xC7F1,0xC7F2,0xC7F3,0xC7F4,0xC7F5,0xC7F6,0xC7F7,/* 0x70-0x77 */
+ 0xC7F8,0xC7F9,0xC7FA,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xC7FB,0xC7FC,0xC7FD,0xC7FE,0xC7FF,0xC802,0xC803,/* 0x80-0x87 */
+ 0xC805,0xC806,0xC807,0xC809,0xC80B,0xC80C,0xC80D,0xC80E,/* 0x88-0x8F */
+ 0xC80F,0xC812,0xC814,0xC817,0xC818,0xC819,0xC81A,0xC81B,/* 0x90-0x97 */
+ 0xC81E,0xC81F,0xC821,0xC822,0xC823,0xC825,0xC826,0xC827,/* 0x98-0x9F */
+ 0xC828,0xC829,0xC82A,0xC82B,0xC82E,0xC830,0xC832,0xC833,/* 0xA0-0xA7 */
+ 0xC834,0xC835,0xC836,0xC837,0xC839,0xC83A,0xC83B,0xC83D,/* 0xA8-0xAF */
+ 0xC83E,0xC83F,0xC841,0xC842,0xC843,0xC844,0xC845,0xC846,/* 0xB0-0xB7 */
+ 0xC847,0xC84A,0xC84B,0xC84E,0xC84F,0xC850,0xC851,0xC852,/* 0xB8-0xBF */
+ 0xC853,0xC855,0xC856,0xC857,0xC858,0xC859,0xC85A,0xC85B,/* 0xC0-0xC7 */
+ 0xC85C,0xC85D,0xC85E,0xC85F,0xC860,0xC861,0xC862,0xC863,/* 0xC8-0xCF */
+ 0xC864,0xC865,0xC866,0xC867,0xC868,0xC869,0xC86A,0xC86B,/* 0xD0-0xD7 */
+ 0xC86C,0xC86D,0xC86E,0xC86F,0xC872,0xC873,0xC875,0xC876,/* 0xD8-0xDF */
+ 0xC877,0xC879,0xC87B,0xC87C,0xC87D,0xC87E,0xC87F,0xC882,/* 0xE0-0xE7 */
+ 0xC884,0xC888,0xC889,0xC88A,0xC88E,0xC88F,0xC890,0xC891,/* 0xE8-0xEF */
+ 0xC892,0xC893,0xC895,0xC896,0xC897,0xC898,0xC899,0xC89A,/* 0xF0-0xF7 */
+ 0xC89B,0xC89C,0xC89E,0xC8A0,0xC8A2,0xC8A3,0xC8A4,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_A1[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xC8A5,0xC8A6,0xC8A7,0xC8A9,0xC8AA,0xC8AB,0xC8AC,/* 0x40-0x47 */
+ 0xC8AD,0xC8AE,0xC8AF,0xC8B0,0xC8B1,0xC8B2,0xC8B3,0xC8B4,/* 0x48-0x4F */
+ 0xC8B5,0xC8B6,0xC8B7,0xC8B8,0xC8B9,0xC8BA,0xC8BB,0xC8BE,/* 0x50-0x57 */
+ 0xC8BF,0xC8C0,0xC8C1,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xC8C2,0xC8C3,0xC8C5,0xC8C6,0xC8C7,0xC8C9,0xC8CA,/* 0x60-0x67 */
+ 0xC8CB,0xC8CD,0xC8CE,0xC8CF,0xC8D0,0xC8D1,0xC8D2,0xC8D3,/* 0x68-0x6F */
+ 0xC8D6,0xC8D8,0xC8DA,0xC8DB,0xC8DC,0xC8DD,0xC8DE,0xC8DF,/* 0x70-0x77 */
+ 0xC8E2,0xC8E3,0xC8E5,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xC8E6,0xC8E7,0xC8E8,0xC8E9,0xC8EA,0xC8EB,0xC8EC,/* 0x80-0x87 */
+ 0xC8ED,0xC8EE,0xC8EF,0xC8F0,0xC8F1,0xC8F2,0xC8F3,0xC8F4,/* 0x88-0x8F */
+ 0xC8F6,0xC8F7,0xC8F8,0xC8F9,0xC8FA,0xC8FB,0xC8FE,0xC8FF,/* 0x90-0x97 */
+ 0xC901,0xC902,0xC903,0xC907,0xC908,0xC909,0xC90A,0xC90B,/* 0x98-0x9F */
+ 0xC90E,0x3000,0x3001,0x3002,0x00B7,0x2025,0x2026,0x00A8,/* 0xA0-0xA7 */
+ 0x3003,0x00AD,0x2015,0x2225,0xFF3C,0x223C,0x2018,0x2019,/* 0xA8-0xAF */
+ 0x201C,0x201D,0x3014,0x3015,0x3008,0x3009,0x300A,0x300B,/* 0xB0-0xB7 */
+ 0x300C,0x300D,0x300E,0x300F,0x3010,0x3011,0x00B1,0x00D7,/* 0xB8-0xBF */
+ 0x00F7,0x2260,0x2264,0x2265,0x221E,0x2234,0x00B0,0x2032,/* 0xC0-0xC7 */
+ 0x2033,0x2103,0x212B,0xFFE0,0xFFE1,0xFFE5,0x2642,0x2640,/* 0xC8-0xCF */
+ 0x2220,0x22A5,0x2312,0x2202,0x2207,0x2261,0x2252,0x00A7,/* 0xD0-0xD7 */
+ 0x203B,0x2606,0x2605,0x25CB,0x25CF,0x25CE,0x25C7,0x25C6,/* 0xD8-0xDF */
+ 0x25A1,0x25A0,0x25B3,0x25B2,0x25BD,0x25BC,0x2192,0x2190,/* 0xE0-0xE7 */
+ 0x2191,0x2193,0x2194,0x3013,0x226A,0x226B,0x221A,0x223D,/* 0xE8-0xEF */
+ 0x221D,0x2235,0x222B,0x222C,0x2208,0x220B,0x2286,0x2287,/* 0xF0-0xF7 */
+ 0x2282,0x2283,0x222A,0x2229,0x2227,0x2228,0xFFE2,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_A2[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xC910,0xC912,0xC913,0xC914,0xC915,0xC916,0xC917,/* 0x40-0x47 */
+ 0xC919,0xC91A,0xC91B,0xC91C,0xC91D,0xC91E,0xC91F,0xC920,/* 0x48-0x4F */
+ 0xC921,0xC922,0xC923,0xC924,0xC925,0xC926,0xC927,0xC928,/* 0x50-0x57 */
+ 0xC929,0xC92A,0xC92B,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xC92D,0xC92E,0xC92F,0xC930,0xC931,0xC932,0xC933,/* 0x60-0x67 */
+ 0xC935,0xC936,0xC937,0xC938,0xC939,0xC93A,0xC93B,0xC93C,/* 0x68-0x6F */
+ 0xC93D,0xC93E,0xC93F,0xC940,0xC941,0xC942,0xC943,0xC944,/* 0x70-0x77 */
+ 0xC945,0xC946,0xC947,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xC948,0xC949,0xC94A,0xC94B,0xC94C,0xC94D,0xC94E,/* 0x80-0x87 */
+ 0xC94F,0xC952,0xC953,0xC955,0xC956,0xC957,0xC959,0xC95A,/* 0x88-0x8F */
+ 0xC95B,0xC95C,0xC95D,0xC95E,0xC95F,0xC962,0xC964,0xC965,/* 0x90-0x97 */
+ 0xC966,0xC967,0xC968,0xC969,0xC96A,0xC96B,0xC96D,0xC96E,/* 0x98-0x9F */
+ 0xC96F,0x21D2,0x21D4,0x2200,0x2203,0x00B4,0xFF5E,0x02C7,/* 0xA0-0xA7 */
+ 0x02D8,0x02DD,0x02DA,0x02D9,0x00B8,0x02DB,0x00A1,0x00BF,/* 0xA8-0xAF */
+ 0x02D0,0x222E,0x2211,0x220F,0x00A4,0x2109,0x2030,0x25C1,/* 0xB0-0xB7 */
+ 0x25C0,0x25B7,0x25B6,0x2664,0x2660,0x2661,0x2665,0x2667,/* 0xB8-0xBF */
+ 0x2663,0x2299,0x25C8,0x25A3,0x25D0,0x25D1,0x2592,0x25A4,/* 0xC0-0xC7 */
+ 0x25A5,0x25A8,0x25A7,0x25A6,0x25A9,0x2668,0x260F,0x260E,/* 0xC8-0xCF */
+ 0x261C,0x261E,0x00B6,0x2020,0x2021,0x2195,0x2197,0x2199,/* 0xD0-0xD7 */
+ 0x2196,0x2198,0x266D,0x2669,0x266A,0x266C,0x327F,0x321C,/* 0xD8-0xDF */
+ 0x2116,0x33C7,0x2122,0x33C2,0x33D8,0x2121,0x0000,0x0000,/* 0xE0-0xE7 */
+};
+
+static wchar_t c2u_A3[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xC971,0xC972,0xC973,0xC975,0xC976,0xC977,0xC978,/* 0x40-0x47 */
+ 0xC979,0xC97A,0xC97B,0xC97D,0xC97E,0xC97F,0xC980,0xC981,/* 0x48-0x4F */
+ 0xC982,0xC983,0xC984,0xC985,0xC986,0xC987,0xC98A,0xC98B,/* 0x50-0x57 */
+ 0xC98D,0xC98E,0xC98F,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xC991,0xC992,0xC993,0xC994,0xC995,0xC996,0xC997,/* 0x60-0x67 */
+ 0xC99A,0xC99C,0xC99E,0xC99F,0xC9A0,0xC9A1,0xC9A2,0xC9A3,/* 0x68-0x6F */
+ 0xC9A4,0xC9A5,0xC9A6,0xC9A7,0xC9A8,0xC9A9,0xC9AA,0xC9AB,/* 0x70-0x77 */
+ 0xC9AC,0xC9AD,0xC9AE,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xC9AF,0xC9B0,0xC9B1,0xC9B2,0xC9B3,0xC9B4,0xC9B5,/* 0x80-0x87 */
+ 0xC9B6,0xC9B7,0xC9B8,0xC9B9,0xC9BA,0xC9BB,0xC9BC,0xC9BD,/* 0x88-0x8F */
+ 0xC9BE,0xC9BF,0xC9C2,0xC9C3,0xC9C5,0xC9C6,0xC9C9,0xC9CB,/* 0x90-0x97 */
+ 0xC9CC,0xC9CD,0xC9CE,0xC9CF,0xC9D2,0xC9D4,0xC9D7,0xC9D8,/* 0x98-0x9F */
+ 0xC9DB,0xFF01,0xFF02,0xFF03,0xFF04,0xFF05,0xFF06,0xFF07,/* 0xA0-0xA7 */
+ 0xFF08,0xFF09,0xFF0A,0xFF0B,0xFF0C,0xFF0D,0xFF0E,0xFF0F,/* 0xA8-0xAF */
+ 0xFF10,0xFF11,0xFF12,0xFF13,0xFF14,0xFF15,0xFF16,0xFF17,/* 0xB0-0xB7 */
+ 0xFF18,0xFF19,0xFF1A,0xFF1B,0xFF1C,0xFF1D,0xFF1E,0xFF1F,/* 0xB8-0xBF */
+ 0xFF20,0xFF21,0xFF22,0xFF23,0xFF24,0xFF25,0xFF26,0xFF27,/* 0xC0-0xC7 */
+ 0xFF28,0xFF29,0xFF2A,0xFF2B,0xFF2C,0xFF2D,0xFF2E,0xFF2F,/* 0xC8-0xCF */
+ 0xFF30,0xFF31,0xFF32,0xFF33,0xFF34,0xFF35,0xFF36,0xFF37,/* 0xD0-0xD7 */
+ 0xFF38,0xFF39,0xFF3A,0xFF3B,0xFFE6,0xFF3D,0xFF3E,0xFF3F,/* 0xD8-0xDF */
+ 0xFF40,0xFF41,0xFF42,0xFF43,0xFF44,0xFF45,0xFF46,0xFF47,/* 0xE0-0xE7 */
+ 0xFF48,0xFF49,0xFF4A,0xFF4B,0xFF4C,0xFF4D,0xFF4E,0xFF4F,/* 0xE8-0xEF */
+ 0xFF50,0xFF51,0xFF52,0xFF53,0xFF54,0xFF55,0xFF56,0xFF57,/* 0xF0-0xF7 */
+ 0xFF58,0xFF59,0xFF5A,0xFF5B,0xFF5C,0xFF5D,0xFFE3,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_A4[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xC9DE,0xC9DF,0xC9E1,0xC9E3,0xC9E5,0xC9E6,0xC9E8,/* 0x40-0x47 */
+ 0xC9E9,0xC9EA,0xC9EB,0xC9EE,0xC9F2,0xC9F3,0xC9F4,0xC9F5,/* 0x48-0x4F */
+ 0xC9F6,0xC9F7,0xC9FA,0xC9FB,0xC9FD,0xC9FE,0xC9FF,0xCA01,/* 0x50-0x57 */
+ 0xCA02,0xCA03,0xCA04,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xCA05,0xCA06,0xCA07,0xCA0A,0xCA0E,0xCA0F,0xCA10,/* 0x60-0x67 */
+ 0xCA11,0xCA12,0xCA13,0xCA15,0xCA16,0xCA17,0xCA19,0xCA1A,/* 0x68-0x6F */
+ 0xCA1B,0xCA1C,0xCA1D,0xCA1E,0xCA1F,0xCA20,0xCA21,0xCA22,/* 0x70-0x77 */
+ 0xCA23,0xCA24,0xCA25,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xCA26,0xCA27,0xCA28,0xCA2A,0xCA2B,0xCA2C,0xCA2D,/* 0x80-0x87 */
+ 0xCA2E,0xCA2F,0xCA30,0xCA31,0xCA32,0xCA33,0xCA34,0xCA35,/* 0x88-0x8F */
+ 0xCA36,0xCA37,0xCA38,0xCA39,0xCA3A,0xCA3B,0xCA3C,0xCA3D,/* 0x90-0x97 */
+ 0xCA3E,0xCA3F,0xCA40,0xCA41,0xCA42,0xCA43,0xCA44,0xCA45,/* 0x98-0x9F */
+ 0xCA46,0xFFA1,0xFFA2,0xFFA3,0xFFA4,0xFFA5,0xFFA6,0xFFA7,/* 0xA0-0xA7 */
+ 0xFFA8,0xFFA9,0xFFAA,0xFFAB,0xFFAC,0xFFAD,0xFFAE,0xFFAF,/* 0xA8-0xAF */
+ 0xFFB0,0xFFB1,0xFFB2,0xFFB3,0xFFB4,0xFFB5,0xFFB6,0xFFB7,/* 0xB0-0xB7 */
+ 0xFFB8,0xFFB9,0xFFBA,0xFFBB,0xFFBC,0xFFBD,0xFFBE,0xFFC2,/* 0xB8-0xBF */
+ 0xFFC3,0xFFC4,0xFFC5,0xFFC6,0xFFC7,0xFFCA,0xFFCB,0xFFCC,/* 0xC0-0xC7 */
+ 0xFFCD,0xFFCE,0xFFCF,0xFFD2,0xFFD3,0xFFD4,0xFFD5,0xFFD6,/* 0xC8-0xCF */
+ 0xFFD7,0xFFDA,0xFFDB,0xFFDC,0xFFA0,0x3165,0x3166,0x3167,/* 0xD0-0xD7 */
+ 0x3168,0x3169,0x316A,0x316B,0x316C,0x316D,0x316E,0x316F,/* 0xD8-0xDF */
+ 0x3170,0x3171,0x3172,0x3173,0x3174,0x3175,0x3176,0x3177,/* 0xE0-0xE7 */
+ 0x3178,0x3179,0x317A,0x317B,0x317C,0x317D,0x317E,0x317F,/* 0xE8-0xEF */
+ 0x3180,0x3181,0x3182,0x3183,0x3184,0x3185,0x3186,0x3187,/* 0xF0-0xF7 */
+ 0x3188,0x3189,0x318A,0x318B,0x318C,0x318D,0x318E,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_A5[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xCA47,0xCA48,0xCA49,0xCA4A,0xCA4B,0xCA4E,0xCA4F,/* 0x40-0x47 */
+ 0xCA51,0xCA52,0xCA53,0xCA55,0xCA56,0xCA57,0xCA58,0xCA59,/* 0x48-0x4F */
+ 0xCA5A,0xCA5B,0xCA5E,0xCA62,0xCA63,0xCA64,0xCA65,0xCA66,/* 0x50-0x57 */
+ 0xCA67,0xCA69,0xCA6A,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xCA6B,0xCA6C,0xCA6D,0xCA6E,0xCA6F,0xCA70,0xCA71,/* 0x60-0x67 */
+ 0xCA72,0xCA73,0xCA74,0xCA75,0xCA76,0xCA77,0xCA78,0xCA79,/* 0x68-0x6F */
+ 0xCA7A,0xCA7B,0xCA7C,0xCA7E,0xCA7F,0xCA80,0xCA81,0xCA82,/* 0x70-0x77 */
+ 0xCA83,0xCA85,0xCA86,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xCA87,0xCA88,0xCA89,0xCA8A,0xCA8B,0xCA8C,0xCA8D,/* 0x80-0x87 */
+ 0xCA8E,0xCA8F,0xCA90,0xCA91,0xCA92,0xCA93,0xCA94,0xCA95,/* 0x88-0x8F */
+ 0xCA96,0xCA97,0xCA99,0xCA9A,0xCA9B,0xCA9C,0xCA9D,0xCA9E,/* 0x90-0x97 */
+ 0xCA9F,0xCAA0,0xCAA1,0xCAA2,0xCAA3,0xCAA4,0xCAA5,0xCAA6,/* 0x98-0x9F */
+ 0xCAA7,0x2170,0x2171,0x2172,0x2173,0x2174,0x2175,0x2176,/* 0xA0-0xA7 */
+ 0x2177,0x2178,0x2179,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xA8-0xAF */
+ 0x2160,0x2161,0x2162,0x2163,0x2164,0x2165,0x2166,0x2167,/* 0xB0-0xB7 */
+ 0x2168,0x2169,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xB8-0xBF */
+ 0x0000,0x0391,0x0392,0x0393,0x0394,0x0395,0x0396,0x0397,/* 0xC0-0xC7 */
+ 0x0398,0x0399,0x039A,0x039B,0x039C,0x039D,0x039E,0x039F,/* 0xC8-0xCF */
+ 0x03A0,0x03A1,0x03A3,0x03A4,0x03A5,0x03A6,0x03A7,0x03A8,/* 0xD0-0xD7 */
+ 0x03A9,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xD8-0xDF */
+ 0x0000,0x03B1,0x03B2,0x03B3,0x03B4,0x03B5,0x03B6,0x03B7,/* 0xE0-0xE7 */
+ 0x03B8,0x03B9,0x03BA,0x03BB,0x03BC,0x03BD,0x03BE,0x03BF,/* 0xE8-0xEF */
+ 0x03C0,0x03C1,0x03C3,0x03C4,0x03C5,0x03C6,0x03C7,0x03C8,/* 0xF0-0xF7 */
+ 0x03C9,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_A6[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xCAA8,0xCAA9,0xCAAA,0xCAAB,0xCAAC,0xCAAD,0xCAAE,/* 0x40-0x47 */
+ 0xCAAF,0xCAB0,0xCAB1,0xCAB2,0xCAB3,0xCAB4,0xCAB5,0xCAB6,/* 0x48-0x4F */
+ 0xCAB7,0xCAB8,0xCAB9,0xCABA,0xCABB,0xCABE,0xCABF,0xCAC1,/* 0x50-0x57 */
+ 0xCAC2,0xCAC3,0xCAC5,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xCAC6,0xCAC7,0xCAC8,0xCAC9,0xCACA,0xCACB,0xCACE,/* 0x60-0x67 */
+ 0xCAD0,0xCAD2,0xCAD4,0xCAD5,0xCAD6,0xCAD7,0xCADA,0xCADB,/* 0x68-0x6F */
+ 0xCADC,0xCADD,0xCADE,0xCADF,0xCAE1,0xCAE2,0xCAE3,0xCAE4,/* 0x70-0x77 */
+ 0xCAE5,0xCAE6,0xCAE7,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xCAE8,0xCAE9,0xCAEA,0xCAEB,0xCAED,0xCAEE,0xCAEF,/* 0x80-0x87 */
+ 0xCAF0,0xCAF1,0xCAF2,0xCAF3,0xCAF5,0xCAF6,0xCAF7,0xCAF8,/* 0x88-0x8F */
+ 0xCAF9,0xCAFA,0xCAFB,0xCAFC,0xCAFD,0xCAFE,0xCAFF,0xCB00,/* 0x90-0x97 */
+ 0xCB01,0xCB02,0xCB03,0xCB04,0xCB05,0xCB06,0xCB07,0xCB09,/* 0x98-0x9F */
+ 0xCB0A,0x2500,0x2502,0x250C,0x2510,0x2518,0x2514,0x251C,/* 0xA0-0xA7 */
+ 0x252C,0x2524,0x2534,0x253C,0x2501,0x2503,0x250F,0x2513,/* 0xA8-0xAF */
+ 0x251B,0x2517,0x2523,0x2533,0x252B,0x253B,0x254B,0x2520,/* 0xB0-0xB7 */
+ 0x252F,0x2528,0x2537,0x253F,0x251D,0x2530,0x2525,0x2538,/* 0xB8-0xBF */
+ 0x2542,0x2512,0x2511,0x251A,0x2519,0x2516,0x2515,0x250E,/* 0xC0-0xC7 */
+ 0x250D,0x251E,0x251F,0x2521,0x2522,0x2526,0x2527,0x2529,/* 0xC8-0xCF */
+ 0x252A,0x252D,0x252E,0x2531,0x2532,0x2535,0x2536,0x2539,/* 0xD0-0xD7 */
+ 0x253A,0x253D,0x253E,0x2540,0x2541,0x2543,0x2544,0x2545,/* 0xD8-0xDF */
+ 0x2546,0x2547,0x2548,0x2549,0x254A,0x0000,0x0000,0x0000,/* 0xE0-0xE7 */
+};
+
+static wchar_t c2u_A7[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xCB0B,0xCB0C,0xCB0D,0xCB0E,0xCB0F,0xCB11,0xCB12,/* 0x40-0x47 */
+ 0xCB13,0xCB15,0xCB16,0xCB17,0xCB19,0xCB1A,0xCB1B,0xCB1C,/* 0x48-0x4F */
+ 0xCB1D,0xCB1E,0xCB1F,0xCB22,0xCB23,0xCB24,0xCB25,0xCB26,/* 0x50-0x57 */
+ 0xCB27,0xCB28,0xCB29,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xCB2A,0xCB2B,0xCB2C,0xCB2D,0xCB2E,0xCB2F,0xCB30,/* 0x60-0x67 */
+ 0xCB31,0xCB32,0xCB33,0xCB34,0xCB35,0xCB36,0xCB37,0xCB38,/* 0x68-0x6F */
+ 0xCB39,0xCB3A,0xCB3B,0xCB3C,0xCB3D,0xCB3E,0xCB3F,0xCB40,/* 0x70-0x77 */
+ 0xCB42,0xCB43,0xCB44,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xCB45,0xCB46,0xCB47,0xCB4A,0xCB4B,0xCB4D,0xCB4E,/* 0x80-0x87 */
+ 0xCB4F,0xCB51,0xCB52,0xCB53,0xCB54,0xCB55,0xCB56,0xCB57,/* 0x88-0x8F */
+ 0xCB5A,0xCB5B,0xCB5C,0xCB5E,0xCB5F,0xCB60,0xCB61,0xCB62,/* 0x90-0x97 */
+ 0xCB63,0xCB65,0xCB66,0xCB67,0xCB68,0xCB69,0xCB6A,0xCB6B,/* 0x98-0x9F */
+ 0xCB6C,0x3395,0x3396,0x3397,0x2113,0x3398,0x33C4,0x33A3,/* 0xA0-0xA7 */
+ 0x33A4,0x33A5,0x33A6,0x3399,0x339A,0x339B,0x339C,0x339D,/* 0xA8-0xAF */
+ 0x339E,0x339F,0x33A0,0x33A1,0x33A2,0x33CA,0x338D,0x338E,/* 0xB0-0xB7 */
+ 0x338F,0x33CF,0x3388,0x3389,0x33C8,0x33A7,0x33A8,0x33B0,/* 0xB8-0xBF */
+ 0x33B1,0x33B2,0x33B3,0x33B4,0x33B5,0x33B6,0x33B7,0x33B8,/* 0xC0-0xC7 */
+ 0x33B9,0x3380,0x3381,0x3382,0x3383,0x3384,0x33BA,0x33BB,/* 0xC8-0xCF */
+ 0x33BC,0x33BD,0x33BE,0x33BF,0x3390,0x3391,0x3392,0x3393,/* 0xD0-0xD7 */
+ 0x3394,0x2126,0x33C0,0x33C1,0x338A,0x338B,0x338C,0x33D6,/* 0xD8-0xDF */
+ 0x33C5,0x33AD,0x33AE,0x33AF,0x33DB,0x33A9,0x33AA,0x33AB,/* 0xE0-0xE7 */
+ 0x33AC,0x33DD,0x33D0,0x33D3,0x33C3,0x33C9,0x33DC,0x33C6,/* 0xE8-0xEF */
+};
+
+static wchar_t c2u_A8[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xCB6D,0xCB6E,0xCB6F,0xCB70,0xCB71,0xCB72,0xCB73,/* 0x40-0x47 */
+ 0xCB74,0xCB75,0xCB76,0xCB77,0xCB7A,0xCB7B,0xCB7C,0xCB7D,/* 0x48-0x4F */
+ 0xCB7E,0xCB7F,0xCB80,0xCB81,0xCB82,0xCB83,0xCB84,0xCB85,/* 0x50-0x57 */
+ 0xCB86,0xCB87,0xCB88,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xCB89,0xCB8A,0xCB8B,0xCB8C,0xCB8D,0xCB8E,0xCB8F,/* 0x60-0x67 */
+ 0xCB90,0xCB91,0xCB92,0xCB93,0xCB94,0xCB95,0xCB96,0xCB97,/* 0x68-0x6F */
+ 0xCB98,0xCB99,0xCB9A,0xCB9B,0xCB9D,0xCB9E,0xCB9F,0xCBA0,/* 0x70-0x77 */
+ 0xCBA1,0xCBA2,0xCBA3,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xCBA4,0xCBA5,0xCBA6,0xCBA7,0xCBA8,0xCBA9,0xCBAA,/* 0x80-0x87 */
+ 0xCBAB,0xCBAC,0xCBAD,0xCBAE,0xCBAF,0xCBB0,0xCBB1,0xCBB2,/* 0x88-0x8F */
+ 0xCBB3,0xCBB4,0xCBB5,0xCBB6,0xCBB7,0xCBB9,0xCBBA,0xCBBB,/* 0x90-0x97 */
+ 0xCBBC,0xCBBD,0xCBBE,0xCBBF,0xCBC0,0xCBC1,0xCBC2,0xCBC3,/* 0x98-0x9F */
+ 0xCBC4,0x00C6,0x00D0,0x00AA,0x0126,0x0000,0x0132,0x0000,/* 0xA0-0xA7 */
+ 0x013F,0x0141,0x00D8,0x0152,0x00BA,0x00DE,0x0166,0x014A,/* 0xA8-0xAF */
+ 0x0000,0x3260,0x3261,0x3262,0x3263,0x3264,0x3265,0x3266,/* 0xB0-0xB7 */
+ 0x3267,0x3268,0x3269,0x326A,0x326B,0x326C,0x326D,0x326E,/* 0xB8-0xBF */
+ 0x326F,0x3270,0x3271,0x3272,0x3273,0x3274,0x3275,0x3276,/* 0xC0-0xC7 */
+ 0x3277,0x3278,0x3279,0x327A,0x327B,0x24D0,0x24D1,0x24D2,/* 0xC8-0xCF */
+ 0x24D3,0x24D4,0x24D5,0x24D6,0x24D7,0x24D8,0x24D9,0x24DA,/* 0xD0-0xD7 */
+ 0x24DB,0x24DC,0x24DD,0x24DE,0x24DF,0x24E0,0x24E1,0x24E2,/* 0xD8-0xDF */
+ 0x24E3,0x24E4,0x24E5,0x24E6,0x24E7,0x24E8,0x24E9,0x2460,/* 0xE0-0xE7 */
+ 0x2461,0x2462,0x2463,0x2464,0x2465,0x2466,0x2467,0x2468,/* 0xE8-0xEF */
+ 0x2469,0x246A,0x246B,0x246C,0x246D,0x246E,0x00BD,0x2153,/* 0xF0-0xF7 */
+ 0x2154,0x00BC,0x00BE,0x215B,0x215C,0x215D,0x215E,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_A9[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xCBC5,0xCBC6,0xCBC7,0xCBC8,0xCBC9,0xCBCA,0xCBCB,/* 0x40-0x47 */
+ 0xCBCC,0xCBCD,0xCBCE,0xCBCF,0xCBD0,0xCBD1,0xCBD2,0xCBD3,/* 0x48-0x4F */
+ 0xCBD5,0xCBD6,0xCBD7,0xCBD8,0xCBD9,0xCBDA,0xCBDB,0xCBDC,/* 0x50-0x57 */
+ 0xCBDD,0xCBDE,0xCBDF,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xCBE0,0xCBE1,0xCBE2,0xCBE3,0xCBE5,0xCBE6,0xCBE8,/* 0x60-0x67 */
+ 0xCBEA,0xCBEB,0xCBEC,0xCBED,0xCBEE,0xCBEF,0xCBF0,0xCBF1,/* 0x68-0x6F */
+ 0xCBF2,0xCBF3,0xCBF4,0xCBF5,0xCBF6,0xCBF7,0xCBF8,0xCBF9,/* 0x70-0x77 */
+ 0xCBFA,0xCBFB,0xCBFC,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xCBFD,0xCBFE,0xCBFF,0xCC00,0xCC01,0xCC02,0xCC03,/* 0x80-0x87 */
+ 0xCC04,0xCC05,0xCC06,0xCC07,0xCC08,0xCC09,0xCC0A,0xCC0B,/* 0x88-0x8F */
+ 0xCC0E,0xCC0F,0xCC11,0xCC12,0xCC13,0xCC15,0xCC16,0xCC17,/* 0x90-0x97 */
+ 0xCC18,0xCC19,0xCC1A,0xCC1B,0xCC1E,0xCC1F,0xCC20,0xCC23,/* 0x98-0x9F */
+ 0xCC24,0x00E6,0x0111,0x00F0,0x0127,0x0131,0x0133,0x0138,/* 0xA0-0xA7 */
+ 0x0140,0x0142,0x00F8,0x0153,0x00DF,0x00FE,0x0167,0x014B,/* 0xA8-0xAF */
+ 0x0149,0x3200,0x3201,0x3202,0x3203,0x3204,0x3205,0x3206,/* 0xB0-0xB7 */
+ 0x3207,0x3208,0x3209,0x320A,0x320B,0x320C,0x320D,0x320E,/* 0xB8-0xBF */
+ 0x320F,0x3210,0x3211,0x3212,0x3213,0x3214,0x3215,0x3216,/* 0xC0-0xC7 */
+ 0x3217,0x3218,0x3219,0x321A,0x321B,0x249C,0x249D,0x249E,/* 0xC8-0xCF */
+ 0x249F,0x24A0,0x24A1,0x24A2,0x24A3,0x24A4,0x24A5,0x24A6,/* 0xD0-0xD7 */
+ 0x24A7,0x24A8,0x24A9,0x24AA,0x24AB,0x24AC,0x24AD,0x24AE,/* 0xD8-0xDF */
+ 0x24AF,0x24B0,0x24B1,0x24B2,0x24B3,0x24B4,0x24B5,0x2474,/* 0xE0-0xE7 */
+ 0x2475,0x2476,0x2477,0x2478,0x2479,0x247A,0x247B,0x247C,/* 0xE8-0xEF */
+ 0x247D,0x247E,0x247F,0x2480,0x2481,0x2482,0x00B9,0x00B2,/* 0xF0-0xF7 */
+ 0x00B3,0x2074,0x207F,0x2081,0x2082,0x2083,0x2084,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_AA[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xCC25,0xCC26,0xCC2A,0xCC2B,0xCC2D,0xCC2F,0xCC31,/* 0x40-0x47 */
+ 0xCC32,0xCC33,0xCC34,0xCC35,0xCC36,0xCC37,0xCC3A,0xCC3F,/* 0x48-0x4F */
+ 0xCC40,0xCC41,0xCC42,0xCC43,0xCC46,0xCC47,0xCC49,0xCC4A,/* 0x50-0x57 */
+ 0xCC4B,0xCC4D,0xCC4E,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xCC4F,0xCC50,0xCC51,0xCC52,0xCC53,0xCC56,0xCC5A,/* 0x60-0x67 */
+ 0xCC5B,0xCC5C,0xCC5D,0xCC5E,0xCC5F,0xCC61,0xCC62,0xCC63,/* 0x68-0x6F */
+ 0xCC65,0xCC67,0xCC69,0xCC6A,0xCC6B,0xCC6C,0xCC6D,0xCC6E,/* 0x70-0x77 */
+ 0xCC6F,0xCC71,0xCC72,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xCC73,0xCC74,0xCC76,0xCC77,0xCC78,0xCC79,0xCC7A,/* 0x80-0x87 */
+ 0xCC7B,0xCC7C,0xCC7D,0xCC7E,0xCC7F,0xCC80,0xCC81,0xCC82,/* 0x88-0x8F */
+ 0xCC83,0xCC84,0xCC85,0xCC86,0xCC87,0xCC88,0xCC89,0xCC8A,/* 0x90-0x97 */
+ 0xCC8B,0xCC8C,0xCC8D,0xCC8E,0xCC8F,0xCC90,0xCC91,0xCC92,/* 0x98-0x9F */
+ 0xCC93,0x3041,0x3042,0x3043,0x3044,0x3045,0x3046,0x3047,/* 0xA0-0xA7 */
+ 0x3048,0x3049,0x304A,0x304B,0x304C,0x304D,0x304E,0x304F,/* 0xA8-0xAF */
+ 0x3050,0x3051,0x3052,0x3053,0x3054,0x3055,0x3056,0x3057,/* 0xB0-0xB7 */
+ 0x3058,0x3059,0x305A,0x305B,0x305C,0x305D,0x305E,0x305F,/* 0xB8-0xBF */
+ 0x3060,0x3061,0x3062,0x3063,0x3064,0x3065,0x3066,0x3067,/* 0xC0-0xC7 */
+ 0x3068,0x3069,0x306A,0x306B,0x306C,0x306D,0x306E,0x306F,/* 0xC8-0xCF */
+ 0x3070,0x3071,0x3072,0x3073,0x3074,0x3075,0x3076,0x3077,/* 0xD0-0xD7 */
+ 0x3078,0x3079,0x307A,0x307B,0x307C,0x307D,0x307E,0x307F,/* 0xD8-0xDF */
+ 0x3080,0x3081,0x3082,0x3083,0x3084,0x3085,0x3086,0x3087,/* 0xE0-0xE7 */
+ 0x3088,0x3089,0x308A,0x308B,0x308C,0x308D,0x308E,0x308F,/* 0xE8-0xEF */
+ 0x3090,0x3091,0x3092,0x3093,0x0000,0x0000,0x0000,0x0000,/* 0xF0-0xF7 */
+};
+
+static wchar_t c2u_AB[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xCC94,0xCC95,0xCC96,0xCC97,0xCC9A,0xCC9B,0xCC9D,/* 0x40-0x47 */
+ 0xCC9E,0xCC9F,0xCCA1,0xCCA2,0xCCA3,0xCCA4,0xCCA5,0xCCA6,/* 0x48-0x4F */
+ 0xCCA7,0xCCAA,0xCCAE,0xCCAF,0xCCB0,0xCCB1,0xCCB2,0xCCB3,/* 0x50-0x57 */
+ 0xCCB6,0xCCB7,0xCCB9,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xCCBA,0xCCBB,0xCCBD,0xCCBE,0xCCBF,0xCCC0,0xCCC1,/* 0x60-0x67 */
+ 0xCCC2,0xCCC3,0xCCC6,0xCCC8,0xCCCA,0xCCCB,0xCCCC,0xCCCD,/* 0x68-0x6F */
+ 0xCCCE,0xCCCF,0xCCD1,0xCCD2,0xCCD3,0xCCD5,0xCCD6,0xCCD7,/* 0x70-0x77 */
+ 0xCCD8,0xCCD9,0xCCDA,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xCCDB,0xCCDC,0xCCDD,0xCCDE,0xCCDF,0xCCE0,0xCCE1,/* 0x80-0x87 */
+ 0xCCE2,0xCCE3,0xCCE5,0xCCE6,0xCCE7,0xCCE8,0xCCE9,0xCCEA,/* 0x88-0x8F */
+ 0xCCEB,0xCCED,0xCCEE,0xCCEF,0xCCF1,0xCCF2,0xCCF3,0xCCF4,/* 0x90-0x97 */
+ 0xCCF5,0xCCF6,0xCCF7,0xCCF8,0xCCF9,0xCCFA,0xCCFB,0xCCFC,/* 0x98-0x9F */
+ 0xCCFD,0x30A1,0x30A2,0x30A3,0x30A4,0x30A5,0x30A6,0x30A7,/* 0xA0-0xA7 */
+ 0x30A8,0x30A9,0x30AA,0x30AB,0x30AC,0x30AD,0x30AE,0x30AF,/* 0xA8-0xAF */
+ 0x30B0,0x30B1,0x30B2,0x30B3,0x30B4,0x30B5,0x30B6,0x30B7,/* 0xB0-0xB7 */
+ 0x30B8,0x30B9,0x30BA,0x30BB,0x30BC,0x30BD,0x30BE,0x30BF,/* 0xB8-0xBF */
+ 0x30C0,0x30C1,0x30C2,0x30C3,0x30C4,0x30C5,0x30C6,0x30C7,/* 0xC0-0xC7 */
+ 0x30C8,0x30C9,0x30CA,0x30CB,0x30CC,0x30CD,0x30CE,0x30CF,/* 0xC8-0xCF */
+ 0x30D0,0x30D1,0x30D2,0x30D3,0x30D4,0x30D5,0x30D6,0x30D7,/* 0xD0-0xD7 */
+ 0x30D8,0x30D9,0x30DA,0x30DB,0x30DC,0x30DD,0x30DE,0x30DF,/* 0xD8-0xDF */
+ 0x30E0,0x30E1,0x30E2,0x30E3,0x30E4,0x30E5,0x30E6,0x30E7,/* 0xE0-0xE7 */
+ 0x30E8,0x30E9,0x30EA,0x30EB,0x30EC,0x30ED,0x30EE,0x30EF,/* 0xE8-0xEF */
+ 0x30F0,0x30F1,0x30F2,0x30F3,0x30F4,0x30F5,0x30F6,0x0000,/* 0xF0-0xF7 */
+};
+
+static wchar_t c2u_AC[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xCCFE,0xCCFF,0xCD00,0xCD02,0xCD03,0xCD04,0xCD05,/* 0x40-0x47 */
+ 0xCD06,0xCD07,0xCD0A,0xCD0B,0xCD0D,0xCD0E,0xCD0F,0xCD11,/* 0x48-0x4F */
+ 0xCD12,0xCD13,0xCD14,0xCD15,0xCD16,0xCD17,0xCD1A,0xCD1C,/* 0x50-0x57 */
+ 0xCD1E,0xCD1F,0xCD20,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xCD21,0xCD22,0xCD23,0xCD25,0xCD26,0xCD27,0xCD29,/* 0x60-0x67 */
+ 0xCD2A,0xCD2B,0xCD2D,0xCD2E,0xCD2F,0xCD30,0xCD31,0xCD32,/* 0x68-0x6F */
+ 0xCD33,0xCD34,0xCD35,0xCD36,0xCD37,0xCD38,0xCD3A,0xCD3B,/* 0x70-0x77 */
+ 0xCD3C,0xCD3D,0xCD3E,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xCD3F,0xCD40,0xCD41,0xCD42,0xCD43,0xCD44,0xCD45,/* 0x80-0x87 */
+ 0xCD46,0xCD47,0xCD48,0xCD49,0xCD4A,0xCD4B,0xCD4C,0xCD4D,/* 0x88-0x8F */
+ 0xCD4E,0xCD4F,0xCD50,0xCD51,0xCD52,0xCD53,0xCD54,0xCD55,/* 0x90-0x97 */
+ 0xCD56,0xCD57,0xCD58,0xCD59,0xCD5A,0xCD5B,0xCD5D,0xCD5E,/* 0x98-0x9F */
+ 0xCD5F,0x0410,0x0411,0x0412,0x0413,0x0414,0x0415,0x0401,/* 0xA0-0xA7 */
+ 0x0416,0x0417,0x0418,0x0419,0x041A,0x041B,0x041C,0x041D,/* 0xA8-0xAF */
+ 0x041E,0x041F,0x0420,0x0421,0x0422,0x0423,0x0424,0x0425,/* 0xB0-0xB7 */
+ 0x0426,0x0427,0x0428,0x0429,0x042A,0x042B,0x042C,0x042D,/* 0xB8-0xBF */
+ 0x042E,0x042F,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xC0-0xC7 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xC8-0xCF */
+ 0x0000,0x0430,0x0431,0x0432,0x0433,0x0434,0x0435,0x0451,/* 0xD0-0xD7 */
+ 0x0436,0x0437,0x0438,0x0439,0x043A,0x043B,0x043C,0x043D,/* 0xD8-0xDF */
+ 0x043E,0x043F,0x0440,0x0441,0x0442,0x0443,0x0444,0x0445,/* 0xE0-0xE7 */
+ 0x0446,0x0447,0x0448,0x0449,0x044A,0x044B,0x044C,0x044D,/* 0xE8-0xEF */
+ 0x044E,0x044F,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xF0-0xF7 */
+};
+
+static wchar_t c2u_AD[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xCD61,0xCD62,0xCD63,0xCD65,0xCD66,0xCD67,0xCD68,/* 0x40-0x47 */
+ 0xCD69,0xCD6A,0xCD6B,0xCD6E,0xCD70,0xCD72,0xCD73,0xCD74,/* 0x48-0x4F */
+ 0xCD75,0xCD76,0xCD77,0xCD79,0xCD7A,0xCD7B,0xCD7C,0xCD7D,/* 0x50-0x57 */
+ 0xCD7E,0xCD7F,0xCD80,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xCD81,0xCD82,0xCD83,0xCD84,0xCD85,0xCD86,0xCD87,/* 0x60-0x67 */
+ 0xCD89,0xCD8A,0xCD8B,0xCD8C,0xCD8D,0xCD8E,0xCD8F,0xCD90,/* 0x68-0x6F */
+ 0xCD91,0xCD92,0xCD93,0xCD96,0xCD97,0xCD99,0xCD9A,0xCD9B,/* 0x70-0x77 */
+ 0xCD9D,0xCD9E,0xCD9F,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xCDA0,0xCDA1,0xCDA2,0xCDA3,0xCDA6,0xCDA8,0xCDAA,/* 0x80-0x87 */
+ 0xCDAB,0xCDAC,0xCDAD,0xCDAE,0xCDAF,0xCDB1,0xCDB2,0xCDB3,/* 0x88-0x8F */
+ 0xCDB4,0xCDB5,0xCDB6,0xCDB7,0xCDB8,0xCDB9,0xCDBA,0xCDBB,/* 0x90-0x97 */
+ 0xCDBC,0xCDBD,0xCDBE,0xCDBF,0xCDC0,0xCDC1,0xCDC2,0xCDC3,/* 0x98-0x9F */
+ 0xCDC5,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xA0-0xA7 */
+};
+
+static wchar_t c2u_AE[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xCDC6,0xCDC7,0xCDC8,0xCDC9,0xCDCA,0xCDCB,0xCDCD,/* 0x40-0x47 */
+ 0xCDCE,0xCDCF,0xCDD1,0xCDD2,0xCDD3,0xCDD4,0xCDD5,0xCDD6,/* 0x48-0x4F */
+ 0xCDD7,0xCDD8,0xCDD9,0xCDDA,0xCDDB,0xCDDC,0xCDDD,0xCDDE,/* 0x50-0x57 */
+ 0xCDDF,0xCDE0,0xCDE1,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xCDE2,0xCDE3,0xCDE4,0xCDE5,0xCDE6,0xCDE7,0xCDE9,/* 0x60-0x67 */
+ 0xCDEA,0xCDEB,0xCDED,0xCDEE,0xCDEF,0xCDF1,0xCDF2,0xCDF3,/* 0x68-0x6F */
+ 0xCDF4,0xCDF5,0xCDF6,0xCDF7,0xCDFA,0xCDFC,0xCDFE,0xCDFF,/* 0x70-0x77 */
+ 0xCE00,0xCE01,0xCE02,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xCE03,0xCE05,0xCE06,0xCE07,0xCE09,0xCE0A,0xCE0B,/* 0x80-0x87 */
+ 0xCE0D,0xCE0E,0xCE0F,0xCE10,0xCE11,0xCE12,0xCE13,0xCE15,/* 0x88-0x8F */
+ 0xCE16,0xCE17,0xCE18,0xCE1A,0xCE1B,0xCE1C,0xCE1D,0xCE1E,/* 0x90-0x97 */
+ 0xCE1F,0xCE22,0xCE23,0xCE25,0xCE26,0xCE27,0xCE29,0xCE2A,/* 0x98-0x9F */
+ 0xCE2B,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xA0-0xA7 */
+};
+
+static wchar_t c2u_AF[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xCE2C,0xCE2D,0xCE2E,0xCE2F,0xCE32,0xCE34,0xCE36,/* 0x40-0x47 */
+ 0xCE37,0xCE38,0xCE39,0xCE3A,0xCE3B,0xCE3C,0xCE3D,0xCE3E,/* 0x48-0x4F */
+ 0xCE3F,0xCE40,0xCE41,0xCE42,0xCE43,0xCE44,0xCE45,0xCE46,/* 0x50-0x57 */
+ 0xCE47,0xCE48,0xCE49,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xCE4A,0xCE4B,0xCE4C,0xCE4D,0xCE4E,0xCE4F,0xCE50,/* 0x60-0x67 */
+ 0xCE51,0xCE52,0xCE53,0xCE54,0xCE55,0xCE56,0xCE57,0xCE5A,/* 0x68-0x6F */
+ 0xCE5B,0xCE5D,0xCE5E,0xCE62,0xCE63,0xCE64,0xCE65,0xCE66,/* 0x70-0x77 */
+ 0xCE67,0xCE6A,0xCE6C,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xCE6E,0xCE6F,0xCE70,0xCE71,0xCE72,0xCE73,0xCE76,/* 0x80-0x87 */
+ 0xCE77,0xCE79,0xCE7A,0xCE7B,0xCE7D,0xCE7E,0xCE7F,0xCE80,/* 0x88-0x8F */
+ 0xCE81,0xCE82,0xCE83,0xCE86,0xCE88,0xCE8A,0xCE8B,0xCE8C,/* 0x90-0x97 */
+ 0xCE8D,0xCE8E,0xCE8F,0xCE92,0xCE93,0xCE95,0xCE96,0xCE97,/* 0x98-0x9F */
+ 0xCE99,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xA0-0xA7 */
+};
+
+static wchar_t c2u_B0[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xCE9A,0xCE9B,0xCE9C,0xCE9D,0xCE9E,0xCE9F,0xCEA2,/* 0x40-0x47 */
+ 0xCEA6,0xCEA7,0xCEA8,0xCEA9,0xCEAA,0xCEAB,0xCEAE,0xCEAF,/* 0x48-0x4F */
+ 0xCEB0,0xCEB1,0xCEB2,0xCEB3,0xCEB4,0xCEB5,0xCEB6,0xCEB7,/* 0x50-0x57 */
+ 0xCEB8,0xCEB9,0xCEBA,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xCEBB,0xCEBC,0xCEBD,0xCEBE,0xCEBF,0xCEC0,0xCEC2,/* 0x60-0x67 */
+ 0xCEC3,0xCEC4,0xCEC5,0xCEC6,0xCEC7,0xCEC8,0xCEC9,0xCECA,/* 0x68-0x6F */
+ 0xCECB,0xCECC,0xCECD,0xCECE,0xCECF,0xCED0,0xCED1,0xCED2,/* 0x70-0x77 */
+ 0xCED3,0xCED4,0xCED5,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xCED6,0xCED7,0xCED8,0xCED9,0xCEDA,0xCEDB,0xCEDC,/* 0x80-0x87 */
+ 0xCEDD,0xCEDE,0xCEDF,0xCEE0,0xCEE1,0xCEE2,0xCEE3,0xCEE6,/* 0x88-0x8F */
+ 0xCEE7,0xCEE9,0xCEEA,0xCEED,0xCEEE,0xCEEF,0xCEF0,0xCEF1,/* 0x90-0x97 */
+ 0xCEF2,0xCEF3,0xCEF6,0xCEFA,0xCEFB,0xCEFC,0xCEFD,0xCEFE,/* 0x98-0x9F */
+ 0xCEFF,0xAC00,0xAC01,0xAC04,0xAC07,0xAC08,0xAC09,0xAC0A,/* 0xA0-0xA7 */
+ 0xAC10,0xAC11,0xAC12,0xAC13,0xAC14,0xAC15,0xAC16,0xAC17,/* 0xA8-0xAF */
+ 0xAC19,0xAC1A,0xAC1B,0xAC1C,0xAC1D,0xAC20,0xAC24,0xAC2C,/* 0xB0-0xB7 */
+ 0xAC2D,0xAC2F,0xAC30,0xAC31,0xAC38,0xAC39,0xAC3C,0xAC40,/* 0xB8-0xBF */
+ 0xAC4B,0xAC4D,0xAC54,0xAC58,0xAC5C,0xAC70,0xAC71,0xAC74,/* 0xC0-0xC7 */
+ 0xAC77,0xAC78,0xAC7A,0xAC80,0xAC81,0xAC83,0xAC84,0xAC85,/* 0xC8-0xCF */
+ 0xAC86,0xAC89,0xAC8A,0xAC8B,0xAC8C,0xAC90,0xAC94,0xAC9C,/* 0xD0-0xD7 */
+ 0xAC9D,0xAC9F,0xACA0,0xACA1,0xACA8,0xACA9,0xACAA,0xACAC,/* 0xD8-0xDF */
+ 0xACAF,0xACB0,0xACB8,0xACB9,0xACBB,0xACBC,0xACBD,0xACC1,/* 0xE0-0xE7 */
+ 0xACC4,0xACC8,0xACCC,0xACD5,0xACD7,0xACE0,0xACE1,0xACE4,/* 0xE8-0xEF */
+ 0xACE7,0xACE8,0xACEA,0xACEC,0xACEF,0xACF0,0xACF1,0xACF3,/* 0xF0-0xF7 */
+ 0xACF5,0xACF6,0xACFC,0xACFD,0xAD00,0xAD04,0xAD06,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_B1[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xCF02,0xCF03,0xCF05,0xCF06,0xCF07,0xCF09,0xCF0A,/* 0x40-0x47 */
+ 0xCF0B,0xCF0C,0xCF0D,0xCF0E,0xCF0F,0xCF12,0xCF14,0xCF16,/* 0x48-0x4F */
+ 0xCF17,0xCF18,0xCF19,0xCF1A,0xCF1B,0xCF1D,0xCF1E,0xCF1F,/* 0x50-0x57 */
+ 0xCF21,0xCF22,0xCF23,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xCF25,0xCF26,0xCF27,0xCF28,0xCF29,0xCF2A,0xCF2B,/* 0x60-0x67 */
+ 0xCF2E,0xCF32,0xCF33,0xCF34,0xCF35,0xCF36,0xCF37,0xCF39,/* 0x68-0x6F */
+ 0xCF3A,0xCF3B,0xCF3C,0xCF3D,0xCF3E,0xCF3F,0xCF40,0xCF41,/* 0x70-0x77 */
+ 0xCF42,0xCF43,0xCF44,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xCF45,0xCF46,0xCF47,0xCF48,0xCF49,0xCF4A,0xCF4B,/* 0x80-0x87 */
+ 0xCF4C,0xCF4D,0xCF4E,0xCF4F,0xCF50,0xCF51,0xCF52,0xCF53,/* 0x88-0x8F */
+ 0xCF56,0xCF57,0xCF59,0xCF5A,0xCF5B,0xCF5D,0xCF5E,0xCF5F,/* 0x90-0x97 */
+ 0xCF60,0xCF61,0xCF62,0xCF63,0xCF66,0xCF68,0xCF6A,0xCF6B,/* 0x98-0x9F */
+ 0xCF6C,0xAD0C,0xAD0D,0xAD0F,0xAD11,0xAD18,0xAD1C,0xAD20,/* 0xA0-0xA7 */
+ 0xAD29,0xAD2C,0xAD2D,0xAD34,0xAD35,0xAD38,0xAD3C,0xAD44,/* 0xA8-0xAF */
+ 0xAD45,0xAD47,0xAD49,0xAD50,0xAD54,0xAD58,0xAD61,0xAD63,/* 0xB0-0xB7 */
+ 0xAD6C,0xAD6D,0xAD70,0xAD73,0xAD74,0xAD75,0xAD76,0xAD7B,/* 0xB8-0xBF */
+ 0xAD7C,0xAD7D,0xAD7F,0xAD81,0xAD82,0xAD88,0xAD89,0xAD8C,/* 0xC0-0xC7 */
+ 0xAD90,0xAD9C,0xAD9D,0xADA4,0xADB7,0xADC0,0xADC1,0xADC4,/* 0xC8-0xCF */
+ 0xADC8,0xADD0,0xADD1,0xADD3,0xADDC,0xADE0,0xADE4,0xADF8,/* 0xD0-0xD7 */
+ 0xADF9,0xADFC,0xADFF,0xAE00,0xAE01,0xAE08,0xAE09,0xAE0B,/* 0xD8-0xDF */
+ 0xAE0D,0xAE14,0xAE30,0xAE31,0xAE34,0xAE37,0xAE38,0xAE3A,/* 0xE0-0xE7 */
+ 0xAE40,0xAE41,0xAE43,0xAE45,0xAE46,0xAE4A,0xAE4C,0xAE4D,/* 0xE8-0xEF */
+ 0xAE4E,0xAE50,0xAE54,0xAE56,0xAE5C,0xAE5D,0xAE5F,0xAE60,/* 0xF0-0xF7 */
+ 0xAE61,0xAE65,0xAE68,0xAE69,0xAE6C,0xAE70,0xAE78,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_B2[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xCF6D,0xCF6E,0xCF6F,0xCF72,0xCF73,0xCF75,0xCF76,/* 0x40-0x47 */
+ 0xCF77,0xCF79,0xCF7A,0xCF7B,0xCF7C,0xCF7D,0xCF7E,0xCF7F,/* 0x48-0x4F */
+ 0xCF81,0xCF82,0xCF83,0xCF84,0xCF86,0xCF87,0xCF88,0xCF89,/* 0x50-0x57 */
+ 0xCF8A,0xCF8B,0xCF8D,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xCF8E,0xCF8F,0xCF90,0xCF91,0xCF92,0xCF93,0xCF94,/* 0x60-0x67 */
+ 0xCF95,0xCF96,0xCF97,0xCF98,0xCF99,0xCF9A,0xCF9B,0xCF9C,/* 0x68-0x6F */
+ 0xCF9D,0xCF9E,0xCF9F,0xCFA0,0xCFA2,0xCFA3,0xCFA4,0xCFA5,/* 0x70-0x77 */
+ 0xCFA6,0xCFA7,0xCFA9,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xCFAA,0xCFAB,0xCFAC,0xCFAD,0xCFAE,0xCFAF,0xCFB1,/* 0x80-0x87 */
+ 0xCFB2,0xCFB3,0xCFB4,0xCFB5,0xCFB6,0xCFB7,0xCFB8,0xCFB9,/* 0x88-0x8F */
+ 0xCFBA,0xCFBB,0xCFBC,0xCFBD,0xCFBE,0xCFBF,0xCFC0,0xCFC1,/* 0x90-0x97 */
+ 0xCFC2,0xCFC3,0xCFC5,0xCFC6,0xCFC7,0xCFC8,0xCFC9,0xCFCA,/* 0x98-0x9F */
+ 0xCFCB,0xAE79,0xAE7B,0xAE7C,0xAE7D,0xAE84,0xAE85,0xAE8C,/* 0xA0-0xA7 */
+ 0xAEBC,0xAEBD,0xAEBE,0xAEC0,0xAEC4,0xAECC,0xAECD,0xAECF,/* 0xA8-0xAF */
+ 0xAED0,0xAED1,0xAED8,0xAED9,0xAEDC,0xAEE8,0xAEEB,0xAEED,/* 0xB0-0xB7 */
+ 0xAEF4,0xAEF8,0xAEFC,0xAF07,0xAF08,0xAF0D,0xAF10,0xAF2C,/* 0xB8-0xBF */
+ 0xAF2D,0xAF30,0xAF32,0xAF34,0xAF3C,0xAF3D,0xAF3F,0xAF41,/* 0xC0-0xC7 */
+ 0xAF42,0xAF43,0xAF48,0xAF49,0xAF50,0xAF5C,0xAF5D,0xAF64,/* 0xC8-0xCF */
+ 0xAF65,0xAF79,0xAF80,0xAF84,0xAF88,0xAF90,0xAF91,0xAF95,/* 0xD0-0xD7 */
+ 0xAF9C,0xAFB8,0xAFB9,0xAFBC,0xAFC0,0xAFC7,0xAFC8,0xAFC9,/* 0xD8-0xDF */
+ 0xAFCB,0xAFCD,0xAFCE,0xAFD4,0xAFDC,0xAFE8,0xAFE9,0xAFF0,/* 0xE0-0xE7 */
+ 0xAFF1,0xAFF4,0xAFF8,0xB000,0xB001,0xB004,0xB00C,0xB010,/* 0xE8-0xEF */
+ 0xB014,0xB01C,0xB01D,0xB028,0xB044,0xB045,0xB048,0xB04A,/* 0xF0-0xF7 */
+ 0xB04C,0xB04E,0xB053,0xB054,0xB055,0xB057,0xB059,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_B3[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xCFCC,0xCFCD,0xCFCE,0xCFCF,0xCFD0,0xCFD1,0xCFD2,/* 0x40-0x47 */
+ 0xCFD3,0xCFD4,0xCFD5,0xCFD6,0xCFD7,0xCFD8,0xCFD9,0xCFDA,/* 0x48-0x4F */
+ 0xCFDB,0xCFDC,0xCFDD,0xCFDE,0xCFDF,0xCFE2,0xCFE3,0xCFE5,/* 0x50-0x57 */
+ 0xCFE6,0xCFE7,0xCFE9,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xCFEA,0xCFEB,0xCFEC,0xCFED,0xCFEE,0xCFEF,0xCFF2,/* 0x60-0x67 */
+ 0xCFF4,0xCFF6,0xCFF7,0xCFF8,0xCFF9,0xCFFA,0xCFFB,0xCFFD,/* 0x68-0x6F */
+ 0xCFFE,0xCFFF,0xD001,0xD002,0xD003,0xD005,0xD006,0xD007,/* 0x70-0x77 */
+ 0xD008,0xD009,0xD00A,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xD00B,0xD00C,0xD00D,0xD00E,0xD00F,0xD010,0xD012,/* 0x80-0x87 */
+ 0xD013,0xD014,0xD015,0xD016,0xD017,0xD019,0xD01A,0xD01B,/* 0x88-0x8F */
+ 0xD01C,0xD01D,0xD01E,0xD01F,0xD020,0xD021,0xD022,0xD023,/* 0x90-0x97 */
+ 0xD024,0xD025,0xD026,0xD027,0xD028,0xD029,0xD02A,0xD02B,/* 0x98-0x9F */
+ 0xD02C,0xB05D,0xB07C,0xB07D,0xB080,0xB084,0xB08C,0xB08D,/* 0xA0-0xA7 */
+ 0xB08F,0xB091,0xB098,0xB099,0xB09A,0xB09C,0xB09F,0xB0A0,/* 0xA8-0xAF */
+ 0xB0A1,0xB0A2,0xB0A8,0xB0A9,0xB0AB,0xB0AC,0xB0AD,0xB0AE,/* 0xB0-0xB7 */
+ 0xB0AF,0xB0B1,0xB0B3,0xB0B4,0xB0B5,0xB0B8,0xB0BC,0xB0C4,/* 0xB8-0xBF */
+ 0xB0C5,0xB0C7,0xB0C8,0xB0C9,0xB0D0,0xB0D1,0xB0D4,0xB0D8,/* 0xC0-0xC7 */
+ 0xB0E0,0xB0E5,0xB108,0xB109,0xB10B,0xB10C,0xB110,0xB112,/* 0xC8-0xCF */
+ 0xB113,0xB118,0xB119,0xB11B,0xB11C,0xB11D,0xB123,0xB124,/* 0xD0-0xD7 */
+ 0xB125,0xB128,0xB12C,0xB134,0xB135,0xB137,0xB138,0xB139,/* 0xD8-0xDF */
+ 0xB140,0xB141,0xB144,0xB148,0xB150,0xB151,0xB154,0xB155,/* 0xE0-0xE7 */
+ 0xB158,0xB15C,0xB160,0xB178,0xB179,0xB17C,0xB180,0xB182,/* 0xE8-0xEF */
+ 0xB188,0xB189,0xB18B,0xB18D,0xB192,0xB193,0xB194,0xB198,/* 0xF0-0xF7 */
+ 0xB19C,0xB1A8,0xB1CC,0xB1D0,0xB1D4,0xB1DC,0xB1DD,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_B4[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xD02E,0xD02F,0xD030,0xD031,0xD032,0xD033,0xD036,/* 0x40-0x47 */
+ 0xD037,0xD039,0xD03A,0xD03B,0xD03D,0xD03E,0xD03F,0xD040,/* 0x48-0x4F */
+ 0xD041,0xD042,0xD043,0xD046,0xD048,0xD04A,0xD04B,0xD04C,/* 0x50-0x57 */
+ 0xD04D,0xD04E,0xD04F,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xD051,0xD052,0xD053,0xD055,0xD056,0xD057,0xD059,/* 0x60-0x67 */
+ 0xD05A,0xD05B,0xD05C,0xD05D,0xD05E,0xD05F,0xD061,0xD062,/* 0x68-0x6F */
+ 0xD063,0xD064,0xD065,0xD066,0xD067,0xD068,0xD069,0xD06A,/* 0x70-0x77 */
+ 0xD06B,0xD06E,0xD06F,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xD071,0xD072,0xD073,0xD075,0xD076,0xD077,0xD078,/* 0x80-0x87 */
+ 0xD079,0xD07A,0xD07B,0xD07E,0xD07F,0xD080,0xD082,0xD083,/* 0x88-0x8F */
+ 0xD084,0xD085,0xD086,0xD087,0xD088,0xD089,0xD08A,0xD08B,/* 0x90-0x97 */
+ 0xD08C,0xD08D,0xD08E,0xD08F,0xD090,0xD091,0xD092,0xD093,/* 0x98-0x9F */
+ 0xD094,0xB1DF,0xB1E8,0xB1E9,0xB1EC,0xB1F0,0xB1F9,0xB1FB,/* 0xA0-0xA7 */
+ 0xB1FD,0xB204,0xB205,0xB208,0xB20B,0xB20C,0xB214,0xB215,/* 0xA8-0xAF */
+ 0xB217,0xB219,0xB220,0xB234,0xB23C,0xB258,0xB25C,0xB260,/* 0xB0-0xB7 */
+ 0xB268,0xB269,0xB274,0xB275,0xB27C,0xB284,0xB285,0xB289,/* 0xB8-0xBF */
+ 0xB290,0xB291,0xB294,0xB298,0xB299,0xB29A,0xB2A0,0xB2A1,/* 0xC0-0xC7 */
+ 0xB2A3,0xB2A5,0xB2A6,0xB2AA,0xB2AC,0xB2B0,0xB2B4,0xB2C8,/* 0xC8-0xCF */
+ 0xB2C9,0xB2CC,0xB2D0,0xB2D2,0xB2D8,0xB2D9,0xB2DB,0xB2DD,/* 0xD0-0xD7 */
+ 0xB2E2,0xB2E4,0xB2E5,0xB2E6,0xB2E8,0xB2EB,0xB2EC,0xB2ED,/* 0xD8-0xDF */
+ 0xB2EE,0xB2EF,0xB2F3,0xB2F4,0xB2F5,0xB2F7,0xB2F8,0xB2F9,/* 0xE0-0xE7 */
+ 0xB2FA,0xB2FB,0xB2FF,0xB300,0xB301,0xB304,0xB308,0xB310,/* 0xE8-0xEF */
+ 0xB311,0xB313,0xB314,0xB315,0xB31C,0xB354,0xB355,0xB356,/* 0xF0-0xF7 */
+ 0xB358,0xB35B,0xB35C,0xB35E,0xB35F,0xB364,0xB365,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_B5[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xD095,0xD096,0xD097,0xD098,0xD099,0xD09A,0xD09B,/* 0x40-0x47 */
+ 0xD09C,0xD09D,0xD09E,0xD09F,0xD0A0,0xD0A1,0xD0A2,0xD0A3,/* 0x48-0x4F */
+ 0xD0A6,0xD0A7,0xD0A9,0xD0AA,0xD0AB,0xD0AD,0xD0AE,0xD0AF,/* 0x50-0x57 */
+ 0xD0B0,0xD0B1,0xD0B2,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xD0B3,0xD0B6,0xD0B8,0xD0BA,0xD0BB,0xD0BC,0xD0BD,/* 0x60-0x67 */
+ 0xD0BE,0xD0BF,0xD0C2,0xD0C3,0xD0C5,0xD0C6,0xD0C7,0xD0CA,/* 0x68-0x6F */
+ 0xD0CB,0xD0CC,0xD0CD,0xD0CE,0xD0CF,0xD0D2,0xD0D6,0xD0D7,/* 0x70-0x77 */
+ 0xD0D8,0xD0D9,0xD0DA,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xD0DB,0xD0DE,0xD0DF,0xD0E1,0xD0E2,0xD0E3,0xD0E5,/* 0x80-0x87 */
+ 0xD0E6,0xD0E7,0xD0E8,0xD0E9,0xD0EA,0xD0EB,0xD0EE,0xD0F2,/* 0x88-0x8F */
+ 0xD0F3,0xD0F4,0xD0F5,0xD0F6,0xD0F7,0xD0F9,0xD0FA,0xD0FB,/* 0x90-0x97 */
+ 0xD0FC,0xD0FD,0xD0FE,0xD0FF,0xD100,0xD101,0xD102,0xD103,/* 0x98-0x9F */
+ 0xD104,0xB367,0xB369,0xB36B,0xB36E,0xB370,0xB371,0xB374,/* 0xA0-0xA7 */
+ 0xB378,0xB380,0xB381,0xB383,0xB384,0xB385,0xB38C,0xB390,/* 0xA8-0xAF */
+ 0xB394,0xB3A0,0xB3A1,0xB3A8,0xB3AC,0xB3C4,0xB3C5,0xB3C8,/* 0xB0-0xB7 */
+ 0xB3CB,0xB3CC,0xB3CE,0xB3D0,0xB3D4,0xB3D5,0xB3D7,0xB3D9,/* 0xB8-0xBF */
+ 0xB3DB,0xB3DD,0xB3E0,0xB3E4,0xB3E8,0xB3FC,0xB410,0xB418,/* 0xC0-0xC7 */
+ 0xB41C,0xB420,0xB428,0xB429,0xB42B,0xB434,0xB450,0xB451,/* 0xC8-0xCF */
+ 0xB454,0xB458,0xB460,0xB461,0xB463,0xB465,0xB46C,0xB480,/* 0xD0-0xD7 */
+ 0xB488,0xB49D,0xB4A4,0xB4A8,0xB4AC,0xB4B5,0xB4B7,0xB4B9,/* 0xD8-0xDF */
+ 0xB4C0,0xB4C4,0xB4C8,0xB4D0,0xB4D5,0xB4DC,0xB4DD,0xB4E0,/* 0xE0-0xE7 */
+ 0xB4E3,0xB4E4,0xB4E6,0xB4EC,0xB4ED,0xB4EF,0xB4F1,0xB4F8,/* 0xE8-0xEF */
+ 0xB514,0xB515,0xB518,0xB51B,0xB51C,0xB524,0xB525,0xB527,/* 0xF0-0xF7 */
+ 0xB528,0xB529,0xB52A,0xB530,0xB531,0xB534,0xB538,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_B6[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xD105,0xD106,0xD107,0xD108,0xD109,0xD10A,0xD10B,/* 0x40-0x47 */
+ 0xD10C,0xD10E,0xD10F,0xD110,0xD111,0xD112,0xD113,0xD114,/* 0x48-0x4F */
+ 0xD115,0xD116,0xD117,0xD118,0xD119,0xD11A,0xD11B,0xD11C,/* 0x50-0x57 */
+ 0xD11D,0xD11E,0xD11F,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xD120,0xD121,0xD122,0xD123,0xD124,0xD125,0xD126,/* 0x60-0x67 */
+ 0xD127,0xD128,0xD129,0xD12A,0xD12B,0xD12C,0xD12D,0xD12E,/* 0x68-0x6F */
+ 0xD12F,0xD132,0xD133,0xD135,0xD136,0xD137,0xD139,0xD13B,/* 0x70-0x77 */
+ 0xD13C,0xD13D,0xD13E,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xD13F,0xD142,0xD146,0xD147,0xD148,0xD149,0xD14A,/* 0x80-0x87 */
+ 0xD14B,0xD14E,0xD14F,0xD151,0xD152,0xD153,0xD155,0xD156,/* 0x88-0x8F */
+ 0xD157,0xD158,0xD159,0xD15A,0xD15B,0xD15E,0xD160,0xD162,/* 0x90-0x97 */
+ 0xD163,0xD164,0xD165,0xD166,0xD167,0xD169,0xD16A,0xD16B,/* 0x98-0x9F */
+ 0xD16D,0xB540,0xB541,0xB543,0xB544,0xB545,0xB54B,0xB54C,/* 0xA0-0xA7 */
+ 0xB54D,0xB550,0xB554,0xB55C,0xB55D,0xB55F,0xB560,0xB561,/* 0xA8-0xAF */
+ 0xB5A0,0xB5A1,0xB5A4,0xB5A8,0xB5AA,0xB5AB,0xB5B0,0xB5B1,/* 0xB0-0xB7 */
+ 0xB5B3,0xB5B4,0xB5B5,0xB5BB,0xB5BC,0xB5BD,0xB5C0,0xB5C4,/* 0xB8-0xBF */
+ 0xB5CC,0xB5CD,0xB5CF,0xB5D0,0xB5D1,0xB5D8,0xB5EC,0xB610,/* 0xC0-0xC7 */
+ 0xB611,0xB614,0xB618,0xB625,0xB62C,0xB634,0xB648,0xB664,/* 0xC8-0xCF */
+ 0xB668,0xB69C,0xB69D,0xB6A0,0xB6A4,0xB6AB,0xB6AC,0xB6B1,/* 0xD0-0xD7 */
+ 0xB6D4,0xB6F0,0xB6F4,0xB6F8,0xB700,0xB701,0xB705,0xB728,/* 0xD8-0xDF */
+ 0xB729,0xB72C,0xB72F,0xB730,0xB738,0xB739,0xB73B,0xB744,/* 0xE0-0xE7 */
+ 0xB748,0xB74C,0xB754,0xB755,0xB760,0xB764,0xB768,0xB770,/* 0xE8-0xEF */
+ 0xB771,0xB773,0xB775,0xB77C,0xB77D,0xB780,0xB784,0xB78C,/* 0xF0-0xF7 */
+ 0xB78D,0xB78F,0xB790,0xB791,0xB792,0xB796,0xB797,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_B7[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xD16E,0xD16F,0xD170,0xD171,0xD172,0xD173,0xD174,/* 0x40-0x47 */
+ 0xD175,0xD176,0xD177,0xD178,0xD179,0xD17A,0xD17B,0xD17D,/* 0x48-0x4F */
+ 0xD17E,0xD17F,0xD180,0xD181,0xD182,0xD183,0xD185,0xD186,/* 0x50-0x57 */
+ 0xD187,0xD189,0xD18A,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xD18B,0xD18C,0xD18D,0xD18E,0xD18F,0xD190,0xD191,/* 0x60-0x67 */
+ 0xD192,0xD193,0xD194,0xD195,0xD196,0xD197,0xD198,0xD199,/* 0x68-0x6F */
+ 0xD19A,0xD19B,0xD19C,0xD19D,0xD19E,0xD19F,0xD1A2,0xD1A3,/* 0x70-0x77 */
+ 0xD1A5,0xD1A6,0xD1A7,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xD1A9,0xD1AA,0xD1AB,0xD1AC,0xD1AD,0xD1AE,0xD1AF,/* 0x80-0x87 */
+ 0xD1B2,0xD1B4,0xD1B6,0xD1B7,0xD1B8,0xD1B9,0xD1BB,0xD1BD,/* 0x88-0x8F */
+ 0xD1BE,0xD1BF,0xD1C1,0xD1C2,0xD1C3,0xD1C4,0xD1C5,0xD1C6,/* 0x90-0x97 */
+ 0xD1C7,0xD1C8,0xD1C9,0xD1CA,0xD1CB,0xD1CC,0xD1CD,0xD1CE,/* 0x98-0x9F */
+ 0xD1CF,0xB798,0xB799,0xB79C,0xB7A0,0xB7A8,0xB7A9,0xB7AB,/* 0xA0-0xA7 */
+ 0xB7AC,0xB7AD,0xB7B4,0xB7B5,0xB7B8,0xB7C7,0xB7C9,0xB7EC,/* 0xA8-0xAF */
+ 0xB7ED,0xB7F0,0xB7F4,0xB7FC,0xB7FD,0xB7FF,0xB800,0xB801,/* 0xB0-0xB7 */
+ 0xB807,0xB808,0xB809,0xB80C,0xB810,0xB818,0xB819,0xB81B,/* 0xB8-0xBF */
+ 0xB81D,0xB824,0xB825,0xB828,0xB82C,0xB834,0xB835,0xB837,/* 0xC0-0xC7 */
+ 0xB838,0xB839,0xB840,0xB844,0xB851,0xB853,0xB85C,0xB85D,/* 0xC8-0xCF */
+ 0xB860,0xB864,0xB86C,0xB86D,0xB86F,0xB871,0xB878,0xB87C,/* 0xD0-0xD7 */
+ 0xB88D,0xB8A8,0xB8B0,0xB8B4,0xB8B8,0xB8C0,0xB8C1,0xB8C3,/* 0xD8-0xDF */
+ 0xB8C5,0xB8CC,0xB8D0,0xB8D4,0xB8DD,0xB8DF,0xB8E1,0xB8E8,/* 0xE0-0xE7 */
+ 0xB8E9,0xB8EC,0xB8F0,0xB8F8,0xB8F9,0xB8FB,0xB8FD,0xB904,/* 0xE8-0xEF */
+ 0xB918,0xB920,0xB93C,0xB93D,0xB940,0xB944,0xB94C,0xB94F,/* 0xF0-0xF7 */
+ 0xB951,0xB958,0xB959,0xB95C,0xB960,0xB968,0xB969,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_B8[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xD1D0,0xD1D1,0xD1D2,0xD1D3,0xD1D4,0xD1D5,0xD1D6,/* 0x40-0x47 */
+ 0xD1D7,0xD1D9,0xD1DA,0xD1DB,0xD1DC,0xD1DD,0xD1DE,0xD1DF,/* 0x48-0x4F */
+ 0xD1E0,0xD1E1,0xD1E2,0xD1E3,0xD1E4,0xD1E5,0xD1E6,0xD1E7,/* 0x50-0x57 */
+ 0xD1E8,0xD1E9,0xD1EA,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xD1EB,0xD1EC,0xD1ED,0xD1EE,0xD1EF,0xD1F0,0xD1F1,/* 0x60-0x67 */
+ 0xD1F2,0xD1F3,0xD1F5,0xD1F6,0xD1F7,0xD1F9,0xD1FA,0xD1FB,/* 0x68-0x6F */
+ 0xD1FC,0xD1FD,0xD1FE,0xD1FF,0xD200,0xD201,0xD202,0xD203,/* 0x70-0x77 */
+ 0xD204,0xD205,0xD206,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xD208,0xD20A,0xD20B,0xD20C,0xD20D,0xD20E,0xD20F,/* 0x80-0x87 */
+ 0xD211,0xD212,0xD213,0xD214,0xD215,0xD216,0xD217,0xD218,/* 0x88-0x8F */
+ 0xD219,0xD21A,0xD21B,0xD21C,0xD21D,0xD21E,0xD21F,0xD220,/* 0x90-0x97 */
+ 0xD221,0xD222,0xD223,0xD224,0xD225,0xD226,0xD227,0xD228,/* 0x98-0x9F */
+ 0xD229,0xB96B,0xB96D,0xB974,0xB975,0xB978,0xB97C,0xB984,/* 0xA0-0xA7 */
+ 0xB985,0xB987,0xB989,0xB98A,0xB98D,0xB98E,0xB9AC,0xB9AD,/* 0xA8-0xAF */
+ 0xB9B0,0xB9B4,0xB9BC,0xB9BD,0xB9BF,0xB9C1,0xB9C8,0xB9C9,/* 0xB0-0xB7 */
+ 0xB9CC,0xB9CE,0xB9CF,0xB9D0,0xB9D1,0xB9D2,0xB9D8,0xB9D9,/* 0xB8-0xBF */
+ 0xB9DB,0xB9DD,0xB9DE,0xB9E1,0xB9E3,0xB9E4,0xB9E5,0xB9E8,/* 0xC0-0xC7 */
+ 0xB9EC,0xB9F4,0xB9F5,0xB9F7,0xB9F8,0xB9F9,0xB9FA,0xBA00,/* 0xC8-0xCF */
+ 0xBA01,0xBA08,0xBA15,0xBA38,0xBA39,0xBA3C,0xBA40,0xBA42,/* 0xD0-0xD7 */
+ 0xBA48,0xBA49,0xBA4B,0xBA4D,0xBA4E,0xBA53,0xBA54,0xBA55,/* 0xD8-0xDF */
+ 0xBA58,0xBA5C,0xBA64,0xBA65,0xBA67,0xBA68,0xBA69,0xBA70,/* 0xE0-0xE7 */
+ 0xBA71,0xBA74,0xBA78,0xBA83,0xBA84,0xBA85,0xBA87,0xBA8C,/* 0xE8-0xEF */
+ 0xBAA8,0xBAA9,0xBAAB,0xBAAC,0xBAB0,0xBAB2,0xBAB8,0xBAB9,/* 0xF0-0xF7 */
+ 0xBABB,0xBABD,0xBAC4,0xBAC8,0xBAD8,0xBAD9,0xBAFC,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_B9[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xD22A,0xD22B,0xD22E,0xD22F,0xD231,0xD232,0xD233,/* 0x40-0x47 */
+ 0xD235,0xD236,0xD237,0xD238,0xD239,0xD23A,0xD23B,0xD23E,/* 0x48-0x4F */
+ 0xD240,0xD242,0xD243,0xD244,0xD245,0xD246,0xD247,0xD249,/* 0x50-0x57 */
+ 0xD24A,0xD24B,0xD24C,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xD24D,0xD24E,0xD24F,0xD250,0xD251,0xD252,0xD253,/* 0x60-0x67 */
+ 0xD254,0xD255,0xD256,0xD257,0xD258,0xD259,0xD25A,0xD25B,/* 0x68-0x6F */
+ 0xD25D,0xD25E,0xD25F,0xD260,0xD261,0xD262,0xD263,0xD265,/* 0x70-0x77 */
+ 0xD266,0xD267,0xD268,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xD269,0xD26A,0xD26B,0xD26C,0xD26D,0xD26E,0xD26F,/* 0x80-0x87 */
+ 0xD270,0xD271,0xD272,0xD273,0xD274,0xD275,0xD276,0xD277,/* 0x88-0x8F */
+ 0xD278,0xD279,0xD27A,0xD27B,0xD27C,0xD27D,0xD27E,0xD27F,/* 0x90-0x97 */
+ 0xD282,0xD283,0xD285,0xD286,0xD287,0xD289,0xD28A,0xD28B,/* 0x98-0x9F */
+ 0xD28C,0xBB00,0xBB04,0xBB0D,0xBB0F,0xBB11,0xBB18,0xBB1C,/* 0xA0-0xA7 */
+ 0xBB20,0xBB29,0xBB2B,0xBB34,0xBB35,0xBB36,0xBB38,0xBB3B,/* 0xA8-0xAF */
+ 0xBB3C,0xBB3D,0xBB3E,0xBB44,0xBB45,0xBB47,0xBB49,0xBB4D,/* 0xB0-0xB7 */
+ 0xBB4F,0xBB50,0xBB54,0xBB58,0xBB61,0xBB63,0xBB6C,0xBB88,/* 0xB8-0xBF */
+ 0xBB8C,0xBB90,0xBBA4,0xBBA8,0xBBAC,0xBBB4,0xBBB7,0xBBC0,/* 0xC0-0xC7 */
+ 0xBBC4,0xBBC8,0xBBD0,0xBBD3,0xBBF8,0xBBF9,0xBBFC,0xBBFF,/* 0xC8-0xCF */
+ 0xBC00,0xBC02,0xBC08,0xBC09,0xBC0B,0xBC0C,0xBC0D,0xBC0F,/* 0xD0-0xD7 */
+ 0xBC11,0xBC14,0xBC15,0xBC16,0xBC17,0xBC18,0xBC1B,0xBC1C,/* 0xD8-0xDF */
+ 0xBC1D,0xBC1E,0xBC1F,0xBC24,0xBC25,0xBC27,0xBC29,0xBC2D,/* 0xE0-0xE7 */
+ 0xBC30,0xBC31,0xBC34,0xBC38,0xBC40,0xBC41,0xBC43,0xBC44,/* 0xE8-0xEF */
+ 0xBC45,0xBC49,0xBC4C,0xBC4D,0xBC50,0xBC5D,0xBC84,0xBC85,/* 0xF0-0xF7 */
+ 0xBC88,0xBC8B,0xBC8C,0xBC8E,0xBC94,0xBC95,0xBC97,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_BA[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xD28D,0xD28E,0xD28F,0xD292,0xD293,0xD294,0xD296,/* 0x40-0x47 */
+ 0xD297,0xD298,0xD299,0xD29A,0xD29B,0xD29D,0xD29E,0xD29F,/* 0x48-0x4F */
+ 0xD2A1,0xD2A2,0xD2A3,0xD2A5,0xD2A6,0xD2A7,0xD2A8,0xD2A9,/* 0x50-0x57 */
+ 0xD2AA,0xD2AB,0xD2AD,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xD2AE,0xD2AF,0xD2B0,0xD2B2,0xD2B3,0xD2B4,0xD2B5,/* 0x60-0x67 */
+ 0xD2B6,0xD2B7,0xD2BA,0xD2BB,0xD2BD,0xD2BE,0xD2C1,0xD2C3,/* 0x68-0x6F */
+ 0xD2C4,0xD2C5,0xD2C6,0xD2C7,0xD2CA,0xD2CC,0xD2CD,0xD2CE,/* 0x70-0x77 */
+ 0xD2CF,0xD2D0,0xD2D1,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xD2D2,0xD2D3,0xD2D5,0xD2D6,0xD2D7,0xD2D9,0xD2DA,/* 0x80-0x87 */
+ 0xD2DB,0xD2DD,0xD2DE,0xD2DF,0xD2E0,0xD2E1,0xD2E2,0xD2E3,/* 0x88-0x8F */
+ 0xD2E6,0xD2E7,0xD2E8,0xD2E9,0xD2EA,0xD2EB,0xD2EC,0xD2ED,/* 0x90-0x97 */
+ 0xD2EE,0xD2EF,0xD2F2,0xD2F3,0xD2F5,0xD2F6,0xD2F7,0xD2F9,/* 0x98-0x9F */
+ 0xD2FA,0xBC99,0xBC9A,0xBCA0,0xBCA1,0xBCA4,0xBCA7,0xBCA8,/* 0xA0-0xA7 */
+ 0xBCB0,0xBCB1,0xBCB3,0xBCB4,0xBCB5,0xBCBC,0xBCBD,0xBCC0,/* 0xA8-0xAF */
+ 0xBCC4,0xBCCD,0xBCCF,0xBCD0,0xBCD1,0xBCD5,0xBCD8,0xBCDC,/* 0xB0-0xB7 */
+ 0xBCF4,0xBCF5,0xBCF6,0xBCF8,0xBCFC,0xBD04,0xBD05,0xBD07,/* 0xB8-0xBF */
+ 0xBD09,0xBD10,0xBD14,0xBD24,0xBD2C,0xBD40,0xBD48,0xBD49,/* 0xC0-0xC7 */
+ 0xBD4C,0xBD50,0xBD58,0xBD59,0xBD64,0xBD68,0xBD80,0xBD81,/* 0xC8-0xCF */
+ 0xBD84,0xBD87,0xBD88,0xBD89,0xBD8A,0xBD90,0xBD91,0xBD93,/* 0xD0-0xD7 */
+ 0xBD95,0xBD99,0xBD9A,0xBD9C,0xBDA4,0xBDB0,0xBDB8,0xBDD4,/* 0xD8-0xDF */
+ 0xBDD5,0xBDD8,0xBDDC,0xBDE9,0xBDF0,0xBDF4,0xBDF8,0xBE00,/* 0xE0-0xE7 */
+ 0xBE03,0xBE05,0xBE0C,0xBE0D,0xBE10,0xBE14,0xBE1C,0xBE1D,/* 0xE8-0xEF */
+ 0xBE1F,0xBE44,0xBE45,0xBE48,0xBE4C,0xBE4E,0xBE54,0xBE55,/* 0xF0-0xF7 */
+ 0xBE57,0xBE59,0xBE5A,0xBE5B,0xBE60,0xBE61,0xBE64,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_BB[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xD2FB,0xD2FC,0xD2FD,0xD2FE,0xD2FF,0xD302,0xD304,/* 0x40-0x47 */
+ 0xD306,0xD307,0xD308,0xD309,0xD30A,0xD30B,0xD30F,0xD311,/* 0x48-0x4F */
+ 0xD312,0xD313,0xD315,0xD317,0xD318,0xD319,0xD31A,0xD31B,/* 0x50-0x57 */
+ 0xD31E,0xD322,0xD323,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xD324,0xD326,0xD327,0xD32A,0xD32B,0xD32D,0xD32E,/* 0x60-0x67 */
+ 0xD32F,0xD331,0xD332,0xD333,0xD334,0xD335,0xD336,0xD337,/* 0x68-0x6F */
+ 0xD33A,0xD33E,0xD33F,0xD340,0xD341,0xD342,0xD343,0xD346,/* 0x70-0x77 */
+ 0xD347,0xD348,0xD349,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xD34A,0xD34B,0xD34C,0xD34D,0xD34E,0xD34F,0xD350,/* 0x80-0x87 */
+ 0xD351,0xD352,0xD353,0xD354,0xD355,0xD356,0xD357,0xD358,/* 0x88-0x8F */
+ 0xD359,0xD35A,0xD35B,0xD35C,0xD35D,0xD35E,0xD35F,0xD360,/* 0x90-0x97 */
+ 0xD361,0xD362,0xD363,0xD364,0xD365,0xD366,0xD367,0xD368,/* 0x98-0x9F */
+ 0xD369,0xBE68,0xBE6A,0xBE70,0xBE71,0xBE73,0xBE74,0xBE75,/* 0xA0-0xA7 */
+ 0xBE7B,0xBE7C,0xBE7D,0xBE80,0xBE84,0xBE8C,0xBE8D,0xBE8F,/* 0xA8-0xAF */
+ 0xBE90,0xBE91,0xBE98,0xBE99,0xBEA8,0xBED0,0xBED1,0xBED4,/* 0xB0-0xB7 */
+ 0xBED7,0xBED8,0xBEE0,0xBEE3,0xBEE4,0xBEE5,0xBEEC,0xBF01,/* 0xB8-0xBF */
+ 0xBF08,0xBF09,0xBF18,0xBF19,0xBF1B,0xBF1C,0xBF1D,0xBF40,/* 0xC0-0xC7 */
+ 0xBF41,0xBF44,0xBF48,0xBF50,0xBF51,0xBF55,0xBF94,0xBFB0,/* 0xC8-0xCF */
+ 0xBFC5,0xBFCC,0xBFCD,0xBFD0,0xBFD4,0xBFDC,0xBFDF,0xBFE1,/* 0xD0-0xD7 */
+ 0xC03C,0xC051,0xC058,0xC05C,0xC060,0xC068,0xC069,0xC090,/* 0xD8-0xDF */
+ 0xC091,0xC094,0xC098,0xC0A0,0xC0A1,0xC0A3,0xC0A5,0xC0AC,/* 0xE0-0xE7 */
+ 0xC0AD,0xC0AF,0xC0B0,0xC0B3,0xC0B4,0xC0B5,0xC0B6,0xC0BC,/* 0xE8-0xEF */
+ 0xC0BD,0xC0BF,0xC0C0,0xC0C1,0xC0C5,0xC0C8,0xC0C9,0xC0CC,/* 0xF0-0xF7 */
+ 0xC0D0,0xC0D8,0xC0D9,0xC0DB,0xC0DC,0xC0DD,0xC0E4,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_BC[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xD36A,0xD36B,0xD36C,0xD36D,0xD36E,0xD36F,0xD370,/* 0x40-0x47 */
+ 0xD371,0xD372,0xD373,0xD374,0xD375,0xD376,0xD377,0xD378,/* 0x48-0x4F */
+ 0xD379,0xD37A,0xD37B,0xD37E,0xD37F,0xD381,0xD382,0xD383,/* 0x50-0x57 */
+ 0xD385,0xD386,0xD387,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xD388,0xD389,0xD38A,0xD38B,0xD38E,0xD392,0xD393,/* 0x60-0x67 */
+ 0xD394,0xD395,0xD396,0xD397,0xD39A,0xD39B,0xD39D,0xD39E,/* 0x68-0x6F */
+ 0xD39F,0xD3A1,0xD3A2,0xD3A3,0xD3A4,0xD3A5,0xD3A6,0xD3A7,/* 0x70-0x77 */
+ 0xD3AA,0xD3AC,0xD3AE,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xD3AF,0xD3B0,0xD3B1,0xD3B2,0xD3B3,0xD3B5,0xD3B6,/* 0x80-0x87 */
+ 0xD3B7,0xD3B9,0xD3BA,0xD3BB,0xD3BD,0xD3BE,0xD3BF,0xD3C0,/* 0x88-0x8F */
+ 0xD3C1,0xD3C2,0xD3C3,0xD3C6,0xD3C7,0xD3CA,0xD3CB,0xD3CC,/* 0x90-0x97 */
+ 0xD3CD,0xD3CE,0xD3CF,0xD3D1,0xD3D2,0xD3D3,0xD3D4,0xD3D5,/* 0x98-0x9F */
+ 0xD3D6,0xC0E5,0xC0E8,0xC0EC,0xC0F4,0xC0F5,0xC0F7,0xC0F9,/* 0xA0-0xA7 */
+ 0xC100,0xC104,0xC108,0xC110,0xC115,0xC11C,0xC11D,0xC11E,/* 0xA8-0xAF */
+ 0xC11F,0xC120,0xC123,0xC124,0xC126,0xC127,0xC12C,0xC12D,/* 0xB0-0xB7 */
+ 0xC12F,0xC130,0xC131,0xC136,0xC138,0xC139,0xC13C,0xC140,/* 0xB8-0xBF */
+ 0xC148,0xC149,0xC14B,0xC14C,0xC14D,0xC154,0xC155,0xC158,/* 0xC0-0xC7 */
+ 0xC15C,0xC164,0xC165,0xC167,0xC168,0xC169,0xC170,0xC174,/* 0xC8-0xCF */
+ 0xC178,0xC185,0xC18C,0xC18D,0xC18E,0xC190,0xC194,0xC196,/* 0xD0-0xD7 */
+ 0xC19C,0xC19D,0xC19F,0xC1A1,0xC1A5,0xC1A8,0xC1A9,0xC1AC,/* 0xD8-0xDF */
+ 0xC1B0,0xC1BD,0xC1C4,0xC1C8,0xC1CC,0xC1D4,0xC1D7,0xC1D8,/* 0xE0-0xE7 */
+ 0xC1E0,0xC1E4,0xC1E8,0xC1F0,0xC1F1,0xC1F3,0xC1FC,0xC1FD,/* 0xE8-0xEF */
+ 0xC200,0xC204,0xC20C,0xC20D,0xC20F,0xC211,0xC218,0xC219,/* 0xF0-0xF7 */
+ 0xC21C,0xC21F,0xC220,0xC228,0xC229,0xC22B,0xC22D,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_BD[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xD3D7,0xD3D9,0xD3DA,0xD3DB,0xD3DC,0xD3DD,0xD3DE,/* 0x40-0x47 */
+ 0xD3DF,0xD3E0,0xD3E2,0xD3E4,0xD3E5,0xD3E6,0xD3E7,0xD3E8,/* 0x48-0x4F */
+ 0xD3E9,0xD3EA,0xD3EB,0xD3EE,0xD3EF,0xD3F1,0xD3F2,0xD3F3,/* 0x50-0x57 */
+ 0xD3F5,0xD3F6,0xD3F7,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xD3F8,0xD3F9,0xD3FA,0xD3FB,0xD3FE,0xD400,0xD402,/* 0x60-0x67 */
+ 0xD403,0xD404,0xD405,0xD406,0xD407,0xD409,0xD40A,0xD40B,/* 0x68-0x6F */
+ 0xD40C,0xD40D,0xD40E,0xD40F,0xD410,0xD411,0xD412,0xD413,/* 0x70-0x77 */
+ 0xD414,0xD415,0xD416,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xD417,0xD418,0xD419,0xD41A,0xD41B,0xD41C,0xD41E,/* 0x80-0x87 */
+ 0xD41F,0xD420,0xD421,0xD422,0xD423,0xD424,0xD425,0xD426,/* 0x88-0x8F */
+ 0xD427,0xD428,0xD429,0xD42A,0xD42B,0xD42C,0xD42D,0xD42E,/* 0x90-0x97 */
+ 0xD42F,0xD430,0xD431,0xD432,0xD433,0xD434,0xD435,0xD436,/* 0x98-0x9F */
+ 0xD437,0xC22F,0xC231,0xC232,0xC234,0xC248,0xC250,0xC251,/* 0xA0-0xA7 */
+ 0xC254,0xC258,0xC260,0xC265,0xC26C,0xC26D,0xC270,0xC274,/* 0xA8-0xAF */
+ 0xC27C,0xC27D,0xC27F,0xC281,0xC288,0xC289,0xC290,0xC298,/* 0xB0-0xB7 */
+ 0xC29B,0xC29D,0xC2A4,0xC2A5,0xC2A8,0xC2AC,0xC2AD,0xC2B4,/* 0xB8-0xBF */
+ 0xC2B5,0xC2B7,0xC2B9,0xC2DC,0xC2DD,0xC2E0,0xC2E3,0xC2E4,/* 0xC0-0xC7 */
+ 0xC2EB,0xC2EC,0xC2ED,0xC2EF,0xC2F1,0xC2F6,0xC2F8,0xC2F9,/* 0xC8-0xCF */
+ 0xC2FB,0xC2FC,0xC300,0xC308,0xC309,0xC30C,0xC30D,0xC313,/* 0xD0-0xD7 */
+ 0xC314,0xC315,0xC318,0xC31C,0xC324,0xC325,0xC328,0xC329,/* 0xD8-0xDF */
+ 0xC345,0xC368,0xC369,0xC36C,0xC370,0xC372,0xC378,0xC379,/* 0xE0-0xE7 */
+ 0xC37C,0xC37D,0xC384,0xC388,0xC38C,0xC3C0,0xC3D8,0xC3D9,/* 0xE8-0xEF */
+ 0xC3DC,0xC3DF,0xC3E0,0xC3E2,0xC3E8,0xC3E9,0xC3ED,0xC3F4,/* 0xF0-0xF7 */
+ 0xC3F5,0xC3F8,0xC408,0xC410,0xC424,0xC42C,0xC430,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_BE[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xD438,0xD439,0xD43A,0xD43B,0xD43C,0xD43D,0xD43E,/* 0x40-0x47 */
+ 0xD43F,0xD441,0xD442,0xD443,0xD445,0xD446,0xD447,0xD448,/* 0x48-0x4F */
+ 0xD449,0xD44A,0xD44B,0xD44C,0xD44D,0xD44E,0xD44F,0xD450,/* 0x50-0x57 */
+ 0xD451,0xD452,0xD453,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xD454,0xD455,0xD456,0xD457,0xD458,0xD459,0xD45A,/* 0x60-0x67 */
+ 0xD45B,0xD45D,0xD45E,0xD45F,0xD461,0xD462,0xD463,0xD465,/* 0x68-0x6F */
+ 0xD466,0xD467,0xD468,0xD469,0xD46A,0xD46B,0xD46C,0xD46E,/* 0x70-0x77 */
+ 0xD470,0xD471,0xD472,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xD473,0xD474,0xD475,0xD476,0xD477,0xD47A,0xD47B,/* 0x80-0x87 */
+ 0xD47D,0xD47E,0xD481,0xD483,0xD484,0xD485,0xD486,0xD487,/* 0x88-0x8F */
+ 0xD48A,0xD48C,0xD48E,0xD48F,0xD490,0xD491,0xD492,0xD493,/* 0x90-0x97 */
+ 0xD495,0xD496,0xD497,0xD498,0xD499,0xD49A,0xD49B,0xD49C,/* 0x98-0x9F */
+ 0xD49D,0xC434,0xC43C,0xC43D,0xC448,0xC464,0xC465,0xC468,/* 0xA0-0xA7 */
+ 0xC46C,0xC474,0xC475,0xC479,0xC480,0xC494,0xC49C,0xC4B8,/* 0xA8-0xAF */
+ 0xC4BC,0xC4E9,0xC4F0,0xC4F1,0xC4F4,0xC4F8,0xC4FA,0xC4FF,/* 0xB0-0xB7 */
+ 0xC500,0xC501,0xC50C,0xC510,0xC514,0xC51C,0xC528,0xC529,/* 0xB8-0xBF */
+ 0xC52C,0xC530,0xC538,0xC539,0xC53B,0xC53D,0xC544,0xC545,/* 0xC0-0xC7 */
+ 0xC548,0xC549,0xC54A,0xC54C,0xC54D,0xC54E,0xC553,0xC554,/* 0xC8-0xCF */
+ 0xC555,0xC557,0xC558,0xC559,0xC55D,0xC55E,0xC560,0xC561,/* 0xD0-0xD7 */
+ 0xC564,0xC568,0xC570,0xC571,0xC573,0xC574,0xC575,0xC57C,/* 0xD8-0xDF */
+ 0xC57D,0xC580,0xC584,0xC587,0xC58C,0xC58D,0xC58F,0xC591,/* 0xE0-0xE7 */
+ 0xC595,0xC597,0xC598,0xC59C,0xC5A0,0xC5A9,0xC5B4,0xC5B5,/* 0xE8-0xEF */
+ 0xC5B8,0xC5B9,0xC5BB,0xC5BC,0xC5BD,0xC5BE,0xC5C4,0xC5C5,/* 0xF0-0xF7 */
+ 0xC5C6,0xC5C7,0xC5C8,0xC5C9,0xC5CA,0xC5CC,0xC5CE,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_BF[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xD49E,0xD49F,0xD4A0,0xD4A1,0xD4A2,0xD4A3,0xD4A4,/* 0x40-0x47 */
+ 0xD4A5,0xD4A6,0xD4A7,0xD4A8,0xD4AA,0xD4AB,0xD4AC,0xD4AD,/* 0x48-0x4F */
+ 0xD4AE,0xD4AF,0xD4B0,0xD4B1,0xD4B2,0xD4B3,0xD4B4,0xD4B5,/* 0x50-0x57 */
+ 0xD4B6,0xD4B7,0xD4B8,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xD4B9,0xD4BA,0xD4BB,0xD4BC,0xD4BD,0xD4BE,0xD4BF,/* 0x60-0x67 */
+ 0xD4C0,0xD4C1,0xD4C2,0xD4C3,0xD4C4,0xD4C5,0xD4C6,0xD4C7,/* 0x68-0x6F */
+ 0xD4C8,0xD4C9,0xD4CA,0xD4CB,0xD4CD,0xD4CE,0xD4CF,0xD4D1,/* 0x70-0x77 */
+ 0xD4D2,0xD4D3,0xD4D5,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xD4D6,0xD4D7,0xD4D8,0xD4D9,0xD4DA,0xD4DB,0xD4DD,/* 0x80-0x87 */
+ 0xD4DE,0xD4E0,0xD4E1,0xD4E2,0xD4E3,0xD4E4,0xD4E5,0xD4E6,/* 0x88-0x8F */
+ 0xD4E7,0xD4E9,0xD4EA,0xD4EB,0xD4ED,0xD4EE,0xD4EF,0xD4F1,/* 0x90-0x97 */
+ 0xD4F2,0xD4F3,0xD4F4,0xD4F5,0xD4F6,0xD4F7,0xD4F9,0xD4FA,/* 0x98-0x9F */
+ 0xD4FC,0xC5D0,0xC5D1,0xC5D4,0xC5D8,0xC5E0,0xC5E1,0xC5E3,/* 0xA0-0xA7 */
+ 0xC5E5,0xC5EC,0xC5ED,0xC5EE,0xC5F0,0xC5F4,0xC5F6,0xC5F7,/* 0xA8-0xAF */
+ 0xC5FC,0xC5FD,0xC5FE,0xC5FF,0xC600,0xC601,0xC605,0xC606,/* 0xB0-0xB7 */
+ 0xC607,0xC608,0xC60C,0xC610,0xC618,0xC619,0xC61B,0xC61C,/* 0xB8-0xBF */
+ 0xC624,0xC625,0xC628,0xC62C,0xC62D,0xC62E,0xC630,0xC633,/* 0xC0-0xC7 */
+ 0xC634,0xC635,0xC637,0xC639,0xC63B,0xC640,0xC641,0xC644,/* 0xC8-0xCF */
+ 0xC648,0xC650,0xC651,0xC653,0xC654,0xC655,0xC65C,0xC65D,/* 0xD0-0xD7 */
+ 0xC660,0xC66C,0xC66F,0xC671,0xC678,0xC679,0xC67C,0xC680,/* 0xD8-0xDF */
+ 0xC688,0xC689,0xC68B,0xC68D,0xC694,0xC695,0xC698,0xC69C,/* 0xE0-0xE7 */
+ 0xC6A4,0xC6A5,0xC6A7,0xC6A9,0xC6B0,0xC6B1,0xC6B4,0xC6B8,/* 0xE8-0xEF */
+ 0xC6B9,0xC6BA,0xC6C0,0xC6C1,0xC6C3,0xC6C5,0xC6CC,0xC6CD,/* 0xF0-0xF7 */
+ 0xC6D0,0xC6D4,0xC6DC,0xC6DD,0xC6E0,0xC6E1,0xC6E8,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_C0[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xD4FE,0xD4FF,0xD500,0xD501,0xD502,0xD503,0xD505,/* 0x40-0x47 */
+ 0xD506,0xD507,0xD509,0xD50A,0xD50B,0xD50D,0xD50E,0xD50F,/* 0x48-0x4F */
+ 0xD510,0xD511,0xD512,0xD513,0xD516,0xD518,0xD519,0xD51A,/* 0x50-0x57 */
+ 0xD51B,0xD51C,0xD51D,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xD51E,0xD51F,0xD520,0xD521,0xD522,0xD523,0xD524,/* 0x60-0x67 */
+ 0xD525,0xD526,0xD527,0xD528,0xD529,0xD52A,0xD52B,0xD52C,/* 0x68-0x6F */
+ 0xD52D,0xD52E,0xD52F,0xD530,0xD531,0xD532,0xD533,0xD534,/* 0x70-0x77 */
+ 0xD535,0xD536,0xD537,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xD538,0xD539,0xD53A,0xD53B,0xD53E,0xD53F,0xD541,/* 0x80-0x87 */
+ 0xD542,0xD543,0xD545,0xD546,0xD547,0xD548,0xD549,0xD54A,/* 0x88-0x8F */
+ 0xD54B,0xD54E,0xD550,0xD552,0xD553,0xD554,0xD555,0xD556,/* 0x90-0x97 */
+ 0xD557,0xD55A,0xD55B,0xD55D,0xD55E,0xD55F,0xD561,0xD562,/* 0x98-0x9F */
+ 0xD563,0xC6E9,0xC6EC,0xC6F0,0xC6F8,0xC6F9,0xC6FD,0xC704,/* 0xA0-0xA7 */
+ 0xC705,0xC708,0xC70C,0xC714,0xC715,0xC717,0xC719,0xC720,/* 0xA8-0xAF */
+ 0xC721,0xC724,0xC728,0xC730,0xC731,0xC733,0xC735,0xC737,/* 0xB0-0xB7 */
+ 0xC73C,0xC73D,0xC740,0xC744,0xC74A,0xC74C,0xC74D,0xC74F,/* 0xB8-0xBF */
+ 0xC751,0xC752,0xC753,0xC754,0xC755,0xC756,0xC757,0xC758,/* 0xC0-0xC7 */
+ 0xC75C,0xC760,0xC768,0xC76B,0xC774,0xC775,0xC778,0xC77C,/* 0xC8-0xCF */
+ 0xC77D,0xC77E,0xC783,0xC784,0xC785,0xC787,0xC788,0xC789,/* 0xD0-0xD7 */
+ 0xC78A,0xC78E,0xC790,0xC791,0xC794,0xC796,0xC797,0xC798,/* 0xD8-0xDF */
+ 0xC79A,0xC7A0,0xC7A1,0xC7A3,0xC7A4,0xC7A5,0xC7A6,0xC7AC,/* 0xE0-0xE7 */
+ 0xC7AD,0xC7B0,0xC7B4,0xC7BC,0xC7BD,0xC7BF,0xC7C0,0xC7C1,/* 0xE8-0xEF */
+ 0xC7C8,0xC7C9,0xC7CC,0xC7CE,0xC7D0,0xC7D8,0xC7DD,0xC7E4,/* 0xF0-0xF7 */
+ 0xC7E8,0xC7EC,0xC800,0xC801,0xC804,0xC808,0xC80A,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_C1[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xD564,0xD566,0xD567,0xD56A,0xD56C,0xD56E,0xD56F,/* 0x40-0x47 */
+ 0xD570,0xD571,0xD572,0xD573,0xD576,0xD577,0xD579,0xD57A,/* 0x48-0x4F */
+ 0xD57B,0xD57D,0xD57E,0xD57F,0xD580,0xD581,0xD582,0xD583,/* 0x50-0x57 */
+ 0xD586,0xD58A,0xD58B,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xD58C,0xD58D,0xD58E,0xD58F,0xD591,0xD592,0xD593,/* 0x60-0x67 */
+ 0xD594,0xD595,0xD596,0xD597,0xD598,0xD599,0xD59A,0xD59B,/* 0x68-0x6F */
+ 0xD59C,0xD59D,0xD59E,0xD59F,0xD5A0,0xD5A1,0xD5A2,0xD5A3,/* 0x70-0x77 */
+ 0xD5A4,0xD5A6,0xD5A7,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xD5A8,0xD5A9,0xD5AA,0xD5AB,0xD5AC,0xD5AD,0xD5AE,/* 0x80-0x87 */
+ 0xD5AF,0xD5B0,0xD5B1,0xD5B2,0xD5B3,0xD5B4,0xD5B5,0xD5B6,/* 0x88-0x8F */
+ 0xD5B7,0xD5B8,0xD5B9,0xD5BA,0xD5BB,0xD5BC,0xD5BD,0xD5BE,/* 0x90-0x97 */
+ 0xD5BF,0xD5C0,0xD5C1,0xD5C2,0xD5C3,0xD5C4,0xD5C5,0xD5C6,/* 0x98-0x9F */
+ 0xD5C7,0xC810,0xC811,0xC813,0xC815,0xC816,0xC81C,0xC81D,/* 0xA0-0xA7 */
+ 0xC820,0xC824,0xC82C,0xC82D,0xC82F,0xC831,0xC838,0xC83C,/* 0xA8-0xAF */
+ 0xC840,0xC848,0xC849,0xC84C,0xC84D,0xC854,0xC870,0xC871,/* 0xB0-0xB7 */
+ 0xC874,0xC878,0xC87A,0xC880,0xC881,0xC883,0xC885,0xC886,/* 0xB8-0xBF */
+ 0xC887,0xC88B,0xC88C,0xC88D,0xC894,0xC89D,0xC89F,0xC8A1,/* 0xC0-0xC7 */
+ 0xC8A8,0xC8BC,0xC8BD,0xC8C4,0xC8C8,0xC8CC,0xC8D4,0xC8D5,/* 0xC8-0xCF */
+ 0xC8D7,0xC8D9,0xC8E0,0xC8E1,0xC8E4,0xC8F5,0xC8FC,0xC8FD,/* 0xD0-0xD7 */
+ 0xC900,0xC904,0xC905,0xC906,0xC90C,0xC90D,0xC90F,0xC911,/* 0xD8-0xDF */
+ 0xC918,0xC92C,0xC934,0xC950,0xC951,0xC954,0xC958,0xC960,/* 0xE0-0xE7 */
+ 0xC961,0xC963,0xC96C,0xC970,0xC974,0xC97C,0xC988,0xC989,/* 0xE8-0xEF */
+ 0xC98C,0xC990,0xC998,0xC999,0xC99B,0xC99D,0xC9C0,0xC9C1,/* 0xF0-0xF7 */
+ 0xC9C4,0xC9C7,0xC9C8,0xC9CA,0xC9D0,0xC9D1,0xC9D3,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_C2[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xD5CA,0xD5CB,0xD5CD,0xD5CE,0xD5CF,0xD5D1,0xD5D3,/* 0x40-0x47 */
+ 0xD5D4,0xD5D5,0xD5D6,0xD5D7,0xD5DA,0xD5DC,0xD5DE,0xD5DF,/* 0x48-0x4F */
+ 0xD5E0,0xD5E1,0xD5E2,0xD5E3,0xD5E6,0xD5E7,0xD5E9,0xD5EA,/* 0x50-0x57 */
+ 0xD5EB,0xD5ED,0xD5EE,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xD5EF,0xD5F0,0xD5F1,0xD5F2,0xD5F3,0xD5F6,0xD5F8,/* 0x60-0x67 */
+ 0xD5FA,0xD5FB,0xD5FC,0xD5FD,0xD5FE,0xD5FF,0xD602,0xD603,/* 0x68-0x6F */
+ 0xD605,0xD606,0xD607,0xD609,0xD60A,0xD60B,0xD60C,0xD60D,/* 0x70-0x77 */
+ 0xD60E,0xD60F,0xD612,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xD616,0xD617,0xD618,0xD619,0xD61A,0xD61B,0xD61D,/* 0x80-0x87 */
+ 0xD61E,0xD61F,0xD621,0xD622,0xD623,0xD625,0xD626,0xD627,/* 0x88-0x8F */
+ 0xD628,0xD629,0xD62A,0xD62B,0xD62C,0xD62E,0xD62F,0xD630,/* 0x90-0x97 */
+ 0xD631,0xD632,0xD633,0xD634,0xD635,0xD636,0xD637,0xD63A,/* 0x98-0x9F */
+ 0xD63B,0xC9D5,0xC9D6,0xC9D9,0xC9DA,0xC9DC,0xC9DD,0xC9E0,/* 0xA0-0xA7 */
+ 0xC9E2,0xC9E4,0xC9E7,0xC9EC,0xC9ED,0xC9EF,0xC9F0,0xC9F1,/* 0xA8-0xAF */
+ 0xC9F8,0xC9F9,0xC9FC,0xCA00,0xCA08,0xCA09,0xCA0B,0xCA0C,/* 0xB0-0xB7 */
+ 0xCA0D,0xCA14,0xCA18,0xCA29,0xCA4C,0xCA4D,0xCA50,0xCA54,/* 0xB8-0xBF */
+ 0xCA5C,0xCA5D,0xCA5F,0xCA60,0xCA61,0xCA68,0xCA7D,0xCA84,/* 0xC0-0xC7 */
+ 0xCA98,0xCABC,0xCABD,0xCAC0,0xCAC4,0xCACC,0xCACD,0xCACF,/* 0xC8-0xCF */
+ 0xCAD1,0xCAD3,0xCAD8,0xCAD9,0xCAE0,0xCAEC,0xCAF4,0xCB08,/* 0xD0-0xD7 */
+ 0xCB10,0xCB14,0xCB18,0xCB20,0xCB21,0xCB41,0xCB48,0xCB49,/* 0xD8-0xDF */
+ 0xCB4C,0xCB50,0xCB58,0xCB59,0xCB5D,0xCB64,0xCB78,0xCB79,/* 0xE0-0xE7 */
+ 0xCB9C,0xCBB8,0xCBD4,0xCBE4,0xCBE7,0xCBE9,0xCC0C,0xCC0D,/* 0xE8-0xEF */
+ 0xCC10,0xCC14,0xCC1C,0xCC1D,0xCC21,0xCC22,0xCC27,0xCC28,/* 0xF0-0xF7 */
+ 0xCC29,0xCC2C,0xCC2E,0xCC30,0xCC38,0xCC39,0xCC3B,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_C3[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xD63D,0xD63E,0xD63F,0xD641,0xD642,0xD643,0xD644,/* 0x40-0x47 */
+ 0xD646,0xD647,0xD64A,0xD64C,0xD64E,0xD64F,0xD650,0xD652,/* 0x48-0x4F */
+ 0xD653,0xD656,0xD657,0xD659,0xD65A,0xD65B,0xD65D,0xD65E,/* 0x50-0x57 */
+ 0xD65F,0xD660,0xD661,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xD662,0xD663,0xD664,0xD665,0xD666,0xD668,0xD66A,/* 0x60-0x67 */
+ 0xD66B,0xD66C,0xD66D,0xD66E,0xD66F,0xD672,0xD673,0xD675,/* 0x68-0x6F */
+ 0xD676,0xD677,0xD678,0xD679,0xD67A,0xD67B,0xD67C,0xD67D,/* 0x70-0x77 */
+ 0xD67E,0xD67F,0xD680,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xD681,0xD682,0xD684,0xD686,0xD687,0xD688,0xD689,/* 0x80-0x87 */
+ 0xD68A,0xD68B,0xD68E,0xD68F,0xD691,0xD692,0xD693,0xD695,/* 0x88-0x8F */
+ 0xD696,0xD697,0xD698,0xD699,0xD69A,0xD69B,0xD69C,0xD69E,/* 0x90-0x97 */
+ 0xD6A0,0xD6A2,0xD6A3,0xD6A4,0xD6A5,0xD6A6,0xD6A7,0xD6A9,/* 0x98-0x9F */
+ 0xD6AA,0xCC3C,0xCC3D,0xCC3E,0xCC44,0xCC45,0xCC48,0xCC4C,/* 0xA0-0xA7 */
+ 0xCC54,0xCC55,0xCC57,0xCC58,0xCC59,0xCC60,0xCC64,0xCC66,/* 0xA8-0xAF */
+ 0xCC68,0xCC70,0xCC75,0xCC98,0xCC99,0xCC9C,0xCCA0,0xCCA8,/* 0xB0-0xB7 */
+ 0xCCA9,0xCCAB,0xCCAC,0xCCAD,0xCCB4,0xCCB5,0xCCB8,0xCCBC,/* 0xB8-0xBF */
+ 0xCCC4,0xCCC5,0xCCC7,0xCCC9,0xCCD0,0xCCD4,0xCCE4,0xCCEC,/* 0xC0-0xC7 */
+ 0xCCF0,0xCD01,0xCD08,0xCD09,0xCD0C,0xCD10,0xCD18,0xCD19,/* 0xC8-0xCF */
+ 0xCD1B,0xCD1D,0xCD24,0xCD28,0xCD2C,0xCD39,0xCD5C,0xCD60,/* 0xD0-0xD7 */
+ 0xCD64,0xCD6C,0xCD6D,0xCD6F,0xCD71,0xCD78,0xCD88,0xCD94,/* 0xD8-0xDF */
+ 0xCD95,0xCD98,0xCD9C,0xCDA4,0xCDA5,0xCDA7,0xCDA9,0xCDB0,/* 0xE0-0xE7 */
+ 0xCDC4,0xCDCC,0xCDD0,0xCDE8,0xCDEC,0xCDF0,0xCDF8,0xCDF9,/* 0xE8-0xEF */
+ 0xCDFB,0xCDFD,0xCE04,0xCE08,0xCE0C,0xCE14,0xCE19,0xCE20,/* 0xF0-0xF7 */
+ 0xCE21,0xCE24,0xCE28,0xCE30,0xCE31,0xCE33,0xCE35,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_C4[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xD6AB,0xD6AD,0xD6AE,0xD6AF,0xD6B1,0xD6B2,0xD6B3,/* 0x40-0x47 */
+ 0xD6B4,0xD6B5,0xD6B6,0xD6B7,0xD6B8,0xD6BA,0xD6BC,0xD6BD,/* 0x48-0x4F */
+ 0xD6BE,0xD6BF,0xD6C0,0xD6C1,0xD6C2,0xD6C3,0xD6C6,0xD6C7,/* 0x50-0x57 */
+ 0xD6C9,0xD6CA,0xD6CB,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xD6CD,0xD6CE,0xD6CF,0xD6D0,0xD6D2,0xD6D3,0xD6D5,/* 0x60-0x67 */
+ 0xD6D6,0xD6D8,0xD6DA,0xD6DB,0xD6DC,0xD6DD,0xD6DE,0xD6DF,/* 0x68-0x6F */
+ 0xD6E1,0xD6E2,0xD6E3,0xD6E5,0xD6E6,0xD6E7,0xD6E9,0xD6EA,/* 0x70-0x77 */
+ 0xD6EB,0xD6EC,0xD6ED,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xD6EE,0xD6EF,0xD6F1,0xD6F2,0xD6F3,0xD6F4,0xD6F6,/* 0x80-0x87 */
+ 0xD6F7,0xD6F8,0xD6F9,0xD6FA,0xD6FB,0xD6FE,0xD6FF,0xD701,/* 0x88-0x8F */
+ 0xD702,0xD703,0xD705,0xD706,0xD707,0xD708,0xD709,0xD70A,/* 0x90-0x97 */
+ 0xD70B,0xD70C,0xD70D,0xD70E,0xD70F,0xD710,0xD712,0xD713,/* 0x98-0x9F */
+ 0xD714,0xCE58,0xCE59,0xCE5C,0xCE5F,0xCE60,0xCE61,0xCE68,/* 0xA0-0xA7 */
+ 0xCE69,0xCE6B,0xCE6D,0xCE74,0xCE75,0xCE78,0xCE7C,0xCE84,/* 0xA8-0xAF */
+ 0xCE85,0xCE87,0xCE89,0xCE90,0xCE91,0xCE94,0xCE98,0xCEA0,/* 0xB0-0xB7 */
+ 0xCEA1,0xCEA3,0xCEA4,0xCEA5,0xCEAC,0xCEAD,0xCEC1,0xCEE4,/* 0xB8-0xBF */
+ 0xCEE5,0xCEE8,0xCEEB,0xCEEC,0xCEF4,0xCEF5,0xCEF7,0xCEF8,/* 0xC0-0xC7 */
+ 0xCEF9,0xCF00,0xCF01,0xCF04,0xCF08,0xCF10,0xCF11,0xCF13,/* 0xC8-0xCF */
+ 0xCF15,0xCF1C,0xCF20,0xCF24,0xCF2C,0xCF2D,0xCF2F,0xCF30,/* 0xD0-0xD7 */
+ 0xCF31,0xCF38,0xCF54,0xCF55,0xCF58,0xCF5C,0xCF64,0xCF65,/* 0xD8-0xDF */
+ 0xCF67,0xCF69,0xCF70,0xCF71,0xCF74,0xCF78,0xCF80,0xCF85,/* 0xE0-0xE7 */
+ 0xCF8C,0xCFA1,0xCFA8,0xCFB0,0xCFC4,0xCFE0,0xCFE1,0xCFE4,/* 0xE8-0xEF */
+ 0xCFE8,0xCFF0,0xCFF1,0xCFF3,0xCFF5,0xCFFC,0xD000,0xD004,/* 0xF0-0xF7 */
+ 0xD011,0xD018,0xD02D,0xD034,0xD035,0xD038,0xD03C,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_C5[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xD715,0xD716,0xD717,0xD71A,0xD71B,0xD71D,0xD71E,/* 0x40-0x47 */
+ 0xD71F,0xD721,0xD722,0xD723,0xD724,0xD725,0xD726,0xD727,/* 0x48-0x4F */
+ 0xD72A,0xD72C,0xD72E,0xD72F,0xD730,0xD731,0xD732,0xD733,/* 0x50-0x57 */
+ 0xD736,0xD737,0xD739,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0xD73A,0xD73B,0xD73D,0xD73E,0xD73F,0xD740,0xD741,/* 0x60-0x67 */
+ 0xD742,0xD743,0xD745,0xD746,0xD748,0xD74A,0xD74B,0xD74C,/* 0x68-0x6F */
+ 0xD74D,0xD74E,0xD74F,0xD752,0xD753,0xD755,0xD75A,0xD75B,/* 0x70-0x77 */
+ 0xD75C,0xD75D,0xD75E,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0xD75F,0xD762,0xD764,0xD766,0xD767,0xD768,0xD76A,/* 0x80-0x87 */
+ 0xD76B,0xD76D,0xD76E,0xD76F,0xD771,0xD772,0xD773,0xD775,/* 0x88-0x8F */
+ 0xD776,0xD777,0xD778,0xD779,0xD77A,0xD77B,0xD77E,0xD77F,/* 0x90-0x97 */
+ 0xD780,0xD782,0xD783,0xD784,0xD785,0xD786,0xD787,0xD78A,/* 0x98-0x9F */
+ 0xD78B,0xD044,0xD045,0xD047,0xD049,0xD050,0xD054,0xD058,/* 0xA0-0xA7 */
+ 0xD060,0xD06C,0xD06D,0xD070,0xD074,0xD07C,0xD07D,0xD081,/* 0xA8-0xAF */
+ 0xD0A4,0xD0A5,0xD0A8,0xD0AC,0xD0B4,0xD0B5,0xD0B7,0xD0B9,/* 0xB0-0xB7 */
+ 0xD0C0,0xD0C1,0xD0C4,0xD0C8,0xD0C9,0xD0D0,0xD0D1,0xD0D3,/* 0xB8-0xBF */
+ 0xD0D4,0xD0D5,0xD0DC,0xD0DD,0xD0E0,0xD0E4,0xD0EC,0xD0ED,/* 0xC0-0xC7 */
+ 0xD0EF,0xD0F0,0xD0F1,0xD0F8,0xD10D,0xD130,0xD131,0xD134,/* 0xC8-0xCF */
+ 0xD138,0xD13A,0xD140,0xD141,0xD143,0xD144,0xD145,0xD14C,/* 0xD0-0xD7 */
+ 0xD14D,0xD150,0xD154,0xD15C,0xD15D,0xD15F,0xD161,0xD168,/* 0xD8-0xDF */
+ 0xD16C,0xD17C,0xD184,0xD188,0xD1A0,0xD1A1,0xD1A4,0xD1A8,/* 0xE0-0xE7 */
+ 0xD1B0,0xD1B1,0xD1B3,0xD1B5,0xD1BA,0xD1BC,0xD1C0,0xD1D8,/* 0xE8-0xEF */
+ 0xD1F4,0xD1F8,0xD207,0xD209,0xD210,0xD22C,0xD22D,0xD230,/* 0xF0-0xF7 */
+ 0xD234,0xD23C,0xD23D,0xD23F,0xD241,0xD248,0xD25C,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_C6[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0xD78D,0xD78E,0xD78F,0xD791,0xD792,0xD793,0xD794,/* 0x40-0x47 */
+ 0xD795,0xD796,0xD797,0xD79A,0xD79C,0xD79E,0xD79F,0xD7A0,/* 0x48-0x4F */
+ 0xD7A1,0xD7A2,0xD7A3,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0xD264,0xD280,0xD281,0xD284,0xD288,0xD290,0xD291,/* 0xA0-0xA7 */
+ 0xD295,0xD29C,0xD2A0,0xD2A4,0xD2AC,0xD2B1,0xD2B8,0xD2B9,/* 0xA8-0xAF */
+ 0xD2BC,0xD2BF,0xD2C0,0xD2C2,0xD2C8,0xD2C9,0xD2CB,0xD2D4,/* 0xB0-0xB7 */
+ 0xD2D8,0xD2DC,0xD2E4,0xD2E5,0xD2F0,0xD2F1,0xD2F4,0xD2F8,/* 0xB8-0xBF */
+ 0xD300,0xD301,0xD303,0xD305,0xD30C,0xD30D,0xD30E,0xD310,/* 0xC0-0xC7 */
+ 0xD314,0xD316,0xD31C,0xD31D,0xD31F,0xD320,0xD321,0xD325,/* 0xC8-0xCF */
+ 0xD328,0xD329,0xD32C,0xD330,0xD338,0xD339,0xD33B,0xD33C,/* 0xD0-0xD7 */
+ 0xD33D,0xD344,0xD345,0xD37C,0xD37D,0xD380,0xD384,0xD38C,/* 0xD8-0xDF */
+ 0xD38D,0xD38F,0xD390,0xD391,0xD398,0xD399,0xD39C,0xD3A0,/* 0xE0-0xE7 */
+ 0xD3A8,0xD3A9,0xD3AB,0xD3AD,0xD3B4,0xD3B8,0xD3BC,0xD3C4,/* 0xE8-0xEF */
+ 0xD3C5,0xD3C8,0xD3C9,0xD3D0,0xD3D8,0xD3E1,0xD3E3,0xD3EC,/* 0xF0-0xF7 */
+ 0xD3ED,0xD3F0,0xD3F4,0xD3FC,0xD3FD,0xD3FF,0xD401,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_C7[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0xD408,0xD41D,0xD440,0xD444,0xD45C,0xD460,0xD464,/* 0xA0-0xA7 */
+ 0xD46D,0xD46F,0xD478,0xD479,0xD47C,0xD47F,0xD480,0xD482,/* 0xA8-0xAF */
+ 0xD488,0xD489,0xD48B,0xD48D,0xD494,0xD4A9,0xD4CC,0xD4D0,/* 0xB0-0xB7 */
+ 0xD4D4,0xD4DC,0xD4DF,0xD4E8,0xD4EC,0xD4F0,0xD4F8,0xD4FB,/* 0xB8-0xBF */
+ 0xD4FD,0xD504,0xD508,0xD50C,0xD514,0xD515,0xD517,0xD53C,/* 0xC0-0xC7 */
+ 0xD53D,0xD540,0xD544,0xD54C,0xD54D,0xD54F,0xD551,0xD558,/* 0xC8-0xCF */
+ 0xD559,0xD55C,0xD560,0xD565,0xD568,0xD569,0xD56B,0xD56D,/* 0xD0-0xD7 */
+ 0xD574,0xD575,0xD578,0xD57C,0xD584,0xD585,0xD587,0xD588,/* 0xD8-0xDF */
+ 0xD589,0xD590,0xD5A5,0xD5C8,0xD5C9,0xD5CC,0xD5D0,0xD5D2,/* 0xE0-0xE7 */
+ 0xD5D8,0xD5D9,0xD5DB,0xD5DD,0xD5E4,0xD5E5,0xD5E8,0xD5EC,/* 0xE8-0xEF */
+ 0xD5F4,0xD5F5,0xD5F7,0xD5F9,0xD600,0xD601,0xD604,0xD608,/* 0xF0-0xF7 */
+ 0xD610,0xD611,0xD613,0xD614,0xD615,0xD61C,0xD620,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_C8[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0xD624,0xD62D,0xD638,0xD639,0xD63C,0xD640,0xD645,/* 0xA0-0xA7 */
+ 0xD648,0xD649,0xD64B,0xD64D,0xD651,0xD654,0xD655,0xD658,/* 0xA8-0xAF */
+ 0xD65C,0xD667,0xD669,0xD670,0xD671,0xD674,0xD683,0xD685,/* 0xB0-0xB7 */
+ 0xD68C,0xD68D,0xD690,0xD694,0xD69D,0xD69F,0xD6A1,0xD6A8,/* 0xB8-0xBF */
+ 0xD6AC,0xD6B0,0xD6B9,0xD6BB,0xD6C4,0xD6C5,0xD6C8,0xD6CC,/* 0xC0-0xC7 */
+ 0xD6D1,0xD6D4,0xD6D7,0xD6D9,0xD6E0,0xD6E4,0xD6E8,0xD6F0,/* 0xC8-0xCF */
+ 0xD6F5,0xD6FC,0xD6FD,0xD700,0xD704,0xD711,0xD718,0xD719,/* 0xD0-0xD7 */
+ 0xD71C,0xD720,0xD728,0xD729,0xD72B,0xD72D,0xD734,0xD735,/* 0xD8-0xDF */
+ 0xD738,0xD73C,0xD744,0xD747,0xD749,0xD750,0xD751,0xD754,/* 0xE0-0xE7 */
+ 0xD756,0xD757,0xD758,0xD759,0xD760,0xD761,0xD763,0xD765,/* 0xE8-0xEF */
+ 0xD769,0xD76C,0xD770,0xD774,0xD77C,0xD77D,0xD781,0xD788,/* 0xF0-0xF7 */
+ 0xD789,0xD78C,0xD790,0xD798,0xD799,0xD79B,0xD79D,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_CA[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x4F3D,0x4F73,0x5047,0x50F9,0x52A0,0x53EF,0x5475,/* 0xA0-0xA7 */
+ 0x54E5,0x5609,0x5AC1,0x5BB6,0x6687,0x67B6,0x67B7,0x67EF,/* 0xA8-0xAF */
+ 0x6B4C,0x73C2,0x75C2,0x7A3C,0x82DB,0x8304,0x8857,0x8888,/* 0xB0-0xB7 */
+ 0x8A36,0x8CC8,0x8DCF,0x8EFB,0x8FE6,0x99D5,0x523B,0x5374,/* 0xB8-0xBF */
+ 0x5404,0x606A,0x6164,0x6BBC,0x73CF,0x811A,0x89BA,0x89D2,/* 0xC0-0xC7 */
+ 0x95A3,0x4F83,0x520A,0x58BE,0x5978,0x59E6,0x5E72,0x5E79,/* 0xC8-0xCF */
+ 0x61C7,0x63C0,0x6746,0x67EC,0x687F,0x6F97,0x764E,0x770B,/* 0xD0-0xD7 */
+ 0x78F5,0x7A08,0x7AFF,0x7C21,0x809D,0x826E,0x8271,0x8AEB,/* 0xD8-0xDF */
+ 0x9593,0x4E6B,0x559D,0x66F7,0x6E34,0x78A3,0x7AED,0x845B,/* 0xE0-0xE7 */
+ 0x8910,0x874E,0x97A8,0x52D8,0x574E,0x582A,0x5D4C,0x611F,/* 0xE8-0xEF */
+ 0x61BE,0x6221,0x6562,0x67D1,0x6A44,0x6E1B,0x7518,0x75B3,/* 0xF0-0xF7 */
+ 0x76E3,0x77B0,0x7D3A,0x90AF,0x9451,0x9452,0x9F95,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_CB[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x5323,0x5CAC,0x7532,0x80DB,0x9240,0x9598,0x525B,/* 0xA0-0xA7 */
+ 0x5808,0x59DC,0x5CA1,0x5D17,0x5EB7,0x5F3A,0x5F4A,0x6177,/* 0xA8-0xAF */
+ 0x6C5F,0x757A,0x7586,0x7CE0,0x7D73,0x7DB1,0x7F8C,0x8154,/* 0xB0-0xB7 */
+ 0x8221,0x8591,0x8941,0x8B1B,0x92FC,0x964D,0x9C47,0x4ECB,/* 0xB8-0xBF */
+ 0x4EF7,0x500B,0x51F1,0x584F,0x6137,0x613E,0x6168,0x6539,/* 0xC0-0xC7 */
+ 0x69EA,0x6F11,0x75A5,0x7686,0x76D6,0x7B87,0x82A5,0x84CB,/* 0xC8-0xCF */
+ 0xF900,0x93A7,0x958B,0x5580,0x5BA2,0x5751,0xF901,0x7CB3,/* 0xD0-0xD7 */
+ 0x7FB9,0x91B5,0x5028,0x53BB,0x5C45,0x5DE8,0x62D2,0x636E,/* 0xD8-0xDF */
+ 0x64DA,0x64E7,0x6E20,0x70AC,0x795B,0x8DDD,0x8E1E,0xF902,/* 0xE0-0xE7 */
+ 0x907D,0x9245,0x92F8,0x4E7E,0x4EF6,0x5065,0x5DFE,0x5EFA,/* 0xE8-0xEF */
+ 0x6106,0x6957,0x8171,0x8654,0x8E47,0x9375,0x9A2B,0x4E5E,/* 0xF0-0xF7 */
+ 0x5091,0x6770,0x6840,0x5109,0x528D,0x5292,0x6AA2,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_CC[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x77BC,0x9210,0x9ED4,0x52AB,0x602F,0x8FF2,0x5048,/* 0xA0-0xA7 */
+ 0x61A9,0x63ED,0x64CA,0x683C,0x6A84,0x6FC0,0x8188,0x89A1,/* 0xA8-0xAF */
+ 0x9694,0x5805,0x727D,0x72AC,0x7504,0x7D79,0x7E6D,0x80A9,/* 0xB0-0xB7 */
+ 0x898B,0x8B74,0x9063,0x9D51,0x6289,0x6C7A,0x6F54,0x7D50,/* 0xB8-0xBF */
+ 0x7F3A,0x8A23,0x517C,0x614A,0x7B9D,0x8B19,0x9257,0x938C,/* 0xC0-0xC7 */
+ 0x4EAC,0x4FD3,0x501E,0x50BE,0x5106,0x52C1,0x52CD,0x537F,/* 0xC8-0xCF */
+ 0x5770,0x5883,0x5E9A,0x5F91,0x6176,0x61AC,0x64CE,0x656C,/* 0xD0-0xD7 */
+ 0x666F,0x66BB,0x66F4,0x6897,0x6D87,0x7085,0x70F1,0x749F,/* 0xD8-0xDF */
+ 0x74A5,0x74CA,0x75D9,0x786C,0x78EC,0x7ADF,0x7AF6,0x7D45,/* 0xE0-0xE7 */
+ 0x7D93,0x8015,0x803F,0x811B,0x8396,0x8B66,0x8F15,0x9015,/* 0xE8-0xEF */
+ 0x93E1,0x9803,0x9838,0x9A5A,0x9BE8,0x4FC2,0x5553,0x583A,/* 0xF0-0xF7 */
+ 0x5951,0x5B63,0x5C46,0x60B8,0x6212,0x6842,0x68B0,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_CD[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x68E8,0x6EAA,0x754C,0x7678,0x78CE,0x7A3D,0x7CFB,/* 0xA0-0xA7 */
+ 0x7E6B,0x7E7C,0x8A08,0x8AA1,0x8C3F,0x968E,0x9DC4,0x53E4,/* 0xA8-0xAF */
+ 0x53E9,0x544A,0x5471,0x56FA,0x59D1,0x5B64,0x5C3B,0x5EAB,/* 0xB0-0xB7 */
+ 0x62F7,0x6537,0x6545,0x6572,0x66A0,0x67AF,0x69C1,0x6CBD,/* 0xB8-0xBF */
+ 0x75FC,0x7690,0x777E,0x7A3F,0x7F94,0x8003,0x80A1,0x818F,/* 0xC0-0xC7 */
+ 0x82E6,0x82FD,0x83F0,0x85C1,0x8831,0x88B4,0x8AA5,0xF903,/* 0xC8-0xCF */
+ 0x8F9C,0x932E,0x96C7,0x9867,0x9AD8,0x9F13,0x54ED,0x659B,/* 0xD0-0xD7 */
+ 0x66F2,0x688F,0x7A40,0x8C37,0x9D60,0x56F0,0x5764,0x5D11,/* 0xD8-0xDF */
+ 0x6606,0x68B1,0x68CD,0x6EFE,0x7428,0x889E,0x9BE4,0x6C68,/* 0xE0-0xE7 */
+ 0xF904,0x9AA8,0x4F9B,0x516C,0x5171,0x529F,0x5B54,0x5DE5,/* 0xE8-0xEF */
+ 0x6050,0x606D,0x62F1,0x63A7,0x653B,0x73D9,0x7A7A,0x86A3,/* 0xF0-0xF7 */
+ 0x8CA2,0x978F,0x4E32,0x5BE1,0x6208,0x679C,0x74DC,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_CE[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x79D1,0x83D3,0x8A87,0x8AB2,0x8DE8,0x904E,0x934B,/* 0xA0-0xA7 */
+ 0x9846,0x5ED3,0x69E8,0x85FF,0x90ED,0xF905,0x51A0,0x5B98,/* 0xA8-0xAF */
+ 0x5BEC,0x6163,0x68FA,0x6B3E,0x704C,0x742F,0x74D8,0x7BA1,/* 0xB0-0xB7 */
+ 0x7F50,0x83C5,0x89C0,0x8CAB,0x95DC,0xFA2C,0x522E,0x605D,/* 0xB8-0xBF */
+ 0x62EC,0x9002,0x4F8A,0x5149,0x5321,0x58D9,0x5EE3,0x66E0,/* 0xC0-0xC7 */
+ 0x6D38,0x709A,0x72C2,0x73D6,0x7B50,0x80F1,0x945B,0x5366,/* 0xC8-0xCF */
+ 0x639B,0x7F6B,0x4E56,0x5080,0x584A,0x58DE,0x602A,0x6127,/* 0xD0-0xD7 */
+ 0x62D0,0x69D0,0x9B41,0x5B8F,0x7D18,0x80B1,0x8F5F,0x4EA4,/* 0xD8-0xDF */
+ 0x50D1,0x54AC,0x55AC,0x5B0C,0x5DA0,0x5DE7,0x652A,0x654E,/* 0xE0-0xE7 */
+ 0x6821,0x6A4B,0x72E1,0x768E,0x77EF,0x7D5E,0x7FF9,0x81A0,/* 0xE8-0xEF */
+ 0x854E,0x86DF,0x8F03,0x8F4E,0x90CA,0x9903,0x9A55,0x9BAB,/* 0xF0-0xF7 */
+ 0x4E18,0x4E45,0x4E5D,0x4EC7,0x4FF1,0x5177,0x52FE,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_CF[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x5340,0x53E3,0x53E5,0x548E,0x5614,0x5775,0x57A2,/* 0xA0-0xA7 */
+ 0x5BC7,0x5D87,0x5ED0,0x61FC,0x62D8,0x6551,0x67B8,0x67E9,/* 0xA8-0xAF */
+ 0x69CB,0x6B50,0x6BC6,0x6BEC,0x6C42,0x6E9D,0x7078,0x72D7,/* 0xB0-0xB7 */
+ 0x7396,0x7403,0x77BF,0x77E9,0x7A76,0x7D7F,0x8009,0x81FC,/* 0xB8-0xBF */
+ 0x8205,0x820A,0x82DF,0x8862,0x8B33,0x8CFC,0x8EC0,0x9011,/* 0xC0-0xC7 */
+ 0x90B1,0x9264,0x92B6,0x99D2,0x9A45,0x9CE9,0x9DD7,0x9F9C,/* 0xC8-0xCF */
+ 0x570B,0x5C40,0x83CA,0x97A0,0x97AB,0x9EB4,0x541B,0x7A98,/* 0xD0-0xD7 */
+ 0x7FA4,0x88D9,0x8ECD,0x90E1,0x5800,0x5C48,0x6398,0x7A9F,/* 0xD8-0xDF */
+ 0x5BAE,0x5F13,0x7A79,0x7AAE,0x828E,0x8EAC,0x5026,0x5238,/* 0xE0-0xE7 */
+ 0x52F8,0x5377,0x5708,0x62F3,0x6372,0x6B0A,0x6DC3,0x7737,/* 0xE8-0xEF */
+ 0x53A5,0x7357,0x8568,0x8E76,0x95D5,0x673A,0x6AC3,0x6F70,/* 0xF0-0xF7 */
+ 0x8A6D,0x8ECC,0x994B,0xF906,0x6677,0x6B78,0x8CB4,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_D0[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x9B3C,0xF907,0x53EB,0x572D,0x594E,0x63C6,0x69FB,/* 0xA0-0xA7 */
+ 0x73EA,0x7845,0x7ABA,0x7AC5,0x7CFE,0x8475,0x898F,0x8D73,/* 0xA8-0xAF */
+ 0x9035,0x95A8,0x52FB,0x5747,0x7547,0x7B60,0x83CC,0x921E,/* 0xB0-0xB7 */
+ 0xF908,0x6A58,0x514B,0x524B,0x5287,0x621F,0x68D8,0x6975,/* 0xB8-0xBF */
+ 0x9699,0x50C5,0x52A4,0x52E4,0x61C3,0x65A4,0x6839,0x69FF,/* 0xC0-0xC7 */
+ 0x747E,0x7B4B,0x82B9,0x83EB,0x89B2,0x8B39,0x8FD1,0x9949,/* 0xC8-0xCF */
+ 0xF909,0x4ECA,0x5997,0x64D2,0x6611,0x6A8E,0x7434,0x7981,/* 0xD0-0xD7 */
+ 0x79BD,0x82A9,0x887E,0x887F,0x895F,0xF90A,0x9326,0x4F0B,/* 0xD8-0xDF */
+ 0x53CA,0x6025,0x6271,0x6C72,0x7D1A,0x7D66,0x4E98,0x5162,/* 0xE0-0xE7 */
+ 0x77DC,0x80AF,0x4F01,0x4F0E,0x5176,0x5180,0x55DC,0x5668,/* 0xE8-0xEF */
+ 0x573B,0x57FA,0x57FC,0x5914,0x5947,0x5993,0x5BC4,0x5C90,/* 0xF0-0xF7 */
+ 0x5D0E,0x5DF1,0x5E7E,0x5FCC,0x6280,0x65D7,0x65E3,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_D1[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x671E,0x671F,0x675E,0x68CB,0x68C4,0x6A5F,0x6B3A,/* 0xA0-0xA7 */
+ 0x6C23,0x6C7D,0x6C82,0x6DC7,0x7398,0x7426,0x742A,0x7482,/* 0xA8-0xAF */
+ 0x74A3,0x7578,0x757F,0x7881,0x78EF,0x7941,0x7947,0x7948,/* 0xB0-0xB7 */
+ 0x797A,0x7B95,0x7D00,0x7DBA,0x7F88,0x8006,0x802D,0x808C,/* 0xB8-0xBF */
+ 0x8A18,0x8B4F,0x8C48,0x8D77,0x9321,0x9324,0x98E2,0x9951,/* 0xC0-0xC7 */
+ 0x9A0E,0x9A0F,0x9A65,0x9E92,0x7DCA,0x4F76,0x5409,0x62EE,/* 0xC8-0xCF */
+ 0x6854,0x91D1,0x55AB,0x513A,0xF90B,0xF90C,0x5A1C,0x61E6,/* 0xD0-0xD7 */
+ 0xF90D,0x62CF,0x62FF,0xF90E,0xF90F,0xF910,0xF911,0xF912,/* 0xD8-0xDF */
+ 0xF913,0x90A3,0xF914,0xF915,0xF916,0xF917,0xF918,0x8AFE,/* 0xE0-0xE7 */
+ 0xF919,0xF91A,0xF91B,0xF91C,0x6696,0xF91D,0x7156,0xF91E,/* 0xE8-0xEF */
+ 0xF91F,0x96E3,0xF920,0x634F,0x637A,0x5357,0xF921,0x678F,/* 0xF0-0xF7 */
+ 0x6960,0x6E73,0xF922,0x7537,0xF923,0xF924,0xF925,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_D2[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x7D0D,0xF926,0xF927,0x8872,0x56CA,0x5A18,0xF928,/* 0xA0-0xA7 */
+ 0xF929,0xF92A,0xF92B,0xF92C,0x4E43,0xF92D,0x5167,0x5948,/* 0xA8-0xAF */
+ 0x67F0,0x8010,0xF92E,0x5973,0x5E74,0x649A,0x79CA,0x5FF5,/* 0xB0-0xB7 */
+ 0x606C,0x62C8,0x637B,0x5BE7,0x5BD7,0x52AA,0xF92F,0x5974,/* 0xB8-0xBF */
+ 0x5F29,0x6012,0xF930,0xF931,0xF932,0x7459,0xF933,0xF934,/* 0xC0-0xC7 */
+ 0xF935,0xF936,0xF937,0xF938,0x99D1,0xF939,0xF93A,0xF93B,/* 0xC8-0xCF */
+ 0xF93C,0xF93D,0xF93E,0xF93F,0xF940,0xF941,0xF942,0xF943,/* 0xD0-0xD7 */
+ 0x6FC3,0xF944,0xF945,0x81BF,0x8FB2,0x60F1,0xF946,0xF947,/* 0xD8-0xDF */
+ 0x8166,0xF948,0xF949,0x5C3F,0xF94A,0xF94B,0xF94C,0xF94D,/* 0xE0-0xE7 */
+ 0xF94E,0xF94F,0xF950,0xF951,0x5AE9,0x8A25,0x677B,0x7D10,/* 0xE8-0xEF */
+ 0xF952,0xF953,0xF954,0xF955,0xF956,0xF957,0x80FD,0xF958,/* 0xF0-0xF7 */
+ 0xF959,0x5C3C,0x6CE5,0x533F,0x6EBA,0x591A,0x8336,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_D3[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x4E39,0x4EB6,0x4F46,0x55AE,0x5718,0x58C7,0x5F56,/* 0xA0-0xA7 */
+ 0x65B7,0x65E6,0x6A80,0x6BB5,0x6E4D,0x77ED,0x7AEF,0x7C1E,/* 0xA8-0xAF */
+ 0x7DDE,0x86CB,0x8892,0x9132,0x935B,0x64BB,0x6FBE,0x737A,/* 0xB0-0xB7 */
+ 0x75B8,0x9054,0x5556,0x574D,0x61BA,0x64D4,0x66C7,0x6DE1,/* 0xB8-0xBF */
+ 0x6E5B,0x6F6D,0x6FB9,0x75F0,0x8043,0x81BD,0x8541,0x8983,/* 0xC0-0xC7 */
+ 0x8AC7,0x8B5A,0x931F,0x6C93,0x7553,0x7B54,0x8E0F,0x905D,/* 0xC8-0xCF */
+ 0x5510,0x5802,0x5858,0x5E62,0x6207,0x649E,0x68E0,0x7576,/* 0xD0-0xD7 */
+ 0x7CD6,0x87B3,0x9EE8,0x4EE3,0x5788,0x576E,0x5927,0x5C0D,/* 0xD8-0xDF */
+ 0x5CB1,0x5E36,0x5F85,0x6234,0x64E1,0x73B3,0x81FA,0x888B,/* 0xE0-0xE7 */
+ 0x8CB8,0x968A,0x9EDB,0x5B85,0x5FB7,0x60B3,0x5012,0x5200,/* 0xE8-0xEF */
+ 0x5230,0x5716,0x5835,0x5857,0x5C0E,0x5C60,0x5CF6,0x5D8B,/* 0xF0-0xF7 */
+ 0x5EA6,0x5F92,0x60BC,0x6311,0x6389,0x6417,0x6843,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_D4[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x68F9,0x6AC2,0x6DD8,0x6E21,0x6ED4,0x6FE4,0x71FE,/* 0xA0-0xA7 */
+ 0x76DC,0x7779,0x79B1,0x7A3B,0x8404,0x89A9,0x8CED,0x8DF3,/* 0xA8-0xAF */
+ 0x8E48,0x9003,0x9014,0x9053,0xFA26,0x934D,0x9676,0x97DC,/* 0xB0-0xB7 */
+ 0x6BD2,0x7006,0x7258,0x72A2,0x7368,0x7763,0x79BF,0x7BE4,/* 0xB8-0xBF */
+ 0x7E9B,0x8B80,0x58A9,0x60C7,0x6566,0x65FD,0x66BE,0x6C8C,/* 0xC0-0xC7 */
+ 0x711E,0x71C9,0x8C5A,0x9813,0x4E6D,0x7A81,0x4EDD,0x51AC,/* 0xC8-0xCF */
+ 0x51CD,0x52D5,0x540C,0x61A7,0x6771,0x6850,0x68DF,0x6D1E,/* 0xD0-0xD7 */
+ 0x6F7C,0x75BC,0x77B3,0x7AE5,0x80F4,0x8463,0x9285,0x515C,/* 0xD8-0xDF */
+ 0x6597,0x675C,0x6793,0x75D8,0x7AC7,0x8373,0xF95A,0x8C46,/* 0xE0-0xE7 */
+ 0x9017,0x982D,0x5C6F,0x81C0,0x829A,0x9041,0x906F,0x920D,/* 0xE8-0xEF */
+ 0x5F97,0x5D9D,0x6A59,0x71C8,0x767B,0x7B49,0x85E4,0x8B04,/* 0xF0-0xF7 */
+ 0x9127,0x9A30,0x5587,0x61F6,0xF95B,0x7669,0x7F85,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_D5[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x863F,0x87BA,0x88F8,0x908F,0xF95C,0x6D1B,0x70D9,/* 0xA0-0xA7 */
+ 0x73DE,0x7D61,0x843D,0xF95D,0x916A,0x99F1,0xF95E,0x4E82,/* 0xA8-0xAF */
+ 0x5375,0x6B04,0x6B12,0x703E,0x721B,0x862D,0x9E1E,0x524C,/* 0xB0-0xB7 */
+ 0x8FA3,0x5D50,0x64E5,0x652C,0x6B16,0x6FEB,0x7C43,0x7E9C,/* 0xB8-0xBF */
+ 0x85CD,0x8964,0x89BD,0x62C9,0x81D8,0x881F,0x5ECA,0x6717,/* 0xC0-0xC7 */
+ 0x6D6A,0x72FC,0x7405,0x746F,0x8782,0x90DE,0x4F86,0x5D0D,/* 0xC8-0xCF */
+ 0x5FA0,0x840A,0x51B7,0x63A0,0x7565,0x4EAE,0x5006,0x5169,/* 0xD0-0xD7 */
+ 0x51C9,0x6881,0x6A11,0x7CAE,0x7CB1,0x7CE7,0x826F,0x8AD2,/* 0xD8-0xDF */
+ 0x8F1B,0x91CF,0x4FB6,0x5137,0x52F5,0x5442,0x5EEC,0x616E,/* 0xE0-0xE7 */
+ 0x623E,0x65C5,0x6ADA,0x6FFE,0x792A,0x85DC,0x8823,0x95AD,/* 0xE8-0xEF */
+ 0x9A62,0x9A6A,0x9E97,0x9ECE,0x529B,0x66C6,0x6B77,0x701D,/* 0xF0-0xF7 */
+ 0x792B,0x8F62,0x9742,0x6190,0x6200,0x6523,0x6F23,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_D6[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x7149,0x7489,0x7DF4,0x806F,0x84EE,0x8F26,0x9023,/* 0xA0-0xA7 */
+ 0x934A,0x51BD,0x5217,0x52A3,0x6D0C,0x70C8,0x88C2,0x5EC9,/* 0xA8-0xAF */
+ 0x6582,0x6BAE,0x6FC2,0x7C3E,0x7375,0x4EE4,0x4F36,0x56F9,/* 0xB0-0xB7 */
+ 0xF95F,0x5CBA,0x5DBA,0x601C,0x73B2,0x7B2D,0x7F9A,0x7FCE,/* 0xB8-0xBF */
+ 0x8046,0x901E,0x9234,0x96F6,0x9748,0x9818,0x9F61,0x4F8B,/* 0xC0-0xC7 */
+ 0x6FA7,0x79AE,0x91B4,0x96B7,0x52DE,0xF960,0x6488,0x64C4,/* 0xC8-0xCF */
+ 0x6AD3,0x6F5E,0x7018,0x7210,0x76E7,0x8001,0x8606,0x865C,/* 0xD0-0xD7 */
+ 0x8DEF,0x8F05,0x9732,0x9B6F,0x9DFA,0x9E75,0x788C,0x797F,/* 0xD8-0xDF */
+ 0x7DA0,0x83C9,0x9304,0x9E7F,0x9E93,0x8AD6,0x58DF,0x5F04,/* 0xE0-0xE7 */
+ 0x6727,0x7027,0x74CF,0x7C60,0x807E,0x5121,0x7028,0x7262,/* 0xE8-0xEF */
+ 0x78CA,0x8CC2,0x8CDA,0x8CF4,0x96F7,0x4E86,0x50DA,0x5BEE,/* 0xF0-0xF7 */
+ 0x5ED6,0x6599,0x71CE,0x7642,0x77AD,0x804A,0x84FC,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_D7[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x907C,0x9B27,0x9F8D,0x58D8,0x5A41,0x5C62,0x6A13,/* 0xA0-0xA7 */
+ 0x6DDA,0x6F0F,0x763B,0x7D2F,0x7E37,0x851E,0x8938,0x93E4,/* 0xA8-0xAF */
+ 0x964B,0x5289,0x65D2,0x67F3,0x69B4,0x6D41,0x6E9C,0x700F,/* 0xB0-0xB7 */
+ 0x7409,0x7460,0x7559,0x7624,0x786B,0x8B2C,0x985E,0x516D,/* 0xB8-0xBF */
+ 0x622E,0x9678,0x4F96,0x502B,0x5D19,0x6DEA,0x7DB8,0x8F2A,/* 0xC0-0xC7 */
+ 0x5F8B,0x6144,0x6817,0xF961,0x9686,0x52D2,0x808B,0x51DC,/* 0xC8-0xCF */
+ 0x51CC,0x695E,0x7A1C,0x7DBE,0x83F1,0x9675,0x4FDA,0x5229,/* 0xD0-0xD7 */
+ 0x5398,0x540F,0x550E,0x5C65,0x60A7,0x674E,0x68A8,0x6D6C,/* 0xD8-0xDF */
+ 0x7281,0x72F8,0x7406,0x7483,0xF962,0x75E2,0x7C6C,0x7F79,/* 0xE0-0xE7 */
+ 0x7FB8,0x8389,0x88CF,0x88E1,0x91CC,0x91D0,0x96E2,0x9BC9,/* 0xE8-0xEF */
+ 0x541D,0x6F7E,0x71D0,0x7498,0x85FA,0x8EAA,0x96A3,0x9C57,/* 0xF0-0xF7 */
+ 0x9E9F,0x6797,0x6DCB,0x7433,0x81E8,0x9716,0x782C,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_D8[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x7ACB,0x7B20,0x7C92,0x6469,0x746A,0x75F2,0x78BC,/* 0xA0-0xA7 */
+ 0x78E8,0x99AC,0x9B54,0x9EBB,0x5BDE,0x5E55,0x6F20,0x819C,/* 0xA8-0xAF */
+ 0x83AB,0x9088,0x4E07,0x534D,0x5A29,0x5DD2,0x5F4E,0x6162,/* 0xB0-0xB7 */
+ 0x633D,0x6669,0x66FC,0x6EFF,0x6F2B,0x7063,0x779E,0x842C,/* 0xB8-0xBF */
+ 0x8513,0x883B,0x8F13,0x9945,0x9C3B,0x551C,0x62B9,0x672B,/* 0xC0-0xC7 */
+ 0x6CAB,0x8309,0x896A,0x977A,0x4EA1,0x5984,0x5FD8,0x5FD9,/* 0xC8-0xCF */
+ 0x671B,0x7DB2,0x7F54,0x8292,0x832B,0x83BD,0x8F1E,0x9099,/* 0xD0-0xD7 */
+ 0x57CB,0x59B9,0x5A92,0x5BD0,0x6627,0x679A,0x6885,0x6BCF,/* 0xD8-0xDF */
+ 0x7164,0x7F75,0x8CB7,0x8CE3,0x9081,0x9B45,0x8108,0x8C8A,/* 0xE0-0xE7 */
+ 0x964C,0x9A40,0x9EA5,0x5B5F,0x6C13,0x731B,0x76F2,0x76DF,/* 0xE8-0xEF */
+ 0x840C,0x51AA,0x8993,0x514D,0x5195,0x52C9,0x68C9,0x6C94,/* 0xF0-0xF7 */
+ 0x7704,0x7720,0x7DBF,0x7DEC,0x9762,0x9EB5,0x6EC5,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_D9[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x8511,0x51A5,0x540D,0x547D,0x660E,0x669D,0x6927,/* 0xA0-0xA7 */
+ 0x6E9F,0x76BF,0x7791,0x8317,0x84C2,0x879F,0x9169,0x9298,/* 0xA8-0xAF */
+ 0x9CF4,0x8882,0x4FAE,0x5192,0x52DF,0x59C6,0x5E3D,0x6155,/* 0xB0-0xB7 */
+ 0x6478,0x6479,0x66AE,0x67D0,0x6A21,0x6BCD,0x6BDB,0x725F,/* 0xB8-0xBF */
+ 0x7261,0x7441,0x7738,0x77DB,0x8017,0x82BC,0x8305,0x8B00,/* 0xC0-0xC7 */
+ 0x8B28,0x8C8C,0x6728,0x6C90,0x7267,0x76EE,0x7766,0x7A46,/* 0xC8-0xCF */
+ 0x9DA9,0x6B7F,0x6C92,0x5922,0x6726,0x8499,0x536F,0x5893,/* 0xD0-0xD7 */
+ 0x5999,0x5EDF,0x63CF,0x6634,0x6773,0x6E3A,0x732B,0x7AD7,/* 0xD8-0xDF */
+ 0x82D7,0x9328,0x52D9,0x5DEB,0x61AE,0x61CB,0x620A,0x62C7,/* 0xE0-0xE7 */
+ 0x64AB,0x65E0,0x6959,0x6B66,0x6BCB,0x7121,0x73F7,0x755D,/* 0xE8-0xEF */
+ 0x7E46,0x821E,0x8302,0x856A,0x8AA3,0x8CBF,0x9727,0x9D61,/* 0xF0-0xF7 */
+ 0x58A8,0x9ED8,0x5011,0x520E,0x543B,0x554F,0x6587,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_DA[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x6C76,0x7D0A,0x7D0B,0x805E,0x868A,0x9580,0x96EF,/* 0xA0-0xA7 */
+ 0x52FF,0x6C95,0x7269,0x5473,0x5A9A,0x5C3E,0x5D4B,0x5F4C,/* 0xA8-0xAF */
+ 0x5FAE,0x672A,0x68B6,0x6963,0x6E3C,0x6E44,0x7709,0x7C73,/* 0xB0-0xB7 */
+ 0x7F8E,0x8587,0x8B0E,0x8FF7,0x9761,0x9EF4,0x5CB7,0x60B6,/* 0xB8-0xBF */
+ 0x610D,0x61AB,0x654F,0x65FB,0x65FC,0x6C11,0x6CEF,0x739F,/* 0xC0-0xC7 */
+ 0x73C9,0x7DE1,0x9594,0x5BC6,0x871C,0x8B10,0x525D,0x535A,/* 0xC8-0xCF */
+ 0x62CD,0x640F,0x64B2,0x6734,0x6A38,0x6CCA,0x73C0,0x749E,/* 0xD0-0xD7 */
+ 0x7B94,0x7C95,0x7E1B,0x818A,0x8236,0x8584,0x8FEB,0x96F9,/* 0xD8-0xDF */
+ 0x99C1,0x4F34,0x534A,0x53CD,0x53DB,0x62CC,0x642C,0x6500,/* 0xE0-0xE7 */
+ 0x6591,0x69C3,0x6CEE,0x6F58,0x73ED,0x7554,0x7622,0x76E4,/* 0xE8-0xEF */
+ 0x76FC,0x78D0,0x78FB,0x792C,0x7D46,0x822C,0x87E0,0x8FD4,/* 0xF0-0xF7 */
+ 0x9812,0xFA2A,0x52C3,0x62D4,0x64A5,0x6E24,0x6F51,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_DB[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x767C,0x8DCB,0x91B1,0x9262,0x9AEE,0x9B43,0x5023,/* 0xA0-0xA7 */
+ 0x508D,0x574A,0x59A8,0x5C28,0x5E47,0x5F77,0x623F,0x653E,/* 0xA8-0xAF */
+ 0x65B9,0x65C1,0x6609,0x678B,0x699C,0x6EC2,0x78C5,0x7D21,/* 0xB0-0xB7 */
+ 0x80AA,0x8180,0x822B,0x82B3,0x84A1,0x868C,0x8A2A,0x8B17,/* 0xB8-0xBF */
+ 0x90A6,0x9632,0x9F90,0x500D,0x4FF3,0xF963,0x57F9,0x5F98,/* 0xC0-0xC7 */
+ 0x62DC,0x6392,0x676F,0x6E43,0x7119,0x76C3,0x80CC,0x80DA,/* 0xC8-0xCF */
+ 0x88F4,0x88F5,0x8919,0x8CE0,0x8F29,0x914D,0x966A,0x4F2F,/* 0xD0-0xD7 */
+ 0x4F70,0x5E1B,0x67CF,0x6822,0x767D,0x767E,0x9B44,0x5E61,/* 0xD8-0xDF */
+ 0x6A0A,0x7169,0x71D4,0x756A,0xF964,0x7E41,0x8543,0x85E9,/* 0xE0-0xE7 */
+ 0x98DC,0x4F10,0x7B4F,0x7F70,0x95A5,0x51E1,0x5E06,0x68B5,/* 0xE8-0xEF */
+ 0x6C3E,0x6C4E,0x6CDB,0x72AF,0x7BC4,0x8303,0x6CD5,0x743A,/* 0xF0-0xF7 */
+ 0x50FB,0x5288,0x58C1,0x64D8,0x6A97,0x74A7,0x7656,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_DC[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x78A7,0x8617,0x95E2,0x9739,0xF965,0x535E,0x5F01,/* 0xA0-0xA7 */
+ 0x8B8A,0x8FA8,0x8FAF,0x908A,0x5225,0x77A5,0x9C49,0x9F08,/* 0xA8-0xAF */
+ 0x4E19,0x5002,0x5175,0x5C5B,0x5E77,0x661E,0x663A,0x67C4,/* 0xB0-0xB7 */
+ 0x68C5,0x70B3,0x7501,0x75C5,0x79C9,0x7ADD,0x8F27,0x9920,/* 0xB8-0xBF */
+ 0x9A08,0x4FDD,0x5821,0x5831,0x5BF6,0x666E,0x6B65,0x6D11,/* 0xC0-0xC7 */
+ 0x6E7A,0x6F7D,0x73E4,0x752B,0x83E9,0x88DC,0x8913,0x8B5C,/* 0xC8-0xCF */
+ 0x8F14,0x4F0F,0x50D5,0x5310,0x535C,0x5B93,0x5FA9,0x670D,/* 0xD0-0xD7 */
+ 0xFA1B,0x8179,0x832F,0x8514,0x8907,0x8986,0x8F39,0x8F3B,/* 0xD8-0xDF */
+ 0x99A5,0x9C12,0x672C,0x4E76,0x4FF8,0x5949,0x5C01,0x5CEF,/* 0xE0-0xE7 */
+ 0x5CF0,0x6367,0x68D2,0x70FD,0x71A2,0x742B,0x7E2B,0x84EC,/* 0xE8-0xEF */
+ 0x8702,0x9022,0x92D2,0x9CF3,0x4E0D,0x4ED8,0x4FEF,0x5085,/* 0xF0-0xF7 */
+ 0x5256,0x526F,0x5426,0x5490,0x57E0,0x592B,0x5A66,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_DD[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x5B5A,0x5B75,0x5BCC,0x5E9C,0xF966,0x6276,0x6577,/* 0xA0-0xA7 */
+ 0x65A7,0x6D6E,0x6EA5,0x7236,0x7B26,0x7C3F,0x7F36,0x8150,/* 0xA8-0xAF */
+ 0x8151,0x819A,0x8240,0x8299,0x83A9,0x8A03,0x8CA0,0x8CE6,/* 0xB0-0xB7 */
+ 0x8CFB,0x8D74,0x8DBA,0x90E8,0x91DC,0x961C,0x9644,0x99D9,/* 0xB8-0xBF */
+ 0x9CE7,0x5317,0x5206,0x5429,0x5674,0x58B3,0x5954,0x596E,/* 0xC0-0xC7 */
+ 0x5FFF,0x61A4,0x626E,0x6610,0x6C7E,0x711A,0x76C6,0x7C89,/* 0xC8-0xCF */
+ 0x7CDE,0x7D1B,0x82AC,0x8CC1,0x96F0,0xF967,0x4F5B,0x5F17,/* 0xD0-0xD7 */
+ 0x5F7F,0x62C2,0x5D29,0x670B,0x68DA,0x787C,0x7E43,0x9D6C,/* 0xD8-0xDF */
+ 0x4E15,0x5099,0x5315,0x532A,0x5351,0x5983,0x5A62,0x5E87,/* 0xE0-0xE7 */
+ 0x60B2,0x618A,0x6249,0x6279,0x6590,0x6787,0x69A7,0x6BD4,/* 0xE8-0xEF */
+ 0x6BD6,0x6BD7,0x6BD8,0x6CB8,0xF968,0x7435,0x75FA,0x7812,/* 0xF0-0xF7 */
+ 0x7891,0x79D5,0x79D8,0x7C83,0x7DCB,0x7FE1,0x80A5,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_DE[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x813E,0x81C2,0x83F2,0x871A,0x88E8,0x8AB9,0x8B6C,/* 0xA0-0xA7 */
+ 0x8CBB,0x9119,0x975E,0x98DB,0x9F3B,0x56AC,0x5B2A,0x5F6C,/* 0xA8-0xAF */
+ 0x658C,0x6AB3,0x6BAF,0x6D5C,0x6FF1,0x7015,0x725D,0x73AD,/* 0xB0-0xB7 */
+ 0x8CA7,0x8CD3,0x983B,0x6191,0x6C37,0x8058,0x9A01,0x4E4D,/* 0xB8-0xBF */
+ 0x4E8B,0x4E9B,0x4ED5,0x4F3A,0x4F3C,0x4F7F,0x4FDF,0x50FF,/* 0xC0-0xC7 */
+ 0x53F2,0x53F8,0x5506,0x55E3,0x56DB,0x58EB,0x5962,0x5A11,/* 0xC8-0xCF */
+ 0x5BEB,0x5BFA,0x5C04,0x5DF3,0x5E2B,0x5F99,0x601D,0x6368,/* 0xD0-0xD7 */
+ 0x659C,0x65AF,0x67F6,0x67FB,0x68AD,0x6B7B,0x6C99,0x6CD7,/* 0xD8-0xDF */
+ 0x6E23,0x7009,0x7345,0x7802,0x793E,0x7940,0x7960,0x79C1,/* 0xE0-0xE7 */
+ 0x7BE9,0x7D17,0x7D72,0x8086,0x820D,0x838E,0x84D1,0x86C7,/* 0xE8-0xEF */
+ 0x88DF,0x8A50,0x8A5E,0x8B1D,0x8CDC,0x8D66,0x8FAD,0x90AA,/* 0xF0-0xF7 */
+ 0xFA2B,0x99DF,0x9E9D,0x524A,0xF969,0x6714,0xF96A,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_DF[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x5098,0x522A,0x5C71,0x6563,0x6C55,0x73CA,0x7523,/* 0xA0-0xA7 */
+ 0x759D,0x7B97,0x849C,0x9178,0x9730,0x4E77,0x6492,0x6BBA,/* 0xA8-0xAF */
+ 0x715E,0x85A9,0x4E09,0xF96B,0x6749,0x68EE,0x6E17,0x829F,/* 0xB0-0xB7 */
+ 0x8518,0x886B,0x63F7,0x6F81,0x9212,0x98AF,0x4E0A,0x50B7,/* 0xB8-0xBF */
+ 0x50CF,0x511F,0x5546,0x55AA,0x5617,0x5B40,0x5C19,0x5CE0,/* 0xC0-0xC7 */
+ 0x5E38,0x5E8A,0x5EA0,0x5EC2,0x60F3,0x6851,0x6A61,0x6E58,/* 0xC8-0xCF */
+ 0x723D,0x7240,0x72C0,0x76F8,0xFA1A,0x7BB1,0x7FD4,0x88F3,/* 0xD0-0xD7 */
+ 0x89F4,0x8A73,0x8C61,0x8CDE,0x971C,0x585E,0x74BD,0x8CFD,/* 0xD8-0xDF */
+ 0x55C7,0xF96C,0x7A61,0x7D22,0x8272,0x7272,0x751F,0x7525,/* 0xE0-0xE7 */
+ 0xF96D,0x7B19,0x5885,0x58FB,0x5DBC,0x5E8F,0x5EB6,0x5F90,/* 0xE8-0xEF */
+ 0x6055,0x6292,0x637F,0x654D,0x6691,0x66D9,0x66F8,0x6816,/* 0xF0-0xF7 */
+ 0x68F2,0x7280,0x745E,0x7B6E,0x7D6E,0x7DD6,0x7F72,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E0[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x80E5,0x8212,0x85AF,0x897F,0x8A93,0x901D,0x92E4,/* 0xA0-0xA7 */
+ 0x9ECD,0x9F20,0x5915,0x596D,0x5E2D,0x60DC,0x6614,0x6673,/* 0xA8-0xAF */
+ 0x6790,0x6C50,0x6DC5,0x6F5F,0x77F3,0x78A9,0x84C6,0x91CB,/* 0xB0-0xB7 */
+ 0x932B,0x4ED9,0x50CA,0x5148,0x5584,0x5B0B,0x5BA3,0x6247,/* 0xB8-0xBF */
+ 0x657E,0x65CB,0x6E32,0x717D,0x7401,0x7444,0x7487,0x74BF,/* 0xC0-0xC7 */
+ 0x766C,0x79AA,0x7DDA,0x7E55,0x7FA8,0x817A,0x81B3,0x8239,/* 0xC8-0xCF */
+ 0x861A,0x87EC,0x8A75,0x8DE3,0x9078,0x9291,0x9425,0x994D,/* 0xD0-0xD7 */
+ 0x9BAE,0x5368,0x5C51,0x6954,0x6CC4,0x6D29,0x6E2B,0x820C,/* 0xD8-0xDF */
+ 0x859B,0x893B,0x8A2D,0x8AAA,0x96EA,0x9F67,0x5261,0x66B9,/* 0xE0-0xE7 */
+ 0x6BB2,0x7E96,0x87FE,0x8D0D,0x9583,0x965D,0x651D,0x6D89,/* 0xE8-0xEF */
+ 0x71EE,0xF96E,0x57CE,0x59D3,0x5BAC,0x6027,0x60FA,0x6210,/* 0xF0-0xF7 */
+ 0x661F,0x665F,0x7329,0x73F9,0x76DB,0x7701,0x7B6C,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E1[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x8056,0x8072,0x8165,0x8AA0,0x9192,0x4E16,0x52E2,/* 0xA0-0xA7 */
+ 0x6B72,0x6D17,0x7A05,0x7B39,0x7D30,0xF96F,0x8CB0,0x53EC,/* 0xA8-0xAF */
+ 0x562F,0x5851,0x5BB5,0x5C0F,0x5C11,0x5DE2,0x6240,0x6383,/* 0xB0-0xB7 */
+ 0x6414,0x662D,0x68B3,0x6CBC,0x6D88,0x6EAF,0x701F,0x70A4,/* 0xB8-0xBF */
+ 0x71D2,0x7526,0x758F,0x758E,0x7619,0x7B11,0x7BE0,0x7C2B,/* 0xC0-0xC7 */
+ 0x7D20,0x7D39,0x852C,0x856D,0x8607,0x8A34,0x900D,0x9061,/* 0xC8-0xCF */
+ 0x90B5,0x92B7,0x97F6,0x9A37,0x4FD7,0x5C6C,0x675F,0x6D91,/* 0xD0-0xD7 */
+ 0x7C9F,0x7E8C,0x8B16,0x8D16,0x901F,0x5B6B,0x5DFD,0x640D,/* 0xD8-0xDF */
+ 0x84C0,0x905C,0x98E1,0x7387,0x5B8B,0x609A,0x677E,0x6DDE,/* 0xE0-0xE7 */
+ 0x8A1F,0x8AA6,0x9001,0x980C,0x5237,0xF970,0x7051,0x788E,/* 0xE8-0xEF */
+ 0x9396,0x8870,0x91D7,0x4FEE,0x53D7,0x55FD,0x56DA,0x5782,/* 0xF0-0xF7 */
+ 0x58FD,0x5AC2,0x5B88,0x5CAB,0x5CC0,0x5E25,0x6101,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E2[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x620D,0x624B,0x6388,0x641C,0x6536,0x6578,0x6A39,/* 0xA0-0xA7 */
+ 0x6B8A,0x6C34,0x6D19,0x6F31,0x71E7,0x72E9,0x7378,0x7407,/* 0xA8-0xAF */
+ 0x74B2,0x7626,0x7761,0x79C0,0x7A57,0x7AEA,0x7CB9,0x7D8F,/* 0xB0-0xB7 */
+ 0x7DAC,0x7E61,0x7F9E,0x8129,0x8331,0x8490,0x84DA,0x85EA,/* 0xB8-0xBF */
+ 0x8896,0x8AB0,0x8B90,0x8F38,0x9042,0x9083,0x916C,0x9296,/* 0xC0-0xC7 */
+ 0x92B9,0x968B,0x96A7,0x96A8,0x96D6,0x9700,0x9808,0x9996,/* 0xC8-0xCF */
+ 0x9AD3,0x9B1A,0x53D4,0x587E,0x5919,0x5B70,0x5BBF,0x6DD1,/* 0xD0-0xD7 */
+ 0x6F5A,0x719F,0x7421,0x74B9,0x8085,0x83FD,0x5DE1,0x5F87,/* 0xD8-0xDF */
+ 0x5FAA,0x6042,0x65EC,0x6812,0x696F,0x6A53,0x6B89,0x6D35,/* 0xE0-0xE7 */
+ 0x6DF3,0x73E3,0x76FE,0x77AC,0x7B4D,0x7D14,0x8123,0x821C,/* 0xE8-0xEF */
+ 0x8340,0x84F4,0x8563,0x8A62,0x8AC4,0x9187,0x931E,0x9806,/* 0xF0-0xF7 */
+ 0x99B4,0x620C,0x8853,0x8FF0,0x9265,0x5D07,0x5D27,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E3[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x5D69,0x745F,0x819D,0x8768,0x6FD5,0x62FE,0x7FD2,/* 0xA0-0xA7 */
+ 0x8936,0x8972,0x4E1E,0x4E58,0x50E7,0x52DD,0x5347,0x627F,/* 0xA8-0xAF */
+ 0x6607,0x7E69,0x8805,0x965E,0x4F8D,0x5319,0x5636,0x59CB,/* 0xB0-0xB7 */
+ 0x5AA4,0x5C38,0x5C4E,0x5C4D,0x5E02,0x5F11,0x6043,0x65BD,/* 0xB8-0xBF */
+ 0x662F,0x6642,0x67BE,0x67F4,0x731C,0x77E2,0x793A,0x7FC5,/* 0xC0-0xC7 */
+ 0x8494,0x84CD,0x8996,0x8A66,0x8A69,0x8AE1,0x8C55,0x8C7A,/* 0xC8-0xCF */
+ 0x57F4,0x5BD4,0x5F0F,0x606F,0x62ED,0x690D,0x6B96,0x6E5C,/* 0xD0-0xD7 */
+ 0x7184,0x7BD2,0x8755,0x8B58,0x8EFE,0x98DF,0x98FE,0x4F38,/* 0xD8-0xDF */
+ 0x4F81,0x4FE1,0x547B,0x5A20,0x5BB8,0x613C,0x65B0,0x6668,/* 0xE0-0xE7 */
+ 0x71FC,0x7533,0xFA19,0x7D33,0x814E,0x81E3,0x8398,0x85AA,/* 0xE8-0xEF */
+ 0x85CE,0x8703,0x8A0A,0x8EAB,0x8F9B,0xF971,0x8FC5,0x5931,/* 0xF0-0xF7 */
+ 0x5BA4,0x5BE6,0x6089,0x5BE9,0x5C0B,0x5FC3,0x6C81,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E4[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0xF972,0x6DF1,0x700B,0x751A,0x82AF,0x8AF6,0x4EC0,/* 0xA0-0xA7 */
+ 0x5341,0xF973,0x96D9,0x6C0F,0x4E9E,0x4FC4,0x5152,0x555E,/* 0xA8-0xAF */
+ 0x5A25,0x5CE8,0x6211,0x7259,0x82BD,0x83AA,0x86FE,0x8859,/* 0xB0-0xB7 */
+ 0x8A1D,0x963F,0x96C5,0x9913,0x9D09,0x9D5D,0x580A,0x5CB3,/* 0xB8-0xBF */
+ 0x5DBD,0x5E44,0x60E1,0x6115,0x63E1,0x6A02,0x6E25,0x9102,/* 0xC0-0xC7 */
+ 0x9354,0x984E,0x9C10,0x9F77,0x5B89,0x5CB8,0x6309,0x664F,/* 0xC8-0xCF */
+ 0x6848,0x773C,0x96C1,0x978D,0x9854,0x9B9F,0x65A1,0x8B01,/* 0xD0-0xD7 */
+ 0x8ECB,0x95BC,0x5535,0x5CA9,0x5DD6,0x5EB5,0x6697,0x764C,/* 0xD8-0xDF */
+ 0x83F4,0x95C7,0x58D3,0x62BC,0x72CE,0x9D28,0x4EF0,0x592E,/* 0xE0-0xE7 */
+ 0x600F,0x663B,0x6B83,0x79E7,0x9D26,0x5393,0x54C0,0x57C3,/* 0xE8-0xEF */
+ 0x5D16,0x611B,0x66D6,0x6DAF,0x788D,0x827E,0x9698,0x9744,/* 0xF0-0xF7 */
+ 0x5384,0x627C,0x6396,0x6DB2,0x7E0A,0x814B,0x984D,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E5[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x6AFB,0x7F4C,0x9DAF,0x9E1A,0x4E5F,0x503B,0x51B6,/* 0xA0-0xA7 */
+ 0x591C,0x60F9,0x63F6,0x6930,0x723A,0x8036,0xF974,0x91CE,/* 0xA8-0xAF */
+ 0x5F31,0xF975,0xF976,0x7D04,0x82E5,0x846F,0x84BB,0x85E5,/* 0xB0-0xB7 */
+ 0x8E8D,0xF977,0x4F6F,0xF978,0xF979,0x58E4,0x5B43,0x6059,/* 0xB8-0xBF */
+ 0x63DA,0x6518,0x656D,0x6698,0xF97A,0x694A,0x6A23,0x6D0B,/* 0xC0-0xC7 */
+ 0x7001,0x716C,0x75D2,0x760D,0x79B3,0x7A70,0xF97B,0x7F8A,/* 0xC8-0xCF */
+ 0xF97C,0x8944,0xF97D,0x8B93,0x91C0,0x967D,0xF97E,0x990A,/* 0xD0-0xD7 */
+ 0x5704,0x5FA1,0x65BC,0x6F01,0x7600,0x79A6,0x8A9E,0x99AD,/* 0xD8-0xDF */
+ 0x9B5A,0x9F6C,0x5104,0x61B6,0x6291,0x6A8D,0x81C6,0x5043,/* 0xE0-0xE7 */
+ 0x5830,0x5F66,0x7109,0x8A00,0x8AFA,0x5B7C,0x8616,0x4FFA,/* 0xE8-0xEF */
+ 0x513C,0x56B4,0x5944,0x63A9,0x6DF9,0x5DAA,0x696D,0x5186,/* 0xF0-0xF7 */
+ 0x4E88,0x4F59,0xF97F,0xF980,0xF981,0x5982,0xF982,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E6[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0xF983,0x6B5F,0x6C5D,0xF984,0x74B5,0x7916,0xF985,/* 0xA0-0xA7 */
+ 0x8207,0x8245,0x8339,0x8F3F,0x8F5D,0xF986,0x9918,0xF987,/* 0xA8-0xAF */
+ 0xF988,0xF989,0x4EA6,0xF98A,0x57DF,0x5F79,0x6613,0xF98B,/* 0xB0-0xB7 */
+ 0xF98C,0x75AB,0x7E79,0x8B6F,0xF98D,0x9006,0x9A5B,0x56A5,/* 0xB8-0xBF */
+ 0x5827,0x59F8,0x5A1F,0x5BB4,0xF98E,0x5EF6,0xF98F,0xF990,/* 0xC0-0xC7 */
+ 0x6350,0x633B,0xF991,0x693D,0x6C87,0x6CBF,0x6D8E,0x6D93,/* 0xC8-0xCF */
+ 0x6DF5,0x6F14,0xF992,0x70DF,0x7136,0x7159,0xF993,0x71C3,/* 0xD0-0xD7 */
+ 0x71D5,0xF994,0x784F,0x786F,0xF995,0x7B75,0x7DE3,0xF996,/* 0xD8-0xDF */
+ 0x7E2F,0xF997,0x884D,0x8EDF,0xF998,0xF999,0xF99A,0x925B,/* 0xE0-0xE7 */
+ 0xF99B,0x9CF6,0xF99C,0xF99D,0xF99E,0x6085,0x6D85,0xF99F,/* 0xE8-0xEF */
+ 0x71B1,0xF9A0,0xF9A1,0x95B1,0x53AD,0xF9A2,0xF9A3,0xF9A4,/* 0xF0-0xF7 */
+ 0x67D3,0xF9A5,0x708E,0x7130,0x7430,0x8276,0x82D2,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E7[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0xF9A6,0x95BB,0x9AE5,0x9E7D,0x66C4,0xF9A7,0x71C1,/* 0xA0-0xA7 */
+ 0x8449,0xF9A8,0xF9A9,0x584B,0xF9AA,0xF9AB,0x5DB8,0x5F71,/* 0xA8-0xAF */
+ 0xF9AC,0x6620,0x668E,0x6979,0x69AE,0x6C38,0x6CF3,0x6E36,/* 0xB0-0xB7 */
+ 0x6F41,0x6FDA,0x701B,0x702F,0x7150,0x71DF,0x7370,0xF9AD,/* 0xB8-0xBF */
+ 0x745B,0xF9AE,0x74D4,0x76C8,0x7A4E,0x7E93,0xF9AF,0xF9B0,/* 0xC0-0xC7 */
+ 0x82F1,0x8A60,0x8FCE,0xF9B1,0x9348,0xF9B2,0x9719,0xF9B3,/* 0xC8-0xCF */
+ 0xF9B4,0x4E42,0x502A,0xF9B5,0x5208,0x53E1,0x66F3,0x6C6D,/* 0xD0-0xD7 */
+ 0x6FCA,0x730A,0x777F,0x7A62,0x82AE,0x85DD,0x8602,0xF9B6,/* 0xD8-0xDF */
+ 0x88D4,0x8A63,0x8B7D,0x8C6B,0xF9B7,0x92B3,0xF9B8,0x9713,/* 0xE0-0xE7 */
+ 0x9810,0x4E94,0x4F0D,0x4FC9,0x50B2,0x5348,0x543E,0x5433,/* 0xE8-0xEF */
+ 0x55DA,0x5862,0x58BA,0x5967,0x5A1B,0x5BE4,0x609F,0xF9B9,/* 0xF0-0xF7 */
+ 0x61CA,0x6556,0x65FF,0x6664,0x68A7,0x6C5A,0x6FB3,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E8[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x70CF,0x71AC,0x7352,0x7B7D,0x8708,0x8AA4,0x9C32,/* 0xA0-0xA7 */
+ 0x9F07,0x5C4B,0x6C83,0x7344,0x7389,0x923A,0x6EAB,0x7465,/* 0xA8-0xAF */
+ 0x761F,0x7A69,0x7E15,0x860A,0xFA0C,0x58C5,0x64C1,0x74EE,/* 0xB0-0xB7 */
+ 0x7515,0x7670,0x7FC1,0x9095,0x96CD,0x9954,0x6E26,0x74E6,/* 0xB8-0xBF */
+ 0x7AA9,0x7AAA,0x81E5,0x86D9,0x8778,0x8A1B,0x5A49,0x5B8C,/* 0xC0-0xC7 */
+ 0x5B9B,0x68A1,0x6900,0x6D63,0x73A9,0x7413,0x742C,0x7897,/* 0xC8-0xCF */
+ 0x7DE9,0x7FEB,0x8118,0x8155,0x839E,0x8C4C,0x962E,0x9811,/* 0xD0-0xD7 */
+ 0x66F0,0x5F80,0x65FA,0x6789,0x6C6A,0x738B,0x502D,0x5A03,/* 0xD8-0xDF */
+ 0x6B6A,0x77EE,0x5916,0x5D6C,0x5DCD,0x7325,0x754F,0xF9BA,/* 0xE0-0xE7 */
+ 0xF9BB,0x50E5,0x51F9,0x582F,0x592D,0x5996,0x59DA,0x5BE5,/* 0xE8-0xEF */
+ 0xF9BC,0xF9BD,0x5DA2,0x62D7,0x6416,0x6493,0x64FE,0xF9BE,/* 0xF0-0xF7 */
+ 0x66DC,0xF9BF,0x6A48,0xF9C0,0x71FF,0x7464,0xF9C1,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E9[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x7A88,0x7AAF,0x7E47,0x7E5E,0x8000,0x8170,0xF9C2,/* 0xA0-0xA7 */
+ 0x87EF,0x8981,0x8B20,0x9059,0xF9C3,0x9080,0x9952,0x617E,/* 0xA8-0xAF */
+ 0x6B32,0x6D74,0x7E1F,0x8925,0x8FB1,0x4FD1,0x50AD,0x5197,/* 0xB0-0xB7 */
+ 0x52C7,0x57C7,0x5889,0x5BB9,0x5EB8,0x6142,0x6995,0x6D8C,/* 0xB8-0xBF */
+ 0x6E67,0x6EB6,0x7194,0x7462,0x7528,0x752C,0x8073,0x8338,/* 0xC0-0xC7 */
+ 0x84C9,0x8E0A,0x9394,0x93DE,0xF9C4,0x4E8E,0x4F51,0x5076,/* 0xC8-0xCF */
+ 0x512A,0x53C8,0x53CB,0x53F3,0x5B87,0x5BD3,0x5C24,0x611A,/* 0xD0-0xD7 */
+ 0x6182,0x65F4,0x725B,0x7397,0x7440,0x76C2,0x7950,0x7991,/* 0xD8-0xDF */
+ 0x79B9,0x7D06,0xFA1E,0x828B,0x85D5,0x865E,0x8FC2,0x9047,/* 0xE0-0xE7 */
+ 0x90F5,0x91EA,0x9685,0x96E8,0x96E9,0x52D6,0x5F67,0x65ED,/* 0xE8-0xEF */
+ 0x6631,0x682F,0x715C,0x7A36,0x90C1,0x980A,0x4E91,0xF9C5,/* 0xF0-0xF7 */
+ 0x6A52,0x6B9E,0x6F90,0x7189,0x8018,0x82B8,0x8553,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_EA[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x904B,0x9695,0x96F2,0x97FB,0x851A,0x9B31,0x4E90,/* 0xA0-0xA7 */
+ 0x718A,0x96C4,0x5143,0x539F,0x54E1,0x5713,0x5712,0x57A3,/* 0xA8-0xAF */
+ 0x5A9B,0x5AC4,0x5BC3,0x6028,0x613F,0x63F4,0x6C85,0x6D39,/* 0xB0-0xB7 */
+ 0x6E72,0x6E90,0x7230,0x733F,0x7457,0x82D1,0x8881,0x8F45,/* 0xB8-0xBF */
+ 0x9060,0xF9C6,0x9662,0x9858,0x9D1B,0x6708,0x8D8A,0x925E,/* 0xC0-0xC7 */
+ 0x4F4D,0x5049,0x50DE,0x5371,0x570D,0x59D4,0x5A01,0x5C09,/* 0xC8-0xCF */
+ 0x6170,0x6690,0x6E2D,0x7232,0x744B,0x7DEF,0x80C3,0x840E,/* 0xD0-0xD7 */
+ 0x8466,0x853F,0x875F,0x885B,0x8918,0x8B02,0x9055,0x97CB,/* 0xD8-0xDF */
+ 0x9B4F,0x4E73,0x4F91,0x5112,0x516A,0xF9C7,0x552F,0x55A9,/* 0xE0-0xE7 */
+ 0x5B7A,0x5BA5,0x5E7C,0x5E7D,0x5EBE,0x60A0,0x60DF,0x6108,/* 0xE8-0xEF */
+ 0x6109,0x63C4,0x6538,0x6709,0xF9C8,0x67D4,0x67DA,0xF9C9,/* 0xF0-0xF7 */
+ 0x6961,0x6962,0x6CB9,0x6D27,0xF9CA,0x6E38,0xF9CB,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_EB[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x6FE1,0x7336,0x7337,0xF9CC,0x745C,0x7531,0xF9CD,/* 0xA0-0xA7 */
+ 0x7652,0xF9CE,0xF9CF,0x7DAD,0x81FE,0x8438,0x88D5,0x8A98,/* 0xA8-0xAF */
+ 0x8ADB,0x8AED,0x8E30,0x8E42,0x904A,0x903E,0x907A,0x9149,/* 0xB0-0xB7 */
+ 0x91C9,0x936E,0xF9D0,0xF9D1,0x5809,0xF9D2,0x6BD3,0x8089,/* 0xB8-0xBF */
+ 0x80B2,0xF9D3,0xF9D4,0x5141,0x596B,0x5C39,0xF9D5,0xF9D6,/* 0xC0-0xC7 */
+ 0x6F64,0x73A7,0x80E4,0x8D07,0xF9D7,0x9217,0x958F,0xF9D8,/* 0xC8-0xCF */
+ 0xF9D9,0xF9DA,0xF9DB,0x807F,0x620E,0x701C,0x7D68,0x878D,/* 0xD0-0xD7 */
+ 0xF9DC,0x57A0,0x6069,0x6147,0x6BB7,0x8ABE,0x9280,0x96B1,/* 0xD8-0xDF */
+ 0x4E59,0x541F,0x6DEB,0x852D,0x9670,0x97F3,0x98EE,0x63D6,/* 0xE0-0xE7 */
+ 0x6CE3,0x9091,0x51DD,0x61C9,0x81BA,0x9DF9,0x4F9D,0x501A,/* 0xE8-0xEF */
+ 0x5100,0x5B9C,0x610F,0x61FF,0x64EC,0x6905,0x6BC5,0x7591,/* 0xF0-0xF7 */
+ 0x77E3,0x7FA9,0x8264,0x858F,0x87FB,0x8863,0x8ABC,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_EC[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x8B70,0x91AB,0x4E8C,0x4EE5,0x4F0A,0xF9DD,0xF9DE,/* 0xA0-0xA7 */
+ 0x5937,0x59E8,0xF9DF,0x5DF2,0x5F1B,0x5F5B,0x6021,0xF9E0,/* 0xA8-0xAF */
+ 0xF9E1,0xF9E2,0xF9E3,0x723E,0x73E5,0xF9E4,0x7570,0x75CD,/* 0xB0-0xB7 */
+ 0xF9E5,0x79FB,0xF9E6,0x800C,0x8033,0x8084,0x82E1,0x8351,/* 0xB8-0xBF */
+ 0xF9E7,0xF9E8,0x8CBD,0x8CB3,0x9087,0xF9E9,0xF9EA,0x98F4,/* 0xC0-0xC7 */
+ 0x990C,0xF9EB,0xF9EC,0x7037,0xFA17,0x7FCA,0x7FCC,0x7FFC,/* 0xC8-0xCF */
+ 0x8B1A,0x4EBA,0x4EC1,0x5203,0x5370,0xF9ED,0x54BD,0x56E0,/* 0xD0-0xD7 */
+ 0x59FB,0x5BC5,0x5F15,0x5FCD,0x6E6E,0xF9EE,0xF9EF,0x7D6A,/* 0xD8-0xDF */
+ 0x8335,0xF9F0,0x8693,0x8A8D,0xF9F1,0x976D,0x9777,0xF9F2,/* 0xE0-0xE7 */
+ 0xF9F3,0x4E00,0x4F5A,0x4F7E,0x58F9,0x65E5,0x6EA2,0xFA25,/* 0xE8-0xEF */
+ 0x93B0,0x99B9,0x4EFB,0x58EC,0x598A,0x59D9,0x6041,0xF9F4,/* 0xF0-0xF7 */
+ 0xF9F5,0x7A14,0xF9F6,0x834F,0x8CC3,0x5165,0x5344,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_ED[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0xF9F7,0xF9F8,0xF9F9,0x4ECD,0x5269,0x5B55,0x82BF,/* 0xA0-0xA7 */
+ 0x4ED4,0x523A,0x54A8,0x59C9,0x59FF,0x5B50,0x5B57,0x5B5C,/* 0xA8-0xAF */
+ 0x6063,0x6148,0x6ECB,0x7099,0x716E,0x7386,0x74F7,0x75B5,/* 0xB0-0xB7 */
+ 0x78C1,0x7D2B,0x8005,0x81EA,0x8328,0x8517,0x85C9,0x8AEE,/* 0xB8-0xBF */
+ 0x8CC7,0x96CC,0x4F5C,0x52FA,0x56BC,0x65AB,0x6628,0x707C,/* 0xC0-0xC7 */
+ 0x70B8,0x7235,0x7DBD,0x828D,0x914C,0x96C0,0x9D72,0x5B71,/* 0xC8-0xCF */
+ 0x68E7,0x6B98,0x6F7A,0x76DE,0x5C91,0x66AB,0x6F5B,0x7BB4,/* 0xD0-0xD7 */
+ 0x7C2A,0x8836,0x96DC,0x4E08,0x4ED7,0x5320,0x5834,0x58BB,/* 0xD8-0xDF */
+ 0x58EF,0x596C,0x5C07,0x5E33,0x5E84,0x5F35,0x638C,0x66B2,/* 0xE0-0xE7 */
+ 0x6756,0x6A1F,0x6AA3,0x6B0C,0x6F3F,0x7246,0xF9FA,0x7350,/* 0xE8-0xEF */
+ 0x748B,0x7AE0,0x7CA7,0x8178,0x81DF,0x81E7,0x838A,0x846C,/* 0xF0-0xF7 */
+ 0x8523,0x8594,0x85CF,0x88DD,0x8D13,0x91AC,0x9577,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_EE[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x969C,0x518D,0x54C9,0x5728,0x5BB0,0x624D,0x6750,/* 0xA0-0xA7 */
+ 0x683D,0x6893,0x6E3D,0x6ED3,0x707D,0x7E21,0x88C1,0x8CA1,/* 0xA8-0xAF */
+ 0x8F09,0x9F4B,0x9F4E,0x722D,0x7B8F,0x8ACD,0x931A,0x4F47,/* 0xB0-0xB7 */
+ 0x4F4E,0x5132,0x5480,0x59D0,0x5E95,0x62B5,0x6775,0x696E,/* 0xB8-0xBF */
+ 0x6A17,0x6CAE,0x6E1A,0x72D9,0x732A,0x75BD,0x7BB8,0x7D35,/* 0xC0-0xC7 */
+ 0x82E7,0x83F9,0x8457,0x85F7,0x8A5B,0x8CAF,0x8E87,0x9019,/* 0xC8-0xCF */
+ 0x90B8,0x96CE,0x9F5F,0x52E3,0x540A,0x5AE1,0x5BC2,0x6458,/* 0xD0-0xD7 */
+ 0x6575,0x6EF4,0x72C4,0xF9FB,0x7684,0x7A4D,0x7B1B,0x7C4D,/* 0xD8-0xDF */
+ 0x7E3E,0x7FDF,0x837B,0x8B2B,0x8CCA,0x8D64,0x8DE1,0x8E5F,/* 0xE0-0xE7 */
+ 0x8FEA,0x8FF9,0x9069,0x93D1,0x4F43,0x4F7A,0x50B3,0x5168,/* 0xE8-0xEF */
+ 0x5178,0x524D,0x526A,0x5861,0x587C,0x5960,0x5C08,0x5C55,/* 0xF0-0xF7 */
+ 0x5EDB,0x609B,0x6230,0x6813,0x6BBF,0x6C08,0x6FB1,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_EF[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x714E,0x7420,0x7530,0x7538,0x7551,0x7672,0x7B4C,/* 0xA0-0xA7 */
+ 0x7B8B,0x7BAD,0x7BC6,0x7E8F,0x8A6E,0x8F3E,0x8F49,0x923F,/* 0xA8-0xAF */
+ 0x9293,0x9322,0x942B,0x96FB,0x985A,0x986B,0x991E,0x5207,/* 0xB0-0xB7 */
+ 0x622A,0x6298,0x6D59,0x7664,0x7ACA,0x7BC0,0x7D76,0x5360,/* 0xB8-0xBF */
+ 0x5CBE,0x5E97,0x6F38,0x70B9,0x7C98,0x9711,0x9B8E,0x9EDE,/* 0xC0-0xC7 */
+ 0x63A5,0x647A,0x8776,0x4E01,0x4E95,0x4EAD,0x505C,0x5075,/* 0xC8-0xCF */
+ 0x5448,0x59C3,0x5B9A,0x5E40,0x5EAD,0x5EF7,0x5F81,0x60C5,/* 0xD0-0xD7 */
+ 0x633A,0x653F,0x6574,0x65CC,0x6676,0x6678,0x67FE,0x6968,/* 0xD8-0xDF */
+ 0x6A89,0x6B63,0x6C40,0x6DC0,0x6DE8,0x6E1F,0x6E5E,0x701E,/* 0xE0-0xE7 */
+ 0x70A1,0x738E,0x73FD,0x753A,0x775B,0x7887,0x798E,0x7A0B,/* 0xE8-0xEF */
+ 0x7A7D,0xFA1D,0x7D8E,0x8247,0x8A02,0x8AEA,0x8C9E,0x912D,/* 0xF0-0xF7 */
+ 0x914A,0x91D8,0x9266,0x92CC,0x9320,0x9706,0xFA1C,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_F0[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x975C,0x9802,0x9F0E,0x5236,0x5291,0x557C,0x5824,/* 0xA0-0xA7 */
+ 0x5E1D,0x5F1F,0x608C,0x63D0,0x68AF,0x6FDF,0x796D,0x7B2C,/* 0xA8-0xAF */
+ 0x81CD,0x85BA,0x88FD,0xFA22,0x8E44,0x918D,0x9664,0x969B,/* 0xB0-0xB7 */
+ 0x973D,0x984C,0x9F4A,0x4FCE,0x5146,0x51CB,0x52A9,0x5632,/* 0xB8-0xBF */
+ 0x5F14,0x5F6B,0x63AA,0x64CD,0x65E9,0x6641,0x66FA,0x66F9,/* 0xC0-0xC7 */
+ 0x671D,0x689D,0x68D7,0x69FD,0x6F15,0x6F6E,0x7167,0x71E5,/* 0xC8-0xCF */
+ 0x722A,0x74AA,0x773A,0x7956,0x795A,0x79DF,0x7A20,0x7A95,/* 0xD0-0xD7 */
+ 0x7C97,0x7CDF,0x7D44,0x7E70,0x8087,0x85FB,0x86A4,0x8A54,/* 0xD8-0xDF */
+ 0x8ABF,0x8D99,0x8E81,0x9020,0x906D,0x91E3,0x963B,0x96D5,/* 0xE0-0xE7 */
+ 0x9CE5,0x65CF,0x7C07,0x8DB3,0x93C3,0x5B58,0x5C0A,0x5352,/* 0xE8-0xEF */
+ 0x62D9,0x731D,0x5027,0x5B97,0x5F9E,0x60B0,0x616B,0x68D5,/* 0xF0-0xF7 */
+ 0x6DD9,0x742E,0x7A2E,0x7D42,0x7D9C,0x7E31,0x816B,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_F1[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x8E2A,0x8E35,0x937E,0x9418,0x4F50,0x5750,0x5DE6,/* 0xA0-0xA7 */
+ 0x5EA7,0x632B,0x7F6A,0x4E3B,0x4F4F,0x4F8F,0x505A,0x59DD,/* 0xA8-0xAF */
+ 0x80C4,0x546A,0x5468,0x55FE,0x594F,0x5B99,0x5DDE,0x5EDA,/* 0xB0-0xB7 */
+ 0x665D,0x6731,0x67F1,0x682A,0x6CE8,0x6D32,0x6E4A,0x6F8D,/* 0xB8-0xBF */
+ 0x70B7,0x73E0,0x7587,0x7C4C,0x7D02,0x7D2C,0x7DA2,0x821F,/* 0xC0-0xC7 */
+ 0x86DB,0x8A3B,0x8A85,0x8D70,0x8E8A,0x8F33,0x9031,0x914E,/* 0xC8-0xCF */
+ 0x9152,0x9444,0x99D0,0x7AF9,0x7CA5,0x4FCA,0x5101,0x51C6,/* 0xD0-0xD7 */
+ 0x57C8,0x5BEF,0x5CFB,0x6659,0x6A3D,0x6D5A,0x6E96,0x6FEC,/* 0xD8-0xDF */
+ 0x710C,0x756F,0x7AE3,0x8822,0x9021,0x9075,0x96CB,0x99FF,/* 0xE0-0xE7 */
+ 0x8301,0x4E2D,0x4EF2,0x8846,0x91CD,0x537D,0x6ADB,0x696B,/* 0xE8-0xEF */
+ 0x6C41,0x847A,0x589E,0x618E,0x66FE,0x62EF,0x70DD,0x7511,/* 0xF0-0xF7 */
+ 0x75C7,0x7E52,0x84B8,0x8B49,0x8D08,0x4E4B,0x53EA,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_F2[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x54AB,0x5730,0x5740,0x5FD7,0x6301,0x6307,0x646F,/* 0xA0-0xA7 */
+ 0x652F,0x65E8,0x667A,0x679D,0x67B3,0x6B62,0x6C60,0x6C9A,/* 0xA8-0xAF */
+ 0x6F2C,0x77E5,0x7825,0x7949,0x7957,0x7D19,0x80A2,0x8102,/* 0xB0-0xB7 */
+ 0x81F3,0x829D,0x82B7,0x8718,0x8A8C,0xF9FC,0x8D04,0x8DBE,/* 0xB8-0xBF */
+ 0x9072,0x76F4,0x7A19,0x7A37,0x7E54,0x8077,0x5507,0x55D4,/* 0xC0-0xC7 */
+ 0x5875,0x632F,0x6422,0x6649,0x664B,0x686D,0x699B,0x6B84,/* 0xC8-0xCF */
+ 0x6D25,0x6EB1,0x73CD,0x7468,0x74A1,0x755B,0x75B9,0x76E1,/* 0xD0-0xD7 */
+ 0x771E,0x778B,0x79E6,0x7E09,0x7E1D,0x81FB,0x852F,0x8897,/* 0xD8-0xDF */
+ 0x8A3A,0x8CD1,0x8EEB,0x8FB0,0x9032,0x93AD,0x9663,0x9673,/* 0xE0-0xE7 */
+ 0x9707,0x4F84,0x53F1,0x59EA,0x5AC9,0x5E19,0x684E,0x74C6,/* 0xE8-0xEF */
+ 0x75BE,0x79E9,0x7A92,0x81A3,0x86ED,0x8CEA,0x8DCC,0x8FED,/* 0xF0-0xF7 */
+ 0x659F,0x6715,0xF9FD,0x57F7,0x6F57,0x7DDD,0x8F2F,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_F3[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x93F6,0x96C6,0x5FB5,0x61F2,0x6F84,0x4E14,0x4F98,/* 0xA0-0xA7 */
+ 0x501F,0x53C9,0x55DF,0x5D6F,0x5DEE,0x6B21,0x6B64,0x78CB,/* 0xA8-0xAF */
+ 0x7B9A,0xF9FE,0x8E49,0x8ECA,0x906E,0x6349,0x643E,0x7740,/* 0xB0-0xB7 */
+ 0x7A84,0x932F,0x947F,0x9F6A,0x64B0,0x6FAF,0x71E6,0x74A8,/* 0xB8-0xBF */
+ 0x74DA,0x7AC4,0x7C12,0x7E82,0x7CB2,0x7E98,0x8B9A,0x8D0A,/* 0xC0-0xC7 */
+ 0x947D,0x9910,0x994C,0x5239,0x5BDF,0x64E6,0x672D,0x7D2E,/* 0xC8-0xCF */
+ 0x50ED,0x53C3,0x5879,0x6158,0x6159,0x61FA,0x65AC,0x7AD9,/* 0xD0-0xD7 */
+ 0x8B92,0x8B96,0x5009,0x5021,0x5275,0x5531,0x5A3C,0x5EE0,/* 0xD8-0xDF */
+ 0x5F70,0x6134,0x655E,0x660C,0x6636,0x66A2,0x69CD,0x6EC4,/* 0xE0-0xE7 */
+ 0x6F32,0x7316,0x7621,0x7A93,0x8139,0x8259,0x83D6,0x84BC,/* 0xE8-0xEF */
+ 0x50B5,0x57F0,0x5BC0,0x5BE8,0x5F69,0x63A1,0x7826,0x7DB5,/* 0xF0-0xF7 */
+ 0x83DC,0x8521,0x91C7,0x91F5,0x518A,0x67F5,0x7B56,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_F4[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x8CAC,0x51C4,0x59BB,0x60BD,0x8655,0x501C,0xF9FF,/* 0xA0-0xA7 */
+ 0x5254,0x5C3A,0x617D,0x621A,0x62D3,0x64F2,0x65A5,0x6ECC,/* 0xA8-0xAF */
+ 0x7620,0x810A,0x8E60,0x965F,0x96BB,0x4EDF,0x5343,0x5598,/* 0xB0-0xB7 */
+ 0x5929,0x5DDD,0x64C5,0x6CC9,0x6DFA,0x7394,0x7A7F,0x821B,/* 0xB8-0xBF */
+ 0x85A6,0x8CE4,0x8E10,0x9077,0x91E7,0x95E1,0x9621,0x97C6,/* 0xC0-0xC7 */
+ 0x51F8,0x54F2,0x5586,0x5FB9,0x64A4,0x6F88,0x7DB4,0x8F1F,/* 0xC8-0xCF */
+ 0x8F4D,0x9435,0x50C9,0x5C16,0x6CBE,0x6DFB,0x751B,0x77BB,/* 0xD0-0xD7 */
+ 0x7C3D,0x7C64,0x8A79,0x8AC2,0x581E,0x59BE,0x5E16,0x6377,/* 0xD8-0xDF */
+ 0x7252,0x758A,0x776B,0x8ADC,0x8CBC,0x8F12,0x5EF3,0xFA12,/* 0xE0-0xE7 */
+ 0x6DF8,0x807D,0x83C1,0x8ACB,0x9751,0x9BD6,0xFA00,0x5243,/* 0xE8-0xEF */
+ 0x66FF,0x6D95,0x6EEF,0x7DE0,0x8AE6,0x902E,0x905E,0x9AD4,/* 0xF0-0xF7 */
+ 0x521D,0x527F,0x54E8,0x6194,0x6284,0x62DB,0x68A2,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_F5[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x6912,0x695A,0x6A35,0x7092,0x7126,0x785D,0x7901,/* 0xA0-0xA7 */
+ 0x790E,0x79D2,0x7A0D,0x8096,0x8278,0x82D5,0x8349,0x8549,/* 0xA8-0xAF */
+ 0x8C82,0x8D85,0x9162,0x918B,0x91AE,0x4FC3,0x56D1,0x71ED,/* 0xB0-0xB7 */
+ 0x77D7,0x8700,0x89F8,0x5BF8,0x5FD6,0x6751,0x90A8,0x53E2,/* 0xB8-0xBF */
+ 0xFA10,0x5BF5,0x60A4,0x6181,0x6460,0x7E3D,0x8070,0x8525,/* 0xC0-0xC7 */
+ 0x9283,0x64AE,0x50AC,0x5D14,0x6700,0x589C,0x62BD,0x63A8,/* 0xC8-0xCF */
+ 0x690E,0x6978,0x6A1E,0x6E6B,0x76BA,0x79CB,0x82BB,0x8429,/* 0xD0-0xD7 */
+ 0x8ACF,0x8DA8,0x8FFD,0x9112,0x914B,0x919C,0x9310,0x9318,/* 0xD8-0xDF */
+ 0x939A,0x96DB,0x9A36,0x9C0D,0x4E11,0x755C,0x795D,0x7AFA,/* 0xE0-0xE7 */
+ 0x7B51,0x7BC9,0x7E2E,0x84C4,0x8E59,0x8E74,0x8EF8,0x9010,/* 0xE8-0xEF */
+ 0x6625,0x693F,0x7443,0x51FA,0x672E,0x9EDC,0x5145,0x5FE0,/* 0xF0-0xF7 */
+ 0x6C96,0x87F2,0x885D,0x8877,0x60B4,0x81B5,0x8403,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_F6[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x8D05,0x53D6,0x5439,0x5634,0x5A36,0x5C31,0x708A,/* 0xA0-0xA7 */
+ 0x7FE0,0x805A,0x8106,0x81ED,0x8DA3,0x9189,0x9A5F,0x9DF2,/* 0xA8-0xAF */
+ 0x5074,0x4EC4,0x53A0,0x60FB,0x6E2C,0x5C64,0x4F88,0x5024,/* 0xB0-0xB7 */
+ 0x55E4,0x5CD9,0x5E5F,0x6065,0x6894,0x6CBB,0x6DC4,0x71BE,/* 0xB8-0xBF */
+ 0x75D4,0x75F4,0x7661,0x7A1A,0x7A49,0x7DC7,0x7DFB,0x7F6E,/* 0xC0-0xC7 */
+ 0x81F4,0x86A9,0x8F1C,0x96C9,0x99B3,0x9F52,0x5247,0x52C5,/* 0xC8-0xCF */
+ 0x98ED,0x89AA,0x4E03,0x67D2,0x6F06,0x4FB5,0x5BE2,0x6795,/* 0xD0-0xD7 */
+ 0x6C88,0x6D78,0x741B,0x7827,0x91DD,0x937C,0x87C4,0x79E4,/* 0xD8-0xDF */
+ 0x7A31,0x5FEB,0x4ED6,0x54A4,0x553E,0x58AE,0x59A5,0x60F0,/* 0xE0-0xE7 */
+ 0x6253,0x62D6,0x6736,0x6955,0x8235,0x9640,0x99B1,0x99DD,/* 0xE8-0xEF */
+ 0x502C,0x5353,0x5544,0x577C,0xFA01,0x6258,0xFA02,0x64E2,/* 0xF0-0xF7 */
+ 0x666B,0x67DD,0x6FC1,0x6FEF,0x7422,0x7438,0x8A17,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_F7[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x9438,0x5451,0x5606,0x5766,0x5F48,0x619A,0x6B4E,/* 0xA0-0xA7 */
+ 0x7058,0x70AD,0x7DBB,0x8A95,0x596A,0x812B,0x63A2,0x7708,/* 0xA8-0xAF */
+ 0x803D,0x8CAA,0x5854,0x642D,0x69BB,0x5B95,0x5E11,0x6E6F,/* 0xB0-0xB7 */
+ 0xFA03,0x8569,0x514C,0x53F0,0x592A,0x6020,0x614B,0x6B86,/* 0xB8-0xBF */
+ 0x6C70,0x6CF0,0x7B1E,0x80CE,0x82D4,0x8DC6,0x90B0,0x98B1,/* 0xC0-0xC7 */
+ 0xFA04,0x64C7,0x6FA4,0x6491,0x6504,0x514E,0x5410,0x571F,/* 0xC8-0xCF */
+ 0x8A0E,0x615F,0x6876,0xFA05,0x75DB,0x7B52,0x7D71,0x901A,/* 0xD0-0xD7 */
+ 0x5806,0x69CC,0x817F,0x892A,0x9000,0x9839,0x5078,0x5957,/* 0xD8-0xDF */
+ 0x59AC,0x6295,0x900F,0x9B2A,0x615D,0x7279,0x95D6,0x5761,/* 0xE0-0xE7 */
+ 0x5A46,0x5DF4,0x628A,0x64AD,0x64FA,0x6777,0x6CE2,0x6D3E,/* 0xE8-0xEF */
+ 0x722C,0x7436,0x7834,0x7F77,0x82AD,0x8DDB,0x9817,0x5224,/* 0xF0-0xF7 */
+ 0x5742,0x677F,0x7248,0x74E3,0x8CA9,0x8FA6,0x9211,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_F8[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x962A,0x516B,0x53ED,0x634C,0x4F69,0x5504,0x6096,/* 0xA0-0xA7 */
+ 0x6557,0x6C9B,0x6D7F,0x724C,0x72FD,0x7A17,0x8987,0x8C9D,/* 0xA8-0xAF */
+ 0x5F6D,0x6F8E,0x70F9,0x81A8,0x610E,0x4FBF,0x504F,0x6241,/* 0xB0-0xB7 */
+ 0x7247,0x7BC7,0x7DE8,0x7FE9,0x904D,0x97AD,0x9A19,0x8CB6,/* 0xB8-0xBF */
+ 0x576A,0x5E73,0x67B0,0x840D,0x8A55,0x5420,0x5B16,0x5E63,/* 0xC0-0xC7 */
+ 0x5EE2,0x5F0A,0x6583,0x80BA,0x853D,0x9589,0x965B,0x4F48,/* 0xC8-0xCF */
+ 0x5305,0x530D,0x530F,0x5486,0x54FA,0x5703,0x5E03,0x6016,/* 0xD0-0xD7 */
+ 0x629B,0x62B1,0x6355,0xFA06,0x6CE1,0x6D66,0x75B1,0x7832,/* 0xD8-0xDF */
+ 0x80DE,0x812F,0x82DE,0x8461,0x84B2,0x888D,0x8912,0x900B,/* 0xE0-0xE7 */
+ 0x92EA,0x98FD,0x9B91,0x5E45,0x66B4,0x66DD,0x7011,0x7206,/* 0xE8-0xEF */
+ 0xFA07,0x4FF5,0x527D,0x5F6A,0x6153,0x6753,0x6A19,0x6F02,/* 0xF0-0xF7 */
+ 0x74E2,0x7968,0x8868,0x8C79,0x98C7,0x98C4,0x9A43,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_F9[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x54C1,0x7A1F,0x6953,0x8AF7,0x8C4A,0x98A8,0x99AE,/* 0xA0-0xA7 */
+ 0x5F7C,0x62AB,0x75B2,0x76AE,0x88AB,0x907F,0x9642,0x5339,/* 0xA8-0xAF */
+ 0x5F3C,0x5FC5,0x6CCC,0x73CC,0x7562,0x758B,0x7B46,0x82FE,/* 0xB0-0xB7 */
+ 0x999D,0x4E4F,0x903C,0x4E0B,0x4F55,0x53A6,0x590F,0x5EC8,/* 0xB8-0xBF */
+ 0x6630,0x6CB3,0x7455,0x8377,0x8766,0x8CC0,0x9050,0x971E,/* 0xC0-0xC7 */
+ 0x9C15,0x58D1,0x5B78,0x8650,0x8B14,0xFA2D,0x5BD2,0x6068,/* 0xC8-0xCF */
+ 0x608D,0x65F1,0x6C57,0x6F22,0x6FA3,0x701A,0x7F55,0x7FF0,/* 0xD0-0xD7 */
+ 0x9591,0x9592,0x9650,0x97D3,0x5272,0x8F44,0x51FD,0x542B,/* 0xD8-0xDF */
+ 0x54B8,0x5563,0x558A,0x6ABB,0x6DB5,0x7DD8,0x8266,0x929C,/* 0xE0-0xE7 */
+ 0x9677,0x9E79,0x5408,0x54C8,0x76D2,0x86E4,0x95A4,0x95D4,/* 0xE8-0xEF */
+ 0x965C,0x4EA2,0x4F09,0x59EE,0x5AE6,0x5DF7,0x6052,0x6297,/* 0xF0-0xF7 */
+ 0x676D,0x6841,0x6C86,0x6E2F,0x7F38,0x809B,0x822A,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_FA[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0xFA08,0xFA09,0x9805,0x4EA5,0x5055,0x54B3,0x5793,/* 0xA0-0xA7 */
+ 0x595A,0x5B69,0x5BB3,0x61C8,0x6977,0x6D77,0x7023,0x87F9,/* 0xA8-0xAF */
+ 0x89E3,0x8A72,0x8AE7,0x9082,0x99ED,0x9AB8,0x52BE,0x6838,/* 0xB0-0xB7 */
+ 0x5016,0x5E78,0x674F,0x8347,0x884C,0x4EAB,0x5411,0x56AE,/* 0xB8-0xBF */
+ 0x73E6,0x9115,0x97FF,0x9909,0x9957,0x9999,0x5653,0x589F,/* 0xC0-0xC7 */
+ 0x865B,0x8A31,0x61B2,0x6AF6,0x737B,0x8ED2,0x6B47,0x96AA,/* 0xC8-0xCF */
+ 0x9A57,0x5955,0x7200,0x8D6B,0x9769,0x4FD4,0x5CF4,0x5F26,/* 0xD0-0xD7 */
+ 0x61F8,0x665B,0x6CEB,0x70AB,0x7384,0x73B9,0x73FE,0x7729,/* 0xD8-0xDF */
+ 0x774D,0x7D43,0x7D62,0x7E23,0x8237,0x8852,0xFA0A,0x8CE2,/* 0xE0-0xE7 */
+ 0x9249,0x986F,0x5B51,0x7A74,0x8840,0x9801,0x5ACC,0x4FE0,/* 0xE8-0xEF */
+ 0x5354,0x593E,0x5CFD,0x633E,0x6D79,0x72F9,0x8105,0x8107,/* 0xF0-0xF7 */
+ 0x83A2,0x92CF,0x9830,0x4EA8,0x5144,0x5211,0x578B,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_FB[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x5F62,0x6CC2,0x6ECE,0x7005,0x7050,0x70AF,0x7192,/* 0xA0-0xA7 */
+ 0x73E9,0x7469,0x834A,0x87A2,0x8861,0x9008,0x90A2,0x93A3,/* 0xA8-0xAF */
+ 0x99A8,0x516E,0x5F57,0x60E0,0x6167,0x66B3,0x8559,0x8E4A,/* 0xB0-0xB7 */
+ 0x91AF,0x978B,0x4E4E,0x4E92,0x547C,0x58D5,0x58FA,0x597D,/* 0xB8-0xBF */
+ 0x5CB5,0x5F27,0x6236,0x6248,0x660A,0x6667,0x6BEB,0x6D69,/* 0xC0-0xC7 */
+ 0x6DCF,0x6E56,0x6EF8,0x6F94,0x6FE0,0x6FE9,0x705D,0x72D0,/* 0xC8-0xCF */
+ 0x7425,0x745A,0x74E0,0x7693,0x795C,0x7CCA,0x7E1E,0x80E1,/* 0xD0-0xD7 */
+ 0x82A6,0x846B,0x84BF,0x864E,0x865F,0x8774,0x8B77,0x8C6A,/* 0xD8-0xDF */
+ 0x93AC,0x9800,0x9865,0x60D1,0x6216,0x9177,0x5A5A,0x660F,/* 0xE0-0xE7 */
+ 0x6DF7,0x6E3E,0x743F,0x9B42,0x5FFD,0x60DA,0x7B0F,0x54C4,/* 0xE8-0xEF */
+ 0x5F18,0x6C5E,0x6CD3,0x6D2A,0x70D8,0x7D05,0x8679,0x8A0C,/* 0xF0-0xF7 */
+ 0x9D3B,0x5316,0x548C,0x5B05,0x6A3A,0x706B,0x7575,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_FC[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x798D,0x79BE,0x82B1,0x83EF,0x8A71,0x8B41,0x8CA8,/* 0xA0-0xA7 */
+ 0x9774,0xFA0B,0x64F4,0x652B,0x78BA,0x78BB,0x7A6B,0x4E38,/* 0xA8-0xAF */
+ 0x559A,0x5950,0x5BA6,0x5E7B,0x60A3,0x63DB,0x6B61,0x6665,/* 0xB0-0xB7 */
+ 0x6853,0x6E19,0x7165,0x74B0,0x7D08,0x9084,0x9A69,0x9C25,/* 0xB8-0xBF */
+ 0x6D3B,0x6ED1,0x733E,0x8C41,0x95CA,0x51F0,0x5E4C,0x5FA8,/* 0xC0-0xC7 */
+ 0x604D,0x60F6,0x6130,0x614C,0x6643,0x6644,0x69A5,0x6CC1,/* 0xC8-0xCF */
+ 0x6E5F,0x6EC9,0x6F62,0x714C,0x749C,0x7687,0x7BC1,0x7C27,/* 0xD0-0xD7 */
+ 0x8352,0x8757,0x9051,0x968D,0x9EC3,0x532F,0x56DE,0x5EFB,/* 0xD8-0xDF */
+ 0x5F8A,0x6062,0x6094,0x61F7,0x6666,0x6703,0x6A9C,0x6DEE,/* 0xE0-0xE7 */
+ 0x6FAE,0x7070,0x736A,0x7E6A,0x81BE,0x8334,0x86D4,0x8AA8,/* 0xE8-0xEF */
+ 0x8CC4,0x5283,0x7372,0x5B96,0x6A6B,0x9404,0x54EE,0x5686,/* 0xF0-0xF7 */
+ 0x5B5D,0x6548,0x6585,0x66C9,0x689F,0x6D8D,0x6DC6,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_FD[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x40-0x47 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x48-0x4F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x50-0x57 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x58-0x5F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x60-0x67 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x68-0x6F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x70-0x77 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x723B,0x80B4,0x9175,0x9A4D,0x4FAF,0x5019,0x539A,/* 0xA0-0xA7 */
+ 0x540E,0x543C,0x5589,0x55C5,0x5E3F,0x5F8C,0x673D,0x7166,/* 0xA8-0xAF */
+ 0x73DD,0x9005,0x52DB,0x52F3,0x5864,0x58CE,0x7104,0x718F,/* 0xB0-0xB7 */
+ 0x71FB,0x85B0,0x8A13,0x6688,0x85A8,0x55A7,0x6684,0x714A,/* 0xB8-0xBF */
+ 0x8431,0x5349,0x5599,0x6BC1,0x5F59,0x5FBD,0x63EE,0x6689,/* 0xC0-0xC7 */
+ 0x7147,0x8AF1,0x8F1D,0x9EBE,0x4F11,0x643A,0x70CB,0x7566,/* 0xC8-0xCF */
+ 0x8667,0x6064,0x8B4E,0x9DF8,0x5147,0x51F6,0x5308,0x6D36,/* 0xD0-0xD7 */
+ 0x80F8,0x9ED1,0x6615,0x6B23,0x7098,0x75D5,0x5403,0x5C79,/* 0xD8-0xDF */
+ 0x7D07,0x8A16,0x6B20,0x6B3D,0x6B46,0x5438,0x6070,0x6D3D,/* 0xE0-0xE7 */
+ 0x7FD5,0x8208,0x50D6,0xFA15,0x559C,0x566B,0x56CD,0x59EC,/* 0xE8-0xEF */
+ 0x5B09,0x5E0C,0x6199,0x6198,0x6231,0x665E,0x66E6,0x7199,/* 0xF0-0xF7 */
+ 0x71B9,0x71BA,0x72A7,0x79A7,0x7A00,0x7FB2,0x8A70,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t *page_charset2uni[256] = {
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, c2u_81, c2u_82, c2u_83, c2u_84, c2u_85, c2u_86, c2u_87,
+ c2u_88, c2u_89, c2u_8A, c2u_8B, c2u_8C, c2u_8D, c2u_8E, c2u_8F,
+ c2u_90, c2u_91, c2u_92, c2u_93, c2u_94, c2u_95, c2u_96, c2u_97,
+ c2u_98, c2u_99, c2u_9A, c2u_9B, c2u_9C, c2u_9D, c2u_9E, c2u_9F,
+ c2u_A0, c2u_A1, c2u_A2, c2u_A3, c2u_A4, c2u_A5, c2u_A6, c2u_A7,
+ c2u_A8, c2u_A9, c2u_AA, c2u_AB, c2u_AC, c2u_AD, c2u_AE, c2u_AF,
+ c2u_B0, c2u_B1, c2u_B2, c2u_B3, c2u_B4, c2u_B5, c2u_B6, c2u_B7,
+ c2u_B8, c2u_B9, c2u_BA, c2u_BB, c2u_BC, c2u_BD, c2u_BE, c2u_BF,
+ c2u_C0, c2u_C1, c2u_C2, c2u_C3, c2u_C4, c2u_C5, c2u_C6, c2u_C7,
+ c2u_C8, NULL, c2u_CA, c2u_CB, c2u_CC, c2u_CD, c2u_CE, c2u_CF,
+ c2u_D0, c2u_D1, c2u_D2, c2u_D3, c2u_D4, c2u_D5, c2u_D6, c2u_D7,
+ c2u_D8, c2u_D9, c2u_DA, c2u_DB, c2u_DC, c2u_DD, c2u_DE, c2u_DF,
+ c2u_E0, c2u_E1, c2u_E2, c2u_E3, c2u_E4, c2u_E5, c2u_E6, c2u_E7,
+ c2u_E8, c2u_E9, c2u_EA, c2u_EB, c2u_EC, c2u_ED, c2u_EE, c2u_EF,
+ c2u_F0, c2u_F1, c2u_F2, c2u_F3, c2u_F4, c2u_F5, c2u_F6, c2u_F7,
+ c2u_F8, c2u_F9, c2u_FA, c2u_FB, c2u_FC, c2u_FD, NULL, NULL,
+};
+
+static unsigned char u2c_01[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xA9, 0xA2, 0xA9, 0xA2, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0xA8, 0xA4, 0xA9, 0xA4, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0xA9, 0xA5, 0xA8, 0xA6, 0xA9, 0xA6, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0xA9, 0xA7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA8, 0xA8, /* 0x3C-0x3F */
+ 0xA9, 0xA8, 0xA8, 0xA9, 0xA9, 0xA9, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0xA9, 0xB0, 0xA8, 0xAF, 0xA9, 0xAF, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0xA8, 0xAB, 0xA9, 0xAB, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0xA8, 0xAE, 0xA9, 0xAE, /* 0x64-0x67 */
+};
+
+static unsigned char u2c_02[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA2, 0xA7, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0xA2, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xA2, 0xA8, 0xA2, 0xAB, 0xA2, 0xAA, 0xA2, 0xAD, /* 0xD8-0xDB */
+ 0x00, 0x00, 0xA2, 0xA9, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+};
+
+static unsigned char u2c_03[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xA5, 0xC1, 0xA5, 0xC2, 0xA5, 0xC3, /* 0x90-0x93 */
+ 0xA5, 0xC4, 0xA5, 0xC5, 0xA5, 0xC6, 0xA5, 0xC7, /* 0x94-0x97 */
+ 0xA5, 0xC8, 0xA5, 0xC9, 0xA5, 0xCA, 0xA5, 0xCB, /* 0x98-0x9B */
+ 0xA5, 0xCC, 0xA5, 0xCD, 0xA5, 0xCE, 0xA5, 0xCF, /* 0x9C-0x9F */
+ 0xA5, 0xD0, 0xA5, 0xD1, 0x00, 0x00, 0xA5, 0xD2, /* 0xA0-0xA3 */
+ 0xA5, 0xD3, 0xA5, 0xD4, 0xA5, 0xD5, 0xA5, 0xD6, /* 0xA4-0xA7 */
+ 0xA5, 0xD7, 0xA5, 0xD8, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0xA5, 0xE1, 0xA5, 0xE2, 0xA5, 0xE3, /* 0xB0-0xB3 */
+ 0xA5, 0xE4, 0xA5, 0xE5, 0xA5, 0xE6, 0xA5, 0xE7, /* 0xB4-0xB7 */
+ 0xA5, 0xE8, 0xA5, 0xE9, 0xA5, 0xEA, 0xA5, 0xEB, /* 0xB8-0xBB */
+ 0xA5, 0xEC, 0xA5, 0xED, 0xA5, 0xEE, 0xA5, 0xEF, /* 0xBC-0xBF */
+ 0xA5, 0xF0, 0xA5, 0xF1, 0x00, 0x00, 0xA5, 0xF2, /* 0xC0-0xC3 */
+ 0xA5, 0xF3, 0xA5, 0xF4, 0xA5, 0xF5, 0xA5, 0xF6, /* 0xC4-0xC7 */
+ 0xA5, 0xF7, 0xA5, 0xF8, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+};
+
+static unsigned char u2c_04[512] = {
+ 0x00, 0x00, 0xAC, 0xA7, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xAC, 0xA1, 0xAC, 0xA2, 0xAC, 0xA3, 0xAC, 0xA4, /* 0x10-0x13 */
+ 0xAC, 0xA5, 0xAC, 0xA6, 0xAC, 0xA8, 0xAC, 0xA9, /* 0x14-0x17 */
+ 0xAC, 0xAA, 0xAC, 0xAB, 0xAC, 0xAC, 0xAC, 0xAD, /* 0x18-0x1B */
+ 0xAC, 0xAE, 0xAC, 0xAF, 0xAC, 0xB0, 0xAC, 0xB1, /* 0x1C-0x1F */
+ 0xAC, 0xB2, 0xAC, 0xB3, 0xAC, 0xB4, 0xAC, 0xB5, /* 0x20-0x23 */
+ 0xAC, 0xB6, 0xAC, 0xB7, 0xAC, 0xB8, 0xAC, 0xB9, /* 0x24-0x27 */
+ 0xAC, 0xBA, 0xAC, 0xBB, 0xAC, 0xBC, 0xAC, 0xBD, /* 0x28-0x2B */
+ 0xAC, 0xBE, 0xAC, 0xBF, 0xAC, 0xC0, 0xAC, 0xC1, /* 0x2C-0x2F */
+ 0xAC, 0xD1, 0xAC, 0xD2, 0xAC, 0xD3, 0xAC, 0xD4, /* 0x30-0x33 */
+ 0xAC, 0xD5, 0xAC, 0xD6, 0xAC, 0xD8, 0xAC, 0xD9, /* 0x34-0x37 */
+ 0xAC, 0xDA, 0xAC, 0xDB, 0xAC, 0xDC, 0xAC, 0xDD, /* 0x38-0x3B */
+ 0xAC, 0xDE, 0xAC, 0xDF, 0xAC, 0xE0, 0xAC, 0xE1, /* 0x3C-0x3F */
+ 0xAC, 0xE2, 0xAC, 0xE3, 0xAC, 0xE4, 0xAC, 0xE5, /* 0x40-0x43 */
+ 0xAC, 0xE6, 0xAC, 0xE7, 0xAC, 0xE8, 0xAC, 0xE9, /* 0x44-0x47 */
+ 0xAC, 0xEA, 0xAC, 0xEB, 0xAC, 0xEC, 0xAC, 0xED, /* 0x48-0x4B */
+ 0xAC, 0xEE, 0xAC, 0xEF, 0xAC, 0xF0, 0xAC, 0xF1, /* 0x4C-0x4F */
+ 0x00, 0x00, 0xAC, 0xD7, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+};
+
+static unsigned char u2c_11[512] = {
+ 0xA4, 0xA1, 0xA4, 0xA2, 0xA4, 0xA4, 0xA4, 0xA7, /* 0x00-0x03 */
+ 0xA4, 0xA8, 0xA4, 0xA9, 0xA4, 0xB1, 0xA4, 0xB2, /* 0x04-0x07 */
+ 0xA4, 0xB3, 0xA4, 0xB5, 0xA4, 0xB6, 0xA4, 0xB7, /* 0x08-0x0B */
+ 0xA4, 0xB8, 0xA4, 0xB9, 0xA4, 0xBA, 0xA4, 0xBB, /* 0x0C-0x0F */
+ 0xA4, 0xBC, 0xA4, 0xBD, 0xA4, 0xBE, 0x00, 0x00, /* 0x10-0x13 */
+ 0xA4, 0xD5, 0xA4, 0xD6, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0xA4, 0xDD, 0x00, 0x00, /* 0x18-0x1B */
+ 0xA4, 0xDE, 0xA4, 0xE1, 0xA4, 0xE2, 0x00, 0x00, /* 0x1C-0x1F */
+ 0xA4, 0xE3, 0xA4, 0xB4, 0xA4, 0xE4, 0xA4, 0xE5, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0xE6, /* 0x24-0x27 */
+ 0x00, 0x00, 0xA4, 0xE7, 0x00, 0x00, 0xA4, 0xE8, /* 0x28-0x2B */
+ 0xA4, 0xE9, 0xA4, 0xEA, 0xA4, 0xEB, 0xA4, 0xEC, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0xA4, 0xED, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0xA4, 0xEE, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0xA4, 0xB5, 0xA4, 0xB6, 0xA4, 0xB5, 0xA4, 0xB6, /* 0x3C-0x3F */
+ 0xA4, 0xEF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0xA4, 0xF2, 0xA4, 0xF3, 0xA4, 0xF0, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0xA4, 0xB7, 0x00, 0x00, 0xA4, 0xB8, 0xA4, 0xB9, /* 0x4C-0x4F */
+ 0xA4, 0xB8, 0xA4, 0xB9, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0xA4, 0xBA, 0xA4, 0xBA, 0x00, 0x00, 0xA4, 0xF4, /* 0x54-0x57 */
+ 0xA4, 0xF5, 0xA4, 0xF6, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0xD4, /* 0x5C-0x5F */
+ 0x00, 0x00, 0xA4, 0xBF, 0xA4, 0xC0, 0xA4, 0xC1, /* 0x60-0x63 */
+ 0xA4, 0xC2, 0xA4, 0xC3, 0xA4, 0xC4, 0xA4, 0xC5, /* 0x64-0x67 */
+ 0xA4, 0xC6, 0xA4, 0xC7, 0xA4, 0xC8, 0xA4, 0xC9, /* 0x68-0x6B */
+ 0xA4, 0xCA, 0xA4, 0xCB, 0xA4, 0xCC, 0xA4, 0xCD, /* 0x6C-0x6F */
+ 0xA4, 0xCE, 0xA4, 0xCF, 0xA4, 0xD0, 0xA4, 0xD1, /* 0x70-0x73 */
+ 0xA4, 0xD2, 0xA4, 0xD3, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0xA4, 0xF7, 0xA4, 0xF8, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0xA4, 0xF9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xA4, 0xFA, 0xA4, 0xFB, 0x00, 0x00, /* 0x90-0x93 */
+ 0xA4, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0xA4, 0xFD, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0xA4, 0xFE, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xA4, 0xA1, 0xA4, 0xA2, 0xA4, 0xA3, 0xA4, 0xA4, /* 0xA8-0xAB */
+ 0xA4, 0xA5, 0xA4, 0xA6, 0xA4, 0xA7, 0xA4, 0xA9, /* 0xAC-0xAF */
+ 0xA4, 0xAA, 0xA4, 0xAB, 0xA4, 0xAC, 0xA4, 0xAD, /* 0xB0-0xB3 */
+ 0xA4, 0xAE, 0xA4, 0xAF, 0xA4, 0xB0, 0xA4, 0xB1, /* 0xB4-0xB7 */
+ 0xA4, 0xB2, 0xA4, 0xB4, 0xA4, 0xB5, 0xA4, 0xB6, /* 0xB8-0xBB */
+ 0xA4, 0xB7, 0xA4, 0xB8, 0xA4, 0xBA, 0xA4, 0xBB, /* 0xBC-0xBF */
+ 0xA4, 0xBC, 0xA4, 0xBD, 0xA4, 0xBE, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0xA4, 0xD6, 0xA4, 0xD7, /* 0xC4-0xC7 */
+ 0xA4, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0xA4, 0xD9, 0x00, 0x00, 0xA4, 0xDA, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0xDB, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0xDC, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0xA4, 0xDD, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0xA4, 0xDE, 0xA4, 0xDF, 0x00, 0x00, 0xA4, 0xE0, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0xA4, 0xE8, 0xA4, 0xEA, /* 0xE4-0xE7 */
+ 0xA4, 0xEC, 0x00, 0x00, 0xA4, 0xED, 0xA4, 0xEF, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0xA4, 0xF0, 0x00, 0x00, /* 0xEC-0xEF */
+ 0xA4, 0xB7, 0xA4, 0xF2, 0xA4, 0xF3, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xA4, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xA4, 0xF6, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+};
+
+static unsigned char u2c_20[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0xA1, 0xAA, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0xA1, 0xAE, 0xA1, 0xAF, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0xA1, 0xB0, 0xA1, 0xB1, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0xA2, 0xD3, 0xA2, 0xD4, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0xA1, 0xA5, 0xA1, 0xA6, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xA2, 0xB6, 0x00, 0x00, 0xA1, 0xC7, 0xA1, 0xC8, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA1, 0xD8, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0xA9, 0xF9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA9, 0xFA, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0xA9, 0xFB, 0xA9, 0xFC, 0xA9, 0xFD, /* 0x80-0x83 */
+ 0xA9, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+};
+
+static unsigned char u2c_21[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA1, 0xC9, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0xA2, 0xB5, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA7, 0xA4, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0xA2, 0xE0, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0xA2, 0xE5, 0xA2, 0xE2, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0xA7, 0xD9, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA1, 0xCA, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA8, 0xF7, /* 0x50-0x53 */
+ 0xA8, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA8, 0xFB, /* 0x58-0x5B */
+ 0xA8, 0xFC, 0xA8, 0xFD, 0xA8, 0xFE, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xA5, 0xB0, 0xA5, 0xB1, 0xA5, 0xB2, 0xA5, 0xB3, /* 0x60-0x63 */
+ 0xA5, 0xB4, 0xA5, 0xB5, 0xA5, 0xB6, 0xA5, 0xB7, /* 0x64-0x67 */
+ 0xA5, 0xB8, 0xA5, 0xB9, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xA5, 0xA1, 0xA5, 0xA2, 0xA5, 0xA3, 0xA5, 0xA4, /* 0x70-0x73 */
+ 0xA5, 0xA5, 0xA5, 0xA6, 0xA5, 0xA7, 0xA5, 0xA8, /* 0x74-0x77 */
+ 0xA5, 0xA9, 0xA5, 0xAA, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0xA1, 0xE7, 0xA1, 0xE8, 0xA1, 0xE6, 0xA1, 0xE9, /* 0x90-0x93 */
+ 0xA1, 0xEA, 0xA2, 0xD5, 0xA2, 0xD8, 0xA2, 0xD6, /* 0x94-0x97 */
+ 0xA2, 0xD9, 0xA2, 0xD7, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0xA2, 0xA1, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xA2, 0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+};
+
+static unsigned char u2c_22[512] = {
+ 0xA2, 0xA3, 0x00, 0x00, 0xA1, 0xD3, 0xA2, 0xA4, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA1, 0xD4, /* 0x04-0x07 */
+ 0xA1, 0xF4, 0x00, 0x00, 0x00, 0x00, 0xA1, 0xF5, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA2, 0xB3, /* 0x0C-0x0F */
+ 0x00, 0x00, 0xA2, 0xB2, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0xA1, 0xEE, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0xA1, 0xF0, 0xA1, 0xC4, 0x00, 0x00, /* 0x1C-0x1F */
+ 0xA1, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0xA1, 0xAB, 0x00, 0x00, 0xA1, 0xFC, /* 0x24-0x27 */
+ 0xA1, 0xFD, 0xA1, 0xFB, 0xA1, 0xFA, 0xA1, 0xF2, /* 0x28-0x2B */
+ 0xA1, 0xF3, 0x00, 0x00, 0xA2, 0xB1, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0xA1, 0xC5, 0xA1, 0xF1, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0xA1, 0xAD, 0xA1, 0xEF, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0xA1, 0xD6, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xA1, 0xC1, 0xA1, 0xD5, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0xA1, 0xC2, 0xA1, 0xC3, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0xA1, 0xEC, 0xA1, 0xED, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0xA1, 0xF8, 0xA1, 0xF9, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0xA1, 0xF6, 0xA1, 0xF7, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0xA2, 0xC1, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0xA1, 0xD1, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+};
+
+static unsigned char u2c_23[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0xA1, 0xD2, 0x00, 0x00, /* 0x10-0x13 */
+};
+
+static unsigned char u2c_24[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xA8, 0xE7, 0xA8, 0xE8, 0xA8, 0xE9, 0xA8, 0xEA, /* 0x60-0x63 */
+ 0xA8, 0xEB, 0xA8, 0xEC, 0xA8, 0xED, 0xA8, 0xEE, /* 0x64-0x67 */
+ 0xA8, 0xEF, 0xA8, 0xF0, 0xA8, 0xF1, 0xA8, 0xF2, /* 0x68-0x6B */
+ 0xA8, 0xF3, 0xA8, 0xF4, 0xA8, 0xF5, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0xA9, 0xE7, 0xA9, 0xE8, 0xA9, 0xE9, 0xA9, 0xEA, /* 0x74-0x77 */
+ 0xA9, 0xEB, 0xA9, 0xEC, 0xA9, 0xED, 0xA9, 0xEE, /* 0x78-0x7B */
+ 0xA9, 0xEF, 0xA9, 0xF0, 0xA9, 0xF1, 0xA9, 0xF2, /* 0x7C-0x7F */
+
+ 0xA9, 0xF3, 0xA9, 0xF4, 0xA9, 0xF5, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0xA9, 0xCD, 0xA9, 0xCE, 0xA9, 0xCF, 0xA9, 0xD0, /* 0x9C-0x9F */
+ 0xA9, 0xD1, 0xA9, 0xD2, 0xA9, 0xD3, 0xA9, 0xD4, /* 0xA0-0xA3 */
+ 0xA9, 0xD5, 0xA9, 0xD6, 0xA9, 0xD7, 0xA9, 0xD8, /* 0xA4-0xA7 */
+ 0xA9, 0xD9, 0xA9, 0xDA, 0xA9, 0xDB, 0xA9, 0xDC, /* 0xA8-0xAB */
+ 0xA9, 0xDD, 0xA9, 0xDE, 0xA9, 0xDF, 0xA9, 0xE0, /* 0xAC-0xAF */
+ 0xA9, 0xE1, 0xA9, 0xE2, 0xA9, 0xE3, 0xA9, 0xE4, /* 0xB0-0xB3 */
+ 0xA9, 0xE5, 0xA9, 0xE6, 0xA8, 0xCD, 0xA8, 0xCE, /* 0xB4-0xB7 */
+ 0xA8, 0xCF, 0xA8, 0xD0, 0xA8, 0xD1, 0xA8, 0xD2, /* 0xB8-0xBB */
+ 0xA8, 0xD3, 0xA8, 0xD4, 0xA8, 0xD5, 0xA8, 0xD6, /* 0xBC-0xBF */
+ 0xA8, 0xD7, 0xA8, 0xD8, 0xA8, 0xD9, 0xA8, 0xDA, /* 0xC0-0xC3 */
+ 0xA8, 0xDB, 0xA8, 0xDC, 0xA8, 0xDD, 0xA8, 0xDE, /* 0xC4-0xC7 */
+ 0xA8, 0xDF, 0xA8, 0xE0, 0xA8, 0xE1, 0xA8, 0xE2, /* 0xC8-0xCB */
+ 0xA8, 0xE3, 0xA8, 0xE4, 0xA8, 0xE5, 0xA8, 0xE6, /* 0xCC-0xCF */
+ 0xA8, 0xCD, 0xA8, 0xCE, 0xA8, 0xCF, 0xA8, 0xD0, /* 0xD0-0xD3 */
+ 0xA8, 0xD1, 0xA8, 0xD2, 0xA8, 0xD3, 0xA8, 0xD4, /* 0xD4-0xD7 */
+ 0xA8, 0xD5, 0xA8, 0xD6, 0xA8, 0xD7, 0xA8, 0xD8, /* 0xD8-0xDB */
+ 0xA8, 0xD9, 0xA8, 0xDA, 0xA8, 0xDB, 0xA8, 0xDC, /* 0xDC-0xDF */
+ 0xA8, 0xDD, 0xA8, 0xDE, 0xA8, 0xDF, 0xA8, 0xE0, /* 0xE0-0xE3 */
+ 0xA8, 0xE1, 0xA8, 0xE2, 0xA8, 0xE3, 0xA8, 0xE4, /* 0xE4-0xE7 */
+ 0xA8, 0xE5, 0xA8, 0xE6, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+};
+
+static unsigned char u2c_25[512] = {
+ 0xA6, 0xA1, 0xA6, 0xAC, 0xA6, 0xA2, 0xA6, 0xAD, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0xA6, 0xA3, 0xA6, 0xC8, 0xA6, 0xC7, 0xA6, 0xAE, /* 0x0C-0x0F */
+ 0xA6, 0xA4, 0xA6, 0xC2, 0xA6, 0xC1, 0xA6, 0xAF, /* 0x10-0x13 */
+ 0xA6, 0xA6, 0xA6, 0xC6, 0xA6, 0xC5, 0xA6, 0xB1, /* 0x14-0x17 */
+ 0xA6, 0xA5, 0xA6, 0xC4, 0xA6, 0xC3, 0xA6, 0xB0, /* 0x18-0x1B */
+ 0xA6, 0xA7, 0xA6, 0xBC, 0xA6, 0xC9, 0xA6, 0xCA, /* 0x1C-0x1F */
+ 0xA6, 0xB7, 0xA6, 0xCB, 0xA6, 0xCC, 0xA6, 0xB2, /* 0x20-0x23 */
+ 0xA6, 0xA9, 0xA6, 0xBE, 0xA6, 0xCD, 0xA6, 0xCE, /* 0x24-0x27 */
+ 0xA6, 0xB9, 0xA6, 0xCF, 0xA6, 0xD0, 0xA6, 0xB4, /* 0x28-0x2B */
+ 0xA6, 0xA8, 0xA6, 0xD1, 0xA6, 0xD2, 0xA6, 0xB8, /* 0x2C-0x2F */
+ 0xA6, 0xBD, 0xA6, 0xD3, 0xA6, 0xD4, 0xA6, 0xB3, /* 0x30-0x33 */
+ 0xA6, 0xAA, 0xA6, 0xD5, 0xA6, 0xD6, 0xA6, 0xBA, /* 0x34-0x37 */
+ 0xA6, 0xBF, 0xA6, 0xD7, 0xA6, 0xD8, 0xA6, 0xB5, /* 0x38-0x3B */
+ 0xA6, 0xAB, 0xA6, 0xD9, 0xA6, 0xDA, 0xA6, 0xBB, /* 0x3C-0x3F */
+ 0xA6, 0xDB, 0xA6, 0xDC, 0xA6, 0xC0, 0xA6, 0xDD, /* 0x40-0x43 */
+ 0xA6, 0xDE, 0xA6, 0xDF, 0xA6, 0xE0, 0xA6, 0xE1, /* 0x44-0x47 */
+ 0xA6, 0xE2, 0xA6, 0xE3, 0xA6, 0xE4, 0xA6, 0xB6, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0xA2, 0xC6, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0xA1, 0xE1, 0xA1, 0xE0, 0x00, 0x00, 0xA2, 0xC3, /* 0xA0-0xA3 */
+ 0xA2, 0xC7, 0xA2, 0xC8, 0xA2, 0xCB, 0xA2, 0xCA, /* 0xA4-0xA7 */
+ 0xA2, 0xC9, 0xA2, 0xCC, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0xA1, 0xE3, 0xA1, 0xE2, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0xA2, 0xBA, 0xA2, 0xB9, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0xA1, 0xE5, 0xA1, 0xE4, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0xA2, 0xB8, 0xA2, 0xB7, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0xA1, 0xDF, 0xA1, 0xDE, /* 0xC4-0xC7 */
+ 0xA2, 0xC2, 0x00, 0x00, 0x00, 0x00, 0xA1, 0xDB, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0xA1, 0xDD, 0xA1, 0xDC, /* 0xCC-0xCF */
+ 0xA2, 0xC4, 0xA2, 0xC5, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+};
+
+static unsigned char u2c_26[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0xA1, 0xDA, 0xA1, 0xD9, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0xA2, 0xCF, 0xA2, 0xCE, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0xA2, 0xD0, 0x00, 0x00, 0xA2, 0xD1, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0xA1, 0xCF, 0x00, 0x00, 0xA1, 0xCE, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xA2, 0xBC, 0xA2, 0xBD, 0x00, 0x00, 0xA2, 0xC0, /* 0x60-0x63 */
+ 0xA2, 0xBB, 0xA2, 0xBE, 0x00, 0x00, 0xA2, 0xBF, /* 0x64-0x67 */
+ 0xA2, 0xCD, 0xA2, 0xDB, 0xA2, 0xDC, 0x00, 0x00, /* 0x68-0x6B */
+ 0xA2, 0xDD, 0xA2, 0xDA, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+};
+
+static unsigned char u2c_30[512] = {
+ 0xA1, 0xA1, 0xA1, 0xA2, 0xA1, 0xA3, 0xA1, 0xA8, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0xA1, 0xB4, 0xA1, 0xB5, 0xA1, 0xB6, 0xA1, 0xB7, /* 0x08-0x0B */
+ 0xA1, 0xB8, 0xA1, 0xB9, 0xA1, 0xBA, 0xA1, 0xBB, /* 0x0C-0x0F */
+ 0xA1, 0xBC, 0xA1, 0xBD, 0x00, 0x00, 0xA1, 0xEB, /* 0x10-0x13 */
+ 0xA1, 0xB2, 0xA1, 0xB3, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0xAA, 0xA1, 0xAA, 0xA2, 0xAA, 0xA3, /* 0x40-0x43 */
+ 0xAA, 0xA4, 0xAA, 0xA5, 0xAA, 0xA6, 0xAA, 0xA7, /* 0x44-0x47 */
+ 0xAA, 0xA8, 0xAA, 0xA9, 0xAA, 0xAA, 0xAA, 0xAB, /* 0x48-0x4B */
+ 0xAA, 0xAC, 0xAA, 0xAD, 0xAA, 0xAE, 0xAA, 0xAF, /* 0x4C-0x4F */
+ 0xAA, 0xB0, 0xAA, 0xB1, 0xAA, 0xB2, 0xAA, 0xB3, /* 0x50-0x53 */
+ 0xAA, 0xB4, 0xAA, 0xB5, 0xAA, 0xB6, 0xAA, 0xB7, /* 0x54-0x57 */
+ 0xAA, 0xB8, 0xAA, 0xB9, 0xAA, 0xBA, 0xAA, 0xBB, /* 0x58-0x5B */
+ 0xAA, 0xBC, 0xAA, 0xBD, 0xAA, 0xBE, 0xAA, 0xBF, /* 0x5C-0x5F */
+ 0xAA, 0xC0, 0xAA, 0xC1, 0xAA, 0xC2, 0xAA, 0xC3, /* 0x60-0x63 */
+ 0xAA, 0xC4, 0xAA, 0xC5, 0xAA, 0xC6, 0xAA, 0xC7, /* 0x64-0x67 */
+ 0xAA, 0xC8, 0xAA, 0xC9, 0xAA, 0xCA, 0xAA, 0xCB, /* 0x68-0x6B */
+ 0xAA, 0xCC, 0xAA, 0xCD, 0xAA, 0xCE, 0xAA, 0xCF, /* 0x6C-0x6F */
+ 0xAA, 0xD0, 0xAA, 0xD1, 0xAA, 0xD2, 0xAA, 0xD3, /* 0x70-0x73 */
+ 0xAA, 0xD4, 0xAA, 0xD5, 0xAA, 0xD6, 0xAA, 0xD7, /* 0x74-0x77 */
+ 0xAA, 0xD8, 0xAA, 0xD9, 0xAA, 0xDA, 0xAA, 0xDB, /* 0x78-0x7B */
+ 0xAA, 0xDC, 0xAA, 0xDD, 0xAA, 0xDE, 0xAA, 0xDF, /* 0x7C-0x7F */
+
+ 0xAA, 0xE0, 0xAA, 0xE1, 0xAA, 0xE2, 0xAA, 0xE3, /* 0x80-0x83 */
+ 0xAA, 0xE4, 0xAA, 0xE5, 0xAA, 0xE6, 0xAA, 0xE7, /* 0x84-0x87 */
+ 0xAA, 0xE8, 0xAA, 0xE9, 0xAA, 0xEA, 0xAA, 0xEB, /* 0x88-0x8B */
+ 0xAA, 0xEC, 0xAA, 0xED, 0xAA, 0xEE, 0xAA, 0xEF, /* 0x8C-0x8F */
+ 0xAA, 0xF0, 0xAA, 0xF1, 0xAA, 0xF2, 0xAA, 0xF3, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0xAB, 0xA1, 0xAB, 0xA2, 0xAB, 0xA3, /* 0xA0-0xA3 */
+ 0xAB, 0xA4, 0xAB, 0xA5, 0xAB, 0xA6, 0xAB, 0xA7, /* 0xA4-0xA7 */
+ 0xAB, 0xA8, 0xAB, 0xA9, 0xAB, 0xAA, 0xAB, 0xAB, /* 0xA8-0xAB */
+ 0xAB, 0xAC, 0xAB, 0xAD, 0xAB, 0xAE, 0xAB, 0xAF, /* 0xAC-0xAF */
+ 0xAB, 0xB0, 0xAB, 0xB1, 0xAB, 0xB2, 0xAB, 0xB3, /* 0xB0-0xB3 */
+ 0xAB, 0xB4, 0xAB, 0xB5, 0xAB, 0xB6, 0xAB, 0xB7, /* 0xB4-0xB7 */
+ 0xAB, 0xB8, 0xAB, 0xB9, 0xAB, 0xBA, 0xAB, 0xBB, /* 0xB8-0xBB */
+ 0xAB, 0xBC, 0xAB, 0xBD, 0xAB, 0xBE, 0xAB, 0xBF, /* 0xBC-0xBF */
+ 0xAB, 0xC0, 0xAB, 0xC1, 0xAB, 0xC2, 0xAB, 0xC3, /* 0xC0-0xC3 */
+ 0xAB, 0xC4, 0xAB, 0xC5, 0xAB, 0xC6, 0xAB, 0xC7, /* 0xC4-0xC7 */
+ 0xAB, 0xC8, 0xAB, 0xC9, 0xAB, 0xCA, 0xAB, 0xCB, /* 0xC8-0xCB */
+ 0xAB, 0xCC, 0xAB, 0xCD, 0xAB, 0xCE, 0xAB, 0xCF, /* 0xCC-0xCF */
+ 0xAB, 0xD0, 0xAB, 0xD1, 0xAB, 0xD2, 0xAB, 0xD3, /* 0xD0-0xD3 */
+ 0xAB, 0xD4, 0xAB, 0xD5, 0xAB, 0xD6, 0xAB, 0xD7, /* 0xD4-0xD7 */
+ 0xAB, 0xD8, 0xAB, 0xD9, 0xAB, 0xDA, 0xAB, 0xDB, /* 0xD8-0xDB */
+ 0xAB, 0xDC, 0xAB, 0xDD, 0xAB, 0xDE, 0xAB, 0xDF, /* 0xDC-0xDF */
+ 0xAB, 0xE0, 0xAB, 0xE1, 0xAB, 0xE2, 0xAB, 0xE3, /* 0xE0-0xE3 */
+ 0xAB, 0xE4, 0xAB, 0xE5, 0xAB, 0xE6, 0xAB, 0xE7, /* 0xE4-0xE7 */
+ 0xAB, 0xE8, 0xAB, 0xE9, 0xAB, 0xEA, 0xAB, 0xEB, /* 0xE8-0xEB */
+ 0xAB, 0xEC, 0xAB, 0xED, 0xAB, 0xEE, 0xAB, 0xEF, /* 0xEC-0xEF */
+ 0xAB, 0xF0, 0xAB, 0xF1, 0xAB, 0xF2, 0xAB, 0xF3, /* 0xF0-0xF3 */
+ 0xAB, 0xF4, 0xAB, 0xF5, 0xAB, 0xF6, 0x00, 0x00, /* 0xF4-0xF7 */
+};
+
+static unsigned char u2c_31[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0xA4, 0xA1, 0xA4, 0xA2, 0xA4, 0xA3, /* 0x30-0x33 */
+ 0xA4, 0xA4, 0xA4, 0xA5, 0xA4, 0xA6, 0xA4, 0xA7, /* 0x34-0x37 */
+ 0xA4, 0xA8, 0xA4, 0xA9, 0xA4, 0xAA, 0xA4, 0xAB, /* 0x38-0x3B */
+ 0xA4, 0xAC, 0xA4, 0xAD, 0xA4, 0xAE, 0xA4, 0xAF, /* 0x3C-0x3F */
+ 0xA4, 0xB0, 0xA4, 0xB1, 0xA4, 0xB2, 0xA4, 0xB3, /* 0x40-0x43 */
+ 0xA4, 0xB4, 0xA4, 0xB5, 0xA4, 0xB6, 0xA4, 0xB7, /* 0x44-0x47 */
+ 0xA4, 0xB8, 0xA4, 0xB9, 0xA4, 0xBA, 0xA4, 0xBB, /* 0x48-0x4B */
+ 0xA4, 0xBC, 0xA4, 0xBD, 0xA4, 0xBE, 0xA4, 0xBF, /* 0x4C-0x4F */
+ 0xA4, 0xC0, 0xA4, 0xC1, 0xA4, 0xC2, 0xA4, 0xC3, /* 0x50-0x53 */
+ 0xA4, 0xC4, 0xA4, 0xC5, 0xA4, 0xC6, 0xA4, 0xC7, /* 0x54-0x57 */
+ 0xA4, 0xC8, 0xA4, 0xC9, 0xA4, 0xCA, 0xA4, 0xCB, /* 0x58-0x5B */
+ 0xA4, 0xCC, 0xA4, 0xCD, 0xA4, 0xCE, 0xA4, 0xCF, /* 0x5C-0x5F */
+ 0xA4, 0xD0, 0xA4, 0xD1, 0xA4, 0xD2, 0xA4, 0xD3, /* 0x60-0x63 */
+ 0xA4, 0xD4, 0xA4, 0xD5, 0xA4, 0xD6, 0xA4, 0xD7, /* 0x64-0x67 */
+ 0xA4, 0xD8, 0xA4, 0xD9, 0xA4, 0xDA, 0xA4, 0xDB, /* 0x68-0x6B */
+ 0xA4, 0xDC, 0xA4, 0xDD, 0xA4, 0xDE, 0xA4, 0xDF, /* 0x6C-0x6F */
+ 0xA4, 0xE0, 0xA4, 0xE1, 0xA4, 0xE2, 0xA4, 0xE3, /* 0x70-0x73 */
+ 0xA4, 0xE4, 0xA4, 0xE5, 0xA4, 0xE6, 0xA4, 0xE7, /* 0x74-0x77 */
+ 0xA4, 0xE8, 0xA4, 0xE9, 0xA4, 0xEA, 0xA4, 0xEB, /* 0x78-0x7B */
+ 0xA4, 0xEC, 0xA4, 0xED, 0xA4, 0xEE, 0xA4, 0xEF, /* 0x7C-0x7F */
+
+ 0xA4, 0xF0, 0xA4, 0xF1, 0xA4, 0xF2, 0xA4, 0xF3, /* 0x80-0x83 */
+ 0xA4, 0xF4, 0xA4, 0xF5, 0xA4, 0xF6, 0xA4, 0xF7, /* 0x84-0x87 */
+ 0xA4, 0xF8, 0xA4, 0xF9, 0xA4, 0xFA, 0xA4, 0xFB, /* 0x88-0x8B */
+ 0xA4, 0xFC, 0xA4, 0xFD, 0xA4, 0xFE, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0xEC, 0xE9, 0xEC, 0xA3, /* 0x90-0x93 */
+ 0xDF, 0xB2, 0xDE, 0xCC, 0xDF, 0xBE, 0xF1, 0xE9, /* 0x94-0x97 */
+ 0xF9, 0xBB, 0xCB, 0xA3, 0xEB, 0xE0, 0xDC, 0xB0, /* 0x98-0x9B */
+ 0xEF, 0xCB, 0xF4, 0xB8, 0xF2, 0xA2, 0xEC, 0xD1, /* 0x9C-0x9F */
+};
+
+static unsigned char u2c_32[512] = {
+ 0xA9, 0xB1, 0xA9, 0xB2, 0xA9, 0xB3, 0xA9, 0xB4, /* 0x00-0x03 */
+ 0xA9, 0xB5, 0xA9, 0xB6, 0xA9, 0xB7, 0xA9, 0xB8, /* 0x04-0x07 */
+ 0xA9, 0xB9, 0xA9, 0xBA, 0xA9, 0xBB, 0xA9, 0xBC, /* 0x08-0x0B */
+ 0xA9, 0xBD, 0xA9, 0xBE, 0xA9, 0xBF, 0xA9, 0xC0, /* 0x0C-0x0F */
+ 0xA9, 0xC1, 0xA9, 0xC2, 0xA9, 0xC3, 0xA9, 0xC4, /* 0x10-0x13 */
+ 0xA9, 0xC5, 0xA9, 0xC6, 0xA9, 0xC7, 0xA9, 0xC8, /* 0x14-0x17 */
+ 0xA9, 0xC9, 0xA9, 0xCA, 0xA9, 0xCB, 0xA9, 0xCC, /* 0x18-0x1B */
+ 0xA2, 0xDF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0xEC, 0xE9, 0xEC, 0xA3, 0xDF, 0xB2, 0xDE, 0xCC, /* 0x20-0x23 */
+ 0xE7, 0xE9, 0xD7, 0xBF, 0xF6, 0xD2, 0xF8, 0xA2, /* 0x24-0x27 */
+ 0xCE, 0xFA, 0xE4, 0xA8, 0xEA, 0xC5, 0xFB, 0xFD, /* 0x28-0x2B */
+ 0xE2, 0xA9, 0xD9, 0xCA, 0xD1, 0xD1, 0xF7, 0xCF, /* 0x2C-0x2F */
+ 0xEC, 0xED, 0xF1, 0xBB, 0xEA, 0xF3, 0xDE, 0xE4, /* 0x30-0x33 */
+ 0xD9, 0xA3, 0xF7, 0xE5, 0xEE, 0xAF, 0xF5, 0xE6, /* 0x34-0x37 */
+ 0xD6, 0xCC, 0xD3, 0xDB, 0xFB, 0xBC, 0xF9, 0xCA, /* 0x38-0x3B */
+ 0xCA, 0xF8, 0xD0, 0xEA, 0xED, 0xC0, 0xFA, 0xF0, /* 0x3C-0x3F */
+ 0xF0, 0xAE, 0xFD, 0xCC, 0xED, 0xBB, 0xF2, 0xB8, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xA8, 0xB1, 0xA8, 0xB2, 0xA8, 0xB3, 0xA8, 0xB4, /* 0x60-0x63 */
+ 0xA8, 0xB5, 0xA8, 0xB6, 0xA8, 0xB7, 0xA8, 0xB8, /* 0x64-0x67 */
+ 0xA8, 0xB9, 0xA8, 0xBA, 0xA8, 0xBB, 0xA8, 0xBC, /* 0x68-0x6B */
+ 0xA8, 0xBD, 0xA8, 0xBE, 0xA8, 0xBF, 0xA8, 0xC0, /* 0x6C-0x6F */
+ 0xA8, 0xC1, 0xA8, 0xC2, 0xA8, 0xC3, 0xA8, 0xC4, /* 0x70-0x73 */
+ 0xA8, 0xC5, 0xA8, 0xC6, 0xA8, 0xC7, 0xA8, 0xC8, /* 0x74-0x77 */
+ 0xA8, 0xC9, 0xA8, 0xCA, 0xA8, 0xCB, 0xA8, 0xCC, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA2, 0xDE, /* 0x7C-0x7F */
+
+ 0xEC, 0xE9, 0xEC, 0xA3, 0xDF, 0xB2, 0xDE, 0xCC, /* 0x80-0x83 */
+ 0xE7, 0xE9, 0xD7, 0xBF, 0xF6, 0xD2, 0xF8, 0xA2, /* 0x84-0x87 */
+ 0xCE, 0xFA, 0xE4, 0xA8, 0xEA, 0xC5, 0xFB, 0xFD, /* 0x88-0x8B */
+ 0xE2, 0xA9, 0xD9, 0xCA, 0xD1, 0xD1, 0xF7, 0xCF, /* 0x8C-0x8F */
+ 0xEC, 0xED, 0xF1, 0xBB, 0xEA, 0xF3, 0xDE, 0xE4, /* 0x90-0x93 */
+ 0xD9, 0xA3, 0xF7, 0xE5, 0xEE, 0xAF, 0xF5, 0xE6, /* 0x94-0x97 */
+ 0xD6, 0xCC, 0xDD, 0xFA, 0xD1, 0xFB, 0xD2, 0xB3, /* 0x98-0x9B */
+ 0xEE, 0xEA, 0xE9, 0xD0, 0xEC, 0xD4, 0xF1, 0xBC, /* 0x9C-0x9F */
+ 0xFA, 0xA3, 0xFD, 0xCC, 0xDE, 0xD0, 0xEF, 0xE1, /* 0xA0-0xA3 */
+ 0xDF, 0xBE, 0xF1, 0xE9, 0xF9, 0xBB, 0xF1, 0xA7, /* 0xA4-0xA7 */
+ 0xE9, 0xD3, 0xEC, 0xA2, 0xF0, 0xF3, 0xF9, 0xCA, /* 0xA8-0xAB */
+ 0xCA, 0xF8, 0xD0, 0xEA, 0xED, 0xC0, 0xFA, 0xF0, /* 0xAC-0xAF */
+ 0xE5, 0xA8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+};
+
+static unsigned char u2c_33[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0xA7, 0xC9, 0xA7, 0xCA, 0xA7, 0xCB, 0xA7, 0xCC, /* 0x80-0x83 */
+ 0xA7, 0xCD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0xA7, 0xBA, 0xA7, 0xBB, 0xA7, 0xDC, 0xA7, 0xDD, /* 0x88-0x8B */
+ 0xA7, 0xDE, 0xA7, 0xB6, 0xA7, 0xB7, 0xA7, 0xB8, /* 0x8C-0x8F */
+ 0xA7, 0xD4, 0xA7, 0xD5, 0xA7, 0xD6, 0xA7, 0xD7, /* 0x90-0x93 */
+ 0xA7, 0xD8, 0xA7, 0xA1, 0xA7, 0xA2, 0xA7, 0xA3, /* 0x94-0x97 */
+ 0xA7, 0xA5, 0xA7, 0xAB, 0xA7, 0xAC, 0xA7, 0xAD, /* 0x98-0x9B */
+ 0xA7, 0xAE, 0xA7, 0xAF, 0xA7, 0xB0, 0xA7, 0xB1, /* 0x9C-0x9F */
+ 0xA7, 0xB2, 0xA7, 0xB3, 0xA7, 0xB4, 0xA7, 0xA7, /* 0xA0-0xA3 */
+ 0xA7, 0xA8, 0xA7, 0xA9, 0xA7, 0xAA, 0xA7, 0xBD, /* 0xA4-0xA7 */
+ 0xA7, 0xBE, 0xA7, 0xE5, 0xA7, 0xE6, 0xA7, 0xE7, /* 0xA8-0xAB */
+ 0xA7, 0xE8, 0xA7, 0xE1, 0xA7, 0xE2, 0xA7, 0xE3, /* 0xAC-0xAF */
+ 0xA7, 0xBF, 0xA7, 0xC0, 0xA7, 0xC1, 0xA7, 0xC2, /* 0xB0-0xB3 */
+ 0xA7, 0xC3, 0xA7, 0xC4, 0xA7, 0xC5, 0xA7, 0xC6, /* 0xB4-0xB7 */
+ 0xA7, 0xC7, 0xA7, 0xC8, 0xA7, 0xCE, 0xA7, 0xCF, /* 0xB8-0xBB */
+ 0xA7, 0xD0, 0xA7, 0xD1, 0xA7, 0xD2, 0xA7, 0xD3, /* 0xBC-0xBF */
+ 0xA7, 0xDA, 0xA7, 0xDB, 0xA2, 0xE3, 0xA7, 0xEC, /* 0xC0-0xC3 */
+ 0xA7, 0xA6, 0xA7, 0xE0, 0xA7, 0xEF, 0xA2, 0xE1, /* 0xC4-0xC7 */
+ 0xA7, 0xBC, 0xA7, 0xED, 0xA7, 0xB5, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA7, 0xB9, /* 0xCC-0xCF */
+ 0xA7, 0xEA, 0x00, 0x00, 0x00, 0x00, 0xA7, 0xEB, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0xA7, 0xDF, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xA2, 0xE4, 0x00, 0x00, 0x00, 0x00, 0xA7, 0xE4, /* 0xD8-0xDB */
+ 0xA7, 0xEE, 0xA7, 0xE9, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+};
+
+static unsigned char u2c_4E[512] = {
+ 0xEC, 0xE9, 0xEF, 0xCB, 0x00, 0x00, 0xF6, 0xD2, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD8, 0xB2, /* 0x04-0x07 */
+ 0xED, 0xDB, 0xDF, 0xB2, 0xDF, 0xBE, 0xF9, 0xBB, /* 0x08-0x0B */
+ 0x00, 0x00, 0xDC, 0xF4, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0xF5, 0xE4, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0xF3, 0xA6, 0xDD, 0xE0, 0xE1, 0xA6, 0x00, 0x00, /* 0x14-0x17 */
+ 0xCE, 0xF8, 0xDC, 0xB0, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0xE3, 0xAA, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0xF1, 0xE9, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0xCD, 0xFA, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0xFC, 0xAF, 0xD3, 0xA1, 0x00, 0x00, 0xF1, 0xAB, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0xE7, 0xD1, 0xD2, 0xAC, /* 0x40-0x43 */
+ 0x00, 0x00, 0xCE, 0xF9, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF1, 0xFD, /* 0x48-0x4B */
+ 0x00, 0x00, 0xDE, 0xBF, 0xFB, 0xBA, 0xF9, 0xB9, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0xCE, 0xD2, 0x00, 0x00, /* 0x54-0x57 */
+ 0xE3, 0xAB, 0xEB, 0xE0, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0xCE, 0xFA, 0xCB, 0xF7, 0xE5, 0xA5, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCA, 0xE1, /* 0x68-0x6B */
+ 0x00, 0x00, 0xD4, 0xCC, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEA, 0xE1, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0xDC, 0xE3, 0xDF, 0xAD, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0xCB, 0xEB, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0xD5, 0xAF, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0xD6, 0xF5, 0x00, 0x00, /* 0x84-0x87 */
+ 0xE5, 0xF8, 0x00, 0x00, 0x00, 0x00, 0xDE, 0xC0, /* 0x88-0x8B */
+ 0xEC, 0xA3, 0x00, 0x00, 0xE9, 0xCD, 0x00, 0x00, /* 0x8C-0x8F */
+ 0xEA, 0xA7, 0xE9, 0xF6, 0xFB, 0xBB, 0x00, 0x00, /* 0x90-0x93 */
+ 0xE7, 0xE9, 0xEF, 0xCC, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0xD0, 0xE6, 0x00, 0x00, 0x00, 0x00, 0xDE, 0xC1, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0xE4, 0xAC, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0xD8, 0xCC, 0xF9, 0xF1, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0xCE, 0xDF, 0xFA, 0xA4, 0xE6, 0xB2, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xFA, 0xFB, 0x00, 0x00, 0x00, 0x00, 0xFA, 0xBD, /* 0xA8-0xAB */
+ 0xCC, 0xC8, 0xEF, 0xCD, 0xD5, 0xD5, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0xD3, 0xA2, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0xEC, 0xD1, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0xE4, 0xA7, 0xEC, 0xD2, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xF6, 0xB1, 0x00, 0x00, 0x00, 0x00, 0xCE, 0xFB, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0xD0, 0xD1, 0xCB, 0xBF, /* 0xC8-0xCB */
+ 0x00, 0x00, 0xED, 0xA4, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xED, 0xA8, 0xDE, 0xC2, 0xF6, 0xE2, 0xED, 0xDC, /* 0xD4-0xD7 */
+ 0xDC, 0xF5, 0xE0, 0xB9, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0xD4, 0xCE, 0x00, 0x00, 0xF4, 0xB5, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD3, 0xDB, /* 0xE0-0xE3 */
+ 0xD6, 0xB5, 0xEC, 0xA4, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0xE4, 0xE6, 0x00, 0x00, 0xF1, 0xEA, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0xCB, 0xEC, 0xCB, 0xC0, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEC, 0xF2, /* 0xF8-0xFB */
+};
+
+static unsigned char u2c_4F[512] = {
+ 0x00, 0x00, 0xD0, 0xEA, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0xF9, 0xF2, 0xEC, 0xA5, 0xD0, 0xDF, /* 0x08-0x0B */
+ 0x00, 0x00, 0xE7, 0xEA, 0xD0, 0xEB, 0xDC, 0xD1, /* 0x0C-0x0F */
+ 0xDB, 0xE9, 0xFD, 0xCC, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDB, 0xD7, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0xDA, 0xE1, 0x00, 0x00, 0xD6, 0xB6, 0x00, 0x00, /* 0x34-0x37 */
+ 0xE3, 0xDF, 0x00, 0x00, 0xDE, 0xC3, 0x00, 0x00, /* 0x38-0x3B */
+ 0xDE, 0xC4, 0xCA, 0xA1, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEE, 0xEC, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0xD3, 0xA3, 0xEE, 0xB7, /* 0x44-0x47 */
+ 0xF8, 0xCF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0xEA, 0xC8, 0xEE, 0xB8, 0xF1, 0xAC, /* 0x4C-0x4F */
+ 0xF1, 0xA5, 0xE9, 0xCE, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0xF9, 0xBC, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0xE5, 0xF9, 0xEC, 0xEA, 0xDD, 0xD6, /* 0x58-0x5B */
+ 0xED, 0xC2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0xF8, 0xA5, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0xBA, /* 0x6C-0x6F */
+ 0xDB, 0xD8, 0x00, 0x00, 0x00, 0x00, 0xCA, 0xA2, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0xD1, 0xCD, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0xEE, 0xED, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0xEC, 0xEB, 0xDE, 0xC5, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0xE3, 0xE0, 0x00, 0x00, 0xCA, 0xC9, /* 0x80-0x83 */
+ 0xF2, 0xE9, 0x00, 0x00, 0xD5, 0xCE, 0x00, 0x00, /* 0x84-0x87 */
+ 0xF6, 0xB6, 0x00, 0x00, 0xCE, 0xC2, 0xD6, 0xC7, /* 0x88-0x8B */
+ 0x00, 0x00, 0xE3, 0xB4, 0x00, 0x00, 0xF1, 0xAD, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xEA, 0xE2, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0xD7, 0xC2, 0x00, 0x00, /* 0x94-0x97 */
+ 0xF3, 0xA7, 0x00, 0x00, 0x00, 0x00, 0xCD, 0xEA, /* 0x98-0x9B */
+ 0x00, 0x00, 0xEB, 0xEE, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0xD9, 0xB2, 0xFD, 0xA5, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0xF6, 0xD5, 0xD5, 0xE2, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xB5, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0xCC, 0xF5, 0xF5, 0xB5, /* 0xC0-0xC3 */
+ 0xE4, 0xAD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xE7, 0xEB, 0xF1, 0xD5, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0xF0, 0xBB, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0xE9, 0xB5, 0x00, 0x00, 0xCC, 0xC9, /* 0xD0-0xD3 */
+ 0xFA, 0xD5, 0x00, 0x00, 0x00, 0x00, 0xE1, 0xD4, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0xD7, 0xD6, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0xDC, 0xC1, 0x00, 0x00, 0xDE, 0xC6, /* 0xDC-0xDF */
+ 0xFA, 0xEF, 0xE3, 0xE1, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0xF3, 0xDC, 0xF6, /* 0xEC-0xEF */
+ 0x00, 0x00, 0xCE, 0xFC, 0x00, 0x00, 0xDB, 0xC4, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0xF8, 0xF1, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0xDC, 0xE4, 0x00, 0x00, 0xE5, 0xEF, 0x00, 0x00, /* 0xF8-0xFB */
+};
+
+static unsigned char u2c_50[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0xDC, 0xB1, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0xD5, 0xD6, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0xF3, 0xDA, 0x00, 0x00, 0xCB, 0xC1, /* 0x08-0x0B */
+ 0x00, 0x00, 0xDB, 0xC3, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0xD9, 0xFA, 0xD3, 0xEE, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0xFA, 0xB8, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0xFD, 0xA6, 0xEB, 0xEF, 0x00, 0x00, /* 0x18-0x1B */
+ 0xF4, 0xA6, 0x00, 0x00, 0xCC, 0xCA, 0xF3, 0xA8, /* 0x1C-0x1F */
+ 0x00, 0x00, 0xF3, 0xDB, 0x00, 0x00, 0xDB, 0xA7, /* 0x20-0x23 */
+ 0xF6, 0xB7, 0x00, 0x00, 0xCF, 0xE6, 0xF0, 0xF2, /* 0x24-0x27 */
+ 0xCB, 0xDA, 0x00, 0x00, 0xE7, 0xD2, 0xD7, 0xC3, /* 0x28-0x2B */
+ 0xF6, 0xF0, 0xE8, 0xDE, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0xA6, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0xE7, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCA, 0xA3, /* 0x44-0x47 */
+ 0xCC, 0xA7, 0xEA, 0xC9, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xB6, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0xFA, 0xA5, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0xF1, 0xAE, 0x00, 0x00, /* 0x58-0x5B */
+ 0xEF, 0xCE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0xCB, 0xED, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0xF6, 0xB0, 0xEF, 0xCF, 0xE9, 0xCF, 0x00, 0x00, /* 0x74-0x77 */
+ 0xF7, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0xCE, 0xD3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0xDC, 0xF7, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0xDB, 0xA8, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xCB, 0xF8, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0xDF, 0xA1, 0xDD, 0xE1, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0xF5, 0xCA, 0xE9, 0xB6, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0xE7, 0xEC, 0xEE, 0xEE, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0xF3, 0xF0, 0x00, 0x00, 0xDF, 0xBF, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0xCC, 0xCB, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0xD0, 0xC1, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xF4, 0xD2, 0xE0, 0xBA, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDF, 0xC0, /* 0xCC-0xCF */
+ 0x00, 0x00, 0xCE, 0xE0, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0xDC, 0xD2, 0xFD, 0xEA, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0xD6, 0xF6, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0xEA, 0xCA, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0xE8, 0xE9, 0x00, 0x00, 0xE3, 0xAC, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0xF3, 0xD0, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xCA, 0xA4, 0x00, 0x00, 0xDB, 0xF8, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDE, 0xC7, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_51[512] = {
+ 0xEB, 0xF0, 0xF1, 0xD6, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0xE5, 0xE2, 0x00, 0x00, 0xCC, 0xCC, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0xCB, 0xFB, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0xEA, 0xE3, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDF, 0xC1, /* 0x1C-0x1F */
+ 0x00, 0x00, 0xD6, 0xED, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0xD0, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0xEE, 0xB9, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD5, 0xE3, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0xD1, 0xD3, 0x00, 0x00, /* 0x38-0x3B */
+ 0xE5, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0xE8, 0xB4, 0xEB, 0xC3, 0x00, 0x00, 0xEA, 0xAA, /* 0x40-0x43 */
+ 0xFA, 0xFC, 0xF5, 0xF6, 0xF0, 0xBC, 0xFD, 0xD4, /* 0x44-0x47 */
+ 0xE0, 0xBB, 0xCE, 0xC3, 0x00, 0x00, 0xD0, 0xBA, /* 0x48-0x4B */
+ 0xF7, 0xBA, 0xD8, 0xF3, 0xF7, 0xCD, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0xE4, 0xAE, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0xD4, 0xDF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0xD0, 0xE7, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0xEC, 0xFD, 0x00, 0x00, 0xD2, 0xAE, /* 0x64-0x67 */
+ 0xEE, 0xEF, 0xD5, 0xD7, 0xEA, 0xE4, 0xF8, 0xA2, /* 0x68-0x6B */
+ 0xCD, 0xEB, 0xD7, 0xBF, 0xFB, 0xB1, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0xCD, 0xEC, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0xDC, 0xB2, 0xD0, 0xEC, 0xCE, 0xFD, /* 0x74-0x77 */
+ 0xEE, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0xCC, 0xC2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0xD0, 0xED, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0xF7, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0xF3, 0xFC, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0xEE, 0xA2, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0xD9, 0xB3, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0xD8, 0xF4, 0x00, 0x00, 0xE9, 0xB7, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0xCE, 0xAE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0xD9, 0xA2, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0xD8, 0xF1, 0x00, 0x00, /* 0xA8-0xAB */
+ 0xD4, 0xCF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0xA7, 0xD5, 0xD2, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0xD6, 0xA9, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xF4, 0xA2, 0x00, 0x00, 0xF1, 0xD7, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xD5, 0xD8, 0x00, 0x00, 0xF0, 0xBD, /* 0xC8-0xCB */
+ 0xD7, 0xD0, 0xD4, 0xD0, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0xD7, 0xCF, 0xEB, 0xEA, 0xFD, 0xEB, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xDB, 0xED, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0xFC, 0xC5, 0xCB, 0xC2, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0xFD, 0xD5, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0xF4, 0xC8, 0xE8, 0xEA, 0xF5, 0xF3, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0xF9, 0xDE, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_52[512] = {
+ 0xD3, 0xEF, 0x00, 0x00, 0x00, 0x00, 0xEC, 0xD3, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0xDD, 0xC2, 0xEF, 0xB7, /* 0x04-0x07 */
+ 0xE7, 0xD4, 0x00, 0x00, 0xCA, 0xCA, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0xD9, 0xFB, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0xFA, 0xFD, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD6, 0xAA, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0xF4, 0xF8, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0xF7, 0xF7, 0xDC, 0xAC, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0xD7, 0xD7, 0xDF, 0xA2, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0xCE, 0xBE, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xD3, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0xF0, 0xA4, 0xE1, 0xEC, /* 0x34-0x37 */
+ 0xCF, 0xE7, 0xF3, 0xCB, 0xED, 0xA9, 0xCA, 0xBE, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF4, 0xEF, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF6, 0xCE, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0xDE, 0xFB, 0xD0, 0xBB, /* 0x48-0x4B */
+ 0xD5, 0xB7, 0xEE, 0xF1, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0xF4, 0xA8, 0x00, 0x00, 0xDC, 0xF8, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCB, 0xA7, /* 0x58-0x5B */
+ 0x00, 0x00, 0xDA, 0xCE, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0xE0, 0xE6, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0xED, 0xA5, 0xEE, 0xF2, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0xF9, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0xF9, 0xDC, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0xF3, 0xDC, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0xF8, 0xF2, 0x00, 0x00, 0xF4, 0xF9, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xF1, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD0, 0xBC, /* 0x84-0x87 */
+ 0xDB, 0xF9, 0xD7, 0xB1, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0xCB, 0xFC, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xF0, 0xA5, 0xCB, 0xFD, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD5, 0xF4, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCD, 0xED, /* 0x9C-0x9F */
+ 0xCA, 0xA5, 0x00, 0x00, 0x00, 0x00, 0xD6, 0xAB, /* 0xA0-0xA3 */
+ 0xD0, 0xC2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0xF0, 0xBE, 0xD2, 0xBD, 0xCC, 0xA4, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0xFA, 0xB6, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0xCC, 0xCD, 0x00, 0x00, 0xDA, 0xFA, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0xF6, 0xCF, 0x00, 0x00, 0xE9, 0xB8, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xD8, 0xF5, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0xCC, 0xCE, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0xD7, 0xCD, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0xD4, 0xD1, 0xE9, 0xED, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xCA, 0xEB, 0xD9, 0xE2, 0x00, 0x00, 0xFD, 0xB2, /* 0xD8-0xDB */
+ 0x00, 0x00, 0xE3, 0xAD, 0xD6, 0xCC, 0xD9, 0xB4, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0xA7, 0xEE, 0xD3, /* 0xE0-0xE3 */
+ 0xD0, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xB3, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0xD5, 0xE4, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0xCF, 0xE8, 0x00, 0x00, 0xED, 0xC3, 0xD0, 0xB2, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0xCE, 0xFE, 0xDA, 0xA8, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_53[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0xF8, 0xD0, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0xFD, 0xD6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0xF8, 0xD1, 0x00, 0x00, 0xF8, 0xD2, /* 0x0C-0x0F */
+ 0xDC, 0xD3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0xDD, 0xE2, 0xFB, 0xF9, 0xDD, 0xC1, /* 0x14-0x17 */
+ 0x00, 0x00, 0xE3, 0xB5, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0xED, 0xDD, 0xCE, 0xC4, 0x00, 0x00, 0xCB, 0xA1, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xDD, 0xE3, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xDD, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0xF9, 0xAF, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD2, 0xFB, /* 0x3C-0x3F */
+ 0xCF, 0xA1, 0xE4, 0xA8, 0x00, 0x00, 0xF4, 0xB6, /* 0x40-0x43 */
+ 0xEC, 0xFE, 0x00, 0x00, 0x00, 0x00, 0xE3, 0xAE, /* 0x44-0x47 */
+ 0xE7, 0xED, 0xFD, 0xC1, 0xDA, 0xE2, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0xD8, 0xB3, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0xDD, 0xE4, 0xF0, 0xEF, 0xF6, 0xF1, /* 0x50-0x53 */
+ 0xFA, 0xF0, 0x00, 0x00, 0x00, 0x00, 0xD1, 0xF5, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0xDA, 0xCF, 0x00, 0x00, /* 0x58-0x5B */
+ 0xDC, 0xD4, 0x00, 0x00, 0xDC, 0xA6, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xEF, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0xCE, 0xCF, 0x00, 0x00, /* 0x64-0x67 */
+ 0xE0, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD9, 0xD6, /* 0x6C-0x6F */
+ 0xEC, 0xD4, 0xEA, 0xCB, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0xCA, 0xBF, 0xD5, 0xB0, 0x00, 0x00, 0xCF, 0xE9, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0xF1, 0xED, 0x00, 0x00, 0xCC, 0xCF, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0xE4, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0xED, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0xD7, 0xD8, 0x00, 0x00, 0xFD, 0xA7, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEA, 0xAB, /* 0x9C-0x9F */
+ 0xF6, 0xB2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0xCF, 0xF0, 0xF9, 0xBD, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0xE6, 0xF4, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCB, 0xDB, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF3, 0xD1, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0xE9, 0xD1, 0xF3, 0xA9, 0xD0, 0xE0, 0xE9, 0xD2, /* 0xC8-0xCB */
+ 0x00, 0x00, 0xDA, 0xE3, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xE2, 0xD2, 0x00, 0x00, 0xF6, 0xA2, 0xE1, 0xF4, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDA, 0xE4, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xE7, 0xD5, 0xF5, 0xBF, 0xCF, 0xA2, /* 0xE0-0xE3 */
+ 0xCD, 0xAF, 0xCF, 0xA3, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0xCD, 0xB0, 0xF1, 0xFE, 0xD0, 0xA3, /* 0xE8-0xEB */
+ 0xE1, 0xAF, 0xF8, 0xA3, 0x00, 0x00, 0xCA, 0xA6, /* 0xEC-0xEF */
+ 0xF7, 0xBB, 0xF2, 0xEA, 0xDE, 0xC8, 0xE9, 0xD3, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0xDE, 0xC9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+};
+
+static unsigned char u2c_54[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xDE, /* 0x00-0x03 */
+ 0xCA, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0xF9, 0xEA, 0xD1, 0xCE, 0xEE, 0xD4, 0x00, 0x00, /* 0x08-0x0B */
+ 0xD4, 0xD2, 0xD9, 0xA3, 0xFD, 0xA8, 0xD7, 0xD9, /* 0x0C-0x0F */
+ 0xF7, 0xCE, 0xFA, 0xBE, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCF, 0xD6, /* 0x18-0x1B */
+ 0x00, 0x00, 0xD7, 0xF0, 0x00, 0x00, 0xEB, 0xE1, /* 0x1C-0x1F */
+ 0xF8, 0xC5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0xDC, 0xFA, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0xDD, 0xC3, 0x00, 0x00, 0xF9, 0xDF, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE7, 0xEF, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0xFD, 0xE5, 0xF6, 0xA3, 0x00, 0x00, 0xD9, 0xFC, /* 0x38-0x3B */
+ 0xFD, 0xA9, 0x00, 0x00, 0xE7, 0xEE, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0xD5, 0xE5, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0xEF, 0xD0, 0x00, 0x00, 0xCD, 0xB1, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0xF7, 0xA2, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0xF1, 0xB2, 0x00, 0x00, 0xF1, 0xB1, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0xCD, 0xB2, 0x00, 0x00, 0xDA, 0xAB, /* 0x70-0x73 */
+ 0x00, 0x00, 0xCA, 0xA7, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE3, 0xE2, /* 0x78-0x7B */
+ 0xFB, 0xBC, 0xD9, 0xA4, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0xEE, 0xBA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0xF8, 0xD3, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0xFB, 0xFA, 0x00, 0x00, 0xCF, 0xA4, 0x00, 0x00, /* 0x8C-0x8F */
+ 0xDC, 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0xF6, 0xE3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xED, 0xAA, 0x00, 0x00, 0x00, 0x00, 0xF2, 0xA1, /* 0xA8-0xAB */
+ 0xCE, 0xE1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0xA6, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0xF9, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0xEC, 0xD6, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0xE4, 0xEE, 0xF9, 0xA1, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xFB, 0xEF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0xF9, 0xEB, 0xEE, 0xA3, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xEA, 0xAC, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0xCA, 0xA8, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xF4, 0xFA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0xCD, 0xD6, 0xFC, 0xF6, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0xF4, 0xC9, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0xF8, 0xD4, 0x00, 0x00, /* 0xF8-0xFB */
+};
+
+static unsigned char u2c_55[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0xF8, 0xA6, 0x00, 0x00, 0xDE, 0xCA, 0xF2, 0xC6, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0xD7, 0xDA, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xD3, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0xD8, 0xC5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEA, 0xE6, /* 0x2C-0x2F */
+ 0x00, 0x00, 0xF3, 0xDD, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0xE4, 0xDA, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0xF6, 0xE4, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0xF6, 0xF2, 0x00, 0x00, 0xDF, 0xC2, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD9, 0xFD, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0xF6, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0xD3, 0xBA, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0xE4, 0xAF, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xE1, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0xF0, 0xA6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0xCB, 0xD3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0xE0, 0xBC, 0x00, 0x00, 0xF4, 0xCA, 0xD4, 0xFA, /* 0x84-0x87 */
+ 0x00, 0x00, 0xFD, 0xAA, 0xF9, 0xE2, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0xF4, 0xB7, 0xFD, 0xC2, 0xFC, 0xB0, 0x00, 0x00, /* 0x98-0x9B */
+ 0xFD, 0xEC, 0xCA, 0xE2, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xBD, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0xEA, 0xE7, 0xDF, 0xC3, 0xD1, 0xD2, /* 0xA8-0xAB */
+ 0xCE, 0xE2, 0x00, 0x00, 0xD3, 0xA4, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0xFD, 0xAB, 0x00, 0x00, 0xDF, 0xE0, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xF2, 0xC7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0xE7, 0xF0, 0x00, 0x00, /* 0xD8-0xDB */
+ 0xD0, 0xEE, 0x00, 0x00, 0x00, 0x00, 0xF3, 0xAA, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDE, 0xCB, /* 0xE0-0xE3 */
+ 0xF6, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0xE1, 0xF5, 0xF1, 0xB3, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_56[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0xF7, 0xA3, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0xCA, 0xA9, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0xCF, 0xA5, 0x00, 0x00, 0x00, 0x00, 0xDF, 0xC4, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE1, 0xB0, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0xF0, 0xBF, 0x00, 0x00, /* 0x30-0x33 */
+ 0xF6, 0xA4, 0x00, 0x00, 0xE3, 0xB6, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0xC6, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0xD0, 0xEF, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xED, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0xDD, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0xFC, 0xF7, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0xE6, 0xBF, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0xDE, 0xAD, 0x00, 0x00, 0xFA, 0xBF, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0xE5, 0xF1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0xED, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0xD2, 0xA5, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0xFD, 0xEE, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0xF5, 0xB6, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0xF6, 0xDE, 0xCC, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0xFC, 0xDE, 0x00, 0x00, /* 0xDC-0xDF */
+ 0xEC, 0xD7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0xCD, 0xDD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xD6, 0xB7, 0xCD, 0xB3, 0x00, 0x00, /* 0xF8-0xFB */
+};
+
+static unsigned char u2c_57[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xD5, /* 0x00-0x03 */
+ 0xE5, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0xCF, 0xEA, 0x00, 0x00, 0x00, 0x00, 0xCF, 0xD0, /* 0x08-0x0B */
+ 0x00, 0x00, 0xEA, 0xCC, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0xEA, 0xAE, 0xEA, 0xAD, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0xD3, 0xF1, 0x00, 0x00, /* 0x14-0x17 */
+ 0xD3, 0xA5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF7, 0xCF, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0xEE, 0xA4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0xD0, 0xA4, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xF2, 0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD0, 0xF0, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0xF2, 0xA3, 0x00, 0x00, 0xF7, 0xF8, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD0, 0xB3, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0xDB, 0xA9, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0xD3, 0xBB, 0xCA, 0xEC, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xF1, 0xA6, 0xCB, 0xD5, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0xF7, 0xE7, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0xCD, 0xDE, 0x00, 0x00, 0xF7, 0xA4, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0xF8, 0xC0, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0xD3, 0xDD, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xCC, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0xCF, 0xA6, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0xF6, 0xF3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0xF7, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0xD3, 0xDC, 0x00, 0x00, 0x00, 0x00, 0xFA, 0xFE, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0xA7, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0xEB, 0xD9, 0x00, 0x00, 0xCF, 0xA7, 0xEA, 0xAF, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0xEF, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE9, 0xB9, /* 0xC4-0xC7 */
+ 0xF1, 0xD8, 0x00, 0x00, 0x00, 0x00, 0xD8, 0xD8, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0xF2, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE6, 0xB4, /* 0xDC-0xDF */
+ 0xDC, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0xF3, 0xF1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xE3, 0xD0, 0x00, 0x00, 0x00, 0x00, 0xF2, 0xFB, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xDB, 0xC6, 0xD0, 0xF1, 0x00, 0x00, /* 0xF8-0xFB */
+ 0xD0, 0xF2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_58[512] = {
+ 0xCF, 0xDC, 0x00, 0x00, 0xD3, 0xD1, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0xCC, 0xB1, 0xF7, 0xD8, 0x00, 0x00, /* 0x04-0x07 */
+ 0xCB, 0xA8, 0xEB, 0xBC, 0xE4, 0xBE, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0xF4, 0xDC, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0xDC, 0xC2, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0xF0, 0xA7, 0x00, 0x00, 0x00, 0x00, 0xE6, 0xC0, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xCA, 0xED, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0xEB, /* 0x2C-0x2F */
+ 0xE5, 0xE8, 0xDC, 0xC3, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0xED, 0xDE, 0xD3, 0xF2, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0xCC, 0xF7, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0xCE, 0xD4, 0xE7, 0xAB, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCB, 0xC3, /* 0x4C-0x4F */
+ 0x00, 0x00, 0xE1, 0xB1, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0xF7, 0xB2, 0x00, 0x00, 0x00, 0x00, 0xD3, 0xF3, /* 0x54-0x57 */
+ 0xD3, 0xD2, 0x00, 0x00, 0xF5, 0xC0, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0xDF, 0xDD, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0xEE, 0xF3, 0xE7, 0xF1, 0x00, 0x00, /* 0x60-0x63 */
+ 0xFD, 0xB4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0xF2, 0xC8, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0xF3, 0xD2, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0xEE, 0xF4, 0x00, 0x00, 0xE2, 0xD3, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0xD1, /* 0x80-0x83 */
+ 0x00, 0x00, 0xDF, 0xEA, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0xE9, 0xBA, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD9, 0xD7, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0xF5, 0xCD, 0x00, 0x00, 0xF1, 0xF2, 0xFA, 0xC7, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xD9, 0xF8, 0xD4, 0xC2, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0xF6, 0xE5, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDD, 0xC5, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0xE7, 0xF2, 0xED, 0xDF, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0xCA, 0xCB, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0xDB, 0xFA, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0xE8, 0xB5, 0x00, 0x00, 0xD3, 0xA6, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0xFD, 0xB5, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0xF9, 0xC9, 0x00, 0x00, 0xE4, 0xE2, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0xFB, 0xBD, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xD7, 0xA4, 0xCE, 0xC5, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0xCE, 0xD5, 0xD6, 0xE6, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0xE5, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDE, 0xCD, /* 0xE8-0xEB */
+ 0xEC, 0xF3, 0x00, 0x00, 0x00, 0x00, 0xED, 0xE0, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xEC, 0xEC, 0xFB, 0xBE, 0xDF, 0xEB, /* 0xF8-0xFB */
+ 0x00, 0x00, 0xE1, 0xF8, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_59[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xBE, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0xD0, 0xF3, 0xE0, 0xAA, 0xE8, 0xE2, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0xE2, 0xD4, 0xD2, 0xFD, 0x00, 0x00, /* 0x18-0x1B */
+ 0xE5, 0xA8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0xD9, 0xD3, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD3, 0xDE, /* 0x24-0x27 */
+ 0x00, 0x00, 0xF4, 0xB8, 0xF7, 0xBC, 0xDC, 0xFD, /* 0x28-0x2B */
+ 0x00, 0x00, 0xE8, 0xEC, 0xE4, 0xE7, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0xE3, 0xF7, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEC, 0xA8, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0xFA, 0xF1, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0xE5, 0xF2, 0x00, 0x00, 0x00, 0x00, 0xD0, 0xF4, /* 0x44-0x47 */
+ 0xD2, 0xAF, 0xDC, 0xE5, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0xD0, 0xA5, 0xF1, 0xB4, /* 0x4C-0x4F */
+ 0xFC, 0xB1, 0xCC, 0xF8, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0xDD, 0xC6, 0xFA, 0xD1, 0x00, 0x00, 0xF7, 0xDF, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0xFA, 0xA8, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xEE, 0xF5, 0x00, 0x00, 0xDE, 0xCE, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE7, 0xF3, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0xF7, 0xAC, 0xEB, 0xC4, /* 0x68-0x6B */
+ 0xED, 0xE1, 0xE0, 0xAB, 0xDD, 0xC7, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD2, 0xB3, /* 0x70-0x73 */
+ 0xD2, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0xCA, 0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0xFB, 0xBF, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0xFD, 0xDD, 0xE5, /* 0x80-0x83 */
+ 0xD8, 0xCD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0xEC, 0xF4, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD0, 0xF5, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0xE8, 0xED, 0xD0, 0xD2, /* 0x94-0x97 */
+ 0x00, 0x00, 0xD9, 0xD8, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0xF6, 0xE6, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xDB, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0xF7, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xD8, 0xD9, 0x00, 0x00, 0xF4, 0xA3, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0xF4, 0xDD, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEF, 0xD1, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0xD9, 0xB5, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xED, 0xAB, 0x00, 0x00, 0xE3, 0xB7, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0xEE, 0xBB, 0xCD, 0xB4, 0x00, 0x00, 0xE0, 0xF3, /* 0xD0-0xD3 */
+ 0xEA, 0xCD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0xEC, 0xF5, 0xE8, 0xEE, 0x00, 0x00, /* 0xD8-0xDB */
+ 0xCB, 0xA9, 0xF1, 0xAF, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0xCA, 0xCD, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xEC, 0xA9, 0x00, 0x00, 0xF2, 0xEB, 0x00, 0x00, /* 0xE8-0xEB */
+ 0xFD, 0xEF, 0x00, 0x00, 0xF9, 0xF3, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0xE6, 0xC1, 0x00, 0x00, 0x00, 0x00, 0xEC, 0xD8, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xED, 0xAC, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_5A[512] = {
+ 0x00, 0x00, 0xEA, 0xCE, 0x00, 0x00, 0xE8, 0xDF, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0xDE, 0xCF, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0xD2, 0xA6, 0x00, 0x00, 0x00, 0x00, 0xE7, 0xF4, /* 0x18-0x1B */
+ 0xD1, 0xD6, 0x00, 0x00, 0x00, 0x00, 0xE6, 0xC2, /* 0x1C-0x1F */
+ 0xE3, 0xE3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0xE4, 0xB0, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0xD8, 0xB4, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0xF6, 0xA5, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0xF3, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0xD7, 0xA5, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0xF7, 0xE8, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0xE8, 0xC6, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0xFB, 0xE6, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0xDD, 0xE6, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0xDC, 0xFE, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0xD8, 0xDA, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0xDA, 0xAC, 0xEA, 0xB0, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0xE3, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0xCA, 0xAA, 0xE1, 0xF9, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xEA, 0xB1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xF2, 0xEC, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0xFA, 0xEE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xEE, 0xD5, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0xF9, 0xF4, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0xD2, 0xEC, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+};
+
+static unsigned char u2c_5B[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0xFB, 0xFB, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0xFD, 0xF0, 0x00, 0x00, 0xE0, 0xBD, /* 0x08-0x0B */
+ 0xCE, 0xE3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0xF8, 0xC6, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xDE, 0xAE, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0xDF, 0xC5, 0x00, 0x00, 0x00, 0x00, 0xE5, 0xBE, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xED, 0xAD, 0xFA, 0xEA, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0xCD, 0xEE, 0xED, 0xA6, 0x00, 0x00, 0xED, 0xAE, /* 0x54-0x57 */
+ 0xF0, 0xED, 0x00, 0x00, 0xDD, 0xA1, 0x00, 0x00, /* 0x58-0x5B */
+ 0xED, 0xAF, 0xFC, 0xF8, 0x00, 0x00, 0xD8, 0xEB, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0xF9, /* 0x60-0x63 */
+ 0xCD, 0xB5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0xFA, 0xA9, 0x00, 0x00, 0xE1, 0xDD, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xE2, 0xD5, 0xED, 0xCF, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0xDD, 0xA2, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0xF9, 0xCA, 0x00, 0x00, 0xEA, 0xE8, 0x00, 0x00, /* 0x78-0x7B */
+ 0xE5, 0xED, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0xD3, 0xEB, 0x00, 0x00, 0xE9, 0xD4, /* 0x84-0x87 */
+ 0xE1, 0xFA, 0xE4, 0xCC, 0x00, 0x00, 0xE1, 0xE4, /* 0x88-0x8B */
+ 0xE8, 0xC7, 0x00, 0x00, 0x00, 0x00, 0xCE, 0xDB, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0xD5, /* 0x90-0x93 */
+ 0x00, 0x00, 0xF7, 0xB5, 0xFC, 0xF3, 0xF0, 0xF3, /* 0x94-0x97 */
+ 0xCE, 0xAF, 0xF1, 0xB5, 0xEF, 0xD2, 0xE8, 0xC8, /* 0x98-0x9B */
+ 0xEB, 0xF1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0xCB, 0xD4, 0xE0, 0xBE, /* 0xA0-0xA3 */
+ 0xE3, 0xF8, 0xEA, 0xE9, 0xFC, 0xB2, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0xE0, 0xF4, 0x00, 0x00, 0xCF, 0xE0, 0x00, 0x00, /* 0xAC-0xAF */
+ 0xEE, 0xA5, 0x00, 0x00, 0x00, 0x00, 0xFA, 0xAA, /* 0xB0-0xB3 */
+ 0xE6, 0xC3, 0xE1, 0xB2, 0xCA, 0xAB, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0xE3, 0xE4, 0xE9, 0xBB, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2, 0xD6, /* 0xBC-0xBF */
+ 0xF3, 0xF2, 0x00, 0x00, 0xEE, 0xD6, 0xEA, 0xB2, /* 0xC0-0xC3 */
+ 0xD0, 0xF6, 0xEC, 0xD9, 0xDA, 0xCB, 0xCF, 0xA8, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0xDD, 0xA3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0xD8, 0xDB, 0x00, 0x00, 0xF9, 0xCE, 0xE9, 0xD5, /* 0xD0-0xD3 */
+ 0xE3, 0xD1, 0x00, 0x00, 0x00, 0x00, 0xD2, 0xBC, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0xD8, 0xAC, 0xF3, 0xCC, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xCD, 0xFB, 0xF6, 0xD6, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0xE7, 0xF5, 0xE8, 0xEF, 0xE3, 0xF9, 0xD2, 0xBB, /* 0xE4-0xE7 */
+ 0xF3, 0xF3, 0xE3, 0xFB, 0x00, 0x00, 0xDE, 0xD0, /* 0xE8-0xEB */
+ 0xCE, 0xB0, 0x00, 0x00, 0xD6, 0xF7, 0xF1, 0xD9, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0xF5, 0xC1, 0xDC, 0xC4, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0xF5, 0xBB, 0x00, 0x00, 0xDE, 0xD1, 0x00, 0x00, /* 0xF8-0xFB */
+};
+
+static unsigned char u2c_5C[512] = {
+ 0x00, 0x00, 0xDC, 0xE6, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0xDE, 0xD2, 0x00, 0x00, 0x00, 0x00, 0xED, 0xE2, /* 0x04-0x07 */
+ 0xEE, 0xF6, 0xEA, 0xCF, 0xF0, 0xEE, 0xE3, 0xFC, /* 0x08-0x0B */
+ 0x00, 0x00, 0xD3, 0xDF, 0xD3, 0xF4, 0xE1, 0xB3, /* 0x0C-0x0F */
+ 0x00, 0x00, 0xE1, 0xB4, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0xF4, 0xD3, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0xDF, 0xC6, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0xE9, 0xD6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0xDB, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0xF6, 0xA6, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0xE3, 0xB9, 0xEB, 0xC5, 0xF4, 0xA9, 0xCD, 0xB6, /* 0x38-0x3B */
+ 0xD2, 0xF9, 0x00, 0x00, 0xDA, 0xAD, 0xD2, 0xE3, /* 0x3C-0x3F */
+ 0xCF, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0xCB, 0xDC, 0xCC, 0xFA, 0x00, 0x00, /* 0x44-0x47 */
+ 0xCF, 0xDD, 0x00, 0x00, 0x00, 0x00, 0xE8, 0xA9, /* 0x48-0x4B */
+ 0x00, 0x00, 0xE3, 0xBB, 0xE3, 0xBA, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0xE0, 0xDA, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0xEE, 0xF7, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0xB3, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xD3, 0xF5, 0x00, 0x00, 0xD7, 0xA6, 0x00, 0x00, /* 0x60-0x63 */
+ 0xF6, 0xB5, 0xD7, 0xDB, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0xE1, 0xD5, 0x00, 0x00, 0x00, 0x00, 0xD4, 0xEA, /* 0x6C-0x6F */
+ 0x00, 0x00, 0xDF, 0xA3, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0xFD, 0xDF, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0xD0, 0xF7, 0xED, 0xD4, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0xCB, 0xAA, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0xE4, 0xDB, 0x00, 0x00, 0xE1, 0xFB, /* 0xA8-0xAB */
+ 0xCB, 0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0xD3, 0xE0, 0x00, 0x00, 0xE4, 0xBF, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0xFB, 0xC0, 0x00, 0x00, 0xDA, 0xBE, /* 0xB4-0xB7 */
+ 0xE4, 0xCD, 0x00, 0x00, 0xD6, 0xB9, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0xEF, 0xC0, 0x00, 0x00, /* 0xBC-0xBF */
+ 0xE1, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0xF6, 0xB9, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0xDF, 0xC7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xE4, 0xB1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0xE7, /* 0xEC-0xEF */
+ 0xDC, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xFA, 0xD6, 0x00, 0x00, 0xD3, 0xF6, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF1, 0xDA, /* 0xF8-0xFB */
+ 0x00, 0x00, 0xFA, 0xF2, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_5D[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2, 0xFD, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0xD5, 0xCF, 0xD0, 0xF8, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0xCD, 0xDF, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0xF5, 0xCB, 0x00, 0x00, 0xE4, 0xF0, 0xCB, 0xAB, /* 0x14-0x17 */
+ 0x00, 0x00, 0xD7, 0xC4, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2, 0xFE, /* 0x24-0x27 */
+ 0x00, 0x00, 0xDD, 0xDA, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDA, 0xAE, /* 0x48-0x4B */
+ 0xCA, 0xEE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xD5, 0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0xE3, 0xA1, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0xE8, 0xE3, 0x00, 0x00, 0x00, 0x00, 0xF3, 0xAB, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCF, 0xA9, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD3, 0xF7, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0xD4, 0xF1, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0xCE, 0xE4, 0x00, 0x00, 0xE8, 0xF2, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0xF5, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0xE7, 0xAE, 0x00, 0x00, 0xD6, 0xBA, 0x00, 0x00, /* 0xB8-0xBB */
+ 0xDF, 0xEC, 0xE4, 0xC0, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0xE8, 0xE4, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0xD8, 0xB5, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0xE4, 0xDC, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0xF4, 0xB9, 0xF1, 0xB6, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xE2, 0xDE, 0xE1, 0xB5, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0xCD, 0xEF, 0xF1, 0xA7, 0xCE, 0xE5, /* 0xE4-0xE7 */
+ 0xCB, 0xDD, 0x00, 0x00, 0x00, 0x00, 0xD9, 0xE3, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0xF3, 0xAC, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0xD0, 0xF9, 0xEC, 0xAB, 0xDE, 0xD3, /* 0xF0-0xF3 */
+ 0xF7, 0xE9, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xF5, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0xE1, 0xDE, 0xCB, 0xEE, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_5E[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0xE3, 0xBC, 0xF8, 0xD6, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0xDB, 0xEE, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0xFD, 0xF1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0xF7, 0xB6, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0xF4, 0xDE, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0xF2, 0xED, 0x00, 0x00, 0xDB, 0xD9, /* 0x18-0x1B */
+ 0x00, 0x00, 0xF0, 0xA8, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0xE1, 0xFD, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDE, 0xD4, /* 0x28-0x2B */
+ 0x00, 0x00, 0xE0, 0xAC, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xED, 0xE3, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0xD3, 0xE1, 0x00, 0x00, /* 0x34-0x37 */
+ 0xDF, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0xD9, 0xB6, 0x00, 0x00, 0xFD, 0xAC, /* 0x3C-0x3F */
+ 0xEF, 0xD3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0xE4, 0xC1, 0xF8, 0xEB, 0x00, 0x00, 0xDB, 0xAC, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0xFC, 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0xD8, 0xAD, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF6, 0xBA, /* 0x5C-0x5F */
+ 0x00, 0x00, 0xDB, 0xDF, 0xD3, 0xD3, 0xF8, 0xC7, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0xCA, 0xCE, 0xF8, 0xC1, /* 0x70-0x73 */
+ 0xD2, 0xB4, 0x00, 0x00, 0x00, 0x00, 0xDC, 0xB4, /* 0x74-0x77 */
+ 0xFA, 0xB9, 0xCA, 0xCF, 0x00, 0x00, 0xFC, 0xB3, /* 0x78-0x7B */
+ 0xEA, 0xEA, 0xEA, 0xEB, 0xD0, 0xFA, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0xED, 0xE4, 0x00, 0x00, 0x00, 0x00, 0xDD, 0xE7, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0xDF, 0xC9, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDF, 0xED, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0xEE, 0xBC, 0x00, 0x00, 0xEF, 0xC1, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0xCC, 0xD2, 0x00, 0x00, /* 0x98-0x9B */
+ 0xDD, 0xA4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0xDF, 0xCA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0xD3, 0xF8, 0xF1, 0xA8, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCD, 0xB7, /* 0xA8-0xAB */
+ 0x00, 0x00, 0xEF, 0xD4, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0xE4, 0xDD, 0xDF, 0xEE, 0xCB, 0xAC, /* 0xB4-0xB7 */
+ 0xE9, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0xEA, 0xEC, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0xDF, 0xCB, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0xF9, 0xBF, 0xD6, 0xAF, 0xD5, 0xC6, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0xCF, 0xAA, 0x00, 0x00, 0x00, 0x00, 0xCE, 0xA9, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0xD6, 0xF8, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0xF1, 0xB7, 0xEE, 0xF8, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD9, 0xD9, /* 0xDC-0xDF */
+ 0xF3, 0xDF, 0x00, 0x00, 0xF8, 0xC8, 0xCE, 0xC6, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0xD5, 0xE6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF4, 0xE6, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0xE6, 0xC5, 0xEF, 0xD5, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0xCB, 0xEF, 0xFC, 0xDF, /* 0xF8-0xFB */
+};
+
+static unsigned char u2c_5F[512] = {
+ 0x00, 0x00, 0xDC, 0xA7, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0xD6, 0xE7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0xF8, 0xC9, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE3, 0xD2, /* 0x0C-0x0F */
+ 0x00, 0x00, 0xE3, 0xBD, 0x00, 0x00, 0xCF, 0xE1, /* 0x10-0x13 */
+ 0xF0, 0xC0, 0xEC, 0xDA, 0x00, 0x00, 0xDD, 0xD7, /* 0x14-0x17 */
+ 0xFB, 0xF0, 0x00, 0x00, 0x00, 0x00, 0xEC, 0xAC, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xA9, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0xFA, 0xD7, 0xFB, 0xC1, /* 0x24-0x27 */
+ 0x00, 0x00, 0xD2, 0xC0, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0xE5, 0xB0, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0xED, 0xE5, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0xCB, 0xAD, 0x00, 0x00, /* 0x38-0x3B */
+ 0xF9, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0xF7, 0xA5, 0x00, 0x00, 0xCB, 0xAE, 0x00, 0x00, /* 0x48-0x4B */
+ 0xDA, 0xAF, 0x00, 0x00, 0xD8, 0xB6, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0xD3, 0xA7, 0xFB, 0xB2, /* 0x54-0x57 */
+ 0x00, 0x00, 0xFD, 0xC4, 0x00, 0x00, 0xEC, 0xAD, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0xFB, 0xA1, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0xE9, 0xE9, 0xEE, /* 0x64-0x67 */
+ 0x00, 0x00, 0xF3, 0xF4, 0xF8, 0xF3, 0xF0, 0xC1, /* 0x68-0x6B */
+ 0xDE, 0xAF, 0xF8, 0xB0, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xF3, 0xE0, 0xE7, 0xAF, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDB, 0xAD, /* 0x74-0x77 */
+ 0x00, 0x00, 0xE6, 0xB5, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0xF9, 0xA8, 0x00, 0x00, 0x00, 0x00, 0xDD, 0xD8, /* 0x7C-0x7F */
+
+ 0xE8, 0xD9, 0xEF, 0xD6, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0xD3, 0xE2, 0x00, 0x00, 0xE2, 0xDF, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0xFC, 0xE0, 0xD7, 0xC8, /* 0x88-0x8B */
+ 0xFD, 0xAD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0xDF, 0xEF, 0xCC, 0xD3, 0xD3, 0xF9, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD4, 0xF0, /* 0x94-0x97 */
+ 0xDB, 0xC7, 0xDE, 0xD5, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF4, 0x00, 0x00, /* 0x9C-0x9F */
+ 0xD5, 0xD0, 0xE5, 0xD9, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xFC, 0xC7, 0xDC, 0xD6, 0xE2, 0xE0, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0xDA, 0xB0, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0xF3, 0xA3, 0x00, 0x00, 0xD3, 0xEC, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xF4, 0xCB, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0xFD, 0xC5, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE3, 0xFD, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0xF9, 0xB1, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0xD0, 0xFB, 0xEC, 0xDB, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0xF5, 0xBC, 0xF2, 0xA4, /* 0xD4-0xD7 */
+ 0xD8, 0xCE, 0xD8, 0xCF, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0xF5, 0xF7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF6, 0xE1, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0xD2, 0xB7, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0xFB, 0xEC, 0x00, 0x00, 0xDD, 0xC8, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_60[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0xE8, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0xD2, 0xC1, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0xF8, 0xD7, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0xD6, 0xBB, 0xDE, 0xD6, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0xF7, 0xBD, 0xEC, 0xAE, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0xD0, 0xE1, 0x00, 0x00, 0xE0, 0xF5, /* 0x24-0x27 */
+ 0xEA, 0xB3, 0x00, 0x00, 0xCE, 0xD6, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0xA5, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0xEC, 0xF6, 0xE2, 0xE1, 0xE3, 0xBE, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0xFC, 0xC8, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xCD, 0xF0, 0x00, 0x00, 0xF9, 0xF6, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0xDF, 0xF0, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0xE5, 0xBF, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0xCE, 0xBF, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0xFC, 0xE1, 0xED, 0xB0, /* 0x60-0x63 */
+ 0xFD, 0xD1, 0xF6, 0xBB, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0xF9, 0xCF, 0xEB, 0xDA, 0xCA, 0xC1, 0x00, 0x00, /* 0x68-0x6B */
+ 0xD2, 0xB8, 0xCD, 0xF1, 0x00, 0x00, 0xE3, 0xD3, /* 0x6C-0x6F */
+ 0xFD, 0xE6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0xE6, 0xED, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0xE3, 0xFA, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0xF0, 0xAA, 0xF9, 0xD0, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0xFC, 0xE2, 0x00, 0x00, 0xF8, 0xA7, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0xE5, 0xEE, 0xF9, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE7, 0xF6, /* 0x9C-0x9F */
+ 0xEA, 0xED, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xB4, /* 0xA0-0xA3 */
+ 0xF5, 0xC2, 0x00, 0x00, 0x00, 0x00, 0xD7, 0xDC, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0xF0, 0xF5, 0x00, 0x00, 0xDD, 0xE8, 0xD3, 0xED, /* 0xB0-0xB3 */
+ 0xF5, 0xFC, 0x00, 0x00, 0xDA, 0xBF, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0xCC, 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0xD3, 0xFA, 0xF4, 0xA4, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0xEF, 0xD7, 0x00, 0x00, 0xD4, 0xC3, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0xFB, 0xE3, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0xFB, 0xED, 0x00, 0x00, /* 0xD8-0xDB */
+ 0xE0, 0xAD, 0x00, 0x00, 0x00, 0x00, 0xEA, 0xEE, /* 0xDC-0xDF */
+ 0xFB, 0xB3, 0xE4, 0xC2, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0xF6, 0xE7, 0xD2, 0xDD, 0x00, 0x00, 0xDF, 0xCC, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0xFC, 0xC9, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xE5, 0xA9, 0xE0, 0xF6, 0xF6, 0xB3, /* 0xF8-0xFB */
+};
+
+static unsigned char u2c_61[512] = {
+ 0x00, 0x00, 0xE1, 0xFE, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0xCB, 0xF0, 0x00, 0x00, /* 0x04-0x07 */
+ 0xEA, 0xEF, 0xEA, 0xF0, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0xDA, 0xC0, 0xF8, 0xB4, 0xEB, 0xF2, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0xE4, 0xC3, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0xD7, 0xE4, 0xF1, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCA, 0xEF, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCE, 0xD7, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xFC, 0xCA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0xF3, 0xE1, 0x00, 0x00, 0x00, 0x00, 0xCB, 0xC4, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0xE3, 0xE5, 0x00, 0x00, 0xCB, 0xC5, 0xEA, 0xB4, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0xBD, 0x00, 0x00, /* 0x40-0x43 */
+ 0xD7, 0xC9, 0x00, 0x00, 0x00, 0x00, 0xEB, 0xDB, /* 0x44-0x47 */
+ 0xED, 0xB1, 0x00, 0x00, 0xCC, 0xC3, 0xF7, 0xBE, /* 0x48-0x4B */
+ 0xFC, 0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xF4, /* 0x50-0x53 */
+ 0x00, 0x00, 0xD9, 0xB7, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0xF3, 0xD3, 0xF3, 0xD4, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0xF7, 0xE4, 0x00, 0x00, 0xF7, 0xD1, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0xD8, 0xB7, 0xCE, 0xB1, /* 0x60-0x63 */
+ 0xCA, 0xC2, 0x00, 0x00, 0x00, 0x00, 0xFB, 0xB4, /* 0x64-0x67 */
+ 0xCB, 0xC6, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF6, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0xD5, 0xE7, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xEA, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0xCC, 0xD4, 0xCB, 0xAF, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0xF4, 0xAA, 0xE9, 0xAF, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0xF5, 0xC3, 0xE9, 0xD8, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0xDD, 0xE9, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0xF1, 0xF3, 0x00, 0x00, /* 0x8C-0x8F */
+ 0xD5, 0xFB, 0xDE, 0xBB, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0xF4, 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0xFD, 0xF3, 0xFD, 0xF2, 0xF7, 0xA6, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0xDD, 0xC9, 0x00, 0x00, 0x00, 0x00, 0xD4, 0xD3, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0xCC, 0xA8, 0x00, 0x00, 0xDA, 0xC1, /* 0xA8-0xAB */
+ 0xCC, 0xD5, 0x00, 0x00, 0xD9, 0xE4, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0xFA, 0xCA, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0xE3, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0xD3, 0xBC, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0xCA, 0xF0, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD0, 0xC4, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCA, 0xD0, /* 0xC4-0xC7 */
+ 0xFA, 0xAB, 0xEB, 0xEB, 0xE7, 0xF8, 0xD9, 0xE5, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0xD1, 0xD7, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0xF3, 0xA4, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0xD4, 0xFB, 0xFC, 0xE3, /* 0xF4-0xF7 */
+ 0xFA, 0xD8, 0x00, 0x00, 0xF3, 0xD5, 0x00, 0x00, /* 0xF8-0xFB */
+ 0xCF, 0xAB, 0x00, 0x00, 0x00, 0x00, 0xEB, 0xF3, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_62[512] = {
+ 0xD5, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD3, 0xD4, /* 0x04-0x07 */
+ 0xCD, 0xFC, 0x00, 0x00, 0xD9, 0xE6, 0x00, 0x00, /* 0x08-0x0B */
+ 0xE2, 0xF9, 0xE2, 0xA1, 0xEB, 0xD4, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xE0, 0xF7, 0xE4, 0xB2, 0xCC, 0xFC, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0xFB, 0xE4, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0xF4, 0xAB, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD0, 0xBD, /* 0x1C-0x1F */
+ 0x00, 0x00, 0xCA, 0xF1, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xEF, 0xB8, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0xD7, 0xC0, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xEE, 0xFA, 0xFD, 0xF4, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0xD3, 0xE3, 0x00, 0x00, 0xFB, 0xC2, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0xD5, 0xE8, 0xDB, 0xAE, /* 0x3C-0x3F */
+ 0xE1, 0xB6, 0xF8, 0xB7, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xBF, /* 0x44-0x47 */
+ 0xFB, 0xC3, 0xDD, 0xEA, 0x00, 0x00, 0xE2, 0xA2, /* 0x48-0x4B */
+ 0x00, 0x00, 0xEE, 0xA6, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF6, 0xE8, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0xF6, 0xF5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0xDD, 0xCA, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0xD0, 0xE2, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0xDD, 0xA6, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0xDD, 0xEB, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0xE4, 0xF9, 0x00, 0x00, 0x00, 0x00, 0xE3, 0xAF, /* 0x7C-0x7F */
+
+ 0xD0, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0xF4, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0xCC, 0xBC, 0xF7, 0xEA, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xE5, 0xE4, 0xDF, 0xF1, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0xF7, 0xE1, 0x00, 0x00, 0xF9, 0xF7, /* 0x94-0x97 */
+ 0xEF, 0xB9, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xD8, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xA9, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0xF8, 0xD9, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0xEE, 0xBD, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xD8, 0xC6, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0xE4, 0xE3, 0xF5, 0xCE, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0xDD, 0xD9, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD9, 0xE7, /* 0xC4-0xC7 */
+ 0xD2, 0xB9, 0xD5, 0xC3, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0xDA, 0xE5, 0xDA, 0xD0, 0x00, 0x00, 0xD1, 0xD9, /* 0xCC-0xCF */
+ 0xCE, 0xD8, 0x00, 0x00, 0xCB, 0xDE, 0xF4, 0xAC, /* 0xD0-0xD3 */
+ 0xDA, 0xFB, 0x00, 0x00, 0xF6, 0xE9, 0xE8, 0xF3, /* 0xD4-0xD7 */
+ 0xCF, 0xAC, 0xF0, 0xF0, 0x00, 0x00, 0xF4, 0xFD, /* 0xD8-0xDB */
+ 0xDB, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0xCE, 0xC0, 0xE3, 0xD4, 0xD1, 0xCF, 0xF1, 0xF5, /* 0xEC-0xEF */
+ 0x00, 0x00, 0xCD, 0xF2, 0x00, 0x00, 0xCF, 0xEB, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCD, 0xB8, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0xE3, 0xA6, 0xD1, 0xDA, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_63[512] = {
+ 0x00, 0x00, 0xF2, 0xA5, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF2, 0xA6, /* 0x04-0x07 */
+ 0x00, 0x00, 0xE4, 0xCE, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0xD3, 0xFB, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF1, 0xA9, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF2, 0xC9, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0xEF, 0xD8, 0xE6, 0xC9, /* 0x38-0x3B */
+ 0x00, 0x00, 0xD8, 0xB8, 0xFA, 0xF3, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0xF3, 0xB5, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0xF8, 0xA4, 0x00, 0x00, 0x00, 0x00, 0xD1, 0xF3, /* 0x4C-0x4F */
+ 0xE6, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0xF8, 0xDA, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0xE9, /* 0x64-0x67 */
+ 0xDE, 0xD7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0xCB, 0xDF, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0xCF, 0xEC, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF4, 0xDF, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0xD1, 0xF4, 0xD2, 0xBA, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDF, 0xF2, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE1, 0xB7, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0xE2, 0xA3, 0xD3, 0xFC, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0xED, 0xE6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0xDB, 0xC9, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0xE4, 0xFA, 0x00, 0x00, /* 0x94-0x97 */
+ 0xCF, 0xDE, 0x00, 0x00, 0x00, 0x00, 0xCE, 0xD0, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0xD5, 0xD3, 0xF3, 0xF5, 0xF7, 0xAE, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0xEF, 0xC8, 0x00, 0x00, 0xCD, 0xF3, /* 0xA4-0xA7 */
+ 0xF5, 0xCF, 0xE5, 0xF3, 0xF0, 0xC2, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0xCA, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xEA, 0xF1, 0x00, 0x00, 0xD0, 0xA6, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD9, 0xDA, /* 0xCC-0xCF */
+ 0xF0, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0xEB, 0xE7, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0xC0, 0xFC, 0xB5, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xE4, 0xC4, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0xCC, 0xA9, 0xFD, 0xC6, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xEA, 0xB5, 0x00, 0x00, 0xE5, 0xAA, 0xDF, 0xBA, /* 0xF4-0xF7 */
+};
+
+static unsigned char u2c_64[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0xE1, 0xDF, 0x00, 0x00, 0xDA, 0xD1, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0xE1, 0xB8, 0x00, 0x00, 0xE8, 0xF4, 0xD3, 0xFD, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0xE2, 0xA4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0xF2, 0xCA, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0xDA, 0xE6, 0xF7, 0xB3, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0xFD, 0xCD, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0xF3, 0xB6, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0xEE, 0xD7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xF5, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0xD8, 0xA4, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF2, 0xA7, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0xD9, 0xB8, 0xD9, 0xB9, 0xEF, 0xC9, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0xD6, 0xCE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xF7, 0xCB, 0xDF, 0xAE, 0xE8, 0xF5, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0xD2, 0xB5, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0xD3, 0xD5, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0xF4, 0xCC, 0xDA, 0xFC, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD9, 0xE8, /* 0xA8-0xAB */
+ 0x00, 0x00, 0xF7, 0xEB, 0xF5, 0xC9, 0x00, 0x00, /* 0xAC-0xAF */
+ 0xF3, 0xBC, 0x00, 0x00, 0xDA, 0xD2, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD3, 0xB5, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0xE8, 0xB6, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xD6, 0xCF, 0xF4, 0xBA, 0x00, 0x00, 0xF7, 0xC9, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0xCC, 0xAA, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0xF0, 0xC3, 0xCC, 0xD6, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0xD0, 0xD3, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xD3, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xDB, 0xFB, 0x00, 0x00, 0xCB, 0xE0, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xD3, 0xE4, 0xF6, 0xF7, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0xD5, 0xBA, 0xF3, 0xCD, 0xCB, 0xE1, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0xEB, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0xF4, 0xAD, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xFC, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0xF7, 0xEC, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0xE8, 0xF6, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_65[512] = {
+ 0xDA, 0xE7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0xF7, 0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0xE5, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0xE0, 0xEE, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD5, 0xFD, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xCE, 0xE6, 0xFC, 0xAB, /* 0x28-0x2B */
+ 0xD5, 0xBB, 0x00, 0x00, 0x00, 0x00, 0xF2, 0xA8, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0xE2, 0xA5, 0xCD, 0xB9, /* 0x34-0x37 */
+ 0xEA, 0xF2, 0xCB, 0xC7, 0x00, 0x00, 0xCD, 0xF4, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0xDB, 0xAF, 0xEF, 0xD9, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0xCD, 0xBA, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0xFC, 0xF9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0xDF, 0xF3, 0xCE, 0xE7, 0xDA, 0xC2, /* 0x4C-0x4F */
+ 0x00, 0x00, 0xCF, 0xAD, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0xE7, 0xF9, 0xF8, 0xA8, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0xF3, 0xE2, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0xCA, 0xF2, 0xDF, 0xA4, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0xD4, 0xC4, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0xCC, 0xD7, 0xE5, 0xC2, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0xCD, 0xBB, 0x00, 0x00, /* 0x70-0x73 */
+ 0xEF, 0xDA, 0xEE, 0xD8, 0x00, 0x00, 0xDD, 0xA7, /* 0x74-0x77 */
+ 0xE2, 0xA6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0xC0, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0xD6, 0xB0, 0xF8, 0xCA, /* 0x80-0x83 */
+ 0x00, 0x00, 0xFC, 0xFA, 0x00, 0x00, 0xD9, 0xFE, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0xDE, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0xDD, 0xEC, 0xDA, 0xE8, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD4, 0xE0, /* 0x94-0x97 */
+ 0x00, 0x00, 0xD6, 0xF9, 0x00, 0x00, 0xCD, 0xD7, /* 0x98-0x9B */
+ 0xDE, 0xD8, 0x00, 0x00, 0x00, 0x00, 0xF2, 0xF8, /* 0x9C-0x9F */
+ 0x00, 0x00, 0xE4, 0xD6, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0xD0, 0xC5, 0xF4, 0xAE, 0x00, 0x00, 0xDD, 0xA8, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xED, 0xC5, /* 0xA8-0xAB */
+ 0xF3, 0xD6, 0x00, 0x00, 0x00, 0x00, 0xDE, 0xD9, /* 0xAC-0xAF */
+ 0xE3, 0xE6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD3, 0xA8, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xDB, 0xB0, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0xE5, 0xDA, 0xE3, 0xBF, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0xDB, 0xB1, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0xD5, 0xE9, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xC1, /* 0xC8-0xCB */
+ 0xEF, 0xDB, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xE9, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0xD7, 0xB2, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD0, 0xFD, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0xD9, 0xE9, 0x00, 0x00, 0x00, 0x00, 0xD0, 0xFE, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0xEC, 0xED, 0xD3, 0xA9, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xF2, 0xA9, 0xF0, 0xC4, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0xE2, 0xE2, 0xE9, 0xEF, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0xF9, 0xD1, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xE9, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0xE8, 0xDA, 0xDA, 0xC3, /* 0xF8-0xFB */
+ 0xDA, 0xC4, 0xD4, 0xC5, 0x00, 0x00, 0xE7, 0xFA, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_66[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0xCD, 0xE0, 0xE3, 0xB0, /* 0x04-0x07 */
+ 0x00, 0x00, 0xDB, 0xB2, 0xFB, 0xC4, 0x00, 0x00, /* 0x08-0x0B */
+ 0xF3, 0xE3, 0x00, 0x00, 0xD9, 0xA5, 0xFB, 0xE7, /* 0x0C-0x0F */
+ 0xDD, 0xCB, 0xD0, 0xD4, 0x00, 0x00, 0xE6, 0xB6, /* 0x10-0x13 */
+ 0xE0, 0xAE, 0xFD, 0xDA, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0xDC, 0xB5, 0xE0, 0xF8, /* 0x1C-0x1F */
+ 0xE7, 0xB1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0xF5, 0xF0, 0x00, 0x00, 0xD8, 0xDC, /* 0x24-0x27 */
+ 0xED, 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0xE1, 0xB9, 0x00, 0x00, 0xE3, 0xC0, /* 0x2C-0x2F */
+ 0xF9, 0xC0, 0xE9, 0xF0, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0xD9, 0xDB, 0x00, 0x00, 0xF3, 0xE4, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0xDC, 0xB6, 0xE4, 0xE9, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0xF0, 0xC5, 0xE3, 0xC1, 0xFC, 0xCC, /* 0x40-0x43 */
+ 0xFC, 0xCD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0xF2, 0xCB, 0x00, 0x00, 0xF2, 0xCC, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0xCF, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0xF1, 0xDB, 0x00, 0x00, 0xFA, 0xD9, /* 0x58-0x5B */
+ 0x00, 0x00, 0xF1, 0xB8, 0xFD, 0xF5, 0xE0, 0xF9, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0xE7, 0xFB, 0xFC, 0xB7, 0xFC, 0xE4, 0xFB, 0xC5, /* 0x64-0x67 */
+ 0xE3, 0xE7, 0xD8, 0xB9, 0x00, 0x00, 0xF6, 0xF8, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0xDC, 0xC5, 0xCC, 0xD8, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xAF, /* 0x70-0x73 */
+ 0xF4, 0xE7, 0x00, 0x00, 0xEF, 0xDC, 0xCF, 0xFC, /* 0x74-0x77 */
+ 0xEF, 0xDD, 0x00, 0x00, 0xF2, 0xAA, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0xFD, 0xBE, 0x00, 0x00, 0x00, 0x00, 0xCA, 0xAC, /* 0x84-0x87 */
+ 0xFD, 0xBB, 0xFD, 0xC7, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0xE7, 0xB2, 0x00, 0x00, /* 0x8C-0x8F */
+ 0xEA, 0xD1, 0xDF, 0xF4, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0xD1, 0xEC, 0xE4, 0xDE, /* 0x94-0x97 */
+ 0xE5, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0xD9, 0xA6, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0xCD, 0xBC, 0x00, 0x00, 0xF3, 0xE5, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xED, 0xD5, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0xD9, 0xBA, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0xED, 0xE7, 0xFB, 0xB5, /* 0xB0-0xB3 */
+ 0xF8, 0xEC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xE0, 0xE7, 0x00, 0x00, 0xCC, 0xD9, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0xD4, 0xC6, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xE7, 0xA5, 0x00, 0x00, 0xD5, 0xF5, 0xD3, 0xBE, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xFC, 0xFB, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0xE4, 0xF2, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0xDF, 0xF5, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0xE8, 0xF8, 0xF8, 0xED, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0xCE, 0xC7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0xFD, 0xF6, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0xE8, 0xD8, 0x00, 0x00, 0xCD, 0xD8, 0xE7, 0xD6, /* 0xF0-0xF3 */
+ 0xCC, 0xDA, 0x00, 0x00, 0x00, 0x00, 0xCA, 0xE3, /* 0xF4-0xF7 */
+ 0xDF, 0xF6, 0xF0, 0xC7, 0xF0, 0xC6, 0x00, 0x00, /* 0xF8-0xFB */
+ 0xD8, 0xBA, 0x00, 0x00, 0xF1, 0xF4, 0xF4, 0xF0, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_67[512] = {
+ 0xF5, 0xCC, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xE5, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0xEA, 0xC5, 0xEA, 0xF3, 0x00, 0x00, 0xDD, 0xDB, /* 0x08-0x0B */
+ 0x00, 0x00, 0xDC, 0xD7, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0xDE, 0xFD, 0xF2, 0xF9, 0x00, 0x00, 0xD5, 0xC7, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD8, 0xD0, /* 0x18-0x1B */
+ 0x00, 0x00, 0xF0, 0xC8, 0xD1, 0xA1, 0xD1, 0xA2, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0xD9, 0xD4, 0xD6, 0xE8, /* 0x24-0x27 */
+ 0xD9, 0xCA, 0x00, 0x00, 0xDA, 0xB1, 0xD8, 0xC7, /* 0x28-0x2B */
+ 0xDC, 0xE2, 0xF3, 0xCE, 0xF5, 0xF4, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0xF1, 0xB9, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0xDA, 0xD3, 0x00, 0x00, 0xF6, 0xEA, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0xCF, 0xF5, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0xFD, 0xAE, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0xCA, 0xD2, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0xDF, 0xB4, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0xD7, 0xDD, 0xFA, 0xBA, /* 0x4C-0x4F */
+ 0xEE, 0xA7, 0xF5, 0xBD, 0x00, 0x00, 0xF8, 0xF5, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0xED, 0xE8, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0xD4, 0xE1, 0x00, 0x00, 0xD1, 0xA3, 0xE1, 0xD6, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0xF9, 0xF8, 0x00, 0x00, 0xDB, 0xCA, /* 0x6C-0x6F */
+ 0xCB, 0xF9, 0xD4, 0xD4, 0x00, 0x00, 0xD9, 0xDC, /* 0x70-0x73 */
+ 0x00, 0x00, 0xEE, 0xBE, 0x00, 0x00, 0xF7, 0xED, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD2, 0xEE, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0xE6, 0xF7, 0xF9, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDD, 0xED, /* 0x84-0x87 */
+ 0x00, 0x00, 0xE8, 0xDB, 0x00, 0x00, 0xDB, 0xB3, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD1, 0xF7, /* 0x8C-0x8F */
+ 0xE0, 0xB0, 0x00, 0x00, 0x00, 0x00, 0xD4, 0xE2, /* 0x90-0x93 */
+ 0x00, 0x00, 0xF6, 0xD7, 0x00, 0x00, 0xD7, 0xF9, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0xD8, 0xDD, 0x00, 0x00, /* 0x98-0x9B */
+ 0xCD, 0xFD, 0xF2, 0xAB, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCD, 0xBD, /* 0xAC-0xAF */
+ 0xF8, 0xC2, 0x00, 0x00, 0x00, 0x00, 0xF2, 0xAC, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0xCA, 0xAD, 0xCA, 0xAE, /* 0xB4-0xB7 */
+ 0xCF, 0xAE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0xE3, 0xC2, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xDC, 0xB7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDB, 0xDA, /* 0xCC-0xCF */
+ 0xD9, 0xBB, 0xCA, 0xF3, 0xF6, 0xD3, 0xE6, 0xF8, /* 0xD0-0xD3 */
+ 0xEA, 0xF5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0xEA, 0xF6, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0xF6, 0xF9, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0xCF, 0xAF, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0xCA, 0xD3, 0x00, 0x00, 0x00, 0x00, 0xCA, 0xAF, /* 0xEC-0xEF */
+ 0xD2, 0xB0, 0xF1, 0xBA, 0x00, 0x00, 0xD7, 0xB3, /* 0xF0-0xF3 */
+ 0xE3, 0xC3, 0xF3, 0xFD, 0xDE, 0xDA, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDE, 0xDB, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0xEF, 0xDE, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_68[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0xE2, 0xE3, 0xEE, 0xFB, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0xDF, 0xF7, 0xD7, 0xCA, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0xCE, 0xE8, 0xDB, 0xDB, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xF1, 0xBB, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE9, 0xF1, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0xFA, 0xB7, 0xD0, 0xC6, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0xCC, 0xAB, 0xEE, 0xA8, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0xCB, 0xFA, 0xF9, 0xF9, 0xCC, 0xFD, 0xD3, 0xFE, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0xE4, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0xF2, 0xEE, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xD4, 0xD5, 0xDF, 0xCD, 0x00, 0x00, 0xFC, 0xB8, /* 0x50-0x53 */
+ 0xD1, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0xF2, 0xCD, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0xF7, 0xD2, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCA, 0xD4, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0xD5, 0xD9, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0xD8, 0xDE, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCD, 0xD9, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEE, 0xA9, /* 0x90-0x93 */
+ 0xF6, 0xBC, 0x00, 0x00, 0x00, 0x00, 0xCC, 0xDB, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0xF0, 0xC9, 0x00, 0x00, 0xFC, 0xFC, /* 0x9C-0x9F */
+ 0x00, 0x00, 0xE8, 0xC9, 0xF4, 0xFE, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE7, 0xFC, /* 0xA4-0xA7 */
+ 0xD7, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0xDE, 0xDC, 0x00, 0x00, 0xF0, 0xAC, /* 0xAC-0xAF */
+ 0xCC, 0xFE, 0xCD, 0xE1, 0x00, 0x00, 0xE1, 0xBA, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0xDB, 0xEF, 0xDA, 0xB2, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xD1, 0xA5, 0xDC, 0xB8, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xD8, 0xF6, 0x00, 0x00, 0xD1, 0xA4, /* 0xC8-0xCB */
+ 0x00, 0x00, 0xCD, 0xE2, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0xDC, 0xEA, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0xF0, 0xF7, 0x00, 0x00, 0xF0, 0xCA, /* 0xD4-0xD7 */
+ 0xD0, 0xBE, 0x00, 0x00, 0xDD, 0xDC, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD4, 0xD6, /* 0xDC-0xDF */
+ 0xD3, 0xD6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xED, 0xD0, /* 0xE4-0xE7 */
+ 0xCD, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0xDF, 0xB5, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0xDF, 0xF8, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xD4, 0xA1, 0xCE, 0xB2, 0x00, 0x00, /* 0xF8-0xFB */
+};
+
+static unsigned char u2c_69[512] = {
+ 0xE8, 0xCA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0xEB, 0xF5, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0xE3, 0xD5, 0xF5, 0xD0, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0xF5, 0xA1, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD9, 0xA7, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xE5, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0xE6, 0xCB, 0x00, 0x00, 0xF5, 0xF1, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0xC5, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xA3, /* 0x50-0x53 */
+ 0xE0, 0xDB, 0xF6, 0xEB, 0x00, 0x00, 0xCB, 0xF1, /* 0x54-0x57 */
+ 0x00, 0x00, 0xD9, 0xEA, 0xF5, 0xA2, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0xD7, 0xD1, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xD1, 0xF8, 0xEA, 0xF8, 0xEA, 0xF9, 0xDA, 0xB3, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0xEF, 0xDF, 0x00, 0x00, 0x00, 0x00, 0xF1, 0xEF, /* 0x68-0x6B */
+ 0x00, 0x00, 0xE5, 0xF6, 0xEE, 0xBF, 0xE2, 0xE4, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0xD0, 0xBF, 0x00, 0x00, 0xFA, 0xAC, /* 0x74-0x77 */
+ 0xF5, 0xD1, 0xE7, 0xB3, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0xE9, 0xBE, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF2, 0xCE, /* 0x98-0x9B */
+ 0xDB, 0xB4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0xFC, 0xCE, 0x00, 0x00, 0xDD, 0xEE, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0xE7, 0xB4, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0xD7, 0xB4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF7, 0xB4, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0xCD, 0xBE, 0x00, 0x00, 0xDA, 0xE9, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCF, 0xB0, /* 0xC8-0xCB */
+ 0xF7, 0xD9, 0xF3, 0xE6, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0xCE, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xCE, 0xAA, 0x00, 0x00, 0xCB, 0xC8, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD0, 0xA7, /* 0xF8-0xFB */
+ 0x00, 0x00, 0xF0, 0xCB, 0x00, 0x00, 0xD0, 0xC7, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_6A[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0xE4, 0xC5, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0xDB, 0xE0, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0xD5, 0xDA, 0x00, 0x00, 0xD7, 0xA7, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEE, 0xC0, /* 0x14-0x17 */
+ 0x00, 0x00, 0xF8, 0xF6, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0xF5, 0xD2, 0xED, 0xE9, /* 0x1C-0x1F */
+ 0x00, 0x00, 0xD9, 0xBC, 0x00, 0x00, 0xE5, 0xC6, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0xF5, 0xA3, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0xDA, 0xD4, 0xE2, 0xA7, 0xFB, 0xFC, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0xF1, 0xDC, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0xCA, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0xE8, 0xFA, 0x00, 0x00, 0x00, 0x00, 0xCE, 0xE9, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0xF8, 0xE2, 0xE5, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0xD0, 0xB9, 0xD4, 0xF2, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD1, 0xA6, /* 0x5C-0x5F */
+ 0x00, 0x00, 0xDF, 0xCE, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xF4, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0xD3, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0xCC, 0xAC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0xEF, 0xE0, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0xE5, 0xE5, 0xD0, 0xD5, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDB, 0xFC, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0xFC, 0xE6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0xCB, 0xFE, 0xED, 0xEA, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDE, 0xB1, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xE3, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0xD4, 0xA2, 0xCF, 0xF6, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD6, 0xD0, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0xD5, 0xEA, 0xF1, 0xEE, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0xFA, 0xCB, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0xA1, /* 0xF8-0xFB */
+};
+
+static unsigned char u2c_6B[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0xD5, 0xB1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0xCF, 0xED, 0x00, 0x00, /* 0x08-0x0B */
+ 0xED, 0xEB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0xD5, 0xB2, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0xD5, 0xBC, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0xFD, 0xE2, 0xF3, 0xAD, 0x00, 0x00, 0xFD, 0xDB, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0xB0, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0xD1, 0xA7, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0xFD, 0xE3, 0xCE, 0xB3, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0xFD, 0xE4, 0xFA, 0xCE, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0xCA, 0xB0, 0x00, 0x00, 0xF7, 0xA7, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xCF, 0xB1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE6, 0xA2, /* 0x5C-0x5F */
+ 0x00, 0x00, 0xFC, 0xB6, 0xF2, 0xAD, 0xEF, 0xE1, /* 0x60-0x63 */
+ 0xF3, 0xAE, 0xDC, 0xC6, 0xD9, 0xEB, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0xE8, 0xE0, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0xA8, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD5, 0xF6, /* 0x74-0x77 */
+ 0xCF, 0xFD, 0x00, 0x00, 0x00, 0x00, 0xDE, 0xDD, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD9, 0xD1, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0xEA, /* 0x80-0x83 */
+ 0xF2, 0xCF, 0x00, 0x00, 0xF7, 0xBF, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0xE2, 0xE6, 0xE2, 0xA8, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0xE3, 0xD6, 0x00, 0x00, /* 0x94-0x97 */
+ 0xED, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0xF9, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0xD6, 0xB1, 0xDE, 0xB2, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0xE8, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0xD3, 0xAB, 0x00, 0x00, 0xEB, 0xDC, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0xDF, 0xAF, 0x00, 0x00, /* 0xB8-0xBB */
+ 0xCA, 0xC3, 0x00, 0x00, 0x00, 0x00, 0xEE, 0xFC, /* 0xBC-0xBF */
+ 0x00, 0x00, 0xFD, 0xC3, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0xEB, 0xF6, 0xCF, 0xB2, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD9, 0xEC, /* 0xC8-0xCB */
+ 0x00, 0x00, 0xD9, 0xBD, 0x00, 0x00, 0xD8, 0xDF, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0xD4, 0xB8, 0xEB, 0xBE, /* 0xD0-0xD3 */
+ 0xDD, 0xEF, 0x00, 0x00, 0xDD, 0xF0, 0xDD, 0xF1, /* 0xD4-0xD7 */
+ 0xDD, 0xF2, 0x00, 0x00, 0x00, 0x00, 0xD9, 0xBE, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFB, 0xC6, /* 0xE8-0xEB */
+ 0xCF, 0xB3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+};
+
+static unsigned char u2c_6C[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0xEE, 0xFD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0xAB, /* 0x0C-0x0F */
+ 0x00, 0x00, 0xDA, 0xC5, 0x00, 0x00, 0xD8, 0xEC, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD1, 0xA8, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0xE2, 0xA9, 0x00, 0x00, 0x00, 0x00, 0xDE, 0xBC, /* 0x34-0x37 */
+ 0xE7, 0xB5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0xDB, 0xF0, 0x00, 0x00, /* 0x3C-0x3F */
+ 0xEF, 0xE2, 0xF1, 0xF0, 0xCF, 0xB4, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0xDB, 0xF1, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xE0, 0xB1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0xDF, 0xA5, 0x00, 0x00, 0xF9, 0xD2, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0xE7, 0xFD, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0xE6, 0xA3, 0xFB, 0xF1, 0xCB, 0xB0, /* 0x5C-0x5F */
+ 0xF2, 0xAE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0xCD, 0xE7, 0x00, 0x00, 0xE8, 0xDC, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0xE7, 0xD7, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xF7, 0xC0, 0x00, 0x00, 0xD0, 0xE3, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0xDA, 0xA1, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0xCC, 0xBD, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0xD1, 0xA9, 0xDD, 0xCC, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0xE3, 0xFE, 0xD1, 0xAA, 0xE8, 0xAA, /* 0x80-0x83 */
+ 0x00, 0x00, 0xEA, 0xB6, 0xF9, 0xFA, 0xE6, 0xCC, /* 0x84-0x87 */
+ 0xF6, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0xD4, 0xC7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0xD9, 0xCB, 0x00, 0x00, 0xD9, 0xD2, 0xD3, 0xCB, /* 0x90-0x93 */
+ 0xD8, 0xF7, 0xDA, 0xA9, 0xF5, 0xF8, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0xDE, 0xDE, 0xF2, 0xAF, 0xF8, 0xA9, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD8, 0xC8, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0xEE, 0xC1, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xC1, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0xDD, 0xF3, 0xEA, 0xFA, 0x00, 0x00, 0xF6, 0xBD, /* 0xB8-0xBB */
+ 0xE1, 0xBB, 0xCD, 0xBF, 0xF4, 0xD4, 0xE6, 0xCD, /* 0xBC-0xBF */
+ 0x00, 0x00, 0xFC, 0xCF, 0xFB, 0xA2, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xE0, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xF4, 0xBB, 0xDA, 0xD5, 0x00, 0x00, /* 0xC8-0xCB */
+ 0xF9, 0xB2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFB, 0xF2, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0xDB, 0xF6, 0x00, 0x00, 0xDE, 0xDF, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDB, 0xF2, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xF8, 0xDC, 0xF7, 0xEE, 0xEB, 0xE8, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0xD2, 0xFA, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xF1, 0xBC, 0x00, 0x00, 0x00, 0x00, 0xFA, 0xDA, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0xDA, 0xEA, 0xDA, 0xC6, /* 0xEC-0xEF */
+ 0xF7, 0xC1, 0x00, 0x00, 0x00, 0x00, 0xE7, 0xB6, /* 0xF0-0xF3 */
+};
+
+static unsigned char u2c_6D[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0xC7, /* 0x08-0x0B */
+ 0xD6, 0xAC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0xDC, 0xC7, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE1, 0xA9, /* 0x14-0x17 */
+ 0x00, 0x00, 0xE2, 0xAA, 0x00, 0x00, 0xD5, 0xA6, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0xD4, 0xD7, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0xF2, 0xD0, 0x00, 0x00, 0xEA, 0xFB, /* 0x24-0x27 */
+ 0x00, 0x00, 0xE0, 0xDD, 0xFB, 0xF3, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0xF1, 0xBD, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0xE2, 0xE7, 0xFD, 0xD7, 0x00, 0x00, /* 0x34-0x37 */
+ 0xCE, 0xC8, 0xEA, 0xB7, 0x00, 0x00, 0xFC, 0xC0, /* 0x38-0x3B */
+ 0x00, 0x00, 0xFD, 0xE7, 0xF7, 0xEF, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0xD7, 0xB5, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0xEF, 0xBA, 0xF1, 0xDD, 0x00, 0x00, /* 0x58-0x5B */
+ 0xDE, 0xB3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0xCB, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0xF8, 0xDD, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0xFB, 0xC7, 0xD5, 0xC8, 0x00, 0x00, /* 0x68-0x6B */
+ 0xD7, 0xDF, 0x00, 0x00, 0xDD, 0xA9, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0xE9, 0xB1, 0x00, 0x00, 0x00, 0x00, 0xFA, 0xAD, /* 0x74-0x77 */
+ 0xF6, 0xD9, 0xFA, 0xF4, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xAA, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0xE6, 0xEE, 0x00, 0x00, 0xCC, 0xDC, /* 0x84-0x87 */
+ 0xE1, 0xBC, 0xE0, 0xEF, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0xE9, 0xBF, 0xFC, 0xFD, 0xE6, 0xCE, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xE1, 0xD7, 0x00, 0x00, 0xE6, 0xCF, /* 0x90-0x93 */
+ 0x00, 0x00, 0xF4, 0xF1, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0xF3, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0xE4, 0xFB, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0xF9, 0xE4, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0xEF, 0xE3, 0x00, 0x00, 0x00, 0x00, 0xCF, 0xEE, /* 0xC0-0xC3 */
+ 0xF6, 0xBE, 0xE0, 0xB2, 0xFC, 0xFE, 0xD1, 0xAB, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD7, 0xFA, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFB, 0xC8, /* 0xCC-0xCF */
+ 0x00, 0x00, 0xE2, 0xD7, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xD4, 0xA3, 0xF0, 0xF8, 0xD7, 0xA8, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0xE7, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xD3, 0xBF, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xEF, 0xE4, 0x00, 0x00, 0xD7, 0xC5, 0xEB, 0xE2, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0xFC, 0xE7, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0xE4, 0xA2, 0x00, 0x00, 0xE2, 0xE8, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0xE6, 0xD0, 0x00, 0x00, 0xFB, 0xE8, /* 0xF4-0xF7 */
+ 0xF4, 0xE8, 0xE5, 0xF4, 0xF4, 0xBC, 0xF4, 0xD5, /* 0xF8-0xFB */
+};
+
+static unsigned char u2c_6E[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDF, 0xB6, /* 0x14-0x17 */
+ 0x00, 0x00, 0xFC, 0xB9, 0xEE, 0xC2, 0xCA, 0xF5, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEF, 0xE5, /* 0x1C-0x1F */
+ 0xCB, 0xE2, 0xD4, 0xA4, 0x00, 0x00, 0xDE, 0xE0, /* 0x20-0x23 */
+ 0xDA, 0xFD, 0xE4, 0xC6, 0xE8, 0xBE, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xDE, /* 0x28-0x2B */
+ 0xF6, 0xB4, 0xEA, 0xD2, 0x00, 0x00, 0xF9, 0xFB, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0xC2, 0x00, 0x00, /* 0x30-0x33 */
+ 0xCA, 0xE4, 0x00, 0x00, 0xE7, 0xB7, 0x00, 0x00, /* 0x34-0x37 */
+ 0xEA, 0xFD, 0x00, 0x00, 0xD9, 0xDD, 0x00, 0x00, /* 0x38-0x3B */
+ 0xDA, 0xB4, 0xEE, 0xAA, 0xFB, 0xE9, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDB, 0xCB, /* 0x40-0x43 */
+ 0xDA, 0xB5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0xF1, 0xBE, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0xD3, 0xAC, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0xFB, 0xC9, 0x00, 0x00, /* 0x54-0x57 */
+ 0xDF, 0xCF, 0x00, 0x00, 0x00, 0x00, 0xD3, 0xC0, /* 0x58-0x5B */
+ 0xE3, 0xD7, 0x00, 0x00, 0xEF, 0xE6, 0xFC, 0xD0, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE9, 0xC0, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF5, 0xD3, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0xEC, 0xDC, 0xF7, 0xB7, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0xEA, 0xB8, 0xD1, 0xF9, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0xDC, 0xC8, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0xEA, 0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0xF1, 0xDE, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0xD7, 0xB6, 0xCF, 0xB5, 0x00, 0x00, 0xD9, 0xA8, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0xEC, 0xEE, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0xDD, 0xAA, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0xCD, 0xA2, 0xE8, 0xAE, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE1, 0xBD, /* 0xAC-0xAF */
+ 0x00, 0x00, 0xF2, 0xD1, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0xC1, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0xD2, 0xFC, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0xDB, 0xB5, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xF3, 0xE7, 0xD8, 0xFE, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xFC, 0xD1, 0x00, 0x00, 0xED, 0xB2, /* 0xC8-0xCB */
+ 0xF4, 0xAF, 0x00, 0x00, 0xFB, 0xA3, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0xFC, 0xC1, 0x00, 0x00, 0xEE, 0xAB, /* 0xD0-0xD3 */
+ 0xD4, 0xA5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF4, 0xF2, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xEE, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0xFB, 0xCA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0xCD, 0xE3, 0xD8, 0xBB, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_6F[512] = {
+ 0x00, 0x00, 0xE5, 0xDB, 0xF8, 0xF7, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0xF6, 0xD4, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD7, 0xA9, /* 0x0C-0x0F */
+ 0x00, 0x00, 0xCB, 0xC9, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0xE6, 0xD1, 0xF0, 0xCC, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0xD8, 0xAE, 0x00, 0x00, 0xF9, 0xD3, 0xD5, 0xFE, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD8, 0xBC, /* 0x28-0x2B */
+ 0xF2, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0xE2, 0xAB, 0xF3, 0xE8, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0xEF, 0xC2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xED, 0xEC, /* 0x3C-0x3F */
+ 0x00, 0x00, 0xE7, 0xB8, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0xDA, 0xFE, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0xCC, 0xBE, 0x00, 0x00, 0x00, 0x00, 0xF2, 0xFC, /* 0x54-0x57 */
+ 0xDA, 0xEB, 0x00, 0x00, 0xE2, 0xD8, 0xED, 0xD6, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0xD6, 0xD1, 0xE0, 0xB3, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0xFC, 0xD2, 0x00, 0x00, /* 0x60-0x63 */
+ 0xEB, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0xD3, 0xC1, 0xF0, 0xCD, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xCF, 0xF7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0xED, 0xD2, 0x00, 0x00, /* 0x78-0x7B */
+ 0xD4, 0xD8, 0xDC, 0xC9, 0xD7, 0xF1, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0xDF, 0xBB, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0xF3, 0xA5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0xF4, 0xCD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0xF1, 0xBF, 0xF8, 0xB1, 0x00, 0x00, /* 0x8C-0x8F */
+ 0xE9, 0xFA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0xFB, 0xCB, 0x00, 0x00, 0x00, 0x00, 0xCA, 0xD5, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xD4, /* 0xA0-0xA3 */
+ 0xF7, 0xCA, 0x00, 0x00, 0x00, 0x00, 0xD6, 0xC8, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0xFC, 0xE8, 0xF3, 0xBD, /* 0xAC-0xAF */
+ 0x00, 0x00, 0xEE, 0xFE, 0x00, 0x00, 0xE7, 0xFE, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xD3, 0xC2, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0xD3, 0xB6, 0x00, 0x00, /* 0xBC-0xBF */
+ 0xCC, 0xAD, 0xF6, 0xFA, 0xD6, 0xB2, 0xD2, 0xD8, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0xE7, 0xD8, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0xE3, 0xA5, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0xE7, 0xB9, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xAD, /* 0xDC-0xDF */
+ 0xFB, 0xCC, 0xEB, 0xA1, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0xD4, 0xA6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0xFB, 0xCD, 0x00, 0x00, 0xD5, 0xBD, /* 0xE8-0xEB */
+ 0xF1, 0xDF, 0x00, 0x00, 0x00, 0x00, 0xF6, 0xFB, /* 0xEC-0xEF */
+ 0x00, 0x00, 0xDE, 0xB4, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0xD5, 0xEB, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_70[512] = {
+ 0x00, 0x00, 0xE5, 0xC8, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0xFB, 0xA4, 0xD4, 0xB9, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0xDE, 0xE1, 0x00, 0x00, 0xE4, 0xA3, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD7, 0xB7, /* 0x0C-0x0F */
+ 0x00, 0x00, 0xF8, 0xEE, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0xDE, 0xB5, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0xD6, 0xD2, 0x00, 0x00, 0xF9, 0xD5, 0xE7, 0xBA, /* 0x18-0x1B */
+ 0xEB, 0xD5, 0xD5, 0xF7, 0xEF, 0xE7, 0xE1, 0xBE, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0xAE, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD6, 0xE9, /* 0x24-0x27 */
+ 0xD6, 0xEE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE7, 0xBB, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEC, 0xCB, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0xD5, 0xB3, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0xCE, 0xB4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xFB, 0xA5, 0xE1, 0xEE, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0xF7, 0xA8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0xFB, 0xCE, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD8, 0xBD, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFB, 0xFD, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xFC, 0xE9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0xCF, 0xB6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0xED, 0xC7, 0xEE, 0xAC, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0xCC, 0xDD, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0xF6, 0xA7, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0xE6, 0xFA, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0xF5, 0xA4, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0xFD, 0xDC, 0xED, 0xB3, 0xCE, 0xC9, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0xEF, 0xE8, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0xE1, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0xDB, /* 0xA8-0xAB */
+ 0xCB, 0xE3, 0xF7, 0xA9, 0x00, 0x00, 0xFB, 0xA6, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0xB9, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF1, 0xC0, /* 0xB4-0xB7 */
+ 0xED, 0xC8, 0xEF, 0xC3, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0xD6, 0xAD, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xCE, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0xA1, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xFB, 0xF4, 0xD5, 0xA7, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0xF1, 0xF6, 0x00, 0x00, 0xE6, 0xD3, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0xCC, 0xDE, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xF8, 0xB2, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0xDC, 0xEB, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_71[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0xFD, 0xB6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0xE5, 0xEA, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0xF1, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0xDB, 0xCC, 0xDD, 0xCD, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0xD4, 0xC8, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0xD9, 0xED, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0xF5, 0xA5, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xE6, 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0xE6, 0xD4, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xC8, /* 0x44-0x47 */
+ 0x00, 0x00, 0xD6, 0xA1, 0xFD, 0xBF, 0x00, 0x00, /* 0x48-0x4B */
+ 0xFC, 0xD3, 0x00, 0x00, 0xEF, 0xA1, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xE7, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0xD1, 0xEE, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0xE6, 0xD5, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0xE9, 0xF2, 0x00, 0x00, 0xDF, 0xB0, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0xD8, 0xE0, 0xFC, 0xBA, 0xFD, 0xAF, 0xF0, 0xCE, /* 0x64-0x67 */
+ 0x00, 0x00, 0xDB, 0xE1, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0xE5, 0xC9, 0x00, 0x00, 0xED, 0xB4, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0xE0, 0xC3, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0xE3, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0xE9, 0xFB, 0xEA, 0xA8, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xB7, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0xFB, 0xA7, 0x00, 0x00, /* 0x90-0x93 */
+ 0xE9, 0xC2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0xFD, 0xF7, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2, 0xD9, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0xDC, 0xEC, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0xE8, 0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0xE6, 0xF0, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xFD, 0xF8, 0xFD, 0xF9, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0xF6, 0xBF, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0xE7, 0xA7, 0x00, 0x00, 0xE6, 0xD7, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0xD4, 0xF3, 0xD4, 0xC9, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0xD6, 0xFA, 0x00, 0x00, /* 0xCC-0xCF */
+ 0xD7, 0xF2, 0x00, 0x00, 0xE1, 0xC0, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xDB, 0xE2, 0xE6, 0xD8, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE7, 0xBD, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0xF0, 0xCF, 0xF3, 0xBE, 0xE2, 0xAC, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0xF5, 0xB7, 0xE0, 0xF0, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xB8, /* 0xF8-0xFB */
+ 0xE3, 0xE8, 0x00, 0x00, 0xD4, 0xA7, 0xE8, 0xFC, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_72[512] = {
+ 0xFA, 0xD2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0xF8, 0xEF, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xD6, 0xD3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD5, 0xB4, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xF0, 0xD0, 0x00, 0x00, /* 0x28-0x2B */
+ 0xF7, 0xF0, 0xEE, 0xB3, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xEA, 0xBA, 0x00, 0x00, 0xEA, 0xD3, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0xED, 0xC9, 0xDD, 0xAB, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0xAC, 0xFD, 0xA1, /* 0x38-0x3B */
+ 0x00, 0x00, 0xDF, 0xD0, 0xEC, 0xB3, 0x00, 0x00, /* 0x3C-0x3F */
+ 0xDF, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0xED, 0xED, 0xF8, 0xB8, /* 0x44-0x47 */
+ 0xF7, 0xFA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0xF8, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0xF4, 0xE0, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0xD4, 0xBA, 0xE4, 0xB3, 0x00, 0x00, 0xE9, 0xDA, /* 0x58-0x5B */
+ 0x00, 0x00, 0xDE, 0xB6, 0x00, 0x00, 0xD9, 0xBF, /* 0x5C-0x5F */
+ 0x00, 0x00, 0xD9, 0xC0, 0xD6, 0xEF, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD9, 0xCC, /* 0x64-0x67 */
+ 0x00, 0x00, 0xDA, 0xAA, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0xDF, 0xE5, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0xF7, 0xE5, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0xCC, 0xB2, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0xDF, 0xF9, 0xD7, 0xE0, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0xD4, 0xBB, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xFA, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0xCC, 0xB3, 0x00, 0x00, 0x00, 0x00, 0xDB, 0xF3, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0xDF, 0xD2, 0x00, 0x00, 0xCE, 0xCA, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xEE, 0xDA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0xE4, 0xE4, 0x00, 0x00, /* 0xCC-0xCF */
+ 0xFB, 0xCF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCF, 0xB7, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0xEE, 0xC3, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xCE, 0xEA, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0xE2, 0xAD, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0xD7, 0xE1, 0xFA, 0xF5, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0xD5, 0xC9, 0xF8, 0xAC, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_73[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0xE7, 0xD9, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0xF3, 0xE9, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD8, 0xED, /* 0x18-0x1B */
+ 0xE3, 0xC4, 0xF0, 0xF1, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0xE8, 0xE5, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0xE0, 0xFA, 0xEE, 0xC4, 0xD9, 0xDE, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0xEB, 0xA2, 0xEB, 0xA3, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0xFC, 0xC2, 0xEA, 0xBB, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0xE8, 0xAB, 0xDE, 0xE2, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xED, 0xEF, 0x00, 0x00, 0xE8, 0xA3, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCF, 0xF1, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0xD4, 0xBC, 0x00, 0x00, 0xFC, 0xEA, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xE7, 0xBE, 0x00, 0x00, 0xFC, 0xF2, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0xD6, 0xB4, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0xE2, 0xAE, 0x00, 0x00, 0xD3, 0xB7, 0xFA, 0xCC, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0xFA, 0xDC, 0x00, 0x00, 0xED, 0xB5, 0xE1, 0xE3, /* 0x84-0x87 */
+ 0x00, 0x00, 0xE8, 0xAC, 0x00, 0x00, 0xE8, 0xDD, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0xEF, 0xE9, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0xF4, 0xBD, 0x00, 0x00, 0xCF, 0xB8, 0xE9, 0xDB, /* 0x94-0x97 */
+ 0xD1, 0xAC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDA, 0xC7, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEB, 0xC9, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0xE8, 0xCC, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0xDE, 0xB7, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0xD6, 0xBC, 0xD3, 0xE5, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xFA, 0xDD, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0xDA, 0xD6, 0x00, 0x00, 0xCA, 0xB1, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xDA, 0xC8, 0xDF, 0xA6, 0x00, 0x00, /* 0xC8-0xCB */
+ 0xF9, 0xB3, 0xF2, 0xD2, 0x00, 0x00, 0xCA, 0xC4, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0xCE, 0xCB, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0xCD, 0xF5, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0xFD, 0xB0, 0xD5, 0xA8, 0x00, 0x00, /* 0xDC-0xDF */
+ 0xF1, 0xC1, 0x00, 0x00, 0x00, 0x00, 0xE2, 0xE9, /* 0xE0-0xE3 */
+ 0xDC, 0xCA, 0xEC, 0xB4, 0xFA, 0xC0, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0xFB, 0xA8, 0xD0, 0xA8, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0xDA, 0xEC, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD9, 0xEE, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xE0, 0xFB, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0xEF, 0xEA, 0xFA, 0xDE, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_74[512] = {
+ 0x00, 0x00, 0xE0, 0xC4, 0x00, 0x00, 0xCF, 0xB9, /* 0x00-0x03 */
+ 0x00, 0x00, 0xD5, 0xCA, 0xD7, 0xE2, 0xE2, 0xAF, /* 0x04-0x07 */
+ 0x00, 0x00, 0xD7, 0xB8, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0xCD, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF6, 0xDA, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0xEF, 0xA2, 0xE2, 0xDA, 0xF6, 0xFC, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0xFB, 0xD0, 0xD1, 0xAD, 0x00, 0x00, /* 0x24-0x27 */
+ 0xCD, 0xE4, 0x00, 0x00, 0xD1, 0xAE, 0xDC, 0xED, /* 0x28-0x2B */
+ 0xE8, 0xCE, 0x00, 0x00, 0xF0, 0xF9, 0xCE, 0xB5, /* 0x2C-0x2F */
+ 0xE6, 0xFC, 0x00, 0x00, 0x00, 0x00, 0xD7, 0xFB, /* 0x30-0x33 */
+ 0xD0, 0xD6, 0xDD, 0xF5, 0xF7, 0xF1, 0x00, 0x00, /* 0x34-0x37 */
+ 0xF6, 0xFD, 0x00, 0x00, 0xDB, 0xF7, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFB, 0xEA, /* 0x3C-0x3F */
+ 0xE9, 0xDC, 0xD9, 0xC1, 0x00, 0x00, 0xF5, 0xF2, /* 0x40-0x43 */
+ 0xE0, 0xC5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEA, 0xD4, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0xF9, 0xC2, 0x00, 0x00, 0xEA, 0xBC, /* 0x54-0x57 */
+ 0x00, 0x00, 0xD2, 0xC5, 0xFB, 0xD1, 0xE7, 0xC0, /* 0x58-0x5B */
+ 0xEB, 0xA5, 0x00, 0x00, 0xDF, 0xFA, 0xE3, 0xA2, /* 0x5C-0x5F */
+ 0xD7, 0xB9, 0x00, 0x00, 0xE9, 0xC3, 0x00, 0x00, /* 0x60-0x63 */
+ 0xE8, 0xFD, 0xE8, 0xAF, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0xF2, 0xD3, 0xFB, 0xA9, 0xD8, 0xA5, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD5, 0xCB, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0xD0, 0xC8, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0xD1, 0xAF, 0xD7, 0xE3, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xC6, /* 0x84-0x87 */
+ 0x00, 0x00, 0xD6, 0xA2, 0x00, 0x00, 0xED, 0xF0, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0xD7, 0xF3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0xFC, 0xD4, 0x00, 0x00, 0xDA, 0xD7, 0xCC, 0xDF, /* 0x9C-0x9F */
+ 0x00, 0x00, 0xF2, 0xD4, 0x00, 0x00, 0xD1, 0xB0, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0xCC, 0xE0, 0x00, 0x00, 0xDB, 0xFD, /* 0xA4-0xA7 */
+ 0xF3, 0xBF, 0x00, 0x00, 0xF0, 0xD1, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0xFC, 0xBB, 0x00, 0x00, 0xE2, 0xB0, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0xE6, 0xA5, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xE2, 0xDB, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0xDF, 0xDE, 0x00, 0x00, 0xE0, 0xC7, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0xF2, 0xEF, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0xCC, 0xE1, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD6, 0xEA, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xE7, 0xC2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xCE, 0xB6, 0x00, 0x00, 0xF3, 0xC0, 0x00, 0x00, /* 0xD8-0xDB */
+ 0xCD, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0xFB, 0xD2, 0x00, 0x00, 0xF8, 0xF8, 0xF7, 0xFB, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0xE8, 0xBF, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0xE8, 0xB7, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xED, 0xB6, /* 0xF4-0xF7 */
+};
+
+static unsigned char u2c_75[512] = {
+ 0x00, 0x00, 0xDC, 0xBA, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0xCC, 0xB4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0xF1, 0xF7, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0xE8, 0xB8, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0xCA, 0xF6, 0x00, 0x00, 0xE4, 0xA4, 0xF4, 0xD6, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDF, 0xE6, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDF, 0xA7, /* 0x20-0x23 */
+ 0x00, 0x00, 0xDF, 0xE7, 0xE1, 0xC1, 0x00, 0x00, /* 0x24-0x27 */
+ 0xE9, 0xC4, 0x00, 0x00, 0x00, 0x00, 0xDC, 0xCB, /* 0x28-0x2B */
+ 0xE9, 0xC5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xEF, 0xA3, 0xEB, 0xA6, 0xCB, 0xA3, 0xE3, 0xE9, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD1, 0xFB, /* 0x34-0x37 */
+ 0xEF, 0xA4, 0x00, 0x00, 0xEF, 0xEB, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD0, 0xB4, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0xCD, 0xA3, 0x00, 0x00, 0x00, 0x00, 0xE8, 0xE6, /* 0x4C-0x4F */
+ 0x00, 0x00, 0xEF, 0xA5, 0x00, 0x00, 0xD3, 0xCC, /* 0x50-0x53 */
+ 0xDA, 0xED, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0xD7, 0xBA, 0x00, 0x00, 0xF2, 0xD5, /* 0x58-0x5B */
+ 0xF5, 0xE5, 0xD9, 0xEF, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0xF9, 0xB4, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0xD5, 0xD4, 0xFD, 0xCF, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0xDB, 0xE3, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF1, 0xE1, /* 0x6C-0x6F */
+ 0xEC, 0xB6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0xFB, 0xFE, 0xD3, 0xD7, 0x00, 0x00, /* 0x74-0x77 */
+ 0xD1, 0xB1, 0x00, 0x00, 0xCB, 0xB1, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD1, 0xB2, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0xCB, 0xB2, 0xF1, 0xC2, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0xF4, 0xE1, 0xF9, 0xB5, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0xC3, 0xE1, 0xC2, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xEB, 0xF7, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0xDF, 0xA8, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0xCB, 0xCA, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE6, 0xB9, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0xF8, 0xDE, 0xF9, 0xAA, 0xCA, 0xF7, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0xED, 0xB7, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0xD3, 0xB8, 0xF2, 0xD6, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0xD4, 0xD9, 0xEE, 0xC5, 0xF2, 0xF0, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0xCA, 0xB2, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0xDC, 0xBB, 0x00, 0x00, 0xF1, 0xF8, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0xEC, 0xB7, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0xCA, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xF6, 0xC0, 0xFD, 0xDD, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xD4, 0xE3, 0xCC, 0xE2, 0x00, 0x00, 0xF7, 0xD4, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0xD7, 0xE5, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0xD3, 0xC3, 0x00, 0x00, 0xD8, 0xA6, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xF6, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0xDD, 0xF6, 0x00, 0x00, /* 0xF8-0xFB */
+ 0xCD, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_76[512] = {
+ 0xE5, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0xE5, 0xCB, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0xE1, 0xC4, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0xB0, /* 0x1C-0x1F */
+ 0xF4, 0xB0, 0xF3, 0xEA, 0xDA, 0xEE, 0x00, 0x00, /* 0x20-0x23 */
+ 0xD7, 0xBB, 0x00, 0x00, 0xE2, 0xB1, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD7, 0xAA, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0xD6, 0xFB, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0xE4, 0xDF, 0x00, 0x00, 0xCA, 0xD6, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0xEB, 0xA8, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0xDB, 0xFE, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0xF6, 0xC2, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0xEF, 0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0xD4, 0xFD, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0xE0, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xE8, 0xB9, 0x00, 0x00, 0xEF, 0xA6, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0xCD, 0xA4, 0x00, 0x00, 0x00, 0x00, 0xD4, 0xF4, /* 0x78-0x7B */
+ 0xDB, 0xA1, 0xDB, 0xDC, 0xDB, 0xDD, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0xEE, 0xDC, 0x00, 0x00, 0xCB, 0xCB, 0xFC, 0xD5, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0xCE, 0xEB, 0x00, 0x00, /* 0x8C-0x8F */
+ 0xCD, 0xC1, 0x00, 0x00, 0x00, 0x00, 0xFB, 0xD3, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0xF9, 0xAB, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0xF5, 0xD4, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD9, 0xA9, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0xDD, 0xDB, 0xCD, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0xDD, 0xCE, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0xE7, 0xC3, 0x00, 0x00, 0xEC, 0xCC, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0xF9, 0xEC, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0xCB, 0xCC, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFC, /* 0xD8-0xDB */
+ 0xD4, 0xA8, 0x00, 0x00, 0xED, 0xD3, 0xD8, 0xEF, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xF2, 0xD7, 0x00, 0x00, 0xCA, 0xF8, /* 0xE0-0xE3 */
+ 0xDA, 0xEF, 0x00, 0x00, 0x00, 0x00, 0xD6, 0xD4, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0xD9, 0xCD, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0xD8, 0xEE, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xF2, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0xDF, 0xD3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0xDA, 0xF0, 0x00, 0x00, 0xE2, 0xEA, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_77[512] = {
+ 0x00, 0x00, 0xE0, 0xFD, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0xD8, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0xF7, 0xAF, 0xDA, 0xB6, 0x00, 0x00, 0xCA, 0xD7, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0xF2, 0xD8, 0x00, 0x00, /* 0x1C-0x1F */
+ 0xD8, 0xF9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0xFA, 0xDF, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCF, 0xEF, /* 0x34-0x37 */
+ 0xD9, 0xC2, 0x00, 0x00, 0xF0, 0xD2, 0x00, 0x00, /* 0x38-0x3B */
+ 0xE4, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0xF3, 0xB7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0xFA, 0xE0, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEF, 0xEC, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0xE2, 0xB2, 0x00, 0x00, 0xD4, 0xBD, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0xD9, 0xCE, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF4, 0xE2, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0xD4, 0xA9, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0xCD, 0xC2, 0xE7, 0xDA, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF2, 0xD9, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xD9, 0xAA, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0xD8, 0xBE, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0xDC, 0xAD, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0xE2, 0xEB, 0xD6, 0xFC, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0xCA, 0xF9, 0x00, 0x00, 0x00, 0x00, 0xD4, 0xDA, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF4, 0xD7, /* 0xB8-0xBB */
+ 0xCC, 0xA1, 0x00, 0x00, 0x00, 0x00, 0xCF, 0xBA, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF5, 0xB8, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD9, 0xC3, /* 0xD8-0xDB */
+ 0xD0, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0xE3, 0xC5, 0xEB, 0xF8, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0xF2, 0xB1, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0xCF, 0xBB, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0xD3, 0xAD, 0xE8, 0xE1, 0xCE, 0xEC, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xB4, /* 0xF0-0xF3 */
+};
+
+static unsigned char u2c_78[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0xDE, 0xE3, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0xDD, 0xF7, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0xF2, 0xB2, 0xF3, 0xF6, 0xF6, 0xDB, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0xD7, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0xF8, 0xDF, 0x00, 0x00, /* 0x30-0x33 */
+ 0xF7, 0xF2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0xD0, 0xA9, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE6, 0xDA, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0xF5, 0xA6, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD7, 0xBC, /* 0x68-0x6B */
+ 0xCC, 0xE3, 0x00, 0x00, 0x00, 0x00, 0xE6, 0xDB, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0xDD, 0xDD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0xD1, 0xB3, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEF, 0xED, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0xD6, 0xDE, 0xE4, 0xF4, 0xE1, 0xEF, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xDD, 0xF8, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0xCF, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCA, 0xE5, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0xA1, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0xE0, 0xB5, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0xFC, 0xAC, 0xFC, 0xAD, /* 0xB8-0xBB */
+ 0xD8, 0xA7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0xED, 0xB8, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0xDB, 0xB6, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0xD6, 0xF0, 0xF3, 0xAF, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0xCD, 0xA5, 0x00, 0x00, /* 0xCC-0xCF */
+ 0xDA, 0xF1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xD8, 0xA8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0xCC, 0xE4, 0x00, 0x00, 0x00, 0x00, 0xD1, 0xB4, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0xCA, 0xD8, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDA, 0xF2, /* 0xF8-0xFB */
+};
+
+static unsigned char u2c_79[512] = {
+ 0x00, 0x00, 0xF5, 0xA7, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0xF5, 0xA8, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0xE6, 0xA6, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xD5, 0xEC, 0xD5, 0xF8, /* 0x28-0x2B */
+ 0xDA, 0xF3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0xE3, 0xC6, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0xDE, 0xE4, 0x00, 0x00, /* 0x3C-0x3F */
+ 0xDE, 0xE5, 0xD1, 0xB5, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD1, 0xB6, /* 0x44-0x47 */
+ 0xD1, 0xB7, 0xF2, 0xB3, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xE9, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0xF0, 0xD3, 0xF2, 0xB4, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0xF0, 0xD4, 0xCB, 0xE4, /* 0x58-0x5B */
+ 0xFB, 0xD4, 0xF5, 0xE6, 0xE3, 0xEA, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xDE, 0xE6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0xDF, 0xD4, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0xF8, 0xF9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0xF0, 0xAE, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0xD1, 0xB8, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD6, 0xDF, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0xD0, 0xD7, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0xFC, 0xA1, 0xEF, 0xEE, 0xDC, 0xD8, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xE9, 0xDF, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0xDD, 0xFD, 0xFB, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0xC9, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0xD6, 0xC9, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0xD4, 0xAA, 0x00, 0x00, 0xE5, 0xCC, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xE9, 0xE0, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0xD0, 0xD8, 0xFC, 0xA2, 0xD4, 0xBE, /* 0xBC-0xBF */
+ 0xE2, 0xB3, 0xDE, 0xE7, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xDC, 0xBC, 0xD2, 0xB6, 0xF5, 0xD5, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0xCE, 0xA1, 0xF5, 0xA9, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0xDD, 0xF9, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xDD, 0xFA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xD5, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0xF6, 0xDF, 0x00, 0x00, 0xF2, 0xDA, 0xE4, 0xEB, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0xF2, 0xF1, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEC, 0xB9, /* 0xF8-0xFB */
+};
+
+static unsigned char u2c_7A[512] = {
+ 0xFD, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0xE1, 0xAA, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0xCA, 0xD9, 0x00, 0x00, 0x00, 0x00, 0xEF, 0xEF, /* 0x08-0x0B */
+ 0x00, 0x00, 0xF5, 0xAA, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0xEC, 0xF9, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xAD, /* 0x14-0x17 */
+ 0x00, 0x00, 0xF2, 0xC2, 0xF6, 0xC3, 0x00, 0x00, /* 0x18-0x1B */
+ 0xD7, 0xD2, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xA2, /* 0x1C-0x1F */
+ 0xF0, 0xD6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFA, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0xF6, 0xE0, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0xF3, 0xF2, 0xC3, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD4, 0xAB, /* 0x38-0x3B */
+ 0xCA, 0xB3, 0xCD, 0xA6, 0x00, 0x00, 0xCD, 0xC3, /* 0x3C-0x3F */
+ 0xCD, 0xDA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0xD9, 0xCF, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0xF6, 0xC4, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0xEE, 0xDD, 0xE7, 0xC4, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2, 0xB4, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0xDF, 0xE2, 0xE7, 0xDB, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0xE8, 0xB1, 0x00, 0x00, 0xFC, 0xAE, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xE5, 0xCD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0xFA, 0xEB, 0x00, 0x00, 0xCF, 0xBC, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0xCF, 0xE2, 0xCD, 0xF6, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0xEF, 0xF0, 0x00, 0x00, 0xF4, 0xBE, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0xD4, 0xCD, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0xF3, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0xE9, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0xF2, 0xF2, 0xF3, 0xEB, /* 0x90-0x93 */
+ 0x00, 0x00, 0xF0, 0xD7, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0xCF, 0xD7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCF, 0xDF, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0xE8, 0xC0, 0xE8, 0xC1, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0xCF, 0xE3, 0xE9, 0xA2, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0xD0, 0xAA, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xF3, 0xC1, 0xD0, 0xAB, 0x00, 0x00, 0xD4, 0xE4, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0xEF, 0xBC, 0xD8, 0xA1, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD9, 0xDF, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0xF3, 0xD7, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0xDC, 0xBD, 0x00, 0x00, 0xCC, 0xE5, /* 0xDC-0xDF */
+ 0xED, 0xF1, 0x00, 0x00, 0x00, 0x00, 0xF1, 0xE2, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0xD4, 0xDB, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0xE2, 0xB5, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0xCA, 0xE6, 0x00, 0x00, 0xD3, 0xAE, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0xCC, 0xE6, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xF1, 0xD3, 0xF5, 0xE7, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCA, 0xDA, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_7B[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFB, 0xEE, /* 0x0C-0x0F */
+ 0x00, 0x00, 0xE1, 0xC5, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0xDF, 0xE9, 0x00, 0x00, 0xEE, 0xDE, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0xF7, 0xC2, 0x00, 0x00, /* 0x1C-0x1F */
+ 0xD8, 0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0xDD, 0xAC, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0xF0, 0xAF, 0xD6, 0xBD, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0xE1, 0xAB, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0xF9, 0xB6, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0xD4, 0xF5, 0x00, 0x00, 0xD0, 0xC9, /* 0x48-0x4B */
+ 0xEF, 0xA7, 0xE2, 0xEC, 0x00, 0x00, 0xDB, 0xEA, /* 0x4C-0x4F */
+ 0xCE, 0xCC, 0xF5, 0xE8, 0xF7, 0xD5, 0x00, 0x00, /* 0x50-0x53 */
+ 0xD3, 0xCD, 0x00, 0x00, 0xF3, 0xFE, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xD0, 0xB5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0xE0, 0xFE, 0x00, 0x00, 0xDF, 0xFB, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0xE6, 0xDD, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0xE8, 0xA4, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCB, 0xCD, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEF, 0xA8, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEE, 0xB4, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0xDA, 0xD8, 0xD1, 0xB9, 0x00, 0x00, 0xDF, 0xA9, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0xF3, 0xB0, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0xCC, 0xC4, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0xCE, 0xB7, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0xEF, 0xA9, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0xDF, 0xD5, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0xED, 0xD7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0xEE, 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0xEF, 0xBD, 0xFC, 0xD6, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xDB, 0xF4, 0x00, 0x00, 0xEF, 0xAA, 0xF8, 0xB9, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xF5, 0xE9, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0xE3, 0xD9, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0xE1, 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0xD4, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0xDE, 0xE8, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+};
+
+static unsigned char u2c_7C[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xEA, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0xF3, 0xC2, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0xD3, 0xAF, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0xCA, 0xDB, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xD7, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xED, 0xD8, 0xE1, 0xC7, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0xF4, 0xD8, 0xD6, 0xB3, 0xDD, 0xAD, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD5, 0xBE, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0xF1, 0xC3, 0xEE, 0xDF, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xD6, 0xEB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0xF4, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0xD7, 0xE6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDA, 0xB7, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDD, 0xFB, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0xDD, 0xCF, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0xD8, 0xA3, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0xDA, 0xD9, 0x00, 0x00, 0xF0, 0xD8, /* 0x94-0x97 */
+ 0xEF, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE1, 0xD8, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0xF1, 0xD4, 0x00, 0x00, 0xED, 0xF2, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0xD5, 0xDB, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0xD5, 0xDC, 0xF3, 0xC4, 0xCB, 0xD7, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xE2, 0xB6, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0xEF, 0xF1, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0xFB, 0xD5, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0xD3, 0xD8, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0xDD, 0xD0, 0xF0, 0xD9, /* 0xDC-0xDF */
+ 0xCB, 0xB3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD5, 0xDD, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCD, 0xA7, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0xD0, 0xAC, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_7D[512] = {
+ 0xD1, 0xBA, 0x00, 0x00, 0xF1, 0xC4, 0x00, 0x00, /* 0x00-0x03 */
+ 0xE5, 0xB3, 0xFB, 0xF5, 0xE9, 0xE1, 0xFD, 0xE0, /* 0x04-0x07 */
+ 0xFC, 0xBC, 0x00, 0x00, 0xDA, 0xA2, 0xDA, 0xA3, /* 0x08-0x0B */
+ 0x00, 0x00, 0xD2, 0xA1, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xD2, 0xEF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0xE2, 0xED, 0x00, 0x00, 0x00, 0x00, 0xDE, 0xE9, /* 0x14-0x17 */
+ 0xCE, 0xDC, 0xF2, 0xB5, 0xD0, 0xE4, 0xDD, 0xD1, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0xE1, 0xC8, 0xDB, 0xB7, 0xDF, 0xE3, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xED, 0xB9, /* 0x28-0x2B */
+ 0xF1, 0xC5, 0x00, 0x00, 0xF3, 0xCF, 0xD7, 0xAB, /* 0x2C-0x2F */
+ 0xE1, 0xAC, 0x00, 0x00, 0x00, 0x00, 0xE3, 0xEB, /* 0x30-0x33 */
+ 0x00, 0x00, 0xEE, 0xC7, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0xE1, 0xC9, 0xCA, 0xFA, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFB, 0xFA, 0xE1, /* 0x40-0x43 */
+ 0xF0, 0xDA, 0xCC, 0xE7, 0xDA, 0xF4, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xCC, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0xCE, 0xED, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0xD5, 0xA9, 0xFA, 0xE2, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0xD0, 0xE5, 0x00, 0x00, /* 0x64-0x67 */
+ 0xEB, 0xD6, 0x00, 0x00, 0xEC, 0xDF, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0xDF, 0xFC, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0xF7, 0xD6, 0xDE, 0xEA, 0xCB, 0xB4, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0xEF, 0xBE, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0xCC, 0xB5, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCF, 0xBD, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0xEF, 0xF2, 0xE2, 0xB7, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0xE8, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0xF0, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0xD6, 0xE0, 0x00, 0x00, 0xF1, 0xC6, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0xE2, 0xB8, 0xEB, 0xAB, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0xCB, 0xB5, 0xD8, 0xD1, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0xF4, 0xCE, 0xF3, 0xF7, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0xD7, 0xC6, 0x00, 0x00, 0xD1, 0xBB, 0xF7, 0xAA, /* 0xB8-0xBB */
+ 0x00, 0x00, 0xED, 0xCA, 0xD7, 0xD3, 0xD8, 0xFA, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF6, 0xC5, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0xD1, 0xCC, 0xDD, 0xFC, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0xDF, 0xFD, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xF9, 0xE5, 0x00, 0x00, 0xE0, 0xCA, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0xF2, 0xFD, 0xD3, 0xB0, 0x00, 0x00, /* 0xDC-0xDF */
+ 0xF4, 0xF3, 0xDA, 0xC9, 0x00, 0x00, 0xE6, 0xDE, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xF8, 0xBA, 0xE8, 0xD0, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0xD8, 0xFB, 0x00, 0x00, 0x00, 0x00, 0xEA, 0xD5, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xD6, 0xA3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF6, 0xC6, /* 0xF8-0xFB */
+};
+
+static unsigned char u2c_7E[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0xF2, 0xDB, 0xE4, 0xFC, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0xE8, 0xB2, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDA, 0xDA, /* 0x18-0x1B */
+ 0x00, 0x00, 0xF2, 0xDC, 0xFB, 0xD6, 0xE9, 0xB2, /* 0x1C-0x1F */
+ 0x00, 0x00, 0xEE, 0xAD, 0x00, 0x00, 0xFA, 0xE3, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0xEE, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0xF5, 0xEA, 0xE6, 0xE0, /* 0x2C-0x2F */
+ 0x00, 0x00, 0xF0, 0xFD, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD7, 0xAC, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0xF5, 0xC5, 0xEE, 0xE0, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0xDB, 0xE5, 0x00, 0x00, 0xDD, 0xDE, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0xD9, 0xF0, 0xE9, 0xA3, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0xF1, 0xF9, 0x00, 0x00, /* 0x50-0x53 */
+ 0xF2, 0xC4, 0xE0, 0xCB, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0xA4, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0xE2, 0xB9, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0xE3, 0xB1, 0xFC, 0xEB, 0xCD, 0xA8, /* 0x68-0x6B */
+ 0x00, 0x00, 0xCC, 0xB6, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xF0, 0xDB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0xE6, 0xBA, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0xCD, 0xA9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0xF3, 0xC3, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0xE1, 0xD9, 0x00, 0x00, 0x00, 0x00, 0xEF, 0xAB, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE7, 0xC5, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0xE9, 0x00, 0x00, /* 0x94-0x97 */
+ 0xF3, 0xC5, 0x00, 0x00, 0x00, 0x00, 0xD4, 0xC0, /* 0x98-0x9B */
+ 0xD5, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+};
+
+static unsigned char u2c_7F[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0xDD, 0xAE, 0x00, 0x00, /* 0x34-0x37 */
+ 0xF9, 0xFC, 0x00, 0x00, 0xCC, 0xC0, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0xE5, 0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xCE, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0xD8, 0xD2, 0xF9, 0xD6, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0xF1, 0xAA, 0xCE, 0xD1, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0xF6, 0xC7, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xDB, 0xEB, 0x00, 0x00, 0xDF, 0xFE, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0xD8, 0xE1, 0x00, 0x00, 0xF7, 0xF3, /* 0x74-0x77 */
+ 0x00, 0x00, 0xD7, 0xE7, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0xD4, 0xFE, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0xD1, 0xBC, 0x00, 0x00, 0xE5, 0xCF, 0x00, 0x00, /* 0x88-0x8B */
+ 0xCB, 0xB6, 0x00, 0x00, 0xDA, 0xB8, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0xCD, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0xD6, 0xBE, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0xE2, 0xBA, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0xCF, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xE0, 0xCC, 0xEB, 0xF9, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0xFD, 0xFD, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0xD7, 0xE8, 0xCB, 0xD8, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0xE9, 0xE2, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0xE8, 0xBA, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0xE3, 0xC7, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0xEC, 0xCD, 0x00, 0x00, /* 0xC8-0xCB */
+ 0xEC, 0xCE, 0x00, 0x00, 0xD6, 0xBF, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0xE3, 0xA7, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xDF, 0xD6, 0xFD, 0xE8, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEE, 0xE1, /* 0xDC-0xDF */
+ 0xF6, 0xA8, 0xDD, 0xFD, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0xF8, 0xBB, 0x00, 0x00, 0xE8, 0xD1, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0xF9, 0xD7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xCE, 0xEE, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0xEC, 0xCF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_80[512] = {
+ 0xE9, 0xA5, 0xD6, 0xD5, 0x00, 0x00, 0xCD, 0xC5, /* 0x00-0x03 */
+ 0x00, 0x00, 0xED, 0xBA, 0xD1, 0xBD, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0xCF, 0xBE, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0xEC, 0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xD2, 0xB1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0xCC, 0xE9, 0x00, 0x00, 0xD9, 0xC4, /* 0x14-0x17 */
+ 0xE9, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0xD1, 0xBE, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEC, 0xBC, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0xAD, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0xF7, 0xB0, 0x00, 0x00, 0xCC, 0xEA, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD3, 0xC4, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0xD6, 0xC0, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0xD6, 0xFD, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0xA1, 0x00, 0x00, /* 0x54-0x57 */
+ 0xDE, 0xBD, 0x00, 0x00, 0xF6, 0xA9, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0xDA, 0xA4, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD6, 0xA4, /* 0x6C-0x6F */
+ 0xF5, 0xC6, 0x00, 0x00, 0xE1, 0xA2, 0xE9, 0xC6, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF2, 0xC5, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0xF4, 0xE9, 0xD6, 0xEC, 0xEB, 0xD3, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0xEC, 0xBD, 0xE2, 0xDC, 0xDE, 0xEB, 0xF0, 0xDC, /* 0x84-0x87 */
+ 0x00, 0x00, 0xEB, 0xBF, 0x00, 0x00, 0xD7, 0xCE, /* 0x88-0x8B */
+ 0xD1, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0xF5, 0xAB, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xFD, /* 0x98-0x9B */
+ 0x00, 0x00, 0xCA, 0xDC, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0xCD, 0xC6, 0xF2, 0xB6, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0xDD, 0xFE, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0xCC, 0xB7, 0xDB, 0xB8, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD0, 0xE9, /* 0xAC-0xAF */
+ 0x00, 0x00, 0xCE, 0xDD, 0xEB, 0xC0, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0xFD, 0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0xF8, 0xCB, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEA, 0xD6, /* 0xC0-0xC3 */
+ 0xF1, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0xDB, 0xCE, 0x00, 0x00, 0xF7, 0xC3, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0xDB, 0xCF, 0xCB, 0xA4, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0xF8, 0xE0, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xFB, 0xD7, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0xEB, 0xCA, 0xE0, 0xA1, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0xCE, 0xCD, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xD4, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0xFD, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0xD2, 0xF6, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_81[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0xF2, 0xB7, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0xFA, 0xF6, 0xF6, 0xAA, 0xFA, 0xF7, /* 0x04-0x07 */
+ 0xD8, 0xE6, 0x00, 0x00, 0xF4, 0xB1, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0xE8, 0xD2, 0x00, 0x00, 0xCA, 0xC5, 0xCC, 0xEB, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2, 0xEE, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0xE2, 0xBB, 0x00, 0x00, 0xF7, 0xAD, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xE1, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0xF3, 0xEC, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0xDE, 0xA1, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0xFD, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0xE3, 0xEC, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xDD, 0xAF, 0xDD, 0xB0, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0xCB, 0xB7, 0xE8, 0xD3, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0xE1, 0xA3, 0xD2, 0xE0, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFE, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xE9, 0xA6, 0xCB, 0xF2, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0xED, 0xF3, 0xDC, 0xD9, 0xE0, 0xCD, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF7, 0xDA, /* 0x7C-0x7F */
+
+ 0xDB, 0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0xCC, 0xAE, 0x00, 0x00, 0xDA, 0xDB, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCD, 0xC7, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0xDD, 0xB1, 0x00, 0x00, /* 0x98-0x9B */
+ 0xD8, 0xAF, 0xE3, 0xA3, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0xCE, 0xEF, 0x00, 0x00, 0x00, 0x00, 0xF2, 0xF3, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xF8, 0xB3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xCE, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0xF5, 0xFD, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0xEB, 0xEC, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0xD3, 0xC5, 0xFC, 0xEC, 0xD2, 0xDB, /* 0xBC-0xBF */
+ 0xD4, 0xEB, 0x00, 0x00, 0xDE, 0xA2, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0xE6, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0xF0, 0xB0, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xD5, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xED, 0xF4, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE3, 0xED, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0xE8, 0xC2, 0x00, 0x00, 0xED, 0xF5, /* 0xE4-0xE7 */
+ 0xD7, 0xFC, 0x00, 0x00, 0xED, 0xBB, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0xF6, 0xAB, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF2, 0xB8, /* 0xF0-0xF3 */
+ 0xF6, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0xD3, 0xE6, 0xF2, 0xDD, /* 0xF8-0xFB */
+ 0xCF, 0xBF, 0x00, 0x00, 0xEB, 0xAC, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_82[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0xCF, 0xC0, 0x00, 0x00, 0xE6, 0xA8, /* 0x04-0x07 */
+ 0xFD, 0xE9, 0x00, 0x00, 0xCF, 0xC1, 0x00, 0x00, /* 0x08-0x0B */
+ 0xE0, 0xDF, 0xDE, 0xEC, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0xA2, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF4, 0xBF, /* 0x18-0x1B */
+ 0xE2, 0xEF, 0x00, 0x00, 0xD9, 0xF1, 0xF1, 0xC7, /* 0x1C-0x1F */
+ 0x00, 0x00, 0xCB, 0xB8, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xF9, 0xFE, 0xDB, 0xBA, /* 0x28-0x2B */
+ 0xDA, 0xF5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0xF6, 0xEC, 0xDA, 0xDC, 0xFA, 0xE4, /* 0x34-0x37 */
+ 0x00, 0x00, 0xE0, 0xCF, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0xDD, 0xB2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0xE6, 0xA9, 0x00, 0x00, 0xEF, 0xF3, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0xF3, 0xED, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0xEB, 0xFA, 0x00, 0x00, 0xF9, 0xE6, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0xCA, 0xDD, 0xD5, 0xDE, /* 0x6C-0x6F */
+ 0x00, 0x00, 0xCA, 0xDE, 0xDF, 0xE4, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0xE6, 0xFD, 0x00, 0x00, /* 0x74-0x77 */
+ 0xF5, 0xAC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0xE4, 0xF5, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE9, 0xE3, /* 0x88-0x8B */
+ 0x00, 0x00, 0xED, 0xCB, 0xCF, 0xE4, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0xD8, 0xD3, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0xDD, 0xB3, 0xD4, 0xEC, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0xF2, 0xB9, 0x00, 0x00, 0xDF, 0xB7, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0xCB, 0xCE, 0xFB, 0xD8, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0xD0, 0xD9, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0xDD, 0xD2, 0xF7, 0xF4, 0xE7, 0xDC, 0xE4, 0xA5, /* 0xAC-0xAF */
+ 0x00, 0x00, 0xFC, 0xA3, 0x00, 0x00, 0xDB, 0xBB, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF2, 0xBA, /* 0xB4-0xB7 */
+ 0xE9, 0xFD, 0xD0, 0xCA, 0x00, 0x00, 0xF5, 0xD6, /* 0xB8-0xBB */
+ 0xD9, 0xC5, 0xE4, 0xB4, 0x00, 0x00, 0xED, 0xA7, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0xEA, 0xBD, 0xE6, 0xFE, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xF7, 0xC4, 0xF5, 0xAD, 0x00, 0x00, 0xD9, 0xE0, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCA, 0xB4, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0xF8, 0xE2, 0xCF, 0xC2, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xEC, 0xBE, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0xE5, 0xB4, 0xCD, 0xC8, 0xEE, 0xC8, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0xE7, 0xC8, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0xCD, 0xC9, 0xF9, 0xB7, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_83[512] = {
+ 0x00, 0x00, 0xF1, 0xE8, 0xD9, 0xF2, 0xDB, 0xF5, /* 0x00-0x03 */
+ 0xCA, 0xB5, 0xD9, 0xC6, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0xD8, 0xC9, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD9, 0xAB, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0xED, 0xBC, 0x00, 0x00, 0x00, 0x00, 0xD8, 0xD4, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0xDA, /* 0x2C-0x2F */
+ 0x00, 0x00, 0xE2, 0xBC, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0xFC, 0xED, 0xEC, 0xE0, 0xD2, 0xFE, 0x00, 0x00, /* 0x34-0x37 */
+ 0xE9, 0xC7, 0xE6, 0xAA, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0xE2, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0xBB, /* 0x44-0x47 */
+ 0x00, 0x00, 0xF5, 0xAE, 0xFB, 0xAA, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEC, 0xFB, /* 0x4C-0x4F */
+ 0x00, 0x00, 0xEC, 0xBF, 0xFC, 0xD8, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD4, 0xE5, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xC3, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEE, 0xE2, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0xD7, 0xE9, 0xED, 0xF6, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0xDE, 0xED, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0xCC, 0xEC, 0x00, 0x00, /* 0x94-0x97 */
+ 0xE3, 0xEE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0xE8, 0xD4, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0xFA, 0xF8, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0xDD, 0xB4, 0xE4, 0xB5, 0xD8, 0xB0, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0xD8, 0xD5, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0xF4, 0xEA, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0xCE, 0xB9, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xD6, 0xE1, 0xCF, 0xD2, 0x00, 0x00, /* 0xC8-0xCB */
+ 0xD0, 0xB6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCE, 0xA2, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0xF3, 0xEE, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0xF3, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0xDC, 0xCC, 0x00, 0x00, 0xD0, 0xCB, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xA4, /* 0xEC-0xEF */
+ 0xCD, 0xCA, 0xD7, 0xD4, 0xDE, 0xA3, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xE4, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xEE, 0xC9, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0xE2, 0xDD, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_84[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF5, 0xFE, /* 0x00-0x03 */
+ 0xD4, 0xAC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0xD5, 0xD1, 0x00, 0x00, /* 0x08-0x0B */
+ 0xD8, 0xF0, 0xF8, 0xC3, 0xEA, 0xD7, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0xF5, 0xD7, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0xD8, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0xFD, 0xC0, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0xEB, 0xAD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0xD5, 0xAA, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0xE7, 0xA8, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEE, 0xCA, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCA, 0xE7, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0xF8, 0xE3, 0x00, 0x00, 0xD4, 0xDD, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0xEA, 0xD8, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFB, 0xD9, /* 0x68-0x6B */
+ 0xED, 0xF7, 0x00, 0x00, 0x00, 0x00, 0xE5, 0xB5, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0xD0, 0xAD, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0xF1, 0xF1, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0xE2, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0xE3, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0xD9, 0xD5, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0xDF, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0xDB, 0xBC, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0xF8, 0xE4, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0xF1, 0xFA, 0x00, 0x00, 0x00, 0x00, 0xE5, 0xB6, /* 0xB8-0xBB */
+ 0xF3, 0xEF, 0x00, 0x00, 0x00, 0x00, 0xFB, 0xDA, /* 0xBC-0xBF */
+ 0xE1, 0xE0, 0x00, 0x00, 0xD9, 0xAC, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xF5, 0xEB, 0x00, 0x00, 0xE0, 0xB6, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xE9, 0xC8, 0x00, 0x00, 0xCB, 0xCF, /* 0xC8-0xCB */
+ 0x00, 0x00, 0xE3, 0xC9, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0xDE, 0xEE, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0xE2, 0xBE, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0xDC, 0xEF, 0x00, 0x00, 0xD6, 0xA5, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xE2, 0xF1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0xD6, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_85[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0xD9, 0xA1, 0x00, 0x00, 0xD8, 0xC0, /* 0x10-0x13 */
+ 0xDC, 0xDB, 0x00, 0x00, 0x00, 0x00, 0xED, 0xBD, /* 0x14-0x17 */
+ 0xDF, 0xB8, 0x00, 0x00, 0xEA, 0xA5, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0xD7, 0xAD, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0xF3, 0xF9, 0x00, 0x00, 0xED, 0xF8, /* 0x20-0x23 */
+ 0x00, 0x00, 0xF5, 0xC7, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0xE1, 0xCA, 0xEB, 0xE3, 0x00, 0x00, 0xF2, 0xDE, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0xF8, 0xCC, 0x00, 0x00, 0xEA, 0xD9, /* 0x3C-0x3F */
+ 0x00, 0x00, 0xD3, 0xC6, 0x00, 0x00, 0xDB, 0xE6, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0xF5, 0xAF, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0xCE, 0xF0, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE9, 0xFE, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0xFB, 0xB6, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2, 0xF2, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0xCF, 0xF2, 0xF7, 0xB9, 0xD9, 0xF3, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0xE1, 0xCB, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0xDA, 0xDD, 0x00, 0x00, 0x00, 0x00, 0xDA, 0xB9, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEB, 0xFB, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xCB, 0xB9, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0xED, 0xF9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xE0, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0xF4, 0xC0, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xFD, 0xBC, 0xDF, 0xB1, 0xE3, 0xEF, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xA3, /* 0xAC-0xAF */
+ 0xFD, 0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0xF0, 0xB1, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0xCD, 0xCB, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xED, 0xBE, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0xD5, 0xC0, 0xE3, 0xF0, 0xED, 0xFA, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0xE9, 0xE4, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0xD5, 0xED, 0xE7, 0xDD, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0xD4, 0xF6, 0xE5, 0xB7, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0xDB, 0xE7, 0xE2, 0xBF, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEE, 0xCB, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0xD7, 0xF4, 0xF0, 0xDD, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCE, 0xAB, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_86[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0xE7, 0xDE, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0xD6, 0xD6, 0xE1, 0xCC, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0xE8, 0xB3, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0xEE, 0xDC, 0xA2, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0xD0, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0xD5, 0xB5, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD5, 0xA1, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0xFB, 0xDB, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xF9, 0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0xCB, 0xF3, 0xF4, 0xA5, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0xC8, /* 0x58-0x5B */
+ 0xD6, 0xD7, 0x00, 0x00, 0xE9, 0xE5, 0xFB, 0xDC, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xD0, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0xFB, 0xF6, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0xDA, 0xA5, 0x00, 0x00, /* 0x88-0x8B */
+ 0xDB, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEC, 0xE2, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCD, 0xF7, /* 0xA0-0xA3 */
+ 0xF0, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0xF6, 0xC9, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDE, 0xEF, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD3, 0xB1, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xFC, 0xEE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0xE8, 0xC3, 0x00, 0x00, 0xF1, 0xC8, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCE, 0xF1, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0xF9, 0xED, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0xF2, 0xF4, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0xE4, 0xB6, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_87[512] = {
+ 0xF5, 0xB9, 0x00, 0x00, 0xDC, 0xF0, 0xE3, 0xF1, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0xE8, 0xA5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0xF2, 0xBB, 0x00, 0x00, 0xDE, 0xA4, 0x00, 0x00, /* 0x18-0x1B */
+ 0xDA, 0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0xCA, 0xE9, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0xE3, 0xDA, 0x00, 0x00, 0xFC, 0xD9, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEA, 0xDA, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0xF9, 0xC4, 0x00, 0x00, /* 0x64-0x67 */
+ 0xE3, 0xA4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0xFB, 0xDD, 0x00, 0x00, 0xEF, 0xCA, 0x00, 0x00, /* 0x74-0x77 */
+ 0xE8, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0xD5, 0xCC, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0xEB, 0xD7, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD9, 0xAD, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0xFB, 0xAB, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD3, 0xD9, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0xD5, 0xA2, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xF6, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0xDA, 0xF6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0xE0, 0xD1, 0x00, 0x00, 0x00, 0x00, 0xE9, 0xA8, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0xF5, 0xF9, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xFA, 0xAF, 0x00, 0x00, 0xEB, 0xFC, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0xEA, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_88[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0xE3, 0xB2, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD5, 0xC5, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0xF1, 0xE3, 0xD5, 0xEE, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0xCD, 0xCC, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0xED, 0xD9, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD8, 0xC1, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0xFA, 0xEC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0xF1, 0xEB, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0xFA, 0xBC, 0xE6, 0xE2, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0xFA, 0xE5, 0xE2, 0xFA, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCA, 0xB6, /* 0x54-0x57 */
+ 0x00, 0x00, 0xE4, 0xB7, 0x00, 0x00, 0xEA, 0xDB, /* 0x58-0x5B */
+ 0x00, 0x00, 0xF5, 0xFA, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0xFB, 0xAC, 0xCF, 0xC3, 0xEB, 0xFD, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0xF8, 0xFA, 0x00, 0x00, 0x00, 0x00, 0xDF, 0xB9, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xE1, 0xF1, 0x00, 0x00, 0xD2, 0xA4, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF5, 0xFB, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0xD0, 0xDA, 0xD0, 0xDB, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0xEA, 0xBE, 0xD9, 0xB1, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0xCA, 0xB7, 0x00, 0x00, 0x00, 0x00, 0xD3, 0xE7, /* 0x88-0x8B */
+ 0x00, 0x00, 0xF8, 0xE5, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0xD3, 0xB2, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0xE2, 0xC0, 0xF2, 0xDF, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0xCD, 0xE5, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xAC, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0xCD, 0xCD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0xEE, 0xAE, 0xD6, 0xAE, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD7, 0xEA, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xE7, 0xE0, 0xEB, 0xAE, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0xCF, 0xD9, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0xDC, 0xCD, 0xED, 0xFB, 0x00, 0x00, 0xDE, 0xF0, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xD7, 0xEB, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xDE, 0xA5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDF, 0xD7, /* 0xF0-0xF3 */
+ 0xDB, 0xD0, 0xDB, 0xD1, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0xD5, 0xA3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0xF0, 0xB2, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_89[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0xDC, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xCA, 0xE8, 0x00, 0x00, 0xF8, 0xE6, 0xDC, 0xCE, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0xEA, 0xDC, 0xDB, 0xD2, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0xE9, 0xB3, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xF7, 0xDB, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0xE3, 0xA8, 0x00, 0x00, /* 0x34-0x37 */
+ 0xD7, 0xAE, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xE1, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0xCB, 0xBA, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0xE5, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD0, 0xDC, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0xD5, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0xD8, 0xCA, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0xE3, 0xA9, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xA4, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0xE9, 0xA9, 0x00, 0x00, 0xD3, 0xC7, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0xDC, 0xDD, 0xF8, 0xAE, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0xB8, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD0, 0xAE, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD8, 0xF2, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0xE3, 0xCA, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0xCC, 0xAF, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0xD4, 0xAD, 0xF6, 0xD1, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0xD0, 0xCC, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0xCA, 0xC6, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0xD5, 0xC2, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0xCE, 0xBA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0xCA, 0xC7, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0xB0, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xDF, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0xF5, 0xBA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+};
+
+static unsigned char u2c_8A[512] = {
+ 0xE5, 0xEB, 0x00, 0x00, 0xEF, 0xF4, 0xDD, 0xB5, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0xCD, 0xAA, 0x00, 0x00, 0xE3, 0xF2, 0x00, 0x00, /* 0x08-0x0B */
+ 0xFB, 0xF7, 0x00, 0x00, 0xF7, 0xD0, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xBA, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0xFD, 0xE1, 0xF6, 0xFE, /* 0x14-0x17 */
+ 0xD1, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xE8, 0xC5, /* 0x18-0x1B */
+ 0x00, 0x00, 0xE4, 0xB8, 0x00, 0x00, 0xE1, 0xE8, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0xC1, /* 0x20-0x23 */
+ 0x00, 0x00, 0xD2, 0xED, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xDB, 0xBE, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0xE0, 0xE2, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0xFA, 0xC9, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0xE1, 0xCD, 0x00, 0x00, 0xCA, 0xB8, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0xF2, 0xE0, 0xF1, 0xC9, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xDE, 0xF1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0xF0, 0xDF, 0xF8, 0xC4, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEE, 0xCC, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0xDE, 0xF2, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xE7, 0xC9, 0x00, 0x00, 0xE2, 0xF3, 0xE7, 0xE1, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0xE3, 0xCB, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0xE3, 0xCC, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0xCF, 0xF8, 0xEF, 0xAC, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xFD, 0xFE, 0xFC, 0xA5, 0xFA, 0xB1, 0xDF, 0xD9, /* 0x70-0x73 */
+ 0x00, 0x00, 0xE0, 0xD2, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0xF4, 0xDA, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0xF1, 0xCA, 0x00, 0x00, 0xCE, 0xA3, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0xF2, 0xBC, 0xEC, 0xE3, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xA5, /* 0x90-0x93 */
+ 0x00, 0x00, 0xF7, 0xAB, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0xEB, 0xAF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0xDE, 0x00, 0x00, /* 0x9C-0x9F */
+ 0xE1, 0xA4, 0xCD, 0xAB, 0x00, 0x00, 0xD9, 0xF4, /* 0xA0-0xA3 */
+ 0xE8, 0xA6, 0xCD, 0xCE, 0xE1, 0xE9, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xFC, 0xEF, 0x00, 0x00, 0xE0, 0xE3, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0xE2, 0xC1, 0x00, 0x00, 0xCE, 0xA4, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xDE, 0xA6, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0xEB, 0xFE, 0x00, 0x00, 0xEB, 0xDD, 0xF0, 0xE0, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0xF4, 0xDB, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xE2, 0xF4, 0x00, 0x00, 0x00, 0x00, 0xD3, 0xC8, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF4, 0xEB, /* 0xC8-0xCB */
+ 0x00, 0x00, 0xEE, 0xB5, 0x00, 0x00, 0xF5, 0xD8, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0xD5, 0xDF, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0xD6, 0xE5, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEB, 0xB0, /* 0xD8-0xDB */
+ 0xF4, 0xE3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xE3, 0xCD, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0xF4, 0xF4, 0xFA, 0xB2, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0xEF, 0xF5, 0xCA, 0xDF, /* 0xE8-0xEB */
+ 0x00, 0x00, 0xEB, 0xB1, 0xED, 0xBF, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0xFD, 0xC9, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0xE4, 0xA6, 0xF9, 0xA4, /* 0xF4-0xF7 */
+ 0xF0, 0xB3, 0x00, 0x00, 0xE5, 0xEC, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0xD1, 0xE7, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_8B[512] = {
+ 0xD9, 0xC7, 0xE4, 0xD7, 0xEA, 0xDD, 0x00, 0x00, /* 0x00-0x03 */
+ 0xD4, 0xF7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0xDA, 0xBA, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xDA, 0xCD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0xF9, 0xCC, 0x00, 0x00, 0xE1, 0xDA, 0xDB, 0xBF, /* 0x14-0x17 */
+ 0x00, 0x00, 0xCC, 0xC5, 0xEC, 0xD0, 0xCB, 0xBB, /* 0x18-0x1B */
+ 0x00, 0x00, 0xDE, 0xF3, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0xE9, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0xD9, 0xC8, 0x00, 0x00, 0x00, 0x00, 0xEE, 0xE3, /* 0x28-0x2B */
+ 0xD7, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCF, 0xC4, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0xD0, 0xCD, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0xFC, 0xA6, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0xF1, 0xFB, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0xFD, 0xD2, 0xD1, 0xC1, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0xE3, 0xDB, 0x00, 0x00, 0xD3, 0xC9, 0x00, 0x00, /* 0x58-0x5B */
+ 0xDC, 0xCF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0xCC, 0xED, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0xDE, 0xA7, 0x00, 0x00, 0x00, 0x00, 0xE6, 0xBB, /* 0x6C-0x6F */
+ 0xEC, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0xCC, 0xB9, 0x00, 0x00, 0x00, 0x00, 0xFB, 0xDE, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0xE7, 0xE2, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0xD4, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0xDC, 0xA8, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0xE2, 0xC2, 0x00, 0x00, 0xF3, 0xD8, 0xE5, 0xD3, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0xF3, 0xD9, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0xF3, 0xC6, 0x00, 0x00, /* 0x98-0x9B */
+};
+
+static unsigned char u2c_8C[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCD, 0xDB, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCD, 0xAC, /* 0x3C-0x3F */
+ 0x00, 0x00, 0xFC, 0xC3, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0xD4, 0xE7, 0x00, 0x00, /* 0x44-0x47 */
+ 0xD1, 0xC2, 0x00, 0x00, 0xF9, 0xA5, 0x00, 0x00, /* 0x48-0x4B */
+ 0xE8, 0xD5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0xE3, 0xCE, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0xD4, 0xCA, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0xDF, 0xDA, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0xFB, 0xDF, 0xE7, 0xE3, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0xF8, 0xFB, 0xE3, 0xCF, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0xF5, 0xB0, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0xD8, 0xE7, 0x00, 0x00, /* 0x88-0x8B */
+ 0xD9, 0xC9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0xF8, 0xAF, 0xEF, 0xF6, 0x00, 0x00, /* 0x9C-0x9F */
+ 0xDD, 0xB6, 0xEE, 0xAF, 0xCD, 0xF8, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDE, 0xB8, /* 0xA4-0xA7 */
+ 0xFC, 0xA7, 0xF7, 0xFC, 0xF7, 0xB1, 0xCE, 0xBB, /* 0xA8-0xAB */
+ 0xF4, 0xA1, 0x00, 0x00, 0x00, 0x00, 0xEE, 0xCD, /* 0xAC-0xAF */
+ 0xE1, 0xAE, 0x00, 0x00, 0x00, 0x00, 0xEC, 0xC3, /* 0xB0-0xB3 */
+ 0xCF, 0xFE, 0x00, 0x00, 0xF8, 0xBF, 0xD8, 0xE2, /* 0xB4-0xB7 */
+ 0xD3, 0xE8, 0x00, 0x00, 0x00, 0x00, 0xDE, 0xA8, /* 0xB8-0xBB */
+ 0xF4, 0xE4, 0xEC, 0xC2, 0x00, 0x00, 0xD9, 0xF5, /* 0xBC-0xBF */
+ 0xF9, 0xC5, 0xDD, 0xD3, 0xD6, 0xF1, 0xEC, 0xFC, /* 0xC0-0xC3 */
+ 0xFC, 0xF0, 0x00, 0x00, 0x00, 0x00, 0xED, 0xC0, /* 0xC4-0xC7 */
+ 0xCA, 0xB9, 0x00, 0x00, 0xEE, 0xE4, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0xF2, 0xE1, 0x00, 0x00, 0xDE, 0xB9, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0xD6, 0xF2, 0x00, 0x00, /* 0xD8-0xDB */
+ 0xDE, 0xF4, 0x00, 0x00, 0xDF, 0xDB, 0x00, 0x00, /* 0xDC-0xDF */
+ 0xDB, 0xD3, 0x00, 0x00, 0xFA, 0xE7, 0xD8, 0xE3, /* 0xE0-0xE3 */
+ 0xF4, 0xC1, 0x00, 0x00, 0xDD, 0xB7, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0xF2, 0xF5, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0xD4, 0xAE, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xD6, 0xF3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDD, 0xB8, /* 0xF8-0xFB */
+ 0xCF, 0xC5, 0xDF, 0xDF, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_8D[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0xF2, 0xBE, 0xF6, 0xA1, 0x00, 0x00, 0xEB, 0xCB, /* 0x04-0x07 */
+ 0xF1, 0xFC, 0x00, 0x00, 0xF3, 0xC7, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0xE0, 0xEB, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xED, 0xFC, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0xDB, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0xEE, 0xE5, 0x00, 0x00, 0xDE, 0xF5, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0xD3, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xF1, 0xCB, 0x00, 0x00, 0x00, 0x00, 0xD0, 0xAF, /* 0x70-0x73 */
+ 0xDD, 0xB9, 0x00, 0x00, 0x00, 0x00, 0xD1, 0xC3, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0xF5, 0xB1, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0xEA, 0xC6, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0xF0, 0xE1, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF6, 0xAC, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xF5, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xEB, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0xDD, 0xBA, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0xF2, 0xBF, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0xF7, 0xC5, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDB, 0xA2, /* 0xC8-0xCB */
+ 0xF2, 0xF6, 0x00, 0x00, 0x00, 0x00, 0xCA, 0xBA, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF7, 0xF5, /* 0xD8-0xDB */
+ 0x00, 0x00, 0xCB, 0xE5, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xEE, 0xE6, 0x00, 0x00, 0xE0, 0xD3, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xCE, 0xA5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD6, 0xD8, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD4, 0xAF, /* 0xF0-0xF3 */
+};
+
+static unsigned char u2c_8E[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0xC9, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD3, 0xCE, /* 0x0C-0x0F */
+ 0xF4, 0xC2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0xCB, 0xE6, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xF1, 0xA1, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xEB, 0xB2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0xF1, 0xA2, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0xEB, 0xB3, 0x00, 0x00, /* 0x40-0x43 */
+ 0xF0, 0xB4, 0x00, 0x00, 0x00, 0x00, 0xCB, 0xF4, /* 0x44-0x47 */
+ 0xD4, 0xB0, 0xF3, 0xB2, 0xFB, 0xB7, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0xF5, 0xEC, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEE, 0xE7, /* 0x5C-0x5F */
+ 0xF4, 0xB2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0xF5, 0xED, 0x00, 0x00, 0xCF, 0xF3, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0xF0, 0xE2, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEE, 0xCE, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0xF1, 0xCC, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0xE5, 0xB8, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0xD7, 0xF5, 0xE3, 0xF3, /* 0xA8-0xAB */
+ 0xCF, 0xE5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0xCF, 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0xF3, 0xB3, 0xE4, 0xD8, /* 0xC8-0xCB */
+ 0xCF, 0xF9, 0xCF, 0xDA, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0xFA, 0xCD, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE6, 0xE3, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF2, 0xE2, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0xF5, 0xEE, 0x00, 0x00, 0x00, 0x00, 0xCA, 0xBB, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0xE3, 0xDC, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_8F[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCE, 0xF2, /* 0x00-0x03 */
+ 0x00, 0x00, 0xD6, 0xD9, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0xEE, 0xB0, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0xF4, 0xE5, 0xD8, 0xC2, /* 0x10-0x13 */
+ 0xDC, 0xD0, 0xCC, 0xEE, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD5, 0xE0, /* 0x18-0x1B */
+ 0xF6, 0xCA, 0xFD, 0xCA, 0xD8, 0xD6, 0xF4, 0xCF, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0xD6, 0xA6, 0xDC, 0xBE, /* 0x24-0x27 */
+ 0x00, 0x00, 0xDB, 0xD4, 0xD7, 0xC7, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF2, 0xFE, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF1, 0xCD, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0xE2, 0xC3, 0xDC, 0xDE, 0x00, 0x00, 0xDC, 0xDF, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0xEF, 0xAD, 0xE6, 0xAB, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0xF9, 0xDD, 0xEA, 0xBF, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0xEF, 0xAE, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0xF4, 0xD0, 0xCE, 0xF3, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0xE6, 0xAC, 0x00, 0x00, 0xCE, 0xDE, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0xD5, 0xF9, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE3, 0xF4, /* 0x98-0x9B */
+ 0xCD, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD5, 0xB8, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0xF7, 0xFD, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xDC, 0xA9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0xDE, 0xF6, 0x00, 0x00, 0xDC, 0xAA, /* 0xAC-0xAF */
+ 0xF2, 0xE3, 0xE9, 0xB4, 0xD2, 0xDC, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0xE6, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0xE3, 0xF6, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0xE7, 0xCA, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0xD0, 0xCE, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xDA, 0xF7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0xCA, 0xBC, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0xEE, 0xE8, 0xDA, 0xDE, /* 0xE8-0xEB */
+ 0x00, 0x00, 0xF2, 0xF7, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0xE2, 0xFB, 0x00, 0x00, 0xCC, 0xA6, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDA, 0xBB, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xEE, 0xE9, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0xF5, 0xDA, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_90[512] = {
+ 0xF7, 0xDC, 0xE1, 0xEA, 0xCE, 0xC1, 0xD4, 0xB1, /* 0x00-0x03 */
+ 0x00, 0x00, 0xFD, 0xB1, 0xE6, 0xBD, 0x00, 0x00, /* 0x04-0x07 */
+ 0xFB, 0xAD, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xE7, /* 0x08-0x0B */
+ 0x00, 0x00, 0xE1, 0xCE, 0x00, 0x00, 0xF7, 0xE2, /* 0x0C-0x0F */
+ 0xF5, 0xEF, 0xCF, 0xC7, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0xD4, 0xB2, 0xCC, 0xEF, 0x00, 0x00, 0xD4, 0xE8, /* 0x14-0x17 */
+ 0x00, 0x00, 0xEE, 0xCF, 0xF7, 0xD7, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0xE0, 0xA6, 0xD6, 0xC1, 0xE1, 0xDC, /* 0x1C-0x1F */
+ 0xF0, 0xE3, 0xF1, 0xE4, 0xDC, 0xF1, 0xD6, 0xA7, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0xF4, 0xF5, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0xF1, 0xCE, 0xF2, 0xE4, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0xD0, 0xB0, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0xEC, 0xEF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0xF9, 0xBA, 0x00, 0x00, 0xEB, 0xB5, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0xD4, 0xED, 0xE2, 0xC4, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE9, 0xE7, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0xEB, 0xB4, 0xEA, 0xA1, /* 0x48-0x4B */
+ 0x00, 0x00, 0xF8, 0xBC, 0xCE, 0xA6, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xF9, 0xC6, 0xFC, 0xDA, 0x00, 0x00, 0xD4, 0xB3, /* 0x50-0x53 */
+ 0xD3, 0xB9, 0xEA, 0xDE, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0xE9, 0xAB, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0xE1, 0xE1, 0xD3, 0xCF, 0xF4, 0xF6, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xEA, 0xC0, 0xE1, 0xCF, 0x00, 0x00, 0xCC, 0xBA, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0xEE, 0xEA, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0xF0, 0xE4, 0xF3, 0xB4, 0xD4, 0xEE, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0xF2, 0xC0, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0xF1, 0xE5, 0x00, 0x00, 0xF4, 0xC3, /* 0x74-0x77 */
+ 0xE0, 0xD4, 0x00, 0x00, 0xEB, 0xB6, 0x00, 0x00, /* 0x78-0x7B */
+ 0xD7, 0xA1, 0xCB, 0xE8, 0x00, 0x00, 0xF9, 0xAD, /* 0x7C-0x7F */
+
+ 0xE9, 0xAD, 0xD8, 0xE4, 0xFA, 0xB3, 0xE2, 0xC5, /* 0x80-0x83 */
+ 0xFC, 0xBD, 0x00, 0x00, 0x00, 0x00, 0xEC, 0xC4, /* 0x84-0x87 */
+ 0xD8, 0xB1, 0x00, 0x00, 0xDC, 0xAB, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD5, 0xA4, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xEB, 0xE9, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0xE8, 0xBB, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0xD8, 0xD7, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0xFB, 0xAE, 0xD1, 0xE1, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0xDB, 0xC0, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xF5, 0xBE, 0x00, 0x00, 0xDE, 0xF7, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCA, 0xFB, /* 0xAC-0xAF */
+ 0xF7, 0xC6, 0xCF, 0xC8, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0xE1, 0xD0, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0xEE, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0xE9, 0xF4, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0xCE, 0xF4, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0xD5, 0xCD, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xCF, 0xDB, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xDD, 0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0xCE, 0xAC, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0xE9, 0xE8, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0xD4, 0xB4, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_91[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0xE4, 0xC7, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0xF5, 0xDB, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0xFA, 0xC1, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0xDE, 0xA9, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD4, 0xF8, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0xEF, 0xF7, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0xD3, 0xB3, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0xEB, 0xB7, 0xEF, 0xF8, 0xF5, 0xDC, /* 0x48-0x4B */
+ 0xED, 0xCC, 0xDB, 0xD5, 0xF1, 0xCF, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0xF1, 0xD0, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0xF5, 0xB2, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0xD9, 0xAE, 0xD5, 0xAC, 0x00, 0x00, /* 0x68-0x6B */
+ 0xE2, 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0xFD, 0xA3, 0x00, 0x00, 0xFB, 0xE5, /* 0x74-0x77 */
+ 0xDF, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2, 0xF5, /* 0x84-0x87 */
+ 0x00, 0x00, 0xF6, 0xAD, 0x00, 0x00, 0xF5, 0xB3, /* 0x88-0x8B */
+ 0x00, 0x00, 0xF0, 0xB5, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0xA5, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0xF5, 0xDD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEC, 0xA2, /* 0xA8-0xAB */
+ 0xED, 0xFD, 0x00, 0x00, 0xF5, 0xB4, 0xFB, 0xB8, /* 0xAC-0xAF */
+ 0x00, 0x00, 0xDB, 0xA3, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0xD6, 0xCA, 0xCB, 0xD9, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0xE5, 0xD4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF3, 0xFA, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xEB, 0xB8, 0x00, 0x00, 0xE0, 0xB7, /* 0xC8-0xCB */
+ 0xD7, 0xEC, 0xF1, 0xEC, 0xE5, 0xAF, 0xD5, 0xE1, /* 0xCC-0xCF */
+ 0xD7, 0xED, 0xD1, 0xD1, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE1, 0xF2, /* 0xD4-0xD7 */
+ 0xEF, 0xF9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0xDD, 0xBC, 0xF6, 0xDC, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xE5, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF4, 0xC4, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0xE9, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0xF3, 0xFB, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+};
+
+static unsigned char u2c_92[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0xD4, 0xEF, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xCC, 0xA2, 0xF7, 0xFE, 0xDF, 0xBC, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEB, 0xCD, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0xD0, 0xB7, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0xD6, 0xC2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0xE8, 0xAD, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEF, 0xAF, /* 0x3C-0x3F */
+ 0xCB, 0xA5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0xCB, 0xE9, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0xFA, 0xE8, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0xC6, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE6, 0xE7, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0xEA, 0xC7, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0xDB, 0xA4, 0x00, 0x00, /* 0x60-0x63 */
+ 0xCF, 0xC9, 0xE2, 0xFC, 0xEF, 0xFA, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0xEB, 0xDE, 0x00, 0x00, 0x00, 0x00, 0xF5, 0xC8, /* 0x80-0x83 */
+ 0x00, 0x00, 0xD4, 0xDE, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xE0, 0xD5, 0x00, 0x00, 0xEF, 0xB0, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0xE2, 0xC7, 0x00, 0x00, /* 0x94-0x97 */
+ 0xD9, 0xAF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0xF9, 0xE7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE7, 0xE5, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0xCF, 0xCA, 0xE1, 0xD1, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xE2, 0xC8, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0xEF, 0xFB, 0x00, 0x00, 0x00, 0x00, 0xFA, 0xF9, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0xDC, 0xF2, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0xE0, 0xA7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0xF8, 0xE8, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0xCB, 0xEA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0xCB, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_93[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0xD6, 0xE2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xF5, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0xF5, 0xDF, 0x00, 0x00, 0xEE, 0xB6, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0xE2, 0xF6, 0xD3, 0xCA, /* 0x1C-0x1F */
+ 0xEF, 0xFC, 0xD1, 0xC4, 0xEF, 0xB1, 0x00, 0x00, /* 0x20-0x23 */
+ 0xD1, 0xC5, 0x00, 0x00, 0xD0, 0xDE, 0x00, 0x00, /* 0x24-0x27 */
+ 0xD9, 0xE1, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xB8, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0xCD, 0xD1, 0xF3, 0xB9, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0xE7, 0xCC, 0x00, 0x00, 0xD6, 0xA8, 0xCE, 0xA7, /* 0x48-0x4B */
+ 0x00, 0x00, 0xD4, 0xB5, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0xE4, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD3, 0xB4, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0xEB, 0xB9, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0xCB, 0xF5, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0xF6, 0xDD, 0x00, 0x00, 0xF1, 0xA3, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0xCC, 0xC7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0xE9, 0xCA, 0x00, 0x00, 0xE1, 0xF0, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0xF5, 0xE0, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFB, 0xAF, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCB, 0xD1, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0xFB, 0xE0, 0xF2, 0xE5, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0xEC, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xEC, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0xEE, 0xEB, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0xCB, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xCC, 0xF0, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0xD7, 0xAF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0xF3, 0xA1, 0x00, 0x00, /* 0xF4-0xF7 */
+};
+
+static unsigned char u2c_94[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0xFC, 0xF5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0xF1, 0xA4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0xE0, 0xD6, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEF, 0xB2, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0xF4, 0xD1, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0xF7, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0xF1, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0xCA, 0xFC, 0xCA, 0xFD, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCE, 0xCE, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0xF3, 0xC8, 0x00, 0x00, 0xF3, 0xBA, /* 0x7C-0x7F */
+};
+
+static unsigned char u2c_95[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xED, 0xFE, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0xDA, 0xA6, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xEC, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0xF8, 0xCD, 0x00, 0x00, 0xCB, 0xD2, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEB, 0xCE, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xF9, 0xD8, 0xF9, 0xD9, 0xCA, 0xE0, /* 0x90-0x93 */
+ 0xDA, 0xCA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0xCB, 0xA6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCA, 0xC8, /* 0xA0-0xA3 */
+ 0xF9, 0xEE, 0xDB, 0xEC, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xD0, 0xB1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0xD5, 0xEF, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0xE6, 0xF3, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE7, 0xA2, /* 0xB8-0xBB */
+ 0xE4, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0xE1, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0xFC, 0xC4, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xF9, 0xEF, 0xCF, 0xF4, 0xF7, 0xE6, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0xCE, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xF4, 0xC5, 0xDC, 0xA3, 0x00, 0x00, /* 0xE0-0xE3 */
+};
+
+static unsigned char u2c_96[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0xDD, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0xF4, 0xC6, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xF8, 0xA1, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0xE8, 0xD6, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0xDB, 0xC1, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xE6, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0xB9, /* 0x3C-0x3F */
+ 0xF6, 0xED, 0x00, 0x00, 0xF9, 0xAE, 0x00, 0x00, /* 0x40-0x43 */
+ 0xDD, 0xBE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD7, 0xB0, /* 0x48-0x4B */
+ 0xD8, 0xE8, 0xCB, 0xBD, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xF9, 0xDA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xCE, /* 0x58-0x5B */
+ 0xF9, 0xF0, 0xE0, 0xED, 0xE3, 0xB3, 0xF4, 0xB3, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0xEA, 0xC2, 0xF2, 0xE6, /* 0x60-0x63 */
+ 0xF0, 0xB6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0xDB, 0xD6, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xEB, 0xE4, 0x00, 0x00, 0x00, 0x00, 0xF2, 0xE7, /* 0x70-0x73 */
+ 0x00, 0x00, 0xD7, 0xD5, 0xD4, 0xB6, 0xF9, 0xE8, /* 0x74-0x77 */
+ 0xD7, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0xE5, 0xD5, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0xE9, 0xEA, 0xD7, 0xCC, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0xD3, 0xE9, 0xE2, 0xC9, /* 0x88-0x8B */
+ 0x00, 0x00, 0xFC, 0xDB, 0xCD, 0xAD, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0xCC, 0xB0, 0xEA, 0xA2, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0xE4, 0xF6, 0xD0, 0xC0, 0x00, 0x00, 0xF0, 0xB7, /* 0x98-0x9B */
+ 0xEE, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD7, 0xF6, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2, 0xCA, /* 0xA4-0xA7 */
+ 0xE2, 0xCB, 0x00, 0x00, 0xFA, 0xCF, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0xEB, 0xDF, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD6, 0xCB, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF4, 0xB4, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0xED, 0xCD, 0xE4, 0xD2, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xEA, 0xA9, 0xE4, 0xBA, 0xF3, 0xA2, 0xCD, 0xD2, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xF6, 0xCB, 0x00, 0x00, 0xF1, 0xE6, /* 0xC8-0xCB */
+ 0xED, 0xC1, 0xE8, 0xBC, 0xEE, 0xD1, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0xF0, 0xE7, 0xE2, 0xCC, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0xE4, 0xAA, 0x00, 0x00, 0xF5, 0xE1, /* 0xD8-0xDB */
+ 0xED, 0xDA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0xD7, 0xEE, 0xD1, 0xF1, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xE9, 0xEB, 0xE9, 0xEC, 0xE0, 0xE4, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDA, 0xA7, /* 0xEC-0xEF */
+ 0xDD, 0xD4, 0x00, 0x00, 0xEA, 0xA3, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0xD6, 0xC3, 0xD6, 0xF4, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xDA, 0xDF, 0x00, 0x00, 0xEF, 0xB3, /* 0xF8-0xFB */
+};
+
+static unsigned char u2c_97[512] = {
+ 0xE2, 0xCD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0xEF, 0xFD, 0xF2, 0xE8, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0xEF, 0xC5, 0x00, 0x00, 0xE7, 0xE7, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0xD7, 0xFD, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0xE7, 0xCE, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0xDF, 0xDC, 0x00, 0x00, 0xF9, 0xC7, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD9, 0xF6, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xDF, 0xAC, 0x00, 0x00, 0xD6, 0xDA, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0xDC, 0xA4, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0xF0, 0xB8, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0xD5, 0xFA, 0x00, 0x00, /* 0x40-0x43 */
+ 0xE4, 0xF7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0xD6, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0xF4, 0xEC, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0xEF, 0xFE, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0xF0, 0xA1, 0x00, 0x00, 0xDE, 0xAA, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0xDA, 0xBC, 0xD8, 0xFC, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0xFA, 0xD4, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0xEC, 0xE5, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0xFC, 0xA8, 0x00, 0x00, 0x00, 0x00, 0xEC, 0xE6, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0xD8, 0xCB, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFB, 0xB9, /* 0x88-0x8B */
+ 0x00, 0x00, 0xE4, 0xD3, 0x00, 0x00, 0xCD, 0xF9, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0xCF, 0xD3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xCA, 0xEA, 0x00, 0x00, 0x00, 0x00, 0xCF, 0xD4, /* 0xA8-0xAB */
+ 0x00, 0x00, 0xF8, 0xBD, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0xF4, 0xC7, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEA, 0xDF, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xDB, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0xD4, 0xB7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEB, 0xE5, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0xD2, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEA, 0xA4, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0xC2, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_98[512] = {
+ 0xFB, 0xE1, 0xFA, 0xED, 0xF0, 0xA2, 0xCC, 0xF1, /* 0x00-0x03 */
+ 0x00, 0x00, 0xFA, 0xA3, 0xE2, 0xF7, 0x00, 0x00, /* 0x04-0x07 */
+ 0xE2, 0xCE, 0x00, 0x00, 0xE9, 0xF5, 0x00, 0x00, /* 0x08-0x0B */
+ 0xE1, 0xEB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xE7, 0xE8, 0xE8, 0xD7, 0xDA, 0xF8, 0xD4, 0xCB, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF7, 0xF6, /* 0x14-0x17 */
+ 0xD6, 0xC5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0xD4, 0xE9, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xFA, 0xFA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0xCC, 0xF2, 0xF7, 0xDD, 0x00, 0x00, 0xDE, 0xBA, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0xCE, 0xA8, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0xF0, 0xB9, 0xE4, 0xFE, 0xE4, 0xC9, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0xE4, 0xD4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0xEA, 0xC3, 0x00, 0x00, 0xEF, 0xB4, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0xD7, 0xBE, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0xFB, 0xE2, 0x00, 0x00, 0xCD, 0xD3, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEF, 0xB5, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0xE9, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xF9, 0xA6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDF, 0xBD, /* 0xAC-0xAF */
+ 0x00, 0x00, 0xF7, 0xC7, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xF8, 0xFD, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFC, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDE, 0xAB, /* 0xD8-0xDB */
+ 0xDB, 0xE8, 0x00, 0x00, 0x00, 0x00, 0xE3, 0xDD, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xE1, 0xE2, 0xD1, 0xC6, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0xF6, 0xD0, 0xEB, 0xE6, 0xDA, 0xF9, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xEC, 0xC7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0xDE, 0xF8, 0xF8, 0xE9, 0xE3, 0xDE, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_99[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCE, 0xF5, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0xFA, 0xC3, 0xE5, 0xD7, 0x00, 0x00, /* 0x08-0x0B */
+ 0xEC, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xF3, 0xC9, 0x00, 0x00, 0x00, 0x00, 0xE4, 0xBB, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0xE6, 0xAE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0xEF, 0xB6, 0x00, 0x00, /* 0x1C-0x1F */
+ 0xDC, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0xCE, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0xD8, 0xC3, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0xD0, 0xCF, 0x00, 0x00, 0xCF, 0xFA, /* 0x48-0x4B */
+ 0xF3, 0xCA, 0xE0, 0xD7, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0xD1, 0xC7, 0xE9, 0xAE, 0x00, 0x00, /* 0x50-0x53 */
+ 0xE8, 0xBD, 0x00, 0x00, 0x00, 0x00, 0xFA, 0xC4, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0xE2, 0xCF, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0xFA, 0xC5, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0xF9, 0xB8, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0xDC, 0xE0, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xFB, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0xD8, 0xA9, 0xE5, 0xDF, 0xF9, 0xA7, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0xF6, 0xEE, 0x00, 0x00, 0xF6, 0xCC, /* 0xB0-0xB3 */
+ 0xE2, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xEC, 0xF1, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0xDA, 0xE0, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0xF1, 0xD2, 0xD2, 0xCC, 0xCF, 0xCB, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0xCA, 0xBD, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0xDD, 0xBF, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0xF6, 0xEF, 0x00, 0x00, 0xDE, 0xF9, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0xFA, 0xB4, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0xD5, 0xAD, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF1, 0xE7, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_9A[512] = {
+ 0x00, 0x00, 0xDE, 0xBE, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0xDC, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0xD1, 0xC8, 0xD1, 0xC9, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0xF8, 0xBE, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCB, 0xF6, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xD4, 0xF9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0xF5, 0xE2, 0xE1, 0xD3, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0xD8, 0xE9, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFE, /* 0x40-0x43 */
+ 0x00, 0x00, 0xCF, 0xCC, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0xFD, 0xA4, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0xCE, 0xF6, 0x00, 0x00, 0xFA, 0xD0, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0xCC, 0xF3, 0xE6, 0xBE, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF6, 0xAE, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0xD5, 0xF0, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0xD1, 0xCA, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0xFC, 0xBE, 0xD5, 0xF1, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xCD, 0xE9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0xFA, 0xB5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2, 0xD0, /* 0xD0-0xD3 */
+ 0xF4, 0xF7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xCD, 0xD4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0xE7, 0xA3, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0xDB, 0xA5, 0x00, 0x00, /* 0xEC-0xEF */
+};
+
+static unsigned char u2c_9B[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0xE2, 0xD1, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD7, 0xA2, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xF7, 0xE3, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0xEA, 0xA6, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0xD0, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0xCE, 0xDA, 0xFB, 0xEB, 0xDB, 0xA6, /* 0x40-0x43 */
+ 0xDB, 0xDE, 0xD8, 0xE5, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEA, 0xE0, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0xD8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0xE0, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD6, 0xDB, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0xEF, 0xC6, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xF8, 0xEA, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0xD5, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCE, 0xF7, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0xD8, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xD7, 0xEF, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0xF4, 0xED, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0xCD, 0xE6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xCC, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+};
+
+static unsigned char u2c_9C[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0xF5, 0xE3, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xE4, 0xCA, 0x00, 0x00, 0xDC, 0xE1, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0xF9, 0xC8, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0xFC, 0xBF, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0xE8, 0xA7, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD8, 0xC4, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCB, 0xBE, /* 0x44-0x47 */
+ 0x00, 0x00, 0xDC, 0xAE, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD7, 0xF7, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0xF0, 0xE8, 0x00, 0x00, 0xDD, 0xC0, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0xCF, 0xCD, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0xF3, /* 0xF0-0xF3 */
+ 0xD9, 0xB0, 0x00, 0x00, 0xE6, 0xE9, 0x00, 0x00, /* 0xF4-0xF7 */
+};
+
+static unsigned char u2c_9D[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0xE4, 0xBC, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEA, 0xC4, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0xE4, 0xEC, 0x00, 0x00, /* 0x24-0x27 */
+ 0xE4, 0xE5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFB, 0xF8, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0xCC, 0xBB, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0xE4, 0xBD, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xCD, 0xDC, 0xD9, 0xF7, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0xDD, 0xDF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0xED, 0xCE, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0xD9, 0xD0, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0xA3, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0xF9, 0xCD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xCD, 0xAE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCF, 0xCE, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0xF6, 0xAF, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0xFD, 0xD3, 0xEB, 0xED, 0xD6, 0xDC, 0x00, 0x00, /* 0xF8-0xFB */
+};
+
+static unsigned char u2c_9E[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0xA4, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0xD5, 0xB6, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0xD6, 0xDD, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0xF9, 0xE9, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0xE7, 0xA4, 0x00, 0x00, 0xD6, 0xE3, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0xD1, 0xCB, 0xD6, 0xE4, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD5, 0xF2, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0xDE, 0xFA, 0x00, 0x00, 0xD7, 0xF8, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0xD8, 0xEA, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0xCF, 0xD5, 0xD8, 0xFD, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD8, 0xAB, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0xFD, 0xCB, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xDC, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0xE0, 0xA8, 0xD5, 0xF3, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0xFD, 0xD9, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xCC, 0xA3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xD9, 0xF9, 0x00, 0x00, 0x00, 0x00, 0xD3, 0xEA, /* 0xD8-0xDB */
+ 0xF5, 0xF5, 0x00, 0x00, 0xEF, 0xC7, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xD3, 0xDA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xDA, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+};
+
+static unsigned char u2c_9F[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0xA8, /* 0x04-0x07 */
+ 0xDC, 0xAF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0xF0, 0xA3, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCD, 0xD5, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0xE0, 0xA9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDE, 0xAC, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0xF0, 0xBA, 0xEE, 0xB1, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0xEE, 0xB2, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0xF6, 0xCD, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEE, 0xD2, /* 0x5C-0x5F */
+ 0x00, 0x00, 0xD6, 0xC6, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xE5, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0xF3, 0xBB, 0x00, 0x00, /* 0x68-0x6B */
+ 0xE5, 0xE1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0xCB, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0xD7, 0xA3, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0xDB, 0xC2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0xCA, 0xFE, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0xCF, 0xCF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+};
+
+static unsigned char u2c_AC[512] = {
+ 0xB0, 0xA1, 0xB0, 0xA2, 0x81, 0x41, 0x81, 0x42, /* 0x00-0x03 */
+ 0xB0, 0xA3, 0x81, 0x43, 0x81, 0x44, 0xB0, 0xA4, /* 0x04-0x07 */
+ 0xB0, 0xA5, 0xB0, 0xA6, 0xB0, 0xA7, 0x81, 0x45, /* 0x08-0x0B */
+ 0x81, 0x46, 0x81, 0x47, 0x81, 0x48, 0x81, 0x49, /* 0x0C-0x0F */
+ 0xB0, 0xA8, 0xB0, 0xA9, 0xB0, 0xAA, 0xB0, 0xAB, /* 0x10-0x13 */
+ 0xB0, 0xAC, 0xB0, 0xAD, 0xB0, 0xAE, 0xB0, 0xAF, /* 0x14-0x17 */
+ 0x81, 0x4A, 0xB0, 0xB0, 0xB0, 0xB1, 0xB0, 0xB2, /* 0x18-0x1B */
+ 0xB0, 0xB3, 0xB0, 0xB4, 0x81, 0x4B, 0x81, 0x4C, /* 0x1C-0x1F */
+ 0xB0, 0xB5, 0x81, 0x4D, 0x81, 0x4E, 0x81, 0x4F, /* 0x20-0x23 */
+ 0xB0, 0xB6, 0x81, 0x50, 0x81, 0x51, 0x81, 0x52, /* 0x24-0x27 */
+ 0x81, 0x53, 0x81, 0x54, 0x81, 0x55, 0x81, 0x56, /* 0x28-0x2B */
+ 0xB0, 0xB7, 0xB0, 0xB8, 0x81, 0x57, 0xB0, 0xB9, /* 0x2C-0x2F */
+ 0xB0, 0xBA, 0xB0, 0xBB, 0x81, 0x58, 0x81, 0x59, /* 0x30-0x33 */
+ 0x81, 0x5A, 0x81, 0x61, 0x81, 0x62, 0x81, 0x63, /* 0x34-0x37 */
+ 0xB0, 0xBC, 0xB0, 0xBD, 0x81, 0x64, 0x81, 0x65, /* 0x38-0x3B */
+ 0xB0, 0xBE, 0x81, 0x66, 0x81, 0x67, 0x81, 0x68, /* 0x3C-0x3F */
+ 0xB0, 0xBF, 0x81, 0x69, 0x81, 0x6A, 0x81, 0x6B, /* 0x40-0x43 */
+ 0x81, 0x6C, 0x81, 0x6D, 0x81, 0x6E, 0x81, 0x6F, /* 0x44-0x47 */
+ 0x81, 0x70, 0x81, 0x71, 0x81, 0x72, 0xB0, 0xC0, /* 0x48-0x4B */
+ 0x81, 0x73, 0xB0, 0xC1, 0x81, 0x74, 0x81, 0x75, /* 0x4C-0x4F */
+ 0x81, 0x76, 0x81, 0x77, 0x81, 0x78, 0x81, 0x79, /* 0x50-0x53 */
+ 0xB0, 0xC2, 0x81, 0x7A, 0x81, 0x81, 0x81, 0x82, /* 0x54-0x57 */
+ 0xB0, 0xC3, 0x81, 0x83, 0x81, 0x84, 0x81, 0x85, /* 0x58-0x5B */
+ 0xB0, 0xC4, 0x81, 0x86, 0x81, 0x87, 0x81, 0x88, /* 0x5C-0x5F */
+ 0x81, 0x89, 0x81, 0x8A, 0x81, 0x8B, 0x81, 0x8C, /* 0x60-0x63 */
+ 0x81, 0x8D, 0x81, 0x8E, 0x81, 0x8F, 0x81, 0x90, /* 0x64-0x67 */
+ 0x81, 0x91, 0x81, 0x92, 0x81, 0x93, 0x81, 0x94, /* 0x68-0x6B */
+ 0x81, 0x95, 0x81, 0x96, 0x81, 0x97, 0x81, 0x98, /* 0x6C-0x6F */
+ 0xB0, 0xC5, 0xB0, 0xC6, 0x81, 0x99, 0x81, 0x9A, /* 0x70-0x73 */
+ 0xB0, 0xC7, 0x81, 0x9B, 0x81, 0x9C, 0xB0, 0xC8, /* 0x74-0x77 */
+ 0xB0, 0xC9, 0x81, 0x9D, 0xB0, 0xCA, 0x81, 0x9E, /* 0x78-0x7B */
+ 0x81, 0x9F, 0x81, 0xA0, 0x81, 0xA1, 0x81, 0xA2, /* 0x7C-0x7F */
+
+ 0xB0, 0xCB, 0xB0, 0xCC, 0x81, 0xA3, 0xB0, 0xCD, /* 0x80-0x83 */
+ 0xB0, 0xCE, 0xB0, 0xCF, 0xB0, 0xD0, 0x81, 0xA4, /* 0x84-0x87 */
+ 0x81, 0xA5, 0xB0, 0xD1, 0xB0, 0xD2, 0xB0, 0xD3, /* 0x88-0x8B */
+ 0xB0, 0xD4, 0x81, 0xA6, 0x81, 0xA7, 0x81, 0xA8, /* 0x8C-0x8F */
+ 0xB0, 0xD5, 0x81, 0xA9, 0x81, 0xAA, 0x81, 0xAB, /* 0x90-0x93 */
+ 0xB0, 0xD6, 0x81, 0xAC, 0x81, 0xAD, 0x81, 0xAE, /* 0x94-0x97 */
+ 0x81, 0xAF, 0x81, 0xB0, 0x81, 0xB1, 0x81, 0xB2, /* 0x98-0x9B */
+ 0xB0, 0xD7, 0xB0, 0xD8, 0x81, 0xB3, 0xB0, 0xD9, /* 0x9C-0x9F */
+ 0xB0, 0xDA, 0xB0, 0xDB, 0x81, 0xB4, 0x81, 0xB5, /* 0xA0-0xA3 */
+ 0x81, 0xB6, 0x81, 0xB7, 0x81, 0xB8, 0x81, 0xB9, /* 0xA4-0xA7 */
+ 0xB0, 0xDC, 0xB0, 0xDD, 0xB0, 0xDE, 0x81, 0xBA, /* 0xA8-0xAB */
+ 0xB0, 0xDF, 0x81, 0xBB, 0x81, 0xBC, 0xB0, 0xE0, /* 0xAC-0xAF */
+ 0xB0, 0xE1, 0x81, 0xBD, 0x81, 0xBE, 0x81, 0xBF, /* 0xB0-0xB3 */
+ 0x81, 0xC0, 0x81, 0xC1, 0x81, 0xC2, 0x81, 0xC3, /* 0xB4-0xB7 */
+ 0xB0, 0xE2, 0xB0, 0xE3, 0x81, 0xC4, 0xB0, 0xE4, /* 0xB8-0xBB */
+ 0xB0, 0xE5, 0xB0, 0xE6, 0x81, 0xC5, 0x81, 0xC6, /* 0xBC-0xBF */
+ 0x81, 0xC7, 0xB0, 0xE7, 0x81, 0xC8, 0x81, 0xC9, /* 0xC0-0xC3 */
+ 0xB0, 0xE8, 0x81, 0xCA, 0x81, 0xCB, 0x81, 0xCC, /* 0xC4-0xC7 */
+ 0xB0, 0xE9, 0x81, 0xCD, 0x81, 0xCE, 0x81, 0xCF, /* 0xC8-0xCB */
+ 0xB0, 0xEA, 0x81, 0xD0, 0x81, 0xD1, 0x81, 0xD2, /* 0xCC-0xCF */
+ 0x81, 0xD3, 0x81, 0xD4, 0x81, 0xD5, 0x81, 0xD6, /* 0xD0-0xD3 */
+ 0x81, 0xD7, 0xB0, 0xEB, 0x81, 0xD8, 0xB0, 0xEC, /* 0xD4-0xD7 */
+ 0x81, 0xD9, 0x81, 0xDA, 0x81, 0xDB, 0x81, 0xDC, /* 0xD8-0xDB */
+ 0x81, 0xDD, 0x81, 0xDE, 0x81, 0xDF, 0x81, 0xE0, /* 0xDC-0xDF */
+ 0xB0, 0xED, 0xB0, 0xEE, 0x81, 0xE1, 0x81, 0xE2, /* 0xE0-0xE3 */
+ 0xB0, 0xEF, 0x81, 0xE3, 0x81, 0xE4, 0xB0, 0xF0, /* 0xE4-0xE7 */
+ 0xB0, 0xF1, 0x81, 0xE5, 0xB0, 0xF2, 0x81, 0xE6, /* 0xE8-0xEB */
+ 0xB0, 0xF3, 0x81, 0xE7, 0x81, 0xE8, 0xB0, 0xF4, /* 0xEC-0xEF */
+ 0xB0, 0xF5, 0xB0, 0xF6, 0x81, 0xE9, 0xB0, 0xF7, /* 0xF0-0xF3 */
+ 0x81, 0xEA, 0xB0, 0xF8, 0xB0, 0xF9, 0x81, 0xEB, /* 0xF4-0xF7 */
+ 0x81, 0xEC, 0x81, 0xED, 0x81, 0xEE, 0x81, 0xEF, /* 0xF8-0xFB */
+ 0xB0, 0xFA, 0xB0, 0xFB, 0x81, 0xF0, 0x81, 0xF1, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_AD[512] = {
+ 0xB0, 0xFC, 0x81, 0xF2, 0x81, 0xF3, 0x81, 0xF4, /* 0x00-0x03 */
+ 0xB0, 0xFD, 0x81, 0xF5, 0xB0, 0xFE, 0x81, 0xF6, /* 0x04-0x07 */
+ 0x81, 0xF7, 0x81, 0xF8, 0x81, 0xF9, 0x81, 0xFA, /* 0x08-0x0B */
+ 0xB1, 0xA1, 0xB1, 0xA2, 0x81, 0xFB, 0xB1, 0xA3, /* 0x0C-0x0F */
+ 0x81, 0xFC, 0xB1, 0xA4, 0x81, 0xFD, 0x81, 0xFE, /* 0x10-0x13 */
+ 0x82, 0x41, 0x82, 0x42, 0x82, 0x43, 0x82, 0x44, /* 0x14-0x17 */
+ 0xB1, 0xA5, 0x82, 0x45, 0x82, 0x46, 0x82, 0x47, /* 0x18-0x1B */
+ 0xB1, 0xA6, 0x82, 0x48, 0x82, 0x49, 0x82, 0x4A, /* 0x1C-0x1F */
+ 0xB1, 0xA7, 0x82, 0x4B, 0x82, 0x4C, 0x82, 0x4D, /* 0x20-0x23 */
+ 0x82, 0x4E, 0x82, 0x4F, 0x82, 0x50, 0x82, 0x51, /* 0x24-0x27 */
+ 0x82, 0x52, 0xB1, 0xA8, 0x82, 0x53, 0x82, 0x54, /* 0x28-0x2B */
+ 0xB1, 0xA9, 0xB1, 0xAA, 0x82, 0x55, 0x82, 0x56, /* 0x2C-0x2F */
+ 0x82, 0x57, 0x82, 0x58, 0x82, 0x59, 0x82, 0x5A, /* 0x30-0x33 */
+ 0xB1, 0xAB, 0xB1, 0xAC, 0x82, 0x61, 0x82, 0x62, /* 0x34-0x37 */
+ 0xB1, 0xAD, 0x82, 0x63, 0x82, 0x64, 0x82, 0x65, /* 0x38-0x3B */
+ 0xB1, 0xAE, 0x82, 0x66, 0x82, 0x67, 0x82, 0x68, /* 0x3C-0x3F */
+ 0x82, 0x69, 0x82, 0x6A, 0x82, 0x6B, 0x82, 0x6C, /* 0x40-0x43 */
+ 0xB1, 0xAF, 0xB1, 0xB0, 0x82, 0x6D, 0xB1, 0xB1, /* 0x44-0x47 */
+ 0x82, 0x6E, 0xB1, 0xB2, 0x82, 0x6F, 0x82, 0x70, /* 0x48-0x4B */
+ 0x82, 0x71, 0x82, 0x72, 0x82, 0x73, 0x82, 0x74, /* 0x4C-0x4F */
+ 0xB1, 0xB3, 0x82, 0x75, 0x82, 0x76, 0x82, 0x77, /* 0x50-0x53 */
+ 0xB1, 0xB4, 0x82, 0x78, 0x82, 0x79, 0x82, 0x7A, /* 0x54-0x57 */
+ 0xB1, 0xB5, 0x82, 0x81, 0x82, 0x82, 0x82, 0x83, /* 0x58-0x5B */
+ 0x82, 0x84, 0x82, 0x85, 0x82, 0x86, 0x82, 0x87, /* 0x5C-0x5F */
+ 0x82, 0x88, 0xB1, 0xB6, 0x82, 0x89, 0xB1, 0xB7, /* 0x60-0x63 */
+ 0x82, 0x8A, 0x82, 0x8B, 0x82, 0x8C, 0x82, 0x8D, /* 0x64-0x67 */
+ 0x82, 0x8E, 0x82, 0x8F, 0x82, 0x90, 0x82, 0x91, /* 0x68-0x6B */
+ 0xB1, 0xB8, 0xB1, 0xB9, 0x82, 0x92, 0x82, 0x93, /* 0x6C-0x6F */
+ 0xB1, 0xBA, 0x82, 0x94, 0x82, 0x95, 0xB1, 0xBB, /* 0x70-0x73 */
+ 0xB1, 0xBC, 0xB1, 0xBD, 0xB1, 0xBE, 0x82, 0x96, /* 0x74-0x77 */
+ 0x82, 0x97, 0x82, 0x98, 0x82, 0x99, 0xB1, 0xBF, /* 0x78-0x7B */
+ 0xB1, 0xC0, 0xB1, 0xC1, 0x82, 0x9A, 0xB1, 0xC2, /* 0x7C-0x7F */
+
+ 0x82, 0x9B, 0xB1, 0xC3, 0xB1, 0xC4, 0x82, 0x9C, /* 0x80-0x83 */
+ 0x82, 0x9D, 0x82, 0x9E, 0x82, 0x9F, 0x82, 0xA0, /* 0x84-0x87 */
+ 0xB1, 0xC5, 0xB1, 0xC6, 0x82, 0xA1, 0x82, 0xA2, /* 0x88-0x8B */
+ 0xB1, 0xC7, 0x82, 0xA3, 0x82, 0xA4, 0x82, 0xA5, /* 0x8C-0x8F */
+ 0xB1, 0xC8, 0x82, 0xA6, 0x82, 0xA7, 0x82, 0xA8, /* 0x90-0x93 */
+ 0x82, 0xA9, 0x82, 0xAA, 0x82, 0xAB, 0x82, 0xAC, /* 0x94-0x97 */
+ 0x82, 0xAD, 0x82, 0xAE, 0x82, 0xAF, 0x82, 0xB0, /* 0x98-0x9B */
+ 0xB1, 0xC9, 0xB1, 0xCA, 0x82, 0xB1, 0x82, 0xB2, /* 0x9C-0x9F */
+ 0x82, 0xB3, 0x82, 0xB4, 0x82, 0xB5, 0x82, 0xB6, /* 0xA0-0xA3 */
+ 0xB1, 0xCB, 0x82, 0xB7, 0x82, 0xB8, 0x82, 0xB9, /* 0xA4-0xA7 */
+ 0x82, 0xBA, 0x82, 0xBB, 0x82, 0xBC, 0x82, 0xBD, /* 0xA8-0xAB */
+ 0x82, 0xBE, 0x82, 0xBF, 0x82, 0xC0, 0x82, 0xC1, /* 0xAC-0xAF */
+ 0x82, 0xC2, 0x82, 0xC3, 0x82, 0xC4, 0x82, 0xC5, /* 0xB0-0xB3 */
+ 0x82, 0xC6, 0x82, 0xC7, 0x82, 0xC8, 0xB1, 0xCC, /* 0xB4-0xB7 */
+ 0x82, 0xC9, 0x82, 0xCA, 0x82, 0xCB, 0x82, 0xCC, /* 0xB8-0xBB */
+ 0x82, 0xCD, 0x82, 0xCE, 0x82, 0xCF, 0x82, 0xD0, /* 0xBC-0xBF */
+ 0xB1, 0xCD, 0xB1, 0xCE, 0x82, 0xD1, 0x82, 0xD2, /* 0xC0-0xC3 */
+ 0xB1, 0xCF, 0x82, 0xD3, 0x82, 0xD4, 0x82, 0xD5, /* 0xC4-0xC7 */
+ 0xB1, 0xD0, 0x82, 0xD6, 0x82, 0xD7, 0x82, 0xD8, /* 0xC8-0xCB */
+ 0x82, 0xD9, 0x82, 0xDA, 0x82, 0xDB, 0x82, 0xDC, /* 0xCC-0xCF */
+ 0xB1, 0xD1, 0xB1, 0xD2, 0x82, 0xDD, 0xB1, 0xD3, /* 0xD0-0xD3 */
+ 0x82, 0xDE, 0x82, 0xDF, 0x82, 0xE0, 0x82, 0xE1, /* 0xD4-0xD7 */
+ 0x82, 0xE2, 0x82, 0xE3, 0x82, 0xE4, 0x82, 0xE5, /* 0xD8-0xDB */
+ 0xB1, 0xD4, 0x82, 0xE6, 0x82, 0xE7, 0x82, 0xE8, /* 0xDC-0xDF */
+ 0xB1, 0xD5, 0x82, 0xE9, 0x82, 0xEA, 0x82, 0xEB, /* 0xE0-0xE3 */
+ 0xB1, 0xD6, 0x82, 0xEC, 0x82, 0xED, 0x82, 0xEE, /* 0xE4-0xE7 */
+ 0x82, 0xEF, 0x82, 0xF0, 0x82, 0xF1, 0x82, 0xF2, /* 0xE8-0xEB */
+ 0x82, 0xF3, 0x82, 0xF4, 0x82, 0xF5, 0x82, 0xF6, /* 0xEC-0xEF */
+ 0x82, 0xF7, 0x82, 0xF8, 0x82, 0xF9, 0x82, 0xFA, /* 0xF0-0xF3 */
+ 0x82, 0xFB, 0x82, 0xFC, 0x82, 0xFD, 0x82, 0xFE, /* 0xF4-0xF7 */
+ 0xB1, 0xD7, 0xB1, 0xD8, 0x83, 0x41, 0x83, 0x42, /* 0xF8-0xFB */
+ 0xB1, 0xD9, 0x83, 0x43, 0x83, 0x44, 0xB1, 0xDA, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_AE[512] = {
+ 0xB1, 0xDB, 0xB1, 0xDC, 0x83, 0x45, 0x83, 0x46, /* 0x00-0x03 */
+ 0x83, 0x47, 0x83, 0x48, 0x83, 0x49, 0x83, 0x4A, /* 0x04-0x07 */
+ 0xB1, 0xDD, 0xB1, 0xDE, 0x83, 0x4B, 0xB1, 0xDF, /* 0x08-0x0B */
+ 0x83, 0x4C, 0xB1, 0xE0, 0x83, 0x4D, 0x83, 0x4E, /* 0x0C-0x0F */
+ 0x83, 0x4F, 0x83, 0x50, 0x83, 0x51, 0x83, 0x52, /* 0x10-0x13 */
+ 0xB1, 0xE1, 0x83, 0x53, 0x83, 0x54, 0x83, 0x55, /* 0x14-0x17 */
+ 0x83, 0x56, 0x83, 0x57, 0x83, 0x58, 0x83, 0x59, /* 0x18-0x1B */
+ 0x83, 0x5A, 0x83, 0x61, 0x83, 0x62, 0x83, 0x63, /* 0x1C-0x1F */
+ 0x83, 0x64, 0x83, 0x65, 0x83, 0x66, 0x83, 0x67, /* 0x20-0x23 */
+ 0x83, 0x68, 0x83, 0x69, 0x83, 0x6A, 0x83, 0x6B, /* 0x24-0x27 */
+ 0x83, 0x6C, 0x83, 0x6D, 0x83, 0x6E, 0x83, 0x6F, /* 0x28-0x2B */
+ 0x83, 0x70, 0x83, 0x71, 0x83, 0x72, 0x83, 0x73, /* 0x2C-0x2F */
+ 0xB1, 0xE2, 0xB1, 0xE3, 0x83, 0x74, 0x83, 0x75, /* 0x30-0x33 */
+ 0xB1, 0xE4, 0x83, 0x76, 0x83, 0x77, 0xB1, 0xE5, /* 0x34-0x37 */
+ 0xB1, 0xE6, 0x83, 0x78, 0xB1, 0xE7, 0x83, 0x79, /* 0x38-0x3B */
+ 0x83, 0x7A, 0x83, 0x81, 0x83, 0x82, 0x83, 0x83, /* 0x3C-0x3F */
+ 0xB1, 0xE8, 0xB1, 0xE9, 0x83, 0x84, 0xB1, 0xEA, /* 0x40-0x43 */
+ 0x83, 0x85, 0xB1, 0xEB, 0xB1, 0xEC, 0x83, 0x86, /* 0x44-0x47 */
+ 0x83, 0x87, 0x83, 0x88, 0xB1, 0xED, 0x83, 0x89, /* 0x48-0x4B */
+ 0xB1, 0xEE, 0xB1, 0xEF, 0xB1, 0xF0, 0x83, 0x8A, /* 0x4C-0x4F */
+ 0xB1, 0xF1, 0x83, 0x8B, 0x83, 0x8C, 0x83, 0x8D, /* 0x50-0x53 */
+ 0xB1, 0xF2, 0x83, 0x8E, 0xB1, 0xF3, 0x83, 0x8F, /* 0x54-0x57 */
+ 0x83, 0x90, 0x83, 0x91, 0x83, 0x92, 0x83, 0x93, /* 0x58-0x5B */
+ 0xB1, 0xF4, 0xB1, 0xF5, 0x83, 0x94, 0xB1, 0xF6, /* 0x5C-0x5F */
+ 0xB1, 0xF7, 0xB1, 0xF8, 0x83, 0x95, 0x83, 0x96, /* 0x60-0x63 */
+ 0x83, 0x97, 0xB1, 0xF9, 0x83, 0x98, 0x83, 0x99, /* 0x64-0x67 */
+ 0xB1, 0xFA, 0xB1, 0xFB, 0x83, 0x9A, 0x83, 0x9B, /* 0x68-0x6B */
+ 0xB1, 0xFC, 0x83, 0x9C, 0x83, 0x9D, 0x83, 0x9E, /* 0x6C-0x6F */
+ 0xB1, 0xFD, 0x83, 0x9F, 0x83, 0xA0, 0x83, 0xA1, /* 0x70-0x73 */
+ 0x83, 0xA2, 0x83, 0xA3, 0x83, 0xA4, 0x83, 0xA5, /* 0x74-0x77 */
+ 0xB1, 0xFE, 0xB2, 0xA1, 0x83, 0xA6, 0xB2, 0xA2, /* 0x78-0x7B */
+ 0xB2, 0xA3, 0xB2, 0xA4, 0x83, 0xA7, 0x83, 0xA8, /* 0x7C-0x7F */
+
+ 0x83, 0xA9, 0x83, 0xAA, 0x83, 0xAB, 0x83, 0xAC, /* 0x80-0x83 */
+ 0xB2, 0xA5, 0xB2, 0xA6, 0x83, 0xAD, 0x83, 0xAE, /* 0x84-0x87 */
+ 0x83, 0xAF, 0x83, 0xB0, 0x83, 0xB1, 0x83, 0xB2, /* 0x88-0x8B */
+ 0xB2, 0xA7, 0x83, 0xB3, 0x83, 0xB4, 0x83, 0xB5, /* 0x8C-0x8F */
+ 0x83, 0xB6, 0x83, 0xB7, 0x83, 0xB8, 0x83, 0xB9, /* 0x90-0x93 */
+ 0x83, 0xBA, 0x83, 0xBB, 0x83, 0xBC, 0x83, 0xBD, /* 0x94-0x97 */
+ 0x83, 0xBE, 0x83, 0xBF, 0x83, 0xC0, 0x83, 0xC1, /* 0x98-0x9B */
+ 0x83, 0xC2, 0x83, 0xC3, 0x83, 0xC4, 0x83, 0xC5, /* 0x9C-0x9F */
+ 0x83, 0xC6, 0x83, 0xC7, 0x83, 0xC8, 0x83, 0xC9, /* 0xA0-0xA3 */
+ 0x83, 0xCA, 0x83, 0xCB, 0x83, 0xCC, 0x83, 0xCD, /* 0xA4-0xA7 */
+ 0x83, 0xCE, 0x83, 0xCF, 0x83, 0xD0, 0x83, 0xD1, /* 0xA8-0xAB */
+ 0x83, 0xD2, 0x83, 0xD3, 0x83, 0xD4, 0x83, 0xD5, /* 0xAC-0xAF */
+ 0x83, 0xD6, 0x83, 0xD7, 0x83, 0xD8, 0x83, 0xD9, /* 0xB0-0xB3 */
+ 0x83, 0xDA, 0x83, 0xDB, 0x83, 0xDC, 0x83, 0xDD, /* 0xB4-0xB7 */
+ 0x83, 0xDE, 0x83, 0xDF, 0x83, 0xE0, 0x83, 0xE1, /* 0xB8-0xBB */
+ 0xB2, 0xA8, 0xB2, 0xA9, 0xB2, 0xAA, 0x83, 0xE2, /* 0xBC-0xBF */
+ 0xB2, 0xAB, 0x83, 0xE3, 0x83, 0xE4, 0x83, 0xE5, /* 0xC0-0xC3 */
+ 0xB2, 0xAC, 0x83, 0xE6, 0x83, 0xE7, 0x83, 0xE8, /* 0xC4-0xC7 */
+ 0x83, 0xE9, 0x83, 0xEA, 0x83, 0xEB, 0x83, 0xEC, /* 0xC8-0xCB */
+ 0xB2, 0xAD, 0xB2, 0xAE, 0x83, 0xED, 0xB2, 0xAF, /* 0xCC-0xCF */
+ 0xB2, 0xB0, 0xB2, 0xB1, 0x83, 0xEE, 0x83, 0xEF, /* 0xD0-0xD3 */
+ 0x83, 0xF0, 0x83, 0xF1, 0x83, 0xF2, 0x83, 0xF3, /* 0xD4-0xD7 */
+ 0xB2, 0xB2, 0xB2, 0xB3, 0x83, 0xF4, 0x83, 0xF5, /* 0xD8-0xDB */
+ 0xB2, 0xB4, 0x83, 0xF6, 0x83, 0xF7, 0x83, 0xF8, /* 0xDC-0xDF */
+ 0x83, 0xF9, 0x83, 0xFA, 0x83, 0xFB, 0x83, 0xFC, /* 0xE0-0xE3 */
+ 0x83, 0xFD, 0x83, 0xFE, 0x84, 0x41, 0x84, 0x42, /* 0xE4-0xE7 */
+ 0xB2, 0xB5, 0x84, 0x43, 0x84, 0x44, 0xB2, 0xB6, /* 0xE8-0xEB */
+ 0x84, 0x45, 0xB2, 0xB7, 0x84, 0x46, 0x84, 0x47, /* 0xEC-0xEF */
+ 0x84, 0x48, 0x84, 0x49, 0x84, 0x4A, 0x84, 0x4B, /* 0xF0-0xF3 */
+ 0xB2, 0xB8, 0x84, 0x4C, 0x84, 0x4D, 0x84, 0x4E, /* 0xF4-0xF7 */
+ 0xB2, 0xB9, 0x84, 0x4F, 0x84, 0x50, 0x84, 0x51, /* 0xF8-0xFB */
+ 0xB2, 0xBA, 0x84, 0x52, 0x84, 0x53, 0x84, 0x54, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_AF[512] = {
+ 0x84, 0x55, 0x84, 0x56, 0x84, 0x57, 0x84, 0x58, /* 0x00-0x03 */
+ 0x84, 0x59, 0x84, 0x5A, 0x84, 0x61, 0xB2, 0xBB, /* 0x04-0x07 */
+ 0xB2, 0xBC, 0x84, 0x62, 0x84, 0x63, 0x84, 0x64, /* 0x08-0x0B */
+ 0x84, 0x65, 0xB2, 0xBD, 0x84, 0x66, 0x84, 0x67, /* 0x0C-0x0F */
+ 0xB2, 0xBE, 0x84, 0x68, 0x84, 0x69, 0x84, 0x6A, /* 0x10-0x13 */
+ 0x84, 0x6B, 0x84, 0x6C, 0x84, 0x6D, 0x84, 0x6E, /* 0x14-0x17 */
+ 0x84, 0x6F, 0x84, 0x70, 0x84, 0x71, 0x84, 0x72, /* 0x18-0x1B */
+ 0x84, 0x73, 0x84, 0x74, 0x84, 0x75, 0x84, 0x76, /* 0x1C-0x1F */
+ 0x84, 0x77, 0x84, 0x78, 0x84, 0x79, 0x84, 0x7A, /* 0x20-0x23 */
+ 0x84, 0x81, 0x84, 0x82, 0x84, 0x83, 0x84, 0x84, /* 0x24-0x27 */
+ 0x84, 0x85, 0x84, 0x86, 0x84, 0x87, 0x84, 0x88, /* 0x28-0x2B */
+ 0xB2, 0xBF, 0xB2, 0xC0, 0x84, 0x89, 0x84, 0x8A, /* 0x2C-0x2F */
+ 0xB2, 0xC1, 0x84, 0x8B, 0xB2, 0xC2, 0x84, 0x8C, /* 0x30-0x33 */
+ 0xB2, 0xC3, 0x84, 0x8D, 0x84, 0x8E, 0x84, 0x8F, /* 0x34-0x37 */
+ 0x84, 0x90, 0x84, 0x91, 0x84, 0x92, 0x84, 0x93, /* 0x38-0x3B */
+ 0xB2, 0xC4, 0xB2, 0xC5, 0x84, 0x94, 0xB2, 0xC6, /* 0x3C-0x3F */
+ 0x84, 0x95, 0xB2, 0xC7, 0xB2, 0xC8, 0xB2, 0xC9, /* 0x40-0x43 */
+ 0x84, 0x96, 0x84, 0x97, 0x84, 0x98, 0x84, 0x99, /* 0x44-0x47 */
+ 0xB2, 0xCA, 0xB2, 0xCB, 0x84, 0x9A, 0x84, 0x9B, /* 0x48-0x4B */
+ 0x84, 0x9C, 0x84, 0x9D, 0x84, 0x9E, 0x84, 0x9F, /* 0x4C-0x4F */
+ 0xB2, 0xCC, 0x84, 0xA0, 0x84, 0xA1, 0x84, 0xA2, /* 0x50-0x53 */
+ 0x84, 0xA3, 0x84, 0xA4, 0x84, 0xA5, 0x84, 0xA6, /* 0x54-0x57 */
+ 0x84, 0xA7, 0x84, 0xA8, 0x84, 0xA9, 0x84, 0xAA, /* 0x58-0x5B */
+ 0xB2, 0xCD, 0xB2, 0xCE, 0x84, 0xAB, 0x84, 0xAC, /* 0x5C-0x5F */
+ 0x84, 0xAD, 0x84, 0xAE, 0x84, 0xAF, 0x84, 0xB0, /* 0x60-0x63 */
+ 0xB2, 0xCF, 0xB2, 0xD0, 0x84, 0xB1, 0x84, 0xB2, /* 0x64-0x67 */
+ 0x84, 0xB3, 0x84, 0xB4, 0x84, 0xB5, 0x84, 0xB6, /* 0x68-0x6B */
+ 0x84, 0xB7, 0x84, 0xB8, 0x84, 0xB9, 0x84, 0xBA, /* 0x6C-0x6F */
+ 0x84, 0xBB, 0x84, 0xBC, 0x84, 0xBD, 0x84, 0xBE, /* 0x70-0x73 */
+ 0x84, 0xBF, 0x84, 0xC0, 0x84, 0xC1, 0x84, 0xC2, /* 0x74-0x77 */
+ 0x84, 0xC3, 0xB2, 0xD1, 0x84, 0xC4, 0x84, 0xC5, /* 0x78-0x7B */
+ 0x84, 0xC6, 0x84, 0xC7, 0x84, 0xC8, 0x84, 0xC9, /* 0x7C-0x7F */
+
+ 0xB2, 0xD2, 0x84, 0xCA, 0x84, 0xCB, 0x84, 0xCC, /* 0x80-0x83 */
+ 0xB2, 0xD3, 0x84, 0xCD, 0x84, 0xCE, 0x84, 0xCF, /* 0x84-0x87 */
+ 0xB2, 0xD4, 0x84, 0xD0, 0x84, 0xD1, 0x84, 0xD2, /* 0x88-0x8B */
+ 0x84, 0xD3, 0x84, 0xD4, 0x84, 0xD5, 0x84, 0xD6, /* 0x8C-0x8F */
+ 0xB2, 0xD5, 0xB2, 0xD6, 0x84, 0xD7, 0x84, 0xD8, /* 0x90-0x93 */
+ 0x84, 0xD9, 0xB2, 0xD7, 0x84, 0xDA, 0x84, 0xDB, /* 0x94-0x97 */
+ 0x84, 0xDC, 0x84, 0xDD, 0x84, 0xDE, 0x84, 0xDF, /* 0x98-0x9B */
+ 0xB2, 0xD8, 0x84, 0xE0, 0x84, 0xE1, 0x84, 0xE2, /* 0x9C-0x9F */
+ 0x84, 0xE3, 0x84, 0xE4, 0x84, 0xE5, 0x84, 0xE6, /* 0xA0-0xA3 */
+ 0x84, 0xE7, 0x84, 0xE8, 0x84, 0xE9, 0x84, 0xEA, /* 0xA4-0xA7 */
+ 0x84, 0xEB, 0x84, 0xEC, 0x84, 0xED, 0x84, 0xEE, /* 0xA8-0xAB */
+ 0x84, 0xEF, 0x84, 0xF0, 0x84, 0xF1, 0x84, 0xF2, /* 0xAC-0xAF */
+ 0x84, 0xF3, 0x84, 0xF4, 0x84, 0xF5, 0x84, 0xF6, /* 0xB0-0xB3 */
+ 0x84, 0xF7, 0x84, 0xF8, 0x84, 0xF9, 0x84, 0xFA, /* 0xB4-0xB7 */
+ 0xB2, 0xD9, 0xB2, 0xDA, 0x84, 0xFB, 0x84, 0xFC, /* 0xB8-0xBB */
+ 0xB2, 0xDB, 0x84, 0xFD, 0x84, 0xFE, 0x85, 0x41, /* 0xBC-0xBF */
+ 0xB2, 0xDC, 0x85, 0x42, 0x85, 0x43, 0x85, 0x44, /* 0xC0-0xC3 */
+ 0x85, 0x45, 0x85, 0x46, 0x85, 0x47, 0xB2, 0xDD, /* 0xC4-0xC7 */
+ 0xB2, 0xDE, 0xB2, 0xDF, 0x85, 0x48, 0xB2, 0xE0, /* 0xC8-0xCB */
+ 0x85, 0x49, 0xB2, 0xE1, 0xB2, 0xE2, 0x85, 0x4A, /* 0xCC-0xCF */
+ 0x85, 0x4B, 0x85, 0x4C, 0x85, 0x4D, 0x85, 0x4E, /* 0xD0-0xD3 */
+ 0xB2, 0xE3, 0x85, 0x4F, 0x85, 0x50, 0x85, 0x51, /* 0xD4-0xD7 */
+ 0x85, 0x52, 0x85, 0x53, 0x85, 0x54, 0x85, 0x55, /* 0xD8-0xDB */
+ 0xB2, 0xE4, 0x85, 0x56, 0x85, 0x57, 0x85, 0x58, /* 0xDC-0xDF */
+ 0x85, 0x59, 0x85, 0x5A, 0x85, 0x61, 0x85, 0x62, /* 0xE0-0xE3 */
+ 0x85, 0x63, 0x85, 0x64, 0x85, 0x65, 0x85, 0x66, /* 0xE4-0xE7 */
+ 0xB2, 0xE5, 0xB2, 0xE6, 0x85, 0x67, 0x85, 0x68, /* 0xE8-0xEB */
+ 0x85, 0x69, 0x85, 0x6A, 0x85, 0x6B, 0x85, 0x6C, /* 0xEC-0xEF */
+ 0xB2, 0xE7, 0xB2, 0xE8, 0x85, 0x6D, 0x85, 0x6E, /* 0xF0-0xF3 */
+ 0xB2, 0xE9, 0x85, 0x6F, 0x85, 0x70, 0x85, 0x71, /* 0xF4-0xF7 */
+ 0xB2, 0xEA, 0x85, 0x72, 0x85, 0x73, 0x85, 0x74, /* 0xF8-0xFB */
+ 0x85, 0x75, 0x85, 0x76, 0x85, 0x77, 0x85, 0x78, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_B0[512] = {
+ 0xB2, 0xEB, 0xB2, 0xEC, 0x85, 0x79, 0x85, 0x7A, /* 0x00-0x03 */
+ 0xB2, 0xED, 0x85, 0x81, 0x85, 0x82, 0x85, 0x83, /* 0x04-0x07 */
+ 0x85, 0x84, 0x85, 0x85, 0x85, 0x86, 0x85, 0x87, /* 0x08-0x0B */
+ 0xB2, 0xEE, 0x85, 0x88, 0x85, 0x89, 0x85, 0x8A, /* 0x0C-0x0F */
+ 0xB2, 0xEF, 0x85, 0x8B, 0x85, 0x8C, 0x85, 0x8D, /* 0x10-0x13 */
+ 0xB2, 0xF0, 0x85, 0x8E, 0x85, 0x8F, 0x85, 0x90, /* 0x14-0x17 */
+ 0x85, 0x91, 0x85, 0x92, 0x85, 0x93, 0x85, 0x94, /* 0x18-0x1B */
+ 0xB2, 0xF1, 0xB2, 0xF2, 0x85, 0x95, 0x85, 0x96, /* 0x1C-0x1F */
+ 0x85, 0x97, 0x85, 0x98, 0x85, 0x99, 0x85, 0x9A, /* 0x20-0x23 */
+ 0x85, 0x9B, 0x85, 0x9C, 0x85, 0x9D, 0x85, 0x9E, /* 0x24-0x27 */
+ 0xB2, 0xF3, 0x85, 0x9F, 0x85, 0xA0, 0x85, 0xA1, /* 0x28-0x2B */
+ 0x85, 0xA2, 0x85, 0xA3, 0x85, 0xA4, 0x85, 0xA5, /* 0x2C-0x2F */
+ 0x85, 0xA6, 0x85, 0xA7, 0x85, 0xA8, 0x85, 0xA9, /* 0x30-0x33 */
+ 0x85, 0xAA, 0x85, 0xAB, 0x85, 0xAC, 0x85, 0xAD, /* 0x34-0x37 */
+ 0x85, 0xAE, 0x85, 0xAF, 0x85, 0xB0, 0x85, 0xB1, /* 0x38-0x3B */
+ 0x85, 0xB2, 0x85, 0xB3, 0x85, 0xB4, 0x85, 0xB5, /* 0x3C-0x3F */
+ 0x85, 0xB6, 0x85, 0xB7, 0x85, 0xB8, 0x85, 0xB9, /* 0x40-0x43 */
+ 0xB2, 0xF4, 0xB2, 0xF5, 0x85, 0xBA, 0x85, 0xBB, /* 0x44-0x47 */
+ 0xB2, 0xF6, 0x85, 0xBC, 0xB2, 0xF7, 0x85, 0xBD, /* 0x48-0x4B */
+ 0xB2, 0xF8, 0x85, 0xBE, 0xB2, 0xF9, 0x85, 0xBF, /* 0x4C-0x4F */
+ 0x85, 0xC0, 0x85, 0xC1, 0x85, 0xC2, 0xB2, 0xFA, /* 0x50-0x53 */
+ 0xB2, 0xFB, 0xB2, 0xFC, 0x85, 0xC3, 0xB2, 0xFD, /* 0x54-0x57 */
+ 0x85, 0xC4, 0xB2, 0xFE, 0x85, 0xC5, 0x85, 0xC6, /* 0x58-0x5B */
+ 0x85, 0xC7, 0xB3, 0xA1, 0x85, 0xC8, 0x85, 0xC9, /* 0x5C-0x5F */
+ 0x85, 0xCA, 0x85, 0xCB, 0x85, 0xCC, 0x85, 0xCD, /* 0x60-0x63 */
+ 0x85, 0xCE, 0x85, 0xCF, 0x85, 0xD0, 0x85, 0xD1, /* 0x64-0x67 */
+ 0x85, 0xD2, 0x85, 0xD3, 0x85, 0xD4, 0x85, 0xD5, /* 0x68-0x6B */
+ 0x85, 0xD6, 0x85, 0xD7, 0x85, 0xD8, 0x85, 0xD9, /* 0x6C-0x6F */
+ 0x85, 0xDA, 0x85, 0xDB, 0x85, 0xDC, 0x85, 0xDD, /* 0x70-0x73 */
+ 0x85, 0xDE, 0x85, 0xDF, 0x85, 0xE0, 0x85, 0xE1, /* 0x74-0x77 */
+ 0x85, 0xE2, 0x85, 0xE3, 0x85, 0xE4, 0x85, 0xE5, /* 0x78-0x7B */
+ 0xB3, 0xA2, 0xB3, 0xA3, 0x85, 0xE6, 0x85, 0xE7, /* 0x7C-0x7F */
+
+ 0xB3, 0xA4, 0x85, 0xE8, 0x85, 0xE9, 0x85, 0xEA, /* 0x80-0x83 */
+ 0xB3, 0xA5, 0x85, 0xEB, 0x85, 0xEC, 0x85, 0xED, /* 0x84-0x87 */
+ 0x85, 0xEE, 0x85, 0xEF, 0x85, 0xF0, 0x85, 0xF1, /* 0x88-0x8B */
+ 0xB3, 0xA6, 0xB3, 0xA7, 0x85, 0xF2, 0xB3, 0xA8, /* 0x8C-0x8F */
+ 0x85, 0xF3, 0xB3, 0xA9, 0x85, 0xF4, 0x85, 0xF5, /* 0x90-0x93 */
+ 0x85, 0xF6, 0x85, 0xF7, 0x85, 0xF8, 0x85, 0xF9, /* 0x94-0x97 */
+ 0xB3, 0xAA, 0xB3, 0xAB, 0xB3, 0xAC, 0x85, 0xFA, /* 0x98-0x9B */
+ 0xB3, 0xAD, 0x85, 0xFB, 0x85, 0xFC, 0xB3, 0xAE, /* 0x9C-0x9F */
+ 0xB3, 0xAF, 0xB3, 0xB0, 0xB3, 0xB1, 0x85, 0xFD, /* 0xA0-0xA3 */
+ 0x85, 0xFE, 0x86, 0x41, 0x86, 0x42, 0x86, 0x43, /* 0xA4-0xA7 */
+ 0xB3, 0xB2, 0xB3, 0xB3, 0x86, 0x44, 0xB3, 0xB4, /* 0xA8-0xAB */
+ 0xB3, 0xB5, 0xB3, 0xB6, 0xB3, 0xB7, 0xB3, 0xB8, /* 0xAC-0xAF */
+ 0x86, 0x45, 0xB3, 0xB9, 0x86, 0x46, 0xB3, 0xBA, /* 0xB0-0xB3 */
+ 0xB3, 0xBB, 0xB3, 0xBC, 0x86, 0x47, 0x86, 0x48, /* 0xB4-0xB7 */
+ 0xB3, 0xBD, 0x86, 0x49, 0x86, 0x4A, 0x86, 0x4B, /* 0xB8-0xBB */
+ 0xB3, 0xBE, 0x86, 0x4C, 0x86, 0x4D, 0x86, 0x4E, /* 0xBC-0xBF */
+ 0x86, 0x4F, 0x86, 0x50, 0x86, 0x51, 0x86, 0x52, /* 0xC0-0xC3 */
+ 0xB3, 0xBF, 0xB3, 0xC0, 0x86, 0x53, 0xB3, 0xC1, /* 0xC4-0xC7 */
+ 0xB3, 0xC2, 0xB3, 0xC3, 0x86, 0x54, 0x86, 0x55, /* 0xC8-0xCB */
+ 0x86, 0x56, 0x86, 0x57, 0x86, 0x58, 0x86, 0x59, /* 0xCC-0xCF */
+ 0xB3, 0xC4, 0xB3, 0xC5, 0x86, 0x5A, 0x86, 0x61, /* 0xD0-0xD3 */
+ 0xB3, 0xC6, 0x86, 0x62, 0x86, 0x63, 0x86, 0x64, /* 0xD4-0xD7 */
+ 0xB3, 0xC7, 0x86, 0x65, 0x86, 0x66, 0x86, 0x67, /* 0xD8-0xDB */
+ 0x86, 0x68, 0x86, 0x69, 0x86, 0x6A, 0x86, 0x6B, /* 0xDC-0xDF */
+ 0xB3, 0xC8, 0x86, 0x6C, 0x86, 0x6D, 0x86, 0x6E, /* 0xE0-0xE3 */
+ 0x86, 0x6F, 0xB3, 0xC9, 0x86, 0x70, 0x86, 0x71, /* 0xE4-0xE7 */
+ 0x86, 0x72, 0x86, 0x73, 0x86, 0x74, 0x86, 0x75, /* 0xE8-0xEB */
+ 0x86, 0x76, 0x86, 0x77, 0x86, 0x78, 0x86, 0x79, /* 0xEC-0xEF */
+ 0x86, 0x7A, 0x86, 0x81, 0x86, 0x82, 0x86, 0x83, /* 0xF0-0xF3 */
+ 0x86, 0x84, 0x86, 0x85, 0x86, 0x86, 0x86, 0x87, /* 0xF4-0xF7 */
+ 0x86, 0x88, 0x86, 0x89, 0x86, 0x8A, 0x86, 0x8B, /* 0xF8-0xFB */
+ 0x86, 0x8C, 0x86, 0x8D, 0x86, 0x8E, 0x86, 0x8F, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_B1[512] = {
+ 0x86, 0x90, 0x86, 0x91, 0x86, 0x92, 0x86, 0x93, /* 0x00-0x03 */
+ 0x86, 0x94, 0x86, 0x95, 0x86, 0x96, 0x86, 0x97, /* 0x04-0x07 */
+ 0xB3, 0xCA, 0xB3, 0xCB, 0x86, 0x98, 0xB3, 0xCC, /* 0x08-0x0B */
+ 0xB3, 0xCD, 0x86, 0x99, 0x86, 0x9A, 0x86, 0x9B, /* 0x0C-0x0F */
+ 0xB3, 0xCE, 0x86, 0x9C, 0xB3, 0xCF, 0xB3, 0xD0, /* 0x10-0x13 */
+ 0x86, 0x9D, 0x86, 0x9E, 0x86, 0x9F, 0x86, 0xA0, /* 0x14-0x17 */
+ 0xB3, 0xD1, 0xB3, 0xD2, 0x86, 0xA1, 0xB3, 0xD3, /* 0x18-0x1B */
+ 0xB3, 0xD4, 0xB3, 0xD5, 0x86, 0xA2, 0x86, 0xA3, /* 0x1C-0x1F */
+ 0x86, 0xA4, 0x86, 0xA5, 0x86, 0xA6, 0xB3, 0xD6, /* 0x20-0x23 */
+ 0xB3, 0xD7, 0xB3, 0xD8, 0x86, 0xA7, 0x86, 0xA8, /* 0x24-0x27 */
+ 0xB3, 0xD9, 0x86, 0xA9, 0x86, 0xAA, 0x86, 0xAB, /* 0x28-0x2B */
+ 0xB3, 0xDA, 0x86, 0xAC, 0x86, 0xAD, 0x86, 0xAE, /* 0x2C-0x2F */
+ 0x86, 0xAF, 0x86, 0xB0, 0x86, 0xB1, 0x86, 0xB2, /* 0x30-0x33 */
+ 0xB3, 0xDB, 0xB3, 0xDC, 0x86, 0xB3, 0xB3, 0xDD, /* 0x34-0x37 */
+ 0xB3, 0xDE, 0xB3, 0xDF, 0x86, 0xB4, 0x86, 0xB5, /* 0x38-0x3B */
+ 0x86, 0xB6, 0x86, 0xB7, 0x86, 0xB8, 0x86, 0xB9, /* 0x3C-0x3F */
+ 0xB3, 0xE0, 0xB3, 0xE1, 0x86, 0xBA, 0x86, 0xBB, /* 0x40-0x43 */
+ 0xB3, 0xE2, 0x86, 0xBC, 0x86, 0xBD, 0x86, 0xBE, /* 0x44-0x47 */
+ 0xB3, 0xE3, 0x86, 0xBF, 0x86, 0xC0, 0x86, 0xC1, /* 0x48-0x4B */
+ 0x86, 0xC2, 0x86, 0xC3, 0x86, 0xC4, 0x86, 0xC5, /* 0x4C-0x4F */
+ 0xB3, 0xE4, 0xB3, 0xE5, 0x86, 0xC6, 0x86, 0xC7, /* 0x50-0x53 */
+ 0xB3, 0xE6, 0xB3, 0xE7, 0x86, 0xC8, 0x86, 0xC9, /* 0x54-0x57 */
+ 0xB3, 0xE8, 0x86, 0xCA, 0x86, 0xCB, 0x86, 0xCC, /* 0x58-0x5B */
+ 0xB3, 0xE9, 0x86, 0xCD, 0x86, 0xCE, 0x86, 0xCF, /* 0x5C-0x5F */
+ 0xB3, 0xEA, 0x86, 0xD0, 0x86, 0xD1, 0x86, 0xD2, /* 0x60-0x63 */
+ 0x86, 0xD3, 0x86, 0xD4, 0x86, 0xD5, 0x86, 0xD6, /* 0x64-0x67 */
+ 0x86, 0xD7, 0x86, 0xD8, 0x86, 0xD9, 0x86, 0xDA, /* 0x68-0x6B */
+ 0x86, 0xDB, 0x86, 0xDC, 0x86, 0xDD, 0x86, 0xDE, /* 0x6C-0x6F */
+ 0x86, 0xDF, 0x86, 0xE0, 0x86, 0xE1, 0x86, 0xE2, /* 0x70-0x73 */
+ 0x86, 0xE3, 0x86, 0xE4, 0x86, 0xE5, 0x86, 0xE6, /* 0x74-0x77 */
+ 0xB3, 0xEB, 0xB3, 0xEC, 0x86, 0xE7, 0x86, 0xE8, /* 0x78-0x7B */
+ 0xB3, 0xED, 0x86, 0xE9, 0x86, 0xEA, 0x86, 0xEB, /* 0x7C-0x7F */
+
+ 0xB3, 0xEE, 0x86, 0xEC, 0xB3, 0xEF, 0x86, 0xED, /* 0x80-0x83 */
+ 0x86, 0xEE, 0x86, 0xEF, 0x86, 0xF0, 0x86, 0xF1, /* 0x84-0x87 */
+ 0xB3, 0xF0, 0xB3, 0xF1, 0x86, 0xF2, 0xB3, 0xF2, /* 0x88-0x8B */
+ 0x86, 0xF3, 0xB3, 0xF3, 0x86, 0xF4, 0x86, 0xF5, /* 0x8C-0x8F */
+ 0x86, 0xF6, 0x86, 0xF7, 0xB3, 0xF4, 0xB3, 0xF5, /* 0x90-0x93 */
+ 0xB3, 0xF6, 0x86, 0xF8, 0x86, 0xF9, 0x86, 0xFA, /* 0x94-0x97 */
+ 0xB3, 0xF7, 0x86, 0xFB, 0x86, 0xFC, 0x86, 0xFD, /* 0x98-0x9B */
+ 0xB3, 0xF8, 0x86, 0xFE, 0x87, 0x41, 0x87, 0x42, /* 0x9C-0x9F */
+ 0x87, 0x43, 0x87, 0x44, 0x87, 0x45, 0x87, 0x46, /* 0xA0-0xA3 */
+ 0x87, 0x47, 0x87, 0x48, 0x87, 0x49, 0x87, 0x4A, /* 0xA4-0xA7 */
+ 0xB3, 0xF9, 0x87, 0x4B, 0x87, 0x4C, 0x87, 0x4D, /* 0xA8-0xAB */
+ 0x87, 0x4E, 0x87, 0x4F, 0x87, 0x50, 0x87, 0x51, /* 0xAC-0xAF */
+ 0x87, 0x52, 0x87, 0x53, 0x87, 0x54, 0x87, 0x55, /* 0xB0-0xB3 */
+ 0x87, 0x56, 0x87, 0x57, 0x87, 0x58, 0x87, 0x59, /* 0xB4-0xB7 */
+ 0x87, 0x5A, 0x87, 0x61, 0x87, 0x62, 0x87, 0x63, /* 0xB8-0xBB */
+ 0x87, 0x64, 0x87, 0x65, 0x87, 0x66, 0x87, 0x67, /* 0xBC-0xBF */
+ 0x87, 0x68, 0x87, 0x69, 0x87, 0x6A, 0x87, 0x6B, /* 0xC0-0xC3 */
+ 0x87, 0x6C, 0x87, 0x6D, 0x87, 0x6E, 0x87, 0x6F, /* 0xC4-0xC7 */
+ 0x87, 0x70, 0x87, 0x71, 0x87, 0x72, 0x87, 0x73, /* 0xC8-0xCB */
+ 0xB3, 0xFA, 0x87, 0x74, 0x87, 0x75, 0x87, 0x76, /* 0xCC-0xCF */
+ 0xB3, 0xFB, 0x87, 0x77, 0x87, 0x78, 0x87, 0x79, /* 0xD0-0xD3 */
+ 0xB3, 0xFC, 0x87, 0x7A, 0x87, 0x81, 0x87, 0x82, /* 0xD4-0xD7 */
+ 0x87, 0x83, 0x87, 0x84, 0x87, 0x85, 0x87, 0x86, /* 0xD8-0xDB */
+ 0xB3, 0xFD, 0xB3, 0xFE, 0x87, 0x87, 0xB4, 0xA1, /* 0xDC-0xDF */
+ 0x87, 0x88, 0x87, 0x89, 0x87, 0x8A, 0x87, 0x8B, /* 0xE0-0xE3 */
+ 0x87, 0x8C, 0x87, 0x8D, 0x87, 0x8E, 0x87, 0x8F, /* 0xE4-0xE7 */
+ 0xB4, 0xA2, 0xB4, 0xA3, 0x87, 0x90, 0x87, 0x91, /* 0xE8-0xEB */
+ 0xB4, 0xA4, 0x87, 0x92, 0x87, 0x93, 0x87, 0x94, /* 0xEC-0xEF */
+ 0xB4, 0xA5, 0x87, 0x95, 0x87, 0x96, 0x87, 0x97, /* 0xF0-0xF3 */
+ 0x87, 0x98, 0x87, 0x99, 0x87, 0x9A, 0x87, 0x9B, /* 0xF4-0xF7 */
+ 0x87, 0x9C, 0xB4, 0xA6, 0x87, 0x9D, 0xB4, 0xA7, /* 0xF8-0xFB */
+ 0x87, 0x9E, 0xB4, 0xA8, 0x87, 0x9F, 0x87, 0xA0, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_B2[512] = {
+ 0x87, 0xA1, 0x87, 0xA2, 0x87, 0xA3, 0x87, 0xA4, /* 0x00-0x03 */
+ 0xB4, 0xA9, 0xB4, 0xAA, 0x87, 0xA5, 0x87, 0xA6, /* 0x04-0x07 */
+ 0xB4, 0xAB, 0x87, 0xA7, 0x87, 0xA8, 0xB4, 0xAC, /* 0x08-0x0B */
+ 0xB4, 0xAD, 0x87, 0xA9, 0x87, 0xAA, 0x87, 0xAB, /* 0x0C-0x0F */
+ 0x87, 0xAC, 0x87, 0xAD, 0x87, 0xAE, 0x87, 0xAF, /* 0x10-0x13 */
+ 0xB4, 0xAE, 0xB4, 0xAF, 0x87, 0xB0, 0xB4, 0xB0, /* 0x14-0x17 */
+ 0x87, 0xB1, 0xB4, 0xB1, 0x87, 0xB2, 0x87, 0xB3, /* 0x18-0x1B */
+ 0x87, 0xB4, 0x87, 0xB5, 0x87, 0xB6, 0x87, 0xB7, /* 0x1C-0x1F */
+ 0xB4, 0xB2, 0x87, 0xB8, 0x87, 0xB9, 0x87, 0xBA, /* 0x20-0x23 */
+ 0x87, 0xBB, 0x87, 0xBC, 0x87, 0xBD, 0x87, 0xBE, /* 0x24-0x27 */
+ 0x87, 0xBF, 0x87, 0xC0, 0x87, 0xC1, 0x87, 0xC2, /* 0x28-0x2B */
+ 0x87, 0xC3, 0x87, 0xC4, 0x87, 0xC5, 0x87, 0xC6, /* 0x2C-0x2F */
+ 0x87, 0xC7, 0x87, 0xC8, 0x87, 0xC9, 0x87, 0xCA, /* 0x30-0x33 */
+ 0xB4, 0xB3, 0x87, 0xCB, 0x87, 0xCC, 0x87, 0xCD, /* 0x34-0x37 */
+ 0x87, 0xCE, 0x87, 0xCF, 0x87, 0xD0, 0x87, 0xD1, /* 0x38-0x3B */
+ 0xB4, 0xB4, 0x87, 0xD2, 0x87, 0xD3, 0x87, 0xD4, /* 0x3C-0x3F */
+ 0x87, 0xD5, 0x87, 0xD6, 0x87, 0xD7, 0x87, 0xD8, /* 0x40-0x43 */
+ 0x87, 0xD9, 0x87, 0xDA, 0x87, 0xDB, 0x87, 0xDC, /* 0x44-0x47 */
+ 0x87, 0xDD, 0x87, 0xDE, 0x87, 0xDF, 0x87, 0xE0, /* 0x48-0x4B */
+ 0x87, 0xE1, 0x87, 0xE2, 0x87, 0xE3, 0x87, 0xE4, /* 0x4C-0x4F */
+ 0x87, 0xE5, 0x87, 0xE6, 0x87, 0xE7, 0x87, 0xE8, /* 0x50-0x53 */
+ 0x87, 0xE9, 0x87, 0xEA, 0x87, 0xEB, 0x87, 0xEC, /* 0x54-0x57 */
+ 0xB4, 0xB5, 0x87, 0xED, 0x87, 0xEE, 0x87, 0xEF, /* 0x58-0x5B */
+ 0xB4, 0xB6, 0x87, 0xF0, 0x87, 0xF1, 0x87, 0xF2, /* 0x5C-0x5F */
+ 0xB4, 0xB7, 0x87, 0xF3, 0x87, 0xF4, 0x87, 0xF5, /* 0x60-0x63 */
+ 0x87, 0xF6, 0x87, 0xF7, 0x87, 0xF8, 0x87, 0xF9, /* 0x64-0x67 */
+ 0xB4, 0xB8, 0xB4, 0xB9, 0x87, 0xFA, 0x87, 0xFB, /* 0x68-0x6B */
+ 0x87, 0xFC, 0x87, 0xFD, 0x87, 0xFE, 0x88, 0x41, /* 0x6C-0x6F */
+ 0x88, 0x42, 0x88, 0x43, 0x88, 0x44, 0x88, 0x45, /* 0x70-0x73 */
+ 0xB4, 0xBA, 0xB4, 0xBB, 0x88, 0x46, 0x88, 0x47, /* 0x74-0x77 */
+ 0x88, 0x48, 0x88, 0x49, 0x88, 0x4A, 0x88, 0x4B, /* 0x78-0x7B */
+ 0xB4, 0xBC, 0x88, 0x4C, 0x88, 0x4D, 0x88, 0x4E, /* 0x7C-0x7F */
+
+ 0x88, 0x4F, 0x88, 0x50, 0x88, 0x51, 0x88, 0x52, /* 0x80-0x83 */
+ 0xB4, 0xBD, 0xB4, 0xBE, 0x88, 0x53, 0x88, 0x54, /* 0x84-0x87 */
+ 0x88, 0x55, 0xB4, 0xBF, 0x88, 0x56, 0x88, 0x57, /* 0x88-0x8B */
+ 0x88, 0x58, 0x88, 0x59, 0x88, 0x5A, 0x88, 0x61, /* 0x8C-0x8F */
+ 0xB4, 0xC0, 0xB4, 0xC1, 0x88, 0x62, 0x88, 0x63, /* 0x90-0x93 */
+ 0xB4, 0xC2, 0x88, 0x64, 0x88, 0x65, 0x88, 0x66, /* 0x94-0x97 */
+ 0xB4, 0xC3, 0xB4, 0xC4, 0xB4, 0xC5, 0x88, 0x67, /* 0x98-0x9B */
+ 0x88, 0x68, 0x88, 0x69, 0x88, 0x6A, 0x88, 0x6B, /* 0x9C-0x9F */
+ 0xB4, 0xC6, 0xB4, 0xC7, 0x88, 0x6C, 0xB4, 0xC8, /* 0xA0-0xA3 */
+ 0x88, 0x6D, 0xB4, 0xC9, 0xB4, 0xCA, 0x88, 0x6E, /* 0xA4-0xA7 */
+ 0x88, 0x6F, 0x88, 0x70, 0xB4, 0xCB, 0x88, 0x71, /* 0xA8-0xAB */
+ 0xB4, 0xCC, 0x88, 0x72, 0x88, 0x73, 0x88, 0x74, /* 0xAC-0xAF */
+ 0xB4, 0xCD, 0x88, 0x75, 0x88, 0x76, 0x88, 0x77, /* 0xB0-0xB3 */
+ 0xB4, 0xCE, 0x88, 0x78, 0x88, 0x79, 0x88, 0x7A, /* 0xB4-0xB7 */
+ 0x88, 0x81, 0x88, 0x82, 0x88, 0x83, 0x88, 0x84, /* 0xB8-0xBB */
+ 0x88, 0x85, 0x88, 0x86, 0x88, 0x87, 0x88, 0x88, /* 0xBC-0xBF */
+ 0x88, 0x89, 0x88, 0x8A, 0x88, 0x8B, 0x88, 0x8C, /* 0xC0-0xC3 */
+ 0x88, 0x8D, 0x88, 0x8E, 0x88, 0x8F, 0x88, 0x90, /* 0xC4-0xC7 */
+ 0xB4, 0xCF, 0xB4, 0xD0, 0x88, 0x91, 0x88, 0x92, /* 0xC8-0xCB */
+ 0xB4, 0xD1, 0x88, 0x93, 0x88, 0x94, 0x88, 0x95, /* 0xCC-0xCF */
+ 0xB4, 0xD2, 0x88, 0x96, 0xB4, 0xD3, 0x88, 0x97, /* 0xD0-0xD3 */
+ 0x88, 0x98, 0x88, 0x99, 0x88, 0x9A, 0x88, 0x9B, /* 0xD4-0xD7 */
+ 0xB4, 0xD4, 0xB4, 0xD5, 0x88, 0x9C, 0xB4, 0xD6, /* 0xD8-0xDB */
+ 0x88, 0x9D, 0xB4, 0xD7, 0x88, 0x9E, 0x88, 0x9F, /* 0xDC-0xDF */
+ 0x88, 0xA0, 0x88, 0xA1, 0xB4, 0xD8, 0x88, 0xA2, /* 0xE0-0xE3 */
+ 0xB4, 0xD9, 0xB4, 0xDA, 0xB4, 0xDB, 0x88, 0xA3, /* 0xE4-0xE7 */
+ 0xB4, 0xDC, 0x88, 0xA4, 0x88, 0xA5, 0xB4, 0xDD, /* 0xE8-0xEB */
+ 0xB4, 0xDE, 0xB4, 0xDF, 0xB4, 0xE0, 0xB4, 0xE1, /* 0xEC-0xEF */
+ 0x88, 0xA6, 0x88, 0xA7, 0x88, 0xA8, 0xB4, 0xE2, /* 0xF0-0xF3 */
+ 0xB4, 0xE3, 0xB4, 0xE4, 0x88, 0xA9, 0xB4, 0xE5, /* 0xF4-0xF7 */
+ 0xB4, 0xE6, 0xB4, 0xE7, 0xB4, 0xE8, 0xB4, 0xE9, /* 0xF8-0xFB */
+ 0x88, 0xAA, 0x88, 0xAB, 0x88, 0xAC, 0xB4, 0xEA, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_B3[512] = {
+ 0xB4, 0xEB, 0xB4, 0xEC, 0x88, 0xAD, 0x88, 0xAE, /* 0x00-0x03 */
+ 0xB4, 0xED, 0x88, 0xAF, 0x88, 0xB0, 0x88, 0xB1, /* 0x04-0x07 */
+ 0xB4, 0xEE, 0x88, 0xB2, 0x88, 0xB3, 0x88, 0xB4, /* 0x08-0x0B */
+ 0x88, 0xB5, 0x88, 0xB6, 0x88, 0xB7, 0x88, 0xB8, /* 0x0C-0x0F */
+ 0xB4, 0xEF, 0xB4, 0xF0, 0x88, 0xB9, 0xB4, 0xF1, /* 0x10-0x13 */
+ 0xB4, 0xF2, 0xB4, 0xF3, 0x88, 0xBA, 0x88, 0xBB, /* 0x14-0x17 */
+ 0x88, 0xBC, 0x88, 0xBD, 0x88, 0xBE, 0x88, 0xBF, /* 0x18-0x1B */
+ 0xB4, 0xF4, 0x88, 0xC0, 0x88, 0xC1, 0x88, 0xC2, /* 0x1C-0x1F */
+ 0x88, 0xC3, 0x88, 0xC4, 0x88, 0xC5, 0x88, 0xC6, /* 0x20-0x23 */
+ 0x88, 0xC7, 0x88, 0xC8, 0x88, 0xC9, 0x88, 0xCA, /* 0x24-0x27 */
+ 0x88, 0xCB, 0x88, 0xCC, 0x88, 0xCD, 0x88, 0xCE, /* 0x28-0x2B */
+ 0x88, 0xCF, 0x88, 0xD0, 0x88, 0xD1, 0x88, 0xD2, /* 0x2C-0x2F */
+ 0x88, 0xD3, 0x88, 0xD4, 0x88, 0xD5, 0x88, 0xD6, /* 0x30-0x33 */
+ 0x88, 0xD7, 0x88, 0xD8, 0x88, 0xD9, 0x88, 0xDA, /* 0x34-0x37 */
+ 0x88, 0xDB, 0x88, 0xDC, 0x88, 0xDD, 0x88, 0xDE, /* 0x38-0x3B */
+ 0x88, 0xDF, 0x88, 0xE0, 0x88, 0xE1, 0x88, 0xE2, /* 0x3C-0x3F */
+ 0x88, 0xE3, 0x88, 0xE4, 0x88, 0xE5, 0x88, 0xE6, /* 0x40-0x43 */
+ 0x88, 0xE7, 0x88, 0xE8, 0x88, 0xE9, 0x88, 0xEA, /* 0x44-0x47 */
+ 0x88, 0xEB, 0x88, 0xEC, 0x88, 0xED, 0x88, 0xEE, /* 0x48-0x4B */
+ 0x88, 0xEF, 0x88, 0xF0, 0x88, 0xF1, 0x88, 0xF2, /* 0x4C-0x4F */
+ 0x88, 0xF3, 0x88, 0xF4, 0x88, 0xF5, 0x88, 0xF6, /* 0x50-0x53 */
+ 0xB4, 0xF5, 0xB4, 0xF6, 0xB4, 0xF7, 0x88, 0xF7, /* 0x54-0x57 */
+ 0xB4, 0xF8, 0x88, 0xF8, 0x88, 0xF9, 0xB4, 0xF9, /* 0x58-0x5B */
+ 0xB4, 0xFA, 0x88, 0xFA, 0xB4, 0xFB, 0xB4, 0xFC, /* 0x5C-0x5F */
+ 0x88, 0xFB, 0x88, 0xFC, 0x88, 0xFD, 0x88, 0xFE, /* 0x60-0x63 */
+ 0xB4, 0xFD, 0xB4, 0xFE, 0x89, 0x41, 0xB5, 0xA1, /* 0x64-0x67 */
+ 0x89, 0x42, 0xB5, 0xA2, 0x89, 0x43, 0xB5, 0xA3, /* 0x68-0x6B */
+ 0x89, 0x44, 0x89, 0x45, 0xB5, 0xA4, 0x89, 0x46, /* 0x6C-0x6F */
+ 0xB5, 0xA5, 0xB5, 0xA6, 0x89, 0x47, 0x89, 0x48, /* 0x70-0x73 */
+ 0xB5, 0xA7, 0x89, 0x49, 0x89, 0x4A, 0x89, 0x4B, /* 0x74-0x77 */
+ 0xB5, 0xA8, 0x89, 0x4C, 0x89, 0x4D, 0x89, 0x4E, /* 0x78-0x7B */
+ 0x89, 0x4F, 0x89, 0x50, 0x89, 0x51, 0x89, 0x52, /* 0x7C-0x7F */
+
+ 0xB5, 0xA9, 0xB5, 0xAA, 0x89, 0x53, 0xB5, 0xAB, /* 0x80-0x83 */
+ 0xB5, 0xAC, 0xB5, 0xAD, 0x89, 0x54, 0x89, 0x55, /* 0x84-0x87 */
+ 0x89, 0x56, 0x89, 0x57, 0x89, 0x58, 0x89, 0x59, /* 0x88-0x8B */
+ 0xB5, 0xAE, 0x89, 0x5A, 0x89, 0x61, 0x89, 0x62, /* 0x8C-0x8F */
+ 0xB5, 0xAF, 0x89, 0x63, 0x89, 0x64, 0x89, 0x65, /* 0x90-0x93 */
+ 0xB5, 0xB0, 0x89, 0x66, 0x89, 0x67, 0x89, 0x68, /* 0x94-0x97 */
+ 0x89, 0x69, 0x89, 0x6A, 0x89, 0x6B, 0x89, 0x6C, /* 0x98-0x9B */
+ 0x89, 0x6D, 0x89, 0x6E, 0x89, 0x6F, 0x89, 0x70, /* 0x9C-0x9F */
+ 0xB5, 0xB1, 0xB5, 0xB2, 0x89, 0x71, 0x89, 0x72, /* 0xA0-0xA3 */
+ 0x89, 0x73, 0x89, 0x74, 0x89, 0x75, 0x89, 0x76, /* 0xA4-0xA7 */
+ 0xB5, 0xB3, 0x89, 0x77, 0x89, 0x78, 0x89, 0x79, /* 0xA8-0xAB */
+ 0xB5, 0xB4, 0x89, 0x7A, 0x89, 0x81, 0x89, 0x82, /* 0xAC-0xAF */
+ 0x89, 0x83, 0x89, 0x84, 0x89, 0x85, 0x89, 0x86, /* 0xB0-0xB3 */
+ 0x89, 0x87, 0x89, 0x88, 0x89, 0x89, 0x89, 0x8A, /* 0xB4-0xB7 */
+ 0x89, 0x8B, 0x89, 0x8C, 0x89, 0x8D, 0x89, 0x8E, /* 0xB8-0xBB */
+ 0x89, 0x8F, 0x89, 0x90, 0x89, 0x91, 0x89, 0x92, /* 0xBC-0xBF */
+ 0x89, 0x93, 0x89, 0x94, 0x89, 0x95, 0x89, 0x96, /* 0xC0-0xC3 */
+ 0xB5, 0xB5, 0xB5, 0xB6, 0x89, 0x97, 0x89, 0x98, /* 0xC4-0xC7 */
+ 0xB5, 0xB7, 0x89, 0x99, 0x89, 0x9A, 0xB5, 0xB8, /* 0xC8-0xCB */
+ 0xB5, 0xB9, 0x89, 0x9B, 0xB5, 0xBA, 0x89, 0x9C, /* 0xCC-0xCF */
+ 0xB5, 0xBB, 0x89, 0x9D, 0x89, 0x9E, 0x89, 0x9F, /* 0xD0-0xD3 */
+ 0xB5, 0xBC, 0xB5, 0xBD, 0x89, 0xA0, 0xB5, 0xBE, /* 0xD4-0xD7 */
+ 0x89, 0xA1, 0xB5, 0xBF, 0x89, 0xA2, 0xB5, 0xC0, /* 0xD8-0xDB */
+ 0x89, 0xA3, 0xB5, 0xC1, 0x89, 0xA4, 0x89, 0xA5, /* 0xDC-0xDF */
+ 0xB5, 0xC2, 0x89, 0xA6, 0x89, 0xA7, 0x89, 0xA8, /* 0xE0-0xE3 */
+ 0xB5, 0xC3, 0x89, 0xA9, 0x89, 0xAA, 0x89, 0xAB, /* 0xE4-0xE7 */
+ 0xB5, 0xC4, 0x89, 0xAC, 0x89, 0xAD, 0x89, 0xAE, /* 0xE8-0xEB */
+ 0x89, 0xAF, 0x89, 0xB0, 0x89, 0xB1, 0x89, 0xB2, /* 0xEC-0xEF */
+ 0x89, 0xB3, 0x89, 0xB4, 0x89, 0xB5, 0x89, 0xB6, /* 0xF0-0xF3 */
+ 0x89, 0xB7, 0x89, 0xB8, 0x89, 0xB9, 0x89, 0xBA, /* 0xF4-0xF7 */
+ 0x89, 0xBB, 0x89, 0xBC, 0x89, 0xBD, 0x89, 0xBE, /* 0xF8-0xFB */
+ 0xB5, 0xC5, 0x89, 0xBF, 0x89, 0xC0, 0x89, 0xC1, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_B4[512] = {
+ 0x89, 0xC2, 0x89, 0xC3, 0x89, 0xC4, 0x89, 0xC5, /* 0x00-0x03 */
+ 0x89, 0xC6, 0x89, 0xC7, 0x89, 0xC8, 0x89, 0xC9, /* 0x04-0x07 */
+ 0x89, 0xCA, 0x89, 0xCB, 0x89, 0xCC, 0x89, 0xCD, /* 0x08-0x0B */
+ 0x89, 0xCE, 0x89, 0xCF, 0x89, 0xD0, 0x89, 0xD1, /* 0x0C-0x0F */
+ 0xB5, 0xC6, 0x89, 0xD2, 0x89, 0xD3, 0x89, 0xD4, /* 0x10-0x13 */
+ 0x89, 0xD5, 0x89, 0xD6, 0x89, 0xD7, 0x89, 0xD8, /* 0x14-0x17 */
+ 0xB5, 0xC7, 0x89, 0xD9, 0x89, 0xDA, 0x89, 0xDB, /* 0x18-0x1B */
+ 0xB5, 0xC8, 0x89, 0xDC, 0x89, 0xDD, 0x89, 0xDE, /* 0x1C-0x1F */
+ 0xB5, 0xC9, 0x89, 0xDF, 0x89, 0xE0, 0x89, 0xE1, /* 0x20-0x23 */
+ 0x89, 0xE2, 0x89, 0xE3, 0x89, 0xE4, 0x89, 0xE5, /* 0x24-0x27 */
+ 0xB5, 0xCA, 0xB5, 0xCB, 0x89, 0xE6, 0xB5, 0xCC, /* 0x28-0x2B */
+ 0x89, 0xE7, 0x89, 0xE8, 0x89, 0xE9, 0x89, 0xEA, /* 0x2C-0x2F */
+ 0x89, 0xEB, 0x89, 0xEC, 0x89, 0xED, 0x89, 0xEE, /* 0x30-0x33 */
+ 0xB5, 0xCD, 0x89, 0xEF, 0x89, 0xF0, 0x89, 0xF1, /* 0x34-0x37 */
+ 0x89, 0xF2, 0x89, 0xF3, 0x89, 0xF4, 0x89, 0xF5, /* 0x38-0x3B */
+ 0x89, 0xF6, 0x89, 0xF7, 0x89, 0xF8, 0x89, 0xF9, /* 0x3C-0x3F */
+ 0x89, 0xFA, 0x89, 0xFB, 0x89, 0xFC, 0x89, 0xFD, /* 0x40-0x43 */
+ 0x89, 0xFE, 0x8A, 0x41, 0x8A, 0x42, 0x8A, 0x43, /* 0x44-0x47 */
+ 0x8A, 0x44, 0x8A, 0x45, 0x8A, 0x46, 0x8A, 0x47, /* 0x48-0x4B */
+ 0x8A, 0x48, 0x8A, 0x49, 0x8A, 0x4A, 0x8A, 0x4B, /* 0x4C-0x4F */
+ 0xB5, 0xCE, 0xB5, 0xCF, 0x8A, 0x4C, 0x8A, 0x4D, /* 0x50-0x53 */
+ 0xB5, 0xD0, 0x8A, 0x4E, 0x8A, 0x4F, 0x8A, 0x50, /* 0x54-0x57 */
+ 0xB5, 0xD1, 0x8A, 0x51, 0x8A, 0x52, 0x8A, 0x53, /* 0x58-0x5B */
+ 0x8A, 0x54, 0x8A, 0x55, 0x8A, 0x56, 0x8A, 0x57, /* 0x5C-0x5F */
+ 0xB5, 0xD2, 0xB5, 0xD3, 0x8A, 0x58, 0xB5, 0xD4, /* 0x60-0x63 */
+ 0x8A, 0x59, 0xB5, 0xD5, 0x8A, 0x5A, 0x8A, 0x61, /* 0x64-0x67 */
+ 0x8A, 0x62, 0x8A, 0x63, 0x8A, 0x64, 0x8A, 0x65, /* 0x68-0x6B */
+ 0xB5, 0xD6, 0x8A, 0x66, 0x8A, 0x67, 0x8A, 0x68, /* 0x6C-0x6F */
+ 0x8A, 0x69, 0x8A, 0x6A, 0x8A, 0x6B, 0x8A, 0x6C, /* 0x70-0x73 */
+ 0x8A, 0x6D, 0x8A, 0x6E, 0x8A, 0x6F, 0x8A, 0x70, /* 0x74-0x77 */
+ 0x8A, 0x71, 0x8A, 0x72, 0x8A, 0x73, 0x8A, 0x74, /* 0x78-0x7B */
+ 0x8A, 0x75, 0x8A, 0x76, 0x8A, 0x77, 0x8A, 0x78, /* 0x7C-0x7F */
+
+ 0xB5, 0xD7, 0x8A, 0x79, 0x8A, 0x7A, 0x8A, 0x81, /* 0x80-0x83 */
+ 0x8A, 0x82, 0x8A, 0x83, 0x8A, 0x84, 0x8A, 0x85, /* 0x84-0x87 */
+ 0xB5, 0xD8, 0x8A, 0x86, 0x8A, 0x87, 0x8A, 0x88, /* 0x88-0x8B */
+ 0x8A, 0x89, 0x8A, 0x8A, 0x8A, 0x8B, 0x8A, 0x8C, /* 0x8C-0x8F */
+ 0x8A, 0x8D, 0x8A, 0x8E, 0x8A, 0x8F, 0x8A, 0x90, /* 0x90-0x93 */
+ 0x8A, 0x91, 0x8A, 0x92, 0x8A, 0x93, 0x8A, 0x94, /* 0x94-0x97 */
+ 0x8A, 0x95, 0x8A, 0x96, 0x8A, 0x97, 0x8A, 0x98, /* 0x98-0x9B */
+ 0x8A, 0x99, 0xB5, 0xD9, 0x8A, 0x9A, 0x8A, 0x9B, /* 0x9C-0x9F */
+ 0x8A, 0x9C, 0x8A, 0x9D, 0x8A, 0x9E, 0x8A, 0x9F, /* 0xA0-0xA3 */
+ 0xB5, 0xDA, 0x8A, 0xA0, 0x8A, 0xA1, 0x8A, 0xA2, /* 0xA4-0xA7 */
+ 0xB5, 0xDB, 0x8A, 0xA3, 0x8A, 0xA4, 0x8A, 0xA5, /* 0xA8-0xAB */
+ 0xB5, 0xDC, 0x8A, 0xA6, 0x8A, 0xA7, 0x8A, 0xA8, /* 0xAC-0xAF */
+ 0x8A, 0xA9, 0x8A, 0xAA, 0x8A, 0xAB, 0x8A, 0xAC, /* 0xB0-0xB3 */
+ 0x8A, 0xAD, 0xB5, 0xDD, 0x8A, 0xAE, 0xB5, 0xDE, /* 0xB4-0xB7 */
+ 0x8A, 0xAF, 0xB5, 0xDF, 0x8A, 0xB0, 0x8A, 0xB1, /* 0xB8-0xBB */
+ 0x8A, 0xB2, 0x8A, 0xB3, 0x8A, 0xB4, 0x8A, 0xB5, /* 0xBC-0xBF */
+ 0xB5, 0xE0, 0x8A, 0xB6, 0x8A, 0xB7, 0x8A, 0xB8, /* 0xC0-0xC3 */
+ 0xB5, 0xE1, 0x8A, 0xB9, 0x8A, 0xBA, 0x8A, 0xBB, /* 0xC4-0xC7 */
+ 0xB5, 0xE2, 0x8A, 0xBC, 0x8A, 0xBD, 0x8A, 0xBE, /* 0xC8-0xCB */
+ 0x8A, 0xBF, 0x8A, 0xC0, 0x8A, 0xC1, 0x8A, 0xC2, /* 0xCC-0xCF */
+ 0xB5, 0xE3, 0x8A, 0xC3, 0x8A, 0xC4, 0x8A, 0xC5, /* 0xD0-0xD3 */
+ 0x8A, 0xC6, 0xB5, 0xE4, 0x8A, 0xC7, 0x8A, 0xC8, /* 0xD4-0xD7 */
+ 0x8A, 0xC9, 0x8A, 0xCA, 0x8A, 0xCB, 0x8A, 0xCC, /* 0xD8-0xDB */
+ 0xB5, 0xE5, 0xB5, 0xE6, 0x8A, 0xCD, 0x8A, 0xCE, /* 0xDC-0xDF */
+ 0xB5, 0xE7, 0x8A, 0xCF, 0x8A, 0xD0, 0xB5, 0xE8, /* 0xE0-0xE3 */
+ 0xB5, 0xE9, 0x8A, 0xD1, 0xB5, 0xEA, 0x8A, 0xD2, /* 0xE4-0xE7 */
+ 0x8A, 0xD3, 0x8A, 0xD4, 0x8A, 0xD5, 0x8A, 0xD6, /* 0xE8-0xEB */
+ 0xB5, 0xEB, 0xB5, 0xEC, 0x8A, 0xD7, 0xB5, 0xED, /* 0xEC-0xEF */
+ 0x8A, 0xD8, 0xB5, 0xEE, 0x8A, 0xD9, 0x8A, 0xDA, /* 0xF0-0xF3 */
+ 0x8A, 0xDB, 0x8A, 0xDC, 0x8A, 0xDD, 0x8A, 0xDE, /* 0xF4-0xF7 */
+ 0xB5, 0xEF, 0x8A, 0xDF, 0x8A, 0xE0, 0x8A, 0xE1, /* 0xF8-0xFB */
+ 0x8A, 0xE2, 0x8A, 0xE3, 0x8A, 0xE4, 0x8A, 0xE5, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_B5[512] = {
+ 0x8A, 0xE6, 0x8A, 0xE7, 0x8A, 0xE8, 0x8A, 0xE9, /* 0x00-0x03 */
+ 0x8A, 0xEA, 0x8A, 0xEB, 0x8A, 0xEC, 0x8A, 0xED, /* 0x04-0x07 */
+ 0x8A, 0xEE, 0x8A, 0xEF, 0x8A, 0xF0, 0x8A, 0xF1, /* 0x08-0x0B */
+ 0x8A, 0xF2, 0x8A, 0xF3, 0x8A, 0xF4, 0x8A, 0xF5, /* 0x0C-0x0F */
+ 0x8A, 0xF6, 0x8A, 0xF7, 0x8A, 0xF8, 0x8A, 0xF9, /* 0x10-0x13 */
+ 0xB5, 0xF0, 0xB5, 0xF1, 0x8A, 0xFA, 0x8A, 0xFB, /* 0x14-0x17 */
+ 0xB5, 0xF2, 0x8A, 0xFC, 0x8A, 0xFD, 0xB5, 0xF3, /* 0x18-0x1B */
+ 0xB5, 0xF4, 0x8A, 0xFE, 0x8B, 0x41, 0x8B, 0x42, /* 0x1C-0x1F */
+ 0x8B, 0x43, 0x8B, 0x44, 0x8B, 0x45, 0x8B, 0x46, /* 0x20-0x23 */
+ 0xB5, 0xF5, 0xB5, 0xF6, 0x8B, 0x47, 0xB5, 0xF7, /* 0x24-0x27 */
+ 0xB5, 0xF8, 0xB5, 0xF9, 0xB5, 0xFA, 0x8B, 0x48, /* 0x28-0x2B */
+ 0x8B, 0x49, 0x8B, 0x4A, 0x8B, 0x4B, 0x8B, 0x4C, /* 0x2C-0x2F */
+ 0xB5, 0xFB, 0xB5, 0xFC, 0x8B, 0x4D, 0x8B, 0x4E, /* 0x30-0x33 */
+ 0xB5, 0xFD, 0x8B, 0x4F, 0x8B, 0x50, 0x8B, 0x51, /* 0x34-0x37 */
+ 0xB5, 0xFE, 0x8B, 0x52, 0x8B, 0x53, 0x8B, 0x54, /* 0x38-0x3B */
+ 0x8B, 0x55, 0x8B, 0x56, 0x8B, 0x57, 0x8B, 0x58, /* 0x3C-0x3F */
+ 0xB6, 0xA1, 0xB6, 0xA2, 0x8B, 0x59, 0xB6, 0xA3, /* 0x40-0x43 */
+ 0xB6, 0xA4, 0xB6, 0xA5, 0x8B, 0x5A, 0x8B, 0x61, /* 0x44-0x47 */
+ 0x8B, 0x62, 0x8B, 0x63, 0x8B, 0x64, 0xB6, 0xA6, /* 0x48-0x4B */
+ 0xB6, 0xA7, 0xB6, 0xA8, 0x8B, 0x65, 0x8B, 0x66, /* 0x4C-0x4F */
+ 0xB6, 0xA9, 0x8B, 0x67, 0x8B, 0x68, 0x8B, 0x69, /* 0x50-0x53 */
+ 0xB6, 0xAA, 0x8B, 0x6A, 0x8B, 0x6B, 0x8B, 0x6C, /* 0x54-0x57 */
+ 0x8B, 0x6D, 0x8B, 0x6E, 0x8B, 0x6F, 0x8B, 0x70, /* 0x58-0x5B */
+ 0xB6, 0xAB, 0xB6, 0xAC, 0x8B, 0x71, 0xB6, 0xAD, /* 0x5C-0x5F */
+ 0xB6, 0xAE, 0xB6, 0xAF, 0x8B, 0x72, 0x8B, 0x73, /* 0x60-0x63 */
+ 0x8B, 0x74, 0x8B, 0x75, 0x8B, 0x76, 0x8B, 0x77, /* 0x64-0x67 */
+ 0x8B, 0x78, 0x8B, 0x79, 0x8B, 0x7A, 0x8B, 0x81, /* 0x68-0x6B */
+ 0x8B, 0x82, 0x8B, 0x83, 0x8B, 0x84, 0x8B, 0x85, /* 0x6C-0x6F */
+ 0x8B, 0x86, 0x8B, 0x87, 0x8B, 0x88, 0x8B, 0x89, /* 0x70-0x73 */
+ 0x8B, 0x8A, 0x8B, 0x8B, 0x8B, 0x8C, 0x8B, 0x8D, /* 0x74-0x77 */
+ 0x8B, 0x8E, 0x8B, 0x8F, 0x8B, 0x90, 0x8B, 0x91, /* 0x78-0x7B */
+ 0x8B, 0x92, 0x8B, 0x93, 0x8B, 0x94, 0x8B, 0x95, /* 0x7C-0x7F */
+
+ 0x8B, 0x96, 0x8B, 0x97, 0x8B, 0x98, 0x8B, 0x99, /* 0x80-0x83 */
+ 0x8B, 0x9A, 0x8B, 0x9B, 0x8B, 0x9C, 0x8B, 0x9D, /* 0x84-0x87 */
+ 0x8B, 0x9E, 0x8B, 0x9F, 0x8B, 0xA0, 0x8B, 0xA1, /* 0x88-0x8B */
+ 0x8B, 0xA2, 0x8B, 0xA3, 0x8B, 0xA4, 0x8B, 0xA5, /* 0x8C-0x8F */
+ 0x8B, 0xA6, 0x8B, 0xA7, 0x8B, 0xA8, 0x8B, 0xA9, /* 0x90-0x93 */
+ 0x8B, 0xAA, 0x8B, 0xAB, 0x8B, 0xAC, 0x8B, 0xAD, /* 0x94-0x97 */
+ 0x8B, 0xAE, 0x8B, 0xAF, 0x8B, 0xB0, 0x8B, 0xB1, /* 0x98-0x9B */
+ 0x8B, 0xB2, 0x8B, 0xB3, 0x8B, 0xB4, 0x8B, 0xB5, /* 0x9C-0x9F */
+ 0xB6, 0xB0, 0xB6, 0xB1, 0x8B, 0xB6, 0x8B, 0xB7, /* 0xA0-0xA3 */
+ 0xB6, 0xB2, 0x8B, 0xB8, 0x8B, 0xB9, 0x8B, 0xBA, /* 0xA4-0xA7 */
+ 0xB6, 0xB3, 0x8B, 0xBB, 0xB6, 0xB4, 0xB6, 0xB5, /* 0xA8-0xAB */
+ 0x8B, 0xBC, 0x8B, 0xBD, 0x8B, 0xBE, 0x8B, 0xBF, /* 0xAC-0xAF */
+ 0xB6, 0xB6, 0xB6, 0xB7, 0x8B, 0xC0, 0xB6, 0xB8, /* 0xB0-0xB3 */
+ 0xB6, 0xB9, 0xB6, 0xBA, 0x8B, 0xC1, 0x8B, 0xC2, /* 0xB4-0xB7 */
+ 0x8B, 0xC3, 0x8B, 0xC4, 0x8B, 0xC5, 0xB6, 0xBB, /* 0xB8-0xBB */
+ 0xB6, 0xBC, 0xB6, 0xBD, 0x8B, 0xC6, 0x8B, 0xC7, /* 0xBC-0xBF */
+ 0xB6, 0xBE, 0x8B, 0xC8, 0x8B, 0xC9, 0x8B, 0xCA, /* 0xC0-0xC3 */
+ 0xB6, 0xBF, 0x8B, 0xCB, 0x8B, 0xCC, 0x8B, 0xCD, /* 0xC4-0xC7 */
+ 0x8B, 0xCE, 0x8B, 0xCF, 0x8B, 0xD0, 0x8B, 0xD1, /* 0xC8-0xCB */
+ 0xB6, 0xC0, 0xB6, 0xC1, 0x8B, 0xD2, 0xB6, 0xC2, /* 0xCC-0xCF */
+ 0xB6, 0xC3, 0xB6, 0xC4, 0x8B, 0xD3, 0x8B, 0xD4, /* 0xD0-0xD3 */
+ 0x8B, 0xD5, 0x8B, 0xD6, 0x8B, 0xD7, 0x8B, 0xD8, /* 0xD4-0xD7 */
+ 0xB6, 0xC5, 0x8B, 0xD9, 0x8B, 0xDA, 0x8B, 0xDB, /* 0xD8-0xDB */
+ 0x8B, 0xDC, 0x8B, 0xDD, 0x8B, 0xDE, 0x8B, 0xDF, /* 0xDC-0xDF */
+ 0x8B, 0xE0, 0x8B, 0xE1, 0x8B, 0xE2, 0x8B, 0xE3, /* 0xE0-0xE3 */
+ 0x8B, 0xE4, 0x8B, 0xE5, 0x8B, 0xE6, 0x8B, 0xE7, /* 0xE4-0xE7 */
+ 0x8B, 0xE8, 0x8B, 0xE9, 0x8B, 0xEA, 0x8B, 0xEB, /* 0xE8-0xEB */
+ 0xB6, 0xC6, 0x8B, 0xEC, 0x8B, 0xED, 0x8B, 0xEE, /* 0xEC-0xEF */
+ 0x8B, 0xEF, 0x8B, 0xF0, 0x8B, 0xF1, 0x8B, 0xF2, /* 0xF0-0xF3 */
+ 0x8B, 0xF3, 0x8B, 0xF4, 0x8B, 0xF5, 0x8B, 0xF6, /* 0xF4-0xF7 */
+ 0x8B, 0xF7, 0x8B, 0xF8, 0x8B, 0xF9, 0x8B, 0xFA, /* 0xF8-0xFB */
+ 0x8B, 0xFB, 0x8B, 0xFC, 0x8B, 0xFD, 0x8B, 0xFE, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_B6[512] = {
+ 0x8C, 0x41, 0x8C, 0x42, 0x8C, 0x43, 0x8C, 0x44, /* 0x00-0x03 */
+ 0x8C, 0x45, 0x8C, 0x46, 0x8C, 0x47, 0x8C, 0x48, /* 0x04-0x07 */
+ 0x8C, 0x49, 0x8C, 0x4A, 0x8C, 0x4B, 0x8C, 0x4C, /* 0x08-0x0B */
+ 0x8C, 0x4D, 0x8C, 0x4E, 0x8C, 0x4F, 0x8C, 0x50, /* 0x0C-0x0F */
+ 0xB6, 0xC7, 0xB6, 0xC8, 0x8C, 0x51, 0x8C, 0x52, /* 0x10-0x13 */
+ 0xB6, 0xC9, 0x8C, 0x53, 0x8C, 0x54, 0x8C, 0x55, /* 0x14-0x17 */
+ 0xB6, 0xCA, 0x8C, 0x56, 0x8C, 0x57, 0x8C, 0x58, /* 0x18-0x1B */
+ 0x8C, 0x59, 0x8C, 0x5A, 0x8C, 0x61, 0x8C, 0x62, /* 0x1C-0x1F */
+ 0x8C, 0x63, 0x8C, 0x64, 0x8C, 0x65, 0x8C, 0x66, /* 0x20-0x23 */
+ 0x8C, 0x67, 0xB6, 0xCB, 0x8C, 0x68, 0x8C, 0x69, /* 0x24-0x27 */
+ 0x8C, 0x6A, 0x8C, 0x6B, 0x8C, 0x6C, 0x8C, 0x6D, /* 0x28-0x2B */
+ 0xB6, 0xCC, 0x8C, 0x6E, 0x8C, 0x6F, 0x8C, 0x70, /* 0x2C-0x2F */
+ 0x8C, 0x71, 0x8C, 0x72, 0x8C, 0x73, 0x8C, 0x74, /* 0x30-0x33 */
+ 0xB6, 0xCD, 0x8C, 0x75, 0x8C, 0x76, 0x8C, 0x77, /* 0x34-0x37 */
+ 0x8C, 0x78, 0x8C, 0x79, 0x8C, 0x7A, 0x8C, 0x81, /* 0x38-0x3B */
+ 0x8C, 0x82, 0x8C, 0x83, 0x8C, 0x84, 0x8C, 0x85, /* 0x3C-0x3F */
+ 0x8C, 0x86, 0x8C, 0x87, 0x8C, 0x88, 0x8C, 0x89, /* 0x40-0x43 */
+ 0x8C, 0x8A, 0x8C, 0x8B, 0x8C, 0x8C, 0x8C, 0x8D, /* 0x44-0x47 */
+ 0xB6, 0xCE, 0x8C, 0x8E, 0x8C, 0x8F, 0x8C, 0x90, /* 0x48-0x4B */
+ 0x8C, 0x91, 0x8C, 0x92, 0x8C, 0x93, 0x8C, 0x94, /* 0x4C-0x4F */
+ 0x8C, 0x95, 0x8C, 0x96, 0x8C, 0x97, 0x8C, 0x98, /* 0x50-0x53 */
+ 0x8C, 0x99, 0x8C, 0x9A, 0x8C, 0x9B, 0x8C, 0x9C, /* 0x54-0x57 */
+ 0x8C, 0x9D, 0x8C, 0x9E, 0x8C, 0x9F, 0x8C, 0xA0, /* 0x58-0x5B */
+ 0x8C, 0xA1, 0x8C, 0xA2, 0x8C, 0xA3, 0x8C, 0xA4, /* 0x5C-0x5F */
+ 0x8C, 0xA5, 0x8C, 0xA6, 0x8C, 0xA7, 0x8C, 0xA8, /* 0x60-0x63 */
+ 0xB6, 0xCF, 0x8C, 0xA9, 0x8C, 0xAA, 0x8C, 0xAB, /* 0x64-0x67 */
+ 0xB6, 0xD0, 0x8C, 0xAC, 0x8C, 0xAD, 0x8C, 0xAE, /* 0x68-0x6B */
+ 0x8C, 0xAF, 0x8C, 0xB0, 0x8C, 0xB1, 0x8C, 0xB2, /* 0x6C-0x6F */
+ 0x8C, 0xB3, 0x8C, 0xB4, 0x8C, 0xB5, 0x8C, 0xB6, /* 0x70-0x73 */
+ 0x8C, 0xB7, 0x8C, 0xB8, 0x8C, 0xB9, 0x8C, 0xBA, /* 0x74-0x77 */
+ 0x8C, 0xBB, 0x8C, 0xBC, 0x8C, 0xBD, 0x8C, 0xBE, /* 0x78-0x7B */
+ 0x8C, 0xBF, 0x8C, 0xC0, 0x8C, 0xC1, 0x8C, 0xC2, /* 0x7C-0x7F */
+
+ 0x8C, 0xC3, 0x8C, 0xC4, 0x8C, 0xC5, 0x8C, 0xC6, /* 0x80-0x83 */
+ 0x8C, 0xC7, 0x8C, 0xC8, 0x8C, 0xC9, 0x8C, 0xCA, /* 0x84-0x87 */
+ 0x8C, 0xCB, 0x8C, 0xCC, 0x8C, 0xCD, 0x8C, 0xCE, /* 0x88-0x8B */
+ 0x8C, 0xCF, 0x8C, 0xD0, 0x8C, 0xD1, 0x8C, 0xD2, /* 0x8C-0x8F */
+ 0x8C, 0xD3, 0x8C, 0xD4, 0x8C, 0xD5, 0x8C, 0xD6, /* 0x90-0x93 */
+ 0x8C, 0xD7, 0x8C, 0xD8, 0x8C, 0xD9, 0x8C, 0xDA, /* 0x94-0x97 */
+ 0x8C, 0xDB, 0x8C, 0xDC, 0x8C, 0xDD, 0x8C, 0xDE, /* 0x98-0x9B */
+ 0xB6, 0xD1, 0xB6, 0xD2, 0x8C, 0xDF, 0x8C, 0xE0, /* 0x9C-0x9F */
+ 0xB6, 0xD3, 0x8C, 0xE1, 0x8C, 0xE2, 0x8C, 0xE3, /* 0xA0-0xA3 */
+ 0xB6, 0xD4, 0x8C, 0xE4, 0x8C, 0xE5, 0x8C, 0xE6, /* 0xA4-0xA7 */
+ 0x8C, 0xE7, 0x8C, 0xE8, 0x8C, 0xE9, 0xB6, 0xD5, /* 0xA8-0xAB */
+ 0xB6, 0xD6, 0x8C, 0xEA, 0x8C, 0xEB, 0x8C, 0xEC, /* 0xAC-0xAF */
+ 0x8C, 0xED, 0xB6, 0xD7, 0x8C, 0xEE, 0x8C, 0xEF, /* 0xB0-0xB3 */
+ 0x8C, 0xF0, 0x8C, 0xF1, 0x8C, 0xF2, 0x8C, 0xF3, /* 0xB4-0xB7 */
+ 0x8C, 0xF4, 0x8C, 0xF5, 0x8C, 0xF6, 0x8C, 0xF7, /* 0xB8-0xBB */
+ 0x8C, 0xF8, 0x8C, 0xF9, 0x8C, 0xFA, 0x8C, 0xFB, /* 0xBC-0xBF */
+ 0x8C, 0xFC, 0x8C, 0xFD, 0x8C, 0xFE, 0x8D, 0x41, /* 0xC0-0xC3 */
+ 0x8D, 0x42, 0x8D, 0x43, 0x8D, 0x44, 0x8D, 0x45, /* 0xC4-0xC7 */
+ 0x8D, 0x46, 0x8D, 0x47, 0x8D, 0x48, 0x8D, 0x49, /* 0xC8-0xCB */
+ 0x8D, 0x4A, 0x8D, 0x4B, 0x8D, 0x4C, 0x8D, 0x4D, /* 0xCC-0xCF */
+ 0x8D, 0x4E, 0x8D, 0x4F, 0x8D, 0x50, 0x8D, 0x51, /* 0xD0-0xD3 */
+ 0xB6, 0xD8, 0x8D, 0x52, 0x8D, 0x53, 0x8D, 0x54, /* 0xD4-0xD7 */
+ 0x8D, 0x55, 0x8D, 0x56, 0x8D, 0x57, 0x8D, 0x58, /* 0xD8-0xDB */
+ 0x8D, 0x59, 0x8D, 0x5A, 0x8D, 0x61, 0x8D, 0x62, /* 0xDC-0xDF */
+ 0x8D, 0x63, 0x8D, 0x64, 0x8D, 0x65, 0x8D, 0x66, /* 0xE0-0xE3 */
+ 0x8D, 0x67, 0x8D, 0x68, 0x8D, 0x69, 0x8D, 0x6A, /* 0xE4-0xE7 */
+ 0x8D, 0x6B, 0x8D, 0x6C, 0x8D, 0x6D, 0x8D, 0x6E, /* 0xE8-0xEB */
+ 0x8D, 0x6F, 0x8D, 0x70, 0x8D, 0x71, 0x8D, 0x72, /* 0xEC-0xEF */
+ 0xB6, 0xD9, 0x8D, 0x73, 0x8D, 0x74, 0x8D, 0x75, /* 0xF0-0xF3 */
+ 0xB6, 0xDA, 0x8D, 0x76, 0x8D, 0x77, 0x8D, 0x78, /* 0xF4-0xF7 */
+ 0xB6, 0xDB, 0x8D, 0x79, 0x8D, 0x7A, 0x8D, 0x81, /* 0xF8-0xFB */
+ 0x8D, 0x82, 0x8D, 0x83, 0x8D, 0x84, 0x8D, 0x85, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_B7[512] = {
+ 0xB6, 0xDC, 0xB6, 0xDD, 0x8D, 0x86, 0x8D, 0x87, /* 0x00-0x03 */
+ 0x8D, 0x88, 0xB6, 0xDE, 0x8D, 0x89, 0x8D, 0x8A, /* 0x04-0x07 */
+ 0x8D, 0x8B, 0x8D, 0x8C, 0x8D, 0x8D, 0x8D, 0x8E, /* 0x08-0x0B */
+ 0x8D, 0x8F, 0x8D, 0x90, 0x8D, 0x91, 0x8D, 0x92, /* 0x0C-0x0F */
+ 0x8D, 0x93, 0x8D, 0x94, 0x8D, 0x95, 0x8D, 0x96, /* 0x10-0x13 */
+ 0x8D, 0x97, 0x8D, 0x98, 0x8D, 0x99, 0x8D, 0x9A, /* 0x14-0x17 */
+ 0x8D, 0x9B, 0x8D, 0x9C, 0x8D, 0x9D, 0x8D, 0x9E, /* 0x18-0x1B */
+ 0x8D, 0x9F, 0x8D, 0xA0, 0x8D, 0xA1, 0x8D, 0xA2, /* 0x1C-0x1F */
+ 0x8D, 0xA3, 0x8D, 0xA4, 0x8D, 0xA5, 0x8D, 0xA6, /* 0x20-0x23 */
+ 0x8D, 0xA7, 0x8D, 0xA8, 0x8D, 0xA9, 0x8D, 0xAA, /* 0x24-0x27 */
+ 0xB6, 0xDF, 0xB6, 0xE0, 0x8D, 0xAB, 0x8D, 0xAC, /* 0x28-0x2B */
+ 0xB6, 0xE1, 0x8D, 0xAD, 0x8D, 0xAE, 0xB6, 0xE2, /* 0x2C-0x2F */
+ 0xB6, 0xE3, 0x8D, 0xAF, 0x8D, 0xB0, 0x8D, 0xB1, /* 0x30-0x33 */
+ 0x8D, 0xB2, 0x8D, 0xB3, 0x8D, 0xB4, 0x8D, 0xB5, /* 0x34-0x37 */
+ 0xB6, 0xE4, 0xB6, 0xE5, 0x8D, 0xB6, 0xB6, 0xE6, /* 0x38-0x3B */
+ 0x8D, 0xB7, 0x8D, 0xB8, 0x8D, 0xB9, 0x8D, 0xBA, /* 0x3C-0x3F */
+ 0x8D, 0xBB, 0x8D, 0xBC, 0x8D, 0xBD, 0x8D, 0xBE, /* 0x40-0x43 */
+ 0xB6, 0xE7, 0x8D, 0xBF, 0x8D, 0xC0, 0x8D, 0xC1, /* 0x44-0x47 */
+ 0xB6, 0xE8, 0x8D, 0xC2, 0x8D, 0xC3, 0x8D, 0xC4, /* 0x48-0x4B */
+ 0xB6, 0xE9, 0x8D, 0xC5, 0x8D, 0xC6, 0x8D, 0xC7, /* 0x4C-0x4F */
+ 0x8D, 0xC8, 0x8D, 0xC9, 0x8D, 0xCA, 0x8D, 0xCB, /* 0x50-0x53 */
+ 0xB6, 0xEA, 0xB6, 0xEB, 0x8D, 0xCC, 0x8D, 0xCD, /* 0x54-0x57 */
+ 0x8D, 0xCE, 0x8D, 0xCF, 0x8D, 0xD0, 0x8D, 0xD1, /* 0x58-0x5B */
+ 0x8D, 0xD2, 0x8D, 0xD3, 0x8D, 0xD4, 0x8D, 0xD5, /* 0x5C-0x5F */
+ 0xB6, 0xEC, 0x8D, 0xD6, 0x8D, 0xD7, 0x8D, 0xD8, /* 0x60-0x63 */
+ 0xB6, 0xED, 0x8D, 0xD9, 0x8D, 0xDA, 0x8D, 0xDB, /* 0x64-0x67 */
+ 0xB6, 0xEE, 0x8D, 0xDC, 0x8D, 0xDD, 0x8D, 0xDE, /* 0x68-0x6B */
+ 0x8D, 0xDF, 0x8D, 0xE0, 0x8D, 0xE1, 0x8D, 0xE2, /* 0x6C-0x6F */
+ 0xB6, 0xEF, 0xB6, 0xF0, 0x8D, 0xE3, 0xB6, 0xF1, /* 0x70-0x73 */
+ 0x8D, 0xE4, 0xB6, 0xF2, 0x8D, 0xE5, 0x8D, 0xE6, /* 0x74-0x77 */
+ 0x8D, 0xE7, 0x8D, 0xE8, 0x8D, 0xE9, 0x8D, 0xEA, /* 0x78-0x7B */
+ 0xB6, 0xF3, 0xB6, 0xF4, 0x8D, 0xEB, 0x8D, 0xEC, /* 0x7C-0x7F */
+
+ 0xB6, 0xF5, 0x8D, 0xED, 0x8D, 0xEE, 0x8D, 0xEF, /* 0x80-0x83 */
+ 0xB6, 0xF6, 0x8D, 0xF0, 0x8D, 0xF1, 0x8D, 0xF2, /* 0x84-0x87 */
+ 0x8D, 0xF3, 0x8D, 0xF4, 0x8D, 0xF5, 0x8D, 0xF6, /* 0x88-0x8B */
+ 0xB6, 0xF7, 0xB6, 0xF8, 0x8D, 0xF7, 0xB6, 0xF9, /* 0x8C-0x8F */
+ 0xB6, 0xFA, 0xB6, 0xFB, 0xB6, 0xFC, 0x8D, 0xF8, /* 0x90-0x93 */
+ 0x8D, 0xF9, 0x8D, 0xFA, 0xB6, 0xFD, 0xB6, 0xFE, /* 0x94-0x97 */
+ 0xB7, 0xA1, 0xB7, 0xA2, 0x8D, 0xFB, 0x8D, 0xFC, /* 0x98-0x9B */
+ 0xB7, 0xA3, 0x8D, 0xFD, 0x8D, 0xFE, 0x8E, 0x41, /* 0x9C-0x9F */
+ 0xB7, 0xA4, 0x8E, 0x42, 0x8E, 0x43, 0x8E, 0x44, /* 0xA0-0xA3 */
+ 0x8E, 0x45, 0x8E, 0x46, 0x8E, 0x47, 0x8E, 0x48, /* 0xA4-0xA7 */
+ 0xB7, 0xA5, 0xB7, 0xA6, 0x8E, 0x49, 0xB7, 0xA7, /* 0xA8-0xAB */
+ 0xB7, 0xA8, 0xB7, 0xA9, 0x8E, 0x4A, 0x8E, 0x4B, /* 0xAC-0xAF */
+ 0x8E, 0x4C, 0x8E, 0x4D, 0x8E, 0x4E, 0x8E, 0x4F, /* 0xB0-0xB3 */
+ 0xB7, 0xAA, 0xB7, 0xAB, 0x8E, 0x50, 0x8E, 0x51, /* 0xB4-0xB7 */
+ 0xB7, 0xAC, 0x8E, 0x52, 0x8E, 0x53, 0x8E, 0x54, /* 0xB8-0xBB */
+ 0x8E, 0x55, 0x8E, 0x56, 0x8E, 0x57, 0x8E, 0x58, /* 0xBC-0xBF */
+ 0x8E, 0x59, 0x8E, 0x5A, 0x8E, 0x61, 0x8E, 0x62, /* 0xC0-0xC3 */
+ 0x8E, 0x63, 0x8E, 0x64, 0x8E, 0x65, 0xB7, 0xAD, /* 0xC4-0xC7 */
+ 0x8E, 0x66, 0xB7, 0xAE, 0x8E, 0x67, 0x8E, 0x68, /* 0xC8-0xCB */
+ 0x8E, 0x69, 0x8E, 0x6A, 0x8E, 0x6B, 0x8E, 0x6C, /* 0xCC-0xCF */
+ 0x8E, 0x6D, 0x8E, 0x6E, 0x8E, 0x6F, 0x8E, 0x70, /* 0xD0-0xD3 */
+ 0x8E, 0x71, 0x8E, 0x72, 0x8E, 0x73, 0x8E, 0x74, /* 0xD4-0xD7 */
+ 0x8E, 0x75, 0x8E, 0x76, 0x8E, 0x77, 0x8E, 0x78, /* 0xD8-0xDB */
+ 0x8E, 0x79, 0x8E, 0x7A, 0x8E, 0x81, 0x8E, 0x82, /* 0xDC-0xDF */
+ 0x8E, 0x83, 0x8E, 0x84, 0x8E, 0x85, 0x8E, 0x86, /* 0xE0-0xE3 */
+ 0x8E, 0x87, 0x8E, 0x88, 0x8E, 0x89, 0x8E, 0x8A, /* 0xE4-0xE7 */
+ 0x8E, 0x8B, 0x8E, 0x8C, 0x8E, 0x8D, 0x8E, 0x8E, /* 0xE8-0xEB */
+ 0xB7, 0xAF, 0xB7, 0xB0, 0x8E, 0x8F, 0x8E, 0x90, /* 0xEC-0xEF */
+ 0xB7, 0xB1, 0x8E, 0x91, 0x8E, 0x92, 0x8E, 0x93, /* 0xF0-0xF3 */
+ 0xB7, 0xB2, 0x8E, 0x94, 0x8E, 0x95, 0x8E, 0x96, /* 0xF4-0xF7 */
+ 0x8E, 0x97, 0x8E, 0x98, 0x8E, 0x99, 0x8E, 0x9A, /* 0xF8-0xFB */
+ 0xB7, 0xB3, 0xB7, 0xB4, 0x8E, 0x9B, 0xB7, 0xB5, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_B8[512] = {
+ 0xB7, 0xB6, 0xB7, 0xB7, 0x8E, 0x9C, 0x8E, 0x9D, /* 0x00-0x03 */
+ 0x8E, 0x9E, 0x8E, 0x9F, 0x8E, 0xA0, 0xB7, 0xB8, /* 0x04-0x07 */
+ 0xB7, 0xB9, 0xB7, 0xBA, 0x8E, 0xA1, 0x8E, 0xA2, /* 0x08-0x0B */
+ 0xB7, 0xBB, 0x8E, 0xA3, 0x8E, 0xA4, 0x8E, 0xA5, /* 0x0C-0x0F */
+ 0xB7, 0xBC, 0x8E, 0xA6, 0x8E, 0xA7, 0x8E, 0xA8, /* 0x10-0x13 */
+ 0x8E, 0xA9, 0x8E, 0xAA, 0x8E, 0xAB, 0x8E, 0xAC, /* 0x14-0x17 */
+ 0xB7, 0xBD, 0xB7, 0xBE, 0x8E, 0xAD, 0xB7, 0xBF, /* 0x18-0x1B */
+ 0x8E, 0xAE, 0xB7, 0xC0, 0x8E, 0xAF, 0x8E, 0xB0, /* 0x1C-0x1F */
+ 0x8E, 0xB1, 0x8E, 0xB2, 0x8E, 0xB3, 0x8E, 0xB4, /* 0x20-0x23 */
+ 0xB7, 0xC1, 0xB7, 0xC2, 0x8E, 0xB5, 0x8E, 0xB6, /* 0x24-0x27 */
+ 0xB7, 0xC3, 0x8E, 0xB7, 0x8E, 0xB8, 0x8E, 0xB9, /* 0x28-0x2B */
+ 0xB7, 0xC4, 0x8E, 0xBA, 0x8E, 0xBB, 0x8E, 0xBC, /* 0x2C-0x2F */
+ 0x8E, 0xBD, 0x8E, 0xBE, 0x8E, 0xBF, 0x8E, 0xC0, /* 0x30-0x33 */
+ 0xB7, 0xC5, 0xB7, 0xC6, 0x8E, 0xC1, 0xB7, 0xC7, /* 0x34-0x37 */
+ 0xB7, 0xC8, 0xB7, 0xC9, 0x8E, 0xC2, 0x8E, 0xC3, /* 0x38-0x3B */
+ 0x8E, 0xC4, 0x8E, 0xC5, 0x8E, 0xC6, 0x8E, 0xC7, /* 0x3C-0x3F */
+ 0xB7, 0xCA, 0x8E, 0xC8, 0x8E, 0xC9, 0x8E, 0xCA, /* 0x40-0x43 */
+ 0xB7, 0xCB, 0x8E, 0xCB, 0x8E, 0xCC, 0x8E, 0xCD, /* 0x44-0x47 */
+ 0x8E, 0xCE, 0x8E, 0xCF, 0x8E, 0xD0, 0x8E, 0xD1, /* 0x48-0x4B */
+ 0x8E, 0xD2, 0x8E, 0xD3, 0x8E, 0xD4, 0x8E, 0xD5, /* 0x4C-0x4F */
+ 0x8E, 0xD6, 0xB7, 0xCC, 0x8E, 0xD7, 0xB7, 0xCD, /* 0x50-0x53 */
+ 0x8E, 0xD8, 0x8E, 0xD9, 0x8E, 0xDA, 0x8E, 0xDB, /* 0x54-0x57 */
+ 0x8E, 0xDC, 0x8E, 0xDD, 0x8E, 0xDE, 0x8E, 0xDF, /* 0x58-0x5B */
+ 0xB7, 0xCE, 0xB7, 0xCF, 0x8E, 0xE0, 0x8E, 0xE1, /* 0x5C-0x5F */
+ 0xB7, 0xD0, 0x8E, 0xE2, 0x8E, 0xE3, 0x8E, 0xE4, /* 0x60-0x63 */
+ 0xB7, 0xD1, 0x8E, 0xE5, 0x8E, 0xE6, 0x8E, 0xE7, /* 0x64-0x67 */
+ 0x8E, 0xE8, 0x8E, 0xE9, 0x8E, 0xEA, 0x8E, 0xEB, /* 0x68-0x6B */
+ 0xB7, 0xD2, 0xB7, 0xD3, 0x8E, 0xEC, 0xB7, 0xD4, /* 0x6C-0x6F */
+ 0x8E, 0xED, 0xB7, 0xD5, 0x8E, 0xEE, 0x8E, 0xEF, /* 0x70-0x73 */
+ 0x8E, 0xF0, 0x8E, 0xF1, 0x8E, 0xF2, 0x8E, 0xF3, /* 0x74-0x77 */
+ 0xB7, 0xD6, 0x8E, 0xF4, 0x8E, 0xF5, 0x8E, 0xF6, /* 0x78-0x7B */
+ 0xB7, 0xD7, 0x8E, 0xF7, 0x8E, 0xF8, 0x8E, 0xF9, /* 0x7C-0x7F */
+
+ 0x8E, 0xFA, 0x8E, 0xFB, 0x8E, 0xFC, 0x8E, 0xFD, /* 0x80-0x83 */
+ 0x8E, 0xFE, 0x8F, 0x41, 0x8F, 0x42, 0x8F, 0x43, /* 0x84-0x87 */
+ 0x8F, 0x44, 0x8F, 0x45, 0x8F, 0x46, 0x8F, 0x47, /* 0x88-0x8B */
+ 0x8F, 0x48, 0xB7, 0xD8, 0x8F, 0x49, 0x8F, 0x4A, /* 0x8C-0x8F */
+ 0x8F, 0x4B, 0x8F, 0x4C, 0x8F, 0x4D, 0x8F, 0x4E, /* 0x90-0x93 */
+ 0x8F, 0x4F, 0x8F, 0x50, 0x8F, 0x51, 0x8F, 0x52, /* 0x94-0x97 */
+ 0x8F, 0x53, 0x8F, 0x54, 0x8F, 0x55, 0x8F, 0x56, /* 0x98-0x9B */
+ 0x8F, 0x57, 0x8F, 0x58, 0x8F, 0x59, 0x8F, 0x5A, /* 0x9C-0x9F */
+ 0x8F, 0x61, 0x8F, 0x62, 0x8F, 0x63, 0x8F, 0x64, /* 0xA0-0xA3 */
+ 0x8F, 0x65, 0x8F, 0x66, 0x8F, 0x67, 0x8F, 0x68, /* 0xA4-0xA7 */
+ 0xB7, 0xD9, 0x8F, 0x69, 0x8F, 0x6A, 0x8F, 0x6B, /* 0xA8-0xAB */
+ 0x8F, 0x6C, 0x8F, 0x6D, 0x8F, 0x6E, 0x8F, 0x6F, /* 0xAC-0xAF */
+ 0xB7, 0xDA, 0x8F, 0x70, 0x8F, 0x71, 0x8F, 0x72, /* 0xB0-0xB3 */
+ 0xB7, 0xDB, 0x8F, 0x73, 0x8F, 0x74, 0x8F, 0x75, /* 0xB4-0xB7 */
+ 0xB7, 0xDC, 0x8F, 0x76, 0x8F, 0x77, 0x8F, 0x78, /* 0xB8-0xBB */
+ 0x8F, 0x79, 0x8F, 0x7A, 0x8F, 0x81, 0x8F, 0x82, /* 0xBC-0xBF */
+ 0xB7, 0xDD, 0xB7, 0xDE, 0x8F, 0x83, 0xB7, 0xDF, /* 0xC0-0xC3 */
+ 0x8F, 0x84, 0xB7, 0xE0, 0x8F, 0x85, 0x8F, 0x86, /* 0xC4-0xC7 */
+ 0x8F, 0x87, 0x8F, 0x88, 0x8F, 0x89, 0x8F, 0x8A, /* 0xC8-0xCB */
+ 0xB7, 0xE1, 0x8F, 0x8B, 0x8F, 0x8C, 0x8F, 0x8D, /* 0xCC-0xCF */
+ 0xB7, 0xE2, 0x8F, 0x8E, 0x8F, 0x8F, 0x8F, 0x90, /* 0xD0-0xD3 */
+ 0xB7, 0xE3, 0x8F, 0x91, 0x8F, 0x92, 0x8F, 0x93, /* 0xD4-0xD7 */
+ 0x8F, 0x94, 0x8F, 0x95, 0x8F, 0x96, 0x8F, 0x97, /* 0xD8-0xDB */
+ 0x8F, 0x98, 0xB7, 0xE4, 0x8F, 0x99, 0xB7, 0xE5, /* 0xDC-0xDF */
+ 0x8F, 0x9A, 0xB7, 0xE6, 0x8F, 0x9B, 0x8F, 0x9C, /* 0xE0-0xE3 */
+ 0x8F, 0x9D, 0x8F, 0x9E, 0x8F, 0x9F, 0x8F, 0xA0, /* 0xE4-0xE7 */
+ 0xB7, 0xE7, 0xB7, 0xE8, 0x8F, 0xA1, 0x8F, 0xA2, /* 0xE8-0xEB */
+ 0xB7, 0xE9, 0x8F, 0xA3, 0x8F, 0xA4, 0x8F, 0xA5, /* 0xEC-0xEF */
+ 0xB7, 0xEA, 0x8F, 0xA6, 0x8F, 0xA7, 0x8F, 0xA8, /* 0xF0-0xF3 */
+ 0x8F, 0xA9, 0x8F, 0xAA, 0x8F, 0xAB, 0x8F, 0xAC, /* 0xF4-0xF7 */
+ 0xB7, 0xEB, 0xB7, 0xEC, 0x8F, 0xAD, 0xB7, 0xED, /* 0xF8-0xFB */
+ 0x8F, 0xAE, 0xB7, 0xEE, 0x8F, 0xAF, 0x8F, 0xB0, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_B9[512] = {
+ 0x8F, 0xB1, 0x8F, 0xB2, 0x8F, 0xB3, 0x8F, 0xB4, /* 0x00-0x03 */
+ 0xB7, 0xEF, 0x8F, 0xB5, 0x8F, 0xB6, 0x8F, 0xB7, /* 0x04-0x07 */
+ 0x8F, 0xB8, 0x8F, 0xB9, 0x8F, 0xBA, 0x8F, 0xBB, /* 0x08-0x0B */
+ 0x8F, 0xBC, 0x8F, 0xBD, 0x8F, 0xBE, 0x8F, 0xBF, /* 0x0C-0x0F */
+ 0x8F, 0xC0, 0x8F, 0xC1, 0x8F, 0xC2, 0x8F, 0xC3, /* 0x10-0x13 */
+ 0x8F, 0xC4, 0x8F, 0xC5, 0x8F, 0xC6, 0x8F, 0xC7, /* 0x14-0x17 */
+ 0xB7, 0xF0, 0x8F, 0xC8, 0x8F, 0xC9, 0x8F, 0xCA, /* 0x18-0x1B */
+ 0x8F, 0xCB, 0x8F, 0xCC, 0x8F, 0xCD, 0x8F, 0xCE, /* 0x1C-0x1F */
+ 0xB7, 0xF1, 0x8F, 0xCF, 0x8F, 0xD0, 0x8F, 0xD1, /* 0x20-0x23 */
+ 0x8F, 0xD2, 0x8F, 0xD3, 0x8F, 0xD4, 0x8F, 0xD5, /* 0x24-0x27 */
+ 0x8F, 0xD6, 0x8F, 0xD7, 0x8F, 0xD8, 0x8F, 0xD9, /* 0x28-0x2B */
+ 0x8F, 0xDA, 0x8F, 0xDB, 0x8F, 0xDC, 0x8F, 0xDD, /* 0x2C-0x2F */
+ 0x8F, 0xDE, 0x8F, 0xDF, 0x8F, 0xE0, 0x8F, 0xE1, /* 0x30-0x33 */
+ 0x8F, 0xE2, 0x8F, 0xE3, 0x8F, 0xE4, 0x8F, 0xE5, /* 0x34-0x37 */
+ 0x8F, 0xE6, 0x8F, 0xE7, 0x8F, 0xE8, 0x8F, 0xE9, /* 0x38-0x3B */
+ 0xB7, 0xF2, 0xB7, 0xF3, 0x8F, 0xEA, 0x8F, 0xEB, /* 0x3C-0x3F */
+ 0xB7, 0xF4, 0x8F, 0xEC, 0x8F, 0xED, 0x8F, 0xEE, /* 0x40-0x43 */
+ 0xB7, 0xF5, 0x8F, 0xEF, 0x8F, 0xF0, 0x8F, 0xF1, /* 0x44-0x47 */
+ 0x8F, 0xF2, 0x8F, 0xF3, 0x8F, 0xF4, 0x8F, 0xF5, /* 0x48-0x4B */
+ 0xB7, 0xF6, 0x8F, 0xF6, 0x8F, 0xF7, 0xB7, 0xF7, /* 0x4C-0x4F */
+ 0x8F, 0xF8, 0xB7, 0xF8, 0x8F, 0xF9, 0x8F, 0xFA, /* 0x50-0x53 */
+ 0x8F, 0xFB, 0x8F, 0xFC, 0x8F, 0xFD, 0x8F, 0xFE, /* 0x54-0x57 */
+ 0xB7, 0xF9, 0xB7, 0xFA, 0x90, 0x41, 0x90, 0x42, /* 0x58-0x5B */
+ 0xB7, 0xFB, 0x90, 0x43, 0x90, 0x44, 0x90, 0x45, /* 0x5C-0x5F */
+ 0xB7, 0xFC, 0x90, 0x46, 0x90, 0x47, 0x90, 0x48, /* 0x60-0x63 */
+ 0x90, 0x49, 0x90, 0x4A, 0x90, 0x4B, 0x90, 0x4C, /* 0x64-0x67 */
+ 0xB7, 0xFD, 0xB7, 0xFE, 0x90, 0x4D, 0xB8, 0xA1, /* 0x68-0x6B */
+ 0x90, 0x4E, 0xB8, 0xA2, 0x90, 0x4F, 0x90, 0x50, /* 0x6C-0x6F */
+ 0x90, 0x51, 0x90, 0x52, 0x90, 0x53, 0x90, 0x54, /* 0x70-0x73 */
+ 0xB8, 0xA3, 0xB8, 0xA4, 0x90, 0x55, 0x90, 0x56, /* 0x74-0x77 */
+ 0xB8, 0xA5, 0x90, 0x57, 0x90, 0x58, 0x90, 0x59, /* 0x78-0x7B */
+ 0xB8, 0xA6, 0x90, 0x5A, 0x90, 0x61, 0x90, 0x62, /* 0x7C-0x7F */
+
+ 0x90, 0x63, 0x90, 0x64, 0x90, 0x65, 0x90, 0x66, /* 0x80-0x83 */
+ 0xB8, 0xA7, 0xB8, 0xA8, 0x90, 0x67, 0xB8, 0xA9, /* 0x84-0x87 */
+ 0x90, 0x68, 0xB8, 0xAA, 0xB8, 0xAB, 0x90, 0x69, /* 0x88-0x8B */
+ 0x90, 0x6A, 0xB8, 0xAC, 0xB8, 0xAD, 0x90, 0x6B, /* 0x8C-0x8F */
+ 0x90, 0x6C, 0x90, 0x6D, 0x90, 0x6E, 0x90, 0x6F, /* 0x90-0x93 */
+ 0x90, 0x70, 0x90, 0x71, 0x90, 0x72, 0x90, 0x73, /* 0x94-0x97 */
+ 0x90, 0x74, 0x90, 0x75, 0x90, 0x76, 0x90, 0x77, /* 0x98-0x9B */
+ 0x90, 0x78, 0x90, 0x79, 0x90, 0x7A, 0x90, 0x81, /* 0x9C-0x9F */
+ 0x90, 0x82, 0x90, 0x83, 0x90, 0x84, 0x90, 0x85, /* 0xA0-0xA3 */
+ 0x90, 0x86, 0x90, 0x87, 0x90, 0x88, 0x90, 0x89, /* 0xA4-0xA7 */
+ 0x90, 0x8A, 0x90, 0x8B, 0x90, 0x8C, 0x90, 0x8D, /* 0xA8-0xAB */
+ 0xB8, 0xAE, 0xB8, 0xAF, 0x90, 0x8E, 0x90, 0x8F, /* 0xAC-0xAF */
+ 0xB8, 0xB0, 0x90, 0x90, 0x90, 0x91, 0x90, 0x92, /* 0xB0-0xB3 */
+ 0xB8, 0xB1, 0x90, 0x93, 0x90, 0x94, 0x90, 0x95, /* 0xB4-0xB7 */
+ 0x90, 0x96, 0x90, 0x97, 0x90, 0x98, 0x90, 0x99, /* 0xB8-0xBB */
+ 0xB8, 0xB2, 0xB8, 0xB3, 0x90, 0x9A, 0xB8, 0xB4, /* 0xBC-0xBF */
+ 0x90, 0x9B, 0xB8, 0xB5, 0x90, 0x9C, 0x90, 0x9D, /* 0xC0-0xC3 */
+ 0x90, 0x9E, 0x90, 0x9F, 0x90, 0xA0, 0x90, 0xA1, /* 0xC4-0xC7 */
+ 0xB8, 0xB6, 0xB8, 0xB7, 0x90, 0xA2, 0x90, 0xA3, /* 0xC8-0xCB */
+ 0xB8, 0xB8, 0x90, 0xA4, 0xB8, 0xB9, 0xB8, 0xBA, /* 0xCC-0xCF */
+ 0xB8, 0xBB, 0xB8, 0xBC, 0xB8, 0xBD, 0x90, 0xA5, /* 0xD0-0xD3 */
+ 0x90, 0xA6, 0x90, 0xA7, 0x90, 0xA8, 0x90, 0xA9, /* 0xD4-0xD7 */
+ 0xB8, 0xBE, 0xB8, 0xBF, 0x90, 0xAA, 0xB8, 0xC0, /* 0xD8-0xDB */
+ 0x90, 0xAB, 0xB8, 0xC1, 0xB8, 0xC2, 0x90, 0xAC, /* 0xDC-0xDF */
+ 0x90, 0xAD, 0xB8, 0xC3, 0x90, 0xAE, 0xB8, 0xC4, /* 0xE0-0xE3 */
+ 0xB8, 0xC5, 0xB8, 0xC6, 0x90, 0xAF, 0x90, 0xB0, /* 0xE4-0xE7 */
+ 0xB8, 0xC7, 0x90, 0xB1, 0x90, 0xB2, 0x90, 0xB3, /* 0xE8-0xEB */
+ 0xB8, 0xC8, 0x90, 0xB4, 0x90, 0xB5, 0x90, 0xB6, /* 0xEC-0xEF */
+ 0x90, 0xB7, 0x90, 0xB8, 0x90, 0xB9, 0x90, 0xBA, /* 0xF0-0xF3 */
+ 0xB8, 0xC9, 0xB8, 0xCA, 0x90, 0xBB, 0xB8, 0xCB, /* 0xF4-0xF7 */
+ 0xB8, 0xCC, 0xB8, 0xCD, 0xB8, 0xCE, 0x90, 0xBC, /* 0xF8-0xFB */
+ 0x90, 0xBD, 0x90, 0xBE, 0x90, 0xBF, 0x90, 0xC0, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_BA[512] = {
+ 0xB8, 0xCF, 0xB8, 0xD0, 0x90, 0xC1, 0x90, 0xC2, /* 0x00-0x03 */
+ 0x90, 0xC3, 0x90, 0xC4, 0x90, 0xC5, 0x90, 0xC6, /* 0x04-0x07 */
+ 0xB8, 0xD1, 0x90, 0xC7, 0x90, 0xC8, 0x90, 0xC9, /* 0x08-0x0B */
+ 0x90, 0xCA, 0x90, 0xCB, 0x90, 0xCC, 0x90, 0xCD, /* 0x0C-0x0F */
+ 0x90, 0xCE, 0x90, 0xCF, 0x90, 0xD0, 0x90, 0xD1, /* 0x10-0x13 */
+ 0x90, 0xD2, 0xB8, 0xD2, 0x90, 0xD3, 0x90, 0xD4, /* 0x14-0x17 */
+ 0x90, 0xD5, 0x90, 0xD6, 0x90, 0xD7, 0x90, 0xD8, /* 0x18-0x1B */
+ 0x90, 0xD9, 0x90, 0xDA, 0x90, 0xDB, 0x90, 0xDC, /* 0x1C-0x1F */
+ 0x90, 0xDD, 0x90, 0xDE, 0x90, 0xDF, 0x90, 0xE0, /* 0x20-0x23 */
+ 0x90, 0xE1, 0x90, 0xE2, 0x90, 0xE3, 0x90, 0xE4, /* 0x24-0x27 */
+ 0x90, 0xE5, 0x90, 0xE6, 0x90, 0xE7, 0x90, 0xE8, /* 0x28-0x2B */
+ 0x90, 0xE9, 0x90, 0xEA, 0x90, 0xEB, 0x90, 0xEC, /* 0x2C-0x2F */
+ 0x90, 0xED, 0x90, 0xEE, 0x90, 0xEF, 0x90, 0xF0, /* 0x30-0x33 */
+ 0x90, 0xF1, 0x90, 0xF2, 0x90, 0xF3, 0x90, 0xF4, /* 0x34-0x37 */
+ 0xB8, 0xD3, 0xB8, 0xD4, 0x90, 0xF5, 0x90, 0xF6, /* 0x38-0x3B */
+ 0xB8, 0xD5, 0x90, 0xF7, 0x90, 0xF8, 0x90, 0xF9, /* 0x3C-0x3F */
+ 0xB8, 0xD6, 0x90, 0xFA, 0xB8, 0xD7, 0x90, 0xFB, /* 0x40-0x43 */
+ 0x90, 0xFC, 0x90, 0xFD, 0x90, 0xFE, 0x91, 0x41, /* 0x44-0x47 */
+ 0xB8, 0xD8, 0xB8, 0xD9, 0x91, 0x42, 0xB8, 0xDA, /* 0x48-0x4B */
+ 0x91, 0x43, 0xB8, 0xDB, 0xB8, 0xDC, 0x91, 0x44, /* 0x4C-0x4F */
+ 0x91, 0x45, 0x91, 0x46, 0x91, 0x47, 0xB8, 0xDD, /* 0x50-0x53 */
+ 0xB8, 0xDE, 0xB8, 0xDF, 0x91, 0x48, 0x91, 0x49, /* 0x54-0x57 */
+ 0xB8, 0xE0, 0x91, 0x4A, 0x91, 0x4B, 0x91, 0x4C, /* 0x58-0x5B */
+ 0xB8, 0xE1, 0x91, 0x4D, 0x91, 0x4E, 0x91, 0x4F, /* 0x5C-0x5F */
+ 0x91, 0x50, 0x91, 0x51, 0x91, 0x52, 0x91, 0x53, /* 0x60-0x63 */
+ 0xB8, 0xE2, 0xB8, 0xE3, 0x91, 0x54, 0xB8, 0xE4, /* 0x64-0x67 */
+ 0xB8, 0xE5, 0xB8, 0xE6, 0x91, 0x55, 0x91, 0x56, /* 0x68-0x6B */
+ 0x91, 0x57, 0x91, 0x58, 0x91, 0x59, 0x91, 0x5A, /* 0x6C-0x6F */
+ 0xB8, 0xE7, 0xB8, 0xE8, 0x91, 0x61, 0x91, 0x62, /* 0x70-0x73 */
+ 0xB8, 0xE9, 0x91, 0x63, 0x91, 0x64, 0x91, 0x65, /* 0x74-0x77 */
+ 0xB8, 0xEA, 0x91, 0x66, 0x91, 0x67, 0x91, 0x68, /* 0x78-0x7B */
+ 0x91, 0x69, 0x91, 0x6A, 0x91, 0x6B, 0x91, 0x6C, /* 0x7C-0x7F */
+
+ 0x91, 0x6D, 0x91, 0x6E, 0x91, 0x6F, 0xB8, 0xEB, /* 0x80-0x83 */
+ 0xB8, 0xEC, 0xB8, 0xED, 0x91, 0x70, 0xB8, 0xEE, /* 0x84-0x87 */
+ 0x91, 0x71, 0x91, 0x72, 0x91, 0x73, 0x91, 0x74, /* 0x88-0x8B */
+ 0xB8, 0xEF, 0x91, 0x75, 0x91, 0x76, 0x91, 0x77, /* 0x8C-0x8F */
+ 0x91, 0x78, 0x91, 0x79, 0x91, 0x7A, 0x91, 0x81, /* 0x90-0x93 */
+ 0x91, 0x82, 0x91, 0x83, 0x91, 0x84, 0x91, 0x85, /* 0x94-0x97 */
+ 0x91, 0x86, 0x91, 0x87, 0x91, 0x88, 0x91, 0x89, /* 0x98-0x9B */
+ 0x91, 0x8A, 0x91, 0x8B, 0x91, 0x8C, 0x91, 0x8D, /* 0x9C-0x9F */
+ 0x91, 0x8E, 0x91, 0x8F, 0x91, 0x90, 0x91, 0x91, /* 0xA0-0xA3 */
+ 0x91, 0x92, 0x91, 0x93, 0x91, 0x94, 0x91, 0x95, /* 0xA4-0xA7 */
+ 0xB8, 0xF0, 0xB8, 0xF1, 0x91, 0x96, 0xB8, 0xF2, /* 0xA8-0xAB */
+ 0xB8, 0xF3, 0x91, 0x97, 0x91, 0x98, 0x91, 0x99, /* 0xAC-0xAF */
+ 0xB8, 0xF4, 0x91, 0x9A, 0xB8, 0xF5, 0x91, 0x9B, /* 0xB0-0xB3 */
+ 0x91, 0x9C, 0x91, 0x9D, 0x91, 0x9E, 0x91, 0x9F, /* 0xB4-0xB7 */
+ 0xB8, 0xF6, 0xB8, 0xF7, 0x91, 0xA0, 0xB8, 0xF8, /* 0xB8-0xBB */
+ 0x91, 0xA1, 0xB8, 0xF9, 0x91, 0xA2, 0x91, 0xA3, /* 0xBC-0xBF */
+ 0x91, 0xA4, 0x91, 0xA5, 0x91, 0xA6, 0x91, 0xA7, /* 0xC0-0xC3 */
+ 0xB8, 0xFA, 0x91, 0xA8, 0x91, 0xA9, 0x91, 0xAA, /* 0xC4-0xC7 */
+ 0xB8, 0xFB, 0x91, 0xAB, 0x91, 0xAC, 0x91, 0xAD, /* 0xC8-0xCB */
+ 0x91, 0xAE, 0x91, 0xAF, 0x91, 0xB0, 0x91, 0xB1, /* 0xCC-0xCF */
+ 0x91, 0xB2, 0x91, 0xB3, 0x91, 0xB4, 0x91, 0xB5, /* 0xD0-0xD3 */
+ 0x91, 0xB6, 0x91, 0xB7, 0x91, 0xB8, 0x91, 0xB9, /* 0xD4-0xD7 */
+ 0xB8, 0xFC, 0xB8, 0xFD, 0x91, 0xBA, 0x91, 0xBB, /* 0xD8-0xDB */
+ 0x91, 0xBC, 0x91, 0xBD, 0x91, 0xBE, 0x91, 0xBF, /* 0xDC-0xDF */
+ 0x91, 0xC0, 0x91, 0xC1, 0x91, 0xC2, 0x91, 0xC3, /* 0xE0-0xE3 */
+ 0x91, 0xC4, 0x91, 0xC5, 0x91, 0xC6, 0x91, 0xC7, /* 0xE4-0xE7 */
+ 0x91, 0xC8, 0x91, 0xC9, 0x91, 0xCA, 0x91, 0xCB, /* 0xE8-0xEB */
+ 0x91, 0xCC, 0x91, 0xCD, 0x91, 0xCE, 0x91, 0xCF, /* 0xEC-0xEF */
+ 0x91, 0xD0, 0x91, 0xD1, 0x91, 0xD2, 0x91, 0xD3, /* 0xF0-0xF3 */
+ 0x91, 0xD4, 0x91, 0xD5, 0x91, 0xD6, 0x91, 0xD7, /* 0xF4-0xF7 */
+ 0x91, 0xD8, 0x91, 0xD9, 0x91, 0xDA, 0x91, 0xDB, /* 0xF8-0xFB */
+ 0xB8, 0xFE, 0x91, 0xDC, 0x91, 0xDD, 0x91, 0xDE, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_BB[512] = {
+ 0xB9, 0xA1, 0x91, 0xDF, 0x91, 0xE0, 0x91, 0xE1, /* 0x00-0x03 */
+ 0xB9, 0xA2, 0x91, 0xE2, 0x91, 0xE3, 0x91, 0xE4, /* 0x04-0x07 */
+ 0x91, 0xE5, 0x91, 0xE6, 0x91, 0xE7, 0x91, 0xE8, /* 0x08-0x0B */
+ 0x91, 0xE9, 0xB9, 0xA3, 0x91, 0xEA, 0xB9, 0xA4, /* 0x0C-0x0F */
+ 0x91, 0xEB, 0xB9, 0xA5, 0x91, 0xEC, 0x91, 0xED, /* 0x10-0x13 */
+ 0x91, 0xEE, 0x91, 0xEF, 0x91, 0xF0, 0x91, 0xF1, /* 0x14-0x17 */
+ 0xB9, 0xA6, 0x91, 0xF2, 0x91, 0xF3, 0x91, 0xF4, /* 0x18-0x1B */
+ 0xB9, 0xA7, 0x91, 0xF5, 0x91, 0xF6, 0x91, 0xF7, /* 0x1C-0x1F */
+ 0xB9, 0xA8, 0x91, 0xF8, 0x91, 0xF9, 0x91, 0xFA, /* 0x20-0x23 */
+ 0x91, 0xFB, 0x91, 0xFC, 0x91, 0xFD, 0x91, 0xFE, /* 0x24-0x27 */
+ 0x92, 0x41, 0xB9, 0xA9, 0x92, 0x42, 0xB9, 0xAA, /* 0x28-0x2B */
+ 0x92, 0x43, 0x92, 0x44, 0x92, 0x45, 0x92, 0x46, /* 0x2C-0x2F */
+ 0x92, 0x47, 0x92, 0x48, 0x92, 0x49, 0x92, 0x4A, /* 0x30-0x33 */
+ 0xB9, 0xAB, 0xB9, 0xAC, 0xB9, 0xAD, 0x92, 0x4B, /* 0x34-0x37 */
+ 0xB9, 0xAE, 0x92, 0x4C, 0x92, 0x4D, 0xB9, 0xAF, /* 0x38-0x3B */
+ 0xB9, 0xB0, 0xB9, 0xB1, 0xB9, 0xB2, 0x92, 0x4E, /* 0x3C-0x3F */
+ 0x92, 0x4F, 0x92, 0x50, 0x92, 0x51, 0x92, 0x52, /* 0x40-0x43 */
+ 0xB9, 0xB3, 0xB9, 0xB4, 0x92, 0x53, 0xB9, 0xB5, /* 0x44-0x47 */
+ 0x92, 0x54, 0xB9, 0xB6, 0x92, 0x55, 0x92, 0x56, /* 0x48-0x4B */
+ 0x92, 0x57, 0xB9, 0xB7, 0x92, 0x58, 0xB9, 0xB8, /* 0x4C-0x4F */
+ 0xB9, 0xB9, 0x92, 0x59, 0x92, 0x5A, 0x92, 0x61, /* 0x50-0x53 */
+ 0xB9, 0xBA, 0x92, 0x62, 0x92, 0x63, 0x92, 0x64, /* 0x54-0x57 */
+ 0xB9, 0xBB, 0x92, 0x65, 0x92, 0x66, 0x92, 0x67, /* 0x58-0x5B */
+ 0x92, 0x68, 0x92, 0x69, 0x92, 0x6A, 0x92, 0x6B, /* 0x5C-0x5F */
+ 0x92, 0x6C, 0xB9, 0xBC, 0x92, 0x6D, 0xB9, 0xBD, /* 0x60-0x63 */
+ 0x92, 0x6E, 0x92, 0x6F, 0x92, 0x70, 0x92, 0x71, /* 0x64-0x67 */
+ 0x92, 0x72, 0x92, 0x73, 0x92, 0x74, 0x92, 0x75, /* 0x68-0x6B */
+ 0xB9, 0xBE, 0x92, 0x76, 0x92, 0x77, 0x92, 0x78, /* 0x6C-0x6F */
+ 0x92, 0x79, 0x92, 0x7A, 0x92, 0x81, 0x92, 0x82, /* 0x70-0x73 */
+ 0x92, 0x83, 0x92, 0x84, 0x92, 0x85, 0x92, 0x86, /* 0x74-0x77 */
+ 0x92, 0x87, 0x92, 0x88, 0x92, 0x89, 0x92, 0x8A, /* 0x78-0x7B */
+ 0x92, 0x8B, 0x92, 0x8C, 0x92, 0x8D, 0x92, 0x8E, /* 0x7C-0x7F */
+
+ 0x92, 0x8F, 0x92, 0x90, 0x92, 0x91, 0x92, 0x92, /* 0x80-0x83 */
+ 0x92, 0x93, 0x92, 0x94, 0x92, 0x95, 0x92, 0x96, /* 0x84-0x87 */
+ 0xB9, 0xBF, 0x92, 0x97, 0x92, 0x98, 0x92, 0x99, /* 0x88-0x8B */
+ 0xB9, 0xC0, 0x92, 0x9A, 0x92, 0x9B, 0x92, 0x9C, /* 0x8C-0x8F */
+ 0xB9, 0xC1, 0x92, 0x9D, 0x92, 0x9E, 0x92, 0x9F, /* 0x90-0x93 */
+ 0x92, 0xA0, 0x92, 0xA1, 0x92, 0xA2, 0x92, 0xA3, /* 0x94-0x97 */
+ 0x92, 0xA4, 0x92, 0xA5, 0x92, 0xA6, 0x92, 0xA7, /* 0x98-0x9B */
+ 0x92, 0xA8, 0x92, 0xA9, 0x92, 0xAA, 0x92, 0xAB, /* 0x9C-0x9F */
+ 0x92, 0xAC, 0x92, 0xAD, 0x92, 0xAE, 0x92, 0xAF, /* 0xA0-0xA3 */
+ 0xB9, 0xC2, 0x92, 0xB0, 0x92, 0xB1, 0x92, 0xB2, /* 0xA4-0xA7 */
+ 0xB9, 0xC3, 0x92, 0xB3, 0x92, 0xB4, 0x92, 0xB5, /* 0xA8-0xAB */
+ 0xB9, 0xC4, 0x92, 0xB6, 0x92, 0xB7, 0x92, 0xB8, /* 0xAC-0xAF */
+ 0x92, 0xB9, 0x92, 0xBA, 0x92, 0xBB, 0x92, 0xBC, /* 0xB0-0xB3 */
+ 0xB9, 0xC5, 0x92, 0xBD, 0x92, 0xBE, 0xB9, 0xC6, /* 0xB4-0xB7 */
+ 0x92, 0xBF, 0x92, 0xC0, 0x92, 0xC1, 0x92, 0xC2, /* 0xB8-0xBB */
+ 0x92, 0xC3, 0x92, 0xC4, 0x92, 0xC5, 0x92, 0xC6, /* 0xBC-0xBF */
+ 0xB9, 0xC7, 0x92, 0xC7, 0x92, 0xC8, 0x92, 0xC9, /* 0xC0-0xC3 */
+ 0xB9, 0xC8, 0x92, 0xCA, 0x92, 0xCB, 0x92, 0xCC, /* 0xC4-0xC7 */
+ 0xB9, 0xC9, 0x92, 0xCD, 0x92, 0xCE, 0x92, 0xCF, /* 0xC8-0xCB */
+ 0x92, 0xD0, 0x92, 0xD1, 0x92, 0xD2, 0x92, 0xD3, /* 0xCC-0xCF */
+ 0xB9, 0xCA, 0x92, 0xD4, 0x92, 0xD5, 0xB9, 0xCB, /* 0xD0-0xD3 */
+ 0x92, 0xD6, 0x92, 0xD7, 0x92, 0xD8, 0x92, 0xD9, /* 0xD4-0xD7 */
+ 0x92, 0xDA, 0x92, 0xDB, 0x92, 0xDC, 0x92, 0xDD, /* 0xD8-0xDB */
+ 0x92, 0xDE, 0x92, 0xDF, 0x92, 0xE0, 0x92, 0xE1, /* 0xDC-0xDF */
+ 0x92, 0xE2, 0x92, 0xE3, 0x92, 0xE4, 0x92, 0xE5, /* 0xE0-0xE3 */
+ 0x92, 0xE6, 0x92, 0xE7, 0x92, 0xE8, 0x92, 0xE9, /* 0xE4-0xE7 */
+ 0x92, 0xEA, 0x92, 0xEB, 0x92, 0xEC, 0x92, 0xED, /* 0xE8-0xEB */
+ 0x92, 0xEE, 0x92, 0xEF, 0x92, 0xF0, 0x92, 0xF1, /* 0xEC-0xEF */
+ 0x92, 0xF2, 0x92, 0xF3, 0x92, 0xF4, 0x92, 0xF5, /* 0xF0-0xF3 */
+ 0x92, 0xF6, 0x92, 0xF7, 0x92, 0xF8, 0x92, 0xF9, /* 0xF4-0xF7 */
+ 0xB9, 0xCC, 0xB9, 0xCD, 0x92, 0xFA, 0x92, 0xFB, /* 0xF8-0xFB */
+ 0xB9, 0xCE, 0x92, 0xFC, 0x92, 0xFD, 0xB9, 0xCF, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_BC[512] = {
+ 0xB9, 0xD0, 0x92, 0xFE, 0xB9, 0xD1, 0x93, 0x41, /* 0x00-0x03 */
+ 0x93, 0x42, 0x93, 0x43, 0x93, 0x44, 0x93, 0x45, /* 0x04-0x07 */
+ 0xB9, 0xD2, 0xB9, 0xD3, 0x93, 0x46, 0xB9, 0xD4, /* 0x08-0x0B */
+ 0xB9, 0xD5, 0xB9, 0xD6, 0x93, 0x47, 0xB9, 0xD7, /* 0x0C-0x0F */
+ 0x93, 0x48, 0xB9, 0xD8, 0x93, 0x49, 0x93, 0x4A, /* 0x10-0x13 */
+ 0xB9, 0xD9, 0xB9, 0xDA, 0xB9, 0xDB, 0xB9, 0xDC, /* 0x14-0x17 */
+ 0xB9, 0xDD, 0x93, 0x4B, 0x93, 0x4C, 0xB9, 0xDE, /* 0x18-0x1B */
+ 0xB9, 0xDF, 0xB9, 0xE0, 0xB9, 0xE1, 0xB9, 0xE2, /* 0x1C-0x1F */
+ 0x93, 0x4D, 0x93, 0x4E, 0x93, 0x4F, 0x93, 0x50, /* 0x20-0x23 */
+ 0xB9, 0xE3, 0xB9, 0xE4, 0x93, 0x51, 0xB9, 0xE5, /* 0x24-0x27 */
+ 0x93, 0x52, 0xB9, 0xE6, 0x93, 0x53, 0x93, 0x54, /* 0x28-0x2B */
+ 0x93, 0x55, 0xB9, 0xE7, 0x93, 0x56, 0x93, 0x57, /* 0x2C-0x2F */
+ 0xB9, 0xE8, 0xB9, 0xE9, 0x93, 0x58, 0x93, 0x59, /* 0x30-0x33 */
+ 0xB9, 0xEA, 0x93, 0x5A, 0x93, 0x61, 0x93, 0x62, /* 0x34-0x37 */
+ 0xB9, 0xEB, 0x93, 0x63, 0x93, 0x64, 0x93, 0x65, /* 0x38-0x3B */
+ 0x93, 0x66, 0x93, 0x67, 0x93, 0x68, 0x93, 0x69, /* 0x3C-0x3F */
+ 0xB9, 0xEC, 0xB9, 0xED, 0x93, 0x6A, 0xB9, 0xEE, /* 0x40-0x43 */
+ 0xB9, 0xEF, 0xB9, 0xF0, 0x93, 0x6B, 0x93, 0x6C, /* 0x44-0x47 */
+ 0x93, 0x6D, 0xB9, 0xF1, 0x93, 0x6E, 0x93, 0x6F, /* 0x48-0x4B */
+ 0xB9, 0xF2, 0xB9, 0xF3, 0x93, 0x70, 0x93, 0x71, /* 0x4C-0x4F */
+ 0xB9, 0xF4, 0x93, 0x72, 0x93, 0x73, 0x93, 0x74, /* 0x50-0x53 */
+ 0x93, 0x75, 0x93, 0x76, 0x93, 0x77, 0x93, 0x78, /* 0x54-0x57 */
+ 0x93, 0x79, 0x93, 0x7A, 0x93, 0x81, 0x93, 0x82, /* 0x58-0x5B */
+ 0x93, 0x83, 0xB9, 0xF5, 0x93, 0x84, 0x93, 0x85, /* 0x5C-0x5F */
+ 0x93, 0x86, 0x93, 0x87, 0x93, 0x88, 0x93, 0x89, /* 0x60-0x63 */
+ 0x93, 0x8A, 0x93, 0x8B, 0x93, 0x8C, 0x93, 0x8D, /* 0x64-0x67 */
+ 0x93, 0x8E, 0x93, 0x8F, 0x93, 0x90, 0x93, 0x91, /* 0x68-0x6B */
+ 0x93, 0x92, 0x93, 0x93, 0x93, 0x94, 0x93, 0x95, /* 0x6C-0x6F */
+ 0x93, 0x96, 0x93, 0x97, 0x93, 0x98, 0x93, 0x99, /* 0x70-0x73 */
+ 0x93, 0x9A, 0x93, 0x9B, 0x93, 0x9C, 0x93, 0x9D, /* 0x74-0x77 */
+ 0x93, 0x9E, 0x93, 0x9F, 0x93, 0xA0, 0x93, 0xA1, /* 0x78-0x7B */
+ 0x93, 0xA2, 0x93, 0xA3, 0x93, 0xA4, 0x93, 0xA5, /* 0x7C-0x7F */
+
+ 0x93, 0xA6, 0x93, 0xA7, 0x93, 0xA8, 0x93, 0xA9, /* 0x80-0x83 */
+ 0xB9, 0xF6, 0xB9, 0xF7, 0x93, 0xAA, 0x93, 0xAB, /* 0x84-0x87 */
+ 0xB9, 0xF8, 0x93, 0xAC, 0x93, 0xAD, 0xB9, 0xF9, /* 0x88-0x8B */
+ 0xB9, 0xFA, 0x93, 0xAE, 0xB9, 0xFB, 0x93, 0xAF, /* 0x8C-0x8F */
+ 0x93, 0xB0, 0x93, 0xB1, 0x93, 0xB2, 0x93, 0xB3, /* 0x90-0x93 */
+ 0xB9, 0xFC, 0xB9, 0xFD, 0x93, 0xB4, 0xB9, 0xFE, /* 0x94-0x97 */
+ 0x93, 0xB5, 0xBA, 0xA1, 0xBA, 0xA2, 0x93, 0xB6, /* 0x98-0x9B */
+ 0x93, 0xB7, 0x93, 0xB8, 0x93, 0xB9, 0x93, 0xBA, /* 0x9C-0x9F */
+ 0xBA, 0xA3, 0xBA, 0xA4, 0x93, 0xBB, 0x93, 0xBC, /* 0xA0-0xA3 */
+ 0xBA, 0xA5, 0x93, 0xBD, 0x93, 0xBE, 0xBA, 0xA6, /* 0xA4-0xA7 */
+ 0xBA, 0xA7, 0x93, 0xBF, 0x93, 0xC0, 0x93, 0xC1, /* 0xA8-0xAB */
+ 0x93, 0xC2, 0x93, 0xC3, 0x93, 0xC4, 0x93, 0xC5, /* 0xAC-0xAF */
+ 0xBA, 0xA8, 0xBA, 0xA9, 0x93, 0xC6, 0xBA, 0xAA, /* 0xB0-0xB3 */
+ 0xBA, 0xAB, 0xBA, 0xAC, 0x93, 0xC7, 0x93, 0xC8, /* 0xB4-0xB7 */
+ 0x93, 0xC9, 0x93, 0xCA, 0x93, 0xCB, 0x93, 0xCC, /* 0xB8-0xBB */
+ 0xBA, 0xAD, 0xBA, 0xAE, 0x93, 0xCD, 0x93, 0xCE, /* 0xBC-0xBF */
+ 0xBA, 0xAF, 0x93, 0xCF, 0x93, 0xD0, 0x93, 0xD1, /* 0xC0-0xC3 */
+ 0xBA, 0xB0, 0x93, 0xD2, 0x93, 0xD3, 0x93, 0xD4, /* 0xC4-0xC7 */
+ 0x93, 0xD5, 0x93, 0xD6, 0x93, 0xD7, 0x93, 0xD8, /* 0xC8-0xCB */
+ 0x93, 0xD9, 0xBA, 0xB1, 0x93, 0xDA, 0xBA, 0xB2, /* 0xCC-0xCF */
+ 0xBA, 0xB3, 0xBA, 0xB4, 0x93, 0xDB, 0x93, 0xDC, /* 0xD0-0xD3 */
+ 0x93, 0xDD, 0xBA, 0xB5, 0x93, 0xDE, 0x93, 0xDF, /* 0xD4-0xD7 */
+ 0xBA, 0xB6, 0x93, 0xE0, 0x93, 0xE1, 0x93, 0xE2, /* 0xD8-0xDB */
+ 0xBA, 0xB7, 0x93, 0xE3, 0x93, 0xE4, 0x93, 0xE5, /* 0xDC-0xDF */
+ 0x93, 0xE6, 0x93, 0xE7, 0x93, 0xE8, 0x93, 0xE9, /* 0xE0-0xE3 */
+ 0x93, 0xEA, 0x93, 0xEB, 0x93, 0xEC, 0x93, 0xED, /* 0xE4-0xE7 */
+ 0x93, 0xEE, 0x93, 0xEF, 0x93, 0xF0, 0x93, 0xF1, /* 0xE8-0xEB */
+ 0x93, 0xF2, 0x93, 0xF3, 0x93, 0xF4, 0x93, 0xF5, /* 0xEC-0xEF */
+ 0x93, 0xF6, 0x93, 0xF7, 0x93, 0xF8, 0x93, 0xF9, /* 0xF0-0xF3 */
+ 0xBA, 0xB8, 0xBA, 0xB9, 0xBA, 0xBA, 0x93, 0xFA, /* 0xF4-0xF7 */
+ 0xBA, 0xBB, 0x93, 0xFB, 0x93, 0xFC, 0x93, 0xFD, /* 0xF8-0xFB */
+ 0xBA, 0xBC, 0x93, 0xFE, 0x94, 0x41, 0x94, 0x42, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_BD[512] = {
+ 0x94, 0x43, 0x94, 0x44, 0x94, 0x45, 0x94, 0x46, /* 0x00-0x03 */
+ 0xBA, 0xBD, 0xBA, 0xBE, 0x94, 0x47, 0xBA, 0xBF, /* 0x04-0x07 */
+ 0x94, 0x48, 0xBA, 0xC0, 0x94, 0x49, 0x94, 0x4A, /* 0x08-0x0B */
+ 0x94, 0x4B, 0x94, 0x4C, 0x94, 0x4D, 0x94, 0x4E, /* 0x0C-0x0F */
+ 0xBA, 0xC1, 0x94, 0x4F, 0x94, 0x50, 0x94, 0x51, /* 0x10-0x13 */
+ 0xBA, 0xC2, 0x94, 0x52, 0x94, 0x53, 0x94, 0x54, /* 0x14-0x17 */
+ 0x94, 0x55, 0x94, 0x56, 0x94, 0x57, 0x94, 0x58, /* 0x18-0x1B */
+ 0x94, 0x59, 0x94, 0x5A, 0x94, 0x61, 0x94, 0x62, /* 0x1C-0x1F */
+ 0x94, 0x63, 0x94, 0x64, 0x94, 0x65, 0x94, 0x66, /* 0x20-0x23 */
+ 0xBA, 0xC3, 0x94, 0x67, 0x94, 0x68, 0x94, 0x69, /* 0x24-0x27 */
+ 0x94, 0x6A, 0x94, 0x6B, 0x94, 0x6C, 0x94, 0x6D, /* 0x28-0x2B */
+ 0xBA, 0xC4, 0x94, 0x6E, 0x94, 0x6F, 0x94, 0x70, /* 0x2C-0x2F */
+ 0x94, 0x71, 0x94, 0x72, 0x94, 0x73, 0x94, 0x74, /* 0x30-0x33 */
+ 0x94, 0x75, 0x94, 0x76, 0x94, 0x77, 0x94, 0x78, /* 0x34-0x37 */
+ 0x94, 0x79, 0x94, 0x7A, 0x94, 0x81, 0x94, 0x82, /* 0x38-0x3B */
+ 0x94, 0x83, 0x94, 0x84, 0x94, 0x85, 0x94, 0x86, /* 0x3C-0x3F */
+ 0xBA, 0xC5, 0x94, 0x87, 0x94, 0x88, 0x94, 0x89, /* 0x40-0x43 */
+ 0x94, 0x8A, 0x94, 0x8B, 0x94, 0x8C, 0x94, 0x8D, /* 0x44-0x47 */
+ 0xBA, 0xC6, 0xBA, 0xC7, 0x94, 0x8E, 0x94, 0x8F, /* 0x48-0x4B */
+ 0xBA, 0xC8, 0x94, 0x90, 0x94, 0x91, 0x94, 0x92, /* 0x4C-0x4F */
+ 0xBA, 0xC9, 0x94, 0x93, 0x94, 0x94, 0x94, 0x95, /* 0x50-0x53 */
+ 0x94, 0x96, 0x94, 0x97, 0x94, 0x98, 0x94, 0x99, /* 0x54-0x57 */
+ 0xBA, 0xCA, 0xBA, 0xCB, 0x94, 0x9A, 0x94, 0x9B, /* 0x58-0x5B */
+ 0x94, 0x9C, 0x94, 0x9D, 0x94, 0x9E, 0x94, 0x9F, /* 0x5C-0x5F */
+ 0x94, 0xA0, 0x94, 0xA1, 0x94, 0xA2, 0x94, 0xA3, /* 0x60-0x63 */
+ 0xBA, 0xCC, 0x94, 0xA4, 0x94, 0xA5, 0x94, 0xA6, /* 0x64-0x67 */
+ 0xBA, 0xCD, 0x94, 0xA7, 0x94, 0xA8, 0x94, 0xA9, /* 0x68-0x6B */
+ 0x94, 0xAA, 0x94, 0xAB, 0x94, 0xAC, 0x94, 0xAD, /* 0x6C-0x6F */
+ 0x94, 0xAE, 0x94, 0xAF, 0x94, 0xB0, 0x94, 0xB1, /* 0x70-0x73 */
+ 0x94, 0xB2, 0x94, 0xB3, 0x94, 0xB4, 0x94, 0xB5, /* 0x74-0x77 */
+ 0x94, 0xB6, 0x94, 0xB7, 0x94, 0xB8, 0x94, 0xB9, /* 0x78-0x7B */
+ 0x94, 0xBA, 0x94, 0xBB, 0x94, 0xBC, 0x94, 0xBD, /* 0x7C-0x7F */
+
+ 0xBA, 0xCE, 0xBA, 0xCF, 0x94, 0xBE, 0x94, 0xBF, /* 0x80-0x83 */
+ 0xBA, 0xD0, 0x94, 0xC0, 0x94, 0xC1, 0xBA, 0xD1, /* 0x84-0x87 */
+ 0xBA, 0xD2, 0xBA, 0xD3, 0xBA, 0xD4, 0x94, 0xC2, /* 0x88-0x8B */
+ 0x94, 0xC3, 0x94, 0xC4, 0x94, 0xC5, 0x94, 0xC6, /* 0x8C-0x8F */
+ 0xBA, 0xD5, 0xBA, 0xD6, 0x94, 0xC7, 0xBA, 0xD7, /* 0x90-0x93 */
+ 0x94, 0xC8, 0xBA, 0xD8, 0x94, 0xC9, 0x94, 0xCA, /* 0x94-0x97 */
+ 0x94, 0xCB, 0xBA, 0xD9, 0xBA, 0xDA, 0x94, 0xCC, /* 0x98-0x9B */
+ 0xBA, 0xDB, 0x94, 0xCD, 0x94, 0xCE, 0x94, 0xCF, /* 0x9C-0x9F */
+ 0x94, 0xD0, 0x94, 0xD1, 0x94, 0xD2, 0x94, 0xD3, /* 0xA0-0xA3 */
+ 0xBA, 0xDC, 0x94, 0xD4, 0x94, 0xD5, 0x94, 0xD6, /* 0xA4-0xA7 */
+ 0x94, 0xD7, 0x94, 0xD8, 0x94, 0xD9, 0x94, 0xDA, /* 0xA8-0xAB */
+ 0x94, 0xDB, 0x94, 0xDC, 0x94, 0xDD, 0x94, 0xDE, /* 0xAC-0xAF */
+ 0xBA, 0xDD, 0x94, 0xDF, 0x94, 0xE0, 0x94, 0xE1, /* 0xB0-0xB3 */
+ 0x94, 0xE2, 0x94, 0xE3, 0x94, 0xE4, 0x94, 0xE5, /* 0xB4-0xB7 */
+ 0xBA, 0xDE, 0x94, 0xE6, 0x94, 0xE7, 0x94, 0xE8, /* 0xB8-0xBB */
+ 0x94, 0xE9, 0x94, 0xEA, 0x94, 0xEB, 0x94, 0xEC, /* 0xBC-0xBF */
+ 0x94, 0xED, 0x94, 0xEE, 0x94, 0xEF, 0x94, 0xF0, /* 0xC0-0xC3 */
+ 0x94, 0xF1, 0x94, 0xF2, 0x94, 0xF3, 0x94, 0xF4, /* 0xC4-0xC7 */
+ 0x94, 0xF5, 0x94, 0xF6, 0x94, 0xF7, 0x94, 0xF8, /* 0xC8-0xCB */
+ 0x94, 0xF9, 0x94, 0xFA, 0x94, 0xFB, 0x94, 0xFC, /* 0xCC-0xCF */
+ 0x94, 0xFD, 0x94, 0xFE, 0x95, 0x41, 0x95, 0x42, /* 0xD0-0xD3 */
+ 0xBA, 0xDF, 0xBA, 0xE0, 0x95, 0x43, 0x95, 0x44, /* 0xD4-0xD7 */
+ 0xBA, 0xE1, 0x95, 0x45, 0x95, 0x46, 0x95, 0x47, /* 0xD8-0xDB */
+ 0xBA, 0xE2, 0x95, 0x48, 0x95, 0x49, 0x95, 0x4A, /* 0xDC-0xDF */
+ 0x95, 0x4B, 0x95, 0x4C, 0x95, 0x4D, 0x95, 0x4E, /* 0xE0-0xE3 */
+ 0x95, 0x4F, 0x95, 0x50, 0x95, 0x51, 0x95, 0x52, /* 0xE4-0xE7 */
+ 0x95, 0x53, 0xBA, 0xE3, 0x95, 0x54, 0x95, 0x55, /* 0xE8-0xEB */
+ 0x95, 0x56, 0x95, 0x57, 0x95, 0x58, 0x95, 0x59, /* 0xEC-0xEF */
+ 0xBA, 0xE4, 0x95, 0x5A, 0x95, 0x61, 0x95, 0x62, /* 0xF0-0xF3 */
+ 0xBA, 0xE5, 0x95, 0x63, 0x95, 0x64, 0x95, 0x65, /* 0xF4-0xF7 */
+ 0xBA, 0xE6, 0x95, 0x66, 0x95, 0x67, 0x95, 0x68, /* 0xF8-0xFB */
+ 0x95, 0x69, 0x95, 0x6A, 0x95, 0x6B, 0x95, 0x6C, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_BE[512] = {
+ 0xBA, 0xE7, 0x95, 0x6D, 0x95, 0x6E, 0xBA, 0xE8, /* 0x00-0x03 */
+ 0x95, 0x6F, 0xBA, 0xE9, 0x95, 0x70, 0x95, 0x71, /* 0x04-0x07 */
+ 0x95, 0x72, 0x95, 0x73, 0x95, 0x74, 0x95, 0x75, /* 0x08-0x0B */
+ 0xBA, 0xEA, 0xBA, 0xEB, 0x95, 0x76, 0x95, 0x77, /* 0x0C-0x0F */
+ 0xBA, 0xEC, 0x95, 0x78, 0x95, 0x79, 0x95, 0x7A, /* 0x10-0x13 */
+ 0xBA, 0xED, 0x95, 0x81, 0x95, 0x82, 0x95, 0x83, /* 0x14-0x17 */
+ 0x95, 0x84, 0x95, 0x85, 0x95, 0x86, 0x95, 0x87, /* 0x18-0x1B */
+ 0xBA, 0xEE, 0xBA, 0xEF, 0x95, 0x88, 0xBA, 0xF0, /* 0x1C-0x1F */
+ 0x95, 0x89, 0x95, 0x8A, 0x95, 0x8B, 0x95, 0x8C, /* 0x20-0x23 */
+ 0x95, 0x8D, 0x95, 0x8E, 0x95, 0x8F, 0x95, 0x90, /* 0x24-0x27 */
+ 0x95, 0x91, 0x95, 0x92, 0x95, 0x93, 0x95, 0x94, /* 0x28-0x2B */
+ 0x95, 0x95, 0x95, 0x96, 0x95, 0x97, 0x95, 0x98, /* 0x2C-0x2F */
+ 0x95, 0x99, 0x95, 0x9A, 0x95, 0x9B, 0x95, 0x9C, /* 0x30-0x33 */
+ 0x95, 0x9D, 0x95, 0x9E, 0x95, 0x9F, 0x95, 0xA0, /* 0x34-0x37 */
+ 0x95, 0xA1, 0x95, 0xA2, 0x95, 0xA3, 0x95, 0xA4, /* 0x38-0x3B */
+ 0x95, 0xA5, 0x95, 0xA6, 0x95, 0xA7, 0x95, 0xA8, /* 0x3C-0x3F */
+ 0x95, 0xA9, 0x95, 0xAA, 0x95, 0xAB, 0x95, 0xAC, /* 0x40-0x43 */
+ 0xBA, 0xF1, 0xBA, 0xF2, 0x95, 0xAD, 0x95, 0xAE, /* 0x44-0x47 */
+ 0xBA, 0xF3, 0x95, 0xAF, 0x95, 0xB0, 0x95, 0xB1, /* 0x48-0x4B */
+ 0xBA, 0xF4, 0x95, 0xB2, 0xBA, 0xF5, 0x95, 0xB3, /* 0x4C-0x4F */
+ 0x95, 0xB4, 0x95, 0xB5, 0x95, 0xB6, 0x95, 0xB7, /* 0x50-0x53 */
+ 0xBA, 0xF6, 0xBA, 0xF7, 0x95, 0xB8, 0xBA, 0xF8, /* 0x54-0x57 */
+ 0x95, 0xB9, 0xBA, 0xF9, 0xBA, 0xFA, 0xBA, 0xFB, /* 0x58-0x5B */
+ 0x95, 0xBA, 0x95, 0xBB, 0x95, 0xBC, 0x95, 0xBD, /* 0x5C-0x5F */
+ 0xBA, 0xFC, 0xBA, 0xFD, 0x95, 0xBE, 0x95, 0xBF, /* 0x60-0x63 */
+ 0xBA, 0xFE, 0x95, 0xC0, 0x95, 0xC1, 0x95, 0xC2, /* 0x64-0x67 */
+ 0xBB, 0xA1, 0x95, 0xC3, 0xBB, 0xA2, 0x95, 0xC4, /* 0x68-0x6B */
+ 0x95, 0xC5, 0x95, 0xC6, 0x95, 0xC7, 0x95, 0xC8, /* 0x6C-0x6F */
+ 0xBB, 0xA3, 0xBB, 0xA4, 0x95, 0xC9, 0xBB, 0xA5, /* 0x70-0x73 */
+ 0xBB, 0xA6, 0xBB, 0xA7, 0x95, 0xCA, 0x95, 0xCB, /* 0x74-0x77 */
+ 0x95, 0xCC, 0x95, 0xCD, 0x95, 0xCE, 0xBB, 0xA8, /* 0x78-0x7B */
+ 0xBB, 0xA9, 0xBB, 0xAA, 0x95, 0xCF, 0x95, 0xD0, /* 0x7C-0x7F */
+
+ 0xBB, 0xAB, 0x95, 0xD1, 0x95, 0xD2, 0x95, 0xD3, /* 0x80-0x83 */
+ 0xBB, 0xAC, 0x95, 0xD4, 0x95, 0xD5, 0x95, 0xD6, /* 0x84-0x87 */
+ 0x95, 0xD7, 0x95, 0xD8, 0x95, 0xD9, 0x95, 0xDA, /* 0x88-0x8B */
+ 0xBB, 0xAD, 0xBB, 0xAE, 0x95, 0xDB, 0xBB, 0xAF, /* 0x8C-0x8F */
+ 0xBB, 0xB0, 0xBB, 0xB1, 0x95, 0xDC, 0x95, 0xDD, /* 0x90-0x93 */
+ 0x95, 0xDE, 0x95, 0xDF, 0x95, 0xE0, 0x95, 0xE1, /* 0x94-0x97 */
+ 0xBB, 0xB2, 0xBB, 0xB3, 0x95, 0xE2, 0x95, 0xE3, /* 0x98-0x9B */
+ 0x95, 0xE4, 0x95, 0xE5, 0x95, 0xE6, 0x95, 0xE7, /* 0x9C-0x9F */
+ 0x95, 0xE8, 0x95, 0xE9, 0x95, 0xEA, 0x95, 0xEB, /* 0xA0-0xA3 */
+ 0x95, 0xEC, 0x95, 0xED, 0x95, 0xEE, 0x95, 0xEF, /* 0xA4-0xA7 */
+ 0xBB, 0xB4, 0x95, 0xF0, 0x95, 0xF1, 0x95, 0xF2, /* 0xA8-0xAB */
+ 0x95, 0xF3, 0x95, 0xF4, 0x95, 0xF5, 0x95, 0xF6, /* 0xAC-0xAF */
+ 0x95, 0xF7, 0x95, 0xF8, 0x95, 0xF9, 0x95, 0xFA, /* 0xB0-0xB3 */
+ 0x95, 0xFB, 0x95, 0xFC, 0x95, 0xFD, 0x95, 0xFE, /* 0xB4-0xB7 */
+ 0x96, 0x41, 0x96, 0x42, 0x96, 0x43, 0x96, 0x44, /* 0xB8-0xBB */
+ 0x96, 0x45, 0x96, 0x46, 0x96, 0x47, 0x96, 0x48, /* 0xBC-0xBF */
+ 0x96, 0x49, 0x96, 0x4A, 0x96, 0x4B, 0x96, 0x4C, /* 0xC0-0xC3 */
+ 0x96, 0x4D, 0x96, 0x4E, 0x96, 0x4F, 0x96, 0x50, /* 0xC4-0xC7 */
+ 0x96, 0x51, 0x96, 0x52, 0x96, 0x53, 0x96, 0x54, /* 0xC8-0xCB */
+ 0x96, 0x55, 0x96, 0x56, 0x96, 0x57, 0x96, 0x58, /* 0xCC-0xCF */
+ 0xBB, 0xB5, 0xBB, 0xB6, 0x96, 0x59, 0x96, 0x5A, /* 0xD0-0xD3 */
+ 0xBB, 0xB7, 0x96, 0x61, 0x96, 0x62, 0xBB, 0xB8, /* 0xD4-0xD7 */
+ 0xBB, 0xB9, 0x96, 0x63, 0x96, 0x64, 0x96, 0x65, /* 0xD8-0xDB */
+ 0x96, 0x66, 0x96, 0x67, 0x96, 0x68, 0x96, 0x69, /* 0xDC-0xDF */
+ 0xBB, 0xBA, 0x96, 0x6A, 0x96, 0x6B, 0xBB, 0xBB, /* 0xE0-0xE3 */
+ 0xBB, 0xBC, 0xBB, 0xBD, 0x96, 0x6C, 0x96, 0x6D, /* 0xE4-0xE7 */
+ 0x96, 0x6E, 0x96, 0x6F, 0x96, 0x70, 0x96, 0x71, /* 0xE8-0xEB */
+ 0xBB, 0xBE, 0x96, 0x72, 0x96, 0x73, 0x96, 0x74, /* 0xEC-0xEF */
+ 0x96, 0x75, 0x96, 0x76, 0x96, 0x77, 0x96, 0x78, /* 0xF0-0xF3 */
+ 0x96, 0x79, 0x96, 0x7A, 0x96, 0x81, 0x96, 0x82, /* 0xF4-0xF7 */
+ 0x96, 0x83, 0x96, 0x84, 0x96, 0x85, 0x96, 0x86, /* 0xF8-0xFB */
+ 0x96, 0x87, 0x96, 0x88, 0x96, 0x89, 0x96, 0x8A, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_BF[512] = {
+ 0x96, 0x8B, 0xBB, 0xBF, 0x96, 0x8C, 0x96, 0x8D, /* 0x00-0x03 */
+ 0x96, 0x8E, 0x96, 0x8F, 0x96, 0x90, 0x96, 0x91, /* 0x04-0x07 */
+ 0xBB, 0xC0, 0xBB, 0xC1, 0x96, 0x92, 0x96, 0x93, /* 0x08-0x0B */
+ 0x96, 0x94, 0x96, 0x95, 0x96, 0x96, 0x96, 0x97, /* 0x0C-0x0F */
+ 0x96, 0x98, 0x96, 0x99, 0x96, 0x9A, 0x96, 0x9B, /* 0x10-0x13 */
+ 0x96, 0x9C, 0x96, 0x9D, 0x96, 0x9E, 0x96, 0x9F, /* 0x14-0x17 */
+ 0xBB, 0xC2, 0xBB, 0xC3, 0x96, 0xA0, 0xBB, 0xC4, /* 0x18-0x1B */
+ 0xBB, 0xC5, 0xBB, 0xC6, 0x96, 0xA1, 0x96, 0xA2, /* 0x1C-0x1F */
+ 0x96, 0xA3, 0x96, 0xA4, 0x96, 0xA5, 0x96, 0xA6, /* 0x20-0x23 */
+ 0x96, 0xA7, 0x96, 0xA8, 0x96, 0xA9, 0x96, 0xAA, /* 0x24-0x27 */
+ 0x96, 0xAB, 0x96, 0xAC, 0x96, 0xAD, 0x96, 0xAE, /* 0x28-0x2B */
+ 0x96, 0xAF, 0x96, 0xB0, 0x96, 0xB1, 0x96, 0xB2, /* 0x2C-0x2F */
+ 0x96, 0xB3, 0x96, 0xB4, 0x96, 0xB5, 0x96, 0xB6, /* 0x30-0x33 */
+ 0x96, 0xB7, 0x96, 0xB8, 0x96, 0xB9, 0x96, 0xBA, /* 0x34-0x37 */
+ 0x96, 0xBB, 0x96, 0xBC, 0x96, 0xBD, 0x96, 0xBE, /* 0x38-0x3B */
+ 0x96, 0xBF, 0x96, 0xC0, 0x96, 0xC1, 0x96, 0xC2, /* 0x3C-0x3F */
+ 0xBB, 0xC7, 0xBB, 0xC8, 0x96, 0xC3, 0x96, 0xC4, /* 0x40-0x43 */
+ 0xBB, 0xC9, 0x96, 0xC5, 0x96, 0xC6, 0x96, 0xC7, /* 0x44-0x47 */
+ 0xBB, 0xCA, 0x96, 0xC8, 0x96, 0xC9, 0x96, 0xCA, /* 0x48-0x4B */
+ 0x96, 0xCB, 0x96, 0xCC, 0x96, 0xCD, 0x96, 0xCE, /* 0x4C-0x4F */
+ 0xBB, 0xCB, 0xBB, 0xCC, 0x96, 0xCF, 0x96, 0xD0, /* 0x50-0x53 */
+ 0x96, 0xD1, 0xBB, 0xCD, 0x96, 0xD2, 0x96, 0xD3, /* 0x54-0x57 */
+ 0x96, 0xD4, 0x96, 0xD5, 0x96, 0xD6, 0x96, 0xD7, /* 0x58-0x5B */
+ 0x96, 0xD8, 0x96, 0xD9, 0x96, 0xDA, 0x96, 0xDB, /* 0x5C-0x5F */
+ 0x96, 0xDC, 0x96, 0xDD, 0x96, 0xDE, 0x96, 0xDF, /* 0x60-0x63 */
+ 0x96, 0xE0, 0x96, 0xE1, 0x96, 0xE2, 0x96, 0xE3, /* 0x64-0x67 */
+ 0x96, 0xE4, 0x96, 0xE5, 0x96, 0xE6, 0x96, 0xE7, /* 0x68-0x6B */
+ 0x96, 0xE8, 0x96, 0xE9, 0x96, 0xEA, 0x96, 0xEB, /* 0x6C-0x6F */
+ 0x96, 0xEC, 0x96, 0xED, 0x96, 0xEE, 0x96, 0xEF, /* 0x70-0x73 */
+ 0x96, 0xF0, 0x96, 0xF1, 0x96, 0xF2, 0x96, 0xF3, /* 0x74-0x77 */
+ 0x96, 0xF4, 0x96, 0xF5, 0x96, 0xF6, 0x96, 0xF7, /* 0x78-0x7B */
+ 0x96, 0xF8, 0x96, 0xF9, 0x96, 0xFA, 0x96, 0xFB, /* 0x7C-0x7F */
+
+ 0x96, 0xFC, 0x96, 0xFD, 0x96, 0xFE, 0x97, 0x41, /* 0x80-0x83 */
+ 0x97, 0x42, 0x97, 0x43, 0x97, 0x44, 0x97, 0x45, /* 0x84-0x87 */
+ 0x97, 0x46, 0x97, 0x47, 0x97, 0x48, 0x97, 0x49, /* 0x88-0x8B */
+ 0x97, 0x4A, 0x97, 0x4B, 0x97, 0x4C, 0x97, 0x4D, /* 0x8C-0x8F */
+ 0x97, 0x4E, 0x97, 0x4F, 0x97, 0x50, 0x97, 0x51, /* 0x90-0x93 */
+ 0xBB, 0xCE, 0x97, 0x52, 0x97, 0x53, 0x97, 0x54, /* 0x94-0x97 */
+ 0x97, 0x55, 0x97, 0x56, 0x97, 0x57, 0x97, 0x58, /* 0x98-0x9B */
+ 0x97, 0x59, 0x97, 0x5A, 0x97, 0x61, 0x97, 0x62, /* 0x9C-0x9F */
+ 0x97, 0x63, 0x97, 0x64, 0x97, 0x65, 0x97, 0x66, /* 0xA0-0xA3 */
+ 0x97, 0x67, 0x97, 0x68, 0x97, 0x69, 0x97, 0x6A, /* 0xA4-0xA7 */
+ 0x97, 0x6B, 0x97, 0x6C, 0x97, 0x6D, 0x97, 0x6E, /* 0xA8-0xAB */
+ 0x97, 0x6F, 0x97, 0x70, 0x97, 0x71, 0x97, 0x72, /* 0xAC-0xAF */
+ 0xBB, 0xCF, 0x97, 0x73, 0x97, 0x74, 0x97, 0x75, /* 0xB0-0xB3 */
+ 0x97, 0x76, 0x97, 0x77, 0x97, 0x78, 0x97, 0x79, /* 0xB4-0xB7 */
+ 0x97, 0x7A, 0x97, 0x81, 0x97, 0x82, 0x97, 0x83, /* 0xB8-0xBB */
+ 0x97, 0x84, 0x97, 0x85, 0x97, 0x86, 0x97, 0x87, /* 0xBC-0xBF */
+ 0x97, 0x88, 0x97, 0x89, 0x97, 0x8A, 0x97, 0x8B, /* 0xC0-0xC3 */
+ 0x97, 0x8C, 0xBB, 0xD0, 0x97, 0x8D, 0x97, 0x8E, /* 0xC4-0xC7 */
+ 0x97, 0x8F, 0x97, 0x90, 0x97, 0x91, 0x97, 0x92, /* 0xC8-0xCB */
+ 0xBB, 0xD1, 0xBB, 0xD2, 0x97, 0x93, 0x97, 0x94, /* 0xCC-0xCF */
+ 0xBB, 0xD3, 0x97, 0x95, 0x97, 0x96, 0x97, 0x97, /* 0xD0-0xD3 */
+ 0xBB, 0xD4, 0x97, 0x98, 0x97, 0x99, 0x97, 0x9A, /* 0xD4-0xD7 */
+ 0x97, 0x9B, 0x97, 0x9C, 0x97, 0x9D, 0x97, 0x9E, /* 0xD8-0xDB */
+ 0xBB, 0xD5, 0x97, 0x9F, 0x97, 0xA0, 0xBB, 0xD6, /* 0xDC-0xDF */
+ 0x97, 0xA1, 0xBB, 0xD7, 0x97, 0xA2, 0x97, 0xA3, /* 0xE0-0xE3 */
+ 0x97, 0xA4, 0x97, 0xA5, 0x97, 0xA6, 0x97, 0xA7, /* 0xE4-0xE7 */
+ 0x97, 0xA8, 0x97, 0xA9, 0x97, 0xAA, 0x97, 0xAB, /* 0xE8-0xEB */
+ 0x97, 0xAC, 0x97, 0xAD, 0x97, 0xAE, 0x97, 0xAF, /* 0xEC-0xEF */
+ 0x97, 0xB0, 0x97, 0xB1, 0x97, 0xB2, 0x97, 0xB3, /* 0xF0-0xF3 */
+ 0x97, 0xB4, 0x97, 0xB5, 0x97, 0xB6, 0x97, 0xB7, /* 0xF4-0xF7 */
+ 0x97, 0xB8, 0x97, 0xB9, 0x97, 0xBA, 0x97, 0xBB, /* 0xF8-0xFB */
+ 0x97, 0xBC, 0x97, 0xBD, 0x97, 0xBE, 0x97, 0xBF, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_C0[512] = {
+ 0x97, 0xC0, 0x97, 0xC1, 0x97, 0xC2, 0x97, 0xC3, /* 0x00-0x03 */
+ 0x97, 0xC4, 0x97, 0xC5, 0x97, 0xC6, 0x97, 0xC7, /* 0x04-0x07 */
+ 0x97, 0xC8, 0x97, 0xC9, 0x97, 0xCA, 0x97, 0xCB, /* 0x08-0x0B */
+ 0x97, 0xCC, 0x97, 0xCD, 0x97, 0xCE, 0x97, 0xCF, /* 0x0C-0x0F */
+ 0x97, 0xD0, 0x97, 0xD1, 0x97, 0xD2, 0x97, 0xD3, /* 0x10-0x13 */
+ 0x97, 0xD4, 0x97, 0xD5, 0x97, 0xD6, 0x97, 0xD7, /* 0x14-0x17 */
+ 0x97, 0xD8, 0x97, 0xD9, 0x97, 0xDA, 0x97, 0xDB, /* 0x18-0x1B */
+ 0x97, 0xDC, 0x97, 0xDD, 0x97, 0xDE, 0x97, 0xDF, /* 0x1C-0x1F */
+ 0x97, 0xE0, 0x97, 0xE1, 0x97, 0xE2, 0x97, 0xE3, /* 0x20-0x23 */
+ 0x97, 0xE4, 0x97, 0xE5, 0x97, 0xE6, 0x97, 0xE7, /* 0x24-0x27 */
+ 0x97, 0xE8, 0x97, 0xE9, 0x97, 0xEA, 0x97, 0xEB, /* 0x28-0x2B */
+ 0x97, 0xEC, 0x97, 0xED, 0x97, 0xEE, 0x97, 0xEF, /* 0x2C-0x2F */
+ 0x97, 0xF0, 0x97, 0xF1, 0x97, 0xF2, 0x97, 0xF3, /* 0x30-0x33 */
+ 0x97, 0xF4, 0x97, 0xF5, 0x97, 0xF6, 0x97, 0xF7, /* 0x34-0x37 */
+ 0x97, 0xF8, 0x97, 0xF9, 0x97, 0xFA, 0x97, 0xFB, /* 0x38-0x3B */
+ 0xBB, 0xD8, 0x97, 0xFC, 0x97, 0xFD, 0x97, 0xFE, /* 0x3C-0x3F */
+ 0x98, 0x41, 0x98, 0x42, 0x98, 0x43, 0x98, 0x44, /* 0x40-0x43 */
+ 0x98, 0x45, 0x98, 0x46, 0x98, 0x47, 0x98, 0x48, /* 0x44-0x47 */
+ 0x98, 0x49, 0x98, 0x4A, 0x98, 0x4B, 0x98, 0x4C, /* 0x48-0x4B */
+ 0x98, 0x4D, 0x98, 0x4E, 0x98, 0x4F, 0x98, 0x50, /* 0x4C-0x4F */
+ 0x98, 0x51, 0xBB, 0xD9, 0x98, 0x52, 0x98, 0x53, /* 0x50-0x53 */
+ 0x98, 0x54, 0x98, 0x55, 0x98, 0x56, 0x98, 0x57, /* 0x54-0x57 */
+ 0xBB, 0xDA, 0x98, 0x58, 0x98, 0x59, 0x98, 0x5A, /* 0x58-0x5B */
+ 0xBB, 0xDB, 0x98, 0x61, 0x98, 0x62, 0x98, 0x63, /* 0x5C-0x5F */
+ 0xBB, 0xDC, 0x98, 0x64, 0x98, 0x65, 0x98, 0x66, /* 0x60-0x63 */
+ 0x98, 0x67, 0x98, 0x68, 0x98, 0x69, 0x98, 0x6A, /* 0x64-0x67 */
+ 0xBB, 0xDD, 0xBB, 0xDE, 0x98, 0x6B, 0x98, 0x6C, /* 0x68-0x6B */
+ 0x98, 0x6D, 0x98, 0x6E, 0x98, 0x6F, 0x98, 0x70, /* 0x6C-0x6F */
+ 0x98, 0x71, 0x98, 0x72, 0x98, 0x73, 0x98, 0x74, /* 0x70-0x73 */
+ 0x98, 0x75, 0x98, 0x76, 0x98, 0x77, 0x98, 0x78, /* 0x74-0x77 */
+ 0x98, 0x79, 0x98, 0x7A, 0x98, 0x81, 0x98, 0x82, /* 0x78-0x7B */
+ 0x98, 0x83, 0x98, 0x84, 0x98, 0x85, 0x98, 0x86, /* 0x7C-0x7F */
+
+ 0x98, 0x87, 0x98, 0x88, 0x98, 0x89, 0x98, 0x8A, /* 0x80-0x83 */
+ 0x98, 0x8B, 0x98, 0x8C, 0x98, 0x8D, 0x98, 0x8E, /* 0x84-0x87 */
+ 0x98, 0x8F, 0x98, 0x90, 0x98, 0x91, 0x98, 0x92, /* 0x88-0x8B */
+ 0x98, 0x93, 0x98, 0x94, 0x98, 0x95, 0x98, 0x96, /* 0x8C-0x8F */
+ 0xBB, 0xDF, 0xBB, 0xE0, 0x98, 0x97, 0x98, 0x98, /* 0x90-0x93 */
+ 0xBB, 0xE1, 0x98, 0x99, 0x98, 0x9A, 0x98, 0x9B, /* 0x94-0x97 */
+ 0xBB, 0xE2, 0x98, 0x9C, 0x98, 0x9D, 0x98, 0x9E, /* 0x98-0x9B */
+ 0x98, 0x9F, 0x98, 0xA0, 0x98, 0xA1, 0x98, 0xA2, /* 0x9C-0x9F */
+ 0xBB, 0xE3, 0xBB, 0xE4, 0x98, 0xA3, 0xBB, 0xE5, /* 0xA0-0xA3 */
+ 0x98, 0xA4, 0xBB, 0xE6, 0x98, 0xA5, 0x98, 0xA6, /* 0xA4-0xA7 */
+ 0x98, 0xA7, 0x98, 0xA8, 0x98, 0xA9, 0x98, 0xAA, /* 0xA8-0xAB */
+ 0xBB, 0xE7, 0xBB, 0xE8, 0x98, 0xAB, 0xBB, 0xE9, /* 0xAC-0xAF */
+ 0xBB, 0xEA, 0x98, 0xAC, 0x98, 0xAD, 0xBB, 0xEB, /* 0xB0-0xB3 */
+ 0xBB, 0xEC, 0xBB, 0xED, 0xBB, 0xEE, 0x98, 0xAE, /* 0xB4-0xB7 */
+ 0x98, 0xAF, 0x98, 0xB0, 0x98, 0xB1, 0x98, 0xB2, /* 0xB8-0xBB */
+ 0xBB, 0xEF, 0xBB, 0xF0, 0x98, 0xB3, 0xBB, 0xF1, /* 0xBC-0xBF */
+ 0xBB, 0xF2, 0xBB, 0xF3, 0x98, 0xB4, 0x98, 0xB5, /* 0xC0-0xC3 */
+ 0x98, 0xB6, 0xBB, 0xF4, 0x98, 0xB7, 0x98, 0xB8, /* 0xC4-0xC7 */
+ 0xBB, 0xF5, 0xBB, 0xF6, 0x98, 0xB9, 0x98, 0xBA, /* 0xC8-0xCB */
+ 0xBB, 0xF7, 0x98, 0xBB, 0x98, 0xBC, 0x98, 0xBD, /* 0xCC-0xCF */
+ 0xBB, 0xF8, 0x98, 0xBE, 0x98, 0xBF, 0x98, 0xC0, /* 0xD0-0xD3 */
+ 0x98, 0xC1, 0x98, 0xC2, 0x98, 0xC3, 0x98, 0xC4, /* 0xD4-0xD7 */
+ 0xBB, 0xF9, 0xBB, 0xFA, 0x98, 0xC5, 0xBB, 0xFB, /* 0xD8-0xDB */
+ 0xBB, 0xFC, 0xBB, 0xFD, 0x98, 0xC6, 0x98, 0xC7, /* 0xDC-0xDF */
+ 0x98, 0xC8, 0x98, 0xC9, 0x98, 0xCA, 0x98, 0xCB, /* 0xE0-0xE3 */
+ 0xBB, 0xFE, 0xBC, 0xA1, 0x98, 0xCC, 0x98, 0xCD, /* 0xE4-0xE7 */
+ 0xBC, 0xA2, 0x98, 0xCE, 0x98, 0xCF, 0x98, 0xD0, /* 0xE8-0xEB */
+ 0xBC, 0xA3, 0x98, 0xD1, 0x98, 0xD2, 0x98, 0xD3, /* 0xEC-0xEF */
+ 0x98, 0xD4, 0x98, 0xD5, 0x98, 0xD6, 0x98, 0xD7, /* 0xF0-0xF3 */
+ 0xBC, 0xA4, 0xBC, 0xA5, 0x98, 0xD8, 0xBC, 0xA6, /* 0xF4-0xF7 */
+ 0x98, 0xD9, 0xBC, 0xA7, 0x98, 0xDA, 0x98, 0xDB, /* 0xF8-0xFB */
+ 0x98, 0xDC, 0x98, 0xDD, 0x98, 0xDE, 0x98, 0xDF, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_C1[512] = {
+ 0xBC, 0xA8, 0x98, 0xE0, 0x98, 0xE1, 0x98, 0xE2, /* 0x00-0x03 */
+ 0xBC, 0xA9, 0x98, 0xE3, 0x98, 0xE4, 0x98, 0xE5, /* 0x04-0x07 */
+ 0xBC, 0xAA, 0x98, 0xE6, 0x98, 0xE7, 0x98, 0xE8, /* 0x08-0x0B */
+ 0x98, 0xE9, 0x98, 0xEA, 0x98, 0xEB, 0x98, 0xEC, /* 0x0C-0x0F */
+ 0xBC, 0xAB, 0x98, 0xED, 0x98, 0xEE, 0x98, 0xEF, /* 0x10-0x13 */
+ 0x98, 0xF0, 0xBC, 0xAC, 0x98, 0xF1, 0x98, 0xF2, /* 0x14-0x17 */
+ 0x98, 0xF3, 0x98, 0xF4, 0x98, 0xF5, 0x98, 0xF6, /* 0x18-0x1B */
+ 0xBC, 0xAD, 0xBC, 0xAE, 0xBC, 0xAF, 0xBC, 0xB0, /* 0x1C-0x1F */
+ 0xBC, 0xB1, 0x98, 0xF7, 0x98, 0xF8, 0xBC, 0xB2, /* 0x20-0x23 */
+ 0xBC, 0xB3, 0x98, 0xF9, 0xBC, 0xB4, 0xBC, 0xB5, /* 0x24-0x27 */
+ 0x98, 0xFA, 0x98, 0xFB, 0x98, 0xFC, 0x98, 0xFD, /* 0x28-0x2B */
+ 0xBC, 0xB6, 0xBC, 0xB7, 0x98, 0xFE, 0xBC, 0xB8, /* 0x2C-0x2F */
+ 0xBC, 0xB9, 0xBC, 0xBA, 0x99, 0x41, 0x99, 0x42, /* 0x30-0x33 */
+ 0x99, 0x43, 0x99, 0x44, 0xBC, 0xBB, 0x99, 0x45, /* 0x34-0x37 */
+ 0xBC, 0xBC, 0xBC, 0xBD, 0x99, 0x46, 0x99, 0x47, /* 0x38-0x3B */
+ 0xBC, 0xBE, 0x99, 0x48, 0x99, 0x49, 0x99, 0x4A, /* 0x3C-0x3F */
+ 0xBC, 0xBF, 0x99, 0x4B, 0x99, 0x4C, 0x99, 0x4D, /* 0x40-0x43 */
+ 0x99, 0x4E, 0x99, 0x4F, 0x99, 0x50, 0x99, 0x51, /* 0x44-0x47 */
+ 0xBC, 0xC0, 0xBC, 0xC1, 0x99, 0x52, 0xBC, 0xC2, /* 0x48-0x4B */
+ 0xBC, 0xC3, 0xBC, 0xC4, 0x99, 0x53, 0x99, 0x54, /* 0x4C-0x4F */
+ 0x99, 0x55, 0x99, 0x56, 0x99, 0x57, 0x99, 0x58, /* 0x50-0x53 */
+ 0xBC, 0xC5, 0xBC, 0xC6, 0x99, 0x59, 0x99, 0x5A, /* 0x54-0x57 */
+ 0xBC, 0xC7, 0x99, 0x61, 0x99, 0x62, 0x99, 0x63, /* 0x58-0x5B */
+ 0xBC, 0xC8, 0x99, 0x64, 0x99, 0x65, 0x99, 0x66, /* 0x5C-0x5F */
+ 0x99, 0x67, 0x99, 0x68, 0x99, 0x69, 0x99, 0x6A, /* 0x60-0x63 */
+ 0xBC, 0xC9, 0xBC, 0xCA, 0x99, 0x6B, 0xBC, 0xCB, /* 0x64-0x67 */
+ 0xBC, 0xCC, 0xBC, 0xCD, 0x99, 0x6C, 0x99, 0x6D, /* 0x68-0x6B */
+ 0x99, 0x6E, 0x99, 0x6F, 0x99, 0x70, 0x99, 0x71, /* 0x6C-0x6F */
+ 0xBC, 0xCE, 0x99, 0x72, 0x99, 0x73, 0x99, 0x74, /* 0x70-0x73 */
+ 0xBC, 0xCF, 0x99, 0x75, 0x99, 0x76, 0x99, 0x77, /* 0x74-0x77 */
+ 0xBC, 0xD0, 0x99, 0x78, 0x99, 0x79, 0x99, 0x7A, /* 0x78-0x7B */
+ 0x99, 0x81, 0x99, 0x82, 0x99, 0x83, 0x99, 0x84, /* 0x7C-0x7F */
+
+ 0x99, 0x85, 0x99, 0x86, 0x99, 0x87, 0x99, 0x88, /* 0x80-0x83 */
+ 0x99, 0x89, 0xBC, 0xD1, 0x99, 0x8A, 0x99, 0x8B, /* 0x84-0x87 */
+ 0x99, 0x8C, 0x99, 0x8D, 0x99, 0x8E, 0x99, 0x8F, /* 0x88-0x8B */
+ 0xBC, 0xD2, 0xBC, 0xD3, 0xBC, 0xD4, 0x99, 0x90, /* 0x8C-0x8F */
+ 0xBC, 0xD5, 0x99, 0x91, 0x99, 0x92, 0x99, 0x93, /* 0x90-0x93 */
+ 0xBC, 0xD6, 0x99, 0x94, 0xBC, 0xD7, 0x99, 0x95, /* 0x94-0x97 */
+ 0x99, 0x96, 0x99, 0x97, 0x99, 0x98, 0x99, 0x99, /* 0x98-0x9B */
+ 0xBC, 0xD8, 0xBC, 0xD9, 0x99, 0x9A, 0xBC, 0xDA, /* 0x9C-0x9F */
+ 0x99, 0x9B, 0xBC, 0xDB, 0x99, 0x9C, 0x99, 0x9D, /* 0xA0-0xA3 */
+ 0x99, 0x9E, 0xBC, 0xDC, 0x99, 0x9F, 0x99, 0xA0, /* 0xA4-0xA7 */
+ 0xBC, 0xDD, 0xBC, 0xDE, 0x99, 0xA1, 0x99, 0xA2, /* 0xA8-0xAB */
+ 0xBC, 0xDF, 0x99, 0xA3, 0x99, 0xA4, 0x99, 0xA5, /* 0xAC-0xAF */
+ 0xBC, 0xE0, 0x99, 0xA6, 0x99, 0xA7, 0x99, 0xA8, /* 0xB0-0xB3 */
+ 0x99, 0xA9, 0x99, 0xAA, 0x99, 0xAB, 0x99, 0xAC, /* 0xB4-0xB7 */
+ 0x99, 0xAD, 0x99, 0xAE, 0x99, 0xAF, 0x99, 0xB0, /* 0xB8-0xBB */
+ 0x99, 0xB1, 0xBC, 0xE1, 0x99, 0xB2, 0x99, 0xB3, /* 0xBC-0xBF */
+ 0x99, 0xB4, 0x99, 0xB5, 0x99, 0xB6, 0x99, 0xB7, /* 0xC0-0xC3 */
+ 0xBC, 0xE2, 0x99, 0xB8, 0x99, 0xB9, 0x99, 0xBA, /* 0xC4-0xC7 */
+ 0xBC, 0xE3, 0x99, 0xBB, 0x99, 0xBC, 0x99, 0xBD, /* 0xC8-0xCB */
+ 0xBC, 0xE4, 0x99, 0xBE, 0x99, 0xBF, 0x99, 0xC0, /* 0xCC-0xCF */
+ 0x99, 0xC1, 0x99, 0xC2, 0x99, 0xC3, 0x99, 0xC4, /* 0xD0-0xD3 */
+ 0xBC, 0xE5, 0x99, 0xC5, 0x99, 0xC6, 0xBC, 0xE6, /* 0xD4-0xD7 */
+ 0xBC, 0xE7, 0x99, 0xC7, 0x99, 0xC8, 0x99, 0xC9, /* 0xD8-0xDB */
+ 0x99, 0xCA, 0x99, 0xCB, 0x99, 0xCC, 0x99, 0xCD, /* 0xDC-0xDF */
+ 0xBC, 0xE8, 0x99, 0xCE, 0x99, 0xCF, 0x99, 0xD0, /* 0xE0-0xE3 */
+ 0xBC, 0xE9, 0x99, 0xD1, 0x99, 0xD2, 0x99, 0xD3, /* 0xE4-0xE7 */
+ 0xBC, 0xEA, 0x99, 0xD4, 0x99, 0xD5, 0x99, 0xD6, /* 0xE8-0xEB */
+ 0x99, 0xD7, 0x99, 0xD8, 0x99, 0xD9, 0x99, 0xDA, /* 0xEC-0xEF */
+ 0xBC, 0xEB, 0xBC, 0xEC, 0x99, 0xDB, 0xBC, 0xED, /* 0xF0-0xF3 */
+ 0x99, 0xDC, 0x99, 0xDD, 0x99, 0xDE, 0x99, 0xDF, /* 0xF4-0xF7 */
+ 0x99, 0xE0, 0x99, 0xE1, 0x99, 0xE2, 0x99, 0xE3, /* 0xF8-0xFB */
+ 0xBC, 0xEE, 0xBC, 0xEF, 0x99, 0xE4, 0x99, 0xE5, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_C2[512] = {
+ 0xBC, 0xF0, 0x99, 0xE6, 0x99, 0xE7, 0x99, 0xE8, /* 0x00-0x03 */
+ 0xBC, 0xF1, 0x99, 0xE9, 0x99, 0xEA, 0x99, 0xEB, /* 0x04-0x07 */
+ 0x99, 0xEC, 0x99, 0xED, 0x99, 0xEE, 0x99, 0xEF, /* 0x08-0x0B */
+ 0xBC, 0xF2, 0xBC, 0xF3, 0x99, 0xF0, 0xBC, 0xF4, /* 0x0C-0x0F */
+ 0x99, 0xF1, 0xBC, 0xF5, 0x99, 0xF2, 0x99, 0xF3, /* 0x10-0x13 */
+ 0x99, 0xF4, 0x99, 0xF5, 0x99, 0xF6, 0x99, 0xF7, /* 0x14-0x17 */
+ 0xBC, 0xF6, 0xBC, 0xF7, 0x99, 0xF8, 0x99, 0xF9, /* 0x18-0x1B */
+ 0xBC, 0xF8, 0x99, 0xFA, 0x99, 0xFB, 0xBC, 0xF9, /* 0x1C-0x1F */
+ 0xBC, 0xFA, 0x99, 0xFC, 0x99, 0xFD, 0x99, 0xFE, /* 0x20-0x23 */
+ 0x9A, 0x41, 0x9A, 0x42, 0x9A, 0x43, 0x9A, 0x44, /* 0x24-0x27 */
+ 0xBC, 0xFB, 0xBC, 0xFC, 0x9A, 0x45, 0xBC, 0xFD, /* 0x28-0x2B */
+ 0x9A, 0x46, 0xBC, 0xFE, 0x9A, 0x47, 0xBD, 0xA1, /* 0x2C-0x2F */
+ 0x9A, 0x48, 0xBD, 0xA2, 0xBD, 0xA3, 0x9A, 0x49, /* 0x30-0x33 */
+ 0xBD, 0xA4, 0x9A, 0x4A, 0x9A, 0x4B, 0x9A, 0x4C, /* 0x34-0x37 */
+ 0x9A, 0x4D, 0x9A, 0x4E, 0x9A, 0x4F, 0x9A, 0x50, /* 0x38-0x3B */
+ 0x9A, 0x51, 0x9A, 0x52, 0x9A, 0x53, 0x9A, 0x54, /* 0x3C-0x3F */
+ 0x9A, 0x55, 0x9A, 0x56, 0x9A, 0x57, 0x9A, 0x58, /* 0x40-0x43 */
+ 0x9A, 0x59, 0x9A, 0x5A, 0x9A, 0x61, 0x9A, 0x62, /* 0x44-0x47 */
+ 0xBD, 0xA5, 0x9A, 0x63, 0x9A, 0x64, 0x9A, 0x65, /* 0x48-0x4B */
+ 0x9A, 0x66, 0x9A, 0x67, 0x9A, 0x68, 0x9A, 0x69, /* 0x4C-0x4F */
+ 0xBD, 0xA6, 0xBD, 0xA7, 0x9A, 0x6A, 0x9A, 0x6B, /* 0x50-0x53 */
+ 0xBD, 0xA8, 0x9A, 0x6C, 0x9A, 0x6D, 0x9A, 0x6E, /* 0x54-0x57 */
+ 0xBD, 0xA9, 0x9A, 0x6F, 0x9A, 0x70, 0x9A, 0x71, /* 0x58-0x5B */
+ 0x9A, 0x72, 0x9A, 0x73, 0x9A, 0x74, 0x9A, 0x75, /* 0x5C-0x5F */
+ 0xBD, 0xAA, 0x9A, 0x76, 0x9A, 0x77, 0x9A, 0x78, /* 0x60-0x63 */
+ 0x9A, 0x79, 0xBD, 0xAB, 0x9A, 0x7A, 0x9A, 0x81, /* 0x64-0x67 */
+ 0x9A, 0x82, 0x9A, 0x83, 0x9A, 0x84, 0x9A, 0x85, /* 0x68-0x6B */
+ 0xBD, 0xAC, 0xBD, 0xAD, 0x9A, 0x86, 0x9A, 0x87, /* 0x6C-0x6F */
+ 0xBD, 0xAE, 0x9A, 0x88, 0x9A, 0x89, 0x9A, 0x8A, /* 0x70-0x73 */
+ 0xBD, 0xAF, 0x9A, 0x8B, 0x9A, 0x8C, 0x9A, 0x8D, /* 0x74-0x77 */
+ 0x9A, 0x8E, 0x9A, 0x8F, 0x9A, 0x90, 0x9A, 0x91, /* 0x78-0x7B */
+ 0xBD, 0xB0, 0xBD, 0xB1, 0x9A, 0x92, 0xBD, 0xB2, /* 0x7C-0x7F */
+
+ 0x9A, 0x93, 0xBD, 0xB3, 0x9A, 0x94, 0x9A, 0x95, /* 0x80-0x83 */
+ 0x9A, 0x96, 0x9A, 0x97, 0x9A, 0x98, 0x9A, 0x99, /* 0x84-0x87 */
+ 0xBD, 0xB4, 0xBD, 0xB5, 0x9A, 0x9A, 0x9A, 0x9B, /* 0x88-0x8B */
+ 0x9A, 0x9C, 0x9A, 0x9D, 0x9A, 0x9E, 0x9A, 0x9F, /* 0x8C-0x8F */
+ 0xBD, 0xB6, 0x9A, 0xA0, 0x9A, 0xA1, 0x9A, 0xA2, /* 0x90-0x93 */
+ 0x9A, 0xA3, 0x9A, 0xA4, 0x9A, 0xA5, 0x9A, 0xA6, /* 0x94-0x97 */
+ 0xBD, 0xB7, 0x9A, 0xA7, 0x9A, 0xA8, 0xBD, 0xB8, /* 0x98-0x9B */
+ 0x9A, 0xA9, 0xBD, 0xB9, 0x9A, 0xAA, 0x9A, 0xAB, /* 0x9C-0x9F */
+ 0x9A, 0xAC, 0x9A, 0xAD, 0x9A, 0xAE, 0x9A, 0xAF, /* 0xA0-0xA3 */
+ 0xBD, 0xBA, 0xBD, 0xBB, 0x9A, 0xB0, 0x9A, 0xB1, /* 0xA4-0xA7 */
+ 0xBD, 0xBC, 0x9A, 0xB2, 0x9A, 0xB3, 0x9A, 0xB4, /* 0xA8-0xAB */
+ 0xBD, 0xBD, 0xBD, 0xBE, 0x9A, 0xB5, 0x9A, 0xB6, /* 0xAC-0xAF */
+ 0x9A, 0xB7, 0x9A, 0xB8, 0x9A, 0xB9, 0x9A, 0xBA, /* 0xB0-0xB3 */
+ 0xBD, 0xBF, 0xBD, 0xC0, 0x9A, 0xBB, 0xBD, 0xC1, /* 0xB4-0xB7 */
+ 0x9A, 0xBC, 0xBD, 0xC2, 0x9A, 0xBD, 0x9A, 0xBE, /* 0xB8-0xBB */
+ 0x9A, 0xBF, 0x9A, 0xC0, 0x9A, 0xC1, 0x9A, 0xC2, /* 0xBC-0xBF */
+ 0x9A, 0xC3, 0x9A, 0xC4, 0x9A, 0xC5, 0x9A, 0xC6, /* 0xC0-0xC3 */
+ 0x9A, 0xC7, 0x9A, 0xC8, 0x9A, 0xC9, 0x9A, 0xCA, /* 0xC4-0xC7 */
+ 0x9A, 0xCB, 0x9A, 0xCC, 0x9A, 0xCD, 0x9A, 0xCE, /* 0xC8-0xCB */
+ 0x9A, 0xCF, 0x9A, 0xD0, 0x9A, 0xD1, 0x9A, 0xD2, /* 0xCC-0xCF */
+ 0x9A, 0xD3, 0x9A, 0xD4, 0x9A, 0xD5, 0x9A, 0xD6, /* 0xD0-0xD3 */
+ 0x9A, 0xD7, 0x9A, 0xD8, 0x9A, 0xD9, 0x9A, 0xDA, /* 0xD4-0xD7 */
+ 0x9A, 0xDB, 0x9A, 0xDC, 0x9A, 0xDD, 0x9A, 0xDE, /* 0xD8-0xDB */
+ 0xBD, 0xC3, 0xBD, 0xC4, 0x9A, 0xDF, 0x9A, 0xE0, /* 0xDC-0xDF */
+ 0xBD, 0xC5, 0x9A, 0xE1, 0x9A, 0xE2, 0xBD, 0xC6, /* 0xE0-0xE3 */
+ 0xBD, 0xC7, 0x9A, 0xE3, 0x9A, 0xE4, 0x9A, 0xE5, /* 0xE4-0xE7 */
+ 0x9A, 0xE6, 0x9A, 0xE7, 0x9A, 0xE8, 0xBD, 0xC8, /* 0xE8-0xEB */
+ 0xBD, 0xC9, 0xBD, 0xCA, 0x9A, 0xE9, 0xBD, 0xCB, /* 0xEC-0xEF */
+ 0x9A, 0xEA, 0xBD, 0xCC, 0x9A, 0xEB, 0x9A, 0xEC, /* 0xF0-0xF3 */
+ 0x9A, 0xED, 0x9A, 0xEE, 0xBD, 0xCD, 0x9A, 0xEF, /* 0xF4-0xF7 */
+ 0xBD, 0xCE, 0xBD, 0xCF, 0x9A, 0xF0, 0xBD, 0xD0, /* 0xF8-0xFB */
+ 0xBD, 0xD1, 0x9A, 0xF1, 0x9A, 0xF2, 0x9A, 0xF3, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_C3[512] = {
+ 0xBD, 0xD2, 0x9A, 0xF4, 0x9A, 0xF5, 0x9A, 0xF6, /* 0x00-0x03 */
+ 0x9A, 0xF7, 0x9A, 0xF8, 0x9A, 0xF9, 0x9A, 0xFA, /* 0x04-0x07 */
+ 0xBD, 0xD3, 0xBD, 0xD4, 0x9A, 0xFB, 0x9A, 0xFC, /* 0x08-0x0B */
+ 0xBD, 0xD5, 0xBD, 0xD6, 0x9A, 0xFD, 0x9A, 0xFE, /* 0x0C-0x0F */
+ 0x9B, 0x41, 0x9B, 0x42, 0x9B, 0x43, 0xBD, 0xD7, /* 0x10-0x13 */
+ 0xBD, 0xD8, 0xBD, 0xD9, 0x9B, 0x44, 0x9B, 0x45, /* 0x14-0x17 */
+ 0xBD, 0xDA, 0x9B, 0x46, 0x9B, 0x47, 0x9B, 0x48, /* 0x18-0x1B */
+ 0xBD, 0xDB, 0x9B, 0x49, 0x9B, 0x4A, 0x9B, 0x4B, /* 0x1C-0x1F */
+ 0x9B, 0x4C, 0x9B, 0x4D, 0x9B, 0x4E, 0x9B, 0x4F, /* 0x20-0x23 */
+ 0xBD, 0xDC, 0xBD, 0xDD, 0x9B, 0x50, 0x9B, 0x51, /* 0x24-0x27 */
+ 0xBD, 0xDE, 0xBD, 0xDF, 0x9B, 0x52, 0x9B, 0x53, /* 0x28-0x2B */
+ 0x9B, 0x54, 0x9B, 0x55, 0x9B, 0x56, 0x9B, 0x57, /* 0x2C-0x2F */
+ 0x9B, 0x58, 0x9B, 0x59, 0x9B, 0x5A, 0x9B, 0x61, /* 0x30-0x33 */
+ 0x9B, 0x62, 0x9B, 0x63, 0x9B, 0x64, 0x9B, 0x65, /* 0x34-0x37 */
+ 0x9B, 0x66, 0x9B, 0x67, 0x9B, 0x68, 0x9B, 0x69, /* 0x38-0x3B */
+ 0x9B, 0x6A, 0x9B, 0x6B, 0x9B, 0x6C, 0x9B, 0x6D, /* 0x3C-0x3F */
+ 0x9B, 0x6E, 0x9B, 0x6F, 0x9B, 0x70, 0x9B, 0x71, /* 0x40-0x43 */
+ 0x9B, 0x72, 0xBD, 0xE0, 0x9B, 0x73, 0x9B, 0x74, /* 0x44-0x47 */
+ 0x9B, 0x75, 0x9B, 0x76, 0x9B, 0x77, 0x9B, 0x78, /* 0x48-0x4B */
+ 0x9B, 0x79, 0x9B, 0x7A, 0x9B, 0x81, 0x9B, 0x82, /* 0x4C-0x4F */
+ 0x9B, 0x83, 0x9B, 0x84, 0x9B, 0x85, 0x9B, 0x86, /* 0x50-0x53 */
+ 0x9B, 0x87, 0x9B, 0x88, 0x9B, 0x89, 0x9B, 0x8A, /* 0x54-0x57 */
+ 0x9B, 0x8B, 0x9B, 0x8C, 0x9B, 0x8D, 0x9B, 0x8E, /* 0x58-0x5B */
+ 0x9B, 0x8F, 0x9B, 0x90, 0x9B, 0x91, 0x9B, 0x92, /* 0x5C-0x5F */
+ 0x9B, 0x93, 0x9B, 0x94, 0x9B, 0x95, 0x9B, 0x96, /* 0x60-0x63 */
+ 0x9B, 0x97, 0x9B, 0x98, 0x9B, 0x99, 0x9B, 0x9A, /* 0x64-0x67 */
+ 0xBD, 0xE1, 0xBD, 0xE2, 0x9B, 0x9B, 0x9B, 0x9C, /* 0x68-0x6B */
+ 0xBD, 0xE3, 0x9B, 0x9D, 0x9B, 0x9E, 0x9B, 0x9F, /* 0x6C-0x6F */
+ 0xBD, 0xE4, 0x9B, 0xA0, 0xBD, 0xE5, 0x9B, 0xA1, /* 0x70-0x73 */
+ 0x9B, 0xA2, 0x9B, 0xA3, 0x9B, 0xA4, 0x9B, 0xA5, /* 0x74-0x77 */
+ 0xBD, 0xE6, 0xBD, 0xE7, 0x9B, 0xA6, 0x9B, 0xA7, /* 0x78-0x7B */
+ 0xBD, 0xE8, 0xBD, 0xE9, 0x9B, 0xA8, 0x9B, 0xA9, /* 0x7C-0x7F */
+
+ 0x9B, 0xAA, 0x9B, 0xAB, 0x9B, 0xAC, 0x9B, 0xAD, /* 0x80-0x83 */
+ 0xBD, 0xEA, 0x9B, 0xAE, 0x9B, 0xAF, 0x9B, 0xB0, /* 0x84-0x87 */
+ 0xBD, 0xEB, 0x9B, 0xB1, 0x9B, 0xB2, 0x9B, 0xB3, /* 0x88-0x8B */
+ 0xBD, 0xEC, 0x9B, 0xB4, 0x9B, 0xB5, 0x9B, 0xB6, /* 0x8C-0x8F */
+ 0x9B, 0xB7, 0x9B, 0xB8, 0x9B, 0xB9, 0x9B, 0xBA, /* 0x90-0x93 */
+ 0x9B, 0xBB, 0x9B, 0xBC, 0x9B, 0xBD, 0x9B, 0xBE, /* 0x94-0x97 */
+ 0x9B, 0xBF, 0x9B, 0xC0, 0x9B, 0xC1, 0x9B, 0xC2, /* 0x98-0x9B */
+ 0x9B, 0xC3, 0x9B, 0xC4, 0x9B, 0xC5, 0x9B, 0xC6, /* 0x9C-0x9F */
+ 0x9B, 0xC7, 0x9B, 0xC8, 0x9B, 0xC9, 0x9B, 0xCA, /* 0xA0-0xA3 */
+ 0x9B, 0xCB, 0x9B, 0xCC, 0x9B, 0xCD, 0x9B, 0xCE, /* 0xA4-0xA7 */
+ 0x9B, 0xCF, 0x9B, 0xD0, 0x9B, 0xD1, 0x9B, 0xD2, /* 0xA8-0xAB */
+ 0x9B, 0xD3, 0x9B, 0xD4, 0x9B, 0xD5, 0x9B, 0xD6, /* 0xAC-0xAF */
+ 0x9B, 0xD7, 0x9B, 0xD8, 0x9B, 0xD9, 0x9B, 0xDA, /* 0xB0-0xB3 */
+ 0x9B, 0xDB, 0x9B, 0xDC, 0x9B, 0xDD, 0x9B, 0xDE, /* 0xB4-0xB7 */
+ 0x9B, 0xDF, 0x9B, 0xE0, 0x9B, 0xE1, 0x9B, 0xE2, /* 0xB8-0xBB */
+ 0x9B, 0xE3, 0x9B, 0xE4, 0x9B, 0xE5, 0x9B, 0xE6, /* 0xBC-0xBF */
+ 0xBD, 0xED, 0x9B, 0xE7, 0x9B, 0xE8, 0x9B, 0xE9, /* 0xC0-0xC3 */
+ 0x9B, 0xEA, 0x9B, 0xEB, 0x9B, 0xEC, 0x9B, 0xED, /* 0xC4-0xC7 */
+ 0x9B, 0xEE, 0x9B, 0xEF, 0x9B, 0xF0, 0x9B, 0xF1, /* 0xC8-0xCB */
+ 0x9B, 0xF2, 0x9B, 0xF3, 0x9B, 0xF4, 0x9B, 0xF5, /* 0xCC-0xCF */
+ 0x9B, 0xF6, 0x9B, 0xF7, 0x9B, 0xF8, 0x9B, 0xF9, /* 0xD0-0xD3 */
+ 0x9B, 0xFA, 0x9B, 0xFB, 0x9B, 0xFC, 0x9B, 0xFD, /* 0xD4-0xD7 */
+ 0xBD, 0xEE, 0xBD, 0xEF, 0x9B, 0xFE, 0x9C, 0x41, /* 0xD8-0xDB */
+ 0xBD, 0xF0, 0x9C, 0x42, 0x9C, 0x43, 0xBD, 0xF1, /* 0xDC-0xDF */
+ 0xBD, 0xF2, 0x9C, 0x44, 0xBD, 0xF3, 0x9C, 0x45, /* 0xE0-0xE3 */
+ 0x9C, 0x46, 0x9C, 0x47, 0x9C, 0x48, 0x9C, 0x49, /* 0xE4-0xE7 */
+ 0xBD, 0xF4, 0xBD, 0xF5, 0x9C, 0x4A, 0x9C, 0x4B, /* 0xE8-0xEB */
+ 0x9C, 0x4C, 0xBD, 0xF6, 0x9C, 0x4D, 0x9C, 0x4E, /* 0xEC-0xEF */
+ 0x9C, 0x4F, 0x9C, 0x50, 0x9C, 0x51, 0x9C, 0x52, /* 0xF0-0xF3 */
+ 0xBD, 0xF7, 0xBD, 0xF8, 0x9C, 0x53, 0x9C, 0x54, /* 0xF4-0xF7 */
+ 0xBD, 0xF9, 0x9C, 0x55, 0x9C, 0x56, 0x9C, 0x57, /* 0xF8-0xFB */
+ 0x9C, 0x58, 0x9C, 0x59, 0x9C, 0x5A, 0x9C, 0x61, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_C4[512] = {
+ 0x9C, 0x62, 0x9C, 0x63, 0x9C, 0x64, 0x9C, 0x65, /* 0x00-0x03 */
+ 0x9C, 0x66, 0x9C, 0x67, 0x9C, 0x68, 0x9C, 0x69, /* 0x04-0x07 */
+ 0xBD, 0xFA, 0x9C, 0x6A, 0x9C, 0x6B, 0x9C, 0x6C, /* 0x08-0x0B */
+ 0x9C, 0x6D, 0x9C, 0x6E, 0x9C, 0x6F, 0x9C, 0x70, /* 0x0C-0x0F */
+ 0xBD, 0xFB, 0x9C, 0x71, 0x9C, 0x72, 0x9C, 0x73, /* 0x10-0x13 */
+ 0x9C, 0x74, 0x9C, 0x75, 0x9C, 0x76, 0x9C, 0x77, /* 0x14-0x17 */
+ 0x9C, 0x78, 0x9C, 0x79, 0x9C, 0x7A, 0x9C, 0x81, /* 0x18-0x1B */
+ 0x9C, 0x82, 0x9C, 0x83, 0x9C, 0x84, 0x9C, 0x85, /* 0x1C-0x1F */
+ 0x9C, 0x86, 0x9C, 0x87, 0x9C, 0x88, 0x9C, 0x89, /* 0x20-0x23 */
+ 0xBD, 0xFC, 0x9C, 0x8A, 0x9C, 0x8B, 0x9C, 0x8C, /* 0x24-0x27 */
+ 0x9C, 0x8D, 0x9C, 0x8E, 0x9C, 0x8F, 0x9C, 0x90, /* 0x28-0x2B */
+ 0xBD, 0xFD, 0x9C, 0x91, 0x9C, 0x92, 0x9C, 0x93, /* 0x2C-0x2F */
+ 0xBD, 0xFE, 0x9C, 0x94, 0x9C, 0x95, 0x9C, 0x96, /* 0x30-0x33 */
+ 0xBE, 0xA1, 0x9C, 0x97, 0x9C, 0x98, 0x9C, 0x99, /* 0x34-0x37 */
+ 0x9C, 0x9A, 0x9C, 0x9B, 0x9C, 0x9C, 0x9C, 0x9D, /* 0x38-0x3B */
+ 0xBE, 0xA2, 0xBE, 0xA3, 0x9C, 0x9E, 0x9C, 0x9F, /* 0x3C-0x3F */
+ 0x9C, 0xA0, 0x9C, 0xA1, 0x9C, 0xA2, 0x9C, 0xA3, /* 0x40-0x43 */
+ 0x9C, 0xA4, 0x9C, 0xA5, 0x9C, 0xA6, 0x9C, 0xA7, /* 0x44-0x47 */
+ 0xBE, 0xA4, 0x9C, 0xA8, 0x9C, 0xA9, 0x9C, 0xAA, /* 0x48-0x4B */
+ 0x9C, 0xAB, 0x9C, 0xAC, 0x9C, 0xAD, 0x9C, 0xAE, /* 0x4C-0x4F */
+ 0x9C, 0xAF, 0x9C, 0xB0, 0x9C, 0xB1, 0x9C, 0xB2, /* 0x50-0x53 */
+ 0x9C, 0xB3, 0x9C, 0xB4, 0x9C, 0xB5, 0x9C, 0xB6, /* 0x54-0x57 */
+ 0x9C, 0xB7, 0x9C, 0xB8, 0x9C, 0xB9, 0x9C, 0xBA, /* 0x58-0x5B */
+ 0x9C, 0xBB, 0x9C, 0xBC, 0x9C, 0xBD, 0x9C, 0xBE, /* 0x5C-0x5F */
+ 0x9C, 0xBF, 0x9C, 0xC0, 0x9C, 0xC1, 0x9C, 0xC2, /* 0x60-0x63 */
+ 0xBE, 0xA5, 0xBE, 0xA6, 0x9C, 0xC3, 0x9C, 0xC4, /* 0x64-0x67 */
+ 0xBE, 0xA7, 0x9C, 0xC5, 0x9C, 0xC6, 0x9C, 0xC7, /* 0x68-0x6B */
+ 0xBE, 0xA8, 0x9C, 0xC8, 0x9C, 0xC9, 0x9C, 0xCA, /* 0x6C-0x6F */
+ 0x9C, 0xCB, 0x9C, 0xCC, 0x9C, 0xCD, 0x9C, 0xCE, /* 0x70-0x73 */
+ 0xBE, 0xA9, 0xBE, 0xAA, 0x9C, 0xCF, 0x9C, 0xD0, /* 0x74-0x77 */
+ 0x9C, 0xD1, 0xBE, 0xAB, 0x9C, 0xD2, 0x9C, 0xD3, /* 0x78-0x7B */
+ 0x9C, 0xD4, 0x9C, 0xD5, 0x9C, 0xD6, 0x9C, 0xD7, /* 0x7C-0x7F */
+
+ 0xBE, 0xAC, 0x9C, 0xD8, 0x9C, 0xD9, 0x9C, 0xDA, /* 0x80-0x83 */
+ 0x9C, 0xDB, 0x9C, 0xDC, 0x9C, 0xDD, 0x9C, 0xDE, /* 0x84-0x87 */
+ 0x9C, 0xDF, 0x9C, 0xE0, 0x9C, 0xE1, 0x9C, 0xE2, /* 0x88-0x8B */
+ 0x9C, 0xE3, 0x9C, 0xE4, 0x9C, 0xE5, 0x9C, 0xE6, /* 0x8C-0x8F */
+ 0x9C, 0xE7, 0x9C, 0xE8, 0x9C, 0xE9, 0x9C, 0xEA, /* 0x90-0x93 */
+ 0xBE, 0xAD, 0x9C, 0xEB, 0x9C, 0xEC, 0x9C, 0xED, /* 0x94-0x97 */
+ 0x9C, 0xEE, 0x9C, 0xEF, 0x9C, 0xF0, 0x9C, 0xF1, /* 0x98-0x9B */
+ 0xBE, 0xAE, 0x9C, 0xF2, 0x9C, 0xF3, 0x9C, 0xF4, /* 0x9C-0x9F */
+ 0x9C, 0xF5, 0x9C, 0xF6, 0x9C, 0xF7, 0x9C, 0xF8, /* 0xA0-0xA3 */
+ 0x9C, 0xF9, 0x9C, 0xFA, 0x9C, 0xFB, 0x9C, 0xFC, /* 0xA4-0xA7 */
+ 0x9C, 0xFD, 0x9C, 0xFE, 0x9D, 0x41, 0x9D, 0x42, /* 0xA8-0xAB */
+ 0x9D, 0x43, 0x9D, 0x44, 0x9D, 0x45, 0x9D, 0x46, /* 0xAC-0xAF */
+ 0x9D, 0x47, 0x9D, 0x48, 0x9D, 0x49, 0x9D, 0x4A, /* 0xB0-0xB3 */
+ 0x9D, 0x4B, 0x9D, 0x4C, 0x9D, 0x4D, 0x9D, 0x4E, /* 0xB4-0xB7 */
+ 0xBE, 0xAF, 0x9D, 0x4F, 0x9D, 0x50, 0x9D, 0x51, /* 0xB8-0xBB */
+ 0xBE, 0xB0, 0x9D, 0x52, 0x9D, 0x53, 0x9D, 0x54, /* 0xBC-0xBF */
+ 0x9D, 0x55, 0x9D, 0x56, 0x9D, 0x57, 0x9D, 0x58, /* 0xC0-0xC3 */
+ 0x9D, 0x59, 0x9D, 0x5A, 0x9D, 0x61, 0x9D, 0x62, /* 0xC4-0xC7 */
+ 0x9D, 0x63, 0x9D, 0x64, 0x9D, 0x65, 0x9D, 0x66, /* 0xC8-0xCB */
+ 0x9D, 0x67, 0x9D, 0x68, 0x9D, 0x69, 0x9D, 0x6A, /* 0xCC-0xCF */
+ 0x9D, 0x6B, 0x9D, 0x6C, 0x9D, 0x6D, 0x9D, 0x6E, /* 0xD0-0xD3 */
+ 0x9D, 0x6F, 0x9D, 0x70, 0x9D, 0x71, 0x9D, 0x72, /* 0xD4-0xD7 */
+ 0x9D, 0x73, 0x9D, 0x74, 0x9D, 0x75, 0x9D, 0x76, /* 0xD8-0xDB */
+ 0x9D, 0x77, 0x9D, 0x78, 0x9D, 0x79, 0x9D, 0x7A, /* 0xDC-0xDF */
+ 0x9D, 0x81, 0x9D, 0x82, 0x9D, 0x83, 0x9D, 0x84, /* 0xE0-0xE3 */
+ 0x9D, 0x85, 0x9D, 0x86, 0x9D, 0x87, 0x9D, 0x88, /* 0xE4-0xE7 */
+ 0x9D, 0x89, 0xBE, 0xB1, 0x9D, 0x8A, 0x9D, 0x8B, /* 0xE8-0xEB */
+ 0x9D, 0x8C, 0x9D, 0x8D, 0x9D, 0x8E, 0x9D, 0x8F, /* 0xEC-0xEF */
+ 0xBE, 0xB2, 0xBE, 0xB3, 0x9D, 0x90, 0x9D, 0x91, /* 0xF0-0xF3 */
+ 0xBE, 0xB4, 0x9D, 0x92, 0x9D, 0x93, 0x9D, 0x94, /* 0xF4-0xF7 */
+ 0xBE, 0xB5, 0x9D, 0x95, 0xBE, 0xB6, 0x9D, 0x96, /* 0xF8-0xFB */
+ 0x9D, 0x97, 0x9D, 0x98, 0x9D, 0x99, 0xBE, 0xB7, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_C5[512] = {
+ 0xBE, 0xB8, 0xBE, 0xB9, 0x9D, 0x9A, 0x9D, 0x9B, /* 0x00-0x03 */
+ 0x9D, 0x9C, 0x9D, 0x9D, 0x9D, 0x9E, 0x9D, 0x9F, /* 0x04-0x07 */
+ 0x9D, 0xA0, 0x9D, 0xA1, 0x9D, 0xA2, 0x9D, 0xA3, /* 0x08-0x0B */
+ 0xBE, 0xBA, 0x9D, 0xA4, 0x9D, 0xA5, 0x9D, 0xA6, /* 0x0C-0x0F */
+ 0xBE, 0xBB, 0x9D, 0xA7, 0x9D, 0xA8, 0x9D, 0xA9, /* 0x10-0x13 */
+ 0xBE, 0xBC, 0x9D, 0xAA, 0x9D, 0xAB, 0x9D, 0xAC, /* 0x14-0x17 */
+ 0x9D, 0xAD, 0x9D, 0xAE, 0x9D, 0xAF, 0x9D, 0xB0, /* 0x18-0x1B */
+ 0xBE, 0xBD, 0x9D, 0xB1, 0x9D, 0xB2, 0x9D, 0xB3, /* 0x1C-0x1F */
+ 0x9D, 0xB4, 0x9D, 0xB5, 0x9D, 0xB6, 0x9D, 0xB7, /* 0x20-0x23 */
+ 0x9D, 0xB8, 0x9D, 0xB9, 0x9D, 0xBA, 0x9D, 0xBB, /* 0x24-0x27 */
+ 0xBE, 0xBE, 0xBE, 0xBF, 0x9D, 0xBC, 0x9D, 0xBD, /* 0x28-0x2B */
+ 0xBE, 0xC0, 0x9D, 0xBE, 0x9D, 0xBF, 0x9D, 0xC0, /* 0x2C-0x2F */
+ 0xBE, 0xC1, 0x9D, 0xC1, 0x9D, 0xC2, 0x9D, 0xC3, /* 0x30-0x33 */
+ 0x9D, 0xC4, 0x9D, 0xC5, 0x9D, 0xC6, 0x9D, 0xC7, /* 0x34-0x37 */
+ 0xBE, 0xC2, 0xBE, 0xC3, 0x9D, 0xC8, 0xBE, 0xC4, /* 0x38-0x3B */
+ 0x9D, 0xC9, 0xBE, 0xC5, 0x9D, 0xCA, 0x9D, 0xCB, /* 0x3C-0x3F */
+ 0x9D, 0xCC, 0x9D, 0xCD, 0x9D, 0xCE, 0x9D, 0xCF, /* 0x40-0x43 */
+ 0xBE, 0xC6, 0xBE, 0xC7, 0x9D, 0xD0, 0x9D, 0xD1, /* 0x44-0x47 */
+ 0xBE, 0xC8, 0xBE, 0xC9, 0xBE, 0xCA, 0x9D, 0xD2, /* 0x48-0x4B */
+ 0xBE, 0xCB, 0xBE, 0xCC, 0xBE, 0xCD, 0x9D, 0xD3, /* 0x4C-0x4F */
+ 0x9D, 0xD4, 0x9D, 0xD5, 0x9D, 0xD6, 0xBE, 0xCE, /* 0x50-0x53 */
+ 0xBE, 0xCF, 0xBE, 0xD0, 0x9D, 0xD7, 0xBE, 0xD1, /* 0x54-0x57 */
+ 0xBE, 0xD2, 0xBE, 0xD3, 0x9D, 0xD8, 0x9D, 0xD9, /* 0x58-0x5B */
+ 0x9D, 0xDA, 0xBE, 0xD4, 0xBE, 0xD5, 0x9D, 0xDB, /* 0x5C-0x5F */
+ 0xBE, 0xD6, 0xBE, 0xD7, 0x9D, 0xDC, 0x9D, 0xDD, /* 0x60-0x63 */
+ 0xBE, 0xD8, 0x9D, 0xDE, 0x9D, 0xDF, 0x9D, 0xE0, /* 0x64-0x67 */
+ 0xBE, 0xD9, 0x9D, 0xE1, 0x9D, 0xE2, 0x9D, 0xE3, /* 0x68-0x6B */
+ 0x9D, 0xE4, 0x9D, 0xE5, 0x9D, 0xE6, 0x9D, 0xE7, /* 0x6C-0x6F */
+ 0xBE, 0xDA, 0xBE, 0xDB, 0x9D, 0xE8, 0xBE, 0xDC, /* 0x70-0x73 */
+ 0xBE, 0xDD, 0xBE, 0xDE, 0x9D, 0xE9, 0x9D, 0xEA, /* 0x74-0x77 */
+ 0x9D, 0xEB, 0x9D, 0xEC, 0x9D, 0xED, 0x9D, 0xEE, /* 0x78-0x7B */
+ 0xBE, 0xDF, 0xBE, 0xE0, 0x9D, 0xEF, 0x9D, 0xF0, /* 0x7C-0x7F */
+
+ 0xBE, 0xE1, 0x9D, 0xF1, 0x9D, 0xF2, 0x9D, 0xF3, /* 0x80-0x83 */
+ 0xBE, 0xE2, 0x9D, 0xF4, 0x9D, 0xF5, 0xBE, 0xE3, /* 0x84-0x87 */
+ 0x9D, 0xF6, 0x9D, 0xF7, 0x9D, 0xF8, 0x9D, 0xF9, /* 0x88-0x8B */
+ 0xBE, 0xE4, 0xBE, 0xE5, 0x9D, 0xFA, 0xBE, 0xE6, /* 0x8C-0x8F */
+ 0x9D, 0xFB, 0xBE, 0xE7, 0x9D, 0xFC, 0x9D, 0xFD, /* 0x90-0x93 */
+ 0x9D, 0xFE, 0xBE, 0xE8, 0x9E, 0x41, 0xBE, 0xE9, /* 0x94-0x97 */
+ 0xBE, 0xEA, 0x9E, 0x42, 0x9E, 0x43, 0x9E, 0x44, /* 0x98-0x9B */
+ 0xBE, 0xEB, 0x9E, 0x45, 0x9E, 0x46, 0x9E, 0x47, /* 0x9C-0x9F */
+ 0xBE, 0xEC, 0x9E, 0x48, 0x9E, 0x49, 0x9E, 0x4A, /* 0xA0-0xA3 */
+ 0x9E, 0x4B, 0x9E, 0x4C, 0x9E, 0x4D, 0x9E, 0x4E, /* 0xA4-0xA7 */
+ 0x9E, 0x4F, 0xBE, 0xED, 0x9E, 0x50, 0x9E, 0x51, /* 0xA8-0xAB */
+ 0x9E, 0x52, 0x9E, 0x53, 0x9E, 0x54, 0x9E, 0x55, /* 0xAC-0xAF */
+ 0x9E, 0x56, 0x9E, 0x57, 0x9E, 0x58, 0x9E, 0x59, /* 0xB0-0xB3 */
+ 0xBE, 0xEE, 0xBE, 0xEF, 0x9E, 0x5A, 0x9E, 0x61, /* 0xB4-0xB7 */
+ 0xBE, 0xF0, 0xBE, 0xF1, 0x9E, 0x62, 0xBE, 0xF2, /* 0xB8-0xBB */
+ 0xBE, 0xF3, 0xBE, 0xF4, 0xBE, 0xF5, 0x9E, 0x63, /* 0xBC-0xBF */
+ 0x9E, 0x64, 0x9E, 0x65, 0x9E, 0x66, 0x9E, 0x67, /* 0xC0-0xC3 */
+ 0xBE, 0xF6, 0xBE, 0xF7, 0xBE, 0xF8, 0xBE, 0xF9, /* 0xC4-0xC7 */
+ 0xBE, 0xFA, 0xBE, 0xFB, 0xBE, 0xFC, 0x9E, 0x68, /* 0xC8-0xCB */
+ 0xBE, 0xFD, 0x9E, 0x69, 0xBE, 0xFE, 0x9E, 0x6A, /* 0xCC-0xCF */
+ 0xBF, 0xA1, 0xBF, 0xA2, 0x9E, 0x6B, 0x9E, 0x6C, /* 0xD0-0xD3 */
+ 0xBF, 0xA3, 0x9E, 0x6D, 0x9E, 0x6E, 0x9E, 0x6F, /* 0xD4-0xD7 */
+ 0xBF, 0xA4, 0x9E, 0x70, 0x9E, 0x71, 0x9E, 0x72, /* 0xD8-0xDB */
+ 0x9E, 0x73, 0x9E, 0x74, 0x9E, 0x75, 0x9E, 0x76, /* 0xDC-0xDF */
+ 0xBF, 0xA5, 0xBF, 0xA6, 0x9E, 0x77, 0xBF, 0xA7, /* 0xE0-0xE3 */
+ 0x9E, 0x78, 0xBF, 0xA8, 0x9E, 0x79, 0x9E, 0x7A, /* 0xE4-0xE7 */
+ 0x9E, 0x81, 0x9E, 0x82, 0x9E, 0x83, 0x9E, 0x84, /* 0xE8-0xEB */
+ 0xBF, 0xA9, 0xBF, 0xAA, 0xBF, 0xAB, 0x9E, 0x85, /* 0xEC-0xEF */
+ 0xBF, 0xAC, 0x9E, 0x86, 0x9E, 0x87, 0x9E, 0x88, /* 0xF0-0xF3 */
+ 0xBF, 0xAD, 0x9E, 0x89, 0xBF, 0xAE, 0xBF, 0xAF, /* 0xF4-0xF7 */
+ 0x9E, 0x8A, 0x9E, 0x8B, 0x9E, 0x8C, 0x9E, 0x8D, /* 0xF8-0xFB */
+ 0xBF, 0xB0, 0xBF, 0xB1, 0xBF, 0xB2, 0xBF, 0xB3, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_C6[512] = {
+ 0xBF, 0xB4, 0xBF, 0xB5, 0x9E, 0x8E, 0x9E, 0x8F, /* 0x00-0x03 */
+ 0x9E, 0x90, 0xBF, 0xB6, 0xBF, 0xB7, 0xBF, 0xB8, /* 0x04-0x07 */
+ 0xBF, 0xB9, 0x9E, 0x91, 0x9E, 0x92, 0x9E, 0x93, /* 0x08-0x0B */
+ 0xBF, 0xBA, 0x9E, 0x94, 0x9E, 0x95, 0x9E, 0x96, /* 0x0C-0x0F */
+ 0xBF, 0xBB, 0x9E, 0x97, 0x9E, 0x98, 0x9E, 0x99, /* 0x10-0x13 */
+ 0x9E, 0x9A, 0x9E, 0x9B, 0x9E, 0x9C, 0x9E, 0x9D, /* 0x14-0x17 */
+ 0xBF, 0xBC, 0xBF, 0xBD, 0x9E, 0x9E, 0xBF, 0xBE, /* 0x18-0x1B */
+ 0xBF, 0xBF, 0x9E, 0x9F, 0x9E, 0xA0, 0x9E, 0xA1, /* 0x1C-0x1F */
+ 0x9E, 0xA2, 0x9E, 0xA3, 0x9E, 0xA4, 0x9E, 0xA5, /* 0x20-0x23 */
+ 0xBF, 0xC0, 0xBF, 0xC1, 0x9E, 0xA6, 0x9E, 0xA7, /* 0x24-0x27 */
+ 0xBF, 0xC2, 0x9E, 0xA8, 0x9E, 0xA9, 0x9E, 0xAA, /* 0x28-0x2B */
+ 0xBF, 0xC3, 0xBF, 0xC4, 0xBF, 0xC5, 0x9E, 0xAB, /* 0x2C-0x2F */
+ 0xBF, 0xC6, 0x9E, 0xAC, 0x9E, 0xAD, 0xBF, 0xC7, /* 0x30-0x33 */
+ 0xBF, 0xC8, 0xBF, 0xC9, 0x9E, 0xAE, 0xBF, 0xCA, /* 0x34-0x37 */
+ 0x9E, 0xAF, 0xBF, 0xCB, 0x9E, 0xB0, 0xBF, 0xCC, /* 0x38-0x3B */
+ 0x9E, 0xB1, 0x9E, 0xB2, 0x9E, 0xB3, 0x9E, 0xB4, /* 0x3C-0x3F */
+ 0xBF, 0xCD, 0xBF, 0xCE, 0x9E, 0xB5, 0x9E, 0xB6, /* 0x40-0x43 */
+ 0xBF, 0xCF, 0x9E, 0xB7, 0x9E, 0xB8, 0x9E, 0xB9, /* 0x44-0x47 */
+ 0xBF, 0xD0, 0x9E, 0xBA, 0x9E, 0xBB, 0x9E, 0xBC, /* 0x48-0x4B */
+ 0x9E, 0xBD, 0x9E, 0xBE, 0x9E, 0xBF, 0x9E, 0xC0, /* 0x4C-0x4F */
+ 0xBF, 0xD1, 0xBF, 0xD2, 0x9E, 0xC1, 0xBF, 0xD3, /* 0x50-0x53 */
+ 0xBF, 0xD4, 0xBF, 0xD5, 0x9E, 0xC2, 0x9E, 0xC3, /* 0x54-0x57 */
+ 0x9E, 0xC4, 0x9E, 0xC5, 0x9E, 0xC6, 0x9E, 0xC7, /* 0x58-0x5B */
+ 0xBF, 0xD6, 0xBF, 0xD7, 0x9E, 0xC8, 0x9E, 0xC9, /* 0x5C-0x5F */
+ 0xBF, 0xD8, 0x9E, 0xCA, 0x9E, 0xCB, 0x9E, 0xCC, /* 0x60-0x63 */
+ 0x9E, 0xCD, 0x9E, 0xCE, 0x9E, 0xCF, 0x9E, 0xD0, /* 0x64-0x67 */
+ 0x9E, 0xD1, 0x9E, 0xD2, 0x9E, 0xD3, 0x9E, 0xD4, /* 0x68-0x6B */
+ 0xBF, 0xD9, 0x9E, 0xD5, 0x9E, 0xD6, 0xBF, 0xDA, /* 0x6C-0x6F */
+ 0x9E, 0xD7, 0xBF, 0xDB, 0x9E, 0xD8, 0x9E, 0xD9, /* 0x70-0x73 */
+ 0x9E, 0xDA, 0x9E, 0xDB, 0x9E, 0xDC, 0x9E, 0xDD, /* 0x74-0x77 */
+ 0xBF, 0xDC, 0xBF, 0xDD, 0x9E, 0xDE, 0x9E, 0xDF, /* 0x78-0x7B */
+ 0xBF, 0xDE, 0x9E, 0xE0, 0x9E, 0xE1, 0x9E, 0xE2, /* 0x7C-0x7F */
+
+ 0xBF, 0xDF, 0x9E, 0xE3, 0x9E, 0xE4, 0x9E, 0xE5, /* 0x80-0x83 */
+ 0x9E, 0xE6, 0x9E, 0xE7, 0x9E, 0xE8, 0x9E, 0xE9, /* 0x84-0x87 */
+ 0xBF, 0xE0, 0xBF, 0xE1, 0x9E, 0xEA, 0xBF, 0xE2, /* 0x88-0x8B */
+ 0x9E, 0xEB, 0xBF, 0xE3, 0x9E, 0xEC, 0x9E, 0xED, /* 0x8C-0x8F */
+ 0x9E, 0xEE, 0x9E, 0xEF, 0x9E, 0xF0, 0x9E, 0xF1, /* 0x90-0x93 */
+ 0xBF, 0xE4, 0xBF, 0xE5, 0x9E, 0xF2, 0x9E, 0xF3, /* 0x94-0x97 */
+ 0xBF, 0xE6, 0x9E, 0xF4, 0x9E, 0xF5, 0x9E, 0xF6, /* 0x98-0x9B */
+ 0xBF, 0xE7, 0x9E, 0xF7, 0x9E, 0xF8, 0x9E, 0xF9, /* 0x9C-0x9F */
+ 0x9E, 0xFA, 0x9E, 0xFB, 0x9E, 0xFC, 0x9E, 0xFD, /* 0xA0-0xA3 */
+ 0xBF, 0xE8, 0xBF, 0xE9, 0x9E, 0xFE, 0xBF, 0xEA, /* 0xA4-0xA7 */
+ 0x9F, 0x41, 0xBF, 0xEB, 0x9F, 0x42, 0x9F, 0x43, /* 0xA8-0xAB */
+ 0x9F, 0x44, 0x9F, 0x45, 0x9F, 0x46, 0x9F, 0x47, /* 0xAC-0xAF */
+ 0xBF, 0xEC, 0xBF, 0xED, 0x9F, 0x48, 0x9F, 0x49, /* 0xB0-0xB3 */
+ 0xBF, 0xEE, 0x9F, 0x4A, 0x9F, 0x4B, 0x9F, 0x4C, /* 0xB4-0xB7 */
+ 0xBF, 0xEF, 0xBF, 0xF0, 0xBF, 0xF1, 0x9F, 0x4D, /* 0xB8-0xBB */
+ 0x9F, 0x4E, 0x9F, 0x4F, 0x9F, 0x50, 0x9F, 0x51, /* 0xBC-0xBF */
+ 0xBF, 0xF2, 0xBF, 0xF3, 0x9F, 0x52, 0xBF, 0xF4, /* 0xC0-0xC3 */
+ 0x9F, 0x53, 0xBF, 0xF5, 0x9F, 0x54, 0x9F, 0x55, /* 0xC4-0xC7 */
+ 0x9F, 0x56, 0x9F, 0x57, 0x9F, 0x58, 0x9F, 0x59, /* 0xC8-0xCB */
+ 0xBF, 0xF6, 0xBF, 0xF7, 0x9F, 0x5A, 0x9F, 0x61, /* 0xCC-0xCF */
+ 0xBF, 0xF8, 0x9F, 0x62, 0x9F, 0x63, 0x9F, 0x64, /* 0xD0-0xD3 */
+ 0xBF, 0xF9, 0x9F, 0x65, 0x9F, 0x66, 0x9F, 0x67, /* 0xD4-0xD7 */
+ 0x9F, 0x68, 0x9F, 0x69, 0x9F, 0x6A, 0x9F, 0x6B, /* 0xD8-0xDB */
+ 0xBF, 0xFA, 0xBF, 0xFB, 0x9F, 0x6C, 0x9F, 0x6D, /* 0xDC-0xDF */
+ 0xBF, 0xFC, 0xBF, 0xFD, 0x9F, 0x6E, 0x9F, 0x6F, /* 0xE0-0xE3 */
+ 0x9F, 0x70, 0x9F, 0x71, 0x9F, 0x72, 0x9F, 0x73, /* 0xE4-0xE7 */
+ 0xBF, 0xFE, 0xC0, 0xA1, 0x9F, 0x74, 0x9F, 0x75, /* 0xE8-0xEB */
+ 0xC0, 0xA2, 0x9F, 0x76, 0x9F, 0x77, 0x9F, 0x78, /* 0xEC-0xEF */
+ 0xC0, 0xA3, 0x9F, 0x79, 0x9F, 0x7A, 0x9F, 0x81, /* 0xF0-0xF3 */
+ 0x9F, 0x82, 0x9F, 0x83, 0x9F, 0x84, 0x9F, 0x85, /* 0xF4-0xF7 */
+ 0xC0, 0xA4, 0xC0, 0xA5, 0x9F, 0x86, 0x9F, 0x87, /* 0xF8-0xFB */
+ 0x9F, 0x88, 0xC0, 0xA6, 0x9F, 0x89, 0x9F, 0x8A, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_C7[512] = {
+ 0x9F, 0x8B, 0x9F, 0x8C, 0x9F, 0x8D, 0x9F, 0x8E, /* 0x00-0x03 */
+ 0xC0, 0xA7, 0xC0, 0xA8, 0x9F, 0x8F, 0x9F, 0x90, /* 0x04-0x07 */
+ 0xC0, 0xA9, 0x9F, 0x91, 0x9F, 0x92, 0x9F, 0x93, /* 0x08-0x0B */
+ 0xC0, 0xAA, 0x9F, 0x94, 0x9F, 0x95, 0x9F, 0x96, /* 0x0C-0x0F */
+ 0x9F, 0x97, 0x9F, 0x98, 0x9F, 0x99, 0x9F, 0x9A, /* 0x10-0x13 */
+ 0xC0, 0xAB, 0xC0, 0xAC, 0x9F, 0x9B, 0xC0, 0xAD, /* 0x14-0x17 */
+ 0x9F, 0x9C, 0xC0, 0xAE, 0x9F, 0x9D, 0x9F, 0x9E, /* 0x18-0x1B */
+ 0x9F, 0x9F, 0x9F, 0xA0, 0x9F, 0xA1, 0x9F, 0xA2, /* 0x1C-0x1F */
+ 0xC0, 0xAF, 0xC0, 0xB0, 0x9F, 0xA3, 0x9F, 0xA4, /* 0x20-0x23 */
+ 0xC0, 0xB1, 0x9F, 0xA5, 0x9F, 0xA6, 0x9F, 0xA7, /* 0x24-0x27 */
+ 0xC0, 0xB2, 0x9F, 0xA8, 0x9F, 0xA9, 0x9F, 0xAA, /* 0x28-0x2B */
+ 0x9F, 0xAB, 0x9F, 0xAC, 0x9F, 0xAD, 0x9F, 0xAE, /* 0x2C-0x2F */
+ 0xC0, 0xB3, 0xC0, 0xB4, 0x9F, 0xAF, 0xC0, 0xB5, /* 0x30-0x33 */
+ 0x9F, 0xB0, 0xC0, 0xB6, 0x9F, 0xB1, 0xC0, 0xB7, /* 0x34-0x37 */
+ 0x9F, 0xB2, 0x9F, 0xB3, 0x9F, 0xB4, 0x9F, 0xB5, /* 0x38-0x3B */
+ 0xC0, 0xB8, 0xC0, 0xB9, 0x9F, 0xB6, 0x9F, 0xB7, /* 0x3C-0x3F */
+ 0xC0, 0xBA, 0x9F, 0xB8, 0x9F, 0xB9, 0x9F, 0xBA, /* 0x40-0x43 */
+ 0xC0, 0xBB, 0x9F, 0xBB, 0x9F, 0xBC, 0x9F, 0xBD, /* 0x44-0x47 */
+ 0x9F, 0xBE, 0x9F, 0xBF, 0xC0, 0xBC, 0x9F, 0xC0, /* 0x48-0x4B */
+ 0xC0, 0xBD, 0xC0, 0xBE, 0x9F, 0xC1, 0xC0, 0xBF, /* 0x4C-0x4F */
+ 0x9F, 0xC2, 0xC0, 0xC0, 0xC0, 0xC1, 0xC0, 0xC2, /* 0x50-0x53 */
+ 0xC0, 0xC3, 0xC0, 0xC4, 0xC0, 0xC5, 0xC0, 0xC6, /* 0x54-0x57 */
+ 0xC0, 0xC7, 0x9F, 0xC3, 0x9F, 0xC4, 0x9F, 0xC5, /* 0x58-0x5B */
+ 0xC0, 0xC8, 0x9F, 0xC6, 0x9F, 0xC7, 0x9F, 0xC8, /* 0x5C-0x5F */
+ 0xC0, 0xC9, 0x9F, 0xC9, 0x9F, 0xCA, 0x9F, 0xCB, /* 0x60-0x63 */
+ 0x9F, 0xCC, 0x9F, 0xCD, 0x9F, 0xCE, 0x9F, 0xCF, /* 0x64-0x67 */
+ 0xC0, 0xCA, 0x9F, 0xD0, 0x9F, 0xD1, 0xC0, 0xCB, /* 0x68-0x6B */
+ 0x9F, 0xD2, 0x9F, 0xD3, 0x9F, 0xD4, 0x9F, 0xD5, /* 0x6C-0x6F */
+ 0x9F, 0xD6, 0x9F, 0xD7, 0x9F, 0xD8, 0x9F, 0xD9, /* 0x70-0x73 */
+ 0xC0, 0xCC, 0xC0, 0xCD, 0x9F, 0xDA, 0x9F, 0xDB, /* 0x74-0x77 */
+ 0xC0, 0xCE, 0x9F, 0xDC, 0x9F, 0xDD, 0x9F, 0xDE, /* 0x78-0x7B */
+ 0xC0, 0xCF, 0xC0, 0xD0, 0xC0, 0xD1, 0x9F, 0xDF, /* 0x7C-0x7F */
+
+ 0x9F, 0xE0, 0x9F, 0xE1, 0x9F, 0xE2, 0xC0, 0xD2, /* 0x80-0x83 */
+ 0xC0, 0xD3, 0xC0, 0xD4, 0x9F, 0xE3, 0xC0, 0xD5, /* 0x84-0x87 */
+ 0xC0, 0xD6, 0xC0, 0xD7, 0xC0, 0xD8, 0x9F, 0xE4, /* 0x88-0x8B */
+ 0x9F, 0xE5, 0x9F, 0xE6, 0xC0, 0xD9, 0x9F, 0xE7, /* 0x8C-0x8F */
+ 0xC0, 0xDA, 0xC0, 0xDB, 0x9F, 0xE8, 0x9F, 0xE9, /* 0x90-0x93 */
+ 0xC0, 0xDC, 0x9F, 0xEA, 0xC0, 0xDD, 0xC0, 0xDE, /* 0x94-0x97 */
+ 0xC0, 0xDF, 0x9F, 0xEB, 0xC0, 0xE0, 0x9F, 0xEC, /* 0x98-0x9B */
+ 0x9F, 0xED, 0x9F, 0xEE, 0x9F, 0xEF, 0x9F, 0xF0, /* 0x9C-0x9F */
+ 0xC0, 0xE1, 0xC0, 0xE2, 0x9F, 0xF1, 0xC0, 0xE3, /* 0xA0-0xA3 */
+ 0xC0, 0xE4, 0xC0, 0xE5, 0xC0, 0xE6, 0x9F, 0xF2, /* 0xA4-0xA7 */
+ 0x9F, 0xF3, 0x9F, 0xF4, 0x9F, 0xF5, 0x9F, 0xF6, /* 0xA8-0xAB */
+ 0xC0, 0xE7, 0xC0, 0xE8, 0x9F, 0xF7, 0x9F, 0xF8, /* 0xAC-0xAF */
+ 0xC0, 0xE9, 0x9F, 0xF9, 0x9F, 0xFA, 0x9F, 0xFB, /* 0xB0-0xB3 */
+ 0xC0, 0xEA, 0x9F, 0xFC, 0x9F, 0xFD, 0x9F, 0xFE, /* 0xB4-0xB7 */
+ 0xA0, 0x41, 0xA0, 0x42, 0xA0, 0x43, 0xA0, 0x44, /* 0xB8-0xBB */
+ 0xC0, 0xEB, 0xC0, 0xEC, 0xA0, 0x45, 0xC0, 0xED, /* 0xBC-0xBF */
+ 0xC0, 0xEE, 0xC0, 0xEF, 0xA0, 0x46, 0xA0, 0x47, /* 0xC0-0xC3 */
+ 0xA0, 0x48, 0xA0, 0x49, 0xA0, 0x4A, 0xA0, 0x4B, /* 0xC4-0xC7 */
+ 0xC0, 0xF0, 0xC0, 0xF1, 0xA0, 0x4C, 0xA0, 0x4D, /* 0xC8-0xCB */
+ 0xC0, 0xF2, 0xA0, 0x4E, 0xC0, 0xF3, 0xA0, 0x4F, /* 0xCC-0xCF */
+ 0xC0, 0xF4, 0xA0, 0x50, 0xA0, 0x51, 0xA0, 0x52, /* 0xD0-0xD3 */
+ 0xA0, 0x53, 0xA0, 0x54, 0xA0, 0x55, 0xA0, 0x56, /* 0xD4-0xD7 */
+ 0xC0, 0xF5, 0xA0, 0x57, 0xA0, 0x58, 0xA0, 0x59, /* 0xD8-0xDB */
+ 0xA0, 0x5A, 0xC0, 0xF6, 0xA0, 0x61, 0xA0, 0x62, /* 0xDC-0xDF */
+ 0xA0, 0x63, 0xA0, 0x64, 0xA0, 0x65, 0xA0, 0x66, /* 0xE0-0xE3 */
+ 0xC0, 0xF7, 0xA0, 0x67, 0xA0, 0x68, 0xA0, 0x69, /* 0xE4-0xE7 */
+ 0xC0, 0xF8, 0xA0, 0x6A, 0xA0, 0x6B, 0xA0, 0x6C, /* 0xE8-0xEB */
+ 0xC0, 0xF9, 0xA0, 0x6D, 0xA0, 0x6E, 0xA0, 0x6F, /* 0xEC-0xEF */
+ 0xA0, 0x70, 0xA0, 0x71, 0xA0, 0x72, 0xA0, 0x73, /* 0xF0-0xF3 */
+ 0xA0, 0x74, 0xA0, 0x75, 0xA0, 0x76, 0xA0, 0x77, /* 0xF4-0xF7 */
+ 0xA0, 0x78, 0xA0, 0x79, 0xA0, 0x7A, 0xA0, 0x81, /* 0xF8-0xFB */
+ 0xA0, 0x82, 0xA0, 0x83, 0xA0, 0x84, 0xA0, 0x85, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_C8[512] = {
+ 0xC0, 0xFA, 0xC0, 0xFB, 0xA0, 0x86, 0xA0, 0x87, /* 0x00-0x03 */
+ 0xC0, 0xFC, 0xA0, 0x88, 0xA0, 0x89, 0xA0, 0x8A, /* 0x04-0x07 */
+ 0xC0, 0xFD, 0xA0, 0x8B, 0xC0, 0xFE, 0xA0, 0x8C, /* 0x08-0x0B */
+ 0xA0, 0x8D, 0xA0, 0x8E, 0xA0, 0x8F, 0xA0, 0x90, /* 0x0C-0x0F */
+ 0xC1, 0xA1, 0xC1, 0xA2, 0xA0, 0x91, 0xC1, 0xA3, /* 0x10-0x13 */
+ 0xA0, 0x92, 0xC1, 0xA4, 0xC1, 0xA5, 0xA0, 0x93, /* 0x14-0x17 */
+ 0xA0, 0x94, 0xA0, 0x95, 0xA0, 0x96, 0xA0, 0x97, /* 0x18-0x1B */
+ 0xC1, 0xA6, 0xC1, 0xA7, 0xA0, 0x98, 0xA0, 0x99, /* 0x1C-0x1F */
+ 0xC1, 0xA8, 0xA0, 0x9A, 0xA0, 0x9B, 0xA0, 0x9C, /* 0x20-0x23 */
+ 0xC1, 0xA9, 0xA0, 0x9D, 0xA0, 0x9E, 0xA0, 0x9F, /* 0x24-0x27 */
+ 0xA0, 0xA0, 0xA0, 0xA1, 0xA0, 0xA2, 0xA0, 0xA3, /* 0x28-0x2B */
+ 0xC1, 0xAA, 0xC1, 0xAB, 0xA0, 0xA4, 0xC1, 0xAC, /* 0x2C-0x2F */
+ 0xA0, 0xA5, 0xC1, 0xAD, 0xA0, 0xA6, 0xA0, 0xA7, /* 0x30-0x33 */
+ 0xA0, 0xA8, 0xA0, 0xA9, 0xA0, 0xAA, 0xA0, 0xAB, /* 0x34-0x37 */
+ 0xC1, 0xAE, 0xA0, 0xAC, 0xA0, 0xAD, 0xA0, 0xAE, /* 0x38-0x3B */
+ 0xC1, 0xAF, 0xA0, 0xAF, 0xA0, 0xB0, 0xA0, 0xB1, /* 0x3C-0x3F */
+ 0xC1, 0xB0, 0xA0, 0xB2, 0xA0, 0xB3, 0xA0, 0xB4, /* 0x40-0x43 */
+ 0xA0, 0xB5, 0xA0, 0xB6, 0xA0, 0xB7, 0xA0, 0xB8, /* 0x44-0x47 */
+ 0xC1, 0xB1, 0xC1, 0xB2, 0xA0, 0xB9, 0xA0, 0xBA, /* 0x48-0x4B */
+ 0xC1, 0xB3, 0xC1, 0xB4, 0xA0, 0xBB, 0xA0, 0xBC, /* 0x4C-0x4F */
+ 0xA0, 0xBD, 0xA0, 0xBE, 0xA0, 0xBF, 0xA0, 0xC0, /* 0x50-0x53 */
+ 0xC1, 0xB5, 0xA0, 0xC1, 0xA0, 0xC2, 0xA0, 0xC3, /* 0x54-0x57 */
+ 0xA0, 0xC4, 0xA0, 0xC5, 0xA0, 0xC6, 0xA0, 0xC7, /* 0x58-0x5B */
+ 0xA0, 0xC8, 0xA0, 0xC9, 0xA0, 0xCA, 0xA0, 0xCB, /* 0x5C-0x5F */
+ 0xA0, 0xCC, 0xA0, 0xCD, 0xA0, 0xCE, 0xA0, 0xCF, /* 0x60-0x63 */
+ 0xA0, 0xD0, 0xA0, 0xD1, 0xA0, 0xD2, 0xA0, 0xD3, /* 0x64-0x67 */
+ 0xA0, 0xD4, 0xA0, 0xD5, 0xA0, 0xD6, 0xA0, 0xD7, /* 0x68-0x6B */
+ 0xA0, 0xD8, 0xA0, 0xD9, 0xA0, 0xDA, 0xA0, 0xDB, /* 0x6C-0x6F */
+ 0xC1, 0xB6, 0xC1, 0xB7, 0xA0, 0xDC, 0xA0, 0xDD, /* 0x70-0x73 */
+ 0xC1, 0xB8, 0xA0, 0xDE, 0xA0, 0xDF, 0xA0, 0xE0, /* 0x74-0x77 */
+ 0xC1, 0xB9, 0xA0, 0xE1, 0xC1, 0xBA, 0xA0, 0xE2, /* 0x78-0x7B */
+ 0xA0, 0xE3, 0xA0, 0xE4, 0xA0, 0xE5, 0xA0, 0xE6, /* 0x7C-0x7F */
+
+ 0xC1, 0xBB, 0xC1, 0xBC, 0xA0, 0xE7, 0xC1, 0xBD, /* 0x80-0x83 */
+ 0xA0, 0xE8, 0xC1, 0xBE, 0xC1, 0xBF, 0xC1, 0xC0, /* 0x84-0x87 */
+ 0xA0, 0xE9, 0xA0, 0xEA, 0xA0, 0xEB, 0xC1, 0xC1, /* 0x88-0x8B */
+ 0xC1, 0xC2, 0xC1, 0xC3, 0xA0, 0xEC, 0xA0, 0xED, /* 0x8C-0x8F */
+ 0xA0, 0xEE, 0xA0, 0xEF, 0xA0, 0xF0, 0xA0, 0xF1, /* 0x90-0x93 */
+ 0xC1, 0xC4, 0xA0, 0xF2, 0xA0, 0xF3, 0xA0, 0xF4, /* 0x94-0x97 */
+ 0xA0, 0xF5, 0xA0, 0xF6, 0xA0, 0xF7, 0xA0, 0xF8, /* 0x98-0x9B */
+ 0xA0, 0xF9, 0xC1, 0xC5, 0xA0, 0xFA, 0xC1, 0xC6, /* 0x9C-0x9F */
+ 0xA0, 0xFB, 0xC1, 0xC7, 0xA0, 0xFC, 0xA0, 0xFD, /* 0xA0-0xA3 */
+ 0xA0, 0xFE, 0xA1, 0x41, 0xA1, 0x42, 0xA1, 0x43, /* 0xA4-0xA7 */
+ 0xC1, 0xC8, 0xA1, 0x44, 0xA1, 0x45, 0xA1, 0x46, /* 0xA8-0xAB */
+ 0xA1, 0x47, 0xA1, 0x48, 0xA1, 0x49, 0xA1, 0x4A, /* 0xAC-0xAF */
+ 0xA1, 0x4B, 0xA1, 0x4C, 0xA1, 0x4D, 0xA1, 0x4E, /* 0xB0-0xB3 */
+ 0xA1, 0x4F, 0xA1, 0x50, 0xA1, 0x51, 0xA1, 0x52, /* 0xB4-0xB7 */
+ 0xA1, 0x53, 0xA1, 0x54, 0xA1, 0x55, 0xA1, 0x56, /* 0xB8-0xBB */
+ 0xC1, 0xC9, 0xC1, 0xCA, 0xA1, 0x57, 0xA1, 0x58, /* 0xBC-0xBF */
+ 0xA1, 0x59, 0xA1, 0x5A, 0xA1, 0x61, 0xA1, 0x62, /* 0xC0-0xC3 */
+ 0xC1, 0xCB, 0xA1, 0x63, 0xA1, 0x64, 0xA1, 0x65, /* 0xC4-0xC7 */
+ 0xC1, 0xCC, 0xA1, 0x66, 0xA1, 0x67, 0xA1, 0x68, /* 0xC8-0xCB */
+ 0xC1, 0xCD, 0xA1, 0x69, 0xA1, 0x6A, 0xA1, 0x6B, /* 0xCC-0xCF */
+ 0xA1, 0x6C, 0xA1, 0x6D, 0xA1, 0x6E, 0xA1, 0x6F, /* 0xD0-0xD3 */
+ 0xC1, 0xCE, 0xC1, 0xCF, 0xA1, 0x70, 0xC1, 0xD0, /* 0xD4-0xD7 */
+ 0xA1, 0x71, 0xC1, 0xD1, 0xA1, 0x72, 0xA1, 0x73, /* 0xD8-0xDB */
+ 0xA1, 0x74, 0xA1, 0x75, 0xA1, 0x76, 0xA1, 0x77, /* 0xDC-0xDF */
+ 0xC1, 0xD2, 0xC1, 0xD3, 0xA1, 0x78, 0xA1, 0x79, /* 0xE0-0xE3 */
+ 0xC1, 0xD4, 0xA1, 0x7A, 0xA1, 0x81, 0xA1, 0x82, /* 0xE4-0xE7 */
+ 0xA1, 0x83, 0xA1, 0x84, 0xA1, 0x85, 0xA1, 0x86, /* 0xE8-0xEB */
+ 0xA1, 0x87, 0xA1, 0x88, 0xA1, 0x89, 0xA1, 0x8A, /* 0xEC-0xEF */
+ 0xA1, 0x8B, 0xA1, 0x8C, 0xA1, 0x8D, 0xA1, 0x8E, /* 0xF0-0xF3 */
+ 0xA1, 0x8F, 0xC1, 0xD5, 0xA1, 0x90, 0xA1, 0x91, /* 0xF4-0xF7 */
+ 0xA1, 0x92, 0xA1, 0x93, 0xA1, 0x94, 0xA1, 0x95, /* 0xF8-0xFB */
+ 0xC1, 0xD6, 0xC1, 0xD7, 0xA1, 0x96, 0xA1, 0x97, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_C9[512] = {
+ 0xC1, 0xD8, 0xA1, 0x98, 0xA1, 0x99, 0xA1, 0x9A, /* 0x00-0x03 */
+ 0xC1, 0xD9, 0xC1, 0xDA, 0xC1, 0xDB, 0xA1, 0x9B, /* 0x04-0x07 */
+ 0xA1, 0x9C, 0xA1, 0x9D, 0xA1, 0x9E, 0xA1, 0x9F, /* 0x08-0x0B */
+ 0xC1, 0xDC, 0xC1, 0xDD, 0xA1, 0xA0, 0xC1, 0xDE, /* 0x0C-0x0F */
+ 0xA2, 0x41, 0xC1, 0xDF, 0xA2, 0x42, 0xA2, 0x43, /* 0x10-0x13 */
+ 0xA2, 0x44, 0xA2, 0x45, 0xA2, 0x46, 0xA2, 0x47, /* 0x14-0x17 */
+ 0xC1, 0xE0, 0xA2, 0x48, 0xA2, 0x49, 0xA2, 0x4A, /* 0x18-0x1B */
+ 0xA2, 0x4B, 0xA2, 0x4C, 0xA2, 0x4D, 0xA2, 0x4E, /* 0x1C-0x1F */
+ 0xA2, 0x4F, 0xA2, 0x50, 0xA2, 0x51, 0xA2, 0x52, /* 0x20-0x23 */
+ 0xA2, 0x53, 0xA2, 0x54, 0xA2, 0x55, 0xA2, 0x56, /* 0x24-0x27 */
+ 0xA2, 0x57, 0xA2, 0x58, 0xA2, 0x59, 0xA2, 0x5A, /* 0x28-0x2B */
+ 0xC1, 0xE1, 0xA2, 0x61, 0xA2, 0x62, 0xA2, 0x63, /* 0x2C-0x2F */
+ 0xA2, 0x64, 0xA2, 0x65, 0xA2, 0x66, 0xA2, 0x67, /* 0x30-0x33 */
+ 0xC1, 0xE2, 0xA2, 0x68, 0xA2, 0x69, 0xA2, 0x6A, /* 0x34-0x37 */
+ 0xA2, 0x6B, 0xA2, 0x6C, 0xA2, 0x6D, 0xA2, 0x6E, /* 0x38-0x3B */
+ 0xA2, 0x6F, 0xA2, 0x70, 0xA2, 0x71, 0xA2, 0x72, /* 0x3C-0x3F */
+ 0xA2, 0x73, 0xA2, 0x74, 0xA2, 0x75, 0xA2, 0x76, /* 0x40-0x43 */
+ 0xA2, 0x77, 0xA2, 0x78, 0xA2, 0x79, 0xA2, 0x7A, /* 0x44-0x47 */
+ 0xA2, 0x81, 0xA2, 0x82, 0xA2, 0x83, 0xA2, 0x84, /* 0x48-0x4B */
+ 0xA2, 0x85, 0xA2, 0x86, 0xA2, 0x87, 0xA2, 0x88, /* 0x4C-0x4F */
+ 0xC1, 0xE3, 0xC1, 0xE4, 0xA2, 0x89, 0xA2, 0x8A, /* 0x50-0x53 */
+ 0xC1, 0xE5, 0xA2, 0x8B, 0xA2, 0x8C, 0xA2, 0x8D, /* 0x54-0x57 */
+ 0xC1, 0xE6, 0xA2, 0x8E, 0xA2, 0x8F, 0xA2, 0x90, /* 0x58-0x5B */
+ 0xA2, 0x91, 0xA2, 0x92, 0xA2, 0x93, 0xA2, 0x94, /* 0x5C-0x5F */
+ 0xC1, 0xE7, 0xC1, 0xE8, 0xA2, 0x95, 0xC1, 0xE9, /* 0x60-0x63 */
+ 0xA2, 0x96, 0xA2, 0x97, 0xA2, 0x98, 0xA2, 0x99, /* 0x64-0x67 */
+ 0xA2, 0x9A, 0xA2, 0x9B, 0xA2, 0x9C, 0xA2, 0x9D, /* 0x68-0x6B */
+ 0xC1, 0xEA, 0xA2, 0x9E, 0xA2, 0x9F, 0xA2, 0xA0, /* 0x6C-0x6F */
+ 0xC1, 0xEB, 0xA3, 0x41, 0xA3, 0x42, 0xA3, 0x43, /* 0x70-0x73 */
+ 0xC1, 0xEC, 0xA3, 0x44, 0xA3, 0x45, 0xA3, 0x46, /* 0x74-0x77 */
+ 0xA3, 0x47, 0xA3, 0x48, 0xA3, 0x49, 0xA3, 0x4A, /* 0x78-0x7B */
+ 0xC1, 0xED, 0xA3, 0x4B, 0xA3, 0x4C, 0xA3, 0x4D, /* 0x7C-0x7F */
+
+ 0xA3, 0x4E, 0xA3, 0x4F, 0xA3, 0x50, 0xA3, 0x51, /* 0x80-0x83 */
+ 0xA3, 0x52, 0xA3, 0x53, 0xA3, 0x54, 0xA3, 0x55, /* 0x84-0x87 */
+ 0xC1, 0xEE, 0xC1, 0xEF, 0xA3, 0x56, 0xA3, 0x57, /* 0x88-0x8B */
+ 0xC1, 0xF0, 0xA3, 0x58, 0xA3, 0x59, 0xA3, 0x5A, /* 0x8C-0x8F */
+ 0xC1, 0xF1, 0xA3, 0x61, 0xA3, 0x62, 0xA3, 0x63, /* 0x90-0x93 */
+ 0xA3, 0x64, 0xA3, 0x65, 0xA3, 0x66, 0xA3, 0x67, /* 0x94-0x97 */
+ 0xC1, 0xF2, 0xC1, 0xF3, 0xA3, 0x68, 0xC1, 0xF4, /* 0x98-0x9B */
+ 0xA3, 0x69, 0xC1, 0xF5, 0xA3, 0x6A, 0xA3, 0x6B, /* 0x9C-0x9F */
+ 0xA3, 0x6C, 0xA3, 0x6D, 0xA3, 0x6E, 0xA3, 0x6F, /* 0xA0-0xA3 */
+ 0xA3, 0x70, 0xA3, 0x71, 0xA3, 0x72, 0xA3, 0x73, /* 0xA4-0xA7 */
+ 0xA3, 0x74, 0xA3, 0x75, 0xA3, 0x76, 0xA3, 0x77, /* 0xA8-0xAB */
+ 0xA3, 0x78, 0xA3, 0x79, 0xA3, 0x7A, 0xA3, 0x81, /* 0xAC-0xAF */
+ 0xA3, 0x82, 0xA3, 0x83, 0xA3, 0x84, 0xA3, 0x85, /* 0xB0-0xB3 */
+ 0xA3, 0x86, 0xA3, 0x87, 0xA3, 0x88, 0xA3, 0x89, /* 0xB4-0xB7 */
+ 0xA3, 0x8A, 0xA3, 0x8B, 0xA3, 0x8C, 0xA3, 0x8D, /* 0xB8-0xBB */
+ 0xA3, 0x8E, 0xA3, 0x8F, 0xA3, 0x90, 0xA3, 0x91, /* 0xBC-0xBF */
+ 0xC1, 0xF6, 0xC1, 0xF7, 0xA3, 0x92, 0xA3, 0x93, /* 0xC0-0xC3 */
+ 0xC1, 0xF8, 0xA3, 0x94, 0xA3, 0x95, 0xC1, 0xF9, /* 0xC4-0xC7 */
+ 0xC1, 0xFA, 0xA3, 0x96, 0xC1, 0xFB, 0xA3, 0x97, /* 0xC8-0xCB */
+ 0xA3, 0x98, 0xA3, 0x99, 0xA3, 0x9A, 0xA3, 0x9B, /* 0xCC-0xCF */
+ 0xC1, 0xFC, 0xC1, 0xFD, 0xA3, 0x9C, 0xC1, 0xFE, /* 0xD0-0xD3 */
+ 0xA3, 0x9D, 0xC2, 0xA1, 0xC2, 0xA2, 0xA3, 0x9E, /* 0xD4-0xD7 */
+ 0xA3, 0x9F, 0xC2, 0xA3, 0xC2, 0xA4, 0xA3, 0xA0, /* 0xD8-0xDB */
+ 0xC2, 0xA5, 0xC2, 0xA6, 0xA4, 0x41, 0xA4, 0x42, /* 0xDC-0xDF */
+ 0xC2, 0xA7, 0xA4, 0x43, 0xC2, 0xA8, 0xA4, 0x44, /* 0xE0-0xE3 */
+ 0xC2, 0xA9, 0xA4, 0x45, 0xA4, 0x46, 0xC2, 0xAA, /* 0xE4-0xE7 */
+ 0xA4, 0x47, 0xA4, 0x48, 0xA4, 0x49, 0xA4, 0x4A, /* 0xE8-0xEB */
+ 0xC2, 0xAB, 0xC2, 0xAC, 0xA4, 0x4B, 0xC2, 0xAD, /* 0xEC-0xEF */
+ 0xC2, 0xAE, 0xC2, 0xAF, 0xA4, 0x4C, 0xA4, 0x4D, /* 0xF0-0xF3 */
+ 0xA4, 0x4E, 0xA4, 0x4F, 0xA4, 0x50, 0xA4, 0x51, /* 0xF4-0xF7 */
+ 0xC2, 0xB0, 0xC2, 0xB1, 0xA4, 0x52, 0xA4, 0x53, /* 0xF8-0xFB */
+ 0xC2, 0xB2, 0xA4, 0x54, 0xA4, 0x55, 0xA4, 0x56, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_CA[512] = {
+ 0xC2, 0xB3, 0xA4, 0x57, 0xA4, 0x58, 0xA4, 0x59, /* 0x00-0x03 */
+ 0xA4, 0x5A, 0xA4, 0x61, 0xA4, 0x62, 0xA4, 0x63, /* 0x04-0x07 */
+ 0xC2, 0xB4, 0xC2, 0xB5, 0xA4, 0x64, 0xC2, 0xB6, /* 0x08-0x0B */
+ 0xC2, 0xB7, 0xC2, 0xB8, 0xA4, 0x65, 0xA4, 0x66, /* 0x0C-0x0F */
+ 0xA4, 0x67, 0xA4, 0x68, 0xA4, 0x69, 0xA4, 0x6A, /* 0x10-0x13 */
+ 0xC2, 0xB9, 0xA4, 0x6B, 0xA4, 0x6C, 0xA4, 0x6D, /* 0x14-0x17 */
+ 0xC2, 0xBA, 0xA4, 0x6E, 0xA4, 0x6F, 0xA4, 0x70, /* 0x18-0x1B */
+ 0xA4, 0x71, 0xA4, 0x72, 0xA4, 0x73, 0xA4, 0x74, /* 0x1C-0x1F */
+ 0xA4, 0x75, 0xA4, 0x76, 0xA4, 0x77, 0xA4, 0x78, /* 0x20-0x23 */
+ 0xA4, 0x79, 0xA4, 0x7A, 0xA4, 0x81, 0xA4, 0x82, /* 0x24-0x27 */
+ 0xA4, 0x83, 0xC2, 0xBB, 0xA4, 0x84, 0xA4, 0x85, /* 0x28-0x2B */
+ 0xA4, 0x86, 0xA4, 0x87, 0xA4, 0x88, 0xA4, 0x89, /* 0x2C-0x2F */
+ 0xA4, 0x8A, 0xA4, 0x8B, 0xA4, 0x8C, 0xA4, 0x8D, /* 0x30-0x33 */
+ 0xA4, 0x8E, 0xA4, 0x8F, 0xA4, 0x90, 0xA4, 0x91, /* 0x34-0x37 */
+ 0xA4, 0x92, 0xA4, 0x93, 0xA4, 0x94, 0xA4, 0x95, /* 0x38-0x3B */
+ 0xA4, 0x96, 0xA4, 0x97, 0xA4, 0x98, 0xA4, 0x99, /* 0x3C-0x3F */
+ 0xA4, 0x9A, 0xA4, 0x9B, 0xA4, 0x9C, 0xA4, 0x9D, /* 0x40-0x43 */
+ 0xA4, 0x9E, 0xA4, 0x9F, 0xA4, 0xA0, 0xA5, 0x41, /* 0x44-0x47 */
+ 0xA5, 0x42, 0xA5, 0x43, 0xA5, 0x44, 0xA5, 0x45, /* 0x48-0x4B */
+ 0xC2, 0xBC, 0xC2, 0xBD, 0xA5, 0x46, 0xA5, 0x47, /* 0x4C-0x4F */
+ 0xC2, 0xBE, 0xA5, 0x48, 0xA5, 0x49, 0xA5, 0x4A, /* 0x50-0x53 */
+ 0xC2, 0xBF, 0xA5, 0x4B, 0xA5, 0x4C, 0xA5, 0x4D, /* 0x54-0x57 */
+ 0xA5, 0x4E, 0xA5, 0x4F, 0xA5, 0x50, 0xA5, 0x51, /* 0x58-0x5B */
+ 0xC2, 0xC0, 0xC2, 0xC1, 0xA5, 0x52, 0xC2, 0xC2, /* 0x5C-0x5F */
+ 0xC2, 0xC3, 0xC2, 0xC4, 0xA5, 0x53, 0xA5, 0x54, /* 0x60-0x63 */
+ 0xA5, 0x55, 0xA5, 0x56, 0xA5, 0x57, 0xA5, 0x58, /* 0x64-0x67 */
+ 0xC2, 0xC5, 0xA5, 0x59, 0xA5, 0x5A, 0xA5, 0x61, /* 0x68-0x6B */
+ 0xA5, 0x62, 0xA5, 0x63, 0xA5, 0x64, 0xA5, 0x65, /* 0x6C-0x6F */
+ 0xA5, 0x66, 0xA5, 0x67, 0xA5, 0x68, 0xA5, 0x69, /* 0x70-0x73 */
+ 0xA5, 0x6A, 0xA5, 0x6B, 0xA5, 0x6C, 0xA5, 0x6D, /* 0x74-0x77 */
+ 0xA5, 0x6E, 0xA5, 0x6F, 0xA5, 0x70, 0xA5, 0x71, /* 0x78-0x7B */
+ 0xA5, 0x72, 0xC2, 0xC6, 0xA5, 0x73, 0xA5, 0x74, /* 0x7C-0x7F */
+
+ 0xA5, 0x75, 0xA5, 0x76, 0xA5, 0x77, 0xA5, 0x78, /* 0x80-0x83 */
+ 0xC2, 0xC7, 0xA5, 0x79, 0xA5, 0x7A, 0xA5, 0x81, /* 0x84-0x87 */
+ 0xA5, 0x82, 0xA5, 0x83, 0xA5, 0x84, 0xA5, 0x85, /* 0x88-0x8B */
+ 0xA5, 0x86, 0xA5, 0x87, 0xA5, 0x88, 0xA5, 0x89, /* 0x8C-0x8F */
+ 0xA5, 0x8A, 0xA5, 0x8B, 0xA5, 0x8C, 0xA5, 0x8D, /* 0x90-0x93 */
+ 0xA5, 0x8E, 0xA5, 0x8F, 0xA5, 0x90, 0xA5, 0x91, /* 0x94-0x97 */
+ 0xC2, 0xC8, 0xA5, 0x92, 0xA5, 0x93, 0xA5, 0x94, /* 0x98-0x9B */
+ 0xA5, 0x95, 0xA5, 0x96, 0xA5, 0x97, 0xA5, 0x98, /* 0x9C-0x9F */
+ 0xA5, 0x99, 0xA5, 0x9A, 0xA5, 0x9B, 0xA5, 0x9C, /* 0xA0-0xA3 */
+ 0xA5, 0x9D, 0xA5, 0x9E, 0xA5, 0x9F, 0xA5, 0xA0, /* 0xA4-0xA7 */
+ 0xA6, 0x41, 0xA6, 0x42, 0xA6, 0x43, 0xA6, 0x44, /* 0xA8-0xAB */
+ 0xA6, 0x45, 0xA6, 0x46, 0xA6, 0x47, 0xA6, 0x48, /* 0xAC-0xAF */
+ 0xA6, 0x49, 0xA6, 0x4A, 0xA6, 0x4B, 0xA6, 0x4C, /* 0xB0-0xB3 */
+ 0xA6, 0x4D, 0xA6, 0x4E, 0xA6, 0x4F, 0xA6, 0x50, /* 0xB4-0xB7 */
+ 0xA6, 0x51, 0xA6, 0x52, 0xA6, 0x53, 0xA6, 0x54, /* 0xB8-0xBB */
+ 0xC2, 0xC9, 0xC2, 0xCA, 0xA6, 0x55, 0xA6, 0x56, /* 0xBC-0xBF */
+ 0xC2, 0xCB, 0xA6, 0x57, 0xA6, 0x58, 0xA6, 0x59, /* 0xC0-0xC3 */
+ 0xC2, 0xCC, 0xA6, 0x5A, 0xA6, 0x61, 0xA6, 0x62, /* 0xC4-0xC7 */
+ 0xA6, 0x63, 0xA6, 0x64, 0xA6, 0x65, 0xA6, 0x66, /* 0xC8-0xCB */
+ 0xC2, 0xCD, 0xC2, 0xCE, 0xA6, 0x67, 0xC2, 0xCF, /* 0xCC-0xCF */
+ 0xA6, 0x68, 0xC2, 0xD0, 0xA6, 0x69, 0xC2, 0xD1, /* 0xD0-0xD3 */
+ 0xA6, 0x6A, 0xA6, 0x6B, 0xA6, 0x6C, 0xA6, 0x6D, /* 0xD4-0xD7 */
+ 0xC2, 0xD2, 0xC2, 0xD3, 0xA6, 0x6E, 0xA6, 0x6F, /* 0xD8-0xDB */
+ 0xA6, 0x70, 0xA6, 0x71, 0xA6, 0x72, 0xA6, 0x73, /* 0xDC-0xDF */
+ 0xC2, 0xD4, 0xA6, 0x74, 0xA6, 0x75, 0xA6, 0x76, /* 0xE0-0xE3 */
+ 0xA6, 0x77, 0xA6, 0x78, 0xA6, 0x79, 0xA6, 0x7A, /* 0xE4-0xE7 */
+ 0xA6, 0x81, 0xA6, 0x82, 0xA6, 0x83, 0xA6, 0x84, /* 0xE8-0xEB */
+ 0xC2, 0xD5, 0xA6, 0x85, 0xA6, 0x86, 0xA6, 0x87, /* 0xEC-0xEF */
+ 0xA6, 0x88, 0xA6, 0x89, 0xA6, 0x8A, 0xA6, 0x8B, /* 0xF0-0xF3 */
+ 0xC2, 0xD6, 0xA6, 0x8C, 0xA6, 0x8D, 0xA6, 0x8E, /* 0xF4-0xF7 */
+ 0xA6, 0x8F, 0xA6, 0x90, 0xA6, 0x91, 0xA6, 0x92, /* 0xF8-0xFB */
+ 0xA6, 0x93, 0xA6, 0x94, 0xA6, 0x95, 0xA6, 0x96, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_CB[512] = {
+ 0xA6, 0x97, 0xA6, 0x98, 0xA6, 0x99, 0xA6, 0x9A, /* 0x00-0x03 */
+ 0xA6, 0x9B, 0xA6, 0x9C, 0xA6, 0x9D, 0xA6, 0x9E, /* 0x04-0x07 */
+ 0xC2, 0xD7, 0xA6, 0x9F, 0xA6, 0xA0, 0xA7, 0x41, /* 0x08-0x0B */
+ 0xA7, 0x42, 0xA7, 0x43, 0xA7, 0x44, 0xA7, 0x45, /* 0x0C-0x0F */
+ 0xC2, 0xD8, 0xA7, 0x46, 0xA7, 0x47, 0xA7, 0x48, /* 0x10-0x13 */
+ 0xC2, 0xD9, 0xA7, 0x49, 0xA7, 0x4A, 0xA7, 0x4B, /* 0x14-0x17 */
+ 0xC2, 0xDA, 0xA7, 0x4C, 0xA7, 0x4D, 0xA7, 0x4E, /* 0x18-0x1B */
+ 0xA7, 0x4F, 0xA7, 0x50, 0xA7, 0x51, 0xA7, 0x52, /* 0x1C-0x1F */
+ 0xC2, 0xDB, 0xC2, 0xDC, 0xA7, 0x53, 0xA7, 0x54, /* 0x20-0x23 */
+ 0xA7, 0x55, 0xA7, 0x56, 0xA7, 0x57, 0xA7, 0x58, /* 0x24-0x27 */
+ 0xA7, 0x59, 0xA7, 0x5A, 0xA7, 0x61, 0xA7, 0x62, /* 0x28-0x2B */
+ 0xA7, 0x63, 0xA7, 0x64, 0xA7, 0x65, 0xA7, 0x66, /* 0x2C-0x2F */
+ 0xA7, 0x67, 0xA7, 0x68, 0xA7, 0x69, 0xA7, 0x6A, /* 0x30-0x33 */
+ 0xA7, 0x6B, 0xA7, 0x6C, 0xA7, 0x6D, 0xA7, 0x6E, /* 0x34-0x37 */
+ 0xA7, 0x6F, 0xA7, 0x70, 0xA7, 0x71, 0xA7, 0x72, /* 0x38-0x3B */
+ 0xA7, 0x73, 0xA7, 0x74, 0xA7, 0x75, 0xA7, 0x76, /* 0x3C-0x3F */
+ 0xA7, 0x77, 0xC2, 0xDD, 0xA7, 0x78, 0xA7, 0x79, /* 0x40-0x43 */
+ 0xA7, 0x7A, 0xA7, 0x81, 0xA7, 0x82, 0xA7, 0x83, /* 0x44-0x47 */
+ 0xC2, 0xDE, 0xC2, 0xDF, 0xA7, 0x84, 0xA7, 0x85, /* 0x48-0x4B */
+ 0xC2, 0xE0, 0xA7, 0x86, 0xA7, 0x87, 0xA7, 0x88, /* 0x4C-0x4F */
+ 0xC2, 0xE1, 0xA7, 0x89, 0xA7, 0x8A, 0xA7, 0x8B, /* 0x50-0x53 */
+ 0xA7, 0x8C, 0xA7, 0x8D, 0xA7, 0x8E, 0xA7, 0x8F, /* 0x54-0x57 */
+ 0xC2, 0xE2, 0xC2, 0xE3, 0xA7, 0x90, 0xA7, 0x91, /* 0x58-0x5B */
+ 0xA7, 0x92, 0xC2, 0xE4, 0xA7, 0x93, 0xA7, 0x94, /* 0x5C-0x5F */
+ 0xA7, 0x95, 0xA7, 0x96, 0xA7, 0x97, 0xA7, 0x98, /* 0x60-0x63 */
+ 0xC2, 0xE5, 0xA7, 0x99, 0xA7, 0x9A, 0xA7, 0x9B, /* 0x64-0x67 */
+ 0xA7, 0x9C, 0xA7, 0x9D, 0xA7, 0x9E, 0xA7, 0x9F, /* 0x68-0x6B */
+ 0xA7, 0xA0, 0xA8, 0x41, 0xA8, 0x42, 0xA8, 0x43, /* 0x6C-0x6F */
+ 0xA8, 0x44, 0xA8, 0x45, 0xA8, 0x46, 0xA8, 0x47, /* 0x70-0x73 */
+ 0xA8, 0x48, 0xA8, 0x49, 0xA8, 0x4A, 0xA8, 0x4B, /* 0x74-0x77 */
+ 0xC2, 0xE6, 0xC2, 0xE7, 0xA8, 0x4C, 0xA8, 0x4D, /* 0x78-0x7B */
+ 0xA8, 0x4E, 0xA8, 0x4F, 0xA8, 0x50, 0xA8, 0x51, /* 0x7C-0x7F */
+
+ 0xA8, 0x52, 0xA8, 0x53, 0xA8, 0x54, 0xA8, 0x55, /* 0x80-0x83 */
+ 0xA8, 0x56, 0xA8, 0x57, 0xA8, 0x58, 0xA8, 0x59, /* 0x84-0x87 */
+ 0xA8, 0x5A, 0xA8, 0x61, 0xA8, 0x62, 0xA8, 0x63, /* 0x88-0x8B */
+ 0xA8, 0x64, 0xA8, 0x65, 0xA8, 0x66, 0xA8, 0x67, /* 0x8C-0x8F */
+ 0xA8, 0x68, 0xA8, 0x69, 0xA8, 0x6A, 0xA8, 0x6B, /* 0x90-0x93 */
+ 0xA8, 0x6C, 0xA8, 0x6D, 0xA8, 0x6E, 0xA8, 0x6F, /* 0x94-0x97 */
+ 0xA8, 0x70, 0xA8, 0x71, 0xA8, 0x72, 0xA8, 0x73, /* 0x98-0x9B */
+ 0xC2, 0xE8, 0xA8, 0x74, 0xA8, 0x75, 0xA8, 0x76, /* 0x9C-0x9F */
+ 0xA8, 0x77, 0xA8, 0x78, 0xA8, 0x79, 0xA8, 0x7A, /* 0xA0-0xA3 */
+ 0xA8, 0x81, 0xA8, 0x82, 0xA8, 0x83, 0xA8, 0x84, /* 0xA4-0xA7 */
+ 0xA8, 0x85, 0xA8, 0x86, 0xA8, 0x87, 0xA8, 0x88, /* 0xA8-0xAB */
+ 0xA8, 0x89, 0xA8, 0x8A, 0xA8, 0x8B, 0xA8, 0x8C, /* 0xAC-0xAF */
+ 0xA8, 0x8D, 0xA8, 0x8E, 0xA8, 0x8F, 0xA8, 0x90, /* 0xB0-0xB3 */
+ 0xA8, 0x91, 0xA8, 0x92, 0xA8, 0x93, 0xA8, 0x94, /* 0xB4-0xB7 */
+ 0xC2, 0xE9, 0xA8, 0x95, 0xA8, 0x96, 0xA8, 0x97, /* 0xB8-0xBB */
+ 0xA8, 0x98, 0xA8, 0x99, 0xA8, 0x9A, 0xA8, 0x9B, /* 0xBC-0xBF */
+ 0xA8, 0x9C, 0xA8, 0x9D, 0xA8, 0x9E, 0xA8, 0x9F, /* 0xC0-0xC3 */
+ 0xA8, 0xA0, 0xA9, 0x41, 0xA9, 0x42, 0xA9, 0x43, /* 0xC4-0xC7 */
+ 0xA9, 0x44, 0xA9, 0x45, 0xA9, 0x46, 0xA9, 0x47, /* 0xC8-0xCB */
+ 0xA9, 0x48, 0xA9, 0x49, 0xA9, 0x4A, 0xA9, 0x4B, /* 0xCC-0xCF */
+ 0xA9, 0x4C, 0xA9, 0x4D, 0xA9, 0x4E, 0xA9, 0x4F, /* 0xD0-0xD3 */
+ 0xC2, 0xEA, 0xA9, 0x50, 0xA9, 0x51, 0xA9, 0x52, /* 0xD4-0xD7 */
+ 0xA9, 0x53, 0xA9, 0x54, 0xA9, 0x55, 0xA9, 0x56, /* 0xD8-0xDB */
+ 0xA9, 0x57, 0xA9, 0x58, 0xA9, 0x59, 0xA9, 0x5A, /* 0xDC-0xDF */
+ 0xA9, 0x61, 0xA9, 0x62, 0xA9, 0x63, 0xA9, 0x64, /* 0xE0-0xE3 */
+ 0xC2, 0xEB, 0xA9, 0x65, 0xA9, 0x66, 0xC2, 0xEC, /* 0xE4-0xE7 */
+ 0xA9, 0x67, 0xC2, 0xED, 0xA9, 0x68, 0xA9, 0x69, /* 0xE8-0xEB */
+ 0xA9, 0x6A, 0xA9, 0x6B, 0xA9, 0x6C, 0xA9, 0x6D, /* 0xEC-0xEF */
+ 0xA9, 0x6E, 0xA9, 0x6F, 0xA9, 0x70, 0xA9, 0x71, /* 0xF0-0xF3 */
+ 0xA9, 0x72, 0xA9, 0x73, 0xA9, 0x74, 0xA9, 0x75, /* 0xF4-0xF7 */
+ 0xA9, 0x76, 0xA9, 0x77, 0xA9, 0x78, 0xA9, 0x79, /* 0xF8-0xFB */
+ 0xA9, 0x7A, 0xA9, 0x81, 0xA9, 0x82, 0xA9, 0x83, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_CC[512] = {
+ 0xA9, 0x84, 0xA9, 0x85, 0xA9, 0x86, 0xA9, 0x87, /* 0x00-0x03 */
+ 0xA9, 0x88, 0xA9, 0x89, 0xA9, 0x8A, 0xA9, 0x8B, /* 0x04-0x07 */
+ 0xA9, 0x8C, 0xA9, 0x8D, 0xA9, 0x8E, 0xA9, 0x8F, /* 0x08-0x0B */
+ 0xC2, 0xEE, 0xC2, 0xEF, 0xA9, 0x90, 0xA9, 0x91, /* 0x0C-0x0F */
+ 0xC2, 0xF0, 0xA9, 0x92, 0xA9, 0x93, 0xA9, 0x94, /* 0x10-0x13 */
+ 0xC2, 0xF1, 0xA9, 0x95, 0xA9, 0x96, 0xA9, 0x97, /* 0x14-0x17 */
+ 0xA9, 0x98, 0xA9, 0x99, 0xA9, 0x9A, 0xA9, 0x9B, /* 0x18-0x1B */
+ 0xC2, 0xF2, 0xC2, 0xF3, 0xA9, 0x9C, 0xA9, 0x9D, /* 0x1C-0x1F */
+ 0xA9, 0x9E, 0xC2, 0xF4, 0xC2, 0xF5, 0xA9, 0x9F, /* 0x20-0x23 */
+ 0xA9, 0xA0, 0xAA, 0x41, 0xAA, 0x42, 0xC2, 0xF6, /* 0x24-0x27 */
+ 0xC2, 0xF7, 0xC2, 0xF8, 0xAA, 0x43, 0xAA, 0x44, /* 0x28-0x2B */
+ 0xC2, 0xF9, 0xAA, 0x45, 0xC2, 0xFA, 0xAA, 0x46, /* 0x2C-0x2F */
+ 0xC2, 0xFB, 0xAA, 0x47, 0xAA, 0x48, 0xAA, 0x49, /* 0x30-0x33 */
+ 0xAA, 0x4A, 0xAA, 0x4B, 0xAA, 0x4C, 0xAA, 0x4D, /* 0x34-0x37 */
+ 0xC2, 0xFC, 0xC2, 0xFD, 0xAA, 0x4E, 0xC2, 0xFE, /* 0x38-0x3B */
+ 0xC3, 0xA1, 0xC3, 0xA2, 0xC3, 0xA3, 0xAA, 0x4F, /* 0x3C-0x3F */
+ 0xAA, 0x50, 0xAA, 0x51, 0xAA, 0x52, 0xAA, 0x53, /* 0x40-0x43 */
+ 0xC3, 0xA4, 0xC3, 0xA5, 0xAA, 0x54, 0xAA, 0x55, /* 0x44-0x47 */
+ 0xC3, 0xA6, 0xAA, 0x56, 0xAA, 0x57, 0xAA, 0x58, /* 0x48-0x4B */
+ 0xC3, 0xA7, 0xAA, 0x59, 0xAA, 0x5A, 0xAA, 0x61, /* 0x4C-0x4F */
+ 0xAA, 0x62, 0xAA, 0x63, 0xAA, 0x64, 0xAA, 0x65, /* 0x50-0x53 */
+ 0xC3, 0xA8, 0xC3, 0xA9, 0xAA, 0x66, 0xC3, 0xAA, /* 0x54-0x57 */
+ 0xC3, 0xAB, 0xC3, 0xAC, 0xAA, 0x67, 0xAA, 0x68, /* 0x58-0x5B */
+ 0xAA, 0x69, 0xAA, 0x6A, 0xAA, 0x6B, 0xAA, 0x6C, /* 0x5C-0x5F */
+ 0xC3, 0xAD, 0xAA, 0x6D, 0xAA, 0x6E, 0xAA, 0x6F, /* 0x60-0x63 */
+ 0xC3, 0xAE, 0xAA, 0x70, 0xC3, 0xAF, 0xAA, 0x71, /* 0x64-0x67 */
+ 0xC3, 0xB0, 0xAA, 0x72, 0xAA, 0x73, 0xAA, 0x74, /* 0x68-0x6B */
+ 0xAA, 0x75, 0xAA, 0x76, 0xAA, 0x77, 0xAA, 0x78, /* 0x6C-0x6F */
+ 0xC3, 0xB1, 0xAA, 0x79, 0xAA, 0x7A, 0xAA, 0x81, /* 0x70-0x73 */
+ 0xAA, 0x82, 0xC3, 0xB2, 0xAA, 0x83, 0xAA, 0x84, /* 0x74-0x77 */
+ 0xAA, 0x85, 0xAA, 0x86, 0xAA, 0x87, 0xAA, 0x88, /* 0x78-0x7B */
+ 0xAA, 0x89, 0xAA, 0x8A, 0xAA, 0x8B, 0xAA, 0x8C, /* 0x7C-0x7F */
+
+ 0xAA, 0x8D, 0xAA, 0x8E, 0xAA, 0x8F, 0xAA, 0x90, /* 0x80-0x83 */
+ 0xAA, 0x91, 0xAA, 0x92, 0xAA, 0x93, 0xAA, 0x94, /* 0x84-0x87 */
+ 0xAA, 0x95, 0xAA, 0x96, 0xAA, 0x97, 0xAA, 0x98, /* 0x88-0x8B */
+ 0xAA, 0x99, 0xAA, 0x9A, 0xAA, 0x9B, 0xAA, 0x9C, /* 0x8C-0x8F */
+ 0xAA, 0x9D, 0xAA, 0x9E, 0xAA, 0x9F, 0xAA, 0xA0, /* 0x90-0x93 */
+ 0xAB, 0x41, 0xAB, 0x42, 0xAB, 0x43, 0xAB, 0x44, /* 0x94-0x97 */
+ 0xC3, 0xB3, 0xC3, 0xB4, 0xAB, 0x45, 0xAB, 0x46, /* 0x98-0x9B */
+ 0xC3, 0xB5, 0xAB, 0x47, 0xAB, 0x48, 0xAB, 0x49, /* 0x9C-0x9F */
+ 0xC3, 0xB6, 0xAB, 0x4A, 0xAB, 0x4B, 0xAB, 0x4C, /* 0xA0-0xA3 */
+ 0xAB, 0x4D, 0xAB, 0x4E, 0xAB, 0x4F, 0xAB, 0x50, /* 0xA4-0xA7 */
+ 0xC3, 0xB7, 0xC3, 0xB8, 0xAB, 0x51, 0xC3, 0xB9, /* 0xA8-0xAB */
+ 0xC3, 0xBA, 0xC3, 0xBB, 0xAB, 0x52, 0xAB, 0x53, /* 0xAC-0xAF */
+ 0xAB, 0x54, 0xAB, 0x55, 0xAB, 0x56, 0xAB, 0x57, /* 0xB0-0xB3 */
+ 0xC3, 0xBC, 0xC3, 0xBD, 0xAB, 0x58, 0xAB, 0x59, /* 0xB4-0xB7 */
+ 0xC3, 0xBE, 0xAB, 0x5A, 0xAB, 0x61, 0xAB, 0x62, /* 0xB8-0xBB */
+ 0xC3, 0xBF, 0xAB, 0x63, 0xAB, 0x64, 0xAB, 0x65, /* 0xBC-0xBF */
+ 0xAB, 0x66, 0xAB, 0x67, 0xAB, 0x68, 0xAB, 0x69, /* 0xC0-0xC3 */
+ 0xC3, 0xC0, 0xC3, 0xC1, 0xAB, 0x6A, 0xC3, 0xC2, /* 0xC4-0xC7 */
+ 0xAB, 0x6B, 0xC3, 0xC3, 0xAB, 0x6C, 0xAB, 0x6D, /* 0xC8-0xCB */
+ 0xAB, 0x6E, 0xAB, 0x6F, 0xAB, 0x70, 0xAB, 0x71, /* 0xCC-0xCF */
+ 0xC3, 0xC4, 0xAB, 0x72, 0xAB, 0x73, 0xAB, 0x74, /* 0xD0-0xD3 */
+ 0xC3, 0xC5, 0xAB, 0x75, 0xAB, 0x76, 0xAB, 0x77, /* 0xD4-0xD7 */
+ 0xAB, 0x78, 0xAB, 0x79, 0xAB, 0x7A, 0xAB, 0x81, /* 0xD8-0xDB */
+ 0xAB, 0x82, 0xAB, 0x83, 0xAB, 0x84, 0xAB, 0x85, /* 0xDC-0xDF */
+ 0xAB, 0x86, 0xAB, 0x87, 0xAB, 0x88, 0xAB, 0x89, /* 0xE0-0xE3 */
+ 0xC3, 0xC6, 0xAB, 0x8A, 0xAB, 0x8B, 0xAB, 0x8C, /* 0xE4-0xE7 */
+ 0xAB, 0x8D, 0xAB, 0x8E, 0xAB, 0x8F, 0xAB, 0x90, /* 0xE8-0xEB */
+ 0xC3, 0xC7, 0xAB, 0x91, 0xAB, 0x92, 0xAB, 0x93, /* 0xEC-0xEF */
+ 0xC3, 0xC8, 0xAB, 0x94, 0xAB, 0x95, 0xAB, 0x96, /* 0xF0-0xF3 */
+ 0xAB, 0x97, 0xAB, 0x98, 0xAB, 0x99, 0xAB, 0x9A, /* 0xF4-0xF7 */
+ 0xAB, 0x9B, 0xAB, 0x9C, 0xAB, 0x9D, 0xAB, 0x9E, /* 0xF8-0xFB */
+ 0xAB, 0x9F, 0xAB, 0xA0, 0xAC, 0x41, 0xAC, 0x42, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_CD[512] = {
+ 0xAC, 0x43, 0xC3, 0xC9, 0xAC, 0x44, 0xAC, 0x45, /* 0x00-0x03 */
+ 0xAC, 0x46, 0xAC, 0x47, 0xAC, 0x48, 0xAC, 0x49, /* 0x04-0x07 */
+ 0xC3, 0xCA, 0xC3, 0xCB, 0xAC, 0x4A, 0xAC, 0x4B, /* 0x08-0x0B */
+ 0xC3, 0xCC, 0xAC, 0x4C, 0xAC, 0x4D, 0xAC, 0x4E, /* 0x0C-0x0F */
+ 0xC3, 0xCD, 0xAC, 0x4F, 0xAC, 0x50, 0xAC, 0x51, /* 0x10-0x13 */
+ 0xAC, 0x52, 0xAC, 0x53, 0xAC, 0x54, 0xAC, 0x55, /* 0x14-0x17 */
+ 0xC3, 0xCE, 0xC3, 0xCF, 0xAC, 0x56, 0xC3, 0xD0, /* 0x18-0x1B */
+ 0xAC, 0x57, 0xC3, 0xD1, 0xAC, 0x58, 0xAC, 0x59, /* 0x1C-0x1F */
+ 0xAC, 0x5A, 0xAC, 0x61, 0xAC, 0x62, 0xAC, 0x63, /* 0x20-0x23 */
+ 0xC3, 0xD2, 0xAC, 0x64, 0xAC, 0x65, 0xAC, 0x66, /* 0x24-0x27 */
+ 0xC3, 0xD3, 0xAC, 0x67, 0xAC, 0x68, 0xAC, 0x69, /* 0x28-0x2B */
+ 0xC3, 0xD4, 0xAC, 0x6A, 0xAC, 0x6B, 0xAC, 0x6C, /* 0x2C-0x2F */
+ 0xAC, 0x6D, 0xAC, 0x6E, 0xAC, 0x6F, 0xAC, 0x70, /* 0x30-0x33 */
+ 0xAC, 0x71, 0xAC, 0x72, 0xAC, 0x73, 0xAC, 0x74, /* 0x34-0x37 */
+ 0xAC, 0x75, 0xC3, 0xD5, 0xAC, 0x76, 0xAC, 0x77, /* 0x38-0x3B */
+ 0xAC, 0x78, 0xAC, 0x79, 0xAC, 0x7A, 0xAC, 0x81, /* 0x3C-0x3F */
+ 0xAC, 0x82, 0xAC, 0x83, 0xAC, 0x84, 0xAC, 0x85, /* 0x40-0x43 */
+ 0xAC, 0x86, 0xAC, 0x87, 0xAC, 0x88, 0xAC, 0x89, /* 0x44-0x47 */
+ 0xAC, 0x8A, 0xAC, 0x8B, 0xAC, 0x8C, 0xAC, 0x8D, /* 0x48-0x4B */
+ 0xAC, 0x8E, 0xAC, 0x8F, 0xAC, 0x90, 0xAC, 0x91, /* 0x4C-0x4F */
+ 0xAC, 0x92, 0xAC, 0x93, 0xAC, 0x94, 0xAC, 0x95, /* 0x50-0x53 */
+ 0xAC, 0x96, 0xAC, 0x97, 0xAC, 0x98, 0xAC, 0x99, /* 0x54-0x57 */
+ 0xAC, 0x9A, 0xAC, 0x9B, 0xAC, 0x9C, 0xAC, 0x9D, /* 0x58-0x5B */
+ 0xC3, 0xD6, 0xAC, 0x9E, 0xAC, 0x9F, 0xAC, 0xA0, /* 0x5C-0x5F */
+ 0xC3, 0xD7, 0xAD, 0x41, 0xAD, 0x42, 0xAD, 0x43, /* 0x60-0x63 */
+ 0xC3, 0xD8, 0xAD, 0x44, 0xAD, 0x45, 0xAD, 0x46, /* 0x64-0x67 */
+ 0xAD, 0x47, 0xAD, 0x48, 0xAD, 0x49, 0xAD, 0x4A, /* 0x68-0x6B */
+ 0xC3, 0xD9, 0xC3, 0xDA, 0xAD, 0x4B, 0xC3, 0xDB, /* 0x6C-0x6F */
+ 0xAD, 0x4C, 0xC3, 0xDC, 0xAD, 0x4D, 0xAD, 0x4E, /* 0x70-0x73 */
+ 0xAD, 0x4F, 0xAD, 0x50, 0xAD, 0x51, 0xAD, 0x52, /* 0x74-0x77 */
+ 0xC3, 0xDD, 0xAD, 0x53, 0xAD, 0x54, 0xAD, 0x55, /* 0x78-0x7B */
+ 0xAD, 0x56, 0xAD, 0x57, 0xAD, 0x58, 0xAD, 0x59, /* 0x7C-0x7F */
+
+ 0xAD, 0x5A, 0xAD, 0x61, 0xAD, 0x62, 0xAD, 0x63, /* 0x80-0x83 */
+ 0xAD, 0x64, 0xAD, 0x65, 0xAD, 0x66, 0xAD, 0x67, /* 0x84-0x87 */
+ 0xC3, 0xDE, 0xAD, 0x68, 0xAD, 0x69, 0xAD, 0x6A, /* 0x88-0x8B */
+ 0xAD, 0x6B, 0xAD, 0x6C, 0xAD, 0x6D, 0xAD, 0x6E, /* 0x8C-0x8F */
+ 0xAD, 0x6F, 0xAD, 0x70, 0xAD, 0x71, 0xAD, 0x72, /* 0x90-0x93 */
+ 0xC3, 0xDF, 0xC3, 0xE0, 0xAD, 0x73, 0xAD, 0x74, /* 0x94-0x97 */
+ 0xC3, 0xE1, 0xAD, 0x75, 0xAD, 0x76, 0xAD, 0x77, /* 0x98-0x9B */
+ 0xC3, 0xE2, 0xAD, 0x78, 0xAD, 0x79, 0xAD, 0x7A, /* 0x9C-0x9F */
+ 0xAD, 0x81, 0xAD, 0x82, 0xAD, 0x83, 0xAD, 0x84, /* 0xA0-0xA3 */
+ 0xC3, 0xE3, 0xC3, 0xE4, 0xAD, 0x85, 0xC3, 0xE5, /* 0xA4-0xA7 */
+ 0xAD, 0x86, 0xC3, 0xE6, 0xAD, 0x87, 0xAD, 0x88, /* 0xA8-0xAB */
+ 0xAD, 0x89, 0xAD, 0x8A, 0xAD, 0x8B, 0xAD, 0x8C, /* 0xAC-0xAF */
+ 0xC3, 0xE7, 0xAD, 0x8D, 0xAD, 0x8E, 0xAD, 0x8F, /* 0xB0-0xB3 */
+ 0xAD, 0x90, 0xAD, 0x91, 0xAD, 0x92, 0xAD, 0x93, /* 0xB4-0xB7 */
+ 0xAD, 0x94, 0xAD, 0x95, 0xAD, 0x96, 0xAD, 0x97, /* 0xB8-0xBB */
+ 0xAD, 0x98, 0xAD, 0x99, 0xAD, 0x9A, 0xAD, 0x9B, /* 0xBC-0xBF */
+ 0xAD, 0x9C, 0xAD, 0x9D, 0xAD, 0x9E, 0xAD, 0x9F, /* 0xC0-0xC3 */
+ 0xC3, 0xE8, 0xAD, 0xA0, 0xAE, 0x41, 0xAE, 0x42, /* 0xC4-0xC7 */
+ 0xAE, 0x43, 0xAE, 0x44, 0xAE, 0x45, 0xAE, 0x46, /* 0xC8-0xCB */
+ 0xC3, 0xE9, 0xAE, 0x47, 0xAE, 0x48, 0xAE, 0x49, /* 0xCC-0xCF */
+ 0xC3, 0xEA, 0xAE, 0x4A, 0xAE, 0x4B, 0xAE, 0x4C, /* 0xD0-0xD3 */
+ 0xAE, 0x4D, 0xAE, 0x4E, 0xAE, 0x4F, 0xAE, 0x50, /* 0xD4-0xD7 */
+ 0xAE, 0x51, 0xAE, 0x52, 0xAE, 0x53, 0xAE, 0x54, /* 0xD8-0xDB */
+ 0xAE, 0x55, 0xAE, 0x56, 0xAE, 0x57, 0xAE, 0x58, /* 0xDC-0xDF */
+ 0xAE, 0x59, 0xAE, 0x5A, 0xAE, 0x61, 0xAE, 0x62, /* 0xE0-0xE3 */
+ 0xAE, 0x63, 0xAE, 0x64, 0xAE, 0x65, 0xAE, 0x66, /* 0xE4-0xE7 */
+ 0xC3, 0xEB, 0xAE, 0x67, 0xAE, 0x68, 0xAE, 0x69, /* 0xE8-0xEB */
+ 0xC3, 0xEC, 0xAE, 0x6A, 0xAE, 0x6B, 0xAE, 0x6C, /* 0xEC-0xEF */
+ 0xC3, 0xED, 0xAE, 0x6D, 0xAE, 0x6E, 0xAE, 0x6F, /* 0xF0-0xF3 */
+ 0xAE, 0x70, 0xAE, 0x71, 0xAE, 0x72, 0xAE, 0x73, /* 0xF4-0xF7 */
+ 0xC3, 0xEE, 0xC3, 0xEF, 0xAE, 0x74, 0xC3, 0xF0, /* 0xF8-0xFB */
+ 0xAE, 0x75, 0xC3, 0xF1, 0xAE, 0x76, 0xAE, 0x77, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_CE[512] = {
+ 0xAE, 0x78, 0xAE, 0x79, 0xAE, 0x7A, 0xAE, 0x81, /* 0x00-0x03 */
+ 0xC3, 0xF2, 0xAE, 0x82, 0xAE, 0x83, 0xAE, 0x84, /* 0x04-0x07 */
+ 0xC3, 0xF3, 0xAE, 0x85, 0xAE, 0x86, 0xAE, 0x87, /* 0x08-0x0B */
+ 0xC3, 0xF4, 0xAE, 0x88, 0xAE, 0x89, 0xAE, 0x8A, /* 0x0C-0x0F */
+ 0xAE, 0x8B, 0xAE, 0x8C, 0xAE, 0x8D, 0xAE, 0x8E, /* 0x10-0x13 */
+ 0xC3, 0xF5, 0xAE, 0x8F, 0xAE, 0x90, 0xAE, 0x91, /* 0x14-0x17 */
+ 0xAE, 0x92, 0xC3, 0xF6, 0xAE, 0x93, 0xAE, 0x94, /* 0x18-0x1B */
+ 0xAE, 0x95, 0xAE, 0x96, 0xAE, 0x97, 0xAE, 0x98, /* 0x1C-0x1F */
+ 0xC3, 0xF7, 0xC3, 0xF8, 0xAE, 0x99, 0xAE, 0x9A, /* 0x20-0x23 */
+ 0xC3, 0xF9, 0xAE, 0x9B, 0xAE, 0x9C, 0xAE, 0x9D, /* 0x24-0x27 */
+ 0xC3, 0xFA, 0xAE, 0x9E, 0xAE, 0x9F, 0xAE, 0xA0, /* 0x28-0x2B */
+ 0xAF, 0x41, 0xAF, 0x42, 0xAF, 0x43, 0xAF, 0x44, /* 0x2C-0x2F */
+ 0xC3, 0xFB, 0xC3, 0xFC, 0xAF, 0x45, 0xC3, 0xFD, /* 0x30-0x33 */
+ 0xAF, 0x46, 0xC3, 0xFE, 0xAF, 0x47, 0xAF, 0x48, /* 0x34-0x37 */
+ 0xAF, 0x49, 0xAF, 0x4A, 0xAF, 0x4B, 0xAF, 0x4C, /* 0x38-0x3B */
+ 0xAF, 0x4D, 0xAF, 0x4E, 0xAF, 0x4F, 0xAF, 0x50, /* 0x3C-0x3F */
+ 0xAF, 0x51, 0xAF, 0x52, 0xAF, 0x53, 0xAF, 0x54, /* 0x40-0x43 */
+ 0xAF, 0x55, 0xAF, 0x56, 0xAF, 0x57, 0xAF, 0x58, /* 0x44-0x47 */
+ 0xAF, 0x59, 0xAF, 0x5A, 0xAF, 0x61, 0xAF, 0x62, /* 0x48-0x4B */
+ 0xAF, 0x63, 0xAF, 0x64, 0xAF, 0x65, 0xAF, 0x66, /* 0x4C-0x4F */
+ 0xAF, 0x67, 0xAF, 0x68, 0xAF, 0x69, 0xAF, 0x6A, /* 0x50-0x53 */
+ 0xAF, 0x6B, 0xAF, 0x6C, 0xAF, 0x6D, 0xAF, 0x6E, /* 0x54-0x57 */
+ 0xC4, 0xA1, 0xC4, 0xA2, 0xAF, 0x6F, 0xAF, 0x70, /* 0x58-0x5B */
+ 0xC4, 0xA3, 0xAF, 0x71, 0xAF, 0x72, 0xC4, 0xA4, /* 0x5C-0x5F */
+ 0xC4, 0xA5, 0xC4, 0xA6, 0xAF, 0x73, 0xAF, 0x74, /* 0x60-0x63 */
+ 0xAF, 0x75, 0xAF, 0x76, 0xAF, 0x77, 0xAF, 0x78, /* 0x64-0x67 */
+ 0xC4, 0xA7, 0xC4, 0xA8, 0xAF, 0x79, 0xC4, 0xA9, /* 0x68-0x6B */
+ 0xAF, 0x7A, 0xC4, 0xAA, 0xAF, 0x81, 0xAF, 0x82, /* 0x6C-0x6F */
+ 0xAF, 0x83, 0xAF, 0x84, 0xAF, 0x85, 0xAF, 0x86, /* 0x70-0x73 */
+ 0xC4, 0xAB, 0xC4, 0xAC, 0xAF, 0x87, 0xAF, 0x88, /* 0x74-0x77 */
+ 0xC4, 0xAD, 0xAF, 0x89, 0xAF, 0x8A, 0xAF, 0x8B, /* 0x78-0x7B */
+ 0xC4, 0xAE, 0xAF, 0x8C, 0xAF, 0x8D, 0xAF, 0x8E, /* 0x7C-0x7F */
+
+ 0xAF, 0x8F, 0xAF, 0x90, 0xAF, 0x91, 0xAF, 0x92, /* 0x80-0x83 */
+ 0xC4, 0xAF, 0xC4, 0xB0, 0xAF, 0x93, 0xC4, 0xB1, /* 0x84-0x87 */
+ 0xAF, 0x94, 0xC4, 0xB2, 0xAF, 0x95, 0xAF, 0x96, /* 0x88-0x8B */
+ 0xAF, 0x97, 0xAF, 0x98, 0xAF, 0x99, 0xAF, 0x9A, /* 0x8C-0x8F */
+ 0xC4, 0xB3, 0xC4, 0xB4, 0xAF, 0x9B, 0xAF, 0x9C, /* 0x90-0x93 */
+ 0xC4, 0xB5, 0xAF, 0x9D, 0xAF, 0x9E, 0xAF, 0x9F, /* 0x94-0x97 */
+ 0xC4, 0xB6, 0xAF, 0xA0, 0xB0, 0x41, 0xB0, 0x42, /* 0x98-0x9B */
+ 0xB0, 0x43, 0xB0, 0x44, 0xB0, 0x45, 0xB0, 0x46, /* 0x9C-0x9F */
+ 0xC4, 0xB7, 0xC4, 0xB8, 0xB0, 0x47, 0xC4, 0xB9, /* 0xA0-0xA3 */
+ 0xC4, 0xBA, 0xC4, 0xBB, 0xB0, 0x48, 0xB0, 0x49, /* 0xA4-0xA7 */
+ 0xB0, 0x4A, 0xB0, 0x4B, 0xB0, 0x4C, 0xB0, 0x4D, /* 0xA8-0xAB */
+ 0xC4, 0xBC, 0xC4, 0xBD, 0xB0, 0x4E, 0xB0, 0x4F, /* 0xAC-0xAF */
+ 0xB0, 0x50, 0xB0, 0x51, 0xB0, 0x52, 0xB0, 0x53, /* 0xB0-0xB3 */
+ 0xB0, 0x54, 0xB0, 0x55, 0xB0, 0x56, 0xB0, 0x57, /* 0xB4-0xB7 */
+ 0xB0, 0x58, 0xB0, 0x59, 0xB0, 0x5A, 0xB0, 0x61, /* 0xB8-0xBB */
+ 0xB0, 0x62, 0xB0, 0x63, 0xB0, 0x64, 0xB0, 0x65, /* 0xBC-0xBF */
+ 0xB0, 0x66, 0xC4, 0xBE, 0xB0, 0x67, 0xB0, 0x68, /* 0xC0-0xC3 */
+ 0xB0, 0x69, 0xB0, 0x6A, 0xB0, 0x6B, 0xB0, 0x6C, /* 0xC4-0xC7 */
+ 0xB0, 0x6D, 0xB0, 0x6E, 0xB0, 0x6F, 0xB0, 0x70, /* 0xC8-0xCB */
+ 0xB0, 0x71, 0xB0, 0x72, 0xB0, 0x73, 0xB0, 0x74, /* 0xCC-0xCF */
+ 0xB0, 0x75, 0xB0, 0x76, 0xB0, 0x77, 0xB0, 0x78, /* 0xD0-0xD3 */
+ 0xB0, 0x79, 0xB0, 0x7A, 0xB0, 0x81, 0xB0, 0x82, /* 0xD4-0xD7 */
+ 0xB0, 0x83, 0xB0, 0x84, 0xB0, 0x85, 0xB0, 0x86, /* 0xD8-0xDB */
+ 0xB0, 0x87, 0xB0, 0x88, 0xB0, 0x89, 0xB0, 0x8A, /* 0xDC-0xDF */
+ 0xB0, 0x8B, 0xB0, 0x8C, 0xB0, 0x8D, 0xB0, 0x8E, /* 0xE0-0xE3 */
+ 0xC4, 0xBF, 0xC4, 0xC0, 0xB0, 0x8F, 0xB0, 0x90, /* 0xE4-0xE7 */
+ 0xC4, 0xC1, 0xB0, 0x91, 0xB0, 0x92, 0xC4, 0xC2, /* 0xE8-0xEB */
+ 0xC4, 0xC3, 0xB0, 0x93, 0xB0, 0x94, 0xB0, 0x95, /* 0xEC-0xEF */
+ 0xB0, 0x96, 0xB0, 0x97, 0xB0, 0x98, 0xB0, 0x99, /* 0xF0-0xF3 */
+ 0xC4, 0xC4, 0xC4, 0xC5, 0xB0, 0x9A, 0xC4, 0xC6, /* 0xF4-0xF7 */
+ 0xC4, 0xC7, 0xC4, 0xC8, 0xB0, 0x9B, 0xB0, 0x9C, /* 0xF8-0xFB */
+ 0xB0, 0x9D, 0xB0, 0x9E, 0xB0, 0x9F, 0xB0, 0xA0, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_CF[512] = {
+ 0xC4, 0xC9, 0xC4, 0xCA, 0xB1, 0x41, 0xB1, 0x42, /* 0x00-0x03 */
+ 0xC4, 0xCB, 0xB1, 0x43, 0xB1, 0x44, 0xB1, 0x45, /* 0x04-0x07 */
+ 0xC4, 0xCC, 0xB1, 0x46, 0xB1, 0x47, 0xB1, 0x48, /* 0x08-0x0B */
+ 0xB1, 0x49, 0xB1, 0x4A, 0xB1, 0x4B, 0xB1, 0x4C, /* 0x0C-0x0F */
+ 0xC4, 0xCD, 0xC4, 0xCE, 0xB1, 0x4D, 0xC4, 0xCF, /* 0x10-0x13 */
+ 0xB1, 0x4E, 0xC4, 0xD0, 0xB1, 0x4F, 0xB1, 0x50, /* 0x14-0x17 */
+ 0xB1, 0x51, 0xB1, 0x52, 0xB1, 0x53, 0xB1, 0x54, /* 0x18-0x1B */
+ 0xC4, 0xD1, 0xB1, 0x55, 0xB1, 0x56, 0xB1, 0x57, /* 0x1C-0x1F */
+ 0xC4, 0xD2, 0xB1, 0x58, 0xB1, 0x59, 0xB1, 0x5A, /* 0x20-0x23 */
+ 0xC4, 0xD3, 0xB1, 0x61, 0xB1, 0x62, 0xB1, 0x63, /* 0x24-0x27 */
+ 0xB1, 0x64, 0xB1, 0x65, 0xB1, 0x66, 0xB1, 0x67, /* 0x28-0x2B */
+ 0xC4, 0xD4, 0xC4, 0xD5, 0xB1, 0x68, 0xC4, 0xD6, /* 0x2C-0x2F */
+ 0xC4, 0xD7, 0xC4, 0xD8, 0xB1, 0x69, 0xB1, 0x6A, /* 0x30-0x33 */
+ 0xB1, 0x6B, 0xB1, 0x6C, 0xB1, 0x6D, 0xB1, 0x6E, /* 0x34-0x37 */
+ 0xC4, 0xD9, 0xB1, 0x6F, 0xB1, 0x70, 0xB1, 0x71, /* 0x38-0x3B */
+ 0xB1, 0x72, 0xB1, 0x73, 0xB1, 0x74, 0xB1, 0x75, /* 0x3C-0x3F */
+ 0xB1, 0x76, 0xB1, 0x77, 0xB1, 0x78, 0xB1, 0x79, /* 0x40-0x43 */
+ 0xB1, 0x7A, 0xB1, 0x81, 0xB1, 0x82, 0xB1, 0x83, /* 0x44-0x47 */
+ 0xB1, 0x84, 0xB1, 0x85, 0xB1, 0x86, 0xB1, 0x87, /* 0x48-0x4B */
+ 0xB1, 0x88, 0xB1, 0x89, 0xB1, 0x8A, 0xB1, 0x8B, /* 0x4C-0x4F */
+ 0xB1, 0x8C, 0xB1, 0x8D, 0xB1, 0x8E, 0xB1, 0x8F, /* 0x50-0x53 */
+ 0xC4, 0xDA, 0xC4, 0xDB, 0xB1, 0x90, 0xB1, 0x91, /* 0x54-0x57 */
+ 0xC4, 0xDC, 0xB1, 0x92, 0xB1, 0x93, 0xB1, 0x94, /* 0x58-0x5B */
+ 0xC4, 0xDD, 0xB1, 0x95, 0xB1, 0x96, 0xB1, 0x97, /* 0x5C-0x5F */
+ 0xB1, 0x98, 0xB1, 0x99, 0xB1, 0x9A, 0xB1, 0x9B, /* 0x60-0x63 */
+ 0xC4, 0xDE, 0xC4, 0xDF, 0xB1, 0x9C, 0xC4, 0xE0, /* 0x64-0x67 */
+ 0xB1, 0x9D, 0xC4, 0xE1, 0xB1, 0x9E, 0xB1, 0x9F, /* 0x68-0x6B */
+ 0xB1, 0xA0, 0xB2, 0x41, 0xB2, 0x42, 0xB2, 0x43, /* 0x6C-0x6F */
+ 0xC4, 0xE2, 0xC4, 0xE3, 0xB2, 0x44, 0xB2, 0x45, /* 0x70-0x73 */
+ 0xC4, 0xE4, 0xB2, 0x46, 0xB2, 0x47, 0xB2, 0x48, /* 0x74-0x77 */
+ 0xC4, 0xE5, 0xB2, 0x49, 0xB2, 0x4A, 0xB2, 0x4B, /* 0x78-0x7B */
+ 0xB2, 0x4C, 0xB2, 0x4D, 0xB2, 0x4E, 0xB2, 0x4F, /* 0x7C-0x7F */
+
+ 0xC4, 0xE6, 0xB2, 0x50, 0xB2, 0x51, 0xB2, 0x52, /* 0x80-0x83 */
+ 0xB2, 0x53, 0xC4, 0xE7, 0xB2, 0x54, 0xB2, 0x55, /* 0x84-0x87 */
+ 0xB2, 0x56, 0xB2, 0x57, 0xB2, 0x58, 0xB2, 0x59, /* 0x88-0x8B */
+ 0xC4, 0xE8, 0xB2, 0x5A, 0xB2, 0x61, 0xB2, 0x62, /* 0x8C-0x8F */
+ 0xB2, 0x63, 0xB2, 0x64, 0xB2, 0x65, 0xB2, 0x66, /* 0x90-0x93 */
+ 0xB2, 0x67, 0xB2, 0x68, 0xB2, 0x69, 0xB2, 0x6A, /* 0x94-0x97 */
+ 0xB2, 0x6B, 0xB2, 0x6C, 0xB2, 0x6D, 0xB2, 0x6E, /* 0x98-0x9B */
+ 0xB2, 0x6F, 0xB2, 0x70, 0xB2, 0x71, 0xB2, 0x72, /* 0x9C-0x9F */
+ 0xB2, 0x73, 0xC4, 0xE9, 0xB2, 0x74, 0xB2, 0x75, /* 0xA0-0xA3 */
+ 0xB2, 0x76, 0xB2, 0x77, 0xB2, 0x78, 0xB2, 0x79, /* 0xA4-0xA7 */
+ 0xC4, 0xEA, 0xB2, 0x7A, 0xB2, 0x81, 0xB2, 0x82, /* 0xA8-0xAB */
+ 0xB2, 0x83, 0xB2, 0x84, 0xB2, 0x85, 0xB2, 0x86, /* 0xAC-0xAF */
+ 0xC4, 0xEB, 0xB2, 0x87, 0xB2, 0x88, 0xB2, 0x89, /* 0xB0-0xB3 */
+ 0xB2, 0x8A, 0xB2, 0x8B, 0xB2, 0x8C, 0xB2, 0x8D, /* 0xB4-0xB7 */
+ 0xB2, 0x8E, 0xB2, 0x8F, 0xB2, 0x90, 0xB2, 0x91, /* 0xB8-0xBB */
+ 0xB2, 0x92, 0xB2, 0x93, 0xB2, 0x94, 0xB2, 0x95, /* 0xBC-0xBF */
+ 0xB2, 0x96, 0xB2, 0x97, 0xB2, 0x98, 0xB2, 0x99, /* 0xC0-0xC3 */
+ 0xC4, 0xEC, 0xB2, 0x9A, 0xB2, 0x9B, 0xB2, 0x9C, /* 0xC4-0xC7 */
+ 0xB2, 0x9D, 0xB2, 0x9E, 0xB2, 0x9F, 0xB2, 0xA0, /* 0xC8-0xCB */
+ 0xB3, 0x41, 0xB3, 0x42, 0xB3, 0x43, 0xB3, 0x44, /* 0xCC-0xCF */
+ 0xB3, 0x45, 0xB3, 0x46, 0xB3, 0x47, 0xB3, 0x48, /* 0xD0-0xD3 */
+ 0xB3, 0x49, 0xB3, 0x4A, 0xB3, 0x4B, 0xB3, 0x4C, /* 0xD4-0xD7 */
+ 0xB3, 0x4D, 0xB3, 0x4E, 0xB3, 0x4F, 0xB3, 0x50, /* 0xD8-0xDB */
+ 0xB3, 0x51, 0xB3, 0x52, 0xB3, 0x53, 0xB3, 0x54, /* 0xDC-0xDF */
+ 0xC4, 0xED, 0xC4, 0xEE, 0xB3, 0x55, 0xB3, 0x56, /* 0xE0-0xE3 */
+ 0xC4, 0xEF, 0xB3, 0x57, 0xB3, 0x58, 0xB3, 0x59, /* 0xE4-0xE7 */
+ 0xC4, 0xF0, 0xB3, 0x5A, 0xB3, 0x61, 0xB3, 0x62, /* 0xE8-0xEB */
+ 0xB3, 0x63, 0xB3, 0x64, 0xB3, 0x65, 0xB3, 0x66, /* 0xEC-0xEF */
+ 0xC4, 0xF1, 0xC4, 0xF2, 0xB3, 0x67, 0xC4, 0xF3, /* 0xF0-0xF3 */
+ 0xB3, 0x68, 0xC4, 0xF4, 0xB3, 0x69, 0xB3, 0x6A, /* 0xF4-0xF7 */
+ 0xB3, 0x6B, 0xB3, 0x6C, 0xB3, 0x6D, 0xB3, 0x6E, /* 0xF8-0xFB */
+ 0xC4, 0xF5, 0xB3, 0x6F, 0xB3, 0x70, 0xB3, 0x71, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_D0[512] = {
+ 0xC4, 0xF6, 0xB3, 0x72, 0xB3, 0x73, 0xB3, 0x74, /* 0x00-0x03 */
+ 0xC4, 0xF7, 0xB3, 0x75, 0xB3, 0x76, 0xB3, 0x77, /* 0x04-0x07 */
+ 0xB3, 0x78, 0xB3, 0x79, 0xB3, 0x7A, 0xB3, 0x81, /* 0x08-0x0B */
+ 0xB3, 0x82, 0xB3, 0x83, 0xB3, 0x84, 0xB3, 0x85, /* 0x0C-0x0F */
+ 0xB3, 0x86, 0xC4, 0xF8, 0xB3, 0x87, 0xB3, 0x88, /* 0x10-0x13 */
+ 0xB3, 0x89, 0xB3, 0x8A, 0xB3, 0x8B, 0xB3, 0x8C, /* 0x14-0x17 */
+ 0xC4, 0xF9, 0xB3, 0x8D, 0xB3, 0x8E, 0xB3, 0x8F, /* 0x18-0x1B */
+ 0xB3, 0x90, 0xB3, 0x91, 0xB3, 0x92, 0xB3, 0x93, /* 0x1C-0x1F */
+ 0xB3, 0x94, 0xB3, 0x95, 0xB3, 0x96, 0xB3, 0x97, /* 0x20-0x23 */
+ 0xB3, 0x98, 0xB3, 0x99, 0xB3, 0x9A, 0xB3, 0x9B, /* 0x24-0x27 */
+ 0xB3, 0x9C, 0xB3, 0x9D, 0xB3, 0x9E, 0xB3, 0x9F, /* 0x28-0x2B */
+ 0xB3, 0xA0, 0xC4, 0xFA, 0xB4, 0x41, 0xB4, 0x42, /* 0x2C-0x2F */
+ 0xB4, 0x43, 0xB4, 0x44, 0xB4, 0x45, 0xB4, 0x46, /* 0x30-0x33 */
+ 0xC4, 0xFB, 0xC4, 0xFC, 0xB4, 0x47, 0xB4, 0x48, /* 0x34-0x37 */
+ 0xC4, 0xFD, 0xB4, 0x49, 0xB4, 0x4A, 0xB4, 0x4B, /* 0x38-0x3B */
+ 0xC4, 0xFE, 0xB4, 0x4C, 0xB4, 0x4D, 0xB4, 0x4E, /* 0x3C-0x3F */
+ 0xB4, 0x4F, 0xB4, 0x50, 0xB4, 0x51, 0xB4, 0x52, /* 0x40-0x43 */
+ 0xC5, 0xA1, 0xC5, 0xA2, 0xB4, 0x53, 0xC5, 0xA3, /* 0x44-0x47 */
+ 0xB4, 0x54, 0xC5, 0xA4, 0xB4, 0x55, 0xB4, 0x56, /* 0x48-0x4B */
+ 0xB4, 0x57, 0xB4, 0x58, 0xB4, 0x59, 0xB4, 0x5A, /* 0x4C-0x4F */
+ 0xC5, 0xA5, 0xB4, 0x61, 0xB4, 0x62, 0xB4, 0x63, /* 0x50-0x53 */
+ 0xC5, 0xA6, 0xB4, 0x64, 0xB4, 0x65, 0xB4, 0x66, /* 0x54-0x57 */
+ 0xC5, 0xA7, 0xB4, 0x67, 0xB4, 0x68, 0xB4, 0x69, /* 0x58-0x5B */
+ 0xB4, 0x6A, 0xB4, 0x6B, 0xB4, 0x6C, 0xB4, 0x6D, /* 0x5C-0x5F */
+ 0xC5, 0xA8, 0xB4, 0x6E, 0xB4, 0x6F, 0xB4, 0x70, /* 0x60-0x63 */
+ 0xB4, 0x71, 0xB4, 0x72, 0xB4, 0x73, 0xB4, 0x74, /* 0x64-0x67 */
+ 0xB4, 0x75, 0xB4, 0x76, 0xB4, 0x77, 0xB4, 0x78, /* 0x68-0x6B */
+ 0xC5, 0xA9, 0xC5, 0xAA, 0xB4, 0x79, 0xB4, 0x7A, /* 0x6C-0x6F */
+ 0xC5, 0xAB, 0xB4, 0x81, 0xB4, 0x82, 0xB4, 0x83, /* 0x70-0x73 */
+ 0xC5, 0xAC, 0xB4, 0x84, 0xB4, 0x85, 0xB4, 0x86, /* 0x74-0x77 */
+ 0xB4, 0x87, 0xB4, 0x88, 0xB4, 0x89, 0xB4, 0x8A, /* 0x78-0x7B */
+ 0xC5, 0xAD, 0xC5, 0xAE, 0xB4, 0x8B, 0xB4, 0x8C, /* 0x7C-0x7F */
+
+ 0xB4, 0x8D, 0xC5, 0xAF, 0xB4, 0x8E, 0xB4, 0x8F, /* 0x80-0x83 */
+ 0xB4, 0x90, 0xB4, 0x91, 0xB4, 0x92, 0xB4, 0x93, /* 0x84-0x87 */
+ 0xB4, 0x94, 0xB4, 0x95, 0xB4, 0x96, 0xB4, 0x97, /* 0x88-0x8B */
+ 0xB4, 0x98, 0xB4, 0x99, 0xB4, 0x9A, 0xB4, 0x9B, /* 0x8C-0x8F */
+ 0xB4, 0x9C, 0xB4, 0x9D, 0xB4, 0x9E, 0xB4, 0x9F, /* 0x90-0x93 */
+ 0xB4, 0xA0, 0xB5, 0x41, 0xB5, 0x42, 0xB5, 0x43, /* 0x94-0x97 */
+ 0xB5, 0x44, 0xB5, 0x45, 0xB5, 0x46, 0xB5, 0x47, /* 0x98-0x9B */
+ 0xB5, 0x48, 0xB5, 0x49, 0xB5, 0x4A, 0xB5, 0x4B, /* 0x9C-0x9F */
+ 0xB5, 0x4C, 0xB5, 0x4D, 0xB5, 0x4E, 0xB5, 0x4F, /* 0xA0-0xA3 */
+ 0xC5, 0xB0, 0xC5, 0xB1, 0xB5, 0x50, 0xB5, 0x51, /* 0xA4-0xA7 */
+ 0xC5, 0xB2, 0xB5, 0x52, 0xB5, 0x53, 0xB5, 0x54, /* 0xA8-0xAB */
+ 0xC5, 0xB3, 0xB5, 0x55, 0xB5, 0x56, 0xB5, 0x57, /* 0xAC-0xAF */
+ 0xB5, 0x58, 0xB5, 0x59, 0xB5, 0x5A, 0xB5, 0x61, /* 0xB0-0xB3 */
+ 0xC5, 0xB4, 0xC5, 0xB5, 0xB5, 0x62, 0xC5, 0xB6, /* 0xB4-0xB7 */
+ 0xB5, 0x63, 0xC5, 0xB7, 0xB5, 0x64, 0xB5, 0x65, /* 0xB8-0xBB */
+ 0xB5, 0x66, 0xB5, 0x67, 0xB5, 0x68, 0xB5, 0x69, /* 0xBC-0xBF */
+ 0xC5, 0xB8, 0xC5, 0xB9, 0xB5, 0x6A, 0xB5, 0x6B, /* 0xC0-0xC3 */
+ 0xC5, 0xBA, 0xB5, 0x6C, 0xB5, 0x6D, 0xB5, 0x6E, /* 0xC4-0xC7 */
+ 0xC5, 0xBB, 0xC5, 0xBC, 0xB5, 0x6F, 0xB5, 0x70, /* 0xC8-0xCB */
+ 0xB5, 0x71, 0xB5, 0x72, 0xB5, 0x73, 0xB5, 0x74, /* 0xCC-0xCF */
+ 0xC5, 0xBD, 0xC5, 0xBE, 0xB5, 0x75, 0xC5, 0xBF, /* 0xD0-0xD3 */
+ 0xC5, 0xC0, 0xC5, 0xC1, 0xB5, 0x76, 0xB5, 0x77, /* 0xD4-0xD7 */
+ 0xB5, 0x78, 0xB5, 0x79, 0xB5, 0x7A, 0xB5, 0x81, /* 0xD8-0xDB */
+ 0xC5, 0xC2, 0xC5, 0xC3, 0xB5, 0x82, 0xB5, 0x83, /* 0xDC-0xDF */
+ 0xC5, 0xC4, 0xB5, 0x84, 0xB5, 0x85, 0xB5, 0x86, /* 0xE0-0xE3 */
+ 0xC5, 0xC5, 0xB5, 0x87, 0xB5, 0x88, 0xB5, 0x89, /* 0xE4-0xE7 */
+ 0xB5, 0x8A, 0xB5, 0x8B, 0xB5, 0x8C, 0xB5, 0x8D, /* 0xE8-0xEB */
+ 0xC5, 0xC6, 0xC5, 0xC7, 0xB5, 0x8E, 0xC5, 0xC8, /* 0xEC-0xEF */
+ 0xC5, 0xC9, 0xC5, 0xCA, 0xB5, 0x8F, 0xB5, 0x90, /* 0xF0-0xF3 */
+ 0xB5, 0x91, 0xB5, 0x92, 0xB5, 0x93, 0xB5, 0x94, /* 0xF4-0xF7 */
+ 0xC5, 0xCB, 0xB5, 0x95, 0xB5, 0x96, 0xB5, 0x97, /* 0xF8-0xFB */
+ 0xB5, 0x98, 0xB5, 0x99, 0xB5, 0x9A, 0xB5, 0x9B, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_D1[512] = {
+ 0xB5, 0x9C, 0xB5, 0x9D, 0xB5, 0x9E, 0xB5, 0x9F, /* 0x00-0x03 */
+ 0xB5, 0xA0, 0xB6, 0x41, 0xB6, 0x42, 0xB6, 0x43, /* 0x04-0x07 */
+ 0xB6, 0x44, 0xB6, 0x45, 0xB6, 0x46, 0xB6, 0x47, /* 0x08-0x0B */
+ 0xB6, 0x48, 0xC5, 0xCC, 0xB6, 0x49, 0xB6, 0x4A, /* 0x0C-0x0F */
+ 0xB6, 0x4B, 0xB6, 0x4C, 0xB6, 0x4D, 0xB6, 0x4E, /* 0x10-0x13 */
+ 0xB6, 0x4F, 0xB6, 0x50, 0xB6, 0x51, 0xB6, 0x52, /* 0x14-0x17 */
+ 0xB6, 0x53, 0xB6, 0x54, 0xB6, 0x55, 0xB6, 0x56, /* 0x18-0x1B */
+ 0xB6, 0x57, 0xB6, 0x58, 0xB6, 0x59, 0xB6, 0x5A, /* 0x1C-0x1F */
+ 0xB6, 0x61, 0xB6, 0x62, 0xB6, 0x63, 0xB6, 0x64, /* 0x20-0x23 */
+ 0xB6, 0x65, 0xB6, 0x66, 0xB6, 0x67, 0xB6, 0x68, /* 0x24-0x27 */
+ 0xB6, 0x69, 0xB6, 0x6A, 0xB6, 0x6B, 0xB6, 0x6C, /* 0x28-0x2B */
+ 0xB6, 0x6D, 0xB6, 0x6E, 0xB6, 0x6F, 0xB6, 0x70, /* 0x2C-0x2F */
+ 0xC5, 0xCD, 0xC5, 0xCE, 0xB6, 0x71, 0xB6, 0x72, /* 0x30-0x33 */
+ 0xC5, 0xCF, 0xB6, 0x73, 0xB6, 0x74, 0xB6, 0x75, /* 0x34-0x37 */
+ 0xC5, 0xD0, 0xB6, 0x76, 0xC5, 0xD1, 0xB6, 0x77, /* 0x38-0x3B */
+ 0xB6, 0x78, 0xB6, 0x79, 0xB6, 0x7A, 0xB6, 0x81, /* 0x3C-0x3F */
+ 0xC5, 0xD2, 0xC5, 0xD3, 0xB6, 0x82, 0xC5, 0xD4, /* 0x40-0x43 */
+ 0xC5, 0xD5, 0xC5, 0xD6, 0xB6, 0x83, 0xB6, 0x84, /* 0x44-0x47 */
+ 0xB6, 0x85, 0xB6, 0x86, 0xB6, 0x87, 0xB6, 0x88, /* 0x48-0x4B */
+ 0xC5, 0xD7, 0xC5, 0xD8, 0xB6, 0x89, 0xB6, 0x8A, /* 0x4C-0x4F */
+ 0xC5, 0xD9, 0xB6, 0x8B, 0xB6, 0x8C, 0xB6, 0x8D, /* 0x50-0x53 */
+ 0xC5, 0xDA, 0xB6, 0x8E, 0xB6, 0x8F, 0xB6, 0x90, /* 0x54-0x57 */
+ 0xB6, 0x91, 0xB6, 0x92, 0xB6, 0x93, 0xB6, 0x94, /* 0x58-0x5B */
+ 0xC5, 0xDB, 0xC5, 0xDC, 0xB6, 0x95, 0xC5, 0xDD, /* 0x5C-0x5F */
+ 0xB6, 0x96, 0xC5, 0xDE, 0xB6, 0x97, 0xB6, 0x98, /* 0x60-0x63 */
+ 0xB6, 0x99, 0xB6, 0x9A, 0xB6, 0x9B, 0xB6, 0x9C, /* 0x64-0x67 */
+ 0xC5, 0xDF, 0xB6, 0x9D, 0xB6, 0x9E, 0xB6, 0x9F, /* 0x68-0x6B */
+ 0xC5, 0xE0, 0xB6, 0xA0, 0xB7, 0x41, 0xB7, 0x42, /* 0x6C-0x6F */
+ 0xB7, 0x43, 0xB7, 0x44, 0xB7, 0x45, 0xB7, 0x46, /* 0x70-0x73 */
+ 0xB7, 0x47, 0xB7, 0x48, 0xB7, 0x49, 0xB7, 0x4A, /* 0x74-0x77 */
+ 0xB7, 0x4B, 0xB7, 0x4C, 0xB7, 0x4D, 0xB7, 0x4E, /* 0x78-0x7B */
+ 0xC5, 0xE1, 0xB7, 0x4F, 0xB7, 0x50, 0xB7, 0x51, /* 0x7C-0x7F */
+
+ 0xB7, 0x52, 0xB7, 0x53, 0xB7, 0x54, 0xB7, 0x55, /* 0x80-0x83 */
+ 0xC5, 0xE2, 0xB7, 0x56, 0xB7, 0x57, 0xB7, 0x58, /* 0x84-0x87 */
+ 0xC5, 0xE3, 0xB7, 0x59, 0xB7, 0x5A, 0xB7, 0x61, /* 0x88-0x8B */
+ 0xB7, 0x62, 0xB7, 0x63, 0xB7, 0x64, 0xB7, 0x65, /* 0x8C-0x8F */
+ 0xB7, 0x66, 0xB7, 0x67, 0xB7, 0x68, 0xB7, 0x69, /* 0x90-0x93 */
+ 0xB7, 0x6A, 0xB7, 0x6B, 0xB7, 0x6C, 0xB7, 0x6D, /* 0x94-0x97 */
+ 0xB7, 0x6E, 0xB7, 0x6F, 0xB7, 0x70, 0xB7, 0x71, /* 0x98-0x9B */
+ 0xB7, 0x72, 0xB7, 0x73, 0xB7, 0x74, 0xB7, 0x75, /* 0x9C-0x9F */
+ 0xC5, 0xE4, 0xC5, 0xE5, 0xB7, 0x76, 0xB7, 0x77, /* 0xA0-0xA3 */
+ 0xC5, 0xE6, 0xB7, 0x78, 0xB7, 0x79, 0xB7, 0x7A, /* 0xA4-0xA7 */
+ 0xC5, 0xE7, 0xB7, 0x81, 0xB7, 0x82, 0xB7, 0x83, /* 0xA8-0xAB */
+ 0xB7, 0x84, 0xB7, 0x85, 0xB7, 0x86, 0xB7, 0x87, /* 0xAC-0xAF */
+ 0xC5, 0xE8, 0xC5, 0xE9, 0xB7, 0x88, 0xC5, 0xEA, /* 0xB0-0xB3 */
+ 0xB7, 0x89, 0xC5, 0xEB, 0xB7, 0x8A, 0xB7, 0x8B, /* 0xB4-0xB7 */
+ 0xB7, 0x8C, 0xB7, 0x8D, 0xC5, 0xEC, 0xB7, 0x8E, /* 0xB8-0xBB */
+ 0xC5, 0xED, 0xB7, 0x8F, 0xB7, 0x90, 0xB7, 0x91, /* 0xBC-0xBF */
+ 0xC5, 0xEE, 0xB7, 0x92, 0xB7, 0x93, 0xB7, 0x94, /* 0xC0-0xC3 */
+ 0xB7, 0x95, 0xB7, 0x96, 0xB7, 0x97, 0xB7, 0x98, /* 0xC4-0xC7 */
+ 0xB7, 0x99, 0xB7, 0x9A, 0xB7, 0x9B, 0xB7, 0x9C, /* 0xC8-0xCB */
+ 0xB7, 0x9D, 0xB7, 0x9E, 0xB7, 0x9F, 0xB7, 0xA0, /* 0xCC-0xCF */
+ 0xB8, 0x41, 0xB8, 0x42, 0xB8, 0x43, 0xB8, 0x44, /* 0xD0-0xD3 */
+ 0xB8, 0x45, 0xB8, 0x46, 0xB8, 0x47, 0xB8, 0x48, /* 0xD4-0xD7 */
+ 0xC5, 0xEF, 0xB8, 0x49, 0xB8, 0x4A, 0xB8, 0x4B, /* 0xD8-0xDB */
+ 0xB8, 0x4C, 0xB8, 0x4D, 0xB8, 0x4E, 0xB8, 0x4F, /* 0xDC-0xDF */
+ 0xB8, 0x50, 0xB8, 0x51, 0xB8, 0x52, 0xB8, 0x53, /* 0xE0-0xE3 */
+ 0xB8, 0x54, 0xB8, 0x55, 0xB8, 0x56, 0xB8, 0x57, /* 0xE4-0xE7 */
+ 0xB8, 0x58, 0xB8, 0x59, 0xB8, 0x5A, 0xB8, 0x61, /* 0xE8-0xEB */
+ 0xB8, 0x62, 0xB8, 0x63, 0xB8, 0x64, 0xB8, 0x65, /* 0xEC-0xEF */
+ 0xB8, 0x66, 0xB8, 0x67, 0xB8, 0x68, 0xB8, 0x69, /* 0xF0-0xF3 */
+ 0xC5, 0xF0, 0xB8, 0x6A, 0xB8, 0x6B, 0xB8, 0x6C, /* 0xF4-0xF7 */
+ 0xC5, 0xF1, 0xB8, 0x6D, 0xB8, 0x6E, 0xB8, 0x6F, /* 0xF8-0xFB */
+ 0xB8, 0x70, 0xB8, 0x71, 0xB8, 0x72, 0xB8, 0x73, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_D2[512] = {
+ 0xB8, 0x74, 0xB8, 0x75, 0xB8, 0x76, 0xB8, 0x77, /* 0x00-0x03 */
+ 0xB8, 0x78, 0xB8, 0x79, 0xB8, 0x7A, 0xC5, 0xF2, /* 0x04-0x07 */
+ 0xB8, 0x81, 0xC5, 0xF3, 0xB8, 0x82, 0xB8, 0x83, /* 0x08-0x0B */
+ 0xB8, 0x84, 0xB8, 0x85, 0xB8, 0x86, 0xB8, 0x87, /* 0x0C-0x0F */
+ 0xC5, 0xF4, 0xB8, 0x88, 0xB8, 0x89, 0xB8, 0x8A, /* 0x10-0x13 */
+ 0xB8, 0x8B, 0xB8, 0x8C, 0xB8, 0x8D, 0xB8, 0x8E, /* 0x14-0x17 */
+ 0xB8, 0x8F, 0xB8, 0x90, 0xB8, 0x91, 0xB8, 0x92, /* 0x18-0x1B */
+ 0xB8, 0x93, 0xB8, 0x94, 0xB8, 0x95, 0xB8, 0x96, /* 0x1C-0x1F */
+ 0xB8, 0x97, 0xB8, 0x98, 0xB8, 0x99, 0xB8, 0x9A, /* 0x20-0x23 */
+ 0xB8, 0x9B, 0xB8, 0x9C, 0xB8, 0x9D, 0xB8, 0x9E, /* 0x24-0x27 */
+ 0xB8, 0x9F, 0xB8, 0xA0, 0xB9, 0x41, 0xB9, 0x42, /* 0x28-0x2B */
+ 0xC5, 0xF5, 0xC5, 0xF6, 0xB9, 0x43, 0xB9, 0x44, /* 0x2C-0x2F */
+ 0xC5, 0xF7, 0xB9, 0x45, 0xB9, 0x46, 0xB9, 0x47, /* 0x30-0x33 */
+ 0xC5, 0xF8, 0xB9, 0x48, 0xB9, 0x49, 0xB9, 0x4A, /* 0x34-0x37 */
+ 0xB9, 0x4B, 0xB9, 0x4C, 0xB9, 0x4D, 0xB9, 0x4E, /* 0x38-0x3B */
+ 0xC5, 0xF9, 0xC5, 0xFA, 0xB9, 0x4F, 0xC5, 0xFB, /* 0x3C-0x3F */
+ 0xB9, 0x50, 0xC5, 0xFC, 0xB9, 0x51, 0xB9, 0x52, /* 0x40-0x43 */
+ 0xB9, 0x53, 0xB9, 0x54, 0xB9, 0x55, 0xB9, 0x56, /* 0x44-0x47 */
+ 0xC5, 0xFD, 0xB9, 0x57, 0xB9, 0x58, 0xB9, 0x59, /* 0x48-0x4B */
+ 0xB9, 0x5A, 0xB9, 0x61, 0xB9, 0x62, 0xB9, 0x63, /* 0x4C-0x4F */
+ 0xB9, 0x64, 0xB9, 0x65, 0xB9, 0x66, 0xB9, 0x67, /* 0x50-0x53 */
+ 0xB9, 0x68, 0xB9, 0x69, 0xB9, 0x6A, 0xB9, 0x6B, /* 0x54-0x57 */
+ 0xB9, 0x6C, 0xB9, 0x6D, 0xB9, 0x6E, 0xB9, 0x6F, /* 0x58-0x5B */
+ 0xC5, 0xFE, 0xB9, 0x70, 0xB9, 0x71, 0xB9, 0x72, /* 0x5C-0x5F */
+ 0xB9, 0x73, 0xB9, 0x74, 0xB9, 0x75, 0xB9, 0x76, /* 0x60-0x63 */
+ 0xC6, 0xA1, 0xB9, 0x77, 0xB9, 0x78, 0xB9, 0x79, /* 0x64-0x67 */
+ 0xB9, 0x7A, 0xB9, 0x81, 0xB9, 0x82, 0xB9, 0x83, /* 0x68-0x6B */
+ 0xB9, 0x84, 0xB9, 0x85, 0xB9, 0x86, 0xB9, 0x87, /* 0x6C-0x6F */
+ 0xB9, 0x88, 0xB9, 0x89, 0xB9, 0x8A, 0xB9, 0x8B, /* 0x70-0x73 */
+ 0xB9, 0x8C, 0xB9, 0x8D, 0xB9, 0x8E, 0xB9, 0x8F, /* 0x74-0x77 */
+ 0xB9, 0x90, 0xB9, 0x91, 0xB9, 0x92, 0xB9, 0x93, /* 0x78-0x7B */
+ 0xB9, 0x94, 0xB9, 0x95, 0xB9, 0x96, 0xB9, 0x97, /* 0x7C-0x7F */
+
+ 0xC6, 0xA2, 0xC6, 0xA3, 0xB9, 0x98, 0xB9, 0x99, /* 0x80-0x83 */
+ 0xC6, 0xA4, 0xB9, 0x9A, 0xB9, 0x9B, 0xB9, 0x9C, /* 0x84-0x87 */
+ 0xC6, 0xA5, 0xB9, 0x9D, 0xB9, 0x9E, 0xB9, 0x9F, /* 0x88-0x8B */
+ 0xB9, 0xA0, 0xBA, 0x41, 0xBA, 0x42, 0xBA, 0x43, /* 0x8C-0x8F */
+ 0xC6, 0xA6, 0xC6, 0xA7, 0xBA, 0x44, 0xBA, 0x45, /* 0x90-0x93 */
+ 0xBA, 0x46, 0xC6, 0xA8, 0xBA, 0x47, 0xBA, 0x48, /* 0x94-0x97 */
+ 0xBA, 0x49, 0xBA, 0x4A, 0xBA, 0x4B, 0xBA, 0x4C, /* 0x98-0x9B */
+ 0xC6, 0xA9, 0xBA, 0x4D, 0xBA, 0x4E, 0xBA, 0x4F, /* 0x9C-0x9F */
+ 0xC6, 0xAA, 0xBA, 0x50, 0xBA, 0x51, 0xBA, 0x52, /* 0xA0-0xA3 */
+ 0xC6, 0xAB, 0xBA, 0x53, 0xBA, 0x54, 0xBA, 0x55, /* 0xA4-0xA7 */
+ 0xBA, 0x56, 0xBA, 0x57, 0xBA, 0x58, 0xBA, 0x59, /* 0xA8-0xAB */
+ 0xC6, 0xAC, 0xBA, 0x5A, 0xBA, 0x61, 0xBA, 0x62, /* 0xAC-0xAF */
+ 0xBA, 0x63, 0xC6, 0xAD, 0xBA, 0x64, 0xBA, 0x65, /* 0xB0-0xB3 */
+ 0xBA, 0x66, 0xBA, 0x67, 0xBA, 0x68, 0xBA, 0x69, /* 0xB4-0xB7 */
+ 0xC6, 0xAE, 0xC6, 0xAF, 0xBA, 0x6A, 0xBA, 0x6B, /* 0xB8-0xBB */
+ 0xC6, 0xB0, 0xBA, 0x6C, 0xBA, 0x6D, 0xC6, 0xB1, /* 0xBC-0xBF */
+ 0xC6, 0xB2, 0xBA, 0x6E, 0xC6, 0xB3, 0xBA, 0x6F, /* 0xC0-0xC3 */
+ 0xBA, 0x70, 0xBA, 0x71, 0xBA, 0x72, 0xBA, 0x73, /* 0xC4-0xC7 */
+ 0xC6, 0xB4, 0xC6, 0xB5, 0xBA, 0x74, 0xC6, 0xB6, /* 0xC8-0xCB */
+ 0xBA, 0x75, 0xBA, 0x76, 0xBA, 0x77, 0xBA, 0x78, /* 0xCC-0xCF */
+ 0xBA, 0x79, 0xBA, 0x7A, 0xBA, 0x81, 0xBA, 0x82, /* 0xD0-0xD3 */
+ 0xC6, 0xB7, 0xBA, 0x83, 0xBA, 0x84, 0xBA, 0x85, /* 0xD4-0xD7 */
+ 0xC6, 0xB8, 0xBA, 0x86, 0xBA, 0x87, 0xBA, 0x88, /* 0xD8-0xDB */
+ 0xC6, 0xB9, 0xBA, 0x89, 0xBA, 0x8A, 0xBA, 0x8B, /* 0xDC-0xDF */
+ 0xBA, 0x8C, 0xBA, 0x8D, 0xBA, 0x8E, 0xBA, 0x8F, /* 0xE0-0xE3 */
+ 0xC6, 0xBA, 0xC6, 0xBB, 0xBA, 0x90, 0xBA, 0x91, /* 0xE4-0xE7 */
+ 0xBA, 0x92, 0xBA, 0x93, 0xBA, 0x94, 0xBA, 0x95, /* 0xE8-0xEB */
+ 0xBA, 0x96, 0xBA, 0x97, 0xBA, 0x98, 0xBA, 0x99, /* 0xEC-0xEF */
+ 0xC6, 0xBC, 0xC6, 0xBD, 0xBA, 0x9A, 0xBA, 0x9B, /* 0xF0-0xF3 */
+ 0xC6, 0xBE, 0xBA, 0x9C, 0xBA, 0x9D, 0xBA, 0x9E, /* 0xF4-0xF7 */
+ 0xC6, 0xBF, 0xBA, 0x9F, 0xBA, 0xA0, 0xBB, 0x41, /* 0xF8-0xFB */
+ 0xBB, 0x42, 0xBB, 0x43, 0xBB, 0x44, 0xBB, 0x45, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_D3[512] = {
+ 0xC6, 0xC0, 0xC6, 0xC1, 0xBB, 0x46, 0xC6, 0xC2, /* 0x00-0x03 */
+ 0xBB, 0x47, 0xC6, 0xC3, 0xBB, 0x48, 0xBB, 0x49, /* 0x04-0x07 */
+ 0xBB, 0x4A, 0xBB, 0x4B, 0xBB, 0x4C, 0xBB, 0x4D, /* 0x08-0x0B */
+ 0xC6, 0xC4, 0xC6, 0xC5, 0xC6, 0xC6, 0xBB, 0x4E, /* 0x0C-0x0F */
+ 0xC6, 0xC7, 0xBB, 0x4F, 0xBB, 0x50, 0xBB, 0x51, /* 0x10-0x13 */
+ 0xC6, 0xC8, 0xBB, 0x52, 0xC6, 0xC9, 0xBB, 0x53, /* 0x14-0x17 */
+ 0xBB, 0x54, 0xBB, 0x55, 0xBB, 0x56, 0xBB, 0x57, /* 0x18-0x1B */
+ 0xC6, 0xCA, 0xC6, 0xCB, 0xBB, 0x58, 0xC6, 0xCC, /* 0x1C-0x1F */
+ 0xC6, 0xCD, 0xC6, 0xCE, 0xBB, 0x59, 0xBB, 0x5A, /* 0x20-0x23 */
+ 0xBB, 0x61, 0xC6, 0xCF, 0xBB, 0x62, 0xBB, 0x63, /* 0x24-0x27 */
+ 0xC6, 0xD0, 0xC6, 0xD1, 0xBB, 0x64, 0xBB, 0x65, /* 0x28-0x2B */
+ 0xC6, 0xD2, 0xBB, 0x66, 0xBB, 0x67, 0xBB, 0x68, /* 0x2C-0x2F */
+ 0xC6, 0xD3, 0xBB, 0x69, 0xBB, 0x6A, 0xBB, 0x6B, /* 0x30-0x33 */
+ 0xBB, 0x6C, 0xBB, 0x6D, 0xBB, 0x6E, 0xBB, 0x6F, /* 0x34-0x37 */
+ 0xC6, 0xD4, 0xC6, 0xD5, 0xBB, 0x70, 0xC6, 0xD6, /* 0x38-0x3B */
+ 0xC6, 0xD7, 0xC6, 0xD8, 0xBB, 0x71, 0xBB, 0x72, /* 0x3C-0x3F */
+ 0xBB, 0x73, 0xBB, 0x74, 0xBB, 0x75, 0xBB, 0x76, /* 0x40-0x43 */
+ 0xC6, 0xD9, 0xC6, 0xDA, 0xBB, 0x77, 0xBB, 0x78, /* 0x44-0x47 */
+ 0xBB, 0x79, 0xBB, 0x7A, 0xBB, 0x81, 0xBB, 0x82, /* 0x48-0x4B */
+ 0xBB, 0x83, 0xBB, 0x84, 0xBB, 0x85, 0xBB, 0x86, /* 0x4C-0x4F */
+ 0xBB, 0x87, 0xBB, 0x88, 0xBB, 0x89, 0xBB, 0x8A, /* 0x50-0x53 */
+ 0xBB, 0x8B, 0xBB, 0x8C, 0xBB, 0x8D, 0xBB, 0x8E, /* 0x54-0x57 */
+ 0xBB, 0x8F, 0xBB, 0x90, 0xBB, 0x91, 0xBB, 0x92, /* 0x58-0x5B */
+ 0xBB, 0x93, 0xBB, 0x94, 0xBB, 0x95, 0xBB, 0x96, /* 0x5C-0x5F */
+ 0xBB, 0x97, 0xBB, 0x98, 0xBB, 0x99, 0xBB, 0x9A, /* 0x60-0x63 */
+ 0xBB, 0x9B, 0xBB, 0x9C, 0xBB, 0x9D, 0xBB, 0x9E, /* 0x64-0x67 */
+ 0xBB, 0x9F, 0xBB, 0xA0, 0xBC, 0x41, 0xBC, 0x42, /* 0x68-0x6B */
+ 0xBC, 0x43, 0xBC, 0x44, 0xBC, 0x45, 0xBC, 0x46, /* 0x6C-0x6F */
+ 0xBC, 0x47, 0xBC, 0x48, 0xBC, 0x49, 0xBC, 0x4A, /* 0x70-0x73 */
+ 0xBC, 0x4B, 0xBC, 0x4C, 0xBC, 0x4D, 0xBC, 0x4E, /* 0x74-0x77 */
+ 0xBC, 0x4F, 0xBC, 0x50, 0xBC, 0x51, 0xBC, 0x52, /* 0x78-0x7B */
+ 0xC6, 0xDB, 0xC6, 0xDC, 0xBC, 0x53, 0xBC, 0x54, /* 0x7C-0x7F */
+
+ 0xC6, 0xDD, 0xBC, 0x55, 0xBC, 0x56, 0xBC, 0x57, /* 0x80-0x83 */
+ 0xC6, 0xDE, 0xBC, 0x58, 0xBC, 0x59, 0xBC, 0x5A, /* 0x84-0x87 */
+ 0xBC, 0x61, 0xBC, 0x62, 0xBC, 0x63, 0xBC, 0x64, /* 0x88-0x8B */
+ 0xC6, 0xDF, 0xC6, 0xE0, 0xBC, 0x65, 0xC6, 0xE1, /* 0x8C-0x8F */
+ 0xC6, 0xE2, 0xC6, 0xE3, 0xBC, 0x66, 0xBC, 0x67, /* 0x90-0x93 */
+ 0xBC, 0x68, 0xBC, 0x69, 0xBC, 0x6A, 0xBC, 0x6B, /* 0x94-0x97 */
+ 0xC6, 0xE4, 0xC6, 0xE5, 0xBC, 0x6C, 0xBC, 0x6D, /* 0x98-0x9B */
+ 0xC6, 0xE6, 0xBC, 0x6E, 0xBC, 0x6F, 0xBC, 0x70, /* 0x9C-0x9F */
+ 0xC6, 0xE7, 0xBC, 0x71, 0xBC, 0x72, 0xBC, 0x73, /* 0xA0-0xA3 */
+ 0xBC, 0x74, 0xBC, 0x75, 0xBC, 0x76, 0xBC, 0x77, /* 0xA4-0xA7 */
+ 0xC6, 0xE8, 0xC6, 0xE9, 0xBC, 0x78, 0xC6, 0xEA, /* 0xA8-0xAB */
+ 0xBC, 0x79, 0xC6, 0xEB, 0xBC, 0x7A, 0xBC, 0x81, /* 0xAC-0xAF */
+ 0xBC, 0x82, 0xBC, 0x83, 0xBC, 0x84, 0xBC, 0x85, /* 0xB0-0xB3 */
+ 0xC6, 0xEC, 0xBC, 0x86, 0xBC, 0x87, 0xBC, 0x88, /* 0xB4-0xB7 */
+ 0xC6, 0xED, 0xBC, 0x89, 0xBC, 0x8A, 0xBC, 0x8B, /* 0xB8-0xBB */
+ 0xC6, 0xEE, 0xBC, 0x8C, 0xBC, 0x8D, 0xBC, 0x8E, /* 0xBC-0xBF */
+ 0xBC, 0x8F, 0xBC, 0x90, 0xBC, 0x91, 0xBC, 0x92, /* 0xC0-0xC3 */
+ 0xC6, 0xEF, 0xC6, 0xF0, 0xBC, 0x93, 0xBC, 0x94, /* 0xC4-0xC7 */
+ 0xC6, 0xF1, 0xC6, 0xF2, 0xBC, 0x95, 0xBC, 0x96, /* 0xC8-0xCB */
+ 0xBC, 0x97, 0xBC, 0x98, 0xBC, 0x99, 0xBC, 0x9A, /* 0xCC-0xCF */
+ 0xC6, 0xF3, 0xBC, 0x9B, 0xBC, 0x9C, 0xBC, 0x9D, /* 0xD0-0xD3 */
+ 0xBC, 0x9E, 0xBC, 0x9F, 0xBC, 0xA0, 0xBD, 0x41, /* 0xD4-0xD7 */
+ 0xC6, 0xF4, 0xBD, 0x42, 0xBD, 0x43, 0xBD, 0x44, /* 0xD8-0xDB */
+ 0xBD, 0x45, 0xBD, 0x46, 0xBD, 0x47, 0xBD, 0x48, /* 0xDC-0xDF */
+ 0xBD, 0x49, 0xC6, 0xF5, 0xBD, 0x4A, 0xC6, 0xF6, /* 0xE0-0xE3 */
+ 0xBD, 0x4B, 0xBD, 0x4C, 0xBD, 0x4D, 0xBD, 0x4E, /* 0xE4-0xE7 */
+ 0xBD, 0x4F, 0xBD, 0x50, 0xBD, 0x51, 0xBD, 0x52, /* 0xE8-0xEB */
+ 0xC6, 0xF7, 0xC6, 0xF8, 0xBD, 0x53, 0xBD, 0x54, /* 0xEC-0xEF */
+ 0xC6, 0xF9, 0xBD, 0x55, 0xBD, 0x56, 0xBD, 0x57, /* 0xF0-0xF3 */
+ 0xC6, 0xFA, 0xBD, 0x58, 0xBD, 0x59, 0xBD, 0x5A, /* 0xF4-0xF7 */
+ 0xBD, 0x61, 0xBD, 0x62, 0xBD, 0x63, 0xBD, 0x64, /* 0xF8-0xFB */
+ 0xC6, 0xFB, 0xC6, 0xFC, 0xBD, 0x65, 0xC6, 0xFD, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_D4[512] = {
+ 0xBD, 0x66, 0xC6, 0xFE, 0xBD, 0x67, 0xBD, 0x68, /* 0x00-0x03 */
+ 0xBD, 0x69, 0xBD, 0x6A, 0xBD, 0x6B, 0xBD, 0x6C, /* 0x04-0x07 */
+ 0xC7, 0xA1, 0xBD, 0x6D, 0xBD, 0x6E, 0xBD, 0x6F, /* 0x08-0x0B */
+ 0xBD, 0x70, 0xBD, 0x71, 0xBD, 0x72, 0xBD, 0x73, /* 0x0C-0x0F */
+ 0xBD, 0x74, 0xBD, 0x75, 0xBD, 0x76, 0xBD, 0x77, /* 0x10-0x13 */
+ 0xBD, 0x78, 0xBD, 0x79, 0xBD, 0x7A, 0xBD, 0x81, /* 0x14-0x17 */
+ 0xBD, 0x82, 0xBD, 0x83, 0xBD, 0x84, 0xBD, 0x85, /* 0x18-0x1B */
+ 0xBD, 0x86, 0xC7, 0xA2, 0xBD, 0x87, 0xBD, 0x88, /* 0x1C-0x1F */
+ 0xBD, 0x89, 0xBD, 0x8A, 0xBD, 0x8B, 0xBD, 0x8C, /* 0x20-0x23 */
+ 0xBD, 0x8D, 0xBD, 0x8E, 0xBD, 0x8F, 0xBD, 0x90, /* 0x24-0x27 */
+ 0xBD, 0x91, 0xBD, 0x92, 0xBD, 0x93, 0xBD, 0x94, /* 0x28-0x2B */
+ 0xBD, 0x95, 0xBD, 0x96, 0xBD, 0x97, 0xBD, 0x98, /* 0x2C-0x2F */
+ 0xBD, 0x99, 0xBD, 0x9A, 0xBD, 0x9B, 0xBD, 0x9C, /* 0x30-0x33 */
+ 0xBD, 0x9D, 0xBD, 0x9E, 0xBD, 0x9F, 0xBD, 0xA0, /* 0x34-0x37 */
+ 0xBE, 0x41, 0xBE, 0x42, 0xBE, 0x43, 0xBE, 0x44, /* 0x38-0x3B */
+ 0xBE, 0x45, 0xBE, 0x46, 0xBE, 0x47, 0xBE, 0x48, /* 0x3C-0x3F */
+ 0xC7, 0xA3, 0xBE, 0x49, 0xBE, 0x4A, 0xBE, 0x4B, /* 0x40-0x43 */
+ 0xC7, 0xA4, 0xBE, 0x4C, 0xBE, 0x4D, 0xBE, 0x4E, /* 0x44-0x47 */
+ 0xBE, 0x4F, 0xBE, 0x50, 0xBE, 0x51, 0xBE, 0x52, /* 0x48-0x4B */
+ 0xBE, 0x53, 0xBE, 0x54, 0xBE, 0x55, 0xBE, 0x56, /* 0x4C-0x4F */
+ 0xBE, 0x57, 0xBE, 0x58, 0xBE, 0x59, 0xBE, 0x5A, /* 0x50-0x53 */
+ 0xBE, 0x61, 0xBE, 0x62, 0xBE, 0x63, 0xBE, 0x64, /* 0x54-0x57 */
+ 0xBE, 0x65, 0xBE, 0x66, 0xBE, 0x67, 0xBE, 0x68, /* 0x58-0x5B */
+ 0xC7, 0xA5, 0xBE, 0x69, 0xBE, 0x6A, 0xBE, 0x6B, /* 0x5C-0x5F */
+ 0xC7, 0xA6, 0xBE, 0x6C, 0xBE, 0x6D, 0xBE, 0x6E, /* 0x60-0x63 */
+ 0xC7, 0xA7, 0xBE, 0x6F, 0xBE, 0x70, 0xBE, 0x71, /* 0x64-0x67 */
+ 0xBE, 0x72, 0xBE, 0x73, 0xBE, 0x74, 0xBE, 0x75, /* 0x68-0x6B */
+ 0xBE, 0x76, 0xC7, 0xA8, 0xBE, 0x77, 0xC7, 0xA9, /* 0x6C-0x6F */
+ 0xBE, 0x78, 0xBE, 0x79, 0xBE, 0x7A, 0xBE, 0x81, /* 0x70-0x73 */
+ 0xBE, 0x82, 0xBE, 0x83, 0xBE, 0x84, 0xBE, 0x85, /* 0x74-0x77 */
+ 0xC7, 0xAA, 0xC7, 0xAB, 0xBE, 0x86, 0xBE, 0x87, /* 0x78-0x7B */
+ 0xC7, 0xAC, 0xBE, 0x88, 0xBE, 0x89, 0xC7, 0xAD, /* 0x7C-0x7F */
+
+ 0xC7, 0xAE, 0xBE, 0x8A, 0xC7, 0xAF, 0xBE, 0x8B, /* 0x80-0x83 */
+ 0xBE, 0x8C, 0xBE, 0x8D, 0xBE, 0x8E, 0xBE, 0x8F, /* 0x84-0x87 */
+ 0xC7, 0xB0, 0xC7, 0xB1, 0xBE, 0x90, 0xC7, 0xB2, /* 0x88-0x8B */
+ 0xBE, 0x91, 0xC7, 0xB3, 0xBE, 0x92, 0xBE, 0x93, /* 0x8C-0x8F */
+ 0xBE, 0x94, 0xBE, 0x95, 0xBE, 0x96, 0xBE, 0x97, /* 0x90-0x93 */
+ 0xC7, 0xB4, 0xBE, 0x98, 0xBE, 0x99, 0xBE, 0x9A, /* 0x94-0x97 */
+ 0xBE, 0x9B, 0xBE, 0x9C, 0xBE, 0x9D, 0xBE, 0x9E, /* 0x98-0x9B */
+ 0xBE, 0x9F, 0xBE, 0xA0, 0xBF, 0x41, 0xBF, 0x42, /* 0x9C-0x9F */
+ 0xBF, 0x43, 0xBF, 0x44, 0xBF, 0x45, 0xBF, 0x46, /* 0xA0-0xA3 */
+ 0xBF, 0x47, 0xBF, 0x48, 0xBF, 0x49, 0xBF, 0x4A, /* 0xA4-0xA7 */
+ 0xBF, 0x4B, 0xC7, 0xB5, 0xBF, 0x4C, 0xBF, 0x4D, /* 0xA8-0xAB */
+ 0xBF, 0x4E, 0xBF, 0x4F, 0xBF, 0x50, 0xBF, 0x51, /* 0xAC-0xAF */
+ 0xBF, 0x52, 0xBF, 0x53, 0xBF, 0x54, 0xBF, 0x55, /* 0xB0-0xB3 */
+ 0xBF, 0x56, 0xBF, 0x57, 0xBF, 0x58, 0xBF, 0x59, /* 0xB4-0xB7 */
+ 0xBF, 0x5A, 0xBF, 0x61, 0xBF, 0x62, 0xBF, 0x63, /* 0xB8-0xBB */
+ 0xBF, 0x64, 0xBF, 0x65, 0xBF, 0x66, 0xBF, 0x67, /* 0xBC-0xBF */
+ 0xBF, 0x68, 0xBF, 0x69, 0xBF, 0x6A, 0xBF, 0x6B, /* 0xC0-0xC3 */
+ 0xBF, 0x6C, 0xBF, 0x6D, 0xBF, 0x6E, 0xBF, 0x6F, /* 0xC4-0xC7 */
+ 0xBF, 0x70, 0xBF, 0x71, 0xBF, 0x72, 0xBF, 0x73, /* 0xC8-0xCB */
+ 0xC7, 0xB6, 0xBF, 0x74, 0xBF, 0x75, 0xBF, 0x76, /* 0xCC-0xCF */
+ 0xC7, 0xB7, 0xBF, 0x77, 0xBF, 0x78, 0xBF, 0x79, /* 0xD0-0xD3 */
+ 0xC7, 0xB8, 0xBF, 0x7A, 0xBF, 0x81, 0xBF, 0x82, /* 0xD4-0xD7 */
+ 0xBF, 0x83, 0xBF, 0x84, 0xBF, 0x85, 0xBF, 0x86, /* 0xD8-0xDB */
+ 0xC7, 0xB9, 0xBF, 0x87, 0xBF, 0x88, 0xC7, 0xBA, /* 0xDC-0xDF */
+ 0xBF, 0x89, 0xBF, 0x8A, 0xBF, 0x8B, 0xBF, 0x8C, /* 0xE0-0xE3 */
+ 0xBF, 0x8D, 0xBF, 0x8E, 0xBF, 0x8F, 0xBF, 0x90, /* 0xE4-0xE7 */
+ 0xC7, 0xBB, 0xBF, 0x91, 0xBF, 0x92, 0xBF, 0x93, /* 0xE8-0xEB */
+ 0xC7, 0xBC, 0xBF, 0x94, 0xBF, 0x95, 0xBF, 0x96, /* 0xEC-0xEF */
+ 0xC7, 0xBD, 0xBF, 0x97, 0xBF, 0x98, 0xBF, 0x99, /* 0xF0-0xF3 */
+ 0xBF, 0x9A, 0xBF, 0x9B, 0xBF, 0x9C, 0xBF, 0x9D, /* 0xF4-0xF7 */
+ 0xC7, 0xBE, 0xBF, 0x9E, 0xBF, 0x9F, 0xC7, 0xBF, /* 0xF8-0xFB */
+ 0xBF, 0xA0, 0xC7, 0xC0, 0xC0, 0x41, 0xC0, 0x42, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_D5[512] = {
+ 0xC0, 0x43, 0xC0, 0x44, 0xC0, 0x45, 0xC0, 0x46, /* 0x00-0x03 */
+ 0xC7, 0xC1, 0xC0, 0x47, 0xC0, 0x48, 0xC0, 0x49, /* 0x04-0x07 */
+ 0xC7, 0xC2, 0xC0, 0x4A, 0xC0, 0x4B, 0xC0, 0x4C, /* 0x08-0x0B */
+ 0xC7, 0xC3, 0xC0, 0x4D, 0xC0, 0x4E, 0xC0, 0x4F, /* 0x0C-0x0F */
+ 0xC0, 0x50, 0xC0, 0x51, 0xC0, 0x52, 0xC0, 0x53, /* 0x10-0x13 */
+ 0xC7, 0xC4, 0xC7, 0xC5, 0xC0, 0x54, 0xC7, 0xC6, /* 0x14-0x17 */
+ 0xC0, 0x55, 0xC0, 0x56, 0xC0, 0x57, 0xC0, 0x58, /* 0x18-0x1B */
+ 0xC0, 0x59, 0xC0, 0x5A, 0xC0, 0x61, 0xC0, 0x62, /* 0x1C-0x1F */
+ 0xC0, 0x63, 0xC0, 0x64, 0xC0, 0x65, 0xC0, 0x66, /* 0x20-0x23 */
+ 0xC0, 0x67, 0xC0, 0x68, 0xC0, 0x69, 0xC0, 0x6A, /* 0x24-0x27 */
+ 0xC0, 0x6B, 0xC0, 0x6C, 0xC0, 0x6D, 0xC0, 0x6E, /* 0x28-0x2B */
+ 0xC0, 0x6F, 0xC0, 0x70, 0xC0, 0x71, 0xC0, 0x72, /* 0x2C-0x2F */
+ 0xC0, 0x73, 0xC0, 0x74, 0xC0, 0x75, 0xC0, 0x76, /* 0x30-0x33 */
+ 0xC0, 0x77, 0xC0, 0x78, 0xC0, 0x79, 0xC0, 0x7A, /* 0x34-0x37 */
+ 0xC0, 0x81, 0xC0, 0x82, 0xC0, 0x83, 0xC0, 0x84, /* 0x38-0x3B */
+ 0xC7, 0xC7, 0xC7, 0xC8, 0xC0, 0x85, 0xC0, 0x86, /* 0x3C-0x3F */
+ 0xC7, 0xC9, 0xC0, 0x87, 0xC0, 0x88, 0xC0, 0x89, /* 0x40-0x43 */
+ 0xC7, 0xCA, 0xC0, 0x8A, 0xC0, 0x8B, 0xC0, 0x8C, /* 0x44-0x47 */
+ 0xC0, 0x8D, 0xC0, 0x8E, 0xC0, 0x8F, 0xC0, 0x90, /* 0x48-0x4B */
+ 0xC7, 0xCB, 0xC7, 0xCC, 0xC0, 0x91, 0xC7, 0xCD, /* 0x4C-0x4F */
+ 0xC0, 0x92, 0xC7, 0xCE, 0xC0, 0x93, 0xC0, 0x94, /* 0x50-0x53 */
+ 0xC0, 0x95, 0xC0, 0x96, 0xC0, 0x97, 0xC0, 0x98, /* 0x54-0x57 */
+ 0xC7, 0xCF, 0xC7, 0xD0, 0xC0, 0x99, 0xC0, 0x9A, /* 0x58-0x5B */
+ 0xC7, 0xD1, 0xC0, 0x9B, 0xC0, 0x9C, 0xC0, 0x9D, /* 0x5C-0x5F */
+ 0xC7, 0xD2, 0xC0, 0x9E, 0xC0, 0x9F, 0xC0, 0xA0, /* 0x60-0x63 */
+ 0xC1, 0x41, 0xC7, 0xD3, 0xC1, 0x42, 0xC1, 0x43, /* 0x64-0x67 */
+ 0xC7, 0xD4, 0xC7, 0xD5, 0xC1, 0x44, 0xC7, 0xD6, /* 0x68-0x6B */
+ 0xC1, 0x45, 0xC7, 0xD7, 0xC1, 0x46, 0xC1, 0x47, /* 0x6C-0x6F */
+ 0xC1, 0x48, 0xC1, 0x49, 0xC1, 0x4A, 0xC1, 0x4B, /* 0x70-0x73 */
+ 0xC7, 0xD8, 0xC7, 0xD9, 0xC1, 0x4C, 0xC1, 0x4D, /* 0x74-0x77 */
+ 0xC7, 0xDA, 0xC1, 0x4E, 0xC1, 0x4F, 0xC1, 0x50, /* 0x78-0x7B */
+ 0xC7, 0xDB, 0xC1, 0x51, 0xC1, 0x52, 0xC1, 0x53, /* 0x7C-0x7F */
+
+ 0xC1, 0x54, 0xC1, 0x55, 0xC1, 0x56, 0xC1, 0x57, /* 0x80-0x83 */
+ 0xC7, 0xDC, 0xC7, 0xDD, 0xC1, 0x58, 0xC7, 0xDE, /* 0x84-0x87 */
+ 0xC7, 0xDF, 0xC7, 0xE0, 0xC1, 0x59, 0xC1, 0x5A, /* 0x88-0x8B */
+ 0xC1, 0x61, 0xC1, 0x62, 0xC1, 0x63, 0xC1, 0x64, /* 0x8C-0x8F */
+ 0xC7, 0xE1, 0xC1, 0x65, 0xC1, 0x66, 0xC1, 0x67, /* 0x90-0x93 */
+ 0xC1, 0x68, 0xC1, 0x69, 0xC1, 0x6A, 0xC1, 0x6B, /* 0x94-0x97 */
+ 0xC1, 0x6C, 0xC1, 0x6D, 0xC1, 0x6E, 0xC1, 0x6F, /* 0x98-0x9B */
+ 0xC1, 0x70, 0xC1, 0x71, 0xC1, 0x72, 0xC1, 0x73, /* 0x9C-0x9F */
+ 0xC1, 0x74, 0xC1, 0x75, 0xC1, 0x76, 0xC1, 0x77, /* 0xA0-0xA3 */
+ 0xC1, 0x78, 0xC7, 0xE2, 0xC1, 0x79, 0xC1, 0x7A, /* 0xA4-0xA7 */
+ 0xC1, 0x81, 0xC1, 0x82, 0xC1, 0x83, 0xC1, 0x84, /* 0xA8-0xAB */
+ 0xC1, 0x85, 0xC1, 0x86, 0xC1, 0x87, 0xC1, 0x88, /* 0xAC-0xAF */
+ 0xC1, 0x89, 0xC1, 0x8A, 0xC1, 0x8B, 0xC1, 0x8C, /* 0xB0-0xB3 */
+ 0xC1, 0x8D, 0xC1, 0x8E, 0xC1, 0x8F, 0xC1, 0x90, /* 0xB4-0xB7 */
+ 0xC1, 0x91, 0xC1, 0x92, 0xC1, 0x93, 0xC1, 0x94, /* 0xB8-0xBB */
+ 0xC1, 0x95, 0xC1, 0x96, 0xC1, 0x97, 0xC1, 0x98, /* 0xBC-0xBF */
+ 0xC1, 0x99, 0xC1, 0x9A, 0xC1, 0x9B, 0xC1, 0x9C, /* 0xC0-0xC3 */
+ 0xC1, 0x9D, 0xC1, 0x9E, 0xC1, 0x9F, 0xC1, 0xA0, /* 0xC4-0xC7 */
+ 0xC7, 0xE3, 0xC7, 0xE4, 0xC2, 0x41, 0xC2, 0x42, /* 0xC8-0xCB */
+ 0xC7, 0xE5, 0xC2, 0x43, 0xC2, 0x44, 0xC2, 0x45, /* 0xCC-0xCF */
+ 0xC7, 0xE6, 0xC2, 0x46, 0xC7, 0xE7, 0xC2, 0x47, /* 0xD0-0xD3 */
+ 0xC2, 0x48, 0xC2, 0x49, 0xC2, 0x4A, 0xC2, 0x4B, /* 0xD4-0xD7 */
+ 0xC7, 0xE8, 0xC7, 0xE9, 0xC2, 0x4C, 0xC7, 0xEA, /* 0xD8-0xDB */
+ 0xC2, 0x4D, 0xC7, 0xEB, 0xC2, 0x4E, 0xC2, 0x4F, /* 0xDC-0xDF */
+ 0xC2, 0x50, 0xC2, 0x51, 0xC2, 0x52, 0xC2, 0x53, /* 0xE0-0xE3 */
+ 0xC7, 0xEC, 0xC7, 0xED, 0xC2, 0x54, 0xC2, 0x55, /* 0xE4-0xE7 */
+ 0xC7, 0xEE, 0xC2, 0x56, 0xC2, 0x57, 0xC2, 0x58, /* 0xE8-0xEB */
+ 0xC7, 0xEF, 0xC2, 0x59, 0xC2, 0x5A, 0xC2, 0x61, /* 0xEC-0xEF */
+ 0xC2, 0x62, 0xC2, 0x63, 0xC2, 0x64, 0xC2, 0x65, /* 0xF0-0xF3 */
+ 0xC7, 0xF0, 0xC7, 0xF1, 0xC2, 0x66, 0xC7, 0xF2, /* 0xF4-0xF7 */
+ 0xC2, 0x67, 0xC7, 0xF3, 0xC2, 0x68, 0xC2, 0x69, /* 0xF8-0xFB */
+ 0xC2, 0x6A, 0xC2, 0x6B, 0xC2, 0x6C, 0xC2, 0x6D, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_D6[512] = {
+ 0xC7, 0xF4, 0xC7, 0xF5, 0xC2, 0x6E, 0xC2, 0x6F, /* 0x00-0x03 */
+ 0xC7, 0xF6, 0xC2, 0x70, 0xC2, 0x71, 0xC2, 0x72, /* 0x04-0x07 */
+ 0xC7, 0xF7, 0xC2, 0x73, 0xC2, 0x74, 0xC2, 0x75, /* 0x08-0x0B */
+ 0xC2, 0x76, 0xC2, 0x77, 0xC2, 0x78, 0xC2, 0x79, /* 0x0C-0x0F */
+ 0xC7, 0xF8, 0xC7, 0xF9, 0xC2, 0x7A, 0xC7, 0xFA, /* 0x10-0x13 */
+ 0xC7, 0xFB, 0xC7, 0xFC, 0xC2, 0x81, 0xC2, 0x82, /* 0x14-0x17 */
+ 0xC2, 0x83, 0xC2, 0x84, 0xC2, 0x85, 0xC2, 0x86, /* 0x18-0x1B */
+ 0xC7, 0xFD, 0xC2, 0x87, 0xC2, 0x88, 0xC2, 0x89, /* 0x1C-0x1F */
+ 0xC7, 0xFE, 0xC2, 0x8A, 0xC2, 0x8B, 0xC2, 0x8C, /* 0x20-0x23 */
+ 0xC8, 0xA1, 0xC2, 0x8D, 0xC2, 0x8E, 0xC2, 0x8F, /* 0x24-0x27 */
+ 0xC2, 0x90, 0xC2, 0x91, 0xC2, 0x92, 0xC2, 0x93, /* 0x28-0x2B */
+ 0xC2, 0x94, 0xC8, 0xA2, 0xC2, 0x95, 0xC2, 0x96, /* 0x2C-0x2F */
+ 0xC2, 0x97, 0xC2, 0x98, 0xC2, 0x99, 0xC2, 0x9A, /* 0x30-0x33 */
+ 0xC2, 0x9B, 0xC2, 0x9C, 0xC2, 0x9D, 0xC2, 0x9E, /* 0x34-0x37 */
+ 0xC8, 0xA3, 0xC8, 0xA4, 0xC2, 0x9F, 0xC2, 0xA0, /* 0x38-0x3B */
+ 0xC8, 0xA5, 0xC3, 0x41, 0xC3, 0x42, 0xC3, 0x43, /* 0x3C-0x3F */
+ 0xC8, 0xA6, 0xC3, 0x44, 0xC3, 0x45, 0xC3, 0x46, /* 0x40-0x43 */
+ 0xC3, 0x47, 0xC8, 0xA7, 0xC3, 0x48, 0xC3, 0x49, /* 0x44-0x47 */
+ 0xC8, 0xA8, 0xC8, 0xA9, 0xC3, 0x4A, 0xC8, 0xAA, /* 0x48-0x4B */
+ 0xC3, 0x4B, 0xC8, 0xAB, 0xC3, 0x4C, 0xC3, 0x4D, /* 0x4C-0x4F */
+ 0xC3, 0x4E, 0xC8, 0xAC, 0xC3, 0x4F, 0xC3, 0x50, /* 0x50-0x53 */
+ 0xC8, 0xAD, 0xC8, 0xAE, 0xC3, 0x51, 0xC3, 0x52, /* 0x54-0x57 */
+ 0xC8, 0xAF, 0xC3, 0x53, 0xC3, 0x54, 0xC3, 0x55, /* 0x58-0x5B */
+ 0xC8, 0xB0, 0xC3, 0x56, 0xC3, 0x57, 0xC3, 0x58, /* 0x5C-0x5F */
+ 0xC3, 0x59, 0xC3, 0x5A, 0xC3, 0x61, 0xC3, 0x62, /* 0x60-0x63 */
+ 0xC3, 0x63, 0xC3, 0x64, 0xC3, 0x65, 0xC8, 0xB1, /* 0x64-0x67 */
+ 0xC3, 0x66, 0xC8, 0xB2, 0xC3, 0x67, 0xC3, 0x68, /* 0x68-0x6B */
+ 0xC3, 0x69, 0xC3, 0x6A, 0xC3, 0x6B, 0xC3, 0x6C, /* 0x6C-0x6F */
+ 0xC8, 0xB3, 0xC8, 0xB4, 0xC3, 0x6D, 0xC3, 0x6E, /* 0x70-0x73 */
+ 0xC8, 0xB5, 0xC3, 0x6F, 0xC3, 0x70, 0xC3, 0x71, /* 0x74-0x77 */
+ 0xC3, 0x72, 0xC3, 0x73, 0xC3, 0x74, 0xC3, 0x75, /* 0x78-0x7B */
+ 0xC3, 0x76, 0xC3, 0x77, 0xC3, 0x78, 0xC3, 0x79, /* 0x7C-0x7F */
+
+ 0xC3, 0x7A, 0xC3, 0x81, 0xC3, 0x82, 0xC8, 0xB6, /* 0x80-0x83 */
+ 0xC3, 0x83, 0xC8, 0xB7, 0xC3, 0x84, 0xC3, 0x85, /* 0x84-0x87 */
+ 0xC3, 0x86, 0xC3, 0x87, 0xC3, 0x88, 0xC3, 0x89, /* 0x88-0x8B */
+ 0xC8, 0xB8, 0xC8, 0xB9, 0xC3, 0x8A, 0xC3, 0x8B, /* 0x8C-0x8F */
+ 0xC8, 0xBA, 0xC3, 0x8C, 0xC3, 0x8D, 0xC3, 0x8E, /* 0x90-0x93 */
+ 0xC8, 0xBB, 0xC3, 0x8F, 0xC3, 0x90, 0xC3, 0x91, /* 0x94-0x97 */
+ 0xC3, 0x92, 0xC3, 0x93, 0xC3, 0x94, 0xC3, 0x95, /* 0x98-0x9B */
+ 0xC3, 0x96, 0xC8, 0xBC, 0xC3, 0x97, 0xC8, 0xBD, /* 0x9C-0x9F */
+ 0xC3, 0x98, 0xC8, 0xBE, 0xC3, 0x99, 0xC3, 0x9A, /* 0xA0-0xA3 */
+ 0xC3, 0x9B, 0xC3, 0x9C, 0xC3, 0x9D, 0xC3, 0x9E, /* 0xA4-0xA7 */
+ 0xC8, 0xBF, 0xC3, 0x9F, 0xC3, 0xA0, 0xC4, 0x41, /* 0xA8-0xAB */
+ 0xC8, 0xC0, 0xC4, 0x42, 0xC4, 0x43, 0xC4, 0x44, /* 0xAC-0xAF */
+ 0xC8, 0xC1, 0xC4, 0x45, 0xC4, 0x46, 0xC4, 0x47, /* 0xB0-0xB3 */
+ 0xC4, 0x48, 0xC4, 0x49, 0xC4, 0x4A, 0xC4, 0x4B, /* 0xB4-0xB7 */
+ 0xC4, 0x4C, 0xC8, 0xC2, 0xC4, 0x4D, 0xC8, 0xC3, /* 0xB8-0xBB */
+ 0xC4, 0x4E, 0xC4, 0x4F, 0xC4, 0x50, 0xC4, 0x51, /* 0xBC-0xBF */
+ 0xC4, 0x52, 0xC4, 0x53, 0xC4, 0x54, 0xC4, 0x55, /* 0xC0-0xC3 */
+ 0xC8, 0xC4, 0xC8, 0xC5, 0xC4, 0x56, 0xC4, 0x57, /* 0xC4-0xC7 */
+ 0xC8, 0xC6, 0xC4, 0x58, 0xC4, 0x59, 0xC4, 0x5A, /* 0xC8-0xCB */
+ 0xC8, 0xC7, 0xC4, 0x61, 0xC4, 0x62, 0xC4, 0x63, /* 0xCC-0xCF */
+ 0xC4, 0x64, 0xC8, 0xC8, 0xC4, 0x65, 0xC4, 0x66, /* 0xD0-0xD3 */
+ 0xC8, 0xC9, 0xC4, 0x67, 0xC4, 0x68, 0xC8, 0xCA, /* 0xD4-0xD7 */
+ 0xC4, 0x69, 0xC8, 0xCB, 0xC4, 0x6A, 0xC4, 0x6B, /* 0xD8-0xDB */
+ 0xC4, 0x6C, 0xC4, 0x6D, 0xC4, 0x6E, 0xC4, 0x6F, /* 0xDC-0xDF */
+ 0xC8, 0xCC, 0xC4, 0x70, 0xC4, 0x71, 0xC4, 0x72, /* 0xE0-0xE3 */
+ 0xC8, 0xCD, 0xC4, 0x73, 0xC4, 0x74, 0xC4, 0x75, /* 0xE4-0xE7 */
+ 0xC8, 0xCE, 0xC4, 0x76, 0xC4, 0x77, 0xC4, 0x78, /* 0xE8-0xEB */
+ 0xC4, 0x79, 0xC4, 0x7A, 0xC4, 0x81, 0xC4, 0x82, /* 0xEC-0xEF */
+ 0xC8, 0xCF, 0xC4, 0x83, 0xC4, 0x84, 0xC4, 0x85, /* 0xF0-0xF3 */
+ 0xC4, 0x86, 0xC8, 0xD0, 0xC4, 0x87, 0xC4, 0x88, /* 0xF4-0xF7 */
+ 0xC4, 0x89, 0xC4, 0x8A, 0xC4, 0x8B, 0xC4, 0x8C, /* 0xF8-0xFB */
+ 0xC8, 0xD1, 0xC8, 0xD2, 0xC4, 0x8D, 0xC4, 0x8E, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_D7[512] = {
+ 0xC8, 0xD3, 0xC4, 0x8F, 0xC4, 0x90, 0xC4, 0x91, /* 0x00-0x03 */
+ 0xC8, 0xD4, 0xC4, 0x92, 0xC4, 0x93, 0xC4, 0x94, /* 0x04-0x07 */
+ 0xC4, 0x95, 0xC4, 0x96, 0xC4, 0x97, 0xC4, 0x98, /* 0x08-0x0B */
+ 0xC4, 0x99, 0xC4, 0x9A, 0xC4, 0x9B, 0xC4, 0x9C, /* 0x0C-0x0F */
+ 0xC4, 0x9D, 0xC8, 0xD5, 0xC4, 0x9E, 0xC4, 0x9F, /* 0x10-0x13 */
+ 0xC4, 0xA0, 0xC5, 0x41, 0xC5, 0x42, 0xC5, 0x43, /* 0x14-0x17 */
+ 0xC8, 0xD6, 0xC8, 0xD7, 0xC5, 0x44, 0xC5, 0x45, /* 0x18-0x1B */
+ 0xC8, 0xD8, 0xC5, 0x46, 0xC5, 0x47, 0xC5, 0x48, /* 0x1C-0x1F */
+ 0xC8, 0xD9, 0xC5, 0x49, 0xC5, 0x4A, 0xC5, 0x4B, /* 0x20-0x23 */
+ 0xC5, 0x4C, 0xC5, 0x4D, 0xC5, 0x4E, 0xC5, 0x4F, /* 0x24-0x27 */
+ 0xC8, 0xDA, 0xC8, 0xDB, 0xC5, 0x50, 0xC8, 0xDC, /* 0x28-0x2B */
+ 0xC5, 0x51, 0xC8, 0xDD, 0xC5, 0x52, 0xC5, 0x53, /* 0x2C-0x2F */
+ 0xC5, 0x54, 0xC5, 0x55, 0xC5, 0x56, 0xC5, 0x57, /* 0x30-0x33 */
+ 0xC8, 0xDE, 0xC8, 0xDF, 0xC5, 0x58, 0xC5, 0x59, /* 0x34-0x37 */
+ 0xC8, 0xE0, 0xC5, 0x5A, 0xC5, 0x61, 0xC5, 0x62, /* 0x38-0x3B */
+ 0xC8, 0xE1, 0xC5, 0x63, 0xC5, 0x64, 0xC5, 0x65, /* 0x3C-0x3F */
+ 0xC5, 0x66, 0xC5, 0x67, 0xC5, 0x68, 0xC5, 0x69, /* 0x40-0x43 */
+ 0xC8, 0xE2, 0xC5, 0x6A, 0xC5, 0x6B, 0xC8, 0xE3, /* 0x44-0x47 */
+ 0xC5, 0x6C, 0xC8, 0xE4, 0xC5, 0x6D, 0xC5, 0x6E, /* 0x48-0x4B */
+ 0xC5, 0x6F, 0xC5, 0x70, 0xC5, 0x71, 0xC5, 0x72, /* 0x4C-0x4F */
+ 0xC8, 0xE5, 0xC8, 0xE6, 0xC5, 0x73, 0xC5, 0x74, /* 0x50-0x53 */
+ 0xC8, 0xE7, 0xC5, 0x75, 0xC8, 0xE8, 0xC8, 0xE9, /* 0x54-0x57 */
+ 0xC8, 0xEA, 0xC8, 0xEB, 0xC5, 0x76, 0xC5, 0x77, /* 0x58-0x5B */
+ 0xC5, 0x78, 0xC5, 0x79, 0xC5, 0x7A, 0xC5, 0x81, /* 0x5C-0x5F */
+ 0xC8, 0xEC, 0xC8, 0xED, 0xC5, 0x82, 0xC8, 0xEE, /* 0x60-0x63 */
+ 0xC5, 0x83, 0xC8, 0xEF, 0xC5, 0x84, 0xC5, 0x85, /* 0x64-0x67 */
+ 0xC5, 0x86, 0xC8, 0xF0, 0xC5, 0x87, 0xC5, 0x88, /* 0x68-0x6B */
+ 0xC8, 0xF1, 0xC5, 0x89, 0xC5, 0x8A, 0xC5, 0x8B, /* 0x6C-0x6F */
+ 0xC8, 0xF2, 0xC5, 0x8C, 0xC5, 0x8D, 0xC5, 0x8E, /* 0x70-0x73 */
+ 0xC8, 0xF3, 0xC5, 0x8F, 0xC5, 0x90, 0xC5, 0x91, /* 0x74-0x77 */
+ 0xC5, 0x92, 0xC5, 0x93, 0xC5, 0x94, 0xC5, 0x95, /* 0x78-0x7B */
+ 0xC8, 0xF4, 0xC8, 0xF5, 0xC5, 0x96, 0xC5, 0x97, /* 0x7C-0x7F */
+
+ 0xC5, 0x98, 0xC8, 0xF6, 0xC5, 0x99, 0xC5, 0x9A, /* 0x80-0x83 */
+ 0xC5, 0x9B, 0xC5, 0x9C, 0xC5, 0x9D, 0xC5, 0x9E, /* 0x84-0x87 */
+ 0xC8, 0xF7, 0xC8, 0xF8, 0xC5, 0x9F, 0xC5, 0xA0, /* 0x88-0x8B */
+ 0xC8, 0xF9, 0xC6, 0x41, 0xC6, 0x42, 0xC6, 0x43, /* 0x8C-0x8F */
+ 0xC8, 0xFA, 0xC6, 0x44, 0xC6, 0x45, 0xC6, 0x46, /* 0x90-0x93 */
+ 0xC6, 0x47, 0xC6, 0x48, 0xC6, 0x49, 0xC6, 0x4A, /* 0x94-0x97 */
+ 0xC8, 0xFB, 0xC8, 0xFC, 0xC6, 0x4B, 0xC8, 0xFD, /* 0x98-0x9B */
+ 0xC6, 0x4C, 0xC8, 0xFE, 0xC6, 0x4D, 0xC6, 0x4E, /* 0x9C-0x9F */
+ 0xC6, 0x4F, 0xC6, 0x50, 0xC6, 0x51, 0xC6, 0x52, /* 0xA0-0xA3 */
+};
+
+static unsigned char u2c_DC[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+};
+
+static unsigned char u2c_F9[512] = {
+ 0xCB, 0xD0, 0xCB, 0xD6, 0xCB, 0xE7, 0xCD, 0xCF, /* 0x00-0x03 */
+ 0xCD, 0xE8, 0xCE, 0xAD, 0xCF, 0xFB, 0xD0, 0xA2, /* 0x04-0x07 */
+ 0xD0, 0xB8, 0xD0, 0xD0, 0xD0, 0xDD, 0xD1, 0xD4, /* 0x08-0x0B */
+ 0xD1, 0xD5, 0xD1, 0xD8, 0xD1, 0xDB, 0xD1, 0xDC, /* 0x0C-0x0F */
+ 0xD1, 0xDD, 0xD1, 0xDE, 0xD1, 0xDF, 0xD1, 0xE0, /* 0x10-0x13 */
+ 0xD1, 0xE2, 0xD1, 0xE3, 0xD1, 0xE4, 0xD1, 0xE5, /* 0x14-0x17 */
+ 0xD1, 0xE6, 0xD1, 0xE8, 0xD1, 0xE9, 0xD1, 0xEA, /* 0x18-0x1B */
+ 0xD1, 0xEB, 0xD1, 0xED, 0xD1, 0xEF, 0xD1, 0xF0, /* 0x1C-0x1F */
+ 0xD1, 0xF2, 0xD1, 0xF6, 0xD1, 0xFA, 0xD1, 0xFC, /* 0x20-0x23 */
+ 0xD1, 0xFD, 0xD1, 0xFE, 0xD2, 0xA2, 0xD2, 0xA3, /* 0x24-0x27 */
+ 0xD2, 0xA7, 0xD2, 0xA8, 0xD2, 0xA9, 0xD2, 0xAA, /* 0x28-0x2B */
+ 0xD2, 0xAB, 0xD2, 0xAD, 0xD2, 0xB2, 0xD2, 0xBE, /* 0x2C-0x2F */
+ 0xD2, 0xC2, 0xD2, 0xC3, 0xD2, 0xC4, 0xD2, 0xC6, /* 0x30-0x33 */
+ 0xD2, 0xC7, 0xD2, 0xC8, 0xD2, 0xC9, 0xD2, 0xCA, /* 0x34-0x37 */
+ 0xD2, 0xCB, 0xD2, 0xCD, 0xD2, 0xCE, 0xD2, 0xCF, /* 0x38-0x3B */
+ 0xD2, 0xD0, 0xD2, 0xD1, 0xD2, 0xD2, 0xD2, 0xD3, /* 0x3C-0x3F */
+ 0xD2, 0xD4, 0xD2, 0xD5, 0xD2, 0xD6, 0xD2, 0xD7, /* 0x40-0x43 */
+ 0xD2, 0xD9, 0xD2, 0xDA, 0xD2, 0xDE, 0xD2, 0xDF, /* 0x44-0x47 */
+ 0xD2, 0xE1, 0xD2, 0xE2, 0xD2, 0xE4, 0xD2, 0xE5, /* 0x48-0x4B */
+ 0xD2, 0xE6, 0xD2, 0xE7, 0xD2, 0xE8, 0xD2, 0xE9, /* 0x4C-0x4F */
+ 0xD2, 0xEA, 0xD2, 0xEB, 0xD2, 0xF0, 0xD2, 0xF1, /* 0x50-0x53 */
+ 0xD2, 0xF2, 0xD2, 0xF3, 0xD2, 0xF4, 0xD2, 0xF5, /* 0x54-0x57 */
+ 0xD2, 0xF7, 0xD2, 0xF8, 0xD4, 0xE6, 0xD4, 0xFC, /* 0x58-0x5B */
+ 0xD5, 0xA5, 0xD5, 0xAB, 0xD5, 0xAE, 0xD6, 0xB8, /* 0x5C-0x5F */
+ 0xD6, 0xCD, 0xD7, 0xCB, 0xD7, 0xE4, 0xDB, 0xC5, /* 0x60-0x63 */
+ 0xDB, 0xE4, 0xDC, 0xA5, 0xDD, 0xA5, 0xDD, 0xD5, /* 0x64-0x67 */
+ 0xDD, 0xF4, 0xDE, 0xFC, 0xDE, 0xFE, 0xDF, 0xB3, /* 0x68-0x6B */
+ 0xDF, 0xE1, 0xDF, 0xE8, 0xE0, 0xF1, 0xE1, 0xAD, /* 0x6C-0x6F */
+ 0xE1, 0xED, 0xE3, 0xF5, 0xE4, 0xA1, 0xE4, 0xA9, /* 0x70-0x73 */
+ 0xE5, 0xAE, 0xE5, 0xB1, 0xE5, 0xB2, 0xE5, 0xB9, /* 0x74-0x77 */
+ 0xE5, 0xBB, 0xE5, 0xBC, 0xE5, 0xC4, 0xE5, 0xCE, /* 0x78-0x7B */
+ 0xE5, 0xD0, 0xE5, 0xD2, 0xE5, 0xD6, 0xE5, 0xFA, /* 0x7C-0x7F */
+
+ 0xE5, 0xFB, 0xE5, 0xFC, 0xE5, 0xFE, 0xE6, 0xA1, /* 0x80-0x83 */
+ 0xE6, 0xA4, 0xE6, 0xA7, 0xE6, 0xAD, 0xE6, 0xAF, /* 0x84-0x87 */
+ 0xE6, 0xB0, 0xE6, 0xB1, 0xE6, 0xB3, 0xE6, 0xB7, /* 0x88-0x8B */
+ 0xE6, 0xB8, 0xE6, 0xBC, 0xE6, 0xC4, 0xE6, 0xC6, /* 0x8C-0x8F */
+ 0xE6, 0xC7, 0xE6, 0xCA, 0xE6, 0xD2, 0xE6, 0xD6, /* 0x90-0x93 */
+ 0xE6, 0xD9, 0xE6, 0xDC, 0xE6, 0xDF, 0xE6, 0xE1, /* 0x94-0x97 */
+ 0xE6, 0xE4, 0xE6, 0xE5, 0xE6, 0xE6, 0xE6, 0xE8, /* 0x98-0x9B */
+ 0xE6, 0xEA, 0xE6, 0xEB, 0xE6, 0xEC, 0xE6, 0xEF, /* 0x9C-0x9F */
+ 0xE6, 0xF1, 0xE6, 0xF2, 0xE6, 0xF5, 0xE6, 0xF6, /* 0xA0-0xA3 */
+ 0xE6, 0xF7, 0xE6, 0xF9, 0xE7, 0xA1, 0xE7, 0xA6, /* 0xA4-0xA7 */
+ 0xE7, 0xA9, 0xE7, 0xAA, 0xE7, 0xAC, 0xE7, 0xAD, /* 0xA8-0xAB */
+ 0xE7, 0xB0, 0xE7, 0xBF, 0xE7, 0xC1, 0xE7, 0xC6, /* 0xAC-0xAF */
+ 0xE7, 0xC7, 0xE7, 0xCB, 0xE7, 0xCD, 0xE7, 0xCF, /* 0xB0-0xB3 */
+ 0xE7, 0xD0, 0xE7, 0xD3, 0xE7, 0xDF, 0xE7, 0xE4, /* 0xB4-0xB7 */
+ 0xE7, 0xE6, 0xE7, 0xF7, 0xE8, 0xE7, 0xE8, 0xE8, /* 0xB8-0xBB */
+ 0xE8, 0xF0, 0xE8, 0xF1, 0xE8, 0xF7, 0xE8, 0xF9, /* 0xBC-0xBF */
+ 0xE8, 0xFB, 0xE8, 0xFE, 0xE9, 0xA7, 0xE9, 0xAC, /* 0xC0-0xC3 */
+ 0xE9, 0xCC, 0xE9, 0xF7, 0xEA, 0xC1, 0xEA, 0xE5, /* 0xC4-0xC7 */
+ 0xEA, 0xF4, 0xEA, 0xF7, 0xEA, 0xFC, 0xEA, 0xFE, /* 0xC8-0xCB */
+ 0xEB, 0xA4, 0xEB, 0xA7, 0xEB, 0xA9, 0xEB, 0xAA, /* 0xCC-0xCF */
+ 0xEB, 0xBA, 0xEB, 0xBB, 0xEB, 0xBD, 0xEB, 0xC1, /* 0xD0-0xD3 */
+ 0xEB, 0xC2, 0xEB, 0xC6, 0xEB, 0xC7, 0xEB, 0xCC, /* 0xD4-0xD7 */
+ 0xEB, 0xCF, 0xEB, 0xD0, 0xEB, 0xD1, 0xEB, 0xD2, /* 0xD8-0xDB */
+ 0xEB, 0xD8, 0xEC, 0xA6, 0xEC, 0xA7, 0xEC, 0xAA, /* 0xDC-0xDF */
+ 0xEC, 0xAF, 0xEC, 0xB0, 0xEC, 0xB1, 0xEC, 0xB2, /* 0xE0-0xE3 */
+ 0xEC, 0xB5, 0xEC, 0xB8, 0xEC, 0xBA, 0xEC, 0xC0, /* 0xE4-0xE7 */
+ 0xEC, 0xC1, 0xEC, 0xC5, 0xEC, 0xC6, 0xEC, 0xC9, /* 0xE8-0xEB */
+ 0xEC, 0xCA, 0xEC, 0xD5, 0xEC, 0xDD, 0xEC, 0xDE, /* 0xEC-0xEF */
+ 0xEC, 0xE1, 0xEC, 0xE4, 0xEC, 0xE7, 0xEC, 0xE8, /* 0xF0-0xF3 */
+ 0xEC, 0xF7, 0xEC, 0xF8, 0xEC, 0xFA, 0xED, 0xA1, /* 0xF4-0xF7 */
+ 0xED, 0xA2, 0xED, 0xA3, 0xED, 0xEE, 0xEE, 0xDB, /* 0xF8-0xFB */
+ 0xF2, 0xBD, 0xF2, 0xFA, 0xF3, 0xB1, 0xF4, 0xA7, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_FA[512] = {
+ 0xF4, 0xEE, 0xF6, 0xF4, 0xF6, 0xF6, 0xF7, 0xB8, /* 0x00-0x03 */
+ 0xF7, 0xC8, 0xF7, 0xD3, 0xF8, 0xDB, 0xF8, 0xF0, /* 0x04-0x07 */
+ 0xFA, 0xA1, 0xFA, 0xA2, 0xFA, 0xE6, 0xFC, 0xA9, /* 0x08-0x0B */
+ 0xE8, 0xB4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xF5, 0xC0, 0x00, 0x00, 0xF4, 0xE7, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0xFD, 0xEB, 0x00, 0x00, 0xEC, 0xCC, /* 0x14-0x17 */
+ 0x00, 0x00, 0xE3, 0xEA, 0xDF, 0xD4, 0xDC, 0xD8, /* 0x18-0x1B */
+ 0xEF, 0xFE, 0xEF, 0xF1, 0xE9, 0xE2, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0xF0, 0xB3, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0xEC, 0xEF, 0xD4, 0xB4, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xDA, 0xF9, 0xDE, 0xF8, /* 0x28-0x2B */
+ 0xCE, 0xBD, 0xF9, 0xCD, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+};
+
+static unsigned char u2c_FF[512] = {
+ 0x00, 0x00, 0xA3, 0xA1, 0xA3, 0xA2, 0xA3, 0xA3, /* 0x00-0x03 */
+ 0xA3, 0xA4, 0xA3, 0xA5, 0xA3, 0xA6, 0xA3, 0xA7, /* 0x04-0x07 */
+ 0xA3, 0xA8, 0xA3, 0xA9, 0xA3, 0xAA, 0xA3, 0xAB, /* 0x08-0x0B */
+ 0xA3, 0xAC, 0xA3, 0xAD, 0xA3, 0xAE, 0xA3, 0xAF, /* 0x0C-0x0F */
+ 0xA3, 0xB0, 0xA3, 0xB1, 0xA3, 0xB2, 0xA3, 0xB3, /* 0x10-0x13 */
+ 0xA3, 0xB4, 0xA3, 0xB5, 0xA3, 0xB6, 0xA3, 0xB7, /* 0x14-0x17 */
+ 0xA3, 0xB8, 0xA3, 0xB9, 0xA3, 0xBA, 0xA3, 0xBB, /* 0x18-0x1B */
+ 0xA3, 0xBC, 0xA3, 0xBD, 0xA3, 0xBE, 0xA3, 0xBF, /* 0x1C-0x1F */
+ 0xA3, 0xC0, 0xA3, 0xC1, 0xA3, 0xC2, 0xA3, 0xC3, /* 0x20-0x23 */
+ 0xA3, 0xC4, 0xA3, 0xC5, 0xA3, 0xC6, 0xA3, 0xC7, /* 0x24-0x27 */
+ 0xA3, 0xC8, 0xA3, 0xC9, 0xA3, 0xCA, 0xA3, 0xCB, /* 0x28-0x2B */
+ 0xA3, 0xCC, 0xA3, 0xCD, 0xA3, 0xCE, 0xA3, 0xCF, /* 0x2C-0x2F */
+ 0xA3, 0xD0, 0xA3, 0xD1, 0xA3, 0xD2, 0xA3, 0xD3, /* 0x30-0x33 */
+ 0xA3, 0xD4, 0xA3, 0xD5, 0xA3, 0xD6, 0xA3, 0xD7, /* 0x34-0x37 */
+ 0xA3, 0xD8, 0xA3, 0xD9, 0xA3, 0xDA, 0xA3, 0xDB, /* 0x38-0x3B */
+ 0xA1, 0xAC, 0xA3, 0xDD, 0xA3, 0xDE, 0xA3, 0xDF, /* 0x3C-0x3F */
+ 0xA3, 0xE0, 0xA3, 0xE1, 0xA3, 0xE2, 0xA3, 0xE3, /* 0x40-0x43 */
+ 0xA3, 0xE4, 0xA3, 0xE5, 0xA3, 0xE6, 0xA3, 0xE7, /* 0x44-0x47 */
+ 0xA3, 0xE8, 0xA3, 0xE9, 0xA3, 0xEA, 0xA3, 0xEB, /* 0x48-0x4B */
+ 0xA3, 0xEC, 0xA3, 0xED, 0xA3, 0xEE, 0xA3, 0xEF, /* 0x4C-0x4F */
+ 0xA3, 0xF0, 0xA3, 0xF1, 0xA3, 0xF2, 0xA3, 0xF3, /* 0x50-0x53 */
+ 0xA3, 0xF4, 0xA3, 0xF5, 0xA3, 0xF6, 0xA3, 0xF7, /* 0x54-0x57 */
+ 0xA3, 0xF8, 0xA3, 0xF9, 0xA3, 0xFA, 0xA3, 0xFB, /* 0x58-0x5B */
+ 0xA3, 0xFC, 0xA3, 0xFD, 0xA2, 0xA6, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0xA4, 0xD4, 0xA4, 0xA1, 0xA4, 0xA2, 0xA4, 0xA3, /* 0xA0-0xA3 */
+ 0xA4, 0xA4, 0xA4, 0xA5, 0xA4, 0xA6, 0xA4, 0xA7, /* 0xA4-0xA7 */
+ 0xA4, 0xA8, 0xA4, 0xA9, 0xA4, 0xAA, 0xA4, 0xAB, /* 0xA8-0xAB */
+ 0xA4, 0xAC, 0xA4, 0xAD, 0xA4, 0xAE, 0xA4, 0xAF, /* 0xAC-0xAF */
+ 0xA4, 0xB0, 0xA4, 0xB1, 0xA4, 0xB2, 0xA4, 0xB3, /* 0xB0-0xB3 */
+ 0xA4, 0xB4, 0xA4, 0xB5, 0xA4, 0xB6, 0xA4, 0xB7, /* 0xB4-0xB7 */
+ 0xA4, 0xB8, 0xA4, 0xB9, 0xA4, 0xBA, 0xA4, 0xBB, /* 0xB8-0xBB */
+ 0xA4, 0xBC, 0xA4, 0xBD, 0xA4, 0xBE, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0xA4, 0xBF, 0xA4, 0xC0, /* 0xC0-0xC3 */
+ 0xA4, 0xC1, 0xA4, 0xC2, 0xA4, 0xC3, 0xA4, 0xC4, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0xA4, 0xC5, 0xA4, 0xC6, /* 0xC8-0xCB */
+ 0xA4, 0xC7, 0xA4, 0xC8, 0xA4, 0xC9, 0xA4, 0xCA, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0xA4, 0xCB, 0xA4, 0xCC, /* 0xD0-0xD3 */
+ 0xA4, 0xCD, 0xA4, 0xCE, 0xA4, 0xCF, 0xA4, 0xD0, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0xA4, 0xD1, 0xA4, 0xD2, /* 0xD8-0xDB */
+ 0xA4, 0xD3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0xA1, 0xCB, 0xA1, 0xCC, 0xA1, 0xFE, 0xA3, 0xFE, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0xA1, 0xCD, 0xA3, 0xDC, 0x00, 0x00, /* 0xE4-0xE7 */
+};
+
+static unsigned char *page_uni2charset[256] = {
+ NULL, u2c_01, u2c_02, u2c_03, u2c_04, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, u2c_11, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ u2c_20, u2c_21, u2c_22, u2c_23, u2c_24, u2c_25, u2c_26, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ u2c_30, u2c_31, u2c_32, u2c_33, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, u2c_4E, u2c_4F,
+ u2c_50, u2c_51, u2c_52, u2c_53, u2c_54, u2c_55, u2c_56, u2c_57,
+ u2c_58, u2c_59, u2c_5A, u2c_5B, u2c_5C, u2c_5D, u2c_5E, u2c_5F,
+ u2c_60, u2c_61, u2c_62, u2c_63, u2c_64, u2c_65, u2c_66, u2c_67,
+ u2c_68, u2c_69, u2c_6A, u2c_6B, u2c_6C, u2c_6D, u2c_6E, u2c_6F,
+ u2c_70, u2c_71, u2c_72, u2c_73, u2c_74, u2c_75, u2c_76, u2c_77,
+ u2c_78, u2c_79, u2c_7A, u2c_7B, u2c_7C, u2c_7D, u2c_7E, u2c_7F,
+ u2c_80, u2c_81, u2c_82, u2c_83, u2c_84, u2c_85, u2c_86, u2c_87,
+ u2c_88, u2c_89, u2c_8A, u2c_8B, u2c_8C, u2c_8D, u2c_8E, u2c_8F,
+ u2c_90, u2c_91, u2c_92, u2c_93, u2c_94, u2c_95, u2c_96, u2c_97,
+ u2c_98, u2c_99, u2c_9A, u2c_9B, u2c_9C, u2c_9D, u2c_9E, u2c_9F,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, u2c_AC, u2c_AD, u2c_AE, u2c_AF,
+ u2c_B0, u2c_B1, u2c_B2, u2c_B3, u2c_B4, u2c_B5, u2c_B6, u2c_B7,
+ u2c_B8, u2c_B9, u2c_BA, u2c_BB, u2c_BC, u2c_BD, u2c_BE, u2c_BF,
+ u2c_C0, u2c_C1, u2c_C2, u2c_C3, u2c_C4, u2c_C5, u2c_C6, u2c_C7,
+ u2c_C8, u2c_C9, u2c_CA, u2c_CB, u2c_CC, u2c_CD, u2c_CE, u2c_CF,
+ u2c_D0, u2c_D1, u2c_D2, u2c_D3, u2c_D4, u2c_D5, u2c_D6, u2c_D7,
+ NULL, NULL, NULL, NULL, u2c_DC, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, u2c_F9, u2c_FA, NULL, NULL, NULL, NULL, u2c_FF, };
+
+static unsigned char charset2lower[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40-0x47 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x48-0x4f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50-0x57 */
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0x88-0x8f */
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static unsigned char charset2upper[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0x88-0x8f */
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static int uni2char(const wchar_t uni,
+ unsigned char *out, int boundlen)
+{
+ unsigned char *uni2charset;
+ unsigned char cl = uni&0xFF;
+ unsigned char ch = (uni>>8)&0xFF;
+ int n;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+
+ uni2charset = page_uni2charset[ch];
+ if (uni2charset) {
+ if (boundlen <= 1)
+ return -ENAMETOOLONG;
+ out[0] = uni2charset[cl*2];
+ out[1] = uni2charset[cl*2+1];
+ if (out[0] == 0x00 && out[1] == 0x00)
+ return -EINVAL;
+ n = 2;
+ } else if (ch==0 && cl) {
+ out[0] = cl;
+ n = 1;
+ }
+ else
+ return -EINVAL;
+
+ return n;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen,
+ wchar_t *uni)
+{
+ unsigned char ch, cl;
+ wchar_t *charset2uni;
+ int n;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ if (boundlen == 1) {
+ *uni = rawstring[0];
+ return 1;
+ }
+
+ ch = rawstring[0];
+ cl = rawstring[1];
+
+ charset2uni = page_charset2uni[ch];
+ if (charset2uni && cl) {
+ *uni = charset2uni[cl];
+ if (*uni == 0x0000)
+ return -EINVAL;
+ n = 2;
+ } else{
+ *uni = ch;
+ n = 1;
+ }
+ return n;
+}
+
+static struct nls_table table = {
+ "cp949",
+ uni2char,
+ char2uni,
+ charset2lower,
+ charset2upper,
+ THIS_MODULE,
+};
+
+static int __init init_nls_cp949(void)
+{
+ return register_nls(&table);
+}
+
+static void __exit exit_nls_cp949(void)
+{
+ unregister_nls(&table);
+}
+
+module_init(init_nls_cp949)
+module_exit(exit_nls_cp949)
+MODULE_LICENSE("Dual BSD/GPL");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ *
+---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/fs/nls/nls_cp950.c b/fs/nls/nls_cp950.c
new file mode 100644
index 00000000000000..10dcc45e64376b
--- /dev/null
+++ b/fs/nls/nls_cp950.c
@@ -0,0 +1,9501 @@
+/*
+ * linux/fs/nls_cp950.c
+ *
+ * Charset cp950 translation tables.
+ * This translation table was generated automatically, the
+ * original table can be download from the Microsoft website.
+ * (http://www.microsoft.com/typography/unicode/unicodecp.htm)
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static wchar_t c2u_A1[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x3000,0xFF0C,0x3001,0x3002,0xFF0E,0x2027,0xFF1B,0xFF1A,/* 0x40-0x47 */
+ 0xFF1F,0xFF01,0xFE30,0x2026,0x2025,0xFE50,0xFF64,0xFE52,/* 0x48-0x4F */
+ 0x00B7,0xFE54,0xFE55,0xFE56,0xFE57,0xFF5C,0x2013,0xFE31,/* 0x50-0x57 */
+ 0x2014,0xFE33,0x2574,0xFE34,0xFE4F,0xFF08,0xFF09,0xFE35,/* 0x58-0x5F */
+ 0xFE36,0xFF5B,0xFF5D,0xFE37,0xFE38,0xFF3B,0xFF3D,0xFE39,/* 0x60-0x67 */
+ 0xFE3A,0x3010,0x3011,0xFE3B,0xFE3C,0x300A,0x300B,0xFE3D,/* 0x68-0x6F */
+ 0xFE3E,0x3008,0x3009,0xFF3E,0xFE40,0x300C,0x300D,0xFE41,/* 0x70-0x77 */
+ 0xFE42,0x300E,0x300F,0xFE43,0xFE44,0xFE59,0xFE5A,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0xFE5B,0xFE5C,0xFE5D,0xFE5E,0xFF40,0xFF07,0x201C,/* 0xA0-0xA7 */
+ 0xFF02,0x301D,0x301E,0x2035,0x2032,0xFF03,0xFF06,0xFF0A,/* 0xA8-0xAF */
+ 0x203B,0x00A7,0x3003,0x25CB,0x25CF,0x25B3,0x25B2,0x25CE,/* 0xB0-0xB7 */
+ 0x2606,0x2605,0x25C7,0x25C6,0x25A1,0x25A0,0x25BD,0x25BC,/* 0xB8-0xBF */
+ 0x32A3,0x2105,0x0305,0xFFE3,0xFF3F,0x02CD,0xFE49,0xFE4A,/* 0xC0-0xC7 */
+ 0xFE4D,0xFE4E,0xFE4B,0xFE4C,0xFE5F,0xFE60,0xFE61,0xFF0B,/* 0xC8-0xCF */
+ 0xFF0D,0x00D7,0x00F7,0x00B1,0x221A,0xFF1C,0xFF1E,0xFF1D,/* 0xD0-0xD7 */
+ 0x2266,0x2267,0x2260,0x221E,0x2252,0x2263,0xFE62,0xFE63,/* 0xD8-0xDF */
+ 0xFE64,0xFE65,0xFE66,0xFF5E,0x2229,0x222A,0x22A5,0x2220,/* 0xE0-0xE7 */
+ 0x221F,0x22BF,0x33D2,0x33D1,0x222B,0x222E,0x2235,0x2234,/* 0xE8-0xEF */
+ 0x2640,0x2642,0x2641,0x2609,0x2191,0x2193,0x2190,0x2192,/* 0xF0-0xF7 */
+ 0x2196,0x2197,0x2199,0x2198,0x2225,0x2223,0xFF0F,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_A2[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0xFF3C,0x2215,0xFE68,0xFF04,0xFFE5,0x3012,0xFFE0,0xFFE1,/* 0x40-0x47 */
+ 0xFF05,0xFF20,0x2103,0x2109,0xFE69,0xFE6A,0xFE6B,0x33D5,/* 0x48-0x4F */
+ 0x339C,0x339D,0x339E,0x33CE,0x33A1,0x338E,0x338F,0x33C4,/* 0x50-0x57 */
+ 0x2218,0x5159,0x515B,0x515E,0x515D,0x5161,0x5163,0x55E7,/* 0x58-0x5F */
+ 0x74E9,0x7CCE,0x2581,0x2582,0x2583,0x2584,0x2585,0x2586,/* 0x60-0x67 */
+ 0x2587,0x2588,0x258F,0x258E,0x258D,0x258C,0x258B,0x258A,/* 0x68-0x6F */
+ 0x2589,0x253C,0x2534,0x252C,0x2524,0x251C,0x2594,0x2500,/* 0x70-0x77 */
+ 0x2502,0x2595,0x250C,0x2510,0x2514,0x2518,0x256D,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x256E,0x2570,0x256F,0x0000,0x0000,0x0000,0x0000,/* 0xA0-0xA7 */
+ 0x25E2,0x25E3,0x25E5,0x25E4,0x2571,0x2572,0x2573,0xFF10,/* 0xA8-0xAF */
+ 0xFF11,0xFF12,0xFF13,0xFF14,0xFF15,0xFF16,0xFF17,0xFF18,/* 0xB0-0xB7 */
+ 0xFF19,0x2160,0x2161,0x2162,0x2163,0x2164,0x2165,0x2166,/* 0xB8-0xBF */
+ 0x2167,0x2168,0x2169,0x3021,0x3022,0x3023,0x3024,0x3025,/* 0xC0-0xC7 */
+ 0x3026,0x3027,0x3028,0x3029,0x0000,0x5344,0x0000,0xFF21,/* 0xC8-0xCF */
+ 0xFF22,0xFF23,0xFF24,0xFF25,0xFF26,0xFF27,0xFF28,0xFF29,/* 0xD0-0xD7 */
+ 0xFF2A,0xFF2B,0xFF2C,0xFF2D,0xFF2E,0xFF2F,0xFF30,0xFF31,/* 0xD8-0xDF */
+ 0xFF32,0xFF33,0xFF34,0xFF35,0xFF36,0xFF37,0xFF38,0xFF39,/* 0xE0-0xE7 */
+ 0xFF3A,0xFF41,0xFF42,0xFF43,0xFF44,0xFF45,0xFF46,0xFF47,/* 0xE8-0xEF */
+ 0xFF48,0xFF49,0xFF4A,0xFF4B,0xFF4C,0xFF4D,0xFF4E,0xFF4F,/* 0xF0-0xF7 */
+ 0xFF50,0xFF51,0xFF52,0xFF53,0xFF54,0xFF55,0xFF56,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_A3[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0xFF57,0xFF58,0xFF59,0xFF5A,0x0391,0x0392,0x0393,0x0394,/* 0x40-0x47 */
+ 0x0395,0x0396,0x0397,0x0398,0x0399,0x039A,0x039B,0x039C,/* 0x48-0x4F */
+ 0x039D,0x039E,0x039F,0x03A0,0x03A1,0x03A3,0x03A4,0x03A5,/* 0x50-0x57 */
+ 0x03A6,0x03A7,0x03A8,0x03A9,0x03B1,0x03B2,0x03B3,0x03B4,/* 0x58-0x5F */
+ 0x03B5,0x03B6,0x03B7,0x03B8,0x03B9,0x03BA,0x03BB,0x03BC,/* 0x60-0x67 */
+ 0x03BD,0x03BE,0x03BF,0x03C0,0x03C1,0x03C3,0x03C4,0x03C5,/* 0x68-0x6F */
+ 0x03C6,0x03C7,0x03C8,0x03C9,0x3105,0x3106,0x3107,0x3108,/* 0x70-0x77 */
+ 0x3109,0x310A,0x310B,0x310C,0x310D,0x310E,0x310F,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x3110,0x3111,0x3112,0x3113,0x3114,0x3115,0x3116,/* 0xA0-0xA7 */
+ 0x3117,0x3118,0x3119,0x311A,0x311B,0x311C,0x311D,0x311E,/* 0xA8-0xAF */
+ 0x311F,0x3120,0x3121,0x3122,0x3123,0x3124,0x3125,0x3126,/* 0xB0-0xB7 */
+ 0x3127,0x3128,0x3129,0x2024,0x02C9,0x02CA,0x02C7,0x02CB,/* 0xB8-0xBF */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xC0-0xC7 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xC8-0xCF */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xD0-0xD7 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xD8-0xDF */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xE0-0xE7 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xE8-0xEF */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xF0-0xF7 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_A4[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x4E00,0x4E59,0x4E01,0x4E03,0x4E43,0x4E5D,0xF9BA,0x4E8C,/* 0x40-0x47 */
+ 0x4EBA,0x513F,0x5165,0x516B,0x51E0,0x5200,0x5201,0xF98A,/* 0x48-0x4F */
+ 0x5315,0x5341,0x535C,0x53C8,0x4E09,0x4E0B,0x4E08,0x4E0A,/* 0x50-0x57 */
+ 0x4E2B,0x4E38,0x51E1,0x4E45,0x4E48,0x4E5F,0x4E5E,0x4E8E,/* 0x58-0x5F */
+ 0x4EA1,0x5140,0x5203,0x52FA,0x5343,0x53C9,0x53E3,0x571F,/* 0x60-0x67 */
+ 0x58EB,0x5915,0x5927,0xF981,0x5B50,0x5B51,0x5B53,0x5BF8,/* 0x68-0x6F */
+ 0x5C0F,0x5C22,0x5C38,0x5C71,0x5DDD,0x5DE5,0x5DF1,0x5DF2,/* 0x70-0x77 */
+ 0x5DF3,0x5DFE,0x5E72,0x5EFE,0x5F0B,0x5F13,0x624D,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x4E11,0x4E10,0xF967,0x4E2D,0x4E30,0xF95E,0x4E4B,/* 0xA0-0xA7 */
+ 0x5C39,0x4E88,0x4E91,0x4E95,0x4E92,0x4E94,0x4EA2,0x4EC1,/* 0xA8-0xAF */
+ 0xF9FD,0x4EC3,0x4EC6,0x4EC7,0x4ECD,0x4ECA,0x4ECB,0x4EC4,/* 0xB0-0xB7 */
+ 0x5143,0x5141,0x5167,0xF9D1,0x516E,0x516C,0x5197,0x51F6,/* 0xB8-0xBF */
+ 0x5206,0xFA00,0x5208,0x52FB,0x52FE,0x52FF,0x5316,0x5339,/* 0xC0-0xC7 */
+ 0x5348,0x5347,0x5345,0x535E,0x5384,0x53CB,0x53CA,0x53CD,/* 0xC8-0xCF */
+ 0x58EC,0x5929,0x592B,0x592A,0x592D,0x5B54,0x5C11,0x5C24,/* 0xD0-0xD7 */
+ 0x5C3A,0x5C6F,0x5DF4,0x5E7B,0x5EFF,0x5F14,0x5F15,0x5FC3,/* 0xD8-0xDF */
+ 0x6208,0x6236,0x624B,0x624E,0x652F,0x6587,0x6597,0x65A4,/* 0xE0-0xE7 */
+ 0x65B9,0x65E5,0x66F0,0x6708,0x6728,0x6B20,0x6B62,0x6B79,/* 0xE8-0xEF */
+ 0x6BCB,0x6BD4,0x6BDB,0x6C0F,0x6C34,0x706B,0x722A,0x7236,/* 0xF0-0xF7 */
+ 0x723B,0x7247,0x7259,0x725B,0x72AC,0x738B,0x4E19,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_A5[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x4E16,0x4E15,0x4E14,0x4E18,0x4E3B,0x4E4D,0x4E4F,0x4E4E,/* 0x40-0x47 */
+ 0x4EE5,0x4ED8,0x4ED4,0x4ED5,0x4ED6,0x4ED7,0x4EE3,0xF9A8,/* 0x48-0x4F */
+ 0x4ED9,0x4EDE,0x5145,0x5144,0x5189,0x518A,0x51AC,0x51F9,/* 0x50-0x57 */
+ 0x51FA,0x51F8,0x520A,0x52A0,0x529F,0x5305,0x5306,0xF963,/* 0x58-0x5F */
+ 0x531D,0x4EDF,0x534A,0x5349,0x5361,0x5360,0x536F,0x536E,/* 0x60-0x67 */
+ 0x53BB,0x53EF,0x53E4,0x53F3,0x53EC,0x53EE,0x53E9,0x53E8,/* 0x68-0x6F */
+ 0x53FC,0x53F8,0x53F5,0x53EB,0x53E6,0x53EA,0x53F2,0x53F1,/* 0x70-0x77 */
+ 0x53F0,0xF906,0x53ED,0x53FB,0x56DB,0x56DA,0x5916,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x592E,0x5931,0x5974,0x5976,0x5B55,0x5B83,0x5C3C,/* 0xA0-0xA7 */
+ 0x5DE8,0x5DE7,0x5DE6,0x5E02,0x5E03,0x5E73,0x5E7C,0x5F01,/* 0xA8-0xAF */
+ 0x5F18,0x5F17,0x5FC5,0x620A,0x6253,0x6254,0x6252,0x6251,/* 0xB0-0xB7 */
+ 0x65A5,0x65E6,0x672E,0x672C,0x672A,0x672B,0x672D,0x6B63,/* 0xB8-0xBF */
+ 0x6BCD,0x6C11,0x6C10,0x6C38,0x6C41,0x6C40,0x6C3E,0x72AF,/* 0xC0-0xC7 */
+ 0x7384,0x7389,0x74DC,0x74E6,0x7518,0x751F,0x7528,0x7529,/* 0xC8-0xCF */
+ 0x7530,0x7531,0x7532,0x7533,0x758B,0x767D,0x76AE,0x76BF,/* 0xD0-0xD7 */
+ 0x76EE,0x77DB,0x77E2,0x77F3,0x793A,0x79BE,0x7A74,0xF9F7,/* 0xD8-0xDF */
+ 0x4E1E,0x4E1F,0x4E52,0x4E53,0x4E69,0x4E99,0x4EA4,0x4EA6,/* 0xE0-0xE7 */
+ 0x4EA5,0x4EFF,0x4F09,0x4F19,0x4F0A,0x4F15,0x4F0D,0x4F10,/* 0xE8-0xEF */
+ 0x4F11,0x4F0F,0x4EF2,0x4EF6,0x4EFB,0x4EF0,0x4EF3,0x4EFD,/* 0xF0-0xF7 */
+ 0x4F01,0x4F0B,0x5149,0x5147,0x5146,0x5148,0x5168,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_A6[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x5171,0x518D,0x51B0,0xF99C,0x5211,0x5212,0x520E,0x5216,/* 0x40-0x47 */
+ 0xF99D,0x5308,0x5321,0x5320,0x5370,0x5371,0x5409,0xF9DE,/* 0x48-0x4F */
+ 0x540C,0x540A,0x5410,0x5401,0x540B,0x5404,0x5411,0x540D,/* 0x50-0x57 */
+ 0x5408,0x5403,0x540E,0x5406,0x5412,0x56E0,0x56DE,0x56DD,/* 0x58-0x5F */
+ 0x5733,0x5730,0x5728,0x572D,0x572C,0x572F,0x5729,0x5919,/* 0x60-0x67 */
+ 0x591A,0x5937,0x5938,0x5984,0x5978,0x5983,0x597D,0x5979,/* 0x68-0x6F */
+ 0x5982,0x5981,0x5B57,0x5B58,0x5B87,0x5B88,0xFA04,0x5B89,/* 0x70-0x77 */
+ 0x5BFA,0x5C16,0x5C79,0x5DDE,0x5E06,0x5E76,0xF98E,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x5F0F,0x5F1B,0x5FD9,0x5FD6,0x620E,0x620C,0x620D,/* 0xA0-0xA7 */
+ 0x6210,0x6263,0x625B,0x6258,0x6536,0x65E9,0x65E8,0x65EC,/* 0xA8-0xAF */
+ 0x65ED,0x66F2,0x66F3,0x6709,0x673D,0x6734,0x6731,0x6735,/* 0xB0-0xB7 */
+ 0x6B21,0x6B64,0x6B7B,0x6C16,0x6C5D,0x6C57,0x6C59,0x6C5F,/* 0xB8-0xBF */
+ 0x6C60,0x6C50,0x6C55,0x6C61,0x6C5B,0x6C4D,0x6C4E,0x7070,/* 0xC0-0xC7 */
+ 0x725F,0x725D,0x767E,0x7AF9,0x7C73,0x7CF8,0x7F36,0x7F8A,/* 0xC8-0xCF */
+ 0xFA1E,0xF934,0x8003,0x800C,0x8012,0x8033,0x807F,0x8089,/* 0xD0-0xD7 */
+ 0xF953,0x808C,0x81E3,0x81EA,0x81F3,0x81FC,0x820C,0x821B,/* 0xD8-0xDF */
+ 0x821F,0x826E,0x8272,0x827E,0x866B,0x8840,0xFA08,0x8863,/* 0xE0-0xE7 */
+ 0x897F,0x9621,0xF905,0x4EA8,0x4F4D,0x4F4F,0x4F47,0x4F57,/* 0xE8-0xEF */
+ 0x4F5E,0x4F34,0x4F5B,0x4F55,0x4F30,0x4F50,0x4F51,0x4F3D,/* 0xF0-0xF7 */
+ 0x4F3A,0x4F38,0x4F43,0x4F54,0x4F3C,0x4F46,0x4F63,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_A7[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x4F5C,0x4F60,0x4F2F,0x4F4E,0x4F36,0x4F59,0x4F5D,0x4F48,/* 0x40-0x47 */
+ 0x4F5A,0x514C,0x514B,0x514D,0x5175,0x51B6,0xF92E,0x5225,/* 0x48-0x4F */
+ 0x5224,0xF9DD,0x522A,0x5228,0x52AB,0x52A9,0x52AA,0x52AC,/* 0x50-0x57 */
+ 0x5323,0x5373,0xF91C,0xF9ED,0x542D,0x541E,0x543E,0x5426,/* 0x58-0x5F */
+ 0x544E,0x5427,0x5446,0x5443,0x5433,0x5448,0xF980,0x541B,/* 0x60-0x67 */
+ 0x5429,0x544A,0x5439,0x543B,0x5438,0x542E,0x5435,0x5436,/* 0x68-0x6F */
+ 0x5420,0x543C,0x5440,0x5431,0x542B,0x541F,0x542C,0x56EA,/* 0x70-0x77 */
+ 0x56F0,0x56E4,0x56EB,0x574A,0x5751,0x5740,0x574D,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x5747,0x574E,0x573E,0x5750,0x574F,0x573B,0x58EF,/* 0xA0-0xA7 */
+ 0x593E,0x599D,0x5992,0x59A8,0x599E,0x59A3,0x5999,0x5996,/* 0xA8-0xAF */
+ 0x598D,0x59A4,0x5993,0x598A,0x59A5,0x5B5D,0x5B5C,0x5B5A,/* 0xB0-0xB7 */
+ 0x5B5B,0x5B8C,0x5B8B,0x5B8F,0x5C2C,0x5C40,0x5C41,0xF9BD,/* 0xB8-0xBF */
+ 0x5C3E,0x5C90,0x5C91,0x5C94,0x5C8C,0x5DEB,0x5E0C,0x5E8F,/* 0xC0-0xC7 */
+ 0x5E87,0x5E8A,0x5EF7,0xF943,0x5F1F,0x5F64,0x5F62,0x5F77,/* 0xC8-0xCF */
+ 0x5F79,0x5FD8,0x5FCC,0x5FD7,0x5FCD,0x5FF1,0x5FEB,0x5FF8,/* 0xD0-0xD7 */
+ 0x5FEA,0x6212,0x6211,0x6284,0x6297,0x6296,0x6280,0x6276,/* 0xD8-0xDF */
+ 0x6289,0x626D,0x628A,0x627C,0x627E,0x6279,0x6273,0x6292,/* 0xE0-0xE7 */
+ 0x626F,0x6298,0x626E,0x6295,0x6293,0x6291,0x6286,0x6539,/* 0xE8-0xEF */
+ 0x653B,0x6538,0x65F1,0xF901,0x675F,0xF9E1,0x674F,0x6750,/* 0xF0-0xF7 */
+ 0x6751,0x675C,0x6756,0x675E,0x6749,0x6746,0x6760,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_A8[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x6753,0x6757,0x6B65,0x6BCF,0x6C42,0x6C5E,0x6C99,0x6C81,/* 0x40-0x47 */
+ 0xF972,0x6C89,0x6C85,0x6C9B,0x6C6A,0x6C7A,0x6C90,0x6C70,/* 0x48-0x4F */
+ 0x6C8C,0x6C68,0x6C96,0x6C92,0x6C7D,0x6C83,0x6C72,0x6C7E,/* 0x50-0x57 */
+ 0x6C74,0x6C86,0x6C76,0x6C8D,0x6C94,0x6C98,0x6C82,0x7076,/* 0x58-0x5F */
+ 0x707C,0x707D,0x7078,0xF946,0x7261,0x7260,0x72C4,0x72C2,/* 0x60-0x67 */
+ 0x7396,0x752C,0x752B,0x7537,0x7538,0x7682,0x76EF,0x77E3,/* 0x68-0x6F */
+ 0x79C1,0x79C0,0x79BF,0x7A76,0x7CFB,0x7F55,0x8096,0x8093,/* 0x70-0x77 */
+ 0x809D,0x8098,0x809B,0x809A,0x80B2,0xF97C,0x8292,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x828B,0x828D,0xFA0A,0x89D2,0x8A00,0x8C37,0x8C46,/* 0xA0-0xA7 */
+ 0x8C55,0x8C9D,0x8D64,0x8D70,0x8DB3,0x8EAB,0xF902,0x8F9B,/* 0xA8-0xAF */
+ 0xF971,0x8FC2,0x8FC6,0x8FC5,0x8FC4,0x5DE1,0x9091,0x90A2,/* 0xB0-0xB7 */
+ 0x90AA,0x90A6,0x90A3,0x9149,0x91C6,0xF9E9,0x9632,0xF9C6,/* 0xB8-0xBF */
+ 0x9631,0x962A,0x962C,0x4E26,0x4E56,0x4E73,0x4E8B,0x4E9B,/* 0xC0-0xC7 */
+ 0x4E9E,0x4EAB,0x4EAC,0x4F6F,0x4F9D,0x4F8D,0x4F73,0x4F7F,/* 0xC8-0xCF */
+ 0x4F6C,0x4F9B,0xF9B5,0xF92D,0x4F83,0x4F70,0x4F75,0x4F88,/* 0xD0-0xD7 */
+ 0x4F69,0x4F7B,0x4F96,0x4F7E,0x4F8F,0x4F91,0x4F7A,0x5154,/* 0xD8-0xDF */
+ 0x5152,0x5155,0xF978,0x5177,0x5176,0x5178,0x51BD,0x51FD,/* 0xE0-0xE7 */
+ 0x523B,0x5238,0x5237,0xF9FF,0x5230,0x522E,0x5236,0x5241,/* 0xE8-0xEF */
+ 0x52BE,0x52BB,0x5352,0x5354,0x5353,0x5351,0x5366,0x5377,/* 0xF0-0xF7 */
+ 0x5378,0x5379,0x53D6,0x53D4,0x53D7,0x5473,0x5475,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_A9[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x5496,0x5478,0x5495,0x5480,0x547B,0x5477,0x5484,0x5492,/* 0x40-0x47 */
+ 0x5486,0x547C,0x5490,0x5471,0x5476,0x548C,0x549A,0x5462,/* 0x48-0x4F */
+ 0x5468,0x548B,0x547D,0x548E,0x56FA,0x5783,0x5777,0x576A,/* 0x50-0x57 */
+ 0x5769,0x5761,0x5766,0x5764,0x577C,0x591C,0x5949,0x5947,/* 0x58-0x5F */
+ 0xF90C,0x5944,0x5954,0x59BE,0x59BB,0x59D4,0x59B9,0x59AE,/* 0x60-0x67 */
+ 0x59D1,0x59C6,0x59D0,0x59CD,0x59CB,0x59D3,0x59CA,0x59AF,/* 0x68-0x6F */
+ 0x59B3,0x59D2,0x59C5,0x5B5F,0x5B64,0x5B63,0x5B97,0x5B9A,/* 0x70-0x77 */
+ 0x5B98,0x5B9C,0x5B99,0x5B9B,0x5C1A,0x5C48,0x5C45,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x5C46,0x5CB7,0x5CA1,0x5CB8,0x5CA9,0x5CAB,0x5CB1,/* 0xA0-0xA7 */
+ 0x5CB3,0x5E18,0x5E1A,0x5E16,0x5E15,0x5E1B,0x5E11,0x5E78,/* 0xA8-0xAF */
+ 0x5E9A,0x5E97,0x5E9C,0x5E95,0x5E96,0x5EF6,0x5F26,0x5F27,/* 0xB0-0xB7 */
+ 0x5F29,0x5F80,0x5F81,0x5F7F,0x5F7C,0x5FDD,0x5FE0,0x5FFD,/* 0xB8-0xBF */
+ 0xF9A3,0x5FFF,0x600F,0x6014,0x602F,0x6035,0x6016,0x602A,/* 0xC0-0xC7 */
+ 0x6015,0x6021,0x6027,0x6029,0x602B,0x601B,0x6216,0x6215,/* 0xC8-0xCF */
+ 0x623F,0x623E,0x6240,0x627F,0xF925,0x62CC,0x62C4,0x62BF,/* 0xD0-0xD7 */
+ 0x62C2,0x62B9,0x62D2,0x62DB,0x62AB,0xFA02,0x62D4,0x62CB,/* 0xD8-0xDF */
+ 0x62C8,0x62A8,0x62BD,0x62BC,0x62D0,0x62D9,0x62C7,0x62CD,/* 0xE0-0xE7 */
+ 0x62B5,0x62DA,0x62B1,0x62D8,0x62D6,0x62D7,0x62C6,0x62AC,/* 0xE8-0xEF */
+ 0x62CE,0x653E,0x65A7,0x65BC,0x65FA,0x6614,0xF9E0,0x660C,/* 0xF0-0xF7 */
+ 0x6606,0x6602,0x660E,0x6600,0x660F,0x6615,0x660A,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_AA[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x6607,0x670D,0x670B,0x676D,0x678B,0x6795,0x6771,0x679C,/* 0x40-0x47 */
+ 0x6773,0x6777,0x6787,0x679D,0xF9F4,0x676F,0x6770,0x677F,/* 0x48-0x4F */
+ 0x6789,0x677E,0x6790,0x6775,0x679A,0x6793,0x677C,0x676A,/* 0x50-0x57 */
+ 0x6772,0x6B23,0x6B66,0x6B67,0x6B7F,0x6C13,0x6C1B,0x6CE3,/* 0x58-0x5F */
+ 0x6CE8,0x6CF3,0x6CB1,0xF968,0xF9E3,0x6CB3,0x6CBD,0x6CBE,/* 0x60-0x67 */
+ 0x6CBC,0x6CE2,0x6CAB,0x6CD5,0x6CD3,0x6CB8,0x6CC4,0x6CB9,/* 0x68-0x6F */
+ 0x6CC1,0x6CAE,0x6CD7,0x6CC5,0x6CF1,0x6CBF,0x6CBB,0x6CE1,/* 0x70-0x77 */
+ 0x6CDB,0x6CCA,0x6CAC,0x6CEF,0x6CDC,0x6CD6,0x6CE0,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x7095,0x708E,0x7092,0x708A,0xF9FB,0x722C,0x722D,/* 0xA0-0xA7 */
+ 0x7238,0x7248,0x7267,0x7269,0xF9FA,0x72CE,0x72D9,0x72D7,/* 0xA8-0xAF */
+ 0x72D0,0x73A9,0x73A8,0x739F,0x73AB,0x73A5,0x753D,0x759D,/* 0xB0-0xB7 */
+ 0x7599,0x759A,0x7684,0x76C2,0x76F2,0x76F4,0x77E5,0x77FD,/* 0xB8-0xBF */
+ 0x793E,0x7940,0x7941,0x79C9,0x79C8,0x7A7A,0x7A79,0x7AFA,/* 0xC0-0xC7 */
+ 0x7CFE,0x7F54,0x7F8C,0x7F8B,0x8005,0x80BA,0x80A5,0x80A2,/* 0xC8-0xCF */
+ 0x80B1,0x80A1,0x80AB,0x80A9,0x80B4,0x80AA,0x80AF,0x81E5,/* 0xD0-0xD7 */
+ 0x81FE,0x820D,0x82B3,0x829D,0x8299,0x82AD,0x82BD,0x829F,/* 0xD8-0xDF */
+ 0x82B9,0x82B1,0x82AC,0x82A5,0x82AF,0x82B8,0x82A3,0x82B0,/* 0xE0-0xE7 */
+ 0x82BE,0x82B7,0x864E,0x8671,0x521D,0x8868,0x8ECB,0x8FCE,/* 0xE8-0xEF */
+ 0x8FD4,0x8FD1,0x90B5,0x90B8,0x90B1,0x90B6,0x91C7,0xF90A,/* 0xF0-0xF7 */
+ 0x9577,0x9580,0x961C,0x9640,0x963F,0x963B,0x9644,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_AB[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x9642,0x96B9,0x96E8,0x9752,0x975E,0x4E9F,0x4EAD,0xF977,/* 0x40-0x47 */
+ 0x4FE1,0x4FB5,0x4FAF,0xF965,0x4FE0,0x4FD1,0x4FCF,0x4FDD,/* 0x48-0x4F */
+ 0x4FC3,0x4FB6,0x4FD8,0x4FDF,0x4FCA,0x4FD7,0x4FAE,0x4FD0,/* 0x50-0x57 */
+ 0x4FC4,0x4FC2,0x4FDA,0x4FCE,0x4FDE,0x4FB7,0x5157,0x5192,/* 0x58-0x5F */
+ 0x5191,0x51A0,0x524E,0x5243,0x524A,0x524D,0x524C,0x524B,/* 0x60-0x67 */
+ 0x5247,0x52C7,0x52C9,0x52C3,0x52C1,0x530D,0x5357,0x537B,/* 0x68-0x6F */
+ 0x539A,0x53DB,0x54AC,0x54C0,0x54A8,0x54CE,0x54C9,0x54B8,/* 0x70-0x77 */
+ 0x54A6,0x54B3,0x54C7,0x54C2,0xF99E,0x54AA,0x54C1,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x54C4,0x54C8,0x54AF,0x54AB,0x54B1,0x54BB,0x54A9,/* 0xA0-0xA7 */
+ 0x54A7,0x54BF,0x56FF,0x5782,0x578B,0x57A0,0x57A3,0x57A2,/* 0xA8-0xAF */
+ 0x57CE,0x57AE,0x5793,0x5955,0xF909,0x594F,0x594E,0x5950,/* 0xB0-0xB7 */
+ 0x59DC,0x59D8,0x59FF,0x59E3,0x59E8,0x5A03,0x59E5,0x59EA,/* 0xB8-0xBF */
+ 0x59DA,0x59E6,0x5A01,0x59FB,0x5B69,0x5BA3,0x5BA6,0x5BA4,/* 0xC0-0xC7 */
+ 0x5BA2,0x5BA5,0x5C01,0x5C4E,0x5C4F,0x5C4D,0x5C4B,0x5CD9,/* 0xC8-0xCF */
+ 0x5CD2,0x5DF7,0x5E1D,0x5E25,0x5E1F,0x5E7D,0x5EA0,0xFA01,/* 0xD0-0xD7 */
+ 0x5EFA,0x5F08,0x5F2D,0x5F65,0x5F88,0x5F85,0x5F8A,0xF9D8,/* 0xD8-0xDF */
+ 0x5F87,0x5F8C,0x5F89,0xF960,0x601D,0x6020,0x6025,0x600E,/* 0xE0-0xE7 */
+ 0x6028,0x604D,0x6070,0x6068,0x6062,0x6046,0x6043,0x606C,/* 0xE8-0xEF */
+ 0x606B,0x606A,0x6064,0x6241,0x62DC,0x6316,0x6309,0x62FC,/* 0xF0-0xF7 */
+ 0x62ED,0x6301,0x62EE,0x62FD,0x6307,0x62F1,0x62F7,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_AC[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x62EF,0x62EC,0xF973,0x62F4,0x6311,0x6302,0x653F,0x6545,/* 0x40-0x47 */
+ 0x65AB,0x65BD,0x65E2,0x6625,0x662D,0x6620,0x6627,0x662F,/* 0x48-0x4F */
+ 0x661F,0x6628,0x6631,0x6624,0x66F7,0x67FF,0x67D3,0x67F1,/* 0x50-0x57 */
+ 0x67D4,0x67D0,0x67EC,0x67B6,0x67AF,0x67F5,0x67E9,0x67EF,/* 0x58-0x5F */
+ 0x67C4,0x67D1,0x67B4,0x67DA,0x67E5,0x67B8,0x67CF,0x67DE,/* 0x60-0x67 */
+ 0xF9C9,0x67B0,0x67D9,0x67E2,0x67DD,0x67D2,0x6B6A,0x6B83,/* 0x68-0x6F */
+ 0x6B86,0x6BB5,0x6BD2,0x6BD7,0x6C1F,0x6CC9,0x6D0B,0x6D32,/* 0x70-0x77 */
+ 0x6D2A,0xF9CA,0x6D25,0x6D0C,0x6D31,0xFA05,0x6D17,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x6D3B,0x6D3D,0x6D3E,0x6D36,0xF915,0x6CF5,0x6D39,/* 0xA0-0xA7 */
+ 0x6D27,0x6D38,0x6D29,0x6D2E,0x6D35,0x6D0E,0x6D2B,0x70AB,/* 0xA8-0xAF */
+ 0x70BA,0x70B3,0x70AC,0x70AF,0x70AD,0x70B8,0x70AE,0x70A4,/* 0xB0-0xB7 */
+ 0x7230,0x7272,0x726F,0x7274,0x72E9,0x72E0,0x72E1,0x73B7,/* 0xB8-0xBF */
+ 0x73CA,0x73BB,0xF9AD,0x73CD,0x73C0,0x73B3,0x751A,0x752D,/* 0xC0-0xC7 */
+ 0x754F,0x754C,0x754E,0x754B,0x75AB,0x75A4,0x75A5,0x75A2,/* 0xC8-0xCF */
+ 0x75A3,0x7678,0x7686,0x7687,0x7688,0x76C8,0x76C6,0x76C3,/* 0xD0-0xD7 */
+ 0x76C5,0xF96D,0x76F9,0x76F8,0x7709,0x770B,0x76FE,0x76FC,/* 0xD8-0xDF */
+ 0x7707,0x77DC,0x7802,0x7814,0x780C,0x780D,0x7946,0x7949,/* 0xE0-0xE7 */
+ 0x7948,0x7947,0x79B9,0x79BA,0x79D1,0x79D2,0x79CB,0x7A7F,/* 0xE8-0xEF */
+ 0x7A81,0x7AFF,0x7AFD,0x7C7D,0x7D02,0x7D05,0x7D00,0x7D09,/* 0xF0-0xF7 */
+ 0x7D07,0x7D04,0x7D06,0x7F38,0x7F8E,0x7FBF,0x8004,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_AD[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x8010,0x800D,0x8011,0x8036,0x80D6,0x80E5,0x80DA,0x80C3,/* 0x40-0x47 */
+ 0x80C4,0x80CC,0x80E1,0x80DB,0x80CE,0x80DE,0x80E4,0x80DD,/* 0x48-0x4F */
+ 0x81F4,0x8222,0x82E7,0x8303,0x8305,0x82E3,0x82DB,0x82E6,/* 0x50-0x57 */
+ 0x8304,0xF974,0x8302,0x8309,0x82D2,0x82D7,0x82F1,0x8301,/* 0x58-0x5F */
+ 0x82DC,0x82D4,0x82D1,0x82DE,0x82D3,0x82DF,0x82EF,0x8306,/* 0x60-0x67 */
+ 0x8650,0x8679,0x867B,0x867A,0x884D,0x886B,0x8981,0x89D4,/* 0x68-0x6F */
+ 0x8A08,0x8A02,0x8A03,0x8C9E,0x8CA0,0x8D74,0x8D73,0x8DB4,/* 0x70-0x77 */
+ 0x8ECD,0x8ECC,0x8FF0,0x8FE6,0x8FE2,0x8FEA,0x8FE5,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x8FED,0x8FEB,0x8FE4,0x8FE8,0x90CA,0x90CE,0x90C1,/* 0xA0-0xA7 */
+ 0x90C3,0x914B,0x914A,0x91CD,0x9582,0x9650,0xF951,0x964C,/* 0xA8-0xAF */
+ 0xFA09,0x9762,0x9769,0x97CB,0x97ED,0x97F3,0x9801,0x98A8,/* 0xB0-0xB7 */
+ 0x98DB,0x98DF,0x9996,0x9999,0x4E58,0x4EB3,0x500C,0x500D,/* 0xB8-0xBF */
+ 0x5023,0x4FEF,0x5026,0x5025,0x4FF8,0x5029,0x5016,0x5006,/* 0xC0-0xC7 */
+ 0x503C,0x501F,0x501A,0x5012,0x5011,0x4FFA,0x5000,0x5014,/* 0xC8-0xCF */
+ 0x5028,0x4FF1,0x5021,0x500B,0x5019,0x5018,0x4FF3,0x4FEE,/* 0xD0-0xD7 */
+ 0x502D,0x502A,0x4FFE,0xF9D4,0x5009,0x517C,0x51A4,0x51A5,/* 0xD8-0xDF */
+ 0x51A2,0x51CD,0xF955,0x51C6,0x51CB,0x5256,0x525C,0x5254,/* 0xE0-0xE7 */
+ 0x525B,0x525D,0x532A,0x537F,0x539F,0x539D,0x53DF,0x54E8,/* 0xE8-0xEF */
+ 0x5510,0x5501,0x5537,0x54FC,0x54E5,0x54F2,0x5506,0x54FA,/* 0xF0-0xF7 */
+ 0x5514,0x54E9,0x54ED,0x54E1,0x5509,0x54EE,0x54EA,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_AE[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x54E6,0x5527,0x5507,0x54FD,0x550F,0x5703,0x5704,0x57C2,/* 0x40-0x47 */
+ 0x57D4,0x57CB,0x57C3,0x5809,0x590F,0x5957,0x5958,0x595A,/* 0x48-0x4F */
+ 0x5A11,0x5A18,0x5A1C,0x5A1F,0x5A1B,0x5A13,0x59EC,0x5A20,/* 0x50-0x57 */
+ 0x5A23,0x5A29,0x5A25,0x5A0C,0x5A09,0x5B6B,0x5C58,0x5BB0,/* 0x58-0x5F */
+ 0x5BB3,0x5BB6,0x5BB4,0x5BAE,0x5BB5,0x5BB9,0x5BB8,0x5C04,/* 0x60-0x67 */
+ 0x5C51,0x5C55,0x5C50,0x5CED,0x5CFD,0x5CFB,0x5CEA,0x5CE8,/* 0x68-0x6F */
+ 0x5CF0,0x5CF6,0x5D01,0x5CF4,0x5DEE,0x5E2D,0x5E2B,0x5EAB,/* 0x70-0x77 */
+ 0x5EAD,0x5EA7,0x5F31,0x5F92,0x5F91,0x5F90,0x6059,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x6063,0x6065,0x6050,0x6055,0x606D,0x6069,0x606F,/* 0xA0-0xA7 */
+ 0x6084,0x609F,0x609A,0x608D,0x6094,0x608C,0x6085,0x6096,/* 0xA8-0xAF */
+ 0x6247,0x62F3,0x6308,0x62FF,0x634E,0x633E,0x632F,0x6355,/* 0xB0-0xB7 */
+ 0x6342,0x6346,0x634F,0x6349,0x633A,0x6350,0x633D,0x632A,/* 0xB8-0xBF */
+ 0x632B,0x6328,0x634D,0x634C,0x6548,0x6549,0xF9BE,0x65C1,/* 0xC0-0xC7 */
+ 0xF983,0x6642,0x6649,0x664F,0x6643,0x6652,0x664C,0x6645,/* 0xC8-0xCF */
+ 0x6641,0x66F8,0x6714,0x6715,0xF929,0x6821,0x6838,0x6848,/* 0xD0-0xD7 */
+ 0x6846,0x6853,0x6839,0x6842,0x6854,0x6829,0x68B3,0xF9DA,/* 0xD8-0xDF */
+ 0x684C,0x6851,0x683D,0x67F4,0x6850,0x6840,0x683C,0x6843,/* 0xE0-0xE7 */
+ 0x682A,0x6845,0x6813,0x6818,0x6841,0x6B8A,0x6B89,0x6BB7,/* 0xE8-0xEF */
+ 0x6C23,0x6C27,0x6C28,0x6C26,0x6C24,0x6CF0,0xF92A,0x6D95,/* 0xF0-0xF7 */
+ 0x6D88,0x6D87,0x6D66,0x6D78,0x6D77,0x6D59,0x6D93,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_AF[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x6D6C,0x6D89,0x6D6E,0x6D5A,0x6D74,0x6D69,0x6D8C,0x6D8A,/* 0x40-0x47 */
+ 0x6D79,0x6D85,0x6D65,0x6D94,0x70CA,0x70D8,0x70E4,0xF916,/* 0x48-0x4F */
+ 0xF99F,0x70CF,0x7239,0x7279,0xF92B,0x72F9,0x72FD,0x72F8,/* 0x50-0x57 */
+ 0x72F7,0x7386,0x73ED,0xF9CC,0x73EE,0x73E0,0x73EA,0xF917,/* 0x58-0x5F */
+ 0x7554,0x755D,0x755C,0x755A,0xF9CD,0x75BE,0x75C5,0x75C7,/* 0x60-0x67 */
+ 0x75B2,0x75B3,0x75BD,0x75BC,0x75B9,0x75C2,0x75B8,0x768B,/* 0x68-0x6F */
+ 0x76B0,0xFA17,0x76CD,0x76CE,0x7729,0x771F,0x7720,0x7728,/* 0x70-0x77 */
+ 0x77E9,0x7830,0x7827,0x7838,0x781D,0x7834,0x7837,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x7825,0x782D,0x7820,0x781F,0x7832,0x7955,0x7950,/* 0xA0-0xA7 */
+ 0x7960,0x795F,0x7956,0xFA19,0x795D,0x7957,0x795A,0x79E4,/* 0xA8-0xAF */
+ 0x79E3,0x79E7,0x79DF,0x79E6,0x79E9,0x79D8,0x7A84,0x7A88,/* 0xB0-0xB7 */
+ 0x7AD9,0x7B06,0x7B11,0x7C89,0x7D21,0x7D17,0x7D0B,0x7D0A,/* 0xB8-0xBF */
+ 0x7D20,0xF96A,0x7D14,0xF9CF,0x7D15,0x7D1A,0x7D1C,0x7D0D,/* 0xC0-0xC7 */
+ 0x7D19,0x7D1B,0x7F3A,0x7F5F,0x7F94,0x7FC5,0x7FC1,0x8006,/* 0xC8-0xCF */
+ 0x8018,0x8015,0x8019,0x8017,0x803D,0x803F,0x80F1,0x8102,/* 0xD0-0xD7 */
+ 0x80F0,0x8105,0x80ED,0x80F4,0x8106,0x80F8,0x80F3,0x8108,/* 0xD8-0xDF */
+ 0x80FD,0x810A,0x80FC,0x80EF,0x81ED,0x81EC,0x8200,0x8210,/* 0xE0-0xE7 */
+ 0x822A,0x822B,0x8228,0x822C,0x82BB,0x832B,0x8352,0x8354,/* 0xE8-0xEF */
+ 0x834A,0x8338,0x8350,0x8349,0x8335,0x8334,0x834F,0x8332,/* 0xF0-0xF7 */
+ 0x8339,0xF9FE,0x8317,0x8340,0x8331,0x8328,0x8343,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_B0[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x8654,0x868A,0x86AA,0x8693,0x86A4,0x86A9,0x868C,0x86A3,/* 0x40-0x47 */
+ 0x869C,0x8870,0x8877,0x8881,0x8882,0x887D,0x8879,0x8A18,/* 0x48-0x4F */
+ 0x8A10,0x8A0E,0x8A0C,0x8A15,0x8A0A,0x8A17,0x8A13,0x8A16,/* 0x50-0x57 */
+ 0x8A0F,0x8A11,0xF900,0x8C7A,0x8C79,0x8CA1,0x8CA2,0x8D77,/* 0x58-0x5F */
+ 0x8EAC,0x8ED2,0x8ED4,0x8ECF,0x8FB1,0x9001,0x9006,0x8FF7,/* 0x60-0x67 */
+ 0x9000,0x8FFA,0x8FF4,0x9003,0x8FFD,0x9005,0x8FF8,0x9095,/* 0x68-0x6F */
+ 0x90E1,0x90DD,0x90E2,0x9152,0x914D,0x914C,0x91D8,0x91DD,/* 0x70-0x77 */
+ 0x91D7,0x91DC,0x91D9,0x9583,0x9662,0x9663,0x9661,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x965B,0x965D,0x9664,0x9658,0x965E,0x96BB,0x98E2,/* 0xA0-0xA7 */
+ 0x99AC,0x9AA8,0x9AD8,0x9B25,0x9B32,0x9B3C,0x4E7E,0x507A,/* 0xA8-0xAF */
+ 0x507D,0x505C,0x5047,0x5043,0x504C,0x505A,0x5049,0x5065,/* 0xB0-0xB7 */
+ 0x5076,0x504E,0x5055,0x5075,0x5074,0x5077,0x504F,0x500F,/* 0xB8-0xBF */
+ 0x506F,0x506D,0x515C,0x5195,0x51F0,0x526A,0x526F,0xF952,/* 0xC0-0xC7 */
+ 0x52D9,0x52D8,0x52D5,0x5310,0x530F,0x5319,0xF9EB,0x5340,/* 0xC8-0xCF */
+ 0x533E,0xF96B,0x66FC,0x5546,0x556A,0x5566,0x5544,0x555E,/* 0xD0-0xD7 */
+ 0x5561,0x5543,0x554A,0x5531,0x5556,0x554F,0x5555,0x552F,/* 0xD8-0xDF */
+ 0x5564,0x5538,0x552E,0x555C,0x552C,0x5563,0x5533,0x5541,/* 0xE0-0xE7 */
+ 0x5557,0x5708,0x570B,0x5709,0x57DF,0x5805,0x580A,0x5806,/* 0xE8-0xEF */
+ 0x57E0,0x57E4,0x57FA,0x5802,0x5835,0x57F7,0x57F9,0x5920,/* 0xF0-0xF7 */
+ 0x5962,0x5A36,0x5A41,0x5A49,0x5A66,0x5A6A,0x5A40,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_B1[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x5A3C,0x5A62,0x5A5A,0x5A46,0x5A4A,0x5B70,0x5BC7,0x5BC5,/* 0x40-0x47 */
+ 0x5BC4,0x5BC2,0x5BBF,0x5BC6,0x5C09,0x5C08,0x5C07,0x5C60,/* 0x48-0x4F */
+ 0x5C5C,0x5C5D,0x5D07,0x5D06,0x5D0E,0x5D1B,0x5D16,0x5D22,/* 0x50-0x57 */
+ 0x5D11,0x5D29,0x5D14,0xF9D5,0x5D24,0x5D27,0x5D17,0x5DE2,/* 0x58-0x5F */
+ 0x5E38,0x5E36,0x5E33,0x5E37,0x5EB7,0x5EB8,0x5EB6,0x5EB5,/* 0x60-0x67 */
+ 0x5EBE,0x5F35,0x5F37,0x5F57,0x5F6C,0x5F69,0x5F6B,0x5F97,/* 0x68-0x6F */
+ 0x5F99,0x5F9E,0x5F98,0x5FA1,0x5FA0,0x5F9C,0x607F,0x60A3,/* 0x70-0x77 */
+ 0x6089,0x60A0,0x60A8,0x60CB,0x60B4,0x60E6,0x60BD,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x60C5,0x60BB,0x60B5,0x60DC,0x60BC,0x60D8,0x60D5,/* 0xA0-0xA7 */
+ 0x60C6,0x60DF,0x60B8,0x60DA,0x60C7,0x621A,0x621B,0x6248,/* 0xA8-0xAF */
+ 0xF975,0x63A7,0x6372,0x6396,0x63A2,0x63A5,0x6377,0x6367,/* 0xB0-0xB7 */
+ 0x6398,0x63AA,0x6371,0x63A9,0x6389,0x6383,0x639B,0x636B,/* 0xB8-0xBF */
+ 0x63A8,0x6384,0x6388,0x6399,0x63A1,0x63AC,0x6392,0x638F,/* 0xC0-0xC7 */
+ 0x6380,0xF9A4,0x6369,0x6368,0x637A,0x655D,0x6556,0x6551,/* 0xC8-0xCF */
+ 0x6559,0x6557,0x555F,0x654F,0x6558,0x6555,0x6554,0x659C,/* 0xD0-0xD7 */
+ 0x659B,0x65AC,0x65CF,0x65CB,0x65CC,0x65CE,0x665D,0x665A,/* 0xD8-0xDF */
+ 0x6664,0x6668,0x6666,0x665E,0x66F9,0x52D7,0x671B,0xF97A,/* 0xE0-0xE7 */
+ 0x68AF,0x68A2,0x6893,0x68B5,0x687F,0x6876,0x68B1,0x68A7,/* 0xE8-0xEF */
+ 0x6897,0x68B0,0x6883,0x68C4,0x68AD,0x6886,0x6885,0x6894,/* 0xF0-0xF7 */
+ 0x689D,0xF9E2,0x689F,0x68A1,0x6882,0x6B32,0xF970,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_B2[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x6BEB,0x6BEC,0x6C2B,0x6D8E,0x6DBC,0x6DF3,0x6DD9,0x6DB2,/* 0x40-0x47 */
+ 0x6DE1,0x6DCC,0x6DE4,0x6DFB,0x6DFA,0x6E05,0x6DC7,0xF9F5,/* 0x48-0x4F */
+ 0x6DAF,0x6DD1,0x6DAE,0x6DDE,0x6DF9,0x6DB8,0x6DF7,0x6DF5,/* 0x50-0x57 */
+ 0x6DC5,0x6DD2,0x6E1A,0x6DB5,0xF94D,0x6DEB,0x6DD8,0xF9D6,/* 0x58-0x5F */
+ 0x6DF1,0x6DEE,0x6DE8,0x6DC6,0x6DC4,0x6DAA,0x6DEC,0x6DBF,/* 0x60-0x67 */
+ 0x6DE6,0x70F9,0x7109,0x710A,0x70FD,0x70EF,0x723D,0x727D,/* 0x68-0x6F */
+ 0x7281,0x731C,0x731B,0x7316,0x7313,0x7319,0xF9DB,0x7405,/* 0x70-0x77 */
+ 0x740A,0x7403,0xF9E4,0x73FE,0x740D,0x74E0,0x74F6,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x74F7,0x751C,0x7522,0xF976,0x7566,0x7562,0xF962,/* 0xA0-0xA7 */
+ 0x758F,0x75D4,0x75D5,0x75B5,0x75CA,0x75CD,0x768E,0x76D4,/* 0xA8-0xAF */
+ 0x76D2,0x76DB,0x7737,0x773E,0x773C,0x7736,0x7738,0x773A,/* 0xB0-0xB7 */
+ 0xF9CE,0x7843,0x784E,0xFA1A,0x7968,0x796D,0x79FB,0x7A92,/* 0xB8-0xBF */
+ 0x7A95,0xF9F8,0x7B28,0x7B1B,0x7B2C,0x7B26,0x7B19,0x7B1E,/* 0xC0-0xC7 */
+ 0x7B2E,0xF9F9,0x7C97,0x7C95,0x7D46,0x7D43,0x7D71,0x7D2E,/* 0xC8-0xCF */
+ 0x7D39,0x7D3C,0x7D40,0x7D30,0x7D33,0x7D44,0xF94F,0x7D42,/* 0xD0-0xD7 */
+ 0x7D32,0x7D31,0x7F3D,0x7F9E,0xF9AF,0x7FCC,0x7FCE,0x7FD2,/* 0xD8-0xDF */
+ 0x801C,0x804A,0xF9B0,0x812F,0x8116,0x8123,0x812B,0x8129,/* 0xE0-0xE7 */
+ 0x8130,0x8124,0x8202,0x8235,0x8237,0x8236,0x8239,0x838E,/* 0xE8-0xEF */
+ 0x839E,0x8398,0x8378,0x83A2,0x8396,0x83BD,0x83AB,0x8392,/* 0xF0-0xF7 */
+ 0x838A,0x8393,0x8389,0x83A0,0x8377,0x837B,0x837C,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_B3[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x8386,0x83A7,0x8655,0x5F6A,0x86C7,0x86C0,0x86B6,0x86C4,/* 0x40-0x47 */
+ 0x86B5,0x86C6,0x86CB,0x86B1,0x86AF,0x86C9,0x8853,0x889E,/* 0x48-0x4F */
+ 0x8888,0x88AB,0x8892,0x8896,0x888D,0x888B,0x8993,0x898F,/* 0x50-0x57 */
+ 0x8A2A,0x8A1D,0x8A23,0x8A25,0x8A31,0x8A2D,0x8A1F,0x8A1B,/* 0x58-0x5F */
+ 0x8A22,0x8C49,0x8C5A,0x8CA9,0x8CAC,0x8CAB,0x8CA8,0x8CAA,/* 0x60-0x67 */
+ 0x8CA7,0x8D67,0x8D66,0x8DBE,0x8DBA,0x8EDB,0x8EDF,0x9019,/* 0x68-0x6F */
+ 0x900D,0x901A,0x9017,0xF99A,0x901F,0x901D,0x9010,0x9015,/* 0x70-0x77 */
+ 0x901E,0x9020,0x900F,0x9022,0x9016,0x901B,0x9014,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x90E8,0x90ED,0xFA26,0x9157,0x91CE,0x91F5,0x91E6,/* 0xA0-0xA7 */
+ 0x91E3,0x91E7,0x91ED,0x91E9,0x9589,0x966A,0xF959,0x9673,/* 0xA8-0xAF */
+ 0xF9D3,0x9670,0x9674,0x9676,0x9677,0x966C,0x96C0,0x96EA,/* 0xB0-0xB7 */
+ 0x96E9,0x7AE0,0x7ADF,0x9802,0x9803,0x9B5A,0x9CE5,0x9E75,/* 0xB8-0xBF */
+ 0xF940,0x9EA5,0x9EBB,0x50A2,0x508D,0x5085,0x5099,0x5091,/* 0xC0-0xC7 */
+ 0x5080,0x5096,0x5098,0x509A,0x6700,0x51F1,0x5272,0x5274,/* 0xC8-0xCF */
+ 0x5275,0x5269,0xF92F,0x52DD,0x52DB,0x535A,0x53A5,0x557B,/* 0xD0-0xD7 */
+ 0x5580,0x55A7,0x557C,0x558A,0x559D,0x5598,0x5582,0x559C,/* 0xD8-0xDF */
+ 0x55AA,0x5594,0xF90B,0x558B,0x5583,0x55B3,0x55AE,0x559F,/* 0xE0-0xE7 */
+ 0x553E,0x55B2,0x559A,0x55BB,0x55AC,0x55B1,0x557E,0x5589,/* 0xE8-0xEF */
+ 0x55AB,0x5599,0x570D,0x582F,0x582A,0x5834,0x5824,0x5830,/* 0xF0-0xF7 */
+ 0x5831,0x5821,0x581D,0x5820,0x58F9,0x58FA,0x5960,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_B4[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x5A77,0x5A9A,0x5A7F,0x5A92,0x5A9B,0x5AA7,0x5B73,0x5B71,/* 0x40-0x47 */
+ 0x5BD2,0x5BCC,0x5BD3,0x5BD0,0x5C0A,0x5C0B,0x5C31,0x5D4C,/* 0x48-0x4F */
+ 0xF921,0x5D34,0x5D47,0x5DFD,0x5E45,0x5E3D,0x5E40,0x5E43,/* 0x50-0x57 */
+ 0x5E7E,0xF928,0x5EC1,0x5EC2,0x5EC4,0x5F3C,0x5F6D,0xF966,/* 0x58-0x5F */
+ 0x5FAA,0x5FA8,0x60D1,0xF9B9,0x60B2,0x60B6,0x60E0,0x611C,/* 0x60-0x67 */
+ 0x6123,0x60FA,0x6115,0x60F0,0x60FB,0x60F4,0x6168,0x60F1,/* 0x68-0x6F */
+ 0x610E,0x60F6,0x6109,0x6100,0x6112,0x621F,0x6249,0x63A3,/* 0x70-0x77 */
+ 0x638C,0x63CF,0x63C0,0x63E9,0x63C9,0x63C6,0x63CD,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x63D2,0x63E3,0x63D0,0x63E1,0x63D6,0x63ED,0x63EE,/* 0xA0-0xA7 */
+ 0x6376,0x63F4,0x63EA,0x63DB,0x6452,0x63DA,0x63F9,0x655E,/* 0xA8-0xAF */
+ 0x6566,0x6562,0x6563,0x6591,0x6590,0x65AF,0x666E,0x6670,/* 0xB0-0xB7 */
+ 0xFA12,0x6676,0x666F,0x6691,0x667A,0x667E,0x6677,0x66FE,/* 0xB8-0xBF */
+ 0x66FF,0x671F,0x671D,0x68FA,0x68D5,0x68E0,0x68D8,0x68D7,/* 0xC0-0xC7 */
+ 0x6905,0x68DF,0x68F5,0x68EE,0x68E7,0x68F9,0x68D2,0x68F2,/* 0xC8-0xCF */
+ 0x68E3,0x68CB,0x68CD,0x690D,0x6912,0x690E,0x68C9,0x68DA,/* 0xD0-0xD7 */
+ 0x696E,0x68FB,0x6B3E,0x6B3A,0x6B3D,0x6B98,0x6B96,0x6BBC,/* 0xD8-0xDF */
+ 0x6BEF,0x6C2E,0x6C2F,0x6C2C,0x6E2F,0x6E38,0x6E54,0x6E21,/* 0xE0-0xE7 */
+ 0x6E32,0x6E67,0x6E4A,0x6E20,0x6E25,0x6E23,0x6E1B,0x6E5B,/* 0xE8-0xEF */
+ 0x6E58,0x6E24,0x6E56,0x6E6E,0x6E2D,0x6E26,0x6E6F,0x6E34,/* 0xF0-0xF7 */
+ 0x6E4D,0x6E3A,0x6E2C,0x6E43,0x6E1D,0x6E3E,0x6ECB,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_B5[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x6E89,0x6E19,0x6E4E,0x6E63,0x6E44,0x6E72,0x6E69,0x6E5F,/* 0x40-0x47 */
+ 0x7119,0x711A,0x7126,0x7130,0x7121,0x7136,0x716E,0x711C,/* 0x48-0x4F */
+ 0x724C,0x7284,0x7280,0x7336,0x7325,0x7334,0x7329,0x743A,/* 0x50-0x57 */
+ 0x742A,0x7433,0x7422,0x7425,0x7435,0x7436,0x7434,0x742F,/* 0x58-0x5F */
+ 0x741B,0x7426,0x7428,0x7525,0x7526,0x756B,0x756A,0xF9E5,/* 0x60-0x67 */
+ 0x75DB,0x75E3,0x75D9,0x75D8,0x75DE,0x75E0,0x767B,0x767C,/* 0x68-0x6F */
+ 0x7696,0x7693,0x76B4,0x76DC,0x774F,0x77ED,0x785D,0x786C,/* 0x70-0x77 */
+ 0x786F,0x7A0D,0x7A08,0x7A0B,0x7A05,0x7A00,0x7A98,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x7A97,0x7A96,0x7AE5,0x7AE3,0x7B49,0x7B56,0x7B46,/* 0xA0-0xA7 */
+ 0x7B50,0x7B52,0x7B54,0x7B4D,0x7B4B,0x7B4F,0x7B51,0x7C9F,/* 0xA8-0xAF */
+ 0x7CA5,0x7D5E,0x7D50,0x7D68,0x7D55,0x7D2B,0x7D6E,0x7D72,/* 0xB0-0xB7 */
+ 0x7D61,0x7D66,0x7D62,0x7D70,0x7D73,0x5584,0x7FD4,0x7FD5,/* 0xB8-0xBF */
+ 0x800B,0x8052,0x8085,0x8155,0x8154,0x814B,0x8151,0x814E,/* 0xC0-0xC7 */
+ 0x8139,0x8146,0x813E,0x814C,0x8153,0x8174,0x8212,0x821C,/* 0xC8-0xCF */
+ 0x83E9,0x8403,0x83F8,0x840D,0x83E0,0x83C5,0x840B,0x83C1,/* 0xD0-0xD7 */
+ 0x83EF,0xF958,0x83F4,0x8457,0x840A,0x83F0,0x840C,0x83CC,/* 0xD8-0xDF */
+ 0x83FD,0x83F2,0x83CA,0x8438,0x840E,0x8404,0x83DC,0x8407,/* 0xE0-0xE7 */
+ 0x83D4,0x83DF,0x865B,0x86DF,0x86D9,0x86ED,0x86D4,0x86DB,/* 0xE8-0xEF */
+ 0x86E4,0x86D0,0x86DE,0x8857,0x88C1,0xF9A0,0x88B1,0x8983,/* 0xF0-0xF7 */
+ 0x8996,0x8A3B,0x8A60,0x8A55,0x8A5E,0x8A3C,0x8A41,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_B6[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x8A54,0x8A5B,0x8A50,0x8A46,0x8A34,0x8A3A,0x8A36,0x8A56,/* 0x40-0x47 */
+ 0x8C61,0x8C82,0x8CAF,0x8CBC,0x8CB3,0x8CBD,0x8CC1,0x8CBB,/* 0x48-0x4F */
+ 0x8CC0,0x8CB4,0x8CB7,0x8CB6,0x8CBF,0x8CB8,0x8D8A,0x8D85,/* 0x50-0x57 */
+ 0x8D81,0x8DCE,0x8DDD,0x8DCB,0x8DDA,0x8DD1,0x8DCC,0x8DDB,/* 0x58-0x5F */
+ 0x8DC6,0x8EFB,0x8EF8,0x8EFC,0x8F9C,0x902E,0x9035,0x9031,/* 0x60-0x67 */
+ 0xFA25,0x9032,0x9036,0x9102,0x90F5,0x9109,0x90FE,0x9163,/* 0x68-0x6F */
+ 0x9165,0xF97E,0x9214,0x9215,0x9223,0x9209,0x921E,0x920D,/* 0x70-0x77 */
+ 0x9210,0x9207,0x9211,0x9594,0x958F,0x958B,0x9591,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x9593,0x9592,0x958E,0x968A,0x968E,0x968B,0x967D,/* 0xA0-0xA7 */
+ 0x9685,0xF9DC,0x968D,0x9672,0x9684,0x96C1,0x96C5,0x96C4,/* 0xA8-0xAF */
+ 0x96C6,0x96C7,0x96EF,0x96F2,0x97CC,0x9805,0x9806,0x9808,/* 0xB0-0xB7 */
+ 0x98E7,0x98EA,0xFA2A,0x98E9,0x98F2,0x98ED,0x99AE,0x99AD,/* 0xB8-0xBF */
+ 0x9EC3,0x9ECD,0x9ED1,0xF91B,0x50AD,0x50B5,0x50B2,0x50B3,/* 0xC0-0xC7 */
+ 0x50C5,0x50BE,0x50AC,0x50B7,0x50BB,0x50AF,0x50C7,0x527F,/* 0xC8-0xCF */
+ 0x5277,0x527D,0x52DF,0x52E6,0x52E4,0x52E2,0x52E3,0x532F,/* 0xD0-0xD7 */
+ 0x55DF,0x55E8,0x55D3,0x55E6,0x55CE,0x55DC,0x55C7,0x55D1,/* 0xD8-0xDF */
+ 0x55E3,0x55E4,0x55EF,0x55DA,0x55E1,0x55C5,0x55C6,0x55E5,/* 0xE0-0xE7 */
+ 0x55C9,0x5712,0x5713,0xF96C,0x5851,0x5858,0x5857,0xFA10,/* 0xE8-0xEF */
+ 0x5854,0x586B,0x584C,0x586D,0x584A,0x5862,0x5852,0x584B,/* 0xF0-0xF7 */
+ 0x5967,0x5AC1,0x5AC9,0x5ACC,0x5ABE,0x5ABD,0x5ABC,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_B7[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x5AB3,0x5AC2,0x5AB2,0x5D69,0x5D6F,0x5E4C,0x5E79,0xF9A2,/* 0x40-0x47 */
+ 0x5EC8,0x5F12,0x5F59,0x5FAC,0x5FAE,0x611A,0x610F,0x6148,/* 0x48-0x4F */
+ 0x611F,0x60F3,0x611B,0x60F9,0x6101,0x6108,0x614E,0x614C,/* 0x50-0x57 */
+ 0xF9D9,0x614D,0x613E,0x6134,0x6127,0x610D,0x6106,0x6137,/* 0x58-0x5F */
+ 0x6221,0x6222,0x6413,0x643E,0x641E,0x642A,0x642D,0x643D,/* 0x60-0x67 */
+ 0x642C,0x640F,0x641C,0x6414,0x640D,0x6436,0x6416,0x6417,/* 0x68-0x6F */
+ 0x6406,0x656C,0x659F,0x65B0,0x6697,0x6689,0x6687,0xF9C5,/* 0x70-0x77 */
+ 0x6696,0x6684,0x6698,0x668D,0x6703,0x6994,0x696D,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x695A,0x6977,0x6960,0x6954,0x6975,0x6930,0x6982,/* 0xA0-0xA7 */
+ 0x694A,0x6968,0x696B,0x695E,0x6953,0x6979,0x6986,0x695D,/* 0xA8-0xAF */
+ 0x6963,0x695B,0x6B47,0x6B72,0x6BC0,0x6BBF,0x6BD3,0x6BFD,/* 0xB0-0xB7 */
+ 0x6EA2,0x6EAF,0x6ED3,0x6EB6,0x6EC2,0x6E90,0x6E9D,0x6EC7,/* 0xB8-0xBF */
+ 0x6EC5,0x6EA5,0x6E98,0x6EBC,0xF9EC,0x6EAB,0xF904,0x6E96,/* 0xC0-0xC7 */
+ 0xF9CB,0x6EC4,0x6ED4,0x6EAA,0x6EA7,0x6EB4,0x714E,0x7159,/* 0xC8-0xCF */
+ 0x7169,0x7164,0xF993,0x7167,0x715C,0x716C,0x7166,0x714C,/* 0xD0-0xD7 */
+ 0x7165,0x715E,0x7146,0x7168,0x7156,0x723A,0x7252,0x7337,/* 0xD8-0xDF */
+ 0x7345,0x733F,0x733E,0x746F,0x745A,0x7455,0x745F,0x745E,/* 0xE0-0xE7 */
+ 0x7441,0x743F,0x7459,0x745B,0x745C,0x7576,0x7578,0x7600,/* 0xE8-0xEF */
+ 0x75F0,0x7601,0x75F2,0x75F1,0x75FA,0x75FF,0x75F4,0x75F3,/* 0xF0-0xF7 */
+ 0x76DE,0x76DF,0x775B,0x776B,0x7766,0x775E,0x7763,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_B8[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x7779,0x776A,0x776C,0x775C,0x7765,0x7768,0x7762,0x77EE,/* 0x40-0x47 */
+ 0x788E,0x78B0,0x7897,0x7898,0xF93B,0x7889,0x787C,0x7891,/* 0x48-0x4F */
+ 0x7893,0x787F,0x797A,0xF93C,0x7981,0x842C,0x79BD,0xF956,/* 0x50-0x57 */
+ 0x7A1A,0x7A20,0x7A14,0x7A1F,0x7A1E,0x7A9F,0x7AA0,0x7B77,/* 0x58-0x5F */
+ 0x7BC0,0x7B60,0x7B6E,0x7B67,0x7CB1,0x7CB3,0x7CB5,0x7D93,/* 0x60-0x67 */
+ 0x7D79,0x7D91,0x7D81,0x7D8F,0x7D5B,0x7F6E,0x7F69,0x7F6A,/* 0x68-0x6F */
+ 0x7F72,0x7FA9,0x7FA8,0x7FA4,0x8056,0x8058,0x8086,0x8084,/* 0x70-0x77 */
+ 0x8171,0x8170,0x8178,0x8165,0x816E,0x8173,0x816B,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x8179,0x817A,0x8166,0x8205,0x8247,0x8482,0x8477,/* 0xA0-0xA7 */
+ 0xF918,0x8431,0x8475,0x8466,0x846B,0xF96E,0x846C,0x845B,/* 0xA8-0xAF */
+ 0x843C,0x8435,0x8461,0x8463,0x8469,0x846D,0x8446,0x865E,/* 0xB0-0xB7 */
+ 0xF936,0x865F,0x86F9,0x8713,0x8708,0x8707,0x8700,0x86FE,/* 0xB8-0xBF */
+ 0x86FB,0x8702,0x8703,0x8706,0x870A,0x8859,0x88DF,0x88D4,/* 0xC0-0xC7 */
+ 0x88D9,0x88DC,0x88D8,0x88DD,0xF9E8,0x88CA,0x88D5,0x88D2,/* 0xC8-0xCF */
+ 0x899C,0x89E3,0x8A6B,0x8A72,0x8A73,0x8A66,0x8A69,0x8A70,/* 0xD0-0xD7 */
+ 0x8A87,0x8A7C,0x8A63,0x8AA0,0x8A71,0x8A85,0x8A6D,0x8A62,/* 0xD8-0xDF */
+ 0x8A6E,0x8A6C,0x8A79,0x8A7B,0x8A3E,0x8A68,0x8C62,0x8C8A,/* 0xE0-0xE7 */
+ 0x8C89,0x8CCA,0x8CC7,0xF903,0x8CC4,0x8CB2,0x8CC3,0xF948,/* 0xE8-0xEF */
+ 0x8CC5,0x8DE1,0x8DDF,0x8DE8,0xF937,0x8DF3,0x8DFA,0x8DEA,/* 0xF0-0xF7 */
+ 0x8DE4,0x8DE6,0x8EB2,0x8F03,0x8F09,0x8EFE,0x8F0A,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_B9[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x8F9F,0x8FB2,0x904B,0x904A,0x9053,0x9042,0x9054,0x903C,/* 0x40-0x47 */
+ 0x9055,0x9050,0x9047,0x904F,0x904E,0x904D,0x9051,0x903E,/* 0x48-0x4F */
+ 0x9041,0x9112,0x9117,0x916C,0xF919,0x9169,0x91C9,0x9237,/* 0x50-0x57 */
+ 0x9257,0x9238,0x923D,0x9240,0x923E,0x925B,0x924B,0x9264,/* 0x58-0x5F */
+ 0x9251,0xF9B1,0x9249,0x924D,0x9245,0x9239,0x923F,0x925A,/* 0x60-0x67 */
+ 0x9598,0x9698,0x9694,0x9695,0x96CD,0x96CB,0x96C9,0x96CA,/* 0x68-0x6F */
+ 0xF949,0x96FB,0x96F9,0xF9B2,0xFA1C,0x9774,0x9776,0x9810,/* 0x70-0x77 */
+ 0x9811,0x9813,0x980A,0x9812,0x980C,0xFA2B,0x98F4,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x98FD,0x98FE,0x99B3,0x99B1,0x99B4,0x9AE1,0x9CE9,/* 0xA0-0xA7 */
+ 0x9E82,0x9F0E,0x9F13,0x9F20,0x50E7,0x50EE,0x50E5,0x50D6,/* 0xA8-0xAF */
+ 0x50ED,0xF9BB,0x50D5,0x50CF,0x50D1,0x50F1,0x50CE,0x50E9,/* 0xB0-0xB7 */
+ 0x5162,0x51F3,0x5283,0x5282,0x5331,0x53AD,0x55FE,0x5600,/* 0xB8-0xBF */
+ 0x561B,0x5617,0x55FD,0x5614,0x5606,0x5609,0x560D,0x560E,/* 0xC0-0xC7 */
+ 0x55F7,0x5616,0x561F,0x5608,0x5610,0x55F6,0x5718,0x5716,/* 0xC8-0xCF */
+ 0x5875,0x587E,0x5883,0x5893,0x588A,0x5879,0x5885,0x587D,/* 0xD0-0xD7 */
+ 0x58FD,0x5925,0x5922,0x5924,0x596A,0x5969,0x5AE1,0x5AE6,/* 0xD8-0xDF */
+ 0x5AE9,0x5AD7,0x5AD6,0x5AD8,0x5AE3,0x5B75,0x5BDE,0xF9AA,/* 0xE0-0xE7 */
+ 0x5BE1,0x5BE5,0x5BE6,0x5BE8,0x5BE2,0x5BE4,0x5BDF,0x5C0D,/* 0xE8-0xEF */
+ 0xF94B,0x5D84,0x5D87,0x5E5B,0x5E63,0x5E55,0x5E57,0x5E54,/* 0xF0-0xF7 */
+ 0xFA0B,0x5ED6,0x5F0A,0x5F46,0x5F70,0x5FB9,0x6147,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_BA[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x613F,0x614B,0x6177,0x6162,0x6163,0x615F,0x615A,0x6158,/* 0x40-0x47 */
+ 0x6175,0x622A,0x6487,0x6458,0x6454,0x64A4,0x6478,0x645F,/* 0x48-0x4F */
+ 0x647A,0x6451,0x6467,0x6434,0x646D,0x647B,0x6572,0x65A1,/* 0x50-0x57 */
+ 0x65D7,0x65D6,0x66A2,0x66A8,0x669D,0x699C,0x69A8,0x6995,/* 0x58-0x5F */
+ 0x69C1,0x69AE,0x69D3,0x69CB,0x699B,0x69B7,0x69BB,0x69AB,/* 0x60-0x67 */
+ 0x69B4,0x69D0,0x69CD,0x69AD,0x69CC,0x69A6,0x69C3,0x69A3,/* 0x68-0x6F */
+ 0x6B49,0x6B4C,0x6C33,0x6F33,0x6F14,0x6EFE,0x6F13,0x6EF4,/* 0x70-0x77 */
+ 0x6F29,0x6F3E,0x6F20,0x6F2C,0xF94E,0x6F02,0x6F22,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x6EFF,0x6EEF,0x6F06,0x6F31,0x6F38,0x6F32,0xF992,/* 0xA0-0xA7 */
+ 0x6F15,0x6F2B,0x6F2F,0x6F88,0x6F2A,0x6EEC,0x6F01,0x6EF2,/* 0xA8-0xAF */
+ 0x6ECC,0x6EF7,0x7194,0x7199,0x717D,0x718A,0x7184,0x7192,/* 0xB0-0xB7 */
+ 0x723E,0x7292,0x7296,0x7344,0x7350,0x7464,0x7463,0x746A,/* 0xB8-0xBF */
+ 0x7470,0x746D,0x7504,0x7591,0x7627,0x760D,0x760B,0x7609,/* 0xC0-0xC7 */
+ 0x7613,0x76E1,0x76E3,0x7784,0x777D,0x777F,0x7761,0x78C1,/* 0xC8-0xCF */
+ 0x789F,0x78A7,0x78B3,0x78A9,0x78A3,0x798E,0xFA1B,0x798D,/* 0xD0-0xD7 */
+ 0x7A2E,0x7A31,0x7AAA,0x7AA9,0x7AED,0x7AEF,0x7BA1,0x7B95,/* 0xD8-0xDF */
+ 0x7B8B,0x7B75,0x7B97,0x7B9D,0x7B94,0x7B8F,0x7BB8,0x7B87,/* 0xE0-0xE7 */
+ 0x7B84,0x7CB9,0x7CBD,0xFA1D,0x7DBB,0x7DB0,0x7D9C,0x7DBD,/* 0xE8-0xEF */
+ 0xF957,0xF93D,0x7DCA,0x7DB4,0x7DB2,0x7DB1,0x7DBA,0x7DA2,/* 0xF0-0xF7 */
+ 0x7DBF,0x7DB5,0x7DB8,0x7DAD,0x7DD2,0x7DC7,0x7DAC,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_BB[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x7F70,0x7FE0,0x7FE1,0x7FDF,0x805E,0x805A,0x8087,0x8150,/* 0x40-0x47 */
+ 0x8180,0x818F,0x8188,0x818A,0x817F,0x8182,0x81E7,0x81FA,/* 0x48-0x4F */
+ 0x8207,0x8214,0x821E,0x824B,0x84C9,0x84BF,0x84C6,0x84C4,/* 0x50-0x57 */
+ 0x8499,0x849E,0x84B2,0x849C,0x84CB,0x84B8,0x84C0,0x84D3,/* 0x58-0x5F */
+ 0x8490,0x84BC,0x84D1,0x84CA,0x873F,0x871C,0x873B,0x8722,/* 0x60-0x67 */
+ 0x8725,0x8734,0x8718,0x8755,0x8737,0x8729,0x88F3,0x8902,/* 0x68-0x6F */
+ 0x88F4,0x88F9,0xF912,0x88FD,0x88E8,0x891A,0x88EF,0x8AA6,/* 0x70-0x77 */
+ 0x8A8C,0x8A9E,0x8AA3,0x8A8D,0x8AA1,0x8A93,0x8AA4,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0xF9A1,0x8AA5,0x8AA8,0x8A98,0x8A91,0x8A9A,0x8AA7,/* 0xA0-0xA7 */
+ 0x8C6A,0x8C8D,0x8C8C,0x8CD3,0x8CD1,0x8CD2,0x8D6B,0x8D99,/* 0xA8-0xAF */
+ 0x8D95,0x8DFC,0x8F14,0x8F12,0x8F15,0x8F13,0x8FA3,0x9060,/* 0xB0-0xB7 */
+ 0x9058,0x905C,0x9063,0x9059,0x905E,0x9062,0x905D,0x905B,/* 0xB8-0xBF */
+ 0x9119,0x9118,0x911E,0x9175,0x9178,0x9177,0x9174,0x9278,/* 0xC0-0xC7 */
+ 0x9280,0x9285,0x9298,0x9296,0x927B,0x9293,0x929C,0x92A8,/* 0xC8-0xCF */
+ 0x927C,0x9291,0x95A1,0x95A8,0x95A9,0x95A3,0x95A5,0x95A4,/* 0xD0-0xD7 */
+ 0x9699,0x969C,0x969B,0x96CC,0x96D2,0x9700,0x977C,0x9785,/* 0xD8-0xDF */
+ 0x97F6,0x9817,0xF9B4,0x98AF,0x98B1,0x9903,0x9905,0x990C,/* 0xE0-0xE7 */
+ 0x9909,0x99C1,0x9AAF,0x9AB0,0x9AE6,0x9B41,0x9B42,0x9CF4,/* 0xE8-0xEF */
+ 0x9CF6,0x9CF3,0x9EBC,0x9F3B,0x9F4A,0x5104,0x5100,0x50FB,/* 0xF0-0xF7 */
+ 0x50F5,0x50F9,0x5102,0x5108,0x5109,0x5105,0xF954,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_BC[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x5287,0x5288,0xF9C7,0x528D,0x528A,0x52F0,0x53B2,0x562E,/* 0x40-0x47 */
+ 0x563B,0x5639,0x5632,0x563F,0x5634,0x5629,0x5653,0x564E,/* 0x48-0x4F */
+ 0x5657,0x5674,0x5636,0x562F,0x5630,0x5880,0x589F,0x589E,/* 0x50-0x57 */
+ 0x58B3,0x589C,0x58AE,0x58A9,0x58A6,0x596D,0x5B09,0x5AFB,/* 0x58-0x5F */
+ 0x5B0B,0x5AF5,0x5B0C,0x5B08,0xF9BC,0x5BEC,0x5BE9,0x5BEB,/* 0x60-0x67 */
+ 0x5C64,0xF9DF,0x5D9D,0x5D94,0x5E62,0x5E5F,0x5E61,0x5EE2,/* 0x68-0x6F */
+ 0x5EDA,0x5EDF,0x5EDD,0x5EE3,0x5EE0,0x5F48,0x5F71,0x5FB7,/* 0x70-0x77 */
+ 0x5FB5,0x6176,0x6167,0x616E,0x615D,0x6155,0x6182,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x617C,0x6170,0x616B,0x617E,0x61A7,0xF98F,0x61AB,/* 0xA0-0xA7 */
+ 0x618E,0x61AC,0x619A,0x61A4,0x6194,0x61AE,0xF9D2,0x6469,/* 0xA8-0xAF */
+ 0x646F,0x6479,0x649E,0x64B2,0x6488,0x6490,0x64B0,0x64A5,/* 0xB0-0xB7 */
+ 0x6493,0x6495,0x64A9,0x6492,0x64AE,0x64AD,0x64AB,0xF991,/* 0xB8-0xBF */
+ 0x64AC,0x6499,0x64A2,0x64B3,0x6575,0x6577,0xF969,0x66AE,/* 0xC0-0xC7 */
+ 0x66AB,0xFA06,0x66B1,0x6A23,0x6A1F,0x69E8,0x6A01,0x6A1E,/* 0xC8-0xCF */
+ 0x6A19,0x69FD,0x6A21,0xF94C,0x6A0A,0x69F3,0xF9BF,0x6A05,/* 0xD0-0xD7 */
+ 0x69ED,0x6A11,0x6B50,0x6B4E,0x6BA4,0x6BC5,0x6BC6,0x6F3F,/* 0xD8-0xDF */
+ 0x6F7C,0x6F84,0x6F51,0x6F66,0x6F54,0x6F86,0x6F6D,0x6F5B,/* 0xE0-0xE7 */
+ 0x6F78,0x6F6E,0x6F8E,0x6F7A,0x6F70,0x6F64,0x6F97,0x6F58,/* 0xE8-0xEF */
+ 0x6ED5,0x6F6F,0x6F60,0x6F5F,0x719F,0x71AC,0x71B1,0x71A8,/* 0xF0-0xF7 */
+ 0x7256,0x729B,0x734E,0x7357,0xF9AE,0x748B,0x7483,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_BD[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x747E,0x7480,0x757F,0x7620,0x7629,0x761F,0x7624,0x7626,/* 0x40-0x47 */
+ 0x7621,0x7622,0x769A,0x76BA,0x76E4,0x778E,0x7787,0x778C,/* 0x48-0x4F */
+ 0x7791,0x778B,0x78CB,0x78C5,0x78BA,0xF947,0x78BE,0x78D5,/* 0x50-0x57 */
+ 0x78BC,0x78D0,0x7A3F,0x7A3C,0x7A40,0x7A3D,0x7A37,0x7A3B,/* 0x58-0x5F */
+ 0x7AAF,0x7AAE,0x7BAD,0x7BB1,0x7BC4,0x7BB4,0x7BC6,0x7BC7,/* 0x60-0x67 */
+ 0x7BC1,0x7BA0,0x7BCC,0x7CCA,0x7DE0,0xF996,0x7DEF,0x7DFB,/* 0x68-0x6F */
+ 0x7DD8,0x7DEC,0x7DDD,0x7DE8,0x7DE3,0x7DDA,0x7DDE,0x7DE9,/* 0x70-0x77 */
+ 0x7D9E,0x7DD9,0x7DF2,0x7DF9,0x7F75,0x7F77,0x7FAF,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x7FE9,0x8026,0x819B,0x819C,0x819D,0x81A0,0x819A,/* 0xA0-0xA7 */
+ 0x8198,0x8517,0x853D,0x851A,0xF999,0x852C,0x852D,0x8513,/* 0xA8-0xAF */
+ 0x8511,0x8523,0x8521,0x8514,0x84EC,0x8525,0x84FF,0x8506,/* 0xB0-0xB7 */
+ 0x8782,0x8774,0x8776,0x8760,0x8766,0x8778,0x8768,0x8759,/* 0xB8-0xBF */
+ 0x8757,0x874C,0x8753,0x885B,0x885D,0x8910,0x8907,0x8912,/* 0xC0-0xC7 */
+ 0x8913,0x8915,0x890A,0x8ABC,0xF97D,0x8AC7,0x8AC4,0x8A95,/* 0xC8-0xCF */
+ 0x8ACB,0xFA22,0x8AB2,0x8AC9,0x8AC2,0x8ABF,0x8AB0,0xF941,/* 0xD0-0xD7 */
+ 0x8ACD,0x8AB6,0x8AB9,0x8ADB,0x8C4C,0x8C4E,0xFA16,0x8CE0,/* 0xD8-0xDF */
+ 0x8CDE,0x8CE6,0x8CE4,0x8CEC,0x8CED,0x8CE2,0x8CE3,0x8CDC,/* 0xE0-0xE7 */
+ 0x8CEA,0x8CE1,0x8D6D,0x8D9F,0x8DA3,0x8E2B,0x8E10,0x8E1D,/* 0xE8-0xEF */
+ 0x8E22,0x8E0F,0x8E29,0x8E1F,0x8E21,0x8E1E,0x8EBA,0x8F1D,/* 0xF0-0xF7 */
+ 0x8F1B,0x8F1F,0x8F29,0xF998,0xF9D7,0x8F1C,0x8F1E,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_BE[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x8F25,0x9069,0x906E,0x9068,0x906D,0x9077,0x9130,0x912D,/* 0x40-0x47 */
+ 0x9127,0x9131,0x9187,0x9189,0x918B,0x9183,0x92C5,0x92BB,/* 0x48-0x4F */
+ 0x92B7,0x92EA,0x92AC,0x92E4,0x92C1,0x92B3,0x92BC,0x92D2,/* 0x50-0x57 */
+ 0x92C7,0x92F0,0x92B2,0xF986,0x95B1,0x9704,0x9706,0x9707,/* 0x58-0x5F */
+ 0x9709,0x9760,0x978D,0x978B,0x978F,0x9821,0x982B,0x981C,/* 0x60-0x67 */
+ 0x98B3,0x990A,0x9913,0x9912,0x9918,0x99DD,0x99D0,0x99DF,/* 0x68-0x6F */
+ 0x99DB,0x99D1,0x99D5,0x99D2,0x99D9,0x9AB7,0x9AEE,0x9AEF,/* 0x70-0x77 */
+ 0x9B27,0x9B45,0x9B44,0x9B77,0xF939,0x9D06,0x9D09,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x9D03,0x9EA9,0x9EBE,0xF989,0x58A8,0x9F52,0x5112,/* 0xA0-0xA7 */
+ 0x5118,0x5114,0x5110,0x5115,0x5180,0x51AA,0x51DD,0x5291,/* 0xA8-0xAF */
+ 0x5293,0x52F3,0x5659,0x566B,0x5679,0x5669,0x5664,0x5678,/* 0xB0-0xB7 */
+ 0x566A,0x5668,0x5665,0x5671,0x566F,0x566C,0x5662,0x5676,/* 0xB8-0xBF */
+ 0x58C1,0x58BE,0x58C7,0x58C5,0x596E,0x5B1D,0x5B34,0x5B78,/* 0xC0-0xC7 */
+ 0x5BF0,0x5C0E,0x5F4A,0x61B2,0x6191,0x61A9,0x618A,0x61CD,/* 0xC8-0xCF */
+ 0x61B6,0x61BE,0x61CA,0x61C8,0x6230,0x64C5,0x64C1,0x64CB,/* 0xD0-0xD7 */
+ 0x64BB,0x64BC,0x64DA,0xF930,0x64C7,0x64C2,0x64CD,0x64BF,/* 0xD8-0xDF */
+ 0x64D2,0x64D4,0x64BE,0x6574,0xF98B,0x66C9,0x66B9,0x66C4,/* 0xE0-0xE7 */
+ 0x66C7,0x66B8,0x6A3D,0x6A38,0x6A3A,0x6A59,0x6A6B,0x6A58,/* 0xE8-0xEF */
+ 0x6A39,0x6A44,0x6A62,0x6A61,0x6A4B,0x6A47,0x6A35,0x6A5F,/* 0xF0-0xF7 */
+ 0x6A48,0x6B59,0xF98C,0x6C05,0x6FC2,0x6FB1,0x6FA1,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_BF[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x6FC3,0x6FA4,0x6FC1,0x6FA7,0x6FB3,0x6FC0,0x6FB9,0x6FB6,/* 0x40-0x47 */
+ 0x6FA6,0x6FA0,0x6FB4,0x71BE,0x71C9,0xF9EE,0x71D2,0x71C8,/* 0x48-0x4F */
+ 0x71D5,0x71B9,0xF9C0,0x71D9,0x71DC,0x71C3,0x71C4,0x7368,/* 0x50-0x57 */
+ 0x749C,0x74A3,0xF9EF,0x749F,0x749E,0x74E2,0x750C,0x750D,/* 0x58-0x5F */
+ 0x7634,0x7638,0x763A,0xF933,0x76E5,0x77A0,0x779E,0x779F,/* 0x60-0x67 */
+ 0x77A5,0x78E8,0x78DA,0x78EC,0x78E7,0x79A6,0x7A4D,0x7A4E,/* 0x68-0x6F */
+ 0x7A46,0x7A4C,0x7A4B,0x7ABA,0x7BD9,0x7C11,0x7BC9,0x7BE4,/* 0x70-0x77 */
+ 0x7BDB,0x7BE1,0x7BE9,0x7BE6,0x7CD5,0xFA03,0x7E0A,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x7E11,0x7E08,0x7E1B,0x7E23,0x7E1E,0x7E1D,0x7E09,/* 0xA0-0xA7 */
+ 0x7E10,0xF9E6,0x7FB2,0x7FF0,0x7FF1,0x7FEE,0x8028,0x81B3,/* 0xA8-0xAF */
+ 0x81A9,0x81A8,0x81FB,0x8208,0x8258,0x8259,0x854A,0x8559,/* 0xB0-0xB7 */
+ 0x8548,0x8568,0x8569,0x8543,0x8549,0x856D,0x856A,0x855E,/* 0xB8-0xBF */
+ 0x8783,0x879F,0x879E,0x87A2,0x878D,0x8861,0x892A,0x8932,/* 0xC0-0xC7 */
+ 0x8925,0x892B,0x8921,0x89AA,0x89A6,0x8AE6,0x8AFA,0x8AEB,/* 0xC8-0xCF */
+ 0x8AF1,0x8B00,0x8ADC,0x8AE7,0x8AEE,0xF95D,0x8B01,0x8B02,/* 0xD0-0xD7 */
+ 0x8AF7,0x8AED,0x8AF3,0x8AF6,0x8AFC,0x8C6B,0x8C6D,0x8C93,/* 0xD8-0xDF */
+ 0x8CF4,0x8E44,0x8E31,0x8E34,0x8E42,0x8E39,0x8E35,0xFA07,/* 0xE0-0xE7 */
+ 0x8F2F,0x8F38,0x8F33,0x8FA8,0x8FA6,0x9075,0x9074,0x9078,/* 0xE8-0xEF */
+ 0x9072,0xF9C3,0x907A,0x9134,0x9192,0x9320,0x9336,0x92F8,/* 0xF0-0xF7 */
+ 0x9333,0x932F,0x9322,0x92FC,0x932B,0xF93F,0x931A,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_C0[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x9310,0x9326,0x9321,0x9315,0x932E,0x9319,0x95BB,0x96A7,/* 0x40-0x47 */
+ 0x96A8,0x96AA,0x96D5,0x970E,0x9711,0x9716,0x970D,0x9713,/* 0x48-0x4F */
+ 0x970F,0x975B,0x975C,0x9766,0x9798,0x9830,0x9838,0x983B,/* 0x50-0x57 */
+ 0x9837,0x982D,0x9839,0x9824,0x9910,0xFA2C,0x991E,0x991B,/* 0x58-0x5F */
+ 0x9921,0x991A,0x99ED,0x99E2,0xF91A,0x9AB8,0x9ABC,0x9AFB,/* 0x60-0x67 */
+ 0x9AED,0x9B28,0x9B91,0x9D15,0x9D23,0x9D26,0x9D28,0x9D12,/* 0x68-0x6F */
+ 0x9D1B,0x9ED8,0x9ED4,0xF9C4,0xF908,0x512A,0x511F,0x5121,/* 0x70-0x77 */
+ 0x5132,0xF97F,0x568E,0x5680,0x5690,0x5685,0x5687,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x568F,0x58D5,0x58D3,0x58D1,0x58CE,0x5B30,0x5B2A,/* 0xA0-0xA7 */
+ 0x5B24,0x5B7A,0x5C37,0x5C68,0x5DBC,0xF9AB,0x5DBD,0x5DB8,/* 0xA8-0xAF */
+ 0x5E6B,0x5F4C,0x5FBD,0x61C9,0x61C2,0x61C7,0x61E6,0x61CB,/* 0xB0-0xB7 */
+ 0x6232,0x6234,0x64CE,0x64CA,0x64D8,0x64E0,0x64F0,0x64E6,/* 0xB8-0xBF */
+ 0x64EC,0x64F1,0x64E2,0x64ED,0x6582,0x6583,0x66D9,0x66D6,/* 0xC0-0xC7 */
+ 0x6A80,0x6A94,0x6A84,0x6AA2,0x6A9C,0x6ADB,0x6AA3,0x6A7E,/* 0xC8-0xCF */
+ 0x6A97,0x6A90,0x6AA0,0x6B5C,0xF9A5,0x6BDA,0x6C08,0x6FD8,/* 0xD0-0xD7 */
+ 0x6FF1,0x6FDF,0x6FE0,0x6FDB,0x6FE4,0xF922,0x6FEF,0x6F80,/* 0xD8-0xDF */
+ 0x6FEC,0x6FE1,0x6FE9,0x6FD5,0x6FEE,0x6FF0,0x71E7,0x71DF,/* 0xE0-0xE7 */
+ 0x71EE,0x71E6,0x71E5,0x71ED,0x71EC,0x71F4,0x71E0,0x7235,/* 0xE8-0xEF */
+ 0x7246,0x7370,0x7372,0x74A9,0x74B0,0x74A6,0x74A8,0x7646,/* 0xF0-0xF7 */
+ 0xF9C1,0x764C,0x76EA,0x77B3,0x77AA,0x77B0,0x77AC,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_C1[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x77A7,0x77AD,0x77EF,0x78F7,0x78FA,0x78F4,0x78EF,0x7901,/* 0x40-0x47 */
+ 0x79A7,0x79AA,0x7A57,0x7ABF,0x7C07,0x7C0D,0x7BFE,0x7BF7,/* 0x48-0x4F */
+ 0x7C0C,0x7BE0,0x7CE0,0x7CDC,0x7CDE,0x7CE2,0x7CDF,0x7CD9,/* 0x50-0x57 */
+ 0x7CDD,0x7E2E,0x7E3E,0x7E46,0xF950,0x7E32,0x7E43,0x7E2B,/* 0x58-0x5F */
+ 0x7E3D,0x7E31,0x7E45,0x7E41,0x7E34,0x7E39,0x7E48,0x7E35,/* 0x60-0x67 */
+ 0x7E3F,0x7E2F,0x7F44,0x7FF3,0x7FFC,0x8071,0x8072,0x8070,/* 0x68-0x6F */
+ 0xF997,0x8073,0x81C6,0x81C3,0x81BA,0x81C2,0x81C0,0x81BF,/* 0x70-0x77 */
+ 0x81BD,0x81C9,0x81BE,0xF9F6,0x8209,0x8271,0x85AA,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x8584,0x857E,0x859C,0x8591,0x8594,0x85AF,0x859B,/* 0xA0-0xA7 */
+ 0x8587,0x85A8,0x858A,0x8667,0x87C0,0x87D1,0x87B3,0x87D2,/* 0xA8-0xAF */
+ 0x87C6,0x87AB,0x87BB,0xF911,0x87C8,0x87CB,0x893B,0x8936,/* 0xB0-0xB7 */
+ 0x8944,0x8938,0x893D,0x89AC,0x8B0E,0x8B17,0x8B19,0x8B1B,/* 0xB8-0xBF */
+ 0x8B0A,0x8B20,0x8B1D,0x8B04,0x8B10,0x8C41,0x8C3F,0x8C73,/* 0xC0-0xC7 */
+ 0x8CFA,0x8CFD,0x8CFC,0x8CF8,0x8CFB,0x8DA8,0x8E49,0x8E4B,/* 0xC8-0xCF */
+ 0x8E48,0x8E4A,0x8F44,0x8F3E,0x8F42,0x8F45,0x8F3F,0x907F,/* 0xD0-0xD7 */
+ 0x907D,0x9084,0x9081,0x9082,0x9080,0x9139,0x91A3,0x919E,/* 0xD8-0xDF */
+ 0x919C,0x934D,0x9382,0x9328,0x9375,0xF99B,0x9365,0x934B,/* 0xE0-0xE7 */
+ 0x9318,0x937E,0x936C,0x935B,0x9370,0x935A,0x9354,0x95CA,/* 0xE8-0xEF */
+ 0x95CB,0x95CC,0x95C8,0x95C6,0x96B1,0xF9B8,0x96D6,0x971C,/* 0xF0-0xF7 */
+ 0x971E,0x97A0,0x97D3,0x9846,0x98B6,0x9935,0x9A01,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_C2[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x99FF,0x9BAE,0x9BAB,0x9BAA,0x9BAD,0x9D3B,0x9D3F,0x9E8B,/* 0x40-0x47 */
+ 0x9ECF,0x9EDE,0x9EDC,0x9EDD,0x9EDB,0x9F3E,0x9F4B,0x53E2,/* 0x48-0x4F */
+ 0x5695,0x56AE,0x58D9,0xF94A,0x5B38,0x5F5D,0x61E3,0x6233,/* 0x50-0x57 */
+ 0x64F4,0x64F2,0x64FE,0x6506,0x64FA,0x64FB,0x64F7,0x65B7,/* 0x58-0x5F */
+ 0x66DC,0x6726,0x6AB3,0x6AAC,0x6AC3,0x6ABB,0x6AB8,0x6AC2,/* 0x60-0x67 */
+ 0x6AAE,0x6AAF,0x6B5F,0x6B78,0x6BAF,0x7009,0x700B,0xF984,/* 0x68-0x6F */
+ 0x7006,0x6FFA,0x7011,0x700F,0x71FB,0x71FC,0x71FE,0x71F8,/* 0x70-0x77 */
+ 0x7377,0xF9A7,0x74A7,0x74BF,0x7515,0x7656,0x7658,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x7652,0x77BD,0x77BF,0x77BB,0x77BC,0x790E,0xF9B6,/* 0xA0-0xA7 */
+ 0x7A61,0x7A62,0x7A60,0x7AC4,0x7AC5,0x7C2B,0x7C27,0x7C2A,/* 0xA8-0xAF */
+ 0x7C1E,0x7C23,0x7C21,0xF97B,0x7E54,0x7E55,0x7E5E,0x7E5A,/* 0xB0-0xB7 */
+ 0x7E61,0x7E52,0x7E59,0x7F48,0x7FF9,0x7FFB,0x8077,0x8076,/* 0xB8-0xBF */
+ 0x81CD,0x81CF,0x820A,0x85CF,0x85A9,0xF923,0x85D0,0x85C9,/* 0xC0-0xC7 */
+ 0x85B0,0x85BA,0x85B9,0x85A6,0x87EF,0x87EC,0x87F2,0x87E0,/* 0xC8-0xCF */
+ 0x8986,0x89B2,0x89F4,0x8B28,0x8B39,0x8B2C,0x8B2B,0x8C50,/* 0xD0-0xD7 */
+ 0x8D05,0x8E59,0x8E63,0x8E66,0x8E64,0x8E5F,0x8E55,0x8EC0,/* 0xD8-0xDF */
+ 0x8F49,0x8F4D,0x9087,0x9083,0x9088,0x91AB,0x91AC,0x91D0,/* 0xE0-0xE7 */
+ 0x9394,0x938A,0x9396,0x93A2,0x93B3,0x93AE,0x93AC,0x93B0,/* 0xE8-0xEF */
+ 0x9398,0x939A,0x9397,0x95D4,0x95D6,0x95D0,0x95D5,0xF9EA,/* 0xF0-0xF7 */
+ 0x96DC,0x96D9,0x96DB,0x96DE,0x9724,0x97A3,0x97A6,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_C3[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x97AD,0x97F9,0x984D,0x984F,0x984C,0x984E,0x9853,0x98BA,/* 0x40-0x47 */
+ 0x993E,0x993F,0x993D,0x992E,0x99A5,0x9A0E,0x9AC1,0x9B03,/* 0x48-0x4F */
+ 0x9B06,0x9B4F,0x9B4E,0x9B4D,0x9BCA,0x9BC9,0x9BFD,0x9BC8,/* 0x50-0x57 */
+ 0x9BC0,0x9D51,0x9D5D,0x9D60,0x9EE0,0x9F15,0x9F2C,0x5133,/* 0x58-0x5F */
+ 0x56A5,0x58DE,0xF942,0x58E2,0x5BF5,0x9F90,0xF982,0x61F2,/* 0x60-0x67 */
+ 0x61F7,0xF90D,0x61F5,0x6500,0x650F,0x66E0,0x66DD,0x6AE5,/* 0x68-0x6F */
+ 0x6ADD,0x6ADA,0xF931,0x701B,0x701F,0x7028,0x701A,0x701D,/* 0x70-0x77 */
+ 0x7015,0x7018,0x7206,0x720D,0x7258,0x72A2,0x7378,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x737A,0x74BD,0x74CA,0x74E3,0x7587,0x7586,0x765F,/* 0xA0-0xA7 */
+ 0x7661,0x77C7,0x7919,0x79B1,0x7A6B,0x7A69,0xF9A6,0x7C3F,/* 0xA8-0xAF */
+ 0x7C38,0x7C3D,0x7C37,0x7C40,0x7E6B,0x7E6D,0x7E79,0x7E69,/* 0xB0-0xB7 */
+ 0x7E6A,0xF90F,0x7E73,0x7FB6,0x7FB9,0x7FB8,0xF926,0x85E9,/* 0xB8-0xBF */
+ 0x85DD,0x85EA,0x85D5,0x85E4,0x85E5,0x85F7,0x87FB,0x8805,/* 0xC0-0xC7 */
+ 0x880D,0x87F9,0x87FE,0x8960,0x895F,0x8956,0x895E,0x8B41,/* 0xC8-0xCF */
+ 0x8B5C,0xF9FC,0x8B49,0x8B5A,0x8B4E,0x8B4F,0x8B46,0x8B59,/* 0xD0-0xD7 */
+ 0x8D08,0x8D0A,0x8E7C,0x8E72,0x8E87,0x8E76,0x8E6C,0x8E7A,/* 0xD8-0xDF */
+ 0x8E74,0x8F54,0x8F4E,0x8FAD,0x908A,0x908B,0x91B1,0x91AE,/* 0xE0-0xE7 */
+ 0x93E1,0x93D1,0x93DF,0x93C3,0x93C8,0x93DC,0x93DD,0x93D6,/* 0xE8-0xEF */
+ 0x93E2,0x93CD,0x93D8,0x93E4,0x93D7,0x93E8,0x95DC,0x96B4,/* 0xF0-0xF7 */
+ 0x96E3,0x972A,0x9727,0x9761,0x97DC,0x97FB,0xF9D0,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_C4[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x9858,0x985B,0x98BC,0x9945,0x9949,0x9A16,0x9A19,0x9B0D,/* 0x40-0x47 */
+ 0x9BE8,0x9BE7,0x9BD6,0x9BDB,0x9D89,0x9D61,0x9D72,0x9D6A,/* 0x48-0x4F */
+ 0x9D6C,0x9E92,0xF988,0x9E93,0x9EB4,0x52F8,0x56A8,0x56B7,/* 0x50-0x57 */
+ 0x56B6,0x56B4,0x56BC,0x58E4,0x5B40,0x5B43,0x5B7D,0x5BF6,/* 0x58-0x5F */
+ 0x5DC9,0x61F8,0x61FA,0x6518,0x6514,0x6519,0x66E6,0x6727,/* 0x60-0x67 */
+ 0x6AEC,0x703E,0x7030,0x7032,0xF932,0x737B,0x74CF,0x7662,/* 0x68-0x6F */
+ 0x7665,0x7926,0xF985,0x792C,0x792B,0x7AC7,0x7AF6,0x7C4C,/* 0x70-0x77 */
+ 0x7C43,0x7C4D,0x7CEF,0x7CF0,0x8FAE,0x7E7D,0x7E7C,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x7E82,0x7F4C,0x8000,0x81DA,0x8266,0x85FB,0x85F9,/* 0xA0-0xA7 */
+ 0x8611,0xF9F0,0xF935,0x860B,0x8607,0x860A,0x8814,0x8815,/* 0xA8-0xAF */
+ 0xF924,0x89BA,0x89F8,0x8B70,0x8B6C,0x8B66,0x8B6F,0x8B5F,/* 0xB0-0xB7 */
+ 0x8B6B,0x8D0F,0x8D0D,0x8E89,0x8E81,0x8E85,0x8E82,0xF9B7,/* 0xB8-0xBF */
+ 0x91CB,0x9418,0x9403,0x93FD,0x95E1,0x9730,0x98C4,0x9952,/* 0xC0-0xC7 */
+ 0x9951,0x99A8,0x9A2B,0x9A30,0x9A37,0x9A35,0x9C13,0x9C0D,/* 0xC8-0xCF */
+ 0x9E79,0x9EB5,0x9EE8,0x9F2F,0x9F5F,0x9F63,0x9F61,0x5137,/* 0xD0-0xD7 */
+ 0x5138,0x56C1,0x56C0,0x56C2,0x5914,0x5C6C,0x5DCD,0x61FC,/* 0xD8-0xDF */
+ 0x61FE,0x651D,0x651C,0x6595,0x66E9,0x6AFB,0xF91D,0x6AFA,/* 0xE0-0xE7 */
+ 0x6BB2,0x704C,0xF91E,0x72A7,0x74D6,0x74D4,0xF90E,0x77D3,/* 0xE8-0xEF */
+ 0x7C50,0x7E8F,0x7E8C,0x7FBC,0x8617,0xF91F,0x861A,0x8823,/* 0xF0-0xF7 */
+ 0x8822,0x8821,0xF927,0x896A,0x896C,0x89BD,0x8B74,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_C5[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x8B77,0x8B7D,0x8D13,0x8E8A,0x8E8D,0x8E8B,0x8F5F,0x8FAF,/* 0x40-0x47 */
+ 0x91BA,0x942E,0x9433,0x9435,0x943A,0x9438,0x9432,0x942B,/* 0x48-0x4F */
+ 0x95E2,0x9738,0x9739,0xF938,0x97FF,0x9867,0x9865,0x9957,/* 0x50-0x57 */
+ 0x9A45,0x9A43,0x9A40,0x9A3E,0x9ACF,0x9B54,0x9B51,0x9C2D,/* 0x58-0x5F */
+ 0x9C25,0x9DAF,0xFA2D,0x9DC2,0x9DB8,0x9E9D,0x9EEF,0x9F19,/* 0x60-0x67 */
+ 0x9F5C,0x9F66,0x9F67,0x513C,0x513B,0x56C8,0x56CA,0x56C9,/* 0x68-0x6F */
+ 0x5B7F,0x5DD4,0x5DD2,0x5F4E,0x61FF,0x6524,0x6B0A,0x6B61,/* 0x70-0x77 */
+ 0x7051,0x7058,0x7380,0x74E4,0x758A,0x766E,0x766C,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x79B3,0xF944,0x7C5F,0xF945,0x807D,0x81DF,0x8972,/* 0xA0-0xA7 */
+ 0x896F,0x89FC,0xF95A,0x8D16,0x8D17,0x8E91,0x8E93,0x8F61,/* 0xA8-0xAF */
+ 0x9148,0x9444,0x9451,0x9452,0x973D,0x973E,0x97C3,0x97C1,/* 0xB0-0xB7 */
+ 0x986B,0x9955,0x9A55,0x9A4D,0x9AD2,0x9B1A,0x9C49,0x9C31,/* 0xB8-0xBF */
+ 0x9C3E,0x9C3B,0x9DD3,0x9DD7,0x9F34,0x9F6C,0x9F6A,0x9F94,/* 0xC0-0xC7 */
+ 0x56CC,0x5DD6,0xF990,0x6523,0x652B,0x652A,0x66EC,0x6B10,/* 0xC8-0xCF */
+ 0x74DA,0x7ACA,0x7C64,0x7C63,0x7C65,0x7E93,0x7E96,0x7E94,/* 0xD0-0xD7 */
+ 0x81E2,0x8638,0xF910,0x8831,0x8B8A,0x9090,0xF913,0x9463,/* 0xD8-0xDF */
+ 0x9460,0x9464,0x9768,0x986F,0x995C,0x9A5A,0x9A5B,0x9A57,/* 0xE0-0xE7 */
+ 0x9AD3,0x9AD4,0x9AD1,0x9C54,0xF9F2,0x9C56,0x9DE5,0xF9F3,/* 0xE8-0xEF */
+ 0x9EF4,0x56D1,0x58E9,0x652C,0x705E,0x7671,0x7672,0x77D7,/* 0xF0-0xF7 */
+ 0x7F50,0x7F88,0x8836,0x8839,0x8862,0x8B93,0x8B92,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_C6[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x8B96,0x8277,0x8D1B,0x91C0,0x946A,0x9742,0xF9B3,0x9744,/* 0x40-0x47 */
+ 0x97C6,0x9870,0x9A5F,0x9B22,0x9B58,0x9C5F,0x9DF9,0xF93A,/* 0x48-0x4F */
+ 0x9E7C,0x9E7D,0x9F07,0x9F77,0x9F72,0x5EF3,0x6B16,0x7063,/* 0x50-0x57 */
+ 0x7C6C,0x7C6E,0x883B,0x89C0,0x8EA1,0x91C1,0x9472,0x9470,/* 0x58-0x5F */
+ 0x9871,0x995E,0x9AD6,0x9B23,0x9ECC,0x7064,0x77DA,0x8B9A,/* 0x60-0x67 */
+ 0x9477,0x97C9,0x9A62,0x9A65,0x7E9C,0x8B9C,0x8EAA,0x91C5,/* 0x68-0x6F */
+ 0x947D,0x947E,0x947C,0x9C77,0x9C78,0x9EF7,0x8C54,0x947F,/* 0x70-0x77 */
+ 0x9E1A,0x7228,0xF987,0x9B31,0x9E1B,0xF920,0x7C72,0x0000,/* 0x78-0x7F */
+};
+
+static wchar_t c2u_C9[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x4E42,0x4E5C,0x51F5,0x531A,0x5382,0x4E07,0x4E0C,0x4E47,/* 0x40-0x47 */
+ 0x4E8D,0x56D7,0xFA0C,0x5C6E,0x5F73,0x4E0F,0x5187,0x4E0E,/* 0x48-0x4F */
+ 0x4E2E,0x4E93,0x4EC2,0x4EC9,0x4EC8,0x5198,0x52FC,0x536C,/* 0x50-0x57 */
+ 0x53B9,0x5720,0x5903,0x592C,0x5C10,0x5DFF,0x65E1,0x6BB3,/* 0x58-0x5F */
+ 0x6BCC,0x6C14,0x723F,0x4E31,0x4E3C,0x4EE8,0x4EDC,0x4EE9,/* 0x60-0x67 */
+ 0x4EE1,0x4EDD,0x4EDA,0x520C,0x531C,0x534C,0x5722,0x5723,/* 0x68-0x6F */
+ 0x5917,0x592F,0x5B81,0x5B84,0x5C12,0x5C3B,0x5C74,0x5C73,/* 0x70-0x77 */
+ 0x5E04,0x5E80,0x5E82,0x5FC9,0x6209,0x6250,0x6C15,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x6C36,0x6C43,0x6C3F,0x6C3B,0x72AE,0x72B0,0x738A,/* 0xA0-0xA7 */
+ 0x79B8,0x808A,0x961E,0x4F0E,0x4F18,0x4F2C,0x4EF5,0x4F14,/* 0xA8-0xAF */
+ 0x4EF1,0x4F00,0x4EF7,0x4F08,0x4F1D,0x4F02,0x4F05,0x4F22,/* 0xB0-0xB7 */
+ 0x4F13,0x4F04,0x4EF4,0x4F12,0x51B1,0x5213,0x5209,0x5210,/* 0xB8-0xBF */
+ 0x52A6,0x5322,0x531F,0x534D,0x538A,0x5407,0x56E1,0x56DF,/* 0xC0-0xC7 */
+ 0x572E,0x572A,0x5734,0x593C,0x5980,0x597C,0x5985,0x597B,/* 0xC8-0xCF */
+ 0x597E,0x5977,0x597F,0x5B56,0x5C15,0x5C25,0x5C7C,0x5C7A,/* 0xD0-0xD7 */
+ 0x5C7B,0x5C7E,0x5DDF,0x5E75,0x5E84,0x5F02,0x5F1A,0x5F74,/* 0xD8-0xDF */
+ 0x5FD5,0x5FD4,0x5FCF,0x625C,0x625E,0x6264,0x6261,0x6266,/* 0xE0-0xE7 */
+ 0x6262,0x6259,0x6260,0x625A,0x6265,0x65EF,0x65EE,0x673E,/* 0xE8-0xEF */
+ 0x6739,0x6738,0x673B,0x673A,0x673F,0x673C,0x6733,0x6C18,/* 0xF0-0xF7 */
+ 0x6C46,0x6C52,0x6C5C,0x6C4F,0x6C4A,0x6C54,0x6C4B,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_CA[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x6C4C,0x7071,0x725E,0x72B4,0x72B5,0x738E,0x752A,0x767F,/* 0x40-0x47 */
+ 0x7A75,0x7F51,0x8278,0x827C,0x8280,0x827D,0x827F,0x864D,/* 0x48-0x4F */
+ 0x897E,0x9099,0x9097,0x9098,0x909B,0x9094,0x9622,0x9624,/* 0x50-0x57 */
+ 0x9620,0x9623,0x4F56,0x4F3B,0x4F62,0x4F49,0x4F53,0x4F64,/* 0x58-0x5F */
+ 0x4F3E,0x4F67,0x4F52,0x4F5F,0x4F41,0x4F58,0x4F2D,0x4F33,/* 0x60-0x67 */
+ 0x4F3F,0x4F61,0x518F,0x51B9,0x521C,0x521E,0x5221,0x52AD,/* 0x68-0x6F */
+ 0x52AE,0x5309,0x5363,0x5372,0x538E,0x538F,0x5430,0x5437,/* 0x70-0x77 */
+ 0x542A,0x5454,0x5445,0x5419,0x541C,0x5425,0x5418,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x543D,0x544F,0x5441,0x5428,0x5424,0x5447,0x56EE,/* 0xA0-0xA7 */
+ 0x56E7,0x56E5,0x5741,0x5745,0x574C,0x5749,0x574B,0x5752,/* 0xA8-0xAF */
+ 0x5906,0x5940,0x59A6,0x5998,0x59A0,0x5997,0x598E,0x59A2,/* 0xB0-0xB7 */
+ 0x5990,0x598F,0x59A7,0x59A1,0x5B8E,0x5B92,0x5C28,0x5C2A,/* 0xB8-0xBF */
+ 0x5C8D,0x5C8F,0x5C88,0x5C8B,0x5C89,0x5C92,0x5C8A,0x5C86,/* 0xC0-0xC7 */
+ 0x5C93,0x5C95,0x5DE0,0x5E0A,0x5E0E,0x5E8B,0x5E89,0x5E8C,/* 0xC8-0xCF */
+ 0x5E88,0x5E8D,0x5F05,0x5F1D,0x5F78,0x5F76,0x5FD2,0x5FD1,/* 0xD0-0xD7 */
+ 0x5FD0,0x5FED,0x5FE8,0x5FEE,0x5FF3,0x5FE1,0x5FE4,0x5FE3,/* 0xD8-0xDF */
+ 0x5FFA,0x5FEF,0x5FF7,0x5FFB,0x6000,0x5FF4,0x623A,0x6283,/* 0xE0-0xE7 */
+ 0x628C,0x628E,0x628F,0x6294,0x6287,0x6271,0x627B,0x627A,/* 0xE8-0xEF */
+ 0x6270,0x6281,0x6288,0x6277,0x627D,0x6272,0x6274,0x6537,/* 0xF0-0xF7 */
+ 0x65F0,0x65F4,0x65F3,0x65F2,0x65F5,0x6745,0x6747,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_CB[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x6759,0x6755,0x674C,0x6748,0x675D,0x674D,0x675A,0x674B,/* 0x40-0x47 */
+ 0x6BD0,0x6C19,0x6C1A,0x6C78,0x6C67,0x6C6B,0x6C84,0x6C8B,/* 0x48-0x4F */
+ 0x6C8F,0x6C71,0x6C6F,0x6C69,0x6C9A,0x6C6D,0x6C87,0x6C95,/* 0x50-0x57 */
+ 0x6C9C,0x6C66,0x6C73,0x6C65,0x6C7B,0x6C8E,0x7074,0x707A,/* 0x58-0x5F */
+ 0x7263,0x72BF,0x72BD,0x72C3,0x72C6,0x72C1,0x72BA,0x72C5,/* 0x60-0x67 */
+ 0x7395,0x7397,0x7393,0x7394,0x7392,0x753A,0x7539,0x7594,/* 0x68-0x6F */
+ 0x7595,0x7681,0x793D,0x8034,0x8095,0x8099,0x8090,0x8092,/* 0x70-0x77 */
+ 0x809C,0x8290,0x828F,0x8285,0x828E,0x8291,0x8293,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x828A,0x8283,0x8284,0x8C78,0x8FC9,0x8FBF,0x909F,/* 0xA0-0xA7 */
+ 0x90A1,0x90A5,0x909E,0x90A7,0x90A0,0x9630,0x9628,0x962F,/* 0xA8-0xAF */
+ 0x962D,0x4E33,0x4F98,0x4F7C,0x4F85,0x4F7D,0x4F80,0x4F87,/* 0xB0-0xB7 */
+ 0x4F76,0x4F74,0x4F89,0x4F84,0x4F77,0x4F4C,0x4F97,0x4F6A,/* 0xB8-0xBF */
+ 0x4F9A,0x4F79,0x4F81,0x4F78,0x4F90,0x4F9C,0x4F94,0x4F9E,/* 0xC0-0xC7 */
+ 0x4F92,0x4F82,0x4F95,0x4F6B,0x4F6E,0x519E,0x51BC,0x51BE,/* 0xC8-0xCF */
+ 0x5235,0x5232,0x5233,0x5246,0x5231,0x52BC,0x530A,0x530B,/* 0xD0-0xD7 */
+ 0x533C,0x5392,0x5394,0x5487,0x547F,0x5481,0x5491,0x5482,/* 0xD8-0xDF */
+ 0x5488,0x546B,0x547A,0x547E,0x5465,0x546C,0x5474,0x5466,/* 0xE0-0xE7 */
+ 0x548D,0x546F,0x5461,0x5460,0x5498,0x5463,0x5467,0x5464,/* 0xE8-0xEF */
+ 0x56F7,0xF9A9,0x576F,0x5772,0x576D,0x576B,0x5771,0x5770,/* 0xF0-0xF7 */
+ 0x5776,0x5780,0x5775,0x577B,0x5773,0x5774,0x5762,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_CC[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x5768,0x577D,0x590C,0x5945,0x59B5,0x59BA,0x59CF,0x59CE,/* 0x40-0x47 */
+ 0x59B2,0x59CC,0x59C1,0x59B6,0x59BC,0x59C3,0x59D6,0x59B1,/* 0x48-0x4F */
+ 0x59BD,0x59C0,0x59C8,0x59B4,0x59C7,0x5B62,0x5B65,0x5B93,/* 0x50-0x57 */
+ 0x5B95,0x5C44,0x5C47,0x5CAE,0x5CA4,0x5CA0,0x5CB5,0x5CAF,/* 0x58-0x5F */
+ 0x5CA8,0x5CAC,0x5C9F,0x5CA3,0x5CAD,0x5CA2,0x5CAA,0x5CA7,/* 0x60-0x67 */
+ 0x5C9D,0x5CA5,0x5CB6,0x5CB0,0x5CA6,0x5E17,0x5E14,0x5E19,/* 0x68-0x6F */
+ 0x5F28,0x5F22,0x5F23,0x5F24,0x5F54,0x5F82,0x5F7E,0x5F7D,/* 0x70-0x77 */
+ 0x5FDE,0x5FE5,0x602D,0x6026,0x6019,0x6032,0x600B,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x6034,0x600A,0x6017,0x6033,0x601A,0x601E,0x602C,/* 0xA0-0xA7 */
+ 0x6022,0x600D,0x6010,0x602E,0x6013,0x6011,0x600C,0x6009,/* 0xA8-0xAF */
+ 0xF9AC,0x6214,0x623D,0x62AD,0x62B4,0x62D1,0x62BE,0x62AA,/* 0xB0-0xB7 */
+ 0x62B6,0x62CA,0x62AE,0x62B3,0x62AF,0x62BB,0x62A9,0x62B0,/* 0xB8-0xBF */
+ 0x62B8,0x653D,0x65A8,0x65BB,0x6609,0x65FC,0x6604,0x6612,/* 0xC0-0xC7 */
+ 0x6608,0x65FB,0x6603,0x660B,0x660D,0x6605,0x65FD,0x6611,/* 0xC8-0xCF */
+ 0x6610,0x66F6,0x670A,0x6785,0x676C,0x678E,0x6792,0x6776,/* 0xD0-0xD7 */
+ 0xF9C8,0x6798,0x6786,0x6784,0x6774,0x678D,0x678C,0x677A,/* 0xD8-0xDF */
+ 0x679F,0x6791,0x6799,0x6783,0x677D,0x6781,0x6778,0x6779,/* 0xE0-0xE7 */
+ 0x6794,0x6B25,0x6B80,0x6B7E,0x6BDE,0x6C1D,0x6C93,0x6CEC,/* 0xE8-0xEF */
+ 0x6CEB,0x6CEE,0x6CD9,0x6CB6,0x6CD4,0x6CAD,0x6CE7,0x6CB7,/* 0xF0-0xF7 */
+ 0x6CD0,0x6CC2,0x6CBA,0x6CC3,0x6CC6,0x6CED,0x6CF2,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_CD[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x6CD2,0x6CDD,0x6CB4,0x6C8A,0x6C9D,0x6C80,0x6CDE,0x6CC0,/* 0x40-0x47 */
+ 0x6D30,0x6CCD,0x6CC7,0x6CB0,0x6CF9,0x6CCF,0x6CE9,0x6CD1,/* 0x48-0x4F */
+ 0x7094,0x7098,0x7085,0x7093,0x7086,0x7084,0x7091,0x7096,/* 0x50-0x57 */
+ 0x7082,0x709A,0x7083,0x726A,0x72D6,0x72CB,0x72D8,0x72C9,/* 0x58-0x5F */
+ 0x72DC,0x72D2,0x72D4,0x72DA,0x72CC,0x72D1,0x73A4,0x73A1,/* 0x60-0x67 */
+ 0x73AD,0x73A6,0x73A2,0x73A0,0x73AC,0x739D,0x74DD,0x74E8,/* 0x68-0x6F */
+ 0x753F,0x7540,0x753E,0x758C,0x7598,0x76AF,0x76F3,0x76F1,/* 0x70-0x77 */
+ 0x76F0,0x76F5,0x77F8,0x77FC,0x77F9,0x77FB,0x77FA,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x77F7,0x7942,0x793F,0x79C5,0x7A78,0x7A7B,0x7AFB,/* 0xA0-0xA7 */
+ 0x7C75,0x7CFD,0x8035,0x808F,0x80AE,0x80A3,0x80B8,0x80B5,/* 0xA8-0xAF */
+ 0x80AD,0x8220,0x82A0,0x82C0,0x82AB,0x829A,0x8298,0x829B,/* 0xB0-0xB7 */
+ 0x82B5,0x82A7,0x82AE,0x82BC,0x829E,0x82BA,0x82B4,0x82A8,/* 0xB8-0xBF */
+ 0x82A1,0x82A9,0x82C2,0x82A4,0x82C3,0x82B6,0x82A2,0x8670,/* 0xC0-0xC7 */
+ 0x866F,0x866D,0x866E,0x8C56,0x8FD2,0x8FCB,0x8FD3,0x8FCD,/* 0xC8-0xCF */
+ 0x8FD6,0x8FD5,0x8FD7,0x90B2,0x90B4,0x90AF,0x90B3,0x90B0,/* 0xD0-0xD7 */
+ 0x9639,0x963D,0x963C,0x963A,0x9643,0x4FCD,0x4FC5,0x4FD3,/* 0xD8-0xDF */
+ 0x4FB2,0x4FC9,0x4FCB,0x4FC1,0x4FD4,0x4FDC,0x4FD9,0x4FBB,/* 0xE0-0xE7 */
+ 0x4FB3,0x4FDB,0x4FC7,0x4FD6,0x4FBA,0x4FC0,0x4FB9,0x4FEC,/* 0xE8-0xEF */
+ 0x5244,0x5249,0x52C0,0x52C2,0x533D,0x537C,0x5397,0x5396,/* 0xF0-0xF7 */
+ 0x5399,0x5398,0x54BA,0x54A1,0x54AD,0x54A5,0x54CF,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_CE[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x54C3,0x830D,0x54B7,0x54AE,0x54D6,0x54B6,0x54C5,0x54C6,/* 0x40-0x47 */
+ 0x54A0,0x5470,0x54BC,0x54A2,0x54BE,0x5472,0x54DE,0x54B0,/* 0x48-0x4F */
+ 0x57B5,0x579E,0x579F,0x57A4,0x578C,0x5797,0x579D,0x579B,/* 0x50-0x57 */
+ 0x5794,0x5798,0x578F,0x5799,0x57A5,0x579A,0x5795,0x58F4,/* 0x58-0x5F */
+ 0x590D,0x5953,0x59E1,0x59DE,0x59EE,0x5A00,0x59F1,0x59DD,/* 0x60-0x67 */
+ 0x59FA,0x59FD,0x59FC,0x59F6,0x59E4,0x59F2,0x59F7,0x59DB,/* 0x68-0x6F */
+ 0x59E9,0x59F3,0x59F5,0x59E0,0x59FE,0x59F4,0x59ED,0x5BA8,/* 0x70-0x77 */
+ 0x5C4C,0x5CD0,0x5CD8,0x5CCC,0x5CD7,0x5CCB,0x5CDB,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x5CDE,0x5CDA,0x5CC9,0x5CC7,0x5CCA,0x5CD6,0x5CD3,/* 0xA0-0xA7 */
+ 0x5CD4,0x5CCF,0x5CC8,0x5CC6,0x5CCE,0x5CDF,0x5CF8,0x5DF9,/* 0xA8-0xAF */
+ 0x5E21,0x5E22,0x5E23,0x5E20,0x5E24,0x5EB0,0x5EA4,0x5EA2,/* 0xB0-0xB7 */
+ 0x5E9B,0x5EA3,0x5EA5,0x5F07,0x5F2E,0x5F56,0x5F86,0x6037,/* 0xB8-0xBF */
+ 0x6039,0x6054,0x6072,0x605E,0x6045,0x6053,0x6047,0x6049,/* 0xC0-0xC7 */
+ 0x605B,0x604C,0x6040,0x6042,0x605F,0x6024,0x6044,0x6058,/* 0xC8-0xCF */
+ 0x6066,0x606E,0x6242,0x6243,0xF95B,0x630D,0x630B,0x62F5,/* 0xD0-0xD7 */
+ 0x630E,0x6303,0x62EB,0x62F9,0x630F,0x630C,0x62F8,0x62F6,/* 0xD8-0xDF */
+ 0x6300,0x6313,0x6314,0x62FA,0x6315,0x62FB,0x62F0,0x6541,/* 0xE0-0xE7 */
+ 0x6543,0x65AA,0x65BF,0x6636,0x6621,0x6632,0x6635,0x661C,/* 0xE8-0xEF */
+ 0x6626,0x6622,0x6633,0x662B,0x663A,0x661D,0x6634,0x6639,/* 0xF0-0xF7 */
+ 0x662E,0x670F,0x6710,0x67C1,0x67F2,0x67C8,0x67BA,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_CF[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x67DC,0x67BB,0x67F8,0x67D8,0x67C0,0x67B7,0x67C5,0x67EB,/* 0x40-0x47 */
+ 0x67E4,0x67DF,0x67B5,0x67CD,0x67B3,0x67F7,0x67F6,0x67EE,/* 0x48-0x4F */
+ 0x67E3,0x67C2,0x67B9,0x67CE,0x67E7,0x67F0,0x67B2,0x67FC,/* 0x50-0x57 */
+ 0x67C6,0x67ED,0x67CC,0x67AE,0x67E6,0x67DB,0x67FA,0x67C9,/* 0x58-0x5F */
+ 0x67CA,0x67C3,0x67EA,0x67CB,0x6B28,0x6B82,0x6B84,0x6BB6,/* 0x60-0x67 */
+ 0x6BD6,0x6BD8,0x6BE0,0x6C20,0x6C21,0x6D28,0x6D34,0x6D2D,/* 0x68-0x6F */
+ 0x6D1F,0x6D3C,0x6D3F,0x6D12,0x6D0A,0x6CDA,0x6D33,0x6D04,/* 0x70-0x77 */
+ 0x6D19,0x6D3A,0x6D1A,0x6D11,0x6D00,0x6D1D,0x6D42,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x6D01,0x6D18,0x6D37,0x6D03,0x6D0F,0x6D40,0x6D07,/* 0xA0-0xA7 */
+ 0x6D20,0x6D2C,0x6D08,0x6D22,0x6D09,0x6D10,0x70B7,0x709F,/* 0xA8-0xAF */
+ 0x70BE,0x70B1,0x70B0,0x70A1,0x70B4,0x70B5,0x70A9,0x7241,/* 0xB0-0xB7 */
+ 0x7249,0x724A,0x726C,0x7270,0x7273,0x726E,0x72CA,0x72E4,/* 0xB8-0xBF */
+ 0x72E8,0x72EB,0x72DF,0x72EA,0x72E6,0x72E3,0x7385,0x73CC,/* 0xC0-0xC7 */
+ 0x73C2,0x73C8,0x73C5,0x73B9,0x73B6,0x73B5,0x73B4,0x73EB,/* 0xC8-0xCF */
+ 0x73BF,0x73C7,0x73BE,0x73C3,0x73C6,0x73B8,0x73CB,0x74EC,/* 0xD0-0xD7 */
+ 0x74EE,0x752E,0x7547,0x7548,0x75A7,0x75AA,0x7679,0x76C4,/* 0xD8-0xDF */
+ 0x7708,0x7703,0x7704,0x7705,0x770A,0x76F7,0x76FB,0x76FA,/* 0xE0-0xE7 */
+ 0x77E7,0x77E8,0x7806,0x7811,0x7812,0x7805,0x7810,0x780F,/* 0xE8-0xEF */
+ 0x780E,0x7809,0x7803,0x7813,0x794A,0x794C,0x794B,0x7945,/* 0xF0-0xF7 */
+ 0x7944,0x79D5,0x79CD,0x79CF,0x79D6,0x79CE,0x7A80,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_D0[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x7A7E,0x7AD1,0x7B00,0x7B01,0x7C7A,0x7C78,0x7C79,0x7C7F,/* 0x40-0x47 */
+ 0x7C80,0x7C81,0x7D03,0x7D08,0x7D01,0x7F58,0x7F91,0x7F8D,/* 0x48-0x4F */
+ 0x7FBE,0x8007,0x800E,0x800F,0x8014,0x8037,0x80D8,0x80C7,/* 0x50-0x57 */
+ 0x80E0,0x80D1,0x80C8,0x80C2,0x80D0,0x80C5,0x80E3,0x80D9,/* 0x58-0x5F */
+ 0x80DC,0x80CA,0x80D5,0x80C9,0x80CF,0x80D7,0x80E6,0x80CD,/* 0x60-0x67 */
+ 0x81FF,0x8221,0x8294,0x82D9,0x82FE,0x82F9,0x8307,0x82E8,/* 0x68-0x6F */
+ 0x8300,0x82D5,0x833A,0x82EB,0x82D6,0x82F4,0x82EC,0x82E1,/* 0x70-0x77 */
+ 0x82F2,0x82F5,0x830C,0x82FB,0x82F6,0x82F0,0x82EA,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x82E4,0x82E0,0x82FA,0x82F3,0x82ED,0x8677,0x8674,/* 0xA0-0xA7 */
+ 0x867C,0x8673,0x8841,0x884E,0x8867,0x886A,0x8869,0x89D3,/* 0xA8-0xAF */
+ 0x8A04,0x8A07,0x8D72,0x8FE3,0x8FE1,0x8FEE,0x8FE0,0x90F1,/* 0xB0-0xB7 */
+ 0x90BD,0x90BF,0x90D5,0x90C5,0x90BE,0x90C7,0x90CB,0x90C8,/* 0xB8-0xBF */
+ 0x91D4,0x91D3,0x9654,0x964F,0x9651,0x9653,0x964A,0x964E,/* 0xC0-0xC7 */
+ 0x501E,0x5005,0x5007,0x5013,0x5022,0x5030,0x501B,0x4FF5,/* 0xC8-0xCF */
+ 0x4FF4,0x5033,0x5037,0x502C,0x4FF6,0x4FF7,0x5017,0x501C,/* 0xD0-0xD7 */
+ 0x5020,0x5027,0x5035,0x502F,0x5031,0x500E,0x515A,0x5194,/* 0xD8-0xDF */
+ 0x5193,0x51CA,0x51C4,0x51C5,0x51C8,0x51CE,0x5261,0x525A,/* 0xE0-0xE7 */
+ 0x5252,0x525E,0x525F,0x5255,0x5262,0x52CD,0x530E,0x539E,/* 0xE8-0xEF */
+ 0x5526,0x54E2,0x5517,0x5512,0x54E7,0x54F3,0x54E4,0x551A,/* 0xF0-0xF7 */
+ 0x54FF,0x5504,0x5508,0x54EB,0x5511,0x5505,0x54F1,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_D1[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x550A,0x54FB,0x54F7,0x54F8,0x54E0,0x550E,0x5503,0x550B,/* 0x40-0x47 */
+ 0x5701,0x5702,0x57CC,0x5832,0x57D5,0x57D2,0x57BA,0x57C6,/* 0x48-0x4F */
+ 0x57BD,0x57BC,0x57B8,0x57B6,0x57BF,0x57C7,0x57D0,0x57B9,/* 0x50-0x57 */
+ 0x57C1,0x590E,0x594A,0x5A19,0x5A16,0x5A2D,0x5A2E,0x5A15,/* 0x58-0x5F */
+ 0x5A0F,0x5A17,0x5A0A,0x5A1E,0x5A33,0x5B6C,0x5BA7,0x5BAD,/* 0x60-0x67 */
+ 0x5BAC,0x5C03,0x5C56,0x5C54,0x5CEC,0x5CFF,0x5CEE,0x5CF1,/* 0x68-0x6F */
+ 0x5CF7,0x5D00,0x5CF9,0x5E29,0x5E28,0x5EA8,0x5EAE,0x5EAA,/* 0x70-0x77 */
+ 0x5EAC,0x5F33,0x5F30,0x5F67,0x605D,0x605A,0x6067,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x6041,0x60A2,0x6088,0x6080,0x6092,0x6081,0x609D,/* 0xA0-0xA7 */
+ 0x6083,0x6095,0x609B,0x6097,0x6087,0x609C,0x608E,0x6219,/* 0xA8-0xAF */
+ 0x6246,0x62F2,0x6310,0x6356,0x632C,0x6344,0x6345,0x6336,/* 0xB0-0xB7 */
+ 0x6343,0x63E4,0x6339,0x634B,0x634A,0x633C,0x6329,0x6341,/* 0xB8-0xBF */
+ 0x6334,0x6358,0x6354,0x6359,0x632D,0x6347,0x6333,0x635A,/* 0xC0-0xC7 */
+ 0x6351,0x6338,0x6357,0x6340,0x6348,0x654A,0x6546,0x65C6,/* 0xC8-0xCF */
+ 0x65C3,0x65C4,0x65C2,0x664A,0x665F,0x6647,0x6651,0x6712,/* 0xD0-0xD7 */
+ 0x6713,0x681F,0x681A,0x6849,0x6832,0x6833,0x683B,0x684B,/* 0xD8-0xDF */
+ 0x684F,0x6816,0x6831,0x681C,0x6835,0x682B,0x682D,0x682F,/* 0xE0-0xE7 */
+ 0x684E,0x6844,0x6834,0x681D,0x6812,0x6814,0x6826,0x6828,/* 0xE8-0xEF */
+ 0x682E,0x684D,0x683A,0x6825,0x6820,0x6B2C,0x6B2F,0x6B2D,/* 0xF0-0xF7 */
+ 0x6B31,0x6B34,0x6B6D,0x8082,0x6B88,0x6BE6,0x6BE4,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_D2[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x6BE8,0x6BE3,0x6BE2,0x6BE7,0x6C25,0x6D7A,0x6D63,0x6D64,/* 0x40-0x47 */
+ 0x6D76,0x6D0D,0x6D61,0x6D92,0x6D58,0x6D62,0x6D6D,0x6D6F,/* 0x48-0x4F */
+ 0x6D91,0x6D8D,0x6DEF,0x6D7F,0x6D86,0x6D5E,0x6D67,0x6D60,/* 0x50-0x57 */
+ 0x6D97,0x6D70,0x6D7C,0x6D5F,0x6D82,0x6D98,0x6D2F,0x6D68,/* 0x58-0x5F */
+ 0x6D8B,0x6D7E,0x6D80,0x6D84,0x6D16,0x6D83,0x6D7B,0x6D7D,/* 0x60-0x67 */
+ 0x6D75,0x6D90,0x70DC,0x70D3,0x70D1,0x70DD,0x70CB,0x7F39,/* 0x68-0x6F */
+ 0x70E2,0x70D7,0x70D2,0x70DE,0x70E0,0x70D4,0x70CD,0x70C5,/* 0x70-0x77 */
+ 0x70C6,0x70C7,0x70DA,0x70CE,0x70E1,0x7242,0x7278,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x7277,0x7276,0x7300,0x72FA,0x72F4,0x72FE,0x72F6,/* 0xA0-0xA7 */
+ 0x72F3,0x72FB,0x7301,0x73D3,0x73D9,0x73E5,0x73D6,0x73BC,/* 0xA8-0xAF */
+ 0x73E7,0x73E3,0x73E9,0x73DC,0x73D2,0x73DB,0x73D4,0x73DD,/* 0xB0-0xB7 */
+ 0x73DA,0x73D7,0x73D8,0x73E8,0x74DE,0x74DF,0x74F4,0x74F5,/* 0xB8-0xBF */
+ 0x7521,0x755B,0x755F,0x75B0,0x75C1,0x75BB,0x75C4,0x75C0,/* 0xC0-0xC7 */
+ 0x75BF,0x75B6,0x75BA,0x768A,0x76C9,0x771D,0x771B,0x7710,/* 0xC8-0xCF */
+ 0x7713,0x7712,0x7723,0x7711,0x7715,0x7719,0x771A,0x7722,/* 0xD0-0xD7 */
+ 0x7727,0x7823,0x782C,0x7822,0x7835,0x782F,0x7828,0x782E,/* 0xD8-0xDF */
+ 0x782B,0x7821,0x7829,0x7833,0x782A,0x7831,0x7954,0x795B,/* 0xE0-0xE7 */
+ 0x794F,0x795C,0x7953,0x7952,0x7951,0x79EB,0x79EC,0x79E0,/* 0xE8-0xEF */
+ 0x79EE,0x79ED,0x79EA,0x79DC,0x79DE,0x79DD,0x7A86,0x7A89,/* 0xF0-0xF7 */
+ 0x7A85,0x7A8B,0x7A8C,0x7A8A,0x7A87,0x7AD8,0x7B10,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_D3[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x7B04,0x7B13,0x7B05,0x7B0F,0x7B08,0x7B0A,0x7B0E,0x7B09,/* 0x40-0x47 */
+ 0x7B12,0x7C84,0x7C91,0x7C8A,0x7C8C,0x7C88,0x7C8D,0x7C85,/* 0x48-0x4F */
+ 0x7D1E,0x7D1D,0x7D11,0x7D0E,0x7D18,0x7D16,0x7D13,0x7D1F,/* 0x50-0x57 */
+ 0x7D12,0x7D0F,0x7D0C,0x7F5C,0x7F61,0x7F5E,0x7F60,0x7F5D,/* 0x58-0x5F */
+ 0x7F5B,0x7F96,0x7F92,0x7FC3,0x7FC2,0x7FC0,0x8016,0x803E,/* 0x60-0x67 */
+ 0x8039,0x80FA,0x80F2,0x80F9,0x80F5,0x8101,0x80FB,0x8100,/* 0x68-0x6F */
+ 0x8201,0x822F,0x8225,0x8333,0x832D,0x8344,0x8319,0x8351,/* 0x70-0x77 */
+ 0x8325,0x8356,0x833F,0x8341,0x8326,0x831C,0x8322,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x8342,0x834E,0x831B,0x832A,0x8308,0x833C,0x834D,/* 0xA0-0xA7 */
+ 0x8316,0x8324,0x8320,0x8337,0x832F,0x8329,0x8347,0x8345,/* 0xA8-0xAF */
+ 0x834C,0x8353,0x831E,0x832C,0x834B,0x8327,0x8348,0x8653,/* 0xB0-0xB7 */
+ 0x8652,0x86A2,0x86A8,0x8696,0x868D,0x8691,0x869E,0x8687,/* 0xB8-0xBF */
+ 0x8697,0x8686,0x868B,0x869A,0x8685,0x86A5,0x8699,0x86A1,/* 0xC0-0xC7 */
+ 0x86A7,0x8695,0x8698,0x868E,0x869D,0x8690,0x8694,0x8843,/* 0xC8-0xCF */
+ 0x8844,0x886D,0x8875,0x8876,0x8872,0x8880,0x8871,0x887F,/* 0xD0-0xD7 */
+ 0x886F,0x8883,0x887E,0x8874,0x887C,0x8A12,0x8C47,0x8C57,/* 0xD8-0xDF */
+ 0x8C7B,0x8CA4,0x8CA3,0x8D76,0x8D78,0x8DB5,0x8DB7,0x8DB6,/* 0xE0-0xE7 */
+ 0x8ED1,0x8ED3,0x8FFE,0x8FF5,0x9002,0x8FFF,0x8FFB,0x9004,/* 0xE8-0xEF */
+ 0x8FFC,0x8FF6,0x90D6,0x90E0,0x90D9,0x90DA,0x90E3,0x90DF,/* 0xF0-0xF7 */
+ 0x90E5,0x90D8,0x90DB,0x90D7,0x90DC,0x90E4,0x9150,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_D4[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x914E,0x914F,0x91D5,0x91E2,0x91DA,0x965C,0x965F,0x96BC,/* 0x40-0x47 */
+ 0x98E3,0x9ADF,0x9B2F,0x4E7F,0x5070,0x506A,0x5061,0x505E,/* 0x48-0x4F */
+ 0x5060,0x5053,0x504B,0x505D,0x5072,0x5048,0x504D,0x5041,/* 0x50-0x57 */
+ 0x505B,0x504A,0x5062,0x5015,0x5045,0x505F,0x5069,0x506B,/* 0x58-0x5F */
+ 0x5063,0x5064,0x5046,0x5040,0x506E,0x5073,0x5057,0x5051,/* 0x60-0x67 */
+ 0x51D0,0x526B,0x526D,0x526C,0x526E,0x52D6,0x52D3,0x532D,/* 0x68-0x6F */
+ 0x539C,0x5575,0x5576,0x553C,0x554D,0x5550,0x5534,0x552A,/* 0x70-0x77 */
+ 0x5551,0x5562,0x5536,0x5535,0x5530,0x5552,0x5545,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x550C,0x5532,0x5565,0x554E,0x5539,0x5548,0x552D,/* 0xA0-0xA7 */
+ 0x553B,0x5540,0x554B,0x570A,0x5707,0x57FB,0x5814,0x57E2,/* 0xA8-0xAF */
+ 0x57F6,0x57DC,0x57F4,0x5800,0x57ED,0x57FD,0x5808,0x57F8,/* 0xB0-0xB7 */
+ 0x580B,0x57F3,0x57CF,0x5807,0x57EE,0x57E3,0x57F2,0x57E5,/* 0xB8-0xBF */
+ 0x57EC,0x57E1,0x580E,0x57FC,0x5810,0x57E7,0x5801,0x580C,/* 0xC0-0xC7 */
+ 0x57F1,0x57E9,0x57F0,0x580D,0x5804,0x595C,0x5A60,0x5A58,/* 0xC8-0xCF */
+ 0x5A55,0x5A67,0x5A5E,0x5A38,0x5A35,0x5A6D,0x5A50,0x5A5F,/* 0xD0-0xD7 */
+ 0x5A65,0x5A6C,0x5A53,0x5A64,0x5A57,0x5A43,0x5A5D,0x5A52,/* 0xD8-0xDF */
+ 0x5A44,0x5A5B,0x5A48,0x5A8E,0x5A3E,0x5A4D,0x5A39,0x5A4C,/* 0xE0-0xE7 */
+ 0x5A70,0x5A69,0x5A47,0x5A51,0x5A56,0x5A42,0x5A5C,0x5B72,/* 0xE8-0xEF */
+ 0x5B6E,0x5BC1,0x5BC0,0x5C59,0x5D1E,0x5D0B,0x5D1D,0x5D1A,/* 0xF0-0xF7 */
+ 0x5D20,0x5D0C,0x5D28,0x5D0D,0x5D26,0x5D25,0x5D0F,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_D5[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x5D30,0x5D12,0x5D23,0x5D1F,0x5D2E,0x5E3E,0x5E34,0x5EB1,/* 0x40-0x47 */
+ 0x5EB4,0x5EB9,0x5EB2,0x5EB3,0x5F36,0x5F38,0x5F9B,0x5F96,/* 0x48-0x4F */
+ 0x5F9F,0x608A,0x6090,0x6086,0x60BE,0x60B0,0x60BA,0x60D3,/* 0x50-0x57 */
+ 0x60D4,0x60CF,0x60E4,0x60D9,0x60DD,0x60C8,0x60B1,0x60DB,/* 0x58-0x5F */
+ 0x60B7,0x60CA,0x60BF,0x60C3,0x60CD,0x60C0,0x6332,0x6365,/* 0x60-0x67 */
+ 0x638A,0x6382,0x637D,0x63BD,0x639E,0x63AD,0x639D,0x6397,/* 0x68-0x6F */
+ 0x63AB,0x638E,0x636F,0x6387,0x6390,0x636E,0x63AF,0x6375,/* 0x70-0x77 */
+ 0x639C,0x636D,0x63AE,0x637C,0x63A4,0x633B,0x639F,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x6378,0x6385,0x6381,0x6391,0x638D,0x6370,0x6553,/* 0xA0-0xA7 */
+ 0x65CD,0x6665,0x6661,0x665B,0x6659,0x665C,0x6662,0x6718,/* 0xA8-0xAF */
+ 0x6879,0x6887,0x6890,0x689C,0x686D,0x686E,0x68AE,0x68AB,/* 0xB0-0xB7 */
+ 0x6956,0x686F,0x68A3,0x68AC,0x68A9,0x6875,0x6874,0x68B2,/* 0xB8-0xBF */
+ 0x688F,0x6877,0x6892,0x687C,0x686B,0x6872,0x68AA,0x6880,/* 0xC0-0xC7 */
+ 0x6871,0x687E,0x689B,0x6896,0x688B,0x68A0,0x6889,0x68A4,/* 0xC8-0xCF */
+ 0x6878,0x687B,0x6891,0x688C,0x688A,0x687D,0x6B36,0x6B33,/* 0xD0-0xD7 */
+ 0x6B37,0x6B38,0x6B91,0x6B8F,0x6B8D,0x6B8E,0x6B8C,0x6C2A,/* 0xD8-0xDF */
+ 0x6DC0,0x6DAB,0x6DB4,0x6DB3,0x6E74,0x6DAC,0x6DE9,0x6DE2,/* 0xE0-0xE7 */
+ 0x6DB7,0x6DF6,0x6DD4,0x6E00,0x6DC8,0x6DE0,0x6DDF,0x6DD6,/* 0xE8-0xEF */
+ 0x6DBE,0x6DE5,0x6DDC,0x6DDD,0x6DDB,0x6DF4,0x6DCA,0x6DBD,/* 0xF0-0xF7 */
+ 0x6DED,0x6DF0,0x6DBA,0x6DD5,0x6DC2,0x6DCF,0x6DC9,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_D6[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x6DD0,0x6DF2,0x6DD3,0x6DFD,0x6DD7,0x6DCD,0x6DE3,0x6DBB,/* 0x40-0x47 */
+ 0x70FA,0x710D,0x70F7,0x7117,0x70F4,0x710C,0x70F0,0x7104,/* 0x48-0x4F */
+ 0x70F3,0x7110,0x70FC,0x70FF,0x7106,0x7113,0x7100,0x70F8,/* 0x50-0x57 */
+ 0x70F6,0x710B,0x7102,0x710E,0x727E,0x727B,0x727C,0x727F,/* 0x58-0x5F */
+ 0x731D,0x7317,0x7307,0x7311,0x7318,0x730A,0x7308,0x72FF,/* 0x60-0x67 */
+ 0x730F,0x731E,0x7388,0x73F6,0x73F8,0x73F5,0x7404,0x7401,/* 0x68-0x6F */
+ 0x73FD,0x7407,0x7400,0x73FA,0x73FC,0x73FF,0x740C,0x740B,/* 0x70-0x77 */
+ 0x73F4,0x7408,0x7564,0x7563,0x75CE,0x75D2,0x75CF,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x75CB,0x75CC,0x75D1,0x75D0,0x768F,0x7689,0x76D3,/* 0xA0-0xA7 */
+ 0x7739,0x772F,0x772D,0x7731,0x7732,0x7734,0x7733,0x773D,/* 0xA8-0xAF */
+ 0x7725,0x773B,0x7735,0x7848,0x7852,0x7849,0x784D,0x784A,/* 0xB0-0xB7 */
+ 0x784C,0x7826,0x7845,0x7850,0x7964,0x7967,0x7969,0x796A,/* 0xB8-0xBF */
+ 0x7963,0x796B,0x7961,0x79BB,0x79FA,0x79F8,0x79F6,0x79F7,/* 0xC0-0xC7 */
+ 0x7A8F,0x7A94,0x7A90,0x7B35,0x7B47,0x7B34,0x7B25,0x7B30,/* 0xC8-0xCF */
+ 0x7B22,0x7B24,0x7B33,0x7B18,0x7B2A,0x7B1D,0x7B31,0x7B2B,/* 0xD0-0xD7 */
+ 0x7B2D,0x7B2F,0x7B32,0x7B38,0x7B1A,0x7B23,0x7C94,0x7C98,/* 0xD8-0xDF */
+ 0x7C96,0x7CA3,0x7D35,0x7D3D,0x7D38,0x7D36,0x7D3A,0x7D45,/* 0xE0-0xE7 */
+ 0x7D2C,0x7D29,0x7D41,0x7D47,0x7D3E,0x7D3F,0x7D4A,0x7D3B,/* 0xE8-0xEF */
+ 0x7D28,0x7F63,0x7F95,0x7F9C,0x7F9D,0x7F9B,0x7FCA,0x7FCB,/* 0xF0-0xF7 */
+ 0x7FCD,0x7FD0,0x7FD1,0x7FC7,0x7FCF,0x7FC9,0x801F,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_D7[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x801E,0x801B,0x8047,0x8043,0x8048,0x8118,0x8125,0x8119,/* 0x40-0x47 */
+ 0x811B,0x812D,0x811F,0x812C,0x811E,0x8121,0x8115,0x8127,/* 0x48-0x4F */
+ 0x811D,0x8122,0x8211,0x8238,0x8233,0x823A,0x8234,0x8232,/* 0x50-0x57 */
+ 0x8274,0x8390,0x83A3,0x83A8,0x838D,0x837A,0x8373,0x83A4,/* 0x58-0x5F */
+ 0x8374,0x838F,0x8381,0x8395,0x8399,0x8375,0x8394,0x83A9,/* 0x60-0x67 */
+ 0x837D,0x8383,0x838C,0x839D,0x839B,0x83AA,0x838B,0x837E,/* 0x68-0x6F */
+ 0x83A5,0x83AF,0x8388,0x8397,0x83B0,0x837F,0x83A6,0x8387,/* 0x70-0x77 */
+ 0x83AE,0x8376,0x839A,0x8659,0x8656,0x86BF,0x86B7,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x86C2,0x86C1,0x86C5,0x86BA,0x86B0,0x86C8,0x86B9,/* 0xA0-0xA7 */
+ 0x86B3,0x86B8,0x86CC,0x86B4,0x86BB,0x86BC,0x86C3,0x86BD,/* 0xA8-0xAF */
+ 0x86BE,0x8852,0x8889,0x8895,0x88A8,0x88A2,0x88AA,0x889A,/* 0xB0-0xB7 */
+ 0x8891,0x88A1,0x889F,0x8898,0x88A7,0x8899,0x889B,0x8897,/* 0xB8-0xBF */
+ 0x88A4,0x88AC,0x888C,0x8893,0x888E,0x8982,0x89D6,0x89D9,/* 0xC0-0xC7 */
+ 0x89D5,0x8A30,0x8A27,0x8A2C,0x8A1E,0x8C39,0x8C3B,0x8C5C,/* 0xC8-0xCF */
+ 0x8C5D,0x8C7D,0x8CA5,0x8D7D,0x8D7B,0x8D79,0x8DBC,0x8DC2,/* 0xD0-0xD7 */
+ 0x8DB9,0x8DBF,0x8DC1,0x8ED8,0x8EDE,0x8EDD,0x8EDC,0x8ED7,/* 0xD8-0xDF */
+ 0x8EE0,0x8EE1,0x9024,0x900B,0x9011,0x901C,0x900C,0x9021,/* 0xE0-0xE7 */
+ 0x90EF,0x90EA,0x90F0,0x90F4,0x90F2,0x90F3,0x90D4,0x90EB,/* 0xE8-0xEF */
+ 0x90EC,0x90E9,0x9156,0x9158,0x915A,0x9153,0x9155,0x91EC,/* 0xF0-0xF7 */
+ 0x91F4,0x91F1,0x91F3,0x91F8,0x91E4,0x91F9,0x91EA,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_D8[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x91EB,0x91F7,0x91E8,0x91EE,0x957A,0x9586,0x9588,0x967C,/* 0x40-0x47 */
+ 0x966D,0x966B,0x9671,0x966F,0x96BF,0x976A,0x9804,0x98E5,/* 0x48-0x4F */
+ 0x9997,0x509B,0x5095,0x5094,0x509E,0x508B,0x50A3,0x5083,/* 0x50-0x57 */
+ 0x508C,0x508E,0x509D,0x5068,0x509C,0x5092,0x5082,0x5087,/* 0x58-0x5F */
+ 0x515F,0x51D4,0x5312,0x5311,0x53A4,0x53A7,0x5591,0x55A8,/* 0x60-0x67 */
+ 0x55A5,0x55AD,0x5577,0x5645,0x55A2,0x5593,0x5588,0x558F,/* 0x68-0x6F */
+ 0x55B5,0x5581,0x55A3,0x5592,0x55A4,0x557D,0x558C,0x55A6,/* 0x70-0x77 */
+ 0x557F,0x5595,0x55A1,0x558E,0x570C,0x5829,0x5837,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x5819,0x581E,0x5827,0x5823,0x5828,0x57F5,0x5848,/* 0xA0-0xA7 */
+ 0x5825,0x581C,0x581B,0x5833,0x583F,0x5836,0x582E,0x5839,/* 0xA8-0xAF */
+ 0x5838,0x582D,0x582C,0x583B,0x5961,0x5AAF,0x5A94,0x5A9F,/* 0xB0-0xB7 */
+ 0x5A7A,0x5AA2,0x5A9E,0x5A78,0x5AA6,0x5A7C,0x5AA5,0x5AAC,/* 0xB8-0xBF */
+ 0x5A95,0x5AAE,0x5A37,0x5A84,0x5A8A,0x5A97,0x5A83,0x5A8B,/* 0xC0-0xC7 */
+ 0x5AA9,0x5A7B,0x5A7D,0x5A8C,0x5A9C,0x5A8F,0x5A93,0x5A9D,/* 0xC8-0xCF */
+ 0x5BEA,0x5BCD,0x5BCB,0x5BD4,0x5BD1,0x5BCA,0x5BCE,0x5C0C,/* 0xD0-0xD7 */
+ 0x5C30,0x5D37,0x5D43,0x5D6B,0x5D41,0x5D4B,0x5D3F,0x5D35,/* 0xD8-0xDF */
+ 0x5D51,0x5D4E,0x5D55,0x5D33,0x5D3A,0x5D52,0x5D3D,0x5D31,/* 0xE0-0xE7 */
+ 0x5D59,0x5D42,0x5D39,0x5D49,0x5D38,0x5D3C,0x5D32,0x5D36,/* 0xE8-0xEF */
+ 0x5D40,0x5D45,0x5E44,0x5E41,0x5F58,0x5FA6,0x5FA5,0x5FAB,/* 0xF0-0xF7 */
+ 0x60C9,0x60B9,0x60CC,0x60E2,0x60CE,0x60C4,0x6114,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_D9[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x60F2,0x610A,0x6116,0x6105,0x60F5,0x6113,0x60F8,0x60FC,/* 0x40-0x47 */
+ 0x60FE,0x60C1,0x6103,0x6118,0x611D,0x6110,0x60FF,0x6104,/* 0x48-0x4F */
+ 0x610B,0x624A,0x6394,0x63B1,0x63B0,0x63CE,0x63E5,0x63E8,/* 0x50-0x57 */
+ 0x63EF,0x63C3,0x649D,0x63F3,0x63CA,0x63E0,0x63F6,0x63D5,/* 0x58-0x5F */
+ 0x63F2,0x63F5,0x6461,0x63DF,0x63BE,0x63DD,0x63DC,0x63C4,/* 0x60-0x67 */
+ 0x63D8,0x63D3,0x63C2,0x63C7,0x63CC,0x63CB,0x63C8,0x63F0,/* 0x68-0x6F */
+ 0x63D7,0x63D9,0x6532,0x6567,0x656A,0x6564,0x655C,0x6568,/* 0x70-0x77 */
+ 0x6565,0x658C,0x659D,0x659E,0x65AE,0x65D0,0x65D2,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x667C,0x666C,0x667B,0x6680,0x6671,0x6679,0x666A,/* 0xA0-0xA7 */
+ 0x6672,0x6701,0x690C,0x68D3,0x6904,0x68DC,0x692A,0x68EC,/* 0xA8-0xAF */
+ 0x68EA,0x68F1,0x690F,0x68D6,0x68F7,0x68EB,0x68E4,0x68F6,/* 0xB0-0xB7 */
+ 0x6913,0x6910,0x68F3,0x68E1,0x6907,0x68CC,0x6908,0x6970,/* 0xB8-0xBF */
+ 0x68B4,0x6911,0x68EF,0x68C6,0x6914,0x68F8,0x68D0,0x68FD,/* 0xC0-0xC7 */
+ 0x68FC,0x68E8,0x690B,0x690A,0x6917,0x68CE,0x68C8,0x68DD,/* 0xC8-0xCF */
+ 0x68DE,0x68E6,0x68F4,0x68D1,0x6906,0x68D4,0x68E9,0x6915,/* 0xD0-0xD7 */
+ 0x6925,0x68C7,0x6B39,0x6B3B,0x6B3F,0x6B3C,0x6B94,0x6B97,/* 0xD8-0xDF */
+ 0x6B99,0x6B95,0x6BBD,0x6BF0,0x6BF2,0x6BF3,0x6C30,0x6DFC,/* 0xE0-0xE7 */
+ 0x6E46,0x6E47,0x6E1F,0x6E49,0x6E88,0x6E3C,0x6E3D,0x6E45,/* 0xE8-0xEF */
+ 0x6E62,0x6E2B,0x6E3F,0x6E41,0x6E5D,0x6E73,0x6E1C,0x6E33,/* 0xF0-0xF7 */
+ 0x6E4B,0x6E40,0x6E51,0x6E3B,0x6E03,0x6E2E,0x6E5E,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_DA[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x6E68,0x6E5C,0x6E61,0x6E31,0x6E28,0x6E60,0x6E71,0x6E6B,/* 0x40-0x47 */
+ 0x6E39,0x6E22,0x6E30,0x6E53,0x6E65,0x6E27,0x6E78,0x6E64,/* 0x48-0x4F */
+ 0x6E77,0x6E55,0x6E79,0x6E52,0x6E66,0x6E35,0x6E36,0x6E5A,/* 0x50-0x57 */
+ 0x7120,0x711E,0x712F,0x70FB,0x712E,0x7131,0x7123,0x7125,/* 0x58-0x5F */
+ 0x7122,0x7132,0x711F,0x7128,0x713A,0x711B,0x724B,0x725A,/* 0x60-0x67 */
+ 0x7288,0x7289,0x7286,0x7285,0x728B,0x7312,0x730B,0x7330,/* 0x68-0x6F */
+ 0x7322,0x7331,0x7333,0x7327,0x7332,0x732D,0x7326,0x7323,/* 0x70-0x77 */
+ 0x7335,0x730C,0x742E,0x742C,0x7430,0x742B,0x7416,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x741A,0x7421,0x742D,0x7431,0x7424,0x7423,0x741D,/* 0xA0-0xA7 */
+ 0x7429,0x7420,0x7432,0x74FB,0x752F,0x756F,0x756C,0x75E7,/* 0xA8-0xAF */
+ 0x75DA,0x75E1,0x75E6,0x75DD,0x75DF,0x75E4,0x75D7,0x7695,/* 0xB0-0xB7 */
+ 0x7692,0x76DA,0x7746,0x7747,0x7744,0x774D,0x7745,0x774A,/* 0xB8-0xBF */
+ 0x774E,0x774B,0x774C,0x77DE,0x77EC,0x7860,0x7864,0x7865,/* 0xC0-0xC7 */
+ 0x785C,0x786D,0x7871,0x786A,0x786E,0x7870,0x7869,0x7868,/* 0xC8-0xCF */
+ 0x785E,0x7862,0x7974,0x7973,0x7972,0x7970,0x7A02,0x7A0A,/* 0xD0-0xD7 */
+ 0x7A03,0x7A0C,0x7A04,0x7A99,0x7AE6,0x7AE4,0x7B4A,0x7B3B,/* 0xD8-0xDF */
+ 0x7B44,0x7B48,0x7B4C,0x7B4E,0x7B40,0x7B58,0x7B45,0x7CA2,/* 0xE0-0xE7 */
+ 0x7C9E,0x7CA8,0x7CA1,0x7D58,0x7D6F,0x7D63,0x7D53,0x7D56,/* 0xE8-0xEF */
+ 0x7D67,0x7D6A,0x7D4F,0x7D6D,0x7D5C,0x7D6B,0x7D52,0x7D54,/* 0xF0-0xF7 */
+ 0x7D69,0x7D51,0x7D5F,0x7D4E,0x7F3E,0x7F3F,0x7F65,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_DB[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x7F66,0x7FA2,0x7FA0,0x7FA1,0x7FD7,0x8051,0x804F,0x8050,/* 0x40-0x47 */
+ 0x80FE,0x80D4,0x8143,0x814A,0x8152,0x814F,0x8147,0x813D,/* 0x48-0x4F */
+ 0x814D,0x813A,0x81E6,0x81EE,0x81F7,0x81F8,0x81F9,0x8204,/* 0x50-0x57 */
+ 0x823C,0x823D,0x823F,0x8275,0x833B,0x83CF,0x83F9,0x8423,/* 0x58-0x5F */
+ 0x83C0,0x83E8,0x8412,0x83E7,0x83E4,0x83FC,0x83F6,0x8410,/* 0x60-0x67 */
+ 0x83C6,0x83C8,0x83EB,0x83E3,0x83BF,0x8401,0x83DD,0x83E5,/* 0x68-0x6F */
+ 0x83D8,0x83FF,0x83E1,0x83CB,0x83CE,0x83D6,0x83F5,0xF93E,/* 0x70-0x77 */
+ 0x8409,0x840F,0x83DE,0x8411,0x8406,0x83C2,0x83F3,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x83D5,0x83FA,0x83C7,0x83D1,0x83EA,0x8413,0x83C3,/* 0xA0-0xA7 */
+ 0x83EC,0x83EE,0x83C4,0x83FB,0x83D7,0x83E2,0x841B,0x83DB,/* 0xA8-0xAF */
+ 0x83FE,0x86D8,0x86E2,0x86E6,0x86D3,0x86E3,0x86DA,0x86EA,/* 0xB0-0xB7 */
+ 0x86DD,0x86EB,0x86DC,0x86EC,0x86E9,0x86D7,0x86E8,0x86D1,/* 0xB8-0xBF */
+ 0x8848,0x8856,0x8855,0x88BA,0x88D7,0x88B9,0x88B8,0x88C0,/* 0xC0-0xC7 */
+ 0x88BE,0x88B6,0x88BC,0x88B7,0x88BD,0x88B2,0x8901,0x88C9,/* 0xC8-0xCF */
+ 0x8995,0x8998,0x8997,0x89DD,0x89DA,0x89DB,0x8A4E,0x8A4D,/* 0xD0-0xD7 */
+ 0x8A39,0x8A59,0x8A40,0x8A57,0x8A58,0x8A44,0x8A45,0x8A52,/* 0xD8-0xDF */
+ 0x8A48,0x8A51,0x8A4A,0x8A4C,0x8A4F,0x8C5F,0x8C81,0x8C80,/* 0xE0-0xE7 */
+ 0x8CBA,0x8CBE,0x8CB0,0x8CB9,0x8CB5,0x8D84,0x8D80,0x8D89,/* 0xE8-0xEF */
+ 0x8DD8,0x8DD3,0x8DCD,0x8DC7,0x8DD6,0x8DDC,0x8DCF,0x8DD5,/* 0xF0-0xF7 */
+ 0x8DD9,0x8DC8,0x8DD7,0x8DC5,0x8EEF,0x8EF7,0x8EFA,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_DC[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x8EF9,0x8EE6,0x8EEE,0x8EE5,0x8EF5,0x8EE7,0x8EE8,0x8EF6,/* 0x40-0x47 */
+ 0x8EEB,0x8EF1,0x8EEC,0x8EF4,0x8EE9,0x902D,0x9034,0x902F,/* 0x48-0x4F */
+ 0x9106,0x912C,0x9104,0x90FF,0x90FC,0x9108,0x90F9,0x90FB,/* 0x50-0x57 */
+ 0x9101,0x9100,0x9107,0x9105,0x9103,0x9161,0x9164,0x915F,/* 0x58-0x5F */
+ 0x9162,0x9160,0x9201,0x920A,0x9225,0x9203,0x921A,0x9226,/* 0x60-0x67 */
+ 0x920F,0x920C,0x9200,0x9212,0x91FF,0x91FD,0x9206,0x9204,/* 0x68-0x6F */
+ 0x9227,0x9202,0x921C,0x9224,0x9219,0x9217,0x9205,0x9216,/* 0x70-0x77 */
+ 0x957B,0x958D,0x958C,0x9590,0x9687,0x967E,0x9688,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x9689,0x9683,0x9680,0x96C2,0x96C8,0x96C3,0x96F1,/* 0xA0-0xA7 */
+ 0x96F0,0x976C,0x9770,0x976E,0x9807,0x98A9,0x98EB,0x9CE6,/* 0xA8-0xAF */
+ 0x9EF9,0x4E83,0x4E84,0x4EB6,0x50BD,0x50BF,0x50C6,0x50AE,/* 0xB0-0xB7 */
+ 0x50C4,0x50CA,0x50B4,0x50C8,0x50C2,0x50B0,0x50C1,0x50BA,/* 0xB8-0xBF */
+ 0x50B1,0x50CB,0x50C9,0x50B6,0x50B8,0x51D7,0x527A,0x5278,/* 0xC0-0xC7 */
+ 0x527B,0x527C,0x55C3,0x55DB,0x55CC,0x55D0,0x55CB,0x55CA,/* 0xC8-0xCF */
+ 0x55DD,0x55C0,0x55D4,0x55C4,0x55E9,0x55BF,0x55D2,0x558D,/* 0xD0-0xD7 */
+ 0x55CF,0x55D5,0x55E2,0x55D6,0x55C8,0x55F2,0x55CD,0x55D9,/* 0xD8-0xDF */
+ 0x55C2,0x5714,0x5853,0x5868,0x5864,0x584F,0x584D,0x5849,/* 0xE0-0xE7 */
+ 0x586F,0x5855,0x584E,0x585D,0x5859,0x5865,0x585B,0x583D,/* 0xE8-0xEF */
+ 0x5863,0x5871,0x58FC,0x5AC7,0x5AC4,0x5ACB,0x5ABA,0x5AB8,/* 0xF0-0xF7 */
+ 0x5AB1,0x5AB5,0x5AB0,0x5ABF,0x5AC8,0x5ABB,0x5AC6,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_DD[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x5AB7,0x5AC0,0x5ACA,0x5AB4,0x5AB6,0x5ACD,0x5AB9,0x5A90,/* 0x40-0x47 */
+ 0x5BD6,0x5BD8,0x5BD9,0x5C1F,0x5C33,0x5D71,0x5D63,0x5D4A,/* 0x48-0x4F */
+ 0x5D65,0x5D72,0x5D6C,0x5D5E,0x5D68,0x5D67,0x5D62,0x5DF0,/* 0x50-0x57 */
+ 0x5E4F,0x5E4E,0x5E4A,0x5E4D,0x5E4B,0x5EC5,0x5ECC,0x5EC6,/* 0x58-0x5F */
+ 0x5ECB,0x5EC7,0x5F40,0x5FAF,0x5FAD,0x60F7,0x6149,0x614A,/* 0x60-0x67 */
+ 0x612B,0x6145,0x6136,0x6132,0x612E,0x6146,0x612F,0x614F,/* 0x68-0x6F */
+ 0x6129,0x6140,0x6220,0x9168,0x6223,0x6225,0x6224,0x63C5,/* 0x70-0x77 */
+ 0x63F1,0x63EB,0x6410,0x6412,0x6409,0x6420,0x6424,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x6433,0x6443,0x641F,0x6415,0x6418,0x6439,0x6437,/* 0xA0-0xA7 */
+ 0x6422,0x6423,0x640C,0x6426,0x6430,0x6428,0x6441,0x6435,/* 0xA8-0xAF */
+ 0x642F,0x640A,0x641A,0x6440,0x6425,0x6427,0x640B,0x63E7,/* 0xB0-0xB7 */
+ 0x641B,0x642E,0x6421,0x640E,0x656F,0x6592,0x65D3,0x6686,/* 0xB8-0xBF */
+ 0x668C,0x6695,0x6690,0x668B,0x668A,0x6699,0x6694,0x6678,/* 0xC0-0xC7 */
+ 0x6720,0x6966,0x695F,0x6938,0x694E,0x6962,0x6971,0x693F,/* 0xC8-0xCF */
+ 0x6945,0x696A,0x6939,0x6942,0x6957,0x6959,0x697A,0x6948,/* 0xD0-0xD7 */
+ 0x6949,0x6935,0x696C,0x6933,0x693D,0x6965,0x68F0,0x6978,/* 0xD8-0xDF */
+ 0x6934,0x6969,0x6940,0x696F,0x6944,0x6976,0x6958,0x6941,/* 0xE0-0xE7 */
+ 0x6974,0x694C,0x693B,0x694B,0x6937,0x695C,0x694F,0x6951,/* 0xE8-0xEF */
+ 0x6932,0x6952,0x692F,0x697B,0x693C,0x6B46,0x6B45,0x6B43,/* 0xF0-0xF7 */
+ 0x6B42,0x6B48,0x6B41,0x6B9B,0xFA0D,0x6BFB,0x6BFC,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_DE[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x6BF9,0x6BF7,0x6BF8,0x6E9B,0x6ED6,0x6EC8,0x6E8F,0x6EC0,/* 0x40-0x47 */
+ 0x6E9F,0x6E93,0x6E94,0x6EA0,0x6EB1,0x6EB9,0x6EC6,0x6ED2,/* 0x48-0x4F */
+ 0x6EBD,0x6EC1,0x6E9E,0x6EC9,0x6EB7,0x6EB0,0x6ECD,0x6EA6,/* 0x50-0x57 */
+ 0x6ECF,0x6EB2,0x6EBE,0x6EC3,0x6EDC,0x6ED8,0x6E99,0x6E92,/* 0x58-0x5F */
+ 0x6E8E,0x6E8D,0x6EA4,0x6EA1,0x6EBF,0x6EB3,0x6ED0,0x6ECA,/* 0x60-0x67 */
+ 0x6E97,0x6EAE,0x6EA3,0x7147,0x7154,0x7152,0x7163,0x7160,/* 0x68-0x6F */
+ 0x7141,0x715D,0x7162,0x7172,0x7178,0x716A,0x7161,0x7142,/* 0x70-0x77 */
+ 0x7158,0x7143,0x714B,0x7170,0x715F,0x7150,0x7153,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x7144,0x714D,0x715A,0x724F,0x728D,0x728C,0x7291,/* 0xA0-0xA7 */
+ 0x7290,0x728E,0x733C,0x7342,0x733B,0x733A,0x7340,0x734A,/* 0xA8-0xAF */
+ 0x7349,0x7444,0x744A,0x744B,0x7452,0x7451,0x7457,0x7440,/* 0xB0-0xB7 */
+ 0x744F,0x7450,0x744E,0x7442,0x7446,0x744D,0x7454,0x74E1,/* 0xB8-0xBF */
+ 0x74FF,0x74FE,0x74FD,0x751D,0x7579,0x7577,0x6983,0x75EF,/* 0xC0-0xC7 */
+ 0x760F,0x7603,0x75F7,0x75FE,0x75FC,0x75F9,0x75F8,0x7610,/* 0xC8-0xCF */
+ 0x75FB,0x75F6,0x75ED,0x75F5,0x75FD,0x7699,0x76B5,0x76DD,/* 0xD0-0xD7 */
+ 0x7755,0x775F,0x7760,0x7752,0x7756,0x775A,0x7769,0x7767,/* 0xD8-0xDF */
+ 0x7754,0x7759,0x776D,0x77E0,0x7887,0x789A,0x7894,0x788F,/* 0xE0-0xE7 */
+ 0x7884,0x7895,0x7885,0x7886,0x78A1,0x7883,0x7879,0x7899,/* 0xE8-0xEF */
+ 0x7880,0x7896,0x787B,0x797C,0x7982,0x797D,0x7979,0x7A11,/* 0xF0-0xF7 */
+ 0x7A18,0x7A19,0x7A12,0x7A17,0x7A15,0x7A22,0x7A13,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_DF[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x7A1B,0x7A10,0x7AA3,0x7AA2,0x7A9E,0x7AEB,0x7B66,0x7B64,/* 0x40-0x47 */
+ 0x7B6D,0x7B74,0x7B69,0x7B72,0x7B65,0x7B73,0x7B71,0x7B70,/* 0x48-0x4F */
+ 0x7B61,0x7B78,0x7B76,0x7B63,0x7CB2,0x7CB4,0x7CAF,0x7D88,/* 0x50-0x57 */
+ 0x7D86,0x7D80,0x7D8D,0x7D7F,0x7D85,0x7D7A,0x7D8E,0x7D7B,/* 0x58-0x5F */
+ 0x7D83,0x7D7C,0x7D8C,0x7D94,0x7D84,0x7D7D,0x7D92,0x7F6D,/* 0x60-0x67 */
+ 0x7F6B,0x7F67,0x7F68,0x7F6C,0x7FA6,0x7FA5,0x7FA7,0x7FDB,/* 0x68-0x6F */
+ 0x7FDC,0x8021,0x8164,0x8160,0x8177,0x815C,0x8169,0x815B,/* 0x70-0x77 */
+ 0x8162,0x8172,0x6721,0x815E,0x8176,0x8167,0x816F,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x8144,0x8161,0x821D,0x8249,0x8244,0x8240,0x8242,/* 0xA0-0xA7 */
+ 0x8245,0x84F1,0x843F,0x8456,0x8476,0x8479,0x848F,0x848D,/* 0xA8-0xAF */
+ 0x8465,0x8451,0x8440,0x8486,0x8467,0x8430,0x844D,0x847D,/* 0xB0-0xB7 */
+ 0x845A,0x8459,0x8474,0x8473,0x845D,0x8507,0x845E,0x8437,/* 0xB8-0xBF */
+ 0x843A,0x8434,0x847A,0x8443,0x8478,0x8432,0x8445,0x8429,/* 0xC0-0xC7 */
+ 0x83D9,0x844B,0x842F,0x8442,0x842D,0x845F,0x8470,0x8439,/* 0xC8-0xCF */
+ 0x844E,0x844C,0x8452,0x846F,0x84C5,0x848E,0x843B,0x8447,/* 0xD0-0xD7 */
+ 0x8436,0x8433,0x8468,0x847E,0x8444,0x842B,0x8460,0x8454,/* 0xD8-0xDF */
+ 0x846E,0x8450,0x870B,0x8704,0x86F7,0x870C,0x86FA,0x86D6,/* 0xE0-0xE7 */
+ 0x86F5,0x874D,0x86F8,0x870E,0x8709,0x8701,0x86F6,0x870D,/* 0xE8-0xEF */
+ 0x8705,0x88D6,0x88CB,0x88CD,0x88CE,0x88DE,0x88DB,0x88DA,/* 0xF0-0xF7 */
+ 0x88CC,0x88D0,0x8985,0x899B,0x89DF,0x89E5,0x89E4,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E0[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x89E1,0x89E0,0x89E2,0x89DC,0x89E6,0x8A76,0x8A86,0x8A7F,/* 0x40-0x47 */
+ 0x8A61,0x8A3F,0x8A77,0x8A82,0x8A84,0x8A75,0x8A83,0x8A81,/* 0x48-0x4F */
+ 0x8A74,0x8A7A,0x8C3C,0x8C4B,0x8C4A,0x8C65,0x8C64,0x8C66,/* 0x50-0x57 */
+ 0x8C86,0x8C84,0x8C85,0x8CCC,0x8D68,0x8D69,0x8D91,0x8D8C,/* 0x58-0x5F */
+ 0x8D8E,0x8D8F,0x8D8D,0x8D93,0x8D94,0x8D90,0x8D92,0x8DF0,/* 0x60-0x67 */
+ 0x8DE0,0x8DEC,0x8DF1,0x8DEE,0x8DD0,0x8DE9,0x8DE3,0x8DE2,/* 0x68-0x6F */
+ 0x8DE7,0x8DF2,0x8DEB,0x8DF4,0x8F06,0x8EFF,0x8F01,0x8F00,/* 0x70-0x77 */
+ 0x8F05,0x8F07,0x8F08,0x8F02,0x8F0B,0x9052,0x903F,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x9044,0x9049,0x903D,0x9110,0x910D,0x910F,0x9111,/* 0xA0-0xA7 */
+ 0x9116,0x9114,0x910B,0x910E,0x916E,0x916F,0x9248,0x9252,/* 0xA8-0xAF */
+ 0x9230,0x923A,0x9266,0x9233,0x9265,0x925E,0x9283,0x922E,/* 0xB0-0xB7 */
+ 0x924A,0x9246,0x926D,0x926C,0x924F,0x9260,0x9267,0x926F,/* 0xB8-0xBF */
+ 0x9236,0x9261,0x9270,0x9231,0x9254,0x9263,0x9250,0x9272,/* 0xC0-0xC7 */
+ 0x924E,0x9253,0x924C,0x9256,0x9232,0x959F,0x959C,0x959E,/* 0xC8-0xCF */
+ 0x959B,0x9692,0x9693,0x9691,0x9697,0x96CE,0x96FA,0x96FD,/* 0xD0-0xD7 */
+ 0x96F8,0x96F5,0x9773,0x9777,0x9778,0x9772,0x980F,0x980D,/* 0xD8-0xDF */
+ 0x980E,0x98AC,0x98F6,0x98F9,0x99AF,0x99B2,0x99B0,0x99B5,/* 0xE0-0xE7 */
+ 0x9AAD,0x9AAB,0x9B5B,0x9CEA,0x9CED,0x9CE7,0x9E80,0x9EFD,/* 0xE8-0xEF */
+ 0x50E6,0x50D4,0x50D7,0x50E8,0x50F3,0x50DB,0x50EA,0x50DD,/* 0xF0-0xF7 */
+ 0x50E4,0x50D3,0x50EC,0x50F0,0x50EF,0x50E3,0x50E0,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E1[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x51D8,0x5280,0x5281,0x52E9,0x52EB,0x5330,0x53AC,0x5627,/* 0x40-0x47 */
+ 0x5615,0x560C,0x5612,0x55FC,0x560F,0x561C,0x5601,0x5613,/* 0x48-0x4F */
+ 0x5602,0x55FA,0x561D,0x5604,0x55FF,0x55F9,0x5889,0x587C,/* 0x50-0x57 */
+ 0x5890,0x5898,0x5886,0x5881,0x587F,0x5874,0x588B,0x587A,/* 0x58-0x5F */
+ 0x5887,0x5891,0x588E,0x5876,0x5882,0x5888,0x587B,0x5894,/* 0x60-0x67 */
+ 0x588F,0x58FE,0x596B,0x5ADC,0x5AEE,0x5AE5,0x5AD5,0x5AEA,/* 0x68-0x6F */
+ 0x5ADA,0x5AED,0x5AEB,0x5AF3,0x5AE2,0x5AE0,0x5ADB,0x5AEC,/* 0x70-0x77 */
+ 0x5ADE,0x5ADD,0x5AD9,0x5AE8,0x5ADF,0x5B77,0x5BE0,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x5BE3,0x5C63,0x5D82,0x5D80,0x5D7D,0x5D86,0x5D7A,/* 0xA0-0xA7 */
+ 0x5D81,0x5D77,0x5D8A,0x5D89,0x5D88,0x5D7E,0x5D7C,0x5D8D,/* 0xA8-0xAF */
+ 0x5D79,0x5D7F,0x5E58,0x5E59,0x5E53,0x5ED8,0x5ED1,0x5ED7,/* 0xB0-0xB7 */
+ 0x5ECE,0x5EDC,0x5ED5,0x5ED9,0x5ED2,0x5ED4,0x5F44,0x5F43,/* 0xB8-0xBF */
+ 0x5F6F,0x5FB6,0x612C,0x6128,0x6141,0x615E,0x6171,0x6173,/* 0xC0-0xC7 */
+ 0x6152,0x6153,0x6172,0x616C,0x6180,0x6174,0x6154,0x617A,/* 0xC8-0xCF */
+ 0x615B,0x6165,0x613B,0x616A,0x6161,0x6156,0x6229,0x6227,/* 0xD0-0xD7 */
+ 0x622B,0x642B,0x644D,0x645B,0x645D,0x6474,0x6476,0x6472,/* 0xD8-0xDF */
+ 0x6473,0x647D,0x6475,0x6466,0x64A6,0x644E,0x6482,0x645E,/* 0xE0-0xE7 */
+ 0x645C,0x644B,0x6453,0x6460,0x6450,0x647F,0x643F,0x646C,/* 0xE8-0xEF */
+ 0x646B,0x6459,0x6465,0x6477,0x6573,0x65A0,0x66A1,0x66A0,/* 0xF0-0xF7 */
+ 0x669F,0x6705,0x6704,0x6722,0x69B1,0x69B6,0x69C9,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E2[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x69A0,0x69CE,0x6996,0x69B0,0x69AC,0x69BC,0x6991,0x6999,/* 0x40-0x47 */
+ 0x698E,0x69A7,0x698D,0x69A9,0x69BE,0x69AF,0x69BF,0x69C4,/* 0x48-0x4F */
+ 0x69BD,0x69A4,0x69D4,0x69B9,0x69CA,0x699A,0x69CF,0x69B3,/* 0x50-0x57 */
+ 0x6993,0x69AA,0x69A1,0x699E,0x69D9,0x6997,0x6990,0x69C2,/* 0x58-0x5F */
+ 0x69B5,0x69A5,0x69C6,0x6B4A,0x6B4D,0x6B4B,0x6B9E,0x6B9F,/* 0x60-0x67 */
+ 0x6BA0,0x6BC3,0x6BC4,0x6BFE,0x6ECE,0x6EF5,0x6EF1,0x6F03,/* 0x68-0x6F */
+ 0x6F25,0x6EF8,0x6F37,0x6EFB,0x6F2E,0x6F09,0x6F4E,0x6F19,/* 0x70-0x77 */
+ 0x6F1A,0x6F27,0x6F18,0x6F3B,0x6F12,0x6EED,0x6F0A,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x6F36,0x6F73,0x6EF9,0x6EEE,0x6F2D,0x6F40,0x6F30,/* 0xA0-0xA7 */
+ 0x6F3C,0x6F35,0x6EEB,0x6F07,0x6F0E,0x6F43,0x6F05,0x6EFD,/* 0xA8-0xAF */
+ 0x6EF6,0x6F39,0x6F1C,0x6EFC,0x6F3A,0x6F1F,0x6F0D,0x6F1E,/* 0xB0-0xB7 */
+ 0x6F08,0x6F21,0x7187,0x7190,0x7189,0x7180,0x7185,0x7182,/* 0xB8-0xBF */
+ 0x718F,0x717B,0x7186,0x7181,0x7197,0x7244,0x7253,0x7297,/* 0xC0-0xC7 */
+ 0x7295,0x7293,0x7343,0x734D,0x7351,0x734C,0x7462,0x7473,/* 0xC8-0xCF */
+ 0x7471,0x7475,0x7472,0x7467,0x746E,0x7500,0x7502,0x7503,/* 0xD0-0xD7 */
+ 0x757D,0x7590,0x7616,0x7608,0x760C,0x7615,0x7611,0x760A,/* 0xD8-0xDF */
+ 0x7614,0x76B8,0x7781,0x777C,0x7785,0x7782,0x776E,0x7780,/* 0xE0-0xE7 */
+ 0x776F,0x777E,0x7783,0x78B2,0x78AA,0x78B4,0x78AD,0x78A8,/* 0xE8-0xEF */
+ 0x787E,0x78AB,0x789E,0x78A5,0x78A0,0x78AC,0x78A2,0x78A4,/* 0xF0-0xF7 */
+ 0x7998,0x798A,0x798B,0x7996,0x7995,0x7994,0x7993,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E3[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x7997,0x7988,0x7992,0x7990,0x7A2B,0x7A4A,0x7A30,0x7A2F,/* 0x40-0x47 */
+ 0x7A28,0x7A26,0x7AA8,0x7AAB,0x7AAC,0x7AEE,0x7B88,0x7B9C,/* 0x48-0x4F */
+ 0x7B8A,0x7B91,0x7B90,0x7B96,0x7B8D,0x7B8C,0x7B9B,0x7B8E,/* 0x50-0x57 */
+ 0x7B85,0x7B98,0x5284,0x7B99,0x7BA4,0x7B82,0x7CBB,0x7CBF,/* 0x58-0x5F */
+ 0x7CBC,0x7CBA,0x7DA7,0x7DB7,0x7DC2,0x7DA3,0x7DAA,0x7DC1,/* 0x60-0x67 */
+ 0x7DC0,0x7DC5,0x7D9D,0x7DCE,0x7DC4,0x7DC6,0x7DCB,0x7DCC,/* 0x68-0x6F */
+ 0x7DAF,0x7DB9,0x7D96,0x7DBC,0x7D9F,0x7DA6,0x7DAE,0x7DA9,/* 0x70-0x77 */
+ 0x7DA1,0x7DC9,0x7F73,0x7FE2,0x7FE3,0x7FE5,0x7FDE,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x8024,0x805D,0x805C,0x8189,0x8186,0x8183,0x8187,/* 0xA0-0xA7 */
+ 0x818D,0x818C,0x818B,0x8215,0x8497,0x84A4,0x84A1,0x849F,/* 0xA8-0xAF */
+ 0x84BA,0x84CE,0x84C2,0x84AC,0x84AE,0x84AB,0x84B9,0x84B4,/* 0xB0-0xB7 */
+ 0x84C1,0x84CD,0x84AA,0x849A,0x84B1,0x84D0,0x849D,0x84A7,/* 0xB8-0xBF */
+ 0x84BB,0x84A2,0x8494,0x84C7,0x84CC,0x849B,0x84A9,0x84AF,/* 0xC0-0xC7 */
+ 0x84A8,0x84D6,0x8498,0x84B6,0x84CF,0x84A0,0x84D7,0x84D4,/* 0xC8-0xCF */
+ 0x84D2,0x84DB,0x84B0,0x8491,0x8661,0x8733,0x8723,0x8728,/* 0xD0-0xD7 */
+ 0x876B,0x8740,0x872E,0x871E,0x8721,0x8719,0x871B,0x8743,/* 0xD8-0xDF */
+ 0x872C,0x8741,0x873E,0x8746,0x8720,0x8732,0x872A,0x872D,/* 0xE0-0xE7 */
+ 0x873C,0x8712,0x873A,0x8731,0x8735,0x8742,0x8726,0x8727,/* 0xE8-0xEF */
+ 0x8738,0x8724,0x871A,0x8730,0x8711,0x88F7,0x88E7,0x88F1,/* 0xF0-0xF7 */
+ 0x88F2,0x88FA,0x88FE,0x88EE,0x88FC,0x88F6,0x88FB,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E4[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x88F0,0x88EC,0x88EB,0x899D,0x89A1,0x899F,0x899E,0x89E9,/* 0x40-0x47 */
+ 0x89EB,0x89E8,0x8AAB,0x8A99,0x8A8B,0x8A92,0x8A8F,0x8A96,/* 0x48-0x4F */
+ 0x8C3D,0x8C68,0x8C69,0x8CD5,0x8CCF,0x8CD7,0x8D96,0x8E09,/* 0x50-0x57 */
+ 0x8E02,0x8DFF,0x8E0D,0x8DFD,0x8E0A,0x8E03,0x8E07,0x8E06,/* 0x58-0x5F */
+ 0x8E05,0x8DFE,0x8E00,0x8E04,0x8F10,0x8F11,0x8F0E,0x8F0D,/* 0x60-0x67 */
+ 0x9123,0x911C,0x9120,0x9122,0x911F,0x911D,0x911A,0x9124,/* 0x68-0x6F */
+ 0x9121,0x911B,0x917A,0x9172,0x9179,0x9173,0x92A5,0x92A4,/* 0x70-0x77 */
+ 0x9276,0x929B,0x927A,0x92A0,0x9294,0x92AA,0x928D,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x92A6,0x929A,0x92AB,0x9279,0x9297,0x927F,0x92A3,/* 0xA0-0xA7 */
+ 0x92EE,0x928E,0x9282,0x9295,0x92A2,0x927D,0x9288,0x92A1,/* 0xA8-0xAF */
+ 0x928A,0x9286,0x928C,0x9299,0x92A7,0x927E,0x9287,0x92A9,/* 0xB0-0xB7 */
+ 0x929D,0x928B,0x922D,0x969E,0x96A1,0x96FF,0x9758,0x977D,/* 0xB8-0xBF */
+ 0x977A,0x977E,0x9783,0x9780,0x9782,0x977B,0x9784,0x9781,/* 0xC0-0xC7 */
+ 0x977F,0x97CE,0x97CD,0x9816,0x98AD,0x98AE,0x9902,0x9900,/* 0xC8-0xCF */
+ 0x9907,0x999D,0x999C,0x99C3,0x99B9,0x99BB,0x99BA,0x99C2,/* 0xD0-0xD7 */
+ 0x99BD,0x99C7,0x9AB1,0x9AE3,0x9AE7,0x9B3E,0x9B3F,0x9B60,/* 0xD8-0xDF */
+ 0x9B61,0x9B5F,0x9CF1,0x9CF2,0x9CF5,0x9EA7,0x50FF,0x5103,/* 0xE0-0xE7 */
+ 0x5130,0x50F8,0x5106,0x5107,0x50F6,0x50FE,0x510B,0x510C,/* 0xE8-0xEF */
+ 0x50FD,0x510A,0x528B,0x528C,0x52F1,0x52EF,0x5648,0x5642,/* 0xF0-0xF7 */
+ 0x564C,0x5635,0x5641,0x564A,0x5649,0x5646,0x5658,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E5[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x565A,0x5640,0x5633,0x563D,0x562C,0x563E,0x5638,0x562A,/* 0x40-0x47 */
+ 0x563A,0x571A,0x58AB,0x589D,0x58B1,0x58A0,0x58A3,0x58AF,/* 0x48-0x4F */
+ 0x58AC,0x58A5,0x58A1,0x58FF,0x5AFF,0x5AF4,0x5AFD,0x5AF7,/* 0x50-0x57 */
+ 0x5AF6,0x5B03,0x5AF8,0x5B02,0x5AF9,0x5B01,0x5B07,0x5B05,/* 0x58-0x5F */
+ 0x5B0F,0x5C67,0x5D99,0x5D97,0x5D9F,0x5D92,0x5DA2,0x5D93,/* 0x60-0x67 */
+ 0x5D95,0x5DA0,0x5D9C,0x5DA1,0x5D9A,0x5D9E,0x5E69,0x5E5D,/* 0x68-0x6F */
+ 0x5E60,0x5E5C,0x7DF3,0x5EDB,0x5EDE,0x5EE1,0x5F49,0x5FB2,/* 0x70-0x77 */
+ 0x618B,0x6183,0x6179,0x61B1,0x61B0,0x61A2,0x6189,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x619B,0x6193,0x61AF,0x61AD,0x619F,0x6192,0x61AA,/* 0xA0-0xA7 */
+ 0x61A1,0x618D,0x6166,0x61B3,0x622D,0x646E,0x6470,0x6496,/* 0xA8-0xAF */
+ 0x64A0,0x6485,0x6497,0x649C,0x648F,0x648B,0x648A,0x648C,/* 0xB0-0xB7 */
+ 0x64A3,0x649F,0x6468,0x64B1,0x6498,0x6576,0x657A,0x6579,/* 0xB8-0xBF */
+ 0x657B,0x65B2,0x65B3,0x66B5,0x66B0,0x66A9,0x66B2,0x66B7,/* 0xC0-0xC7 */
+ 0x66AA,0x66AF,0x6A00,0x6A06,0x6A17,0x69E5,0x69F8,0x6A15,/* 0xC8-0xCF */
+ 0x69F1,0x69E4,0x6A20,0x69FF,0x69EC,0x69E2,0x6A1B,0x6A1D,/* 0xD0-0xD7 */
+ 0x69FE,0x6A27,0x69F2,0x69EE,0x6A14,0x69F7,0x69E7,0x6A40,/* 0xD8-0xDF */
+ 0x6A08,0x69E6,0x69FB,0x6A0D,0x69FC,0x69EB,0x6A09,0x6A04,/* 0xE0-0xE7 */
+ 0x6A18,0x6A25,0x6A0F,0x69F6,0x6A26,0x6A07,0x69F4,0x6A16,/* 0xE8-0xEF */
+ 0x6B51,0x6BA5,0x6BA3,0x6BA2,0x6BA6,0x6C01,0x6C00,0x6BFF,/* 0xF0-0xF7 */
+ 0x6C02,0x6F41,0x6F26,0x6F7E,0x6F87,0x6FC6,0x6F92,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E6[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x6F8D,0x6F89,0x6F8C,0x6F62,0x6F4F,0x6F85,0x6F5A,0x6F96,/* 0x40-0x47 */
+ 0x6F76,0x6F6C,0x6F82,0x6F55,0x6F72,0x6F52,0x6F50,0x6F57,/* 0x48-0x4F */
+ 0x6F94,0x6F93,0x6F5D,0x6F00,0x6F61,0x6F6B,0x6F7D,0x6F67,/* 0x50-0x57 */
+ 0x6F90,0x6F53,0x6F8B,0x6F69,0x6F7F,0x6F95,0x6F63,0x6F77,/* 0x58-0x5F */
+ 0x6F6A,0x6F7B,0x71B2,0x71AF,0x719B,0x71B0,0x71A0,0x719A,/* 0x60-0x67 */
+ 0x71A9,0x71B5,0x719D,0x71A5,0x719E,0x71A4,0x71A1,0x71AA,/* 0x68-0x6F */
+ 0x719C,0x71A7,0x71B3,0x7298,0x729A,0x7358,0x7352,0x735E,/* 0x70-0x77 */
+ 0x735F,0x7360,0x735D,0x735B,0x7361,0x735A,0x7359,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x7362,0x7487,0xF994,0x748A,0x7486,0x7481,0x747D,/* 0xA0-0xA7 */
+ 0x7485,0x7488,0x747C,0x7479,0x7508,0x7507,0x757E,0x7625,/* 0xA8-0xAF */
+ 0x761E,0x7619,0x761D,0x761C,0x7623,0x761A,0x7628,0x761B,/* 0xB0-0xB7 */
+ 0x769C,0x769D,0x769E,0x769B,0x778D,0x778F,0x7789,0x7788,/* 0xB8-0xBF */
+ 0x78CD,0x78BB,0x78CF,0x78CC,0x78D1,0x78CE,0x78D4,0x78C8,/* 0xC0-0xC7 */
+ 0x78C3,0x78C4,0x78C9,0x799A,0x79A1,0x79A0,0x799C,0x79A2,/* 0xC8-0xCF */
+ 0x799B,0x6B76,0x7A39,0x7AB2,0x7AB4,0x7AB3,0x7BB7,0x7BCB,/* 0xD0-0xD7 */
+ 0x7BBE,0x7BAC,0x7BCE,0x7BAF,0x7BB9,0x7BCA,0x7BB5,0x7CC5,/* 0xD8-0xDF */
+ 0x7CC8,0x7CCC,0x7CCB,0x7DF7,0x7DDB,0x7DEA,0x7DE7,0x7DD7,/* 0xE0-0xE7 */
+ 0x7DE1,0x7E03,0x7DFA,0x7DE6,0x7DF6,0x7DF1,0x7DF0,0x7DEE,/* 0xE8-0xEF */
+ 0x7DDF,0x7F76,0x7FAC,0x7FB0,0x7FAD,0x7FED,0x7FEB,0x7FEA,/* 0xF0-0xF7 */
+ 0x7FEC,0x7FE6,0x7FE8,0x8064,0x8067,0x81A3,0x819F,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E7[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x819E,0x8195,0x81A2,0x8199,0x8197,0x8216,0x824F,0x8253,/* 0x40-0x47 */
+ 0x8252,0x8250,0x824E,0x8251,0x8524,0x853B,0x850F,0x8500,/* 0x48-0x4F */
+ 0x8529,0x850E,0x8509,0x850D,0x851F,0x850A,0x8527,0x851C,/* 0x50-0x57 */
+ 0x84FB,0x852B,0x84FA,0x8508,0x850C,0x84F4,0x852A,0x84F2,/* 0x58-0x5F */
+ 0x8515,0x84F7,0x84EB,0x84F3,0xF9C2,0x8512,0x84EA,0x84E9,/* 0x60-0x67 */
+ 0x8516,0x84FE,0x8528,0x851D,0x852E,0x8502,0x84FD,0x851E,/* 0x68-0x6F */
+ 0x84F6,0x8531,0x8526,0x84E7,0x84E8,0x84F0,0x84EF,0x84F9,/* 0x70-0x77 */
+ 0x8518,0x8520,0x8530,0x850B,0x8519,0x852F,0x8662,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x8756,0x8763,0x8764,0x8777,0x87E1,0x8773,0x8758,/* 0xA0-0xA7 */
+ 0x8754,0x875B,0x8752,0x8761,0x875A,0x8751,0x875E,0x876D,/* 0xA8-0xAF */
+ 0x876A,0x8750,0x874E,0x875F,0x875D,0x876F,0x876C,0x877A,/* 0xB0-0xB7 */
+ 0x876E,0x875C,0x8765,0x874F,0x877B,0x8775,0x8762,0x8767,/* 0xB8-0xBF */
+ 0x8769,0x885A,0x8905,0x890C,0x8914,0x890B,0x8917,0x8918,/* 0xC0-0xC7 */
+ 0x8919,0x8906,0x8916,0x8911,0x890E,0x8909,0x89A2,0x89A4,/* 0xC8-0xCF */
+ 0x89A3,0x89ED,0x89F0,0x89EC,0x8ACF,0x8AC6,0x8AB8,0x8AD3,/* 0xD0-0xD7 */
+ 0x8AD1,0x8AD4,0x8AD5,0x8ABB,0x8AD7,0x8ABE,0x8AC0,0x8AC5,/* 0xD8-0xDF */
+ 0x8AD8,0x8AC3,0x8ABA,0x8ABD,0x8AD9,0x8C3E,0x8C4D,0x8C8F,/* 0xE0-0xE7 */
+ 0x8CE5,0x8CDF,0x8CD9,0x8CE8,0x8CDA,0x8CDD,0x8CE7,0x8DA0,/* 0xE8-0xEF */
+ 0x8D9C,0x8DA1,0x8D9B,0x8E20,0x8E23,0x8E25,0x8E24,0x8E2E,/* 0xF0-0xF7 */
+ 0x8E15,0x8E1B,0x8E16,0x8E11,0x8E19,0x8E26,0x8E27,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E8[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x8E14,0x8E12,0x8E18,0x8E13,0x8E1C,0x8E17,0x8E1A,0x8F2C,/* 0x40-0x47 */
+ 0x8F24,0x8F18,0x8F1A,0x8F20,0x8F23,0x8F16,0x8F17,0x9073,/* 0x48-0x4F */
+ 0x9070,0x906F,0x9067,0x906B,0x912F,0x912B,0x9129,0x912A,/* 0x50-0x57 */
+ 0x9132,0x9126,0x912E,0x9185,0x9186,0x918A,0x9181,0x9182,/* 0x58-0x5F */
+ 0x9184,0x9180,0x92D0,0x92C3,0x92C4,0x92C0,0x92D9,0x92B6,/* 0x60-0x67 */
+ 0x92CF,0x92F1,0x92DF,0x92D8,0x92E9,0x92D7,0x92DD,0x92CC,/* 0x68-0x6F */
+ 0x92EF,0x92C2,0x92E8,0x92CA,0x92C8,0x92CE,0x92E6,0x92CD,/* 0x70-0x77 */
+ 0x92D5,0x92C9,0x92E0,0x92DE,0x92E7,0x92D1,0x92D3,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x92B5,0x92E1,0x92C6,0x92B4,0x957C,0x95AC,0x95AB,/* 0xA0-0xA7 */
+ 0x95AE,0x95B0,0x96A4,0x96A2,0x96D3,0x9705,0x9708,0x9702,/* 0xA8-0xAF */
+ 0x975A,0x978A,0x978E,0x9788,0x97D0,0x97CF,0x981E,0x981D,/* 0xB0-0xB7 */
+ 0x9826,0x9829,0x9828,0x9820,0x981B,0x9827,0x98B2,0x9908,/* 0xB8-0xBF */
+ 0x98FA,0x9911,0x9914,0x9916,0x9917,0x9915,0x99DC,0x99CD,/* 0xC0-0xC7 */
+ 0x99CF,0x99D3,0x99D4,0x99CE,0x99C9,0x99D6,0x99D8,0x99CB,/* 0xC8-0xCF */
+ 0x99D7,0x99CC,0x9AB3,0x9AEC,0x9AEB,0x9AF3,0x9AF2,0x9AF1,/* 0xD0-0xD7 */
+ 0x9B46,0x9B43,0x9B67,0x9B74,0x9B71,0x9B66,0x9B76,0x9B75,/* 0xD8-0xDF */
+ 0x9B70,0x9B68,0x9B64,0x9B6C,0x9CFC,0x9CFA,0x9CFD,0x9CFF,/* 0xE0-0xE7 */
+ 0x9CF7,0x9D07,0x9D00,0x9CF9,0x9CFB,0x9D08,0x9D05,0x9D04,/* 0xE8-0xEF */
+ 0x9E83,0x9ED3,0x9F0F,0x9F10,0x511C,0x5113,0x5117,0x511A,/* 0xF0-0xF7 */
+ 0x5111,0xFA15,0x5334,0x53E1,0x5670,0x5660,0x566E,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_E9[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x5673,0x5666,0x5663,0x566D,0x5672,0x565E,0x5677,0x571C,/* 0x40-0x47 */
+ 0x571B,0x58C8,0x58BD,0x58C9,0x58BF,0x58BA,0x58C2,0x58BC,/* 0x48-0x4F */
+ 0x58C6,0x5B17,0x5B19,0x5B1B,0x5B21,0x5B14,0x5B13,0x5B10,/* 0x50-0x57 */
+ 0x5B16,0x5B28,0x5B1A,0x5B20,0x5B1E,0x5BEF,0x5DAC,0x5DB1,/* 0x58-0x5F */
+ 0x5DA9,0x5DA7,0x5DB5,0x5DB0,0x5DAE,0x5DAA,0x5DA8,0x5DB2,/* 0x60-0x67 */
+ 0x5DAD,0x5DAF,0x5DB4,0x5E67,0x5E68,0x5E66,0x5E6F,0x5EE9,/* 0x68-0x6F */
+ 0x5EE7,0x5EE6,0x5EE8,0x5EE5,0x5F4B,0x5FBC,0x619D,0x61A8,/* 0x70-0x77 */
+ 0x6196,0x61C5,0x61B4,0x61C6,0x61C1,0x61CC,0x61BA,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x61BF,0x61B8,0x618C,0x64D7,0x64D6,0x64D0,0x64CF,/* 0xA0-0xA7 */
+ 0x64C9,0x64BD,0x6489,0x64C3,0x64DB,0x64F3,0x64D9,0x6533,/* 0xA8-0xAF */
+ 0x657F,0x657C,0x65A2,0x66C8,0x66BE,0x66C0,0x66CA,0x66CB,/* 0xB0-0xB7 */
+ 0x66CF,0x66BD,0x66BB,0x66BA,0x66CC,0x6723,0x6A34,0x6A66,/* 0xB8-0xBF */
+ 0x6A49,0x6A67,0x6A32,0x6A68,0x6A3E,0x6A5D,0x6A6D,0x6A76,/* 0xC0-0xC7 */
+ 0x6A5B,0x6A51,0x6A28,0x6A5A,0x6A3B,0x6A3F,0x6A41,0x6A6A,/* 0xC8-0xCF */
+ 0x6A64,0x6A50,0x6A4F,0x6A54,0x6A6F,0x6A69,0x6A60,0x6A3C,/* 0xD0-0xD7 */
+ 0x6A5E,0x6A56,0x6A55,0x6A4D,0x6A4E,0x6A46,0x6B55,0x6B54,/* 0xD8-0xDF */
+ 0x6B56,0x6BA7,0x6BAA,0x6BAB,0x6BC8,0x6BC7,0x6C04,0x6C03,/* 0xE0-0xE7 */
+ 0x6C06,0x6FAD,0x6FCB,0x6FA3,0x6FC7,0x6FBC,0x6FCE,0x6FC8,/* 0xE8-0xEF */
+ 0x6F5E,0x6FC4,0x6FBD,0x6F9E,0x6FCA,0x6FA8,0x7004,0x6FA5,/* 0xF0-0xF7 */
+ 0x6FAE,0x6FBA,0x6FAC,0x6FAA,0x6FCF,0x6FBF,0x6FB8,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_EA[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x6FA2,0x6FC9,0x6FAB,0x6FCD,0x6FAF,0x6FB2,0x6FB0,0x71C5,/* 0x40-0x47 */
+ 0x71C2,0x71BF,0x71B8,0x71D6,0x71C0,0x71C1,0x71CB,0x71D4,/* 0x48-0x4F */
+ 0x71CA,0x71C7,0x71CF,0x71BD,0x71D8,0x71BC,0x71C6,0x71DA,/* 0x50-0x57 */
+ 0x71DB,0x729D,0x729E,0x7369,0x7366,0x7367,0x736C,0x7365,/* 0x58-0x5F */
+ 0x736B,0x736A,0x747F,0x749A,0x74A0,0x7494,0x7492,0x7495,/* 0x60-0x67 */
+ 0x74A1,0x750B,0x7580,0x762F,0x762D,0x7631,0x763D,0x7633,/* 0x68-0x6F */
+ 0x763C,0x7635,0x7632,0x7630,0x76BB,0x76E6,0x779A,0x779D,/* 0x70-0x77 */
+ 0x77A1,0x779C,0x779B,0x77A2,0x77A3,0x7795,0x7799,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x7797,0x78DD,0x78E9,0x78E5,0x78EA,0x78DE,0x78E3,/* 0xA0-0xA7 */
+ 0x78DB,0x78E1,0x78E2,0x78ED,0x78DF,0x78E0,0x79A4,0x7A44,/* 0xA8-0xAF */
+ 0x7A48,0x7A47,0x7AB6,0x7AB8,0x7AB5,0x7AB1,0x7AB7,0x7BDE,/* 0xB0-0xB7 */
+ 0x7BE3,0x7BE7,0x7BDD,0x7BD5,0x7BE5,0x7BDA,0x7BE8,0x7BF9,/* 0xB8-0xBF */
+ 0x7BD4,0x7BEA,0x7BE2,0x7BDC,0x7BEB,0x7BD8,0x7BDF,0x7CD2,/* 0xC0-0xC7 */
+ 0x7CD4,0x7CD7,0x7CD0,0x7CD1,0x7E12,0x7E21,0x7E17,0x7E0C,/* 0xC8-0xCF */
+ 0x7E1F,0x7E20,0x7E13,0x7E0E,0x7E1C,0x7E15,0x7E1A,0x7E22,/* 0xD0-0xD7 */
+ 0x7E0B,0x7E0F,0x7E16,0x7E0D,0x7E14,0x7E25,0x7E24,0x7F43,/* 0xD8-0xDF */
+ 0x7F7B,0x7F7C,0x7F7A,0x7FB1,0x7FEF,0x802A,0x8029,0x806C,/* 0xE0-0xE7 */
+ 0x81B1,0x81A6,0x81AE,0x81B9,0x81B5,0x81AB,0x81B0,0x81AC,/* 0xE8-0xEF */
+ 0x81B4,0x81B2,0x81B7,0x81A7,0x81F2,0x8255,0x8256,0x8257,/* 0xF0-0xF7 */
+ 0x8556,0x8545,0x856B,0x854D,0x8553,0x8561,0x8558,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_EB[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x8540,0x8546,0x8564,0x8541,0x8562,0x8544,0x8551,0x8547,/* 0x40-0x47 */
+ 0x8563,0x853E,0x855B,0x8571,0x854E,0x856E,0x8575,0x8555,/* 0x48-0x4F */
+ 0x8567,0x8560,0x858C,0x8566,0x855D,0x8554,0x8565,0x856C,/* 0x50-0x57 */
+ 0x8663,0x8665,0x8664,0x879B,0x878F,0x8797,0x8793,0x8792,/* 0x58-0x5F */
+ 0x8788,0x8781,0x8796,0x8798,0x8779,0x8787,0x87A3,0x8785,/* 0x60-0x67 */
+ 0x8790,0x8791,0x879D,0x8784,0x8794,0x879C,0x879A,0x8789,/* 0x68-0x6F */
+ 0x891E,0x8926,0x8930,0x892D,0x892E,0x8927,0x8931,0x8922,/* 0x70-0x77 */
+ 0x8929,0x8923,0x892F,0x892C,0x891F,0x89F1,0x8AE0,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x8AE2,0x8AF2,0x8AF4,0x8AF5,0x8ADD,0x8B14,0x8AE4,/* 0xA0-0xA7 */
+ 0x8ADF,0x8AF0,0x8AC8,0x8ADE,0x8AE1,0x8AE8,0x8AFF,0x8AEF,/* 0xA8-0xAF */
+ 0x8AFB,0x8C91,0x8C92,0x8C90,0x8CF5,0x8CEE,0x8CF1,0x8CF0,/* 0xB0-0xB7 */
+ 0x8CF3,0x8D6C,0x8D6E,0x8DA5,0x8DA7,0x8E33,0x8E3E,0x8E38,/* 0xB8-0xBF */
+ 0x8E40,0x8E45,0x8E36,0x8E3C,0x8E3D,0x8E41,0x8E30,0x8E3F,/* 0xC0-0xC7 */
+ 0x8EBD,0x8F36,0x8F2E,0x8F35,0x8F32,0x8F39,0x8F37,0x8F34,/* 0xC8-0xCF */
+ 0x9076,0x9079,0x907B,0x9086,0x90FA,0x9133,0x9135,0x9136,/* 0xD0-0xD7 */
+ 0x9193,0x9190,0x9191,0x918D,0x918F,0x9327,0x931E,0x9308,/* 0xD8-0xDF */
+ 0x931F,0x9306,0x930F,0x937A,0x9338,0x933C,0x931B,0x9323,/* 0xE0-0xE7 */
+ 0x9312,0x9301,0x9346,0x932D,0x930E,0x930D,0x92CB,0x931D,/* 0xE8-0xEF */
+ 0x92FA,0x9325,0x9313,0x92F9,0x92F7,0x9334,0x9302,0x9324,/* 0xF0-0xF7 */
+ 0x92FF,0x9329,0x9339,0x9335,0x932A,0x9314,0x930C,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_EC[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x930B,0x92FE,0x9309,0x9300,0x92FB,0x9316,0x95BC,0x95CD,/* 0x40-0x47 */
+ 0x95BE,0x95B9,0x95BA,0x95B6,0x95BF,0x95B5,0x95BD,0x96A9,/* 0x48-0x4F */
+ 0x96D4,0x970B,0x9712,0x9710,0x9799,0x9797,0x9794,0x97F0,/* 0x50-0x57 */
+ 0x97F8,0x9835,0x982F,0x9832,0x9924,0x991F,0x9927,0x9929,/* 0x58-0x5F */
+ 0x999E,0x99EE,0x99EC,0x99E5,0x99E4,0x99F0,0x99E3,0x99EA,/* 0x60-0x67 */
+ 0x99E9,0x99E7,0x9AB9,0x9ABF,0x9AB4,0x9ABB,0x9AF6,0x9AFA,/* 0x68-0x6F */
+ 0x9AF9,0x9AF7,0x9B33,0x9B80,0x9B85,0x9B87,0x9B7C,0x9B7E,/* 0x70-0x77 */
+ 0x9B7B,0x9B82,0x9B93,0x9B92,0x9B90,0x9B7A,0x9B95,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x9B7D,0x9B88,0x9D25,0x9D17,0x9D20,0x9D1E,0x9D14,/* 0xA0-0xA7 */
+ 0x9D29,0x9D1D,0x9D18,0x9D22,0x9D10,0x9D19,0x9D1F,0x9E88,/* 0xA8-0xAF */
+ 0x9E86,0x9E87,0x9EAE,0x9EAD,0x9ED5,0x9ED6,0x9EFA,0x9F12,/* 0xB0-0xB7 */
+ 0x9F3D,0x5126,0x5125,0x5122,0x5124,0x5120,0x5129,0x52F4,/* 0xB8-0xBF */
+ 0x5693,0x568C,0x568D,0x5686,0x5684,0x5683,0x567E,0x5682,/* 0xC0-0xC7 */
+ 0x567F,0x5681,0x58D6,0x58D4,0x58CF,0x58D2,0x5B2D,0x5B25,/* 0xC8-0xCF */
+ 0x5B32,0x5B23,0x5B2C,0x5B27,0x5B26,0x5B2F,0x5B2E,0x5B7B,/* 0xD0-0xD7 */
+ 0x5BF1,0x5BF2,0x5DB7,0x5E6C,0x5E6A,0x5FBE,0x5FBB,0x61C3,/* 0xD8-0xDF */
+ 0x61B5,0x61BC,0x61E7,0x61E0,0x61E5,0x61E4,0x61E8,0x61DE,/* 0xE0-0xE7 */
+ 0x64EF,0x64E9,0x64E3,0x64EB,0x64E4,0x64E8,0x6581,0x6580,/* 0xE8-0xEF */
+ 0x65B6,0x65DA,0x66D2,0x6A8D,0x6A96,0x6A81,0x6AA5,0x6A89,/* 0xF0-0xF7 */
+ 0x6A9F,0x6A9B,0x6AA1,0x6A9E,0x6A87,0x6A93,0x6A8E,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_ED[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x6A95,0x6A83,0x6AA8,0x6AA4,0x6A91,0x6A7F,0x6AA6,0x6A9A,/* 0x40-0x47 */
+ 0x6A85,0x6A8C,0x6A92,0x6B5B,0x6BAD,0x6C09,0x6FCC,0x6FA9,/* 0x48-0x4F */
+ 0x6FF4,0x6FD4,0x6FE3,0x6FDC,0x6FED,0x6FE7,0x6FE6,0x6FDE,/* 0x50-0x57 */
+ 0x6FF2,0x6FDD,0x6FE2,0x6FE8,0x71E1,0x71F1,0x71E8,0x71F2,/* 0x58-0x5F */
+ 0x71E4,0x71F0,0x71E2,0x7373,0x736E,0x736F,0x7497,0x74B2,/* 0x60-0x67 */
+ 0x74AB,0x7490,0x74AA,0x74AD,0x74B1,0x74A5,0x74AF,0x7510,/* 0x68-0x6F */
+ 0x7511,0x7512,0x750F,0x7584,0x7643,0x7648,0x7649,0x7647,/* 0x70-0x77 */
+ 0x76A4,0x76E9,0x77B5,0x77AB,0x77B2,0x77B7,0x77B6,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x77B4,0x77B1,0x77A8,0x77F0,0x78F3,0x78FD,0x7902,/* 0xA0-0xA7 */
+ 0xF964,0x78FC,0x78F2,0x7905,0x78F9,0x78FE,0x7904,0x79AB,/* 0xA8-0xAF */
+ 0x79A8,0x7A5C,0x7A5B,0x7A56,0x7A58,0x7A54,0x7A5A,0x7ABE,/* 0xB0-0xB7 */
+ 0x7AC0,0x7AC1,0x7C05,0x7C0F,0x7BF2,0x7C00,0x7BFF,0x7BFB,/* 0xB8-0xBF */
+ 0x7C0E,0x7BF4,0x7C0B,0x7BF3,0x7C02,0x7C09,0x7C03,0x7C01,/* 0xC0-0xC7 */
+ 0x7BF8,0x7BFD,0x7C06,0x7BF0,0x7BF1,0x7C10,0x7C0A,0x7CE8,/* 0xC8-0xCF */
+ 0x7E2D,0x7E3C,0x7E42,0x7E33,0x9848,0x7E38,0x7E2A,0x7E49,/* 0xD0-0xD7 */
+ 0x7E40,0x7E47,0x7E29,0x7E4C,0x7E30,0x7E3B,0x7E36,0x7E44,/* 0xD8-0xDF */
+ 0x7E3A,0x7F45,0x7F7F,0x7F7E,0x7F7D,0x7FF4,0x7FF2,0x802C,/* 0xE0-0xE7 */
+ 0x81BB,0x81C4,0x81CC,0x81CA,0x81C5,0x81C7,0x81BC,0x81E9,/* 0xE8-0xEF */
+ 0x825B,0x825A,0x825C,0x8583,0x8580,0x858F,0x85A7,0x8595,/* 0xF0-0xF7 */
+ 0x85A0,0x858B,0x85A3,0x857B,0x85A4,0x859A,0x859E,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_EE[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x8577,0x857C,0x8589,0x85A1,0x857A,0x8578,0x8557,0x858E,/* 0x40-0x47 */
+ 0x8596,0x8586,0x858D,0x8599,0x859D,0x8581,0x85A2,0x8582,/* 0x48-0x4F */
+ 0x8588,0x8585,0x8579,0x8576,0x8598,0x8590,0x859F,0x8668,/* 0x50-0x57 */
+ 0x87BE,0x87AA,0x87AD,0x87C5,0x87B0,0x87AC,0x87B9,0x87B5,/* 0x58-0x5F */
+ 0x87BC,0x87AE,0x87C9,0x87C3,0x87C2,0x87CC,0x87B7,0x87AF,/* 0x60-0x67 */
+ 0x87C4,0x87CA,0x87B4,0x87B6,0x87BF,0x87B8,0x87BD,0x87DE,/* 0x68-0x6F */
+ 0x87B2,0x8935,0x8933,0x893C,0x893E,0x8941,0x8952,0x8937,/* 0x70-0x77 */
+ 0x8942,0x89AD,0x89AF,0x89AE,0x89F2,0x89F3,0x8B1E,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x8B18,0x8B16,0x8B11,0x8B05,0x8B0B,0x8B22,0x8B0F,/* 0xA0-0xA7 */
+ 0x8B12,0x8B15,0x8B07,0x8B0D,0x8B08,0x8B06,0x8B1C,0x8B13,/* 0xA8-0xAF */
+ 0x8B1A,0x8C4F,0x8C70,0x8C72,0x8C71,0x8C6F,0x8C95,0x8C94,/* 0xB0-0xB7 */
+ 0x8CF9,0x8D6F,0x8E4E,0x8E4D,0x8E53,0x8E50,0x8E4C,0x8E47,/* 0xB8-0xBF */
+ 0x8F43,0x8F40,0x9085,0x907E,0x9138,0x919A,0x91A2,0x919B,/* 0xC0-0xC7 */
+ 0x9199,0x919F,0x91A1,0x919D,0x91A0,0x93A1,0x9383,0x93AF,/* 0xC8-0xCF */
+ 0x9364,0x9356,0x9347,0x937C,0x9358,0x935C,0x9376,0x9349,/* 0xD0-0xD7 */
+ 0x9350,0x9351,0x9360,0x936D,0x938F,0x934C,0x936A,0x9379,/* 0xD8-0xDF */
+ 0x9357,0x9355,0x9352,0x934F,0x9371,0x9377,0x937B,0x9361,/* 0xE0-0xE7 */
+ 0x935E,0x9363,0x9367,0x9380,0x934E,0x9359,0x95C7,0x95C0,/* 0xE8-0xEF */
+ 0x95C9,0x95C3,0x95C5,0x95B7,0x96AE,0x96B0,0x96AC,0x9720,/* 0xF0-0xF7 */
+ 0x971F,0x9718,0x971D,0x9719,0x979A,0x97A1,0x979C,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_EF[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x979E,0x979D,0x97D5,0x97D4,0x97F1,0x9841,0x9844,0x984A,/* 0x40-0x47 */
+ 0x9849,0x9845,0x9843,0x9925,0x992B,0x992C,0x992A,0x9933,/* 0x48-0x4F */
+ 0x9932,0x992F,0x992D,0x9931,0x9930,0x9998,0x99A3,0x99A1,/* 0x50-0x57 */
+ 0x9A02,0x99FA,0x99F4,0x99F7,0x99F9,0x99F8,0x99F6,0x99FB,/* 0x58-0x5F */
+ 0x99FD,0x99FE,0x99FC,0x9A03,0x9ABE,0x9AFE,0x9AFD,0x9B01,/* 0x60-0x67 */
+ 0x9AFC,0x9B48,0x9B9A,0x9BA8,0x9B9E,0x9B9B,0x9BA6,0x9BA1,/* 0x68-0x6F */
+ 0x9BA5,0x9BA4,0x9B86,0x9BA2,0x9BA0,0x9BAF,0x9D33,0x9D41,/* 0x70-0x77 */
+ 0x9D67,0x9D36,0x9D2E,0x9D2F,0x9D31,0x9D38,0x9D30,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x9D45,0x9D42,0x9D43,0x9D3E,0x9D37,0x9D40,0x9D3D,/* 0xA0-0xA7 */
+ 0x7FF5,0x9D2D,0x9E8A,0x9E89,0x9E8D,0x9EB0,0x9EC8,0x9EDA,/* 0xA8-0xAF */
+ 0x9EFB,0x9EFF,0x9F24,0x9F23,0x9F22,0x9F54,0x9FA0,0x5131,/* 0xB0-0xB7 */
+ 0x512D,0x512E,0x5698,0x569C,0x5697,0x569A,0x569D,0x5699,/* 0xB8-0xBF */
+ 0x5970,0x5B3C,0x5C69,0x5C6A,0x5DC0,0x5E6D,0x5E6E,0x61D8,/* 0xC0-0xC7 */
+ 0x61DF,0x61ED,0x61EE,0x61F1,0x61EA,0x61F0,0x61EB,0x61D6,/* 0xC8-0xCF */
+ 0x61E9,0x64FF,0x6504,0x64FD,0x64F8,0x6501,0x6503,0x64FC,/* 0xD0-0xD7 */
+ 0x6594,0x65DB,0x66DA,0x66DB,0x66D8,0x6AC5,0x6AB9,0x6ABD,/* 0xD8-0xDF */
+ 0x6AE1,0x6AC6,0x6ABA,0x6AB6,0x6AB7,0x6AC7,0x6AB4,0x6AAD,/* 0xE0-0xE7 */
+ 0x6B5E,0x6BC9,0x6C0B,0x7007,0x700C,0x700D,0x7001,0x7005,/* 0xE8-0xEF */
+ 0x7014,0x700E,0x6FFF,0x7000,0x6FFB,0x7026,0x6FFC,0x6FF7,/* 0xF0-0xF7 */
+ 0x700A,0x7201,0x71FF,0x71F9,0x7203,0x71FD,0x7376,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_F0[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x74B8,0x74C0,0x74B5,0x74C1,0x74BE,0x74B6,0x74BB,0x74C2,/* 0x40-0x47 */
+ 0x7514,0x7513,0x765C,0x7664,0x7659,0x7650,0x7653,0x7657,/* 0x48-0x4F */
+ 0x765A,0x76A6,0x76BD,0x76EC,0x77C2,0x77BA,0x78FF,0x790C,/* 0x50-0x57 */
+ 0x7913,0x7914,0x7909,0x7910,0x7912,0x7911,0x79AD,0x79AC,/* 0x58-0x5F */
+ 0x7A5F,0x7C1C,0x7C29,0x7C19,0x7C20,0x7C1F,0x7C2D,0x7C1D,/* 0x60-0x67 */
+ 0x7C26,0x7C28,0x7C22,0x7C25,0x7C30,0x7E5C,0x7E50,0x7E56,/* 0x68-0x6F */
+ 0x7E63,0x7E58,0x7E62,0x7E5F,0x7E51,0x7E60,0x7E57,0x7E53,/* 0x70-0x77 */
+ 0x7FB5,0x7FB3,0x7FF7,0x7FF8,0x8075,0x81D1,0x81D2,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x81D0,0x825F,0x825E,0x85B4,0x85C6,0x85C0,0x85C3,/* 0xA0-0xA7 */
+ 0x85C2,0x85B3,0x85B5,0x85BD,0x85C7,0x85C4,0x85BF,0x85CB,/* 0xA8-0xAF */
+ 0x85CE,0x85C8,0x85C5,0x85B1,0x85B6,0x85D2,0x8624,0x85B8,/* 0xB0-0xB7 */
+ 0x85B7,0x85BE,0x8669,0x87E7,0x87E6,0x87E2,0x87DB,0x87EB,/* 0xB8-0xBF */
+ 0x87EA,0x87E5,0x87DF,0x87F3,0x87E4,0x87D4,0x87DC,0x87D3,/* 0xC0-0xC7 */
+ 0x87ED,0x87D8,0x87E3,0x87A4,0x87D7,0x87D9,0x8801,0x87F4,/* 0xC8-0xCF */
+ 0x87E8,0x87DD,0x8953,0x894B,0x894F,0x894C,0x8946,0x8950,/* 0xD0-0xD7 */
+ 0x8951,0x8949,0x8B2A,0x8B27,0x8B23,0x8B33,0x8B30,0x8B35,/* 0xD8-0xDF */
+ 0x8B47,0x8B2F,0x8B3C,0x8B3E,0x8B31,0x8B25,0x8B37,0x8B26,/* 0xE0-0xE7 */
+ 0x8B36,0x8B2E,0x8B24,0x8B3B,0x8B3D,0x8B3A,0x8C42,0x8C75,/* 0xE8-0xEF */
+ 0x8C99,0x8C98,0x8C97,0x8CFE,0x8D04,0x8D02,0x8D00,0x8E5C,/* 0xF0-0xF7 */
+ 0x8E62,0x8E60,0x8E57,0x8E56,0x8E5E,0x8E65,0x8E67,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_F1[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x8E5B,0x8E5A,0x8E61,0x8E5D,0x8E69,0x8E54,0x8F46,0x8F47,/* 0x40-0x47 */
+ 0x8F48,0x8F4B,0x9128,0x913A,0x913B,0x913E,0x91A8,0x91A5,/* 0x48-0x4F */
+ 0x91A7,0x91AF,0x91AA,0x93B5,0x938C,0x9392,0x93B7,0x939B,/* 0x50-0x57 */
+ 0x939D,0x9389,0x93A7,0x938E,0x93AA,0x939E,0x93A6,0x9395,/* 0x58-0x5F */
+ 0x9388,0x9399,0x939F,0x938D,0x93B1,0x9391,0x93B2,0x93A4,/* 0x60-0x67 */
+ 0x93A8,0x93B4,0x93A3,0x93A5,0x95D2,0x95D3,0x95D1,0x96B3,/* 0x68-0x6F */
+ 0x96D7,0x96DA,0x5DC2,0x96DF,0x96D8,0x96DD,0x9723,0x9722,/* 0x70-0x77 */
+ 0x9725,0x97AC,0x97AE,0x97A8,0x97AB,0x97A4,0x97AA,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x97A2,0x97A5,0x97D7,0x97D9,0x97D6,0x97D8,0x97FA,/* 0xA0-0xA7 */
+ 0x9850,0x9851,0x9852,0x98B8,0x9941,0x993C,0x993A,0x9A0F,/* 0xA8-0xAF */
+ 0x9A0B,0x9A09,0x9A0D,0x9A04,0x9A11,0x9A0A,0x9A05,0x9A07,/* 0xB0-0xB7 */
+ 0x9A06,0x9AC0,0x9ADC,0x9B08,0x9B04,0x9B05,0x9B29,0x9B35,/* 0xB8-0xBF */
+ 0x9B4A,0x9B4C,0x9B4B,0x9BC7,0x9BC6,0x9BC3,0x9BBF,0x9BC1,/* 0xC0-0xC7 */
+ 0x9BB5,0x9BB8,0x9BD3,0x9BB6,0x9BC4,0x9BB9,0x9BBD,0x9D5C,/* 0xC8-0xCF */
+ 0x9D53,0x9D4F,0x9D4A,0x9D5B,0x9D4B,0x9D59,0x9D56,0x9D4C,/* 0xD0-0xD7 */
+ 0x9D57,0x9D52,0x9D54,0x9D5F,0x9D58,0x9D5A,0x9E8E,0x9E8C,/* 0xD8-0xDF */
+ 0x9EDF,0x9F01,0x9F00,0x9F16,0x9F25,0x9F2B,0x9F2A,0x9F29,/* 0xE0-0xE7 */
+ 0x9F28,0x9F4C,0x9F55,0x5134,0x5135,0x5296,0x52F7,0x53B4,/* 0xE8-0xEF */
+ 0x56AB,0x56AD,0x56A6,0x56A7,0x56AA,0x56AC,0x58DA,0x58DD,/* 0xF0-0xF7 */
+ 0x58DB,0x5912,0x5B3D,0x5B3E,0x5B3F,0x5DC3,0x5E70,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_F2[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x5FBF,0x61FB,0x6507,0x6510,0x650D,0x6509,0x650C,0x650E,/* 0x40-0x47 */
+ 0x6584,0x65DE,0x65DD,0x66DE,0x6AE7,0x6AE0,0x6ACC,0x6AD1,/* 0x48-0x4F */
+ 0x6AD9,0x6ACB,0x6ADF,0x6ADC,0x6AD0,0x6AEB,0x6ACF,0x6ACD,/* 0x50-0x57 */
+ 0x6ADE,0x6B60,0x6BB0,0x6C0C,0x7019,0x7027,0x7020,0x7016,/* 0x58-0x5F */
+ 0x702B,0x7021,0x7022,0x7023,0x7029,0x7017,0x7024,0x701C,/* 0x60-0x67 */
+ 0x702A,0x720C,0x720A,0x7207,0x7202,0x7205,0x72A5,0x72A6,/* 0x68-0x6F */
+ 0x72A4,0x72A3,0x72A1,0x74CB,0x74C5,0x74B7,0x74C3,0x7516,/* 0x70-0x77 */
+ 0x7660,0x77C9,0x77CA,0x77C4,0x77F1,0x791D,0x791B,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x7921,0x791C,0x7917,0x791E,0x79B0,0x7A67,0x7A68,/* 0xA0-0xA7 */
+ 0x7C33,0x7C3C,0x7C39,0x7C2C,0x7C3B,0x7CEC,0x7CEA,0x7E76,/* 0xA8-0xAF */
+ 0x7E75,0x7E78,0x7E70,0x7E77,0x7E6F,0x7E7A,0x7E72,0x7E74,/* 0xB0-0xB7 */
+ 0x7E68,0x7F4B,0x7F4A,0x7F83,0x7F86,0x7FB7,0x7FFD,0x7FFE,/* 0xB8-0xBF */
+ 0x8078,0x81D7,0x81D5,0x8264,0x8261,0x8263,0x85EB,0x85F1,/* 0xC0-0xC7 */
+ 0x85ED,0x85D9,0x85E1,0x85E8,0x85DA,0x85D7,0x85EC,0x85F2,/* 0xC8-0xCF */
+ 0x85F8,0x85D8,0x85DF,0x85E3,0x85DC,0x85D1,0x85F0,0x85E6,/* 0xD0-0xD7 */
+ 0x85EF,0x85DE,0x85E2,0x8800,0x87FA,0x8803,0x87F6,0x87F7,/* 0xD8-0xDF */
+ 0x8809,0x880C,0x880B,0x8806,0x87FC,0x8808,0x87FF,0x880A,/* 0xE0-0xE7 */
+ 0x8802,0x8962,0x895A,0x895B,0x8957,0x8961,0x895C,0x8958,/* 0xE8-0xEF */
+ 0x895D,0x8959,0x8988,0x89B7,0x89B6,0x89F6,0x8B50,0x8B48,/* 0xF0-0xF7 */
+ 0x8B4A,0x8B40,0x8B53,0x8B56,0x8B54,0x8B4B,0x8B55,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_F3[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x8B51,0x8B42,0x8B52,0x8B57,0x8C43,0x8C77,0x8C76,0x8C9A,/* 0x40-0x47 */
+ 0x8D06,0x8D07,0x8D09,0x8DAC,0x8DAA,0x8DAD,0x8DAB,0x8E6D,/* 0x48-0x4F */
+ 0x8E78,0x8E73,0x8E6A,0x8E6F,0x8E7B,0x8EC2,0x8F52,0x8F51,/* 0x50-0x57 */
+ 0x8F4F,0x8F50,0x8F53,0x8FB4,0x9140,0x913F,0x91B0,0x91AD,/* 0x58-0x5F */
+ 0x93DE,0x93C7,0x93CF,0x93C2,0x93DA,0x93D0,0x93F9,0x93EC,/* 0x60-0x67 */
+ 0x93CC,0x93D9,0x93A9,0x93E6,0x93CA,0x93D4,0x93EE,0x93E3,/* 0x68-0x6F */
+ 0x93D5,0x93C4,0x93CE,0x93C0,0x93D2,0x93E7,0x957D,0x95DA,/* 0x70-0x77 */
+ 0x95DB,0x96E1,0x9729,0x972B,0x972C,0x9728,0x9726,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x97B3,0x97B7,0x97B6,0x97DD,0x97DE,0x97DF,0x985C,/* 0xA0-0xA7 */
+ 0x9859,0x985D,0x9857,0x98BF,0x98BD,0x98BB,0x98BE,0x9948,/* 0xA8-0xAF */
+ 0x9947,0x9943,0x99A6,0x99A7,0x9A1A,0x9A15,0x9A25,0x9A1D,/* 0xB0-0xB7 */
+ 0x9A24,0x9A1B,0x9A22,0x9A20,0x9A27,0x9A23,0x9A1E,0x9A1C,/* 0xB8-0xBF */
+ 0x9A14,0x9AC2,0x9B0B,0x9B0A,0x9B0E,0x9B0C,0x9B37,0x9BEA,/* 0xC0-0xC7 */
+ 0x9BEB,0x9BE0,0x9BDE,0x9BE4,0x9BE6,0x9BE2,0x9BF0,0x9BD4,/* 0xC8-0xCF */
+ 0x9BD7,0x9BEC,0x9BDC,0x9BD9,0x9BE5,0x9BD5,0x9BE1,0x9BDA,/* 0xD0-0xD7 */
+ 0x9D77,0x9D81,0x9D8A,0x9D84,0x9D88,0x9D71,0x9D80,0x9D78,/* 0xD8-0xDF */
+ 0x9D86,0x9D8B,0x9D8C,0x9D7D,0x9D6B,0x9D74,0x9D75,0x9D70,/* 0xE0-0xE7 */
+ 0x9D69,0x9D85,0x9D73,0x9D7B,0x9D82,0x9D6F,0x9D79,0x9D7F,/* 0xE8-0xEF */
+ 0x9D87,0x9D68,0x9E94,0x9E91,0x9EC0,0x9EFC,0x9F2D,0x9F40,/* 0xF0-0xF7 */
+ 0x9F41,0x9F4D,0x9F56,0x9F57,0x9F58,0x5337,0x56B2,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_F4[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x56B5,0x56B3,0x58E3,0x5B45,0x5DC6,0x5DC7,0x5EEE,0x5EEF,/* 0x40-0x47 */
+ 0x5FC0,0x5FC1,0x61F9,0x6517,0x6516,0x6515,0x6513,0x65DF,/* 0x48-0x4F */
+ 0x66E8,0x66E3,0x66E4,0x6AF3,0x6AF0,0x6AEA,0x6AE8,0x6AF9,/* 0x50-0x57 */
+ 0x6AF1,0x6AEE,0x6AEF,0x703C,0x7035,0x702F,0x7037,0x7034,/* 0x58-0x5F */
+ 0x7031,0x7042,0x7038,0x703F,0x703A,0x7039,0x7040,0x703B,/* 0x60-0x67 */
+ 0x7033,0x7041,0x7213,0x7214,0x72A8,0x737D,0x737C,0x74BA,/* 0x68-0x6F */
+ 0x76AB,0x76AA,0x76BE,0x76ED,0x77CC,0x77CE,0x77CF,0x77CD,/* 0x70-0x77 */
+ 0x77F2,0x7925,0x7923,0x7927,0x7928,0x7924,0x7929,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x79B2,0x7A6E,0x7A6C,0x7A6D,0x7AF7,0x7C49,0x7C48,/* 0xA0-0xA7 */
+ 0x7C4A,0x7C47,0x7C45,0x7CEE,0x7E7B,0x7E7E,0x7E81,0x7E80,/* 0xA8-0xAF */
+ 0x7FBA,0x7FFF,0x8079,0x81DB,0x81D9,0x820B,0x8268,0x8269,/* 0xB0-0xB7 */
+ 0x8622,0x85FF,0x8601,0x85FE,0x861B,0x8600,0x85F6,0x8604,/* 0xB8-0xBF */
+ 0x8609,0x8605,0x860C,0x85FD,0x8819,0x8810,0x8811,0x8817,/* 0xC0-0xC7 */
+ 0x8813,0x8816,0x8963,0x8966,0x89B9,0x89F7,0x8B60,0x8B6A,/* 0xC8-0xCF */
+ 0x8B5D,0x8B68,0x8B63,0x8B65,0x8B67,0x8B6D,0x8DAE,0x8E86,/* 0xD0-0xD7 */
+ 0x8E88,0x8E84,0x8F59,0x8F56,0x8F57,0x8F55,0x8F58,0x8F5A,/* 0xD8-0xDF */
+ 0x908D,0x9143,0x9141,0x91B7,0x91B5,0x91B2,0x91B3,0x940B,/* 0xE0-0xE7 */
+ 0x9413,0x93FB,0x9420,0x940F,0x9414,0x93FE,0x9415,0x9410,/* 0xE8-0xEF */
+ 0x9428,0x9419,0x940D,0x93F5,0x9400,0x93F7,0x9407,0x940E,/* 0xF0-0xF7 */
+ 0x9416,0x9412,0x93FA,0x9409,0x93F8,0x940A,0x93FF,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_F5[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x93FC,0x940C,0x93F6,0x9411,0x9406,0x95DE,0x95E0,0x95DF,/* 0x40-0x47 */
+ 0x972E,0x972F,0x97B9,0x97BB,0x97FD,0x97FE,0x9860,0x9862,/* 0x48-0x4F */
+ 0x9863,0x985F,0x98C1,0x98C2,0x9950,0x994E,0x9959,0x994C,/* 0x50-0x57 */
+ 0x994B,0x9953,0x9A32,0x9A34,0x9A31,0x9A2C,0x9A2A,0x9A36,/* 0x58-0x5F */
+ 0x9A29,0x9A2E,0x9A38,0x9A2D,0x9AC7,0x9ACA,0x9AC6,0x9B10,/* 0x60-0x67 */
+ 0x9B12,0x9B11,0x9C0B,0x9C08,0x9BF7,0x9C05,0x9C12,0x9BF8,/* 0x68-0x6F */
+ 0x9C40,0x9C07,0x9C0E,0x9C06,0x9C17,0x9C14,0x9C09,0x9D9F,/* 0x70-0x77 */
+ 0x9D99,0x9DA4,0x9D9D,0x9D92,0x9D98,0x9D90,0x9D9B,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x9DA0,0x9D94,0x9D9C,0x9DAA,0x9D97,0x9DA1,0x9D9A,/* 0xA0-0xA7 */
+ 0x9DA2,0x9DA8,0x9D9E,0x9DA3,0x9DBF,0x9DA9,0x9D96,0x9DA6,/* 0xA8-0xAF */
+ 0x9DA7,0x9E99,0x9E9B,0x9E9A,0x9EE5,0x9EE4,0x9EE7,0x9EE6,/* 0xB0-0xB7 */
+ 0x9F30,0x9F2E,0x9F5B,0x9F60,0x9F5E,0x9F5D,0x9F59,0x9F91,/* 0xB8-0xBF */
+ 0x513A,0x5139,0x5298,0x5297,0x56C3,0x56BD,0x56BE,0x5B48,/* 0xC0-0xC7 */
+ 0x5B47,0x5DCB,0x5DCF,0x5EF1,0x61FD,0x651B,0x6B02,0x6AFC,/* 0xC8-0xCF */
+ 0x6B03,0x6AF8,0x6B00,0x7043,0x7044,0x704A,0x7048,0x7049,/* 0xD0-0xD7 */
+ 0x7045,0x7046,0x721D,0x721A,0x7219,0x737E,0x7517,0x766A,/* 0xD8-0xDF */
+ 0x77D0,0x792D,0x7931,0x792F,0x7C54,0x7C53,0x7CF2,0x7E8A,/* 0xE0-0xE7 */
+ 0x7E87,0x7E88,0x7E8B,0x7E86,0x7E8D,0x7F4D,0x7FBB,0x8030,/* 0xE8-0xEF */
+ 0x81DD,0x8618,0x862A,0x8626,0x861F,0x8623,0x861C,0x8619,/* 0xF0-0xF7 */
+ 0x8627,0x862E,0x8621,0x8620,0x8629,0x861E,0x8625,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_F6[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x8829,0x881D,0x881B,0x8820,0x8824,0x881C,0x882B,0x884A,/* 0x40-0x47 */
+ 0x896D,0x8969,0x896E,0x896B,0x89FA,0x8B79,0x8B78,0x8B45,/* 0x48-0x4F */
+ 0x8B7A,0x8B7B,0x8D10,0x8D14,0x8DAF,0x8E8E,0x8E8C,0x8F5E,/* 0x50-0x57 */
+ 0x8F5B,0x8F5D,0x9146,0x9144,0x9145,0x91B9,0x943F,0x943B,/* 0x58-0x5F */
+ 0x9436,0x9429,0x943D,0x943C,0x9430,0x9439,0x942A,0x9437,/* 0x60-0x67 */
+ 0x942C,0x9440,0x9431,0x95E5,0x95E4,0x95E3,0x9735,0x973A,/* 0x68-0x6F */
+ 0x97BF,0x97E1,0x9864,0x98C9,0x98C6,0x98C0,0x9958,0x9956,/* 0x70-0x77 */
+ 0x9A39,0x9A3D,0x9A46,0x9A44,0x9A42,0x9A41,0x9A3A,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x9A3F,0x9ACD,0x9B15,0x9B17,0x9B18,0x9B16,0x9B3A,/* 0xA0-0xA7 */
+ 0x9B52,0x9C2B,0x9C1D,0x9C1C,0x9C2C,0x9C23,0x9C28,0x9C29,/* 0xA8-0xAF */
+ 0x9C24,0x9C21,0x9DB7,0x9DB6,0x9DBC,0x9DC1,0x9DC7,0x9DCA,/* 0xB0-0xB7 */
+ 0x9DCF,0x9DBE,0x9DC5,0x9DC3,0x9DBB,0x9DB5,0x9DCE,0x9DB9,/* 0xB8-0xBF */
+ 0x9DBA,0x9DAC,0x9DC8,0x9DB1,0x9DAD,0x9DCC,0x9DB3,0x9DCD,/* 0xC0-0xC7 */
+ 0x9DB2,0x9E7A,0x9E9C,0x9EEB,0x9EEE,0x9EED,0x9F1B,0x9F18,/* 0xC8-0xCF */
+ 0x9F1A,0x9F31,0x9F4E,0x9F65,0x9F64,0x9F92,0x4EB9,0x56C6,/* 0xD0-0xD7 */
+ 0x56C5,0x56CB,0x5971,0x5B4B,0x5B4C,0x5DD5,0x5DD1,0x5EF2,/* 0xD8-0xDF */
+ 0x6521,0x6520,0x6526,0x6522,0x6B0B,0x6B08,0x6B09,0x6C0D,/* 0xE0-0xE7 */
+ 0x7055,0x7056,0x7057,0x7052,0x721E,0x721F,0x72A9,0x737F,/* 0xE8-0xEF */
+ 0x74D8,0x74D5,0x74D9,0x74D7,0x766D,0x76AD,0x7935,0x79B4,/* 0xF0-0xF7 */
+ 0x7A70,0x7A71,0x7C57,0x7C5C,0x7C59,0x7C5B,0x7C5A,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_F7[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x7CF4,0x7CF1,0x7E91,0x7F4F,0x7F87,0x81DE,0x826B,0x8634,/* 0x40-0x47 */
+ 0x8635,0x8633,0x862C,0x8632,0x8636,0x882C,0x8828,0x8826,/* 0x48-0x4F */
+ 0x882A,0x8825,0x8971,0x89BF,0x89BE,0x89FB,0x8B7E,0x8B84,/* 0x50-0x57 */
+ 0x8B82,0x8B86,0x8B85,0x8B7F,0x8D15,0x8E95,0x8E94,0x8E9A,/* 0x58-0x5F */
+ 0x8E92,0x8E90,0x8E96,0x8E97,0x8F60,0xF98D,0x9147,0x944C,/* 0x60-0x67 */
+ 0x9450,0x944A,0x944B,0x944F,0x9447,0x9445,0x9448,0x9449,/* 0x68-0x6F */
+ 0x9446,0x973F,0x97E3,0x986A,0x9869,0x98CB,0x9954,0x995B,/* 0x70-0x77 */
+ 0x9A4E,0x9A53,0x9A54,0x9A4C,0x9A4F,0x9A48,0x9A4A,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x9A49,0x9A52,0x9A50,0x9AD0,0x9B19,0x9B2B,0x9B3B,/* 0xA0-0xA7 */
+ 0x9B56,0x9B55,0x9C46,0x9C48,0x9C3F,0x9C44,0x9C39,0x9C33,/* 0xA8-0xAF */
+ 0x9C41,0x9C3C,0x9C37,0x9C34,0x9C32,0x9C3D,0x9C36,0x9DDB,/* 0xB0-0xB7 */
+ 0x9DD2,0x9DDE,0x9DDA,0x9DCB,0x9DD0,0x9DDC,0x9DD1,0x9DDF,/* 0xB8-0xBF */
+ 0x9DE9,0x9DD9,0x9DD8,0x9DD6,0x9DF5,0x9DD5,0x9DDD,0x9EB6,/* 0xC0-0xC7 */
+ 0x9EF0,0x9F35,0x9F33,0x9F32,0x9F42,0x9F6B,0x9F95,0x9FA2,/* 0xC8-0xCF */
+ 0x513D,0x5299,0x58E8,0x58E7,0x5972,0x5B4D,0x5DD8,0x882F,/* 0xD0-0xD7 */
+ 0x5F4F,0x6201,0x6203,0x6204,0x6529,0x6525,0x6596,0x66EB,/* 0xD8-0xDF */
+ 0x6B11,0x6B12,0x6B0F,0x6BCA,0x705B,0x705A,0x7222,0x7382,/* 0xE0-0xE7 */
+ 0x7381,0x7383,0x7670,0x77D4,0x7C67,0x7C66,0x7E95,0x826C,/* 0xE8-0xEF */
+ 0x863A,0x8640,0x8639,0x863C,0x8631,0x863B,0x863E,0x8830,/* 0xF0-0xF7 */
+ 0x8832,0x882E,0x8833,0x8976,0x8974,0x8973,0x89FE,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_F8[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x8B8C,0x8B8E,0x8B8B,0x8B88,0x8C45,0x8D19,0x8E98,0x8F64,/* 0x40-0x47 */
+ 0x8F63,0x91BC,0x9462,0x9455,0x945D,0x9457,0x945E,0x97C4,/* 0x48-0x4F */
+ 0x97C5,0x9800,0x9A56,0x9A59,0x9B1E,0x9B1F,0x9B20,0x9C52,/* 0x50-0x57 */
+ 0x9C58,0x9C50,0x9C4A,0x9C4D,0x9C4B,0x9C55,0x9C59,0x9C4C,/* 0x58-0x5F */
+ 0x9C4E,0x9DFB,0x9DF7,0x9DEF,0x9DE3,0x9DEB,0x9DF8,0x9DE4,/* 0x60-0x67 */
+ 0x9DF6,0x9DE1,0x9DEE,0x9DE6,0x9DF2,0x9DF0,0x9DE2,0x9DEC,/* 0x68-0x6F */
+ 0x9DF4,0x9DF3,0x9DE8,0x9DED,0x9EC2,0x9ED0,0x9EF2,0x9EF3,/* 0x70-0x77 */
+ 0x9F06,0x9F1C,0x9F38,0x9F37,0x9F36,0x9F43,0x9F4F,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x9F71,0x9F70,0x9F6E,0x9F6F,0x56D3,0x56CD,0x5B4E,/* 0xA0-0xA7 */
+ 0x5C6D,0x652D,0x66ED,0x66EE,0x6B13,0x705F,0x7061,0x705D,/* 0xA8-0xAF */
+ 0x7060,0x7223,0x74DB,0x74E5,0x77D5,0x7938,0x79B7,0x79B6,/* 0xB0-0xB7 */
+ 0x7C6A,0x7E97,0x7F89,0x826D,0x8643,0x8838,0x8837,0x8835,/* 0xB8-0xBF */
+ 0x884B,0x8B94,0x8B95,0x8E9E,0x8E9F,0x8EA0,0x8E9D,0x91BE,/* 0xC0-0xC7 */
+ 0x91BD,0x91C2,0x946B,0x9468,0x9469,0x96E5,0x9746,0x9743,/* 0xC8-0xCF */
+ 0x9747,0x97C7,0x97E5,0x9A5E,0x9AD5,0x9B59,0x9C63,0x9C67,/* 0xD0-0xD7 */
+ 0x9C66,0x9C62,0x9C5E,0x9C60,0x9E02,0x9DFE,0x9E07,0x9E03,/* 0xD8-0xDF */
+ 0x9E06,0x9E05,0x9E00,0x9E01,0x9E09,0x9DFF,0x9DFD,0x9E04,/* 0xE0-0xE7 */
+ 0x9EA0,0x9F1E,0x9F46,0x9F74,0x9F75,0x9F76,0x56D4,0x652E,/* 0xE8-0xEF */
+ 0x65B8,0x6B18,0x6B19,0x6B17,0x6B1A,0x7062,0x7226,0x72AA,/* 0xF0-0xF7 */
+ 0x77D8,0x77D9,0x7939,0x7C69,0x7C6B,0x7CF6,0x7E9A,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t c2u_F9[256] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x00-0x07 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x08-0x0F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x10-0x17 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x18-0x1F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x20-0x27 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x28-0x2F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x30-0x37 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x38-0x3F */
+ 0x7E98,0x7E9B,0x7E99,0x81E0,0x81E1,0x8646,0x8647,0x8648,/* 0x40-0x47 */
+ 0x8979,0x897A,0x897C,0x897B,0x89FF,0x8B98,0x8B99,0x8EA5,/* 0x48-0x4F */
+ 0x8EA4,0x8EA3,0x946E,0x946D,0x946F,0x9471,0x9473,0x9749,/* 0x50-0x57 */
+ 0x9872,0x995F,0x9C68,0x9C6E,0x9C6D,0x9E0B,0x9E0D,0x9E10,/* 0x58-0x5F */
+ 0x9E0F,0x9E12,0x9E11,0x9EA1,0x9EF5,0x9F09,0x9F47,0x9F78,/* 0x60-0x67 */
+ 0x9F7B,0x9F7A,0x9F79,0x571E,0x7066,0x7C6F,0x883C,0x8DB2,/* 0x68-0x6F */
+ 0x8EA6,0x91C3,0x9474,0x9478,0x9476,0x9475,0x9A60,0x9C74,/* 0x70-0x77 */
+ 0x9C73,0x9C71,0x9C75,0x9E14,0x9E13,0x9EF6,0x9F0A,0x0000,/* 0x78-0x7F */
+
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x80-0x87 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x88-0x8F */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x90-0x97 */
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,/* 0x98-0x9F */
+ 0x0000,0x9FA4,0x7068,0x7065,0x7CF7,0x866A,0x883E,0x883D,/* 0xA0-0xA7 */
+ 0x883F,0x8B9E,0x8C9C,0x8EA9,0x8EC9,0x974B,0x9873,0x9874,/* 0xA8-0xAF */
+ 0x98CC,0x9961,0x99AB,0x9A64,0x9A66,0x9A67,0x9B24,0x9E15,/* 0xB0-0xB7 */
+ 0x9E17,0x9F48,0x6207,0x6B1E,0x7227,0x864C,0x8EA8,0x9482,/* 0xB8-0xBF */
+ 0x9480,0x9481,0x9A69,0x9A68,0x9B2E,0x9E19,0x7229,0x864B,/* 0xC0-0xC7 */
+ 0x8B9F,0x9483,0x9C79,0x9EB7,0x7675,0x9A6B,0x9C7A,0x9E1D,/* 0xC8-0xCF */
+ 0x7069,0x706A,0x9EA4,0x9F7E,0x9F49,0x9F98,0x7881,0x92B9,/* 0xD0-0xD7 */
+ 0x88CF,0x58BB,0x6052,0x7CA7,0x5AFA,0x2554,0x2566,0x2557,/* 0xD8-0xDF */
+ 0x2560,0x256C,0x2563,0x255A,0x2569,0x255D,0x2552,0x2564,/* 0xE0-0xE7 */
+ 0x2555,0x255E,0x256A,0x2561,0x2558,0x2567,0x255B,0x2553,/* 0xE8-0xEF */
+ 0x2565,0x2556,0x255F,0x256B,0x2562,0x2559,0x2568,0x255C,/* 0xF0-0xF7 */
+ 0x2551,0x2550,0x0000,0x0000,0x0000,0x0000,0x2593,0x0000,/* 0xF8-0xFF */
+};
+
+static wchar_t *page_charset2uni[256] = {
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, c2u_A1, c2u_A2, c2u_A3, c2u_A4, c2u_A5, c2u_A6, c2u_A7,
+ c2u_A8, c2u_A9, c2u_AA, c2u_AB, c2u_AC, c2u_AD, c2u_AE, c2u_AF,
+ c2u_B0, c2u_B1, c2u_B2, c2u_B3, c2u_B4, c2u_B5, c2u_B6, c2u_B7,
+ c2u_B8, c2u_B9, c2u_BA, c2u_BB, c2u_BC, c2u_BD, c2u_BE, c2u_BF,
+ c2u_C0, c2u_C1, c2u_C2, c2u_C3, c2u_C4, c2u_C5, c2u_C6, NULL,
+ NULL, c2u_C9, c2u_CA, c2u_CB, c2u_CC, c2u_CD, c2u_CE, c2u_CF,
+ c2u_D0, c2u_D1, c2u_D2, c2u_D3, c2u_D4, c2u_D5, c2u_D6, c2u_D7,
+ c2u_D8, c2u_D9, c2u_DA, c2u_DB, c2u_DC, c2u_DD, c2u_DE, c2u_DF,
+ c2u_E0, c2u_E1, c2u_E2, c2u_E3, c2u_E4, c2u_E5, c2u_E6, c2u_E7,
+ c2u_E8, c2u_E9, c2u_EA, c2u_EB, c2u_EC, c2u_ED, c2u_EE, c2u_EF,
+ c2u_F0, c2u_F1, c2u_F2, c2u_F3, c2u_F4, c2u_F5, c2u_F6, c2u_F7,
+ c2u_F8, c2u_F9, NULL, NULL, NULL, NULL, NULL, NULL,
+};
+
+static unsigned char u2c_02[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA3, 0xBE, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xA3, 0xBC, 0xA3, 0xBD, 0xA3, 0xBF, /* 0xC8-0xCB */
+ 0x00, 0x00, 0xA1, 0xC5, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0xA3, 0xBB, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+};
+
+static unsigned char u2c_03[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0xA1, 0xC2, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xA3, 0x44, 0xA3, 0x45, 0xA3, 0x46, /* 0x90-0x93 */
+ 0xA3, 0x47, 0xA3, 0x48, 0xA3, 0x49, 0xA3, 0x4A, /* 0x94-0x97 */
+ 0xA3, 0x4B, 0xA3, 0x4C, 0xA3, 0x4D, 0xA3, 0x4E, /* 0x98-0x9B */
+ 0xA3, 0x4F, 0xA3, 0x50, 0xA3, 0x51, 0xA3, 0x52, /* 0x9C-0x9F */
+ 0xA3, 0x53, 0xA3, 0x54, 0x00, 0x00, 0xA3, 0x55, /* 0xA0-0xA3 */
+ 0xA3, 0x56, 0xA3, 0x57, 0xA3, 0x58, 0xA3, 0x59, /* 0xA4-0xA7 */
+ 0xA3, 0x5A, 0xA3, 0x5B, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0xA3, 0x5C, 0xA3, 0x5D, 0xA3, 0x5E, /* 0xB0-0xB3 */
+ 0xA3, 0x5F, 0xA3, 0x60, 0xA3, 0x61, 0xA3, 0x62, /* 0xB4-0xB7 */
+ 0xA3, 0x63, 0xA3, 0x64, 0xA3, 0x65, 0xA3, 0x66, /* 0xB8-0xBB */
+ 0xA3, 0x67, 0xA3, 0x68, 0xA3, 0x69, 0xA3, 0x6A, /* 0xBC-0xBF */
+ 0xA3, 0x6B, 0xA3, 0x6C, 0x00, 0x00, 0xA3, 0x6D, /* 0xC0-0xC3 */
+ 0xA3, 0x6E, 0xA3, 0x6F, 0xA3, 0x70, 0xA3, 0x71, /* 0xC4-0xC7 */
+ 0xA3, 0x72, 0xA3, 0x73, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+};
+
+static unsigned char u2c_20[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA1, 0x56, /* 0x10-0x13 */
+ 0xA1, 0x58, 0xA2, 0x77, 0xA1, 0xFC, 0x00, 0x00, /* 0x14-0x17 */
+ 0xA1, 0xA5, 0xA1, 0xA6, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0xA1, 0xA7, 0xA1, 0xA8, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0xA1, 0x45, 0x00, 0x00, /* 0x20-0x23 */
+ 0xA3, 0xBB, 0xA1, 0x4C, 0xA1, 0x4B, 0xA1, 0x45, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0xA1, 0xAC, 0xA1, 0xB2, /* 0x30-0x33 */
+ 0x00, 0x00, 0xA1, 0xAB, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA1, 0xB0, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0xA1, 0xC3, 0x00, 0x00, /* 0x3C-0x3F */
+};
+
+static unsigned char u2c_21[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA2, 0x4A, /* 0x00-0x03 */
+ 0x00, 0x00, 0xA1, 0xC1, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0xA2, 0x4B, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xA2, 0xB9, 0xA2, 0xBA, 0xA2, 0xBB, 0xA2, 0xBC, /* 0x60-0x63 */
+ 0xA2, 0xBD, 0xA2, 0xBE, 0xA2, 0xBF, 0xA2, 0xC0, /* 0x64-0x67 */
+ 0xA2, 0xC1, 0xA2, 0xC2, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0xA1, 0xF6, 0xA1, 0xF4, 0xA1, 0xF7, 0xA1, 0xF5, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0xA1, 0xF8, 0xA1, 0xF9, /* 0x94-0x97 */
+ 0xA1, 0xFB, 0xA1, 0xFA, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+};
+
+static unsigned char u2c_22[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0xA2, 0x41, 0xA2, 0x42, 0x00, 0x00, /* 0x14-0x17 */
+ 0xA2, 0x58, 0x00, 0x00, 0xA1, 0xD4, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0xA1, 0xDB, 0xA1, 0xE8, /* 0x1C-0x1F */
+ 0xA1, 0xE7, 0x00, 0x00, 0x00, 0x00, 0xA1, 0xFD, /* 0x20-0x23 */
+ 0x00, 0x00, 0xA1, 0xFC, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0xA1, 0xE4, 0xA1, 0xE5, 0xA1, 0xEC, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0xA1, 0xED, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0xA1, 0xEF, 0xA1, 0xEE, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0xA1, 0xDC, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xA1, 0xDA, 0xA1, 0xDD, 0x00, 0x00, 0xA1, 0xDD, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0xA1, 0xD8, 0xA1, 0xD9, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0xA1, 0xF2, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0xA1, 0xF3, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0xA1, 0xE6, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA1, 0xE9, /* 0xBC-0xBF */
+};
+
+static unsigned char u2c_23[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA1, 0x5B, /* 0x04-0x07 */
+};
+
+static unsigned char u2c_25[512] = {
+ 0xA2, 0x77, 0x00, 0x00, 0xA2, 0x78, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0xA2, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xA2, 0x7B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0xA2, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0xA2, 0x7D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0xA2, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0xA2, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0xA2, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0xA2, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0xA2, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xF9, 0xF9, 0xF9, 0xF8, 0xF9, 0xE6, 0xF9, 0xEF, /* 0x50-0x53 */
+ 0xF9, 0xDD, 0xF9, 0xE8, 0xF9, 0xF1, 0xF9, 0xDF, /* 0x54-0x57 */
+ 0xF9, 0xEC, 0xF9, 0xF5, 0xF9, 0xE3, 0xF9, 0xEE, /* 0x58-0x5B */
+ 0xF9, 0xF7, 0xF9, 0xE5, 0xF9, 0xE9, 0xF9, 0xF2, /* 0x5C-0x5F */
+ 0xF9, 0xE0, 0xF9, 0xEB, 0xF9, 0xF4, 0xF9, 0xE2, /* 0x60-0x63 */
+ 0xF9, 0xE7, 0xF9, 0xF0, 0xF9, 0xDE, 0xF9, 0xED, /* 0x64-0x67 */
+ 0xF9, 0xF6, 0xF9, 0xE4, 0xF9, 0xEA, 0xF9, 0xF3, /* 0x68-0x6B */
+ 0xF9, 0xE1, 0xA2, 0x7E, 0xA2, 0xA1, 0xA2, 0xA3, /* 0x6C-0x6F */
+ 0xA2, 0xA2, 0xA2, 0xAC, 0xA2, 0xAD, 0xA2, 0xAE, /* 0x70-0x73 */
+ 0xA1, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0xA2, 0x62, 0xA2, 0x63, 0xA2, 0x64, /* 0x80-0x83 */
+ 0xA2, 0x65, 0xA2, 0x66, 0xA2, 0x67, 0xA2, 0x68, /* 0x84-0x87 */
+ 0xA2, 0x69, 0xA2, 0x70, 0xA2, 0x6F, 0xA2, 0x6E, /* 0x88-0x8B */
+ 0xA2, 0x6D, 0xA2, 0x6C, 0xA2, 0x6B, 0xA2, 0x6A, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xFE, /* 0x90-0x93 */
+ 0xA2, 0x76, 0xA2, 0x79, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0xA1, 0xBD, 0xA1, 0xBC, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0xA1, 0xB6, 0xA1, 0xB5, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0xA1, 0xBF, 0xA1, 0xBE, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0xA1, 0xBB, 0xA1, 0xBA, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA1, 0xB3, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0xA1, 0xB7, 0xA1, 0xB4, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0xA2, 0xA8, 0xA2, 0xA9, /* 0xE0-0xE3 */
+ 0xA2, 0xAB, 0xA2, 0xAA, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+};
+
+static unsigned char u2c_26[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0xA1, 0xB9, 0xA1, 0xB8, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0xA1, 0xF3, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0xA1, 0xF0, 0xA1, 0xF2, 0xA1, 0xF1, 0x00, 0x00, /* 0x40-0x43 */
+};
+
+static unsigned char u2c_30[512] = {
+ 0xA1, 0x40, 0xA1, 0x42, 0xA1, 0x43, 0xA1, 0xB2, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0xA1, 0x71, 0xA1, 0x72, 0xA1, 0x6D, 0xA1, 0x6E, /* 0x08-0x0B */
+ 0xA1, 0x75, 0xA1, 0x76, 0xA1, 0x79, 0xA1, 0x7A, /* 0x0C-0x0F */
+ 0xA1, 0x69, 0xA1, 0x6A, 0xA2, 0x45, 0x00, 0x00, /* 0x10-0x13 */
+ 0xA1, 0x65, 0xA1, 0x66, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0xA1, 0xE3, 0xA1, 0xA9, 0xA1, 0xAA, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0xA2, 0xC3, 0xA2, 0xC4, 0xA2, 0xC5, /* 0x20-0x23 */
+ 0xA2, 0xC6, 0xA2, 0xC7, 0xA2, 0xC8, 0xA2, 0xC9, /* 0x24-0x27 */
+ 0xA2, 0xCA, 0xA2, 0xCB, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xA1, 0xCA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+};
+
+static unsigned char u2c_31[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0xA3, 0x74, 0xA3, 0x75, 0xA3, 0x76, /* 0x04-0x07 */
+ 0xA3, 0x77, 0xA3, 0x78, 0xA3, 0x79, 0xA3, 0x7A, /* 0x08-0x0B */
+ 0xA3, 0x7B, 0xA3, 0x7C, 0xA3, 0x7D, 0xA3, 0x7E, /* 0x0C-0x0F */
+ 0xA3, 0xA1, 0xA3, 0xA2, 0xA3, 0xA3, 0xA3, 0xA4, /* 0x10-0x13 */
+ 0xA3, 0xA5, 0xA3, 0xA6, 0xA3, 0xA7, 0xA3, 0xA8, /* 0x14-0x17 */
+ 0xA3, 0xA9, 0xA3, 0xAA, 0xA3, 0xAB, 0xA3, 0xAC, /* 0x18-0x1B */
+ 0xA3, 0xAD, 0xA3, 0xAE, 0xA3, 0xAF, 0xA3, 0xB0, /* 0x1C-0x1F */
+ 0xA3, 0xB1, 0xA3, 0xB2, 0xA3, 0xB3, 0xA3, 0xB4, /* 0x20-0x23 */
+ 0xA3, 0xB5, 0xA3, 0xB6, 0xA3, 0xB7, 0xA3, 0xB8, /* 0x24-0x27 */
+ 0xA3, 0xB9, 0xA3, 0xBA, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0xA4, 0x40, 0xA4, 0x47, /* 0x90-0x93 */
+ 0xA4, 0x54, 0xA5, 0x7C, 0xA4, 0x57, 0xA4, 0xA4, /* 0x94-0x97 */
+ 0xA4, 0x55, 0xA5, 0xD2, 0xA4, 0x41, 0xA4, 0xFE, /* 0x98-0x9B */
+ 0xA4, 0x42, 0xA4, 0xD1, 0xA6, 0x61, 0xA4, 0x48, /* 0x9C-0x9F */
+};
+
+static unsigned char u2c_32[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0xA4, 0x40, 0xA4, 0x47, 0xA4, 0x54, 0xA5, 0x7C, /* 0x20-0x23 */
+ 0xA4, 0xAD, 0xA4, 0xBB, 0xA4, 0x43, 0xA4, 0x4B, /* 0x24-0x27 */
+ 0xA4, 0x45, 0xA4, 0x51, 0xA4, 0xEB, 0xA4, 0xF5, /* 0x28-0x2B */
+ 0xA4, 0xF4, 0xA4, 0xEC, 0xAA, 0xF7, 0xA4, 0x67, /* 0x2C-0x2F */
+ 0xA4, 0xE9, 0xAE, 0xE8, 0xA6, 0xB3, 0xAA, 0xC0, /* 0x30-0x33 */
+ 0xA6, 0x57, 0xAF, 0x53, 0xB0, 0x5D, 0xAF, 0xAC, /* 0x34-0x37 */
+ 0xB3, 0xD2, 0xA5, 0x4E, 0xA9, 0x49, 0xBE, 0xC7, /* 0x38-0x3B */
+ 0xBA, 0xCA, 0xA5, 0xF8, 0xB8, 0xEA, 0xA8, 0xF3, /* 0x3C-0x3F */
+ 0xB2, 0xBD, 0xA5, 0xF0, 0xA6, 0xDB, 0xA6, 0xDC, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0xA4, 0x40, 0xA4, 0x47, 0xA4, 0x54, 0xA5, 0x7C, /* 0x80-0x83 */
+ 0xA4, 0xAD, 0xA4, 0xBB, 0xA4, 0x43, 0xA4, 0x4B, /* 0x84-0x87 */
+ 0xA4, 0x45, 0xA4, 0x51, 0xA4, 0xEB, 0xA4, 0xF5, /* 0x88-0x8B */
+ 0xA4, 0xF4, 0xA4, 0xEC, 0xAA, 0xF7, 0xA4, 0x67, /* 0x8C-0x8F */
+ 0xA4, 0xE9, 0xAE, 0xE8, 0xA6, 0xB3, 0xAA, 0xC0, /* 0x90-0x93 */
+ 0xA6, 0x57, 0xAF, 0x53, 0xB0, 0x5D, 0xAF, 0xAC, /* 0x94-0x97 */
+ 0xB3, 0xD2, 0xAF, 0xB5, 0xA8, 0x6B, 0xA4, 0x6B, /* 0x98-0x9B */
+ 0xBE, 0x41, 0xC0, 0x75, 0xA6, 0x4C, 0xAA, 0x60, /* 0x9C-0x9F */
+ 0xB6, 0xB5, 0xA5, 0xF0, 0xBC, 0x67, 0xA1, 0xC0, /* 0xA0-0xA3 */
+ 0xA4, 0x57, 0xA4, 0xA4, 0xA4, 0x55, 0xA5, 0xAA, /* 0xA4-0xA7 */
+ 0xA5, 0x6B, 0xC2, 0xE5, 0xA9, 0x76, 0xBE, 0xC7, /* 0xA8-0xAB */
+ 0xBA, 0xCA, 0xA5, 0xF8, 0xB8, 0xEA, 0xA8, 0xF3, /* 0xAC-0xAF */
+ 0xA9, 0x5D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+};
+
+static unsigned char u2c_33[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0xA2, 0x55, 0xA2, 0x56, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0xA2, 0x50, 0xA2, 0x51, 0xA2, 0x52, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0xA2, 0x54, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xA2, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0xA2, 0x53, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0xA1, 0xEB, 0xA1, 0xEA, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0xA2, 0x4F, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+};
+
+static unsigned char u2c_4E[512] = {
+ 0xA4, 0x40, 0xA4, 0x42, 0x00, 0x00, 0xA4, 0x43, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC9, 0x45, /* 0x04-0x07 */
+ 0xA4, 0x56, 0xA4, 0x54, 0xA4, 0x57, 0xA4, 0x55, /* 0x08-0x0B */
+ 0xC9, 0x46, 0xA4, 0xA3, 0xC9, 0x4F, 0xC9, 0x4D, /* 0x0C-0x0F */
+ 0xA4, 0xA2, 0xA4, 0xA1, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0xA5, 0x42, 0xA5, 0x41, 0xA5, 0x40, 0x00, 0x00, /* 0x14-0x17 */
+ 0xA5, 0x43, 0xA4, 0xFE, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0xA5, 0xE0, 0xA5, 0xE1, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0xA8, 0xC3, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0x58, /* 0x28-0x2B */
+ 0x00, 0x00, 0xA4, 0xA4, 0xC9, 0x50, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xA4, 0xA5, 0xC9, 0x63, 0xA6, 0xEA, 0xCB, 0xB1, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0xA4, 0x59, 0xA4, 0xA6, 0x00, 0x00, 0xA5, 0x44, /* 0x38-0x3B */
+ 0xC9, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0xC9, 0x40, 0xA4, 0x44, /* 0x40-0x43 */
+ 0x00, 0x00, 0xA4, 0x5B, 0x00, 0x00, 0xC9, 0x47, /* 0x44-0x47 */
+ 0xA4, 0x5C, 0x00, 0x00, 0x00, 0x00, 0xA4, 0xA7, /* 0x48-0x4B */
+ 0x00, 0x00, 0xA5, 0x45, 0xA5, 0x47, 0xA5, 0x46, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0xA5, 0xE2, 0xA5, 0xE3, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0xA8, 0xC4, 0x00, 0x00, /* 0x54-0x57 */
+ 0xAD, 0xBC, 0xA4, 0x41, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0xC9, 0x41, 0xA4, 0x45, 0xA4, 0x5E, 0xA4, 0x5D, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0xA5, 0xE4, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA8, 0xC5, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0xB0, 0xAE, 0xD4, 0x4B, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0xB6, 0xC3, 0xDC, 0xB1, /* 0x80-0x83 */
+ 0xDC, 0xB2, 0x00, 0x00, 0xA4, 0x46, 0x00, 0x00, /* 0x84-0x87 */
+ 0xA4, 0xA9, 0x00, 0x00, 0x00, 0x00, 0xA8, 0xC6, /* 0x88-0x8B */
+ 0xA4, 0x47, 0xC9, 0x48, 0xA4, 0x5F, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xA4, 0xAA, 0xA4, 0xAC, 0xC9, 0x51, /* 0x90-0x93 */
+ 0xA4, 0xAD, 0xA4, 0xAB, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0xA5, 0xE5, 0x00, 0x00, 0xA8, 0xC7, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0xA8, 0xC8, 0xAB, 0x45, /* 0x9C-0x9F */
+ 0x00, 0x00, 0xA4, 0x60, 0xA4, 0xAE, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0xA5, 0xE6, 0xA5, 0xE8, 0xA5, 0xE7, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xA6, 0xEB, 0x00, 0x00, 0x00, 0x00, 0xA8, 0xC9, /* 0xA8-0xAB */
+ 0xA8, 0xCA, 0xAB, 0x46, 0xAB, 0x47, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAD, 0xBD, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0xDC, 0xB3, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xF6, 0xD6, 0xA4, 0x48, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0xA4, 0xB0, 0xA4, 0xAF, 0xC9, 0x52, 0xA4, 0xB1, /* 0xC0-0xC3 */
+ 0xA4, 0xB7, 0x00, 0x00, 0xA4, 0xB2, 0xA4, 0xB3, /* 0xC4-0xC7 */
+ 0xC9, 0x54, 0xC9, 0x53, 0xA4, 0xB5, 0xA4, 0xB6, /* 0xC8-0xCB */
+ 0x00, 0x00, 0xA4, 0xB4, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xA5, 0x4A, 0xA5, 0x4B, 0xA5, 0x4C, 0xA5, 0x4D, /* 0xD4-0xD7 */
+ 0xA5, 0x49, 0xA5, 0x50, 0xC9, 0x6A, 0x00, 0x00, /* 0xD8-0xDB */
+ 0xC9, 0x66, 0xC9, 0x69, 0xA5, 0x51, 0xA5, 0x61, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xC9, 0x68, 0x00, 0x00, 0xA5, 0x4E, /* 0xE0-0xE3 */
+ 0xA5, 0x4F, 0xA5, 0x48, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xC9, 0x65, 0xC9, 0x67, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0xA5, 0xF5, 0xC9, 0xB0, 0xA5, 0xF2, 0xA5, 0xF6, /* 0xF0-0xF3 */
+ 0xC9, 0xBA, 0xC9, 0xAE, 0xA5, 0xF3, 0xC9, 0xB2, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA5, 0xF4, /* 0xF8-0xFB */
+ 0x00, 0x00, 0xA5, 0xF7, 0x00, 0x00, 0xA5, 0xE9, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_4F[512] = {
+ 0xC9, 0xB1, 0xA5, 0xF8, 0xC9, 0xB5, 0x00, 0x00, /* 0x00-0x03 */
+ 0xC9, 0xB9, 0xC9, 0xB6, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0xC9, 0xB3, 0xA5, 0xEA, 0xA5, 0xEC, 0xA5, 0xF9, /* 0x08-0x0B */
+ 0x00, 0x00, 0xA5, 0xEE, 0xC9, 0xAB, 0xA5, 0xF1, /* 0x0C-0x0F */
+ 0xA5, 0xEF, 0xA5, 0xF0, 0xC9, 0xBB, 0xC9, 0xB8, /* 0x10-0x13 */
+ 0xC9, 0xAF, 0xA5, 0xED, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0xC9, 0xAC, 0xA5, 0xEB, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0xC9, 0xB4, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0xC9, 0xB7, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0xC9, 0xAD, 0xCA, 0x66, 0x00, 0x00, 0xA7, 0x42, /* 0x2C-0x2F */
+ 0xA6, 0xF4, 0x00, 0x00, 0x00, 0x00, 0xCA, 0x67, /* 0x30-0x33 */
+ 0xA6, 0xF1, 0x00, 0x00, 0xA7, 0x44, 0x00, 0x00, /* 0x34-0x37 */
+ 0xA6, 0xF9, 0x00, 0x00, 0xA6, 0xF8, 0xCA, 0x5B, /* 0x38-0x3B */
+ 0xA6, 0xFC, 0xA6, 0xF7, 0xCA, 0x60, 0xCA, 0x68, /* 0x3C-0x3F */
+ 0x00, 0x00, 0xCA, 0x64, 0x00, 0x00, 0xA6, 0xFA, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0xA6, 0xFD, 0xA6, 0xEE, /* 0x44-0x47 */
+ 0xA7, 0x47, 0xCA, 0x5D, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0xCB, 0xBD, 0xA6, 0xEC, 0xA7, 0x43, 0xA6, 0xED, /* 0x4C-0x4F */
+ 0xA6, 0xF5, 0xA6, 0xF6, 0xCA, 0x62, 0xCA, 0x5E, /* 0x50-0x53 */
+ 0xA6, 0xFB, 0xA6, 0xF3, 0xCA, 0x5A, 0xA6, 0xEF, /* 0x54-0x57 */
+ 0xCA, 0x65, 0xA7, 0x45, 0xA7, 0x48, 0xA6, 0xF2, /* 0x58-0x5B */
+ 0xA7, 0x40, 0xA7, 0x46, 0xA6, 0xF0, 0xCA, 0x63, /* 0x5C-0x5F */
+ 0xA7, 0x41, 0xCA, 0x69, 0xCA, 0x5C, 0xA6, 0xFE, /* 0x60-0x63 */
+ 0xCA, 0x5F, 0x00, 0x00, 0x00, 0x00, 0xCA, 0x61, /* 0x64-0x67 */
+ 0x00, 0x00, 0xA8, 0xD8, 0xCB, 0xBF, 0xCB, 0xCB, /* 0x68-0x6B */
+ 0xA8, 0xD0, 0x00, 0x00, 0xCB, 0xCC, 0xA8, 0xCB, /* 0x6C-0x6F */
+ 0xA8, 0xD5, 0x00, 0x00, 0x00, 0x00, 0xA8, 0xCE, /* 0x70-0x73 */
+ 0xCB, 0xB9, 0xA8, 0xD6, 0xCB, 0xB8, 0xCB, 0xBC, /* 0x74-0x77 */
+ 0xCB, 0xC3, 0xCB, 0xC1, 0xA8, 0xDE, 0xA8, 0xD9, /* 0x78-0x7B */
+ 0xCB, 0xB3, 0xCB, 0xB5, 0xA8, 0xDB, 0xA8, 0xCF, /* 0x7C-0x7F */
+
+ 0xCB, 0xB6, 0xCB, 0xC2, 0xCB, 0xC9, 0xA8, 0xD4, /* 0x80-0x83 */
+ 0xCB, 0xBB, 0xCB, 0xB4, 0xA8, 0xD3, 0xCB, 0xB7, /* 0x84-0x87 */
+ 0xA8, 0xD7, 0xCB, 0xBA, 0x00, 0x00, 0xA8, 0xD2, /* 0x88-0x8B */
+ 0x00, 0x00, 0xA8, 0xCD, 0x00, 0x00, 0xA8, 0xDC, /* 0x8C-0x8F */
+ 0xCB, 0xC4, 0xA8, 0xDD, 0xCB, 0xC8, 0x00, 0x00, /* 0x90-0x93 */
+ 0xCB, 0xC6, 0xCB, 0xCA, 0xA8, 0xDA, 0xCB, 0xBE, /* 0x94-0x97 */
+ 0xCB, 0xB2, 0x00, 0x00, 0xCB, 0xC0, 0xA8, 0xD1, /* 0x98-0x9B */
+ 0xCB, 0xC5, 0xA8, 0xCC, 0xCB, 0xC7, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0xAB, 0x56, 0xAB, 0x4A, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0xCD, 0xE0, 0xCD, 0xE8, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0xAB, 0x49, 0xAB, 0x51, 0xAB, 0x5D, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xCD, 0xEE, 0xCD, 0xEC, 0xCD, 0xE7, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAB, 0x4B, /* 0xBC-0xBF */
+ 0xCD, 0xED, 0xCD, 0xE3, 0xAB, 0x59, 0xAB, 0x50, /* 0xC0-0xC3 */
+ 0xAB, 0x58, 0xCD, 0xDE, 0x00, 0x00, 0xCD, 0xEA, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xCD, 0xE1, 0xAB, 0x54, 0xCD, 0xE2, /* 0xC8-0xCB */
+ 0x00, 0x00, 0xCD, 0xDD, 0xAB, 0x5B, 0xAB, 0x4E, /* 0xCC-0xCF */
+ 0xAB, 0x57, 0xAB, 0x4D, 0x00, 0x00, 0xCD, 0xDF, /* 0xD0-0xD3 */
+ 0xCD, 0xE4, 0x00, 0x00, 0xCD, 0xEB, 0xAB, 0x55, /* 0xD4-0xD7 */
+ 0xAB, 0x52, 0xCD, 0xE6, 0xAB, 0x5A, 0xCD, 0xE9, /* 0xD8-0xDB */
+ 0xCD, 0xE5, 0xAB, 0x4F, 0xAB, 0x5C, 0xAB, 0x53, /* 0xDC-0xDF */
+ 0xAB, 0x4C, 0xAB, 0x48, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0xCD, 0xEF, 0x00, 0x00, 0xAD, 0xD7, 0xAD, 0xC1, /* 0xEC-0xEF */
+ 0x00, 0x00, 0xAD, 0xD1, 0x00, 0x00, 0xAD, 0xD6, /* 0xF0-0xF3 */
+ 0xD0, 0xD0, 0xD0, 0xCF, 0xD0, 0xD4, 0xD0, 0xD5, /* 0xF4-0xF7 */
+ 0xAD, 0xC4, 0x00, 0x00, 0xAD, 0xCD, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0xAD, 0xDA, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_50[512] = {
+ 0xAD, 0xCE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0xD0, 0xC9, 0xAD, 0xC7, 0xD0, 0xCA, /* 0x04-0x07 */
+ 0x00, 0x00, 0xAD, 0xDC, 0x00, 0x00, 0xAD, 0xD3, /* 0x08-0x0B */
+ 0xAD, 0xBE, 0xAD, 0xBF, 0xD0, 0xDD, 0xB0, 0xBF, /* 0x0C-0x0F */
+ 0x00, 0x00, 0xAD, 0xCC, 0xAD, 0xCB, 0xD0, 0xCB, /* 0x10-0x13 */
+ 0xAD, 0xCF, 0xD4, 0x5B, 0xAD, 0xC6, 0xD0, 0xD6, /* 0x14-0x17 */
+ 0xAD, 0xD5, 0xAD, 0xD4, 0xAD, 0xCA, 0xD0, 0xCE, /* 0x18-0x1B */
+ 0xD0, 0xD7, 0x00, 0x00, 0xD0, 0xC8, 0xAD, 0xC9, /* 0x1C-0x1F */
+ 0xD0, 0xD8, 0xAD, 0xD2, 0xD0, 0xCC, 0xAD, 0xC0, /* 0x20-0x23 */
+ 0x00, 0x00, 0xAD, 0xC3, 0xAD, 0xC2, 0xD0, 0xD9, /* 0x24-0x27 */
+ 0xAD, 0xD0, 0xAD, 0xC5, 0xAD, 0xD9, 0xAD, 0xDB, /* 0x28-0x2B */
+ 0xD0, 0xD3, 0xAD, 0xD8, 0x00, 0x00, 0xD0, 0xDB, /* 0x2C-0x2F */
+ 0xD0, 0xCD, 0xD0, 0xDC, 0x00, 0x00, 0xD0, 0xD1, /* 0x30-0x33 */
+ 0x00, 0x00, 0xD0, 0xDA, 0x00, 0x00, 0xD0, 0xD2, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0xAD, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0xD4, 0x63, 0xD4, 0x57, 0x00, 0x00, 0xB0, 0xB3, /* 0x40-0x43 */
+ 0x00, 0x00, 0xD4, 0x5C, 0xD4, 0x62, 0xB0, 0xB2, /* 0x44-0x47 */
+ 0xD4, 0x55, 0xB0, 0xB6, 0xD4, 0x59, 0xD4, 0x52, /* 0x48-0x4B */
+ 0xB0, 0xB4, 0xD4, 0x56, 0xB0, 0xB9, 0xB0, 0xBE, /* 0x4C-0x4F */
+ 0x00, 0x00, 0xD4, 0x67, 0x00, 0x00, 0xD4, 0x51, /* 0x50-0x53 */
+ 0x00, 0x00, 0xB0, 0xBA, 0x00, 0x00, 0xD4, 0x66, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0xB0, 0xB5, 0xD4, 0x58, /* 0x58-0x5B */
+ 0xB0, 0xB1, 0xD4, 0x53, 0xD4, 0x4F, 0xD4, 0x5D, /* 0x5C-0x5F */
+ 0xD4, 0x50, 0xD4, 0x4E, 0xD4, 0x5A, 0xD4, 0x60, /* 0x60-0x63 */
+ 0xD4, 0x61, 0xB0, 0xB7, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0xD8, 0x5B, 0xD4, 0x5E, 0xD4, 0x4D, 0xD4, 0x5F, /* 0x68-0x6B */
+ 0x00, 0x00, 0xB0, 0xC1, 0xD4, 0x64, 0xB0, 0xC0, /* 0x6C-0x6F */
+ 0xD4, 0x4C, 0x00, 0x00, 0xD4, 0x54, 0xD4, 0x65, /* 0x70-0x73 */
+ 0xB0, 0xBC, 0xB0, 0xBB, 0xB0, 0xB8, 0xB0, 0xBD, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0xB0, 0xAF, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0xB0, 0xB0, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0xB3, 0xC8, 0x00, 0x00, 0xD8, 0x5E, 0xD8, 0x57, /* 0x80-0x83 */
+ 0x00, 0x00, 0xB3, 0xC5, 0x00, 0x00, 0xD8, 0x5F, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD8, 0x55, /* 0x88-0x8B */
+ 0xD8, 0x58, 0xB3, 0xC4, 0xD8, 0x59, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xB3, 0xC7, 0xD8, 0x5D, 0x00, 0x00, /* 0x90-0x93 */
+ 0xD8, 0x53, 0xD8, 0x52, 0xB3, 0xC9, 0x00, 0x00, /* 0x94-0x97 */
+ 0xB3, 0xCA, 0xB3, 0xC6, 0xB3, 0xCB, 0xD8, 0x51, /* 0x98-0x9B */
+ 0xD8, 0x5C, 0xD8, 0x5A, 0xD8, 0x54, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0xB3, 0xC3, 0xD8, 0x56, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0xB6, 0xCA, 0xB6, 0xC4, 0xDC, 0xB7, 0xB6, 0xCD, /* 0xAC-0xAF */
+ 0xDC, 0xBD, 0xDC, 0xC0, 0xB6, 0xC6, 0xB6, 0xC7, /* 0xB0-0xB3 */
+ 0xDC, 0xBA, 0xB6, 0xC5, 0xDC, 0xC3, 0xB6, 0xCB, /* 0xB4-0xB7 */
+ 0xDC, 0xC4, 0x00, 0x00, 0xDC, 0xBF, 0xB6, 0xCC, /* 0xB8-0xBB */
+ 0x00, 0x00, 0xDC, 0xB4, 0xB6, 0xC9, 0xDC, 0xB5, /* 0xBC-0xBF */
+ 0x00, 0x00, 0xDC, 0xBE, 0xDC, 0xBC, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xDC, 0xB8, 0xB6, 0xC8, 0xDC, 0xB6, 0xB6, 0xCE, /* 0xC4-0xC7 */
+ 0xDC, 0xBB, 0xDC, 0xC2, 0xDC, 0xB9, 0xDC, 0xC1, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0xB9, 0xB6, 0xB9, 0xB3, /* 0xCC-0xCF */
+ 0x00, 0x00, 0xB9, 0xB4, 0x00, 0x00, 0xE0, 0xF9, /* 0xD0-0xD3 */
+ 0xE0, 0xF1, 0xB9, 0xB2, 0xB9, 0xAF, 0xE0, 0xF2, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0xB9, 0xB1, 0xE0, 0xF5, /* 0xD8-0xDB */
+ 0x00, 0x00, 0xE0, 0xF7, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0xE0, 0xFE, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFD, /* 0xE0-0xE3 */
+ 0xE0, 0xF8, 0xB9, 0xAE, 0xE0, 0xF0, 0xB9, 0xAC, /* 0xE4-0xE7 */
+ 0xE0, 0xF3, 0xB9, 0xB7, 0xE0, 0xF6, 0x00, 0x00, /* 0xE8-0xEB */
+ 0xE0, 0xFA, 0xB9, 0xB0, 0xB9, 0xAD, 0xE0, 0xFC, /* 0xEC-0xEF */
+ 0xE0, 0xFB, 0xB9, 0xB5, 0x00, 0x00, 0xE0, 0xF4, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0xBB, 0xF8, 0xE4, 0xEC, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0xE4, 0xE9, 0xBB, 0xF9, 0x00, 0x00, 0xBB, 0xF7, /* 0xF8-0xFB */
+ 0x00, 0x00, 0xE4, 0xF0, 0xE4, 0xED, 0xE4, 0xE6, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_51[512] = {
+ 0xBB, 0xF6, 0x00, 0x00, 0xBB, 0xFA, 0xE4, 0xE7, /* 0x00-0x03 */
+ 0xBB, 0xF5, 0xBB, 0xFD, 0xE4, 0xEA, 0xE4, 0xEB, /* 0x04-0x07 */
+ 0xBB, 0xFB, 0xBB, 0xFC, 0xE4, 0xF1, 0xE4, 0xEE, /* 0x08-0x0B */
+ 0xE4, 0xEF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xBE, 0xAA, 0xE8, 0xF8, 0xBE, 0xA7, 0xE8, 0xF5, /* 0x10-0x13 */
+ 0xBE, 0xA9, 0xBE, 0xAB, 0x00, 0x00, 0xE8, 0xF6, /* 0x14-0x17 */
+ 0xBE, 0xA8, 0x00, 0x00, 0xE8, 0xF7, 0x00, 0x00, /* 0x18-0x1B */
+ 0xE8, 0xF4, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x76, /* 0x1C-0x1F */
+ 0xEC, 0xBD, 0xC0, 0x77, 0xEC, 0xBB, 0x00, 0x00, /* 0x20-0x23 */
+ 0xEC, 0xBC, 0xEC, 0xBA, 0xEC, 0xB9, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0xEC, 0xBE, 0xC0, 0x75, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0xEF, 0xB8, 0xEF, 0xB9, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xE4, 0xE8, 0xEF, 0xB7, 0xC0, 0x78, 0xC3, 0x5F, /* 0x30-0x33 */
+ 0xF1, 0xEB, 0xF1, 0xEC, 0x00, 0x00, 0xC4, 0xD7, /* 0x34-0x37 */
+ 0xC4, 0xD8, 0xF5, 0xC1, 0xF5, 0xC0, 0xC5, 0x6C, /* 0x38-0x3B */
+ 0xC5, 0x6B, 0xF7, 0xD0, 0x00, 0x00, 0xA4, 0x49, /* 0x3C-0x3F */
+ 0xA4, 0x61, 0xA4, 0xB9, 0x00, 0x00, 0xA4, 0xB8, /* 0x40-0x43 */
+ 0xA5, 0x53, 0xA5, 0x52, 0xA5, 0xFC, 0xA5, 0xFB, /* 0x44-0x47 */
+ 0xA5, 0xFD, 0xA5, 0xFA, 0x00, 0x00, 0xA7, 0x4A, /* 0x48-0x4B */
+ 0xA7, 0x49, 0xA7, 0x4B, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0xA8, 0xE0, 0x00, 0x00, /* 0x50-0x53 */
+ 0xA8, 0xDF, 0xA8, 0xE1, 0x00, 0x00, 0xAB, 0x5E, /* 0x54-0x57 */
+ 0x00, 0x00, 0xA2, 0x59, 0xD0, 0xDE, 0xA2, 0x5A, /* 0x58-0x5B */
+ 0xB0, 0xC2, 0xA2, 0x5C, 0xA2, 0x5B, 0xD8, 0x60, /* 0x5C-0x5F */
+ 0x00, 0x00, 0xA2, 0x5D, 0xB9, 0xB8, 0xA2, 0x5E, /* 0x60-0x63 */
+ 0x00, 0x00, 0xA4, 0x4A, 0x00, 0x00, 0xA4, 0xBA, /* 0x64-0x67 */
+ 0xA5, 0xFE, 0xA8, 0xE2, 0x00, 0x00, 0xA4, 0x4B, /* 0x68-0x6B */
+ 0xA4, 0xBD, 0xA4, 0xBB, 0xA4, 0xBC, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0xA6, 0x40, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0xA7, 0x4C, 0xA8, 0xE4, 0xA8, 0xE3, /* 0x74-0x77 */
+ 0xA8, 0xE5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0xAD, 0xDD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0xBE, 0xAC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC9, 0x4E, /* 0x84-0x87 */
+ 0x00, 0x00, 0xA5, 0x54, 0xA5, 0x55, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0xA6, 0x41, 0x00, 0x00, 0xCA, 0x6A, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xAB, 0x60, 0xAB, 0x5F, 0xD0, 0xE0, /* 0x90-0x93 */
+ 0xD0, 0xDF, 0xB0, 0xC3, 0x00, 0x00, 0xA4, 0xBE, /* 0x94-0x97 */
+ 0xC9, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0xCB, 0xCD, 0x00, 0x00, /* 0x9C-0x9F */
+ 0xAB, 0x61, 0x00, 0x00, 0xAD, 0xE0, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0xAD, 0xDE, 0xAD, 0xDF, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0xBE, 0xAD, 0x00, 0x00, /* 0xA8-0xAB */
+ 0xA5, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0xA6, 0x42, 0xC9, 0xBC, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0xA7, 0x4D, 0xA7, 0x4E, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xCA, 0x6B, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0xCB, 0xCE, 0xA8, 0xE6, 0xCB, 0xCF, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xD0, 0xE2, 0xD0, 0xE3, 0xAD, 0xE3, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0xD0, 0xE4, 0x00, 0x00, 0xD0, 0xE1, 0xAD, 0xE4, /* 0xC8-0xCB */
+ 0xAD, 0xE2, 0xAD, 0xE1, 0xD0, 0xE5, 0x00, 0x00, /* 0xCC-0xCF */
+ 0xD4, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xD8, 0x61, 0x00, 0x00, 0x00, 0x00, 0xDC, 0xC5, /* 0xD4-0xD7 */
+ 0xE1, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0xBB, 0xFE, 0xBE, 0xAE, 0xE8, 0xF9, 0x00, 0x00, /* 0xDC-0xDF */
+ 0xA4, 0x4C, 0xA4, 0x5A, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0xB0, 0xC4, 0xB3, 0xCD, 0x00, 0x00, 0xB9, 0xB9, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0xC9, 0x42, 0xA4, 0xBF, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0xA5, 0x59, 0xA5, 0x57, 0xA5, 0x58, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0xA8, 0xE7, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_52[512] = {
+ 0xA4, 0x4D, 0xA4, 0x4E, 0x00, 0x00, 0xA4, 0x62, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0xA4, 0xC0, 0xA4, 0xC1, /* 0x04-0x07 */
+ 0xA4, 0xC2, 0xC9, 0xBE, 0xA5, 0x5A, 0x00, 0x00, /* 0x08-0x0B */
+ 0xC9, 0x6B, 0x00, 0x00, 0xA6, 0x46, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xC9, 0xBF, 0xA6, 0x44, 0xA6, 0x45, 0xC9, 0xBD, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0xA6, 0x47, 0xA6, 0x43, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0xCA, 0x6C, 0xAA, 0xEC, 0xCA, 0x6D, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0xCA, 0x6E, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0xA7, 0x50, 0xA7, 0x4F, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0xA7, 0x53, 0xA7, 0x51, 0xA7, 0x52, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0xA8, 0xED, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xA8, 0xEC, 0xCB, 0xD4, 0xCB, 0xD1, 0xCB, 0xD2, /* 0x30-0x33 */
+ 0x00, 0x00, 0xCB, 0xD0, 0xA8, 0xEE, 0xA8, 0xEA, /* 0x34-0x37 */
+ 0xA8, 0xE9, 0x00, 0x00, 0xA8, 0xEB, 0xA8, 0xE8, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0xA8, 0xEF, 0x00, 0x00, 0xAB, 0x63, /* 0x40-0x43 */
+ 0xCD, 0xF0, 0x00, 0x00, 0xCB, 0xD3, 0xAB, 0x68, /* 0x44-0x47 */
+ 0x00, 0x00, 0xCD, 0xF1, 0xAB, 0x64, 0xAB, 0x67, /* 0x48-0x4B */
+ 0xAB, 0x66, 0xAB, 0x65, 0xAB, 0x62, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0xD0, 0xE8, 0x00, 0x00, /* 0x50-0x53 */
+ 0xAD, 0xE7, 0xD0, 0xEB, 0xAD, 0xE5, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0xD0, 0xE7, 0xAD, 0xE8, /* 0x58-0x5B */
+ 0xAD, 0xE6, 0xAD, 0xE9, 0xD0, 0xE9, 0xD0, 0xEA, /* 0x5C-0x5F */
+ 0x00, 0x00, 0xD0, 0xE6, 0xD0, 0xEC, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0xB3, 0xD1, 0xB0, 0xC5, 0xD4, 0x69, /* 0x68-0x6B */
+ 0xD4, 0x6B, 0xD4, 0x6A, 0xD4, 0x6C, 0xB0, 0xC6, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0xB3, 0xCE, 0x00, 0x00, /* 0x70-0x73 */
+ 0xB3, 0xCF, 0xB3, 0xD0, 0x00, 0x00, 0xB6, 0xD0, /* 0x74-0x77 */
+ 0xDC, 0xC7, 0x00, 0x00, 0xDC, 0xC6, 0xDC, 0xC8, /* 0x78-0x7B */
+ 0xDC, 0xC9, 0xB6, 0xD1, 0x00, 0x00, 0xB6, 0xCF, /* 0x7C-0x7F */
+
+ 0xE1, 0x41, 0xE1, 0x42, 0xB9, 0xBB, 0xB9, 0xBA, /* 0x80-0x83 */
+ 0xE3, 0x5A, 0x00, 0x00, 0x00, 0x00, 0xBC, 0x40, /* 0x84-0x87 */
+ 0xBC, 0x41, 0xBC, 0x42, 0xBC, 0x44, 0xE4, 0xF2, /* 0x88-0x8B */
+ 0xE4, 0xF3, 0xBC, 0x43, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xBE, 0xAF, 0x00, 0x00, 0xBE, 0xB0, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0xF1, 0xED, 0xF5, 0xC3, /* 0x94-0x97 */
+ 0xF5, 0xC2, 0xF7, 0xD1, 0x00, 0x00, 0xA4, 0x4F, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA5, 0x5C, /* 0x9C-0x9F */
+ 0xA5, 0x5B, 0x00, 0x00, 0x00, 0x00, 0xA6, 0x48, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0xC9, 0xC0, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0xA7, 0x55, 0xA7, 0x56, 0xA7, 0x54, /* 0xA8-0xAB */
+ 0xA7, 0x57, 0xCA, 0x6F, 0xCA, 0x70, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA8, 0xF1, /* 0xB8-0xBB */
+ 0xCB, 0xD5, 0x00, 0x00, 0xA8, 0xF0, 0x00, 0x00, /* 0xBC-0xBF */
+ 0xCD, 0xF2, 0xAB, 0x6C, 0xCD, 0xF3, 0xAB, 0x6B, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAB, 0x69, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xAB, 0x6A, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0xD0, 0xED, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0xB0, 0xC7, 0xD4, 0x6E, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0xB0, 0xCA, 0xD4, 0x6D, 0xB1, 0xE5, /* 0xD4-0xD7 */
+ 0xB0, 0xC9, 0xB0, 0xC8, 0x00, 0x00, 0xB3, 0xD4, /* 0xD8-0xDB */
+ 0x00, 0x00, 0xB3, 0xD3, 0xB3, 0xD2, 0xB6, 0xD2, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0xB6, 0xD5, 0xB6, 0xD6, /* 0xE0-0xE3 */
+ 0xB6, 0xD4, 0x00, 0x00, 0xB6, 0xD3, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0xE1, 0x43, 0x00, 0x00, 0xE1, 0x44, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0xF5, /* 0xEC-0xEF */
+ 0xBC, 0x45, 0xE4, 0xF4, 0x00, 0x00, 0xBE, 0xB1, /* 0xF0-0xF3 */
+ 0xEC, 0xBF, 0xC0, 0x79, 0x00, 0x00, 0xF1, 0xEE, /* 0xF4-0xF7 */
+ 0xC4, 0x55, 0x00, 0x00, 0xA4, 0x63, 0xA4, 0xC3, /* 0xF8-0xFB */
+ 0xC9, 0x56, 0x00, 0x00, 0xA4, 0xC4, 0xA4, 0xC5, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_53[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0xA5, 0x5D, 0xA5, 0x5E, 0x00, 0x00, /* 0x04-0x07 */
+ 0xA6, 0x49, 0xCA, 0x71, 0xCB, 0xD6, 0xCB, 0xD7, /* 0x08-0x0B */
+ 0x00, 0x00, 0xAB, 0x6D, 0xD0, 0xEE, 0xB0, 0xCC, /* 0x0C-0x0F */
+ 0xB0, 0xCB, 0xD8, 0x63, 0xD8, 0x62, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0xA4, 0x50, 0xA4, 0xC6, 0xA5, 0x5F, /* 0x14-0x17 */
+ 0x00, 0x00, 0xB0, 0xCD, 0xC9, 0x43, 0x00, 0x00, /* 0x18-0x1B */
+ 0xC9, 0x6C, 0xA5, 0x60, 0x00, 0x00, 0xC9, 0xC2, /* 0x1C-0x1F */
+ 0xA6, 0x4B, 0xA6, 0x4A, 0xC9, 0xC1, 0xA7, 0x58, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xAD, 0xEA, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0xD4, 0x6F, 0x00, 0x00, 0xB6, 0xD7, /* 0x2C-0x2F */
+ 0xE1, 0x45, 0xB9, 0xBC, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0xE8, 0xFA, 0x00, 0x00, 0x00, 0x00, 0xF3, 0xFD, /* 0x34-0x37 */
+ 0x00, 0x00, 0xA4, 0xC7, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0xCB, 0xD8, 0xCD, 0xF4, 0xB0, 0xD0, 0xB0, 0xCE, /* 0x3C-0x3F */
+ 0xB0, 0xCF, 0xA4, 0x51, 0x00, 0x00, 0xA4, 0x64, /* 0x40-0x43 */
+ 0xA2, 0xCD, 0xA4, 0xCA, 0x00, 0x00, 0xA4, 0xC9, /* 0x44-0x47 */
+ 0xA4, 0xC8, 0xA5, 0x63, 0xA5, 0x62, 0x00, 0x00, /* 0x48-0x4B */
+ 0xC9, 0x6D, 0xC9, 0xC3, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0xA8, 0xF5, 0xA8, 0xF2, 0xA8, 0xF4, /* 0x50-0x53 */
+ 0xA8, 0xF3, 0x00, 0x00, 0x00, 0x00, 0xAB, 0x6E, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0xB3, 0xD5, 0x00, 0x00, /* 0x58-0x5B */
+ 0xA4, 0x52, 0x00, 0x00, 0xA4, 0xCB, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xA5, 0x65, 0xA5, 0x64, 0x00, 0x00, 0xCA, 0x72, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0xA8, 0xF6, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0xC9, 0x57, 0x00, 0x00, 0xA5, 0x67, 0xA5, 0x66, /* 0x6C-0x6F */
+ 0xA6, 0x4C, 0xA6, 0x4D, 0xCA, 0x73, 0xA7, 0x59, /* 0x70-0x73 */
+ 0x00, 0x00, 0xA7, 0x5A, 0x00, 0x00, 0xA8, 0xF7, /* 0x74-0x77 */
+ 0xA8, 0xF8, 0xA8, 0xF9, 0x00, 0x00, 0xAB, 0x6F, /* 0x78-0x7B */
+ 0xCD, 0xF5, 0x00, 0x00, 0x00, 0x00, 0xAD, 0xEB, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0xC9, 0x44, 0x00, 0x00, /* 0x80-0x83 */
+ 0xA4, 0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0xC9, 0xC4, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0xCA, 0x74, 0xCA, 0x75, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0xCB, 0xD9, 0x00, 0x00, /* 0x90-0x93 */
+ 0xCB, 0xDA, 0x00, 0x00, 0xCD, 0xF7, 0xCD, 0xF6, /* 0x94-0x97 */
+ 0xCD, 0xF9, 0xCD, 0xF8, 0xAB, 0x70, 0x00, 0x00, /* 0x98-0x9B */
+ 0xD4, 0x70, 0xAD, 0xED, 0xD0, 0xEF, 0xAD, 0xEC, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0xD8, 0x64, 0xB3, 0xD6, 0x00, 0x00, 0xD8, 0x65, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0xE1, 0x46, 0xB9, 0xBD, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0xBC, 0x46, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0xF1, 0xEF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xC9, 0x58, 0x00, 0x00, 0xA5, 0x68, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB0, 0xD1, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0xA4, 0x53, 0xA4, 0x65, 0xA4, 0xCE, 0xA4, 0xCD, /* 0xC8-0xCB */
+ 0x00, 0x00, 0xA4, 0xCF, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xA8, 0xFB, 0x00, 0x00, 0xA8, 0xFA, 0xA8, 0xFC, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAB, 0x71, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAD, 0xEE, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xE8, 0xFB, 0xC2, 0x4F, 0xA4, 0x66, /* 0xE0-0xE3 */
+ 0xA5, 0x6A, 0xA5, 0x79, 0xA5, 0x74, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xA5, 0x6F, 0xA5, 0x6E, 0xA5, 0x75, 0xA5, 0x73, /* 0xE8-0xEB */
+ 0xA5, 0x6C, 0xA5, 0x7A, 0xA5, 0x6D, 0xA5, 0x69, /* 0xEC-0xEF */
+ 0xA5, 0x78, 0xA5, 0x77, 0xA5, 0x76, 0xA5, 0x6B, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0xA5, 0x72, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0xA5, 0x71, 0x00, 0x00, 0x00, 0x00, 0xA5, 0x7B, /* 0xF8-0xFB */
+ 0xA5, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_54[512] = {
+ 0x00, 0x00, 0xA6, 0x53, 0x00, 0x00, 0xA6, 0x59, /* 0x00-0x03 */
+ 0xA6, 0x55, 0x00, 0x00, 0xA6, 0x5B, 0xC9, 0xC5, /* 0x04-0x07 */
+ 0xA6, 0x58, 0xA6, 0x4E, 0xA6, 0x51, 0xA6, 0x54, /* 0x08-0x0B */
+ 0xA6, 0x50, 0xA6, 0x57, 0xA6, 0x5A, 0xA6, 0x4F, /* 0x0C-0x0F */
+ 0xA6, 0x52, 0xA6, 0x56, 0xA6, 0x5C, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0xCA, 0x7E, 0xCA, 0x7B, 0x00, 0x00, 0xA7, 0x67, /* 0x18-0x1B */
+ 0xCA, 0x7C, 0xA7, 0x5B, 0xA7, 0x5D, 0xA7, 0x75, /* 0x1C-0x1F */
+ 0xA7, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0xCA, 0xA5, 0xCA, 0x7D, 0xA7, 0x5F, 0xA7, 0x61, /* 0x24-0x27 */
+ 0xCA, 0xA4, 0xA7, 0x68, 0xCA, 0x78, 0xA7, 0x74, /* 0x28-0x2B */
+ 0xA7, 0x76, 0xA7, 0x5C, 0xA7, 0x6D, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xCA, 0x76, 0xA7, 0x73, 0x00, 0x00, 0xA7, 0x64, /* 0x30-0x33 */
+ 0x00, 0x00, 0xA7, 0x6E, 0xA7, 0x6F, 0xCA, 0x77, /* 0x34-0x37 */
+ 0xA7, 0x6C, 0xA7, 0x6A, 0x00, 0x00, 0xA7, 0x6B, /* 0x38-0x3B */
+ 0xA7, 0x71, 0xCA, 0xA1, 0xA7, 0x5E, 0x00, 0x00, /* 0x3C-0x3F */
+ 0xA7, 0x72, 0xCA, 0xA3, 0xA7, 0x66, 0xA7, 0x63, /* 0x40-0x43 */
+ 0x00, 0x00, 0xCA, 0x7A, 0xA7, 0x62, 0xCA, 0xA6, /* 0x44-0x47 */
+ 0xA7, 0x65, 0x00, 0x00, 0xA7, 0x69, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0xA7, 0x60, 0xCA, 0xA2, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0xCA, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xCB, 0xEB, 0xCB, 0xEA, 0xA9, 0x4F, 0xCB, 0xED, /* 0x60-0x63 */
+ 0xCB, 0xEF, 0xCB, 0xE4, 0xCB, 0xE7, 0xCB, 0xEE, /* 0x64-0x67 */
+ 0xA9, 0x50, 0x00, 0x00, 0x00, 0x00, 0xCB, 0xE1, /* 0x68-0x6B */
+ 0xCB, 0xE5, 0x00, 0x00, 0x00, 0x00, 0xCB, 0xE9, /* 0x6C-0x6F */
+ 0xCE, 0x49, 0xA9, 0x4B, 0xCE, 0x4D, 0xA8, 0xFD, /* 0x70-0x73 */
+ 0xCB, 0xE6, 0xA8, 0xFE, 0xA9, 0x4C, 0xA9, 0x45, /* 0x74-0x77 */
+ 0xA9, 0x41, 0x00, 0x00, 0xCB, 0xE2, 0xA9, 0x44, /* 0x78-0x7B */
+ 0xA9, 0x49, 0xA9, 0x52, 0xCB, 0xE3, 0xCB, 0xDC, /* 0x7C-0x7F */
+
+ 0xA9, 0x43, 0xCB, 0xDD, 0xCB, 0xDF, 0x00, 0x00, /* 0x80-0x83 */
+ 0xA9, 0x46, 0x00, 0x00, 0xA9, 0x48, 0xCB, 0xDB, /* 0x84-0x87 */
+ 0xCB, 0xE0, 0x00, 0x00, 0x00, 0x00, 0xA9, 0x51, /* 0x88-0x8B */
+ 0xA9, 0x4D, 0xCB, 0xE8, 0xA9, 0x53, 0x00, 0x00, /* 0x8C-0x8F */
+ 0xA9, 0x4A, 0xCB, 0xDE, 0xA9, 0x47, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0xA9, 0x42, 0xA9, 0x40, 0x00, 0x00, /* 0x94-0x97 */
+ 0xCB, 0xEC, 0x00, 0x00, 0xA9, 0x4E, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0xCE, 0x48, 0xCD, 0xFB, 0xCE, 0x4B, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0xCD, 0xFD, 0xAB, 0x78, 0xAB, 0xA8, /* 0xA4-0xA7 */
+ 0xAB, 0x74, 0xAB, 0xA7, 0xAB, 0x7D, 0xAB, 0xA4, /* 0xA8-0xAB */
+ 0xAB, 0x72, 0xCD, 0xFC, 0xCE, 0x43, 0xAB, 0xA3, /* 0xAC-0xAF */
+ 0xCE, 0x4F, 0xAB, 0xA5, 0x00, 0x00, 0xAB, 0x79, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0xCE, 0x45, 0xCE, 0x42, /* 0xB4-0xB7 */
+ 0xAB, 0x77, 0x00, 0x00, 0xCD, 0xFA, 0xAB, 0xA6, /* 0xB8-0xBB */
+ 0xCE, 0x4A, 0xAB, 0x7C, 0xCE, 0x4C, 0xAB, 0xA9, /* 0xBC-0xBF */
+ 0xAB, 0x73, 0xAB, 0x7E, 0xAB, 0x7B, 0xCE, 0x40, /* 0xC0-0xC3 */
+ 0xAB, 0xA1, 0xCE, 0x46, 0xCE, 0x47, 0xAB, 0x7A, /* 0xC4-0xC7 */
+ 0xAB, 0xA2, 0xAB, 0x76, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0xAB, 0x75, 0xCD, 0xFE, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0xCE, 0x44, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0xCE, 0x4E, 0x00, 0x00, /* 0xDC-0xDF */
+ 0xD1, 0x44, 0xAD, 0xFB, 0xD0, 0xF1, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0xD0, 0xF6, 0xAD, 0xF4, 0xAE, 0x40, 0xD0, 0xF4, /* 0xE4-0xE7 */
+ 0xAD, 0xEF, 0xAD, 0xF9, 0xAD, 0xFE, 0xD0, 0xFB, /* 0xE8-0xEB */
+ 0x00, 0x00, 0xAD, 0xFA, 0xAD, 0xFD, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0xD0, 0xFE, 0xAD, 0xF5, 0xD0, 0xF5, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD1, 0x42, /* 0xF4-0xF7 */
+ 0xD1, 0x43, 0x00, 0x00, 0xAD, 0xF7, 0xD1, 0x41, /* 0xF8-0xFB */
+ 0xAD, 0xF3, 0xAE, 0x43, 0x00, 0x00, 0xD0, 0xF8, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_55[512] = {
+ 0x00, 0x00, 0xAD, 0xF1, 0x00, 0x00, 0xD1, 0x46, /* 0x00-0x03 */
+ 0xD0, 0xF9, 0xD0, 0xFD, 0xAD, 0xF6, 0xAE, 0x42, /* 0x04-0x07 */
+ 0xD0, 0xFA, 0xAD, 0xFC, 0xD1, 0x40, 0xD1, 0x47, /* 0x08-0x0B */
+ 0xD4, 0xA1, 0x00, 0x00, 0xD1, 0x45, 0xAE, 0x44, /* 0x0C-0x0F */
+ 0xAD, 0xF0, 0xD0, 0xFC, 0xD0, 0xF3, 0x00, 0x00, /* 0x10-0x13 */
+ 0xAD, 0xF8, 0x00, 0x00, 0x00, 0x00, 0xD0, 0xF2, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0xD0, 0xF7, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0xD0, 0xF0, 0xAE, 0x41, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xD4, 0x77, 0x00, 0x00, /* 0x28-0x2B */
+ 0xB0, 0xE4, 0xD4, 0xA7, 0xB0, 0xE2, 0xB0, 0xDF, /* 0x2C-0x2F */
+ 0xD4, 0x7C, 0xB0, 0xDB, 0xD4, 0xA2, 0xB0, 0xE6, /* 0x30-0x33 */
+ 0xD4, 0x76, 0xD4, 0x7B, 0xD4, 0x7A, 0xAD, 0xF2, /* 0x34-0x37 */
+ 0xB0, 0xE1, 0xD4, 0xA5, 0x00, 0x00, 0xD4, 0xA8, /* 0x38-0x3B */
+ 0xD4, 0x73, 0x00, 0x00, 0xB3, 0xE8, 0x00, 0x00, /* 0x3C-0x3F */
+ 0xD4, 0xA9, 0xB0, 0xE7, 0x00, 0x00, 0xB0, 0xD9, /* 0x40-0x43 */
+ 0xB0, 0xD6, 0xD4, 0x7E, 0xB0, 0xD3, 0x00, 0x00, /* 0x44-0x47 */
+ 0xD4, 0xA6, 0x00, 0x00, 0xB0, 0xDA, 0xD4, 0xAA, /* 0x48-0x4B */
+ 0x00, 0x00, 0xD4, 0x74, 0xD4, 0xA4, 0xB0, 0xDD, /* 0x4C-0x4F */
+ 0xD4, 0x75, 0xD4, 0x78, 0xD4, 0x7D, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0xB0, 0xDE, 0xB0, 0xDC, 0xB0, 0xE8, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0xB0, 0xE3, 0x00, 0x00, 0xB0, 0xD7, 0xB1, 0xD2, /* 0x5C-0x5F */
+ 0x00, 0x00, 0xB0, 0xD8, 0xD4, 0x79, 0xB0, 0xE5, /* 0x60-0x63 */
+ 0xB0, 0xE0, 0xD4, 0xA3, 0xB0, 0xD5, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0xB0, 0xD4, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0xD4, 0x71, 0xD4, 0x72, 0xD8, 0x6A, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB3, 0xD7, /* 0x78-0x7B */
+ 0xB3, 0xDA, 0xD8, 0x75, 0xB3, 0xEE, 0xD8, 0x78, /* 0x7C-0x7F */
+
+ 0xB3, 0xD8, 0xD8, 0x71, 0xB3, 0xDE, 0xB3, 0xE4, /* 0x80-0x83 */
+ 0xB5, 0xBD, 0x00, 0x00, 0x00, 0x00, 0xB3, 0xE2, /* 0x84-0x87 */
+ 0xD8, 0x6E, 0xB3, 0xEF, 0xB3, 0xDB, 0xB3, 0xE3, /* 0x88-0x8B */
+ 0xD8, 0x76, 0xDC, 0xD7, 0xD8, 0x7B, 0xD8, 0x6F, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xD8, 0x66, 0xD8, 0x73, 0xD8, 0x6D, /* 0x90-0x93 */
+ 0xB3, 0xE1, 0xD8, 0x79, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0xB3, 0xDD, 0xB3, 0xF1, 0xB3, 0xEA, 0x00, 0x00, /* 0x98-0x9B */
+ 0xB3, 0xDF, 0xB3, 0xDC, 0x00, 0x00, 0xB3, 0xE7, /* 0x9C-0x9F */
+ 0x00, 0x00, 0xD8, 0x7A, 0xD8, 0x6C, 0xD8, 0x72, /* 0xA0-0xA3 */
+ 0xD8, 0x74, 0xD8, 0x68, 0xD8, 0x77, 0xB3, 0xD9, /* 0xA4-0xA7 */
+ 0xD8, 0x67, 0x00, 0x00, 0xB3, 0xE0, 0xB3, 0xF0, /* 0xA8-0xAB */
+ 0xB3, 0xEC, 0xD8, 0x69, 0xB3, 0xE6, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0xB3, 0xED, 0xB3, 0xE9, 0xB3, 0xE5, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0xD8, 0x70, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB3, 0xEB, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0xD5, /* 0xBC-0xBF */
+ 0xDC, 0xD1, 0x00, 0x00, 0xDC, 0xE0, 0xDC, 0xCA, /* 0xC0-0xC3 */
+ 0xDC, 0xD3, 0xB6, 0xE5, 0xB6, 0xE6, 0xB6, 0xDE, /* 0xC4-0xC7 */
+ 0xDC, 0xDC, 0xB6, 0xE8, 0xDC, 0xCF, 0xDC, 0xCE, /* 0xC8-0xCB */
+ 0xDC, 0xCC, 0xDC, 0xDE, 0xB6, 0xDC, 0xDC, 0xD8, /* 0xCC-0xCF */
+ 0xDC, 0xCD, 0xB6, 0xDF, 0xDC, 0xD6, 0xB6, 0xDA, /* 0xD0-0xD3 */
+ 0xDC, 0xD2, 0xDC, 0xD9, 0xDC, 0xDB, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0xDC, 0xDF, 0xB6, 0xE3, 0xDC, 0xCB, /* 0xD8-0xDB */
+ 0xB6, 0xDD, 0xDC, 0xD0, 0x00, 0x00, 0xB6, 0xD8, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xB6, 0xE4, 0xDC, 0xDA, 0xB6, 0xE0, /* 0xE0-0xE3 */
+ 0xB6, 0xE1, 0xB6, 0xE7, 0xB6, 0xDB, 0xA2, 0x5F, /* 0xE4-0xE7 */
+ 0xB6, 0xD9, 0xDC, 0xD4, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB6, 0xE2, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0xDC, 0xDD, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0xB9, 0xCD, 0xB9, 0xC8, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xE1, 0x55, 0xE1, 0x51, 0x00, 0x00, /* 0xF8-0xFB */
+ 0xE1, 0x4B, 0xB9, 0xC2, 0xB9, 0xBE, 0xE1, 0x54, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_56[512] = {
+ 0xB9, 0xBF, 0xE1, 0x4E, 0xE1, 0x50, 0x00, 0x00, /* 0x00-0x03 */
+ 0xE1, 0x53, 0x00, 0x00, 0xB9, 0xC4, 0x00, 0x00, /* 0x04-0x07 */
+ 0xB9, 0xCB, 0xB9, 0xC5, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0xE1, 0x49, 0xB9, 0xC6, 0xB9, 0xC7, 0xE1, 0x4C, /* 0x0C-0x0F */
+ 0xB9, 0xCC, 0x00, 0x00, 0xE1, 0x4A, 0xE1, 0x4F, /* 0x10-0x13 */
+ 0xB9, 0xC3, 0xE1, 0x48, 0xB9, 0xC9, 0xB9, 0xC1, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB9, 0xC0, /* 0x18-0x1B */
+ 0xE1, 0x4D, 0xE1, 0x52, 0x00, 0x00, 0xB9, 0xCA, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE1, 0x47, /* 0x24-0x27 */
+ 0x00, 0x00, 0xBC, 0x4D, 0xE5, 0x47, 0x00, 0x00, /* 0x28-0x2B */
+ 0xE5, 0x44, 0x00, 0x00, 0xBC, 0x47, 0xBC, 0x53, /* 0x2C-0x2F */
+ 0xBC, 0x54, 0x00, 0x00, 0xBC, 0x4A, 0xE5, 0x42, /* 0x30-0x33 */
+ 0xBC, 0x4C, 0xE4, 0xF9, 0xBC, 0x52, 0x00, 0x00, /* 0x34-0x37 */
+ 0xE5, 0x46, 0xBC, 0x49, 0xE5, 0x48, 0xBC, 0x48, /* 0x38-0x3B */
+ 0x00, 0x00, 0xE5, 0x43, 0xE5, 0x45, 0xBC, 0x4B, /* 0x3C-0x3F */
+ 0xE5, 0x41, 0xE4, 0xFA, 0xE4, 0xF7, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0xD8, 0x6B, 0xE4, 0xFD, 0x00, 0x00, /* 0x44-0x47 */
+ 0xE4, 0xF6, 0xE4, 0xFC, 0xE4, 0xFB, 0x00, 0x00, /* 0x48-0x4B */
+ 0xE4, 0xF8, 0x00, 0x00, 0xBC, 0x4F, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBC, 0x4E, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBC, 0x50, /* 0x54-0x57 */
+ 0xE4, 0xFE, 0xBE, 0xB2, 0xE5, 0x40, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0x45, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xE8, 0xFD, 0x00, 0x00, 0xBE, 0xBE, 0xE9, 0x42, /* 0x60-0x63 */
+ 0xBE, 0xB6, 0xBE, 0xBA, 0xE9, 0x41, 0x00, 0x00, /* 0x64-0x67 */
+ 0xBE, 0xB9, 0xBE, 0xB5, 0xBE, 0xB8, 0xBE, 0xB3, /* 0x68-0x6B */
+ 0xBE, 0xBD, 0xE9, 0x43, 0xE8, 0xFE, 0xBE, 0xBC, /* 0x6C-0x6F */
+ 0xE8, 0xFC, 0xBE, 0xBB, 0xE9, 0x44, 0xE9, 0x40, /* 0x70-0x73 */
+ 0xBC, 0x51, 0x00, 0x00, 0xBE, 0xBF, 0xE9, 0x46, /* 0x74-0x77 */
+ 0xBE, 0xB7, 0xBE, 0xB4, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0xEC, 0xC6, 0xEC, 0xC8, /* 0x7C-0x7F */
+
+ 0xC0, 0x7B, 0xEC, 0xC9, 0xEC, 0xC7, 0xEC, 0xC5, /* 0x80-0x83 */
+ 0xEC, 0xC4, 0xC0, 0x7D, 0xEC, 0xC3, 0xC0, 0x7E, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0xEC, 0xC1, 0xEC, 0xC2, 0xC0, 0x7A, 0xC0, 0xA1, /* 0x8C-0x8F */
+ 0xC0, 0x7C, 0x00, 0x00, 0x00, 0x00, 0xEC, 0xC0, /* 0x90-0x93 */
+ 0x00, 0x00, 0xC2, 0x50, 0x00, 0x00, 0xEF, 0xBC, /* 0x94-0x97 */
+ 0xEF, 0xBA, 0xEF, 0xBF, 0xEF, 0xBD, 0x00, 0x00, /* 0x98-0x9B */
+ 0xEF, 0xBB, 0xEF, 0xBE, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0xC3, 0x60, 0xF1, 0xF2, 0xF1, 0xF3, /* 0xA4-0xA7 */
+ 0xC4, 0x56, 0x00, 0x00, 0xF1, 0xF4, 0xF1, 0xF0, /* 0xA8-0xAB */
+ 0xF1, 0xF5, 0xF1, 0xF1, 0xC2, 0x51, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0xF3, 0xFE, 0xF4, 0x41, /* 0xB0-0xB3 */
+ 0xC4, 0x59, 0xF4, 0x40, 0xC4, 0x58, 0xC4, 0x57, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0xC4, 0x5A, 0xF5, 0xC5, 0xF5, 0xC6, 0x00, 0x00, /* 0xBC-0xBF */
+ 0xC4, 0xDA, 0xC4, 0xD9, 0xC4, 0xDB, 0xF5, 0xC4, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0xF6, 0xD8, 0xF6, 0xD7, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0xC5, 0x6D, 0xC5, 0x6F, 0xC5, 0x6E, 0xF6, 0xD9, /* 0xC8-0xCB */
+ 0xC5, 0xC8, 0xF8, 0xA6, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0xC5, 0xF1, 0x00, 0x00, 0xF8, 0xA5, /* 0xD0-0xD3 */
+ 0xF8, 0xEE, 0x00, 0x00, 0x00, 0x00, 0xC9, 0x49, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0xA5, 0x7D, 0xA5, 0x7C, /* 0xD8-0xDB */
+ 0x00, 0x00, 0xA6, 0x5F, 0xA6, 0x5E, 0xC9, 0xC7, /* 0xDC-0xDF */
+ 0xA6, 0x5D, 0xC9, 0xC6, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0xA7, 0x79, 0xCA, 0xA9, 0x00, 0x00, 0xCA, 0xA8, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0xA7, 0x77, 0xA7, 0x7A, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0xCA, 0xA7, 0x00, 0x00, /* 0xEC-0xEF */
+ 0xA7, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCB, 0xF0, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xCB, 0xF1, 0xA9, 0x54, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAB, 0xAA, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_57[512] = {
+ 0x00, 0x00, 0xD1, 0x48, 0xD1, 0x49, 0xAE, 0x45, /* 0x00-0x03 */
+ 0xAE, 0x46, 0x00, 0x00, 0x00, 0x00, 0xD4, 0xAC, /* 0x04-0x07 */
+ 0xB0, 0xE9, 0xB0, 0xEB, 0xD4, 0xAB, 0xB0, 0xEA, /* 0x08-0x0B */
+ 0xD8, 0x7C, 0xB3, 0xF2, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0xB6, 0xE9, 0xB6, 0xEA, /* 0x10-0x13 */
+ 0xDC, 0xE1, 0x00, 0x00, 0xB9, 0xCF, 0x00, 0x00, /* 0x14-0x17 */
+ 0xB9, 0xCE, 0x00, 0x00, 0xE5, 0x49, 0xE9, 0x48, /* 0x18-0x1B */
+ 0xE9, 0x47, 0x00, 0x00, 0xF9, 0x6B, 0xA4, 0x67, /* 0x1C-0x1F */
+ 0xC9, 0x59, 0x00, 0x00, 0xC9, 0x6E, 0xC9, 0x6F, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0xA6, 0x62, 0xA6, 0x66, 0xC9, 0xC9, 0x00, 0x00, /* 0x28-0x2B */
+ 0xA6, 0x64, 0xA6, 0x63, 0xC9, 0xC8, 0xA6, 0x65, /* 0x2C-0x2F */
+ 0xA6, 0x61, 0x00, 0x00, 0x00, 0x00, 0xA6, 0x60, /* 0x30-0x33 */
+ 0xC9, 0xCA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA7, 0xA6, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0xA7, 0xA3, 0x00, 0x00, /* 0x3C-0x3F */
+ 0xA7, 0x7D, 0xCA, 0xAA, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0xCA, 0xAB, 0x00, 0x00, 0xA7, 0xA1, /* 0x44-0x47 */
+ 0x00, 0x00, 0xCA, 0xAD, 0xA7, 0x7B, 0xCA, 0xAE, /* 0x48-0x4B */
+ 0xCA, 0xAC, 0xA7, 0x7E, 0xA7, 0xA2, 0xA7, 0xA5, /* 0x4C-0x4F */
+ 0xA7, 0xA4, 0xA7, 0x7C, 0xCA, 0xAF, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0xA9, 0x59, 0xCB, 0xFE, 0x00, 0x00, /* 0x60-0x63 */
+ 0xA9, 0x5B, 0x00, 0x00, 0xA9, 0x5A, 0x00, 0x00, /* 0x64-0x67 */
+ 0xCC, 0x40, 0xA9, 0x58, 0xA9, 0x57, 0xCB, 0xF5, /* 0x68-0x6B */
+ 0x00, 0x00, 0xCB, 0xF4, 0x00, 0x00, 0xCB, 0xF2, /* 0x6C-0x6F */
+ 0xCB, 0xF7, 0xCB, 0xF6, 0xCB, 0xF3, 0xCB, 0xFC, /* 0x70-0x73 */
+ 0xCB, 0xFD, 0xCB, 0xFA, 0xCB, 0xF8, 0xA9, 0x56, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCB, 0xFB, /* 0x78-0x7B */
+ 0xA9, 0x5C, 0xCC, 0x41, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0xCB, 0xF9, 0x00, 0x00, 0xAB, 0xAB, 0xA9, 0x55, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAB, 0xAC, /* 0x88-0x8B */
+ 0xCE, 0x54, 0x00, 0x00, 0x00, 0x00, 0xCE, 0x5A, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAB, 0xB2, /* 0x90-0x93 */
+ 0xCE, 0x58, 0xCE, 0x5E, 0x00, 0x00, 0xCE, 0x55, /* 0x94-0x97 */
+ 0xCE, 0x59, 0xCE, 0x5B, 0xCE, 0x5D, 0xCE, 0x57, /* 0x98-0x9B */
+ 0x00, 0x00, 0xCE, 0x56, 0xCE, 0x51, 0xCE, 0x52, /* 0x9C-0x9F */
+ 0xAB, 0xAD, 0x00, 0x00, 0xAB, 0xAF, 0xAB, 0xAE, /* 0xA0-0xA3 */
+ 0xCE, 0x53, 0xCE, 0x5C, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0xAB, 0xB1, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0xCE, 0x50, 0xD1, 0x53, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0xD1, 0x52, 0xD1, 0x57, 0xD1, 0x4E, 0x00, 0x00, /* 0xB8-0xBB */
+ 0xD1, 0x51, 0xD1, 0x50, 0x00, 0x00, 0xD1, 0x54, /* 0xBC-0xBF */
+ 0x00, 0x00, 0xD1, 0x58, 0xAE, 0x47, 0xAE, 0x4A, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0xD1, 0x4F, 0xD1, 0x55, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAE, 0x49, /* 0xC8-0xCB */
+ 0xD1, 0x4A, 0x00, 0x00, 0xAB, 0xB0, 0xD4, 0xBA, /* 0xCC-0xCF */
+ 0xD1, 0x56, 0x00, 0x00, 0xD1, 0x4D, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xAE, 0x48, 0xD1, 0x4C, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0xD4, 0xB1, 0x00, 0x00, 0x00, 0x00, 0xB0, 0xEC, /* 0xDC-0xDF */
+ 0xB0, 0xF0, 0xD4, 0xC1, 0xD4, 0xAF, 0xD4, 0xBD, /* 0xE0-0xE3 */
+ 0xB0, 0xF1, 0xD4, 0xBF, 0x00, 0x00, 0xD4, 0xC5, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0xD4, 0xC9, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0xD4, 0xC0, 0xD4, 0xB4, 0xD4, 0xBC, 0x00, 0x00, /* 0xEC-0xEF */
+ 0xD4, 0xCA, 0xD4, 0xC8, 0xD4, 0xBE, 0xD4, 0xB9, /* 0xF0-0xF3 */
+ 0xD4, 0xB2, 0xD8, 0xA6, 0xD4, 0xB0, 0xB0, 0xF5, /* 0xF4-0xF7 */
+ 0xD4, 0xB7, 0xB0, 0xF6, 0xB0, 0xF2, 0xD4, 0xAD, /* 0xF8-0xFB */
+ 0xD4, 0xC3, 0xD4, 0xB5, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_58[512] = {
+ 0xD4, 0xB3, 0xD4, 0xC6, 0xB0, 0xF3, 0x00, 0x00, /* 0x00-0x03 */
+ 0xD4, 0xCC, 0xB0, 0xED, 0xB0, 0xEF, 0xD4, 0xBB, /* 0x04-0x07 */
+ 0xD4, 0xB6, 0xAE, 0x4B, 0xB0, 0xEE, 0xD4, 0xB8, /* 0x08-0x0B */
+ 0xD4, 0xC7, 0xD4, 0xCB, 0xD4, 0xC2, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xD4, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0xD4, 0xAE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0xD8, 0xA1, 0x00, 0x00, 0xD8, 0xAA, /* 0x18-0x1B */
+ 0xD8, 0xA9, 0xB3, 0xFA, 0xD8, 0xA2, 0x00, 0x00, /* 0x1C-0x1F */
+ 0xB3, 0xFB, 0xB3, 0xF9, 0x00, 0x00, 0xD8, 0xA4, /* 0x20-0x23 */
+ 0xB3, 0xF6, 0xD8, 0xA8, 0x00, 0x00, 0xD8, 0xA3, /* 0x24-0x27 */
+ 0xD8, 0xA5, 0xD8, 0x7D, 0xB3, 0xF4, 0x00, 0x00, /* 0x28-0x2B */
+ 0xD8, 0xB2, 0xD8, 0xB1, 0xD8, 0xAE, 0xB3, 0xF3, /* 0x2C-0x2F */
+ 0xB3, 0xF7, 0xB3, 0xF8, 0xD1, 0x4B, 0xD8, 0xAB, /* 0x30-0x33 */
+ 0xB3, 0xF5, 0xB0, 0xF4, 0xD8, 0xAD, 0xD8, 0x7E, /* 0x34-0x37 */
+ 0xD8, 0xB0, 0xD8, 0xAF, 0x00, 0x00, 0xD8, 0xB3, /* 0x38-0x3B */
+ 0x00, 0x00, 0xDC, 0xEF, 0x00, 0x00, 0xD8, 0xAC, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0xD8, 0xA7, 0xDC, 0xE7, 0xB6, 0xF4, 0xB6, 0xF7, /* 0x48-0x4B */
+ 0xB6, 0xF2, 0xDC, 0xE6, 0xDC, 0xEA, 0xDC, 0xE5, /* 0x4C-0x4F */
+ 0x00, 0x00, 0xB6, 0xEC, 0xB6, 0xF6, 0xDC, 0xE2, /* 0x50-0x53 */
+ 0xB6, 0xF0, 0xDC, 0xE9, 0x00, 0x00, 0xB6, 0xEE, /* 0x54-0x57 */
+ 0xB6, 0xED, 0xDC, 0xEC, 0xB6, 0xEF, 0xDC, 0xEE, /* 0x58-0x5B */
+ 0x00, 0x00, 0xDC, 0xEB, 0xB6, 0xEB, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0xB6, 0xF5, 0xDC, 0xF0, /* 0x60-0x63 */
+ 0xDC, 0xE4, 0xDC, 0xED, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0xDC, 0xE3, 0x00, 0x00, 0x00, 0x00, 0xB6, 0xF1, /* 0x68-0x6B */
+ 0x00, 0x00, 0xB6, 0xF3, 0x00, 0x00, 0xDC, 0xE8, /* 0x6C-0x6F */
+ 0x00, 0x00, 0xDC, 0xF1, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0xE1, 0x5D, 0xB9, 0xD0, 0xE1, 0x63, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0xB9, 0xD5, 0xE1, 0x5F, 0xE1, 0x66, /* 0x78-0x7B */
+ 0xE1, 0x57, 0xB9, 0xD7, 0xB9, 0xD1, 0xE1, 0x5C, /* 0x7C-0x7F */
+
+ 0xBC, 0x55, 0xE1, 0x5B, 0xE1, 0x64, 0xB9, 0xD2, /* 0x80-0x83 */
+ 0x00, 0x00, 0xB9, 0xD6, 0xE1, 0x5A, 0xE1, 0x60, /* 0x84-0x87 */
+ 0xE1, 0x65, 0xE1, 0x56, 0xB9, 0xD4, 0xE1, 0x5E, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0x62, 0xE1, 0x68, /* 0x8C-0x8F */
+ 0xE1, 0x58, 0xE1, 0x61, 0x00, 0x00, 0xB9, 0xD3, /* 0x90-0x93 */
+ 0xE1, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0xE1, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0xBC, 0x59, 0xE5, 0x4B, 0xBC, 0x57, 0xBC, 0x56, /* 0x9C-0x9F */
+ 0xE5, 0x4D, 0xE5, 0x52, 0x00, 0x00, 0xE5, 0x4E, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0xE5, 0x51, 0xBC, 0x5C, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xBE, 0xA5, 0xBC, 0x5B, 0x00, 0x00, 0xE5, 0x4A, /* 0xA8-0xAB */
+ 0xE5, 0x50, 0x00, 0x00, 0xBC, 0x5A, 0xE5, 0x4F, /* 0xAC-0xAF */
+ 0x00, 0x00, 0xE5, 0x4C, 0x00, 0x00, 0xBC, 0x58, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0x4D, 0xF9, 0xD9, /* 0xB8-0xBB */
+ 0xE9, 0x4F, 0xE9, 0x4A, 0xBE, 0xC1, 0xE9, 0x4C, /* 0xBC-0xBF */
+ 0x00, 0x00, 0xBE, 0xC0, 0xE9, 0x4E, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0xBE, 0xC3, 0xE9, 0x50, 0xBE, 0xC2, /* 0xC4-0xC7 */
+ 0xE9, 0x49, 0xE9, 0x4B, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0xC0, 0xA5, 0xEC, 0xCC, /* 0xCC-0xCF */
+ 0x00, 0x00, 0xC0, 0xA4, 0xEC, 0xCD, 0xC0, 0xA3, /* 0xD0-0xD3 */
+ 0xEC, 0xCB, 0xC0, 0xA2, 0xEC, 0xCA, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xC2, 0x53, 0xC2, 0x52, 0xF1, 0xF6, 0xF1, 0xF8, /* 0xD8-0xDB */
+ 0x00, 0x00, 0xF1, 0xF7, 0xC3, 0x61, 0xC3, 0x62, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0xC3, 0x63, 0xF4, 0x42, /* 0xE0-0xE3 */
+ 0xC4, 0x5B, 0x00, 0x00, 0x00, 0x00, 0xF7, 0xD3, /* 0xE4-0xE7 */
+ 0xF7, 0xD2, 0xC5, 0xF2, 0x00, 0x00, 0xA4, 0x68, /* 0xE8-0xEB */
+ 0xA4, 0xD0, 0x00, 0x00, 0x00, 0x00, 0xA7, 0xA7, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xCE, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xB3, 0xFC, 0xB3, 0xFD, 0x00, 0x00, /* 0xF8-0xFB */
+ 0xDC, 0xF2, 0xB9, 0xD8, 0xE1, 0x69, 0xE5, 0x53, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_59[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC9, 0x5A, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0xCA, 0xB0, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0xCC, 0x42, 0xCE, 0x60, 0xD1, 0x59, 0xAE, 0x4C, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0xF1, 0xF9, 0x00, 0x00, /* 0x10-0x13 */
+ 0xC4, 0xDC, 0xA4, 0x69, 0xA5, 0x7E, 0xC9, 0x70, /* 0x14-0x17 */
+ 0x00, 0x00, 0xA6, 0x67, 0xA6, 0x68, 0x00, 0x00, /* 0x18-0x1B */
+ 0xA9, 0x5D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0xB0, 0xF7, 0x00, 0x00, 0xB9, 0xDA, 0x00, 0x00, /* 0x20-0x23 */
+ 0xB9, 0xDB, 0xB9, 0xD9, 0x00, 0x00, 0xA4, 0x6A, /* 0x24-0x27 */
+ 0x00, 0x00, 0xA4, 0xD1, 0xA4, 0xD3, 0xA4, 0xD2, /* 0x28-0x2B */
+ 0xC9, 0x5B, 0xA4, 0xD4, 0xA5, 0xA1, 0xC9, 0x71, /* 0x2C-0x2F */
+ 0x00, 0x00, 0xA5, 0xA2, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA6, 0x69, /* 0x34-0x37 */
+ 0xA6, 0x6A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0xC9, 0xCB, 0x00, 0x00, 0xA7, 0xA8, 0x00, 0x00, /* 0x3C-0x3F */
+ 0xCA, 0xB1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0xA9, 0x61, 0xCC, 0x43, 0x00, 0x00, 0xA9, 0x5F, /* 0x44-0x47 */
+ 0xA9, 0x60, 0xA9, 0x5E, 0xD1, 0x5A, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0xAB, 0xB6, 0xAB, 0xB5, /* 0x4C-0x4F */
+ 0xAB, 0xB7, 0xAB, 0xB4, 0x00, 0x00, 0xCE, 0x61, /* 0x50-0x53 */
+ 0xA9, 0x62, 0xAB, 0xB3, 0x00, 0x00, 0xAE, 0x4D, /* 0x54-0x57 */
+ 0xAE, 0x4E, 0x00, 0x00, 0xAE, 0x4F, 0x00, 0x00, /* 0x58-0x5B */
+ 0xD4, 0xCD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xB3, 0xFE, 0xD8, 0xB4, 0xB0, 0xF8, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB6, 0xF8, /* 0x64-0x67 */
+ 0x00, 0x00, 0xB9, 0xDD, 0xB9, 0xDC, 0xE1, 0x6A, /* 0x68-0x6B */
+ 0x00, 0x00, 0xBC, 0x5D, 0xBE, 0xC4, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xEF, 0xC0, 0xF6, 0xDA, 0xF7, 0xD4, 0xA4, 0x6B, /* 0x70-0x73 */
+ 0xA5, 0xA3, 0x00, 0x00, 0xA5, 0xA4, 0xC9, 0xD1, /* 0x74-0x77 */
+ 0xA6, 0x6C, 0xA6, 0x6F, 0x00, 0x00, 0xC9, 0xCF, /* 0x78-0x7B */
+ 0xC9, 0xCD, 0xA6, 0x6E, 0xC9, 0xD0, 0xC9, 0xD2, /* 0x7C-0x7F */
+
+ 0xC9, 0xCC, 0xA6, 0x71, 0xA6, 0x70, 0xA6, 0x6D, /* 0x80-0x83 */
+ 0xA6, 0x6B, 0xC9, 0xCE, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0xA7, 0xB3, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0xA7, 0xB0, 0xCA, 0xB6, 0xCA, 0xB9, /* 0x8C-0x8F */
+ 0xCA, 0xB8, 0x00, 0x00, 0xA7, 0xAA, 0xA7, 0xB2, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0xA7, 0xAF, 0xCA, 0xB5, /* 0x94-0x97 */
+ 0xCA, 0xB3, 0xA7, 0xAE, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0xA7, 0xA9, 0xA7, 0xAC, 0x00, 0x00, /* 0x9C-0x9F */
+ 0xCA, 0xB4, 0xCA, 0xBB, 0xCA, 0xB7, 0xA7, 0xAD, /* 0xA0-0xA3 */
+ 0xA7, 0xB1, 0xA7, 0xB4, 0xCA, 0xB2, 0xCA, 0xBA, /* 0xA4-0xA7 */
+ 0xA7, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0xA9, 0x67, 0xA9, 0x6F, /* 0xAC-0xAF */
+ 0x00, 0x00, 0xCC, 0x4F, 0xCC, 0x48, 0xA9, 0x70, /* 0xB0-0xB3 */
+ 0xCC, 0x53, 0xCC, 0x44, 0xCC, 0x4B, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xA9, 0x66, 0xCC, 0x45, 0xA9, 0x64, /* 0xB8-0xBB */
+ 0xCC, 0x4C, 0xCC, 0x50, 0xA9, 0x63, 0x00, 0x00, /* 0xBC-0xBF */
+ 0xCC, 0x51, 0xCC, 0x4A, 0x00, 0x00, 0xCC, 0x4D, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0xA9, 0x72, 0xA9, 0x69, 0xCC, 0x54, /* 0xC4-0xC7 */
+ 0xCC, 0x52, 0x00, 0x00, 0xA9, 0x6E, 0xA9, 0x6C, /* 0xC8-0xCB */
+ 0xCC, 0x49, 0xA9, 0x6B, 0xCC, 0x47, 0xCC, 0x46, /* 0xCC-0xCF */
+ 0xA9, 0x6A, 0xA9, 0x68, 0xA9, 0x71, 0xA9, 0x6D, /* 0xD0-0xD3 */
+ 0xA9, 0x65, 0x00, 0x00, 0xCC, 0x4E, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xAB, 0xB9, 0x00, 0x00, 0xAB, 0xC0, 0xCE, 0x6F, /* 0xD8-0xDB */
+ 0xAB, 0xB8, 0xCE, 0x67, 0xCE, 0x63, 0x00, 0x00, /* 0xDC-0xDF */
+ 0xCE, 0x73, 0xCE, 0x62, 0x00, 0x00, 0xAB, 0xBB, /* 0xE0-0xE3 */
+ 0xCE, 0x6C, 0xAB, 0xBE, 0xAB, 0xC1, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xAB, 0xBC, 0xCE, 0x70, 0xAB, 0xBF, 0x00, 0x00, /* 0xE8-0xEB */
+ 0xAE, 0x56, 0xCE, 0x76, 0xCE, 0x64, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0xCE, 0x66, 0xCE, 0x6D, 0xCE, 0x71, /* 0xF0-0xF3 */
+ 0xCE, 0x75, 0xCE, 0x72, 0xCE, 0x6B, 0xCE, 0x6E, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0xCE, 0x68, 0xAB, 0xC3, /* 0xF8-0xFB */
+ 0xCE, 0x6A, 0xCE, 0x69, 0xCE, 0x74, 0xAB, 0xBA, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_5A[512] = {
+ 0xCE, 0x65, 0xAB, 0xC2, 0x00, 0x00, 0xAB, 0xBD, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0xAE, 0x5C, 0xD1, 0x62, 0x00, 0x00, /* 0x08-0x0B */
+ 0xAE, 0x5B, 0x00, 0x00, 0x00, 0x00, 0xD1, 0x60, /* 0x0C-0x0F */
+ 0x00, 0x00, 0xAE, 0x50, 0x00, 0x00, 0xAE, 0x55, /* 0x10-0x13 */
+ 0x00, 0x00, 0xD1, 0x5F, 0xD1, 0x5C, 0xD1, 0x61, /* 0x14-0x17 */
+ 0xAE, 0x51, 0xD1, 0x5B, 0x00, 0x00, 0xAE, 0x54, /* 0x18-0x1B */
+ 0xAE, 0x52, 0x00, 0x00, 0xD1, 0x63, 0xAE, 0x53, /* 0x1C-0x1F */
+ 0xAE, 0x57, 0x00, 0x00, 0x00, 0x00, 0xAE, 0x58, /* 0x20-0x23 */
+ 0x00, 0x00, 0xAE, 0x5A, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0xAE, 0x59, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0xD1, 0x5D, 0xD1, 0x5E, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD1, 0x64, /* 0x30-0x33 */
+ 0x00, 0x00, 0xD4, 0xD4, 0xB0, 0xF9, 0xD8, 0xC2, /* 0x34-0x37 */
+ 0xD4, 0xD3, 0xD4, 0xE6, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0xB1, 0x40, 0x00, 0x00, 0xD4, 0xE4, 0x00, 0x00, /* 0x3C-0x3F */
+ 0xB0, 0xFE, 0xB0, 0xFA, 0xD4, 0xED, 0xD4, 0xDD, /* 0x40-0x43 */
+ 0xD4, 0xE0, 0x00, 0x00, 0xB1, 0x43, 0xD4, 0xEA, /* 0x44-0x47 */
+ 0xD4, 0xE2, 0xB0, 0xFB, 0xB1, 0x44, 0x00, 0x00, /* 0x48-0x4B */
+ 0xD4, 0xE7, 0xD4, 0xE5, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xD4, 0xD6, 0xD4, 0xEB, 0xD4, 0xDF, 0xD4, 0xDA, /* 0x50-0x53 */
+ 0x00, 0x00, 0xD4, 0xD0, 0xD4, 0xEC, 0xD4, 0xDC, /* 0x54-0x57 */
+ 0xD4, 0xCF, 0x00, 0x00, 0xB1, 0x42, 0xD4, 0xE1, /* 0x58-0x5B */
+ 0xD4, 0xEE, 0xD4, 0xDE, 0xD4, 0xD2, 0xD4, 0xD7, /* 0x5C-0x5F */
+ 0xD4, 0xCE, 0x00, 0x00, 0xB1, 0x41, 0x00, 0x00, /* 0x60-0x63 */
+ 0xD4, 0xDB, 0xD4, 0xD8, 0xB0, 0xFC, 0xD4, 0xD1, /* 0x64-0x67 */
+ 0x00, 0x00, 0xD4, 0xE9, 0xB0, 0xFD, 0x00, 0x00, /* 0x68-0x6B */
+ 0xD4, 0xD9, 0xD4, 0xD5, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xD4, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB4, 0x40, /* 0x74-0x77 */
+ 0xD8, 0xBB, 0x00, 0x00, 0xD8, 0xB8, 0xD8, 0xC9, /* 0x78-0x7B */
+ 0xD8, 0xBD, 0xD8, 0xCA, 0x00, 0x00, 0xB4, 0x42, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD8, 0xC6, /* 0x80-0x83 */
+ 0xD8, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0xD8, 0xC4, 0xD8, 0xC7, /* 0x88-0x8B */
+ 0xD8, 0xCB, 0x00, 0x00, 0xD4, 0xE3, 0xD8, 0xCD, /* 0x8C-0x8F */
+ 0xDD, 0x47, 0x00, 0x00, 0xB4, 0x43, 0xD8, 0xCE, /* 0x90-0x93 */
+ 0xD8, 0xB6, 0xD8, 0xC0, 0x00, 0x00, 0xD8, 0xC5, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0xB4, 0x41, 0xB4, 0x44, /* 0x98-0x9B */
+ 0xD8, 0xCC, 0xD8, 0xCF, 0xD8, 0xBA, 0xD8, 0xB7, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0xD8, 0xB9, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0xD8, 0xBE, 0xD8, 0xBC, 0xB4, 0x45, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0xD8, 0xC8, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0xD8, 0xBF, 0x00, 0x00, 0xD8, 0xC1, 0xD8, 0xB5, /* 0xAC-0xAF */
+ 0xDC, 0xFA, 0xDC, 0xF8, 0xB7, 0x42, 0xB7, 0x40, /* 0xB0-0xB3 */
+ 0xDD, 0x43, 0xDC, 0xF9, 0xDD, 0x44, 0xDD, 0x40, /* 0xB4-0xB7 */
+ 0xDC, 0xF7, 0xDD, 0x46, 0xDC, 0xF6, 0xDC, 0xFD, /* 0xB8-0xBB */
+ 0xB6, 0xFE, 0xB6, 0xFD, 0xB6, 0xFC, 0xDC, 0xFB, /* 0xBC-0xBF */
+ 0xDD, 0x41, 0xB6, 0xF9, 0xB7, 0x41, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xDC, 0xF4, 0x00, 0x00, 0xDC, 0xFE, 0xDC, 0xF3, /* 0xC4-0xC7 */
+ 0xDC, 0xFC, 0xB6, 0xFA, 0xDD, 0x42, 0xDC, 0xF5, /* 0xC8-0xCB */
+ 0xB6, 0xFB, 0xDD, 0x45, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0xE1, 0x6E, 0xB9, 0xE2, 0xB9, 0xE1, /* 0xD4-0xD7 */
+ 0xB9, 0xE3, 0xE1, 0x7A, 0xE1, 0x70, 0xE1, 0x76, /* 0xD8-0xDB */
+ 0xE1, 0x6B, 0xE1, 0x79, 0xE1, 0x78, 0xE1, 0x7C, /* 0xDC-0xDF */
+ 0xE1, 0x75, 0xB9, 0xDE, 0xE1, 0x74, 0xB9, 0xE4, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0xE1, 0x6D, 0xB9, 0xDF, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xE1, 0x7B, 0xB9, 0xE0, 0xE1, 0x6F, 0xE1, 0x72, /* 0xE8-0xEB */
+ 0xE1, 0x77, 0xE1, 0x71, 0xE1, 0x6C, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE1, 0x73, /* 0xF0-0xF3 */
+ 0xE5, 0x55, 0xBC, 0x61, 0xE5, 0x58, 0xE5, 0x57, /* 0xF4-0xF7 */
+ 0xE5, 0x5A, 0xE5, 0x5C, 0xF9, 0xDC, 0xBC, 0x5F, /* 0xF8-0xFB */
+ 0x00, 0x00, 0xE5, 0x56, 0x00, 0x00, 0xE5, 0x54, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_5B[512] = {
+ 0x00, 0x00, 0xE5, 0x5D, 0xE5, 0x5B, 0xE5, 0x59, /* 0x00-0x03 */
+ 0x00, 0x00, 0xE5, 0x5F, 0x00, 0x00, 0xE5, 0x5E, /* 0x04-0x07 */
+ 0xBC, 0x63, 0xBC, 0x5E, 0x00, 0x00, 0xBC, 0x60, /* 0x08-0x0B */
+ 0xBC, 0x62, 0x00, 0x00, 0x00, 0x00, 0xE5, 0x60, /* 0x0C-0x0F */
+ 0xE9, 0x57, 0x00, 0x00, 0x00, 0x00, 0xE9, 0x56, /* 0x10-0x13 */
+ 0xE9, 0x55, 0x00, 0x00, 0xE9, 0x58, 0xE9, 0x51, /* 0x14-0x17 */
+ 0x00, 0x00, 0xE9, 0x52, 0xE9, 0x5A, 0xE9, 0x53, /* 0x18-0x1B */
+ 0x00, 0x00, 0xBE, 0xC5, 0xE9, 0x5C, 0x00, 0x00, /* 0x1C-0x1F */
+ 0xE9, 0x5B, 0xE9, 0x54, 0x00, 0x00, 0xEC, 0xD1, /* 0x20-0x23 */
+ 0xC0, 0xA8, 0xEC, 0xCF, 0xEC, 0xD4, 0xEC, 0xD3, /* 0x24-0x27 */
+ 0xE9, 0x59, 0x00, 0x00, 0xC0, 0xA7, 0x00, 0x00, /* 0x28-0x2B */
+ 0xEC, 0xD2, 0xEC, 0xCE, 0xEC, 0xD6, 0xEC, 0xD5, /* 0x2C-0x2F */
+ 0xC0, 0xA6, 0x00, 0x00, 0xEC, 0xD0, 0x00, 0x00, /* 0x30-0x33 */
+ 0xBE, 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0xC2, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0xEF, 0xC1, 0xF1, 0xFA, 0xF1, 0xFB, 0xF1, 0xFC, /* 0x3C-0x3F */
+ 0xC4, 0x5C, 0x00, 0x00, 0x00, 0x00, 0xC4, 0x5D, /* 0x40-0x43 */
+ 0x00, 0x00, 0xF4, 0x43, 0x00, 0x00, 0xF5, 0xC8, /* 0x44-0x47 */
+ 0xF5, 0xC7, 0x00, 0x00, 0x00, 0x00, 0xF6, 0xDB, /* 0x48-0x4B */
+ 0xF6, 0xDC, 0xF7, 0xD5, 0xF8, 0xA7, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xA4, 0x6C, 0xA4, 0x6D, 0x00, 0x00, 0xA4, 0x6E, /* 0x50-0x53 */
+ 0xA4, 0xD5, 0xA5, 0xA5, 0xC9, 0xD3, 0xA6, 0x72, /* 0x54-0x57 */
+ 0xA6, 0x73, 0x00, 0x00, 0xA7, 0xB7, 0xA7, 0xB8, /* 0x58-0x5B */
+ 0xA7, 0xB6, 0xA7, 0xB5, 0x00, 0x00, 0xA9, 0x73, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0xCC, 0x55, 0xA9, 0x75, /* 0x60-0x63 */
+ 0xA9, 0x74, 0xCC, 0x56, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0xAB, 0xC4, 0x00, 0x00, 0xAE, 0x5D, /* 0x68-0x6B */
+ 0xD1, 0x65, 0x00, 0x00, 0xD4, 0xF0, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xB1, 0x45, 0xB4, 0x47, 0xD4, 0xEF, 0xB4, 0x46, /* 0x70-0x73 */
+ 0x00, 0x00, 0xB9, 0xE5, 0x00, 0x00, 0xE1, 0x7D, /* 0x74-0x77 */
+ 0xBE, 0xC7, 0x00, 0x00, 0xC0, 0xA9, 0xEC, 0xD7, /* 0x78-0x7B */
+ 0x00, 0x00, 0xC4, 0x5E, 0x00, 0x00, 0xC5, 0x70, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0xC9, 0x72, 0x00, 0x00, 0xA5, 0xA6, /* 0x80-0x83 */
+ 0xC9, 0x73, 0xA6, 0x76, 0x00, 0x00, 0xA6, 0x74, /* 0x84-0x87 */
+ 0xA6, 0x75, 0xA6, 0x77, 0x00, 0x00, 0xA7, 0xBA, /* 0x88-0x8B */
+ 0xA7, 0xB9, 0x00, 0x00, 0xCA, 0xBC, 0xA7, 0xBB, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0xCA, 0xBD, 0xCC, 0x57, /* 0x90-0x93 */
+ 0x00, 0x00, 0xCC, 0x58, 0x00, 0x00, 0xA9, 0x76, /* 0x94-0x97 */
+ 0xA9, 0x78, 0xA9, 0x7A, 0xA9, 0x77, 0xA9, 0x7B, /* 0x98-0x9B */
+ 0xA9, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0xAB, 0xC8, 0xAB, 0xC5, /* 0xA0-0xA3 */
+ 0xAB, 0xC7, 0xAB, 0xC9, 0xAB, 0xC6, 0xD1, 0x66, /* 0xA4-0xA7 */
+ 0xCE, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0xD1, 0x68, 0xD1, 0x67, 0xAE, 0x63, 0x00, 0x00, /* 0xAC-0xAF */
+ 0xAE, 0x5F, 0x00, 0x00, 0x00, 0x00, 0xAE, 0x60, /* 0xB0-0xB3 */
+ 0xAE, 0x62, 0xAE, 0x64, 0xAE, 0x61, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0xAE, 0x66, 0xAE, 0x65, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB1, 0x4A, /* 0xBC-0xBF */
+ 0xD4, 0xF2, 0xD4, 0xF1, 0xB1, 0x49, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xB1, 0x48, 0xB1, 0x47, 0xB1, 0x4B, 0xB1, 0x46, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0xD8, 0xD5, 0xD8, 0xD2, /* 0xC8-0xCB */
+ 0xB4, 0x49, 0xD8, 0xD1, 0xD8, 0xD6, 0x00, 0x00, /* 0xCC-0xCF */
+ 0xB4, 0x4B, 0xD8, 0xD4, 0xB4, 0x48, 0xB4, 0x4A, /* 0xD0-0xD3 */
+ 0xD8, 0xD3, 0x00, 0x00, 0xDD, 0x48, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xDD, 0x49, 0xDD, 0x4A, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0xB9, 0xE6, 0xB9, 0xEE, /* 0xDC-0xDF */
+ 0xE1, 0x7E, 0xB9, 0xE8, 0xB9, 0xEC, 0xE1, 0xA1, /* 0xE0-0xE3 */
+ 0xB9, 0xED, 0xB9, 0xE9, 0xB9, 0xEA, 0xB9, 0xE7, /* 0xE4-0xE7 */
+ 0xB9, 0xEB, 0xBC, 0x66, 0xD8, 0xD0, 0xBC, 0x67, /* 0xE8-0xEB */
+ 0xBC, 0x65, 0x00, 0x00, 0xBC, 0x64, 0xE9, 0x5D, /* 0xEC-0xEF */
+ 0xBE, 0xC8, 0xEC, 0xD8, 0xEC, 0xD9, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0xC3, 0x64, 0xC4, 0x5F, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0xA4, 0x6F, 0x00, 0x00, 0xA6, 0x78, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_5C[512] = {
+ 0x00, 0x00, 0xAB, 0xCA, 0x00, 0x00, 0xD1, 0x69, /* 0x00-0x03 */
+ 0xAE, 0x67, 0x00, 0x00, 0x00, 0x00, 0xB1, 0x4E, /* 0x04-0x07 */
+ 0xB1, 0x4D, 0xB1, 0x4C, 0xB4, 0x4C, 0xB4, 0x4D, /* 0x08-0x0B */
+ 0xD8, 0xD7, 0xB9, 0xEF, 0xBE, 0xC9, 0xA4, 0x70, /* 0x0C-0x0F */
+ 0xC9, 0x5C, 0xA4, 0xD6, 0xC9, 0x74, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0xC9, 0xD4, 0xA6, 0x79, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0xA9, 0x7C, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDD, 0x4B, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0xA4, 0x71, 0x00, 0x00, /* 0x20-0x23 */
+ 0xA4, 0xD7, 0xC9, 0xD5, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0xCA, 0xBE, 0x00, 0x00, 0xCA, 0xBF, 0x00, 0x00, /* 0x28-0x2B */
+ 0xA7, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xD8, 0xD8, 0xB4, 0x4E, 0x00, 0x00, 0xDD, 0x4C, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xAA, /* 0x34-0x37 */
+ 0xA4, 0x72, 0xA4, 0xA8, 0xA4, 0xD8, 0xC9, 0x75, /* 0x38-0x3B */
+ 0xA5, 0xA7, 0x00, 0x00, 0xA7, 0xC0, 0xA7, 0xBF, /* 0x3C-0x3F */
+ 0xA7, 0xBD, 0xA7, 0xBE, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0xCC, 0x59, 0xA9, 0x7E, 0xA9, 0xA1, 0xCC, 0x5A, /* 0x44-0x47 */
+ 0xA9, 0x7D, 0x00, 0x00, 0x00, 0x00, 0xAB, 0xCE, /* 0x48-0x4B */
+ 0xCE, 0x78, 0xAB, 0xCD, 0xAB, 0xCB, 0xAB, 0xCC, /* 0x4C-0x4F */
+ 0xAE, 0x6A, 0xAE, 0x68, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0xD1, 0x6B, 0xAE, 0x69, 0xD1, 0x6A, 0x00, 0x00, /* 0x54-0x57 */
+ 0xAE, 0x5E, 0xD4, 0xF3, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0xB1, 0x50, 0xB1, 0x51, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xB1, 0x4F, 0x00, 0x00, 0xB9, 0xF0, 0xE1, 0xA2, /* 0x60-0x63 */
+ 0xBC, 0x68, 0xBC, 0x69, 0x00, 0x00, 0xE5, 0x61, /* 0x64-0x67 */
+ 0xC0, 0xAB, 0xEF, 0xC2, 0xEF, 0xC3, 0x00, 0x00, /* 0x68-0x6B */
+ 0xC4, 0xDD, 0xF8, 0xA8, 0xC9, 0x4B, 0xA4, 0xD9, /* 0x6C-0x6F */
+ 0x00, 0x00, 0xA4, 0x73, 0x00, 0x00, 0xC9, 0x77, /* 0x70-0x73 */
+ 0xC9, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0xA6, 0x7A, 0xC9, 0xD7, 0xC9, 0xD8, /* 0x78-0x7B */
+ 0xC9, 0xD6, 0x00, 0x00, 0xC9, 0xD9, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0xCA, 0xC7, 0x00, 0x00, /* 0x84-0x87 */
+ 0xCA, 0xC2, 0xCA, 0xC4, 0xCA, 0xC6, 0xCA, 0xC3, /* 0x88-0x8B */
+ 0xA7, 0xC4, 0xCA, 0xC0, 0x00, 0x00, 0xCA, 0xC1, /* 0x8C-0x8F */
+ 0xA7, 0xC1, 0xA7, 0xC2, 0xCA, 0xC5, 0xCA, 0xC8, /* 0x90-0x93 */
+ 0xA7, 0xC3, 0xCA, 0xC9, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0xCC, 0x68, 0x00, 0x00, 0xCC, 0x62, /* 0x9C-0x9F */
+ 0xCC, 0x5D, 0xA9, 0xA3, 0xCC, 0x65, 0xCC, 0x63, /* 0xA0-0xA3 */
+ 0xCC, 0x5C, 0xCC, 0x69, 0xCC, 0x6C, 0xCC, 0x67, /* 0xA4-0xA7 */
+ 0xCC, 0x60, 0xA9, 0xA5, 0xCC, 0x66, 0xA9, 0xA6, /* 0xA8-0xAB */
+ 0xCC, 0x61, 0xCC, 0x64, 0xCC, 0x5B, 0xCC, 0x5F, /* 0xAC-0xAF */
+ 0xCC, 0x6B, 0xA9, 0xA7, 0x00, 0x00, 0xA9, 0xA8, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0xCC, 0x5E, 0xCC, 0x6A, 0xA9, 0xA2, /* 0xB4-0xB7 */
+ 0xA9, 0xA4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0xCE, 0xAB, 0xCE, 0xA4, /* 0xC4-0xC7 */
+ 0xCE, 0xAA, 0xCE, 0xA3, 0xCE, 0xA5, 0xCE, 0x7D, /* 0xC8-0xCB */
+ 0xCE, 0x7B, 0x00, 0x00, 0xCE, 0xAC, 0xCE, 0xA9, /* 0xCC-0xCF */
+ 0xCE, 0x79, 0x00, 0x00, 0xAB, 0xD0, 0xCE, 0xA7, /* 0xD0-0xD3 */
+ 0xCE, 0xA8, 0x00, 0x00, 0xCE, 0xA6, 0xCE, 0x7C, /* 0xD4-0xD7 */
+ 0xCE, 0x7A, 0xAB, 0xCF, 0xCE, 0xA2, 0xCE, 0x7E, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0xCE, 0xA1, 0xCE, 0xAD, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xAE, 0x6F, 0x00, 0x00, 0xAE, 0x6E, 0x00, 0x00, /* 0xE8-0xEB */
+ 0xD1, 0x6C, 0xAE, 0x6B, 0xD1, 0x6E, 0x00, 0x00, /* 0xEC-0xEF */
+ 0xAE, 0x70, 0xD1, 0x6F, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xAE, 0x73, 0x00, 0x00, 0xAE, 0x71, 0xD1, 0x70, /* 0xF4-0xF7 */
+ 0xCE, 0xAE, 0xD1, 0x72, 0x00, 0x00, 0xAE, 0x6D, /* 0xF8-0xFB */
+ 0x00, 0x00, 0xAE, 0x6C, 0x00, 0x00, 0xD1, 0x6D, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_5D[512] = {
+ 0xD1, 0x71, 0xAE, 0x72, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0xB1, 0x53, 0xB1, 0x52, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD4, 0xF5, /* 0x08-0x0B */
+ 0xD4, 0xF9, 0xD4, 0xFB, 0xB1, 0x54, 0xD4, 0xFE, /* 0x0C-0x0F */
+ 0x00, 0x00, 0xB1, 0x58, 0xD5, 0x41, 0x00, 0x00, /* 0x10-0x13 */
+ 0xB1, 0x5A, 0x00, 0x00, 0xB1, 0x56, 0xB1, 0x5E, /* 0x14-0x17 */
+ 0x00, 0x00, 0xB1, 0x5B, 0xD4, 0xF7, 0xB1, 0x55, /* 0x18-0x1B */
+ 0x00, 0x00, 0xD4, 0xF6, 0xD4, 0xF4, 0xD5, 0x43, /* 0x1C-0x1F */
+ 0xD4, 0xF8, 0x00, 0x00, 0xB1, 0x57, 0xD5, 0x42, /* 0x20-0x23 */
+ 0xB1, 0x5C, 0xD4, 0xFD, 0xD4, 0xFC, 0xB1, 0x5D, /* 0x24-0x27 */
+ 0xD4, 0xFA, 0xB1, 0x59, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0xD5, 0x44, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xD5, 0x40, 0xD8, 0xE7, 0xD8, 0xEE, 0xD8, 0xE3, /* 0x30-0x33 */
+ 0xB4, 0x51, 0xD8, 0xDF, 0xD8, 0xEF, 0xD8, 0xD9, /* 0x34-0x37 */
+ 0xD8, 0xEC, 0xD8, 0xEA, 0xD8, 0xE4, 0x00, 0x00, /* 0x38-0x3B */
+ 0xD8, 0xED, 0xD8, 0xE6, 0x00, 0x00, 0xD8, 0xDE, /* 0x3C-0x3F */
+ 0xD8, 0xF0, 0xD8, 0xDC, 0xD8, 0xE9, 0xD8, 0xDA, /* 0x40-0x43 */
+ 0x00, 0x00, 0xD8, 0xF1, 0x00, 0x00, 0xB4, 0x52, /* 0x44-0x47 */
+ 0x00, 0x00, 0xD8, 0xEB, 0xDD, 0x4F, 0xD8, 0xDD, /* 0x48-0x4B */
+ 0xB4, 0x4F, 0x00, 0x00, 0xD8, 0xE1, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xB4, 0x50, 0xD8, 0xE0, 0xD8, 0xE5, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0xD8, 0xE2, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0xD8, 0xE8, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0xDD, 0x53, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0xDD, 0x56, 0xDD, 0x4E, /* 0x60-0x63 */
+ 0x00, 0x00, 0xDD, 0x50, 0x00, 0x00, 0xDD, 0x55, /* 0x64-0x67 */
+ 0xDD, 0x54, 0xB7, 0x43, 0x00, 0x00, 0xD8, 0xDB, /* 0x68-0x6B */
+ 0xDD, 0x52, 0x00, 0x00, 0x00, 0x00, 0xB7, 0x44, /* 0x6C-0x6F */
+ 0x00, 0x00, 0xDD, 0x4D, 0xDD, 0x51, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE1, 0xA9, /* 0x74-0x77 */
+ 0x00, 0x00, 0xE1, 0xB0, 0xE1, 0xA7, 0x00, 0x00, /* 0x78-0x7B */
+ 0xE1, 0xAE, 0xE1, 0xA5, 0xE1, 0xAD, 0xE1, 0xB1, /* 0x7C-0x7F */
+
+ 0xE1, 0xA4, 0xE1, 0xA8, 0xE1, 0xA3, 0x00, 0x00, /* 0x80-0x83 */
+ 0xB9, 0xF1, 0x00, 0x00, 0xE1, 0xA6, 0xB9, 0xF2, /* 0x84-0x87 */
+ 0xE1, 0xAC, 0xE1, 0xAB, 0xE1, 0xAA, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0xE1, 0xAF, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0x65, 0xE5, 0x67, /* 0x90-0x93 */
+ 0xBC, 0x6B, 0xE5, 0x68, 0x00, 0x00, 0xE5, 0x63, /* 0x94-0x97 */
+ 0x00, 0x00, 0xE5, 0x62, 0xE5, 0x6C, 0x00, 0x00, /* 0x98-0x9B */
+ 0xE5, 0x6A, 0xBC, 0x6A, 0xE5, 0x6D, 0xE5, 0x64, /* 0x9C-0x9F */
+ 0xE5, 0x69, 0xE5, 0x6B, 0xE5, 0x66, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE9, 0x61, /* 0xA4-0xA7 */
+ 0xE9, 0x66, 0xE9, 0x60, 0xE9, 0x65, 0x00, 0x00, /* 0xA8-0xAB */
+ 0xE9, 0x5E, 0xE9, 0x68, 0xE9, 0x64, 0xE9, 0x69, /* 0xAC-0xAF */
+ 0xE9, 0x63, 0xE9, 0x5F, 0xE9, 0x67, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0xE9, 0x6A, 0xE9, 0x62, 0x00, 0x00, 0xEC, 0xDA, /* 0xB4-0xB7 */
+ 0xC0, 0xAF, 0x00, 0x00, 0xC0, 0xAD, 0x00, 0x00, /* 0xB8-0xBB */
+ 0xC0, 0xAC, 0xC0, 0xAE, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0xEF, 0xC4, 0x00, 0x00, 0xF1, 0x72, 0xF1, 0xFD, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0xF4, 0x44, 0xF4, 0x45, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xC4, 0x60, 0x00, 0x00, 0xF5, 0xC9, /* 0xC8-0xCB */
+ 0x00, 0x00, 0xC4, 0xDE, 0x00, 0x00, 0xF5, 0xCA, /* 0xCC-0xCF */
+ 0x00, 0x00, 0xF6, 0xDE, 0xC5, 0x72, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xC5, 0x71, 0xF6, 0xDD, 0xC5, 0xC9, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xF7, 0xD6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0xA4, 0x74, 0xA6, 0x7B, 0xC9, 0xDA, /* 0xDC-0xDF */
+ 0xCA, 0xCA, 0xA8, 0xB5, 0xB1, 0x5F, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0xA4, 0x75, 0xA5, 0xAA, 0xA5, 0xA9, /* 0xE4-0xE7 */
+ 0xA5, 0xA8, 0x00, 0x00, 0x00, 0x00, 0xA7, 0xC5, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0xAE, 0x74, 0x00, 0x00, /* 0xEC-0xEF */
+ 0xDD, 0x57, 0xA4, 0x76, 0xA4, 0x77, 0xA4, 0x78, /* 0xF0-0xF3 */
+ 0xA4, 0xDA, 0x00, 0x00, 0x00, 0x00, 0xAB, 0xD1, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xCE, 0xAF, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0xB4, 0x53, 0xA4, 0x79, 0xC9, 0x5D, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_5E[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0xA5, 0xAB, 0xA5, 0xAC, /* 0x00-0x03 */
+ 0xC9, 0x78, 0x00, 0x00, 0xA6, 0x7C, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0xCA, 0xCB, 0x00, 0x00, /* 0x08-0x0B */
+ 0xA7, 0xC6, 0x00, 0x00, 0xCA, 0xCC, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0xA9, 0xAE, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0xCC, 0x6E, 0xA9, 0xAC, 0xA9, 0xAB, 0xCC, 0x6D, /* 0x14-0x17 */
+ 0xA9, 0xA9, 0xCC, 0x6F, 0xA9, 0xAA, 0xA9, 0xAD, /* 0x18-0x1B */
+ 0x00, 0x00, 0xAB, 0xD2, 0x00, 0x00, 0xAB, 0xD4, /* 0x1C-0x1F */
+ 0xCE, 0xB3, 0xCE, 0xB0, 0xCE, 0xB1, 0xCE, 0xB2, /* 0x20-0x23 */
+ 0xCE, 0xB4, 0xAB, 0xD3, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0xD1, 0x74, 0xD1, 0x73, 0x00, 0x00, 0xAE, 0x76, /* 0x28-0x2B */
+ 0x00, 0x00, 0xAE, 0x75, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB1, 0x62, /* 0x30-0x33 */
+ 0xD5, 0x46, 0x00, 0x00, 0xB1, 0x61, 0xB1, 0x63, /* 0x34-0x37 */
+ 0xB1, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0xB4, 0x55, 0xD5, 0x45, 0x00, 0x00, /* 0x3C-0x3F */
+ 0xB4, 0x56, 0xD8, 0xF3, 0x00, 0x00, 0xB4, 0x57, /* 0x40-0x43 */
+ 0xD8, 0xF2, 0xB4, 0x54, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0xDD, 0x5A, 0xDD, 0x5C, /* 0x48-0x4B */
+ 0xB7, 0x45, 0xDD, 0x5B, 0xDD, 0x59, 0xDD, 0x58, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE1, 0xB4, /* 0x50-0x53 */
+ 0xB9, 0xF7, 0xB9, 0xF5, 0x00, 0x00, 0xB9, 0xF6, /* 0x54-0x57 */
+ 0xE1, 0xB2, 0xE1, 0xB3, 0x00, 0x00, 0xB9, 0xF3, /* 0x58-0x5B */
+ 0xE5, 0x71, 0xE5, 0x6F, 0x00, 0x00, 0xBC, 0x6D, /* 0x5C-0x5F */
+ 0xE5, 0x70, 0xBC, 0x6E, 0xBC, 0x6C, 0xB9, 0xF4, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0x6D, 0xE9, 0x6B, /* 0x64-0x67 */
+ 0xE9, 0x6C, 0xE5, 0x6E, 0xEC, 0xDC, 0xC0, 0xB0, /* 0x68-0x6B */
+ 0xEC, 0xDB, 0xEF, 0xC5, 0xEF, 0xC6, 0xE9, 0x6E, /* 0x6C-0x6F */
+ 0xF1, 0xFE, 0x00, 0x00, 0xA4, 0x7A, 0xA5, 0xAD, /* 0x70-0x73 */
+ 0xA6, 0x7E, 0xC9, 0xDB, 0xA6, 0x7D, 0x00, 0x00, /* 0x74-0x77 */
+ 0xA9, 0xAF, 0xB7, 0x46, 0x00, 0x00, 0xA4, 0xDB, /* 0x78-0x7B */
+ 0xA5, 0xAE, 0xAB, 0xD5, 0xB4, 0x58, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0xC9, 0x79, 0x00, 0x00, 0xC9, 0x7A, 0x00, 0x00, /* 0x80-0x83 */
+ 0xC9, 0xDC, 0x00, 0x00, 0x00, 0x00, 0xA7, 0xC8, /* 0x84-0x87 */
+ 0xCA, 0xD0, 0xCA, 0xCE, 0xA7, 0xC9, 0xCA, 0xCD, /* 0x88-0x8B */
+ 0xCA, 0xCF, 0xCA, 0xD1, 0x00, 0x00, 0xA7, 0xC7, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0xA9, 0xB3, 0xA9, 0xB4, 0xA9, 0xB1, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0xA9, 0xB0, 0xCE, 0xB8, /* 0x98-0x9B */
+ 0xA9, 0xB2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0xAB, 0xD6, 0x00, 0x00, 0xCE, 0xB7, 0xCE, 0xB9, /* 0xA0-0xA3 */
+ 0xCE, 0xB6, 0xCE, 0xBA, 0xAB, 0xD7, 0xAE, 0x79, /* 0xA4-0xA7 */
+ 0xD1, 0x75, 0x00, 0x00, 0xD1, 0x77, 0xAE, 0x77, /* 0xA8-0xAB */
+ 0xD1, 0x78, 0xAE, 0x78, 0xD1, 0x76, 0x00, 0x00, /* 0xAC-0xAF */
+ 0xCE, 0xB5, 0xD5, 0x47, 0xD5, 0x4A, 0xD5, 0x4B, /* 0xB0-0xB3 */
+ 0xD5, 0x48, 0xB1, 0x67, 0xB1, 0x66, 0xB1, 0x64, /* 0xB4-0xB7 */
+ 0xB1, 0x65, 0xD5, 0x49, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0xB1, 0x68, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0xB4, 0x5A, 0xB4, 0x5B, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xB4, 0x5C, 0xDD, 0x5D, 0xDD, 0x5F, 0xDD, 0x61, /* 0xC4-0xC7 */
+ 0xB7, 0x48, 0xB7, 0x47, 0xB4, 0x59, 0xDD, 0x60, /* 0xC8-0xCB */
+ 0xDD, 0x5E, 0x00, 0x00, 0xE1, 0xB8, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0xE1, 0xB6, 0xE1, 0xBC, 0xB9, 0xF8, /* 0xD0-0xD3 */
+ 0xE1, 0xBD, 0xE1, 0xBA, 0xB9, 0xF9, 0xE1, 0xB7, /* 0xD4-0xD7 */
+ 0xE1, 0xB5, 0xE1, 0xBB, 0xBC, 0x70, 0xE5, 0x73, /* 0xD8-0xDB */
+ 0xE1, 0xB9, 0xBC, 0x72, 0xE5, 0x74, 0xBC, 0x71, /* 0xDC-0xDF */
+ 0xBC, 0x74, 0xE5, 0x75, 0xBC, 0x6F, 0xBC, 0x73, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0xE9, 0x73, 0xE9, 0x71, 0xE9, 0x70, /* 0xE4-0xE7 */
+ 0xE9, 0x72, 0xE9, 0x6F, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0xC3, 0x66, 0x00, 0x00, 0xF4, 0x46, 0xF4, 0x47, /* 0xEC-0xEF */
+ 0x00, 0x00, 0xF5, 0xCB, 0xF6, 0xDF, 0xC6, 0x55, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0xA9, 0xB5, 0xA7, 0xCA, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0xAB, 0xD8, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0xA4, 0x7B, 0xA4, 0xDC, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_5F[512] = {
+ 0x00, 0x00, 0xA5, 0xAF, 0xC9, 0xDD, 0x00, 0x00, /* 0x00-0x03 */
+ 0xA7, 0xCB, 0xCA, 0xD2, 0x00, 0x00, 0xCE, 0xBB, /* 0x04-0x07 */
+ 0xAB, 0xD9, 0x00, 0x00, 0xB9, 0xFA, 0xA4, 0x7C, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA6, 0xA1, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0xB7, 0x49, 0xA4, 0x7D, /* 0x10-0x13 */
+ 0xA4, 0xDD, 0xA4, 0xDE, 0x00, 0x00, 0xA5, 0xB1, /* 0x14-0x17 */
+ 0xA5, 0xB0, 0x00, 0x00, 0xC9, 0xDE, 0xA6, 0xA2, /* 0x18-0x1B */
+ 0x00, 0x00, 0xCA, 0xD3, 0x00, 0x00, 0xA7, 0xCC, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0xCC, 0x71, 0xCC, 0x72, /* 0x20-0x23 */
+ 0xCC, 0x73, 0x00, 0x00, 0xA9, 0xB6, 0xA9, 0xB7, /* 0x24-0x27 */
+ 0xCC, 0x70, 0xA9, 0xB8, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0xAB, 0xDA, 0xCE, 0xBC, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xD1, 0x7A, 0xAE, 0x7A, 0x00, 0x00, 0xD1, 0x79, /* 0x30-0x33 */
+ 0x00, 0x00, 0xB1, 0x69, 0xD5, 0x4C, 0xB1, 0x6A, /* 0x34-0x37 */
+ 0xD5, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0xB4, 0x5D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0xDD, 0x62, 0x00, 0x00, 0x00, 0x00, 0xE1, 0xBF, /* 0x40-0x43 */
+ 0xE1, 0xBE, 0x00, 0x00, 0xB9, 0xFB, 0x00, 0x00, /* 0x44-0x47 */
+ 0xBC, 0x75, 0xE5, 0x76, 0xBE, 0xCA, 0xE9, 0x74, /* 0x48-0x4B */
+ 0xC0, 0xB1, 0x00, 0x00, 0xC5, 0x73, 0xF7, 0xD8, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0xCC, 0x74, 0x00, 0x00, 0xCE, 0xBD, 0xB1, 0x6B, /* 0x54-0x57 */
+ 0xD8, 0xF4, 0xB7, 0x4A, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0xC2, 0x55, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0xA7, 0xCE, 0x00, 0x00, /* 0x60-0x63 */
+ 0xA7, 0xCD, 0xAB, 0xDB, 0x00, 0x00, 0xD1, 0x7B, /* 0x64-0x67 */
+ 0x00, 0x00, 0xB1, 0x6D, 0xB3, 0x43, 0xB1, 0x6E, /* 0x68-0x6B */
+ 0xB1, 0x6C, 0xB4, 0x5E, 0x00, 0x00, 0xE1, 0xC0, /* 0x6C-0x6F */
+ 0xB9, 0xFC, 0xBC, 0x76, 0x00, 0x00, 0xC9, 0x4C, /* 0x70-0x73 */
+ 0xC9, 0xDF, 0x00, 0x00, 0xCA, 0xD5, 0xA7, 0xCF, /* 0x74-0x77 */
+ 0xCA, 0xD4, 0xA7, 0xD0, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0xA9, 0xBC, 0xCC, 0x77, 0xCC, 0x76, 0xA9, 0xBB, /* 0x7C-0x7F */
+
+ 0xA9, 0xB9, 0xA9, 0xBA, 0xCC, 0x75, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0xAB, 0xDD, 0xCE, 0xBE, 0xAB, 0xE0, /* 0x84-0x87 */
+ 0xAB, 0xDC, 0xAB, 0xE2, 0xAB, 0xDE, 0xAB, 0xDF, /* 0x88-0x8B */
+ 0xAB, 0xE1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0xAE, 0x7D, 0xAE, 0x7C, 0xAE, 0x7B, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0xD5, 0x4F, 0xB1, 0x6F, /* 0x94-0x97 */
+ 0xB1, 0x72, 0xB1, 0x70, 0x00, 0x00, 0xD5, 0x4E, /* 0x98-0x9B */
+ 0xB1, 0x75, 0x00, 0x00, 0xB1, 0x71, 0xD5, 0x50, /* 0x9C-0x9F */
+ 0xB1, 0x74, 0xB1, 0x73, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0xD8, 0xF6, 0xD8, 0xF5, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xB4, 0x61, 0xB4, 0x5F, 0xB4, 0x60, 0xD8, 0xF7, /* 0xA8-0xAB */
+ 0xB7, 0x4B, 0xDD, 0x64, 0xB7, 0x4C, 0xDD, 0x63, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0x77, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0xBC, 0x78, 0xE1, 0xC1, 0xBC, 0x77, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xB9, 0xFD, 0x00, 0x00, 0xEC, 0xDE, /* 0xB8-0xBB */
+ 0xE9, 0x75, 0xC0, 0xB2, 0xEC, 0xDD, 0xF2, 0x40, /* 0xBC-0xBF */
+ 0xF4, 0x48, 0xF4, 0x49, 0x00, 0x00, 0xA4, 0xDF, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0xA5, 0xB2, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xC9, 0x7B, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0xA7, 0xD2, 0xA7, 0xD4, 0x00, 0x00, 0xC9, 0xE2, /* 0xCC-0xCF */
+ 0xCA, 0xD8, 0xCA, 0xD7, 0xCA, 0xD6, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xC9, 0xE1, 0xC9, 0xE0, 0xA6, 0xA4, 0xA7, 0xD3, /* 0xD4-0xD7 */
+ 0xA7, 0xD1, 0xA6, 0xA3, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0xA9, 0xBD, 0xCC, 0x78, 0x00, 0x00, /* 0xDC-0xDF */
+ 0xA9, 0xBE, 0xCA, 0xDD, 0x00, 0x00, 0xCA, 0xDF, /* 0xE0-0xE3 */
+ 0xCA, 0xDE, 0xCC, 0x79, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xCA, 0xDA, 0x00, 0x00, 0xA7, 0xD8, 0xA7, 0xD6, /* 0xE8-0xEB */
+ 0x00, 0x00, 0xCA, 0xD9, 0xCA, 0xDB, 0xCA, 0xE1, /* 0xEC-0xEF */
+ 0x00, 0x00, 0xA7, 0xD5, 0x00, 0x00, 0xCA, 0xDC, /* 0xF0-0xF3 */
+ 0xCA, 0xE5, 0xA9, 0xC0, 0x00, 0x00, 0xCA, 0xE2, /* 0xF4-0xF7 */
+ 0xA7, 0xD7, 0x00, 0x00, 0xCA, 0xE0, 0xCA, 0xE3, /* 0xF8-0xFB */
+ 0x00, 0x00, 0xA9, 0xBF, 0x00, 0x00, 0xA9, 0xC1, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_60[512] = {
+ 0xCA, 0xE4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0xCC, 0xAF, 0xCC, 0xA2, 0xCC, 0x7E, /* 0x08-0x0B */
+ 0xCC, 0xAE, 0xCC, 0xA9, 0xAB, 0xE7, 0xA9, 0xC2, /* 0x0C-0x0F */
+ 0xCC, 0xAA, 0xCC, 0xAD, 0xAB, 0xE3, 0xCC, 0xAC, /* 0x10-0x13 */
+ 0xA9, 0xC3, 0xA9, 0xC8, 0xA9, 0xC6, 0xCC, 0xA3, /* 0x14-0x17 */
+ 0x00, 0x00, 0xCC, 0x7C, 0xCC, 0xA5, 0xA9, 0xCD, /* 0x18-0x1B */
+ 0xCC, 0xB0, 0xAB, 0xE4, 0xCC, 0xA6, 0x00, 0x00, /* 0x1C-0x1F */
+ 0xAB, 0xE5, 0xA9, 0xC9, 0xCC, 0xA8, 0x00, 0x00, /* 0x20-0x23 */
+ 0xCE, 0xCD, 0xAB, 0xE6, 0xCC, 0x7B, 0xA9, 0xCA, /* 0x24-0x27 */
+ 0xAB, 0xE8, 0xA9, 0xCB, 0xA9, 0xC7, 0xA9, 0xCC, /* 0x28-0x2B */
+ 0xCC, 0xA7, 0xCC, 0x7A, 0xCC, 0xAB, 0xA9, 0xC4, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0xCC, 0x7D, 0xCC, 0xA4, /* 0x30-0x33 */
+ 0xCC, 0xA1, 0xA9, 0xC5, 0x00, 0x00, 0xCE, 0xBF, /* 0x34-0x37 */
+ 0x00, 0x00, 0xCE, 0xC0, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0xCE, 0xCA, 0xD1, 0xA1, 0xCE, 0xCB, 0xAB, 0xEE, /* 0x40-0x43 */
+ 0xCE, 0xCE, 0xCE, 0xC4, 0xAB, 0xED, 0xCE, 0xC6, /* 0x44-0x47 */
+ 0x00, 0x00, 0xCE, 0xC7, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0xCE, 0xC9, 0xAB, 0xE9, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xAE, 0xA3, 0x00, 0x00, 0xF9, 0xDA, 0xCE, 0xC5, /* 0x50-0x53 */
+ 0xCE, 0xC1, 0xAE, 0xA4, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0xCE, 0xCF, 0xAE, 0x7E, 0xD1, 0x7D, 0xCE, 0xC8, /* 0x58-0x5B */
+ 0x00, 0x00, 0xD1, 0x7C, 0xCE, 0xC3, 0xCE, 0xCC, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0xAB, 0xEC, 0xAE, 0xA1, /* 0x60-0x63 */
+ 0xAB, 0xF2, 0xAE, 0xA2, 0xCE, 0xD0, 0xD1, 0x7E, /* 0x64-0x67 */
+ 0xAB, 0xEB, 0xAE, 0xA6, 0xAB, 0xF1, 0xAB, 0xF0, /* 0x68-0x6B */
+ 0xAB, 0xEF, 0xAE, 0xA5, 0xCE, 0xD1, 0xAE, 0xA7, /* 0x6C-0x6F */
+ 0xAB, 0xEA, 0x00, 0x00, 0xCE, 0xC2, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB1, 0x76, /* 0x7C-0x7F */
+
+ 0xD1, 0xA4, 0xD1, 0xA6, 0x00, 0x00, 0xD1, 0xA8, /* 0x80-0x83 */
+ 0xAE, 0xA8, 0xAE, 0xAE, 0xD5, 0x53, 0xD1, 0xAC, /* 0x84-0x87 */
+ 0xD1, 0xA3, 0xB1, 0x78, 0xD5, 0x51, 0x00, 0x00, /* 0x88-0x8B */
+ 0xAE, 0xAD, 0xAE, 0xAB, 0xD1, 0xAE, 0x00, 0x00, /* 0x8C-0x8F */
+ 0xD5, 0x52, 0x00, 0x00, 0xD1, 0xA5, 0x00, 0x00, /* 0x90-0x93 */
+ 0xAE, 0xAC, 0xD1, 0xA9, 0xAE, 0xAF, 0xD1, 0xAB, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0xAE, 0xAA, 0xD1, 0xAA, /* 0x98-0x9B */
+ 0xD1, 0xAD, 0xD1, 0xA7, 0x00, 0x00, 0xAE, 0xA9, /* 0x9C-0x9F */
+ 0xB1, 0x79, 0x00, 0x00, 0xD1, 0xA2, 0xB1, 0x77, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xB1, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0xD5, 0x55, 0xD5, 0x5E, 0xB4, 0x64, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0xB1, 0x7C, 0xB1, 0xA3, 0xB4, 0x65, 0xD5, 0x60, /* 0xB4-0xB7 */
+ 0xB1, 0xAA, 0xD8, 0xF9, 0xD5, 0x56, 0xB1, 0xA2, /* 0xB8-0xBB */
+ 0xB1, 0xA5, 0xB1, 0x7E, 0xD5, 0x54, 0xD5, 0x62, /* 0xBC-0xBF */
+ 0xD5, 0x65, 0xD9, 0x49, 0x00, 0x00, 0xD5, 0x63, /* 0xC0-0xC3 */
+ 0xD8, 0xFD, 0xB1, 0xA1, 0xB1, 0xA8, 0xB1, 0xAC, /* 0xC4-0xC7 */
+ 0xD5, 0x5D, 0xD8, 0xF8, 0xD5, 0x61, 0xB1, 0x7B, /* 0xC8-0xCB */
+ 0xD8, 0xFA, 0xD5, 0x64, 0xD8, 0xFC, 0xD5, 0x59, /* 0xCC-0xCF */
+ 0x00, 0x00, 0xB4, 0x62, 0x00, 0x00, 0xD5, 0x57, /* 0xD0-0xD3 */
+ 0xD5, 0x58, 0xB1, 0xA7, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xB1, 0xA6, 0xD5, 0x5B, 0xB1, 0xAB, 0xD5, 0x5F, /* 0xD8-0xDB */
+ 0xB1, 0xA4, 0xD5, 0x5C, 0x00, 0x00, 0xB1, 0xA9, /* 0xDC-0xDF */
+ 0xB4, 0x66, 0xB4, 0x63, 0xD8, 0xFB, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0xD5, 0x5A, 0x00, 0x00, 0xB1, 0x7D, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0xB4, 0x6B, 0xB4, 0x6F, 0xD9, 0x40, 0xB7, 0x51, /* 0xF0-0xF3 */
+ 0xB4, 0x6D, 0xD9, 0x44, 0xB4, 0x71, 0xDD, 0x65, /* 0xF4-0xF7 */
+ 0xD9, 0x46, 0xB7, 0x53, 0xB4, 0x69, 0xB4, 0x6C, /* 0xF8-0xFB */
+ 0xD9, 0x47, 0x00, 0x00, 0xD9, 0x48, 0xD9, 0x4E, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_61[512] = {
+ 0xB4, 0x73, 0xB7, 0x54, 0x00, 0x00, 0xD9, 0x4A, /* 0x00-0x03 */
+ 0xD9, 0x4F, 0xD9, 0x43, 0xB7, 0x5E, 0x00, 0x00, /* 0x04-0x07 */
+ 0xB7, 0x55, 0xB4, 0x72, 0xD9, 0x41, 0xD9, 0x50, /* 0x08-0x0B */
+ 0x00, 0x00, 0xB7, 0x5D, 0xB4, 0x70, 0xB7, 0x4E, /* 0x0C-0x0F */
+ 0xD9, 0x4D, 0x00, 0x00, 0xB4, 0x74, 0xD9, 0x45, /* 0x10-0x13 */
+ 0xD8, 0xFE, 0xB4, 0x6A, 0xD9, 0x42, 0x00, 0x00, /* 0x14-0x17 */
+ 0xD9, 0x4B, 0x00, 0x00, 0xB7, 0x4D, 0xB7, 0x52, /* 0x18-0x1B */
+ 0xB4, 0x67, 0xD9, 0x4C, 0x00, 0x00, 0xB7, 0x50, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB4, 0x68, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB7, 0x5C, /* 0x24-0x27 */
+ 0xE1, 0xC3, 0xDD, 0x70, 0x00, 0x00, 0xDD, 0x68, /* 0x28-0x2B */
+ 0xE1, 0xC2, 0x00, 0x00, 0xDD, 0x6C, 0xDD, 0x6E, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0xDD, 0x6B, 0x00, 0x00, /* 0x30-0x33 */
+ 0xB7, 0x5B, 0x00, 0x00, 0xDD, 0x6A, 0xB7, 0x5F, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE1, 0xD2, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0xB7, 0x5A, 0xBA, 0x40, /* 0x3C-0x3F */
+ 0xDD, 0x71, 0xE1, 0xC4, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0xB7, 0x58, 0xDD, 0x69, 0xDD, 0x6D, 0xB9, 0xFE, /* 0x44-0x47 */
+ 0xB7, 0x4F, 0xDD, 0x66, 0xDD, 0x67, 0xBA, 0x41, /* 0x48-0x4B */
+ 0xB7, 0x57, 0xB7, 0x59, 0xB7, 0x56, 0xDD, 0x6F, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0xC8, 0xE1, 0xC9, /* 0x50-0x53 */
+ 0xE1, 0xCE, 0xBC, 0x7D, 0xE1, 0xD5, 0x00, 0x00, /* 0x54-0x57 */
+ 0xBA, 0x47, 0x00, 0x00, 0xBA, 0x46, 0xE1, 0xD0, /* 0x58-0x5B */
+ 0x00, 0x00, 0xBC, 0x7C, 0xE1, 0xC5, 0xBA, 0x45, /* 0x5C-0x5F */
+ 0x00, 0x00, 0xE1, 0xD4, 0xBA, 0x43, 0xBA, 0x44, /* 0x60-0x63 */
+ 0x00, 0x00, 0xE1, 0xD1, 0xE5, 0xAA, 0xBC, 0x7A, /* 0x64-0x67 */
+ 0xB4, 0x6E, 0x00, 0x00, 0xE1, 0xD3, 0xBC, 0xA3, /* 0x68-0x6B */
+ 0xE1, 0xCB, 0x00, 0x00, 0xBC, 0x7B, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xBC, 0xA2, 0xE1, 0xC6, 0xE1, 0xCA, 0xE1, 0xC7, /* 0x70-0x73 */
+ 0xE1, 0xCD, 0xBA, 0x48, 0xBC, 0x79, 0xBA, 0x42, /* 0x74-0x77 */
+ 0x00, 0x00, 0xE5, 0x7A, 0xE1, 0xCF, 0x00, 0x00, /* 0x78-0x7B */
+ 0xBC, 0xA1, 0x00, 0x00, 0xBC, 0xA4, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0xE1, 0xCC, 0x00, 0x00, 0xBC, 0x7E, 0xE5, 0x79, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0xE5, 0x7E, 0xBE, 0xCE, 0xE5, 0x78, /* 0x88-0x8B */
+ 0xE9, 0xA3, 0xE5, 0xA9, 0xBC, 0xA8, 0x00, 0x00, /* 0x8C-0x8F */
+ 0xBC, 0xA6, 0xBE, 0xCC, 0xE5, 0xA6, 0xE5, 0xA2, /* 0x90-0x93 */
+ 0xBC, 0xAC, 0x00, 0x00, 0xE9, 0x78, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0xBC, 0xAA, 0xE5, 0xA1, /* 0x98-0x9B */
+ 0x00, 0x00, 0xE9, 0x76, 0x00, 0x00, 0xE5, 0xA5, /* 0x9C-0x9F */
+ 0x00, 0x00, 0xE5, 0xA8, 0xE5, 0x7D, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0xBC, 0xAB, 0x00, 0x00, 0x00, 0x00, 0xBC, 0xA5, /* 0xA4-0xA7 */
+ 0xE9, 0x77, 0xBE, 0xCD, 0xE5, 0xA7, 0xBC, 0xA7, /* 0xA8-0xAB */
+ 0xBC, 0xA9, 0xE5, 0xA4, 0xBC, 0xAD, 0xE5, 0xA3, /* 0xAC-0xAF */
+ 0xE5, 0x7C, 0xE5, 0x7B, 0xBE, 0xCB, 0xE5, 0xAB, /* 0xB0-0xB3 */
+ 0xE9, 0x7A, 0xEC, 0xE0, 0xBE, 0xD0, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0xE9, 0xA2, 0x00, 0x00, 0xE9, 0x7E, 0x00, 0x00, /* 0xB8-0xBB */
+ 0xEC, 0xE1, 0x00, 0x00, 0xBE, 0xD1, 0xE9, 0xA1, /* 0xBC-0xBF */
+ 0x00, 0x00, 0xE9, 0x7C, 0xC0, 0xB4, 0xEC, 0xDF, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0xE9, 0x79, 0xE9, 0x7B, 0xC0, 0xB5, /* 0xC4-0xC7 */
+ 0xBE, 0xD3, 0xC0, 0xB3, 0xBE, 0xD2, 0xC0, 0xB7, /* 0xC8-0xCB */
+ 0xE9, 0x7D, 0xBE, 0xCF, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0xEF, 0xCF, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xEF, 0xC7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0xEC, 0xE7, 0xEF, 0xC8, /* 0xDC-0xDF */
+ 0xEC, 0xE3, 0x00, 0x00, 0x00, 0x00, 0xC2, 0x56, /* 0xE0-0xE3 */
+ 0xEC, 0xE5, 0xEC, 0xE4, 0xC0, 0xB6, 0xEC, 0xE2, /* 0xE4-0xE7 */
+ 0xEC, 0xE6, 0xEF, 0xD0, 0xEF, 0xCC, 0xEF, 0xCE, /* 0xE8-0xEB */
+ 0x00, 0x00, 0xEF, 0xC9, 0xEF, 0xCA, 0x00, 0x00, /* 0xEC-0xEF */
+ 0xEF, 0xCD, 0xEF, 0xCB, 0xC3, 0x67, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0xC3, 0x6A, 0xC3, 0x69, 0xC3, 0x68, /* 0xF4-0xF7 */
+ 0xC4, 0x61, 0xF4, 0x4A, 0xC4, 0x62, 0xF2, 0x41, /* 0xF8-0xFB */
+ 0xC4, 0xDF, 0xF5, 0xCC, 0xC4, 0xE0, 0xC5, 0x74, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_62[512] = {
+ 0xC5, 0xCA, 0xF7, 0xD9, 0x00, 0x00, 0xF7, 0xDA, /* 0x00-0x03 */
+ 0xF7, 0xDB, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xBA, /* 0x04-0x07 */
+ 0xA4, 0xE0, 0xC9, 0x7C, 0xA5, 0xB3, 0x00, 0x00, /* 0x08-0x0B */
+ 0xA6, 0xA6, 0xA6, 0xA7, 0xA6, 0xA5, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xA6, 0xA8, 0xA7, 0xDA, 0xA7, 0xD9, 0x00, 0x00, /* 0x10-0x13 */
+ 0xCC, 0xB1, 0xA9, 0xCF, 0xA9, 0xCE, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0xD1, 0xAF, 0xB1, 0xAD, 0xB1, 0xAE, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB4, 0x75, /* 0x1C-0x1F */
+ 0xDD, 0x72, 0xB7, 0x60, 0xB7, 0x61, 0xDD, 0x74, /* 0x20-0x23 */
+ 0xDD, 0x76, 0xDD, 0x75, 0x00, 0x00, 0xE1, 0xD7, /* 0x24-0x27 */
+ 0x00, 0x00, 0xE1, 0xD6, 0xBA, 0x49, 0xE1, 0xD8, /* 0x28-0x2B */
+ 0x00, 0x00, 0xE5, 0xAC, 0xBC, 0xAE, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xBE, 0xD4, 0x00, 0x00, 0xC0, 0xB8, 0xC2, 0x57, /* 0x30-0x33 */
+ 0xC0, 0xB9, 0x00, 0x00, 0xA4, 0xE1, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0xCA, 0xE6, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0xCC, 0xB2, 0xA9, 0xD1, 0xA9, 0xD0, /* 0x3C-0x3F */
+ 0xA9, 0xD2, 0xAB, 0xF3, 0xCE, 0xD2, 0xCE, 0xD3, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0xD1, 0xB0, 0xAE, 0xB0, /* 0x44-0x47 */
+ 0xB1, 0xAF, 0xB4, 0x76, 0xD9, 0x51, 0xA4, 0xE2, /* 0x48-0x4B */
+ 0x00, 0x00, 0xA4, 0x7E, 0xA4, 0xE3, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xC9, 0x7D, 0xA5, 0xB7, 0xA5, 0xB6, 0xA5, 0xB4, /* 0x50-0x53 */
+ 0xA5, 0xB5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0xA6, 0xAB, 0xC9, 0xE9, 0xC9, 0xEB, 0xA6, 0xAA, /* 0x58-0x5B */
+ 0xC9, 0xE3, 0x00, 0x00, 0xC9, 0xE4, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xC9, 0xEA, 0xC9, 0xE6, 0xC9, 0xE8, 0xA6, 0xA9, /* 0x60-0x63 */
+ 0xC9, 0xE5, 0xC9, 0xEC, 0xC9, 0xE7, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0xA7, 0xE1, 0xA7, 0xEA, 0xA7, 0xE8, /* 0x6C-0x6F */
+ 0xCA, 0xF0, 0xCA, 0xED, 0xCA, 0xF5, 0xA7, 0xE6, /* 0x70-0x73 */
+ 0xCA, 0xF6, 0x00, 0x00, 0xA7, 0xDF, 0xCA, 0xF3, /* 0x74-0x77 */
+ 0x00, 0x00, 0xA7, 0xE5, 0xCA, 0xEF, 0xCA, 0xEE, /* 0x78-0x7B */
+ 0xA7, 0xE3, 0xCA, 0xF4, 0xA7, 0xE4, 0xA9, 0xD3, /* 0x7C-0x7F */
+
+ 0xA7, 0xDE, 0xCA, 0xF1, 0x00, 0x00, 0xCA, 0xE7, /* 0x80-0x83 */
+ 0xA7, 0xDB, 0x00, 0x00, 0xA7, 0xEE, 0xCA, 0xEC, /* 0x84-0x87 */
+ 0xCA, 0xF2, 0xA7, 0xE0, 0xA7, 0xE2, 0x00, 0x00, /* 0x88-0x8B */
+ 0xCA, 0xE8, 0x00, 0x00, 0xCA, 0xE9, 0xCA, 0xEA, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xA7, 0xED, 0xA7, 0xE7, 0xA7, 0xEC, /* 0x90-0x93 */
+ 0xCA, 0xEB, 0xA7, 0xEB, 0xA7, 0xDD, 0xA7, 0xDC, /* 0x94-0x97 */
+ 0xA7, 0xE9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xA9, 0xE1, 0xCC, 0xBE, 0xCC, 0xB7, 0xA9, 0xDC, /* 0xA8-0xAB */
+ 0xA9, 0xEF, 0xCC, 0xB3, 0xCC, 0xBA, 0xCC, 0xBC, /* 0xAC-0xAF */
+ 0xCC, 0xBF, 0xA9, 0xEA, 0x00, 0x00, 0xCC, 0xBB, /* 0xB0-0xB3 */
+ 0xCC, 0xB4, 0xA9, 0xE8, 0xCC, 0xB8, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0xCC, 0xC0, 0xA9, 0xD9, 0x00, 0x00, 0xCC, 0xBD, /* 0xB8-0xBB */
+ 0xA9, 0xE3, 0xA9, 0xE2, 0xCC, 0xB6, 0xA9, 0xD7, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0xA9, 0xD8, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xA9, 0xD6, 0x00, 0x00, 0xA9, 0xEE, 0xA9, 0xE6, /* 0xC4-0xC7 */
+ 0xA9, 0xE0, 0xA9, 0xD4, 0xCC, 0xB9, 0xA9, 0xDF, /* 0xC8-0xCB */
+ 0xA9, 0xD5, 0xA9, 0xE7, 0xA9, 0xF0, 0xCE, 0xD4, /* 0xCC-0xCF */
+ 0xA9, 0xE4, 0xCC, 0xB5, 0xA9, 0xDA, 0xA9, 0xDD, /* 0xD0-0xD3 */
+ 0xA9, 0xDE, 0x00, 0x00, 0xA9, 0xEC, 0xA9, 0xED, /* 0xD4-0xD7 */
+ 0xA9, 0xEB, 0xA9, 0xE5, 0xA9, 0xE9, 0xA9, 0xDB, /* 0xD8-0xDB */
+ 0xAB, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCE, 0xDA, /* 0xE8-0xEB */
+ 0xAC, 0x41, 0xAB, 0xF8, 0xAB, 0xFA, 0xAC, 0x40, /* 0xEC-0xEF */
+ 0xCE, 0xE6, 0xAB, 0xFD, 0xD1, 0xB1, 0xAE, 0xB1, /* 0xF0-0xF3 */
+ 0xAC, 0x43, 0xCE, 0xD7, 0xCE, 0xDF, 0xAB, 0xFE, /* 0xF4-0xF7 */
+ 0xCE, 0xDE, 0xCE, 0xDB, 0xCE, 0xE3, 0xCE, 0xE5, /* 0xF8-0xFB */
+ 0xAB, 0xF7, 0xAB, 0xFB, 0xAC, 0x42, 0xAE, 0xB3, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_63[512] = {
+ 0xCE, 0xE0, 0xAB, 0xF9, 0xAC, 0x45, 0xCE, 0xD9, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAB, 0xFC, /* 0x04-0x07 */
+ 0xAE, 0xB2, 0xAB, 0xF6, 0x00, 0x00, 0xCE, 0xD6, /* 0x08-0x0B */
+ 0xCE, 0xDD, 0xCE, 0xD5, 0xCE, 0xD8, 0xCE, 0xDC, /* 0x0C-0x0F */
+ 0xD1, 0xB2, 0xAC, 0x44, 0x00, 0x00, 0xCE, 0xE1, /* 0x10-0x13 */
+ 0xCE, 0xE2, 0xCE, 0xE4, 0xAB, 0xF5, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0xAE, 0xC1, 0xD1, 0xBE, 0xAE, 0xBF, 0xAE, 0xC0, /* 0x28-0x2B */
+ 0xD1, 0xB4, 0xD1, 0xC4, 0x00, 0x00, 0xAE, 0xB6, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0xD5, 0x66, 0xD1, 0xC6, /* 0x30-0x33 */
+ 0xD1, 0xC0, 0x00, 0x00, 0xD1, 0xB7, 0x00, 0x00, /* 0x34-0x37 */
+ 0xD1, 0xC9, 0xD1, 0xBA, 0xAE, 0xBC, 0xD5, 0x7D, /* 0x38-0x3B */
+ 0xD1, 0xBD, 0xAE, 0xBE, 0xAE, 0xB5, 0x00, 0x00, /* 0x3C-0x3F */
+ 0xD1, 0xCB, 0xD1, 0xBF, 0xAE, 0xB8, 0xD1, 0xB8, /* 0x40-0x43 */
+ 0xD1, 0xB5, 0xD1, 0xB6, 0xAE, 0xB9, 0xD1, 0xC5, /* 0x44-0x47 */
+ 0xD1, 0xCC, 0xAE, 0xBB, 0xD1, 0xBC, 0xD1, 0xBB, /* 0x48-0x4B */
+ 0xAE, 0xC3, 0xAE, 0xC2, 0xAE, 0xB4, 0xAE, 0xBA, /* 0x4C-0x4F */
+ 0xAE, 0xBD, 0xD1, 0xC8, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0xD1, 0xC2, 0xAE, 0xB7, 0xD1, 0xB3, 0xD1, 0xCA, /* 0x54-0x57 */
+ 0xD1, 0xC1, 0xD1, 0xC3, 0xD1, 0xC7, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0xD5, 0x67, 0x00, 0x00, 0xB1, 0xB7, /* 0x64-0x67 */
+ 0xB1, 0xCB, 0xB1, 0xCA, 0x00, 0x00, 0xB1, 0xBF, /* 0x68-0x6B */
+ 0x00, 0x00, 0xD5, 0x79, 0xD5, 0x75, 0xD5, 0x72, /* 0x6C-0x6F */
+ 0xD5, 0xA6, 0xB1, 0xBA, 0xB1, 0xB2, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0xD5, 0x77, 0xB4, 0xA8, 0xB1, 0xB6, /* 0x74-0x77 */
+ 0xD5, 0xA1, 0x00, 0x00, 0xB1, 0xCC, 0xB1, 0xC9, /* 0x78-0x7B */
+ 0xD5, 0x7B, 0xD5, 0x6A, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0xB1, 0xC8, 0xD5, 0xA3, 0xD5, 0x69, 0xB1, 0xBD, /* 0x80-0x83 */
+ 0xB1, 0xC1, 0xD5, 0xA2, 0x00, 0x00, 0xD5, 0x73, /* 0x84-0x87 */
+ 0xB1, 0xC2, 0xB1, 0xBC, 0xD5, 0x68, 0x00, 0x00, /* 0x88-0x8B */
+ 0xB4, 0x78, 0xD5, 0xA5, 0xD5, 0x71, 0xB1, 0xC7, /* 0x8C-0x8F */
+ 0xD5, 0x74, 0xD5, 0xA4, 0xB1, 0xC6, 0x00, 0x00, /* 0x90-0x93 */
+ 0xD9, 0x52, 0x00, 0x00, 0xB1, 0xB3, 0xD5, 0x6F, /* 0x94-0x97 */
+ 0xB1, 0xB8, 0xB1, 0xC3, 0x00, 0x00, 0xB1, 0xBE, /* 0x98-0x9B */
+ 0xD5, 0x78, 0xD5, 0x6E, 0xD5, 0x6C, 0xD5, 0x7E, /* 0x9C-0x9F */
+ 0xB1, 0xB0, 0xB1, 0xC4, 0xB1, 0xB4, 0xB4, 0x77, /* 0xA0-0xA3 */
+ 0xD5, 0x7C, 0xB1, 0xB5, 0x00, 0x00, 0xB1, 0xB1, /* 0xA4-0xA7 */
+ 0xB1, 0xC0, 0xB1, 0xBB, 0xB1, 0xB9, 0xD5, 0x70, /* 0xA8-0xAB */
+ 0xB1, 0xC5, 0xD5, 0x6D, 0xD5, 0x7A, 0xD5, 0x76, /* 0xAC-0xAF */
+ 0xD9, 0x54, 0xD9, 0x53, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0xD5, 0x6B, 0xD9, 0x64, 0x00, 0x00, /* 0xBC-0xBF */
+ 0xB4, 0x7A, 0x00, 0x00, 0xD9, 0x6A, 0xD9, 0x59, /* 0xC0-0xC3 */
+ 0xD9, 0x67, 0xDD, 0x77, 0xB4, 0x7D, 0xD9, 0x6B, /* 0xC4-0xC7 */
+ 0xD9, 0x6E, 0xB4, 0x7C, 0xD9, 0x5C, 0xD9, 0x6D, /* 0xC8-0xCB */
+ 0xD9, 0x6C, 0xB4, 0x7E, 0xD9, 0x55, 0xB4, 0x79, /* 0xCC-0xCF */
+ 0xB4, 0xA3, 0x00, 0x00, 0xB4, 0xA1, 0xD9, 0x69, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0xD9, 0x5F, 0xB4, 0xA5, 0xD9, 0x70, /* 0xD4-0xD7 */
+ 0xD9, 0x68, 0xD9, 0x71, 0xB4, 0xAD, 0xB4, 0xAB, /* 0xD8-0xDB */
+ 0xD9, 0x66, 0xD9, 0x65, 0x00, 0x00, 0xD9, 0x63, /* 0xDC-0xDF */
+ 0xD9, 0x5D, 0xB4, 0xA4, 0x00, 0x00, 0xB4, 0xA2, /* 0xE0-0xE3 */
+ 0xD1, 0xB9, 0xD9, 0x56, 0x00, 0x00, 0xDD, 0xB7, /* 0xE4-0xE7 */
+ 0xD9, 0x57, 0xB4, 0x7B, 0xB4, 0xAA, 0xDD, 0x79, /* 0xE8-0xEB */
+ 0x00, 0x00, 0xB4, 0xA6, 0xB4, 0xA7, 0xD9, 0x58, /* 0xEC-0xEF */
+ 0xD9, 0x6F, 0xDD, 0x78, 0xD9, 0x60, 0xD9, 0x5B, /* 0xF0-0xF3 */
+ 0xB4, 0xA9, 0xD9, 0x61, 0xD9, 0x5E, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xB4, 0xAE, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_64[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0xB7, 0x70, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0xDD, 0x7C, 0xDD, 0xB1, 0xDD, 0xB6, /* 0x08-0x0B */
+ 0xDD, 0xAA, 0xB7, 0x6C, 0xDD, 0xBB, 0xB7, 0x69, /* 0x0C-0x0F */
+ 0xDD, 0x7A, 0x00, 0x00, 0xDD, 0x7B, 0xB7, 0x62, /* 0x10-0x13 */
+ 0xB7, 0x6B, 0xDD, 0xA4, 0xB7, 0x6E, 0xB7, 0x6F, /* 0x14-0x17 */
+ 0xDD, 0xA5, 0x00, 0x00, 0xDD, 0xB2, 0xDD, 0xB8, /* 0x18-0x1B */
+ 0xB7, 0x6A, 0x00, 0x00, 0xB7, 0x64, 0xDD, 0xA3, /* 0x1C-0x1F */
+ 0xDD, 0x7D, 0xDD, 0xBA, 0xDD, 0xA8, 0xDD, 0xA9, /* 0x20-0x23 */
+ 0xDD, 0x7E, 0xDD, 0xB4, 0xDD, 0xAB, 0xDD, 0xB5, /* 0x24-0x27 */
+ 0xDD, 0xAD, 0x00, 0x00, 0xB7, 0x65, 0xE1, 0xD9, /* 0x28-0x2B */
+ 0xB7, 0x68, 0xB7, 0x66, 0xDD, 0xB9, 0xDD, 0xB0, /* 0x2C-0x2F */
+ 0xDD, 0xAC, 0x00, 0x00, 0x00, 0x00, 0xDD, 0xA1, /* 0x30-0x33 */
+ 0xBA, 0x53, 0xDD, 0xAF, 0xB7, 0x6D, 0xDD, 0xA7, /* 0x34-0x37 */
+ 0x00, 0x00, 0xDD, 0xA6, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0xB7, 0x67, 0xB7, 0x63, 0xE1, 0xEE, /* 0x3C-0x3F */
+ 0xDD, 0xB3, 0xDD, 0xAE, 0x00, 0x00, 0xDD, 0xA2, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE1, 0xE9, /* 0x48-0x4B */
+ 0x00, 0x00, 0xE1, 0xDA, 0xE1, 0xE5, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xE1, 0xEC, 0xBA, 0x51, 0xB4, 0xAC, 0xE1, 0xEA, /* 0x50-0x53 */
+ 0xBA, 0x4C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0xBA, 0x4B, 0xE1, 0xF1, 0x00, 0x00, 0xE1, 0xDB, /* 0x58-0x5B */
+ 0xE1, 0xE8, 0xE1, 0xDC, 0xE1, 0xE7, 0xBA, 0x4F, /* 0x5C-0x5F */
+ 0xE1, 0xEB, 0xD9, 0x62, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0xE1, 0xF2, 0xE1, 0xE3, 0xBA, 0x52, /* 0x64-0x67 */
+ 0xE5, 0xBA, 0xBC, 0xAF, 0x00, 0x00, 0xE1, 0xF0, /* 0x68-0x6B */
+ 0xE1, 0xEF, 0xBA, 0x54, 0xE5, 0xAD, 0xBC, 0xB0, /* 0x6C-0x6F */
+ 0xE5, 0xAE, 0x00, 0x00, 0xE1, 0xDF, 0xE1, 0xE0, /* 0x70-0x73 */
+ 0xE1, 0xDD, 0xE1, 0xE2, 0xE1, 0xDE, 0xE1, 0xF3, /* 0x74-0x77 */
+ 0xBA, 0x4E, 0xBC, 0xB1, 0xBA, 0x50, 0xBA, 0x55, /* 0x78-0x7B */
+ 0x00, 0x00, 0xE1, 0xE1, 0x00, 0x00, 0xE1, 0xED, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0xE1, 0xE6, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0xE5, 0xB1, 0x00, 0x00, 0xBA, 0x4A, /* 0x84-0x87 */
+ 0xBC, 0xB4, 0xE9, 0xAA, 0xE5, 0xB6, 0xE5, 0xB5, /* 0x88-0x8B */
+ 0xE5, 0xB7, 0x00, 0x00, 0x00, 0x00, 0xE5, 0xB4, /* 0x8C-0x8F */
+ 0xBC, 0xB5, 0x00, 0x00, 0xBC, 0xBB, 0xBC, 0xB8, /* 0x90-0x93 */
+ 0x00, 0x00, 0xBC, 0xB9, 0xE5, 0xAF, 0xE5, 0xB2, /* 0x94-0x97 */
+ 0xE5, 0xBC, 0xBC, 0xC1, 0xBC, 0xBF, 0x00, 0x00, /* 0x98-0x9B */
+ 0xE5, 0xB3, 0xD9, 0x5A, 0xBC, 0xB2, 0xE5, 0xB9, /* 0x9C-0x9F */
+ 0xE5, 0xB0, 0x00, 0x00, 0xBC, 0xC2, 0xE5, 0xB8, /* 0xA0-0xA3 */
+ 0xBA, 0x4D, 0xBC, 0xB7, 0xE1, 0xE4, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0xBC, 0xBA, 0x00, 0x00, 0xBC, 0xBE, /* 0xA8-0xAB */
+ 0xBC, 0xC0, 0xBC, 0xBD, 0xBC, 0xBC, 0x00, 0x00, /* 0xAC-0xAF */
+ 0xBC, 0xB6, 0xE5, 0xBB, 0xBC, 0xB3, 0xBC, 0xC3, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0xD8, /* 0xB8-0xBB */
+ 0xBE, 0xD9, 0xE9, 0xA9, 0xBE, 0xE2, 0xBE, 0xDF, /* 0xBC-0xBF */
+ 0x00, 0x00, 0xBE, 0xD6, 0xBE, 0xDD, 0xE9, 0xAB, /* 0xC0-0xC3 */
+ 0xBE, 0xDB, 0xBE, 0xD5, 0x00, 0x00, 0xBE, 0xDC, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xE9, 0xA8, 0xC0, 0xBB, 0xBE, 0xD7, /* 0xC8-0xCB */
+ 0x00, 0x00, 0xBE, 0xDE, 0xC0, 0xBA, 0xE9, 0xA7, /* 0xCC-0xCF */
+ 0xE9, 0xA6, 0x00, 0x00, 0xBE, 0xE0, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xBE, 0xE1, 0x00, 0x00, 0xE9, 0xA5, 0xE9, 0xA4, /* 0xD4-0xD7 */
+ 0xC0, 0xBC, 0xE9, 0xAE, 0xBE, 0xDA, 0xE9, 0xAC, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0xC0, 0xBD, 0x00, 0x00, 0xC0, 0xC2, 0xEC, 0xEA, /* 0xE0-0xE3 */
+ 0xEC, 0xEC, 0x00, 0x00, 0xC0, 0xBF, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xEC, 0xED, 0xEC, 0xE9, 0x00, 0x00, 0xEC, 0xEB, /* 0xE8-0xEB */
+ 0xC0, 0xC0, 0xC0, 0xC3, 0x00, 0x00, 0xEC, 0xE8, /* 0xEC-0xEF */
+ 0xC0, 0xBE, 0xC0, 0xC1, 0xC2, 0x59, 0xE9, 0xAD, /* 0xF0-0xF3 */
+ 0xC2, 0x58, 0x00, 0x00, 0x00, 0x00, 0xC2, 0x5E, /* 0xF4-0xF7 */
+ 0xEF, 0xD4, 0x00, 0x00, 0xC2, 0x5C, 0xC2, 0x5D, /* 0xF8-0xFB */
+ 0xEF, 0xD7, 0xEF, 0xD3, 0xC2, 0x5A, 0xEF, 0xD1, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_65[512] = {
+ 0xC3, 0x6B, 0xEF, 0xD5, 0x00, 0x00, 0xEF, 0xD6, /* 0x00-0x03 */
+ 0xEF, 0xD2, 0x00, 0x00, 0xC2, 0x5B, 0xF2, 0x42, /* 0x04-0x07 */
+ 0x00, 0x00, 0xF2, 0x45, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0xF2, 0x46, 0xF2, 0x44, 0xF2, 0x47, 0xC3, 0x6C, /* 0x0C-0x0F */
+ 0xF2, 0x43, 0x00, 0x00, 0x00, 0x00, 0xF4, 0x4E, /* 0x10-0x13 */
+ 0xC4, 0x64, 0xF4, 0x4D, 0xF4, 0x4C, 0xF4, 0x4B, /* 0x14-0x17 */
+ 0xC4, 0x63, 0xC4, 0x65, 0x00, 0x00, 0xF5, 0xCD, /* 0x18-0x1B */
+ 0xC4, 0xE2, 0xC4, 0xE1, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0xF6, 0xE1, 0xF6, 0xE0, 0xF6, 0xE3, 0xC5, 0xCB, /* 0x20-0x23 */
+ 0xC5, 0x75, 0xF7, 0xDD, 0xF6, 0xE2, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0xF7, 0xDC, 0xC5, 0xCD, 0xC5, 0xCC, /* 0x28-0x2B */
+ 0xC5, 0xF3, 0xF8, 0xA9, 0xF8, 0xEF, 0xA4, 0xE4, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0xD9, 0x72, 0xE9, 0xAF, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0xA6, 0xAC, 0xCA, 0xF7, /* 0x34-0x37 */
+ 0xA7, 0xF1, 0xA7, 0xEF, 0x00, 0x00, 0xA7, 0xF0, /* 0x38-0x3B */
+ 0x00, 0x00, 0xCC, 0xC1, 0xA9, 0xF1, 0xAC, 0x46, /* 0x3C-0x3F */
+ 0x00, 0x00, 0xCE, 0xE7, 0x00, 0x00, 0xCE, 0xE8, /* 0x40-0x43 */
+ 0x00, 0x00, 0xAC, 0x47, 0xD1, 0xCE, 0x00, 0x00, /* 0x44-0x47 */
+ 0xAE, 0xC4, 0xAE, 0xC5, 0xD1, 0xCD, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB1, 0xD3, /* 0x4C-0x4F */
+ 0x00, 0x00, 0xB1, 0xCF, 0x00, 0x00, 0xD5, 0xA7, /* 0x50-0x53 */
+ 0xB1, 0xD6, 0xB1, 0xD5, 0xB1, 0xCE, 0xB1, 0xD1, /* 0x54-0x57 */
+ 0xB1, 0xD4, 0xB1, 0xD0, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0xD9, 0x76, 0xB1, 0xCD, 0xB4, 0xAF, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0xB4, 0xB1, 0xB4, 0xB2, /* 0x60-0x63 */
+ 0xD9, 0x75, 0xD9, 0x78, 0xB4, 0xB0, 0xD9, 0x73, /* 0x64-0x67 */
+ 0xD9, 0x77, 0x00, 0x00, 0xD9, 0x74, 0x00, 0x00, /* 0x68-0x6B */
+ 0xB7, 0x71, 0x00, 0x00, 0x00, 0x00, 0xDD, 0xBC, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0xBA, 0x56, 0xE1, 0xF4, /* 0x70-0x73 */
+ 0xBE, 0xE3, 0xBC, 0xC4, 0xE5, 0xBD, 0xBC, 0xC5, /* 0x74-0x77 */
+ 0xBC, 0xC6, 0xE5, 0xBF, 0xE5, 0xBE, 0xE5, 0xC0, /* 0x78-0x7B */
+ 0xE9, 0xB1, 0x00, 0x00, 0x00, 0x00, 0xE9, 0xB0, /* 0x7C-0x7F */
+
+ 0xEC, 0xEF, 0xEC, 0xEE, 0xC0, 0xC4, 0xC0, 0xC5, /* 0x80-0x83 */
+ 0xF2, 0x48, 0x00, 0x00, 0x00, 0x00, 0xA4, 0xE5, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0xD9, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0xB4, 0xB4, 0xB4, 0xB3, 0xDD, 0xBD, 0x00, 0x00, /* 0x90-0x93 */
+ 0xEF, 0xD8, 0xC4, 0xE3, 0xF7, 0xDE, 0xA4, 0xE6, /* 0x94-0x97 */
+ 0x00, 0x00, 0xAE, 0xC6, 0x00, 0x00, 0xB1, 0xD8, /* 0x98-0x9B */
+ 0xB1, 0xD7, 0xD9, 0x7A, 0xD9, 0x7B, 0xB7, 0x72, /* 0x9C-0x9F */
+ 0xE1, 0xF5, 0xBA, 0x57, 0xE9, 0xB2, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0xA4, 0xE7, 0xA5, 0xB8, 0x00, 0x00, 0xA9, 0xF2, /* 0xA4-0xA7 */
+ 0xCC, 0xC2, 0x00, 0x00, 0xCE, 0xE9, 0xAC, 0x48, /* 0xA8-0xAB */
+ 0xB1, 0xD9, 0x00, 0x00, 0xD9, 0x7C, 0xB4, 0xB5, /* 0xAC-0xAF */
+ 0xB7, 0x73, 0x00, 0x00, 0xE5, 0xC1, 0xE5, 0xC2, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0xEC, 0xF0, 0xC2, 0x5F, /* 0xB4-0xB7 */
+ 0xF8, 0xF0, 0xA4, 0xE8, 0x00, 0x00, 0xCC, 0xC3, /* 0xB8-0xBB */
+ 0xA9, 0xF3, 0xAC, 0x49, 0x00, 0x00, 0xCE, 0xEA, /* 0xBC-0xBF */
+ 0x00, 0x00, 0xAE, 0xC7, 0xD1, 0xD2, 0xD1, 0xD0, /* 0xC0-0xC3 */
+ 0xD1, 0xD1, 0xAE, 0xC8, 0xD1, 0xCF, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB1, 0xDB, /* 0xC8-0xCB */
+ 0xB1, 0xDC, 0xD5, 0xA8, 0xB1, 0xDD, 0xB1, 0xDA, /* 0xCC-0xCF */
+ 0xD9, 0x7D, 0x00, 0x00, 0xD9, 0x7E, 0xDD, 0xBE, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0xBA, 0x59, 0xBA, 0x58, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0xEC, 0xF1, 0xEF, 0xD9, /* 0xD8-0xDB */
+ 0x00, 0x00, 0xF2, 0x4A, 0xF2, 0x49, 0xF4, 0x4F, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xC9, 0x5E, 0xAC, 0x4A, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0xA4, 0xE9, 0xA5, 0xB9, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xA6, 0xAE, 0xA6, 0xAD, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0xA6, 0xAF, 0xA6, 0xB0, 0xC9, 0xEE, 0xC9, 0xED, /* 0xEC-0xEF */
+ 0xCA, 0xF8, 0xA7, 0xF2, 0xCA, 0xFB, 0xCA, 0xFA, /* 0xF0-0xF3 */
+ 0xCA, 0xF9, 0xCA, 0xFC, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0xA9, 0xF4, 0xCC, 0xC9, /* 0xF8-0xFB */
+ 0xCC, 0xC5, 0xCC, 0xCE, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_66[512] = {
+ 0xA9, 0xFB, 0x00, 0x00, 0xA9, 0xF9, 0xCC, 0xCA, /* 0x00-0x03 */
+ 0xCC, 0xC6, 0xCC, 0xCD, 0xA9, 0xF8, 0xAA, 0x40, /* 0x04-0x07 */
+ 0xCC, 0xC8, 0xCC, 0xC4, 0xA9, 0xFE, 0xCC, 0xCB, /* 0x08-0x0B */
+ 0xA9, 0xF7, 0xCC, 0xCC, 0xA9, 0xFA, 0xA9, 0xFC, /* 0x0C-0x0F */
+ 0xCC, 0xD0, 0xCC, 0xCF, 0xCC, 0xC7, 0xA9, 0xF6, /* 0x10-0x13 */
+ 0xA9, 0xF5, 0xA9, 0xFD, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0xCE, 0xEF, 0xCE, 0xF5, 0x00, 0x00, 0xAC, 0x50, /* 0x1C-0x1F */
+ 0xAC, 0x4D, 0xCE, 0xEC, 0xCE, 0xF1, 0x00, 0x00, /* 0x20-0x23 */
+ 0xAC, 0x53, 0xAC, 0x4B, 0xCE, 0xF0, 0xAC, 0x4E, /* 0x24-0x27 */
+ 0xAC, 0x51, 0x00, 0x00, 0x00, 0x00, 0xCE, 0xF3, /* 0x28-0x2B */
+ 0x00, 0x00, 0xAC, 0x4C, 0xCE, 0xF8, 0xAC, 0x4F, /* 0x2C-0x2F */
+ 0x00, 0x00, 0xAC, 0x52, 0xCE, 0xED, 0xCE, 0xF2, /* 0x30-0x33 */
+ 0xCE, 0xF6, 0xCE, 0xEE, 0xCE, 0xEB, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0xCE, 0xF7, 0xCE, 0xF4, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0xAE, 0xD0, 0xAE, 0xC9, 0xAE, 0xCC, /* 0x40-0x43 */
+ 0x00, 0x00, 0xAE, 0xCF, 0x00, 0x00, 0xD1, 0xD5, /* 0x44-0x47 */
+ 0x00, 0x00, 0xAE, 0xCA, 0xD1, 0xD3, 0x00, 0x00, /* 0x48-0x4B */
+ 0xAE, 0xCE, 0x00, 0x00, 0x00, 0x00, 0xAE, 0xCB, /* 0x4C-0x4F */
+ 0x00, 0x00, 0xD1, 0xD6, 0xAE, 0xCD, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0xD5, 0xAC, 0xB1, 0xDF, 0xD5, 0xAB, /* 0x58-0x5B */
+ 0xD5, 0xAD, 0xB1, 0xDE, 0xB1, 0xE3, 0xD1, 0xD4, /* 0x5C-0x5F */
+ 0x00, 0x00, 0xD5, 0xAA, 0xD5, 0xAE, 0x00, 0x00, /* 0x60-0x63 */
+ 0xB1, 0xE0, 0xD5, 0xA9, 0xB1, 0xE2, 0x00, 0x00, /* 0x64-0x67 */
+ 0xB1, 0xE1, 0x00, 0x00, 0xD9, 0xA7, 0x00, 0x00, /* 0x68-0x6B */
+ 0xD9, 0xA2, 0x00, 0x00, 0xB4, 0xB6, 0xB4, 0xBA, /* 0x6C-0x6F */
+ 0xB4, 0xB7, 0xD9, 0xA5, 0xD9, 0xA8, 0x00, 0x00, /* 0x70-0x73 */
+ 0xB4, 0xB8, 0x00, 0x00, 0xB4, 0xB9, 0xB4, 0xBE, /* 0x74-0x77 */
+ 0xDD, 0xC7, 0xD9, 0xA6, 0xB4, 0xBC, 0xD9, 0xA3, /* 0x78-0x7B */
+ 0xD9, 0xA1, 0x00, 0x00, 0xB4, 0xBD, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0xD9, 0xA4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0xB7, 0x79, 0x00, 0x00, 0xDD, 0xBF, 0xB7, 0x76, /* 0x84-0x87 */
+ 0xB7, 0x77, 0xB7, 0x75, 0xDD, 0xC4, 0xDD, 0xC3, /* 0x88-0x8B */
+ 0xDD, 0xC0, 0xB7, 0x7B, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0xDD, 0xC2, 0xB4, 0xBB, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0xDD, 0xC6, 0xDD, 0xC1, 0xB7, 0x78, 0xB7, 0x74, /* 0x94-0x97 */
+ 0xB7, 0x7A, 0xDD, 0xC5, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0xBA, 0x5C, 0x00, 0x00, 0xE1, 0xF8, /* 0x9C-0x9F */
+ 0xE1, 0xF7, 0xE1, 0xF6, 0xBA, 0x5A, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xBA, 0x5B, 0xE5, 0xC5, 0xE5, 0xC8, 0xBC, 0xC8, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0xBC, 0xC7, 0xE5, 0xC9, /* 0xAC-0xAF */
+ 0xE5, 0xC4, 0xBC, 0xCA, 0xE5, 0xC6, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0xBC, 0xC9, 0xE5, 0xC3, 0x00, 0x00, 0xE5, 0xC7, /* 0xB4-0xB7 */
+ 0xBE, 0xE9, 0xBE, 0xE6, 0xE9, 0xBB, 0xE9, 0xBA, /* 0xB8-0xBB */
+ 0x00, 0x00, 0xE9, 0xB9, 0xE9, 0xB4, 0x00, 0x00, /* 0xBC-0xBF */
+ 0xE9, 0xB5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xBE, 0xE7, 0x00, 0x00, 0xBE, 0xE4, 0xBE, 0xE8, /* 0xC4-0xC7 */
+ 0xE9, 0xB3, 0xBE, 0xE5, 0xE9, 0xB6, 0xE9, 0xB7, /* 0xC8-0xCB */
+ 0xE9, 0xBC, 0x00, 0x00, 0x00, 0x00, 0xE9, 0xB8, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0xEC, 0xF2, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC7, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xEF, 0xDC, 0xC0, 0xC6, 0xEF, 0xDA, 0xEF, 0xDB, /* 0xD8-0xDB */
+ 0xC2, 0x60, 0xC3, 0x6E, 0xF2, 0x4B, 0x00, 0x00, /* 0xDC-0xDF */
+ 0xC3, 0x6D, 0x00, 0x00, 0x00, 0x00, 0xF4, 0x51, /* 0xE0-0xE3 */
+ 0xF4, 0x52, 0x00, 0x00, 0xC4, 0x66, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xF4, 0x50, 0xC4, 0xE4, 0x00, 0x00, 0xF7, 0xDF, /* 0xE8-0xEB */
+ 0xC5, 0xCE, 0xF8, 0xAA, 0xF8, 0xAB, 0x00, 0x00, /* 0xEC-0xEF */
+ 0xA4, 0xEA, 0x00, 0x00, 0xA6, 0xB1, 0xA6, 0xB2, /* 0xF0-0xF3 */
+ 0xA7, 0xF3, 0x00, 0x00, 0xCC, 0xD1, 0xAC, 0x54, /* 0xF4-0xF7 */
+ 0xAE, 0xD1, 0xB1, 0xE4, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0xB0, 0xD2, 0x00, 0x00, 0xB4, 0xBF, 0xB4, 0xC0, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_67[512] = {
+ 0xB3, 0xCC, 0xD9, 0xA9, 0x00, 0x00, 0xB7, 0x7C, /* 0x00-0x03 */
+ 0xE1, 0xFA, 0xE1, 0xF9, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0xA4, 0xEB, 0xA6, 0xB3, 0xCC, 0xD2, 0xAA, 0x42, /* 0x08-0x0B */
+ 0x00, 0x00, 0xAA, 0x41, 0x00, 0x00, 0xCE, 0xF9, /* 0x0C-0x0F */
+ 0xCE, 0xFA, 0x00, 0x00, 0xD1, 0xD7, 0xD1, 0xD8, /* 0x10-0x13 */
+ 0xAE, 0xD2, 0xAE, 0xD3, 0x00, 0x00, 0xAE, 0xD4, /* 0x14-0x17 */
+ 0xD5, 0xAF, 0x00, 0x00, 0x00, 0x00, 0xB1, 0xE6, /* 0x18-0x1B */
+ 0x00, 0x00, 0xB4, 0xC2, 0x00, 0x00, 0xB4, 0xC1, /* 0x1C-0x1F */
+ 0xDD, 0xC8, 0xDF, 0x7A, 0xE1, 0xFB, 0xE9, 0xBD, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0xC2, 0x61, 0xC4, 0x67, /* 0x24-0x27 */
+ 0xA4, 0xEC, 0x00, 0x00, 0xA5, 0xBC, 0xA5, 0xBD, /* 0x28-0x2B */
+ 0xA5, 0xBB, 0xA5, 0xBE, 0xA5, 0xBA, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0xA6, 0xB6, 0x00, 0x00, 0xC9, 0xF6, /* 0x30-0x33 */
+ 0xA6, 0xB5, 0xA6, 0xB7, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0xC9, 0xF1, 0xC9, 0xF0, 0xC9, 0xF3, 0xC9, 0xF2, /* 0x38-0x3B */
+ 0xC9, 0xF5, 0xA6, 0xB4, 0xC9, 0xEF, 0xC9, 0xF4, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0xCA, 0xFD, 0xA7, 0xFD, 0xCA, 0xFE, /* 0x44-0x47 */
+ 0xCB, 0x43, 0xA7, 0xFC, 0x00, 0x00, 0xCB, 0x47, /* 0x48-0x4B */
+ 0xCB, 0x42, 0xCB, 0x45, 0xA7, 0xF5, 0xA7, 0xF6, /* 0x4C-0x4F */
+ 0xA7, 0xF7, 0xA7, 0xF8, 0x00, 0x00, 0xA8, 0x40, /* 0x50-0x53 */
+ 0x00, 0x00, 0xCB, 0x41, 0xA7, 0xFA, 0xA8, 0x41, /* 0x54-0x57 */
+ 0x00, 0x00, 0xCB, 0x40, 0xCB, 0x46, 0x00, 0x00, /* 0x58-0x5B */
+ 0xA7, 0xF9, 0xCB, 0x44, 0xA7, 0xFB, 0xA7, 0xF4, /* 0x5C-0x5F */
+ 0xA7, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0xAA, 0x57, 0x00, 0x00, /* 0x68-0x6B */
+ 0xCC, 0xD4, 0xAA, 0x43, 0x00, 0x00, 0xAA, 0x4D, /* 0x6C-0x6F */
+ 0xAA, 0x4E, 0xAA, 0x46, 0xAA, 0x58, 0xAA, 0x48, /* 0x70-0x73 */
+ 0xCC, 0xDC, 0xAA, 0x53, 0xCC, 0xD7, 0xAA, 0x49, /* 0x74-0x77 */
+ 0xCC, 0xE6, 0xCC, 0xE7, 0xCC, 0xDF, 0xCC, 0xD8, /* 0x78-0x7B */
+ 0xAA, 0x56, 0xCC, 0xE4, 0xAA, 0x51, 0xAA, 0x4F, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0xCC, 0xE5, 0x00, 0x00, 0xCC, 0xE3, /* 0x80-0x83 */
+ 0xCC, 0xDB, 0xCC, 0xD3, 0xCC, 0xDA, 0xAA, 0x4A, /* 0x84-0x87 */
+ 0x00, 0x00, 0xAA, 0x50, 0x00, 0x00, 0xAA, 0x44, /* 0x88-0x8B */
+ 0xCC, 0xDE, 0xCC, 0xDD, 0xCC, 0xD5, 0x00, 0x00, /* 0x8C-0x8F */
+ 0xAA, 0x52, 0xCC, 0xE1, 0xCC, 0xD6, 0xAA, 0x55, /* 0x90-0x93 */
+ 0xCC, 0xE8, 0xAA, 0x45, 0x00, 0x00, 0xAA, 0x4C, /* 0x94-0x97 */
+ 0xCC, 0xD9, 0xCC, 0xE2, 0xAA, 0x54, 0x00, 0x00, /* 0x98-0x9B */
+ 0xAA, 0x47, 0xAA, 0x4B, 0x00, 0x00, 0xCC, 0xE0, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0xCF, 0x5B, 0xAC, 0x5C, /* 0xAC-0xAF */
+ 0xAC, 0x69, 0x00, 0x00, 0xCF, 0x56, 0xCF, 0x4C, /* 0xB0-0xB3 */
+ 0xAC, 0x62, 0xCF, 0x4A, 0xAC, 0x5B, 0xCF, 0x45, /* 0xB4-0xB7 */
+ 0xAC, 0x65, 0xCF, 0x52, 0xCE, 0xFE, 0xCF, 0x41, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0xCF, 0x44, 0xCE, 0xFB, 0xCF, 0x51, 0xCF, 0x61, /* 0xC0-0xC3 */
+ 0xAC, 0x60, 0xCF, 0x46, 0xCF, 0x58, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0xCE, 0xFD, 0xCF, 0x5F, 0xCF, 0x60, 0xCF, 0x63, /* 0xC8-0xCB */
+ 0xCF, 0x5A, 0xCF, 0x4B, 0xCF, 0x53, 0xAC, 0x66, /* 0xCC-0xCF */
+ 0xAC, 0x59, 0xAC, 0x61, 0xAC, 0x6D, 0xAC, 0x56, /* 0xD0-0xD3 */
+ 0xAC, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xCF, 0x43, 0xAC, 0x6A, 0xAC, 0x63, 0xCF, 0x5D, /* 0xD8-0xDB */
+ 0xCF, 0x40, 0xAC, 0x6C, 0xAC, 0x67, 0xCF, 0x49, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0xAC, 0x6B, 0xCF, 0x50, /* 0xE0-0xE3 */
+ 0xCF, 0x48, 0xAC, 0x64, 0xCF, 0x5C, 0xCF, 0x54, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0xAC, 0x5E, 0xCF, 0x62, 0xCF, 0x47, /* 0xE8-0xEB */
+ 0xAC, 0x5A, 0xCF, 0x59, 0xCF, 0x4F, 0xAC, 0x5F, /* 0xEC-0xEF */
+ 0xCF, 0x55, 0xAC, 0x57, 0xCE, 0xFC, 0xAC, 0x68, /* 0xF0-0xF3 */
+ 0xAE, 0xE3, 0xAC, 0x5D, 0xCF, 0x4E, 0xCF, 0x4D, /* 0xF4-0xF7 */
+ 0xCF, 0x42, 0x00, 0x00, 0xCF, 0x5E, 0x00, 0x00, /* 0xF8-0xFB */
+ 0xCF, 0x57, 0x00, 0x00, 0x00, 0x00, 0xAC, 0x55, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_68[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0xD1, 0xEC, 0xAE, 0xEA, /* 0x10-0x13 */
+ 0xD1, 0xED, 0x00, 0x00, 0xD1, 0xE1, 0xAE, 0xDF, /* 0x14-0x17 */
+ 0xAE, 0xEB, 0x00, 0x00, 0xD1, 0xDA, 0x00, 0x00, /* 0x18-0x1B */
+ 0xD1, 0xE3, 0xD1, 0xEB, 0x00, 0x00, 0xD1, 0xD9, /* 0x1C-0x1F */
+ 0xD1, 0xF4, 0xAE, 0xD5, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0xD1, 0xF3, 0xD1, 0xEE, 0x00, 0x00, /* 0x24-0x27 */
+ 0xD1, 0xEF, 0xAE, 0xDD, 0xAE, 0xE8, 0xD1, 0xE5, /* 0x28-0x2B */
+ 0x00, 0x00, 0xD1, 0xE6, 0xD1, 0xF0, 0xD1, 0xE7, /* 0x2C-0x2F */
+ 0x00, 0x00, 0xD1, 0xE2, 0xD1, 0xDC, 0xD1, 0xDD, /* 0x30-0x33 */
+ 0xD1, 0xEA, 0xD1, 0xE4, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0xAE, 0xD6, 0xAE, 0xDA, 0xD1, 0xF2, 0xD1, 0xDE, /* 0x38-0x3B */
+ 0xAE, 0xE6, 0xAE, 0xE2, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0xAE, 0xE5, 0xAE, 0xEC, 0xAE, 0xDB, 0xAE, 0xE7, /* 0x40-0x43 */
+ 0xD1, 0xE9, 0xAE, 0xE9, 0xAE, 0xD8, 0x00, 0x00, /* 0x44-0x47 */
+ 0xAE, 0xD7, 0xD1, 0xDB, 0x00, 0x00, 0xD1, 0xDF, /* 0x48-0x4B */
+ 0xAE, 0xE0, 0xD1, 0xF1, 0xD1, 0xE8, 0xD1, 0xE0, /* 0x4C-0x4F */
+ 0xAE, 0xE4, 0xAE, 0xE1, 0x00, 0x00, 0xAE, 0xD9, /* 0x50-0x53 */
+ 0xAE, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD5, 0xC4, /* 0x68-0x6B */
+ 0x00, 0x00, 0xD5, 0xB4, 0xD5, 0xB5, 0xD5, 0xB9, /* 0x6C-0x6F */
+ 0x00, 0x00, 0xD5, 0xC8, 0xD5, 0xC5, 0x00, 0x00, /* 0x70-0x73 */
+ 0xD5, 0xBE, 0xD5, 0xBD, 0xB1, 0xED, 0xD5, 0xC1, /* 0x74-0x77 */
+ 0xD5, 0xD0, 0xD5, 0xB0, 0x00, 0x00, 0xD5, 0xD1, /* 0x78-0x7B */
+ 0xD5, 0xC3, 0xD5, 0xD5, 0xD5, 0xC9, 0xB1, 0xEC, /* 0x7C-0x7F */
+
+ 0xD5, 0xC7, 0xB1, 0xE7, 0xB1, 0xFC, 0xB1, 0xF2, /* 0x80-0x83 */
+ 0x00, 0x00, 0xB1, 0xF6, 0xB1, 0xF5, 0xD5, 0xB1, /* 0x84-0x87 */
+ 0x00, 0x00, 0xD5, 0xCE, 0xD5, 0xD4, 0xD5, 0xCC, /* 0x88-0x8B */
+ 0xD5, 0xD3, 0x00, 0x00, 0x00, 0x00, 0xD5, 0xC0, /* 0x8C-0x8F */
+ 0xD5, 0xB2, 0xD5, 0xD2, 0xD5, 0xC2, 0xB1, 0xEA, /* 0x90-0x93 */
+ 0xB1, 0xF7, 0x00, 0x00, 0xD5, 0xCB, 0xB1, 0xF0, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD5, 0xCA, /* 0x98-0x9B */
+ 0xD5, 0xB3, 0xB1, 0xF8, 0x00, 0x00, 0xB1, 0xFA, /* 0x9C-0x9F */
+ 0xD5, 0xCD, 0xB1, 0xFB, 0xB1, 0xE9, 0xD5, 0xBA, /* 0xA0-0xA3 */
+ 0xD5, 0xCF, 0x00, 0x00, 0x00, 0x00, 0xB1, 0xEF, /* 0xA4-0xA7 */
+ 0xB1, 0xF9, 0xD5, 0xBC, 0xD5, 0xC6, 0xD5, 0xB7, /* 0xA8-0xAB */
+ 0xD5, 0xBB, 0xB1, 0xF4, 0xD5, 0xB6, 0xB1, 0xE8, /* 0xAC-0xAF */
+ 0xB1, 0xF1, 0xB1, 0xEE, 0xD5, 0xBF, 0xAE, 0xDE, /* 0xB0-0xB3 */
+ 0xD9, 0xC0, 0xB1, 0xEB, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xB1, 0xF3, 0x00, 0x00, 0xD9, 0xC3, 0xD9, 0xD9, /* 0xC4-0xC7 */
+ 0xD9, 0xCE, 0xB4, 0xD6, 0x00, 0x00, 0xB4, 0xD1, /* 0xC8-0xCB */
+ 0xD9, 0xBD, 0xB4, 0xD2, 0xD9, 0xCD, 0x00, 0x00, /* 0xCC-0xCF */
+ 0xD9, 0xC6, 0xD9, 0xD3, 0xB4, 0xCE, 0xD9, 0xAB, /* 0xD0-0xD3 */
+ 0xD9, 0xD5, 0xB4, 0xC4, 0xD9, 0xB3, 0xB4, 0xC7, /* 0xD4-0xD7 */
+ 0xB4, 0xC6, 0x00, 0x00, 0xB4, 0xD7, 0x00, 0x00, /* 0xD8-0xDB */
+ 0xD9, 0xAD, 0xD9, 0xCF, 0xD9, 0xD0, 0xB4, 0xC9, /* 0xDC-0xDF */
+ 0xB4, 0xC5, 0xD9, 0xBB, 0x00, 0x00, 0xB4, 0xD0, /* 0xE0-0xE3 */
+ 0xD9, 0xB6, 0x00, 0x00, 0xD9, 0xD1, 0xB4, 0xCC, /* 0xE4-0xE7 */
+ 0xD9, 0xC9, 0xD9, 0xD6, 0xD9, 0xB0, 0xD9, 0xB5, /* 0xE8-0xEB */
+ 0xD9, 0xAF, 0x00, 0x00, 0xB4, 0xCB, 0xD9, 0xC2, /* 0xEC-0xEF */
+ 0xDD, 0xDE, 0xD9, 0xB1, 0xB4, 0xCF, 0xD9, 0xBA, /* 0xF0-0xF3 */
+ 0xD9, 0xD2, 0xB4, 0xCA, 0xD9, 0xB7, 0xD9, 0xB4, /* 0xF4-0xF7 */
+ 0xD9, 0xC5, 0xB4, 0xCD, 0xB4, 0xC3, 0xB4, 0xD9, /* 0xF8-0xFB */
+ 0xD9, 0xC8, 0xD9, 0xC7, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_69[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0xD9, 0xAC, 0xB4, 0xC8, 0xD9, 0xD4, 0xD9, 0xBC, /* 0x04-0x07 */
+ 0xD9, 0xBE, 0x00, 0x00, 0xD9, 0xCB, 0xD9, 0xCA, /* 0x08-0x0B */
+ 0xD9, 0xAA, 0xB4, 0xD3, 0xB4, 0xD5, 0xD9, 0xB2, /* 0x0C-0x0F */
+ 0xD9, 0xB9, 0xD9, 0xC1, 0xB4, 0xD4, 0xD9, 0xB8, /* 0x10-0x13 */
+ 0xD9, 0xC4, 0xD9, 0xD7, 0x00, 0x00, 0xD9, 0xCC, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0xD9, 0xD8, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xD9, 0xAE, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDD, 0xF2, /* 0x2C-0x2F */
+ 0xB7, 0xA6, 0x00, 0x00, 0xDD, 0xF0, 0xDD, 0xDB, /* 0x30-0x33 */
+ 0xDD, 0xE0, 0xDD, 0xD9, 0x00, 0x00, 0xDD, 0xEC, /* 0x34-0x37 */
+ 0xDD, 0xCB, 0xDD, 0xD2, 0x00, 0x00, 0xDD, 0xEA, /* 0x38-0x3B */
+ 0xDD, 0xF4, 0xDD, 0xDC, 0x00, 0x00, 0xDD, 0xCF, /* 0x3C-0x3F */
+ 0xDD, 0xE2, 0xDD, 0xE7, 0xDD, 0xD3, 0x00, 0x00, /* 0x40-0x43 */
+ 0xDD, 0xE4, 0xDD, 0xD0, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0xDD, 0xD7, 0xDD, 0xD8, 0xB7, 0xA8, 0xDD, 0xEB, /* 0x48-0x4B */
+ 0xDD, 0xE9, 0x00, 0x00, 0xDD, 0xCC, 0xDD, 0xEE, /* 0x4C-0x4F */
+ 0x00, 0x00, 0xDD, 0xEF, 0xDD, 0xF1, 0xB7, 0xAC, /* 0x50-0x53 */
+ 0xB7, 0xA4, 0x00, 0x00, 0xD5, 0xB8, 0xDD, 0xD4, /* 0x54-0x57 */
+ 0xDD, 0xE6, 0xDD, 0xD5, 0xB7, 0xA1, 0xB7, 0xB1, /* 0x58-0x5B */
+ 0xDD, 0xED, 0xB7, 0xAF, 0xB7, 0xAB, 0xDD, 0xCA, /* 0x5C-0x5F */
+ 0xB7, 0xA3, 0x00, 0x00, 0xDD, 0xCD, 0xB7, 0xB0, /* 0x60-0x63 */
+ 0x00, 0x00, 0xDD, 0xDD, 0xDD, 0xC9, 0x00, 0x00, /* 0x64-0x67 */
+ 0xB7, 0xA9, 0xDD, 0xE1, 0xDD, 0xD1, 0xB7, 0xAA, /* 0x68-0x6B */
+ 0xDD, 0xDA, 0xB7, 0x7E, 0xB4, 0xD8, 0xDD, 0xE3, /* 0x6C-0x6F */
+ 0xD9, 0xBF, 0xDD, 0xCE, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0xDD, 0xE8, 0xB7, 0xA5, 0xDD, 0xE5, 0xB7, 0xA2, /* 0x74-0x77 */
+ 0xDD, 0xDF, 0xB7, 0xAD, 0xDD, 0xD6, 0xDD, 0xF3, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0xB7, 0xA7, 0xDE, 0xC6, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0xB7, 0xAE, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0xE2, 0x4A, 0xE2, 0x48, 0x00, 0x00, /* 0x8C-0x8F */
+ 0xE2, 0x5E, 0xE2, 0x46, 0x00, 0x00, 0xE2, 0x58, /* 0x90-0x93 */
+ 0xB7, 0x7D, 0xBA, 0x5F, 0xE2, 0x42, 0xE2, 0x5D, /* 0x94-0x97 */
+ 0x00, 0x00, 0xE2, 0x47, 0xE2, 0x55, 0xBA, 0x64, /* 0x98-0x9B */
+ 0xBA, 0x5D, 0x00, 0x00, 0xE2, 0x5B, 0x00, 0x00, /* 0x9C-0x9F */
+ 0xE2, 0x40, 0xE2, 0x5A, 0x00, 0x00, 0xBA, 0x6F, /* 0xA0-0xA3 */
+ 0xE2, 0x51, 0xE2, 0x61, 0xBA, 0x6D, 0xE2, 0x49, /* 0xA4-0xA7 */
+ 0xBA, 0x5E, 0xE2, 0x4B, 0xE2, 0x59, 0xBA, 0x67, /* 0xA8-0xAB */
+ 0xE2, 0x44, 0xBA, 0x6B, 0xBA, 0x61, 0xE2, 0x4D, /* 0xAC-0xAF */
+ 0xE2, 0x43, 0xE1, 0xFC, 0x00, 0x00, 0xE2, 0x57, /* 0xB0-0xB3 */
+ 0xBA, 0x68, 0xE2, 0x60, 0xE1, 0xFD, 0xBA, 0x65, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xE2, 0x53, 0x00, 0x00, 0xBA, 0x66, /* 0xB8-0xBB */
+ 0xE2, 0x45, 0xE2, 0x50, 0xE2, 0x4C, 0xE2, 0x4E, /* 0xBC-0xBF */
+ 0x00, 0x00, 0xBA, 0x60, 0xE2, 0x5F, 0xBA, 0x6E, /* 0xC0-0xC3 */
+ 0xE2, 0x4F, 0x00, 0x00, 0xE2, 0x62, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xE1, 0xFE, 0xE2, 0x54, 0xBA, 0x63, /* 0xC8-0xCB */
+ 0xBA, 0x6C, 0xBA, 0x6A, 0xE2, 0x41, 0xE2, 0x56, /* 0xCC-0xCF */
+ 0xBA, 0x69, 0x00, 0x00, 0x00, 0x00, 0xBA, 0x62, /* 0xD0-0xD3 */
+ 0xE2, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0xE2, 0x5C, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0xE5, 0xD5, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0xE5, 0xD1, 0xE5, 0xCD, 0xE5, 0xE1, 0xE5, 0xDE, /* 0xE4-0xE7 */
+ 0xBC, 0xCD, 0x00, 0x00, 0x00, 0x00, 0xE5, 0xE5, /* 0xE8-0xEB */
+ 0xE5, 0xD4, 0xBC, 0xD8, 0xE5, 0xDB, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0xE5, 0xD0, 0xE5, 0xDA, 0xBC, 0xD5, /* 0xF0-0xF3 */
+ 0xE5, 0xEE, 0x00, 0x00, 0xE5, 0xEB, 0xE5, 0xDD, /* 0xF4-0xF7 */
+ 0xE5, 0xCE, 0x00, 0x00, 0x00, 0x00, 0xE5, 0xE2, /* 0xF8-0xFB */
+ 0xE5, 0xE4, 0xBC, 0xD1, 0xE5, 0xD8, 0xE5, 0xD3, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_6A[512] = {
+ 0xE5, 0xCA, 0xBC, 0xCE, 0xBC, 0xD6, 0x00, 0x00, /* 0x00-0x03 */
+ 0xE5, 0xE7, 0xBC, 0xD7, 0xE5, 0xCB, 0xE5, 0xED, /* 0x04-0x07 */
+ 0xE5, 0xE0, 0xE5, 0xE6, 0xBC, 0xD4, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0xE5, 0xE3, 0x00, 0x00, 0xE5, 0xEA, /* 0x0C-0x0F */
+ 0x00, 0x00, 0xBC, 0xD9, 0x00, 0x00, 0xBC, 0xD3, /* 0x10-0x13 */
+ 0xE5, 0xDC, 0xE5, 0xCF, 0xE5, 0xEF, 0xE5, 0xCC, /* 0x14-0x17 */
+ 0xE5, 0xE8, 0xBC, 0xD0, 0x00, 0x00, 0xE5, 0xD6, /* 0x18-0x1B */
+ 0x00, 0x00, 0xE5, 0xD7, 0xBC, 0xCF, 0xBC, 0xCC, /* 0x1C-0x1F */
+ 0xE5, 0xD2, 0xBC, 0xD2, 0x00, 0x00, 0xBC, 0xCB, /* 0x20-0x23 */
+ 0x00, 0x00, 0xE5, 0xE9, 0xE5, 0xEC, 0xE5, 0xD9, /* 0x24-0x27 */
+ 0xE9, 0xCA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0xC2, 0x00, 0x00, /* 0x30-0x33 */
+ 0xE9, 0xBE, 0xBE, 0xF6, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0xBE, 0xEB, 0xBE, 0xF0, 0xBE, 0xEC, 0xE9, 0xCC, /* 0x38-0x3B */
+ 0xE9, 0xD7, 0xBE, 0xEA, 0xE9, 0xC4, 0xE9, 0xCD, /* 0x3C-0x3F */
+ 0xE5, 0xDF, 0xE9, 0xCE, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0xBE, 0xF1, 0x00, 0x00, 0xE9, 0xDD, 0xBE, 0xF5, /* 0x44-0x47 */
+ 0xBE, 0xF8, 0xE9, 0xC0, 0x00, 0x00, 0xBE, 0xF4, /* 0x48-0x4B */
+ 0x00, 0x00, 0xE9, 0xDB, 0xE9, 0xDC, 0xE9, 0xD2, /* 0x4C-0x4F */
+ 0xE9, 0xD1, 0xE9, 0xC9, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0xE9, 0xD3, 0xE9, 0xDA, 0xE9, 0xD9, 0x00, 0x00, /* 0x54-0x57 */
+ 0xBE, 0xEF, 0xBE, 0xED, 0xE9, 0xCB, 0xE9, 0xC8, /* 0x58-0x5B */
+ 0x00, 0x00, 0xE9, 0xC5, 0xE9, 0xD8, 0xBE, 0xF7, /* 0x5C-0x5F */
+ 0xE9, 0xD6, 0xBE, 0xF3, 0xBE, 0xF2, 0x00, 0x00, /* 0x60-0x63 */
+ 0xE9, 0xD0, 0x00, 0x00, 0xE9, 0xBF, 0xE9, 0xC1, /* 0x64-0x67 */
+ 0xE9, 0xC3, 0xE9, 0xD5, 0xE9, 0xCF, 0xBE, 0xEE, /* 0x68-0x6B */
+ 0x00, 0x00, 0xE9, 0xC6, 0x00, 0x00, 0xE9, 0xD4, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0xC7, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0xC0, 0xCF, 0xED, 0x45, /* 0x7C-0x7F */
+
+ 0xC0, 0xC8, 0xEC, 0xF5, 0x00, 0x00, 0xED, 0x41, /* 0x80-0x83 */
+ 0xC0, 0xCA, 0xED, 0x48, 0x00, 0x00, 0xEC, 0xFC, /* 0x84-0x87 */
+ 0x00, 0x00, 0xEC, 0xF7, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0xED, 0x49, 0xEC, 0xF3, 0xEC, 0xFE, 0x00, 0x00, /* 0x8C-0x8F */
+ 0xC0, 0xD1, 0xED, 0x44, 0xED, 0x4A, 0xEC, 0xFD, /* 0x90-0x93 */
+ 0xC0, 0xC9, 0xED, 0x40, 0xEC, 0xF4, 0xC0, 0xD0, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0xED, 0x47, 0xEC, 0xF9, /* 0x98-0x9B */
+ 0xC0, 0xCC, 0x00, 0x00, 0xEC, 0xFB, 0xEC, 0xF8, /* 0x9C-0x9F */
+ 0xC0, 0xD2, 0xEC, 0xFA, 0xC0, 0xCB, 0xC0, 0xCE, /* 0xA0-0xA3 */
+ 0xED, 0x43, 0xEC, 0xF6, 0xED, 0x46, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xED, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0xC2, 0x63, 0xEF, 0xE7, 0xC2, 0x68, 0xC2, 0x69, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC2, 0x62, /* 0xB0-0xB3 */
+ 0xEF, 0xE6, 0x00, 0x00, 0xEF, 0xE3, 0xEF, 0xE4, /* 0xB4-0xB7 */
+ 0xC2, 0x66, 0xEF, 0xDE, 0xEF, 0xE2, 0xC2, 0x65, /* 0xB8-0xBB */
+ 0x00, 0x00, 0xEF, 0xDF, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0xC2, 0x67, 0xC2, 0x64, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0xEF, 0xDD, 0xEF, 0xE1, 0xEF, 0xE5, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF2, 0x51, /* 0xC8-0xCB */
+ 0xF2, 0x4E, 0xF2, 0x57, 0x00, 0x00, 0xF2, 0x56, /* 0xCC-0xCF */
+ 0xF2, 0x54, 0xF2, 0x4F, 0x00, 0x00, 0xC3, 0x72, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0xF2, 0x50, 0xC3, 0x71, 0xC0, 0xCD, /* 0xD8-0xDB */
+ 0xF2, 0x53, 0xC3, 0x70, 0xF2, 0x58, 0xF2, 0x52, /* 0xDC-0xDF */
+ 0xF2, 0x4D, 0xEF, 0xE0, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0xC3, 0x6F, 0x00, 0x00, 0xF2, 0x4C, /* 0xE4-0xE7 */
+ 0xF4, 0x56, 0x00, 0x00, 0xF4, 0x55, 0xF2, 0x55, /* 0xE8-0xEB */
+ 0xC4, 0x68, 0x00, 0x00, 0xF4, 0x59, 0xF4, 0x5A, /* 0xEC-0xEF */
+ 0xF4, 0x54, 0xF4, 0x58, 0x00, 0x00, 0xF4, 0x53, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0xF5, 0xD1, 0xF4, 0x57, 0xC4, 0xE7, 0xC4, 0xE5, /* 0xF8-0xFB */
+ 0xF5, 0xCF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_6B[512] = {
+ 0xF5, 0xD2, 0x00, 0x00, 0xF5, 0xCE, 0xF5, 0xD0, /* 0x00-0x03 */
+ 0xC4, 0xE6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0xF6, 0xE5, 0xF6, 0xE6, 0xC5, 0x76, 0xF6, 0xE4, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF7, 0xE2, /* 0x0C-0x0F */
+ 0xC5, 0xCF, 0xF7, 0xE0, 0xF7, 0xE1, 0xF8, 0xAC, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0xC6, 0x56, 0xF8, 0xF3, /* 0x14-0x17 */
+ 0xF8, 0xF1, 0xF8, 0xF2, 0xF8, 0xF4, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0xF9, 0xBB, 0x00, 0x00, /* 0x1C-0x1F */
+ 0xA4, 0xED, 0xA6, 0xB8, 0x00, 0x00, 0xAA, 0x59, /* 0x20-0x23 */
+ 0x00, 0x00, 0xCC, 0xE9, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0xCF, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0xD1, 0xF5, 0xD1, 0xF7, 0x00, 0x00, 0xD1, 0xF6, /* 0x2C-0x2F */
+ 0x00, 0x00, 0xD1, 0xF8, 0xB1, 0xFD, 0xD5, 0xD7, /* 0x30-0x33 */
+ 0xD1, 0xF9, 0x00, 0x00, 0xD5, 0xD6, 0xD5, 0xD8, /* 0x34-0x37 */
+ 0xD5, 0xD9, 0xD9, 0xDA, 0xB4, 0xDB, 0xD9, 0xDB, /* 0x38-0x3B */
+ 0xD9, 0xDD, 0xB4, 0xDC, 0xB4, 0xDA, 0xD9, 0xDC, /* 0x3C-0x3F */
+ 0x00, 0x00, 0xDD, 0xFA, 0xDD, 0xF8, 0xDD, 0xF7, /* 0x40-0x43 */
+ 0x00, 0x00, 0xDD, 0xF6, 0xDD, 0xF5, 0xB7, 0xB2, /* 0x44-0x47 */
+ 0xDD, 0xF9, 0xBA, 0x70, 0xE2, 0x63, 0xE2, 0x65, /* 0x48-0x4B */
+ 0xBA, 0x71, 0xE2, 0x64, 0xBC, 0xDB, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xBC, 0xDA, 0xE5, 0xF0, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0xE9, 0xDF, 0xE9, 0xDE, 0xE9, 0xE0, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0xBE, 0xF9, 0x00, 0x00, 0xED, 0x4B, /* 0x58-0x5B */
+ 0xC0, 0xD3, 0x00, 0x00, 0xEF, 0xE8, 0xC2, 0x6A, /* 0x5C-0x5F */
+ 0xF2, 0x59, 0xC5, 0x77, 0xA4, 0xEE, 0xA5, 0xBF, /* 0x60-0x63 */
+ 0xA6, 0xB9, 0xA8, 0x42, 0xAA, 0x5A, 0xAA, 0x5B, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0xAC, 0x6E, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0xD1, 0xFA, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0xB7, 0xB3, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0xE6, 0xD1, 0xBE, 0xFA, /* 0x74-0x77 */
+ 0xC2, 0x6B, 0xA4, 0xEF, 0x00, 0x00, 0xA6, 0xBA, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0xCC, 0xEB, 0xAA, 0x5C, /* 0x7C-0x7F */
+
+ 0xCC, 0xEA, 0x00, 0x00, 0xCF, 0x65, 0xAC, 0x6F, /* 0x80-0x83 */
+ 0xCF, 0x66, 0x00, 0x00, 0xAC, 0x70, 0x00, 0x00, /* 0x84-0x87 */
+ 0xD1, 0xFC, 0xAE, 0xEE, 0xAE, 0xED, 0x00, 0x00, /* 0x88-0x8B */
+ 0xD5, 0xDE, 0xD5, 0xDC, 0xD5, 0xDD, 0xD5, 0xDB, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xD5, 0xDA, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0xD9, 0xDE, 0xD9, 0xE1, 0xB4, 0xDE, 0xD9, 0xDF, /* 0x94-0x97 */
+ 0xB4, 0xDD, 0xD9, 0xE0, 0x00, 0x00, 0xDD, 0xFB, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0xE2, 0x66, 0xE2, 0x67, /* 0x9C-0x9F */
+ 0xE2, 0x68, 0x00, 0x00, 0xE5, 0xF3, 0xE5, 0xF2, /* 0xA0-0xA3 */
+ 0xBC, 0xDC, 0xE5, 0xF1, 0xE5, 0xF4, 0xE9, 0xE1, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0xE2, 0xE9, 0xE3, /* 0xA8-0xAB */
+ 0x00, 0x00, 0xED, 0x4C, 0xC0, 0xD4, 0xC2, 0x6C, /* 0xAC-0xAF */
+ 0xF2, 0x5A, 0x00, 0x00, 0xC4, 0xE8, 0xC9, 0x5F, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0xAC, 0x71, 0xCF, 0x67, 0xAE, 0xEF, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0xB1, 0xFE, 0x00, 0x00, /* 0xB8-0xBB */
+ 0xB4, 0xDF, 0xD9, 0xE2, 0x00, 0x00, 0xB7, 0xB5, /* 0xBC-0xBF */
+ 0xB7, 0xB4, 0x00, 0x00, 0x00, 0x00, 0xE2, 0x69, /* 0xC0-0xC3 */
+ 0xE2, 0x6A, 0xBC, 0xDD, 0xBC, 0xDE, 0xE9, 0xE5, /* 0xC4-0xC7 */
+ 0xE9, 0xE4, 0xEF, 0xE9, 0xF7, 0xE3, 0xA4, 0xF0, /* 0xC8-0xCB */
+ 0xC9, 0x60, 0xA5, 0xC0, 0x00, 0x00, 0xA8, 0x43, /* 0xCC-0xCF */
+ 0xCB, 0x48, 0x00, 0x00, 0xAC, 0x72, 0xB7, 0xB6, /* 0xD0-0xD3 */
+ 0xA4, 0xF1, 0x00, 0x00, 0xCF, 0x68, 0xAC, 0x73, /* 0xD4-0xD7 */
+ 0xCF, 0x69, 0x00, 0x00, 0xC0, 0xD5, 0xA4, 0xF2, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0xCC, 0xEC, 0x00, 0x00, /* 0xDC-0xDF */
+ 0xCF, 0x6A, 0x00, 0x00, 0xD2, 0x42, 0xD2, 0x41, /* 0xE0-0xE3 */
+ 0xD1, 0xFE, 0x00, 0x00, 0xD1, 0xFD, 0xD2, 0x43, /* 0xE4-0xE7 */
+ 0xD2, 0x40, 0x00, 0x00, 0x00, 0x00, 0xB2, 0x40, /* 0xE8-0xEB */
+ 0xB2, 0x41, 0x00, 0x00, 0x00, 0x00, 0xB4, 0xE0, /* 0xEC-0xEF */
+ 0xD9, 0xE3, 0x00, 0x00, 0xD9, 0xE4, 0xD9, 0xE5, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDE, 0x41, /* 0xF4-0xF7 */
+ 0xDE, 0x42, 0xDE, 0x40, 0x00, 0x00, 0xDD, 0xFD, /* 0xF8-0xFB */
+ 0xDD, 0xFE, 0xB7, 0xB7, 0xE2, 0x6B, 0xE5, 0xF7, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_6C[512] = {
+ 0xE5, 0xF6, 0xE5, 0xF5, 0xE5, 0xF8, 0xE9, 0xE7, /* 0x00-0x03 */
+ 0xE9, 0xE6, 0xBE, 0xFB, 0xE9, 0xE8, 0x00, 0x00, /* 0x04-0x07 */
+ 0xC0, 0xD6, 0xED, 0x4D, 0x00, 0x00, 0xEF, 0xEA, /* 0x08-0x0B */
+ 0xF2, 0x5B, 0xF6, 0xE7, 0x00, 0x00, 0xA4, 0xF3, /* 0x0C-0x0F */
+ 0xA5, 0xC2, 0xA5, 0xC1, 0x00, 0x00, 0xAA, 0x5D, /* 0x10-0x13 */
+ 0xC9, 0x61, 0xC9, 0x7E, 0xA6, 0xBB, 0x00, 0x00, /* 0x14-0x17 */
+ 0xC9, 0xF7, 0xCB, 0x49, 0xCB, 0x4A, 0xAA, 0x5E, /* 0x18-0x1B */
+ 0x00, 0x00, 0xCC, 0xED, 0x00, 0x00, 0xAC, 0x74, /* 0x1C-0x1F */
+ 0xCF, 0x6B, 0xCF, 0x6C, 0x00, 0x00, 0xAE, 0xF0, /* 0x20-0x23 */
+ 0xAE, 0xF4, 0xD2, 0x44, 0xAE, 0xF3, 0xAE, 0xF1, /* 0x24-0x27 */
+ 0xAE, 0xF2, 0x00, 0x00, 0xD5, 0xDF, 0xB2, 0x42, /* 0x28-0x2B */
+ 0xB4, 0xE3, 0x00, 0x00, 0xB4, 0xE1, 0xB4, 0xE2, /* 0x2C-0x2F */
+ 0xD9, 0xE6, 0x00, 0x00, 0x00, 0x00, 0xBA, 0x72, /* 0x30-0x33 */
+ 0xA4, 0xF4, 0x00, 0x00, 0xC9, 0xA1, 0x00, 0x00, /* 0x34-0x37 */
+ 0xA5, 0xC3, 0x00, 0x00, 0x00, 0x00, 0xC9, 0xA4, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0xA5, 0xC6, 0xC9, 0xA3, /* 0x3C-0x3F */
+ 0xA5, 0xC5, 0xA5, 0xC4, 0xA8, 0x44, 0xC9, 0xA2, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0xC9, 0xF8, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0xC9, 0xFC, 0xC9, 0xFE, /* 0x48-0x4B */
+ 0xCA, 0x40, 0xA6, 0xC5, 0xA6, 0xC6, 0xC9, 0xFB, /* 0x4C-0x4F */
+ 0xA6, 0xC1, 0x00, 0x00, 0xC9, 0xF9, 0x00, 0x00, /* 0x50-0x53 */
+ 0xC9, 0xFD, 0xA6, 0xC2, 0x00, 0x00, 0xA6, 0xBD, /* 0x54-0x57 */
+ 0x00, 0x00, 0xA6, 0xBE, 0x00, 0x00, 0xA6, 0xC4, /* 0x58-0x5B */
+ 0xC9, 0xFA, 0xA6, 0xBC, 0xA8, 0x45, 0xA6, 0xBF, /* 0x5C-0x5F */
+ 0xA6, 0xC0, 0xA6, 0xC3, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0xCB, 0x5B, 0xCB, 0x59, 0xCB, 0x4C, /* 0x64-0x67 */
+ 0xA8, 0x51, 0xCB, 0x53, 0xA8, 0x4C, 0xCB, 0x4D, /* 0x68-0x6B */
+ 0x00, 0x00, 0xCB, 0x55, 0x00, 0x00, 0xCB, 0x52, /* 0x6C-0x6F */
+ 0xA8, 0x4F, 0xCB, 0x51, 0xA8, 0x56, 0xCB, 0x5A, /* 0x70-0x73 */
+ 0xA8, 0x58, 0x00, 0x00, 0xA8, 0x5A, 0x00, 0x00, /* 0x74-0x77 */
+ 0xCB, 0x4B, 0x00, 0x00, 0xA8, 0x4D, 0xCB, 0x5C, /* 0x78-0x7B */
+ 0x00, 0x00, 0xA8, 0x54, 0xA8, 0x57, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0xCD, 0x45, 0xA8, 0x47, 0xA8, 0x5E, 0xA8, 0x55, /* 0x80-0x83 */
+ 0xCB, 0x4E, 0xA8, 0x4A, 0xA8, 0x59, 0xCB, 0x56, /* 0x84-0x87 */
+ 0xA8, 0x48, 0xA8, 0x49, 0xCD, 0x43, 0xCB, 0x4F, /* 0x88-0x8B */
+ 0xA8, 0x50, 0xA8, 0x5B, 0xCB, 0x5D, 0xCB, 0x50, /* 0x8C-0x8F */
+ 0xA8, 0x4E, 0x00, 0x00, 0xA8, 0x53, 0xCC, 0xEE, /* 0x90-0x93 */
+ 0xA8, 0x5C, 0xCB, 0x57, 0xA8, 0x52, 0x00, 0x00, /* 0x94-0x97 */
+ 0xA8, 0x5D, 0xA8, 0x46, 0xCB, 0x54, 0xA8, 0x4B, /* 0x98-0x9B */
+ 0xCB, 0x58, 0xCD, 0x44, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0x6A, /* 0xA8-0xAB */
+ 0xAA, 0x7A, 0xCC, 0xF5, 0xAA, 0x71, 0x00, 0x00, /* 0xAC-0xAF */
+ 0xCD, 0x4B, 0xAA, 0x62, 0x00, 0x00, 0xAA, 0x65, /* 0xB0-0xB3 */
+ 0xCD, 0x42, 0x00, 0x00, 0xCC, 0xF3, 0xCC, 0xF7, /* 0xB4-0xB7 */
+ 0xAA, 0x6D, 0xAA, 0x6F, 0xCC, 0xFA, 0xAA, 0x76, /* 0xB8-0xBB */
+ 0xAA, 0x68, 0xAA, 0x66, 0xAA, 0x67, 0xAA, 0x75, /* 0xBC-0xBF */
+ 0xCD, 0x47, 0xAA, 0x70, 0xCC, 0xF9, 0xCC, 0xFB, /* 0xC0-0xC3 */
+ 0xAA, 0x6E, 0xAA, 0x73, 0xCC, 0xFC, 0xCD, 0x4A, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xAC, 0x75, 0xAA, 0x79, 0x00, 0x00, /* 0xC8-0xCB */
+ 0xAA, 0x63, 0xCD, 0x49, 0x00, 0x00, 0xCD, 0x4D, /* 0xCC-0xCF */
+ 0xCC, 0xF8, 0xCD, 0x4F, 0xCD, 0x40, 0xAA, 0x6C, /* 0xD0-0xD3 */
+ 0xCC, 0xF4, 0xAA, 0x6B, 0xAA, 0x7D, 0xAA, 0x72, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0xCC, 0xF2, 0xCF, 0x75, 0xAA, 0x78, /* 0xD8-0xDB */
+ 0xAA, 0x7C, 0xCD, 0x41, 0xCD, 0x46, 0x00, 0x00, /* 0xDC-0xDF */
+ 0xAA, 0x7E, 0xAA, 0x77, 0xAA, 0x69, 0xAA, 0x5F, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0xAA, 0x64, 0x00, 0x00, 0xCC, 0xF6, /* 0xE4-0xE7 */
+ 0xAA, 0x60, 0xCD, 0x4E, 0x00, 0x00, 0xCC, 0xF0, /* 0xE8-0xEB */
+ 0xCC, 0xEF, 0xCC, 0xFD, 0xCC, 0xF1, 0xAA, 0x7B, /* 0xEC-0xEF */
+ 0xAE, 0xF5, 0xAA, 0x74, 0xCC, 0xFE, 0xAA, 0x61, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0xAC, 0xA6, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xCD, 0x4C, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+};
+
+static unsigned char u2c_6D[512] = {
+ 0xCF, 0x7C, 0xCF, 0xA1, 0x00, 0x00, 0xCF, 0xA4, /* 0x00-0x03 */
+ 0xCF, 0x77, 0x00, 0x00, 0x00, 0x00, 0xCF, 0xA7, /* 0x04-0x07 */
+ 0xCF, 0xAA, 0xCF, 0xAC, 0xCF, 0x74, 0xAC, 0x76, /* 0x08-0x0B */
+ 0xAC, 0x7B, 0xD2, 0x49, 0xAC, 0xAD, 0xCF, 0xA5, /* 0x0C-0x0F */
+ 0xCF, 0xAD, 0xCF, 0x7B, 0xCF, 0x73, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0xD2, 0x64, 0xAC, 0x7E, /* 0x14-0x17 */
+ 0xCF, 0xA2, 0xCF, 0x78, 0xCF, 0x7A, 0xAC, 0xA5, /* 0x18-0x1B */
+ 0x00, 0x00, 0xCF, 0x7D, 0xAC, 0x7D, 0xCF, 0x70, /* 0x1C-0x1F */
+ 0xCF, 0xA8, 0x00, 0x00, 0xCF, 0xAB, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0xAC, 0x7A, 0x00, 0x00, 0xAC, 0xA8, /* 0x24-0x27 */
+ 0xCF, 0x6D, 0xAC, 0xAA, 0xAC, 0x78, 0xAC, 0xAE, /* 0x28-0x2B */
+ 0xCF, 0xA9, 0xCF, 0x6F, 0xAC, 0xAB, 0xD2, 0x5E, /* 0x2C-0x2F */
+ 0xCD, 0x48, 0xAC, 0x7C, 0xAC, 0x77, 0xCF, 0x76, /* 0x30-0x33 */
+ 0xCF, 0x6E, 0xAC, 0xAC, 0xAC, 0xA4, 0xCF, 0xA3, /* 0x34-0x37 */
+ 0xAC, 0xA9, 0xAC, 0xA7, 0xCF, 0x79, 0xAC, 0xA1, /* 0x38-0x3B */
+ 0xCF, 0x71, 0xAC, 0xA2, 0xAC, 0xA3, 0xCF, 0x72, /* 0x3C-0x3F */
+ 0xCF, 0xA6, 0xAC, 0x79, 0xCF, 0x7E, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0xD2, 0x4C, 0xAE, 0xFD, 0xAF, 0x43, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0xD2, 0x55, 0xD2, 0x5B, /* 0x5C-0x5F */
+ 0xD2, 0x57, 0xD2, 0x4A, 0xD2, 0x4D, 0xD2, 0x46, /* 0x60-0x63 */
+ 0xD2, 0x47, 0xAF, 0x4A, 0xAE, 0xFA, 0xD2, 0x56, /* 0x64-0x67 */
+ 0xD2, 0x5F, 0xAF, 0x45, 0xAE, 0xF6, 0x00, 0x00, /* 0x68-0x6B */
+ 0xAF, 0x40, 0xD2, 0x4E, 0xAF, 0x42, 0xD2, 0x4F, /* 0x6C-0x6F */
+ 0xD2, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0xAF, 0x44, 0xD2, 0x68, 0xD2, 0x48, 0xAE, 0xFC, /* 0x74-0x77 */
+ 0xAE, 0xFB, 0xAF, 0x48, 0xD2, 0x45, 0xD2, 0x66, /* 0x78-0x7B */
+ 0xD2, 0x5A, 0xD2, 0x67, 0xD2, 0x61, 0xD2, 0x53, /* 0x7C-0x7F */
+
+ 0xD2, 0x62, 0x00, 0x00, 0xD2, 0x5C, 0xD2, 0x65, /* 0x80-0x83 */
+ 0xD2, 0x63, 0xAF, 0x49, 0xD2, 0x54, 0xAE, 0xF9, /* 0x84-0x87 */
+ 0xAE, 0xF8, 0xAF, 0x41, 0xAF, 0x47, 0xD2, 0x60, /* 0x88-0x8B */
+ 0xAF, 0x46, 0xD2, 0x51, 0xB2, 0x43, 0x00, 0x00, /* 0x8C-0x8F */
+ 0xD2, 0x69, 0xD2, 0x50, 0xD2, 0x4B, 0xAE, 0xFE, /* 0x90-0x93 */
+ 0xAF, 0x4B, 0xAE, 0xF7, 0x00, 0x00, 0xD2, 0x58, /* 0x94-0x97 */
+ 0xD2, 0x5D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0xB2, 0x65, 0xD5, 0xE1, /* 0xA8-0xAB */
+ 0xD5, 0xE5, 0x00, 0x00, 0xB2, 0x52, 0xB2, 0x50, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0xB2, 0x47, 0xD5, 0xE3, /* 0xB0-0xB3 */
+ 0xD5, 0xE2, 0xB2, 0x5B, 0x00, 0x00, 0xD5, 0xE8, /* 0xB4-0xB7 */
+ 0xB2, 0x55, 0x00, 0x00, 0xD5, 0xFA, 0xD6, 0x47, /* 0xB8-0xBB */
+ 0xB2, 0x44, 0xD5, 0xF7, 0xD5, 0xF0, 0xB2, 0x67, /* 0xBC-0xBF */
+ 0xD5, 0xE0, 0x00, 0x00, 0xD5, 0xFC, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xB2, 0x64, 0xB2, 0x58, 0xB2, 0x63, 0xB2, 0x4E, /* 0xC4-0xC7 */
+ 0xD5, 0xEC, 0xD5, 0xFE, 0xD5, 0xF6, 0xB2, 0x4F, /* 0xC8-0xCB */
+ 0xB2, 0x49, 0xD6, 0x45, 0x00, 0x00, 0xD5, 0xFD, /* 0xCC-0xCF */
+ 0xD6, 0x40, 0xB2, 0x51, 0xB2, 0x59, 0xD6, 0x42, /* 0xD0-0xD3 */
+ 0xD5, 0xEA, 0xD5, 0xFB, 0xD5, 0xEF, 0xD6, 0x44, /* 0xD4-0xD7 */
+ 0xB2, 0x5E, 0xB2, 0x46, 0xB2, 0x5C, 0xD5, 0xF4, /* 0xD8-0xDB */
+ 0xD5, 0xF2, 0xD5, 0xF3, 0xB2, 0x53, 0xD5, 0xEE, /* 0xDC-0xDF */
+ 0xD5, 0xED, 0xB2, 0x48, 0xD5, 0xE7, 0xD6, 0x46, /* 0xE0-0xE3 */
+ 0xB2, 0x4A, 0xD5, 0xF1, 0xB2, 0x68, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xB2, 0x62, 0xD5, 0xE6, 0xB2, 0x5F, 0xB2, 0x5D, /* 0xE8-0xEB */
+ 0xB2, 0x66, 0xD5, 0xF8, 0xB2, 0x61, 0xD2, 0x52, /* 0xEC-0xEF */
+ 0xD5, 0xF9, 0xB2, 0x60, 0xD6, 0x41, 0xB2, 0x45, /* 0xF0-0xF3 */
+ 0xD5, 0xF5, 0xB2, 0x57, 0xD5, 0xE9, 0xB2, 0x56, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xB2, 0x54, 0xB2, 0x4C, 0xB2, 0x4B, /* 0xF8-0xFB */
+ 0xD9, 0xE7, 0xD6, 0x43, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_6E[512] = {
+ 0xD5, 0xEB, 0x00, 0x00, 0x00, 0x00, 0xD9, 0xFC, /* 0x00-0x03 */
+ 0x00, 0x00, 0xB2, 0x4D, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0xB5, 0x41, 0xB2, 0x5A, 0xB4, 0xEE, /* 0x18-0x1B */
+ 0xD9, 0xF6, 0xB4, 0xFC, 0x00, 0x00, 0xD9, 0xEA, /* 0x1C-0x1F */
+ 0xB4, 0xEB, 0xB4, 0xE7, 0xDA, 0x49, 0xB4, 0xED, /* 0x20-0x23 */
+ 0xB4, 0xF1, 0xB4, 0xEC, 0xB4, 0xF5, 0xDA, 0x4D, /* 0x24-0x27 */
+ 0xDA, 0x44, 0x00, 0x00, 0x00, 0x00, 0xD9, 0xF1, /* 0x28-0x2B */
+ 0xB4, 0xFA, 0xB4, 0xF4, 0xD9, 0xFD, 0xB4, 0xE4, /* 0x2C-0x2F */
+ 0xDA, 0x4A, 0xDA, 0x43, 0xB4, 0xE8, 0xD9, 0xF7, /* 0x30-0x33 */
+ 0xB4, 0xF7, 0xDA, 0x55, 0xDA, 0x56, 0x00, 0x00, /* 0x34-0x37 */
+ 0xB4, 0xE5, 0xDA, 0x48, 0xB4, 0xF9, 0xD9, 0xFB, /* 0x38-0x3B */
+ 0xD9, 0xED, 0xD9, 0xEE, 0xB4, 0xFD, 0xD9, 0xF2, /* 0x3C-0x3F */
+ 0xD9, 0xF9, 0xD9, 0xF3, 0x00, 0x00, 0xB4, 0xFB, /* 0x40-0x43 */
+ 0xB5, 0x44, 0xD9, 0xEF, 0xD9, 0xE8, 0xD9, 0xE9, /* 0x44-0x47 */
+ 0x00, 0x00, 0xD9, 0xEB, 0xB4, 0xEA, 0xD9, 0xF8, /* 0x48-0x4B */
+ 0x00, 0x00, 0xB4, 0xF8, 0xB5, 0x42, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0xD9, 0xFA, 0xDA, 0x53, 0xDA, 0x4B, /* 0x50-0x53 */
+ 0xB4, 0xE6, 0xDA, 0x51, 0xB4, 0xF2, 0x00, 0x00, /* 0x54-0x57 */
+ 0xB4, 0xF0, 0x00, 0x00, 0xDA, 0x57, 0xB4, 0xEF, /* 0x58-0x5B */
+ 0xDA, 0x41, 0xD9, 0xF4, 0xD9, 0xFE, 0xB5, 0x47, /* 0x5C-0x5F */
+ 0xDA, 0x45, 0xDA, 0x42, 0xD9, 0xF0, 0xB5, 0x43, /* 0x60-0x63 */
+ 0xDA, 0x4F, 0xDA, 0x4C, 0xDA, 0x54, 0xB4, 0xE9, /* 0x64-0x67 */
+ 0xDA, 0x40, 0xB5, 0x46, 0x00, 0x00, 0xDA, 0x47, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0xB4, 0xF3, 0xB4, 0xF6, /* 0x6C-0x6F */
+ 0x00, 0x00, 0xDA, 0x46, 0xB5, 0x45, 0xD9, 0xF5, /* 0x70-0x73 */
+ 0xD5, 0xE4, 0x00, 0x00, 0x00, 0x00, 0xDA, 0x50, /* 0x74-0x77 */
+ 0xDA, 0x4E, 0xDA, 0x52, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0xD9, 0xEC, 0xB5, 0x40, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0xDE, 0x61, 0xDE, 0x60, 0xDE, 0x46, /* 0x8C-0x8F */
+ 0xB7, 0xBD, 0x00, 0x00, 0xDE, 0x5F, 0xDE, 0x49, /* 0x90-0x93 */
+ 0xDE, 0x4A, 0x00, 0x00, 0xB7, 0xC7, 0xDE, 0x68, /* 0x94-0x97 */
+ 0xB7, 0xC2, 0xDE, 0x5E, 0x00, 0x00, 0xDE, 0x43, /* 0x98-0x9B */
+ 0xB7, 0xC8, 0xB7, 0xBE, 0xDE, 0x52, 0xDE, 0x48, /* 0x9C-0x9F */
+ 0xDE, 0x4B, 0xDE, 0x63, 0xB7, 0xB8, 0xDE, 0x6A, /* 0xA0-0xA3 */
+ 0xDE, 0x62, 0xB7, 0xC1, 0xDE, 0x57, 0xB7, 0xCC, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0xB7, 0xCB, 0xB7, 0xC5, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0xDE, 0x69, 0xB7, 0xB9, /* 0xAC-0xAF */
+ 0xDE, 0x55, 0xDE, 0x4C, 0xDE, 0x59, 0xDE, 0x65, /* 0xB0-0xB3 */
+ 0xB7, 0xCD, 0x00, 0x00, 0xB7, 0xBB, 0xDE, 0x54, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xDE, 0x4D, 0xB7, 0xC4, 0x00, 0x00, /* 0xB8-0xBB */
+ 0xB7, 0xC3, 0xDE, 0x50, 0xDE, 0x5A, 0xDE, 0x64, /* 0xBC-0xBF */
+ 0xDE, 0x47, 0xDE, 0x51, 0xB7, 0xBC, 0xDE, 0x5B, /* 0xC0-0xC3 */
+ 0xB7, 0xC9, 0xB7, 0xC0, 0xDE, 0x4E, 0xB7, 0xBF, /* 0xC4-0xC7 */
+ 0xDE, 0x45, 0xDE, 0x53, 0xDE, 0x67, 0xB4, 0xFE, /* 0xC8-0xCB */
+ 0xBA, 0xB0, 0xDE, 0x56, 0xE2, 0x6C, 0xDE, 0x58, /* 0xCC-0xCF */
+ 0xDE, 0x66, 0xB7, 0xC6, 0xDE, 0x4F, 0xB7, 0xBA, /* 0xD0-0xD3 */
+ 0xB7, 0xCA, 0xBC, 0xF0, 0xDE, 0x44, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xDE, 0x5D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0xDE, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2, 0xAA, /* 0xE8-0xEB */
+ 0xBA, 0xAD, 0xE2, 0x7D, 0xE2, 0xA4, 0xBA, 0xA2, /* 0xEC-0xEF */
+ 0x00, 0x00, 0xE2, 0x6E, 0xBA, 0xAF, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xBA, 0x77, 0xE2, 0x6D, 0xE2, 0xB0, 0xBA, 0xB1, /* 0xF4-0xF7 */
+ 0xE2, 0x71, 0xE2, 0xA3, 0x00, 0x00, 0xE2, 0x73, /* 0xF8-0xFB */
+ 0xE2, 0xB3, 0xE2, 0xAF, 0xBA, 0x75, 0xBA, 0xA1, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_6F[512] = {
+ 0xE6, 0x53, 0xBA, 0xAE, 0xBA, 0x7D, 0xE2, 0x6F, /* 0x00-0x03 */
+ 0x00, 0x00, 0xE2, 0xAE, 0xBA, 0xA3, 0xE2, 0xAB, /* 0x04-0x07 */
+ 0xE2, 0xB8, 0xE2, 0x75, 0xE2, 0x7E, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0xE2, 0xB6, 0xE2, 0xAC, 0xBA, 0x7C, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0xE2, 0x7C, 0xBA, 0x76, /* 0x10-0x13 */
+ 0xBA, 0x74, 0xBA, 0xA8, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0xE2, 0x7A, 0xE2, 0x77, 0xE2, 0x78, 0x00, 0x00, /* 0x18-0x1B */
+ 0xE2, 0xB2, 0x00, 0x00, 0xE2, 0xB7, 0xE2, 0xB5, /* 0x1C-0x1F */
+ 0xBA, 0x7A, 0xE2, 0xB9, 0xBA, 0x7E, 0xBA, 0xA7, /* 0x20-0x23 */
+ 0x00, 0x00, 0xE2, 0x70, 0xE5, 0xFA, 0xE2, 0x79, /* 0x24-0x27 */
+ 0x00, 0x00, 0xBA, 0x78, 0xBA, 0xAC, 0xBA, 0xA9, /* 0x28-0x2B */
+ 0xBA, 0x7B, 0xE2, 0xA5, 0xE2, 0x74, 0xBA, 0xAA, /* 0x2C-0x2F */
+ 0xE2, 0xA7, 0xBA, 0xA4, 0xBA, 0xA6, 0xBA, 0x73, /* 0x30-0x33 */
+ 0x00, 0x00, 0xE2, 0xA9, 0xE2, 0xA1, 0xE2, 0x72, /* 0x34-0x37 */
+ 0xBA, 0xA5, 0xE2, 0xB1, 0xE2, 0xB4, 0xE2, 0x7B, /* 0x38-0x3B */
+ 0xE2, 0xA8, 0x00, 0x00, 0xBA, 0x79, 0xBC, 0xDF, /* 0x3C-0x3F */
+ 0xE2, 0xA6, 0xE5, 0xF9, 0x00, 0x00, 0xE2, 0xAD, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0xE2, 0x76, 0xE6, 0x44, /* 0x4C-0x4F */
+ 0xE6, 0x4E, 0xBC, 0xE2, 0xE6, 0x4D, 0xE6, 0x59, /* 0x50-0x53 */
+ 0xBC, 0xE4, 0xE6, 0x4B, 0x00, 0x00, 0xE6, 0x4F, /* 0x54-0x57 */
+ 0xBC, 0xEF, 0x00, 0x00, 0xE6, 0x46, 0xBC, 0xE7, /* 0x58-0x5B */
+ 0x00, 0x00, 0xE6, 0x52, 0xE9, 0xF0, 0xBC, 0xF3, /* 0x5C-0x5F */
+ 0xBC, 0xF2, 0xE6, 0x54, 0xE6, 0x43, 0xE6, 0x5E, /* 0x60-0x63 */
+ 0xBC, 0xED, 0x00, 0x00, 0xBC, 0xE3, 0xE6, 0x57, /* 0x64-0x67 */
+ 0x00, 0x00, 0xE6, 0x5B, 0xE6, 0x60, 0xE6, 0x55, /* 0x68-0x6B */
+ 0xE6, 0x49, 0xBC, 0xE6, 0xBC, 0xE9, 0xBC, 0xF1, /* 0x6C-0x6F */
+ 0xBC, 0xEC, 0x00, 0x00, 0xE6, 0x4C, 0xE2, 0xA2, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0xE6, 0x48, 0xE6, 0x5F, /* 0x74-0x77 */
+ 0xBC, 0xE8, 0x00, 0x00, 0xBC, 0xEB, 0xE6, 0x61, /* 0x78-0x7B */
+ 0xBC, 0xE0, 0xE6, 0x56, 0xE5, 0xFB, 0xE6, 0x5C, /* 0x7C-0x7F */
+
+ 0xC0, 0xDF, 0x00, 0x00, 0xE6, 0x4A, 0x00, 0x00, /* 0x80-0x83 */
+ 0xBC, 0xE1, 0xE6, 0x45, 0xBC, 0xE5, 0xE5, 0xFC, /* 0x84-0x87 */
+ 0xBA, 0xAB, 0xE6, 0x41, 0x00, 0x00, 0xE6, 0x5A, /* 0x88-0x8B */
+ 0xE6, 0x42, 0xE6, 0x40, 0xBC, 0xEA, 0x00, 0x00, /* 0x8C-0x8F */
+ 0xE6, 0x58, 0x00, 0x00, 0xE5, 0xFE, 0xE6, 0x51, /* 0x90-0x93 */
+ 0xE6, 0x50, 0xE6, 0x5D, 0xE6, 0x47, 0xBC, 0xEE, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0xE9, 0xF3, 0x00, 0x00, /* 0x9C-0x9F */
+ 0xBF, 0x49, 0xBE, 0xFE, 0xEA, 0x40, 0xE9, 0xEB, /* 0xA0-0xA3 */
+ 0xBF, 0x41, 0xE9, 0xF7, 0xBF, 0x48, 0xBF, 0x43, /* 0xA4-0xA7 */
+ 0xE9, 0xF5, 0xED, 0x4F, 0xE9, 0xFB, 0xEA, 0x42, /* 0xA8-0xAB */
+ 0xE9, 0xFA, 0xE9, 0xE9, 0xE9, 0xF8, 0xEA, 0x44, /* 0xAC-0xAF */
+ 0xEA, 0x46, 0xBE, 0xFD, 0xEA, 0x45, 0xBF, 0x44, /* 0xB0-0xB3 */
+ 0xBF, 0x4A, 0x00, 0x00, 0xBF, 0x47, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0xE9, 0xFE, 0xBF, 0x46, 0xE9, 0xF9, 0x00, 0x00, /* 0xB8-0xBB */
+ 0xE9, 0xED, 0xE9, 0xF2, 0x00, 0x00, 0xE9, 0xFD, /* 0xBC-0xBF */
+ 0xBF, 0x45, 0xBF, 0x42, 0xBE, 0xFC, 0xBF, 0x40, /* 0xC0-0xC3 */
+ 0xE9, 0xF1, 0x00, 0x00, 0xE5, 0xFD, 0xE9, 0xEC, /* 0xC4-0xC7 */
+ 0xE9, 0xEF, 0xEA, 0x41, 0xE9, 0xF4, 0xE9, 0xEA, /* 0xC8-0xCB */
+ 0xED, 0x4E, 0xEA, 0x43, 0xE9, 0xEE, 0xE9, 0xFC, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xED, 0x51, 0xC0, 0xE3, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xC0, 0xD7, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xDB, /* 0xD8-0xDB */
+ 0xED, 0x53, 0xED, 0x59, 0xED, 0x57, 0xC0, 0xD9, /* 0xDC-0xDF */
+ 0xC0, 0xDA, 0xC0, 0xE1, 0xED, 0x5A, 0xED, 0x52, /* 0xE0-0xE3 */
+ 0xC0, 0xDC, 0x00, 0x00, 0xED, 0x56, 0xED, 0x55, /* 0xE4-0xE7 */
+ 0xED, 0x5B, 0xC0, 0xE2, 0x00, 0x00, 0xC0, 0xDD, /* 0xE8-0xEB */
+ 0xC0, 0xE0, 0xED, 0x54, 0xC0, 0xE4, 0xC0, 0xDE, /* 0xEC-0xEF */
+ 0xC0, 0xE5, 0xC0, 0xD8, 0xED, 0x58, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xED, 0x50, 0x00, 0x00, 0x00, 0x00, 0xEF, 0xF7, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0xC2, 0x71, 0xEF, 0xF4, /* 0xF8-0xFB */
+ 0xEF, 0xF6, 0x00, 0x00, 0xC2, 0x6F, 0xEF, 0xF2, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_70[512] = {
+ 0xEF, 0xF3, 0xEF, 0xEE, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0xE9, 0xF6, 0xEF, 0xEF, 0xC2, 0x70, 0xEF, 0xEB, /* 0x04-0x07 */
+ 0x00, 0x00, 0xC2, 0x6D, 0xEF, 0xF8, 0xC2, 0x6E, /* 0x08-0x0B */
+ 0xEF, 0xEC, 0xEF, 0xED, 0xEF, 0xF1, 0xC2, 0x73, /* 0x0C-0x0F */
+ 0x00, 0x00, 0xC2, 0x72, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0xEF, 0xF0, 0xC3, 0x78, 0xF2, 0x5F, 0xF2, 0x65, /* 0x14-0x17 */
+ 0xC3, 0x79, 0xF2, 0x5C, 0xC3, 0x76, 0xC3, 0x73, /* 0x18-0x1B */
+ 0xF2, 0x67, 0xC3, 0x77, 0x00, 0x00, 0xC3, 0x74, /* 0x1C-0x1F */
+ 0xF2, 0x5E, 0xF2, 0x61, 0xF2, 0x62, 0xF2, 0x63, /* 0x20-0x23 */
+ 0xF2, 0x66, 0x00, 0x00, 0xEF, 0xF5, 0xF2, 0x5D, /* 0x24-0x27 */
+ 0xC3, 0x75, 0xF2, 0x64, 0xF2, 0x68, 0xF2, 0x60, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF4, 0x5D, /* 0x2C-0x2F */
+ 0xC4, 0x6A, 0xF4, 0x60, 0xC4, 0x6B, 0xF4, 0x68, /* 0x30-0x33 */
+ 0xF4, 0x5F, 0xF4, 0x5C, 0x00, 0x00, 0xF4, 0x5E, /* 0x34-0x37 */
+ 0xF4, 0x62, 0xF4, 0x65, 0xF4, 0x64, 0xF4, 0x67, /* 0x38-0x3B */
+ 0xF4, 0x5B, 0x00, 0x00, 0xC4, 0x69, 0xF4, 0x63, /* 0x3C-0x3F */
+ 0xF4, 0x66, 0xF4, 0x69, 0xF4, 0x61, 0xF5, 0xD3, /* 0x40-0x43 */
+ 0xF5, 0xD4, 0xF5, 0xD8, 0xF5, 0xD9, 0x00, 0x00, /* 0x44-0x47 */
+ 0xF5, 0xD6, 0xF5, 0xD7, 0xF5, 0xD5, 0x00, 0x00, /* 0x48-0x4B */
+ 0xC4, 0xE9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0xC5, 0x78, 0xF6, 0xEB, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0xF6, 0xE8, 0xF6, 0xE9, 0xF6, 0xEA, /* 0x54-0x57 */
+ 0xC5, 0x79, 0x00, 0x00, 0xF7, 0xE5, 0xF7, 0xE4, /* 0x58-0x5B */
+ 0x00, 0x00, 0xF8, 0xAF, 0xC5, 0xF4, 0xF8, 0xAD, /* 0x5C-0x5F */
+ 0xF8, 0xB0, 0xF8, 0xAE, 0xF8, 0xF5, 0xC6, 0x57, /* 0x60-0x63 */
+ 0xC6, 0x65, 0xF9, 0xA3, 0xF9, 0x6C, 0x00, 0x00, /* 0x64-0x67 */
+ 0xF9, 0xA2, 0xF9, 0xD0, 0xF9, 0xD1, 0xA4, 0xF5, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xA6, 0xC7, 0xCA, 0x41, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0xCB, 0x5E, 0x00, 0x00, 0xA8, 0x5F, 0x00, 0x00, /* 0x74-0x77 */
+ 0xA8, 0x62, 0x00, 0x00, 0xCB, 0x5F, 0x00, 0x00, /* 0x78-0x7B */
+ 0xA8, 0x60, 0xA8, 0x61, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0xCD, 0x58, 0xCD, 0x5A, /* 0x80-0x83 */
+ 0xCD, 0x55, 0xCD, 0x52, 0xCD, 0x54, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA4, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xCD, 0x56, 0xAA, 0xA3, 0xCD, 0x53, /* 0x90-0x93 */
+ 0xCD, 0x50, 0xAA, 0xA1, 0xCD, 0x57, 0x00, 0x00, /* 0x94-0x97 */
+ 0xCD, 0x51, 0xAA, 0xA5, 0xCD, 0x59, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCF, 0xAF, /* 0x9C-0x9F */
+ 0x00, 0x00, 0xCF, 0xB3, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0xAC, 0xB7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0xCF, 0xB6, 0x00, 0x00, 0xAC, 0xAF, /* 0xA8-0xAB */
+ 0xAC, 0xB2, 0xAC, 0xB4, 0xAC, 0xB6, 0xAC, 0xB3, /* 0xAC-0xAF */
+ 0xCF, 0xB2, 0xCF, 0xB1, 0x00, 0x00, 0xAC, 0xB1, /* 0xB0-0xB3 */
+ 0xCF, 0xB4, 0xCF, 0xB5, 0x00, 0x00, 0xCF, 0xAE, /* 0xB4-0xB7 */
+ 0xAC, 0xB5, 0x00, 0x00, 0xAC, 0xB0, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0xCF, 0xB0, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0xD2, 0x77, 0xD2, 0x78, 0xD2, 0x79, /* 0xC4-0xC7 */
+ 0xAF, 0x50, 0x00, 0x00, 0xAF, 0x4C, 0xD2, 0x6E, /* 0xC8-0xCB */
+ 0x00, 0x00, 0xD2, 0x76, 0xD2, 0x7B, 0xAF, 0x51, /* 0xCC-0xCF */
+ 0x00, 0x00, 0xD2, 0x6C, 0xD2, 0x72, 0xD2, 0x6B, /* 0xD0-0xD3 */
+ 0xD2, 0x75, 0x00, 0x00, 0x00, 0x00, 0xD2, 0x71, /* 0xD4-0xD7 */
+ 0xAF, 0x4D, 0xAF, 0x4F, 0xD2, 0x7A, 0x00, 0x00, /* 0xD8-0xDB */
+ 0xD2, 0x6A, 0xD2, 0x6D, 0xD2, 0x73, 0x00, 0x00, /* 0xDC-0xDF */
+ 0xD2, 0x74, 0xD2, 0x7C, 0xD2, 0x70, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0xAF, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB2, 0x6D, /* 0xEC-0xEF */
+ 0xD6, 0x4E, 0x00, 0x00, 0x00, 0x00, 0xD6, 0x50, /* 0xF0-0xF3 */
+ 0xD6, 0x4C, 0x00, 0x00, 0xD6, 0x58, 0xD6, 0x4A, /* 0xF4-0xF7 */
+ 0xD6, 0x57, 0xB2, 0x69, 0xD6, 0x48, 0xDA, 0x5B, /* 0xF8-0xFB */
+ 0xD6, 0x52, 0xB2, 0x6C, 0x00, 0x00, 0xD6, 0x53, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_71[512] = {
+ 0xD6, 0x56, 0x00, 0x00, 0xD6, 0x5A, 0x00, 0x00, /* 0x00-0x03 */
+ 0xD6, 0x4F, 0x00, 0x00, 0xD6, 0x54, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0xB2, 0x6A, 0xB2, 0x6B, 0xD6, 0x59, /* 0x08-0x0B */
+ 0xD6, 0x4D, 0xD6, 0x49, 0xD6, 0x5B, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xD6, 0x51, 0x00, 0x00, 0x00, 0x00, 0xD6, 0x55, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD6, 0x4B, /* 0x14-0x17 */
+ 0x00, 0x00, 0xB5, 0x48, 0xB5, 0x49, 0xDA, 0x65, /* 0x18-0x1B */
+ 0xB5, 0x4F, 0x00, 0x00, 0xDA, 0x59, 0xDA, 0x62, /* 0x1C-0x1F */
+ 0xDA, 0x58, 0xB5, 0x4C, 0xDA, 0x60, 0xDA, 0x5E, /* 0x20-0x23 */
+ 0x00, 0x00, 0xDA, 0x5F, 0xB5, 0x4A, 0x00, 0x00, /* 0x24-0x27 */
+ 0xDA, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0xDA, 0x5C, 0xDA, 0x5A, /* 0x2C-0x2F */
+ 0xB5, 0x4B, 0xDA, 0x5D, 0xDA, 0x61, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0xB5, 0x4D, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0xDA, 0x64, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0xDE, 0x70, 0xDE, 0x77, 0xDE, 0x79, /* 0x40-0x43 */
+ 0xDE, 0xA1, 0x00, 0x00, 0xB7, 0xDA, 0xDE, 0x6B, /* 0x44-0x47 */
+ 0x00, 0x00, 0xB7, 0xD2, 0x00, 0x00, 0xDE, 0x7A, /* 0x48-0x4B */
+ 0xB7, 0xD7, 0xDE, 0xA2, 0xB7, 0xCE, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xDE, 0x7D, 0x00, 0x00, 0xDE, 0x6D, 0xDE, 0x7E, /* 0x50-0x53 */
+ 0xDE, 0x6C, 0x00, 0x00, 0xB7, 0xDC, 0x00, 0x00, /* 0x54-0x57 */
+ 0xDE, 0x78, 0xB7, 0xCF, 0xDE, 0xA3, 0x00, 0x00, /* 0x58-0x5B */
+ 0xB7, 0xD4, 0xDE, 0x71, 0xB7, 0xD9, 0xDE, 0x7C, /* 0x5C-0x5F */
+ 0xDE, 0x6F, 0xDE, 0x76, 0xDE, 0x72, 0xDE, 0x6E, /* 0x60-0x63 */
+ 0xB7, 0xD1, 0xB7, 0xD8, 0xB7, 0xD6, 0xB7, 0xD3, /* 0x64-0x67 */
+ 0xB7, 0xDB, 0xB7, 0xD0, 0xDE, 0x75, 0x00, 0x00, /* 0x68-0x6B */
+ 0xB7, 0xD5, 0x00, 0x00, 0xB5, 0x4E, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xDE, 0x7B, 0x00, 0x00, 0xDE, 0x73, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0xDE, 0x74, 0x00, 0x00, 0x00, 0x00, 0xE2, 0xC1, /* 0x78-0x7B */
+ 0x00, 0x00, 0xBA, 0xB4, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0xE2, 0xBD, 0xE2, 0xC3, 0xE2, 0xBF, 0x00, 0x00, /* 0x80-0x83 */
+ 0xBA, 0xB6, 0xE2, 0xBE, 0xE2, 0xC2, 0xE2, 0xBA, /* 0x84-0x87 */
+ 0x00, 0x00, 0xE2, 0xBC, 0xBA, 0xB5, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2, 0xC0, /* 0x8C-0x8F */
+ 0xE2, 0xBB, 0x00, 0x00, 0xBA, 0xB7, 0x00, 0x00, /* 0x90-0x93 */
+ 0xBA, 0xB2, 0x00, 0x00, 0x00, 0x00, 0xE2, 0xC4, /* 0x94-0x97 */
+ 0x00, 0x00, 0xBA, 0xB3, 0xE6, 0x67, 0xE6, 0x64, /* 0x98-0x9B */
+ 0xE6, 0x70, 0xE6, 0x6A, 0xE6, 0x6C, 0xBC, 0xF4, /* 0x9C-0x9F */
+ 0xE6, 0x66, 0xE6, 0x6E, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0xE6, 0x6D, 0xE6, 0x6B, 0x00, 0x00, 0xE6, 0x71, /* 0xA4-0xA7 */
+ 0xBC, 0xF7, 0xE6, 0x68, 0xE6, 0x6F, 0x00, 0x00, /* 0xA8-0xAB */
+ 0xBC, 0xF5, 0x00, 0x00, 0x00, 0x00, 0xE6, 0x63, /* 0xAC-0xAF */
+ 0xE6, 0x65, 0xBC, 0xF6, 0xE6, 0x62, 0xE6, 0x72, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0xE6, 0x69, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0xEA, 0x4A, 0xBF, 0x51, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0xEA, 0x55, 0xEA, 0x53, 0xBF, 0x4B, 0xEA, 0x49, /* 0xBC-0xBF */
+ 0xEA, 0x4C, 0xEA, 0x4D, 0xEA, 0x48, 0xBF, 0x55, /* 0xC0-0xC3 */
+ 0xBF, 0x56, 0xEA, 0x47, 0xEA, 0x56, 0xEA, 0x51, /* 0xC4-0xC7 */
+ 0xBF, 0x4F, 0xBF, 0x4C, 0xEA, 0x50, 0xEA, 0x4E, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0xBF, 0x52, 0xEA, 0x52, /* 0xCC-0xCF */
+ 0xBF, 0x4D, 0x00, 0x00, 0xBF, 0x4E, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xEA, 0x4F, 0xBF, 0x50, 0xEA, 0x4B, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xEA, 0x54, 0xBF, 0x53, 0xEA, 0x57, 0xEA, 0x58, /* 0xD8-0xDB */
+ 0xBF, 0x54, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xE7, /* 0xDC-0xDF */
+ 0xC0, 0xEE, 0xED, 0x5C, 0xED, 0x62, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0xED, 0x60, 0xC0, 0xEA, 0xC0, 0xE9, 0xC0, 0xE6, /* 0xE4-0xE7 */
+ 0xED, 0x5E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0xC0, 0xEC, 0xC0, 0xEB, 0xC0, 0xE8, 0x00, 0x00, /* 0xEC-0xEF */
+ 0xED, 0x61, 0xED, 0x5D, 0xED, 0x5F, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xC0, 0xED, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0xC2, 0x77, 0xEF, 0xFB, 0x00, 0x00, 0xC2, 0x74, /* 0xF8-0xFB */
+ 0xC2, 0x75, 0xEF, 0xFD, 0xC2, 0x76, 0xEF, 0xFA, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_72[512] = {
+ 0x00, 0x00, 0xEF, 0xF9, 0xF2, 0x6C, 0xEF, 0xFC, /* 0x00-0x03 */
+ 0x00, 0x00, 0xF2, 0x6D, 0xC3, 0x7A, 0xF2, 0x6B, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0xF2, 0x6A, 0x00, 0x00, /* 0x08-0x0B */
+ 0xF2, 0x69, 0xC3, 0x7B, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xC4, 0x6C, 0x00, 0x00, 0x00, 0x00, 0xF4, 0x6A, /* 0x10-0x13 */
+ 0xF4, 0x6B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0xF5, 0xDC, 0xF5, 0xDB, 0xC4, 0xEA, /* 0x18-0x1B */
+ 0x00, 0x00, 0xF5, 0xDA, 0xF6, 0xEC, 0xF6, 0xED, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0xF7, 0xE6, 0xF8, 0xB1, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0xF8, 0xF6, 0xF9, 0xBC, /* 0x24-0x27 */
+ 0xC6, 0x79, 0xF9, 0xC6, 0xA4, 0xF6, 0x00, 0x00, /* 0x28-0x2B */
+ 0xAA, 0xA6, 0xAA, 0xA7, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xAC, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0xC0, 0xEF, 0xA4, 0xF7, 0x00, 0x00, /* 0x34-0x37 */
+ 0xAA, 0xA8, 0xAF, 0x52, 0xB7, 0xDD, 0xA4, 0xF8, /* 0x38-0x3B */
+ 0x00, 0x00, 0xB2, 0x6E, 0xBA, 0xB8, 0xC9, 0x62, /* 0x3C-0x3F */
+ 0x00, 0x00, 0xCF, 0xB7, 0xD2, 0x7D, 0x00, 0x00, /* 0x40-0x43 */
+ 0xE2, 0xC5, 0x00, 0x00, 0xC0, 0xF0, 0xA4, 0xF9, /* 0x44-0x47 */
+ 0xAA, 0xA9, 0xCF, 0xB8, 0xCF, 0xB9, 0xDA, 0x66, /* 0x48-0x4B */
+ 0xB5, 0x50, 0x00, 0x00, 0x00, 0x00, 0xDE, 0xA4, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0xB7, 0xDE, 0xE2, 0xC6, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0xBC, 0xF8, 0x00, 0x00, /* 0x54-0x57 */
+ 0xC3, 0x7C, 0xA4, 0xFA, 0xDA, 0x67, 0xA4, 0xFB, /* 0x58-0x5B */
+ 0x00, 0x00, 0xA6, 0xC9, 0xCA, 0x42, 0xA6, 0xC8, /* 0x5C-0x5F */
+ 0xA8, 0x65, 0xA8, 0x64, 0xA8, 0x63, 0xCB, 0x60, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, /* 0x64-0x67 */
+ 0x00, 0x00, 0xAA, 0xAB, 0xCD, 0x5B, 0x00, 0x00, /* 0x68-0x6B */
+ 0xCF, 0xBA, 0x00, 0x00, 0xCF, 0xBD, 0xAC, 0xBA, /* 0x6C-0x6F */
+ 0xCF, 0xBB, 0x00, 0x00, 0xAC, 0xB9, 0xCF, 0xBC, /* 0x70-0x73 */
+ 0xAC, 0xBB, 0x00, 0x00, 0xD2, 0xA2, 0xD2, 0xA1, /* 0x74-0x77 */
+ 0xD2, 0x7E, 0xAF, 0x53, 0x00, 0x00, 0xD6, 0x5D, /* 0x78-0x7B */
+ 0xD6, 0x5E, 0xB2, 0x6F, 0xD6, 0x5C, 0xD6, 0x5F, /* 0x7C-0x7F */
+
+ 0xB5, 0x52, 0xB2, 0x70, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0xB5, 0x51, 0xDA, 0x6B, 0xDA, 0x6A, 0x00, 0x00, /* 0x84-0x87 */
+ 0xDA, 0x68, 0xDA, 0x69, 0x00, 0x00, 0xDA, 0x6C, /* 0x88-0x8B */
+ 0xDE, 0xA6, 0xDE, 0xA5, 0xDE, 0xA9, 0x00, 0x00, /* 0x8C-0x8F */
+ 0xDE, 0xA8, 0xDE, 0xA7, 0xBA, 0xB9, 0xE2, 0xC9, /* 0x90-0x93 */
+ 0x00, 0x00, 0xE2, 0xC8, 0xBA, 0xBA, 0xE2, 0xC7, /* 0x94-0x97 */
+ 0xE6, 0x73, 0x00, 0x00, 0xE6, 0x74, 0xBC, 0xF9, /* 0x98-0x9B */
+ 0x00, 0x00, 0xEA, 0x59, 0xEA, 0x5A, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0xF2, 0x72, 0xC3, 0x7D, 0xF2, 0x71, /* 0xA0-0xA3 */
+ 0xF2, 0x70, 0xF2, 0x6E, 0xF2, 0x6F, 0xC4, 0xEB, /* 0xA4-0xA7 */
+ 0xF4, 0x6C, 0xF6, 0xEE, 0xF8, 0xF7, 0x00, 0x00, /* 0xA8-0xAB */
+ 0xA4, 0xFC, 0x00, 0x00, 0xC9, 0xA5, 0xA5, 0xC7, /* 0xAC-0xAF */
+ 0xC9, 0xA6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0xCA, 0x43, 0xCA, 0x44, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0xCB, 0x66, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0xCB, 0x62, 0x00, 0x00, 0xCB, 0x61, /* 0xBC-0xBF */
+ 0xAA, 0xAC, 0xCB, 0x65, 0xA8, 0x67, 0xCB, 0x63, /* 0xC0-0xC3 */
+ 0xA8, 0x66, 0xCB, 0x67, 0xCB, 0x64, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xCD, 0x5F, 0xCF, 0xBE, 0xCD, 0x5D, /* 0xC8-0xCB */
+ 0xCD, 0x64, 0x00, 0x00, 0xAA, 0xAD, 0x00, 0x00, /* 0xCC-0xCF */
+ 0xAA, 0xB0, 0xCD, 0x65, 0xCD, 0x61, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xCD, 0x62, 0x00, 0x00, 0xCD, 0x5C, 0xAA, 0xAF, /* 0xD4-0xD7 */
+ 0xCD, 0x5E, 0xAA, 0xAE, 0xCD, 0x63, 0x00, 0x00, /* 0xD8-0xDB */
+ 0xCD, 0x60, 0x00, 0x00, 0x00, 0x00, 0xCF, 0xC2, /* 0xDC-0xDF */
+ 0xAC, 0xBD, 0xAC, 0xBE, 0x00, 0x00, 0xCF, 0xC5, /* 0xE0-0xE3 */
+ 0xCF, 0xBF, 0x00, 0x00, 0xCF, 0xC4, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xCF, 0xC0, 0xAC, 0xBC, 0xCF, 0xC3, 0xCF, 0xC1, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD2, 0xA8, /* 0xF0-0xF3 */
+ 0xD2, 0xA5, 0x00, 0x00, 0xD2, 0xA7, 0xAF, 0x58, /* 0xF4-0xF7 */
+ 0xAF, 0x57, 0xAF, 0x55, 0xD2, 0xA4, 0xD2, 0xA9, /* 0xF8-0xFB */
+ 0xAF, 0x54, 0xAF, 0x56, 0xD2, 0xA6, 0xD6, 0x67, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_73[512] = {
+ 0xD2, 0xA3, 0xD2, 0xAA, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD6, 0x62, /* 0x04-0x07 */
+ 0xD6, 0x66, 0x00, 0x00, 0xD6, 0x65, 0xDA, 0x6E, /* 0x08-0x0B */
+ 0xDA, 0x79, 0x00, 0x00, 0x00, 0x00, 0xD6, 0x68, /* 0x0C-0x0F */
+ 0x00, 0x00, 0xD6, 0x63, 0xDA, 0x6D, 0xB2, 0x74, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0xB2, 0x73, 0xD6, 0x61, /* 0x14-0x17 */
+ 0xD6, 0x64, 0xB2, 0x75, 0x00, 0x00, 0xB2, 0x72, /* 0x18-0x1B */
+ 0xB2, 0x71, 0xD6, 0x60, 0xD6, 0x69, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0xDA, 0x70, 0xDA, 0x77, /* 0x20-0x23 */
+ 0x00, 0x00, 0xB5, 0x54, 0xDA, 0x76, 0xDA, 0x73, /* 0x24-0x27 */
+ 0x00, 0x00, 0xB5, 0x56, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0xDA, 0x75, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xDA, 0x6F, 0xDA, 0x71, 0xDA, 0x74, 0xDA, 0x72, /* 0x30-0x33 */
+ 0xB5, 0x55, 0xDA, 0x78, 0xB5, 0x53, 0xB7, 0xDF, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0xDE, 0xAD, 0xDE, 0xAC, /* 0x38-0x3B */
+ 0xDE, 0xAA, 0x00, 0x00, 0xB7, 0xE2, 0xB7, 0xE1, /* 0x3C-0x3F */
+ 0xDE, 0xAE, 0x00, 0x00, 0xDE, 0xAB, 0xE2, 0xCA, /* 0x40-0x43 */
+ 0xBA, 0xBB, 0xB7, 0xE0, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0xDE, 0xB0, 0xDE, 0xAF, 0x00, 0x00, /* 0x48-0x4B */
+ 0xE2, 0xCD, 0xE2, 0xCB, 0xBC, 0xFA, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xBA, 0xBC, 0xE2, 0xCC, 0xE6, 0x76, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBC, 0xFB, /* 0x54-0x57 */
+ 0xE6, 0x75, 0xE6, 0x7E, 0xE6, 0x7D, 0xE6, 0x7B, /* 0x58-0x5B */
+ 0x00, 0x00, 0xE6, 0x7A, 0xE6, 0x77, 0xE6, 0x78, /* 0x5C-0x5F */
+ 0xE6, 0x79, 0xE6, 0x7C, 0xE6, 0xA1, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0xEA, 0x5F, 0xEA, 0x5C, 0xEA, 0x5D, /* 0x64-0x67 */
+ 0xBF, 0x57, 0xEA, 0x5B, 0xEA, 0x61, 0xEA, 0x60, /* 0x68-0x6B */
+ 0xEA, 0x5E, 0x00, 0x00, 0xED, 0x64, 0xED, 0x65, /* 0x6C-0x6F */
+ 0xC0, 0xF1, 0x00, 0x00, 0xC0, 0xF2, 0xED, 0x63, /* 0x70-0x73 */
+ 0x00, 0x00, 0xC2, 0x79, 0xEF, 0xFE, 0xC2, 0x78, /* 0x74-0x77 */
+ 0xC3, 0x7E, 0x00, 0x00, 0xC3, 0xA1, 0xC4, 0x6D, /* 0x78-0x7B */
+ 0xF4, 0x6E, 0xF4, 0x6D, 0xF5, 0xDD, 0xF6, 0xEF, /* 0x7C-0x7F */
+
+ 0xC5, 0x7A, 0xF7, 0xE8, 0xF7, 0xE7, 0xF7, 0xE9, /* 0x80-0x83 */
+ 0xA5, 0xC8, 0xCF, 0xC6, 0xAF, 0x59, 0xB2, 0x76, /* 0x84-0x87 */
+ 0xD6, 0x6A, 0xA5, 0xC9, 0xC9, 0xA7, 0xA4, 0xFD, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0xCA, 0x45, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0xCB, 0x6C, 0xCB, 0x6A, /* 0x90-0x93 */
+ 0xCB, 0x6B, 0xCB, 0x68, 0xA8, 0x68, 0xCB, 0x69, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0xCD, 0x6D, 0x00, 0x00, 0xAA, 0xB3, /* 0x9C-0x9F */
+ 0xCD, 0x6B, 0xCD, 0x67, 0xCD, 0x6A, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0xCD, 0x66, 0xAA, 0xB5, 0xCD, 0x69, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xAA, 0xB2, 0xAA, 0xB1, 0x00, 0x00, 0xAA, 0xB4, /* 0xA8-0xAB */
+ 0xCD, 0x6C, 0xCD, 0x68, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0xAC, 0xC2, 0xAC, 0xC5, /* 0xB0-0xB3 */
+ 0xCF, 0xCE, 0xCF, 0xCD, 0xCF, 0xCC, 0xAC, 0xBF, /* 0xB4-0xB7 */
+ 0xCF, 0xD5, 0xCF, 0xCB, 0x00, 0x00, 0xAC, 0xC1, /* 0xB8-0xBB */
+ 0xD2, 0xAF, 0x00, 0x00, 0xCF, 0xD2, 0xCF, 0xD0, /* 0xBC-0xBF */
+ 0xAC, 0xC4, 0x00, 0x00, 0xCF, 0xC8, 0xCF, 0xD3, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0xCF, 0xCA, 0xCF, 0xD4, 0xCF, 0xD1, /* 0xC4-0xC7 */
+ 0xCF, 0xC9, 0x00, 0x00, 0xAC, 0xC0, 0xCF, 0xD6, /* 0xC8-0xCB */
+ 0xCF, 0xC7, 0xAC, 0xC3, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0xD2, 0xB4, 0xD2, 0xAB, /* 0xD0-0xD3 */
+ 0xD2, 0xB6, 0x00, 0x00, 0xD2, 0xAE, 0xD2, 0xB9, /* 0xD4-0xD7 */
+ 0xD2, 0xBA, 0xD2, 0xAC, 0xD2, 0xB8, 0xD2, 0xB5, /* 0xD8-0xDB */
+ 0xD2, 0xB3, 0xD2, 0xB7, 0xAF, 0x5F, 0x00, 0x00, /* 0xDC-0xDF */
+ 0xAF, 0x5D, 0x00, 0x00, 0x00, 0x00, 0xD2, 0xB1, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0xD2, 0xAD, 0x00, 0x00, 0xD2, 0xB0, /* 0xE4-0xE7 */
+ 0xD2, 0xBB, 0xD2, 0xB2, 0xAF, 0x5E, 0xCF, 0xCF, /* 0xE8-0xEB */
+ 0x00, 0x00, 0xAF, 0x5A, 0xAF, 0x5C, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xD6, 0x78, 0xD6, 0x6D, 0xD6, 0x6B, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0xD6, 0x6C, 0x00, 0x00, 0xD6, 0x73, 0x00, 0x00, /* 0xF8-0xFB */
+ 0xD6, 0x74, 0xD6, 0x70, 0xB2, 0x7B, 0xD6, 0x75, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_74[512] = {
+ 0xD6, 0x72, 0xD6, 0x6F, 0x00, 0x00, 0xB2, 0x79, /* 0x00-0x03 */
+ 0xD6, 0x6E, 0xB2, 0x77, 0xB2, 0x7A, 0xD6, 0x71, /* 0x04-0x07 */
+ 0xD6, 0x79, 0xAF, 0x5B, 0xB2, 0x78, 0xD6, 0x77, /* 0x08-0x0B */
+ 0xD6, 0x76, 0xB2, 0x7C, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0xDA, 0x7E, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0xDA, 0xA1, 0xB5, 0x60, /* 0x18-0x1B */
+ 0x00, 0x00, 0xDA, 0xA7, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0xDA, 0xA9, 0xDA, 0xA2, 0xB5, 0x5A, 0xDA, 0xA6, /* 0x20-0x23 */
+ 0xDA, 0xA5, 0xB5, 0x5B, 0xB5, 0x61, 0x00, 0x00, /* 0x24-0x27 */
+ 0xB5, 0x62, 0xDA, 0xA8, 0xB5, 0x58, 0xDA, 0x7D, /* 0x28-0x2B */
+ 0xDA, 0x7B, 0xDA, 0xA3, 0xDA, 0x7A, 0xB5, 0x5F, /* 0x2C-0x2F */
+ 0xDA, 0x7C, 0xDA, 0xA4, 0xDA, 0xAA, 0xB5, 0x59, /* 0x30-0x33 */
+ 0xB5, 0x5E, 0xB5, 0x5C, 0xB5, 0x5D, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0xB5, 0x57, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB7, 0xE9, /* 0x3C-0x3F */
+ 0xDE, 0xB7, 0xB7, 0xE8, 0xDE, 0xBB, 0x00, 0x00, /* 0x40-0x43 */
+ 0xDE, 0xB1, 0x00, 0x00, 0xDE, 0xBC, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0xDE, 0xB2, 0xDE, 0xB3, /* 0x48-0x4B */
+ 0x00, 0x00, 0xDE, 0xBD, 0xDE, 0xBA, 0xDE, 0xB8, /* 0x4C-0x4F */
+ 0xDE, 0xB9, 0xDE, 0xB5, 0xDE, 0xB4, 0x00, 0x00, /* 0x50-0x53 */
+ 0xDE, 0xBE, 0xB7, 0xE5, 0x00, 0x00, 0xDE, 0xB6, /* 0x54-0x57 */
+ 0x00, 0x00, 0xB7, 0xEA, 0xB7, 0xE4, 0xB7, 0xEB, /* 0x58-0x5B */
+ 0xB7, 0xEC, 0x00, 0x00, 0xB7, 0xE7, 0xB7, 0xE6, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0xE2, 0xCE, 0xBA, 0xBE, /* 0x60-0x63 */
+ 0xBA, 0xBD, 0x00, 0x00, 0x00, 0x00, 0xE2, 0xD3, /* 0x64-0x67 */
+ 0x00, 0x00, 0xBC, 0xFC, 0xBA, 0xBF, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0xBA, 0xC1, 0xE2, 0xD4, 0xB7, 0xE3, /* 0x6C-0x6F */
+ 0xBA, 0xC0, 0xE2, 0xD0, 0xE2, 0xD2, 0xE2, 0xCF, /* 0x70-0x73 */
+ 0x00, 0x00, 0xE2, 0xD1, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0xE6, 0xAB, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0xE6, 0xAA, 0xE6, 0xA7, 0xBD, 0x40, 0xEA, 0x62, /* 0x7C-0x7F */
+
+ 0xBD, 0x41, 0xE6, 0xA6, 0x00, 0x00, 0xBC, 0xFE, /* 0x80-0x83 */
+ 0x00, 0x00, 0xE6, 0xA8, 0xE6, 0xA5, 0xE6, 0xA2, /* 0x84-0x87 */
+ 0xE6, 0xA9, 0xE6, 0xA3, 0xE6, 0xA4, 0xBC, 0xFD, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0xED, 0x69, 0x00, 0x00, 0xEA, 0x66, 0x00, 0x00, /* 0x90-0x93 */
+ 0xEA, 0x65, 0xEA, 0x67, 0x00, 0x00, 0xED, 0x66, /* 0x94-0x97 */
+ 0xBF, 0x5A, 0x00, 0x00, 0xEA, 0x63, 0x00, 0x00, /* 0x98-0x9B */
+ 0xBF, 0x58, 0x00, 0x00, 0xBF, 0x5C, 0xBF, 0x5B, /* 0x9C-0x9F */
+ 0xEA, 0x64, 0xEA, 0x68, 0x00, 0x00, 0xBF, 0x59, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0xED, 0x6D, 0xC0, 0xF5, 0xC2, 0x7A, /* 0xA4-0xA7 */
+ 0xC0, 0xF6, 0xC0, 0xF3, 0xED, 0x6A, 0xED, 0x68, /* 0xA8-0xAB */
+ 0x00, 0x00, 0xED, 0x6B, 0x00, 0x00, 0xED, 0x6E, /* 0xAC-0xAF */
+ 0xC0, 0xF4, 0xED, 0x6C, 0xED, 0x67, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0xF0, 0x42, 0xF0, 0x45, 0xF2, 0x75, /* 0xB4-0xB7 */
+ 0xF0, 0x40, 0x00, 0x00, 0xF4, 0x6F, 0xF0, 0x46, /* 0xB8-0xBB */
+ 0x00, 0x00, 0xC3, 0xA2, 0xF0, 0x44, 0xC2, 0x7B, /* 0xBC-0xBF */
+ 0xF0, 0x41, 0xF0, 0x43, 0xF0, 0x47, 0xF2, 0x76, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0xF2, 0x74, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0xC3, 0xA3, 0xF2, 0x73, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC4, 0x6E, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xC4, 0xED, 0xF6, 0xF1, 0xC4, 0xEC, 0xF6, 0xF3, /* 0xD4-0xD7 */
+ 0xF6, 0xF0, 0xF6, 0xF2, 0xC5, 0xD0, 0xF8, 0xB2, /* 0xD8-0xDB */
+ 0xA5, 0xCA, 0xCD, 0x6E, 0xD2, 0xBC, 0xD2, 0xBD, /* 0xDC-0xDF */
+ 0xB2, 0x7D, 0xDE, 0xBF, 0xBF, 0x5D, 0xC3, 0xA4, /* 0xE0-0xE3 */
+ 0xC5, 0x7B, 0xF8, 0xB3, 0xA5, 0xCB, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xCD, 0x6F, 0xA2, 0x60, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0xCF, 0xD7, 0x00, 0x00, 0xCF, 0xD8, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xD2, 0xBE, 0xD2, 0xBF, 0xB2, 0x7E, 0xB2, 0xA1, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDA, 0xAB, /* 0xF8-0xFB */
+ 0x00, 0x00, 0xDE, 0xC2, 0xDE, 0xC1, 0xDE, 0xC0, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_75[512] = {
+ 0xE2, 0xD5, 0x00, 0x00, 0xE2, 0xD6, 0xE2, 0xD7, /* 0x00-0x03 */
+ 0xBA, 0xC2, 0x00, 0x00, 0x00, 0x00, 0xE6, 0xAD, /* 0x04-0x07 */
+ 0xE6, 0xAC, 0x00, 0x00, 0x00, 0x00, 0xEA, 0x69, /* 0x08-0x0B */
+ 0xBF, 0x5E, 0xBF, 0x5F, 0x00, 0x00, 0xED, 0x72, /* 0x0C-0x0F */
+ 0xED, 0x6F, 0xED, 0x70, 0xED, 0x71, 0xF0, 0x49, /* 0x10-0x13 */
+ 0xF0, 0x48, 0xC2, 0x7C, 0xF2, 0x77, 0xF5, 0xDE, /* 0x14-0x17 */
+ 0xA5, 0xCC, 0x00, 0x00, 0xAC, 0xC6, 0x00, 0x00, /* 0x18-0x1B */
+ 0xB2, 0xA2, 0xDE, 0xC3, 0x00, 0x00, 0xA5, 0xCD, /* 0x1C-0x1F */
+ 0x00, 0x00, 0xD2, 0xC0, 0xB2, 0xA3, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0xB5, 0x63, 0xB5, 0x64, 0x00, 0x00, /* 0x24-0x27 */
+ 0xA5, 0xCE, 0xA5, 0xCF, 0xCA, 0x46, 0xA8, 0x6A, /* 0x28-0x2B */
+ 0xA8, 0x69, 0xAC, 0xC7, 0xCF, 0xD9, 0xDA, 0xAC, /* 0x2C-0x2F */
+ 0xA5, 0xD0, 0xA5, 0xD1, 0xA5, 0xD2, 0xA5, 0xD3, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA8, 0x6B, /* 0x34-0x37 */
+ 0xA8, 0x6C, 0xCB, 0x6E, 0xCB, 0x6D, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0xAA, 0xB6, 0xCD, 0x72, 0xCD, 0x70, /* 0x3C-0x3F */
+ 0xCD, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCF, 0xDA, /* 0x44-0x47 */
+ 0xCF, 0xDB, 0x00, 0x00, 0x00, 0x00, 0xAC, 0xCB, /* 0x48-0x4B */
+ 0xAC, 0xC9, 0x00, 0x00, 0xAC, 0xCA, 0xAC, 0xC8, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0xAF, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0xAF, 0x64, 0xAF, 0x63, 0xD2, 0xC1, /* 0x58-0x5B */
+ 0xAF, 0x62, 0xAF, 0x61, 0x00, 0x00, 0xD2, 0xC2, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0xB2, 0xA6, 0xD6, 0x7B, /* 0x60-0x63 */
+ 0xD6, 0x7A, 0xB2, 0xA4, 0xB2, 0xA5, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0xB5, 0x66, 0xB5, 0x65, /* 0x68-0x6B */
+ 0xDA, 0xAE, 0x00, 0x00, 0x00, 0x00, 0xDA, 0xAD, /* 0x6C-0x6F */
+ 0xB2, 0xA7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0xB7, 0xED, 0xDE, 0xC5, /* 0x74-0x77 */
+ 0xB7, 0xEE, 0xDE, 0xC4, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0xE2, 0xD8, 0xE6, 0xAE, 0xBD, 0x42, /* 0x7C-0x7F */
+
+ 0xEA, 0x6A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0xED, 0x73, 0x00, 0x00, 0xC3, 0xA6, 0xC3, 0xA5, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0xC5, 0x7C, 0xA5, 0xD4, /* 0x88-0x8B */
+ 0xCD, 0x73, 0x00, 0x00, 0x00, 0x00, 0xB2, 0xA8, /* 0x8C-0x8F */
+ 0xE2, 0xD9, 0xBA, 0xC3, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0xCB, 0x6F, 0xCB, 0x70, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0xCD, 0x74, 0xAA, 0xB8, 0xAA, 0xB9, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0xAA, 0xB7, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0xAC, 0xCF, 0xAC, 0xD0, /* 0xA0-0xA3 */
+ 0xAC, 0xCD, 0xAC, 0xCE, 0x00, 0x00, 0xCF, 0xDC, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0xCF, 0xDD, 0xAC, 0xCC, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0xD2, 0xC3, 0x00, 0x00, 0xAF, 0x68, 0xAF, 0x69, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0xB2, 0xAB, 0xD2, 0xC9, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0xAF, 0x6E, 0xAF, 0x6C, 0xD2, 0xCA, 0xD2, 0xC5, /* 0xB8-0xBB */
+ 0xAF, 0x6B, 0xAF, 0x6A, 0xAF, 0x65, 0xD2, 0xC8, /* 0xBC-0xBF */
+ 0xD2, 0xC7, 0xD2, 0xC4, 0xAF, 0x6D, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xD2, 0xC6, 0xAF, 0x66, 0x00, 0x00, 0xAF, 0x67, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0xB2, 0xAC, 0xD6, 0xA1, /* 0xC8-0xCB */
+ 0xD6, 0xA2, 0xB2, 0xAD, 0xD6, 0x7C, 0xD6, 0x7E, /* 0xCC-0xCF */
+ 0xD6, 0xA4, 0xD6, 0xA3, 0xD6, 0x7D, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xB2, 0xA9, 0xB2, 0xAA, 0x00, 0x00, 0xDA, 0xB6, /* 0xD4-0xD7 */
+ 0xB5, 0x6B, 0xB5, 0x6A, 0xDA, 0xB0, 0xB5, 0x68, /* 0xD8-0xDB */
+ 0x00, 0x00, 0xDA, 0xB3, 0xB5, 0x6C, 0xDA, 0xB4, /* 0xDC-0xDF */
+ 0xB5, 0x6D, 0xDA, 0xB1, 0xB5, 0x67, 0xB5, 0x69, /* 0xE0-0xE3 */
+ 0xDA, 0xB5, 0x00, 0x00, 0xDA, 0xB2, 0xDA, 0xAF, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0xDE, 0xD2, 0x00, 0x00, 0xDE, 0xC7, /* 0xEC-0xEF */
+ 0xB7, 0xF0, 0xB7, 0xF3, 0xB7, 0xF2, 0xB7, 0xF7, /* 0xF0-0xF3 */
+ 0xB7, 0xF6, 0xDE, 0xD3, 0xDE, 0xD1, 0xDE, 0xCA, /* 0xF4-0xF7 */
+ 0xDE, 0xCE, 0xDE, 0xCD, 0xB7, 0xF4, 0xDE, 0xD0, /* 0xF8-0xFB */
+ 0xDE, 0xCC, 0xDE, 0xD4, 0xDE, 0xCB, 0xB7, 0xF5, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_76[512] = {
+ 0xB7, 0xEF, 0xB7, 0xF1, 0x00, 0x00, 0xDE, 0xC9, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0xE2, 0xDB, 0xBA, 0xC7, 0xE2, 0xDF, 0xBA, 0xC6, /* 0x08-0x0B */
+ 0xE2, 0xDC, 0xBA, 0xC5, 0x00, 0x00, 0xDE, 0xC8, /* 0x0C-0x0F */
+ 0xDE, 0xCF, 0xE2, 0xDE, 0x00, 0x00, 0xBA, 0xC8, /* 0x10-0x13 */
+ 0xE2, 0xE0, 0xE2, 0xDD, 0xE2, 0xDA, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0xE6, 0xB1, 0xE6, 0xB5, 0xE6, 0xB7, /* 0x18-0x1B */
+ 0xE6, 0xB3, 0xE6, 0xB2, 0xE6, 0xB0, 0xBD, 0x45, /* 0x1C-0x1F */
+ 0xBD, 0x43, 0xBD, 0x48, 0xBD, 0x49, 0xE6, 0xB4, /* 0x20-0x23 */
+ 0xBD, 0x46, 0xE6, 0xAF, 0xBD, 0x47, 0xBA, 0xC4, /* 0x24-0x27 */
+ 0xE6, 0xB6, 0xBD, 0x44, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0xEA, 0x6C, 0x00, 0x00, 0xEA, 0x6B, /* 0x2C-0x2F */
+ 0xEA, 0x73, 0xEA, 0x6D, 0xEA, 0x72, 0xEA, 0x6F, /* 0x30-0x33 */
+ 0xBF, 0x60, 0xEA, 0x71, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0xBF, 0x61, 0x00, 0x00, 0xBF, 0x62, 0x00, 0x00, /* 0x38-0x3B */
+ 0xEA, 0x70, 0xEA, 0x6E, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0xC0, 0xF8, 0xED, 0x74, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0xC0, 0xF7, 0xED, 0x77, /* 0x44-0x47 */
+ 0xED, 0x75, 0xED, 0x76, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0xC0, 0xF9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xF0, 0x4D, 0x00, 0x00, 0xC2, 0xA1, 0xF0, 0x4E, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0xC2, 0x7D, 0xF0, 0x4F, /* 0x54-0x57 */
+ 0xC2, 0x7E, 0xF0, 0x4C, 0xF0, 0x50, 0x00, 0x00, /* 0x58-0x5B */
+ 0xF0, 0x4A, 0x00, 0x00, 0x00, 0x00, 0xC3, 0xA7, /* 0x5C-0x5F */
+ 0xF2, 0x78, 0xC3, 0xA8, 0xC4, 0x6F, 0x00, 0x00, /* 0x60-0x63 */
+ 0xF0, 0x4B, 0xC4, 0x70, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0xC4, 0xEE, 0xF5, 0xDF, 0x00, 0x00, /* 0x68-0x6B */
+ 0xC5, 0x7E, 0xF6, 0xF4, 0xC5, 0x7D, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xF7, 0xEA, 0xC5, 0xF5, 0xC5, 0xF6, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0xF9, 0xCC, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0xAC, 0xD1, 0xCF, 0xDE, 0x00, 0x00, 0xB5, 0x6E, /* 0x78-0x7B */
+ 0xB5, 0x6F, 0xA5, 0xD5, 0xA6, 0xCA, 0xCA, 0x47, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0xCB, 0x71, 0xA8, 0x6D, 0x00, 0x00, /* 0x80-0x83 */
+ 0xAA, 0xBA, 0x00, 0x00, 0xAC, 0xD2, 0xAC, 0xD3, /* 0x84-0x87 */
+ 0xAC, 0xD4, 0xD6, 0xA6, 0xD2, 0xCB, 0xAF, 0x6F, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0xB2, 0xAE, 0xD6, 0xA5, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0xDA, 0xB8, 0xB5, 0x71, /* 0x90-0x93 */
+ 0x00, 0x00, 0xDA, 0xB7, 0xB5, 0x70, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0xDE, 0xD5, 0xBD, 0x4A, 0xE6, 0xBB, /* 0x98-0x9B */
+ 0xE6, 0xB8, 0xE6, 0xB9, 0xE6, 0xBA, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0xED, 0x78, 0x00, 0x00, 0xF0, 0x51, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0xF4, 0x71, 0xF4, 0x70, /* 0xA8-0xAB */
+ 0x00, 0x00, 0xF6, 0xF5, 0xA5, 0xD6, 0xCD, 0x75, /* 0xAC-0xAF */
+ 0xAF, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0xB5, 0x72, 0xDE, 0xD6, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0xE2, 0xE1, 0x00, 0x00, 0xBD, 0x4B, 0xEA, 0x74, /* 0xB8-0xBB */
+ 0x00, 0x00, 0xF0, 0x52, 0xF4, 0x72, 0xA5, 0xD7, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0xAA, 0xBB, 0xAC, 0xD7, /* 0xC0-0xC3 */
+ 0xCF, 0xDF, 0xAC, 0xD8, 0xAC, 0xD6, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0xAC, 0xD5, 0xD2, 0xCC, 0xAF, 0x71, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0xAF, 0x72, 0xAF, 0x73, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0xB2, 0xB0, 0xD6, 0xA7, /* 0xD0-0xD3 */
+ 0xB2, 0xAF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0xDA, 0xB9, 0xB2, 0xB1, /* 0xD8-0xDB */
+ 0xB5, 0x73, 0xDE, 0xD7, 0xB7, 0xF8, 0xB7, 0xF9, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xBA, 0xC9, 0x00, 0x00, 0xBA, 0xCA, /* 0xE0-0xE3 */
+ 0xBD, 0x4C, 0xBF, 0x64, 0xEA, 0x75, 0xBF, 0x63, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0xED, 0x79, 0xC0, 0xFA, 0x00, 0x00, /* 0xE8-0xEB */
+ 0xF0, 0x53, 0xF4, 0x73, 0xA5, 0xD8, 0xA8, 0x6E, /* 0xEC-0xEF */
+ 0xCD, 0x78, 0xCD, 0x77, 0xAA, 0xBC, 0xCD, 0x76, /* 0xF0-0xF3 */
+ 0xAA, 0xBD, 0xCD, 0x79, 0x00, 0x00, 0xCF, 0xE5, /* 0xF4-0xF7 */
+ 0xAC, 0xDB, 0xAC, 0xDA, 0xCF, 0xE7, 0xCF, 0xE6, /* 0xF8-0xFB */
+ 0xAC, 0xDF, 0x00, 0x00, 0xAC, 0xDE, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_77[512] = {
+ 0x00, 0x00, 0xAC, 0xD9, 0x00, 0x00, 0xCF, 0xE1, /* 0x00-0x03 */
+ 0xCF, 0xE2, 0xCF, 0xE3, 0x00, 0x00, 0xAC, 0xE0, /* 0x04-0x07 */
+ 0xCF, 0xE0, 0xAC, 0xDC, 0xCF, 0xE4, 0xAC, 0xDD, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xD2, 0xCF, 0xD2, 0xD3, 0xD2, 0xD1, 0xD2, 0xD0, /* 0x10-0x13 */
+ 0x00, 0x00, 0xD2, 0xD4, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0xD2, 0xD5, 0xD2, 0xD6, 0xD2, 0xCE, /* 0x18-0x1B */
+ 0x00, 0x00, 0xD2, 0xCD, 0x00, 0x00, 0xAF, 0x75, /* 0x1C-0x1F */
+ 0xAF, 0x76, 0x00, 0x00, 0xD2, 0xD7, 0xD2, 0xD2, /* 0x20-0x23 */
+ 0x00, 0x00, 0xD6, 0xB0, 0x00, 0x00, 0xD2, 0xD8, /* 0x24-0x27 */
+ 0xAF, 0x77, 0xAF, 0x74, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0xD6, 0xAA, 0x00, 0x00, 0xD6, 0xA9, /* 0x2C-0x2F */
+ 0x00, 0x00, 0xD6, 0xAB, 0xD6, 0xAC, 0xD6, 0xAE, /* 0x30-0x33 */
+ 0xD6, 0xAD, 0xD6, 0xB2, 0xB2, 0xB5, 0xB2, 0xB2, /* 0x34-0x37 */
+ 0xB2, 0xB6, 0xD6, 0xA8, 0xB2, 0xB7, 0xD6, 0xB1, /* 0x38-0x3B */
+ 0xB2, 0xB4, 0xD6, 0xAF, 0xB2, 0xB3, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0xDA, 0xBC, 0xDA, 0xBE, 0xDA, 0xBA, 0xDA, 0xBB, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0xDA, 0xBF, 0xDA, 0xC1, /* 0x48-0x4B */
+ 0xDA, 0xC2, 0xDA, 0xBD, 0xDA, 0xC0, 0xB5, 0x74, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0xDE, 0xDB, 0x00, 0x00, /* 0x50-0x53 */
+ 0xDE, 0xE0, 0xDE, 0xD8, 0xDE, 0xDC, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0xDE, 0xE1, 0xDE, 0xDD, 0xB7, 0xFA, /* 0x58-0x5B */
+ 0xB8, 0x43, 0x00, 0x00, 0xB7, 0xFD, 0xDE, 0xD9, /* 0x5C-0x5F */
+ 0xDE, 0xDA, 0xBA, 0xCE, 0xB8, 0x46, 0xB7, 0xFE, /* 0x60-0x63 */
+ 0x00, 0x00, 0xB8, 0x44, 0xB7, 0xFC, 0xDE, 0xDF, /* 0x64-0x67 */
+ 0xB8, 0x45, 0xDE, 0xDE, 0xB8, 0x41, 0xB7, 0xFB, /* 0x68-0x6B */
+ 0xB8, 0x42, 0xDE, 0xE2, 0xE2, 0xE6, 0xE2, 0xE8, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0xB8, 0x40, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0xE2, 0xE3, 0xBA, 0xCC, 0xE2, 0xE9, 0xBA, 0xCD, /* 0x7C-0x7F */
+
+ 0xE2, 0xE7, 0xE2, 0xE2, 0xE2, 0xE5, 0xE2, 0xEA, /* 0x80-0x83 */
+ 0xBA, 0xCB, 0xE2, 0xE4, 0x00, 0x00, 0xBD, 0x4E, /* 0x84-0x87 */
+ 0xE6, 0xBF, 0xE6, 0xBE, 0x00, 0x00, 0xBD, 0x51, /* 0x88-0x8B */
+ 0xBD, 0x4F, 0xE6, 0xBC, 0xBD, 0x4D, 0xE6, 0xBD, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xBD, 0x50, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0xEA, 0x7D, 0x00, 0x00, 0xEA, 0xA1, /* 0x94-0x97 */
+ 0x00, 0x00, 0xEA, 0x7E, 0xEA, 0x76, 0xEA, 0x7A, /* 0x98-0x9B */
+ 0xEA, 0x79, 0xEA, 0x77, 0xBF, 0x66, 0xBF, 0x67, /* 0x9C-0x9F */
+ 0xBF, 0x65, 0xEA, 0x78, 0xEA, 0x7B, 0xEA, 0x7C, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0xBF, 0x68, 0x00, 0x00, 0xC1, 0x40, /* 0xA4-0xA7 */
+ 0xED, 0xA3, 0x00, 0x00, 0xC0, 0xFC, 0xED, 0x7B, /* 0xA8-0xAB */
+ 0xC0, 0xFE, 0xC1, 0x41, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0xC0, 0xFD, 0xED, 0xA2, 0xED, 0x7C, 0xC0, 0xFB, /* 0xB0-0xB3 */
+ 0xED, 0xA1, 0xED, 0x7A, 0xED, 0x7E, 0xED, 0x7D, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0xF0, 0x55, 0xC2, 0xA4, /* 0xB8-0xBB */
+ 0xC2, 0xA5, 0xC2, 0xA2, 0x00, 0x00, 0xC2, 0xA3, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0xF0, 0x54, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xF2, 0x7B, 0x00, 0x00, 0x00, 0x00, 0xC3, 0xA9, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xF2, 0x79, 0xF2, 0x7A, 0x00, 0x00, /* 0xC8-0xCB */
+ 0xF4, 0x74, 0xF4, 0x77, 0xF4, 0x75, 0xF4, 0x76, /* 0xCC-0xCF */
+ 0xF5, 0xE0, 0x00, 0x00, 0x00, 0x00, 0xC4, 0xEF, /* 0xD0-0xD3 */
+ 0xF7, 0xEB, 0xF8, 0xB4, 0x00, 0x00, 0xC5, 0xF7, /* 0xD4-0xD7 */
+ 0xF8, 0xF8, 0xF8, 0xF9, 0xC6, 0x66, 0xA5, 0xD9, /* 0xD8-0xDB */
+ 0xAC, 0xE1, 0x00, 0x00, 0xDA, 0xC3, 0x00, 0x00, /* 0xDC-0xDF */
+ 0xDE, 0xE3, 0x00, 0x00, 0xA5, 0xDA, 0xA8, 0x6F, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0xAA, 0xBE, 0x00, 0x00, 0xCF, 0xE8, /* 0xE4-0xE7 */
+ 0xCF, 0xE9, 0xAF, 0x78, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0xDA, 0xC4, 0xB5, 0x75, 0xB8, 0x47, 0xC1, 0x42, /* 0xEC-0xEF */
+ 0xED, 0xA4, 0xF2, 0x7C, 0xF4, 0x78, 0xA5, 0xDB, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCD, 0xA1, /* 0xF4-0xF7 */
+ 0xCD, 0x7A, 0xCD, 0x7C, 0xCD, 0x7E, 0xCD, 0x7D, /* 0xF8-0xFB */
+ 0xCD, 0x7B, 0xAA, 0xBF, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_78[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0xAC, 0xE2, 0xCF, 0xF2, /* 0x00-0x03 */
+ 0x00, 0x00, 0xCF, 0xED, 0xCF, 0xEA, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0xCF, 0xF1, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0xAC, 0xE4, 0xAC, 0xE5, 0xCF, 0xF0, 0xCF, 0xEF, /* 0x0C-0x0F */
+ 0xCF, 0xEE, 0xCF, 0xEB, 0xCF, 0xEC, 0xCF, 0xF3, /* 0x10-0x13 */
+ 0xAC, 0xE3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0xAF, 0x7C, 0x00, 0x00, 0xAF, 0xA4, /* 0x1C-0x1F */
+ 0xAF, 0xA3, 0xD2, 0xE1, 0xD2, 0xDB, 0xD2, 0xD9, /* 0x20-0x23 */
+ 0x00, 0x00, 0xAF, 0xA1, 0xD6, 0xB9, 0xAF, 0x7A, /* 0x24-0x27 */
+ 0xD2, 0xDE, 0xD2, 0xE2, 0xD2, 0xE4, 0xD2, 0xE0, /* 0x28-0x2B */
+ 0xD2, 0xDA, 0xAF, 0xA2, 0xD2, 0xDF, 0xD2, 0xDD, /* 0x2C-0x2F */
+ 0xAF, 0x79, 0xD2, 0xE5, 0xAF, 0xA5, 0xD2, 0xE3, /* 0x30-0x33 */
+ 0xAF, 0x7D, 0xD2, 0xDC, 0x00, 0x00, 0xAF, 0x7E, /* 0x34-0x37 */
+ 0xAF, 0x7B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB2, 0xB9, /* 0x40-0x43 */
+ 0x00, 0x00, 0xD6, 0xBA, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0xD6, 0xB3, 0xD6, 0xB5, 0xD6, 0xB7, 0x00, 0x00, /* 0x48-0x4B */
+ 0xD6, 0xB8, 0xD6, 0xB6, 0xB2, 0xBA, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xD6, 0xBB, 0x00, 0x00, 0xD6, 0xB4, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0xDA, 0xC8, 0xB5, 0x76, 0xDA, 0xD0, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xDA, 0xC5, 0x00, 0x00, 0xDA, 0xD1, 0x00, 0x00, /* 0x60-0x63 */
+ 0xDA, 0xC6, 0xDA, 0xC7, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0xDA, 0xCF, 0xDA, 0xCE, 0xDA, 0xCB, 0xB2, 0xB8, /* 0x68-0x6B */
+ 0xB5, 0x77, 0xDA, 0xC9, 0xDA, 0xCC, 0xB5, 0x78, /* 0x6C-0x6F */
+ 0xDA, 0xCD, 0xDA, 0xCA, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0xDE, 0xEE, 0x00, 0x00, 0xDE, 0xF2, /* 0x78-0x7B */
+ 0xB8, 0x4E, 0x00, 0x00, 0xE2, 0xF0, 0xB8, 0x51, /* 0x7C-0x7F */
+
+ 0xDE, 0xF0, 0xF9, 0xD6, 0x00, 0x00, 0xDE, 0xED, /* 0x80-0x83 */
+ 0xDE, 0xE8, 0xDE, 0xEA, 0xDE, 0xEB, 0xDE, 0xE4, /* 0x84-0x87 */
+ 0x00, 0x00, 0xB8, 0x4D, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0xB8, 0x4C, 0x00, 0x00, 0xB8, 0x48, 0xDE, 0xE7, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xB8, 0x4F, 0x00, 0x00, 0xB8, 0x50, /* 0x90-0x93 */
+ 0xDE, 0xE6, 0xDE, 0xE9, 0xDE, 0xF1, 0xB8, 0x4A, /* 0x94-0x97 */
+ 0xB8, 0x4B, 0xDE, 0xEF, 0xDE, 0xE5, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0xE2, 0xF2, 0xBA, 0xD0, /* 0x9C-0x9F */
+ 0xE2, 0xF4, 0xDE, 0xEC, 0xE2, 0xF6, 0xBA, 0xD4, /* 0xA0-0xA3 */
+ 0xE2, 0xF7, 0xE2, 0xF3, 0x00, 0x00, 0xBA, 0xD1, /* 0xA4-0xA7 */
+ 0xE2, 0xEF, 0xBA, 0xD3, 0xE2, 0xEC, 0xE2, 0xF1, /* 0xA8-0xAB */
+ 0xE2, 0xF5, 0xE2, 0xEE, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0xB8, 0x49, 0x00, 0x00, 0xE2, 0xEB, 0xBA, 0xD2, /* 0xB0-0xB3 */
+ 0xE2, 0xED, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0xBD, 0x54, 0xE6, 0xC1, /* 0xB8-0xBB */
+ 0xBD, 0x58, 0x00, 0x00, 0xBD, 0x56, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0xBA, 0xCF, 0x00, 0x00, 0xE6, 0xC8, /* 0xC0-0xC3 */
+ 0xE6, 0xC9, 0xBD, 0x53, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0xE6, 0xC7, 0xE6, 0xCA, 0xBD, 0x55, 0xBD, 0x52, /* 0xC8-0xCB */
+ 0xE6, 0xC3, 0xE6, 0xC0, 0xE6, 0xC5, 0xE6, 0xC2, /* 0xCC-0xCF */
+ 0xBD, 0x59, 0xE6, 0xC4, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xE6, 0xC6, 0xBD, 0x57, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0xBF, 0x6A, 0xEA, 0xA8, /* 0xD8-0xDB */
+ 0x00, 0x00, 0xEA, 0xA2, 0xEA, 0xA6, 0xEA, 0xAC, /* 0xDC-0xDF */
+ 0xEA, 0xAD, 0xEA, 0xA9, 0xEA, 0xAA, 0xEA, 0xA7, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0xEA, 0xA4, 0x00, 0x00, 0xBF, 0x6C, /* 0xE4-0xE7 */
+ 0xBF, 0x69, 0xEA, 0xA3, 0xEA, 0xA5, 0x00, 0x00, /* 0xE8-0xEB */
+ 0xBF, 0x6B, 0xEA, 0xAB, 0x00, 0x00, 0xC1, 0x46, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0xED, 0xAA, 0xED, 0xA5, /* 0xF0-0xF3 */
+ 0xC1, 0x45, 0x00, 0x00, 0x00, 0x00, 0xC1, 0x43, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xED, 0xAC, 0xC1, 0x44, 0xED, 0xA8, /* 0xF8-0xFB */
+ 0xED, 0xA9, 0xED, 0xA6, 0xED, 0xAD, 0xF0, 0x56, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_79[512] = {
+ 0x00, 0x00, 0xC1, 0x47, 0xED, 0xA7, 0x00, 0x00, /* 0x00-0x03 */
+ 0xED, 0xAE, 0xED, 0xAB, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0xF0, 0x5A, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0xF0, 0x57, 0x00, 0x00, 0xC2, 0xA6, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xF0, 0x5B, 0xF0, 0x5D, 0xF0, 0x5C, 0xF0, 0x58, /* 0x10-0x13 */
+ 0xF0, 0x59, 0x00, 0x00, 0x00, 0x00, 0xF2, 0xA3, /* 0x14-0x17 */
+ 0x00, 0x00, 0xC3, 0xAA, 0x00, 0x00, 0xF2, 0x7E, /* 0x18-0x1B */
+ 0xF2, 0xA2, 0xF2, 0x7D, 0xF2, 0xA4, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0xF2, 0xA1, 0x00, 0x00, 0xF4, 0x7A, /* 0x20-0x23 */
+ 0xF4, 0x7D, 0xF4, 0x79, 0xC4, 0x71, 0xF4, 0x7B, /* 0x24-0x27 */
+ 0xF4, 0x7C, 0xF4, 0x7E, 0xC4, 0x72, 0xC4, 0x74, /* 0x28-0x2B */
+ 0xC4, 0x73, 0xF5, 0xE1, 0x00, 0x00, 0xF5, 0xE3, /* 0x2C-0x2F */
+ 0x00, 0x00, 0xF5, 0xE2, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0xF6, 0xF6, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0xF8, 0xB5, 0xF8, 0xFA, 0xA5, 0xDC, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0xCB, 0x72, 0xAA, 0xC0, 0xCD, 0xA3, /* 0x3C-0x3F */
+ 0xAA, 0xC1, 0xAA, 0xC2, 0xCD, 0xA2, 0x00, 0x00, /* 0x40-0x43 */
+ 0xCF, 0xF8, 0xCF, 0xF7, 0xAC, 0xE6, 0xAC, 0xE9, /* 0x44-0x47 */
+ 0xAC, 0xE8, 0xAC, 0xE7, 0xCF, 0xF4, 0xCF, 0xF6, /* 0x48-0x4B */
+ 0xCF, 0xF5, 0x00, 0x00, 0x00, 0x00, 0xD2, 0xE8, /* 0x4C-0x4F */
+ 0xAF, 0xA7, 0xD2, 0xEC, 0xD2, 0xEB, 0xD2, 0xEA, /* 0x50-0x53 */
+ 0xD2, 0xE6, 0xAF, 0xA6, 0xAF, 0xAA, 0xAF, 0xAD, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0xAF, 0xAE, 0xD2, 0xE7, /* 0x58-0x5B */
+ 0xD2, 0xE9, 0xAF, 0xAC, 0xAF, 0xAB, 0xAF, 0xA9, /* 0x5C-0x5F */
+ 0xAF, 0xA8, 0xD6, 0xC2, 0x00, 0x00, 0xD6, 0xC0, /* 0x60-0x63 */
+ 0xD6, 0xBC, 0xB2, 0xBB, 0x00, 0x00, 0xD6, 0xBD, /* 0x64-0x67 */
+ 0xB2, 0xBC, 0xD6, 0xBE, 0xD6, 0xBF, 0xD6, 0xC1, /* 0x68-0x6B */
+ 0x00, 0x00, 0xB2, 0xBD, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xDA, 0xD5, 0x00, 0x00, 0xDA, 0xD4, 0xDA, 0xD3, /* 0x70-0x73 */
+ 0xDA, 0xD2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0xDE, 0xF6, 0xB8, 0x52, 0x00, 0x00, /* 0x78-0x7B */
+ 0xDE, 0xF3, 0xDE, 0xF5, 0x00, 0x00, 0xB8, 0x53, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0xB8, 0x54, 0xDE, 0xF4, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0xE3, 0x41, 0x00, 0x00, 0xE2, 0xF9, 0xE2, 0xFA, /* 0x88-0x8B */
+ 0x00, 0x00, 0xBA, 0xD7, 0xBA, 0xD5, 0xBA, 0xD6, /* 0x8C-0x8F */
+ 0xE3, 0x43, 0x00, 0x00, 0xE3, 0x42, 0xE2, 0xFE, /* 0x90-0x93 */
+ 0xE2, 0xFD, 0xE2, 0xFC, 0xE2, 0xFB, 0xE3, 0x40, /* 0x94-0x97 */
+ 0xE2, 0xF8, 0x00, 0x00, 0xE6, 0xCB, 0xE6, 0xD0, /* 0x98-0x9B */
+ 0xE6, 0xCE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0xE6, 0xCD, 0xE6, 0xCC, 0xE6, 0xCF, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0xEA, 0xAE, 0x00, 0x00, 0xBF, 0x6D, 0xC1, 0x48, /* 0xA4-0xA7 */
+ 0xED, 0xB0, 0x00, 0x00, 0xC1, 0x49, 0xED, 0xAF, /* 0xA8-0xAB */
+ 0xF0, 0x5F, 0xF0, 0x5E, 0xC2, 0xA7, 0x00, 0x00, /* 0xAC-0xAF */
+ 0xF2, 0xA5, 0xC3, 0xAB, 0xF4, 0xA1, 0xC5, 0xA1, /* 0xB0-0xB3 */
+ 0xF6, 0xF7, 0x00, 0x00, 0xF8, 0xB7, 0xF8, 0xB6, /* 0xB4-0xB7 */
+ 0xC9, 0xA8, 0xAC, 0xEA, 0xAC, 0xEB, 0xD6, 0xC3, /* 0xB8-0xBB */
+ 0x00, 0x00, 0xB8, 0x56, 0xA5, 0xDD, 0xA8, 0x72, /* 0xBC-0xBF */
+ 0xA8, 0x71, 0xA8, 0x70, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0xCD, 0xA4, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0xAA, 0xC4, 0xAA, 0xC3, 0x00, 0x00, 0xAC, 0xEE, /* 0xC8-0xCB */
+ 0x00, 0x00, 0xCF, 0xFA, 0xCF, 0xFD, 0xCF, 0xFB, /* 0xCC-0xCF */
+ 0x00, 0x00, 0xAC, 0xEC, 0xAC, 0xED, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0xCF, 0xF9, 0xCF, 0xFC, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xAF, 0xB5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0xD2, 0xF3, 0xD2, 0xF5, 0xD2, 0xF4, 0xAF, 0xB2, /* 0xDC-0xDF */
+ 0xD2, 0xEF, 0x00, 0x00, 0x00, 0x00, 0xAF, 0xB0, /* 0xE0-0xE3 */
+ 0xAF, 0xAF, 0x00, 0x00, 0xAF, 0xB3, 0xAF, 0xB1, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0xAF, 0xB4, 0xD2, 0xF2, 0xD2, 0xED, /* 0xE8-0xEB */
+ 0xD2, 0xEE, 0xD2, 0xF1, 0xD2, 0xF0, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0xD6, 0xC6, 0xD6, 0xC7, /* 0xF4-0xF7 */
+ 0xD6, 0xC5, 0x00, 0x00, 0xD6, 0xC4, 0xB2, 0xBE, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_7A[512] = {
+ 0xB5, 0x7D, 0x00, 0x00, 0xDA, 0xD6, 0xDA, 0xD8, /* 0x00-0x03 */
+ 0xDA, 0xDA, 0xB5, 0x7C, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0xB5, 0x7A, 0x00, 0x00, 0xDA, 0xD7, 0xB5, 0x7B, /* 0x08-0x0B */
+ 0xDA, 0xD9, 0xB5, 0x79, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xDF, 0x41, 0xDE, 0xF7, 0xDE, 0xFA, 0xDE, 0xFE, /* 0x10-0x13 */
+ 0xB8, 0x5A, 0xDE, 0xFC, 0x00, 0x00, 0xDE, 0xFB, /* 0x14-0x17 */
+ 0xDE, 0xF8, 0xDE, 0xF9, 0xB8, 0x58, 0xDF, 0x40, /* 0x18-0x1B */
+ 0xB8, 0x57, 0x00, 0x00, 0xB8, 0x5C, 0xB8, 0x5B, /* 0x1C-0x1F */
+ 0xB8, 0x59, 0x00, 0x00, 0xDE, 0xFD, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0xE3, 0x49, 0x00, 0x00, /* 0x24-0x27 */
+ 0xE3, 0x48, 0x00, 0x00, 0x00, 0x00, 0xE3, 0x44, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0xBA, 0xD8, 0xE3, 0x47, /* 0x2C-0x2F */
+ 0xE3, 0x46, 0xBA, 0xD9, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBD, 0x5E, /* 0x34-0x37 */
+ 0x00, 0x00, 0xE6, 0xD2, 0x00, 0x00, 0xBD, 0x5F, /* 0x38-0x3B */
+ 0xBD, 0x5B, 0xBD, 0x5D, 0x00, 0x00, 0xBD, 0x5A, /* 0x3C-0x3F */
+ 0xBD, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0xEA, 0xAF, 0x00, 0x00, 0xBF, 0x70, 0xEA, 0xB1, /* 0x44-0x47 */
+ 0xEA, 0xB0, 0x00, 0x00, 0xE3, 0x45, 0xBF, 0x72, /* 0x48-0x4B */
+ 0xBF, 0x71, 0xBF, 0x6E, 0xBF, 0x6F, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0xED, 0xB5, 0x00, 0x00, 0xED, 0xB3, 0xC1, 0x4A, /* 0x54-0x57 */
+ 0xED, 0xB4, 0x00, 0x00, 0xED, 0xB6, 0xED, 0xB2, /* 0x58-0x5B */
+ 0xED, 0xB1, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x60, /* 0x5C-0x5F */
+ 0xC2, 0xAA, 0xC2, 0xA8, 0xC2, 0xA9, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF2, 0xA6, /* 0x64-0x67 */
+ 0xF2, 0xA7, 0xC3, 0xAD, 0x00, 0x00, 0xC3, 0xAC, /* 0x68-0x6B */
+ 0xF4, 0xA3, 0xF4, 0xA4, 0xF4, 0xA2, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xF6, 0xF8, 0xF6, 0xF9, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0xA5, 0xDE, 0xCA, 0x48, 0xA8, 0x73, 0x00, 0x00, /* 0x74-0x77 */
+ 0xCD, 0xA5, 0xAA, 0xC6, 0xAA, 0xC5, 0xCD, 0xA6, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0xD0, 0x40, 0xAC, 0xEF, /* 0x7C-0x7F */
+
+ 0xCF, 0xFE, 0xAC, 0xF0, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0xAF, 0xB6, 0xD2, 0xF8, 0xD2, 0xF6, 0xD2, 0xFC, /* 0x84-0x87 */
+ 0xAF, 0xB7, 0xD2, 0xF7, 0xD2, 0xFB, 0xD2, 0xF9, /* 0x88-0x8B */
+ 0xD2, 0xFA, 0x00, 0x00, 0x00, 0x00, 0xD6, 0xC8, /* 0x8C-0x8F */
+ 0xD6, 0xCA, 0x00, 0x00, 0xB2, 0xBF, 0x00, 0x00, /* 0x90-0x93 */
+ 0xD6, 0xC9, 0xB2, 0xC0, 0xB5, 0xA2, 0xB5, 0xA1, /* 0x94-0x97 */
+ 0xB5, 0x7E, 0xDA, 0xDB, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0xDF, 0x44, 0xB8, 0x5D, /* 0x9C-0x9F */
+ 0xB8, 0x5E, 0x00, 0x00, 0xDF, 0x43, 0xDF, 0x42, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xE3, 0x4A, 0xBA, 0xDB, 0xBA, 0xDA, 0xE3, 0x4B, /* 0xA8-0xAB */
+ 0xE3, 0x4C, 0x00, 0x00, 0xBD, 0x61, 0xBD, 0x60, /* 0xAC-0xAF */
+ 0x00, 0x00, 0xEA, 0xB5, 0xE6, 0xD3, 0xE6, 0xD5, /* 0xB0-0xB3 */
+ 0xE6, 0xD4, 0xEA, 0xB4, 0xEA, 0xB2, 0xEA, 0xB6, /* 0xB4-0xB7 */
+ 0xEA, 0xB3, 0x00, 0x00, 0xBF, 0x73, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0xED, 0xB7, 0xC1, 0x4B, /* 0xBC-0xBF */
+ 0xED, 0xB8, 0xED, 0xB9, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xC2, 0xAB, 0xC2, 0xAC, 0x00, 0x00, 0xC4, 0x75, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0xC5, 0xD1, 0xA5, 0xDF, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0xD0, 0x41, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xD2, 0xFD, 0xAF, 0xB8, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB3, 0xBA, /* 0xDC-0xDF */
+ 0xB3, 0xB9, 0x00, 0x00, 0x00, 0x00, 0xB5, 0xA4, /* 0xE0-0xE3 */
+ 0xDA, 0xDD, 0xB5, 0xA3, 0xDA, 0xDC, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDF, 0x45, /* 0xE8-0xEB */
+ 0x00, 0x00, 0xBA, 0xDC, 0xE3, 0x4D, 0xBA, 0xDD, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0xC4, 0x76, 0xF4, 0xA5, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xA6, 0xCB, 0xAA, 0xC7, 0xCD, 0xA7, /* 0xF8-0xFB */
+ 0x00, 0x00, 0xAC, 0xF2, 0x00, 0x00, 0xAC, 0xF1, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_7B[512] = {
+ 0xD0, 0x42, 0xD0, 0x43, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0xD3, 0x40, 0xD3, 0x42, 0xAF, 0xB9, 0x00, 0x00, /* 0x04-0x07 */
+ 0xD3, 0x44, 0xD3, 0x47, 0xD3, 0x45, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0xD3, 0x46, 0xD3, 0x43, /* 0x0C-0x0F */
+ 0xD2, 0xFE, 0xAF, 0xBA, 0xD3, 0x48, 0xD3, 0x41, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0xD6, 0xD3, 0xB2, 0xC6, 0xD6, 0xDC, 0xB2, 0xC3, /* 0x18-0x1B */
+ 0x00, 0x00, 0xD6, 0xD5, 0xB2, 0xC7, 0x00, 0x00, /* 0x1C-0x1F */
+ 0xB2, 0xC1, 0x00, 0x00, 0xD6, 0xD0, 0xD6, 0xDD, /* 0x20-0x23 */
+ 0xD6, 0xD1, 0xD6, 0xCE, 0xB2, 0xC5, 0x00, 0x00, /* 0x24-0x27 */
+ 0xB2, 0xC2, 0x00, 0x00, 0xD6, 0xD4, 0xD6, 0xD7, /* 0x28-0x2B */
+ 0xB2, 0xC4, 0xD6, 0xD8, 0xB2, 0xC8, 0xD6, 0xD9, /* 0x2C-0x2F */
+ 0xD6, 0xCF, 0xD6, 0xD6, 0xD6, 0xDA, 0xD6, 0xD2, /* 0x30-0x33 */
+ 0xD6, 0xCD, 0xD6, 0xCB, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0xD6, 0xDB, 0x00, 0x00, 0x00, 0x00, 0xDA, 0xDF, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0xDA, 0xE4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0xDA, 0xE0, 0xDA, 0xE6, 0xB5, 0xA7, 0xD6, 0xCC, /* 0x44-0x47 */
+ 0xDA, 0xE1, 0xB5, 0xA5, 0xDA, 0xDE, 0xB5, 0xAC, /* 0x48-0x4B */
+ 0xDA, 0xE2, 0xB5, 0xAB, 0xDA, 0xE3, 0xB5, 0xAD, /* 0x4C-0x4F */
+ 0xB5, 0xA8, 0xB5, 0xAE, 0xB5, 0xA9, 0x00, 0x00, /* 0x50-0x53 */
+ 0xB5, 0xAA, 0x00, 0x00, 0xB5, 0xA6, 0x00, 0x00, /* 0x54-0x57 */
+ 0xDA, 0xE5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xB8, 0x61, 0xDF, 0x50, 0x00, 0x00, 0xDF, 0x53, /* 0x60-0x63 */
+ 0xDF, 0x47, 0xDF, 0x4C, 0xDF, 0x46, 0xB8, 0x63, /* 0x64-0x67 */
+ 0x00, 0x00, 0xDF, 0x4A, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0xDF, 0x48, 0xB8, 0x62, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xDF, 0x4F, 0xDF, 0x4E, 0xDF, 0x4B, 0xDF, 0x4D, /* 0x70-0x73 */
+ 0xDF, 0x49, 0xBA, 0xE1, 0xDF, 0x52, 0xB8, 0x5F, /* 0x74-0x77 */
+ 0xDF, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0xE3, 0x5D, 0x00, 0x00, /* 0x80-0x83 */
+ 0xBA, 0xE8, 0xE3, 0x58, 0x00, 0x00, 0xBA, 0xE7, /* 0x84-0x87 */
+ 0xE3, 0x4E, 0x00, 0x00, 0xE3, 0x50, 0xBA, 0xE0, /* 0x88-0x8B */
+ 0xE3, 0x55, 0xE3, 0x54, 0xE3, 0x57, 0xBA, 0xE5, /* 0x8C-0x8F */
+ 0xE3, 0x52, 0xE3, 0x51, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0xBA, 0xE4, 0xBA, 0xDF, 0xE3, 0x53, 0xBA, 0xE2, /* 0x94-0x97 */
+ 0xE3, 0x59, 0xE3, 0x5B, 0x00, 0x00, 0xE3, 0x56, /* 0x98-0x9B */
+ 0xE3, 0x4F, 0xBA, 0xE3, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0xBD, 0x69, 0xBA, 0xDE, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0xE3, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0xE6, 0xD9, 0xBD, 0x62, 0x00, 0x00, 0xE6, 0xDB, /* 0xAC-0xAF */
+ 0x00, 0x00, 0xBD, 0x63, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0xBD, 0x65, 0xE6, 0xDE, 0x00, 0x00, 0xE6, 0xD6, /* 0xB4-0xB7 */
+ 0xBA, 0xE6, 0xE6, 0xDC, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0xE6, 0xD8, 0x00, 0x00, /* 0xBC-0xBF */
+ 0xB8, 0x60, 0xBD, 0x68, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xBD, 0x64, 0x00, 0x00, 0xBD, 0x66, 0xBD, 0x67, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xBF, 0x76, 0xE6, 0xDD, 0xE6, 0xD7, /* 0xC8-0xCB */
+ 0xBD, 0x6A, 0x00, 0x00, 0xE6, 0xDA, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xEA, 0xC0, 0xEA, 0xBB, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xEA, 0xC5, 0xBF, 0x74, 0xEA, 0xBD, 0xBF, 0x78, /* 0xD8-0xDB */
+ 0xEA, 0xC3, 0xEA, 0xBA, 0xEA, 0xB7, 0xEA, 0xC6, /* 0xDC-0xDF */
+ 0xC1, 0x51, 0xBF, 0x79, 0xEA, 0xC2, 0xEA, 0xB8, /* 0xE0-0xE3 */
+ 0xBF, 0x77, 0xEA, 0xBC, 0xBF, 0x7B, 0xEA, 0xB9, /* 0xE4-0xE7 */
+ 0xEA, 0xBE, 0xBF, 0x7A, 0xEA, 0xC1, 0xEA, 0xC4, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0xED, 0xCB, 0xED, 0xCC, 0xED, 0xBC, 0xED, 0xC3, /* 0xF0-0xF3 */
+ 0xED, 0xC1, 0x00, 0x00, 0x00, 0x00, 0xC1, 0x4F, /* 0xF4-0xF7 */
+ 0xED, 0xC8, 0xEA, 0xBF, 0x00, 0x00, 0xED, 0xBF, /* 0xF8-0xFB */
+ 0x00, 0x00, 0xED, 0xC9, 0xC1, 0x4E, 0xED, 0xBE, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_7C[512] = {
+ 0xED, 0xBD, 0xED, 0xC7, 0xED, 0xC4, 0xED, 0xC6, /* 0x00-0x03 */
+ 0x00, 0x00, 0xED, 0xBA, 0xED, 0xCA, 0xC1, 0x4C, /* 0x04-0x07 */
+ 0x00, 0x00, 0xED, 0xC5, 0xED, 0xCE, 0xED, 0xC2, /* 0x08-0x0B */
+ 0xC1, 0x50, 0xC1, 0x4D, 0xED, 0xC0, 0xED, 0xBB, /* 0x0C-0x0F */
+ 0xED, 0xCD, 0xBF, 0x75, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0xF0, 0x63, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0xF0, 0x61, 0xF0, 0x67, 0xC2, 0xB0, 0xF0, 0x65, /* 0x1C-0x1F */
+ 0xF0, 0x64, 0xC2, 0xB2, 0xF0, 0x6A, 0xC2, 0xB1, /* 0x20-0x23 */
+ 0x00, 0x00, 0xF0, 0x6B, 0xF0, 0x68, 0xC2, 0xAE, /* 0x24-0x27 */
+ 0xF0, 0x69, 0xF0, 0x62, 0xC2, 0xAF, 0xC2, 0xAD, /* 0x28-0x2B */
+ 0xF2, 0xAB, 0xF0, 0x66, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xF0, 0x6C, 0x00, 0x00, 0x00, 0x00, 0xF2, 0xA8, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC3, 0xB2, /* 0x34-0x37 */
+ 0xC3, 0xB0, 0xF2, 0xAA, 0x00, 0x00, 0xF2, 0xAC, /* 0x38-0x3B */
+ 0xF2, 0xA9, 0xC3, 0xB1, 0xC3, 0xAE, 0xC3, 0xAF, /* 0x3C-0x3F */
+ 0xC3, 0xB3, 0x00, 0x00, 0x00, 0x00, 0xC4, 0x78, /* 0x40-0x43 */
+ 0x00, 0x00, 0xF4, 0xAA, 0x00, 0x00, 0xF4, 0xA9, /* 0x44-0x47 */
+ 0xF4, 0xA7, 0xF4, 0xA6, 0xF4, 0xA8, 0x00, 0x00, /* 0x48-0x4B */
+ 0xC4, 0x77, 0xC4, 0x79, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xC4, 0xF0, 0x00, 0x00, 0x00, 0x00, 0xF5, 0xE5, /* 0x50-0x53 */
+ 0xF5, 0xE4, 0x00, 0x00, 0x00, 0x00, 0xF6, 0xFA, /* 0x54-0x57 */
+ 0x00, 0x00, 0xF6, 0xFC, 0xF6, 0xFE, 0xF6, 0xFD, /* 0x58-0x5B */
+ 0xF6, 0xFB, 0x00, 0x00, 0x00, 0x00, 0xC5, 0xA3, /* 0x5C-0x5F */
+ 0xC5, 0xA2, 0x00, 0x00, 0x00, 0x00, 0xC5, 0xD3, /* 0x60-0x63 */
+ 0xC5, 0xD2, 0xC5, 0xD4, 0xF7, 0xED, 0xF7, 0xEC, /* 0x64-0x67 */
+ 0x00, 0x00, 0xF8, 0xFB, 0xF8, 0xB8, 0xF8, 0xFC, /* 0x68-0x6B */
+ 0xC6, 0x58, 0x00, 0x00, 0xC6, 0x59, 0xF9, 0x6D, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0xC6, 0x7E, 0xA6, 0xCC, /* 0x70-0x73 */
+ 0x00, 0x00, 0xCD, 0xA8, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0xD0, 0x45, 0xD0, 0x46, 0xD0, 0x44, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0xAC, 0xF3, 0x00, 0x00, 0xD0, 0x47, /* 0x7C-0x7F */
+
+ 0xD0, 0x48, 0xD0, 0x49, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0xD3, 0x49, 0xD3, 0x4F, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0xD3, 0x4D, 0xAF, 0xBB, 0xD3, 0x4B, 0x00, 0x00, /* 0x88-0x8B */
+ 0xD3, 0x4C, 0xD3, 0x4E, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xD3, 0x4A, 0xB2, 0xC9, 0x00, 0x00, /* 0x90-0x93 */
+ 0xD6, 0xDE, 0xB2, 0xCB, 0xD6, 0xE0, 0xB2, 0xCA, /* 0x94-0x97 */
+ 0xD6, 0xDF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0xDA, 0xE8, 0xB5, 0xAF, /* 0x9C-0x9F */
+ 0x00, 0x00, 0xDA, 0xEA, 0xDA, 0xE7, 0xD6, 0xE1, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0xB5, 0xB0, 0x00, 0x00, 0xF9, 0xDB, /* 0xA4-0xA7 */
+ 0xDA, 0xE9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDF, 0x56, /* 0xAC-0xAF */
+ 0x00, 0x00, 0xB8, 0x64, 0xDF, 0x54, 0xB8, 0x65, /* 0xB0-0xB3 */
+ 0xDF, 0x55, 0xB8, 0x66, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xBA, 0xE9, 0xE3, 0x61, 0xE3, 0x5E, /* 0xB8-0xBB */
+ 0xE3, 0x60, 0xBA, 0xEA, 0xBA, 0xEB, 0xE3, 0x5F, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0xE6, 0xDF, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0xE6, 0xE0, 0x00, 0x00, 0xBD, 0x6B, 0xE6, 0xE2, /* 0xC8-0xCB */
+ 0xE6, 0xE1, 0x00, 0x00, 0xA2, 0x61, 0x00, 0x00, /* 0xCC-0xCF */
+ 0xEA, 0xCA, 0xEA, 0xCB, 0xEA, 0xC7, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xEA, 0xC8, 0xBF, 0x7C, 0xBF, 0x7D, 0xEA, 0xC9, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0xC1, 0x57, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0xC1, 0x53, 0xC1, 0x58, 0xC1, 0x54, 0xC1, 0x56, /* 0xDC-0xDF */
+ 0xC1, 0x52, 0x00, 0x00, 0xC1, 0x55, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC2, 0xB3, /* 0xE4-0xE7 */
+ 0xED, 0xCF, 0x00, 0x00, 0xF2, 0xAE, 0x00, 0x00, /* 0xE8-0xEB */
+ 0xF2, 0xAD, 0x00, 0x00, 0xF4, 0xAB, 0xC4, 0x7A, /* 0xEC-0xEF */
+ 0xC4, 0x7B, 0xF7, 0x41, 0xF5, 0xE6, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xF7, 0x40, 0x00, 0x00, 0xF8, 0xFD, 0xF9, 0xA4, /* 0xF4-0xF7 */
+ 0xA6, 0xCD, 0x00, 0x00, 0x00, 0x00, 0xA8, 0x74, /* 0xF8-0xFB */
+ 0x00, 0x00, 0xCD, 0xA9, 0xAA, 0xC8, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_7D[512] = {
+ 0xAC, 0xF6, 0xD0, 0x4C, 0xAC, 0xF4, 0xD0, 0x4A, /* 0x00-0x03 */
+ 0xAC, 0xF9, 0xAC, 0xF5, 0xAC, 0xFA, 0xAC, 0xF8, /* 0x04-0x07 */
+ 0xD0, 0x4B, 0xAC, 0xF7, 0xAF, 0xBF, 0xAF, 0xBE, /* 0x08-0x0B */
+ 0xD3, 0x5A, 0xAF, 0xC7, 0xD3, 0x53, 0xD3, 0x59, /* 0x0C-0x0F */
+ 0xAF, 0xC3, 0xD3, 0x52, 0xD3, 0x58, 0xD3, 0x56, /* 0x10-0x13 */
+ 0xAF, 0xC2, 0xAF, 0xC4, 0xD3, 0x55, 0xAF, 0xBD, /* 0x14-0x17 */
+ 0xD3, 0x54, 0xAF, 0xC8, 0xAF, 0xC5, 0xAF, 0xC9, /* 0x18-0x1B */
+ 0xAF, 0xC6, 0xD3, 0x51, 0xD3, 0x50, 0xD3, 0x57, /* 0x1C-0x1F */
+ 0xAF, 0xC0, 0xAF, 0xBC, 0xAF, 0xC1, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0xD6, 0xF0, 0xD6, 0xE9, 0x00, 0x00, 0xB5, 0xB5, /* 0x28-0x2B */
+ 0xD6, 0xE8, 0x00, 0x00, 0xB2, 0xCF, 0xB2, 0xD6, /* 0x2C-0x2F */
+ 0xB2, 0xD3, 0xB2, 0xD9, 0xB2, 0xD8, 0xB2, 0xD4, /* 0x30-0x33 */
+ 0x00, 0x00, 0xD6, 0xE2, 0xD6, 0xE5, 0x00, 0x00, /* 0x34-0x37 */
+ 0xD6, 0xE4, 0xB2, 0xD0, 0xD6, 0xE6, 0xD6, 0xEF, /* 0x38-0x3B */
+ 0xB2, 0xD1, 0xD6, 0xE3, 0xD6, 0xEC, 0xD6, 0xED, /* 0x3C-0x3F */
+ 0xB2, 0xD2, 0xD6, 0xEA, 0xB2, 0xD7, 0xB2, 0xCD, /* 0x40-0x43 */
+ 0xB2, 0xD5, 0xD6, 0xE7, 0xB2, 0xCC, 0xD6, 0xEB, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0xD6, 0xEE, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0xDA, 0xFB, 0xDA, 0xF2, /* 0x4C-0x4F */
+ 0xB5, 0xB2, 0xDA, 0xF9, 0xDA, 0xF6, 0xDA, 0xEE, /* 0x50-0x53 */
+ 0xDA, 0xF7, 0xB5, 0xB4, 0xDA, 0xEF, 0x00, 0x00, /* 0x54-0x57 */
+ 0xDA, 0xEB, 0x00, 0x00, 0x00, 0x00, 0xB8, 0x6C, /* 0x58-0x5B */
+ 0xDA, 0xF4, 0x00, 0x00, 0xB5, 0xB1, 0xDA, 0xFA, /* 0x5C-0x5F */
+ 0x00, 0x00, 0xB5, 0xB8, 0xB5, 0xBA, 0xDA, 0xED, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0xB5, 0xB9, 0xDA, 0xF0, /* 0x64-0x67 */
+ 0xB5, 0xB3, 0xDA, 0xF8, 0xDA, 0xF1, 0xDA, 0xF5, /* 0x68-0x6B */
+ 0x00, 0x00, 0xDA, 0xF3, 0xB5, 0xB6, 0xDA, 0xEC, /* 0x6C-0x6F */
+ 0xB5, 0xBB, 0xB2, 0xCE, 0xB5, 0xB7, 0xB5, 0xBC, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0xB8, 0x68, 0xDF, 0x5D, 0xDF, 0x5F, /* 0x78-0x7B */
+ 0xDF, 0x61, 0xDF, 0x65, 0x00, 0x00, 0xDF, 0x5B, /* 0x7C-0x7F */
+
+ 0xDF, 0x59, 0xB8, 0x6A, 0x00, 0x00, 0xDF, 0x60, /* 0x80-0x83 */
+ 0xDF, 0x64, 0xDF, 0x5C, 0xDF, 0x58, 0x00, 0x00, /* 0x84-0x87 */
+ 0xDF, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0xDF, 0x62, 0xDF, 0x5A, 0xDF, 0x5E, 0xB8, 0x6B, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xB8, 0x69, 0xDF, 0x66, 0xB8, 0x67, /* 0x90-0x93 */
+ 0xDF, 0x63, 0x00, 0x00, 0xE3, 0x72, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0xBA, 0xEE, 0xE3, 0x6A, 0xBD, 0x78, 0xE3, 0x74, /* 0x9C-0x9F */
+ 0xBA, 0xF1, 0xE3, 0x78, 0xBA, 0xF7, 0xE3, 0x65, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0xE3, 0x75, 0xE3, 0x62, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0xE3, 0x77, 0xE3, 0x66, 0x00, 0x00, /* 0xA8-0xAB */
+ 0xBA, 0xFE, 0xBA, 0xFB, 0xE3, 0x76, 0xE3, 0x70, /* 0xAC-0xAF */
+ 0xBA, 0xED, 0xBA, 0xF5, 0xBA, 0xF4, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0xBA, 0xF3, 0xBA, 0xF9, 0x00, 0x00, 0xE3, 0x63, /* 0xB4-0xB7 */
+ 0xBA, 0xFA, 0xE3, 0x71, 0xBA, 0xF6, 0xBA, 0xEC, /* 0xB8-0xBB */
+ 0xE3, 0x73, 0xBA, 0xEF, 0xBA, 0xF0, 0xBA, 0xF8, /* 0xBC-0xBF */
+ 0xE3, 0x68, 0xE3, 0x67, 0xE3, 0x64, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xE3, 0x6C, 0xE3, 0x69, 0xE3, 0x6D, 0xBA, 0xFD, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xE3, 0x79, 0xBA, 0xF2, 0xE3, 0x6E, /* 0xC8-0xCB */
+ 0xE3, 0x6F, 0x00, 0x00, 0xE3, 0x6B, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0xBA, 0xFC, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE6, 0xE7, /* 0xD4-0xD7 */
+ 0xBD, 0x70, 0xBD, 0x79, 0xBD, 0x75, 0xE6, 0xE4, /* 0xD8-0xDB */
+ 0x00, 0x00, 0xBD, 0x72, 0xBD, 0x76, 0xE6, 0xF0, /* 0xDC-0xDF */
+ 0xBD, 0x6C, 0xE6, 0xE8, 0x00, 0x00, 0xBD, 0x74, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0xE6, 0xEB, 0xE6, 0xE6, /* 0xE4-0xE7 */
+ 0xBD, 0x73, 0xBD, 0x77, 0xE6, 0xE5, 0x00, 0x00, /* 0xE8-0xEB */
+ 0xBD, 0x71, 0x00, 0x00, 0xE6, 0xEF, 0xBD, 0x6E, /* 0xEC-0xEF */
+ 0xE6, 0xEE, 0xE6, 0xED, 0xBD, 0x7A, 0xE5, 0x72, /* 0xF0-0xF3 */
+ 0xBD, 0x6D, 0x00, 0x00, 0xE6, 0xEC, 0xE6, 0xE3, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xBD, 0x7B, 0xE6, 0xEA, 0xBD, 0x6F, /* 0xF8-0xFB */
+};
+
+static unsigned char u2c_7E[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE6, 0xE9, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0xBF, 0xA2, 0xBF, 0xA7, 0xBF, 0x7E, 0xEA, 0xD8, /* 0x08-0x0B */
+ 0xEA, 0xCF, 0xEA, 0xDB, 0xEA, 0xD3, 0xEA, 0xD9, /* 0x0C-0x0F */
+ 0xBF, 0xA8, 0xBF, 0xA1, 0xEA, 0xCC, 0xEA, 0xD2, /* 0x10-0x13 */
+ 0xEA, 0xDC, 0xEA, 0xD5, 0xEA, 0xDA, 0xEA, 0xCE, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0xEA, 0xD6, 0xBF, 0xA3, /* 0x18-0x1B */
+ 0xEA, 0xD4, 0xBF, 0xA6, 0xBF, 0xA5, 0xEA, 0xD0, /* 0x1C-0x1F */
+ 0xEA, 0xD1, 0xEA, 0xCD, 0xEA, 0xD7, 0xBF, 0xA4, /* 0x20-0x23 */
+ 0xEA, 0xDE, 0xEA, 0xDD, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0xED, 0xDA, 0xED, 0xD6, 0xC1, 0x5F, /* 0x28-0x2B */
+ 0x00, 0x00, 0xED, 0xD0, 0xC1, 0x59, 0xC1, 0x69, /* 0x2C-0x2F */
+ 0xED, 0xDC, 0xC1, 0x61, 0xC1, 0x5D, 0xED, 0xD3, /* 0x30-0x33 */
+ 0xC1, 0x64, 0xC1, 0x67, 0xED, 0xDE, 0xC1, 0x5C, /* 0x34-0x37 */
+ 0xED, 0xD5, 0xC1, 0x65, 0xED, 0xE0, 0xED, 0xDD, /* 0x38-0x3B */
+ 0xED, 0xD1, 0xC1, 0x60, 0xC1, 0x5A, 0xC1, 0x68, /* 0x3C-0x3F */
+ 0xED, 0xD8, 0xC1, 0x63, 0xED, 0xD2, 0xC1, 0x5E, /* 0x40-0x43 */
+ 0xED, 0xDF, 0xC1, 0x62, 0xC1, 0x5B, 0xED, 0xD9, /* 0x44-0x47 */
+ 0xC1, 0x66, 0xED, 0xD7, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0xED, 0xDB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xF0, 0x6E, 0xF0, 0x74, 0xC2, 0xB9, 0xF0, 0x77, /* 0x50-0x53 */
+ 0xC2, 0xB4, 0xC2, 0xB5, 0xF0, 0x6F, 0xF0, 0x76, /* 0x54-0x57 */
+ 0xF0, 0x71, 0xC2, 0xBA, 0xC2, 0xB7, 0x00, 0x00, /* 0x58-0x5B */
+ 0xF0, 0x6D, 0x00, 0x00, 0xC2, 0xB6, 0xF0, 0x73, /* 0x5C-0x5F */
+ 0xF0, 0x75, 0xC2, 0xB8, 0xF0, 0x72, 0xF0, 0x70, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0xF2, 0xB8, 0xC3, 0xB7, 0xC3, 0xB8, 0xC3, 0xB4, /* 0x68-0x6B */
+ 0x00, 0x00, 0xC3, 0xB5, 0x00, 0x00, 0xF2, 0xB4, /* 0x6C-0x6F */
+ 0xF2, 0xB2, 0x00, 0x00, 0xF2, 0xB6, 0xC3, 0xBA, /* 0x70-0x73 */
+ 0xF2, 0xB7, 0xF2, 0xB0, 0xF2, 0xAF, 0xF2, 0xB3, /* 0x74-0x77 */
+ 0xF2, 0xB1, 0xC3, 0xB6, 0xF2, 0xB5, 0xF4, 0xAC, /* 0x78-0x7B */
+ 0xC4, 0x7E, 0xC4, 0x7D, 0xF4, 0xAD, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0xF4, 0xAF, 0xF4, 0xAE, 0xC4, 0xA1, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0xF5, 0xEB, 0xF5, 0xE8, /* 0x84-0x87 */
+ 0xF5, 0xE9, 0x00, 0x00, 0xF5, 0xE7, 0xF5, 0xEA, /* 0x88-0x8B */
+ 0xC4, 0xF2, 0xF5, 0xEC, 0x00, 0x00, 0xC4, 0xF1, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xF7, 0x42, 0x00, 0x00, 0xC5, 0xD5, /* 0x90-0x93 */
+ 0xC5, 0xD7, 0xF7, 0xEE, 0xC5, 0xD6, 0xF8, 0xB9, /* 0x94-0x97 */
+ 0xF9, 0x40, 0xF9, 0x42, 0xF8, 0xFE, 0xF9, 0x41, /* 0x98-0x9B */
+ 0xC6, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+};
+
+static unsigned char u2c_7F[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0xA6, 0xCE, 0x00, 0x00, /* 0x34-0x37 */
+ 0xAC, 0xFB, 0xD2, 0x6F, 0xAF, 0xCA, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0xB2, 0xDA, 0xDA, 0xFC, 0xDA, 0xFD, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEA, 0xDF, /* 0x40-0x43 */
+ 0xC1, 0x6A, 0xED, 0xE1, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0xC2, 0xBB, 0x00, 0x00, 0xF2, 0xBA, 0xF2, 0xB9, /* 0x48-0x4B */
+ 0xC4, 0xA2, 0xF5, 0xED, 0x00, 0x00, 0xF7, 0x43, /* 0x4C-0x4F */
+ 0xC5, 0xF8, 0xCA, 0x49, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0xAA, 0xC9, 0xA8, 0x75, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0xD0, 0x4D, 0x00, 0x00, 0x00, 0x00, 0xD3, 0x60, /* 0x58-0x5B */
+ 0xD3, 0x5B, 0xD3, 0x5F, 0xD3, 0x5D, 0xAF, 0xCB, /* 0x5C-0x5F */
+ 0xD3, 0x5E, 0xD3, 0x5C, 0x00, 0x00, 0xD6, 0xF1, /* 0x60-0x63 */
+ 0x00, 0x00, 0xDA, 0xFE, 0xDB, 0x40, 0xDF, 0x69, /* 0x64-0x67 */
+ 0xDF, 0x6A, 0xB8, 0x6E, 0xB8, 0x6F, 0xDF, 0x68, /* 0x68-0x6B */
+ 0xDF, 0x6B, 0xDF, 0x67, 0xB8, 0x6D, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xBB, 0x40, 0x00, 0x00, 0xB8, 0x70, 0xE3, 0x7A, /* 0x70-0x73 */
+ 0x00, 0x00, 0xBD, 0x7C, 0xE6, 0xF1, 0xBD, 0x7D, /* 0x74-0x77 */
+ 0x00, 0x00, 0xBF, 0xA9, 0xEA, 0xE2, 0xEA, 0xE0, /* 0x78-0x7B */
+ 0xEA, 0xE1, 0xED, 0xE4, 0xED, 0xE3, 0xED, 0xE2, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF2, 0xBB, /* 0x80-0x83 */
+ 0x00, 0x00, 0xC3, 0xB9, 0xF2, 0xBC, 0xF7, 0x44, /* 0x84-0x87 */
+ 0xC5, 0xF9, 0xF8, 0xBA, 0xA6, 0xCF, 0xAA, 0xCB, /* 0x88-0x8B */
+ 0xAA, 0xCA, 0xD0, 0x4F, 0xAC, 0xFC, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xD0, 0x4E, 0xD3, 0x62, 0x00, 0x00, /* 0x90-0x93 */
+ 0xAF, 0xCC, 0xD6, 0xF2, 0xD3, 0x61, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0xB2, 0xDC, 0xD6, 0xF5, /* 0x98-0x9B */
+ 0xD6, 0xF3, 0xD6, 0xF4, 0xB2, 0xDB, 0x00, 0x00, /* 0x9C-0x9F */
+ 0xDB, 0x42, 0xDB, 0x43, 0xDB, 0x41, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0xB8, 0x73, 0xDF, 0x6D, 0xDF, 0x6C, 0xDF, 0x6E, /* 0xA4-0xA7 */
+ 0xB8, 0x72, 0xB8, 0x71, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0xE6, 0xF2, 0xE6, 0xF4, 0x00, 0x00, 0xBD, 0x7E, /* 0xAC-0xAF */
+ 0xE6, 0xF3, 0xEA, 0xE3, 0xBF, 0xAA, 0xF0, 0x79, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0xF0, 0x78, 0xC3, 0xBB, 0xF2, 0xBD, /* 0xB4-0xB7 */
+ 0xC3, 0xBD, 0xC3, 0xBC, 0xF4, 0xB0, 0xF5, 0xEE, /* 0xB8-0xBB */
+ 0xC4, 0xF3, 0xA6, 0xD0, 0xD0, 0x50, 0xAC, 0xFD, /* 0xBC-0xBF */
+ 0xD3, 0x65, 0xAF, 0xCE, 0xD3, 0x64, 0xD3, 0x63, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0xAF, 0xCD, 0x00, 0x00, 0xD6, 0xFB, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xD6, 0xFD, 0xD6, 0xF6, 0xD6, 0xF7, /* 0xC8-0xCB */
+ 0xB2, 0xDD, 0xD6, 0xF8, 0xB2, 0xDE, 0xD6, 0xFC, /* 0xCC-0xCF */
+ 0xD6, 0xF9, 0xD6, 0xFA, 0xB2, 0xDF, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xB5, 0xBE, 0xB5, 0xBF, 0x00, 0x00, 0xDB, 0x44, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDF, 0x6F, /* 0xD8-0xDB */
+ 0xDF, 0x70, 0x00, 0x00, 0xE3, 0x7E, 0xBB, 0x43, /* 0xDC-0xDF */
+ 0xBB, 0x41, 0xBB, 0x42, 0xE3, 0x7B, 0xE3, 0x7C, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0xE3, 0x7D, 0xE6, 0xF9, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xE6, 0xFA, 0xBD, 0xA1, 0xE6, 0xF7, 0xE6, 0xF6, /* 0xE8-0xEB */
+ 0xE6, 0xF8, 0xE6, 0xF5, 0xBF, 0xAD, 0xEA, 0xE4, /* 0xEC-0xEF */
+ 0xBF, 0xAB, 0xBF, 0xAC, 0xED, 0xE6, 0xC1, 0x6B, /* 0xF0-0xF3 */
+ 0xED, 0xE5, 0xEF, 0xA8, 0x00, 0x00, 0xF0, 0x7A, /* 0xF4-0xF7 */
+ 0xF0, 0x7B, 0xC2, 0xBC, 0x00, 0x00, 0xC2, 0xBD, /* 0xF8-0xFB */
+ 0xC1, 0x6C, 0xF2, 0xBE, 0xF2, 0xBF, 0xF4, 0xB1, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_80[512] = {
+ 0xC4, 0xA3, 0xA6, 0xD1, 0x00, 0x00, 0xA6, 0xD2, /* 0x00-0x03 */
+ 0xAC, 0xFE, 0xAA, 0xCC, 0xAF, 0xCF, 0xD0, 0x51, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB5, 0xC0, /* 0x08-0x0B */
+ 0xA6, 0xD3, 0xAD, 0x41, 0xD0, 0x52, 0xD0, 0x53, /* 0x0C-0x0F */
+ 0xAD, 0x40, 0xAD, 0x42, 0xA6, 0xD4, 0x00, 0x00, /* 0x10-0x13 */
+ 0xD0, 0x54, 0xAF, 0xD1, 0xD3, 0x66, 0xAF, 0xD3, /* 0x14-0x17 */
+ 0xAF, 0xD0, 0xAF, 0xD2, 0x00, 0x00, 0xD7, 0x41, /* 0x18-0x1B */
+ 0xB2, 0xE0, 0x00, 0x00, 0xD7, 0x40, 0xD6, 0xFE, /* 0x1C-0x1F */
+ 0x00, 0x00, 0xDF, 0x71, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0xE3, 0xA1, 0x00, 0x00, 0xBD, 0xA2, 0x00, 0x00, /* 0x24-0x27 */
+ 0xBF, 0xAE, 0xEA, 0xE6, 0xEA, 0xE5, 0x00, 0x00, /* 0x28-0x2B */
+ 0xED, 0xE7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xF5, 0xEF, 0x00, 0x00, 0x00, 0x00, 0xA6, 0xD5, /* 0x30-0x33 */
+ 0xCB, 0x73, 0xCD, 0xAA, 0xAD, 0x43, 0xD0, 0x55, /* 0x34-0x37 */
+ 0x00, 0x00, 0xD3, 0x68, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0xAF, 0xD4, 0xD3, 0x67, 0xAF, 0xD5, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD7, 0x43, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0xB2, 0xE2, 0xD7, 0x42, /* 0x44-0x47 */
+ 0xD7, 0x44, 0x00, 0x00, 0xB2, 0xE1, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDB, 0x46, /* 0x4C-0x4F */
+ 0xDB, 0x47, 0xDB, 0x45, 0xB5, 0xC1, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0xB8, 0x74, 0x00, 0x00, /* 0x54-0x57 */
+ 0xB8, 0x75, 0x00, 0x00, 0xBB, 0x45, 0x00, 0x00, /* 0x58-0x5B */
+ 0xE3, 0xA3, 0xE3, 0xA2, 0xBB, 0x44, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0xE6, 0xFB, 0x00, 0x00, 0x00, 0x00, 0xE6, 0xFC, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0xEA, 0xE7, 0x00, 0x00, 0x00, 0x00, 0xC1, 0x70, /* 0x6C-0x6F */
+ 0xC1, 0x6F, 0xC1, 0x6D, 0xC1, 0x6E, 0xC1, 0x71, /* 0x70-0x73 */
+ 0x00, 0x00, 0xF0, 0x7C, 0xC2, 0xBF, 0xC2, 0xBE, /* 0x74-0x77 */
+ 0xF2, 0xC0, 0xF4, 0xB2, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0xC5, 0xA5, 0xC5, 0xA4, 0xA6, 0xD6, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0xD1, 0xFB, 0x00, 0x00, /* 0x80-0x83 */
+ 0xB8, 0x77, 0xB5, 0xC2, 0xB8, 0x76, 0xBB, 0x46, /* 0x84-0x87 */
+ 0x00, 0x00, 0xA6, 0xD7, 0xC9, 0xA9, 0xA6, 0xD8, /* 0x88-0x8B */
+ 0xA6, 0xD9, 0x00, 0x00, 0x00, 0x00, 0xCD, 0xAB, /* 0x8C-0x8F */
+ 0xCB, 0x76, 0x00, 0x00, 0xCB, 0x77, 0xA8, 0x77, /* 0x90-0x93 */
+ 0x00, 0x00, 0xCB, 0x74, 0xA8, 0x76, 0x00, 0x00, /* 0x94-0x97 */
+ 0xA8, 0x79, 0xCB, 0x75, 0xA8, 0x7B, 0xA8, 0x7A, /* 0x98-0x9B */
+ 0xCB, 0x78, 0xA8, 0x78, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0xAA, 0xD1, 0xAA, 0xCF, 0xCD, 0xAD, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0xAA, 0xCE, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0xAA, 0xD3, 0xAA, 0xD5, 0xAA, 0xD2, /* 0xA8-0xAB */
+ 0x00, 0x00, 0xCD, 0xB0, 0xCD, 0xAC, 0xAA, 0xD6, /* 0xAC-0xAF */
+ 0x00, 0x00, 0xAA, 0xD0, 0xA8, 0x7C, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0xAA, 0xD4, 0xCD, 0xAF, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0xCD, 0xAE, 0x00, 0x00, 0xAA, 0xCD, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0xD0, 0x5B, 0xAD, 0x47, /* 0xC0-0xC3 */
+ 0xAD, 0x48, 0xD0, 0x5D, 0x00, 0x00, 0xD0, 0x57, /* 0xC4-0xC7 */
+ 0xD0, 0x5A, 0xD0, 0x63, 0xD0, 0x61, 0x00, 0x00, /* 0xC8-0xCB */
+ 0xAD, 0x49, 0xD0, 0x67, 0xAD, 0x4C, 0xD0, 0x64, /* 0xCC-0xCF */
+ 0xD0, 0x5C, 0xD0, 0x59, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xDB, 0x49, 0xD0, 0x62, 0xAD, 0x44, 0xD0, 0x65, /* 0xD4-0xD7 */
+ 0xD0, 0x56, 0xD0, 0x5F, 0xAD, 0x46, 0xAD, 0x4B, /* 0xD8-0xDB */
+ 0xD0, 0x60, 0xAD, 0x4F, 0xAD, 0x4D, 0x00, 0x00, /* 0xDC-0xDF */
+ 0xD0, 0x58, 0xAD, 0x4A, 0x00, 0x00, 0xD0, 0x5E, /* 0xE0-0xE3 */
+ 0xAD, 0x4E, 0xAD, 0x45, 0xD0, 0x66, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0xAF, 0xDA, 0x00, 0x00, 0xAF, 0xE3, /* 0xEC-0xEF */
+ 0xAF, 0xD8, 0xAF, 0xD6, 0xD3, 0x6A, 0xAF, 0xDE, /* 0xF0-0xF3 */
+ 0xAF, 0xDB, 0xD3, 0x6C, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0xAF, 0xDD, 0xD3, 0x6B, 0xD3, 0x69, 0xD3, 0x6E, /* 0xF8-0xFB */
+ 0xAF, 0xE2, 0xAF, 0xE0, 0xDB, 0x48, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_81[512] = {
+ 0xD3, 0x6F, 0xD3, 0x6D, 0xAF, 0xD7, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0xAF, 0xD9, 0xAF, 0xDC, 0x00, 0x00, /* 0x04-0x07 */
+ 0xAF, 0xDF, 0x00, 0x00, 0xAF, 0xE1, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0xD7, 0x4E, 0xB2, 0xE4, 0x00, 0x00, /* 0x14-0x17 */
+ 0xD7, 0x45, 0xD7, 0x47, 0x00, 0x00, 0xD7, 0x48, /* 0x18-0x1B */
+ 0x00, 0x00, 0xD7, 0x50, 0xD7, 0x4C, 0xD7, 0x4A, /* 0x1C-0x1F */
+ 0x00, 0x00, 0xD7, 0x4D, 0xD7, 0x51, 0xB2, 0xE5, /* 0x20-0x23 */
+ 0xB2, 0xE9, 0xD7, 0x46, 0x00, 0x00, 0xD7, 0x4F, /* 0x24-0x27 */
+ 0x00, 0x00, 0xB2, 0xE7, 0x00, 0x00, 0xB2, 0xE6, /* 0x28-0x2B */
+ 0xD7, 0x4B, 0xD7, 0x49, 0x00, 0x00, 0xB2, 0xE3, /* 0x2C-0x2F */
+ 0xB2, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0xB5, 0xC8, 0xDB, 0x51, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0xDB, 0x4F, 0xB5, 0xCA, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDB, 0x4A, /* 0x40-0x43 */
+ 0xDF, 0xA1, 0x00, 0x00, 0xB5, 0xC9, 0xDB, 0x4E, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0xDB, 0x4B, 0xB5, 0xC5, /* 0x48-0x4B */
+ 0xB5, 0xCB, 0xDB, 0x50, 0xB5, 0xC7, 0xDB, 0x4D, /* 0x4C-0x4F */
+ 0xBB, 0x47, 0xB5, 0xC6, 0xDB, 0x4C, 0xB5, 0xCC, /* 0x50-0x53 */
+ 0xB5, 0xC4, 0xB5, 0xC3, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDF, 0x77, /* 0x58-0x5B */
+ 0xDF, 0x75, 0x00, 0x00, 0xDF, 0x7B, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xDF, 0x73, 0xDF, 0xA2, 0xDF, 0x78, 0x00, 0x00, /* 0x60-0x63 */
+ 0xDF, 0x72, 0xB8, 0x7B, 0xB8, 0xA3, 0xDF, 0x7D, /* 0x64-0x67 */
+ 0x00, 0x00, 0xDF, 0x76, 0x00, 0x00, 0xB8, 0x7E, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0xB8, 0x7C, 0xDF, 0x7E, /* 0x6C-0x6F */
+ 0xB8, 0x79, 0xB8, 0x78, 0xDF, 0x79, 0xB8, 0x7D, /* 0x70-0x73 */
+ 0xB5, 0xCD, 0x00, 0x00, 0xDF, 0x7C, 0xDF, 0x74, /* 0x74-0x77 */
+ 0xB8, 0x7A, 0xB8, 0xA1, 0xB8, 0xA2, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBB, 0x4C, /* 0x7C-0x7F */
+
+ 0xBB, 0x48, 0x00, 0x00, 0xBB, 0x4D, 0xE3, 0xA6, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0xE3, 0xA5, 0xE3, 0xA7, /* 0x84-0x87 */
+ 0xBB, 0x4A, 0xE3, 0xA4, 0xBB, 0x4B, 0xE3, 0xAA, /* 0x88-0x8B */
+ 0xE3, 0xA9, 0xE3, 0xA8, 0x00, 0x00, 0xBB, 0x49, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0xE7, 0x41, 0x00, 0x00, 0xE7, 0x44, /* 0x94-0x97 */
+ 0xBD, 0xA8, 0xE7, 0x43, 0xBD, 0xA7, 0xBD, 0xA3, /* 0x98-0x9B */
+ 0xBD, 0xA4, 0xBD, 0xA5, 0xE7, 0x40, 0xE6, 0xFE, /* 0x9C-0x9F */
+ 0xBD, 0xA6, 0x00, 0x00, 0xE7, 0x42, 0xE6, 0xFD, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0xEA, 0xE9, 0xEA, 0xF3, /* 0xA4-0xA7 */
+ 0xBF, 0xB1, 0xBF, 0xB0, 0x00, 0x00, 0xEA, 0xED, /* 0xA8-0xAB */
+ 0xEA, 0xEF, 0x00, 0x00, 0xEA, 0xEA, 0x00, 0x00, /* 0xAC-0xAF */
+ 0xEA, 0xEE, 0xEA, 0xE8, 0xEA, 0xF1, 0xBF, 0xAF, /* 0xB0-0xB3 */
+ 0xEA, 0xF0, 0xEA, 0xEC, 0x00, 0x00, 0xEA, 0xF2, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xEA, 0xEB, 0xC1, 0x74, 0xED, 0xE8, /* 0xB8-0xBB */
+ 0xED, 0xEE, 0xC1, 0x78, 0xC1, 0x7A, 0xC1, 0x77, /* 0xBC-0xBF */
+ 0xC1, 0x76, 0x00, 0x00, 0xC1, 0x75, 0xC1, 0x73, /* 0xC0-0xC3 */
+ 0xED, 0xE9, 0xED, 0xEC, 0xC1, 0x72, 0xED, 0xED, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xC1, 0x79, 0xED, 0xEB, 0x00, 0x00, /* 0xC8-0xCB */
+ 0xED, 0xEA, 0xC2, 0xC0, 0x00, 0x00, 0xC2, 0xC1, /* 0xCC-0xCF */
+ 0xF0, 0xA1, 0xF0, 0x7D, 0xF0, 0x7E, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0xF2, 0xC2, 0x00, 0x00, 0xF2, 0xC1, /* 0xD4-0xD7 */
+ 0xC3, 0xBE, 0xF4, 0xB4, 0xC4, 0xA4, 0xF4, 0xB3, /* 0xD8-0xDB */
+ 0x00, 0x00, 0xF5, 0xF0, 0xF7, 0x45, 0xC5, 0xA6, /* 0xDC-0xDF */
+ 0xF9, 0x43, 0xF9, 0x44, 0xC5, 0xD8, 0xA6, 0xDA, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0xAA, 0xD7, 0xDB, 0x52, 0xBB, 0x4E, /* 0xE4-0xE7 */
+ 0xC1, 0x7B, 0xED, 0xEF, 0xA6, 0xDB, 0x00, 0x00, /* 0xE8-0xEB */
+ 0xAF, 0xE5, 0xAF, 0xE4, 0xDB, 0x53, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0xEA, 0xF4, 0xA6, 0xDC, /* 0xF0-0xF3 */
+ 0xAD, 0x50, 0x00, 0x00, 0x00, 0x00, 0xDB, 0x54, /* 0xF4-0xF7 */
+ 0xDB, 0x55, 0xDB, 0x56, 0xBB, 0x4F, 0xBF, 0xB2, /* 0xF8-0xFB */
+ 0xA6, 0xDD, 0x00, 0x00, 0xAA, 0xD8, 0xD0, 0x68, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_82[512] = {
+ 0xAF, 0xE6, 0xD3, 0x70, 0xB2, 0xEA, 0x00, 0x00, /* 0x00-0x03 */
+ 0xDB, 0x57, 0xB8, 0xA4, 0x00, 0x00, 0xBB, 0x50, /* 0x04-0x07 */
+ 0xBF, 0xB3, 0xC1, 0x7C, 0xC2, 0xC2, 0xF4, 0xB5, /* 0x08-0x0B */
+ 0xA6, 0xDE, 0xAA, 0xD9, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xAF, 0xE7, 0xD7, 0x52, 0xB5, 0xCE, 0x00, 0x00, /* 0x10-0x13 */
+ 0xBB, 0x51, 0xE3, 0xAB, 0xE7, 0x45, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA6, 0xDF, /* 0x18-0x1B */
+ 0xB5, 0xCF, 0xDF, 0xA3, 0xBB, 0x52, 0xA6, 0xE0, /* 0x1C-0x1F */
+ 0xCD, 0xB1, 0xD0, 0x69, 0xAD, 0x51, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0xD3, 0x72, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0xAF, 0xEA, 0x00, 0x00, 0xAF, 0xE8, 0xAF, 0xE9, /* 0x28-0x2B */
+ 0xAF, 0xEB, 0x00, 0x00, 0x00, 0x00, 0xD3, 0x71, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0xD7, 0x57, 0xD7, 0x54, /* 0x30-0x33 */
+ 0xD7, 0x56, 0xB2, 0xEB, 0xB2, 0xED, 0xB2, 0xEC, /* 0x34-0x37 */
+ 0xD7, 0x53, 0xB2, 0xEE, 0xD7, 0x55, 0x00, 0x00, /* 0x38-0x3B */
+ 0xDB, 0x58, 0xDB, 0x59, 0x00, 0x00, 0xDB, 0x5A, /* 0x3C-0x3F */
+ 0xDF, 0xA6, 0x00, 0x00, 0xDF, 0xA7, 0x00, 0x00, /* 0x40-0x43 */
+ 0xDF, 0xA5, 0xDF, 0xA8, 0x00, 0x00, 0xB8, 0xA5, /* 0x44-0x47 */
+ 0x00, 0x00, 0xDF, 0xA4, 0x00, 0x00, 0xBB, 0x53, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0xE7, 0x4A, 0xE7, 0x46, /* 0x4C-0x4F */
+ 0xE7, 0x49, 0xE7, 0x4B, 0xE7, 0x48, 0xE7, 0x47, /* 0x50-0x53 */
+ 0x00, 0x00, 0xEA, 0xF5, 0xEA, 0xF6, 0xEA, 0xF7, /* 0x54-0x57 */
+ 0xBF, 0xB4, 0xBF, 0xB5, 0xED, 0xF1, 0xED, 0xF0, /* 0x58-0x5B */
+ 0xED, 0xF2, 0x00, 0x00, 0xF0, 0xA3, 0xF0, 0xA2, /* 0x5C-0x5F */
+ 0x00, 0x00, 0xF2, 0xC4, 0x00, 0x00, 0xF2, 0xC5, /* 0x60-0x63 */
+ 0xF2, 0xC3, 0x00, 0x00, 0xC4, 0xA5, 0x00, 0x00, /* 0x64-0x67 */
+ 0xF4, 0xB6, 0xF4, 0xB7, 0x00, 0x00, 0xF7, 0x46, /* 0x68-0x6B */
+ 0xF7, 0xEF, 0xF8, 0xBB, 0xA6, 0xE1, 0xA8, 0x7D, /* 0x6C-0x6F */
+ 0x00, 0x00, 0xC1, 0x7D, 0xA6, 0xE2, 0x00, 0x00, /* 0x70-0x73 */
+ 0xD7, 0x58, 0xDB, 0x5B, 0x00, 0x00, 0xC6, 0x41, /* 0x74-0x77 */
+ 0xCA, 0x4A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0xCA, 0x4B, 0xCA, 0x4D, 0xA6, 0xE3, 0xCA, 0x4E, /* 0x7C-0x7F */
+
+ 0xCA, 0x4C, 0x00, 0x00, 0x00, 0x00, 0xCB, 0xA2, /* 0x80-0x83 */
+ 0xCB, 0xA3, 0xCB, 0x7B, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0xCB, 0xA1, 0xA8, 0xA1, /* 0x88-0x8B */
+ 0x00, 0x00, 0xA8, 0xA2, 0xCB, 0x7C, 0xCB, 0x7A, /* 0x8C-0x8F */
+ 0xCB, 0x79, 0xCB, 0x7D, 0xA8, 0x7E, 0xCB, 0x7E, /* 0x90-0x93 */
+ 0xD0, 0x6A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0xCD, 0xB6, 0xAA, 0xDC, 0xCD, 0xB5, 0xCD, 0xB7, /* 0x98-0x9B */
+ 0x00, 0x00, 0xAA, 0xDB, 0xCD, 0xBC, 0xAA, 0xDF, /* 0x9C-0x9F */
+ 0xCD, 0xB2, 0xCD, 0xC0, 0xCD, 0xC6, 0xAA, 0xE6, /* 0xA0-0xA3 */
+ 0xCD, 0xC3, 0xAA, 0xE3, 0x00, 0x00, 0xCD, 0xB9, /* 0xA4-0xA7 */
+ 0xCD, 0xBF, 0xCD, 0xC1, 0x00, 0x00, 0xCD, 0xB4, /* 0xA8-0xAB */
+ 0xAA, 0xE2, 0xAA, 0xDD, 0xCD, 0xBA, 0xAA, 0xE4, /* 0xAC-0xAF */
+ 0xAA, 0xE7, 0xAA, 0xE1, 0x00, 0x00, 0xAA, 0xDA, /* 0xB0-0xB3 */
+ 0xCD, 0xBE, 0xCD, 0xB8, 0xCD, 0xC5, 0xAA, 0xE9, /* 0xB4-0xB7 */
+ 0xAA, 0xE5, 0xAA, 0xE0, 0xCD, 0xBD, 0xAF, 0xEC, /* 0xB8-0xBB */
+ 0xCD, 0xBB, 0xAA, 0xDE, 0xAA, 0xE8, 0x00, 0x00, /* 0xBC-0xBF */
+ 0xCD, 0xB3, 0x00, 0x00, 0xCD, 0xC2, 0xCD, 0xC4, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0xAD, 0x62, 0xAD, 0x5C, 0xAD, 0x64, /* 0xD0-0xD3 */
+ 0xAD, 0x61, 0xD0, 0x71, 0xD0, 0x74, 0xAD, 0x5D, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0xD0, 0x6B, 0x00, 0x00, 0xAD, 0x56, /* 0xD8-0xDB */
+ 0xAD, 0x60, 0x00, 0x00, 0xAD, 0x63, 0xAD, 0x65, /* 0xDC-0xDF */
+ 0xD0, 0xA2, 0xD0, 0x77, 0x00, 0x00, 0xAD, 0x55, /* 0xE0-0xE3 */
+ 0xD0, 0xA1, 0xAD, 0x59, 0xAD, 0x57, 0xAD, 0x52, /* 0xE4-0xE7 */
+ 0xD0, 0x6F, 0x00, 0x00, 0xD0, 0x7E, 0xD0, 0x73, /* 0xE8-0xEB */
+ 0xD0, 0x76, 0xD0, 0xA5, 0x00, 0x00, 0xAD, 0x66, /* 0xEC-0xEF */
+ 0xD0, 0x7D, 0xAD, 0x5E, 0xD0, 0x78, 0xD0, 0xA4, /* 0xF0-0xF3 */
+ 0xD0, 0x75, 0xD0, 0x79, 0xD0, 0x7C, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xD0, 0x6D, 0xD0, 0xA3, 0xD0, 0x7B, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0xD0, 0x6C, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_83[512] = {
+ 0xD0, 0x70, 0xAD, 0x5F, 0xAD, 0x5A, 0xAD, 0x53, /* 0x00-0x03 */
+ 0xAD, 0x58, 0xAD, 0x54, 0xAD, 0x67, 0xD0, 0x6E, /* 0x04-0x07 */
+ 0xD3, 0xA5, 0xAD, 0x5B, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0xD0, 0x7A, 0xCE, 0x41, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0xD3, 0xA8, 0xAF, 0xFA, /* 0x14-0x17 */
+ 0x00, 0x00, 0xD3, 0x76, 0x00, 0x00, 0xD3, 0xA3, /* 0x18-0x1B */
+ 0xD3, 0x7D, 0x00, 0x00, 0xD3, 0xB2, 0x00, 0x00, /* 0x1C-0x1F */
+ 0xD3, 0xAA, 0x00, 0x00, 0xD3, 0x7E, 0x00, 0x00, /* 0x20-0x23 */
+ 0xD3, 0xA9, 0xD3, 0x78, 0xD3, 0x7C, 0xD3, 0xB5, /* 0x24-0x27 */
+ 0xAF, 0xFD, 0xD3, 0xAD, 0xD3, 0xA4, 0xAF, 0xED, /* 0x28-0x2B */
+ 0xD3, 0xB3, 0xD3, 0x74, 0x00, 0x00, 0xD3, 0xAC, /* 0x2C-0x2F */
+ 0x00, 0x00, 0xAF, 0xFC, 0xAF, 0xF7, 0xD3, 0x73, /* 0x30-0x33 */
+ 0xAF, 0xF5, 0xAF, 0xF4, 0xAF, 0xF9, 0xD3, 0xAB, /* 0x34-0x37 */
+ 0xAF, 0xF1, 0xAF, 0xF8, 0xD0, 0x72, 0xDB, 0x5C, /* 0x38-0x3B */
+ 0xD3, 0xA6, 0x00, 0x00, 0x00, 0x00, 0xD3, 0x7A, /* 0x3C-0x3F */
+ 0xAF, 0xFB, 0xD3, 0x7B, 0xD3, 0xA1, 0xAF, 0xFE, /* 0x40-0x43 */
+ 0xD3, 0x75, 0xD3, 0xAF, 0x00, 0x00, 0xD3, 0xAE, /* 0x44-0x47 */
+ 0xD3, 0xB6, 0xAF, 0xF3, 0xAF, 0xF0, 0xD3, 0xB4, /* 0x48-0x4B */
+ 0xD3, 0xB0, 0xD3, 0xA7, 0xD3, 0xA2, 0xAF, 0xF6, /* 0x4C-0x4F */
+ 0xAF, 0xF2, 0xD3, 0x77, 0xAF, 0xEE, 0xD3, 0xB1, /* 0x50-0x53 */
+ 0xAF, 0xEF, 0x00, 0x00, 0xD3, 0x79, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD7, 0x5E, /* 0x70-0x73 */
+ 0xD7, 0x60, 0xD7, 0x65, 0xD7, 0x79, 0xB2, 0xFC, /* 0x74-0x77 */
+ 0xB2, 0xF2, 0x00, 0x00, 0xD7, 0x5D, 0xB2, 0xFD, /* 0x78-0x7B */
+ 0xB2, 0xFE, 0xD7, 0x68, 0xD7, 0x6F, 0xD7, 0x75, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0xD7, 0x62, 0x00, 0x00, 0xD7, 0x69, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0xB3, 0x40, 0xD7, 0x77, /* 0x84-0x87 */
+ 0xD7, 0x72, 0xB2, 0xFA, 0xB2, 0xF8, 0xD7, 0x6E, /* 0x88-0x8B */
+ 0xD7, 0x6A, 0xD7, 0x5C, 0xB2, 0xEF, 0xD7, 0x61, /* 0x8C-0x8F */
+ 0xD7, 0x59, 0x00, 0x00, 0xB2, 0xF7, 0xB2, 0xF9, /* 0x90-0x93 */
+ 0xD7, 0x66, 0xD7, 0x63, 0xB2, 0xF4, 0xD7, 0x73, /* 0x94-0x97 */
+ 0xB2, 0xF1, 0xD7, 0x64, 0xD7, 0x7A, 0xD7, 0x6C, /* 0x98-0x9B */
+ 0x00, 0x00, 0xD7, 0x6B, 0xB2, 0xF0, 0x00, 0x00, /* 0x9C-0x9F */
+ 0xB2, 0xFB, 0x00, 0x00, 0xB2, 0xF3, 0xD7, 0x5A, /* 0xA0-0xA3 */
+ 0xD7, 0x5F, 0xD7, 0x70, 0xD7, 0x76, 0xB3, 0x41, /* 0xA4-0xA7 */
+ 0xD7, 0x5B, 0xD7, 0x67, 0xD7, 0x6D, 0xB2, 0xF6, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0xD7, 0x78, 0xD7, 0x71, /* 0xAC-0xAF */
+ 0xD7, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0xB2, 0xF5, 0x00, 0x00, 0xDB, 0x6C, /* 0xBC-0xBF */
+ 0xDB, 0x60, 0xB5, 0xD7, 0xDB, 0x7D, 0xDB, 0xA7, /* 0xC0-0xC3 */
+ 0xDB, 0xAA, 0xB5, 0xD5, 0xDB, 0x68, 0xDB, 0xA3, /* 0xC4-0xC7 */
+ 0xDB, 0x69, 0xDB, 0x77, 0xB5, 0xE2, 0xDB, 0x73, /* 0xC8-0xCB */
+ 0xB5, 0xDF, 0x00, 0x00, 0xDB, 0x74, 0xDB, 0x5D, /* 0xCC-0xCF */
+ 0x00, 0x00, 0xDB, 0xA4, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xB5, 0xE8, 0xDB, 0xA1, 0xDB, 0x75, 0xDB, 0xAC, /* 0xD4-0xD7 */
+ 0xDB, 0x70, 0xDF, 0xC8, 0x00, 0x00, 0xDB, 0xAF, /* 0xD8-0xDB */
+ 0xB5, 0xE6, 0xDB, 0x6E, 0xDB, 0x7A, 0xB5, 0xE9, /* 0xDC-0xDF */
+ 0xB5, 0xD4, 0xDB, 0x72, 0xDB, 0xAD, 0xDB, 0x6B, /* 0xE0-0xE3 */
+ 0xDB, 0x64, 0xDB, 0x6F, 0x00, 0x00, 0xDB, 0x63, /* 0xE4-0xE7 */
+ 0xDB, 0x61, 0xB5, 0xD0, 0xDB, 0xA5, 0xDB, 0x6A, /* 0xE8-0xEB */
+ 0xDB, 0xA8, 0x00, 0x00, 0xDB, 0xA9, 0xB5, 0xD8, /* 0xEC-0xEF */
+ 0xB5, 0xDD, 0xB5, 0xD9, 0xB5, 0xE1, 0xDB, 0x7E, /* 0xF0-0xF3 */
+ 0xB5, 0xDA, 0xDB, 0x76, 0xDB, 0x66, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0xB5, 0xD2, 0xDB, 0x5E, 0xDB, 0xA2, 0xDB, 0xAB, /* 0xF8-0xFB */
+ 0xDB, 0x65, 0xB5, 0xE0, 0xDB, 0xB0, 0xDB, 0x71, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_84[512] = {
+ 0x00, 0x00, 0xDB, 0x6D, 0x00, 0x00, 0xB5, 0xD1, /* 0x00-0x03 */
+ 0xB5, 0xE5, 0x00, 0x00, 0xDB, 0x7C, 0xB5, 0xE7, /* 0x04-0x07 */
+ 0x00, 0x00, 0xDB, 0x78, 0xB5, 0xDC, 0xB5, 0xD6, /* 0x08-0x0B */
+ 0xB5, 0xDE, 0xB5, 0xD3, 0xB5, 0xE4, 0xDB, 0x79, /* 0x0C-0x0F */
+ 0xDB, 0x67, 0xDB, 0x7B, 0xDB, 0x62, 0xDB, 0xA6, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDB, 0xAE, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDB, 0x5F, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0xDF, 0xC7, 0x00, 0x00, 0xDF, 0xDD, /* 0x28-0x2B */
+ 0xB8, 0x55, 0xDF, 0xCC, 0x00, 0x00, 0xDF, 0xCA, /* 0x2C-0x2F */
+ 0xDF, 0xB5, 0xB8, 0xA9, 0xDF, 0xC5, 0xDF, 0xD9, /* 0x30-0x33 */
+ 0xDF, 0xC1, 0xB8, 0xB1, 0xDF, 0xD8, 0xDF, 0xBF, /* 0x34-0x37 */
+ 0xB5, 0xE3, 0xDF, 0xCF, 0xDF, 0xC0, 0xDF, 0xD6, /* 0x38-0x3B */
+ 0xB8, 0xB0, 0xB8, 0xA8, 0x00, 0x00, 0xDF, 0xAA, /* 0x3C-0x3F */
+ 0xDF, 0xB2, 0x00, 0x00, 0xDF, 0xCB, 0xDF, 0xC3, /* 0x40-0x43 */
+ 0xDF, 0xDC, 0xDF, 0xC6, 0xB8, 0xB6, 0xDF, 0xD7, /* 0x44-0x47 */
+ 0x00, 0x00, 0xB8, 0xAD, 0x00, 0x00, 0xDF, 0xC9, /* 0x48-0x4B */
+ 0xDF, 0xD1, 0xDF, 0xB6, 0xDF, 0xD0, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xDF, 0xE1, 0xDF, 0xB1, 0xDF, 0xD2, 0x00, 0x00, /* 0x50-0x53 */
+ 0xDF, 0xDF, 0x00, 0x00, 0xDF, 0xAB, 0xB5, 0xDB, /* 0x54-0x57 */
+ 0x00, 0x00, 0xDF, 0xB9, 0xDF, 0xB8, 0xB8, 0xAF, /* 0x58-0x5B */
+ 0x00, 0x00, 0xDF, 0xBC, 0xDF, 0xBE, 0xDF, 0xCD, /* 0x5C-0x5F */
+ 0xDF, 0xDE, 0xB8, 0xB2, 0x00, 0x00, 0xB8, 0xB3, /* 0x60-0x63 */
+ 0x00, 0x00, 0xDF, 0xB0, 0xB8, 0xAB, 0xDF, 0xB4, /* 0x64-0x67 */
+ 0xDF, 0xDA, 0xB8, 0xB4, 0x00, 0x00, 0xB8, 0xAC, /* 0x68-0x6B */
+ 0xB8, 0xAE, 0xB8, 0xB5, 0xDF, 0xE0, 0xDF, 0xD3, /* 0x6C-0x6F */
+ 0xDF, 0xCE, 0x00, 0x00, 0x00, 0x00, 0xDF, 0xBB, /* 0x70-0x73 */
+ 0xDF, 0xBA, 0xB8, 0xAA, 0xDF, 0xAC, 0xB8, 0xA7, /* 0x74-0x77 */
+ 0xDF, 0xC4, 0xDF, 0xAD, 0xDF, 0xC2, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0xDF, 0xB7, 0xDF, 0xDB, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0xB8, 0xA6, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0xDF, 0xB3, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0xDF, 0xAF, 0xDF, 0xD5, 0xDF, 0xAE, /* 0x8C-0x8F */
+ 0xBB, 0x60, 0xE3, 0xD3, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0xE3, 0xC2, 0x00, 0x00, 0x00, 0x00, 0xE3, 0xAC, /* 0x94-0x97 */
+ 0xE3, 0xCA, 0xBB, 0x58, 0xE3, 0xBB, 0xE3, 0xC5, /* 0x98-0x9B */
+ 0xBB, 0x5B, 0xE3, 0xBE, 0xBB, 0x59, 0xE3, 0xAF, /* 0x9C-0x9F */
+ 0xE3, 0xCD, 0xE3, 0xAE, 0xE3, 0xC1, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0xE3, 0xAD, 0x00, 0x00, 0x00, 0x00, 0xE3, 0xBF, /* 0xA4-0xA7 */
+ 0xE3, 0xC8, 0xE3, 0xC6, 0xE3, 0xBA, 0xE3, 0xB5, /* 0xA8-0xAB */
+ 0xE3, 0xB3, 0x00, 0x00, 0xE3, 0xB4, 0xE3, 0xC7, /* 0xAC-0xAF */
+ 0xE3, 0xD2, 0xE3, 0xBC, 0xBB, 0x5A, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0xE3, 0xB7, 0x00, 0x00, 0xE3, 0xCB, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0xBB, 0x5D, 0xE3, 0xB6, 0xE3, 0xB0, 0xE3, 0xC0, /* 0xB8-0xBB */
+ 0xBB, 0x61, 0x00, 0x00, 0x00, 0x00, 0xBB, 0x55, /* 0xBC-0xBF */
+ 0xBB, 0x5E, 0xE3, 0xB8, 0xE3, 0xB2, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xBB, 0x57, 0xDF, 0xD4, 0xBB, 0x56, 0xE3, 0xC3, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xBB, 0x54, 0xBB, 0x63, 0xBB, 0x5C, /* 0xC8-0xCB */
+ 0xE3, 0xC4, 0xE3, 0xB9, 0xE3, 0xB1, 0xE3, 0xCC, /* 0xCC-0xCF */
+ 0xE3, 0xBD, 0xBB, 0x62, 0xE3, 0xD0, 0xBB, 0x5F, /* 0xD0-0xD3 */
+ 0xE3, 0xCF, 0x00, 0x00, 0xE3, 0xC9, 0xE3, 0xCE, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE3, 0xD1, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE7, 0x73, /* 0xE4-0xE7 */
+ 0xE7, 0x74, 0xE7, 0x67, 0xE7, 0x66, 0xE7, 0x62, /* 0xE8-0xEB */
+ 0xBD, 0xB4, 0x00, 0x00, 0xBD, 0xAC, 0xE7, 0x76, /* 0xEC-0xEF */
+ 0xE7, 0x75, 0xDF, 0xA9, 0xE7, 0x5F, 0xE7, 0x63, /* 0xF0-0xF3 */
+ 0xE7, 0x5D, 0x00, 0x00, 0xE7, 0x70, 0xE7, 0x61, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xE7, 0x77, 0xE7, 0x5A, 0xE7, 0x58, /* 0xF8-0xFB */
+ 0xE7, 0x64, 0xE7, 0x6E, 0xE7, 0x69, 0xBD, 0xB6, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_85[512] = {
+ 0xE7, 0x4F, 0x00, 0x00, 0xE7, 0x6D, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0xBD, 0xB7, 0xDF, 0xBD, /* 0x04-0x07 */
+ 0xE7, 0x5B, 0xE7, 0x52, 0xE7, 0x55, 0xE7, 0x7B, /* 0x08-0x0B */
+ 0xE7, 0x5C, 0xE7, 0x53, 0xE7, 0x51, 0xE7, 0x4E, /* 0x0C-0x0F */
+ 0x00, 0x00, 0xBD, 0xB0, 0xE7, 0x65, 0xBD, 0xAF, /* 0x10-0x13 */
+ 0xBD, 0xB3, 0xE7, 0x60, 0xE7, 0x68, 0xBD, 0xA9, /* 0x14-0x17 */
+ 0xE7, 0x78, 0xE7, 0x7C, 0xBD, 0xAB, 0x00, 0x00, /* 0x18-0x1B */
+ 0xE7, 0x57, 0xE7, 0x6B, 0xE7, 0x6F, 0xE7, 0x54, /* 0x1C-0x1F */
+ 0xE7, 0x79, 0xBD, 0xB2, 0x00, 0x00, 0xBD, 0xB1, /* 0x20-0x23 */
+ 0xE7, 0x4C, 0xBD, 0xB5, 0xE7, 0x72, 0xE7, 0x56, /* 0x24-0x27 */
+ 0xE7, 0x6A, 0xE7, 0x50, 0xE7, 0x5E, 0xE7, 0x59, /* 0x28-0x2B */
+ 0xBD, 0xAD, 0xBD, 0xAE, 0xE7, 0x6C, 0xE7, 0x7D, /* 0x2C-0x2F */
+ 0xE7, 0x7A, 0xE7, 0x71, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE7, 0x4D, /* 0x38-0x3B */
+ 0x00, 0x00, 0xBD, 0xAA, 0xEB, 0x49, 0x00, 0x00, /* 0x3C-0x3F */
+ 0xEB, 0x40, 0xEB, 0x43, 0x00, 0x00, 0xBF, 0xBB, /* 0x40-0x43 */
+ 0xEB, 0x45, 0xEA, 0xF9, 0xEB, 0x41, 0xEB, 0x47, /* 0x44-0x47 */
+ 0xBF, 0xB8, 0xBF, 0xBC, 0xBF, 0xB6, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0xEA, 0xFB, 0xEB, 0x4C, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0xEB, 0x46, 0x00, 0x00, 0xEA, 0xFC, /* 0x50-0x53 */
+ 0xEB, 0x55, 0xEB, 0x4F, 0xEA, 0xF8, 0xEE, 0x46, /* 0x54-0x57 */
+ 0xEA, 0xFE, 0xBF, 0xB7, 0x00, 0x00, 0xEB, 0x4A, /* 0x58-0x5B */
+ 0x00, 0x00, 0xEB, 0x54, 0xBF, 0xBF, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xEB, 0x51, 0xEA, 0xFD, 0xEB, 0x44, 0xEB, 0x48, /* 0x60-0x63 */
+ 0xEB, 0x42, 0xEB, 0x56, 0xEB, 0x53, 0xEB, 0x50, /* 0x64-0x67 */
+ 0xBF, 0xB9, 0xBF, 0xBA, 0xBF, 0xBE, 0xEA, 0xFA, /* 0x68-0x6B */
+ 0xEB, 0x57, 0xBF, 0xBD, 0xEB, 0x4D, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0xEB, 0x4B, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0xEB, 0x4E, 0xEE, 0x53, 0xEE, 0x40, /* 0x74-0x77 */
+ 0xEE, 0x45, 0xEE, 0x52, 0xEE, 0x44, 0xED, 0xFB, /* 0x78-0x7B */
+ 0xEE, 0x41, 0x00, 0x00, 0xC1, 0xA2, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0xED, 0xF4, 0xEE, 0x4D, 0xEE, 0x4F, 0xED, 0xF3, /* 0x80-0x83 */
+ 0xC1, 0xA1, 0xEE, 0x51, 0xEE, 0x49, 0xC1, 0xA8, /* 0x84-0x87 */
+ 0xEE, 0x50, 0xEE, 0x42, 0xC1, 0xAA, 0xED, 0xF9, /* 0x88-0x8B */
+ 0xEB, 0x52, 0xEE, 0x4A, 0xEE, 0x47, 0xED, 0xF5, /* 0x8C-0x8F */
+ 0xEE, 0x55, 0xC1, 0xA4, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0xC1, 0xA5, 0xED, 0xF7, 0xEE, 0x48, 0x00, 0x00, /* 0x94-0x97 */
+ 0xEE, 0x54, 0xEE, 0x4B, 0xED, 0xFD, 0xC1, 0xA7, /* 0x98-0x9B */
+ 0xC1, 0xA3, 0xEE, 0x4C, 0xED, 0xFE, 0xEE, 0x56, /* 0x9C-0x9F */
+ 0xED, 0xF8, 0xEE, 0x43, 0xEE, 0x4E, 0xED, 0xFA, /* 0xA0-0xA3 */
+ 0xED, 0xFC, 0x00, 0x00, 0xC2, 0xCB, 0xED, 0xF6, /* 0xA4-0xA7 */
+ 0xC1, 0xA9, 0xC2, 0xC4, 0xC1, 0x7E, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC1, 0xA6, /* 0xAC-0xAF */
+ 0xC2, 0xC8, 0xF0, 0xB3, 0x00, 0x00, 0xF0, 0xA9, /* 0xB0-0xB3 */
+ 0xF0, 0xA4, 0xF0, 0xAA, 0xF0, 0xB4, 0xF0, 0xB8, /* 0xB4-0xB7 */
+ 0xF0, 0xB7, 0xC2, 0xCA, 0xC2, 0xC9, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0xF0, 0xAB, 0xF0, 0xB9, 0xF0, 0xAE, /* 0xBC-0xBF */
+ 0xF0, 0xA6, 0x00, 0x00, 0xF0, 0xA8, 0xF0, 0xA7, /* 0xC0-0xC3 */
+ 0xF0, 0xAD, 0xF0, 0xB2, 0xF0, 0xA5, 0xF0, 0xAC, /* 0xC4-0xC7 */
+ 0xF0, 0xB1, 0xC2, 0xC7, 0x00, 0x00, 0xF0, 0xAF, /* 0xC8-0xCB */
+ 0x00, 0x00, 0xC2, 0xC5, 0xF0, 0xB0, 0xC2, 0xC3, /* 0xCC-0xCF */
+ 0xC2, 0xC6, 0xF2, 0xD5, 0xF0, 0xB5, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0xC3, 0xC2, 0x00, 0x00, 0xF2, 0xCD, /* 0xD4-0xD7 */
+ 0xF2, 0xD1, 0xF2, 0xC9, 0xF2, 0xCC, 0x00, 0x00, /* 0xD8-0xDB */
+ 0xF2, 0xD4, 0xC3, 0xC0, 0xF2, 0xD9, 0xF2, 0xD2, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xF2, 0xCA, 0xF2, 0xDA, 0xF2, 0xD3, /* 0xE0-0xE3 */
+ 0xC3, 0xC3, 0xC3, 0xC4, 0xF2, 0xD7, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xF2, 0xCB, 0xC3, 0xBF, 0xC3, 0xC1, 0xF2, 0xC6, /* 0xE8-0xEB */
+ 0xF2, 0xCE, 0xF2, 0xC8, 0x00, 0x00, 0xF2, 0xD8, /* 0xEC-0xEF */
+ 0xF2, 0xD6, 0xF2, 0xC7, 0xF2, 0xCF, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0xF4, 0xBE, 0xC3, 0xC5, /* 0xF4-0xF7 */
+ 0xF2, 0xD0, 0xC4, 0xA7, 0xC4, 0xA9, 0xC4, 0xA6, /* 0xF8-0xFB */
+ 0x00, 0x00, 0xF4, 0xC3, 0xF4, 0xBB, 0xF4, 0xB9, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_86[512] = {
+ 0xF4, 0xBD, 0xF4, 0xBA, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0xF4, 0xBF, 0xF4, 0xC1, 0xC4, 0xAA, 0xC4, 0xAC, /* 0x04-0x07 */
+ 0x00, 0x00, 0xF4, 0xC0, 0xC4, 0xAD, 0xC4, 0xAB, /* 0x08-0x0B */
+ 0xF4, 0xC2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0xC4, 0xA8, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC4, 0xF4, /* 0x14-0x17 */
+ 0xF5, 0xF1, 0xF5, 0xF7, 0xC4, 0xF6, 0xF4, 0xBC, /* 0x18-0x1B */
+ 0xF5, 0xF6, 0x00, 0x00, 0xF5, 0xFD, 0xF5, 0xF4, /* 0x1C-0x1F */
+ 0xF5, 0xFB, 0xF5, 0xFA, 0xF4, 0xB8, 0xF5, 0xF5, /* 0x20-0x23 */
+ 0xF0, 0xB6, 0xF5, 0xFE, 0xF5, 0xF3, 0xF5, 0xF8, /* 0x24-0x27 */
+ 0x00, 0x00, 0xF5, 0xFC, 0xF5, 0xF2, 0x00, 0x00, /* 0x28-0x2B */
+ 0xF7, 0x4A, 0xC4, 0xF5, 0xF5, 0xF9, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0xF7, 0xF4, 0xF7, 0x4B, 0xF7, 0x49, /* 0x30-0x33 */
+ 0xF7, 0x47, 0xF7, 0x48, 0xF7, 0x4C, 0x00, 0x00, /* 0x34-0x37 */
+ 0xC5, 0xD9, 0xF7, 0xF2, 0xF7, 0xF0, 0xF7, 0xF5, /* 0x38-0x3B */
+ 0xF7, 0xF3, 0x00, 0x00, 0xF7, 0xF6, 0xC5, 0xDA, /* 0x3C-0x3F */
+ 0xF7, 0xF1, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xBC, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0xF9, 0x45, 0xF9, 0x46, /* 0x44-0x47 */
+ 0xF9, 0x47, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xC7, /* 0x48-0x4B */
+ 0xF9, 0xBD, 0xCA, 0x4F, 0xAA, 0xEA, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xAD, 0x68, 0x00, 0x00, 0xD3, 0xB8, 0xD3, 0xB7, /* 0x50-0x53 */
+ 0xB0, 0x40, 0xB3, 0x42, 0xD7, 0x7C, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0xD7, 0x7B, 0x00, 0x00, 0xB5, 0xEA, /* 0x58-0x5B */
+ 0xB8, 0xB8, 0x00, 0x00, 0xB8, 0xB7, 0xB8, 0xB9, /* 0x5C-0x5F */
+ 0x00, 0x00, 0xE3, 0xD4, 0xE7, 0x7E, 0xEB, 0x58, /* 0x60-0x63 */
+ 0xEB, 0x5A, 0xEB, 0x59, 0x00, 0x00, 0xC1, 0xAB, /* 0x64-0x67 */
+ 0xEE, 0x57, 0xF0, 0xBA, 0xF9, 0xA5, 0xA6, 0xE4, /* 0x68-0x6B */
+ 0x00, 0x00, 0xCD, 0xC9, 0xCD, 0xCA, 0xCD, 0xC8, /* 0x6C-0x6F */
+ 0xCD, 0xC7, 0xAA, 0xEB, 0x00, 0x00, 0xD0, 0xA9, /* 0x70-0x73 */
+ 0xD0, 0xA7, 0x00, 0x00, 0x00, 0x00, 0xD0, 0xA6, /* 0x74-0x77 */
+ 0x00, 0x00, 0xAD, 0x69, 0xAD, 0x6B, 0xAD, 0x6A, /* 0x78-0x7B */
+ 0xD0, 0xA8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0xD3, 0xC4, 0xD3, 0xC1, 0xD3, 0xBF, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0xB0, 0x41, 0xD3, 0xC2, /* 0x88-0x8B */
+ 0xB0, 0x46, 0xD3, 0xBC, 0xD3, 0xCB, 0x00, 0x00, /* 0x8C-0x8F */
+ 0xD3, 0xCD, 0xD3, 0xBD, 0x00, 0x00, 0xB0, 0x43, /* 0x90-0x93 */
+ 0xD3, 0xCE, 0xD3, 0xC9, 0xD3, 0xBB, 0xD3, 0xC0, /* 0x94-0x97 */
+ 0xD3, 0xCA, 0xD3, 0xC6, 0xD3, 0xC3, 0x00, 0x00, /* 0x98-0x9B */
+ 0xB0, 0x48, 0xD3, 0xCC, 0xD3, 0xBE, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0xD3, 0xC7, 0xD3, 0xB9, 0xB0, 0x47, /* 0xA0-0xA3 */
+ 0xB0, 0x44, 0xD3, 0xC5, 0x00, 0x00, 0xD3, 0xC8, /* 0xA4-0xA7 */
+ 0xD3, 0xBA, 0xB0, 0x45, 0xB0, 0x42, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB3, 0x4C, /* 0xAC-0xAF */
+ 0xD7, 0xA5, 0xB3, 0x4B, 0x00, 0x00, 0xD7, 0xA8, /* 0xB0-0xB3 */
+ 0xD7, 0xAB, 0xB3, 0x48, 0xB3, 0x46, 0xD7, 0x7E, /* 0xB4-0xB7 */
+ 0xD7, 0xA9, 0xD7, 0xA7, 0xD7, 0xA4, 0xD7, 0xAC, /* 0xB8-0xBB */
+ 0xD7, 0xAD, 0xD7, 0xAF, 0xD7, 0xB0, 0xD7, 0x7D, /* 0xBC-0xBF */
+ 0xB3, 0x45, 0xD7, 0xA2, 0xD7, 0xA1, 0xD7, 0xAE, /* 0xC0-0xC3 */
+ 0xB3, 0x47, 0xD7, 0xA3, 0xB3, 0x49, 0xB3, 0x44, /* 0xC4-0xC7 */
+ 0xD7, 0xA6, 0xB3, 0x4D, 0x00, 0x00, 0xB3, 0x4A, /* 0xC8-0xCB */
+ 0xD7, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0xB5, 0xF1, 0xDB, 0xBF, 0x00, 0x00, 0xDB, 0xB4, /* 0xD0-0xD3 */
+ 0xB5, 0xEE, 0x00, 0x00, 0xDF, 0xE7, 0xDB, 0xBD, /* 0xD4-0xD7 */
+ 0xDB, 0xB1, 0xB5, 0xEC, 0xDB, 0xB6, 0xB5, 0xEF, /* 0xD8-0xDB */
+ 0xDB, 0xBA, 0xDB, 0xB8, 0xB5, 0xF2, 0xB5, 0xEB, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0xDB, 0xB2, 0xDB, 0xB5, /* 0xE0-0xE3 */
+ 0xB5, 0xF0, 0x00, 0x00, 0xDB, 0xB3, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xDB, 0xBE, 0xDB, 0xBC, 0xDB, 0xB7, 0xDB, 0xB9, /* 0xE8-0xEB */
+ 0xDB, 0xBB, 0xB5, 0xED, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0xDF, 0xE8, 0xDF, 0xEE, 0xDF, 0xE4, /* 0xF4-0xF7 */
+ 0xDF, 0xEA, 0xB8, 0xBA, 0xDF, 0xE6, 0xB8, 0xC0, /* 0xF8-0xFB */
+ 0x00, 0x00, 0x00, 0x00, 0xB8, 0xBF, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_87[512] = {
+ 0xB8, 0xBE, 0xDF, 0xED, 0xB8, 0xC1, 0xB8, 0xC2, /* 0x00-0x03 */
+ 0xDF, 0xE3, 0xDF, 0xF0, 0xB8, 0xC3, 0xB8, 0xBD, /* 0x04-0x07 */
+ 0xB8, 0xBC, 0xDF, 0xEC, 0xB8, 0xC4, 0xDF, 0xE2, /* 0x08-0x0B */
+ 0xDF, 0xE5, 0xDF, 0xEF, 0xDF, 0xEB, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0xE3, 0xF4, 0xE3, 0xE9, 0xB8, 0xBB, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0xBB, 0x6A, 0xE3, 0xDD, 0xE3, 0xF2, 0xE3, 0xDE, /* 0x18-0x1B */
+ 0xBB, 0x65, 0x00, 0x00, 0xE3, 0xDB, 0x00, 0x00, /* 0x1C-0x1F */
+ 0xE3, 0xE4, 0xE3, 0xDC, 0xBB, 0x67, 0xE3, 0xD6, /* 0x20-0x23 */
+ 0xE3, 0xF1, 0xBB, 0x68, 0xE3, 0xEE, 0xE3, 0xEF, /* 0x24-0x27 */
+ 0xE3, 0xD7, 0xBB, 0x6D, 0xE3, 0xE6, 0x00, 0x00, /* 0x28-0x2B */
+ 0xE3, 0xE0, 0xE3, 0xE7, 0xE3, 0xDA, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xE3, 0xF3, 0xE3, 0xEB, 0xE3, 0xE5, 0xE3, 0xD5, /* 0x30-0x33 */
+ 0xBB, 0x69, 0xE3, 0xEC, 0x00, 0x00, 0xBB, 0x6C, /* 0x34-0x37 */
+ 0xE3, 0xF0, 0x00, 0x00, 0xE3, 0xEA, 0xBB, 0x66, /* 0x38-0x3B */
+ 0xE3, 0xE8, 0x00, 0x00, 0xE3, 0xE2, 0xBB, 0x64, /* 0x3C-0x3F */
+ 0xE3, 0xD9, 0xE3, 0xE1, 0xE3, 0xED, 0xE3, 0xDF, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0xE3, 0xE3, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0xBD, 0xC1, 0xDF, 0xE9, 0xE7, 0xB2, 0xE7, 0xBB, /* 0x4C-0x4F */
+ 0xE7, 0xB1, 0xE7, 0xAD, 0xE7, 0xAA, 0xBD, 0xC2, /* 0x50-0x53 */
+ 0xE7, 0xA8, 0xBB, 0x6B, 0xE7, 0xA1, 0xBD, 0xC0, /* 0x54-0x57 */
+ 0xE7, 0xA7, 0xBD, 0xBF, 0xE7, 0xAC, 0xE7, 0xA9, /* 0x58-0x5B */
+ 0xE7, 0xB9, 0xE7, 0xB4, 0xE7, 0xAE, 0xE7, 0xB3, /* 0x5C-0x5F */
+ 0xBD, 0xBB, 0xE7, 0xAB, 0xE7, 0xBE, 0xE7, 0xA2, /* 0x60-0x63 */
+ 0xE7, 0xA3, 0xE7, 0xBA, 0xBD, 0xBC, 0xE7, 0xBF, /* 0x64-0x67 */
+ 0xBD, 0xBE, 0xE7, 0xC0, 0xE7, 0xB0, 0xE3, 0xD8, /* 0x68-0x6B */
+ 0xE7, 0xB6, 0xE7, 0xAF, 0xE7, 0xB8, 0xE7, 0xB5, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE7, 0xA6, /* 0x70-0x73 */
+ 0xBD, 0xB9, 0xE7, 0xBD, 0xBD, 0xBA, 0xE7, 0xA4, /* 0x74-0x77 */
+ 0xBD, 0xBD, 0xEB, 0x64, 0xE7, 0xB7, 0xE7, 0xBC, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0xEB, 0x61, 0xBD, 0xB8, 0xBF, 0xC0, /* 0x80-0x83 */
+ 0xEB, 0x6B, 0xEB, 0x67, 0x00, 0x00, 0xEB, 0x65, /* 0x84-0x87 */
+ 0xEB, 0x60, 0xEB, 0x6F, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0xBF, 0xC4, 0x00, 0x00, 0xEB, 0x5C, /* 0x8C-0x8F */
+ 0xEB, 0x68, 0xEB, 0x69, 0xEB, 0x5F, 0xEB, 0x5E, /* 0x90-0x93 */
+ 0xEB, 0x6C, 0x00, 0x00, 0xEB, 0x62, 0xEB, 0x5D, /* 0x94-0x97 */
+ 0xEB, 0x63, 0x00, 0x00, 0xEB, 0x6E, 0xEB, 0x5B, /* 0x98-0x9B */
+ 0xEB, 0x6D, 0xEB, 0x6A, 0xBF, 0xC2, 0xBF, 0xC1, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0xBF, 0xC3, 0xEB, 0x66, /* 0xA0-0xA3 */
+ 0xF0, 0xCB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0xEE, 0x59, 0xC1, 0xB1, /* 0xA8-0xAB */
+ 0xEE, 0x5D, 0xEE, 0x5A, 0xEE, 0x61, 0xEE, 0x67, /* 0xAC-0xAF */
+ 0xEE, 0x5C, 0x00, 0x00, 0xEE, 0x70, 0xC1, 0xAE, /* 0xB0-0xB3 */
+ 0xEE, 0x6A, 0xEE, 0x5F, 0xEE, 0x6B, 0xEE, 0x66, /* 0xB4-0xB7 */
+ 0xEE, 0x6D, 0xEE, 0x5E, 0xC1, 0xB3, 0xC1, 0xB2, /* 0xB8-0xBB */
+ 0xEE, 0x60, 0xEE, 0x6E, 0xEE, 0x58, 0xEE, 0x6C, /* 0xBC-0xBF */
+ 0xC1, 0xAC, 0x00, 0x00, 0xEE, 0x64, 0xEE, 0x63, /* 0xC0-0xC3 */
+ 0xEE, 0x68, 0xEE, 0x5B, 0xC1, 0xB0, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0xC1, 0xB4, 0xEE, 0x62, 0xEE, 0x69, 0xC1, 0xB5, /* 0xC8-0xCB */
+ 0xEE, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0xC1, 0xAD, 0xC1, 0xAF, 0xF0, 0xC7, /* 0xD0-0xD3 */
+ 0xF0, 0xC5, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xCC, /* 0xD4-0xD7 */
+ 0xF0, 0xC9, 0xF0, 0xCD, 0x00, 0x00, 0xF0, 0xBE, /* 0xD8-0xDB */
+ 0xF0, 0xC6, 0xF0, 0xD1, 0xEE, 0x6F, 0xF0, 0xC2, /* 0xDC-0xDF */
+ 0xC2, 0xCF, 0xE7, 0xA5, 0xF0, 0xBD, 0xF0, 0xCA, /* 0xE0-0xE3 */
+ 0xF0, 0xC4, 0xF0, 0xC1, 0xF0, 0xBC, 0xF0, 0xBB, /* 0xE4-0xE7 */
+ 0xF0, 0xD0, 0x00, 0x00, 0xF0, 0xC0, 0xF0, 0xBF, /* 0xE8-0xEB */
+ 0xC2, 0xCD, 0xF0, 0xC8, 0x00, 0x00, 0xC2, 0xCC, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0xC2, 0xCE, 0xF0, 0xC3, /* 0xF0-0xF3 */
+ 0xF0, 0xCF, 0x00, 0x00, 0xF2, 0xDE, 0xF2, 0xDF, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xC3, 0xC9, 0xF2, 0xDC, 0xC3, 0xC6, /* 0xF8-0xFB */
+ 0xF2, 0xE4, 0x00, 0x00, 0xC3, 0xCA, 0xF2, 0xE6, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_88[512] = {
+ 0xF2, 0xDB, 0xF0, 0xCE, 0xF2, 0xE8, 0xF2, 0xDD, /* 0x00-0x03 */
+ 0x00, 0x00, 0xC3, 0xC7, 0xF2, 0xE3, 0x00, 0x00, /* 0x04-0x07 */
+ 0xF2, 0xE5, 0xF2, 0xE0, 0xF2, 0xE7, 0xF2, 0xE2, /* 0x08-0x0B */
+ 0xF2, 0xE1, 0xC3, 0xC8, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xF4, 0xC5, 0xF4, 0xC6, 0x00, 0x00, 0xF4, 0xC8, /* 0x10-0x13 */
+ 0xC4, 0xAE, 0xC4, 0xAF, 0xF4, 0xC9, 0xF4, 0xC7, /* 0x14-0x17 */
+ 0x00, 0x00, 0xF4, 0xC4, 0x00, 0x00, 0xF6, 0x42, /* 0x18-0x1B */
+ 0xF6, 0x45, 0xF6, 0x41, 0x00, 0x00, 0xC4, 0xFA, /* 0x1C-0x1F */
+ 0xF6, 0x43, 0xC4, 0xF9, 0xC4, 0xF8, 0xC4, 0xF7, /* 0x20-0x23 */
+ 0xF6, 0x44, 0xF7, 0x51, 0xF7, 0x4F, 0x00, 0x00, /* 0x24-0x27 */
+ 0xF7, 0x4E, 0xF6, 0x40, 0xF7, 0x50, 0xF6, 0x46, /* 0x28-0x2B */
+ 0xF7, 0x4D, 0x00, 0x00, 0xF7, 0xF9, 0xF7, 0xD7, /* 0x2C-0x2F */
+ 0xF7, 0xF7, 0xC5, 0xDB, 0xF7, 0xF8, 0xF7, 0xFA, /* 0x30-0x33 */
+ 0x00, 0x00, 0xF8, 0xBF, 0xC5, 0xFA, 0xF8, 0xBE, /* 0x34-0x37 */
+ 0xF8, 0xBD, 0xC5, 0xFB, 0x00, 0x00, 0xC6, 0x5A, /* 0x38-0x3B */
+ 0xF9, 0x6E, 0xF9, 0xA7, 0xF9, 0xA6, 0xF9, 0xA8, /* 0x3C-0x3F */
+ 0xA6, 0xE5, 0xD0, 0xAA, 0x00, 0x00, 0xD3, 0xCF, /* 0x40-0x43 */
+ 0xD3, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0xDB, 0xC0, 0x00, 0x00, 0xF6, 0x47, 0xF8, 0xC0, /* 0x48-0x4B */
+ 0xA6, 0xE6, 0xAD, 0x6C, 0xD0, 0xAB, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0xD7, 0xB1, 0xB3, 0x4E, /* 0x50-0x53 */
+ 0x00, 0x00, 0xDB, 0xC2, 0xDB, 0xC1, 0xB5, 0xF3, /* 0x54-0x57 */
+ 0x00, 0x00, 0xB8, 0xC5, 0xE7, 0xC1, 0xBD, 0xC3, /* 0x58-0x5B */
+ 0x00, 0x00, 0xBD, 0xC4, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0xBF, 0xC5, 0xC5, 0xFC, 0xA6, 0xE7, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD0, 0xAC, /* 0x64-0x67 */
+ 0xAA, 0xED, 0xD0, 0xAE, 0xD0, 0xAD, 0xAD, 0x6D, /* 0x68-0x6B */
+ 0x00, 0x00, 0xD3, 0xD1, 0x00, 0x00, 0xD3, 0xD8, /* 0x6C-0x6F */
+ 0xB0, 0x49, 0xD3, 0xD6, 0xD3, 0xD4, 0x00, 0x00, /* 0x70-0x73 */
+ 0xD3, 0xDB, 0xD3, 0xD2, 0xD3, 0xD3, 0xB0, 0x4A, /* 0x74-0x77 */
+ 0x00, 0x00, 0xB0, 0x4E, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0xD3, 0xDC, 0xB0, 0x4D, 0xD3, 0xDA, 0xD3, 0xD7, /* 0x7C-0x7F */
+
+ 0xD3, 0xD5, 0xB0, 0x4B, 0xB0, 0x4C, 0xD3, 0xD9, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0xB3, 0x50, 0xD7, 0xB2, 0x00, 0x00, 0xB3, 0x55, /* 0x88-0x8B */
+ 0xD7, 0xC2, 0xB3, 0x54, 0xD7, 0xC4, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xD7, 0xB8, 0xB3, 0x52, 0xD7, 0xC3, /* 0x90-0x93 */
+ 0x00, 0x00, 0xD7, 0xB3, 0xB3, 0x53, 0xD7, 0xBF, /* 0x94-0x97 */
+ 0xD7, 0xBB, 0xD7, 0xBD, 0xD7, 0xB7, 0xD7, 0xBE, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0xB3, 0x4F, 0xD7, 0xBA, /* 0x9C-0x9F */
+ 0x00, 0x00, 0xD7, 0xB9, 0xD7, 0xB5, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0xD7, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xD7, 0xBC, /* 0xA4-0xA7 */
+ 0xD7, 0xB4, 0x00, 0x00, 0xD7, 0xB6, 0xB3, 0x51, /* 0xA8-0xAB */
+ 0xD7, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0xB5, 0xF6, 0xDB, 0xCD, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0xDB, 0xC9, 0xDB, 0xCB, /* 0xB4-0xB7 */
+ 0xDB, 0xC6, 0xDB, 0xC5, 0xDB, 0xC3, 0x00, 0x00, /* 0xB8-0xBB */
+ 0xDB, 0xCA, 0xDB, 0xCC, 0xDB, 0xC8, 0x00, 0x00, /* 0xBC-0xBF */
+ 0xDB, 0xC7, 0xB5, 0xF4, 0xB5, 0xF5, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xDB, 0xCF, 0xB8, 0xCD, 0xDF, 0xF2, /* 0xC8-0xCB */
+ 0xDF, 0xF8, 0xDF, 0xF3, 0xDF, 0xF4, 0xF9, 0xD8, /* 0xCC-0xCF */
+ 0xDF, 0xF9, 0x00, 0x00, 0xB8, 0xCF, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xB8, 0xC7, 0xB8, 0xCE, 0xDF, 0xF1, 0xDB, 0xC4, /* 0xD4-0xD7 */
+ 0xB8, 0xCA, 0xB8, 0xC8, 0xDF, 0xF7, 0xDF, 0xF6, /* 0xD8-0xDB */
+ 0xB8, 0xC9, 0xB8, 0xCB, 0xDF, 0xF5, 0xB8, 0xC6, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xB8, 0xCC, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE3, 0xF6, /* 0xE4-0xE7 */
+ 0xBB, 0x74, 0x00, 0x00, 0x00, 0x00, 0xE4, 0x42, /* 0xE8-0xEB */
+ 0xE4, 0x41, 0x00, 0x00, 0xE3, 0xFB, 0xBB, 0x76, /* 0xEC-0xEF */
+ 0xE4, 0x40, 0xE3, 0xF7, 0xE3, 0xF8, 0xBB, 0x6E, /* 0xF0-0xF3 */
+ 0xBB, 0x70, 0x00, 0x00, 0xE3, 0xFD, 0xE3, 0xF5, /* 0xF4-0xF7 */
+ 0xBB, 0x72, 0xBB, 0x71, 0xE3, 0xF9, 0xE3, 0xFE, /* 0xF8-0xFB */
+ 0xE3, 0xFC, 0xBB, 0x73, 0xE3, 0xFA, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_89[512] = {
+ 0x00, 0x00, 0xDB, 0xCE, 0xBB, 0x6F, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0xE7, 0xC2, 0xE7, 0xC9, 0xBD, 0xC6, /* 0x04-0x07 */
+ 0x00, 0x00, 0xE7, 0xCD, 0xBD, 0xCA, 0xE7, 0xC5, /* 0x08-0x0B */
+ 0xE7, 0xC3, 0x00, 0x00, 0xE7, 0xCC, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xBD, 0xC5, 0xE7, 0xCB, 0xBD, 0xC7, 0xBD, 0xC8, /* 0x10-0x13 */
+ 0xE7, 0xC4, 0xBD, 0xC9, 0xE7, 0xCA, 0xE7, 0xC6, /* 0x14-0x17 */
+ 0xE7, 0xC7, 0xE7, 0xC8, 0xBB, 0x75, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0xEB, 0x70, 0xEB, 0x7C, /* 0x1C-0x1F */
+ 0x00, 0x00, 0xBF, 0xCA, 0xEB, 0x77, 0xEB, 0x79, /* 0x20-0x23 */
+ 0x00, 0x00, 0xBF, 0xC8, 0xEB, 0x71, 0xEB, 0x75, /* 0x24-0x27 */
+ 0x00, 0x00, 0xEB, 0x78, 0xBF, 0xC6, 0xBF, 0xC9, /* 0x28-0x2B */
+ 0xEB, 0x7B, 0xEB, 0x73, 0xEB, 0x74, 0xEB, 0x7A, /* 0x2C-0x2F */
+ 0xEB, 0x72, 0xEB, 0x76, 0xBF, 0xC7, 0xEE, 0x72, /* 0x30-0x33 */
+ 0x00, 0x00, 0xEE, 0x71, 0xC1, 0xB7, 0xEE, 0x77, /* 0x34-0x37 */
+ 0xC1, 0xB9, 0x00, 0x00, 0x00, 0x00, 0xC1, 0xB6, /* 0x38-0x3B */
+ 0xEE, 0x73, 0xC1, 0xBA, 0xEE, 0x74, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0xEE, 0x75, 0xEE, 0x78, 0x00, 0x00, /* 0x40-0x43 */
+ 0xC1, 0xB8, 0x00, 0x00, 0xF0, 0xD6, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0xF0, 0xD9, 0x00, 0x00, 0xF0, 0xD3, /* 0x48-0x4B */
+ 0xF0, 0xD5, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xD4, /* 0x4C-0x4F */
+ 0xF0, 0xD7, 0xF0, 0xD8, 0xEE, 0x76, 0xF0, 0xD2, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0xC3, 0xCD, 0xF2, 0xEC, /* 0x54-0x57 */
+ 0xF2, 0xEF, 0xF2, 0xF1, 0xF2, 0xEA, 0xF2, 0xEB, /* 0x58-0x5B */
+ 0xF2, 0xEE, 0xF2, 0xF0, 0xC3, 0xCE, 0xC3, 0xCC, /* 0x5C-0x5F */
+ 0xC3, 0xCB, 0xF2, 0xED, 0xF2, 0xE9, 0xF4, 0xCA, /* 0x60-0x63 */
+ 0xC4, 0xB0, 0x00, 0x00, 0xF4, 0xCB, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0xF6, 0x49, 0xC4, 0xFB, 0xF6, 0x4B, /* 0x68-0x6B */
+ 0xC4, 0xFC, 0xF6, 0x48, 0xF6, 0x4A, 0xC5, 0xA8, /* 0x6C-0x6F */
+ 0x00, 0x00, 0xF7, 0x52, 0xC5, 0xA7, 0xF7, 0xFD, /* 0x70-0x73 */
+ 0xF7, 0xFC, 0x00, 0x00, 0xF7, 0xFB, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0xF9, 0x48, 0xF9, 0x49, 0xF9, 0x4B, /* 0x78-0x7B */
+ 0xF9, 0x4A, 0x00, 0x00, 0xCA, 0x50, 0xA6, 0xE8, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0xAD, 0x6E, 0xD7, 0xC5, 0xB5, 0xF7, /* 0x80-0x83 */
+ 0x00, 0x00, 0xDF, 0xFA, 0xC2, 0xD0, 0x00, 0x00, /* 0x84-0x87 */
+ 0xF2, 0xF2, 0x00, 0x00, 0x00, 0x00, 0xA8, 0xA3, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB3, 0x57, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB3, 0x56, /* 0x90-0x93 */
+ 0x00, 0x00, 0xDB, 0xD0, 0xB5, 0xF8, 0xDB, 0xD2, /* 0x94-0x97 */
+ 0xDB, 0xD1, 0x00, 0x00, 0x00, 0x00, 0xDF, 0xFB, /* 0x98-0x9B */
+ 0xB8, 0xD0, 0xE4, 0x43, 0xE4, 0x46, 0xE4, 0x45, /* 0x9C-0x9F */
+ 0x00, 0x00, 0xE4, 0x44, 0xE7, 0xCE, 0xE7, 0xD0, /* 0xA0-0xA3 */
+ 0xE7, 0xCF, 0x00, 0x00, 0xBF, 0xCC, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0xBF, 0xCB, 0x00, 0x00, /* 0xA8-0xAB */
+ 0xC1, 0xBB, 0xEE, 0x79, 0xEE, 0x7B, 0xEE, 0x7A, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0xC2, 0xD1, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0xF2, 0xF4, 0xF2, 0xF3, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xF4, 0xCC, 0xC4, 0xB1, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0xC4, 0xFD, 0xF7, 0x54, 0xF7, 0x53, /* 0xBC-0xBF */
+ 0xC6, 0x5B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0xA8, 0xA4, 0xD0, 0xAF, /* 0xD0-0xD3 */
+ 0xAD, 0x6F, 0xD7, 0xC8, 0xD7, 0xC6, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0xD7, 0xC7, 0xDB, 0xD4, 0xDB, 0xD5, /* 0xD8-0xDB */
+ 0xE0, 0x43, 0xDB, 0xD3, 0x00, 0x00, 0xDF, 0xFC, /* 0xDC-0xDF */
+ 0xE0, 0x41, 0xE0, 0x40, 0xE0, 0x42, 0xB8, 0xD1, /* 0xE0-0xE3 */
+ 0xDF, 0xFE, 0xDF, 0xFD, 0xE0, 0x44, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xE4, 0x49, 0xE4, 0x47, 0x00, 0x00, 0xE4, 0x48, /* 0xE8-0xEB */
+ 0xE7, 0xD3, 0xE7, 0xD1, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0xE7, 0xD2, 0xEB, 0x7D, 0xEE, 0x7C, 0xEE, 0x7D, /* 0xF0-0xF3 */
+ 0xC2, 0xD2, 0x00, 0x00, 0xF2, 0xF5, 0xF4, 0xCD, /* 0xF4-0xF7 */
+ 0xC4, 0xB2, 0x00, 0x00, 0xF6, 0x4C, 0xF7, 0x55, /* 0xF8-0xFB */
+ 0xC5, 0xA9, 0x00, 0x00, 0xF7, 0xFE, 0xF9, 0x4C, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_8A[512] = {
+ 0xA8, 0xA5, 0x00, 0x00, 0xAD, 0x71, 0xAD, 0x72, /* 0x00-0x03 */
+ 0xD0, 0xB0, 0x00, 0x00, 0x00, 0x00, 0xD0, 0xB1, /* 0x04-0x07 */
+ 0xAD, 0x70, 0x00, 0x00, 0xB0, 0x54, 0x00, 0x00, /* 0x08-0x0B */
+ 0xB0, 0x52, 0x00, 0x00, 0xB0, 0x51, 0xB0, 0x58, /* 0x0C-0x0F */
+ 0xB0, 0x50, 0xB0, 0x59, 0xD3, 0xDD, 0xB0, 0x56, /* 0x10-0x13 */
+ 0x00, 0x00, 0xB0, 0x53, 0xB0, 0x57, 0xB0, 0x55, /* 0x14-0x17 */
+ 0xB0, 0x4F, 0x00, 0x00, 0x00, 0x00, 0xB3, 0x5F, /* 0x18-0x1B */
+ 0x00, 0x00, 0xB3, 0x59, 0xD7, 0xCC, 0xB3, 0x5E, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0xB3, 0x60, 0xB3, 0x5A, /* 0x20-0x23 */
+ 0x00, 0x00, 0xB3, 0x5B, 0x00, 0x00, 0xD7, 0xCA, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xB3, 0x58, 0x00, 0x00, /* 0x28-0x2B */
+ 0xD7, 0xCB, 0xB3, 0x5D, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xD7, 0xC9, 0xB3, 0x5C, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0xB6, 0x44, 0x00, 0x00, 0xB6, 0x46, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0xDB, 0xD8, 0xB6, 0x45, 0xB5, 0xF9, /* 0x38-0x3B */
+ 0xB5, 0xFD, 0x00, 0x00, 0xB8, 0xE4, 0xE0, 0x49, /* 0x3C-0x3F */
+ 0xDB, 0xDA, 0xB5, 0xFE, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0xDB, 0xDD, 0xDB, 0xDE, 0xB6, 0x43, 0x00, 0x00, /* 0x44-0x47 */
+ 0xDB, 0xE0, 0x00, 0x00, 0xDB, 0xE2, 0x00, 0x00, /* 0x48-0x4B */
+ 0xDB, 0xE3, 0xDB, 0xD7, 0xDB, 0xD6, 0xDB, 0xE4, /* 0x4C-0x4F */
+ 0xB6, 0x42, 0xDB, 0xE1, 0xDB, 0xDF, 0x00, 0x00, /* 0x50-0x53 */
+ 0xB6, 0x40, 0xB5, 0xFB, 0xB6, 0x47, 0xDB, 0xDB, /* 0x54-0x57 */
+ 0xDB, 0xDC, 0xDB, 0xD9, 0x00, 0x00, 0xB6, 0x41, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0xB5, 0xFC, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xB5, 0xFA, 0xE0, 0x48, 0xB8, 0xDF, 0xB8, 0xDA, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0xB8, 0xD5, 0x00, 0x00, /* 0x64-0x67 */
+ 0xB8, 0xE5, 0xB8, 0xD6, 0x00, 0x00, 0xB8, 0xD2, /* 0x68-0x6B */
+ 0xB8, 0xE1, 0xB8, 0xDE, 0xB8, 0xE0, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xB8, 0xD7, 0xB8, 0xDC, 0xB8, 0xD3, 0xB8, 0xD4, /* 0x70-0x73 */
+ 0xE0, 0x50, 0xE0, 0x4D, 0xE0, 0x45, 0xE0, 0x4A, /* 0x74-0x77 */
+ 0x00, 0x00, 0xB8, 0xE2, 0xE0, 0x51, 0xB8, 0xE3, /* 0x78-0x7B */
+ 0xB8, 0xD9, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x47, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0xE0, 0x4F, 0xE0, 0x4B, 0xE0, 0x4E, /* 0x80-0x83 */
+ 0xE0, 0x4C, 0xB8, 0xDD, 0xE0, 0x46, 0xB8, 0xD8, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0x4C, /* 0x88-0x8B */
+ 0xBB, 0x78, 0xBB, 0x7B, 0x00, 0x00, 0xE4, 0x4E, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xBB, 0xA5, 0xE4, 0x4D, 0xBB, 0x7D, /* 0x90-0x93 */
+ 0x00, 0x00, 0xBD, 0xCF, 0xE4, 0x4F, 0x00, 0x00, /* 0x94-0x97 */
+ 0xBB, 0xA4, 0xE4, 0x4B, 0xBB, 0xA6, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0xBB, 0x79, 0x00, 0x00, /* 0x9C-0x9F */
+ 0xB8, 0xDB, 0xBB, 0x7C, 0x00, 0x00, 0xBB, 0x7A, /* 0xA0-0xA3 */
+ 0xBB, 0x7E, 0xBB, 0xA2, 0xBB, 0x77, 0xBB, 0xA7, /* 0xA4-0xA7 */
+ 0xBB, 0xA3, 0x00, 0x00, 0xBB, 0xA1, 0xE4, 0x4A, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0xBD, 0xD6, 0x00, 0x00, 0xBD, 0xD2, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0xBD, 0xD9, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0xE7, 0xD6, 0xBD, 0xDA, 0xE7, 0xE2, 0xE7, 0xDB, /* 0xB8-0xBB */
+ 0xBD, 0xCB, 0xE7, 0xE3, 0xE7, 0xDD, 0xBD, 0xD5, /* 0xBC-0xBF */
+ 0xE7, 0xDE, 0x00, 0x00, 0xBD, 0xD4, 0xE7, 0xE1, /* 0xC0-0xC3 */
+ 0xBD, 0xCE, 0xE7, 0xDF, 0xE7, 0xD5, 0xBD, 0xCD, /* 0xC4-0xC7 */
+ 0xEB, 0xAA, 0xBD, 0xD3, 0x00, 0x00, 0xBD, 0xD0, /* 0xC8-0xCB */
+ 0x00, 0x00, 0xBD, 0xD8, 0x00, 0x00, 0xE7, 0xD4, /* 0xCC-0xCF */
+ 0x00, 0x00, 0xE7, 0xD8, 0xBD, 0xCC, 0xE7, 0xD7, /* 0xD0-0xD3 */
+ 0xE7, 0xD9, 0xE7, 0xDA, 0xBD, 0xD7, 0xE7, 0xDC, /* 0xD4-0xD7 */
+ 0xE7, 0xE0, 0xE7, 0xE4, 0x00, 0x00, 0xBD, 0xDB, /* 0xD8-0xDB */
+ 0xBF, 0xD2, 0xEB, 0xA5, 0xEB, 0xAB, 0xEB, 0xA8, /* 0xDC-0xDF */
+ 0xEB, 0x7E, 0xEB, 0xAC, 0xEB, 0xA1, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0xEB, 0xA7, 0x00, 0x00, 0xBF, 0xCD, 0xBF, 0xD3, /* 0xE4-0xE7 */
+ 0xEB, 0xAD, 0x00, 0x00, 0x00, 0x00, 0xBF, 0xCF, /* 0xE8-0xEB */
+ 0x00, 0x00, 0xBF, 0xD9, 0xBF, 0xD4, 0xEB, 0xAF, /* 0xEC-0xEF */
+ 0xEB, 0xA9, 0xBF, 0xD0, 0xEB, 0xA2, 0xBF, 0xDA, /* 0xF0-0xF3 */
+ 0xEB, 0xA3, 0xEB, 0xA4, 0xBF, 0xDB, 0xBF, 0xD8, /* 0xF4-0xF7 */
+ 0xBD, 0xD1, 0x00, 0x00, 0xBF, 0xCE, 0xEB, 0xB0, /* 0xF8-0xFB */
+ 0xBF, 0xDC, 0x00, 0x00, 0xBF, 0xD5, 0xEB, 0xAE, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_8B[512] = {
+ 0xBF, 0xD1, 0xBF, 0xD6, 0xBF, 0xD7, 0x00, 0x00, /* 0x00-0x03 */
+ 0xC1, 0xC3, 0xEE, 0xA4, 0xEE, 0xAD, 0xEE, 0xAA, /* 0x04-0x07 */
+ 0xEE, 0xAC, 0x00, 0x00, 0xC1, 0xC0, 0xEE, 0xA5, /* 0x08-0x0B */
+ 0x00, 0x00, 0xEE, 0xAB, 0xC1, 0xBC, 0xEE, 0xA7, /* 0x0C-0x0F */
+ 0xC1, 0xC4, 0xEE, 0xA3, 0xEE, 0xA8, 0xEE, 0xAF, /* 0x10-0x13 */
+ 0xEB, 0xA6, 0xEE, 0xA9, 0xEE, 0xA2, 0xC1, 0xBD, /* 0x14-0x17 */
+ 0xEE, 0xA1, 0xC1, 0xBE, 0xEE, 0xB0, 0xC1, 0xBF, /* 0x18-0x1B */
+ 0xEE, 0xAE, 0xC1, 0xC2, 0xEE, 0x7E, 0x00, 0x00, /* 0x1C-0x1F */
+ 0xC1, 0xC1, 0x00, 0x00, 0xEE, 0xA6, 0xF0, 0xDC, /* 0x20-0x23 */
+ 0xF0, 0xEA, 0xF0, 0xE5, 0xF0, 0xE7, 0xF0, 0xDB, /* 0x24-0x27 */
+ 0xC2, 0xD3, 0x00, 0x00, 0xF0, 0xDA, 0xC2, 0xD6, /* 0x28-0x2B */
+ 0xC2, 0xD5, 0x00, 0x00, 0xF0, 0xE9, 0xF0, 0xE1, /* 0x2C-0x2F */
+ 0xF0, 0xDE, 0xF0, 0xE4, 0x00, 0x00, 0xF0, 0xDD, /* 0x30-0x33 */
+ 0x00, 0x00, 0xF0, 0xDF, 0xF0, 0xE8, 0xF0, 0xE6, /* 0x34-0x37 */
+ 0x00, 0x00, 0xC2, 0xD4, 0xF0, 0xED, 0xF0, 0xEB, /* 0x38-0x3B */
+ 0xF0, 0xE2, 0xF0, 0xEC, 0xF0, 0xE3, 0x00, 0x00, /* 0x3C-0x3F */
+ 0xF2, 0xF9, 0xC3, 0xCF, 0xF3, 0x41, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0xF6, 0x4F, 0xC3, 0xD6, 0xF0, 0xE0, /* 0x44-0x47 */
+ 0xF2, 0xF7, 0xC3, 0xD2, 0xF2, 0xF8, 0xF2, 0xFD, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0xC3, 0xD4, 0xC3, 0xD5, /* 0x4C-0x4F */
+ 0xF2, 0xF6, 0xF3, 0x40, 0xF3, 0x42, 0xF2, 0xFA, /* 0x50-0x53 */
+ 0xF2, 0xFC, 0xF2, 0xFE, 0xF2, 0xFB, 0xF3, 0x43, /* 0x54-0x57 */
+ 0xC3, 0xD1, 0xC3, 0xD7, 0xC3, 0xD3, 0x00, 0x00, /* 0x58-0x5B */
+ 0xC3, 0xD0, 0xF4, 0xD0, 0x00, 0x00, 0xC4, 0xB7, /* 0x5C-0x5F */
+ 0xF4, 0xCE, 0x00, 0x00, 0x00, 0x00, 0xF4, 0xD2, /* 0x60-0x63 */
+ 0x00, 0x00, 0xF4, 0xD3, 0xC4, 0xB5, 0xF4, 0xD4, /* 0x64-0x67 */
+ 0xF4, 0xD1, 0x00, 0x00, 0xF4, 0xCF, 0xC4, 0xB8, /* 0x68-0x6B */
+ 0xC4, 0xB4, 0xF4, 0xD5, 0x00, 0x00, 0xC4, 0xB6, /* 0x6C-0x6F */
+ 0xC4, 0xB3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0xC4, 0xFE, 0x00, 0x00, 0x00, 0x00, 0xC5, 0x40, /* 0x74-0x77 */
+ 0xF6, 0x4E, 0xF6, 0x4D, 0xF6, 0x50, 0xF6, 0x51, /* 0x78-0x7B */
+ 0x00, 0x00, 0xC5, 0x41, 0xF7, 0x56, 0xF7, 0x5B, /* 0x7C-0x7F */
+
+ 0xC5, 0xAA, 0x00, 0x00, 0xF7, 0x58, 0x00, 0x00, /* 0x80-0x83 */
+ 0xF7, 0x57, 0xF7, 0x5A, 0xF7, 0x59, 0x00, 0x00, /* 0x84-0x87 */
+ 0xF8, 0x43, 0x00, 0x00, 0xC5, 0xDC, 0xF8, 0x42, /* 0x88-0x8B */
+ 0xF8, 0x40, 0x00, 0x00, 0xF8, 0x41, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0xC5, 0xFE, 0xC5, 0xFD, /* 0x90-0x93 */
+ 0xF8, 0xC1, 0xF8, 0xC2, 0xC6, 0x40, 0x00, 0x00, /* 0x94-0x97 */
+ 0xF9, 0x4D, 0xF9, 0x4E, 0xC6, 0x67, 0x00, 0x00, /* 0x98-0x9B */
+ 0xC6, 0x6D, 0x00, 0x00, 0xF9, 0xA9, 0xF9, 0xC8, /* 0x9C-0x9F */
+};
+
+static unsigned char u2c_8C[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA8, 0xA6, /* 0x34-0x37 */
+ 0x00, 0x00, 0xD7, 0xCD, 0x00, 0x00, 0xD7, 0xCE, /* 0x38-0x3B */
+ 0xE0, 0x52, 0xE4, 0x50, 0xE7, 0xE5, 0xC1, 0xC6, /* 0x3C-0x3F */
+ 0x00, 0x00, 0xC1, 0xC5, 0xF0, 0xEE, 0xF3, 0x44, /* 0x40-0x43 */
+ 0x00, 0x00, 0xF8, 0x44, 0xA8, 0xA7, 0xD3, 0xDE, /* 0x44-0x47 */
+ 0xB0, 0x5A, 0xB3, 0x61, 0xE0, 0x54, 0xE0, 0x53, /* 0x48-0x4B */
+ 0xBD, 0xDC, 0xE7, 0xE6, 0xBD, 0xDD, 0xEE, 0xB1, /* 0x4C-0x4F */
+ 0xC2, 0xD7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0xC6, 0x76, 0xA8, 0xA8, 0xCD, 0xCB, 0xD3, 0xDF, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0xB3, 0x62, 0x00, 0x00, /* 0x58-0x5B */
+ 0xD7, 0xCF, 0xD7, 0xD0, 0x00, 0x00, 0xDB, 0xE5, /* 0x5C-0x5F */
+ 0x00, 0x00, 0xB6, 0x48, 0xB8, 0xE6, 0x00, 0x00, /* 0x60-0x63 */
+ 0xE0, 0x56, 0xE0, 0x55, 0xE0, 0x57, 0x00, 0x00, /* 0x64-0x67 */
+ 0xE4, 0x51, 0xE4, 0x52, 0xBB, 0xA8, 0xBF, 0xDD, /* 0x68-0x6B */
+ 0xBD, 0xDE, 0xBF, 0xDE, 0x00, 0x00, 0xEE, 0xB5, /* 0x6C-0x6F */
+ 0xEE, 0xB2, 0xEE, 0xB4, 0xEE, 0xB3, 0xC1, 0xC7, /* 0x70-0x73 */
+ 0x00, 0x00, 0xF0, 0xEF, 0xF3, 0x46, 0xF3, 0x45, /* 0x74-0x77 */
+ 0xCB, 0xA4, 0xB0, 0x5C, 0xB0, 0x5B, 0xD3, 0xE0, /* 0x78-0x7B */
+ 0x00, 0x00, 0xD7, 0xD1, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0xDB, 0xE7, 0xDB, 0xE6, 0xB6, 0x49, 0x00, 0x00, /* 0x80-0x83 */
+ 0xE0, 0x59, 0xE0, 0x5A, 0xE0, 0x58, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0xB8, 0xE8, 0xB8, 0xE7, 0x00, 0x00, /* 0x88-0x8B */
+ 0xBB, 0xAA, 0xBB, 0xA9, 0x00, 0x00, 0xE7, 0xE7, /* 0x8C-0x8F */
+ 0xEB, 0xB3, 0xEB, 0xB1, 0xEB, 0xB2, 0xBF, 0xDF, /* 0x90-0x93 */
+ 0xEE, 0xB7, 0xEE, 0xB6, 0x00, 0x00, 0xF0, 0xF2, /* 0x94-0x97 */
+ 0xF0, 0xF1, 0xF0, 0xF0, 0xF3, 0x47, 0x00, 0x00, /* 0x98-0x9B */
+ 0xF9, 0xAA, 0xA8, 0xA9, 0xAD, 0x73, 0x00, 0x00, /* 0x9C-0x9F */
+ 0xAD, 0x74, 0xB0, 0x5D, 0xB0, 0x5E, 0xD3, 0xE2, /* 0xA0-0xA3 */
+ 0xD3, 0xE1, 0xD7, 0xD2, 0x00, 0x00, 0xB3, 0x68, /* 0xA4-0xA7 */
+ 0xB3, 0x66, 0xB3, 0x63, 0xB3, 0x67, 0xB3, 0x65, /* 0xA8-0xAB */
+ 0xB3, 0x64, 0x00, 0x00, 0x00, 0x00, 0xB6, 0x4A, /* 0xAC-0xAF */
+ 0xDB, 0xEA, 0x00, 0x00, 0xB8, 0xED, 0xB6, 0x4C, /* 0xB0-0xB3 */
+ 0xB6, 0x51, 0xDB, 0xEC, 0xB6, 0x53, 0xB6, 0x52, /* 0xB4-0xB7 */
+ 0xB6, 0x55, 0xDB, 0xEB, 0xDB, 0xE8, 0xB6, 0x4F, /* 0xB8-0xBB */
+ 0xB6, 0x4B, 0xB6, 0x4D, 0xDB, 0xE9, 0xB6, 0x54, /* 0xBC-0xBF */
+ 0xB6, 0x50, 0xB6, 0x4E, 0xB8, 0xEF, 0xB8, 0xEE, /* 0xC0-0xC3 */
+ 0xB8, 0xEC, 0xB8, 0xF0, 0x00, 0x00, 0xB8, 0xEA, /* 0xC4-0xC7 */
+ 0xB8, 0xEB, 0x00, 0x00, 0xB8, 0xE9, 0x00, 0x00, /* 0xC8-0xCB */
+ 0xE0, 0x5B, 0x00, 0x00, 0x00, 0x00, 0xE4, 0x54, /* 0xCC-0xCF */
+ 0x00, 0x00, 0xBB, 0xAC, 0xBB, 0xAD, 0xBB, 0xAB, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0xE4, 0x53, 0x00, 0x00, 0xE4, 0x55, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0xE7, 0xEA, 0xE7, 0xEC, 0x00, 0x00, /* 0xD8-0xDB */
+ 0xBD, 0xE7, 0xE7, 0xED, 0xBD, 0xE0, 0xE7, 0xE9, /* 0xDC-0xDF */
+ 0xBD, 0xDF, 0xBD, 0xE9, 0xBD, 0xE5, 0xBD, 0xE6, /* 0xE0-0xE3 */
+ 0xBD, 0xE2, 0xE7, 0xE8, 0xBD, 0xE1, 0xE7, 0xEE, /* 0xE4-0xE7 */
+ 0xE7, 0xEB, 0x00, 0x00, 0xBD, 0xE8, 0x00, 0x00, /* 0xE8-0xEB */
+ 0xBD, 0xE3, 0xBD, 0xE4, 0xEB, 0xB5, 0x00, 0x00, /* 0xEC-0xEF */
+ 0xEB, 0xB7, 0xEB, 0xB6, 0x00, 0x00, 0xEB, 0xB8, /* 0xF0-0xF3 */
+ 0xBF, 0xE0, 0xEB, 0xB4, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0xC1, 0xCB, 0xEE, 0xB8, 0xC1, 0xC8, 0xC1, 0xCC, /* 0xF8-0xFB */
+ 0xC1, 0xCA, 0xC1, 0xC9, 0xF0, 0xF3, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_8D[512] = {
+ 0xF0, 0xF6, 0x00, 0x00, 0xF0, 0xF5, 0x00, 0x00, /* 0x00-0x03 */
+ 0xF0, 0xF4, 0xC2, 0xD8, 0xF3, 0x48, 0xF3, 0x49, /* 0x04-0x07 */
+ 0xC3, 0xD8, 0xF3, 0x4A, 0xC3, 0xD9, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0xC4, 0xBA, 0x00, 0x00, 0xC4, 0xB9, /* 0x0C-0x0F */
+ 0xF6, 0x52, 0x00, 0x00, 0x00, 0x00, 0xC5, 0x42, /* 0x10-0x13 */
+ 0xF6, 0x53, 0xF7, 0x5C, 0xC5, 0xAB, 0xC5, 0xAC, /* 0x14-0x17 */
+ 0x00, 0x00, 0xF8, 0x45, 0x00, 0x00, 0xC6, 0x42, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0xA8, 0xAA, 0x00, 0x00, 0xB3, 0x6A, 0xB3, 0x69, /* 0x64-0x67 */
+ 0xE0, 0x5C, 0xE0, 0x5D, 0x00, 0x00, 0xBB, 0xAE, /* 0x68-0x6B */
+ 0xEB, 0xB9, 0xBD, 0xEA, 0xEB, 0xBA, 0xEE, 0xB9, /* 0x6C-0x6F */
+ 0xA8, 0xAB, 0x00, 0x00, 0xD0, 0xB2, 0xAD, 0x76, /* 0x70-0x73 */
+ 0xAD, 0x75, 0x00, 0x00, 0xD3, 0xE3, 0xB0, 0x5F, /* 0x74-0x77 */
+ 0xD3, 0xE4, 0xD7, 0xD5, 0x00, 0x00, 0xD7, 0xD4, /* 0x78-0x7B */
+ 0x00, 0x00, 0xD7, 0xD3, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0xDB, 0xEE, 0xB6, 0x58, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0xDB, 0xED, 0xB6, 0x57, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0xDB, 0xEF, 0xB6, 0x56, 0x00, 0x00, /* 0x88-0x8B */
+ 0xE0, 0x5F, 0xE0, 0x62, 0xE0, 0x60, 0xE0, 0x61, /* 0x8C-0x8F */
+ 0xE0, 0x65, 0xE0, 0x5E, 0xE0, 0x66, 0xE0, 0x63, /* 0x90-0x93 */
+ 0xE0, 0x64, 0xBB, 0xB0, 0xE4, 0x56, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0xBB, 0xAF, 0x00, 0x00, 0xE7, 0xF2, /* 0x98-0x9B */
+ 0xE7, 0xF0, 0x00, 0x00, 0x00, 0x00, 0xBD, 0xEB, /* 0x9C-0x9F */
+ 0xE7, 0xEF, 0xE7, 0xF1, 0x00, 0x00, 0xBD, 0xEC, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0xEB, 0xBB, 0x00, 0x00, 0xEB, 0xBC, /* 0xA4-0xA7 */
+ 0xC1, 0xCD, 0x00, 0x00, 0xF3, 0x4C, 0xF3, 0x4E, /* 0xA8-0xAB */
+ 0xF3, 0x4B, 0xF3, 0x4D, 0xF4, 0xD6, 0xF6, 0x54, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0xF9, 0x6F, 0xA8, 0xAC, /* 0xB0-0xB3 */
+ 0xAD, 0x77, 0xD3, 0xE5, 0xD3, 0xE7, 0xD3, 0xE6, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xD7, 0xD8, 0xB3, 0x6C, 0x00, 0x00, /* 0xB8-0xBB */
+ 0xD7, 0xD6, 0x00, 0x00, 0xB3, 0x6B, 0xD7, 0xD9, /* 0xBC-0xBF */
+ 0x00, 0x00, 0xD7, 0xDA, 0xD7, 0xD7, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0xDB, 0xFB, 0xB6, 0x60, 0xDB, 0xF3, /* 0xC4-0xC7 */
+ 0xDB, 0xF9, 0x00, 0x00, 0x00, 0x00, 0xB6, 0x5B, /* 0xC8-0xCB */
+ 0xB6, 0x5E, 0xDB, 0xF2, 0xB6, 0x59, 0xDB, 0xF6, /* 0xCC-0xCF */
+ 0xE0, 0x6C, 0xB6, 0x5D, 0x00, 0x00, 0xDB, 0xF1, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0xDB, 0xF7, 0xDB, 0xF4, 0xDB, 0xFA, /* 0xD4-0xD7 */
+ 0xDB, 0xF0, 0xDB, 0xF8, 0xB6, 0x5C, 0xB6, 0x5F, /* 0xD8-0xDB */
+ 0xDB, 0xF5, 0xB6, 0x5A, 0x00, 0x00, 0xB8, 0xF2, /* 0xDC-0xDF */
+ 0xE0, 0x68, 0xB8, 0xF1, 0xE0, 0x6F, 0xE0, 0x6E, /* 0xE0-0xE3 */
+ 0xB8, 0xF8, 0x00, 0x00, 0xB8, 0xF9, 0xE0, 0x70, /* 0xE4-0xE7 */
+ 0xB8, 0xF3, 0xE0, 0x6D, 0xB8, 0xF7, 0xE0, 0x72, /* 0xE8-0xEB */
+ 0xE0, 0x69, 0x00, 0x00, 0xE0, 0x6B, 0xB8, 0xF4, /* 0xEC-0xEF */
+ 0xE0, 0x67, 0xE0, 0x6A, 0xE0, 0x71, 0xB8, 0xF5, /* 0xF0-0xF3 */
+ 0xE0, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0x00, 0x00, 0xB8, 0xF6, 0x00, 0x00, /* 0xF8-0xFB */
+ 0xBB, 0xB1, 0xE4, 0x5B, 0xE4, 0x61, 0xE4, 0x59, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_8E[512] = {
+ 0xE4, 0x62, 0x00, 0x00, 0xE4, 0x58, 0xE4, 0x5D, /* 0x00-0x03 */
+ 0xE4, 0x63, 0xE4, 0x60, 0xE4, 0x5F, 0xE4, 0x5E, /* 0x04-0x07 */
+ 0x00, 0x00, 0xE4, 0x57, 0xE4, 0x5C, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0xE4, 0x5A, 0x00, 0x00, 0xBD, 0xF1, /* 0x0C-0x0F */
+ 0xBD, 0xEE, 0xE7, 0xFB, 0xE8, 0x41, 0xE8, 0x43, /* 0x10-0x13 */
+ 0xE8, 0x40, 0xE7, 0xF8, 0xE7, 0xFA, 0xE8, 0x45, /* 0x14-0x17 */
+ 0xE8, 0x42, 0xE7, 0xFC, 0xE8, 0x46, 0xE7, 0xF9, /* 0x18-0x1B */
+ 0xE8, 0x44, 0xBD, 0xEF, 0xBD, 0xF5, 0xBD, 0xF3, /* 0x1C-0x1F */
+ 0xE7, 0xF3, 0xBD, 0xF4, 0xBD, 0xF0, 0xE7, 0xF4, /* 0x20-0x23 */
+ 0xE7, 0xF6, 0xE7, 0xF5, 0xE7, 0xFD, 0xE7, 0xFE, /* 0x24-0x27 */
+ 0x00, 0x00, 0xBD, 0xF2, 0x00, 0x00, 0xBD, 0xED, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0xE7, 0xF7, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xEB, 0xC6, 0xBF, 0xE2, 0x00, 0x00, 0xEB, 0xBD, /* 0x30-0x33 */
+ 0xBF, 0xE3, 0xBF, 0xE6, 0xEB, 0xC2, 0x00, 0x00, /* 0x34-0x37 */
+ 0xEB, 0xBF, 0xBF, 0xE5, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0xEB, 0xC3, 0xEB, 0xC4, 0xEB, 0xBE, 0xEB, 0xC7, /* 0x3C-0x3F */
+ 0xEB, 0xC0, 0xEB, 0xC5, 0xBF, 0xE4, 0x00, 0x00, /* 0x40-0x43 */
+ 0xBF, 0xE1, 0xEB, 0xC1, 0x00, 0x00, 0xEE, 0xBF, /* 0x44-0x47 */
+ 0xC1, 0xD0, 0xC1, 0xCE, 0xC1, 0xD1, 0xC1, 0xCF, /* 0x48-0x4B */
+ 0xEE, 0xBE, 0xEE, 0xBB, 0xEE, 0xBA, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xEE, 0xBD, 0x00, 0x00, 0x00, 0x00, 0xEE, 0xBC, /* 0x50-0x53 */
+ 0xF1, 0x45, 0xC2, 0xDE, 0xF0, 0xFB, 0xF0, 0xFA, /* 0x54-0x57 */
+ 0x00, 0x00, 0xC2, 0xD9, 0xF1, 0x41, 0xF1, 0x40, /* 0x58-0x5B */
+ 0xF0, 0xF7, 0xF1, 0x43, 0xF0, 0xFC, 0xC2, 0xDD, /* 0x5C-0x5F */
+ 0xF0, 0xF9, 0xF1, 0x42, 0xF0, 0xF8, 0xC2, 0xDA, /* 0x60-0x63 */
+ 0xC2, 0xDC, 0xF0, 0xFD, 0xC2, 0xDB, 0xF0, 0xFE, /* 0x64-0x67 */
+ 0x00, 0x00, 0xF1, 0x44, 0xF3, 0x52, 0x00, 0x00, /* 0x68-0x6B */
+ 0xC3, 0xDE, 0xF3, 0x4F, 0x00, 0x00, 0xF3, 0x53, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0xC3, 0xDB, 0xF3, 0x51, /* 0x70-0x73 */
+ 0xC3, 0xE0, 0x00, 0x00, 0xC3, 0xDD, 0x00, 0x00, /* 0x74-0x77 */
+ 0xF3, 0x50, 0x00, 0x00, 0xC3, 0xDF, 0xF3, 0x54, /* 0x78-0x7B */
+ 0xC3, 0xDA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0xC4, 0xBC, 0xC4, 0xBE, 0x00, 0x00, /* 0x80-0x83 */
+ 0xF4, 0xD9, 0xC4, 0xBD, 0xF4, 0xD7, 0xC3, 0xDC, /* 0x84-0x87 */
+ 0xF4, 0xD8, 0xC4, 0xBB, 0xC5, 0x43, 0xC5, 0x45, /* 0x88-0x8B */
+ 0xF6, 0x56, 0xC5, 0x44, 0xF6, 0x55, 0x00, 0x00, /* 0x8C-0x8F */
+ 0xF7, 0x61, 0xC5, 0xAD, 0xF7, 0x60, 0xC5, 0xAE, /* 0x90-0x93 */
+ 0xF7, 0x5E, 0xF7, 0x5D, 0xF7, 0x62, 0xF7, 0x63, /* 0x94-0x97 */
+ 0xF8, 0x46, 0x00, 0x00, 0xF7, 0x5F, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0xF8, 0xC6, 0xF8, 0xC3, 0xF8, 0xC4, /* 0x9C-0x9F */
+ 0xF8, 0xC5, 0xC6, 0x5C, 0x00, 0x00, 0xF9, 0x51, /* 0xA0-0xA3 */
+ 0xF9, 0x50, 0xF9, 0x4F, 0xF9, 0x70, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xF9, 0xBE, 0xF9, 0xAB, 0xC6, 0x6E, 0xA8, 0xAD, /* 0xA8-0xAB */
+ 0xB0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0xB8, 0xFA, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0xBD, 0xF6, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0xEB, 0xC8, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0xC2, 0xDF, 0x00, 0x00, 0xF3, 0x55, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xF9, 0xAC, 0xA8, 0xAE, 0xAA, 0xEE, /* 0xC8-0xCB */
+ 0xAD, 0x79, 0xAD, 0x78, 0x00, 0x00, 0xB0, 0x63, /* 0xCC-0xCF */
+ 0x00, 0x00, 0xD3, 0xE8, 0xB0, 0x61, 0xD3, 0xE9, /* 0xD0-0xD3 */
+ 0xB0, 0x62, 0x00, 0x00, 0x00, 0x00, 0xD7, 0xDF, /* 0xD4-0xD7 */
+ 0xD7, 0xDB, 0x00, 0x00, 0x00, 0x00, 0xB3, 0x6D, /* 0xD8-0xDB */
+ 0xD7, 0xDE, 0xD7, 0xDD, 0xD7, 0xDC, 0xB3, 0x6E, /* 0xDC-0xDF */
+ 0xD7, 0xE0, 0xD7, 0xE1, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0xDC, 0x43, 0xDC, 0x41, 0xDC, 0x45, /* 0xE4-0xE7 */
+ 0xDC, 0x46, 0xDC, 0x4C, 0x00, 0x00, 0xDC, 0x48, /* 0xE8-0xEB */
+ 0xDC, 0x4A, 0x00, 0x00, 0xDC, 0x42, 0xDB, 0xFC, /* 0xEC-0xEF */
+ 0x00, 0x00, 0xDC, 0x49, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xDC, 0x4B, 0xDC, 0x44, 0xDC, 0x47, 0xDB, 0xFD, /* 0xF4-0xF7 */
+ 0xB6, 0x62, 0xDC, 0x40, 0xDB, 0xFE, 0xB6, 0x61, /* 0xF8-0xFB */
+ 0xB6, 0x63, 0x00, 0x00, 0xB8, 0xFD, 0xE0, 0x75, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_8F[512] = {
+ 0xE0, 0x77, 0xE0, 0x76, 0xE0, 0x7B, 0xB8, 0xFB, /* 0x00-0x03 */
+ 0x00, 0x00, 0xE0, 0x78, 0xE0, 0x74, 0xE0, 0x79, /* 0x04-0x07 */
+ 0xE0, 0x7A, 0xB8, 0xFC, 0xB8, 0xFE, 0xE0, 0x7C, /* 0x08-0x0B */
+ 0x00, 0x00, 0xE4, 0x67, 0xE4, 0x66, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xE4, 0x64, 0xE4, 0x65, 0xBB, 0xB3, 0xBB, 0xB5, /* 0x10-0x13 */
+ 0xBB, 0xB2, 0xBB, 0xB4, 0xE8, 0x4D, 0xE8, 0x4E, /* 0x14-0x17 */
+ 0xE8, 0x49, 0x00, 0x00, 0xE8, 0x4A, 0xBD, 0xF8, /* 0x18-0x1B */
+ 0xBD, 0xFD, 0xBD, 0xF7, 0xBD, 0xFE, 0xBD, 0xF9, /* 0x1C-0x1F */
+ 0xE8, 0x4B, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x4C, /* 0x20-0x23 */
+ 0xE8, 0x48, 0xBE, 0x40, 0xBD, 0xFB, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0xBD, 0xFA, 0xBD, 0xFC, 0x00, 0x00, /* 0x28-0x2B */
+ 0xE8, 0x47, 0x00, 0x00, 0xEB, 0xCA, 0xBF, 0xE8, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0xEB, 0xCC, 0xBF, 0xEA, /* 0x30-0x33 */
+ 0xEB, 0xCF, 0xEB, 0xCB, 0xEB, 0xC9, 0xEB, 0xCE, /* 0x34-0x37 */
+ 0xBF, 0xE9, 0xEB, 0xCD, 0x00, 0x00, 0xBF, 0xE7, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0xC1, 0xD3, 0xC1, 0xD6, /* 0x3C-0x3F */
+ 0xEE, 0xC1, 0x00, 0x00, 0xC1, 0xD4, 0xEE, 0xC0, /* 0x40-0x43 */
+ 0xC1, 0xD2, 0xC1, 0xD5, 0xF1, 0x46, 0xF1, 0x47, /* 0x44-0x47 */
+ 0xF1, 0x48, 0xC2, 0xE0, 0x00, 0x00, 0xF1, 0x49, /* 0x48-0x4B */
+ 0x00, 0x00, 0xC2, 0xE1, 0xC3, 0xE2, 0xF3, 0x58, /* 0x4C-0x4F */
+ 0xF3, 0x59, 0xF3, 0x57, 0xF3, 0x56, 0xF3, 0x5A, /* 0x50-0x53 */
+ 0xC3, 0xE1, 0xF4, 0xDD, 0xF4, 0xDB, 0xF4, 0xDC, /* 0x54-0x57 */
+ 0xF4, 0xDE, 0xF4, 0xDA, 0xF4, 0xDF, 0xF6, 0x58, /* 0x58-0x5B */
+ 0x00, 0x00, 0xF6, 0x59, 0xF6, 0x57, 0xC5, 0x46, /* 0x5C-0x5F */
+ 0xF7, 0x64, 0xC5, 0xAF, 0xF7, 0x65, 0xF8, 0x48, /* 0x60-0x63 */
+ 0xF8, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA8, 0xAF, /* 0x98-0x9B */
+ 0xB6, 0x64, 0x00, 0x00, 0x00, 0x00, 0xB9, 0x40, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBB, 0xB6, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0xBF, 0xEC, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xBF, 0xEB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0xC3, 0xE3, 0xC4, 0x7C, 0xC5, 0x47, /* 0xAC-0xAF */
+ 0xA8, 0xB0, 0xB0, 0x64, 0xB9, 0x41, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0xF3, 0x5B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCB, 0xA6, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0xA8, 0xB1, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xA8, 0xB4, 0xA8, 0xB3, 0xA8, 0xB2, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xCB, 0xA5, 0x00, 0x00, 0xCD, 0xCD, /* 0xC8-0xCB */
+ 0x00, 0x00, 0xCD, 0xCF, 0xAA, 0xEF, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0xAA, 0xF1, 0xCD, 0xCC, 0xCD, 0xCE, /* 0xD0-0xD3 */
+ 0xAA, 0xF0, 0xCD, 0xD1, 0xCD, 0xD0, 0xCD, 0xD2, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0xD0, 0xB6, 0xD0, 0xB4, 0xAD, 0x7C, 0xD0, 0xB3, /* 0xE0-0xE3 */
+ 0xAD, 0xA3, 0xAD, 0x7E, 0xAD, 0x7B, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xAD, 0xA4, 0x00, 0x00, 0xAD, 0x7D, 0xAD, 0xA2, /* 0xE8-0xEB */
+ 0x00, 0x00, 0xAD, 0xA1, 0xD0, 0xB5, 0x00, 0x00, /* 0xEC-0xEF */
+ 0xAD, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xB0, 0x6A, 0xD3, 0xEB, 0xD3, 0xF1, 0xB0, 0x67, /* 0xF4-0xF7 */
+ 0xB0, 0x6E, 0x00, 0x00, 0xB0, 0x69, 0xD3, 0xEE, /* 0xF8-0xFB */
+ 0xD3, 0xF0, 0xB0, 0x6C, 0xD3, 0xEA, 0xD3, 0xED, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_90[512] = {
+ 0xB0, 0x68, 0xB0, 0x65, 0xD3, 0xEC, 0xB0, 0x6B, /* 0x00-0x03 */
+ 0xD3, 0xEF, 0xB0, 0x6D, 0xB0, 0x66, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD7, 0xE3, /* 0x08-0x0B */
+ 0xD7, 0xE6, 0xB3, 0x70, 0x00, 0x00, 0xB3, 0x7A, /* 0x0C-0x0F */
+ 0xB3, 0x76, 0xD7, 0xE4, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0xB3, 0x7E, 0xB3, 0x77, 0xB3, 0x7C, 0xB3, 0x72, /* 0x14-0x17 */
+ 0x00, 0x00, 0xB3, 0x6F, 0xB3, 0x71, 0xB3, 0x7D, /* 0x18-0x1B */
+ 0xD7, 0xE5, 0xB3, 0x75, 0xB3, 0x78, 0xB3, 0x74, /* 0x1C-0x1F */
+ 0xB3, 0x79, 0xD7, 0xE7, 0xB3, 0x7B, 0xB3, 0x73, /* 0x20-0x23 */
+ 0xD7, 0xE2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0xDC, 0x4D, 0xB6, 0x65, 0xDC, 0x4F, /* 0x2C-0x2F */
+ 0x00, 0x00, 0xB6, 0x67, 0xB6, 0x69, 0x00, 0x00, /* 0x30-0x33 */
+ 0xDC, 0x4E, 0xB6, 0x66, 0xB6, 0x6A, 0x00, 0x00, /* 0x34-0x37 */
+ 0xB6, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0xB9, 0x47, 0xE0, 0xA3, 0xB9, 0x4F, 0xE0, 0x7E, /* 0x3C-0x3F */
+ 0x00, 0x00, 0xB9, 0x50, 0xB9, 0x45, 0x00, 0x00, /* 0x40-0x43 */
+ 0xE0, 0xA1, 0x00, 0x00, 0x00, 0x00, 0xB9, 0x4A, /* 0x44-0x47 */
+ 0x00, 0x00, 0xE0, 0xA2, 0xB9, 0x43, 0xB9, 0x42, /* 0x48-0x4B */
+ 0x00, 0x00, 0xB9, 0x4D, 0xB9, 0x4C, 0xB9, 0x4B, /* 0x4C-0x4F */
+ 0xB9, 0x49, 0xB9, 0x4E, 0xE0, 0x7D, 0xB9, 0x44, /* 0x50-0x53 */
+ 0xB9, 0x46, 0xB9, 0x48, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0xBB, 0xB8, 0xBB, 0xBB, 0x00, 0x00, 0xBB, 0xBF, /* 0x58-0x5B */
+ 0xBB, 0xB9, 0xBB, 0xBE, 0xBB, 0xBC, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xBB, 0xB7, 0x00, 0x00, 0xBB, 0xBD, 0xBB, 0xBA, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x52, /* 0x64-0x67 */
+ 0xBE, 0x43, 0xBE, 0x41, 0x00, 0x00, 0xE8, 0x53, /* 0x68-0x6B */
+ 0x00, 0x00, 0xBE, 0x44, 0xBE, 0x42, 0xE8, 0x51, /* 0x6C-0x6F */
+ 0xE8, 0x50, 0x00, 0x00, 0xBF, 0xF0, 0xE8, 0x4F, /* 0x70-0x73 */
+ 0xBF, 0xEE, 0xBF, 0xED, 0xEB, 0xD0, 0xBE, 0x45, /* 0x74-0x77 */
+ 0xBF, 0xEF, 0xEB, 0xD1, 0xBF, 0xF2, 0xEB, 0xD2, /* 0x78-0x7B */
+ 0xBF, 0xF1, 0xC1, 0xD8, 0xEE, 0xC3, 0xC1, 0xD7, /* 0x7C-0x7F */
+
+ 0xC1, 0xDC, 0xC1, 0xDA, 0xC1, 0xDB, 0xC2, 0xE3, /* 0x80-0x83 */
+ 0xC1, 0xD9, 0xEE, 0xC2, 0xEB, 0xD3, 0xC2, 0xE2, /* 0x84-0x87 */
+ 0xC2, 0xE4, 0x00, 0x00, 0xC3, 0xE4, 0xC3, 0xE5, /* 0x88-0x8B */
+ 0x00, 0x00, 0xF4, 0xE0, 0x00, 0x00, 0xC5, 0xDE, /* 0x8C-0x8F */
+ 0xC5, 0xDD, 0xA8, 0xB6, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0xCA, 0x55, 0xB0, 0x6F, 0x00, 0x00, 0xCA, 0x52, /* 0x94-0x97 */
+ 0xCA, 0x53, 0xCA, 0x51, 0x00, 0x00, 0xCA, 0x54, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0xCB, 0xAA, 0xCB, 0xA7, /* 0x9C-0x9F */
+ 0xCB, 0xAC, 0xCB, 0xA8, 0xA8, 0xB7, 0xA8, 0xBA, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0xCB, 0xA9, 0xA8, 0xB9, 0xCB, 0xAB, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0xA8, 0xB8, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCD, 0xD5, /* 0xAC-0xAF */
+ 0xCD, 0xD7, 0xAA, 0xF4, 0xCD, 0xD3, 0xCD, 0xD6, /* 0xB0-0xB3 */
+ 0xCD, 0xD4, 0xAA, 0xF2, 0xAA, 0xF5, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0xAA, 0xF3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0xD0, 0xB8, 0xD0, 0xBC, 0xD0, 0xB9, /* 0xBC-0xBF */
+ 0x00, 0x00, 0xAD, 0xA7, 0x00, 0x00, 0xAD, 0xA8, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0xD0, 0xBB, 0x00, 0x00, 0xD0, 0xBD, /* 0xC4-0xC7 */
+ 0xD0, 0xBF, 0x00, 0x00, 0xAD, 0xA5, 0xD0, 0xBE, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0xAD, 0xA6, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xD7, 0xEE, 0xD0, 0xBA, 0xD3, 0xF2, 0xD3, 0xFB, /* 0xD4-0xD7 */
+ 0xD3, 0xF9, 0xD3, 0xF4, 0xD3, 0xF5, 0xD3, 0xFA, /* 0xD8-0xDB */
+ 0xD3, 0xFC, 0xB0, 0x71, 0x00, 0x00, 0xD3, 0xF7, /* 0xDC-0xDF */
+ 0xD3, 0xF3, 0xB0, 0x70, 0xB0, 0x72, 0xD3, 0xF6, /* 0xE0-0xE3 */
+ 0xD3, 0xFD, 0xD3, 0xF8, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xB3, 0xA1, 0xD7, 0xF1, 0xD7, 0xE9, 0xD7, 0xEF, /* 0xE8-0xEB */
+ 0xD7, 0xF0, 0xB3, 0xA2, 0x00, 0x00, 0xD7, 0xE8, /* 0xEC-0xEF */
+ 0xD7, 0xEA, 0xD0, 0xB7, 0xD7, 0xEC, 0xD7, 0xED, /* 0xF0-0xF3 */
+ 0xD7, 0xEB, 0xB6, 0x6C, 0x00, 0x00, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xDC, 0x56, 0xEB, 0xD4, 0xDC, 0x57, /* 0xF8-0xFB */
+ 0xDC, 0x54, 0xB3, 0xA3, 0xB6, 0x6E, 0xDC, 0x53, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_91[512] = {
+ 0xDC, 0x59, 0xDC, 0x58, 0xB6, 0x6B, 0xDC, 0x5C, /* 0x00-0x03 */
+ 0xDC, 0x52, 0xDC, 0x5B, 0xDC, 0x50, 0xDC, 0x5A, /* 0x04-0x07 */
+ 0xDC, 0x55, 0xB6, 0x6D, 0x00, 0x00, 0xE0, 0xAA, /* 0x08-0x0B */
+ 0x00, 0x00, 0xE0, 0xA5, 0xE0, 0xAB, 0xE0, 0xA6, /* 0x0C-0x0F */
+ 0xE0, 0xA4, 0xE0, 0xA7, 0xB9, 0x51, 0x00, 0x00, /* 0x10-0x13 */
+ 0xE0, 0xA9, 0x00, 0x00, 0xE0, 0xA8, 0xB9, 0x52, /* 0x14-0x17 */
+ 0xBB, 0xC1, 0xBB, 0xC0, 0xE4, 0x6E, 0xE4, 0x71, /* 0x18-0x1B */
+ 0xE4, 0x69, 0xE4, 0x6D, 0xBB, 0xC2, 0xE4, 0x6C, /* 0x1C-0x1F */
+ 0xE4, 0x6A, 0xE4, 0x70, 0xE4, 0x6B, 0xE4, 0x68, /* 0x20-0x23 */
+ 0xE4, 0x6F, 0x00, 0x00, 0xE8, 0x59, 0xBE, 0x48, /* 0x24-0x27 */
+ 0xF1, 0x4A, 0xE8, 0x56, 0xE8, 0x57, 0xE8, 0x55, /* 0x28-0x2B */
+ 0xDC, 0x51, 0xBE, 0x47, 0xE8, 0x5A, 0xE8, 0x54, /* 0x2C-0x2F */
+ 0xBE, 0x46, 0xBE, 0x49, 0xE8, 0x58, 0xEB, 0xD5, /* 0x30-0x33 */
+ 0xBF, 0xF3, 0xEB, 0xD6, 0xEB, 0xD7, 0x00, 0x00, /* 0x34-0x37 */
+ 0xEE, 0xC4, 0xC1, 0xDD, 0xF1, 0x4B, 0xF1, 0x4C, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0xF1, 0x4D, 0xF3, 0x5D, /* 0x3C-0x3F */
+ 0xF3, 0x5C, 0xF4, 0xE2, 0x00, 0x00, 0xF4, 0xE1, /* 0x40-0x43 */
+ 0xF6, 0x5B, 0xF6, 0x5C, 0xF6, 0x5A, 0xF7, 0x66, /* 0x44-0x47 */
+ 0xC5, 0xB0, 0xA8, 0xBB, 0xAD, 0xAA, 0xAD, 0xA9, /* 0x48-0x4B */
+ 0xB0, 0x75, 0xB0, 0x74, 0xD4, 0x40, 0xD4, 0x41, /* 0x4C-0x4F */
+ 0xD3, 0xFE, 0x00, 0x00, 0xB0, 0x73, 0xD7, 0xF5, /* 0x50-0x53 */
+ 0x00, 0x00, 0xD7, 0xF6, 0xD7, 0xF2, 0xB3, 0xA4, /* 0x54-0x57 */
+ 0xD7, 0xF3, 0x00, 0x00, 0xD7, 0xF4, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x5F, /* 0x5C-0x5F */
+ 0xDC, 0x61, 0xDC, 0x5D, 0xDC, 0x60, 0xB6, 0x6F, /* 0x60-0x63 */
+ 0xDC, 0x5E, 0xB6, 0x70, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0xDD, 0x73, 0xB9, 0x55, 0xB9, 0x54, 0x00, 0x00, /* 0x68-0x6B */
+ 0xB9, 0x53, 0x00, 0x00, 0xE0, 0xAC, 0xE0, 0xAD, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0xE4, 0x73, 0xE4, 0x75, /* 0x70-0x73 */
+ 0xBB, 0xC6, 0xBB, 0xC3, 0x00, 0x00, 0xBB, 0xC5, /* 0x74-0x77 */
+ 0xBB, 0xC4, 0xE4, 0x74, 0xE4, 0x72, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0xE8, 0x61, 0xE8, 0x5E, 0xE8, 0x5F, 0xBE, 0x4D, /* 0x80-0x83 */
+ 0xE8, 0x60, 0xE8, 0x5B, 0xE8, 0x5C, 0xBE, 0x4A, /* 0x84-0x87 */
+ 0x00, 0x00, 0xBE, 0x4B, 0xE8, 0x5D, 0xBE, 0x4C, /* 0x88-0x8B */
+ 0x00, 0x00, 0xEB, 0xDB, 0x00, 0x00, 0xEB, 0xDC, /* 0x8C-0x8F */
+ 0xEB, 0xD9, 0xEB, 0xDA, 0xBF, 0xF4, 0xEB, 0xD8, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0xEE, 0xC8, 0xEE, 0xC5, 0xEE, 0xC7, /* 0x98-0x9B */
+ 0xC1, 0xE0, 0xEE, 0xCB, 0xC1, 0xDF, 0xEE, 0xC9, /* 0x9C-0x9F */
+ 0xEE, 0xCC, 0xEE, 0xCA, 0xEE, 0xC6, 0xC1, 0xDE, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0xF1, 0x4F, 0x00, 0x00, 0xF1, 0x50, /* 0xA4-0xA7 */
+ 0xF1, 0x4E, 0x00, 0x00, 0xF1, 0x52, 0xC2, 0xE5, /* 0xA8-0xAB */
+ 0xC2, 0xE6, 0xF3, 0x5F, 0xC3, 0xE7, 0xF1, 0x51, /* 0xAC-0xAF */
+ 0xF3, 0x5E, 0xC3, 0xE6, 0xF4, 0xE5, 0xF4, 0xE6, /* 0xB0-0xB3 */
+ 0xC4, 0xBF, 0xF4, 0xE4, 0x00, 0x00, 0xF4, 0xE3, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xF6, 0x5D, 0xC5, 0x48, 0x00, 0x00, /* 0xB8-0xBB */
+ 0xF8, 0x49, 0xF8, 0xC8, 0xF8, 0xC7, 0x00, 0x00, /* 0xBC-0xBF */
+ 0xC6, 0x43, 0xC6, 0x5D, 0xF8, 0xC9, 0xF9, 0x71, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0xC6, 0x6F, 0xA8, 0xBC, 0xAA, 0xF6, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xB9, 0x56, 0x00, 0x00, 0xC4, 0xC0, /* 0xC8-0xCB */
+ 0xA8, 0xBD, 0xAD, 0xAB, 0xB3, 0xA5, 0xB6, 0x71, /* 0xCC-0xCF */
+ 0xC2, 0xE7, 0xAA, 0xF7, 0x00, 0x00, 0xD0, 0xC1, /* 0xD0-0xD3 */
+ 0xD0, 0xC0, 0xD4, 0x42, 0x00, 0x00, 0xB0, 0x78, /* 0xD4-0xD7 */
+ 0xB0, 0x76, 0xB0, 0x7A, 0xD4, 0x44, 0x00, 0x00, /* 0xD8-0xDB */
+ 0xB0, 0x79, 0xB0, 0x77, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0xD4, 0x43, 0xB3, 0xA8, /* 0xE0-0xE3 */
+ 0xD7, 0xFC, 0x00, 0x00, 0xB3, 0xA7, 0xB3, 0xA9, /* 0xE4-0xE7 */
+ 0xD8, 0x42, 0xB3, 0xAB, 0xD7, 0xFE, 0xD8, 0x40, /* 0xE8-0xEB */
+ 0xD7, 0xF7, 0xB3, 0xAA, 0xD8, 0x43, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0xD7, 0xF9, 0x00, 0x00, 0xD7, 0xFA, /* 0xF0-0xF3 */
+ 0xD7, 0xF8, 0xB3, 0xA6, 0x00, 0x00, 0xD8, 0x41, /* 0xF4-0xF7 */
+ 0xD7, 0xFB, 0xD7, 0xFD, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0xDC, 0x6D, 0x00, 0x00, 0xDC, 0x6C, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_92[512] = {
+ 0xDC, 0x6A, 0xDC, 0x62, 0xDC, 0x71, 0xDC, 0x65, /* 0x00-0x03 */
+ 0xDC, 0x6F, 0xDC, 0x76, 0xDC, 0x6E, 0xB6, 0x79, /* 0x04-0x07 */
+ 0x00, 0x00, 0xB6, 0x75, 0xDC, 0x63, 0x00, 0x00, /* 0x08-0x0B */
+ 0xDC, 0x69, 0xB6, 0x77, 0x00, 0x00, 0xDC, 0x68, /* 0x0C-0x0F */
+ 0xB6, 0x78, 0xB6, 0x7A, 0xDC, 0x6B, 0x00, 0x00, /* 0x10-0x13 */
+ 0xB6, 0x72, 0xB6, 0x73, 0xDC, 0x77, 0xDC, 0x75, /* 0x14-0x17 */
+ 0x00, 0x00, 0xDC, 0x74, 0xDC, 0x66, 0x00, 0x00, /* 0x18-0x1B */
+ 0xDC, 0x72, 0x00, 0x00, 0xB6, 0x76, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB6, 0x74, /* 0x20-0x23 */
+ 0xDC, 0x73, 0xDC, 0x64, 0xDC, 0x67, 0xDC, 0x70, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0xE4, 0xBA, 0xE0, 0xB7, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xE0, 0xB0, 0xE0, 0xC3, 0xE0, 0xCC, 0xE0, 0xB3, /* 0x30-0x33 */
+ 0xB9, 0x61, 0x00, 0x00, 0xE0, 0xC0, 0xB9, 0x57, /* 0x34-0x37 */
+ 0xB9, 0x59, 0xB9, 0x65, 0xE0, 0xB1, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0xB9, 0x5A, 0xB9, 0x5C, 0xB9, 0x66, /* 0x3C-0x3F */
+ 0xB9, 0x5B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0xB9, 0x64, 0xE0, 0xB9, 0x00, 0x00, /* 0x44-0x47 */
+ 0xE0, 0xAE, 0xB9, 0x62, 0xE0, 0xB8, 0xB9, 0x5E, /* 0x48-0x4B */
+ 0xE0, 0xCA, 0xB9, 0x63, 0xE0, 0xC8, 0xE0, 0xBC, /* 0x4C-0x4F */
+ 0xE0, 0xC6, 0xB9, 0x60, 0xE0, 0xAF, 0xE0, 0xC9, /* 0x50-0x53 */
+ 0xE0, 0xC4, 0x00, 0x00, 0xE0, 0xCB, 0xB9, 0x58, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0xB9, 0x67, 0xB9, 0x5D, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0xE0, 0xB5, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xE0, 0xBD, 0xE0, 0xC1, 0x00, 0x00, 0xE0, 0xC5, /* 0x60-0x63 */
+ 0xB9, 0x5F, 0xE0, 0xB4, 0xE0, 0xB2, 0xE0, 0xBE, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0xE0, 0xBB, 0xE0, 0xBA, 0x00, 0x00, 0xE0, 0xBF, /* 0x6C-0x6F */
+ 0xE0, 0xC2, 0x00, 0x00, 0xE0, 0xC7, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0xE4, 0x78, 0x00, 0x00, /* 0x74-0x77 */
+ 0xBB, 0xC7, 0xE4, 0xA4, 0xE4, 0x7A, 0xBB, 0xCC, /* 0x78-0x7B */
+ 0xBB, 0xD0, 0xE4, 0xAD, 0xE4, 0xB5, 0xE4, 0xA6, /* 0x7C-0x7F */
+
+ 0xBB, 0xC8, 0x00, 0x00, 0xE4, 0xAA, 0xE0, 0xB6, /* 0x80-0x83 */
+ 0x00, 0x00, 0xBB, 0xC9, 0xE4, 0xB1, 0xE4, 0xB6, /* 0x84-0x87 */
+ 0xE4, 0xAE, 0x00, 0x00, 0xE4, 0xB0, 0xE4, 0xB9, /* 0x88-0x8B */
+ 0xE4, 0xB2, 0xE4, 0x7E, 0xE4, 0xA9, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xBB, 0xD1, 0x00, 0x00, 0xBB, 0xCD, /* 0x90-0x93 */
+ 0xE4, 0x7C, 0xE4, 0xAB, 0xBB, 0xCB, 0xE4, 0xA5, /* 0x94-0x97 */
+ 0xBB, 0xCA, 0xE4, 0xB3, 0xE4, 0xA2, 0xE4, 0x79, /* 0x98-0x9B */
+ 0xBB, 0xCE, 0xE4, 0xB8, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0xE4, 0x7B, 0xE4, 0xAF, 0xE4, 0xAC, 0xE4, 0xA7, /* 0xA0-0xA3 */
+ 0xE4, 0x77, 0xE4, 0x76, 0xE4, 0xA1, 0xE4, 0xB4, /* 0xA4-0xA7 */
+ 0xBB, 0xCF, 0xE4, 0xB7, 0xE4, 0x7D, 0xE4, 0xA3, /* 0xA8-0xAB */
+ 0xBE, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0xBE, 0x5A, 0xBE, 0x55, /* 0xB0-0xB3 */
+ 0xE8, 0xA4, 0xE8, 0xA1, 0xE8, 0x67, 0xBE, 0x50, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xF9, 0xD7, 0x00, 0x00, 0xBE, 0x4F, /* 0xB8-0xBB */
+ 0xBE, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0xE8, 0x65, 0xBE, 0x54, 0xE8, 0x71, 0xE8, 0x63, /* 0xC0-0xC3 */
+ 0xE8, 0x64, 0xBE, 0x4E, 0xE8, 0xA3, 0xBE, 0x58, /* 0xC4-0xC7 */
+ 0xE8, 0x74, 0xE8, 0x79, 0xE8, 0x73, 0xEB, 0xEE, /* 0xC8-0xCB */
+ 0xE8, 0x6F, 0xE8, 0x77, 0xE8, 0x75, 0xE8, 0x68, /* 0xCC-0xCF */
+ 0xE8, 0x62, 0xE8, 0x7D, 0xBE, 0x57, 0xE8, 0x7E, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0xE8, 0x78, 0x00, 0x00, 0xE8, 0x6D, /* 0xD4-0xD7 */
+ 0xE8, 0x6B, 0xE8, 0x66, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0xE8, 0x6E, 0xE8, 0x7B, 0xE8, 0x6A, /* 0xDC-0xDF */
+ 0xE8, 0x7A, 0xE8, 0xA2, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0xBE, 0x53, 0x00, 0x00, 0xE8, 0x76, 0xE8, 0x7C, /* 0xE4-0xE7 */
+ 0xE8, 0x72, 0xE8, 0x6C, 0xBE, 0x51, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0xE4, 0xA8, 0xE8, 0x70, /* 0xEC-0xEF */
+ 0xBE, 0x59, 0xE8, 0x69, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEB, 0xF4, /* 0xF4-0xF7 */
+ 0xBF, 0xF7, 0xEB, 0xF3, 0xEB, 0xF0, 0xEC, 0x44, /* 0xF8-0xFB */
+ 0xBF, 0xFB, 0x00, 0x00, 0xEC, 0x41, 0xEB, 0xF8, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_93[512] = {
+ 0xEC, 0x43, 0xEB, 0xE9, 0xEB, 0xF6, 0x00, 0x00, /* 0x00-0x03 */
+ 0xBF, 0xFD, 0x00, 0x00, 0xEB, 0xE1, 0x00, 0x00, /* 0x04-0x07 */
+ 0xEB, 0xDF, 0xEC, 0x42, 0x00, 0x00, 0xEC, 0x40, /* 0x08-0x0B */
+ 0xEB, 0xFE, 0xEB, 0xED, 0xEB, 0xEC, 0xEB, 0xE2, /* 0x0C-0x0F */
+ 0xC0, 0x40, 0x00, 0x00, 0xEB, 0xE8, 0xEB, 0xF2, /* 0x10-0x13 */
+ 0xEB, 0xFD, 0xC0, 0x43, 0xEC, 0x45, 0x00, 0x00, /* 0x14-0x17 */
+ 0xC1, 0xE8, 0xC0, 0x45, 0xBF, 0xFE, 0xEB, 0xE6, /* 0x18-0x1B */
+ 0x00, 0x00, 0xEB, 0xEF, 0xEB, 0xDE, 0xEB, 0xE0, /* 0x1C-0x1F */
+ 0xBF, 0xF5, 0xC0, 0x42, 0xBF, 0xFA, 0xEB, 0xE7, /* 0x20-0x23 */
+ 0xEB, 0xF7, 0xEB, 0xF1, 0xC0, 0x41, 0xEB, 0xDD, /* 0x24-0x27 */
+ 0xC1, 0xE3, 0xEB, 0xF9, 0xEB, 0xFC, 0xBF, 0xFC, /* 0x28-0x2B */
+ 0x00, 0x00, 0xEB, 0xEB, 0xC0, 0x44, 0xBF, 0xF9, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0xF8, /* 0x30-0x33 */
+ 0xEB, 0xF5, 0xEB, 0xFB, 0xBF, 0xF6, 0x00, 0x00, /* 0x34-0x37 */
+ 0xEB, 0xE4, 0xEB, 0xFA, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0xEB, 0xE5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0xEB, 0xEA, 0xEE, 0xD2, /* 0x44-0x47 */
+ 0x00, 0x00, 0xEE, 0xD7, 0xC1, 0xE5, 0xC1, 0xE7, /* 0x48-0x4B */
+ 0xEE, 0xDD, 0xC1, 0xE1, 0xEE, 0xEC, 0xEE, 0xE3, /* 0x4C-0x4F */
+ 0xEE, 0xD8, 0xEE, 0xD9, 0xEE, 0xE2, 0x00, 0x00, /* 0x50-0x53 */
+ 0xC1, 0xEE, 0xEE, 0xE1, 0xEE, 0xD1, 0xEE, 0xE0, /* 0x54-0x57 */
+ 0xEE, 0xD4, 0xEE, 0xED, 0xC1, 0xED, 0xC1, 0xEB, /* 0x58-0x5B */
+ 0xEE, 0xD5, 0x00, 0x00, 0xEE, 0xE8, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xEE, 0xDA, 0xEE, 0xE7, 0x00, 0x00, 0xEE, 0xE9, /* 0x60-0x63 */
+ 0xEE, 0xD0, 0xC1, 0xE6, 0x00, 0x00, 0xEE, 0xEA, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0xEE, 0xDE, 0x00, 0x00, /* 0x68-0x6B */
+ 0xC1, 0xEA, 0xEE, 0xDB, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xC1, 0xEC, 0xEE, 0xE4, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0xC1, 0xE4, 0xEE, 0xD6, 0xEE, 0xE5, /* 0x74-0x77 */
+ 0x00, 0x00, 0xEE, 0xDF, 0xEB, 0xE3, 0xEE, 0xE6, /* 0x78-0x7B */
+ 0xEE, 0xD3, 0x00, 0x00, 0xC1, 0xE9, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0xEE, 0xEB, 0x00, 0x00, 0xC1, 0xE2, 0xEE, 0xCE, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0xF1, 0x60, 0xF1, 0x59, 0xC2, 0xE9, 0x00, 0x00, /* 0x88-0x8B */
+ 0xF1, 0x54, 0xF1, 0x63, 0xF1, 0x5B, 0xEE, 0xDC, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xF1, 0x65, 0xF1, 0x55, 0x00, 0x00, /* 0x90-0x93 */
+ 0xC2, 0xE8, 0xF1, 0x5F, 0xC2, 0xEA, 0xC2, 0xF2, /* 0x94-0x97 */
+ 0xC2, 0xF0, 0xF1, 0x61, 0xC2, 0xF1, 0xF1, 0x57, /* 0x98-0x9B */
+ 0x00, 0x00, 0xF1, 0x58, 0xF1, 0x5D, 0xF1, 0x62, /* 0x9C-0x9F */
+ 0x00, 0x00, 0xEE, 0xCD, 0xC2, 0xEB, 0xF1, 0x6A, /* 0xA0-0xA3 */
+ 0xF1, 0x67, 0xF1, 0x6B, 0xF1, 0x5E, 0xF1, 0x5A, /* 0xA4-0xA7 */
+ 0xF1, 0x68, 0xF3, 0x6A, 0xF1, 0x5C, 0x00, 0x00, /* 0xA8-0xAB */
+ 0xC2, 0xEE, 0x00, 0x00, 0xC2, 0xED, 0xEE, 0xCF, /* 0xAC-0xAF */
+ 0xC2, 0xEF, 0xF1, 0x64, 0xF1, 0x66, 0xC2, 0xEC, /* 0xB0-0xB3 */
+ 0xF1, 0x69, 0xF1, 0x53, 0x00, 0x00, 0xF1, 0x56, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0xF3, 0x73, 0x00, 0x00, 0xF3, 0x63, 0xC3, 0xEB, /* 0xC0-0xC3 */
+ 0xF3, 0x71, 0x00, 0x00, 0x00, 0x00, 0xF3, 0x61, /* 0xC4-0xC7 */
+ 0xC3, 0xEC, 0x00, 0x00, 0xF3, 0x6C, 0x00, 0x00, /* 0xC8-0xCB */
+ 0xF3, 0x68, 0xC3, 0xF1, 0xF3, 0x72, 0xF3, 0x62, /* 0xCC-0xCF */
+ 0xF3, 0x65, 0xC3, 0xE9, 0xF3, 0x74, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0xF3, 0x6D, 0xF3, 0x70, 0xC3, 0xEF, 0xC3, 0xF4, /* 0xD4-0xD7 */
+ 0xC3, 0xF2, 0xF3, 0x69, 0xF3, 0x64, 0x00, 0x00, /* 0xD8-0xDB */
+ 0xC3, 0xED, 0xC3, 0xEE, 0xF3, 0x60, 0xC3, 0xEA, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xC3, 0xE8, 0xC3, 0xF0, 0xF3, 0x6F, /* 0xE0-0xE3 */
+ 0xC3, 0xF3, 0x00, 0x00, 0xF3, 0x6B, 0xF3, 0x75, /* 0xE4-0xE7 */
+ 0xC3, 0xF5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0xF3, 0x67, 0x00, 0x00, 0xF3, 0x6E, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0xF4, 0xF3, 0xF5, 0x42, 0xF4, 0xF5, /* 0xF4-0xF7 */
+ 0xF4, 0xFC, 0xF3, 0x66, 0xF4, 0xFA, 0xF4, 0xE9, /* 0xF8-0xFB */
+ 0xF5, 0x40, 0xC4, 0xC3, 0xF4, 0xED, 0xF4, 0xFE, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_94[512] = {
+ 0xF4, 0xF4, 0x00, 0x00, 0x00, 0x00, 0xC4, 0xC2, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0xF5, 0x44, 0xF4, 0xF6, /* 0x04-0x07 */
+ 0x00, 0x00, 0xF4, 0xFB, 0xF4, 0xFD, 0xF4, 0xE7, /* 0x08-0x0B */
+ 0xF5, 0x41, 0xF4, 0xF2, 0xF4, 0xF7, 0xF4, 0xEB, /* 0x0C-0x0F */
+ 0xF4, 0xEF, 0xF5, 0x43, 0xF4, 0xF9, 0xF4, 0xE8, /* 0x10-0x13 */
+ 0xF4, 0xEC, 0xF4, 0xEE, 0xF4, 0xF8, 0x00, 0x00, /* 0x14-0x17 */
+ 0xC4, 0xC1, 0xF4, 0xF1, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0xF4, 0xEA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0xF4, 0xF0, 0xF6, 0x61, 0xF6, 0x66, 0xC5, 0x4F, /* 0x28-0x2B */
+ 0xF6, 0x68, 0x00, 0x00, 0xC5, 0x49, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xF6, 0x64, 0xF6, 0x6A, 0xC5, 0x4E, 0xC5, 0x4A, /* 0x30-0x33 */
+ 0x00, 0x00, 0xC5, 0x4B, 0xF6, 0x60, 0xF6, 0x67, /* 0x34-0x37 */
+ 0xC5, 0x4D, 0xF6, 0x65, 0xC5, 0x4C, 0xF6, 0x5F, /* 0x38-0x3B */
+ 0xF6, 0x63, 0xF6, 0x62, 0x00, 0x00, 0xF6, 0x5E, /* 0x3C-0x3F */
+ 0xF6, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0xC5, 0xB1, 0xF7, 0x6D, 0xF7, 0x70, 0xF7, 0x6C, /* 0x44-0x47 */
+ 0xF7, 0x6E, 0xF7, 0x6F, 0xF7, 0x69, 0xF7, 0x6A, /* 0x48-0x4B */
+ 0xF7, 0x67, 0x00, 0x00, 0x00, 0x00, 0xF7, 0x6B, /* 0x4C-0x4F */
+ 0xF7, 0x68, 0xC5, 0xB2, 0xC5, 0xB3, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0xF8, 0x4B, 0x00, 0x00, 0xF8, 0x4D, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0xF8, 0x4C, 0xF8, 0x4E, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xC5, 0xE0, 0x00, 0x00, 0xF8, 0x4A, 0xC5, 0xDF, /* 0x60-0x63 */
+ 0xC5, 0xE1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0xF8, 0xCB, 0xF8, 0xCC, 0xC6, 0x44, 0xF8, 0xCA, /* 0x68-0x6B */
+ 0x00, 0x00, 0xF9, 0x53, 0xF9, 0x52, 0xF9, 0x54, /* 0x6C-0x6F */
+ 0xC6, 0x5F, 0xF9, 0x55, 0xC6, 0x5E, 0xF9, 0x56, /* 0x70-0x73 */
+ 0xF9, 0x72, 0xF9, 0x75, 0xF9, 0x74, 0xC6, 0x68, /* 0x74-0x77 */
+ 0xF9, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0xC6, 0x72, 0xC6, 0x70, 0xC6, 0x71, 0xC6, 0x77, /* 0x7C-0x7F */
+
+ 0xF9, 0xC0, 0xF9, 0xC1, 0xF9, 0xBF, 0xF9, 0xC9, /* 0x80-0x83 */
+};
+
+static unsigned char u2c_95[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xF8, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0xD8, 0x44, 0xDC, 0x78, /* 0x78-0x7B */
+ 0xE8, 0xA5, 0xF3, 0x76, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0xAA, 0xF9, 0x00, 0x00, 0xAD, 0xAC, 0xB0, 0x7B, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0xD8, 0x45, 0x00, 0x00, /* 0x84-0x87 */
+ 0xD8, 0x46, 0xB3, 0xAC, 0x00, 0x00, 0xB6, 0x7D, /* 0x88-0x8B */
+ 0xDC, 0x7A, 0xDC, 0x79, 0xB6, 0xA3, 0xB6, 0x7C, /* 0x8C-0x8F */
+ 0xDC, 0x7B, 0xB6, 0x7E, 0xB6, 0xA2, 0xB6, 0xA1, /* 0x90-0x93 */
+ 0xB6, 0x7B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0xB9, 0x68, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xD0, /* 0x98-0x9B */
+ 0xE0, 0xCE, 0x00, 0x00, 0xE0, 0xCF, 0xE0, 0xCD, /* 0x9C-0x9F */
+ 0x00, 0x00, 0xBB, 0xD2, 0x00, 0x00, 0xBB, 0xD5, /* 0xA0-0xA3 */
+ 0xBB, 0xD7, 0xBB, 0xD6, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xBB, 0xD3, 0xBB, 0xD4, 0x00, 0x00, 0xE8, 0xA7, /* 0xA8-0xAB */
+ 0xE8, 0xA6, 0xBE, 0x5B, 0xE8, 0xA8, 0x00, 0x00, /* 0xAC-0xAF */
+ 0xE8, 0xA9, 0xBE, 0x5C, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0xEC, 0x4D, 0xEC, 0x4B, 0xEE, 0xF3, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xEC, 0x49, 0xEC, 0x4A, 0xC0, 0x46, /* 0xB8-0xBB */
+ 0xEC, 0x46, 0xEC, 0x4E, 0xEC, 0x48, 0xEC, 0x4C, /* 0xBC-0xBF */
+ 0xEE, 0xEF, 0x00, 0x00, 0x00, 0x00, 0xEE, 0xF1, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0xEE, 0xF2, 0xC1, 0xF3, 0xEE, 0xEE, /* 0xC4-0xC7 */
+ 0xC1, 0xF2, 0xEE, 0xF0, 0xC1, 0xEF, 0xC1, 0xF0, /* 0xC8-0xCB */
+ 0xC1, 0xF1, 0xEC, 0x47, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0xC2, 0xF5, 0xF1, 0x6E, 0xF1, 0x6C, 0xF1, 0x6D, /* 0xD0-0xD3 */
+ 0xC2, 0xF3, 0xC2, 0xF6, 0xC2, 0xF4, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0xF3, 0x77, 0xF3, 0x78, /* 0xD8-0xDB */
+ 0xC3, 0xF6, 0x00, 0x00, 0xF5, 0x45, 0xF5, 0x47, /* 0xDC-0xDF */
+ 0xF5, 0x46, 0xC4, 0xC4, 0xC5, 0x50, 0xF6, 0x6D, /* 0xE0-0xE3 */
+ 0xF6, 0x6C, 0xF6, 0x6B, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+};
+
+static unsigned char u2c_96[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0xAA, 0xFA, 0x00, 0x00, 0xC9, 0xAA, 0x00, 0x00, /* 0x1C-0x1F */
+ 0xCA, 0x58, 0xA6, 0xE9, 0xCA, 0x56, 0xCA, 0x59, /* 0x20-0x23 */
+ 0xCA, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0xCB, 0xAE, 0x00, 0x00, 0xA8, 0xC1, 0x00, 0x00, /* 0x28-0x2B */
+ 0xA8, 0xC2, 0xCB, 0xB0, 0xA8, 0xBF, 0xCB, 0xAF, /* 0x2C-0x2F */
+ 0xCB, 0xAD, 0xA8, 0xC0, 0xA8, 0xBE, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0xCD, 0xD8, 0xCD, 0xDB, 0xAA, 0xFD, /* 0x38-0x3B */
+ 0xCD, 0xDA, 0xCD, 0xD9, 0x00, 0x00, 0xAA, 0xFC, /* 0x3C-0x3F */
+ 0xAA, 0xFB, 0x00, 0x00, 0xAB, 0x40, 0xCD, 0xDC, /* 0x40-0x43 */
+ 0xAA, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0xD0, 0xC6, 0xAD, 0xAE, /* 0x48-0x4B */
+ 0xAD, 0xAF, 0xAD, 0xB0, 0xD0, 0xC7, 0xD0, 0xC3, /* 0x4C-0x4F */
+ 0xAD, 0xAD, 0xD0, 0xC4, 0x00, 0x00, 0xD0, 0xC5, /* 0x50-0x53 */
+ 0xD0, 0xC2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0xB0, 0xA4, 0x00, 0x00, 0x00, 0x00, 0xB0, 0xA1, /* 0x58-0x5B */
+ 0xD4, 0x45, 0xB0, 0xA2, 0xB0, 0xA5, 0xD4, 0x46, /* 0x5C-0x5F */
+ 0x00, 0x00, 0xB0, 0x7E, 0xB0, 0x7C, 0xB0, 0x7D, /* 0x60-0x63 */
+ 0xB0, 0xA3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0xB3, 0xAD, 0xD8, 0x49, /* 0x68-0x6B */
+ 0xB3, 0xB5, 0xD8, 0x48, 0x00, 0x00, 0xD8, 0x4B, /* 0x6C-0x6F */
+ 0xB3, 0xB1, 0xD8, 0x4A, 0xB6, 0xAB, 0xB3, 0xAF, /* 0x70-0x73 */
+ 0xB3, 0xB2, 0xB3, 0xAE, 0xB3, 0xB3, 0xB3, 0xB4, /* 0x74-0x77 */
+ 0xB3, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0xD8, 0x47, 0xB6, 0xA7, 0xDC, 0x7D, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0xDC, 0xA3, 0x00, 0x00, 0x00, 0x00, 0xDC, 0xA2, /* 0x80-0x83 */
+ 0xB6, 0xAC, 0xB6, 0xA8, 0xB6, 0xA9, 0xDC, 0x7C, /* 0x84-0x87 */
+ 0xDC, 0x7E, 0xDC, 0xA1, 0xB6, 0xA4, 0xB6, 0xA6, /* 0x88-0x8B */
+ 0x00, 0x00, 0xB6, 0xAA, 0xB6, 0xA5, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xE0, 0xD3, 0xE0, 0xD1, 0xE0, 0xD2, /* 0x90-0x93 */
+ 0xB9, 0x6A, 0xB9, 0x6B, 0x00, 0x00, 0xE0, 0xD4, /* 0x94-0x97 */
+ 0xB9, 0x69, 0xBB, 0xD8, 0x00, 0x00, 0xBB, 0xDA, /* 0x98-0x9B */
+ 0xBB, 0xD9, 0x00, 0x00, 0xE4, 0xBB, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0xE4, 0xBC, 0xE8, 0xAB, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0xE8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x47, /* 0xA4-0xA7 */
+ 0xC0, 0x48, 0xEC, 0x4F, 0xC0, 0x49, 0x00, 0x00, /* 0xA8-0xAB */
+ 0xEE, 0xF6, 0x00, 0x00, 0xEE, 0xF4, 0x00, 0x00, /* 0xAC-0xAF */
+ 0xEE, 0xF5, 0xC1, 0xF4, 0x00, 0x00, 0xF1, 0x6F, /* 0xB0-0xB3 */
+ 0xC3, 0xF7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0xC1, 0xF5, 0xAB, 0x41, 0x00, 0x00, 0xB0, 0xA6, /* 0xB8-0xBB */
+ 0xD4, 0x47, 0x00, 0x00, 0x00, 0x00, 0xD8, 0x4C, /* 0xBC-0xBF */
+ 0xB3, 0xB6, 0xB6, 0xAD, 0xDC, 0xA4, 0xDC, 0xA6, /* 0xC0-0xC3 */
+ 0xB6, 0xAF, 0xB6, 0xAE, 0xB6, 0xB0, 0xB6, 0xB1, /* 0xC4-0xC7 */
+ 0xDC, 0xA5, 0xB9, 0x6E, 0xB9, 0x6F, 0xB9, 0x6D, /* 0xC8-0xCB */
+ 0xBB, 0xDB, 0xB9, 0x6C, 0xE0, 0xD5, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0xBB, 0xDC, 0xE8, 0xAC, /* 0xD0-0xD3 */
+ 0xEC, 0x50, 0xC0, 0x4A, 0xC1, 0xF6, 0xF1, 0x70, /* 0xD4-0xD7 */
+ 0xF1, 0x74, 0xC2, 0xF9, 0xF1, 0x71, 0xC2, 0xFA, /* 0xD8-0xDB */
+ 0xC2, 0xF8, 0xF1, 0x75, 0xC2, 0xFB, 0xF1, 0x73, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xF3, 0x79, 0xC2, 0xF7, 0xC3, 0xF8, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0xF8, 0xCD, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xAB, 0x42, 0xB3, 0xB8, 0xB3, 0xB7, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB6, 0xB2, /* 0xEC-0xEF */
+ 0xDC, 0xA8, 0xDC, 0xA7, 0xB6, 0xB3, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0xE0, 0xD9, 0xB9, 0x73, 0xB9, 0x70, /* 0xF4-0xF7 */
+ 0xE0, 0xD8, 0xB9, 0x72, 0xE0, 0xD6, 0xB9, 0x71, /* 0xF8-0xFB */
+ 0x00, 0x00, 0xE0, 0xD7, 0x00, 0x00, 0xE4, 0xBD, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_97[512] = {
+ 0xBB, 0xDD, 0x00, 0x00, 0xE8, 0xAF, 0x00, 0x00, /* 0x00-0x03 */
+ 0xBE, 0x5D, 0xE8, 0xAD, 0xBE, 0x5E, 0xBE, 0x5F, /* 0x04-0x07 */
+ 0xE8, 0xAE, 0xBE, 0x60, 0x00, 0x00, 0xEC, 0x51, /* 0x08-0x0B */
+ 0x00, 0x00, 0xC0, 0x4E, 0xC0, 0x4B, 0xC0, 0x50, /* 0x0C-0x0F */
+ 0xEC, 0x53, 0xC0, 0x4C, 0xEC, 0x52, 0xC0, 0x4F, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0xC0, 0x4D, 0x00, 0x00, /* 0x14-0x17 */
+ 0xEE, 0xF9, 0xEE, 0xFB, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0xC1, 0xF7, 0xEE, 0xFA, 0xC1, 0xF8, 0xEE, 0xF8, /* 0x1C-0x1F */
+ 0xEE, 0xF7, 0x00, 0x00, 0xF1, 0x77, 0xF1, 0x76, /* 0x20-0x23 */
+ 0xC2, 0xFC, 0xF1, 0x78, 0xF3, 0x7E, 0xC3, 0xFA, /* 0x24-0x27 */
+ 0xF3, 0x7D, 0xF3, 0x7A, 0xC3, 0xF9, 0xF3, 0x7B, /* 0x28-0x2B */
+ 0xF3, 0x7C, 0x00, 0x00, 0xF5, 0x48, 0xF5, 0x49, /* 0x2C-0x2F */
+ 0xC4, 0xC5, 0x00, 0x00, 0xC5, 0x53, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0xF6, 0x6E, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0xC5, 0x51, 0xC5, 0x52, 0xF6, 0x6F, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0xC5, 0xB4, 0xC5, 0xB5, 0xF7, 0x71, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0xC6, 0x45, 0xF8, 0xCF, /* 0x40-0x43 */
+ 0xC6, 0x47, 0x00, 0x00, 0xF8, 0xCE, 0xF8, 0xD0, /* 0x44-0x47 */
+ 0xC6, 0x46, 0xF9, 0x57, 0x00, 0x00, 0xF9, 0xAD, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0xAB, 0x43, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0xB9, 0x74, 0x00, 0x00, /* 0x54-0x57 */
+ 0xE4, 0xBE, 0x00, 0x00, 0xE8, 0xB0, 0xC0, 0x51, /* 0x58-0x5B */
+ 0xC0, 0x52, 0x00, 0x00, 0xAB, 0x44, 0x00, 0x00, /* 0x5C-0x5F */
+ 0xBE, 0x61, 0xC3, 0xFB, 0xAD, 0xB1, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0xC0, 0x53, 0x00, 0x00, /* 0x64-0x67 */
+ 0xC5, 0xE2, 0xAD, 0xB2, 0xD8, 0x4D, 0x00, 0x00, /* 0x68-0x6B */
+ 0xDC, 0xA9, 0x00, 0x00, 0xDC, 0xAB, 0x00, 0x00, /* 0x6C-0x6F */
+ 0xDC, 0xAA, 0x00, 0x00, 0xE0, 0xDD, 0xE0, 0xDA, /* 0x70-0x73 */
+ 0xB9, 0x75, 0x00, 0x00, 0xB9, 0x76, 0xE0, 0xDB, /* 0x74-0x77 */
+ 0xE0, 0xDC, 0x00, 0x00, 0xE4, 0xC0, 0xE4, 0xC5, /* 0x78-0x7B */
+ 0xBB, 0xDE, 0xE4, 0xBF, 0xE4, 0xC1, 0xE4, 0xC8, /* 0x7C-0x7F */
+
+ 0xE4, 0xC3, 0xE4, 0xC7, 0xE4, 0xC4, 0xE4, 0xC2, /* 0x80-0x83 */
+ 0xE4, 0xC6, 0xBB, 0xDF, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0xE8, 0xB3, 0x00, 0x00, 0xE8, 0xB1, 0xBE, 0x63, /* 0x88-0x8B */
+ 0x00, 0x00, 0xBE, 0x62, 0xE8, 0xB2, 0xBE, 0x64, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0xEC, 0x56, 0x00, 0x00, 0x00, 0x00, 0xEC, 0x55, /* 0x94-0x97 */
+ 0xC0, 0x54, 0xEC, 0x54, 0xEE, 0xFC, 0x00, 0x00, /* 0x98-0x9B */
+ 0xEE, 0xFE, 0xEF, 0x41, 0xEF, 0x40, 0x00, 0x00, /* 0x9C-0x9F */
+ 0xC1, 0xF9, 0xEE, 0xFD, 0xF1, 0xA1, 0xC2, 0xFD, /* 0xA0-0xA3 */
+ 0xF1, 0x7D, 0xF1, 0xA2, 0xC2, 0xFE, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xF1, 0x7B, 0x00, 0x00, 0xF1, 0x7E, 0xF1, 0x7C, /* 0xA8-0xAB */
+ 0xF1, 0x79, 0xC3, 0x40, 0xF1, 0x7A, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF3, 0xA1, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0xF3, 0xA3, 0xF3, 0xA2, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xF5, 0x4A, 0x00, 0x00, 0xF5, 0x4B, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF6, 0x70, /* 0xBC-0xBF */
+ 0x00, 0x00, 0xC5, 0xB7, 0x00, 0x00, 0xC5, 0xB6, /* 0xC0-0xC3 */
+ 0xF8, 0x4F, 0xF8, 0x50, 0xC6, 0x48, 0xF8, 0xD1, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xC6, 0x69, 0x00, 0x00, 0xAD, 0xB3, /* 0xC8-0xCB */
+ 0xB6, 0xB4, 0xE4, 0xCA, 0xE4, 0xC9, 0xE8, 0xB5, /* 0xCC-0xCF */
+ 0xE8, 0xB4, 0x00, 0x00, 0x00, 0x00, 0xC1, 0xFA, /* 0xD0-0xD3 */
+ 0xEF, 0x43, 0xEF, 0x42, 0xF1, 0xA5, 0xF1, 0xA3, /* 0xD4-0xD7 */
+ 0xF1, 0xA6, 0xF1, 0xA4, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0xC3, 0xFC, 0xF3, 0xA4, 0xF3, 0xA5, 0xF3, 0xA6, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xF6, 0x71, 0x00, 0x00, 0xF7, 0x72, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0xF8, 0xD2, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0xAD, 0xB4, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0xEC, 0x57, 0xEF, 0x44, 0x00, 0x00, 0xAD, 0xB5, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0xBB, 0xE0, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0xEC, 0x58, 0xC3, 0x41, 0xF1, 0xA7, 0xC3, 0xFD, /* 0xF8-0xFB */
+ 0x00, 0x00, 0xF5, 0x4C, 0xF5, 0x4D, 0xC5, 0x54, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_98[512] = {
+ 0xF8, 0x51, 0xAD, 0xB6, 0xB3, 0xBB, 0xB3, 0xBC, /* 0x00-0x03 */
+ 0xD8, 0x4E, 0xB6, 0xB5, 0xB6, 0xB6, 0xDC, 0xAC, /* 0x04-0x07 */
+ 0xB6, 0xB7, 0x00, 0x00, 0xB9, 0x7A, 0x00, 0x00, /* 0x08-0x0B */
+ 0xB9, 0x7C, 0xE0, 0xDF, 0xE0, 0xE0, 0xE0, 0xDE, /* 0x0C-0x0F */
+ 0xB9, 0x77, 0xB9, 0x78, 0xB9, 0x7B, 0xB9, 0x79, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0xE4, 0xCB, 0xBB, 0xE1, /* 0x14-0x17 */
+ 0xBB, 0xE2, 0x00, 0x00, 0x00, 0x00, 0xE8, 0xBC, /* 0x18-0x1B */
+ 0xBE, 0x67, 0xE8, 0xB7, 0xE8, 0xB6, 0x00, 0x00, /* 0x1C-0x1F */
+ 0xE8, 0xBB, 0xBE, 0x65, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0xC0, 0x5B, 0x00, 0x00, 0xE8, 0xB8, 0xE8, 0xBD, /* 0x24-0x27 */
+ 0xE8, 0xBA, 0xE8, 0xB9, 0x00, 0x00, 0xBE, 0x66, /* 0x28-0x2B */
+ 0x00, 0x00, 0xC0, 0x59, 0x00, 0x00, 0xEC, 0x5A, /* 0x2C-0x2F */
+ 0xC0, 0x55, 0x00, 0x00, 0xEC, 0x5B, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0xEC, 0x59, 0x00, 0x00, 0xC0, 0x58, /* 0x34-0x37 */
+ 0xC0, 0x56, 0xC0, 0x5A, 0x00, 0x00, 0xC0, 0x57, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0xEF, 0x45, 0x00, 0x00, 0xEF, 0x4A, /* 0x40-0x43 */
+ 0xEF, 0x46, 0xEF, 0x49, 0xC1, 0xFB, 0x00, 0x00, /* 0x44-0x47 */
+ 0xED, 0xD4, 0xEF, 0x48, 0xEF, 0x47, 0x00, 0x00, /* 0x48-0x4B */
+ 0xC3, 0x44, 0xC3, 0x42, 0xC3, 0x45, 0xC3, 0x43, /* 0x4C-0x4F */
+ 0xF1, 0xA8, 0xF1, 0xA9, 0xF1, 0xAA, 0xC3, 0x46, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF3, 0xAA, /* 0x54-0x57 */
+ 0xC4, 0x40, 0xF3, 0xA8, 0x00, 0x00, 0xC4, 0x41, /* 0x58-0x5B */
+ 0xF3, 0xA7, 0xF3, 0xA9, 0xC3, 0xFE, 0xF5, 0x51, /* 0x5C-0x5F */
+ 0xF5, 0x4E, 0x00, 0x00, 0xF5, 0x4F, 0xF5, 0x50, /* 0x60-0x63 */
+ 0xF6, 0x72, 0xC5, 0x56, 0x00, 0x00, 0xC5, 0x55, /* 0x64-0x67 */
+ 0x00, 0x00, 0xF7, 0x74, 0xF7, 0x73, 0xC5, 0xB8, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC5, 0xE3, /* 0x6C-0x6F */
+ 0xC6, 0x49, 0xC6, 0x60, 0xF9, 0x58, 0xF9, 0xAE, /* 0x70-0x73 */
+ 0xF9, 0xAF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xAD, 0xB7, 0xDC, 0xAD, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0xE0, 0xE1, 0xE4, 0xCC, 0xE4, 0xCD, 0xBB, 0xE3, /* 0xAC-0xAF */
+ 0x00, 0x00, 0xBB, 0xE4, 0xE8, 0xBE, 0xBE, 0x68, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0xC1, 0xFC, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0xF1, 0xAB, 0x00, 0x00, 0xC3, 0x47, 0xF3, 0xAD, /* 0xB8-0xBB */
+ 0xC4, 0x42, 0xF3, 0xAC, 0xF3, 0xAE, 0xF3, 0xAB, /* 0xBC-0xBF */
+ 0xF6, 0x75, 0xF5, 0x52, 0xF5, 0x53, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0xC4, 0xC6, 0x00, 0x00, 0xF6, 0x74, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xF6, 0x73, 0x00, 0x00, 0xF7, 0x75, /* 0xC8-0xCB */
+ 0xF9, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAD, 0xB8, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAD, 0xB9, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0xB0, 0xA7, 0xD4, 0x48, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0xD8, 0x4F, 0x00, 0x00, 0xB6, 0xB8, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0xB6, 0xBB, 0xB6, 0xB9, 0xDC, 0xAE, /* 0xE8-0xEB */
+ 0x00, 0x00, 0xB6, 0xBD, 0x00, 0x00, 0xB6, 0xBA, /* 0xEC-0xEF */
+ 0x00, 0x00, 0x00, 0x00, 0xB6, 0xBC, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xB9, 0x7E, 0x00, 0x00, 0xE0, 0xE2, 0x00, 0x00, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xE0, 0xE3, 0xE8, 0xC0, 0x00, 0x00, /* 0xF8-0xFB */
+ 0xB9, 0x7D, 0xB9, 0xA1, 0xB9, 0xA2, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_99[512] = {
+ 0xE4, 0xCF, 0x00, 0x00, 0xE4, 0xCE, 0xBB, 0xE5, /* 0x00-0x03 */
+ 0x00, 0x00, 0xBB, 0xE6, 0x00, 0x00, 0xE4, 0xD0, /* 0x04-0x07 */
+ 0xE8, 0xBF, 0xBB, 0xE8, 0xBE, 0x69, 0x00, 0x00, /* 0x08-0x0B */
+ 0xBB, 0xE7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xC0, 0x5C, 0xE8, 0xC1, 0xBE, 0x6B, 0xBE, 0x6A, /* 0x10-0x13 */
+ 0xE8, 0xC2, 0xE8, 0xC5, 0xE8, 0xC3, 0xE8, 0xC4, /* 0x14-0x17 */
+ 0xBE, 0x6C, 0x00, 0x00, 0xC0, 0x61, 0xC0, 0x5F, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0xC0, 0x5E, 0xEC, 0x5D, /* 0x1C-0x1F */
+ 0x00, 0x00, 0xC0, 0x60, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0xEC, 0x5C, 0xEF, 0x4B, 0x00, 0x00, 0xEC, 0x5E, /* 0x24-0x27 */
+ 0xC0, 0x5D, 0xEC, 0x5F, 0xEF, 0x4E, 0xEF, 0x4C, /* 0x28-0x2B */
+ 0xEF, 0x4D, 0xEF, 0x52, 0xC3, 0x4B, 0xEF, 0x51, /* 0x2C-0x2F */
+ 0xEF, 0x54, 0xEF, 0x53, 0xEF, 0x50, 0xEF, 0x4F, /* 0x30-0x33 */
+ 0x00, 0x00, 0xC1, 0xFD, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0xF1, 0xAE, 0x00, 0x00, /* 0x38-0x3B */
+ 0xF1, 0xAD, 0xC3, 0x4A, 0xC3, 0x48, 0xC3, 0x49, /* 0x3C-0x3F */
+ 0x00, 0x00, 0xF1, 0xAC, 0x00, 0x00, 0xF3, 0xB1, /* 0x40-0x43 */
+ 0x00, 0x00, 0xC4, 0x43, 0x00, 0x00, 0xF3, 0xB0, /* 0x44-0x47 */
+ 0xF3, 0xAF, 0xC4, 0x44, 0x00, 0x00, 0xF5, 0x58, /* 0x48-0x4B */
+ 0xF5, 0x57, 0x00, 0x00, 0xF5, 0x55, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xF5, 0x54, 0xC4, 0xC8, 0xC4, 0xC7, 0xF5, 0x59, /* 0x50-0x53 */
+ 0xF7, 0x76, 0xC5, 0xB9, 0xF6, 0x77, 0xC5, 0x57, /* 0x54-0x57 */
+ 0xF6, 0x76, 0xF5, 0x56, 0x00, 0x00, 0xF7, 0x77, /* 0x58-0x5B */
+ 0xC5, 0xE4, 0x00, 0x00, 0xC6, 0x61, 0xF9, 0x59, /* 0x5C-0x5F */
+ 0x00, 0x00, 0xF9, 0xB1, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0xAD, 0xBA, 0xD8, 0x50, /* 0x94-0x97 */
+ 0xEF, 0x55, 0xAD, 0xBB, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0xE4, 0xD2, 0xE4, 0xD1, 0xEC, 0x60, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0xEF, 0x57, 0x00, 0x00, 0xEF, 0x56, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0xC3, 0x4C, 0xF3, 0xB2, 0xF3, 0xB3, /* 0xA4-0xA7 */
+ 0xC4, 0xC9, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xB2, /* 0xA8-0xAB */
+ 0xB0, 0xA8, 0xB6, 0xBF, 0xB6, 0xBE, 0xE0, 0xE4, /* 0xAC-0xAF */
+ 0xE0, 0xE6, 0xB9, 0xA4, 0xE0, 0xE5, 0xB9, 0xA3, /* 0xB0-0xB3 */
+ 0xB9, 0xA5, 0xE0, 0xE7, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0xE4, 0xD4, 0xE4, 0xD6, 0xE4, 0xD5, /* 0xB8-0xBB */
+ 0x00, 0x00, 0xE4, 0xD8, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0xBB, 0xE9, 0xE4, 0xD7, 0xE4, 0xD3, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0xD9, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0xE8, 0xCC, 0x00, 0x00, 0xE8, 0xCF, /* 0xC8-0xCB */
+ 0xE8, 0xD1, 0xE8, 0xC7, 0xE8, 0xCB, 0xE8, 0xC8, /* 0xCC-0xCF */
+ 0xBE, 0x6E, 0xBE, 0x71, 0xBE, 0x73, 0xE8, 0xC9, /* 0xD0-0xD3 */
+ 0xE8, 0xCA, 0xBE, 0x72, 0xE8, 0xCD, 0xE8, 0xD0, /* 0xD4-0xD7 */
+ 0xE8, 0xCE, 0xBE, 0x74, 0x00, 0x00, 0xBE, 0x70, /* 0xD8-0xDB */
+ 0xE8, 0xC6, 0xBE, 0x6D, 0x00, 0x00, 0xBE, 0x6F, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0xC0, 0x63, 0xEC, 0x66, /* 0xE0-0xE3 */
+ 0xEC, 0x64, 0xEC, 0x63, 0x00, 0x00, 0xEC, 0x69, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0xEC, 0x68, 0xEC, 0x67, 0x00, 0x00, /* 0xE8-0xEB */
+ 0xEC, 0x62, 0xC0, 0x62, 0xEC, 0x61, 0x00, 0x00, /* 0xEC-0xEF */
+ 0xEC, 0x65, 0xC0, 0x64, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0xEF, 0x5A, 0x00, 0x00, 0xEF, 0x5E, 0xEF, 0x5B, /* 0xF4-0xF7 */
+ 0xEF, 0x5D, 0xEF, 0x5C, 0xEF, 0x59, 0xEF, 0x5F, /* 0xF8-0xFB */
+ 0xEF, 0x62, 0xEF, 0x60, 0xEF, 0x61, 0xC2, 0x40, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_9A[512] = {
+ 0x00, 0x00, 0xC1, 0xFE, 0xEF, 0x58, 0xEF, 0x63, /* 0x00-0x03 */
+ 0xF1, 0xB3, 0xF1, 0xB6, 0xF1, 0xB8, 0xF1, 0xB7, /* 0x04-0x07 */
+ 0x00, 0x00, 0xF1, 0xB1, 0xF1, 0xB5, 0xF1, 0xB0, /* 0x08-0x0B */
+ 0x00, 0x00, 0xF1, 0xB2, 0xC3, 0x4D, 0xF1, 0xAF, /* 0x0C-0x0F */
+ 0x00, 0x00, 0xF1, 0xB4, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0xF3, 0xC0, 0xF3, 0xB5, 0xC4, 0x45, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0xC4, 0x46, 0xF3, 0xB4, 0xF3, 0xB9, /* 0x18-0x1B */
+ 0xF3, 0xBF, 0xF3, 0xB7, 0xF3, 0xBE, 0x00, 0x00, /* 0x1C-0x1F */
+ 0xF3, 0xBB, 0x00, 0x00, 0xF3, 0xBA, 0xF3, 0xBD, /* 0x20-0x23 */
+ 0xF3, 0xB8, 0xF3, 0xB6, 0x00, 0x00, 0xF3, 0xBC, /* 0x24-0x27 */
+ 0x00, 0x00, 0xF5, 0x60, 0xF5, 0x5E, 0xC4, 0xCA, /* 0x28-0x2B */
+ 0xF5, 0x5D, 0xF5, 0x63, 0xF5, 0x61, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xC4, 0xCB, 0xF5, 0x5C, 0xF5, 0x5A, 0x00, 0x00, /* 0x30-0x33 */
+ 0xF5, 0x5B, 0xC4, 0xCD, 0xF5, 0x5F, 0xC4, 0xCC, /* 0x34-0x37 */
+ 0xF5, 0x62, 0xF6, 0x78, 0xF6, 0x7E, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0xF6, 0x79, 0xC5, 0x5B, 0xF6, 0xA1, /* 0x3C-0x3F */
+ 0xC5, 0x5A, 0xF6, 0x7D, 0xF6, 0x7C, 0xC5, 0x59, /* 0x40-0x43 */
+ 0xF6, 0x7B, 0xC5, 0x58, 0xF6, 0x7A, 0x00, 0x00, /* 0x44-0x47 */
+ 0xF7, 0x7D, 0xF7, 0xA1, 0xF7, 0x7E, 0x00, 0x00, /* 0x48-0x4B */
+ 0xF7, 0x7B, 0xC5, 0xBB, 0xF7, 0x78, 0xF7, 0x7C, /* 0x4C-0x4F */
+ 0xF7, 0xA3, 0x00, 0x00, 0xF7, 0xA2, 0xF7, 0x79, /* 0x50-0x53 */
+ 0xF7, 0x7A, 0xC5, 0xBA, 0xF8, 0x52, 0xC5, 0xE7, /* 0x54-0x57 */
+ 0x00, 0x00, 0xF8, 0x53, 0xC5, 0xE5, 0xC5, 0xE6, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0xF8, 0xD3, 0xC6, 0x4A, /* 0x5C-0x5F */
+ 0xF9, 0x76, 0x00, 0x00, 0xC6, 0x6A, 0x00, 0x00, /* 0x60-0x63 */
+ 0xF9, 0xB3, 0xC6, 0x6B, 0xF9, 0xB4, 0xF9, 0xB5, /* 0x64-0x67 */
+ 0xF9, 0xC3, 0xF9, 0xC2, 0xC6, 0x7A, 0xF9, 0xCD, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xB0, 0xA9, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xE9, /* 0xA8-0xAB */
+ 0x00, 0x00, 0xE0, 0xE8, 0x00, 0x00, 0xBB, 0xEA, /* 0xAC-0xAF */
+ 0xBB, 0xEB, 0xE4, 0xDA, 0x00, 0x00, 0xE8, 0xD2, /* 0xB0-0xB3 */
+ 0xEC, 0x6C, 0x00, 0x00, 0x00, 0x00, 0xBE, 0x75, /* 0xB4-0xB7 */
+ 0xC0, 0x65, 0xEC, 0x6A, 0x00, 0x00, 0xEC, 0x6D, /* 0xB8-0xBB */
+ 0xC0, 0x66, 0x00, 0x00, 0xEF, 0x64, 0xEC, 0x6B, /* 0xBC-0xBF */
+ 0xF1, 0xB9, 0xC3, 0x4E, 0xF3, 0xC1, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0xF5, 0x66, 0xF5, 0x64, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0xF5, 0x65, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0xF6, 0xA2, 0x00, 0x00, 0xC5, 0x5C, /* 0xCC-0xCF */
+ 0xF7, 0xA4, 0xC5, 0xEA, 0xC5, 0xBC, 0xC5, 0xE8, /* 0xD0-0xD3 */
+ 0xC5, 0xE9, 0xF8, 0xD4, 0xC6, 0x62, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xB0, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0xF1, 0xBA, 0x00, 0x00, 0x00, 0x00, 0xD4, 0x49, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xB9, 0xA6, 0x00, 0x00, 0xE4, 0xDB, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0x00, 0x00, 0xBB, 0xEC, 0xE4, 0xDC, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0xD4, /* 0xE8-0xEB */
+ 0xE8, 0xD3, 0xC0, 0x68, 0xBE, 0x76, 0xBE, 0x77, /* 0xEC-0xEF */
+ 0x00, 0x00, 0xE8, 0xD7, 0xE8, 0xD6, 0xE8, 0xD5, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0xEC, 0x6E, 0xEC, 0x71, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xEC, 0x70, 0xEC, 0x6F, 0xC0, 0x67, /* 0xF8-0xFB */
+ 0xEF, 0x68, 0xEF, 0x66, 0xEF, 0x65, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_9B[512] = {
+ 0x00, 0x00, 0xEF, 0x67, 0x00, 0x00, 0xC3, 0x4F, /* 0x00-0x03 */
+ 0xF1, 0xBC, 0xF1, 0xBD, 0xC3, 0x50, 0x00, 0x00, /* 0x04-0x07 */
+ 0xF1, 0xBB, 0x00, 0x00, 0xF3, 0xC3, 0xF3, 0xC2, /* 0x08-0x0B */
+ 0xF3, 0xC5, 0xC4, 0x47, 0xF3, 0xC4, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xF5, 0x67, 0xF5, 0x69, 0xF5, 0x68, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0xF6, 0xA3, 0xF6, 0xA6, 0xF6, 0xA4, /* 0x14-0x17 */
+ 0xF6, 0xA5, 0xF7, 0xA5, 0xC5, 0xBD, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0xF8, 0x54, 0xF8, 0x55, /* 0x1C-0x1F */
+ 0xF8, 0x56, 0x00, 0x00, 0xC6, 0x4B, 0xC6, 0x63, /* 0x20-0x23 */
+ 0xF9, 0xB6, 0xB0, 0xAB, 0x00, 0x00, 0xBE, 0x78, /* 0x24-0x27 */
+ 0xC0, 0x69, 0xF1, 0xBE, 0x00, 0x00, 0xF7, 0xA6, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0xF9, 0xC4, 0xD4, 0x4A, /* 0x2C-0x2F */
+ 0x00, 0x00, 0xC6, 0x7B, 0xB0, 0xAC, 0xEC, 0x72, /* 0x30-0x33 */
+ 0x00, 0x00, 0xF1, 0xBF, 0x00, 0x00, 0xF3, 0xC6, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0xF6, 0xA7, 0xF7, 0xA7, /* 0x38-0x3B */
+ 0xB0, 0xAD, 0x00, 0x00, 0xE4, 0xDD, 0xE4, 0xDE, /* 0x3C-0x3F */
+ 0x00, 0x00, 0xBB, 0xED, 0xBB, 0xEE, 0xE8, 0xD9, /* 0x40-0x43 */
+ 0xBE, 0x7A, 0xBE, 0x79, 0xE8, 0xD8, 0x00, 0x00, /* 0x44-0x47 */
+ 0xEF, 0x69, 0x00, 0x00, 0xF1, 0xC0, 0xF1, 0xC2, /* 0x48-0x4B */
+ 0xF1, 0xC1, 0xC3, 0x53, 0xC3, 0x52, 0xC3, 0x51, /* 0x4C-0x4F */
+ 0x00, 0x00, 0xC5, 0x5E, 0xF6, 0xA8, 0x00, 0x00, /* 0x50-0x53 */
+ 0xC5, 0x5D, 0xF7, 0xA9, 0xF7, 0xA8, 0x00, 0x00, /* 0x54-0x57 */
+ 0xC6, 0x4C, 0xF8, 0xD5, 0xB3, 0xBD, 0xE0, 0xEA, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0xE1, /* 0x5C-0x5F */
+ 0xE4, 0xDF, 0xE4, 0xE0, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0xE8, 0xE2, 0x00, 0x00, 0xE8, 0xDD, 0xE8, 0xDA, /* 0x64-0x67 */
+ 0xE8, 0xE1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0xE8, 0xE3, 0x00, 0x00, 0x00, 0x00, 0xBE, 0x7C, /* 0x6C-0x6F */
+ 0xE8, 0xE0, 0xE8, 0xDC, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0xE8, 0xDB, 0xE8, 0xDF, 0xE8, 0xDE, 0xBE, 0x7B, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0xEC, 0x7D, 0xEC, 0x78, /* 0x78-0x7B */
+ 0xEC, 0x76, 0xEC, 0xA1, 0xEC, 0x77, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0xEC, 0x73, 0x00, 0x00, 0xEC, 0x79, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0xEC, 0x74, 0xEF, 0x72, 0xEC, 0x75, /* 0x84-0x87 */
+ 0xEC, 0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0xEC, 0x7C, 0xC0, 0x6A, 0xEC, 0x7B, 0xEC, 0x7A, /* 0x90-0x93 */
+ 0x00, 0x00, 0xEC, 0x7E, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0xEF, 0x6A, 0xEF, 0x6D, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0xEF, 0x6C, 0x00, 0x00, /* 0x9C-0x9F */
+ 0xEF, 0x74, 0xEF, 0x6F, 0xEF, 0x73, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0xEF, 0x71, 0xEF, 0x70, 0xEF, 0x6E, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0xEF, 0x6B, 0x00, 0x00, 0xC2, 0x43, 0xC2, 0x42, /* 0xA8-0xAB */
+ 0x00, 0x00, 0xC2, 0x44, 0xC2, 0x41, 0xEF, 0x75, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0xF1, 0xC8, 0xF1, 0xCB, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0xF1, 0xC9, 0xF1, 0xCD, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0xF1, 0xCE, 0x00, 0x00, 0xF1, 0xC6, /* 0xBC-0xBF */
+ 0xC3, 0x58, 0xF1, 0xC7, 0x00, 0x00, 0xF1, 0xC5, /* 0xC0-0xC3 */
+ 0xF1, 0xCC, 0x00, 0x00, 0xF1, 0xC4, 0xF1, 0xC3, /* 0xC4-0xC7 */
+ 0xC3, 0x57, 0xC3, 0x55, 0xC3, 0x54, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF1, 0xCA, /* 0xD0-0xD3 */
+ 0xF3, 0xCF, 0xF3, 0xD5, 0xC4, 0x4A, 0xF3, 0xD0, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0xF3, 0xD3, 0xF3, 0xD7, 0xC4, 0x4B, /* 0xD8-0xDB */
+ 0xF3, 0xD2, 0x00, 0x00, 0xF3, 0xCA, 0x00, 0x00, /* 0xDC-0xDF */
+ 0xF3, 0xC9, 0xF3, 0xD6, 0xF3, 0xCD, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0xF3, 0xCB, 0xF3, 0xD4, 0xF3, 0xCC, 0xC4, 0x49, /* 0xE4-0xE7 */
+ 0xC4, 0x48, 0x00, 0x00, 0xF3, 0xC7, 0xF3, 0xC8, /* 0xE8-0xEB */
+ 0xF3, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0xF3, 0xCE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF0-0xF3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF5, 0x6C, /* 0xF4-0xF7 */
+ 0xF5, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xF8-0xFB */
+ 0x00, 0x00, 0xC3, 0x56, 0x00, 0x00, 0x00, 0x00, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_9C[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0xF5, 0x6D, 0xF5, 0x73, 0xF5, 0x71, /* 0x04-0x07 */
+ 0xF5, 0x6B, 0xF5, 0x76, 0x00, 0x00, 0xF5, 0x6A, /* 0x08-0x0B */
+ 0x00, 0x00, 0xC4, 0xCF, 0xF5, 0x72, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0xF5, 0x6E, 0xC4, 0xCE, /* 0x10-0x13 */
+ 0xF5, 0x75, 0x00, 0x00, 0x00, 0x00, 0xF5, 0x74, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0xF6, 0xAB, 0xF6, 0xAA, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0xF6, 0xB1, 0x00, 0x00, 0xF6, 0xAD, /* 0x20-0x23 */
+ 0xF6, 0xB0, 0xC5, 0x60, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0xF6, 0xAE, 0xF6, 0xAF, 0x00, 0x00, 0xF6, 0xA9, /* 0x28-0x2B */
+ 0xF6, 0xAC, 0xC5, 0x5F, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0xC5, 0xBF, 0xF7, 0xB4, 0xF7, 0xAF, /* 0x30-0x33 */
+ 0xF7, 0xB3, 0x00, 0x00, 0xF7, 0xB6, 0xF7, 0xB2, /* 0x34-0x37 */
+ 0x00, 0x00, 0xF7, 0xAE, 0x00, 0x00, 0xC5, 0xC1, /* 0x38-0x3B */
+ 0xF7, 0xB1, 0xF7, 0xB5, 0xC5, 0xC0, 0xF7, 0xAC, /* 0x3C-0x3F */
+ 0xF5, 0x70, 0xF7, 0xB0, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0xF7, 0xAD, 0x00, 0x00, 0xF7, 0xAA, 0x00, 0x00, /* 0x44-0x47 */
+ 0xF7, 0xAB, 0xC5, 0xBE, 0xF8, 0x5A, 0xF8, 0x5C, /* 0x48-0x4B */
+ 0xF8, 0x5F, 0xF8, 0x5B, 0xF8, 0x60, 0x00, 0x00, /* 0x4C-0x4F */
+ 0xF8, 0x59, 0x00, 0x00, 0xF8, 0x57, 0x00, 0x00, /* 0x50-0x53 */
+ 0xC5, 0xEB, 0xF8, 0x5D, 0xC5, 0xED, 0xC5, 0xEC, /* 0x54-0x57 */
+ 0xF8, 0x58, 0xF8, 0x5E, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0xF8, 0xDA, 0xC6, 0x4D, /* 0x5C-0x5F */
+ 0xF8, 0xDB, 0x00, 0x00, 0xF8, 0xD9, 0xF8, 0xD6, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0xF8, 0xD8, 0xF8, 0xD7, /* 0x64-0x67 */
+ 0xF9, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0xF9, 0x5C, 0xF9, 0x5B, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0xF9, 0x79, 0x00, 0x00, 0xF9, 0x78, /* 0x70-0x73 */
+ 0xF9, 0x77, 0xF9, 0x7A, 0x00, 0x00, 0xC6, 0x73, /* 0x74-0x77 */
+ 0xC6, 0x74, 0xF9, 0xCA, 0xF9, 0xCE, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0xB3, 0xBE, 0xDC, 0xAF, 0xE0, 0xED, /* 0xE4-0xE7 */
+ 0x00, 0x00, 0xB9, 0xA7, 0xE0, 0xEB, 0x00, 0x00, /* 0xE8-0xEB */
+ 0x00, 0x00, 0xE0, 0xEC, 0x00, 0x00, 0x00, 0x00, /* 0xEC-0xEF */
+ 0x00, 0x00, 0xE4, 0xE2, 0xE4, 0xE3, 0xBB, 0xF1, /* 0xF0-0xF3 */
+ 0xBB, 0xEF, 0xE4, 0xE4, 0xBB, 0xF0, 0xE8, 0xE8, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xE8, 0xEB, 0xE8, 0xE5, 0xE8, 0xEC, /* 0xF8-0xFB */
+ 0xE8, 0xE4, 0xE8, 0xE6, 0x00, 0x00, 0xE8, 0xE7, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_9D[512] = {
+ 0xE8, 0xEA, 0x00, 0x00, 0x00, 0x00, 0xBE, 0xA1, /* 0x00-0x03 */
+ 0xE8, 0xEF, 0xE8, 0xEE, 0xBE, 0x7D, 0xE8, 0xE9, /* 0x04-0x07 */
+ 0xE8, 0xED, 0xBE, 0x7E, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xEC, 0xAC, 0x00, 0x00, 0xC0, 0x6F, 0x00, 0x00, /* 0x10-0x13 */
+ 0xEC, 0xA7, 0xC0, 0x6B, 0x00, 0x00, 0xEC, 0xA4, /* 0x14-0x17 */
+ 0xEC, 0xAA, 0xEC, 0xAD, 0x00, 0x00, 0xC0, 0x70, /* 0x18-0x1B */
+ 0x00, 0x00, 0xEC, 0xA9, 0xEC, 0xA6, 0xEC, 0xAE, /* 0x1C-0x1F */
+ 0xEC, 0xA5, 0x00, 0x00, 0xEC, 0xAB, 0xC0, 0x6C, /* 0x20-0x23 */
+ 0x00, 0x00, 0xEC, 0xA3, 0xC0, 0x6D, 0x00, 0x00, /* 0x24-0x27 */
+ 0xC0, 0x6E, 0xEC, 0xA8, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0xEF, 0xA9, 0xEF, 0x7A, 0xEF, 0x7B, /* 0x2C-0x2F */
+ 0xEF, 0x7E, 0xEF, 0x7C, 0x00, 0x00, 0xEF, 0x76, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0xEF, 0x79, 0xEF, 0xA5, /* 0x34-0x37 */
+ 0xEF, 0x7D, 0x00, 0x00, 0x00, 0x00, 0xC2, 0x45, /* 0x38-0x3B */
+ 0x00, 0x00, 0xEF, 0xA7, 0xEF, 0xA4, 0xC2, 0x46, /* 0x3C-0x3F */
+ 0xEF, 0xA6, 0xEF, 0x77, 0xEF, 0xA2, 0xEF, 0xA3, /* 0x40-0x43 */
+ 0x00, 0x00, 0xEF, 0xA1, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0xF1, 0xD2, 0xF1, 0xD4, /* 0x48-0x4B */
+ 0xF1, 0xD7, 0x00, 0x00, 0x00, 0x00, 0xF1, 0xD1, /* 0x4C-0x4F */
+ 0x00, 0x00, 0xC3, 0x59, 0xF1, 0xD9, 0xF1, 0xD0, /* 0x50-0x53 */
+ 0xF1, 0xDA, 0x00, 0x00, 0xF1, 0xD6, 0xF1, 0xD8, /* 0x54-0x57 */
+ 0xF1, 0xDC, 0xF1, 0xD5, 0xF1, 0xDD, 0xF1, 0xD3, /* 0x58-0x5B */
+ 0xF1, 0xCF, 0xC3, 0x5A, 0x00, 0x00, 0xF1, 0xDB, /* 0x5C-0x5F */
+ 0xC3, 0x5B, 0xC4, 0x4D, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEF, 0x78, /* 0x64-0x67 */
+ 0xF3, 0xF1, 0xF3, 0xE8, 0xC4, 0x4F, 0xF3, 0xE4, /* 0x68-0x6B */
+ 0xC4, 0x50, 0x00, 0x00, 0x00, 0x00, 0xF3, 0xED, /* 0x6C-0x6F */
+ 0xF3, 0xE7, 0xF3, 0xDD, 0xC4, 0x4E, 0xF3, 0xEA, /* 0x70-0x73 */
+ 0xF3, 0xE5, 0xF3, 0xE6, 0x00, 0x00, 0xF3, 0xD8, /* 0x74-0x77 */
+ 0xF3, 0xDF, 0xF3, 0xEE, 0x00, 0x00, 0xF3, 0xEB, /* 0x78-0x7B */
+ 0x00, 0x00, 0xF3, 0xE3, 0x00, 0x00, 0xF3, 0xEF, /* 0x7C-0x7F */
+
+ 0xF3, 0xDE, 0xF3, 0xD9, 0xF3, 0xEC, 0x00, 0x00, /* 0x80-0x83 */
+ 0xF3, 0xDB, 0xF3, 0xE9, 0xF3, 0xE0, 0xF3, 0xF0, /* 0x84-0x87 */
+ 0xF3, 0xDC, 0xC4, 0x4C, 0xF3, 0xDA, 0xF3, 0xE1, /* 0x88-0x8B */
+ 0xF3, 0xE2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0xF5, 0x7D, 0x00, 0x00, 0xF5, 0x7B, 0x00, 0x00, /* 0x90-0x93 */
+ 0xF5, 0xA2, 0x00, 0x00, 0xF5, 0xAE, 0xF5, 0xA5, /* 0x94-0x97 */
+ 0xF5, 0x7C, 0xF5, 0x78, 0xF5, 0xA7, 0xF5, 0x7E, /* 0x98-0x9B */
+ 0xF5, 0xA3, 0xF5, 0x7A, 0xF5, 0xAA, 0xF5, 0x77, /* 0x9C-0x9F */
+ 0xF5, 0xA1, 0xF5, 0xA6, 0xF5, 0xA8, 0xF5, 0xAB, /* 0xA0-0xA3 */
+ 0xF5, 0x79, 0x00, 0x00, 0xF5, 0xAF, 0xF5, 0xB0, /* 0xA4-0xA7 */
+ 0xF5, 0xA9, 0xF5, 0xAD, 0xF5, 0xA4, 0x00, 0x00, /* 0xA8-0xAB */
+ 0xF6, 0xC1, 0xF6, 0xC4, 0x00, 0x00, 0xC5, 0x61, /* 0xAC-0xAF */
+ 0x00, 0x00, 0xF6, 0xC3, 0xF6, 0xC8, 0xF6, 0xC6, /* 0xB0-0xB3 */
+ 0xC5, 0x62, 0xF6, 0xBD, 0xF6, 0xB3, 0xF6, 0xB2, /* 0xB4-0xB7 */
+ 0xC5, 0x64, 0xF6, 0xBF, 0xF6, 0xC0, 0xF6, 0xBC, /* 0xB8-0xBB */
+ 0xF6, 0xB4, 0x00, 0x00, 0xF6, 0xB9, 0xF5, 0xAC, /* 0xBC-0xBF */
+ 0x00, 0x00, 0xF6, 0xB5, 0xC5, 0x63, 0xF6, 0xBB, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0xF6, 0xBA, 0x00, 0x00, 0xF6, 0xB6, /* 0xC4-0xC7 */
+ 0xF6, 0xC2, 0x00, 0x00, 0xF6, 0xB7, 0xF7, 0xBB, /* 0xC8-0xCB */
+ 0xF6, 0xC5, 0xF6, 0xC7, 0xF6, 0xBE, 0xF6, 0xB8, /* 0xCC-0xCF */
+ 0xF7, 0xBC, 0xF7, 0xBE, 0xF7, 0xB8, 0xC5, 0xC2, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0xF7, 0xC5, 0xF7, 0xC3, 0xC5, 0xC3, /* 0xD4-0xD7 */
+ 0xF7, 0xC2, 0xF7, 0xC1, 0xF7, 0xBA, 0xF7, 0xB7, /* 0xD8-0xDB */
+ 0xF7, 0xBD, 0xF7, 0xC6, 0xF7, 0xB9, 0xF7, 0xBF, /* 0xDC-0xDF */
+ 0x00, 0x00, 0xF8, 0x69, 0xF8, 0x6E, 0xF8, 0x64, /* 0xE0-0xE3 */
+ 0xF8, 0x67, 0xC5, 0xEE, 0xF8, 0x6B, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xF8, 0x72, 0xF7, 0xC0, 0x00, 0x00, 0xF8, 0x65, /* 0xE8-0xEB */
+ 0xF8, 0x6F, 0xF8, 0x73, 0xF8, 0x6A, 0xF8, 0x63, /* 0xEC-0xEF */
+ 0xF8, 0x6D, 0x00, 0x00, 0xF8, 0x6C, 0xF8, 0x71, /* 0xF0-0xF3 */
+ 0xF8, 0x70, 0xF7, 0xC4, 0xF8, 0x68, 0xF8, 0x62, /* 0xF4-0xF7 */
+ 0xF8, 0x66, 0xC6, 0x4E, 0xC6, 0x4F, 0xF8, 0x61, /* 0xF8-0xFB */
+ 0x00, 0x00, 0xF8, 0xE6, 0xF8, 0xDD, 0xF8, 0xE5, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_9E[512] = {
+ 0xF8, 0xE2, 0xF8, 0xE3, 0xF8, 0xDC, 0xF8, 0xDF, /* 0x00-0x03 */
+ 0xF8, 0xE7, 0xF8, 0xE1, 0xF8, 0xE0, 0xF8, 0xDE, /* 0x04-0x07 */
+ 0x00, 0x00, 0xF8, 0xE4, 0x00, 0x00, 0xF9, 0x5D, /* 0x08-0x0B */
+ 0x00, 0x00, 0xF9, 0x5E, 0x00, 0x00, 0xF9, 0x60, /* 0x0C-0x0F */
+ 0xF9, 0x5F, 0xF9, 0x62, 0xF9, 0x61, 0xF9, 0x7C, /* 0x10-0x13 */
+ 0xF9, 0x7B, 0xF9, 0xB7, 0x00, 0x00, 0xF9, 0xB8, /* 0x14-0x17 */
+ 0x00, 0x00, 0xF9, 0xC5, 0xC6, 0x78, 0xC6, 0x7C, /* 0x18-0x1B */
+ 0x00, 0x00, 0xF9, 0xCF, 0xC6, 0x7D, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x33 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x34-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x3C-0x3F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x53 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x54-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0xB3, 0xBF, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0xC4, 0xD0, 0xF6, 0xC9, 0x00, 0x00, /* 0x78-0x7B */
+ 0xC6, 0x50, 0xC6, 0x51, 0x00, 0x00, 0xB3, 0xC0, /* 0x7C-0x7F */
+
+ 0xE0, 0xEE, 0x00, 0x00, 0xB9, 0xA8, 0xE8, 0xF0, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0xEC, 0xB0, 0xEC, 0xB1, /* 0x84-0x87 */
+ 0xEC, 0xAF, 0xEF, 0xAB, 0xEF, 0xAA, 0xC2, 0x47, /* 0x88-0x8B */
+ 0xF1, 0xDF, 0xEF, 0xAC, 0xF1, 0xDE, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0xF3, 0xF3, 0xC4, 0x51, 0xC4, 0x53, /* 0x90-0x93 */
+ 0xF3, 0xF2, 0x00, 0x00, 0x00, 0x00, 0xC4, 0x52, /* 0x94-0x97 */
+ 0x00, 0x00, 0xF5, 0xB1, 0xF5, 0xB3, 0xF5, 0xB2, /* 0x98-0x9B */
+ 0xF6, 0xCA, 0xC5, 0x65, 0x00, 0x00, 0xC5, 0xEF, /* 0x9C-0x9F */
+ 0xF8, 0xE8, 0xF9, 0x63, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0xF9, 0xD2, 0xB3, 0xC1, 0x00, 0x00, 0xE4, 0xE5, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0xBE, 0xA2, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0xEC, 0xB3, 0xEC, 0xB2, 0x00, 0x00, /* 0xAC-0xAF */
+ 0xEF, 0xAD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0xC4, 0x54, 0xC4, 0xD1, 0xF7, 0xC7, 0xF9, 0xCB, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB3, 0xC2, /* 0xB8-0xBB */
+ 0xBB, 0xF2, 0x00, 0x00, 0xBE, 0xA3, 0x00, 0x00, /* 0xBC-0xBF */
+ 0xF3, 0xF4, 0x00, 0x00, 0xF8, 0x74, 0xB6, 0xC0, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0xEF, 0xAE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0xC6, 0x64, 0xB6, 0xC1, 0xBE, 0xA4, 0xC2, 0x48, /* 0xCC-0xCF */
+ 0xF8, 0x75, 0xB6, 0xC2, 0x00, 0x00, 0xE8, 0xF1, /* 0xD0-0xD3 */
+ 0xC0, 0x72, 0xEC, 0xB4, 0xEC, 0xB5, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0xC0, 0x71, 0x00, 0x00, 0xEF, 0xAF, 0xC2, 0x4C, /* 0xD8-0xDB */
+ 0xC2, 0x4A, 0xC2, 0x4B, 0xC2, 0x49, 0xF1, 0xE0, /* 0xDC-0xDF */
+ 0xC3, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xE0-0xE3 */
+ 0xF5, 0xB5, 0xF5, 0xB4, 0xF5, 0xB7, 0xF5, 0xB6, /* 0xE4-0xE7 */
+ 0xC4, 0xD2, 0x00, 0x00, 0x00, 0x00, 0xF6, 0xCB, /* 0xE8-0xEB */
+ 0x00, 0x00, 0xF6, 0xCD, 0xF6, 0xCC, 0xC5, 0x66, /* 0xEC-0xEF */
+ 0xF7, 0xC8, 0x00, 0x00, 0xF8, 0x76, 0xF8, 0x77, /* 0xF0-0xF3 */
+ 0xC5, 0xF0, 0xF9, 0x64, 0xF9, 0x7D, 0xC6, 0x75, /* 0xF4-0xF7 */
+ 0x00, 0x00, 0xDC, 0xB0, 0xEC, 0xB6, 0xEF, 0xB0, /* 0xF8-0xFB */
+ 0xF3, 0xF5, 0xE0, 0xEF, 0x00, 0x00, 0xEF, 0xB1, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_9F[512] = {
+ 0xF1, 0xE2, 0xF1, 0xE1, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0xF8, 0x78, 0xC6, 0x52, /* 0x04-0x07 */
+ 0x00, 0x00, 0xF9, 0x65, 0xF9, 0x7E, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0xB9, 0xA9, 0xE8, 0xF2, /* 0x0C-0x0F */
+ 0xE8, 0xF3, 0x00, 0x00, 0xEC, 0xB7, 0xB9, 0xAA, /* 0x10-0x13 */
+ 0x00, 0x00, 0xC3, 0x5D, 0xF1, 0xE3, 0x00, 0x00, /* 0x14-0x17 */
+ 0xF6, 0xCF, 0xC5, 0x67, 0xF6, 0xD0, 0xF6, 0xCE, /* 0x18-0x1B */
+ 0xF8, 0x79, 0x00, 0x00, 0xF8, 0xE9, 0x00, 0x00, /* 0x1C-0x1F */
+ 0xB9, 0xAB, 0x00, 0x00, 0xEF, 0xB4, 0xEF, 0xB3, /* 0x20-0x23 */
+ 0xEF, 0xB2, 0xF1, 0xE4, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0xF1, 0xE8, 0xF1, 0xE7, 0xF1, 0xE6, 0xF1, 0xE5, /* 0x28-0x2B */
+ 0xC3, 0x5E, 0xF3, 0xF6, 0xF5, 0xB9, 0xC4, 0xD3, /* 0x2C-0x2F */
+ 0xF5, 0xB8, 0xF6, 0xD1, 0xF7, 0xCB, 0xF7, 0xCA, /* 0x30-0x33 */
+ 0xC5, 0xC4, 0xF7, 0xC9, 0xF8, 0x7C, 0xF8, 0x7B, /* 0x34-0x37 */
+ 0xF8, 0x7A, 0x00, 0x00, 0x00, 0x00, 0xBB, 0xF3, /* 0x38-0x3B */
+ 0x00, 0x00, 0xEC, 0xB8, 0xC2, 0x4D, 0x00, 0x00, /* 0x3C-0x3F */
+ 0xF3, 0xF7, 0xF3, 0xF8, 0xF7, 0xCC, 0xF8, 0x7D, /* 0x40-0x43 */
+ 0x00, 0x00, 0x00, 0x00, 0xF8, 0xEA, 0xF9, 0x66, /* 0x44-0x47 */
+ 0xF9, 0xB9, 0xF9, 0xD4, 0xBB, 0xF4, 0xC2, 0x4E, /* 0x48-0x4B */
+ 0xF1, 0xE9, 0xF3, 0xF9, 0xF6, 0xD2, 0xF8, 0x7E, /* 0x4C-0x4F */
+ 0x00, 0x00, 0x00, 0x00, 0xBE, 0xA6, 0x00, 0x00, /* 0x50-0x53 */
+ 0xEF, 0xB5, 0xF1, 0xEA, 0xF3, 0xFA, 0xF3, 0xFB, /* 0x54-0x57 */
+ 0xF3, 0xFC, 0xF5, 0xBE, 0x00, 0x00, 0xF5, 0xBA, /* 0x58-0x5B */
+ 0xC5, 0x68, 0xF5, 0xBD, 0xF5, 0xBC, 0xC4, 0xD4, /* 0x5C-0x5F */
+ 0xF5, 0xBB, 0xC4, 0xD6, 0x00, 0x00, 0xC4, 0xD5, /* 0x60-0x63 */
+ 0xF6, 0xD4, 0xF6, 0xD3, 0xC5, 0x69, 0xC5, 0x6A, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0xC5, 0xC6, 0xF7, 0xCD, /* 0x68-0x6B */
+ 0xC5, 0xC5, 0x00, 0x00, 0xF8, 0xA3, 0xF8, 0xA4, /* 0x6C-0x6F */
+ 0xF8, 0xA2, 0xF8, 0xA1, 0xC6, 0x54, 0x00, 0x00, /* 0x70-0x73 */
+ 0xF8, 0xEB, 0xF8, 0xEC, 0xF8, 0xED, 0xC6, 0x53, /* 0x74-0x77 */
+ 0xF9, 0x67, 0xF9, 0x6A, 0xF9, 0x69, 0xF9, 0x68, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0xF9, 0xD3, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0xC0, 0x73, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0xC3, 0x65, 0xF5, 0xBF, 0xF6, 0xD5, 0x00, 0x00, /* 0x90-0x93 */
+ 0xC5, 0xC7, 0xF7, 0xCE, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0xF9, 0xD5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0xC0, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0xEF, 0xB6, 0x00, 0x00, 0xF7, 0xCF, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0xF9, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+};
+
+static unsigned char u2c_DC[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+};
+
+static unsigned char u2c_F9[512] = {
+ 0xB0, 0x5A, 0xA7, 0xF3, 0xA8, 0xAE, 0xB8, 0xEB, /* 0x00-0x03 */
+ 0xB7, 0xC6, 0xA6, 0xEA, 0xA5, 0x79, 0xC0, 0x74, /* 0x04-0x07 */
+ 0xC0, 0x74, 0xAB, 0xB4, 0xAA, 0xF7, 0xB3, 0xE2, /* 0x08-0x0B */
+ 0xA9, 0x60, 0xC3, 0x69, 0xC4, 0xEE, 0xC3, 0xB9, /* 0x0C-0x0F */
+ 0xC5, 0xDA, 0xC1, 0xB3, 0xBB, 0x72, 0xC5, 0xDE, /* 0x10-0x13 */
+ 0xBC, 0xD6, 0xAC, 0xA5, 0xAF, 0x4F, 0xAF, 0x5F, /* 0x14-0x17 */
+ 0xB8, 0xA8, 0xB9, 0x54, 0xC0, 0x64, 0xB6, 0xC3, /* 0x18-0x1B */
+ 0xA7, 0x5A, 0xC4, 0xE6, 0xC4, 0xEA, 0xC4, 0xF5, /* 0x1C-0x1F */
+ 0xC6, 0x7D, 0xB4, 0x50, 0xC0, 0xDD, 0xC2, 0xC5, /* 0x20-0x23 */
+ 0xC4, 0xB0, 0xA9, 0xD4, 0xC3, 0xBE, 0xC4, 0xFA, /* 0x24-0x27 */
+ 0xB4, 0x59, 0xAE, 0xD4, 0xAE, 0xF6, 0xAF, 0x54, /* 0x28-0x2B */
+ 0x00, 0x00, 0xA8, 0xD3, 0xA7, 0x4E, 0xB3, 0xD2, /* 0x2C-0x2F */
+ 0xBE, 0xDB, 0xC3, 0x72, 0xC4, 0x6C, 0xBF, 0x63, /* 0x30-0x33 */
+ 0xA6, 0xD1, 0xC4, 0xAA, 0xB8, 0xB8, 0xB8, 0xF4, /* 0x34-0x37 */
+ 0xC5, 0x53, 0xBE, 0x7C, 0xC6, 0x4F, 0xB8, 0x4C, /* 0x38-0x3B */
+ 0xB8, 0x53, 0xBA, 0xF1, 0xDB, 0x77, 0xBF, 0xFD, /* 0x3C-0x3F */
+ 0xB3, 0xC0, 0xBD, 0xD7, 0xC3, 0x62, 0xA7, 0xCB, /* 0x40-0x43 */
+ 0xC5, 0xA2, 0xC5, 0xA4, 0xA8, 0x63, 0xBD, 0x55, /* 0x44-0x47 */
+ 0xB8, 0xEF, 0xB9, 0x70, 0xC2, 0x53, 0xB9, 0xF0, /* 0x48-0x4B */
+ 0xBC, 0xD3, 0xB2, 0x5C, 0xBA, 0x7C, 0xB2, 0xD6, /* 0x4C-0x4F */
+ 0xC1, 0x5C, 0xAD, 0xAE, 0xB0, 0xC7, 0xA6, 0xD8, /* 0x50-0x53 */
+ 0xBB, 0xFE, 0xAD, 0xE2, 0xB8, 0x57, 0xBA, 0xF0, /* 0x54-0x57 */
+ 0xB5, 0xD9, 0xB3, 0xAE, 0xC5, 0xAA, 0xCE, 0xD4, /* 0x58-0x5B */
+ 0xBC, 0xD6, 0xBF, 0xD5, 0xA4, 0xA6, 0xB9, 0xE7, /* 0x5C-0x5F */
+ 0xAB, 0xE3, 0xB2, 0x76, 0xB2, 0xA7, 0xA5, 0x5F, /* 0x60-0x63 */
+ 0xED, 0xA8, 0xAB, 0x4B, 0xB4, 0x5F, 0xA4, 0xA3, /* 0x64-0x67 */
+ 0xAA, 0x63, 0xBC, 0xC6, 0xAF, 0xC1, 0xB0, 0xD1, /* 0x68-0x6B */
+ 0xB6, 0xEB, 0xAC, 0xD9, 0xB8, 0xAD, 0xBB, 0xA1, /* 0x6C-0x6F */
+ 0xB1, 0xFE, 0xA8, 0xB0, 0xA8, 0x48, 0xAC, 0x42, /* 0x70-0x73 */
+ 0xAD, 0x59, 0xB1, 0xB0, 0xB2, 0xA4, 0xAB, 0x47, /* 0x74-0x77 */
+ 0xA8, 0xE2, 0x00, 0x00, 0xB1, 0xE7, 0xC2, 0xB3, /* 0x78-0x7B */
+ 0xA8, 0x7D, 0xBD, 0xCC, 0xB6, 0x71, 0xC0, 0x79, /* 0x7C-0x7F */
+
+ 0xA7, 0x66, 0xA4, 0x6B, 0xC3, 0x66, 0xAE, 0xC8, /* 0x80-0x83 */
+ 0xC2, 0x6F, 0xC4, 0x72, 0xBE, 0x5B, 0xC6, 0x7A, /* 0x84-0x87 */
+ 0xC4, 0x52, 0xBE, 0xA4, 0xA4, 0x4F, 0xBE, 0xE4, /* 0x88-0x8B */
+ 0xBE, 0xFA, 0xF7, 0x65, 0xA6, 0x7E, 0xBC, 0xA6, /* 0x8C-0x8F */
+ 0xC5, 0xCA, 0xBC, 0xBF, 0xBA, 0xA7, 0xB7, 0xD2, /* 0x90-0x93 */
+ 0xE6, 0xA3, 0x00, 0x00, 0xBD, 0x6D, 0xC1, 0x70, /* 0x94-0x97 */
+ 0xBD, 0xFB, 0xBD, 0xAC, 0xB3, 0x73, 0xC1, 0xE5, /* 0x98-0x9B */
+ 0xA6, 0x43, 0xA6, 0x48, 0xAB, 0x7C, 0xAF, 0x50, /* 0x9C-0x9F */
+ 0xB5, 0xF5, 0xBB, 0xA1, 0xB7, 0x47, 0xA9, 0xC0, /* 0xA0-0xA3 */
+ 0xB1, 0xC9, 0xC0, 0xD4, 0xC3, 0xAE, 0xC2, 0x79, /* 0xA4-0xA7 */
+ 0xA5, 0x4F, 0xCB, 0xF1, 0xB9, 0xE7, 0xC0, 0xAD, /* 0xA8-0xAB */
+ 0xCC, 0xB0, 0xAC, 0xC2, 0xBC, 0xFC, 0xB2, 0xDC, /* 0xAC-0xAF */
+ 0xB2, 0xE2, 0xB9, 0x61, 0xB9, 0x73, 0xC6, 0x46, /* 0xB0-0xB3 */
+ 0xBB, 0xE2, 0xA8, 0xD2, 0xC2, 0xA7, 0xC4, 0xBF, /* 0xB4-0xB7 */
+ 0xC1, 0xF5, 0xB4, 0x63, 0xA4, 0x46, 0xB9, 0xB1, /* 0xB8-0xBB */
+ 0xBC, 0x64, 0xA7, 0xBF, 0xAE, 0xC6, 0xBC, 0xD6, /* 0xBC-0xBF */
+ 0xBF, 0x52, 0xC0, 0xF8, 0xE7, 0x64, 0xBF, 0xF1, /* 0xC0-0xC3 */
+ 0xC0, 0x73, 0xB7, 0x77, 0xA8, 0xBF, 0xBC, 0x42, /* 0xC4-0xC7 */
+ 0xCC, 0xD8, 0xAC, 0x68, 0xAC, 0x79, 0xB7, 0xC8, /* 0xC8-0xCB */
+ 0xAF, 0x5B, 0xAF, 0x64, 0xB2, 0xB8, 0xAF, 0xC3, /* 0xCC-0xCF */
+ 0xC3, 0xFE, 0xA4, 0xBB, 0xBC, 0xAE, 0xB3, 0xB0, /* 0xD0-0xD3 */
+ 0xAD, 0xDB, 0xB1, 0x5B, 0xB2, 0x5F, 0xBD, 0xFC, /* 0xD4-0xD7 */
+ 0xAB, 0xDF, 0xB7, 0x58, 0xAE, 0xDF, 0xB2, 0x76, /* 0xD8-0xDB */
+ 0xB6, 0xA9, 0xA7, 0x51, 0xA6, 0x4F, 0xBC, 0x69, /* 0xDC-0xDF */
+ 0xA9, 0xF6, 0xA7, 0xF5, 0xB1, 0xF9, 0xAA, 0x64, /* 0xE0-0xE3 */
+ 0xB2, 0x7A, 0xB5, 0x67, 0xBF, 0xA9, 0x00, 0x00, /* 0xE4-0xE7 */
+ 0xB8, 0xCC, 0xA8, 0xBD, 0xC2, 0xF7, 0xB0, 0xCE, /* 0xE8-0xEB */
+ 0xB7, 0xC4, 0xA7, 0x5B, 0xBF, 0x4D, 0xBF, 0x5A, /* 0xEC-0xEF */
+ 0xC4, 0xA9, 0x00, 0x00, 0xC5, 0xEC, 0xC5, 0xEF, /* 0xF0-0xF3 */
+ 0xAA, 0x4C, 0xB2, 0x4F, 0xC1, 0x7B, 0xA5, 0xDF, /* 0xF4-0xF7 */
+ 0xB2, 0xC1, 0xB2, 0xC9, 0xAA, 0xAC, 0xAA, 0xA5, /* 0xF8-0xFB */
+ 0xC3, 0xD1, 0xA4, 0xB0, 0xAF, 0xF9, 0xA8, 0xEB, /* 0xFC-0xFF */
+};
+
+static unsigned char u2c_FA[512] = {
+ 0xA4, 0xC1, 0xAB, 0xD7, 0xA9, 0xDD, 0xBF, 0x7D, /* 0x00-0x03 */
+ 0xA6, 0x76, 0xAC, 0x7D, 0xBC, 0xC9, 0xBF, 0xE7, /* 0x04-0x07 */
+ 0xA6, 0xE6, 0xAD, 0xB0, 0xA8, 0xA3, 0xB9, 0xF8, /* 0x08-0x0B */
+ 0xC9, 0x4A, 0xDD, 0xFC, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0xB6, 0xEF, 0x00, 0x00, 0xB4, 0xB8, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0xE8, 0xF9, 0xBD, 0xDE, 0xAF, 0x71, /* 0x14-0x17 */
+ 0x00, 0x00, 0xAF, 0xAB, 0xB2, 0xBB, 0xBA, 0xD6, /* 0x18-0x1B */
+ 0xB9, 0x74, 0xBA, 0xEB, 0xA6, 0xD0, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0xBD, 0xD1, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0xB6, 0x68, 0xB3, 0xA3, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0xB6, 0xBA, 0xB9, 0x7D, /* 0x28-0x2B */
+ 0xC0, 0x5D, 0xC5, 0x62, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+};
+
+static unsigned char u2c_FE[512] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x03 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x04-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0C-0x0F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x13 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x14-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x1C-0x1F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x23 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x24-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x2C-0x2F */
+ 0xA1, 0x4A, 0xA1, 0x57, 0x00, 0x00, 0xA1, 0x59, /* 0x30-0x33 */
+ 0xA1, 0x5B, 0xA1, 0x5F, 0xA1, 0x60, 0xA1, 0x63, /* 0x34-0x37 */
+ 0xA1, 0x64, 0xA1, 0x67, 0xA1, 0x68, 0xA1, 0x6B, /* 0x38-0x3B */
+ 0xA1, 0x6C, 0xA1, 0x6F, 0xA1, 0x70, 0xA1, 0x73, /* 0x3C-0x3F */
+ 0xA1, 0x74, 0xA1, 0x77, 0xA1, 0x78, 0xA1, 0x7B, /* 0x40-0x43 */
+ 0xA1, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x44-0x47 */
+ 0x00, 0x00, 0xA1, 0xC6, 0xA1, 0xC7, 0xA1, 0xCA, /* 0x48-0x4B */
+ 0xA1, 0xCB, 0xA1, 0xC8, 0xA1, 0xC9, 0xA1, 0x5C, /* 0x4C-0x4F */
+ 0xA1, 0x4D, 0xA1, 0x4E, 0xA1, 0x4F, 0x00, 0x00, /* 0x50-0x53 */
+ 0xA1, 0x51, 0xA1, 0x52, 0xA1, 0x53, 0xA1, 0x54, /* 0x54-0x57 */
+ 0x00, 0x00, 0xA1, 0x7D, 0xA1, 0x7E, 0xA1, 0xA1, /* 0x58-0x5B */
+ 0xA1, 0xA2, 0xA1, 0xA3, 0xA1, 0xA4, 0xA1, 0xCC, /* 0x5C-0x5F */
+ 0xA1, 0xCD, 0xA1, 0xCE, 0xA1, 0xDE, 0xA1, 0xDF, /* 0x60-0x63 */
+ 0xA1, 0xE0, 0xA1, 0xE1, 0xA1, 0xE2, 0x00, 0x00, /* 0x64-0x67 */
+ 0xA2, 0x42, 0xA2, 0x4C, 0xA2, 0x4D, 0xA2, 0x4E, /* 0x68-0x6B */
+};
+
+static unsigned char u2c_FF[512] = {
+ 0x00, 0x00, 0xA1, 0x49, 0xA1, 0xA8, 0xA1, 0xAD, /* 0x00-0x03 */
+ 0xA2, 0x43, 0xA2, 0x48, 0xA1, 0xAE, 0xA1, 0xA6, /* 0x04-0x07 */
+ 0xA1, 0x5D, 0xA1, 0x5E, 0xA1, 0xAF, 0xA1, 0xCF, /* 0x08-0x0B */
+ 0xA1, 0x41, 0xA1, 0xD0, 0xA1, 0x44, 0xA1, 0xFE, /* 0x0C-0x0F */
+ 0xA2, 0xAF, 0xA2, 0xB0, 0xA2, 0xB1, 0xA2, 0xB2, /* 0x10-0x13 */
+ 0xA2, 0xB3, 0xA2, 0xB4, 0xA2, 0xB5, 0xA2, 0xB6, /* 0x14-0x17 */
+ 0xA2, 0xB7, 0xA2, 0xB8, 0xA1, 0x47, 0xA1, 0x46, /* 0x18-0x1B */
+ 0xA1, 0xD5, 0xA1, 0xD7, 0xA1, 0xD6, 0xA1, 0x48, /* 0x1C-0x1F */
+ 0xA2, 0x49, 0xA2, 0xCF, 0xA2, 0xD0, 0xA2, 0xD1, /* 0x20-0x23 */
+ 0xA2, 0xD2, 0xA2, 0xD3, 0xA2, 0xD4, 0xA2, 0xD5, /* 0x24-0x27 */
+ 0xA2, 0xD6, 0xA2, 0xD7, 0xA2, 0xD8, 0xA2, 0xD9, /* 0x28-0x2B */
+ 0xA2, 0xDA, 0xA2, 0xDB, 0xA2, 0xDC, 0xA2, 0xDD, /* 0x2C-0x2F */
+ 0xA2, 0xDE, 0xA2, 0xDF, 0xA2, 0xE0, 0xA2, 0xE1, /* 0x30-0x33 */
+ 0xA2, 0xE2, 0xA2, 0xE3, 0xA2, 0xE4, 0xA2, 0xE5, /* 0x34-0x37 */
+ 0xA2, 0xE6, 0xA2, 0xE7, 0xA2, 0xE8, 0xA1, 0x65, /* 0x38-0x3B */
+ 0xA2, 0x40, 0xA1, 0x66, 0xA1, 0x73, 0xA1, 0xC4, /* 0x3C-0x3F */
+ 0xA1, 0xA5, 0xA2, 0xE9, 0xA2, 0xEA, 0xA2, 0xEB, /* 0x40-0x43 */
+ 0xA2, 0xEC, 0xA2, 0xED, 0xA2, 0xEE, 0xA2, 0xEF, /* 0x44-0x47 */
+ 0xA2, 0xF0, 0xA2, 0xF1, 0xA2, 0xF2, 0xA2, 0xF3, /* 0x48-0x4B */
+ 0xA2, 0xF4, 0xA2, 0xF5, 0xA2, 0xF6, 0xA2, 0xF7, /* 0x4C-0x4F */
+ 0xA2, 0xF8, 0xA2, 0xF9, 0xA2, 0xFA, 0xA2, 0xFB, /* 0x50-0x53 */
+ 0xA2, 0xFC, 0xA2, 0xFD, 0xA2, 0xFE, 0xA3, 0x40, /* 0x54-0x57 */
+ 0xA3, 0x41, 0xA3, 0x42, 0xA3, 0x43, 0xA1, 0x61, /* 0x58-0x5B */
+ 0xA1, 0x55, 0xA1, 0x62, 0xA1, 0xE3, 0x00, 0x00, /* 0x5C-0x5F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x63 */
+ 0xA1, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x64-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x6C-0x6F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x73 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x74-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x7C-0x7F */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x83 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x84-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x8C-0x8F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x93 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x94-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9B */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x9C-0x9F */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA0-0xA3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA4-0xA7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xA8-0xAB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xAC-0xAF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB0-0xB3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB4-0xB7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xB8-0xBB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xBC-0xBF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC0-0xC3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC4-0xC7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xC8-0xCB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xCC-0xCF */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD0-0xD3 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD4-0xD7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xD8-0xDB */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xDC-0xDF */
+ 0xA2, 0x46, 0xA2, 0x47, 0x00, 0x00, 0xA1, 0xC3, /* 0xE0-0xE3 */
+ 0x00, 0x00, 0xA2, 0x44, 0x00, 0x00, 0x00, 0x00, /* 0xE4-0xE7 */
+};
+
+static unsigned char *page_uni2charset[256] = {
+ NULL, NULL, u2c_02, u2c_03, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ u2c_20, u2c_21, u2c_22, u2c_23, NULL, u2c_25, u2c_26, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ u2c_30, u2c_31, u2c_32, u2c_33, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, u2c_4E, u2c_4F,
+ u2c_50, u2c_51, u2c_52, u2c_53, u2c_54, u2c_55, u2c_56, u2c_57,
+ u2c_58, u2c_59, u2c_5A, u2c_5B, u2c_5C, u2c_5D, u2c_5E, u2c_5F,
+ u2c_60, u2c_61, u2c_62, u2c_63, u2c_64, u2c_65, u2c_66, u2c_67,
+ u2c_68, u2c_69, u2c_6A, u2c_6B, u2c_6C, u2c_6D, u2c_6E, u2c_6F,
+ u2c_70, u2c_71, u2c_72, u2c_73, u2c_74, u2c_75, u2c_76, u2c_77,
+ u2c_78, u2c_79, u2c_7A, u2c_7B, u2c_7C, u2c_7D, u2c_7E, u2c_7F,
+ u2c_80, u2c_81, u2c_82, u2c_83, u2c_84, u2c_85, u2c_86, u2c_87,
+ u2c_88, u2c_89, u2c_8A, u2c_8B, u2c_8C, u2c_8D, u2c_8E, u2c_8F,
+ u2c_90, u2c_91, u2c_92, u2c_93, u2c_94, u2c_95, u2c_96, u2c_97,
+ u2c_98, u2c_99, u2c_9A, u2c_9B, u2c_9C, u2c_9D, u2c_9E, u2c_9F,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, u2c_DC, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, u2c_F9, u2c_FA, NULL, NULL, NULL, u2c_FE, u2c_FF, };
+
+static unsigned char charset2lower[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40-0x47 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x48-0x4f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50-0x57 */
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0x88-0x8f */
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static unsigned char charset2upper[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0x88-0x8f */
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static int uni2char(const wchar_t uni,
+ unsigned char *out, int boundlen)
+{
+ unsigned char *uni2charset;
+ unsigned char cl = uni&0xFF;
+ unsigned char ch = (uni>>8)&0xFF;
+ int n;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+
+ uni2charset = page_uni2charset[ch];
+ if (uni2charset) {
+ if (boundlen <= 1)
+ return -ENAMETOOLONG;
+ out[0] = uni2charset[cl*2];
+ out[1] = uni2charset[cl*2+1];
+ if (out[0] == 0x00 && out[1] == 0x00)
+ return -EINVAL;
+ n = 2;
+ } else if (ch==0 && cl) {
+ out[0] = cl;
+ n = 1;
+ }
+ else
+ return -EINVAL;
+
+ return n;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen,
+ wchar_t *uni)
+{
+ unsigned char ch, cl;
+ wchar_t *charset2uni;
+ int n;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ if (boundlen == 1) {
+ *uni = rawstring[0];
+ return 1;
+ }
+
+ ch = rawstring[0];
+ cl = rawstring[1];
+
+ charset2uni = page_charset2uni[ch];
+ if (charset2uni && cl) {
+ *uni = charset2uni[cl];
+ if (*uni == 0x0000)
+ return -EINVAL;
+ n = 2;
+ } else{
+ *uni = ch;
+ n = 1;
+ }
+ return n;
+}
+
+static struct nls_table table = {
+ "cp950",
+ uni2char,
+ char2uni,
+ charset2lower,
+ charset2upper,
+ THIS_MODULE,
+};
+
+static int __init init_nls_cp950(void)
+{
+ return register_nls(&table);
+}
+
+static void __exit exit_nls_cp950(void)
+{
+ unregister_nls(&table);
+}
+
+module_init(init_nls_cp950)
+module_exit(exit_nls_cp950)
+MODULE_LICENSE("Dual BSD/GPL");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ *
+---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/fs/nls/nls_euc-jp.c b/fs/nls/nls_euc-jp.c
new file mode 100644
index 00000000000000..ea4015677da05f
--- /dev/null
+++ b/fs/nls/nls_euc-jp.c
@@ -0,0 +1,602 @@
+/*
+ * linux/fs/nls_euc-jp.c
+ *
+ * Added `OSF/JVC Recommended Code Set Conversion Specification
+ * between Japanese EUC and Shift-JIS' support: <hirofumi@mail.parknet.co.jp>
+ * (http://www.opengroup.or.jp/jvc/cde/sjis-euc-e.html)
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static struct nls_table *p_nls;
+
+#define IS_SJIS_LOW_BYTE(l) ((0x40 <= (l)) && ((l) <= 0xFC) && ((l) != 0x7F))
+/* JIS X 0208 (include NEC spesial characters) */
+#define IS_SJIS_JISX0208(h, l) ((((0x81 <= (h)) && ((h) <= 0x9F)) \
+ || ((0xE0 <= (h)) && ((h) <= 0xEA))) \
+ && IS_SJIS_LOW_BYTE(l))
+#define IS_SJIS_JISX0201KANA(c) ((0xA1 <= (c)) && ((c) <= 0xDF))
+#define IS_SJIS_UDC_LOW(h, l) (((0xF0 <= (h)) && ((h) <= 0xF4)) \
+ && IS_SJIS_LOW_BYTE(l))
+#define IS_SJIS_UDC_HI(h, l) (((0xF5 <= (h)) && ((h) <= 0xF9)) \
+ && IS_SJIS_LOW_BYTE(l))
+#define IS_SJIS_IBM(h, l) (((0xFA <= (h)) && ((h) <= 0xFC)) \
+ && IS_SJIS_LOW_BYTE(l))
+#define IS_SJIS_NECIBM(h, l) (((0xED <= (h)) && ((h) <= 0xEE)) \
+ && IS_SJIS_LOW_BYTE(l))
+#define MAP_SJIS2EUC(sjis_hi, sjis_lo, sjis_p, euc_hi, euc_lo, euc_p) { \
+ if ((sjis_lo) >= 0x9F) { \
+ (euc_hi) = (sjis_hi) * 2 - (((sjis_p) * 2 - (euc_p)) - 1); \
+ (euc_lo) = (sjis_lo) + 2; \
+ } else { \
+ (euc_hi) = (sjis_hi) * 2 - ((sjis_p) * 2 - (euc_p)); \
+ (euc_lo) = (sjis_lo) + ((sjis_lo) >= 0x7F ? 0x60 : 0x61); \
+ } \
+} while(0)
+
+#define SS2 (0x8E) /* Single Shift 2 */
+#define SS3 (0x8F) /* Single Shift 3 */
+#define IS_EUC_BYTE(c) ((0xA1 <= (c)) && ((c) <= 0xFE))
+#define IS_EUC_JISX0208(h, l) (IS_EUC_BYTE(h) && IS_EUC_BYTE(l))
+#define IS_EUC_JISX0201KANA(h, l) (((h) == SS2) && (0xA1 <= (l) && (l) <= 0xDF))
+#define IS_EUC_UDC_LOW(h, l) (((0xF5 <= (h)) && ((h) <= 0xFE)) \
+ && IS_EUC_BYTE(l))
+#define IS_EUC_UDC_HI(h, l) IS_EUC_UDC_LOW(h, l) /* G3 block */
+#define MAP_EUC2SJIS(euc_hi, euc_lo, euc_p, sjis_hi, sjis_lo, sjis_p) { \
+ if ((euc_hi) & 1) { \
+ (sjis_hi) = (euc_hi) / 2 + ((sjis_p) - (euc_p) / 2); \
+ (sjis_lo) = (euc_lo) - ((euc_lo) >= 0xE0 ? 0x60 : 0x61); \
+ } else { \
+ (sjis_hi) = (euc_hi) / 2 + (((sjis_p) - (euc_p) / 2) - 1); \
+ (sjis_lo) = (euc_lo) - 2; \
+ } \
+} while(0)
+
+/* SJIS IBM extended characters to EUC map */
+static unsigned char sjisibm2euc_map[][2] = {
+ {0xF3, 0xF3}, {0xF3, 0xF4}, {0xF3, 0xF5}, {0xF3, 0xF6}, {0xF3, 0xF7},
+ {0xF3, 0xF8}, {0xF3, 0xF9}, {0xF3, 0xFA}, {0xF3, 0xFB}, {0xF3, 0xFC},
+ {0xF3, 0xFD}, {0xF3, 0xFE}, {0xF4, 0xA1}, {0xF4, 0xA2}, {0xF4, 0xA3},
+ {0xF4, 0xA4}, {0xF4, 0xA5}, {0xF4, 0xA6}, {0xF4, 0xA7}, {0xF4, 0xA8},
+ {0xA2, 0xCC}, {0xA2, 0xC3}, {0xF4, 0xA9}, {0xF4, 0xAA}, {0xF4, 0xAB},
+ {0xF4, 0xAC}, {0xF4, 0xAD}, {0xA2, 0xE8}, {0xD4, 0xE3}, {0xDC, 0xDF},
+ {0xE4, 0xE9}, {0xE3, 0xF8}, {0xD9, 0xA1}, {0xB1, 0xBB}, {0xF4, 0xAE},
+ {0xC2, 0xAD}, {0xC3, 0xFC}, {0xE4, 0xD0}, {0xC2, 0xBF}, {0xBC, 0xF4},
+ {0xB0, 0xA9}, {0xB0, 0xC8}, {0xF4, 0xAF}, {0xB0, 0xD2}, {0xB0, 0xD4},
+ {0xB0, 0xE3}, {0xB0, 0xEE}, {0xB1, 0xA7}, {0xB1, 0xA3}, {0xB1, 0xAC},
+ {0xB1, 0xA9}, {0xB1, 0xBE}, {0xB1, 0xDF}, {0xB1, 0xD8}, {0xB1, 0xC8},
+ {0xB1, 0xD7}, {0xB1, 0xE3}, {0xB1, 0xF4}, {0xB1, 0xE1}, {0xB2, 0xA3},
+ {0xF4, 0xB0}, {0xB2, 0xBB}, {0xB2, 0xE6}, {0x00, 0x00}, {0xB2, 0xED},
+ {0xB2, 0xF5}, {0xB2, 0xFC}, {0xF4, 0xB1}, {0xB3, 0xB5}, {0xB3, 0xD8},
+ {0xB3, 0xDB}, {0xB3, 0xE5}, {0xB3, 0xEE}, {0xB3, 0xFB}, {0xF4, 0xB2},
+ {0xF4, 0xB3}, {0xB4, 0xC0}, {0xB4, 0xC7}, {0xB4, 0xD0}, {0xB4, 0xDE},
+ {0xF4, 0xB4}, {0xB5, 0xAA}, {0xF4, 0xB5}, {0xB5, 0xAF}, {0xB5, 0xC4},
+ {0xB5, 0xE8}, {0xF4, 0xB6}, {0xB7, 0xC2}, {0xB7, 0xE4}, {0xB7, 0xE8},
+ {0xB7, 0xE7}, {0xF4, 0xB7}, {0xF4, 0xB8}, {0xF4, 0xB9}, {0xB8, 0xCE},
+ {0xB8, 0xE1}, {0xB8, 0xF5}, {0xB8, 0xF7}, {0xB8, 0xF8}, {0xB8, 0xFC},
+ {0xB9, 0xAF}, {0xB9, 0xB7}, {0xBA, 0xBE}, {0xBA, 0xDB}, {0xCD, 0xAA},
+ {0xBA, 0xE1}, {0xF4, 0xBA}, {0xBA, 0xEB}, {0xBB, 0xB3}, {0xBB, 0xB8},
+ {0xF4, 0xBB}, {0xBB, 0xCA}, {0xF4, 0xBC}, {0xF4, 0xBD}, {0xBB, 0xD0},
+ {0xBB, 0xDE}, {0xBB, 0xF4}, {0xBB, 0xF5}, {0xBB, 0xF9}, {0xBC, 0xE4},
+ {0xBC, 0xED}, {0xBC, 0xFE}, {0xF4, 0xBE}, {0xBD, 0xC2}, {0xBD, 0xE7},
+ {0xF4, 0xBF}, {0xBD, 0xF0}, {0xBE, 0xB0}, {0xBE, 0xAC}, {0xF4, 0xC0},
+ {0xBE, 0xB3}, {0xBE, 0xBD}, {0xBE, 0xCD}, {0xBE, 0xC9}, {0xBE, 0xE4},
+ {0xBF, 0xA8}, {0xBF, 0xC9}, {0xC0, 0xC4}, {0xC0, 0xE4}, {0xC0, 0xF4},
+ {0xC1, 0xA6}, {0xF4, 0xC1}, {0xC1, 0xF5}, {0xC1, 0xFC}, {0xF4, 0xC2},
+ {0xC1, 0xF8}, {0xC2, 0xAB}, {0xC2, 0xA1}, {0xC2, 0xA5}, {0xF4, 0xC3},
+ {0xC2, 0xB8}, {0xC2, 0xBA}, {0xF4, 0xC4}, {0xC2, 0xC4}, {0xC2, 0xD2},
+ {0xC2, 0xD7}, {0xC2, 0xDB}, {0xC2, 0xDE}, {0xC2, 0xED}, {0xC2, 0xF0},
+ {0xF4, 0xC5}, {0xC3, 0xA1}, {0xC3, 0xB5}, {0xC3, 0xC9}, {0xC3, 0xB9},
+ {0xF4, 0xC6}, {0xC3, 0xD8}, {0xC3, 0xFE}, {0xF4, 0xC7}, {0xC4, 0xCC},
+ {0xF4, 0xC8}, {0xC4, 0xD9}, {0xC4, 0xEA}, {0xC4, 0xFD}, {0xF4, 0xC9},
+ {0xC5, 0xA7}, {0xC5, 0xB5}, {0xC5, 0xB6}, {0xF4, 0xCA}, {0xC5, 0xD5},
+ {0xC6, 0xB8}, {0xC6, 0xD7}, {0xC6, 0xE0}, {0xC6, 0xEA}, {0xC6, 0xE3},
+ {0xC7, 0xA1}, {0xC7, 0xAB}, {0xC7, 0xC7}, {0xC7, 0xC3}, {0xC7, 0xCB},
+ {0xC7, 0xCF}, {0xC7, 0xD9}, {0xF4, 0xCB}, {0xF4, 0xCC}, {0xC7, 0xE6},
+ {0xC7, 0xEE}, {0xC7, 0xFC}, {0xC7, 0xEB}, {0xC7, 0xF0}, {0xC8, 0xB1},
+ {0xC8, 0xE5}, {0xC8, 0xF8}, {0xC9, 0xA6}, {0xC9, 0xAB}, {0xC9, 0xAD},
+ {0xF4, 0xCD}, {0xC9, 0xCA}, {0xC9, 0xD3}, {0xC9, 0xE9}, {0xC9, 0xE3},
+ {0xC9, 0xFC}, {0xC9, 0xF4}, {0xC9, 0xF5}, {0xF4, 0xCE}, {0xCA, 0xB3},
+ {0xCA, 0xBD}, {0xCA, 0xEF}, {0xCA, 0xF1}, {0xCB, 0xAE}, {0xF4, 0xCF},
+ {0xCB, 0xCA}, {0xCB, 0xE6}, {0xCB, 0xEA}, {0xCB, 0xF0}, {0xCB, 0xF4},
+ {0xCB, 0xEE}, {0xCC, 0xA5}, {0xCB, 0xF9}, {0xCC, 0xAB}, {0xCC, 0xAE},
+ {0xCC, 0xAD}, {0xCC, 0xB2}, {0xCC, 0xC2}, {0xCC, 0xD0}, {0xCC, 0xD9},
+ {0xF4, 0xD0}, {0xCD, 0xBB}, {0xF4, 0xD1}, {0xCE, 0xBB}, {0xF4, 0xD2},
+ {0xCE, 0xBA}, {0xCE, 0xC3}, {0xF4, 0xD3}, {0xCE, 0xF2}, {0xB3, 0xDD},
+ {0xCF, 0xD5}, {0xCF, 0xE2}, {0xCF, 0xE9}, {0xCF, 0xED}, {0xF4, 0xD4},
+ {0xF4, 0xD5}, {0xF4, 0xD6}, {0x00, 0x00}, {0xF4, 0xD7}, {0xD0, 0xE5},
+ {0xF4, 0xD8}, {0xD0, 0xE9}, {0xD1, 0xE8}, {0xF4, 0xD9}, {0xF4, 0xDA},
+ {0xD1, 0xEC}, {0xD2, 0xBB}, {0xF4, 0xDB}, {0xD3, 0xE1}, {0xD3, 0xE8},
+ {0xD4, 0xA7}, {0xF4, 0xDC}, {0xF4, 0xDD}, {0xD4, 0xD4}, {0xD4, 0xF2},
+ {0xD5, 0xAE}, {0xF4, 0xDE}, {0xD7, 0xDE}, {0xF4, 0xDF}, {0xD8, 0xA2},
+ {0xD8, 0xB7}, {0xD8, 0xC1}, {0xD8, 0xD1}, {0xD8, 0xF4}, {0xD9, 0xC6},
+ {0xD9, 0xC8}, {0xD9, 0xD1}, {0xF4, 0xE0}, {0xF4, 0xE1}, {0xF4, 0xE2},
+ {0xF4, 0xE3}, {0xF4, 0xE4}, {0xDC, 0xD3}, {0xDD, 0xC8}, {0xDD, 0xD4},
+ {0xDD, 0xEA}, {0xDD, 0xFA}, {0xDE, 0xA4}, {0xDE, 0xB0}, {0xF4, 0xE5},
+ {0xDE, 0xB5}, {0xDE, 0xCB}, {0xF4, 0xE6}, {0xDF, 0xB9}, {0xF4, 0xE7},
+ {0xDF, 0xC3}, {0xF4, 0xE8}, {0xF4, 0xE9}, {0xE0, 0xD9}, {0xF4, 0xEA},
+ {0xF4, 0xEB}, {0xE1, 0xE2}, {0xF4, 0xEC}, {0xF4, 0xED}, {0xF4, 0xEE},
+ {0xE2, 0xC7}, {0xE3, 0xA8}, {0xE3, 0xA6}, {0xE3, 0xA9}, {0xE3, 0xAF},
+ {0xE3, 0xB0}, {0xE3, 0xAA}, {0xE3, 0xAB}, {0xE3, 0xBC}, {0xE3, 0xC1},
+ {0xE3, 0xBF}, {0xE3, 0xD5}, {0xE3, 0xD8}, {0xE3, 0xD6}, {0xE3, 0xDF},
+ {0xE3, 0xE3}, {0xE3, 0xE1}, {0xE3, 0xD4}, {0xE3, 0xE9}, {0xE4, 0xA6},
+ {0xE3, 0xF1}, {0xE3, 0xF2}, {0xE4, 0xCB}, {0xE4, 0xC1}, {0xE4, 0xC3},
+ {0xE4, 0xBE}, {0xF4, 0xEF}, {0xE4, 0xC0}, {0xE4, 0xC7}, {0xE4, 0xBF},
+ {0xE4, 0xE0}, {0xE4, 0xDE}, {0xE4, 0xD1}, {0xF4, 0xF0}, {0xE4, 0xDC},
+ {0xE4, 0xD2}, {0xE4, 0xDB}, {0xE4, 0xD4}, {0xE4, 0xFA}, {0xE4, 0xEF},
+ {0xE5, 0xB3}, {0xE5, 0xBF}, {0xE5, 0xC9}, {0xE5, 0xD0}, {0xE5, 0xE2},
+ {0xE5, 0xEA}, {0xE5, 0xEB}, {0xF4, 0xF1}, {0xF4, 0xF2}, {0xF4, 0xF3},
+ {0xE6, 0xE8}, {0xE6, 0xEF}, {0xE7, 0xAC}, {0xF4, 0xF4}, {0xE7, 0xAE},
+ {0xF4, 0xF5}, {0xE7, 0xB1}, {0xF4, 0xF6}, {0xE7, 0xB2}, {0xE8, 0xB1},
+ {0xE8, 0xB6}, {0xF4, 0xF7}, {0xF4, 0xF8}, {0xE8, 0xDD}, {0xF4, 0xF9},
+ {0xF4, 0xFA}, {0xE9, 0xD1}, {0xF4, 0xFB}, {0xE9, 0xED}, {0xEA, 0xCD},
+ {0xF4, 0xFC}, {0xEA, 0xDB}, {0xEA, 0xE6}, {0xEA, 0xEA}, {0xEB, 0xA5},
+ {0xEB, 0xFB}, {0xEB, 0xFA}, {0xF4, 0xFD}, {0xEC, 0xD6}, {0xF4, 0xFE},
+};
+
+#define IS_EUC_IBM2JISX0208(h, l) \
+ (((h) == 0xA2 && (l) == 0xCC) || ((h) == 0xA2 && (l) == 0xE8))
+
+/* EUC to SJIS IBM extended characters map (G3 JIS X 0212 block) */
+static struct {
+ unsigned short euc;
+ unsigned char sjis[2];
+} euc2sjisibm_jisx0212_map[] = {
+ {0xA2C3, {0xFA, 0x55}}, {0xB0A9, {0xFA, 0x68}}, {0xB0C8, {0xFA, 0x69}},
+ {0xB0D2, {0xFA, 0x6B}}, {0xB0D4, {0xFA, 0x6C}}, {0xB0E3, {0xFA, 0x6D}},
+ {0xB0EE, {0xFA, 0x6E}}, {0xB1A3, {0xFA, 0x70}}, {0xB1A7, {0xFA, 0x6F}},
+ {0xB1A9, {0xFA, 0x72}}, {0xB1AC, {0xFA, 0x71}}, {0xB1BB, {0xFA, 0x61}},
+ {0xB1BE, {0xFA, 0x73}}, {0xB1C8, {0xFA, 0x76}}, {0xB1D7, {0xFA, 0x77}},
+ {0xB1D8, {0xFA, 0x75}}, {0xB1DF, {0xFA, 0x74}}, {0xB1E1, {0xFA, 0x7A}},
+ {0xB1E3, {0xFA, 0x78}}, {0xB1F4, {0xFA, 0x79}}, {0xB2A3, {0xFA, 0x7B}},
+ {0xB2BB, {0xFA, 0x7D}}, {0xB2E6, {0xFA, 0x7E}}, {0xB2ED, {0xFA, 0x80}},
+ {0xB2F5, {0xFA, 0x81}}, {0xB2FC, {0xFA, 0x82}}, {0xB3B5, {0xFA, 0x84}},
+ {0xB3D8, {0xFA, 0x85}}, {0xB3DB, {0xFA, 0x86}}, {0xB3DD, {0xFB, 0x77}},
+ {0xB3E5, {0xFA, 0x87}}, {0xB3EE, {0xFA, 0x88}}, {0xB3FB, {0xFA, 0x89}},
+ {0xB4C0, {0xFA, 0x8C}}, {0xB4C7, {0xFA, 0x8D}}, {0xB4D0, {0xFA, 0x8E}},
+ {0xB4DE, {0xFA, 0x8F}}, {0xB5AA, {0xFA, 0x91}}, {0xB5AF, {0xFA, 0x93}},
+ {0xB5C4, {0xFA, 0x94}}, {0xB5E8, {0xFA, 0x95}}, {0xB7C2, {0xFA, 0x97}},
+ {0xB7E4, {0xFA, 0x98}}, {0xB7E7, {0xFA, 0x9A}}, {0xB7E8, {0xFA, 0x99}},
+ {0xB8CE, {0xFA, 0x9E}}, {0xB8E1, {0xFA, 0x9F}}, {0xB8F5, {0xFA, 0xA0}},
+ {0xB8F7, {0xFA, 0xA1}}, {0xB8F8, {0xFA, 0xA2}}, {0xB8FC, {0xFA, 0xA3}},
+ {0xB9AF, {0xFA, 0xA4}}, {0xB9B7, {0xFA, 0xA5}}, {0xBABE, {0xFA, 0xA6}},
+ {0xBADB, {0xFA, 0xA7}}, {0xBAE1, {0xFA, 0xA9}}, {0xBAEB, {0xFA, 0xAB}},
+ {0xBBB3, {0xFA, 0xAC}}, {0xBBB8, {0xFA, 0xAD}}, {0xBBCA, {0xFA, 0xAF}},
+ {0xBBD0, {0xFA, 0xB2}}, {0xBBDE, {0xFA, 0xB3}}, {0xBBF4, {0xFA, 0xB4}},
+ {0xBBF5, {0xFA, 0xB5}}, {0xBBF9, {0xFA, 0xB6}}, {0xBCE4, {0xFA, 0xB7}},
+ {0xBCED, {0xFA, 0xB8}}, {0xBCF4, {0xFA, 0x67}}, {0xBCFE, {0xFA, 0xB9}},
+ {0xBDC2, {0xFA, 0xBB}}, {0xBDE7, {0xFA, 0xBC}}, {0xBDF0, {0xFA, 0xBE}},
+ {0xBEAC, {0xFA, 0xC0}}, {0xBEB0, {0xFA, 0xBF}}, {0xBEB3, {0xFA, 0xC2}},
+ {0xBEBD, {0xFA, 0xC3}}, {0xBEC9, {0xFA, 0xC5}}, {0xBECD, {0xFA, 0xC4}},
+ {0xBEE4, {0xFA, 0xC6}}, {0xBFA8, {0xFA, 0xC7}}, {0xBFC9, {0xFA, 0xC8}},
+ {0xC0C4, {0xFA, 0xC9}}, {0xC0E4, {0xFA, 0xCA}}, {0xC0F4, {0xFA, 0xCB}},
+ {0xC1A6, {0xFA, 0xCC}}, {0xC1F5, {0xFA, 0xCE}}, {0xC1F8, {0xFA, 0xD1}},
+ {0xC1FC, {0xFA, 0xCF}}, {0xC2A1, {0xFA, 0xD3}}, {0xC2A5, {0xFA, 0xD4}},
+ {0xC2AB, {0xFA, 0xD2}}, {0xC2AD, {0xFA, 0x63}}, {0xC2B8, {0xFA, 0xD6}},
+ {0xC2BA, {0xFA, 0xD7}}, {0xC2BF, {0xFA, 0x66}}, {0xC2C4, {0xFA, 0xD9}},
+ {0xC2D2, {0xFA, 0xDA}}, {0xC2D7, {0xFA, 0xDB}}, {0xC2DB, {0xFA, 0xDC}},
+ {0xC2DE, {0xFA, 0xDD}}, {0xC2ED, {0xFA, 0xDE}}, {0xC2F0, {0xFA, 0xDF}},
+ {0xC3A1, {0xFA, 0xE1}}, {0xC3B5, {0xFA, 0xE2}}, {0xC3B9, {0xFA, 0xE4}},
+ {0xC3C9, {0xFA, 0xE3}}, {0xC3D8, {0xFA, 0xE6}}, {0xC3FC, {0xFA, 0x64}},
+ {0xC3FE, {0xFA, 0xE7}}, {0xC4CC, {0xFA, 0xE9}}, {0xC4D9, {0xFA, 0xEB}},
+ {0xC4EA, {0xFA, 0xEC}}, {0xC4FD, {0xFA, 0xED}}, {0xC5A7, {0xFA, 0xEF}},
+ {0xC5B5, {0xFA, 0xF0}}, {0xC5B6, {0xFA, 0xF1}}, {0xC5D5, {0xFA, 0xF3}},
+ {0xC6B8, {0xFA, 0xF4}}, {0xC6D7, {0xFA, 0xF5}}, {0xC6E0, {0xFA, 0xF6}},
+ {0xC6E3, {0xFA, 0xF8}}, {0xC6EA, {0xFA, 0xF7}}, {0xC7A1, {0xFA, 0xF9}},
+ {0xC7AB, {0xFA, 0xFA}}, {0xC7C3, {0xFA, 0xFC}}, {0xC7C7, {0xFA, 0xFB}},
+ {0xC7CB, {0xFB, 0x40}}, {0xC7CF, {0xFB, 0x41}}, {0xC7D9, {0xFB, 0x42}},
+ {0xC7E6, {0xFB, 0x45}}, {0xC7EB, {0xFB, 0x48}}, {0xC7EE, {0xFB, 0x46}},
+ {0xC7F0, {0xFB, 0x49}}, {0xC7FC, {0xFB, 0x47}}, {0xC8B1, {0xFB, 0x4A}},
+ {0xC8E5, {0xFB, 0x4B}}, {0xC8F8, {0xFB, 0x4C}}, {0xC9A6, {0xFB, 0x4D}},
+ {0xC9AB, {0xFB, 0x4E}}, {0xC9AD, {0xFB, 0x4F}}, {0xC9CA, {0xFB, 0x51}},
+ {0xC9D3, {0xFB, 0x52}}, {0xC9E3, {0xFB, 0x54}}, {0xC9E9, {0xFB, 0x53}},
+ {0xC9F4, {0xFB, 0x56}}, {0xC9F5, {0xFB, 0x57}}, {0xC9FC, {0xFB, 0x55}},
+ {0xCAB3, {0xFB, 0x59}}, {0xCABD, {0xFB, 0x5A}}, {0xCAEF, {0xFB, 0x5B}},
+ {0xCAF1, {0xFB, 0x5C}}, {0xCBAE, {0xFB, 0x5D}}, {0xCBCA, {0xFB, 0x5F}},
+ {0xCBE6, {0xFB, 0x60}}, {0xCBEA, {0xFB, 0x61}}, {0xCBEE, {0xFB, 0x64}},
+ {0xCBF0, {0xFB, 0x62}}, {0xCBF4, {0xFB, 0x63}}, {0xCBF9, {0xFB, 0x66}},
+ {0xCCA5, {0xFB, 0x65}}, {0xCCAB, {0xFB, 0x67}}, {0xCCAD, {0xFB, 0x69}},
+ {0xCCAE, {0xFB, 0x68}}, {0xCCB2, {0xFB, 0x6A}}, {0xCCC2, {0xFB, 0x6B}},
+ {0xCCD0, {0xFB, 0x6C}}, {0xCCD9, {0xFB, 0x6D}}, {0xCDAA, {0xFA, 0xA8}},
+ {0xCDBB, {0xFB, 0x6F}}, {0xCEBA, {0xFB, 0x73}}, {0xCEBB, {0xFB, 0x71}},
+ {0xCEC3, {0xFB, 0x74}}, {0xCEF2, {0xFB, 0x76}}, {0xCFD5, {0xFB, 0x78}},
+ {0xCFE2, {0xFB, 0x79}}, {0xCFE9, {0xFB, 0x7A}}, {0xCFED, {0xFB, 0x7B}},
+ {0xD0E5, {0xFB, 0x81}}, {0xD0E9, {0xFB, 0x83}}, {0xD1E8, {0xFB, 0x84}},
+ {0xD1EC, {0xFB, 0x87}}, {0xD2BB, {0xFB, 0x88}}, {0xD3E1, {0xFB, 0x8A}},
+ {0xD3E8, {0xFB, 0x8B}}, {0xD4A7, {0xFB, 0x8C}}, {0xD4D4, {0xFB, 0x8F}},
+ {0xD4E3, {0xFA, 0x5C}}, {0xD4F2, {0xFB, 0x90}}, {0xD5AE, {0xFB, 0x91}},
+ {0xD7DE, {0xFB, 0x93}}, {0xD8A2, {0xFB, 0x95}}, {0xD8B7, {0xFB, 0x96}},
+ {0xD8C1, {0xFB, 0x97}}, {0xD8D1, {0xFB, 0x98}}, {0xD8F4, {0xFB, 0x99}},
+ {0xD9A1, {0xFA, 0x60}}, {0xD9C6, {0xFB, 0x9A}}, {0xD9C8, {0xFB, 0x9B}},
+ {0xD9D1, {0xFB, 0x9C}}, {0xDCD3, {0xFB, 0xA2}}, {0xDCDF, {0xFA, 0x5D}},
+ {0xDDC8, {0xFB, 0xA3}}, {0xDDD4, {0xFB, 0xA4}}, {0xDDEA, {0xFB, 0xA5}},
+ {0xDDFA, {0xFB, 0xA6}}, {0xDEA4, {0xFB, 0xA7}}, {0xDEB0, {0xFB, 0xA8}},
+ {0xDEB5, {0xFB, 0xAA}}, {0xDECB, {0xFB, 0xAB}}, {0xDFB9, {0xFB, 0xAD}},
+ {0xDFC3, {0xFB, 0xAF}}, {0xE0D9, {0xFB, 0xB2}}, {0xE1E2, {0xFB, 0xB5}},
+ {0xE2C7, {0xFB, 0xB9}}, {0xE3A6, {0xFB, 0xBB}}, {0xE3A8, {0xFB, 0xBA}},
+ {0xE3A9, {0xFB, 0xBC}}, {0xE3AA, {0xFB, 0xBF}}, {0xE3AB, {0xFB, 0xC0}},
+ {0xE3AF, {0xFB, 0xBD}}, {0xE3B0, {0xFB, 0xBE}}, {0xE3BC, {0xFB, 0xC1}},
+ {0xE3BF, {0xFB, 0xC3}}, {0xE3C1, {0xFB, 0xC2}}, {0xE3D4, {0xFB, 0xCA}},
+ {0xE3D5, {0xFB, 0xC4}}, {0xE3D6, {0xFB, 0xC6}}, {0xE3D8, {0xFB, 0xC5}},
+ {0xE3DF, {0xFB, 0xC7}}, {0xE3E1, {0xFB, 0xC9}}, {0xE3E3, {0xFB, 0xC8}},
+ {0xE3E9, {0xFB, 0xCB}}, {0xE3F1, {0xFB, 0xCD}}, {0xE3F2, {0xFB, 0xCE}},
+ {0xE3F8, {0xFA, 0x5F}}, {0xE4A6, {0xFB, 0xCC}}, {0xE4BE, {0xFB, 0xD2}},
+ {0xE4BF, {0xFB, 0xD6}}, {0xE4C0, {0xFB, 0xD4}}, {0xE4C1, {0xFB, 0xD0}},
+ {0xE4C3, {0xFB, 0xD1}}, {0xE4C7, {0xFB, 0xD5}}, {0xE4CB, {0xFB, 0xCF}},
+ {0xE4D0, {0xFA, 0x65}}, {0xE4D1, {0xFB, 0xD9}}, {0xE4D2, {0xFB, 0xDC}},
+ {0xE4D4, {0xFB, 0xDE}}, {0xE4DB, {0xFB, 0xDD}}, {0xE4DC, {0xFB, 0xDB}},
+ {0xE4DE, {0xFB, 0xD8}}, {0xE4E0, {0xFB, 0xD7}}, {0xE4E9, {0xFA, 0x5E}},
+ {0xE4EF, {0xFB, 0xE0}}, {0xE4FA, {0xFB, 0xDF}}, {0xE5B3, {0xFB, 0xE1}},
+ {0xE5BF, {0xFB, 0xE2}}, {0xE5C9, {0xFB, 0xE3}}, {0xE5D0, {0xFB, 0xE4}},
+ {0xE5E2, {0xFB, 0xE5}}, {0xE5EA, {0xFB, 0xE6}}, {0xE5EB, {0xFB, 0xE7}},
+ {0xE6E8, {0xFB, 0xEB}}, {0xE6EF, {0xFB, 0xEC}}, {0xE7AC, {0xFB, 0xED}},
+ {0xE7AE, {0xFB, 0xEF}}, {0xE7B1, {0xFB, 0xF1}}, {0xE7B2, {0xFB, 0xF3}},
+ {0xE8B1, {0xFB, 0xF4}}, {0xE8B6, {0xFB, 0xF5}}, {0xE8DD, {0xFB, 0xF8}},
+ {0xE9D1, {0xFB, 0xFB}}, {0xE9ED, {0xFC, 0x40}}, {0xEACD, {0xFC, 0x41}},
+ {0xEADB, {0xFC, 0x43}}, {0xEAE6, {0xFC, 0x44}}, {0xEAEA, {0xFC, 0x45}},
+ {0xEBA5, {0xFC, 0x46}}, {0xEBFA, {0xFC, 0x48}}, {0xEBFB, {0xFC, 0x47}},
+ {0xECD6, {0xFC, 0x4A}},
+};
+
+/* EUC to SJIS IBM extended characters map (G3 Upper block) */
+static unsigned char euc2sjisibm_g3upper_map[][2] = {
+ {0xFA, 0x40}, {0xFA, 0x41}, {0xFA, 0x42}, {0xFA, 0x43}, {0xFA, 0x44},
+ {0xFA, 0x45}, {0xFA, 0x46}, {0xFA, 0x47}, {0xFA, 0x48}, {0xFA, 0x49},
+ {0xFA, 0x4A}, {0xFA, 0x4B}, {0xFA, 0x4C}, {0xFA, 0x4D}, {0xFA, 0x4E},
+ {0xFA, 0x4F}, {0xFA, 0x50}, {0xFA, 0x51}, {0xFA, 0x52}, {0xFA, 0x53},
+ {0xFA, 0x56}, {0xFA, 0x57}, {0xFA, 0x58}, {0xFA, 0x59}, {0xFA, 0x5A},
+ {0xFA, 0x62}, {0xFA, 0x6A}, {0xFA, 0x7C}, {0xFA, 0x83}, {0xFA, 0x8A},
+ {0xFA, 0x8B}, {0xFA, 0x90}, {0xFA, 0x92}, {0xFA, 0x96}, {0xFA, 0x9B},
+ {0xFA, 0x9C}, {0xFA, 0x9D}, {0xFA, 0xAA}, {0xFA, 0xAE}, {0xFA, 0xB0},
+ {0xFA, 0xB1}, {0xFA, 0xBA}, {0xFA, 0xBD}, {0xFA, 0xC1}, {0xFA, 0xCD},
+ {0xFA, 0xD0}, {0xFA, 0xD5}, {0xFA, 0xD8}, {0xFA, 0xE0}, {0xFA, 0xE5},
+ {0xFA, 0xE8}, {0xFA, 0xEA}, {0xFA, 0xEE}, {0xFA, 0xF2}, {0xFB, 0x43},
+ {0xFB, 0x44}, {0xFB, 0x50}, {0xFB, 0x58}, {0xFB, 0x5E}, {0xFB, 0x6E},
+ {0xFB, 0x70}, {0xFB, 0x72}, {0xFB, 0x75}, {0xFB, 0x7C}, {0xFB, 0x7D},
+ {0xFB, 0x7E}, {0xFB, 0x80}, {0xFB, 0x82}, {0xFB, 0x85}, {0xFB, 0x86},
+ {0xFB, 0x89}, {0xFB, 0x8D}, {0xFB, 0x8E}, {0xFB, 0x92}, {0xFB, 0x94},
+ {0xFB, 0x9D}, {0xFB, 0x9E}, {0xFB, 0x9F}, {0xFB, 0xA0}, {0xFB, 0xA1},
+ {0xFB, 0xA9}, {0xFB, 0xAC}, {0xFB, 0xAE}, {0xFB, 0xB0}, {0xFB, 0xB1},
+ {0xFB, 0xB3}, {0xFB, 0xB4}, {0xFB, 0xB6}, {0xFB, 0xB7}, {0xFB, 0xB8},
+ {0xFB, 0xD3}, {0xFB, 0xDA}, {0xFB, 0xE8}, {0xFB, 0xE9}, {0xFB, 0xEA},
+ {0xFB, 0xEE}, {0xFB, 0xF0}, {0xFB, 0xF2}, {0xFB, 0xF6}, {0xFB, 0xF7},
+ {0xFB, 0xF9}, {0xFB, 0xFA}, {0xFB, 0xFC}, {0xFC, 0x42}, {0xFC, 0x49},
+ {0xFC, 0x4B},
+};
+
+#define MAP_ELEMENT_OF(map) (sizeof(map) / sizeof(map[0]))
+
+static inline int sjisibm2euc(unsigned char *euc, const unsigned char sjis_hi,
+ const unsigned char sjis_lo);
+static inline int euc2sjisibm_jisx0212(unsigned char *sjis, const unsigned char euc_hi,
+ const unsigned char euc_lo);
+static inline int euc2sjisibm_g3upper(unsigned char *sjis, const unsigned char euc_hi,
+ const unsigned char euc_lo);
+static inline int euc2sjisibm(unsigned char *sjis, const unsigned char euc_hi,
+ const unsigned char euc_lo);
+static inline int sjisnec2sjisibm(unsigned char *sjisibm,
+ const unsigned char sjisnec_hi,
+ const unsigned char sjisnec_lo);
+
+/* SJIS IBM extended characters to EUC */
+static inline int sjisibm2euc(unsigned char *euc, const unsigned char sjis_hi,
+ const unsigned char sjis_lo)
+{
+ int index;
+
+ index = ((sjis_hi - 0xFA) * (0xFD - 0x40)) + (sjis_lo - 0x40);
+ if (IS_EUC_IBM2JISX0208(sjisibm2euc_map[index][0],
+ sjisibm2euc_map[index][1])) {
+ euc[0] = sjisibm2euc_map[index][0];
+ euc[1] = sjisibm2euc_map[index][1];
+ return 2;
+ } else {
+ euc[0] = SS3;
+ euc[1] = sjisibm2euc_map[index][0];
+ euc[2] = sjisibm2euc_map[index][1];
+ return 3;
+ }
+}
+
+/* EUC to SJIS IBM extended characters (G3 JIS X 0212 block) */
+static inline int euc2sjisibm_jisx0212(unsigned char *sjis, const unsigned char euc_hi,
+ const unsigned char euc_lo)
+{
+ int index, min_index, max_index;
+ unsigned short euc;
+
+ min_index = 0;
+ max_index = MAP_ELEMENT_OF(euc2sjisibm_jisx0212_map) - 1;
+ euc = (euc_hi << 8) | euc_lo;
+
+ while (min_index <= max_index) {
+ index = (min_index + max_index) / 2;
+ if (euc < euc2sjisibm_jisx0212_map[index].euc)
+ max_index = index - 1;
+ else
+ min_index = index + 1;
+ if (euc == euc2sjisibm_jisx0212_map[index].euc) {
+ sjis[0] = euc2sjisibm_jisx0212_map[index].sjis[0];
+ sjis[1] = euc2sjisibm_jisx0212_map[index].sjis[1];
+ return 3;
+ }
+ }
+ return 0;
+}
+
+/* EUC to SJIS IBM extended characters (G3 Upper block) */
+static inline int euc2sjisibm_g3upper(unsigned char *sjis, const unsigned char euc_hi,
+ const unsigned char euc_lo)
+{
+ int index;
+
+ if (euc_hi == 0xF3)
+ index = ((euc_hi << 8) | euc_lo) - 0xF3F3;
+ else
+ index = ((euc_hi << 8) | euc_lo) - 0xF4A1 + 12;
+
+ if ((index < 0) || (index >= MAP_ELEMENT_OF(euc2sjisibm_g3upper_map)))
+ return 0;
+
+ sjis[0] = euc2sjisibm_g3upper_map[index][0];
+ sjis[1] = euc2sjisibm_g3upper_map[index][1];
+
+ return 3;
+}
+
+/* EUC to SJIS IBM extended characters (G3 block) */
+static inline int euc2sjisibm(unsigned char *sjis, const unsigned char euc_hi,
+ const unsigned char euc_lo)
+{
+ int n;
+
+#if 0
+ if ((euc_hi == 0xA2) && (euc_lo == 0xCC)) {
+ sjis[0] = 0xFA;
+ sjis[1] = 0x54;
+ return 2;
+ } else if ((euc_hi == 0xA2) && (euc_lo == 0xE8)) {
+ sjis[0] = 0xFA;
+ sjis[1] = 0x5B;
+ return 2;
+ }
+#endif
+ if ((n = euc2sjisibm_g3upper(sjis, euc_hi, euc_lo))) {
+ return n;
+ } else if ((n = euc2sjisibm_jisx0212(sjis, euc_hi, euc_lo))) {
+ return n;
+ }
+
+ return 0;
+}
+
+/* NEC/IBM extended characters to IBM extended characters */
+static inline int sjisnec2sjisibm(unsigned char *sjisibm,
+ const unsigned char sjisnec_hi,
+ const unsigned char sjisnec_lo)
+{
+ int count;
+
+ if (! IS_SJIS_NECIBM(sjisnec_hi, sjisnec_lo))
+ return 0;
+
+ if ((sjisnec_hi == 0xEE) && (sjisnec_lo == 0xF9)) {
+ sjisibm[0] = 0x81;
+ sjisibm[1] = 0xCA;
+ return 2;
+ }
+
+ if ((sjisnec_hi == 0xEE) && (sjisnec_lo >= 0xEF)) {
+ count = (sjisnec_hi << 8 | sjisnec_lo)
+ - (sjisnec_lo <= 0xF9 ? 0xEEEF : (0xEEEF - 10));
+ } else {
+ count = (sjisnec_hi - 0xED) * (0xFC - 0x40)
+ + (sjisnec_lo - 0x40) + (0x5C - 0x40);
+ if (sjisnec_lo >= 0x7F)
+ count--;
+ }
+
+ sjisibm[0] = 0xFA + (count / (0xFC - 0x40));
+ sjisibm[1] = 0x40 + (count % (0xFC - 0x40));
+ if (sjisibm[1] >= 0x7F)
+ sjisibm[1]++;
+
+ return 2;
+}
+
+static int uni2char(const wchar_t uni,
+ unsigned char *out, int boundlen)
+{
+ int n;
+
+ if (!p_nls)
+ return -EINVAL;
+ if ((n = p_nls->uni2char(uni, out, boundlen)) < 0)
+ return n;
+
+ /* translate SJIS into EUC-JP */
+ if (n == 1) {
+ if (IS_SJIS_JISX0201KANA(out[0])) {
+ /* JIS X 0201 KANA */
+ if (boundlen < 2)
+ return -ENAMETOOLONG;
+
+ out[1] = out[0];
+ out[0] = SS2;
+ return 2;
+ }
+ } else if (n == 2) {
+ /* NEC/IBM extended characters to IBM extended characters */
+ sjisnec2sjisibm(out, out[0], out[1]);
+
+ if (IS_SJIS_UDC_LOW(out[0], out[1])) {
+ /* User defined characters half low */
+ MAP_SJIS2EUC(out[0], out[1], 0xF0, out[0], out[1], 0xF5);
+ } else if (IS_SJIS_UDC_HI(out[0], out[1])) {
+ /* User defined characters half high */
+ unsigned char ch, cl;
+
+ if (boundlen < 3)
+ return -ENAMETOOLONG;
+
+ n = 3; ch = out[0]; cl = out[1];
+ out[0] = SS3;
+ MAP_SJIS2EUC(ch, cl, 0xF5, out[1], out[2], 0xF5);
+ } else if (IS_SJIS_IBM(out[0], out[1])) {
+ /* IBM extended characters */
+ unsigned char euc[3], i;
+
+ n = sjisibm2euc(euc, out[0], out[1]);
+ if (boundlen < n)
+ return -ENAMETOOLONG;
+ for (i = 0; i < n; i++)
+ out[i] = euc[i];
+ } else if (IS_SJIS_JISX0208(out[0], out[1])) {
+ /* JIS X 0208 (include NEC special characters) */
+ out[0] = (out[0]^0xA0)*2 + 0x5F;
+ if (out[1] > 0x9E)
+ out[0]++;
+
+ if (out[1] < 0x7F)
+ out[1] = out[1] + 0x61;
+ else if (out[1] < 0x9F)
+ out[1] = out[1] + 0x60;
+ else
+ out[1] = out[1] + 0x02;
+ } else {
+ /* Invalid characters */
+ return -EINVAL;
+ }
+ }
+ else
+ return -EINVAL;
+
+ return n;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen,
+ wchar_t *uni)
+{
+ unsigned char sjis_temp[2];
+ int euc_offset, n;
+
+ if ( !p_nls )
+ return -EINVAL;
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ /* translate EUC-JP into SJIS */
+ if (rawstring[0] > 0x7F) {
+ if (rawstring[0] == SS3) {
+ if (boundlen < 3)
+ return -EINVAL;
+ euc_offset = 3;
+
+ if (IS_EUC_UDC_HI(rawstring[1], rawstring[2])) {
+ /* User defined characters half high */
+ MAP_EUC2SJIS(rawstring[1], rawstring[2], 0xF5,
+ sjis_temp[0], sjis_temp[1], 0xF5);
+ } else if (euc2sjisibm(sjis_temp,rawstring[1],rawstring[2])) {
+ /* IBM extended characters */
+ } else {
+ /* JIS X 0212 and Invalid characters*/
+ return -EINVAL;
+
+ /* 'GETA' with SJIS coding */
+ /* sjis_temp[0] = 0x81; */
+ /* sjis_temp[1] = 0xAC; */
+ }
+ } else {
+ if (boundlen < 2)
+ return -EINVAL;
+ euc_offset = 2;
+
+ if (IS_EUC_JISX0201KANA(rawstring[0], rawstring[1])) {
+ /* JIS X 0201 KANA */
+ sjis_temp[0] = rawstring[1];
+ sjis_temp[1] = 0x00;
+ } else if (IS_EUC_UDC_LOW(rawstring[0], rawstring[1])) {
+ /* User defined characters half low */
+ MAP_EUC2SJIS(rawstring[0], rawstring[1], 0xF5,
+ sjis_temp[0], sjis_temp[1], 0xF0);
+ } else if (IS_EUC_JISX0208(rawstring[0], rawstring[1])) {
+ /* JIS X 0208 (include NEC spesial characters) */
+ sjis_temp[0] = ((rawstring[0]-0x5f)/2) ^ 0xA0;
+ if (!(rawstring[0] & 1))
+ sjis_temp[1] = rawstring[1] - 0x02;
+ else if (rawstring[1] < 0xE0)
+ sjis_temp[1] = rawstring[1] - 0x61;
+ else
+ sjis_temp[1] = rawstring[1] - 0x60;
+ } else {
+ /* Invalid characters */
+ return -EINVAL;
+ }
+ }
+ } else {
+ euc_offset = 1;
+
+ /* JIS X 0201 ROMAJI */
+ sjis_temp[0] = rawstring[0];
+ sjis_temp[1] = 0x00;
+ }
+
+ if ( (n = p_nls->char2uni(sjis_temp, sizeof(sjis_temp), uni)) < 0)
+ return n;
+
+ return euc_offset;
+}
+
+static struct nls_table table = {
+ "euc-jp",
+ uni2char,
+ char2uni,
+ NULL,
+ NULL,
+ THIS_MODULE,
+};
+
+static int __init init_nls_euc_jp(void)
+{
+ p_nls = load_nls("cp932");
+
+ if (p_nls) {
+ table.charset2upper = p_nls->charset2upper;
+ table.charset2lower = p_nls->charset2lower;
+ return register_nls(&table);
+ }
+
+ return -EINVAL;
+}
+
+static void __exit exit_nls_euc_jp(void)
+{
+ unregister_nls(&table);
+ unload_nls(p_nls);
+}
+
+module_init(init_nls_euc_jp)
+module_exit(exit_nls_euc_jp)
+MODULE_LICENSE("Dual BSD/GPL");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ *
+---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/fs/nls/nls_euc-kr.c b/fs/nls/nls_euc-kr.c
new file mode 100644
index 00000000000000..e7b030aa0b9aef
--- /dev/null
+++ b/fs/nls/nls_euc-kr.c
@@ -0,0 +1,63 @@
+/*
+ * linux/fs/nls_euc-kr.c
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static struct nls_table *p_nls;
+
+static struct nls_table table = {
+ "euc-kr",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ THIS_MODULE,
+};
+
+static int __init init_nls_euc_kr(void)
+{
+ p_nls = load_nls("cp949");
+
+ if (p_nls) {
+ table.uni2char = p_nls->uni2char;
+ table.char2uni = p_nls->char2uni;
+ table.charset2upper = p_nls->charset2upper;
+ table.charset2lower = p_nls->charset2lower;
+ return register_nls(&table);
+ }
+
+ return -EINVAL;
+}
+
+static void __exit exit_nls_euc_kr(void)
+{
+ unregister_nls(&table);
+ unload_nls(p_nls);
+}
+
+module_init(init_nls_euc_kr)
+module_exit(exit_nls_euc_kr)
+MODULE_LICENSE("Dual BSD/GPL");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ *
+---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/fs/nls/nls_gb2312.c b/fs/nls/nls_gb2312.c
new file mode 100644
index 00000000000000..dd64d84c377804
--- /dev/null
+++ b/fs/nls/nls_gb2312.c
@@ -0,0 +1,63 @@
+/*
+ * linux/fs/nls_gb2312.c
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static struct nls_table *p_nls;
+
+static struct nls_table table = {
+ "gb2312",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ THIS_MODULE,
+};
+
+static int __init init_nls_gb2312(void)
+{
+ p_nls = load_nls("cp936");
+
+ if (p_nls) {
+ table.uni2char = p_nls->uni2char;
+ table.char2uni = p_nls->char2uni;
+ table.charset2upper = p_nls->charset2upper;
+ table.charset2lower = p_nls->charset2lower;
+ return register_nls(&table);
+ }
+
+ return -EINVAL;
+}
+
+static void __exit exit_nls_gb2312(void)
+{
+ unregister_nls(&table);
+ unload_nls(p_nls);
+}
+
+module_init(init_nls_gb2312)
+module_exit(exit_nls_gb2312)
+MODULE_LICENSE("Dual BSD/GPL");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ *
+---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/fs/nls/nls_iso8859-1.c b/fs/nls/nls_iso8859-1.c
new file mode 100644
index 00000000000000..9477f418a481a7
--- /dev/null
+++ b/fs/nls/nls_iso8859-1.c
@@ -0,0 +1,274 @@
+/*
+ * linux/fs/nls_iso8859-1.c
+ *
+ * Charset iso8859-1 translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static wchar_t charset2uni[256] = {
+ /* 0x00*/
+ 0x0000, 0x0001, 0x0002, 0x0003,
+ 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b,
+ 0x000c, 0x000d, 0x000e, 0x000f,
+ /* 0x10*/
+ 0x0010, 0x0011, 0x0012, 0x0013,
+ 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b,
+ 0x001c, 0x001d, 0x001e, 0x001f,
+ /* 0x20*/
+ 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b,
+ 0x002c, 0x002d, 0x002e, 0x002f,
+ /* 0x30*/
+ 0x0030, 0x0031, 0x0032, 0x0033,
+ 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b,
+ 0x003c, 0x003d, 0x003e, 0x003f,
+ /* 0x40*/
+ 0x0040, 0x0041, 0x0042, 0x0043,
+ 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b,
+ 0x004c, 0x004d, 0x004e, 0x004f,
+ /* 0x50*/
+ 0x0050, 0x0051, 0x0052, 0x0053,
+ 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b,
+ 0x005c, 0x005d, 0x005e, 0x005f,
+ /* 0x60*/
+ 0x0060, 0x0061, 0x0062, 0x0063,
+ 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b,
+ 0x006c, 0x006d, 0x006e, 0x006f,
+ /* 0x70*/
+ 0x0070, 0x0071, 0x0072, 0x0073,
+ 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b,
+ 0x007c, 0x007d, 0x007e, 0x007f,
+ /* 0x80*/
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0x90*/
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0xa0*/
+ 0x00a0, 0x00a1, 0x00a2, 0x00a3,
+ 0x00a4, 0x00a5, 0x00a6, 0x00a7,
+ 0x00a8, 0x00a9, 0x00aa, 0x00ab,
+ 0x00ac, 0x00ad, 0x00ae, 0x00af,
+ /* 0xb0*/
+ 0x00b0, 0x00b1, 0x00b2, 0x00b3,
+ 0x00b4, 0x00b5, 0x00b6, 0x00b7,
+ 0x00b8, 0x00b9, 0x00ba, 0x00bb,
+ 0x00bc, 0x00bd, 0x00be, 0x00bf,
+ /* 0xc0*/
+ 0x00c0, 0x00c1, 0x00c2, 0x00c3,
+ 0x00c4, 0x00c5, 0x00c6, 0x00c7,
+ 0x00c8, 0x00c9, 0x00ca, 0x00cb,
+ 0x00cc, 0x00cd, 0x00ce, 0x00cf,
+ /* 0xd0*/
+ 0x00d0, 0x00d1, 0x00d2, 0x00d3,
+ 0x00d4, 0x00d5, 0x00d6, 0x00d7,
+ 0x00d8, 0x00d9, 0x00da, 0x00db,
+ 0x00dc, 0x00dd, 0x00de, 0x00df,
+ /* 0xe0*/
+ 0x00e0, 0x00e1, 0x00e2, 0x00e3,
+ 0x00e4, 0x00e5, 0x00e6, 0x00e7,
+ 0x00e8, 0x00e9, 0x00ea, 0x00eb,
+ 0x00ec, 0x00ed, 0x00ee, 0x00ef,
+ /* 0xf0*/
+ 0x00f0, 0x00f1, 0x00f2, 0x00f3,
+ 0x00f4, 0x00f5, 0x00f6, 0x00f7,
+ 0x00f8, 0x00f9, 0x00fa, 0x00fb,
+ 0x00fc, 0x00fd, 0x00fe, 0x00ff,
+};
+
+static unsigned char page00[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static unsigned char *page_uni2charset[256] = {
+ page00, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+};
+
+static unsigned char charset2lower[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40-0x47 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x48-0x4f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50-0x57 */
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xc0-0xc7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xc8-0xcf */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xd7, /* 0xd0-0xd7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static unsigned char charset2upper[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0x00, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xe0-0xe7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xe8-0xef */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xf7, /* 0xf0-0xf7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0x00, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+ unsigned char *uni2charset;
+ unsigned char cl = uni & 0x00ff;
+ unsigned char ch = (uni & 0xff00) >> 8;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ uni2charset = page_uni2charset[ch];
+ if (uni2charset && uni2charset[cl])
+ out[0] = uni2charset[cl];
+ else
+ return -EINVAL;
+ return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+ *uni = charset2uni[*rawstring];
+ if (*uni == 0x0000)
+ return -EINVAL;
+ return 1;
+}
+
+static struct nls_table table = {
+ "iso8859-1",
+ uni2char,
+ char2uni,
+ charset2lower,
+ charset2upper,
+ THIS_MODULE,
+};
+
+static int __init init_nls_iso8859_1(void)
+{
+ return register_nls(&table);
+}
+
+static void __exit exit_nls_iso8859_1(void)
+{
+ unregister_nls(&table);
+}
+
+module_init(init_nls_iso8859_1)
+module_exit(exit_nls_iso8859_1)
+MODULE_LICENSE("Dual BSD/GPL");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/fs/nls/nls_iso8859-13.c b/fs/nls/nls_iso8859-13.c
new file mode 100644
index 00000000000000..c8e626afc8b689
--- /dev/null
+++ b/fs/nls/nls_iso8859-13.c
@@ -0,0 +1,302 @@
+/*
+ * linux/fs/nls_iso8859-13.c
+ *
+ * Charset iso8859-13 translation tables.
+ * The Unicode to charset table has only exact mappings.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static wchar_t charset2uni[256] = {
+ /* 0x00*/
+ 0x0000, 0x0001, 0x0002, 0x0003,
+ 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b,
+ 0x000c, 0x000d, 0x000e, 0x000f,
+ /* 0x10*/
+ 0x0010, 0x0011, 0x0012, 0x0013,
+ 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b,
+ 0x001c, 0x001d, 0x001e, 0x001f,
+ /* 0x20*/
+ 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b,
+ 0x002c, 0x002d, 0x002e, 0x002f,
+ /* 0x30*/
+ 0x0030, 0x0031, 0x0032, 0x0033,
+ 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b,
+ 0x003c, 0x003d, 0x003e, 0x003f,
+ /* 0x40*/
+ 0x0040, 0x0041, 0x0042, 0x0043,
+ 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b,
+ 0x004c, 0x004d, 0x004e, 0x004f,
+ /* 0x50*/
+ 0x0050, 0x0051, 0x0052, 0x0053,
+ 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b,
+ 0x005c, 0x005d, 0x005e, 0x005f,
+ /* 0x60*/
+ 0x0060, 0x0061, 0x0062, 0x0063,
+ 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b,
+ 0x006c, 0x006d, 0x006e, 0x006f,
+ /* 0x70*/
+ 0x0070, 0x0071, 0x0072, 0x0073,
+ 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b,
+ 0x007c, 0x007d, 0x007e, 0x007f,
+ /* 0x80*/
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0x90*/
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0xa0*/
+ 0x00a0, 0x201d, 0x00a2, 0x00a3,
+ 0x00a4, 0x201e, 0x00a6, 0x00a7,
+ 0x00d8, 0x00a9, 0x0156, 0x00ab,
+ 0x00ac, 0x00ad, 0x00ae, 0x00c6,
+ /* 0xb0*/
+ 0x00b0, 0x00b1, 0x00b2, 0x00b3,
+ 0x201c, 0x00b5, 0x00b6, 0x00b7,
+ 0x00f8, 0x00b9, 0x0157, 0x00bb,
+ 0x00bc, 0x00bd, 0x00be, 0x00e6,
+ /* 0xc0*/
+ 0x0104, 0x012e, 0x0100, 0x0106,
+ 0x00c4, 0x00c5, 0x0118, 0x0112,
+ 0x010c, 0x00c9, 0x0179, 0x0116,
+ 0x0122, 0x0136, 0x012a, 0x013b,
+ /* 0xd0*/
+ 0x0160, 0x0143, 0x0145, 0x00d3,
+ 0x014c, 0x00d5, 0x00d6, 0x00d7,
+ 0x0172, 0x0141, 0x015a, 0x016a,
+ 0x00dc, 0x017b, 0x017d, 0x00df,
+ /* 0xe0*/
+ 0x0105, 0x012f, 0x0101, 0x0107,
+ 0x00e4, 0x00e5, 0x0119, 0x0113,
+ 0x010d, 0x00e9, 0x017a, 0x0117,
+ 0x0123, 0x0137, 0x012b, 0x013c,
+ /* 0xf0*/
+ 0x0161, 0x0144, 0x0146, 0x00f3,
+ 0x014d, 0x00f5, 0x00f6, 0x00f7,
+ 0x0173, 0x0142, 0x015b, 0x016b,
+ 0x00fc, 0x017c, 0x017e, 0x2019,
+};
+
+static unsigned char page00[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0x00, 0xa2, 0xa3, 0xa4, 0x00, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0x00, 0xa9, 0x00, 0xab, 0xac, 0xad, 0xae, 0x00, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0x00, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0x00, 0xb9, 0x00, 0xbb, 0xbc, 0xbd, 0xbe, 0x00, /* 0xb8-0xbf */
+ 0x00, 0x00, 0x00, 0x00, 0xc4, 0xc5, 0xaf, 0x00, /* 0xc0-0xc7 */
+ 0x00, 0xc9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+ 0x00, 0x00, 0x00, 0xd3, 0x00, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xa8, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0xdf, /* 0xd8-0xdf */
+ 0x00, 0x00, 0x00, 0x00, 0xe4, 0xe5, 0xbf, 0x00, /* 0xe0-0xe7 */
+ 0x00, 0xe9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+ 0x00, 0x00, 0x00, 0xf3, 0x00, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xb8, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static unsigned char page01[256] = {
+ 0xc2, 0xe2, 0x00, 0x00, 0xc0, 0xe0, 0xc3, 0xe3, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0xc8, 0xe8, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0xc7, 0xe7, 0x00, 0x00, 0xcb, 0xeb, /* 0x10-0x17 */
+ 0xc6, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0xcc, 0xec, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0xce, 0xee, 0x00, 0x00, 0xc1, 0xe1, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcd, 0xed, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0xcf, 0xef, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0xd9, 0xf9, 0xd1, 0xf1, 0xd2, 0xf2, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0xd4, 0xf4, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xba, /* 0x50-0x57 */
+ 0x00, 0x00, 0xda, 0xfa, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0xd0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0xdb, 0xfb, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0xd8, 0xf8, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0xca, 0xea, 0xdd, 0xfd, 0xde, 0xfe, 0x00, /* 0x78-0x7f */
+};
+
+static unsigned char page20[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0xff, 0x00, 0x00, 0xb4, 0xa1, 0xa5, 0x00, /* 0x18-0x1f */
+};
+
+static unsigned char *page_uni2charset[256] = {
+ page00, page01, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ page20, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+};
+
+static unsigned char charset2lower[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40-0x47 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x48-0x4f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50-0x57 */
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0xb1, 0xa2, 0xb3, 0xa4, 0xb5, 0xb6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xb9, 0xba, 0xbb, 0xbc, 0xad, 0xbe, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbf, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xc0-0xc7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xc8-0xcf */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xd7, /* 0xd0-0xd7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static unsigned char charset2upper[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xa1, 0xb2, 0xa3, 0xb4, 0xa5, 0xa6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xa9, 0xaa, 0xab, 0xac, 0xbd, 0xae, 0xbd, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xe0-0xe7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xe8-0xef */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xf7, /* 0xf0-0xf7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xff, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+ unsigned char *uni2charset;
+ unsigned char cl = uni & 0x00ff;
+ unsigned char ch = (uni & 0xff00) >> 8;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ uni2charset = page_uni2charset[ch];
+ if (uni2charset && uni2charset[cl])
+ out[0] = uni2charset[cl];
+ else
+ return -EINVAL;
+ return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+ *uni = charset2uni[*rawstring];
+ if (*uni == 0x0000)
+ return -EINVAL;
+ return 1;
+}
+
+static struct nls_table table = {
+ "iso8859-13",
+ uni2char,
+ char2uni,
+ charset2lower,
+ charset2upper,
+ THIS_MODULE,
+};
+
+static int __init init_nls_iso8859_13(void)
+{
+ return register_nls(&table);
+}
+
+static void __exit exit_nls_iso8859_13(void)
+{
+ unregister_nls(&table);
+}
+
+module_init(init_nls_iso8859_13)
+module_exit(exit_nls_iso8859_13)
+MODULE_LICENSE("Dual BSD/GPL");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/fs/nls/nls_iso8859-14.c b/fs/nls/nls_iso8859-14.c
new file mode 100644
index 00000000000000..6a032a2d0fbf6d
--- /dev/null
+++ b/fs/nls/nls_iso8859-14.c
@@ -0,0 +1,358 @@
+/*
+ * linux/fs/nls_iso8859-14.c
+ *
+ * Charset iso8859-14 translation tables.
+ *
+ * Generated automatically from the Unicode and charset table
+ * provided by the Unicode Organisation at
+ * http://www.unicode.org/
+ * The Unicode to charset table has only exact mappings.
+ *
+ * Rhys Jones, Swansea University Computer Society
+ * rhys@sucs.swan.ac.uk
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static wchar_t charset2uni[256] = {
+ /* 0x00*/
+ 0x0000, 0x0001, 0x0002, 0x0003,
+ 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b,
+ 0x000c, 0x000d, 0x000e, 0x000f,
+ /* 0x10*/
+ 0x0010, 0x0011, 0x0012, 0x0013,
+ 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b,
+ 0x001c, 0x001d, 0x001e, 0x001f,
+ /* 0x20*/
+ 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b,
+ 0x002c, 0x002d, 0x002e, 0x002f,
+ /* 0x30*/
+ 0x0030, 0x0031, 0x0032, 0x0033,
+ 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b,
+ 0x003c, 0x003d, 0x003e, 0x003f,
+ /* 0x40*/
+ 0x0040, 0x0041, 0x0042, 0x0043,
+ 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b,
+ 0x004c, 0x004d, 0x004e, 0x004f,
+ /* 0x50*/
+ 0x0050, 0x0051, 0x0052, 0x0053,
+ 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b,
+ 0x005c, 0x005d, 0x005e, 0x005f,
+ /* 0x60*/
+ 0x0060, 0x0061, 0x0062, 0x0063,
+ 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b,
+ 0x006c, 0x006d, 0x006e, 0x006f,
+ /* 0x70*/
+ 0x0070, 0x0071, 0x0072, 0x0073,
+ 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b,
+ 0x007c, 0x007d, 0x007e, 0x007f,
+ /* 0x80*/
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0x90*/
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0xa0*/
+ 0x00a0, 0x1e02, 0x1e03, 0x00a3,
+ 0x010a, 0x010b, 0x1e0a, 0x00a7,
+ 0x1e80, 0x00a9, 0x1e82, 0x1e0b,
+ 0x1ef2, 0x00ad, 0x00ae, 0x0178,
+ /* 0xb0*/
+ 0x1e1e, 0x1e1f, 0x0120, 0x0121,
+ 0x1e40, 0x1e41, 0x00b6, 0x1e56,
+ 0x1e81, 0x1e57, 0x1e83, 0x1e60,
+ 0x1ef3, 0x1e84, 0x1e85, 0x1e61,
+ /* 0xc0*/
+ 0x00c0, 0x00c1, 0x00c2, 0x00c3,
+ 0x00c4, 0x00c5, 0x00c6, 0x00c7,
+ 0x00c8, 0x00c9, 0x00ca, 0x00cb,
+ 0x00cc, 0x00cd, 0x00ce, 0x00cf,
+ /* 0xd0*/
+ 0x0174, 0x00d1, 0x00d2, 0x00d3,
+ 0x00d4, 0x00d5, 0x00d6, 0x1e6a,
+ 0x00d8, 0x00d9, 0x00da, 0x00db,
+ 0x00dc, 0x00dd, 0x0176, 0x00df,
+ /* 0xe0*/
+ 0x00e0, 0x00e1, 0x00e2, 0x00e3,
+ 0x00e4, 0x00e5, 0x00e6, 0x00e7,
+ 0x00e8, 0x00e9, 0x00ea, 0x00eb,
+ 0x00ec, 0x00ed, 0x00ee, 0x00ef,
+ /* 0xf0*/
+ 0x0175, 0x00f1, 0x00f2, 0x00f3,
+ 0x00f4, 0x00f5, 0x00f6, 0x1e6b,
+ 0x00f8, 0x00f9, 0x00fa, 0x00fb,
+ 0x00fc, 0x00fd, 0x0177, 0x00ff,
+};
+
+static unsigned char page00[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00, 0xa7, /* 0xa0-0xa7 */
+ 0x00, 0xa9, 0x00, 0x00, 0x00, 0xad, 0xae, 0x00, /* 0xa8-0xaf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb6, 0x00, /* 0xb0-0xb7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0x00, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0x00, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0x00, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0x00, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0x00, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0x00, 0xff, /* 0xf8-0xff */
+};
+
+static unsigned char page01[256] = {
+ 0x00, 0x00, 0xa1, 0xa2, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0xa6, 0xab, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0xb1, /* 0x18-0x1f */
+ 0xb2, 0xb3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0xb4, 0xb5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0xb9, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0xbb, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0xd7, 0xf7, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0xd0, 0xf0, 0xde, 0xfe, /* 0x70-0x77 */
+ 0xaf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0xa8, 0xb8, 0xaa, 0xba, 0xbd, 0xbe, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+ 0x00, 0x00, 0xac, 0xbc, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static unsigned char page1e[256] = {
+ 0x00, 0x00, 0xa1, 0xa2, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0xa6, 0xab, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0xb1, /* 0x18-0x1f */
+ 0xb2, 0xb3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0xb4, 0xb5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0xb9, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0xbb, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0xd7, 0xf7, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0xd0, 0xf0, 0xde, 0xfe, /* 0x70-0x77 */
+ 0xaf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0xa8, 0xb8, 0xaa, 0xba, 0xbd, 0xbe, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+ 0x00, 0x00, 0xac, 0xbc, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static unsigned char *page_uni2charset[256] = {
+ page00, page01, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, page1e, NULL,
+
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+};
+
+static unsigned char charset2lower[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40-0x47 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x48-0x4f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50-0x57 */
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0xa2, 0xa2, 0xa3, 0xab, 0xab, 0xab, 0xa7, /* 0xa0-0xa7 */
+ 0xb8, 0xa9, 0xba, 0xab, 0xbc, 0xad, 0xae, 0xff, /* 0xa8-0xaf */
+ 0xb1, 0xb1, 0xb3, 0xb3, 0xb5, 0xb5, 0xb6, 0xb9, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbf, 0xbc, 0xbe, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xc0-0xc7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xc8-0xcf */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xd0-0xd7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static unsigned char charset2upper[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa1, 0xa3, 0xa6, 0xa6, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xa6, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb0, 0xb2, 0xb2, 0xb4, 0xb4, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xa8, 0xb7, 0xaa, 0xbb, 0xac, 0xbd, 0xbd, 0xbb, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xe0-0xe7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xe8-0xef */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xf0-0xf7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xaf, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+ unsigned char *uni2charset;
+ unsigned char cl = uni & 0x00ff;
+ unsigned char ch = (uni & 0xff00) >> 8;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ uni2charset = page_uni2charset[ch];
+ if (uni2charset && uni2charset[cl])
+ out[0] = uni2charset[cl];
+ else
+ return -EINVAL;
+ return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+ *uni = charset2uni[*rawstring];
+ if (*uni == 0x0000)
+ return -EINVAL;
+ return 1;
+}
+
+static struct nls_table table = {
+ "iso8859-14",
+ uni2char,
+ char2uni,
+ charset2lower,
+ charset2upper,
+ THIS_MODULE,
+};
+
+static int __init init_nls_iso8859_14(void)
+{
+ return register_nls(&table);
+}
+
+static void __exit exit_nls_iso8859_14(void)
+{
+ unregister_nls(&table);
+}
+
+module_init(init_nls_iso8859_14)
+module_exit(exit_nls_iso8859_14)
+MODULE_LICENSE("Dual BSD/GPL");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/fs/nls/nls_iso8859-15.c b/fs/nls/nls_iso8859-15.c
new file mode 100644
index 00000000000000..8cad93f099ae97
--- /dev/null
+++ b/fs/nls/nls_iso8859-15.c
@@ -0,0 +1,324 @@
+/*
+ * linux/fs/nls_iso8859-15.c
+ *
+ * Charset iso8859-15 translation tables.
+ * The Unicode to charset table has only exact mappings.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static wchar_t charset2uni[256] = {
+ /* 0x00*/
+ 0x0000, 0x0001, 0x0002, 0x0003,
+ 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b,
+ 0x000c, 0x000d, 0x000e, 0x000f,
+ /* 0x10*/
+ 0x0010, 0x0011, 0x0012, 0x0013,
+ 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b,
+ 0x001c, 0x001d, 0x001e, 0x001f,
+ /* 0x20*/
+ 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b,
+ 0x002c, 0x002d, 0x002e, 0x002f,
+ /* 0x30*/
+ 0x0030, 0x0031, 0x0032, 0x0033,
+ 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b,
+ 0x003c, 0x003d, 0x003e, 0x003f,
+ /* 0x40*/
+ 0x0040, 0x0041, 0x0042, 0x0043,
+ 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b,
+ 0x004c, 0x004d, 0x004e, 0x004f,
+ /* 0x50*/
+ 0x0050, 0x0051, 0x0052, 0x0053,
+ 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b,
+ 0x005c, 0x005d, 0x005e, 0x005f,
+ /* 0x60*/
+ 0x0060, 0x0061, 0x0062, 0x0063,
+ 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b,
+ 0x006c, 0x006d, 0x006e, 0x006f,
+ /* 0x70*/
+ 0x0070, 0x0071, 0x0072, 0x0073,
+ 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b,
+ 0x007c, 0x007d, 0x007e, 0x007f,
+ /* 0x80*/
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0x90*/
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0xa0*/
+ 0x00a0, 0x00a1, 0x00a2, 0x00a3,
+ 0x20ac, 0x00a5, 0x0160, 0x00a7,
+ 0x0161, 0x00a9, 0x00aa, 0x00ab,
+ 0x00ac, 0x00ad, 0x00ae, 0x00af,
+ /* 0xb0*/
+ 0x00b0, 0x00b1, 0x00b2, 0x00b3,
+ 0x017d, 0x00b5, 0x00b6, 0x00b7,
+ 0x017e, 0x00b9, 0x00ba, 0x00bb,
+ 0x0152, 0x0153, 0x0178, 0x00bf,
+ /* 0xc0*/
+ 0x00c0, 0x00c1, 0x00c2, 0x00c3,
+ 0x00c4, 0x00c5, 0x00c6, 0x00c7,
+ 0x00c8, 0x00c9, 0x00ca, 0x00cb,
+ 0x00cc, 0x00cd, 0x00ce, 0x00cf,
+ /* 0xd0*/
+ 0x00d0, 0x00d1, 0x00d2, 0x00d3,
+ 0x00d4, 0x00d5, 0x00d6, 0x00d7,
+ 0x00d8, 0x00d9, 0x00da, 0x00db,
+ 0x00dc, 0x00dd, 0x00de, 0x00df,
+ /* 0xe0*/
+ 0x00e0, 0x00e1, 0x00e2, 0x00e3,
+ 0x00e4, 0x00e5, 0x00e6, 0x00e7,
+ 0x00e8, 0x00e9, 0x00ea, 0x00eb,
+ 0x00ec, 0x00ed, 0x00ee, 0x00ef,
+ /* 0xf0*/
+ 0x00f0, 0x00f1, 0x00f2, 0x00f3,
+ 0x00f4, 0x00f5, 0x00f6, 0x00f7,
+ 0x00f8, 0x00f9, 0x00fa, 0x00fb,
+ 0x00fc, 0x00fd, 0x00fe, 0x00ff,
+};
+
+static unsigned char page00[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0x00, 0xa5, 0x00, 0xa7, /* 0xa0-0xa7 */
+ 0x00, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0x00, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0x00, 0xb9, 0xba, 0xbb, 0x00, 0x00, 0x00, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static unsigned char page01[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0xbc, 0xbd, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0xa6, 0xa8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0xbe, 0x00, 0x00, 0x00, 0x00, 0xb4, 0xb8, 0x00, /* 0x78-0x7f */
+};
+
+static unsigned char page20[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+ 0x00, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+};
+
+static unsigned char *page_uni2charset[256] = {
+ page00, page01, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+
+ page20, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+};
+
+static unsigned char charset2lower[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40-0x47 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x48-0x4f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50-0x57 */
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa8, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb8, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbd, 0xbd, 0xff, 0xbf, /* 0xb8-0xbf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xc0-0xc7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xc8-0xcf */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xd7, /* 0xd0-0xd7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static unsigned char charset2upper[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa6, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0x00, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb4, 0xb9, 0xba, 0xbb, 0xbc, 0xbc, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xe0-0xe7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xe8-0xef */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xf7, /* 0xf0-0xf7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xbe, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+ unsigned char *uni2charset;
+ unsigned char cl = uni & 0x00ff;
+ unsigned char ch = (uni & 0xff00) >> 8;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ uni2charset = page_uni2charset[ch];
+ if (uni2charset && uni2charset[cl])
+ out[0] = uni2charset[cl];
+ else
+ return -EINVAL;
+ return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+ *uni = charset2uni[*rawstring];
+ if (*uni == 0x0000)
+ return -EINVAL;
+ return 1;
+}
+
+static struct nls_table table = {
+ "iso8859-15",
+ uni2char,
+ char2uni,
+ charset2lower,
+ charset2upper,
+ THIS_MODULE,
+};
+
+static int __init init_nls_iso8859_15(void)
+{
+ return register_nls(&table);
+}
+
+static void __exit exit_nls_iso8859_15(void)
+{
+ unregister_nls(&table);
+}
+
+module_init(init_nls_iso8859_15)
+module_exit(exit_nls_iso8859_15)
+MODULE_LICENSE("Dual BSD/GPL");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/fs/nls/nls_iso8859-2.c b/fs/nls/nls_iso8859-2.c
new file mode 100644
index 00000000000000..597b658bb0ec80
--- /dev/null
+++ b/fs/nls/nls_iso8859-2.c
@@ -0,0 +1,325 @@
+/*
+ * linux/fs/nls_iso8859-2.c
+ *
+ * Charset iso8859-2 translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static wchar_t charset2uni[256] = {
+ /* 0x00*/
+ 0x0000, 0x0001, 0x0002, 0x0003,
+ 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b,
+ 0x000c, 0x000d, 0x000e, 0x000f,
+ /* 0x10*/
+ 0x0010, 0x0011, 0x0012, 0x0013,
+ 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b,
+ 0x001c, 0x001d, 0x001e, 0x001f,
+ /* 0x20*/
+ 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b,
+ 0x002c, 0x002d, 0x002e, 0x002f,
+ /* 0x30*/
+ 0x0030, 0x0031, 0x0032, 0x0033,
+ 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b,
+ 0x003c, 0x003d, 0x003e, 0x003f,
+ /* 0x40*/
+ 0x0040, 0x0041, 0x0042, 0x0043,
+ 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b,
+ 0x004c, 0x004d, 0x004e, 0x004f,
+ /* 0x50*/
+ 0x0050, 0x0051, 0x0052, 0x0053,
+ 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b,
+ 0x005c, 0x005d, 0x005e, 0x005f,
+ /* 0x60*/
+ 0x0060, 0x0061, 0x0062, 0x0063,
+ 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b,
+ 0x006c, 0x006d, 0x006e, 0x006f,
+ /* 0x70*/
+ 0x0070, 0x0071, 0x0072, 0x0073,
+ 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b,
+ 0x007c, 0x007d, 0x007e, 0x007f,
+ /* 0x80*/
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0x90*/
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0xa0*/
+ 0x00a0, 0x0104, 0x02d8, 0x0141,
+ 0x00a4, 0x013d, 0x015a, 0x00a7,
+ 0x00a8, 0x0160, 0x015e, 0x0164,
+ 0x0179, 0x00ad, 0x017d, 0x017b,
+ /* 0xb0*/
+ 0x00b0, 0x0105, 0x02db, 0x0142,
+ 0x00b4, 0x013e, 0x015b, 0x02c7,
+ 0x00b8, 0x0161, 0x015f, 0x0165,
+ 0x017a, 0x02dd, 0x017e, 0x017c,
+ /* 0xc0*/
+ 0x0154, 0x00c1, 0x00c2, 0x0102,
+ 0x00c4, 0x0139, 0x0106, 0x00c7,
+ 0x010c, 0x00c9, 0x0118, 0x00cb,
+ 0x011a, 0x00cd, 0x00ce, 0x010e,
+ /* 0xd0*/
+ 0x0110, 0x0143, 0x0147, 0x00d3,
+ 0x00d4, 0x0150, 0x00d6, 0x00d7,
+ 0x0158, 0x016e, 0x00da, 0x0170,
+ 0x00dc, 0x00dd, 0x0162, 0x00df,
+ /* 0xe0*/
+ 0x0155, 0x00e1, 0x00e2, 0x0103,
+ 0x00e4, 0x013a, 0x0107, 0x00e7,
+ 0x010d, 0x00e9, 0x0119, 0x00eb,
+ 0x011b, 0x00ed, 0x00ee, 0x010f,
+ /* 0xf0*/
+ 0x0111, 0x0144, 0x0148, 0x00f3,
+ 0x00f4, 0x0151, 0x00f6, 0x00f7,
+ 0x0159, 0x016f, 0x00fa, 0x0171,
+ 0x00fc, 0x00fd, 0x0163, 0x02d9,
+};
+
+static unsigned char page00[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0x00, 0x00, 0x00, 0x00, 0xad, 0x00, 0x00, /* 0xa8-0xaf */
+ 0xb0, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+ 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+ 0x00, 0xc1, 0xc2, 0x00, 0xc4, 0x00, 0x00, 0xc7, /* 0xc0-0xc7 */
+ 0x00, 0xc9, 0x00, 0xcb, 0x00, 0xcd, 0xce, 0x00, /* 0xc8-0xcf */
+ 0x00, 0x00, 0x00, 0xd3, 0xd4, 0x00, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0x00, 0x00, 0xda, 0x00, 0xdc, 0xdd, 0x00, 0xdf, /* 0xd8-0xdf */
+ 0x00, 0xe1, 0xe2, 0x00, 0xe4, 0x00, 0x00, 0xe7, /* 0xe0-0xe7 */
+ 0x00, 0xe9, 0x00, 0xeb, 0x00, 0xed, 0xee, 0x00, /* 0xe8-0xef */
+ 0x00, 0x00, 0x00, 0xf3, 0xf4, 0x00, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0x00, 0x00, 0xfa, 0x00, 0xfc, 0xfd, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static unsigned char page01[256] = {
+ 0x00, 0x00, 0xc3, 0xe3, 0xa1, 0xb1, 0xc6, 0xe6, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0xc8, 0xe8, 0xcf, 0xef, /* 0x08-0x0f */
+ 0xd0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0xca, 0xea, 0xcc, 0xec, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0xc5, 0xe5, 0x00, 0x00, 0xa5, 0xb5, 0x00, /* 0x38-0x3f */
+ 0x00, 0xa3, 0xb3, 0xd1, 0xf1, 0x00, 0x00, 0xd2, /* 0x40-0x47 */
+ 0xf2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0xd5, 0xf5, 0x00, 0x00, 0xc0, 0xe0, 0x00, 0x00, /* 0x50-0x57 */
+ 0xd8, 0xf8, 0xa6, 0xb6, 0x00, 0x00, 0xaa, 0xba, /* 0x58-0x5f */
+ 0xa9, 0xb9, 0xde, 0xfe, 0xab, 0xbb, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd9, 0xf9, /* 0x68-0x6f */
+ 0xdb, 0xfb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0xac, 0xbc, 0xaf, 0xbf, 0xae, 0xbe, 0x00, /* 0x78-0x7f */
+};
+
+static unsigned char page02[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, /* 0xc0-0xc7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+ 0xa2, 0xff, 0x00, 0xb2, 0x00, 0xbd, 0x00, 0x00, /* 0xd8-0xdf */
+};
+
+static unsigned char *page_uni2charset[256] = {
+ page00, page01, page02, NULL, NULL, NULL, NULL, NULL,
+};
+
+static unsigned char charset2lower[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40-0x47 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x48-0x4f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50-0x57 */
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0xb1, 0xa2, 0xb3, 0xa4, 0xb5, 0xb6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xb9, 0xba, 0xbb, 0xbc, 0xad, 0xbe, 0xbf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xc0-0xc7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xc8-0xcf */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xd7, /* 0xd0-0xd7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static unsigned char charset2upper[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xa1, 0xb2, 0xa3, 0xb4, 0xa5, 0xa6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xa9, 0xaa, 0xab, 0xac, 0xbd, 0xae, 0xaf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xe0-0xe7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xe8-0xef */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xf7, /* 0xf0-0xf7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xff, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+ unsigned char *uni2charset;
+ unsigned char cl = uni & 0x00ff;
+ unsigned char ch = (uni & 0xff00) >> 8;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ uni2charset = page_uni2charset[ch];
+ if (uni2charset && uni2charset[cl])
+ out[0] = uni2charset[cl];
+ else
+ return -EINVAL;
+ return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+ *uni = charset2uni[*rawstring];
+ if (*uni == 0x0000)
+ return -EINVAL;
+ return 1;
+}
+
+static struct nls_table table = {
+ "iso8859-2",
+ uni2char,
+ char2uni,
+ charset2lower,
+ charset2upper,
+ THIS_MODULE,
+};
+
+static int __init init_nls_iso8859_2(void)
+{
+ return register_nls(&table);
+}
+
+static void __exit exit_nls_iso8859_2(void)
+{
+ unregister_nls(&table);
+}
+
+module_init(init_nls_iso8859_2)
+module_exit(exit_nls_iso8859_2)
+MODULE_LICENSE("Dual BSD/GPL");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/fs/nls/nls_iso8859-3.c b/fs/nls/nls_iso8859-3.c
new file mode 100644
index 00000000000000..b49abeef466c46
--- /dev/null
+++ b/fs/nls/nls_iso8859-3.c
@@ -0,0 +1,325 @@
+/*
+ * linux/fs/nls_iso8859-3.c
+ *
+ * Charset iso8859-3 translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static wchar_t charset2uni[256] = {
+ /* 0x00*/
+ 0x0000, 0x0001, 0x0002, 0x0003,
+ 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b,
+ 0x000c, 0x000d, 0x000e, 0x000f,
+ /* 0x10*/
+ 0x0010, 0x0011, 0x0012, 0x0013,
+ 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b,
+ 0x001c, 0x001d, 0x001e, 0x001f,
+ /* 0x20*/
+ 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b,
+ 0x002c, 0x002d, 0x002e, 0x002f,
+ /* 0x30*/
+ 0x0030, 0x0031, 0x0032, 0x0033,
+ 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b,
+ 0x003c, 0x003d, 0x003e, 0x003f,
+ /* 0x40*/
+ 0x0040, 0x0041, 0x0042, 0x0043,
+ 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b,
+ 0x004c, 0x004d, 0x004e, 0x004f,
+ /* 0x50*/
+ 0x0050, 0x0051, 0x0052, 0x0053,
+ 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b,
+ 0x005c, 0x005d, 0x005e, 0x005f,
+ /* 0x60*/
+ 0x0060, 0x0061, 0x0062, 0x0063,
+ 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b,
+ 0x006c, 0x006d, 0x006e, 0x006f,
+ /* 0x70*/
+ 0x0070, 0x0071, 0x0072, 0x0073,
+ 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b,
+ 0x007c, 0x007d, 0x007e, 0x007f,
+ /* 0x80*/
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0x90*/
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0xa0*/
+ 0x00a0, 0x0126, 0x02d8, 0x00a3,
+ 0x00a4, 0x0000, 0x0124, 0x00a7,
+ 0x00a8, 0x0130, 0x015e, 0x011e,
+ 0x0134, 0x00ad, 0x0000, 0x017b,
+ /* 0xb0*/
+ 0x00b0, 0x0127, 0x00b2, 0x00b3,
+ 0x00b4, 0x00b5, 0x0125, 0x00b7,
+ 0x00b8, 0x0131, 0x015f, 0x011f,
+ 0x0135, 0x00bd, 0x0000, 0x017c,
+ /* 0xc0*/
+ 0x00c0, 0x00c1, 0x00c2, 0x0000,
+ 0x00c4, 0x010a, 0x0108, 0x00c7,
+ 0x00c8, 0x00c9, 0x00ca, 0x00cb,
+ 0x00cc, 0x00cd, 0x00ce, 0x00cf,
+ /* 0xd0*/
+ 0x0000, 0x00d1, 0x00d2, 0x00d3,
+ 0x00d4, 0x0120, 0x00d6, 0x00d7,
+ 0x011c, 0x00d9, 0x00da, 0x00db,
+ 0x00dc, 0x016c, 0x015c, 0x00df,
+ /* 0xe0*/
+ 0x00e0, 0x00e1, 0x00e2, 0x0000,
+ 0x00e4, 0x010b, 0x0109, 0x00e7,
+ 0x00e8, 0x00e9, 0x00ea, 0x00eb,
+ 0x00ec, 0x00ed, 0x00ee, 0x00ef,
+ /* 0xf0*/
+ 0x0000, 0x00f1, 0x00f2, 0x00f3,
+ 0x00f4, 0x0121, 0x00f6, 0x00f7,
+ 0x011d, 0x00f9, 0x00fa, 0x00fb,
+ 0x00fc, 0x016d, 0x015d, 0x02d9,
+};
+
+static unsigned char page00[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0x00, 0x00, 0xa3, 0xa4, 0x00, 0x00, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0x00, 0x00, 0x00, 0x00, 0xad, 0x00, 0x00, /* 0xa8-0xaf */
+ 0xb0, 0x00, 0xb2, 0xb3, 0xb4, 0xb5, 0x00, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0x00, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0x00, 0xc4, 0x00, 0x00, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0x00, 0xd1, 0xd2, 0xd3, 0xd4, 0x00, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0x00, 0xd9, 0xda, 0xdb, 0xdc, 0x00, 0x00, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0x00, 0xe4, 0x00, 0x00, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0x00, 0xf1, 0xf2, 0xf3, 0xf4, 0x00, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0x00, 0xf9, 0xfa, 0xfb, 0xfc, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static unsigned char page01[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0xc6, 0xe6, 0xc5, 0xe5, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0xd8, 0xf8, 0xab, 0xbb, /* 0x18-0x1f */
+ 0xd5, 0xf5, 0x00, 0x00, 0xa6, 0xb6, 0xa1, 0xb1, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0xa9, 0xb9, 0x00, 0x00, 0xac, 0xbc, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0xde, 0xfe, 0xaa, 0xba, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0xdd, 0xfd, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0xaf, 0xbf, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+};
+
+static unsigned char page02[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+ 0xa2, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+};
+
+static unsigned char *page_uni2charset[256] = {
+ page00, page01, page02, NULL, NULL, NULL, NULL, NULL,
+};
+
+static unsigned char charset2lower[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40-0x47 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x48-0x4f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50-0x57 */
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0xb1, 0xa2, 0xa3, 0xa4, 0x00, 0xb6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0x69, 0xba, 0xbb, 0xbc, 0xad, 0x00, 0xbf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0x00, 0xbf, /* 0xb8-0xbf */
+ 0xe0, 0xe1, 0xe2, 0x00, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xc0-0xc7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xc8-0xcf */
+ 0x00, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xd7, /* 0xd0-0xd7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0x00, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0x00, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static unsigned char charset2upper[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0x00, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0x00, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xa1, 0xb2, 0xb3, 0xb4, 0x00, 0xa6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0x49, 0xaa, 0xab, 0xac, 0xbd, 0x00, 0xaf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0x00, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0x00, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xc0, 0xc1, 0xc2, 0x00, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xe0-0xe7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xe8-0xef */
+ 0x00, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xf7, /* 0xf0-0xf7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xff, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+ unsigned char *uni2charset;
+ unsigned char cl = uni & 0x00ff;
+ unsigned char ch = (uni & 0xff00) >> 8;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ uni2charset = page_uni2charset[ch];
+ if (uni2charset && uni2charset[cl])
+ out[0] = uni2charset[cl];
+ else
+ return -EINVAL;
+ return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+ *uni = charset2uni[*rawstring];
+ if (*uni == 0x0000)
+ return -EINVAL;
+ return 1;
+}
+
+static struct nls_table table = {
+ "iso8859-3",
+ uni2char,
+ char2uni,
+ charset2lower,
+ charset2upper,
+ THIS_MODULE,
+};
+
+static int __init init_nls_iso8859_3(void)
+{
+ return register_nls(&table);
+}
+
+static void __exit exit_nls_iso8859_3(void)
+{
+ unregister_nls(&table);
+}
+
+module_init(init_nls_iso8859_3)
+module_exit(exit_nls_iso8859_3)
+MODULE_LICENSE("Dual BSD/GPL");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/fs/nls/nls_iso8859-4.c b/fs/nls/nls_iso8859-4.c
new file mode 100644
index 00000000000000..7f263e3f3fe495
--- /dev/null
+++ b/fs/nls/nls_iso8859-4.c
@@ -0,0 +1,325 @@
+/*
+ * linux/fs/nls_iso8859-4.c
+ *
+ * Charset iso8859-4 translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static wchar_t charset2uni[256] = {
+ /* 0x00*/
+ 0x0000, 0x0001, 0x0002, 0x0003,
+ 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b,
+ 0x000c, 0x000d, 0x000e, 0x000f,
+ /* 0x10*/
+ 0x0010, 0x0011, 0x0012, 0x0013,
+ 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b,
+ 0x001c, 0x001d, 0x001e, 0x001f,
+ /* 0x20*/
+ 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b,
+ 0x002c, 0x002d, 0x002e, 0x002f,
+ /* 0x30*/
+ 0x0030, 0x0031, 0x0032, 0x0033,
+ 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b,
+ 0x003c, 0x003d, 0x003e, 0x003f,
+ /* 0x40*/
+ 0x0040, 0x0041, 0x0042, 0x0043,
+ 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b,
+ 0x004c, 0x004d, 0x004e, 0x004f,
+ /* 0x50*/
+ 0x0050, 0x0051, 0x0052, 0x0053,
+ 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b,
+ 0x005c, 0x005d, 0x005e, 0x005f,
+ /* 0x60*/
+ 0x0060, 0x0061, 0x0062, 0x0063,
+ 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b,
+ 0x006c, 0x006d, 0x006e, 0x006f,
+ /* 0x70*/
+ 0x0070, 0x0071, 0x0072, 0x0073,
+ 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b,
+ 0x007c, 0x007d, 0x007e, 0x007f,
+ /* 0x80*/
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0x90*/
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0xa0*/
+ 0x00a0, 0x0104, 0x0138, 0x0156,
+ 0x00a4, 0x0128, 0x013b, 0x00a7,
+ 0x00a8, 0x0160, 0x0112, 0x0122,
+ 0x0166, 0x00ad, 0x017d, 0x00af,
+ /* 0xb0*/
+ 0x00b0, 0x0105, 0x02db, 0x0157,
+ 0x00b4, 0x0129, 0x013c, 0x02c7,
+ 0x00b8, 0x0161, 0x0113, 0x0123,
+ 0x0167, 0x014a, 0x017e, 0x014b,
+ /* 0xc0*/
+ 0x0100, 0x00c1, 0x00c2, 0x00c3,
+ 0x00c4, 0x00c5, 0x00c6, 0x012e,
+ 0x010c, 0x00c9, 0x0118, 0x00cb,
+ 0x0116, 0x00cd, 0x00ce, 0x012a,
+ /* 0xd0*/
+ 0x0110, 0x0145, 0x014c, 0x0136,
+ 0x00d4, 0x00d5, 0x00d6, 0x00d7,
+ 0x00d8, 0x0172, 0x00da, 0x00db,
+ 0x00dc, 0x0168, 0x016a, 0x00df,
+ /* 0xe0*/
+ 0x0101, 0x00e1, 0x00e2, 0x00e3,
+ 0x00e4, 0x00e5, 0x00e6, 0x012f,
+ 0x010d, 0x00e9, 0x0119, 0x00eb,
+ 0x0117, 0x00ed, 0x00ee, 0x012b,
+ /* 0xf0*/
+ 0x0111, 0x0146, 0x014d, 0x0137,
+ 0x00f4, 0x00f5, 0x00f6, 0x00f7,
+ 0x00f8, 0x0173, 0x00fa, 0x00fb,
+ 0x00fc, 0x0169, 0x016b, 0x02d9,
+};
+
+static unsigned char page00[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0x00, 0x00, 0x00, 0x00, 0xad, 0x00, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+ 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+ 0x00, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0x00, /* 0xc0-0xc7 */
+ 0x00, 0xc9, 0x00, 0xcb, 0x00, 0xcd, 0xce, 0x00, /* 0xc8-0xcf */
+ 0x00, 0x00, 0x00, 0x00, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0x00, 0xda, 0xdb, 0xdc, 0x00, 0x00, 0xdf, /* 0xd8-0xdf */
+ 0x00, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0x00, /* 0xe0-0xe7 */
+ 0x00, 0xe9, 0x00, 0xeb, 0x00, 0xed, 0xee, 0x00, /* 0xe8-0xef */
+ 0x00, 0x00, 0x00, 0x00, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0x00, 0xfa, 0xfb, 0xfc, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static unsigned char page01[256] = {
+ 0xc0, 0xe0, 0x00, 0x00, 0xa1, 0xb1, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0xc8, 0xe8, 0x00, 0x00, /* 0x08-0x0f */
+ 0xd0, 0xf0, 0xaa, 0xba, 0x00, 0x00, 0xcc, 0xec, /* 0x10-0x17 */
+ 0xca, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0xab, 0xbb, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0xa5, 0xb5, 0xcf, 0xef, 0x00, 0x00, 0xc7, 0xe7, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd3, 0xf3, /* 0x30-0x37 */
+ 0xa2, 0x00, 0x00, 0xa6, 0xb6, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xd1, 0xf1, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0xbd, 0xbf, 0xd2, 0xf2, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa3, 0xb3, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0xa9, 0xb9, 0x00, 0x00, 0x00, 0x00, 0xac, 0xbc, /* 0x60-0x67 */
+ 0xdd, 0xfd, 0xde, 0xfe, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0xd9, 0xf9, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xae, 0xbe, 0x00, /* 0x78-0x7f */
+};
+
+static unsigned char page02[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, /* 0xc0-0xc7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+ 0x00, 0xff, 0x00, 0xb2, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+};
+
+static unsigned char *page_uni2charset[256] = {
+ page00, page01, page02, NULL, NULL, NULL, NULL, NULL,
+};
+
+static unsigned char charset2lower[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40-0x47 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x48-0x4f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50-0x57 */
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0xb1, 0xa2, 0xb3, 0xa4, 0xb5, 0xb6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xb9, 0xba, 0xbb, 0xbc, 0xad, 0xbe, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbf, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xc0-0xc7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xc8-0xcf */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xd7, /* 0xd0-0xd7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static unsigned char charset2upper[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xa1, 0xb2, 0xa3, 0xb4, 0xa5, 0xa6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xa9, 0xaa, 0xab, 0xac, 0xbd, 0xae, 0xbd, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xe0-0xe7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xe8-0xef */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xf7, /* 0xf0-0xf7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xff, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+ unsigned char *uni2charset;
+ unsigned char cl = uni & 0x00ff;
+ unsigned char ch = (uni & 0xff00) >> 8;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ uni2charset = page_uni2charset[ch];
+ if (uni2charset && uni2charset[cl])
+ out[0] = uni2charset[cl];
+ else
+ return -EINVAL;
+ return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+ *uni = charset2uni[*rawstring];
+ if (*uni == 0x0000)
+ return -EINVAL;
+ return 1;
+}
+
+static struct nls_table table = {
+ "iso8859-4",
+ uni2char,
+ char2uni,
+ charset2lower,
+ charset2upper,
+ THIS_MODULE,
+};
+
+static int __init init_nls_iso8859_4(void)
+{
+ return register_nls(&table);
+}
+
+static void __exit exit_nls_iso8859_4(void)
+{
+ unregister_nls(&table);
+}
+
+module_init(init_nls_iso8859_4)
+module_exit(exit_nls_iso8859_4)
+MODULE_LICENSE("Dual BSD/GPL");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/fs/nls/nls_iso8859-5.c b/fs/nls/nls_iso8859-5.c
new file mode 100644
index 00000000000000..0edba68882877a
--- /dev/null
+++ b/fs/nls/nls_iso8859-5.c
@@ -0,0 +1,289 @@
+/*
+ * linux/fs/nls_iso8859-5.c
+ *
+ * Charset iso8859-5 translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static wchar_t charset2uni[256] = {
+ /* 0x00*/
+ 0x0000, 0x0001, 0x0002, 0x0003,
+ 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b,
+ 0x000c, 0x000d, 0x000e, 0x000f,
+ /* 0x10*/
+ 0x0010, 0x0011, 0x0012, 0x0013,
+ 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b,
+ 0x001c, 0x001d, 0x001e, 0x001f,
+ /* 0x20*/
+ 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b,
+ 0x002c, 0x002d, 0x002e, 0x002f,
+ /* 0x30*/
+ 0x0030, 0x0031, 0x0032, 0x0033,
+ 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b,
+ 0x003c, 0x003d, 0x003e, 0x003f,
+ /* 0x40*/
+ 0x0040, 0x0041, 0x0042, 0x0043,
+ 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b,
+ 0x004c, 0x004d, 0x004e, 0x004f,
+ /* 0x50*/
+ 0x0050, 0x0051, 0x0052, 0x0053,
+ 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b,
+ 0x005c, 0x005d, 0x005e, 0x005f,
+ /* 0x60*/
+ 0x0060, 0x0061, 0x0062, 0x0063,
+ 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b,
+ 0x006c, 0x006d, 0x006e, 0x006f,
+ /* 0x70*/
+ 0x0070, 0x0071, 0x0072, 0x0073,
+ 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b,
+ 0x007c, 0x007d, 0x007e, 0x007f,
+ /* 0x80*/
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0x90*/
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0xa0*/
+ 0x00a0, 0x0401, 0x0402, 0x0403,
+ 0x0404, 0x0405, 0x0406, 0x0407,
+ 0x0408, 0x0409, 0x040a, 0x040b,
+ 0x040c, 0x00ad, 0x040e, 0x040f,
+ /* 0xb0*/
+ 0x0410, 0x0411, 0x0412, 0x0413,
+ 0x0414, 0x0415, 0x0416, 0x0417,
+ 0x0418, 0x0419, 0x041a, 0x041b,
+ 0x041c, 0x041d, 0x041e, 0x041f,
+ /* 0xc0*/
+ 0x0420, 0x0421, 0x0422, 0x0423,
+ 0x0424, 0x0425, 0x0426, 0x0427,
+ 0x0428, 0x0429, 0x042a, 0x042b,
+ 0x042c, 0x042d, 0x042e, 0x042f,
+ /* 0xd0*/
+ 0x0430, 0x0431, 0x0432, 0x0433,
+ 0x0434, 0x0435, 0x0436, 0x0437,
+ 0x0438, 0x0439, 0x043a, 0x043b,
+ 0x043c, 0x043d, 0x043e, 0x043f,
+ /* 0xe0*/
+ 0x0440, 0x0441, 0x0442, 0x0443,
+ 0x0444, 0x0445, 0x0446, 0x0447,
+ 0x0448, 0x0449, 0x044a, 0x044b,
+ 0x044c, 0x044d, 0x044e, 0x044f,
+ /* 0xf0*/
+ 0x2116, 0x0451, 0x0452, 0x0453,
+ 0x0454, 0x0455, 0x0456, 0x0457,
+ 0x0458, 0x0459, 0x045a, 0x045b,
+ 0x045c, 0x00a7, 0x045e, 0x045f,
+};
+
+static unsigned char page00[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, /* 0xa0-0xa7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xad, 0x00, 0x00, /* 0xa8-0xaf */
+};
+
+static unsigned char page04[256] = {
+ 0x00, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0x00-0x07 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0x00, 0xae, 0xaf, /* 0x08-0x0f */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0x10-0x17 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0x18-0x1f */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0x20-0x27 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0x28-0x2f */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0x30-0x37 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0x38-0x3f */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0x40-0x47 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0x48-0x4f */
+ 0x00, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0x50-0x57 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0x00, 0xfe, 0xff, /* 0x58-0x5f */
+};
+
+static unsigned char page21[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, /* 0x10-0x17 */
+};
+
+static unsigned char *page_uni2charset[256] = {
+ page00, NULL, NULL, NULL, page04, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, page21, NULL, NULL, NULL, NULL, NULL, NULL,
+};
+
+static unsigned char charset2lower[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40-0x47 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x48-0x4f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50-0x57 */
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xa0-0xa7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xad, 0xfe, 0xff, /* 0xa8-0xaf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xb0-0xb7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xb8-0xbf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xc0-0xc7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static unsigned char charset2upper[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xd0-0xd7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xd8-0xdf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xe0-0xe7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xe8-0xef */
+ 0xf0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xf0-0xf7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xfd, 0xae, 0xaf, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+ unsigned char *uni2charset;
+ unsigned char cl = uni & 0x00ff;
+ unsigned char ch = (uni & 0xff00) >> 8;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ uni2charset = page_uni2charset[ch];
+ if (uni2charset && uni2charset[cl])
+ out[0] = uni2charset[cl];
+ else
+ return -EINVAL;
+ return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+ *uni = charset2uni[*rawstring];
+ if (*uni == 0x0000)
+ return -EINVAL;
+ return 1;
+}
+
+static struct nls_table table = {
+ "iso8859-5",
+ uni2char,
+ char2uni,
+ charset2lower,
+ charset2upper,
+ THIS_MODULE,
+};
+
+static int __init init_nls_iso8859_5(void)
+{
+ return register_nls(&table);
+}
+
+static void __exit exit_nls_iso8859_5(void)
+{
+ unregister_nls(&table);
+}
+
+module_init(init_nls_iso8859_5)
+module_exit(exit_nls_iso8859_5)
+MODULE_LICENSE("Dual BSD/GPL");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/fs/nls/nls_iso8859-6.c b/fs/nls/nls_iso8859-6.c
new file mode 100644
index 00000000000000..403b59cf0933b2
--- /dev/null
+++ b/fs/nls/nls_iso8859-6.c
@@ -0,0 +1,280 @@
+/*
+ * linux/fs/nls_iso8859-6.c
+ *
+ * Charset iso8859-6 translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static wchar_t charset2uni[256] = {
+ /* 0x00*/
+ 0x0000, 0x0001, 0x0002, 0x0003,
+ 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b,
+ 0x000c, 0x000d, 0x000e, 0x000f,
+ /* 0x10*/
+ 0x0010, 0x0011, 0x0012, 0x0013,
+ 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b,
+ 0x001c, 0x001d, 0x001e, 0x001f,
+ /* 0x20*/
+ 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b,
+ 0x002c, 0x002d, 0x002e, 0x002f,
+ /* 0x30*/
+ 0x0660, 0x0661, 0x0662, 0x0663,
+ 0x0664, 0x0665, 0x0666, 0x0667,
+ 0x0668, 0x0669, 0x003a, 0x003b,
+ 0x003c, 0x003d, 0x003e, 0x003f,
+ /* 0x40*/
+ 0x0040, 0x0041, 0x0042, 0x0043,
+ 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b,
+ 0x004c, 0x004d, 0x004e, 0x004f,
+ /* 0x50*/
+ 0x0050, 0x0051, 0x0052, 0x0053,
+ 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b,
+ 0x005c, 0x005d, 0x005e, 0x005f,
+ /* 0x60*/
+ 0x0060, 0x0061, 0x0062, 0x0063,
+ 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b,
+ 0x006c, 0x006d, 0x006e, 0x006f,
+ /* 0x70*/
+ 0x0070, 0x0071, 0x0072, 0x0073,
+ 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b,
+ 0x007c, 0x007d, 0x007e, 0x007f,
+ /* 0x80*/
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0x90*/
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0xa0*/
+ 0x00a0, 0x0000, 0x0000, 0x0000,
+ 0x00a4, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x060c, 0x00ad, 0x0000, 0x0000,
+ /* 0xb0*/
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x061b,
+ 0x0000, 0x0000, 0x0000, 0x061f,
+ /* 0xc0*/
+ 0x0000, 0x0621, 0x0622, 0x0623,
+ 0x0624, 0x0625, 0x0626, 0x0627,
+ 0x0628, 0x0629, 0x062a, 0x062b,
+ 0x062c, 0x062d, 0x062e, 0x062f,
+ /* 0xd0*/
+ 0x0630, 0x0631, 0x0632, 0x0633,
+ 0x0634, 0x0635, 0x0636, 0x0637,
+ 0x0638, 0x0639, 0x063a, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0xe0*/
+ 0x0640, 0x0641, 0x0642, 0x0643,
+ 0x0644, 0x0645, 0x0646, 0x0647,
+ 0x0648, 0x0649, 0x064a, 0x064b,
+ 0x064c, 0x064d, 0x064e, 0x064f,
+ /* 0xf0*/
+ 0x0650, 0x0651, 0x0652, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+};
+
+static unsigned char page00[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xad, 0x00, 0x00, /* 0xa8-0xaf */
+};
+
+static unsigned char page06[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0xac, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0xbb, 0x00, 0x00, 0x00, 0xbf, /* 0x18-0x1f */
+ 0x00, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0x20-0x27 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0x28-0x2f */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0x30-0x37 */
+ 0xd8, 0xd9, 0xda, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0x40-0x47 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0x48-0x4f */
+ 0xf0, 0xf1, 0xf2, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x60-0x67 */
+ 0x38, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+};
+
+static unsigned char *page_uni2charset[256] = {
+ page00, NULL, NULL, NULL, NULL, NULL, page06, NULL,
+};
+
+static unsigned char charset2lower[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40-0x47 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x48-0x4f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50-0x57 */
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+ 0x00, 0x00, 0x00, 0x00, 0xac, 0xad, 0x00, 0x00, /* 0xa8-0xaf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+ 0x00, 0x00, 0x00, 0xbb, 0x00, 0x00, 0x00, 0xbf, /* 0xb8-0xbf */
+ 0x00, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+static unsigned char charset2upper[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+ 0x00, 0x00, 0x00, 0x00, 0xac, 0xad, 0x00, 0x00, /* 0xa8-0xaf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+ 0x00, 0x00, 0x00, 0xbb, 0x00, 0x00, 0x00, 0xbf, /* 0xb8-0xbf */
+ 0x00, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xf0-0xf7 */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+ unsigned char *uni2charset;
+ unsigned char cl = uni & 0x00ff;
+ unsigned char ch = (uni & 0xff00) >> 8;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ uni2charset = page_uni2charset[ch];
+ if (uni2charset && uni2charset[cl])
+ out[0] = uni2charset[cl];
+ else
+ return -EINVAL;
+ return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+ *uni = charset2uni[*rawstring];
+ if (*uni == 0x0000)
+ return -EINVAL;
+ return 1;
+}
+
+static struct nls_table table = {
+ "iso8859-6",
+ uni2char,
+ char2uni,
+ charset2lower,
+ charset2upper,
+ THIS_MODULE,
+};
+
+static int __init init_nls_iso8859_6(void)
+{
+ return register_nls(&table);
+}
+
+static void __exit exit_nls_iso8859_6(void)
+{
+ unregister_nls(&table);
+}
+
+module_init(init_nls_iso8859_6)
+module_exit(exit_nls_iso8859_6)
+MODULE_LICENSE("Dual BSD/GPL");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/fs/nls/nls_iso8859-7.c b/fs/nls/nls_iso8859-7.c
new file mode 100644
index 00000000000000..6dd6d55c448275
--- /dev/null
+++ b/fs/nls/nls_iso8859-7.c
@@ -0,0 +1,334 @@
+/*
+ * linux/fs/nls_iso8859-7.c
+ *
+ * Charset iso8859-7 translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static wchar_t charset2uni[256] = {
+ /* 0x00*/
+ 0x0000, 0x0001, 0x0002, 0x0003,
+ 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b,
+ 0x000c, 0x000d, 0x000e, 0x000f,
+ /* 0x10*/
+ 0x0010, 0x0011, 0x0012, 0x0013,
+ 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b,
+ 0x001c, 0x001d, 0x001e, 0x001f,
+ /* 0x20*/
+ 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b,
+ 0x002c, 0x002d, 0x002e, 0x002f,
+ /* 0x30*/
+ 0x0030, 0x0031, 0x0032, 0x0033,
+ 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b,
+ 0x003c, 0x003d, 0x003e, 0x003f,
+ /* 0x40*/
+ 0x0040, 0x0041, 0x0042, 0x0043,
+ 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b,
+ 0x004c, 0x004d, 0x004e, 0x004f,
+ /* 0x50*/
+ 0x0050, 0x0051, 0x0052, 0x0053,
+ 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b,
+ 0x005c, 0x005d, 0x005e, 0x005f,
+ /* 0x60*/
+ 0x0060, 0x0061, 0x0062, 0x0063,
+ 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b,
+ 0x006c, 0x006d, 0x006e, 0x006f,
+ /* 0x70*/
+ 0x0070, 0x0071, 0x0072, 0x0073,
+ 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b,
+ 0x007c, 0x007d, 0x007e, 0x007f,
+ /* 0x80*/
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0x90*/
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0xa0*/
+ 0x00a0, 0x02bd, 0x02bc, 0x00a3,
+ 0x0000, 0x0000, 0x00a6, 0x00a7,
+ 0x00a8, 0x00a9, 0x0000, 0x00ab,
+ 0x00ac, 0x00ad, 0x0000, 0x2015,
+ /* 0xb0*/
+ 0x00b0, 0x00b1, 0x00b2, 0x00b3,
+ 0x0384, 0x0385, 0x0386, 0x00b7,
+ 0x0388, 0x0389, 0x038a, 0x00bb,
+ 0x038c, 0x00bd, 0x038e, 0x038f,
+ /* 0xc0*/
+ 0x0390, 0x0391, 0x0392, 0x0393,
+ 0x0394, 0x0395, 0x0396, 0x0397,
+ 0x0398, 0x0399, 0x039a, 0x039b,
+ 0x039c, 0x039d, 0x039e, 0x039f,
+ /* 0xd0*/
+ 0x03a0, 0x03a1, 0x0000, 0x03a3,
+ 0x03a4, 0x03a5, 0x03a6, 0x03a7,
+ 0x03a8, 0x03a9, 0x03aa, 0x03ab,
+ 0x03ac, 0x03ad, 0x03ae, 0x03af,
+ /* 0xe0*/
+ 0x03b0, 0x03b1, 0x03b2, 0x03b3,
+ 0x03b4, 0x03b5, 0x03b6, 0x03b7,
+ 0x03b8, 0x03b9, 0x03ba, 0x03bb,
+ 0x03bc, 0x03bd, 0x03be, 0x03bf,
+ /* 0xf0*/
+ 0x03c0, 0x03c1, 0x03c2, 0x03c3,
+ 0x03c4, 0x03c5, 0x03c6, 0x03c7,
+ 0x03c8, 0x03c9, 0x03ca, 0x03cb,
+ 0x03cc, 0x03cd, 0x03ce, 0x0000,
+};
+
+static unsigned char page00[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0x00, 0x00, 0xa3, 0x00, 0x00, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0x00, 0xab, 0xac, 0xad, 0x00, 0x00, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0x00, 0x00, 0x00, 0xb7, /* 0xb0-0xb7 */
+ 0x00, 0x00, 0x00, 0xbb, 0x00, 0xbd, 0x00, 0x00, /* 0xb8-0xbf */
+};
+
+static unsigned char page02[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+ 0x00, 0x00, 0x00, 0x00, 0xa2, 0xa1, 0x00, 0x00, /* 0xb8-0xbf */
+};
+
+static unsigned char page03[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0xb4, 0xb5, 0xb6, 0x00, /* 0x80-0x87 */
+ 0xb8, 0xb9, 0xba, 0x00, 0xbc, 0x00, 0xbe, 0xbf, /* 0x88-0x8f */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0x90-0x97 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0x98-0x9f */
+ 0xd0, 0xd1, 0x00, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xa0-0xa7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xa8-0xaf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xb0-0xb7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xb8-0xbf */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xc0-0xc7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0x00, /* 0xc8-0xcf */
+};
+
+static unsigned char page20[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xaf, 0x00, 0x00, /* 0x10-0x17 */
+};
+
+static unsigned char *page_uni2charset[256] = {
+ page00, NULL, page02, page03, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ page20, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+};
+
+static unsigned char charset2lower[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40-0x47 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x48-0x4f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50-0x57 */
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0x00, 0x00, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0x00, 0xab, 0xac, 0xad, 0x00, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xdc, 0xb7, /* 0xb0-0xb7 */
+ 0xdd, 0xde, 0xdf, 0xbb, 0xfc, 0xbd, 0xfd, 0xfe, /* 0xb8-0xbf */
+ 0xc0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xc0-0xc7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xc8-0xcf */
+ 0xf0, 0xf1, 0x00, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xd0-0xd7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0x00, /* 0xf8-0xff */
+};
+
+static unsigned char charset2upper[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0x00, 0x00, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0x00, 0xab, 0xac, 0xad, 0x00, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0x00, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xb6, 0xb8, 0xb9, 0xba, /* 0xd8-0xdf */
+ 0xe0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xe0-0xe7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xe8-0xef */
+ 0xd0, 0xd1, 0xd3, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xf0-0xf7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xbc, 0xbe, 0xbf, 0x00, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+ unsigned char *uni2charset;
+ unsigned char cl = uni & 0x00ff;
+ unsigned char ch = (uni & 0xff00) >> 8;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ uni2charset = page_uni2charset[ch];
+ if (uni2charset && uni2charset[cl])
+ out[0] = uni2charset[cl];
+ else
+ return -EINVAL;
+ return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+ *uni = charset2uni[*rawstring];
+ if (*uni == 0x0000)
+ return -EINVAL;
+ return 1;
+}
+
+static struct nls_table table = {
+ "iso8859-7",
+ uni2char,
+ char2uni,
+ charset2lower,
+ charset2upper,
+ THIS_MODULE,
+};
+
+static int __init init_nls_iso8859_7(void)
+{
+ return register_nls(&table);
+}
+
+static void __exit exit_nls_iso8859_7(void)
+{
+ unregister_nls(&table);
+}
+
+module_init(init_nls_iso8859_7)
+module_exit(exit_nls_iso8859_7)
+MODULE_LICENSE("Dual BSD/GPL");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/fs/nls/nls_iso8859-8.c b/fs/nls/nls_iso8859-8.c
new file mode 100644
index 00000000000000..3005f680928f22
--- /dev/null
+++ b/fs/nls/nls_iso8859-8.c
@@ -0,0 +1,63 @@
+/*
+ * linux/fs/nls_iso8859-8.c
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static struct nls_table *p_nls;
+
+static struct nls_table table = {
+ "iso8859-8",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ THIS_MODULE,
+};
+
+static int __init init_nls_iso8859_8(void)
+{
+ p_nls = load_nls("cp1255");
+
+ if (p_nls) {
+ table.uni2char = p_nls->uni2char;
+ table.char2uni = p_nls->char2uni;
+ table.charset2upper = p_nls->charset2upper;
+ table.charset2lower = p_nls->charset2lower;
+ return register_nls(&table);
+ }
+
+ return -EINVAL;
+}
+
+static void __exit exit_nls_iso8859_8(void)
+{
+ unregister_nls(&table);
+ unload_nls(p_nls);
+}
+
+module_init(init_nls_iso8859_8)
+module_exit(exit_nls_iso8859_8)
+MODULE_LICENSE("Dual BSD/GPL");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ *
+---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/fs/nls/nls_iso8859-9.c b/fs/nls/nls_iso8859-9.c
new file mode 100644
index 00000000000000..dfc813e0a11637
--- /dev/null
+++ b/fs/nls/nls_iso8859-9.c
@@ -0,0 +1,289 @@
+/*
+ * linux/fs/nls_iso8859-9.c
+ *
+ * Charset iso8859-9 translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static wchar_t charset2uni[256] = {
+ /* 0x00*/
+ 0x0000, 0x0001, 0x0002, 0x0003,
+ 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b,
+ 0x000c, 0x000d, 0x000e, 0x000f,
+ /* 0x10*/
+ 0x0010, 0x0011, 0x0012, 0x0013,
+ 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b,
+ 0x001c, 0x001d, 0x001e, 0x001f,
+ /* 0x20*/
+ 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b,
+ 0x002c, 0x002d, 0x002e, 0x002f,
+ /* 0x30*/
+ 0x0030, 0x0031, 0x0032, 0x0033,
+ 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b,
+ 0x003c, 0x003d, 0x003e, 0x003f,
+ /* 0x40*/
+ 0x0040, 0x0041, 0x0042, 0x0043,
+ 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b,
+ 0x004c, 0x004d, 0x004e, 0x004f,
+ /* 0x50*/
+ 0x0050, 0x0051, 0x0052, 0x0053,
+ 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b,
+ 0x005c, 0x005d, 0x005e, 0x005f,
+ /* 0x60*/
+ 0x0060, 0x0061, 0x0062, 0x0063,
+ 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b,
+ 0x006c, 0x006d, 0x006e, 0x006f,
+ /* 0x70*/
+ 0x0070, 0x0071, 0x0072, 0x0073,
+ 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b,
+ 0x007c, 0x007d, 0x007e, 0x007f,
+ /* 0x80*/
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0x90*/
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ /* 0xa0*/
+ 0x00a0, 0x00a1, 0x00a2, 0x00a3,
+ 0x00a4, 0x00a5, 0x00a6, 0x00a7,
+ 0x00a8, 0x00a9, 0x00aa, 0x00ab,
+ 0x00ac, 0x00ad, 0x00ae, 0x00af,
+ /* 0xb0*/
+ 0x00b0, 0x00b1, 0x00b2, 0x00b3,
+ 0x00b4, 0x00b5, 0x00b6, 0x00b7,
+ 0x00b8, 0x00b9, 0x00ba, 0x00bb,
+ 0x00bc, 0x00bd, 0x00be, 0x00bf,
+ /* 0xc0*/
+ 0x00c0, 0x00c1, 0x00c2, 0x00c3,
+ 0x00c4, 0x00c5, 0x00c6, 0x00c7,
+ 0x00c8, 0x00c9, 0x00ca, 0x00cb,
+ 0x00cc, 0x00cd, 0x00ce, 0x00cf,
+ /* 0xd0*/
+ 0x011e, 0x00d1, 0x00d2, 0x00d3,
+ 0x00d4, 0x00d5, 0x00d6, 0x00d7,
+ 0x00d8, 0x00d9, 0x00da, 0x00db,
+ 0x00dc, 0x0130, 0x015e, 0x00df,
+ /* 0xe0*/
+ 0x00e0, 0x00e1, 0x00e2, 0x00e3,
+ 0x00e4, 0x00e5, 0x00e6, 0x00e7,
+ 0x00e8, 0x00e9, 0x00ea, 0x00eb,
+ 0x00ec, 0x00ed, 0x00ee, 0x00ef,
+ /* 0xf0*/
+ 0x011f, 0x00f1, 0x00f2, 0x00f3,
+ 0x00f4, 0x00f5, 0x00f6, 0x00f7,
+ 0x00f8, 0x00f9, 0x00fa, 0x00fb,
+ 0x00fc, 0x0131, 0x015f, 0x00ff,
+};
+
+static unsigned char page00[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0x00, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0x00, 0x00, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0x00, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0x00, 0x00, 0xff, /* 0xf8-0xff */
+};
+
+static unsigned char page01[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0xf0, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0xdd, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0xfe, /* 0x58-0x5f */
+};
+
+static unsigned char *page_uni2charset[256] = {
+ page00, page01, NULL, NULL, NULL, NULL, NULL, NULL,
+};
+
+static unsigned char charset2lower[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40-0x47 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x48-0x4f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50-0x57 */
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xc0-0xc7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xc8-0xcf */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xd7, /* 0xd0-0xd7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0x69, 0xfe, 0xdf, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static unsigned char charset2upper[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0x00, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xe0-0xe7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xe8-0xef */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xf7, /* 0xf0-0xf7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0x49, 0xde, 0x00, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+ unsigned char *uni2charset;
+ unsigned char cl = uni & 0x00ff;
+ unsigned char ch = (uni & 0xff00) >> 8;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ uni2charset = page_uni2charset[ch];
+ if (uni2charset && uni2charset[cl])
+ out[0] = uni2charset[cl];
+ else
+ return -EINVAL;
+ return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+ *uni = charset2uni[*rawstring];
+ if (*uni == 0x0000)
+ return -EINVAL;
+ return 1;
+}
+
+static struct nls_table table = {
+ "iso8859-9",
+ uni2char,
+ char2uni,
+ charset2lower,
+ charset2upper,
+ THIS_MODULE,
+};
+
+static int __init init_nls_iso8859_9(void)
+{
+ return register_nls(&table);
+}
+
+static void __exit exit_nls_iso8859_9(void)
+{
+ unregister_nls(&table);
+}
+
+module_init(init_nls_iso8859_9)
+module_exit(exit_nls_iso8859_9)
+MODULE_LICENSE("Dual BSD/GPL");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/fs/nls/nls_koi8-r.c b/fs/nls/nls_koi8-r.c
new file mode 100644
index 00000000000000..5889f1f7da1c20
--- /dev/null
+++ b/fs/nls/nls_koi8-r.c
@@ -0,0 +1,340 @@
+/*
+ * linux/fs/nls_koi8-r.c
+ *
+ * Charset koi8-r translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static wchar_t charset2uni[256] = {
+ /* 0x00*/
+ 0x0000, 0x0001, 0x0002, 0x0003,
+ 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b,
+ 0x000c, 0x000d, 0x000e, 0x000f,
+ /* 0x10*/
+ 0x0010, 0x0011, 0x0012, 0x0013,
+ 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b,
+ 0x001c, 0x001d, 0x001e, 0x001f,
+ /* 0x20*/
+ 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b,
+ 0x002c, 0x002d, 0x002e, 0x002f,
+ /* 0x30*/
+ 0x0030, 0x0031, 0x0032, 0x0033,
+ 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b,
+ 0x003c, 0x003d, 0x003e, 0x003f,
+ /* 0x40*/
+ 0x0040, 0x0041, 0x0042, 0x0043,
+ 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b,
+ 0x004c, 0x004d, 0x004e, 0x004f,
+ /* 0x50*/
+ 0x0050, 0x0051, 0x0052, 0x0053,
+ 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b,
+ 0x005c, 0x005d, 0x005e, 0x005f,
+ /* 0x60*/
+ 0x0060, 0x0061, 0x0062, 0x0063,
+ 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b,
+ 0x006c, 0x006d, 0x006e, 0x006f,
+ /* 0x70*/
+ 0x0070, 0x0071, 0x0072, 0x0073,
+ 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b,
+ 0x007c, 0x007d, 0x007e, 0x007f,
+ /* 0x80*/
+ 0x2500, 0x2502, 0x250c, 0x2510,
+ 0x2514, 0x2518, 0x251c, 0x2524,
+ 0x252c, 0x2534, 0x253c, 0x2580,
+ 0x2584, 0x2588, 0x258c, 0x2590,
+ /* 0x90*/
+ 0x2591, 0x2592, 0x2593, 0x2320,
+ 0x25a0, 0x2219, 0x221a, 0x2248,
+ 0x2264, 0x2265, 0x00a0, 0x2321,
+ 0x00b0, 0x00b2, 0x00b7, 0x00f7,
+ /* 0xa0*/
+ 0x2550, 0x2551, 0x2552, 0x0451,
+ 0x2553, 0x2554, 0x2555, 0x2556,
+ 0x2557, 0x2558, 0x2559, 0x255a,
+ 0x255b, 0x255c, 0x255d, 0x255e,
+ /* 0xb0*/
+ 0x255f, 0x2560, 0x2561, 0x0401,
+ 0x2562, 0x2563, 0x2564, 0x2565,
+ 0x2566, 0x2567, 0x2568, 0x2569,
+ 0x256a, 0x256b, 0x256c, 0x00a9,
+ /* 0xc0*/
+ 0x044e, 0x0430, 0x0431, 0x0446,
+ 0x0434, 0x0435, 0x0444, 0x0433,
+ 0x0445, 0x0438, 0x0439, 0x043a,
+ 0x043b, 0x043c, 0x043d, 0x043e,
+ /* 0xd0*/
+ 0x043f, 0x044f, 0x0440, 0x0441,
+ 0x0442, 0x0443, 0x0436, 0x0432,
+ 0x044c, 0x044b, 0x0437, 0x0448,
+ 0x044d, 0x0449, 0x0447, 0x044a,
+ /* 0xe0*/
+ 0x042e, 0x0410, 0x0411, 0x0426,
+ 0x0414, 0x0415, 0x0424, 0x0413,
+ 0x0425, 0x0418, 0x0419, 0x041a,
+ 0x041b, 0x041c, 0x041d, 0x041e,
+ /* 0xf0*/
+ 0x041f, 0x042f, 0x0420, 0x0421,
+ 0x0422, 0x0423, 0x0416, 0x0412,
+ 0x042c, 0x042b, 0x0417, 0x0428,
+ 0x042d, 0x0429, 0x0427, 0x042a,
+};
+
+static unsigned char page00[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+ 0x00, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+ 0x9c, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x00, 0x9e, /* 0xb0-0xb7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9f, /* 0xf0-0xf7 */
+};
+
+static unsigned char page04[256] = {
+ 0x00, 0xb3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0xe1, 0xe2, 0xf7, 0xe7, 0xe4, 0xe5, 0xf6, 0xfa, /* 0x10-0x17 */
+ 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, /* 0x18-0x1f */
+ 0xf2, 0xf3, 0xf4, 0xf5, 0xe6, 0xe8, 0xe3, 0xfe, /* 0x20-0x27 */
+ 0xfb, 0xfd, 0xff, 0xf9, 0xf8, 0xfc, 0xe0, 0xf1, /* 0x28-0x2f */
+ 0xc1, 0xc2, 0xd7, 0xc7, 0xc4, 0xc5, 0xd6, 0xda, /* 0x30-0x37 */
+ 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, /* 0x38-0x3f */
+ 0xd2, 0xd3, 0xd4, 0xd5, 0xc6, 0xc8, 0xc3, 0xde, /* 0x40-0x47 */
+ 0xdb, 0xdd, 0xdf, 0xd9, 0xd8, 0xdc, 0xc0, 0xd1, /* 0x48-0x4f */
+ 0x00, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+};
+
+static unsigned char page22[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x95, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x97, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x98, 0x99, 0x00, 0x00, /* 0x60-0x67 */
+};
+
+static unsigned char page23[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x93, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+};
+
+static unsigned char page25[256] = {
+ 0x80, 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x83, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x85, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0xa0, 0xa1, 0xa2, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, /* 0x50-0x57 */
+ 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, /* 0x58-0x5f */
+ 0xb1, 0xb2, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, /* 0x60-0x67 */
+ 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0x8b, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x8d, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x8f, 0x90, 0x91, 0x92, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+};
+
+static unsigned char *page_uni2charset[256] = {
+ page00, NULL, NULL, NULL, page04, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, page22, page23, NULL, page25, NULL, NULL,
+};
+
+static unsigned char charset2lower[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40-0x47 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x48-0x4f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50-0x57 */
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0x88-0x8f */
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xa3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xe0-0xe7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xe8-0xef */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xf0-0xf7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xf8-0xff */
+};
+
+static unsigned char charset2upper[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0x88-0x8f */
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xb3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xc0-0xc7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xc8-0xcf */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xd0-0xd7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+ unsigned char *uni2charset;
+ unsigned char cl = uni & 0x00ff;
+ unsigned char ch = (uni & 0xff00) >> 8;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ uni2charset = page_uni2charset[ch];
+ if (uni2charset && uni2charset[cl])
+ out[0] = uni2charset[cl];
+ else
+ return -EINVAL;
+ return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+ *uni = charset2uni[*rawstring];
+ if (*uni == 0x0000)
+ return -EINVAL;
+ return 1;
+}
+
+static struct nls_table table = {
+ "koi8-r",
+ uni2char,
+ char2uni,
+ charset2lower,
+ charset2upper,
+ THIS_MODULE,
+};
+
+static int __init init_nls_koi8_r(void)
+{
+ return register_nls(&table);
+}
+
+static void __exit exit_nls_koi8_r(void)
+{
+ unregister_nls(&table);
+}
+
+module_init(init_nls_koi8_r)
+module_exit(exit_nls_koi8_r)
+MODULE_LICENSE("Dual BSD/GPL");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/fs/nls/nls_koi8-ru.c b/fs/nls/nls_koi8-ru.c
new file mode 100644
index 00000000000000..b94aaf1ee720bd
--- /dev/null
+++ b/fs/nls/nls_koi8-ru.c
@@ -0,0 +1,101 @@
+/*
+ * linux/fs/nls_koi8-ru.c
+ *
+ * Charset koi8-ru translation based on charset koi8-u.
+ * The Unicode to charset table has only exact mappings.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static struct nls_table *p_nls;
+
+static int uni2char(const wchar_t uni,
+ unsigned char *out, int boundlen)
+{
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ if ((uni & 0xffaf) == 0x040e || (uni & 0xffce) == 0x254c) {
+ /* koi8-ru and koi8-u differ only on two characters */
+ if (uni == 0x040e)
+ out[0] = 0xbe;
+ else if (uni == 0x045e)
+ out[0] = 0xae;
+ else if (uni == 0x255d || uni == 0x256c)
+ return 0;
+ else
+ return p_nls->uni2char(uni, out, boundlen);
+ return 1;
+ }
+ else
+ /* fast path */
+ return p_nls->uni2char(uni, out, boundlen);
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen,
+ wchar_t *uni)
+{
+ int n;
+
+ if ((*rawstring & 0xef) != 0xae) {
+ /* koi8-ru and koi8-u differ only on two characters */
+ *uni = (*rawstring & 0x10) ? 0x040e : 0x045e;
+ return 1;
+ }
+
+ n = p_nls->char2uni(rawstring, boundlen, uni);
+ return n;
+}
+
+static struct nls_table table = {
+ "koi8-ru",
+ uni2char,
+ char2uni,
+ NULL,
+ NULL,
+ THIS_MODULE,
+};
+
+static int __init init_nls_koi8_ru(void)
+{
+ p_nls = load_nls("koi8-u");
+
+ if (p_nls) {
+ table.charset2upper = p_nls->charset2upper;
+ table.charset2lower = p_nls->charset2lower;
+ return register_nls(&table);
+ }
+
+ return -EINVAL;
+}
+
+static void __exit exit_nls_koi8_ru(void)
+{
+ unregister_nls(&table);
+ unload_nls(p_nls);
+}
+
+module_init(init_nls_koi8_ru)
+module_exit(exit_nls_koi8_ru)
+MODULE_LICENSE("Dual BSD/GPL");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/fs/nls/nls_koi8-u.c b/fs/nls/nls_koi8-u.c
new file mode 100644
index 00000000000000..c9efbffb12434d
--- /dev/null
+++ b/fs/nls/nls_koi8-u.c
@@ -0,0 +1,347 @@
+/*
+ * linux/fs/nls_koi8-u.c
+ *
+ * Charset koi8-u translation tables.
+ * The Unicode to charset table has only exact mappings.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static wchar_t charset2uni[256] = {
+ /* 0x00*/
+ 0x0000, 0x0001, 0x0002, 0x0003,
+ 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b,
+ 0x000c, 0x000d, 0x000e, 0x000f,
+ /* 0x10*/
+ 0x0010, 0x0011, 0x0012, 0x0013,
+ 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b,
+ 0x001c, 0x001d, 0x001e, 0x001f,
+ /* 0x20*/
+ 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b,
+ 0x002c, 0x002d, 0x002e, 0x002f,
+ /* 0x30*/
+ 0x0030, 0x0031, 0x0032, 0x0033,
+ 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b,
+ 0x003c, 0x003d, 0x003e, 0x003f,
+ /* 0x40*/
+ 0x0040, 0x0041, 0x0042, 0x0043,
+ 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b,
+ 0x004c, 0x004d, 0x004e, 0x004f,
+ /* 0x50*/
+ 0x0050, 0x0051, 0x0052, 0x0053,
+ 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b,
+ 0x005c, 0x005d, 0x005e, 0x005f,
+ /* 0x60*/
+ 0x0060, 0x0061, 0x0062, 0x0063,
+ 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b,
+ 0x006c, 0x006d, 0x006e, 0x006f,
+ /* 0x70*/
+ 0x0070, 0x0071, 0x0072, 0x0073,
+ 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b,
+ 0x007c, 0x007d, 0x007e, 0x007f,
+ /* 0x80*/
+ 0x2500, 0x2502, 0x250c, 0x2510,
+ 0x2514, 0x2518, 0x251c, 0x2524,
+ 0x252c, 0x2534, 0x253c, 0x2580,
+ 0x2584, 0x2588, 0x258c, 0x2590,
+ /* 0x90*/
+ 0x2591, 0x2592, 0x2593, 0x2320,
+ 0x25a0, 0x2219, 0x221a, 0x2248,
+ 0x2264, 0x2265, 0x00a0, 0x2321,
+ 0x00b0, 0x00b2, 0x00b7, 0x00f7,
+ /* 0xa0*/
+ 0x2550, 0x2551, 0x2552, 0x0451,
+ 0x0454, 0x2554, 0x0456, 0x0457,
+ 0x2557, 0x2558, 0x2559, 0x255a,
+ 0x255b, 0x0491, 0x255d, 0x255e,
+ /* 0xb0*/
+ 0x255f, 0x2560, 0x2561, 0x0401,
+ 0x0404, 0x2563, 0x0406, 0x0407,
+ 0x2566, 0x2567, 0x2568, 0x2569,
+ 0x256a, 0x0490, 0x256c, 0x00a9,
+ /* 0xc0*/
+ 0x044e, 0x0430, 0x0431, 0x0446,
+ 0x0434, 0x0435, 0x0444, 0x0433,
+ 0x0445, 0x0438, 0x0439, 0x043a,
+ 0x043b, 0x043c, 0x043d, 0x043e,
+ /* 0xd0*/
+ 0x043f, 0x044f, 0x0440, 0x0441,
+ 0x0442, 0x0443, 0x0436, 0x0432,
+ 0x044c, 0x044b, 0x0437, 0x0448,
+ 0x044d, 0x0449, 0x0447, 0x044a,
+ /* 0xe0*/
+ 0x042e, 0x0410, 0x0411, 0x0426,
+ 0x0414, 0x0415, 0x0424, 0x0413,
+ 0x0425, 0x0418, 0x0419, 0x041a,
+ 0x041b, 0x041c, 0x041d, 0x041e,
+ /* 0xf0*/
+ 0x041f, 0x042f, 0x0420, 0x0421,
+ 0x0422, 0x0423, 0x0416, 0x0412,
+ 0x042c, 0x042b, 0x0417, 0x0428,
+ 0x042d, 0x0429, 0x0427, 0x042a,
+};
+
+static unsigned char page00[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+ 0x00, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+ 0x9c, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x00, 0x9e, /* 0xb0-0xb7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0-0xc7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe0-0xe7 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9f, /* 0xf0-0xf7 */
+};
+
+static unsigned char page04[256] = {
+ 0x00, 0xb3, 0x00, 0x00, 0xb4, 0x00, 0xb6, 0xb7, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0xe1, 0xe2, 0xf7, 0xe7, 0xe4, 0xe5, 0xf6, 0xfa, /* 0x10-0x17 */
+ 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, /* 0x18-0x1f */
+ 0xf2, 0xf3, 0xf4, 0xf5, 0xe6, 0xe8, 0xe3, 0xfe, /* 0x20-0x27 */
+ 0xfb, 0xfd, 0xff, 0xf9, 0xf8, 0xfc, 0xe0, 0xf1, /* 0x28-0x2f */
+ 0xc1, 0xc2, 0xd7, 0xc7, 0xc4, 0xc5, 0xd6, 0xda, /* 0x30-0x37 */
+ 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, /* 0x38-0x3f */
+ 0xd2, 0xd3, 0xd4, 0xd5, 0xc6, 0xc8, 0xc3, 0xde, /* 0x40-0x47 */
+ 0xdb, 0xdd, 0xdf, 0xd9, 0xd8, 0xdc, 0xc0, 0xd1, /* 0x48-0x4f */
+ 0x00, 0xa3, 0x00, 0x00, 0xa4, 0x00, 0xa6, 0xa7, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0xbd, 0xad, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+};
+
+static unsigned char page22[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x95, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x97, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+ 0x00, 0x00, 0x00, 0x00, 0x98, 0x99, 0x00, 0x00, /* 0x60-0x67 */
+};
+
+static unsigned char page23[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x93, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+};
+
+static unsigned char page25[256] = {
+ 0x80, 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+ 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+ 0x83, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+ 0x85, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+ 0x00, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+ 0x00, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+ 0x00, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+ 0x00, 0x00, 0x00, 0x00, 0x8a, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+ 0xa0, 0xa1, 0xa2, 0x00, 0xa5, 0x00, 0x00, 0xa8, /* 0x50-0x57 */
+ 0xa9, 0xaa, 0xab, 0xac, 0x00, 0xae, 0xaf, 0xb0, /* 0x58-0x5f */
+ 0xb1, 0xb2, 0x00, 0xb5, 0x00, 0x00, 0xb8, 0xb9, /* 0x60-0x67 */
+ 0xba, 0xbb, 0xbc, 0x00, 0xbe, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+ 0x8b, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+ 0x8d, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+ 0x8f, 0x90, 0x91, 0x92, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+ 0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+};
+
+static unsigned char *page_uni2charset[256] = {
+ page00, NULL, NULL, NULL, page04, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, page22, page23, NULL, page25, NULL, NULL,
+};
+
+static unsigned char charset2lower[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40-0x47 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x48-0x4f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50-0x57 */
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0x88-0x8f */
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xa3, 0xa4, 0xb5, 0xa6, 0xa7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xad, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xe0-0xe7 */
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xe8-0xef */
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xf0-0xf7 */
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xf8-0xff */
+};
+
+static unsigned char charset2upper[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0x88-0x8f */
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x98-0x9f */
+ 0xa0, 0xa1, 0xa2, 0xb3, 0xb4, 0xa5, 0xb6, 0xb7, /* 0xa0-0xa7 */
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xbd, 0xae, 0xaf, /* 0xa8-0xaf */
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xc0-0xc7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xc8-0xcf */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xd0-0xd7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xd8-0xdf */
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+ unsigned char *uni2charset;
+ unsigned char cl = uni & 0x00ff;
+ unsigned char ch = (uni & 0xff00) >> 8;
+
+ if (boundlen <= 0)
+ return -ENAMETOOLONG;
+
+ uni2charset = page_uni2charset[ch];
+ if (uni2charset && uni2charset[cl])
+ out[0] = uni2charset[cl];
+ else
+ return -EINVAL;
+ return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+ *uni = charset2uni[*rawstring];
+ if (*uni == 0x0000)
+ return -EINVAL;
+ return 1;
+}
+
+static struct nls_table table = {
+ "koi8-u",
+ uni2char,
+ char2uni,
+ charset2lower,
+ charset2upper,
+ THIS_MODULE,
+};
+
+static int __init init_nls_koi8_u(void)
+{
+ return register_nls(&table);
+}
+
+static void __exit exit_nls_koi8_u(void)
+{
+ unregister_nls(&table);
+}
+
+module_init(init_nls_koi8_u)
+module_exit(exit_nls_koi8_u)
+MODULE_LICENSE("Dual BSD/GPL");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/fs/nls/nls_sjis.c b/fs/nls/nls_sjis.c
new file mode 100644
index 00000000000000..024d59d1946408
--- /dev/null
+++ b/fs/nls/nls_sjis.c
@@ -0,0 +1,63 @@
+/*
+ * linux/fs/nls_sjis.c
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static struct nls_table *p_nls;
+
+static struct nls_table table = {
+ "sjis",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ THIS_MODULE,
+};
+
+static int __init init_nls_sjis(void)
+{
+ p_nls = load_nls("cp932");
+
+ if (p_nls) {
+ table.uni2char = p_nls->uni2char;
+ table.char2uni = p_nls->char2uni;
+ table.charset2upper = p_nls->charset2upper;
+ table.charset2lower = p_nls->charset2lower;
+ return register_nls(&table);
+ }
+
+ return -EINVAL;
+}
+
+static void __exit exit_nls_sjis(void)
+{
+ unregister_nls(&table);
+ unload_nls(p_nls);
+}
+
+module_init(init_nls_sjis)
+module_exit(exit_nls_sjis)
+MODULE_LICENSE("Dual BSD/GPL");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ *
+---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/fs/nls/nls_tis-620.c b/fs/nls/nls_tis-620.c
new file mode 100644
index 00000000000000..b85a4f85818994
--- /dev/null
+++ b/fs/nls/nls_tis-620.c
@@ -0,0 +1,63 @@
+/*
+ * linux/fs/nls_tis-620.c
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static struct nls_table *p_nls;
+
+static struct nls_table table = {
+ "tis-620",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ THIS_MODULE,
+};
+
+static int __init init_nls_tis_620(void)
+{
+ p_nls = load_nls("cp874");
+
+ if (p_nls) {
+ table.uni2char = p_nls->uni2char;
+ table.char2uni = p_nls->char2uni;
+ table.charset2upper = p_nls->charset2upper;
+ table.charset2lower = p_nls->charset2lower;
+ return register_nls(&table);
+ }
+
+ return -EINVAL;
+}
+
+static void __exit exit_nls_tis_620(void)
+{
+ unregister_nls(&table);
+ unload_nls(p_nls);
+}
+
+module_init(init_nls_tis_620)
+module_exit(exit_nls_tis_620)
+MODULE_LICENSE("Dual BSD/GPL");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ *
+---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/fs/nls/nls_utf8.c b/fs/nls/nls_utf8.c
new file mode 100644
index 00000000000000..09a84d76f2bc23
--- /dev/null
+++ b/fs/nls/nls_utf8.c
@@ -0,0 +1,61 @@
+/*
+ * Module for handling utf8 just like any other charset.
+ * By Urban Widmark 2000
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static unsigned char identity[256];
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+ int n;
+
+ if ( (n = utf8_wctomb(out, uni, boundlen)) == -1) {
+ *out = '?';
+ return -EINVAL;
+ }
+ return n;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+ int n;
+
+ if ( (n = utf8_mbtowc(uni, rawstring, boundlen)) == -1) {
+ *uni = 0x003f; /* ? */
+ n = -EINVAL;
+ }
+ return n;
+}
+
+static struct nls_table table = {
+ "utf8",
+ uni2char,
+ char2uni,
+ identity, /* no conversion */
+ identity,
+ THIS_MODULE,
+};
+
+static int __init init_nls_utf8(void)
+{
+ int i;
+ for (i=0; i<256; i++)
+ identity[i] = i;
+
+ return register_nls(&table);
+}
+
+static void __exit exit_nls_utf8(void)
+{
+ unregister_nls(&table);
+}
+
+module_init(init_nls_utf8)
+module_exit(exit_nls_utf8)
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/fs/ntfs/Makefile b/fs/ntfs/Makefile
new file mode 100644
index 00000000000000..dbed71023a172e
--- /dev/null
+++ b/fs/ntfs/Makefile
@@ -0,0 +1,11 @@
+# Rules for making the NTFS driver
+
+O_TARGET := ntfs.o
+
+obj-y := fs.o sysctl.o support.o util.o inode.o dir.o super.o attr.o unistr.o
+obj-m := $(O_TARGET)
+# New version format started 3 February 2001.
+EXTRA_CFLAGS = -DNTFS_VERSION=\"1.1.22\" #-DDEBUG
+
+include $(TOPDIR)/Rules.make
+
diff --git a/fs/ntfs/attr.c b/fs/ntfs/attr.c
new file mode 100644
index 00000000000000..b64aee7e17ba84
--- /dev/null
+++ b/fs/ntfs/attr.c
@@ -0,0 +1,872 @@
+/*
+ * attr.c
+ *
+ * Copyright (C) 1996-1999 Martin von Löwis
+ * Copyright (C) 1996-1997 Régis Duchesne
+ * Copyright (C) 1998 Joseph Malicki
+ * Copyright (C) 1999 Steve Dodd
+ * Copyright (C) 2001 Anton Altaparmakov (AIA)
+ */
+
+#include "ntfstypes.h"
+#include "struct.h"
+#include "attr.h"
+
+#include <linux/errno.h>
+#include <linux/ntfs_fs.h>
+#include "macros.h"
+#include "support.h"
+#include "util.h"
+#include "super.h"
+#include "inode.h"
+#include "unistr.h"
+
+/**
+ * ntfs_find_attr_in_mft_rec - find attribute in mft record
+ * @vol: volume on which attr resides
+ * @m: mft record to search
+ * @type: attribute type to find
+ * @name: attribute name to find (optional, i.e. NULL means don't care)
+ * @name_len: attribute name length (only needed if @name present)
+ * @ic: ignore case if 1 or case sensitive if 0 (ignored if @name NULL)
+ * @instance: instance number to find
+ *
+ * Only search the specified mft record and it ignores the presence of an
+ * attribute list attribute (unless it is the one being searched for,
+ * obviously, in which case it is returned).
+ */
+ntfs_u8* ntfs_find_attr_in_mft_rec(ntfs_volume *vol, ntfs_u8 *m, __u32 type,
+ wchar_t *name, __u32 name_len, int ic, __u16 instance)
+{
+ ntfs_u8 *a;
+
+ /* Iterate over attributes in mft record @m. */
+ a = m + NTFS_GETU16(m + 20); /* attrs_offset */
+ for (; a >= m && a <= m + vol->mft_record_size;
+ a += NTFS_GETU32(a + 4 /* length */)) {
+ /* We catch $END with this more general check, too... */
+ if (NTFS_GETU32(a + 0 /* type */) > type)
+ return NULL;
+ if (!NTFS_GETU32(a + 4 /* length */))
+ break;
+ if (NTFS_GETU32(a + 0 /* type */) != type)
+ continue;
+ /* If @name is present, compare the two names. */
+ if (name && !ntfs_are_names_equal(name, name_len, (wchar_t*)
+ (a + NTFS_GETU16(a + 10 /* name_offset */)),
+ a[9] /* name_length */, ic, vol->upcase,
+ vol->upcase_length)) {
+ register int rc;
+
+ rc = ntfs_collate_names(vol->upcase, vol->upcase_length,
+ name, name_len, (wchar_t*)(a +
+ NTFS_GETU16(a + 10 /* name_offset */)),
+ a[9] /* name_length */, 1, 1);
+ /*
+ * If @name collates before a->name, there is no
+ * matching attribute.
+ */
+ if (rc == -1)
+ return NULL;
+ /* If the strings are not equal, continue search. */
+ if (rc)
+ continue;
+ rc = ntfs_collate_names(vol->upcase, vol->upcase_length,
+ name, name_len, (wchar_t*)(a +
+ NTFS_GETU16(a + 10 /* name_offset */)),
+ a[9] /* name_length */, 0, 1);
+ if (rc == -1)
+ return NULL;
+ if (rc)
+ continue;
+ }
+ /*
+ * The names match or @name not present. Check instance number.
+ * and if it matches we have found the attribute and are done.
+ */
+ if (instance != NTFS_GETU16(a + 14 /* instance */))
+ continue;
+ ntfs_debug(DEBUG_FILE3, "ntfs_find_attr_in_mft_record: found: "
+ "attr type 0x%x, instance number = 0x%x.\n",
+ NTFS_GETU32(a + 0), instance);
+ return a;
+ }
+ ntfs_error("ntfs_find_attr_in_mft_record: mft record 0x%x is corrupt"
+ ". Run chkdsk.\n", m);
+ return NULL;
+}
+
+/* Look if an attribute already exists in the inode, and if not, create it. */
+int ntfs_new_attr(ntfs_inode *ino, int type, void *name, int namelen,
+ void *value, int value_len, int *pos, int *found)
+{
+ int do_insert = 0;
+ int i, m;
+ ntfs_attribute *a;
+
+ for (i = 0; i < ino->attr_count; i++)
+ {
+ a = ino->attrs + i;
+ if (a->type < type)
+ continue;
+ if (a->type > type) {
+ do_insert = 1;
+ break;
+ }
+ /* If @name is present, compare the two names. */
+ if (namelen && !ntfs_are_names_equal((wchar_t*)name, namelen,
+ a->name, a->namelen /* name_length */,
+ 1 /* ignore case*/, ino->vol->upcase,
+ ino->vol->upcase_length)) {
+ register int rc;
+
+ rc = ntfs_collate_names(ino->vol->upcase,
+ ino->vol->upcase_length, a->name,
+ a->namelen, (wchar_t*)name, namelen,
+ 1 /* ignore case */, 1);
+ if (rc == -1)
+ continue;
+ if (rc == 1) {
+ do_insert = 1;
+ break;
+ }
+ rc = ntfs_collate_names(ino->vol->upcase,
+ ino->vol->upcase_length, a->name,
+ a->namelen, (wchar_t*)name, namelen,
+ 0 /* case sensitive */, 1);
+ if (rc == -1)
+ continue;
+ if (rc == 1) {
+ do_insert = 1;
+ break;
+ }
+ }
+ /* Names are equal or no name was asked for. */
+ /* If a value was specified compare the values. */
+ if (value_len && a->resident) {
+ if (!a->resident) {
+ ntfs_error("ntfs_new_attr: Value specified but "
+ "attribute non-resident. Bug!\n");
+ return -EINVAL;
+ }
+ m = value_len;
+ if (m > a->size)
+ m = a->size;
+ m = memcmp(value, a->d.data, m);
+ if (m > 0)
+ continue;
+ if (m < 0) {
+ do_insert = 1;
+ break;
+ }
+ /* Values match until min of value lengths. */
+ if (value_len > a->size)
+ continue;
+ if (value_len < a->size) {
+ do_insert = 1;
+ break;
+ }
+ }
+ /* Full match! */
+ *found = 1;
+ *pos = i;
+ return 0;
+ }
+ /* Re-allocate space. */
+ if (ino->attr_count % 8 == 0)
+ {
+ ntfs_attribute* new;
+ new = (ntfs_attribute*)ntfs_malloc((ino->attr_count + 8) *
+ sizeof(ntfs_attribute));
+ if (!new)
+ return -ENOMEM;
+ if (ino->attrs) {
+ ntfs_memcpy(new, ino->attrs, ino->attr_count *
+ sizeof(ntfs_attribute));
+ ntfs_free(ino->attrs);
+ }
+ ino->attrs = new;
+ }
+ if (do_insert)
+ ntfs_memmove(ino->attrs + i + 1, ino->attrs + i,
+ (ino->attr_count - i) * sizeof(ntfs_attribute));
+ ino->attr_count++;
+ ino->attrs[i].type = type;
+ ino->attrs[i].namelen = namelen;
+ ino->attrs[i].name = name;
+ *pos = i;
+ *found = 0;
+ return 0;
+}
+
+int ntfs_make_attr_resident(ntfs_inode *ino, ntfs_attribute *attr)
+{
+ __s64 size = attr->size;
+ if (size > 0) {
+ /* FIXME: read data, free clusters */
+ return -EOPNOTSUPP;
+ }
+ attr->resident = 1;
+ return 0;
+}
+
+/* Store in the inode readable information about a run. */
+int ntfs_insert_run(ntfs_attribute *attr, int cnum, ntfs_cluster_t cluster,
+ int len)
+{
+ /* (re-)allocate space if necessary. */
+ if ((attr->d.r.len * sizeof(ntfs_runlist)) % PAGE_SIZE == 0) {
+ ntfs_runlist* new;
+ unsigned long new_size;
+
+ ntfs_debug(DEBUG_MALLOC, "ntfs_insert_run: re-allocating "
+ "space: old attr->d.r.len = 0x%x\n",
+ attr->d.r.len);
+ new_size = attr->d.r.len * sizeof(ntfs_runlist) + PAGE_SIZE;
+ if ((new_size >> PAGE_SHIFT) > num_physpages) {
+ ntfs_error("ntfs_insert_run: attempted to allocate "
+ "more pages than num_physpages."
+ "This might be a bug or a corrupt"
+ "file system.\n");
+ return -1;
+ }
+ new = ntfs_vmalloc(new_size);
+ if (!new) {
+ ntfs_error("ntfs_insert_run: ntfs_vmalloc(new_size = "
+ "0x%x) failed\n", new_size);
+ return -1;
+ }
+ if (attr->d.r.runlist) {
+ ntfs_memcpy(new, attr->d.r.runlist, attr->d.r.len
+ * sizeof(ntfs_runlist));
+ ntfs_vfree(attr->d.r.runlist);
+ }
+ attr->d.r.runlist = new;
+ }
+ if (attr->d.r.len > cnum)
+ ntfs_memmove(attr->d.r.runlist + cnum + 1,
+ attr->d.r.runlist + cnum,
+ (attr->d.r.len - cnum) * sizeof(ntfs_runlist));
+ attr->d.r.runlist[cnum].lcn = cluster;
+ attr->d.r.runlist[cnum].len = len;
+ attr->d.r.len++;
+ return 0;
+}
+
+/**
+ * ntfs_extend_attr - extend allocated size of an attribute
+ * @ino: ntfs inode containing the attribute to extend
+ * @attr: attribute which to extend
+ * @len: desired new length for @attr (_not_ the amount to extend by)
+ *
+ * Extends an attribute. Allocate clusters on the volume which @ino belongs to.
+ * Extends the run list accordingly, preferably by extending the last run of
+ * the existing run list, first.
+ *
+ * Only modifies attr->allocated, i.e. doesn't touch attr->size, nor
+ * attr->initialized.
+ */
+int ntfs_extend_attr(ntfs_inode *ino, ntfs_attribute *attr, const __s64 len)
+{
+ int rlen, rl2_len, err = 0;
+ ntfs_cluster_t cluster, clen;
+ ntfs_runlist *rl, *rl2;
+
+ if ((attr->flags & (ATTR_IS_COMPRESSED | ATTR_IS_ENCRYPTED)) ||
+ ino->record_count > 1)
+ return -EOPNOTSUPP;
+ /*
+ * FIXME: Don't make non-resident if the attribute type is not right.
+ * For example cannot make index attribute non-resident! (AIA)
+ */
+ if (attr->resident) {
+ err = ntfs_make_attr_nonresident(ino, attr);
+ if (err)
+ return err;
+ }
+ if (len <= attr->allocated)
+ return 0; /* Truly stupid things do sometimes happen. */
+ rl = attr->d.r.runlist;
+ rlen = attr->d.r.len;
+ if (rlen > 0)
+ cluster = rl[rlen - 1].lcn + rl[rlen - 1].len;
+ else
+ /* No preference for allocation space. */
+ cluster = (ntfs_cluster_t)-1;
+ /*
+ * Calculate the extra space we need, and round up to multiple of
+ * cluster size to get number of new clusters needed.
+ */
+ clen = (len - attr->allocated + ino->vol->cluster_size - 1) >>
+ ino->vol->cluster_size_bits;
+ if (!clen)
+ return 0;
+ err = ntfs_allocate_clusters(ino->vol, &cluster, &clen, &rl2,
+ &rl2_len, DATA_ZONE);
+ if (err)
+ return err;
+ attr->allocated += (__s64)clen << ino->vol->cluster_size_bits;
+ if (rlen > 0) {
+ err = splice_runlists(&rl, &rlen, rl2, rl2_len);
+ ntfs_vfree(rl2);
+ if (err)
+ return err;
+ } else {
+ if (rl)
+ ntfs_vfree(rl);
+ rl = rl2;
+ rlen = rl2_len;
+ }
+ attr->d.r.runlist = rl;
+ attr->d.r.len = rlen;
+ return 0;
+}
+
+int ntfs_make_attr_nonresident(ntfs_inode *ino, ntfs_attribute *attr)
+{
+ int error;
+ ntfs_io io;
+ void *data = attr->d.data;
+ __s64 len = attr->size;
+
+ attr->d.r.len = 0;
+ attr->d.r.runlist = NULL;
+ attr->resident = 0;
+ /*
+ * ->allocated is updated by ntfs_extend_attr(), while ->initialized
+ * and ->size are updated by ntfs_readwrite_attr(). (AIA)
+ */
+ attr->allocated = attr->initialized = 0;
+ error = ntfs_extend_attr(ino, attr, len);
+ if (error)
+ return error; /* FIXME: On error, restore old values. */
+ io.fn_put = ntfs_put;
+ io.fn_get = ntfs_get;
+ io.param = data;
+ io.size = len;
+ io.do_read = 0;
+ return ntfs_readwrite_attr(ino, attr, 0, &io);
+}
+
+int ntfs_attr_allnonresident(ntfs_inode *ino)
+{
+ int i, error = 0;
+ ntfs_volume *vol = ino->vol;
+
+ for (i = 0; !error && i < ino->attr_count; i++)
+ {
+ if (ino->attrs[i].type != vol->at_security_descriptor &&
+ ino->attrs[i].type != vol->at_data)
+ continue;
+ error = ntfs_make_attr_nonresident(ino, ino->attrs + i);
+ }
+ return error;
+}
+
+/*
+ * Resize the attribute to a newsize. attr->allocated and attr->size are
+ * updated, but attr->initialized is not changed unless it becomes bigger than
+ * attr->size, in which case it is set to attr->size.
+ */
+int ntfs_resize_attr(ntfs_inode *ino, ntfs_attribute *attr, __s64 newsize)
+{
+ int error = 0;
+ __s64 oldsize = attr->size;
+ int clustersizebits = ino->vol->cluster_size_bits;
+ int i, count, newcount;
+ ntfs_runlist *rl, *rlt;
+
+ if (newsize == oldsize)
+ return 0;
+ if (attr->flags & (ATTR_IS_COMPRESSED | ATTR_IS_ENCRYPTED))
+ return -EOPNOTSUPP;
+ if (attr->resident) {
+ void *v;
+ if (newsize > ino->vol->mft_record_size) {
+ error = ntfs_make_attr_nonresident(ino, attr);
+ if (error)
+ return error;
+ return ntfs_resize_attr(ino, attr, newsize);
+ }
+ v = attr->d.data;
+ if (newsize) {
+ __s64 minsize = newsize;
+ attr->d.data = ntfs_malloc(newsize);
+ if (!attr->d.data) {
+ ntfs_free(v);
+ return -ENOMEM;
+ }
+ if (newsize > oldsize) {
+ minsize = oldsize;
+ ntfs_bzero((char*)attr->d.data + oldsize,
+ newsize - oldsize);
+ }
+ ntfs_memcpy((char*)attr->d.data, v, minsize);
+ } else
+ attr->d.data = 0;
+ ntfs_free(v);
+ attr->size = newsize;
+ return 0;
+ }
+ /* Non-resident attribute. */
+ rl = attr->d.r.runlist;
+ if (newsize < oldsize) {
+ int rl_size;
+ /*
+ * FIXME: We might be going awfully wrong for newsize = 0,
+ * possibly even leaking memory really badly. But considering
+ * in that case there is more breakage due to -EOPNOTSUPP stuff
+ * further down the code path, who cares for the moment... (AIA)
+ */
+ for (i = 0, count = 0; i < attr->d.r.len; i++) {
+ if ((__s64)(count + rl[i].len) << clustersizebits >
+ newsize) {
+ i++;
+ break;
+ }
+ count += (int)rl[i].len;
+ }
+ newcount = count;
+ /* Free unused clusters in current run, unless sparse. */
+ if (rl[--i].lcn != (ntfs_cluster_t)-1) {
+ ntfs_cluster_t rounded = newsize - ((__s64)count <<
+ clustersizebits);
+ rounded = (rounded + ino->vol->cluster_size - 1) >>
+ clustersizebits;
+ error = ntfs_deallocate_cluster_run(ino->vol,
+ rl[i].lcn + rounded,
+ rl[i].len - rounded);
+ if (error)
+ return error; /* FIXME: Incomplete operation. */
+ rl[i].len = rounded;
+ newcount = count + rounded;
+ }
+ /* Free all other runs. */
+ i++;
+ error = ntfs_deallocate_clusters(ino->vol, rl + i,
+ attr->d.r.len - i);
+ if (error)
+ return error; /* FIXME: Incomplete operation. */
+ /*
+ * Free space for extra runs in memory if enough memory left
+ * to do so. FIXME: Only do it if it would free memory. (AIA)
+ */
+ rl_size = ((i + 1) * sizeof(ntfs_runlist) + PAGE_SIZE - 1) &
+ PAGE_MASK;
+ if (rl_size < ((attr->d.r.len * sizeof(ntfs_runlist) +
+ PAGE_SIZE - 1) & PAGE_MASK)) {
+ rlt = ntfs_vmalloc(rl_size);
+ if (rlt) {
+ ntfs_memcpy(rlt, rl, i * sizeof(ntfs_runlist));
+ ntfs_vfree(rl);
+ attr->d.r.runlist = rl = rlt;
+ }
+ }
+ rl[i].lcn = (ntfs_cluster_t)-1;
+ rl[i].len = (ntfs_cluster_t)0;
+ attr->d.r.len = i;
+ } else {
+ error = ntfs_extend_attr(ino, attr, newsize);
+ if (error)
+ return error; /* FIXME: Incomplete operation. */
+ newcount = (newsize + ino->vol->cluster_size - 1) >>
+ clustersizebits;
+ }
+ /* Fill in new sizes. */
+ attr->allocated = (__s64)newcount << clustersizebits;
+ attr->size = newsize;
+ if (attr->initialized > newsize)
+ attr->initialized = newsize;
+ if (!newsize)
+ error = ntfs_make_attr_resident(ino, attr);
+ return error;
+}
+
+int ntfs_create_attr(ntfs_inode *ino, int anum, char *aname, void *data,
+ int dsize, ntfs_attribute **rattr)
+{
+ void *name;
+ int namelen;
+ int found, i;
+ int error;
+ ntfs_attribute *attr;
+
+ if (dsize > ino->vol->mft_record_size)
+ /* FIXME: Non-resident attributes. */
+ return -EOPNOTSUPP;
+ if (aname) {
+ namelen = strlen(aname);
+ name = ntfs_malloc(2 * namelen);
+ if (!name)
+ return -ENOMEM;
+ ntfs_ascii2uni(name, aname, namelen);
+ } else {
+ name = 0;
+ namelen = 0;
+ }
+ error = ntfs_new_attr(ino, anum, name, namelen, data, dsize, &i,
+ &found);
+ if (error || found) {
+ ntfs_free(name);
+ return error ? error : -EEXIST;
+ }
+ *rattr = attr = ino->attrs + i;
+ /* Allocate a new number.
+ * FIXME: Should this happen on inode writeback?
+ * FIXME: Extension records not supported. */
+ error = ntfs_allocate_attr_number(ino, &i);
+ if (error)
+ return error;
+ attr->attrno = i;
+ if (attr->attrno + 1 != NTFS_GETU16(ino->attr + 0x28))
+ ntfs_error("UH OH! attr->attrno (%i) != NTFS_GETU16(ino->attr "
+ "+ 0x28) (%i)\n", attr->attrno,
+ NTFS_GETU16(ino->attr + 0x28));
+ attr->resident = 1;
+ attr->flags = 0;
+ attr->cengine = 0;
+ attr->size = attr->allocated = attr->initialized = dsize;
+
+ /* FIXME: INDEXED information should come from $AttrDef
+ * Currently, only file names are indexed. As of NTFS v3.0 (Win2k),
+ * this is no longer true. Different attributes can be indexed now. */
+ if (anum == ino->vol->at_file_name)
+ attr->indexed = 1;
+ else
+ attr->indexed = 0;
+ attr->d.data = ntfs_malloc(dsize);
+ if (!attr->d.data)
+ return -ENOMEM;
+ ntfs_memcpy(attr->d.data, data, dsize);
+ return 0;
+}
+
+/*
+ * Non-resident attributes are stored in runs (intervals of clusters).
+ *
+ * This function stores in the inode readable information about a non-resident
+ * attribute.
+ */
+static int ntfs_process_runs(ntfs_inode *ino, ntfs_attribute* attr,
+ unsigned char *data)
+{
+ int startvcn, endvcn;
+ int vcn, cnum;
+ ntfs_cluster_t cluster;
+ int len, ctype;
+ int er = 0;
+ startvcn = NTFS_GETS64(data + 0x10);
+ endvcn = NTFS_GETS64(data + 0x18);
+
+ /* Check whether this chunk really belongs to the end. Problem with
+ * this: this functions can get called on the last extent first, before
+ * it is called on the other extents in sequence. This happens when the
+ * base mft record contains the last extent instead of the first one
+ * and the first extent is stored, like any intermediate extents in
+ * extension mft records. This would be difficult to allow the way the
+ * runlist is stored in memory. Thus we fix elsewhere by causing the
+ * attribute list attribute to be processed immediately when found. The
+ * extents will then be processed starting with the first one. */
+ for (cnum = 0, vcn = 0; cnum < attr->d.r.len; cnum++)
+ vcn += attr->d.r.runlist[cnum].len;
+ if (vcn != startvcn) {
+ ntfs_debug(DEBUG_FILE3, "ntfs_process_runs: ino = 0x%x, "
+ "attr->type = 0x%x, startvcn = 0x%x, endvcn = 0x%x, "
+ "vcn = 0x%x, cnum = 0x%x\n", ino->i_number, attr->type,
+ startvcn, endvcn, vcn, cnum);
+ if (vcn < startvcn) {
+ ntfs_error("Problem with runlist in extended record\n");
+ return -1;
+ }
+ /* Tried to insert an already inserted runlist. */
+ return 0;
+ }
+ if (!endvcn) {
+ if (!startvcn) {
+ /* Allocated length. */
+ endvcn = NTFS_GETS64(data + 0x28) - 1;
+ endvcn >>= ino->vol->cluster_size_bits;
+ } else {
+ /* This is an extent. Allocated length is not defined!
+ * Extents must have an endvcn though so this is an
+ * error. */
+ ntfs_error("Corrupt attribute extent. (endvcn is "
+ "missing)\n");
+ return -1;
+ }
+ }
+ data = data + NTFS_GETU16(data + 0x20);
+ cnum = attr->d.r.len;
+ cluster = 0;
+ for (vcn = startvcn; vcn <= endvcn; vcn += len) {
+ if (ntfs_decompress_run(&data, &len, &cluster, &ctype)) {
+ ntfs_debug(DEBUG_FILE3, "ntfs_process_runs: "
+ "ntfs_decompress_run failed. i_number = 0x%x\n",
+ ino->i_number);
+ return -1;
+ }
+ if (ctype)
+ er = ntfs_insert_run(attr, cnum, -1, len);
+ else
+ er = ntfs_insert_run(attr, cnum, cluster, len);
+ if (er)
+ break;
+ cnum++;
+ }
+ if (er)
+ ntfs_error("ntfs_process_runs: ntfs_insert_run failed\n");
+ ntfs_debug(DEBUG_FILE3, "ntfs_process_runs: startvcn = 0x%x, vcn = 0x%x"
+ ", endvcn = 0x%x, cnum = %i\n", startvcn, vcn,
+ endvcn, cnum);
+ return er;
+}
+
+/* Insert the attribute starting at attr in the inode ino. */
+int ntfs_insert_attribute(ntfs_inode *ino, unsigned char *attrdata)
+{
+ int i, found;
+ int type;
+ short int *name;
+ int namelen;
+ void *data;
+ ntfs_attribute *attr;
+ int error;
+
+ type = NTFS_GETU32(attrdata);
+ namelen = NTFS_GETU8(attrdata + 9);
+ ntfs_debug(DEBUG_FILE3, "ntfs_insert_attribute: ino->i_number 0x%x, "
+ "attr type 0x%x\n", ino->i_number, type);
+ /* Read the attribute's name if it has one. */
+ if (!namelen)
+ name = 0;
+ else {
+ /* 1 Unicode character fits in 2 bytes. */
+ name = ntfs_malloc(2 * namelen);
+ if (!name)
+ return -ENOMEM;
+ ntfs_memcpy(name, attrdata + NTFS_GETU16(attrdata + 10),
+ 2 * namelen);
+ }
+ /* If resident look for value, too. */
+ if (NTFS_GETU8(attrdata + 8) == 0)
+ error = ntfs_new_attr(ino, type, name, namelen,
+ attrdata + NTFS_GETU16(attrdata + 0x14),
+ NTFS_GETU16(attrdata + 0x10), &i, &found);
+ else
+ error = ntfs_new_attr(ino, type, name, namelen, NULL, 0, &i,
+ &found);
+ if (error) {
+ ntfs_debug(DEBUG_FILE3, "ntfs_insert_attribute: ntfs_new_attr "
+ "failed.\n");
+ if (name)
+ ntfs_free(name);
+ return error;
+ }
+ if (found) {
+ /* It's already there, if not resident just process the runs. */
+ if (!ino->attrs[i].resident) {
+ ntfs_debug(DEBUG_FILE3, "ntfs_insert_attribute:"
+ " processing runs 1.\n");
+ /* FIXME: Check error code! (AIA) */
+ ntfs_process_runs(ino, ino->attrs + i, attrdata);
+ }
+ return 0;
+ }
+ attr = ino->attrs + i;
+ attr->resident = NTFS_GETU8(attrdata + 8) == 0;
+ attr->flags = *(__u16*)(attrdata + 0xC);
+ attr->attrno = NTFS_GETU16(attrdata + 0xE);
+
+ if (attr->resident) {
+ attr->size = NTFS_GETU16(attrdata + 0x10);
+ data = attrdata + NTFS_GETU16(attrdata + 0x14);
+ attr->d.data = (void*)ntfs_malloc(attr->size);
+ if (!attr->d.data)
+ return -ENOMEM;
+ ntfs_memcpy(attr->d.data, data, attr->size);
+ attr->indexed = NTFS_GETU8(attrdata + 0x16);
+ } else {
+ attr->allocated = NTFS_GETS64(attrdata + 0x28);
+ attr->size = NTFS_GETS64(attrdata + 0x30);
+ attr->initialized = NTFS_GETS64(attrdata + 0x38);
+ attr->cengine = NTFS_GETU16(attrdata + 0x22);
+ if (attr->flags & ATTR_IS_COMPRESSED)
+ attr->compsize = NTFS_GETS64(attrdata + 0x40);
+ ntfs_debug(DEBUG_FILE3, "ntfs_insert_attribute: "
+ "attr->allocated = 0x%Lx, attr->size = 0x%Lx, "
+ "attr->initialized = 0x%Lx\n", attr->allocated,
+ attr->size, attr->initialized);
+ ino->attrs[i].d.r.runlist = 0;
+ ino->attrs[i].d.r.len = 0;
+ ntfs_debug(DEBUG_FILE3, "ntfs_insert_attribute: processing "
+ "runs 2.\n");
+ /* FIXME: Check error code! (AIA) */
+ ntfs_process_runs(ino, attr, attrdata);
+ }
+ return 0;
+}
+
+int ntfs_read_zero(ntfs_io *dest, int size)
+{
+ int i;
+ char *sparse = ntfs_calloc(512);
+ if (!sparse)
+ return -ENOMEM;
+ i = 512;
+ while (size) {
+ if (i > size)
+ i = size;
+ dest->fn_put(dest, sparse, i);
+ size -= i;
+ }
+ ntfs_free(sparse);
+ return 0;
+}
+
+/* Process compressed attributes. */
+int ntfs_read_compressed(ntfs_inode *ino, ntfs_attribute *attr, __s64 offset,
+ ntfs_io *dest)
+{
+ int error = 0;
+ int clustersizebits;
+ int s_vcn, rnum, vcn, got, l1;
+ __s64 copied, len, chunk, offs1, l, chunk2;
+ ntfs_cluster_t cluster, cl1;
+ char *comp = 0, *comp1;
+ char *decomp = 0;
+ ntfs_io io;
+ ntfs_runlist *rl;
+
+ l = dest->size;
+ clustersizebits = ino->vol->cluster_size_bits;
+ /* Starting cluster of potential chunk. There are three situations:
+ a) In a large uncompressible or sparse chunk, s_vcn is in the middle
+ of a run.
+ b) s_vcn is right on a run border.
+ c) When several runs make a chunk, s_vcn is before the chunks. */
+ s_vcn = offset >> clustersizebits;
+ /* Round down to multiple of 16. */
+ s_vcn &= ~15;
+ rl = attr->d.r.runlist;
+ for (rnum = vcn = 0; rnum < attr->d.r.len && vcn + rl->len <= s_vcn;
+ rnum++, rl++)
+ vcn += rl->len;
+ if (rnum == attr->d.r.len) {
+ /* Beyond end of file. */
+ /* FIXME: Check allocated / initialized. */
+ dest->size = 0;
+ return 0;
+ }
+ io.do_read = 1;
+ io.fn_put = ntfs_put;
+ io.fn_get = 0;
+ cluster = rl->lcn;
+ len = rl->len;
+ copied = 0;
+ while (l) {
+ chunk = 0;
+ if (cluster == (ntfs_cluster_t)-1) {
+ /* Sparse cluster. */
+ __s64 ll;
+
+ if ((len - (s_vcn - vcn)) & 15)
+ ntfs_error("Unexpected sparse chunk size.");
+ ll = ((__s64)(vcn + len) << clustersizebits) - offset;
+ if (ll > l)
+ ll = l;
+ chunk = ll;
+ error = ntfs_read_zero(dest, ll);
+ if (error)
+ goto out;
+ } else if (dest->do_read) {
+ if (!comp) {
+ comp = ntfs_malloc(16 << clustersizebits);
+ if (!comp) {
+ error = -ENOMEM;
+ goto out;
+ }
+ }
+ got = 0;
+ /* We might need to start in the middle of a run. */
+ cl1 = cluster + s_vcn - vcn;
+ comp1 = comp;
+ do {
+ int delta;
+
+ io.param = comp1;
+ delta = s_vcn - vcn;
+ if (delta < 0)
+ delta = 0;
+ l1 = len - delta;
+ if (l1 > 16 - got)
+ l1 = 16 - got;
+ io.size = (__s64)l1 << clustersizebits;
+ error = ntfs_getput_clusters(ino->vol, cl1, 0,
+ &io);
+ if (error)
+ goto out;
+ if (l1 + delta == len) {
+ rnum++;
+ rl++;
+ vcn += len;
+ cluster = cl1 = rl->lcn;
+ len = rl->len;
+ }
+ got += l1;
+ comp1 += (__s64)l1 << clustersizebits;
+ } while (cluster != (ntfs_cluster_t)-1 && got < 16);
+ /* Until empty run. */
+ chunk = 16 << clustersizebits;
+ if (cluster != (ntfs_cluster_t)-1 || got == 16)
+ /* Uncompressible */
+ comp1 = comp;
+ else {
+ if (!decomp) {
+ decomp = ntfs_malloc(16 <<
+ clustersizebits);
+ if (!decomp) {
+ error = -ENOMEM;
+ goto out;
+ }
+ }
+ /* Make sure there are null bytes after the
+ * last block. */
+ *(ntfs_u32*)comp1 = 0;
+ ntfs_decompress(decomp, comp, chunk);
+ comp1 = decomp;
+ }
+ offs1 = offset - ((__s64)s_vcn << clustersizebits);
+ chunk2 = (16 << clustersizebits) - offs1;
+ if (chunk2 > l)
+ chunk2 = l;
+ if (chunk > chunk2)
+ chunk = chunk2;
+ dest->fn_put(dest, comp1 + offs1, chunk);
+ }
+ l -= chunk;
+ copied += chunk;
+ offset += chunk;
+ s_vcn = (offset >> clustersizebits) & ~15;
+ if (l && offset >= ((__s64)(vcn + len) << clustersizebits)) {
+ rnum++;
+ rl++;
+ vcn += len;
+ cluster = rl->lcn;
+ len = rl->len;
+ }
+ }
+out:
+ if (comp)
+ ntfs_free(comp);
+ if (decomp)
+ ntfs_free(decomp);
+ dest->size = copied;
+ return error;
+}
+
+int ntfs_write_compressed(ntfs_inode *ino, ntfs_attribute *attr, __s64 offset,
+ ntfs_io *dest)
+{
+ return -EOPNOTSUPP;
+}
+
diff --git a/fs/ntfs/attr.h b/fs/ntfs/attr.h
new file mode 100644
index 00000000000000..cb8d9fd08d963f
--- /dev/null
+++ b/fs/ntfs/attr.h
@@ -0,0 +1,38 @@
+/*
+ * attr.h - Header file for attr.c
+ *
+ * Copyright (C) 1997 Régis Duchesne
+ * Copyright (c) 2001 Anton Altaparmakov (AIA)
+ */
+#include <linux/nls.h>
+
+ntfs_u8* ntfs_find_attr_in_mft_rec(ntfs_volume *vol, ntfs_u8 *m, __u32 type,
+ wchar_t *name, __u32 name_len, int ic, __u16 instance);
+
+int ntfs_extend_attr(ntfs_inode *ino, ntfs_attribute *attr, const __s64 len);
+
+int ntfs_resize_attr(ntfs_inode *ino, ntfs_attribute *attr, __s64 newsize);
+
+int ntfs_insert_attribute(ntfs_inode *ino, unsigned char* attrdata);
+
+int ntfs_read_compressed(ntfs_inode *ino, ntfs_attribute *attr, __s64 offset,
+ ntfs_io *dest);
+
+int ntfs_write_compressed(ntfs_inode *ino, ntfs_attribute *attr, __s64 offset,
+ ntfs_io *dest);
+
+int ntfs_create_attr(ntfs_inode *ino, int anum, char *aname, void *data,
+ int dsize, ntfs_attribute **rattr);
+
+int ntfs_read_zero(ntfs_io *dest, int size);
+
+int ntfs_make_attr_nonresident(ntfs_inode *ino, ntfs_attribute *attr);
+
+int ntfs_attr_allnonresident(ntfs_inode *ino);
+
+int ntfs_new_attr(ntfs_inode *ino, int type, void *name, int namelen,
+ void *value, int value_len, int *pos, int *found);
+
+int ntfs_insert_run(ntfs_attribute *attr, int cnum, ntfs_cluster_t cluster,
+ int len);
+
diff --git a/fs/ntfs/dir.c b/fs/ntfs/dir.c
new file mode 100644
index 00000000000000..872acf78a89c3d
--- /dev/null
+++ b/fs/ntfs/dir.c
@@ -0,0 +1,1103 @@
+/*
+ * dir.c
+ *
+ * Copyright (C) 1995-1997, 1999 Martin von Löwis
+ * Copyright (C) 1999 Steve Dodd
+ * Copyright (C) 1999 Joseph Malicki
+ * Copyright (C) 2001 Anton Altaparmakov (AIA)
+ */
+
+#include "ntfstypes.h"
+#include "struct.h"
+#include "dir.h"
+#include "macros.h"
+
+#include <linux/errno.h>
+#include "super.h"
+#include "inode.h"
+#include "attr.h"
+#include "support.h"
+#include "util.h"
+#include <linux/smp_lock.h>
+#include <linux/bitops.h>
+
+static char I30[] = "$I30";
+
+/* An index record should start with INDX, and the last word in each block
+ * should contain the check value. If it passes, the original values need to
+ * be restored. */
+int ntfs_check_index_record(ntfs_inode *ino, char *record)
+{
+ return ntfs_fixup_record(record, "INDX", ino->u.index.recordsize);
+}
+
+static inline int ntfs_is_top(ntfs_u64 stack)
+{
+ return stack == 14;
+}
+
+static int ntfs_pop(ntfs_u64 *stack)
+{
+ static int width[16] = {1,2,1,3,1,2,1,4,1,2,1,3,1,2,1,-1};
+ int res = -1;
+
+ switch (width[*stack & 15]) {
+ case 1:
+ res = (int)((*stack & 15) >> 1);
+ *stack >>= 4;
+ break;
+ case 2:
+ res = (int)(((*stack & 63) >> 2) + 7);
+ *stack >>= 6;
+ break;
+ case 3:
+ res = (int)(((*stack & 255) >> 3) + 23);
+ *stack >>= 8;
+ break;
+ case 4:
+ res = (int)(((*stack & 1023) >> 4) + 55);
+ *stack >>= 10;
+ break;
+ default:
+ ntfs_error("Unknown encoding\n");
+ }
+ return res;
+}
+
+static inline unsigned int ntfs_top(void)
+{
+ return 14;
+}
+
+static ntfs_u64 ntfs_push(ntfs_u64 stack, int i)
+{
+ if (i < 7)
+ return (stack << 4) | (i << 1);
+ if (i < 23)
+ return (stack << 6) | ((i - 7) << 2) | 1;
+ if (i < 55)
+ return (stack << 8) | ((i - 23) << 3) | 3;
+ if (i < 120)
+ return (stack << 10) | ((i - 55) << 4) | 7;
+ ntfs_error("Too many entries\n");
+ return ~((ntfs_u64)0);
+}
+
+#if 0
+static void ntfs_display_stack(ntfs_u64 stack)
+{
+ while(!ntfs_is_top(stack))
+ {
+ printf("%d ", ntfs_pop(&stack));
+ }
+ printf("\n");
+}
+#endif
+
+/* True if the entry points to another block of entries. */
+static inline int ntfs_entry_has_subnodes(char *entry)
+{
+ return (NTFS_GETU16(entry + 0xc) & 1);
+}
+
+/* True if it is not the 'end of dir' entry. */
+static inline int ntfs_entry_is_used(char *entry)
+{
+ return !(NTFS_GETU16(entry + 0xc) & 2);
+}
+
+/*
+ * Removed RACE for allocating index blocks. But stil not too happy.
+ * There might be more races afterwards. (AIA)
+ */
+static int ntfs_allocate_index_block(ntfs_iterate_s *walk)
+{
+ ntfs_attribute *allocation, *bitmap = 0;
+ int error, size, i, bit;
+ ntfs_u8 *bmap;
+ ntfs_io io;
+ ntfs_volume *vol = walk->dir->vol;
+
+ /* Check for allocation attribute. */
+ allocation = ntfs_find_attr(walk->dir, vol->at_index_allocation, I30);
+ if (!allocation) {
+ ntfs_u8 bmp[8];
+ /* Create index allocation attribute. */
+ error = ntfs_create_attr(walk->dir, vol->at_index_allocation,
+ I30, 0, 0, &allocation);
+ if (error)
+ goto err_ret;
+ ntfs_bzero(bmp, sizeof(bmp));
+ error = ntfs_create_attr(walk->dir, vol->at_bitmap, I30, bmp,
+ sizeof(bmp), &bitmap);
+ if (error)
+ goto err_ret;
+ } else
+ bitmap = ntfs_find_attr(walk->dir, vol->at_bitmap, I30);
+ if (!bitmap) {
+ ntfs_error("Directory w/o bitmap\n");
+ error = -EINVAL;
+ goto err_ret;
+ }
+ size = bitmap->size;
+ bmap = ntfs_malloc(size);
+ if (!bmap) {
+ error = -ENOMEM;
+ goto err_ret;
+ }
+ io.fn_put = ntfs_put;
+ io.fn_get = ntfs_get;
+try_again:
+ io.param = bmap;
+ io.size = size;
+ error = ntfs_read_attr(walk->dir, vol->at_bitmap, I30, 0, &io);
+ if (error || (io.size != size && (error = -EIO, 1)))
+ goto err_fb_out;
+ /* Allocate a bit. */
+ for (bit = i = 0; i < size; i++) {
+ if (bmap[i] == 0xFF)
+ continue;
+ bit = ffz(bmap[i]);
+ if (bit < 8)
+ break;
+ }
+ if (i >= size) {
+ /* FIXME: Extend bitmap. */
+ error = -EOPNOTSUPP;
+ goto err_fb_out;
+ }
+ /* Get the byte containing our bit again, now taking the BKL. */
+ io.param = bmap;
+ io.size = 1;
+ lock_kernel();
+ error = ntfs_read_attr(walk->dir, vol->at_bitmap, I30, i, &io);
+ if (error || (io.size != 1 && (error = -EIO, 1)))
+ goto err_unl_out;
+ if (ntfs_test_and_set_bit(bmap, bit)) {
+ unlock_kernel();
+ /* Give other process(es) a chance to finish. */
+ schedule();
+ goto try_again;
+ }
+ walk->newblock = (i * 8 + bit) * walk->dir->u.index.clusters_per_record;
+ io.param = bmap;
+ error = ntfs_write_attr(walk->dir, vol->at_bitmap, I30, i, &io);
+ if (error || (io.size != size && (error = -EIO, 1)))
+ goto err_unl_out;
+ /* Change inode on disk, required when bitmap is resident. */
+ error = ntfs_update_inode(walk->dir);
+ if (error)
+ goto err_unl_out;
+ unlock_kernel();
+ ntfs_free(bmap);
+ /* Check whether record is out of allocated range. */
+ size = allocation->size;
+ if (walk->newblock * vol->cluster_size >= size) {
+ /* Build index record. */
+ int hsize;
+ int s1 = walk->dir->u.index.recordsize;
+ int nr_fix = (s1 >> vol->sector_size) + 1;
+ char *record = ntfs_malloc(s1);
+ if (!record) {
+ error = -ENOMEM;
+ goto err_ret;
+ }
+ ntfs_bzero(record, s1);
+ /* Magic */
+ ntfs_memcpy(record, "INDX", 4);
+ /* Offset to fixups */
+ NTFS_PUTU16(record + 4, 0x28);
+ /* Number of fixups. */
+ NTFS_PUTU16(record + 6, nr_fix);
+ /* Log file sequence number - We don't do journalling so we
+ * just set it to zero which should be the Right Thing. (AIA) */
+ NTFS_PUTU64(record + 8, 0);
+ /* VCN of buffer */
+ NTFS_PUTU64(record + 0x10, walk->newblock);
+ /* Header size. */
+ hsize = 0x10 + 2 * nr_fix;
+ hsize = (hsize + 7) & ~7; /* Align. */
+ NTFS_PUTU16(record + 0x18, hsize);
+ /* Total size of record. */
+ NTFS_PUTU32(record + 0x20, s1 - 0x18);
+ /* Writing the data will extend the attribute. */
+ io.param = record;
+ io.size = s1;
+ io.do_read = 0;
+ error = ntfs_readwrite_attr(walk->dir, allocation, size, &io);
+ ntfs_free(record);
+ if (error || (io.size != s1 && (error = -EIO, 1)))
+ goto err_ret;
+ error = ntfs_update_inode(walk->dir);
+ if (error)
+ goto err_ret;
+ }
+ return 0;
+err_unl_out:
+ unlock_kernel();
+err_fb_out:
+ ntfs_free(bmap);
+err_ret:
+ return error;
+}
+
+/* Write an index block (root or allocation) back to storage.
+ * Used is the total number of bytes in buf, including all headers. */
+static int ntfs_index_writeback(ntfs_iterate_s *walk, ntfs_u8 *buf, int block,
+ int used)
+{
+ ntfs_io io;
+ int error;
+ ntfs_attribute *a;
+ ntfs_volume *vol = walk->dir->vol;
+
+ io.fn_put = 0;
+ io.fn_get = ntfs_get;
+ io.param = buf;
+ if (block == -1) { /* Index root. */
+ NTFS_PUTU16(buf + 0x14, used - 0x10);
+ /* 0x18 is a copy thereof. */
+ NTFS_PUTU16(buf + 0x18, used - 0x10);
+ io.size = used;
+ error = ntfs_write_attr(walk->dir, vol->at_index_root, I30, 0,
+ &io);
+ if (error || (io.size != used && (error = -EIO, 1)))
+ return error;
+ /* Shrink if necessary. */
+ a = ntfs_find_attr(walk->dir, vol->at_index_root, I30);
+ ntfs_resize_attr(walk->dir, a, used);
+ } else {
+ NTFS_PUTU16(buf + 0x1C, used - 0x18);
+ io.size = walk->dir->u.index.recordsize;
+ error = ntfs_insert_fixups(buf, io.size);
+ if (error) {
+ printk(KERN_ALERT "NTFS: ntfs_index_writeback() caught "
+ "corrupt index record ntfs record "
+ "header. Refusing to write corrupt "
+ "data to disk. Unmount and run chkdsk "
+ "immediately!\n");
+ return -EIO;
+ }
+ error = ntfs_write_attr(walk->dir, vol->at_index_allocation,
+ I30, (__s64)block << vol->cluster_size_bits,
+ &io);
+ if (error || (io.size != walk->dir->u.index.recordsize &&
+ (error = -EIO, 1)))
+ return error;
+ }
+ return 0;
+}
+
+static int ntfs_split_record(ntfs_iterate_s *walk, char *start, int bsize,
+ int usize)
+{
+ char *entry, *prev;
+ ntfs_u8 *newbuf = 0, *middle = 0;
+ int error, othersize, mlen;
+ ntfs_io io;
+ ntfs_volume *vol = walk->dir->vol;
+ int oldblock;
+
+ error = ntfs_allocate_index_block(walk);
+ if (error)
+ return error;
+ /* This should not happen. */
+ if (walk->block == -1) {
+ ntfs_error("Trying to split root");
+ return -EOPNOTSUPP;
+ }
+ entry = start + NTFS_GETU16(start + 0x18) + 0x18;
+ for (prev = entry; entry - start < usize / 2;
+ entry += NTFS_GETU16(entry + 8))
+ prev = entry;
+ newbuf = ntfs_malloc(vol->index_record_size);
+ if (!newbuf)
+ return -ENOMEM;
+ io.fn_put = ntfs_put;
+ io.fn_get = ntfs_get;
+ io.param = newbuf;
+ io.size = vol->index_record_size;
+ /* Read in old header. FIXME: Reading everything is overkill. */
+ error = ntfs_read_attr(walk->dir, vol->at_index_allocation, I30,
+ (__s64)walk->newblock << vol->cluster_size_bits, &io);
+ if (error)
+ goto out;
+ if (io.size != vol->index_record_size) {
+ error = -EIO;
+ goto out;
+ }
+ /* FIXME: Adjust header. */
+ /* Copy everything from entry to new block. */
+ othersize = usize - (entry - start);
+ ntfs_memcpy(newbuf + NTFS_GETU16(newbuf + 0x18) + 0x18, entry,
+ othersize);
+ /* Copy flags. */
+ NTFS_PUTU32(newbuf + 0x24, NTFS_GETU32(start + 0x24));
+ error = ntfs_index_writeback(walk, newbuf, walk->newblock,
+ othersize + NTFS_GETU16(newbuf + 0x18) + 0x18);
+ if (error)
+ goto out;
+ /* Move prev to walk. */
+ mlen = NTFS_GETU16(prev + 0x8);
+ /* Remember old child node. */
+ if (ntfs_entry_has_subnodes(prev))
+ oldblock = NTFS_GETU32(prev + mlen - 8);
+ else
+ oldblock = -1;
+ /* Allow for pointer to subnode. */
+ middle = ntfs_malloc(ntfs_entry_has_subnodes(prev) ? mlen : mlen + 8);
+ if (!middle){
+ error = -ENOMEM;
+ goto out;
+ }
+ ntfs_memcpy(middle, prev, mlen);
+ /* Set has_subnodes flag. */
+ NTFS_PUTU8(middle + 0xC, NTFS_GETU8(middle + 0xC) | 1);
+ /* Middle entry points to block, parent entry will point to newblock. */
+ NTFS_PUTU64(middle + mlen - 8, walk->block);
+ if (walk->new_entry)
+ ntfs_error("Entry not reset");
+ walk->new_entry = middle;
+ walk->u.flags |= ITERATE_SPLIT_DONE;
+ /* Terminate old block. */
+ othersize = usize - (prev-start);
+ NTFS_PUTU64(prev, 0);
+ if (oldblock == -1) {
+ NTFS_PUTU32(prev + 8, 0x10);
+ NTFS_PUTU32(prev + 0xC, 2);
+ othersize += 0x10;
+ } else {
+ NTFS_PUTU32(prev + 8, 0x18);
+ NTFS_PUTU32(prev + 0xC, 3);
+ NTFS_PUTU64(prev + 0x10, oldblock);
+ othersize += 0x18;
+ }
+ /* Write back original block. */
+ error = ntfs_index_writeback(walk, start, walk->block, othersize);
+ out:
+ if (newbuf)
+ ntfs_free(newbuf);
+ if (middle)
+ ntfs_free(middle);
+ return error;
+}
+
+static int ntfs_dir_insert(ntfs_iterate_s *walk, char *start, char* entry)
+{
+ int blocksize, usedsize, error, offset;
+ int do_split = 0;
+ offset = entry - start;
+ if (walk->block == -1) { /* index root */
+ blocksize = walk->dir->vol->mft_record_size;
+ usedsize = NTFS_GETU16(start + 0x14) + 0x10;
+ } else {
+ blocksize = walk->dir->u.index.recordsize;
+ usedsize = NTFS_GETU16(start + 0x1C) + 0x18;
+ }
+ if (usedsize + walk->new_entry_size > blocksize) {
+ char* s1 = ntfs_malloc(blocksize + walk->new_entry_size);
+ if (!s1)
+ return -ENOMEM;
+ ntfs_memcpy(s1, start, usedsize);
+ do_split = 1;
+ /* Adjust entry to s1. */
+ entry = s1 + (entry - start);
+ start = s1;
+ }
+ ntfs_memmove(entry + walk->new_entry_size, entry, usedsize - offset);
+ ntfs_memcpy(entry, walk->new_entry, walk->new_entry_size);
+ usedsize += walk->new_entry_size;
+ ntfs_free(walk->new_entry);
+ walk->new_entry = 0;
+ if (do_split) {
+ error = ntfs_split_record(walk, start, blocksize, usedsize);
+ ntfs_free(start);
+ } else {
+ error = ntfs_index_writeback(walk, start, walk->block,usedsize);
+ if (error)
+ return error;
+ }
+ return 0;
+}
+
+/* Try to split INDEX_ROOT attributes. Return -E2BIG if nothing changed. */
+int ntfs_split_indexroot(ntfs_inode *ino)
+{
+ ntfs_attribute *ra;
+ ntfs_u8 *root = 0, *index = 0;
+ ntfs_io io;
+ int error, off, i, bsize, isize;
+ ntfs_iterate_s walk;
+
+ ra = ntfs_find_attr(ino, ino->vol->at_index_root, I30);
+ if (!ra)
+ return -ENOTDIR;
+ bsize = ino->vol->mft_record_size;
+ root = ntfs_malloc(bsize);
+ if (!root)
+ return -E2BIG;
+ io.fn_put = ntfs_put;
+ io.param = root;
+ io.size = bsize;
+ error = ntfs_read_attr(ino, ino->vol->at_index_root, I30, 0, &io);
+ if (error)
+ goto out;
+ off = 0x20;
+ /* Count number of entries. */
+ for (i = 0; ntfs_entry_is_used(root + off); i++)
+ off += NTFS_GETU16(root + off + 8);
+ if (i <= 2) {
+ /* We don't split small index roots. */
+ error = -E2BIG;
+ goto out;
+ }
+ index = ntfs_malloc(ino->vol->index_record_size);
+ if (!index) {
+ error = -ENOMEM;
+ goto out;
+ }
+ walk.dir = ino;
+ walk.block = -1;
+ walk.result = walk.new_entry = 0;
+ walk.name = 0;
+ error = ntfs_allocate_index_block(&walk);
+ if (error)
+ goto out;
+ /* Write old root to new index block. */
+ io.param = index;
+ io.size = ino->vol->index_record_size;
+ error = ntfs_read_attr(ino, ino->vol->at_index_allocation, I30,
+ (__s64)walk.newblock << ino->vol->cluster_size_bits, &io);
+ if (error)
+ goto out;
+ isize = NTFS_GETU16(root + 0x18) - 0x10;
+ ntfs_memcpy(index + NTFS_GETU16(index + 0x18) + 0x18, root+0x20, isize);
+ /* Copy flags. */
+ NTFS_PUTU32(index + 0x24, NTFS_GETU32(root + 0x1C));
+ error = ntfs_index_writeback(&walk, index, walk.newblock,
+ isize + NTFS_GETU16(index + 0x18) + 0x18);
+ if (error)
+ goto out;
+ /* Mark root as split. */
+ NTFS_PUTU32(root + 0x1C, 1);
+ /* Truncate index root. */
+ NTFS_PUTU64(root + 0x20, 0);
+ NTFS_PUTU32(root + 0x28, 0x18);
+ NTFS_PUTU32(root + 0x2C, 3);
+ NTFS_PUTU64(root + 0x30, walk.newblock);
+ error = ntfs_index_writeback(&walk, root, -1, 0x38);
+ out:
+ ntfs_free(root);
+ ntfs_free(index);
+ return error;
+}
+
+/* The entry has been found. Copy the result in the caller's buffer */
+static int ntfs_copyresult(char *dest, char *source)
+{
+ int length = NTFS_GETU16(source + 8);
+ ntfs_memcpy(dest, source, length);
+ return 1;
+}
+
+/* Use $UpCase some day. */
+static inline unsigned short ntfs_my_toupper(ntfs_volume *vol, ntfs_u16 x)
+{
+ /* We should read any pending rest of $UpCase here. */
+ if (x >= vol->upcase_length)
+ return x;
+ return vol->upcase[x];
+}
+
+/* Everything passed in walk and entry. */
+static int ntfs_my_strcmp(ntfs_iterate_s *walk, const unsigned char *entry)
+{
+ int lu = *(entry + 0x50);
+ int i;
+
+ ntfs_u16* name = (ntfs_u16*)(entry + 0x52);
+ ntfs_volume *vol = walk->dir->vol;
+ for (i = 0; i < lu && i < walk->namelen; i++)
+ if (ntfs_my_toupper(vol, NTFS_GETU16(name + i)) !=
+ ntfs_my_toupper(vol, NTFS_GETU16(walk->name + i)))
+ break;
+ if (i == lu && i == walk->namelen)
+ return 0;
+ if (i == lu)
+ return 1;
+ if (i == walk->namelen)
+ return -1;
+ if (ntfs_my_toupper(vol, NTFS_GETU16(name + i)) <
+ ntfs_my_toupper(vol, NTFS_GETU16(walk->name + i)))
+ return 1;
+ return -1;
+}
+
+/* Necessary forward declaration. */
+static int ntfs_getdir_iterate(ntfs_iterate_s *walk, char *start, char *entry);
+
+/* Parse a block of entries. Load the block, fix it up, and iterate over the
+ * entries. The block is given as virtual cluster number. */
+static int ntfs_getdir_record(ntfs_iterate_s *walk, int block)
+{
+ int length = walk->dir->u.index.recordsize;
+ char *record = (char*)ntfs_malloc(length);
+ char *offset;
+ int retval,error;
+ int oldblock;
+ ntfs_io io;
+
+ if (!record)
+ return -ENOMEM;
+ io.fn_put = ntfs_put;
+ io.param = record;
+ io.size = length;
+ /* Read the block from the index allocation attribute. */
+ error = ntfs_read_attr(walk->dir, walk->dir->vol->at_index_allocation,
+ I30, (__s64)block << walk->dir->vol->cluster_size_bits, &io);
+ if (error || io.size != length) {
+ ntfs_error("read failed\n");
+ ntfs_free(record);
+ return 0;
+ }
+ if (!ntfs_check_index_record(walk->dir, record)) {
+ ntfs_error("%x is not an index record\n", block);
+ ntfs_free(record);
+ return 0;
+ }
+ offset = record + NTFS_GETU16(record + 0x18) + 0x18;
+ oldblock = walk->block;
+ walk->block = block;
+ retval = ntfs_getdir_iterate(walk, record, offset);
+ walk->block = oldblock;
+ ntfs_free(record);
+ return retval;
+}
+
+/* Go down to the next block of entries. These collate before the current
+ * entry. */
+static int ntfs_descend(ntfs_iterate_s *walk, ntfs_u8 *start, ntfs_u8 *entry)
+{
+ int length = NTFS_GETU16(entry + 8);
+ int nextblock = NTFS_GETU32(entry + length - 8);
+ int error;
+
+ if (!ntfs_entry_has_subnodes(entry)) {
+ ntfs_error("illegal ntfs_descend call\n");
+ return 0;
+ }
+ error = ntfs_getdir_record(walk, nextblock);
+ if (!error && walk->type == DIR_INSERT &&
+ (walk->u.flags & ITERATE_SPLIT_DONE)) {
+ /* Split has occurred. Adjust entry, insert new_entry. */
+ NTFS_PUTU32(entry + length - 8, walk->newblock);
+ /* Reset flags, as the current block might be split again. */
+ walk->u.flags &= ~ITERATE_SPLIT_DONE;
+ error = ntfs_dir_insert(walk, start, entry);
+ }
+ return error;
+}
+
+static int ntfs_getdir_iterate_byposition(ntfs_iterate_s *walk, char* start,
+ char *entry)
+{
+ int retval = 0;
+ int curpos = 0, destpos = 0;
+ int length;
+ if (walk->u.pos != 0) {
+ if (ntfs_is_top(walk->u.pos))
+ return 0;
+ destpos = ntfs_pop(&walk->u.pos);
+ }
+ while (1) {
+ if (walk->u.pos == 0) {
+ if (ntfs_entry_has_subnodes(entry))
+ ntfs_descend(walk, start, entry);
+ else
+ walk->u.pos = ntfs_top();
+ if (ntfs_is_top(walk->u.pos) &&
+ !ntfs_entry_is_used(entry))
+ return 1;
+ walk->u.pos = ntfs_push(walk->u.pos, curpos);
+ return 1;
+ }
+ if (curpos == destpos) {
+ if (!ntfs_is_top(walk->u.pos) &&
+ ntfs_entry_has_subnodes(entry)) {
+ retval = ntfs_descend(walk, start, entry);
+ if (retval) {
+ walk->u.pos = ntfs_push(walk->u.pos,
+ curpos);
+ return retval;
+ }
+ if (!ntfs_entry_is_used(entry))
+ return 0;
+ walk->u.pos = 0;
+ }
+ if (ntfs_entry_is_used(entry)) {
+ retval = ntfs_copyresult(walk->result, entry);
+ walk->u.pos = 0;
+ } else {
+ walk->u.pos = ntfs_top();
+ return 0;
+ }
+ }
+ curpos++;
+ if (!ntfs_entry_is_used(entry))
+ break;
+ length = NTFS_GETU16(entry + 8);
+ if (!length) {
+ ntfs_error("infinite loop\n");
+ break;
+ }
+ entry += length;
+ }
+ return -1;
+}
+
+/* Iterate over a list of entries, either from an index block, or from the
+ * index root.
+ * If searching BY_POSITION, pop the top index from the position. If the
+ * position stack is empty then, return the item at the index and set the
+ * position to the next entry. If the position stack is not empty,
+ * recursively proceed for subnodes. If the entry at the position is the
+ * 'end of dir' entry, return 'not found' and the empty stack.
+ * If searching BY_NAME, walk through the items until found or until
+ * one item is collated after the requested item. In the former case, return
+ * the result. In the latter case, recursively proceed to the subnodes.
+ * If 'end of dir' is reached, the name is not in the directory */
+static int ntfs_getdir_iterate(ntfs_iterate_s *walk, char *start, char *entry)
+{
+ int length;
+ int cmp;
+
+ if (walk->type == BY_POSITION)
+ return ntfs_getdir_iterate_byposition(walk, start, entry);
+ do {
+ /* If the current entry is a real one, compare with the
+ * requested item. If the current entry is the last item, it
+ * is always larger than the requested item. */
+ cmp = ntfs_entry_is_used(entry) ?
+ ntfs_my_strcmp(walk,entry) : -1;
+ switch (walk->type) {
+ case BY_NAME:
+ switch (cmp) {
+ case -1:
+ return ntfs_entry_has_subnodes(entry) ?
+ ntfs_descend(walk, start, entry) : 0;
+ case 0:
+ return ntfs_copyresult(walk->result, entry);
+ case 1:
+ break;
+ }
+ break;
+ case DIR_INSERT:
+ switch (cmp) {
+ case -1:
+ return ntfs_entry_has_subnodes(entry) ?
+ ntfs_descend(walk, start, entry) :
+ ntfs_dir_insert(walk, start, entry);
+ case 0:
+ return -EEXIST;
+ case 1:
+ break;
+ }
+ break;
+ default:
+ ntfs_error("TODO\n"); /* FIXME: ? */
+ }
+ if (!ntfs_entry_is_used(entry))
+ break;
+ length = NTFS_GETU16(entry + 8);
+ if (!length) {
+ ntfs_error("infinite loop\n");
+ break;
+ }
+ entry += length;
+ } while (1);
+ return 0;
+}
+
+/* Tree walking is done using position numbers. The following numbers have a
+ * special meaning:
+ * 0 start (.)
+ * -1 no more entries
+ * -2 ..
+ * All other numbers encode sequences of indices. The sequence a, b, c is
+ * encoded as <stop><c><b><a>, where <foo> is the encoding of foo. The
+ * first few integers are encoded as follows:
+ * 0: 0000 1: 0010 2: 0100 3: 0110
+ * 4: 1000 5: 1010 6: 1100 stop: 1110
+ * 7: 000001 8: 000101 9: 001001 10: 001101
+ * The least significant bits give the width of this encoding, the other bits
+ * encode the value, starting from the first value of the interval.
+ * tag width first value last value
+ * 0 3 0 6
+ * 01 4 7 22
+ * 011 5 23 54
+ * 0111 6 55 119
+ * More values are hopefully not needed, as the file position has currently
+ * 64 bits in total. */
+
+/* Find an entry in the directory. Return 0 if not found, otherwise copy the
+ * entry to the result buffer. */
+int ntfs_getdir(ntfs_iterate_s *walk)
+{
+ int length = walk->dir->vol->mft_record_size;
+ int retval, error;
+ /* Start at the index root. */
+ char *root = ntfs_malloc(length);
+ ntfs_io io;
+
+ if (!root)
+ return -ENOMEM;
+ io.fn_put = ntfs_put;
+ io.param = root;
+ io.size = length;
+ error = ntfs_read_attr(walk->dir, walk->dir->vol->at_index_root, I30,
+ 0, &io);
+ if (error) {
+ ntfs_error("Not a directory\n");
+ return 0;
+ }
+ walk->block = -1;
+ /* FIXME: Move these to walk. */
+ walk->dir->u.index.recordsize = NTFS_GETU32(root + 0x8);
+ walk->dir->u.index.clusters_per_record = NTFS_GETU32(root + 0xC);
+ /* FIXME: Consistency check. */
+ /* Skip header. */
+ retval = ntfs_getdir_iterate(walk, root, root + 0x20);
+ ntfs_free(root);
+ return retval;
+}
+
+/* Find an entry in the directory by its position stack. Iteration starts
+ * if the stack is 0, in which case the position is set to the first item
+ * in the directory. If the position is nonzero, return the item at the
+ * position and change the position to the next item. The position is -1
+ * if there are no more items. */
+int ntfs_getdir_byposition(ntfs_iterate_s *walk)
+{
+ walk->type = BY_POSITION;
+ return ntfs_getdir(walk);
+}
+
+/* Find an entry in the directory by its name. Return 0 if not found. */
+int ntfs_getdir_byname(ntfs_iterate_s *walk)
+{
+ walk->type = BY_NAME;
+ return ntfs_getdir(walk);
+}
+
+int ntfs_getdir_unsorted(ntfs_inode *ino, u32 *p_high, u32 *p_low,
+ int (*cb)(ntfs_u8 *, void *), void *param)
+{
+ s64 ib_ofs;
+ char *buf = 0, *entry = 0;
+ ntfs_attribute *attr;
+ ntfs_volume *vol;
+ int byte, bit, err = 0;
+ u32 start, finish, ibs, max_size;
+ ntfs_io io;
+ u8 ibs_bits;
+
+ if (!ino) {
+ ntfs_error("%s(): No inode! Returning -EINVAL.\n",__FUNCTION__);
+ return -EINVAL;
+ }
+ vol = ino->vol;
+ if (!vol) {
+ ntfs_error("%s(): Inode 0x%lx has no volume. Returning "
+ "-EINVAL.\n", __FUNCTION__, ino->i_number);
+ return -EINVAL;
+ }
+ ntfs_debug(DEBUG_DIR3, "%s(): Unsorted 1: Entering for inode 0x%lx, "
+ "p_high = 0x%x, p_low = 0x%x.\n", __FUNCTION__,
+ ino->i_number, *p_high, *p_low);
+ if (!*p_high) {
+ /* We are still in the index root. */
+ buf = ntfs_malloc(io.size = vol->mft_record_size);
+ if (!buf)
+ return -ENOMEM;
+ io.fn_put = ntfs_put;
+ io.param = buf;
+ err = ntfs_read_attr(ino, vol->at_index_root, I30, 0, &io);
+ if (err || !io.size)
+ goto read_err_ret;
+ ino->u.index.recordsize = ibs = NTFS_GETU32(buf + 0x8);
+ ino->u.index.clusters_per_record = NTFS_GETU32(buf + 0xC);
+ entry = buf + 0x20;
+ ntfs_debug(DEBUG_DIR3, "%s(): Unsorted 2: In index root.\n",
+ __FUNCTION__);
+ ibs_bits = ffs(ibs) - 1;
+ /* Compensate for faked "." and "..". */
+ start = 2;
+ } else { /* We are in an index record. */
+ io.size = ibs = ino->u.index.recordsize;
+ buf = ntfs_malloc(ibs);
+ if (!buf)
+ return -ENOMEM;
+ ibs_bits = ffs(ibs) - 1;
+ io.fn_put = ntfs_put;
+ io.param = buf;
+ /*
+ * 0 is index root, index allocation starts at 1 and works in
+ * units of index block size (ibs).
+ */
+ ib_ofs = (s64)(*p_high - 1) << ibs_bits;
+ err = ntfs_read_attr(ino, vol->at_index_allocation, I30, ib_ofs,
+ &io);
+ if (err || io.size != ibs)
+ goto read_err_ret;
+ if (!ntfs_check_index_record(ino, buf)) {
+ ntfs_error("%s(): Index block 0x%x is not an index "
+ "record. Returning -ENOTDIR.\n",
+ __FUNCTION__, *p_high - 1);
+ ntfs_free(buf);
+ return -ENOTDIR;
+ }
+ entry = buf + 0x18 + NTFS_GETU16(buf + 0x18);
+ ntfs_debug(DEBUG_DIR3, "%s(): Unsorted 3: In index "
+ "allocation.\n", __FUNCTION__);
+ start = 0;
+ }
+ /* Process the entries. */
+ finish = *p_low;
+ for (; entry < (buf + ibs) && ntfs_entry_is_used(entry);
+ entry += NTFS_GETU16(entry + 8)) {
+ if (start < finish) {
+ /* Skip entries that were already processed. */
+ ntfs_debug(DEBUG_DIR3, "%s(): Unsorted 4: Skipping "
+ "already processed entry p_high 0x%x, "
+ "p_low 0x%x.\n", __FUNCTION__, *p_high,
+ start);
+ start++;
+ continue;
+ }
+ ntfs_debug(DEBUG_DIR3, "%s(): Unsorted 5: Processing entry "
+ "p_high 0x%x, p_low 0x%x.\n", __FUNCTION__,
+ *p_high, *p_low);
+ if ((err = cb(entry, param))) {
+ /* filldir signalled us to stop. */
+ ntfs_debug(DEBUG_DIR3, "%s(): Unsorted 6: cb returned "
+ "%i, returning 0, p_high 0x%x, "
+ "p_low 0x%x.\n", __FUNCTION__, err,
+ *p_high, *p_low);
+ ntfs_free(buf);
+ return 0;
+ }
+ ++*p_low;
+ }
+ ntfs_debug(DEBUG_DIR3, "%s(): Unsorted 7: After processing entries, "
+ "p_high 0x%x, p_low 0x%x.\n", __FUNCTION__, *p_high,
+ *p_low);
+ /* We have to locate the next record. */
+ ntfs_free(buf);
+ buf = 0;
+ *p_low = 0;
+ attr = ntfs_find_attr(ino, vol->at_bitmap, I30);
+ if (!attr) {
+ /* Directory does not have index bitmap and index allocation. */
+ *p_high = 0x7fff;
+ ntfs_debug(DEBUG_DIR3, "%s(): Unsorted 8: No index allocation. "
+ "Returning 0, p_high 0x7fff, p_low 0x0.\n",
+ __FUNCTION__);
+ return 0;
+ }
+ max_size = attr->size;
+ if (max_size > 0x7fff >> 3) {
+ ntfs_error("%s(): Directory too large. Visible "
+ "length is truncated.\n", __FUNCTION__);
+ max_size = 0x7fff >> 3;
+ }
+ buf = ntfs_malloc(max_size);
+ if (!buf)
+ return -ENOMEM;
+ io.param = buf;
+ io.size = max_size;
+ err = ntfs_read_attr(ino, vol->at_bitmap, I30, 0, &io);
+ if (err || io.size != max_size)
+ goto read_err_ret;
+ attr = ntfs_find_attr(ino, vol->at_index_allocation, I30);
+ if (!attr) {
+ ntfs_free(buf);
+ ntfs_debug(DEBUG_DIR3, "%s(): Unsorted 9: Find attr failed. "
+ "Returning -EIO.\n", __FUNCTION__);
+ return -EIO;
+ }
+ if (attr->resident) {
+ ntfs_free(buf);
+ ntfs_debug(DEBUG_DIR3, "%s(): Unsorted 9.5: IA is resident. Not"
+ " allowed. Returning EINVAL.\n", __FUNCTION__);
+ return -EINVAL;
+ }
+ /* Loop while going through non-allocated index records. */
+ max_size <<= 3;
+ while (1) {
+ if (++*p_high >= 0x7fff) {
+ ntfs_error("%s(): Unsorted 10: Directory "
+ "inode 0x%lx overflowed the maximum "
+ "number of index allocation buffers "
+ "the driver can cope with. Pretending "
+ "to be at end of directory.\n",
+ __FUNCTION__, ino->i_number);
+ goto fake_eod;
+ }
+ if (*p_high > max_size || (s64)*p_high << ibs_bits >
+ attr->initialized) {
+fake_eod:
+ /* No more index records. */
+ *p_high = 0x7fff;
+ *p_low = 0;
+ ntfs_free(buf);
+ ntfs_debug(DEBUG_DIR3, "%s(): Unsorted 10.5: No more "
+ "index records. Returning 0, p_high "
+ "0x7fff, p_low 0.\n", __FUNCTION__);
+ return 0;
+ }
+ byte = (ntfs_cluster_t)(*p_high - 1);
+ bit = 1 << (byte & 7);
+ byte >>= 3;
+ if ((buf[byte] & bit))
+ break;
+ };
+ ntfs_debug(DEBUG_DIR3, "%s(): Unsorted 11: Done. Returning 0, p_high "
+ "0x%x, p_low 0x%x.\n", __FUNCTION__, *p_high, *p_low);
+ ntfs_free(buf);
+ return 0;
+read_err_ret:
+ if (!err)
+ err = -EIO;
+ ntfs_error("%s(): Read failed. Returning error code %i.\n",
+ __FUNCTION__, err);
+ ntfs_free(buf);
+ return err;
+}
+
+int ntfs_dir_add(ntfs_inode *dir, ntfs_inode *new, ntfs_attribute *name)
+{
+ ntfs_iterate_s walk;
+ int nsize, esize;
+ ntfs_u8* entry, *ndata;
+ int error;
+
+ walk.type = DIR_INSERT;
+ walk.dir = dir;
+ walk.u.flags = 0;
+ nsize = name->size;
+ ndata = name->d.data;
+ walk.name = (ntfs_u16*)(ndata + 0x42);
+ walk.namelen = NTFS_GETU8(ndata + 0x40);
+ walk.new_entry_size = esize = (nsize + 0x10 + 7) & ~7;
+ walk.new_entry = entry = ntfs_malloc(esize);
+ if (!entry)
+ return -ENOMEM;
+ NTFS_PUTINUM(entry, new);
+ NTFS_PUTU16(entry + 0x8, esize); /* Size of entry. */
+ NTFS_PUTU16(entry + 0xA, nsize); /* Size of original name attribute. */
+ NTFS_PUTU16(entry + 0xC, 0); /* Flags. */
+ NTFS_PUTU16(entry + 0xE, 0); /* Reserved. */
+ ntfs_memcpy(entry + 0x10, ndata, nsize);
+ ntfs_bzero(entry + 0x10 + nsize, esize - 0x10 - nsize);
+ error = ntfs_getdir(&walk);
+ if (walk.new_entry)
+ ntfs_free(walk.new_entry);
+ return error;
+}
+
+#if 0
+int ntfs_dir_add1(ntfs_inode *dir, const char* name, int namelen,
+ ntfs_inode *ino)
+{
+ ntfs_iterate_s walk;
+ int error;
+ int nsize;
+ char *entry;
+ ntfs_attribute *name_attr;
+ error = ntfs_decodeuni(dir->vol, name, namelen, &walk.name,
+ &walk.namelen);
+ if (error)
+ return error;
+ /* FIXME: Set flags. */
+ walk.type = DIR_INSERT;
+ walk.dir = dir;
+ /* walk.new = ino; */
+ /* Prepare new entry. */
+ /* Round up to a multiple of 8. */
+ walk.new_entry_size = nsize = ((0x52 + 2 * walk.namelen + 7) / 8) * 8;
+ walk.new_entry = entry = ntfs_malloc(nsize);
+ if (!entry)
+ return -ENOMEM;
+ ntfs_bzero(entry, nsize);
+ NTFS_PUTINUM(entry, ino);
+ NTFS_PUTU16(entry + 8, nsize);
+ NTFS_PUTU16(entry + 0xA, 0x42 + 2 * namelen); /* FIXME: Size of name
+ * attribute. */
+ NTFS_PUTU32(entry + 0xC, 0); /* FIXME: D-F? */
+ name_attr = ntfs_find_attr(ino, vol->at_file_name, 0);
+ /* FIXME: multiple names */
+ if (!name_attr || !name_attr->resident)
+ return -EIDRM;
+ /* Directory, file stamps, sizes, filename. */
+ ntfs_memcpy(entry + 0x10, name_attr->d.data, 0x42 + 2 * namelen);
+ error = ntfs_getdir(&walk);
+ ntfs_free(walk.name);
+ return error;
+}
+#endif
+
+/* Fills out and creates an INDEX_ROOT attribute. */
+int ntfs_add_index_root(ntfs_inode *ino, int type)
+{
+ ntfs_attribute *da;
+ ntfs_u8 data[0x30]; /* 0x20 header, 0x10 last entry. */
+ char name[10];
+
+ NTFS_PUTU32(data, type);
+ /* Collation rule. 1 == COLLATION_FILENAME */
+ NTFS_PUTU32(data + 4, 1);
+ NTFS_PUTU32(data + 8, ino->vol->index_record_size);
+ NTFS_PUTU32(data + 0xC, ino->vol->index_clusters_per_record);
+ /* Byte offset to first INDEX_ENTRY. */
+ NTFS_PUTU32(data + 0x10, 0x10);
+ /* Size of entries, including header. */
+ NTFS_PUTU32(data + 0x14, 0x20);
+ NTFS_PUTU32(data + 0x18, 0x20);
+ /* No index allocation, yet. */
+ NTFS_PUTU32(data + 0x1C, 0);
+ /* Add last entry. */
+ /* Indexed MFT record. */
+ NTFS_PUTU64(data + 0x20, 0);
+ /* Size of entry. */
+ NTFS_PUTU32(data + 0x28, 0x10);
+ /* Flags: Last entry, no child nodes. */
+ NTFS_PUTU32(data + 0x2C, 2);
+ /* Compute name. */
+ ntfs_indexname(name, type);
+ return ntfs_create_attr(ino, ino->vol->at_index_root, name,
+ data, sizeof(data), &da);
+}
+
+int ntfs_mkdir(ntfs_inode *dir, const char *name, int namelen,
+ ntfs_inode *result)
+{
+ int error;
+
+ error = ntfs_alloc_inode(dir, result, name, namelen, NTFS_AFLAG_DIR);
+ if (error)
+ goto out;
+ error = ntfs_add_index_root(result, 0x30);
+ if (error)
+ goto out;
+ /* Set directory bit. */
+ result->attr[0x16] |= 2;
+ error = ntfs_update_inode(dir);
+ if (error)
+ goto out;
+ error = ntfs_update_inode(result);
+ if (error)
+ goto out;
+ out:
+ return error;
+}
+
diff --git a/fs/ntfs/dir.h b/fs/ntfs/dir.h
new file mode 100644
index 00000000000000..3ded6dca3fafb5
--- /dev/null
+++ b/fs/ntfs/dir.h
@@ -0,0 +1,48 @@
+/*
+ * dir.h - Header file for dir.c
+ *
+ * Copyright (C) 1997 Régis Duchesne
+ */
+#define ITERATE_SPLIT_DONE 1
+
+enum ntfs_iterate_e {
+ BY_POSITION,
+ BY_NAME,
+ DIR_INSERT
+};
+
+/* not all fields are used for all operations */
+typedef struct ntfs_iterate_s {
+ enum ntfs_iterate_e type;
+ ntfs_inode *dir;
+ union{
+ ntfs_u64 pos;
+ int flags;
+ }u;
+ char *result; /* pointer to entry if found */
+ ntfs_u16* name;
+ int namelen;
+ int block; /* current index record */
+ int newblock; /* index record created in a split */
+ char *new_entry;
+ int new_entry_size;
+ /*ntfs_inode* new;*/
+} ntfs_iterate_s;
+
+int ntfs_getdir_unsorted(ntfs_inode *ino, ntfs_u32 *p_high, ntfs_u32* p_low,
+ int (*cb)(ntfs_u8*, void*), void *param);
+
+int ntfs_getdir_byname(ntfs_iterate_s *walk);
+
+int ntfs_dir_add(ntfs_inode *dir, ntfs_inode *new, ntfs_attribute *name);
+
+int ntfs_check_index_record(ntfs_inode *ino, char *record);
+
+int ntfs_getdir_byposition(ntfs_iterate_s *walk);
+
+int ntfs_mkdir(ntfs_inode* dir,const char* name,int namelen, ntfs_inode *ino);
+
+int ntfs_split_indexroot(ntfs_inode *ino);
+
+int ntfs_add_index_root(ntfs_inode *ino, int type);
+
diff --git a/fs/ntfs/fs.c b/fs/ntfs/fs.c
new file mode 100644
index 00000000000000..167f45971f7e11
--- /dev/null
+++ b/fs/ntfs/fs.c
@@ -0,0 +1,1167 @@
+/*
+ * fs.c - NTFS driver for Linux 2.4.x
+ *
+ * Legato Systems, Inc. (http://www.legato.com) have sponsored Anton
+ * Altaparmakov to develop NTFS on Linux since June 2001.
+ *
+ * Copyright (C) 1995-1997, 1999 Martin von Löwis
+ * Copyright (C) 1996 Richard Russon
+ * Copyright (C) 1996-1997 Régis Duchesne
+ * Copyright (C) 2000-2001, Anton Altaparmakov (AIA)
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include "ntfstypes.h"
+#include "struct.h"
+#include "util.h"
+#include "inode.h"
+#include "super.h"
+#include "dir.h"
+#include "support.h"
+#include "macros.h"
+#include "sysctl.h"
+#include "attr.h"
+#include <linux/module.h>
+#include <asm/uaccess.h>
+#include <linux/locks.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+#include <linux/blkdev.h>
+#include <asm/page.h>
+#include <linux/nls.h>
+#include <linux/ntfs_fs.h>
+
+/* Forward declarations. */
+static struct inode_operations ntfs_dir_inode_operations;
+static struct file_operations ntfs_dir_operations;
+
+#define ITEM_SIZE 2040
+
+/* Io functions to user space. */
+static void ntfs_putuser(ntfs_io* dest, void *src, ntfs_size_t len)
+{
+ copy_to_user(dest->param, src, len);
+ dest->param += len;
+}
+
+#ifdef CONFIG_NTFS_RW
+struct ntfs_getuser_update_vm_s {
+ const char *user;
+ struct inode *ino;
+ loff_t off;
+};
+
+static void ntfs_getuser_update_vm(void *dest, ntfs_io *src, ntfs_size_t len)
+{
+ struct ntfs_getuser_update_vm_s *p = src->param;
+
+ copy_from_user(dest, p->user, len);
+ p->user += len;
+ p->off += len;
+}
+#endif
+
+/* loff_t is 64 bit signed, so is cool. */
+static ssize_t ntfs_read(struct file *filp, char *buf, size_t count,loff_t *off)
+{
+ int error;
+ ntfs_io io;
+ ntfs_attribute *attr;
+ ntfs_inode *ino = NTFS_LINO2NINO(filp->f_dentry->d_inode);
+
+ /* Inode is not properly initialized. */
+ if (!ino)
+ return -EINVAL;
+ ntfs_debug(DEBUG_OTHER, "ntfs_read %x, %Lx, %x ->",
+ (unsigned)ino->i_number, (unsigned long long)*off,
+ (unsigned)count);
+ attr = ntfs_find_attr(ino, ino->vol->at_data, NULL);
+ /* Inode has no unnamed data attribute. */
+ if (!attr) {
+ ntfs_debug(DEBUG_OTHER, "ntfs_read: $DATA not found!\n");
+ return -EINVAL;
+ }
+ if (attr->flags & ATTR_IS_ENCRYPTED)
+ return -EACCES;
+ /* Read the data. */
+ io.fn_put = ntfs_putuser;
+ io.fn_get = 0;
+ io.param = buf;
+ io.size = count;
+ error = ntfs_read_attr(ino, ino->vol->at_data, NULL, *off, &io);
+ if (error && !io.size) {
+ ntfs_debug(DEBUG_OTHER, "ntfs_read: read_attr failed with "
+ "error %i, io size %u.\n", error, io.size);
+ return error;
+ }
+ *off += io.size;
+ ntfs_debug(DEBUG_OTHER, "ntfs_read: finished. read %u bytes.\n",
+ io.size);
+ return io.size;
+}
+
+#ifdef CONFIG_NTFS_RW
+static ssize_t ntfs_write(struct file *filp, const char *buf, size_t count,
+ loff_t *pos)
+{
+ int err;
+ struct inode *vfs_ino = filp->f_dentry->d_inode;
+ ntfs_inode *ntfs_ino = NTFS_LINO2NINO(vfs_ino);
+ ntfs_attribute *data;
+ ntfs_io io;
+ struct ntfs_getuser_update_vm_s param;
+
+ if (!ntfs_ino)
+ return -EINVAL;
+ ntfs_debug(DEBUG_LINUX, "%s(): Entering for inode 0x%lx, *pos 0x%Lx, "
+ "count 0x%x.\n", __FUNCTION__, ntfs_ino->i_number,
+ *pos, count);
+ /* Allows to lock fs ro at any time. */
+ if (vfs_ino->i_sb->s_flags & MS_RDONLY)
+ return -EROFS;
+ data = ntfs_find_attr(ntfs_ino, ntfs_ino->vol->at_data, NULL);
+ if (!data)
+ return -EINVAL;
+ /* Evaluating O_APPEND is the file system's job... */
+ if (filp->f_flags & O_APPEND)
+ *pos = vfs_ino->i_size;
+ if (!data->resident && *pos + count > data->allocated) {
+ err = ntfs_extend_attr(ntfs_ino, data, *pos + count);
+ if (err < 0)
+ return err;
+ }
+ param.user = buf;
+ param.ino = vfs_ino;
+ param.off = *pos;
+ io.fn_put = 0;
+ io.fn_get = ntfs_getuser_update_vm;
+ io.param = &param;
+ io.size = count;
+ io.do_read = 0;
+ err = ntfs_readwrite_attr(ntfs_ino, data, *pos, &io);
+ ntfs_debug(DEBUG_LINUX, "%s(): Returning %i\n", __FUNCTION__, -err);
+ if (!err) {
+ *pos += io.size;
+ if (*pos > vfs_ino->i_size)
+ vfs_ino->i_size = *pos;
+ mark_inode_dirty(vfs_ino);
+ return io.size;
+ }
+ return err;
+}
+#endif
+
+struct ntfs_filldir {
+ struct inode *dir;
+ filldir_t filldir;
+ unsigned int type;
+ u32 ph, pl;
+ void *dirent;
+ char *name;
+ int namelen;
+ int ret_code;
+};
+
+static int ntfs_printcb(ntfs_u8 *entry, void *param)
+{
+ unsigned long inum = NTFS_GETU64(entry) & 0xffffffffffff;
+ struct ntfs_filldir *nf = param;
+ u32 flags = NTFS_GETU32(entry + 0x48);
+ char show_sys_files = 0;
+ u8 name_len = NTFS_GETU8(entry + 0x50);
+ u8 name_type = NTFS_GETU8(entry + 0x51);
+ int err;
+ unsigned file_type;
+
+ switch (nf->type) {
+ case ngt_dos:
+ /* Don't display long names. */
+ if (!(name_type & 2))
+ return 0;
+ break;
+ case ngt_nt:
+ /* Don't display short-only names. */
+ if ((name_type & 3) == 2)
+ return 0;
+ break;
+ case ngt_posix:
+ break;
+ case ngt_full:
+ show_sys_files = 1;
+ break;
+ default:
+ BUG();
+ }
+ err = ntfs_encodeuni(NTFS_INO2VOL(nf->dir), (ntfs_u16*)(entry + 0x52),
+ name_len, &nf->name, &nf->namelen);
+ if (err) {
+ ntfs_debug(DEBUG_OTHER, "%s(): Skipping unrepresentable "
+ "file.\n", __FUNCTION__);
+ err = 0;
+ goto err_noname;
+ }
+ if (!show_sys_files && inum < 0x10UL) {
+ ntfs_debug(DEBUG_OTHER, "%s(): Skipping system file (%s).\n",
+ __FUNCTION__, nf->name);
+ err = 0;
+ goto err_ret;
+ }
+ /* Do not return ".", as this is faked. */
+ if (nf->namelen == 1 && nf->name[0] == '.') {
+ ntfs_debug(DEBUG_OTHER, "%s(): Skipping \".\"\n", __FUNCTION__);
+ err = 0;
+ goto err_ret;
+ }
+ nf->name[nf->namelen] = 0;
+ if (flags & 0x10000000) /* FILE_ATTR_DUP_FILE_NAME_INDEX_PRESENT */
+ file_type = DT_DIR;
+ else
+ file_type = DT_REG;
+ ntfs_debug(DEBUG_OTHER, "%s(): Calling filldir for %s with "
+ "len %i, f_pos 0x%Lx, inode %lu, %s.\n", __FUNCTION__,
+ nf->name, nf->namelen, (loff_t)(nf->ph << 16) | nf->pl,
+ inum, file_type == DT_DIR ? "DT_DIR" : "DT_REG");
+ /*
+ * Userspace side of filldir expects an off_t rather than an loff_t.
+ * And it also doesn't like the most significant bit being set as it
+ * then considers the value to be negative. Thus this implementation
+ * limits the number of index records to 32766, which should be plenty.
+ */
+ err = nf->filldir(nf->dirent, nf->name, nf->namelen,
+ (loff_t)(nf->ph << 16) | nf->pl, inum, file_type);
+ if (err)
+ nf->ret_code = err;
+err_ret:
+ ntfs_free(nf->name);
+err_noname:
+ nf->namelen = 0;
+ nf->name = NULL;
+ return err;
+}
+
+/*
+ * readdir returns '.', then '..', then the directory entries in sequence.
+ * As the root directory contains an entry for itself, '.' is not emulated for
+ * the root directory.
+ */
+static int ntfs_readdir(struct file* filp, void *dirent, filldir_t filldir)
+{
+ struct inode *dir = filp->f_dentry->d_inode;
+ int err;
+ struct ntfs_filldir cb;
+
+ cb.ret_code = 0;
+ cb.pl = filp->f_pos & 0xffff;
+ cb.ph = (filp->f_pos >> 16) & 0x7fff;
+ filp->f_pos = (loff_t)(cb.ph << 16) | cb.pl;
+ ntfs_debug(DEBUG_OTHER, "%s(): Entering for inode %lu, f_pos 0x%Lx, "
+ "i_mode 0x%x, i_count %lu.\n", __FUNCTION__,
+ dir->i_ino, filp->f_pos, (unsigned int)dir->i_mode,
+ atomic_read(&dir->i_count));
+ if (!cb.ph) {
+ /* Start of directory. Emulate "." and "..". */
+ if (!cb.pl) {
+ ntfs_debug(DEBUG_OTHER, "%s(): Calling filldir for . "
+ "with len 1, f_pos 0x%Lx, inode %lu, "
+ "DT_DIR.\n", __FUNCTION__, filp->f_pos,
+ dir->i_ino);
+ cb.ret_code = filldir(dirent, ".", 1, filp->f_pos,
+ dir->i_ino, DT_DIR);
+ if (cb.ret_code)
+ goto done;
+ cb.pl++;
+ filp->f_pos = (loff_t)(cb.ph << 16) | cb.pl;
+ }
+ if (cb.pl == (u32)1) {
+ ntfs_debug(DEBUG_OTHER, "%s(): Calling filldir for .. "
+ "with len 2, f_pos 0x%Lx, inode %lu, "
+ "DT_DIR.\n", __FUNCTION__, filp->f_pos,
+ filp->f_dentry->d_parent->d_inode->i_ino);
+ cb.ret_code = filldir(dirent, "..", 2, filp->f_pos,
+ filp->f_dentry->d_parent->d_inode->i_ino,
+ DT_DIR);
+ if (cb.ret_code)
+ goto done;
+ cb.pl++;
+ filp->f_pos = (loff_t)(cb.ph << 16) | cb.pl;
+ }
+ } else if (cb.ph >= 0x7fff)
+ /* End of directory. */
+ goto done;
+ cb.dir = dir;
+ cb.filldir = filldir;
+ cb.dirent = dirent;
+ cb.type = NTFS_INO2VOL(dir)->ngt;
+ do {
+ ntfs_debug(DEBUG_OTHER, "%s(): Looking for next file using "
+ "ntfs_getdir_unsorted(), f_pos 0x%Lx.\n",
+ __FUNCTION__, (loff_t)(cb.ph << 16) | cb.pl);
+ err = ntfs_getdir_unsorted(NTFS_LINO2NINO(dir), &cb.ph, &cb.pl,
+ ntfs_printcb, &cb);
+ } while (!err && !cb.ret_code && cb.ph < 0x7fff);
+ filp->f_pos = (loff_t)(cb.ph << 16) | cb.pl;
+ ntfs_debug(DEBUG_OTHER, "%s(): After ntfs_getdir_unsorted()"
+ " calls, f_pos 0x%Lx.\n", __FUNCTION__, filp->f_pos);
+ if (!err) {
+done:
+#ifdef DEBUG
+ if (!cb.ret_code)
+ ntfs_debug(DEBUG_OTHER, "%s(): EOD, f_pos 0x%Lx, "
+ "returning 0.\n", __FUNCTION__,
+ filp->f_pos);
+ else
+ ntfs_debug(DEBUG_OTHER, "%s(): filldir returned %i, "
+ "returning 0, f_pos 0x%Lx.\n",
+ __FUNCTION__, cb.ret_code, filp->f_pos);
+#endif
+ return 0;
+ }
+ ntfs_debug(DEBUG_OTHER, "%s(): Returning %i, f_pos 0x%Lx.\n",
+ __FUNCTION__, err, filp->f_pos);
+ return err;
+}
+
+/* Copied from vfat driver. */
+static int simple_getbool(char *s, int *setval)
+{
+ if (s) {
+ if (!strcmp(s, "1") || !strcmp(s, "yes") || !strcmp(s, "true"))
+ *setval = 1;
+ else if (!strcmp(s, "0") || !strcmp(s, "no") ||
+ !strcmp(s, "false"))
+ *setval = 0;
+ else
+ return 0;
+ } else
+ *setval = 1;
+ return 1;
+}
+
+/*
+ * This needs to be outside parse_options() otherwise a remount will reset
+ * these unintentionally.
+ */
+static void init_ntfs_super_block(ntfs_volume* vol)
+{
+ vol->uid = vol->gid = 0;
+ vol->umask = 0077;
+ vol->ngt = ngt_nt;
+ vol->nls_map = (void*)-1;
+ vol->mft_zone_multiplier = -1;
+}
+
+/* Parse the (re)mount options. */
+static int parse_options(ntfs_volume *vol, char *opt)
+{
+ char *value; /* Defaults if not specified and !remount. */
+ ntfs_uid_t uid = -1; /* 0, root user only */
+ ntfs_gid_t gid = -1; /* 0, root user only */
+ int umask = -1; /* 0077, owner access only */
+ unsigned int ngt = -1; /* ngt_nt */
+ void *nls_map = NULL; /* Try to load the default NLS. */
+ int use_utf8 = -1; /* If no NLS specified and loading the default
+ NLS failed use utf8. */
+ int mft_zone_mul = -1; /* 1 */
+
+ if (!opt)
+ goto done;
+ for (opt = strtok(opt, ","); opt; opt = strtok(NULL, ",")) {
+ if ((value = strchr(opt, '=')) != NULL)
+ *value ++= '\0';
+ if (strcmp(opt, "uid") == 0) {
+ if (!value || !*value)
+ goto needs_arg;
+ uid = simple_strtoul(value, &value, 0);
+ if (*value) {
+ printk(KERN_ERR "NTFS: uid invalid argument\n");
+ return 0;
+ }
+ } else if (strcmp(opt, "gid") == 0) {
+ if (!value || !*value)
+ goto needs_arg;
+ gid = simple_strtoul(value, &value, 0);
+ if (*value) {
+ printk(KERN_ERR "NTFS: gid invalid argument\n");
+ return 0;
+ }
+ } else if (strcmp(opt, "umask") == 0) {
+ if (!value || !*value)
+ goto needs_arg;
+ umask = simple_strtoul(value, &value, 0);
+ if (*value) {
+ printk(KERN_ERR "NTFS: umask invalid "
+ "argument\n");
+ return 0;
+ }
+ } else if (strcmp(opt, "mft_zone_multiplier") == 0) {
+ unsigned long ul;
+
+ if (!value || !*value)
+ goto needs_arg;
+ ul = simple_strtoul(value, &value, 0);
+ if (*value) {
+ printk(KERN_ERR "NTFS: mft_zone_multiplier "
+ "invalid argument\n");
+ return 0;
+ }
+ if (ul >= 1 && ul <= 4)
+ mft_zone_mul = ul;
+ else {
+ mft_zone_mul = 1;
+ printk(KERN_WARNING "NTFS: mft_zone_multiplier "
+ "out of range. Setting to 1.\n");
+ }
+ } else if (strcmp(opt, "posix") == 0) {
+ int val;
+ if (!value || !*value)
+ goto needs_arg;
+ if (!simple_getbool(value, &val))
+ goto needs_bool;
+ ngt = val ? ngt_posix : ngt_nt;
+ } else if (strcmp(opt, "show_sys_files") == 0) {
+ int val = 0;
+ if (!value || !*value)
+ val = 1;
+ else if (!simple_getbool(value, &val))
+ goto needs_bool;
+ ngt = val ? ngt_full : ngt_nt;
+ } else if (strcmp(opt, "iocharset") == 0) {
+ if (!value || !*value)
+ goto needs_arg;
+ nls_map = load_nls(value);
+ if (!nls_map) {
+ printk(KERN_ERR "NTFS: charset not found");
+ return 0;
+ }
+ } else if (strcmp(opt, "utf8") == 0) {
+ int val = 0;
+ if (!value || !*value)
+ val = 1;
+ else if (!simple_getbool(value, &val))
+ goto needs_bool;
+ use_utf8 = val;
+ } else {
+ printk(KERN_ERR "NTFS: unkown option '%s'\n", opt);
+ return 0;
+ }
+ }
+done:
+ if (use_utf8 == -1) {
+ /* utf8 was not specified at all. */
+ if (!nls_map) {
+ /*
+ * No NLS was specified. If first mount, load the
+ * default NLS, otherwise don't change the NLS setting.
+ */
+ if (vol->nls_map == (void*)-1)
+ vol->nls_map = load_nls_default();
+ } else {
+ /* If an NLS was already loaded, unload it first. */
+ if (vol->nls_map && vol->nls_map != (void*)-1)
+ unload_nls(vol->nls_map);
+ /* Use the specified NLS. */
+ vol->nls_map = nls_map;
+ }
+ } else {
+ /* utf8 was specified. */
+ if (use_utf8 && nls_map) {
+ unload_nls(nls_map);
+ printk(KERN_ERR "NTFS: utf8 cannot be combined with "
+ "iocharset.\n");
+ return 0;
+ }
+ /* If an NLS was already loaded, unload it first. */
+ if (vol->nls_map && vol->nls_map != (void*)-1)
+ unload_nls(vol->nls_map);
+ if (!use_utf8) {
+ /* utf8 was specified as false. */
+ if (!nls_map)
+ /* No NLS was specified, load the default. */
+ vol->nls_map = load_nls_default();
+ else
+ /* Use the specified NLS. */
+ vol->nls_map = nls_map;
+ } else
+ /* utf8 was specified as true. */
+ vol->nls_map = NULL;
+ }
+ if (uid != -1)
+ vol->uid = uid;
+ if (gid != -1)
+ vol->gid = gid;
+ if (umask != -1)
+ vol->umask = (ntmode_t)umask;
+ if (ngt != -1)
+ vol->ngt = ngt;
+ if (mft_zone_mul != -1) {
+ /* mft_zone_multiplier was specified. */
+ if (vol->mft_zone_multiplier != -1) {
+ /* This is a remount, ignore a change and warn user. */
+ if (vol->mft_zone_multiplier != mft_zone_mul)
+ printk(KERN_WARNING "NTFS: Ignoring changes in "
+ "mft_zone_multiplier on "
+ "remount. If you want to "
+ "change this you need to "
+ "umount and mount again.\n");
+ } else
+ /* Use the specified multiplier. */
+ vol->mft_zone_multiplier = mft_zone_mul;
+ } else if (vol->mft_zone_multiplier == -1)
+ /* No multiplier specified and first mount, so set default. */
+ vol->mft_zone_multiplier = 1;
+ return 1;
+needs_arg:
+ printk(KERN_ERR "NTFS: %s needs an argument", opt);
+ return 0;
+needs_bool:
+ printk(KERN_ERR "NTFS: %s needs boolean argument", opt);
+ return 0;
+}
+
+static struct dentry *ntfs_lookup(struct inode *dir, struct dentry *d)
+{
+ struct inode *res = 0;
+ char *item = 0;
+ ntfs_iterate_s walk;
+ int err;
+
+ ntfs_debug(DEBUG_NAME1, "%s(): Looking up %s in directory ino 0x%x.\n",
+ __FUNCTION__, d->d_name.name, (unsigned)dir->i_ino);
+ walk.name = NULL;
+ walk.namelen = 0;
+ /* Convert to wide string. */
+ err = ntfs_decodeuni(NTFS_INO2VOL(dir), (char*)d->d_name.name,
+ d->d_name.len, &walk.name, &walk.namelen);
+ if (err)
+ goto err_ret;
+ item = ntfs_malloc(ITEM_SIZE);
+ if (!item) {
+ err = -ENOMEM;
+ goto err_ret;
+ }
+ /* ntfs_getdir will place the directory entry into item, and the first
+ * long long is the MFT record number. */
+ walk.type = BY_NAME;
+ walk.dir = NTFS_LINO2NINO(dir);
+ walk.result = item;
+ if (ntfs_getdir_byname(&walk))
+ res = iget(dir->i_sb, NTFS_GETU32(item));
+ d_add(d, res);
+ ntfs_free(item);
+ ntfs_free(walk.name);
+ /* Always return success, the dcache will handle negative entries. */
+ return NULL;
+err_ret:
+ ntfs_free(walk.name);
+ return ERR_PTR(err);
+}
+
+static struct file_operations ntfs_file_operations = {
+ llseek: generic_file_llseek,
+ read: ntfs_read,
+#ifdef CONFIG_NTFS_RW
+ write: ntfs_write,
+#endif
+ open: generic_file_open,
+};
+
+static struct inode_operations ntfs_inode_operations;
+
+#ifdef CONFIG_NTFS_RW
+static int ntfs_create(struct inode* dir, struct dentry *d, int mode)
+{
+ struct inode *r = 0;
+ ntfs_inode *ino = 0;
+ ntfs_volume *vol;
+ int error = 0;
+ ntfs_attribute *si;
+
+ r = new_inode(dir->i_sb);
+ if (!r) {
+ error = -ENOMEM;
+ goto fail;
+ }
+ ntfs_debug(DEBUG_OTHER, "ntfs_create %s\n", d->d_name.name);
+ vol = NTFS_INO2VOL(dir);
+ ino = NTFS_LINO2NINO(r);
+ error = ntfs_alloc_file(NTFS_LINO2NINO(dir), ino, (char*)d->d_name.name,
+ d->d_name.len);
+ if (error) {
+ ntfs_error("ntfs_alloc_file FAILED: error = %i", error);
+ goto fail;
+ }
+ /* Not doing this one was causing a huge amount of corruption! Now the
+ * bugger bytes the dust! (-8 (AIA) */
+ r->i_ino = ino->i_number;
+ error = ntfs_update_inode(ino);
+ if (error)
+ goto fail;
+ error = ntfs_update_inode(NTFS_LINO2NINO(dir));
+ if (error)
+ goto fail;
+ r->i_uid = vol->uid;
+ r->i_gid = vol->gid;
+ /* FIXME: dirty? dev? */
+ /* Get the file modification times from the standard information. */
+ si = ntfs_find_attr(ino, vol->at_standard_information, NULL);
+ if (si) {
+ char *attr = si->d.data;
+ r->i_atime = ntfs_ntutc2unixutc(NTFS_GETU64(attr + 0x18));
+ r->i_ctime = ntfs_ntutc2unixutc(NTFS_GETU64(attr));
+ r->i_mtime = ntfs_ntutc2unixutc(NTFS_GETU64(attr + 8));
+ }
+ /* It's not a directory */
+ r->i_op = &ntfs_inode_operations;
+ r->i_fop = &ntfs_file_operations;
+ r->i_mode = S_IFREG | S_IRUGO;
+#ifdef CONFIG_NTFS_RW
+ r->i_mode |= S_IWUGO;
+#endif
+ r->i_mode &= ~vol->umask;
+ insert_inode_hash(r);
+ d_instantiate(d, r);
+ return 0;
+ fail:
+ if (r)
+ iput(r);
+ return error;
+}
+
+static int _linux_ntfs_mkdir(struct inode *dir, struct dentry* d, int mode)
+{
+ int error;
+ struct inode *r = 0;
+ ntfs_volume *vol;
+ ntfs_inode *ino;
+ ntfs_attribute *si;
+
+ ntfs_debug (DEBUG_DIR1, "mkdir %s in %x\n", d->d_name.name, dir->i_ino);
+ error = -ENAMETOOLONG;
+ if (d->d_name.len > /* FIXME: */ 255)
+ goto out;
+ error = -EIO;
+ r = new_inode(dir->i_sb);
+ if (!r)
+ goto out;
+ vol = NTFS_INO2VOL(dir);
+ ino = NTFS_LINO2NINO(r);
+ error = ntfs_mkdir(NTFS_LINO2NINO(dir), d->d_name.name, d->d_name.len,
+ ino);
+ if (error)
+ goto out;
+ /* Not doing this one was causing a huge amount of corruption! Now the
+ * bugger bytes the dust! (-8 (AIA) */
+ r->i_ino = ino->i_number;
+ r->i_uid = vol->uid;
+ r->i_gid = vol->gid;
+ si = ntfs_find_attr(ino, vol->at_standard_information, NULL);
+ if (si) {
+ char *attr = si->d.data;
+ r->i_atime = ntfs_ntutc2unixutc(NTFS_GETU64(attr + 0x18));
+ r->i_ctime = ntfs_ntutc2unixutc(NTFS_GETU64(attr));
+ r->i_mtime = ntfs_ntutc2unixutc(NTFS_GETU64(attr + 8));
+ }
+ /* It's a directory. */
+ r->i_op = &ntfs_dir_inode_operations;
+ r->i_fop = &ntfs_dir_operations;
+ r->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
+#ifdef CONFIG_NTFS_RW
+ r->i_mode |= S_IWUGO;
+#endif
+ r->i_mode &= ~vol->umask;
+
+ insert_inode_hash(r);
+ d_instantiate(d, r);
+ error = 0;
+ out:
+ ntfs_debug (DEBUG_DIR1, "mkdir returns %d\n", error);
+ return error;
+}
+#endif
+
+static struct file_operations ntfs_dir_operations = {
+ read: generic_read_dir,
+ readdir: ntfs_readdir,
+};
+
+static struct inode_operations ntfs_dir_inode_operations = {
+ lookup: ntfs_lookup,
+#ifdef CONFIG_NTFS_RW
+ create: ntfs_create,
+ mkdir: _linux_ntfs_mkdir,
+#endif
+};
+
+/* ntfs_read_inode() is called by the Virtual File System (the kernel layer
+ * that deals with filesystems) when iget is called requesting an inode not
+ * already present in the inode table. Typically filesystems have separate
+ * inode_operations for directories, files and symlinks. */
+static void ntfs_read_inode(struct inode* inode)
+{
+ ntfs_volume *vol;
+ ntfs_inode *ino;
+ ntfs_attribute *data;
+ ntfs_attribute *si;
+
+ vol = NTFS_INO2VOL(inode);
+ inode->i_mode = 0;
+ ntfs_debug(DEBUG_OTHER, "ntfs_read_inode 0x%lx\n", inode->i_ino);
+ switch (inode->i_ino) {
+ /* Those are loaded special files. */
+ case FILE_Mft:
+ if (!vol->mft_ino || ((vol->ino_flags & 1) == 0))
+ goto sys_file_error;
+ ntfs_memcpy(&inode->u.ntfs_i, vol->mft_ino, sizeof(ntfs_inode));
+ ino = vol->mft_ino;
+ vol->mft_ino = &inode->u.ntfs_i;
+ vol->ino_flags &= ~1;
+ ntfs_free(ino);
+ ino = vol->mft_ino;
+ ntfs_debug(DEBUG_OTHER, "Opening $MFT!\n");
+ break;
+ case FILE_MftMirr:
+ if (!vol->mftmirr || ((vol->ino_flags & 2) == 0))
+ goto sys_file_error;
+ ntfs_memcpy(&inode->u.ntfs_i, vol->mftmirr, sizeof(ntfs_inode));
+ ino = vol->mftmirr;
+ vol->mftmirr = &inode->u.ntfs_i;
+ vol->ino_flags &= ~2;
+ ntfs_free(ino);
+ ino = vol->mftmirr;
+ ntfs_debug(DEBUG_OTHER, "Opening $MFTMirr!\n");
+ break;
+ case FILE_BitMap:
+ if (!vol->bitmap || ((vol->ino_flags & 4) == 0))
+ goto sys_file_error;
+ ntfs_memcpy(&inode->u.ntfs_i, vol->bitmap, sizeof(ntfs_inode));
+ ino = vol->bitmap;
+ vol->bitmap = &inode->u.ntfs_i;
+ vol->ino_flags &= ~4;
+ ntfs_free(ino);
+ ino = vol->bitmap;
+ ntfs_debug(DEBUG_OTHER, "Opening $Bitmap!\n");
+ break;
+ case FILE_LogFile ... FILE_AttrDef:
+ /* No need to log root directory accesses. */
+ case FILE_Boot ... FILE_UpCase:
+ ntfs_debug(DEBUG_OTHER, "Opening system file %i!\n",
+ inode->i_ino);
+ default:
+ ino = &inode->u.ntfs_i;
+ if (!ino || ntfs_init_inode(ino, NTFS_INO2VOL(inode),
+ inode->i_ino))
+ {
+ ntfs_debug(DEBUG_OTHER, "NTFS: Error loading inode "
+ "0x%x\n", (unsigned int)inode->i_ino);
+ return;
+ }
+ }
+ /* Set uid/gid from mount options */
+ inode->i_uid = vol->uid;
+ inode->i_gid = vol->gid;
+ inode->i_nlink = 1;
+ /* Use the size of the data attribute as file size */
+ data = ntfs_find_attr(ino, vol->at_data, NULL);
+ if (!data)
+ inode->i_size = 0;
+ else
+ inode->i_size = data->size;
+ /* Get the file modification times from the standard information. */
+ si = ntfs_find_attr(ino, vol->at_standard_information, NULL);
+ if (si) {
+ char *attr = si->d.data;
+ inode->i_atime = ntfs_ntutc2unixutc(NTFS_GETU64(attr + 0x18));
+ inode->i_ctime = ntfs_ntutc2unixutc(NTFS_GETU64(attr));
+ inode->i_mtime = ntfs_ntutc2unixutc(NTFS_GETU64(attr + 8));
+ }
+ /* If it has an index root, it's a directory. */
+ if (ntfs_find_attr(ino, vol->at_index_root, "$I30")) {
+ ntfs_attribute *at;
+ at = ntfs_find_attr(ino, vol->at_index_allocation, "$I30");
+ inode->i_size = at ? at->size : 0;
+ inode->i_op = &ntfs_dir_inode_operations;
+ inode->i_fop = &ntfs_dir_operations;
+ inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
+ } else {
+ inode->i_op = &ntfs_inode_operations;
+ inode->i_fop = &ntfs_file_operations;
+ inode->i_mode = S_IFREG | S_IRUGO;
+ }
+#ifdef CONFIG_NTFS_RW
+ if (!data || !(data->flags & (ATTR_IS_COMPRESSED | ATTR_IS_ENCRYPTED)))
+ inode->i_mode |= S_IWUGO;
+#endif
+ inode->i_mode &= ~vol->umask;
+ return;
+sys_file_error:
+ ntfs_error("Critical error. Tried to call ntfs_read_inode() before we "
+ "have completed read_super() or VFS error.\n");
+ // FIXME: Should we panic() at this stage?
+}
+
+#ifdef CONFIG_NTFS_RW
+static void ntfs_write_inode(struct inode *ino, int unused)
+{
+ lock_kernel();
+ ntfs_debug(DEBUG_LINUX, "ntfs_write_inode 0x%x\n", ino->i_ino);
+ ntfs_update_inode(NTFS_LINO2NINO(ino));
+ unlock_kernel();
+}
+#endif
+
+static void _ntfs_clear_inode(struct inode *inode)
+{
+ ntfs_inode *ino;
+ ntfs_volume *vol;
+
+ lock_kernel();
+ ntfs_debug(DEBUG_OTHER, "_ntfs_clear_inode 0x%x\n", inode->i_ino);
+ vol = NTFS_INO2VOL(inode);
+ if (!vol)
+ ntfs_error("_ntfs_clear_inode: vol = NTFS_INO2VOL(inode) is "
+ "NULL.\n");
+ switch (inode->i_ino) {
+ case FILE_Mft:
+ if (vol->mft_ino && ((vol->ino_flags & 1) == 0)) {
+ ino = (ntfs_inode*)ntfs_malloc(sizeof(ntfs_inode));
+ ntfs_memcpy(ino, &inode->u.ntfs_i, sizeof(ntfs_inode));
+ vol->mft_ino = ino;
+ vol->ino_flags |= 1;
+ goto unl_out;
+ }
+ break;
+ case FILE_MftMirr:
+ if (vol->mftmirr && ((vol->ino_flags & 2) == 0)) {
+ ino = (ntfs_inode*)ntfs_malloc(sizeof(ntfs_inode));
+ ntfs_memcpy(ino, &inode->u.ntfs_i, sizeof(ntfs_inode));
+ vol->mftmirr = ino;
+ vol->ino_flags |= 2;
+ goto unl_out;
+ }
+ break;
+ case FILE_BitMap:
+ if (vol->bitmap && ((vol->ino_flags & 4) == 0)) {
+ ino = (ntfs_inode*)ntfs_malloc(sizeof(ntfs_inode));
+ ntfs_memcpy(ino, &inode->u.ntfs_i, sizeof(ntfs_inode));
+ vol->bitmap = ino;
+ vol->ino_flags |= 4;
+ goto unl_out;
+ }
+ break;
+ /* Nothing. Just clear the inode and exit. */
+ }
+ ntfs_clear_inode(&inode->u.ntfs_i);
+unl_out:
+ unlock_kernel();
+ return;
+}
+
+/* Called when umounting a filesystem by do_umount() in fs/super.c. */
+static void ntfs_put_super(struct super_block *sb)
+{
+ ntfs_volume *vol;
+
+ ntfs_debug(DEBUG_OTHER, "ntfs_put_super\n");
+ vol = NTFS_SB2VOL(sb);
+ ntfs_release_volume(vol);
+ if (vol->nls_map)
+ unload_nls(vol->nls_map);
+ ntfs_debug(DEBUG_OTHER, "ntfs_put_super: done\n");
+}
+
+/* Called by the kernel when asking for stats. */
+static int ntfs_statfs(struct super_block *sb, struct statfs *sf)
+{
+ struct inode *mft;
+ ntfs_volume *vol;
+ __s64 size;
+ int error;
+
+ ntfs_debug(DEBUG_OTHER, "ntfs_statfs\n");
+ vol = NTFS_SB2VOL(sb);
+ sf->f_type = NTFS_SUPER_MAGIC;
+ sf->f_bsize = vol->cluster_size;
+ error = ntfs_get_volumesize(NTFS_SB2VOL(sb), &size);
+ if (error)
+ return error;
+ sf->f_blocks = size; /* Volumesize is in clusters. */
+ size = (__s64)ntfs_get_free_cluster_count(vol->bitmap);
+ /* Just say zero if the call failed. */
+ if (size < 0LL)
+ size = 0;
+ sf->f_bfree = sf->f_bavail = size;
+ ntfs_debug(DEBUG_OTHER, "ntfs_statfs: calling mft = iget(sb, "
+ "FILE_Mft)\n");
+ mft = iget(sb, FILE_Mft);
+ ntfs_debug(DEBUG_OTHER, "ntfs_statfs: iget(sb, FILE_Mft) returned "
+ "0x%x\n", mft);
+ if (!mft)
+ return -EIO;
+ sf->f_files = mft->i_size >> vol->mft_record_size_bits;
+ ntfs_debug(DEBUG_OTHER, "ntfs_statfs: calling iput(mft)\n");
+ iput(mft);
+ /* Should be read from volume. */
+ sf->f_namelen = 255;
+ return 0;
+}
+
+/* Called when remounting a filesystem by do_remount_sb() in fs/super.c. */
+static int ntfs_remount_fs(struct super_block *sb, int *flags, char *options)
+{
+ if (!parse_options(NTFS_SB2VOL(sb), options))
+ return -EINVAL;
+ return 0;
+}
+
+/* Define the super block operation that are implemented */
+static struct super_operations ntfs_super_operations = {
+ read_inode: ntfs_read_inode,
+#ifdef CONFIG_NTFS_RW
+ write_inode: ntfs_write_inode,
+#endif
+ put_super: ntfs_put_super,
+ statfs: ntfs_statfs,
+ remount_fs: ntfs_remount_fs,
+ clear_inode: _ntfs_clear_inode,
+};
+
+/**
+ * is_boot_sector_ntfs - check an NTFS boot sector for validity
+ * @b: buffer containing bootsector to check
+ *
+ * Check whether @b contains a valid NTFS boot sector.
+ * Return 1 if @b is a valid NTFS bootsector or 0 if not.
+ */
+static int is_boot_sector_ntfs(ntfs_u8 *b)
+{
+ ntfs_u32 i;
+
+ /* FIXME: We don't use checksumming yet as NT4(SP6a) doesn't either...
+ * But we might as well have the code ready to do it. (AIA) */
+#if 0
+ /* Calculate the checksum. */
+ if (b < b + 0x50) {
+ ntfs_u32 *u;
+ ntfs_u32 *bi = (ntfs_u32 *)(b + 0x50);
+
+ for (u = bi, i = 0; u < bi; ++u)
+ i += NTFS_GETU32(*u);
+ }
+#endif
+ /* Check magic is "NTFS " */
+ if (b[3] != 0x4e) goto not_ntfs;
+ if (b[4] != 0x54) goto not_ntfs;
+ if (b[5] != 0x46) goto not_ntfs;
+ if (b[6] != 0x53) goto not_ntfs;
+ for (i = 7; i < 0xb; ++i)
+ if (b[i] != 0x20) goto not_ntfs;
+ /* Check bytes per sector value is between 512 and 4096. */
+ if (b[0xb] != 0) goto not_ntfs;
+ if (b[0xc] > 0x10) goto not_ntfs;
+ /* Check sectors per cluster value is valid. */
+ switch (b[0xd]) {
+ case 1: case 2: case 4: case 8: case 16:
+ case 32: case 64: case 128:
+ break;
+ default:
+ goto not_ntfs;
+ }
+ /* Check reserved sectors value and four other fields are zero. */
+ for (i = 0xe; i < 0x15; ++i)
+ if (b[i] != 0) goto not_ntfs;
+ if (b[0x16] != 0) goto not_ntfs;
+ if (b[0x17] != 0) goto not_ntfs;
+ for (i = 0x20; i < 0x24; ++i)
+ if (b[i] != 0) goto not_ntfs;
+ /* Check clusters per file record segment value is valid. */
+ if (b[0x40] < 0xe1 || b[0x40] > 0xf7) {
+ switch (b[0x40]) {
+ case 1: case 2: case 4: case 8: case 16: case 32: case 64:
+ break;
+ default:
+ goto not_ntfs;
+ }
+ }
+ /* Check clusters per index block value is valid. */
+ if (b[0x44] < 0xe1 || b[0x44] > 0xf7) {
+ switch (b[0x44]) {
+ case 1: case 2: case 4: case 8: case 16: case 32: case 64:
+ break;
+ default:
+ goto not_ntfs;
+ }
+ }
+ return 1;
+not_ntfs:
+ return 0;
+}
+
+/* Called to mount a filesystem by read_super() in fs/super.c.
+ * Return a super block, the main structure of a filesystem.
+ *
+ * NOTE : Don't store a pointer to an option, as the page containing the
+ * options is freed after ntfs_read_super() returns.
+ *
+ * NOTE : A context switch can happen in kernel code only if the code blocks
+ * (= calls schedule() in kernel/sched.c). */
+struct super_block *ntfs_read_super(struct super_block *sb, void *options,
+ int silent)
+{
+ ntfs_volume *vol;
+ struct buffer_head *bh;
+ int i, to_read, blocksize;
+
+ ntfs_debug(DEBUG_OTHER, "ntfs_read_super\n");
+ vol = NTFS_SB2VOL(sb);
+ init_ntfs_super_block(vol);
+ if (!parse_options(vol, (char*)options))
+ goto ntfs_read_super_vol;
+ blocksize = get_hardsect_size(sb->s_dev);
+ if (blocksize < 512)
+ blocksize = 512;
+ if (set_blocksize(sb->s_dev, blocksize) < 0) {
+ ntfs_error("Unable to set blocksize %d.\n", blocksize);
+ goto ntfs_read_super_vol;
+ }
+ sb->s_blocksize = blocksize;
+ /* Read the super block (boot block). */
+ if (!(bh = sb_bread(sb, 0))) {
+ ntfs_error("Reading super block failed\n");
+ goto ntfs_read_super_unl;
+ }
+ ntfs_debug(DEBUG_OTHER, "Done reading boot block\n");
+ /* Check for valid 'NTFS' boot sector. */
+ if (!is_boot_sector_ntfs(bh->b_data)) {
+ ntfs_debug(DEBUG_OTHER, "Not a NTFS volume\n");
+ bforget(bh);
+ goto ntfs_read_super_unl;
+ }
+ ntfs_debug(DEBUG_OTHER, "Going to init volume\n");
+ if (ntfs_init_volume(vol, bh->b_data) < 0) {
+ ntfs_debug(DEBUG_OTHER, "Init volume failed.\n");
+ bforget(bh);
+ goto ntfs_read_super_unl;
+ }
+ ntfs_debug(DEBUG_OTHER, "$Mft at cluster 0x%lx\n", vol->mft_lcn);
+ brelse(bh);
+ vol->sb = sb;
+ if (vol->cluster_size > PAGE_SIZE) {
+ ntfs_error("Partition cluster size is not supported yet (it "
+ "is > max kernel blocksize).\n");
+ goto ntfs_read_super_unl;
+ }
+ ntfs_debug(DEBUG_OTHER, "Done to init volume\n");
+ /* Inform the kernel that a device block is a NTFS cluster. */
+ sb->s_blocksize = vol->cluster_size;
+ sb->s_blocksize_bits = vol->cluster_size_bits;
+ if (blocksize != vol->cluster_size &&
+ set_blocksize(sb->s_dev, sb->s_blocksize) < 0) {
+ ntfs_error("Cluster size too small for device.\n");
+ goto ntfs_read_super_unl;
+ }
+ ntfs_debug(DEBUG_OTHER, "set_blocksize\n");
+ /* Allocate an MFT record (MFT record can be smaller than a cluster). */
+ i = vol->cluster_size;
+ if (i < vol->mft_record_size)
+ i = vol->mft_record_size;
+ if (!(vol->mft = ntfs_malloc(i)))
+ goto ntfs_read_super_unl;
+
+ /* Read at least the MFT record for $Mft. */
+ to_read = vol->mft_clusters_per_record;
+ if (to_read < 1)
+ to_read = 1;
+ for (i = 0; i < to_read; i++) {
+ if (!(bh = sb_bread(sb, vol->mft_lcn + i))) {
+ ntfs_error("Could not read $Mft record 0\n");
+ goto ntfs_read_super_mft;
+ }
+ ntfs_memcpy(vol->mft + ((__s64)i << vol->cluster_size_bits),
+ bh->b_data, vol->cluster_size);
+ brelse(bh);
+ ntfs_debug(DEBUG_OTHER, "Read cluster 0x%x\n",
+ vol->mft_lcn + i);
+ }
+ /* Check and fixup this MFT record */
+ if (!ntfs_check_mft_record(vol, vol->mft)){
+ ntfs_error("Invalid $Mft record 0\n");
+ goto ntfs_read_super_mft;
+ }
+ /* Inform the kernel about which super operations are available. */
+ sb->s_op = &ntfs_super_operations;
+ sb->s_magic = NTFS_SUPER_MAGIC;
+ sb->s_maxbytes = MAX_LFS_FILESIZE;
+ ntfs_debug(DEBUG_OTHER, "Reading special files\n");
+ if (ntfs_load_special_files(vol)) {
+ ntfs_error("Error loading special files\n");
+ goto ntfs_read_super_mft;
+ }
+ ntfs_debug(DEBUG_OTHER, "Getting RootDir\n");
+ /* Get the root directory. */
+ if (!(sb->s_root = d_alloc_root(iget(sb, FILE_root)))) {
+ ntfs_error("Could not get root dir inode\n");
+ goto ntfs_read_super_mft;
+ }
+ntfs_read_super_ret:
+ ntfs_debug(DEBUG_OTHER, "read_super: done\n");
+ return sb;
+ntfs_read_super_mft:
+ ntfs_free(vol->mft);
+ntfs_read_super_unl:
+ntfs_read_super_vol:
+ sb = NULL;
+ goto ntfs_read_super_ret;
+}
+
+/* Define the filesystem */
+static DECLARE_FSTYPE_DEV(ntfs_fs_type, "ntfs", ntfs_read_super);
+
+static int __init init_ntfs_fs(void)
+{
+ /* Comment this if you trust klogd. There are reasons not to trust it */
+#if defined(DEBUG) && !defined(MODULE)
+ console_verbose();
+#endif
+ printk(KERN_NOTICE "NTFS driver v" NTFS_VERSION " [Flags: R/"
+#ifdef CONFIG_NTFS_RW
+ "W"
+#else
+ "O"
+#endif
+#ifdef DEBUG
+ " DEBUG"
+#endif
+#ifdef MODULE
+ " MODULE"
+#endif
+ "]\n");
+ SYSCTL(1);
+ ntfs_debug(DEBUG_OTHER, "registering %s\n", ntfs_fs_type.name);
+ /* Add this filesystem to the kernel table of filesystems. */
+ return register_filesystem(&ntfs_fs_type);
+}
+
+static void __exit exit_ntfs_fs(void)
+{
+ SYSCTL(0);
+ ntfs_debug(DEBUG_OTHER, "unregistering %s\n", ntfs_fs_type.name);
+ unregister_filesystem(&ntfs_fs_type);
+}
+
+EXPORT_NO_SYMBOLS;
+/*
+ * Not strictly true. The driver was written originally by Martin von Löwis.
+ * I am just maintaining and rewriting it.
+ */
+MODULE_AUTHOR("Anton Altaparmakov <aia21@cus.cam.ac.uk>");
+MODULE_DESCRIPTION("Linux NTFS driver");
+MODULE_LICENSE("GPL");
+#ifdef DEBUG
+MODULE_PARM(ntdebug, "i");
+MODULE_PARM_DESC(ntdebug, "Debug level");
+#endif
+
+module_init(init_ntfs_fs)
+module_exit(exit_ntfs_fs)
+
diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c
new file mode 100644
index 00000000000000..55d8a817a43882
--- /dev/null
+++ b/fs/ntfs/inode.c
@@ -0,0 +1,2322 @@
+/*
+ * inode.c
+ *
+ * Copyright (C) 1995-1999 Martin von Löwis
+ * Copyright (C) 1996 Albert D. Cahalan
+ * Copyright (C) 1996-1997 Régis Duchesne
+ * Copyright (C) 1998 Joseph Malicki
+ * Copyright (C) 1999 Steve Dodd
+ * Copyright (C) 2000-2001 Anton Altaparmakov (AIA)
+ */
+#include "ntfstypes.h"
+#include "ntfsendian.h"
+#include "struct.h"
+#include "inode.h"
+#include <linux/errno.h>
+#include "macros.h"
+#include "attr.h"
+#include "super.h"
+#include "dir.h"
+#include "support.h"
+#include "util.h"
+#include <linux/ntfs_fs.h>
+#include <linux/smp_lock.h>
+
+typedef struct {
+ int recno;
+ unsigned char *record;
+} ntfs_mft_record;
+
+typedef struct {
+ int size;
+ int count;
+ ntfs_mft_record *records;
+} ntfs_disk_inode;
+
+static void ntfs_fill_mft_header(ntfs_u8 *mft, int rec_size, int seq_no,
+ int links, int flags)
+{
+ int fixup_ofs = 0x2a;
+ int fixup_cnt = rec_size / NTFS_SECTOR_SIZE + 1;
+ int attr_ofs = (fixup_ofs + 2 * fixup_cnt + 7) & ~7;
+
+ NTFS_PUTU32(mft + 0x00, 0x454c4946); /* FILE */
+ NTFS_PUTU16(mft + 0x04, fixup_ofs); /* Offset to fixup. */
+ NTFS_PUTU16(mft + 0x06, fixup_cnt); /* Number of fixups. */
+ NTFS_PUTU64(mft + 0x08, 0); /* Logical sequence number. */
+ NTFS_PUTU16(mft + 0x10, seq_no); /* Sequence number. */
+ NTFS_PUTU16(mft + 0x12, links); /* Hard link count. */
+ NTFS_PUTU16(mft + 0x14, attr_ofs); /* Offset to attributes. */
+ NTFS_PUTU16(mft + 0x16, flags); /* Flags: 1 = In use,
+ 2 = Directory. */
+ NTFS_PUTU32(mft + 0x18, attr_ofs + 8); /* Bytes in use. */
+ NTFS_PUTU32(mft + 0x1c, rec_size); /* Total allocated size. */
+ NTFS_PUTU64(mft + 0x20, 0); /* Base mft record. */
+ NTFS_PUTU16(mft + 0x28, 0); /* Next attr instance. */
+ NTFS_PUTU16(mft + fixup_ofs, 1); /* Fixup word. */
+ NTFS_PUTU32(mft + attr_ofs, (__u32)-1); /* End of attributes marker. */
+}
+
+/*
+ * Search in an inode an attribute by type and name.
+ * FIXME: Check that when attributes are inserted all attribute list
+ * attributes are expanded otherwise need to modify this function to deal
+ * with attribute lists. (AIA)
+ */
+ntfs_attribute *ntfs_find_attr(ntfs_inode *ino, int type, char *name)
+{
+ int i;
+
+ if (!ino) {
+ ntfs_error("ntfs_find_attr: NO INODE!\n");
+ return 0;
+ }
+ for (i = 0; i < ino->attr_count; i++) {
+ if (type < ino->attrs[i].type)
+ return 0;
+ if (type == ino->attrs[i].type) {
+ if (!name) {
+ if (!ino->attrs[i].name)
+ return ino->attrs + i;
+ } else if (ino->attrs[i].name &&
+ !ntfs_ua_strncmp(ino->attrs[i].name, name,
+ strlen(name)))
+ return ino->attrs + i;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Insert all attributes from the record mftno of the MFT in the inode ino.
+ * If mftno is a base mft record we abort as soon as we find the attribute
+ * list, but only on the first pass. We will get called later when the attribute
+ * list attribute is being parsed so we need to distinguish the two cases.
+ * FIXME: We should be performing structural consistency checks. (AIA)
+ * Return 0 on success or -errno on error.
+ */
+static int ntfs_insert_mft_attributes(ntfs_inode* ino, char *mft, int mftno)
+{
+ int i, error, type, len, present = 0;
+ char *it;
+
+ /* Check for duplicate extension record. */
+ for(i = 0; i < ino->record_count; i++)
+ if (ino->records[i] == mftno) {
+ if (i)
+ return 0;
+ present = 1;
+ break;
+ }
+ if (!present) {
+ /* (re-)allocate space if necessary. */
+ if (ino->record_count % 8 == 0) {
+ int *new;
+
+ new = ntfs_malloc((ino->record_count + 8) *
+ sizeof(int));
+ if (!new)
+ return -ENOMEM;
+ if (ino->records) {
+ for (i = 0; i < ino->record_count; i++)
+ new[i] = ino->records[i];
+ ntfs_free(ino->records);
+ }
+ ino->records = new;
+ }
+ ino->records[ino->record_count] = mftno;
+ ino->record_count++;
+ }
+ it = mft + NTFS_GETU16(mft + 0x14); /* mft->attrs_offset */
+ do {
+ type = NTFS_GETU32(it);
+ len = NTFS_GETU32(it + 4);
+ if (type != -1) {
+ error = ntfs_insert_attribute(ino, it);
+ if (error)
+ return error;
+ }
+ /* If we have just processed the attribute list and this is
+ * the first time we are parsing this (base) mft record then we
+ * are done so that the attribute list gets parsed before the
+ * entries in the base mft record. Otherwise we run into
+ * problems with encountering attributes out of order and when
+ * this happens with different attribute extents we die. )-:
+ * This way we are ok as the attribute list is always sorted
+ * fully and correctly. (-: */
+ if (type == 0x20 && !present)
+ return 0;
+ it += len;
+ } while (type != -1); /* Attribute listing ends with type -1. */
+ return 0;
+}
+
+/*
+ * Insert a single specific attribute from the record mftno of the MFT in the
+ * inode ino. We disregard the attribute list assuming we have already parsed
+ * it.
+ * FIXME: We should be performing structural consistency checks. (AIA)
+ * Return 0 on success or -errno on error.
+ */
+static int ntfs_insert_mft_attribute(ntfs_inode* ino, int mftno,
+ ntfs_u8 *attr)
+{
+ int i, error, present = 0;
+
+ /* Check for duplicate extension record. */
+ for(i = 0; i < ino->record_count; i++)
+ if (ino->records[i] == mftno) {
+ present = 1;
+ break;
+ }
+ if (!present) {
+ /* (re-)allocate space if necessary. */
+ if (ino->record_count % 8 == 0) {
+ int *new;
+
+ new = ntfs_malloc((ino->record_count + 8) *
+ sizeof(int));
+ if (!new)
+ return -ENOMEM;
+ if (ino->records) {
+ for (i = 0; i < ino->record_count; i++)
+ new[i] = ino->records[i];
+ ntfs_free(ino->records);
+ }
+ ino->records = new;
+ }
+ ino->records[ino->record_count] = mftno;
+ ino->record_count++;
+ }
+ if (NTFS_GETU32(attr) == -1) {
+ ntfs_debug(DEBUG_FILE3, "ntfs_insert_mft_attribute: attribute "
+ "type is -1.\n");
+ return 0;
+ }
+ error = ntfs_insert_attribute(ino, attr);
+ if (error)
+ return error;
+ return 0;
+}
+
+/* Read and insert all the attributes of an 'attribute list' attribute.
+ * Return the number of remaining bytes in *plen. */
+static int parse_attributes(ntfs_inode *ino, ntfs_u8 *alist, int *plen)
+{
+ ntfs_u8 *mft, *attr;
+ int mftno, l, error;
+ int last_mft = -1;
+ int len = *plen;
+ int tries = 0;
+
+ if (!ino->attr) {
+ ntfs_error("parse_attributes: called on inode 0x%x without a "
+ "loaded base mft record.\n", ino->i_number);
+ return -EINVAL;
+ }
+ mft = ntfs_malloc(ino->vol->mft_record_size);
+ if (!mft)
+ return -ENOMEM;
+ while (len > 8) {
+ l = NTFS_GETU16(alist + 4);
+ if (l > len)
+ break;
+ /* Process an attribute description. */
+ mftno = NTFS_GETU32(alist + 0x10);
+ /* FIXME: The mft reference (alist + 0x10) is __s64.
+ * - Not a problem unless we encounter a huge partition.
+ * - Should be consistency checking the sequence numbers
+ * though! This should maybe happen in
+ * ntfs_read_mft_record() itself and a hotfix could
+ * then occur there or the user notified to run
+ * ntfsck. (AIA) */
+ if (mftno != ino->i_number && mftno != last_mft) {
+continue_after_loading_mft_data:
+ last_mft = mftno;
+ error = ntfs_read_mft_record(ino->vol, mftno, mft);
+ if (error) {
+ if (error == -EINVAL && !tries)
+ goto force_load_mft_data;
+failed_reading_mft_data:
+ ntfs_debug(DEBUG_FILE3, "parse_attributes: "
+ "ntfs_read_mft_record(mftno = 0x%x) "
+ "failed\n", mftno);
+ ntfs_free(mft);
+ return error;
+ }
+ }
+ attr = ntfs_find_attr_in_mft_rec(
+ ino->vol, /* ntfs volume */
+ mftno == ino->i_number ?/* mft record is: */
+ ino->attr: /* base record */
+ mft, /* extension record */
+ NTFS_GETU32(alist + 0), /* type */
+ (wchar_t*)(alist + alist[7]), /* name */
+ alist[6], /* name length */
+ 1, /* ignore case */
+ NTFS_GETU16(alist + 24) /* instance number */
+ );
+ if (!attr) {
+ ntfs_error("parse_attributes: mft records 0x%x and/or "
+ "0x%x corrupt!\n", ino->i_number, mftno);
+ ntfs_free(mft);
+ return -EINVAL; /* FIXME: Better error code? (AIA) */
+ }
+ error = ntfs_insert_mft_attribute(ino, mftno, attr);
+ if (error) {
+ ntfs_debug(DEBUG_FILE3, "parse_attributes: "
+ "ntfs_insert_mft_attribute(mftno 0x%x, "
+ "attribute type 0x%x) failed\n", mftno,
+ NTFS_GETU32(alist + 0));
+ ntfs_free(mft);
+ return error;
+ }
+ len -= l;
+ alist += l;
+ }
+ ntfs_free(mft);
+ *plen = len;
+ return 0;
+force_load_mft_data:
+{
+ ntfs_u8 *mft2, *attr2;
+ int mftno2;
+ int last_mft2 = last_mft;
+ int len2 = len;
+ int error2;
+ int found2 = 0;
+ ntfs_u8 *alist2 = alist;
+ /*
+ * We only get here if $DATA wasn't found in $MFT which only happens
+ * on volume mount when $MFT has an attribute list and there are
+ * attributes before $DATA which are inside extent mft records. So
+ * we just skip forward to the $DATA attribute and read that. Then we
+ * restart which is safe as an attribute will not be inserted twice.
+ *
+ * This still will not fix the case where the attribute list is non-
+ * resident, larger than 1024 bytes, and the $DATA attribute list entry
+ * is not in the first 1024 bytes. FIXME: This should be implemented
+ * somehow! Perhaps by passing special error code up to
+ * ntfs_load_attributes() so it keeps going trying to get to $DATA
+ * regardless. Then it would have to restart just like we do here.
+ */
+ mft2 = ntfs_malloc(ino->vol->mft_record_size);
+ if (!mft2) {
+ ntfs_free(mft);
+ return -ENOMEM;
+ }
+ ntfs_memcpy(mft2, mft, ino->vol->mft_record_size);
+ while (len2 > 8) {
+ l = NTFS_GETU16(alist2 + 4);
+ if (l > len2)
+ break;
+ if (NTFS_GETU32(alist2 + 0x0) < ino->vol->at_data) {
+ len2 -= l;
+ alist2 += l;
+ continue;
+ }
+ if (NTFS_GETU32(alist2 + 0x0) > ino->vol->at_data) {
+ if (found2)
+ break;
+ /* Uh-oh! It really isn't there! */
+ ntfs_error("Either the $MFT is corrupt or, equally "
+ "likely, the $MFT is too complex for "
+ "the current driver to handle. Please "
+ "email the ntfs maintainer that you "
+ "saw this message. Thank you.\n");
+ goto failed_reading_mft_data;
+ }
+ /* Process attribute description. */
+ mftno2 = NTFS_GETU32(alist2 + 0x10);
+ if (mftno2 != ino->i_number && mftno2 != last_mft2) {
+ last_mft2 = mftno2;
+ error2 = ntfs_read_mft_record(ino->vol, mftno2, mft2);
+ if (error2) {
+ ntfs_debug(DEBUG_FILE3, "parse_attributes: "
+ "ntfs_read_mft_record(mftno2 = 0x%x) "
+ "failed\n", mftno2);
+ ntfs_free(mft2);
+ goto failed_reading_mft_data;
+ }
+ }
+ attr2 = ntfs_find_attr_in_mft_rec(
+ ino->vol, /* ntfs volume */
+ mftno2 == ino->i_number ?/* mft record is: */
+ ino->attr: /* base record */
+ mft2, /* extension record */
+ NTFS_GETU32(alist2 + 0), /* type */
+ (wchar_t*)(alist2 + alist2[7]), /* name */
+ alist2[6], /* name length */
+ 1, /* ignore case */
+ NTFS_GETU16(alist2 + 24) /* instance number */
+ );
+ if (!attr2) {
+ ntfs_error("parse_attributes: mft records 0x%x and/or "
+ "0x%x corrupt!\n", ino->i_number,
+ mftno2);
+ ntfs_free(mft2);
+ goto failed_reading_mft_data;
+ }
+ error2 = ntfs_insert_mft_attribute(ino, mftno2, attr2);
+ if (error2) {
+ ntfs_debug(DEBUG_FILE3, "parse_attributes: "
+ "ntfs_insert_mft_attribute(mftno2 0x%x, "
+ "attribute2 type 0x%x) failed\n", mftno2,
+ NTFS_GETU32(alist2 + 0));
+ ntfs_free(mft2);
+ goto failed_reading_mft_data;
+ }
+ len2 -= l;
+ alist2 += l;
+ found2 = 1;
+ }
+ ntfs_free(mft2);
+ tries = 1;
+ goto continue_after_loading_mft_data;
+}
+}
+
+static void ntfs_load_attributes(ntfs_inode *ino)
+{
+ ntfs_attribute *alist;
+ int datasize;
+ int offset, len, delta;
+ char *buf;
+ ntfs_volume *vol = ino->vol;
+
+ ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x 1\n", ino->i_number);
+ if (ntfs_insert_mft_attributes(ino, ino->attr, ino->i_number))
+ return;
+ ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x 2\n", ino->i_number);
+ alist = ntfs_find_attr(ino, vol->at_attribute_list, 0);
+ ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x 3\n", ino->i_number);
+ if (!alist)
+ return;
+ ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x 4\n", ino->i_number);
+ datasize = alist->size;
+ ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x: alist->size = 0x%x\n",
+ ino->i_number, alist->size);
+ if (alist->resident) {
+ parse_attributes(ino, alist->d.data, &datasize);
+ return;
+ }
+ ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x 5\n", ino->i_number);
+ buf = ntfs_malloc(1024);
+ if (!buf) /* FIXME: Should be passing error code to caller. (AIA) */
+ return;
+ delta = 0;
+ for (offset = 0; datasize; datasize -= len, offset += len) {
+ ntfs_io io;
+
+ io.fn_put = ntfs_put;
+ io.fn_get = 0;
+ io.param = buf + delta;
+ len = 1024 - delta;
+ if (len > datasize)
+ len = datasize;
+ ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x: len = %i\n",
+ ino->i_number, len);
+ ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x: delta = %i\n",
+ ino->i_number, delta);
+ io.size = len;
+ if (ntfs_read_attr(ino, vol->at_attribute_list, 0, offset,
+ &io))
+ ntfs_error("error in load_attributes\n");
+ delta += len;
+ ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x: after += len, "
+ "delta = %i\n", ino->i_number, delta);
+ parse_attributes(ino, buf, &delta);
+ ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x: after "
+ "parse_attr, delta = %i\n", ino->i_number,
+ delta);
+ if (delta)
+ /* Move remaining bytes to buffer start. */
+ ntfs_memmove(buf, buf + len - delta, delta);
+ }
+ ntfs_debug(DEBUG_FILE2, "load_attributes 0x%x 6\n", ino->i_number);
+ ntfs_free(buf);
+}
+
+int ntfs_init_inode(ntfs_inode *ino, ntfs_volume *vol, int inum)
+{
+ char *buf;
+ int error;
+
+ ntfs_debug(DEBUG_FILE1, "Initializing inode 0x%x\n", inum);
+ ino->i_number = inum;
+ ino->vol = vol;
+ ino->attr = buf = ntfs_malloc(vol->mft_record_size);
+ if (!buf)
+ return -ENOMEM;
+ error = ntfs_read_mft_record(vol, inum, ino->attr);
+ if (error) {
+ ntfs_debug(DEBUG_OTHER, "Init inode: 0x%x failed\n", inum);
+ return error;
+ }
+ ntfs_debug(DEBUG_FILE2, "Init inode: got mft 0x%x\n", inum);
+ ino->sequence_number = NTFS_GETU16(buf + 0x10);
+ ino->attr_count = 0;
+ ino->record_count = 0;
+ ino->records = 0;
+ ino->attrs = 0;
+ ntfs_load_attributes(ino);
+ ntfs_debug(DEBUG_FILE2, "Init inode: done 0x%x\n", inum);
+ return 0;
+}
+
+void ntfs_clear_inode(ntfs_inode *ino)
+{
+ int i;
+ if (!ino->attr) {
+ ntfs_error("ntfs_clear_inode: double free\n");
+ return;
+ }
+ ntfs_free(ino->attr);
+ ino->attr = 0;
+ ntfs_free(ino->records);
+ ino->records = 0;
+ for (i = 0; i < ino->attr_count; i++) {
+ if (ino->attrs[i].name)
+ ntfs_free(ino->attrs[i].name);
+ if (ino->attrs[i].resident) {
+ if (ino->attrs[i].d.data)
+ ntfs_free(ino->attrs[i].d.data);
+ } else {
+ if (ino->attrs[i].d.r.runlist)
+ ntfs_vfree(ino->attrs[i].d.r.runlist);
+ }
+ }
+ ntfs_free(ino->attrs);
+ ino->attrs = 0;
+}
+
+/* Check and fixup a MFT record. */
+int ntfs_check_mft_record(ntfs_volume *vol, char *record)
+{
+ return ntfs_fixup_record(record, "FILE", vol->mft_record_size);
+}
+
+/* Return (in result) the value indicating the next available attribute
+ * chunk number. Works for inodes w/o extension records only. */
+int ntfs_allocate_attr_number(ntfs_inode *ino, int *result)
+{
+ if (ino->record_count != 1)
+ return -EOPNOTSUPP;
+ *result = NTFS_GETU16(ino->attr + 0x28);
+ NTFS_PUTU16(ino->attr + 0x28, (*result) + 1);
+ return 0;
+}
+
+/* Find the location of an attribute in the inode. A name of NULL indicates
+ * unnamed attributes. Return pointer to attribute or NULL if not found. */
+char *ntfs_get_attr(ntfs_inode *ino, int attr, char *name)
+{
+ /* Location of first attribute. */
+ char *it = ino->attr + NTFS_GETU16(ino->attr + 0x14);
+ int type;
+ int len;
+
+ /* Only check for magic DWORD here, fixup should have happened before.*/
+ if (!IS_MFT_RECORD(ino->attr))
+ return 0;
+ do {
+ type = NTFS_GETU32(it);
+ len = NTFS_GETU16(it + 4);
+ /* We found the attribute type. Is the name correct, too? */
+ if (type == attr) {
+ int namelen = NTFS_GETU8(it + 9);
+ char *name_it, *n = name;
+ /* Match given name and attribute name if present.
+ Make sure attribute name is Unicode. */
+ if (!name) {
+ goto check_namelen;
+ } else if (namelen) {
+ for (name_it = it + NTFS_GETU16(it + 10);
+ namelen; n++, name_it += 2, namelen--)
+ if (*name_it != *n || name_it[1])
+ break;
+check_namelen:
+ if (!namelen)
+ break;
+ }
+ }
+ it += len;
+ } while (type != -1); /* List of attributes ends with type -1. */
+ if (type == -1)
+ return 0;
+ return it;
+}
+
+__s64 ntfs_get_attr_size(ntfs_inode *ino, int type, char *name)
+{
+ ntfs_attribute *attr = ntfs_find_attr(ino, type, name);
+ if (!attr)
+ return 0;
+ return
+ attr->size;
+}
+
+int ntfs_attr_is_resident(ntfs_inode *ino, int type, char *name)
+{
+ ntfs_attribute *attr = ntfs_find_attr(ino, type, name);
+ if (!attr)
+ return 0;
+ return attr->resident;
+}
+
+/*
+ * A run is coded as a type indicator, an unsigned length, and a signed cluster
+ * offset.
+ * . To save space, length and offset are fields of variable length. The low
+ * nibble of the type indicates the width of the length :), the high nibble
+ * the width of the offset.
+ * . The first offset is relative to cluster 0, later offsets are relative to
+ * the previous cluster.
+ *
+ * This function decodes a run. Length is an output parameter, data and cluster
+ * are in/out parameters.
+ */
+int ntfs_decompress_run(unsigned char **data, int *length,
+ ntfs_cluster_t *cluster, int *ctype)
+{
+ unsigned char type = *(*data)++;
+ *ctype = 0;
+ switch (type & 0xF) {
+ case 1:
+ *length = NTFS_GETS8(*data);
+ break;
+ case 2:
+ *length = NTFS_GETS16(*data);
+ break;
+ case 3:
+ *length = NTFS_GETS24(*data);
+ break;
+ case 4:
+ *length = NTFS_GETS32(*data);
+ break;
+ /* Note: cases 5-8 are probably pointless to code, since how
+ * many runs > 4GB of length are there? At the most, cases 5
+ * and 6 are probably necessary, and would also require making
+ * length 64-bit throughout. */
+ default:
+ ntfs_error("Can't decode run type field 0x%x\n", type);
+ return -1;
+ }
+// ntfs_debug(DEBUG_FILE3, "ntfs_decompress_run: length = 0x%x\n",*length);
+ if (*length < 0)
+ {
+ ntfs_error("Negative run length decoded\n");
+ return -1;
+ }
+ *data += (type & 0xF);
+ switch (type & 0xF0) {
+ case 0:
+ *ctype = 2;
+ break;
+ case 0x10:
+ *cluster += NTFS_GETS8(*data);
+ break;
+ case 0x20:
+ *cluster += NTFS_GETS16(*data);
+ break;
+ case 0x30:
+ *cluster += NTFS_GETS24(*data);
+ break;
+ case 0x40:
+ *cluster += NTFS_GETS32(*data);
+ break;
+#if 0 /* Keep for future, in case ntfs_cluster_t ever becomes 64bit. */
+ case 0x50:
+ *cluster += NTFS_GETS40(*data);
+ break;
+ case 0x60:
+ *cluster += NTFS_GETS48(*data);
+ break;
+ case 0x70:
+ *cluster += NTFS_GETS56(*data);
+ break;
+ case 0x80:
+ *cluster += NTFS_GETS64(*data);
+ break;
+#endif
+ default:
+ ntfs_error("Can't decode run type field 0x%x\n", type);
+ return -1;
+ }
+// ntfs_debug(DEBUG_FILE3, "ntfs_decompress_run: cluster = 0x%x\n",
+// *cluster);
+ *data += (type >> 4);
+ return 0;
+}
+
+static void dump_runlist(const ntfs_runlist *rl, const int rlen);
+
+/*
+ * FIXME: ntfs_readwrite_attr() has the effect of writing @dest to @offset of
+ * the attribute value of the attribute @attr in the in memory inode @ino.
+ * If the attribute value of @attr is non-resident the value's contents at
+ * @offset are actually written to disk (from @dest). The on disk mft record
+ * describing the non-resident attribute value is not updated!
+ * If the attribute value is resident then the value is written only in
+ * memory. The on disk mft record containing the value is not written to disk.
+ * A possible fix would be to call ntfs_update_inode() before returning. (AIA)
+ */
+/* Reads l bytes of the attribute (attr, name) of ino starting at offset on
+ * vol into buf. Returns the number of bytes read in the ntfs_io struct.
+ * Returns 0 on success, errno on failure */
+int ntfs_readwrite_attr(ntfs_inode *ino, ntfs_attribute *attr, __s64 offset,
+ ntfs_io *dest)
+{
+ int rnum, s_vcn, error, clustersizebits;
+ ntfs_cluster_t cluster, s_cluster, vcn, len;
+ __s64 l, chunk, copied;
+
+ ntfs_debug(DEBUG_FILE3, "%s(): %s 0x%x bytes at offset "
+ "0x%Lx %s inode 0x%x, attr type 0x%x.\n", __FUNCTION__,
+ dest->do_read ? "Read" : "Write", dest->size, offset,
+ dest->do_read ? "from" : "to", ino->i_number,
+ attr->type);
+ l = dest->size;
+ if (l == 0)
+ return 0;
+ if (dest->do_read) {
+ /* If read _starts_ beyond end of stream, return nothing. */
+ if (offset >= attr->size) {
+ dest->size = 0;
+ return 0;
+ }
+ /* If read _extends_ beyond end of stream, return as much
+ * initialised data as we have. */
+ if (offset + l >= attr->size)
+ l = dest->size = attr->size - offset;
+ } else {
+ /*
+ * If write extends beyond _allocated_ size, extend attribute,
+ * updating attr->allocated and attr->size in the process. (AIA)
+ */
+ if ((!attr->resident && offset + l > attr->allocated) ||
+ (attr->resident && offset + l > attr->size)) {
+ error = ntfs_resize_attr(ino, attr, offset + l);
+ if (error)
+ return error;
+ }
+ if (!attr->resident) {
+ /* Has amount of data increased? */
+ if (offset + l > attr->size)
+ attr->size = offset + l;
+ /* Has amount of initialised data increased? */
+ if (offset + l > attr->initialized) {
+ /* FIXME: Clear the section between the old
+ * initialised length and the write start.
+ * (AIA) */
+ attr->initialized = offset + l;
+ }
+ }
+ }
+ if (attr->resident) {
+ if (dest->do_read)
+ dest->fn_put(dest, (ntfs_u8*)attr->d.data + offset, l);
+ else
+ dest->fn_get((ntfs_u8*)attr->d.data + offset, dest, l);
+ dest->size = l;
+ return 0;
+ }
+ if (dest->do_read) {
+ /* Read uninitialized data. */
+ if (offset >= attr->initialized)
+ return ntfs_read_zero(dest, l);
+ if (offset + l > attr->initialized) {
+ dest->size = chunk = attr->initialized - offset;
+ error = ntfs_readwrite_attr(ino, attr, offset, dest);
+ if (error || (dest->size != chunk && (error = -EIO, 1)))
+ return error;
+ dest->size += l - chunk;
+ return ntfs_read_zero(dest, l - chunk);
+ }
+ if (attr->flags & ATTR_IS_COMPRESSED)
+ return ntfs_read_compressed(ino, attr, offset, dest);
+ } else {
+ if (attr->flags & ATTR_IS_COMPRESSED)
+ return ntfs_write_compressed(ino, attr, offset, dest);
+ }
+ vcn = 0;
+ clustersizebits = ino->vol->cluster_size_bits;
+ s_vcn = offset >> clustersizebits;
+ for (rnum = 0; rnum < attr->d.r.len &&
+ vcn + attr->d.r.runlist[rnum].len <= s_vcn; rnum++)
+ vcn += attr->d.r.runlist[rnum].len;
+ if (rnum == attr->d.r.len) {
+ ntfs_debug(DEBUG_FILE3, "%s(): EOPNOTSUPP: "
+ "inode = 0x%x, rnum = %i, offset = 0x%Lx, vcn = 0x%x, "
+ "s_vcn = 0x%x.\n", __FUNCTION__, ino->i_number, rnum,
+ offset, vcn, s_vcn);
+ dump_runlist(attr->d.r.runlist, attr->d.r.len);
+ /*FIXME: Should extend runlist. */
+ return -EOPNOTSUPP;
+ }
+ copied = 0;
+ while (l) {
+ s_vcn = offset >> clustersizebits;
+ cluster = attr->d.r.runlist[rnum].lcn;
+ len = attr->d.r.runlist[rnum].len;
+ s_cluster = cluster + s_vcn - vcn;
+ chunk = ((__s64)(vcn + len) << clustersizebits) - offset;
+ if (chunk > l)
+ chunk = l;
+ dest->size = chunk;
+ error = ntfs_getput_clusters(ino->vol, s_cluster, offset -
+ ((__s64)s_vcn << clustersizebits), dest);
+ if (error) {
+ ntfs_error("Read/write error.\n");
+ dest->size = copied;
+ return error;
+ }
+ l -= chunk;
+ copied += chunk;
+ offset += chunk;
+ if (l && offset >= ((__s64)(vcn + len) << clustersizebits)) {
+ rnum++;
+ vcn += len;
+ cluster = attr->d.r.runlist[rnum].lcn;
+ len = attr->d.r.runlist[rnum].len;
+ }
+ }
+ dest->size = copied;
+ return 0;
+}
+
+int ntfs_read_attr(ntfs_inode *ino, int type, char *name, __s64 offset,
+ ntfs_io *buf)
+{
+ ntfs_attribute *attr;
+
+ buf->do_read = 1;
+ attr = ntfs_find_attr(ino, type, name);
+ if (!attr) {
+ ntfs_debug(DEBUG_FILE3, "%s(): attr 0x%x not found in inode "
+ "0x%x\n", __FUNCTION__, type, ino->i_number);
+ return -EINVAL;
+ }
+ return ntfs_readwrite_attr(ino, attr, offset, buf);
+}
+
+int ntfs_write_attr(ntfs_inode *ino, int type, char *name, __s64 offset,
+ ntfs_io *buf)
+{
+ ntfs_attribute *attr;
+
+ buf->do_read = 0;
+ attr = ntfs_find_attr(ino, type, name);
+ if (!attr) {
+ ntfs_debug(DEBUG_FILE3, "%s(): attr 0x%x not found in inode "
+ "0x%x\n", __FUNCTION__, type, ino->i_number);
+ return -EINVAL;
+ }
+ return ntfs_readwrite_attr(ino, attr, offset, buf);
+}
+
+/* -2 = error, -1 = hole, >= 0 means real disk cluster (lcn). */
+int ntfs_vcn_to_lcn(ntfs_inode *ino, int vcn)
+{
+ int rnum;
+ ntfs_attribute *data;
+
+ data = ntfs_find_attr(ino, ino->vol->at_data, 0);
+ if (!data || data->resident || data->flags & (ATTR_IS_COMPRESSED |
+ ATTR_IS_ENCRYPTED))
+ return -2;
+ if (data->size <= (__s64)vcn << ino->vol->cluster_size_bits)
+ return -2;
+ if (data->initialized <= (__s64)vcn << ino->vol->cluster_size_bits)
+ return -1;
+ for (rnum = 0; rnum < data->d.r.len &&
+ vcn >= data->d.r.runlist[rnum].len; rnum++)
+ vcn -= data->d.r.runlist[rnum].len;
+ if (data->d.r.runlist[rnum].lcn >= 0)
+ return data->d.r.runlist[rnum].lcn + vcn;
+ return data->d.r.runlist[rnum].lcn + vcn;
+}
+
+static int allocate_store(ntfs_volume *vol, ntfs_disk_inode *store, int count)
+{
+ int i;
+
+ if (store->count > count)
+ return 0;
+ if (store->size < count) {
+ ntfs_mft_record *n = ntfs_malloc((count + 4) *
+ sizeof(ntfs_mft_record));
+ if (!n)
+ return -ENOMEM;
+ if (store->size) {
+ for (i = 0; i < store->size; i++)
+ n[i] = store->records[i];
+ ntfs_free(store->records);
+ }
+ store->size = count + 4;
+ store->records = n;
+ }
+ for (i = store->count; i < count; i++) {
+ store->records[i].record = ntfs_malloc(vol->mft_record_size);
+ if (!store->records[i].record)
+ return -ENOMEM;
+ store->count++;
+ }
+ return 0;
+}
+
+static void deallocate_store(ntfs_disk_inode* store)
+{
+ int i;
+
+ for (i = 0; i < store->count; i++)
+ ntfs_free(store->records[i].record);
+ ntfs_free(store->records);
+ store->count = store->size = 0;
+ store->records = 0;
+}
+
+/**
+ * layout_runs - compress runlist into mapping pairs array
+ * @attr: attribute containing the runlist to compress
+ * @rec: destination buffer to hold the mapping pairs array
+ * @offs: current position in @rec (in/out variable)
+ * @size: size of the buffer @rec
+ *
+ * layout_runs walks the runlist in @attr, compresses it and writes it out the
+ * resulting mapping pairs array into @rec (up to a maximum of @size bytes are
+ * written). On entry @offs is the offset in @rec at which to begin writing the
+ * mapping pairs array. On exit, it contains the offset in @rec of the first
+ * byte after the end of the mapping pairs array.
+ */
+static int layout_runs(ntfs_attribute *attr, char *rec, int *offs, int size)
+{
+ int i, len, offset, coffs;
+ /* ntfs_cluster_t MUST be signed! (AIA) */
+ ntfs_cluster_t cluster, rclus;
+ ntfs_runlist *rl = attr->d.r.runlist;
+ cluster = 0;
+ offset = *offs;
+ for (i = 0; i < attr->d.r.len; i++) {
+ /*
+ * We cheat with this check on the basis that lcn will never
+ * be less than -1 and the lcn delta will fit in signed
+ * 32-bits (ntfs_cluster_t). (AIA)
+ */
+ if (rl[i].lcn < (ntfs_cluster_t)-1) {
+ ntfs_error("layout_runs() encountered an out of bounds "
+ "cluster delta, lcn = %i.\n",
+ rl[i].lcn);
+ return -ERANGE;
+ }
+ rclus = rl[i].lcn - cluster;
+ len = rl[i].len;
+ rec[offset] = 0;
+ if (offset + 9 > size)
+ return -E2BIG; /* It might still fit, but this
+ * simplifies testing. */
+ /*
+ * Run length is stored as signed number, so deal with it
+ * properly, i.e. observe that a negative number will have all
+ * its most significant bits set to 1 but we don't store that
+ * in the mapping pairs array. We store the smallest type of
+ * negative number required, thus in the first if we check
+ * whether len fits inside a signed byte and if so we store it
+ * as such, the next ifs check for a signed short, then a signed
+ * 24-bit and finally the full blown signed 32-bit. Same goes
+ * for rlus below. (AIA)
+ */
+ if (len >= -0x80 && len <= 0x7f) {
+ NTFS_PUTU8(rec + offset + 1, len & 0xff);
+ coffs = 1;
+ } else if (len >= -0x8000 && len <= 0x7fff) {
+ NTFS_PUTU16(rec + offset + 1, len & 0xffff);
+ coffs = 2;
+ } else if (len >= -0x800000 && len <= 0x7fffff) {
+ NTFS_PUTU24(rec + offset + 1, len & 0xffffff);
+ coffs = 3;
+ } else /* if (len >= -0x80000000LL && len <= 0x7fffffff */ {
+ NTFS_PUTU32(rec + offset + 1, len);
+ coffs = 4;
+ } /* else ... FIXME: When len becomes 64-bit we need to extend
+ * the else if () statements. (AIA) */
+ *(rec + offset) |= coffs++;
+ if (rl[i].lcn == (ntfs_cluster_t)-1) /* Compressed run. */
+ /* Nothing */;
+ else if (rclus >= -0x80 && rclus <= 0x7f) {
+ *(rec + offset) |= 0x10;
+ NTFS_PUTS8(rec + offset + coffs, rclus & 0xff);
+ coffs += 1;
+ } else if (rclus >= -0x8000 && rclus <= 0x7fff) {
+ *(rec + offset) |= 0x20;
+ NTFS_PUTS16(rec + offset + coffs, rclus & 0xffff);
+ coffs += 2;
+ } else if (rclus >= -0x800000 && rclus <= 0x7fffff) {
+ *(rec + offset) |= 0x30;
+ NTFS_PUTS24(rec + offset + coffs, rclus & 0xffffff);
+ coffs += 3;
+ } else /* if (rclus >= -0x80000000LL && rclus <= 0x7fffffff)*/ {
+ *(rec + offset) |= 0x40;
+ NTFS_PUTS32(rec + offset + coffs, rclus
+ /* & 0xffffffffLL */);
+ coffs += 4;
+ } /* FIXME: When rclus becomes 64-bit.
+ else if (rclus >= -0x8000000000 && rclus <= 0x7FFFFFFFFF) {
+ *(rec + offset) |= 0x50;
+ NTFS_PUTS40(rec + offset + coffs, rclus &
+ 0xffffffffffLL);
+ coffs += 5;
+ } else if (rclus >= -0x800000000000 &&
+ rclus <= 0x7FFFFFFFFFFF) {
+ *(rec + offset) |= 0x60;
+ NTFS_PUTS48(rec + offset + coffs, rclus &
+ 0xffffffffffffLL);
+ coffs += 6;
+ } else if (rclus >= -0x80000000000000 &&
+ rclus <= 0x7FFFFFFFFFFFFF) {
+ *(rec + offset) |= 0x70;
+ NTFS_PUTS56(rec + offset + coffs, rclus &
+ 0xffffffffffffffLL);
+ coffs += 7;
+ } else {
+ *(rec + offset) |= 0x80;
+ NTFS_PUTS64(rec + offset + coffs, rclus);
+ coffs += 8;
+ } */
+ offset += coffs;
+ if (rl[i].lcn)
+ cluster = rl[i].lcn;
+ }
+ if (offset >= size)
+ return -E2BIG;
+ /* Terminating null. */
+ *(rec + offset++) = 0;
+ *offs = offset;
+ return 0;
+}
+
+static void count_runs(ntfs_attribute *attr, char *buf)
+{
+ ntfs_u32 first, count, last, i;
+
+ first = 0;
+ for (i = 0, count = 0; i < attr->d.r.len; i++)
+ count += attr->d.r.runlist[i].len;
+ last = first + count - 1;
+ NTFS_PUTU64(buf + 0x10, first);
+ NTFS_PUTU64(buf + 0x18, last);
+}
+
+/**
+ * layout_attr - convert in memory attribute to on disk attribute record
+ * @attr: in memory attribute to convert
+ * @buf: destination buffer for on disk attribute record
+ * @size: size of the destination buffer
+ * @psize: size of converted on disk attribute record (out variable)
+ *
+ * layout_attr() takes the attribute @attr and converts it into the appropriate
+ * on disk structure, writing it into @buf (up to @size bytes are written).
+ *
+ * On success we return 0 and set @*psize to the actual byte size of the on-
+ * disk attribute that was written into @buf.
+ */
+static int layout_attr(ntfs_attribute *attr, char *buf, int size, int *psize)
+{
+ int nameoff, hdrsize, asize;
+
+ if (attr->resident) {
+ nameoff = 0x18;
+ hdrsize = (nameoff + 2 * attr->namelen + 7) & ~7;
+ asize = (hdrsize + attr->size + 7) & ~7;
+ if (size < asize)
+ return -E2BIG;
+ NTFS_PUTU32(buf + 0x10, attr->size);
+ NTFS_PUTU8(buf + 0x16, attr->indexed);
+ NTFS_PUTU16(buf + 0x14, hdrsize);
+ if (attr->size)
+ ntfs_memcpy(buf + hdrsize, attr->d.data, attr->size);
+ } else {
+ int error;
+
+ if (attr->flags & ATTR_IS_COMPRESSED)
+ nameoff = 0x48;
+ else
+ nameoff = 0x40;
+ hdrsize = (nameoff + 2 * attr->namelen + 7) & ~7;
+ if (size < hdrsize)
+ return -E2BIG;
+ /* Make asize point at the end of the attribute record header,
+ i.e. at the beginning of the mapping pairs array. */
+ asize = hdrsize;
+ error = layout_runs(attr, buf, &asize, size);
+ /* Now, asize points one byte beyond the end of the mapping
+ pairs array. */
+ if (error)
+ return error;
+ /* The next attribute has to begin on 8-byte boundary. */
+ asize = (asize + 7) & ~7;
+ /* FIXME: fragments */
+ count_runs(attr, buf);
+ NTFS_PUTU16(buf + 0x20, hdrsize);
+ NTFS_PUTU16(buf + 0x22, attr->cengine);
+ NTFS_PUTU32(buf + 0x24, 0);
+ NTFS_PUTS64(buf + 0x28, attr->allocated);
+ NTFS_PUTS64(buf + 0x30, attr->size);
+ NTFS_PUTS64(buf + 0x38, attr->initialized);
+ if (attr->flags & ATTR_IS_COMPRESSED)
+ NTFS_PUTS64(buf + 0x40, attr->compsize);
+ }
+ NTFS_PUTU32(buf, attr->type);
+ NTFS_PUTU32(buf + 4, asize);
+ NTFS_PUTU8(buf + 8, attr->resident ? 0 : 1);
+ NTFS_PUTU8(buf + 9, attr->namelen);
+ NTFS_PUTU16(buf + 0xa, nameoff);
+ NTFS_PUTU16(buf + 0xc, attr->flags);
+ NTFS_PUTU16(buf + 0xe, attr->attrno);
+ if (attr->namelen)
+ ntfs_memcpy(buf + nameoff, attr->name, 2 * attr->namelen);
+ *psize = asize;
+ return 0;
+}
+
+/**
+ * layout_inode - convert an in-memory inode into on disk mft record(s)
+ * @ino: in memory inode to convert
+ * @store: on disk inode, contain buffers for the on disk mft record(s)
+ *
+ * layout_inode takes the in memory inode @ino, converts it into a (sequence of)
+ * mft record(s) and writes them to the appropriate buffers in the @store.
+ *
+ * Return 0 on success,
+ * the required mft record count (>0) if the inode does not fit,
+ * -ENOMEM if memory allocation problem, or
+ * -EOPNOTSUP if beyond our capabilities.
+ *
+ * TODO: We at the moment do not support extension mft records. (AIA)
+ */
+int layout_inode(ntfs_inode *ino, ntfs_disk_inode *store)
+{
+ int offset, i, size, psize, error, count, recno;
+ ntfs_attribute *attr;
+ unsigned char *rec;
+
+ error = allocate_store(ino->vol, store, ino->record_count);
+ if (error)
+ return error;
+ size = ino->vol->mft_record_size;
+ count = i = 0;
+ do {
+ if (count < ino->record_count) {
+ recno = ino->records[count];
+ } else {
+ error = allocate_store(ino->vol, store, count + 1);
+ if (error)
+ return error;
+ recno = -1;
+ }
+ /*
+ * FIXME: We need to support extension records properly.
+ * At the moment they wouldn't work. Probably would "just" get
+ * corrupted if we write to them... (AIA)
+ */
+ store->records[count].recno = recno;
+ rec = store->records[count].record;
+ count++;
+ /* Copy mft record header. */
+ offset = NTFS_GETU16(ino->attr + 0x14); /* attrs_offset */
+ ntfs_memcpy(rec, ino->attr, offset);
+ /* Copy attributes. */
+ while (i < ino->attr_count) {
+ attr = ino->attrs + i;
+ error = layout_attr(attr, rec + offset,
+ size - offset - 8, &psize);
+ if (error == -E2BIG && offset != NTFS_GETU16(ino->attr
+ + 0x14))
+ break;
+ if (error)
+ return error;
+ offset += psize;
+ i++;
+ }
+ /* Terminating attribute. */
+ NTFS_PUTU32(rec + offset, 0xFFFFFFFF);
+ offset += 4;
+ NTFS_PUTU32(rec + offset, 0);
+ offset += 4;
+ NTFS_PUTU32(rec + 0x18, offset);
+ } while (i < ino->attr_count || count < ino->record_count);
+ return count - ino->record_count;
+}
+
+/*
+ * FIXME: ntfs_update_inode() calls layout_inode() to create the mft record on
+ * disk structure corresponding to the inode @ino. After that, ntfs_write_attr()
+ * is called to write out the created mft record to disk.
+ * We shouldn't need to re-layout every single time we are updating an mft
+ * record. No wonder the ntfs driver is slow like hell. (AIA)
+ */
+int ntfs_update_inode(ntfs_inode *ino)
+{
+ int error, i;
+ ntfs_disk_inode store;
+ ntfs_io io;
+
+ ntfs_bzero(&store, sizeof(store));
+ error = layout_inode(ino, &store);
+ if (error == -E2BIG) {
+ i = ntfs_split_indexroot(ino);
+ if (i != -ENOTDIR) {
+ if (!i)
+ i = layout_inode(ino, &store);
+ error = i;
+ }
+ }
+ if (error == -E2BIG) {
+ error = ntfs_attr_allnonresident(ino);
+ if (!error)
+ error = layout_inode(ino, &store);
+ }
+ if (error > 0) {
+ /* FIXME: Introduce extension records. */
+ error = -E2BIG;
+ }
+ if (error) {
+ if (error == -E2BIG)
+ ntfs_error("Cannot handle saving inode 0x%x.\n",
+ ino->i_number);
+ deallocate_store(&store);
+ return error;
+ }
+ io.fn_get = ntfs_get;
+ io.fn_put = 0;
+ for (i = 0; i < store.count; i++) {
+ error = ntfs_insert_fixups(store.records[i].record,
+ ino->vol->mft_record_size);
+ if (error) {
+ printk(KERN_ALERT "NTFS: ntfs_update_inode() caught "
+ "corrupt %s mtf record ntfs record "
+ "header. Refusing to write corrupt "
+ "data to disk. Unmount and run chkdsk "
+ "immediately!\n", i ? "extension":
+ "base");
+ deallocate_store(&store);
+ return -EIO;
+ }
+ io.param = store.records[i].record;
+ io.size = ino->vol->mft_record_size;
+ error = ntfs_write_attr(ino->vol->mft_ino, ino->vol->at_data,
+ 0, (__s64)store.records[i].recno <<
+ ino->vol->mft_record_size_bits, &io);
+ if (error || io.size != ino->vol->mft_record_size) {
+ /* Big trouble, partially written file. */
+ ntfs_error("Please unmount: Write error in inode "
+ "0x%x\n", ino->i_number);
+ deallocate_store(&store);
+ return error ? error : -EIO;
+ }
+ }
+ deallocate_store(&store);
+ return 0;
+}
+
+void ntfs_decompress(unsigned char *dest, unsigned char *src, ntfs_size_t l)
+{
+ int head, comp;
+ int copied = 0;
+ unsigned char *stop;
+ int bits;
+ int tag = 0;
+ int clear_pos;
+
+ while (1) {
+ head = NTFS_GETU16(src) & 0xFFF;
+ /* High bit indicates that compression was performed. */
+ comp = NTFS_GETU16(src) & 0x8000;
+ src += 2;
+ stop = src + head;
+ bits = 0;
+ clear_pos = 0;
+ if (head == 0)
+ /* Block is not used. */
+ return;/* FIXME: copied */
+ if (!comp) { /* uncompressible */
+ ntfs_memcpy(dest, src, 0x1000);
+ dest += 0x1000;
+ copied += 0x1000;
+ src += 0x1000;
+ if (l == copied)
+ return;
+ continue;
+ }
+ while (src <= stop) {
+ if (clear_pos > 4096) {
+ ntfs_error("Error 1 in decompress\n");
+ return;
+ }
+ if (!bits) {
+ tag = NTFS_GETU8(src);
+ bits = 8;
+ src++;
+ if (src > stop)
+ break;
+ }
+ if (tag & 1) {
+ int i, len, delta, code, lmask, dshift;
+ code = NTFS_GETU16(src);
+ src += 2;
+ if (!clear_pos) {
+ ntfs_error("Error 2 in decompress\n");
+ return;
+ }
+ for (i = clear_pos - 1, lmask = 0xFFF,
+ dshift = 12; i >= 0x10; i >>= 1) {
+ lmask >>= 1;
+ dshift--;
+ }
+ delta = code >> dshift;
+ len = (code & lmask) + 3;
+ for (i = 0; i < len; i++) {
+ dest[clear_pos] = dest[clear_pos -
+ delta - 1];
+ clear_pos++;
+ copied++;
+ if (copied==l)
+ return;
+ }
+ } else {
+ dest[clear_pos++] = NTFS_GETU8(src);
+ src++;
+ copied++;
+ if (copied==l)
+ return;
+ }
+ tag >>= 1;
+ bits--;
+ }
+ dest += clear_pos;
+ }
+}
+
+/*
+ * NOTE: Neither of the ntfs_*_bit functions are atomic! But we don't need
+ * them atomic at present as we never operate on shared/cached bitmaps.
+ */
+static __inline__ int ntfs_test_bit(unsigned char *byte, const int bit)
+{
+ return byte[bit >> 3] & (1 << (bit & 7)) ? 1 : 0;
+}
+
+static __inline__ void ntfs_set_bit(unsigned char *byte, const int bit)
+{
+ byte[bit >> 3] |= 1 << (bit & 7);
+}
+
+static __inline__ void ntfs_clear_bit(unsigned char *byte, const int bit)
+{
+ byte[bit >> 3] &= ~(1 << (bit & 7));
+}
+
+static __inline__ int ntfs_test_and_clear_bit(unsigned char *byte,
+ const int bit)
+{
+ unsigned char *ptr = byte + (bit >> 3);
+ int b = 1 << (bit & 7);
+ int oldbit = *ptr & b ? 1 : 0;
+ *ptr &= ~b;
+ return oldbit;
+}
+
+static void dump_runlist(const ntfs_runlist *rl, const int rlen)
+{
+#ifdef DEBUG
+ int i;
+ ntfs_cluster_t ct;
+
+ ntfs_debug(DEBUG_OTHER, "%s(): rlen = %i.\n", __FUNCTION__, rlen);
+ ntfs_debug(DEBUG_OTHER, "VCN LCN Run length\n");
+ for (i = 0, ct = 0; i < rlen; ct += rl[i++].len) {
+ if (rl[i].lcn == (ntfs_cluster_t)-1)
+ ntfs_debug(DEBUG_OTHER, "0x%-8x LCN_HOLE 0x%-8x "
+ "(%s)\n", ct, rl[i].len, rl[i].len ?
+ "sparse run" : "run list end");
+ else
+ ntfs_debug(DEBUG_OTHER, "0x%-8x 0x%-8x 0x%-8x%s\n", ct,
+ rl[i].lcn, rl[i].len, rl[i].len &&
+ i + 1 < rlen ? "" : " (run list end)");
+ if (!rl[i].len)
+ break;
+ }
+#endif
+}
+
+/**
+ * splice_runlists - splice two run lists into one
+ * @rl1: pointer to address of first run list
+ * @r1len: number of elementfs in first run list
+ * @rl2: pointer to second run list
+ * @r2len: number of elements in second run list
+ *
+ * Append the run list @rl2 to the run list *@rl1 and return the result in
+ * *@rl1 and *@r1len.
+ *
+ * Return 0 on success or -errno on error, in which case *@rl1 and *@r1len are
+ * left untouched.
+ *
+ * The only possible error code at the moment is -ENOMEM and only happens if
+ * there is insufficient memory to allocate the new run list (only happens
+ * when size of (rl1 + rl2) > allocated size of rl1).
+ */
+int splice_runlists(ntfs_runlist **rl1, int *r1len, const ntfs_runlist *rl2,
+ int r2len)
+{
+ ntfs_runlist *rl;
+ int rlen, rl_size, rl2_pos;
+
+ ntfs_debug(DEBUG_OTHER, "%s(): Entering with *r1len = %i, "
+ "r2len = %i.\n", __FUNCTION__, *r1len, r2len);
+ ntfs_debug(DEBUG_OTHER, "%s(): Dumping 1st runlist.\n", __FUNCTION__);
+ if (*rl1)
+ dump_runlist(*rl1, *r1len);
+ else
+ ntfs_debug(DEBUG_OTHER, "%s(): Not present.\n", __FUNCTION__);
+ ntfs_debug(DEBUG_OTHER, "%s(): Dumping 2nd runlist.\n", __FUNCTION__);
+ dump_runlist(rl2, r2len);
+ rlen = *r1len + r2len + 1;
+ rl_size = (rlen * sizeof(ntfs_runlist) + PAGE_SIZE - 1) &
+ PAGE_MASK;
+ ntfs_debug(DEBUG_OTHER, "%s(): rlen = %i, rl_size = %i.\n",
+ __FUNCTION__, rlen, rl_size);
+ /* Do we have enough space? */
+ if (rl_size <= ((*r1len * sizeof(ntfs_runlist) + PAGE_SIZE - 1) &
+ PAGE_MASK)) {
+ /* Have enough space already. */
+ rl = *rl1;
+ ntfs_debug(DEBUG_OTHER, "%s(): Have enough space already.\n",
+ __FUNCTION__);
+ } else {
+ /* Need more space. Reallocate. */
+ ntfs_debug(DEBUG_OTHER, "%s(): Need more space.\n",
+ __FUNCTION__);
+ rl = ntfs_vmalloc(rlen << sizeof(ntfs_runlist));
+ if (!rl)
+ return -ENOMEM;
+ /* Copy over rl1. */
+ ntfs_memcpy(rl, *rl1, *r1len * sizeof(ntfs_runlist));
+ ntfs_vfree(*rl1);
+ *rl1 = rl;
+ }
+ /* Reuse rl_size as the current position index into rl. */
+ rl_size = *r1len - 1;
+ ntfs_debug(DEBUG_OTHER, "%s(): rl_size = %i.\n", __FUNCTION__,rl_size);
+ /* Coalesce neighbouring elements, if present. */
+ rl2_pos = 0;
+ if (rl[rl_size].lcn + rl[rl_size].len == rl2[rl2_pos].lcn) {
+ ntfs_debug(DEBUG_OTHER, "%s(): Coalescing adjacent runs.\n",
+ __FUNCTION__);
+ ntfs_debug(DEBUG_OTHER, "%s(): Before: rl[rl_size].len = %i.\n",
+ __FUNCTION__, rl[rl_size].len);
+ rl[rl_size].len += rl2[rl2_pos].len;
+ ntfs_debug(DEBUG_OTHER, "%s(): After: rl[rl_size].len = %i.\n",
+ __FUNCTION__, rl[rl_size].len);
+ rl2_pos++;
+ r2len--;
+ rlen--;
+ }
+ rl_size++;
+ /* Copy over rl2. */
+ ntfs_memcpy(rl + rl_size, rl2 + rl2_pos, r2len * sizeof(ntfs_runlist));
+ rlen--;
+ rl[rlen].lcn = (ntfs_cluster_t)-1;
+ rl[rlen].len = (ntfs_cluster_t)0;
+ *r1len = rlen;
+ ntfs_debug(DEBUG_OTHER, "%s(): Dumping result runlist.\n",
+ __FUNCTION__);
+ dump_runlist(*rl1, *r1len);
+ ntfs_debug(DEBUG_OTHER, "%s(): Returning with *r1len = %i.\n",
+ __FUNCTION__, rlen);
+ return 0;
+}
+
+/**
+ * ntfs_alloc_mft_record - allocate an mft record
+ * @vol: volume to allocate an mft record on
+ * @result: the mft record number allocated
+ *
+ * Allocate a new mft record on disk. Return 0 on success or -ERRNO on error.
+ * On success, *@result contains the allocated mft record number. On error,
+ * *@result is -1UL.
+ *
+ * Note, this function doesn't actually set the mft record to be in use. This
+ * is done by the caller, which at the moment is only ntfs_alloc_inode().
+ *
+ * To find a free mft record, we scan the mft bitmap for a zero bit. To
+ * optimize this we start scanning at the place where we last stopped and we
+ * perform wrap around when we reach the end. Note, we do not try to allocate
+ * mft records below number 24 because numbers 0 to 15 are the defined system
+ * files anyway and 16 to 24 are special in that they are used for storing
+ * extension mft records for $MFT's $DATA attribute. This is required to avoid
+ * the possibility of creating a run list with a circular dependence which once
+ * written to disk can never be read in again. Windows will only use records
+ * 16 to 24 for normal files if the volume is completely out of space. We never
+ * use them which means that when the volume is really out of space we cannot
+ * create any more files while Windows can still create up to 8 small files. We
+ * can start doing this at some later time, doesn't matter much for now.
+ *
+ * When scanning the mft bitmap, we only search up to the last allocated mft
+ * record. If there are no free records left in the range 24 to number of
+ * allocated mft records, then we extend the mft data in order to create free
+ * mft records. We extend the allocated size of $MFT/$DATA by 16 records at a
+ * time or one cluster, if cluster size is above 16kiB. If there isn't
+ * sufficient space to do this, we try to extend by a single mft record or one
+ * cluster, if cluster size is above mft record size, but we only do this if
+ * there is enough free space, which we know from the values returned by the
+ * failed cluster allocation function when we tried to do the first allocation.
+ *
+ * No matter how many mft records we allocate, we initialize only the first
+ * allocated mft record (incrementing mft data size and initialized size) and
+ * return its number to the caller in @*result, unless there are less than 24
+ * mft records, in which case we allocate and initialize mft records until we
+ * reach record 24 which we consider as the first free mft record for use by
+ * normal files.
+ *
+ * If during any stage we overflow the initialized data in the mft bitmap, we
+ * extend the initialized size (and data size) by 8 bytes, allocating another
+ * cluster if required. The bitmap data size has to be at least equal to the
+ * number of mft records in the mft, but it can be bigger, in which case the
+ * superflous bits are padded with zeroes.
+ *
+ * Thus, when we return successfully (return value 0), we will have:
+ * - initialized / extended the mft bitmap if necessary,
+ * - initialized / extended the mft data if necessary,
+ * - set the bit corresponding to the mft record being allocated in the
+ * mft bitmap, and we will
+ * - return the mft record number in @*result.
+ *
+ * On error (return value below zero), nothing will have changed. If we had
+ * changed anything before the error occured, we will have reverted back to
+ * the starting state before returning to the caller. Thus, except for bugs,
+ * we should always leave the volume in a consitents state when returning from
+ * this function. NOTE: Small exception to this is that we set the bit in the
+ * mft bitmap but we do not mark the mft record in use, which is inconsistent.
+ * However, the caller will immediately add the wanted attributes to the mft
+ * record, set it in use and write it out to disk, so there should be no
+ * problem.
+ *
+ * Note, this function cannot make use of most of the normal functions, like
+ * for example for attribute resizing, etc, because when the run list overflows
+ * the base mft record and an attribute list is used, it is very important
+ * that the extension mft records used to store the $DATA attribute of $MFT
+ * can be reached without having to read the information contained inside
+ * them, as this would make it impossible to find them in the first place
+ * after the volume is dismounted. $MFT/$BITMAP probably doesn't need to
+ * follow this rule because the bitmap is not essential for finding the mft
+ * records, but on the other hand, handling the bitmap in this special way
+ * would make life easier because otherwise there might be circular invocations
+ * of functions when reading the bitmap but if we are careful, we should be
+ * able to avoid all problems.
+ *
+ * FIXME: Don't forget $MftMirr, though this probably belongs in
+ * ntfs_update_inode() (or even deeper). (AIA)
+ *
+ * FIXME: Want finer grained locking. (AIA)
+ */
+static int ntfs_alloc_mft_record(ntfs_volume *vol, unsigned long *result)
+{
+ unsigned long nr_mft_records, buf_size, buf_pos, pass_start, pass_end;
+ unsigned long last_read_pos, mft_rec_size, bit, l;
+ ntfs_attribute *data, *bmp;
+ __u8 *buf, *byte, pass, b, have_allocated_mftbmp = 0;
+ int rlen, rl_size = 0, r2len, rl2_size, old_data_rlen, err = 0;
+ ntfs_runlist *rl, *rl2;
+ ntfs_cluster_t lcn = 0, old_data_len;
+ ntfs_io io;
+ __s64 ll, old_data_allocated, old_data_initialized, old_data_size;
+
+ *result = -1UL;
+ /* Allocate a buffer and setup the io structure. */
+ buf = (__u8*)__get_free_page(GFP_NOFS);
+ if (!buf)
+ return -ENOMEM;
+ lock_kernel();
+ /* Get the $DATA and $BITMAP attributes of $MFT. */
+ data = ntfs_find_attr(vol->mft_ino, vol->at_data, 0);
+ bmp = ntfs_find_attr(vol->mft_ino, vol->at_bitmap, 0);
+ if (!data || !bmp) {
+ err = -EINVAL;
+ goto err_ret;
+ }
+ /* Determine the number of allocated mft records in the mft. */
+ pass_end = nr_mft_records = data->allocated >>
+ vol->mft_record_size_bits;
+ ntfs_debug(DEBUG_OTHER, "%s(): nr_mft_records = %lu.\n", __FUNCTION__,
+ nr_mft_records);
+ /* Make sure we don't overflow the bitmap. */
+ l = bmp->initialized << 3;
+ if (l < nr_mft_records)
+ // FIXME: It might be a good idea to extend the bitmap instead.
+ pass_end = l;
+ pass = 1;
+ buf_pos = vol->mft_data_pos;
+ if (buf_pos >= pass_end) {
+ buf_pos = 24UL;
+ pass = 2;
+ }
+ pass_start = buf_pos;
+ rl = bmp->d.r.runlist;
+ rlen = bmp->d.r.len - 1;
+ lcn = rl[rlen].lcn + rl[rlen].len;
+ io.fn_put = ntfs_put;
+ io.fn_get = ntfs_get;
+ ntfs_debug(DEBUG_OTHER, "%s(): Starting bitmap search.\n",
+ __FUNCTION__);
+ ntfs_debug(DEBUG_OTHER, "%s(): pass = %i, pass_start = %lu, pass_end = "
+ "%lu.\n", __FUNCTION__, pass, pass_start, pass_end);
+ byte = NULL; // FIXME: For debugging only.
+ /* Loop until a free mft record is found. */
+ io.size = (nr_mft_records >> 3) & ~PAGE_MASK;
+ for (;; io.size = PAGE_SIZE) {
+ io.param = buf;
+ io.do_read = 1;
+ last_read_pos = buf_pos >> 3;
+ ntfs_debug(DEBUG_OTHER, "%s(): Before: bmp->allocated = 0x%Lx, "
+ "bmp->size = 0x%Lx, bmp->initialized = "
+ "0x%Lx.\n", __FUNCTION__, bmp->allocated,
+ bmp->size, bmp->initialized);
+ err = ntfs_readwrite_attr(vol->mft_ino, bmp, last_read_pos,
+ &io);
+ if (err)
+ goto err_ret;
+ ntfs_debug(DEBUG_OTHER, "%s(): Read %lu bytes.\n", __FUNCTION__,
+ (unsigned long)io.size);
+ ntfs_debug(DEBUG_OTHER, "%s(): After: bmp->allocated = 0x%Lx, "
+ "bmp->size = 0x%Lx, bmp->initialized = "
+ "0x%Lx.\n", __FUNCTION__, bmp->allocated,
+ bmp->size, bmp->initialized);
+ if (!io.size)
+ goto pass_done;
+ buf_size = io.size << 3;
+ bit = buf_pos & 7UL;
+ buf_pos &= ~7UL;
+ ntfs_debug(DEBUG_OTHER, "%s(): Before loop: buf_size = %lu, "
+ "buf_pos = %lu, bit = %lu, *byte = 0x%x, b = "
+ "%u.\n", __FUNCTION__, buf_size, buf_pos, bit,
+ byte ? *byte : -1, b);
+ for (; bit < buf_size && bit + buf_pos < pass_end;
+ bit &= ~7UL, bit += 8UL) {
+ byte = buf + (bit >> 3);
+ if (*byte == 0xff)
+ continue;
+ b = ffz((unsigned long)*byte);
+ if (b < (__u8)8 && b >= (bit & 7UL)) {
+ bit = b + (bit & ~7UL) + buf_pos;
+ ntfs_debug(DEBUG_OTHER, "%s(): Found free rec "
+ "in for loop. bit = %lu\n",
+ __FUNCTION__, bit);
+ goto found_free_rec;
+ }
+ }
+ ntfs_debug(DEBUG_OTHER, "%s(): After loop: buf_size = %lu, "
+ "buf_pos = %lu, bit = %lu, *byte = 0x%x, b = "
+ "%u.\n", __FUNCTION__, buf_size, buf_pos, bit,
+ byte ? *byte : -1, b);
+ buf_pos += buf_size;
+ if (buf_pos < pass_end)
+ continue;
+pass_done: /* Finished with the current pass. */
+ ntfs_debug(DEBUG_OTHER, "%s(): At pass_done.\n", __FUNCTION__);
+ if (pass == 1) {
+ /*
+ * Now do pass 2, scanning the first part of the zone
+ * we omitted in pass 1.
+ */
+ ntfs_debug(DEBUG_OTHER, "%s(): Done pass 1.\n",
+ __FUNCTION__);
+ ntfs_debug(DEBUG_OTHER, "%s(): Pass = 2.\n",
+ __FUNCTION__);
+ pass = 2;
+ pass_end = pass_start;
+ buf_pos = pass_start = 24UL;
+ ntfs_debug(DEBUG_OTHER, "%s(): pass = %i, pass_start = "
+ "%lu, pass_end = %lu.\n", __FUNCTION__,
+ pass, pass_start, pass_end);
+ continue;
+ } /* pass == 2 */
+ /* No free records left. */
+ if (bmp->initialized << 3 > nr_mft_records &&
+ bmp->initialized > 3) {
+ /*
+ * The mft bitmap is already bigger but the space is
+ * not covered by mft records, this implies that the
+ * next records are all free, so we already have found
+ * a free record.
+ */
+ bit = nr_mft_records;
+ if (bit < 24UL)
+ bit = 24UL;
+ ntfs_debug(DEBUG_OTHER, "%s(): Found free record bit "
+ "(#1) = 0x%lx.\n", __FUNCTION__, bit);
+ goto found_free_rec;
+ }
+ ntfs_debug(DEBUG_OTHER, "%s(): Done pass 2.\n", __FUNCTION__);
+ ntfs_debug(DEBUG_OTHER, "%s(): Before: bmp->allocated = 0x%Lx, "
+ "bmp->size = 0x%Lx, bmp->initialized = "
+ "0x%Lx.\n", __FUNCTION__, bmp->allocated,
+ bmp->size, bmp->initialized);
+ /* Need to extend the mft bitmap. */
+ if (bmp->initialized + 8LL > bmp->allocated) {
+ ntfs_io io2;
+
+ ntfs_debug(DEBUG_OTHER, "%s(): Initialized "
+ "> allocated.\n", __FUNCTION__);
+ /* Need to extend bitmap by one more cluster. */
+ rl = bmp->d.r.runlist;
+ rlen = bmp->d.r.len - 1;
+ lcn = rl[rlen].lcn + rl[rlen].len;
+ io2.fn_put = ntfs_put;
+ io2.fn_get = ntfs_get;
+ io2.param = &b;
+ io2.size = 1;
+ io2.do_read = 1;
+ err = ntfs_readwrite_attr(vol->bitmap, data, lcn >> 3,
+ &io2);
+ if (err)
+ goto err_ret;
+ ntfs_debug(DEBUG_OTHER, "%s(): Read %lu bytes.\n",
+ __FUNCTION__, (unsigned long)io2.size);
+ if (io2.size == 1 && b != 0xff) {
+ __u8 tb = 1 << (lcn & (ntfs_cluster_t)7);
+ if (!(b & tb)) {
+ /* Next cluster is free. Allocate it. */
+ b |= tb;
+ io2.param = &b;
+ io2.do_read = 0;
+ err = ntfs_readwrite_attr(vol->bitmap,
+ data, lcn >> 3, &io2);
+ if (err || io.size != 1) {
+ if (!err)
+ err = -EIO;
+ goto err_ret;
+ }
+append_mftbmp_simple: rl[rlen].len++;
+ have_allocated_mftbmp |= 1;
+ ntfs_debug(DEBUG_OTHER, "%s(): "
+ "Appending one cluster "
+ "to mftbmp.\n",
+ __FUNCTION__);
+ }
+ }
+ if (!have_allocated_mftbmp) {
+ /* Allocate a cluster from the DATA_ZONE. */
+ ntfs_cluster_t lcn2 = lcn;
+ ntfs_cluster_t count = 1;
+ err = ntfs_allocate_clusters(vol, &lcn2,
+ &count, &rl2, &r2len,
+ DATA_ZONE);
+ if (err)
+ goto err_ret;
+ if (count != 1 || lcn2 <= 0) {
+ if (count > 0) {
+rl2_dealloc_err_out: if (ntfs_deallocate_clusters(
+ vol, rl2, r2len))
+ ntfs_error("%s(): "
+ "Cluster "
+ "deallocation in error "
+ "code path failed! You "
+ "should run chkdsk.\n",
+ __FUNCTION__);
+ }
+ ntfs_vfree(rl2);
+ if (!err)
+ err = -EINVAL;
+ goto err_ret;
+ }
+ if (lcn2 == lcn) {
+ ntfs_vfree(rl2);
+ goto append_mftbmp_simple;
+ }
+ /* We need to append a new run. */
+ rl_size = (rlen * sizeof(ntfs_runlist) +
+ PAGE_SIZE - 1) & PAGE_MASK;
+ /* Reallocate memory if necessary. */
+ if ((rlen + 2) * sizeof(ntfs_runlist) >=
+ rl_size) {
+ ntfs_runlist *rlt;
+
+ rl_size += PAGE_SIZE;
+ rlt = ntfs_vmalloc(rl_size);
+ if (!rlt) {
+ err = -ENOMEM;
+ goto rl2_dealloc_err_out;
+ }
+ ntfs_memcpy(rlt, rl, rl_size -
+ PAGE_SIZE);
+ ntfs_vfree(rl);
+ bmp->d.r.runlist = rl = rlt;
+ }
+ ntfs_vfree(rl2);
+ rl[rlen].lcn = lcn = lcn2;
+ rl[rlen].len = count;
+ bmp->d.r.len = ++rlen;
+ have_allocated_mftbmp |= 2;
+ ntfs_debug(DEBUG_OTHER, "%s(): Adding run to "
+ "mftbmp. LCN = %i, len = %i\n",
+ __FUNCTION__, lcn, count);
+ }
+ /*
+ * We now have extended the mft bitmap allocated size
+ * by one cluster. Reflect this in the attribute.
+ */
+ bmp->allocated += (__s64)vol->cluster_size;
+ }
+ ntfs_debug(DEBUG_OTHER, "%s(): After: bmp->allocated = 0x%Lx, "
+ "bmp->size = 0x%Lx, bmp->initialized = "
+ "0x%Lx.\n", __FUNCTION__, bmp->allocated,
+ bmp->size, bmp->initialized);
+ /* We now have sufficient allocated space. */
+ ntfs_debug(DEBUG_OTHER, "%s(): Now have sufficient allocated "
+ "space in mftbmp.\n", __FUNCTION__);
+ ntfs_debug(DEBUG_OTHER, "%s(): Before: bmp->allocated = 0x%Lx, "
+ "bmp->size = 0x%Lx, bmp->initialized = "
+ "0x%Lx.\n", __FUNCTION__, bmp->allocated,
+ bmp->size, bmp->initialized);
+ buf_pos = bmp->initialized;
+ bmp->initialized += 8LL;
+ if (bmp->initialized > bmp->size)
+ bmp->size = bmp->initialized;
+ ntfs_debug(DEBUG_OTHER, "%s(): After: bmp->allocated = 0x%Lx, "
+ "bmp->size = 0x%Lx, bmp->initialized = "
+ "0x%Lx.\n", __FUNCTION__, bmp->allocated,
+ bmp->size, bmp->initialized);
+ have_allocated_mftbmp |= 4;
+ /* Update the mft bitmap attribute value. */
+ memset(buf, 0, 8);
+ io.param = buf;
+ io.size = 8;
+ io.do_read = 0;
+ err = ntfs_readwrite_attr(vol->mft_ino, bmp, buf_pos, &io);
+ if (err || io.size != 8) {
+ if (!err)
+ err = -EIO;
+ goto shrink_mftbmp_err_ret;
+ }
+ ntfs_debug(DEBUG_OTHER, "%s(): Wrote extended mftbmp bytes "
+ "%lu.\n", __FUNCTION__, (unsigned long)io.size);
+ ntfs_debug(DEBUG_OTHER, "%s(): After write: bmp->allocated = "
+ "0x%Lx, bmp->size = 0x%Lx, bmp->initialized = "
+ "0x%Lx.\n", __FUNCTION__, bmp->allocated,
+ bmp->size, bmp->initialized);
+ bit = buf_pos << 3;
+ ntfs_debug(DEBUG_OTHER, "%s(): Found free record bit (#2) = "
+ "0x%lx.\n", __FUNCTION__, bit);
+ goto found_free_rec;
+ }
+found_free_rec:
+ /* bit is the found free mft record. Allocate it in the mft bitmap. */
+ vol->mft_data_pos = bit;
+ ntfs_debug(DEBUG_OTHER, "%s(): At found_free_rec.\n", __FUNCTION__);
+ io.param = buf;
+ io.size = 1;
+ io.do_read = 1;
+ ntfs_debug(DEBUG_OTHER, "%s(): Before update: bmp->allocated = 0x%Lx, "
+ "bmp->size = 0x%Lx, bmp->initialized = 0x%Lx.\n",
+ __FUNCTION__, bmp->allocated,
+ bmp->size, bmp->initialized);
+ err = ntfs_readwrite_attr(vol->mft_ino, bmp, bit >> 3, &io);
+ if (err || io.size != 1) {
+ if (!err)
+ err = -EIO;
+ goto shrink_mftbmp_err_ret;
+ }
+ ntfs_debug(DEBUG_OTHER, "%s(): Read %lu bytes.\n", __FUNCTION__,
+ (unsigned long)io.size);
+#ifdef DEBUG
+ /* Check our bit is really zero! */
+ if (*buf & (1 << (bit & 7)))
+ BUG();
+#endif
+ *buf |= 1 << (bit & 7);
+ io.param = buf;
+ io.do_read = 0;
+ err = ntfs_readwrite_attr(vol->mft_ino, bmp, bit >> 3, &io);
+ if (err || io.size != 1) {
+ if (!err)
+ err = -EIO;
+ goto shrink_mftbmp_err_ret;
+ }
+ ntfs_debug(DEBUG_OTHER, "%s(): Wrote %lu bytes.\n", __FUNCTION__,
+ (unsigned long)io.size);
+ ntfs_debug(DEBUG_OTHER, "%s(): After update: bmp->allocated = 0x%Lx, "
+ "bmp->size = 0x%Lx, bmp->initialized = 0x%Lx.\n",
+ __FUNCTION__, bmp->allocated,
+ bmp->size, bmp->initialized);
+ /* The mft bitmap is now uptodate. Deal with mft data attribute now. */
+ ll = (__s64)(bit + 1) << vol->mft_record_size_bits;
+ if (ll <= data->initialized) {
+ /* The allocated record is already initialized. We are done! */
+ ntfs_debug(DEBUG_OTHER, "%s(): Allocated mft record "
+ "already initialized!\n", __FUNCTION__);
+ goto done_ret;
+ }
+ ntfs_debug(DEBUG_OTHER, "%s(): Allocated mft record needs "
+ "to be initialized.\n", __FUNCTION__);
+ /* The mft record is outside the initialized data. */
+ mft_rec_size = (unsigned long)vol->mft_record_size;
+ /* Preserve old values for undo purposes. */
+ old_data_allocated = data->allocated;
+ old_data_rlen = data->d.r.len - 1;
+ old_data_len = data->d.r.runlist[old_data_rlen].len;
+ /*
+ * If necessary, extend the mft until it covers the allocated record.
+ * The loop is only actually used when a freshly formatted volume is
+ * first written to. But it optimizes away nicely in the common case.
+ */
+ while (ll > data->allocated) {
+ ntfs_cluster_t lcn2, nr_lcn2, nr, min_nr;
+
+ ntfs_debug(DEBUG_OTHER, "%s(): Extending mft data allocation, "
+ "data->allocated = 0x%Lx, data->size = 0x%Lx, "
+ "data->initialized = 0x%Lx.\n", __FUNCTION__,
+ data->allocated, data->size, data->initialized);
+ /* Minimum allocation is one mft record worth of clusters. */
+ if (mft_rec_size <= vol->cluster_size)
+ min_nr = (ntfs_cluster_t)1;
+ else
+ min_nr = mft_rec_size >> vol->cluster_size_bits;
+ ntfs_debug(DEBUG_OTHER, "%s(): min_nr = %i.\n", __FUNCTION__,
+ min_nr);
+ /* Allocate 16 mft records worth of clusters. */
+ nr = mft_rec_size << 4 >> vol->cluster_size_bits;
+ if (!nr)
+ nr = (ntfs_cluster_t)1;
+ /* Determine the preferred allocation location. */
+ ntfs_debug(DEBUG_OTHER, "%s(): nr = %i.\n", __FUNCTION__, nr);
+ rl2 = data->d.r.runlist;
+ r2len = data->d.r.len;
+ lcn2 = rl2[r2len - 1].lcn + rl2[r2len - 1].len;
+ ntfs_debug(DEBUG_OTHER, "%s(): rl2[r2len - 1].lcn = %i, .len = "
+ "%i.\n", __FUNCTION__, rl2[r2len - 1].lcn,
+ rl2[r2len - 1].len);
+ ntfs_debug(DEBUG_OTHER, "%s(): lcn2 = %i, r2len = %i.\n",
+ __FUNCTION__, lcn2, r2len);
+retry_mft_data_allocation:
+ nr_lcn2 = nr;
+ err = ntfs_allocate_clusters(vol, &lcn2, &nr_lcn2, &rl2,
+ &r2len, MFT_ZONE);
+#ifdef DEBUG
+ if (!err && nr_lcn2 < min_nr)
+ /* Allocated less than minimum needed. Weird! */
+ BUG();
+#endif
+ if (err) {
+ /*
+ * If there isn't enough space to do the wanted
+ * allocation, but there is enough space to do a
+ * minimal allocation, then try that, unless the wanted
+ * allocation was already the minimal allocation.
+ */
+ if (err == -ENOSPC && nr > min_nr &&
+ nr_lcn2 >= min_nr) {
+ nr = min_nr;
+ ntfs_debug(DEBUG_OTHER, "%s(): Retrying mft "
+ "data allocation, nr = min_nr "
+ "= %i.\n", __FUNCTION__, nr);
+ goto retry_mft_data_allocation;
+ }
+ goto undo_mftbmp_alloc_err_ret;
+ }
+ ntfs_debug(DEBUG_OTHER, "%s(): Allocated %i clusters starting "
+ "at LCN %i.\n", __FUNCTION__, nr_lcn2, lcn2);
+ ntfs_debug(DEBUG_OTHER, "%s(): Allocated runlist:\n",
+ __FUNCTION__);
+ dump_runlist(rl2, r2len);
+ /* Append rl2 to the mft data attribute's run list. */
+ err = splice_runlists(&data->d.r.runlist, (int*)&data->d.r.len,
+ rl2, r2len);
+ if (err) {
+ ntfs_debug(DEBUG_OTHER, "%s(): splice_runlists failed "
+ "with error code %i.\n", __FUNCTION__,
+ -err);
+ goto undo_partial_data_alloc_err_ret;
+ }
+ /* Reflect the allocated clusters in the mft allocated data. */
+ data->allocated += nr_lcn2 << vol->cluster_size_bits;
+ ntfs_debug(DEBUG_OTHER, "%s(): After extending mft data "
+ "allocation, data->allocated = 0x%Lx, "
+ "data->size = 0x%Lx, data->initialized = "
+ "0x%Lx.\n", __FUNCTION__, data->allocated,
+ data->size, data->initialized);
+ }
+ /* Prepare a formatted (empty) mft record. */
+ memset(buf, 0, mft_rec_size);
+ ntfs_fill_mft_header(buf, mft_rec_size, 0, 0, 0);
+ err = ntfs_insert_fixups(buf, mft_rec_size);
+ if (err)
+ goto undo_data_alloc_err_ret;
+ /*
+ * Extend mft data initialized size to reach the allocated mft record
+ * and write the formatted mft record buffer to each mft record being
+ * initialized. Note, that ntfs_readwrite_attr extends both
+ * data->initialized and data->size, so no need for us to touch them.
+ */
+ old_data_initialized = data->initialized;
+ old_data_size = data->size;
+ while (ll > data->initialized) {
+ ntfs_debug(DEBUG_OTHER, "%s(): Initializing mft record "
+ "0x%Lx.\n", __FUNCTION__,
+ data->initialized >> vol->mft_record_size_bits);
+ io.param = buf;
+ io.size = mft_rec_size;
+ io.do_read = 0;
+ err = ntfs_readwrite_attr(vol->mft_ino, data,
+ data->initialized, &io);
+ if (err || io.size != mft_rec_size) {
+ if (!err)
+ err = -EIO;
+ goto undo_data_init_err_ret;
+ }
+ ntfs_debug(DEBUG_OTHER, "%s(): Wrote %i bytes to mft data.\n",
+ __FUNCTION__, io.size);
+ }
+ /* Update the VFS inode size as well. */
+ VFS_I(vol->mft_ino)->i_size = data->size;
+#ifdef DEBUG
+ ntfs_debug(DEBUG_OTHER, "%s(): After mft record "
+ "initialization: data->allocated = 0x%Lx, data->size "
+ "= 0x%Lx, data->initialized = 0x%Lx.\n", __FUNCTION__,
+ data->allocated, data->size, data->initialized);
+ /* Sanity checks. */
+ if (data->size > data->allocated || data->size < data->initialized ||
+ data->initialized > data->allocated)
+ BUG();
+#endif
+done_ret:
+ /* Return the number of the allocated mft record. */
+ ntfs_debug(DEBUG_OTHER, "%s(): At done_ret. *result = bit = 0x%lx.\n",
+ __FUNCTION__, bit);
+ *result = bit;
+ vol->mft_data_pos = bit + 1;
+err_ret:
+ unlock_kernel();
+ free_page((unsigned long)buf);
+ ntfs_debug(DEBUG_OTHER, "%s(): Syncing inode $MFT.\n", __FUNCTION__);
+ if (ntfs_update_inode(vol->mft_ino))
+ ntfs_error("%s(): Failed to sync inode $MFT. "
+ "Continuing anyway.\n",__FUNCTION__);
+ if (!err) {
+ ntfs_debug(DEBUG_FILE3, "%s(): Done. Allocated mft record "
+ "number *result = 0x%lx.\n", __FUNCTION__,
+ *result);
+ return 0;
+ }
+ if (err != -ENOSPC)
+ ntfs_error("%s(): Failed to allocate an mft record. Returning "
+ "error code %i.\n", __FUNCTION__, -err);
+ else
+ ntfs_debug(DEBUG_FILE3, "%s(): Failed to allocate an mft "
+ "record due to lack of free space.\n",
+ __FUNCTION__);
+ return err;
+undo_data_init_err_ret:
+ ntfs_debug(DEBUG_OTHER, "%s(): At undo_data_init_err_ret.\n",
+ __FUNCTION__);
+ data->initialized = old_data_initialized;
+ data->size = old_data_size;
+undo_data_alloc_err_ret:
+ ntfs_debug(DEBUG_OTHER, "%s(): At undo_data_alloc_err_ret.\n",
+ __FUNCTION__);
+ data->allocated = old_data_allocated;
+undo_partial_data_alloc_err_ret:
+ ntfs_debug(DEBUG_OTHER, "%s(): At undo_partial_data_alloc_err_ret.\n",
+ __FUNCTION__);
+ /* Deallocate the clusters. */
+ if (ntfs_deallocate_clusters(vol, rl2, r2len))
+ ntfs_error("%s(): Error deallocating clusters in error code "
+ "path. You should run chkdsk.\n", __FUNCTION__);
+ ntfs_vfree(rl2);
+ /* Revert the run list back to what it was before. */
+ r2len = data->d.r.len;
+ rl2 = data->d.r.runlist;
+ rl2[old_data_rlen++].len = old_data_len;
+ rl2[old_data_rlen].lcn = (ntfs_cluster_t)-1;
+ rl2[old_data_rlen].len = (ntfs_cluster_t)0;
+ data->d.r.len = old_data_rlen;
+ rl2_size = ((old_data_rlen + 1) * sizeof(ntfs_runlist) + PAGE_SIZE -
+ 1) & PAGE_MASK;
+ /* Reallocate memory freeing any extra memory allocated. */
+ if (rl2_size < ((r2len * sizeof(ntfs_runlist) + PAGE_SIZE - 1) &
+ PAGE_MASK)) {
+ rl2 = ntfs_vmalloc(rl2_size);
+ if (rl2) {
+ ntfs_memcpy(rl2, data->d.r.runlist, rl2_size);
+ ntfs_vfree(data->d.r.runlist);
+ data->d.r.runlist = rl2;
+ } else
+ ntfs_error("%s(): Error reallocating "
+ "memory in error code path. This "
+ "should be harmless.\n", __FUNCTION__);
+ }
+undo_mftbmp_alloc_err_ret:
+ ntfs_debug(DEBUG_OTHER, "%s(): At undo_mftbmp_alloc_err_ret.\n",
+ __FUNCTION__);
+ /* Deallocate the allocated bit in the mft bitmap. */
+ io.param = buf;
+ io.size = 1;
+ io.do_read = 1;
+ err = ntfs_readwrite_attr(vol->mft_ino, bmp, bit >> 3, &io);
+ if (!err && io.size == 1) {
+ *buf &= ~(1 << (bit & 7));
+ io.param = buf;
+ io.do_read = 0;
+ err = ntfs_readwrite_attr(vol->mft_ino, bmp, bit >> 3, &io);
+ }
+ if (err || io.size != 1) {
+ if (!err)
+ err = -EIO;
+ ntfs_error("%s(): Error deallocating mft record in error code "
+ "path. You should run chkdsk.\n", __FUNCTION__);
+ }
+shrink_mftbmp_err_ret:
+ ntfs_debug(DEBUG_OTHER, "%s(): At shrink_mftbmp_err_ret.\n",
+ __FUNCTION__);
+ ntfs_debug(DEBUG_OTHER, "%s(): have_allocated_mftbmp = %i.\n",
+ __FUNCTION__, have_allocated_mftbmp);
+ if (!have_allocated_mftbmp)
+ goto err_ret;
+ /* Shrink the mftbmp back to previous size. */
+ if (bmp->size == bmp->initialized)
+ bmp->size -= 8LL;
+ bmp->initialized -= 8LL;
+ have_allocated_mftbmp &= ~4;
+ /* If no allocation occured then we are done. */
+ ntfs_debug(DEBUG_OTHER, "%s(): have_allocated_mftbmp = %i.\n",
+ __FUNCTION__, have_allocated_mftbmp);
+ if (!have_allocated_mftbmp)
+ goto err_ret;
+ /* Deallocate the allocated cluster. */
+ bmp->allocated -= (__s64)vol->cluster_size;
+ if (ntfs_deallocate_cluster_run(vol, lcn, (ntfs_cluster_t)1))
+ ntfs_error("%s(): Error deallocating cluster in error code "
+ "path. You should run chkdsk.\n", __FUNCTION__);
+ switch (have_allocated_mftbmp & 3) {
+ case 1:
+ /* Delete the last lcn from the last run of mftbmp. */
+ rl[rlen - 1].len--;
+ break;
+ case 2:
+ /* Delete the last run of mftbmp. */
+ bmp->d.r.len = --rlen;
+ /* Reallocate memory if necessary. */
+ if ((rlen + 1) * sizeof(ntfs_runlist) <= rl_size - PAGE_SIZE) {
+ ntfs_runlist *rlt;
+
+ rl_size -= PAGE_SIZE;
+ rlt = ntfs_vmalloc(rl_size);
+ if (rlt) {
+ ntfs_memcpy(rlt, rl, rl_size);
+ ntfs_vfree(rl);
+ bmp->d.r.runlist = rl = rlt;
+ } else
+ ntfs_error("%s(): Error "
+ "reallocating memory in error "
+ "code path. This should be "
+ "harmless.\n", __FUNCTION__);
+ }
+ bmp->d.r.runlist[bmp->d.r.len].lcn = (ntfs_cluster_t)-1;
+ bmp->d.r.runlist[bmp->d.r.len].len = (ntfs_cluster_t)0;
+ break;
+ default:
+ BUG();
+ }
+ goto err_ret;
+}
+
+/* We need 0x48 bytes in total. */
+static int add_standard_information(ntfs_inode *ino)
+{
+ ntfs_time64_t now;
+ char data[0x30];
+ char *position = data;
+ ntfs_attribute *si;
+
+ now = ntfs_now();
+ NTFS_PUTU64(position + 0x00, now); /* File creation */
+ NTFS_PUTU64(position + 0x08, now); /* Last modification */
+ NTFS_PUTU64(position + 0x10, now); /* Last mod for MFT */
+ NTFS_PUTU64(position + 0x18, now); /* Last access */
+ NTFS_PUTU64(position + 0x20, 0); /* MSDOS file perms */
+ NTFS_PUTU64(position + 0x28, 0); /* unknown */
+ return ntfs_create_attr(ino, ino->vol->at_standard_information, 0,
+ data, sizeof(data), &si);
+}
+
+static int add_filename(ntfs_inode *ino, ntfs_inode *dir,
+ const unsigned char *filename, int length, ntfs_u32 flags)
+{
+ unsigned char *position;
+ unsigned int size;
+ ntfs_time64_t now;
+ int count, error;
+ unsigned char* data;
+ ntfs_attribute *fn;
+
+ /* Work out the size. */
+ size = 0x42 + 2 * length;
+ data = ntfs_malloc(size);
+ if (!data)
+ return -ENOMEM;
+ /* Search for a position. */
+ position = data;
+ NTFS_PUTINUM(position, dir); /* Inode num of dir */
+ now = ntfs_now();
+ NTFS_PUTU64(position + 0x08, now); /* File creation */
+ NTFS_PUTU64(position + 0x10, now); /* Last modification */
+ NTFS_PUTU64(position + 0x18, now); /* Last mod for MFT */
+ NTFS_PUTU64(position + 0x20, now); /* Last access */
+ /* FIXME: Get the following two sizes by finding the data attribute
+ * in ino->attr and copying the corresponding fields from there.
+ * If no data present then set to zero. In current implementation
+ * add_data is called after add_filename so zero is correct on
+ * creation. Need to change when we have hard links / support different
+ * filename namespaces. (AIA) */
+ NTFS_PUTS64(position + 0x28, 0); /* Allocated size */
+ NTFS_PUTS64(position + 0x30, 0); /* Data size */
+ NTFS_PUTU32(position + 0x38, flags); /* File flags */
+ NTFS_PUTU32(position + 0x3c, 0); /* We don't use these
+ * features yet. */
+ NTFS_PUTU8(position + 0x40, length); /* Filename length */
+ NTFS_PUTU8(position + 0x41, 0); /* Only long name */
+ /* FIXME: This is madness. We are defining the POSIX namespace
+ * for the filename here which can mean that the file will be
+ * invisible when in Windows NT/2k! )-: (AIA) */
+ position += 0x42;
+ for (count = 0; count < length; count++) {
+ NTFS_PUTU16(position + 2 * count, filename[count]);
+ }
+ error = ntfs_create_attr(ino, ino->vol->at_file_name, 0, data, size,
+ &fn);
+ if (!error)
+ error = ntfs_dir_add(dir, ino, fn);
+ ntfs_free(data);
+ return error;
+}
+
+int add_security(ntfs_inode* ino, ntfs_inode* dir)
+{
+ int error;
+ char *buf;
+ int size;
+ ntfs_attribute* attr;
+ ntfs_io io;
+ ntfs_attribute *se;
+
+ attr = ntfs_find_attr(dir, ino->vol->at_security_descriptor, 0);
+ if (!attr)
+ return -EOPNOTSUPP; /* Need security in directory. */
+ size = attr->size;
+ if (size > 512)
+ return -EOPNOTSUPP;
+ buf = ntfs_malloc(size);
+ if (!buf)
+ return -ENOMEM;
+ io.fn_get = ntfs_get;
+ io.fn_put = ntfs_put;
+ io.param = buf;
+ io.size = size;
+ error = ntfs_read_attr(dir, ino->vol->at_security_descriptor, 0, 0,&io);
+ if (!error && io.size != size)
+ ntfs_error("wrong size in add_security\n");
+ if (error) {
+ ntfs_free(buf);
+ return error;
+ }
+ /* FIXME: Consider ACL inheritance. */
+ error = ntfs_create_attr(ino, ino->vol->at_security_descriptor,
+ 0, buf, size, &se);
+ ntfs_free(buf);
+ return error;
+}
+
+static int add_data(ntfs_inode* ino, unsigned char *data, int length)
+{
+ ntfs_attribute *da;
+
+ return ntfs_create_attr(ino, ino->vol->at_data, 0, data, length, &da);
+}
+
+/*
+ * We _could_ use 'dir' to help optimise inode allocation.
+ *
+ * FIXME: Need to undo what we do in ntfs_alloc_mft_record if we get an error
+ * further on in ntfs_alloc_inode. Either fold the two functions to allow
+ * proper undo or just deallocate the record from the mft bitmap. (AIA)
+ */
+int ntfs_alloc_inode(ntfs_inode *dir, ntfs_inode *result, const char *filename,
+ int namelen, ntfs_u32 flags)
+{
+ ntfs_volume *vol = dir->vol;
+ int err;
+ ntfs_u8 buffer[2];
+ ntfs_io io;
+
+ err = ntfs_alloc_mft_record(vol, &(result->i_number));
+ if (err) {
+ if (err == -ENOSPC)
+ ntfs_error("%s(): No free inodes.\n", __FUNCTION__);
+ return err;
+ }
+ /* Get the sequence number. */
+ io.fn_put = ntfs_put;
+ io.fn_get = ntfs_get;
+ io.param = buffer;
+ io.size = 2;
+ err = ntfs_read_attr(vol->mft_ino, vol->at_data, 0,
+ ((__s64)result->i_number << vol->mft_record_size_bits)
+ + 0x10, &io);
+ // FIXME: We are leaving the MFT in inconsistent state! (AIA)
+ if (err)
+ return err;
+ /* Increment the sequence number skipping zero. */
+ result->sequence_number = (NTFS_GETU16(buffer) + 1) & 0xffff;
+ if (!result->sequence_number)
+ result->sequence_number++;
+ result->vol = vol;
+ result->attr_count = 0;
+ result->attrs = 0;
+ result->record_count = 1;
+ result->records = ntfs_calloc(8 * sizeof(int));
+ if (!result->records)
+ goto mem_err_out;
+ result->records[0] = result->i_number;
+ result->attr = ntfs_calloc(vol->mft_record_size);
+ if (!result->attr) {
+ ntfs_free(result->records);
+ result->records = NULL;
+ goto mem_err_out;
+ }
+ ntfs_fill_mft_header(result->attr, vol->mft_record_size,
+ result->sequence_number, 1, 1);
+ err = add_standard_information(result);
+ if (!err)
+ err = add_filename(result, dir, filename, namelen, flags);
+ if (!err)
+ err = add_security(result, dir);
+ // FIXME: We are leaving the MFT in inconsistent state on error! (AIA)
+ return err;
+mem_err_out:
+ // FIXME: We are leaving the MFT in inconsistent state! (AIA)
+ result->record_count = 0;
+ result->attr = NULL;
+ return -ENOMEM;
+}
+
+int ntfs_alloc_file(ntfs_inode *dir, ntfs_inode *result, char *filename,
+ int namelen)
+{
+ int err;
+
+ err = ntfs_alloc_inode(dir, result, filename, namelen, 0);
+ if (!err)
+ err = add_data(result, 0, 0);
+ return err;
+}
+
diff --git a/fs/ntfs/inode.h b/fs/ntfs/inode.h
new file mode 100644
index 00000000000000..96fcb824fbf5e2
--- /dev/null
+++ b/fs/ntfs/inode.h
@@ -0,0 +1,58 @@
+/*
+ * inode.h - Header file for inode.c
+ *
+ * Copyright (C) 1997 Régis Duchesne
+ * Copyright (C) 1998 Martin von Löwis
+ * Copyright (c) 2001 Anton Altparmakov (AIA)
+ */
+
+ntfs_attribute *ntfs_find_attr(ntfs_inode *ino, int type, char *name);
+
+int ntfs_read_attr(ntfs_inode *ino, int type, char *name, __s64 offset,
+ ntfs_io *buf);
+
+int ntfs_write_attr(ntfs_inode *ino, int type, char *name, __s64 offset,
+ ntfs_io *buf);
+
+int ntfs_init_inode(ntfs_inode *ino, ntfs_volume *vol, int inum);
+
+void ntfs_clear_inode(ntfs_inode *ino);
+
+int ntfs_check_mft_record(ntfs_volume *vol, char *record);
+
+int ntfs_alloc_inode(ntfs_inode *dir, ntfs_inode *result, const char *filename,
+ int namelen, ntfs_u32);
+
+int ntfs_alloc_file(ntfs_inode *dir, ntfs_inode *result, char *filename,
+ int namelen);
+
+int ntfs_update_inode(ntfs_inode *ino);
+
+int ntfs_vcn_to_lcn(ntfs_inode *ino, int vcn);
+
+int ntfs_readwrite_attr(ntfs_inode *ino, ntfs_attribute *attr, __s64 offset,
+ ntfs_io *dest);
+
+int ntfs_allocate_attr_number(ntfs_inode *ino, int *result);
+
+int ntfs_decompress_run(unsigned char **data, int *length,
+ ntfs_cluster_t *cluster, int *ctype);
+
+void ntfs_decompress(unsigned char *dest, unsigned char *src, ntfs_size_t l);
+
+int splice_runlists(ntfs_runlist **rl1, int *r1len, const ntfs_runlist *rl2,
+ int r2len);
+
+/*
+ * NOTE: Neither of the ntfs_*_bit functions are atomic! But we don't need
+ * them atomic at present as we never operate on shared/cached bitmaps.
+ */
+static __inline__ int ntfs_test_and_set_bit(unsigned char *byte, const int bit)
+{
+ unsigned char *ptr = byte + (bit >> 3);
+ int b = 1 << (bit & 7);
+ int oldbit = *ptr & b ? 1 : 0;
+ *ptr |= b;
+ return oldbit;
+}
+
diff --git a/fs/ntfs/macros.h b/fs/ntfs/macros.h
new file mode 100644
index 00000000000000..708730950db164
--- /dev/null
+++ b/fs/ntfs/macros.h
@@ -0,0 +1,43 @@
+/*
+ * macros.h
+ *
+ * Copyright (C) 1995 Martin von Löwis
+ * Copyright (C) 1996 Régis Duchesne
+ * Copyright (c) 2001 Anton Altaparmakov
+ */
+#include <linux/ntfs_fs_i.h>
+#include <linux/fs.h>
+#include <asm/page.h>
+
+#define NTFS_FD(vol) ((vol)->u.fd)
+
+#define NTFS_SB(vol) ((struct super_block*)(vol)->sb)
+#define NTFS_SB2VOL(sb) (&(sb)->u.ntfs_sb)
+#define NTFS_INO2VOL(ino) (&((ino)->i_sb->u.ntfs_sb))
+#define NTFS_LINO2NINO(ino) ((struct ntfs_inode_info*)(&((ino)->u.ntfs_i)))
+static inline struct inode *VFS_I(struct ntfs_inode_info *ntfs_ino)
+{
+ struct inode *i = (struct inode*)((char*)ntfs_ino -
+ ((char*)&(((struct inode*)NULL)->u.ntfs_i) -
+ (char*)NULL));
+#ifdef DEBUG
+ if ((char*)NTFS_LINO2NINO(i) != (char*)ntfs_ino)
+ BUG();
+#endif
+ return i;
+}
+
+#define IS_MAGIC(a,b) (*(int*)(a) == *(int*)(b))
+#define IS_MFT_RECORD(a) IS_MAGIC((a),"FILE")
+#define IS_INDEX_RECORD(a) IS_MAGIC((a),"INDX")
+
+/* 'NTFS' in little endian */
+#define NTFS_SUPER_MAGIC 0x5346544E
+
+#define NTFS_AFLAG_RO 1
+#define NTFS_AFLAG_HIDDEN 2
+#define NTFS_AFLAG_SYSTEM 4
+#define NTFS_AFLAG_ARCHIVE 20
+#define NTFS_AFLAG_COMPRESSED 0x800
+#define NTFS_AFLAG_DIR 0x10000000
+
diff --git a/fs/ntfs/ntfsendian.h b/fs/ntfs/ntfsendian.h
new file mode 100644
index 00000000000000..1ec6946e8a1858
--- /dev/null
+++ b/fs/ntfs/ntfsendian.h
@@ -0,0 +1,60 @@
+/*
+ * ntfsendian.h
+ *
+ * Copyright (C) 1998, 1999 Martin von Löwis
+ * Copyright (C) 1998 Joseph Malicki
+ * Copyright (C) 1999 Werner Seiler
+ * Copyright (C) 2001 Anton Altaparmakov (AIA)
+ */
+#include <asm/byteorder.h>
+
+#define CPU_TO_LE16(a) __cpu_to_le16(a)
+#define CPU_TO_LE32(a) __cpu_to_le32(a)
+#define CPU_TO_LE64(a) __cpu_to_le64(a)
+
+#define LE16_TO_CPU(a) __cpu_to_le16(a)
+#define LE32_TO_CPU(a) __cpu_to_le32(a)
+#define LE64_TO_CPU(a) __cpu_to_le64(a)
+
+#define NTFS_GETU8(p) (*(ntfs_u8*)(p))
+#define NTFS_GETU16(p) ((ntfs_u16)LE16_TO_CPU(*(ntfs_u16*)(p)))
+#define NTFS_GETU24(p) ((ntfs_u32)NTFS_GETU16(p) | \
+ ((ntfs_u32)NTFS_GETU8(((char*)(p)) + 2) << 16))
+#define NTFS_GETU32(p) ((ntfs_u32)LE32_TO_CPU(*(ntfs_u32*)(p)))
+#define NTFS_GETU40(p) ((ntfs_u64)NTFS_GETU32(p) | \
+ (((ntfs_u64)NTFS_GETU8(((char*)(p)) + 4)) << 32))
+#define NTFS_GETU48(p) ((ntfs_u64)NTFS_GETU32(p) | \
+ (((ntfs_u64)NTFS_GETU16(((char*)(p)) + 4)) << 32))
+#define NTFS_GETU56(p) ((ntfs_u64)NTFS_GETU32(p) | \
+ (((ntfs_u64)NTFS_GETU24(((char*)(p)) + 4)) << 32))
+#define NTFS_GETU64(p) ((ntfs_u64)LE64_TO_CPU(*(ntfs_u64*)(p)))
+
+ /* Macros writing unsigned integers */
+#define NTFS_PUTU8(p,v) ((*(ntfs_u8*)(p)) = (v))
+#define NTFS_PUTU16(p,v) ((*(ntfs_u16*)(p)) = CPU_TO_LE16(v))
+#define NTFS_PUTU24(p,v) NTFS_PUTU16(p, (v) & 0xFFFF);\
+ NTFS_PUTU8(((char*)(p)) + 2, (v) >> 16)
+#define NTFS_PUTU32(p,v) ((*(ntfs_u32*)(p)) = CPU_TO_LE32(v))
+#define NTFS_PUTU64(p,v) ((*(ntfs_u64*)(p)) = CPU_TO_LE64(v))
+
+ /* Macros reading signed integers */
+#define NTFS_GETS8(p) ((*(ntfs_s8*)(p)))
+#define NTFS_GETS16(p) ((ntfs_s16)LE16_TO_CPU(*(short*)(p)))
+#define NTFS_GETS24(p) (NTFS_GETU24(p) < 0x800000 ? \
+ (int)NTFS_GETU24(p) : \
+ (int)(NTFS_GETU24(p) - 0x1000000))
+#define NTFS_GETS32(p) ((ntfs_s32)LE32_TO_CPU(*(int*)(p)))
+#define NTFS_GETS40(p) (((ntfs_s64)NTFS_GETU32(p)) | \
+ (((ntfs_s64)NTFS_GETS8(((char*)(p)) + 4)) << 32))
+#define NTFS_GETS48(p) (((ntfs_s64)NTFS_GETU32(p)) | \
+ (((ntfs_s64)NTFS_GETS16(((char*)(p)) + 4)) << 32))
+#define NTFS_GETS56(p) (((ntfs_s64)NTFS_GETU32(p)) | \
+ (((ntfs_s64)NTFS_GETS24(((char*)(p)) + 4)) << 32))
+#define NTFS_GETS64(p) ((ntfs_s64)NTFS_GETU64(p))
+
+#define NTFS_PUTS8(p,v) NTFS_PUTU8(p,v)
+#define NTFS_PUTS16(p,v) NTFS_PUTU16(p,v)
+#define NTFS_PUTS24(p,v) NTFS_PUTU24(p,v)
+#define NTFS_PUTS32(p,v) NTFS_PUTU32(p,v)
+#define NTFS_PUTS64(p,v) NTFS_PUTU64(p,v)
+
diff --git a/fs/ntfs/ntfstypes.h b/fs/ntfs/ntfstypes.h
new file mode 100644
index 00000000000000..efd527d81e7a1c
--- /dev/null
+++ b/fs/ntfs/ntfstypes.h
@@ -0,0 +1,84 @@
+/*
+ * ntfstypes.h - This file defines four things:
+ * - Generic platform independent fixed-size types (e.g. ntfs_u32).
+ * - Specific fixed-size types (e.g. ntfs_offset_t).
+ * - Macros that read and write those types from and to byte arrays.
+ * - Types derived from OS specific ones.
+ *
+ * Copyright (C) 1996, 1998, 1999 Martin von Löwis
+ * Copyright (C) 2001 Anton Altaparmakov (AIA)
+ */
+#include <linux/fs.h>
+#include "ntfsendian.h"
+#include <asm/types.h>
+
+/* Integral types */
+#ifndef NTFS_INTEGRAL_TYPES
+#define NTFS_INTEGRAL_TYPES
+typedef u8 ntfs_u8;
+typedef u16 ntfs_u16;
+typedef u32 ntfs_u32;
+typedef u64 ntfs_u64;
+typedef s8 ntfs_s8;
+typedef s16 ntfs_s16;
+typedef s32 ntfs_s32;
+typedef s64 ntfs_s64;
+#endif
+
+/* Unicode character type */
+#ifndef NTFS_WCHAR_T
+#define NTFS_WCHAR_T
+typedef u16 ntfs_wchar_t;
+#endif
+/* File offset */
+#ifndef NTFS_OFFSET_T
+#define NTFS_OFFSET_T
+typedef s64 ntfs_offset_t;
+#endif
+/* UTC */
+#ifndef NTFS_TIME64_T
+#define NTFS_TIME64_T
+typedef u64 ntfs_time64_t;
+#endif
+/*
+ * This is really signed long long. So we support only volumes up to 2Tb. This
+ * is ok as Win2k also only uses 32-bits to store clusters.
+ * Whatever you do keep this a SIGNED value or a lot of NTFS users with
+ * corrupted filesystems will lynch you! It causes massive fs corruption when
+ * unsigned due to the nature of many checks relying on being performed on
+ * signed quantities. (AIA)
+ */
+#ifndef NTFS_CLUSTER_T
+#define NTFS_CLUSTER_T
+typedef s32 ntfs_cluster_t;
+#endif
+
+/* Architecture independent macros. */
+
+/* PUTU32 would not clear all bytes. */
+#define NTFS_PUTINUM(p,i) NTFS_PUTU64(p, i->i_number); \
+ NTFS_PUTU16(((char*)p) + 6, i->sequence_number)
+
+/* System dependent types. */
+#include <asm/posix_types.h>
+#ifndef NTMODE_T
+#define NTMODE_T
+typedef __kernel_mode_t ntmode_t;
+#endif
+#ifndef NTFS_UID_T
+#define NTFS_UID_T
+typedef uid_t ntfs_uid_t;
+#endif
+#ifndef NTFS_GID_T
+#define NTFS_GID_T
+typedef gid_t ntfs_gid_t;
+#endif
+#ifndef NTFS_SIZE_T
+#define NTFS_SIZE_T
+typedef __kernel_size_t ntfs_size_t;
+#endif
+#ifndef NTFS_TIME_T
+#define NTFS_TIME_T
+typedef __kernel_time_t ntfs_time_t;
+#endif
+
diff --git a/fs/ntfs/struct.h b/fs/ntfs/struct.h
new file mode 100644
index 00000000000000..0cb909b037e4a3
--- /dev/null
+++ b/fs/ntfs/struct.h
@@ -0,0 +1,69 @@
+/*
+ * struct.h - Structure definitions
+ *
+ * Copyright (C) 1997 Régis Duchesne
+ * Copyright (C) 2000-2001 Anton Altaparmakov (AIA)
+ */
+#include <linux/ntfs_fs.h>
+
+/* Necessary forward definition. */
+struct ntfs_inode;
+
+/* Which files should be returned from a director listing. */
+#define ngt_dos 1 /* only short names, no system files */
+#define ngt_nt 2 /* only long names, all-uppercase becomes
+ * all-lowercase, no system files */
+#define ngt_posix 3 /* all names except system files */
+#define ngt_full 4 /* all entries */
+
+typedef struct ntfs_sb_info ntfs_volume;
+
+typedef struct {
+ ntfs_cluster_t lcn;
+ ntfs_cluster_t len;
+} ntfs_runlist;
+
+typedef struct ntfs_attribute {
+ int type;
+ ntfs_u16 *name;
+ int namelen;
+ int attrno;
+ __s64 size, allocated, initialized, compsize;
+ ATTR_FLAGS flags;
+ __u8 resident, indexed;
+ int cengine;
+ union {
+ void *data; /* if resident */
+ struct {
+ ntfs_runlist *runlist;
+ unsigned long len;
+ } r;
+ } d;
+} ntfs_attribute;
+
+typedef struct ntfs_inode_info ntfs_inode;
+
+/* Structure to define IO to user buffer. do_read means that the destination
+ * has to be written using fn_put, do_write means that the destination has to
+ * read using fn_get. So, do_read is from a user's point of view, while put and
+ * get are from the driver's point of view. The first argument is always the
+ * destination of the IO. */
+typedef struct ntfs_io{
+ int do_read;
+ void (*fn_put)(struct ntfs_io *dest, void *buf, ntfs_size_t);
+ void (*fn_get)(void *buf, struct ntfs_io *src, ntfs_size_t len);
+ void *param;
+ unsigned long size;
+} ntfs_io;
+
+#if 0
+typedef struct {
+ ntfs_volume *vol;
+ ntfs_inode *ino;
+ int type;
+ char *name;
+ int mftno;
+ int start_vcn;
+} ntfs_attrlist_item;
+#endif
+
diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c
new file mode 100644
index 00000000000000..3dc9013b7822f7
--- /dev/null
+++ b/fs/ntfs/super.c
@@ -0,0 +1,1416 @@
+/*
+ * super.c
+ *
+ * Copyright (C) 1995-1997, 1999 Martin von Löwis
+ * Copyright (C) 1996-1997 Régis Duchesne
+ * Copyright (C) 1999 Steve Dodd
+ * Copyright (C) 2000-2001 Anton Altparmakov (AIA)
+ */
+
+#include <linux/ntfs_fs.h>
+#include <linux/errno.h>
+#include <linux/bitops.h>
+#include <linux/module.h>
+#include "ntfstypes.h"
+#include "struct.h"
+#include "super.h"
+#include "macros.h"
+#include "inode.h"
+#include "support.h"
+#include "util.h"
+#include <linux/smp_lock.h>
+
+/* All important structures in NTFS use 2 consistency checks:
+ * . a magic structure identifier (FILE, INDX, RSTR, RCRD...)
+ * . a fixup technique : the last word of each sector (called a fixup) of a
+ * structure's record should end with the word at offset <n> of the first
+ * sector, and if it is the case, must be replaced with the words following
+ * <n>. The value of <n> and the number of fixups is taken from the fields
+ * at the offsets 4 and 6. Note that the sector size is defined as
+ * NTFS_SECTOR_SIZE and not as the hardware sector size (this is concordant
+ * with what the Windows NTFS driver does).
+ *
+ * This function performs these 2 checks, and _fails_ if:
+ * . the input size is invalid
+ * . the fixup header is invalid
+ * . the size does not match the number of sectors
+ * . the magic identifier is wrong
+ * . a fixup is invalid
+ */
+int ntfs_fixup_record(char *record, char *magic, int size)
+{
+ int start, count, offset;
+ ntfs_u16 fixup;
+
+ if (!IS_MAGIC(record, magic))
+ return 0;
+ start = NTFS_GETU16(record + 4);
+ count = NTFS_GETU16(record + 6) - 1;
+ if (size & (NTFS_SECTOR_SIZE - 1) || start & 1 ||
+ start + count * 2 > size || size >> 9 != count) {
+ if (size <= 0)
+ printk(KERN_ERR "NTFS: BUG: ntfs_fixup_record() got "
+ "zero size! Please report this to "
+ "linux-ntfs-dev@lists.sf.net\n");
+ return 0;
+ }
+ fixup = NTFS_GETU16(record + start);
+ start += 2;
+ offset = NTFS_SECTOR_SIZE - 2;
+ while (count--) {
+ if (NTFS_GETU16(record + offset) != fixup)
+ return 0;
+ NTFS_PUTU16(record + offset, NTFS_GETU16(record + start));
+ start += 2;
+ offset += NTFS_SECTOR_SIZE;
+ }
+ return 1;
+}
+
+/*
+ * Get vital informations about the ntfs partition from the boot sector.
+ * Return 0 on success or -1 on error.
+ */
+int ntfs_init_volume(ntfs_volume *vol, char *boot)
+{
+ int sectors_per_cluster_bits;
+ __s64 ll;
+ ntfs_cluster_t mft_zone_size, tc;
+
+ /* System defined default values, in case we don't load $AttrDef. */
+ vol->at_standard_information = 0x10;
+ vol->at_attribute_list = 0x20;
+ vol->at_file_name = 0x30;
+ vol->at_volume_version = 0x40;
+ vol->at_security_descriptor = 0x50;
+ vol->at_volume_name = 0x60;
+ vol->at_volume_information = 0x70;
+ vol->at_data = 0x80;
+ vol->at_index_root = 0x90;
+ vol->at_index_allocation = 0xA0;
+ vol->at_bitmap = 0xB0;
+ vol->at_symlink = 0xC0;
+ /* Sector size. */
+ vol->sector_size = NTFS_GETU16(boot + 0xB);
+ ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->sector_size = 0x%x\n",
+ vol->sector_size);
+ ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: sectors_per_cluster = "
+ "0x%x\n", NTFS_GETU8(boot + 0xD));
+ sectors_per_cluster_bits = ffs(NTFS_GETU8(boot + 0xD)) - 1;
+ ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: sectors_per_cluster_bits "
+ "= 0x%x\n", sectors_per_cluster_bits);
+ vol->mft_clusters_per_record = NTFS_GETS8(boot + 0x40);
+ ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->mft_clusters_per_record"
+ " = 0x%x\n", vol->mft_clusters_per_record);
+ vol->index_clusters_per_record = NTFS_GETS8(boot + 0x44);
+ ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: "
+ "vol->index_clusters_per_record = 0x%x\n",
+ vol->index_clusters_per_record);
+ vol->cluster_size = vol->sector_size << sectors_per_cluster_bits;
+ vol->cluster_size_bits = ffs(vol->cluster_size) - 1;
+ ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->cluster_size = 0x%x\n",
+ vol->cluster_size);
+ ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->cluster_size_bits = "
+ "0x%x\n", vol->cluster_size_bits);
+ if (vol->mft_clusters_per_record > 0)
+ vol->mft_record_size = vol->cluster_size <<
+ (ffs(vol->mft_clusters_per_record) - 1);
+ else
+ /*
+ * When mft_record_size < cluster_size, mft_clusters_per_record
+ * = -log2(mft_record_size) bytes. mft_record_size normaly is
+ * 1024 bytes, which is encoded as 0xF6 (-10 in decimal).
+ */
+ vol->mft_record_size = 1 << -vol->mft_clusters_per_record;
+ vol->mft_record_size_bits = ffs(vol->mft_record_size) - 1;
+ ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->mft_record_size = 0x%x"
+ "\n", vol->mft_record_size);
+ ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->mft_record_size_bits = "
+ "0x%x\n", vol->mft_record_size_bits);
+ if (vol->index_clusters_per_record > 0)
+ vol->index_record_size = vol->cluster_size <<
+ (ffs(vol->index_clusters_per_record) - 1);
+ else
+ /*
+ * When index_record_size < cluster_size,
+ * index_clusters_per_record = -log2(index_record_size) bytes.
+ * index_record_size normaly equals 4096 bytes, which is
+ * encoded as 0xF4 (-12 in decimal).
+ */
+ vol->index_record_size = 1 << -vol->index_clusters_per_record;
+ vol->index_record_size_bits = ffs(vol->index_record_size) - 1;
+ ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->index_record_size = "
+ "0x%x\n", vol->index_record_size);
+ ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->index_record_size_bits "
+ "= 0x%x\n", vol->index_record_size_bits);
+ /*
+ * Get the size of the volume in clusters (ofs 0x28 is nr_sectors) and
+ * check for 64-bit-ness. Windows currently only uses 32 bits to save
+ * the clusters so we do the same as it is much faster on 32-bit CPUs.
+ */
+ ll = NTFS_GETS64(boot + 0x28) >> sectors_per_cluster_bits;
+ if (ll >= (__s64)1 << 31) {
+ ntfs_error("Cannot handle 64-bit clusters. Please inform "
+ "linux-ntfs-dev@lists.sf.net that you got this "
+ "error.\n");
+ return -1;
+ }
+ vol->nr_clusters = (ntfs_cluster_t)ll;
+ ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->nr_clusters = 0x%x\n",
+ vol->nr_clusters);
+ vol->mft_lcn = (ntfs_cluster_t)NTFS_GETS64(boot + 0x30);
+ vol->mft_mirr_lcn = (ntfs_cluster_t)NTFS_GETS64(boot + 0x38);
+ /* Determine MFT zone size. */
+ mft_zone_size = vol->nr_clusters;
+ switch (vol->mft_zone_multiplier) { /* % of volume size in clusters */
+ case 4:
+ mft_zone_size >>= 1; /* 50% */
+ break;
+ case 3:
+ mft_zone_size = mft_zone_size * 3 >> 3; /* 37.5% */
+ break;
+ case 2:
+ mft_zone_size >>= 2; /* 25% */
+ break;
+ /* case 1: */
+ default:
+ mft_zone_size >>= 3; /* 12.5% */
+ break;
+ }
+ /* Setup mft zone. */
+ vol->mft_zone_start = vol->mft_zone_pos = vol->mft_lcn;
+ ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->mft_zone_pos = %x\n",
+ vol->mft_zone_pos);
+ /*
+ * Calculate the mft_lcn for an unmodified NTFS volume (see mkntfs
+ * source) and if the actual mft_lcn is in the expected place or even
+ * further to the front of the volume, extend the mft_zone to cover the
+ * beginning of the volume as well. This is in order to protect the
+ * area reserved for the mft bitmap as well within the mft_zone itself.
+ * On non-standard volumes we don't protect it as well as the overhead
+ * would be higher than the speed increase we would get by doing it.
+ */
+ tc = (8192 + 2 * vol->cluster_size - 1) / vol->cluster_size;
+ if (tc * vol->cluster_size < 16 * 1024)
+ tc = (16 * 1024 + vol->cluster_size - 1) / vol->cluster_size;
+ if (vol->mft_zone_start <= tc)
+ vol->mft_zone_start = (ntfs_cluster_t)0;
+ ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->mft_zone_start = %x\n",
+ vol->mft_zone_start);
+ /*
+ * Need to cap the mft zone on non-standard volumes so that it does
+ * not point outside the boundaries of the volume, we do this by
+ * halving the zone size until we are inside the volume.
+ */
+ vol->mft_zone_end = vol->mft_lcn + mft_zone_size;
+ while (vol->mft_zone_end >= vol->nr_clusters) {
+ mft_zone_size >>= 1;
+ vol->mft_zone_end = vol->mft_lcn + mft_zone_size;
+ }
+ ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->mft_zone_end = %x\n",
+ vol->mft_zone_end);
+ /*
+ * Set the current position within each data zone to the start of the
+ * respective zone.
+ */
+ vol->data1_zone_pos = vol->mft_zone_end;
+ ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->data1_zone_pos = %x\n",
+ vol->data1_zone_pos);
+ vol->data2_zone_pos = (ntfs_cluster_t)0;
+ ntfs_debug(DEBUG_FILE3, "ntfs_init_volume: vol->data2_zone_pos = %x\n",
+ vol->data2_zone_pos);
+ /* Set the mft data allocation position to mft record 24. */
+ vol->mft_data_pos = 24UL;
+ /* This will be initialized later. */
+ vol->upcase = 0;
+ vol->upcase_length = 0;
+ vol->mft_ino = 0;
+ return 0;
+}
+
+static void ntfs_init_upcase(ntfs_inode *upcase)
+{
+ ntfs_io io;
+#define UPCASE_LENGTH 256
+ upcase->vol->upcase = ntfs_malloc(UPCASE_LENGTH << 1);
+ if (!upcase->vol->upcase)
+ return;
+ io.fn_put = ntfs_put;
+ io.fn_get = 0;
+ io.param = (char*)upcase->vol->upcase;
+ io.size = UPCASE_LENGTH << 1;
+ ntfs_read_attr(upcase, upcase->vol->at_data, 0, 0, &io);
+ upcase->vol->upcase_length = io.size >> 1;
+}
+
+static int process_attrdef(ntfs_inode* attrdef, ntfs_u8* def)
+{
+ int type = NTFS_GETU32(def+0x80);
+ int check_type = 0;
+ ntfs_volume *vol = attrdef->vol;
+ ntfs_u16* name = (ntfs_u16*)def;
+
+ if (!type) {
+ ntfs_debug(DEBUG_OTHER, "process_atrdef: finished processing "
+ "and returning 1\n");
+ return 1;
+ }
+ if (ntfs_ua_strncmp(name, "$STANDARD_INFORMATION", 64) == 0) {
+ vol->at_standard_information = type;
+ check_type = 0x10;
+ } else if (ntfs_ua_strncmp(name, "$ATTRIBUTE_LIST", 64) == 0) {
+ vol->at_attribute_list = type;
+ check_type = 0x20;
+ } else if (ntfs_ua_strncmp(name, "$FILE_NAME", 64) == 0) {
+ vol->at_file_name = type;
+ check_type = 0x30;
+ } else if (ntfs_ua_strncmp(name, "$VOLUME_VERSION", 64) == 0) {
+ vol->at_volume_version = type;
+ check_type = 0x40;
+ } else if (ntfs_ua_strncmp(name, "$SECURITY_DESCRIPTOR", 64) == 0) {
+ vol->at_security_descriptor = type;
+ check_type = 0x50;
+ } else if (ntfs_ua_strncmp(name, "$VOLUME_NAME", 64) == 0) {
+ vol->at_volume_name = type;
+ check_type = 0x60;
+ } else if (ntfs_ua_strncmp(name, "$VOLUME_INFORMATION", 64) == 0) {
+ vol->at_volume_information = type;
+ check_type = 0x70;
+ } else if (ntfs_ua_strncmp(name, "$DATA", 64) == 0) {
+ vol->at_data = type;
+ check_type = 0x80;
+ } else if (ntfs_ua_strncmp(name, "$INDEX_ROOT", 64) == 0) {
+ vol->at_index_root = type;
+ check_type = 0x90;
+ } else if (ntfs_ua_strncmp(name, "$INDEX_ALLOCATION", 64) == 0) {
+ vol->at_index_allocation = type;
+ check_type = 0xA0;
+ } else if (ntfs_ua_strncmp(name, "$BITMAP", 64) == 0) {
+ vol->at_bitmap = type;
+ check_type = 0xB0;
+ } else if (ntfs_ua_strncmp(name, "$SYMBOLIC_LINK", 64) == 0 ||
+ ntfs_ua_strncmp(name, "$REPARSE_POINT", 64) == 0) {
+ vol->at_symlink = type;
+ check_type = 0xC0;
+ }
+ if (check_type && check_type != type) {
+ ntfs_error("process_attrdef: unexpected type 0x%x for 0x%x\n",
+ type, check_type);
+ return -EINVAL;
+ }
+ ntfs_debug(DEBUG_OTHER, "process_attrdef: found %s attribute of type "
+ "0x%x\n", check_type ? "known" : "unknown", type);
+ return 0;
+}
+
+int ntfs_init_attrdef(ntfs_inode* attrdef)
+{
+ ntfs_u8 *buf;
+ ntfs_io io;
+ __s64 offset;
+ unsigned i;
+ int error;
+ ntfs_attribute *data;
+
+ ntfs_debug(DEBUG_BSD, "Entered ntfs_init_attrdef()\n");
+ buf = ntfs_malloc(4050); /* 90*45 */
+ if (!buf)
+ return -ENOMEM;
+ io.fn_put = ntfs_put;
+ io.fn_get = ntfs_get;
+ io.do_read = 1;
+ offset = 0;
+ data = ntfs_find_attr(attrdef, attrdef->vol->at_data, 0);
+ ntfs_debug(DEBUG_BSD, "In ntfs_init_attrdef() after call to "
+ "ntfs_find_attr.\n");
+ if (!data) {
+ ntfs_free(buf);
+ return -EINVAL;
+ }
+ do {
+ io.param = buf;
+ io.size = 4050;
+ ntfs_debug(DEBUG_BSD, "In ntfs_init_attrdef() going to call "
+ "ntfs_readwrite_attr.\n");
+ error = ntfs_readwrite_attr(attrdef, data, offset, &io);
+ ntfs_debug(DEBUG_BSD, "In ntfs_init_attrdef() after call to "
+ "ntfs_readwrite_attr.\n");
+ for (i = 0; !error && i <= io.size - 0xA0; i += 0xA0) {
+ ntfs_debug(DEBUG_BSD, "In ntfs_init_attrdef() going "
+ "to call process_attrdef.\n");
+ error = process_attrdef(attrdef, buf + i);
+ ntfs_debug(DEBUG_BSD, "In ntfs_init_attrdef() after "
+ "call to process_attrdef.\n");
+ }
+ offset += 4096;
+ } while (!error && io.size);
+ ntfs_debug(DEBUG_BSD, "Exiting ntfs_init_attrdef()\n");
+ ntfs_free(buf);
+ return error == 1 ? 0 : error;
+}
+
+/* ntfs_get_version will determine the NTFS version of the volume and will
+ * return the version in a BCD format, with the MSB being the major version
+ * number and the LSB the minor one. Otherwise return <0 on error.
+ * Example: version 3.1 will be returned as 0x0301. This has the obvious
+ * limitation of not coping with version numbers above 0x80 but that shouldn't
+ * be a problem... */
+int ntfs_get_version(ntfs_inode* volume)
+{
+ ntfs_attribute *volinfo;
+
+ volinfo = ntfs_find_attr(volume, volume->vol->at_volume_information, 0);
+ if (!volinfo)
+ return -EINVAL;
+ if (!volinfo->resident) {
+ ntfs_error("Volume information attribute is not resident!\n");
+ return -EINVAL;
+ }
+ return ((ntfs_u8*)volinfo->d.data)[8] << 8 |
+ ((ntfs_u8*)volinfo->d.data)[9];
+}
+
+int ntfs_load_special_files(ntfs_volume *vol)
+{
+ int error;
+ ntfs_inode upcase, attrdef, volume;
+
+ vol->mft_ino = (ntfs_inode*)ntfs_calloc(sizeof(ntfs_inode));
+ vol->mftmirr = (ntfs_inode*)ntfs_calloc(sizeof(ntfs_inode));
+ vol->bitmap = (ntfs_inode*)ntfs_calloc(sizeof(ntfs_inode));
+ vol->ino_flags = 4 | 2 | 1;
+ error = -ENOMEM;
+ ntfs_debug(DEBUG_BSD, "Going to load MFT\n");
+ if (!vol->mft_ino || (error = ntfs_init_inode(vol->mft_ino, vol,
+ FILE_Mft))) {
+ ntfs_error("Problem loading MFT\n");
+ return error;
+ }
+ ntfs_debug(DEBUG_BSD, "Going to load MIRR\n");
+ if ((error = ntfs_init_inode(vol->mftmirr, vol, FILE_MftMirr))) {
+ ntfs_error("Problem %d loading MFTMirr\n", error);
+ return error;
+ }
+ ntfs_debug(DEBUG_BSD, "Going to load BITMAP\n");
+ if ((error = ntfs_init_inode(vol->bitmap, vol, FILE_BitMap))) {
+ ntfs_error("Problem loading Bitmap\n");
+ return error;
+ }
+ ntfs_debug(DEBUG_BSD, "Going to load UPCASE\n");
+ error = ntfs_init_inode(&upcase, vol, FILE_UpCase);
+ if (error)
+ return error;
+ ntfs_init_upcase(&upcase);
+ ntfs_clear_inode(&upcase);
+ ntfs_debug(DEBUG_BSD, "Going to load ATTRDEF\n");
+ error = ntfs_init_inode(&attrdef, vol, FILE_AttrDef);
+ if (error)
+ return error;
+ error = ntfs_init_attrdef(&attrdef);
+ ntfs_clear_inode(&attrdef);
+ if (error)
+ return error;
+
+ /* Check for NTFS version and if Win2k version (ie. 3.0+) do not allow
+ * write access since the driver write support is broken. */
+ ntfs_debug(DEBUG_BSD, "Going to load VOLUME\n");
+ error = ntfs_init_inode(&volume, vol, FILE_Volume);
+ if (error)
+ return error;
+ if ((error = ntfs_get_version(&volume)) >= 0x0300 &&
+ !(NTFS_SB(vol)->s_flags & MS_RDONLY)) {
+ NTFS_SB(vol)->s_flags |= MS_RDONLY;
+ ntfs_error("Warning! NTFS volume version is Win2k+: Mounting "
+ "read-only\n");
+ }
+ ntfs_clear_inode(&volume);
+ if (error < 0)
+ return error;
+ ntfs_debug(DEBUG_BSD, "NTFS volume is v%d.%d\n", error >> 8,
+ error & 0xff);
+ return 0;
+}
+
+int ntfs_release_volume(ntfs_volume *vol)
+{
+ if (((vol->ino_flags & 1) == 1) && vol->mft_ino) {
+ ntfs_clear_inode(vol->mft_ino);
+ ntfs_free(vol->mft_ino);
+ vol->mft_ino = 0;
+ }
+ if (((vol->ino_flags & 2) == 2) && vol->mftmirr) {
+ ntfs_clear_inode(vol->mftmirr);
+ ntfs_free(vol->mftmirr);
+ vol->mftmirr = 0;
+ }
+ if (((vol->ino_flags & 4) == 4) && vol->bitmap) {
+ ntfs_clear_inode(vol->bitmap);
+ ntfs_free(vol->bitmap);
+ vol->bitmap = 0;
+ }
+ ntfs_free(vol->mft);
+ ntfs_free(vol->upcase);
+ return 0;
+}
+
+/*
+ * Writes the volume size (units of clusters) into vol_size.
+ * Returns 0 if successful or error.
+ */
+int ntfs_get_volumesize(ntfs_volume *vol, ntfs_s64 *vol_size)
+{
+ ntfs_io io;
+ char *cluster0;
+
+ if (!vol_size)
+ return -EFAULT;
+ cluster0 = ntfs_malloc(vol->cluster_size);
+ if (!cluster0)
+ return -ENOMEM;
+ io.fn_put = ntfs_put;
+ io.fn_get = ntfs_get;
+ io.param = cluster0;
+ io.do_read = 1;
+ io.size = vol->cluster_size;
+ ntfs_getput_clusters(vol, 0, 0, &io);
+ *vol_size = NTFS_GETU64(cluster0 + 0x28) >>
+ (ffs(NTFS_GETU8(cluster0 + 0xD)) - 1);
+ ntfs_free(cluster0);
+ return 0;
+}
+
+static int nc[16]={4,3,3,2,3,2,2,1,3,2,2,1,2,1,1,0};
+
+int ntfs_get_free_cluster_count(ntfs_inode *bitmap)
+{
+ ntfs_io io;
+ int offset, error, clusters;
+ unsigned char *bits = ntfs_malloc(2048);
+ if (!bits)
+ return -ENOMEM;
+ offset = clusters = 0;
+ io.fn_put = ntfs_put;
+ io.fn_get = ntfs_get;
+ while (1) {
+ register int i;
+ io.param = bits;
+ io.size = 2048;
+ error = ntfs_read_attr(bitmap, bitmap->vol->at_data, 0, offset,
+ &io);
+ if (error || io.size == 0)
+ break;
+ /* I never thought I would do loop unrolling some day */
+ for (i = 0; i < io.size - 8; ) {
+ clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
+ clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
+ clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
+ clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
+ clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
+ clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
+ clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
+ clusters+=nc[bits[i]>>4];clusters+=nc[bits[i++] & 0xF];
+ }
+ while (i < io.size) {
+ clusters += nc[bits[i] >> 4];
+ clusters += nc[bits[i++] & 0xF];
+ }
+ offset += io.size;
+ }
+ ntfs_free(bits);
+ return clusters;
+}
+
+/*
+ * Insert the fixups for the record. The number and location of the fixes
+ * is obtained from the record header but we double check with @rec_size and
+ * use that as the upper boundary, if necessary overwriting the count value in
+ * the record header.
+ *
+ * We return 0 on success or -1 if fixup header indicated the beginning of the
+ * update sequence array to be beyond the valid limit.
+ */
+int ntfs_insert_fixups(unsigned char *rec, int rec_size)
+{
+ int first;
+ int count;
+ int offset = -2;
+ ntfs_u16 fix;
+
+ first = NTFS_GETU16(rec + 4);
+ count = (rec_size >> NTFS_SECTOR_BITS) + 1;
+ if (first + count * 2 > NTFS_SECTOR_SIZE - 2) {
+ printk(KERN_CRIT "NTFS: ntfs_insert_fixups() detected corrupt "
+ "NTFS record update sequence array position. - "
+ "Cannot hotfix.\n");
+ return -1;
+ }
+ if (count != NTFS_GETU16(rec + 6)) {
+ printk(KERN_ERR "NTFS: ntfs_insert_fixups() detected corrupt "
+ "NTFS record update sequence array size. - "
+ "Applying hotfix.\n");
+ NTFS_PUTU16(rec + 6, count);
+ }
+ fix = (NTFS_GETU16(rec + first) + 1) & 0xffff;
+ if (fix == 0xffff || !fix)
+ fix = 1;
+ NTFS_PUTU16(rec + first, fix);
+ count--;
+ while (count--) {
+ first += 2;
+ offset += NTFS_SECTOR_SIZE;
+ NTFS_PUTU16(rec + first, NTFS_GETU16(rec + offset));
+ NTFS_PUTU16(rec + offset, fix);
+ }
+ return 0;
+}
+
+/**
+ * ntfs_allocate_clusters - allocate logical clusters on an ntfs volume
+ * @vol: volume on which to allocate clusters
+ * @location: preferred location for first allocated cluster
+ * @count: number of clusters to allocate
+ * @rl: address of pointer in which to return the allocated run list
+ * @rl_len: the number of elements returned in @*rl
+ *
+ * Allocate @*count clusters (LCNs), preferably beginning at @*location in the
+ * bitmap of the volume @vol. If @*location is -1, it does not matter where the
+ * clusters are. @rl is the address of a ntfs_runlist pointer which this
+ * function will allocate and fill with the runlist of the allocated clusters.
+ * It is the callers responsibility to ntfs_vfree() @*rl after she is finished
+ * with it. If the function was not successful, @*rl will be set to NULL.
+ * @*rl_len will contain the number of ntfs_runlist elements in @*rl or 0 if
+ * @*rl is NULL.
+ *
+ * Return 0 on success, or -errno on error. On success, @*location and @*count
+ * say what was really allocated. On -ENOSPC, @*location and @*count say what
+ * could have been allocated. If nothing could be allocated or a different
+ * error occured, @*location = -1 and @*count = 0.
+ *
+ * There are two data zones. First is the area between the end of the mft zone
+ * and the end of the volume, and second is the area between the start of the
+ * volume and the start of the mft zone. On unmodified/standard volumes, the
+ * second mft zone doesn't exist due to the mft zone being expanded to cover
+ * the start of volume in order to reserve space for the mft bitmap attribute.
+ *
+ * This is not the prettiest function but the complexity stems from the need of
+ * implementing the mft vs data zoned approach and from the fact that we have
+ * access to the lcn bitmap in portions of PAGE_SIZE bytes at a time, so we
+ * need to cope with crossing over boundaries of two pages. Further, the fact
+ * that the allocator allows for caller supplied hints as to the location of
+ * where allocation should begin and the fact that the allocator keeps track of
+ * where in the data zones the next natural allocation should occur, contribute
+ * to the complexity of the function. But it should all be worthwhile, because
+ * this allocator should: 1) be a full implementation of the MFT zone approach
+ * used by Windows, 2) cause reduction in fragmentation as much as possible,
+ * and 3) be speedy in allocations (the code is not optimized for speed, but
+ * the algorithm is, so further speed improvements are probably possible).
+ *
+ * FIXME: Really need finer-grained locking but this will do for the moment. I
+ * just want to kill all races and have a working allocator. When that is done,
+ * we can beautify... (AIA)
+ *
+ * FIXME: We should be monitoring cluster allocation and increment the MFT zone
+ * size dynamically but this is something for the future. We will just cause
+ * heavier fragmentation by not doing it and I am not even sure Windows would
+ * grow the MFT zone dynamically, so might even be correct not doing this. The
+ * overhead in doing dynamic MFT zone expansion would be very large and unlikely
+ * worth the effort. (AIA)
+ *
+ * TODO: I have added in double the required zone position pointer wrap around
+ * logic which can be optimized to having only one of the two logic sets.
+ * However, having the double logic will work fine, but if we have only one of
+ * the sets and we get it wrong somewhere, then we get into trouble, so
+ * removing the duplicate logic requires _very_ careful consideration of _all_
+ * possible code paths. So at least for now, I am leaving the double logic -
+ * better safe than sorry... (AIA)
+ */
+int ntfs_allocate_clusters(ntfs_volume *vol, ntfs_cluster_t *location,
+ ntfs_cluster_t *count, ntfs_runlist **rl, int *rl_len,
+ const NTFS_CLUSTER_ALLOCATION_ZONES zone)
+{
+ ntfs_runlist *rl2 = NULL, *rlt;
+ ntfs_attribute *data;
+ ntfs_cluster_t buf_pos, zone_start, zone_end, mft_zone_size;
+ ntfs_cluster_t lcn, last_read_pos, prev_lcn = (ntfs_cluster_t)0;
+ ntfs_cluster_t initial_location, prev_run_len = (ntfs_cluster_t)0;
+ ntfs_cluster_t clusters = (ntfs_cluster_t)0;
+ unsigned char *buf, *byte, bit, search_zone, done_zones;
+ unsigned char pass, need_writeback;
+ int rlpos = 0, rlsize, buf_size, err = 0;
+ ntfs_io io;
+
+ ntfs_debug(DEBUG_OTHER, "%s(): Entering with *location = 0x%x, "
+ "*count = 0x%x, zone = %s_ZONE.\n", __FUNCTION__,
+ *location, *count, zone == DATA_ZONE ? "DATA" : "MFT");
+ buf = (char*)__get_free_page(GFP_NOFS);
+ if (!buf) {
+ ntfs_debug(DEBUG_OTHER, "%s(): Returning -ENOMEM.\n",
+ __FUNCTION__);
+ return -ENOMEM;
+ }
+ io.fn_put = ntfs_put;
+ io.fn_get = ntfs_get;
+ lock_kernel();
+ /* Get the $DATA attribute of $Bitmap. */
+ data = ntfs_find_attr(vol->bitmap, vol->at_data, 0);
+ if (!data) {
+ err = -EINVAL;
+ goto err_ret;
+ }
+ /*
+ * If no specific location was requested, use the current data zone
+ * position, otherwise use the requested location but make sure it lies
+ * outside the mft zone. Also set done_zones to 0 (no zones done) and
+ * pass depending on whether we are starting inside a zone (1) or
+ * at the beginning of a zone (2). If requesting from the MFT_ZONE, then
+ * we either start at the current position within the mft zone or at the
+ * specified position and if the latter is out of bounds then we start
+ * at the beginning of the MFT_ZONE.
+ */
+ done_zones = 0;
+ pass = 1;
+ /*
+ * zone_start and zone_end are the current search range. search_zone
+ * is 1 for mft zone, 2 for data zone 1 (end of mft zone till end of
+ * volume) and 4 for data zone 2 (start of volume till start of mft
+ * zone).
+ */
+ zone_start = *location;
+ if (zone_start < 0) {
+ if (zone == DATA_ZONE)
+ zone_start = vol->data1_zone_pos;
+ else
+ zone_start = vol->mft_zone_pos;
+ if (!zone_start)
+ /*
+ * Zone starts at beginning of volume which means a
+ * single pass is sufficient.
+ */
+ pass = 2;
+ } else if (zone_start >= vol->mft_zone_start && zone_start <
+ vol->mft_zone_end && zone == DATA_ZONE) {
+ zone_start = vol->mft_zone_end;
+ pass = 2;
+ } else if ((zone_start < vol->mft_zone_start || zone_start >=
+ vol->mft_zone_end) && zone == MFT_ZONE) {
+ zone_start = vol->mft_lcn;
+ if (!vol->mft_zone_end)
+ zone_start = (ntfs_cluster_t)0;
+ pass = 2;
+ }
+ if (zone == DATA_ZONE) {
+ /* Skip searching the mft zone. */
+ done_zones |= 1;
+ if (zone_start >= vol->mft_zone_end) {
+ zone_end = vol->nr_clusters;
+ search_zone = 2;
+ } else {
+ zone_end = vol->mft_zone_start;
+ search_zone = 4;
+ }
+ } else /* if (zone == MFT_ZONE) */ {
+ zone_end = vol->mft_zone_end;
+ search_zone = 1;
+ }
+ /*
+ * buf_pos is the current bit position inside the bitmap. We use
+ * initial_location to determine whether or not to do a zone switch.
+ */
+ buf_pos = initial_location = zone_start;
+ /* Loop until all clusters are allocated, i.e. clusters == 0. */
+ clusters = *count;
+ rlpos = rlsize = 0;
+ if (*count <= 0) {
+ ntfs_debug(DEBUG_OTHER, "%s(): *count <= 0, "
+ "returning -EINVAL.\n", __FUNCTION__);
+ err = -EINVAL;
+ goto err_ret;
+ }
+ while (1) {
+ ntfs_debug(DEBUG_OTHER, "%s(): Start of outer while "
+ "loop: done_zones = 0x%x, search_zone = %i, "
+ "pass = %i, zone_start = 0x%x, zone_end = "
+ "0x%x, initial_location = 0x%x, buf_pos = "
+ "0x%x, rlpos = %i, rlsize = %i.\n",
+ __FUNCTION__, done_zones, search_zone, pass,
+ zone_start, zone_end, initial_location, buf_pos,
+ rlpos, rlsize);
+ /* Loop until we run out of free clusters. */
+ io.param = buf;
+ io.size = PAGE_SIZE;
+ io.do_read = 1;
+ last_read_pos = buf_pos >> 3;
+ ntfs_debug(DEBUG_OTHER, "%s(): last_read_pos = 0x%x.\n",
+ __FUNCTION__, last_read_pos);
+ err = ntfs_readwrite_attr(vol->bitmap, data, last_read_pos,
+ &io);
+ if (err) {
+ ntfs_debug(DEBUG_OTHER, "%s(): ntfs_read_attr failed "
+ "with error code %i, going to "
+ "err_ret.\n", __FUNCTION__, -err);
+ goto err_ret;
+ }
+ if (!io.size) {
+ ntfs_debug(DEBUG_OTHER, "%s(): !io.size, going to "
+ "zone_pass_done.\n", __FUNCTION__);
+ goto zone_pass_done;
+ }
+ buf_size = io.size << 3;
+ lcn = buf_pos & 7;
+ buf_pos &= ~7;
+ need_writeback = 0;
+ ntfs_debug(DEBUG_OTHER, "%s(): Before inner while "
+ "loop: buf_size = 0x%x, lcn = 0x%x, buf_pos = "
+ "0x%x, need_writeback = %i.\n", __FUNCTION__,
+ buf_size, lcn, buf_pos, need_writeback);
+ while (lcn < buf_size && lcn + buf_pos < zone_end) {
+ byte = buf + (lcn >> 3);
+ ntfs_debug(DEBUG_OTHER, "%s(): In inner while loop: "
+ "buf_size = 0x%x, lcn = 0x%x, buf_pos "
+ "= 0x%x, need_writeback = %i, byte ofs "
+ "= 0x%x, *byte = 0x%x.\n", __FUNCTION__,
+ buf_size, lcn, buf_pos, need_writeback,
+ lcn >> 3, *byte);
+ /* Skip full bytes. */
+ if (*byte == 0xff) {
+ lcn += 8;
+ ntfs_debug(DEBUG_OTHER, "%s(): continuing while"
+ " loop 1.\n", __FUNCTION__);
+ continue;
+ }
+ bit = 1 << (lcn & 7);
+ ntfs_debug(DEBUG_OTHER, "%s(): bit = %i.\n",
+ __FUNCTION__, bit);
+ /* If the bit is already set, go onto the next one. */
+ if (*byte & bit) {
+ lcn++;
+ ntfs_debug(DEBUG_OTHER, "%s(): continuing while"
+ " loop 2.\n", __FUNCTION__);
+ continue;
+ }
+ /* Allocate the bitmap bit. */
+ *byte |= bit;
+ /* We need to write this bitmap buffer back to disk! */
+ need_writeback = 1;
+ ntfs_debug(DEBUG_OTHER, "%s(): *byte = 0x%x, "
+ "need_writeback = %i.\n", __FUNCTION__,
+ *byte, need_writeback);
+ /* Reallocate memory if necessary. */
+ if ((rlpos + 2) * sizeof(ntfs_runlist) >= rlsize) {
+ ntfs_debug(DEBUG_OTHER, "%s(): Reallocating "
+ "space.\n", __FUNCTION__);
+ /* Setup first free bit return value. */
+ if (!rl2) {
+ *location = lcn + buf_pos;
+ ntfs_debug(DEBUG_OTHER, "%s(): "
+ "*location = 0x%x.\n",
+ __FUNCTION__,
+ *location);
+ }
+ rlsize += PAGE_SIZE;
+ rlt = ntfs_vmalloc(rlsize);
+ if (!rlt) {
+ err = -ENOMEM;
+ ntfs_debug(DEBUG_OTHER, "%s(): Failed "
+ "to allocate memory, "
+ "returning -ENOMEM, "
+ "going to wb_err_ret.\n",
+ __FUNCTION__);
+ goto wb_err_ret;
+ }
+ if (rl2) {
+ ntfs_memcpy(rlt, rl2, rlsize -
+ PAGE_SIZE);
+ ntfs_vfree(rl2);
+ }
+ rl2 = rlt;
+ ntfs_debug(DEBUG_OTHER, "%s(): Reallocated "
+ "memory, rlsize = 0x%x.\n",
+ __FUNCTION__, rlsize);
+ }
+ /*
+ * Coalesce with previous run if adjacent LCNs.
+ * Otherwise, append a new run.
+ */
+ ntfs_debug(DEBUG_OTHER, "%s(): Adding run (lcn 0x%x, "
+ "len 0x%x), prev_lcn = 0x%x, lcn = "
+ "0x%x, buf_pos = 0x%x, prev_run_len = "
+ "0x%x, rlpos = %i.\n", __FUNCTION__,
+ lcn + buf_pos, 1, prev_lcn, lcn,
+ buf_pos, prev_run_len, rlpos);
+ if (prev_lcn == lcn + buf_pos - prev_run_len && rlpos) {
+ ntfs_debug(DEBUG_OTHER, "%s(): Coalescing to "
+ "run (lcn 0x%x, len 0x%x).\n",
+ __FUNCTION__,
+ rl2[rlpos - 1].lcn,
+ rl2[rlpos - 1].len);
+ rl2[rlpos - 1].len = ++prev_run_len;
+ ntfs_debug(DEBUG_OTHER, "%s(): Run now (lcn "
+ "0x%x, len 0x%x), prev_run_len "
+ "= 0x%x.\n", __FUNCTION__,
+ rl2[rlpos - 1].lcn,
+ rl2[rlpos - 1].len,
+ prev_run_len);
+ } else {
+ if (rlpos)
+ ntfs_debug(DEBUG_OTHER, "%s(): Adding "
+ "new run, (previous "
+ "run lcn 0x%x, "
+ "len 0x%x).\n",
+ __FUNCTION__,
+ rl2[rlpos - 1].lcn,
+ rl2[rlpos - 1].len);
+ else
+ ntfs_debug(DEBUG_OTHER, "%s(): Adding "
+ "new run, is first "
+ "run.\n", __FUNCTION__);
+ rl2[rlpos].lcn = prev_lcn = lcn + buf_pos;
+ rl2[rlpos].len = prev_run_len =
+ (ntfs_cluster_t)1;
+
+ rlpos++;
+ }
+ /* Done? */
+ if (!--clusters) {
+ ntfs_cluster_t tc;
+ /*
+ * Update the current zone position. Positions
+ * of already scanned zones have been updated
+ * during the respective zone switches.
+ */
+ tc = lcn + buf_pos + 1;
+ ntfs_debug(DEBUG_OTHER, "%s(): Done. Updating "
+ "current zone position, tc = "
+ "0x%x, search_zone = %i.\n",
+ __FUNCTION__, tc, search_zone);
+ switch (search_zone) {
+ case 1:
+ ntfs_debug(DEBUG_OTHER,
+ "%s(): Before checks, "
+ "vol->mft_zone_pos = "
+ "0x%x.\n", __FUNCTION__,
+ vol->mft_zone_pos);
+ if (tc >= vol->mft_zone_end) {
+ vol->mft_zone_pos =
+ vol->mft_lcn;
+ if (!vol->mft_zone_end)
+ vol->mft_zone_pos =
+ (ntfs_cluster_t)0;
+ } else if ((initial_location >=
+ vol->mft_zone_pos ||
+ tc > vol->mft_zone_pos)
+ && tc >= vol->mft_lcn)
+ vol->mft_zone_pos = tc;
+ ntfs_debug(DEBUG_OTHER,
+ "%s(): After checks, "
+ "vol->mft_zone_pos = "
+ "0x%x.\n", __FUNCTION__,
+ vol->mft_zone_pos);
+ break;
+ case 2:
+ ntfs_debug(DEBUG_OTHER,
+ "%s(): Before checks, "
+ "vol->data1_zone_pos = "
+ "0x%x.\n", __FUNCTION__,
+ vol->data1_zone_pos);
+ if (tc >= vol->nr_clusters)
+ vol->data1_zone_pos =
+ vol->mft_zone_end;
+ else if ((initial_location >=
+ vol->data1_zone_pos ||
+ tc > vol->data1_zone_pos)
+ && tc >= vol->mft_zone_end)
+ vol->data1_zone_pos = tc;
+ ntfs_debug(DEBUG_OTHER,
+ "%s(): After checks, "
+ "vol->data1_zone_pos = "
+ "0x%x.\n", __FUNCTION__,
+ vol->data1_zone_pos);
+ break;
+ case 4:
+ ntfs_debug(DEBUG_OTHER,
+ "%s(): Before checks, "
+ "vol->data2_zone_pos = "
+ "0x%x.\n", __FUNCTION__,
+ vol->data2_zone_pos);
+ if (tc >= vol->mft_zone_start)
+ vol->data2_zone_pos =
+ (ntfs_cluster_t)0;
+ else if (initial_location >=
+ vol->data2_zone_pos ||
+ tc > vol->data2_zone_pos)
+ vol->data2_zone_pos = tc;
+ ntfs_debug(DEBUG_OTHER,
+ "%s(): After checks, "
+ "vol->data2_zone_pos = "
+ "0x%x.\n", __FUNCTION__,
+ vol->data2_zone_pos);
+ break;
+ default:
+ BUG();
+ }
+ ntfs_debug(DEBUG_OTHER, "%s(): Going to "
+ "done_ret.\n", __FUNCTION__);
+ goto done_ret;
+ }
+ lcn++;
+ }
+ buf_pos += buf_size;
+ ntfs_debug(DEBUG_OTHER, "%s(): After inner while "
+ "loop: buf_size = 0x%x, lcn = 0x%x, buf_pos = "
+ "0x%x, need_writeback = %i.\n", __FUNCTION__,
+ buf_size, lcn, buf_pos, need_writeback);
+ if (need_writeback) {
+ ntfs_debug(DEBUG_OTHER, "%s(): Writing back.\n",
+ __FUNCTION__);
+ need_writeback = 0;
+ io.param = buf;
+ io.do_read = 0;
+ err = ntfs_readwrite_attr(vol->bitmap, data,
+ last_read_pos, &io);
+ if (err) {
+ ntfs_error("%s(): Bitmap writeback failed "
+ "in read next buffer code "
+ "path with error code %i.\n",
+ __FUNCTION__, -err);
+ goto err_ret;
+ }
+ }
+ if (buf_pos < zone_end) {
+ ntfs_debug(DEBUG_OTHER, "%s(): Continuing "
+ "outer while loop, buf_pos = 0x%x, "
+ "zone_end = 0x%x.\n", __FUNCTION__,
+ buf_pos, zone_end);
+ continue;
+ }
+zone_pass_done: /* Finished with the current zone pass. */
+ ntfs_debug(DEBUG_OTHER, "%s(): At zone_pass_done, pass = %i.\n",
+ __FUNCTION__, pass);
+ if (pass == 1) {
+ /*
+ * Now do pass 2, scanning the first part of the zone
+ * we omitted in pass 1.
+ */
+ pass = 2;
+ zone_end = zone_start;
+ switch (search_zone) {
+ case 1: /* mft_zone */
+ zone_start = vol->mft_zone_start;
+ break;
+ case 2: /* data1_zone */
+ zone_start = vol->mft_zone_end;
+ break;
+ case 4: /* data2_zone */
+ zone_start = (ntfs_cluster_t)0;
+ break;
+ default:
+ BUG();
+ }
+ /* Sanity check. */
+ if (zone_end < zone_start)
+ zone_end = zone_start;
+ buf_pos = zone_start;
+ ntfs_debug(DEBUG_OTHER, "%s(): Continuing "
+ "outer while loop, pass = 2, "
+ "zone_start = 0x%x, zone_end = 0x%x, "
+ "buf_pos = 0x%x.\n", __FUNCTION__,
+ zone_start, zone_end, buf_pos);
+ continue;
+ } /* pass == 2 */
+done_zones_check:
+ ntfs_debug(DEBUG_OTHER, "%s(): At done_zones_check, "
+ "search_zone = %i, done_zones before = 0x%x, "
+ "done_zones after = 0x%x.\n", __FUNCTION__,
+ search_zone, done_zones, done_zones |
+ search_zone);
+ done_zones |= search_zone;
+ if (done_zones < 7) {
+ ntfs_debug(DEBUG_OTHER, "%s(): Switching zone.\n",
+ __FUNCTION__);
+ /* Now switch to the next zone we haven't done yet. */
+ pass = 1;
+ switch (search_zone) {
+ case 1:
+ ntfs_debug(DEBUG_OTHER, "%s(): Switching from "
+ "mft zone to data1 zone.\n",
+ __FUNCTION__);
+ /* Update mft zone position. */
+ if (rlpos) {
+ ntfs_cluster_t tc;
+ ntfs_debug(DEBUG_OTHER,
+ "%s(): Before checks, "
+ "vol->mft_zone_pos = "
+ "0x%x.\n", __FUNCTION__,
+ vol->mft_zone_pos);
+ tc = rl2[rlpos - 1].lcn +
+ rl2[rlpos - 1].len;
+ if (tc >= vol->mft_zone_end) {
+ vol->mft_zone_pos =
+ vol->mft_lcn;
+ if (!vol->mft_zone_end)
+ vol->mft_zone_pos =
+ (ntfs_cluster_t)0;
+ } else if ((initial_location >=
+ vol->mft_zone_pos ||
+ tc > vol->mft_zone_pos)
+ && tc >= vol->mft_lcn)
+ vol->mft_zone_pos = tc;
+ ntfs_debug(DEBUG_OTHER,
+ "%s(): After checks, "
+ "vol->mft_zone_pos = "
+ "0x%x.\n", __FUNCTION__,
+ vol->mft_zone_pos);
+ }
+ /* Switch from mft zone to data1 zone. */
+switch_to_data1_zone: search_zone = 2;
+ zone_start = initial_location =
+ vol->data1_zone_pos;
+ zone_end = vol->nr_clusters;
+ if (zone_start == vol->mft_zone_end)
+ pass = 2;
+ if (zone_start >= zone_end) {
+ vol->data1_zone_pos = zone_start =
+ vol->mft_zone_end;
+ pass = 2;
+ }
+ break;
+ case 2:
+ ntfs_debug(DEBUG_OTHER, "%s(): Switching from "
+ "data1 zone to data2 zone.\n",
+ __FUNCTION__);
+ /* Update data1 zone position. */
+ if (rlpos) {
+ ntfs_cluster_t tc;
+ ntfs_debug(DEBUG_OTHER,
+ "%s(): Before checks, "
+ "vol->data1_zone_pos = "
+ "0x%x.\n", __FUNCTION__,
+ vol->data1_zone_pos);
+ tc = rl2[rlpos - 1].lcn +
+ rl2[rlpos - 1].len;
+ if (tc >= vol->nr_clusters)
+ vol->data1_zone_pos =
+ vol->mft_zone_end;
+ else if ((initial_location >=
+ vol->data1_zone_pos ||
+ tc > vol->data1_zone_pos)
+ && tc >= vol->mft_zone_end)
+ vol->data1_zone_pos = tc;
+ ntfs_debug(DEBUG_OTHER,
+ "%s(): After checks, "
+ "vol->data1_zone_pos = "
+ "0x%x.\n", __FUNCTION__,
+ vol->data1_zone_pos);
+ }
+ /* Switch from data1 zone to data2 zone. */
+ search_zone = 4;
+ zone_start = initial_location =
+ vol->data2_zone_pos;
+ zone_end = vol->mft_zone_start;
+ if (!zone_start)
+ pass = 2;
+ if (zone_start >= zone_end) {
+ vol->data2_zone_pos = zone_start =
+ initial_location =
+ (ntfs_cluster_t)0;
+ pass = 2;
+ }
+ break;
+ case 4:
+ ntfs_debug(DEBUG_OTHER, "%s(): Switching from "
+ "data2 zone to data1 zone.\n",
+ __FUNCTION__);
+ /* Update data2 zone position. */
+ if (rlpos) {
+ ntfs_cluster_t tc;
+ ntfs_debug(DEBUG_OTHER,
+ "%s(): Before checks, "
+ "vol->data2_zone_pos = "
+ "0x%x.\n", __FUNCTION__,
+ vol->data2_zone_pos);
+ tc = rl2[rlpos - 1].lcn +
+ rl2[rlpos - 1].len;
+ if (tc >= vol->mft_zone_start)
+ vol->data2_zone_pos =
+ (ntfs_cluster_t)0;
+ else if (initial_location >=
+ vol->data2_zone_pos ||
+ tc > vol->data2_zone_pos)
+ vol->data2_zone_pos = tc;
+ ntfs_debug(DEBUG_OTHER,
+ "%s(): After checks, "
+ "vol->data2_zone_pos = "
+ "0x%x.\n", __FUNCTION__,
+ vol->data2_zone_pos);
+ }
+ /* Switch from data2 zone to data1 zone. */
+ goto switch_to_data1_zone; /* See above. */
+ default:
+ BUG();
+ }
+ ntfs_debug(DEBUG_OTHER, "%s(): After zone switch, "
+ "search_zone = %i, pass = %i, "
+ "initial_location = 0x%x, zone_start "
+ "= 0x%x, zone_end = 0x%x.\n",
+ __FUNCTION__, search_zone, pass,
+ initial_location, zone_start, zone_end);
+ buf_pos = zone_start;
+ if (zone_start == zone_end) {
+ ntfs_debug(DEBUG_OTHER, "%s(): Empty zone, "
+ "going to done_zones_check.\n",
+ __FUNCTION__);
+ /* Empty zone. Don't bother searching it. */
+ goto done_zones_check;
+ }
+ ntfs_debug(DEBUG_OTHER, "%s(): Continuing outer while "
+ "loop.\n", __FUNCTION__);
+ continue;
+ } /* done_zones == 7 */
+ ntfs_debug(DEBUG_OTHER, "%s(): All zones are finished.\n",
+ __FUNCTION__);
+ /*
+ * All zones are finished! If DATA_ZONE, shrink mft zone. If
+ * MFT_ZONE, we have really run out of space.
+ */
+ mft_zone_size = vol->mft_zone_end - vol->mft_zone_start;
+ ntfs_debug(DEBUG_OTHER, "%s(): vol->mft_zone_start = 0x%x, "
+ "vol->mft_zone_end = 0x%x, mft_zone_size = "
+ "0x%x.\n", __FUNCTION__, vol->mft_zone_start,
+ vol->mft_zone_end, mft_zone_size);
+ if (zone == MFT_ZONE || mft_zone_size <= (ntfs_cluster_t)0) {
+ ntfs_debug(DEBUG_OTHER, "%s(): No free clusters left, "
+ "returning -ENOSPC, going to "
+ "fail_ret.\n", __FUNCTION__);
+ /* Really no more space left on device. */
+ err = -ENOSPC;
+ goto fail_ret;
+ } /* zone == DATA_ZONE && mft_zone_size > 0 */
+ ntfs_debug(DEBUG_OTHER, "%s(): Shrinking mft zone.\n",
+ __FUNCTION__);
+ zone_end = vol->mft_zone_end;
+ mft_zone_size >>= 1;
+ if (mft_zone_size > (ntfs_cluster_t)0)
+ vol->mft_zone_end = vol->mft_zone_start + mft_zone_size;
+ else /* mft zone and data2 zone no longer exist. */
+ vol->data2_zone_pos = vol->mft_zone_start =
+ vol->mft_zone_end = (ntfs_cluster_t)0;
+ if (vol->mft_zone_pos >= vol->mft_zone_end) {
+ vol->mft_zone_pos = vol->mft_lcn;
+ if (!vol->mft_zone_end)
+ vol->mft_zone_pos = (ntfs_cluster_t)0;
+ }
+ buf_pos = zone_start = initial_location =
+ vol->data1_zone_pos = vol->mft_zone_end;
+ search_zone = 2;
+ pass = 2;
+ done_zones &= ~2;
+ ntfs_debug(DEBUG_OTHER, "%s(): After shrinking mft "
+ "zone, mft_zone_size = 0x%x, "
+ "vol->mft_zone_start = 0x%x, vol->mft_zone_end "
+ "= 0x%x, vol->mft_zone_pos = 0x%x, search_zone "
+ "= 2, pass = 2, dones_zones = 0x%x, zone_start "
+ "= 0x%x, zone_end = 0x%x, vol->data1_zone_pos "
+ "= 0x%x, continuing outer while loop.\n",
+ __FUNCTION__, mft_zone_size,
+ vol->mft_zone_start, vol->mft_zone_end,
+ vol->mft_zone_pos, done_zones, zone_start,
+ zone_end, vol->data1_zone_pos);
+ }
+ ntfs_debug(DEBUG_OTHER, "%s(): After outer while loop.\n",
+ __FUNCTION__);
+done_ret:
+ ntfs_debug(DEBUG_OTHER, "%s(): At done_ret.\n", __FUNCTION__);
+ rl2[rlpos].lcn = (ntfs_cluster_t)-1;
+ rl2[rlpos].len = (ntfs_cluster_t)0;
+ *rl = rl2;
+ *rl_len = rlpos;
+ if (need_writeback) {
+ ntfs_debug(DEBUG_OTHER, "%s(): Writing back.\n", __FUNCTION__);
+ need_writeback = 0;
+ io.param = buf;
+ io.do_read = 0;
+ err = ntfs_readwrite_attr(vol->bitmap, data, last_read_pos,
+ &io);
+ if (err) {
+ ntfs_error("%s(): Bitmap writeback failed in done "
+ "code path with error code %i.\n",
+ __FUNCTION__, -err);
+ goto err_ret;
+ }
+ ntfs_debug(DEBUG_OTHER, "%s(): Wrote 0x%Lx bytes.\n",
+ __FUNCTION__, io.size);
+ }
+done_fail_ret:
+ ntfs_debug(DEBUG_OTHER, "%s(): At done_fail_ret (follows done_ret).\n",
+ __FUNCTION__);
+ unlock_kernel();
+ free_page((unsigned long)buf);
+ if (err)
+ ntfs_debug(DEBUG_FILE3, "%s(): Failed to allocate "
+ "clusters. Returning with error code %i.\n",
+ __FUNCTION__, -err);
+ ntfs_debug(DEBUG_OTHER, "%s(): Syncing $Bitmap inode.\n", __FUNCTION__);
+ if (ntfs_update_inode(vol->bitmap))
+ ntfs_error("%s(): Failed to sync inode $Bitmap. "
+ "Continuing anyway.\n", __FUNCTION__);
+ ntfs_debug(DEBUG_OTHER, "%s(): Returning with code %i.\n", __FUNCTION__,
+ err);
+ return err;
+fail_ret:
+ ntfs_debug(DEBUG_OTHER, "%s(): At fail_ret.\n", __FUNCTION__);
+ if (rl2) {
+ if (err == -ENOSPC) {
+ /* Return first free lcn and count of free clusters. */
+ *location = rl2[0].lcn;
+ *count -= clusters;
+ ntfs_debug(DEBUG_OTHER, "%s(): err = -ENOSPC, "
+ "*location = 0x%x, *count = 0x%x.\n",
+ __FUNCTION__, *location, *count);
+ }
+ /* Deallocate all allocated clusters. */
+ ntfs_debug(DEBUG_OTHER, "%s(): Deallocating allocated "
+ "clusters.\n", __FUNCTION__);
+ ntfs_deallocate_clusters(vol, rl2, rlpos);
+ /* Free the runlist. */
+ ntfs_vfree(rl2);
+ } else {
+ if (err == -ENOSPC) {
+ /* Nothing free at all. */
+ *location = vol->data1_zone_pos; /* Irrelevant... */
+ *count = 0;
+ ntfs_debug(DEBUG_OTHER, "%s(): No space left at all, "
+ "err = -ENOSPC, *location = 0x%x, "
+ "*count = 0.\n",
+ __FUNCTION__, *location);
+ }
+ }
+ *rl = NULL;
+ *rl_len = 0;
+ ntfs_debug(DEBUG_OTHER, "%s(): *rl = NULL, *rl_len = 0, "
+ "going to done_fail_ret.\n", __FUNCTION__);
+ goto done_fail_ret;
+wb_err_ret:
+ ntfs_debug(DEBUG_OTHER, "%s(): At wb_err_ret.\n", __FUNCTION__);
+ if (need_writeback) {
+ int __err;
+ ntfs_debug(DEBUG_OTHER, "%s(): Writing back.\n", __FUNCTION__);
+ io.param = buf;
+ io.do_read = 0;
+ __err = ntfs_readwrite_attr(vol->bitmap, data, last_read_pos,
+ &io);
+ if (__err)
+ ntfs_error("%s(): Bitmap writeback failed in error "
+ "code path with error code %i.\n",
+ __FUNCTION__, -__err);
+ need_writeback = 0;
+ }
+err_ret:
+ ntfs_debug(DEBUG_OTHER, "%s(): At err_ret, *location = -1, "
+ "*count = 0, going to fail_ret.\n", __FUNCTION__);
+ *location = -1;
+ *count = 0;
+ goto fail_ret;
+}
+
+/*
+ * IMPORTANT: Caller has to hold big kernel lock or the race monster will come
+ * to get you! (-;
+ * TODO: Need our own lock for bitmap accesses but BKL is more secure for now,
+ * considering we might not have covered all places with a lock yet. In that
+ * case the BKL offers a one way exclusion which is better than no exclusion
+ * at all... (AIA)
+ */
+static int ntfs_clear_bitrange(ntfs_inode *bitmap,
+ const ntfs_cluster_t start_bit, const ntfs_cluster_t count)
+{
+ ntfs_cluster_t buf_size, bit, nr_bits = count;
+ unsigned char *buf, *byte;
+ int err;
+ ntfs_io io;
+
+ io.fn_put = ntfs_put;
+ io.fn_get = ntfs_get;
+ /* Calculate the required buffer size in bytes. */
+ buf_size = (ntfs_cluster_t)((start_bit & 7) + nr_bits + 7) >> 3;
+ if (buf_size <= (ntfs_cluster_t)(64 * 1024))
+ buf = ntfs_malloc(buf_size);
+ else
+ buf = ntfs_vmalloc(buf_size);
+ if (!buf)
+ return -ENOMEM;
+ /* Read the bitmap from the data attribute. */
+ io.param = byte = buf;
+ io.size = buf_size;
+ err = ntfs_read_attr(bitmap, bitmap->vol->at_data, 0, start_bit >> 3,
+ &io);
+ if (err || io.size != buf_size)
+ goto err_out;
+ /* Now clear the bits in the read bitmap. */
+ bit = start_bit & 7;
+ while (bit && nr_bits) { /* Process first partial byte, if present. */
+ *byte &= ~(1 << bit++);
+ nr_bits--;
+ bit &= 7;
+ if (!bit)
+ byte++;
+ }
+ while (nr_bits >= 8) { /* Process full bytes. */
+ *byte = 0;
+ nr_bits -= 8;
+ byte++;
+ }
+ bit = 0;
+ while (nr_bits) { /* Process last partial byte, if present. */
+ *byte &= ~(1 << bit);
+ nr_bits--;
+ bit++;
+ }
+ /* Write the modified bitmap back to disk. */
+ io.param = buf;
+ io.size = buf_size;
+ err = ntfs_write_attr(bitmap, bitmap->vol->at_data, 0, start_bit >> 3,
+ &io);
+err_out:
+ if (buf_size <= (ntfs_cluster_t)(64 * 1024))
+ ntfs_free(buf);
+ else
+ ntfs_vfree(buf);
+ if (!err && io.size != buf_size)
+ err = -EIO;
+ return err;
+}
+
+/*
+ * See comments for lack of zone adjustments below in the description of the
+ * function ntfs_deallocate_clusters().
+ */
+int ntfs_deallocate_cluster_run(const ntfs_volume *vol,
+ const ntfs_cluster_t lcn, const ntfs_cluster_t len)
+{
+ int err;
+
+ lock_kernel();
+ err = ntfs_clear_bitrange(vol->bitmap, lcn, len);
+ unlock_kernel();
+ return err;
+}
+
+/*
+ * This is inefficient, but logically trivial, so will do for now. Note, we
+ * do not touch the mft nor the data zones here because we want to minimize
+ * recycling of clusters to enhance the chances of data being undeleteable.
+ * Also we don't want the overhead. Instead we do one additional sweep of the
+ * current data zone during cluster allocation to check for freed clusters.
+ */
+int ntfs_deallocate_clusters(const ntfs_volume *vol, const ntfs_runlist *rl,
+ const int rl_len)
+{
+ int i, err;
+
+ lock_kernel();
+ for (i = err = 0; i < rl_len && !err; i++)
+ err = ntfs_clear_bitrange(vol->bitmap, rl[i].lcn, rl[i].len);
+ unlock_kernel();
+ return err;
+}
+
diff --git a/fs/ntfs/super.h b/fs/ntfs/super.h
new file mode 100644
index 00000000000000..a974a5f46340c1
--- /dev/null
+++ b/fs/ntfs/super.h
@@ -0,0 +1,32 @@
+/*
+ * super.h - Header file for super.c
+ *
+ * Copyright (C) 1995-1997 Martin von Löwis
+ * Copyright (C) 1996-1997 Régis Duchesne
+ * Copyright (c) 2001 Anton Altaparmakov
+ */
+
+int ntfs_get_free_cluster_count(ntfs_inode *bitmap);
+
+int ntfs_get_volumesize(ntfs_volume *vol, __s64 *vol_size);
+
+int ntfs_init_volume(ntfs_volume *vol, char *boot);
+
+int ntfs_load_special_files(ntfs_volume *vol);
+
+int ntfs_release_volume(ntfs_volume *vol);
+
+int ntfs_insert_fixups(unsigned char *rec, int rec_size);
+
+int ntfs_fixup_record(char *record, char *magic, int size);
+
+int ntfs_allocate_clusters(ntfs_volume *vol, ntfs_cluster_t *location,
+ ntfs_cluster_t *count, ntfs_runlist **rl, int *rl_len,
+ const NTFS_CLUSTER_ALLOCATION_ZONES zone);
+
+int ntfs_deallocate_cluster_run(const ntfs_volume *vol,
+ const ntfs_cluster_t lcn, const ntfs_cluster_t len);
+
+int ntfs_deallocate_clusters(const ntfs_volume *vol, const ntfs_runlist *rl,
+ const int rl_len);
+
diff --git a/fs/ntfs/support.c b/fs/ntfs/support.c
new file mode 100644
index 00000000000000..8cc6f26ec8ebce
--- /dev/null
+++ b/fs/ntfs/support.c
@@ -0,0 +1,316 @@
+/*
+ * support.c - Specific support functions
+ *
+ * Copyright (C) 1997 Martin von Löwis
+ * Copyright (C) 1997 Régis Duchesne
+ * Copyright (C) 2001 Anton Altaparmakov (AIA)
+ */
+
+#include "ntfstypes.h"
+#include "struct.h"
+#include "support.h"
+
+#include <stdarg.h>
+#include <linux/slab.h>
+#include <linux/locks.h>
+#include <linux/fs.h>
+#include "util.h"
+#include "inode.h"
+#include "macros.h"
+#include <linux/nls.h>
+
+static char print_buf[1024];
+
+#ifdef DEBUG
+#include "sysctl.h"
+#include <linux/kernel.h>
+
+/* Debugging output */
+void ntfs_debug(int mask, const char *fmt, ...)
+{
+ va_list ap;
+
+ /* Filter it with the debugging level required */
+ if (ntdebug & mask) {
+ va_start(ap,fmt);
+ strcpy(print_buf, KERN_DEBUG "NTFS: ");
+ vsprintf(print_buf + 9, fmt, ap);
+ printk(print_buf);
+ va_end(ap);
+ }
+}
+
+#ifndef ntfs_malloc
+/* Verbose kmalloc */
+void *ntfs_malloc(int size)
+{
+ void *ret;
+
+ ret = kmalloc(size, GFP_KERNEL);
+ ntfs_debug(DEBUG_MALLOC, "Allocating %x at %p\n", size, ret);
+
+ return ret;
+}
+#endif
+
+#ifndef ntfs_free
+/* Verbose kfree() */
+void ntfs_free(void *block)
+{
+ ntfs_debug(DEBUG_MALLOC, "Freeing memory at %p\n", block);
+ kfree(block);
+}
+#endif
+#else /* End of DEBUG functions. Normal ones below... */
+
+#ifndef ntfs_malloc
+void *ntfs_malloc(int size)
+{
+ return kmalloc(size, GFP_KERNEL);
+}
+#endif
+
+#ifndef ntfs_free
+void ntfs_free(void *block)
+{
+ kfree(block);
+}
+#endif
+#endif /* DEBUG */
+
+void ntfs_bzero(void *s, int n)
+{
+ memset(s, 0, n);
+}
+
+/* These functions deliberately return no value. It is dest, anyway,
+ and not used anywhere in the NTFS code. */
+
+void ntfs_memcpy(void *dest, const void *src, ntfs_size_t n)
+{
+ memcpy(dest, src, n);
+}
+
+void ntfs_memmove(void *dest, const void *src, ntfs_size_t n)
+{
+ memmove(dest, src, n);
+}
+
+/* Warn that an error occurred. */
+void ntfs_error(const char *fmt,...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ strcpy(print_buf, KERN_ERR "NTFS: ");
+ vsprintf(print_buf + 9, fmt, ap);
+ printk(print_buf);
+ va_end(ap);
+}
+
+int ntfs_read_mft_record(ntfs_volume *vol, int mftno, char *buf)
+{
+ int error;
+ ntfs_io io;
+
+ ntfs_debug(DEBUG_OTHER, "read_mft_record 0x%x\n", mftno);
+ if (mftno == FILE_Mft)
+ {
+ ntfs_memcpy(buf, vol->mft, vol->mft_record_size);
+ return 0;
+ }
+ if (!vol->mft_ino)
+ {
+ printk(KERN_ERR "NTFS: mft_ino is NULL. Something is terribly "
+ "wrong here!\n");
+ return -ENODATA;
+ }
+ io.fn_put = ntfs_put;
+ io.fn_get = 0;
+ io.param = buf;
+ io.size = vol->mft_record_size;
+ ntfs_debug(DEBUG_OTHER, "read_mft_record: calling ntfs_read_attr with: "
+ "mftno = 0x%x, vol->mft_record_size_bits = 0x%x, "
+ "mftno << vol->mft_record_size_bits = 0x%Lx\n", mftno,
+ vol->mft_record_size_bits,
+ (__s64)mftno << vol->mft_record_size_bits);
+ error = ntfs_read_attr(vol->mft_ino, vol->at_data, NULL,
+ (__s64)mftno << vol->mft_record_size_bits, &io);
+ if (error || (io.size != vol->mft_record_size)) {
+ ntfs_debug(DEBUG_OTHER, "read_mft_record: read 0x%x failed "
+ "(%d,%d,%d)\n", mftno, error, io.size,
+ vol->mft_record_size);
+ return error ? error : -ENODATA;
+ }
+ ntfs_debug(DEBUG_OTHER, "read_mft_record: finished read 0x%x\n", mftno);
+ if (!ntfs_check_mft_record(vol, buf)) {
+ /* FIXME: This is incomplete behaviour. We might be able to
+ * recover at this stage. ntfs_check_mft_record() is too
+ * conservative at aborting it's operations. It is OK for
+ * now as we just can't handle some on disk structures
+ * this way. (AIA) */
+ printk(KERN_WARNING "NTFS: Invalid MFT record for 0x%x\n", mftno);
+ return -EIO;
+ }
+ ntfs_debug(DEBUG_OTHER, "read_mft_record: Done 0x%x\n", mftno);
+ return 0;
+}
+
+int ntfs_getput_clusters(ntfs_volume *vol, int cluster, ntfs_size_t start_offs,
+ ntfs_io *buf)
+{
+ struct super_block *sb = NTFS_SB(vol);
+ struct buffer_head *bh;
+ int length = buf->size;
+ int error = 0;
+ ntfs_size_t to_copy;
+
+ ntfs_debug(DEBUG_OTHER, "%s_clusters %d %d %d\n",
+ buf->do_read ? "get" : "put", cluster, start_offs, length);
+ to_copy = vol->cluster_size - start_offs;
+ while (length) {
+ if (!(bh = sb_bread(sb, cluster))) {
+ ntfs_debug(DEBUG_OTHER, "%s failed\n",
+ buf->do_read ? "Reading" : "Writing");
+ error = -EIO;
+ goto error_ret;
+ }
+ if (to_copy > length)
+ to_copy = length;
+ lock_buffer(bh);
+ if (buf->do_read) {
+ buf->fn_put(buf, bh->b_data + start_offs, to_copy);
+ unlock_buffer(bh);
+ } else {
+ buf->fn_get(bh->b_data + start_offs, buf, to_copy);
+ mark_buffer_dirty(bh);
+ unlock_buffer(bh);
+ /*
+ * Note: We treat synchronous IO on a per volume basis
+ * disregarding flags of individual inodes. This can
+ * lead to some strange write ordering effects upon a
+ * remount with a change in the sync flag but it should
+ * not break anything. [Except if the system crashes
+ * at that point in time but there would be more thigs
+ * to worry about than that in that case...]. (AIA)
+ */
+ if (sb->s_flags & MS_SYNCHRONOUS) {
+ ll_rw_block(WRITE, 1, &bh);
+ wait_on_buffer(bh);
+ if (buffer_req(bh) && !buffer_uptodate(bh)) {
+ printk(KERN_ERR "IO error syncing NTFS "
+ "cluster [%s:%i]\n",
+ bdevname(sb->s_dev), cluster);
+ brelse(bh);
+ error = -EIO;
+ goto error_ret;
+ }
+ }
+ }
+ brelse(bh);
+ length -= to_copy;
+ start_offs = 0;
+ to_copy = vol->cluster_size;
+ cluster++;
+ }
+error_ret:
+ return error;
+}
+
+ntfs_time64_t ntfs_now(void)
+{
+ return ntfs_unixutc2ntutc(CURRENT_TIME);
+}
+
+int ntfs_dupuni2map(ntfs_volume *vol, ntfs_u16 *in, int in_len, char **out,
+ int *out_len)
+{
+ int i, o, chl, chi;
+ char *result, *buf, charbuf[NLS_MAX_CHARSET_SIZE];
+ struct nls_table *nls = vol->nls_map;
+
+ result = ntfs_malloc(in_len + 1);
+ if (!result)
+ return -ENOMEM;
+ *out_len = in_len;
+ for (i = o = 0; i < in_len; i++) {
+ /* FIXME: Byte order? */
+ wchar_t uni = in[i];
+ if ((chl = nls->uni2char(uni, charbuf,
+ NLS_MAX_CHARSET_SIZE)) > 0) {
+ /* Adjust result buffer. */
+ if (chl > 1) {
+ buf = ntfs_malloc(*out_len + chl);
+ if (!buf) {
+ i = -ENOMEM;
+ goto err_ret;
+ }
+ memcpy(buf, result, o);
+ ntfs_free(result);
+ result = buf;
+ *out_len += (chl - 1);
+ }
+ for (chi = 0; chi < chl; chi++)
+ result[o++] = charbuf[chi];
+ } else {
+ /* Invalid character. */
+ printk(KERN_ERR "NTFS: Unicode name contains a "
+ "character that cannot be converted "
+ "to chosen character set. Remount "
+ "with utf8 encoding and this should "
+ "work.\n");
+ i = -EILSEQ;
+ goto err_ret;
+ }
+ }
+ result[*out_len] = '\0';
+ *out = result;
+ return 0;
+err_ret:
+ ntfs_free(result);
+ *out_len = 0;
+ *out = NULL;
+ return i;
+}
+
+int ntfs_dupmap2uni(ntfs_volume *vol, char* in, int in_len, ntfs_u16 **out,
+ int *out_len)
+{
+ int i, o;
+ ntfs_u16 *result;
+ struct nls_table *nls = vol->nls_map;
+
+ *out = result = ntfs_malloc(2 * in_len);
+ if (!result) {
+ *out_len = 0;
+ return -ENOMEM;
+ }
+ *out_len = in_len;
+ for (i = o = 0; i < in_len; i++, o++) {
+ wchar_t uni;
+ int charlen;
+
+ charlen = nls->char2uni(&in[i], in_len - i, &uni);
+ if (charlen < 0) {
+ i = charlen;
+ goto err_ret;
+ }
+ *out_len -= charlen - 1;
+ i += charlen - 1;
+ /* FIXME: Byte order? */
+ result[o] = uni;
+ if (!result[o]) {
+ i = -EILSEQ;
+ goto err_ret;
+ }
+ }
+ return 0;
+err_ret:
+ printk(KERN_ERR "NTFS: Name contains a character that cannot be "
+ "converted to Unicode.\n");
+ ntfs_free(result);
+ *out_len = 0;
+ *out = NULL;
+ return i;
+}
+
diff --git a/fs/ntfs/support.h b/fs/ntfs/support.h
new file mode 100644
index 00000000000000..0d91462fe4a00c
--- /dev/null
+++ b/fs/ntfs/support.h
@@ -0,0 +1,89 @@
+/*
+ * support.h - Header file for specific support.c
+ *
+ * Copyright (C) 1997 Régis Duchesne
+ * Copyright (c) 2001 Anton Altaparmakov (AIA)
+ */
+
+/* Debug levels */
+#define DEBUG_OTHER 1
+#define DEBUG_MALLOC 2
+#define DEBUG_BSD 4
+#define DEBUG_LINUX 8
+#define DEBUG_DIR1 16
+#define DEBUG_DIR2 32
+#define DEBUG_DIR3 64
+#define DEBUG_FILE1 128
+#define DEBUG_FILE2 256
+#define DEBUG_FILE3 512
+#define DEBUG_NAME1 1024
+#define DEBUG_NAME2 2048
+
+#ifdef DEBUG
+void ntfs_debug(int mask, const char *fmt, ...);
+#else
+#define ntfs_debug(mask, fmt...) do {} while (0)
+#endif
+
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#define ntfs_malloc(size) kmalloc(size, GFP_KERNEL)
+
+#define ntfs_free(ptr) kfree(ptr)
+
+/**
+ * ntfs_vmalloc - allocate memory in multiples of pages
+ * @size number of bytes to allocate
+ *
+ * Allocates @size bytes of memory, rounded up to multiples of PAGE_SIZE and
+ * returns a pointer to the allocated memory.
+ *
+ * If there was insufficient memory to complete the request, return NULL.
+ */
+static inline void *ntfs_vmalloc(unsigned long size)
+{
+ if (size <= PAGE_SIZE) {
+ if (size) {
+ /* kmalloc() has per-CPU caches so if faster for now. */
+ return kmalloc(PAGE_SIZE, GFP_NOFS);
+ /* return (void *)__get_free_page(GFP_NOFS |
+ __GFP_HIGHMEM); */
+ }
+ BUG();
+ }
+ if (size >> PAGE_SHIFT < num_physpages)
+ return __vmalloc(size, GFP_NOFS | __GFP_HIGHMEM, PAGE_KERNEL);
+ return NULL;
+}
+
+static inline void ntfs_vfree(void *addr)
+{
+ if ((unsigned long)addr < VMALLOC_START) {
+ return kfree(addr);
+ /* return free_page((unsigned long)addr); */
+ }
+ vfree(addr);
+}
+
+void ntfs_bzero(void *s, int n);
+
+void ntfs_memcpy(void *dest, const void *src, ntfs_size_t n);
+
+void ntfs_memmove(void *dest, const void *src, ntfs_size_t n);
+
+void ntfs_error(const char *fmt,...);
+
+int ntfs_read_mft_record(ntfs_volume *vol, int mftno, char *buf);
+
+int ntfs_getput_clusters(ntfs_volume *pvol, int cluster, ntfs_size_t offs,
+ ntfs_io *buf);
+
+ntfs_time64_t ntfs_now(void);
+
+int ntfs_dupuni2map(ntfs_volume *vol, ntfs_u16 *in, int in_len, char **out,
+ int *out_len);
+
+int ntfs_dupmap2uni(ntfs_volume *vol, char* in, int in_len, ntfs_u16 **out,
+ int *out_len);
+
diff --git a/fs/ntfs/sysctl.c b/fs/ntfs/sysctl.c
new file mode 100644
index 00000000000000..8d5807d5da31ed
--- /dev/null
+++ b/fs/ntfs/sysctl.c
@@ -0,0 +1,55 @@
+/*
+ * sysctl.c - System control stuff
+ *
+ * Copyright (C) 1997 Martin von Löwis
+ * Copyright (C) 1997 Régis Duchesne
+ */
+
+#include "sysctl.h"
+
+#ifdef DEBUG
+#include <linux/locks.h>
+#include <linux/sysctl.h>
+
+int ntdebug = 0;
+
+/* Add or remove the debug sysctl
+ * Is this really the only file system with sysctls ?
+ */
+void ntfs_sysctl(int add)
+{
+#define FS_NTFS 1
+ /* Definition of the sysctl */
+ static ctl_table ntfs_sysctls[]={
+ {FS_NTFS, /* ID */
+ "ntfs-debug", /* name in /proc */
+ &ntdebug,sizeof(ntdebug), /* data ptr, data size */
+ 0644, /* mode */
+ 0, /* child */
+ proc_dointvec, /* proc handler */
+ 0, /* strategy */
+ 0, /* proc control block */
+ 0,0}, /* extra */
+ {0}
+ };
+ /* Define the parent file : /proc/sys/fs */
+ static ctl_table sysctls_root[]={
+ {CTL_FS,
+ "fs",
+ NULL,0,
+ 0555,
+ ntfs_sysctls},
+ {0}
+ };
+ static struct ctl_table_header *sysctls_root_header = NULL;
+
+ if(add){
+ if(!sysctls_root_header)
+ sysctls_root_header = register_sysctl_table(sysctls_root, 0);
+ } else if(sysctls_root_header) {
+ unregister_sysctl_table(sysctls_root_header);
+ sysctls_root_header = NULL;
+ }
+}
+#endif /* DEBUG */
+
diff --git a/fs/ntfs/sysctl.h b/fs/ntfs/sysctl.h
new file mode 100644
index 00000000000000..e10481b58d21b3
--- /dev/null
+++ b/fs/ntfs/sysctl.h
@@ -0,0 +1,17 @@
+/*
+ * sysctl.h - Header file for sysctl.c
+ *
+ * Copyright (C) 1997 Martin von Löwis
+ * Copyright (C) 1997 Régis Duchesne
+ */
+
+#ifdef DEBUG
+ extern int ntdebug;
+
+ void ntfs_sysctl(int add);
+
+ #define SYSCTL(x) ntfs_sysctl(x)
+#else
+ #define SYSCTL(x)
+#endif /* DEBUG */
+
diff --git a/fs/ntfs/unistr.c b/fs/ntfs/unistr.c
new file mode 100644
index 00000000000000..2f32a419b14554
--- /dev/null
+++ b/fs/ntfs/unistr.c
@@ -0,0 +1,167 @@
+/*
+ * unistr.c - Unicode string handling. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2000,2001 Anton Altaparmakov.
+ *
+ * This program/include file 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program/include file 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 this program (in the main directory of the Linux-NTFS
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/string.h>
+#include <asm/byteorder.h>
+
+#include "unistr.h"
+#include "macros.h"
+
+/*
+ * This is used by the name collation functions to quickly determine what
+ * characters are (in)valid.
+ */
+const __u8 legal_ansi_char_array[0x40] = {
+ 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
+
+ 0x17, 0x07, 0x18, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x18, 0x16, 0x16, 0x17, 0x07, 0x00,
+
+ 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17, 0x17,
+ 0x17, 0x17, 0x04, 0x16, 0x18, 0x16, 0x18, 0x18,
+};
+
+/**
+ * ntfs_are_names_equal - compare two Unicode names for equality
+ * @s1: name to compare to @s2
+ * @s1_len: length in Unicode characters of @s1
+ * @s2: name to compare to @s1
+ * @s2_len: length in Unicode characters of @s2
+ * @ic: ignore case bool
+ * @upcase: upcase table (only if @ic == IGNORE_CASE)
+ * @upcase_size: length in Unicode characters of @upcase (if present)
+ *
+ * Compare the names @s1 and @s2 and return TRUE (1) if the names are
+ * identical, or FALSE (0) if they are not identical. If @ic is IGNORE_CASE,
+ * the @upcase table is used to performa a case insensitive comparison.
+ */
+int ntfs_are_names_equal(wchar_t *s1, size_t s1_len,
+ wchar_t *s2, size_t s2_len, int ic,
+ wchar_t *upcase, __u32 upcase_size)
+{
+ if (s1_len != s2_len)
+ return 0;
+ if (!ic)
+ return memcmp(s1, s2, s1_len << 1) ? 0: 1;
+ return ntfs_wcsncasecmp(s1, s2, s1_len, upcase, upcase_size) ? 0: 1;
+}
+
+/**
+ * ntfs_collate_names - collate two Unicode names
+ * @upcase: upcase table (ignored if @ic is CASE_SENSITIVE)
+ * @upcase_len: upcase table size (ignored if @ic is CASE_SENSITIVE)
+ * @name1: first Unicode name to compare
+ * @name2: second Unicode name to compare
+ * @ic: either CASE_SENSITIVE or IGNORE_CASE
+ * @err_val: if @name1 contains an invalid character return this value
+ *
+ * ntfs_collate_names collates two Unicode names and returns:
+ *
+ * -1 if the first name collates before the second one,
+ * 0 if the names match,
+ * 1 if the second name collates before the first one, or
+ * @ec if an invalid character is encountered in @name1 during the comparison.
+ *
+ * The following characters are considered invalid: '"', '*', '<', '>' and '?'.
+ */
+int ntfs_collate_names(wchar_t *upcase, __u32 upcase_len,
+ wchar_t *name1, __u32 name1_len,
+ wchar_t *name2, __u32 name2_len,
+ int ic, int err_val)
+{
+ __u32 cnt, min_len;
+ wchar_t c1, c2;
+
+ min_len = name1_len;
+ if (min_len > name2_len)
+ min_len = name2_len;
+ for (cnt = 0; cnt < min_len; ++cnt) {
+ c1 = le16_to_cpu(*name1++);
+ c2 = le16_to_cpu(*name2++);
+ if (ic) {
+ if (c1 < upcase_len)
+ c1 = le16_to_cpu(upcase[c1]);
+ if (c2 < upcase_len)
+ c2 = le16_to_cpu(upcase[c2]);
+ }
+ if (c1 < 64 && legal_ansi_char_array[c1] & 8)
+ return err_val;
+ if (c1 < c2)
+ return -1;
+ if (c1 > c2)
+ return 1;
+ ++name1;
+ ++name2;
+ }
+ if (name1_len < name2_len)
+ return -1;
+ if (name1_len == name2_len)
+ return 0;
+ /* name1_len > name2_len */
+ c1 = le16_to_cpu(*name1);
+ if (c1 < 64 && legal_ansi_char_array[c1] & 8)
+ return err_val;
+ return 1;
+}
+
+/**
+ * ntfs_wcsncasecmp - compare two little endian Unicode strings, ignoring case
+ * @s1: first string
+ * @s2: second string
+ * @n: maximum unicode characters to compare
+ * @upcase: upcase table
+ * @upcase_size: upcase table size in Unicode characters
+ *
+ * Compare the first @n characters of the Unicode strings @s1 and @s2,
+ * ignoring case. The strings in little endian format and appropriate
+ * le16_to_cpu() conversion is performed on non-little endian machines.
+ *
+ * Each character is uppercased using the @upcase table before the comparison.
+ *
+ * The function returns an integer less than, equal to, or greater than zero
+ * if @s1 (or the first @n Unicode characters thereof) is found, respectively,
+ * to be less than, to match, or be greater than @s2.
+ */
+int ntfs_wcsncasecmp(wchar_t *s1, wchar_t *s2, size_t n,
+ wchar_t *upcase, __u32 upcase_size)
+{
+ wchar_t c1, c2;
+ size_t i;
+
+ for (i = 0; i < n; ++i) {
+ if ((c1 = le16_to_cpu(s1[i])) < upcase_size)
+ c1 = le16_to_cpu(upcase[c1]);
+ if ((c2 = le16_to_cpu(s2[i])) < upcase_size)
+ c2 = le16_to_cpu(upcase[c2]);
+ if (c1 < c2)
+ return -1;
+ if (c1 > c2)
+ return 1;
+ if (!c1)
+ break;
+ }
+ return 0;
+}
+
diff --git a/fs/ntfs/unistr.h b/fs/ntfs/unistr.h
new file mode 100644
index 00000000000000..8b546d1378655e
--- /dev/null
+++ b/fs/ntfs/unistr.h
@@ -0,0 +1,44 @@
+/*
+ * unistr.h - Exports for unicode string handling. Part of the Linux-NTFS
+ * project.
+ *
+ * Copyright (c) 2000,2001 Anton Altaparmakov.
+ *
+ * This program/include file 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program/include file 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 this program (in the main directory of the Linux-NTFS
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _LINUX_NTFS_UNISTR_H
+#define _LINUX_NTFS_UNISTR_H
+
+#include <linux/types.h>
+#include <linux/nls.h>
+
+extern const __u8 legal_ansi_char_array[0x40];
+
+int ntfs_are_names_equal(wchar_t *s1, size_t s1_len,
+ wchar_t *s2, size_t s2_len, int ic,
+ wchar_t *upcase, __u32 upcase_size);
+
+int ntfs_collate_names(wchar_t *upcase, __u32 upcase_len,
+ wchar_t *name1, __u32 name1_len,
+ wchar_t *name2, __u32 name2_len,
+ int ic, int err_val);
+
+int ntfs_wcsncasecmp(wchar_t *s1, wchar_t *s2, size_t n,
+ wchar_t *upcase, __u32 upcase_size);
+
+#endif /* defined _LINUX_NTFS_UNISTR_H */
+
diff --git a/fs/ntfs/util.c b/fs/ntfs/util.c
new file mode 100644
index 00000000000000..c8e18b5540dc87
--- /dev/null
+++ b/fs/ntfs/util.c
@@ -0,0 +1,265 @@
+/*
+ * util.c - Miscellaneous support
+ *
+ * Copyright (C) 1997,1999 Martin von Löwis
+ * Copyright (C) 1997 Régis Duchesne
+ * Copyright (C) 2001 Anton Altaparmakov (AIA)
+ *
+ * The utf8 routines are copied from Python wstrop module.
+ */
+
+#include "ntfstypes.h"
+#include "struct.h"
+#include "util.h"
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <asm/div64.h> /* For do_div(). */
+#include "support.h"
+
+/*
+ * Converts a single wide character to a sequence of utf8 bytes.
+ * The character is represented in host byte order.
+ * Returns the number of bytes, or 0 on error.
+ */
+static int to_utf8(ntfs_u16 c, unsigned char *buf)
+{
+ if (c == 0)
+ return 0; /* No support for embedded 0 runes. */
+ if (c < 0x80) {
+ if (buf)
+ buf[0] = (unsigned char)c;
+ return 1;
+ }
+ if (c < 0x800) {
+ if (buf) {
+ buf[0] = 0xc0 | (c >> 6);
+ buf[1] = 0x80 | (c & 0x3f);
+ }
+ return 2;
+ }
+ /* c < 0x10000 */
+ if (buf) {
+ buf[0] = 0xe0 | (c >> 12);
+ buf[1] = 0x80 | ((c >> 6) & 0x3f);
+ buf[2] = 0x80 | (c & 0x3f);
+ }
+ return 3;
+}
+
+/*
+ * Decodes a sequence of utf8 bytes into a single wide character.
+ * The character is returned in host byte order.
+ * Returns the number of bytes consumed, or 0 on error.
+ */
+static int from_utf8(const unsigned char *str, ntfs_u16 *c)
+{
+ int l = 0, i;
+
+ if (*str < 0x80) {
+ *c = *str;
+ return 1;
+ }
+ if (*str < 0xc0) /* Lead byte must not be 10xxxxxx. */
+ return 0; /* Is c0 a possible lead byte? */
+ if (*str < 0xe0) { /* 110xxxxx */
+ *c = *str & 0x1f;
+ l = 2;
+ } else if (*str < 0xf0) { /* 1110xxxx */
+ *c = *str & 0xf;
+ l = 3;
+ } else if (*str < 0xf8) { /* 11110xxx */
+ *c = *str & 7;
+ l = 4;
+ } else /* We don't support characters above 0xFFFF in NTFS. */
+ return 0;
+ for (i = 1; i < l; i++) {
+ /* All other bytes must be 10xxxxxx. */
+ if ((str[i] & 0xc0) != 0x80)
+ return 0;
+ *c <<= 6;
+ *c |= str[i] & 0x3f;
+ }
+ return l;
+}
+
+/*
+ * Converts wide string to UTF-8. Expects two in- and two out-parameters.
+ * Returns 0 on success, or error code.
+ * The caller has to free the result string.
+ */
+static int ntfs_dupuni2utf8(ntfs_u16 *in, int in_len, char **out, int *out_len)
+{
+ int i, tmp;
+ int len8;
+ unsigned char *result;
+
+ ntfs_debug(DEBUG_NAME1, "converting l = %d\n", in_len);
+ /* Count the length of the resulting UTF-8. */
+ for (i = len8 = 0; i < in_len; i++) {
+ tmp = to_utf8(NTFS_GETU16(in + i), 0);
+ if (!tmp)
+ /* Invalid character. */
+ return -EILSEQ;
+ len8 += tmp;
+ }
+ *out = result = ntfs_malloc(len8 + 1); /* allow for zero-termination */
+ if (!result)
+ return -ENOMEM;
+ result[len8] = '\0';
+ *out_len = len8;
+ for (i = len8 = 0; i < in_len; i++)
+ len8 += to_utf8(NTFS_GETU16(in + i), result + len8);
+ ntfs_debug(DEBUG_NAME1, "result %p:%s\n", result, result);
+ return 0;
+}
+
+/*
+ * Converts an UTF-8 sequence to a wide string. Same conventions as the
+ * previous function.
+ */
+static int ntfs_duputf82uni(unsigned char* in, int in_len, ntfs_u16** out,
+ int *out_len)
+{
+ int i, tmp;
+ int len16;
+ ntfs_u16* result;
+ ntfs_u16 wtmp;
+
+ for (i = len16 = 0; i < in_len; i += tmp, len16++) {
+ tmp = from_utf8(in + i, &wtmp);
+ if (!tmp)
+ return -EILSEQ;
+ }
+ *out = result = ntfs_malloc(2 * (len16 + 1));
+ if (!result)
+ return -ENOMEM;
+ result[len16] = 0;
+ *out_len = len16;
+ for (i = len16 = 0; i < in_len; i += tmp, len16++) {
+ tmp = from_utf8(in + i, &wtmp);
+ NTFS_PUTU16(result + len16, wtmp);
+ }
+ return 0;
+}
+
+/* Encodings dispatchers. */
+int ntfs_encodeuni(ntfs_volume *vol, ntfs_u16 *in, int in_len, char **out,
+ int *out_len)
+{
+ if (vol->nls_map)
+ return ntfs_dupuni2map(vol, in, in_len, out, out_len);
+ else
+ return ntfs_dupuni2utf8(in, in_len, out, out_len);
+}
+
+int ntfs_decodeuni(ntfs_volume *vol, char *in, int in_len, ntfs_u16 **out,
+ int *out_len)
+{
+ if (vol->nls_map)
+ return ntfs_dupmap2uni(vol, in, in_len, out, out_len);
+ else
+ return ntfs_duputf82uni(in, in_len, out, out_len);
+}
+
+/* Same address space copies. */
+void ntfs_put(ntfs_io *dest, void *src, ntfs_size_t n)
+{
+ ntfs_memcpy(dest->param, src, n);
+ dest->param = (char*)dest->param + n;
+}
+
+void ntfs_get(void* dest, ntfs_io *src, ntfs_size_t n)
+{
+ ntfs_memcpy(dest, src->param, n);
+ src->param = (char*)src->param + n;
+}
+
+void *ntfs_calloc(int size)
+{
+ void *result = ntfs_malloc(size);
+ if (result)
+ ntfs_bzero(result, size);
+ return result;
+}
+
+/* Copy len ascii characters from from to to. :) */
+void ntfs_ascii2uni(short int *to, char *from, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ NTFS_PUTU16(to + i, from[i]);
+ to[i] = 0;
+}
+
+/* strncmp for Unicode strings. */
+int ntfs_uni_strncmp(short int* a, short int *b, int n)
+{
+ int i;
+
+ for(i = 0; i < n; i++)
+ {
+ if (NTFS_GETU16(a + i) < NTFS_GETU16(b + i))
+ return -1;
+ if (NTFS_GETU16(b + i) < NTFS_GETU16(a + i))
+ return 1;
+ if (NTFS_GETU16(a + i) == 0)
+ break;
+ }
+ return 0;
+}
+
+/* strncmp between Unicode and ASCII strings. */
+int ntfs_ua_strncmp(short int* a, char* b, int n)
+{
+ int i;
+
+ for (i = 0; i < n; i++) {
+ if(NTFS_GETU16(a + i) < b[i])
+ return -1;
+ if(b[i] < NTFS_GETU16(a + i))
+ return 1;
+ if (b[i] == 0)
+ return 0;
+ }
+ return 0;
+}
+
+#define NTFS_TIME_OFFSET ((ntfs_time64_t)(369*365 + 89) * 24 * 3600 * 10000000)
+
+/* Convert the NT UTC (based 1.1.1601, in hundred nanosecond units)
+ * into Unix UTC (based 1.1.1970, in seconds). */
+ntfs_time_t ntfs_ntutc2unixutc(ntfs_time64_t ntutc)
+{
+ /* Subtract the NTFS time offset, then convert to 1s intervals. */
+ ntfs_time64_t t = ntutc - NTFS_TIME_OFFSET;
+ do_div(t, 10000000);
+ return (ntfs_time_t)t;
+}
+
+/* Convert the Unix UTC into NT UTC. */
+ntfs_time64_t ntfs_unixutc2ntutc(ntfs_time_t t)
+{
+ /* Convert to 100ns intervals and then add the NTFS time offset. */
+ return (ntfs_time64_t)t * 10000000 + NTFS_TIME_OFFSET;
+}
+
+#undef NTFS_TIME_OFFSET
+
+/* Fill index name. */
+void ntfs_indexname(char *buf, int type)
+{
+ char hex[] = "0123456789ABCDEF";
+ int index;
+ *buf++ = '$';
+ *buf++ = 'I';
+ for (index = 24; index > 0; index -= 4)
+ if ((0xF << index) & type)
+ break;
+ while (index >= 0) {
+ *buf++ = hex[(type >> index) & 0xF];
+ index -= 4;
+ }
+ *buf = '\0';
+}
+
diff --git a/fs/ntfs/util.h b/fs/ntfs/util.h
new file mode 100644
index 00000000000000..5d21bb7fc9ac06
--- /dev/null
+++ b/fs/ntfs/util.h
@@ -0,0 +1,56 @@
+/*
+ * util.h - Header file for util.c
+ *
+ * Copyright (C) 1997 Régis Duchesne
+ * Copyright (C) 2001 Anton Altaparmakov (AIA)
+ */
+
+/* The first 16 inodes correspond to NTFS special files. */
+typedef enum {
+ FILE_Mft = 0,
+ FILE_MftMirr = 1,
+ FILE_LogFile = 2,
+ FILE_Volume = 3,
+ FILE_AttrDef = 4,
+ FILE_root = 5,
+ FILE_BitMap = 6,
+ FILE_Boot = 7,
+ FILE_BadClus = 8,
+ FILE_Secure = 9,
+ FILE_UpCase = 10,
+ FILE_Extend = 11,
+ FILE_Reserved12 = 12,
+ FILE_Reserved13 = 13,
+ FILE_Reserved14 = 14,
+ FILE_Reserved15 = 15,
+} NTFS_SYSTEM_FILES;
+
+/* Memory management */
+void *ntfs_calloc(int size);
+
+/* String operations */
+/* Copy Unicode <-> ASCII */
+void ntfs_ascii2uni(short int *to, char *from, int len);
+
+/* Comparison */
+int ntfs_uni_strncmp(short int* a, short int *b, int n);
+int ntfs_ua_strncmp(short int* a, char* b, int n);
+
+/* Same address space copies */
+void ntfs_put(ntfs_io *dest, void *src, ntfs_size_t n);
+void ntfs_get(void* dest, ntfs_io *src, ntfs_size_t n);
+
+/* Charset conversion */
+int ntfs_encodeuni(ntfs_volume *vol, ntfs_u16 *in, int in_len, char **out,
+ int *out_len);
+int ntfs_decodeuni(ntfs_volume *vol, char *in, int in_len, ntfs_u16 **out,
+ int *out_len);
+
+/* Time conversion */
+/* NT <-> Unix */
+ntfs_time_t ntfs_ntutc2unixutc(ntfs_time64_t ntutc);
+ntfs_time64_t ntfs_unixutc2ntutc(ntfs_time_t t);
+
+/* Attribute names */
+void ntfs_indexname(char *buf, int type);
+
diff --git a/fs/open.c b/fs/open.c
new file mode 100644
index 00000000000000..512b60f58de3ff
--- /dev/null
+++ b/fs/open.c
@@ -0,0 +1,918 @@
+/*
+ * linux/fs/open.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/utime.h>
+#include <linux/file.h>
+#include <linux/smp_lock.h>
+#include <linux/quotaops.h>
+#include <linux/dnotify.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/iobuf.h>
+
+#include <asm/uaccess.h>
+
+#define special_file(m) (S_ISCHR(m)||S_ISBLK(m)||S_ISFIFO(m)||S_ISSOCK(m))
+
+int vfs_statfs(struct super_block *sb, struct statfs *buf)
+{
+ int retval = -ENODEV;
+
+ if (sb) {
+ retval = -ENOSYS;
+ if (sb->s_op && sb->s_op->statfs) {
+ memset(buf, 0, sizeof(struct statfs));
+ lock_kernel();
+ retval = sb->s_op->statfs(sb, buf);
+ unlock_kernel();
+ }
+ }
+ return retval;
+}
+
+
+asmlinkage long sys_statfs(const char * path, struct statfs * buf)
+{
+ struct nameidata nd;
+ int error;
+
+ error = user_path_walk(path, &nd);
+ if (!error) {
+ struct statfs tmp;
+ error = vfs_statfs(nd.dentry->d_inode->i_sb, &tmp);
+ if (!error && copy_to_user(buf, &tmp, sizeof(struct statfs)))
+ error = -EFAULT;
+ path_release(&nd);
+ }
+ return error;
+}
+
+asmlinkage long sys_fstatfs(unsigned int fd, struct statfs * buf)
+{
+ struct file * file;
+ struct statfs tmp;
+ int error;
+
+ error = -EBADF;
+ file = fget(fd);
+ if (!file)
+ goto out;
+ error = vfs_statfs(file->f_dentry->d_inode->i_sb, &tmp);
+ if (!error && copy_to_user(buf, &tmp, sizeof(struct statfs)))
+ error = -EFAULT;
+ fput(file);
+out:
+ return error;
+}
+
+/*
+ * Install a file pointer in the fd array.
+ *
+ * The VFS is full of places where we drop the files lock between
+ * setting the open_fds bitmap and installing the file in the file
+ * array. At any such point, we are vulnerable to a dup2() race
+ * installing a file in the array before us. We need to detect this and
+ * fput() the struct file we are about to overwrite in this case.
+ *
+ * It should never happen - if we allow dup2() do it, _really_ bad things
+ * will follow.
+ */
+
+void fd_install(unsigned int fd, struct file * file)
+{
+ struct files_struct *files = current->files;
+
+ write_lock(&files->file_lock);
+ if (files->fd[fd])
+ BUG();
+ files->fd[fd] = file;
+ write_unlock(&files->file_lock);
+}
+
+int do_truncate(struct dentry *dentry, loff_t length)
+{
+ struct inode *inode = dentry->d_inode;
+ int error;
+ struct iattr newattrs;
+
+ /* Not pretty: "inode->i_size" shouldn't really be signed. But it is. */
+ if (length < 0)
+ return -EINVAL;
+
+ down_write(&inode->i_alloc_sem);
+ down(&inode->i_sem);
+ newattrs.ia_size = length;
+ newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME;
+ error = notify_change(dentry, &newattrs);
+ up(&inode->i_sem);
+ up_write(&inode->i_alloc_sem);
+ return error;
+}
+
+static inline long do_sys_truncate(const char * path, loff_t length)
+{
+ struct nameidata nd;
+ struct inode * inode;
+ int error;
+
+ error = -EINVAL;
+ if (length < 0) /* sorry, but loff_t says... */
+ goto out;
+
+ error = user_path_walk(path, &nd);
+ if (error)
+ goto out;
+ inode = nd.dentry->d_inode;
+
+ /* For directories it's -EISDIR, for other non-regulars - -EINVAL */
+ error = -EISDIR;
+ if (S_ISDIR(inode->i_mode))
+ goto dput_and_out;
+
+ error = -EINVAL;
+ if (!S_ISREG(inode->i_mode))
+ goto dput_and_out;
+
+ error = permission(inode,MAY_WRITE);
+ if (error)
+ goto dput_and_out;
+
+ error = -EROFS;
+ if (IS_RDONLY(inode))
+ goto dput_and_out;
+
+ error = -EPERM;
+ if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
+ goto dput_and_out;
+
+ /*
+ * Make sure that there are no leases.
+ */
+ error = get_lease(inode, FMODE_WRITE);
+ if (error)
+ goto dput_and_out;
+
+ error = get_write_access(inode);
+ if (error)
+ goto dput_and_out;
+
+ error = locks_verify_truncate(inode, NULL, length);
+ if (!error) {
+ DQUOT_INIT(inode);
+ error = do_truncate(nd.dentry, length);
+ }
+ put_write_access(inode);
+
+dput_and_out:
+ path_release(&nd);
+out:
+ return error;
+}
+
+asmlinkage long sys_truncate(const char * path, unsigned long length)
+{
+ /* on 32-bit boxen it will cut the range 2^31--2^32-1 off */
+ return do_sys_truncate(path, (long)length);
+}
+
+static inline long do_sys_ftruncate(unsigned int fd, loff_t length, int small)
+{
+ struct inode * inode;
+ struct dentry *dentry;
+ struct file * file;
+ int error;
+
+ error = -EINVAL;
+ if (length < 0)
+ goto out;
+ error = -EBADF;
+ file = fget(fd);
+ if (!file)
+ goto out;
+
+ /* explicitly opened as large or we are on 64-bit box */
+ if (file->f_flags & O_LARGEFILE)
+ small = 0;
+
+ dentry = file->f_dentry;
+ inode = dentry->d_inode;
+ error = -EINVAL;
+ if (!S_ISREG(inode->i_mode) || !(file->f_mode & FMODE_WRITE))
+ goto out_putf;
+
+ error = -EINVAL;
+ /* Cannot ftruncate over 2^31 bytes without large file support */
+ if (small && length > MAX_NON_LFS)
+ goto out_putf;
+
+ error = -EPERM;
+ if (IS_APPEND(inode))
+ goto out_putf;
+
+ error = locks_verify_truncate(inode, file, length);
+ if (!error)
+ error = do_truncate(dentry, length);
+out_putf:
+ fput(file);
+out:
+ return error;
+}
+
+asmlinkage long sys_ftruncate(unsigned int fd, unsigned long length)
+{
+ return do_sys_ftruncate(fd, length, 1);
+}
+
+/* LFS versions of truncate are only needed on 32 bit machines */
+#if BITS_PER_LONG == 32
+asmlinkage long sys_truncate64(const char * path, loff_t length)
+{
+ return do_sys_truncate(path, length);
+}
+
+asmlinkage long sys_ftruncate64(unsigned int fd, loff_t length)
+{
+ return do_sys_ftruncate(fd, length, 0);
+}
+#endif
+
+#if !(defined(__alpha__) || defined(__ia64__))
+
+/*
+ * sys_utime() can be implemented in user-level using sys_utimes().
+ * Is this for backwards compatibility? If so, why not move it
+ * into the appropriate arch directory (for those architectures that
+ * need it).
+ */
+
+/* If times==NULL, set access and modification to current time,
+ * must be owner or have write permission.
+ * Else, update from *times, must be owner or super user.
+ */
+asmlinkage long sys_utime(char * filename, struct utimbuf * times)
+{
+ int error;
+ struct nameidata nd;
+ struct inode * inode;
+ struct iattr newattrs;
+
+ error = user_path_walk(filename, &nd);
+ if (error)
+ goto out;
+ inode = nd.dentry->d_inode;
+
+ error = -EROFS;
+ if (IS_RDONLY(inode))
+ goto dput_and_out;
+
+ /* Don't worry, the checks are done in inode_change_ok() */
+ newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME;
+ if (times) {
+ error = -EPERM;
+ if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
+ goto dput_and_out;
+ error = get_user(newattrs.ia_atime, &times->actime);
+ if (!error)
+ error = get_user(newattrs.ia_mtime, &times->modtime);
+ if (error)
+ goto dput_and_out;
+
+ newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET;
+ } else {
+ error = -EACCES;
+ if (IS_IMMUTABLE(inode))
+ goto dput_and_out;
+ if (current->fsuid != inode->i_uid &&
+ (error = permission(inode,MAY_WRITE)) != 0)
+ goto dput_and_out;
+ }
+ error = notify_change(nd.dentry, &newattrs);
+dput_and_out:
+ path_release(&nd);
+out:
+ return error;
+}
+
+#endif
+
+/* If times==NULL, set access and modification to current time,
+ * must be owner or have write permission.
+ * Else, update from *times, must be owner or super user.
+ */
+asmlinkage long sys_utimes(char * filename, struct timeval * utimes)
+{
+ int error;
+ struct nameidata nd;
+ struct inode * inode;
+ struct iattr newattrs;
+
+ error = user_path_walk(filename, &nd);
+
+ if (error)
+ goto out;
+ inode = nd.dentry->d_inode;
+
+ error = -EROFS;
+ if (IS_RDONLY(inode))
+ goto dput_and_out;
+
+ /* Don't worry, the checks are done in inode_change_ok() */
+ newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME;
+ if (utimes) {
+ struct timeval times[2];
+ error = -EPERM;
+ if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
+ goto dput_and_out;
+ error = -EFAULT;
+ if (copy_from_user(&times, utimes, sizeof(times)))
+ goto dput_and_out;
+ newattrs.ia_atime = times[0].tv_sec;
+ newattrs.ia_mtime = times[1].tv_sec;
+ newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET;
+ } else {
+ error = -EACCES;
+ if (IS_IMMUTABLE(inode))
+ goto dput_and_out;
+
+ if (current->fsuid != inode->i_uid &&
+ (error = permission(inode,MAY_WRITE)) != 0)
+ goto dput_and_out;
+ }
+ error = notify_change(nd.dentry, &newattrs);
+dput_and_out:
+ path_release(&nd);
+out:
+ return error;
+}
+
+/*
+ * access() needs to use the real uid/gid, not the effective uid/gid.
+ * We do this by temporarily clearing all FS-related capabilities and
+ * switching the fsuid/fsgid around to the real ones.
+ */
+asmlinkage long sys_access(const char * filename, int mode)
+{
+ struct nameidata nd;
+ int old_fsuid, old_fsgid;
+ kernel_cap_t old_cap;
+ int res;
+
+ if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */
+ return -EINVAL;
+
+ old_fsuid = current->fsuid;
+ old_fsgid = current->fsgid;
+ old_cap = current->cap_effective;
+
+ current->fsuid = current->uid;
+ current->fsgid = current->gid;
+
+ /* Clear the capabilities if we switch to a non-root user */
+ if (current->uid)
+ cap_clear(current->cap_effective);
+ else
+ current->cap_effective = current->cap_permitted;
+
+ res = user_path_walk(filename, &nd);
+ if (!res) {
+ res = permission(nd.dentry->d_inode, mode);
+ /* SuS v2 requires we report a read only fs too */
+ if(!res && (mode & S_IWOTH) && IS_RDONLY(nd.dentry->d_inode)
+ && !special_file(nd.dentry->d_inode->i_mode))
+ res = -EROFS;
+ path_release(&nd);
+ }
+
+ current->fsuid = old_fsuid;
+ current->fsgid = old_fsgid;
+ current->cap_effective = old_cap;
+
+ return res;
+}
+
+asmlinkage long sys_chdir(const char * filename)
+{
+ int error;
+ struct nameidata nd;
+
+ error = __user_walk(filename,LOOKUP_POSITIVE|LOOKUP_FOLLOW|LOOKUP_DIRECTORY,&nd);
+ if (error)
+ goto out;
+
+ error = permission(nd.dentry->d_inode,MAY_EXEC);
+ if (error)
+ goto dput_and_out;
+
+ set_fs_pwd(current->fs, nd.mnt, nd.dentry);
+
+dput_and_out:
+ path_release(&nd);
+out:
+ return error;
+}
+
+asmlinkage long sys_fchdir(unsigned int fd)
+{
+ struct file *file;
+ struct dentry *dentry;
+ struct inode *inode;
+ struct vfsmount *mnt;
+ int error;
+
+ error = -EBADF;
+ file = fget(fd);
+ if (!file)
+ goto out;
+
+ dentry = file->f_dentry;
+ mnt = file->f_vfsmnt;
+ inode = dentry->d_inode;
+
+ error = -ENOTDIR;
+ if (!S_ISDIR(inode->i_mode))
+ goto out_putf;
+
+ error = permission(inode, MAY_EXEC);
+ if (!error)
+ set_fs_pwd(current->fs, mnt, dentry);
+out_putf:
+ fput(file);
+out:
+ return error;
+}
+
+asmlinkage long sys_chroot(const char * filename)
+{
+ int error;
+ struct nameidata nd;
+
+ error = __user_walk(filename, LOOKUP_POSITIVE | LOOKUP_FOLLOW |
+ LOOKUP_DIRECTORY | LOOKUP_NOALT, &nd);
+ if (error)
+ goto out;
+
+ error = permission(nd.dentry->d_inode,MAY_EXEC);
+ if (error)
+ goto dput_and_out;
+
+ error = -EPERM;
+ if (!capable(CAP_SYS_CHROOT))
+ goto dput_and_out;
+
+ set_fs_root(current->fs, nd.mnt, nd.dentry);
+ set_fs_altroot();
+ error = 0;
+dput_and_out:
+ path_release(&nd);
+out:
+ return error;
+}
+
+asmlinkage long sys_fchmod(unsigned int fd, mode_t mode)
+{
+ struct inode * inode;
+ struct dentry * dentry;
+ struct file * file;
+ int err = -EBADF;
+ struct iattr newattrs;
+
+ file = fget(fd);
+ if (!file)
+ goto out;
+
+ dentry = file->f_dentry;
+ inode = dentry->d_inode;
+
+ err = -EROFS;
+ if (IS_RDONLY(inode))
+ goto out_putf;
+ err = -EPERM;
+ if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
+ goto out_putf;
+ if (mode == (mode_t) -1)
+ mode = inode->i_mode;
+ newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
+ newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
+ err = notify_change(dentry, &newattrs);
+
+out_putf:
+ fput(file);
+out:
+ return err;
+}
+
+asmlinkage long sys_chmod(const char * filename, mode_t mode)
+{
+ struct nameidata nd;
+ struct inode * inode;
+ int error;
+ struct iattr newattrs;
+
+ error = user_path_walk(filename, &nd);
+ if (error)
+ goto out;
+ inode = nd.dentry->d_inode;
+
+ error = -EROFS;
+ if (IS_RDONLY(inode))
+ goto dput_and_out;
+
+ error = -EPERM;
+ if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
+ goto dput_and_out;
+
+ if (mode == (mode_t) -1)
+ mode = inode->i_mode;
+ newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
+ newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
+ error = notify_change(nd.dentry, &newattrs);
+
+dput_and_out:
+ path_release(&nd);
+out:
+ return error;
+}
+
+static int chown_common(struct dentry * dentry, uid_t user, gid_t group)
+{
+ struct inode * inode;
+ int error;
+ struct iattr newattrs;
+
+ error = -ENOENT;
+ if (!(inode = dentry->d_inode)) {
+ printk(KERN_ERR "chown_common: NULL inode\n");
+ goto out;
+ }
+ error = -EROFS;
+ if (IS_RDONLY(inode))
+ goto out;
+ error = -EPERM;
+ if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
+ goto out;
+ if (user == (uid_t) -1)
+ user = inode->i_uid;
+ if (group == (gid_t) -1)
+ group = inode->i_gid;
+ newattrs.ia_mode = inode->i_mode;
+ newattrs.ia_uid = user;
+ newattrs.ia_gid = group;
+ newattrs.ia_valid = ATTR_UID | ATTR_GID | ATTR_CTIME;
+ /*
+ * If the user or group of a non-directory has been changed by a
+ * non-root user, remove the setuid bit.
+ * 19981026 David C Niemi <niemi@tux.org>
+ *
+ * Changed this to apply to all users, including root, to avoid
+ * some races. This is the behavior we had in 2.0. The check for
+ * non-root was definitely wrong for 2.2 anyway, as it should
+ * have been using CAP_FSETID rather than fsuid -- 19990830 SD.
+ */
+ if ((inode->i_mode & S_ISUID) == S_ISUID &&
+ !S_ISDIR(inode->i_mode))
+ {
+ newattrs.ia_mode &= ~S_ISUID;
+ newattrs.ia_valid |= ATTR_MODE;
+ }
+ /*
+ * Likewise, if the user or group of a non-directory has been changed
+ * by a non-root user, remove the setgid bit UNLESS there is no group
+ * execute bit (this would be a file marked for mandatory locking).
+ * 19981026 David C Niemi <niemi@tux.org>
+ *
+ * Removed the fsuid check (see the comment above) -- 19990830 SD.
+ */
+ if (((inode->i_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP))
+ && !S_ISDIR(inode->i_mode))
+ {
+ newattrs.ia_mode &= ~S_ISGID;
+ newattrs.ia_valid |= ATTR_MODE;
+ }
+ error = notify_change(dentry, &newattrs);
+out:
+ return error;
+}
+
+asmlinkage long sys_chown(const char * filename, uid_t user, gid_t group)
+{
+ struct nameidata nd;
+ int error;
+
+ error = user_path_walk(filename, &nd);
+ if (!error) {
+ error = chown_common(nd.dentry, user, group);
+ path_release(&nd);
+ }
+ return error;
+}
+
+asmlinkage long sys_lchown(const char * filename, uid_t user, gid_t group)
+{
+ struct nameidata nd;
+ int error;
+
+ error = user_path_walk_link(filename, &nd);
+ if (!error) {
+ error = chown_common(nd.dentry, user, group);
+ path_release(&nd);
+ }
+ return error;
+}
+
+
+asmlinkage long sys_fchown(unsigned int fd, uid_t user, gid_t group)
+{
+ struct file * file;
+ int error = -EBADF;
+
+ file = fget(fd);
+ if (file) {
+ error = chown_common(file->f_dentry, user, group);
+ fput(file);
+ }
+ return error;
+}
+
+/*
+ * Note that while the flag value (low two bits) for sys_open means:
+ * 00 - read-only
+ * 01 - write-only
+ * 10 - read-write
+ * 11 - special
+ * it is changed into
+ * 00 - no permissions needed
+ * 01 - read-permission
+ * 10 - write-permission
+ * 11 - read-write
+ * for the internal routines (ie open_namei()/follow_link() etc). 00 is
+ * used by symlinks.
+ */
+struct file *filp_open(const char * filename, int flags, int mode)
+{
+ int namei_flags, error;
+ struct nameidata nd;
+
+ namei_flags = flags;
+ if ((namei_flags+1) & O_ACCMODE)
+ namei_flags++;
+ if (namei_flags & O_TRUNC)
+ namei_flags |= 2;
+
+ error = open_namei(filename, namei_flags, mode, &nd);
+ if (!error)
+ return dentry_open(nd.dentry, nd.mnt, flags);
+
+ return ERR_PTR(error);
+}
+
+struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags)
+{
+ struct file * f;
+ struct inode *inode;
+ static LIST_HEAD(kill_list);
+ int error;
+
+ error = -ENFILE;
+ f = get_empty_filp();
+ if (!f)
+ goto cleanup_dentry;
+ f->f_flags = flags;
+ f->f_mode = (flags+1) & O_ACCMODE;
+ inode = dentry->d_inode;
+ if (f->f_mode & FMODE_WRITE) {
+ error = get_write_access(inode);
+ if (error)
+ goto cleanup_file;
+ }
+
+ f->f_dentry = dentry;
+ f->f_vfsmnt = mnt;
+ f->f_pos = 0;
+ f->f_reada = 0;
+ f->f_op = fops_get(inode->i_fop);
+ file_move(f, &inode->i_sb->s_files);
+
+ /* preallocate kiobuf for O_DIRECT */
+ f->f_iobuf = NULL;
+ f->f_iobuf_lock = 0;
+ if (f->f_flags & O_DIRECT) {
+ error = alloc_kiovec(1, &f->f_iobuf);
+ if (error)
+ goto cleanup_all;
+ }
+
+ if (f->f_op && f->f_op->open) {
+ error = f->f_op->open(inode,f);
+ if (error)
+ goto cleanup_all;
+ }
+ f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
+
+ return f;
+
+cleanup_all:
+ if (f->f_iobuf)
+ free_kiovec(1, &f->f_iobuf);
+ fops_put(f->f_op);
+ if (f->f_mode & FMODE_WRITE)
+ put_write_access(inode);
+ file_move(f, &kill_list); /* out of the way.. */
+ f->f_dentry = NULL;
+ f->f_vfsmnt = NULL;
+cleanup_file:
+ put_filp(f);
+cleanup_dentry:
+ dput(dentry);
+ mntput(mnt);
+ return ERR_PTR(error);
+}
+
+/*
+ * Find an empty file descriptor entry, and mark it busy.
+ */
+int get_unused_fd(void)
+{
+ struct files_struct * files = current->files;
+ int fd, error;
+
+ error = -EMFILE;
+ write_lock(&files->file_lock);
+
+repeat:
+ fd = find_next_zero_bit(files->open_fds,
+ files->max_fdset,
+ files->next_fd);
+
+ /*
+ * N.B. For clone tasks sharing a files structure, this test
+ * will limit the total number of files that can be opened.
+ */
+ if (fd >= current->rlim[RLIMIT_NOFILE].rlim_cur)
+ goto out;
+
+ /* Do we need to expand the fdset array? */
+ if (fd >= files->max_fdset) {
+ error = expand_fdset(files, fd);
+ if (!error) {
+ error = -EMFILE;
+ goto repeat;
+ }
+ goto out;
+ }
+
+ /*
+ * Check whether we need to expand the fd array.
+ */
+ if (fd >= files->max_fds) {
+ error = expand_fd_array(files, fd);
+ if (!error) {
+ error = -EMFILE;
+ goto repeat;
+ }
+ goto out;
+ }
+
+ FD_SET(fd, files->open_fds);
+ FD_CLR(fd, files->close_on_exec);
+ files->next_fd = fd + 1;
+#if 1
+ /* Sanity check */
+ if (files->fd[fd] != NULL) {
+ printk(KERN_WARNING "get_unused_fd: slot %d not NULL!\n", fd);
+ files->fd[fd] = NULL;
+ }
+#endif
+ error = fd;
+
+out:
+ write_unlock(&files->file_lock);
+ return error;
+}
+
+asmlinkage long sys_open(const char * filename, int flags, int mode)
+{
+ char * tmp;
+ int fd, error;
+
+#if BITS_PER_LONG != 32
+ flags |= O_LARGEFILE;
+#endif
+ tmp = getname(filename);
+ fd = PTR_ERR(tmp);
+ if (!IS_ERR(tmp)) {
+ fd = get_unused_fd();
+ if (fd >= 0) {
+ struct file *f = filp_open(tmp, flags, mode);
+ error = PTR_ERR(f);
+ if (IS_ERR(f))
+ goto out_error;
+ fd_install(fd, f);
+ }
+out:
+ putname(tmp);
+ }
+ return fd;
+
+out_error:
+ put_unused_fd(fd);
+ fd = error;
+ goto out;
+}
+
+#ifndef __alpha__
+
+/*
+ * For backward compatibility? Maybe this should be moved
+ * into arch/i386 instead?
+ */
+asmlinkage long sys_creat(const char * pathname, int mode)
+{
+ return sys_open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode);
+}
+
+#endif
+
+/*
+ * "id" is the POSIX thread ID. We use the
+ * files pointer for this..
+ */
+int filp_close(struct file *filp, fl_owner_t id)
+{
+ int retval;
+
+ if (!file_count(filp)) {
+ printk(KERN_ERR "VFS: Close: file count is 0\n");
+ return 0;
+ }
+ retval = 0;
+ if (filp->f_op && filp->f_op->flush) {
+ lock_kernel();
+ retval = filp->f_op->flush(filp);
+ unlock_kernel();
+ }
+ dnotify_flush(filp, id);
+ locks_remove_posix(filp, id);
+ fput(filp);
+ return retval;
+}
+
+/*
+ * Careful here! We test whether the file pointer is NULL before
+ * releasing the fd. This ensures that one clone task can't release
+ * an fd while another clone is opening it.
+ */
+asmlinkage long sys_close(unsigned int fd)
+{
+ struct file * filp;
+ struct files_struct *files = current->files;
+
+ write_lock(&files->file_lock);
+ if (fd >= files->max_fds)
+ goto out_unlock;
+ filp = files->fd[fd];
+ if (!filp)
+ goto out_unlock;
+ files->fd[fd] = NULL;
+ FD_CLR(fd, files->close_on_exec);
+ __put_unused_fd(files, fd);
+ write_unlock(&files->file_lock);
+ return filp_close(filp, files);
+
+out_unlock:
+ write_unlock(&files->file_lock);
+ return -EBADF;
+}
+
+/*
+ * This routine simulates a hangup on the tty, to arrange that users
+ * are given clean terminals at login time.
+ */
+asmlinkage long sys_vhangup(void)
+{
+ if (capable(CAP_SYS_TTY_CONFIG)) {
+ tty_vhangup(current->tty);
+ return 0;
+ }
+ return -EPERM;
+}
+
+/*
+ * Called when an inode is about to be open.
+ * We use this to disallow opening RW large files on 32bit systems if
+ * the caller didn't specify O_LARGEFILE. On 64bit systems we force
+ * on this flag in sys_open.
+ */
+int generic_file_open(struct inode * inode, struct file * filp)
+{
+ if (!(filp->f_flags & O_LARGEFILE) && inode->i_size > MAX_NON_LFS)
+ return -EFBIG;
+ return 0;
+}
+
+EXPORT_SYMBOL(generic_file_open);
diff --git a/fs/openpromfs/Makefile b/fs/openpromfs/Makefile
new file mode 100644
index 00000000000000..f9d8cd339c3ad1
--- /dev/null
+++ b/fs/openpromfs/Makefile
@@ -0,0 +1,15 @@
+#
+# Makefile for the Linux minix filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile.
+
+O_TARGET := openpromfs.o
+
+obj-y := inode.o
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/openpromfs/inode.c b/fs/openpromfs/inode.c
new file mode 100644
index 00000000000000..8822345b78b70a
--- /dev/null
+++ b/fs/openpromfs/inode.c
@@ -0,0 +1,1063 @@
+/* $Id: inode.c,v 1.15 2001/11/12 09:43:39 davem Exp $
+ * openpromfs.c: /proc/openprom handling routines
+ *
+ * Copyright (C) 1996-1999 Jakub Jelinek (jakub@redhat.com)
+ * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be)
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/openprom_fs.h>
+#include <linux/locks.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+#include <asm/uaccess.h>
+
+#define ALIASES_NNODES 64
+
+typedef struct {
+ u16 parent;
+ u16 next;
+ u16 child;
+ u16 first_prop;
+ u32 node;
+} openpromfs_node;
+
+typedef struct {
+#define OPP_STRING 0x10
+#define OPP_STRINGLIST 0x20
+#define OPP_BINARY 0x40
+#define OPP_HEXSTRING 0x80
+#define OPP_DIRTY 0x01
+#define OPP_QUOTED 0x02
+#define OPP_NOTQUOTED 0x04
+#define OPP_ASCIIZ 0x08
+ u32 flag;
+ u32 alloclen;
+ u32 len;
+ char *value;
+ char name[8];
+} openprom_property;
+
+static openpromfs_node *nodes = NULL;
+static int alloced = 0;
+static u16 last_node = 0;
+static u16 first_prop = 0;
+static u16 options = 0xffff;
+static u16 aliases = 0xffff;
+static int aliases_nodes = 0;
+static char *alias_names [ALIASES_NNODES];
+
+#define OPENPROM_ROOT_INO 16
+#define OPENPROM_FIRST_INO OPENPROM_ROOT_INO
+#define NODE(ino) nodes[ino - OPENPROM_FIRST_INO]
+#define NODE2INO(node) (node + OPENPROM_FIRST_INO)
+#define NODEP2INO(no) (no + OPENPROM_FIRST_INO + last_node)
+
+static int openpromfs_create (struct inode *, struct dentry *, int);
+static int openpromfs_readdir(struct file *, void *, filldir_t);
+static struct dentry *openpromfs_lookup(struct inode *, struct dentry *dentry);
+static int openpromfs_unlink (struct inode *, struct dentry *dentry);
+
+static ssize_t nodenum_read(struct file *file, char *buf,
+ size_t count, loff_t *ppos)
+{
+ struct inode *inode = file->f_dentry->d_inode;
+ loff_t pos = *ppos;
+ char buffer[10];
+
+ if (count < 0 || !inode->u.generic_ip)
+ return -EINVAL;
+ sprintf (buffer, "%8.8x\n", (u32)(long)(inode->u.generic_ip));
+ if (pos != (unsigned)pos || pos >= 9)
+ return 0;
+ if (count > 9 - pos)
+ count = 9 - pos;
+ copy_to_user(buf, buffer + pos, count);
+ *ppos = pos + count;
+ return count;
+}
+
+static ssize_t property_read(struct file *filp, char *buf,
+ size_t count, loff_t *ppos)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ loff_t pos = *ppos;
+ int i, j, k;
+ u32 node;
+ char *p, *s;
+ u32 *q;
+ openprom_property *op;
+ char buffer[64];
+
+ if (pos < 0 || pos >= 0xffffff)
+ return -EINVAL;
+ if (!filp->private_data) {
+ node = nodes[(u16)((long)inode->u.generic_ip)].node;
+ i = ((u32)(long)inode->u.generic_ip) >> 16;
+ if ((u16)((long)inode->u.generic_ip) == aliases) {
+ if (i >= aliases_nodes)
+ p = 0;
+ else
+ p = alias_names [i];
+ } else
+ for (p = prom_firstprop (node, buffer);
+ i && p && *p;
+ p = prom_nextprop (node, p, buffer), i--)
+ /* nothing */ ;
+ if (!p || !*p)
+ return -EIO;
+ i = prom_getproplen (node, p);
+ if (i < 0) {
+ if ((u16)((long)inode->u.generic_ip) == aliases)
+ i = 0;
+ else
+ return -EIO;
+ }
+ k = i;
+ if (i < 64) i = 64;
+ filp->private_data = kmalloc (sizeof (openprom_property)
+ + (j = strlen (p)) + 2 * i,
+ GFP_KERNEL);
+ if (!filp->private_data)
+ return -ENOMEM;
+ op = (openprom_property *)filp->private_data;
+ op->flag = 0;
+ op->alloclen = 2 * i;
+ strcpy (op->name, p);
+ op->value = (char *)(((unsigned long)(op->name + j + 4)) & ~3);
+ op->len = k;
+ if (k && prom_getproperty (node, p, op->value, i) < 0)
+ return -EIO;
+ op->value [k] = 0;
+ if (k) {
+ for (s = 0, p = op->value; p < op->value + k; p++) {
+ if ((*p >= ' ' && *p <= '~') || *p == '\n') {
+ op->flag |= OPP_STRING;
+ s = p;
+ continue;
+ }
+ if (p > op->value && !*p && s == p - 1) {
+ if (p < op->value + k - 1)
+ op->flag |= OPP_STRINGLIST;
+ else
+ op->flag |= OPP_ASCIIZ;
+ continue;
+ }
+ if (k == 1 && !*p) {
+ op->flag |= (OPP_STRING|OPP_ASCIIZ);
+ break;
+ }
+ op->flag &= ~(OPP_STRING|OPP_STRINGLIST);
+ if (k & 3)
+ op->flag |= OPP_HEXSTRING;
+ else
+ op->flag |= OPP_BINARY;
+ break;
+ }
+ if (op->flag & OPP_STRINGLIST)
+ op->flag &= ~(OPP_STRING);
+ if (op->flag & OPP_ASCIIZ)
+ op->len--;
+ }
+ } else
+ op = (openprom_property *)filp->private_data;
+ if (!count || !(op->len || (op->flag & OPP_ASCIIZ)))
+ return 0;
+ if (op->flag & OPP_STRINGLIST) {
+ for (k = 0, p = op->value; p < op->value + op->len; p++)
+ if (!*p)
+ k++;
+ i = op->len + 4 * k + 3;
+ } else if (op->flag & OPP_STRING) {
+ i = op->len + 3;
+ } else if (op->flag & OPP_BINARY) {
+ i = (op->len * 9) >> 2;
+ } else {
+ i = (op->len << 1) + 1;
+ }
+ k = pos;
+ if (k >= i) return 0;
+ if (count > i - k) count = i - k;
+ if (op->flag & OPP_STRING) {
+ if (!k) {
+ __put_user('\'', buf);
+ k++;
+ count--;
+ }
+
+ if (k + count >= i - 2)
+ j = i - 2 - k;
+ else
+ j = count;
+
+ if (j >= 0) {
+ copy_to_user(buf + k - pos,
+ op->value + k - 1, j);
+ count -= j;
+ k += j;
+ }
+
+ if (count)
+ __put_user('\'', &buf [k++ - pos]);
+ if (count > 1)
+ __put_user('\n', &buf [k++ - pos]);
+
+ } else if (op->flag & OPP_STRINGLIST) {
+ char *tmp;
+
+ tmp = kmalloc (i, GFP_KERNEL);
+ if (!tmp)
+ return -ENOMEM;
+
+ s = tmp;
+ *s++ = '\'';
+ for (p = op->value; p < op->value + op->len; p++) {
+ if (!*p) {
+ strcpy(s, "' + '");
+ s += 5;
+ continue;
+ }
+ *s++ = *p;
+ }
+ strcpy(s, "'\n");
+
+ copy_to_user(buf, tmp + k, count);
+
+ kfree(tmp);
+ k += count;
+
+ } else if (op->flag & OPP_BINARY) {
+ char buffer[10];
+ u32 *first, *last;
+ int first_off, last_cnt;
+
+ first = ((u32 *)op->value) + k / 9;
+ first_off = k % 9;
+ last = ((u32 *)op->value) + (k + count - 1) / 9;
+ last_cnt = (k + count) % 9;
+ if (!last_cnt) last_cnt = 9;
+
+ if (first == last) {
+ sprintf (buffer, "%08x.", *first);
+ copy_to_user (buf, buffer + first_off, last_cnt - first_off);
+ buf += last_cnt - first_off;
+ } else {
+ for (q = first; q <= last; q++) {
+ sprintf (buffer, "%08x.", *q);
+ if (q == first) {
+ copy_to_user (buf, buffer + first_off,
+ 9 - first_off);
+ buf += 9 - first_off;
+ } else if (q == last) {
+ copy_to_user (buf, buffer, last_cnt);
+ buf += last_cnt;
+ } else {
+ copy_to_user (buf, buffer, 9);
+ buf += 9;
+ }
+ }
+ }
+
+ if (last == (u32 *)(op->value + op->len - 4) && last_cnt == 9)
+ __put_user('\n', (buf - 1));
+
+ k += count;
+
+ } else if (op->flag & OPP_HEXSTRING) {
+ char buffer[2];
+
+ if ((k < i - 1) && (k & 1)) {
+ sprintf (buffer, "%02x", *(op->value + (k >> 1)));
+ __put_user(buffer[1], &buf[k++ - pos]);
+ count--;
+ }
+
+ for (; (count > 1) && (k < i - 1); k += 2) {
+ sprintf (buffer, "%02x", *(op->value + (k >> 1)));
+ copy_to_user (buf + k - pos, buffer, 2);
+ count -= 2;
+ }
+
+ if (count && (k < i - 1)) {
+ sprintf (buffer, "%02x", *(op->value + (k >> 1)));
+ __put_user(buffer[0], &buf[k++ - pos]);
+ count--;
+ }
+
+ if (count)
+ __put_user('\n', &buf [k++ - pos]);
+ }
+ count = k - pos;
+ *ppos = k;
+ return count;
+}
+
+static ssize_t property_write(struct file *filp, const char *buf,
+ size_t count, loff_t *ppos)
+{
+ loff_t pos = *ppos;
+ int i, j, k;
+ char *p;
+ u32 *q;
+ void *b;
+ openprom_property *op;
+
+ if (pos < 0 || pos >= 0xffffff)
+ return -EINVAL;
+ if (!filp->private_data) {
+ i = property_read (filp, NULL, 0, 0);
+ if (i)
+ return i;
+ }
+ k = pos;
+ op = (openprom_property *)filp->private_data;
+ if (!(op->flag & OPP_STRING)) {
+ u32 *first, *last;
+ int first_off, last_cnt;
+ u32 mask, mask2;
+ char tmp [9];
+ int forcelen = 0;
+
+ j = k % 9;
+ for (i = 0; i < count; i++, j++) {
+ if (j == 9) j = 0;
+ if (!j) {
+ char ctmp;
+ __get_user(ctmp, &buf[i]);
+ if (ctmp != '.') {
+ if (ctmp != '\n') {
+ if (op->flag & OPP_BINARY)
+ return -EINVAL;
+ else
+ goto write_try_string;
+ } else {
+ count = i + 1;
+ forcelen = 1;
+ break;
+ }
+ }
+ } else {
+ char ctmp;
+ __get_user(ctmp, &buf[i]);
+ if (ctmp < '0' ||
+ (ctmp > '9' && ctmp < 'A') ||
+ (ctmp > 'F' && ctmp < 'a') ||
+ ctmp > 'f') {
+ if (op->flag & OPP_BINARY)
+ return -EINVAL;
+ else
+ goto write_try_string;
+ }
+ }
+ }
+ op->flag |= OPP_BINARY;
+ tmp [8] = 0;
+ i = ((count + k + 8) / 9) << 2;
+ if (op->alloclen <= i) {
+ b = kmalloc (sizeof (openprom_property) + 2 * i,
+ GFP_KERNEL);
+ if (!b)
+ return -ENOMEM;
+ memcpy (b, filp->private_data,
+ sizeof (openprom_property)
+ + strlen (op->name) + op->alloclen);
+ memset (((char *)b) + sizeof (openprom_property)
+ + strlen (op->name) + op->alloclen,
+ 0, 2 * i - op->alloclen);
+ op = (openprom_property *)b;
+ op->alloclen = 2*i;
+ b = filp->private_data;
+ filp->private_data = (void *)op;
+ kfree (b);
+ }
+ first = ((u32 *)op->value) + (k / 9);
+ first_off = k % 9;
+ last = (u32 *)(op->value + i);
+ last_cnt = (k + count) % 9;
+ if (first + 1 == last) {
+ memset (tmp, '0', 8);
+ copy_from_user (tmp + first_off, buf,
+ (count + first_off > 8) ? 8 - first_off : count);
+ mask = 0xffffffff;
+ mask2 = 0xffffffff;
+ for (j = 0; j < first_off; j++)
+ mask >>= 1;
+ for (j = 8 - count - first_off; j > 0; j--)
+ mask2 <<= 1;
+ mask &= mask2;
+ if (mask) {
+ *first &= ~mask;
+ *first |= simple_strtoul (tmp, 0, 16);
+ op->flag |= OPP_DIRTY;
+ }
+ } else {
+ op->flag |= OPP_DIRTY;
+ for (q = first; q < last; q++) {
+ if (q == first) {
+ if (first_off < 8) {
+ memset (tmp, '0', 8);
+ copy_from_user (tmp + first_off, buf,
+ 8 - first_off);
+ mask = 0xffffffff;
+ for (j = 0; j < first_off; j++)
+ mask >>= 1;
+ *q &= ~mask;
+ *q |= simple_strtoul (tmp,0,16);
+ }
+ buf += 9;
+ } else if ((q == last - 1) && last_cnt
+ && (last_cnt < 8)) {
+ memset (tmp, '0', 8);
+ copy_from_user (tmp, buf, last_cnt);
+ mask = 0xffffffff;
+ for (j = 0; j < 8 - last_cnt; j++)
+ mask <<= 1;
+ *q &= ~mask;
+ *q |= simple_strtoul (tmp, 0, 16);
+ buf += last_cnt;
+ } else {
+ char tchars[17]; /* XXX yuck... */
+
+ copy_from_user(tchars, buf, 16);
+ *q = simple_strtoul (tchars, 0, 16);
+ buf += 9;
+ }
+ }
+ }
+ if (!forcelen) {
+ if (op->len < i)
+ op->len = i;
+ } else
+ op->len = i;
+ pos += count;
+ *ppos = pos;
+ }
+write_try_string:
+ if (!(op->flag & OPP_BINARY)) {
+ if (!(op->flag & (OPP_QUOTED | OPP_NOTQUOTED))) {
+ char ctmp;
+
+ /* No way, if somebody starts writing from the middle,
+ * we don't know whether he uses quotes around or not
+ */
+ if (k > 0)
+ return -EINVAL;
+ __get_user(ctmp, buf);
+ if (ctmp == '\'') {
+ op->flag |= OPP_QUOTED;
+ buf++;
+ count--;
+ pos++;
+ *ppos = pos;
+ if (!count) {
+ op->flag |= OPP_STRING;
+ return 1;
+ }
+ } else
+ op->flag |= OPP_NOTQUOTED;
+ }
+ op->flag |= OPP_STRING;
+ if (op->alloclen <= count + pos) {
+ b = kmalloc (sizeof (openprom_property)
+ + 2 * (count + pos), GFP_KERNEL);
+ if (!b)
+ return -ENOMEM;
+ memcpy (b, filp->private_data,
+ sizeof (openprom_property)
+ + strlen (op->name) + op->alloclen);
+ memset (((char *)b) + sizeof (openprom_property)
+ + strlen (op->name) + op->alloclen,
+ 0, 2*(count - pos) - op->alloclen);
+ op = (openprom_property *)b;
+ op->alloclen = 2*(count + pos);
+ b = filp->private_data;
+ filp->private_data = (void *)op;
+ kfree (b);
+ }
+ p = op->value + pos - ((op->flag & OPP_QUOTED) ? 1 : 0);
+ copy_from_user (p, buf, count);
+ op->flag |= OPP_DIRTY;
+ for (i = 0; i < count; i++, p++)
+ if (*p == '\n') {
+ *p = 0;
+ break;
+ }
+ if (i < count) {
+ op->len = p - op->value;
+ pos += i + 1;
+ *ppos = pos;
+ if ((p > op->value) && (op->flag & OPP_QUOTED)
+ && (*(p - 1) == '\''))
+ op->len--;
+ } else {
+ if (p - op->value > op->len)
+ op->len = p - op->value;
+ pos += count;
+ *ppos = pos;
+ }
+ }
+ return pos - k;
+}
+
+int property_release (struct inode *inode, struct file *filp)
+{
+ openprom_property *op = (openprom_property *)filp->private_data;
+ unsigned long flags;
+ int error;
+ u32 node;
+
+ if (!op)
+ return 0;
+ lock_kernel();
+ node = nodes[(u16)((long)inode->u.generic_ip)].node;
+ if ((u16)((long)inode->u.generic_ip) == aliases) {
+ if ((op->flag & OPP_DIRTY) && (op->flag & OPP_STRING)) {
+ char *p = op->name;
+ int i = (op->value - op->name) - strlen (op->name) - 1;
+ op->value [op->len] = 0;
+ *(op->value - 1) = ' ';
+ if (i) {
+ for (p = op->value - i - 2; p >= op->name; p--)
+ p[i] = *p;
+ p = op->name + i;
+ }
+ memcpy (p - 8, "nvalias ", 8);
+ prom_feval (p - 8);
+ }
+ } else if (op->flag & OPP_DIRTY) {
+ if (op->flag & OPP_STRING) {
+ op->value [op->len] = 0;
+ save_and_cli (flags);
+ error = prom_setprop (node, op->name,
+ op->value, op->len + 1);
+ restore_flags (flags);
+ if (error <= 0)
+ printk (KERN_WARNING "openpromfs: "
+ "Couldn't write property %s\n",
+ op->name);
+ } else if ((op->flag & OPP_BINARY) || !op->len) {
+ save_and_cli (flags);
+ error = prom_setprop (node, op->name,
+ op->value, op->len);
+ restore_flags (flags);
+ if (error <= 0)
+ printk (KERN_WARNING "openpromfs: "
+ "Couldn't write property %s\n",
+ op->name);
+ } else {
+ printk (KERN_WARNING "openpromfs: "
+ "Unknown property type of %s\n",
+ op->name);
+ }
+ }
+ unlock_kernel();
+ kfree (filp->private_data);
+ return 0;
+}
+
+static struct file_operations openpromfs_prop_ops = {
+ read: property_read,
+ write: property_write,
+ release: property_release,
+};
+
+static struct file_operations openpromfs_nodenum_ops = {
+ read: nodenum_read,
+};
+
+static struct file_operations openprom_operations = {
+ read: generic_read_dir,
+ readdir: openpromfs_readdir,
+};
+
+static struct inode_operations openprom_alias_inode_operations = {
+ create: openpromfs_create,
+ lookup: openpromfs_lookup,
+ unlink: openpromfs_unlink,
+};
+
+static struct inode_operations openprom_inode_operations = {
+ lookup: openpromfs_lookup,
+};
+
+static int lookup_children(u16 n, const char * name, int len)
+{
+ int ret;
+ u16 node;
+ for (; n != 0xffff; n = nodes[n].next) {
+ node = nodes[n].child;
+ if (node != 0xffff) {
+ char buffer[128];
+ int i;
+ char *p;
+
+ while (node != 0xffff) {
+ if (prom_getname (nodes[node].node,
+ buffer, 128) >= 0) {
+ i = strlen (buffer);
+ if ((len == i)
+ && !strncmp (buffer, name, len))
+ return NODE2INO(node);
+ p = strchr (buffer, '@');
+ if (p && (len == p - buffer)
+ && !strncmp (buffer, name, len))
+ return NODE2INO(node);
+ }
+ node = nodes[node].next;
+ }
+ } else
+ continue;
+ ret = lookup_children (nodes[n].child, name, len);
+ if (ret) return ret;
+ }
+ return 0;
+}
+
+static struct dentry *openpromfs_lookup(struct inode * dir, struct dentry *dentry)
+{
+ int ino = 0;
+#define OPFSL_DIR 0
+#define OPFSL_PROPERTY 1
+#define OPFSL_NODENUM 2
+ int type = 0;
+ char buffer[128];
+ char *p;
+ const char *name;
+ u32 n;
+ u16 dirnode;
+ unsigned int len;
+ int i;
+ struct inode *inode;
+ char buffer2[64];
+
+ inode = NULL;
+ name = dentry->d_name.name;
+ len = dentry->d_name.len;
+ if (name [0] == '.' && len == 5 && !strncmp (name + 1, "node", 4)) {
+ ino = NODEP2INO(NODE(dir->i_ino).first_prop);
+ type = OPFSL_NODENUM;
+ }
+ if (!ino) {
+ u16 node = NODE(dir->i_ino).child;
+ while (node != 0xffff) {
+ if (prom_getname (nodes[node].node, buffer, 128) >= 0) {
+ i = strlen (buffer);
+ if (len == i && !strncmp (buffer, name, len)) {
+ ino = NODE2INO(node);
+ type = OPFSL_DIR;
+ break;
+ }
+ p = strchr (buffer, '@');
+ if (p && (len == p - buffer)
+ && !strncmp (buffer, name, len)) {
+ ino = NODE2INO(node);
+ type = OPFSL_DIR;
+ break;
+ }
+ }
+ node = nodes[node].next;
+ }
+ }
+ n = NODE(dir->i_ino).node;
+ dirnode = dir->i_ino - OPENPROM_FIRST_INO;
+ if (!ino) {
+ int j = NODEP2INO(NODE(dir->i_ino).first_prop);
+ if (dirnode != aliases) {
+ for (p = prom_firstprop (n, buffer2);
+ p && *p;
+ p = prom_nextprop (n, p, buffer2)) {
+ j++;
+ if ((len == strlen (p))
+ && !strncmp (p, name, len)) {
+ ino = j;
+ type = OPFSL_PROPERTY;
+ break;
+ }
+ }
+ } else {
+ int k;
+ for (k = 0; k < aliases_nodes; k++) {
+ j++;
+ if (alias_names [k]
+ && (len == strlen (alias_names [k]))
+ && !strncmp (alias_names [k], name, len)) {
+ ino = j;
+ type = OPFSL_PROPERTY;
+ break;
+ }
+ }
+ }
+ }
+ if (!ino) {
+ ino = lookup_children (NODE(dir->i_ino).child, name, len);
+ if (ino)
+ type = OPFSL_DIR;
+ else
+ return ERR_PTR(-ENOENT);
+ }
+ inode = iget (dir->i_sb, ino);
+ if (!inode)
+ return ERR_PTR(-EINVAL);
+ switch (type) {
+ case OPFSL_DIR:
+ inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
+ if (ino == OPENPROM_FIRST_INO + aliases) {
+ inode->i_mode |= S_IWUSR;
+ inode->i_op = &openprom_alias_inode_operations;
+ } else
+ inode->i_op = &openprom_inode_operations;
+ inode->i_fop = &openprom_operations;
+ inode->i_nlink = 2;
+ break;
+ case OPFSL_NODENUM:
+ inode->i_mode = S_IFREG | S_IRUGO;
+ inode->i_fop = &openpromfs_nodenum_ops;
+ inode->i_nlink = 1;
+ inode->u.generic_ip = (void *)(long)(n);
+ break;
+ case OPFSL_PROPERTY:
+ if ((dirnode == options) && (len == 17)
+ && !strncmp (name, "security-password", 17))
+ inode->i_mode = S_IFREG | S_IRUSR | S_IWUSR;
+ else {
+ inode->i_mode = S_IFREG | S_IRUGO;
+ if (dirnode == options || dirnode == aliases) {
+ if (len != 4 || strncmp (name, "name", 4))
+ inode->i_mode |= S_IWUSR;
+ }
+ }
+ inode->i_fop = &openpromfs_prop_ops;
+ inode->i_nlink = 1;
+ if (inode->i_size < 0)
+ inode->i_size = 0;
+ inode->u.generic_ip = (void *)(long)(((u16)dirnode) |
+ (((u16)(ino - NODEP2INO(NODE(dir->i_ino).first_prop) - 1)) << 16));
+ break;
+ }
+
+ inode->i_gid = 0;
+ inode->i_uid = 0;
+
+ d_add(dentry, inode);
+ return NULL;
+}
+
+static int openpromfs_readdir(struct file * filp, void * dirent, filldir_t filldir)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ unsigned int ino;
+ u32 n;
+ int i, j;
+ char buffer[128];
+ u16 node;
+ char *p;
+ char buffer2[64];
+
+ ino = inode->i_ino;
+ i = filp->f_pos;
+ switch (i) {
+ case 0:
+ if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0) return 0;
+ i++;
+ filp->f_pos++;
+ /* fall thru */
+ case 1:
+ if (filldir(dirent, "..", 2, i,
+ (NODE(ino).parent == 0xffff) ?
+ OPENPROM_ROOT_INO : NODE2INO(NODE(ino).parent), DT_DIR) < 0)
+ return 0;
+ i++;
+ filp->f_pos++;
+ /* fall thru */
+ default:
+ i -= 2;
+ node = NODE(ino).child;
+ while (i && node != 0xffff) {
+ node = nodes[node].next;
+ i--;
+ }
+ while (node != 0xffff) {
+ if (prom_getname (nodes[node].node, buffer, 128) < 0)
+ return 0;
+ if (filldir(dirent, buffer, strlen(buffer),
+ filp->f_pos, NODE2INO(node), DT_DIR) < 0)
+ return 0;
+ filp->f_pos++;
+ node = nodes[node].next;
+ }
+ j = NODEP2INO(NODE(ino).first_prop);
+ if (!i) {
+ if (filldir(dirent, ".node", 5, filp->f_pos, j, DT_REG) < 0)
+ return 0;
+ filp->f_pos++;
+ } else
+ i--;
+ n = NODE(ino).node;
+ if (ino == OPENPROM_FIRST_INO + aliases) {
+ for (j++; i < aliases_nodes; i++, j++) {
+ if (alias_names [i]) {
+ if (filldir (dirent, alias_names [i],
+ strlen (alias_names [i]),
+ filp->f_pos, j, DT_REG) < 0) return 0;
+ filp->f_pos++;
+ }
+ }
+ } else {
+ for (p = prom_firstprop (n, buffer2);
+ p && *p;
+ p = prom_nextprop (n, p, buffer2)) {
+ j++;
+ if (i) i--;
+ else {
+ if (filldir(dirent, p, strlen(p),
+ filp->f_pos, j, DT_REG) < 0)
+ return 0;
+ filp->f_pos++;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static int openpromfs_create (struct inode *dir, struct dentry *dentry, int mode)
+{
+ char *p;
+ struct inode *inode;
+
+ if (!dir)
+ return -ENOENT;
+ if (dentry->d_name.len > 256)
+ return -EINVAL;
+ if (aliases_nodes == ALIASES_NNODES)
+ return -EIO;
+ p = kmalloc (dentry->d_name.len + 1, GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+ strncpy (p, dentry->d_name.name, dentry->d_name.len);
+ p [dentry->d_name.len] = 0;
+ alias_names [aliases_nodes++] = p;
+ inode = iget (dir->i_sb,
+ NODEP2INO(NODE(dir->i_ino).first_prop) + aliases_nodes);
+ if (!inode)
+ return -EINVAL;
+ inode->i_mode = S_IFREG | S_IRUGO | S_IWUSR;
+ inode->i_fop = &openpromfs_prop_ops;
+ inode->i_nlink = 1;
+ if (inode->i_size < 0) inode->i_size = 0;
+ inode->u.generic_ip = (void *)(long)(((u16)aliases) |
+ (((u16)(aliases_nodes - 1)) << 16));
+ d_instantiate(dentry, inode);
+ return 0;
+}
+
+static int openpromfs_unlink (struct inode *dir, struct dentry *dentry)
+{
+ unsigned int len;
+ char *p;
+ const char *name;
+ int i;
+
+ name = dentry->d_name.name;
+ len = dentry->d_name.len;
+ for (i = 0; i < aliases_nodes; i++)
+ if ((strlen (alias_names [i]) == len)
+ && !strncmp (name, alias_names[i], len)) {
+ char buffer[512];
+
+ p = alias_names [i];
+ alias_names [i] = NULL;
+ kfree (p);
+ strcpy (buffer, "nvunalias ");
+ memcpy (buffer + 10, name, len);
+ buffer [10 + len] = 0;
+ prom_feval (buffer);
+ }
+ return 0;
+}
+
+/* {{{ init section */
+#ifndef MODULE
+static int __init check_space (u16 n)
+#else
+static int check_space (u16 n)
+#endif
+{
+ unsigned long pages;
+
+ if ((1 << alloced) * PAGE_SIZE < (n + 2) * sizeof(openpromfs_node)) {
+ pages = __get_free_pages (GFP_KERNEL, alloced + 1);
+ if (!pages)
+ return -1;
+
+ if (nodes) {
+ memcpy ((char *)pages, (char *)nodes,
+ (1 << alloced) * PAGE_SIZE);
+ free_pages ((unsigned long)nodes, alloced);
+ }
+ alloced++;
+ nodes = (openpromfs_node *)pages;
+ }
+ return 0;
+}
+
+#ifndef MODULE
+static u16 __init get_nodes (u16 parent, u32 node)
+#else
+static u16 get_nodes (u16 parent, u32 node)
+#endif
+{
+ char *p;
+ u16 n = last_node++, i;
+ char buffer[64];
+
+ if (check_space (n) < 0)
+ return 0xffff;
+ nodes[n].parent = parent;
+ nodes[n].node = node;
+ nodes[n].next = 0xffff;
+ nodes[n].child = 0xffff;
+ nodes[n].first_prop = first_prop++;
+ if (!parent) {
+ char buffer[8];
+ int j;
+
+ if ((j = prom_getproperty (node, "name", buffer, 8)) >= 0) {
+ buffer[j] = 0;
+ if (!strcmp (buffer, "options"))
+ options = n;
+ else if (!strcmp (buffer, "aliases"))
+ aliases = n;
+ }
+ }
+ if (n != aliases)
+ for (p = prom_firstprop (node, buffer);
+ p && p != (char *)-1 && *p;
+ p = prom_nextprop (node, p, buffer))
+ first_prop++;
+ else {
+ char *q;
+ for (p = prom_firstprop (node, buffer);
+ p && p != (char *)-1 && *p;
+ p = prom_nextprop (node, p, buffer)) {
+ if (aliases_nodes == ALIASES_NNODES)
+ break;
+ for (i = 0; i < aliases_nodes; i++)
+ if (!strcmp (p, alias_names [i]))
+ break;
+ if (i < aliases_nodes)
+ continue;
+ q = kmalloc (strlen (p) + 1, GFP_KERNEL);
+ if (!q)
+ return 0xffff;
+ strcpy (q, p);
+ alias_names [aliases_nodes++] = q;
+ }
+ first_prop += ALIASES_NNODES;
+ }
+ node = prom_getchild (node);
+ if (node) {
+ parent = get_nodes (n, node);
+ if (parent == 0xffff)
+ return 0xffff;
+ nodes[n].child = parent;
+ while ((node = prom_getsibling (node)) != 0) {
+ i = get_nodes (n, node);
+ if (i == 0xffff)
+ return 0xffff;
+ nodes[parent].next = i;
+ parent = i;
+ }
+ }
+ return n;
+}
+
+static void openprom_read_inode(struct inode * inode)
+{
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+ if (inode->i_ino == OPENPROM_ROOT_INO) {
+ inode->i_op = &openprom_inode_operations;
+ inode->i_fop = &openprom_operations;
+ inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
+ }
+}
+
+static int openprom_statfs(struct super_block *sb, struct statfs *buf)
+{
+ buf->f_type = OPENPROM_SUPER_MAGIC;
+ buf->f_bsize = PAGE_SIZE/sizeof(long); /* ??? */
+ buf->f_bfree = 0;
+ buf->f_bavail = 0;
+ buf->f_ffree = 0;
+ buf->f_namelen = NAME_MAX;
+ return 0;
+}
+
+static struct super_operations openprom_sops = {
+ read_inode: openprom_read_inode,
+ statfs: openprom_statfs,
+};
+
+struct super_block *openprom_read_super(struct super_block *s,void *data,
+ int silent)
+{
+ struct inode * root_inode;
+
+ s->s_blocksize = 1024;
+ s->s_blocksize_bits = 10;
+ s->s_magic = OPENPROM_SUPER_MAGIC;
+ s->s_op = &openprom_sops;
+ root_inode = iget(s, OPENPROM_ROOT_INO);
+ if (!root_inode)
+ goto out_no_root;
+ s->s_root = d_alloc_root(root_inode);
+ if (!s->s_root)
+ goto out_no_root;
+ return s;
+
+out_no_root:
+ printk("openprom_read_super: get root inode failed\n");
+ iput(root_inode);
+ return NULL;
+}
+
+static DECLARE_FSTYPE(openprom_fs_type, "openpromfs", openprom_read_super, 0);
+
+static int __init init_openprom_fs(void)
+{
+ nodes = (openpromfs_node *)__get_free_pages(GFP_KERNEL, 0);
+ if (!nodes) {
+ printk (KERN_WARNING "openpromfs: can't get free page\n");
+ return -EIO;
+ }
+ if (get_nodes (0xffff, prom_root_node) == 0xffff) {
+ printk (KERN_WARNING "openpromfs: couldn't setup tree\n");
+ return -EIO;
+ }
+ nodes[last_node].first_prop = first_prop;
+ return register_filesystem(&openprom_fs_type);
+}
+
+static void __exit exit_openprom_fs(void)
+{
+ int i;
+ unregister_filesystem(&openprom_fs_type);
+ free_pages ((unsigned long)nodes, alloced);
+ for (i = 0; i < aliases_nodes; i++)
+ if (alias_names [i])
+ kfree (alias_names [i]);
+ nodes = NULL;
+}
+
+EXPORT_NO_SYMBOLS;
+
+module_init(init_openprom_fs)
+module_exit(exit_openprom_fs)
+MODULE_LICENSE("GPL");
diff --git a/fs/partitions/Config.in b/fs/partitions/Config.in
new file mode 100644
index 00000000000000..ed7a62b9630e4a
--- /dev/null
+++ b/fs/partitions/Config.in
@@ -0,0 +1,71 @@
+#
+# Partition configuration
+#
+bool 'Advanced partition selection' CONFIG_PARTITION_ADVANCED
+if [ "$CONFIG_PARTITION_ADVANCED" = "y" ]; then
+ bool ' Acorn partition support' CONFIG_ACORN_PARTITION
+ if [ "$CONFIG_ACORN_PARTITION" != "n" ]; then
+# bool ' Cumana partition support' CONFIG_ACORN_PARTITION_CUMANA
+ bool ' ICS partition support' CONFIG_ACORN_PARTITION_ICS
+ bool ' Native filecore partition support' CONFIG_ACORN_PARTITION_ADFS
+ bool ' PowerTec partition support' CONFIG_ACORN_PARTITION_POWERTEC
+ bool ' RISCiX partition support' CONFIG_ACORN_PARTITION_RISCIX
+ fi
+ bool ' Alpha OSF partition support' CONFIG_OSF_PARTITION
+ bool ' Amiga partition table support' CONFIG_AMIGA_PARTITION
+ bool ' Atari partition table support' CONFIG_ATARI_PARTITION
+ if [ "$CONFIG_ARCH_S390" = "y" ]; then
+ bool ' IBM disk label and partition support' CONFIG_IBM_PARTITION
+ fi
+ bool ' Macintosh partition map support' CONFIG_MAC_PARTITION
+ bool ' PC BIOS (MSDOS partition tables) support' CONFIG_MSDOS_PARTITION
+ if [ "$CONFIG_MSDOS_PARTITION" = "y" ]; then
+ bool ' BSD disklabel (FreeBSD partition tables) support' CONFIG_BSD_DISKLABEL
+ bool ' Minix subpartition support' CONFIG_MINIX_SUBPARTITION
+ bool ' Solaris (x86) partition table support' CONFIG_SOLARIS_X86_PARTITION
+ bool ' Unixware slices support' CONFIG_UNIXWARE_DISKLABEL
+ fi
+ dep_bool ' Windows Logical Disk Manager (Dynamic Disk) support (EXPERIMENTAL)' CONFIG_LDM_PARTITION $CONFIG_EXPERIMENTAL
+ if [ "$CONFIG_LDM_PARTITION" = "y" ]; then
+ bool ' Windows LDM extra logging' CONFIG_LDM_DEBUG
+ fi
+ bool ' SGI partition support' CONFIG_SGI_PARTITION
+ bool ' Ultrix partition table support' CONFIG_ULTRIX_PARTITION
+ bool ' Sun partition tables support' CONFIG_SUN_PARTITION
+ bool ' EFI GUID Partition support' CONFIG_EFI_PARTITION
+else
+ if [ "$CONFIG_ALPHA" = "y" ]; then
+ define_bool CONFIG_OSF_PARTITION y
+ fi
+ if [ "$CONFIG_AMIGA" != "y" -a "$CONFIG_ATARI" != "y" -a \
+ "$CONFIG_MAC" != "y" -a "$CONFIG_SGI_IP22" != "y" -a \
+ "$CONFIG_SGI_IP27" != "y" ]; then
+ define_bool CONFIG_MSDOS_PARTITION y
+ fi
+ if [ "$CONFIG_AMIGA" = "y" -o "$CONFIG_AFFS_FS" = "y" ]; then
+ define_bool CONFIG_AMIGA_PARTITION y
+ fi
+ if [ "$CONFIG_MAC" = "y" ]; then
+ define_bool CONFIG_MAC_PARTITION y
+ fi
+ if [ "$CONFIG_ARCH_ACORN" = "y" ]; then
+ define_bool CONFIG_ACORN_PARTITION y
+ define_bool CONFIG_ACORN_PARTITION_ADFS y
+# define_bool CONFIG_ACORN_PARTITION_CUMANA y
+ define_bool CONFIG_ACORN_PARTITION_ICS y
+ define_bool CONFIG_ACORN_PARTITION_POWERTEC y
+ define_bool CONFIG_ACORN_PARTITION_RISCIX y
+ fi
+ if [ "$CONFIG_ATARI" = "y" ]; then
+ define_bool CONFIG_ATARI_PARTITION y
+ fi
+ if [ "$CONFIG_SGI_IP22" = "y" -o "$CONFIG_SGI_IP27" = "y" ]; then
+ define_bool CONFIG_SGI_PARTITION y
+ fi
+ if [ "$CONFIG_DECSTATION" = "y" ]; then
+ define_bool CONFIG_ULTRIX_PARTITION y
+ fi
+ if [ "$CONFIG_SPARC32" = "y" -o "$CONFIG_SPARC64" = "y" ]; then
+ define_bool CONFIG_SUN_PARTITION y
+ fi
+fi
diff --git a/fs/partitions/Makefile b/fs/partitions/Makefile
new file mode 100644
index 00000000000000..cb2a10e6eb4f28
--- /dev/null
+++ b/fs/partitions/Makefile
@@ -0,0 +1,29 @@
+#
+# Makefile for the linux kernel.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+O_TARGET := partitions.o
+
+export-objs := check.o msdos.o
+
+obj-y := check.o
+
+obj-$(CONFIG_ACORN_PARTITION) += acorn.o
+obj-$(CONFIG_AMIGA_PARTITION) += amiga.o
+obj-$(CONFIG_ATARI_PARTITION) += atari.o
+obj-$(CONFIG_MAC_PARTITION) += mac.o
+obj-$(CONFIG_LDM_PARTITION) += ldm.o
+obj-$(CONFIG_MSDOS_PARTITION) += msdos.o
+obj-$(CONFIG_OSF_PARTITION) += osf.o
+obj-$(CONFIG_SGI_PARTITION) += sgi.o
+obj-$(CONFIG_SUN_PARTITION) += sun.o
+obj-$(CONFIG_ULTRIX_PARTITION) += ultrix.o
+obj-$(CONFIG_IBM_PARTITION) += ibm.o
+obj-$(CONFIG_EFI_PARTITION) += efi.o
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/partitions/acorn.c b/fs/partitions/acorn.c
new file mode 100644
index 00000000000000..e9da3ced3570f7
--- /dev/null
+++ b/fs/partitions/acorn.c
@@ -0,0 +1,478 @@
+/*
+ * linux/fs/partitions/acorn.c
+ *
+ * Copyright (c) 1996-2000 Russell King.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Scan ADFS partitions on hard disk drives.
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/genhd.h>
+#include <linux/fs.h>
+#include <linux/pagemap.h>
+
+#include "check.h"
+#include "acorn.h"
+
+static void
+adfspart_setgeometry(kdev_t dev, unsigned int secspertrack, unsigned int heads)
+{
+#ifdef CONFIG_BLK_DEV_MFM
+ extern void xd_set_geometry(kdev_t dev, unsigned char, unsigned char,
+ unsigned long, unsigned int);
+
+ if (MAJOR(dev) == MFM_ACORN_MAJOR) {
+ unsigned long totalblocks = hd->part[MINOR(dev)].nr_sects;
+ xd_set_geometry(dev, secspertrack, heads, totalblocks, 1);
+ }
+#endif
+}
+
+static struct adfs_discrecord *
+adfs_partition(struct gendisk *hd, char *name, char *data,
+ unsigned long first_sector, int minor)
+{
+ struct adfs_discrecord *dr;
+ unsigned int nr_sects;
+
+ if (adfs_checkbblk(data))
+ return NULL;
+
+ dr = (struct adfs_discrecord *)(data + 0x1c0);
+
+ if (dr->disc_size == 0 && dr->disc_size_high == 0)
+ return NULL;
+
+ nr_sects = (le32_to_cpu(dr->disc_size_high) << 23) |
+ (le32_to_cpu(dr->disc_size) >> 9);
+
+ if (name)
+ printk(" [%s]", name);
+ add_gd_partition(hd, minor, first_sector, nr_sects);
+ return dr;
+}
+
+#ifdef CONFIG_ACORN_PARTITION_RISCIX
+static int
+riscix_partition(struct gendisk *hd, struct block_device *bdev,
+ unsigned long first_sect, int minor, unsigned long nr_sects)
+{
+ Sector sect;
+ struct riscix_record *rr;
+
+ rr = (struct riscix_record *)read_dev_sector(bdev, first_sect, &sect);
+ if (!rr)
+ return -1;
+
+ printk(" [RISCiX]");
+
+
+ if (rr->magic == RISCIX_MAGIC) {
+ unsigned long size = nr_sects > 2 ? 2 : nr_sects;
+ int part;
+
+ printk(" <");
+
+ add_gd_partition(hd, minor++, first_sect, size);
+ for (part = 0; part < 8; part++) {
+ if (rr->part[part].one &&
+ memcmp(rr->part[part].name, "All\0", 4)) {
+ add_gd_partition(hd, minor++,
+ le32_to_cpu(rr->part[part].start),
+ le32_to_cpu(rr->part[part].length));
+ printk("(%s)", rr->part[part].name);
+ }
+ }
+
+ printk(" >\n");
+ } else {
+ add_gd_partition(hd, minor++, first_sect, nr_sects);
+ }
+
+ put_dev_sector(sect);
+ return minor;
+}
+#endif
+
+static int
+linux_partition(struct gendisk *hd, struct block_device *bdev,
+ unsigned long first_sect, int minor, unsigned long nr_sects)
+{
+ Sector sect;
+ struct linux_part *linuxp;
+ unsigned int mask = (1 << hd->minor_shift) - 1;
+ unsigned long size = nr_sects > 2 ? 2 : nr_sects;
+
+ printk(" [Linux]");
+
+ add_gd_partition(hd, minor++, first_sect, size);
+
+ linuxp = (struct linux_part *)read_dev_sector(bdev, first_sect, &sect);
+ if (!linuxp)
+ return -1;
+
+ printk(" <");
+ while (linuxp->magic == cpu_to_le32(LINUX_NATIVE_MAGIC) ||
+ linuxp->magic == cpu_to_le32(LINUX_SWAP_MAGIC)) {
+ if (!(minor & mask))
+ break;
+ add_gd_partition(hd, minor++, first_sect +
+ le32_to_cpu(linuxp->start_sect),
+ le32_to_cpu(linuxp->nr_sects));
+ linuxp ++;
+ }
+ printk(" >");
+
+ put_dev_sector(sect);
+ return minor;
+}
+
+#ifdef CONFIG_ACORN_PARTITION_CUMANA
+static int
+adfspart_check_CUMANA(struct gendisk *hd, struct block_device *bdev,
+ unsigned long first_sector, int minor)
+{
+ unsigned int start_blk = 0, mask = (1 << hd->minor_shift) - 1;
+ Sector sect;
+ unsigned char *data;
+ char *name = "CUMANA/ADFS";
+ int first = 1;
+
+ /*
+ * Try Cumana style partitions - sector 3 contains ADFS boot block
+ * with pointer to next 'drive'.
+ *
+ * There are unknowns in this code - is the 'cylinder number' of the
+ * next partition relative to the start of this one - I'm assuming
+ * it is.
+ *
+ * Also, which ID did Cumana use?
+ *
+ * This is totally unfinished, and will require more work to get it
+ * going. Hence it is totally untested.
+ */
+ do {
+ struct adfs_discrecord *dr;
+ unsigned int nr_sects;
+
+ if (!(minor & mask))
+ break;
+
+ data = read_dev_sector(bdev, start_blk * 2 + 6, &sect);
+ if (!data)
+ return -1;
+
+ dr = adfs_partition(hd, name, data, first_sector, minor++);
+ if (!dr)
+ break;
+
+ name = NULL;
+
+ nr_sects = (data[0x1fd] + (data[0x1fe] << 8)) *
+ (dr->heads + (dr->lowsector & 0x40 ? 1 : 0)) *
+ dr->secspertrack;
+
+ if (!nr_sects)
+ break;
+
+ first = 0;
+ first_sector += nr_sects;
+ start_blk += nr_sects >> (BLOCK_SIZE_BITS - 9);
+ nr_sects = 0; /* hmm - should be partition size */
+
+ switch (data[0x1fc] & 15) {
+ case 0: /* No partition / ADFS? */
+ break;
+
+#ifdef CONFIG_ACORN_PARTITION_RISCIX
+ case PARTITION_RISCIX_SCSI:
+ /* RISCiX - we don't know how to find the next one. */
+ minor = riscix_partition(hd, bdev, first_sector,
+ minor, nr_sects);
+ break;
+#endif
+
+ case PARTITION_LINUX:
+ minor = linux_partition(hd, bdev, first_sector,
+ minor, nr_sects);
+ break;
+ }
+ put_dev_sector(sect);
+ if (minor == -1)
+ return minor;
+ } while (1);
+ put_dev_sector(sect);
+ return first ? 0 : 1;
+}
+#endif
+
+#ifdef CONFIG_ACORN_PARTITION_ADFS
+/*
+ * Purpose: allocate ADFS partitions.
+ *
+ * Params : hd - pointer to gendisk structure to store partition info.
+ * dev - device number to access.
+ * first_sector- first readable sector on the device.
+ * minor - first available minor on device.
+ *
+ * Returns: -1 on error, 0 for no ADFS boot sector, 1 for ok.
+ *
+ * Alloc : hda = whole drive
+ * hda1 = ADFS partition on first drive.
+ * hda2 = non-ADFS partition.
+ */
+static int
+adfspart_check_ADFS(struct gendisk *hd, struct block_device *bdev,
+ unsigned long first_sector, int minor)
+{
+ unsigned long start_sect, nr_sects, sectscyl, heads;
+ Sector sect;
+ unsigned char *data;
+ struct adfs_discrecord *dr;
+ unsigned char id;
+
+ data = read_dev_sector(bdev, 6, &sect);
+ if (!data)
+ return -1;
+
+ dr = adfs_partition(hd, "ADFS", data, first_sector, minor++);
+ if (!dr) {
+ put_dev_sector(sect);
+ return 0;
+ }
+
+ heads = dr->heads + ((dr->lowsector >> 6) & 1);
+ sectscyl = dr->secspertrack * heads;
+ start_sect = ((data[0x1fe] << 8) + data[0x1fd]) * sectscyl;
+ id = data[0x1fc] & 15;
+ put_dev_sector(sect);
+
+ adfspart_setgeometry(to_kdev_t(bdev->bd_dev), dr->secspertrack, heads);
+ invalidate_bdev(bdev, 1);
+ truncate_inode_pages(bdev->bd_inode->i_mapping, 0);
+
+ /*
+ * Work out start of non-adfs partition.
+ */
+ nr_sects = hd->part[MINOR(to_kdev_t(bdev->bd_dev))].nr_sects - start_sect;
+
+ if (start_sect) {
+ first_sector += start_sect;
+
+ switch (id) {
+#ifdef CONFIG_ACORN_PARTITION_RISCIX
+ case PARTITION_RISCIX_SCSI:
+ case PARTITION_RISCIX_MFM:
+ minor = riscix_partition(hd, bdev, first_sector,
+ minor, nr_sects);
+ break;
+#endif
+
+ case PARTITION_LINUX:
+ minor = linux_partition(hd, bdev, first_sector,
+ minor, nr_sects);
+ break;
+ }
+ }
+ return 1;
+}
+#endif
+
+#ifdef CONFIG_ACORN_PARTITION_ICS
+static int adfspart_check_ICSLinux(struct block_device *bdev, unsigned long block)
+{
+ Sector sect;
+ unsigned char *data = read_dev_sector(bdev, block, &sect);
+ int result = 0;
+
+ if (data) {
+ if (memcmp(data, "LinuxPart", 9) == 0)
+ result = 1;
+ put_dev_sector(sect);
+ }
+
+ return result;
+}
+
+/*
+ * Purpose: allocate ICS partitions.
+ * Params : hd - pointer to gendisk structure to store partition info.
+ * dev - device number to access.
+ * first_sector- first readable sector on the device.
+ * minor - first available minor on device.
+ * Returns: -1 on error, 0 for no ICS table, 1 for partitions ok.
+ * Alloc : hda = whole drive
+ * hda1 = ADFS partition 0 on first drive.
+ * hda2 = ADFS partition 1 on first drive.
+ * ..etc..
+ */
+static int
+adfspart_check_ICS(struct gendisk *hd, struct block_device *bdev,
+ unsigned long first_sector, int minor)
+{
+ Sector sect;
+ unsigned char *data;
+ unsigned long sum;
+ unsigned int i, mask = (1 << hd->minor_shift) - 1;
+ struct ics_part *p;
+
+ /*
+ * Try ICS style partitions - sector 0 contains partition info.
+ */
+ data = read_dev_sector(bdev, 0, &sect);
+ if (!data)
+ return -1;
+
+ /*
+ * check for a valid checksum
+ */
+ for (i = 0, sum = 0x50617274; i < 508; i++)
+ sum += data[i];
+
+ sum -= le32_to_cpu(*(__u32 *)(&data[508]));
+ if (sum) {
+ put_dev_sector(sect);
+ return 0; /* not ICS partition table */
+ }
+
+ printk(" [ICS]");
+
+ for (p = (struct ics_part *)data; p->size; p++) {
+ unsigned long start;
+ long size;
+
+ if ((minor & mask) == 0)
+ break;
+
+ start = le32_to_cpu(p->start);
+ size = le32_to_cpu(p->size);
+
+ if (size < 0) {
+ size = -size;
+
+ /*
+ * We use the first sector to identify what type
+ * this partition is...
+ */
+ if (size > 1 && adfspart_check_ICSLinux(bdev, start)) {
+ start += 1;
+ size -= 1;
+ }
+ }
+
+ if (size) {
+ add_gd_partition(hd, minor, first_sector + start, size);
+ minor++;
+ }
+ }
+
+ put_dev_sector(sect);
+ return 1;
+}
+#endif
+
+/*
+ * Purpose: allocate ICS partitions.
+ * Params : hd - pointer to gendisk structure to store partition info.
+ * dev - device number to access.
+ * first_sector- first readable sector on the device.
+ * minor - first available minor on device.
+ * Returns: -1 on error, 0 for no ICS table, 1 for partitions ok.
+ * Alloc : hda = whole drive
+ * hda1 = ADFS partition 0 on first drive.
+ * hda2 = ADFS partition 1 on first drive.
+ * ..etc..
+ */
+#ifdef CONFIG_ACORN_PARTITION_POWERTEC
+static int
+adfspart_check_POWERTEC(struct gendisk *hd, struct block_device *bdev,
+ unsigned long first_sector, int minor)
+{
+ Sector sect;
+ unsigned char *data;
+ struct ptec_partition *p;
+ unsigned char checksum;
+ int i;
+
+ data = read_dev_sector(bdev, 0, &sect);
+ if (!data)
+ return -1;
+
+ for (checksum = 0x2a, i = 0; i < 511; i++)
+ checksum += data[i];
+
+ if (checksum != data[511]) {
+ put_dev_sector(sect);
+ return 0;
+ }
+
+ printk(" [POWERTEC]");
+
+ for (i = 0, p = (struct ptec_partition *)data; i < 12; i++, p++) {
+ unsigned long start;
+ unsigned long size;
+
+ start = le32_to_cpu(p->start);
+ size = le32_to_cpu(p->size);
+
+ if (size)
+ add_gd_partition(hd, minor, first_sector + start,
+ size);
+ minor++;
+ }
+
+ put_dev_sector(sect);
+ return 1;
+}
+#endif
+
+static int (*partfn[])(struct gendisk *, struct block_device *, unsigned long, int) = {
+#ifdef CONFIG_ACORN_PARTITION_ICS
+ adfspart_check_ICS,
+#endif
+#ifdef CONFIG_ACORN_PARTITION_POWERTEC
+ adfspart_check_POWERTEC,
+#endif
+#ifdef CONFIG_ACORN_PARTITION_CUMANA
+ adfspart_check_CUMANA,
+#endif
+#ifdef CONFIG_ACORN_PARTITION_ADFS
+ adfspart_check_ADFS,
+#endif
+ NULL
+};
+/*
+ * Purpose: initialise all the partitions on an ADFS drive.
+ * These may be other ADFS partitions or a Linux/RiscBSD/RISCiX
+ * partition.
+ *
+ * Params : hd - pointer to gendisk structure
+ * dev - device number to access
+ * first_sect - first available sector on the disk.
+ * first_minor - first available minor on this device.
+ *
+ * Returns: -1 on error, 0 if not ADFS format, 1 if ok.
+ */
+int acorn_partition(struct gendisk *hd, struct block_device *bdev,
+ unsigned long first_sect, int first_minor)
+{
+ int i;
+
+ for (i = 0; partfn[i]; i++) {
+ int r = partfn[i](hd, bdev, first_sect, first_minor);
+ if (r) {
+ if (r > 0)
+ printk("\n");
+ return r;
+ }
+ }
+ return 0;
+}
diff --git a/fs/partitions/acorn.h b/fs/partitions/acorn.h
new file mode 100644
index 00000000000000..f4b3b80d383c96
--- /dev/null
+++ b/fs/partitions/acorn.h
@@ -0,0 +1,55 @@
+/*
+ * fs/partitions/acorn.h
+ *
+ * Copyright (C) 1996-1998 Russell King
+ */
+#include <linux/adfs_fs.h>
+
+/*
+ * Partition types. (Oh for reusability)
+ */
+#define PARTITION_RISCIX_MFM 1
+#define PARTITION_RISCIX_SCSI 2
+#define PARTITION_LINUX 9
+
+struct riscix_part {
+ __u32 start;
+ __u32 length;
+ __u32 one;
+ char name[16];
+};
+
+struct riscix_record {
+ __u32 magic;
+#define RISCIX_MAGIC (0x4a657320)
+ __u32 date;
+ struct riscix_part part[8];
+};
+
+#define LINUX_NATIVE_MAGIC 0xdeafa1de
+#define LINUX_SWAP_MAGIC 0xdeafab1e
+
+struct linux_part {
+ __u32 magic;
+ __u32 start_sect;
+ __u32 nr_sects;
+};
+
+struct ics_part {
+ __u32 start;
+ __s32 size;
+};
+
+struct ptec_partition {
+ __u32 unused1;
+ __u32 unused2;
+ __u32 start;
+ __u32 size;
+ __u32 unused5;
+ char type[8];
+};
+
+
+int acorn_partition(struct gendisk *hd, struct block_device *bdev,
+ unsigned long first_sect, int first_minor);
+
diff --git a/fs/partitions/amiga.c b/fs/partitions/amiga.c
new file mode 100644
index 00000000000000..3ecf9c4f88b427
--- /dev/null
+++ b/fs/partitions/amiga.c
@@ -0,0 +1,112 @@
+/*
+ * fs/partitions/amiga.c
+ *
+ * Code extracted from drivers/block/genhd.c
+ *
+ * Copyright (C) 1991-1998 Linus Torvalds
+ * Re-organised Feb 1998 Russell King
+ */
+
+#include <linux/fs.h>
+#include <linux/genhd.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/blk.h>
+
+#include <asm/byteorder.h>
+#include <linux/affs_hardblocks.h>
+
+#include "check.h"
+#include "amiga.h"
+
+static __inline__ u32
+checksum_block(u32 *m, int size)
+{
+ u32 sum = 0;
+
+ while (size--)
+ sum += be32_to_cpu(*m++);
+ return sum;
+}
+
+int
+amiga_partition(struct gendisk *hd, struct block_device *bdev,
+ unsigned long first_sector, int first_part_minor)
+{
+ Sector sect;
+ unsigned char *data;
+ struct RigidDiskBlock *rdb;
+ struct PartitionBlock *pb;
+ int start_sect, nr_sects, blk, part, res = 0;
+ kdev_t dev = to_kdev_t(bdev->bd_dev);
+
+ for (blk = 0; ; blk++, put_dev_sector(sect)) {
+ if (blk == RDB_ALLOCATION_LIMIT)
+ goto rdb_done;
+ data = read_dev_sector(bdev, blk, &sect);
+ if (!data) {
+ if (warn_no_part)
+ printk("Dev %s: unable to read RDB block %d\n",
+ bdevname(dev), blk);
+ goto rdb_done;
+ }
+ if (*(u32 *)data != cpu_to_be32(IDNAME_RIGIDDISK))
+ continue;
+
+ rdb = (struct RigidDiskBlock *)data;
+ if (checksum_block((u32 *)data, be32_to_cpu(rdb->rdb_SummedLongs) & 0x7F) == 0)
+ break;
+ /* Try again with 0xdc..0xdf zeroed, Windows might have
+ * trashed it.
+ */
+ *(u32 *)(data+0xdc) = 0;
+ if (checksum_block((u32 *)data,
+ be32_to_cpu(rdb->rdb_SummedLongs) & 0x7F)==0) {
+ printk("Warning: Trashed word at 0xd0 in block %d "
+ "ignored in checksum calculation\n",blk);
+ break;
+ }
+
+ printk("Dev %s: RDB in block %d has bad checksum\n",
+ bdevname(dev),blk);
+ }
+
+ printk(" RDSK");
+ blk = be32_to_cpu(rdb->rdb_PartitionList);
+ put_dev_sector(sect);
+ for (part = 1; blk>0 && part<=16; part++, put_dev_sector(sect)) {
+ data = read_dev_sector(bdev, blk, &sect);
+ if (!data) {
+ if (warn_no_part)
+ printk("Dev %s: unable to read partition block %d\n",
+ bdevname(dev),blk);
+ goto rdb_done;
+ }
+ pb = (struct PartitionBlock *)data;
+ blk = be32_to_cpu(pb->pb_Next);
+ if (pb->pb_ID != cpu_to_be32(IDNAME_PARTITION))
+ continue;
+ if (checksum_block((u32 *)pb, be32_to_cpu(pb->pb_SummedLongs) & 0x7F) != 0 )
+ continue;
+
+ /* Tell Kernel about it */
+
+ nr_sects = (be32_to_cpu(pb->pb_Environment[10]) + 1 -
+ be32_to_cpu(pb->pb_Environment[9])) *
+ be32_to_cpu(pb->pb_Environment[3]) *
+ be32_to_cpu(pb->pb_Environment[5]);
+ if (!nr_sects)
+ continue;
+ start_sect = be32_to_cpu(pb->pb_Environment[9]) *
+ be32_to_cpu(pb->pb_Environment[3]) *
+ be32_to_cpu(pb->pb_Environment[5]);
+ add_gd_partition(hd,first_part_minor,start_sect,nr_sects);
+ first_part_minor++;
+ res = 1;
+ }
+ printk("\n");
+
+rdb_done:
+ return res;
+}
diff --git a/fs/partitions/amiga.h b/fs/partitions/amiga.h
new file mode 100644
index 00000000000000..4f4f8a2772d970
--- /dev/null
+++ b/fs/partitions/amiga.h
@@ -0,0 +1,8 @@
+/*
+ * fs/partitions/amiga.h
+ */
+
+int
+amiga_partition(struct gendisk *hd, struct block_device *bdev,
+ unsigned long first_sector, int first_part_minor);
+
diff --git a/fs/partitions/atari.c b/fs/partitions/atari.c
new file mode 100644
index 00000000000000..11a7b4a79625e9
--- /dev/null
+++ b/fs/partitions/atari.c
@@ -0,0 +1,162 @@
+/*
+ * fs/partitions/atari.c
+ *
+ * Code extracted from drivers/block/genhd.c
+ *
+ * Copyright (C) 1991-1998 Linus Torvalds
+ * Re-organised Feb 1998 Russell King
+ */
+
+#include <linux/fs.h>
+#include <linux/genhd.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/blk.h>
+#include <linux/ctype.h>
+
+#include <asm/byteorder.h>
+#include <asm/system.h>
+
+#include "check.h"
+#include "atari.h"
+
+/* ++guenther: this should be settable by the user ("make config")?.
+ */
+#define ICD_PARTS
+
+/* check if a partition entry looks valid -- Atari format is assumed if at
+ least one of the primary entries is ok this way */
+#define VALID_PARTITION(pi,hdsiz) \
+ (((pi)->flg & 1) && \
+ isalnum((pi)->id[0]) && isalnum((pi)->id[1]) && isalnum((pi)->id[2]) && \
+ be32_to_cpu((pi)->st) <= (hdsiz) && \
+ be32_to_cpu((pi)->st) + be32_to_cpu((pi)->siz) <= (hdsiz))
+
+static inline int OK_id(char *s)
+{
+ return memcmp (s, "GEM", 3) == 0 || memcmp (s, "BGM", 3) == 0 ||
+ memcmp (s, "LNX", 3) == 0 || memcmp (s, "SWP", 3) == 0 ||
+ memcmp (s, "RAW", 3) == 0 ;
+}
+
+int atari_partition (struct gendisk *hd, struct block_device *bdev,
+ unsigned long first_sector, int minor)
+{
+ int m_lim = minor + hd->max_p;
+ Sector sect;
+ struct rootsector *rs;
+ struct partition_info *pi;
+ u32 extensect;
+ u32 hd_size;
+#ifdef ICD_PARTS
+ int part_fmt = 0; /* 0:unknown, 1:AHDI, 2:ICD/Supra */
+#endif
+
+ rs = (struct rootsector *) read_dev_sector(bdev, 0, &sect);
+ if (!rs)
+ return -1;
+
+ /* Verify this is an Atari rootsector: */
+ hd_size = hd->part[minor - 1].nr_sects;
+ if (!VALID_PARTITION(&rs->part[0], hd_size) &&
+ !VALID_PARTITION(&rs->part[1], hd_size) &&
+ !VALID_PARTITION(&rs->part[2], hd_size) &&
+ !VALID_PARTITION(&rs->part[3], hd_size)) {
+ /*
+ * if there's no valid primary partition, assume that no Atari
+ * format partition table (there's no reliable magic or the like
+ * :-()
+ */
+ put_dev_sector(sect);
+ return 0;
+ }
+
+ pi = &rs->part[0];
+ printk (" AHDI");
+ for (; pi < &rs->part[4] && minor < m_lim; minor++, pi++) {
+ struct rootsector *xrs;
+ Sector sect2;
+ ulong partsect;
+
+ if ( !(pi->flg & 1) )
+ continue;
+ /* active partition */
+ if (memcmp (pi->id, "XGM", 3) != 0) {
+ /* we don't care about other id's */
+ add_gd_partition (hd, minor, be32_to_cpu(pi->st),
+ be32_to_cpu(pi->siz));
+ continue;
+ }
+ /* extension partition */
+#ifdef ICD_PARTS
+ part_fmt = 1;
+#endif
+ printk(" XGM<");
+ partsect = extensect = be32_to_cpu(pi->st);
+ while (1) {
+ xrs = (struct rootsector *)read_dev_sector(bdev, partsect, &sect2);
+ if (!xrs) {
+ printk (" block %ld read failed\n", partsect);
+ put_dev_sector(sect);
+ return 0;
+ }
+
+ /* ++roman: sanity check: bit 0 of flg field must be set */
+ if (!(xrs->part[0].flg & 1)) {
+ printk( "\nFirst sub-partition in extended partition is not valid!\n" );
+ put_dev_sector(sect2);
+ break;
+ }
+
+ add_gd_partition(hd, minor,
+ partsect + be32_to_cpu(xrs->part[0].st),
+ be32_to_cpu(xrs->part[0].siz));
+
+ if (!(xrs->part[1].flg & 1)) {
+ /* end of linked partition list */
+ put_dev_sector(sect2);
+ break;
+ }
+ if (memcmp( xrs->part[1].id, "XGM", 3 ) != 0) {
+ printk("\nID of extended partition is not XGM!\n");
+ put_dev_sector(sect2);
+ break;
+ }
+
+ partsect = be32_to_cpu(xrs->part[1].st) + extensect;
+ put_dev_sector(sect2);
+ minor++;
+ if (minor >= m_lim) {
+ printk( "\nMaximum number of partitions reached!\n" );
+ break;
+ }
+ }
+ printk(" >");
+ }
+#ifdef ICD_PARTS
+ if ( part_fmt!=1 ) { /* no extended partitions -> test ICD-format */
+ pi = &rs->icdpart[0];
+ /* sanity check: no ICD format if first partition invalid */
+ if (OK_id(pi->id)) {
+ printk(" ICD<");
+ for (; pi < &rs->icdpart[8] && minor < m_lim; minor++, pi++) {
+ /* accept only GEM,BGM,RAW,LNX,SWP partitions */
+ if (!((pi->flg & 1) && OK_id(pi->id)))
+ continue;
+ part_fmt = 2;
+ add_gd_partition (hd, minor,
+ be32_to_cpu(pi->st),
+ be32_to_cpu(pi->siz));
+ }
+ printk(" >");
+ }
+ }
+#endif
+ put_dev_sector(sect);
+
+ printk ("\n");
+
+ return 1;
+}
+
diff --git a/fs/partitions/atari.h b/fs/partitions/atari.h
new file mode 100644
index 00000000000000..6a14eb75947d6b
--- /dev/null
+++ b/fs/partitions/atari.h
@@ -0,0 +1,36 @@
+/*
+ * fs/partitions/atari.h
+ * Moved by Russell King from:
+ *
+ * linux/include/linux/atari_rootsec.h
+ * definitions for Atari Rootsector layout
+ * by Andreas Schwab (schwab@ls5.informatik.uni-dortmund.de)
+ *
+ * modified for ICD/Supra partitioning scheme restricted to at most 12
+ * partitions
+ * by Guenther Kelleter (guenther@pool.informatik.rwth-aachen.de)
+ */
+
+struct partition_info
+{
+ u8 flg; /* bit 0: active; bit 7: bootable */
+ char id[3]; /* "GEM", "BGM", "XGM", or other */
+ u32 st; /* start of partition */
+ u32 siz; /* length of partition */
+};
+
+struct rootsector
+{
+ char unused[0x156]; /* room for boot code */
+ struct partition_info icdpart[8]; /* info for ICD-partitions 5..12 */
+ char unused2[0xc];
+ u32 hd_siz; /* size of disk in blocks */
+ struct partition_info part[4];
+ u32 bsl_st; /* start of bad sector list */
+ u32 bsl_cnt; /* length of bad sector list */
+ u16 checksum; /* checksum for bootable disks */
+} __attribute__((__packed__));
+
+int atari_partition (struct gendisk *hd, struct block_device *bdev,
+ unsigned long first_sector, int first_part_minor);
+
diff --git a/fs/partitions/check.c b/fs/partitions/check.c
new file mode 100644
index 00000000000000..323572335d781b
--- /dev/null
+++ b/fs/partitions/check.c
@@ -0,0 +1,439 @@
+/*
+ * Code extracted from drivers/block/genhd.c
+ * Copyright (C) 1991-1998 Linus Torvalds
+ * Re-organised Feb 1998 Russell King
+ *
+ * We now have independent partition support from the
+ * block drivers, which allows all the partition code to
+ * be grouped in one location, and it to be mostly self
+ * contained.
+ *
+ * Added needed MAJORS for new pairs, {hdi,hdj}, {hdk,hdl}
+ */
+
+#include <linux/config.h>
+#include <linux/fs.h>
+#include <linux/genhd.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/blk.h>
+#include <linux/init.h>
+#include <linux/raid/md.h>
+
+#include "check.h"
+
+#include "acorn.h"
+#include "amiga.h"
+#include "atari.h"
+#include "ldm.h"
+#include "mac.h"
+#include "msdos.h"
+#include "osf.h"
+#include "sgi.h"
+#include "sun.h"
+#include "ibm.h"
+#include "ultrix.h"
+#include "efi.h"
+
+extern int *blk_size[];
+
+int warn_no_part = 1; /*This is ugly: should make genhd removable media aware*/
+
+static int (*check_part[])(struct gendisk *hd, struct block_device *bdev, unsigned long first_sect, int first_minor) = {
+#ifdef CONFIG_ACORN_PARTITION
+ acorn_partition,
+#endif
+#ifdef CONFIG_SGI_PARTITION
+ sgi_partition,
+#endif
+#ifdef CONFIG_EFI_PARTITION
+ efi_partition, /* this must come before msdos */
+#endif
+#ifdef CONFIG_LDM_PARTITION
+ ldm_partition, /* this must come before msdos */
+#endif
+#ifdef CONFIG_MSDOS_PARTITION
+ msdos_partition,
+#endif
+#ifdef CONFIG_OSF_PARTITION
+ osf_partition,
+#endif
+#ifdef CONFIG_SUN_PARTITION
+ sun_partition,
+#endif
+#ifdef CONFIG_AMIGA_PARTITION
+ amiga_partition,
+#endif
+#ifdef CONFIG_ATARI_PARTITION
+ atari_partition,
+#endif
+#ifdef CONFIG_MAC_PARTITION
+ mac_partition,
+#endif
+#ifdef CONFIG_ULTRIX_PARTITION
+ ultrix_partition,
+#endif
+#ifdef CONFIG_IBM_PARTITION
+ ibm_partition,
+#endif
+ NULL
+};
+
+/*
+ * This is ucking fugly but its probably the best thing for 2.4.x
+ * Take it as a clear reminder that: 1) we should put the device name
+ * generation in the object kdev_t points to in 2.5.
+ * and 2) ioctls better work on half-opened devices.
+ */
+
+#ifdef CONFIG_ARCH_S390
+int (*genhd_dasd_name)(char*,int,int,struct gendisk*) = NULL;
+int (*genhd_dasd_ioctl)(struct inode *inp, struct file *filp,
+ unsigned int no, unsigned long data);
+EXPORT_SYMBOL(genhd_dasd_name);
+EXPORT_SYMBOL(genhd_dasd_ioctl);
+#endif
+
+/*
+ * disk_name() is used by partition check code and the md driver.
+ * It formats the devicename of the indicated disk into
+ * the supplied buffer (of size at least 32), and returns
+ * a pointer to that same buffer (for convenience).
+ */
+
+char *disk_name (struct gendisk *hd, int minor, char *buf)
+{
+ const char *maj = hd->major_name;
+ unsigned int unit = (minor >> hd->minor_shift);
+ unsigned int part = (minor & ((1 << hd->minor_shift) -1 ));
+
+ if ((unit < hd->nr_real) && hd->part[minor].de) {
+ int pos;
+
+ pos = devfs_generate_path (hd->part[minor].de, buf, 64);
+ if (pos >= 0)
+ return buf + pos;
+ }
+
+#ifdef CONFIG_ARCH_S390
+ if (genhd_dasd_name
+ && genhd_dasd_name (buf, unit, part, hd) == 0)
+ return buf;
+#endif
+ /*
+ * IDE devices use multiple major numbers, but the drives
+ * are named as: {hda,hdb}, {hdc,hdd}, {hde,hdf}, {hdg,hdh}..
+ * This requires special handling here.
+ */
+ switch (hd->major) {
+ case IDE9_MAJOR:
+ unit += 2;
+ case IDE8_MAJOR:
+ unit += 2;
+ case IDE7_MAJOR:
+ unit += 2;
+ case IDE6_MAJOR:
+ unit += 2;
+ case IDE5_MAJOR:
+ unit += 2;
+ case IDE4_MAJOR:
+ unit += 2;
+ case IDE3_MAJOR:
+ unit += 2;
+ case IDE2_MAJOR:
+ unit += 2;
+ case IDE1_MAJOR:
+ unit += 2;
+ case IDE0_MAJOR:
+ maj = "hd";
+ break;
+ case MD_MAJOR:
+ sprintf(buf, "%s%d", maj, unit);
+ return buf;
+ }
+ if (hd->major >= SCSI_DISK1_MAJOR && hd->major <= SCSI_DISK7_MAJOR) {
+ unit = unit + (hd->major - SCSI_DISK1_MAJOR + 1) * 16;
+ if (unit+'a' > 'z') {
+ unit -= 26;
+ sprintf(buf, "sd%c%c", 'a' + unit / 26, 'a' + unit % 26);
+ if (part)
+ sprintf(buf + 4, "%d", part);
+ return buf;
+ }
+ }
+ if (hd->major >= COMPAQ_SMART2_MAJOR && hd->major <= COMPAQ_SMART2_MAJOR+7) {
+ int ctlr = hd->major - COMPAQ_SMART2_MAJOR;
+ if (part == 0)
+ sprintf(buf, "%s/c%dd%d", maj, ctlr, unit);
+ else
+ sprintf(buf, "%s/c%dd%dp%d", maj, ctlr, unit, part);
+ return buf;
+ }
+ if (hd->major >= COMPAQ_CISS_MAJOR && hd->major <= COMPAQ_CISS_MAJOR+7) {
+ int ctlr = hd->major - COMPAQ_CISS_MAJOR;
+ if (part == 0)
+ sprintf(buf, "%s/c%dd%d", maj, ctlr, unit);
+ else
+ sprintf(buf, "%s/c%dd%dp%d", maj, ctlr, unit, part);
+ return buf;
+ }
+ if (hd->major >= DAC960_MAJOR && hd->major <= DAC960_MAJOR+7) {
+ int ctlr = hd->major - DAC960_MAJOR;
+ if (part == 0)
+ sprintf(buf, "%s/c%dd%d", maj, ctlr, unit);
+ else
+ sprintf(buf, "%s/c%dd%dp%d", maj, ctlr, unit, part);
+ return buf;
+ }
+ if (hd->major == ATARAID_MAJOR) {
+ int disk = minor >> hd->minor_shift;
+ int part = minor & (( 1 << hd->minor_shift) - 1);
+ if (part == 0)
+ sprintf(buf, "%s/d%d", maj, disk);
+ else
+ sprintf(buf, "%s/d%dp%d", maj, disk, part);
+ return buf;
+ }
+ if (part)
+ sprintf(buf, "%s%c%d", maj, unit+'a', part);
+ else
+ sprintf(buf, "%s%c", maj, unit+'a');
+ return buf;
+}
+
+/*
+ * Add a partitions details to the devices partition description.
+ */
+void add_gd_partition(struct gendisk *hd, int minor, int start, int size)
+{
+#ifndef CONFIG_DEVFS_FS
+ char buf[40];
+#endif
+
+ hd->part[minor].start_sect = start;
+ hd->part[minor].nr_sects = size;
+#ifdef CONFIG_DEVFS_FS
+ printk(" p%d", (minor & ((1 << hd->minor_shift) - 1)));
+#else
+ if ((hd->major >= COMPAQ_SMART2_MAJOR+0 && hd->major <= COMPAQ_SMART2_MAJOR+7) ||
+ (hd->major >= COMPAQ_CISS_MAJOR+0 && hd->major <= COMPAQ_CISS_MAJOR+7))
+ printk(" p%d", (minor & ((1 << hd->minor_shift) - 1)));
+ else
+ printk(" %s", disk_name(hd, minor, buf));
+#endif
+}
+
+static void check_partition(struct gendisk *hd, kdev_t dev, int first_part_minor)
+{
+ devfs_handle_t de = NULL;
+ static int first_time = 1;
+ unsigned long first_sector;
+ struct block_device *bdev;
+ char buf[64];
+ int i;
+
+ if (first_time)
+ printk(KERN_INFO "Partition check:\n");
+ first_time = 0;
+ first_sector = hd->part[MINOR(dev)].start_sect;
+
+ /*
+ * This is a kludge to allow the partition check to be
+ * skipped for specific drives (e.g. IDE CD-ROM drives)
+ */
+ if ((int)first_sector == -1) {
+ hd->part[MINOR(dev)].start_sect = 0;
+ return;
+ }
+
+ if (hd->de_arr)
+ de = hd->de_arr[MINOR(dev) >> hd->minor_shift];
+ i = devfs_generate_path (de, buf, sizeof buf);
+ if (i >= 0)
+ printk(KERN_INFO " /dev/%s:", buf + i);
+ else
+ printk(KERN_INFO " %s:", disk_name(hd, MINOR(dev), buf));
+ bdev = bdget(kdev_t_to_nr(dev));
+ bdev->bd_inode->i_size = (loff_t)hd->part[MINOR(dev)].nr_sects << 9;
+ bdev->bd_inode->i_blkbits = blksize_bits(block_size(dev));
+ for (i = 0; check_part[i]; i++) {
+ int res;
+ res = check_part[i](hd, bdev, first_sector, first_part_minor);
+ if (res) {
+ if (res < 0 && warn_no_part)
+ printk(" unable to read partition table\n");
+ goto setup_devfs;
+ }
+ }
+
+ printk(" unknown partition table\n");
+setup_devfs:
+ invalidate_bdev(bdev, 1);
+ truncate_inode_pages(bdev->bd_inode->i_mapping, 0);
+ bdput(bdev);
+ i = first_part_minor - 1;
+ devfs_register_partitions (hd, i, hd->sizes ? 0 : 1);
+}
+
+#ifdef CONFIG_DEVFS_FS
+static void devfs_register_partition (struct gendisk *dev, int minor, int part)
+{
+ int devnum = minor >> dev->minor_shift;
+ devfs_handle_t dir;
+ unsigned int devfs_flags = DEVFS_FL_DEFAULT;
+ char devname[16];
+
+ if (dev->part[minor + part].de) return;
+ dir = devfs_get_parent (dev->part[minor].de);
+ if (!dir) return;
+ if ( dev->flags && (dev->flags[devnum] & GENHD_FL_REMOVABLE) )
+ devfs_flags |= DEVFS_FL_REMOVABLE;
+ sprintf (devname, "part%d", part);
+ dev->part[minor + part].de =
+ devfs_register (dir, devname, devfs_flags,
+ dev->major, minor + part,
+ S_IFBLK | S_IRUSR | S_IWUSR,
+ dev->fops, NULL);
+}
+
+static struct unique_numspace disc_numspace = UNIQUE_NUMBERSPACE_INITIALISER;
+
+static void devfs_register_disc (struct gendisk *dev, int minor)
+{
+ int pos = 0;
+ int devnum = minor >> dev->minor_shift;
+ devfs_handle_t dir, slave;
+ unsigned int devfs_flags = DEVFS_FL_DEFAULT;
+ char dirname[64], symlink[16];
+ static devfs_handle_t devfs_handle;
+
+ if (dev->part[minor].de) return;
+ if ( dev->flags && (dev->flags[devnum] & GENHD_FL_REMOVABLE) )
+ devfs_flags |= DEVFS_FL_REMOVABLE;
+ if (dev->de_arr) {
+ dir = dev->de_arr[devnum];
+ if (!dir) /* Aware driver wants to block disc management */
+ return;
+ pos = devfs_generate_path (dir, dirname + 3, sizeof dirname-3);
+ if (pos < 0) return;
+ strncpy (dirname + pos, "../", 3);
+ }
+ else {
+ /* Unaware driver: construct "real" directory */
+ sprintf (dirname, "../%s/disc%d", dev->major_name, devnum);
+ dir = devfs_mk_dir (NULL, dirname + 3, NULL);
+ }
+ if (!devfs_handle)
+ devfs_handle = devfs_mk_dir (NULL, "discs", NULL);
+ dev->part[minor].number = devfs_alloc_unique_number (&disc_numspace);
+ sprintf (symlink, "disc%d", dev->part[minor].number);
+ devfs_mk_symlink (devfs_handle, symlink, DEVFS_FL_DEFAULT,
+ dirname + pos, &slave, NULL);
+ dev->part[minor].de =
+ devfs_register (dir, "disc", devfs_flags, dev->major, minor,
+ S_IFBLK | S_IRUSR | S_IWUSR, dev->fops, NULL);
+ devfs_auto_unregister (dev->part[minor].de, slave);
+ if (!dev->de_arr)
+ devfs_auto_unregister (slave, dir);
+}
+#endif /* CONFIG_DEVFS_FS */
+
+void devfs_register_partitions (struct gendisk *dev, int minor, int unregister)
+{
+#ifdef CONFIG_DEVFS_FS
+ int part;
+
+ if (!unregister)
+ devfs_register_disc (dev, minor);
+ for (part = 1; part < dev->max_p; part++) {
+ if ( unregister || (dev->part[minor].nr_sects < 1) ||
+ (dev->part[part + minor].nr_sects < 1) ) {
+ devfs_unregister (dev->part[part + minor].de);
+ dev->part[part + minor].de = NULL;
+ continue;
+ }
+ devfs_register_partition (dev, minor, part);
+ }
+ if (unregister) {
+ devfs_unregister (dev->part[minor].de);
+ dev->part[minor].de = NULL;
+ devfs_dealloc_unique_number (&disc_numspace,
+ dev->part[minor].number);
+ }
+#endif /* CONFIG_DEVFS_FS */
+}
+
+/*
+ * This function will re-read the partition tables for a given device,
+ * and set things back up again. There are some important caveats,
+ * however. You must ensure that no one is using the device, and no one
+ * can start using the device while this function is being executed.
+ *
+ * Much of the cleanup from the old partition tables should have already been
+ * done
+ */
+
+void register_disk(struct gendisk *gdev, kdev_t dev, unsigned minors,
+ struct block_device_operations *ops, long size)
+{
+ if (!gdev)
+ return;
+ grok_partitions(gdev, MINOR(dev)>>gdev->minor_shift, minors, size);
+}
+
+void grok_partitions(struct gendisk *dev, int drive, unsigned minors, long size)
+{
+ int i;
+ int first_minor = drive << dev->minor_shift;
+ int end_minor = first_minor + dev->max_p;
+
+ if(!dev->sizes)
+ blk_size[dev->major] = NULL;
+
+ dev->part[first_minor].nr_sects = size;
+ /* No such device or no minors to use for partitions */
+ if ( !size && dev->flags && (dev->flags[drive] & GENHD_FL_REMOVABLE) )
+ devfs_register_partitions (dev, first_minor, 0);
+ if (!size || minors == 1)
+ return;
+
+ if (dev->sizes) {
+ dev->sizes[first_minor] = size >> (BLOCK_SIZE_BITS - 9);
+ for (i = first_minor + 1; i < end_minor; i++)
+ dev->sizes[i] = 0;
+ }
+ blk_size[dev->major] = dev->sizes;
+ check_partition(dev, MKDEV(dev->major, first_minor), 1 + first_minor);
+
+ /*
+ * We need to set the sizes array before we will be able to access
+ * any of the partitions on this device.
+ */
+ if (dev->sizes != NULL) { /* optional safeguard in ll_rw_blk.c */
+ for (i = first_minor; i < end_minor; i++)
+ dev->sizes[i] = dev->part[i].nr_sects >> (BLOCK_SIZE_BITS - 9);
+ }
+}
+
+unsigned char *read_dev_sector(struct block_device *bdev, unsigned long n, Sector *p)
+{
+ struct address_space *mapping = bdev->bd_inode->i_mapping;
+ int sect = PAGE_CACHE_SIZE / 512;
+ struct page *page;
+
+ page = read_cache_page(mapping, n/sect,
+ (filler_t *)mapping->a_ops->readpage, NULL);
+ if (!IS_ERR(page)) {
+ wait_on_page(page);
+ if (!Page_Uptodate(page))
+ goto fail;
+ if (PageError(page))
+ goto fail;
+ p->v = page;
+ return (unsigned char *)page_address(page) + 512 * (n % sect);
+fail:
+ page_cache_release(page);
+ }
+ p->v = NULL;
+ return NULL;
+}
diff --git a/fs/partitions/check.h b/fs/partitions/check.h
new file mode 100644
index 00000000000000..32d7940469a406
--- /dev/null
+++ b/fs/partitions/check.h
@@ -0,0 +1,16 @@
+/*
+ * add_partition adds a partitions details to the devices partition
+ * description.
+ */
+void add_gd_partition(struct gendisk *hd, int minor, int start, int size);
+
+typedef struct {struct page *v;} Sector;
+
+unsigned char *read_dev_sector(struct block_device *, unsigned long, Sector *);
+
+static inline void put_dev_sector(Sector p)
+{
+ page_cache_release(p.v);
+}
+
+extern int warn_no_part;
diff --git a/fs/partitions/efi.c b/fs/partitions/efi.c
new file mode 100644
index 00000000000000..8ae4908f0758c4
--- /dev/null
+++ b/fs/partitions/efi.c
@@ -0,0 +1,738 @@
+/************************************************************
+ * EFI GUID Partition Table handling
+ * Per Intel EFI Specification v1.02
+ * http://developer.intel.com/technology/efi/efi.htm
+ * efi.[ch] by Matt Domsch <Matt_Domsch@dell.com>
+ * Copyright 2000,2001,2002 Dell Computer Corporation
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * TODO:
+ *
+ * Changelog:
+ * Wed Mar 27 2002 Matt Domsch <Matt_Domsch@dell.com>
+ * - Ported to 2.5.7-pre1 and 2.4.18
+ * - Applied patch to avoid fault in alternate header handling
+ * - cleaned up find_valid_gpt
+ * - On-disk structure and copy in memory is *always* LE now -
+ * swab fields as needed
+ * - remove print_gpt_header()
+ * - only use first max_p partition entries, to keep the kernel minor number
+ * and partition numbers tied.
+ * - 2.4.18 patch needs own crc32() function - there's no official
+ * lib/crc32.c in 2.4.x.
+ *
+ * Mon Feb 04 2002 Matt Domsch <Matt_Domsch@dell.com>
+ * - Removed __PRIPTR_PREFIX - not being used
+ *
+ * Mon Jan 14 2002 Matt Domsch <Matt_Domsch@dell.com>
+ * - Ported to 2.5.2-pre11 + library crc32 patch Linus applied
+ *
+ * Thu Dec 6 2001 Matt Domsch <Matt_Domsch@dell.com>
+ * - Added compare_gpts().
+ * - moved le_efi_guid_to_cpus() back into this file. GPT is the only
+ * thing that keeps EFI GUIDs on disk.
+ * - Changed gpt structure names and members to be simpler and more Linux-like.
+ *
+ * Wed Oct 17 2001 Matt Domsch <Matt_Domsch@dell.com>
+ * - Removed CONFIG_DEVFS_VOLUMES_UUID code entirely per Martin Wilck
+ *
+ * Wed Oct 10 2001 Matt Domsch <Matt_Domsch@dell.com>
+ * - Changed function comments to DocBook style per Andreas Dilger suggestion.
+ *
+ * Mon Oct 08 2001 Matt Domsch <Matt_Domsch@dell.com>
+ * - Change read_lba() to use the page cache per Al Viro's work.
+ * - print u64s properly on all architectures
+ * - fixed debug_printk(), now Dprintk()
+ *
+ * Mon Oct 01 2001 Matt Domsch <Matt_Domsch@dell.com>
+ * - Style cleanups
+ * - made most functions static
+ * - Endianness addition
+ * - remove test for second alternate header, as it's not per spec,
+ * and is unnecessary. There's now a method to read/write the last
+ * sector of an odd-sized disk from user space. No tools have ever
+ * been released which used this code, so it's effectively dead.
+ * - Per Asit Mallick of Intel, added a test for a valid PMBR.
+ * - Added kernel command line option 'gpt' to override valid PMBR test.
+ *
+ * Wed Jun 6 2001 Martin Wilck <Martin.Wilck@Fujitsu-Siemens.com>
+ * - added devfs volume UUID support (/dev/volumes/uuids) for
+ * mounting file systems by the partition GUID.
+ *
+ * Tue Dec 5 2000 Matt Domsch <Matt_Domsch@dell.com>
+ * - Moved crc32() to linux/lib, added efi_crc32().
+ *
+ * Thu Nov 30 2000 Matt Domsch <Matt_Domsch@dell.com>
+ * - Replaced Intel's CRC32 function with an equivalent
+ * non-license-restricted version.
+ *
+ * Wed Oct 25 2000 Matt Domsch <Matt_Domsch@dell.com>
+ * - Fixed the last_lba() call to return the proper last block
+ *
+ * Thu Oct 12 2000 Matt Domsch <Matt_Domsch@dell.com>
+ * - Thanks to Andries Brouwer for his debugging assistance.
+ * - Code works, detects all the partitions.
+ *
+ ************************************************************/
+#include <linux/config.h>
+#include <linux/fs.h>
+#include <linux/genhd.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/blk.h>
+#include <linux/blkpg.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/init.h>
+#include <linux/crc32.h>
+#include <asm/system.h>
+#include <asm/byteorder.h>
+#include "check.h"
+#include "efi.h"
+
+#if CONFIG_BLK_DEV_MD
+extern void md_autodetect_dev(kdev_t dev);
+#endif
+
+/* Handle printing of 64-bit values */
+/* Borrowed from /usr/include/inttypes.h */
+# if BITS_PER_LONG == 64
+# define __PRI64_PREFIX "l"
+# else
+# define __PRI64_PREFIX "ll"
+# endif
+# define PRIx64 __PRI64_PREFIX "x"
+
+
+#undef EFI_DEBUG
+#ifdef EFI_DEBUG
+#define Dprintk(x...) printk(KERN_DEBUG x)
+#else
+#define Dprintk(x...)
+#endif
+
+/* This allows a kernel command line option 'gpt' to override
+ * the test for invalid PMBR. Not __initdata because reloading
+ * the partition tables happens after init too.
+ */
+static int force_gpt;
+static int __init
+force_gpt_fn(char *str)
+{
+ force_gpt = 1;
+ return 1;
+}
+__setup("gpt", force_gpt_fn);
+
+/**
+ * efi_crc32() - EFI version of crc32 function
+ * @buf: buffer to calculate crc32 of
+ * @len - length of buf
+ *
+ * Description: Returns EFI-style CRC32 value for @buf
+ *
+ * This function uses the little endian Ethernet polynomial
+ * but seeds the function with ~0, and xor's with ~0 at the end.
+ * Note, the EFI Specification, v1.02, has a reference to
+ * Dr. Dobbs Journal, May 1994 (actually it's in May 1992).
+ */
+static inline u32
+efi_crc32(const void *buf, unsigned long len)
+{
+ return (crc32(~0L, buf, len) ^ ~0L);
+}
+
+/**
+ * is_pmbr_valid(): test Protective MBR for validity
+ * @mbr: pointer to a legacy mbr structure
+ *
+ * Description: Returns 1 if PMBR is valid, 0 otherwise.
+ * Validity depends on two things:
+ * 1) MSDOS signature is in the last two bytes of the MBR
+ * 2) One partition of type 0xEE is found
+ */
+static int
+is_pmbr_valid(legacy_mbr *mbr)
+{
+ int i, found = 0, signature = 0;
+ if (!mbr)
+ return 0;
+ signature = (le16_to_cpu(mbr->signature) == MSDOS_MBR_SIGNATURE);
+ for (i = 0; signature && i < 4; i++) {
+ if (mbr->partition_record[i].sys_ind ==
+ EFI_PMBR_OSTYPE_EFI_GPT) {
+ found = 1;
+ break;
+ }
+ }
+ return (signature && found);
+}
+
+/**
+ * last_lba(): return number of last logical block of device
+ * @hd: gendisk with partition list
+ * @bdev: block device
+ *
+ * Description: Returns last LBA value on success, 0 on error.
+ * This is stored (by sd and ide-geometry) in
+ * the part[0] entry for this disk, and is the number of
+ * physical sectors available on the disk.
+ */
+static u64
+last_lba(struct gendisk *hd, struct block_device *bdev)
+{
+ if (!hd || !hd->part || !bdev)
+ return 0;
+ return hd->part[MINOR(to_kdev_t(bdev->bd_dev))].nr_sects - 1;
+}
+
+/**
+ * read_lba(): Read bytes from disk, starting at given LBA
+ * @hd
+ * @bdev
+ * @lba
+ * @buffer
+ * @size_t
+ *
+ * Description: Reads @count bytes from @bdev into @buffer.
+ * Returns number of bytes read on success, 0 on error.
+ */
+static size_t
+read_lba(struct gendisk *hd, struct block_device *bdev, u64 lba,
+ u8 * buffer, size_t count)
+{
+
+ size_t totalreadcount = 0, bytesread = 0;
+ unsigned long blocksize;
+ int i;
+ Sector sect;
+ unsigned char *data = NULL;
+
+ if (!hd || !bdev || !buffer || !count)
+ return 0;
+
+ blocksize = get_hardsect_size(to_kdev_t(bdev->bd_dev));
+ if (!blocksize)
+ blocksize = 512;
+
+ for (i = 0; count > 0; i++) {
+ data = read_dev_sector(bdev, lba, &sect);
+ if (!data)
+ return totalreadcount;
+
+ bytesread =
+ PAGE_CACHE_SIZE - (data -
+ (unsigned char *) page_address(sect.v));
+ bytesread = min(bytesread, count);
+ memcpy(buffer, data, bytesread);
+ put_dev_sector(sect);
+
+ buffer += bytesread;
+ totalreadcount += bytesread;
+ count -= bytesread;
+ lba += (bytesread / blocksize);
+ }
+ return totalreadcount;
+}
+
+
+/**
+ * alloc_read_gpt_entries(): reads partition entries from disk
+ * @hd
+ * @bdev
+ * @gpt - GPT header
+ *
+ * Description: Returns ptes on success, NULL on error.
+ * Allocates space for PTEs based on information found in @gpt.
+ * Notes: remember to free pte when you're done!
+ */
+static gpt_entry *
+alloc_read_gpt_entries(struct gendisk *hd,
+ struct block_device *bdev, gpt_header *gpt)
+{
+ size_t count;
+ gpt_entry *pte;
+ if (!hd || !bdev || !gpt)
+ return NULL;
+
+ count = le32_to_cpu(gpt->num_partition_entries) *
+ le32_to_cpu(gpt->sizeof_partition_entry);
+ if (!count)
+ return NULL;
+ pte = kmalloc(count, GFP_KERNEL);
+ if (!pte)
+ return NULL;
+ memset(pte, 0, count);
+
+ if (read_lba(hd, bdev, le64_to_cpu(gpt->partition_entry_lba),
+ (u8 *) pte,
+ count) < count) {
+ kfree(pte);
+ pte=NULL;
+ return NULL;
+ }
+ return pte;
+}
+
+/**
+ * alloc_read_gpt_header(): Allocates GPT header, reads into it from disk
+ * @hd
+ * @bdev
+ * @lba is the Logical Block Address of the partition table
+ *
+ * Description: returns GPT header on success, NULL on error. Allocates
+ * and fills a GPT header starting at @ from @bdev.
+ * Note: remember to free gpt when finished with it.
+ */
+static gpt_header *
+alloc_read_gpt_header(struct gendisk *hd, struct block_device *bdev, u64 lba)
+{
+ gpt_header *gpt;
+ if (!hd || !bdev)
+ return NULL;
+
+ gpt = kmalloc(sizeof (gpt_header), GFP_KERNEL);
+ if (!gpt)
+ return NULL;
+ memset(gpt, 0, sizeof (gpt_header));
+
+ if (read_lba(hd, bdev, lba, (u8 *) gpt,
+ sizeof (gpt_header)) < sizeof (gpt_header)) {
+ kfree(gpt);
+ gpt=NULL;
+ return NULL;
+ }
+
+ return gpt;
+}
+
+/**
+ * is_gpt_valid() - tests one GPT header and PTEs for validity
+ * @hd
+ * @bdev
+ * @lba is the logical block address of the GPT header to test
+ * @gpt is a GPT header ptr, filled on return.
+ * @ptes is a PTEs ptr, filled on return.
+ *
+ * Description: returns 1 if valid, 0 on error.
+ * If valid, returns pointers to newly allocated GPT header and PTEs.
+ */
+static int
+is_gpt_valid(struct gendisk *hd, struct block_device *bdev, u64 lba,
+ gpt_header **gpt, gpt_entry **ptes)
+{
+ u32 crc, origcrc;
+
+ if (!hd || !bdev || !gpt || !ptes)
+ return 0;
+ if (!(*gpt = alloc_read_gpt_header(hd, bdev, lba)))
+ return 0;
+
+ /* Check the GUID Partition Table signature */
+ if (le64_to_cpu((*gpt)->signature) != GPT_HEADER_SIGNATURE) {
+ Dprintk("GUID Partition Table Header signature is wrong: %"
+ PRIx64 " != %" PRIx64 "\n", le64_to_cpu((*gpt)->signature),
+ GPT_HEADER_SIGNATURE);
+ kfree(*gpt);
+ *gpt = NULL;
+ return 0;
+ }
+
+ /* Check the GUID Partition Table CRC */
+ origcrc = le32_to_cpu((*gpt)->header_crc32);
+ (*gpt)->header_crc32 = 0;
+ crc = efi_crc32((const unsigned char *) (*gpt), le32_to_cpu((*gpt)->header_size));
+
+ if (crc != origcrc) {
+ Dprintk
+ ("GUID Partition Table Header CRC is wrong: %x != %x\n",
+ crc, origcrc);
+ kfree(*gpt);
+ *gpt = NULL;
+ return 0;
+ }
+ (*gpt)->header_crc32 = cpu_to_le32(origcrc);
+
+ /* Check that the my_lba entry points to the LBA that contains
+ * the GUID Partition Table */
+ if (le64_to_cpu((*gpt)->my_lba) != lba) {
+ Dprintk("GPT my_lba incorrect: %" PRIx64 " != %" PRIx64 "\n",
+ le64_to_cpu((*gpt)->my_lba), lba);
+ kfree(*gpt);
+ *gpt = NULL;
+ return 0;
+ }
+
+ if (!(*ptes = alloc_read_gpt_entries(hd, bdev, *gpt))) {
+ kfree(*gpt);
+ *gpt = NULL;
+ return 0;
+ }
+
+ /* Check the GUID Partition Entry Array CRC */
+ crc = efi_crc32((const unsigned char *) (*ptes),
+ le32_to_cpu((*gpt)->num_partition_entries) *
+ le32_to_cpu((*gpt)->sizeof_partition_entry));
+
+ if (crc != le32_to_cpu((*gpt)->partition_entry_array_crc32)) {
+ Dprintk("GUID Partitition Entry Array CRC check failed.\n");
+ kfree(*gpt);
+ *gpt = NULL;
+ kfree(*ptes);
+ *ptes = NULL;
+ return 0;
+ }
+
+ /* We're done, all's well */
+ return 1;
+}
+
+/**
+ * compare_gpts() - Search disk for valid GPT headers and PTEs
+ * @pgpt is the primary GPT header
+ * @agpt is the alternate GPT header
+ * @lastlba is the last LBA number
+ * Description: Returns nothing. Sanity checks pgpt and agpt fields
+ * and prints warnings on discrepancies.
+ *
+ */
+static void
+compare_gpts(gpt_header *pgpt, gpt_header *agpt, u64 lastlba)
+{
+ int error_found = 0;
+ if (!pgpt || !agpt)
+ return;
+ if (le64_to_cpu(pgpt->my_lba) != le64_to_cpu(agpt->alternate_lba)) {
+ printk(KERN_WARNING
+ "GPT:Primary header LBA != Alt. header alternate_lba\n");
+ printk(KERN_WARNING "GPT:%" PRIx64 " != %" PRIx64 "\n",
+ le64_to_cpu(pgpt->my_lba),
+ le64_to_cpu(agpt->alternate_lba));
+ error_found++;
+ }
+ if (le64_to_cpu(pgpt->alternate_lba) != le64_to_cpu(agpt->my_lba)) {
+ printk(KERN_WARNING
+ "GPT:Primary header alternate_lba != Alt. header my_lba\n");
+ printk(KERN_WARNING "GPT:%" PRIx64 " != %" PRIx64 "\n",
+ le64_to_cpu(pgpt->alternate_lba),
+ le64_to_cpu(agpt->my_lba));
+ error_found++;
+ }
+ if (le64_to_cpu(pgpt->first_usable_lba) !=
+ le64_to_cpu(agpt->first_usable_lba)) {
+ printk(KERN_WARNING "GPT:first_usable_lbas don't match.\n");
+ printk(KERN_WARNING "GPT:%" PRIx64 " != %" PRIx64 "\n",
+ le64_to_cpu(pgpt->first_usable_lba),
+ le64_to_cpu(agpt->first_usable_lba));
+ error_found++;
+ }
+ if (le64_to_cpu(pgpt->last_usable_lba) !=
+ le64_to_cpu(agpt->last_usable_lba)) {
+ printk(KERN_WARNING "GPT:last_usable_lbas don't match.\n");
+ printk(KERN_WARNING "GPT:%" PRIx64 " != %" PRIx64 "\n",
+ le64_to_cpu(pgpt->last_usable_lba),
+ le64_to_cpu(agpt->last_usable_lba));
+ error_found++;
+ }
+ if (efi_guidcmp(pgpt->disk_guid, agpt->disk_guid)) {
+ printk(KERN_WARNING "GPT:disk_guids don't match.\n");
+ error_found++;
+ }
+ if (le32_to_cpu(pgpt->num_partition_entries) !=
+ le32_to_cpu(agpt->num_partition_entries)) {
+ printk(KERN_WARNING "GPT:num_partition_entries don't match: "
+ "0x%x != 0x%x\n",
+ le32_to_cpu(pgpt->num_partition_entries),
+ le32_to_cpu(agpt->num_partition_entries));
+ error_found++;
+ }
+ if (le32_to_cpu(pgpt->sizeof_partition_entry) !=
+ le32_to_cpu(agpt->sizeof_partition_entry)) {
+ printk(KERN_WARNING
+ "GPT:sizeof_partition_entry values don't match: "
+ "0x%x != 0x%x\n",
+ le32_to_cpu(pgpt->sizeof_partition_entry),
+ le32_to_cpu(agpt->sizeof_partition_entry));
+ error_found++;
+ }
+ if (le32_to_cpu(pgpt->partition_entry_array_crc32) !=
+ le32_to_cpu(agpt->partition_entry_array_crc32)) {
+ printk(KERN_WARNING
+ "GPT:partition_entry_array_crc32 values don't match: "
+ "0x%x != 0x%x\n",
+ le32_to_cpu(pgpt->partition_entry_array_crc32),
+ le32_to_cpu(agpt->partition_entry_array_crc32));
+ error_found++;
+ }
+ if (le64_to_cpu(pgpt->alternate_lba) != lastlba) {
+ printk(KERN_WARNING
+ "GPT:Primary header thinks Alt. header is not at the end of the disk.\n");
+ printk(KERN_WARNING "GPT:%" PRIx64 " != %" PRIx64 "\n",
+ le64_to_cpu(pgpt->alternate_lba), lastlba);
+ error_found++;
+ }
+
+ if (le64_to_cpu(agpt->my_lba) != lastlba) {
+ printk(KERN_WARNING
+ "GPT:Alternate GPT header not at the end of the disk.\n");
+ printk(KERN_WARNING "GPT:%" PRIx64 " != %" PRIx64 "\n",
+ le64_to_cpu(agpt->my_lba), lastlba);
+ error_found++;
+ }
+
+ if (error_found)
+ printk(KERN_WARNING
+ "GPT: Use GNU Parted to correct GPT errors.\n");
+ return;
+}
+
+/**
+ * find_valid_gpt() - Search disk for valid GPT headers and PTEs
+ * @hd
+ * @bdev
+ * @gpt is a GPT header ptr, filled on return.
+ * @ptes is a PTEs ptr, filled on return.
+ * Description: Returns 1 if valid, 0 on error.
+ * If valid, returns pointers to newly allocated GPT header and PTEs.
+ * Validity depends on finding either the Primary GPT header and PTEs valid,
+ * or the Alternate GPT header and PTEs valid, and the PMBR valid.
+ */
+static int
+find_valid_gpt(struct gendisk *hd, struct block_device *bdev,
+ gpt_header **gpt, gpt_entry **ptes)
+{
+ int good_pgpt = 0, good_agpt = 0, good_pmbr = 0;
+ gpt_header *pgpt = NULL, *agpt = NULL;
+ gpt_entry *pptes = NULL, *aptes = NULL;
+ legacy_mbr *legacymbr = NULL;
+ u64 lastlba;
+ if (!hd || !bdev || !gpt || !ptes)
+ return 0;
+
+ lastlba = last_lba(hd, bdev);
+ good_pgpt = is_gpt_valid(hd, bdev, GPT_PRIMARY_PARTITION_TABLE_LBA,
+ &pgpt, &pptes);
+ if (good_pgpt) {
+ good_agpt = is_gpt_valid(hd, bdev,
+ le64_to_cpu(pgpt->alternate_lba),
+ &agpt, &aptes);
+ if (!good_agpt) {
+ good_agpt = is_gpt_valid(hd, bdev, lastlba,
+ &agpt, &aptes);
+ }
+ }
+ else {
+ good_agpt = is_gpt_valid(hd, bdev, lastlba,
+ &agpt, &aptes);
+ }
+
+ /* The obviously unsuccessful case */
+ if (!good_pgpt && !good_agpt) {
+ goto fail;
+ }
+
+ /* This will be added to the EFI Spec. per Intel after v1.02. */
+ legacymbr = kmalloc(sizeof (*legacymbr), GFP_KERNEL);
+ if (legacymbr) {
+ memset(legacymbr, 0, sizeof (*legacymbr));
+ read_lba(hd, bdev, 0, (u8 *) legacymbr,
+ sizeof (*legacymbr));
+ good_pmbr = is_pmbr_valid(legacymbr);
+ kfree(legacymbr);
+ legacymbr=NULL;
+ }
+
+ /* Failure due to bad PMBR */
+ if ((good_pgpt || good_agpt) && !good_pmbr && !force_gpt) {
+ printk(KERN_WARNING
+ " Warning: Disk has a valid GPT signature "
+ "but invalid PMBR.\n");
+ printk(KERN_WARNING
+ " Assuming this disk is *not* a GPT disk anymore.\n");
+ printk(KERN_WARNING
+ " Use gpt kernel option to override. "
+ "Use GNU Parted to correct disk.\n");
+ goto fail;
+ }
+
+ /* Would fail due to bad PMBR, but force GPT anyhow */
+ if ((good_pgpt || good_agpt) && !good_pmbr && force_gpt) {
+ printk(KERN_WARNING
+ " Warning: Disk has a valid GPT signature but "
+ "invalid PMBR.\n");
+ printk(KERN_WARNING
+ " Use GNU Parted to correct disk.\n");
+ printk(KERN_WARNING
+ " gpt option taken, disk treated as GPT.\n");
+ }
+
+ compare_gpts(pgpt, agpt, lastlba);
+
+ /* The good cases */
+ if (good_pgpt && (good_pmbr || force_gpt)) {
+ *gpt = pgpt;
+ *ptes = pptes;
+ if (agpt) { kfree(agpt); agpt = NULL; }
+ if (aptes) { kfree(aptes); aptes = NULL; }
+ if (!good_agpt) {
+ printk(KERN_WARNING
+ "Alternate GPT is invalid, "
+ "using primary GPT.\n");
+ }
+ return 1;
+ }
+ else if (good_agpt && (good_pmbr || force_gpt)) {
+ *gpt = agpt;
+ *ptes = aptes;
+ if (pgpt) { kfree(pgpt); pgpt = NULL; }
+ if (pptes) { kfree(pptes); pptes = NULL; }
+ printk(KERN_WARNING
+ "Primary GPT is invalid, using alternate GPT.\n");
+ return 1;
+ }
+
+ fail:
+ if (pgpt) { kfree(pgpt); pgpt=NULL; }
+ if (agpt) { kfree(agpt); agpt=NULL; }
+ if (pptes) { kfree(pptes); pptes=NULL; }
+ if (aptes) { kfree(aptes); aptes=NULL; }
+ *gpt = NULL;
+ *ptes = NULL;
+ return 0;
+}
+
+/**
+ * add_gpt_partitions(struct gendisk *hd, struct block_device *bdev,
+ * @hd
+ * @bdev
+ *
+ * Description: Create devices for each entry in the GUID Partition Table
+ * Entries.
+ *
+ * We do not create a Linux partition for GPT, but
+ * only for the actual data partitions.
+ * Returns:
+ * -1 if unable to read the partition table
+ * 0 if this isn't our partition table
+ * 1 if successful
+ *
+ */
+static int
+add_gpt_partitions(struct gendisk *hd, struct block_device *bdev, int nextminor)
+{
+ gpt_header *gpt = NULL;
+ gpt_entry *ptes = NULL;
+ u32 i;
+ int max_p;
+
+ if (!hd || !bdev)
+ return -1;
+
+ if (!find_valid_gpt(hd, bdev, &gpt, &ptes) || !gpt || !ptes) {
+ if (gpt) {
+ kfree(gpt);
+ gpt = NULL;
+ }
+ if (ptes) {
+ kfree(ptes);
+ ptes = NULL;
+ }
+ return 0;
+ }
+
+ Dprintk("GUID Partition Table is valid! Yea!\n");
+
+ max_p = (1 << hd->minor_shift) - 1;
+ for (i = 0; i < le32_to_cpu(gpt->num_partition_entries) && i < max_p; i++) {
+ if (!efi_guidcmp(ptes[i].partition_type_guid, NULL_GUID))
+ continue;
+
+ add_gd_partition(hd, nextminor+i,
+ le64_to_cpu(ptes[i].starting_lba),
+ (le64_to_cpu(ptes[i].ending_lba) -
+ le64_to_cpu(ptes[i].starting_lba) +
+ 1));
+
+ /* If there's this is a RAID volume, tell md */
+#if CONFIG_BLK_DEV_MD
+ if (!efi_guidcmp(ptes[i].partition_type_guid,
+ PARTITION_LINUX_RAID_GUID)) {
+ md_autodetect_dev(MKDEV
+ (MAJOR(to_kdev_t(bdev->bd_dev)),
+ nextminor+i));
+ }
+#endif
+ }
+ kfree(ptes);
+ ptes=NULL;
+ kfree(gpt);
+ gpt=NULL;
+ printk("\n");
+ return 1;
+}
+
+/**
+ * efi_partition(): EFI GPT partition handling entry function
+ * @hd
+ * @bdev
+ * @first_sector: unused
+ * @first_part_minor: minor number assigned to first GPT partition found
+ *
+ * Description: called from check.c, if the disk contains GPT
+ * partitions, sets up partition entries in the kernel.
+ *
+ * If the first block on the disk is a legacy MBR,
+ * it will get handled by msdos_partition().
+ * If it's a Protective MBR, we'll handle it here.
+ *
+ * set_blocksize() calls are necessary to be able to read
+ * a disk with an odd number of 512-byte sectors, as the
+ * default BLOCK_SIZE of 1024 bytes won't let that last
+ * sector be read otherwise.
+ *
+ * Returns:
+ * -1 if unable to read the partition table
+ * 0 if this isn't our partitoin table
+ * 1 if successful
+ */
+int
+efi_partition(struct gendisk *hd, struct block_device *bdev,
+ unsigned long first_sector, int first_part_minor)
+{
+
+ kdev_t dev = to_kdev_t(bdev->bd_dev);
+ int hardblocksize = get_hardsect_size(dev);
+ int orig_blksize_size = BLOCK_SIZE;
+ int rc = 0;
+
+ /* Need to change the block size that the block layer uses */
+ if (blksize_size[MAJOR(dev)]) {
+ orig_blksize_size = blksize_size[MAJOR(dev)][MINOR(dev)];
+ }
+
+ if (orig_blksize_size != hardblocksize)
+ set_blocksize(dev, hardblocksize);
+
+ rc = add_gpt_partitions(hd, bdev, first_part_minor);
+
+ /* change back */
+ if (orig_blksize_size != hardblocksize)
+ set_blocksize(dev, orig_blksize_size);
+
+ return rc;
+}
diff --git a/fs/partitions/efi.h b/fs/partitions/efi.h
new file mode 100644
index 00000000000000..fcf586278f78a5
--- /dev/null
+++ b/fs/partitions/efi.h
@@ -0,0 +1,115 @@
+/************************************************************
+ * EFI GUID Partition Table
+ * Per Intel EFI Specification v1.02
+ * http://developer.intel.com/technology/efi/efi.htm
+ *
+ * By Matt Domsch <Matt_Domsch@dell.com> Fri Sep 22 22:15:56 CDT 2000
+ * Copyright 2000,2001 Dell Computer Corporation
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ ************************************************************/
+
+#ifndef FS_PART_EFI_H_INCLUDED
+#define FS_PART_EFI_H_INCLUDED
+
+#include <linux/types.h>
+#include <linux/config.h>
+#include <linux/fs.h>
+#include <linux/genhd.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/blk.h>
+#include <linux/efi.h>
+
+#define MSDOS_MBR_SIGNATURE 0xaa55
+#define EFI_PMBR_OSTYPE_EFI 0xEF
+#define EFI_PMBR_OSTYPE_EFI_GPT 0xEE
+
+#define GPT_BLOCK_SIZE 512
+#define GPT_HEADER_SIGNATURE 0x5452415020494645ULL
+#define GPT_HEADER_REVISION_V1 0x00010000
+#define GPT_PRIMARY_PARTITION_TABLE_LBA 1
+
+#define PARTITION_SYSTEM_GUID \
+ EFI_GUID( 0xC12A7328, 0xF81F, 0x11d2, \
+ 0xBA, 0x4B, 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B)
+#define LEGACY_MBR_PARTITION_GUID \
+ EFI_GUID( 0x024DEE41, 0x33E7, 0x11d3, \
+ 0x9D, 0x69, 0x00, 0x08, 0xC7, 0x81, 0xF3, 0x9F)
+#define PARTITION_MSFT_RESERVED_GUID \
+ EFI_GUID( 0xE3C9E316, 0x0B5C, 0x4DB8, \
+ 0x81, 0x7D, 0xF9, 0x2D, 0xF0, 0x02, 0x15, 0xAE)
+#define PARTITION_BASIC_DATA_GUID \
+ EFI_GUID( 0xEBD0A0A2, 0xB9E5, 0x4433, \
+ 0x87, 0xC0, 0x68, 0xB6, 0xB7, 0x26, 0x99, 0xC7)
+#define PARTITION_LINUX_RAID_GUID \
+ EFI_GUID( 0xa19d880f, 0x05fc, 0x4d3b, \
+ 0xa0, 0x06, 0x74, 0x3f, 0x0f, 0x84, 0x91, 0x1e)
+#define PARTITION_LINUX_SWAP_GUID \
+ EFI_GUID( 0x0657fd6d, 0xa4ab, 0x43c4, \
+ 0x84, 0xe5, 0x09, 0x33, 0xc8, 0x4b, 0x4f, 0x4f)
+#define PARTITION_LINUX_LVM_GUID \
+ EFI_GUID( 0xe6d6d379, 0xf507, 0x44c2, \
+ 0xa2, 0x3c, 0x23, 0x8f, 0x2a, 0x3d, 0xf9, 0x28)
+
+typedef struct _gpt_header {
+ u64 signature;
+ u32 revision;
+ u32 header_size;
+ u32 header_crc32;
+ u32 reserved1;
+ u64 my_lba;
+ u64 alternate_lba;
+ u64 first_usable_lba;
+ u64 last_usable_lba;
+ efi_guid_t disk_guid;
+ u64 partition_entry_lba;
+ u32 num_partition_entries;
+ u32 sizeof_partition_entry;
+ u32 partition_entry_array_crc32;
+ u8 reserved2[GPT_BLOCK_SIZE - 92];
+} __attribute__ ((packed)) gpt_header;
+
+typedef struct _gpt_entry_attributes {
+ u64 required_to_function:1;
+ u64 reserved:47;
+ u64 type_guid_specific:16;
+} __attribute__ ((packed)) gpt_entry_attributes;
+
+typedef struct _gpt_entry {
+ efi_guid_t partition_type_guid;
+ efi_guid_t unique_partition_guid;
+ u64 starting_lba;
+ u64 ending_lba;
+ gpt_entry_attributes attributes;
+ efi_char16_t partition_name[72 / sizeof (efi_char16_t)];
+} __attribute__ ((packed)) gpt_entry;
+
+typedef struct _legacy_mbr {
+ u8 boot_code[440];
+ u32 unique_mbr_signature;
+ u16 unknown;
+ struct partition partition_record[4];
+ u16 signature;
+} __attribute__ ((packed)) legacy_mbr;
+
+/* Functions */
+extern int
+ efi_partition(struct gendisk *hd, struct block_device *bdev,
+ unsigned long first_sector, int first_part_minor);
+
+#endif
diff --git a/fs/partitions/ibm.c b/fs/partitions/ibm.c
new file mode 100644
index 00000000000000..bdcdd59f9c8229
--- /dev/null
+++ b/fs/partitions/ibm.c
@@ -0,0 +1,238 @@
+/*
+ * File...........: linux/fs/partitions/ibm.c
+ * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
+ * Volker Sameske <sameske@de.ibm.com>
+ * Bugreports.to..: <Linux390@de.ibm.com>
+ * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
+
+ * History of changes (starts July 2000)
+ * 07/10/00 Fixed detection of CMS formatted disks
+ * 02/13/00 VTOC partition support added
+ * 12/27/01 fixed PL030593 (CMS reserved minidisk not detected on 64 bit)
+ * 07/24/03 no longer using contents of freed page for CMS label recognition (BZ3611)
+ */
+
+#include <linux/config.h>
+#include <linux/fs.h>
+#include <linux/genhd.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/blk.h>
+#include <linux/slab.h>
+#include <linux/hdreg.h>
+#include <linux/ioctl.h>
+#include <linux/version.h>
+#include <asm/ebcdic.h>
+#include <asm/uaccess.h>
+#include <asm/dasd.h>
+
+#include "ibm.h"
+#include "check.h"
+#include <asm/vtoc.h>
+
+/*
+ * compute the block number from a
+ * cyl-cyl-head-head structure
+ */
+static inline int
+cchh2blk (cchh_t *ptr, struct hd_geometry *geo) {
+ return ptr->cc * geo->heads * geo->sectors +
+ ptr->hh * geo->sectors;
+}
+
+
+/*
+ * compute the block number from a
+ * cyl-cyl-head-head-block structure
+ */
+static inline int
+cchhb2blk (cchhb_t *ptr, struct hd_geometry *geo) {
+ return ptr->cc * geo->heads * geo->sectors +
+ ptr->hh * geo->sectors +
+ ptr->b;
+}
+
+/*
+ * We used to use ioctl_by_bdev in early 2.4, but it broke
+ * between 2.4.9 and 2.4.18 somewhere.
+ */
+extern int (*genhd_dasd_ioctl)(struct inode *inp, struct file *filp,
+ unsigned int no, unsigned long data);
+
+static int
+ibm_ioctl_unopened(struct block_device *bdev, unsigned cmd, unsigned long arg)
+{
+ int res;
+ mm_segment_t old_fs = get_fs();
+
+ if (genhd_dasd_ioctl == NULL)
+ return -ENODEV;
+#if 0
+ lock_kernel();
+ if (bd_ops->owner)
+ __MOD_INC_USE_COUNT(bdev->bd_op->owner);
+ unlock_kernel();
+#endif
+ set_fs(KERNEL_DS);
+ res = (*genhd_dasd_ioctl)(bdev->bd_inode, NULL, cmd, arg);
+ set_fs(old_fs);
+#if 0
+ lock_kernel();
+ if (bd_ops->owner)
+ __MOD_DEV_USE_COUNT(bd_ops->owner);
+ unlock_kernel();
+#endif
+ return res;
+}
+
+/*
+ */
+int
+ibm_partition(struct gendisk *hd, struct block_device *bdev,
+ unsigned long first_sector, int first_part_minor)
+{
+ int blocksize, offset, size;
+ dasd_information_t *info;
+ struct hd_geometry *geo;
+ char type[5] = {0,};
+ char name[7] = {0,};
+ volume_label_t *vlabel;
+ unsigned char *data;
+ Sector sect;
+
+ if ( first_sector != 0 )
+ BUG();
+
+ if ((info = kmalloc(sizeof(dasd_information_t), GFP_KERNEL)) == NULL)
+ goto out_noinfo;
+ if ((geo = kmalloc(sizeof(struct hd_geometry), GFP_KERNEL)) == NULL)
+ goto out_nogeo;
+ if ((vlabel = kmalloc(sizeof(volume_label_t), GFP_KERNEL)) == NULL)
+ goto out_novlab;
+
+ if (ibm_ioctl_unopened(bdev, BIODASDINFO, (unsigned long)info) != 0 ||
+ ibm_ioctl_unopened(bdev, HDIO_GETGEO, (unsigned long)geo) != 0)
+ goto out_noioctl;
+
+ if ((blocksize = get_hardsect_size(to_kdev_t(bdev->bd_dev))) <= 0)
+ goto out_badsect;
+
+ /*
+ * Get volume label, extract name and type.
+ */
+ data = read_dev_sector(bdev, info->label_block*(blocksize/512), &sect);
+ if (data == NULL)
+ goto out_readerr;
+ strncpy (type, data, 4);
+ if ((!info->FBA_layout) && (!strcmp(info->type, "ECKD")))
+ strncpy(name, data + 8, 6);
+ else
+ strncpy(name, data + 4, 6);
+ memcpy (vlabel, data, sizeof(volume_label_t));
+ put_dev_sector(sect);
+
+ EBCASC(type, 4);
+ EBCASC(name, 6);
+
+ /*
+ * Three different types: CMS1, VOL1 and LNX1/unlabeled
+ */
+ if (strncmp(type, "CMS1", 4) == 0) {
+ /*
+ * VM style CMS1 labeled disk
+ */
+ int *label = (int *) vlabel;
+
+ if (label[13] != 0) {
+ printk("CMS1/%8s(MDSK):", name);
+ /* disk is reserved minidisk */
+ blocksize = label[3];
+ offset = label[13];
+ size = (label[7] - 1)*(blocksize >> 9);
+ } else {
+ printk("CMS1/%8s:", name);
+ offset = (info->label_block + 1);
+ size = bdev->bd_inode->i_size >> 9;
+ }
+ // add_gd_partition(hd, first_part_minor - 1, 0, size);
+ add_gd_partition(hd, first_part_minor,
+ offset*(blocksize >> 9),
+ size-offset*(blocksize >> 9));
+ } else if ((strncmp(type, "VOL1", 4) == 0) &&
+ (!info->FBA_layout) && (!strcmp(info->type, "ECKD"))) {
+ /*
+ * New style VOL1 labeled disk
+ */
+ unsigned int blk;
+ int counter;
+
+ printk("VOL1/%8s:", name);
+
+ /* get block number and read then go through format1 labels */
+ blk = cchhb2blk(&vlabel->vtoc, geo) + 1;
+ counter = 0;
+ while ((data = read_dev_sector(bdev, blk*(blocksize/512),
+ &sect)) != NULL) {
+ format1_label_t f1;
+
+ memcpy(&f1, data, sizeof(format1_label_t));
+ put_dev_sector(sect);
+
+ /* skip FMT4 / FMT5 / FMT7 labels */
+ if (f1.DS1FMTID == _ascebc['4']
+ || f1.DS1FMTID == _ascebc['5']
+ || f1.DS1FMTID == _ascebc['7']) {
+ blk++;
+ continue;
+ }
+
+ /* only FMT1 valid at this point */
+ if (f1.DS1FMTID != _ascebc['1'])
+ break;
+
+ /* OK, we got valid partition data */
+ offset = cchh2blk(&f1.DS1EXT1.llimit, geo);
+ size = cchh2blk(&f1.DS1EXT1.ulimit, geo) -
+ offset + geo->sectors;
+ if (counter >= hd->max_p)
+ break;
+ add_gd_partition(hd, first_part_minor + counter,
+ offset * (blocksize >> 9),
+ size * (blocksize >> 9));
+ counter++;
+ blk++;
+ }
+ } else {
+ /*
+ * Old style LNX1 or unlabeled disk
+ */
+ if (strncmp(type, "LNX1", 4) == 0)
+ printk ("LNX1/%8s:", name);
+ else
+ printk("(nonl)/%8s:", name);
+ offset = (info->label_block + 1);
+ size = (bdev->bd_inode->i_size >> 9);
+ // add_gd_partition(hd, first_part_minor - 1, 0, size);
+ add_gd_partition(hd, first_part_minor,
+ offset*(blocksize >> 9),
+ size-offset*(blocksize >> 9));
+ }
+
+ printk("\n");
+ kfree(vlabel);
+ kfree(geo);
+ kfree(info);
+ return 1;
+
+out_readerr:
+out_badsect:
+out_noioctl:
+ kfree(vlabel);
+out_novlab:
+ kfree(geo);
+out_nogeo:
+ kfree(info);
+out_noinfo:
+ return 0;
+}
diff --git a/fs/partitions/ibm.h b/fs/partitions/ibm.h
new file mode 100644
index 00000000000000..08b05ed151feb5
--- /dev/null
+++ b/fs/partitions/ibm.h
@@ -0,0 +1 @@
+int ibm_partition(struct gendisk *, struct block_device *, unsigned long, int);
diff --git a/fs/partitions/ldm.c b/fs/partitions/ldm.c
new file mode 100644
index 00000000000000..d94155b613e2c0
--- /dev/null
+++ b/fs/partitions/ldm.c
@@ -0,0 +1,1545 @@
+/**
+ * ldm - Support for Windows Logical Disk Manager (Dynamic Disks)
+ *
+ * Copyright (C) 2001,2002 Richard Russon <ldm@flatcap.org>
+ * Copyright (C) 2001 Anton Altaparmakov <aia21@cantab.net>
+ * Copyright (C) 2001,2002 Jakob Kemi <jakob.kemi@telia.com>
+ *
+ * Documentation is available at http://linux-ntfs.sf.net/ldm
+ *
+ * This program 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 of the License, or (at your option) any later
+ * version.
+ *
+ * This program 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
+ * this program (in the main directory of the source in the file COPYING); if
+ * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307 USA
+ */
+
+#include <linux/slab.h>
+#include <linux/stringify.h>
+#include <linux/pagemap.h>
+#include "ldm.h"
+#include "check.h"
+#include "msdos.h"
+
+typedef enum {
+ FALSE = 0,
+ TRUE = 1
+} BOOL;
+
+/**
+ * ldm_debug/info/error/crit - Output an error message
+ * @f: A printf format string containing the message
+ * @...: Variables to substitute into @f
+ *
+ * ldm_debug() writes a DEBUG level message to the syslog but only if the
+ * driver was compiled with debug enabled. Otherwise, the call turns into a NOP.
+ */
+#ifndef CONFIG_LDM_DEBUG
+#define ldm_debug(...) do {} while (0)
+#else
+#define ldm_debug(f, a...) _ldm_printk (KERN_DEBUG, __FUNCTION__, f, ##a)
+#endif
+
+#define ldm_crit(f, a...) _ldm_printk (KERN_CRIT, __FUNCTION__, f, ##a)
+#define ldm_error(f, a...) _ldm_printk (KERN_ERR, __FUNCTION__, f, ##a)
+#define ldm_info(f, a...) _ldm_printk (KERN_INFO, __FUNCTION__, f, ##a)
+
+__attribute__ ((format (printf, 3, 4)))
+static void _ldm_printk (const char *level, const char *function,
+ const char *fmt, ...)
+{
+ static char buf[128];
+ va_list args;
+
+ va_start (args, fmt);
+ vsnprintf (buf, sizeof (buf), fmt, args);
+ va_end (args);
+
+ printk ("%s%s(): %s\n", level, function, buf);
+}
+
+
+/**
+ * ldm_parse_hexbyte - Convert a ASCII hex number to a byte
+ * @src: Pointer to at least 2 characters to convert.
+ *
+ * Convert a two character ASCII hex string to a number.
+ *
+ * Return: 0-255 Success, the byte was parsed correctly
+ * -1 Error, an invalid character was supplied
+ */
+static int ldm_parse_hexbyte (const u8 *src)
+{
+ unsigned int x; /* For correct wrapping */
+ int h;
+
+ /* high part */
+ if ((x = src[0] - '0') <= '9'-'0') h = x;
+ else if ((x = src[0] - 'a') <= 'f'-'a') h = x+10;
+ else if ((x = src[0] - 'A') <= 'F'-'A') h = x+10;
+ else return -1;
+ h <<= 4;
+
+ /* low part */
+ if ((x = src[1] - '0') <= '9'-'0') return h | x;
+ if ((x = src[1] - 'a') <= 'f'-'a') return h | (x+10);
+ if ((x = src[1] - 'A') <= 'F'-'A') return h | (x+10);
+ return -1;
+}
+
+/**
+ * ldm_parse_guid - Convert GUID from ASCII to binary
+ * @src: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
+ * @dest: Memory block to hold binary GUID (16 bytes)
+ *
+ * N.B. The GUID need not be NULL terminated.
+ *
+ * Return: TRUE @dest contains binary GUID
+ * FALSE @dest contents are undefined
+ */
+static BOOL ldm_parse_guid (const u8 *src, u8 *dest)
+{
+ static const int size[] = { 4, 2, 2, 2, 6 };
+ int i, j, v;
+
+ if (src[8] != '-' || src[13] != '-' ||
+ src[18] != '-' || src[23] != '-')
+ return FALSE;
+
+ for (j = 0; j < 5; j++, src++)
+ for (i = 0; i < size[j]; i++, src+=2, *dest++ = v)
+ if ((v = ldm_parse_hexbyte (src)) < 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+
+/**
+ * ldm_parse_privhead - Read the LDM Database PRIVHEAD structure
+ * @data: Raw database PRIVHEAD structure loaded from the device
+ * @ph: In-memory privhead structure in which to return parsed information
+ *
+ * This parses the LDM database PRIVHEAD structure supplied in @data and
+ * sets up the in-memory privhead structure @ph with the obtained information.
+ *
+ * Return: TRUE @ph contains the PRIVHEAD data
+ * FALSE @ph contents are undefined
+ */
+static BOOL ldm_parse_privhead (const u8 *data, struct privhead *ph)
+{
+ BUG_ON (!data);
+ BUG_ON (!ph);
+
+ if (MAGIC_PRIVHEAD != BE64 (data)) {
+ ldm_error ("Cannot find PRIVHEAD structure. LDM database is"
+ " corrupt. Aborting.");
+ return FALSE;
+ }
+
+ ph->ver_major = BE16 (data + 0x000C);
+ ph->ver_minor = BE16 (data + 0x000E);
+ ph->logical_disk_start = BE64 (data + 0x011B);
+ ph->logical_disk_size = BE64 (data + 0x0123);
+ ph->config_start = BE64 (data + 0x012B);
+ ph->config_size = BE64 (data + 0x0133);
+
+ if ((ph->ver_major != 2) || (ph->ver_minor != 11)) {
+ ldm_error ("Expected PRIVHEAD version %d.%d, got %d.%d."
+ " Aborting.", 2, 11, ph->ver_major, ph->ver_minor);
+ return FALSE;
+ }
+ if (ph->config_size != LDM_DB_SIZE) { /* 1 MiB in sectors. */
+ /* Warn the user and continue, carefully */
+ ldm_info ("Database is normally %u bytes, it claims to "
+ "be %llu bytes.", LDM_DB_SIZE,
+ (unsigned long long)ph->config_size );
+ }
+ if ((ph->logical_disk_size == 0) ||
+ (ph->logical_disk_start + ph->logical_disk_size > ph->config_start)) {
+ ldm_error ("PRIVHEAD disk size doesn't match real disk size");
+ return FALSE;
+ }
+
+ if (!ldm_parse_guid (data + 0x0030, ph->disk_id)) {
+ ldm_error ("PRIVHEAD contains an invalid GUID.");
+ return FALSE;
+ }
+
+ ldm_debug ("Parsed PRIVHEAD successfully.");
+ return TRUE;
+}
+
+/**
+ * ldm_parse_tocblock - Read the LDM Database TOCBLOCK structure
+ * @data: Raw database TOCBLOCK structure loaded from the device
+ * @toc: In-memory toc structure in which to return parsed information
+ *
+ * This parses the LDM Database TOCBLOCK (table of contents) structure supplied
+ * in @data and sets up the in-memory tocblock structure @toc with the obtained
+ * information.
+ *
+ * N.B. The *_start and *_size values returned in @toc are not range-checked.
+ *
+ * Return: TRUE @toc contains the TOCBLOCK data
+ * FALSE @toc contents are undefined
+ */
+static BOOL ldm_parse_tocblock (const u8 *data, struct tocblock *toc)
+{
+ BUG_ON (!data);
+ BUG_ON (!toc);
+
+ if (MAGIC_TOCBLOCK != BE64 (data)) {
+ ldm_crit ("Cannot find TOCBLOCK, database may be corrupt.");
+ return FALSE;
+ }
+ strncpy (toc->bitmap1_name, data + 0x24, sizeof (toc->bitmap1_name));
+ toc->bitmap1_name[sizeof (toc->bitmap1_name) - 1] = 0;
+ toc->bitmap1_start = BE64 (data + 0x2E);
+ toc->bitmap1_size = BE64 (data + 0x36);
+
+ if (strncmp (toc->bitmap1_name, TOC_BITMAP1,
+ sizeof (toc->bitmap1_name)) != 0) {
+ ldm_crit ("TOCBLOCK's first bitmap is '%s', should be '%s'.",
+ TOC_BITMAP1, toc->bitmap1_name);
+ return FALSE;
+ }
+ strncpy (toc->bitmap2_name, data + 0x46, sizeof (toc->bitmap2_name));
+ toc->bitmap2_name[sizeof (toc->bitmap2_name) - 1] = 0;
+ toc->bitmap2_start = BE64 (data + 0x50);
+ toc->bitmap2_size = BE64 (data + 0x58);
+ if (strncmp (toc->bitmap2_name, TOC_BITMAP2,
+ sizeof (toc->bitmap2_name)) != 0) {
+ ldm_crit ("TOCBLOCK's second bitmap is '%s', should be '%s'.",
+ TOC_BITMAP2, toc->bitmap2_name);
+ return FALSE;
+ }
+ ldm_debug ("Parsed TOCBLOCK successfully.");
+ return TRUE;
+}
+
+/**
+ * ldm_parse_vmdb - Read the LDM Database VMDB structure
+ * @data: Raw database VMDB structure loaded from the device
+ * @vm: In-memory vmdb structure in which to return parsed information
+ *
+ * This parses the LDM Database VMDB structure supplied in @data and sets up
+ * the in-memory vmdb structure @vm with the obtained information.
+ *
+ * N.B. The *_start, *_size and *_seq values will be range-checked later.
+ *
+ * Return: TRUE @vm contains VMDB info
+ * FALSE @vm contents are undefined
+ */
+static BOOL ldm_parse_vmdb (const u8 *data, struct vmdb *vm)
+{
+ BUG_ON (!data);
+ BUG_ON (!vm);
+
+ if (MAGIC_VMDB != BE32 (data)) {
+ ldm_crit ("Cannot find the VMDB, database may be corrupt.");
+ return FALSE;
+ }
+
+ vm->ver_major = BE16 (data + 0x12);
+ vm->ver_minor = BE16 (data + 0x14);
+ if ((vm->ver_major != 4) || (vm->ver_minor != 10)) {
+ ldm_error ("Expected VMDB version %d.%d, got %d.%d. "
+ "Aborting.", 4, 10, vm->ver_major, vm->ver_minor);
+ return FALSE;
+ }
+
+ vm->vblk_size = BE32 (data + 0x08);
+ vm->vblk_offset = BE32 (data + 0x0C);
+ vm->last_vblk_seq = BE32 (data + 0x04);
+
+ ldm_debug ("Parsed VMDB successfully.");
+ return TRUE;
+}
+
+/**
+ * ldm_compare_privheads - Compare two privhead objects
+ * @ph1: First privhead
+ * @ph2: Second privhead
+ *
+ * This compares the two privhead structures @ph1 and @ph2.
+ *
+ * Return: TRUE Identical
+ * FALSE Different
+ */
+static BOOL ldm_compare_privheads (const struct privhead *ph1,
+ const struct privhead *ph2)
+{
+ BUG_ON (!ph1);
+ BUG_ON (!ph2);
+
+ return ((ph1->ver_major == ph2->ver_major) &&
+ (ph1->ver_minor == ph2->ver_minor) &&
+ (ph1->logical_disk_start == ph2->logical_disk_start) &&
+ (ph1->logical_disk_size == ph2->logical_disk_size) &&
+ (ph1->config_start == ph2->config_start) &&
+ (ph1->config_size == ph2->config_size) &&
+ !memcmp (ph1->disk_id, ph2->disk_id, GUID_SIZE));
+}
+
+/**
+ * ldm_compare_tocblocks - Compare two tocblock objects
+ * @toc1: First toc
+ * @toc2: Second toc
+ *
+ * This compares the two tocblock structures @toc1 and @toc2.
+ *
+ * Return: TRUE Identical
+ * FALSE Different
+ */
+static BOOL ldm_compare_tocblocks (const struct tocblock *toc1,
+ const struct tocblock *toc2)
+{
+ BUG_ON (!toc1);
+ BUG_ON (!toc2);
+
+ return ((toc1->bitmap1_start == toc2->bitmap1_start) &&
+ (toc1->bitmap1_size == toc2->bitmap1_size) &&
+ (toc1->bitmap2_start == toc2->bitmap2_start) &&
+ (toc1->bitmap2_size == toc2->bitmap2_size) &&
+ !strncmp (toc1->bitmap1_name, toc2->bitmap1_name,
+ sizeof (toc1->bitmap1_name)) &&
+ !strncmp (toc1->bitmap2_name, toc2->bitmap2_name,
+ sizeof (toc1->bitmap2_name)));
+}
+
+/**
+ * ldm_validate_privheads - Compare the primary privhead with its backups
+ * @bdev: Device holding the LDM Database
+ * @ph1: Memory struct to fill with ph contents
+ *
+ * Read and compare all three privheads from disk.
+ *
+ * The privheads on disk show the size and location of the main disk area and
+ * the configuration area (the database).
+ *
+ * Return: TRUE Success
+ * FALSE Error
+ */
+static BOOL ldm_validate_privheads (struct block_device *bdev,
+ unsigned long first_sector, struct privhead *ph1, struct gendisk *hd,
+ unsigned long first_minor)
+{
+ static const int off[3] = { OFF_PRIV1, OFF_PRIV2, OFF_PRIV3 };
+ struct privhead *ph[3] = { ph1 };
+ Sector sect;
+ u8 *data;
+ BOOL result = FALSE;
+ long num_sects;
+ int i;
+
+ BUG_ON (!bdev);
+ BUG_ON (!ph1);
+
+ ph[1] = kmalloc (sizeof (*ph[1]), GFP_KERNEL);
+ ph[2] = kmalloc (sizeof (*ph[2]), GFP_KERNEL);
+ if (!ph[1] || !ph[2]) {
+ ldm_crit ("Out of memory.");
+ goto out;
+ }
+
+ /* off[1 & 2] are relative to ph[0]->config_start */
+ ph[0]->config_start = 0;
+
+ /* Read and parse privheads */
+ for (i = 0; i < 3; i++) {
+ data = read_dev_sector (bdev,
+ first_sector + ph[0]->config_start + off[i], &sect);
+ if (!data) {
+ ldm_crit ("Disk read failed.");
+ goto out;
+ }
+ result = ldm_parse_privhead (data, ph[i]);
+ put_dev_sector (sect);
+ if (!result) {
+ ldm_error ("Cannot find PRIVHEAD %d.", i+1); /* Log again */
+ if (i < 2)
+ goto out; /* Already logged */
+ else
+ break; /* FIXME ignore for now, 3rd PH can fail on odd-sized disks */
+ }
+ }
+
+ num_sects = hd->part[(first_minor >> hd->minor_shift)
+ << hd->minor_shift].nr_sects;
+
+ if ((ph[0]->config_start > num_sects) ||
+ ((ph[0]->config_start + ph[0]->config_size) > num_sects)) {
+ ldm_crit ("Database extends beyond the end of the disk.");
+ goto out;
+ }
+
+ if ((ph[0]->logical_disk_start > ph[0]->config_start) ||
+ ((ph[0]->logical_disk_start + ph[0]->logical_disk_size)
+ > ph[0]->config_start)) {
+ ldm_crit ("Disk and database overlap.");
+ goto out;
+ }
+
+ if (!ldm_compare_privheads (ph[0], ph[1])) {
+ ldm_crit ("Primary and backup PRIVHEADs don't match.");
+ goto out;
+ }
+ /* FIXME ignore this for now
+ if (!ldm_compare_privheads (ph[0], ph[2])) {
+ ldm_crit ("Primary and backup PRIVHEADs don't match.");
+ goto out;
+ }*/
+ ldm_debug ("Validated PRIVHEADs successfully.");
+ result = TRUE;
+out:
+ kfree (ph[1]);
+ kfree (ph[2]);
+ return result;
+}
+
+/**
+ * ldm_validate_tocblocks - Validate the table of contents and its backups
+ * @bdev: Device holding the LDM Database
+ * @base: Offset, into @bdev, of the database
+ * @ldb: Cache of the database structures
+ *
+ * Find and compare the four tables of contents of the LDM Database stored on
+ * @bdev and return the parsed information into @toc1.
+ *
+ * The offsets and sizes of the configs are range-checked against a privhead.
+ *
+ * Return: TRUE @toc1 contains validated TOCBLOCK info
+ * FALSE @toc1 contents are undefined
+ */
+static BOOL ldm_validate_tocblocks (struct block_device *bdev,
+ unsigned long base, struct ldmdb *ldb)
+{
+ static const int off[4] = { OFF_TOCB1, OFF_TOCB2, OFF_TOCB3, OFF_TOCB4};
+ struct tocblock *tb[4];
+ struct privhead *ph;
+ Sector sect;
+ u8 *data;
+ BOOL result = FALSE;
+ int i;
+
+ BUG_ON (!bdev);
+ BUG_ON (!ldb);
+
+ ph = &ldb->ph;
+ tb[0] = &ldb->toc;
+ tb[1] = kmalloc (sizeof (*tb[1]), GFP_KERNEL);
+ tb[2] = kmalloc (sizeof (*tb[2]), GFP_KERNEL);
+ tb[3] = kmalloc (sizeof (*tb[3]), GFP_KERNEL);
+ if (!tb[1] || !tb[2] || !tb[3]) {
+ ldm_crit ("Out of memory.");
+ goto out;
+ }
+
+ for (i = 0; i < 4; i++) /* Read and parse all four toc's. */
+ {
+ data = read_dev_sector (bdev, base + off[i], &sect);
+ if (!data) {
+ ldm_crit ("Disk read failed.");
+ goto out;
+ }
+ result = ldm_parse_tocblock (data, tb[i]);
+ put_dev_sector (sect);
+ if (!result)
+ goto out; /* Already logged */
+ }
+
+ /* Range check the toc against a privhead. */
+ if (((tb[0]->bitmap1_start + tb[0]->bitmap1_size) > ph->config_size) ||
+ ((tb[0]->bitmap2_start + tb[0]->bitmap2_size) > ph->config_size)) {
+ ldm_crit ("The bitmaps are out of range. Giving up.");
+ goto out;
+ }
+
+ if (!ldm_compare_tocblocks (tb[0], tb[1]) || /* Compare all tocs. */
+ !ldm_compare_tocblocks (tb[0], tb[2]) ||
+ !ldm_compare_tocblocks (tb[0], tb[3])) {
+ ldm_crit ("The TOCBLOCKs don't match.");
+ goto out;
+ }
+
+ ldm_debug ("Validated TOCBLOCKs successfully.");
+ result = TRUE;
+out:
+ kfree (tb[1]);
+ kfree (tb[2]);
+ kfree (tb[3]);
+ return result;
+}
+
+/**
+ * ldm_validate_vmdb - Read the VMDB and validate it
+ * @bdev: Device holding the LDM Database
+ * @base: Offset, into @bdev, of the database
+ * @ldb: Cache of the database structures
+ *
+ * Find the vmdb of the LDM Database stored on @bdev and return the parsed
+ * information in @ldb.
+ *
+ * Return: TRUE @ldb contains validated VBDB info
+ * FALSE @ldb contents are undefined
+ */
+static BOOL ldm_validate_vmdb (struct block_device *bdev, unsigned long base,
+ struct ldmdb *ldb)
+{
+ Sector sect;
+ u8 *data;
+ BOOL result = FALSE;
+ struct vmdb *vm;
+ struct tocblock *toc;
+
+ BUG_ON (!bdev);
+ BUG_ON (!ldb);
+
+ vm = &ldb->vm;
+ toc = &ldb->toc;
+
+ data = read_dev_sector (bdev, base + OFF_VMDB, &sect);
+ if (!data) {
+ ldm_crit ("Disk read failed.");
+ return FALSE;
+ }
+
+ if (!ldm_parse_vmdb (data, vm))
+ goto out; /* Already logged */
+
+ /* Are there uncommitted transactions? */
+ if (BE16(data + 0x10) != 0x01) {
+ ldm_crit ("Database is not in a consistant state. Aborting.");
+ goto out;
+ }
+
+ if (vm->vblk_offset != 512)
+ ldm_info ("VBLKs start at offset 0x%04x.", vm->vblk_offset);
+
+ /* FIXME: How should we handle this situation? */
+ if ((vm->vblk_size * vm->last_vblk_seq) != (toc->bitmap1_size << 9))
+ ldm_info ("VMDB and TOCBLOCK don't agree on the database size.");
+
+ result = TRUE;
+out:
+ put_dev_sector (sect);
+ return result;
+}
+
+
+/**
+ * ldm_validate_partition_table - Determine whether bdev might be a dynamic disk
+ * @bdev: Device holding the LDM Database
+ *
+ * This function provides a weak test to decide whether the device is a dynamic
+ * disk or not. It looks for an MS-DOS-style partition table containing at
+ * least one partition of type 0x42 (formerly SFS, now used by Windows for
+ * dynamic disks).
+ *
+ * N.B. The only possible error can come from the read_dev_sector and that is
+ * only likely to happen if the underlying device is strange. If that IS
+ * the case we should return zero to let someone else try.
+ *
+ * Return: TRUE @bdev is a dynamic disk
+ * FALSE @bdev is not a dynamic disk, or an error occurred
+ */
+static BOOL ldm_validate_partition_table (struct block_device *bdev)
+{
+ Sector sect;
+ u8 *data;
+ struct partition *p;
+ int i;
+ BOOL result = FALSE;
+
+ BUG_ON (!bdev);
+
+ data = read_dev_sector (bdev, 0, &sect);
+ if (!data) {
+ ldm_crit ("Disk read failed.");
+ return FALSE;
+ }
+
+ if (*(u16*) (data + 0x01FE) != cpu_to_le16 (MSDOS_LABEL_MAGIC)) {
+ ldm_debug ("No MS-DOS partition table found.");
+ goto out;
+ }
+
+ p = (struct partition*)(data + 0x01BE);
+ for (i = 0; i < 4; i++, p++)
+ if (SYS_IND (p) == WIN2K_DYNAMIC_PARTITION) {
+ result = TRUE;
+ break;
+ }
+
+ if (result)
+ ldm_debug ("Parsed partition table successfully.");
+ else
+ ldm_debug ("Found an MS-DOS partition table, not a dynamic disk.");
+out:
+ put_dev_sector (sect);
+ return result;
+}
+
+/**
+ * ldm_get_disk_objid - Search a linked list of vblk's for a given Disk Id
+ * @ldb: Cache of the database structures
+ *
+ * The LDM Database contains a list of all partitions on all dynamic disks. The
+ * primary PRIVHEAD, at the beginning of the physical disk, tells us the GUID of
+ * this disk. This function searches for the GUID in a linked list of vblk's.
+ *
+ * Return: Pointer, A matching vblk was found
+ * NULL, No match, or an error
+ */
+static struct vblk * ldm_get_disk_objid (const struct ldmdb *ldb)
+{
+ struct list_head *item;
+
+ BUG_ON (!ldb);
+
+ list_for_each (item, &ldb->v_disk) {
+ struct vblk *v = list_entry (item, struct vblk, list);
+ if (!memcmp (v->vblk.disk.disk_id, ldb->ph.disk_id, GUID_SIZE))
+ return v;
+ }
+
+ return NULL;
+}
+
+/**
+ * ldm_create_partition - Create a kernel partition device
+ * @hd: gendisk structure in which to create partition
+ * @minor: Create a this minor number on the device
+ * @start: Offset (in sectors) into the device of the partition
+ * @size: Size (in sectors) of the partition
+ *
+ * This validates the range, then puts an entry into the kernel's partition
+ * table.
+ *
+ * Return: TRUE Created the partition
+ * FALSE Error
+ */
+static BOOL ldm_create_partition (struct gendisk *hd, int minor, int start,
+ int size)
+{
+ int disk_minor;
+
+ BUG_ON (!hd);;
+ BUG_ON (!hd->part);
+
+ /* Get the minor number of the parent device
+ * so we can check we don't go beyond the end of the device. */
+ disk_minor = (minor >> hd->minor_shift) << hd->minor_shift;
+ if ((start < 1) || ((start + size) > hd->part[disk_minor].nr_sects)) {
+ ldm_crit ("Partition exceeds physical disk. Aborting.");
+ return FALSE;
+ }
+ add_gd_partition (hd, minor, start, size);
+ ldm_debug ("Created partition successfully.");
+ return TRUE;
+}
+
+/**
+ * ldm_create_data_partitions - Create data partitions for this device
+ * @pp: List of the partitions parsed so far
+ * @ldb: Cache of the database structures
+ *
+ * The database contains ALL the partitions for ALL disk groups, so we need to
+ * filter out this specific disk. Using the disk's object id, we can find all
+ * the partitions in the database that belong to this disk.
+ *
+ * Add each partition in our database, to the parsed_partitions structure.
+ *
+ * N.B. This function creates the partitions in the order it finds partition
+ * objects in the linked list.
+ *
+ * Return: TRUE Partition created
+ * FALSE Error, probably a range checking problem
+ */
+static BOOL ldm_create_data_partitions (struct gendisk *hd,
+ unsigned long first_sector, int first_minor, const struct ldmdb *ldb)
+{
+ struct list_head *item;
+ struct vblk_part *part;
+ struct vblk *disk;
+ int disk_minor;
+ int minor;
+
+ BUG_ON (!hd);
+ BUG_ON (!ldb);
+
+ disk = ldm_get_disk_objid (ldb);
+ if (!disk) {
+ ldm_crit ("Can't find the ID of this disk in the database.");
+ return FALSE;
+ }
+
+ /* We use the range-check the partitions against the parent device. */
+ disk_minor = (first_minor >> hd->minor_shift) << hd->minor_shift;
+ minor = first_minor;
+
+ printk (" [LDM]");
+
+ /* Create the data partitions */
+ list_for_each (item, &ldb->v_part) {
+ struct vblk *vb;
+ vb = list_entry (item, struct vblk, list);
+ part = &vb->vblk.part;
+
+ if (part->disk_id != disk->obj_id)
+ continue;
+
+ if (!ldm_create_partition (hd, minor,
+ part->start + ldb->ph.logical_disk_start, part->size))
+ continue; /* Already logged */
+ minor++;
+ }
+
+ printk ("\n");
+ return TRUE;
+}
+
+
+/**
+ * ldm_relative - Calculate the next relative offset
+ * @buffer: Block of data being worked on
+ * @buflen: Size of the block of data
+ * @base: Size of the previous fixed width fields
+ * @offset: Cumulative size of the previous variable-width fields
+ *
+ * Because many of the VBLK fields are variable-width, it's necessary
+ * to calculate each offset based on the previous one and the length
+ * of the field it pointed to.
+ *
+ * Return: -1 Error, the calculated offset exceeded the size of the buffer
+ * n OK, a range-checked offset into buffer
+ */
+static int ldm_relative (const u8 *buffer, int buflen, int base, int offset)
+{
+
+ base += offset;
+ if ((!buffer) || (offset < 0) || (base > buflen))
+ return -1;
+ if ((base + buffer[base]) >= buflen)
+ return -1;
+
+ return buffer[base] + offset + 1;
+}
+
+/**
+ * ldm_get_vnum - Convert a variable-width, big endian number, into cpu order
+ * @block: Pointer to the variable-width number to convert
+ *
+ * Large numbers in the LDM Database are often stored in a packed format. Each
+ * number is prefixed by a one byte width marker. All numbers in the database
+ * are stored in big-endian byte order. This function reads one of these
+ * numbers and returns the result
+ *
+ * N.B. This function DOES NOT perform any range checking, though the most
+ * it will read is eight bytes.
+ *
+ * Return: n A number
+ * 0 Zero, or an error occurred
+ */
+static u64 ldm_get_vnum (const u8 *block)
+{
+ u64 tmp = 0;
+ u8 length;
+
+ BUG_ON (!block);
+
+ length = *block++;
+
+ if (length && length <= 8)
+ while (length--)
+ tmp = (tmp << 8) | *block++;
+ else
+ ldm_error ("Illegal length %d.", length);
+
+ return tmp;
+}
+
+/**
+ * ldm_get_vstr - Read a length-prefixed string into a buffer
+ * @block: Pointer to the length marker
+ * @buffer: Location to copy string to
+ * @buflen: Size of the output buffer
+ *
+ * Many of the strings in the LDM Database are not NULL terminated. Instead
+ * they are prefixed by a one byte length marker. This function copies one of
+ * these strings into a buffer.
+ *
+ * N.B. This function DOES NOT perform any range checking on the input.
+ * If the buffer is too small, the output will be truncated.
+ *
+ * Return: 0, Error and @buffer contents are undefined
+ * n, String length in characters (excluding NULL)
+ * buflen-1, String was truncated.
+ */
+static int ldm_get_vstr (const u8 *block, u8 *buffer, int buflen)
+{
+ int length;
+
+ BUG_ON (!block);
+ BUG_ON (!buffer);
+
+ length = block[0];
+ if (length >= buflen) {
+ ldm_error ("Truncating string %d -> %d.", length, buflen);
+ length = buflen - 1;
+ }
+ memcpy (buffer, block + 1, length);
+ buffer[length] = 0;
+ return length;
+}
+
+
+/**
+ * ldm_parse_cmp3 - Read a raw VBLK Component object into a vblk structure
+ * @buffer: Block of data being worked on
+ * @buflen: Size of the block of data
+ * @vb: In-memory vblk in which to return information
+ *
+ * Read a raw VBLK Component object (version 3) into a vblk structure.
+ *
+ * Return: TRUE @vb contains a Component VBLK
+ * FALSE @vb contents are not defined
+ */
+static BOOL ldm_parse_cmp3 (const u8 *buffer, int buflen, struct vblk *vb)
+{
+ int r_objid, r_name, r_vstate, r_child, r_parent, r_stripe, r_cols, len;
+ struct vblk_comp *comp;
+
+ BUG_ON (!buffer);
+ BUG_ON (!vb);
+
+ r_objid = ldm_relative (buffer, buflen, 0x18, 0);
+ r_name = ldm_relative (buffer, buflen, 0x18, r_objid);
+ r_vstate = ldm_relative (buffer, buflen, 0x18, r_name);
+ r_child = ldm_relative (buffer, buflen, 0x1D, r_vstate);
+ r_parent = ldm_relative (buffer, buflen, 0x2D, r_child);
+
+ if (buffer[0x12] & VBLK_FLAG_COMP_STRIPE) {
+ r_stripe = ldm_relative (buffer, buflen, 0x2E, r_parent);
+ r_cols = ldm_relative (buffer, buflen, 0x2E, r_stripe);
+ len = r_cols;
+ } else {
+ r_stripe = 0;
+ r_cols = 0;
+ len = r_parent;
+ }
+ if (len < 0)
+ return FALSE;
+
+ len += VBLK_SIZE_CMP3;
+ if (len != BE32 (buffer + 0x14))
+ return FALSE;
+
+ comp = &vb->vblk.comp;
+ ldm_get_vstr (buffer + 0x18 + r_name, comp->state,
+ sizeof (comp->state));
+ comp->type = buffer[0x18 + r_vstate];
+ comp->children = ldm_get_vnum (buffer + 0x1D + r_vstate);
+ comp->parent_id = ldm_get_vnum (buffer + 0x2D + r_child);
+ comp->chunksize = r_stripe ? ldm_get_vnum (buffer+r_parent+0x2E) : 0;
+
+ return TRUE;
+}
+
+/**
+ * ldm_parse_dgr3 - Read a raw VBLK Disk Group object into a vblk structure
+ * @buffer: Block of data being worked on
+ * @buflen: Size of the block of data
+ * @vb: In-memory vblk in which to return information
+ *
+ * Read a raw VBLK Disk Group object (version 3) into a vblk structure.
+ *
+ * Return: TRUE @vb contains a Disk Group VBLK
+ * FALSE @vb contents are not defined
+ */
+static int ldm_parse_dgr3 (const u8 *buffer, int buflen, struct vblk *vb)
+{
+ int r_objid, r_name, r_diskid, r_id1, r_id2, len;
+ struct vblk_dgrp *dgrp;
+
+ BUG_ON (!buffer);
+ BUG_ON (!vb);
+
+ r_objid = ldm_relative (buffer, buflen, 0x18, 0);
+ r_name = ldm_relative (buffer, buflen, 0x18, r_objid);
+ r_diskid = ldm_relative (buffer, buflen, 0x18, r_name);
+
+ if (buffer[0x12] & VBLK_FLAG_DGR3_IDS) {
+ r_id1 = ldm_relative (buffer, buflen, 0x24, r_diskid);
+ r_id2 = ldm_relative (buffer, buflen, 0x24, r_id1);
+ len = r_id2;
+ } else {
+ r_id1 = 0;
+ r_id2 = 0;
+ len = r_diskid;
+ }
+ if (len < 0)
+ return FALSE;
+
+ len += VBLK_SIZE_DGR3;
+ if (len != BE32 (buffer + 0x14))
+ return FALSE;
+
+ dgrp = &vb->vblk.dgrp;
+ ldm_get_vstr (buffer + 0x18 + r_name, dgrp->disk_id,
+ sizeof (dgrp->disk_id));
+ return TRUE;
+}
+
+/**
+ * ldm_parse_dgr4 - Read a raw VBLK Disk Group object into a vblk structure
+ * @buffer: Block of data being worked on
+ * @buflen: Size of the block of data
+ * @vb: In-memory vblk in which to return information
+ *
+ * Read a raw VBLK Disk Group object (version 4) into a vblk structure.
+ *
+ * Return: TRUE @vb contains a Disk Group VBLK
+ * FALSE @vb contents are not defined
+ */
+static BOOL ldm_parse_dgr4 (const u8 *buffer, int buflen, struct vblk *vb)
+{
+ char buf[64];
+ int r_objid, r_name, r_id1, r_id2, len;
+ struct vblk_dgrp *dgrp;
+
+ BUG_ON (!buffer);
+ BUG_ON (!vb);
+
+ r_objid = ldm_relative (buffer, buflen, 0x18, 0);
+ r_name = ldm_relative (buffer, buflen, 0x18, r_objid);
+
+ if (buffer[0x12] & VBLK_FLAG_DGR4_IDS) {
+ r_id1 = ldm_relative (buffer, buflen, 0x44, r_name);
+ r_id2 = ldm_relative (buffer, buflen, 0x44, r_id1);
+ len = r_id2;
+ } else {
+ r_id1 = 0;
+ r_id2 = 0;
+ len = r_name;
+ }
+ if (len < 0)
+ return FALSE;
+
+ len += VBLK_SIZE_DGR4;
+ if (len != BE32 (buffer + 0x14))
+ return FALSE;
+
+ dgrp = &vb->vblk.dgrp;
+
+ ldm_get_vstr (buffer + 0x18 + r_objid, buf, sizeof (buf));
+ return TRUE;
+}
+
+/**
+ * ldm_parse_dsk3 - Read a raw VBLK Disk object into a vblk structure
+ * @buffer: Block of data being worked on
+ * @buflen: Size of the block of data
+ * @vb: In-memory vblk in which to return information
+ *
+ * Read a raw VBLK Disk object (version 3) into a vblk structure.
+ *
+ * Return: TRUE @vb contains a Disk VBLK
+ * FALSE @vb contents are not defined
+ */
+static BOOL ldm_parse_dsk3 (const u8 *buffer, int buflen, struct vblk *vb)
+{
+ int r_objid, r_name, r_diskid, r_altname, len;
+ struct vblk_disk *disk;
+
+ BUG_ON (!buffer);
+ BUG_ON (!vb);
+
+ r_objid = ldm_relative (buffer, buflen, 0x18, 0);
+ r_name = ldm_relative (buffer, buflen, 0x18, r_objid);
+ r_diskid = ldm_relative (buffer, buflen, 0x18, r_name);
+ r_altname = ldm_relative (buffer, buflen, 0x18, r_diskid);
+ len = r_altname;
+ if (len < 0)
+ return FALSE;
+
+ len += VBLK_SIZE_DSK3;
+ if (len != BE32 (buffer + 0x14))
+ return FALSE;
+
+ disk = &vb->vblk.disk;
+ ldm_get_vstr (buffer + 0x18 + r_diskid, disk->alt_name,
+ sizeof (disk->alt_name));
+ if (!ldm_parse_guid (buffer + 0x19 + r_name, disk->disk_id))
+ return FALSE;
+
+ return TRUE;
+}
+
+/**
+ * ldm_parse_dsk4 - Read a raw VBLK Disk object into a vblk structure
+ * @buffer: Block of data being worked on
+ * @buflen: Size of the block of data
+ * @vb: In-memory vblk in which to return information
+ *
+ * Read a raw VBLK Disk object (version 4) into a vblk structure.
+ *
+ * Return: TRUE @vb contains a Disk VBLK
+ * FALSE @vb contents are not defined
+ */
+static BOOL ldm_parse_dsk4 (const u8 *buffer, int buflen, struct vblk *vb)
+{
+ int r_objid, r_name, len;
+ struct vblk_disk *disk;
+
+ BUG_ON (!buffer);
+ BUG_ON (!vb);
+
+ r_objid = ldm_relative (buffer, buflen, 0x18, 0);
+ r_name = ldm_relative (buffer, buflen, 0x18, r_objid);
+ len = r_name;
+ if (len < 0)
+ return FALSE;
+
+ len += VBLK_SIZE_DSK4;
+ if (len != BE32 (buffer + 0x14))
+ return FALSE;
+
+ disk = &vb->vblk.disk;
+ memcpy (disk->disk_id, buffer + 0x18 + r_name, GUID_SIZE);
+ return TRUE;
+}
+
+/**
+ * ldm_parse_prt3 - Read a raw VBLK Partition object into a vblk structure
+ * @buffer: Block of data being worked on
+ * @buflen: Size of the block of data
+ * @vb: In-memory vblk in which to return information
+ *
+ * Read a raw VBLK Partition object (version 3) into a vblk structure.
+ *
+ * Return: TRUE @vb contains a Partition VBLK
+ * FALSE @vb contents are not defined
+ */
+static BOOL ldm_parse_prt3 (const u8 *buffer, int buflen, struct vblk *vb)
+{
+ int r_objid, r_name, r_size, r_parent, r_diskid, r_index, len;
+ struct vblk_part *part;
+
+ BUG_ON (!buffer);
+ BUG_ON (!vb);
+
+ r_objid = ldm_relative (buffer, buflen, 0x18, 0);
+ r_name = ldm_relative (buffer, buflen, 0x18, r_objid);
+ r_size = ldm_relative (buffer, buflen, 0x34, r_name);
+ r_parent = ldm_relative (buffer, buflen, 0x34, r_size);
+ r_diskid = ldm_relative (buffer, buflen, 0x34, r_parent);
+
+ if (buffer[0x12] & VBLK_FLAG_PART_INDEX) {
+ r_index = ldm_relative (buffer, buflen, 0x34, r_diskid);
+ len = r_index;
+ } else {
+ r_index = 0;
+ len = r_diskid;
+ }
+ if (len < 0)
+ return FALSE;
+
+ len += VBLK_SIZE_PRT3;
+ if (len != BE32 (buffer + 0x14))
+ return FALSE;
+
+ part = &vb->vblk.part;
+ part->start = BE64 (buffer + 0x24 + r_name);
+ part->volume_offset = BE64 (buffer + 0x2C + r_name);
+ part->size = ldm_get_vnum (buffer + 0x34 + r_name);
+ part->parent_id = ldm_get_vnum (buffer + 0x34 + r_size);
+ part->disk_id = ldm_get_vnum (buffer + 0x34 + r_parent);
+ if (vb->flags & VBLK_FLAG_PART_INDEX)
+ part->partnum = buffer[0x35 + r_diskid];
+ else
+ part->partnum = 0;
+
+ return TRUE;
+}
+
+/**
+ * ldm_parse_vol5 - Read a raw VBLK Volume object into a vblk structure
+ * @buffer: Block of data being worked on
+ * @buflen: Size of the block of data
+ * @vb: In-memory vblk in which to return information
+ *
+ * Read a raw VBLK Volume object (version 5) into a vblk structure.
+ *
+ * Return: TRUE @vb contains a Volume VBLK
+ * FALSE @vb contents are not defined
+ */
+static BOOL ldm_parse_vol5 (const u8 *buffer, int buflen, struct vblk *vb)
+{
+ int r_objid, r_name, r_vtype, r_child, r_size, r_id1, r_id2, r_size2;
+ int r_drive, len;
+ struct vblk_volu *volu;
+
+ BUG_ON (!buffer);
+ BUG_ON (!vb);
+
+ r_objid = ldm_relative (buffer, buflen, 0x18, 0);
+ r_name = ldm_relative (buffer, buflen, 0x18, r_objid);
+ r_vtype = ldm_relative (buffer, buflen, 0x18, r_name);
+ r_child = ldm_relative (buffer, buflen, 0x2E, r_vtype);
+ r_size = ldm_relative (buffer, buflen, 0x3E, r_child);
+
+ if (buffer[0x12] & VBLK_FLAG_VOLU_ID1)
+ r_id1 = ldm_relative (buffer, buflen, 0x53, r_size);
+ else
+ r_id1 = r_size;
+
+ if (buffer[0x12] & VBLK_FLAG_VOLU_ID2)
+ r_id2 = ldm_relative (buffer, buflen, 0x53, r_id1);
+ else
+ r_id2 = r_id1;
+
+ if (buffer[0x12] & VBLK_FLAG_VOLU_SIZE)
+ r_size2 = ldm_relative (buffer, buflen, 0x53, r_id2);
+ else
+ r_size2 = r_id2;
+
+ if (buffer[0x12] & VBLK_FLAG_VOLU_DRIVE)
+ r_drive = ldm_relative (buffer, buflen, 0x53, r_size2);
+ else
+ r_drive = r_size2;
+
+ len = r_drive;
+ if (len < 0)
+ return FALSE;
+
+ len += VBLK_SIZE_VOL5;
+ if (len != BE32 (buffer + 0x14))
+ return FALSE;
+
+ volu = &vb->vblk.volu;
+
+ ldm_get_vstr (buffer + 0x18 + r_name, volu->volume_type,
+ sizeof (volu->volume_type));
+ memcpy (volu->volume_state, buffer + 0x19 + r_vtype,
+ sizeof (volu->volume_state));
+ volu->size = ldm_get_vnum (buffer + 0x3E + r_child);
+ volu->partition_type = buffer[0x42 + r_size];
+ memcpy (volu->guid, buffer + 0x43 + r_size, sizeof (volu->guid));
+ if (buffer[0x12] & VBLK_FLAG_VOLU_DRIVE) {
+ ldm_get_vstr (buffer + 0x53 + r_size, volu->drive_hint,
+ sizeof (volu->drive_hint));
+ }
+ return TRUE;
+}
+
+/**
+ * ldm_parse_vblk - Read a raw VBLK object into a vblk structure
+ * @buf: Block of data being worked on
+ * @len: Size of the block of data
+ * @vb: In-memory vblk in which to return information
+ *
+ * Read a raw VBLK object into a vblk structure. This function just reads the
+ * information common to all VBLK types, then delegates the rest of the work to
+ * helper functions: ldm_parse_*.
+ *
+ * Return: TRUE @vb contains a VBLK
+ * FALSE @vb contents are not defined
+ */
+static BOOL ldm_parse_vblk (const u8 *buf, int len, struct vblk *vb)
+{
+ BOOL result = FALSE;
+ int r_objid;
+
+ BUG_ON (!buf);
+ BUG_ON (!vb);
+
+ r_objid = ldm_relative (buf, len, 0x18, 0);
+ if (r_objid < 0) {
+ ldm_error ("VBLK header is corrupt.");
+ return FALSE;
+ }
+
+ vb->flags = buf[0x12];
+ vb->type = buf[0x13];
+ vb->obj_id = ldm_get_vnum (buf + 0x18);
+ ldm_get_vstr (buf+0x18+r_objid, vb->name, sizeof (vb->name));
+
+ switch (vb->type) {
+ case VBLK_CMP3: result = ldm_parse_cmp3 (buf, len, vb); break;
+ case VBLK_DSK3: result = ldm_parse_dsk3 (buf, len, vb); break;
+ case VBLK_DSK4: result = ldm_parse_dsk4 (buf, len, vb); break;
+ case VBLK_DGR3: result = ldm_parse_dgr3 (buf, len, vb); break;
+ case VBLK_DGR4: result = ldm_parse_dgr4 (buf, len, vb); break;
+ case VBLK_PRT3: result = ldm_parse_prt3 (buf, len, vb); break;
+ case VBLK_VOL5: result = ldm_parse_vol5 (buf, len, vb); break;
+ }
+
+ if (result)
+ ldm_debug ("Parsed VBLK 0x%llx (type: 0x%02x) ok.",
+ (unsigned long long) vb->obj_id, vb->type);
+ else
+ ldm_error ("Failed to parse VBLK 0x%llx (type: 0x%02x).",
+ (unsigned long long) vb->obj_id, vb->type);
+
+ return result;
+}
+
+
+/**
+ * ldm_ldmdb_add - Adds a raw VBLK entry to the ldmdb database
+ * @data: Raw VBLK to add to the database
+ * @len: Size of the raw VBLK
+ * @ldb: Cache of the database structures
+ *
+ * The VBLKs are sorted into categories. Partitions are also sorted by offset.
+ *
+ * N.B. This function does not check the validity of the VBLKs.
+ *
+ * Return: TRUE The VBLK was added
+ * FALSE An error occurred
+ */
+static BOOL ldm_ldmdb_add (u8 *data, int len, struct ldmdb *ldb)
+{
+ struct vblk *vb;
+ struct list_head *item;
+
+ BUG_ON (!data);
+ BUG_ON (!ldb);
+
+ vb = kmalloc (sizeof (*vb), GFP_KERNEL);
+ if (!vb) {
+ ldm_crit ("Out of memory.");
+ return FALSE;
+ }
+
+ if (!ldm_parse_vblk (data, len, vb)) {
+ kfree(vb);
+ return FALSE; /* Already logged */
+ }
+
+ /* Put vblk into the correct list. */
+ switch (vb->type) {
+ case VBLK_DGR3:
+ case VBLK_DGR4:
+ list_add (&vb->list, &ldb->v_dgrp);
+ break;
+ case VBLK_DSK3:
+ case VBLK_DSK4:
+ list_add (&vb->list, &ldb->v_disk);
+ break;
+ case VBLK_VOL5:
+ list_add (&vb->list, &ldb->v_volu);
+ break;
+ case VBLK_CMP3:
+ list_add (&vb->list, &ldb->v_comp);
+ break;
+ case VBLK_PRT3:
+ /* Sort by the partition's start sector. */
+ list_for_each (item, &ldb->v_part) {
+ struct vblk *v = list_entry (item, struct vblk, list);
+ if ((v->vblk.part.disk_id == vb->vblk.part.disk_id) &&
+ (v->vblk.part.start > vb->vblk.part.start)) {
+ list_add_tail (&vb->list, &v->list);
+ return TRUE;
+ }
+ }
+ list_add_tail (&vb->list, &ldb->v_part);
+ break;
+ }
+ return TRUE;
+}
+
+/**
+ * ldm_frag_add - Add a VBLK fragment to a list
+ * @data: Raw fragment to be added to the list
+ * @size: Size of the raw fragment
+ * @frags: Linked list of VBLK fragments
+ *
+ * Fragmented VBLKs may not be consecutive in the database, so they are placed
+ * in a list so they can be pieced together later.
+ *
+ * Return: TRUE Success, the VBLK was added to the list
+ * FALSE Error, a problem occurred
+ */
+static BOOL ldm_frag_add (const u8 *data, int size, struct list_head *frags)
+{
+ struct frag *f;
+ struct list_head *item;
+ int rec, num, group;
+
+ BUG_ON (!data);
+ BUG_ON (!frags);
+
+ group = BE32 (data + 0x08);
+ rec = BE16 (data + 0x0C);
+ num = BE16 (data + 0x0E);
+ if ((num < 1) || (num > 4)) {
+ ldm_error ("A VBLK claims to have %d parts.", num);
+ return FALSE;
+ }
+
+ list_for_each (item, frags) {
+ f = list_entry (item, struct frag, list);
+ if (f->group == group)
+ goto found;
+ }
+
+ f = kmalloc (sizeof (*f) + size*num, GFP_KERNEL);
+ if (!f) {
+ ldm_crit ("Out of memory.");
+ return FALSE;
+ }
+
+ f->group = group;
+ f->num = num;
+ f->rec = rec;
+ f->map = 0xFF << num;
+
+ list_add_tail (&f->list, frags);
+found:
+ if (f->map & (1 << rec)) {
+ ldm_error ("Duplicate VBLK, part %d.", rec);
+ f->map &= 0x7F; /* Mark the group as broken */
+ return FALSE;
+ }
+
+ f->map |= (1 << rec);
+
+ if (num > 0) {
+ data += VBLK_SIZE_HEAD;
+ size -= VBLK_SIZE_HEAD;
+ }
+ memcpy (f->data+rec*(size-VBLK_SIZE_HEAD)+VBLK_SIZE_HEAD, data, size);
+
+ return TRUE;
+}
+
+/**
+ * ldm_frag_free - Free a linked list of VBLK fragments
+ * @list: Linked list of fragments
+ *
+ * Free a linked list of VBLK fragments
+ *
+ * Return: none
+ */
+static void ldm_frag_free (struct list_head *list)
+{
+ struct list_head *item, *tmp;
+
+ BUG_ON (!list);
+
+ list_for_each_safe (item, tmp, list)
+ kfree (list_entry (item, struct frag, list));
+}
+
+/**
+ * ldm_frag_commit - Validate fragmented VBLKs and add them to the database
+ * @frags: Linked list of VBLK fragments
+ * @ldb: Cache of the database structures
+ *
+ * Now that all the fragmented VBLKs have been collected, they must be added to
+ * the database for later use.
+ *
+ * Return: TRUE All the fragments we added successfully
+ * FALSE One or more of the fragments we invalid
+ */
+static BOOL ldm_frag_commit (struct list_head *frags, struct ldmdb *ldb)
+{
+ struct frag *f;
+ struct list_head *item;
+
+ BUG_ON (!frags);
+ BUG_ON (!ldb);
+
+ list_for_each (item, frags) {
+ f = list_entry (item, struct frag, list);
+
+ if (f->map != 0xFF) {
+ ldm_error ("VBLK group %d is incomplete (0x%02x).",
+ f->group, f->map);
+ return FALSE;
+ }
+
+ if (!ldm_ldmdb_add (f->data, f->num*ldb->vm.vblk_size, ldb))
+ return FALSE; /* Already logged */
+ }
+ return TRUE;
+}
+
+/**
+ * ldm_get_vblks - Read the on-disk database of VBLKs into memory
+ * @bdev: Device holding the LDM Database
+ * @base: Offset, into @bdev, of the database
+ * @ldb: Cache of the database structures
+ *
+ * To use the information from the VBLKs, they need to be read from the disk,
+ * unpacked and validated. We cache them in @ldb according to their type.
+ *
+ * Return: TRUE All the VBLKs were read successfully
+ * FALSE An error occurred
+ */
+static BOOL ldm_get_vblks (struct block_device *bdev, unsigned long base,
+ struct ldmdb *ldb)
+{
+ int size, perbuf, skip, finish, s, v, recs;
+ u8 *data = NULL;
+ Sector sect;
+ BOOL result = FALSE;
+ LIST_HEAD (frags);
+
+ BUG_ON (!bdev);
+ BUG_ON (!ldb);
+
+ size = ldb->vm.vblk_size;
+ perbuf = 512 / size;
+ skip = ldb->vm.vblk_offset >> 9; /* Bytes to sectors */
+ finish = (size * ldb->vm.last_vblk_seq) >> 9;
+
+ for (s = skip; s < finish; s++) { /* For each sector */
+ data = read_dev_sector (bdev, base + OFF_VMDB + s, &sect);
+ if (!data) {
+ ldm_crit ("Disk read failed.");
+ goto out;
+ }
+
+ for (v = 0; v < perbuf; v++, data+=size) { /* For each vblk */
+ if (MAGIC_VBLK != BE32 (data)) {
+ ldm_error ("Expected to find a VBLK.");
+ goto out;
+ }
+
+ recs = BE16 (data + 0x0E); /* Number of records */
+ if (recs == 1) {
+ if (!ldm_ldmdb_add (data, size, ldb))
+ goto out; /* Already logged */
+ } else if (recs > 1) {
+ if (!ldm_frag_add (data, size, &frags))
+ goto out; /* Already logged */
+ }
+ /* else Record is not in use, ignore it. */
+ }
+ put_dev_sector (sect);
+ data = NULL;
+ }
+
+ result = ldm_frag_commit (&frags, ldb); /* Failures, already logged */
+out:
+ if (data)
+ put_dev_sector (sect);
+ ldm_frag_free (&frags);
+
+ return result;
+}
+
+/**
+ * ldm_free_vblks - Free a linked list of vblk's
+ * @lh: Head of a linked list of struct vblk
+ *
+ * Free a list of vblk's and free the memory used to maintain the list.
+ *
+ * Return: none
+ */
+static void ldm_free_vblks (struct list_head *lh)
+{
+ struct list_head *item, *tmp;
+
+ BUG_ON (!lh);
+
+ list_for_each_safe (item, tmp, lh)
+ kfree (list_entry (item, struct vblk, list));
+}
+
+
+/**
+ * ldm_partition - Find out whether a device is a dynamic disk and handle it
+ * @hd: gendisk structure in which to return the handled disk
+ * @bdev: Device we need to look at
+ * @first_sector: First sector within the device
+ * @first_minor: First minor number of partitions for the device
+ *
+ * This determines whether the device @bdev is a dynamic disk and if so creates
+ * the partitions necessary in the gendisk structure pointed to by @hd.
+ *
+ * We create a dummy device 1, which contains the LDM database, and then create
+ * each partition described by the LDM database in sequence as devices 2+. For
+ * example, if the device is hda, we would have: hda1: LDM database, hda2, hda3,
+ * and so on: the actual data containing partitions.
+ *
+ * Return: 1 Success, @bdev is a dynamic disk and we handled it
+ * 0 Success, @bdev is not a dynamic disk
+ * -1 An error occurred before enough information had been read
+ * Or @bdev is a dynamic disk, but it may be corrupted
+ */
+int ldm_partition (struct gendisk *hd, struct block_device *bdev,
+ unsigned long first_sector, int first_minor)
+{
+ struct ldmdb *ldb;
+ unsigned long base;
+ int result = -1;
+
+ BUG_ON (!hd);
+ BUG_ON (!bdev);
+
+ /* Look for signs of a Dynamic Disk */
+ if (!ldm_validate_partition_table (bdev))
+ return 0;
+
+ ldb = kmalloc (sizeof (*ldb), GFP_KERNEL);
+ if (!ldb) {
+ ldm_crit ("Out of memory.");
+ goto out;
+ }
+
+ /* Parse and check privheads. */
+ if (!ldm_validate_privheads (bdev, first_sector, &ldb->ph, hd, first_minor))
+ goto out; /* Already logged */
+
+ /* All further references are relative to base (database start). */
+ base = first_sector + ldb->ph.config_start;
+
+ /* Parse and check tocs and vmdb. */
+ if (!ldm_validate_tocblocks (bdev, base, ldb) ||
+ !ldm_validate_vmdb (bdev, base, ldb))
+ goto out; /* Already logged */
+
+ /* Initialize vblk lists in ldmdb struct */
+ INIT_LIST_HEAD (&ldb->v_dgrp);
+ INIT_LIST_HEAD (&ldb->v_disk);
+ INIT_LIST_HEAD (&ldb->v_volu);
+ INIT_LIST_HEAD (&ldb->v_comp);
+ INIT_LIST_HEAD (&ldb->v_part);
+
+ if (!ldm_get_vblks (bdev, base, ldb)) {
+ ldm_crit ("Failed to read the VBLKs from the database.");
+ goto cleanup;
+ }
+
+ /* Finally, create the data partition devices. */
+ if (ldm_create_data_partitions (hd, first_sector, first_minor, ldb)) {
+ ldm_debug ("Parsed LDM database successfully.");
+ result = 1;
+ }
+ /* else Already logged */
+
+cleanup:
+ ldm_free_vblks (&ldb->v_dgrp);
+ ldm_free_vblks (&ldb->v_disk);
+ ldm_free_vblks (&ldb->v_volu);
+ ldm_free_vblks (&ldb->v_comp);
+ ldm_free_vblks (&ldb->v_part);
+out:
+ kfree (ldb);
+ return result;
+}
+
diff --git a/fs/partitions/ldm.h b/fs/partitions/ldm.h
new file mode 100644
index 00000000000000..dfc3097d95ccc6
--- /dev/null
+++ b/fs/partitions/ldm.h
@@ -0,0 +1,221 @@
+/**
+ * ldm - Part of the Linux-NTFS project.
+ *
+ * Copyright (C) 2001,2002 Richard Russon <ldm@flatcap.org>
+ * Copyright (C) 2001 Anton Altaparmakov <aia21@cantab.net>
+ * Copyright (C) 2001,2002 Jakob Kemi <jakob.kemi@telia.com>
+ *
+ * Documentation is available at http://linux-ntfs.sf.net/ldm
+ *
+ * This program 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 of the License, or (at your option)
+ * any later version.
+ *
+ * This program 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 this program (in the main directory of the Linux-NTFS source
+ * in the file COPYING); if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _FS_PT_LDM_H_
+#define _FS_PT_LDM_H_
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/genhd.h>
+#include <linux/fs.h>
+#include <asm/unaligned.h>
+#include <asm/byteorder.h>
+
+struct parsed_partitions;
+
+/* Magic numbers in CPU format. */
+#define MAGIC_VMDB 0x564D4442 /* VMDB */
+#define MAGIC_VBLK 0x56424C4B /* VBLK */
+#define MAGIC_PRIVHEAD 0x5052495648454144ULL /* PRIVHEAD */
+#define MAGIC_TOCBLOCK 0x544F43424C4F434BULL /* TOCBLOCK */
+
+/* The defined vblk types. */
+#define VBLK_VOL5 0x51 /* Volume, version 5 */
+#define VBLK_CMP3 0x32 /* Component, version 3 */
+#define VBLK_PRT3 0x33 /* Partition, version 3 */
+#define VBLK_DSK3 0x34 /* Disk, version 3 */
+#define VBLK_DSK4 0x44 /* Disk, version 4 */
+#define VBLK_DGR3 0x35 /* Disk Group, version 3 */
+#define VBLK_DGR4 0x45 /* Disk Group, version 4 */
+
+/* vblk flags indicating extra information will be present */
+#define VBLK_FLAG_COMP_STRIPE 0x10
+#define VBLK_FLAG_PART_INDEX 0x08
+#define VBLK_FLAG_DGR3_IDS 0x08
+#define VBLK_FLAG_DGR4_IDS 0x08
+#define VBLK_FLAG_VOLU_ID1 0x08
+#define VBLK_FLAG_VOLU_ID2 0x20
+#define VBLK_FLAG_VOLU_SIZE 0x80
+#define VBLK_FLAG_VOLU_DRIVE 0x02
+
+/* size of a vblk's static parts */
+#define VBLK_SIZE_HEAD 16
+#define VBLK_SIZE_CMP3 22 /* Name and version */
+#define VBLK_SIZE_DGR3 12
+#define VBLK_SIZE_DGR4 44
+#define VBLK_SIZE_DSK3 12
+#define VBLK_SIZE_DSK4 45
+#define VBLK_SIZE_PRT3 28
+#define VBLK_SIZE_VOL5 59
+
+/* component types */
+#define COMP_STRIPE 0x01 /* Stripe-set */
+#define COMP_BASIC 0x02 /* Basic disk */
+#define COMP_RAID 0x03 /* Raid-set */
+
+/* Other constants. */
+#define LDM_DB_SIZE 2048 /* Size in sectors (= 1MiB). */
+
+#define OFF_PRIV1 6 /* Offset of the first privhead
+ relative to the start of the
+ device in sectors */
+
+/* Offsets to structures within the LDM Database in sectors. */
+#define OFF_PRIV2 1856 /* Backup private headers. */
+#define OFF_PRIV3 2047
+
+#define OFF_TOCB1 1 /* Tables of contents. */
+#define OFF_TOCB2 2
+#define OFF_TOCB3 2045
+#define OFF_TOCB4 2046
+
+#define OFF_VMDB 17 /* List of partitions. */
+
+#define WIN2K_DYNAMIC_PARTITION 0x42 /* Formerly SFS (Landis). */
+
+#define TOC_BITMAP1 "config" /* Names of the two defined */
+#define TOC_BITMAP2 "log" /* bitmaps in the TOCBLOCK. */
+
+/* Most numbers we deal with are big-endian and won't be aligned. */
+#define BE16(x) ((u16)be16_to_cpu(get_unaligned((u16*)(x))))
+#define BE32(x) ((u32)be32_to_cpu(get_unaligned((u32*)(x))))
+#define BE64(x) ((u64)be64_to_cpu(get_unaligned((u64*)(x))))
+
+/* Borrowed from msdos.c */
+#define SYS_IND(p) (get_unaligned(&(p)->sys_ind))
+
+struct frag { /* VBLK Fragment handling */
+ struct list_head list;
+ u32 group;
+ u8 num; /* Total number of records */
+ u8 rec; /* This is record number n */
+ u8 map; /* Which portions are in use */
+ u8 data[0];
+};
+
+/* In memory LDM database structures. */
+
+#define GUID_SIZE 16
+
+struct privhead { /* Offsets and sizes are in sectors. */
+ u16 ver_major;
+ u16 ver_minor;
+ u64 logical_disk_start;
+ u64 logical_disk_size;
+ u64 config_start;
+ u64 config_size;
+ u8 disk_id[GUID_SIZE];
+};
+
+struct tocblock { /* We have exactly two bitmaps. */
+ u8 bitmap1_name[16];
+ u64 bitmap1_start;
+ u64 bitmap1_size;
+ u8 bitmap2_name[16];
+ u64 bitmap2_start;
+ u64 bitmap2_size;
+};
+
+struct vmdb { /* VMDB: The database header */
+ u16 ver_major;
+ u16 ver_minor;
+ u32 vblk_size;
+ u32 vblk_offset;
+ u32 last_vblk_seq;
+};
+
+struct vblk_comp { /* VBLK Component */
+ u8 state[16];
+ u64 parent_id;
+ u8 type;
+ u8 children;
+ u16 chunksize;
+};
+
+struct vblk_dgrp { /* VBLK Disk Group */
+ u8 disk_id[64];
+};
+
+struct vblk_disk { /* VBLK Disk */
+ u8 disk_id[GUID_SIZE];
+ u8 alt_name[128];
+};
+
+struct vblk_part { /* VBLK Partition */
+ u64 start;
+ u64 size; /* start, size and vol_off in sectors */
+ u64 volume_offset;
+ u64 parent_id;
+ u64 disk_id;
+ u8 partnum;
+};
+
+struct vblk_volu { /* VBLK Volume */
+ u8 volume_type[16];
+ u8 volume_state[16];
+ u8 guid[16];
+ u8 drive_hint[4];
+ u64 size;
+ u8 partition_type;
+};
+
+struct vblk_head { /* VBLK standard header */
+ u32 group;
+ u16 rec;
+ u16 nrec;
+};
+
+struct vblk { /* Generalised VBLK */
+ u8 name[64];
+ u64 obj_id;
+ u32 sequence;
+ u8 flags;
+ u8 type;
+ union {
+ struct vblk_comp comp;
+ struct vblk_dgrp dgrp;
+ struct vblk_disk disk;
+ struct vblk_part part;
+ struct vblk_volu volu;
+ } vblk;
+ struct list_head list;
+};
+
+struct ldmdb { /* Cache of the database */
+ struct privhead ph;
+ struct tocblock toc;
+ struct vmdb vm;
+ struct list_head v_dgrp;
+ struct list_head v_disk;
+ struct list_head v_volu;
+ struct list_head v_comp;
+ struct list_head v_part;
+};
+
+int ldm_partition (struct gendisk *hd, struct block_device *bdev,
+ unsigned long first_sector, int first_minor);
+
+#endif /* _FS_PT_LDM_H_ */
+
diff --git a/fs/partitions/mac.c b/fs/partitions/mac.c
new file mode 100644
index 00000000000000..52c898e30a1122
--- /dev/null
+++ b/fs/partitions/mac.c
@@ -0,0 +1,77 @@
+/*
+ * fs/partitions/mac.c
+ *
+ * Code extracted from drivers/block/genhd.c
+ * Copyright (C) 1991-1998 Linus Torvalds
+ * Re-organised Feb 1998 Russell King
+ */
+
+#include <linux/config.h>
+#include <linux/fs.h>
+#include <linux/genhd.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/blk.h>
+#include <linux/ctype.h>
+
+#include <asm/system.h>
+
+#include "check.h"
+#include "mac.h"
+
+/*
+ * Code to understand MacOS partition tables.
+ */
+
+int mac_partition(struct gendisk *hd, struct block_device *bdev,
+ unsigned long fsec, int first_part_minor)
+{
+ Sector sect;
+ unsigned char *data;
+ int blk, blocks_in_map;
+ unsigned secsize;
+ struct mac_partition *part;
+ struct mac_driver_desc *md;
+
+ /* Get 0th block and look at the first partition map entry. */
+ md = (struct mac_driver_desc *) read_dev_sector(bdev, 0, &sect);
+ if (!md)
+ return -1;
+ if (be16_to_cpu(md->signature) != MAC_DRIVER_MAGIC) {
+ put_dev_sector(sect);
+ return 0;
+ }
+ secsize = be16_to_cpu(md->block_size);
+ put_dev_sector(sect);
+ data = read_dev_sector(bdev, secsize/512, &sect);
+ if (!data)
+ return -1;
+ part = (struct mac_partition *) (data + secsize%512);
+ if (be16_to_cpu(part->signature) != MAC_PARTITION_MAGIC) {
+ put_dev_sector(sect);
+ return 0; /* not a MacOS disk */
+ }
+ printk(" [mac]");
+ blocks_in_map = be32_to_cpu(part->map_count);
+ for (blk = 1; blk <= blocks_in_map; ++blk) {
+ int pos = blk * secsize;
+ put_dev_sector(sect);
+ data = read_dev_sector(bdev, pos/512, &sect);
+ if (!data)
+ return -1;
+ part = (struct mac_partition *) (data + pos%512);
+ if (be16_to_cpu(part->signature) != MAC_PARTITION_MAGIC)
+ break;
+ add_gd_partition(hd, first_part_minor,
+ fsec + be32_to_cpu(part->start_block) * (secsize/512),
+ be32_to_cpu(part->block_count) * (secsize/512));
+
+ ++first_part_minor;
+ }
+
+ put_dev_sector(sect);
+ printk("\n");
+ return 1;
+}
+
diff --git a/fs/partitions/mac.h b/fs/partitions/mac.h
new file mode 100644
index 00000000000000..cbd8861865694a
--- /dev/null
+++ b/fs/partitions/mac.h
@@ -0,0 +1,52 @@
+/*
+ * fs/partitions/mac.h
+ */
+
+#define MAC_PARTITION_MAGIC 0x504d
+
+/* type field value for A/UX or other Unix partitions */
+#define APPLE_AUX_TYPE "Apple_UNIX_SVR2"
+
+struct mac_partition {
+ __u16 signature; /* expected to be MAC_PARTITION_MAGIC */
+ __u16 res1;
+ __u32 map_count; /* # blocks in partition map */
+ __u32 start_block; /* absolute starting block # of partition */
+ __u32 block_count; /* number of blocks in partition */
+ char name[32]; /* partition name */
+ char type[32]; /* string type description */
+ __u32 data_start; /* rel block # of first data block */
+ __u32 data_count; /* number of data blocks */
+ __u32 status; /* partition status bits */
+ __u32 boot_start;
+ __u32 boot_size;
+ __u32 boot_load;
+ __u32 boot_load2;
+ __u32 boot_entry;
+ __u32 boot_entry2;
+ __u32 boot_cksum;
+ char processor[16]; /* identifies ISA of boot */
+ /* there is more stuff after this that we don't need */
+};
+
+static inline void macpart_fix_string(char *stg, int len)
+{
+ int i;
+
+ for (i = len - 1; i >= 0 && stg[i] == ' '; i--)
+ stg[i] = 0;
+}
+
+#define MAC_STATUS_BOOTABLE 8 /* partition is bootable */
+
+#define MAC_DRIVER_MAGIC 0x4552
+
+/* Driver descriptor structure, in block 0 */
+struct mac_driver_desc {
+ __u16 signature; /* expected to be MAC_DRIVER_MAGIC */
+ __u16 block_size;
+ __u32 block_count;
+ /* ... more stuff */
+};
+
+int mac_partition(struct gendisk *hd, struct block_device *bdev, unsigned long fsec, int first_part_minor);
diff --git a/fs/partitions/msdos.c b/fs/partitions/msdos.c
new file mode 100644
index 00000000000000..46b15a4e9d13c6
--- /dev/null
+++ b/fs/partitions/msdos.c
@@ -0,0 +1,641 @@
+/*
+ * fs/partitions/msdos.c
+ *
+ * Code extracted from drivers/block/genhd.c
+ * Copyright (C) 1991-1998 Linus Torvalds
+ *
+ * Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
+ * in the early extended-partition checks and added DM partitions
+ *
+ * Support for DiskManager v6.0x added by Mark Lord,
+ * with information provided by OnTrack. This now works for linux fdisk
+ * and LILO, as well as loadlin and bootln. Note that disks other than
+ * /dev/hda *must* have a "DOS" type 0x51 partition in the first slot (hda1).
+ *
+ * More flexible handling of extended partitions - aeb, 950831
+ *
+ * Check partition table on IDE disks for common CHS translations
+ *
+ * Re-organised Feb 1998 Russell King
+ */
+
+#include <linux/config.h>
+#include <linux/fs.h>
+#include <linux/genhd.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/blk.h>
+
+#ifdef CONFIG_BLK_DEV_IDE
+#include <linux/ide.h> /* IDE xlate */
+#elif defined(CONFIG_BLK_DEV_IDE_MODULE)
+#include <linux/module.h>
+
+int (*ide_xlate_1024_hook)(kdev_t, int, int, const char *);
+EXPORT_SYMBOL(ide_xlate_1024_hook);
+#define ide_xlate_1024 ide_xlate_1024_hook
+#endif
+
+#include <asm/system.h>
+
+#include "check.h"
+#include "msdos.h"
+
+#if CONFIG_BLK_DEV_MD
+extern void md_autodetect_dev(kdev_t dev);
+#endif
+
+/*
+ * Many architectures don't like unaligned accesses, which is
+ * frequently the case with the nr_sects and start_sect partition
+ * table entries.
+ */
+#include <asm/unaligned.h>
+
+#define SYS_IND(p) (get_unaligned(&p->sys_ind))
+#define NR_SECTS(p) ({ __typeof__(p->nr_sects) __a = \
+ get_unaligned(&p->nr_sects); \
+ le32_to_cpu(__a); \
+ })
+
+#define START_SECT(p) ({ __typeof__(p->start_sect) __a = \
+ get_unaligned(&p->start_sect); \
+ le32_to_cpu(__a); \
+ })
+
+static inline int is_extended_partition(struct partition *p)
+{
+ return (SYS_IND(p) == DOS_EXTENDED_PARTITION ||
+ SYS_IND(p) == WIN98_EXTENDED_PARTITION ||
+ SYS_IND(p) == LINUX_EXTENDED_PARTITION);
+}
+
+/*
+ * msdos_partition_name() formats the short partition name into the supplied
+ * buffer, and returns a pointer to that buffer.
+ * Used by several partition types which makes conditional inclusion messy,
+ * use __attribute__ ((unused)) instead.
+ */
+static char __attribute__ ((unused))
+ *msdos_partition_name (struct gendisk *hd, int minor, char *buf)
+{
+#ifdef CONFIG_DEVFS_FS
+ sprintf(buf, "p%d", (minor & ((1 << hd->minor_shift) - 1)));
+ return buf;
+#else
+ return disk_name(hd, minor, buf);
+#endif
+}
+
+#define MSDOS_LABEL_MAGIC1 0x55
+#define MSDOS_LABEL_MAGIC2 0xAA
+
+static inline int
+msdos_magic_present(unsigned char *p)
+{
+ return (p[0] == MSDOS_LABEL_MAGIC1 && p[1] == MSDOS_LABEL_MAGIC2);
+}
+
+/*
+ * Create devices for each logical partition in an extended partition.
+ * The logical partitions form a linked list, with each entry being
+ * a partition table with two entries. The first entry
+ * is the real data partition (with a start relative to the partition
+ * table start). The second is a pointer to the next logical partition
+ * (with a start relative to the entire extended partition).
+ * We do not create a Linux partition for the partition tables, but
+ * only for the actual data partitions.
+ */
+
+static void extended_partition(struct gendisk *hd, struct block_device *bdev,
+ int minor, unsigned long first_size, int *current_minor)
+{
+ struct partition *p;
+ Sector sect;
+ unsigned char *data;
+ unsigned long first_sector, this_sector, this_size;
+ int mask = (1 << hd->minor_shift) - 1;
+ int sector_size = get_hardsect_size(to_kdev_t(bdev->bd_dev)) / 512;
+ int loopct = 0; /* number of links followed
+ without finding a data partition */
+ int i;
+
+ this_sector = first_sector = hd->part[minor].start_sect;
+ this_size = first_size;
+
+ while (1) {
+ if (++loopct > 100)
+ return;
+ if ((*current_minor & mask) == 0)
+ return;
+ data = read_dev_sector(bdev, this_sector, &sect);
+ if (!data)
+ return;
+
+ if (!msdos_magic_present(data + 510))
+ goto done;
+
+ p = (struct partition *) (data + 0x1be);
+
+ /*
+ * Usually, the first entry is the real data partition,
+ * the 2nd entry is the next extended partition, or empty,
+ * and the 3rd and 4th entries are unused.
+ * However, DRDOS sometimes has the extended partition as
+ * the first entry (when the data partition is empty),
+ * and OS/2 seems to use all four entries.
+ */
+
+ /*
+ * First process the data partition(s)
+ */
+ for (i=0; i<4; i++, p++) {
+ unsigned long offs, size, next;
+ if (!NR_SECTS(p) || is_extended_partition(p))
+ continue;
+
+ /* Check the 3rd and 4th entries -
+ these sometimes contain random garbage */
+ offs = START_SECT(p)*sector_size;
+ size = NR_SECTS(p)*sector_size;
+ next = this_sector + offs;
+ if (i >= 2) {
+ if (offs + size > this_size)
+ continue;
+ if (next < first_sector)
+ continue;
+ if (next + size > first_sector + first_size)
+ continue;
+ }
+
+ add_gd_partition(hd, *current_minor, next, size);
+#if CONFIG_BLK_DEV_MD
+ if (SYS_IND(p) == LINUX_RAID_PARTITION) {
+ md_autodetect_dev(MKDEV(hd->major,*current_minor));
+ }
+#endif
+
+ (*current_minor)++;
+ loopct = 0;
+ if ((*current_minor & mask) == 0)
+ goto done;
+ }
+ /*
+ * Next, process the (first) extended partition, if present.
+ * (So far, there seems to be no reason to make
+ * extended_partition() recursive and allow a tree
+ * of extended partitions.)
+ * It should be a link to the next logical partition.
+ * Create a minor for this just long enough to get the next
+ * partition table. The minor will be reused for the next
+ * data partition.
+ */
+ p -= 4;
+ for (i=0; i<4; i++, p++)
+ if (NR_SECTS(p) && is_extended_partition(p))
+ break;
+ if (i == 4)
+ goto done; /* nothing left to do */
+
+ this_sector = first_sector + START_SECT(p) * sector_size;
+ this_size = NR_SECTS(p) * sector_size;
+ minor = *current_minor;
+ put_dev_sector(sect);
+ }
+done:
+ put_dev_sector(sect);
+}
+
+/* james@bpgc.com: Solaris has a nasty indicator: 0x82 which also
+ indicates linux swap. Be careful before believing this is Solaris. */
+
+static void
+solaris_x86_partition(struct gendisk *hd, struct block_device *bdev,
+ int minor, int *current_minor)
+{
+
+#ifdef CONFIG_SOLARIS_X86_PARTITION
+ long offset = hd->part[minor].start_sect;
+ Sector sect;
+ struct solaris_x86_vtoc *v;
+ struct solaris_x86_slice *s;
+ int mask = (1 << hd->minor_shift) - 1;
+ int i;
+ char buf[40];
+
+ v = (struct solaris_x86_vtoc *)read_dev_sector(bdev, offset+1, &sect);
+ if (!v)
+ return;
+ if (le32_to_cpu(v->v_sanity) != SOLARIS_X86_VTOC_SANE) {
+ put_dev_sector(sect);
+ return;
+ }
+ printk(" %s: <solaris:", msdos_partition_name(hd, minor, buf));
+ if (le32_to_cpu(v->v_version) != 1) {
+ printk(" cannot handle version %d vtoc>\n",
+ le32_to_cpu(v->v_version));
+ put_dev_sector(sect);
+ return;
+ }
+ for (i=0; i<SOLARIS_X86_NUMSLICE; i++) {
+ if ((*current_minor & mask) == 0)
+ break;
+ s = &v->v_slice[i];
+
+ if (s->s_size == 0)
+ continue;
+ printk(" [s%d]", i);
+ /* solaris partitions are relative to current MS-DOS
+ * one but add_gd_partition starts relative to sector
+ * zero of the disk. Therefore, must add the offset
+ * of the current partition */
+ add_gd_partition(hd, *current_minor,
+ le32_to_cpu(s->s_start)+offset,
+ le32_to_cpu(s->s_size));
+ (*current_minor)++;
+ }
+ put_dev_sector(sect);
+ printk(" >\n");
+#endif
+}
+
+#ifdef CONFIG_BSD_DISKLABEL
+static void
+check_and_add_bsd_partition(struct gendisk *hd, struct bsd_partition *bsd_p,
+ int baseminor, int *current_minor)
+{
+ int i, bsd_start, bsd_size;
+
+ bsd_start = le32_to_cpu(bsd_p->p_offset);
+ bsd_size = le32_to_cpu(bsd_p->p_size);
+
+ /* check relative position of already allocated partitions */
+ for (i = baseminor+1; i < *current_minor; i++) {
+ int start = hd->part[i].start_sect;
+ int size = hd->part[i].nr_sects;
+
+ if (start+size <= bsd_start || start >= bsd_start+bsd_size)
+ continue; /* no overlap */
+
+ if (start == bsd_start && size == bsd_size)
+ return; /* equal -> no need to add */
+
+ if (start <= bsd_start && start+size >= bsd_start+bsd_size) {
+ /* bsd living within dos partition */
+#ifdef DEBUG_BSD_DISKLABEL
+ printk("w: %d %ld+%ld,%d+%d",
+ i, start, size, bsd_start, bsd_size);
+#endif
+ break; /* ok */
+ }
+
+ /* ouch: bsd and linux overlap */
+#ifdef DEBUG_BSD_DISKLABEL
+ printk("???: %d %ld+%ld,%d+%d",
+ i, start, size, bsd_start, bsd_size);
+#endif
+ printk("???");
+ return;
+ }
+
+ add_gd_partition(hd, *current_minor, bsd_start, bsd_size);
+ (*current_minor)++;
+}
+
+/*
+ * Create devices for BSD partitions listed in a disklabel, under a
+ * dos-like partition. See extended_partition() for more information.
+ */
+static void do_bsd_partition(struct gendisk *hd, struct block_device *bdev,
+ int minor, int *current_minor, char *name, int max_partitions)
+{
+ long offset = hd->part[minor].start_sect;
+ Sector sect;
+ struct bsd_disklabel *l;
+ struct bsd_partition *p;
+ int mask = (1 << hd->minor_shift) - 1;
+ int baseminor = (minor & ~mask);
+ char buf[40];
+
+ l = (struct bsd_disklabel *)read_dev_sector(bdev, offset+1, &sect);
+ if (!l)
+ return;
+ if (le32_to_cpu(l->d_magic) != BSD_DISKMAGIC) {
+ put_dev_sector(sect);
+ return;
+ }
+ printk(" %s: <%s:", msdos_partition_name(hd, minor, buf), name);
+
+ if (le16_to_cpu(l->d_npartitions) < max_partitions)
+ max_partitions = le16_to_cpu(l->d_npartitions);
+ for (p = l->d_partitions; p - l->d_partitions < max_partitions; p++) {
+ if ((*current_minor & mask) == 0)
+ break;
+ if (p->p_fstype == BSD_FS_UNUSED)
+ continue;
+ check_and_add_bsd_partition(hd, p, baseminor, current_minor);
+ }
+ put_dev_sector(sect);
+ printk(" >\n");
+}
+#endif
+
+static void bsd_partition(struct gendisk *hd, struct block_device *bdev,
+ int minor, int *current_minor)
+{
+#ifdef CONFIG_BSD_DISKLABEL
+ do_bsd_partition(hd, bdev, minor, current_minor, "bsd",
+ BSD_MAXPARTITIONS);
+#endif
+}
+
+static void netbsd_partition(struct gendisk *hd, struct block_device *bdev,
+ int minor, int *current_minor)
+{
+#ifdef CONFIG_BSD_DISKLABEL
+ do_bsd_partition(hd, bdev, minor, current_minor, "netbsd",
+ BSD_MAXPARTITIONS);
+#endif
+}
+
+static void openbsd_partition(struct gendisk *hd, struct block_device *bdev,
+ int minor, int *current_minor)
+{
+#ifdef CONFIG_BSD_DISKLABEL
+ do_bsd_partition(hd, bdev, minor, current_minor,
+ "openbsd", OPENBSD_MAXPARTITIONS);
+#endif
+}
+
+/*
+ * Create devices for Unixware partitions listed in a disklabel, under a
+ * dos-like partition. See extended_partition() for more information.
+ */
+static void unixware_partition(struct gendisk *hd, struct block_device *bdev,
+ int minor, int *current_minor)
+{
+#ifdef CONFIG_UNIXWARE_DISKLABEL
+ long offset = hd->part[minor].start_sect;
+ Sector sect;
+ struct unixware_disklabel *l;
+ struct unixware_slice *p;
+ int mask = (1 << hd->minor_shift) - 1;
+ char buf[40];
+
+ l = (struct unixware_disklabel *)read_dev_sector(bdev, offset+29, &sect);
+ if (!l)
+ return;
+ if (le32_to_cpu(l->d_magic) != UNIXWARE_DISKMAGIC ||
+ le32_to_cpu(l->vtoc.v_magic) != UNIXWARE_DISKMAGIC2) {
+ put_dev_sector(sect);
+ return;
+ }
+ printk(" %s: <unixware:", msdos_partition_name(hd, minor, buf));
+ p = &l->vtoc.v_slice[1];
+ /* I omit the 0th slice as it is the same as whole disk. */
+ while (p - &l->vtoc.v_slice[0] < UNIXWARE_NUMSLICE) {
+ if ((*current_minor & mask) == 0)
+ break;
+
+ if (p->s_label != UNIXWARE_FS_UNUSED) {
+ add_gd_partition(hd, *current_minor, START_SECT(p),
+ NR_SECTS(p));
+ (*current_minor)++;
+ }
+ p++;
+ }
+ put_dev_sector(sect);
+ printk(" >\n");
+#endif
+}
+
+/*
+ * Minix 2.0.0/2.0.2 subpartition support.
+ * Anand Krishnamurthy <anandk@wiproge.med.ge.com>
+ * Rajeev V. Pillai <rajeevvp@yahoo.com>
+ */
+static void minix_partition(struct gendisk *hd, struct block_device *bdev,
+ int minor, int *current_minor)
+{
+#ifdef CONFIG_MINIX_SUBPARTITION
+ long offset = hd->part[minor].start_sect;
+ Sector sect;
+ unsigned char *data;
+ struct partition *p;
+ int mask = (1 << hd->minor_shift) - 1;
+ int i;
+ char buf[40];
+
+ data = read_dev_sector(bdev, offset, &sect);
+ if (!data)
+ return;
+
+ p = (struct partition *)(data + 0x1be);
+
+ /* The first sector of a Minix partition can have either
+ * a secondary MBR describing its subpartitions, or
+ * the normal boot sector. */
+ if (msdos_magic_present (data + 510) &&
+ SYS_IND(p) == MINIX_PARTITION) { /* subpartition table present */
+
+ printk(" %s: <minix:", msdos_partition_name(hd, minor, buf));
+ for (i = 0; i < MINIX_NR_SUBPARTITIONS; i++, p++) {
+ if ((*current_minor & mask) == 0)
+ break;
+ /* add each partition in use */
+ if (SYS_IND(p) == MINIX_PARTITION) {
+ add_gd_partition(hd, *current_minor,
+ START_SECT(p), NR_SECTS(p));
+ (*current_minor)++;
+ }
+ }
+ printk(" >\n");
+ }
+ put_dev_sector(sect);
+#endif /* CONFIG_MINIX_SUBPARTITION */
+}
+
+static struct {
+ unsigned char id;
+ void (*parse)(struct gendisk *, struct block_device *, int, int *);
+} subtypes[] = {
+ {BSD_PARTITION, bsd_partition},
+ {NETBSD_PARTITION, netbsd_partition},
+ {OPENBSD_PARTITION, openbsd_partition},
+ {MINIX_PARTITION, minix_partition},
+ {UNIXWARE_PARTITION, unixware_partition},
+ {SOLARIS_X86_PARTITION, solaris_x86_partition},
+ {0, NULL},
+};
+/*
+ * Look for various forms of IDE disk geometry translation
+ */
+static int handle_ide_mess(struct block_device *bdev)
+{
+#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
+ Sector sect;
+ unsigned char *data;
+ kdev_t dev = to_kdev_t(bdev->bd_dev);
+ unsigned int sig;
+ int heads = 0;
+ struct partition *p;
+ int i;
+#ifdef CONFIG_BLK_DEV_IDE_MODULE
+ if (!ide_xlate_1024)
+ return 1;
+#endif
+ /*
+ * The i386 partition handling programs very often
+ * make partitions end on cylinder boundaries.
+ * There is no need to do so, and Linux fdisk doesn't always
+ * do this, and Windows NT on Alpha doesn't do this either,
+ * but still, this helps to guess #heads.
+ */
+ data = read_dev_sector(bdev, 0, &sect);
+ if (!data)
+ return -1;
+ if (!msdos_magic_present(data + 510)) {
+ put_dev_sector(sect);
+ return 0;
+ }
+ sig = le16_to_cpu(*(unsigned short *)(data + 2));
+ p = (struct partition *) (data + 0x1be);
+ for (i = 0; i < 4; i++) {
+ struct partition *q = &p[i];
+ if (NR_SECTS(q)) {
+ if ((q->sector & 63) == 1 &&
+ (q->end_sector & 63) == 63)
+ heads = q->end_head + 1;
+ break;
+ }
+ }
+ if (SYS_IND(p) == EZD_PARTITION) {
+ /*
+ * Accesses to sector 0 must go to sector 1 instead.
+ */
+ if (ide_xlate_1024(dev, -1, heads, " [EZD]"))
+ goto reread;
+ } else if (SYS_IND(p) == DM6_PARTITION) {
+
+ /*
+ * Everything on the disk is offset by 63 sectors,
+ * including a "new" MBR with its own partition table.
+ */
+ if (ide_xlate_1024(dev, 1, heads, " [DM6:DDO]"))
+ goto reread;
+ } else if (sig <= 0x1ae &&
+ data[sig] == 0xAA && data[sig+1] == 0x55 &&
+ (data[sig+2] & 1)) {
+ /* DM6 signature in MBR, courtesy of OnTrack */
+ (void) ide_xlate_1024 (dev, 0, heads, " [DM6:MBR]");
+ } else if (SYS_IND(p) == DM6_AUX1PARTITION ||
+ SYS_IND(p) == DM6_AUX3PARTITION) {
+ /*
+ * DM6 on other than the first (boot) drive
+ */
+ (void) ide_xlate_1024(dev, 0, heads, " [DM6:AUX]");
+ } else {
+ (void) ide_xlate_1024(dev, 2, heads, " [PTBL]");
+ }
+ put_dev_sector(sect);
+ return 1;
+
+reread:
+ put_dev_sector(sect);
+ /* Flush the cache */
+ invalidate_bdev(bdev, 1);
+ truncate_inode_pages(bdev->bd_inode->i_mapping, 0);
+#endif /* defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) */
+ return 1;
+}
+
+int msdos_partition(struct gendisk *hd, struct block_device *bdev,
+ unsigned long first_sector, int first_part_minor)
+{
+ int i, minor = first_part_minor;
+ Sector sect;
+ struct partition *p;
+ unsigned char *data;
+ int mask = (1 << hd->minor_shift) - 1;
+ int sector_size = get_hardsect_size(to_kdev_t(bdev->bd_dev)) / 512;
+ int current_minor = first_part_minor;
+ int err;
+
+ err = handle_ide_mess(bdev);
+ if (err <= 0)
+ return err;
+ data = read_dev_sector(bdev, 0, &sect);
+ if (!data)
+ return -1;
+ if (!msdos_magic_present(data + 510)) {
+ put_dev_sector(sect);
+ return 0;
+ }
+ p = (struct partition *) (data + 0x1be);
+
+ /*
+ * Look for partitions in two passes:
+ * First find the primary and DOS-type extended partitions.
+ * On the second pass look inside *BSD, Unixware and Solaris partitions.
+ */
+
+ current_minor += 4;
+ for (i=1 ; i<=4 ; minor++,i++,p++) {
+ if (!NR_SECTS(p))
+ continue;
+ add_gd_partition(hd, minor,
+ first_sector+START_SECT(p)*sector_size,
+ NR_SECTS(p)*sector_size);
+#if CONFIG_BLK_DEV_MD
+ if (SYS_IND(p) == LINUX_RAID_PARTITION) {
+ md_autodetect_dev(MKDEV(hd->major,minor));
+ }
+#endif
+ if (is_extended_partition(p)) {
+ unsigned long size = hd->part[minor].nr_sects;
+ printk(" <");
+ /* prevent someone doing mkfs or mkswap on an
+ extended partition, but leave room for LILO */
+ if (size > 2)
+ hd->part[minor].nr_sects = 2;
+ extended_partition(hd, bdev, minor, size, &current_minor);
+ printk(" >");
+ }
+ }
+
+ /*
+ * Check for old-style Disk Manager partition table
+ */
+ if (msdos_magic_present(data + 0xfc)) {
+ p = (struct partition *) (0x1be + data);
+ for (i = 4 ; i < 16 ; i++, current_minor++) {
+ p--;
+ if ((current_minor & mask) == 0)
+ break;
+ if (!(START_SECT(p) && NR_SECTS(p)))
+ continue;
+ add_gd_partition(hd, current_minor, START_SECT(p), NR_SECTS(p));
+ }
+ }
+ printk("\n");
+
+ /* second pass - output for each on a separate line */
+ minor -= 4;
+ p = (struct partition *) (0x1be + data);
+ for (i=1 ; i<=4 ; minor++,i++,p++) {
+ unsigned char id = SYS_IND(p);
+ int n;
+
+ if (!NR_SECTS(p))
+ continue;
+
+ for (n = 0; subtypes[n].parse && id != subtypes[n].id; n++)
+ ;
+
+ if (subtypes[n].parse)
+ subtypes[n].parse(hd, bdev, minor, &current_minor);
+ }
+ put_dev_sector(sect);
+ return 1;
+}
diff --git a/fs/partitions/msdos.h b/fs/partitions/msdos.h
new file mode 100644
index 00000000000000..09c8b095ad5609
--- /dev/null
+++ b/fs/partitions/msdos.h
@@ -0,0 +1,9 @@
+/*
+ * fs/partitions/msdos.h
+ */
+
+#define MSDOS_LABEL_MAGIC 0xAA55
+
+int msdos_partition(struct gendisk *hd, struct block_device *bdev,
+ unsigned long first_sector, int first_part_minor);
+
diff --git a/fs/partitions/osf.c b/fs/partitions/osf.c
new file mode 100644
index 00000000000000..e178ca2b6543b4
--- /dev/null
+++ b/fs/partitions/osf.c
@@ -0,0 +1,87 @@
+/*
+ * fs/partitions/osf.c
+ *
+ * Code extracted from drivers/block/genhd.c
+ *
+ * Copyright (C) 1991-1998 Linus Torvalds
+ * Re-organised Feb 1998 Russell King
+ */
+
+#include <linux/fs.h>
+#include <linux/genhd.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/blk.h>
+
+#include "check.h"
+#include "osf.h"
+
+int osf_partition(struct gendisk *hd, struct block_device *bdev,
+ unsigned long first_sector, int current_minor)
+{
+ int i;
+ Sector sect;
+ unsigned char *data;
+ int mask = (1 << hd->minor_shift) - 1;
+ struct disklabel {
+ u32 d_magic;
+ u16 d_type,d_subtype;
+ u8 d_typename[16];
+ u8 d_packname[16];
+ u32 d_secsize;
+ u32 d_nsectors;
+ u32 d_ntracks;
+ u32 d_ncylinders;
+ u32 d_secpercyl;
+ u32 d_secprtunit;
+ u16 d_sparespertrack;
+ u16 d_sparespercyl;
+ u32 d_acylinders;
+ u16 d_rpm, d_interleave, d_trackskew, d_cylskew;
+ u32 d_headswitch, d_trkseek, d_flags;
+ u32 d_drivedata[5];
+ u32 d_spare[5];
+ u32 d_magic2;
+ u16 d_checksum;
+ u16 d_npartitions;
+ u32 d_bbsize, d_sbsize;
+ struct d_partition {
+ u32 p_size;
+ u32 p_offset;
+ u32 p_fsize;
+ u8 p_fstype;
+ u8 p_frag;
+ u16 p_cpg;
+ } d_partitions[8];
+ } * label;
+ struct d_partition * partition;
+
+ data = read_dev_sector(bdev, 0, &sect);
+ if (!data)
+ return -1;
+
+ label = (struct disklabel *) (data+64);
+ partition = label->d_partitions;
+ if (le32_to_cpu(label->d_magic) != DISKLABELMAGIC) {
+ put_dev_sector(sect);
+ return 0;
+ }
+ if (le32_to_cpu(label->d_magic2) != DISKLABELMAGIC) {
+ put_dev_sector(sect);
+ return 0;
+ }
+ for (i = 0 ; i < le16_to_cpu(label->d_npartitions); i++, partition++) {
+ if ((current_minor & mask) == 0)
+ break;
+ if (le32_to_cpu(partition->p_size))
+ add_gd_partition(hd, current_minor,
+ first_sector+le32_to_cpu(partition->p_offset),
+ le32_to_cpu(partition->p_size));
+ current_minor++;
+ }
+ printk("\n");
+ put_dev_sector(sect);
+ return 1;
+}
+
diff --git a/fs/partitions/osf.h b/fs/partitions/osf.h
new file mode 100644
index 00000000000000..6f02393c490b6d
--- /dev/null
+++ b/fs/partitions/osf.h
@@ -0,0 +1,9 @@
+/*
+ * fs/partitions/osf.h
+ */
+
+#define DISKLABELMAGIC (0x82564557UL)
+
+int osf_partition(struct gendisk *hd, struct block_device *bdev,
+ unsigned long first_sector, int current_minor);
+
diff --git a/fs/partitions/sgi.c b/fs/partitions/sgi.c
new file mode 100644
index 00000000000000..ea74ebb4b57b59
--- /dev/null
+++ b/fs/partitions/sgi.c
@@ -0,0 +1,85 @@
+/*
+ * fs/partitions/sgi.c
+ *
+ * Code extracted from drivers/block/genhd.c
+ */
+
+#include <linux/fs.h>
+#include <linux/genhd.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/blk.h>
+
+#include <asm/byteorder.h>
+#include <asm/system.h>
+
+#include "check.h"
+#include "sgi.h"
+
+int sgi_partition(struct gendisk *hd, struct block_device *bdev, unsigned long first_sector, int current_minor)
+{
+ int i, csum, magic;
+ unsigned int *ui, start, blocks, cs;
+ Sector sect;
+ kdev_t dev = to_kdev_t(bdev->bd_dev);
+ struct sgi_disklabel {
+ int magic_mushroom; /* Big fat spliff... */
+ short root_part_num; /* Root partition number */
+ short swap_part_num; /* Swap partition number */
+ char boot_file[16]; /* Name of boot file for ARCS */
+ unsigned char _unused0[48]; /* Device parameter useless crapola.. */
+ struct sgi_volume {
+ char name[8]; /* Name of volume */
+ int block_num; /* Logical block number */
+ int num_bytes; /* How big, in bytes */
+ } volume[15];
+ struct sgi_partition {
+ int num_blocks; /* Size in logical blocks */
+ int first_block; /* First logical block */
+ int type; /* Type of this partition */
+ } partitions[16];
+ int csum; /* Disk label checksum */
+ int _unused1; /* Padding */
+ } *label;
+ struct sgi_partition *p;
+
+ label = (struct sgi_disklabel *) read_dev_sector(bdev, 0, &sect);
+ if (!label)
+ return -1;
+ p = &label->partitions[0];
+ magic = label->magic_mushroom;
+ if(be32_to_cpu(magic) != SGI_LABEL_MAGIC) {
+ /*printk("Dev %s SGI disklabel: bad magic %08x\n",
+ bdevname(dev), magic);*/
+ put_dev_sector(sect);
+ return 0;
+ }
+ ui = ((unsigned int *) (label + 1)) - 1;
+ for(csum = 0; ui >= ((unsigned int *) label);) {
+ cs = *ui--;
+ csum += be32_to_cpu(cs);
+ }
+ if(csum) {
+ printk(KERN_WARNING "Dev %s SGI disklabel: csum bad, label corrupted\n",
+ bdevname(dev));
+ put_dev_sector(sect);
+ return 0;
+ }
+ /* All SGI disk labels have 16 partitions, disks under Linux only
+ * have 15 minor's. Luckily there are always a few zero length
+ * partitions which we don't care about so we never overflow the
+ * current_minor.
+ */
+ for(i = 0; i < 16; i++, p++) {
+ blocks = be32_to_cpu(p->num_blocks);
+ start = be32_to_cpu(p->first_block);
+ if(!blocks)
+ continue;
+ add_gd_partition(hd, current_minor, start, blocks);
+ current_minor++;
+ }
+ printk("\n");
+ put_dev_sector(sect);
+ return 1;
+}
diff --git a/fs/partitions/sgi.h b/fs/partitions/sgi.h
new file mode 100644
index 00000000000000..55840d9285e576
--- /dev/null
+++ b/fs/partitions/sgi.h
@@ -0,0 +1,9 @@
+/*
+ * fs/partitions/sgi.h
+ */
+
+extern int sgi_partition(struct gendisk *hd, struct block_device *bdev,
+ unsigned long first_sector, int first_part_minor);
+
+#define SGI_LABEL_MAGIC 0x0be5a941
+
diff --git a/fs/partitions/sun.c b/fs/partitions/sun.c
new file mode 100644
index 00000000000000..a0ca0b17260726
--- /dev/null
+++ b/fs/partitions/sun.c
@@ -0,0 +1,107 @@
+/*
+ * fs/partitions/sun.c
+ *
+ * Code extracted from drivers/block/genhd.c
+ *
+ * Copyright (C) 1991-1998 Linus Torvalds
+ * Re-organised Feb 1998 Russell King
+ */
+
+#include <linux/config.h>
+#include <linux/fs.h>
+#include <linux/genhd.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/blk.h>
+
+#include <asm/system.h>
+
+#include "check.h"
+#include "sun.h"
+
+#ifdef CONFIG_BLK_DEV_MD
+extern void md_autodetect_dev(kdev_t dev);
+#endif
+
+int sun_partition(struct gendisk *hd, struct block_device *bdev, unsigned long first_sector, int first_part_minor)
+{
+ int i, csum;
+ unsigned short *ush;
+ Sector sect;
+ kdev_t dev = to_kdev_t(bdev->bd_dev);
+ struct sun_disklabel {
+ unsigned char info[128]; /* Informative text string */
+ unsigned char spare0[14];
+ struct sun_info {
+ unsigned char spare1;
+ unsigned char id;
+ unsigned char spare2;
+ unsigned char flags;
+ } infos[8];
+ unsigned char spare[246]; /* Boot information etc. */
+ unsigned short rspeed; /* Disk rotational speed */
+ unsigned short pcylcount; /* Physical cylinder count */
+ unsigned short sparecyl; /* extra sects per cylinder */
+ unsigned char spare2[4]; /* More magic... */
+ unsigned short ilfact; /* Interleave factor */
+ unsigned short ncyl; /* Data cylinder count */
+ unsigned short nacyl; /* Alt. cylinder count */
+ unsigned short ntrks; /* Tracks per cylinder */
+ unsigned short nsect; /* Sectors per track */
+ unsigned char spare3[4]; /* Even more magic... */
+ struct sun_partition {
+ __u32 start_cylinder;
+ __u32 num_sectors;
+ } partitions[8];
+ unsigned short magic; /* Magic number */
+ unsigned short csum; /* Label xor'd checksum */
+ } * label;
+ struct sun_partition *p;
+ unsigned long spc;
+
+ label = (struct sun_disklabel *)read_dev_sector(bdev, 0, &sect);
+ if (!label)
+ return -1;
+
+ p = label->partitions;
+ if (be16_to_cpu(label->magic) != SUN_LABEL_MAGIC) {
+/* printk(KERN_INFO "Dev %s Sun disklabel: bad magic %04x\n",
+ bdevname(dev), be16_to_cpu(label->magic)); */
+ put_dev_sector(sect);
+ return 0;
+ }
+ /* Look at the checksum */
+ ush = ((unsigned short *) (label+1)) - 1;
+ for (csum = 0; ush >= ((unsigned short *) label);)
+ csum ^= *ush--;
+ if (csum) {
+ printk("Dev %s Sun disklabel: Csum bad, label corrupted\n",
+ bdevname(dev));
+ put_dev_sector(sect);
+ return 0;
+ }
+
+ /* All Sun disks have 8 partition entries */
+ spc = be16_to_cpu(label->ntrks) * be16_to_cpu(label->nsect);
+ for (i = 0; i < 8; i++, p++) {
+ unsigned long st_sector;
+ int num_sectors;
+
+ st_sector = first_sector + be32_to_cpu(p->start_cylinder) * spc;
+ num_sectors = be32_to_cpu(p->num_sectors);
+ if (num_sectors) {
+ add_gd_partition(hd, first_part_minor,
+ st_sector, num_sectors);
+#ifdef CONFIG_BLK_DEV_MD
+ if (label->infos[i].id == LINUX_RAID_PARTITION)
+ md_autodetect_dev(MKDEV(hd->major,
+ first_part_minor));
+#endif
+ }
+ first_part_minor++;
+ }
+ printk("\n");
+ put_dev_sector(sect);
+ return 1;
+}
diff --git a/fs/partitions/sun.h b/fs/partitions/sun.h
new file mode 100644
index 00000000000000..ae2f9579ef8755
--- /dev/null
+++ b/fs/partitions/sun.h
@@ -0,0 +1,9 @@
+/*
+ * fs/partitions/sun.h
+ */
+
+#define SUN_LABEL_MAGIC 0xDABE
+
+int sun_partition(struct gendisk *hd, struct block_device *bdev,
+ unsigned long first_sector, int first_part_minor);
+
diff --git a/fs/partitions/ultrix.c b/fs/partitions/ultrix.c
new file mode 100644
index 00000000000000..a7e0629dd2da58
--- /dev/null
+++ b/fs/partitions/ultrix.c
@@ -0,0 +1,54 @@
+/*
+ * fs/partitions/ultrix.c
+ *
+ * Code extracted from drivers/block/genhd.c
+ *
+ * Re-organised Jul 1999 Russell King
+ */
+
+#include <linux/fs.h>
+#include <linux/genhd.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/blk.h>
+
+#include "check.h"
+
+int ultrix_partition(struct gendisk *hd, struct block_device *bdev,
+ unsigned long first_sector, int first_part_minor)
+{
+ int i;
+ Sector sect;
+ unsigned char *data;
+ struct ultrix_disklabel {
+ s32 pt_magic; /* magic no. indicating part. info exits */
+ s32 pt_valid; /* set by driver if pt is current */
+ struct pt_info {
+ s32 pi_nblocks; /* no. of sectors */
+ u32 pi_blkoff; /* block offset for start */
+ } pt_part[8];
+ } *label;
+
+#define PT_MAGIC 0x032957 /* Partition magic number */
+#define PT_VALID 1 /* Indicates if struct is valid */
+
+ data = read_dev_sector(bdev, (16384 - sizeof(*label))/512, &sect);
+ if (!data)
+ return -1;
+
+ label = (struct ultrix_disklabel *)(data + 512 - sizeof(*label));
+
+ if (label->pt_magic == PT_MAGIC && label->pt_valid == PT_VALID) {
+ for (i=0; i<8; i++, first_part_minor++)
+ if (label->pt_part[i].pi_nblocks)
+ add_gd_partition(hd, first_part_minor,
+ label->pt_part[i].pi_blkoff,
+ label->pt_part[i].pi_nblocks);
+ put_dev_sector(sect);
+ printk ("\n");
+ return 1;
+ } else {
+ put_dev_sector(sect);
+ return 0;
+ }
+}
diff --git a/fs/partitions/ultrix.h b/fs/partitions/ultrix.h
new file mode 100644
index 00000000000000..1572c90d90bee7
--- /dev/null
+++ b/fs/partitions/ultrix.h
@@ -0,0 +1,7 @@
+/*
+ * fs/partitions/ultrix.h
+ */
+
+int ultrix_partition(struct gendisk *hd, struct block_device *bdev,
+ unsigned long first_sector, int first_part_minor);
+
diff --git a/fs/pipe.c b/fs/pipe.c
new file mode 100644
index 00000000000000..704a95840350e6
--- /dev/null
+++ b/fs/pipe.c
@@ -0,0 +1,650 @@
+/*
+ * linux/fs/pipe.c
+ *
+ * Copyright (C) 1991, 1992, 1999 Linus Torvalds
+ */
+
+#include <linux/mm.h>
+#include <linux/file.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <asm/uaccess.h>
+#include <asm/ioctls.h>
+
+/*
+ * We use a start+len construction, which provides full use of the
+ * allocated memory.
+ * -- Florian Coosmann (FGC)
+ *
+ * Reads with count = 0 should always return 0.
+ * -- Julian Bradfield 1999-06-07.
+ */
+
+/* Drop the inode semaphore and wait for a pipe event, atomically */
+void pipe_wait(struct inode * inode)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ current->state = TASK_INTERRUPTIBLE;
+ add_wait_queue(PIPE_WAIT(*inode), &wait);
+ up(PIPE_SEM(*inode));
+ schedule();
+ remove_wait_queue(PIPE_WAIT(*inode), &wait);
+ current->state = TASK_RUNNING;
+ down(PIPE_SEM(*inode));
+}
+
+static ssize_t
+pipe_read(struct file *filp, char *buf, size_t count, loff_t *ppos)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ ssize_t size, read, ret;
+
+ /* Seeks are not allowed on pipes. */
+ ret = -ESPIPE;
+ read = 0;
+ if (ppos != &filp->f_pos)
+ goto out_nolock;
+
+ /* Always return 0 on null read. */
+ ret = 0;
+ if (count == 0)
+ goto out_nolock;
+
+ /* Get the pipe semaphore */
+ ret = -ERESTARTSYS;
+ if (down_interruptible(PIPE_SEM(*inode)))
+ goto out_nolock;
+
+ if (PIPE_EMPTY(*inode)) {
+do_more_read:
+ ret = 0;
+ if (!PIPE_WRITERS(*inode))
+ goto out;
+
+ ret = -EAGAIN;
+ if (filp->f_flags & O_NONBLOCK)
+ goto out;
+
+ for (;;) {
+ PIPE_WAITING_READERS(*inode)++;
+ pipe_wait(inode);
+ PIPE_WAITING_READERS(*inode)--;
+ ret = -ERESTARTSYS;
+ if (signal_pending(current))
+ goto out;
+ ret = 0;
+ if (!PIPE_EMPTY(*inode))
+ break;
+ if (!PIPE_WRITERS(*inode))
+ goto out;
+ }
+ }
+
+ /* Read what data is available. */
+ ret = -EFAULT;
+ while (count > 0 && (size = PIPE_LEN(*inode))) {
+ char *pipebuf = PIPE_BASE(*inode) + PIPE_START(*inode);
+ ssize_t chars = PIPE_MAX_RCHUNK(*inode);
+
+ if (chars > count)
+ chars = count;
+ if (chars > size)
+ chars = size;
+
+ if (copy_to_user(buf, pipebuf, chars))
+ goto out;
+
+ read += chars;
+ PIPE_START(*inode) += chars;
+ PIPE_START(*inode) &= (PIPE_SIZE - 1);
+ PIPE_LEN(*inode) -= chars;
+ count -= chars;
+ buf += chars;
+ }
+
+ /* Cache behaviour optimization */
+ if (!PIPE_LEN(*inode))
+ PIPE_START(*inode) = 0;
+
+ if (count && PIPE_WAITING_WRITERS(*inode) && !(filp->f_flags & O_NONBLOCK)) {
+ /*
+ * We know that we are going to sleep: signal
+ * writers synchronously that there is more
+ * room.
+ */
+ wake_up_interruptible_sync(PIPE_WAIT(*inode));
+ if (!PIPE_EMPTY(*inode))
+ BUG();
+ goto do_more_read;
+ }
+ /* Signal writers asynchronously that there is more room. */
+ wake_up_interruptible(PIPE_WAIT(*inode));
+
+ ret = read;
+out:
+ up(PIPE_SEM(*inode));
+out_nolock:
+ if (read)
+ ret = read;
+
+ UPDATE_ATIME(inode);
+ return ret;
+}
+
+static ssize_t
+pipe_write(struct file *filp, const char *buf, size_t count, loff_t *ppos)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ ssize_t free, written, ret;
+
+ /* Seeks are not allowed on pipes. */
+ ret = -ESPIPE;
+ written = 0;
+ if (ppos != &filp->f_pos)
+ goto out_nolock;
+
+ /* Null write succeeds. */
+ ret = 0;
+ if (count == 0)
+ goto out_nolock;
+
+ ret = -ERESTARTSYS;
+ if (down_interruptible(PIPE_SEM(*inode)))
+ goto out_nolock;
+
+ /* No readers yields SIGPIPE. */
+ if (!PIPE_READERS(*inode))
+ goto sigpipe;
+
+ /* If count <= PIPE_BUF, we have to make it atomic. */
+ free = (count <= PIPE_BUF ? count : 1);
+
+ /* Wait, or check for, available space. */
+ if (filp->f_flags & O_NONBLOCK) {
+ ret = -EAGAIN;
+ if (PIPE_FREE(*inode) < free)
+ goto out;
+ } else {
+ while (PIPE_FREE(*inode) < free) {
+ PIPE_WAITING_WRITERS(*inode)++;
+ pipe_wait(inode);
+ PIPE_WAITING_WRITERS(*inode)--;
+ ret = -ERESTARTSYS;
+ if (signal_pending(current))
+ goto out;
+
+ if (!PIPE_READERS(*inode))
+ goto sigpipe;
+ }
+ }
+
+ /* Copy into available space. */
+ ret = -EFAULT;
+ while (count > 0) {
+ int space;
+ char *pipebuf = PIPE_BASE(*inode) + PIPE_END(*inode);
+ ssize_t chars = PIPE_MAX_WCHUNK(*inode);
+
+ if ((space = PIPE_FREE(*inode)) != 0) {
+ if (chars > count)
+ chars = count;
+ if (chars > space)
+ chars = space;
+
+ if (copy_from_user(pipebuf, buf, chars))
+ goto out;
+
+ written += chars;
+ PIPE_LEN(*inode) += chars;
+ count -= chars;
+ buf += chars;
+ space = PIPE_FREE(*inode);
+ continue;
+ }
+
+ ret = written;
+ if (filp->f_flags & O_NONBLOCK)
+ break;
+
+ do {
+ /*
+ * Synchronous wake-up: it knows that this process
+ * is going to give up this CPU, so it doesn't have
+ * to do idle reschedules.
+ */
+ wake_up_interruptible_sync(PIPE_WAIT(*inode));
+ PIPE_WAITING_WRITERS(*inode)++;
+ pipe_wait(inode);
+ PIPE_WAITING_WRITERS(*inode)--;
+ if (signal_pending(current))
+ goto out;
+ if (!PIPE_READERS(*inode))
+ goto sigpipe;
+ } while (!PIPE_FREE(*inode));
+ ret = -EFAULT;
+ }
+
+ /* Signal readers asynchronously that there is more data. */
+ wake_up_interruptible(PIPE_WAIT(*inode));
+
+ update_mctime(inode);
+
+out:
+ up(PIPE_SEM(*inode));
+out_nolock:
+ if (written)
+ ret = written;
+ return ret;
+
+sigpipe:
+ if (written)
+ goto out;
+ up(PIPE_SEM(*inode));
+ send_sig(SIGPIPE, current, 0);
+ return -EPIPE;
+}
+
+static ssize_t
+bad_pipe_r(struct file *filp, char *buf, size_t count, loff_t *ppos)
+{
+ return -EBADF;
+}
+
+static ssize_t
+bad_pipe_w(struct file *filp, const char *buf, size_t count, loff_t *ppos)
+{
+ return -EBADF;
+}
+
+static int
+pipe_ioctl(struct inode *pino, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ switch (cmd) {
+ case FIONREAD:
+ return put_user(PIPE_LEN(*pino), (int *)arg);
+ default:
+ return -EINVAL;
+ }
+}
+
+/* No kernel lock held - fine */
+static unsigned int
+pipe_poll(struct file *filp, poll_table *wait)
+{
+ unsigned int mask;
+ struct inode *inode = filp->f_dentry->d_inode;
+
+ poll_wait(filp, PIPE_WAIT(*inode), wait);
+
+ /* Reading only -- no need for acquiring the semaphore. */
+ mask = POLLIN | POLLRDNORM;
+ if (PIPE_EMPTY(*inode))
+ mask = POLLOUT | POLLWRNORM;
+ if (!PIPE_WRITERS(*inode) && filp->f_version != PIPE_WCOUNTER(*inode))
+ mask |= POLLHUP;
+ if (!PIPE_READERS(*inode))
+ mask |= POLLERR;
+
+ return mask;
+}
+
+/* FIXME: most Unices do not set POLLERR for fifos */
+#define fifo_poll pipe_poll
+
+static int
+pipe_release(struct inode *inode, int decr, int decw)
+{
+ down(PIPE_SEM(*inode));
+ PIPE_READERS(*inode) -= decr;
+ PIPE_WRITERS(*inode) -= decw;
+ if (!PIPE_READERS(*inode) && !PIPE_WRITERS(*inode)) {
+ struct pipe_inode_info *info = inode->i_pipe;
+ inode->i_pipe = NULL;
+ free_page((unsigned long) info->base);
+ kfree(info);
+ } else {
+ wake_up_interruptible(PIPE_WAIT(*inode));
+ }
+ up(PIPE_SEM(*inode));
+
+ return 0;
+}
+
+static int
+pipe_read_release(struct inode *inode, struct file *filp)
+{
+ return pipe_release(inode, 1, 0);
+}
+
+static int
+pipe_write_release(struct inode *inode, struct file *filp)
+{
+ return pipe_release(inode, 0, 1);
+}
+
+static int
+pipe_rdwr_release(struct inode *inode, struct file *filp)
+{
+ int decr, decw;
+
+ decr = (filp->f_mode & FMODE_READ) != 0;
+ decw = (filp->f_mode & FMODE_WRITE) != 0;
+ return pipe_release(inode, decr, decw);
+}
+
+static int
+pipe_read_open(struct inode *inode, struct file *filp)
+{
+ /* We could have perhaps used atomic_t, but this and friends
+ below are the only places. So it doesn't seem worthwhile. */
+ down(PIPE_SEM(*inode));
+ PIPE_READERS(*inode)++;
+ up(PIPE_SEM(*inode));
+
+ return 0;
+}
+
+static int
+pipe_write_open(struct inode *inode, struct file *filp)
+{
+ down(PIPE_SEM(*inode));
+ PIPE_WRITERS(*inode)++;
+ up(PIPE_SEM(*inode));
+
+ return 0;
+}
+
+static int
+pipe_rdwr_open(struct inode *inode, struct file *filp)
+{
+ down(PIPE_SEM(*inode));
+ if (filp->f_mode & FMODE_READ)
+ PIPE_READERS(*inode)++;
+ if (filp->f_mode & FMODE_WRITE)
+ PIPE_WRITERS(*inode)++;
+ up(PIPE_SEM(*inode));
+
+ return 0;
+}
+
+/*
+ * The file_operations structs are not static because they
+ * are also used in linux/fs/fifo.c to do operations on FIFOs.
+ */
+struct file_operations read_fifo_fops = {
+ llseek: no_llseek,
+ read: pipe_read,
+ write: bad_pipe_w,
+ poll: fifo_poll,
+ ioctl: pipe_ioctl,
+ open: pipe_read_open,
+ release: pipe_read_release,
+};
+
+struct file_operations write_fifo_fops = {
+ llseek: no_llseek,
+ read: bad_pipe_r,
+ write: pipe_write,
+ poll: fifo_poll,
+ ioctl: pipe_ioctl,
+ open: pipe_write_open,
+ release: pipe_write_release,
+};
+
+struct file_operations rdwr_fifo_fops = {
+ llseek: no_llseek,
+ read: pipe_read,
+ write: pipe_write,
+ poll: fifo_poll,
+ ioctl: pipe_ioctl,
+ open: pipe_rdwr_open,
+ release: pipe_rdwr_release,
+};
+
+struct file_operations read_pipe_fops = {
+ llseek: no_llseek,
+ read: pipe_read,
+ write: bad_pipe_w,
+ poll: pipe_poll,
+ ioctl: pipe_ioctl,
+ open: pipe_read_open,
+ release: pipe_read_release,
+};
+
+struct file_operations write_pipe_fops = {
+ llseek: no_llseek,
+ read: bad_pipe_r,
+ write: pipe_write,
+ poll: pipe_poll,
+ ioctl: pipe_ioctl,
+ open: pipe_write_open,
+ release: pipe_write_release,
+};
+
+struct file_operations rdwr_pipe_fops = {
+ llseek: no_llseek,
+ read: pipe_read,
+ write: pipe_write,
+ poll: pipe_poll,
+ ioctl: pipe_ioctl,
+ open: pipe_rdwr_open,
+ release: pipe_rdwr_release,
+};
+
+struct inode* pipe_new(struct inode* inode)
+{
+ unsigned long page;
+
+ page = __get_free_page(GFP_USER);
+ if (!page)
+ return NULL;
+
+ inode->i_pipe = kmalloc(sizeof(struct pipe_inode_info), GFP_KERNEL);
+ if (!inode->i_pipe)
+ goto fail_page;
+
+ init_waitqueue_head(PIPE_WAIT(*inode));
+ PIPE_BASE(*inode) = (char*) page;
+ PIPE_START(*inode) = PIPE_LEN(*inode) = 0;
+ PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 0;
+ PIPE_WAITING_READERS(*inode) = PIPE_WAITING_WRITERS(*inode) = 0;
+ PIPE_RCOUNTER(*inode) = PIPE_WCOUNTER(*inode) = 1;
+
+ return inode;
+fail_page:
+ free_page(page);
+ return NULL;
+}
+
+static struct vfsmount *pipe_mnt;
+static int pipefs_delete_dentry(struct dentry *dentry)
+{
+ return 1;
+}
+static struct dentry_operations pipefs_dentry_operations = {
+ d_delete: pipefs_delete_dentry,
+};
+
+static struct inode * get_pipe_inode(void)
+{
+ struct inode *inode = new_inode(pipe_mnt->mnt_sb);
+
+ if (!inode)
+ goto fail_inode;
+
+ if(!pipe_new(inode))
+ goto fail_iput;
+ PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 1;
+ inode->i_fop = &rdwr_pipe_fops;
+
+ /*
+ * Mark the inode dirty from the very beginning,
+ * that way it will never be moved to the dirty
+ * list because "mark_inode_dirty()" will think
+ * that it already _is_ on the dirty list.
+ */
+ inode->i_state = I_DIRTY;
+ inode->i_mode = S_IFIFO | S_IRUSR | S_IWUSR;
+ inode->i_uid = current->fsuid;
+ inode->i_gid = current->fsgid;
+ inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ inode->i_blksize = PAGE_SIZE;
+ return inode;
+
+fail_iput:
+ iput(inode);
+fail_inode:
+ return NULL;
+}
+
+int do_pipe(int *fd)
+{
+ struct qstr this;
+ char name[32];
+ struct dentry *dentry;
+ struct inode * inode;
+ struct file *f1, *f2;
+ int error;
+ int i,j;
+
+ error = -ENFILE;
+ f1 = get_empty_filp();
+ if (!f1)
+ goto no_files;
+
+ f2 = get_empty_filp();
+ if (!f2)
+ goto close_f1;
+
+ inode = get_pipe_inode();
+ if (!inode)
+ goto close_f12;
+
+ error = get_unused_fd();
+ if (error < 0)
+ goto close_f12_inode;
+ i = error;
+
+ error = get_unused_fd();
+ if (error < 0)
+ goto close_f12_inode_i;
+ j = error;
+
+ error = -ENOMEM;
+ sprintf(name, "[%lu]", inode->i_ino);
+ this.name = name;
+ this.len = strlen(name);
+ this.hash = inode->i_ino; /* will go */
+ dentry = d_alloc(pipe_mnt->mnt_sb->s_root, &this);
+ if (!dentry)
+ goto close_f12_inode_i_j;
+ dentry->d_op = &pipefs_dentry_operations;
+ d_add(dentry, inode);
+ f1->f_vfsmnt = f2->f_vfsmnt = mntget(mntget(pipe_mnt));
+ f1->f_dentry = f2->f_dentry = dget(dentry);
+
+ /* read file */
+ f1->f_pos = f2->f_pos = 0;
+ f1->f_flags = O_RDONLY;
+ f1->f_op = &read_pipe_fops;
+ f1->f_mode = 1;
+ f1->f_version = 0;
+
+ /* write file */
+ f2->f_flags = O_WRONLY;
+ f2->f_op = &write_pipe_fops;
+ f2->f_mode = 2;
+ f2->f_version = 0;
+
+ fd_install(i, f1);
+ fd_install(j, f2);
+ fd[0] = i;
+ fd[1] = j;
+ return 0;
+
+close_f12_inode_i_j:
+ put_unused_fd(j);
+close_f12_inode_i:
+ put_unused_fd(i);
+close_f12_inode:
+ free_page((unsigned long) PIPE_BASE(*inode));
+ kfree(inode->i_pipe);
+ inode->i_pipe = NULL;
+ iput(inode);
+close_f12:
+ put_filp(f2);
+close_f1:
+ put_filp(f1);
+no_files:
+ return error;
+}
+
+/*
+ * pipefs should _never_ be mounted by userland - too much of security hassle,
+ * no real gain from having the whole whorehouse mounted. So we don't need
+ * any operations on the root directory. However, we need a non-trivial
+ * d_name - pipe: will go nicely and kill the special-casing in procfs.
+ */
+static int pipefs_statfs(struct super_block *sb, struct statfs *buf)
+{
+ buf->f_type = PIPEFS_MAGIC;
+ buf->f_bsize = 1024;
+ buf->f_namelen = 255;
+ return 0;
+}
+
+static struct super_operations pipefs_ops = {
+ statfs: pipefs_statfs,
+};
+
+static struct super_block * pipefs_read_super(struct super_block *sb, void *data, int silent)
+{
+ struct inode *root = new_inode(sb);
+ if (!root)
+ return NULL;
+ root->i_mode = S_IFDIR | S_IRUSR | S_IWUSR;
+ root->i_uid = root->i_gid = 0;
+ root->i_atime = root->i_mtime = root->i_ctime = CURRENT_TIME;
+ sb->s_blocksize = 1024;
+ sb->s_blocksize_bits = 10;
+ sb->s_magic = PIPEFS_MAGIC;
+ sb->s_op = &pipefs_ops;
+ sb->s_root = d_alloc(NULL, &(const struct qstr) { "pipe:", 5, 0 });
+ if (!sb->s_root) {
+ iput(root);
+ return NULL;
+ }
+ sb->s_root->d_sb = sb;
+ sb->s_root->d_parent = sb->s_root;
+ d_instantiate(sb->s_root, root);
+ return sb;
+}
+
+static DECLARE_FSTYPE(pipe_fs_type, "pipefs", pipefs_read_super, FS_NOMOUNT);
+
+static int __init init_pipe_fs(void)
+{
+ int err = register_filesystem(&pipe_fs_type);
+ if (!err) {
+ pipe_mnt = kern_mount(&pipe_fs_type);
+ err = PTR_ERR(pipe_mnt);
+ if (IS_ERR(pipe_mnt))
+ unregister_filesystem(&pipe_fs_type);
+ else
+ err = 0;
+ }
+ return err;
+}
+
+static void __exit exit_pipe_fs(void)
+{
+ unregister_filesystem(&pipe_fs_type);
+ mntput(pipe_mnt);
+}
+
+module_init(init_pipe_fs)
+module_exit(exit_pipe_fs)
diff --git a/fs/proc/Makefile b/fs/proc/Makefile
new file mode 100644
index 00000000000000..95160610eba0fd
--- /dev/null
+++ b/fs/proc/Makefile
@@ -0,0 +1,21 @@
+#
+# Makefile for the Linux proc filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile.
+
+O_TARGET := proc.o
+
+export-objs := root.o
+
+obj-y := inode.o root.o base.o generic.o array.o \
+ kmsg.o proc_tty.o proc_misc.o kcore.o
+
+ifeq ($(CONFIG_PROC_DEVICETREE),y)
+obj-y += proc_devtree.o
+endif
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/proc/array.c b/fs/proc/array.c
new file mode 100644
index 00000000000000..324ef38fe8a3c0
--- /dev/null
+++ b/fs/proc/array.c
@@ -0,0 +1,629 @@
+/*
+ * linux/fs/proc/array.c
+ *
+ * Copyright (C) 1992 by Linus Torvalds
+ * based on ideas by Darren Senn
+ *
+ * Fixes:
+ * Michael. K. Johnson: stat,statm extensions.
+ * <johnsonm@stolaf.edu>
+ *
+ * Pauline Middelink : Made cmdline,envline only break at '\0's, to
+ * make sure SET_PROCTITLE works. Also removed
+ * bad '!' which forced address recalculation for
+ * EVERY character on the current page.
+ * <middelin@polyware.iaf.nl>
+ *
+ * Danny ter Haar : added cpuinfo
+ * <dth@cistron.nl>
+ *
+ * Alessandro Rubini : profile extension.
+ * <rubini@ipvvis.unipv.it>
+ *
+ * Jeff Tranter : added BogoMips field to cpuinfo
+ * <Jeff_Tranter@Mitel.COM>
+ *
+ * Bruno Haible : remove 4K limit for the maps file
+ * <haible@ma2s2.mathematik.uni-karlsruhe.de>
+ *
+ * Yves Arrouye : remove removal of trailing spaces in get_array.
+ * <Yves.Arrouye@marin.fdn.fr>
+ *
+ * Jerome Forissier : added per-CPU time information to /proc/stat
+ * and /proc/<pid>/cpu extension
+ * <forissier@isia.cma.fr>
+ * - Incorporation and non-SMP safe operation
+ * of forissier patch in 2.1.78 by
+ * Hans Marcus <crowbar@concepts.nl>
+ *
+ * aeb@cwi.nl : /proc/partitions
+ *
+ *
+ * Alan Cox : security fixes.
+ * <Alan.Cox@linux.org>
+ *
+ * Al Viro : safe handling of mm_struct
+ *
+ * Gerhard Wichert : added BIGMEM support
+ * Siemens AG <Gerhard.Wichert@pdb.siemens.de>
+ *
+ * Al Viro & Jeff Garzik : moved most of the thing into base.c and
+ * : proc_misc.c. The rest may eventually go into
+ * : base.c too.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/kernel_stat.h>
+#include <linux/tty.h>
+#include <linux/string.h>
+#include <linux/mman.h>
+#include <linux/proc_fs.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/swap.h>
+#include <linux/slab.h>
+#include <linux/smp.h>
+#include <linux/signal.h>
+#include <linux/highmem.h>
+#include <linux/seq_file.h>
+
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+#include <asm/io.h>
+#include <asm/processor.h>
+
+/* Gcc optimizes away "strlen(x)" for constant x */
+#define ADDBUF(buffer, string) \
+do { memcpy(buffer, string, strlen(string)); \
+ buffer += strlen(string); } while (0)
+
+static inline char * task_name(struct task_struct *p, char * buf)
+{
+ int i;
+ char * name;
+ char tcomm[sizeof(p->comm)];
+
+ get_task_comm(tcomm, p);
+
+ ADDBUF(buf, "Name:\t");
+ name = tcomm;
+ i = sizeof(tcomm);
+ do {
+ unsigned char c = *name;
+ name++;
+ i--;
+ *buf = c;
+ if (!c)
+ break;
+ if (c == '\\') {
+ buf[1] = c;
+ buf += 2;
+ continue;
+ }
+ if (c == '\n') {
+ buf[0] = '\\';
+ buf[1] = 'n';
+ buf += 2;
+ continue;
+ }
+ buf++;
+ } while (i);
+ *buf = '\n';
+ return buf+1;
+}
+
+/*
+ * The task state array is a strange "bitmap" of
+ * reasons to sleep. Thus "running" is zero, and
+ * you can test for combinations of others with
+ * simple bit tests.
+ */
+static const char *task_state_array[] = {
+ "R (running)", /* 0 */
+ "S (sleeping)", /* 1 */
+ "D (disk sleep)", /* 2 */
+ "Z (zombie)", /* 4 */
+ "T (stopped)", /* 8 */
+ "W (paging)" /* 16 */
+};
+
+static inline const char * get_task_state(struct task_struct *tsk)
+{
+ unsigned int state = tsk->state & (TASK_RUNNING |
+ TASK_INTERRUPTIBLE |
+ TASK_UNINTERRUPTIBLE |
+ TASK_ZOMBIE |
+ TASK_STOPPED);
+ const char **p = &task_state_array[0];
+
+ while (state) {
+ p++;
+ state >>= 1;
+ }
+ return *p;
+}
+
+static inline char * task_state(struct task_struct *p, char *buffer)
+{
+ int g;
+
+ read_lock(&tasklist_lock);
+ buffer += sprintf(buffer,
+ "State:\t%s\n"
+ "Tgid:\t%d\n"
+ "Pid:\t%d\n"
+ "PPid:\t%d\n"
+ "TracerPid:\t%d\n"
+ "Uid:\t%d\t%d\t%d\t%d\n"
+ "Gid:\t%d\t%d\t%d\t%d\n",
+ get_task_state(p), p->tgid,
+ p->pid, p->pid ? p->p_opptr->pid : 0, 0,
+ p->uid, p->euid, p->suid, p->fsuid,
+ p->gid, p->egid, p->sgid, p->fsgid);
+ read_unlock(&tasklist_lock);
+ task_lock(p);
+ buffer += sprintf(buffer,
+ "FDSize:\t%d\n"
+ "Groups:\t",
+ p->files ? p->files->max_fds : 0);
+ task_unlock(p);
+
+ for (g = 0; g < p->ngroups; g++)
+ buffer += sprintf(buffer, "%d ", p->groups[g]);
+
+ buffer += sprintf(buffer, "\n");
+ return buffer;
+}
+
+static inline char * task_mem(struct mm_struct *mm, char *buffer)
+{
+ struct vm_area_struct * vma;
+ unsigned long data = 0, stack = 0;
+ unsigned long exec = 0, lib = 0;
+
+ down_read(&mm->mmap_sem);
+ for (vma = mm->mmap; vma; vma = vma->vm_next) {
+ unsigned long len = (vma->vm_end - vma->vm_start) >> 10;
+ if (!vma->vm_file) {
+ data += len;
+ if (vma->vm_flags & VM_GROWSDOWN)
+ stack += len;
+ continue;
+ }
+ if (vma->vm_flags & VM_WRITE)
+ continue;
+ if (vma->vm_flags & VM_EXEC) {
+ exec += len;
+ if (vma->vm_flags & VM_EXECUTABLE)
+ continue;
+ lib += len;
+ }
+ }
+ buffer += sprintf(buffer,
+ "VmSize:\t%8lu kB\n"
+ "VmLck:\t%8lu kB\n"
+ "VmRSS:\t%8lu kB\n"
+ "VmData:\t%8lu kB\n"
+ "VmStk:\t%8lu kB\n"
+ "VmExe:\t%8lu kB\n"
+ "VmLib:\t%8lu kB\n",
+ mm->total_vm << (PAGE_SHIFT-10),
+ mm->locked_vm << (PAGE_SHIFT-10),
+ mm->rss << (PAGE_SHIFT-10),
+ data - stack, stack,
+ exec - lib, lib);
+ up_read(&mm->mmap_sem);
+ return buffer;
+}
+
+static void collect_sigign_sigcatch(struct task_struct *p, sigset_t *ign,
+ sigset_t *catch)
+{
+ struct k_sigaction *k;
+ int i;
+
+ sigemptyset(ign);
+ sigemptyset(catch);
+
+ spin_lock_irq(&p->sigmask_lock);
+
+ if (p->sig) {
+ k = p->sig->action;
+ for (i = 1; i <= _NSIG; ++i, ++k) {
+ if (k->sa.sa_handler == SIG_IGN)
+ sigaddset(ign, i);
+ else if (k->sa.sa_handler != SIG_DFL)
+ sigaddset(catch, i);
+ }
+ }
+ spin_unlock_irq(&p->sigmask_lock);
+}
+
+static inline char * task_sig(struct task_struct *p, char *buffer)
+{
+ sigset_t ign, catch;
+
+ buffer += sprintf(buffer, "SigPnd:\t");
+ buffer = render_sigset_t(&p->pending.signal, buffer);
+ *buffer++ = '\n';
+ buffer += sprintf(buffer, "SigBlk:\t");
+ buffer = render_sigset_t(&p->blocked, buffer);
+ *buffer++ = '\n';
+
+ collect_sigign_sigcatch(p, &ign, &catch);
+ buffer += sprintf(buffer, "SigIgn:\t");
+ buffer = render_sigset_t(&ign, buffer);
+ *buffer++ = '\n';
+ buffer += sprintf(buffer, "SigCgt:\t"); /* Linux 2.0 uses "SigCgt" */
+ buffer = render_sigset_t(&catch, buffer);
+ *buffer++ = '\n';
+
+ return buffer;
+}
+
+static inline char *task_cap(struct task_struct *p, char *buffer)
+{
+ return buffer + sprintf(buffer, "CapInh:\t%016x\n"
+ "CapPrm:\t%016x\n"
+ "CapEff:\t%016x\n",
+ cap_t(p->cap_inheritable),
+ cap_t(p->cap_permitted),
+ cap_t(p->cap_effective));
+}
+
+
+int proc_pid_status(struct task_struct *task, char * buffer)
+{
+ char * orig = buffer;
+ struct mm_struct *mm;
+
+ buffer = task_name(task, buffer);
+ buffer = task_state(task, buffer);
+ task_lock(task);
+ mm = task->mm;
+ if(mm)
+ atomic_inc(&mm->mm_users);
+ task_unlock(task);
+ if (mm) {
+ buffer = task_mem(mm, buffer);
+ mmput(mm);
+ }
+ buffer = task_sig(task, buffer);
+ buffer = task_cap(task, buffer);
+#if defined(CONFIG_ARCH_S390)
+ buffer = task_show_regs(task, buffer);
+#endif
+ return buffer - orig;
+}
+
+int proc_pid_stat(struct task_struct *task, char * buffer)
+{
+ unsigned long vsize, eip, esp, wchan;
+ long priority, nice;
+ int tty_pgrp = -1, tty_nr = 0;
+ sigset_t sigign, sigcatch;
+ char state;
+ int res;
+ pid_t ppid;
+ struct mm_struct *mm;
+ char tcomm[sizeof(task->comm)];
+
+ state = *get_task_state(task);
+ vsize = eip = esp = 0;
+ task_lock(task);
+ mm = task->mm;
+ if(mm)
+ atomic_inc(&mm->mm_users);
+ if (task->tty) {
+ tty_pgrp = task->tty->pgrp;
+ tty_nr = kdev_t_to_nr(task->tty->device);
+ }
+ task_unlock(task);
+ if (mm) {
+ struct vm_area_struct *vma;
+ down_read(&mm->mmap_sem);
+ vma = mm->mmap;
+ while (vma) {
+ vsize += vma->vm_end - vma->vm_start;
+ vma = vma->vm_next;
+ }
+ eip = KSTK_EIP(task);
+ esp = KSTK_ESP(task);
+ up_read(&mm->mmap_sem);
+ }
+
+ get_task_comm(tcomm, task);
+
+ wchan = get_wchan(task);
+
+ collect_sigign_sigcatch(task, &sigign, &sigcatch);
+
+ /* scale priority and nice values from timeslices to -20..20 */
+ /* to make it look like a "normal" Unix priority/nice value */
+ priority = task->counter;
+ priority = 20 - (priority * 10 + DEF_COUNTER / 2) / DEF_COUNTER;
+ nice = task->nice;
+
+ read_lock(&tasklist_lock);
+ ppid = task->pid ? task->p_opptr->pid : 0;
+ read_unlock(&tasklist_lock);
+ res = sprintf(buffer,"%d (%s) %c %d %d %d %d %d %lu %lu \
+%lu %lu %lu %lu %lu %ld %ld %ld %ld %ld %ld %lu %lu %ld %lu %lu %lu %lu %lu \
+%lu %lu %lu %lu %lu %lu %lu %lu %d %d\n",
+ task->pid,
+ tcomm,
+ state,
+ ppid,
+ task->pgrp,
+ task->session,
+ tty_nr,
+ tty_pgrp,
+ task->flags,
+ task->min_flt,
+ task->cmin_flt,
+ task->maj_flt,
+ task->cmaj_flt,
+ task->times.tms_utime,
+ task->times.tms_stime,
+ task->times.tms_cutime,
+ task->times.tms_cstime,
+ priority,
+ nice,
+ 0UL /* removed */,
+ task->it_real_value,
+ task->start_time,
+ vsize,
+ mm ? mm->rss : 0, /* you might want to shift this left 3 */
+ task->rlim[RLIMIT_RSS].rlim_cur,
+ mm ? mm->start_code : 0,
+ mm ? mm->end_code : 0,
+ mm ? mm->start_stack : 0,
+ esp,
+ eip,
+ /* The signal information here is obsolete.
+ * It must be decimal for Linux 2.0 compatibility.
+ * Use /proc/#/status for real-time signals.
+ */
+ task->pending.signal.sig[0] & 0x7fffffffUL,
+ task->blocked.sig[0] & 0x7fffffffUL,
+ sigign .sig[0] & 0x7fffffffUL,
+ sigcatch .sig[0] & 0x7fffffffUL,
+ wchan,
+ task->nswap,
+ task->cnswap,
+ task->exit_signal,
+ task->processor);
+ if(mm)
+ mmput(mm);
+ return res;
+}
+
+static inline void statm_pte_range(pmd_t * pmd, unsigned long address, unsigned long size,
+ int * pages, int * shared, int * dirty, int * total)
+{
+ pte_t * pte;
+ unsigned long end;
+
+ if (pmd_none(*pmd))
+ return;
+ if (pmd_bad(*pmd)) {
+ pmd_ERROR(*pmd);
+ pmd_clear(pmd);
+ return;
+ }
+ pte = pte_offset(pmd, address);
+ address &= ~PMD_MASK;
+ end = address + size;
+ if (end > PMD_SIZE)
+ end = PMD_SIZE;
+ do {
+ pte_t page = *pte;
+ struct page *ptpage;
+
+ address += PAGE_SIZE;
+ pte++;
+ if (pte_none(page))
+ continue;
+ ++*total;
+ if (!pte_present(page))
+ continue;
+ ptpage = pte_page(page);
+ if ((!VALID_PAGE(ptpage)) || PageReserved(ptpage))
+ continue;
+ ++*pages;
+ if (pte_dirty(page))
+ ++*dirty;
+ if (page_count(pte_page(page)) > 1)
+ ++*shared;
+ } while (address < end);
+}
+
+static inline void statm_pmd_range(pgd_t * pgd, unsigned long address, unsigned long size,
+ int * pages, int * shared, int * dirty, int * total)
+{
+ pmd_t * pmd;
+ unsigned long end;
+
+ if (pgd_none(*pgd))
+ return;
+ if (pgd_bad(*pgd)) {
+ pgd_ERROR(*pgd);
+ pgd_clear(pgd);
+ return;
+ }
+ pmd = pmd_offset(pgd, address);
+ address &= ~PGDIR_MASK;
+ end = address + size;
+ if (end > PGDIR_SIZE)
+ end = PGDIR_SIZE;
+ do {
+ statm_pte_range(pmd, address, end - address, pages, shared, dirty, total);
+ address = (address + PMD_SIZE) & PMD_MASK;
+ pmd++;
+ } while (address < end);
+}
+
+static void statm_pgd_range(pgd_t * pgd, unsigned long address, unsigned long end,
+ int * pages, int * shared, int * dirty, int * total)
+{
+ while (address < end) {
+ statm_pmd_range(pgd, address, end - address, pages, shared, dirty, total);
+ address = (address + PGDIR_SIZE) & PGDIR_MASK;
+ pgd++;
+ }
+}
+
+int proc_pid_statm(struct task_struct *task, char * buffer)
+{
+ struct mm_struct *mm;
+ int size=0, resident=0, share=0, trs=0, lrs=0, drs=0, dt=0;
+
+ task_lock(task);
+ mm = task->mm;
+ if(mm)
+ atomic_inc(&mm->mm_users);
+ task_unlock(task);
+ if (mm) {
+ struct vm_area_struct * vma;
+ down_read(&mm->mmap_sem);
+ vma = mm->mmap;
+ while (vma) {
+ pgd_t *pgd = pgd_offset(mm, vma->vm_start);
+ int pages = 0, shared = 0, dirty = 0, total = 0;
+
+ statm_pgd_range(pgd, vma->vm_start, vma->vm_end, &pages, &shared, &dirty, &total);
+ resident += pages;
+ share += shared;
+ dt += dirty;
+ size += total;
+ if (vma->vm_flags & VM_EXECUTABLE)
+ trs += pages; /* text */
+ else if (vma->vm_flags & VM_GROWSDOWN)
+ drs += pages; /* stack */
+ else if (vma->vm_end > 0x60000000)
+ lrs += pages; /* library */
+ else
+ drs += pages;
+ vma = vma->vm_next;
+ }
+ up_read(&mm->mmap_sem);
+ mmput(mm);
+ }
+ return sprintf(buffer,"%d %d %d %d %d %d %d\n",
+ size, resident, share, trs, lrs, drs, dt);
+}
+
+static int show_map(struct seq_file *m, void *v)
+{
+ struct vm_area_struct *map = v;
+ struct file *file = map->vm_file;
+ int flags = map->vm_flags;
+ unsigned long ino = 0;
+ dev_t dev = 0;
+ int len;
+
+ if (file) {
+ struct inode *inode = map->vm_file->f_dentry->d_inode;
+ dev = kdev_t_to_nr(inode->i_sb->s_dev);
+ ino = inode->i_ino;
+ }
+
+ seq_printf(m, "%08lx-%08lx %c%c%c%c %08lx %02x:%02x %lu %n",
+ map->vm_start,
+ map->vm_end,
+ flags & VM_READ ? 'r' : '-',
+ flags & VM_WRITE ? 'w' : '-',
+ flags & VM_EXEC ? 'x' : '-',
+ flags & VM_MAYSHARE ? 's' : 'p',
+ map->vm_pgoff << PAGE_SHIFT,
+ MAJOR(dev), MINOR(dev), ino, &len);
+
+ if (map->vm_file) {
+ len = 25 + sizeof(void*) * 6 - len;
+ if (len < 1)
+ len = 1;
+ seq_printf(m, "%*c", len, ' ');
+ seq_path(m, file->f_vfsmnt, file->f_dentry, "");
+ }
+ seq_putc(m, '\n');
+ return 0;
+}
+
+static void *m_start(struct seq_file *m, loff_t *pos)
+{
+ struct task_struct *task = m->private;
+ struct mm_struct *mm;
+ struct vm_area_struct * map;
+ loff_t l = *pos;
+
+ task_lock(task);
+ mm = task->mm;
+ if (mm)
+ atomic_inc(&mm->mm_users);
+ task_unlock(task);
+
+ if (!mm)
+ return NULL;
+
+ down_read(&mm->mmap_sem);
+ map = mm->mmap;
+ while (l-- && map)
+ map = map->vm_next;
+ if (!map) {
+ up_read(&mm->mmap_sem);
+ mmput(mm);
+ }
+ return map;
+}
+
+static void m_stop(struct seq_file *m, void *v)
+{
+ struct vm_area_struct *map = v;
+ if (map) {
+ struct mm_struct *mm = map->vm_mm;
+ up_read(&mm->mmap_sem);
+ mmput(mm);
+ }
+}
+
+static void *m_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ struct vm_area_struct *map = v;
+ (*pos)++;
+ if (map->vm_next)
+ return map->vm_next;
+ m_stop(m, v);
+ return NULL;
+}
+
+struct seq_operations proc_pid_maps_op = {
+ .start = m_start,
+ .next = m_next,
+ .stop = m_stop,
+ .show = show_map
+};
+
+#ifdef CONFIG_SMP
+int proc_pid_cpu(struct task_struct *task, char * buffer)
+{
+ int i, len;
+
+ len = sprintf(buffer,
+ "cpu %lu %lu\n",
+ task->times.tms_utime,
+ task->times.tms_stime);
+
+ for (i = 0 ; i < smp_num_cpus; i++)
+ len += sprintf(buffer + len, "cpu%d %lu %lu\n",
+ i,
+ task->per_cpu_utime[cpu_logical_map(i)],
+ task->per_cpu_stime[cpu_logical_map(i)]);
+
+ return len;
+}
+#endif
diff --git a/fs/proc/base.c b/fs/proc/base.c
new file mode 100644
index 00000000000000..95fba3f8584bd8
--- /dev/null
+++ b/fs/proc/base.c
@@ -0,0 +1,1169 @@
+/*
+ * linux/fs/proc/base.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * proc base directory handling functions
+ *
+ * 1999, Al Viro. Rewritten. Now it covers the whole per-process part.
+ * Instead of using magical inumbers to determine the kind of object
+ * we allocate and fill in-core inodes upon lookup. They don't even
+ * go into icache. We cache the reference to task_struct upon lookup too.
+ * Eventually it should become a filesystem in its own. We don't use the
+ * rest of procfs anymore.
+ */
+
+#include <asm/uaccess.h>
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/init.h>
+#include <linux/file.h>
+#include <linux/string.h>
+#include <linux/seq_file.h>
+#include <linux/namespace.h>
+
+/*
+ * For hysterical raisins we keep the same inumbers as in the old procfs.
+ * Feel free to change the macro below - just keep the range distinct from
+ * inumbers of the rest of procfs (currently those are in 0x0000--0xffff).
+ * As soon as we'll get a separate superblock we will be able to forget
+ * about magical ranges too.
+ */
+
+#define fake_ino(pid,ino) (((pid)<<16)|(ino))
+
+int proc_pid_stat(struct task_struct*,char*);
+int proc_pid_status(struct task_struct*,char*);
+int proc_pid_statm(struct task_struct*,char*);
+int proc_pid_cpu(struct task_struct*,char*);
+
+static int proc_fd_link(struct inode *inode, struct dentry **dentry, struct vfsmount **mnt)
+{
+ if (inode->u.proc_i.file) {
+ *mnt = mntget(inode->u.proc_i.file->f_vfsmnt);
+ *dentry = dget(inode->u.proc_i.file->f_dentry);
+ return 0;
+ }
+ return -ENOENT;
+}
+
+static int proc_exe_link(struct inode *inode, struct dentry **dentry, struct vfsmount **mnt)
+{
+ struct mm_struct * mm;
+ struct vm_area_struct * vma;
+ int result = -ENOENT;
+ struct task_struct *task = inode->u.proc_i.task;
+
+ task_lock(task);
+ mm = task->mm;
+ if (mm)
+ atomic_inc(&mm->mm_users);
+ task_unlock(task);
+ if (!mm)
+ goto out;
+ down_read(&mm->mmap_sem);
+ vma = mm->mmap;
+ while (vma) {
+ if ((vma->vm_flags & VM_EXECUTABLE) &&
+ vma->vm_file) {
+ *mnt = mntget(vma->vm_file->f_vfsmnt);
+ *dentry = dget(vma->vm_file->f_dentry);
+ result = 0;
+ break;
+ }
+ vma = vma->vm_next;
+ }
+ up_read(&mm->mmap_sem);
+ mmput(mm);
+out:
+ return result;
+}
+
+static int proc_cwd_link(struct inode *inode, struct dentry **dentry, struct vfsmount **mnt)
+{
+ struct fs_struct *fs;
+ int result = -ENOENT;
+ task_lock(inode->u.proc_i.task);
+ fs = inode->u.proc_i.task->fs;
+ if(fs)
+ atomic_inc(&fs->count);
+ task_unlock(inode->u.proc_i.task);
+ if (fs) {
+ read_lock(&fs->lock);
+ *mnt = mntget(fs->pwdmnt);
+ *dentry = dget(fs->pwd);
+ read_unlock(&fs->lock);
+ result = 0;
+ put_fs_struct(fs);
+ }
+ return result;
+}
+
+static int proc_root_link(struct inode *inode, struct dentry **dentry, struct vfsmount **mnt)
+{
+ struct fs_struct *fs;
+ int result = -ENOENT;
+ task_lock(inode->u.proc_i.task);
+ fs = inode->u.proc_i.task->fs;
+ if(fs)
+ atomic_inc(&fs->count);
+ task_unlock(inode->u.proc_i.task);
+ if (fs) {
+ read_lock(&fs->lock);
+ *mnt = mntget(fs->rootmnt);
+ *dentry = dget(fs->root);
+ read_unlock(&fs->lock);
+ result = 0;
+ put_fs_struct(fs);
+ }
+ return result;
+}
+
+#define MAY_PTRACE(task) \
+ (task == current || \
+ (task->p_pptr == current && \
+ (task->ptrace & PT_PTRACED) && task->state == TASK_STOPPED))
+
+static int may_ptrace_attach(struct task_struct *task)
+{
+ int retval = 0;
+
+ task_lock(task);
+
+ if (((current->uid != task->euid) ||
+ (current->uid != task->suid) ||
+ (current->uid != task->uid) ||
+ (current->gid != task->egid) ||
+ (current->gid != task->sgid) ||
+ (!cap_issubset(task->cap_permitted, current->cap_permitted)) ||
+ (current->gid != task->gid)) && !capable(CAP_SYS_PTRACE))
+ goto out;
+ rmb();
+ if (!is_dumpable(task) && !capable(CAP_SYS_PTRACE))
+ goto out;
+
+ retval = 1;
+
+out:
+ task_unlock(task);
+ return retval;
+}
+
+static int proc_pid_environ(struct task_struct *task, char * buffer)
+{
+ struct mm_struct *mm;
+ int res = 0;
+
+ if (!may_ptrace_attach(task))
+ return -ESRCH;
+
+ task_lock(task);
+ mm = task->mm;
+ if (mm)
+ atomic_inc(&mm->mm_users);
+ task_unlock(task);
+ if (mm && mm->env_start && mm->env_start < mm->env_end) {
+ unsigned long len = mm->env_end - mm->env_start;
+ if (len > PAGE_SIZE)
+ len = PAGE_SIZE;
+ res = access_process_vm(task, mm->env_start, buffer, len, 0);
+ if (res >= 0 && !may_ptrace_attach(task))
+ res = -ESRCH;
+ }
+ if (mm)
+ mmput(mm);
+ return res;
+}
+
+static int proc_pid_cmdline(struct task_struct *task, char * buffer)
+{
+ struct mm_struct *mm;
+ int res = 0;
+ task_lock(task);
+ mm = task->mm;
+ if (mm)
+ atomic_inc(&mm->mm_users);
+ task_unlock(task);
+ if (mm && mm->arg_start && mm->arg_start < mm->arg_end) {
+ unsigned long len = mm->arg_end - mm->arg_start;
+ if (len > PAGE_SIZE)
+ len = PAGE_SIZE;
+ res = access_process_vm(task, mm->arg_start, buffer, len, 0);
+ /* If the nul at the end of args has been overwritten, then
+ assume application is using setproctitle(3). */
+ if (res > 0 && buffer[res - 1] != '\0') {
+ len = strnlen(buffer, res);
+ if (len < res) {
+ res = len;
+ } else
+ if (mm->env_start < mm->env_end && res <= PAGE_SIZE) {
+ len = mm->env_end - mm->env_start;
+ if (len > PAGE_SIZE - res)
+ len = PAGE_SIZE - res;
+ res += access_process_vm(task, mm->env_start, buffer+res, len, 0);
+ res = strnlen(buffer, res);
+ } else
+ res = 0;
+ }
+ }
+ if (mm)
+ mmput(mm);
+ return res;
+}
+
+/************************************************************************/
+/* Here the fs part begins */
+/************************************************************************/
+
+/* permission checks */
+
+static int proc_check_root(struct inode *inode)
+{
+ struct dentry *de, *base, *root;
+ struct vfsmount *our_vfsmnt, *vfsmnt, *mnt;
+ int res = 0;
+
+ if (proc_root_link(inode, &root, &vfsmnt)) /* Ewww... */
+ return -ENOENT;
+ read_lock(&current->fs->lock);
+ our_vfsmnt = mntget(current->fs->rootmnt);
+ base = dget(current->fs->root);
+ read_unlock(&current->fs->lock);
+
+ spin_lock(&dcache_lock);
+ de = root;
+ mnt = vfsmnt;
+
+ while (vfsmnt != our_vfsmnt) {
+ if (vfsmnt == vfsmnt->mnt_parent)
+ goto out;
+ de = vfsmnt->mnt_mountpoint;
+ vfsmnt = vfsmnt->mnt_parent;
+ }
+
+ if (!is_subdir(de, base))
+ goto out;
+ spin_unlock(&dcache_lock);
+
+exit:
+ dput(base);
+ mntput(our_vfsmnt);
+ dput(root);
+ mntput(mnt);
+ return res;
+out:
+ spin_unlock(&dcache_lock);
+ res = -EACCES;
+ goto exit;
+}
+
+static int proc_permission(struct inode *inode, int mask)
+{
+ if (vfs_permission(inode, mask) != 0)
+ return -EACCES;
+ return proc_check_root(inode);
+}
+
+extern struct seq_operations proc_pid_maps_op;
+static int maps_open(struct inode *inode, struct file *file)
+{
+ struct task_struct *task = inode->u.proc_i.task;
+ int ret = seq_open(file, &proc_pid_maps_op);
+ if (!ret) {
+ struct seq_file *m = file->private_data;
+ m->private = task;
+ }
+ return ret;
+}
+
+static struct file_operations proc_maps_operations = {
+ .open = maps_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+extern struct seq_operations mounts_op;
+static int mounts_open(struct inode *inode, struct file *file)
+{
+ struct task_struct *task = inode->u.proc_i.task;
+ int ret = seq_open(file, &mounts_op);
+
+ if (!ret) {
+ struct seq_file *m = file->private_data;
+ struct namespace *namespace;
+ task_lock(task);
+ namespace = task->namespace;
+ if (namespace)
+ get_namespace(namespace);
+ task_unlock(task);
+
+ if (namespace)
+ m->private = namespace;
+ else {
+ seq_release(inode, file);
+ ret = -EINVAL;
+ }
+ }
+ return ret;
+}
+
+static int mounts_release(struct inode *inode, struct file *file)
+{
+ struct seq_file *m = file->private_data;
+ struct namespace *namespace = m->private;
+ put_namespace(namespace);
+ return seq_release(inode, file);
+}
+
+static struct file_operations proc_mounts_operations = {
+ open: mounts_open,
+ read: seq_read,
+ llseek: seq_lseek,
+ release: mounts_release,
+};
+
+#define PROC_BLOCK_SIZE (3*1024) /* 4K page size but our output routines use some slack for overruns */
+
+static ssize_t proc_info_read(struct file * file, char * buf,
+ size_t count, loff_t *ppos)
+{
+ struct inode * inode = file->f_dentry->d_inode;
+ unsigned long page;
+ ssize_t length;
+ ssize_t end;
+ struct task_struct *task = inode->u.proc_i.task;
+ loff_t pos = *ppos;
+
+ if (count > PROC_BLOCK_SIZE)
+ count = PROC_BLOCK_SIZE;
+ if (!(page = __get_free_page(GFP_KERNEL)))
+ return -ENOMEM;
+
+ length = inode->u.proc_i.op.proc_read(task, (char*)page);
+
+ if (length < 0) {
+ free_page(page);
+ return length;
+ }
+ /* Static 4kB (or whatever) block capacity */
+ if (pos < 0 || pos >= length) {
+ free_page(page);
+ return 0;
+ }
+ if (count > length - pos)
+ count = length - pos;
+ end = count + pos;
+ copy_to_user(buf, (char *) page + pos, count);
+ *ppos = end;
+ free_page(page);
+ return count;
+}
+
+static struct file_operations proc_info_file_operations = {
+ read: proc_info_read,
+};
+
+static int mem_open(struct inode* inode, struct file* file)
+{
+ file->private_data = (void*)((long)current->self_exec_id);
+ return 0;
+}
+
+static ssize_t mem_read(struct file * file, char * buf,
+ size_t count, loff_t *ppos)
+{
+ struct task_struct *task = file->f_dentry->d_inode->u.proc_i.task;
+ char *page;
+ unsigned long src = *ppos;
+ int copied = 0;
+ struct mm_struct *mm;
+
+ if (!MAY_PTRACE(task) || !may_ptrace_attach(task))
+ return -ESRCH;
+
+ page = (char *)__get_free_page(GFP_USER);
+ if (!page)
+ return -ENOMEM;
+
+ task_lock(task);
+ mm = task->mm;
+ if (mm)
+ atomic_inc(&mm->mm_users);
+ task_unlock(task);
+ if (!mm){
+ copied = 0;
+ goto out_free;
+ }
+
+ if (file->private_data != (void*)((long)current->self_exec_id) ) {
+ mmput(mm);
+ copied = -EIO;
+ goto out_free;
+ }
+
+ while (count > 0) {
+ int this_len, retval;
+
+ this_len = (count > PAGE_SIZE) ? PAGE_SIZE : count;
+ retval = access_process_vm(task, src, page, this_len, 0);
+ if (!retval || !MAY_PTRACE(task) || !may_ptrace_attach(task)) {
+ if (!copied)
+ copied = -EIO;
+ break;
+ }
+ if (copy_to_user(buf, page, retval)) {
+ copied = -EFAULT;
+ break;
+ }
+ copied += retval;
+ src += retval;
+ buf += retval;
+ count -= retval;
+ }
+ *ppos = src;
+ mmput(mm);
+
+out_free:
+ free_page((unsigned long) page);
+ return copied;
+}
+
+#define mem_write NULL
+
+#ifndef mem_write
+/* This is a security hazard */
+static ssize_t mem_write(struct file * file, const char * buf,
+ size_t count, loff_t *ppos)
+{
+ int copied = 0;
+ char *page;
+ struct task_struct *task = file->f_dentry->d_inode->u.proc_i.task;
+ unsigned long dst = *ppos;
+
+ if (!MAY_PTRACE(task) || !may_ptrace_attach(task))
+ return -ESRCH;
+
+ page = (char *)__get_free_page(GFP_USER);
+ if (!page)
+ return -ENOMEM;
+
+ while (count > 0) {
+ int this_len, retval;
+
+ this_len = (count > PAGE_SIZE) ? PAGE_SIZE : count;
+ if (copy_from_user(page, buf, this_len)) {
+ copied = -EFAULT;
+ break;
+ }
+ retval = access_process_vm(task, dst, page, this_len, 1);
+ if (!retval) {
+ if (!copied)
+ copied = -EIO;
+ break;
+ }
+ copied += retval;
+ buf += retval;
+ dst += retval;
+ count -= retval;
+ }
+ *ppos = dst;
+ free_page((unsigned long) page);
+ return copied;
+}
+#endif
+
+static loff_t mem_lseek(struct file * file, loff_t offset, int orig)
+{
+ switch (orig) {
+ case 0:
+ file->f_pos = offset;
+ break;
+ case 1:
+ file->f_pos += offset;
+ break;
+ default:
+ return -EINVAL;
+ }
+ force_successful_syscall_return();
+ return file->f_pos;
+}
+
+static struct file_operations proc_mem_operations = {
+ llseek: mem_lseek,
+ read: mem_read,
+ write: mem_write,
+ open: mem_open,
+};
+
+static struct inode_operations proc_mem_inode_operations = {
+ permission: proc_permission,
+};
+
+static int proc_pid_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+ struct inode *inode = dentry->d_inode;
+ int error = -EACCES;
+
+ /* We don't need a base pointer in the /proc filesystem */
+ path_release(nd);
+
+ if (current->fsuid != inode->i_uid && !capable(CAP_DAC_OVERRIDE))
+ goto out;
+ error = proc_check_root(inode);
+ if (error)
+ goto out;
+
+ error = inode->u.proc_i.op.proc_get_link(inode, &nd->dentry, &nd->mnt);
+ nd->last_type = LAST_BIND;
+out:
+ return error;
+}
+
+static int do_proc_readlink(struct dentry *dentry, struct vfsmount *mnt,
+ char * buffer, int buflen)
+{
+ struct inode * inode;
+ char * tmp = (char*)__get_free_page(GFP_KERNEL), *path;
+ int len;
+
+ if (!tmp)
+ return -ENOMEM;
+
+ inode = dentry->d_inode;
+ path = d_path(dentry, mnt, tmp, PAGE_SIZE);
+ if (IS_ERR(path)) {
+ free_page((unsigned long)tmp);
+ return PTR_ERR(path);
+ }
+ len = tmp + PAGE_SIZE - 1 - path;
+
+ if (len < buflen)
+ buflen = len;
+ copy_to_user(buffer, path, buflen);
+ free_page((unsigned long)tmp);
+ return buflen;
+}
+
+static int proc_pid_readlink(struct dentry * dentry, char * buffer, int buflen)
+{
+ int error = -EACCES;
+ struct inode *inode = dentry->d_inode;
+ struct dentry *de;
+ struct vfsmount *mnt = NULL;
+
+ if (current->fsuid != inode->i_uid && !capable(CAP_DAC_OVERRIDE))
+ goto out;
+ error = proc_check_root(inode);
+ if (error)
+ goto out;
+
+ error = inode->u.proc_i.op.proc_get_link(inode, &de, &mnt);
+ if (error)
+ goto out;
+
+ error = do_proc_readlink(de, mnt, buffer, buflen);
+ dput(de);
+ mntput(mnt);
+out:
+ return error;
+}
+
+static struct inode_operations proc_pid_link_inode_operations = {
+ readlink: proc_pid_readlink,
+ follow_link: proc_pid_follow_link
+};
+
+struct pid_entry {
+ int type;
+ int len;
+ char *name;
+ mode_t mode;
+};
+
+enum pid_directory_inos {
+ PROC_PID_INO = 2,
+ PROC_PID_STATUS,
+ PROC_PID_MEM,
+ PROC_PID_CWD,
+ PROC_PID_ROOT,
+ PROC_PID_EXE,
+ PROC_PID_FD,
+ PROC_PID_ENVIRON,
+ PROC_PID_CMDLINE,
+ PROC_PID_STAT,
+ PROC_PID_STATM,
+ PROC_PID_MAPS,
+ PROC_PID_CPU,
+ PROC_PID_MOUNTS,
+ PROC_PID_FD_DIR = 0x8000, /* 0x8000-0xffff */
+};
+
+#define E(type,name,mode) {(type),sizeof(name)-1,(name),(mode)}
+static struct pid_entry base_stuff[] = {
+ E(PROC_PID_FD, "fd", S_IFDIR|S_IRUSR|S_IXUSR),
+ E(PROC_PID_ENVIRON, "environ", S_IFREG|S_IRUSR),
+ E(PROC_PID_STATUS, "status", S_IFREG|S_IRUGO),
+ E(PROC_PID_CMDLINE, "cmdline", S_IFREG|S_IRUGO),
+ E(PROC_PID_STAT, "stat", S_IFREG|S_IRUGO),
+ E(PROC_PID_STATM, "statm", S_IFREG|S_IRUGO),
+#ifdef CONFIG_SMP
+ E(PROC_PID_CPU, "cpu", S_IFREG|S_IRUGO),
+#endif
+ E(PROC_PID_MAPS, "maps", S_IFREG|S_IRUGO),
+ E(PROC_PID_MEM, "mem", S_IFREG|S_IRUSR|S_IWUSR),
+ E(PROC_PID_CWD, "cwd", S_IFLNK|S_IRWXUGO),
+ E(PROC_PID_ROOT, "root", S_IFLNK|S_IRWXUGO),
+ E(PROC_PID_EXE, "exe", S_IFLNK|S_IRWXUGO),
+ E(PROC_PID_MOUNTS, "mounts", S_IFREG|S_IRUGO),
+ {0,0,NULL,0}
+};
+#undef E
+
+#define NUMBUF 10
+
+static int proc_readfd(struct file * filp, void * dirent, filldir_t filldir)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ struct task_struct *p = inode->u.proc_i.task;
+ unsigned int fd, pid, ino;
+ int retval;
+ char buf[NUMBUF];
+ struct files_struct * files;
+
+ retval = 0;
+ pid = p->pid;
+
+ fd = filp->f_pos;
+ switch (fd) {
+ case 0:
+ if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR) < 0)
+ goto out;
+ filp->f_pos++;
+ case 1:
+ ino = fake_ino(pid, PROC_PID_INO);
+ if (filldir(dirent, "..", 2, 1, ino, DT_DIR) < 0)
+ goto out;
+ filp->f_pos++;
+ default:
+ task_lock(p);
+ files = p->files;
+ if (files)
+ atomic_inc(&files->count);
+ task_unlock(p);
+ if (!files)
+ goto out;
+ read_lock(&files->file_lock);
+ for (fd = filp->f_pos-2;
+ fd < files->max_fds;
+ fd++, filp->f_pos++) {
+ unsigned int i,j;
+
+ if (!fcheck_files(files, fd))
+ continue;
+ read_unlock(&files->file_lock);
+
+ j = NUMBUF;
+ i = fd;
+ do {
+ j--;
+ buf[j] = '0' + (i % 10);
+ i /= 10;
+ } while (i);
+
+ ino = fake_ino(pid, PROC_PID_FD_DIR + fd);
+ if (filldir(dirent, buf+j, NUMBUF-j, fd+2, ino, DT_LNK) < 0) {
+ read_lock(&files->file_lock);
+ break;
+ }
+ read_lock(&files->file_lock);
+ }
+ read_unlock(&files->file_lock);
+ put_files_struct(files);
+ }
+out:
+ return retval;
+}
+
+static int proc_base_readdir(struct file * filp,
+ void * dirent, filldir_t filldir)
+{
+ int i;
+ int pid;
+ struct inode *inode = filp->f_dentry->d_inode;
+ struct pid_entry *p;
+
+ pid = inode->u.proc_i.task->pid;
+ if (!pid)
+ return -ENOENT;
+ i = filp->f_pos;
+ switch (i) {
+ case 0:
+ if (filldir(dirent, ".", 1, i, inode->i_ino, DT_DIR) < 0)
+ return 0;
+ i++;
+ filp->f_pos++;
+ /* fall through */
+ case 1:
+ if (filldir(dirent, "..", 2, i, PROC_ROOT_INO, DT_DIR) < 0)
+ return 0;
+ i++;
+ filp->f_pos++;
+ /* fall through */
+ default:
+ i -= 2;
+ if (i>=sizeof(base_stuff)/sizeof(base_stuff[0]))
+ return 1;
+ p = base_stuff + i;
+ while (p->name) {
+ if (filldir(dirent, p->name, p->len, filp->f_pos,
+ fake_ino(pid, p->type), p->mode >> 12) < 0)
+ return 0;
+ filp->f_pos++;
+ p++;
+ }
+ }
+ return 1;
+}
+
+/* building an inode */
+
+static int task_dumpable(struct task_struct *task)
+{
+ int dumpable = 0;
+ struct mm_struct *mm;
+
+ task_lock(task);
+ mm = task->mm;
+ if (mm)
+ dumpable = mm->dumpable;
+ task_unlock(task);
+ return dumpable;
+}
+
+
+static struct inode *proc_pid_make_inode(struct super_block * sb, struct task_struct *task, int ino)
+{
+ struct inode * inode;
+
+ /* We need a new inode */
+
+ inode = new_inode(sb);
+ if (!inode)
+ goto out;
+
+ /* Common stuff */
+
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+ inode->i_ino = fake_ino(task->pid, ino);
+
+ if (!task->pid)
+ goto out_unlock;
+
+ /*
+ * grab the reference to task.
+ */
+ get_task_struct(task);
+ inode->u.proc_i.task = task;
+ inode->i_uid = 0;
+ inode->i_gid = 0;
+ if (ino == PROC_PID_INO || task_dumpable(task)) {
+ inode->i_uid = task->euid;
+ inode->i_gid = task->egid;
+ }
+
+out:
+ return inode;
+
+out_unlock:
+ inode->u.generic_ip = NULL;
+ iput(inode);
+ return NULL;
+}
+
+/* dentry stuff */
+
+static int pid_fd_revalidate(struct dentry * dentry, int flags)
+{
+ return 0;
+}
+
+/*
+ * Exceptional case: normally we are not allowed to unhash a busy
+ * directory. In this case, however, we can do it - no aliasing problems
+ * due to the way we treat inodes.
+ */
+static int pid_base_revalidate(struct dentry * dentry, int flags)
+{
+ if (dentry->d_inode->u.proc_i.task->pid)
+ return 1;
+ d_drop(dentry);
+ return 0;
+}
+
+static int pid_delete_dentry(struct dentry * dentry)
+{
+ return 1;
+}
+
+static struct dentry_operations pid_fd_dentry_operations =
+{
+ d_revalidate: pid_fd_revalidate,
+ d_delete: pid_delete_dentry,
+};
+
+static struct dentry_operations pid_dentry_operations =
+{
+ d_delete: pid_delete_dentry,
+};
+
+static struct dentry_operations pid_base_dentry_operations =
+{
+ d_revalidate: pid_base_revalidate,
+ d_delete: pid_delete_dentry,
+};
+
+/* Lookups */
+#define MAX_MULBY10 ((~0U-9)/10)
+
+static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry)
+{
+ unsigned int fd, c;
+ struct task_struct *task = dir->u.proc_i.task;
+ struct file * file;
+ struct files_struct * files;
+ struct inode *inode;
+ const char *name;
+ int len;
+
+ fd = 0;
+ len = dentry->d_name.len;
+ name = dentry->d_name.name;
+ if (len > 1 && *name == '0') goto out;
+ while (len-- > 0) {
+ c = *name - '0';
+ name++;
+ if (c > 9)
+ goto out;
+ if (fd >= MAX_MULBY10)
+ goto out;
+ fd *= 10;
+ fd += c;
+ }
+
+ inode = proc_pid_make_inode(dir->i_sb, task, PROC_PID_FD_DIR+fd);
+ if (!inode)
+ goto out;
+ task_lock(task);
+ files = task->files;
+ if (files)
+ atomic_inc(&files->count);
+ task_unlock(task);
+ if (!files)
+ goto out_unlock;
+ read_lock(&files->file_lock);
+ file = inode->u.proc_i.file = fcheck_files(files, fd);
+ if (!file)
+ goto out_unlock2;
+ get_file(file);
+ read_unlock(&files->file_lock);
+ put_files_struct(files);
+ inode->i_op = &proc_pid_link_inode_operations;
+ inode->i_size = 64;
+ inode->i_mode = S_IFLNK;
+ inode->u.proc_i.op.proc_get_link = proc_fd_link;
+ if (file->f_mode & 1)
+ inode->i_mode |= S_IRUSR | S_IXUSR;
+ if (file->f_mode & 2)
+ inode->i_mode |= S_IWUSR | S_IXUSR;
+ dentry->d_op = &pid_fd_dentry_operations;
+ d_add(dentry, inode);
+ return NULL;
+
+out_unlock2:
+ read_unlock(&files->file_lock);
+ put_files_struct(files);
+out_unlock:
+ iput(inode);
+out:
+ return ERR_PTR(-ENOENT);
+}
+
+static struct file_operations proc_fd_operations = {
+ read: generic_read_dir,
+ readdir: proc_readfd,
+};
+
+/*
+ * proc directories can do almost nothing..
+ */
+static struct inode_operations proc_fd_inode_operations = {
+ lookup: proc_lookupfd,
+ permission: proc_permission,
+};
+
+static struct dentry *proc_base_lookup(struct inode *dir, struct dentry *dentry)
+{
+ struct inode *inode;
+ int error;
+ struct task_struct *task = dir->u.proc_i.task;
+ struct pid_entry *p;
+
+ error = -ENOENT;
+ inode = NULL;
+
+ for (p = base_stuff; p->name; p++) {
+ if (p->len != dentry->d_name.len)
+ continue;
+ if (!memcmp(dentry->d_name.name, p->name, p->len))
+ break;
+ }
+ if (!p->name)
+ goto out;
+
+ error = -EINVAL;
+ inode = proc_pid_make_inode(dir->i_sb, task, p->type);
+ if (!inode)
+ goto out;
+
+ inode->i_mode = p->mode;
+ /*
+ * Yes, it does not scale. And it should not. Don't add
+ * new entries into /proc/<pid>/ without very good reasons.
+ */
+ switch(p->type) {
+ case PROC_PID_FD:
+ inode->i_nlink = 2;
+ inode->i_op = &proc_fd_inode_operations;
+ inode->i_fop = &proc_fd_operations;
+ break;
+ case PROC_PID_EXE:
+ inode->i_op = &proc_pid_link_inode_operations;
+ inode->u.proc_i.op.proc_get_link = proc_exe_link;
+ break;
+ case PROC_PID_CWD:
+ inode->i_op = &proc_pid_link_inode_operations;
+ inode->u.proc_i.op.proc_get_link = proc_cwd_link;
+ break;
+ case PROC_PID_ROOT:
+ inode->i_op = &proc_pid_link_inode_operations;
+ inode->u.proc_i.op.proc_get_link = proc_root_link;
+ break;
+ case PROC_PID_ENVIRON:
+ inode->i_fop = &proc_info_file_operations;
+ inode->u.proc_i.op.proc_read = proc_pid_environ;
+ break;
+ case PROC_PID_STATUS:
+ inode->i_fop = &proc_info_file_operations;
+ inode->u.proc_i.op.proc_read = proc_pid_status;
+ break;
+ case PROC_PID_STAT:
+ inode->i_fop = &proc_info_file_operations;
+ inode->u.proc_i.op.proc_read = proc_pid_stat;
+ break;
+ case PROC_PID_CMDLINE:
+ inode->i_fop = &proc_info_file_operations;
+ inode->u.proc_i.op.proc_read = proc_pid_cmdline;
+ break;
+ case PROC_PID_STATM:
+ inode->i_fop = &proc_info_file_operations;
+ inode->u.proc_i.op.proc_read = proc_pid_statm;
+ break;
+ case PROC_PID_MAPS:
+ inode->i_fop = &proc_maps_operations;
+ break;
+#ifdef CONFIG_SMP
+ case PROC_PID_CPU:
+ inode->i_fop = &proc_info_file_operations;
+ inode->u.proc_i.op.proc_read = proc_pid_cpu;
+ break;
+#endif
+ case PROC_PID_MEM:
+ inode->i_op = &proc_mem_inode_operations;
+ inode->i_fop = &proc_mem_operations;
+ break;
+ case PROC_PID_MOUNTS:
+ inode->i_fop = &proc_mounts_operations;
+ break;
+ default:
+ printk("procfs: impossible type (%d)",p->type);
+ iput(inode);
+ return ERR_PTR(-EINVAL);
+ }
+ dentry->d_op = &pid_dentry_operations;
+ d_add(dentry, inode);
+ return NULL;
+
+out:
+ return ERR_PTR(error);
+}
+
+static struct file_operations proc_base_operations = {
+ read: generic_read_dir,
+ readdir: proc_base_readdir,
+};
+
+static struct inode_operations proc_base_inode_operations = {
+ lookup: proc_base_lookup,
+};
+
+/*
+ * /proc/self:
+ */
+static int proc_self_readlink(struct dentry *dentry, char *buffer, int buflen)
+{
+ char tmp[30];
+ sprintf(tmp, "%d", current->pid);
+ return vfs_readlink(dentry,buffer,buflen,tmp);
+}
+
+static int proc_self_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+ char tmp[30];
+ sprintf(tmp, "%d", current->pid);
+ return vfs_follow_link(nd,tmp);
+}
+
+static struct inode_operations proc_self_inode_operations = {
+ readlink: proc_self_readlink,
+ follow_link: proc_self_follow_link,
+};
+
+struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry)
+{
+ unsigned int pid, c;
+ struct task_struct *task;
+ const char *name;
+ struct inode *inode;
+ int len;
+
+ pid = 0;
+ name = dentry->d_name.name;
+ len = dentry->d_name.len;
+ if (len == 4 && !memcmp(name, "self", 4)) {
+ inode = new_inode(dir->i_sb);
+ if (!inode)
+ return ERR_PTR(-ENOMEM);
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+ inode->i_ino = fake_ino(0, PROC_PID_INO);
+ inode->u.proc_i.file = NULL;
+ inode->u.proc_i.task = NULL;
+ inode->i_mode = S_IFLNK|S_IRWXUGO;
+ inode->i_uid = inode->i_gid = 0;
+ inode->i_size = 64;
+ inode->i_op = &proc_self_inode_operations;
+ d_add(dentry, inode);
+ return NULL;
+ }
+ while (len-- > 0) {
+ c = *name - '0';
+ name++;
+ if (c > 9)
+ goto out;
+ if (pid >= MAX_MULBY10)
+ goto out;
+ pid *= 10;
+ pid += c;
+ if (!pid)
+ goto out;
+ }
+
+ read_lock(&tasklist_lock);
+ task = find_task_by_pid(pid);
+ if (task)
+ get_task_struct(task);
+ read_unlock(&tasklist_lock);
+ if (!task)
+ goto out;
+
+ inode = proc_pid_make_inode(dir->i_sb, task, PROC_PID_INO);
+
+ free_task_struct(task);
+
+ if (!inode)
+ goto out;
+ inode->i_mode = S_IFDIR|S_IRUGO|S_IXUGO;
+ inode->i_op = &proc_base_inode_operations;
+ inode->i_fop = &proc_base_operations;
+ inode->i_nlink = 3;
+ inode->i_flags|=S_IMMUTABLE;
+
+ dentry->d_op = &pid_base_dentry_operations;
+ d_add(dentry, inode);
+ return NULL;
+out:
+ return ERR_PTR(-ENOENT);
+}
+
+void proc_pid_delete_inode(struct inode *inode)
+{
+ if (inode->u.proc_i.file)
+ fput(inode->u.proc_i.file);
+ if (inode->u.proc_i.task)
+ free_task_struct(inode->u.proc_i.task);
+}
+
+#define PROC_NUMBUF 10
+#define PROC_MAXPIDS 20
+
+/*
+ * Get a few pid's to return for filldir - we need to hold the
+ * tasklist lock while doing this, and we must release it before
+ * we actually do the filldir itself, so we use a temp buffer..
+ */
+static int get_pid_list(int index, unsigned int *pids)
+{
+ struct task_struct *p;
+ int nr_pids = 0;
+
+ index--;
+ read_lock(&tasklist_lock);
+ for_each_task(p) {
+ int pid = p->pid;
+ if (!pid)
+ continue;
+ if (--index >= 0)
+ continue;
+ pids[nr_pids] = pid;
+ nr_pids++;
+ if (nr_pids >= PROC_MAXPIDS)
+ break;
+ }
+ read_unlock(&tasklist_lock);
+ return nr_pids;
+}
+
+int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir)
+{
+ unsigned int pid_array[PROC_MAXPIDS];
+ char buf[PROC_NUMBUF];
+ unsigned int nr = filp->f_pos - FIRST_PROCESS_ENTRY;
+ unsigned int nr_pids, i;
+
+ if (!nr) {
+ ino_t ino = fake_ino(0,PROC_PID_INO);
+ if (filldir(dirent, "self", 4, filp->f_pos, ino, DT_LNK) < 0)
+ return 0;
+ filp->f_pos++;
+ nr++;
+ }
+
+ nr_pids = get_pid_list(nr, pid_array);
+
+ for (i = 0; i < nr_pids; i++) {
+ int pid = pid_array[i];
+ ino_t ino = fake_ino(pid,PROC_PID_INO);
+ unsigned long j = PROC_NUMBUF;
+
+ do buf[--j] = '0' + (pid % 10); while (pid/=10);
+
+ if (filldir(dirent, buf+j, PROC_NUMBUF-j, filp->f_pos, ino, DT_DIR) < 0)
+ break;
+ filp->f_pos++;
+ }
+ return 0;
+}
diff --git a/fs/proc/generic.c b/fs/proc/generic.c
new file mode 100644
index 00000000000000..896e79a460fca2
--- /dev/null
+++ b/fs/proc/generic.c
@@ -0,0 +1,598 @@
+/*
+ * proc/fs/generic.c --- generic routines for the proc-fs
+ *
+ * This file contains generic proc-fs routines for handling
+ * directories and files.
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds.
+ * Copyright (C) 1997 Theodore Ts'o
+ */
+
+#include <asm/uaccess.h>
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <asm/bitops.h>
+
+static ssize_t proc_file_read(struct file * file, char * buf,
+ size_t nbytes, loff_t *ppos);
+static ssize_t proc_file_write(struct file * file, const char * buffer,
+ size_t count, loff_t *ppos);
+static loff_t proc_file_lseek(struct file *, loff_t, int);
+
+int proc_match(int len, const char *name,struct proc_dir_entry * de)
+{
+ if (!de || !de->low_ino)
+ return 0;
+ if (de->namelen != len)
+ return 0;
+ return !memcmp(name, de->name, len);
+}
+
+static struct file_operations proc_file_operations = {
+ llseek: proc_file_lseek,
+ read: proc_file_read,
+ write: proc_file_write,
+};
+
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+/* buffer size is one page but our output routines use some slack for overruns */
+#define PROC_BLOCK_SIZE (PAGE_SIZE - 1024)
+
+static ssize_t
+proc_file_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos)
+{
+ struct inode * inode = file->f_dentry->d_inode;
+ char *page;
+ ssize_t retval=0;
+ int eof=0;
+ ssize_t n, count;
+ char *start;
+ struct proc_dir_entry * dp;
+ loff_t pos = *ppos;
+
+ dp = (struct proc_dir_entry *) inode->u.generic_ip;
+ if (!(page = (char*) __get_free_page(GFP_KERNEL)))
+ return -ENOMEM;
+
+ while ((nbytes > 0) && !eof)
+ {
+ count = MIN(PROC_BLOCK_SIZE, nbytes);
+ if ((unsigned)pos > INT_MAX)
+ break;
+
+ start = NULL;
+ if (dp->get_info) {
+ /*
+ * Handle backwards compatibility with the old net
+ * routines.
+ */
+ n = dp->get_info(page, &start, pos, count);
+ if (n < count)
+ eof = 1;
+ } else if (dp->read_proc) {
+ n = dp->read_proc(page, &start, pos,
+ count, &eof, dp->data);
+ } else
+ break;
+
+ if (!start) {
+ /*
+ * For proc files that are less than 4k
+ */
+ start = page + pos;
+ n -= pos;
+ if (n <= 0)
+ break;
+ if (n > count)
+ n = count;
+ }
+ if (n == 0)
+ break; /* End of file */
+ if (n < 0) {
+ if (retval == 0)
+ retval = n;
+ break;
+ }
+
+ /* This is a hack to allow mangling of file pos independent
+ * of actual bytes read. Simply place the data at page,
+ * return the bytes, and set `start' to the desired offset
+ * as an unsigned int. - Paul.Russell@rustcorp.com.au
+ */
+ n -= copy_to_user(buf, start < page ? page : start, n);
+ if (n == 0) {
+ if (retval == 0)
+ retval = -EFAULT;
+ break;
+ }
+
+ pos += start < page ? (long)start : n; /* Move down the file */
+ nbytes -= n;
+ buf += n;
+ retval += n;
+ }
+ free_page((unsigned long) page);
+ *ppos = pos;
+ return retval;
+}
+
+static ssize_t
+proc_file_write(struct file * file, const char * buffer,
+ size_t count, loff_t *ppos)
+{
+ struct inode *inode = file->f_dentry->d_inode;
+ struct proc_dir_entry * dp;
+
+ dp = (struct proc_dir_entry *) inode->u.generic_ip;
+
+ if (!dp->write_proc)
+ return -EIO;
+
+ /* FIXME: does this routine need ppos? probably... */
+ return dp->write_proc(file, buffer, count, dp->data);
+}
+
+
+static loff_t
+proc_file_lseek(struct file * file, loff_t offset, int origin)
+{
+ long long retval;
+
+ switch (origin) {
+ case 2:
+ offset += file->f_dentry->d_inode->i_size;
+ break;
+ case 1:
+ offset += file->f_pos;
+ }
+ retval = -EINVAL;
+ if (offset>=0 && (unsigned long long)offset<=file->f_dentry->d_inode->i_sb->s_maxbytes) {
+ if (offset != file->f_pos) {
+ file->f_pos = offset;
+ file->f_reada = 0;
+ }
+ retval = offset;
+ }
+ /* RED-PEN user can fake an error here by setting offset to >=-4095 && <0 */
+ return retval;
+}
+
+/*
+ * This function parses a name such as "tty/driver/serial", and
+ * returns the struct proc_dir_entry for "/proc/tty/driver", and
+ * returns "serial" in residual.
+ */
+static int xlate_proc_name(const char *name,
+ struct proc_dir_entry **ret, const char **residual)
+{
+ const char *cp = name, *next;
+ struct proc_dir_entry *de;
+ int len;
+
+ de = &proc_root;
+ while (1) {
+ next = strchr(cp, '/');
+ if (!next)
+ break;
+
+ len = next - cp;
+ for (de = de->subdir; de ; de = de->next) {
+ if (proc_match(len, cp, de))
+ break;
+ }
+ if (!de)
+ return -ENOENT;
+ cp += len + 1;
+ }
+ *residual = cp;
+ *ret = de;
+ return 0;
+}
+
+static unsigned long proc_alloc_map[(PROC_NDYNAMIC + BITS_PER_LONG - 1) / BITS_PER_LONG];
+
+spinlock_t proc_alloc_map_lock = SPIN_LOCK_UNLOCKED;
+
+static int make_inode_number(void)
+{
+ int i;
+ spin_lock(&proc_alloc_map_lock);
+ i = find_first_zero_bit(proc_alloc_map, PROC_NDYNAMIC);
+ if (i < 0 || i >= PROC_NDYNAMIC) {
+ i = -1;
+ goto out;
+ }
+ set_bit(i, proc_alloc_map);
+ i += PROC_DYNAMIC_FIRST;
+out:
+ spin_unlock(&proc_alloc_map_lock);
+ return i;
+}
+
+static int proc_readlink(struct dentry *dentry, char *buffer, int buflen)
+{
+ char *s=((struct proc_dir_entry *)dentry->d_inode->u.generic_ip)->data;
+ return vfs_readlink(dentry, buffer, buflen, s);
+}
+
+static int proc_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+ char *s=((struct proc_dir_entry *)dentry->d_inode->u.generic_ip)->data;
+ return vfs_follow_link(nd, s);
+}
+
+static struct inode_operations proc_link_inode_operations = {
+ readlink: proc_readlink,
+ follow_link: proc_follow_link,
+};
+
+/*
+ * As some entries in /proc are volatile, we want to
+ * get rid of unused dentries. This could be made
+ * smarter: we could keep a "volatile" flag in the
+ * inode to indicate which ones to keep.
+ */
+static int proc_delete_dentry(struct dentry * dentry)
+{
+ return 1;
+}
+
+static struct dentry_operations proc_dentry_operations =
+{
+ d_delete: proc_delete_dentry,
+};
+
+/*
+ * Don't create negative dentries here, return -ENOENT by hand
+ * instead.
+ */
+struct dentry *proc_lookup(struct inode * dir, struct dentry *dentry)
+{
+ struct inode *inode;
+ struct proc_dir_entry * de;
+ int error;
+
+ error = -ENOENT;
+ inode = NULL;
+ de = (struct proc_dir_entry *) dir->u.generic_ip;
+ if (de) {
+ for (de = de->subdir; de ; de = de->next) {
+ if (!de || !de->low_ino)
+ continue;
+ if (de->namelen != dentry->d_name.len)
+ continue;
+ if (!memcmp(dentry->d_name.name, de->name, de->namelen)) {
+ int ino = de->low_ino;
+ error = -EINVAL;
+ inode = proc_get_inode(dir->i_sb, ino, de);
+ break;
+ }
+ }
+ }
+
+ if (inode) {
+ dentry->d_op = &proc_dentry_operations;
+ d_add(dentry, inode);
+ return NULL;
+ }
+ return ERR_PTR(error);
+}
+
+/*
+ * This returns non-zero if at EOF, so that the /proc
+ * root directory can use this and check if it should
+ * continue with the <pid> entries..
+ *
+ * Note that the VFS-layer doesn't care about the return
+ * value of the readdir() call, as long as it's non-negative
+ * for success..
+ */
+int proc_readdir(struct file * filp,
+ void * dirent, filldir_t filldir)
+{
+ struct proc_dir_entry * de;
+ unsigned int ino;
+ int i;
+ struct inode *inode = filp->f_dentry->d_inode;
+
+ ino = inode->i_ino;
+ de = (struct proc_dir_entry *) inode->u.generic_ip;
+ if (!de)
+ return -EINVAL;
+ i = filp->f_pos;
+ switch (i) {
+ case 0:
+ if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0)
+ return 0;
+ i++;
+ filp->f_pos++;
+ /* fall through */
+ case 1:
+ if (filldir(dirent, "..", 2, i,
+ filp->f_dentry->d_parent->d_inode->i_ino,
+ DT_DIR) < 0)
+ return 0;
+ i++;
+ filp->f_pos++;
+ /* fall through */
+ default:
+ de = de->subdir;
+ i -= 2;
+ for (;;) {
+ if (!de)
+ return 1;
+ if (!i)
+ break;
+ de = de->next;
+ i--;
+ }
+
+ do {
+ if (filldir(dirent, de->name, de->namelen, filp->f_pos,
+ de->low_ino, de->mode >> 12) < 0)
+ return 0;
+ filp->f_pos++;
+ de = de->next;
+ } while (de);
+ }
+ return 1;
+}
+
+/*
+ * These are the generic /proc directory operations. They
+ * use the in-memory "struct proc_dir_entry" tree to parse
+ * the /proc directory.
+ */
+static struct file_operations proc_dir_operations = {
+ read: generic_read_dir,
+ readdir: proc_readdir,
+};
+
+/*
+ * proc directories can do almost nothing..
+ */
+static struct inode_operations proc_dir_inode_operations = {
+ lookup: proc_lookup,
+};
+
+static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp)
+{
+ int i;
+
+ i = make_inode_number();
+ if (i < 0)
+ return -EAGAIN;
+ dp->low_ino = i;
+ dp->next = dir->subdir;
+ dp->parent = dir;
+ dir->subdir = dp;
+ if (S_ISDIR(dp->mode)) {
+ if (dp->proc_iops == NULL) {
+ dp->proc_fops = &proc_dir_operations;
+ dp->proc_iops = &proc_dir_inode_operations;
+ }
+ dir->nlink++;
+ } else if (S_ISLNK(dp->mode)) {
+ if (dp->proc_iops == NULL)
+ dp->proc_iops = &proc_link_inode_operations;
+ } else if (S_ISREG(dp->mode)) {
+ if (dp->proc_fops == NULL)
+ dp->proc_fops = &proc_file_operations;
+ }
+ return 0;
+}
+
+/*
+ * Kill an inode that got unregistered..
+ */
+static void proc_kill_inodes(struct proc_dir_entry *de)
+{
+ struct list_head *p;
+ struct super_block *sb = proc_mnt->mnt_sb;
+
+ /*
+ * Actually it's a partial revoke().
+ */
+ file_list_lock();
+ for (p = sb->s_files.next; p != &sb->s_files; p = p->next) {
+ struct file * filp = list_entry(p, struct file, f_list);
+ struct dentry * dentry = filp->f_dentry;
+ struct inode * inode;
+ struct file_operations *fops;
+
+ if (dentry->d_op != &proc_dentry_operations)
+ continue;
+ inode = dentry->d_inode;
+ if (inode->u.generic_ip != de)
+ continue;
+ fops = filp->f_op;
+ filp->f_op = NULL;
+ fops_put(fops);
+ }
+ file_list_unlock();
+}
+
+static struct proc_dir_entry *proc_create(struct proc_dir_entry **parent,
+ const char *name,
+ mode_t mode,
+ nlink_t nlink)
+{
+ struct proc_dir_entry *ent = NULL;
+ const char *fn = name;
+ int len;
+
+ /* make sure name is valid */
+ if (!name || !strlen(name)) goto out;
+
+ if (!(*parent) && xlate_proc_name(name, parent, &fn) != 0)
+ goto out;
+ len = strlen(fn);
+
+ ent = kmalloc(sizeof(struct proc_dir_entry) + len + 1, GFP_KERNEL);
+ if (!ent) goto out;
+
+ memset(ent, 0, sizeof(struct proc_dir_entry));
+ memcpy(((char *) ent) + sizeof(struct proc_dir_entry), fn, len + 1);
+ ent->name = ((char *) ent) + sizeof(*ent);
+ ent->namelen = len;
+ ent->mode = mode;
+ ent->nlink = nlink;
+ out:
+ return ent;
+}
+
+struct proc_dir_entry *proc_symlink(const char *name,
+ struct proc_dir_entry *parent, const char *dest)
+{
+ struct proc_dir_entry *ent;
+
+ ent = proc_create(&parent,name,
+ (S_IFLNK | S_IRUGO | S_IWUGO | S_IXUGO),1);
+
+ if (ent) {
+ ent->data = kmalloc((ent->size=strlen(dest))+1, GFP_KERNEL);
+ if (ent->data) {
+ strcpy((char*)ent->data,dest);
+ if (proc_register(parent, ent) < 0) {
+ kfree(ent->data);
+ kfree(ent);
+ ent = NULL;
+ }
+ } else {
+ kfree(ent);
+ ent = NULL;
+ }
+ }
+ return ent;
+}
+
+struct proc_dir_entry *proc_mknod(const char *name, mode_t mode,
+ struct proc_dir_entry *parent, kdev_t rdev)
+{
+ struct proc_dir_entry *ent;
+
+ ent = proc_create(&parent,name,mode,1);
+ if (ent) {
+ ent->rdev = rdev;
+ if (proc_register(parent, ent) < 0) {
+ kfree(ent);
+ ent = NULL;
+ }
+ }
+ return ent;
+}
+
+struct proc_dir_entry *proc_mkdir_mode(const char *name, mode_t mode,
+ struct proc_dir_entry *parent)
+{
+ struct proc_dir_entry *ent;
+
+ ent = proc_create(&parent, name, S_IFDIR | mode, 2);
+ if (ent) {
+ ent->proc_fops = &proc_dir_operations;
+ ent->proc_iops = &proc_dir_inode_operations;
+
+ if (proc_register(parent, ent) < 0) {
+ kfree(ent);
+ ent = NULL;
+ }
+ }
+ return ent;
+}
+
+struct proc_dir_entry *proc_mkdir(const char *name,
+ struct proc_dir_entry *parent)
+{
+ return proc_mkdir_mode(name, S_IRUGO | S_IXUGO, parent);
+}
+
+struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
+ struct proc_dir_entry *parent)
+{
+ struct proc_dir_entry *ent;
+ nlink_t nlink;
+
+ if (S_ISDIR(mode)) {
+ if ((mode & S_IALLUGO) == 0)
+ mode |= S_IRUGO | S_IXUGO;
+ nlink = 2;
+ } else {
+ if ((mode & S_IFMT) == 0)
+ mode |= S_IFREG;
+ if ((mode & S_IALLUGO) == 0)
+ mode |= S_IRUGO;
+ nlink = 1;
+ }
+
+ ent = proc_create(&parent,name,mode,nlink);
+ if (ent) {
+ if (S_ISDIR(mode)) {
+ ent->proc_fops = &proc_dir_operations;
+ ent->proc_iops = &proc_dir_inode_operations;
+ }
+ if (proc_register(parent, ent) < 0) {
+ kfree(ent);
+ ent = NULL;
+ }
+ }
+ return ent;
+}
+
+void free_proc_entry(struct proc_dir_entry *de)
+{
+ int ino = de->low_ino;
+
+ if (ino < PROC_DYNAMIC_FIRST ||
+ ino >= PROC_DYNAMIC_FIRST+PROC_NDYNAMIC)
+ return;
+ if (S_ISLNK(de->mode) && de->data)
+ kfree(de->data);
+ kfree(de);
+}
+
+/*
+ * Remove a /proc entry and free it if it's not currently in use.
+ * If it is in use, we set the 'deleted' flag.
+ */
+void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
+{
+ struct proc_dir_entry **p;
+ struct proc_dir_entry *de;
+ const char *fn = name;
+ int len;
+
+ if (!parent && xlate_proc_name(name, &parent, &fn) != 0)
+ goto out;
+ len = strlen(fn);
+ for (p = &parent->subdir; *p; p=&(*p)->next ) {
+ if (!proc_match(len, fn, *p))
+ continue;
+ de = *p;
+ *p = de->next;
+ de->next = NULL;
+ if (S_ISDIR(de->mode))
+ parent->nlink--;
+ clear_bit(de->low_ino - PROC_DYNAMIC_FIRST,
+ proc_alloc_map);
+ proc_kill_inodes(de);
+ de->nlink = 0;
+ if (!atomic_read(&de->count))
+ free_proc_entry(de);
+ else {
+ de->deleted = 1;
+ printk("remove_proc_entry: %s/%s busy, count=%d\n",
+ parent->name, de->name, atomic_read(&de->count));
+ }
+ break;
+ }
+out:
+ return;
+}
diff --git a/fs/proc/inode-alloc.txt b/fs/proc/inode-alloc.txt
new file mode 100644
index 00000000000000..fbcfa4e402e244
--- /dev/null
+++ b/fs/proc/inode-alloc.txt
@@ -0,0 +1,13 @@
+Current inode allocations in the proc-fs (hex-numbers):
+
+ 00000000 reserved
+ 00000001-00000fff static entries (goners)
+ 001 root-ino
+
+ 00001000-00001fff dynamic entries
+ 0001xxxx-7fffxxxx pid-dir entries for pid 1-7fff
+ 80000000-ffffffff unused
+
+Goal:
+ a) once we'll split the thing into several virtual filesystems we
+ will get rid of magical ranges (and this file, BTW).
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
new file mode 100644
index 00000000000000..2937eb02910383
--- /dev/null
+++ b/fs/proc/inode.c
@@ -0,0 +1,211 @@
+/*
+ * linux/fs/proc/inode.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/file.h>
+#include <linux/locks.h>
+#include <linux/limits.h>
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/smp_lock.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+extern void free_proc_entry(struct proc_dir_entry *);
+
+static inline struct proc_dir_entry * de_get(struct proc_dir_entry *de)
+{
+ if (de)
+ atomic_inc(&de->count);
+ return de;
+}
+
+/*
+ * Decrements the use count and checks for deferred deletion.
+ */
+static void de_put(struct proc_dir_entry *de)
+{
+ if (de) {
+ lock_kernel();
+ if (!atomic_read(&de->count)) {
+ printk("de_put: entry %s already free!\n", de->name);
+ unlock_kernel();
+ return;
+ }
+
+ if (atomic_dec_and_test(&de->count)) {
+ if (de->deleted) {
+ printk("de_put: deferred delete of %s\n",
+ de->name);
+ free_proc_entry(de);
+ }
+ }
+ unlock_kernel();
+ }
+}
+
+/*
+ * Decrement the use count of the proc_dir_entry.
+ */
+static void proc_delete_inode(struct inode *inode)
+{
+ struct proc_dir_entry *de = inode->u.generic_ip;
+
+ inode->i_state = I_CLEAR;
+
+ if (PROC_INODE_PROPER(inode)) {
+ proc_pid_delete_inode(inode);
+ return;
+ }
+ if (de) {
+ if (de->owner)
+ __MOD_DEC_USE_COUNT(de->owner);
+ de_put(de);
+ }
+}
+
+struct vfsmount *proc_mnt;
+
+static void proc_read_inode(struct inode * inode)
+{
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+}
+
+static int proc_statfs(struct super_block *sb, struct statfs *buf)
+{
+ buf->f_type = PROC_SUPER_MAGIC;
+ buf->f_bsize = PAGE_SIZE/sizeof(long);
+ buf->f_bfree = 0;
+ buf->f_bavail = 0;
+ buf->f_ffree = 0;
+ buf->f_namelen = NAME_MAX;
+ return 0;
+}
+
+static struct super_operations proc_sops = {
+ read_inode: proc_read_inode,
+ put_inode: force_delete,
+ delete_inode: proc_delete_inode,
+ statfs: proc_statfs,
+};
+
+
+static int parse_options(char *options,uid_t *uid,gid_t *gid)
+{
+ char *this_char,*value;
+
+ *uid = current->uid;
+ *gid = current->gid;
+ if (!options) return 1;
+ for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) {
+ if ((value = strchr(this_char,'=')) != NULL)
+ *value++ = 0;
+ if (!strcmp(this_char,"uid")) {
+ if (!value || !*value)
+ return 0;
+ *uid = simple_strtoul(value,&value,0);
+ if (*value)
+ return 0;
+ }
+ else if (!strcmp(this_char,"gid")) {
+ if (!value || !*value)
+ return 0;
+ *gid = simple_strtoul(value,&value,0);
+ if (*value)
+ return 0;
+ }
+ else return 1;
+ }
+ return 1;
+}
+
+struct inode * proc_get_inode(struct super_block * sb, int ino,
+ struct proc_dir_entry * de)
+{
+ struct inode * inode;
+
+ /*
+ * Increment the use count so the dir entry can't disappear.
+ */
+ de_get(de);
+#if 1
+/* shouldn't ever happen */
+if (de && de->deleted)
+printk("proc_iget: using deleted entry %s, count=%d\n", de->name, atomic_read(&de->count));
+#endif
+
+ inode = iget(sb, ino);
+ if (!inode)
+ goto out_fail;
+
+ inode->u.generic_ip = (void *) de;
+ if (de) {
+ if (de->mode) {
+ inode->i_mode = de->mode;
+ inode->i_uid = de->uid;
+ inode->i_gid = de->gid;
+ }
+ if (de->size)
+ inode->i_size = de->size;
+ if (de->nlink)
+ inode->i_nlink = de->nlink;
+ if (de->owner)
+ __MOD_INC_USE_COUNT(de->owner);
+ if (de->proc_iops)
+ inode->i_op = de->proc_iops;
+ if (de->proc_fops)
+ inode->i_fop = de->proc_fops;
+ else if (S_ISBLK(de->mode)||S_ISCHR(de->mode)||S_ISFIFO(de->mode))
+ init_special_inode(inode,de->mode,kdev_t_to_nr(de->rdev));
+ }
+
+out:
+ return inode;
+
+out_fail:
+ de_put(de);
+ goto out;
+}
+
+struct super_block *proc_read_super(struct super_block *s,void *data,
+ int silent)
+{
+ struct inode * root_inode;
+ struct task_struct *p;
+
+ s->s_blocksize = 1024;
+ s->s_blocksize_bits = 10;
+ s->s_magic = PROC_SUPER_MAGIC;
+ s->s_op = &proc_sops;
+ s->s_maxbytes = ~0UL;
+
+ root_inode = proc_get_inode(s, PROC_ROOT_INO, &proc_root);
+ if (!root_inode)
+ goto out_no_root;
+ /*
+ * Fixup the root inode's nlink value
+ */
+ read_lock(&tasklist_lock);
+ for_each_task(p) if (p->pid) root_inode->i_nlink++;
+ read_unlock(&tasklist_lock);
+ s->s_root = d_alloc_root(root_inode);
+ if (!s->s_root)
+ goto out_no_root;
+ parse_options(data, &root_inode->i_uid, &root_inode->i_gid);
+ return s;
+
+out_no_root:
+ printk("proc_read_super: get root inode failed\n");
+ iput(root_inode);
+ return NULL;
+}
+MODULE_LICENSE("GPL");
diff --git a/fs/proc/kcore.c b/fs/proc/kcore.c
new file mode 100644
index 00000000000000..cfd5102c6901cc
--- /dev/null
+++ b/fs/proc/kcore.c
@@ -0,0 +1,478 @@
+/*
+ * fs/proc/kcore.c kernel ELF/AOUT core dumper
+ *
+ * Modelled on fs/exec.c:aout_core_dump()
+ * Jeremy Fitzhardinge <jeremy@sw.oz.au>
+ * ELF version written by David Howells <David.Howells@nexor.co.uk>
+ * Modified and incorporated into 2.3.x by Tigran Aivazian <tigran@veritas.com>
+ * Support to dump vmalloc'd areas (ELF only), Tigran Aivazian <tigran@veritas.com>
+ * Safe accesses to vmalloc/direct-mapped discontiguous areas, Kanoj Sarcar <kanoj@sgi.com>
+ */
+
+#include <linux/config.h>
+#include <linux/mm.h>
+#include <linux/proc_fs.h>
+#include <linux/user.h>
+#include <linux/a.out.h>
+#include <linux/elf.h>
+#include <linux/elfcore.h>
+#include <linux/vmalloc.h>
+#include <linux/highmem.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+
+static int open_kcore(struct inode * inode, struct file * filp)
+{
+ return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;
+}
+
+static loff_t lseek_kcore(struct file * file, loff_t offset, int origin);
+
+static ssize_t read_kcore(struct file *, char *, size_t, loff_t *);
+
+struct file_operations proc_kcore_operations = {
+ read: read_kcore,
+ open: open_kcore,
+ llseek: lseek_kcore,
+};
+
+#ifdef CONFIG_KCORE_AOUT
+static ssize_t read_kcore(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+ unsigned long long p = *ppos, memsize;
+ ssize_t read;
+ ssize_t count1;
+ char * pnt;
+ struct user dump;
+#if defined (__i386__) || defined (__mc68000__) || defined(__x86_64__)
+# define FIRST_MAPPED PAGE_SIZE /* we don't have page 0 mapped on x86.. */
+#else
+# define FIRST_MAPPED 0
+#endif
+
+ memset(&dump, 0, sizeof(struct user));
+ dump.magic = CMAGIC;
+ dump.u_dsize = (virt_to_phys(high_memory) >> PAGE_SHIFT);
+#if defined (__i386__) || defined(__x86_64__)
+ dump.start_code = PAGE_OFFSET;
+#endif
+#ifdef __alpha__
+ dump.start_data = PAGE_OFFSET;
+#endif
+
+ memsize = virt_to_phys(high_memory);
+ if (p >= memsize)
+ return 0;
+ if (count > memsize - p)
+ count = memsize - p;
+ read = 0;
+
+ if (p < sizeof(struct user) && count > 0) {
+ count1 = count;
+ if (p + count1 > sizeof(struct user))
+ count1 = sizeof(struct user)-p;
+ pnt = (char *) &dump + p;
+ if (copy_to_user(buf,(void *) pnt, count1))
+ return -EFAULT;
+ buf += count1;
+ p += count1;
+ count -= count1;
+ read += count1;
+ }
+
+ if (count > 0 && p < PAGE_SIZE + FIRST_MAPPED) {
+ count1 = PAGE_SIZE + FIRST_MAPPED - p;
+ if (count1 > count)
+ count1 = count;
+ if (clear_user(buf, count1))
+ return -EFAULT;
+ buf += count1;
+ p += count1;
+ count -= count1;
+ read += count1;
+ }
+ if (count > 0) {
+ if (copy_to_user(buf, (void *) (PAGE_OFFSET+p-PAGE_SIZE), count))
+ return -EFAULT;
+ read += count;
+ p += count;
+ }
+ *ppos = p;
+ return read;
+}
+#else /* CONFIG_KCORE_AOUT */
+
+#define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
+
+/* An ELF note in memory */
+struct memelfnote
+{
+ const char *name;
+ int type;
+ unsigned int datasz;
+ void *data;
+};
+
+extern char saved_command_line[];
+
+static unsigned long get_kcore_size(int *num_vma, size_t *elf_buflen)
+{
+ unsigned long try, size;
+ struct vm_struct *m;
+
+ *num_vma = 0;
+ size = ((size_t)high_memory - PAGE_OFFSET + PAGE_SIZE);
+ if (!vmlist) {
+ *elf_buflen = PAGE_SIZE;
+ return (size);
+ }
+
+ for (m=vmlist; m; m=m->next) {
+ try = (unsigned long)m->addr + m->size;
+ if (try > size)
+ size = try;
+ *num_vma = *num_vma + 1;
+ }
+ *elf_buflen = sizeof(struct elfhdr) +
+ (*num_vma + 2)*sizeof(struct elf_phdr) +
+ 3 * (sizeof(struct elf_note) + 4) +
+ sizeof(struct elf_prstatus) +
+ sizeof(struct elf_prpsinfo) +
+ sizeof(struct task_struct);
+ *elf_buflen = PAGE_ALIGN(*elf_buflen);
+ return (size - PAGE_OFFSET + *elf_buflen);
+}
+
+
+/*****************************************************************************/
+/*
+ * determine size of ELF note
+ */
+static int notesize(struct memelfnote *en)
+{
+ int sz;
+
+ sz = sizeof(struct elf_note);
+ sz += roundup(strlen(en->name), 4);
+ sz += roundup(en->datasz, 4);
+
+ return sz;
+} /* end notesize() */
+
+/*****************************************************************************/
+/*
+ * store a note in the header buffer
+ */
+static char *storenote(struct memelfnote *men, char *bufp)
+{
+ struct elf_note en;
+
+#define DUMP_WRITE(addr,nr) do { memcpy(bufp,addr,nr); bufp += nr; } while(0)
+
+ en.n_namesz = strlen(men->name);
+ en.n_descsz = men->datasz;
+ en.n_type = men->type;
+
+ DUMP_WRITE(&en, sizeof(en));
+ DUMP_WRITE(men->name, en.n_namesz);
+
+ /* XXX - cast from long long to long to avoid need for libgcc.a */
+ bufp = (char*) roundup((unsigned long)bufp,4);
+ DUMP_WRITE(men->data, men->datasz);
+ bufp = (char*) roundup((unsigned long)bufp,4);
+
+#undef DUMP_WRITE
+
+ return bufp;
+} /* end storenote() */
+
+/*
+ * store an ELF coredump header in the supplied buffer
+ * num_vma is the number of elements in vmlist
+ */
+static void elf_kcore_store_hdr(char *bufp, int num_vma, int dataoff)
+{
+ struct elf_prstatus prstatus; /* NT_PRSTATUS */
+ struct elf_prpsinfo prpsinfo; /* NT_PRPSINFO */
+ struct elf_phdr *nhdr, *phdr;
+ struct elfhdr *elf;
+ struct memelfnote notes[3];
+ off_t offset = 0;
+ struct vm_struct *m;
+
+ /* setup ELF header */
+ elf = (struct elfhdr *) bufp;
+ bufp += sizeof(struct elfhdr);
+ offset += sizeof(struct elfhdr);
+ memcpy(elf->e_ident, ELFMAG, SELFMAG);
+ elf->e_ident[EI_CLASS] = ELF_CLASS;
+ elf->e_ident[EI_DATA] = ELF_DATA;
+ elf->e_ident[EI_VERSION]= EV_CURRENT;
+ memset(elf->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD);
+ elf->e_type = ET_CORE;
+ elf->e_machine = ELF_ARCH;
+ elf->e_version = EV_CURRENT;
+ elf->e_entry = 0;
+ elf->e_phoff = sizeof(struct elfhdr);
+ elf->e_shoff = 0;
+ elf->e_flags = 0;
+ elf->e_ehsize = sizeof(struct elfhdr);
+ elf->e_phentsize= sizeof(struct elf_phdr);
+ elf->e_phnum = 2 + num_vma;
+ elf->e_shentsize= 0;
+ elf->e_shnum = 0;
+ elf->e_shstrndx = 0;
+
+ /* setup ELF PT_NOTE program header */
+ nhdr = (struct elf_phdr *) bufp;
+ bufp += sizeof(struct elf_phdr);
+ offset += sizeof(struct elf_phdr);
+ nhdr->p_type = PT_NOTE;
+ nhdr->p_offset = 0;
+ nhdr->p_vaddr = 0;
+ nhdr->p_paddr = 0;
+ nhdr->p_filesz = 0;
+ nhdr->p_memsz = 0;
+ nhdr->p_flags = 0;
+ nhdr->p_align = 0;
+
+ /* setup ELF PT_LOAD program header for the
+ * virtual range 0xc0000000 -> high_memory */
+ phdr = (struct elf_phdr *) bufp;
+ bufp += sizeof(struct elf_phdr);
+ offset += sizeof(struct elf_phdr);
+ phdr->p_type = PT_LOAD;
+ phdr->p_flags = PF_R|PF_W|PF_X;
+ phdr->p_offset = dataoff;
+ phdr->p_vaddr = PAGE_OFFSET;
+ phdr->p_paddr = __pa(PAGE_OFFSET);
+ phdr->p_filesz = phdr->p_memsz = ((unsigned long)high_memory - PAGE_OFFSET);
+ phdr->p_align = PAGE_SIZE;
+
+ /* setup ELF PT_LOAD program header for every vmalloc'd area */
+ for (m=vmlist; m; m=m->next) {
+ if (m->flags & VM_IOREMAP) /* don't dump ioremap'd stuff! (TA) */
+ continue;
+
+ phdr = (struct elf_phdr *) bufp;
+ bufp += sizeof(struct elf_phdr);
+ offset += sizeof(struct elf_phdr);
+
+ phdr->p_type = PT_LOAD;
+ phdr->p_flags = PF_R|PF_W|PF_X;
+ phdr->p_offset = (size_t)m->addr - PAGE_OFFSET + dataoff;
+ phdr->p_vaddr = (size_t)m->addr;
+ phdr->p_paddr = __pa(m->addr);
+ phdr->p_filesz = phdr->p_memsz = m->size;
+ phdr->p_align = PAGE_SIZE;
+ }
+
+ /*
+ * Set up the notes in similar form to SVR4 core dumps made
+ * with info from their /proc.
+ */
+ nhdr->p_offset = offset;
+
+ /* set up the process status */
+ notes[0].name = "CORE";
+ notes[0].type = NT_PRSTATUS;
+ notes[0].datasz = sizeof(struct elf_prstatus);
+ notes[0].data = &prstatus;
+
+ memset(&prstatus, 0, sizeof(struct elf_prstatus));
+
+ nhdr->p_filesz += notesize(&notes[0]);
+ bufp = storenote(&notes[0], bufp);
+
+ /* set up the process info */
+ notes[1].name = "CORE";
+ notes[1].type = NT_PRPSINFO;
+ notes[1].datasz = sizeof(struct elf_prpsinfo);
+ notes[1].data = &prpsinfo;
+
+ memset(&prpsinfo, 0, sizeof(struct elf_prpsinfo));
+ prpsinfo.pr_state = 0;
+ prpsinfo.pr_sname = 'R';
+ prpsinfo.pr_zomb = 0;
+
+ strcpy(prpsinfo.pr_fname, "vmlinux");
+ strncpy(prpsinfo.pr_psargs, saved_command_line, ELF_PRARGSZ);
+
+ nhdr->p_filesz += notesize(&notes[1]);
+ bufp = storenote(&notes[1], bufp);
+
+ /* set up the task structure */
+ notes[2].name = "CORE";
+ notes[2].type = NT_TASKSTRUCT;
+ notes[2].datasz = sizeof(struct task_struct);
+ notes[2].data = current;
+
+ nhdr->p_filesz += notesize(&notes[2]);
+ bufp = storenote(&notes[2], bufp);
+
+} /* end elf_kcore_store_hdr() */
+
+/*****************************************************************************/
+/*
+ * read from the ELF header and then kernel memory
+ */
+static ssize_t read_kcore(struct file *file, char *buffer, size_t buflen, loff_t *fpos)
+{
+ ssize_t acc = 0;
+ unsigned long size, tsz;
+ size_t elf_buflen;
+ int num_vma;
+ unsigned long start;
+
+ read_lock(&vmlist_lock);
+ proc_root_kcore->size = size = get_kcore_size(&num_vma, &elf_buflen);
+ if (buflen == 0 || (unsigned long long)*fpos >= size) {
+ read_unlock(&vmlist_lock);
+ return 0;
+ }
+
+ /* trim buflen to not go beyond EOF */
+ if (buflen > size - *fpos)
+ buflen = size - *fpos;
+
+ /* construct an ELF core header if we'll need some of it */
+ if (*fpos < elf_buflen) {
+ char * elf_buf;
+
+ tsz = elf_buflen - *fpos;
+ if (buflen < tsz)
+ tsz = buflen;
+ elf_buf = kmalloc(elf_buflen, GFP_ATOMIC);
+ if (!elf_buf) {
+ read_unlock(&vmlist_lock);
+ return -ENOMEM;
+ }
+ memset(elf_buf, 0, elf_buflen);
+ elf_kcore_store_hdr(elf_buf, num_vma, elf_buflen);
+ read_unlock(&vmlist_lock);
+ if (copy_to_user(buffer, elf_buf + *fpos, tsz)) {
+ kfree(elf_buf);
+ return -EFAULT;
+ }
+ kfree(elf_buf);
+ buflen -= tsz;
+ *fpos += tsz;
+ buffer += tsz;
+ acc += tsz;
+
+ /* leave now if filled buffer already */
+ if (buflen == 0)
+ return acc;
+ } else
+ read_unlock(&vmlist_lock);
+
+ /* where page 0 not mapped, write zeros into buffer */
+#if defined (__i386__) || defined (__mc68000__) || defined(__x86_64__)
+ if (*fpos < PAGE_SIZE + elf_buflen) {
+ /* work out how much to clear */
+ tsz = PAGE_SIZE + elf_buflen - *fpos;
+ if (buflen < tsz)
+ tsz = buflen;
+
+ /* write zeros to buffer */
+ if (clear_user(buffer, tsz))
+ return -EFAULT;
+ buflen -= tsz;
+ *fpos += tsz;
+ buffer += tsz;
+ acc += tsz;
+
+ /* leave now if filled buffer already */
+ if (buflen == 0)
+ return tsz;
+ }
+#endif
+
+ /*
+ * Fill the remainder of the buffer from kernel VM space.
+ * We said in the ELF header that the data which starts
+ * at 'elf_buflen' is virtual address PAGE_OFFSET. --rmk
+ */
+ start = PAGE_OFFSET + (*fpos - elf_buflen);
+ if ((tsz = (PAGE_SIZE - (start & ~PAGE_MASK))) > buflen)
+ tsz = buflen;
+ while (buflen) {
+ int err;
+
+ if ((start > PAGE_OFFSET) && (start < (unsigned long)high_memory)) {
+ if (kern_addr_valid(start)) {
+ err = copy_to_user(buffer, (char *)start, tsz);
+ } else {
+ err = clear_user(buffer, tsz);
+ }
+ } else {
+ char * elf_buf;
+ struct vm_struct *m;
+ unsigned long curstart = start;
+ unsigned long cursize = tsz;
+
+ elf_buf = kmalloc(tsz, GFP_KERNEL);
+ if (!elf_buf)
+ return -ENOMEM;
+ memset(elf_buf, 0, tsz);
+
+ read_lock(&vmlist_lock);
+ for (m=vmlist; m && cursize; m=m->next) {
+ unsigned long vmstart;
+ unsigned long vmsize;
+ unsigned long msize = m->size - PAGE_SIZE;
+
+ if (((unsigned long)m->addr + msize) <
+ curstart)
+ continue;
+ if ((unsigned long)m->addr > (curstart +
+ cursize))
+ break;
+ vmstart = (curstart < (unsigned long)m->addr ?
+ (unsigned long)m->addr : curstart);
+ if (((unsigned long)m->addr + msize) >
+ (curstart + cursize))
+ vmsize = curstart + cursize - vmstart;
+ else
+ vmsize = (unsigned long)m->addr +
+ msize - vmstart;
+ curstart = vmstart + vmsize;
+ cursize -= vmsize;
+ /* don't dump ioremap'd stuff! (TA) */
+ if (m->flags & VM_IOREMAP)
+ continue;
+ memcpy(elf_buf + (vmstart - start),
+ (char *)vmstart, vmsize);
+ }
+ read_unlock(&vmlist_lock);
+ err = copy_to_user(buffer, elf_buf, tsz);
+ kfree(elf_buf);
+ }
+ if (err)
+ return -EFAULT;
+ buflen -= tsz;
+ *fpos += tsz;
+ buffer += tsz;
+ acc += tsz;
+ start += tsz;
+ tsz = (buflen > PAGE_SIZE ? PAGE_SIZE : buflen);
+ }
+
+ return acc;
+}
+#endif /* CONFIG_KCORE_AOUT */
+
+static loff_t lseek_kcore(struct file * file, loff_t offset, int origin)
+{
+ switch (origin) {
+ case 2:
+ offset += file->f_dentry->d_inode->i_size;
+ break;
+ case 1:
+ offset += file->f_pos;
+ }
+ /* RED-PEN user can fake an error here by setting offset to >=-4095 && <0 */
+ file->f_pos = offset;
+ return offset;
+}
diff --git a/fs/proc/kmsg.c b/fs/proc/kmsg.c
new file mode 100644
index 00000000000000..69aeddd3f1667c
--- /dev/null
+++ b/fs/proc/kmsg.c
@@ -0,0 +1,52 @@
+/*
+ * linux/fs/proc/kmsg.c
+ *
+ * Copyright (C) 1992 by Linus Torvalds
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/poll.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+extern wait_queue_head_t log_wait;
+
+extern int do_syslog(int type, char * bug, int count);
+
+static int kmsg_open(struct inode * inode, struct file * file)
+{
+ return do_syslog(1,NULL,0);
+}
+
+static int kmsg_release(struct inode * inode, struct file * file)
+{
+ (void) do_syslog(0,NULL,0);
+ return 0;
+}
+
+static ssize_t kmsg_read(struct file * file, char * buf,
+ size_t count, loff_t *ppos)
+{
+ return do_syslog(2,buf,count);
+}
+
+static unsigned int kmsg_poll(struct file *file, poll_table * wait)
+{
+ poll_wait(file, &log_wait, wait);
+ if (do_syslog(9, 0, 0))
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+
+
+struct file_operations proc_kmsg_operations = {
+ read: kmsg_read,
+ poll: kmsg_poll,
+ open: kmsg_open,
+ release: kmsg_release,
+};
diff --git a/fs/proc/proc_devtree.c b/fs/proc/proc_devtree.c
new file mode 100644
index 00000000000000..45f60fa6d44501
--- /dev/null
+++ b/fs/proc/proc_devtree.c
@@ -0,0 +1,142 @@
+/*
+ * proc_devtree.c - handles /proc/device-tree
+ *
+ * Copyright 1997 Paul Mackerras
+ */
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <asm/prom.h>
+#include <asm/uaccess.h>
+
+static struct proc_dir_entry *proc_device_tree;
+
+/*
+ * Supply data on a read from /proc/device-tree/node/property.
+ */
+static int property_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ struct property *pp = data;
+ int n;
+
+ if (off >= pp->length) {
+ *eof = 1;
+ return 0;
+ }
+ n = pp->length - off;
+ if (n > count)
+ n = count;
+ else
+ *eof = 1;
+ memcpy(page, pp->value + off, n);
+ *start = page;
+ return n;
+}
+
+/*
+ * For a node with a name like "gc@10", we make symlinks called "gc"
+ * and "@10" to it.
+ */
+
+/*
+ * Process a node, adding entries for its children and its properties.
+ */
+static void add_node(struct device_node *np, struct proc_dir_entry *de)
+{
+ struct property *pp;
+ struct proc_dir_entry *ent;
+ struct device_node *child, *sib;
+ const char *p, *at;
+ int l;
+ struct proc_dir_entry *list, **lastp, *al;
+
+ lastp = &list;
+ for (pp = np->properties; pp != 0; pp = pp->next) {
+ /*
+ * Unfortunately proc_register puts each new entry
+ * at the beginning of the list. So we rearrange them.
+ */
+ ent = create_proc_read_entry(pp->name, strncmp(pp->name, "security-", 9) ?
+ S_IRUGO : S_IRUSR, de, property_read_proc, pp);
+ if (ent == 0)
+ break;
+ if (!strncmp(pp->name, "security-", 9))
+ ent->size = 0; /* don't leak number of password chars */
+ else
+ ent->size = pp->length;
+ *lastp = ent;
+ lastp = &ent->next;
+ }
+ for (child = np->child; child != 0; child = child->sibling) {
+ p = strrchr(child->full_name, '/');
+ if (p == 0)
+ p = child->full_name;
+ else
+ ++p;
+ /* chop off '@0' if the name ends with that */
+ l = strlen(p);
+ if (l > 2 && p[l-2] == '@' && p[l-1] == '0')
+ l -= 2;
+ ent = proc_mkdir(p, de);
+ if (ent == 0)
+ break;
+ *lastp = ent;
+ lastp = &ent->next;
+ add_node(child, ent);
+
+ /*
+ * If we left the address part on the name, consider
+ * adding symlinks from the name and address parts.
+ */
+ if (p[l] != 0 || (at = strchr(p, '@')) == 0)
+ continue;
+
+ /*
+ * If this is the first node with a given name property,
+ * add a symlink with the name property as its name.
+ */
+ for (sib = np->child; sib != child; sib = sib->sibling)
+ if (sib->name && strcmp(sib->name, child->name) == 0)
+ break;
+ if (sib == child && strncmp(p, child->name, l) != 0) {
+ al = proc_symlink(child->name, de, ent->name);
+ if (al == 0)
+ break;
+ *lastp = al;
+ lastp = &al->next;
+ }
+
+ /*
+ * Add another directory with the @address part as its name.
+ */
+ al = proc_symlink(at, de, ent->name);
+ if (al == 0)
+ break;
+ *lastp = al;
+ lastp = &al->next;
+ }
+ *lastp = 0;
+ de->subdir = list;
+}
+
+/*
+ * Called on initialization to set up the /proc/device-tree subtree
+ */
+void proc_device_tree_init(void)
+{
+ struct device_node *root;
+ if ( !have_of )
+ return;
+ proc_device_tree = proc_mkdir("device-tree", 0);
+ if (proc_device_tree == 0)
+ return;
+ root = find_path_device("/");
+ if (root == 0) {
+ printk(KERN_ERR "/proc/device-tree: can't find root\n");
+ return;
+ }
+ add_node(root, proc_device_tree);
+}
diff --git a/fs/proc/proc_misc.c b/fs/proc/proc_misc.c
new file mode 100644
index 00000000000000..033ed623a0d1a5
--- /dev/null
+++ b/fs/proc/proc_misc.c
@@ -0,0 +1,673 @@
+/*
+ * linux/fs/proc/proc_misc.c
+ *
+ * linux/fs/proc/array.c
+ * Copyright (C) 1992 by Linus Torvalds
+ * based on ideas by Darren Senn
+ *
+ * This used to be the part of array.c. See the rest of history and credits
+ * there. I took this into a separate file and switched the thing to generic
+ * proc_file_inode_operations, leaving in array.c only per-process stuff.
+ * Inumbers allocation made dynamic (via create_proc_entry()). AV, May 1999.
+ *
+ * Changes:
+ * Fulton Green : Encapsulated position metric calculations.
+ * <kernel@FultonGreen.com>
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/kernel_stat.h>
+#include <linux/tty.h>
+#include <linux/string.h>
+#include <linux/mman.h>
+#include <linux/proc_fs.h>
+#include <linux/ioport.h>
+#include <linux/config.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/swap.h>
+#include <linux/slab.h>
+#include <linux/smp.h>
+#include <linux/signal.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+#include <linux/seq_file.h>
+#include <linux/sysrq.h>
+
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+#include <asm/io.h>
+
+#define LOAD_INT(x) ((x) >> FSHIFT)
+#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100)
+/*
+ * Warning: stuff below (imported functions) assumes that its output will fit
+ * into one page. For some of those functions it may be wrong. Moreover, we
+ * have a way to deal with that gracefully. Right now I used straightforward
+ * wrappers, but this needs further analysis wrt potential overflows.
+ */
+extern int get_hardware_list(char *);
+extern int get_stram_list(char *);
+#ifdef CONFIG_MODULES
+extern int get_module_list(char *);
+#endif
+extern int get_device_list(char *);
+extern int get_filesystem_list(char *);
+extern int get_exec_domain_list(char *);
+#ifndef CONFIG_X86
+extern int get_irq_list(char *);
+#endif
+extern int get_dma_list(char *);
+extern int get_locks_status (char *, char **, off_t, int);
+extern int get_swaparea_info (char *);
+#ifdef CONFIG_SGI_DS1286
+extern int get_ds1286_status(char *);
+#endif
+
+void proc_sprintf(char *page, off_t *off, int *lenp, const char *format, ...)
+{
+ int len = *lenp;
+ va_list args;
+
+ /* try to only print whole lines */
+ if (len > PAGE_SIZE-512)
+ return;
+
+ va_start(args, format);
+ len += vsnprintf(page + len, PAGE_SIZE-len, format, args);
+ va_end(args);
+
+ if (len <= *off) {
+ *off -= len;
+ len = 0;
+ }
+
+ *lenp = len;
+}
+
+static int proc_calc_metrics(char *page, char **start, off_t off,
+ int count, int *eof, int len)
+{
+ if (len <= off+count) *eof = 1;
+ *start = page + off;
+ len -= off;
+ if (len>count) len = count;
+ if (len<0) len = 0;
+ return len;
+}
+
+static int loadavg_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int a, b, c;
+ int len;
+
+ a = avenrun[0] + (FIXED_1/200);
+ b = avenrun[1] + (FIXED_1/200);
+ c = avenrun[2] + (FIXED_1/200);
+ len = sprintf(page,"%d.%02d %d.%02d %d.%02d %d/%d %d\n",
+ LOAD_INT(a), LOAD_FRAC(a),
+ LOAD_INT(b), LOAD_FRAC(b),
+ LOAD_INT(c), LOAD_FRAC(c),
+ nr_running, nr_threads, last_pid);
+ return proc_calc_metrics(page, start, off, count, eof, len);
+}
+
+static int uptime_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ unsigned long uptime;
+ unsigned long idle;
+ int len;
+
+ uptime = jiffies;
+ idle = init_tasks[0]->times.tms_utime + init_tasks[0]->times.tms_stime;
+
+ /* The formula for the fraction parts really is ((t * 100) / HZ) % 100, but
+ that would overflow about every five days at HZ == 100.
+ Therefore the identity a = (a / b) * b + a % b is used so that it is
+ calculated as (((t / HZ) * 100) + ((t % HZ) * 100) / HZ) % 100.
+ The part in front of the '+' always evaluates as 0 (mod 100). All divisions
+ in the above formulas are truncating. For HZ being a power of 10, the
+ calculations simplify to the version in the #else part (if the printf
+ format is adapted to the same number of digits as zeroes in HZ.
+ */
+#if HZ!=100
+ len = sprintf(page,"%lu.%02lu %lu.%02lu\n",
+ uptime / HZ,
+ (((uptime % HZ) * 100) / HZ) % 100,
+ idle / HZ,
+ (((idle % HZ) * 100) / HZ) % 100);
+#else
+ len = sprintf(page,"%lu.%02lu %lu.%02lu\n",
+ uptime / HZ,
+ uptime % HZ,
+ idle / HZ,
+ idle % HZ);
+#endif
+ return proc_calc_metrics(page, start, off, count, eof, len);
+}
+
+static int meminfo_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ struct sysinfo i;
+ int len;
+ int pg_size ;
+
+/*
+ * display in kilobytes.
+ */
+#define K(x) ((x) << (PAGE_SHIFT - 10))
+#define B(x) ((unsigned long long)(x) << PAGE_SHIFT)
+ si_meminfo(&i);
+ si_swapinfo(&i);
+ pg_size = page_cache_size - i.bufferram;
+
+ len = sprintf(page, " total: used: free: shared: buffers: cached:\n"
+ "Mem: %8Lu %8Lu %8Lu %8Lu %8Lu %8Lu\n"
+ "Swap: %8Lu %8Lu %8Lu\n",
+ B(i.totalram), B(i.totalram-i.freeram), B(i.freeram),
+ B(i.sharedram), B(i.bufferram),
+ B(pg_size), B(i.totalswap),
+ B(i.totalswap-i.freeswap), B(i.freeswap));
+ /*
+ * Tagged format, for easy grepping and expansion.
+ * The above will go away eventually, once the tools
+ * have been updated.
+ */
+ len += sprintf(page+len,
+ "MemTotal: %8lu kB\n"
+ "MemFree: %8lu kB\n"
+ "MemShared: %8lu kB\n"
+ "Buffers: %8lu kB\n"
+ "Cached: %8lu kB\n"
+ "SwapCached: %8lu kB\n"
+ "Active: %8u kB\n"
+ "Inactive: %8u kB\n"
+ "HighTotal: %8lu kB\n"
+ "HighFree: %8lu kB\n"
+ "LowTotal: %8lu kB\n"
+ "LowFree: %8lu kB\n"
+ "SwapTotal: %8lu kB\n"
+ "SwapFree: %8lu kB\n",
+ K(i.totalram),
+ K(i.freeram),
+ K(i.sharedram),
+ K(i.bufferram),
+ K(pg_size - swapper_space.nrpages),
+ K(swapper_space.nrpages),
+ K(nr_active_pages),
+ K(nr_inactive_pages),
+ K(i.totalhigh),
+ K(i.freehigh),
+ K(i.totalram-i.totalhigh),
+ K(i.freeram-i.freehigh),
+ K(i.totalswap),
+ K(i.freeswap));
+
+ return proc_calc_metrics(page, start, off, count, eof, len);
+#undef B
+#undef K
+}
+
+static int version_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ extern char *linux_banner;
+ int len;
+
+ strcpy(page, linux_banner);
+ len = strlen(page);
+ return proc_calc_metrics(page, start, off, count, eof, len);
+}
+
+extern struct seq_operations cpuinfo_op;
+static int cpuinfo_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &cpuinfo_op);
+}
+static struct file_operations proc_cpuinfo_operations = {
+ open: cpuinfo_open,
+ read: seq_read,
+ llseek: seq_lseek,
+ release: seq_release,
+};
+
+#ifdef CONFIG_PROC_HARDWARE
+static int hardware_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = get_hardware_list(page);
+ return proc_calc_metrics(page, start, off, count, eof, len);
+}
+#endif
+
+#ifdef CONFIG_STRAM_PROC
+static int stram_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = get_stram_list(page);
+ return proc_calc_metrics(page, start, off, count, eof, len);
+}
+#endif
+
+extern struct seq_operations partitions_op;
+static int partitions_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &partitions_op);
+}
+static struct file_operations proc_partitions_operations = {
+ open: partitions_open,
+ read: seq_read,
+ llseek: seq_lseek,
+ release: seq_release,
+};
+
+#ifdef CONFIG_MODULES
+static int modules_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = get_module_list(page);
+ return proc_calc_metrics(page, start, off, count, eof, len);
+}
+
+extern struct seq_operations ksyms_op;
+static int ksyms_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &ksyms_op);
+}
+static struct file_operations proc_ksyms_operations = {
+ open: ksyms_open,
+ read: seq_read,
+ llseek: seq_lseek,
+ release: seq_release,
+};
+#endif
+
+extern struct seq_operations slabinfo_op;
+extern ssize_t slabinfo_write(struct file *, const char *, size_t, loff_t *);
+static int slabinfo_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &slabinfo_op);
+}
+static struct file_operations proc_slabinfo_operations = {
+ open: slabinfo_open,
+ read: seq_read,
+ write: slabinfo_write,
+ llseek: seq_lseek,
+ release: seq_release,
+};
+
+static int kstat_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int i, len = 0;
+ extern unsigned long total_forks;
+ unsigned long jif = jiffies;
+ unsigned int sum = 0, user = 0, nice = 0, system = 0;
+ int major, disk;
+
+ for (i = 0 ; i < smp_num_cpus; i++) {
+ int cpu = cpu_logical_map(i), j;
+
+ user += kstat.per_cpu_user[cpu];
+ nice += kstat.per_cpu_nice[cpu];
+ system += kstat.per_cpu_system[cpu];
+#if !defined(CONFIG_ARCH_S390)
+ for (j = 0 ; j < NR_IRQS ; j++)
+ sum += kstat.irqs[cpu][j];
+#endif
+ }
+
+ proc_sprintf(page, &off, &len,
+ "cpu %u %u %u %lu\n", user, nice, system,
+ jif * smp_num_cpus - (user + nice + system));
+ for (i = 0 ; i < smp_num_cpus; i++)
+ proc_sprintf(page, &off, &len,
+ "cpu%d %u %u %u %lu\n",
+ i,
+ kstat.per_cpu_user[cpu_logical_map(i)],
+ kstat.per_cpu_nice[cpu_logical_map(i)],
+ kstat.per_cpu_system[cpu_logical_map(i)],
+ jif - ( kstat.per_cpu_user[cpu_logical_map(i)] \
+ + kstat.per_cpu_nice[cpu_logical_map(i)] \
+ + kstat.per_cpu_system[cpu_logical_map(i)]));
+ proc_sprintf(page, &off, &len,
+ "page %u %u\n"
+ "swap %u %u\n"
+ "intr %u",
+ kstat.pgpgin >> 1,
+ kstat.pgpgout >> 1,
+ kstat.pswpin,
+ kstat.pswpout,
+ sum
+ );
+#if !defined(CONFIG_ARCH_S390) && !defined(CONFIG_ALPHA)
+ for (i = 0 ; i < NR_IRQS ; i++)
+ proc_sprintf(page, &off, &len,
+ " %u", kstat_irqs(i));
+#endif
+
+ proc_sprintf(page, &off, &len, "\ndisk_io: ");
+
+ for (major = 0; major < DK_MAX_MAJOR; major++) {
+ for (disk = 0; disk < DK_MAX_DISK; disk++) {
+ int active = kstat.dk_drive[major][disk] +
+ kstat.dk_drive_rblk[major][disk] +
+ kstat.dk_drive_wblk[major][disk];
+ if (active)
+ proc_sprintf(page, &off, &len,
+ "(%u,%u):(%u,%u,%u,%u,%u) ",
+ major, disk,
+ kstat.dk_drive[major][disk],
+ kstat.dk_drive_rio[major][disk],
+ kstat.dk_drive_rblk[major][disk],
+ kstat.dk_drive_wio[major][disk],
+ kstat.dk_drive_wblk[major][disk]
+ );
+ }
+ }
+
+ proc_sprintf(page, &off, &len,
+ "\nctxt %u\n"
+ "btime %lu\n"
+ "processes %lu\n",
+ kstat.context_swtch,
+ xtime.tv_sec - jif / HZ,
+ total_forks);
+
+ return proc_calc_metrics(page, start, off, count, eof, len);
+}
+
+static int devices_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = get_device_list(page);
+ return proc_calc_metrics(page, start, off, count, eof, len);
+}
+
+#ifndef CONFIG_X86
+#if !defined(CONFIG_ARCH_S390)
+static int interrupts_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = get_irq_list(page);
+ return proc_calc_metrics(page, start, off, count, eof, len);
+}
+#endif
+
+#else /* !CONFIG_X86 */
+
+extern int show_interrupts(struct seq_file *p, void *v);
+static int interrupts_open(struct inode *inode, struct file *file)
+{
+ unsigned size = PAGE_SIZE * (1 + smp_num_cpus / 8);
+ char *buf = kmalloc(size, GFP_KERNEL);
+ struct seq_file *m;
+ int res;
+
+ if (!buf)
+ return -ENOMEM;
+ res = single_open(file, show_interrupts, NULL);
+ if (!res) {
+ m = file->private_data;
+ m->buf = buf;
+ m->size = size;
+ } else
+ kfree(buf);
+ return res;
+}
+static struct file_operations proc_interrupts_operations = {
+ .open = interrupts_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+#endif /* !CONFIG_X86 */
+
+extern struct file_operations proc_ioports_operations;
+extern struct file_operations proc_iomem_operations;
+
+static int filesystems_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = get_filesystem_list(page);
+ return proc_calc_metrics(page, start, off, count, eof, len);
+}
+
+static int dma_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = get_dma_list(page);
+ return proc_calc_metrics(page, start, off, count, eof, len);
+}
+
+static int cmdline_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ extern char saved_command_line[];
+ int len = 0;
+
+ proc_sprintf(page, &off, &len, "%s\n", saved_command_line);
+ return proc_calc_metrics(page, start, off, count, eof, len);
+}
+
+#ifdef CONFIG_SGI_DS1286
+static int ds1286_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = get_ds1286_status(page);
+ return proc_calc_metrics(page, start, off, count, eof, len);
+}
+#endif
+
+static int locks_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len;
+ lock_kernel();
+ len = get_locks_status(page, start, off, count);
+ unlock_kernel();
+ if (len < count) *eof = 1;
+ return len;
+}
+
+static int execdomains_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = get_exec_domain_list(page);
+ return proc_calc_metrics(page, start, off, count, eof, len);
+}
+
+static int swaps_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = get_swaparea_info(page);
+ return proc_calc_metrics(page, start, off, count, eof, len);
+}
+
+/*
+ * This function accesses profiling information. The returned data is
+ * binary: the sampling step and the actual contents of the profile
+ * buffer. Use of the program readprofile is recommended in order to
+ * get meaningful info out of these data.
+ */
+static ssize_t read_profile(struct file *file, char *buf,
+ size_t count, loff_t *ppos)
+{
+ loff_t n = *ppos;
+ unsigned p = n;
+ ssize_t read;
+ char * pnt;
+ unsigned int sample_step = 1 << prof_shift;
+
+ if (p != n || p >= (prof_len+1)*sizeof(unsigned int))
+ return 0;
+ if (count > (prof_len+1)*sizeof(unsigned int) - p)
+ count = (prof_len+1)*sizeof(unsigned int) - p;
+ read = 0;
+
+ while (p < sizeof(unsigned int) && count > 0) {
+ put_user(*((char *)(&sample_step)+p),buf);
+ buf++; p++; count--; read++;
+ }
+ pnt = (char *)prof_buffer + p - sizeof(unsigned int);
+ if (copy_to_user(buf,(void *)pnt,count))
+ return -EFAULT;
+ read += count;
+ *ppos = n + read;
+ return read;
+}
+
+/*
+ * Writing to /proc/profile resets the counters
+ *
+ * Writing a 'profiling multiplier' value into it also re-sets the profiling
+ * interrupt frequency, on architectures that support this.
+ */
+static ssize_t write_profile(struct file * file, const char * buf,
+ size_t count, loff_t *ppos)
+{
+#ifdef CONFIG_SMP
+ extern int setup_profiling_timer (unsigned int multiplier);
+
+ if (count==sizeof(int)) {
+ unsigned int multiplier;
+
+ if (copy_from_user(&multiplier, buf, sizeof(int)))
+ return -EFAULT;
+
+ if (setup_profiling_timer(multiplier))
+ return -EINVAL;
+ }
+#endif
+
+ memset(prof_buffer, 0, prof_len * sizeof(*prof_buffer));
+ return count;
+}
+
+static struct file_operations proc_profile_operations = {
+ read: read_profile,
+ write: write_profile,
+};
+
+#ifdef CONFIG_MAGIC_SYSRQ
+/*
+ * writing 'C' to /proc/sysrq-trigger is like sysrq-C
+ */
+static ssize_t write_sysrq_trigger(struct file *file, const char *buf,
+ size_t count, loff_t *ppos)
+{
+ if (count) {
+ char c;
+
+ if (get_user(c, buf))
+ return -EFAULT;
+ handle_sysrq(c, NULL, NULL, NULL);
+ }
+ return count;
+}
+
+static struct file_operations proc_sysrq_trigger_operations = {
+ .write = write_sysrq_trigger,
+};
+#endif
+
+struct proc_dir_entry *proc_root_kcore;
+
+static void create_seq_entry(char *name, mode_t mode, struct file_operations *f)
+{
+ struct proc_dir_entry *entry;
+ entry = create_proc_entry(name, mode, NULL);
+ if (entry)
+ entry->proc_fops = f;
+}
+
+void __init proc_misc_init(void)
+{
+ struct proc_dir_entry *entry;
+ static struct {
+ char *name;
+ int (*read_proc)(char*,char**,off_t,int,int*,void*);
+ } *p, simple_ones[] = {
+ {"loadavg", loadavg_read_proc},
+ {"uptime", uptime_read_proc},
+ {"meminfo", meminfo_read_proc},
+ {"version", version_read_proc},
+#ifdef CONFIG_PROC_HARDWARE
+ {"hardware", hardware_read_proc},
+#endif
+#ifdef CONFIG_STRAM_PROC
+ {"stram", stram_read_proc},
+#endif
+#ifdef CONFIG_MODULES
+ {"modules", modules_read_proc},
+#endif
+ {"stat", kstat_read_proc},
+ {"devices", devices_read_proc},
+#if !defined(CONFIG_ARCH_S390) && !defined(CONFIG_X86)
+ {"interrupts", interrupts_read_proc},
+#endif
+ {"filesystems", filesystems_read_proc},
+ {"dma", dma_read_proc},
+ {"cmdline", cmdline_read_proc},
+#ifdef CONFIG_SGI_DS1286
+ {"rtc", ds1286_read_proc},
+#endif
+ {"locks", locks_read_proc},
+ {"swaps", swaps_read_proc},
+ {"execdomains", execdomains_read_proc},
+ {NULL,}
+ };
+ for (p = simple_ones; p->name; p++)
+ create_proc_read_entry(p->name, 0, NULL, p->read_proc, NULL);
+
+ proc_symlink("mounts", NULL, "self/mounts");
+
+ /* And now for trickier ones */
+ entry = create_proc_entry("kmsg", S_IRUSR, &proc_root);
+ if (entry)
+ entry->proc_fops = &proc_kmsg_operations;
+ create_seq_entry("cpuinfo", 0, &proc_cpuinfo_operations);
+#if defined(CONFIG_X86)
+ create_seq_entry("interrupts", 0, &proc_interrupts_operations);
+#endif
+ create_seq_entry("ioports", 0, &proc_ioports_operations);
+ create_seq_entry("iomem", 0, &proc_iomem_operations);
+ create_seq_entry("partitions", 0, &proc_partitions_operations);
+ create_seq_entry("slabinfo",S_IWUSR|S_IRUGO,&proc_slabinfo_operations);
+#ifdef CONFIG_MODULES
+ create_seq_entry("ksyms", 0, &proc_ksyms_operations);
+#endif
+ proc_root_kcore = create_proc_entry("kcore", S_IRUSR, NULL);
+ if (proc_root_kcore) {
+ proc_root_kcore->proc_fops = &proc_kcore_operations;
+ proc_root_kcore->size =
+ (size_t)high_memory - PAGE_OFFSET + PAGE_SIZE;
+ }
+ if (prof_shift) {
+ entry = create_proc_entry("profile", S_IWUSR | S_IRUGO, NULL);
+ if (entry) {
+ entry->proc_fops = &proc_profile_operations;
+ entry->size = (1+prof_len) * sizeof(unsigned int);
+ }
+ }
+#ifdef CONFIG_MAGIC_SYSRQ
+ entry = create_proc_entry("sysrq-trigger", S_IWUSR, NULL);
+ if (entry)
+ entry->proc_fops = &proc_sysrq_trigger_operations;
+#endif
+#ifdef CONFIG_PPC32
+ {
+ extern struct file_operations ppc_htab_operations;
+ entry = create_proc_entry("ppc_htab", S_IRUGO|S_IWUSR, NULL);
+ if (entry)
+ entry->proc_fops = &ppc_htab_operations;
+ }
+#endif
+}
diff --git a/fs/proc/proc_tty.c b/fs/proc/proc_tty.c
new file mode 100644
index 00000000000000..efebc640198a59
--- /dev/null
+++ b/fs/proc/proc_tty.c
@@ -0,0 +1,188 @@
+/*
+ * proc_tty.c -- handles /proc/tty
+ *
+ * Copyright 1997, Theodore Ts'o
+ */
+
+#include <asm/uaccess.h>
+
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/tty.h>
+#include <asm/bitops.h>
+
+extern struct tty_driver *tty_drivers; /* linked list of tty drivers */
+
+static int tty_drivers_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data);
+static int tty_ldiscs_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data);
+
+/*
+ * The /proc/tty directory inodes...
+ */
+static struct proc_dir_entry *proc_tty_ldisc, *proc_tty_driver;
+
+/*
+ * This is the handler for /proc/tty/drivers
+ */
+static int tty_drivers_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+ off_t begin = 0;
+ struct tty_driver *p;
+ char range[20], deftype[20];
+ char *type;
+
+ for (p = tty_drivers; p; p = p->next) {
+ if (p->num > 1)
+ sprintf(range, "%d-%d", p->minor_start,
+ p->minor_start + p->num - 1);
+ else
+ sprintf(range, "%d", p->minor_start);
+ switch (p->type) {
+ case TTY_DRIVER_TYPE_SYSTEM:
+ if (p->subtype == SYSTEM_TYPE_TTY)
+ type = "system:/dev/tty";
+ else if (p->subtype == SYSTEM_TYPE_SYSCONS)
+ type = "system:console";
+ else if (p->subtype == SYSTEM_TYPE_CONSOLE)
+ type = "system:vtmaster";
+ else
+ type = "system";
+ break;
+ case TTY_DRIVER_TYPE_CONSOLE:
+ type = "console";
+ break;
+ case TTY_DRIVER_TYPE_SERIAL:
+ if (p->subtype == 2)
+ type = "serial:callout";
+ else
+ type = "serial";
+ break;
+ case TTY_DRIVER_TYPE_PTY:
+ if (p->subtype == PTY_TYPE_MASTER)
+ type = "pty:master";
+ else if (p->subtype == PTY_TYPE_SLAVE)
+ type = "pty:slave";
+ else
+ type = "pty";
+ break;
+ default:
+ sprintf(deftype, "type:%d.%d", p->type, p->subtype);
+ type = deftype;
+ break;
+ }
+ len += sprintf(page+len, "%-20s /dev/%-8s %3d %7s %s\n",
+ p->driver_name ? p->driver_name : "unknown",
+ p->name, p->major, range, type);
+ if (len+begin > off+count)
+ break;
+ if (len+begin < off) {
+ begin += len;
+ len = 0;
+ }
+ }
+ if (!p)
+ *eof = 1;
+ if (off >= len+begin)
+ return 0;
+ *start = page + (off-begin);
+ return ((count < begin+len-off) ? count : begin+len-off);
+}
+
+/*
+ * This is the handler for /proc/tty/ldiscs
+ */
+static int tty_ldiscs_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int i;
+ int len = 0;
+ off_t begin = 0;
+ struct tty_ldisc *ld;
+
+ for (i=0; i < NR_LDISCS; i++) {
+ ld = tty_ldisc_get(i);
+ if (ld == NULL)
+ continue;
+ len += sprintf(page+len, "%-10s %2d\n",
+ ld->name ? ld->name : "???", i);
+ tty_ldisc_put(i);
+ if (len+begin > off+count)
+ break;
+ if (len+begin < off) {
+ begin += len;
+ len = 0;
+ }
+ }
+ if (i >= NR_LDISCS)
+ *eof = 1;
+ if (off >= len+begin)
+ return 0;
+ *start = page + (off-begin);
+ return ((count < begin+len-off) ? count : begin+len-off);
+}
+
+/*
+ * This function is called by tty_register_driver() to handle
+ * registering the driver's /proc handler into /proc/tty/driver/<foo>
+ */
+void proc_tty_register_driver(struct tty_driver *driver)
+{
+ struct proc_dir_entry *ent;
+
+ if ((!driver->read_proc && !driver->write_proc) ||
+ !driver->driver_name ||
+ driver->proc_entry)
+ return;
+
+ ent = create_proc_entry(driver->driver_name, 0, proc_tty_driver);
+ if (!ent)
+ return;
+ ent->read_proc = driver->read_proc;
+ ent->write_proc = driver->write_proc;
+ ent->data = driver;
+
+ driver->proc_entry = ent;
+}
+
+/*
+ * This function is called by tty_unregister_driver()
+ */
+void proc_tty_unregister_driver(struct tty_driver *driver)
+{
+ struct proc_dir_entry *ent;
+
+ ent = driver->proc_entry;
+ if (!ent)
+ return;
+
+ remove_proc_entry(driver->driver_name, proc_tty_driver);
+
+ driver->proc_entry = 0;
+}
+
+/*
+ * Called by proc_root_init() to initialize the /proc/tty subtree
+ */
+void __init proc_tty_init(void)
+{
+ if (!proc_mkdir("tty", 0))
+ return;
+ proc_tty_ldisc = proc_mkdir("tty/ldisc", 0);
+ /*
+ * /proc/tty/driver/serial reveals the exact character counts for
+ * serial links which is just too easy to abuse for inferring
+ * password lengths and inter-keystroke timings during password
+ * entry.
+ */
+ proc_tty_driver = proc_mkdir_mode("tty/driver", S_IRUSR | S_IXUSR, 0);
+
+ create_proc_read_entry("tty/ldiscs", 0, 0, tty_ldiscs_read_proc,NULL);
+ create_proc_read_entry("tty/drivers", 0, 0, tty_drivers_read_proc,NULL);
+}
diff --git a/fs/proc/root.c b/fs/proc/root.c
new file mode 100644
index 00000000000000..92260bdcb7fc10
--- /dev/null
+++ b/fs/proc/root.c
@@ -0,0 +1,150 @@
+/*
+ * linux/fs/proc/root.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * proc root directory handling functions
+ */
+
+#include <asm/uaccess.h>
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <asm/bitops.h>
+
+struct proc_dir_entry *proc_net, *proc_net_stat, *proc_bus, *proc_root_fs, *proc_root_driver;
+
+#ifdef CONFIG_SYSCTL
+struct proc_dir_entry *proc_sys_root;
+#endif
+
+static DECLARE_FSTYPE(proc_fs_type, "proc", proc_read_super, FS_SINGLE);
+
+void __init proc_root_init(void)
+{
+ int err = register_filesystem(&proc_fs_type);
+ if (err)
+ return;
+ proc_mnt = kern_mount(&proc_fs_type);
+ err = PTR_ERR(proc_mnt);
+ if (IS_ERR(proc_mnt)) {
+ unregister_filesystem(&proc_fs_type);
+ return;
+ }
+ proc_misc_init();
+ proc_net = proc_mkdir("net", 0);
+ proc_net_stat = proc_mkdir("net/stat", NULL);
+
+#ifdef CONFIG_SYSVIPC
+ proc_mkdir("sysvipc", 0);
+#endif
+#ifdef CONFIG_SYSCTL
+ proc_sys_root = proc_mkdir("sys", 0);
+#endif
+#if defined(CONFIG_BINFMT_MISC) || defined(CONFIG_BINFMT_MISC_MODULE)
+ proc_mkdir("sys/fs", 0);
+ proc_mkdir("sys/fs/binfmt_misc", 0);
+#endif
+ proc_root_fs = proc_mkdir("fs", 0);
+ proc_root_driver = proc_mkdir("driver", 0);
+#if defined(CONFIG_SUN_OPENPROMFS) || defined(CONFIG_SUN_OPENPROMFS_MODULE)
+ /* just give it a mountpoint */
+ proc_mkdir("openprom", 0);
+#endif
+ proc_tty_init();
+#ifdef CONFIG_PROC_DEVICETREE
+ proc_device_tree_init();
+#endif
+#ifdef CONFIG_PPC_ISERIES
+ iSeries_proc_create();
+#endif
+#ifdef CONFIG_PPC64
+ proc_ppc64_init();
+#endif
+#ifdef CONFIG_PPC_RTAS
+ proc_rtas_init();
+#endif
+ proc_bus = proc_mkdir("bus", 0);
+}
+
+static struct dentry *proc_root_lookup(struct inode * dir, struct dentry * dentry)
+{
+ if (dir->i_ino == PROC_ROOT_INO) { /* check for safety... */
+ int nlink = proc_root.nlink;
+
+ nlink += nr_threads;
+
+ dir->i_nlink = nlink;
+ }
+
+ if (!proc_lookup(dir, dentry))
+ return NULL;
+
+ return proc_pid_lookup(dir, dentry);
+}
+
+static int proc_root_readdir(struct file * filp,
+ void * dirent, filldir_t filldir)
+{
+ unsigned int nr = filp->f_pos;
+
+ if (nr < FIRST_PROCESS_ENTRY) {
+ int error = proc_readdir(filp, dirent, filldir);
+ if (error <= 0)
+ return error;
+ filp->f_pos = FIRST_PROCESS_ENTRY;
+ }
+
+ return proc_pid_readdir(filp, dirent, filldir);
+}
+
+/*
+ * The root /proc directory is special, as it has the
+ * <pid> directories. Thus we don't use the generic
+ * directory handling functions for that..
+ */
+static struct file_operations proc_root_operations = {
+ read: generic_read_dir,
+ readdir: proc_root_readdir,
+};
+
+/*
+ * proc root can do almost nothing..
+ */
+static struct inode_operations proc_root_inode_operations = {
+ lookup: proc_root_lookup,
+};
+
+/*
+ * This is the root "inode" in the /proc tree..
+ */
+struct proc_dir_entry proc_root = {
+ low_ino: PROC_ROOT_INO,
+ namelen: 5,
+ name: "/proc",
+ mode: S_IFDIR | S_IRUGO | S_IXUGO,
+ nlink: 2,
+ proc_iops: &proc_root_inode_operations,
+ proc_fops: &proc_root_operations,
+ parent: &proc_root,
+};
+
+#ifdef CONFIG_SYSCTL
+EXPORT_SYMBOL(proc_sys_root);
+#endif
+EXPORT_SYMBOL(proc_symlink);
+EXPORT_SYMBOL(proc_mknod);
+EXPORT_SYMBOL(proc_mkdir);
+EXPORT_SYMBOL(create_proc_entry);
+EXPORT_SYMBOL(remove_proc_entry);
+EXPORT_SYMBOL(proc_root);
+EXPORT_SYMBOL(proc_root_fs);
+EXPORT_SYMBOL(proc_net);
+EXPORT_SYMBOL(proc_net_stat);
+EXPORT_SYMBOL(proc_bus);
+EXPORT_SYMBOL(proc_root_driver);
diff --git a/fs/qnx4/Makefile b/fs/qnx4/Makefile
new file mode 100644
index 00000000000000..a3a3c5a7da14de
--- /dev/null
+++ b/fs/qnx4/Makefile
@@ -0,0 +1,15 @@
+#
+# Makefile for the linux qnx4-filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+O_TARGET := qnx4.o
+
+obj-y := inode.o dir.o namei.o file.o bitmap.o truncate.o fsync.o
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/qnx4/README b/fs/qnx4/README
new file mode 100644
index 00000000000000..1f1e320d91da8f
--- /dev/null
+++ b/fs/qnx4/README
@@ -0,0 +1,9 @@
+
+ This is a snapshot of the QNX4 filesystem for Linux.
+ Please send diffs and remarks to <al@alarsen.net> .
+
+Credits :
+
+Richard "Scuba" A. Frowijn <scuba@wxs.nl>
+Frank "Jedi/Sector One" Denis <j@pureftpd.org>
+Anders Larsen <al@alarsen.net> (Maintainer)
diff --git a/fs/qnx4/bitmap.c b/fs/qnx4/bitmap.c
new file mode 100644
index 00000000000000..f544a436c822f3
--- /dev/null
+++ b/fs/qnx4/bitmap.c
@@ -0,0 +1,164 @@
+/*
+ * QNX4 file system, Linux implementation.
+ *
+ * Version : 0.2.1
+ *
+ * Using parts of the xiafs filesystem.
+ *
+ * History :
+ *
+ * 28-05-1998 by Richard Frowijn : first release.
+ * 20-06-1998 by Frank Denis : basic optimisations.
+ * 25-06-1998 by Frank Denis : qnx4_is_free, qnx4_set_bitmap, qnx4_bmap .
+ * 28-06-1998 by Frank Denis : qnx4_free_inode (to be fixed) .
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/qnx4_fs.h>
+#include <linux/stat.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+#include <asm/bitops.h>
+
+int qnx4_new_block(struct super_block *sb)
+{
+ return 0;
+}
+
+void count_bits(const register char *bmPart, register int size,
+ int *const tf)
+{
+ char b;
+ int tot = *tf;
+
+ if (size > QNX4_BLOCK_SIZE) {
+ size = QNX4_BLOCK_SIZE;
+ }
+ do {
+ b = *bmPart++;
+ if ((b & 1) == 0)
+ tot++;
+ if ((b & 2) == 0)
+ tot++;
+ if ((b & 4) == 0)
+ tot++;
+ if ((b & 8) == 0)
+ tot++;
+ if ((b & 16) == 0)
+ tot++;
+ if ((b & 32) == 0)
+ tot++;
+ if ((b & 64) == 0)
+ tot++;
+ if ((b & 128) == 0)
+ tot++;
+ size--;
+ } while (size != 0);
+ *tf = tot;
+}
+
+unsigned long qnx4_count_free_blocks(struct super_block *sb)
+{
+ int start = le32_to_cpu(sb->u.qnx4_sb.BitMap->di_first_xtnt.xtnt_blk) - 1;
+ int total = 0;
+ int total_free = 0;
+ int offset = 0;
+ int size = le32_to_cpu(sb->u.qnx4_sb.BitMap->di_size);
+ struct buffer_head *bh;
+
+ while (total < size) {
+ if ((bh = sb_bread(sb, start + offset)) == NULL) {
+ printk("qnx4: I/O error in counting free blocks\n");
+ break;
+ }
+ count_bits(bh->b_data, size - total, &total_free);
+ brelse(bh);
+ total += QNX4_BLOCK_SIZE;
+ offset++;
+ }
+
+ return total_free;
+}
+
+#ifdef CONFIG_QNX4FS_RW
+
+int qnx4_is_free(struct super_block *sb, long block)
+{
+ int start = le32_to_cpu(sb->u.qnx4_sb.BitMap->di_first_xtnt.xtnt_blk) - 1;
+ int size = le32_to_cpu(sb->u.qnx4_sb.BitMap->di_size);
+ struct buffer_head *bh;
+ const char *g;
+ int ret = -EIO;
+
+ start += block / (QNX4_BLOCK_SIZE * 8);
+ QNX4DEBUG(("qnx4: is_free requesting block [%lu], bitmap in block [%lu]\n",
+ (unsigned long) block, (unsigned long) start));
+ (void) size; /* CHECKME */
+ bh = sb_bread(sb, start);
+ if (bh == NULL) {
+ return -EIO;
+ }
+ g = bh->b_data + (block % QNX4_BLOCK_SIZE);
+ if (((*g) & (1 << (block % 8))) == 0) {
+ QNX4DEBUG(("qnx4: is_free -> block is free\n"));
+ ret = 1;
+ } else {
+ QNX4DEBUG(("qnx4: is_free -> block is busy\n"));
+ ret = 0;
+ }
+ brelse(bh);
+
+ return ret;
+}
+
+int qnx4_set_bitmap(struct super_block *sb, long block, int busy)
+{
+ int start = le32_to_cpu(sb->u.qnx4_sb.BitMap->di_first_xtnt.xtnt_blk) - 1;
+ int size = le32_to_cpu(sb->u.qnx4_sb.BitMap->di_size);
+ struct buffer_head *bh;
+ char *g;
+
+ start += block / (QNX4_BLOCK_SIZE * 8);
+ QNX4DEBUG(("qnx4: set_bitmap requesting block [%lu], bitmap in block [%lu]\n",
+ (unsigned long) block, (unsigned long) start));
+ (void) size; /* CHECKME */
+ bh = sb_bread(sb, start);
+ if (bh == NULL) {
+ return -EIO;
+ }
+ g = bh->b_data + (block % QNX4_BLOCK_SIZE);
+ if (busy == 0) {
+ (*g) &= ~(1 << (block % 8));
+ } else {
+ (*g) |= (1 << (block % 8));
+ }
+ mark_buffer_dirty(bh);
+ brelse(bh);
+
+ return 0;
+}
+
+static void qnx4_clear_inode(struct inode *inode)
+{
+ struct qnx4_inode_info *qnx4_ino = &inode->u.qnx4_i;
+
+ memset(qnx4_ino->i_reserved, 0, sizeof qnx4_ino->i_reserved);
+ qnx4_ino->i_size = 0;
+ qnx4_ino->i_num_xtnts = 0;
+ qnx4_ino->i_mode = 0;
+ qnx4_ino->i_status = 0;
+}
+
+void qnx4_free_inode(struct inode *inode)
+{
+ if (inode->i_ino < 1) {
+ printk("free_inode: inode 0 or nonexistent inode\n");
+ return;
+ }
+ qnx4_clear_inode(inode);
+ clear_inode(inode);
+}
+
+#endif
diff --git a/fs/qnx4/dir.c b/fs/qnx4/dir.c
new file mode 100644
index 00000000000000..49df47d8f3ee88
--- /dev/null
+++ b/fs/qnx4/dir.c
@@ -0,0 +1,96 @@
+/*
+ * QNX4 file system, Linux implementation.
+ *
+ * Version : 0.2.1
+ *
+ * Using parts of the xiafs filesystem.
+ *
+ * History :
+ *
+ * 28-05-1998 by Richard Frowijn : first release.
+ * 20-06-1998 by Frank Denis : Linux 2.1.99+ & dcache support.
+ */
+
+#include <linux/config.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/qnx4_fs.h>
+#include <linux/stat.h>
+
+#include <asm/segment.h>
+
+static int qnx4_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ unsigned int offset;
+ struct buffer_head *bh;
+ struct qnx4_inode_entry *de;
+ struct qnx4_link_info *le;
+ unsigned long blknum;
+ int ix, ino;
+ int size;
+
+ QNX4DEBUG(("qnx4_readdir:i_size = %ld\n", (long) inode->i_size));
+ QNX4DEBUG(("filp->f_pos = %ld\n", (long) filp->f_pos));
+
+ while (filp->f_pos < inode->i_size) {
+ blknum = qnx4_block_map( inode, filp->f_pos >> QNX4_BLOCK_SIZE_BITS );
+ bh = sb_bread(inode->i_sb, blknum);
+ if(bh==NULL) {
+ printk(KERN_ERR "qnx4_readdir: bread failed (%ld)\n", blknum);
+ break;
+ }
+ ix = (int)(filp->f_pos >> QNX4_DIR_ENTRY_SIZE_BITS) % QNX4_INODES_PER_BLOCK;
+ while (ix < QNX4_INODES_PER_BLOCK) {
+ offset = ix * QNX4_DIR_ENTRY_SIZE;
+ de = (struct qnx4_inode_entry *) (bh->b_data + offset);
+ size = strlen(de->di_fname);
+ if (size) {
+ if ( !( de->di_status & QNX4_FILE_LINK ) && size > QNX4_SHORT_NAME_MAX )
+ size = QNX4_SHORT_NAME_MAX;
+ else if ( size > QNX4_NAME_MAX )
+ size = QNX4_NAME_MAX;
+
+ if ( ( de->di_status & (QNX4_FILE_USED|QNX4_FILE_LINK) ) != 0 ) {
+ QNX4DEBUG(("qnx4_readdir:%.*s\n", size, de->di_fname));
+ if ( ( de->di_status & QNX4_FILE_LINK ) == 0 )
+ ino = blknum * QNX4_INODES_PER_BLOCK + ix - 1;
+ else {
+ le = (struct qnx4_link_info*)de;
+ ino = ( le->dl_inode_blk - 1 ) *
+ QNX4_INODES_PER_BLOCK +
+ le->dl_inode_ndx;
+ }
+ if (filldir(dirent, de->di_fname, size, filp->f_pos, ino, DT_UNKNOWN) < 0) {
+ brelse(bh);
+ return 0;
+ }
+ }
+ }
+ ix++;
+ filp->f_pos += QNX4_DIR_ENTRY_SIZE;
+ }
+ brelse(bh);
+ }
+ UPDATE_ATIME(inode);
+
+ return 0;
+}
+
+struct file_operations qnx4_dir_operations =
+{
+ read: generic_read_dir,
+ readdir: qnx4_readdir,
+ fsync: file_fsync,
+};
+
+struct inode_operations qnx4_dir_inode_operations =
+{
+ lookup: qnx4_lookup,
+#ifdef CONFIG_QNX4FS_RW
+ create: qnx4_create,
+ unlink: qnx4_unlink,
+ rmdir: qnx4_rmdir,
+#endif
+};
diff --git a/fs/qnx4/file.c b/fs/qnx4/file.c
new file mode 100644
index 00000000000000..ab5856ace4f2ad
--- /dev/null
+++ b/fs/qnx4/file.c
@@ -0,0 +1,43 @@
+/*
+ * QNX4 file system, Linux implementation.
+ *
+ * Version : 0.2.1
+ *
+ * Using parts of the xiafs filesystem.
+ *
+ * History :
+ *
+ * 25-05-1998 by Richard Frowijn : first release.
+ * 21-06-1998 by Frank Denis : wrote qnx4_readpage to use generic_file_read.
+ * 27-06-1998 by Frank Denis : file overwriting.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/qnx4_fs.h>
+
+/*
+ * We have mostly NULL's here: the current defaults are ok for
+ * the qnx4 filesystem.
+ */
+struct file_operations qnx4_file_operations =
+{
+ llseek: generic_file_llseek,
+ read: generic_file_read,
+#ifdef CONFIG_QNX4FS_RW
+ write: generic_file_write,
+#endif
+ mmap: generic_file_mmap,
+#ifdef CONFIG_QNX4FS_RW
+ fsync: qnx4_sync_file,
+#endif
+};
+
+struct inode_operations qnx4_file_inode_operations =
+{
+#ifdef CONFIG_QNX4FS_RW
+ truncate: qnx4_truncate,
+#endif
+};
diff --git a/fs/qnx4/fsync.c b/fs/qnx4/fsync.c
new file mode 100644
index 00000000000000..4ef5de9554e16f
--- /dev/null
+++ b/fs/qnx4/fsync.c
@@ -0,0 +1,171 @@
+/*
+ * QNX4 file system, Linux implementation.
+ *
+ * Version : 0.1
+ *
+ * Using parts of the xiafs filesystem.
+ *
+ * History :
+ *
+ * 24-03-1998 by Richard Frowijn : first release.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/locks.h>
+#include <linux/smp_lock.h>
+
+#include <linux/fs.h>
+#include <linux/qnx4_fs.h>
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+/*
+ * The functions for qnx4 fs file synchronization.
+ */
+
+#ifdef CONFIG_QNX4FS_RW
+
+static int sync_block(struct inode *inode, unsigned short *block, int wait)
+{
+ struct buffer_head *bh;
+ unsigned short tmp;
+
+ if (!*block)
+ return 0;
+ tmp = *block;
+ bh = sb_get_hash_table(inode->i_sb, *block);
+ if (!bh)
+ return 0;
+ if (*block != tmp) {
+ brelse(bh);
+ return 1;
+ }
+ if (wait && buffer_req(bh) && !buffer_uptodate(bh)) {
+ brelse(bh);
+ return -1;
+ }
+ if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) {
+ brelse(bh);
+ return 0;
+ }
+ ll_rw_block(WRITE, 1, &bh);
+ atomic_dec(&bh->b_count);
+ return 0;
+}
+
+#ifdef WTF
+static int sync_iblock(struct inode *inode, unsigned short *iblock,
+ struct buffer_head **bh, int wait)
+{
+ int rc;
+ unsigned short tmp;
+
+ *bh = NULL;
+ tmp = *iblock;
+ if (!tmp)
+ return 0;
+ rc = sync_block(inode, iblock, wait);
+ if (rc)
+ return rc;
+ *bh = sb_bread(inode->i_sb, tmp);
+ if (tmp != *iblock) {
+ brelse(*bh);
+ *bh = NULL;
+ return 1;
+ }
+ if (!*bh)
+ return -1;
+ return 0;
+}
+#endif
+
+static int sync_direct(struct inode *inode, int wait)
+{
+ int i;
+ int rc, err = 0;
+
+ for (i = 0; i < 7; i++) {
+ rc = sync_block(inode,
+ (unsigned short *) inode->u.qnx4_i.i_first_xtnt.xtnt_blk + i, wait);
+ if (rc > 0)
+ break;
+ if (rc)
+ err = rc;
+ }
+ return err;
+}
+
+#ifdef WTF
+static int sync_indirect(struct inode *inode, unsigned short *iblock, int wait)
+{
+ int i;
+ struct buffer_head *ind_bh;
+ int rc, err = 0;
+
+ rc = sync_iblock(inode, iblock, &ind_bh, wait);
+ if (rc || !ind_bh)
+ return rc;
+
+ for (i = 0; i < 512; i++) {
+ rc = sync_block(inode,
+ ((unsigned short *) ind_bh->b_data) + i,
+ wait);
+ if (rc > 0)
+ break;
+ if (rc)
+ err = rc;
+ }
+ brelse(ind_bh);
+ return err;
+}
+
+static int sync_dindirect(struct inode *inode, unsigned short *diblock,
+ int wait)
+{
+ int i;
+ struct buffer_head *dind_bh;
+ int rc, err = 0;
+
+ rc = sync_iblock(inode, diblock, &dind_bh, wait);
+ if (rc || !dind_bh)
+ return rc;
+
+ for (i = 0; i < 512; i++) {
+ rc = sync_indirect(inode,
+ ((unsigned short *) dind_bh->b_data) + i,
+ wait);
+ if (rc > 0)
+ break;
+ if (rc)
+ err = rc;
+ }
+ brelse(dind_bh);
+ return err;
+}
+#endif
+
+int qnx4_sync_file(struct file *file, struct dentry *dentry, int unused)
+{
+ struct inode *inode = dentry->d_inode;
+ int wait, err = 0;
+
+ (void) file;
+ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+ S_ISLNK(inode->i_mode)))
+ return -EINVAL;
+
+ lock_kernel();
+ for (wait = 0; wait <= 1; wait++) {
+ err |= sync_direct(inode, wait);
+ }
+ err |= qnx4_sync_inode(inode);
+ unlock_kernel();
+ return (err < 0) ? -EIO : 0;
+}
+
+#endif
diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c
new file mode 100644
index 00000000000000..0f5a4dba734cfd
--- /dev/null
+++ b/fs/qnx4/inode.c
@@ -0,0 +1,512 @@
+/*
+ * QNX4 file system, Linux implementation.
+ *
+ * Version : 0.2.1
+ *
+ * Using parts of the xiafs filesystem.
+ *
+ * History :
+ *
+ * 01-06-1998 by Richard Frowijn : first release.
+ * 20-06-1998 by Frank Denis : Linux 2.1.99+ support, boot signature, misc.
+ * 30-06-1998 by Frank Denis : first step to write inodes.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/qnx4_fs.h>
+#include <linux/fs.h>
+#include <linux/locks.h>
+#include <linux/init.h>
+#include <linux/highuid.h>
+#include <linux/smp_lock.h>
+
+#include <asm/uaccess.h>
+
+#define QNX4_VERSION 4
+#define QNX4_BMNAME ".bitmap"
+
+static struct super_operations qnx4_sops;
+
+#ifdef CONFIG_QNX4FS_RW
+
+int qnx4_sync_inode(struct inode *inode)
+{
+ int err = 0;
+# if 0
+ struct buffer_head *bh;
+
+ bh = qnx4_update_inode(inode);
+ if (bh && buffer_dirty(bh))
+ {
+ ll_rw_block(WRITE, 1, &bh);
+ wait_on_buffer(bh);
+ if (buffer_req(bh) && !buffer_uptodate(bh))
+ {
+ printk ("IO error syncing qnx4 inode [%s:%08lx]\n",
+ kdevname(inode->i_dev), inode->i_ino);
+ err = -1;
+ }
+ brelse (bh);
+ } else if (!bh) {
+ err = -1;
+ }
+# endif
+
+ return err;
+}
+
+static void qnx4_delete_inode(struct inode *inode)
+{
+ QNX4DEBUG(("qnx4: deleting inode [%lu]\n", (unsigned long) inode->i_ino));
+ lock_kernel();
+ inode->i_size = 0;
+ qnx4_truncate(inode);
+ qnx4_free_inode(inode);
+ unlock_kernel();
+}
+
+static void qnx4_write_super(struct super_block *sb)
+{
+ QNX4DEBUG(("qnx4: write_super\n"));
+ sb->s_dirt = 0;
+}
+
+static void qnx4_write_inode(struct inode *inode, int unused)
+{
+ struct qnx4_inode_entry *raw_inode;
+ int block, ino;
+ struct buffer_head *bh;
+ ino = inode->i_ino;
+
+ QNX4DEBUG(("qnx4: write inode 1.\n"));
+ if (inode->i_nlink == 0) {
+ return;
+ }
+ if (!ino) {
+ printk("qnx4: bad inode number on dev %s: %d is out of range\n",
+ kdevname(inode->i_dev), ino);
+ return;
+ }
+ QNX4DEBUG(("qnx4: write inode 2.\n"));
+ block = ino / QNX4_INODES_PER_BLOCK;
+ lock_kernel();
+ if (!(bh = sb_bread(inode->i_sb, block))) {
+ printk("qnx4: major problem: unable to read inode from dev "
+ "%s\n", kdevname(inode->i_dev));
+ unlock_kernel();
+ return;
+ }
+ raw_inode = ((struct qnx4_inode_entry *) bh->b_data) +
+ (ino % QNX4_INODES_PER_BLOCK);
+ raw_inode->di_mode = cpu_to_le16(inode->i_mode);
+ raw_inode->di_uid = cpu_to_le16(fs_high2lowuid(inode->i_uid));
+ raw_inode->di_gid = cpu_to_le16(fs_high2lowgid(inode->i_gid));
+ raw_inode->di_nlink = cpu_to_le16(inode->i_nlink);
+ raw_inode->di_size = cpu_to_le32(inode->i_size);
+ raw_inode->di_mtime = cpu_to_le32(inode->i_mtime);
+ raw_inode->di_atime = cpu_to_le32(inode->i_atime);
+ raw_inode->di_ctime = cpu_to_le32(inode->i_ctime);
+ raw_inode->di_first_xtnt.xtnt_size = cpu_to_le32(inode->i_blocks);
+ mark_buffer_dirty(bh);
+ brelse(bh);
+ unlock_kernel();
+}
+
+#endif
+
+static struct super_block *qnx4_read_super(struct super_block *, void *, int);
+static void qnx4_put_super(struct super_block *sb);
+static void qnx4_read_inode(struct inode *);
+static int qnx4_remount(struct super_block *sb, int *flags, char *data);
+static int qnx4_statfs(struct super_block *, struct statfs *);
+
+static struct super_operations qnx4_sops =
+{
+ read_inode: qnx4_read_inode,
+#ifdef CONFIG_QNX4FS_RW
+ write_inode: qnx4_write_inode,
+ delete_inode: qnx4_delete_inode,
+#endif
+ put_super: qnx4_put_super,
+#ifdef CONFIG_QNX4FS_RW
+ write_super: qnx4_write_super,
+#endif
+ statfs: qnx4_statfs,
+ remount_fs: qnx4_remount,
+};
+
+static int qnx4_remount(struct super_block *sb, int *flags, char *data)
+{
+ struct qnx4_sb_info *qs;
+
+ qs = &sb->u.qnx4_sb;
+ qs->Version = QNX4_VERSION;
+ if (*flags & MS_RDONLY) {
+ return 0;
+ }
+ mark_buffer_dirty(qs->sb_buf);
+
+ return 0;
+}
+
+struct buffer_head *qnx4_getblk(struct inode *inode, int nr,
+ int create)
+{
+ struct buffer_head *result = NULL;
+
+ if ( nr >= 0 )
+ nr = qnx4_block_map( inode, nr );
+ if (nr) {
+ result = sb_getblk(inode->i_sb, nr);
+ return result;
+ }
+ if (!create) {
+ return NULL;
+ }
+#if 0
+ tmp = qnx4_new_block(inode->i_sb);
+ if (!tmp) {
+ return NULL;
+ }
+ result = sb_getblk(inode->i_sb, tmp);
+ if (tst) {
+ qnx4_free_block(inode->i_sb, tmp);
+ brelse(result);
+ goto repeat;
+ }
+ tst = tmp;
+#endif
+ inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+ return result;
+}
+
+struct buffer_head *qnx4_bread(struct inode *inode, int block, int create)
+{
+ struct buffer_head *bh;
+
+ bh = qnx4_getblk(inode, block, create);
+ if (!bh || buffer_uptodate(bh)) {
+ return bh;
+ }
+ ll_rw_block(READ, 1, &bh);
+ wait_on_buffer(bh);
+ if (buffer_uptodate(bh)) {
+ return bh;
+ }
+ brelse(bh);
+
+ return NULL;
+}
+
+int qnx4_get_block( struct inode *inode, long iblock, struct buffer_head *bh, int create )
+{
+ unsigned long phys;
+
+ QNX4DEBUG(("qnx4: qnx4_get_block inode=[%ld] iblock=[%ld]\n",inode->i_ino,iblock));
+
+ phys = qnx4_block_map( inode, iblock );
+ if ( phys ) {
+ // logical block is before EOF
+ bh->b_dev = inode->i_dev;
+ bh->b_blocknr = phys;
+ bh->b_state |= (1UL << BH_Mapped);
+ } else if ( create ) {
+ // to be done.
+ }
+ return 0;
+}
+
+unsigned long qnx4_block_map( struct inode *inode, long iblock )
+{
+ int ix;
+ long offset, i_xblk;
+ unsigned long block = 0;
+ struct buffer_head *bh = 0;
+ struct qnx4_xblk *xblk = 0;
+ struct qnx4_inode_info *qnx4_inode = &inode->u.qnx4_i;
+ qnx4_nxtnt_t nxtnt = le16_to_cpu(qnx4_inode->i_num_xtnts);
+
+ if ( iblock < le32_to_cpu(qnx4_inode->i_first_xtnt.xtnt_size) ) {
+ // iblock is in the first extent. This is easy.
+ block = le32_to_cpu(qnx4_inode->i_first_xtnt.xtnt_blk) + iblock - 1;
+ } else {
+ // iblock is beyond first extent. We have to follow the extent chain.
+ i_xblk = le32_to_cpu(qnx4_inode->i_xblk);
+ offset = iblock - le32_to_cpu(qnx4_inode->i_first_xtnt.xtnt_size);
+ ix = 0;
+ while ( --nxtnt > 0 ) {
+ if ( ix == 0 ) {
+ // read next xtnt block.
+ bh = sb_bread(inode->i_sb, i_xblk - 1);
+ if ( !bh ) {
+ QNX4DEBUG(("qnx4: I/O error reading xtnt block [%ld])\n", i_xblk - 1));
+ return -EIO;
+ }
+ xblk = (struct qnx4_xblk*)bh->b_data;
+ if ( memcmp( xblk->xblk_signature, "IamXblk", 7 ) ) {
+ QNX4DEBUG(("qnx4: block at %ld is not a valid xtnt\n", qnx4_inode->i_xblk));
+ return -EIO;
+ }
+ }
+ if ( offset < le32_to_cpu(xblk->xblk_xtnts[ix].xtnt_size) ) {
+ // got it!
+ block = le32_to_cpu(xblk->xblk_xtnts[ix].xtnt_blk) + offset - 1;
+ break;
+ }
+ offset -= le32_to_cpu(xblk->xblk_xtnts[ix].xtnt_size);
+ if ( ++ix >= xblk->xblk_num_xtnts ) {
+ i_xblk = le32_to_cpu(xblk->xblk_next_xblk);
+ ix = 0;
+ brelse( bh );
+ bh = 0;
+ }
+ }
+ if ( bh )
+ brelse( bh );
+ }
+
+ QNX4DEBUG(("qnx4: mapping block %ld of inode %ld = %ld\n",iblock,inode->i_ino,block));
+ return block;
+}
+
+static int qnx4_statfs(struct super_block *sb, struct statfs *buf)
+{
+ buf->f_type = sb->s_magic;
+ buf->f_bsize = sb->s_blocksize;
+ buf->f_blocks = le32_to_cpu(sb->u.qnx4_sb.BitMap->di_size) * 8;
+ buf->f_bfree = qnx4_count_free_blocks(sb);
+ buf->f_bavail = buf->f_bfree;
+ buf->f_namelen = QNX4_NAME_MAX;
+
+ return 0;
+}
+
+/*
+ * Check the root directory of the filesystem to make sure
+ * it really _is_ a qnx4 filesystem, and to check the size
+ * of the directory entry.
+ */
+static const char *qnx4_checkroot(struct super_block *sb)
+{
+ struct buffer_head *bh;
+ struct qnx4_inode_entry *rootdir;
+ int rd, rl;
+ int i, j;
+ int found = 0;
+
+ if (*(sb->u.qnx4_sb.sb->RootDir.di_fname) != '/') {
+ return "no qnx4 filesystem (no root dir).";
+ } else {
+ QNX4DEBUG(("QNX4 filesystem found on dev %s.\n", kdevname(sb->s_dev)));
+ rd = le32_to_cpu(sb->u.qnx4_sb.sb->RootDir.di_first_xtnt.xtnt_blk) - 1;
+ rl = le32_to_cpu(sb->u.qnx4_sb.sb->RootDir.di_first_xtnt.xtnt_size);
+ for (j = 0; j < rl; j++) {
+ bh = sb_bread(sb, rd + j); /* root dir, first block */
+ if (bh == NULL) {
+ return "unable to read root entry.";
+ }
+ for (i = 0; i < QNX4_INODES_PER_BLOCK; i++) {
+ rootdir = (struct qnx4_inode_entry *) (bh->b_data + i * QNX4_DIR_ENTRY_SIZE);
+ if (rootdir->di_fname != NULL) {
+ QNX4DEBUG(("Rootdir entry found : [%s]\n", rootdir->di_fname));
+ if (!strncmp(rootdir->di_fname, QNX4_BMNAME, sizeof QNX4_BMNAME)) {
+ found = 1;
+ sb->u.qnx4_sb.BitMap = kmalloc( sizeof( struct qnx4_inode_entry ), GFP_KERNEL );
+ if (!sb->u.qnx4_sb.BitMap) {
+ brelse (bh);
+ return "not enough memory for bitmap inode";
+ }
+ memcpy( sb->u.qnx4_sb.BitMap, rootdir, sizeof( struct qnx4_inode_entry ) ); /* keep bitmap inode known */
+ break;
+ }
+ }
+ }
+ brelse(bh);
+ if (found != 0) {
+ break;
+ }
+ }
+ if (found == 0) {
+ return "bitmap file not found.";
+ }
+ }
+ return NULL;
+}
+
+static struct super_block *qnx4_read_super(struct super_block *s,
+ void *data, int silent)
+{
+ struct buffer_head *bh;
+ kdev_t dev = s->s_dev;
+ struct inode *root;
+ const char *errmsg;
+
+ set_blocksize(dev, QNX4_BLOCK_SIZE);
+ s->s_blocksize = QNX4_BLOCK_SIZE;
+ s->s_blocksize_bits = QNX4_BLOCK_SIZE_BITS;
+
+ /* Check the superblock signature. Since the qnx4 code is
+ dangerous, we should leave as quickly as possible
+ if we don't belong here... */
+ bh = sb_bread(s, 1);
+ if (!bh) {
+ printk("qnx4: unable to read the superblock\n");
+ goto outnobh;
+ }
+ if ( le32_to_cpu( *(__u32*)bh->b_data ) != QNX4_SUPER_MAGIC ) {
+ if (!silent)
+ printk("qnx4: wrong fsid in superblock sector.\n");
+ goto out;
+ }
+ s->s_op = &qnx4_sops;
+ s->s_magic = QNX4_SUPER_MAGIC;
+#ifndef CONFIG_QNX4FS_RW
+ s->s_flags |= MS_RDONLY; /* Yup, read-only yet */
+#endif
+ s->u.qnx4_sb.sb_buf = bh;
+ s->u.qnx4_sb.sb = (struct qnx4_super_block *) bh->b_data;
+
+
+ /* check before allocating dentries, inodes, .. */
+ errmsg = qnx4_checkroot(s);
+ if (errmsg != NULL) {
+ if (!silent)
+ printk("qnx4: %s\n", errmsg);
+ goto out;
+ }
+
+ /* does root not have inode number QNX4_ROOT_INO ?? */
+ root = iget(s, QNX4_ROOT_INO * QNX4_INODES_PER_BLOCK);
+ if (!root) {
+ printk("qnx4: get inode failed\n");
+ goto out;
+ }
+
+ s->s_root = d_alloc_root(root);
+ if (s->s_root == NULL)
+ goto outi;
+
+ brelse(bh);
+
+ return s;
+
+ outi:
+ iput(root);
+ out:
+ brelse(bh);
+ outnobh:
+
+ return NULL;
+}
+
+static void qnx4_put_super(struct super_block *sb)
+{
+ kfree( sb->u.qnx4_sb.BitMap );
+ return;
+}
+
+static int qnx4_writepage(struct page *page)
+{
+ return block_write_full_page(page,qnx4_get_block);
+}
+static int qnx4_readpage(struct file *file, struct page *page)
+{
+ return block_read_full_page(page,qnx4_get_block);
+}
+static int qnx4_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
+{
+ return cont_prepare_write(page,from,to,qnx4_get_block,
+ &page->mapping->host->u.qnx4_i.mmu_private);
+}
+static int qnx4_bmap(struct address_space *mapping, long block)
+{
+ return generic_block_bmap(mapping,block,qnx4_get_block);
+}
+struct address_space_operations qnx4_aops = {
+ readpage: qnx4_readpage,
+ writepage: qnx4_writepage,
+ sync_page: block_sync_page,
+ prepare_write: qnx4_prepare_write,
+ commit_write: generic_commit_write,
+ bmap: qnx4_bmap
+};
+
+static void qnx4_read_inode(struct inode *inode)
+{
+ struct buffer_head *bh;
+ struct qnx4_inode_entry *raw_inode;
+ int block, ino;
+
+ ino = inode->i_ino;
+ inode->i_mode = 0;
+
+ QNX4DEBUG(("Reading inode : [%d]\n", ino));
+ if (!ino) {
+ printk("qnx4: bad inode number on dev %s: %d is out of range\n",
+ kdevname(inode->i_dev), ino);
+ return;
+ }
+ block = ino / QNX4_INODES_PER_BLOCK;
+
+ if (!(bh = sb_bread(inode->i_sb, block))) {
+ printk("qnx4: major problem: unable to read inode from dev "
+ "%s\n", kdevname(inode->i_dev));
+ return;
+ }
+ raw_inode = ((struct qnx4_inode_entry *) bh->b_data) +
+ (ino % QNX4_INODES_PER_BLOCK);
+
+ inode->i_mode = le16_to_cpu(raw_inode->di_mode);
+ inode->i_uid = (uid_t)le16_to_cpu(raw_inode->di_uid);
+ inode->i_gid = (gid_t)le16_to_cpu(raw_inode->di_gid);
+ inode->i_nlink = le16_to_cpu(raw_inode->di_nlink);
+ inode->i_size = le32_to_cpu(raw_inode->di_size);
+ inode->i_mtime = le32_to_cpu(raw_inode->di_mtime);
+ inode->i_atime = le32_to_cpu(raw_inode->di_atime);
+ inode->i_ctime = le32_to_cpu(raw_inode->di_ctime);
+ inode->i_blocks = le32_to_cpu(raw_inode->di_first_xtnt.xtnt_size);
+ inode->i_blksize = QNX4_DIR_ENTRY_SIZE;
+
+ memcpy(&inode->u.qnx4_i, (struct qnx4_inode_info *) raw_inode, QNX4_DIR_ENTRY_SIZE);
+ if (S_ISREG(inode->i_mode)) {
+ inode->i_op = &qnx4_file_inode_operations;
+ inode->i_fop = &qnx4_file_operations;
+ inode->i_mapping->a_ops = &qnx4_aops;
+ inode->u.qnx4_i.mmu_private = inode->i_size;
+ } else if (S_ISDIR(inode->i_mode)) {
+ inode->i_op = &qnx4_dir_inode_operations;
+ inode->i_fop = &qnx4_dir_operations;
+ } else if (S_ISLNK(inode->i_mode)) {
+ inode->i_op = &page_symlink_inode_operations;
+ inode->i_mapping->a_ops = &qnx4_aops;
+ inode->u.qnx4_i.mmu_private = inode->i_size;
+ } else
+ printk("qnx4: bad inode %d on dev %s\n",ino,kdevname(inode->i_dev));
+ brelse(bh);
+}
+
+static DECLARE_FSTYPE_DEV(qnx4_fs_type, "qnx4", qnx4_read_super);
+
+static int __init init_qnx4_fs(void)
+{
+ printk("QNX4 filesystem 0.2.2 registered.\n");
+ return register_filesystem(&qnx4_fs_type);
+}
+
+static void __exit exit_qnx4_fs(void)
+{
+ unregister_filesystem(&qnx4_fs_type);
+}
+
+EXPORT_NO_SYMBOLS;
+
+module_init(init_qnx4_fs)
+module_exit(exit_qnx4_fs)
+MODULE_LICENSE("GPL");
+
diff --git a/fs/qnx4/namei.c b/fs/qnx4/namei.c
new file mode 100644
index 00000000000000..7366b757fec71f
--- /dev/null
+++ b/fs/qnx4/namei.c
@@ -0,0 +1,237 @@
+/*
+ * QNX4 file system, Linux implementation.
+ *
+ * Version : 0.2.1
+ *
+ * Using parts of the xiafs filesystem.
+ *
+ * History :
+ *
+ * 01-06-1998 by Richard Frowijn : first release.
+ * 21-06-1998 by Frank Denis : dcache support, fixed error codes.
+ * 04-07-1998 by Frank Denis : first step for rmdir/unlink.
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/qnx4_fs.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/errno.h>
+
+#include <asm/segment.h>
+
+/*
+ * check if the filename is correct. For some obscure reason, qnx writes a
+ * new file twice in the directory entry, first with all possible options at 0
+ * and for a second time the way it is, they want us not to access the qnx
+ * filesystem when whe are using linux.
+ */
+static int qnx4_match(int len, const char *name,
+ struct buffer_head *bh, unsigned long *offset)
+{
+ struct qnx4_inode_entry *de;
+ int namelen, thislen;
+
+ if (bh == NULL) {
+ printk("qnx4: matching unassigned buffer !\n");
+ return 0;
+ }
+ de = (struct qnx4_inode_entry *) (bh->b_data + *offset);
+ *offset += QNX4_DIR_ENTRY_SIZE;
+ if ((de->di_status & QNX4_FILE_LINK) != 0) {
+ namelen = QNX4_NAME_MAX;
+ } else {
+ namelen = QNX4_SHORT_NAME_MAX;
+ }
+ /* "" means "." ---> so paths like "/usr/lib//libc.a" work */
+ if (!len && (de->di_fname[0] == '.') && (de->di_fname[1] == '\0')) {
+ return 1;
+ }
+ thislen = strlen( de->di_fname );
+ if ( thislen > namelen )
+ thislen = namelen;
+ if (len != thislen) {
+ return 0;
+ }
+ if (strncmp(name, de->di_fname, len) == 0) {
+ if ((de->di_status & (QNX4_FILE_USED|QNX4_FILE_LINK)) != 0) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static struct buffer_head *qnx4_find_entry(int len, struct inode *dir,
+ const char *name, struct qnx4_inode_entry **res_dir, int *ino)
+{
+ unsigned long block, offset, blkofs;
+ struct buffer_head *bh;
+
+ *res_dir = NULL;
+ if (!dir->i_sb) {
+ printk("qnx4: no superblock on dir.\n");
+ return NULL;
+ }
+ bh = NULL;
+ block = offset = blkofs = 0;
+ while (blkofs * QNX4_BLOCK_SIZE + offset < dir->i_size) {
+ if (!bh) {
+ bh = qnx4_bread(dir, blkofs, 0);
+ if (!bh) {
+ blkofs++;
+ continue;
+ }
+ }
+ *res_dir = (struct qnx4_inode_entry *) (bh->b_data + offset);
+ if (qnx4_match(len, name, bh, &offset)) {
+ block = qnx4_block_map( dir, blkofs );
+ *ino = block * QNX4_INODES_PER_BLOCK +
+ (offset / QNX4_DIR_ENTRY_SIZE) - 1;
+ return bh;
+ }
+ if (offset < bh->b_size) {
+ continue;
+ }
+ brelse(bh);
+ bh = NULL;
+ offset = 0;
+ blkofs++;
+ }
+ brelse(bh);
+ *res_dir = NULL;
+ return NULL;
+}
+
+struct dentry * qnx4_lookup(struct inode *dir, struct dentry *dentry)
+{
+ int ino;
+ struct qnx4_inode_entry *de;
+ struct qnx4_link_info *lnk;
+ struct buffer_head *bh;
+ const char *name = dentry->d_name.name;
+ int len = dentry->d_name.len;
+ struct inode *foundinode = NULL;
+
+ if (!(bh = qnx4_find_entry(len, dir, name, &de, &ino)))
+ goto out;
+ /* The entry is linked, let's get the real info */
+ if ((de->di_status & QNX4_FILE_LINK) == QNX4_FILE_LINK) {
+ lnk = (struct qnx4_link_info *) de;
+ ino = (le32_to_cpu(lnk->dl_inode_blk) - 1) *
+ QNX4_INODES_PER_BLOCK +
+ lnk->dl_inode_ndx;
+ }
+ brelse(bh);
+
+ if ((foundinode = iget(dir->i_sb, ino)) == NULL) {
+ QNX4DEBUG(("qnx4: lookup->iget -> NULL\n"));
+ return ERR_PTR(-EACCES);
+ }
+out:
+ d_add(dentry, foundinode);
+
+ return NULL;
+}
+
+#ifdef CONFIG_QNX4FS_RW
+int qnx4_create(struct inode *dir, struct dentry *dentry, int mode)
+{
+ QNX4DEBUG(("qnx4: qnx4_create\n"));
+ if (dir == NULL) {
+ return -ENOENT;
+ }
+ return -ENOSPC;
+}
+
+int qnx4_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ struct buffer_head *bh;
+ struct qnx4_inode_entry *de;
+ struct inode *inode;
+ int retval;
+ int ino;
+
+ QNX4DEBUG(("qnx4: qnx4_rmdir [%s]\n", dentry->d_name.name));
+ bh = qnx4_find_entry(dentry->d_name.len, dir, dentry->d_name.name,
+ &de, &ino);
+ if (bh == NULL) {
+ return -ENOENT;
+ }
+ inode = dentry->d_inode;
+ if (inode->i_ino != ino) {
+ retval = -EIO;
+ goto end_rmdir;
+ }
+#if 0
+ if (!empty_dir(inode)) {
+ retval = -ENOTEMPTY;
+ goto end_rmdir;
+ }
+#endif
+ if (inode->i_nlink != 2) {
+ QNX4DEBUG(("empty directory has nlink!=2 (%d)\n", inode->i_nlink));
+ }
+ QNX4DEBUG(("qnx4: deleting directory\n"));
+ de->di_status = 0;
+ memset(de->di_fname, 0, sizeof de->di_fname);
+ de->di_mode = 0;
+ mark_buffer_dirty(bh);
+ inode->i_nlink = 0;
+ mark_inode_dirty(inode);
+ inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ dir->i_nlink--;
+ mark_inode_dirty(dir);
+ retval = 0;
+
+ end_rmdir:
+ brelse(bh);
+
+ return retval;
+}
+
+int qnx4_unlink(struct inode *dir, struct dentry *dentry)
+{
+ struct buffer_head *bh;
+ struct qnx4_inode_entry *de;
+ struct inode *inode;
+ int retval;
+ int ino;
+
+ QNX4DEBUG(("qnx4: qnx4_unlink [%s]\n", dentry->d_name.name));
+ bh = qnx4_find_entry(dentry->d_name.len, dir, dentry->d_name.name,
+ &de, &ino);
+ if (bh == NULL) {
+ return -ENOENT;
+ }
+ inode = dentry->d_inode;
+ if (inode->i_ino != ino) {
+ retval = -EIO;
+ goto end_unlink;
+ }
+ retval = -EPERM;
+ if (!inode->i_nlink) {
+ QNX4DEBUG(("Deleting nonexistent file (%s:%lu), %d\n",
+ kdevname(inode->i_dev),
+ inode->i_ino, inode->i_nlink));
+ inode->i_nlink = 1;
+ }
+ de->di_status = 0;
+ memset(de->di_fname, 0, sizeof de->di_fname);
+ de->di_mode = 0;
+ mark_buffer_dirty(bh);
+ dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ mark_inode_dirty(dir);
+ inode->i_nlink--;
+ inode->i_ctime = dir->i_ctime;
+ mark_inode_dirty(inode);
+ retval = 0;
+
+ end_unlink:
+ brelse(bh);
+
+ return retval;
+}
+#endif
diff --git a/fs/qnx4/truncate.c b/fs/qnx4/truncate.c
new file mode 100644
index 00000000000000..9dad91a173adec
--- /dev/null
+++ b/fs/qnx4/truncate.c
@@ -0,0 +1,38 @@
+/*
+ * QNX4 file system, Linux implementation.
+ *
+ * Version : 0.1
+ *
+ * Using parts of the xiafs filesystem.
+ *
+ * History :
+ *
+ * 30-06-1998 by Frank DENIS : ugly filler.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/qnx4_fs.h>
+#include <linux/fs.h>
+#include <linux/locks.h>
+#include <asm/uaccess.h>
+
+#ifdef CONFIG_QNX4FS_RW
+
+void qnx4_truncate(struct inode *inode)
+{
+ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+ S_ISLNK(inode->i_mode))) {
+ return;
+ }
+ if (!(S_ISDIR(inode->i_mode))) {
+ /* TODO */
+ }
+ QNX4DEBUG(("qnx4: qnx4_truncate called\n"));
+ inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+}
+
+#endif
diff --git a/fs/quota.c b/fs/quota.c
new file mode 100644
index 00000000000000..2fac44da6b65e3
--- /dev/null
+++ b/fs/quota.c
@@ -0,0 +1,489 @@
+/*
+ * Quota code necessary even when VFS quota support is not compiled
+ * into the kernel. The interesting stuff is over in dquot.c, here
+ * we have symbols for initial quotactl(2) handling, the sysctl(2)
+ * variables, etc - things needed even when quota support disabled.
+ */
+
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <asm/current.h>
+#include <asm/uaccess.h>
+#include <linux/kernel.h>
+#include <linux/smp_lock.h>
+#include <linux/quotaops.h>
+#include <linux/quotacompat.h>
+
+struct dqstats dqstats;
+
+/* Check validity of quotactl */
+static int check_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t id)
+{
+ if (type >= MAXQUOTAS)
+ return -EINVAL;
+ if (!sb && cmd != Q_SYNC)
+ return -ENODEV;
+ /* Is operation supported? */
+ if (sb && !sb->s_qcop)
+ return -ENOSYS;
+
+ switch (cmd) {
+ case Q_GETFMT:
+ break;
+ case Q_QUOTAON:
+ if (!sb->s_qcop->quota_on)
+ return -ENOSYS;
+ break;
+ case Q_QUOTAOFF:
+ if (!sb->s_qcop->quota_off)
+ return -ENOSYS;
+ break;
+ case Q_SETINFO:
+ if (!sb->s_qcop->set_info)
+ return -ENOSYS;
+ break;
+ case Q_GETINFO:
+ if (!sb->s_qcop->get_info)
+ return -ENOSYS;
+ break;
+ case Q_SETQUOTA:
+ if (!sb->s_qcop->set_dqblk)
+ return -ENOSYS;
+ break;
+ case Q_GETQUOTA:
+ if (!sb->s_qcop->get_dqblk)
+ return -ENOSYS;
+ break;
+ case Q_SYNC:
+ if (sb && !sb->s_qcop->quota_sync)
+ return -ENOSYS;
+ break;
+ case Q_XQUOTAON:
+ case Q_XQUOTAOFF:
+ case Q_XQUOTARM:
+ if (!sb->s_qcop->set_xstate)
+ return -ENOSYS;
+ break;
+ case Q_XGETQSTAT:
+ if (!sb->s_qcop->get_xstate)
+ return -ENOSYS;
+ break;
+ case Q_XSETQLIM:
+ if (!sb->s_qcop->set_xquota)
+ return -ENOSYS;
+ break;
+ case Q_XGETQUOTA:
+ if (!sb->s_qcop->get_xquota)
+ return -ENOSYS;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Is quota turned on for commands which need it? */
+ switch (cmd) {
+ case Q_GETFMT:
+ case Q_GETINFO:
+ case Q_QUOTAOFF:
+ case Q_SETINFO:
+ case Q_SETQUOTA:
+ case Q_GETQUOTA:
+ if (!sb_has_quota_enabled(sb, type))
+ return -ESRCH;
+ }
+ /* Check privileges */
+ if (cmd == Q_GETQUOTA || cmd == Q_XGETQUOTA) {
+ if (((type == USRQUOTA && current->euid != id) ||
+ (type == GRPQUOTA && !in_egroup_p(id))) &&
+ !capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ }
+ else if (cmd != Q_GETFMT && cmd != Q_SYNC && cmd != Q_GETINFO && cmd != Q_XGETQSTAT)
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ return 0;
+}
+
+/* Resolve device pathname to superblock */
+static struct super_block *resolve_dev(const char *path)
+{
+ int ret;
+ mode_t mode;
+ struct nameidata nd;
+ kdev_t dev;
+ struct super_block *sb;
+
+ ret = user_path_walk(path, &nd);
+ if (ret)
+ goto out;
+
+ dev = nd.dentry->d_inode->i_rdev;
+ mode = nd.dentry->d_inode->i_mode;
+ path_release(&nd);
+
+ ret = -ENOTBLK;
+ if (!S_ISBLK(mode))
+ goto out;
+ ret = -ENODEV;
+ sb = get_super(dev);
+ if (!sb)
+ goto out;
+ return sb;
+out:
+ return ERR_PTR(ret);
+}
+
+/* Copy parameters and call proper function */
+static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, caddr_t addr)
+{
+ int ret;
+
+ switch (cmd) {
+ case Q_QUOTAON: {
+ char *pathname;
+
+ if (IS_ERR(pathname = getname(addr)))
+ return PTR_ERR(pathname);
+ ret = sb->s_qcop->quota_on(sb, type, id, pathname);
+ putname(pathname);
+ return ret;
+ }
+ case Q_QUOTAOFF:
+ return sb->s_qcop->quota_off(sb, type);
+
+ case Q_GETFMT: {
+ __u32 fmt;
+
+ fmt = sb_dqopt(sb)->info[type].dqi_format->qf_fmt_id;
+ if (copy_to_user(addr, &fmt, sizeof(fmt)))
+ return -EFAULT;
+ return 0;
+ }
+ case Q_GETINFO: {
+ struct if_dqinfo info;
+
+ if ((ret = sb->s_qcop->get_info(sb, type, &info)))
+ return ret;
+ if (copy_to_user(addr, &info, sizeof(info)))
+ return -EFAULT;
+ return 0;
+ }
+ case Q_SETINFO: {
+ struct if_dqinfo info;
+
+ if (copy_from_user(&info, addr, sizeof(info)))
+ return -EFAULT;
+ return sb->s_qcop->set_info(sb, type, &info);
+ }
+ case Q_GETQUOTA: {
+ struct if_dqblk idq;
+
+ if ((ret = sb->s_qcop->get_dqblk(sb, type, id, &idq)))
+ return ret;
+ if (copy_to_user(addr, &idq, sizeof(idq)))
+ return -EFAULT;
+ return 0;
+ }
+ case Q_SETQUOTA: {
+ struct if_dqblk idq;
+
+ if (copy_from_user(&idq, addr, sizeof(idq)))
+ return -EFAULT;
+ return sb->s_qcop->set_dqblk(sb, type, id, &idq);
+ }
+ case Q_SYNC:
+ if (sb)
+ return sb->s_qcop->quota_sync(sb, type);
+ sync_dquots_dev(NODEV, type);
+ return 0;
+ case Q_XQUOTAON:
+ case Q_XQUOTAOFF:
+ case Q_XQUOTARM: {
+ __u32 flags;
+
+ if (copy_from_user(&flags, addr, sizeof(flags)))
+ return -EFAULT;
+ return sb->s_qcop->set_xstate(sb, flags, cmd);
+ }
+ case Q_XGETQSTAT: {
+ struct fs_quota_stat fqs;
+
+ if ((ret = sb->s_qcop->get_xstate(sb, &fqs)))
+ return ret;
+ if (copy_to_user(addr, &fqs, sizeof(fqs)))
+ return -EFAULT;
+ return 0;
+ }
+ case Q_XSETQLIM: {
+ struct fs_disk_quota fdq;
+
+ if (copy_from_user(&fdq, addr, sizeof(fdq)))
+ return -EFAULT;
+ return sb->s_qcop->set_xquota(sb, type, id, &fdq);
+ }
+ case Q_XGETQUOTA: {
+ struct fs_disk_quota fdq;
+
+ if ((ret = sb->s_qcop->get_xquota(sb, type, id, &fdq)))
+ return ret;
+ if (copy_to_user(addr, &fdq, sizeof(fdq)))
+ return -EFAULT;
+ return 0;
+ }
+ /* We never reach here unless validity check is broken */
+ default:
+ BUG();
+ }
+ return 0;
+}
+
+static int check_compat_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t id)
+{
+ if (type >= MAXQUOTAS)
+ return -EINVAL;
+ /* Is operation supported? */
+ /* sb==NULL for GETSTATS calls */
+ if (sb && !sb->s_qcop)
+ return -ENOSYS;
+
+ switch (cmd) {
+ case Q_COMP_QUOTAON:
+ if (!sb->s_qcop->quota_on)
+ return -ENOSYS;
+ break;
+ case Q_COMP_QUOTAOFF:
+ if (!sb->s_qcop->quota_off)
+ return -ENOSYS;
+ break;
+ case Q_COMP_SYNC:
+ if (sb && !sb->s_qcop->quota_sync)
+ return -ENOSYS;
+ break;
+ case Q_V1_SETQLIM:
+ case Q_V1_SETUSE:
+ case Q_V1_SETQUOTA:
+ if (!sb->s_qcop->set_dqblk)
+ return -ENOSYS;
+ break;
+ case Q_V1_GETQUOTA:
+ if (!sb->s_qcop->get_dqblk)
+ return -ENOSYS;
+ break;
+ case Q_V1_RSQUASH:
+ if (!sb->s_qcop->set_info)
+ return -ENOSYS;
+ break;
+ case Q_V1_GETSTATS:
+ return 0; /* GETSTATS need no other checks */
+ default:
+ return -EINVAL;
+ }
+
+ /* Is quota turned on for commands which need it? */
+ switch (cmd) {
+ case Q_V2_SETFLAGS:
+ case Q_V2_SETGRACE:
+ case Q_V2_SETINFO:
+ case Q_V2_GETINFO:
+ case Q_COMP_QUOTAOFF:
+ case Q_V1_RSQUASH:
+ case Q_V1_SETQUOTA:
+ case Q_V1_SETQLIM:
+ case Q_V1_SETUSE:
+ case Q_V2_SETQUOTA:
+ /* Q_V2_SETQLIM: collision with Q_V1_SETQLIM */
+ case Q_V2_SETUSE:
+ case Q_V1_GETQUOTA:
+ case Q_V2_GETQUOTA:
+ if (!sb_has_quota_enabled(sb, type))
+ return -ESRCH;
+ }
+ if (cmd != Q_COMP_QUOTAON &&
+ cmd != Q_COMP_QUOTAOFF &&
+ cmd != Q_COMP_SYNC &&
+ sb_dqopt(sb)->info[type].dqi_format->qf_fmt_id != QFMT_VFS_OLD)
+ return -ESRCH;
+
+ /* Check privileges */
+ if (cmd == Q_V1_GETQUOTA || cmd == Q_V2_GETQUOTA) {
+ if (((type == USRQUOTA && current->euid != id) ||
+ (type == GRPQUOTA && !in_egroup_p(id))) &&
+ !capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ }
+ else if (cmd != Q_V1_GETSTATS && cmd != Q_V2_GETSTATS && cmd != Q_V2_GETINFO && cmd != Q_COMP_SYNC)
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ return 0;
+}
+
+static int v1_set_rsquash(struct super_block *sb, int type, int flag)
+{
+ struct if_dqinfo info;
+
+ info.dqi_valid = IIF_FLAGS;
+ info.dqi_flags = flag ? V1_DQF_RSQUASH : 0;
+ return sb->s_qcop->set_info(sb, type, &info);
+}
+
+static int v1_get_dqblk(struct super_block *sb, int type, qid_t id, struct v1c_mem_dqblk *mdq)
+{
+ struct if_dqblk idq;
+ int ret;
+
+ if ((ret = sb->s_qcop->get_dqblk(sb, type, id, &idq)) < 0)
+ return ret;
+ mdq->dqb_ihardlimit = idq.dqb_ihardlimit;
+ mdq->dqb_isoftlimit = idq.dqb_isoftlimit;
+ mdq->dqb_curinodes = idq.dqb_curinodes;
+ mdq->dqb_bhardlimit = idq.dqb_bhardlimit;
+ mdq->dqb_bsoftlimit = idq.dqb_bsoftlimit;
+ mdq->dqb_curblocks = toqb(idq.dqb_curspace);
+ mdq->dqb_itime = idq.dqb_itime;
+ mdq->dqb_btime = idq.dqb_btime;
+ if (id == 0) { /* Times for id 0 are in fact grace times */
+ struct if_dqinfo info;
+
+ if ((ret = sb->s_qcop->get_info(sb, type, &info)) < 0)
+ return ret;
+ mdq->dqb_btime = info.dqi_bgrace;
+ mdq->dqb_itime = info.dqi_igrace;
+ }
+ return 0;
+}
+
+static int v1_set_dqblk(struct super_block *sb, int type, int cmd, qid_t id, struct v1c_mem_dqblk *mdq)
+{
+ struct if_dqblk idq;
+ int ret;
+
+ idq.dqb_valid = 0;
+ if (cmd == Q_V1_SETQUOTA || cmd == Q_V1_SETQLIM) {
+ idq.dqb_ihardlimit = mdq->dqb_ihardlimit;
+ idq.dqb_isoftlimit = mdq->dqb_isoftlimit;
+ idq.dqb_bhardlimit = mdq->dqb_bhardlimit;
+ idq.dqb_bsoftlimit = mdq->dqb_bsoftlimit;
+ idq.dqb_valid |= QIF_LIMITS;
+ }
+ if (cmd == Q_V1_SETQUOTA || cmd == Q_V1_SETUSE) {
+ idq.dqb_curinodes = mdq->dqb_curinodes;
+ idq.dqb_curspace = ((qsize_t)mdq->dqb_curblocks) << QUOTABLOCK_BITS;
+ idq.dqb_valid |= QIF_USAGE;
+ }
+ ret = sb->s_qcop->set_dqblk(sb, type, id, &idq);
+ if (!ret && id == 0 && cmd == Q_V1_SETQUOTA) { /* Times for id 0 are in fact grace times */
+ struct if_dqinfo info;
+
+ info.dqi_bgrace = mdq->dqb_btime;
+ info.dqi_igrace = mdq->dqb_itime;
+ info.dqi_valid = IIF_BGRACE | IIF_IGRACE;
+ ret = sb->s_qcop->set_info(sb, type, &info);
+ }
+ return ret;
+}
+
+static void v1_get_stats(struct v1c_dqstats *dst)
+{
+ memcpy(dst, &dqstats, sizeof(dqstats));
+}
+
+/* Handle requests to old interface */
+static int do_compat_quotactl(struct super_block *sb, int type, int cmd, qid_t id, caddr_t addr)
+{
+ int ret;
+
+ switch (cmd) {
+ case Q_COMP_QUOTAON: {
+ char *pathname;
+
+ if (IS_ERR(pathname = getname(addr)))
+ return PTR_ERR(pathname);
+ ret = sb->s_qcop->quota_on(sb, type, QFMT_VFS_OLD, pathname);
+ putname(pathname);
+ return ret;
+ }
+ case Q_COMP_QUOTAOFF:
+ return sb->s_qcop->quota_off(sb, type);
+ case Q_COMP_SYNC:
+ if (sb)
+ return sb->s_qcop->quota_sync(sb, type);
+ sync_dquots_dev(NODEV, type);
+ return 0;
+ case Q_V1_RSQUASH: {
+ int flag;
+
+ if (copy_from_user(&flag, addr, sizeof(flag)))
+ return -EFAULT;
+ return v1_set_rsquash(sb, type, flag);
+ }
+ case Q_V1_GETQUOTA: {
+ struct v1c_mem_dqblk mdq;
+
+ if ((ret = v1_get_dqblk(sb, type, id, &mdq)))
+ return ret;
+ if (copy_to_user(addr, &mdq, sizeof(mdq)))
+ return -EFAULT;
+ return 0;
+ }
+ case Q_V1_SETQLIM:
+ case Q_V1_SETUSE:
+ case Q_V1_SETQUOTA: {
+ struct v1c_mem_dqblk mdq;
+
+ if (copy_from_user(&mdq, addr, sizeof(mdq)))
+ return -EFAULT;
+ return v1_set_dqblk(sb, type, cmd, id, &mdq);
+ }
+ case Q_V1_GETSTATS: {
+ struct v1c_dqstats dst;
+
+ v1_get_stats(&dst);
+ if (copy_to_user(addr, &dst, sizeof(dst)))
+ return -EFAULT;
+ return 0;
+ }
+ }
+ BUG();
+ return 0;
+}
+
+/* Macros for short-circuiting the compatibility tests */
+#define NEW_COMMAND(c) ((c) & (0x80 << 16))
+#define XQM_COMMAND(c) (((c) & ('X' << 8)) == ('X' << 8))
+
+/*
+ * This is the system call interface. This communicates with
+ * the user-level programs. Currently this only supports diskquota
+ * calls. Maybe we need to add the process quotas etc. in the future,
+ * but we probably should use rlimits for that.
+ */
+asmlinkage long sys_quotactl(unsigned int cmd, const char *special, qid_t id, caddr_t addr)
+{
+ uint cmds, type;
+ struct super_block *sb = NULL;
+ int ret = -EINVAL;
+
+ lock_kernel();
+ cmds = cmd >> SUBCMDSHIFT;
+ type = cmd & SUBCMDMASK;
+
+ if (cmds != Q_V1_GETSTATS && cmds != Q_V2_GETSTATS && IS_ERR(sb = resolve_dev(special))) {
+ ret = PTR_ERR(sb);
+ sb = NULL;
+ goto out;
+ }
+ if (!NEW_COMMAND(cmds) && !XQM_COMMAND(cmds)) {
+ if ((ret = check_compat_quotactl_valid(sb, type, cmds, id)) < 0)
+ goto out;
+ ret = do_compat_quotactl(sb, type, cmds, id, addr);
+ goto out;
+ }
+ if ((ret = check_quotactl_valid(sb, type, cmds, id)) < 0)
+ goto out;
+ ret = do_quotactl(sb, type, cmds, id, addr);
+out:
+ if (sb)
+ drop_super(sb);
+ unlock_kernel();
+ return ret;
+}
diff --git a/fs/quota_v1.c b/fs/quota_v1.c
new file mode 100644
index 00000000000000..27a73bf7956de9
--- /dev/null
+++ b/fs/quota_v1.c
@@ -0,0 +1,240 @@
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/quota.h>
+#include <linux/dqblk_v1.h>
+#include <linux/quotaio_v1.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include <asm/uaccess.h>
+#include <asm/byteorder.h>
+
+static void v1_disk2mem_dqblk(struct mem_dqblk *m, struct v1_disk_dqblk *d)
+{
+ m->dqb_ihardlimit = d->dqb_ihardlimit;
+ m->dqb_isoftlimit = d->dqb_isoftlimit;
+ m->dqb_curinodes = d->dqb_curinodes;
+ m->dqb_bhardlimit = d->dqb_bhardlimit;
+ m->dqb_bsoftlimit = d->dqb_bsoftlimit;
+ m->dqb_curspace = ((qsize_t)d->dqb_curblocks) << QUOTABLOCK_BITS;
+ m->dqb_itime = d->dqb_itime;
+ m->dqb_btime = d->dqb_btime;
+}
+
+static void v1_mem2disk_dqblk(struct v1_disk_dqblk *d, struct mem_dqblk *m)
+{
+ d->dqb_ihardlimit = m->dqb_ihardlimit;
+ d->dqb_isoftlimit = m->dqb_isoftlimit;
+ d->dqb_curinodes = m->dqb_curinodes;
+ d->dqb_bhardlimit = m->dqb_bhardlimit;
+ d->dqb_bsoftlimit = m->dqb_bsoftlimit;
+ d->dqb_curblocks = toqb(m->dqb_curspace);
+ d->dqb_itime = m->dqb_itime;
+ d->dqb_btime = m->dqb_btime;
+}
+
+static int v1_read_dqblk(struct dquot *dquot)
+{
+ int type = dquot->dq_type;
+ struct file *filp;
+ mm_segment_t fs;
+ loff_t offset;
+ struct v1_disk_dqblk dqblk;
+
+ filp = sb_dqopt(dquot->dq_sb)->files[type];
+ if (filp == (struct file *)NULL)
+ return -EINVAL;
+
+ memset(&dqblk, 0, sizeof(dqblk)); /* Initialize buffer in case file is too short */
+ /* Now we are sure filp is valid */
+ offset = v1_dqoff(dquot->dq_id);
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+ filp->f_op->read(filp, (char *)&dqblk, sizeof(struct v1_disk_dqblk), &offset);
+ set_fs(fs);
+
+ v1_disk2mem_dqblk(&dquot->dq_dqb, &dqblk);
+ if (dquot->dq_dqb.dqb_bhardlimit == 0 && dquot->dq_dqb.dqb_bsoftlimit == 0 &&
+ dquot->dq_dqb.dqb_ihardlimit == 0 && dquot->dq_dqb.dqb_isoftlimit == 0)
+ dquot->dq_flags |= DQ_FAKE;
+ dqstats.reads++;
+ return 0;
+}
+
+static int v1_commit_dqblk(struct dquot *dquot)
+{
+ short type = dquot->dq_type;
+ struct file *filp;
+ mm_segment_t fs;
+ loff_t offset;
+ ssize_t ret;
+ struct v1_disk_dqblk dqblk;
+
+ filp = sb_dqopt(dquot->dq_sb)->files[type];
+ offset = v1_dqoff(dquot->dq_id);
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ /*
+ * Note: clear the DQ_MOD flag unconditionally,
+ * so we don't loop forever on failure.
+ */
+ v1_mem2disk_dqblk(&dqblk, &dquot->dq_dqb);
+ dquot->dq_flags &= ~DQ_MOD;
+ if (dquot->dq_id == 0) {
+ dqblk.dqb_btime = sb_dqopt(dquot->dq_sb)->info[type].dqi_bgrace;
+ dqblk.dqb_itime = sb_dqopt(dquot->dq_sb)->info[type].dqi_igrace;
+ }
+ ret = 0;
+ if (filp)
+ ret = filp->f_op->write(filp, (char *)&dqblk,
+ sizeof(struct v1_disk_dqblk), &offset);
+ if (ret != sizeof(struct v1_disk_dqblk)) {
+ printk(KERN_WARNING "VFS: dquota write failed on dev %s\n",
+ kdevname(dquot->dq_dev));
+ if (ret >= 0)
+ ret = -EIO;
+ goto out;
+ }
+ ret = 0;
+
+out:
+ set_fs(fs);
+ dqstats.writes++;
+ return ret;
+}
+
+/* Magics of new quota format */
+#define V2_INITQMAGICS {\
+ 0xd9c01f11, /* USRQUOTA */\
+ 0xd9c01927 /* GRPQUOTA */\
+}
+
+/* Header of new quota format */
+struct v2_disk_dqheader {
+ __u32 dqh_magic; /* Magic number identifying file */
+ __u32 dqh_version; /* File version */
+};
+
+static int v1_check_quota_file(struct super_block *sb, int type)
+{
+ struct file *f = sb_dqopt(sb)->files[type];
+ struct inode *inode = f->f_dentry->d_inode;
+ ulong blocks;
+ size_t off;
+ struct v2_disk_dqheader dqhead;
+ mm_segment_t fs;
+ ssize_t size;
+ loff_t offset = 0;
+ static const uint quota_magics[] = V2_INITQMAGICS;
+
+ if (!inode->i_size)
+ return 0;
+ blocks = inode->i_size >> BLOCK_SIZE_BITS;
+ off = inode->i_size & (BLOCK_SIZE - 1);
+ if ((blocks % sizeof(struct v1_disk_dqblk) * BLOCK_SIZE + off) % sizeof(struct v1_disk_dqblk))
+ return 0;
+ /* Doublecheck whether we didn't get file with new format - with old quotactl() this could happen */
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+ size = f->f_op->read(f, (char *)&dqhead, sizeof(struct v2_disk_dqheader), &offset);
+ set_fs(fs);
+ if (size != sizeof(struct v2_disk_dqheader))
+ return 1; /* Probably not new format */
+ if (le32_to_cpu(dqhead.dqh_magic) != quota_magics[type])
+ return 1; /* Definitely not new format */
+ printk(KERN_INFO "VFS: %s: Refusing to turn on old quota format on given file. It probably contains newer quota format.\n", kdevname(sb->s_dev));
+ return 0; /* Seems like a new format file -> refuse it */
+}
+
+static int v1_read_file_info(struct super_block *sb, int type)
+{
+ struct quota_info *dqopt = sb_dqopt(sb);
+ mm_segment_t fs;
+ loff_t offset;
+ struct file *filp = dqopt->files[type];
+ struct v1_disk_dqblk dqblk;
+ int ret;
+
+ down(&dqopt->dqio_sem);
+ offset = v1_dqoff(0);
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+ if ((ret = filp->f_op->read(filp, (char *)&dqblk, sizeof(struct v1_disk_dqblk), &offset)) != sizeof(struct v1_disk_dqblk)) {
+ if (ret >= 0)
+ ret = -EIO;
+ goto out;
+ }
+ ret = 0;
+ dqopt->info[type].dqi_igrace = dqblk.dqb_itime ? dqblk.dqb_itime : MAX_IQ_TIME;
+ dqopt->info[type].dqi_bgrace = dqblk.dqb_btime ? dqblk.dqb_btime : MAX_DQ_TIME;
+out:
+ up(&dqopt->dqio_sem);
+ set_fs(fs);
+ return ret;
+}
+
+static int v1_write_file_info(struct super_block *sb, int type)
+{
+ struct quota_info *dqopt = sb_dqopt(sb);
+ mm_segment_t fs;
+ struct file *filp = dqopt->files[type];
+ struct v1_disk_dqblk dqblk;
+ loff_t offset;
+ int ret;
+
+ down(&dqopt->dqio_sem);
+ dqopt->info[type].dqi_flags &= ~DQF_INFO_DIRTY;
+ offset = v1_dqoff(0);
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+ if ((ret = filp->f_op->read(filp, (char *)&dqblk, sizeof(struct v1_disk_dqblk), &offset)) != sizeof(struct v1_disk_dqblk)) {
+ if (ret >= 0)
+ ret = -EIO;
+ goto out;
+ }
+ dqblk.dqb_itime = dqopt->info[type].dqi_igrace;
+ dqblk.dqb_btime = dqopt->info[type].dqi_bgrace;
+ offset = v1_dqoff(0);
+ ret = filp->f_op->write(filp, (char *)&dqblk, sizeof(struct v1_disk_dqblk), &offset);
+ if (ret == sizeof(struct v1_disk_dqblk))
+ ret = 0;
+ else if (ret > 0)
+ ret = -EIO;
+out:
+ up(&dqopt->dqio_sem);
+ set_fs(fs);
+ return ret;
+}
+
+static struct quota_format_ops v1_format_ops = {
+ check_quota_file: v1_check_quota_file,
+ read_file_info: v1_read_file_info,
+ write_file_info: v1_write_file_info,
+ free_file_info: NULL,
+ read_dqblk: v1_read_dqblk,
+ commit_dqblk: v1_commit_dqblk,
+};
+
+static struct quota_format_type v1_quota_format = {
+ qf_fmt_id: QFMT_VFS_OLD,
+ qf_ops: &v1_format_ops,
+ qf_owner: THIS_MODULE
+};
+
+static int __init init_v1_quota_format(void)
+{
+ return register_quota_format(&v1_quota_format);
+}
+
+static void __exit exit_v1_quota_format(void)
+{
+ unregister_quota_format(&v1_quota_format);
+}
+
+EXPORT_NO_SYMBOLS;
+
+module_init(init_v1_quota_format);
+module_exit(exit_v1_quota_format);
+
diff --git a/fs/quota_v2.c b/fs/quota_v2.c
new file mode 100644
index 00000000000000..87ffecac9e0ed4
--- /dev/null
+++ b/fs/quota_v2.c
@@ -0,0 +1,690 @@
+/*
+ * vfsv0 quota IO operations on file
+ */
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/dqblk_v2.h>
+#include <linux/quotaio_v2.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+
+#define __QUOTA_V2_PARANOIA
+
+typedef char *dqbuf_t;
+
+#define GETIDINDEX(id, depth) (((id) >> ((V2_DQTREEDEPTH-(depth)-1)*8)) & 0xff)
+#define GETENTRIES(buf) ((struct v2_disk_dqblk *)(((char *)buf)+sizeof(struct v2_disk_dqdbheader)))
+
+/* Check whether given file is really vfsv0 quotafile */
+static int v2_check_quota_file(struct super_block *sb, int type)
+{
+ struct v2_disk_dqheader dqhead;
+ struct file *f = sb_dqopt(sb)->files[type];
+ mm_segment_t fs;
+ ssize_t size;
+ loff_t offset = 0;
+ static const uint quota_magics[] = V2_INITQMAGICS;
+ static const uint quota_versions[] = V2_INITQVERSIONS;
+
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+ size = f->f_op->read(f, (char *)&dqhead, sizeof(struct v2_disk_dqheader), &offset);
+ set_fs(fs);
+ if (size != sizeof(struct v2_disk_dqheader))
+ return 0;
+ if (le32_to_cpu(dqhead.dqh_magic) != quota_magics[type] ||
+ le32_to_cpu(dqhead.dqh_version) != quota_versions[type])
+ return 0;
+ return 1;
+}
+
+/* Read information header from quota file */
+static int v2_read_file_info(struct super_block *sb, int type)
+{
+ mm_segment_t fs;
+ struct v2_disk_dqinfo dinfo;
+ struct mem_dqinfo *info = sb_dqopt(sb)->info+type;
+ struct file *f = sb_dqopt(sb)->files[type];
+ ssize_t size;
+ loff_t offset = V2_DQINFOOFF;
+
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+ size = f->f_op->read(f, (char *)&dinfo, sizeof(struct v2_disk_dqinfo), &offset);
+ set_fs(fs);
+ if (size != sizeof(struct v2_disk_dqinfo)) {
+ printk(KERN_WARNING "Can't read info structure on device %s.\n",
+ kdevname(f->f_dentry->d_sb->s_dev));
+ return -1;
+ }
+ info->dqi_bgrace = le32_to_cpu(dinfo.dqi_bgrace);
+ info->dqi_igrace = le32_to_cpu(dinfo.dqi_igrace);
+ info->dqi_flags = le32_to_cpu(dinfo.dqi_flags);
+ info->u.v2_i.dqi_blocks = le32_to_cpu(dinfo.dqi_blocks);
+ info->u.v2_i.dqi_free_blk = le32_to_cpu(dinfo.dqi_free_blk);
+ info->u.v2_i.dqi_free_entry = le32_to_cpu(dinfo.dqi_free_entry);
+ return 0;
+}
+
+/* Write information header to quota file */
+static int v2_write_file_info(struct super_block *sb, int type)
+{
+ mm_segment_t fs;
+ struct v2_disk_dqinfo dinfo;
+ struct mem_dqinfo *info = sb_dqopt(sb)->info+type;
+ struct file *f = sb_dqopt(sb)->files[type];
+ ssize_t size;
+ loff_t offset = V2_DQINFOOFF;
+
+ info->dqi_flags &= ~DQF_INFO_DIRTY;
+ dinfo.dqi_bgrace = cpu_to_le32(info->dqi_bgrace);
+ dinfo.dqi_igrace = cpu_to_le32(info->dqi_igrace);
+ dinfo.dqi_flags = cpu_to_le32(info->dqi_flags & DQF_MASK);
+ dinfo.dqi_blocks = cpu_to_le32(info->u.v2_i.dqi_blocks);
+ dinfo.dqi_free_blk = cpu_to_le32(info->u.v2_i.dqi_free_blk);
+ dinfo.dqi_free_entry = cpu_to_le32(info->u.v2_i.dqi_free_entry);
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+ size = f->f_op->write(f, (char *)&dinfo, sizeof(struct v2_disk_dqinfo), &offset);
+ set_fs(fs);
+ if (size != sizeof(struct v2_disk_dqinfo)) {
+ printk(KERN_WARNING "Can't write info structure on device %s.\n",
+ kdevname(f->f_dentry->d_sb->s_dev));
+ return -1;
+ }
+ return 0;
+}
+
+static void disk2memdqb(struct mem_dqblk *m, struct v2_disk_dqblk *d)
+{
+ m->dqb_ihardlimit = le32_to_cpu(d->dqb_ihardlimit);
+ m->dqb_isoftlimit = le32_to_cpu(d->dqb_isoftlimit);
+ m->dqb_curinodes = le32_to_cpu(d->dqb_curinodes);
+ m->dqb_itime = le64_to_cpu(d->dqb_itime);
+ m->dqb_bhardlimit = le32_to_cpu(d->dqb_bhardlimit);
+ m->dqb_bsoftlimit = le32_to_cpu(d->dqb_bsoftlimit);
+ m->dqb_curspace = le64_to_cpu(d->dqb_curspace);
+ m->dqb_btime = le64_to_cpu(d->dqb_btime);
+}
+
+static void mem2diskdqb(struct v2_disk_dqblk *d, struct mem_dqblk *m, qid_t id)
+{
+ d->dqb_ihardlimit = cpu_to_le32(m->dqb_ihardlimit);
+ d->dqb_isoftlimit = cpu_to_le32(m->dqb_isoftlimit);
+ d->dqb_curinodes = cpu_to_le32(m->dqb_curinodes);
+ d->dqb_itime = cpu_to_le64(m->dqb_itime);
+ d->dqb_bhardlimit = cpu_to_le32(m->dqb_bhardlimit);
+ d->dqb_bsoftlimit = cpu_to_le32(m->dqb_bsoftlimit);
+ d->dqb_curspace = cpu_to_le64(m->dqb_curspace);
+ d->dqb_btime = cpu_to_le64(m->dqb_btime);
+ d->dqb_id = cpu_to_le32(id);
+}
+
+static dqbuf_t getdqbuf(void)
+{
+ dqbuf_t buf = kmalloc(V2_DQBLKSIZE, GFP_KERNEL);
+ if (!buf)
+ printk(KERN_WARNING "VFS: Not enough memory for quota buffers.\n");
+ return buf;
+}
+
+static inline void freedqbuf(dqbuf_t buf)
+{
+ kfree(buf);
+}
+
+static ssize_t read_blk(struct file *filp, uint blk, dqbuf_t buf)
+{
+ mm_segment_t fs;
+ ssize_t ret;
+ loff_t offset = blk<<V2_DQBLKSIZE_BITS;
+
+ memset(buf, 0, V2_DQBLKSIZE);
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+ ret = filp->f_op->read(filp, (char *)buf, V2_DQBLKSIZE, &offset);
+ set_fs(fs);
+ return ret;
+}
+
+static ssize_t write_blk(struct file *filp, uint blk, dqbuf_t buf)
+{
+ mm_segment_t fs;
+ ssize_t ret;
+ loff_t offset = blk<<V2_DQBLKSIZE_BITS;
+
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+ ret = filp->f_op->write(filp, (char *)buf, V2_DQBLKSIZE, &offset);
+ set_fs(fs);
+ return ret;
+
+}
+
+/* Remove empty block from list and return it */
+static int get_free_dqblk(struct file *filp, struct mem_dqinfo *info)
+{
+ dqbuf_t buf = getdqbuf();
+ struct v2_disk_dqdbheader *dh = (struct v2_disk_dqdbheader *)buf;
+ int ret, blk;
+
+ if (!buf)
+ return -ENOMEM;
+ if (info->u.v2_i.dqi_free_blk) {
+ blk = info->u.v2_i.dqi_free_blk;
+ if ((ret = read_blk(filp, blk, buf)) < 0)
+ goto out_buf;
+ info->u.v2_i.dqi_free_blk = le32_to_cpu(dh->dqdh_next_free);
+ }
+ else {
+ memset(buf, 0, V2_DQBLKSIZE);
+ if ((ret = write_blk(filp, info->u.v2_i.dqi_blocks, buf)) < 0) /* Assure block allocation... */
+ goto out_buf;
+ blk = info->u.v2_i.dqi_blocks++;
+ }
+ mark_info_dirty(info);
+ ret = blk;
+out_buf:
+ freedqbuf(buf);
+ return ret;
+}
+
+/* Insert empty block to the list */
+static int put_free_dqblk(struct file *filp, struct mem_dqinfo *info, dqbuf_t buf, uint blk)
+{
+ struct v2_disk_dqdbheader *dh = (struct v2_disk_dqdbheader *)buf;
+ int err;
+
+ dh->dqdh_next_free = cpu_to_le32(info->u.v2_i.dqi_free_blk);
+ dh->dqdh_prev_free = cpu_to_le32(0);
+ dh->dqdh_entries = cpu_to_le16(0);
+ info->u.v2_i.dqi_free_blk = blk;
+ mark_info_dirty(info);
+ if ((err = write_blk(filp, blk, buf)) < 0) /* Some strange block. We had better leave it... */
+ return err;
+ return 0;
+}
+
+/* Remove given block from the list of blocks with free entries */
+static int remove_free_dqentry(struct file *filp, struct mem_dqinfo *info, dqbuf_t buf, uint blk)
+{
+ dqbuf_t tmpbuf = getdqbuf();
+ struct v2_disk_dqdbheader *dh = (struct v2_disk_dqdbheader *)buf;
+ uint nextblk = le32_to_cpu(dh->dqdh_next_free), prevblk = le32_to_cpu(dh->dqdh_prev_free);
+ int err;
+
+ if (!tmpbuf)
+ return -ENOMEM;
+ if (nextblk) {
+ if ((err = read_blk(filp, nextblk, tmpbuf)) < 0)
+ goto out_buf;
+ ((struct v2_disk_dqdbheader *)tmpbuf)->dqdh_prev_free = dh->dqdh_prev_free;
+ if ((err = write_blk(filp, nextblk, tmpbuf)) < 0)
+ goto out_buf;
+ }
+ if (prevblk) {
+ if ((err = read_blk(filp, prevblk, tmpbuf)) < 0)
+ goto out_buf;
+ ((struct v2_disk_dqdbheader *)tmpbuf)->dqdh_next_free = dh->dqdh_next_free;
+ if ((err = write_blk(filp, prevblk, tmpbuf)) < 0)
+ goto out_buf;
+ }
+ else {
+ info->u.v2_i.dqi_free_entry = nextblk;
+ mark_info_dirty(info);
+ }
+ freedqbuf(tmpbuf);
+ dh->dqdh_next_free = dh->dqdh_prev_free = cpu_to_le32(0);
+ if (write_blk(filp, blk, buf) < 0) /* No matter whether write succeeds block is out of list */
+ printk(KERN_ERR "VFS: Can't write block (%u) with free entries.\n", blk);
+ return 0;
+out_buf:
+ freedqbuf(tmpbuf);
+ return err;
+}
+
+/* Insert given block to the beginning of list with free entries */
+static int insert_free_dqentry(struct file *filp, struct mem_dqinfo *info, dqbuf_t buf, uint blk)
+{
+ dqbuf_t tmpbuf = getdqbuf();
+ struct v2_disk_dqdbheader *dh = (struct v2_disk_dqdbheader *)buf;
+ int err;
+
+ if (!tmpbuf)
+ return -ENOMEM;
+ dh->dqdh_next_free = cpu_to_le32(info->u.v2_i.dqi_free_entry);
+ dh->dqdh_prev_free = cpu_to_le32(0);
+ if ((err = write_blk(filp, blk, buf)) < 0)
+ goto out_buf;
+ if (info->u.v2_i.dqi_free_entry) {
+ if ((err = read_blk(filp, info->u.v2_i.dqi_free_entry, tmpbuf)) < 0)
+ goto out_buf;
+ ((struct v2_disk_dqdbheader *)tmpbuf)->dqdh_prev_free = cpu_to_le32(blk);
+ if ((err = write_blk(filp, info->u.v2_i.dqi_free_entry, tmpbuf)) < 0)
+ goto out_buf;
+ }
+ freedqbuf(tmpbuf);
+ info->u.v2_i.dqi_free_entry = blk;
+ mark_info_dirty(info);
+ return 0;
+out_buf:
+ freedqbuf(tmpbuf);
+ return err;
+}
+
+/* Find space for dquot */
+static uint find_free_dqentry(struct dquot *dquot, int *err)
+{
+ struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
+ struct mem_dqinfo *info = sb_dqopt(dquot->dq_sb)->info+dquot->dq_type;
+ uint blk, i;
+ struct v2_disk_dqdbheader *dh;
+ struct v2_disk_dqblk *ddquot;
+ struct v2_disk_dqblk fakedquot;
+ dqbuf_t buf;
+
+ *err = 0;
+ if (!(buf = getdqbuf())) {
+ *err = -ENOMEM;
+ return 0;
+ }
+ dh = (struct v2_disk_dqdbheader *)buf;
+ ddquot = GETENTRIES(buf);
+ if (info->u.v2_i.dqi_free_entry) {
+ blk = info->u.v2_i.dqi_free_entry;
+ if ((*err = read_blk(filp, blk, buf)) < 0)
+ goto out_buf;
+ }
+ else {
+ blk = get_free_dqblk(filp, info);
+ if ((int)blk < 0) {
+ *err = blk;
+ return 0;
+ }
+ memset(buf, 0, V2_DQBLKSIZE);
+ info->u.v2_i.dqi_free_entry = blk; /* This is enough as block is already zeroed and entry list is empty... */
+ mark_info_dirty(info);
+ }
+ if (le16_to_cpu(dh->dqdh_entries)+1 >= V2_DQSTRINBLK) /* Block will be full? */
+ if ((*err = remove_free_dqentry(filp, info, buf, blk)) < 0) {
+ printk(KERN_ERR "VFS: find_free_dqentry(): Can't remove block (%u) from entry free list.\n", blk);
+ goto out_buf;
+ }
+ dh->dqdh_entries = cpu_to_le16(le16_to_cpu(dh->dqdh_entries)+1);
+ memset(&fakedquot, 0, sizeof(struct v2_disk_dqblk));
+ /* Find free structure in block */
+ for (i = 0; i < V2_DQSTRINBLK && memcmp(&fakedquot, ddquot+i, sizeof(struct v2_disk_dqblk)); i++);
+#ifdef __QUOTA_V2_PARANOIA
+ if (i == V2_DQSTRINBLK) {
+ printk(KERN_ERR "VFS: find_free_dqentry(): Data block full but it shouldn't.\n");
+ *err = -EIO;
+ goto out_buf;
+ }
+#endif
+ if ((*err = write_blk(filp, blk, buf)) < 0) {
+ printk(KERN_ERR "VFS: find_free_dqentry(): Can't write quota data block %u.\n", blk);
+ goto out_buf;
+ }
+ dquot->dq_off = (blk<<V2_DQBLKSIZE_BITS)+sizeof(struct v2_disk_dqdbheader)+i*sizeof(struct v2_disk_dqblk);
+ freedqbuf(buf);
+ return blk;
+out_buf:
+ freedqbuf(buf);
+ return 0;
+}
+
+/* Insert reference to structure into the trie */
+static int do_insert_tree(struct dquot *dquot, uint *treeblk, int depth)
+{
+ struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
+ struct mem_dqinfo *info = sb_dqopt(dquot->dq_sb)->info + dquot->dq_type;
+ dqbuf_t buf;
+ int ret = 0, newson = 0, newact = 0;
+ u32 *ref;
+ uint newblk;
+
+ if (!(buf = getdqbuf()))
+ return -ENOMEM;
+ if (!*treeblk) {
+ ret = get_free_dqblk(filp, info);
+ if (ret < 0)
+ goto out_buf;
+ *treeblk = ret;
+ memset(buf, 0, V2_DQBLKSIZE);
+ newact = 1;
+ }
+ else {
+ if ((ret = read_blk(filp, *treeblk, buf)) < 0) {
+ printk(KERN_ERR "VFS: Can't read tree quota block %u.\n", *treeblk);
+ goto out_buf;
+ }
+ }
+ ref = (u32 *)buf;
+ newblk = le32_to_cpu(ref[GETIDINDEX(dquot->dq_id, depth)]);
+ if (!newblk)
+ newson = 1;
+ if (depth == V2_DQTREEDEPTH-1) {
+#ifdef __QUOTA_V2_PARANOIA
+ if (newblk) {
+ printk(KERN_ERR "VFS: Inserting already present quota entry (block %u).\n", ref[GETIDINDEX(dquot->dq_id, depth)]);
+ ret = -EIO;
+ goto out_buf;
+ }
+#endif
+ newblk = find_free_dqentry(dquot, &ret);
+ }
+ else
+ ret = do_insert_tree(dquot, &newblk, depth+1);
+ if (newson && ret >= 0) {
+ ref[GETIDINDEX(dquot->dq_id, depth)] = cpu_to_le32(newblk);
+ ret = write_blk(filp, *treeblk, buf);
+ }
+ else if (newact && ret < 0)
+ put_free_dqblk(filp, info, buf, *treeblk);
+out_buf:
+ freedqbuf(buf);
+ return ret;
+}
+
+/* Wrapper for inserting quota structure into tree */
+static inline int dq_insert_tree(struct dquot *dquot)
+{
+ int tmp = V2_DQTREEOFF;
+ return do_insert_tree(dquot, &tmp, 0);
+}
+
+/*
+ * We don't have to be afraid of deadlocks as we never have quotas on quota files...
+ */
+static int v2_write_dquot(struct dquot *dquot)
+{
+ int type = dquot->dq_type;
+ struct file *filp;
+ mm_segment_t fs;
+ loff_t offset;
+ ssize_t ret;
+ struct v2_disk_dqblk ddquot;
+
+ if (!dquot->dq_off)
+ if ((ret = dq_insert_tree(dquot)) < 0) {
+ printk(KERN_ERR "VFS: Error %d occured while creating quota.\n", ret);
+ return ret;
+ }
+ filp = sb_dqopt(dquot->dq_sb)->files[type];
+ offset = dquot->dq_off;
+ mem2diskdqb(&ddquot, &dquot->dq_dqb, dquot->dq_id);
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+ ret = filp->f_op->write(filp, (char *)&ddquot, sizeof(struct v2_disk_dqblk), &offset);
+ set_fs(fs);
+ if (ret != sizeof(struct v2_disk_dqblk)) {
+ printk(KERN_WARNING "VFS: dquota write failed on dev %s\n", kdevname(dquot->dq_dev));
+ if (ret >= 0)
+ ret = -ENOSPC;
+ }
+ else
+ ret = 0;
+ dqstats.writes++;
+ return ret;
+}
+
+/* Free dquot entry in data block */
+static int free_dqentry(struct dquot *dquot, uint blk)
+{
+ struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
+ struct mem_dqinfo *info = sb_dqopt(dquot->dq_sb)->info + dquot->dq_type;
+ struct v2_disk_dqdbheader *dh;
+ dqbuf_t buf = getdqbuf();
+ int ret = 0;
+
+ if (!buf)
+ return -ENOMEM;
+ if (dquot->dq_off >> V2_DQBLKSIZE_BITS != blk) {
+ printk(KERN_ERR "VFS: Quota structure has offset to other block (%u) than it should (%u).\n", blk, (uint)(dquot->dq_off >> V2_DQBLKSIZE_BITS));
+ goto out_buf;
+ }
+ if ((ret = read_blk(filp, blk, buf)) < 0) {
+ printk(KERN_ERR "VFS: Can't read quota data block %u\n", blk);
+ goto out_buf;
+ }
+ dh = (struct v2_disk_dqdbheader *)buf;
+ dh->dqdh_entries = cpu_to_le16(le16_to_cpu(dh->dqdh_entries)-1);
+ if (!le16_to_cpu(dh->dqdh_entries)) { /* Block got free? */
+ if ((ret = remove_free_dqentry(filp, info, buf, blk)) < 0 ||
+ (ret = put_free_dqblk(filp, info, buf, blk)) < 0) {
+ printk(KERN_ERR "VFS: Can't move quota data block (%u) to free list.\n", blk);
+ goto out_buf;
+ }
+ }
+ else {
+ memset(buf+(dquot->dq_off & ((1 << V2_DQBLKSIZE_BITS)-1)), 0, sizeof(struct v2_disk_dqblk));
+ if (le16_to_cpu(dh->dqdh_entries) == V2_DQSTRINBLK-1) {
+ /* Insert will write block itself */
+ if ((ret = insert_free_dqentry(filp, info, buf, blk)) < 0) {
+ printk(KERN_ERR "VFS: Can't insert quota data block (%u) to free entry list.\n", blk);
+ goto out_buf;
+ }
+ }
+ else
+ if ((ret = write_blk(filp, blk, buf)) < 0) {
+ printk(KERN_ERR "VFS: Can't write quota data block %u\n", blk);
+ goto out_buf;
+ }
+ }
+ dquot->dq_off = 0; /* Quota is now unattached */
+out_buf:
+ freedqbuf(buf);
+ return ret;
+}
+
+/* Remove reference to dquot from tree */
+static int remove_tree(struct dquot *dquot, uint *blk, int depth)
+{
+ struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
+ struct mem_dqinfo *info = sb_dqopt(dquot->dq_sb)->info + dquot->dq_type;
+ dqbuf_t buf = getdqbuf();
+ int ret = 0;
+ uint newblk;
+ u32 *ref = (u32 *)buf;
+
+ if (!buf)
+ return -ENOMEM;
+ if ((ret = read_blk(filp, *blk, buf)) < 0) {
+ printk(KERN_ERR "VFS: Can't read quota data block %u\n", *blk);
+ goto out_buf;
+ }
+ newblk = le32_to_cpu(ref[GETIDINDEX(dquot->dq_id, depth)]);
+ if (depth == V2_DQTREEDEPTH-1) {
+ ret = free_dqentry(dquot, newblk);
+ newblk = 0;
+ }
+ else
+ ret = remove_tree(dquot, &newblk, depth+1);
+ if (ret >= 0 && !newblk) {
+ int i;
+ ref[GETIDINDEX(dquot->dq_id, depth)] = cpu_to_le32(0);
+ for (i = 0; i < V2_DQBLKSIZE && !buf[i]; i++); /* Block got empty? */
+ if (i == V2_DQBLKSIZE) {
+ put_free_dqblk(filp, info, buf, *blk);
+ *blk = 0;
+ }
+ else
+ if ((ret = write_blk(filp, *blk, buf)) < 0)
+ printk(KERN_ERR "VFS: Can't write quota tree block %u.\n", *blk);
+ }
+out_buf:
+ freedqbuf(buf);
+ return ret;
+}
+
+/* Delete dquot from tree */
+static int v2_delete_dquot(struct dquot *dquot)
+{
+ uint tmp = V2_DQTREEOFF;
+
+ if (!dquot->dq_off) /* Even not allocated? */
+ return 0;
+ return remove_tree(dquot, &tmp, 0);
+}
+
+/* Find entry in block */
+static loff_t find_block_dqentry(struct dquot *dquot, uint blk)
+{
+ struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
+ dqbuf_t buf = getdqbuf();
+ loff_t ret = 0;
+ int i;
+ struct v2_disk_dqblk *ddquot = GETENTRIES(buf);
+
+ if (!buf)
+ return -ENOMEM;
+ if ((ret = read_blk(filp, blk, buf)) < 0) {
+ printk(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk);
+ goto out_buf;
+ }
+ if (dquot->dq_id)
+ for (i = 0; i < V2_DQSTRINBLK && le32_to_cpu(ddquot[i].dqb_id) != dquot->dq_id; i++);
+ else { /* ID 0 as a bit more complicated searching... */
+ struct v2_disk_dqblk fakedquot;
+
+ memset(&fakedquot, 0, sizeof(struct v2_disk_dqblk));
+ for (i = 0; i < V2_DQSTRINBLK; i++)
+ if (!le32_to_cpu(ddquot[i].dqb_id) && memcmp(&fakedquot, ddquot+i, sizeof(struct v2_disk_dqblk)))
+ break;
+ }
+ if (i == V2_DQSTRINBLK) {
+ printk(KERN_ERR "VFS: Quota for id %u referenced but not present.\n", dquot->dq_id);
+ ret = -EIO;
+ goto out_buf;
+ }
+ else
+ ret = (blk << V2_DQBLKSIZE_BITS) + sizeof(struct v2_disk_dqdbheader) + i * sizeof(struct v2_disk_dqblk);
+out_buf:
+ freedqbuf(buf);
+ return ret;
+}
+
+/* Find entry for given id in the tree */
+static loff_t find_tree_dqentry(struct dquot *dquot, uint blk, int depth)
+{
+ struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
+ dqbuf_t buf = getdqbuf();
+ loff_t ret = 0;
+ u32 *ref = (u32 *)buf;
+
+ if (!buf)
+ return -ENOMEM;
+ if ((ret = read_blk(filp, blk, buf)) < 0) {
+ printk(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk);
+ goto out_buf;
+ }
+ ret = 0;
+ blk = le32_to_cpu(ref[GETIDINDEX(dquot->dq_id, depth)]);
+ if (!blk) /* No reference? */
+ goto out_buf;
+ if (depth < V2_DQTREEDEPTH-1)
+ ret = find_tree_dqentry(dquot, blk, depth+1);
+ else
+ ret = find_block_dqentry(dquot, blk);
+out_buf:
+ freedqbuf(buf);
+ return ret;
+}
+
+/* Find entry for given id in the tree - wrapper function */
+static inline loff_t find_dqentry(struct dquot *dquot)
+{
+ return find_tree_dqentry(dquot, V2_DQTREEOFF, 0);
+}
+
+static int v2_read_dquot(struct dquot *dquot)
+{
+ int type = dquot->dq_type;
+ struct file *filp;
+ mm_segment_t fs;
+ loff_t offset;
+ struct v2_disk_dqblk ddquot;
+ int ret = 0;
+
+ filp = sb_dqopt(dquot->dq_sb)->files[type];
+
+#ifdef __QUOTA_V2_PARANOIA
+ if (!filp || !dquot->dq_sb) { /* Invalidated quota? */
+ printk(KERN_ERR "VFS: Quota invalidated while reading!\n");
+ return -EIO;
+ }
+#endif
+ offset = find_dqentry(dquot);
+ if (offset <= 0) { /* Entry not present? */
+ if (offset < 0)
+ printk(KERN_ERR "VFS: Can't read quota structure for id %u.\n", dquot->dq_id);
+ dquot->dq_off = 0;
+ dquot->dq_flags |= DQ_FAKE;
+ memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk));
+ ret = offset;
+ }
+ else {
+ dquot->dq_off = offset;
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+ if ((ret = filp->f_op->read(filp, (char *)&ddquot, sizeof(struct v2_disk_dqblk), &offset)) != sizeof(struct v2_disk_dqblk)) {
+ if (ret >= 0)
+ ret = -EIO;
+ printk(KERN_ERR "VFS: Error while reading quota structure for id %u.\n", dquot->dq_id);
+ memset(&ddquot, 0, sizeof(struct v2_disk_dqblk));
+ }
+ else
+ ret = 0;
+ set_fs(fs);
+ disk2memdqb(&dquot->dq_dqb, &ddquot);
+ }
+ dqstats.reads++;
+ return ret;
+}
+
+/* Commit changes of dquot to disk - it might also mean deleting it when quota became fake one and user has no blocks... */
+static int v2_commit_dquot(struct dquot *dquot)
+{
+ /* We clear the flag everytime so we don't loop when there was an IO error... */
+ dquot->dq_flags &= ~DQ_MOD;
+ if (dquot->dq_flags & DQ_FAKE && !(dquot->dq_dqb.dqb_curinodes | dquot->dq_dqb.dqb_curspace))
+ return v2_delete_dquot(dquot);
+ else
+ return v2_write_dquot(dquot);
+}
+
+static struct quota_format_ops v2_format_ops = {
+ check_quota_file: v2_check_quota_file,
+ read_file_info: v2_read_file_info,
+ write_file_info: v2_write_file_info,
+ free_file_info: NULL,
+ read_dqblk: v2_read_dquot,
+ commit_dqblk: v2_commit_dquot,
+};
+
+static struct quota_format_type v2_quota_format = {
+ qf_fmt_id: QFMT_VFS_V0,
+ qf_ops: &v2_format_ops,
+ qf_owner: THIS_MODULE
+};
+
+static int __init init_v2_quota_format(void)
+{
+ return register_quota_format(&v2_quota_format);
+}
+
+static void __exit exit_v2_quota_format(void)
+{
+ unregister_quota_format(&v2_quota_format);
+}
+
+EXPORT_NO_SYMBOLS;
+
+module_init(init_v2_quota_format);
+module_exit(exit_v2_quota_format);
diff --git a/fs/ramfs/Makefile b/fs/ramfs/Makefile
new file mode 100644
index 00000000000000..44ff24f061fc5f
--- /dev/null
+++ b/fs/ramfs/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for the linux ramfs routines.
+#
+
+O_TARGET := ramfs.o
+
+obj-y := inode.o
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/ramfs/inode.c b/fs/ramfs/inode.c
new file mode 100644
index 00000000000000..208a520e5bb2ad
--- /dev/null
+++ b/fs/ramfs/inode.c
@@ -0,0 +1,349 @@
+/*
+ * Resizable simple ram filesystem for Linux.
+ *
+ * Copyright (C) 2000 Linus Torvalds.
+ * 2000 Transmeta Corp.
+ *
+ * Usage limits added by David Gibson, Linuxcare Australia.
+ * This file is released under the GPL.
+ */
+
+/*
+ * NOTE! This filesystem is probably most useful
+ * not as a real filesystem, but as an example of
+ * how virtual filesystems can be written.
+ *
+ * It doesn't get much simpler than this. Consider
+ * that this file implements the full semantics of
+ * a POSIX-compliant read-write filesystem.
+ *
+ * Note in particular how the filesystem does not
+ * need to implement any data structures of its own
+ * to keep track of the virtual data: using the VFS
+ * caches is sufficient.
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/pagemap.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+
+#include <asm/uaccess.h>
+
+/* some random number */
+#define RAMFS_MAGIC 0x858458f6
+
+static struct super_operations ramfs_ops;
+static struct address_space_operations ramfs_aops;
+static struct file_operations ramfs_file_operations;
+static struct inode_operations ramfs_dir_inode_operations;
+
+static int ramfs_statfs(struct super_block *sb, struct statfs *buf)
+{
+ buf->f_type = RAMFS_MAGIC;
+ buf->f_bsize = PAGE_CACHE_SIZE;
+ buf->f_namelen = NAME_MAX;
+ return 0;
+}
+
+/*
+ * Lookup the data. This is trivial - if the dentry didn't already
+ * exist, we know it is negative.
+ */
+static struct dentry * ramfs_lookup(struct inode *dir, struct dentry *dentry)
+{
+ if (dentry->d_name.len > NAME_MAX)
+ return ERR_PTR(-ENAMETOOLONG);
+ d_add(dentry, NULL);
+ return NULL;
+}
+
+/*
+ * Read a page. Again trivial. If it didn't already exist
+ * in the page cache, it is zero-filled.
+ */
+static int ramfs_readpage(struct file *file, struct page * page)
+{
+ if (!Page_Uptodate(page)) {
+ memset(kmap(page), 0, PAGE_CACHE_SIZE);
+ kunmap(page);
+ flush_dcache_page(page);
+ SetPageUptodate(page);
+ }
+ UnlockPage(page);
+ return 0;
+}
+
+static int ramfs_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to)
+{
+ void *addr = kmap(page);
+ if (!Page_Uptodate(page)) {
+ memset(addr, 0, PAGE_CACHE_SIZE);
+ flush_dcache_page(page);
+ SetPageUptodate(page);
+ }
+ SetPageDirty(page);
+ return 0;
+}
+
+static int ramfs_commit_write(struct file *file, struct page *page, unsigned offset, unsigned to)
+{
+ struct inode *inode = page->mapping->host;
+ loff_t pos = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to;
+
+ kunmap(page);
+ if (pos > inode->i_size)
+ inode->i_size = pos;
+ return 0;
+}
+
+struct inode *ramfs_get_inode(struct super_block *sb, int mode, int dev)
+{
+ struct inode * inode = new_inode(sb);
+
+ if (inode) {
+ inode->i_mode = mode;
+ inode->i_uid = current->fsuid;
+ inode->i_gid = current->fsgid;
+ inode->i_blksize = PAGE_CACHE_SIZE;
+ inode->i_blocks = 0;
+ inode->i_rdev = NODEV;
+ inode->i_mapping->a_ops = &ramfs_aops;
+ inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ switch (mode & S_IFMT) {
+ default:
+ init_special_inode(inode, mode, dev);
+ break;
+ case S_IFREG:
+ inode->i_fop = &ramfs_file_operations;
+ break;
+ case S_IFDIR:
+ inode->i_op = &ramfs_dir_inode_operations;
+ inode->i_fop = &dcache_dir_ops;
+ break;
+ case S_IFLNK:
+ inode->i_op = &page_symlink_inode_operations;
+ break;
+ }
+ }
+ return inode;
+}
+
+/*
+ * File creation. Allocate an inode, and we're done..
+ */
+static int ramfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int dev)
+{
+ struct inode * inode = ramfs_get_inode(dir->i_sb, mode, dev);
+ int error = -ENOSPC;
+
+ if (inode) {
+ if (dir->i_mode & S_ISGID) {
+ inode->i_gid = dir->i_gid;
+ if (S_ISDIR(mode))
+ inode->i_mode |= S_ISGID;
+ }
+ d_instantiate(dentry, inode);
+ dget(dentry); /* Extra count - pin the dentry in core */
+ error = 0;
+ }
+ return error;
+}
+
+static int ramfs_mkdir(struct inode * dir, struct dentry * dentry, int mode)
+{
+ return ramfs_mknod(dir, dentry, mode | S_IFDIR, 0);
+}
+
+static int ramfs_create(struct inode *dir, struct dentry *dentry, int mode)
+{
+ return ramfs_mknod(dir, dentry, mode | S_IFREG, 0);
+}
+
+/*
+ * Link a file..
+ */
+static int ramfs_link(struct dentry *old_dentry, struct inode * dir, struct dentry * dentry)
+{
+ struct inode *inode = old_dentry->d_inode;
+
+ if (S_ISDIR(inode->i_mode))
+ return -EPERM;
+
+ inode->i_nlink++;
+ atomic_inc(&inode->i_count); /* New dentry reference */
+ dget(dentry); /* Extra pinning count for the created dentry */
+ d_instantiate(dentry, inode);
+ return 0;
+}
+
+static inline int ramfs_positive(struct dentry *dentry)
+{
+ return dentry->d_inode && !d_unhashed(dentry);
+}
+
+/*
+ * Check that a directory is empty (this works
+ * for regular files too, they'll just always be
+ * considered empty..).
+ *
+ * Note that an empty directory can still have
+ * children, they just all have to be negative..
+ */
+static int ramfs_empty(struct dentry *dentry)
+{
+ struct list_head *list;
+
+ spin_lock(&dcache_lock);
+ list = dentry->d_subdirs.next;
+
+ while (list != &dentry->d_subdirs) {
+ struct dentry *de = list_entry(list, struct dentry, d_child);
+
+ if (ramfs_positive(de)) {
+ spin_unlock(&dcache_lock);
+ return 0;
+ }
+ list = list->next;
+ }
+ spin_unlock(&dcache_lock);
+ return 1;
+}
+
+/*
+ * This works for both directories and regular files.
+ * (non-directories will always have empty subdirs)
+ */
+static int ramfs_unlink(struct inode * dir, struct dentry *dentry)
+{
+ int retval = -ENOTEMPTY;
+
+ if (ramfs_empty(dentry)) {
+ struct inode *inode = dentry->d_inode;
+
+ inode->i_nlink--;
+ dput(dentry); /* Undo the count from "create" - this does all the work */
+ retval = 0;
+ }
+ return retval;
+}
+
+#define ramfs_rmdir ramfs_unlink
+
+/*
+ * The VFS layer already does all the dentry stuff for rename,
+ * we just have to decrement the usage count for the target if
+ * it exists so that the VFS layer correctly free's it when it
+ * gets overwritten.
+ */
+static int ramfs_rename(struct inode * old_dir, struct dentry *old_dentry, struct inode * new_dir,struct dentry *new_dentry)
+{
+ int error = -ENOTEMPTY;
+
+ if (ramfs_empty(new_dentry)) {
+ struct inode *inode = new_dentry->d_inode;
+ if (inode) {
+ inode->i_nlink--;
+ dput(new_dentry);
+ }
+ error = 0;
+ }
+ return error;
+}
+
+static int ramfs_symlink(struct inode * dir, struct dentry *dentry, const char * symname)
+{
+ int error;
+
+ error = ramfs_mknod(dir, dentry, S_IFLNK | S_IRWXUGO, 0);
+ if (!error) {
+ int l = strlen(symname)+1;
+ struct inode *inode = dentry->d_inode;
+ error = block_symlink(inode, symname, l);
+ }
+ return error;
+}
+
+static int ramfs_sync_file(struct file * file, struct dentry *dentry, int datasync)
+{
+ return 0;
+}
+
+static struct address_space_operations ramfs_aops = {
+ readpage: ramfs_readpage,
+ writepage: fail_writepage,
+ prepare_write: ramfs_prepare_write,
+ commit_write: ramfs_commit_write
+};
+
+static struct file_operations ramfs_file_operations = {
+ read: generic_file_read,
+ write: generic_file_write,
+ mmap: generic_file_mmap,
+ fsync: ramfs_sync_file,
+};
+
+static struct inode_operations ramfs_dir_inode_operations = {
+ create: ramfs_create,
+ lookup: ramfs_lookup,
+ link: ramfs_link,
+ unlink: ramfs_unlink,
+ symlink: ramfs_symlink,
+ mkdir: ramfs_mkdir,
+ rmdir: ramfs_rmdir,
+ mknod: ramfs_mknod,
+ rename: ramfs_rename,
+};
+
+static struct super_operations ramfs_ops = {
+ statfs: ramfs_statfs,
+ put_inode: force_delete,
+};
+
+static struct super_block *ramfs_read_super(struct super_block * sb, void * data, int silent)
+{
+ struct inode * inode;
+ struct dentry * root;
+
+ sb->s_blocksize = PAGE_CACHE_SIZE;
+ sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+ sb->s_magic = RAMFS_MAGIC;
+ sb->s_op = &ramfs_ops;
+ inode = ramfs_get_inode(sb, S_IFDIR | 0755, 0);
+ if (!inode)
+ return NULL;
+
+ root = d_alloc_root(inode);
+ if (!root) {
+ iput(inode);
+ return NULL;
+ }
+ sb->s_root = root;
+ return sb;
+}
+
+static DECLARE_FSTYPE(ramfs_fs_type, "ramfs", ramfs_read_super, FS_LITTER);
+static DECLARE_FSTYPE(rootfs_fs_type, "rootfs", ramfs_read_super, FS_NOMOUNT|FS_LITTER);
+
+static int __init init_ramfs_fs(void)
+{
+ return register_filesystem(&ramfs_fs_type);
+}
+
+static void __exit exit_ramfs_fs(void)
+{
+ unregister_filesystem(&ramfs_fs_type);
+}
+
+module_init(init_ramfs_fs)
+module_exit(exit_ramfs_fs)
+
+int __init init_rootfs(void)
+{
+ return register_filesystem(&rootfs_fs_type);
+}
+
+MODULE_LICENSE("GPL");
+
diff --git a/fs/read_write.c b/fs/read_write.c
new file mode 100644
index 00000000000000..b0026c4ae31505
--- /dev/null
+++ b/fs/read_write.c
@@ -0,0 +1,452 @@
+/*
+ * linux/fs/read_write.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ * Minor pieces Copyright (C) 2002 Red Hat Inc, All Rights Reserved
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/file.h>
+#include <linux/uio.h>
+#include <linux/smp_lock.h>
+#include <linux/dnotify.h>
+
+#include <asm/uaccess.h>
+
+struct file_operations generic_ro_fops = {
+ llseek: generic_file_llseek,
+ read: generic_file_read,
+ mmap: generic_file_mmap,
+};
+
+ssize_t generic_read_dir(struct file *filp, char *buf, size_t siz, loff_t *ppos)
+{
+ return -EISDIR;
+}
+
+int rw_verify_area(int read_write, struct file *file, loff_t *ppos, size_t count)
+{
+ struct inode *inode;
+ loff_t pos;
+
+ if (unlikely(count > file->f_maxcount))
+ goto Einval;
+
+ pos = *ppos;
+
+ if (unlikely((pos < 0) || (loff_t) (pos + count) < 0))
+ goto Einval;
+
+ inode = file->f_dentry->d_inode;
+ if (inode->i_flock && MANDATORY_LOCK(inode))
+ return locks_mandatory_area(read_write == READ ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE, inode, file, *ppos, count);
+ return 0;
+
+Einval:
+ return -EINVAL;
+}
+
+loff_t generic_file_llseek(struct file *file, loff_t offset, int origin)
+{
+ long long retval;
+
+ switch (origin) {
+ case 2:
+ offset += file->f_dentry->d_inode->i_size;
+ break;
+ case 1:
+ offset += file->f_pos;
+ }
+ retval = -EINVAL;
+ if (offset>=0 && offset<=file->f_dentry->d_inode->i_sb->s_maxbytes) {
+ if (offset != file->f_pos) {
+ file->f_pos = offset;
+ file->f_reada = 0;
+ file->f_version = ++event;
+ }
+ retval = offset;
+ }
+ return retval;
+}
+
+loff_t no_llseek(struct file *file, loff_t offset, int origin)
+{
+ return -ESPIPE;
+}
+
+loff_t default_llseek(struct file *file, loff_t offset, int origin)
+{
+ long long retval;
+
+ switch (origin) {
+ case 2:
+ offset += file->f_dentry->d_inode->i_size;
+ break;
+ case 1:
+ offset += file->f_pos;
+ }
+ retval = -EINVAL;
+ if (offset >= 0) {
+ if (offset != file->f_pos) {
+ file->f_pos = offset;
+ file->f_reada = 0;
+ file->f_version = ++event;
+ }
+ retval = offset;
+ }
+ return retval;
+}
+
+static inline loff_t llseek(struct file *file, loff_t offset, int origin)
+{
+ loff_t (*fn)(struct file *, loff_t, int);
+ loff_t retval;
+
+ fn = default_llseek;
+ if (file->f_op && file->f_op->llseek)
+ fn = file->f_op->llseek;
+ lock_kernel();
+ retval = fn(file, offset, origin);
+ unlock_kernel();
+ return retval;
+}
+
+asmlinkage off_t sys_lseek(unsigned int fd, off_t offset, unsigned int origin)
+{
+ off_t retval;
+ struct file * file;
+
+ retval = -EBADF;
+ file = fget(fd);
+ if (!file)
+ goto bad;
+ retval = -EINVAL;
+ if (origin <= 2) {
+ loff_t res = llseek(file, offset, origin);
+ retval = res;
+ if (res != (loff_t)retval)
+ retval = -EOVERFLOW; /* LFS: should only happen on 32 bit platforms */
+ }
+ fput(file);
+bad:
+ return retval;
+}
+
+#if !defined(__alpha__)
+asmlinkage long sys_llseek(unsigned int fd, unsigned long offset_high,
+ unsigned long offset_low, loff_t * result,
+ unsigned int origin)
+{
+ int retval;
+ struct file * file;
+ loff_t offset;
+
+ retval = -EBADF;
+ file = fget(fd);
+ if (!file)
+ goto bad;
+ retval = -EINVAL;
+ if (origin > 2)
+ goto out_putf;
+
+ offset = llseek(file, ((loff_t) offset_high << 32) | offset_low,
+ origin);
+
+ retval = (int)offset;
+ if (offset >= 0) {
+ retval = -EFAULT;
+ if (!copy_to_user(result, &offset, sizeof(offset)))
+ retval = 0;
+ }
+out_putf:
+ fput(file);
+bad:
+ return retval;
+}
+#endif
+
+asmlinkage ssize_t sys_read(unsigned int fd, char * buf, size_t count)
+{
+ ssize_t ret;
+ struct file * file;
+
+ ret = -EBADF;
+ file = fget(fd);
+ if (file) {
+ if (file->f_mode & FMODE_READ) {
+ ret = rw_verify_area(READ, file, &file->f_pos, count);
+
+ if (!ret) {
+ ssize_t (*read)(struct file *, char *, size_t, loff_t *);
+ ret = -EINVAL;
+ if (file->f_op && (read = file->f_op->read) != NULL)
+ ret = read(file, buf, count, &file->f_pos);
+ }
+ }
+ if (ret > 0)
+ dnotify_parent(file->f_dentry, DN_ACCESS);
+ fput(file);
+ }
+ return ret;
+}
+
+asmlinkage ssize_t sys_write(unsigned int fd, const char * buf, size_t count)
+{
+ ssize_t ret;
+ struct file * file;
+
+ ret = -EBADF;
+ file = fget(fd);
+ if (file) {
+ if (file->f_mode & FMODE_WRITE) {
+ ret = rw_verify_area(WRITE, file, &file->f_pos, count);
+ if (!ret) {
+ ssize_t (*write)(struct file *, const char *, size_t, loff_t *);
+ ret = -EINVAL;
+ if (file->f_op && (write = file->f_op->write) != NULL)
+ ret = write(file, buf, count, &file->f_pos);
+ }
+ }
+ if (ret > 0)
+ dnotify_parent(file->f_dentry, DN_MODIFY);
+ fput(file);
+ }
+ return ret;
+}
+
+
+static ssize_t do_readv_writev(int type, struct file *file,
+ const struct iovec * vector,
+ unsigned long count)
+{
+ typedef ssize_t (*io_fn_t)(struct file *, char *, size_t, loff_t *);
+ typedef ssize_t (*iov_fn_t)(struct file *, const struct iovec *, unsigned long, loff_t *);
+
+ size_t tot_len;
+ struct iovec iovstack[UIO_FASTIOV];
+ struct iovec *iov=iovstack;
+ ssize_t ret, i;
+ io_fn_t fn;
+ iov_fn_t fnv;
+
+ /*
+ * First get the "struct iovec" from user memory and
+ * verify all the pointers
+ */
+ ret = 0;
+ if (!count)
+ goto out_nofree;
+ ret = -EINVAL;
+ if (count > UIO_MAXIOV)
+ goto out_nofree;
+ if (!file->f_op)
+ goto out_nofree;
+ if (count > UIO_FASTIOV) {
+ ret = -ENOMEM;
+ iov = kmalloc(count*sizeof(struct iovec), GFP_KERNEL);
+ if (!iov)
+ goto out_nofree;
+ }
+ ret = -EFAULT;
+ if (copy_from_user(iov, vector, count*sizeof(*vector)))
+ goto out;
+
+ /*
+ * Single unix specification:
+ * We should -EINVAL if an element length is not >= 0 and fitting an ssize_t
+ * The total length is fitting an ssize_t
+ *
+ * Be careful here because iov_len is a size_t not an ssize_t
+ */
+
+ tot_len = 0;
+ ret = -EINVAL;
+ for (i = 0 ; i < count ; i++) {
+ ssize_t len = (ssize_t) iov[i].iov_len;
+ if (len < 0) /* size_t not fitting an ssize_t .. */
+ goto out;
+ tot_len += len;
+ /* We must do this work unsigned - signed overflow is
+ undefined and gcc 3.2 now uses that fact sometimes...
+
+ FIXME: put in a proper limits.h for each platform */
+#if BITS_PER_LONG==64
+ if (tot_len > 0x7FFFFFFFFFFFFFFFUL)
+#else
+ if (tot_len > 0x7FFFFFFFUL)
+#endif
+ goto out;
+ }
+
+ /* VERIFY_WRITE actually means a read, as we write to user space */
+ ret = rw_verify_area((type == VERIFY_WRITE ? READ : WRITE),
+ file, &file->f_pos, tot_len);
+ if (ret)
+ goto out;
+
+ fnv = (type == VERIFY_WRITE ? file->f_op->readv : file->f_op->writev);
+ if (fnv) {
+ ret = fnv(file, iov, count, &file->f_pos);
+ goto out;
+ }
+
+ /* VERIFY_WRITE actually means a read, as we write to user space */
+ fn = (type == VERIFY_WRITE ? file->f_op->read :
+ (io_fn_t) file->f_op->write);
+
+ ret = 0;
+ vector = iov;
+ while (count > 0) {
+ void * base;
+ size_t len;
+ ssize_t nr;
+
+ base = vector->iov_base;
+ len = vector->iov_len;
+ vector++;
+ count--;
+
+ nr = fn(file, base, len, &file->f_pos);
+
+ if (nr < 0) {
+ if (!ret) ret = nr;
+ break;
+ }
+ ret += nr;
+ if (nr != len)
+ break;
+ }
+
+out:
+ if (iov != iovstack)
+ kfree(iov);
+out_nofree:
+ /* VERIFY_WRITE actually means a read, as we write to user space */
+ if ((ret + (type == VERIFY_WRITE)) > 0)
+ dnotify_parent(file->f_dentry,
+ (type == VERIFY_WRITE) ? DN_ACCESS : DN_MODIFY);
+ return ret;
+}
+
+asmlinkage ssize_t sys_readv(unsigned long fd, const struct iovec * vector,
+ unsigned long count)
+{
+ struct file * file;
+ ssize_t ret;
+
+
+ ret = -EBADF;
+ file = fget(fd);
+ if (!file)
+ goto bad_file;
+ if (file->f_op && (file->f_mode & FMODE_READ) &&
+ (file->f_op->readv || file->f_op->read))
+ ret = do_readv_writev(VERIFY_WRITE, file, vector, count);
+ fput(file);
+
+bad_file:
+ return ret;
+}
+
+asmlinkage ssize_t sys_writev(unsigned long fd, const struct iovec * vector,
+ unsigned long count)
+{
+ struct file * file;
+ ssize_t ret;
+
+
+ ret = -EBADF;
+ file = fget(fd);
+ if (!file)
+ goto bad_file;
+ if (file->f_op && (file->f_mode & FMODE_WRITE) &&
+ (file->f_op->writev || file->f_op->write))
+ ret = do_readv_writev(VERIFY_READ, file, vector, count);
+ fput(file);
+
+bad_file:
+ return ret;
+}
+
+/* From the Single Unix Spec: pread & pwrite act like lseek to pos + op +
+ lseek back to original location. They fail just like lseek does on
+ non-seekable files. */
+
+asmlinkage ssize_t sys_pread(unsigned int fd, char * buf,
+ size_t count, loff_t pos)
+{
+ ssize_t ret;
+ struct file * file;
+ ssize_t (*read)(struct file *, char *, size_t, loff_t *);
+
+ ret = -EBADF;
+ file = fget(fd);
+ if (!file)
+ goto bad_file;
+ if (!(file->f_mode & FMODE_READ))
+ goto out;
+ ret = rw_verify_area(READ, file, &pos, count);
+
+ if (ret)
+ goto out;
+ ret = -EINVAL;
+ if (!file->f_op || !(read = file->f_op->read))
+ goto out;
+ if (pos < 0)
+ goto out;
+ ret = read(file, buf, count, &pos);
+ if (ret > 0)
+ dnotify_parent(file->f_dentry, DN_ACCESS);
+out:
+ fput(file);
+bad_file:
+ return ret;
+}
+
+asmlinkage ssize_t sys_pwrite(unsigned int fd, const char * buf,
+ size_t count, loff_t pos)
+{
+ ssize_t ret;
+ struct file * file;
+ ssize_t (*write)(struct file *, const char *, size_t, loff_t *);
+
+ ret = -EBADF;
+ file = fget(fd);
+ if (!file)
+ goto bad_file;
+ if (!(file->f_mode & FMODE_WRITE))
+ goto out;
+ ret = rw_verify_area(WRITE, file, &pos, count);
+
+ if (ret)
+ goto out;
+ ret = -EINVAL;
+ if (!file->f_op || !(write = file->f_op->write))
+ goto out;
+ if (pos < 0)
+ goto out;
+
+ ret = write(file, buf, count, &pos);
+ if (ret > 0)
+ dnotify_parent(file->f_dentry, DN_MODIFY);
+out:
+ fput(file);
+bad_file:
+ return ret;
+}
diff --git a/fs/readdir.c b/fs/readdir.c
new file mode 100644
index 00000000000000..5e85174732609e
--- /dev/null
+++ b/fs/readdir.c
@@ -0,0 +1,389 @@
+/*
+ * linux/fs/readdir.c
+ *
+ * Copyright (C) 1995 Linus Torvalds
+ */
+
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/errno.h>
+#include <linux/stat.h>
+#include <linux/file.h>
+#include <linux/smp_lock.h>
+
+#include <asm/uaccess.h>
+
+int vfs_readdir(struct file *file, filldir_t filler, void *buf)
+{
+ struct inode *inode = file->f_dentry->d_inode;
+ int res = -ENOTDIR;
+ if (!file->f_op || !file->f_op->readdir)
+ goto out;
+ down(&inode->i_sem);
+ down(&inode->i_zombie);
+ res = -ENOENT;
+ if (!IS_DEADDIR(inode)) {
+ lock_kernel();
+ res = file->f_op->readdir(file, buf, filler);
+ unlock_kernel();
+ }
+ up(&inode->i_zombie);
+ up(&inode->i_sem);
+out:
+ return res;
+}
+
+int dcache_dir_open(struct inode *inode, struct file *file)
+{
+ static struct qstr cursor_name = {len:1, name:"."};
+
+ file->private_data = d_alloc(file->f_dentry, &cursor_name);
+
+ return file->private_data ? 0 : -ENOMEM;
+}
+
+int dcache_dir_close(struct inode *inode, struct file *file)
+{
+ dput(file->private_data);
+ return 0;
+}
+
+loff_t dcache_dir_lseek(struct file *file, loff_t offset, int origin)
+{
+ down(&file->f_dentry->d_inode->i_sem);
+ switch (origin) {
+ case 1:
+ offset += file->f_pos;
+ case 0:
+ if (offset >= 0)
+ break;
+ default:
+ up(&file->f_dentry->d_inode->i_sem);
+ return -EINVAL;
+ }
+ if (offset != file->f_pos) {
+ file->f_pos = offset;
+ if (file->f_pos >= 2) {
+ struct list_head *p;
+ struct dentry *cursor = file->private_data;
+ loff_t n = file->f_pos - 2;
+
+ spin_lock(&dcache_lock);
+ list_del(&cursor->d_child);
+ p = file->f_dentry->d_subdirs.next;
+ while (n && p != &file->f_dentry->d_subdirs) {
+ struct dentry *next;
+ next = list_entry(p, struct dentry, d_child);
+ if (!list_empty(&next->d_hash) && next->d_inode)
+ n--;
+ p = p->next;
+ }
+ list_add_tail(&cursor->d_child, p);
+ spin_unlock(&dcache_lock);
+ }
+ }
+ up(&file->f_dentry->d_inode->i_sem);
+ return offset;
+}
+
+int dcache_dir_fsync(struct file * file, struct dentry *dentry, int datasync)
+{
+ return 0;
+}
+
+/*
+ * Directory is locked and all positive dentries in it are safe, since
+ * for ramfs-type trees they can't go away without unlink() or rmdir(),
+ * both impossible due to the lock on directory.
+ */
+
+int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir)
+{
+ struct dentry *dentry = filp->f_dentry;
+ struct dentry *cursor = filp->private_data;
+ struct list_head *p, *q = &cursor->d_child;
+ ino_t ino;
+ int i = filp->f_pos;
+
+ switch (i) {
+ case 0:
+ ino = dentry->d_inode->i_ino;
+ if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0)
+ break;
+ filp->f_pos++;
+ i++;
+ /* fallthrough */
+ case 1:
+ spin_lock(&dcache_lock);
+ ino = dentry->d_parent->d_inode->i_ino;
+ spin_unlock(&dcache_lock);
+ if (filldir(dirent, "..", 2, i, ino, DT_DIR) < 0)
+ break;
+ filp->f_pos++;
+ i++;
+ /* fallthrough */
+ default:
+ spin_lock(&dcache_lock);
+ if (filp->f_pos == 2) {
+ list_del(q);
+ list_add(q, &dentry->d_subdirs);
+ }
+ for (p=q->next; p != &dentry->d_subdirs; p=p->next) {
+ struct dentry *next;
+ next = list_entry(p, struct dentry, d_child);
+ if (list_empty(&next->d_hash) || !next->d_inode)
+ continue;
+
+ spin_unlock(&dcache_lock);
+ if (filldir(dirent, next->d_name.name, next->d_name.len, filp->f_pos, next->d_inode->i_ino, DT_UNKNOWN) < 0)
+ return 0;
+ spin_lock(&dcache_lock);
+ /* next is still alive */
+ list_del(q);
+ list_add(q, p);
+ p = q;
+ filp->f_pos++;
+ }
+ spin_unlock(&dcache_lock);
+ }
+ UPDATE_ATIME(dentry->d_inode);
+ return 0;
+}
+
+struct file_operations dcache_dir_ops = {
+ open: dcache_dir_open,
+ release: dcache_dir_close,
+ llseek: dcache_dir_lseek,
+ read: generic_read_dir,
+ readdir: dcache_readdir,
+ fsync: dcache_dir_fsync,
+};
+
+/*
+ * Traditional linux readdir() handling..
+ *
+ * "count=1" is a special case, meaning that the buffer is one
+ * dirent-structure in size and that the code can't handle more
+ * anyway. Thus the special "fillonedir()" function for that
+ * case (the low-level handlers don't need to care about this).
+ */
+#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de)))
+#define ROUND_UP(x) (((x)+sizeof(long)-1) & ~(sizeof(long)-1))
+
+#ifndef __ia64__
+
+struct old_linux_dirent {
+ unsigned long d_ino;
+ unsigned long d_offset;
+ unsigned short d_namlen;
+ char d_name[1];
+};
+
+struct readdir_callback {
+ struct old_linux_dirent * dirent;
+ int count;
+};
+
+static int fillonedir(void * __buf, const char * name, int namlen, loff_t offset,
+ ino_t ino, unsigned int d_type)
+{
+ struct readdir_callback * buf = (struct readdir_callback *) __buf;
+ struct old_linux_dirent * dirent;
+
+ if (buf->count)
+ return -EINVAL;
+ buf->count++;
+ dirent = buf->dirent;
+ put_user(ino, &dirent->d_ino);
+ put_user(offset, &dirent->d_offset);
+ put_user(namlen, &dirent->d_namlen);
+ copy_to_user(dirent->d_name, name, namlen);
+ put_user(0, dirent->d_name + namlen);
+ return 0;
+}
+
+asmlinkage int old_readdir(unsigned int fd, void * dirent, unsigned int count)
+{
+ int error;
+ struct file * file;
+ struct readdir_callback buf;
+
+ error = -EBADF;
+ file = fget(fd);
+ if (!file)
+ goto out;
+
+ buf.count = 0;
+ buf.dirent = dirent;
+
+ error = vfs_readdir(file, fillonedir, &buf);
+ if (error >= 0)
+ error = buf.count;
+
+ fput(file);
+out:
+ return error;
+}
+
+#endif /* !__ia64__ */
+
+/*
+ * New, all-improved, singing, dancing, iBCS2-compliant getdents()
+ * interface.
+ */
+struct linux_dirent {
+ unsigned long d_ino;
+ unsigned long d_off;
+ unsigned short d_reclen;
+ char d_name[1];
+};
+
+struct getdents_callback {
+ struct linux_dirent * current_dir;
+ struct linux_dirent * previous;
+ int count;
+ int error;
+};
+
+static int filldir(void * __buf, const char * name, int namlen, loff_t offset,
+ ino_t ino, unsigned int d_type)
+{
+ struct linux_dirent * dirent;
+ struct getdents_callback * buf = (struct getdents_callback *) __buf;
+ int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1);
+
+ buf->error = -EINVAL; /* only used if we fail.. */
+ if (reclen > buf->count)
+ return -EINVAL;
+ dirent = buf->previous;
+ if (dirent)
+ put_user(offset, &dirent->d_off);
+ dirent = buf->current_dir;
+ buf->previous = dirent;
+ put_user(ino, &dirent->d_ino);
+ put_user(reclen, &dirent->d_reclen);
+ copy_to_user(dirent->d_name, name, namlen);
+ put_user(0, dirent->d_name + namlen);
+ dirent = (void *)dirent + reclen;
+ buf->current_dir = dirent;
+ buf->count -= reclen;
+ return 0;
+}
+
+asmlinkage long sys_getdents(unsigned int fd, void * dirent, unsigned int count)
+{
+ struct file * file;
+ struct linux_dirent * lastdirent;
+ struct getdents_callback buf;
+ int error;
+
+ error = -EBADF;
+ file = fget(fd);
+ if (!file)
+ goto out;
+
+ buf.current_dir = (struct linux_dirent *) dirent;
+ buf.previous = NULL;
+ buf.count = count;
+ buf.error = 0;
+
+ error = vfs_readdir(file, filldir, &buf);
+ if (error < 0)
+ goto out_putf;
+ error = buf.error;
+ lastdirent = buf.previous;
+ if (lastdirent) {
+ put_user(file->f_pos, &lastdirent->d_off);
+ error = count - buf.count;
+ }
+
+out_putf:
+ fput(file);
+out:
+ return error;
+}
+
+/*
+ * And even better one including d_type field and 64bit d_ino and d_off.
+ */
+struct linux_dirent64 {
+ u64 d_ino;
+ s64 d_off;
+ unsigned short d_reclen;
+ unsigned char d_type;
+ char d_name[0];
+};
+
+#define ROUND_UP64(x) (((x)+sizeof(u64)-1) & ~(sizeof(u64)-1))
+
+struct getdents_callback64 {
+ struct linux_dirent64 * current_dir;
+ struct linux_dirent64 * previous;
+ int count;
+ int error;
+};
+
+static int filldir64(void * __buf, const char * name, int namlen, loff_t offset,
+ ino_t ino, unsigned int d_type)
+{
+ struct linux_dirent64 * dirent, d;
+ struct getdents_callback64 * buf = (struct getdents_callback64 *) __buf;
+ int reclen = ROUND_UP64(NAME_OFFSET(dirent) + namlen + 1);
+
+ buf->error = -EINVAL; /* only used if we fail.. */
+ if (reclen > buf->count)
+ return -EINVAL;
+ dirent = buf->previous;
+ if (dirent) {
+ d.d_off = offset;
+ copy_to_user(&dirent->d_off, &d.d_off, sizeof(d.d_off));
+ }
+ dirent = buf->current_dir;
+ buf->previous = dirent;
+ memset(&d, 0, NAME_OFFSET(&d));
+ d.d_ino = ino;
+ d.d_reclen = reclen;
+ d.d_type = d_type;
+ copy_to_user(dirent, &d, NAME_OFFSET(&d));
+ copy_to_user(dirent->d_name, name, namlen);
+ put_user(0, dirent->d_name + namlen);
+ dirent = (void *)dirent + reclen;
+ buf->current_dir = dirent;
+ buf->count -= reclen;
+ return 0;
+}
+
+asmlinkage long sys_getdents64(unsigned int fd, void * dirent, unsigned int count)
+{
+ struct file * file;
+ struct linux_dirent64 * lastdirent;
+ struct getdents_callback64 buf;
+ int error;
+
+ error = -EBADF;
+ file = fget(fd);
+ if (!file)
+ goto out;
+
+ buf.current_dir = (struct linux_dirent64 *) dirent;
+ buf.previous = NULL;
+ buf.count = count;
+ buf.error = 0;
+
+ error = vfs_readdir(file, filldir64, &buf);
+ if (error < 0)
+ goto out_putf;
+ error = buf.error;
+ lastdirent = buf.previous;
+ if (lastdirent) {
+ struct linux_dirent64 d;
+ d.d_off = file->f_pos;
+ copy_to_user(&lastdirent->d_off, &d.d_off, sizeof(d.d_off));
+ error = count - buf.count;
+ }
+
+out_putf:
+ fput(file);
+out:
+ return error;
+}
diff --git a/fs/reiserfs/Makefile b/fs/reiserfs/Makefile
new file mode 100644
index 00000000000000..1ea70777f5c294
--- /dev/null
+++ b/fs/reiserfs/Makefile
@@ -0,0 +1,30 @@
+#
+# Makefile for the linux reiser-filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+O_TARGET := reiserfs.o
+obj-y := bitmap.o do_balan.o namei.o inode.o file.o dir.o fix_node.o super.o prints.o objectid.o \
+lbalance.o ibalance.o stree.o hashes.o buffer2.o tail_conversion.o journal.o resize.o item_ops.o ioctl.o procfs.o
+
+obj-m := $(O_TARGET)
+
+# gcc -O2 (the kernel default) is overaggressive on ppc32 when many inline
+# functions are used. This causes the compiler to advance the stack
+# pointer out of the available stack space, corrupting kernel space,
+# and causing a panic. Since this behavior only affects ppc32, this ifeq
+# will work around it. If any other architecture displays this behavior,
+# add it here.
+ifeq ($(CONFIG_PPC32),y)
+EXTRA_CFLAGS := -O1
+endif
+
+include $(TOPDIR)/Rules.make
+
+TAGS:
+ etags *.c
+
diff --git a/fs/reiserfs/README b/fs/reiserfs/README
new file mode 100644
index 00000000000000..90e1670e4e6f5a
--- /dev/null
+++ b/fs/reiserfs/README
@@ -0,0 +1,161 @@
+[LICENSING]
+
+ReiserFS is hereby licensed under the GNU General
+Public License version 2.
+
+Source code files that contain the phrase "licensing governed by
+reiserfs/README" are "governed files" throughout this file. Governed
+files are licensed under the GPL. The portions of them owned by Hans
+Reiser, or authorized to be licensed by him, have been in the past,
+and likely will be in the future, licensed to other parties under
+other licenses. If you add your code to governed files, and don't
+want it to be owned by Hans Reiser, put your copyright label on that
+code so the poor blight and his customers can keep things straight.
+All portions of governed files not labeled otherwise are owned by Hans
+Reiser, and by adding your code to it, widely distributing it to
+others or sending us a patch, and leaving the sentence in stating that
+licensing is governed by the statement in this file, you accept this.
+It will be a kindness if you identify whether Hans Reiser is allowed
+to license code labeled as owned by you on your behalf other than
+under the GPL, because he wants to know if it is okay to do so and put
+a check in the mail to you (for non-trivial improvements) when he
+makes his next sale. He makes no guarantees as to the amount if any,
+though he feels motivated to motivate contributors, and you can surely
+discuss this with him before or after contributing. You have the
+right to decline to allow him to license your code contribution other
+than under the GPL.
+
+Further licensing options are available for commercial and/or other
+interests directly from Hans Reiser: hans@reiser.to. If you interpret
+the GPL as not allowing those additional licensing options, you read
+it wrongly, and Richard Stallman agrees with me, when carefully read
+you can see that those restrictions on additional terms do not apply
+to the owner of the copyright, and my interpretation of this shall
+govern for this license.
+
+Finally, nothing in this license shall be interpreted to allow you to
+fail to fairly credit me, or to remove my credits, without my
+permission, unless you are an end user not redistributing to others.
+If you have doubts about how to properly do that, or about what is
+fair, ask. (Last I spoke with him Richard was contemplating how best
+to address the fair crediting issue in the next GPL version.)
+
+[END LICENSING]
+
+Reiserfs is a file system based on balanced tree algorithms, which is
+described at http://devlinux.com/namesys.
+
+Stop reading here. Go there, then return.
+
+Send bug reports to yura@namesys.botik.ru.
+
+mkreiserfs and other utilities are in reiserfs/utils, or wherever your
+Linux provider put them. There is some disagreement about how useful
+it is for users to get their fsck and mkreiserfs out of sync with the
+version of reiserfs that is in their kernel, with many important
+distributors wanting them out of sync.:-) Please try to remember to
+recompile and reinstall fsck and mkreiserfs with every update of
+reiserfs, this is a common source of confusion. Note that some of the
+utilities cannot be compiled without accessing the balancing code
+which is in the kernel code, and relocating the utilities may require
+you to specify where that code can be found.
+
+Yes, if you update your reiserfs kernel module you do have to
+recompile your kernel, most of the time. The errors you get will be
+quite cryptic if your forget to do so.
+
+Real users, as opposed to folks who want to hack and then understand
+what went wrong, will want REISERFS_CHECK off.
+
+Hideous Commercial Pitch: Spread your development costs across other OS
+vendors. Select from the best in the world, not the best in your
+building, by buying from third party OS component suppliers. Leverage
+the software component development power of the internet. Be the most
+aggressive in taking advantage of the commercial possibilities of
+decentralized internet development, and add value through your branded
+integration that you sell as an operating system. Let your competitors
+be the ones to compete against the entire internet by themselves. Be
+hip, get with the new economic trend, before your competitors do. Send
+email to hans@reiser.to.
+
+To understand the code, after reading the website, start reading the
+code by reading reiserfs_fs.h first.
+
+Hans Reiser was the project initiator, primary architect, source of all
+funding for the first 5.5 years, and one of the programmers. He owns
+the copyright.
+
+Vladimir Saveljev was one of the programmers, and he worked long hours
+writing the cleanest code. He always made the effort to be the best he
+could be, and to make his code the best that it could be. What resulted
+was quite remarkable. I don't think that money can ever motivate someone
+to work the way he did, he is one of the most selfless men I know.
+
+Yura helps with benchmarking, coding hashes, and block pre-allocation
+code.
+
+Anatoly Pinchuk is a former member of our team who worked closely with
+Vladimir throughout the project's development. He wrote a quite
+substantial portion of the total code. He realized that there was a
+space problem with packing tails of files for files larger than a node
+that start on a node aligned boundary (there are reasons to want to node
+align files), and he invented and implemented indirect items and
+unformatted nodes as the solution.
+
+Konstantin Shvachko, with the help of the Russian version of a VC,
+tried to put me in a position where I was forced into giving control
+of the project to him. (Fortunately, as the person paying the money
+for all salaries from my dayjob I owned all copyrights, and you can't
+really force takeovers of sole proprietorships.) This was something
+curious, because he never really understood the value of our project,
+why we should do what we do, or why innovation was possible in
+general, but he was sure that he ought to be controlling it. Every
+innovation had to be forced past him while he was with us. He added
+two years to the time required to complete reiserfs, and was a net
+loss for me. Mikhail Gilula was a brilliant innovator who also left
+in a destructive way that erased the value of his contributions, and
+that he was shown much generosity just makes it more painful.
+
+Grigory Zaigralin was an extremely effective system administrator for
+our group.
+
+Igor Krasheninnikov was wonderful at hardware procurement, repair, and
+network installation.
+
+Jeremy Fitzhardinge wrote the teahash.c code, and he gives credit to a
+textbook he got the algorithm from in the code. Note that his analysis
+of how we could use the hashing code in making 32 bit NFS cookies work
+was probably more important than the actual algorithm. Colin Plumb also
+contributed to it.
+
+Chris Mason dived right into our code, and in just a few months produced
+the journaling code that dramatically increased the value of ReiserFS.
+He is just an amazing programmer.
+
+Igor Zagorovsky is writing much of the new item handler and extent code
+for our next major release.
+
+Alexander Zarochentcev (sometimes known as zam, or sasha), wrote the
+resizer, and is hard at work on implementing allocate on flush. SGI
+implemented allocate on flush before us for XFS, and generously took
+the time to convince me we should do it also. They are great people,
+and a great company.
+
+Yuri Shevchuk and Nikita Danilov are doing squid cache optimization.
+
+Vitaly Fertman is doing fsck.
+
+Jeff Mahoney, of SuSE, contributed a few cleanup fixes, most notably
+the endian safe patches which allow ReiserFS to run on any platform
+supported by the Linux kernel.
+
+SuSE, IntegratedLinux.com, Ecila, MP3.com, bigstorage.com, and the
+Alpha PC Company made it possible for me to not have a day job
+anymore, and to dramatically increase our staffing. Ecila funded
+hypertext feature development, MP3.com funded journaling, SuSE funded
+core development, IntegratedLinux.com funded squid web cache
+appliances, bigstorage.com funded HSM, and the alpha PC company funded
+the alpha port. Many of these tasks were helped by sponsors other
+than the ones just named. SuSE has helped in much more than just
+funding....
+
diff --git a/fs/reiserfs/bitmap.c b/fs/reiserfs/bitmap.c
new file mode 100644
index 00000000000000..26ec6f9fa9b357
--- /dev/null
+++ b/fs/reiserfs/bitmap.c
@@ -0,0 +1,906 @@
+/*
+ * Copyright 2000-2002 by Hans Reiser, licensing governed by reiserfs/README
+ */
+
+/* Reiserfs block (de)allocator, bitmap-based. */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/vmalloc.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <linux/kernel.h>
+
+#include <linux/reiserfs_fs.h>
+#include <linux/reiserfs_fs_sb.h>
+#include <linux/reiserfs_fs_i.h>
+
+#define PREALLOCATION_SIZE 9
+
+#define INODE_INFO(inode) (&(inode)->u.reiserfs_i)
+
+/* different reiserfs block allocator options */
+
+#define SB_ALLOC_OPTS(s) ((s)->u.reiserfs_sb.s_alloc_options.bits)
+
+#define _ALLOC_concentrating_formatted_nodes 0
+#define _ALLOC_displacing_large_files 1
+#define _ALLOC_displacing_new_packing_localities 2
+#define _ALLOC_old_hashed_relocation 3
+#define _ALLOC_new_hashed_relocation 4
+#define _ALLOC_skip_busy 5
+#define _ALLOC_displace_based_on_dirid 6
+#define _ALLOC_hashed_formatted_nodes 7
+#define _ALLOC_old_way 8
+#define _ALLOC_hundredth_slices 9
+
+#define concentrating_formatted_nodes(s) test_bit(_ALLOC_concentrating_formatted_nodes, &SB_ALLOC_OPTS(s))
+#define displacing_large_files(s) test_bit(_ALLOC_displacing_large_files, &SB_ALLOC_OPTS(s))
+#define displacing_new_packing_localities(s) test_bit(_ALLOC_displacing_new_packing_localities, &SB_ALLOC_OPTS(s))
+
+#define SET_OPTION(optname) \
+ do { \
+ reiserfs_warning(s, "reiserfs: option \"%s\" is set\n", #optname); \
+ set_bit(_ALLOC_ ## optname , &SB_ALLOC_OPTS(s)); \
+ } while(0)
+#define TEST_OPTION(optname, s) \
+ test_bit(_ALLOC_ ## optname , &SB_ALLOC_OPTS(s))
+
+
+/* #define LIMIT(a,b) do { if ((a) > (b)) (a) = (b); } while(0) */
+
+static inline void get_bit_address (struct super_block * s,
+ unsigned long block, int * bmap_nr, int * offset)
+{
+ /* It is in the bitmap block number equal to the block
+ * number divided by the number of bits in a block. */
+ *bmap_nr = block / (s->s_blocksize << 3);
+ /* Within that bitmap block it is located at bit offset *offset. */
+ *offset = block & ((s->s_blocksize << 3) - 1 );
+ return;
+}
+
+#ifdef CONFIG_REISERFS_CHECK
+int is_reusable (struct super_block * s, unsigned long block, int bit_value)
+{
+ int i, j;
+
+ if (block == 0 || block >= SB_BLOCK_COUNT (s)) {
+ reiserfs_warning (s, "vs-4010: is_reusable: block number is out of range %lu (%u)\n",
+ block, SB_BLOCK_COUNT (s));
+ return 0;
+ }
+
+ /* it can't be one of the bitmap blocks */
+ for (i = 0; i < SB_BMAP_NR (s); i ++)
+ if (block == SB_AP_BITMAP (s)[i].bh->b_blocknr) {
+ reiserfs_warning (s, "vs: 4020: is_reusable: "
+ "bitmap block %lu(%u) can't be freed or reused\n",
+ block, SB_BMAP_NR (s));
+ return 0;
+ }
+
+ get_bit_address (s, block, &i, &j);
+
+ if (i >= SB_BMAP_NR (s)) {
+ reiserfs_warning (s, "vs-4030: is_reusable: there is no so many bitmap blocks: "
+ "block=%lu, bitmap_nr=%d\n", block, i);
+ return 0;
+ }
+
+ if ((bit_value == 0 &&
+ reiserfs_test_le_bit(j, SB_AP_BITMAP(s)[i].bh->b_data)) ||
+ (bit_value == 1 &&
+ reiserfs_test_le_bit(j, SB_AP_BITMAP (s)[i].bh->b_data) == 0)) {
+ reiserfs_warning (s, "vs-4040: is_reusable: corresponding bit of block %lu does not "
+ "match required value (i==%d, j==%d) test_bit==%d\n",
+ block, i, j, reiserfs_test_le_bit (j, SB_AP_BITMAP (s)[i].bh->b_data));
+
+ return 0;
+ }
+
+ if (bit_value == 0 && block == SB_ROOT_BLOCK (s)) {
+ reiserfs_warning (s, "vs-4050: is_reusable: this is root block (%u), "
+ "it must be busy\n", SB_ROOT_BLOCK (s));
+ return 0;
+ }
+
+ return 1;
+}
+#endif /* CONFIG_REISERFS_CHECK */
+
+/* searches in journal structures for a given block number (bmap, off). If block
+ is found in reiserfs journal it suggests next free block candidate to test. */
+static inline int is_block_in_journal (struct super_block * s, int bmap, int off, int *next)
+{
+ unsigned int tmp;
+
+ if (reiserfs_in_journal (s, s->s_dev, bmap, off, s->s_blocksize, 1, &tmp)) {
+ if (tmp) { /* hint supplied */
+ *next = tmp;
+ PROC_INFO_INC( s, scan_bitmap.in_journal_hint );
+ } else {
+ (*next) = off + 1; /* inc offset to avoid looping. */
+ PROC_INFO_INC( s, scan_bitmap.in_journal_nohint );
+ }
+ PROC_INFO_INC( s, scan_bitmap.retry );
+ return 1;
+ }
+ return 0;
+}
+
+/* it searches for a window of zero bits with given minimum and maximum lengths in one bitmap
+ * block; */
+static int scan_bitmap_block (struct reiserfs_transaction_handle *th,
+ int bmap_n, int *beg, int boundary, int min, int max, int unfm)
+{
+ struct super_block *s = th->t_super;
+ struct reiserfs_bitmap_info *bi=&SB_AP_BITMAP(s)[bmap_n];
+ int end, next;
+ int org = *beg;
+
+ RFALSE(bmap_n >= SB_BMAP_NR (s), "Bitmap %d is out of range (0..%d)\n",bmap_n, SB_BMAP_NR (s) - 1);
+ PROC_INFO_INC( s, scan_bitmap.bmap );
+/* this is unclear and lacks comments, explain how journal bitmaps
+ work here for the reader. Convey a sense of the design here. What
+ is a window? */
+/* - I mean `a window of zero bits' as in description of this function - Zam. */
+
+ if ( !bi ) {
+ printk("Hey, bitmap info pointer is zero for bitmap %d!\n",bmap_n);
+ return 0;
+ }
+ if (buffer_locked (bi->bh)) {
+ PROC_INFO_INC( s, scan_bitmap.wait );
+ __wait_on_buffer (bi->bh);
+ }
+
+ /* If we know that first zero bit is only one or first zero bit is
+ closer to the end of bitmap than our start pointer */
+ if (bi->first_zero_hint > *beg || bi->free_count == 1)
+ *beg = bi->first_zero_hint;
+
+ while (1) {
+ cont:
+ if (bi->free_count < min)
+ return 0; // No free blocks in this bitmap
+
+ /* search for a first zero bit -- beggining of a window */
+ *beg = reiserfs_find_next_zero_le_bit
+ ((unsigned long*)(bi->bh->b_data), boundary, *beg);
+
+ if (*beg + min > boundary) { /* search for a zero bit fails or the rest of bitmap block
+ * cannot contain a zero window of minimum size */
+ return 0;
+ }
+
+ if (unfm && is_block_in_journal(s,bmap_n, *beg, beg))
+ continue;
+ /* first zero bit found; we check next bits */
+ for (end = *beg + 1;; end ++) {
+ if (end >= *beg + max || end >= boundary || reiserfs_test_le_bit (end, bi->bh->b_data)) {
+ next = end;
+ break;
+ }
+ /* finding the other end of zero bit window requires looking into journal structures (in
+ * case of searching for free blocks for unformatted nodes) */
+ if (unfm && is_block_in_journal(s, bmap_n, end, &next))
+ break;
+ }
+
+ /* now (*beg) points to beginning of zero bits window,
+ * (end) points to one bit after the window end */
+ if (end - *beg >= min) { /* it seems we have found window of proper size */
+ int i;
+ reiserfs_prepare_for_journal (s, bi->bh, 1);
+ /* try to set all blocks used checking are they still free */
+ for (i = *beg; i < end; i++) {
+ /* It seems that we should not check in journal again. */
+ if (reiserfs_test_and_set_le_bit (i, bi->bh->b_data)) {
+ /* bit was set by another process
+ * while we slept in prepare_for_journal() */
+ PROC_INFO_INC( s, scan_bitmap.stolen );
+ if (i >= *beg + min) { /* we can continue with smaller set of allocated blocks,
+ * if length of this set is more or equal to `min' */
+ end = i;
+ break;
+ }
+ /* otherwise we clear all bit were set ... */
+ while (--i >= *beg)
+ reiserfs_test_and_clear_le_bit (i, bi->bh->b_data);
+ reiserfs_restore_prepared_buffer (s, bi->bh);
+ *beg = max(org, (int)bi->first_zero_hint);
+ /* ... and search again in current block from beginning */
+ goto cont;
+ }
+ }
+ bi->free_count -= (end - *beg);
+
+ /* if search started from zero_hint bit, and zero hint have not
+ changed since, then we need to update first_zero_hint */
+ if ( bi->first_zero_hint >= *beg)
+ /* no point in looking for free bit if there is not any */
+ bi->first_zero_hint = (bi->free_count > 0 ) ?
+ reiserfs_find_next_zero_le_bit
+ ((unsigned long*)(bi->bh->b_data), s->s_blocksize << 3, end) : (s->s_blocksize << 3);
+
+ journal_mark_dirty (th, s, bi->bh);
+
+ /* free block count calculation */
+ reiserfs_prepare_for_journal (s, SB_BUFFER_WITH_SB(s), 1);
+ PUT_SB_FREE_BLOCKS(s, SB_FREE_BLOCKS(s) - (end - *beg));
+ journal_mark_dirty (th, s, SB_BUFFER_WITH_SB(s));
+
+ return end - (*beg);
+ } else {
+ *beg = next;
+ }
+ }
+}
+
+/* Tries to find contiguous zero bit window (given size) in given region of
+ * bitmap and place new blocks there. Returns number of allocated blocks. */
+static int scan_bitmap (struct reiserfs_transaction_handle *th,
+ unsigned long *start, unsigned long finish,
+ int min, int max, int unfm, unsigned long file_block)
+{
+ int nr_allocated=0;
+ struct super_block * s = th->t_super;
+ /* find every bm and bmap and bmap_nr in this file, and change them all to bitmap_blocknr
+ * - Hans, it is not a block number - Zam. */
+
+ int bm, off;
+ int end_bm, end_off;
+ int off_max = s->s_blocksize << 3;
+
+ PROC_INFO_INC( s, scan_bitmap.call );
+ if ( SB_FREE_BLOCKS(s) <= 0)
+ return 0; // No point in looking for more free blocks
+
+ get_bit_address (s, *start, &bm, &off);
+ get_bit_address (s, finish, &end_bm, &end_off);
+
+ // With this option set first we try to find a bitmap that is at least 10%
+ // free, and if that fails, then we fall back to old whole bitmap scanning
+ if ( TEST_OPTION(skip_busy, s) && SB_FREE_BLOCKS(s) > SB_BLOCK_COUNT(s)/20 ) {
+ for (;bm < end_bm; bm++, off = 0) {
+ if ( ( off && (!unfm || (file_block != 0))) || SB_AP_BITMAP(s)[bm].free_count > (s->s_blocksize << 3) / 10 )
+ nr_allocated = scan_bitmap_block(th, bm, &off, off_max, min, max, unfm);
+ if (nr_allocated)
+ goto ret;
+ }
+ get_bit_address (s, *start, &bm, &off);
+ }
+
+ for (;bm < end_bm; bm++, off = 0) {
+ nr_allocated = scan_bitmap_block(th, bm, &off, off_max, min, max, unfm);
+ if (nr_allocated)
+ goto ret;
+ }
+
+ nr_allocated = scan_bitmap_block(th, bm, &off, end_off + 1, min, max, unfm);
+
+ ret:
+ *start = bm * off_max + off;
+ return nr_allocated;
+
+}
+
+static void _reiserfs_free_block (struct reiserfs_transaction_handle *th,
+ b_blocknr_t block)
+{
+ struct super_block * s = th->t_super;
+ struct reiserfs_super_block * rs;
+ struct buffer_head * sbh;
+ struct reiserfs_bitmap_info *apbi;
+ int nr, offset;
+
+ PROC_INFO_INC( s, free_block );
+
+ rs = SB_DISK_SUPER_BLOCK (s);
+ sbh = SB_BUFFER_WITH_SB (s);
+ apbi = SB_AP_BITMAP(s);
+
+ get_bit_address (s, block, &nr, &offset);
+
+ if (nr >= sb_bmap_nr (rs)) {
+ reiserfs_warning (s, "vs-4075: reiserfs_free_block: "
+ "block %lu is out of range on %s\n",
+ block, bdevname(s->s_dev));
+ return;
+ }
+
+ reiserfs_prepare_for_journal(s, apbi[nr].bh, 1 ) ;
+
+ /* clear bit for the given block in bit map */
+ if (!reiserfs_test_and_clear_le_bit (offset, apbi[nr].bh->b_data)) {
+ reiserfs_warning (s, "vs-4080: reiserfs_free_block: "
+ "free_block (%04x:%lu)[dev:blocknr]: bit already cleared\n",
+ s->s_dev, block);
+ }
+ if (offset < apbi[nr].first_zero_hint) {
+ apbi[nr].first_zero_hint = offset;
+ }
+ apbi[nr].free_count ++;
+ journal_mark_dirty (th, s, apbi[nr].bh);
+
+ reiserfs_prepare_for_journal(s, sbh, 1) ;
+ /* update super block */
+ set_sb_free_blocks( rs, sb_free_blocks(rs) + 1 );
+
+ journal_mark_dirty (th, s, sbh);
+}
+
+void reiserfs_free_block (struct reiserfs_transaction_handle *th,
+ unsigned long block) {
+ struct super_block * s = th->t_super;
+
+ RFALSE(!s, "vs-4061: trying to free block on nonexistent device");
+ RFALSE(is_reusable (s, block, 1) == 0, "vs-4071: can not free such block");
+ /* mark it before we clear it, just in case */
+ journal_mark_freed(th, s, block) ;
+ _reiserfs_free_block(th, block) ;
+}
+
+/* preallocated blocks don't need to be run through journal_mark_freed */
+void reiserfs_free_prealloc_block (struct reiserfs_transaction_handle *th,
+ unsigned long block) {
+ RFALSE(!th->t_super, "vs-4060: trying to free block on nonexistent device");
+ RFALSE(is_reusable (th->t_super, block, 1) == 0, "vs-4070: can not free such block");
+ _reiserfs_free_block(th, block) ;
+}
+
+static void __discard_prealloc (struct reiserfs_transaction_handle * th,
+ struct inode * inode)
+{
+ unsigned long save = inode->u.reiserfs_i.i_prealloc_block ;
+#ifdef CONFIG_REISERFS_CHECK
+ if (inode->u.reiserfs_i.i_prealloc_count < 0)
+ reiserfs_warning(th->t_super, "zam-4001:%s: inode has negative prealloc blocks count.\n", __FUNCTION__ );
+#endif
+ while (inode->u.reiserfs_i.i_prealloc_count > 0) {
+ reiserfs_free_prealloc_block(th,inode->u.reiserfs_i.i_prealloc_block);
+ inode->u.reiserfs_i.i_prealloc_block++;
+ inode->u.reiserfs_i.i_prealloc_count --;
+ }
+ inode->u.reiserfs_i.i_prealloc_block = save ;
+ list_del (&(inode->u.reiserfs_i.i_prealloc_list));
+}
+
+/* FIXME: It should be inline function */
+void reiserfs_discard_prealloc (struct reiserfs_transaction_handle *th,
+ struct inode * inode)
+{
+ if (inode->u.reiserfs_i.i_prealloc_count) {
+ __discard_prealloc(th, inode);
+ }
+}
+
+void reiserfs_discard_all_prealloc (struct reiserfs_transaction_handle *th)
+{
+ struct list_head * plist = &SB_JOURNAL(th->t_super)->j_prealloc_list;
+ struct inode * inode;
+
+ while (!list_empty(plist)) {
+ inode = list_entry(plist->next, struct inode, u.reiserfs_i.i_prealloc_list);
+#ifdef CONFIG_REISERFS_CHECK
+ if (!inode->u.reiserfs_i.i_prealloc_count) {
+ reiserfs_warning(th->t_super, "zam-4001:%s: inode is in prealloc list but has no preallocated blocks.\n", __FUNCTION__ );
+ }
+#endif
+ __discard_prealloc(th, inode);
+ }
+}
+
+/* block allocator related options are parsed here */
+int reiserfs_parse_alloc_options(struct super_block * s, char * options)
+{
+ char * this_char, * value;
+
+ s->u.reiserfs_sb.s_alloc_options.bits = 0; /* clear default settings */
+
+ for (this_char = strtok (options, ":"); this_char != NULL; this_char = strtok (NULL, ":")) {
+ if ((value = strchr (this_char, '=')) != NULL)
+ *value++ = 0;
+
+ if (!strcmp(this_char, "concentrating_formatted_nodes")) {
+ int temp;
+ SET_OPTION(concentrating_formatted_nodes);
+ temp = (value && *value) ? simple_strtoul (value, &value, 0) : 10;
+ if (temp <= 0 || temp > 100) {
+ s->u.reiserfs_sb.s_alloc_options.border = 10;
+ } else {
+ s->u.reiserfs_sb.s_alloc_options.border = 100 / temp;
+ }
+ continue;
+ }
+ if (!strcmp(this_char, "displacing_large_files")) {
+ SET_OPTION(displacing_large_files);
+ s->u.reiserfs_sb.s_alloc_options.large_file_size =
+ (value && *value) ? simple_strtoul (value, &value, 0) : 16;
+ continue;
+ }
+ if (!strcmp(this_char, "displacing_new_packing_localities")) {
+ SET_OPTION(displacing_new_packing_localities);
+ continue;
+ };
+
+ if (!strcmp(this_char, "old_hashed_relocation")) {
+ SET_OPTION(old_hashed_relocation);
+ continue;
+ }
+
+ if (!strcmp(this_char, "new_hashed_relocation")) {
+ SET_OPTION(new_hashed_relocation);
+ continue;
+ }
+
+ if (!strcmp(this_char, "hashed_formatted_nodes")) {
+ SET_OPTION(hashed_formatted_nodes);
+ continue;
+ }
+
+ if (!strcmp(this_char, "skip_busy")) {
+ SET_OPTION(skip_busy);
+ continue;
+ }
+
+ if (!strcmp(this_char, "hundredth_slices")) {
+ SET_OPTION(hundredth_slices);
+ continue;
+ }
+
+ if (!strcmp(this_char, "old_way")) {
+ SET_OPTION(old_way);
+ continue;
+ }
+
+ if (!strcmp(this_char, "displace_based_on_dirid")) {
+ SET_OPTION(displace_based_on_dirid);
+ continue;
+ }
+
+ if (!strcmp(this_char, "preallocmin")) {
+ s->u.reiserfs_sb.s_alloc_options.preallocmin =
+ (value && *value) ? simple_strtoul (value, &value, 0) : 4;
+ continue;
+ }
+
+ if (!strcmp(this_char, "preallocsize")) {
+ s->u.reiserfs_sb.s_alloc_options.preallocsize =
+ (value && *value) ? simple_strtoul (value, &value, 0) : PREALLOCATION_SIZE;
+ continue;
+ }
+
+ reiserfs_warning(s, "zam-4001: %s : unknown option - %s\n", __FUNCTION__ , this_char);
+ return 1;
+ }
+
+ return 0;
+}
+
+static void inline new_hashed_relocation (reiserfs_blocknr_hint_t * hint)
+{
+ char * hash_in;
+ if (hint->formatted_node) {
+ hash_in = (char*)&hint->key.k_dir_id;
+ } else {
+ if (!hint->inode) {
+ //hint->search_start = hint->beg;
+ hash_in = (char*)&hint->key.k_dir_id;
+ } else
+ if ( TEST_OPTION(displace_based_on_dirid, hint->th->t_super))
+ hash_in = (char *)(&INODE_PKEY(hint->inode)->k_dir_id);
+ else
+ hash_in = (char *)(&INODE_PKEY(hint->inode)->k_objectid);
+ }
+
+ hint->search_start = hint->beg + keyed_hash(hash_in, 4) % (hint->end - hint->beg);
+}
+
+static void inline get_left_neighbor(reiserfs_blocknr_hint_t *hint)
+{
+ struct path * path;
+ struct buffer_head * bh;
+ struct item_head * ih;
+ int pos_in_item;
+ __u32 * item;
+
+ if (!hint->path) /* reiserfs code can call this function w/o pointer to path
+ * structure supplied; then we rely on supplied search_start */
+ return;
+
+ path = hint->path;
+ bh = get_last_bh(path);
+ RFALSE( !bh, "green-4002: Illegal path specified to get_left_neighbor\n");
+ ih = get_ih(path);
+ pos_in_item = path->pos_in_item;
+ item = get_item (path);
+
+ hint->search_start = bh->b_blocknr;
+
+ if (!hint->formatted_node && is_indirect_le_ih (ih)) {
+ /* for indirect item: go to left and look for the first non-hole entry
+ in the indirect item */
+ if (pos_in_item == I_UNFM_NUM (ih))
+ pos_in_item--;
+// pos_in_item = I_UNFM_NUM (ih) - 1;
+ while (pos_in_item >= 0) {
+ int t=get_block_num(item,pos_in_item);
+ if (t) {
+ hint->search_start = t;
+ break;
+ }
+ pos_in_item --;
+ }
+ } else {
+ }
+
+ /* does result value fit into specified region? */
+ return;
+}
+
+/* should be, if formatted node, then try to put on first part of the device
+ specified as number of percent with mount option device, else try to put
+ on last of device. This is not to say it is good code to do so,
+ but the effect should be measured. */
+static void inline set_border_in_hint(struct super_block *s, reiserfs_blocknr_hint_t *hint)
+{
+ b_blocknr_t border = SB_BLOCK_COUNT(hint->th->t_super) / s->u.reiserfs_sb.s_alloc_options.border;
+
+ if (hint->formatted_node)
+ hint->end = border - 1;
+ else
+ hint->beg = border;
+}
+
+static void inline displace_large_file(reiserfs_blocknr_hint_t *hint)
+{
+ if ( TEST_OPTION(displace_based_on_dirid, hint->th->t_super))
+ hint->search_start = hint->beg + keyed_hash((char *)(&INODE_PKEY(hint->inode)->k_dir_id), 4) % (hint->end - hint->beg);
+ else
+ hint->search_start = hint->beg + keyed_hash((char *)(&INODE_PKEY(hint->inode)->k_objectid), 4) % (hint->end - hint->beg);
+}
+
+static void inline hash_formatted_node(reiserfs_blocknr_hint_t *hint)
+{
+ char * hash_in;
+
+ if (!hint->inode)
+ hash_in = (char*)&hint->key.k_dir_id;
+ else if ( TEST_OPTION(displace_based_on_dirid, hint->th->t_super))
+ hash_in = (char *)(&INODE_PKEY(hint->inode)->k_dir_id);
+ else
+ hash_in = (char *)(&INODE_PKEY(hint->inode)->k_objectid);
+
+ hint->search_start = hint->beg + keyed_hash(hash_in, 4) % (hint->end - hint->beg);
+}
+
+static int inline this_blocknr_allocation_would_make_it_a_large_file(reiserfs_blocknr_hint_t *hint)
+{
+ return hint->block == hint->th->t_super->u.reiserfs_sb.s_alloc_options.large_file_size;
+}
+
+#ifdef DISPLACE_NEW_PACKING_LOCALITIES
+static void inline displace_new_packing_locality (reiserfs_blocknr_hint_t *hint)
+{
+ struct key * key = &hint->key;
+
+ hint->th->displace_new_blocks = 0;
+ hint->search_start = hint->beg + keyed_hash((char*)(&key->k_objectid),4) % (hint->end - hint->beg);
+}
+#endif
+
+static int inline old_hashed_relocation (reiserfs_blocknr_hint_t * hint)
+{
+ unsigned long border;
+ unsigned long hash_in;
+
+ if (hint->formatted_node || hint->inode == NULL) {
+ return 0;
+ }
+
+ hash_in = le32_to_cpu((INODE_PKEY(hint->inode))->k_dir_id);
+ border = hint->beg + (unsigned long) keyed_hash(((char *) (&hash_in)), 4) % (hint->end - hint->beg - 1);
+ if (border > hint->search_start)
+ hint->search_start = border;
+
+ return 1;
+}
+
+static int inline old_way (reiserfs_blocknr_hint_t * hint)
+{
+ unsigned long border;
+
+ if (hint->formatted_node || hint->inode == NULL) {
+ return 0;
+ }
+
+ border = hint->beg + le32_to_cpu(INODE_PKEY(hint->inode)->k_dir_id) % (hint->end - hint->beg);
+ if (border > hint->search_start)
+ hint->search_start = border;
+
+ return 1;
+}
+
+static void inline hundredth_slices (reiserfs_blocknr_hint_t * hint)
+{
+ struct key * key = &hint->key;
+ unsigned long slice_start;
+
+ slice_start = (keyed_hash((char*)(&key->k_dir_id),4) % 100) * (hint->end / 100);
+ if ( slice_start > hint->search_start || slice_start + (hint->end / 100) <= hint->search_start) {
+ hint->search_start = slice_start;
+ }
+}
+
+static void inline determine_search_start(reiserfs_blocknr_hint_t *hint,
+ int amount_needed)
+{
+ struct super_block *s = hint->th->t_super;
+ hint->beg = 0;
+ hint->end = SB_BLOCK_COUNT(s) - 1;
+
+ /* This is former border algorithm. Now with tunable border offset */
+ if (concentrating_formatted_nodes(s))
+ set_border_in_hint(s, hint);
+
+#ifdef DISPLACE_NEW_PACKING_LOCALITIES
+ /* whenever we create a new directory, we displace it. At first we will
+ hash for location, later we might look for a moderately empty place for
+ it */
+ if (displacing_new_packing_localities(s)
+ && hint->th->displace_new_blocks) {
+ displace_new_packing_locality(hint);
+
+ /* we do not continue determine_search_start,
+ * if new packing locality is being displaced */
+ return;
+ }
+#endif
+
+ /* all persons should feel encouraged to add more special cases here and
+ * test them */
+
+ if (displacing_large_files(s) && !hint->formatted_node
+ && this_blocknr_allocation_would_make_it_a_large_file(hint)) {
+ displace_large_file(hint);
+ return;
+ }
+
+ /* attempt to copy a feature from old block allocator code */
+ if (TEST_OPTION(old_hashed_relocation, s) && !hint->formatted_node) {
+ old_hashed_relocation(hint);
+ }
+
+ /* if none of our special cases is relevant, use the left neighbor in the
+ tree order of the new node we are allocating for */
+ if (hint->formatted_node && TEST_OPTION(hashed_formatted_nodes,s)) {
+ hash_formatted_node(hint);
+ return;
+ }
+
+ get_left_neighbor(hint);
+
+ /* Mimic old block allocator behaviour, that is if VFS allowed for preallocation,
+ new blocks are displaced based on directory ID. Also, if suggested search_start
+ is less than last preallocated block, we start searching from it, assuming that
+ HDD dataflow is faster in forward direction */
+ if ( TEST_OPTION(old_way, s)) {
+ if (!hint->formatted_node) {
+ if ( !reiserfs_hashed_relocation(s))
+ old_way(hint);
+ else if (!reiserfs_no_unhashed_relocation(s))
+ old_hashed_relocation(hint);
+
+ if ( hint->inode && hint->search_start < hint->inode->u.reiserfs_i.i_prealloc_block)
+ hint->search_start = hint->inode->u.reiserfs_i.i_prealloc_block;
+ }
+ return;
+ }
+
+ /* This is an approach proposed by Hans */
+ if ( TEST_OPTION(hundredth_slices, s) && ! (displacing_large_files(s) && !hint->formatted_node)) {
+ hundredth_slices(hint);
+ return;
+ }
+
+ if (TEST_OPTION(old_hashed_relocation, s))
+ old_hashed_relocation(hint);
+ if (TEST_OPTION(new_hashed_relocation, s))
+ new_hashed_relocation(hint);
+ return;
+}
+
+static int determine_prealloc_size(reiserfs_blocknr_hint_t * hint)
+{
+ /* make minimum size a mount option and benchmark both ways */
+ /* we preallocate blocks only for regular files, specific size */
+ /* benchmark preallocating always and see what happens */
+
+ hint->prealloc_size = 0;
+
+ if (!hint->formatted_node && hint->preallocate) {
+ if (S_ISREG(hint->inode->i_mode)
+ && hint->inode->i_size >= hint->th->t_super->u.reiserfs_sb.s_alloc_options.preallocmin * hint->inode->i_sb->s_blocksize)
+ hint->prealloc_size = hint->th->t_super->u.reiserfs_sb.s_alloc_options.preallocsize - 1;
+ }
+ return CARRY_ON;
+}
+
+/* XXX I know it could be merged with upper-level function;
+ but may be result function would be too complex. */
+static inline int allocate_without_wrapping_disk (reiserfs_blocknr_hint_t * hint,
+ b_blocknr_t * new_blocknrs,
+ b_blocknr_t start, b_blocknr_t finish,
+ int amount_needed, int prealloc_size)
+{
+ int rest = amount_needed;
+ int nr_allocated;
+
+ while (rest > 0) {
+ nr_allocated = scan_bitmap (hint->th, &start, finish, 1,
+ rest + prealloc_size, !hint->formatted_node,
+ hint->block);
+
+ if (nr_allocated == 0) /* no new blocks allocated, return */
+ break;
+
+ /* fill free_blocknrs array first */
+ while (rest > 0 && nr_allocated > 0) {
+ * new_blocknrs ++ = start ++;
+ rest --; nr_allocated --;
+ }
+
+ /* do we have something to fill prealloc. array also ? */
+ if (nr_allocated > 0) {
+ /* it means prealloc_size was greater that 0 and we do preallocation */
+ list_add(&INODE_INFO(hint->inode)->i_prealloc_list,
+ &SB_JOURNAL(hint->th->t_super)->j_prealloc_list);
+ INODE_INFO(hint->inode)->i_prealloc_block = start;
+ INODE_INFO(hint->inode)->i_prealloc_count = nr_allocated;
+ break;
+ }
+ }
+
+ return (amount_needed - rest);
+}
+
+static inline int blocknrs_and_prealloc_arrays_from_search_start
+ (reiserfs_blocknr_hint_t *hint, b_blocknr_t *new_blocknrs, int amount_needed)
+{
+ struct super_block *s = hint->th->t_super;
+ b_blocknr_t start = hint->search_start;
+ b_blocknr_t finish = SB_BLOCK_COUNT(s) - 1;
+ int second_pass = 0;
+ int nr_allocated = 0;
+
+ determine_prealloc_size(hint);
+ while((nr_allocated
+ += allocate_without_wrapping_disk(hint, new_blocknrs + nr_allocated, start, finish,
+ amount_needed - nr_allocated, hint->prealloc_size))
+ < amount_needed) {
+
+ /* not all blocks were successfully allocated yet*/
+ if (second_pass) { /* it was a second pass; we must free all blocks */
+ while (nr_allocated --)
+ reiserfs_free_block(hint->th, new_blocknrs[nr_allocated]);
+
+ return NO_DISK_SPACE;
+ } else { /* refine search parameters for next pass */
+ second_pass = 1;
+ finish = start;
+ start = 0;
+ continue;
+ }
+ }
+ return CARRY_ON;
+}
+
+/* grab new blocknrs from preallocated list */
+/* return amount still needed after using them */
+static int use_preallocated_list_if_available (reiserfs_blocknr_hint_t *hint,
+ b_blocknr_t *new_blocknrs, int amount_needed)
+{
+ struct inode * inode = hint->inode;
+
+ if (INODE_INFO(inode)->i_prealloc_count > 0) {
+ while (amount_needed) {
+
+ *new_blocknrs ++ = INODE_INFO(inode)->i_prealloc_block ++;
+ INODE_INFO(inode)->i_prealloc_count --;
+
+ amount_needed --;
+
+ if (INODE_INFO(inode)->i_prealloc_count <= 0) {
+ list_del(&inode->u.reiserfs_i.i_prealloc_list);
+ break;
+ }
+ }
+ }
+ /* return amount still needed after using preallocated blocks */
+ return amount_needed;
+}
+
+int reiserfs_allocate_blocknrs(reiserfs_blocknr_hint_t *hint,
+ b_blocknr_t * new_blocknrs, int amount_needed,
+ int reserved_by_us /* Amount of blocks we have
+ already reserved */)
+{
+ int initial_amount_needed = amount_needed;
+ int ret;
+
+ /* Check if there is enough space, taking into account reserved space */
+ if ( SB_FREE_BLOCKS(hint->th->t_super) - hint->th->t_super->u.reiserfs_sb.reserved_blocks <
+ amount_needed - reserved_by_us)
+ return NO_DISK_SPACE;
+ /* should this be if !hint->inode && hint->preallocate? */
+ /* do you mean hint->formatted_node can be removed ? - Zam */
+ /* hint->formatted_node cannot be removed because we try to access
+ inode information here, and there is often no inode assotiated with
+ metadata allocations - green */
+
+ if (!hint->formatted_node && hint->preallocate) {
+ amount_needed = use_preallocated_list_if_available
+ (hint, new_blocknrs, amount_needed);
+ if (amount_needed == 0) /* all blocknrs we need we got from
+ prealloc. list */
+ return CARRY_ON;
+ new_blocknrs += (initial_amount_needed - amount_needed);
+ }
+
+ /* find search start and save it in hint structure */
+ determine_search_start(hint, amount_needed);
+
+ /* allocation itself; fill new_blocknrs and preallocation arrays */
+ ret = blocknrs_and_prealloc_arrays_from_search_start
+ (hint, new_blocknrs, amount_needed);
+
+ /* we used prealloc. list to fill (partially) new_blocknrs array. If final allocation fails we
+ * need to return blocks back to prealloc. list or just free them. -- Zam (I chose second
+ * variant) */
+
+ if (ret != CARRY_ON) {
+ while (amount_needed ++ < initial_amount_needed) {
+ reiserfs_free_block(hint->th, *(--new_blocknrs));
+ }
+ }
+ return ret;
+}
+
+/* These 2 functions are here to provide blocks reservation to the rest of kernel */
+/* Reserve @blocks amount of blocks in fs pointed by @sb. Caller must make sure
+ there are actually this much blocks on the FS available */
+void reiserfs_claim_blocks_to_be_allocated(
+ struct super_block *sb, /* super block of
+ filesystem where
+ blocks should be
+ reserved */
+ int blocks /* How much to reserve */
+ )
+{
+
+ /* Fast case, if reservation is zero - exit immediately. */
+ if ( !blocks )
+ return;
+
+ sb->u.reiserfs_sb.reserved_blocks += blocks;
+}
+
+/* Unreserve @blocks amount of blocks in fs pointed by @sb */
+void reiserfs_release_claimed_blocks(
+ struct super_block *sb, /* super block of
+ filesystem where
+ blocks should be
+ reserved */
+ int blocks /* How much to unreserve */
+ )
+{
+
+ /* Fast case, if unreservation is zero - exit immediately. */
+ if ( !blocks )
+ return;
+
+ sb->u.reiserfs_sb.reserved_blocks -= blocks;
+ RFALSE( sb->u.reiserfs_sb.reserved_blocks < 0, "amount of blocks reserved became zero?");
+}
diff --git a/fs/reiserfs/buffer2.c b/fs/reiserfs/buffer2.c
new file mode 100644
index 00000000000000..63ca8bbcbb5bd0
--- /dev/null
+++ b/fs/reiserfs/buffer2.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2000-2002 by Hans Reiser, licensing governed by reiserfs/README
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/locks.h>
+#include <linux/reiserfs_fs.h>
+#include <linux/smp_lock.h>
+#include <linux/kernel_stat.h>
+
+/*
+ * wait_buffer_until_released
+ * reiserfs_bread
+ */
+
+/* when we allocate a new block (get_new_buffer, get_empty_nodes) and
+ get buffer for it, it is possible that it is held by someone else
+ or even by this process. In this function we wait until all other
+ holders release buffer. To make sure, that current process does not
+ hold we did free all buffers in tree balance structure
+ (get_empty_nodes and get_nodes_for_preserving) or in path structure
+ only (get_new_buffer) just before calling this */
+void wait_buffer_until_released (const struct buffer_head * bh)
+{
+ int repeat_counter = 0;
+
+ while (atomic_read (&(bh->b_count)) > 1) {
+
+ if ( !(++repeat_counter % 30000000) ) {
+ reiserfs_warning (NULL, "vs-3050: wait_buffer_until_released: nobody releases buffer (%b). Still waiting (%d) %cJDIRTY %cJWAIT\n",
+ bh, repeat_counter, buffer_journaled(bh) ? ' ' : '!',
+ buffer_journal_dirty(bh) ? ' ' : '!');
+ }
+ run_task_queue(&tq_disk);
+ yield();
+ }
+ if (repeat_counter > 30000000) {
+ reiserfs_warning(NULL, "vs-3051: done waiting, ignore vs-3050 messages for (%b)\n", bh) ;
+ }
+}
+
+/*
+ * reiserfs_bread() reads a specified block and returns the buffer that contains
+ * it. It returns NULL if the block was unreadable.
+ */
+/* It first tries to find the block in cache, and if it cannot do so
+ then it creates a new buffer and schedules I/O to read the
+ block. */
+/* The function is NOT SCHEDULE-SAFE! */
+struct buffer_head * reiserfs_bread (struct super_block *super, int n_block, int n_size)
+{
+ struct buffer_head *result;
+ PROC_EXP( unsigned int ctx_switches = kstat.context_swtch );
+
+ result = bread (super -> s_dev, n_block, n_size);
+ PROC_INFO_INC( super, breads );
+ PROC_EXP( if( kstat.context_swtch != ctx_switches )
+ PROC_INFO_INC( super, bread_miss ) );
+ return result;
+}
+
+struct buffer_head * journal_bread (struct super_block *s, int block)
+{
+ return bread (SB_JOURNAL_DEV(s), block, s->s_blocksize );
+}
+
+struct buffer_head * journal_getblk (struct super_block *s, int block)
+{
+ return getblk (SB_JOURNAL_DEV(s), block, s->s_blocksize );
+}
+
+struct buffer_head * journal_get_hash_table (struct super_block *s, int block)
+{
+ return get_hash_table (SB_JOURNAL_DEV(s), block, s->s_blocksize );
+}
diff --git a/fs/reiserfs/dir.c b/fs/reiserfs/dir.c
new file mode 100644
index 00000000000000..91aeb603ca084c
--- /dev/null
+++ b/fs/reiserfs/dir.c
@@ -0,0 +1,254 @@
+/*
+ * Copyright 2000-2002 by Hans Reiser, licensing governed by reiserfs/README
+ */
+
+#include <linux/config.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/reiserfs_fs.h>
+#include <linux/stat.h>
+#include <linux/smp_lock.h>
+#include <asm/uaccess.h>
+
+extern struct key MIN_KEY;
+
+static int reiserfs_readdir (struct file *, void *, filldir_t);
+int reiserfs_dir_fsync(struct file *filp, struct dentry *dentry, int datasync) ;
+
+struct file_operations reiserfs_dir_operations = {
+ read: generic_read_dir,
+ readdir: reiserfs_readdir,
+ fsync: reiserfs_dir_fsync,
+ ioctl: reiserfs_ioctl,
+};
+
+int reiserfs_dir_fsync(struct file *filp, struct dentry *dentry, int datasync) {
+ lock_kernel();
+ reiserfs_commit_for_inode(dentry->d_inode) ;
+ unlock_kernel() ;
+ return 0 ;
+}
+
+
+#define store_ih(where,what) copy_item_head (where, what)
+
+//
+static int reiserfs_readdir (struct file * filp, void * dirent, filldir_t filldir)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ struct cpu_key pos_key; /* key of current position in the directory (key of directory entry) */
+ INITIALIZE_PATH (path_to_entry);
+ struct buffer_head * bh;
+ int item_num, entry_num;
+ const struct key * rkey;
+ struct item_head * ih, tmp_ih;
+ int search_res;
+ char * local_buf;
+ loff_t next_pos;
+ char small_buf[32] ; /* avoid kmalloc if we can */
+ struct reiserfs_dir_entry de;
+
+
+ reiserfs_check_lock_depth("readdir") ;
+
+ /* form key for search the next directory entry using f_pos field of
+ file structure */
+ make_cpu_key (&pos_key, inode, (filp->f_pos) ? (filp->f_pos) : DOT_OFFSET,
+ TYPE_DIRENTRY, 3);
+ next_pos = cpu_key_k_offset (&pos_key);
+
+ /* reiserfs_warning (inode->i_sb, "reiserfs_readdir 1: f_pos = %Ld\n", filp->f_pos);*/
+
+ while (1) {
+ research:
+ /* search the directory item, containing entry with specified key */
+ search_res = search_by_entry_key (inode->i_sb, &pos_key, &path_to_entry, &de);
+ if (search_res == IO_ERROR) {
+ // FIXME: we could just skip part of directory which could
+ // not be read
+ return -EIO;
+ }
+ entry_num = de.de_entry_num;
+ bh = de.de_bh;
+ item_num = de.de_item_num;
+ ih = de.de_ih;
+ store_ih (&tmp_ih, ih);
+
+ /* we must have found item, that is item of this directory, */
+ RFALSE( COMP_SHORT_KEYS (&(ih->ih_key), &pos_key),
+ "vs-9000: found item %h does not match to dir we readdir %K",
+ ih, &pos_key);
+ RFALSE( item_num > B_NR_ITEMS (bh) - 1,
+ "vs-9005 item_num == %d, item amount == %d",
+ item_num, B_NR_ITEMS (bh));
+
+ /* and entry must be not more than number of entries in the item */
+ RFALSE( I_ENTRY_COUNT (ih) < entry_num,
+ "vs-9010: entry number is too big %d (%d)",
+ entry_num, I_ENTRY_COUNT (ih));
+
+ if (search_res == POSITION_FOUND || entry_num < I_ENTRY_COUNT (ih)) {
+ /* go through all entries in the directory item beginning from the entry, that has been found */
+ struct reiserfs_de_head * deh = B_I_DEH (bh, ih) + entry_num;
+
+ for (; entry_num < I_ENTRY_COUNT (ih); entry_num ++, deh ++) {
+ int d_reclen;
+ char * d_name;
+ off_t d_off;
+ ino_t d_ino;
+
+ if (!de_visible (deh))
+ /* it is hidden entry */
+ continue;
+ d_reclen = entry_length (bh, ih, entry_num);
+ d_name = B_I_DEH_ENTRY_FILE_NAME (bh, ih, deh);
+ if (!d_name[d_reclen - 1])
+ d_reclen = strlen (d_name);
+
+ if (d_reclen > REISERFS_MAX_NAME(inode->i_sb->s_blocksize)){
+ /* too big to send back to VFS */
+ continue ;
+ }
+ d_off = deh_offset (deh);
+ filp->f_pos = d_off ;
+ d_ino = deh_objectid (deh);
+ if (d_reclen <= 32) {
+ local_buf = small_buf ;
+ } else {
+ local_buf = reiserfs_kmalloc(d_reclen, GFP_NOFS, inode->i_sb) ;
+ if (!local_buf) {
+ pathrelse (&path_to_entry);
+ return -ENOMEM ;
+ }
+ if (item_moved (&tmp_ih, &path_to_entry)) {
+ reiserfs_kfree(local_buf, d_reclen, inode->i_sb) ;
+ goto research;
+ }
+ }
+ // Note, that we copy name to user space via temporary
+ // buffer (local_buf) because filldir will block if
+ // user space buffer is swapped out. At that time
+ // entry can move to somewhere else
+ memcpy (local_buf, d_name, d_reclen);
+ if (filldir (dirent, local_buf, d_reclen, d_off, d_ino,
+ DT_UNKNOWN) < 0) {
+ if (local_buf != small_buf) {
+ reiserfs_kfree(local_buf, d_reclen, inode->i_sb) ;
+ }
+ goto end;
+ }
+ if (local_buf != small_buf) {
+ reiserfs_kfree(local_buf, d_reclen, inode->i_sb) ;
+ }
+
+ // next entry should be looked for with such offset
+ next_pos = deh_offset (deh) + 1;
+
+ if (item_moved (&tmp_ih, &path_to_entry)) {
+ goto research;
+ }
+ } /* for */
+ }
+
+ if (item_num != B_NR_ITEMS (bh) - 1)
+ // end of directory has been reached
+ goto end;
+
+ /* item we went through is last item of node. Using right
+ delimiting key check is it directory end */
+ rkey = get_rkey (&path_to_entry, inode->i_sb);
+ if (! comp_le_keys (rkey, &MIN_KEY)) {
+ /* set pos_key to key, that is the smallest and greater
+ that key of the last entry in the item */
+ set_cpu_key_k_offset (&pos_key, next_pos);
+ continue;
+ }
+
+ if ( COMP_SHORT_KEYS (rkey, &pos_key)) {
+ // end of directory has been reached
+ goto end;
+ }
+
+ /* directory continues in the right neighboring block */
+ set_cpu_key_k_offset (&pos_key, le_key_k_offset (KEY_FORMAT_3_5, rkey));
+
+ } /* while */
+
+
+ end:
+
+ filp->f_pos = next_pos;
+ pathrelse (&path_to_entry);
+ reiserfs_check_path(&path_to_entry) ;
+ UPDATE_ATIME(inode) ;
+ return 0;
+}
+
+/* compose directory item containing "." and ".." entries (entries are
+ not aligned to 4 byte boundary) */
+/* the last four params are LE */
+void make_empty_dir_item_v1 (char * body, __u32 dirid, __u32 objid,
+ __u32 par_dirid, __u32 par_objid)
+{
+ struct reiserfs_de_head * deh;
+
+ memset (body, 0, EMPTY_DIR_SIZE_V1);
+ deh = (struct reiserfs_de_head *)body;
+
+ /* direntry header of "." */
+ put_deh_offset( &(deh[0]), DOT_OFFSET );
+ /* these two are from make_le_item_head, and are are LE */
+ deh[0].deh_dir_id = dirid;
+ deh[0].deh_objectid = objid;
+ deh[0].deh_state = 0; /* Endian safe if 0 */
+ put_deh_location( &(deh[0]), EMPTY_DIR_SIZE_V1 - strlen( "." ));
+ mark_de_visible(&(deh[0]));
+
+ /* direntry header of ".." */
+ put_deh_offset( &(deh[1]), DOT_DOT_OFFSET);
+ /* key of ".." for the root directory */
+ /* these two are from the inode, and are are LE */
+ deh[1].deh_dir_id = par_dirid;
+ deh[1].deh_objectid = par_objid;
+ deh[1].deh_state = 0; /* Endian safe if 0 */
+ put_deh_location( &(deh[1]), deh_location( &(deh[0]) ) - strlen( ".." ) );
+ mark_de_visible(&(deh[1]));
+
+ /* copy ".." and "." */
+ memcpy (body + deh_location( &(deh[0]) ), ".", 1);
+ memcpy (body + deh_location( &(deh[1]) ), "..", 2);
+}
+
+/* compose directory item containing "." and ".." entries */
+void make_empty_dir_item (char * body, __u32 dirid, __u32 objid,
+ __u32 par_dirid, __u32 par_objid)
+{
+ struct reiserfs_de_head * deh;
+
+ memset (body, 0, EMPTY_DIR_SIZE);
+ deh = (struct reiserfs_de_head *)body;
+
+ /* direntry header of "." */
+ put_deh_offset( &(deh[0]), DOT_OFFSET );
+ /* these two are from make_le_item_head, and are are LE */
+ deh[0].deh_dir_id = dirid;
+ deh[0].deh_objectid = objid;
+ deh[0].deh_state = 0; /* Endian safe if 0 */
+ put_deh_location( &(deh[0]), EMPTY_DIR_SIZE - ROUND_UP( strlen( "." ) ) );
+ mark_de_visible(&(deh[0]));
+
+ /* direntry header of ".." */
+ put_deh_offset( &(deh[1]), DOT_DOT_OFFSET );
+ /* key of ".." for the root directory */
+ /* these two are from the inode, and are are LE */
+ deh[1].deh_dir_id = par_dirid;
+ deh[1].deh_objectid = par_objid;
+ deh[1].deh_state = 0; /* Endian safe if 0 */
+ put_deh_location( &(deh[1]), deh_location( &(deh[0])) - ROUND_UP( strlen( ".." ) ) );
+ mark_de_visible(&(deh[1]));
+
+ /* copy ".." and "." */
+ memcpy (body + deh_location( &(deh[0]) ), ".", 1);
+ memcpy (body + deh_location( &(deh[1]) ), "..", 2);
+}
diff --git a/fs/reiserfs/do_balan.c b/fs/reiserfs/do_balan.c
new file mode 100644
index 00000000000000..3dcfc05f2bddfb
--- /dev/null
+++ b/fs/reiserfs/do_balan.c
@@ -0,0 +1,1609 @@
+/*
+ * Copyright 2000-2002 by Hans Reiser, licensing governed by reiserfs/README
+ */
+
+/* Now we have all buffers that must be used in balancing of the tree */
+/* Further calculations can not cause schedule(), and thus the buffer */
+/* tree will be stable until the balancing will be finished */
+/* balance the tree according to the analysis made before, */
+/* and using buffers obtained after all above. */
+
+
+/**
+ ** balance_leaf_when_delete
+ ** balance_leaf
+ ** do_balance
+ **
+ **/
+
+#include <linux/config.h>
+#include <asm/uaccess.h>
+#include <linux/sched.h>
+#include <linux/reiserfs_fs.h>
+
+#ifdef CONFIG_REISERFS_CHECK
+
+struct tree_balance * cur_tb = NULL; /* detects whether more than one
+ copy of tb exists as a means
+ of checking whether schedule
+ is interrupting do_balance */
+#endif
+
+
+inline void do_balance_mark_leaf_dirty (struct tree_balance * tb,
+ struct buffer_head * bh, int flag)
+{
+ if (reiserfs_dont_log(tb->tb_sb)) {
+ if (!test_and_set_bit(BH_Dirty, &bh->b_state)) {
+ __mark_buffer_dirty(bh) ;
+ tb->need_balance_dirty = 1;
+ }
+ } else {
+ int windex = push_journal_writer("do_balance") ;
+ journal_mark_dirty(tb->transaction_handle, tb->transaction_handle->t_super, bh) ;
+ pop_journal_writer(windex) ;
+ }
+}
+
+#define do_balance_mark_internal_dirty do_balance_mark_leaf_dirty
+#define do_balance_mark_sb_dirty do_balance_mark_leaf_dirty
+
+
+/* summary:
+ if deleting something ( tb->insert_size[0] < 0 )
+ return(balance_leaf_when_delete()); (flag d handled here)
+ else
+ if lnum is larger than 0 we put items into the left node
+ if rnum is larger than 0 we put items into the right node
+ if snum1 is larger than 0 we put items into the new node s1
+ if snum2 is larger than 0 we put items into the new node s2
+Note that all *num* count new items being created.
+
+It would be easier to read balance_leaf() if each of these summary
+lines was a separate procedure rather than being inlined. I think
+that there are many passages here and in balance_leaf_when_delete() in
+which two calls to one procedure can replace two passages, and it
+might save cache space and improve software maintenance costs to do so.
+
+Vladimir made the perceptive comment that we should offload most of
+the decision making in this function into fix_nodes/check_balance, and
+then create some sort of structure in tb that says what actions should
+be performed by do_balance.
+
+-Hans */
+
+
+
+/* Balance leaf node in case of delete or cut: insert_size[0] < 0
+ *
+ * lnum, rnum can have values >= -1
+ * -1 means that the neighbor must be joined with S
+ * 0 means that nothing should be done with the neighbor
+ * >0 means to shift entirely or partly the specified number of items to the neighbor
+ */
+static int balance_leaf_when_delete (struct tree_balance * tb, int flag)
+{
+ struct buffer_head * tbS0 = PATH_PLAST_BUFFER (tb->tb_path);
+ int item_pos = PATH_LAST_POSITION (tb->tb_path);
+ int pos_in_item = tb->tb_path->pos_in_item;
+ struct buffer_info bi;
+ int n;
+ struct item_head * ih;
+
+ RFALSE( tb->FR[0] && B_LEVEL (tb->FR[0]) != DISK_LEAF_NODE_LEVEL + 1,
+ "vs- 12000: level: wrong FR %z\n", tb->FR[0]);
+ RFALSE( tb->blknum[0] > 1,
+ "PAP-12005: tb->blknum == %d, can not be > 1", tb->blknum[0]);
+ RFALSE( ! tb->blknum[0] && ! PATH_H_PPARENT(tb->tb_path, 0),
+ "PAP-12010: tree can not be empty");
+
+ ih = B_N_PITEM_HEAD (tbS0, item_pos);
+
+ /* Delete or truncate the item */
+
+ switch (flag) {
+ case M_DELETE: /* delete item in S[0] */
+
+ RFALSE( ih_item_len(ih) + IH_SIZE != -tb->insert_size[0],
+ "vs-12013: mode Delete, insert size %d, ih to be deleted %h",
+ -tb->insert_size [0], ih);
+
+ bi.tb = tb;
+ bi.bi_bh = tbS0;
+ bi.bi_parent = PATH_H_PPARENT (tb->tb_path, 0);
+ bi.bi_position = PATH_H_POSITION (tb->tb_path, 1);
+ leaf_delete_items (&bi, 0, item_pos, 1, -1);
+
+ if ( ! item_pos && tb->CFL[0] ) {
+ if ( B_NR_ITEMS(tbS0) ) {
+ replace_key(tb, tb->CFL[0],tb->lkey[0],tbS0,0);
+ }
+ else {
+ if ( ! PATH_H_POSITION (tb->tb_path, 1) )
+ replace_key(tb, tb->CFL[0],tb->lkey[0],PATH_H_PPARENT(tb->tb_path, 0),0);
+ }
+ }
+
+ RFALSE( ! item_pos && !tb->CFL[0],
+ "PAP-12020: tb->CFL[0]==%p, tb->L[0]==%p", tb->CFL[0], tb->L[0]);
+
+ break;
+
+ case M_CUT: { /* cut item in S[0] */
+ bi.tb = tb;
+ bi.bi_bh = tbS0;
+ bi.bi_parent = PATH_H_PPARENT (tb->tb_path, 0);
+ bi.bi_position = PATH_H_POSITION (tb->tb_path, 1);
+ if (is_direntry_le_ih (ih)) {
+
+ /* UFS unlink semantics are such that you can only delete one directory entry at a time. */
+ /* when we cut a directory tb->insert_size[0] means number of entries to be cut (always 1) */
+ tb->insert_size[0] = -1;
+ leaf_cut_from_buffer (&bi, item_pos, pos_in_item, -tb->insert_size[0]);
+
+ RFALSE( ! item_pos && ! pos_in_item && ! tb->CFL[0],
+ "PAP-12030: can not change delimiting key. CFL[0]=%p",
+ tb->CFL[0]);
+
+ if ( ! item_pos && ! pos_in_item && tb->CFL[0] ) {
+ replace_key(tb, tb->CFL[0],tb->lkey[0],tbS0,0);
+ }
+ } else {
+ leaf_cut_from_buffer (&bi, item_pos, pos_in_item, -tb->insert_size[0]);
+
+ RFALSE( ! ih_item_len(ih),
+ "PAP-12035: cut must leave non-zero dynamic length of item");
+ }
+ break;
+ }
+
+ default:
+ print_cur_tb ("12040");
+ reiserfs_panic (tb->tb_sb, "PAP-12040: balance_leaf_when_delete: unexpectable mode: %s(%d)",
+ (flag == M_PASTE) ? "PASTE" : ((flag == M_INSERT) ? "INSERT" : "UNKNOWN"), flag);
+ }
+
+ /* the rule is that no shifting occurs unless by shifting a node can be freed */
+ n = B_NR_ITEMS(tbS0);
+ if ( tb->lnum[0] ) /* L[0] takes part in balancing */
+ {
+ if ( tb->lnum[0] == -1 ) /* L[0] must be joined with S[0] */
+ {
+ if ( tb->rnum[0] == -1 ) /* R[0] must be also joined with S[0] */
+ {
+ if ( tb->FR[0] == PATH_H_PPARENT(tb->tb_path, 0) )
+ {
+ /* all contents of all the 3 buffers will be in L[0] */
+ if ( PATH_H_POSITION (tb->tb_path, 1) == 0 && 1 < B_NR_ITEMS(tb->FR[0]) )
+ replace_key(tb, tb->CFL[0],tb->lkey[0],tb->FR[0],1);
+
+ leaf_move_items (LEAF_FROM_S_TO_L, tb, n, -1, 0);
+ leaf_move_items (LEAF_FROM_R_TO_L, tb, B_NR_ITEMS(tb->R[0]), -1, 0);
+
+ reiserfs_invalidate_buffer (tb, tbS0);
+ reiserfs_invalidate_buffer (tb, tb->R[0]);
+
+ return 0;
+ }
+ /* all contents of all the 3 buffers will be in R[0] */
+ leaf_move_items (LEAF_FROM_S_TO_R, tb, n, -1, 0);
+ leaf_move_items (LEAF_FROM_L_TO_R, tb, B_NR_ITEMS(tb->L[0]), -1, 0);
+
+ /* right_delimiting_key is correct in R[0] */
+ replace_key(tb, tb->CFR[0],tb->rkey[0],tb->R[0],0);
+
+ reiserfs_invalidate_buffer (tb, tbS0);
+ reiserfs_invalidate_buffer (tb, tb->L[0]);
+
+ return -1;
+ }
+
+ RFALSE( tb->rnum[0] != 0,
+ "PAP-12045: rnum must be 0 (%d)", tb->rnum[0]);
+ /* all contents of L[0] and S[0] will be in L[0] */
+ leaf_shift_left(tb, n, -1);
+
+ reiserfs_invalidate_buffer (tb, tbS0);
+
+ return 0;
+ }
+ /* a part of contents of S[0] will be in L[0] and the rest part of S[0] will be in R[0] */
+
+ RFALSE( ( tb->lnum[0] + tb->rnum[0] < n ) ||
+ ( tb->lnum[0] + tb->rnum[0] > n+1 ),
+ "PAP-12050: rnum(%d) and lnum(%d) and item number(%d) in S[0] are not consistent",
+ tb->rnum[0], tb->lnum[0], n);
+ RFALSE( ( tb->lnum[0] + tb->rnum[0] == n ) &&
+ (tb->lbytes != -1 || tb->rbytes != -1),
+ "PAP-12055: bad rbytes (%d)/lbytes (%d) parameters when items are not split",
+ tb->rbytes, tb->lbytes);
+ RFALSE( ( tb->lnum[0] + tb->rnum[0] == n + 1 ) &&
+ (tb->lbytes < 1 || tb->rbytes != -1),
+ "PAP-12060: bad rbytes (%d)/lbytes (%d) parameters when items are split",
+ tb->rbytes, tb->lbytes);
+
+ leaf_shift_left (tb, tb->lnum[0], tb->lbytes);
+ leaf_shift_right(tb, tb->rnum[0], tb->rbytes);
+
+ reiserfs_invalidate_buffer (tb, tbS0);
+
+ return 0;
+ }
+
+ if ( tb->rnum[0] == -1 ) {
+ /* all contents of R[0] and S[0] will be in R[0] */
+ leaf_shift_right(tb, n, -1);
+ reiserfs_invalidate_buffer (tb, tbS0);
+ return 0;
+ }
+
+ RFALSE( tb->rnum[0],
+ "PAP-12065: bad rnum parameter must be 0 (%d)", tb->rnum[0]);
+ return 0;
+}
+
+
+static int balance_leaf (struct tree_balance * tb,
+ struct item_head * ih, /* item header of inserted item (this is on little endian) */
+ const char * body, /* body of inserted item or bytes to paste */
+ int flag, /* i - insert, d - delete, c - cut, p - paste
+ (see comment to do_balance) */
+ struct item_head * insert_key, /* in our processing of one level we sometimes determine what
+ must be inserted into the next higher level. This insertion
+ consists of a key or two keys and their corresponding
+ pointers */
+ struct buffer_head ** insert_ptr /* inserted node-ptrs for the next level */
+ )
+{
+ struct buffer_head * tbS0 = PATH_PLAST_BUFFER (tb->tb_path);
+ int item_pos = PATH_LAST_POSITION (tb->tb_path); /* index into the array of item headers in S[0]
+ of the affected item */
+ struct buffer_info bi;
+ struct buffer_head *S_new[2]; /* new nodes allocated to hold what could not fit into S */
+ int snum[2]; /* number of items that will be placed
+ into S_new (includes partially shifted
+ items) */
+ int sbytes[2]; /* if an item is partially shifted into S_new then
+ if it is a directory item
+ it is the number of entries from the item that are shifted into S_new
+ else
+ it is the number of bytes from the item that are shifted into S_new
+ */
+ int n, i;
+ int ret_val;
+ int pos_in_item;
+ int zeros_num;
+
+ PROC_INFO_INC( tb -> tb_sb, balance_at[ 0 ] );
+
+ /* Make balance in case insert_size[0] < 0 */
+ if ( tb->insert_size[0] < 0 )
+ return balance_leaf_when_delete (tb, flag);
+
+ zeros_num = 0;
+ if (flag == M_INSERT && body == 0)
+ zeros_num = ih_item_len( ih );
+
+ pos_in_item = tb->tb_path->pos_in_item;
+ /* for indirect item pos_in_item is measured in unformatted node
+ pointers. Recalculate to bytes */
+ if (flag != M_INSERT && is_indirect_le_ih (B_N_PITEM_HEAD (tbS0, item_pos)))
+ pos_in_item *= UNFM_P_SIZE;
+
+ if ( tb->lnum[0] > 0 ) {
+ /* Shift lnum[0] items from S[0] to the left neighbor L[0] */
+ if ( item_pos < tb->lnum[0] ) {
+ /* new item or it part falls to L[0], shift it too */
+ n = B_NR_ITEMS(tb->L[0]);
+
+ switch (flag) {
+ case M_INSERT: /* insert item into L[0] */
+
+ if ( item_pos == tb->lnum[0] - 1 && tb->lbytes != -1 ) {
+ /* part of new item falls into L[0] */
+ int new_item_len;
+ int version;
+
+ ret_val = leaf_shift_left (tb, tb->lnum[0]-1, -1);
+
+ /* Calculate item length to insert to S[0] */
+ new_item_len = ih_item_len(ih) - tb->lbytes;
+ /* Calculate and check item length to insert to L[0] */
+ put_ih_item_len(ih, ih_item_len(ih) - new_item_len );
+
+ RFALSE( ih_item_len(ih) <= 0,
+ "PAP-12080: there is nothing to insert into L[0]: ih_item_len=%d",
+ ih_item_len(ih));
+
+ /* Insert new item into L[0] */
+ bi.tb = tb;
+ bi.bi_bh = tb->L[0];
+ bi.bi_parent = tb->FL[0];
+ bi.bi_position = get_left_neighbor_position (tb, 0);
+ leaf_insert_into_buf (&bi, n + item_pos - ret_val, ih, body,
+ zeros_num > ih_item_len(ih) ? ih_item_len(ih) : zeros_num);
+
+ version = ih_version (ih);
+
+ /* Calculate key component, item length and body to insert into S[0] */
+ set_le_ih_k_offset( ih, le_ih_k_offset( ih ) + (tb->lbytes << (is_indirect_le_ih(ih)?tb->tb_sb->s_blocksize_bits-UNFM_P_SHIFT:0)) );
+
+ put_ih_item_len( ih, new_item_len );
+ if ( tb->lbytes > zeros_num ) {
+ body += (tb->lbytes - zeros_num);
+ zeros_num = 0;
+ }
+ else
+ zeros_num -= tb->lbytes;
+
+ RFALSE( ih_item_len(ih) <= 0,
+ "PAP-12085: there is nothing to insert into S[0]: ih_item_len=%d",
+ ih_item_len(ih));
+ } else {
+ /* new item in whole falls into L[0] */
+ /* Shift lnum[0]-1 items to L[0] */
+ ret_val = leaf_shift_left(tb, tb->lnum[0]-1, tb->lbytes);
+ /* Insert new item into L[0] */
+ bi.tb = tb;
+ bi.bi_bh = tb->L[0];
+ bi.bi_parent = tb->FL[0];
+ bi.bi_position = get_left_neighbor_position (tb, 0);
+ leaf_insert_into_buf (&bi, n + item_pos - ret_val, ih, body, zeros_num);
+ tb->insert_size[0] = 0;
+ zeros_num = 0;
+ }
+ break;
+
+ case M_PASTE: /* append item in L[0] */
+
+ if ( item_pos == tb->lnum[0] - 1 && tb->lbytes != -1 ) {
+ /* we must shift the part of the appended item */
+ if ( is_direntry_le_ih (B_N_PITEM_HEAD (tbS0, item_pos))) {
+
+ RFALSE( zeros_num,
+ "PAP-12090: illegal parameter in case of a directory");
+ /* directory item */
+ if ( tb->lbytes > pos_in_item ) {
+ /* new directory entry falls into L[0] */
+ struct item_head * pasted;
+ int l_pos_in_item = pos_in_item;
+
+ /* Shift lnum[0] - 1 items in whole. Shift lbytes - 1 entries from given directory item */
+ ret_val = leaf_shift_left(tb, tb->lnum[0], tb->lbytes - 1);
+ if ( ret_val && ! item_pos ) {
+ pasted = B_N_PITEM_HEAD(tb->L[0],B_NR_ITEMS(tb->L[0])-1);
+ l_pos_in_item += I_ENTRY_COUNT(pasted) - (tb->lbytes-1);
+ }
+
+ /* Append given directory entry to directory item */
+ bi.tb = tb;
+ bi.bi_bh = tb->L[0];
+ bi.bi_parent = tb->FL[0];
+ bi.bi_position = get_left_neighbor_position (tb, 0);
+ leaf_paste_in_buffer (&bi, n + item_pos - ret_val, l_pos_in_item,
+ tb->insert_size[0], body, zeros_num);
+
+ /* previous string prepared space for pasting new entry, following string pastes this entry */
+
+ /* when we have merge directory item, pos_in_item has been changed too */
+
+ /* paste new directory entry. 1 is entry number */
+ leaf_paste_entries (bi.bi_bh, n + item_pos - ret_val, l_pos_in_item, 1,
+ (struct reiserfs_de_head *)body,
+ body + DEH_SIZE, tb->insert_size[0]
+ );
+ tb->insert_size[0] = 0;
+ } else {
+ /* new directory item doesn't fall into L[0] */
+ /* Shift lnum[0]-1 items in whole. Shift lbytes directory entries from directory item number lnum[0] */
+ leaf_shift_left (tb, tb->lnum[0], tb->lbytes);
+ }
+ /* Calculate new position to append in item body */
+ pos_in_item -= tb->lbytes;
+ }
+ else {
+ /* regular object */
+ RFALSE( tb->lbytes <= 0,
+ "PAP-12095: there is nothing to shift to L[0]. lbytes=%d",
+ tb->lbytes);
+ RFALSE( pos_in_item != ih_item_len(B_N_PITEM_HEAD(tbS0, item_pos)),
+ "PAP-12100: incorrect position to paste: item_len=%d, pos_in_item=%d",
+ ih_item_len(B_N_PITEM_HEAD(tbS0,item_pos)), pos_in_item);
+
+ if ( tb->lbytes >= pos_in_item ) {
+ /* appended item will be in L[0] in whole */
+ int l_n;
+
+ /* this bytes number must be appended to the last item of L[h] */
+ l_n = tb->lbytes - pos_in_item;
+
+ /* Calculate new insert_size[0] */
+ tb->insert_size[0] -= l_n;
+
+ RFALSE( tb->insert_size[0] <= 0,
+ "PAP-12105: there is nothing to paste into L[0]. insert_size=%d",
+ tb->insert_size[0]);
+ ret_val = leaf_shift_left(tb,tb->lnum[0],
+ ih_item_len(B_N_PITEM_HEAD(tbS0,item_pos)));
+ /* Append to body of item in L[0] */
+ bi.tb = tb;
+ bi.bi_bh = tb->L[0];
+ bi.bi_parent = tb->FL[0];
+ bi.bi_position = get_left_neighbor_position (tb, 0);
+ leaf_paste_in_buffer(
+ &bi,n + item_pos - ret_val,
+ ih_item_len( B_N_PITEM_HEAD(tb->L[0],n+item_pos-ret_val)),
+ l_n,body, zeros_num > l_n ? l_n : zeros_num
+ );
+ /* 0-th item in S0 can be only of DIRECT type when l_n != 0*/
+ {
+ int version;
+ int temp_l = l_n;
+
+ RFALSE (ih_item_len (B_N_PITEM_HEAD (tbS0, 0)),
+ "PAP-12106: item length must be 0");
+ RFALSE (comp_short_le_keys (B_N_PKEY (tbS0, 0),
+ B_N_PKEY (tb->L[0],
+ n + item_pos - ret_val)),
+ "PAP-12107: items must be of the same file");
+ if (is_indirect_le_ih(B_N_PITEM_HEAD (tb->L[0],
+ n + item_pos - ret_val))) {
+ temp_l = l_n << (tb->tb_sb->s_blocksize_bits - UNFM_P_SHIFT);
+ }
+ /* update key of first item in S0 */
+ version = ih_version (B_N_PITEM_HEAD (tbS0, 0));
+ set_le_key_k_offset (version, B_N_PKEY (tbS0, 0),
+ le_key_k_offset (version, B_N_PKEY (tbS0, 0)) + temp_l);
+ /* update left delimiting key */
+ set_le_key_k_offset (version, B_N_PDELIM_KEY(tb->CFL[0],tb->lkey[0]),
+ le_key_k_offset (version, B_N_PDELIM_KEY(tb->CFL[0],tb->lkey[0])) + temp_l);
+ }
+
+ /* Calculate new body, position in item and insert_size[0] */
+ if ( l_n > zeros_num ) {
+ body += (l_n - zeros_num);
+ zeros_num = 0;
+ }
+ else
+ zeros_num -= l_n;
+ pos_in_item = 0;
+
+ RFALSE( comp_short_le_keys
+ (B_N_PKEY(tbS0,0),
+ B_N_PKEY(tb->L[0],B_NR_ITEMS(tb->L[0])-1)) ||
+
+ !op_is_left_mergeable
+ (B_N_PKEY (tbS0, 0), tbS0->b_size) ||
+ !op_is_left_mergeable
+ (B_N_PDELIM_KEY(tb->CFL[0],tb->lkey[0]),
+ tbS0->b_size),
+ "PAP-12120: item must be merge-able with left neighboring item");
+ }
+ else /* only part of the appended item will be in L[0] */
+ {
+ /* Calculate position in item for append in S[0] */
+ pos_in_item -= tb->lbytes;
+
+ RFALSE( pos_in_item <= 0,
+ "PAP-12125: no place for paste. pos_in_item=%d", pos_in_item);
+
+ /* Shift lnum[0] - 1 items in whole. Shift lbytes - 1 byte from item number lnum[0] */
+ leaf_shift_left(tb,tb->lnum[0],tb->lbytes);
+ }
+ }
+ }
+ else /* appended item will be in L[0] in whole */
+ {
+ struct item_head * pasted;
+
+ if ( ! item_pos && op_is_left_mergeable (B_N_PKEY (tbS0, 0), tbS0->b_size) )
+ { /* if we paste into first item of S[0] and it is left mergable */
+ /* then increment pos_in_item by the size of the last item in L[0] */
+ pasted = B_N_PITEM_HEAD(tb->L[0],n-1);
+ if ( is_direntry_le_ih (pasted) )
+ pos_in_item += ih_entry_count(pasted);
+ else
+ pos_in_item += ih_item_len(pasted);
+ }
+
+ /* Shift lnum[0] - 1 items in whole. Shift lbytes - 1 byte from item number lnum[0] */
+ ret_val = leaf_shift_left(tb,tb->lnum[0],tb->lbytes);
+ /* Append to body of item in L[0] */
+ bi.tb = tb;
+ bi.bi_bh = tb->L[0];
+ bi.bi_parent = tb->FL[0];
+ bi.bi_position = get_left_neighbor_position (tb, 0);
+ leaf_paste_in_buffer (&bi, n + item_pos - ret_val, pos_in_item, tb->insert_size[0],
+ body, zeros_num);
+
+ /* if appended item is directory, paste entry */
+ pasted = B_N_PITEM_HEAD (tb->L[0], n + item_pos - ret_val);
+ if (is_direntry_le_ih (pasted))
+ leaf_paste_entries (
+ bi.bi_bh, n + item_pos - ret_val, pos_in_item, 1,
+ (struct reiserfs_de_head *)body, body + DEH_SIZE, tb->insert_size[0]
+ );
+ /* if appended item is indirect item, put unformatted node into un list */
+ if (is_indirect_le_ih (pasted))
+ set_ih_free_space (pasted, 0);
+ tb->insert_size[0] = 0;
+ zeros_num = 0;
+ }
+ break;
+ default: /* cases d and t */
+ reiserfs_panic (tb->tb_sb, "PAP-12130: balance_leaf: lnum > 0: unexpectable mode: %s(%d)",
+ (flag == M_DELETE) ? "DELETE" : ((flag == M_CUT) ? "CUT" : "UNKNOWN"), flag);
+ }
+ } else {
+ /* new item doesn't fall into L[0] */
+ leaf_shift_left(tb,tb->lnum[0],tb->lbytes);
+ }
+ } /* tb->lnum[0] > 0 */
+
+ /* Calculate new item position */
+ item_pos -= ( tb->lnum[0] - (( tb->lbytes != -1 ) ? 1 : 0));
+
+ if ( tb->rnum[0] > 0 ) {
+ /* shift rnum[0] items from S[0] to the right neighbor R[0] */
+ n = B_NR_ITEMS(tbS0);
+ switch ( flag ) {
+
+ case M_INSERT: /* insert item */
+ if ( n - tb->rnum[0] < item_pos )
+ { /* new item or its part falls to R[0] */
+ if ( item_pos == n - tb->rnum[0] + 1 && tb->rbytes != -1 )
+ { /* part of new item falls into R[0] */
+ loff_t old_key_comp, old_len, r_zeros_number;
+ const char * r_body;
+ int version;
+ loff_t offset;
+
+ leaf_shift_right(tb,tb->rnum[0]-1,-1);
+
+ version = ih_version(ih);
+ /* Remember key component and item length */
+ old_key_comp = le_ih_k_offset( ih );
+ old_len = ih_item_len(ih);
+
+ /* Calculate key component and item length to insert into R[0] */
+ offset = le_ih_k_offset( ih ) + ((old_len - tb->rbytes )<<(is_indirect_le_ih(ih)?tb->tb_sb->s_blocksize_bits-UNFM_P_SHIFT:0));
+ set_le_ih_k_offset( ih, offset );
+ put_ih_item_len( ih, tb->rbytes);
+ /* Insert part of the item into R[0] */
+ bi.tb = tb;
+ bi.bi_bh = tb->R[0];
+ bi.bi_parent = tb->FR[0];
+ bi.bi_position = get_right_neighbor_position (tb, 0);
+ if ( (old_len - tb->rbytes) > zeros_num ) {
+ r_zeros_number = 0;
+ r_body = body + (old_len - tb->rbytes) - zeros_num;
+ }
+ else {
+ r_body = body;
+ r_zeros_number = zeros_num - (old_len - tb->rbytes);
+ zeros_num -= r_zeros_number;
+ }
+
+ leaf_insert_into_buf (&bi, 0, ih, r_body, r_zeros_number);
+
+ /* Replace right delimiting key by first key in R[0] */
+ replace_key(tb, tb->CFR[0],tb->rkey[0],tb->R[0],0);
+
+ /* Calculate key component and item length to insert into S[0] */
+ set_le_ih_k_offset( ih, old_key_comp );
+ put_ih_item_len( ih, old_len - tb->rbytes );
+
+ tb->insert_size[0] -= tb->rbytes;
+
+ }
+ else /* whole new item falls into R[0] */
+ {
+ /* Shift rnum[0]-1 items to R[0] */
+ ret_val = leaf_shift_right(tb,tb->rnum[0]-1,tb->rbytes);
+ /* Insert new item into R[0] */
+ bi.tb = tb;
+ bi.bi_bh = tb->R[0];
+ bi.bi_parent = tb->FR[0];
+ bi.bi_position = get_right_neighbor_position (tb, 0);
+ leaf_insert_into_buf (&bi, item_pos - n + tb->rnum[0] - 1, ih, body, zeros_num);
+
+ if ( item_pos - n + tb->rnum[0] - 1 == 0 ) {
+ replace_key(tb, tb->CFR[0],tb->rkey[0],tb->R[0],0);
+
+ }
+ zeros_num = tb->insert_size[0] = 0;
+ }
+ }
+ else /* new item or part of it doesn't fall into R[0] */
+ {
+ leaf_shift_right(tb,tb->rnum[0],tb->rbytes);
+ }
+ break;
+
+ case M_PASTE: /* append item */
+
+ if ( n - tb->rnum[0] <= item_pos ) /* pasted item or part of it falls to R[0] */
+ {
+ if ( item_pos == n - tb->rnum[0] && tb->rbytes != -1 )
+ { /* we must shift the part of the appended item */
+ if ( is_direntry_le_ih (B_N_PITEM_HEAD(tbS0, item_pos)))
+ { /* we append to directory item */
+ int entry_count;
+
+ RFALSE( zeros_num,
+ "PAP-12145: illegal parametr in case of a directory");
+ entry_count = I_ENTRY_COUNT(B_N_PITEM_HEAD(tbS0, item_pos));
+ if ( entry_count - tb->rbytes < pos_in_item )
+ /* new directory entry falls into R[0] */
+ {
+ int paste_entry_position;
+
+ RFALSE( tb->rbytes - 1 >= entry_count ||
+ ! tb->insert_size[0],
+ "PAP-12150: no enough of entries to shift to R[0]: rbytes=%d, entry_count=%d",
+ tb->rbytes, entry_count);
+ /* Shift rnum[0]-1 items in whole. Shift rbytes-1 directory entries from directory item number rnum[0] */
+ leaf_shift_right(tb,tb->rnum[0],tb->rbytes - 1);
+ /* Paste given directory entry to directory item */
+ paste_entry_position = pos_in_item - entry_count + tb->rbytes - 1;
+ bi.tb = tb;
+ bi.bi_bh = tb->R[0];
+ bi.bi_parent = tb->FR[0];
+ bi.bi_position = get_right_neighbor_position (tb, 0);
+ leaf_paste_in_buffer (&bi, 0, paste_entry_position,
+ tb->insert_size[0],body,zeros_num);
+ /* paste entry */
+ leaf_paste_entries (
+ bi.bi_bh, 0, paste_entry_position, 1, (struct reiserfs_de_head *)body,
+ body + DEH_SIZE, tb->insert_size[0]
+ );
+
+ if ( paste_entry_position == 0 ) {
+ /* change delimiting keys */
+ replace_key(tb, tb->CFR[0],tb->rkey[0],tb->R[0],0);
+ }
+
+ tb->insert_size[0] = 0;
+ pos_in_item++;
+ }
+ else /* new directory entry doesn't fall into R[0] */
+ {
+ leaf_shift_right(tb,tb->rnum[0],tb->rbytes);
+ }
+ }
+ else /* regular object */
+ {
+ int n_shift, n_rem, r_zeros_number;
+ const char * r_body;
+
+ /* Calculate number of bytes which must be shifted from appended item */
+ if ( (n_shift = tb->rbytes - tb->insert_size[0]) < 0 )
+ n_shift = 0;
+
+ RFALSE(pos_in_item != ih_item_len(B_N_PITEM_HEAD (tbS0, item_pos)),
+ "PAP-12155: invalid position to paste. ih_item_len=%d, pos_in_item=%d",
+ pos_in_item, ih_item_len( B_N_PITEM_HEAD(tbS0,item_pos)));
+
+ leaf_shift_right(tb,tb->rnum[0],n_shift);
+ /* Calculate number of bytes which must remain in body after appending to R[0] */
+ if ( (n_rem = tb->insert_size[0] - tb->rbytes) < 0 )
+ n_rem = 0;
+
+ {
+ int version;
+ unsigned long temp_rem = n_rem;
+
+ version = ih_version (B_N_PITEM_HEAD (tb->R[0],0));
+ if (is_indirect_le_key(version,B_N_PKEY(tb->R[0],0))){
+ temp_rem = n_rem << (tb->tb_sb->s_blocksize_bits -
+ UNFM_P_SHIFT);
+ }
+ set_le_key_k_offset (version, B_N_PKEY(tb->R[0],0),
+ le_key_k_offset (version, B_N_PKEY(tb->R[0],0)) + temp_rem);
+ set_le_key_k_offset (version, B_N_PDELIM_KEY(tb->CFR[0],tb->rkey[0]),
+ le_key_k_offset (version, B_N_PDELIM_KEY(tb->CFR[0],tb->rkey[0])) + temp_rem);
+ }
+/* k_offset (B_N_PKEY(tb->R[0],0)) += n_rem;
+ k_offset (B_N_PDELIM_KEY(tb->CFR[0],tb->rkey[0])) += n_rem;*/
+ do_balance_mark_internal_dirty (tb, tb->CFR[0], 0);
+
+ /* Append part of body into R[0] */
+ bi.tb = tb;
+ bi.bi_bh = tb->R[0];
+ bi.bi_parent = tb->FR[0];
+ bi.bi_position = get_right_neighbor_position (tb, 0);
+ if ( n_rem > zeros_num ) {
+ r_zeros_number = 0;
+ r_body = body + n_rem - zeros_num;
+ }
+ else {
+ r_body = body;
+ r_zeros_number = zeros_num - n_rem;
+ zeros_num -= r_zeros_number;
+ }
+
+ leaf_paste_in_buffer(&bi, 0, n_shift, tb->insert_size[0] - n_rem, r_body, r_zeros_number);
+
+ if (is_indirect_le_ih (B_N_PITEM_HEAD(tb->R[0],0))) {
+#if 0
+ RFALSE( n_rem,
+ "PAP-12160: paste more than one unformatted node pointer");
+#endif
+ set_ih_free_space (B_N_PITEM_HEAD(tb->R[0],0), 0);
+ }
+ tb->insert_size[0] = n_rem;
+ if ( ! n_rem )
+ pos_in_item ++;
+ }
+ }
+ else /* pasted item in whole falls into R[0] */
+ {
+ struct item_head * pasted;
+
+ ret_val = leaf_shift_right(tb,tb->rnum[0],tb->rbytes);
+ /* append item in R[0] */
+ if ( pos_in_item >= 0 ) {
+ bi.tb = tb;
+ bi.bi_bh = tb->R[0];
+ bi.bi_parent = tb->FR[0];
+ bi.bi_position = get_right_neighbor_position (tb, 0);
+ leaf_paste_in_buffer(&bi,item_pos - n + tb->rnum[0], pos_in_item,
+ tb->insert_size[0],body, zeros_num);
+ }
+
+ /* paste new entry, if item is directory item */
+ pasted = B_N_PITEM_HEAD(tb->R[0], item_pos - n + tb->rnum[0]);
+ if (is_direntry_le_ih (pasted) && pos_in_item >= 0 ) {
+ leaf_paste_entries (
+ bi.bi_bh, item_pos - n + tb->rnum[0], pos_in_item, 1,
+ (struct reiserfs_de_head *)body, body + DEH_SIZE, tb->insert_size[0]
+ );
+ if ( ! pos_in_item ) {
+
+ RFALSE( item_pos - n + tb->rnum[0],
+ "PAP-12165: directory item must be first item of node when pasting is in 0th position");
+
+ /* update delimiting keys */
+ replace_key(tb, tb->CFR[0],tb->rkey[0],tb->R[0],0);
+ }
+ }
+
+ if (is_indirect_le_ih (pasted))
+ set_ih_free_space (pasted, 0);
+ zeros_num = tb->insert_size[0] = 0;
+ }
+ }
+ else /* new item doesn't fall into R[0] */
+ {
+ leaf_shift_right(tb,tb->rnum[0],tb->rbytes);
+ }
+ break;
+ default: /* cases d and t */
+ reiserfs_panic (tb->tb_sb, "PAP-12175: balance_leaf: rnum > 0: unexpectable mode: %s(%d)",
+ (flag == M_DELETE) ? "DELETE" : ((flag == M_CUT) ? "CUT" : "UNKNOWN"), flag);
+ }
+
+ } /* tb->rnum[0] > 0 */
+
+
+ RFALSE( tb->blknum[0] > 3,
+ "PAP-12180: blknum can not be %d. It must be <= 3", tb->blknum[0]);
+ RFALSE( tb->blknum[0] < 0,
+ "PAP-12185: blknum can not be %d. It must be >= 0", tb->blknum[0]);
+
+ /* if while adding to a node we discover that it is possible to split
+ it in two, and merge the left part into the left neighbor and the
+ right part into the right neighbor, eliminating the node */
+ if ( tb->blknum[0] == 0 ) { /* node S[0] is empty now */
+
+ RFALSE( ! tb->lnum[0] || ! tb->rnum[0],
+ "PAP-12190: lnum and rnum must not be zero");
+ /* if insertion was done before 0-th position in R[0], right
+ delimiting key of the tb->L[0]'s and left delimiting key are
+ not set correctly */
+ if (tb->CFL[0]) {
+ if (!tb->CFR[0])
+ reiserfs_panic (tb->tb_sb, "vs-12195: balance_leaf: CFR not initialized");
+ copy_key (B_N_PDELIM_KEY (tb->CFL[0], tb->lkey[0]), B_N_PDELIM_KEY (tb->CFR[0], tb->rkey[0]));
+ do_balance_mark_internal_dirty (tb, tb->CFL[0], 0);
+ }
+
+ reiserfs_invalidate_buffer(tb,tbS0);
+ return 0;
+ }
+
+
+ /* Fill new nodes that appear in place of S[0] */
+
+ /* I am told that this copying is because we need an array to enable
+ the looping code. -Hans */
+ snum[0] = tb->s1num,
+ snum[1] = tb->s2num;
+ sbytes[0] = tb->s1bytes;
+ sbytes[1] = tb->s2bytes;
+ for( i = tb->blknum[0] - 2; i >= 0; i-- ) {
+
+ RFALSE( !snum[i], "PAP-12200: snum[%d] == %d. Must be > 0", i, snum[i]);
+
+ /* here we shift from S to S_new nodes */
+
+ S_new[i] = get_FEB(tb);
+
+ /* initialized block type and tree level */
+ set_blkh_level( B_BLK_HEAD(S_new[i]), DISK_LEAF_NODE_LEVEL );
+
+
+ n = B_NR_ITEMS(tbS0);
+
+ switch (flag) {
+ case M_INSERT: /* insert item */
+
+ if ( n - snum[i] < item_pos )
+ { /* new item or it's part falls to first new node S_new[i]*/
+ if ( item_pos == n - snum[i] + 1 && sbytes[i] != -1 )
+ { /* part of new item falls into S_new[i] */
+ int old_key_comp, old_len, r_zeros_number;
+ const char * r_body;
+ int version;
+
+ /* Move snum[i]-1 items from S[0] to S_new[i] */
+ leaf_move_items (LEAF_FROM_S_TO_SNEW, tb, snum[i] - 1, -1, S_new[i]);
+ /* Remember key component and item length */
+ version = ih_version (ih);
+ old_key_comp = le_ih_k_offset( ih );
+ old_len = ih_item_len(ih);
+
+ /* Calculate key component and item length to insert into S_new[i] */
+ set_le_ih_k_offset( ih,
+ le_ih_k_offset(ih) + ((old_len - sbytes[i] )<<(is_indirect_le_ih(ih)?tb->tb_sb->s_blocksize_bits-UNFM_P_SHIFT:0) ));
+
+ put_ih_item_len( ih, sbytes[i] );
+
+ /* Insert part of the item into S_new[i] before 0-th item */
+ bi.tb = tb;
+ bi.bi_bh = S_new[i];
+ bi.bi_parent = 0;
+ bi.bi_position = 0;
+
+ if ( (old_len - sbytes[i]) > zeros_num ) {
+ r_zeros_number = 0;
+ r_body = body + (old_len - sbytes[i]) - zeros_num;
+ }
+ else {
+ r_body = body;
+ r_zeros_number = zeros_num - (old_len - sbytes[i]);
+ zeros_num -= r_zeros_number;
+ }
+
+ leaf_insert_into_buf (&bi, 0, ih, r_body, r_zeros_number);
+
+ /* Calculate key component and item length to insert into S[i] */
+ set_le_ih_k_offset( ih, old_key_comp );
+ put_ih_item_len( ih, old_len - sbytes[i] );
+ tb->insert_size[0] -= sbytes[i];
+ }
+ else /* whole new item falls into S_new[i] */
+ {
+ /* Shift snum[0] - 1 items to S_new[i] (sbytes[i] of split item) */
+ leaf_move_items (LEAF_FROM_S_TO_SNEW, tb, snum[i] - 1, sbytes[i], S_new[i]);
+
+ /* Insert new item into S_new[i] */
+ bi.tb = tb;
+ bi.bi_bh = S_new[i];
+ bi.bi_parent = 0;
+ bi.bi_position = 0;
+ leaf_insert_into_buf (&bi, item_pos - n + snum[i] - 1, ih, body, zeros_num);
+
+ zeros_num = tb->insert_size[0] = 0;
+ }
+ }
+
+ else /* new item or it part don't falls into S_new[i] */
+ {
+ leaf_move_items (LEAF_FROM_S_TO_SNEW, tb, snum[i], sbytes[i], S_new[i]);
+ }
+ break;
+
+ case M_PASTE: /* append item */
+
+ if ( n - snum[i] <= item_pos ) /* pasted item or part if it falls to S_new[i] */
+ {
+ if ( item_pos == n - snum[i] && sbytes[i] != -1 )
+ { /* we must shift part of the appended item */
+ struct item_head * aux_ih;
+
+ RFALSE( ih, "PAP-12210: ih must be 0");
+
+ if ( is_direntry_le_ih (aux_ih = B_N_PITEM_HEAD(tbS0,item_pos))) {
+ /* we append to directory item */
+
+ int entry_count;
+
+ entry_count = ih_entry_count(aux_ih);
+
+ if ( entry_count - sbytes[i] < pos_in_item && pos_in_item <= entry_count ) {
+ /* new directory entry falls into S_new[i] */
+
+ RFALSE( ! tb->insert_size[0],
+ "PAP-12215: insert_size is already 0");
+ RFALSE( sbytes[i] - 1 >= entry_count,
+ "PAP-12220: there are no so much entries (%d), only %d",
+ sbytes[i] - 1, entry_count);
+
+ /* Shift snum[i]-1 items in whole. Shift sbytes[i] directory entries from directory item number snum[i] */
+ leaf_move_items (LEAF_FROM_S_TO_SNEW, tb, snum[i], sbytes[i]-1, S_new[i]);
+ /* Paste given directory entry to directory item */
+ bi.tb = tb;
+ bi.bi_bh = S_new[i];
+ bi.bi_parent = 0;
+ bi.bi_position = 0;
+ leaf_paste_in_buffer (&bi, 0, pos_in_item - entry_count + sbytes[i] - 1,
+ tb->insert_size[0], body,zeros_num);
+ /* paste new directory entry */
+ leaf_paste_entries (
+ bi.bi_bh, 0, pos_in_item - entry_count + sbytes[i] - 1,
+ 1, (struct reiserfs_de_head *)body, body + DEH_SIZE,
+ tb->insert_size[0]
+ );
+ tb->insert_size[0] = 0;
+ pos_in_item++;
+ } else { /* new directory entry doesn't fall into S_new[i] */
+ leaf_move_items (LEAF_FROM_S_TO_SNEW, tb, snum[i], sbytes[i], S_new[i]);
+ }
+ }
+ else /* regular object */
+ {
+ int n_shift, n_rem, r_zeros_number;
+ const char * r_body;
+
+ RFALSE( pos_in_item != ih_item_len(B_N_PITEM_HEAD(tbS0,item_pos)) ||
+ tb->insert_size[0] <= 0,
+ "PAP-12225: item too short or insert_size <= 0");
+
+ /* Calculate number of bytes which must be shifted from appended item */
+ n_shift = sbytes[i] - tb->insert_size[0];
+ if ( n_shift < 0 )
+ n_shift = 0;
+ leaf_move_items (LEAF_FROM_S_TO_SNEW, tb, snum[i], n_shift, S_new[i]);
+
+ /* Calculate number of bytes which must remain in body after append to S_new[i] */
+ n_rem = tb->insert_size[0] - sbytes[i];
+ if ( n_rem < 0 )
+ n_rem = 0;
+ /* Append part of body into S_new[0] */
+ bi.tb = tb;
+ bi.bi_bh = S_new[i];
+ bi.bi_parent = 0;
+ bi.bi_position = 0;
+
+ if ( n_rem > zeros_num ) {
+ r_zeros_number = 0;
+ r_body = body + n_rem - zeros_num;
+ }
+ else {
+ r_body = body;
+ r_zeros_number = zeros_num - n_rem;
+ zeros_num -= r_zeros_number;
+ }
+
+ leaf_paste_in_buffer(&bi, 0, n_shift, tb->insert_size[0]-n_rem, r_body,r_zeros_number);
+ {
+ struct item_head * tmp;
+
+ tmp = B_N_PITEM_HEAD(S_new[i],0);
+ if (is_indirect_le_ih (tmp)) {
+ set_ih_free_space (tmp, 0);
+ set_le_ih_k_offset( tmp, le_ih_k_offset(tmp) +
+ (n_rem << (tb->tb_sb->s_blocksize_bits -
+ UNFM_P_SHIFT)));
+ } else {
+ set_le_ih_k_offset( tmp, le_ih_k_offset(tmp) +
+ n_rem );
+ }
+ }
+
+ tb->insert_size[0] = n_rem;
+ if ( ! n_rem )
+ pos_in_item++;
+ }
+ }
+ else
+ /* item falls wholly into S_new[i] */
+ {
+ int ret_val;
+ struct item_head * pasted;
+
+#ifdef CONFIG_REISERFS_CHECK
+ struct item_head * ih = B_N_PITEM_HEAD(tbS0,item_pos);
+
+ if ( ! is_direntry_le_ih(ih) && (pos_in_item != ih_item_len(ih) ||
+ tb->insert_size[0] <= 0) )
+ reiserfs_panic (tb->tb_sb, "PAP-12235: balance_leaf: pos_in_item must be equal to ih_item_len");
+#endif /* CONFIG_REISERFS_CHECK */
+
+ ret_val = leaf_move_items (LEAF_FROM_S_TO_SNEW, tb, snum[i], sbytes[i], S_new[i]);
+
+ RFALSE( ret_val,
+ "PAP-12240: unexpected value returned by leaf_move_items (%d)",
+ ret_val);
+
+ /* paste into item */
+ bi.tb = tb;
+ bi.bi_bh = S_new[i];
+ bi.bi_parent = 0;
+ bi.bi_position = 0;
+ leaf_paste_in_buffer(&bi, item_pos - n + snum[i], pos_in_item, tb->insert_size[0], body, zeros_num);
+
+ pasted = B_N_PITEM_HEAD(S_new[i], item_pos - n + snum[i]);
+ if (is_direntry_le_ih (pasted))
+ {
+ leaf_paste_entries (
+ bi.bi_bh, item_pos - n + snum[i], pos_in_item, 1,
+ (struct reiserfs_de_head *)body, body + DEH_SIZE, tb->insert_size[0]
+ );
+ }
+
+ /* if we paste to indirect item update ih_free_space */
+ if (is_indirect_le_ih (pasted))
+ set_ih_free_space (pasted, 0);
+ zeros_num = tb->insert_size[0] = 0;
+ }
+ }
+
+ else /* pasted item doesn't fall into S_new[i] */
+ {
+ leaf_move_items (LEAF_FROM_S_TO_SNEW, tb, snum[i], sbytes[i], S_new[i]);
+ }
+ break;
+ default: /* cases d and t */
+ reiserfs_panic (tb->tb_sb, "PAP-12245: balance_leaf: blknum > 2: unexpectable mode: %s(%d)",
+ (flag == M_DELETE) ? "DELETE" : ((flag == M_CUT) ? "CUT" : "UNKNOWN"), flag);
+ }
+
+ memcpy (insert_key + i,B_N_PKEY(S_new[i],0),KEY_SIZE);
+ insert_ptr[i] = S_new[i];
+
+ RFALSE( (atomic_read (&(S_new[i]->b_count)) != 1) &&
+ (atomic_read(&(S_new[i]->b_count)) != 2 ||
+ !(buffer_journaled(S_new[i]) ||
+ buffer_journal_dirty(S_new[i]))),
+ "PAP-12247: S_new[%d] : (%b)\n", i, S_new[i]);
+
+
+ }
+
+ /* if the affected item was not wholly shifted then we perform all necessary operations on that part or whole of the
+ affected item which remains in S */
+ if ( 0 <= item_pos && item_pos < tb->s0num )
+ { /* if we must insert or append into buffer S[0] */
+
+ switch (flag)
+ {
+ case M_INSERT: /* insert item into S[0] */
+ bi.tb = tb;
+ bi.bi_bh = tbS0;
+ bi.bi_parent = PATH_H_PPARENT (tb->tb_path, 0);
+ bi.bi_position = PATH_H_POSITION (tb->tb_path, 1);
+ leaf_insert_into_buf (&bi, item_pos, ih, body, zeros_num);
+
+ /* If we insert the first key change the delimiting key */
+ if( item_pos == 0 ) {
+ if (tb->CFL[0]) /* can be 0 in reiserfsck */
+ replace_key(tb, tb->CFL[0], tb->lkey[0],tbS0,0);
+
+ }
+ break;
+
+ case M_PASTE: { /* append item in S[0] */
+ struct item_head * pasted;
+
+ pasted = B_N_PITEM_HEAD (tbS0, item_pos);
+ /* when directory, may be new entry already pasted */
+ if (is_direntry_le_ih (pasted)) {
+ if ( pos_in_item >= 0 &&
+ pos_in_item <= ih_entry_count(pasted) ) {
+
+ RFALSE( ! tb->insert_size[0],
+ "PAP-12260: insert_size is 0 already");
+
+ /* prepare space */
+ bi.tb = tb;
+ bi.bi_bh = tbS0;
+ bi.bi_parent = PATH_H_PPARENT (tb->tb_path, 0);
+ bi.bi_position = PATH_H_POSITION (tb->tb_path, 1);
+ leaf_paste_in_buffer(&bi, item_pos, pos_in_item, tb->insert_size[0], body, zeros_num);
+
+ /* paste entry */
+ leaf_paste_entries (
+ bi.bi_bh, item_pos, pos_in_item, 1, (struct reiserfs_de_head *)body,
+ body + DEH_SIZE, tb->insert_size[0]
+ );
+ if ( ! item_pos && ! pos_in_item ) {
+ RFALSE( !tb->CFL[0] || !tb->L[0],
+ "PAP-12270: CFL[0]/L[0] must be specified");
+ if (tb->CFL[0]) {
+ replace_key(tb, tb->CFL[0], tb->lkey[0],tbS0,0);
+
+ }
+ }
+ tb->insert_size[0] = 0;
+ }
+ } else { /* regular object */
+ if ( pos_in_item == ih_item_len(pasted) ) {
+
+ RFALSE( tb->insert_size[0] <= 0,
+ "PAP-12275: insert size must not be %d",
+ tb->insert_size[0]);
+ bi.tb = tb;
+ bi.bi_bh = tbS0;
+ bi.bi_parent = PATH_H_PPARENT (tb->tb_path, 0);
+ bi.bi_position = PATH_H_POSITION (tb->tb_path, 1);
+ leaf_paste_in_buffer (&bi, item_pos, pos_in_item, tb->insert_size[0], body, zeros_num);
+
+ if (is_indirect_le_ih (pasted)) {
+#if 0
+ RFALSE( tb->insert_size[0] != UNFM_P_SIZE,
+ "PAP-12280: insert_size for indirect item must be %d, not %d",
+ UNFM_P_SIZE, tb->insert_size[0]);
+#endif
+ set_ih_free_space (pasted, 0);
+ }
+ tb->insert_size[0] = 0;
+ }
+
+#ifdef CONFIG_REISERFS_CHECK
+ else {
+ if ( tb->insert_size[0] ) {
+ print_cur_tb ("12285");
+ reiserfs_panic (tb->tb_sb, "PAP-12285: balance_leaf: insert_size must be 0 (%d)", tb->insert_size[0]);
+ }
+ }
+#endif /* CONFIG_REISERFS_CHECK */
+
+ }
+ } /* case M_PASTE: */
+ }
+ }
+
+#ifdef CONFIG_REISERFS_CHECK
+ if ( flag == M_PASTE && tb->insert_size[0] ) {
+ print_cur_tb ("12290");
+ reiserfs_panic (tb->tb_sb, "PAP-12290: balance_leaf: insert_size is still not 0 (%d)", tb->insert_size[0]);
+ }
+#endif /* CONFIG_REISERFS_CHECK */
+
+ return 0;
+} /* Leaf level of the tree is balanced (end of balance_leaf) */
+
+
+
+/* Make empty node */
+void make_empty_node (struct buffer_info * bi)
+{
+ struct block_head * blkh;
+
+ RFALSE( bi->bi_bh == NULL, "PAP-12295: pointer to the buffer is NULL");
+
+ blkh = B_BLK_HEAD(bi->bi_bh);
+ set_blkh_nr_item( blkh, 0 );
+ set_blkh_free_space( blkh, MAX_CHILD_SIZE(bi->bi_bh) );
+
+ if (bi->bi_parent)
+ B_N_CHILD (bi->bi_parent, bi->bi_position)->dc_size = 0; /* Endian safe if 0 */
+}
+
+
+/* Get first empty buffer */
+struct buffer_head * get_FEB (struct tree_balance * tb)
+{
+ int i;
+ struct buffer_head * first_b;
+ struct buffer_info bi;
+
+ for (i = 0; i < MAX_FEB_SIZE; i ++)
+ if (tb->FEB[i] != 0)
+ break;
+
+ if (i == MAX_FEB_SIZE)
+ reiserfs_panic(tb->tb_sb, "vs-12300: get_FEB: FEB list is empty");
+
+ bi.tb = tb;
+ bi.bi_bh = first_b = tb->FEB[i];
+ bi.bi_parent = 0;
+ bi.bi_position = 0;
+ make_empty_node (&bi);
+ set_bit(BH_Uptodate, &first_b->b_state);
+ tb->FEB[i] = 0;
+ tb->used[i] = first_b;
+
+ return(first_b);
+}
+
+
+/* This is now used because reiserfs_free_block has to be able to
+** schedule.
+*/
+static void store_thrown (struct tree_balance * tb, struct buffer_head * bh)
+{
+ int i;
+
+ if (buffer_dirty (bh))
+ reiserfs_warning (tb->tb_sb, "store_thrown deals with dirty buffer\n");
+ for (i = 0; i < sizeof (tb->thrown)/sizeof (tb->thrown[0]); i ++)
+ if (!tb->thrown[i]) {
+ tb->thrown[i] = bh;
+ get_bh(bh) ; /* free_thrown puts this */
+ return;
+ }
+ reiserfs_warning (tb->tb_sb, "store_thrown: too many thrown buffers\n");
+}
+
+static void free_thrown(struct tree_balance *tb) {
+ int i ;
+ unsigned long blocknr ;
+ for (i = 0; i < sizeof (tb->thrown)/sizeof (tb->thrown[0]); i++) {
+ if (tb->thrown[i]) {
+ blocknr = tb->thrown[i]->b_blocknr ;
+ if (buffer_dirty (tb->thrown[i]))
+ reiserfs_warning (tb->tb_sb, "free_thrown deals with dirty buffer %ld\n", blocknr);
+ brelse(tb->thrown[i]) ; /* incremented in store_thrown */
+ reiserfs_free_block (tb->transaction_handle, blocknr);
+ }
+ }
+}
+
+void reiserfs_invalidate_buffer (struct tree_balance * tb, struct buffer_head * bh)
+{
+ struct block_head *blkh;
+ blkh = B_BLK_HEAD(bh);
+ set_blkh_level( blkh, FREE_LEVEL );
+ set_blkh_nr_item( blkh, 0 );
+
+ mark_buffer_clean (bh);
+ /* reiserfs_free_block is no longer schedule safe
+ reiserfs_free_block (tb->transaction_handle, tb->tb_sb, bh->b_blocknr);
+ */
+
+ store_thrown (tb, bh);
+}
+
+/* Replace n_dest'th key in buffer dest by n_src'th key of buffer src.*/
+void replace_key (struct tree_balance * tb, struct buffer_head * dest, int n_dest,
+ struct buffer_head * src, int n_src)
+{
+
+ RFALSE( dest == NULL || src == NULL,
+ "vs-12305: source or destination buffer is 0 (src=%p, dest=%p)",
+ src, dest);
+ RFALSE( ! B_IS_KEYS_LEVEL (dest),
+ "vs-12310: invalid level (%z) for destination buffer. dest must be leaf",
+ dest);
+ RFALSE( n_dest < 0 || n_src < 0,
+ "vs-12315: src(%d) or dest(%d) key number < 0", n_src, n_dest);
+ RFALSE( n_dest >= B_NR_ITEMS(dest) || n_src >= B_NR_ITEMS(src),
+ "vs-12320: src(%d(%d)) or dest(%d(%d)) key number is too big",
+ n_src, B_NR_ITEMS(src), n_dest, B_NR_ITEMS(dest));
+
+ if (B_IS_ITEMS_LEVEL (src))
+ /* source buffer contains leaf node */
+ memcpy (B_N_PDELIM_KEY(dest,n_dest), B_N_PITEM_HEAD(src,n_src), KEY_SIZE);
+ else
+ memcpy (B_N_PDELIM_KEY(dest,n_dest), B_N_PDELIM_KEY(src,n_src), KEY_SIZE);
+
+ do_balance_mark_internal_dirty (tb, dest, 0);
+}
+
+
+int get_left_neighbor_position (
+ struct tree_balance * tb,
+ int h
+ )
+{
+ int Sh_position = PATH_H_POSITION (tb->tb_path, h + 1);
+
+ RFALSE( PATH_H_PPARENT (tb->tb_path, h) == 0 || tb->FL[h] == 0,
+ "vs-12325: FL[%d](%p) or F[%d](%p) does not exist",
+ h, tb->FL[h], h, PATH_H_PPARENT (tb->tb_path, h));
+
+ if (Sh_position == 0)
+ return B_NR_ITEMS (tb->FL[h]);
+ else
+ return Sh_position - 1;
+}
+
+
+int get_right_neighbor_position (struct tree_balance * tb, int h)
+{
+ int Sh_position = PATH_H_POSITION (tb->tb_path, h + 1);
+
+ RFALSE( PATH_H_PPARENT (tb->tb_path, h) == 0 || tb->FR[h] == 0,
+ "vs-12330: F[%d](%p) or FR[%d](%p) does not exist",
+ h, PATH_H_PPARENT (tb->tb_path, h), h, tb->FR[h]);
+
+ if (Sh_position == B_NR_ITEMS (PATH_H_PPARENT (tb->tb_path, h)))
+ return 0;
+ else
+ return Sh_position + 1;
+}
+
+
+#ifdef CONFIG_REISERFS_CHECK
+
+int is_reusable (struct super_block * s, unsigned long block, int bit_value);
+static void check_internal_node (struct super_block * s, struct buffer_head * bh, char * mes)
+{
+ struct disk_child * dc;
+ int i;
+
+ RFALSE( !bh, "PAP-12336: bh == 0");
+
+ if (!bh || !B_IS_IN_TREE (bh))
+ return;
+
+ RFALSE( !buffer_dirty (bh) &&
+ !(buffer_journaled(bh) || buffer_journal_dirty(bh)),
+ "PAP-12337: buffer (%b) must be dirty", bh);
+ dc = B_N_CHILD (bh, 0);
+
+ for (i = 0; i <= B_NR_ITEMS (bh); i ++, dc ++) {
+ if (!is_reusable (s, dc_block_number(dc), 1) ) {
+ print_cur_tb (mes);
+ reiserfs_panic (s, "PAP-12338: check_internal_node: invalid child pointer %y in %b", dc, bh);
+ }
+ }
+}
+
+
+static int locked_or_not_in_tree (struct buffer_head * bh, char * which)
+{
+ if ( buffer_locked (bh) || !B_IS_IN_TREE (bh) ) {
+ reiserfs_warning (NULL, "vs-12339: locked_or_not_in_tree: %s (%b)\n", which, bh);
+ return 1;
+ }
+ return 0;
+}
+
+
+static int check_before_balancing (struct tree_balance * tb)
+{
+ int retval = 0;
+
+ if ( cur_tb ) {
+ reiserfs_panic (tb->tb_sb, "vs-12335: check_before_balancing: "
+ "suspect that schedule occurred based on cur_tb not being null at this point in code. "
+ "do_balance cannot properly handle schedule occuring while it runs.");
+ }
+
+ /* double check that buffers that we will modify are unlocked. (fix_nodes should already have
+ prepped all of these for us). */
+ if ( tb->lnum[0] ) {
+ retval |= locked_or_not_in_tree (tb->L[0], "L[0]");
+ retval |= locked_or_not_in_tree (tb->FL[0], "FL[0]");
+ retval |= locked_or_not_in_tree (tb->CFL[0], "CFL[0]");
+ check_leaf (tb->L[0]);
+ }
+ if ( tb->rnum[0] ) {
+ retval |= locked_or_not_in_tree (tb->R[0], "R[0]");
+ retval |= locked_or_not_in_tree (tb->FR[0], "FR[0]");
+ retval |= locked_or_not_in_tree (tb->CFR[0], "CFR[0]");
+ check_leaf (tb->R[0]);
+ }
+ retval |= locked_or_not_in_tree (PATH_PLAST_BUFFER (tb->tb_path), "S[0]");
+ check_leaf (PATH_PLAST_BUFFER (tb->tb_path));
+
+ return retval;
+}
+
+
+void check_after_balance_leaf (struct tree_balance * tb)
+{
+ if (tb->lnum[0]) {
+ if (B_FREE_SPACE (tb->L[0]) !=
+ MAX_CHILD_SIZE (tb->L[0]) - dc_size(B_N_CHILD (tb->FL[0], get_left_neighbor_position (tb, 0)))) {
+ print_cur_tb ("12221");
+ reiserfs_panic (tb->tb_sb, "PAP-12355: check_after_balance_leaf: shift to left was incorrect");
+ }
+ }
+ if (tb->rnum[0]) {
+ if (B_FREE_SPACE (tb->R[0]) !=
+ MAX_CHILD_SIZE (tb->R[0]) - dc_size(B_N_CHILD (tb->FR[0], get_right_neighbor_position (tb, 0)))) {
+ print_cur_tb ("12222");
+ reiserfs_panic (tb->tb_sb, "PAP-12360: check_after_balance_leaf: shift to right was incorrect");
+ }
+ }
+ if (PATH_H_PBUFFER(tb->tb_path,1) &&
+ (B_FREE_SPACE (PATH_H_PBUFFER(tb->tb_path,0)) !=
+ (MAX_CHILD_SIZE (PATH_H_PBUFFER(tb->tb_path,0)) -
+ dc_size(B_N_CHILD (PATH_H_PBUFFER(tb->tb_path,1),
+ PATH_H_POSITION (tb->tb_path, 1)))) )) {
+ int left = B_FREE_SPACE (PATH_H_PBUFFER(tb->tb_path,0));
+ int right = (MAX_CHILD_SIZE (PATH_H_PBUFFER(tb->tb_path,0)) -
+ dc_size(B_N_CHILD (PATH_H_PBUFFER(tb->tb_path,1),
+ PATH_H_POSITION (tb->tb_path, 1))));
+ print_cur_tb ("12223");
+ reiserfs_warning( tb->tb_sb,
+ "B_FREE_SPACE (PATH_H_PBUFFER(tb->tb_path,0)) = %d; "
+ "MAX_CHILD_SIZE (%d) - dc_size( %y, %d ) [%d] = %d\n",
+ left,
+ MAX_CHILD_SIZE (PATH_H_PBUFFER(tb->tb_path,0)),
+ PATH_H_PBUFFER(tb->tb_path,1),
+ PATH_H_POSITION (tb->tb_path, 1),
+ dc_size(B_N_CHILD (PATH_H_PBUFFER(tb->tb_path,1), PATH_H_POSITION (tb->tb_path, 1 )) ),
+ right );
+ reiserfs_panic (tb->tb_sb, "PAP-12365: check_after_balance_leaf: S is incorrect");
+ }
+}
+
+
+void check_leaf_level (struct tree_balance * tb)
+{
+ check_leaf (tb->L[0]);
+ check_leaf (tb->R[0]);
+ check_leaf (PATH_PLAST_BUFFER (tb->tb_path));
+}
+
+void check_internal_levels (struct tree_balance * tb)
+{
+ int h;
+
+ /* check all internal nodes */
+ for (h = 1; tb->insert_size[h]; h ++) {
+ check_internal_node (tb->tb_sb, PATH_H_PBUFFER (tb->tb_path, h), "BAD BUFFER ON PATH");
+ if (tb->lnum[h])
+ check_internal_node (tb->tb_sb, tb->L[h], "BAD L");
+ if (tb->rnum[h])
+ check_internal_node (tb->tb_sb, tb->R[h], "BAD R");
+ }
+
+}
+
+#endif
+
+
+
+
+
+
+/* Now we have all of the buffers that must be used in balancing of
+ the tree. We rely on the assumption that schedule() will not occur
+ while do_balance works. ( Only interrupt handlers are acceptable.)
+ We balance the tree according to the analysis made before this,
+ using buffers already obtained. For SMP support it will someday be
+ necessary to add ordered locking of tb. */
+
+/* Some interesting rules of balancing:
+
+ we delete a maximum of two nodes per level per balancing: we never
+ delete R, when we delete two of three nodes L, S, R then we move
+ them into R.
+
+ we only delete L if we are deleting two nodes, if we delete only
+ one node we delete S
+
+ if we shift leaves then we shift as much as we can: this is a
+ deliberate policy of extremism in node packing which results in
+ higher average utilization after repeated random balance operations
+ at the cost of more memory copies and more balancing as a result of
+ small insertions to full nodes.
+
+ if we shift internal nodes we try to evenly balance the node
+ utilization, with consequent less balancing at the cost of lower
+ utilization.
+
+ one could argue that the policy for directories in leaves should be
+ that of internal nodes, but we will wait until another day to
+ evaluate this.... It would be nice to someday measure and prove
+ these assumptions as to what is optimal....
+
+*/
+
+static inline void do_balance_starts (struct tree_balance *tb)
+{
+ /* use print_cur_tb() to see initial state of struct
+ tree_balance */
+
+ /* store_print_tb (tb); */
+
+ /* do not delete, just comment it out */
+/* print_tb(flag, PATH_LAST_POSITION(tb->tb_path), tb->tb_path->pos_in_item, tb,
+ "check");*/
+ RFALSE( check_before_balancing (tb), "PAP-12340: locked buffers in TB");
+#ifdef CONFIG_REISERFS_CHECK
+ cur_tb = tb;
+#endif
+}
+
+
+static inline void do_balance_completed (struct tree_balance * tb)
+{
+
+#ifdef CONFIG_REISERFS_CHECK
+ check_leaf_level (tb);
+ check_internal_levels (tb);
+ cur_tb = NULL;
+#endif
+
+ /* reiserfs_free_block is no longer schedule safe. So, we need to
+ ** put the buffers we want freed on the thrown list during do_balance,
+ ** and then free them now
+ */
+
+ tb->tb_sb->u.reiserfs_sb.s_do_balance ++;
+
+
+ /* release all nodes hold to perform the balancing */
+ unfix_nodes(tb);
+
+ free_thrown(tb) ;
+}
+
+
+
+
+
+void do_balance (struct tree_balance * tb, /* tree_balance structure */
+ struct item_head * ih, /* item header of inserted item */
+ const char * body, /* body of inserted item or bytes to paste */
+ int flag) /* i - insert, d - delete
+ c - cut, p - paste
+
+ Cut means delete part of an item
+ (includes removing an entry from a
+ directory).
+
+ Delete means delete whole item.
+
+ Insert means add a new item into the
+ tree.
+
+ Paste means to append to the end of an
+ existing file or to insert a directory
+ entry. */
+{
+ int child_pos, /* position of a child node in its parent */
+ h; /* level of the tree being processed */
+ struct item_head insert_key[2]; /* in our processing of one level
+ we sometimes determine what
+ must be inserted into the next
+ higher level. This insertion
+ consists of a key or two keys
+ and their corresponding
+ pointers */
+ struct buffer_head *insert_ptr[2]; /* inserted node-ptrs for the next
+ level */
+
+ tb->tb_mode = flag;
+ tb->need_balance_dirty = 0;
+
+ if (FILESYSTEM_CHANGED_TB(tb)) {
+ reiserfs_panic(tb->tb_sb, "clm-6000: do_balance, fs generation has changed\n") ;
+ }
+ /* if we have no real work to do */
+ if ( ! tb->insert_size[0] ) {
+ reiserfs_warning (tb->tb_sb, "PAP-12350: do_balance: insert_size == 0, mode == %c\n ",
+ flag);
+ unfix_nodes(tb);
+ return;
+ }
+
+ atomic_inc (&(fs_generation (tb->tb_sb)));
+ do_balance_starts (tb);
+
+ /* balance leaf returns 0 except if combining L R and S into
+ one node. see balance_internal() for explanation of this
+ line of code.*/
+ child_pos = PATH_H_B_ITEM_ORDER (tb->tb_path, 0) +
+ balance_leaf (tb, ih, body, flag, insert_key, insert_ptr);
+
+#ifdef CONFIG_REISERFS_CHECK
+ check_after_balance_leaf (tb);
+#endif
+
+ /* Balance internal level of the tree. */
+ for ( h = 1; h < MAX_HEIGHT && tb->insert_size[h]; h++ )
+ child_pos = balance_internal (tb, h, child_pos, insert_key, insert_ptr);
+
+
+ do_balance_completed (tb);
+
+}
diff --git a/fs/reiserfs/file.c b/fs/reiserfs/file.c
new file mode 100644
index 00000000000000..e6f35cd2d1a4f6
--- /dev/null
+++ b/fs/reiserfs/file.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2000-2002 by Hans Reiser, licensing governed by reiserfs/README
+ */
+
+
+#include <linux/sched.h>
+#include <linux/reiserfs_fs.h>
+#include <linux/smp_lock.h>
+
+/*
+** We pack the tails of files on file close, not at the time they are written.
+** This implies an unnecessary copy of the tail and an unnecessary indirect item
+** insertion/balancing, for files that are written in one write.
+** It avoids unnecessary tail packings (balances) for files that are written in
+** multiple writes and are small enough to have tails.
+**
+** file_release is called by the VFS layer when the file is closed. If
+** this is the last open file descriptor, and the file
+** small enough to have a tail, and the tail is currently in an
+** unformatted node, the tail is converted back into a direct item.
+**
+** We use reiserfs_truncate_file to pack the tail, since it already has
+** all the conditions coded.
+*/
+static int reiserfs_file_release (struct inode * inode, struct file * filp)
+{
+
+ struct reiserfs_transaction_handle th ;
+ int windex ;
+
+ if (!S_ISREG (inode->i_mode))
+ BUG ();
+
+ /* fast out for when nothing needs to be done */
+ if ((atomic_read(&inode->i_count) > 1 ||
+ !(inode->u.reiserfs_i.i_flags & i_pack_on_close_mask) ||
+ !tail_has_to_be_packed(inode)) &&
+ inode->u.reiserfs_i.i_prealloc_count <= 0) {
+ return 0;
+ }
+
+ lock_kernel() ;
+ down (&inode->i_sem);
+ journal_begin(&th, inode->i_sb, JOURNAL_PER_BALANCE_CNT * 3) ;
+ reiserfs_update_inode_transaction(inode) ;
+
+#ifdef REISERFS_PREALLOCATE
+ reiserfs_discard_prealloc (&th, inode);
+#endif
+ journal_end(&th, inode->i_sb, JOURNAL_PER_BALANCE_CNT * 3) ;
+
+ if (atomic_read(&inode->i_count) <= 1 &&
+ (inode->u.reiserfs_i.i_flags & i_pack_on_close_mask) &&
+ tail_has_to_be_packed (inode)) {
+ /* if regular file is released by last holder and it has been
+ appended (we append by unformatted node only) or its direct
+ item(s) had to be converted, then it may have to be
+ indirect2direct converted */
+ windex = push_journal_writer("file_release") ;
+ reiserfs_truncate_file(inode, 0) ;
+ pop_journal_writer(windex) ;
+ }
+ up (&inode->i_sem);
+ unlock_kernel() ;
+ return 0;
+}
+
+static void reiserfs_vfs_truncate_file(struct inode *inode) {
+ reiserfs_truncate_file(inode, 1) ;
+}
+
+/* Sync a reiserfs file. */
+static int reiserfs_sync_file(
+ struct file * p_s_filp,
+ struct dentry * p_s_dentry,
+ int datasync
+ ) {
+ struct inode * p_s_inode = p_s_dentry->d_inode;
+ int n_err;
+
+ lock_kernel() ;
+
+ if (!S_ISREG(p_s_inode->i_mode))
+ BUG ();
+
+ n_err = fsync_inode_buffers(p_s_inode) ;
+ n_err |= fsync_inode_data_buffers(p_s_inode);
+ reiserfs_commit_for_inode(p_s_inode) ;
+ unlock_kernel() ;
+ return ( n_err < 0 ) ? -EIO : 0;
+}
+
+static int reiserfs_setattr(struct dentry *dentry, struct iattr *attr) {
+ struct inode *inode = dentry->d_inode ;
+ int error ;
+ if (attr->ia_valid & ATTR_SIZE) {
+ /* version 2 items will be caught by the s_maxbytes check
+ ** done for us in vmtruncate
+ */
+ if (get_inode_item_key_version(inode) == KEY_FORMAT_3_5 &&
+ attr->ia_size > MAX_NON_LFS)
+ return -EFBIG ;
+
+ /* fill in hole pointers in the expanding truncate case. */
+ if (attr->ia_size > inode->i_size) {
+ error = generic_cont_expand(inode, attr->ia_size) ;
+ if (inode->u.reiserfs_i.i_prealloc_count > 0) {
+ struct reiserfs_transaction_handle th ;
+ /* we're changing at most 2 bitmaps, inode + super */
+ journal_begin(&th, inode->i_sb, 4) ;
+ reiserfs_discard_prealloc (&th, inode);
+ journal_end(&th, inode->i_sb, 4) ;
+ }
+ if (error)
+ return error ;
+ }
+ }
+
+ if ((((attr->ia_valid & ATTR_UID) && (attr->ia_uid & ~0xffff)) ||
+ ((attr->ia_valid & ATTR_GID) && (attr->ia_gid & ~0xffff))) &&
+ (get_inode_sd_version (inode) == STAT_DATA_V1))
+ /* stat data of format v3.5 has 16 bit uid and gid */
+ return -EINVAL;
+
+ error = inode_change_ok(inode, attr) ;
+ if (!error)
+ inode_setattr(inode, attr) ;
+
+ return error ;
+}
+
+struct file_operations reiserfs_file_operations = {
+ read: generic_file_read,
+ write: generic_file_write,
+ ioctl: reiserfs_ioctl,
+ mmap: generic_file_mmap,
+ release: reiserfs_file_release,
+ fsync: reiserfs_sync_file,
+};
+
+
+struct inode_operations reiserfs_file_inode_operations = {
+ truncate: reiserfs_vfs_truncate_file,
+ setattr: reiserfs_setattr,
+};
+
+
diff --git a/fs/reiserfs/fix_node.c b/fs/reiserfs/fix_node.c
new file mode 100644
index 00000000000000..05586f26ab7efc
--- /dev/null
+++ b/fs/reiserfs/fix_node.c
@@ -0,0 +1,2535 @@
+/*
+ * Copyright 2000-2002 by Hans Reiser, licensing governed by reiserfs/README
+ */
+
+/**
+ ** old_item_num
+ ** old_entry_num
+ ** set_entry_sizes
+ ** create_virtual_node
+ ** check_left
+ ** check_right
+ ** directory_part_size
+ ** get_num_ver
+ ** set_parameters
+ ** is_leaf_removable
+ ** are_leaves_removable
+ ** get_empty_nodes
+ ** get_lfree
+ ** get_rfree
+ ** is_left_neighbor_in_cache
+ ** decrement_key
+ ** get_far_parent
+ ** get_parents
+ ** can_node_be_removed
+ ** ip_check_balance
+ ** dc_check_balance_internal
+ ** dc_check_balance_leaf
+ ** dc_check_balance
+ ** check_balance
+ ** get_direct_parent
+ ** get_neighbors
+ ** fix_nodes
+ **
+ **
+ **/
+
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/reiserfs_fs.h>
+
+
+/* To make any changes in the tree we find a node, that contains item
+ to be changed/deleted or position in the node we insert a new item
+ to. We call this node S. To do balancing we need to decide what we
+ will shift to left/right neighbor, or to a new node, where new item
+ will be etc. To make this analysis simpler we build virtual
+ node. Virtual node is an array of items, that will replace items of
+ node S. (For instance if we are going to delete an item, virtual
+ node does not contain it). Virtual node keeps information about
+ item sizes and types, mergeability of first and last items, sizes
+ of all entries in directory item. We use this array of items when
+ calculating what we can shift to neighbors and how many nodes we
+ have to have if we do not any shiftings, if we shift to left/right
+ neighbor or to both. */
+
+
+/* taking item number in virtual node, returns number of item, that it has in source buffer */
+static inline int old_item_num (int new_num, int affected_item_num, int mode)
+{
+ if (mode == M_PASTE || mode == M_CUT || new_num < affected_item_num)
+ return new_num;
+
+ if (mode == M_INSERT) {
+
+ RFALSE( new_num == 0,
+ "vs-8005: for INSERT mode and item number of inserted item");
+
+ return new_num - 1;
+ }
+
+ RFALSE( mode != M_DELETE,
+ "vs-8010: old_item_num: mode must be M_DELETE (mode = \'%c\'", mode);
+ /* delete mode */
+ return new_num + 1;
+}
+
+static void create_virtual_node (struct tree_balance * tb, int h)
+{
+ struct item_head * ih;
+ struct virtual_node * vn = tb->tb_vn;
+ int new_num;
+ struct buffer_head * Sh; /* this comes from tb->S[h] */
+
+ Sh = PATH_H_PBUFFER (tb->tb_path, h);
+
+ /* size of changed node */
+ vn->vn_size = MAX_CHILD_SIZE (Sh) - B_FREE_SPACE (Sh) + tb->insert_size[h];
+
+ /* for internal nodes array if virtual items is not created */
+ if (h) {
+ vn->vn_nr_item = (vn->vn_size - DC_SIZE) / (DC_SIZE + KEY_SIZE);
+ return;
+ }
+
+ /* number of items in virtual node */
+ vn->vn_nr_item = B_NR_ITEMS (Sh) + ((vn->vn_mode == M_INSERT)? 1 : 0) - ((vn->vn_mode == M_DELETE)? 1 : 0);
+
+ /* first virtual item */
+ vn->vn_vi = (struct virtual_item *)(tb->tb_vn + 1);
+ memset (vn->vn_vi, 0, vn->vn_nr_item * sizeof (struct virtual_item));
+ vn->vn_free_ptr += vn->vn_nr_item * sizeof (struct virtual_item);
+
+
+ /* first item in the node */
+ ih = B_N_PITEM_HEAD (Sh, 0);
+
+ /* define the mergeability for 0-th item (if it is not being deleted) */
+ if (op_is_left_mergeable (&(ih->ih_key), Sh->b_size) && (vn->vn_mode != M_DELETE || vn->vn_affected_item_num))
+ vn->vn_vi[0].vi_type |= VI_TYPE_LEFT_MERGEABLE;
+
+ /* go through all items those remain in the virtual node (except for the new (inserted) one) */
+ for (new_num = 0; new_num < vn->vn_nr_item; new_num ++) {
+ int j;
+ struct virtual_item * vi = vn->vn_vi + new_num;
+ int is_affected = ((new_num != vn->vn_affected_item_num) ? 0 : 1);
+
+
+ if (is_affected && vn->vn_mode == M_INSERT)
+ continue;
+
+ /* get item number in source node */
+ j = old_item_num (new_num, vn->vn_affected_item_num, vn->vn_mode);
+
+ vi->vi_item_len += ih_item_len(ih + j) + IH_SIZE;
+ vi->vi_ih = ih + j;
+ vi->vi_item = B_I_PITEM (Sh, ih + j);
+ vi->vi_uarea = vn->vn_free_ptr;
+
+ // FIXME: there is no check, that item operation did not
+ // consume too much memory
+ vn->vn_free_ptr += op_create_vi (vn, vi, is_affected, tb->insert_size [0]);
+ if (tb->vn_buf + tb->vn_buf_size < vn->vn_free_ptr)
+ reiserfs_panic (tb->tb_sb, "vs-8030: create_virtual_node: "
+ "virtual node space consumed");
+
+ if (!is_affected)
+ /* this is not being changed */
+ continue;
+
+ if (vn->vn_mode == M_PASTE || vn->vn_mode == M_CUT) {
+ vn->vn_vi[new_num].vi_item_len += tb->insert_size[0];
+ vi->vi_new_data = vn->vn_data; // pointer to data which is going to be pasted
+ }
+ }
+
+
+ /* virtual inserted item is not defined yet */
+ if (vn->vn_mode == M_INSERT) {
+ struct virtual_item * vi = vn->vn_vi + vn->vn_affected_item_num;
+
+ RFALSE( vn->vn_ins_ih == 0,
+ "vs-8040: item header of inserted item is not specified");
+ vi->vi_item_len = tb->insert_size[0];
+ vi->vi_ih = vn->vn_ins_ih;
+ vi->vi_item = vn->vn_data;
+ vi->vi_uarea = vn->vn_free_ptr;
+
+ op_create_vi (vn, vi, 0/*not pasted or cut*/, tb->insert_size [0]);
+ }
+
+ /* set right merge flag we take right delimiting key and check whether it is a mergeable item */
+ if (tb->CFR[0]) {
+ struct key * key;
+
+ key = B_N_PDELIM_KEY (tb->CFR[0], tb->rkey[0]);
+ if (op_is_left_mergeable (key, Sh->b_size) && (vn->vn_mode != M_DELETE ||
+ vn->vn_affected_item_num != B_NR_ITEMS (Sh) - 1))
+ vn->vn_vi[vn->vn_nr_item-1].vi_type |= VI_TYPE_RIGHT_MERGEABLE;
+
+#ifdef CONFIG_REISERFS_CHECK
+ if (op_is_left_mergeable (key, Sh->b_size) &&
+ !(vn->vn_mode != M_DELETE || vn->vn_affected_item_num != B_NR_ITEMS (Sh) - 1) ) {
+ /* we delete last item and it could be merged with right neighbor's first item */
+ if (!(B_NR_ITEMS (Sh) == 1 && is_direntry_le_ih (B_N_PITEM_HEAD (Sh, 0)) &&
+ I_ENTRY_COUNT (B_N_PITEM_HEAD (Sh, 0)) == 1)) {
+ /* node contains more than 1 item, or item is not directory item, or this item contains more than 1 entry */
+ print_block (Sh, 0, -1, -1);
+ reiserfs_panic (tb->tb_sb, "vs-8045: create_virtual_node: rdkey %k, affected item==%d (mode==%c) Must be %c",
+ key, vn->vn_affected_item_num, vn->vn_mode, M_DELETE);
+ } else
+ /* we can delete directory item, that has only one directory entry in it */
+ ;
+ }
+#endif
+
+ }
+}
+
+
+/* using virtual node check, how many items can be shifted to left
+ neighbor */
+static void check_left (struct tree_balance * tb, int h, int cur_free)
+{
+ int i;
+ struct virtual_node * vn = tb->tb_vn;
+ struct virtual_item * vi;
+ int d_size, ih_size;
+
+ RFALSE( cur_free < 0, "vs-8050: cur_free (%d) < 0", cur_free);
+
+ /* internal level */
+ if (h > 0) {
+ tb->lnum[h] = cur_free / (DC_SIZE + KEY_SIZE);
+ return;
+ }
+
+ /* leaf level */
+
+ if (!cur_free || !vn->vn_nr_item) {
+ /* no free space or nothing to move */
+ tb->lnum[h] = 0;
+ tb->lbytes = -1;
+ return;
+ }
+
+ RFALSE( !PATH_H_PPARENT (tb->tb_path, 0),
+ "vs-8055: parent does not exist or invalid");
+
+ vi = vn->vn_vi;
+ if ((unsigned int)cur_free >= (vn->vn_size - ((vi->vi_type & VI_TYPE_LEFT_MERGEABLE) ? IH_SIZE : 0))) {
+ /* all contents of S[0] fits into L[0] */
+
+ RFALSE( vn->vn_mode == M_INSERT || vn->vn_mode == M_PASTE,
+ "vs-8055: invalid mode or balance condition failed");
+
+ tb->lnum[0] = vn->vn_nr_item;
+ tb->lbytes = -1;
+ return;
+ }
+
+
+ d_size = 0, ih_size = IH_SIZE;
+
+ /* first item may be merge with last item in left neighbor */
+ if (vi->vi_type & VI_TYPE_LEFT_MERGEABLE)
+ d_size = -((int)IH_SIZE), ih_size = 0;
+
+ tb->lnum[0] = 0;
+ for (i = 0; i < vn->vn_nr_item; i ++, ih_size = IH_SIZE, d_size = 0, vi ++) {
+ d_size += vi->vi_item_len;
+ if (cur_free >= d_size) {
+ /* the item can be shifted entirely */
+ cur_free -= d_size;
+ tb->lnum[0] ++;
+ continue;
+ }
+
+ /* the item cannot be shifted entirely, try to split it */
+ /* check whether L[0] can hold ih and at least one byte of the item body */
+ if (cur_free <= ih_size) {
+ /* cannot shift even a part of the current item */
+ tb->lbytes = -1;
+ return;
+ }
+ cur_free -= ih_size;
+
+ tb->lbytes = op_check_left (vi, cur_free, 0, 0);
+ if (tb->lbytes != -1)
+ /* count partially shifted item */
+ tb->lnum[0] ++;
+
+ break;
+ }
+
+ return;
+}
+
+
+/* using virtual node check, how many items can be shifted to right
+ neighbor */
+static void check_right (struct tree_balance * tb, int h, int cur_free)
+{
+ int i;
+ struct virtual_node * vn = tb->tb_vn;
+ struct virtual_item * vi;
+ int d_size, ih_size;
+
+ RFALSE( cur_free < 0, "vs-8070: cur_free < 0");
+
+ /* internal level */
+ if (h > 0) {
+ tb->rnum[h] = cur_free / (DC_SIZE + KEY_SIZE);
+ return;
+ }
+
+ /* leaf level */
+
+ if (!cur_free || !vn->vn_nr_item) {
+ /* no free space */
+ tb->rnum[h] = 0;
+ tb->rbytes = -1;
+ return;
+ }
+
+ RFALSE( !PATH_H_PPARENT (tb->tb_path, 0),
+ "vs-8075: parent does not exist or invalid");
+
+ vi = vn->vn_vi + vn->vn_nr_item - 1;
+ if ((unsigned int)cur_free >= (vn->vn_size - ((vi->vi_type & VI_TYPE_RIGHT_MERGEABLE) ? IH_SIZE : 0))) {
+ /* all contents of S[0] fits into R[0] */
+
+ RFALSE( vn->vn_mode == M_INSERT || vn->vn_mode == M_PASTE,
+ "vs-8080: invalid mode or balance condition failed");
+
+ tb->rnum[h] = vn->vn_nr_item;
+ tb->rbytes = -1;
+ return;
+ }
+
+ d_size = 0, ih_size = IH_SIZE;
+
+ /* last item may be merge with first item in right neighbor */
+ if (vi->vi_type & VI_TYPE_RIGHT_MERGEABLE)
+ d_size = -(int)IH_SIZE, ih_size = 0;
+
+ tb->rnum[0] = 0;
+ for (i = vn->vn_nr_item - 1; i >= 0; i --, d_size = 0, ih_size = IH_SIZE, vi --) {
+ d_size += vi->vi_item_len;
+ if (cur_free >= d_size) {
+ /* the item can be shifted entirely */
+ cur_free -= d_size;
+ tb->rnum[0] ++;
+ continue;
+ }
+
+ /* check whether R[0] can hold ih and at least one byte of the item body */
+ if ( cur_free <= ih_size ) { /* cannot shift even a part of the current item */
+ tb->rbytes = -1;
+ return;
+ }
+
+ /* R[0] can hold the header of the item and at least one byte of its body */
+ cur_free -= ih_size; /* cur_free is still > 0 */
+
+ tb->rbytes = op_check_right (vi, cur_free);
+ if (tb->rbytes != -1)
+ /* count partially shifted item */
+ tb->rnum[0] ++;
+
+ break;
+ }
+
+ return;
+}
+
+
+/*
+ * from - number of items, which are shifted to left neighbor entirely
+ * to - number of item, which are shifted to right neighbor entirely
+ * from_bytes - number of bytes of boundary item (or directory entries) which are shifted to left neighbor
+ * to_bytes - number of bytes of boundary item (or directory entries) which are shifted to right neighbor */
+static int get_num_ver (int mode, struct tree_balance * tb, int h,
+ int from, int from_bytes,
+ int to, int to_bytes,
+ short * snum012, int flow
+ )
+{
+ int i;
+ int cur_free;
+ // int bytes;
+ int units;
+ struct virtual_node * vn = tb->tb_vn;
+ // struct virtual_item * vi;
+
+ int total_node_size, max_node_size, current_item_size;
+ int needed_nodes;
+ int start_item, /* position of item we start filling node from */
+ end_item, /* position of item we finish filling node by */
+ start_bytes,/* number of first bytes (entries for directory) of start_item-th item
+ we do not include into node that is being filled */
+ end_bytes; /* number of last bytes (entries for directory) of end_item-th item
+ we do node include into node that is being filled */
+ int split_item_positions[2]; /* these are positions in virtual item of
+ items, that are split between S[0] and
+ S1new and S1new and S2new */
+
+ split_item_positions[0] = -1;
+ split_item_positions[1] = -1;
+
+ /* We only create additional nodes if we are in insert or paste mode
+ or we are in replace mode at the internal level. If h is 0 and
+ the mode is M_REPLACE then in fix_nodes we change the mode to
+ paste or insert before we get here in the code. */
+ RFALSE( tb->insert_size[h] < 0 || (mode != M_INSERT && mode != M_PASTE),
+ "vs-8100: insert_size < 0 in overflow");
+
+ max_node_size = MAX_CHILD_SIZE (PATH_H_PBUFFER (tb->tb_path, h));
+
+ /* snum012 [0-2] - number of items, that lay
+ to S[0], first new node and second new node */
+ snum012[3] = -1; /* s1bytes */
+ snum012[4] = -1; /* s2bytes */
+
+ /* internal level */
+ if (h > 0) {
+ i = ((to - from) * (KEY_SIZE + DC_SIZE) + DC_SIZE);
+ if (i == max_node_size)
+ return 1;
+ return (i / max_node_size + 1);
+ }
+
+ /* leaf level */
+ needed_nodes = 1;
+ total_node_size = 0;
+ cur_free = max_node_size;
+
+ // start from 'from'-th item
+ start_item = from;
+ // skip its first 'start_bytes' units
+ start_bytes = ((from_bytes != -1) ? from_bytes : 0);
+
+ // last included item is the 'end_item'-th one
+ end_item = vn->vn_nr_item - to - 1;
+ // do not count last 'end_bytes' units of 'end_item'-th item
+ end_bytes = (to_bytes != -1) ? to_bytes : 0;
+
+ /* go through all item beginning from the start_item-th item and ending by
+ the end_item-th item. Do not count first 'start_bytes' units of
+ 'start_item'-th item and last 'end_bytes' of 'end_item'-th item */
+
+ for (i = start_item; i <= end_item; i ++) {
+ struct virtual_item * vi = vn->vn_vi + i;
+ int skip_from_end = ((i == end_item) ? end_bytes : 0);
+
+ RFALSE( needed_nodes > 3, "vs-8105: too many nodes are needed");
+
+ /* get size of current item */
+ current_item_size = vi->vi_item_len;
+
+ /* do not take in calculation head part (from_bytes) of from-th item */
+ current_item_size -= op_part_size (vi, 0/*from start*/, start_bytes);
+
+ /* do not take in calculation tail part of last item */
+ current_item_size -= op_part_size (vi, 1/*from end*/, skip_from_end);
+
+ /* if item fits into current node entierly */
+ if (total_node_size + current_item_size <= max_node_size) {
+ snum012[needed_nodes - 1] ++;
+ total_node_size += current_item_size;
+ start_bytes = 0;
+ continue;
+ }
+
+ if (current_item_size > max_node_size) {
+ /* virtual item length is longer, than max size of item in
+ a node. It is impossible for direct item */
+ RFALSE( is_direct_le_ih (vi->vi_ih),
+ "vs-8110: "
+ "direct item length is %d. It can not be longer than %d",
+ current_item_size, max_node_size);
+ /* we will try to split it */
+ flow = 1;
+ }
+
+ if (!flow) {
+ /* as we do not split items, take new node and continue */
+ needed_nodes ++; i --; total_node_size = 0;
+ continue;
+ }
+
+ // calculate number of item units which fit into node being
+ // filled
+ {
+ int free_space;
+
+ free_space = max_node_size - total_node_size - IH_SIZE;
+ units = op_check_left (vi, free_space, start_bytes, skip_from_end);
+ if (units == -1) {
+ /* nothing fits into current node, take new node and continue */
+ needed_nodes ++, i--, total_node_size = 0;
+ continue;
+ }
+ }
+
+ /* something fits into the current node */
+ //if (snum012[3] != -1 || needed_nodes != 1)
+ // reiserfs_panic (tb->tb_sb, "vs-8115: get_num_ver: too many nodes required");
+ //snum012[needed_nodes - 1 + 3] = op_unit_num (vi) - start_bytes - units;
+ start_bytes += units;
+ snum012[needed_nodes - 1 + 3] = units;
+
+ if (needed_nodes > 2)
+ reiserfs_warning (tb->tb_sb, "vs-8111: get_num_ver: split_item_position is out of boundary\n");
+ snum012[needed_nodes - 1] ++;
+ split_item_positions[needed_nodes - 1] = i;
+ needed_nodes ++;
+ /* continue from the same item with start_bytes != -1 */
+ start_item = i;
+ i --;
+ total_node_size = 0;
+ }
+
+ // sum012[4] (if it is not -1) contains number of units of which
+ // are to be in S1new, snum012[3] - to be in S0. They are supposed
+ // to be S1bytes and S2bytes correspondingly, so recalculate
+ if (snum012[4] > 0) {
+ int split_item_num;
+ int bytes_to_r, bytes_to_l;
+ int bytes_to_S1new;
+
+ split_item_num = split_item_positions[1];
+ bytes_to_l = ((from == split_item_num && from_bytes != -1) ? from_bytes : 0);
+ bytes_to_r = ((end_item == split_item_num && end_bytes != -1) ? end_bytes : 0);
+ bytes_to_S1new = ((split_item_positions[0] == split_item_positions[1]) ? snum012[3] : 0);
+
+ // s2bytes
+ snum012[4] = op_unit_num (&vn->vn_vi[split_item_num]) - snum012[4] - bytes_to_r - bytes_to_l - bytes_to_S1new;
+
+ if (vn->vn_vi[split_item_num].vi_index != TYPE_DIRENTRY)
+ reiserfs_warning (tb->tb_sb, "vs-8115: get_num_ver: not directory item\n");
+ }
+
+ /* now we know S2bytes, calculate S1bytes */
+ if (snum012[3] > 0) {
+ int split_item_num;
+ int bytes_to_r, bytes_to_l;
+ int bytes_to_S2new;
+
+ split_item_num = split_item_positions[0];
+ bytes_to_l = ((from == split_item_num && from_bytes != -1) ? from_bytes : 0);
+ bytes_to_r = ((end_item == split_item_num && end_bytes != -1) ? end_bytes : 0);
+ bytes_to_S2new = ((split_item_positions[0] == split_item_positions[1] && snum012[4] != -1) ? snum012[4] : 0);
+
+ // s1bytes
+ snum012[3] = op_unit_num (&vn->vn_vi[split_item_num]) - snum012[3] - bytes_to_r - bytes_to_l - bytes_to_S2new;
+ }
+
+ return needed_nodes;
+}
+
+
+#ifdef CONFIG_REISERFS_CHECK
+extern struct tree_balance * cur_tb;
+#endif
+
+
+/* Set parameters for balancing.
+ * Performs write of results of analysis of balancing into structure tb,
+ * where it will later be used by the functions that actually do the balancing.
+ * Parameters:
+ * tb tree_balance structure;
+ * h current level of the node;
+ * lnum number of items from S[h] that must be shifted to L[h];
+ * rnum number of items from S[h] that must be shifted to R[h];
+ * blk_num number of blocks that S[h] will be splitted into;
+ * s012 number of items that fall into splitted nodes.
+ * lbytes number of bytes which flow to the left neighbor from the item that is not
+ * not shifted entirely
+ * rbytes number of bytes which flow to the right neighbor from the item that is not
+ * not shifted entirely
+ * s1bytes number of bytes which flow to the first new node when S[0] splits (this number is contained in s012 array)
+ */
+
+static void set_parameters (struct tree_balance * tb, int h, int lnum,
+ int rnum, int blk_num, short * s012, int lb, int rb)
+{
+
+ tb->lnum[h] = lnum;
+ tb->rnum[h] = rnum;
+ tb->blknum[h] = blk_num;
+
+ if (h == 0)
+ { /* only for leaf level */
+ if (s012 != NULL)
+ {
+ tb->s0num = * s012 ++,
+ tb->s1num = * s012 ++,
+ tb->s2num = * s012 ++;
+ tb->s1bytes = * s012 ++;
+ tb->s2bytes = * s012;
+ }
+ tb->lbytes = lb;
+ tb->rbytes = rb;
+ }
+ PROC_INFO_ADD( tb -> tb_sb, lnum[ h ], lnum );
+ PROC_INFO_ADD( tb -> tb_sb, rnum[ h ], rnum );
+
+ PROC_INFO_ADD( tb -> tb_sb, lbytes[ h ], lb );
+ PROC_INFO_ADD( tb -> tb_sb, rbytes[ h ], rb );
+}
+
+
+
+/* check, does node disappear if we shift tb->lnum[0] items to left
+ neighbor and tb->rnum[0] to the right one. */
+static int is_leaf_removable (struct tree_balance * tb)
+{
+ struct virtual_node * vn = tb->tb_vn;
+ int to_left, to_right;
+ int size;
+ int remain_items;
+
+ /* number of items, that will be shifted to left (right) neighbor
+ entirely */
+ to_left = tb->lnum[0] - ((tb->lbytes != -1) ? 1 : 0);
+ to_right = tb->rnum[0] - ((tb->rbytes != -1) ? 1 : 0);
+ remain_items = vn->vn_nr_item;
+
+ /* how many items remain in S[0] after shiftings to neighbors */
+ remain_items -= (to_left + to_right);
+
+ if (remain_items < 1) {
+ /* all content of node can be shifted to neighbors */
+ set_parameters (tb, 0, to_left, vn->vn_nr_item - to_left, 0, NULL, -1, -1);
+ return 1;
+ }
+
+ if (remain_items > 1 || tb->lbytes == -1 || tb->rbytes == -1)
+ /* S[0] is not removable */
+ return 0;
+
+ /* check, whether we can divide 1 remaining item between neighbors */
+
+ /* get size of remaining item (in item units) */
+ size = op_unit_num (&(vn->vn_vi[to_left]));
+
+ if (tb->lbytes + tb->rbytes >= size) {
+ set_parameters (tb, 0, to_left + 1, to_right + 1, 0, NULL, tb->lbytes, -1);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/* check whether L, S, R can be joined in one node */
+static int are_leaves_removable (struct tree_balance * tb, int lfree, int rfree)
+{
+ struct virtual_node * vn = tb->tb_vn;
+ int ih_size;
+ struct buffer_head *S0;
+
+ S0 = PATH_H_PBUFFER (tb->tb_path, 0);
+
+ ih_size = 0;
+ if (vn->vn_nr_item) {
+ if (vn->vn_vi[0].vi_type & VI_TYPE_LEFT_MERGEABLE)
+ ih_size += IH_SIZE;
+
+ if (vn->vn_vi[vn->vn_nr_item-1].vi_type & VI_TYPE_RIGHT_MERGEABLE)
+ ih_size += IH_SIZE;
+ } else {
+ /* there was only one item and it will be deleted */
+ struct item_head * ih;
+
+ RFALSE( B_NR_ITEMS (S0) != 1,
+ "vs-8125: item number must be 1: it is %d", B_NR_ITEMS(S0));
+
+ ih = B_N_PITEM_HEAD (S0, 0);
+ if (tb->CFR[0] && !comp_short_le_keys (&(ih->ih_key), B_N_PDELIM_KEY (tb->CFR[0], tb->rkey[0])))
+ if (is_direntry_le_ih (ih)) {
+ /* Directory must be in correct state here: that is
+ somewhere at the left side should exist first directory
+ item. But the item being deleted can not be that first
+ one because its right neighbor is item of the same
+ directory. (But first item always gets deleted in last
+ turn). So, neighbors of deleted item can be merged, so
+ we can save ih_size */
+ ih_size = IH_SIZE;
+
+ /* we might check that left neighbor exists and is of the
+ same directory */
+ RFALSE(le_ih_k_offset (ih) == DOT_OFFSET,
+ "vs-8130: first directory item can not be removed until directory is not empty");
+ }
+
+ }
+
+ if (MAX_CHILD_SIZE (S0) + vn->vn_size <= rfree + lfree + ih_size) {
+ set_parameters (tb, 0, -1, -1, -1, NULL, -1, -1);
+ PROC_INFO_INC( tb -> tb_sb, leaves_removable );
+ return 1;
+ }
+ return 0;
+
+}
+
+
+
+/* when we do not split item, lnum and rnum are numbers of entire items */
+#define SET_PAR_SHIFT_LEFT \
+if (h)\
+{\
+ int to_l;\
+ \
+ to_l = (MAX_NR_KEY(Sh)+1 - lpar + vn->vn_nr_item + 1) / 2 -\
+ (MAX_NR_KEY(Sh) + 1 - lpar);\
+ \
+ set_parameters (tb, h, to_l, 0, lnver, NULL, -1, -1);\
+}\
+else \
+{\
+ if (lset==LEFT_SHIFT_FLOW)\
+ set_parameters (tb, h, lpar, 0, lnver, snum012+lset,\
+ tb->lbytes, -1);\
+ else\
+ set_parameters (tb, h, lpar - (tb->lbytes!=-1), 0, lnver, snum012+lset,\
+ -1, -1);\
+}
+
+
+#define SET_PAR_SHIFT_RIGHT \
+if (h)\
+{\
+ int to_r;\
+ \
+ to_r = (MAX_NR_KEY(Sh)+1 - rpar + vn->vn_nr_item + 1) / 2 - (MAX_NR_KEY(Sh) + 1 - rpar);\
+ \
+ set_parameters (tb, h, 0, to_r, rnver, NULL, -1, -1);\
+}\
+else \
+{\
+ if (rset==RIGHT_SHIFT_FLOW)\
+ set_parameters (tb, h, 0, rpar, rnver, snum012+rset,\
+ -1, tb->rbytes);\
+ else\
+ set_parameters (tb, h, 0, rpar - (tb->rbytes!=-1), rnver, snum012+rset,\
+ -1, -1);\
+}
+
+
+void free_buffers_in_tb (
+ struct tree_balance * p_s_tb
+ ) {
+ int n_counter;
+
+ decrement_counters_in_path(p_s_tb->tb_path);
+
+ for ( n_counter = 0; n_counter < MAX_HEIGHT; n_counter++ ) {
+ decrement_bcount(p_s_tb->L[n_counter]);
+ p_s_tb->L[n_counter] = NULL;
+ decrement_bcount(p_s_tb->R[n_counter]);
+ p_s_tb->R[n_counter] = NULL;
+ decrement_bcount(p_s_tb->FL[n_counter]);
+ p_s_tb->FL[n_counter] = NULL;
+ decrement_bcount(p_s_tb->FR[n_counter]);
+ p_s_tb->FR[n_counter] = NULL;
+ decrement_bcount(p_s_tb->CFL[n_counter]);
+ p_s_tb->CFL[n_counter] = NULL;
+ decrement_bcount(p_s_tb->CFR[n_counter]);
+ p_s_tb->CFR[n_counter] = NULL;
+ }
+}
+
+
+/* Get new buffers for storing new nodes that are created while balancing.
+ * Returns: SCHEDULE_OCCURRED - schedule occurred while the function worked;
+ * CARRY_ON - schedule didn't occur while the function worked;
+ * NO_DISK_SPACE - no disk space.
+ */
+/* The function is NOT SCHEDULE-SAFE! */
+static int get_empty_nodes(
+ struct tree_balance * p_s_tb,
+ int n_h
+ ) {
+ struct buffer_head * p_s_new_bh,
+ * p_s_Sh = PATH_H_PBUFFER (p_s_tb->tb_path, n_h);
+ unsigned long * p_n_blocknr,
+ a_n_blocknrs[MAX_AMOUNT_NEEDED] = {0, };
+ int n_counter,
+ n_number_of_freeblk,
+ n_amount_needed,/* number of needed empty blocks */
+ n_retval = CARRY_ON;
+ struct super_block * p_s_sb = p_s_tb->tb_sb;
+
+
+ /* number_of_freeblk is the number of empty blocks which have been
+ acquired for use by the balancing algorithm minus the number of
+ empty blocks used in the previous levels of the analysis,
+ number_of_freeblk = tb->cur_blknum can be non-zero if a schedule occurs
+ after empty blocks are acquired, and the balancing analysis is
+ then restarted, amount_needed is the number needed by this level
+ (n_h) of the balancing analysis.
+
+ Note that for systems with many processes writing, it would be
+ more layout optimal to calculate the total number needed by all
+ levels and then to run reiserfs_new_blocks to get all of them at once. */
+
+ /* Initiate number_of_freeblk to the amount acquired prior to the restart of
+ the analysis or 0 if not restarted, then subtract the amount needed
+ by all of the levels of the tree below n_h. */
+ /* blknum includes S[n_h], so we subtract 1 in this calculation */
+ for ( n_counter = 0, n_number_of_freeblk = p_s_tb->cur_blknum; n_counter < n_h; n_counter++ )
+ n_number_of_freeblk -= ( p_s_tb->blknum[n_counter] ) ? (p_s_tb->blknum[n_counter] - 1) : 0;
+
+ /* Allocate missing empty blocks. */
+ /* if p_s_Sh == 0 then we are getting a new root */
+ n_amount_needed = ( p_s_Sh ) ? (p_s_tb->blknum[n_h] - 1) : 1;
+ /* Amount_needed = the amount that we need more than the amount that we have. */
+ if ( n_amount_needed > n_number_of_freeblk )
+ n_amount_needed -= n_number_of_freeblk;
+ else /* If we have enough already then there is nothing to do. */
+ return CARRY_ON;
+
+ if ( reiserfs_new_form_blocknrs (p_s_tb, a_n_blocknrs,
+ n_amount_needed) == NO_DISK_SPACE )
+ return NO_DISK_SPACE;
+
+ /* for each blocknumber we just got, get a buffer and stick it on FEB */
+ for ( p_n_blocknr = a_n_blocknrs, n_counter = 0; n_counter < n_amount_needed;
+ p_n_blocknr++, n_counter++ ) {
+
+ RFALSE( ! *p_n_blocknr,
+ "PAP-8135: reiserfs_new_blocknrs failed when got new blocks");
+
+ p_s_new_bh = getblk(p_s_sb->s_dev, *p_n_blocknr, p_s_sb->s_blocksize);
+ if (atomic_read (&(p_s_new_bh->b_count)) > 1) {
+/*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
+/*
+ reiserfs_warning (p_s_sb, "waiting for buffer %b, iput inode pid = %d, this pid %d, mode %c, %h\n",
+ p_s_new_bh, put_inode_pid, current->pid, p_s_tb->tb_vn->vn_mode, p_s_tb->tb_vn->vn_ins_ih);
+ print_tb (0, 0, 0, p_s_tb, "tb");
+*/
+/*&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&*/
+ if (atomic_read(&(p_s_new_bh->b_count)) > 2 ||
+ !(buffer_journaled(p_s_new_bh) || buffer_journal_dirty(p_s_new_bh))) {
+ n_retval = REPEAT_SEARCH ;
+ free_buffers_in_tb (p_s_tb);
+ wait_buffer_until_released (p_s_new_bh);
+ }
+ }
+ RFALSE( (atomic_read (&(p_s_new_bh->b_count)) != 1 ||
+ buffer_dirty (p_s_new_bh)) &&
+ (atomic_read(&(p_s_new_bh->b_count)) > 2 ||
+ !(buffer_journaled(p_s_new_bh) ||
+ buffer_journal_dirty(p_s_new_bh))),
+ "PAP-8140: not free or dirty buffer %b for the new block",
+ p_s_new_bh);
+
+ /* Put empty buffers into the array. */
+ if (p_s_tb->FEB[p_s_tb->cur_blknum])
+ BUG();
+
+ mark_buffer_journal_new(p_s_new_bh) ;
+ p_s_tb->FEB[p_s_tb->cur_blknum++] = p_s_new_bh;
+ }
+
+ if ( n_retval == CARRY_ON && FILESYSTEM_CHANGED_TB (p_s_tb) )
+ n_retval = REPEAT_SEARCH ;
+
+ return n_retval;
+}
+
+
+/* Get free space of the left neighbor, which is stored in the parent
+ * node of the left neighbor. */
+static int get_lfree (struct tree_balance * tb, int h)
+{
+ struct buffer_head * l, * f;
+ int order;
+
+ if ((f = PATH_H_PPARENT (tb->tb_path, h)) == 0 || (l = tb->FL[h]) == 0)
+ return 0;
+
+ if (f == l)
+ order = PATH_H_B_ITEM_ORDER (tb->tb_path, h) - 1;
+ else {
+ order = B_NR_ITEMS (l);
+ f = l;
+ }
+
+ return (MAX_CHILD_SIZE(f) - dc_size(B_N_CHILD(f,order)));
+}
+
+
+/* Get free space of the right neighbor,
+ * which is stored in the parent node of the right neighbor.
+ */
+static int get_rfree (struct tree_balance * tb, int h)
+{
+ struct buffer_head * r, * f;
+ int order;
+
+ if ((f = PATH_H_PPARENT (tb->tb_path, h)) == 0 || (r = tb->FR[h]) == 0)
+ return 0;
+
+ if (f == r)
+ order = PATH_H_B_ITEM_ORDER (tb->tb_path, h) + 1;
+ else {
+ order = 0;
+ f = r;
+ }
+
+ return (MAX_CHILD_SIZE(f) - dc_size( B_N_CHILD(f,order)));
+
+}
+
+
+/* Check whether left neighbor is in memory. */
+static int is_left_neighbor_in_cache(
+ struct tree_balance * p_s_tb,
+ int n_h
+ ) {
+ struct buffer_head * p_s_father, * left;
+ struct super_block * p_s_sb = p_s_tb->tb_sb;
+ unsigned long n_left_neighbor_blocknr;
+ int n_left_neighbor_position;
+
+ if ( ! p_s_tb->FL[n_h] ) /* Father of the left neighbor does not exist. */
+ return 0;
+
+ /* Calculate father of the node to be balanced. */
+ p_s_father = PATH_H_PBUFFER(p_s_tb->tb_path, n_h + 1);
+
+ RFALSE( ! p_s_father ||
+ ! B_IS_IN_TREE (p_s_father) ||
+ ! B_IS_IN_TREE (p_s_tb->FL[n_h]) ||
+ ! buffer_uptodate (p_s_father) ||
+ ! buffer_uptodate (p_s_tb->FL[n_h]),
+ "vs-8165: F[h] (%b) or FL[h] (%b) is invalid",
+ p_s_father, p_s_tb->FL[n_h]);
+
+
+ /* Get position of the pointer to the left neighbor into the left father. */
+ n_left_neighbor_position = ( p_s_father == p_s_tb->FL[n_h] ) ?
+ p_s_tb->lkey[n_h] : B_NR_ITEMS (p_s_tb->FL[n_h]);
+ /* Get left neighbor block number. */
+ n_left_neighbor_blocknr = B_N_CHILD_NUM(p_s_tb->FL[n_h], n_left_neighbor_position);
+ /* Look for the left neighbor in the cache. */
+ if ( (left = sb_get_hash_table(p_s_sb, n_left_neighbor_blocknr)) ) {
+
+ RFALSE( buffer_uptodate (left) && ! B_IS_IN_TREE(left),
+ "vs-8170: left neighbor (%b %z) is not in the tree", left, left);
+ put_bh(left) ;
+ return 1;
+ }
+
+ return 0;
+}
+
+
+#define LEFT_PARENTS 'l'
+#define RIGHT_PARENTS 'r'
+
+
+static void decrement_key (struct cpu_key * p_s_key)
+{
+ // call item specific function for this key
+ item_ops[cpu_key_k_type (p_s_key)]->decrement_key (p_s_key);
+}
+
+
+
+
+/* Calculate far left/right parent of the left/right neighbor of the current node, that
+ * is calculate the left/right (FL[h]/FR[h]) neighbor of the parent F[h].
+ * Calculate left/right common parent of the current node and L[h]/R[h].
+ * Calculate left/right delimiting key position.
+ * Returns: PATH_INCORRECT - path in the tree is not correct;
+ SCHEDULE_OCCURRED - schedule occurred while the function worked;
+ * CARRY_ON - schedule didn't occur while the function worked;
+ */
+static int get_far_parent (struct tree_balance * p_s_tb,
+ int n_h,
+ struct buffer_head ** pp_s_father,
+ struct buffer_head ** pp_s_com_father,
+ char c_lr_par)
+{
+ struct buffer_head * p_s_parent;
+ INITIALIZE_PATH (s_path_to_neighbor_father);
+ struct path * p_s_path = p_s_tb->tb_path;
+ struct cpu_key s_lr_father_key;
+ int n_counter,
+ n_position = INT_MAX,
+ n_first_last_position = 0,
+ n_path_offset = PATH_H_PATH_OFFSET(p_s_path, n_h);
+
+ /* Starting from F[n_h] go upwards in the tree, and look for the common
+ ancestor of F[n_h], and its neighbor l/r, that should be obtained. */
+
+ n_counter = n_path_offset;
+
+ RFALSE( n_counter < FIRST_PATH_ELEMENT_OFFSET,
+ "PAP-8180: invalid path length");
+
+
+ for ( ; n_counter > FIRST_PATH_ELEMENT_OFFSET; n_counter-- ) {
+ /* Check whether parent of the current buffer in the path is really parent in the tree. */
+ if ( ! B_IS_IN_TREE(p_s_parent = PATH_OFFSET_PBUFFER(p_s_path, n_counter - 1)) )
+ return REPEAT_SEARCH;
+ /* Check whether position in the parent is correct. */
+ if ( (n_position = PATH_OFFSET_POSITION(p_s_path, n_counter - 1)) > B_NR_ITEMS(p_s_parent) )
+ return REPEAT_SEARCH;
+ /* Check whether parent at the path really points to the child. */
+ if ( B_N_CHILD_NUM(p_s_parent, n_position) !=
+ PATH_OFFSET_PBUFFER(p_s_path, n_counter)->b_blocknr )
+ return REPEAT_SEARCH;
+ /* Return delimiting key if position in the parent is not equal to first/last one. */
+ if ( c_lr_par == RIGHT_PARENTS )
+ n_first_last_position = B_NR_ITEMS (p_s_parent);
+ if ( n_position != n_first_last_position ) {
+ *pp_s_com_father = p_s_parent;
+ get_bh(*pp_s_com_father) ;
+ /*(*pp_s_com_father = p_s_parent)->b_count++;*/
+ break;
+ }
+ }
+
+ /* if we are in the root of the tree, then there is no common father */
+ if ( n_counter == FIRST_PATH_ELEMENT_OFFSET ) {
+ /* Check whether first buffer in the path is the root of the tree. */
+ if ( PATH_OFFSET_PBUFFER(p_s_tb->tb_path, FIRST_PATH_ELEMENT_OFFSET)->b_blocknr ==
+ SB_ROOT_BLOCK (p_s_tb->tb_sb) ) {
+ *pp_s_father = *pp_s_com_father = NULL;
+ return CARRY_ON;
+ }
+ return REPEAT_SEARCH;
+ }
+
+ RFALSE( B_LEVEL (*pp_s_com_father) <= DISK_LEAF_NODE_LEVEL,
+ "PAP-8185: (%b %z) level too small",
+ *pp_s_com_father, *pp_s_com_father);
+
+ /* Check whether the common parent is locked. */
+
+ if ( buffer_locked (*pp_s_com_father) ) {
+ __wait_on_buffer(*pp_s_com_father);
+ if ( FILESYSTEM_CHANGED_TB (p_s_tb) ) {
+ decrement_bcount(*pp_s_com_father);
+ return REPEAT_SEARCH;
+ }
+ }
+
+ /* So, we got common parent of the current node and its left/right neighbor.
+ Now we are geting the parent of the left/right neighbor. */
+
+ /* Form key to get parent of the left/right neighbor. */
+ le_key2cpu_key (&s_lr_father_key, B_N_PDELIM_KEY(*pp_s_com_father, ( c_lr_par == LEFT_PARENTS ) ?
+ (p_s_tb->lkey[n_h - 1] = n_position - 1) : (p_s_tb->rkey[n_h - 1] = n_position)));
+
+
+ if ( c_lr_par == LEFT_PARENTS )
+ decrement_key(&s_lr_father_key);
+
+ if (search_by_key(p_s_tb->tb_sb, &s_lr_father_key, &s_path_to_neighbor_father, n_h + 1) == IO_ERROR)
+ // path is released
+ return IO_ERROR;
+
+ if ( FILESYSTEM_CHANGED_TB (p_s_tb) ) {
+ decrement_counters_in_path(&s_path_to_neighbor_father);
+ decrement_bcount(*pp_s_com_father);
+ return REPEAT_SEARCH;
+ }
+
+ *pp_s_father = PATH_PLAST_BUFFER(&s_path_to_neighbor_father);
+
+ RFALSE( B_LEVEL (*pp_s_father) != n_h + 1,
+ "PAP-8190: (%b %z) level too small", *pp_s_father, *pp_s_father);
+ RFALSE( s_path_to_neighbor_father.path_length < FIRST_PATH_ELEMENT_OFFSET,
+ "PAP-8192: path length is too small");
+
+ s_path_to_neighbor_father.path_length--;
+ decrement_counters_in_path(&s_path_to_neighbor_father);
+ return CARRY_ON;
+}
+
+
+/* Get parents of neighbors of node in the path(S[n_path_offset]) and common parents of
+ * S[n_path_offset] and L[n_path_offset]/R[n_path_offset]: F[n_path_offset], FL[n_path_offset],
+ * FR[n_path_offset], CFL[n_path_offset], CFR[n_path_offset].
+ * Calculate numbers of left and right delimiting keys position: lkey[n_path_offset], rkey[n_path_offset].
+ * Returns: SCHEDULE_OCCURRED - schedule occurred while the function worked;
+ * CARRY_ON - schedule didn't occur while the function worked;
+ */
+static int get_parents (struct tree_balance * p_s_tb, int n_h)
+{
+ struct path * p_s_path = p_s_tb->tb_path;
+ int n_position,
+ n_ret_value,
+ n_path_offset = PATH_H_PATH_OFFSET(p_s_tb->tb_path, n_h);
+ struct buffer_head * p_s_curf,
+ * p_s_curcf;
+
+ /* Current node is the root of the tree or will be root of the tree */
+ if ( n_path_offset <= FIRST_PATH_ELEMENT_OFFSET ) {
+ /* The root can not have parents.
+ Release nodes which previously were obtained as parents of the current node neighbors. */
+ decrement_bcount(p_s_tb->FL[n_h]);
+ decrement_bcount(p_s_tb->CFL[n_h]);
+ decrement_bcount(p_s_tb->FR[n_h]);
+ decrement_bcount(p_s_tb->CFR[n_h]);
+ p_s_tb->FL[n_h] = p_s_tb->CFL[n_h] = p_s_tb->FR[n_h] = p_s_tb->CFR[n_h] = NULL;
+ return CARRY_ON;
+ }
+
+ /* Get parent FL[n_path_offset] of L[n_path_offset]. */
+ if ( (n_position = PATH_OFFSET_POSITION(p_s_path, n_path_offset - 1)) ) {
+ /* Current node is not the first child of its parent. */
+ /*(p_s_curf = p_s_curcf = PATH_OFFSET_PBUFFER(p_s_path, n_path_offset - 1))->b_count += 2;*/
+ p_s_curf = p_s_curcf = PATH_OFFSET_PBUFFER(p_s_path, n_path_offset - 1);
+ get_bh(p_s_curf) ;
+ get_bh(p_s_curf) ;
+ p_s_tb->lkey[n_h] = n_position - 1;
+ }
+ else {
+ /* Calculate current parent of L[n_path_offset], which is the left neighbor of the current node.
+ Calculate current common parent of L[n_path_offset] and the current node. Note that
+ CFL[n_path_offset] not equal FL[n_path_offset] and CFL[n_path_offset] not equal F[n_path_offset].
+ Calculate lkey[n_path_offset]. */
+ if ( (n_ret_value = get_far_parent(p_s_tb, n_h + 1, &p_s_curf,
+ &p_s_curcf, LEFT_PARENTS)) != CARRY_ON )
+ return n_ret_value;
+ }
+
+ decrement_bcount(p_s_tb->FL[n_h]);
+ p_s_tb->FL[n_h] = p_s_curf; /* New initialization of FL[n_h]. */
+ decrement_bcount(p_s_tb->CFL[n_h]);
+ p_s_tb->CFL[n_h] = p_s_curcf; /* New initialization of CFL[n_h]. */
+
+ RFALSE( (p_s_curf && !B_IS_IN_TREE (p_s_curf)) ||
+ (p_s_curcf && !B_IS_IN_TREE (p_s_curcf)),
+ "PAP-8195: FL (%b) or CFL (%b) is invalid", p_s_curf, p_s_curcf);
+
+/* Get parent FR[n_h] of R[n_h]. */
+
+/* Current node is the last child of F[n_h]. FR[n_h] != F[n_h]. */
+ if ( n_position == B_NR_ITEMS (PATH_H_PBUFFER(p_s_path, n_h + 1)) ) {
+/* Calculate current parent of R[n_h], which is the right neighbor of F[n_h].
+ Calculate current common parent of R[n_h] and current node. Note that CFR[n_h]
+ not equal FR[n_path_offset] and CFR[n_h] not equal F[n_h]. */
+ if ( (n_ret_value = get_far_parent(p_s_tb, n_h + 1, &p_s_curf, &p_s_curcf, RIGHT_PARENTS)) != CARRY_ON )
+ return n_ret_value;
+ }
+ else {
+/* Current node is not the last child of its parent F[n_h]. */
+ /*(p_s_curf = p_s_curcf = PATH_OFFSET_PBUFFER(p_s_path, n_path_offset - 1))->b_count += 2;*/
+ p_s_curf = p_s_curcf = PATH_OFFSET_PBUFFER(p_s_path, n_path_offset - 1);
+ get_bh(p_s_curf) ;
+ get_bh(p_s_curf) ;
+ p_s_tb->rkey[n_h] = n_position;
+ }
+
+ decrement_bcount(p_s_tb->FR[n_h]);
+ p_s_tb->FR[n_h] = p_s_curf; /* New initialization of FR[n_path_offset]. */
+
+ decrement_bcount(p_s_tb->CFR[n_h]);
+ p_s_tb->CFR[n_h] = p_s_curcf; /* New initialization of CFR[n_path_offset]. */
+
+ RFALSE( (p_s_curf && !B_IS_IN_TREE (p_s_curf)) ||
+ (p_s_curcf && !B_IS_IN_TREE (p_s_curcf)),
+ "PAP-8205: FR (%b) or CFR (%b) is invalid", p_s_curf, p_s_curcf);
+
+ return CARRY_ON;
+}
+
+
+/* it is possible to remove node as result of shiftings to
+ neighbors even when we insert or paste item. */
+static inline int can_node_be_removed (int mode, int lfree, int sfree, int rfree, struct tree_balance * tb, int h)
+{
+ struct buffer_head * Sh = PATH_H_PBUFFER (tb->tb_path, h);
+ int levbytes = tb->insert_size[h];
+ struct item_head * ih;
+ struct key * r_key = NULL;
+
+ ih = B_N_PITEM_HEAD (Sh, 0);
+ if ( tb->CFR[h] )
+ r_key = B_N_PDELIM_KEY(tb->CFR[h],tb->rkey[h]);
+
+ if (
+ lfree + rfree + sfree < MAX_CHILD_SIZE(Sh) + levbytes
+ /* shifting may merge items which might save space */
+ - (( ! h && op_is_left_mergeable (&(ih->ih_key), Sh->b_size) ) ? IH_SIZE : 0)
+ - (( ! h && r_key && op_is_left_mergeable (r_key, Sh->b_size) ) ? IH_SIZE : 0)
+ + (( h ) ? KEY_SIZE : 0))
+ {
+ /* node can not be removed */
+ if (sfree >= levbytes ) { /* new item fits into node S[h] without any shifting */
+ if ( ! h )
+ tb->s0num = B_NR_ITEMS(Sh) + ((mode == M_INSERT ) ? 1 : 0);
+ set_parameters (tb, h, 0, 0, 1, NULL, -1, -1);
+ return NO_BALANCING_NEEDED;
+ }
+ }
+ PROC_INFO_INC( tb -> tb_sb, can_node_be_removed[ h ] );
+ return !NO_BALANCING_NEEDED;
+}
+
+
+
+/* Check whether current node S[h] is balanced when increasing its size by
+ * Inserting or Pasting.
+ * Calculate parameters for balancing for current level h.
+ * Parameters:
+ * tb tree_balance structure;
+ * h current level of the node;
+ * inum item number in S[h];
+ * mode i - insert, p - paste;
+ * Returns: 1 - schedule occurred;
+ * 0 - balancing for higher levels needed;
+ * -1 - no balancing for higher levels needed;
+ * -2 - no disk space.
+ */
+/* ip means Inserting or Pasting */
+static int ip_check_balance (struct tree_balance * tb, int h)
+{
+ struct virtual_node * vn = tb->tb_vn;
+ int levbytes, /* Number of bytes that must be inserted into (value
+ is negative if bytes are deleted) buffer which
+ contains node being balanced. The mnemonic is
+ that the attempted change in node space used level
+ is levbytes bytes. */
+ n_ret_value;
+
+ int lfree, sfree, rfree /* free space in L, S and R */;
+
+ /* nver is short for number of vertixes, and lnver is the number if
+ we shift to the left, rnver is the number if we shift to the
+ right, and lrnver is the number if we shift in both directions.
+ The goal is to minimize first the number of vertixes, and second,
+ the number of vertixes whose contents are changed by shifting,
+ and third the number of uncached vertixes whose contents are
+ changed by shifting and must be read from disk. */
+ int nver, lnver, rnver, lrnver;
+
+ /* used at leaf level only, S0 = S[0] is the node being balanced,
+ sInum [ I = 0,1,2 ] is the number of items that will
+ remain in node SI after balancing. S1 and S2 are new
+ nodes that might be created. */
+
+ /* we perform 8 calls to get_num_ver(). For each call we calculate five parameters.
+ where 4th parameter is s1bytes and 5th - s2bytes
+ */
+ short snum012[40] = {0,}; /* s0num, s1num, s2num for 8 cases
+ 0,1 - do not shift and do not shift but bottle
+ 2 - shift only whole item to left
+ 3 - shift to left and bottle as much as possible
+ 4,5 - shift to right (whole items and as much as possible
+ 6,7 - shift to both directions (whole items and as much as possible)
+ */
+
+ /* Sh is the node whose balance is currently being checked */
+ struct buffer_head * Sh;
+
+ Sh = PATH_H_PBUFFER (tb->tb_path, h);
+ levbytes = tb->insert_size[h];
+
+ /* Calculate balance parameters for creating new root. */
+ if ( ! Sh ) {
+ if ( ! h )
+ reiserfs_panic (tb->tb_sb, "vs-8210: ip_check_balance: S[0] can not be 0");
+ switch ( n_ret_value = get_empty_nodes (tb, h) ) {
+ case CARRY_ON:
+ set_parameters (tb, h, 0, 0, 1, NULL, -1, -1);
+ return NO_BALANCING_NEEDED; /* no balancing for higher levels needed */
+
+ case NO_DISK_SPACE:
+ case REPEAT_SEARCH:
+ return n_ret_value;
+ default:
+ reiserfs_panic(tb->tb_sb, "vs-8215: ip_check_balance: incorrect return value of get_empty_nodes");
+ }
+ }
+
+ if ( (n_ret_value = get_parents (tb, h)) != CARRY_ON ) /* get parents of S[h] neighbors. */
+ return n_ret_value;
+
+ sfree = B_FREE_SPACE (Sh);
+
+ /* get free space of neighbors */
+ rfree = get_rfree (tb, h);
+ lfree = get_lfree (tb, h);
+
+ if (can_node_be_removed (vn->vn_mode, lfree, sfree, rfree, tb, h) == NO_BALANCING_NEEDED)
+ /* and new item fits into node S[h] without any shifting */
+ return NO_BALANCING_NEEDED;
+
+ create_virtual_node (tb, h);
+
+ /*
+ determine maximal number of items we can shift to the left neighbor (in tb structure)
+ and the maximal number of bytes that can flow to the left neighbor
+ from the left most liquid item that cannot be shifted from S[0] entirely (returned value)
+ */
+ check_left (tb, h, lfree);
+
+ /*
+ determine maximal number of items we can shift to the right neighbor (in tb structure)
+ and the maximal number of bytes that can flow to the right neighbor
+ from the right most liquid item that cannot be shifted from S[0] entirely (returned value)
+ */
+ check_right (tb, h, rfree);
+
+
+ /* all contents of internal node S[h] can be moved into its
+ neighbors, S[h] will be removed after balancing */
+ if (h && (tb->rnum[h] + tb->lnum[h] >= vn->vn_nr_item + 1)) {
+ int to_r;
+
+ /* Since we are working on internal nodes, and our internal
+ nodes have fixed size entries, then we can balance by the
+ number of items rather than the space they consume. In this
+ routine we set the left node equal to the right node,
+ allowing a difference of less than or equal to 1 child
+ pointer. */
+ to_r = ((MAX_NR_KEY(Sh)<<1)+2-tb->lnum[h]-tb->rnum[h]+vn->vn_nr_item+1)/2 -
+ (MAX_NR_KEY(Sh) + 1 - tb->rnum[h]);
+ set_parameters (tb, h, vn->vn_nr_item + 1 - to_r, to_r, 0, NULL, -1, -1);
+ return CARRY_ON;
+ }
+
+ /* this checks balance condition, that any two neighboring nodes can not fit in one node */
+ RFALSE( h &&
+ ( tb->lnum[h] >= vn->vn_nr_item + 1 ||
+ tb->rnum[h] >= vn->vn_nr_item + 1),
+ "vs-8220: tree is not balanced on internal level");
+ RFALSE( ! h && ((tb->lnum[h] >= vn->vn_nr_item && (tb->lbytes == -1)) ||
+ (tb->rnum[h] >= vn->vn_nr_item && (tb->rbytes == -1)) ),
+ "vs-8225: tree is not balanced on leaf level");
+
+ /* all contents of S[0] can be moved into its neighbors
+ S[0] will be removed after balancing. */
+ if (!h && is_leaf_removable (tb))
+ return CARRY_ON;
+
+
+ /* why do we perform this check here rather than earlier??
+ Answer: we can win 1 node in some cases above. Moreover we
+ checked it above, when we checked, that S[0] is not removable
+ in principle */
+ if (sfree >= levbytes) { /* new item fits into node S[h] without any shifting */
+ if ( ! h )
+ tb->s0num = vn->vn_nr_item;
+ set_parameters (tb, h, 0, 0, 1, NULL, -1, -1);
+ return NO_BALANCING_NEEDED;
+ }
+
+
+ {
+ int lpar, rpar, nset, lset, rset, lrset;
+ /*
+ * regular overflowing of the node
+ */
+
+ /* get_num_ver works in 2 modes (FLOW & NO_FLOW)
+ lpar, rpar - number of items we can shift to left/right neighbor (including splitting item)
+ nset, lset, rset, lrset - shows, whether flowing items give better packing
+ */
+#define FLOW 1
+#define NO_FLOW 0 /* do not any splitting */
+
+ /* we choose one the following */
+#define NOTHING_SHIFT_NO_FLOW 0
+#define NOTHING_SHIFT_FLOW 5
+#define LEFT_SHIFT_NO_FLOW 10
+#define LEFT_SHIFT_FLOW 15
+#define RIGHT_SHIFT_NO_FLOW 20
+#define RIGHT_SHIFT_FLOW 25
+#define LR_SHIFT_NO_FLOW 30
+#define LR_SHIFT_FLOW 35
+
+
+ lpar = tb->lnum[h];
+ rpar = tb->rnum[h];
+
+
+ /* calculate number of blocks S[h] must be split into when
+ nothing is shifted to the neighbors,
+ as well as number of items in each part of the split node (s012 numbers),
+ and number of bytes (s1bytes) of the shared drop which flow to S1 if any */
+ nset = NOTHING_SHIFT_NO_FLOW;
+ nver = get_num_ver (vn->vn_mode, tb, h,
+ 0, -1, h?vn->vn_nr_item:0, -1,
+ snum012, NO_FLOW);
+
+ if (!h)
+ {
+ int nver1;
+
+ /* note, that in this case we try to bottle between S[0] and S1 (S1 - the first new node) */
+ nver1 = get_num_ver (vn->vn_mode, tb, h,
+ 0, -1, 0, -1,
+ snum012 + NOTHING_SHIFT_FLOW, FLOW);
+ if (nver > nver1)
+ nset = NOTHING_SHIFT_FLOW, nver = nver1;
+ }
+
+
+ /* calculate number of blocks S[h] must be split into when
+ l_shift_num first items and l_shift_bytes of the right most
+ liquid item to be shifted are shifted to the left neighbor,
+ as well as number of items in each part of the splitted node (s012 numbers),
+ and number of bytes (s1bytes) of the shared drop which flow to S1 if any
+ */
+ lset = LEFT_SHIFT_NO_FLOW;
+ lnver = get_num_ver (vn->vn_mode, tb, h,
+ lpar - (( h || tb->lbytes == -1 ) ? 0 : 1), -1, h ? vn->vn_nr_item:0, -1,
+ snum012 + LEFT_SHIFT_NO_FLOW, NO_FLOW);
+ if (!h)
+ {
+ int lnver1;
+
+ lnver1 = get_num_ver (vn->vn_mode, tb, h,
+ lpar - ((tb->lbytes != -1) ? 1 : 0), tb->lbytes, 0, -1,
+ snum012 + LEFT_SHIFT_FLOW, FLOW);
+ if (lnver > lnver1)
+ lset = LEFT_SHIFT_FLOW, lnver = lnver1;
+ }
+
+
+ /* calculate number of blocks S[h] must be split into when
+ r_shift_num first items and r_shift_bytes of the left most
+ liquid item to be shifted are shifted to the right neighbor,
+ as well as number of items in each part of the splitted node (s012 numbers),
+ and number of bytes (s1bytes) of the shared drop which flow to S1 if any
+ */
+ rset = RIGHT_SHIFT_NO_FLOW;
+ rnver = get_num_ver (vn->vn_mode, tb, h,
+ 0, -1, h ? (vn->vn_nr_item-rpar) : (rpar - (( tb->rbytes != -1 ) ? 1 : 0)), -1,
+ snum012 + RIGHT_SHIFT_NO_FLOW, NO_FLOW);
+ if (!h)
+ {
+ int rnver1;
+
+ rnver1 = get_num_ver (vn->vn_mode, tb, h,
+ 0, -1, (rpar - ((tb->rbytes != -1) ? 1 : 0)), tb->rbytes,
+ snum012 + RIGHT_SHIFT_FLOW, FLOW);
+
+ if (rnver > rnver1)
+ rset = RIGHT_SHIFT_FLOW, rnver = rnver1;
+ }
+
+
+ /* calculate number of blocks S[h] must be split into when
+ items are shifted in both directions,
+ as well as number of items in each part of the splitted node (s012 numbers),
+ and number of bytes (s1bytes) of the shared drop which flow to S1 if any
+ */
+ lrset = LR_SHIFT_NO_FLOW;
+ lrnver = get_num_ver (vn->vn_mode, tb, h,
+ lpar - ((h || tb->lbytes == -1) ? 0 : 1), -1, h ? (vn->vn_nr_item-rpar):(rpar - ((tb->rbytes != -1) ? 1 : 0)), -1,
+ snum012 + LR_SHIFT_NO_FLOW, NO_FLOW);
+ if (!h)
+ {
+ int lrnver1;
+
+ lrnver1 = get_num_ver (vn->vn_mode, tb, h,
+ lpar - ((tb->lbytes != -1) ? 1 : 0), tb->lbytes, (rpar - ((tb->rbytes != -1) ? 1 : 0)), tb->rbytes,
+ snum012 + LR_SHIFT_FLOW, FLOW);
+ if (lrnver > lrnver1)
+ lrset = LR_SHIFT_FLOW, lrnver = lrnver1;
+ }
+
+
+
+ /* Our general shifting strategy is:
+ 1) to minimized number of new nodes;
+ 2) to minimized number of neighbors involved in shifting;
+ 3) to minimized number of disk reads; */
+
+ /* we can win TWO or ONE nodes by shifting in both directions */
+ if (lrnver < lnver && lrnver < rnver)
+ {
+ RFALSE( h &&
+ (tb->lnum[h] != 1 ||
+ tb->rnum[h] != 1 ||
+ lrnver != 1 || rnver != 2 || lnver != 2 || h != 1),
+ "vs-8230: bad h");
+ if (lrset == LR_SHIFT_FLOW)
+ set_parameters (tb, h, tb->lnum[h], tb->rnum[h], lrnver, snum012 + lrset,
+ tb->lbytes, tb->rbytes);
+ else
+ set_parameters (tb, h, tb->lnum[h] - ((tb->lbytes == -1) ? 0 : 1),
+ tb->rnum[h] - ((tb->rbytes == -1) ? 0 : 1), lrnver, snum012 + lrset, -1, -1);
+
+ return CARRY_ON;
+ }
+
+ /* if shifting doesn't lead to better packing then don't shift */
+ if (nver == lrnver)
+ {
+ set_parameters (tb, h, 0, 0, nver, snum012 + nset, -1, -1);
+ return CARRY_ON;
+ }
+
+
+ /* now we know that for better packing shifting in only one
+ direction either to the left or to the right is required */
+
+ /* if shifting to the left is better than shifting to the right */
+ if (lnver < rnver)
+ {
+ SET_PAR_SHIFT_LEFT;
+ return CARRY_ON;
+ }
+
+ /* if shifting to the right is better than shifting to the left */
+ if (lnver > rnver)
+ {
+ SET_PAR_SHIFT_RIGHT;
+ return CARRY_ON;
+ }
+
+
+ /* now shifting in either direction gives the same number
+ of nodes and we can make use of the cached neighbors */
+ if (is_left_neighbor_in_cache (tb,h))
+ {
+ SET_PAR_SHIFT_LEFT;
+ return CARRY_ON;
+ }
+
+ /* shift to the right independently on whether the right neighbor in cache or not */
+ SET_PAR_SHIFT_RIGHT;
+ return CARRY_ON;
+ }
+}
+
+
+/* Check whether current node S[h] is balanced when Decreasing its size by
+ * Deleting or Cutting for INTERNAL node of S+tree.
+ * Calculate parameters for balancing for current level h.
+ * Parameters:
+ * tb tree_balance structure;
+ * h current level of the node;
+ * inum item number in S[h];
+ * mode i - insert, p - paste;
+ * Returns: 1 - schedule occurred;
+ * 0 - balancing for higher levels needed;
+ * -1 - no balancing for higher levels needed;
+ * -2 - no disk space.
+ *
+ * Note: Items of internal nodes have fixed size, so the balance condition for
+ * the internal part of S+tree is as for the B-trees.
+ */
+static int dc_check_balance_internal (struct tree_balance * tb, int h)
+{
+ struct virtual_node * vn = tb->tb_vn;
+
+ /* Sh is the node whose balance is currently being checked,
+ and Fh is its father. */
+ struct buffer_head * Sh, * Fh;
+ int maxsize,
+ n_ret_value;
+ int lfree, rfree /* free space in L and R */;
+
+ Sh = PATH_H_PBUFFER (tb->tb_path, h);
+ Fh = PATH_H_PPARENT (tb->tb_path, h);
+
+ maxsize = MAX_CHILD_SIZE(Sh);
+
+/* using tb->insert_size[h], which is negative in this case, create_virtual_node calculates: */
+/* new_nr_item = number of items node would have if operation is */
+/* performed without balancing (new_nr_item); */
+ create_virtual_node (tb, h);
+
+ if ( ! Fh )
+ { /* S[h] is the root. */
+ if ( vn->vn_nr_item > 0 )
+ {
+ set_parameters (tb, h, 0, 0, 1, NULL, -1, -1);
+ return NO_BALANCING_NEEDED; /* no balancing for higher levels needed */
+ }
+ /* new_nr_item == 0.
+ * Current root will be deleted resulting in
+ * decrementing the tree height. */
+ set_parameters (tb, h, 0, 0, 0, NULL, -1, -1);
+ return CARRY_ON;
+ }
+
+ if ( (n_ret_value = get_parents(tb,h)) != CARRY_ON )
+ return n_ret_value;
+
+
+ /* get free space of neighbors */
+ rfree = get_rfree (tb, h);
+ lfree = get_lfree (tb, h);
+
+ /* determine maximal number of items we can fit into neighbors */
+ check_left (tb, h, lfree);
+ check_right (tb, h, rfree);
+
+
+ if ( vn->vn_nr_item >= MIN_NR_KEY(Sh) )
+ { /* Balance condition for the internal node is valid.
+ * In this case we balance only if it leads to better packing. */
+ if ( vn->vn_nr_item == MIN_NR_KEY(Sh) )
+ { /* Here we join S[h] with one of its neighbors,
+ * which is impossible with greater values of new_nr_item. */
+ if ( tb->lnum[h] >= vn->vn_nr_item + 1 )
+ {
+ /* All contents of S[h] can be moved to L[h]. */
+ int n;
+ int order_L;
+
+ order_L = ((n=PATH_H_B_ITEM_ORDER(tb->tb_path, h))==0) ? B_NR_ITEMS(tb->FL[h]) : n - 1;
+ n = dc_size(B_N_CHILD(tb->FL[h],order_L)) / (DC_SIZE + KEY_SIZE);
+ set_parameters (tb, h, -n-1, 0, 0, NULL, -1, -1);
+ return CARRY_ON;
+ }
+
+ if ( tb->rnum[h] >= vn->vn_nr_item + 1 )
+ {
+ /* All contents of S[h] can be moved to R[h]. */
+ int n;
+ int order_R;
+
+ order_R = ((n=PATH_H_B_ITEM_ORDER(tb->tb_path, h))==B_NR_ITEMS(Fh)) ? 0 : n + 1;
+ n = dc_size(B_N_CHILD(tb->FR[h],order_R)) / (DC_SIZE + KEY_SIZE);
+ set_parameters (tb, h, 0, -n-1, 0, NULL, -1, -1);
+ return CARRY_ON;
+ }
+ }
+
+ if (tb->rnum[h] + tb->lnum[h] >= vn->vn_nr_item + 1)
+ {
+ /* All contents of S[h] can be moved to the neighbors (L[h] & R[h]). */
+ int to_r;
+
+ to_r = ((MAX_NR_KEY(Sh)<<1)+2-tb->lnum[h]-tb->rnum[h]+vn->vn_nr_item+1)/2 -
+ (MAX_NR_KEY(Sh) + 1 - tb->rnum[h]);
+ set_parameters (tb, h, vn->vn_nr_item + 1 - to_r, to_r, 0, NULL, -1, -1);
+ return CARRY_ON;
+ }
+
+ /* Balancing does not lead to better packing. */
+ set_parameters (tb, h, 0, 0, 1, NULL, -1, -1);
+ return NO_BALANCING_NEEDED;
+ }
+
+ /* Current node contain insufficient number of items. Balancing is required. */
+ /* Check whether we can merge S[h] with left neighbor. */
+ if (tb->lnum[h] >= vn->vn_nr_item + 1)
+ if (is_left_neighbor_in_cache (tb,h) || tb->rnum[h] < vn->vn_nr_item + 1 || !tb->FR[h])
+ {
+ int n;
+ int order_L;
+
+ order_L = ((n=PATH_H_B_ITEM_ORDER(tb->tb_path, h))==0) ? B_NR_ITEMS(tb->FL[h]) : n - 1;
+ n = dc_size(B_N_CHILD(tb->FL[h],order_L)) / (DC_SIZE + KEY_SIZE);
+ set_parameters (tb, h, -n-1, 0, 0, NULL, -1, -1);
+ return CARRY_ON;
+ }
+
+ /* Check whether we can merge S[h] with right neighbor. */
+ if (tb->rnum[h] >= vn->vn_nr_item + 1)
+ {
+ int n;
+ int order_R;
+
+ order_R = ((n=PATH_H_B_ITEM_ORDER(tb->tb_path, h))==B_NR_ITEMS(Fh)) ? 0 : (n + 1);
+ n = dc_size(B_N_CHILD(tb->FR[h],order_R)) / (DC_SIZE + KEY_SIZE);
+ set_parameters (tb, h, 0, -n-1, 0, NULL, -1, -1);
+ return CARRY_ON;
+ }
+
+ /* All contents of S[h] can be moved to the neighbors (L[h] & R[h]). */
+ if (tb->rnum[h] + tb->lnum[h] >= vn->vn_nr_item + 1)
+ {
+ int to_r;
+
+ to_r = ((MAX_NR_KEY(Sh)<<1)+2-tb->lnum[h]-tb->rnum[h]+vn->vn_nr_item+1)/2 -
+ (MAX_NR_KEY(Sh) + 1 - tb->rnum[h]);
+ set_parameters (tb, h, vn->vn_nr_item + 1 - to_r, to_r, 0, NULL, -1, -1);
+ return CARRY_ON;
+ }
+
+ /* For internal nodes try to borrow item from a neighbor */
+ RFALSE( !tb->FL[h] && !tb->FR[h], "vs-8235: trying to borrow for root");
+
+ /* Borrow one or two items from caching neighbor */
+ if (is_left_neighbor_in_cache (tb,h) || !tb->FR[h])
+ {
+ int from_l;
+
+ from_l = (MAX_NR_KEY(Sh) + 1 - tb->lnum[h] + vn->vn_nr_item + 1) / 2 - (vn->vn_nr_item + 1);
+ set_parameters (tb, h, -from_l, 0, 1, NULL, -1, -1);
+ return CARRY_ON;
+ }
+
+ set_parameters (tb, h, 0, -((MAX_NR_KEY(Sh)+1-tb->rnum[h]+vn->vn_nr_item+1)/2-(vn->vn_nr_item+1)), 1,
+ NULL, -1, -1);
+ return CARRY_ON;
+}
+
+
+/* Check whether current node S[h] is balanced when Decreasing its size by
+ * Deleting or Truncating for LEAF node of S+tree.
+ * Calculate parameters for balancing for current level h.
+ * Parameters:
+ * tb tree_balance structure;
+ * h current level of the node;
+ * inum item number in S[h];
+ * mode i - insert, p - paste;
+ * Returns: 1 - schedule occurred;
+ * 0 - balancing for higher levels needed;
+ * -1 - no balancing for higher levels needed;
+ * -2 - no disk space.
+ */
+static int dc_check_balance_leaf (struct tree_balance * tb, int h)
+{
+ struct virtual_node * vn = tb->tb_vn;
+
+ /* Number of bytes that must be deleted from
+ (value is negative if bytes are deleted) buffer which
+ contains node being balanced. The mnemonic is that the
+ attempted change in node space used level is levbytes bytes. */
+ int levbytes;
+ /* the maximal item size */
+ int maxsize,
+ n_ret_value;
+ /* S0 is the node whose balance is currently being checked,
+ and F0 is its father. */
+ struct buffer_head * S0, * F0;
+ int lfree, rfree /* free space in L and R */;
+
+ S0 = PATH_H_PBUFFER (tb->tb_path, 0);
+ F0 = PATH_H_PPARENT (tb->tb_path, 0);
+
+ levbytes = tb->insert_size[h];
+
+ maxsize = MAX_CHILD_SIZE(S0); /* maximal possible size of an item */
+
+ if ( ! F0 )
+ { /* S[0] is the root now. */
+
+ RFALSE( -levbytes >= maxsize - B_FREE_SPACE (S0),
+ "vs-8240: attempt to create empty buffer tree");
+
+ set_parameters (tb, h, 0, 0, 1, NULL, -1, -1);
+ return NO_BALANCING_NEEDED;
+ }
+
+ if ( (n_ret_value = get_parents(tb,h)) != CARRY_ON )
+ return n_ret_value;
+
+ /* get free space of neighbors */
+ rfree = get_rfree (tb, h);
+ lfree = get_lfree (tb, h);
+
+ create_virtual_node (tb, h);
+
+ /* if 3 leaves can be merge to one, set parameters and return */
+ if (are_leaves_removable (tb, lfree, rfree))
+ return CARRY_ON;
+
+ /* determine maximal number of items we can shift to the left/right neighbor
+ and the maximal number of bytes that can flow to the left/right neighbor
+ from the left/right most liquid item that cannot be shifted from S[0] entirely
+ */
+ check_left (tb, h, lfree);
+ check_right (tb, h, rfree);
+
+ /* check whether we can merge S with left neighbor. */
+ if (tb->lnum[0] >= vn->vn_nr_item && tb->lbytes == -1)
+ if (is_left_neighbor_in_cache (tb,h) ||
+ ((tb->rnum[0] - ((tb->rbytes == -1) ? 0 : 1)) < vn->vn_nr_item) || /* S can not be merged with R */
+ !tb->FR[h]) {
+
+ RFALSE( !tb->FL[h], "vs-8245: dc_check_balance_leaf: FL[h] must exist");
+
+ /* set parameter to merge S[0] with its left neighbor */
+ set_parameters (tb, h, -1, 0, 0, NULL, -1, -1);
+ return CARRY_ON;
+ }
+
+ /* check whether we can merge S[0] with right neighbor. */
+ if (tb->rnum[0] >= vn->vn_nr_item && tb->rbytes == -1) {
+ set_parameters (tb, h, 0, -1, 0, NULL, -1, -1);
+ return CARRY_ON;
+ }
+
+ /* All contents of S[0] can be moved to the neighbors (L[0] & R[0]). Set parameters and return */
+ if (is_leaf_removable (tb))
+ return CARRY_ON;
+
+ /* Balancing is not required. */
+ tb->s0num = vn->vn_nr_item;
+ set_parameters (tb, h, 0, 0, 1, NULL, -1, -1);
+ return NO_BALANCING_NEEDED;
+}
+
+
+
+/* Check whether current node S[h] is balanced when Decreasing its size by
+ * Deleting or Cutting.
+ * Calculate parameters for balancing for current level h.
+ * Parameters:
+ * tb tree_balance structure;
+ * h current level of the node;
+ * inum item number in S[h];
+ * mode d - delete, c - cut.
+ * Returns: 1 - schedule occurred;
+ * 0 - balancing for higher levels needed;
+ * -1 - no balancing for higher levels needed;
+ * -2 - no disk space.
+ */
+static int dc_check_balance (struct tree_balance * tb, int h)
+{
+ RFALSE( ! (PATH_H_PBUFFER (tb->tb_path, h)), "vs-8250: S is not initialized");
+
+ if ( h )
+ return dc_check_balance_internal (tb, h);
+ else
+ return dc_check_balance_leaf (tb, h);
+}
+
+
+
+/* Check whether current node S[h] is balanced.
+ * Calculate parameters for balancing for current level h.
+ * Parameters:
+ *
+ * tb tree_balance structure:
+ *
+ * tb is a large structure that must be read about in the header file
+ * at the same time as this procedure if the reader is to successfully
+ * understand this procedure
+ *
+ * h current level of the node;
+ * inum item number in S[h];
+ * mode i - insert, p - paste, d - delete, c - cut.
+ * Returns: 1 - schedule occurred;
+ * 0 - balancing for higher levels needed;
+ * -1 - no balancing for higher levels needed;
+ * -2 - no disk space.
+ */
+static int check_balance (int mode,
+ struct tree_balance * tb,
+ int h,
+ int inum,
+ int pos_in_item,
+ struct item_head * ins_ih,
+ const void * data
+ )
+{
+ struct virtual_node * vn;
+
+ vn = tb->tb_vn = (struct virtual_node *)(tb->vn_buf);
+ vn->vn_free_ptr = (char *)(tb->tb_vn + 1);
+ vn->vn_mode = mode;
+ vn->vn_affected_item_num = inum;
+ vn->vn_pos_in_item = pos_in_item;
+ vn->vn_ins_ih = ins_ih;
+ vn->vn_data = data;
+
+ RFALSE( mode == M_INSERT && !vn->vn_ins_ih,
+ "vs-8255: ins_ih can not be 0 in insert mode");
+
+ if ( tb->insert_size[h] > 0 )
+ /* Calculate balance parameters when size of node is increasing. */
+ return ip_check_balance (tb, h);
+
+ /* Calculate balance parameters when size of node is decreasing. */
+ return dc_check_balance (tb, h);
+}
+
+
+
+/* Check whether parent at the path is the really parent of the current node.*/
+static int get_direct_parent(
+ struct tree_balance * p_s_tb,
+ int n_h
+ ) {
+ struct buffer_head * p_s_bh;
+ struct path * p_s_path = p_s_tb->tb_path;
+ int n_position,
+ n_path_offset = PATH_H_PATH_OFFSET(p_s_tb->tb_path, n_h);
+
+ /* We are in the root or in the new root. */
+ if ( n_path_offset <= FIRST_PATH_ELEMENT_OFFSET ) {
+
+ RFALSE( n_path_offset < FIRST_PATH_ELEMENT_OFFSET - 1,
+ "PAP-8260: illegal offset in the path");
+
+ if ( PATH_OFFSET_PBUFFER(p_s_path, FIRST_PATH_ELEMENT_OFFSET)->b_blocknr ==
+ SB_ROOT_BLOCK (p_s_tb->tb_sb) ) {
+ /* Root is not changed. */
+ PATH_OFFSET_PBUFFER(p_s_path, n_path_offset - 1) = NULL;
+ PATH_OFFSET_POSITION(p_s_path, n_path_offset - 1) = 0;
+ return CARRY_ON;
+ }
+ return REPEAT_SEARCH; /* Root is changed and we must recalculate the path. */
+ }
+
+ if ( ! B_IS_IN_TREE(p_s_bh = PATH_OFFSET_PBUFFER(p_s_path, n_path_offset - 1)) )
+ return REPEAT_SEARCH; /* Parent in the path is not in the tree. */
+
+ if ( (n_position = PATH_OFFSET_POSITION(p_s_path, n_path_offset - 1)) > B_NR_ITEMS(p_s_bh) )
+ return REPEAT_SEARCH;
+
+ if ( B_N_CHILD_NUM(p_s_bh, n_position) != PATH_OFFSET_PBUFFER(p_s_path, n_path_offset)->b_blocknr )
+ /* Parent in the path is not parent of the current node in the tree. */
+ return REPEAT_SEARCH;
+
+ if ( buffer_locked(p_s_bh) ) {
+ __wait_on_buffer(p_s_bh);
+ if ( FILESYSTEM_CHANGED_TB (p_s_tb) )
+ return REPEAT_SEARCH;
+ }
+
+ return CARRY_ON; /* Parent in the path is unlocked and really parent of the current node. */
+}
+
+
+/* Using lnum[n_h] and rnum[n_h] we should determine what neighbors
+ * of S[n_h] we
+ * need in order to balance S[n_h], and get them if necessary.
+ * Returns: SCHEDULE_OCCURRED - schedule occurred while the function worked;
+ * CARRY_ON - schedule didn't occur while the function worked;
+ */
+static int get_neighbors(
+ struct tree_balance * p_s_tb,
+ int n_h
+ ) {
+ int n_child_position,
+ n_path_offset = PATH_H_PATH_OFFSET(p_s_tb->tb_path, n_h + 1);
+ unsigned long n_son_number;
+ struct super_block * p_s_sb = p_s_tb->tb_sb;
+ struct buffer_head * p_s_bh;
+
+
+ PROC_INFO_INC( p_s_sb, get_neighbors[ n_h ] );
+
+ if ( p_s_tb->lnum[n_h] ) {
+ /* We need left neighbor to balance S[n_h]. */
+ PROC_INFO_INC( p_s_sb, need_l_neighbor[ n_h ] );
+ p_s_bh = PATH_OFFSET_PBUFFER(p_s_tb->tb_path, n_path_offset);
+
+ RFALSE( p_s_bh == p_s_tb->FL[n_h] &&
+ ! PATH_OFFSET_POSITION(p_s_tb->tb_path, n_path_offset),
+ "PAP-8270: invalid position in the parent");
+
+ n_child_position = ( p_s_bh == p_s_tb->FL[n_h] ) ? p_s_tb->lkey[n_h] : B_NR_ITEMS (p_s_tb->FL[n_h]);
+ n_son_number = B_N_CHILD_NUM(p_s_tb->FL[n_h], n_child_position);
+ p_s_bh = reiserfs_bread(p_s_sb, n_son_number, p_s_sb->s_blocksize);
+ if (!p_s_bh)
+ return IO_ERROR;
+ if ( FILESYSTEM_CHANGED_TB (p_s_tb) ) {
+ decrement_bcount(p_s_bh);
+ PROC_INFO_INC( p_s_sb, get_neighbors_restart[ n_h ] );
+ return REPEAT_SEARCH;
+ }
+
+ RFALSE( ! B_IS_IN_TREE(p_s_tb->FL[n_h]) ||
+ n_child_position > B_NR_ITEMS(p_s_tb->FL[n_h]) ||
+ B_N_CHILD_NUM(p_s_tb->FL[n_h], n_child_position) !=
+ p_s_bh->b_blocknr, "PAP-8275: invalid parent");
+ RFALSE( ! B_IS_IN_TREE(p_s_bh), "PAP-8280: invalid child");
+ RFALSE( ! n_h &&
+ B_FREE_SPACE (p_s_bh) != MAX_CHILD_SIZE (p_s_bh) - dc_size(B_N_CHILD (p_s_tb->FL[0],n_child_position)),
+ "PAP-8290: invalid child size of left neighbor");
+
+ decrement_bcount(p_s_tb->L[n_h]);
+ p_s_tb->L[n_h] = p_s_bh;
+ }
+
+
+ if ( p_s_tb->rnum[n_h] ) { /* We need right neighbor to balance S[n_path_offset]. */
+ PROC_INFO_INC( p_s_sb, need_r_neighbor[ n_h ] );
+ p_s_bh = PATH_OFFSET_PBUFFER(p_s_tb->tb_path, n_path_offset);
+
+ RFALSE( p_s_bh == p_s_tb->FR[n_h] &&
+ PATH_OFFSET_POSITION(p_s_tb->tb_path, n_path_offset) >= B_NR_ITEMS(p_s_bh),
+ "PAP-8295: invalid position in the parent");
+
+ n_child_position = ( p_s_bh == p_s_tb->FR[n_h] ) ? p_s_tb->rkey[n_h] + 1 : 0;
+ n_son_number = B_N_CHILD_NUM(p_s_tb->FR[n_h], n_child_position);
+ p_s_bh = reiserfs_bread(p_s_sb, n_son_number, p_s_sb->s_blocksize);
+ if (!p_s_bh)
+ return IO_ERROR;
+ if ( FILESYSTEM_CHANGED_TB (p_s_tb) ) {
+ decrement_bcount(p_s_bh);
+ PROC_INFO_INC( p_s_sb, get_neighbors_restart[ n_h ] );
+ return REPEAT_SEARCH;
+ }
+ decrement_bcount(p_s_tb->R[n_h]);
+ p_s_tb->R[n_h] = p_s_bh;
+
+ RFALSE( ! n_h && B_FREE_SPACE (p_s_bh) != MAX_CHILD_SIZE (p_s_bh) - dc_size(B_N_CHILD (p_s_tb->FR[0],n_child_position)),
+ "PAP-8300: invalid child size of right neighbor (%d != %d - %d)",
+ B_FREE_SPACE (p_s_bh), MAX_CHILD_SIZE (p_s_bh),
+ dc_size(B_N_CHILD (p_s_tb->FR[0],n_child_position)));
+
+ }
+ return CARRY_ON;
+}
+
+#ifdef CONFIG_REISERFS_CHECK
+void * reiserfs_kmalloc (size_t size, int flags, struct super_block * s)
+{
+ void * vp;
+ static size_t malloced;
+
+
+ vp = kmalloc (size, flags);
+ if (vp) {
+ s->u.reiserfs_sb.s_kmallocs += size;
+ if (s->u.reiserfs_sb.s_kmallocs > malloced + 200000) {
+ reiserfs_warning (s, "vs-8301: reiserfs_kmalloc: allocated memory %d\n", s->u.reiserfs_sb.s_kmallocs);
+ malloced = s->u.reiserfs_sb.s_kmallocs;
+ }
+ }
+/*printk ("malloc : size %d, allocated %d\n", size, s->u.reiserfs_sb.s_kmallocs);*/
+ return vp;
+}
+
+void reiserfs_kfree (const void * vp, size_t size, struct super_block * s)
+{
+ if (!vp)
+ return;
+ kfree (vp);
+
+ s->u.reiserfs_sb.s_kmallocs -= size;
+ if (s->u.reiserfs_sb.s_kmallocs < 0)
+ reiserfs_warning (s, "vs-8302: reiserfs_kfree: allocated memory %d\n", s->u.reiserfs_sb.s_kmallocs);
+
+}
+#endif
+
+
+static int get_virtual_node_size (struct super_block * sb, struct buffer_head * bh)
+{
+ int max_num_of_items;
+ int max_num_of_entries;
+ unsigned long blocksize = sb->s_blocksize;
+
+#define MIN_NAME_LEN 1
+
+ max_num_of_items = (blocksize - BLKH_SIZE) / (IH_SIZE + MIN_ITEM_LEN);
+ max_num_of_entries = (blocksize - BLKH_SIZE - IH_SIZE) /
+ (DEH_SIZE + MIN_NAME_LEN);
+
+ return sizeof(struct virtual_node) +
+ max(max_num_of_items * sizeof (struct virtual_item),
+ sizeof (struct virtual_item) + sizeof(struct direntry_uarea) +
+ (max_num_of_entries - 1) * sizeof (__u16));
+}
+
+
+
+/* maybe we should fail balancing we are going to perform when kmalloc
+ fails several times. But now it will loop until kmalloc gets
+ required memory */
+static int get_mem_for_virtual_node (struct tree_balance * tb)
+{
+ int check_fs = 0;
+ int size;
+ char * buf;
+
+ size = get_virtual_node_size (tb->tb_sb, PATH_PLAST_BUFFER (tb->tb_path));
+
+ if (size > tb->vn_buf_size) {
+ /* we have to allocate more memory for virtual node */
+ if (tb->vn_buf) {
+ /* free memory allocated before */
+ reiserfs_kfree (tb->vn_buf, tb->vn_buf_size, tb->tb_sb);
+ /* this is not needed if kfree is atomic */
+ check_fs = 1;
+ }
+
+ /* virtual node requires now more memory */
+ tb->vn_buf_size = size;
+
+ /* get memory for virtual item */
+ buf = reiserfs_kmalloc(size, GFP_ATOMIC, tb->tb_sb);
+ if ( ! buf ) {
+ /* getting memory with GFP_KERNEL priority may involve
+ balancing now (due to indirect_to_direct conversion on
+ dcache shrinking). So, release path and collected
+ resources here */
+ free_buffers_in_tb (tb);
+ buf = reiserfs_kmalloc(size, GFP_NOFS, tb->tb_sb);
+ if ( !buf ) {
+#ifdef CONFIG_REISERFS_CHECK
+ reiserfs_warning (tb->tb_sb, "vs-8345: get_mem_for_virtual_node: "
+ "kmalloc failed. reiserfs kmalloced %d bytes\n",
+ tb->tb_sb->u.reiserfs_sb.s_kmallocs);
+#endif
+ tb->vn_buf_size = 0;
+ }
+ tb->vn_buf = buf;
+ schedule() ;
+ return REPEAT_SEARCH;
+ }
+
+ tb->vn_buf = buf;
+ }
+
+ if ( check_fs && FILESYSTEM_CHANGED_TB (tb) )
+ return REPEAT_SEARCH;
+
+ return CARRY_ON;
+}
+
+
+#ifdef CONFIG_REISERFS_CHECK
+static void tb_buffer_sanity_check (struct super_block * p_s_sb,
+ struct buffer_head * p_s_bh,
+ const char *descr, int level) {
+ if (p_s_bh) {
+ if (atomic_read (&(p_s_bh->b_count)) <= 0) {
+
+ reiserfs_panic (p_s_sb, "tb_buffer_sanity_check(): negative or zero reference counter for buffer %s[%d] (%b)\n", descr, level, p_s_bh);
+ }
+
+ if ( ! buffer_uptodate (p_s_bh) ) {
+ reiserfs_panic (p_s_sb, "tb_buffer_sanity_check(): buffer is not up to date %s[%d] (%b)\n", descr, level, p_s_bh);
+ }
+
+ if ( ! B_IS_IN_TREE (p_s_bh) ) {
+ reiserfs_panic (p_s_sb, "tb_buffer_sanity_check(): buffer is not in tree %s[%d] (%b)\n", descr, level, p_s_bh);
+ }
+
+ if (p_s_bh->b_dev != p_s_sb->s_dev ||
+ p_s_bh->b_size != p_s_sb->s_blocksize ||
+ p_s_bh->b_blocknr > SB_BLOCK_COUNT(p_s_sb)) {
+ reiserfs_panic (p_s_sb, "tb_buffer_sanity_check(): check failed for buffer %s[%d] (%b)\n", descr, level, p_s_bh);
+ }
+ }
+}
+#else
+static void tb_buffer_sanity_check (struct super_block * p_s_sb,
+ struct buffer_head * p_s_bh,
+ const char *descr, int level)
+{;}
+#endif
+
+static void clear_all_dirty_bits(struct super_block *s,
+ struct buffer_head *bh) {
+ reiserfs_prepare_for_journal(s, bh, 0) ;
+}
+
+static int wait_tb_buffers_until_unlocked (struct tree_balance * p_s_tb)
+{
+ struct buffer_head * locked;
+#ifdef CONFIG_REISERFS_CHECK
+ int repeat_counter = 0;
+#endif
+ int i;
+
+ do {
+
+ locked = NULL;
+
+ for ( i = p_s_tb->tb_path->path_length; !locked && i > ILLEGAL_PATH_ELEMENT_OFFSET; i-- ) {
+ if ( PATH_OFFSET_PBUFFER (p_s_tb->tb_path, i) ) {
+ /* if I understand correctly, we can only be sure the last buffer
+ ** in the path is in the tree --clm
+ */
+#ifdef CONFIG_REISERFS_CHECK
+ if (PATH_PLAST_BUFFER(p_s_tb->tb_path) ==
+ PATH_OFFSET_PBUFFER(p_s_tb->tb_path, i)) {
+ tb_buffer_sanity_check (p_s_tb->tb_sb,
+ PATH_OFFSET_PBUFFER (p_s_tb->tb_path, i),
+ "S",
+ p_s_tb->tb_path->path_length - i);
+ }
+#endif
+ clear_all_dirty_bits(p_s_tb->tb_sb,
+ PATH_OFFSET_PBUFFER (p_s_tb->tb_path, i)) ;
+
+ if ( buffer_locked (PATH_OFFSET_PBUFFER (p_s_tb->tb_path, i)) )
+ locked = PATH_OFFSET_PBUFFER (p_s_tb->tb_path, i);
+ }
+ }
+
+ for ( i = 0; !locked && i < MAX_HEIGHT && p_s_tb->insert_size[i]; i++ ) {
+
+ if (p_s_tb->lnum[i] ) {
+
+ if ( p_s_tb->L[i] ) {
+ tb_buffer_sanity_check (p_s_tb->tb_sb, p_s_tb->L[i], "L", i);
+ clear_all_dirty_bits(p_s_tb->tb_sb, p_s_tb->L[i]) ;
+ if ( buffer_locked (p_s_tb->L[i]) )
+ locked = p_s_tb->L[i];
+ }
+
+ if ( !locked && p_s_tb->FL[i] ) {
+ tb_buffer_sanity_check (p_s_tb->tb_sb, p_s_tb->FL[i], "FL", i);
+ clear_all_dirty_bits(p_s_tb->tb_sb, p_s_tb->FL[i]) ;
+ if ( buffer_locked (p_s_tb->FL[i]) )
+ locked = p_s_tb->FL[i];
+ }
+
+ if ( !locked && p_s_tb->CFL[i] ) {
+ tb_buffer_sanity_check (p_s_tb->tb_sb, p_s_tb->CFL[i], "CFL", i);
+ clear_all_dirty_bits(p_s_tb->tb_sb, p_s_tb->CFL[i]) ;
+ if ( buffer_locked (p_s_tb->CFL[i]) )
+ locked = p_s_tb->CFL[i];
+ }
+
+ }
+
+ if ( !locked && (p_s_tb->rnum[i]) ) {
+
+ if ( p_s_tb->R[i] ) {
+ tb_buffer_sanity_check (p_s_tb->tb_sb, p_s_tb->R[i], "R", i);
+ clear_all_dirty_bits(p_s_tb->tb_sb, p_s_tb->R[i]) ;
+ if ( buffer_locked (p_s_tb->R[i]) )
+ locked = p_s_tb->R[i];
+ }
+
+
+ if ( !locked && p_s_tb->FR[i] ) {
+ tb_buffer_sanity_check (p_s_tb->tb_sb, p_s_tb->FR[i], "FR", i);
+ clear_all_dirty_bits(p_s_tb->tb_sb, p_s_tb->FR[i]) ;
+ if ( buffer_locked (p_s_tb->FR[i]) )
+ locked = p_s_tb->FR[i];
+ }
+
+ if ( !locked && p_s_tb->CFR[i] ) {
+ tb_buffer_sanity_check (p_s_tb->tb_sb, p_s_tb->CFR[i], "CFR", i);
+ clear_all_dirty_bits(p_s_tb->tb_sb, p_s_tb->CFR[i]) ;
+ if ( buffer_locked (p_s_tb->CFR[i]) )
+ locked = p_s_tb->CFR[i];
+ }
+ }
+ }
+ /* as far as I can tell, this is not required. The FEB list seems
+ ** to be full of newly allocated nodes, which will never be locked,
+ ** dirty, or anything else.
+ ** To be safe, I'm putting in the checks and waits in. For the moment,
+ ** they are needed to keep the code in journal.c from complaining
+ ** about the buffer. That code is inside CONFIG_REISERFS_CHECK as well.
+ ** --clm
+ */
+ for ( i = 0; !locked && i < MAX_FEB_SIZE; i++ ) {
+ if ( p_s_tb->FEB[i] ) {
+ clear_all_dirty_bits(p_s_tb->tb_sb, p_s_tb->FEB[i]) ;
+ if (buffer_locked(p_s_tb->FEB[i])) {
+ locked = p_s_tb->FEB[i] ;
+ }
+ }
+ }
+
+ if (locked) {
+#ifdef CONFIG_REISERFS_CHECK
+ repeat_counter++;
+ if ( (repeat_counter % 10000) == 0) {
+ reiserfs_warning (p_s_tb->tb_sb, "wait_tb_buffers_until_released(): too many iterations waiting for buffer to unlock (%b)\n", locked);
+
+ /* Don't loop forever. Try to recover from possible error. */
+
+ return ( FILESYSTEM_CHANGED_TB (p_s_tb) ) ? REPEAT_SEARCH : CARRY_ON;
+ }
+#endif
+ __wait_on_buffer (locked);
+ if ( FILESYSTEM_CHANGED_TB (p_s_tb) ) {
+ return REPEAT_SEARCH;
+ }
+ }
+
+ } while (locked);
+
+ return CARRY_ON;
+}
+
+
+/* Prepare for balancing, that is
+ * get all necessary parents, and neighbors;
+ * analyze what and where should be moved;
+ * get sufficient number of new nodes;
+ * Balancing will start only after all resources will be collected at a time.
+ *
+ * When ported to SMP kernels, only at the last moment after all needed nodes
+ * are collected in cache, will the resources be locked using the usual
+ * textbook ordered lock acquisition algorithms. Note that ensuring that
+ * this code neither write locks what it does not need to write lock nor locks out of order
+ * will be a pain in the butt that could have been avoided. Grumble grumble. -Hans
+ *
+ * fix is meant in the sense of render unchanging
+ *
+ * Latency might be improved by first gathering a list of what buffers are needed
+ * and then getting as many of them in parallel as possible? -Hans
+ *
+ * Parameters:
+ * op_mode i - insert, d - delete, c - cut (truncate), p - paste (append)
+ * tb tree_balance structure;
+ * inum item number in S[h];
+ * pos_in_item - comment this if you can
+ * ins_ih & ins_sd are used when inserting
+ * Returns: 1 - schedule occurred while the function worked;
+ * 0 - schedule didn't occur while the function worked;
+ * -1 - if no_disk_space
+ */
+
+
+int fix_nodes (int n_op_mode,
+ struct tree_balance * p_s_tb,
+ struct item_head * p_s_ins_ih, // item head of item being inserted
+ const void * data // inserted item or data to be pasted
+ ) {
+ int n_ret_value,
+ n_h,
+ n_item_num = PATH_LAST_POSITION(p_s_tb->tb_path);
+ int n_pos_in_item;
+
+ /* we set wait_tb_buffers_run when we have to restore any dirty bits cleared
+ ** during wait_tb_buffers_run
+ */
+ int wait_tb_buffers_run = 0 ;
+ int windex ;
+ struct buffer_head * p_s_tbS0 = PATH_PLAST_BUFFER(p_s_tb->tb_path);
+
+ ++ p_s_tb -> tb_sb -> u.reiserfs_sb.s_fix_nodes;
+
+ n_pos_in_item = p_s_tb->tb_path->pos_in_item;
+
+
+ p_s_tb->fs_gen = get_generation (p_s_tb->tb_sb);
+
+ /* we prepare and log the super here so it will already be in the
+ ** transaction when do_balance needs to change it.
+ ** This way do_balance won't have to schedule when trying to prepare
+ ** the super for logging
+ */
+ reiserfs_prepare_for_journal(p_s_tb->tb_sb,
+ SB_BUFFER_WITH_SB(p_s_tb->tb_sb), 1) ;
+ journal_mark_dirty(p_s_tb->transaction_handle, p_s_tb->tb_sb,
+ SB_BUFFER_WITH_SB(p_s_tb->tb_sb)) ;
+ if ( FILESYSTEM_CHANGED_TB (p_s_tb) )
+ return REPEAT_SEARCH;
+
+ /* if it possible in indirect_to_direct conversion */
+ if (buffer_locked (p_s_tbS0)) {
+ __wait_on_buffer (p_s_tbS0);
+ if ( FILESYSTEM_CHANGED_TB (p_s_tb) )
+ return REPEAT_SEARCH;
+ }
+
+#ifdef CONFIG_REISERFS_CHECK
+ if ( cur_tb ) {
+ print_cur_tb ("fix_nodes");
+ reiserfs_panic(p_s_tb->tb_sb,"PAP-8305: fix_nodes: there is pending do_balance");
+ }
+
+ if (!buffer_uptodate (p_s_tbS0) || !B_IS_IN_TREE (p_s_tbS0)) {
+ reiserfs_panic (p_s_tb->tb_sb, "PAP-8320: fix_nodes: S[0] (%b %z) is not uptodate "
+ "at the beginning of fix_nodes or not in tree (mode %c)", p_s_tbS0, p_s_tbS0, n_op_mode);
+ }
+
+ /* Check parameters. */
+ switch (n_op_mode) {
+ case M_INSERT:
+ if ( n_item_num <= 0 || n_item_num > B_NR_ITEMS(p_s_tbS0) )
+ reiserfs_panic(p_s_tb->tb_sb,"PAP-8330: fix_nodes: Incorrect item number %d (in S0 - %d) in case of insert",
+ n_item_num, B_NR_ITEMS(p_s_tbS0));
+ break;
+ case M_PASTE:
+ case M_DELETE:
+ case M_CUT:
+ if ( n_item_num < 0 || n_item_num >= B_NR_ITEMS(p_s_tbS0) ) {
+ print_block (p_s_tbS0, 0, -1, -1);
+ printk("mode = %c insert_size = %d\n", n_op_mode, p_s_tb->insert_size[0]);
+ reiserfs_panic(p_s_tb->tb_sb,"PAP-8335: fix_nodes: Incorrect item number(%d)", n_item_num);
+ }
+ break;
+ default:
+ reiserfs_panic(p_s_tb->tb_sb,"PAP-8340: fix_nodes: Incorrect mode of operation");
+ }
+#endif
+
+ if (get_mem_for_virtual_node (p_s_tb) == REPEAT_SEARCH)
+ // FIXME: maybe -ENOMEM when tb->vn_buf == 0? Now just repeat
+ return REPEAT_SEARCH;
+
+
+ /* Starting from the leaf level; for all levels n_h of the tree. */
+ for ( n_h = 0; n_h < MAX_HEIGHT && p_s_tb->insert_size[n_h]; n_h++ ) {
+ if ( (n_ret_value = get_direct_parent(p_s_tb, n_h)) != CARRY_ON ) {
+ goto repeat;
+ }
+
+ if ( (n_ret_value = check_balance (n_op_mode, p_s_tb, n_h, n_item_num,
+ n_pos_in_item, p_s_ins_ih, data)) != CARRY_ON ) {
+ if ( n_ret_value == NO_BALANCING_NEEDED ) {
+ /* No balancing for higher levels needed. */
+ if ( (n_ret_value = get_neighbors(p_s_tb, n_h)) != CARRY_ON ) {
+ goto repeat;
+ }
+ if ( n_h != MAX_HEIGHT - 1 )
+ p_s_tb->insert_size[n_h + 1] = 0;
+ /* ok, analysis and resource gathering are complete */
+ break;
+ }
+ goto repeat;
+ }
+
+ if ( (n_ret_value = get_neighbors(p_s_tb, n_h)) != CARRY_ON ) {
+ goto repeat;
+ }
+
+ if ( (n_ret_value = get_empty_nodes(p_s_tb, n_h)) != CARRY_ON ) {
+ goto repeat; /* No disk space, or schedule occurred and
+ analysis may be invalid and needs to be redone. */
+ }
+
+ if ( ! PATH_H_PBUFFER(p_s_tb->tb_path, n_h) ) {
+ /* We have a positive insert size but no nodes exist on this
+ level, this means that we are creating a new root. */
+
+ RFALSE( p_s_tb->blknum[n_h] != 1,
+ "PAP-8350: creating new empty root");
+
+ if ( n_h < MAX_HEIGHT - 1 )
+ p_s_tb->insert_size[n_h + 1] = 0;
+ }
+ else
+ if ( ! PATH_H_PBUFFER(p_s_tb->tb_path, n_h + 1) ) {
+ if ( p_s_tb->blknum[n_h] > 1 ) {
+ /* The tree needs to be grown, so this node S[n_h]
+ which is the root node is split into two nodes,
+ and a new node (S[n_h+1]) will be created to
+ become the root node. */
+
+ RFALSE( n_h == MAX_HEIGHT - 1,
+ "PAP-8355: attempt to create too high of a tree");
+
+ p_s_tb->insert_size[n_h + 1] = (DC_SIZE + KEY_SIZE) * (p_s_tb->blknum[n_h] - 1) + DC_SIZE;
+ }
+ else
+ if ( n_h < MAX_HEIGHT - 1 )
+ p_s_tb->insert_size[n_h + 1] = 0;
+ }
+ else
+ p_s_tb->insert_size[n_h + 1] = (DC_SIZE + KEY_SIZE) * (p_s_tb->blknum[n_h] - 1);
+ }
+
+
+ windex = push_journal_writer("fix_nodes") ;
+ if ((n_ret_value = wait_tb_buffers_until_unlocked (p_s_tb)) == CARRY_ON) {
+ pop_journal_writer(windex) ;
+ if (FILESYSTEM_CHANGED_TB(p_s_tb)) {
+ wait_tb_buffers_run = 1 ;
+ n_ret_value = REPEAT_SEARCH ;
+ goto repeat;
+ } else {
+ return CARRY_ON;
+ }
+ } else {
+ wait_tb_buffers_run = 1 ;
+ pop_journal_writer(windex) ;
+ goto repeat;
+ }
+
+ repeat:
+ // fix_nodes was unable to perform its calculation due to
+ // filesystem got changed under us, lack of free disk space or i/o
+ // failure. If the first is the case - the search will be
+ // repeated. For now - free all resources acquired so far except
+ // for the new allocated nodes
+ {
+ int i;
+
+ /* Release path buffers. */
+ if (wait_tb_buffers_run) {
+ pathrelse_and_restore(p_s_tb->tb_sb, p_s_tb->tb_path) ;
+ } else {
+ pathrelse (p_s_tb->tb_path);
+ }
+ /* brelse all resources collected for balancing */
+ for ( i = 0; i < MAX_HEIGHT; i++ ) {
+ if (wait_tb_buffers_run) {
+ reiserfs_restore_prepared_buffer(p_s_tb->tb_sb, p_s_tb->L[i]);
+ reiserfs_restore_prepared_buffer(p_s_tb->tb_sb, p_s_tb->R[i]);
+ reiserfs_restore_prepared_buffer(p_s_tb->tb_sb, p_s_tb->FL[i]);
+ reiserfs_restore_prepared_buffer(p_s_tb->tb_sb, p_s_tb->FR[i]);
+ reiserfs_restore_prepared_buffer(p_s_tb->tb_sb, p_s_tb->CFL[i]);
+ reiserfs_restore_prepared_buffer(p_s_tb->tb_sb, p_s_tb->CFR[i]);
+ }
+
+ brelse (p_s_tb->L[i]);p_s_tb->L[i] = 0;
+ brelse (p_s_tb->R[i]);p_s_tb->R[i] = 0;
+ brelse (p_s_tb->FL[i]);p_s_tb->FL[i] = 0;
+ brelse (p_s_tb->FR[i]);p_s_tb->FR[i] = 0;
+ brelse (p_s_tb->CFL[i]);p_s_tb->CFL[i] = 0;
+ brelse (p_s_tb->CFR[i]);p_s_tb->CFR[i] = 0;
+ }
+
+ if (wait_tb_buffers_run) {
+ for ( i = 0; i < MAX_FEB_SIZE; i++ ) {
+ if ( p_s_tb->FEB[i] ) {
+ reiserfs_restore_prepared_buffer(p_s_tb->tb_sb,
+ p_s_tb->FEB[i]) ;
+ }
+ }
+ }
+ return n_ret_value;
+ }
+
+}
+
+
+/* Anatoly will probably forgive me renaming p_s_tb to tb. I just
+ wanted to make lines shorter */
+void unfix_nodes (struct tree_balance * tb)
+{
+ int i;
+
+ /* Release path buffers. */
+ pathrelse_and_restore (tb->tb_sb, tb->tb_path);
+
+ /* brelse all resources collected for balancing */
+ for ( i = 0; i < MAX_HEIGHT; i++ ) {
+ reiserfs_restore_prepared_buffer (tb->tb_sb, tb->L[i]);
+ reiserfs_restore_prepared_buffer (tb->tb_sb, tb->R[i]);
+ reiserfs_restore_prepared_buffer (tb->tb_sb, tb->FL[i]);
+ reiserfs_restore_prepared_buffer (tb->tb_sb, tb->FR[i]);
+ reiserfs_restore_prepared_buffer (tb->tb_sb, tb->CFL[i]);
+ reiserfs_restore_prepared_buffer (tb->tb_sb, tb->CFR[i]);
+
+ brelse (tb->L[i]);
+ brelse (tb->R[i]);
+ brelse (tb->FL[i]);
+ brelse (tb->FR[i]);
+ brelse (tb->CFL[i]);
+ brelse (tb->CFR[i]);
+ }
+
+ /* deal with list of allocated (used and unused) nodes */
+ for ( i = 0; i < MAX_FEB_SIZE; i++ ) {
+ if ( tb->FEB[i] ) {
+ unsigned long blocknr = tb->FEB[i]->b_blocknr ;
+ /* de-allocated block which was not used by balancing and
+ bforget about buffer for it */
+ brelse (tb->FEB[i]);
+ reiserfs_free_block (tb->transaction_handle, blocknr);
+ }
+ if (tb->used[i]) {
+ /* release used as new nodes including a new root */
+ brelse (tb->used[i]);
+ }
+ }
+
+ if (tb->vn_buf)
+ reiserfs_kfree (tb->vn_buf, tb->vn_buf_size, tb->tb_sb);
+
+}
+
+
+
diff --git a/fs/reiserfs/hashes.c b/fs/reiserfs/hashes.c
new file mode 100644
index 00000000000000..ed503a423da066
--- /dev/null
+++ b/fs/reiserfs/hashes.c
@@ -0,0 +1,223 @@
+
+/*
+ * Keyed 32-bit hash function using TEA in a Davis-Meyer function
+ * H0 = Key
+ * Hi = E Mi(Hi-1) + Hi-1
+ *
+ * (see Applied Cryptography, 2nd edition, p448).
+ *
+ * Jeremy Fitzhardinge <jeremy@zip.com.au> 1998
+ *
+ * Jeremy has agreed to the contents of reiserfs/README. -Hans
+ * Yura's function is added (04/07/2000)
+ */
+
+//
+// keyed_hash
+// yura_hash
+// r5_hash
+//
+
+#include <linux/kernel.h> /* for printk() as called by BUG() */
+#include <asm/types.h>
+#include <asm/page.h>
+
+
+
+#define DELTA 0x9E3779B9
+#define FULLROUNDS 10 /* 32 is overkill, 16 is strong crypto */
+#define PARTROUNDS 6 /* 6 gets complete mixing */
+
+/* a, b, c, d - data; h0, h1 - accumulated hash */
+#define TEACORE(rounds) \
+ do { \
+ u32 sum = 0; \
+ int n = rounds; \
+ u32 b0, b1; \
+ \
+ b0 = h0; \
+ b1 = h1; \
+ \
+ do \
+ { \
+ sum += DELTA; \
+ b0 += ((b1 << 4)+a) ^ (b1+sum) ^ ((b1 >> 5)+b); \
+ b1 += ((b0 << 4)+c) ^ (b0+sum) ^ ((b0 >> 5)+d); \
+ } while(--n); \
+ \
+ h0 += b0; \
+ h1 += b1; \
+ } while(0)
+
+
+u32 keyed_hash(const signed char *msg, int len)
+{
+ u32 k[] = { 0x9464a485, 0x542e1a94, 0x3e846bff, 0xb75bcfc3};
+
+ u32 h0 = k[0], h1 = k[1];
+ u32 a, b, c, d;
+ u32 pad;
+ int i;
+
+ // assert(len >= 0 && len < 256);
+
+ pad = (u32)len | ((u32)len << 8);
+ pad |= pad << 16;
+
+ while(len >= 16)
+ {
+ a = (u32)msg[ 0] |
+ (u32)msg[ 1] << 8 |
+ (u32)msg[ 2] << 16|
+ (u32)msg[ 3] << 24;
+ b = (u32)msg[ 4] |
+ (u32)msg[ 5] << 8 |
+ (u32)msg[ 6] << 16|
+ (u32)msg[ 7] << 24;
+ c = (u32)msg[ 8] |
+ (u32)msg[ 9] << 8 |
+ (u32)msg[10] << 16|
+ (u32)msg[11] << 24;
+ d = (u32)msg[12] |
+ (u32)msg[13] << 8 |
+ (u32)msg[14] << 16|
+ (u32)msg[15] << 24;
+
+ TEACORE(PARTROUNDS);
+
+ len -= 16;
+ msg += 16;
+ }
+
+ if (len >= 12)
+ {
+ //assert(len < 16);
+ if (len >= 16)
+ BUG();
+
+ a = (u32)msg[ 0] |
+ (u32)msg[ 1] << 8 |
+ (u32)msg[ 2] << 16|
+ (u32)msg[ 3] << 24;
+ b = (u32)msg[ 4] |
+ (u32)msg[ 5] << 8 |
+ (u32)msg[ 6] << 16|
+ (u32)msg[ 7] << 24;
+ c = (u32)msg[ 8] |
+ (u32)msg[ 9] << 8 |
+ (u32)msg[10] << 16|
+ (u32)msg[11] << 24;
+
+ d = pad;
+ for(i = 12; i < len; i++)
+ {
+ d <<= 8;
+ d |= msg[i];
+ }
+ }
+ else if (len >= 8)
+ {
+ //assert(len < 12);
+ if (len >= 12)
+ BUG();
+ a = (u32)msg[ 0] |
+ (u32)msg[ 1] << 8 |
+ (u32)msg[ 2] << 16|
+ (u32)msg[ 3] << 24;
+ b = (u32)msg[ 4] |
+ (u32)msg[ 5] << 8 |
+ (u32)msg[ 6] << 16|
+ (u32)msg[ 7] << 24;
+
+ c = d = pad;
+ for(i = 8; i < len; i++)
+ {
+ c <<= 8;
+ c |= msg[i];
+ }
+ }
+ else if (len >= 4)
+ {
+ //assert(len < 8);
+ if (len >= 8)
+ BUG();
+ a = (u32)msg[ 0] |
+ (u32)msg[ 1] << 8 |
+ (u32)msg[ 2] << 16|
+ (u32)msg[ 3] << 24;
+
+ b = c = d = pad;
+ for(i = 4; i < len; i++)
+ {
+ b <<= 8;
+ b |= msg[i];
+ }
+ }
+ else
+ {
+ //assert(len < 4);
+ if (len >= 4)
+ BUG();
+ a = b = c = d = pad;
+ for(i = 0; i < len; i++)
+ {
+ a <<= 8;
+ a |= msg[i];
+ }
+ }
+
+ TEACORE(FULLROUNDS);
+
+/* return 0;*/
+ return h0^h1;
+}
+
+/* What follows in this file is copyright 2000-2002 by Hans Reiser, and the
+ * licensing of what follows is governed by reiserfs/README */
+
+u32 yura_hash (const signed char *msg, int len)
+{
+ int j, pow;
+ u32 a, c;
+ int i;
+
+ for (pow=1,i=1; i < len; i++) pow = pow * 10;
+
+ if (len == 1)
+ a = msg[0]-48;
+ else
+ a = (msg[0] - 48) * pow;
+
+ for (i=1; i < len; i++) {
+ c = msg[i] - 48;
+ for (pow=1,j=i; j < len-1; j++) pow = pow * 10;
+ a = a + c * pow;
+ }
+
+ for (; i < 40; i++) {
+ c = '0' - 48;
+ for (pow=1,j=i; j < len-1; j++) pow = pow * 10;
+ a = a + c * pow;
+ }
+
+ for (; i < 256; i++) {
+ c = i;
+ for (pow=1,j=i; j < len-1; j++) pow = pow * 10;
+ a = a + c * pow;
+ }
+
+ a = a << 7;
+ return a;
+}
+
+u32 r5_hash (const signed char *msg, int len)
+{
+ u32 a=0;
+ while(*msg) {
+ a += *msg << 4;
+ a += *msg >> 4;
+ a *= 11;
+ msg++;
+ }
+ return a;
+}
diff --git a/fs/reiserfs/ibalance.c b/fs/reiserfs/ibalance.c
new file mode 100644
index 00000000000000..d217647f8f0662
--- /dev/null
+++ b/fs/reiserfs/ibalance.c
@@ -0,0 +1,1060 @@
+/*
+ * Copyright 2000-2002 by Hans Reiser, licensing governed by reiserfs/README
+ */
+
+#include <linux/config.h>
+#include <asm/uaccess.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/reiserfs_fs.h>
+
+/* this is one and only function that is used outside (do_balance.c) */
+int balance_internal (
+ struct tree_balance * ,
+ int,
+ int,
+ struct item_head * ,
+ struct buffer_head **
+ );
+
+/* modes of internal_shift_left, internal_shift_right and internal_insert_childs */
+#define INTERNAL_SHIFT_FROM_S_TO_L 0
+#define INTERNAL_SHIFT_FROM_R_TO_S 1
+#define INTERNAL_SHIFT_FROM_L_TO_S 2
+#define INTERNAL_SHIFT_FROM_S_TO_R 3
+#define INTERNAL_INSERT_TO_S 4
+#define INTERNAL_INSERT_TO_L 5
+#define INTERNAL_INSERT_TO_R 6
+
+static void internal_define_dest_src_infos (
+ int shift_mode,
+ struct tree_balance * tb,
+ int h,
+ struct buffer_info * dest_bi,
+ struct buffer_info * src_bi,
+ int * d_key,
+ struct buffer_head ** cf
+ )
+{
+ memset (dest_bi, 0, sizeof (struct buffer_info));
+ memset (src_bi, 0, sizeof (struct buffer_info));
+ /* define dest, src, dest parent, dest position */
+ switch (shift_mode) {
+ case INTERNAL_SHIFT_FROM_S_TO_L: /* used in internal_shift_left */
+ src_bi->tb = tb;
+ src_bi->bi_bh = PATH_H_PBUFFER (tb->tb_path, h);
+ src_bi->bi_parent = PATH_H_PPARENT (tb->tb_path, h);
+ src_bi->bi_position = PATH_H_POSITION (tb->tb_path, h + 1);
+ dest_bi->tb = tb;
+ dest_bi->bi_bh = tb->L[h];
+ dest_bi->bi_parent = tb->FL[h];
+ dest_bi->bi_position = get_left_neighbor_position (tb, h);
+ *d_key = tb->lkey[h];
+ *cf = tb->CFL[h];
+ break;
+ case INTERNAL_SHIFT_FROM_L_TO_S:
+ src_bi->tb = tb;
+ src_bi->bi_bh = tb->L[h];
+ src_bi->bi_parent = tb->FL[h];
+ src_bi->bi_position = get_left_neighbor_position (tb, h);
+ dest_bi->tb = tb;
+ dest_bi->bi_bh = PATH_H_PBUFFER (tb->tb_path, h);
+ dest_bi->bi_parent = PATH_H_PPARENT (tb->tb_path, h);
+ dest_bi->bi_position = PATH_H_POSITION (tb->tb_path, h + 1); /* dest position is analog of dest->b_item_order */
+ *d_key = tb->lkey[h];
+ *cf = tb->CFL[h];
+ break;
+
+ case INTERNAL_SHIFT_FROM_R_TO_S: /* used in internal_shift_left */
+ src_bi->tb = tb;
+ src_bi->bi_bh = tb->R[h];
+ src_bi->bi_parent = tb->FR[h];
+ src_bi->bi_position = get_right_neighbor_position (tb, h);
+ dest_bi->tb = tb;
+ dest_bi->bi_bh = PATH_H_PBUFFER (tb->tb_path, h);
+ dest_bi->bi_parent = PATH_H_PPARENT (tb->tb_path, h);
+ dest_bi->bi_position = PATH_H_POSITION (tb->tb_path, h + 1);
+ *d_key = tb->rkey[h];
+ *cf = tb->CFR[h];
+ break;
+
+ case INTERNAL_SHIFT_FROM_S_TO_R:
+ src_bi->tb = tb;
+ src_bi->bi_bh = PATH_H_PBUFFER (tb->tb_path, h);
+ src_bi->bi_parent = PATH_H_PPARENT (tb->tb_path, h);
+ src_bi->bi_position = PATH_H_POSITION (tb->tb_path, h + 1);
+ dest_bi->tb = tb;
+ dest_bi->bi_bh = tb->R[h];
+ dest_bi->bi_parent = tb->FR[h];
+ dest_bi->bi_position = get_right_neighbor_position (tb, h);
+ *d_key = tb->rkey[h];
+ *cf = tb->CFR[h];
+ break;
+
+ case INTERNAL_INSERT_TO_L:
+ dest_bi->tb = tb;
+ dest_bi->bi_bh = tb->L[h];
+ dest_bi->bi_parent = tb->FL[h];
+ dest_bi->bi_position = get_left_neighbor_position (tb, h);
+ break;
+
+ case INTERNAL_INSERT_TO_S:
+ dest_bi->tb = tb;
+ dest_bi->bi_bh = PATH_H_PBUFFER (tb->tb_path, h);
+ dest_bi->bi_parent = PATH_H_PPARENT (tb->tb_path, h);
+ dest_bi->bi_position = PATH_H_POSITION (tb->tb_path, h + 1);
+ break;
+
+ case INTERNAL_INSERT_TO_R:
+ dest_bi->tb = tb;
+ dest_bi->bi_bh = tb->R[h];
+ dest_bi->bi_parent = tb->FR[h];
+ dest_bi->bi_position = get_right_neighbor_position (tb, h);
+ break;
+
+ default:
+ reiserfs_panic (tb->tb_sb, "internal_define_dest_src_infos: shift type is unknown (%d)", shift_mode);
+ }
+}
+
+
+
+/* Insert count node pointers into buffer cur before position to + 1.
+ * Insert count items into buffer cur before position to.
+ * Items and node pointers are specified by inserted and bh respectively.
+ */
+static void internal_insert_childs (struct buffer_info * cur_bi,
+ int to, int count,
+ struct item_head * inserted,
+ struct buffer_head ** bh
+ )
+{
+ struct buffer_head * cur = cur_bi->bi_bh;
+ struct block_head * blkh;
+ int nr;
+ struct key * ih;
+ struct disk_child new_dc[2];
+ struct disk_child * dc;
+ int i;
+
+ if (count <= 0)
+ return;
+
+ blkh = B_BLK_HEAD(cur);
+ nr = blkh_nr_item(blkh);
+
+ RFALSE( count > 2,
+ "too many children (%d) are to be inserted", count);
+ RFALSE( B_FREE_SPACE (cur) < count * (KEY_SIZE + DC_SIZE),
+ "no enough free space (%d), needed %d bytes",
+ B_FREE_SPACE (cur), count * (KEY_SIZE + DC_SIZE));
+
+ /* prepare space for count disk_child */
+ dc = B_N_CHILD(cur,to+1);
+
+ memmove (dc + count, dc, (nr+1-(to+1)) * DC_SIZE);
+
+ /* copy to_be_insert disk children */
+ for (i = 0; i < count; i ++) {
+ put_dc_size( &(new_dc[i]), MAX_CHILD_SIZE(bh[i]) - B_FREE_SPACE(bh[i]));
+ put_dc_block_number( &(new_dc[i]), bh[i]->b_blocknr );
+ }
+ memcpy (dc, new_dc, DC_SIZE * count);
+
+
+ /* prepare space for count items */
+ ih = B_N_PDELIM_KEY (cur, ((to == -1) ? 0 : to));
+
+ memmove (ih + count, ih, (nr - to) * KEY_SIZE + (nr + 1 + count) * DC_SIZE);
+
+ /* copy item headers (keys) */
+ memcpy (ih, inserted, KEY_SIZE);
+ if ( count > 1 )
+ memcpy (ih + 1, inserted + 1, KEY_SIZE);
+
+ /* sizes, item number */
+ set_blkh_nr_item( blkh, blkh_nr_item(blkh) + count );
+ set_blkh_free_space( blkh,
+ blkh_free_space(blkh) - count * (DC_SIZE + KEY_SIZE ) );
+
+ do_balance_mark_internal_dirty (cur_bi->tb, cur,0);
+
+ /*&&&&&&&&&&&&&&&&&&&&&&&&*/
+ check_internal (cur);
+ /*&&&&&&&&&&&&&&&&&&&&&&&&*/
+
+ if (cur_bi->bi_parent) {
+ struct disk_child *t_dc = B_N_CHILD (cur_bi->bi_parent,cur_bi->bi_position);
+ put_dc_size( t_dc, dc_size(t_dc) + (count * (DC_SIZE + KEY_SIZE)));
+ do_balance_mark_internal_dirty(cur_bi->tb, cur_bi->bi_parent, 0);
+
+ /*&&&&&&&&&&&&&&&&&&&&&&&&*/
+ check_internal (cur_bi->bi_parent);
+ /*&&&&&&&&&&&&&&&&&&&&&&&&*/
+ }
+
+}
+
+
+/* Delete del_num items and node pointers from buffer cur starting from *
+ * the first_i'th item and first_p'th pointers respectively. */
+static void internal_delete_pointers_items (
+ struct buffer_info * cur_bi,
+ int first_p,
+ int first_i,
+ int del_num
+ )
+{
+ struct buffer_head * cur = cur_bi->bi_bh;
+ int nr;
+ struct block_head * blkh;
+ struct key * key;
+ struct disk_child * dc;
+
+ RFALSE( cur == NULL, "buffer is 0");
+ RFALSE( del_num < 0,
+ "negative number of items (%d) can not be deleted", del_num);
+ RFALSE( first_p < 0 || first_p + del_num > B_NR_ITEMS (cur) + 1 || first_i < 0,
+ "first pointer order (%d) < 0 or "
+ "no so many pointers (%d), only (%d) or "
+ "first key order %d < 0", first_p,
+ first_p + del_num, B_NR_ITEMS (cur) + 1, first_i);
+ if ( del_num == 0 )
+ return;
+
+ blkh = B_BLK_HEAD(cur);
+ nr = blkh_nr_item(blkh);
+
+ if ( first_p == 0 && del_num == nr + 1 ) {
+ RFALSE( first_i != 0, "1st deleted key must have order 0, not %d", first_i);
+ make_empty_node (cur_bi);
+ return;
+ }
+
+ RFALSE( first_i + del_num > B_NR_ITEMS (cur),
+ "first_i = %d del_num = %d "
+ "no so many keys (%d) in the node (%b)(%z)",
+ first_i, del_num, first_i + del_num, cur, cur);
+
+
+ /* deleting */
+ dc = B_N_CHILD (cur, first_p);
+
+ memmove (dc, dc + del_num, (nr + 1 - first_p - del_num) * DC_SIZE);
+ key = B_N_PDELIM_KEY (cur, first_i);
+ memmove (key, key + del_num, (nr - first_i - del_num) * KEY_SIZE + (nr + 1 - del_num) * DC_SIZE);
+
+
+ /* sizes, item number */
+ set_blkh_nr_item( blkh, blkh_nr_item(blkh) - del_num );
+ set_blkh_free_space( blkh,
+ blkh_free_space(blkh) + (del_num * (KEY_SIZE + DC_SIZE) ) );
+
+ do_balance_mark_internal_dirty (cur_bi->tb, cur, 0);
+ /*&&&&&&&&&&&&&&&&&&&&&&&*/
+ check_internal (cur);
+ /*&&&&&&&&&&&&&&&&&&&&&&&*/
+
+ if (cur_bi->bi_parent) {
+ struct disk_child *t_dc;
+ t_dc = B_N_CHILD (cur_bi->bi_parent, cur_bi->bi_position);
+ put_dc_size( t_dc, dc_size(t_dc) - (del_num * (KEY_SIZE + DC_SIZE) ) );
+
+ do_balance_mark_internal_dirty (cur_bi->tb, cur_bi->bi_parent,0);
+ /*&&&&&&&&&&&&&&&&&&&&&&&&*/
+ check_internal (cur_bi->bi_parent);
+ /*&&&&&&&&&&&&&&&&&&&&&&&&*/
+ }
+}
+
+
+/* delete n node pointers and items starting from given position */
+static void internal_delete_childs (struct buffer_info * cur_bi,
+ int from, int n)
+{
+ int i_from;
+
+ i_from = (from == 0) ? from : from - 1;
+
+ /* delete n pointers starting from `from' position in CUR;
+ delete n keys starting from 'i_from' position in CUR;
+ */
+ internal_delete_pointers_items (cur_bi, from, i_from, n);
+}
+
+
+/* copy cpy_num node pointers and cpy_num - 1 items from buffer src to buffer dest
+* last_first == FIRST_TO_LAST means, that we copy first items from src to tail of dest
+ * last_first == LAST_TO_FIRST means, that we copy last items from src to head of dest
+ */
+static void internal_copy_pointers_items (
+ struct buffer_info * dest_bi,
+ struct buffer_head * src,
+ int last_first, int cpy_num
+ )
+{
+ /* ATTENTION! Number of node pointers in DEST is equal to number of items in DEST *
+ * as delimiting key have already inserted to buffer dest.*/
+ struct buffer_head * dest = dest_bi->bi_bh;
+ int nr_dest, nr_src;
+ int dest_order, src_order;
+ struct block_head * blkh;
+ struct key * key;
+ struct disk_child * dc;
+
+ nr_src = B_NR_ITEMS (src);
+
+ RFALSE( dest == NULL || src == NULL,
+ "src (%p) or dest (%p) buffer is 0", src, dest);
+ RFALSE( last_first != FIRST_TO_LAST && last_first != LAST_TO_FIRST,
+ "invalid last_first parameter (%d)", last_first);
+ RFALSE( nr_src < cpy_num - 1,
+ "no so many items (%d) in src (%d)", cpy_num, nr_src);
+ RFALSE( cpy_num < 0, "cpy_num less than 0 (%d)", cpy_num);
+ RFALSE( cpy_num - 1 + B_NR_ITEMS(dest) > (int)MAX_NR_KEY(dest),
+ "cpy_num (%d) + item number in dest (%d) can not be > MAX_NR_KEY(%d)",
+ cpy_num, B_NR_ITEMS(dest), MAX_NR_KEY(dest));
+
+ if ( cpy_num == 0 )
+ return;
+
+ /* coping */
+ blkh = B_BLK_HEAD(dest);
+ nr_dest = blkh_nr_item(blkh);
+
+ /*dest_order = (last_first == LAST_TO_FIRST) ? 0 : nr_dest;*/
+ /*src_order = (last_first == LAST_TO_FIRST) ? (nr_src - cpy_num + 1) : 0;*/
+ (last_first == LAST_TO_FIRST) ? (dest_order = 0, src_order = nr_src - cpy_num + 1) :
+ (dest_order = nr_dest, src_order = 0);
+
+ /* prepare space for cpy_num pointers */
+ dc = B_N_CHILD (dest, dest_order);
+
+ memmove (dc + cpy_num, dc, (nr_dest - dest_order) * DC_SIZE);
+
+ /* insert pointers */
+ memcpy (dc, B_N_CHILD (src, src_order), DC_SIZE * cpy_num);
+
+
+ /* prepare space for cpy_num - 1 item headers */
+ key = B_N_PDELIM_KEY(dest, dest_order);
+ memmove (key + cpy_num - 1, key,
+ KEY_SIZE * (nr_dest - dest_order) + DC_SIZE * (nr_dest + cpy_num));
+
+
+ /* insert headers */
+ memcpy (key, B_N_PDELIM_KEY (src, src_order), KEY_SIZE * (cpy_num - 1));
+
+ /* sizes, item number */
+ set_blkh_nr_item( blkh, blkh_nr_item(blkh) + (cpy_num - 1 ) );
+ set_blkh_free_space( blkh,
+ blkh_free_space(blkh) - (KEY_SIZE * (cpy_num - 1) + DC_SIZE * cpy_num ) );
+
+ do_balance_mark_internal_dirty (dest_bi->tb, dest, 0);
+
+ /*&&&&&&&&&&&&&&&&&&&&&&&&*/
+ check_internal (dest);
+ /*&&&&&&&&&&&&&&&&&&&&&&&&*/
+
+ if (dest_bi->bi_parent) {
+ struct disk_child *t_dc;
+ t_dc = B_N_CHILD(dest_bi->bi_parent,dest_bi->bi_position);
+ put_dc_size( t_dc, dc_size(t_dc) + (KEY_SIZE * (cpy_num - 1) + DC_SIZE * cpy_num) );
+
+ do_balance_mark_internal_dirty (dest_bi->tb, dest_bi->bi_parent,0);
+ /*&&&&&&&&&&&&&&&&&&&&&&&&*/
+ check_internal (dest_bi->bi_parent);
+ /*&&&&&&&&&&&&&&&&&&&&&&&&*/
+ }
+
+}
+
+
+/* Copy cpy_num node pointers and cpy_num - 1 items from buffer src to buffer dest.
+ * Delete cpy_num - del_par items and node pointers from buffer src.
+ * last_first == FIRST_TO_LAST means, that we copy/delete first items from src.
+ * last_first == LAST_TO_FIRST means, that we copy/delete last items from src.
+ */
+static void internal_move_pointers_items (struct buffer_info * dest_bi,
+ struct buffer_info * src_bi,
+ int last_first, int cpy_num, int del_par)
+{
+ int first_pointer;
+ int first_item;
+
+ internal_copy_pointers_items (dest_bi, src_bi->bi_bh, last_first, cpy_num);
+
+ if (last_first == FIRST_TO_LAST) { /* shift_left occurs */
+ first_pointer = 0;
+ first_item = 0;
+ /* delete cpy_num - del_par pointers and keys starting for pointers with first_pointer,
+ for key - with first_item */
+ internal_delete_pointers_items (src_bi, first_pointer, first_item, cpy_num - del_par);
+ } else { /* shift_right occurs */
+ int i, j;
+
+ i = ( cpy_num - del_par == ( j = B_NR_ITEMS(src_bi->bi_bh)) + 1 ) ? 0 : j - cpy_num + del_par;
+
+ internal_delete_pointers_items (src_bi, j + 1 - cpy_num + del_par, i, cpy_num - del_par);
+ }
+}
+
+/* Insert n_src'th key of buffer src before n_dest'th key of buffer dest. */
+static void internal_insert_key (struct buffer_info * dest_bi,
+ int dest_position_before, /* insert key before key with n_dest number */
+ struct buffer_head * src,
+ int src_position)
+{
+ struct buffer_head * dest = dest_bi->bi_bh;
+ int nr;
+ struct block_head * blkh;
+ struct key * key;
+
+ RFALSE( dest == NULL || src == NULL,
+ "source(%p) or dest(%p) buffer is 0", src, dest);
+ RFALSE( dest_position_before < 0 || src_position < 0,
+ "source(%d) or dest(%d) key number less than 0",
+ src_position, dest_position_before);
+ RFALSE( dest_position_before > B_NR_ITEMS (dest) ||
+ src_position >= B_NR_ITEMS(src),
+ "invalid position in dest (%d (key number %d)) or in src (%d (key number %d))",
+ dest_position_before, B_NR_ITEMS (dest),
+ src_position, B_NR_ITEMS(src));
+ RFALSE( B_FREE_SPACE (dest) < KEY_SIZE,
+ "no enough free space (%d) in dest buffer", B_FREE_SPACE (dest));
+
+ blkh = B_BLK_HEAD(dest);
+ nr = blkh_nr_item(blkh);
+
+ /* prepare space for inserting key */
+ key = B_N_PDELIM_KEY (dest, dest_position_before);
+ memmove (key + 1, key, (nr - dest_position_before) * KEY_SIZE + (nr + 1) * DC_SIZE);
+
+ /* insert key */
+ memcpy (key, B_N_PDELIM_KEY(src, src_position), KEY_SIZE);
+
+ /* Change dirt, free space, item number fields. */
+
+ set_blkh_nr_item( blkh, blkh_nr_item(blkh) + 1 );
+ set_blkh_free_space( blkh, blkh_free_space(blkh) - KEY_SIZE );
+
+ do_balance_mark_internal_dirty (dest_bi->tb, dest, 0);
+
+ if (dest_bi->bi_parent) {
+ struct disk_child *t_dc;
+ t_dc = B_N_CHILD(dest_bi->bi_parent,dest_bi->bi_position);
+ put_dc_size( t_dc, dc_size(t_dc) + KEY_SIZE );
+
+ do_balance_mark_internal_dirty (dest_bi->tb, dest_bi->bi_parent,0);
+ }
+}
+
+
+
+/* Insert d_key'th (delimiting) key from buffer cfl to tail of dest.
+ * Copy pointer_amount node pointers and pointer_amount - 1 items from buffer src to buffer dest.
+ * Replace d_key'th key in buffer cfl.
+ * Delete pointer_amount items and node pointers from buffer src.
+ */
+/* this can be invoked both to shift from S to L and from R to S */
+static void internal_shift_left (
+ int mode, /* INTERNAL_FROM_S_TO_L | INTERNAL_FROM_R_TO_S */
+ struct tree_balance * tb,
+ int h,
+ int pointer_amount
+ )
+{
+ struct buffer_info dest_bi, src_bi;
+ struct buffer_head * cf;
+ int d_key_position;
+
+ internal_define_dest_src_infos (mode, tb, h, &dest_bi, &src_bi, &d_key_position, &cf);
+
+ /*printk("pointer_amount = %d\n",pointer_amount);*/
+
+ if (pointer_amount) {
+ /* insert delimiting key from common father of dest and src to node dest into position B_NR_ITEM(dest) */
+ internal_insert_key (&dest_bi, B_NR_ITEMS(dest_bi.bi_bh), cf, d_key_position);
+
+ if (B_NR_ITEMS(src_bi.bi_bh) == pointer_amount - 1) {
+ if (src_bi.bi_position/*src->b_item_order*/ == 0)
+ replace_key (tb, cf, d_key_position, src_bi.bi_parent/*src->b_parent*/, 0);
+ } else
+ replace_key (tb, cf, d_key_position, src_bi.bi_bh, pointer_amount - 1);
+ }
+ /* last parameter is del_parameter */
+ internal_move_pointers_items (&dest_bi, &src_bi, FIRST_TO_LAST, pointer_amount, 0);
+
+}
+
+/* Insert delimiting key to L[h].
+ * Copy n node pointers and n - 1 items from buffer S[h] to L[h].
+ * Delete n - 1 items and node pointers from buffer S[h].
+ */
+/* it always shifts from S[h] to L[h] */
+static void internal_shift1_left (
+ struct tree_balance * tb,
+ int h,
+ int pointer_amount
+ )
+{
+ struct buffer_info dest_bi, src_bi;
+ struct buffer_head * cf;
+ int d_key_position;
+
+ internal_define_dest_src_infos (INTERNAL_SHIFT_FROM_S_TO_L, tb, h, &dest_bi, &src_bi, &d_key_position, &cf);
+
+ if ( pointer_amount > 0 ) /* insert lkey[h]-th key from CFL[h] to left neighbor L[h] */
+ internal_insert_key (&dest_bi, B_NR_ITEMS(dest_bi.bi_bh), cf, d_key_position);
+ /* internal_insert_key (tb->L[h], B_NR_ITEM(tb->L[h]), tb->CFL[h], tb->lkey[h]);*/
+
+ /* last parameter is del_parameter */
+ internal_move_pointers_items (&dest_bi, &src_bi, FIRST_TO_LAST, pointer_amount, 1);
+ /* internal_move_pointers_items (tb->L[h], tb->S[h], FIRST_TO_LAST, pointer_amount, 1);*/
+}
+
+
+/* Insert d_key'th (delimiting) key from buffer cfr to head of dest.
+ * Copy n node pointers and n - 1 items from buffer src to buffer dest.
+ * Replace d_key'th key in buffer cfr.
+ * Delete n items and node pointers from buffer src.
+ */
+static void internal_shift_right (
+ int mode, /* INTERNAL_FROM_S_TO_R | INTERNAL_FROM_L_TO_S */
+ struct tree_balance * tb,
+ int h,
+ int pointer_amount
+ )
+{
+ struct buffer_info dest_bi, src_bi;
+ struct buffer_head * cf;
+ int d_key_position;
+ int nr;
+
+
+ internal_define_dest_src_infos (mode, tb, h, &dest_bi, &src_bi, &d_key_position, &cf);
+
+ nr = B_NR_ITEMS (src_bi.bi_bh);
+
+ if (pointer_amount > 0) {
+ /* insert delimiting key from common father of dest and src to dest node into position 0 */
+ internal_insert_key (&dest_bi, 0, cf, d_key_position);
+ if (nr == pointer_amount - 1) {
+ RFALSE( src_bi.bi_bh != PATH_H_PBUFFER (tb->tb_path, h)/*tb->S[h]*/ ||
+ dest_bi.bi_bh != tb->R[h],
+ "src (%p) must be == tb->S[h](%p) when it disappears",
+ src_bi.bi_bh, PATH_H_PBUFFER (tb->tb_path, h));
+ /* when S[h] disappers replace left delemiting key as well */
+ if (tb->CFL[h])
+ replace_key (tb, cf, d_key_position, tb->CFL[h], tb->lkey[h]);
+ } else
+ replace_key (tb, cf, d_key_position, src_bi.bi_bh, nr - pointer_amount);
+ }
+
+ /* last parameter is del_parameter */
+ internal_move_pointers_items (&dest_bi, &src_bi, LAST_TO_FIRST, pointer_amount, 0);
+}
+
+/* Insert delimiting key to R[h].
+ * Copy n node pointers and n - 1 items from buffer S[h] to R[h].
+ * Delete n - 1 items and node pointers from buffer S[h].
+ */
+/* it always shift from S[h] to R[h] */
+static void internal_shift1_right (
+ struct tree_balance * tb,
+ int h,
+ int pointer_amount
+ )
+{
+ struct buffer_info dest_bi, src_bi;
+ struct buffer_head * cf;
+ int d_key_position;
+
+ internal_define_dest_src_infos (INTERNAL_SHIFT_FROM_S_TO_R, tb, h, &dest_bi, &src_bi, &d_key_position, &cf);
+
+ if (pointer_amount > 0) /* insert rkey from CFR[h] to right neighbor R[h] */
+ internal_insert_key (&dest_bi, 0, cf, d_key_position);
+ /* internal_insert_key (tb->R[h], 0, tb->CFR[h], tb->rkey[h]);*/
+
+ /* last parameter is del_parameter */
+ internal_move_pointers_items (&dest_bi, &src_bi, LAST_TO_FIRST, pointer_amount, 1);
+ /* internal_move_pointers_items (tb->R[h], tb->S[h], LAST_TO_FIRST, pointer_amount, 1);*/
+}
+
+
+/* Delete insert_num node pointers together with their left items
+ * and balance current node.*/
+static void balance_internal_when_delete (struct tree_balance * tb,
+ int h, int child_pos)
+{
+ int insert_num;
+ int n;
+ struct buffer_head * tbSh = PATH_H_PBUFFER (tb->tb_path, h);
+ struct buffer_info bi;
+
+ insert_num = tb->insert_size[h] / ((int)(DC_SIZE + KEY_SIZE));
+
+ /* delete child-node-pointer(s) together with their left item(s) */
+ bi.tb = tb;
+ bi.bi_bh = tbSh;
+ bi.bi_parent = PATH_H_PPARENT (tb->tb_path, h);
+ bi.bi_position = PATH_H_POSITION (tb->tb_path, h + 1);
+
+ internal_delete_childs (&bi, child_pos, -insert_num);
+
+ RFALSE( tb->blknum[h] > 1,
+ "tb->blknum[%d]=%d when insert_size < 0", h, tb->blknum[h]);
+
+ n = B_NR_ITEMS(tbSh);
+
+ if ( tb->lnum[h] == 0 && tb->rnum[h] == 0 ) {
+ if ( tb->blknum[h] == 0 ) {
+ /* node S[h] (root of the tree) is empty now */
+ struct buffer_head *new_root;
+
+ RFALSE( n || B_FREE_SPACE (tbSh) != MAX_CHILD_SIZE(tbSh) - DC_SIZE,
+ "buffer must have only 0 keys (%d)", n);
+ RFALSE( bi.bi_parent, "root has parent (%p)", bi.bi_parent);
+
+ /* choose a new root */
+ if ( ! tb->L[h-1] || ! B_NR_ITEMS(tb->L[h-1]) )
+ new_root = tb->R[h-1];
+ else
+ new_root = tb->L[h-1];
+ /* switch super block's tree root block number to the new value */
+ PUT_SB_ROOT_BLOCK( tb->tb_sb, new_root->b_blocknr );
+ //tb->tb_sb->u.reiserfs_sb.s_rs->s_tree_height --;
+ PUT_SB_TREE_HEIGHT( tb->tb_sb, SB_TREE_HEIGHT(tb->tb_sb) - 1 );
+
+ do_balance_mark_sb_dirty (tb, tb->tb_sb->u.reiserfs_sb.s_sbh, 1);
+ /*&&&&&&&&&&&&&&&&&&&&&&*/
+ if (h > 1)
+ /* use check_internal if new root is an internal node */
+ check_internal (new_root);
+ /*&&&&&&&&&&&&&&&&&&&&&&*/
+ tb->tb_sb->s_dirt = 1;
+
+ /* do what is needed for buffer thrown from tree */
+ reiserfs_invalidate_buffer(tb, tbSh);
+ return;
+ }
+ return;
+ }
+
+ if ( tb->L[h] && tb->lnum[h] == -B_NR_ITEMS(tb->L[h]) - 1 ) { /* join S[h] with L[h] */
+
+ RFALSE( tb->rnum[h] != 0,
+ "invalid tb->rnum[%d]==%d when joining S[h] with L[h]",
+ h, tb->rnum[h]);
+
+ internal_shift_left (INTERNAL_SHIFT_FROM_S_TO_L, tb, h, n + 1);
+ reiserfs_invalidate_buffer(tb, tbSh);
+
+ return;
+ }
+
+ if ( tb->R[h] && tb->rnum[h] == -B_NR_ITEMS(tb->R[h]) - 1 ) { /* join S[h] with R[h] */
+ RFALSE( tb->lnum[h] != 0,
+ "invalid tb->lnum[%d]==%d when joining S[h] with R[h]",
+ h, tb->lnum[h]);
+
+ internal_shift_right (INTERNAL_SHIFT_FROM_S_TO_R, tb, h, n + 1);
+
+ reiserfs_invalidate_buffer(tb,tbSh);
+ return;
+ }
+
+ if ( tb->lnum[h] < 0 ) { /* borrow from left neighbor L[h] */
+ RFALSE( tb->rnum[h] != 0,
+ "wrong tb->rnum[%d]==%d when borrow from L[h]", h, tb->rnum[h]);
+ /*internal_shift_right (tb, h, tb->L[h], tb->CFL[h], tb->lkey[h], tb->S[h], -tb->lnum[h]);*/
+ internal_shift_right (INTERNAL_SHIFT_FROM_L_TO_S, tb, h, -tb->lnum[h]);
+ return;
+ }
+
+ if ( tb->rnum[h] < 0 ) { /* borrow from right neighbor R[h] */
+ RFALSE( tb->lnum[h] != 0,
+ "invalid tb->lnum[%d]==%d when borrow from R[h]",
+ h, tb->lnum[h]);
+ internal_shift_left (INTERNAL_SHIFT_FROM_R_TO_S, tb, h, -tb->rnum[h]);/*tb->S[h], tb->CFR[h], tb->rkey[h], tb->R[h], -tb->rnum[h]);*/
+ return;
+ }
+
+ if ( tb->lnum[h] > 0 ) { /* split S[h] into two parts and put them into neighbors */
+ RFALSE( tb->rnum[h] == 0 || tb->lnum[h] + tb->rnum[h] != n + 1,
+ "invalid tb->lnum[%d]==%d or tb->rnum[%d]==%d when S[h](item number == %d) is split between them",
+ h, tb->lnum[h], h, tb->rnum[h], n);
+
+ internal_shift_left (INTERNAL_SHIFT_FROM_S_TO_L, tb, h, tb->lnum[h]);/*tb->L[h], tb->CFL[h], tb->lkey[h], tb->S[h], tb->lnum[h]);*/
+ internal_shift_right (INTERNAL_SHIFT_FROM_S_TO_R, tb, h, tb->rnum[h]);
+
+ reiserfs_invalidate_buffer (tb, tbSh);
+
+ return;
+ }
+ reiserfs_panic (tb->tb_sb, "balance_internal_when_delete: unexpected tb->lnum[%d]==%d or tb->rnum[%d]==%d",
+ h, tb->lnum[h], h, tb->rnum[h]);
+}
+
+
+/* Replace delimiting key of buffers L[h] and S[h] by the given key.*/
+void replace_lkey (
+ struct tree_balance * tb,
+ int h,
+ struct item_head * key
+ )
+{
+ RFALSE( tb->L[h] == NULL || tb->CFL[h] == NULL,
+ "L[h](%p) and CFL[h](%p) must exist in replace_lkey",
+ tb->L[h], tb->CFL[h]);
+
+ if (B_NR_ITEMS(PATH_H_PBUFFER(tb->tb_path, h)) == 0)
+ return;
+
+ memcpy (B_N_PDELIM_KEY(tb->CFL[h],tb->lkey[h]), key, KEY_SIZE);
+
+ do_balance_mark_internal_dirty (tb, tb->CFL[h],0);
+}
+
+
+/* Replace delimiting key of buffers S[h] and R[h] by the given key.*/
+void replace_rkey (
+ struct tree_balance * tb,
+ int h,
+ struct item_head * key
+ )
+{
+ RFALSE( tb->R[h] == NULL || tb->CFR[h] == NULL,
+ "R[h](%p) and CFR[h](%p) must exist in replace_rkey",
+ tb->R[h], tb->CFR[h]);
+ RFALSE( B_NR_ITEMS(tb->R[h]) == 0,
+ "R[h] can not be empty if it exists (item number=%d)",
+ B_NR_ITEMS(tb->R[h]));
+
+ memcpy (B_N_PDELIM_KEY(tb->CFR[h],tb->rkey[h]), key, KEY_SIZE);
+
+ do_balance_mark_internal_dirty (tb, tb->CFR[h], 0);
+}
+
+
+int balance_internal (struct tree_balance * tb, /* tree_balance structure */
+ int h, /* level of the tree */
+ int child_pos,
+ struct item_head * insert_key, /* key for insertion on higher level */
+ struct buffer_head ** insert_ptr /* node for insertion on higher level*/
+ )
+ /* if inserting/pasting
+ {
+ child_pos is the position of the node-pointer in S[h] that *
+ pointed to S[h-1] before balancing of the h-1 level; *
+ this means that new pointers and items must be inserted AFTER *
+ child_pos
+ }
+ else
+ {
+ it is the position of the leftmost pointer that must be deleted (together with
+ its corresponding key to the left of the pointer)
+ as a result of the previous level's balancing.
+ }
+*/
+{
+ struct buffer_head * tbSh = PATH_H_PBUFFER (tb->tb_path, h);
+ struct buffer_info bi;
+ int order; /* we return this: it is 0 if there is no S[h], else it is tb->S[h]->b_item_order */
+ int insert_num, n, k;
+ struct buffer_head * S_new;
+ struct item_head new_insert_key;
+ struct buffer_head * new_insert_ptr = NULL;
+ struct item_head * new_insert_key_addr = insert_key;
+
+ RFALSE( h < 1, "h (%d) can not be < 1 on internal level", h);
+
+ PROC_INFO_INC( tb -> tb_sb, balance_at[ h ] );
+
+ order = ( tbSh ) ? PATH_H_POSITION (tb->tb_path, h + 1)/*tb->S[h]->b_item_order*/ : 0;
+
+ /* Using insert_size[h] calculate the number insert_num of items
+ that must be inserted to or deleted from S[h]. */
+ insert_num = tb->insert_size[h]/((int)(KEY_SIZE + DC_SIZE));
+
+ /* Check whether insert_num is proper **/
+ RFALSE( insert_num < -2 || insert_num > 2,
+ "incorrect number of items inserted to the internal node (%d)",
+ insert_num);
+ RFALSE( h > 1 && (insert_num > 1 || insert_num < -1),
+ "incorrect number of items (%d) inserted to the internal node on a level (h=%d) higher than last internal level",
+ insert_num, h);
+
+ /* Make balance in case insert_num < 0 */
+ if ( insert_num < 0 ) {
+ balance_internal_when_delete (tb, h, child_pos);
+ return order;
+ }
+
+ k = 0;
+ if ( tb->lnum[h] > 0 ) {
+ /* shift lnum[h] items from S[h] to the left neighbor L[h].
+ check how many of new items fall into L[h] or CFL[h] after
+ shifting */
+ n = B_NR_ITEMS (tb->L[h]); /* number of items in L[h] */
+ if ( tb->lnum[h] <= child_pos ) {
+ /* new items don't fall into L[h] or CFL[h] */
+ internal_shift_left (INTERNAL_SHIFT_FROM_S_TO_L, tb, h, tb->lnum[h]);
+ /*internal_shift_left (tb->L[h],tb->CFL[h],tb->lkey[h],tbSh,tb->lnum[h]);*/
+ child_pos -= tb->lnum[h];
+ } else if ( tb->lnum[h] > child_pos + insert_num ) {
+ /* all new items fall into L[h] */
+ internal_shift_left (INTERNAL_SHIFT_FROM_S_TO_L, tb, h, tb->lnum[h] - insert_num);
+ /* internal_shift_left(tb->L[h],tb->CFL[h],tb->lkey[h],tbSh,
+ tb->lnum[h]-insert_num);
+ */
+ /* insert insert_num keys and node-pointers into L[h] */
+ bi.tb = tb;
+ bi.bi_bh = tb->L[h];
+ bi.bi_parent = tb->FL[h];
+ bi.bi_position = get_left_neighbor_position (tb, h);
+ internal_insert_childs (&bi,/*tb->L[h], tb->S[h-1]->b_next*/ n + child_pos + 1,
+ insert_num,insert_key,insert_ptr);
+
+ insert_num = 0;
+ } else {
+ struct disk_child * dc;
+
+ /* some items fall into L[h] or CFL[h], but some don't fall */
+ internal_shift1_left(tb,h,child_pos+1);
+ /* calculate number of new items that fall into L[h] */
+ k = tb->lnum[h] - child_pos - 1;
+ bi.tb = tb;
+ bi.bi_bh = tb->L[h];
+ bi.bi_parent = tb->FL[h];
+ bi.bi_position = get_left_neighbor_position (tb, h);
+ internal_insert_childs (&bi,/*tb->L[h], tb->S[h-1]->b_next,*/ n + child_pos + 1,k,
+ insert_key,insert_ptr);
+
+ replace_lkey(tb,h,insert_key + k);
+
+ /* replace the first node-ptr in S[h] by node-ptr to insert_ptr[k] */
+ dc = B_N_CHILD(tbSh, 0);
+ put_dc_size( dc, MAX_CHILD_SIZE(insert_ptr[k]) - B_FREE_SPACE (insert_ptr[k]));
+ put_dc_block_number( dc, insert_ptr[k]->b_blocknr );
+
+ do_balance_mark_internal_dirty (tb, tbSh, 0);
+
+ k++;
+ insert_key += k;
+ insert_ptr += k;
+ insert_num -= k;
+ child_pos = 0;
+ }
+ } /* tb->lnum[h] > 0 */
+
+ if ( tb->rnum[h] > 0 ) {
+ /*shift rnum[h] items from S[h] to the right neighbor R[h]*/
+ /* check how many of new items fall into R or CFR after shifting */
+ n = B_NR_ITEMS (tbSh); /* number of items in S[h] */
+ if ( n - tb->rnum[h] >= child_pos )
+ /* new items fall into S[h] */
+ /*internal_shift_right(tb,h,tbSh,tb->CFR[h],tb->rkey[h],tb->R[h],tb->rnum[h]);*/
+ internal_shift_right (INTERNAL_SHIFT_FROM_S_TO_R, tb, h, tb->rnum[h]);
+ else
+ if ( n + insert_num - tb->rnum[h] < child_pos )
+ {
+ /* all new items fall into R[h] */
+ /*internal_shift_right(tb,h,tbSh,tb->CFR[h],tb->rkey[h],tb->R[h],
+ tb->rnum[h] - insert_num);*/
+ internal_shift_right (INTERNAL_SHIFT_FROM_S_TO_R, tb, h, tb->rnum[h] - insert_num);
+
+ /* insert insert_num keys and node-pointers into R[h] */
+ bi.tb = tb;
+ bi.bi_bh = tb->R[h];
+ bi.bi_parent = tb->FR[h];
+ bi.bi_position = get_right_neighbor_position (tb, h);
+ internal_insert_childs (&bi, /*tb->R[h],tb->S[h-1]->b_next*/ child_pos - n - insert_num + tb->rnum[h] - 1,
+ insert_num,insert_key,insert_ptr);
+ insert_num = 0;
+ }
+ else
+ {
+ struct disk_child * dc;
+
+ /* one of the items falls into CFR[h] */
+ internal_shift1_right(tb,h,n - child_pos + 1);
+ /* calculate number of new items that fall into R[h] */
+ k = tb->rnum[h] - n + child_pos - 1;
+ bi.tb = tb;
+ bi.bi_bh = tb->R[h];
+ bi.bi_parent = tb->FR[h];
+ bi.bi_position = get_right_neighbor_position (tb, h);
+ internal_insert_childs (&bi, /*tb->R[h], tb->R[h]->b_child,*/ 0, k, insert_key + 1, insert_ptr + 1);
+
+ replace_rkey(tb,h,insert_key + insert_num - k - 1);
+
+ /* replace the first node-ptr in R[h] by node-ptr insert_ptr[insert_num-k-1]*/
+ dc = B_N_CHILD(tb->R[h], 0);
+ put_dc_size( dc, MAX_CHILD_SIZE(insert_ptr[insert_num-k-1]) -
+ B_FREE_SPACE (insert_ptr[insert_num-k-1]));
+ put_dc_block_number( dc, insert_ptr[insert_num-k-1]->b_blocknr );
+
+ do_balance_mark_internal_dirty (tb, tb->R[h],0);
+
+ insert_num -= (k + 1);
+ }
+ }
+
+ /** Fill new node that appears instead of S[h] **/
+ RFALSE( tb->blknum[h] > 2, "blknum can not be > 2 for internal level");
+ RFALSE( tb->blknum[h] < 0, "blknum can not be < 0");
+
+ if ( ! tb->blknum[h] )
+ { /* node S[h] is empty now */
+ RFALSE( ! tbSh, "S[h] is equal NULL");
+
+ /* do what is needed for buffer thrown from tree */
+ reiserfs_invalidate_buffer(tb,tbSh);
+ return order;
+ }
+
+ if ( ! tbSh ) {
+ /* create new root */
+ struct disk_child * dc;
+ struct buffer_head * tbSh_1 = PATH_H_PBUFFER (tb->tb_path, h - 1);
+ struct block_head * blkh;
+
+
+ if ( tb->blknum[h] != 1 )
+ reiserfs_panic(0, "balance_internal: One new node required for creating the new root");
+ /* S[h] = empty buffer from the list FEB. */
+ tbSh = get_FEB (tb);
+ blkh = B_BLK_HEAD(tbSh);
+ set_blkh_level( blkh, h + 1 );
+
+ /* Put the unique node-pointer to S[h] that points to S[h-1]. */
+
+ dc = B_N_CHILD(tbSh, 0);
+ put_dc_block_number( dc, tbSh_1->b_blocknr );
+ put_dc_size( dc, (MAX_CHILD_SIZE (tbSh_1) - B_FREE_SPACE (tbSh_1)));
+
+ tb->insert_size[h] -= DC_SIZE;
+ set_blkh_free_space( blkh, blkh_free_space(blkh) - DC_SIZE );
+
+ do_balance_mark_internal_dirty (tb, tbSh, 0);
+
+ /*&&&&&&&&&&&&&&&&&&&&&&&&*/
+ check_internal (tbSh);
+ /*&&&&&&&&&&&&&&&&&&&&&&&&*/
+
+ /* put new root into path structure */
+ PATH_OFFSET_PBUFFER(tb->tb_path, ILLEGAL_PATH_ELEMENT_OFFSET) = tbSh;
+
+ /* Change root in structure super block. */
+ PUT_SB_ROOT_BLOCK( tb->tb_sb, tbSh->b_blocknr );
+ PUT_SB_TREE_HEIGHT( tb->tb_sb, SB_TREE_HEIGHT(tb->tb_sb) + 1 );
+ do_balance_mark_sb_dirty (tb, tb->tb_sb->u.reiserfs_sb.s_sbh, 1);
+ tb->tb_sb->s_dirt = 1;
+ }
+
+ if ( tb->blknum[h] == 2 ) {
+ int snum;
+ struct buffer_info dest_bi, src_bi;
+
+
+ /* S_new = free buffer from list FEB */
+ S_new = get_FEB(tb);
+
+ set_blkh_level( B_BLK_HEAD(S_new), h + 1 );
+
+ dest_bi.tb = tb;
+ dest_bi.bi_bh = S_new;
+ dest_bi.bi_parent = 0;
+ dest_bi.bi_position = 0;
+ src_bi.tb = tb;
+ src_bi.bi_bh = tbSh;
+ src_bi.bi_parent = PATH_H_PPARENT (tb->tb_path, h);
+ src_bi.bi_position = PATH_H_POSITION (tb->tb_path, h + 1);
+
+ n = B_NR_ITEMS (tbSh); /* number of items in S[h] */
+ snum = (insert_num + n + 1)/2;
+ if ( n - snum >= child_pos ) {
+ /* new items don't fall into S_new */
+ /* store the delimiting key for the next level */
+ /* new_insert_key = (n - snum)'th key in S[h] */
+ memcpy (&new_insert_key,B_N_PDELIM_KEY(tbSh,n - snum),
+ KEY_SIZE);
+ /* last parameter is del_par */
+ internal_move_pointers_items (&dest_bi, &src_bi, LAST_TO_FIRST, snum, 0);
+ /* internal_move_pointers_items(S_new, tbSh, LAST_TO_FIRST, snum, 0);*/
+ } else if ( n + insert_num - snum < child_pos ) {
+ /* all new items fall into S_new */
+ /* store the delimiting key for the next level */
+ /* new_insert_key = (n + insert_item - snum)'th key in S[h] */
+ memcpy(&new_insert_key,B_N_PDELIM_KEY(tbSh,n + insert_num - snum),
+ KEY_SIZE);
+ /* last parameter is del_par */
+ internal_move_pointers_items (&dest_bi, &src_bi, LAST_TO_FIRST, snum - insert_num, 0);
+ /* internal_move_pointers_items(S_new,tbSh,1,snum - insert_num,0);*/
+
+ /* insert insert_num keys and node-pointers into S_new */
+ internal_insert_childs (&dest_bi, /*S_new,tb->S[h-1]->b_next,*/child_pos - n - insert_num + snum - 1,
+ insert_num,insert_key,insert_ptr);
+
+ insert_num = 0;
+ } else {
+ struct disk_child * dc;
+
+ /* some items fall into S_new, but some don't fall */
+ /* last parameter is del_par */
+ internal_move_pointers_items (&dest_bi, &src_bi, LAST_TO_FIRST, n - child_pos + 1, 1);
+ /* internal_move_pointers_items(S_new,tbSh,1,n - child_pos + 1,1);*/
+ /* calculate number of new items that fall into S_new */
+ k = snum - n + child_pos - 1;
+
+ internal_insert_childs (&dest_bi, /*S_new,*/ 0, k, insert_key + 1, insert_ptr+1);
+
+ /* new_insert_key = insert_key[insert_num - k - 1] */
+ memcpy(&new_insert_key,insert_key + insert_num - k - 1,
+ KEY_SIZE);
+ /* replace first node-ptr in S_new by node-ptr to insert_ptr[insert_num-k-1] */
+
+ dc = B_N_CHILD(S_new,0);
+ put_dc_size( dc, (MAX_CHILD_SIZE(insert_ptr[insert_num-k-1]) -
+ B_FREE_SPACE(insert_ptr[insert_num-k-1])) );
+ put_dc_block_number( dc, insert_ptr[insert_num-k-1]->b_blocknr );
+
+ do_balance_mark_internal_dirty (tb, S_new,0);
+
+ insert_num -= (k + 1);
+ }
+ /* new_insert_ptr = node_pointer to S_new */
+ new_insert_ptr = S_new;
+
+ RFALSE(( buffer_locked(S_new) || atomic_read (&(S_new->b_count)) != 1) &&
+ (buffer_locked(S_new) || atomic_read(&(S_new->b_count)) > 2 ||
+ !(buffer_journaled(S_new) || buffer_journal_dirty(S_new))),
+ "cm-00001: bad S_new (%b)", S_new);
+
+ // S_new is released in unfix_nodes
+ }
+
+ n = B_NR_ITEMS (tbSh); /*number of items in S[h] */
+
+ if ( 0 <= child_pos && child_pos <= n && insert_num > 0 ) {
+ bi.tb = tb;
+ bi.bi_bh = tbSh;
+ bi.bi_parent = PATH_H_PPARENT (tb->tb_path, h);
+ bi.bi_position = PATH_H_POSITION (tb->tb_path, h + 1);
+ internal_insert_childs (
+ &bi,/*tbSh,*/
+ /* ( tb->S[h-1]->b_parent == tb->S[h] ) ? tb->S[h-1]->b_next : tb->S[h]->b_child->b_next,*/
+ child_pos,insert_num,insert_key,insert_ptr
+ );
+ }
+
+
+ memcpy (new_insert_key_addr,&new_insert_key,KEY_SIZE);
+ insert_ptr[0] = new_insert_ptr;
+
+ return order;
+ }
+
+
+
diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c
new file mode 100644
index 00000000000000..6ad9250552bb92
--- /dev/null
+++ b/fs/reiserfs/inode.c
@@ -0,0 +1,2240 @@
+/*
+ * Copyright 2000-2002 by Hans Reiser, licensing governed by reiserfs/README
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/reiserfs_fs.h>
+#include <linux/locks.h>
+#include <linux/smp_lock.h>
+#include <asm/uaccess.h>
+#include <asm/unaligned.h>
+
+/* args for the create parameter of reiserfs_get_block */
+#define GET_BLOCK_NO_CREATE 0 /* don't create new blocks or convert tails */
+#define GET_BLOCK_CREATE 1 /* add anything you need to find block */
+#define GET_BLOCK_NO_HOLE 2 /* return -ENOENT for file holes */
+#define GET_BLOCK_READ_DIRECT 4 /* read the tail if indirect item not found */
+#define GET_BLOCK_NO_ISEM 8 /* i_sem is not held, don't preallocate */
+
+static int reiserfs_get_block (struct inode * inode, long block,
+ struct buffer_head * bh_result, int create);
+
+/* This spinlock guards inode pkey in private part of inode
+ against race between find_actor() vs reiserfs_read_inode2 */
+static spinlock_t keycopy_lock = SPIN_LOCK_UNLOCKED;
+
+void reiserfs_delete_inode (struct inode * inode)
+{
+ int jbegin_count = JOURNAL_PER_BALANCE_CNT * 2;
+ int windex ;
+ struct reiserfs_transaction_handle th ;
+
+
+ lock_kernel() ;
+
+ /* The = 0 happens when we abort creating a new inode for some reason like lack of space.. */
+ if (INODE_PKEY(inode)->k_objectid != 0) { /* also handles bad_inode case */
+ down (&inode->i_sem);
+
+ journal_begin(&th, inode->i_sb, jbegin_count) ;
+ reiserfs_update_inode_transaction(inode) ;
+ windex = push_journal_writer("delete_inode") ;
+
+ reiserfs_delete_object (&th, inode);
+ pop_journal_writer(windex) ;
+
+ journal_end(&th, inode->i_sb, jbegin_count) ;
+
+ up (&inode->i_sem);
+
+ /* all items of file are deleted, so we can remove "save" link */
+ remove_save_link (inode, 0/* not truncate */);
+ } else {
+ /* no object items are in the tree */
+ ;
+ }
+ clear_inode (inode); /* note this must go after the journal_end to prevent deadlock */
+ inode->i_blocks = 0;
+ unlock_kernel() ;
+}
+
+static void _make_cpu_key (struct cpu_key * key, int version, __u32 dirid, __u32 objectid,
+ loff_t offset, int type, int length )
+{
+ key->version = version;
+
+ key->on_disk_key.k_dir_id = dirid;
+ key->on_disk_key.k_objectid = objectid;
+ set_cpu_key_k_offset (key, offset);
+ set_cpu_key_k_type (key, type);
+ key->key_length = length;
+}
+
+
+/* take base of inode_key (it comes from inode always) (dirid, objectid) and version from an inode, set
+ offset and type of key */
+void make_cpu_key (struct cpu_key * key, const struct inode * inode, loff_t offset,
+ int type, int length )
+{
+ _make_cpu_key (key, get_inode_item_key_version (inode), le32_to_cpu (INODE_PKEY (inode)->k_dir_id),
+ le32_to_cpu (INODE_PKEY (inode)->k_objectid),
+ offset, type, length);
+}
+
+
+//
+// when key is 0, do not set version and short key
+//
+inline void make_le_item_head (struct item_head * ih, const struct cpu_key * key,
+ int version,
+ loff_t offset, int type, int length,
+ int entry_count/*or ih_free_space*/)
+{
+ if (key) {
+ ih->ih_key.k_dir_id = cpu_to_le32 (key->on_disk_key.k_dir_id);
+ ih->ih_key.k_objectid = cpu_to_le32 (key->on_disk_key.k_objectid);
+ }
+ put_ih_version( ih, version );
+ set_le_ih_k_offset (ih, offset);
+ set_le_ih_k_type (ih, type);
+ put_ih_item_len( ih, length );
+ /* set_ih_free_space (ih, 0);*/
+ // for directory items it is entry count, for directs and stat
+ // datas - 0xffff, for indirects - 0
+ put_ih_entry_count( ih, entry_count );
+}
+
+static void add_to_flushlist(struct inode *inode, struct buffer_head *bh) {
+ struct reiserfs_journal *j = SB_JOURNAL(inode->i_sb) ;
+
+ buffer_insert_list(bh, &j->j_dirty_buffers) ;
+}
+
+//
+// FIXME: we might cache recently accessed indirect item
+
+// Ugh. Not too eager for that....
+// I cut the code until such time as I see a convincing argument (benchmark).
+// I don't want a bloated inode struct..., and I don't like code complexity....
+
+/* cutting the code is fine, since it really isn't in use yet and is easy
+** to add back in. But, Vladimir has a really good idea here. Think
+** about what happens for reading a file. For each page,
+** The VFS layer calls reiserfs_readpage, who searches the tree to find
+** an indirect item. This indirect item has X number of pointers, where
+** X is a big number if we've done the block allocation right. But,
+** we only use one or two of these pointers during each call to readpage,
+** needlessly researching again later on.
+**
+** The size of the cache could be dynamic based on the size of the file.
+**
+** I'd also like to see us cache the location the stat data item, since
+** we are needlessly researching for that frequently.
+**
+** --chris
+*/
+
+/* If this page has a file tail in it, and
+** it was read in by get_block_create_0, the page data is valid,
+** but tail is still sitting in a direct item, and we can't write to
+** it. So, look through this page, and check all the mapped buffers
+** to make sure they have valid block numbers. Any that don't need
+** to be unmapped, so that block_prepare_write will correctly call
+** reiserfs_get_block to convert the tail into an unformatted node
+*/
+static inline void fix_tail_page_for_writing(struct page *page) {
+ struct buffer_head *head, *next, *bh ;
+
+ if (page && page->buffers) {
+ head = page->buffers ;
+ bh = head ;
+ do {
+ next = bh->b_this_page ;
+ if (buffer_mapped(bh) && bh->b_blocknr == 0) {
+ reiserfs_unmap_buffer(bh) ;
+ }
+ bh = next ;
+ } while (bh != head) ;
+ }
+}
+
+/* reiserfs_get_block does not need to allocate a block only if it has been
+ done already or non-hole position has been found in the indirect item */
+static inline int allocation_needed (int retval, b_blocknr_t allocated,
+ struct item_head * ih,
+ __u32 * item, int pos_in_item)
+{
+ if (allocated)
+ return 0;
+ if (retval == POSITION_FOUND && is_indirect_le_ih (ih) &&
+ get_block_num(item, pos_in_item))
+ return 0;
+ return 1;
+}
+
+static inline int indirect_item_found (int retval, struct item_head * ih)
+{
+ return (retval == POSITION_FOUND) && is_indirect_le_ih (ih);
+}
+
+
+static inline void set_block_dev_mapped (struct buffer_head * bh,
+ b_blocknr_t block, struct inode * inode)
+{
+ bh->b_dev = inode->i_dev;
+ bh->b_blocknr = block;
+ bh->b_state |= (1UL << BH_Mapped);
+}
+
+
+//
+// files which were created in the earlier version can not be longer,
+// than 2 gb
+//
+static int file_capable (struct inode * inode, long block)
+{
+ if (get_inode_item_key_version (inode) != KEY_FORMAT_3_5 || // it is new file.
+ block < (1 << (31 - inode->i_sb->s_blocksize_bits))) // old file, but 'block' is inside of 2gb
+ return 1;
+
+ return 0;
+}
+
+/*static*/ void restart_transaction(struct reiserfs_transaction_handle *th,
+ struct inode *inode, struct path *path) {
+ struct super_block *s = th->t_super ;
+ int len = th->t_blocks_allocated ;
+
+ pathrelse(path) ;
+ reiserfs_update_sd(th, inode) ;
+ journal_end(th, s, len) ;
+ journal_begin(th, s, len) ;
+ reiserfs_update_inode_transaction(inode) ;
+}
+
+// it is called by get_block when create == 0. Returns block number
+// for 'block'-th logical block of file. When it hits direct item it
+// returns 0 (being called from bmap) or read direct item into piece
+// of page (bh_result)
+
+// Please improve the english/clarity in the comment above, as it is
+// hard to understand.
+
+static int _get_block_create_0 (struct inode * inode, long block,
+ struct buffer_head * bh_result,
+ int args)
+{
+ INITIALIZE_PATH (path);
+ struct cpu_key key;
+ struct buffer_head * bh;
+ struct item_head * ih, tmp_ih;
+ int fs_gen ;
+ int blocknr;
+ char * p = NULL;
+ int chars;
+ int ret ;
+ int done = 0 ;
+ unsigned long offset ;
+
+ // prepare the key to look for the 'block'-th block of file
+ make_cpu_key (&key, inode,
+ (loff_t)block * inode->i_sb->s_blocksize + 1, TYPE_ANY, 3);
+
+research:
+ if (search_for_position_by_key (inode->i_sb, &key, &path) != POSITION_FOUND) {
+ pathrelse (&path);
+ if (p)
+ kunmap(bh_result->b_page) ;
+ // We do not return -ENOENT if there is a hole but page is uptodate, because it means
+ // That there is some MMAPED data associated with it that is yet to be written to disk.
+ if ((args & GET_BLOCK_NO_HOLE) && !Page_Uptodate(bh_result->b_page) ) {
+ return -ENOENT ;
+ }
+ return 0 ;
+ }
+
+ //
+ bh = get_last_bh (&path);
+ ih = get_ih (&path);
+ if (is_indirect_le_ih (ih)) {
+ __u32 * ind_item = (__u32 *)B_I_PITEM (bh, ih);
+
+ /* FIXME: here we could cache indirect item or part of it in
+ the inode to avoid search_by_key in case of subsequent
+ access to file */
+ blocknr = get_block_num(ind_item, path.pos_in_item) ;
+ ret = 0 ;
+ if (blocknr) {
+ bh_result->b_dev = inode->i_dev;
+ bh_result->b_blocknr = blocknr;
+ bh_result->b_state |= (1UL << BH_Mapped);
+ } else
+ // We do not return -ENOENT if there is a hole but page is uptodate, because it means
+ // That there is some MMAPED data associated with it that is yet to be written to disk.
+ if ((args & GET_BLOCK_NO_HOLE) && !Page_Uptodate(bh_result->b_page) ) {
+ ret = -ENOENT ;
+ }
+
+ pathrelse (&path);
+ if (p)
+ kunmap(bh_result->b_page) ;
+ return ret ;
+ }
+
+ // requested data are in direct item(s)
+ if (!(args & GET_BLOCK_READ_DIRECT)) {
+ // we are called by bmap. FIXME: we can not map block of file
+ // when it is stored in direct item(s)
+ pathrelse (&path);
+ if (p)
+ kunmap(bh_result->b_page) ;
+ return -ENOENT;
+ }
+
+ /* if we've got a direct item, and the buffer was uptodate,
+ ** we don't want to pull data off disk again. skip to the
+ ** end, where we map the buffer and return
+ */
+ if (buffer_uptodate(bh_result)) {
+ goto finished ;
+ } else
+ /*
+ ** grab_tail_page can trigger calls to reiserfs_get_block on up to date
+ ** pages without any buffers. If the page is up to date, we don't want
+ ** read old data off disk. Set the up to date bit on the buffer instead
+ ** and jump to the end
+ */
+ if (Page_Uptodate(bh_result->b_page)) {
+ mark_buffer_uptodate(bh_result, 1);
+ goto finished ;
+ }
+
+ // read file tail into part of page
+ offset = (cpu_key_k_offset(&key) - 1) & (PAGE_CACHE_SIZE - 1) ;
+ fs_gen = get_generation(inode->i_sb) ;
+ copy_item_head (&tmp_ih, ih);
+
+ /* we only want to kmap if we are reading the tail into the page.
+ ** this is not the common case, so we don't kmap until we are
+ ** sure we need to. But, this means the item might move if
+ ** kmap schedules
+ */
+ if (!p) {
+ p = (char *)kmap(bh_result->b_page) ;
+ if (fs_changed (fs_gen, inode->i_sb) && item_moved (&tmp_ih, &path)) {
+ goto research;
+ }
+ }
+ p += offset ;
+ memset (p, 0, inode->i_sb->s_blocksize);
+ do {
+ if (!is_direct_le_ih (ih)) {
+ BUG ();
+ }
+ /* make sure we don't read more bytes than actually exist in
+ ** the file. This can happen in odd cases where i_size isn't
+ ** correct, and when direct item padding results in a few
+ ** extra bytes at the end of the direct item
+ */
+ if ((le_ih_k_offset(ih) + path.pos_in_item) > inode->i_size)
+ break ;
+ if ((le_ih_k_offset(ih) - 1 + ih_item_len(ih)) > inode->i_size) {
+ chars = inode->i_size - (le_ih_k_offset(ih) - 1) - path.pos_in_item;
+ done = 1 ;
+ } else {
+ chars = ih_item_len(ih) - path.pos_in_item;
+ }
+ memcpy (p, B_I_PITEM (bh, ih) + path.pos_in_item, chars);
+
+ if (done)
+ break ;
+
+ p += chars;
+
+ if (PATH_LAST_POSITION (&path) != (B_NR_ITEMS (bh) - 1))
+ // we done, if read direct item is not the last item of
+ // node FIXME: we could try to check right delimiting key
+ // to see whether direct item continues in the right
+ // neighbor or rely on i_size
+ break;
+
+ // update key to look for the next piece
+ set_cpu_key_k_offset (&key, cpu_key_k_offset (&key) + chars);
+ if (search_for_position_by_key (inode->i_sb, &key, &path) != POSITION_FOUND)
+ // we read something from tail, even if now we got IO_ERROR
+ break;
+ bh = get_last_bh (&path);
+ ih = get_ih (&path);
+ } while (1);
+
+ flush_dcache_page(bh_result->b_page) ;
+ kunmap(bh_result->b_page) ;
+
+finished:
+ pathrelse (&path);
+ bh_result->b_blocknr = 0 ;
+ bh_result->b_dev = inode->i_dev;
+ mark_buffer_uptodate (bh_result, 1);
+ bh_result->b_state |= (1UL << BH_Mapped);
+ return 0;
+}
+
+
+// this is called to create file map. So, _get_block_create_0 will not
+// read direct item
+int reiserfs_bmap (struct inode * inode, long block,
+ struct buffer_head * bh_result, int create)
+{
+ if (!file_capable (inode, block))
+ return -EFBIG;
+
+ lock_kernel() ;
+ /* do not read the direct item */
+ _get_block_create_0 (inode, block, bh_result, 0) ;
+ unlock_kernel() ;
+ return 0;
+}
+
+/* special version of get_block that is only used by grab_tail_page right
+** now. It is sent to block_prepare_write, and when you try to get a
+** block past the end of the file (or a block from a hole) it returns
+** -ENOENT instead of a valid buffer. block_prepare_write expects to
+** be able to do i/o on the buffers returned, unless an error value
+** is also returned.
+**
+** So, this allows block_prepare_write to be used for reading a single block
+** in a page. Where it does not produce a valid page for holes, or past the
+** end of the file. This turns out to be exactly what we need for reading
+** tails for conversion.
+**
+** The point of the wrapper is forcing a certain value for create, even
+** though the VFS layer is calling this function with create==1. If you
+** don't want to send create == GET_BLOCK_NO_HOLE to reiserfs_get_block,
+** don't use this function.
+*/
+static int reiserfs_get_block_create_0 (struct inode * inode, long block,
+ struct buffer_head * bh_result, int create) {
+ return reiserfs_get_block(inode, block, bh_result, GET_BLOCK_NO_HOLE) ;
+}
+
+static int reiserfs_get_block_direct_io (struct inode * inode, long block,
+ struct buffer_head * bh_result, int create) {
+ int ret ;
+
+ bh_result->b_page = NULL;
+ ret = reiserfs_get_block(inode, block, bh_result, create) ;
+
+ /* don't allow direct io onto tail pages */
+ if (ret == 0 && buffer_mapped(bh_result) && bh_result->b_blocknr == 0) {
+ /* make sure future calls to the direct io funcs for this offset
+ ** in the file fail by unmapping the buffer
+ */
+ reiserfs_unmap_buffer(bh_result);
+ ret = -EINVAL ;
+ }
+ /* Possible unpacked tail. Flush the data before pages have
+ disappeared */
+ if (inode->u.reiserfs_i.i_flags & i_pack_on_close_mask) {
+ lock_kernel();
+ reiserfs_commit_for_inode(inode);
+ inode->u.reiserfs_i.i_flags &= ~i_pack_on_close_mask;
+ unlock_kernel();
+ }
+ return ret ;
+}
+
+
+/*
+** helper function for when reiserfs_get_block is called for a hole
+** but the file tail is still in a direct item
+** bh_result is the buffer head for the hole
+** tail_offset is the offset of the start of the tail in the file
+**
+** This calls prepare_write, which will start a new transaction
+** you should not be in a transaction, or have any paths held when you
+** call this.
+*/
+static int convert_tail_for_hole(struct inode *inode,
+ struct buffer_head *bh_result,
+ loff_t tail_offset) {
+ unsigned long index ;
+ unsigned long tail_end ;
+ unsigned long tail_start ;
+ struct page * tail_page ;
+ struct page * hole_page = bh_result->b_page ;
+ int retval = 0 ;
+
+ if ((tail_offset & (bh_result->b_size - 1)) != 1)
+ return -EIO ;
+
+ /* always try to read until the end of the block */
+ tail_start = tail_offset & (PAGE_CACHE_SIZE - 1) ;
+ tail_end = (tail_start | (bh_result->b_size - 1)) + 1 ;
+
+ index = tail_offset >> PAGE_CACHE_SHIFT ;
+ if ( !hole_page || index != hole_page->index) {
+ tail_page = grab_cache_page(inode->i_mapping, index) ;
+ retval = -ENOMEM;
+ if (!tail_page) {
+ goto out ;
+ }
+ } else {
+ tail_page = hole_page ;
+ }
+
+ /* we don't have to make sure the conversion did not happen while
+ ** we were locking the page because anyone that could convert
+ ** must first take i_sem.
+ **
+ ** We must fix the tail page for writing because it might have buffers
+ ** that are mapped, but have a block number of 0. This indicates tail
+ ** data that has been read directly into the page, and block_prepare_write
+ ** won't trigger a get_block in this case.
+ */
+ fix_tail_page_for_writing(tail_page) ;
+ retval = block_prepare_write(tail_page, tail_start, tail_end,
+ reiserfs_get_block) ;
+ if (retval)
+ goto unlock ;
+
+ /* tail conversion might change the data in the page */
+ flush_dcache_page(tail_page) ;
+
+ retval = generic_commit_write(NULL, tail_page, tail_start, tail_end) ;
+
+unlock:
+ if (tail_page != hole_page) {
+ UnlockPage(tail_page) ;
+ page_cache_release(tail_page) ;
+ }
+out:
+ return retval ;
+}
+
+static inline int _allocate_block(struct reiserfs_transaction_handle *th,
+ long block,
+ struct inode *inode,
+ b_blocknr_t *allocated_block_nr,
+ struct path * path,
+ int flags) {
+
+#ifdef REISERFS_PREALLOCATE
+ if (!(flags & GET_BLOCK_NO_ISEM)) {
+ return reiserfs_new_unf_blocknrs2(th, inode, allocated_block_nr, path, block);
+ }
+#endif
+ return reiserfs_new_unf_blocknrs (th, inode, allocated_block_nr, path, block);
+}
+
+static int reiserfs_get_block (struct inode * inode, long block,
+ struct buffer_head * bh_result, int create)
+{
+ int repeat, retval;
+ b_blocknr_t allocated_block_nr = 0;// b_blocknr_t is unsigned long
+ INITIALIZE_PATH(path);
+ int pos_in_item;
+ struct cpu_key key;
+ struct buffer_head * bh, * unbh = 0;
+ struct item_head * ih, tmp_ih;
+ __u32 * item;
+ int done;
+ int fs_gen;
+ int windex ;
+ struct reiserfs_transaction_handle th ;
+ /* space reserved in transaction batch:
+ . 3 balancings in direct->indirect conversion
+ . 1 block involved into reiserfs_update_sd()
+ XXX in practically impossible worst case direct2indirect()
+ can incur (much) more that 3 balancings. */
+ int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3 + 1;
+ int version;
+ int transaction_started = 0 ;
+ loff_t new_offset = (((loff_t)block) << inode->i_sb->s_blocksize_bits) + 1 ;
+
+ /* bad.... */
+ lock_kernel() ;
+ th.t_trans_id = 0 ;
+ version = get_inode_item_key_version (inode);
+
+ if (block < 0) {
+ unlock_kernel();
+ return -EIO;
+ }
+
+ if (!file_capable (inode, block)) {
+ unlock_kernel() ;
+ return -EFBIG;
+ }
+
+ /* if !create, we aren't changing the FS, so we don't need to
+ ** log anything, so we don't need to start a transaction
+ */
+ if (!(create & GET_BLOCK_CREATE)) {
+ int ret ;
+ /* find number of block-th logical block of the file */
+ ret = _get_block_create_0 (inode, block, bh_result,
+ create | GET_BLOCK_READ_DIRECT) ;
+ unlock_kernel() ;
+ return ret;
+ }
+
+ /* If file is of such a size, that it might have a tail and tails are enabled
+ ** we should mark it as possibly needing tail packing on close
+ */
+ if ( (have_large_tails (inode->i_sb) && inode->i_size < block_size (inode)*4) ||
+ (have_small_tails (inode->i_sb) && inode->i_size < block_size(inode)) )
+ inode->u.reiserfs_i.i_flags |= i_pack_on_close_mask;
+
+ windex = push_journal_writer("reiserfs_get_block") ;
+
+ /* set the key of the first byte in the 'block'-th block of file */
+ make_cpu_key (&key, inode, new_offset,
+ TYPE_ANY, 3/*key length*/);
+ if ((new_offset + inode->i_sb->s_blocksize - 1) > inode->i_size) {
+ journal_begin(&th, inode->i_sb, jbegin_count) ;
+ reiserfs_update_inode_transaction(inode) ;
+ transaction_started = 1 ;
+ }
+ research:
+
+ retval = search_for_position_by_key (inode->i_sb, &key, &path);
+ if (retval == IO_ERROR) {
+ retval = -EIO;
+ goto failure;
+ }
+
+ bh = get_last_bh (&path);
+ ih = get_ih (&path);
+ item = get_item (&path);
+ pos_in_item = path.pos_in_item;
+
+ fs_gen = get_generation (inode->i_sb);
+ copy_item_head (&tmp_ih, ih);
+
+ if (allocation_needed (retval, allocated_block_nr, ih, item, pos_in_item)) {
+ /* we have to allocate block for the unformatted node */
+ if (!transaction_started) {
+ pathrelse(&path) ;
+ journal_begin(&th, inode->i_sb, jbegin_count) ;
+ reiserfs_update_inode_transaction(inode) ;
+ transaction_started = 1 ;
+ goto research ;
+ }
+
+ repeat = _allocate_block(&th, block, inode, &allocated_block_nr, &path, create);
+
+ if (repeat == NO_DISK_SPACE) {
+ /* restart the transaction to give the journal a chance to free
+ ** some blocks. releases the path, so we have to go back to
+ ** research if we succeed on the second try
+ */
+ restart_transaction(&th, inode, &path) ;
+ repeat = _allocate_block(&th, block, inode, &allocated_block_nr, NULL, create);
+
+ if (repeat != NO_DISK_SPACE) {
+ goto research ;
+ }
+ retval = -ENOSPC;
+ goto failure;
+ }
+
+ if (fs_changed (fs_gen, inode->i_sb) && item_moved (&tmp_ih, &path)) {
+ goto research;
+ }
+ }
+
+ if (indirect_item_found (retval, ih)) {
+ b_blocknr_t unfm_ptr;
+ /* 'block'-th block is in the file already (there is
+ corresponding cell in some indirect item). But it may be
+ zero unformatted node pointer (hole) */
+ unfm_ptr = get_block_num (item, pos_in_item);
+ if (unfm_ptr == 0) {
+ /* use allocated block to plug the hole */
+ reiserfs_prepare_for_journal(inode->i_sb, bh, 1) ;
+ if (fs_changed (fs_gen, inode->i_sb) && item_moved (&tmp_ih, &path)) {
+ reiserfs_restore_prepared_buffer(inode->i_sb, bh) ;
+ goto research;
+ }
+ bh_result->b_state |= (1UL << BH_New);
+ put_block_num(item, pos_in_item, allocated_block_nr) ;
+ unfm_ptr = allocated_block_nr;
+ journal_mark_dirty (&th, inode->i_sb, bh);
+ inode->i_blocks += (inode->i_sb->s_blocksize / 512) ;
+ reiserfs_update_sd(&th, inode) ;
+ }
+ set_block_dev_mapped(bh_result, unfm_ptr, inode);
+ pathrelse (&path);
+ pop_journal_writer(windex) ;
+ if (transaction_started)
+ journal_end(&th, inode->i_sb, jbegin_count) ;
+
+ unlock_kernel() ;
+
+ /* the item was found, so new blocks were not added to the file
+ ** there is no need to make sure the inode is updated with this
+ ** transaction
+ */
+ return 0;
+ }
+
+ if (!transaction_started) {
+ /* if we don't pathrelse, we could vs-3050 on the buffer if
+ ** someone is waiting for it (they can't finish until the buffer
+ ** is released, we can start a new transaction until they finish)
+ */
+ pathrelse(&path) ;
+ journal_begin(&th, inode->i_sb, jbegin_count) ;
+ reiserfs_update_inode_transaction(inode) ;
+ transaction_started = 1 ;
+ goto research;
+ }
+
+ /* desired position is not found or is in the direct item. We have
+ to append file with holes up to 'block'-th block converting
+ direct items to indirect one if necessary */
+ done = 0;
+ do {
+ if (is_statdata_le_ih (ih)) {
+ __u32 unp = 0;
+ struct cpu_key tmp_key;
+
+ /* indirect item has to be inserted */
+ make_le_item_head (&tmp_ih, &key, version, 1, TYPE_INDIRECT,
+ UNFM_P_SIZE, 0/* free_space */);
+
+ if (cpu_key_k_offset (&key) == 1) {
+ /* we are going to add 'block'-th block to the file. Use
+ allocated block for that */
+ unp = cpu_to_le32 (allocated_block_nr);
+ set_block_dev_mapped (bh_result, allocated_block_nr, inode);
+ bh_result->b_state |= (1UL << BH_New);
+ done = 1;
+ }
+ tmp_key = key; // ;)
+ set_cpu_key_k_offset (&tmp_key, 1);
+ PATH_LAST_POSITION(&path) ++;
+
+ retval = reiserfs_insert_item (&th, &path, &tmp_key, &tmp_ih, (char *)&unp);
+ if (retval) {
+ reiserfs_free_block (&th, allocated_block_nr);
+ goto failure; // retval == -ENOSPC or -EIO or -EEXIST
+ }
+ if (unp)
+ inode->i_blocks += inode->i_sb->s_blocksize / 512;
+ //mark_tail_converted (inode);
+ } else if (is_direct_le_ih (ih)) {
+ /* direct item has to be converted */
+ loff_t tail_offset;
+
+ tail_offset = ((le_ih_k_offset (ih) - 1) & ~(inode->i_sb->s_blocksize - 1)) + 1;
+ if (tail_offset == cpu_key_k_offset (&key)) {
+ /* direct item we just found fits into block we have
+ to map. Convert it into unformatted node: use
+ bh_result for the conversion */
+ set_block_dev_mapped (bh_result, allocated_block_nr, inode);
+ unbh = bh_result;
+ done = 1;
+ } else {
+ /* we have to padd file tail stored in direct item(s)
+ up to block size and convert it to unformatted
+ node. FIXME: this should also get into page cache */
+
+ pathrelse(&path) ;
+ journal_end(&th, inode->i_sb, jbegin_count) ;
+ transaction_started = 0 ;
+
+ retval = convert_tail_for_hole(inode, bh_result, tail_offset) ;
+ if (retval) {
+ if ( retval != -ENOSPC )
+ reiserfs_warning(inode->i_sb, "clm-6004: convert tail failed inode %lu, error %d\n", inode->i_ino, retval) ;
+ if (allocated_block_nr) {
+ /* the bitmap, the super, and the stat data == 3 */
+ journal_begin(&th, inode->i_sb, 3) ;
+ reiserfs_free_block (&th, allocated_block_nr);
+ transaction_started = 1 ;
+ }
+ goto failure ;
+ }
+ goto research ;
+ }
+ retval = direct2indirect (&th, inode, &path, unbh, tail_offset);
+ if (retval) {
+ reiserfs_unmap_buffer(unbh);
+ reiserfs_free_block (&th, allocated_block_nr);
+ goto failure;
+ }
+ /* it is important the mark_buffer_uptodate is done after
+ ** the direct2indirect. The buffer might contain valid
+ ** data newer than the data on disk (read by readpage, changed,
+ ** and then sent here by writepage). direct2indirect needs
+ ** to know if unbh was already up to date, so it can decide
+ ** if the data in unbh needs to be replaced with data from
+ ** the disk
+ */
+ mark_buffer_uptodate (unbh, 1);
+
+ /* unbh->b_page == NULL in case of DIRECT_IO request, this means
+ buffer will disappear shortly, so it should not be added to
+ any of our lists.
+ */
+ if ( unbh->b_page ) {
+ /* we've converted the tail, so we must
+ ** flush unbh before the transaction commits
+ */
+ add_to_flushlist(inode, unbh) ;
+
+ /* mark it dirty now to prevent commit_write from adding
+ ** this buffer to the inode's dirty buffer list
+ */
+ __mark_buffer_dirty(unbh) ;
+ }
+
+ //inode->i_blocks += inode->i_sb->s_blocksize / 512;
+ //mark_tail_converted (inode);
+ } else {
+ /* append indirect item with holes if needed, when appending
+ pointer to 'block'-th block use block, which is already
+ allocated */
+ struct cpu_key tmp_key;
+ unp_t unf_single=0; // We use this in case we need to allocate only
+ // one block which is a fastpath
+ unp_t *un;
+ __u64 max_to_insert=MAX_ITEM_LEN(inode->i_sb->s_blocksize)/UNFM_P_SIZE;
+ __u64 blocks_needed;
+
+ RFALSE( pos_in_item != ih_item_len(ih) / UNFM_P_SIZE,
+ "vs-804: invalid position for append");
+ /* indirect item has to be appended, set up key of that position */
+ make_cpu_key (&tmp_key, inode,
+ le_key_k_offset (version, &(ih->ih_key)) + op_bytes_number (ih, inode->i_sb->s_blocksize),
+ //pos_in_item * inode->i_sb->s_blocksize,
+ TYPE_INDIRECT, 3);// key type is unimportant
+
+ blocks_needed = 1 + ((cpu_key_k_offset (&key) - cpu_key_k_offset (&tmp_key)) >> inode->i_sb->s_blocksize_bits);
+ RFALSE( blocks_needed < 0, "green-805: invalid offset");
+
+ if ( blocks_needed == 1 ) {
+ un = &unf_single;
+ } else {
+ un=kmalloc( min(blocks_needed,max_to_insert)*UNFM_P_SIZE,
+ GFP_ATOMIC); // We need to avoid scheduling.
+ if ( !un) {
+ un = &unf_single;
+ blocks_needed = 1;
+ max_to_insert = 0;
+ } else
+ memset(un, 0, UNFM_P_SIZE * min(blocks_needed,max_to_insert));
+ }
+ if ( blocks_needed <= max_to_insert) {
+ /* we are going to add target block to the file. Use allocated
+ block for that */
+ un[blocks_needed-1] = cpu_to_le32 (allocated_block_nr);
+ set_block_dev_mapped (bh_result, allocated_block_nr, inode);
+ bh_result->b_state |= (1UL << BH_New);
+ done = 1;
+ } else {
+ /* paste hole to the indirect item */
+ /* If kmalloc failed, max_to_insert becomes zero and it means we
+ only have space for one block */
+ blocks_needed=max_to_insert?max_to_insert:1;
+ }
+ retval = reiserfs_paste_into_item (&th, &path, &tmp_key, (char *)un, UNFM_P_SIZE * blocks_needed);
+
+ if (blocks_needed != 1)
+ kfree(un);
+
+ if (retval) {
+ reiserfs_free_block (&th, allocated_block_nr);
+ goto failure;
+ }
+ if (done) {
+ inode->i_blocks += inode->i_sb->s_blocksize / 512;
+ } else {
+ /* We need to mark new file size in case this function will be
+ interrupted/aborted later on. And we may do this only for
+ holes. */
+ inode->i_size += blocks_needed << inode->i_blkbits;
+ }
+ //mark_tail_converted (inode);
+ }
+
+ if (done == 1)
+ break;
+
+ /* this loop could log more blocks than we had originally asked
+ ** for. So, we have to allow the transaction to end if it is
+ ** too big or too full. Update the inode so things are
+ ** consistent if we crash before the function returns
+ **
+ ** release the path so that anybody waiting on the path before
+ ** ending their transaction will be able to continue.
+ */
+ if (journal_transaction_should_end(&th, th.t_blocks_allocated)) {
+ restart_transaction(&th, inode, &path) ;
+ }
+ /* inserting indirect pointers for a hole can take a
+ ** long time. reschedule if needed
+ */
+ if (current->need_resched)
+ schedule() ;
+
+ retval = search_for_position_by_key (inode->i_sb, &key, &path);
+ if (retval == IO_ERROR) {
+ retval = -EIO;
+ goto failure;
+ }
+ if (retval == POSITION_FOUND) {
+ reiserfs_warning (inode->i_sb, "vs-825: reiserfs_get_block: "
+ "%K should not be found\n", &key);
+ retval = -EEXIST;
+ if (allocated_block_nr)
+ reiserfs_free_block (&th, allocated_block_nr);
+ pathrelse(&path) ;
+ goto failure;
+ }
+ bh = get_last_bh (&path);
+ ih = get_ih (&path);
+ item = get_item (&path);
+ pos_in_item = path.pos_in_item;
+ } while (1);
+
+
+ retval = 0;
+ reiserfs_check_path(&path) ;
+
+ failure:
+ if (transaction_started) {
+ reiserfs_update_sd(&th, inode) ;
+ journal_end(&th, inode->i_sb, jbegin_count) ;
+ }
+ pop_journal_writer(windex) ;
+ unlock_kernel() ;
+ reiserfs_check_path(&path) ;
+ return retval;
+}
+
+
+//
+// BAD: new directories have stat data of new type and all other items
+// of old type. Version stored in the inode says about body items, so
+// in update_stat_data we can not rely on inode, but have to check
+// item version directly
+//
+
+// called by read_inode
+static void init_inode (struct inode * inode, struct path * path)
+{
+ struct buffer_head * bh;
+ struct item_head * ih;
+ __u32 rdev;
+ //int version = ITEM_VERSION_1;
+
+ bh = PATH_PLAST_BUFFER (path);
+ ih = PATH_PITEM_HEAD (path);
+
+ spin_lock(&keycopy_lock);
+ copy_key (INODE_PKEY (inode), &(ih->ih_key));
+ spin_unlock(&keycopy_lock);
+ inode->i_blksize = PAGE_SIZE;
+
+ INIT_LIST_HEAD(&inode->u.reiserfs_i.i_prealloc_list) ;
+
+ if (stat_data_v1 (ih)) {
+ struct stat_data_v1 * sd = (struct stat_data_v1 *)B_I_PITEM (bh, ih);
+ unsigned long blocks;
+
+ set_inode_item_key_version (inode, KEY_FORMAT_3_5);
+ set_inode_sd_version (inode, STAT_DATA_V1);
+ inode->i_mode = sd_v1_mode(sd);
+ inode->i_nlink = sd_v1_nlink(sd);
+ inode->i_uid = sd_v1_uid(sd);
+ inode->i_gid = sd_v1_gid(sd);
+ inode->i_size = sd_v1_size(sd);
+ inode->i_atime = sd_v1_atime(sd);
+ inode->i_mtime = sd_v1_mtime(sd);
+ inode->i_ctime = sd_v1_ctime(sd);
+
+ inode->i_blocks = sd_v1_blocks(sd);
+ inode->i_generation = le32_to_cpu (INODE_PKEY (inode)->k_dir_id);
+ blocks = (inode->i_size + 511) >> 9;
+ blocks = _ROUND_UP (blocks, inode->i_sb->s_blocksize >> 9);
+ if (inode->i_blocks > blocks) {
+ // there was a bug in <=3.5.23 when i_blocks could take negative
+ // values. Starting from 3.5.17 this value could even be stored in
+ // stat data. For such files we set i_blocks based on file
+ // size. Just 2 notes: this can be wrong for sparce files. On-disk value will be
+ // only updated if file's inode will ever change
+ inode->i_blocks = blocks;
+ }
+
+ rdev = sd_v1_rdev(sd);
+ inode->u.reiserfs_i.i_first_direct_byte = sd_v1_first_direct_byte(sd);
+ /* nopack is initially zero for v1 objects. For v2 objects,
+ nopack is initialised from sd_attrs */
+ inode->u.reiserfs_i.i_flags &= ~i_nopack_mask;
+ } else {
+ // new stat data found, but object may have old items
+ // (directories and symlinks)
+ struct stat_data * sd = (struct stat_data *)B_I_PITEM (bh, ih);
+
+ inode->i_mode = sd_v2_mode(sd);
+ inode->i_nlink = sd_v2_nlink(sd);
+ inode->i_uid = sd_v2_uid(sd);
+ inode->i_size = sd_v2_size(sd);
+ inode->i_gid = sd_v2_gid(sd);
+ inode->i_mtime = sd_v2_mtime(sd);
+ inode->i_atime = sd_v2_atime(sd);
+ inode->i_ctime = sd_v2_ctime(sd);
+ inode->i_blocks = sd_v2_blocks(sd);
+ rdev = sd_v2_rdev(sd);
+ if( S_ISCHR( inode -> i_mode ) || S_ISBLK( inode -> i_mode ) )
+ inode->i_generation = le32_to_cpu (INODE_PKEY (inode)->k_dir_id);
+ else
+ inode->i_generation = sd_v2_generation(sd);
+
+ if (S_ISDIR (inode->i_mode) || S_ISLNK (inode->i_mode))
+ set_inode_item_key_version (inode, KEY_FORMAT_3_5);
+ else
+ set_inode_item_key_version (inode, KEY_FORMAT_3_6);
+
+ set_inode_sd_version (inode, STAT_DATA_V2);
+ /* read persistent inode attributes from sd and initalise
+ generic inode flags from them */
+ inode -> u.reiserfs_i.i_attrs = sd_v2_attrs( sd );
+ sd_attrs_to_i_attrs( sd_v2_attrs( sd ), inode );
+ }
+
+
+ pathrelse (path);
+ if (S_ISREG (inode->i_mode)) {
+ inode->i_op = &reiserfs_file_inode_operations;
+ inode->i_fop = &reiserfs_file_operations;
+ inode->i_mapping->a_ops = &reiserfs_address_space_operations ;
+ } else if (S_ISDIR (inode->i_mode)) {
+ inode->i_op = &reiserfs_dir_inode_operations;
+ inode->i_fop = &reiserfs_dir_operations;
+ } else if (S_ISLNK (inode->i_mode)) {
+ inode->i_op = &page_symlink_inode_operations;
+ inode->i_mapping->a_ops = &reiserfs_address_space_operations;
+ } else {
+ inode->i_blocks = 0;
+ init_special_inode(inode, inode->i_mode, rdev) ;
+ }
+}
+
+
+// update new stat data with inode fields
+static void inode2sd (void * sd, struct inode * inode)
+{
+ struct stat_data * sd_v2 = (struct stat_data *)sd;
+ __u16 flags;
+
+ set_sd_v2_mode(sd_v2, inode->i_mode );
+ set_sd_v2_nlink(sd_v2, inode->i_nlink );
+ set_sd_v2_uid(sd_v2, inode->i_uid );
+ set_sd_v2_size(sd_v2, inode->i_size );
+ set_sd_v2_gid(sd_v2, inode->i_gid );
+ set_sd_v2_mtime(sd_v2, inode->i_mtime );
+ set_sd_v2_atime(sd_v2, inode->i_atime );
+ set_sd_v2_ctime(sd_v2, inode->i_ctime );
+ set_sd_v2_blocks(sd_v2, inode->i_blocks );
+ if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+ set_sd_v2_rdev(sd_v2, inode->i_rdev );
+ else
+ set_sd_v2_generation(sd_v2, inode->i_generation);
+ flags = inode -> u.reiserfs_i.i_attrs;
+ i_attrs_to_sd_attrs( inode, &flags );
+ set_sd_v2_attrs( sd_v2, flags );
+}
+
+
+// used to copy inode's fields to old stat data
+static void inode2sd_v1 (void * sd, struct inode * inode)
+{
+ struct stat_data_v1 * sd_v1 = (struct stat_data_v1 *)sd;
+
+ set_sd_v1_mode(sd_v1, inode->i_mode );
+ set_sd_v1_uid(sd_v1, inode->i_uid );
+ set_sd_v1_gid(sd_v1, inode->i_gid );
+ set_sd_v1_nlink(sd_v1, inode->i_nlink );
+ set_sd_v1_size(sd_v1, inode->i_size );
+ set_sd_v1_atime(sd_v1, inode->i_atime );
+ set_sd_v1_ctime(sd_v1, inode->i_ctime );
+ set_sd_v1_mtime(sd_v1, inode->i_mtime );
+
+ if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+ set_sd_v1_rdev(sd_v1, inode->i_rdev );
+ else
+ set_sd_v1_blocks(sd_v1, inode->i_blocks );
+
+ // Sigh. i_first_direct_byte is back
+ set_sd_v1_first_direct_byte(sd_v1, inode->u.reiserfs_i.i_first_direct_byte);
+}
+
+
+/* NOTE, you must prepare the buffer head before sending it here,
+** and then log it after the call
+*/
+static void update_stat_data (struct path * path, struct inode * inode)
+{
+ struct buffer_head * bh;
+ struct item_head * ih;
+
+ bh = PATH_PLAST_BUFFER (path);
+ ih = PATH_PITEM_HEAD (path);
+
+ if (!is_statdata_le_ih (ih))
+ reiserfs_panic (inode->i_sb, "vs-13065: update_stat_data: key %k, found item %h",
+ INODE_PKEY (inode), ih);
+
+ if (stat_data_v1 (ih)) {
+ // path points to old stat data
+ inode2sd_v1 (B_I_PITEM (bh, ih), inode);
+ } else {
+ inode2sd (B_I_PITEM (bh, ih), inode);
+ }
+
+ return;
+}
+
+
+void reiserfs_update_sd (struct reiserfs_transaction_handle *th,
+ struct inode * inode)
+{
+ struct cpu_key key;
+ INITIALIZE_PATH(path);
+ struct buffer_head *bh ;
+ int fs_gen ;
+ struct item_head *ih, tmp_ih ;
+ int retval;
+
+ make_cpu_key (&key, inode, SD_OFFSET, TYPE_STAT_DATA, 3);//key type is unimportant
+
+ for(;;) {
+ int pos;
+ /* look for the object's stat data */
+ retval = search_item (inode->i_sb, &key, &path);
+ if (retval == IO_ERROR) {
+ reiserfs_warning (inode->i_sb, "vs-13050: reiserfs_update_sd: "
+ "i/o failure occurred trying to update %K stat data\n",
+ &key);
+ return;
+ }
+ if (retval == ITEM_NOT_FOUND) {
+ pos = PATH_LAST_POSITION (&path);
+ pathrelse(&path) ;
+ if (inode->i_nlink == 0) {
+ /*printk ("vs-13050: reiserfs_update_sd: i_nlink == 0, stat data not found\n");*/
+ return;
+ }
+ reiserfs_warning (inode->i_sb, "vs-13060: reiserfs_update_sd: "
+ "stat data of object %k (nlink == %d) not found (pos %d)\n",
+ INODE_PKEY (inode), inode->i_nlink, pos);
+ reiserfs_check_path(&path) ;
+ return;
+ }
+
+ /* sigh, prepare_for_journal might schedule. When it schedules the
+ ** FS might change. We have to detect that, and loop back to the
+ ** search if the stat data item has moved
+ */
+ bh = get_last_bh(&path) ;
+ ih = get_ih(&path) ;
+ copy_item_head (&tmp_ih, ih);
+ fs_gen = get_generation (inode->i_sb);
+ reiserfs_prepare_for_journal(inode->i_sb, bh, 1) ;
+ if (fs_changed (fs_gen, inode->i_sb) && item_moved(&tmp_ih, &path)) {
+ reiserfs_restore_prepared_buffer(inode->i_sb, bh) ;
+ continue ; /* Stat_data item has been moved after scheduling. */
+ }
+ break;
+ }
+ update_stat_data (&path, inode);
+ journal_mark_dirty(th, th->t_super, bh) ;
+ pathrelse (&path);
+ return;
+}
+
+/* We need to clear inode key in private part of inode to avoid races between
+ blocking iput, knfsd and file deletion with creating of safelinks.*/
+static void reiserfs_make_bad_inode(struct inode *inode) {
+ memset(INODE_PKEY(inode), 0, KEY_SIZE);
+ make_bad_inode(inode);
+}
+
+void reiserfs_read_inode(struct inode *inode) {
+ reiserfs_make_bad_inode(inode) ;
+}
+
+
+/* looks for stat data in the tree, and fills up the fields of in-core
+ inode stat data fields */
+void reiserfs_read_inode2 (struct inode * inode, void *p)
+{
+ INITIALIZE_PATH (path_to_sd);
+ struct cpu_key key;
+ struct reiserfs_iget4_args *args = (struct reiserfs_iget4_args *)p ;
+ unsigned long dirino;
+ int retval;
+
+ if (!p) {
+ reiserfs_make_bad_inode(inode) ;
+ return;
+ }
+
+ dirino = args->objectid ;
+
+ /* set version 1, version 2 could be used too, because stat data
+ key is the same in both versions */
+ key.version = KEY_FORMAT_3_5;
+ key.on_disk_key.k_dir_id = dirino;
+ key.on_disk_key.k_objectid = inode->i_ino;
+ key.on_disk_key.u.k_offset_v1.k_offset = SD_OFFSET;
+ key.on_disk_key.u.k_offset_v1.k_uniqueness = SD_UNIQUENESS;
+
+ /* look for the object's stat data */
+ retval = search_item (inode->i_sb, &key, &path_to_sd);
+ if (retval == IO_ERROR) {
+ reiserfs_warning (inode->i_sb, "vs-13070: reiserfs_read_inode2: "
+ "i/o failure occurred trying to find stat data of %K\n",
+ &key);
+ reiserfs_make_bad_inode(inode) ;
+ return;
+ }
+ if (retval != ITEM_FOUND) {
+ /* a stale NFS handle can trigger this without it being an error */
+ pathrelse (&path_to_sd);
+ reiserfs_make_bad_inode(inode) ;
+ inode->i_nlink = 0;
+ return;
+ }
+
+ init_inode (inode, &path_to_sd);
+
+ /* It is possible that knfsd is trying to access inode of a file
+ that is being removed from the disk by some other thread. As we
+ update sd on unlink all that is required is to check for nlink
+ here. This bug was first found by Sizif when debugging
+ SquidNG/Butterfly, forgotten, and found again after Philippe
+ Gramoulle <philippe.gramoulle@mmania.com> reproduced it.
+
+ More logical fix would require changes in fs/inode.c:iput() to
+ remove inode from hash-table _after_ fs cleaned disk stuff up and
+ in iget() to return NULL if I_FREEING inode is found in
+ hash-table. */
+ /* Currently there is one place where it's ok to meet inode with
+ nlink==0: processing of open-unlinked and half-truncated files
+ during mount (fs/reiserfs/super.c:finish_unfinished()). */
+ if( ( inode -> i_nlink == 0 ) &&
+ ! inode -> i_sb -> u.reiserfs_sb.s_is_unlinked_ok ) {
+ reiserfs_warning( inode->i_sb, "vs-13075: reiserfs_read_inode2: "
+ "dead inode read from disk %K. "
+ "This is likely to be race with knfsd. Ignore\n",
+ &key );
+ reiserfs_make_bad_inode( inode );
+ }
+
+ reiserfs_check_path(&path_to_sd) ; /* init inode should be relsing */
+
+}
+
+/**
+ * reiserfs_find_actor() - "find actor" reiserfs supplies to iget4().
+ *
+ * @inode: inode from hash table to check
+ * @inode_no: inode number we are looking for
+ * @opaque: "cookie" passed to iget4(). This is &reiserfs_iget4_args.
+ *
+ * This function is called by iget4() to distinguish reiserfs inodes
+ * having the same inode numbers. Such inodes can only exist due to some
+ * error condition. One of them should be bad. Inodes with identical
+ * inode numbers (objectids) are distinguished by parent directory ids.
+ *
+ */
+static int reiserfs_find_actor( struct inode *inode,
+ unsigned long inode_no, void *opaque )
+{
+ struct reiserfs_iget4_args *args;
+ int retval;
+
+ args = opaque;
+ /* We protect against possible parallel init_inode() on another CPU here. */
+ spin_lock(&keycopy_lock);
+ /* args is already in CPU order */
+ if (le32_to_cpu(INODE_PKEY(inode)->k_dir_id) == args -> objectid)
+ retval = 1;
+ else
+ /* If The key does not match, lets see if we are racing
+ with another iget4, that already progressed so far
+ to reiserfs_read_inode2() and was preempted in
+ call to search_by_key(). The signs of that are:
+ Inode is locked
+ dirid and object id are zero (not yet initialized)*/
+ retval = (inode->i_state & I_LOCK) &&
+ !INODE_PKEY(inode)->k_dir_id &&
+ !INODE_PKEY(inode)->k_objectid;
+
+ spin_unlock(&keycopy_lock);
+ return retval;
+}
+
+struct inode * reiserfs_iget (struct super_block * s, const struct cpu_key * key)
+{
+ struct inode * inode;
+ struct reiserfs_iget4_args args ;
+
+ args.objectid = key->on_disk_key.k_dir_id ;
+ inode = iget4 (s, key->on_disk_key.k_objectid,
+ reiserfs_find_actor, (void *)(&args));
+ if (!inode)
+ return ERR_PTR(-ENOMEM) ;
+
+ if (comp_short_keys (INODE_PKEY (inode), key) || is_bad_inode (inode)) {
+ /* either due to i/o error or a stale NFS handle */
+ iput (inode);
+ inode = 0;
+ }
+ return inode;
+}
+
+struct dentry *reiserfs_fh_to_dentry(struct super_block *sb, __u32 *data,
+ int len, int fhtype, int parent) {
+ struct cpu_key key ;
+ struct inode *inode = NULL ;
+ struct list_head *lp;
+ struct dentry *result;
+
+ /* fhtype happens to reflect the number of u32s encoded.
+ * due to a bug in earlier code, fhtype might indicate there
+ * are more u32s then actually fitted.
+ * so if fhtype seems to be more than len, reduce fhtype.
+ * Valid types are:
+ * 2 - objectid + dir_id - legacy support
+ * 3 - objectid + dir_id + generation
+ * 4 - objectid + dir_id + objectid and dirid of parent - legacy
+ * 5 - objectid + dir_id + generation + objectid and dirid of parent
+ * 6 - as above plus generation of directory
+ * 6 does not fit in NFSv2 handles
+ */
+ if (fhtype > len) {
+ if (fhtype != 6 || len != 5)
+ reiserfs_warning(sb, "nfsd/reiserfs, fhtype=%d, len=%d - odd\n",
+ fhtype, len);
+ fhtype = 5;
+ }
+ if (fhtype < 2 || (parent && fhtype < 4))
+ goto out ;
+
+ if (! parent) {
+ /* this works for handles from old kernels because the default
+ ** reiserfs generation number is the packing locality.
+ */
+ key.on_disk_key.k_objectid = data[0] ;
+ key.on_disk_key.k_dir_id = data[1] ;
+ inode = reiserfs_iget(sb, &key) ;
+ if (inode && !IS_ERR(inode) && (fhtype == 3 || fhtype >= 5) &&
+ data[2] != inode->i_generation) {
+ iput(inode) ;
+ inode = NULL ;
+ }
+ } else {
+ key.on_disk_key.k_objectid = data[fhtype>=5?3:2] ;
+ key.on_disk_key.k_dir_id = data[fhtype>=5?4:3] ;
+ inode = reiserfs_iget(sb, &key) ;
+ if (inode && !IS_ERR(inode) && fhtype == 6 &&
+ data[5] != inode->i_generation) {
+ iput(inode) ;
+ inode = NULL ;
+ }
+ }
+out:
+ if (IS_ERR(inode))
+ return ERR_PTR(PTR_ERR(inode));
+ if (!inode)
+ return ERR_PTR(-ESTALE) ;
+
+ /* now to find a dentry.
+ * If possible, get a well-connected one
+ */
+ spin_lock(&dcache_lock);
+ for (lp = inode->i_dentry.next; lp != &inode->i_dentry ; lp=lp->next) {
+ result = list_entry(lp,struct dentry, d_alias);
+ if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED)) {
+ dget_locked(result);
+ result->d_vfs_flags |= DCACHE_REFERENCED;
+ spin_unlock(&dcache_lock);
+ iput(inode);
+ return result;
+ }
+ }
+ spin_unlock(&dcache_lock);
+ result = d_alloc_root(inode);
+ if (result == NULL) {
+ iput(inode);
+ return ERR_PTR(-ENOMEM);
+ }
+ result->d_flags |= DCACHE_NFSD_DISCONNECTED;
+ return result;
+
+}
+
+int reiserfs_dentry_to_fh(struct dentry *dentry, __u32 *data, int *lenp, int need_parent) {
+ struct inode *inode = dentry->d_inode ;
+ int maxlen = *lenp;
+
+ if (maxlen < 3)
+ return 255 ;
+
+ data[0] = inode->i_ino ;
+ data[1] = le32_to_cpu(INODE_PKEY (inode)->k_dir_id) ;
+ data[2] = inode->i_generation ;
+ *lenp = 3 ;
+ /* no room for directory info? return what we've stored so far */
+ if (maxlen < 5 || ! need_parent)
+ return 3 ;
+
+ inode = dentry->d_parent->d_inode ;
+ data[3] = inode->i_ino ;
+ data[4] = le32_to_cpu(INODE_PKEY (inode)->k_dir_id) ;
+ *lenp = 5 ;
+ if (maxlen < 6)
+ return 5 ;
+ data[5] = inode->i_generation ;
+ *lenp = 6 ;
+ return 6 ;
+}
+
+
+/* looks for stat data, then copies fields to it, marks the buffer
+ containing stat data as dirty */
+/* reiserfs inodes are never really dirty, since the dirty inode call
+** always logs them. This call allows the VFS inode marking routines
+** to properly mark inodes for datasync and such, but only actually
+** does something when called for a synchronous update.
+*/
+void reiserfs_write_inode (struct inode * inode, int do_sync) {
+ struct reiserfs_transaction_handle th ;
+ int jbegin_count = 1 ;
+
+ if (inode->i_sb->s_flags & MS_RDONLY) {
+ reiserfs_warning(inode->i_sb, "clm-6005: writing inode %lu on readonly FS\n",
+ inode->i_ino) ;
+ return ;
+ }
+ /* memory pressure can sometimes initiate write_inode calls with sync == 1,
+ ** these cases are just when the system needs ram, not when the
+ ** inode needs to reach disk for safety, and they can safely be
+ ** ignored because the altered inode has already been logged.
+ */
+ if (do_sync && !(current->flags & PF_MEMALLOC)) {
+ lock_kernel() ;
+ journal_begin(&th, inode->i_sb, jbegin_count) ;
+ reiserfs_update_sd (&th, inode);
+ journal_end_sync(&th, inode->i_sb, jbegin_count) ;
+ unlock_kernel() ;
+ }
+}
+
+/* FIXME: no need any more. right? */
+int reiserfs_sync_inode (struct reiserfs_transaction_handle *th, struct inode * inode)
+{
+ int err = 0;
+
+ reiserfs_update_sd (th, inode);
+ return err;
+}
+
+
+/* stat data of new object is inserted already, this inserts the item
+ containing "." and ".." entries */
+static int reiserfs_new_directory (struct reiserfs_transaction_handle *th,
+ struct item_head * ih, struct path * path,
+ const struct inode * dir)
+{
+ struct super_block * sb = th->t_super;
+ char empty_dir [EMPTY_DIR_SIZE];
+ char * body = empty_dir;
+ struct cpu_key key;
+ int retval;
+
+ _make_cpu_key (&key, KEY_FORMAT_3_5, le32_to_cpu (ih->ih_key.k_dir_id),
+ le32_to_cpu (ih->ih_key.k_objectid), DOT_OFFSET, TYPE_DIRENTRY, 3/*key length*/);
+
+ /* compose item head for new item. Directories consist of items of
+ old type (ITEM_VERSION_1). Do not set key (second arg is 0), it
+ is done by reiserfs_new_inode */
+ if (old_format_only (sb)) {
+ make_le_item_head (ih, 0, KEY_FORMAT_3_5, DOT_OFFSET, TYPE_DIRENTRY, EMPTY_DIR_SIZE_V1, 2);
+
+ make_empty_dir_item_v1 (body, ih->ih_key.k_dir_id, ih->ih_key.k_objectid,
+ INODE_PKEY (dir)->k_dir_id,
+ INODE_PKEY (dir)->k_objectid );
+ } else {
+ make_le_item_head (ih, 0, KEY_FORMAT_3_5, DOT_OFFSET, TYPE_DIRENTRY, EMPTY_DIR_SIZE, 2);
+
+ make_empty_dir_item (body, ih->ih_key.k_dir_id, ih->ih_key.k_objectid,
+ INODE_PKEY (dir)->k_dir_id,
+ INODE_PKEY (dir)->k_objectid );
+ }
+
+ /* look for place in the tree for new item */
+ retval = search_item (sb, &key, path);
+ if (retval == IO_ERROR) {
+ reiserfs_warning (sb, "vs-13080: reiserfs_new_directory: "
+ "i/o failure occurred creating new directory\n");
+ return -EIO;
+ }
+ if (retval == ITEM_FOUND) {
+ pathrelse (path);
+ reiserfs_warning (sb, "vs-13070: reiserfs_new_directory: "
+ "object with this key exists (%k)\n", &(ih->ih_key));
+ return -EEXIST;
+ }
+
+ /* insert item, that is empty directory item */
+ return reiserfs_insert_item (th, path, &key, ih, body);
+}
+
+
+/* stat data of object has been inserted, this inserts the item
+ containing the body of symlink */
+static int reiserfs_new_symlink (struct reiserfs_transaction_handle *th,
+ struct item_head * ih,
+ struct path * path, const char * symname, int item_len)
+{
+ struct super_block * sb = th->t_super;
+ struct cpu_key key;
+ int retval;
+
+ _make_cpu_key (&key, KEY_FORMAT_3_5,
+ le32_to_cpu (ih->ih_key.k_dir_id),
+ le32_to_cpu (ih->ih_key.k_objectid),
+ 1, TYPE_DIRECT, 3/*key length*/);
+
+ make_le_item_head (ih, 0, KEY_FORMAT_3_5, 1, TYPE_DIRECT, item_len, 0/*free_space*/);
+
+ /* look for place in the tree for new item */
+ retval = search_item (sb, &key, path);
+ if (retval == IO_ERROR) {
+ reiserfs_warning (sb, "vs-13080: reiserfs_new_symlinik: "
+ "i/o failure occurred creating new symlink\n");
+ return -EIO;
+ }
+ if (retval == ITEM_FOUND) {
+ pathrelse (path);
+ reiserfs_warning (sb, "vs-13080: reiserfs_new_symlink: "
+ "object with this key exists (%k)\n", &(ih->ih_key));
+ return -EEXIST;
+ }
+
+ /* insert item, that is body of symlink */
+ return reiserfs_insert_item (th, path, &key, ih, symname);
+}
+
+
+/* inserts the stat data into the tree, and then calls
+ reiserfs_new_directory (to insert ".", ".." item if new object is
+ directory) or reiserfs_new_symlink (to insert symlink body if new
+ object is symlink) or nothing (if new object is regular file)
+
+ NOTE! uid and gid must already be set in the inode. If we return
+ non-zero due to an error, we have to drop the quota previously allocated
+ for the fresh inode. This can only be done outside a transaction, so
+ if we return non-zero, we also end the transaction.
+
+ */
+int reiserfs_new_inode (struct reiserfs_transaction_handle *th,
+ struct inode * dir, int mode,
+ const char * symname,
+ /* 0 for regular, EMTRY_DIR_SIZE for dirs,
+ strlen (symname) for symlinks) */
+ int i_size,
+ struct dentry *dentry,
+ struct inode *inode)
+{
+ struct super_block * sb;
+ INITIALIZE_PATH (path_to_key);
+ struct cpu_key key;
+ struct item_head ih;
+ struct stat_data sd;
+ int retval;
+ int err ;
+
+ if (!dir || !dir->i_nlink) {
+ err = -EPERM ;
+ goto out_bad_inode ;
+ }
+
+ sb = dir->i_sb;
+ inode -> u.reiserfs_i.i_attrs =
+ dir -> u.reiserfs_i.i_attrs & REISERFS_INHERIT_MASK;
+ sd_attrs_to_i_attrs( inode -> u.reiserfs_i.i_attrs, inode );
+
+ /* symlink cannot be immutable or append only, right? */
+ if( S_ISLNK( inode -> i_mode ) )
+ inode -> i_flags &= ~ ( S_IMMUTABLE | S_APPEND );
+
+ /* item head of new item */
+ ih.ih_key.k_dir_id = INODE_PKEY (dir)->k_objectid;
+ ih.ih_key.k_objectid = cpu_to_le32 (reiserfs_get_unused_objectid (th));
+ if (!ih.ih_key.k_objectid) {
+ err = -ENOMEM ;
+ goto out_bad_inode ;
+ }
+ if (old_format_only (sb))
+ /* not a perfect generation count, as object ids can be reused, but this
+ ** is as good as reiserfs can do right now.
+ ** note that the private part of inode isn't filled in yet, we have
+ ** to use the directory.
+ */
+ inode->i_generation = le32_to_cpu (INODE_PKEY (dir)->k_objectid);
+ else
+#if defined( USE_INODE_GENERATION_COUNTER )
+ inode->i_generation =
+ le32_to_cpu( sb -> u.reiserfs_sb.s_rs -> s_inode_generation );
+#else
+ inode->i_generation = ++event;
+#endif
+ /* fill stat data */
+ inode->i_nlink = (S_ISDIR (mode) ? 2 : 1);
+
+ /* uid and gid must already be set by the caller for quota init */
+
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+ inode->i_size = i_size;
+ inode->i_blocks = (inode->i_size + 511) >> 9;
+ inode->u.reiserfs_i.i_first_direct_byte = S_ISLNK(mode) ? 1 :
+ U32_MAX/*NO_BYTES_IN_DIRECT_ITEM*/;
+
+ INIT_LIST_HEAD(&inode->u.reiserfs_i.i_prealloc_list) ;
+
+ if (old_format_only (sb))
+ make_le_item_head (&ih, 0, KEY_FORMAT_3_5, SD_OFFSET, TYPE_STAT_DATA, SD_V1_SIZE, MAX_US_INT);
+ else
+ make_le_item_head (&ih, 0, KEY_FORMAT_3_6, SD_OFFSET, TYPE_STAT_DATA, SD_SIZE, MAX_US_INT);
+
+ /* key to search for correct place for new stat data */
+ _make_cpu_key (&key, KEY_FORMAT_3_6, le32_to_cpu (ih.ih_key.k_dir_id),
+ le32_to_cpu (ih.ih_key.k_objectid), SD_OFFSET, TYPE_STAT_DATA, 3/*key length*/);
+
+ /* find proper place for inserting of stat data */
+ retval = search_item (sb, &key, &path_to_key);
+ if (retval == IO_ERROR) {
+ err = -EIO;
+ goto out_bad_inode;
+ }
+ if (retval == ITEM_FOUND) {
+ pathrelse (&path_to_key);
+ err = -EEXIST;
+ goto out_bad_inode;
+ }
+
+ if (old_format_only (sb)) {
+ if (inode->i_uid & ~0xffff || inode->i_gid & ~0xffff) {
+ pathrelse (&path_to_key);
+ /* i_uid or i_gid is too big to be stored in stat data v3.5 */
+ err = -EINVAL;
+ goto out_bad_inode;
+ }
+ inode2sd_v1 (&sd, inode);
+ } else
+ inode2sd (&sd, inode);
+
+ // these do not go to on-disk stat data
+ inode->i_ino = le32_to_cpu (ih.ih_key.k_objectid);
+ inode->i_blksize = PAGE_SIZE;
+ inode->i_dev = sb->s_dev;
+
+ // store in in-core inode the key of stat data and version all
+ // object items will have (directory items will have old offset
+ // format, other new objects will consist of new items)
+ memcpy (INODE_PKEY (inode), &(ih.ih_key), KEY_SIZE);
+ if (old_format_only (sb) || S_ISDIR(mode) || S_ISLNK(mode))
+ set_inode_item_key_version (inode, KEY_FORMAT_3_5);
+ else
+ set_inode_item_key_version (inode, KEY_FORMAT_3_6);
+ if (old_format_only (sb))
+ set_inode_sd_version (inode, STAT_DATA_V1);
+ else
+ set_inode_sd_version (inode, STAT_DATA_V2);
+
+ /* insert the stat data into the tree */
+#ifdef DISPLACE_NEW_PACKING_LOCALITIES
+ if (dir->u.reiserfs_i.new_packing_locality)
+ th->displace_new_blocks = 1;
+#endif
+ retval = reiserfs_insert_item (th, &path_to_key, &key, &ih, (char *)(&sd));
+ if (retval) {
+ reiserfs_check_path(&path_to_key) ;
+ err = retval;
+ goto out_bad_inode;
+ }
+
+#ifdef DISPLACE_NEW_PACKING_LOCALITIES
+ if (!th->displace_new_blocks)
+ dir->u.reiserfs_i.new_packing_locality = 0;
+#endif
+ if (S_ISDIR(mode)) {
+ /* insert item with "." and ".." */
+ retval = reiserfs_new_directory (th, &ih, &path_to_key, dir);
+ }
+
+ if (S_ISLNK(mode)) {
+ /* insert body of symlink */
+ if (!old_format_only (sb))
+ i_size = ROUND_UP(i_size);
+ retval = reiserfs_new_symlink (th, &ih, &path_to_key, symname, i_size);
+ }
+ if (retval) {
+ err = retval;
+ reiserfs_check_path(&path_to_key) ;
+ journal_end(th, th->t_super, th->t_blocks_allocated) ;
+ goto out_inserted_sd;
+ }
+
+ insert_inode_hash (inode);
+ reiserfs_update_sd(th, inode) ;
+ reiserfs_check_path(&path_to_key) ;
+
+ return 0;
+out_bad_inode:
+ /* Invalidate the object, nothing was inserted yet */
+ INODE_PKEY(inode)->k_objectid = 0;
+
+ /* dquot_drop must be done outside a transaction */
+ journal_end(th, th->t_super, th->t_blocks_allocated) ;
+ make_bad_inode(inode);
+
+out_inserted_sd:
+ inode->i_nlink = 0;
+ th->t_trans_id = 0 ; /* so the caller can't use this handle later */
+ iput(inode) ;
+ return err;
+}
+
+/*
+** finds the tail page in the page cache,
+** reads the last block in.
+**
+** On success, page_result is set to a locked, pinned page, and bh_result
+** is set to an up to date buffer for the last block in the file. returns 0.
+**
+** tail conversion is not done, so bh_result might not be valid for writing
+** check buffer_mapped(bh_result) and bh_result->b_blocknr != 0 before
+** trying to write the block.
+**
+** on failure, nonzero is returned, page_result and bh_result are untouched.
+*/
+static int grab_tail_page(struct inode *p_s_inode,
+ struct page **page_result,
+ struct buffer_head **bh_result) {
+
+ /* we want the page with the last byte in the file,
+ ** not the page that will hold the next byte for appending
+ */
+ unsigned long index = (p_s_inode->i_size-1) >> PAGE_CACHE_SHIFT ;
+ unsigned long pos = 0 ;
+ unsigned long start = 0 ;
+ unsigned long blocksize = p_s_inode->i_sb->s_blocksize ;
+ unsigned long offset = (p_s_inode->i_size) & (PAGE_CACHE_SIZE - 1) ;
+ struct buffer_head *bh ;
+ struct buffer_head *head ;
+ struct page * page ;
+ int error ;
+
+ /* we know that we are only called with inode->i_size > 0.
+ ** we also know that a file tail can never be as big as a block
+ ** If i_size % blocksize == 0, our file is currently block aligned
+ ** and it won't need converting or zeroing after a truncate.
+ */
+ if ((offset & (blocksize - 1)) == 0) {
+ return -ENOENT ;
+ }
+ page = grab_cache_page(p_s_inode->i_mapping, index) ;
+ error = -ENOMEM ;
+ if (!page) {
+ goto out ;
+ }
+ /* start within the page of the last block in the file */
+ start = (offset / blocksize) * blocksize ;
+
+ error = block_prepare_write(page, start, offset,
+ reiserfs_get_block_create_0) ;
+ if (error)
+ goto unlock ;
+
+ kunmap(page) ; /* mapped by block_prepare_write */
+
+ head = page->buffers ;
+ bh = head;
+ do {
+ if (pos >= start) {
+ break ;
+ }
+ bh = bh->b_this_page ;
+ pos += blocksize ;
+ } while(bh != head) ;
+
+ if (!buffer_uptodate(bh)) {
+ /* note, this should never happen, prepare_write should
+ ** be taking care of this for us. If the buffer isn't up to date,
+ ** I've screwed up the code to find the buffer, or the code to
+ ** call prepare_write
+ */
+ reiserfs_warning(p_s_inode->i_sb, "clm-6000: error reading block %lu\n",
+ bh->b_blocknr) ;
+ error = -EIO ;
+ goto unlock ;
+ }
+ *bh_result = bh ;
+ *page_result = page ;
+
+out:
+ return error ;
+
+unlock:
+ UnlockPage(page) ;
+ page_cache_release(page) ;
+ return error ;
+}
+
+/*
+** vfs version of truncate file. Must NOT be called with
+** a transaction already started.
+**
+** some code taken from block_truncate_page
+*/
+void reiserfs_truncate_file(struct inode *p_s_inode, int update_timestamps) {
+ struct reiserfs_transaction_handle th ;
+ int windex ;
+
+ /* we want the offset for the first byte after the end of the file */
+ unsigned long offset = p_s_inode->i_size & (PAGE_CACHE_SIZE - 1) ;
+ unsigned blocksize = p_s_inode->i_sb->s_blocksize ;
+ unsigned length ;
+ struct page *page = NULL ;
+ int error ;
+ struct buffer_head *bh = NULL ;
+
+ if (p_s_inode->i_size > 0) {
+ if ((error = grab_tail_page(p_s_inode, &page, &bh))) {
+ // -ENOENT means we truncated past the end of the file,
+ // and get_block_create_0 could not find a block to read in,
+ // which is ok.
+ if (error != -ENOENT)
+ reiserfs_warning(p_s_inode->i_sb, "clm-6001: grab_tail_page failed %d\n", error);
+ page = NULL ;
+ bh = NULL ;
+ }
+ }
+
+ /* so, if page != NULL, we have a buffer head for the offset at
+ ** the end of the file. if the bh is mapped, and bh->b_blocknr != 0,
+ ** then we have an unformatted node. Otherwise, we have a direct item,
+ ** and no zeroing is required on disk. We zero after the truncate,
+ ** because the truncate might pack the item anyway
+ ** (it will unmap bh if it packs).
+ */
+ /* it is enough to reserve space in transaction for 2 balancings:
+ one for "save" link adding and another for the first
+ cut_from_item. 1 is for update_sd */
+ journal_begin(&th, p_s_inode->i_sb, JOURNAL_PER_BALANCE_CNT * 2 + 1 ) ;
+ reiserfs_update_inode_transaction(p_s_inode) ;
+ windex = push_journal_writer("reiserfs_vfs_truncate_file") ;
+ if (update_timestamps)
+ /* we are doing real truncate: if the system crashes before the last
+ transaction of truncating gets committed - on reboot the file
+ either appears truncated properly or not truncated at all */
+ add_save_link (&th, p_s_inode, 1);
+ reiserfs_do_truncate (&th, p_s_inode, page, update_timestamps) ;
+ pop_journal_writer(windex) ;
+ journal_end(&th, p_s_inode->i_sb, JOURNAL_PER_BALANCE_CNT * 2 + 1 ) ;
+
+ if (update_timestamps)
+ remove_save_link (p_s_inode, 1/* truncate */);
+
+ if (page) {
+ length = offset & (blocksize - 1) ;
+ /* if we are not on a block boundary */
+ if (length) {
+ length = blocksize - length ;
+ memset((char *)kmap(page) + offset, 0, length) ;
+ flush_dcache_page(page) ;
+ kunmap(page) ;
+ if (buffer_mapped(bh) && bh->b_blocknr != 0) {
+ if (!atomic_set_buffer_dirty(bh)) {
+ set_buffer_flushtime(bh);
+ refile_buffer(bh);
+ buffer_insert_inode_data_queue(bh, p_s_inode);
+ balance_dirty();
+ }
+ }
+ }
+ UnlockPage(page) ;
+ page_cache_release(page) ;
+ }
+
+ return ;
+}
+
+static int map_block_for_writepage(struct inode *inode,
+ struct buffer_head *bh_result,
+ unsigned long block) {
+ struct reiserfs_transaction_handle th ;
+ int fs_gen ;
+ struct item_head tmp_ih ;
+ struct item_head *ih ;
+ struct buffer_head *bh ;
+ __u32 *item ;
+ struct cpu_key key ;
+ INITIALIZE_PATH(path) ;
+ int pos_in_item ;
+ int jbegin_count = JOURNAL_PER_BALANCE_CNT ;
+ loff_t byte_offset = (block << inode->i_sb->s_blocksize_bits) + 1 ;
+ int retval ;
+ int use_get_block = 0 ;
+ int bytes_copied = 0 ;
+ int copy_size ;
+
+ kmap(bh_result->b_page) ;
+start_over:
+ lock_kernel() ;
+ journal_begin(&th, inode->i_sb, jbegin_count) ;
+ reiserfs_update_inode_transaction(inode) ;
+
+ make_cpu_key(&key, inode, byte_offset, TYPE_ANY, 3) ;
+
+research:
+ retval = search_for_position_by_key(inode->i_sb, &key, &path) ;
+ if (retval != POSITION_FOUND) {
+ use_get_block = 1;
+ goto out ;
+ }
+
+ bh = get_last_bh(&path) ;
+ ih = get_ih(&path) ;
+ item = get_item(&path) ;
+ pos_in_item = path.pos_in_item ;
+
+ /* we've found an unformatted node */
+ if (indirect_item_found(retval, ih)) {
+ if (bytes_copied > 0) {
+ reiserfs_warning(inode->i_sb, "clm-6002: bytes_copied %d\n", bytes_copied) ;
+ }
+ if (!get_block_num(item, pos_in_item)) {
+ /* crap, we are writing to a hole */
+ use_get_block = 1;
+ goto out ;
+ }
+ set_block_dev_mapped(bh_result, get_block_num(item,pos_in_item),inode);
+ mark_buffer_uptodate(bh_result, 1);
+ } else if (is_direct_le_ih(ih)) {
+ char *p ;
+ p = page_address(bh_result->b_page) ;
+ p += (byte_offset -1) & (PAGE_CACHE_SIZE - 1) ;
+ copy_size = ih_item_len(ih) - pos_in_item;
+
+ fs_gen = get_generation(inode->i_sb) ;
+ copy_item_head(&tmp_ih, ih) ;
+ reiserfs_prepare_for_journal(inode->i_sb, bh, 1) ;
+ if (fs_changed (fs_gen, inode->i_sb) && item_moved (&tmp_ih, &path)) {
+ reiserfs_restore_prepared_buffer(inode->i_sb, bh) ;
+ goto research;
+ }
+
+ memcpy( B_I_PITEM(bh, ih) + pos_in_item, p + bytes_copied, copy_size) ;
+
+ journal_mark_dirty(&th, inode->i_sb, bh) ;
+ bytes_copied += copy_size ;
+ set_block_dev_mapped(bh_result, 0, inode);
+ mark_buffer_uptodate(bh_result, 1);
+
+ /* are there still bytes left? */
+ if (bytes_copied < bh_result->b_size &&
+ (byte_offset + bytes_copied) < inode->i_size) {
+ set_cpu_key_k_offset(&key, cpu_key_k_offset(&key) + copy_size) ;
+ goto research ;
+ }
+ } else {
+ reiserfs_warning(inode->i_sb, "clm-6003: bad item inode %lu\n", inode->i_ino) ;
+ retval = -EIO ;
+ goto out ;
+ }
+ retval = 0 ;
+
+out:
+ pathrelse(&path) ;
+ journal_end(&th, inode->i_sb, jbegin_count) ;
+ unlock_kernel() ;
+
+ /* this is where we fill in holes in the file. */
+ if (use_get_block) {
+ retval = reiserfs_get_block(inode, block, bh_result,
+ GET_BLOCK_CREATE | GET_BLOCK_NO_ISEM) ;
+ if (!retval) {
+ if (!buffer_mapped(bh_result) || bh_result->b_blocknr == 0) {
+ /* get_block failed to find a mapped unformatted node. */
+ use_get_block = 0 ;
+ goto start_over ;
+ }
+ }
+ }
+ kunmap(bh_result->b_page) ;
+ return retval ;
+}
+
+/* helper func to get a buffer head ready for writepage to send to
+** ll_rw_block
+*/
+static inline void submit_bh_for_writepage(struct buffer_head **bhp, int nr) {
+ struct buffer_head *bh ;
+ int i;
+
+ /* lock them all first so the end_io handler doesn't unlock the page
+ ** too early
+ */
+ for(i = 0 ; i < nr ; i++) {
+ bh = bhp[i] ;
+ lock_buffer(bh) ;
+ set_buffer_async_io(bh) ;
+ }
+ for(i = 0 ; i < nr ; i++) {
+ /* submit_bh doesn't care if the buffer is dirty, but nobody
+ ** later on in the call chain will be cleaning it. So, we
+ ** clean the buffer here, it still gets written either way.
+ */
+ bh = bhp[i] ;
+ clear_bit(BH_Dirty, &bh->b_state) ;
+ set_bit(BH_Uptodate, &bh->b_state) ;
+ submit_bh(WRITE, bh) ;
+ }
+}
+
+static int reiserfs_write_full_page(struct page *page) {
+ struct inode *inode = page->mapping->host ;
+ unsigned long end_index = inode->i_size >> PAGE_CACHE_SHIFT ;
+ unsigned last_offset = PAGE_CACHE_SIZE;
+ int error = 0;
+ unsigned long block ;
+ unsigned cur_offset = 0 ;
+ struct buffer_head *head, *bh ;
+ int partial = 0 ;
+ struct buffer_head *arr[PAGE_CACHE_SIZE/512] ;
+ int nr = 0 ;
+
+ if (!page->buffers) {
+ block_prepare_write(page, 0, 0, NULL) ;
+ kunmap(page) ;
+ }
+ /* last page in the file, zero out any contents past the
+ ** last byte in the file
+ */
+ if (page->index >= end_index) {
+ last_offset = inode->i_size & (PAGE_CACHE_SIZE - 1) ;
+ /* no file contents in this page */
+ if (page->index >= end_index + 1 || !last_offset) {
+ error = -EIO ;
+ goto fail ;
+ }
+ memset((char *)kmap(page)+last_offset, 0, PAGE_CACHE_SIZE-last_offset) ;
+ flush_dcache_page(page) ;
+ kunmap(page) ;
+ }
+ head = page->buffers ;
+ bh = head ;
+ block = page->index << (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits) ;
+ do {
+ /* if this offset in the page is outside the file */
+ if (cur_offset >= last_offset) {
+ if (!buffer_uptodate(bh))
+ partial = 1 ;
+ } else {
+ /* fast path, buffer mapped to an unformatted node */
+ if (buffer_mapped(bh) && bh->b_blocknr != 0) {
+ arr[nr++] = bh ;
+ } else {
+ /* buffer not mapped yet, or points to a direct item.
+ ** search and dirty or log
+ */
+ if ((error = map_block_for_writepage(inode, bh, block))) {
+ goto fail ;
+ }
+ /* map_block_for_writepage either found an unformatted node
+ ** and mapped it for us, or it found a direct item
+ ** and logged the changes.
+ */
+ if (buffer_mapped(bh) && bh->b_blocknr != 0) {
+ arr[nr++] = bh ;
+ }
+ }
+ }
+ bh = bh->b_this_page ;
+ cur_offset += bh->b_size ;
+ block++ ;
+ } while(bh != head) ;
+
+ /* if this page only had a direct item, it is very possible for
+ ** nr == 0 without there being any kind of error.
+ */
+ if (nr) {
+ submit_bh_for_writepage(arr, nr) ;
+ wakeup_page_waiters(page);
+ } else {
+ UnlockPage(page) ;
+ }
+ if (!partial)
+ SetPageUptodate(page) ;
+
+ return 0 ;
+
+fail:
+ if (nr) {
+ submit_bh_for_writepage(arr, nr) ;
+ } else {
+ UnlockPage(page) ;
+ }
+ ClearPageUptodate(page) ;
+ return error ;
+}
+
+
+static int reiserfs_readpage (struct file *f, struct page * page)
+{
+ return block_read_full_page (page, reiserfs_get_block);
+}
+
+
+static int reiserfs_writepage (struct page * page)
+{
+ struct inode *inode = page->mapping->host ;
+ reiserfs_wait_on_write_block(inode->i_sb) ;
+ return reiserfs_write_full_page(page) ;
+}
+
+
+int reiserfs_prepare_write(struct file *f, struct page *page,
+ unsigned from, unsigned to) {
+ struct inode *inode = page->mapping->host ;
+ reiserfs_wait_on_write_block(inode->i_sb) ;
+ fix_tail_page_for_writing(page) ;
+ return block_prepare_write(page, from, to, reiserfs_get_block) ;
+}
+
+
+static int reiserfs_aop_bmap(struct address_space *as, long block) {
+ return generic_block_bmap(as, block, reiserfs_bmap) ;
+}
+
+static int reiserfs_commit_write(struct file *f, struct page *page,
+ unsigned from, unsigned to) {
+ struct inode *inode = page->mapping->host ;
+ loff_t pos = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to;
+ int ret ;
+
+ reiserfs_wait_on_write_block(inode->i_sb) ;
+
+ /* generic_commit_write does this for us, but does not update the
+ ** transaction tracking stuff when the size changes. So, we have
+ ** to do the i_size updates here.
+ */
+ if (pos > inode->i_size) {
+ struct reiserfs_transaction_handle th ;
+ lock_kernel();
+ /* If the file have grown beyond the border where it
+ can have a tail, unmark it as needing a tail
+ packing */
+ if ( (have_large_tails (inode->i_sb) && inode->i_size > block_size (inode)*4) ||
+ (have_small_tails (inode->i_sb) && inode->i_size > block_size(inode)) )
+ inode->u.reiserfs_i.i_flags &= ~i_pack_on_close_mask;
+
+ journal_begin(&th, inode->i_sb, 1) ;
+ reiserfs_update_inode_transaction(inode) ;
+ inode->i_size = pos ;
+ reiserfs_update_sd(&th, inode) ;
+ journal_end(&th, inode->i_sb, 1) ;
+ unlock_kernel();
+ }
+
+ ret = generic_commit_write(f, page, from, to) ;
+
+ /* we test for O_SYNC here so we can commit the transaction
+ ** for any packed tails the file might have had
+ */
+ if (f && (f->f_flags & O_SYNC)) {
+ lock_kernel() ;
+ reiserfs_commit_for_inode(inode) ;
+ unlock_kernel();
+ }
+ return ret ;
+}
+
+void sd_attrs_to_i_attrs( __u16 sd_attrs, struct inode *inode )
+{
+ if( reiserfs_attrs( inode -> i_sb ) ) {
+ if( sd_attrs & REISERFS_SYNC_FL )
+ inode -> i_flags |= S_SYNC;
+ else
+ inode -> i_flags &= ~S_SYNC;
+ if( sd_attrs & REISERFS_IMMUTABLE_FL )
+ inode -> i_flags |= S_IMMUTABLE;
+ else
+ inode -> i_flags &= ~S_IMMUTABLE;
+ if( sd_attrs & REISERFS_APPEND_FL )
+ inode -> i_flags |= S_APPEND;
+ else
+ inode -> i_flags &= ~S_APPEND;
+ if( sd_attrs & REISERFS_NOATIME_FL )
+ inode -> i_flags |= S_NOATIME;
+ else
+ inode -> i_flags &= ~S_NOATIME;
+ if( sd_attrs & REISERFS_NOTAIL_FL )
+ inode->u.reiserfs_i.i_flags |= i_nopack_mask;
+ else
+ inode->u.reiserfs_i.i_flags &= ~i_nopack_mask;
+ }
+}
+
+void i_attrs_to_sd_attrs( struct inode *inode, __u16 *sd_attrs )
+{
+ if( reiserfs_attrs( inode -> i_sb ) ) {
+ if( inode -> i_flags & S_IMMUTABLE )
+ *sd_attrs |= REISERFS_IMMUTABLE_FL;
+ else
+ *sd_attrs &= ~REISERFS_IMMUTABLE_FL;
+ if( inode -> i_flags & S_SYNC )
+ *sd_attrs |= REISERFS_SYNC_FL;
+ else
+ *sd_attrs &= ~REISERFS_SYNC_FL;
+ if( inode -> i_flags & S_NOATIME )
+ *sd_attrs |= REISERFS_NOATIME_FL;
+ else
+ *sd_attrs &= ~REISERFS_NOATIME_FL;
+ if( inode->u.reiserfs_i.i_flags & i_nopack_mask )
+ *sd_attrs |= REISERFS_NOTAIL_FL;
+ else
+ *sd_attrs &= ~REISERFS_NOTAIL_FL;
+ }
+}
+
+static int reiserfs_direct_io(int rw, struct inode *inode,
+ struct kiobuf *iobuf, unsigned long blocknr,
+ int blocksize)
+{
+ lock_kernel();
+ reiserfs_commit_for_tail(inode);
+ unlock_kernel();
+ return generic_direct_IO(rw, inode, iobuf, blocknr, blocksize,
+ reiserfs_get_block_direct_io) ;
+}
+
+struct address_space_operations reiserfs_address_space_operations = {
+ writepage: reiserfs_writepage,
+ readpage: reiserfs_readpage,
+ sync_page: block_sync_page,
+ prepare_write: reiserfs_prepare_write,
+ commit_write: reiserfs_commit_write,
+ bmap: reiserfs_aop_bmap,
+ direct_IO: reiserfs_direct_io,
+} ;
diff --git a/fs/reiserfs/ioctl.c b/fs/reiserfs/ioctl.c
new file mode 100644
index 00000000000000..345ff07c05d6c7
--- /dev/null
+++ b/fs/reiserfs/ioctl.c
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2000-2002 by Hans Reiser, licensing governed by reiserfs/README
+ */
+
+#include <linux/fs.h>
+#include <linux/reiserfs_fs.h>
+#include <linux/sched.h>
+#include <asm/uaccess.h>
+#include <linux/smp_lock.h>
+#include <linux/locks.h>
+
+/*
+** reiserfs_ioctl - handler for ioctl for inode
+** supported commands:
+** 1) REISERFS_IOC_UNPACK - try to unpack tail from direct item into indirect
+** and prevent packing file (argument arg has to be non-zero)
+** 2) REISERFS_IOC_[GS]ETFLAGS, REISERFS_IOC_[GS]ETVERSION
+** 3) That's all for a while ...
+*/
+int reiserfs_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
+ unsigned long arg)
+{
+ unsigned int flags;
+
+ switch (cmd) {
+ case REISERFS_IOC_UNPACK:
+ if( S_ISREG( inode -> i_mode ) ) {
+ if (arg)
+ return reiserfs_unpack (inode, filp);
+ else
+ return 0;
+ } else
+ return -ENOTTY;
+ /*
+ * Following {G,S}ETFLAGS, and {G,S}ETVERSION are providing ext2
+ * binary compatible interface (used by lsattr(1), and chattr(1)) and
+ * are * thus conceptually similar to appropriate pieces of
+ * fs/ext2/ioctl.c
+ */
+ case REISERFS_IOC_GETFLAGS:
+ flags = inode -> u.reiserfs_i.i_attrs;
+ i_attrs_to_sd_attrs( inode, ( __u16 * ) &flags );
+ return put_user(flags, (int *) arg);
+ case REISERFS_IOC_SETFLAGS: {
+ if (IS_RDONLY(inode))
+ return -EROFS;
+
+ if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
+ return -EPERM;
+
+ if (get_user(flags, (int *) arg))
+ return -EFAULT;
+
+ if ( ( ( flags ^ inode->u.reiserfs_i.i_attrs) & ( REISERFS_IMMUTABLE_FL | REISERFS_APPEND_FL)) &&
+ !capable( CAP_LINUX_IMMUTABLE ) )
+ return -EPERM;
+
+ if( ( flags & REISERFS_NOTAIL_FL ) &&
+ S_ISREG( inode -> i_mode ) ) {
+ int result;
+
+ result = reiserfs_unpack( inode, filp );
+ if( result )
+ return result;
+ }
+ sd_attrs_to_i_attrs( flags, inode );
+ inode -> u.reiserfs_i.i_attrs = flags;
+ inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+ return 0;
+ }
+ case REISERFS_IOC_GETVERSION:
+ return put_user(inode->i_generation, (int *) arg);
+ case REISERFS_IOC_SETVERSION:
+ if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
+ return -EPERM;
+ if (IS_RDONLY(inode))
+ return -EROFS;
+ if (get_user(inode->i_generation, (int *) arg))
+ return -EFAULT;
+ inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+ return 0;
+ default:
+ return -ENOTTY;
+ }
+ return 0;
+}
+
+/*
+** reiserfs_unpack
+** Function try to convert tail from direct item into indirect.
+** It set up nopack attribute in the inode.u.reiserfs_i.nopack
+*/
+int reiserfs_unpack (struct inode * inode, struct file * filp)
+{
+ int retval = 0;
+ int index ;
+ struct page *page ;
+ unsigned long write_from ;
+ unsigned long blocksize = inode->i_sb->s_blocksize ;
+
+ if (inode->i_size == 0) {
+ inode->u.reiserfs_i.i_flags |= i_nopack_mask;
+ return 0 ;
+ }
+ /* ioctl already done */
+ if (inode->u.reiserfs_i.i_flags & i_nopack_mask) {
+ return 0 ;
+ }
+ lock_kernel();
+
+ /* we need to make sure nobody is changing the file size beneath
+ ** us
+ */
+ down(&inode->i_sem) ;
+
+ write_from = inode->i_size & (blocksize - 1) ;
+ /* if we are on a block boundary, we are already unpacked. */
+ if ( write_from == 0) {
+ inode->u.reiserfs_i.i_flags |= i_nopack_mask;
+ goto out ;
+ }
+
+ /* we unpack by finding the page with the tail, and calling
+ ** reiserfs_prepare_write on that page. This will force a
+ ** reiserfs_get_block to unpack the tail for us.
+ */
+ index = inode->i_size >> PAGE_CACHE_SHIFT ;
+ page = grab_cache_page(inode->i_mapping, index) ;
+ retval = -ENOMEM;
+ if (!page) {
+ goto out ;
+ }
+ retval = reiserfs_prepare_write(NULL, page, write_from, blocksize) ;
+ if (retval)
+ goto out_unlock ;
+
+ /* conversion can change page contents, must flush */
+ flush_dcache_page(page) ;
+ inode->u.reiserfs_i.i_flags |= i_nopack_mask;
+ kunmap(page) ; /* mapped by prepare_write */
+
+out_unlock:
+ UnlockPage(page) ;
+ page_cache_release(page) ;
+
+out:
+ up(&inode->i_sem) ;
+ unlock_kernel();
+ return retval;
+}
diff --git a/fs/reiserfs/item_ops.c b/fs/reiserfs/item_ops.c
new file mode 100644
index 00000000000000..faa4a2138f3127
--- /dev/null
+++ b/fs/reiserfs/item_ops.c
@@ -0,0 +1,789 @@
+/*
+ * Copyright 2000-2002 by Hans Reiser, licensing governed by reiserfs/README
+ */
+
+#include <linux/sched.h>
+#include <linux/reiserfs_fs.h>
+
+// this contains item handlers for old item types: sd, direct,
+// indirect, directory
+
+/* and where are the comments? how about saying where we can find an
+ explanation of each item handler method? -Hans */
+
+//////////////////////////////////////////////////////////////////////////////
+// stat data functions
+//
+static int sd_bytes_number (struct item_head * ih, int block_size)
+{
+ return 0;
+}
+
+static void sd_decrement_key (struct cpu_key * key)
+{
+ key->on_disk_key.k_objectid --;
+ set_cpu_key_k_type (key, TYPE_ANY);
+ set_cpu_key_k_offset(key, (loff_t)(-1));
+}
+
+static int sd_is_left_mergeable (struct key * key, unsigned long bsize)
+{
+ return 0;
+}
+
+
+
+static char * print_time (time_t t)
+{
+ static char timebuf[256];
+
+ sprintf (timebuf, "%ld", t);
+ return timebuf;
+}
+
+
+static void sd_print_item (struct item_head * ih, char * item)
+{
+ printk ("\tmode | size | nlinks | first direct | mtime\n");
+ if (stat_data_v1 (ih)) {
+ struct stat_data_v1 * sd = (struct stat_data_v1 *)item;
+
+ printk ("\t0%-6o | %6u | %2u | %d | %s\n", sd_v1_mode(sd),
+ sd_v1_size(sd), sd_v1_nlink(sd), sd_v1_first_direct_byte(sd),
+ print_time( sd_v1_mtime(sd) ) );
+ } else {
+ struct stat_data * sd = (struct stat_data *)item;
+
+ printk ("\t0%-6o | %6Lu | %2u | %d | %s\n", sd_v2_mode(sd),
+ (unsigned long long)sd_v2_size(sd), sd_v2_nlink(sd),
+ sd_v2_rdev(sd), print_time(sd_v2_mtime(sd)));
+ }
+}
+
+static void sd_check_item (struct item_head * ih, char * item)
+{
+ // FIXME: type something here!
+}
+
+
+static int sd_create_vi (struct virtual_node * vn,
+ struct virtual_item * vi,
+ int is_affected,
+ int insert_size)
+{
+ vi->vi_index = TYPE_STAT_DATA;
+ //vi->vi_type |= VI_TYPE_STAT_DATA;// not needed?
+ return 0;
+}
+
+
+static int sd_check_left (struct virtual_item * vi, int free,
+ int start_skip, int end_skip)
+{
+ if (start_skip || end_skip)
+ BUG ();
+ return -1;
+}
+
+
+static int sd_check_right (struct virtual_item * vi, int free)
+{
+ return -1;
+}
+
+static int sd_part_size (struct virtual_item * vi, int first, int count)
+{
+ if (count)
+ BUG ();
+ return 0;
+}
+
+static int sd_unit_num (struct virtual_item * vi)
+{
+ return vi->vi_item_len - IH_SIZE;
+}
+
+
+static void sd_print_vi (struct virtual_item * vi)
+{
+ reiserfs_warning (NULL, "STATDATA, index %d, type 0x%x, %h\n",
+ vi->vi_index, vi->vi_type, vi->vi_ih);
+}
+
+struct item_operations stat_data_ops = {
+ sd_bytes_number,
+ sd_decrement_key,
+ sd_is_left_mergeable,
+ sd_print_item,
+ sd_check_item,
+
+ sd_create_vi,
+ sd_check_left,
+ sd_check_right,
+ sd_part_size,
+ sd_unit_num,
+ sd_print_vi
+};
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+// direct item functions
+//
+static int direct_bytes_number (struct item_head * ih, int block_size)
+{
+ return ih_item_len(ih);
+}
+
+
+// FIXME: this should probably switch to indirect as well
+static void direct_decrement_key (struct cpu_key * key)
+{
+ cpu_key_k_offset_dec (key);
+ if (cpu_key_k_offset (key) == 0)
+ set_cpu_key_k_type (key, TYPE_STAT_DATA);
+}
+
+
+static int direct_is_left_mergeable (struct key * key, unsigned long bsize)
+{
+ int version = le_key_version (key);
+ return ((le_key_k_offset (version, key) & (bsize - 1)) != 1);
+}
+
+
+static void direct_print_item (struct item_head * ih, char * item)
+{
+ int j = 0;
+
+// return;
+ printk ("\"");
+ while (j < ih_item_len(ih))
+ printk ("%c", item[j++]);
+ printk ("\"\n");
+}
+
+
+static void direct_check_item (struct item_head * ih, char * item)
+{
+ // FIXME: type something here!
+}
+
+
+static int direct_create_vi (struct virtual_node * vn,
+ struct virtual_item * vi,
+ int is_affected,
+ int insert_size)
+{
+ vi->vi_index = TYPE_DIRECT;
+ //vi->vi_type |= VI_TYPE_DIRECT;
+ return 0;
+}
+
+static int direct_check_left (struct virtual_item * vi, int free,
+ int start_skip, int end_skip)
+{
+ int bytes;
+
+ bytes = free - free % 8;
+ return bytes ?: -1;
+}
+
+
+static int direct_check_right (struct virtual_item * vi, int free)
+{
+ return direct_check_left (vi, free, 0, 0);
+}
+
+static int direct_part_size (struct virtual_item * vi, int first, int count)
+{
+ return count;
+}
+
+
+static int direct_unit_num (struct virtual_item * vi)
+{
+ return vi->vi_item_len - IH_SIZE;
+}
+
+
+static void direct_print_vi (struct virtual_item * vi)
+{
+ reiserfs_warning (NULL, "DIRECT, index %d, type 0x%x, %h\n",
+ vi->vi_index, vi->vi_type, vi->vi_ih);
+}
+
+struct item_operations direct_ops = {
+ direct_bytes_number,
+ direct_decrement_key,
+ direct_is_left_mergeable,
+ direct_print_item,
+ direct_check_item,
+
+ direct_create_vi,
+ direct_check_left,
+ direct_check_right,
+ direct_part_size,
+ direct_unit_num,
+ direct_print_vi
+};
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+// indirect item functions
+//
+
+static int indirect_bytes_number (struct item_head * ih, int block_size)
+{
+ return ih_item_len(ih) / UNFM_P_SIZE * block_size; //- get_ih_free_space (ih);
+}
+
+
+// decrease offset, if it becomes 0, change type to stat data
+static void indirect_decrement_key (struct cpu_key * key)
+{
+ cpu_key_k_offset_dec (key);
+ if (cpu_key_k_offset (key) == 0)
+ set_cpu_key_k_type (key, TYPE_STAT_DATA);
+}
+
+
+// if it is not first item of the body, then it is mergeable
+static int indirect_is_left_mergeable (struct key * key, unsigned long bsize)
+{
+ int version = le_key_version (key);
+ return (le_key_k_offset (version, key) != 1);
+}
+
+
+// printing of indirect item
+static void start_new_sequence (__u32 * start, int * len, __u32 new)
+{
+ *start = new;
+ *len = 1;
+}
+
+
+static int sequence_finished (__u32 start, int * len, __u32 new)
+{
+ if (start == INT_MAX)
+ return 1;
+
+ if (start == 0 && new == 0) {
+ (*len) ++;
+ return 0;
+ }
+ if (start != 0 && (start + *len) == new) {
+ (*len) ++;
+ return 0;
+ }
+ return 1;
+}
+
+static void print_sequence (__u32 start, int len)
+{
+ if (start == INT_MAX)
+ return;
+
+ if (len == 1)
+ printk (" %d", start);
+ else
+ printk (" %d(%d)", start, len);
+}
+
+
+static void indirect_print_item (struct item_head * ih, char * item)
+{
+ int j;
+ __u32 * unp, prev = INT_MAX;
+ int num;
+
+ unp = (__u32 *)item;
+
+ if (ih_item_len(ih) % UNFM_P_SIZE)
+ printk ("indirect_print_item: invalid item len");
+
+ printk ("%d pointers\n[ ", (int)I_UNFM_NUM (ih));
+ for (j = 0; j < I_UNFM_NUM (ih); j ++) {
+ if (sequence_finished (prev, &num, get_block_num(unp, j))) {
+ print_sequence (prev, num);
+ start_new_sequence (&prev, &num, get_block_num(unp, j));
+ }
+ }
+ print_sequence (prev, num);
+ printk ("]\n");
+}
+
+static void indirect_check_item (struct item_head * ih, char * item)
+{
+ // FIXME: type something here!
+}
+
+
+static int indirect_create_vi (struct virtual_node * vn,
+ struct virtual_item * vi,
+ int is_affected,
+ int insert_size)
+{
+ vi->vi_index = TYPE_INDIRECT;
+ //vi->vi_type |= VI_TYPE_INDIRECT;
+ return 0;
+}
+
+static int indirect_check_left (struct virtual_item * vi, int free,
+ int start_skip, int end_skip)
+{
+ int bytes;
+
+ bytes = free - free % UNFM_P_SIZE;
+ return bytes ?: -1;
+}
+
+
+static int indirect_check_right (struct virtual_item * vi, int free)
+{
+ return indirect_check_left (vi, free, 0, 0);
+}
+
+
+
+// return size in bytes of 'units' units. If first == 0 - calculate from the head (left), otherwise - from tail (right)
+static int indirect_part_size (struct virtual_item * vi, int first, int units)
+{
+ // unit of indirect item is byte (yet)
+ return units;
+}
+
+static int indirect_unit_num (struct virtual_item * vi)
+{
+ // unit of indirect item is byte (yet)
+ return vi->vi_item_len - IH_SIZE;
+}
+
+static void indirect_print_vi (struct virtual_item * vi)
+{
+ reiserfs_warning (NULL, "INDIRECT, index %d, type 0x%x, %h\n",
+ vi->vi_index, vi->vi_type, vi->vi_ih);
+}
+
+struct item_operations indirect_ops = {
+ indirect_bytes_number,
+ indirect_decrement_key,
+ indirect_is_left_mergeable,
+ indirect_print_item,
+ indirect_check_item,
+
+ indirect_create_vi,
+ indirect_check_left,
+ indirect_check_right,
+ indirect_part_size,
+ indirect_unit_num,
+ indirect_print_vi
+};
+
+
+//////////////////////////////////////////////////////////////////////////////
+// direntry functions
+//
+
+
+static int direntry_bytes_number (struct item_head * ih, int block_size)
+{
+ reiserfs_warning (NULL, "vs-16090: direntry_bytes_number: "
+ "bytes number is asked for direntry");
+ return 0;
+}
+
+static void direntry_decrement_key (struct cpu_key * key)
+{
+ cpu_key_k_offset_dec (key);
+ if (cpu_key_k_offset (key) == 0)
+ set_cpu_key_k_type (key, TYPE_STAT_DATA);
+}
+
+
+static int direntry_is_left_mergeable (struct key * key, unsigned long bsize)
+{
+ if (le32_to_cpu (key->u.k_offset_v1.k_offset) == DOT_OFFSET)
+ return 0;
+ return 1;
+
+}
+
+
+static void direntry_print_item (struct item_head * ih, char * item)
+{
+ int i;
+ int namelen;
+ struct reiserfs_de_head * deh;
+ char * name;
+ static char namebuf [80];
+
+
+ printk ("\n # %-15s%-30s%-15s%-15s%-15s\n", "Name", "Key of pointed object", "Hash", "Gen number", "Status");
+
+ deh = (struct reiserfs_de_head *)item;
+
+ for (i = 0; i < I_ENTRY_COUNT (ih); i ++, deh ++) {
+ namelen = (i ? (deh_location(deh - 1)) : ih_item_len(ih)) - deh_location(deh);
+ name = item + deh_location(deh);
+ if (name[namelen-1] == 0)
+ namelen = strlen (name);
+ namebuf[0] = '"';
+ if (namelen > sizeof (namebuf) - 3) {
+ strncpy (namebuf + 1, name, sizeof (namebuf) - 3);
+ namebuf[sizeof (namebuf) - 2] = '"';
+ namebuf[sizeof (namebuf) - 1] = 0;
+ } else {
+ memcpy (namebuf + 1, name, namelen);
+ namebuf[namelen + 1] = '"';
+ namebuf[namelen + 2] = 0;
+ }
+
+ printk ("%d: %-15s%-15d%-15d%-15Ld%-15Ld(%s)\n",
+ i, namebuf,
+ deh_dir_id(deh), deh_objectid(deh),
+ GET_HASH_VALUE (deh_offset (deh)), GET_GENERATION_NUMBER ((deh_offset (deh))),
+ (de_hidden (deh)) ? "HIDDEN" : "VISIBLE");
+ }
+}
+
+
+static void direntry_check_item (struct item_head * ih, char * item)
+{
+ int i;
+ struct reiserfs_de_head * deh;
+
+ // FIXME: type something here!
+ deh = (struct reiserfs_de_head *)item;
+ for (i = 0; i < I_ENTRY_COUNT (ih); i ++, deh ++) {
+ ;
+ }
+}
+
+
+
+#define DIRENTRY_VI_FIRST_DIRENTRY_ITEM 1
+
+/*
+ * function returns old entry number in directory item in real node
+ * using new entry number in virtual item in virtual node */
+static inline int old_entry_num (int is_affected, int virtual_entry_num, int pos_in_item, int mode)
+{
+ if ( mode == M_INSERT || mode == M_DELETE)
+ return virtual_entry_num;
+
+ if (!is_affected)
+ /* cut or paste is applied to another item */
+ return virtual_entry_num;
+
+ if (virtual_entry_num < pos_in_item)
+ return virtual_entry_num;
+
+ if (mode == M_CUT)
+ return virtual_entry_num + 1;
+
+ RFALSE( mode != M_PASTE || virtual_entry_num == 0,
+ "vs-8015: old_entry_num: mode must be M_PASTE (mode = \'%c\'", mode);
+
+ return virtual_entry_num - 1;
+}
+
+
+
+
+/* Create an array of sizes of directory entries for virtual
+ item. Return space used by an item. FIXME: no control over
+ consuming of space used by this item handler */
+static int direntry_create_vi (struct virtual_node * vn,
+ struct virtual_item * vi,
+ int is_affected,
+ int insert_size)
+{
+ struct direntry_uarea * dir_u = vi->vi_uarea;
+ int i, j;
+ int size = sizeof (struct direntry_uarea);
+ struct reiserfs_de_head * deh;
+
+ vi->vi_index = TYPE_DIRENTRY;
+
+ if (!(vi->vi_ih) || !vi->vi_item)
+ BUG ();
+
+
+ dir_u->flags = 0;
+ if (le_ih_k_offset (vi->vi_ih) == DOT_OFFSET)
+ dir_u->flags |= DIRENTRY_VI_FIRST_DIRENTRY_ITEM;
+
+ deh = (struct reiserfs_de_head *)(vi->vi_item);
+
+
+ /* virtual directory item have this amount of entry after */
+ dir_u->entry_count = ih_entry_count (vi->vi_ih) +
+ ((is_affected) ? ((vn->vn_mode == M_CUT) ? -1 :
+ (vn->vn_mode == M_PASTE ? 1 : 0)) : 0);
+
+ for (i = 0; i < dir_u->entry_count; i ++) {
+ j = old_entry_num (is_affected, i, vn->vn_pos_in_item, vn->vn_mode);
+ dir_u->entry_sizes[i] = (j ? deh_location( &(deh[j - 1]) ) :
+ ih_item_len (vi->vi_ih)) -
+ deh_location( &(deh[j])) + DEH_SIZE;
+ }
+
+ size += (dir_u->entry_count * sizeof (short));
+
+ /* set size of pasted entry */
+ if (is_affected && vn->vn_mode == M_PASTE)
+ dir_u->entry_sizes[vn->vn_pos_in_item] = insert_size;
+
+
+#ifdef CONFIG_REISERFS_CHECK
+ /* compare total size of entries with item length */
+ {
+ int k, l;
+
+ l = 0;
+ for (k = 0; k < dir_u->entry_count; k ++)
+ l += dir_u->entry_sizes[k];
+
+ if (l + IH_SIZE != vi->vi_item_len +
+ ((is_affected && (vn->vn_mode == M_PASTE || vn->vn_mode == M_CUT)) ? insert_size : 0) ) {
+ reiserfs_panic (0, "vs-8025: set_entry_sizes: (mode==%c, insert_size==%d), invalid length of directory item",
+ vn->vn_mode, insert_size);
+ }
+ }
+#endif
+
+ return size;
+
+
+}
+
+
+//
+// return number of entries which may fit into specified amount of
+// free space, or -1 if free space is not enough even for 1 entry
+//
+static int direntry_check_left (struct virtual_item * vi, int free,
+ int start_skip, int end_skip)
+{
+ int i;
+ int entries = 0;
+ struct direntry_uarea * dir_u = vi->vi_uarea;
+
+ for (i = start_skip; i < dir_u->entry_count - end_skip; i ++) {
+ if (dir_u->entry_sizes[i] > free)
+ /* i-th entry doesn't fit into the remaining free space */
+ break;
+
+ free -= dir_u->entry_sizes[i];
+ entries ++;
+ }
+
+ if (entries == dir_u->entry_count) {
+ printk ("free spze %d, entry_count %d\n", free, dir_u->entry_count);
+ BUG ();
+ }
+
+ /* "." and ".." can not be separated from each other */
+ if (start_skip == 0 && (dir_u->flags & DIRENTRY_VI_FIRST_DIRENTRY_ITEM) && entries < 2)
+ entries = 0;
+
+ return entries ?: -1;
+}
+
+
+static int direntry_check_right (struct virtual_item * vi, int free)
+{
+ int i;
+ int entries = 0;
+ struct direntry_uarea * dir_u = vi->vi_uarea;
+
+ for (i = dir_u->entry_count - 1; i >= 0; i --) {
+ if (dir_u->entry_sizes[i] > free)
+ /* i-th entry doesn't fit into the remaining free space */
+ break;
+
+ free -= dir_u->entry_sizes[i];
+ entries ++;
+ }
+ if (entries == dir_u->entry_count)
+ BUG ();
+
+ /* "." and ".." can not be separated from each other */
+ if ((dir_u->flags & DIRENTRY_VI_FIRST_DIRENTRY_ITEM) && entries > dir_u->entry_count - 2)
+ entries = dir_u->entry_count - 2;
+
+ return entries ?: -1;
+}
+
+
+/* sum of entry sizes between from-th and to-th entries including both edges */
+static int direntry_part_size (struct virtual_item * vi, int first, int count)
+{
+ int i, retval;
+ int from, to;
+ struct direntry_uarea * dir_u = vi->vi_uarea;
+
+ retval = 0;
+ if (first == 0)
+ from = 0;
+ else
+ from = dir_u->entry_count - count;
+ to = from + count - 1;
+
+ for (i = from; i <= to; i ++)
+ retval += dir_u->entry_sizes[i];
+
+ return retval;
+}
+
+static int direntry_unit_num (struct virtual_item * vi)
+{
+ struct direntry_uarea * dir_u = vi->vi_uarea;
+
+ return dir_u->entry_count;
+}
+
+
+
+static void direntry_print_vi (struct virtual_item * vi)
+{
+ int i;
+ struct direntry_uarea * dir_u = vi->vi_uarea;
+
+ reiserfs_warning (NULL, "DIRENTRY, index %d, type 0x%x, %h, flags 0x%x\n",
+ vi->vi_index, vi->vi_type, vi->vi_ih, dir_u->flags);
+ printk ("%d entries: ", dir_u->entry_count);
+ for (i = 0; i < dir_u->entry_count; i ++)
+ printk ("%d ", dir_u->entry_sizes[i]);
+ printk ("\n");
+}
+
+struct item_operations direntry_ops = {
+ direntry_bytes_number,
+ direntry_decrement_key,
+ direntry_is_left_mergeable,
+ direntry_print_item,
+ direntry_check_item,
+
+ direntry_create_vi,
+ direntry_check_left,
+ direntry_check_right,
+ direntry_part_size,
+ direntry_unit_num,
+ direntry_print_vi
+};
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Error catching functions to catch errors caused by incorrect item types.
+//
+static int errcatch_bytes_number (struct item_head * ih, int block_size)
+{
+ reiserfs_warning (NULL, "green-16001: Invalid item type observed, run fsck ASAP\n");
+ return 0;
+}
+
+static void errcatch_decrement_key (struct cpu_key * key)
+{
+ reiserfs_warning (NULL, "green-16002: Invalid item type observed, run fsck ASAP\n");
+}
+
+
+static int errcatch_is_left_mergeable (struct key * key, unsigned long bsize)
+{
+ reiserfs_warning (NULL, "green-16003: Invalid item type observed, run fsck ASAP\n");
+ return 0;
+}
+
+
+static void errcatch_print_item (struct item_head * ih, char * item)
+{
+ reiserfs_warning (NULL, "green-16004: Invalid item type observed, run fsck ASAP\n");
+}
+
+
+static void errcatch_check_item (struct item_head * ih, char * item)
+{
+ reiserfs_warning (NULL, "green-16005: Invalid item type observed, run fsck ASAP\n");
+}
+
+static int errcatch_create_vi (struct virtual_node * vn,
+ struct virtual_item * vi,
+ int is_affected,
+ int insert_size)
+{
+ reiserfs_warning (NULL, "green-16006: Invalid item type observed, run fsck ASAP\n");
+ return 0; // We might return -1 here as well, but it won't help as create_virtual_node() from where
+ // this operation is called from is of return type void.
+}
+
+static int errcatch_check_left (struct virtual_item * vi, int free,
+ int start_skip, int end_skip)
+{
+ reiserfs_warning (NULL, "green-16007: Invalid item type observed, run fsck ASAP\n");
+ return -1;
+}
+
+
+static int errcatch_check_right (struct virtual_item * vi, int free)
+{
+ reiserfs_warning (NULL, "green-16008: Invalid item type observed, run fsck ASAP\n");
+ return -1;
+}
+
+static int errcatch_part_size (struct virtual_item * vi, int first, int count)
+{
+ reiserfs_warning (NULL, "green-16009: Invalid item type observed, run fsck ASAP\n");
+ return 0;
+}
+
+static int errcatch_unit_num (struct virtual_item * vi)
+{
+ reiserfs_warning (NULL, "green-16010: Invalid item type observed, run fsck ASAP\n");
+ return 0;
+}
+
+static void errcatch_print_vi (struct virtual_item * vi)
+{
+ reiserfs_warning (NULL, "green-16011: Invalid item type observed, run fsck ASAP\n");
+}
+
+struct item_operations errcatch_ops = {
+ errcatch_bytes_number,
+ errcatch_decrement_key,
+ errcatch_is_left_mergeable,
+ errcatch_print_item,
+ errcatch_check_item,
+
+ errcatch_create_vi,
+ errcatch_check_left,
+ errcatch_check_right,
+ errcatch_part_size,
+ errcatch_unit_num,
+ errcatch_print_vi
+};
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+//
+#if ! (TYPE_STAT_DATA == 0 && TYPE_INDIRECT == 1 && TYPE_DIRECT == 2 && TYPE_DIRENTRY == 3)
+ do not compile
+#endif
+
+struct item_operations * item_ops [TYPE_ANY + 1] = {
+ &stat_data_ops,
+ &indirect_ops,
+ &direct_ops,
+ &direntry_ops,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ &errcatch_ops /* This is to catch errors with invalid type (15th entry for TYPE_ANY) */
+};
+
+
+
+
diff --git a/fs/reiserfs/journal.c b/fs/reiserfs/journal.c
new file mode 100644
index 00000000000000..80a018f4426bd1
--- /dev/null
+++ b/fs/reiserfs/journal.c
@@ -0,0 +1,3274 @@
+/*
+** Write ahead logging implementation copyright Chris Mason 2000
+**
+** The background commits make this code very interelated, and
+** overly complex. I need to rethink things a bit....The major players:
+**
+** journal_begin -- call with the number of blocks you expect to log.
+** If the current transaction is too
+** old, it will block until the current transaction is
+** finished, and then start a new one.
+** Usually, your transaction will get joined in with
+** previous ones for speed.
+**
+** journal_join -- same as journal_begin, but won't block on the current
+** transaction regardless of age. Don't ever call
+** this. Ever. There are only two places it should be
+** called from, and they are both inside this file.
+**
+** journal_mark_dirty -- adds blocks into this transaction. clears any flags
+** that might make them get sent to disk
+** and then marks them BH_JDirty. Puts the buffer head
+** into the current transaction hash.
+**
+** journal_end -- if the current transaction is batchable, it does nothing
+** otherwise, it could do an async/synchronous commit, or
+** a full flush of all log and real blocks in the
+** transaction.
+**
+** flush_old_commits -- if the current transaction is too old, it is ended and
+** commit blocks are sent to disk. Forces commit blocks
+** to disk for all backgrounded commits that have been
+** around too long.
+** -- Note, if you call this as an immediate flush from
+** from within kupdate, it will ignore the immediate flag
+**
+** The commit thread -- a writer process for async commits. It allows a
+** a process to request a log flush on a task queue.
+** the commit will happen once the commit thread wakes up.
+** The benefit here is the writer (with whatever
+** related locks it has) doesn't have to wait for the
+** log blocks to hit disk if it doesn't want to.
+*/
+
+#include <linux/config.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#include <linux/sched.h>
+#include <asm/semaphore.h>
+
+#include <linux/vmalloc.h>
+#include <linux/reiserfs_fs.h>
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/locks.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/smp_lock.h>
+
+/* the number of mounted filesystems. This is used to decide when to
+** start and kill the commit thread
+*/
+static int reiserfs_mounted_fs_count = 0 ;
+
+/* wake this up when you add something to the commit thread task queue */
+DECLARE_WAIT_QUEUE_HEAD(reiserfs_commit_thread_wait) ;
+
+/* wait on this if you need to be sure you task queue entries have been run */
+static DECLARE_WAIT_QUEUE_HEAD(reiserfs_commit_thread_done) ;
+DECLARE_TASK_QUEUE(reiserfs_commit_thread_tq) ;
+
+#define JOURNAL_TRANS_HALF 1018 /* must be correct to keep the desc and commit
+ structs at 4k */
+#define BUFNR 64 /*read ahead */
+
+/* cnode stat bits. Move these into reiserfs_fs.h */
+
+#define BLOCK_FREED 2 /* this block was freed, and can't be written. */
+#define BLOCK_FREED_HOLDER 3 /* this block was freed during this transaction, and can't be written */
+
+#define BLOCK_NEEDS_FLUSH 4 /* used in flush_journal_list */
+
+/* flags for do_journal_end */
+#define FLUSH_ALL 1 /* flush commit and real blocks */
+#define COMMIT_NOW 2 /* end and commit this transaction */
+#define WAIT 4 /* wait for the log blocks to hit the disk*/
+
+/* state bits for the journal */
+#define WRITERS_BLOCKED 1 /* set when new writers not allowed */
+
+static int do_journal_end(struct reiserfs_transaction_handle *,struct super_block *,unsigned long nblocks,int flags) ;
+static int flush_journal_list(struct super_block *s, struct reiserfs_journal_list *jl, int flushall) ;
+static int flush_commit_list(struct super_block *s, struct reiserfs_journal_list *jl, int flushall) ;
+static int can_dirty(struct reiserfs_journal_cnode *cn) ;
+static int remove_from_journal_list(struct super_block *s, struct reiserfs_journal_list *jl, struct buffer_head *bh, int remove_freed);
+static int journal_join(struct reiserfs_transaction_handle *th, struct super_block *p_s_sb, unsigned long nblocks);
+static int release_journal_dev( struct super_block *super,
+ struct reiserfs_journal *journal );
+static void init_journal_hash(struct super_block *p_s_sb) {
+ memset(SB_JOURNAL(p_s_sb)->j_hash_table, 0, JOURNAL_HASH_SIZE * sizeof(struct reiserfs_journal_cnode *)) ;
+}
+
+/*
+** clears BH_Dirty and sticks the buffer on the clean list. Called because I can't allow refile_buffer to
+** make schedule happen after I've freed a block. Look at remove_from_transaction and journal_mark_freed for
+** more details.
+*/
+static int reiserfs_clean_and_file_buffer(struct buffer_head *bh) {
+ if (bh) {
+ clear_bit(BH_Dirty, &bh->b_state) ;
+ refile_buffer(bh) ;
+ }
+ return 0 ;
+}
+
+static struct reiserfs_bitmap_node *
+allocate_bitmap_node(struct super_block *p_s_sb) {
+ struct reiserfs_bitmap_node *bn ;
+ static int id = 0 ;
+
+ bn = reiserfs_kmalloc(sizeof(struct reiserfs_bitmap_node), GFP_NOFS, p_s_sb) ;
+ if (!bn) {
+ return NULL ;
+ }
+ bn->data = reiserfs_kmalloc(p_s_sb->s_blocksize, GFP_NOFS, p_s_sb) ;
+ if (!bn->data) {
+ reiserfs_kfree(bn, sizeof(struct reiserfs_bitmap_node), p_s_sb) ;
+ return NULL ;
+ }
+ bn->id = id++ ;
+ memset(bn->data, 0, p_s_sb->s_blocksize) ;
+ INIT_LIST_HEAD(&bn->list) ;
+ return bn ;
+}
+
+static struct reiserfs_bitmap_node *
+get_bitmap_node(struct super_block *p_s_sb) {
+ struct reiserfs_bitmap_node *bn = NULL;
+ struct list_head *entry = SB_JOURNAL(p_s_sb)->j_bitmap_nodes.next ;
+
+ SB_JOURNAL(p_s_sb)->j_used_bitmap_nodes++ ;
+repeat:
+
+ if(entry != &SB_JOURNAL(p_s_sb)->j_bitmap_nodes) {
+ bn = list_entry(entry, struct reiserfs_bitmap_node, list) ;
+ list_del(entry) ;
+ memset(bn->data, 0, p_s_sb->s_blocksize) ;
+ SB_JOURNAL(p_s_sb)->j_free_bitmap_nodes-- ;
+ return bn ;
+ }
+ bn = allocate_bitmap_node(p_s_sb) ;
+ if (!bn) {
+ yield();
+ goto repeat ;
+ }
+ return bn ;
+}
+static inline void free_bitmap_node(struct super_block *p_s_sb,
+ struct reiserfs_bitmap_node *bn) {
+ SB_JOURNAL(p_s_sb)->j_used_bitmap_nodes-- ;
+ if (SB_JOURNAL(p_s_sb)->j_free_bitmap_nodes > REISERFS_MAX_BITMAP_NODES) {
+ reiserfs_kfree(bn->data, p_s_sb->s_blocksize, p_s_sb) ;
+ reiserfs_kfree(bn, sizeof(struct reiserfs_bitmap_node), p_s_sb) ;
+ } else {
+ list_add(&bn->list, &SB_JOURNAL(p_s_sb)->j_bitmap_nodes) ;
+ SB_JOURNAL(p_s_sb)->j_free_bitmap_nodes++ ;
+ }
+}
+
+static void allocate_bitmap_nodes(struct super_block *p_s_sb) {
+ int i ;
+ struct reiserfs_bitmap_node *bn = NULL ;
+ for (i = 0 ; i < REISERFS_MIN_BITMAP_NODES ; i++) {
+ bn = allocate_bitmap_node(p_s_sb) ;
+ if (bn) {
+ list_add(&bn->list, &SB_JOURNAL(p_s_sb)->j_bitmap_nodes) ;
+ SB_JOURNAL(p_s_sb)->j_free_bitmap_nodes++ ;
+ } else {
+ break ; // this is ok, we'll try again when more are needed
+ }
+ }
+}
+
+static int set_bit_in_list_bitmap(struct super_block *p_s_sb, int block,
+ struct reiserfs_list_bitmap *jb) {
+ int bmap_nr = block / (p_s_sb->s_blocksize << 3) ;
+ int bit_nr = block % (p_s_sb->s_blocksize << 3) ;
+
+ if (!jb->bitmaps[bmap_nr]) {
+ jb->bitmaps[bmap_nr] = get_bitmap_node(p_s_sb) ;
+ }
+ set_bit(bit_nr, (unsigned long *)jb->bitmaps[bmap_nr]->data) ;
+ return 0 ;
+}
+
+static void cleanup_bitmap_list(struct super_block *p_s_sb,
+ struct reiserfs_list_bitmap *jb) {
+ int i;
+ if (jb->bitmaps == NULL)
+ return ;
+
+ for (i = 0 ; i < SB_BMAP_NR(p_s_sb) ; i++) {
+ if (jb->bitmaps[i]) {
+ free_bitmap_node(p_s_sb, jb->bitmaps[i]) ;
+ jb->bitmaps[i] = NULL ;
+ }
+ }
+}
+
+/*
+** only call this on FS unmount.
+*/
+static int free_list_bitmaps(struct super_block *p_s_sb,
+ struct reiserfs_list_bitmap *jb_array) {
+ int i ;
+ struct reiserfs_list_bitmap *jb ;
+ for (i = 0 ; i < JOURNAL_NUM_BITMAPS ; i++) {
+ jb = jb_array + i ;
+ jb->journal_list = NULL ;
+ cleanup_bitmap_list(p_s_sb, jb) ;
+ vfree(jb->bitmaps) ;
+ jb->bitmaps = NULL ;
+ }
+ return 0;
+}
+
+static int free_bitmap_nodes(struct super_block *p_s_sb) {
+ struct list_head *next = SB_JOURNAL(p_s_sb)->j_bitmap_nodes.next ;
+ struct reiserfs_bitmap_node *bn ;
+
+ while(next != &SB_JOURNAL(p_s_sb)->j_bitmap_nodes) {
+ bn = list_entry(next, struct reiserfs_bitmap_node, list) ;
+ list_del(next) ;
+ reiserfs_kfree(bn->data, p_s_sb->s_blocksize, p_s_sb) ;
+ reiserfs_kfree(bn, sizeof(struct reiserfs_bitmap_node), p_s_sb) ;
+ next = SB_JOURNAL(p_s_sb)->j_bitmap_nodes.next ;
+ SB_JOURNAL(p_s_sb)->j_free_bitmap_nodes-- ;
+ }
+
+ return 0 ;
+}
+
+/*
+** get memory for JOURNAL_NUM_BITMAPS worth of bitmaps.
+** jb_array is the array to be filled in.
+*/
+int reiserfs_allocate_list_bitmaps(struct super_block *p_s_sb,
+ struct reiserfs_list_bitmap *jb_array,
+ int bmap_nr) {
+ int i ;
+ int failed = 0 ;
+ struct reiserfs_list_bitmap *jb ;
+ int mem = bmap_nr * sizeof(struct reiserfs_bitmap_node *) ;
+
+ for (i = 0 ; i < JOURNAL_NUM_BITMAPS ; i++) {
+ jb = jb_array + i ;
+ jb->journal_list = NULL ;
+ jb->bitmaps = vmalloc( mem ) ;
+ if (!jb->bitmaps) {
+ reiserfs_warning(p_s_sb, "clm-2000, unable to allocate bitmaps for journal lists\n") ;
+ failed = 1;
+ break ;
+ }
+ memset(jb->bitmaps, 0, mem) ;
+ }
+ if (failed) {
+ free_list_bitmaps(p_s_sb, jb_array) ;
+ return -1 ;
+ }
+ return 0 ;
+}
+
+/*
+** find an available list bitmap. If you can't find one, flush a commit list
+** and try again
+*/
+static struct reiserfs_list_bitmap *
+get_list_bitmap(struct super_block *p_s_sb, struct reiserfs_journal_list *jl) {
+ int i,j ;
+ struct reiserfs_list_bitmap *jb = NULL ;
+
+ for (j = 0 ; j < (JOURNAL_NUM_BITMAPS * 3) ; j++) {
+ i = SB_JOURNAL(p_s_sb)->j_list_bitmap_index ;
+ SB_JOURNAL(p_s_sb)->j_list_bitmap_index = (i + 1) % JOURNAL_NUM_BITMAPS ;
+ jb = SB_JOURNAL(p_s_sb)->j_list_bitmap + i ;
+ if (SB_JOURNAL(p_s_sb)->j_list_bitmap[i].journal_list) {
+ flush_commit_list(p_s_sb, SB_JOURNAL(p_s_sb)->j_list_bitmap[i].journal_list, 1) ;
+ if (!SB_JOURNAL(p_s_sb)->j_list_bitmap[i].journal_list) {
+ break ;
+ }
+ } else {
+ break ;
+ }
+ }
+ if (jb->journal_list) { /* double check to make sure if flushed correctly */
+ return NULL ;
+ }
+ jb->journal_list = jl ;
+ return jb ;
+}
+
+/*
+** allocates a new chunk of X nodes, and links them all together as a list.
+** Uses the cnode->next and cnode->prev pointers
+** returns NULL on failure
+*/
+static struct reiserfs_journal_cnode *allocate_cnodes(int num_cnodes) {
+ struct reiserfs_journal_cnode *head ;
+ int i ;
+ if (num_cnodes <= 0) {
+ return NULL ;
+ }
+ head = vmalloc(num_cnodes * sizeof(struct reiserfs_journal_cnode)) ;
+ if (!head) {
+ return NULL ;
+ }
+ memset(head, 0, num_cnodes * sizeof(struct reiserfs_journal_cnode)) ;
+ head[0].prev = NULL ;
+ head[0].next = head + 1 ;
+ for (i = 1 ; i < num_cnodes; i++) {
+ head[i].prev = head + (i - 1) ;
+ head[i].next = head + (i + 1) ; /* if last one, overwrite it after the if */
+ }
+ head[num_cnodes -1].next = NULL ;
+ return head ;
+}
+
+/*
+** pulls a cnode off the free list, or returns NULL on failure
+*/
+static struct reiserfs_journal_cnode *get_cnode(struct super_block *p_s_sb) {
+ struct reiserfs_journal_cnode *cn ;
+
+ reiserfs_check_lock_depth("get_cnode") ;
+
+ if (SB_JOURNAL(p_s_sb)->j_cnode_free <= 0) {
+ return NULL ;
+ }
+ SB_JOURNAL(p_s_sb)->j_cnode_used++ ;
+ SB_JOURNAL(p_s_sb)->j_cnode_free-- ;
+ cn = SB_JOURNAL(p_s_sb)->j_cnode_free_list ;
+ if (!cn) {
+ return cn ;
+ }
+ if (cn->next) {
+ cn->next->prev = NULL ;
+ }
+ SB_JOURNAL(p_s_sb)->j_cnode_free_list = cn->next ;
+ memset(cn, 0, sizeof(struct reiserfs_journal_cnode)) ;
+ return cn ;
+}
+
+/*
+** returns a cnode to the free list
+*/
+static void free_cnode(struct super_block *p_s_sb, struct reiserfs_journal_cnode *cn) {
+
+ reiserfs_check_lock_depth("free_cnode") ;
+
+ SB_JOURNAL(p_s_sb)->j_cnode_used-- ;
+ SB_JOURNAL(p_s_sb)->j_cnode_free++ ;
+ /* memset(cn, 0, sizeof(struct reiserfs_journal_cnode)) ; */
+ cn->next = SB_JOURNAL(p_s_sb)->j_cnode_free_list ;
+ if (SB_JOURNAL(p_s_sb)->j_cnode_free_list) {
+ SB_JOURNAL(p_s_sb)->j_cnode_free_list->prev = cn ;
+ }
+ cn->prev = NULL ; /* not needed with the memset, but I might kill the memset, and forget to do this */
+ SB_JOURNAL(p_s_sb)->j_cnode_free_list = cn ;
+}
+
+static int clear_prepared_bits(struct buffer_head *bh) {
+ clear_bit(BH_JPrepared, &bh->b_state) ;
+ return 0 ;
+}
+
+/* buffer is in current transaction */
+inline int buffer_journaled(const struct buffer_head *bh) {
+ if (bh)
+ return test_bit(BH_JDirty, &((struct buffer_head *)bh)->b_state) ;
+ else
+ return 0 ;
+}
+
+/* disk block was taken off free list before being in a finished transation, or written to disk
+** journal_new blocks can be reused immediately, for any purpose
+*/
+inline int buffer_journal_new(const struct buffer_head *bh) {
+ if (bh)
+ return test_bit(BH_JNew, &((struct buffer_head *)bh)->b_state) ;
+ else
+ return 0 ;
+}
+
+inline int mark_buffer_journal_new(struct buffer_head *bh) {
+ if (bh) {
+ set_bit(BH_JNew, &bh->b_state) ;
+ }
+ return 0 ;
+}
+
+inline int mark_buffer_not_journaled(struct buffer_head *bh) {
+ if (bh)
+ clear_bit(BH_JDirty, &bh->b_state) ;
+ return 0 ;
+}
+
+/* utility function to force a BUG if it is called without the big
+** kernel lock held. caller is the string printed just before calling BUG()
+*/
+void reiserfs_check_lock_depth(char *caller) {
+#ifdef CONFIG_SMP
+ if (current->lock_depth < 0) {
+ printk("%s called without kernel lock held\n", caller) ;
+ show_reiserfs_locks() ;
+ BUG() ;
+ }
+#else
+ ;
+#endif
+}
+
+/* return a cnode with same dev, block number and size in table, or null if not found */
+static inline struct reiserfs_journal_cnode *get_journal_hash_dev(struct reiserfs_journal_cnode **table,
+ kdev_t dev,long bl,int size) {
+ struct reiserfs_journal_cnode *cn ;
+ cn = journal_hash(table, dev, bl) ;
+ while(cn) {
+ if ((cn->blocknr == bl) && (cn->dev == dev))
+ return cn ;
+ cn = cn->hnext ;
+ }
+ return (struct reiserfs_journal_cnode *)0 ;
+}
+
+/* returns a cnode with same size, block number and dev as bh in the current transaction hash. NULL if not found */
+static inline struct reiserfs_journal_cnode *get_journal_hash(struct super_block *p_s_sb, struct buffer_head *bh) {
+ struct reiserfs_journal_cnode *cn ;
+ if (bh) {
+ cn = get_journal_hash_dev(SB_JOURNAL(p_s_sb)->j_hash_table, bh->b_dev, bh->b_blocknr, bh->b_size) ;
+ }
+ else {
+ return (struct reiserfs_journal_cnode *)0 ;
+ }
+ return cn ;
+}
+
+/* once upon a time, the journal would deadlock. a lot. Now, when
+** CONFIG_REISERFS_CHECK is defined, anytime someone enters a
+** transaction, it pushes itself into this ugly static list, and pops
+** itself off before calling journal_end. I made a SysRq key to dump
+** the list, and tell me what the writers are when I'm deadlocked. */
+
+ /* are you depending on the compiler
+ to optimize this function away
+ everywhere it is called? It is not
+ obvious how this works, but I
+ suppose debugging code need not be
+ clear. -Hans */
+static char *journal_writers[512] ;
+int push_journal_writer(char *s) {
+#ifdef CONFIG_REISERFS_CHECK
+ int i ;
+ for (i = 0 ; i < 512 ; i++) {
+ if (!journal_writers[i]) {
+ journal_writers[i] = s ;
+ return i ;
+ }
+ }
+ return -1 ;
+#else
+ return 0 ;
+#endif
+}
+int pop_journal_writer(int index) {
+#ifdef CONFIG_REISERFS_CHECK
+ if (index >= 0) {
+ journal_writers[index] = NULL ;
+ }
+#endif
+ return 0 ;
+}
+
+int dump_journal_writers(void) {
+ int i ;
+ for (i = 0 ; i < 512 ; i++) {
+ if (journal_writers[i]) {
+ printk("%d: %s\n", i, journal_writers[i]) ;
+ }
+ }
+ return 0 ;
+}
+
+/*
+** this actually means 'can this block be reallocated yet?'. If you set search_all, a block can only be allocated
+** if it is not in the current transaction, was not freed by the current transaction, and has no chance of ever
+** being overwritten by a replay after crashing.
+**
+** If you don't set search_all, a block can only be allocated if it is not in the current transaction. Since deleting
+** a block removes it from the current transaction, this case should never happen. If you don't set search_all, make
+** sure you never write the block without logging it.
+**
+** next_zero_bit is a suggestion about the next block to try for find_forward.
+** when bl is rejected because it is set in a journal list bitmap, we search
+** for the next zero bit in the bitmap that rejected bl. Then, we return that
+** through next_zero_bit for find_forward to try.
+**
+** Just because we return something in next_zero_bit does not mean we won't
+** reject it on the next call to reiserfs_in_journal
+**
+*/
+int reiserfs_in_journal(struct super_block *p_s_sb, kdev_t dev,
+ int bmap_nr, int bit_nr, int size, int search_all,
+ unsigned int *next_zero_bit) {
+ struct reiserfs_journal_cnode *cn ;
+ struct reiserfs_list_bitmap *jb ;
+ int i ;
+ unsigned long bl;
+
+ *next_zero_bit = 0 ; /* always start this at zero. */
+
+ /* we aren't logging all blocks are safe for reuse */
+ if (reiserfs_dont_log(p_s_sb)) {
+ return 0 ;
+ }
+
+ PROC_INFO_INC( p_s_sb, journal.in_journal );
+ /* If we aren't doing a search_all, this is a metablock, and it will be logged before use.
+ ** if we crash before the transaction that freed it commits, this transaction won't
+ ** have committed either, and the block will never be written
+ */
+ if (search_all) {
+ for (i = 0 ; i < JOURNAL_NUM_BITMAPS ; i++) {
+ PROC_INFO_INC( p_s_sb, journal.in_journal_bitmap );
+ jb = SB_JOURNAL(p_s_sb)->j_list_bitmap + i ;
+ if (jb->journal_list && jb->bitmaps[bmap_nr] &&
+ test_bit(bit_nr, (unsigned long *)jb->bitmaps[bmap_nr]->data)) {
+ *next_zero_bit = find_next_zero_bit((unsigned long *)
+ (jb->bitmaps[bmap_nr]->data),
+ p_s_sb->s_blocksize << 3, bit_nr+1) ;
+ return 1 ;
+ }
+ }
+ }
+
+ bl = bmap_nr * (p_s_sb->s_blocksize << 3) + bit_nr;
+ /* is it in any old transactions? */
+ if (search_all && (cn = get_journal_hash_dev(SB_JOURNAL(p_s_sb)->j_list_hash_table, dev,bl,size))) {
+ return 1;
+ }
+
+ /* is it in the current transaction. This should never happen */
+ if ((cn = get_journal_hash_dev(SB_JOURNAL(p_s_sb)->j_hash_table, dev,bl,size))) {
+ return 1;
+ }
+
+ PROC_INFO_INC( p_s_sb, journal.in_journal_reusable );
+ /* safe for reuse */
+ return 0 ;
+}
+
+/* insert cn into table
+*/
+inline void insert_journal_hash(struct reiserfs_journal_cnode **table, struct reiserfs_journal_cnode *cn) {
+ struct reiserfs_journal_cnode *cn_orig ;
+
+ cn_orig = journal_hash(table, cn->dev, cn->blocknr) ;
+ cn->hnext = cn_orig ;
+ cn->hprev = NULL ;
+ if (cn_orig) {
+ cn_orig->hprev = cn ;
+ }
+ journal_hash(table, cn->dev, cn->blocknr) = cn ;
+}
+
+/* lock the current transaction */
+inline static void lock_journal(struct super_block *p_s_sb) {
+ PROC_INFO_INC( p_s_sb, journal.lock_journal );
+ while(atomic_read(&(SB_JOURNAL(p_s_sb)->j_wlock)) > 0) {
+ PROC_INFO_INC( p_s_sb, journal.lock_journal_wait );
+ sleep_on(&(SB_JOURNAL(p_s_sb)->j_wait)) ;
+ }
+ atomic_set(&(SB_JOURNAL(p_s_sb)->j_wlock), 1) ;
+}
+
+/* unlock the current transaction */
+inline static void unlock_journal(struct super_block *p_s_sb) {
+ atomic_dec(&(SB_JOURNAL(p_s_sb)->j_wlock)) ;
+ wake_up(&(SB_JOURNAL(p_s_sb)->j_wait)) ;
+}
+
+/*
+** this used to be much more involved, and I'm keeping it just in case things get ugly again.
+** it gets called by flush_commit_list, and cleans up any data stored about blocks freed during a
+** transaction.
+*/
+static void cleanup_freed_for_journal_list(struct super_block *p_s_sb, struct reiserfs_journal_list *jl) {
+
+ struct reiserfs_list_bitmap *jb = jl->j_list_bitmap ;
+ if (jb) {
+ cleanup_bitmap_list(p_s_sb, jb) ;
+ }
+ jl->j_list_bitmap->journal_list = NULL ;
+ jl->j_list_bitmap = NULL ;
+}
+
+/*
+** if this journal list still has commit blocks unflushed, send them to disk.
+**
+** log areas must be flushed in order (transaction 2 can't commit before transaction 1)
+** Before the commit block can by written, every other log block must be safely on disk
+**
+*/
+static int flush_commit_list(struct super_block *s, struct reiserfs_journal_list *jl, int flushall) {
+ int i, count ;
+ int index = 0 ;
+ int bn ;
+ int retry_count = 0 ;
+ int orig_commit_left = 0 ;
+ struct buffer_head *tbh = NULL ;
+ struct reiserfs_journal_list *other_jl ;
+
+ reiserfs_check_lock_depth("flush_commit_list") ;
+
+ if (atomic_read(&jl->j_older_commits_done)) {
+ return 0 ;
+ }
+
+ /* before we can put our commit blocks on disk, we have to make sure everyone older than
+ ** us is on disk too
+ */
+ if (jl->j_len <= 0) {
+ return 0 ;
+ }
+ if (flushall) {
+ /* we _must_ make sure the transactions are committed in order. Start with the
+ ** index after this one, wrap all the way around
+ */
+ index = (jl - SB_JOURNAL_LIST(s)) + 1 ;
+ for (i = 0 ; i < JOURNAL_LIST_COUNT ; i++) {
+ other_jl = SB_JOURNAL_LIST(s) + ( (index + i) % JOURNAL_LIST_COUNT) ;
+ if (other_jl && other_jl != jl && other_jl->j_len > 0 && other_jl->j_trans_id > 0 &&
+ other_jl->j_trans_id <= jl->j_trans_id && (atomic_read(&(jl->j_older_commits_done)) == 0)) {
+ flush_commit_list(s, other_jl, 0) ;
+ }
+ }
+ }
+
+ count = 0 ;
+ /* don't flush the commit list for the current transactoin */
+ if (jl == ((SB_JOURNAL_LIST(s) + SB_JOURNAL_LIST_INDEX(s)))) {
+ return 0 ;
+ }
+
+ /* make sure nobody is trying to flush this one at the same time */
+ if (atomic_read(&(jl->j_commit_flushing))) {
+ sleep_on(&(jl->j_commit_wait)) ;
+ if (flushall) {
+ atomic_set(&(jl->j_older_commits_done), 1) ;
+ }
+ return 0 ;
+ }
+
+ /* this commit is done, exit */
+ if (atomic_read(&(jl->j_commit_left)) <= 0) {
+ if (flushall) {
+ atomic_set(&(jl->j_older_commits_done), 1) ;
+ }
+ return 0 ;
+ }
+ /* keeps others from flushing while we are flushing */
+ atomic_set(&(jl->j_commit_flushing), 1) ;
+
+
+ if (jl->j_len > SB_JOURNAL_TRANS_MAX(s)) {
+ reiserfs_panic(s, "journal-512: flush_commit_list: length is %lu, list number %d\n", jl->j_len, jl - SB_JOURNAL_LIST(s)) ;
+ return 0 ;
+ }
+
+ orig_commit_left = atomic_read(&(jl->j_commit_left)) ;
+
+ /* start by checking all the commit blocks in this transaction.
+ ** Add anyone not on disk into tbh. Stop checking once commit_left <= 1, because that means we
+ ** only have the commit block left
+ */
+retry:
+ count = 0 ;
+ for (i = 0 ; atomic_read(&(jl->j_commit_left)) > 1 && i < (jl->j_len + 1) ; i++) { /* everything but commit_bh */
+ bn = SB_ONDISK_JOURNAL_1st_BLOCK(s) + (jl->j_start+i) % SB_ONDISK_JOURNAL_SIZE(s);
+ tbh = journal_get_hash_table(s, bn) ;
+
+/* kill this sanity check */
+if (count > (orig_commit_left + 2)) {
+reiserfs_panic(s, "journal-539: flush_commit_list: BAD count(%d) > orig_commit_left(%d)!\n", count, orig_commit_left) ;
+}
+ if (tbh) {
+ if (buffer_locked(tbh)) { /* wait on it, redo it just to make sure */
+ wait_on_buffer(tbh) ;
+ if (!buffer_uptodate(tbh)) {
+ reiserfs_panic(s, "journal-584, buffer write failed\n") ;
+ }
+ }
+ if (buffer_dirty(tbh)) {
+ reiserfs_warning(s, "journal-569: flush_commit_list, block already dirty!\n") ;
+ } else {
+ mark_buffer_dirty(tbh) ;
+ }
+ ll_rw_block(WRITE, 1, &tbh) ;
+ count++ ;
+ put_bh(tbh) ; /* once for our get_hash */
+ }
+ }
+
+ /* wait on everyone in tbh before writing commit block*/
+ if (count > 0) {
+ for (i = 0 ; atomic_read(&(jl->j_commit_left)) > 1 &&
+ i < (jl->j_len + 1) ; i++) { /* everything but commit_bh */
+ bn = SB_ONDISK_JOURNAL_1st_BLOCK(s) + (jl->j_start + i) % SB_ONDISK_JOURNAL_SIZE(s) ;
+ tbh = journal_get_hash_table(s, bn) ;
+
+ wait_on_buffer(tbh) ;
+ if (!buffer_uptodate(tbh)) {
+ reiserfs_panic(s, "journal-601, buffer write failed\n") ;
+ }
+ put_bh(tbh) ; /* once for our get_hash */
+ bforget(tbh) ; /* once due to original getblk in do_journal_end */
+ atomic_dec(&(jl->j_commit_left)) ;
+ }
+ }
+
+ if (atomic_read(&(jl->j_commit_left)) != 1) { /* just the commit_bh left, flush it without calling getblk for everyone */
+ if (retry_count < 2) {
+ reiserfs_warning(s, "journal-582: flush_commit_list, not all log blocks on disk yet, trying again\n") ;
+ retry_count++ ;
+ goto retry;
+ }
+ reiserfs_panic(s, "journal-563: flush_commit_list: BAD, j_commit_left is %u, should be 1\n",
+ atomic_read(&(jl->j_commit_left)));
+ }
+
+ mark_buffer_dirty(jl->j_commit_bh) ;
+ ll_rw_block(WRITE, 1, &(jl->j_commit_bh)) ;
+ wait_on_buffer(jl->j_commit_bh) ;
+ if (!buffer_uptodate(jl->j_commit_bh)) {
+ reiserfs_panic(s, "journal-615: buffer write failed\n") ;
+ }
+ atomic_dec(&(jl->j_commit_left)) ;
+ bforget(jl->j_commit_bh) ;
+
+ /* now, every commit block is on the disk. It is safe to allow blocks freed during this transaction to be reallocated */
+ cleanup_freed_for_journal_list(s, jl) ;
+
+ if (flushall) {
+ atomic_set(&(jl->j_older_commits_done), 1) ;
+ }
+ atomic_set(&(jl->j_commit_flushing), 0) ;
+ wake_up(&(jl->j_commit_wait)) ;
+
+ s->s_dirt = 1 ;
+ return 0 ;
+}
+
+/*
+** flush_journal_list frequently needs to find a newer transaction for a given block. This does that, or
+** returns NULL if it can't find anything
+*/
+static struct reiserfs_journal_list *find_newer_jl_for_cn(struct reiserfs_journal_cnode *cn) {
+ kdev_t dev = cn->dev;
+ unsigned long blocknr = cn->blocknr ;
+
+ cn = cn->hprev ;
+ while(cn) {
+ if (cn->dev == dev && cn->blocknr == blocknr && cn->jlist) {
+ return cn->jlist ;
+ }
+ cn = cn->hprev ;
+ }
+ return NULL ;
+}
+
+
+/*
+** once all the real blocks have been flushed, it is safe to remove them from the
+** journal list for this transaction. Aside from freeing the cnode, this also allows the
+** block to be reallocated for data blocks if it had been deleted.
+*/
+static void remove_all_from_journal_list(struct super_block *p_s_sb, struct reiserfs_journal_list *jl, int debug) {
+ struct buffer_head fake_bh ;
+ struct reiserfs_journal_cnode *cn, *last ;
+ cn = jl->j_realblock ;
+
+ /* which is better, to lock once around the whole loop, or
+ ** to lock for each call to remove_from_journal_list?
+ */
+ while(cn) {
+ if (cn->blocknr != 0) {
+ if (debug) {
+ reiserfs_warning(p_s_sb, "block %lu, bh is %d, state %ld\n", cn->blocknr, cn->bh ? 1: 0,
+ cn->state) ;
+ }
+ fake_bh.b_blocknr = cn->blocknr ;
+ fake_bh.b_dev = cn->dev ;
+ cn->state = 0 ;
+ remove_from_journal_list(p_s_sb, jl, &fake_bh, 1) ;
+ }
+ last = cn ;
+ cn = cn->next ;
+ free_cnode(p_s_sb, last) ;
+ }
+ jl->j_realblock = NULL ;
+}
+
+/*
+** if this timestamp is greater than the timestamp we wrote last to the header block, write it to the header block.
+** once this is done, I can safely say the log area for this transaction won't ever be replayed, and I can start
+** releasing blocks in this transaction for reuse as data blocks.
+** called by flush_journal_list, before it calls remove_all_from_journal_list
+**
+*/
+static int _update_journal_header_block(struct super_block *p_s_sb, unsigned long offset, unsigned long trans_id) {
+ struct reiserfs_journal_header *jh ;
+ if (trans_id >= SB_JOURNAL(p_s_sb)->j_last_flush_trans_id) {
+ if (buffer_locked((SB_JOURNAL(p_s_sb)->j_header_bh))) {
+ wait_on_buffer((SB_JOURNAL(p_s_sb)->j_header_bh)) ;
+ if (!buffer_uptodate(SB_JOURNAL(p_s_sb)->j_header_bh)) {
+ reiserfs_panic(p_s_sb, "journal-699: buffer write failed\n") ;
+ }
+ }
+ SB_JOURNAL(p_s_sb)->j_last_flush_trans_id = trans_id ;
+ SB_JOURNAL(p_s_sb)->j_first_unflushed_offset = offset ;
+ jh = (struct reiserfs_journal_header *)(SB_JOURNAL(p_s_sb)->j_header_bh->b_data) ;
+ jh->j_last_flush_trans_id = cpu_to_le32(trans_id) ;
+ jh->j_first_unflushed_offset = cpu_to_le32(offset) ;
+ jh->j_mount_id = cpu_to_le32(SB_JOURNAL(p_s_sb)->j_mount_id) ;
+ set_bit(BH_Dirty, &(SB_JOURNAL(p_s_sb)->j_header_bh->b_state)) ;
+ ll_rw_block(WRITE, 1, &(SB_JOURNAL(p_s_sb)->j_header_bh)) ;
+ wait_on_buffer((SB_JOURNAL(p_s_sb)->j_header_bh)) ;
+ if (!buffer_uptodate(SB_JOURNAL(p_s_sb)->j_header_bh)) {
+ reiserfs_warning( p_s_sb, "reiserfs: journal-837: IO error during journal replay\n" );
+ return -EIO ;
+ }
+ }
+ return 0 ;
+}
+
+static int update_journal_header_block(struct super_block *p_s_sb,
+ unsigned long offset,
+ unsigned long trans_id) {
+ if (_update_journal_header_block(p_s_sb, offset, trans_id)) {
+ reiserfs_panic(p_s_sb, "journal-712: buffer write failed\n") ;
+ }
+ return 0 ;
+}
+/*
+** flush any and all journal lists older than you are
+** can only be called from flush_journal_list
+*/
+static int flush_older_journal_lists(struct super_block *p_s_sb, struct reiserfs_journal_list *jl, unsigned long trans_id) {
+ int i, index ;
+ struct reiserfs_journal_list *other_jl ;
+
+ index = jl - SB_JOURNAL_LIST(p_s_sb) ;
+ for (i = 0 ; i < JOURNAL_LIST_COUNT ; i++) {
+ other_jl = SB_JOURNAL_LIST(p_s_sb) + ((index + i) % JOURNAL_LIST_COUNT) ;
+ if (other_jl && other_jl->j_len > 0 &&
+ other_jl->j_trans_id > 0 &&
+ other_jl->j_trans_id < trans_id &&
+ other_jl != jl) {
+ /* do not flush all */
+ flush_journal_list(p_s_sb, other_jl, 0) ;
+ }
+ }
+ return 0 ;
+}
+
+static void reiserfs_end_buffer_io_sync(struct buffer_head *bh, int uptodate) {
+ if (buffer_journaled(bh)) {
+ reiserfs_warning(NULL, "clm-2084: pinned buffer %lu:%s sent to disk\n",
+ bh->b_blocknr, kdevname(bh->b_dev)) ;
+ }
+ mark_buffer_uptodate(bh, uptodate) ;
+ unlock_buffer(bh) ;
+ put_bh(bh) ;
+}
+static void submit_logged_buffer(struct buffer_head *bh) {
+ lock_buffer(bh) ;
+ get_bh(bh) ;
+ bh->b_end_io = reiserfs_end_buffer_io_sync ;
+ mark_buffer_notjournal_new(bh) ;
+ clear_bit(BH_Dirty, &bh->b_state) ;
+ submit_bh(WRITE, bh) ;
+}
+
+/* flush a journal list, both commit and real blocks
+**
+** always set flushall to 1, unless you are calling from inside
+** flush_journal_list
+**
+** IMPORTANT. This can only be called while there are no journal writers,
+** and the journal is locked. That means it can only be called from
+** do_journal_end, or by journal_release
+*/
+static int flush_journal_list(struct super_block *s,
+ struct reiserfs_journal_list *jl, int flushall) {
+ struct reiserfs_journal_list *pjl ;
+ struct reiserfs_journal_cnode *cn, *last ;
+ int count ;
+ int was_jwait = 0 ;
+ int was_dirty = 0 ;
+ struct buffer_head *saved_bh ;
+ unsigned long j_len_saved = jl->j_len ;
+
+ if (j_len_saved <= 0) {
+ return 0 ;
+ }
+
+ if (atomic_read(&SB_JOURNAL(s)->j_wcount) != 0) {
+ reiserfs_warning(s, "clm-2048: flush_journal_list called with wcount %d\n",
+ atomic_read(&SB_JOURNAL(s)->j_wcount)) ;
+ }
+ /* if someone is getting the commit list, we must wait for them */
+ while (atomic_read(&(jl->j_commit_flushing))) {
+ sleep_on(&(jl->j_commit_wait)) ;
+ }
+ /* if someone is flushing this list, we must wait for them */
+ while (atomic_read(&(jl->j_flushing))) {
+ sleep_on(&(jl->j_flush_wait)) ;
+ }
+
+ /* this list is now ours, we can change anything we want */
+ atomic_set(&(jl->j_flushing), 1) ;
+
+ count = 0 ;
+ if (j_len_saved > SB_JOURNAL_TRANS_MAX(s)) {
+ reiserfs_panic(s, "journal-715: flush_journal_list, length is %lu, list number %d\n", j_len_saved, jl - SB_JOURNAL_LIST(s)) ;
+ atomic_dec(&(jl->j_flushing)) ;
+ return 0 ;
+ }
+
+ /* if all the work is already done, get out of here */
+ if (atomic_read(&(jl->j_nonzerolen)) <= 0 &&
+ atomic_read(&(jl->j_commit_left)) <= 0) {
+ goto flush_older_and_return ;
+ }
+
+ /* start by putting the commit list on disk. This will also flush
+ ** the commit lists of any olders transactions
+ */
+ flush_commit_list(s, jl, 1) ;
+
+ /* are we done now? */
+ if (atomic_read(&(jl->j_nonzerolen)) <= 0 &&
+ atomic_read(&(jl->j_commit_left)) <= 0) {
+ goto flush_older_and_return ;
+ }
+
+ /* loop through each cnode, see if we need to write it,
+ ** or wait on a more recent transaction, or just ignore it
+ */
+ if (atomic_read(&(SB_JOURNAL(s)->j_wcount)) != 0) {
+ reiserfs_panic(s, "journal-844: panic journal list is flushing, wcount is not 0\n") ;
+ }
+ cn = jl->j_realblock ;
+ while(cn) {
+ was_jwait = 0 ;
+ was_dirty = 0 ;
+ saved_bh = NULL ;
+ /* blocknr of 0 is no longer in the hash, ignore it */
+ if (cn->blocknr == 0) {
+ goto free_cnode ;
+ }
+ pjl = find_newer_jl_for_cn(cn) ;
+ /* the order is important here. We check pjl to make sure we
+ ** don't clear BH_JDirty_wait if we aren't the one writing this
+ ** block to disk
+ */
+ if (!pjl && cn->bh) {
+ saved_bh = cn->bh ;
+
+ /* we do this to make sure nobody releases the buffer while
+ ** we are working with it
+ */
+ get_bh(saved_bh) ;
+
+ if (buffer_journal_dirty(saved_bh)) {
+ was_jwait = 1 ;
+ mark_buffer_notjournal_dirty(saved_bh) ;
+ /* undo the inc from journal_mark_dirty */
+ put_bh(saved_bh) ;
+ }
+ if (can_dirty(cn)) {
+ was_dirty = 1 ;
+ }
+ }
+
+ /* if someone has this block in a newer transaction, just make
+ ** sure they are commited, and don't try writing it to disk
+ */
+ if (pjl) {
+ flush_commit_list(s, pjl, 1) ;
+ goto free_cnode ;
+ }
+
+ /* bh == NULL when the block got to disk on its own, OR,
+ ** the block got freed in a future transaction
+ */
+ if (saved_bh == NULL) {
+ goto free_cnode ;
+ }
+
+ /* this should never happen. kupdate_one_transaction has this list
+ ** locked while it works, so we should never see a buffer here that
+ ** is not marked JDirty_wait
+ */
+ if ((!was_jwait) && !buffer_locked(saved_bh)) {
+reiserfs_warning(s, "journal-813: BAD! buffer %lu %cdirty %cjwait, not in a newer tranasction\n", saved_bh->b_blocknr,
+ was_dirty ? ' ' : '!', was_jwait ? ' ' : '!') ;
+ }
+ /* kupdate_one_transaction waits on the buffers it is writing, so we
+ ** should never see locked buffers here
+ */
+ if (buffer_locked(saved_bh)) {
+ reiserfs_warning(s, "clm-2083: locked buffer %lu in flush_journal_list\n",
+ saved_bh->b_blocknr) ;
+ wait_on_buffer(saved_bh) ;
+ if (!buffer_uptodate(saved_bh)) {
+ reiserfs_panic(s, "journal-923: buffer write failed\n") ;
+ }
+ }
+ if (was_dirty) {
+ /* we inc again because saved_bh gets decremented at free_cnode */
+ get_bh(saved_bh) ;
+ set_bit(BLOCK_NEEDS_FLUSH, &cn->state) ;
+ submit_logged_buffer(saved_bh) ;
+ count++ ;
+ } else {
+ reiserfs_warning(s, "clm-2082: Unable to flush buffer %lu in flush_journal_list\n",
+ saved_bh->b_blocknr) ;
+ }
+free_cnode:
+ last = cn ;
+ cn = cn->next ;
+ if (saved_bh) {
+ /* we incremented this to keep others from taking the buffer head away */
+ put_bh(saved_bh) ;
+ if (atomic_read(&(saved_bh->b_count)) < 0) {
+ reiserfs_warning(s, "journal-945: saved_bh->b_count < 0\n") ;
+ }
+ }
+ }
+ if (count > 0) {
+ cn = jl->j_realblock ;
+ while(cn) {
+ if (test_bit(BLOCK_NEEDS_FLUSH, &cn->state)) {
+ if (!cn->bh) {
+ reiserfs_panic(s, "journal-1011: cn->bh is NULL\n") ;
+ }
+ wait_on_buffer(cn->bh) ;
+ if (!cn->bh) {
+ reiserfs_panic(s, "journal-1012: cn->bh is NULL\n") ;
+ }
+ if (!buffer_uptodate(cn->bh)) {
+ reiserfs_panic(s, "journal-949: buffer write failed\n") ;
+ }
+ refile_buffer(cn->bh) ;
+ brelse(cn->bh) ;
+ }
+ cn = cn->next ;
+ }
+ }
+
+flush_older_and_return:
+ /* before we can update the journal header block, we _must_ flush all
+ ** real blocks from all older transactions to disk. This is because
+ ** once the header block is updated, this transaction will not be
+ ** replayed after a crash
+ */
+ if (flushall) {
+ flush_older_journal_lists(s, jl, jl->j_trans_id) ;
+ }
+
+ /* before we can remove everything from the hash tables for this
+ ** transaction, we must make sure it can never be replayed
+ **
+ ** since we are only called from do_journal_end, we know for sure there
+ ** are no allocations going on while we are flushing journal lists. So,
+ ** we only need to update the journal header block for the last list
+ ** being flushed
+ */
+ if (flushall) {
+ update_journal_header_block(s, (jl->j_start + jl->j_len + 2) % SB_ONDISK_JOURNAL_SIZE(s), jl->j_trans_id) ;
+ }
+ remove_all_from_journal_list(s, jl, 0) ;
+ jl->j_len = 0 ;
+ atomic_set(&(jl->j_nonzerolen), 0) ;
+ jl->j_start = 0 ;
+ jl->j_realblock = NULL ;
+ jl->j_commit_bh = NULL ;
+ jl->j_trans_id = 0 ;
+ atomic_dec(&(jl->j_flushing)) ;
+ wake_up(&(jl->j_flush_wait)) ;
+ return 0 ;
+}
+
+
+static int kupdate_one_transaction(struct super_block *s,
+ struct reiserfs_journal_list *jl)
+{
+ struct reiserfs_journal_list *pjl ; /* previous list for this cn */
+ struct reiserfs_journal_cnode *cn, *walk_cn ;
+ unsigned long blocknr ;
+ int run = 0 ;
+ int orig_trans_id = jl->j_trans_id ;
+ struct buffer_head *saved_bh ;
+ int ret = 0 ;
+
+ /* if someone is getting the commit list, we must wait for them */
+ while (atomic_read(&(jl->j_commit_flushing))) {
+ sleep_on(&(jl->j_commit_wait)) ;
+ }
+ /* if someone is flushing this list, we must wait for them */
+ while (atomic_read(&(jl->j_flushing))) {
+ sleep_on(&(jl->j_flush_wait)) ;
+ }
+ /* was it flushed while we slept? */
+ if (jl->j_len <= 0 || jl->j_trans_id != orig_trans_id) {
+ return 0 ;
+ }
+
+ /* this list is now ours, we can change anything we want */
+ atomic_set(&(jl->j_flushing), 1) ;
+
+loop_start:
+ cn = jl->j_realblock ;
+ while(cn) {
+ saved_bh = NULL ;
+ /* if the blocknr == 0, this has been cleared from the hash,
+ ** skip it
+ */
+ if (cn->blocknr == 0) {
+ goto next ;
+ }
+ /* look for a more recent transaction that logged this
+ ** buffer. Only the most recent transaction with a buffer in
+ ** it is allowed to send that buffer to disk
+ */
+ pjl = find_newer_jl_for_cn(cn) ;
+ if (run == 0 && !pjl && cn->bh && buffer_journal_dirty(cn->bh) &&
+ can_dirty(cn))
+ {
+ if (!test_bit(BH_JPrepared, &cn->bh->b_state)) {
+ set_bit(BLOCK_NEEDS_FLUSH, &cn->state) ;
+ submit_logged_buffer(cn->bh) ;
+ } else {
+ /* someone else is using this buffer. We can't
+ ** send it to disk right now because they might
+ ** be changing/logging it.
+ */
+ ret = 1 ;
+ }
+ } else if (test_bit(BLOCK_NEEDS_FLUSH, &cn->state)) {
+ clear_bit(BLOCK_NEEDS_FLUSH, &cn->state) ;
+ if (!pjl && cn->bh) {
+ wait_on_buffer(cn->bh) ;
+ }
+ /* check again, someone could have logged while we scheduled */
+ pjl = find_newer_jl_for_cn(cn) ;
+
+ /* before the JDirty_wait bit is set, the
+ ** buffer is added to the hash list. So, if we are
+ ** run in the middle of a do_journal_end, we will notice
+ ** if this buffer was logged and added from the latest
+ ** transaction. In this case, we don't want to decrement
+ ** b_count
+ */
+ if (!pjl && cn->bh && buffer_journal_dirty(cn->bh)) {
+ blocknr = cn->blocknr ;
+ walk_cn = cn ;
+ saved_bh= cn->bh ;
+ /* update all older transactions to show this block
+ ** was flushed
+ */
+ mark_buffer_notjournal_dirty(cn->bh) ;
+ while(walk_cn) {
+ if (walk_cn->bh && walk_cn->blocknr == blocknr &&
+ walk_cn->dev == cn->dev) {
+ if (walk_cn->jlist) {
+ atomic_dec(&(walk_cn->jlist->j_nonzerolen)) ;
+ }
+ walk_cn->bh = NULL ;
+ }
+ walk_cn = walk_cn->hnext ;
+ }
+ if (atomic_read(&saved_bh->b_count) < 1) {
+ reiserfs_warning(s, "clm-2081: bad count on %lu\n",
+ saved_bh->b_blocknr) ;
+ }
+ brelse(saved_bh) ;
+ }
+ }
+ /*
+ ** if the more recent transaction is committed to the log,
+ ** this buffer can be considered flushed. Decrement our
+ ** counters to reflect one less buffer that needs writing.
+ **
+ ** note, this relies on all of the above code being
+ ** schedule free once pjl comes back non-null.
+ */
+ if (pjl && cn->bh && atomic_read(&pjl->j_commit_left) == 0) {
+ atomic_dec(&cn->jlist->j_nonzerolen) ;
+ cn->bh = NULL ;
+ }
+next:
+ cn = cn->next ;
+ }
+ /* the first run through the loop sends all the dirty buffers to
+ ** ll_rw_block.
+ ** the second run through the loop does all the accounting
+ */
+ if (run++ == 0) {
+ goto loop_start ;
+ }
+
+ atomic_set(&(jl->j_flushing), 0) ;
+ wake_up(&(jl->j_flush_wait)) ;
+ return ret ;
+}
+/* since we never give dirty buffers to bdflush/kupdate, we have to
+** flush them ourselves. This runs through the journal lists, finds
+** old metadata in need of flushing and sends it to disk.
+** this does not end transactions, commit anything, or free
+** cnodes.
+**
+** returns the highest transaction id that was flushed last time
+*/
+static unsigned long reiserfs_journal_kupdate(struct super_block *s) {
+ struct reiserfs_journal_list *jl ;
+ int i ;
+ int start ;
+ time_t age ;
+ int ret = 0 ;
+
+ start = SB_JOURNAL_LIST_INDEX(s) ;
+
+ /* safety check to prevent flush attempts during a mount */
+ if (start < 0) {
+ return 0 ;
+ }
+ i = (start + 1) % JOURNAL_LIST_COUNT ;
+ while(i != start) {
+ jl = SB_JOURNAL_LIST(s) + i ;
+ age = CURRENT_TIME - jl->j_timestamp ;
+ if (jl->j_len > 0 && // age >= (JOURNAL_MAX_COMMIT_AGE * 2) &&
+ atomic_read(&(jl->j_nonzerolen)) > 0 &&
+ atomic_read(&(jl->j_commit_left)) == 0) {
+
+ if (jl->j_trans_id == SB_JOURNAL(s)->j_trans_id) {
+ break ;
+ }
+ /* if ret was already 1, we want to preserve that */
+ ret |= kupdate_one_transaction(s, jl) ;
+ }
+ if (atomic_read(&(jl->j_nonzerolen)) > 0) {
+ ret |= 1 ;
+ }
+ i = (i + 1) % JOURNAL_LIST_COUNT ;
+ }
+ return ret ;
+}
+
+/*
+** removes any nodes in table with name block and dev as bh.
+** only touchs the hnext and hprev pointers.
+*/
+void remove_journal_hash(struct reiserfs_journal_cnode **table, struct reiserfs_journal_list *jl,struct buffer_head *bh,
+ int remove_freed){
+ struct reiserfs_journal_cnode *cur ;
+ struct reiserfs_journal_cnode **head ;
+
+ if (!bh)
+ return ;
+
+ head= &(journal_hash(table, bh->b_dev, bh->b_blocknr)) ;
+ if (!head) {
+ return ;
+ }
+ cur = *head ;
+ while(cur) {
+ if (cur->blocknr == bh->b_blocknr && cur->dev == bh->b_dev && (jl == NULL || jl == cur->jlist) &&
+ (!test_bit(BLOCK_FREED, &cur->state) || remove_freed)) {
+ if (cur->hnext) {
+ cur->hnext->hprev = cur->hprev ;
+ }
+ if (cur->hprev) {
+ cur->hprev->hnext = cur->hnext ;
+ } else {
+ *head = cur->hnext ;
+ }
+ cur->blocknr = 0 ;
+ cur->dev = 0 ;
+ cur->state = 0 ;
+ if (cur->bh && cur->jlist) /* anybody who clears the cur->bh will also dec the nonzerolen */
+ atomic_dec(&(cur->jlist->j_nonzerolen)) ;
+ cur->bh = NULL ;
+ cur->jlist = NULL ;
+ }
+ cur = cur->hnext ;
+ }
+}
+
+static void free_journal_ram(struct super_block *p_s_sb) {
+ vfree(SB_JOURNAL(p_s_sb)->j_cnode_free_orig) ;
+ free_list_bitmaps(p_s_sb, SB_JOURNAL(p_s_sb)->j_list_bitmap) ;
+ free_bitmap_nodes(p_s_sb) ; /* must be after free_list_bitmaps */
+ if (SB_JOURNAL(p_s_sb)->j_header_bh) {
+ brelse(SB_JOURNAL(p_s_sb)->j_header_bh) ;
+ }
+ /* j_header_bh is on the journal dev, make sure not to release the journal
+ * dev until we brelse j_header_bh
+ */
+ release_journal_dev(p_s_sb, SB_JOURNAL(p_s_sb));
+ vfree(SB_JOURNAL(p_s_sb)) ;
+}
+
+/*
+** call on unmount. Only set error to 1 if you haven't made your way out
+** of read_super() yet. Any other caller must keep error at 0.
+*/
+static int do_journal_release(struct reiserfs_transaction_handle *th, struct super_block *p_s_sb, int error) {
+ struct reiserfs_transaction_handle myth ;
+
+ /* we only want to flush out transactions if we were called with error == 0
+ */
+ if (!error && !(p_s_sb->s_flags & MS_RDONLY)) {
+ /* end the current trans */
+ do_journal_end(th, p_s_sb,10, FLUSH_ALL) ;
+
+ /* make sure something gets logged to force our way into the flush code */
+ journal_join(&myth, p_s_sb, 1) ;
+ reiserfs_prepare_for_journal(p_s_sb, SB_BUFFER_WITH_SB(p_s_sb), 1) ;
+ journal_mark_dirty(&myth, p_s_sb, SB_BUFFER_WITH_SB(p_s_sb)) ;
+ do_journal_end(&myth, p_s_sb,1, FLUSH_ALL) ;
+ }
+
+ /* we decrement before we wake up, because the commit thread dies off
+ ** when it has been woken up and the count is <= 0
+ */
+ reiserfs_mounted_fs_count-- ;
+ wake_up(&reiserfs_commit_thread_wait) ;
+ sleep_on(&reiserfs_commit_thread_done) ;
+
+ free_journal_ram(p_s_sb) ;
+
+ return 0 ;
+}
+
+/*
+** call on unmount. flush all journal trans, release all alloc'd ram
+*/
+int journal_release(struct reiserfs_transaction_handle *th, struct super_block *p_s_sb) {
+ return do_journal_release(th, p_s_sb, 0) ;
+}
+/*
+** only call from an error condition inside reiserfs_read_super!
+*/
+int journal_release_error(struct reiserfs_transaction_handle *th, struct super_block *p_s_sb) {
+ return do_journal_release(th, p_s_sb, 1) ;
+}
+
+/* compares description block with commit block. returns 1 if they differ, 0 if they are the same */
+static int journal_compare_desc_commit(struct super_block *p_s_sb, struct reiserfs_journal_desc *desc,
+ struct reiserfs_journal_commit *commit) {
+ if (le32_to_cpu(commit->j_trans_id) != le32_to_cpu(desc->j_trans_id) ||
+ le32_to_cpu(commit->j_len) != le32_to_cpu(desc->j_len) ||
+ le32_to_cpu(commit->j_len) > SB_JOURNAL_TRANS_MAX(p_s_sb) ||
+ le32_to_cpu(commit->j_len) <= 0
+ ) {
+ return 1 ;
+ }
+ return 0 ;
+}
+/* returns 0 if it did not find a description block
+** returns -1 if it found a corrupt commit block
+** returns 1 if both desc and commit were valid
+*/
+static int journal_transaction_is_valid(struct super_block *p_s_sb, struct buffer_head *d_bh, unsigned long *oldest_invalid_trans_id, unsigned long *newest_mount_id) {
+ struct reiserfs_journal_desc *desc ;
+ struct reiserfs_journal_commit *commit ;
+ struct buffer_head *c_bh ;
+ unsigned long offset ;
+
+ if (!d_bh)
+ return 0 ;
+
+ desc = (struct reiserfs_journal_desc *)d_bh->b_data ;
+ if (le32_to_cpu(desc->j_len) > 0 && !memcmp(desc->j_magic, JOURNAL_DESC_MAGIC, 8)) {
+ if (oldest_invalid_trans_id && *oldest_invalid_trans_id && le32_to_cpu(desc->j_trans_id) > *oldest_invalid_trans_id) {
+ reiserfs_debug(p_s_sb, REISERFS_DEBUG_CODE, "journal-986: transaction "
+ "is valid returning because trans_id %d is greater than "
+ "oldest_invalid %lu\n", le32_to_cpu(desc->j_trans_id),
+ *oldest_invalid_trans_id);
+ return 0 ;
+ }
+ if (newest_mount_id && *newest_mount_id > le32_to_cpu(desc->j_mount_id)) {
+ reiserfs_debug(p_s_sb, REISERFS_DEBUG_CODE, "journal-1087: transaction "
+ "is valid returning because mount_id %d is less than "
+ "newest_mount_id %lu\n", desc->j_mount_id,
+ *newest_mount_id) ;
+ return -1 ;
+ }
+ if ( le32_to_cpu(desc->j_len) > SB_JOURNAL_TRANS_MAX(p_s_sb) ) {
+ reiserfs_warning(p_s_sb, "journal-2018: Bad transaction length %d encountered, ignoring transaction\n", le32_to_cpu(desc->j_len));
+ return -1 ;
+ }
+ offset = d_bh->b_blocknr - SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) ;
+
+ /* ok, we have a journal description block, lets see if the transaction was valid */
+ c_bh = journal_bread(p_s_sb, SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) +
+ ((offset + le32_to_cpu(desc->j_len) + 1) % SB_ONDISK_JOURNAL_SIZE(p_s_sb))) ;
+ if (!c_bh)
+ return 0 ;
+ commit = (struct reiserfs_journal_commit *)c_bh->b_data ;
+ if (journal_compare_desc_commit(p_s_sb, desc, commit)) {
+ reiserfs_debug(p_s_sb, REISERFS_DEBUG_CODE,
+ "journal_transaction_is_valid, commit offset %ld had bad "
+ "time %d or length %d\n",
+ c_bh->b_blocknr - SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb),
+ le32_to_cpu(commit->j_trans_id),
+ le32_to_cpu(commit->j_len));
+ brelse(c_bh) ;
+ if (oldest_invalid_trans_id) {
+ *oldest_invalid_trans_id = le32_to_cpu(desc->j_trans_id) ;
+ reiserfs_debug(p_s_sb, REISERFS_DEBUG_CODE, "journal-1004: "
+ "transaction_is_valid setting oldest invalid trans_id "
+ "to %d\n", le32_to_cpu(desc->j_trans_id)) ;
+ }
+ return -1;
+ }
+ brelse(c_bh) ;
+ reiserfs_debug(p_s_sb, REISERFS_DEBUG_CODE, "journal-1006: found valid "
+ "transaction start offset %lu, len %d id %d\n",
+ d_bh->b_blocknr - SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb),
+ le32_to_cpu(desc->j_len), le32_to_cpu(desc->j_trans_id)) ;
+ return 1 ;
+ } else {
+ return 0 ;
+ }
+}
+
+static void brelse_array(struct buffer_head **heads, int num) {
+ int i ;
+ for (i = 0 ; i < num ; i++) {
+ brelse(heads[i]) ;
+ }
+}
+
+/*
+** given the start, and values for the oldest acceptable transactions,
+** this either reads in a replays a transaction, or returns because the transaction
+** is invalid, or too old.
+*/
+static int journal_read_transaction(struct super_block *p_s_sb, unsigned long cur_dblock, unsigned long oldest_start,
+ unsigned long oldest_trans_id, unsigned long newest_mount_id) {
+ struct reiserfs_journal_desc *desc ;
+ struct reiserfs_journal_commit *commit ;
+ unsigned long trans_id = 0 ;
+ struct buffer_head *c_bh ;
+ struct buffer_head *d_bh ;
+ struct buffer_head **log_blocks = NULL ;
+ struct buffer_head **real_blocks = NULL ;
+ unsigned long trans_offset ;
+ int i;
+
+ d_bh = journal_bread(p_s_sb, cur_dblock) ;
+ if (!d_bh)
+ return 1 ;
+ desc = (struct reiserfs_journal_desc *)d_bh->b_data ;
+ trans_offset = d_bh->b_blocknr - SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) ;
+ reiserfs_debug(p_s_sb, REISERFS_DEBUG_CODE, "journal-1037: "
+ "journal_read_transaction, offset %lu, len %d mount_id %d\n",
+ d_bh->b_blocknr - SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb),
+ le32_to_cpu(desc->j_len), le32_to_cpu(desc->j_mount_id)) ;
+ if (le32_to_cpu(desc->j_trans_id) < oldest_trans_id) {
+ reiserfs_debug(p_s_sb, REISERFS_DEBUG_CODE, "journal-1039: "
+ "journal_read_trans skipping because %lu is too old\n",
+ cur_dblock - SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb)) ;
+ brelse(d_bh) ;
+ return 1 ;
+ }
+ if (le32_to_cpu(desc->j_mount_id) != newest_mount_id) {
+ reiserfs_debug(p_s_sb, REISERFS_DEBUG_CODE, "journal-1146: "
+ "journal_read_trans skipping because %d is != "
+ "newest_mount_id %lu\n", le32_to_cpu(desc->j_mount_id),
+ newest_mount_id) ;
+ brelse(d_bh) ;
+ return 1 ;
+ }
+ c_bh = journal_bread(p_s_sb, SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) +
+ ((trans_offset + le32_to_cpu(desc->j_len) + 1) %
+ SB_ONDISK_JOURNAL_SIZE(p_s_sb))) ;
+ if (!c_bh) {
+ brelse(d_bh) ;
+ return 1 ;
+ }
+ commit = (struct reiserfs_journal_commit *)c_bh->b_data ;
+ if (journal_compare_desc_commit(p_s_sb, desc, commit)) {
+ reiserfs_debug(p_s_sb, REISERFS_DEBUG_CODE, "journal_read_transaction, "
+ "commit offset %ld had bad time %d or length %d\n",
+ c_bh->b_blocknr - SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb),
+ le32_to_cpu(commit->j_trans_id), le32_to_cpu(commit->j_len));
+ brelse(c_bh) ;
+ brelse(d_bh) ;
+ return 1;
+ }
+ trans_id = le32_to_cpu(desc->j_trans_id) ;
+ /* now we know we've got a good transaction, and it was inside the valid time ranges */
+ log_blocks = reiserfs_kmalloc(le32_to_cpu(desc->j_len) * sizeof(struct buffer_head *), GFP_NOFS, p_s_sb) ;
+ real_blocks = reiserfs_kmalloc(le32_to_cpu(desc->j_len) * sizeof(struct buffer_head *), GFP_NOFS, p_s_sb) ;
+ if (!log_blocks || !real_blocks) {
+ brelse(c_bh) ;
+ brelse(d_bh) ;
+ reiserfs_kfree(log_blocks, le32_to_cpu(desc->j_len) * sizeof(struct buffer_head *), p_s_sb) ;
+ reiserfs_kfree(real_blocks, le32_to_cpu(desc->j_len) * sizeof(struct buffer_head *), p_s_sb) ;
+ reiserfs_warning(p_s_sb, "journal-1169: kmalloc failed, unable to mount FS\n") ;
+ return -1 ;
+ }
+ /* get all the buffer heads */
+ for(i = 0 ; i < le32_to_cpu(desc->j_len) ; i++) {
+ log_blocks[i] = journal_getblk(p_s_sb, SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) + (trans_offset + 1 + i) % SB_ONDISK_JOURNAL_SIZE(p_s_sb));
+ if (i < JOURNAL_TRANS_HALF) {
+ real_blocks[i] = sb_getblk(p_s_sb, le32_to_cpu(desc->j_realblock[i])) ;
+ } else {
+ real_blocks[i] = sb_getblk(p_s_sb, le32_to_cpu(commit->j_realblock[i - JOURNAL_TRANS_HALF])) ;
+ }
+ if ( real_blocks[i]->b_blocknr > SB_BLOCK_COUNT(p_s_sb) ) {
+ reiserfs_warning(p_s_sb, "journal-1207: REPLAY FAILURE fsck required! Block to replay is outside of filesystem\n");
+ goto abort_replay;
+ }
+ /* make sure we don't try to replay onto log or reserved area */
+ if (is_block_in_log_or_reserved_area(p_s_sb, real_blocks[i]->b_blocknr)) {
+ reiserfs_warning(p_s_sb, "journal-1204: REPLAY FAILURE fsck required! Trying to replay onto a log block\n") ;
+abort_replay:
+ brelse_array(log_blocks, i) ;
+ brelse_array(real_blocks, i) ;
+ brelse(c_bh) ;
+ brelse(d_bh) ;
+ reiserfs_kfree(log_blocks, le32_to_cpu(desc->j_len) * sizeof(struct buffer_head *), p_s_sb) ;
+ reiserfs_kfree(real_blocks, le32_to_cpu(desc->j_len) * sizeof(struct buffer_head *), p_s_sb) ;
+ return -1 ;
+ }
+ }
+ /* read in the log blocks, memcpy to the corresponding real block */
+ ll_rw_block(READ, le32_to_cpu(desc->j_len), log_blocks) ;
+ for (i = 0 ; i < le32_to_cpu(desc->j_len) ; i++) {
+ wait_on_buffer(log_blocks[i]) ;
+ if (!buffer_uptodate(log_blocks[i])) {
+ reiserfs_warning(p_s_sb, "journal-1212: REPLAY FAILURE fsck required! buffer write failed\n") ;
+ brelse_array(log_blocks + i, le32_to_cpu(desc->j_len) - i) ;
+ brelse_array(real_blocks, le32_to_cpu(desc->j_len)) ;
+ brelse(c_bh) ;
+ brelse(d_bh) ;
+ reiserfs_kfree(log_blocks, le32_to_cpu(desc->j_len) * sizeof(struct buffer_head *), p_s_sb) ;
+ reiserfs_kfree(real_blocks, le32_to_cpu(desc->j_len) * sizeof(struct buffer_head *), p_s_sb) ;
+ return -1 ;
+ }
+ memcpy(real_blocks[i]->b_data, log_blocks[i]->b_data, real_blocks[i]->b_size) ;
+ mark_buffer_uptodate(real_blocks[i], 1) ;
+ brelse(log_blocks[i]) ;
+ }
+ /* flush out the real blocks */
+ for (i = 0 ; i < le32_to_cpu(desc->j_len) ; i++) {
+ set_bit(BH_Dirty, &(real_blocks[i]->b_state)) ;
+ ll_rw_block(WRITE, 1, real_blocks + i) ;
+ }
+ for (i = 0 ; i < le32_to_cpu(desc->j_len) ; i++) {
+ wait_on_buffer(real_blocks[i]) ;
+ if (!buffer_uptodate(real_blocks[i])) {
+ reiserfs_warning(p_s_sb, "journal-1226: REPLAY FAILURE, fsck required! buffer write failed\n") ;
+ brelse_array(real_blocks + i, le32_to_cpu(desc->j_len) - i) ;
+ brelse(c_bh) ;
+ brelse(d_bh) ;
+ reiserfs_kfree(log_blocks, le32_to_cpu(desc->j_len) * sizeof(struct buffer_head *), p_s_sb) ;
+ reiserfs_kfree(real_blocks, le32_to_cpu(desc->j_len) * sizeof(struct buffer_head *), p_s_sb) ;
+ return -1 ;
+ }
+ brelse(real_blocks[i]) ;
+ }
+ cur_dblock = SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) + ((trans_offset + le32_to_cpu(desc->j_len) + 2) % SB_ONDISK_JOURNAL_SIZE(p_s_sb)) ;
+ reiserfs_debug(p_s_sb, REISERFS_DEBUG_CODE, "journal-1095: setting journal "
+ "start to offset %ld\n",
+ cur_dblock - SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb)) ;
+
+ /* init starting values for the first transaction, in case this is the last transaction to be replayed. */
+ SB_JOURNAL(p_s_sb)->j_start = cur_dblock - SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) ;
+ SB_JOURNAL(p_s_sb)->j_last_flush_trans_id = trans_id ;
+ SB_JOURNAL(p_s_sb)->j_trans_id = trans_id + 1;
+ brelse(c_bh) ;
+ brelse(d_bh) ;
+ reiserfs_kfree(log_blocks, le32_to_cpu(desc->j_len) * sizeof(struct buffer_head *), p_s_sb) ;
+ reiserfs_kfree(real_blocks, le32_to_cpu(desc->j_len) * sizeof(struct buffer_head *), p_s_sb) ;
+ return 0 ;
+}
+
+/*
+** read and replay the log
+** on a clean unmount, the journal header's next unflushed pointer will be to an invalid
+** transaction. This tests that before finding all the transactions in the log, whic makes normal mount times fast.
+**
+** After a crash, this starts with the next unflushed transaction, and replays until it finds one too old, or invalid.
+**
+** On exit, it sets things up so the first transaction will work correctly.
+*/
+struct buffer_head * reiserfs_breada (kdev_t dev, int block, int bufsize,
+ unsigned int max_block)
+{
+ struct buffer_head * bhlist[BUFNR];
+ unsigned int blocks = BUFNR;
+ struct buffer_head * bh;
+ int i, j;
+
+ bh = getblk (dev, block, bufsize);
+ if (buffer_uptodate (bh))
+ return (bh);
+
+ if (block + BUFNR > max_block) {
+ blocks = max_block - block;
+ }
+ bhlist[0] = bh;
+ j = 1;
+ for (i = 1; i < blocks; i++) {
+ bh = getblk (dev, block + i, bufsize);
+ if (buffer_uptodate (bh)) {
+ brelse (bh);
+ break;
+ }
+ else bhlist[j++] = bh;
+ }
+ ll_rw_block (READ, j, bhlist);
+ for(i = 1; i < j; i++)
+ brelse (bhlist[i]);
+ bh = bhlist[0];
+ wait_on_buffer (bh);
+ if (buffer_uptodate (bh))
+ return bh;
+ brelse (bh);
+ return NULL;
+}
+
+static struct buffer_head * journal_breada (struct super_block *p_s_sb, int block)
+{
+ return reiserfs_breada (SB_JOURNAL_DEV(p_s_sb), block, p_s_sb->s_blocksize,
+ SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) + SB_ONDISK_JOURNAL_SIZE(p_s_sb));
+}
+
+static int journal_read(struct super_block *p_s_sb) {
+ struct reiserfs_journal_desc *desc ;
+ unsigned long oldest_trans_id = 0;
+ unsigned long oldest_invalid_trans_id = 0 ;
+ time_t start ;
+ unsigned long oldest_start = 0;
+ unsigned long cur_dblock = 0 ;
+ unsigned long newest_mount_id = 9 ;
+ struct buffer_head *d_bh ;
+ struct reiserfs_journal_header *jh ;
+ int valid_journal_header = 0 ;
+ int replay_count = 0 ;
+ int continue_replay = 1 ;
+ int ret ;
+
+ cur_dblock = SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) ;
+ printk("reiserfs: checking transaction log (device %s) ...\n",
+ bdevname(SB_JOURNAL_DEV(p_s_sb))) ;
+ printk("for (%s)\n",
+ bdevname(p_s_sb->s_dev)) ;
+
+ start = CURRENT_TIME ;
+
+ /* step 1, read in the journal header block. Check the transaction it says
+ ** is the first unflushed, and if that transaction is not valid,
+ ** replay is done
+ */
+ SB_JOURNAL(p_s_sb)->j_header_bh = journal_bread(p_s_sb,
+ SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) +
+ SB_ONDISK_JOURNAL_SIZE(p_s_sb)) ;
+ if (!SB_JOURNAL(p_s_sb)->j_header_bh) {
+ return 1 ;
+ }
+ jh = (struct reiserfs_journal_header *)(SB_JOURNAL(p_s_sb)->j_header_bh->b_data) ;
+ if (le32_to_cpu(jh->j_first_unflushed_offset) >= 0 &&
+ le32_to_cpu(jh->j_first_unflushed_offset) < SB_ONDISK_JOURNAL_SIZE(p_s_sb) &&
+ le32_to_cpu(jh->j_last_flush_trans_id) > 0) {
+ oldest_start = SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) +
+ le32_to_cpu(jh->j_first_unflushed_offset) ;
+ oldest_trans_id = le32_to_cpu(jh->j_last_flush_trans_id) + 1;
+ newest_mount_id = le32_to_cpu(jh->j_mount_id);
+ reiserfs_debug(p_s_sb, REISERFS_DEBUG_CODE, "journal-1153: found in "
+ "header: first_unflushed_offset %d, last_flushed_trans_id "
+ "%lu\n", le32_to_cpu(jh->j_first_unflushed_offset),
+ le32_to_cpu(jh->j_last_flush_trans_id)) ;
+ valid_journal_header = 1 ;
+
+ /* now, we try to read the first unflushed offset. If it is not valid,
+ ** there is nothing more we can do, and it makes no sense to read
+ ** through the whole log.
+ */
+ d_bh = journal_bread(p_s_sb, SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) + le32_to_cpu(jh->j_first_unflushed_offset)) ;
+ ret = journal_transaction_is_valid(p_s_sb, d_bh, NULL, NULL) ;
+ if (!ret) {
+ continue_replay = 0 ;
+ }
+ brelse(d_bh) ;
+ goto start_log_replay;
+ }
+
+ if (continue_replay && is_read_only(p_s_sb->s_dev)) {
+ reiserfs_warning(p_s_sb, "clm-2076: device is readonly, unable to replay log\n") ;
+ return -1 ;
+ }
+ if (continue_replay && (p_s_sb->s_flags & MS_RDONLY)) {
+ printk("Warning, log replay starting on readonly filesystem\n") ;
+ }
+
+ /* ok, there are transactions that need to be replayed. start with the first log block, find
+ ** all the valid transactions, and pick out the oldest.
+ */
+ while(continue_replay && cur_dblock < (SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) + SB_ONDISK_JOURNAL_SIZE(p_s_sb))) {
+ d_bh = journal_breada(p_s_sb, cur_dblock) ;
+ ret = journal_transaction_is_valid(p_s_sb, d_bh, &oldest_invalid_trans_id, &newest_mount_id) ;
+ if (ret == 1) {
+ desc = (struct reiserfs_journal_desc *)d_bh->b_data ;
+ if (oldest_start == 0) { /* init all oldest_ values */
+ oldest_trans_id = le32_to_cpu(desc->j_trans_id) ;
+ oldest_start = d_bh->b_blocknr ;
+ newest_mount_id = le32_to_cpu(desc->j_mount_id) ;
+ reiserfs_debug(p_s_sb, REISERFS_DEBUG_CODE, "journal-1179: Setting "
+ "oldest_start to offset %lu, trans_id %lu\n",
+ oldest_start - SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb),
+ oldest_trans_id) ;
+ } else if (oldest_trans_id > le32_to_cpu(desc->j_trans_id)) {
+ /* one we just read was older */
+ oldest_trans_id = le32_to_cpu(desc->j_trans_id) ;
+ oldest_start = d_bh->b_blocknr ;
+ reiserfs_debug(p_s_sb, REISERFS_DEBUG_CODE, "journal-1180: Resetting "
+ "oldest_start to offset %lu, trans_id %lu\n",
+ oldest_start - SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb),
+ oldest_trans_id) ;
+ }
+ if (newest_mount_id < le32_to_cpu(desc->j_mount_id)) {
+ newest_mount_id = le32_to_cpu(desc->j_mount_id) ;
+ reiserfs_debug(p_s_sb, REISERFS_DEBUG_CODE, "journal-1299: Setting "
+ "newest_mount_id to %d\n", le32_to_cpu(desc->j_mount_id));
+ }
+ cur_dblock += le32_to_cpu(desc->j_len) + 2 ;
+ } else {
+ cur_dblock++ ;
+ }
+ brelse(d_bh) ;
+ }
+
+start_log_replay:
+ cur_dblock = oldest_start ;
+ if (oldest_trans_id) {
+ reiserfs_debug(p_s_sb, REISERFS_DEBUG_CODE, "journal-1206: Starting replay "
+ "from offset %lu, trans_id %lu\n",
+ cur_dblock - SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb),
+ oldest_trans_id) ;
+
+ }
+ replay_count = 0 ;
+ while(continue_replay && oldest_trans_id > 0) {
+ ret = journal_read_transaction(p_s_sb, cur_dblock, oldest_start, oldest_trans_id, newest_mount_id) ;
+ if (ret < 0) {
+ return ret ;
+ } else if (ret != 0) {
+ break ;
+ }
+ cur_dblock = SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) + SB_JOURNAL(p_s_sb)->j_start ;
+ replay_count++ ;
+ if (cur_dblock == oldest_start)
+ break;
+ }
+
+ if (oldest_trans_id == 0) {
+ reiserfs_debug(p_s_sb, REISERFS_DEBUG_CODE, "journal-1225: No valid "
+ "transactions found\n") ;
+ }
+ /* j_start does not get set correctly if we don't replay any transactions.
+ ** if we had a valid journal_header, set j_start to the first unflushed transaction value,
+ ** copy the trans_id from the header
+ */
+ if (valid_journal_header && replay_count == 0) {
+ SB_JOURNAL(p_s_sb)->j_start = le32_to_cpu(jh->j_first_unflushed_offset) ;
+ SB_JOURNAL(p_s_sb)->j_trans_id = le32_to_cpu(jh->j_last_flush_trans_id) + 1;
+ SB_JOURNAL(p_s_sb)->j_last_flush_trans_id = le32_to_cpu(jh->j_last_flush_trans_id) ;
+ SB_JOURNAL(p_s_sb)->j_mount_id = le32_to_cpu(jh->j_mount_id) + 1;
+ } else {
+ SB_JOURNAL(p_s_sb)->j_mount_id = newest_mount_id + 1 ;
+ }
+ reiserfs_debug(p_s_sb, REISERFS_DEBUG_CODE, "journal-1299: Setting "
+ "newest_mount_id to %lu\n", SB_JOURNAL(p_s_sb)->j_mount_id) ;
+ SB_JOURNAL(p_s_sb)->j_first_unflushed_offset = SB_JOURNAL(p_s_sb)->j_start ;
+ if (replay_count > 0) {
+ printk("reiserfs: replayed %d transactions in %lu seconds\n", replay_count,
+ CURRENT_TIME - start) ;
+ }
+ if (!is_read_only(p_s_sb->s_dev) &&
+ _update_journal_header_block(p_s_sb, SB_JOURNAL(p_s_sb)->j_start,
+ SB_JOURNAL(p_s_sb)->j_last_flush_trans_id))
+ {
+ /* replay failed, caller must call free_journal_ram and abort
+ ** the mount
+ */
+ return -1 ;
+ }
+ return 0 ;
+}
+
+
+struct reiserfs_journal_commit_task {
+ struct super_block *p_s_sb ;
+ int jindex ;
+ int wake_on_finish ; /* if this is one, we wake the task_done queue, if it
+ ** is zero, we free the whole struct on finish
+ */
+ struct reiserfs_journal_commit_task *self ;
+ struct wait_queue *task_done ;
+ struct tq_struct task ;
+} ;
+
+static void reiserfs_journal_commit_task_func(struct reiserfs_journal_commit_task *ct) {
+
+ struct reiserfs_journal_list *jl ;
+ jl = SB_JOURNAL_LIST(ct->p_s_sb) + ct->jindex ;
+
+ flush_commit_list(ct->p_s_sb, SB_JOURNAL_LIST(ct->p_s_sb) + ct->jindex, 1) ;
+
+ if (jl->j_len > 0 && atomic_read(&(jl->j_nonzerolen)) > 0 &&
+ atomic_read(&(jl->j_commit_left)) == 0) {
+ kupdate_one_transaction(ct->p_s_sb, jl) ;
+ }
+ reiserfs_kfree(ct->self, sizeof(struct reiserfs_journal_commit_task), ct->p_s_sb) ;
+}
+
+static void setup_commit_task_arg(struct reiserfs_journal_commit_task *ct,
+ struct super_block *p_s_sb,
+ int jindex) {
+ if (!ct) {
+ reiserfs_panic(NULL, "journal-1360: setup_commit_task_arg called with NULL struct\n") ;
+ }
+ ct->p_s_sb = p_s_sb ;
+ ct->jindex = jindex ;
+ ct->task_done = NULL ;
+ INIT_LIST_HEAD(&ct->task.list) ;
+ ct->task.sync = 0 ;
+ ct->task.routine = (void *)(void *)reiserfs_journal_commit_task_func ;
+ ct->self = ct ;
+ ct->task.data = (void *)ct ;
+}
+
+static void commit_flush_async(struct super_block *p_s_sb, int jindex) {
+ struct reiserfs_journal_commit_task *ct ;
+ /* using GFP_NOFS, GFP_KERNEL could try to flush inodes, which will try
+ ** to start/join a transaction, which will deadlock
+ */
+ ct = reiserfs_kmalloc(sizeof(struct reiserfs_journal_commit_task), GFP_NOFS, p_s_sb) ;
+ if (ct) {
+ setup_commit_task_arg(ct, p_s_sb, jindex) ;
+ queue_task(&(ct->task), &reiserfs_commit_thread_tq);
+ wake_up(&reiserfs_commit_thread_wait) ;
+ } else {
+#ifdef CONFIG_REISERFS_CHECK
+ reiserfs_warning(p_s_sb, "journal-1540: kmalloc failed, doing sync commit\n") ;
+#endif
+ flush_commit_list(p_s_sb, SB_JOURNAL_LIST(p_s_sb) + jindex, 1) ;
+ }
+}
+
+/*
+** this is the commit thread. It is started with kernel_thread on
+** FS mount, and journal_release() waits for it to exit.
+**
+** It could do a periodic commit, but there is a lot code for that
+** elsewhere right now, and I only wanted to implement this little
+** piece for starters.
+**
+** All we do here is sleep on the j_commit_thread_wait wait queue, and
+** then run the per filesystem commit task queue when we wakeup.
+*/
+static int reiserfs_journal_commit_thread(void *nullp) {
+
+ daemonize() ;
+
+ spin_lock_irq(&current->sigmask_lock);
+ sigfillset(&current->blocked);
+ recalc_sigpending(current);
+ spin_unlock_irq(&current->sigmask_lock);
+
+ sprintf(current->comm, "kreiserfsd") ;
+ lock_kernel() ;
+ while(1) {
+
+ while(TQ_ACTIVE(reiserfs_commit_thread_tq)) {
+ run_task_queue(&reiserfs_commit_thread_tq) ;
+ }
+
+ /* if there aren't any more filesystems left, break */
+ if (reiserfs_mounted_fs_count <= 0) {
+ run_task_queue(&reiserfs_commit_thread_tq) ;
+ break ;
+ }
+ wake_up(&reiserfs_commit_thread_done) ;
+ interruptible_sleep_on_timeout(&reiserfs_commit_thread_wait, 5 * HZ) ;
+ }
+ unlock_kernel() ;
+ wake_up(&reiserfs_commit_thread_done) ;
+ return 0 ;
+}
+
+static void journal_list_init(struct super_block *p_s_sb) {
+ int i ;
+ for (i = 0 ; i < JOURNAL_LIST_COUNT ; i++) {
+ init_waitqueue_head(&(SB_JOURNAL_LIST(p_s_sb)[i].j_commit_wait)) ;
+ init_waitqueue_head(&(SB_JOURNAL_LIST(p_s_sb)[i].j_flush_wait)) ;
+ }
+}
+
+static int release_journal_dev( struct super_block *super,
+ struct reiserfs_journal *journal )
+{
+ int result;
+
+ result = 0;
+
+ if( journal -> j_dev_bd != NULL && journal->j_dev_bd != super->s_bdev) {
+ result = blkdev_put( journal -> j_dev_bd, BDEV_FS );
+ journal -> j_dev_bd = NULL;
+ }
+ if( journal -> j_dev_file != NULL ) {
+ result = filp_close( journal -> j_dev_file, NULL );
+ journal -> j_dev_file = NULL;
+ }
+ if( result != 0 ) {
+ reiserfs_warning(super, "release_journal_dev: Cannot release journal device: %i", result );
+ }
+ return result;
+}
+
+static int journal_init_dev( struct super_block *super,
+ struct reiserfs_journal *journal,
+ const char *jdev_name )
+{
+ int result;
+ kdev_t jdev;
+ int blkdev_mode = FMODE_READ | FMODE_WRITE;
+
+ result = 0;
+
+ journal -> j_dev_bd = NULL;
+ journal -> j_dev_file = NULL;
+ jdev = SB_JOURNAL_DEV( super ) =
+ SB_ONDISK_JOURNAL_DEVICE( super ) ?
+ to_kdev_t(SB_ONDISK_JOURNAL_DEVICE( super )) : super -> s_dev;
+
+ /* there is no "jdev" option */
+
+ if (is_read_only(super->s_dev))
+ blkdev_mode = FMODE_READ;
+
+ if( ( !jdev_name || !jdev_name[ 0 ] ) ) {
+
+ /* don't add an extra reference to the device when
+ * the log is on the same disk as the FS. It makes the
+ * raid code unhappy
+ */
+ if (jdev == super->s_dev) {
+ journal->j_dev_bd = super->s_bdev;
+ return 0;
+ }
+ journal -> j_dev_bd = bdget( kdev_t_to_nr( jdev ) );
+ if( journal -> j_dev_bd ) {
+ result = blkdev_get( journal -> j_dev_bd,
+ blkdev_mode, 0, BDEV_FS );
+ if (result) {
+ bdput(journal->j_dev_bd);
+ journal->j_dev_bd = NULL;
+ }
+ } else {
+ result = -ENOMEM;
+ }
+ if( result != 0 )
+ printk( "journal_init_dev: cannot init journal device\n '%s': %i",
+ kdevname( jdev ), result );
+
+ return result;
+ }
+
+ /* "jdev" option has been found */
+
+ journal -> j_dev_file = filp_open( jdev_name, 0, 0 );
+ if( !IS_ERR( journal -> j_dev_file ) ) {
+ struct inode *jdev_inode;
+
+ jdev_inode = journal -> j_dev_file -> f_dentry -> d_inode;
+ journal -> j_dev_bd = jdev_inode -> i_bdev;
+ if( !S_ISBLK( jdev_inode -> i_mode ) ) {
+ printk( "journal_init_dev: '%s' is not a block device", jdev_name );
+ result = -ENOTBLK;
+ } else if( journal -> j_dev_file -> f_vfsmnt -> mnt_flags & MNT_NODEV) {
+ printk( "journal_init_dev: Cannot use devices on '%s'", jdev_name );
+ result = -EACCES;
+ } else if( jdev_inode -> i_bdev == NULL ) {
+ printk( "journal_init_dev: bdev unintialized for '%s'", jdev_name );
+ result = -ENOMEM;
+ } else if( ( result = blkdev_get( jdev_inode -> i_bdev,
+ blkdev_mode,
+ 0, BDEV_FS ) ) != 0 ) {
+ journal -> j_dev_bd = NULL;
+ printk( "journal_init_dev: Cannot load device '%s': %i", jdev_name,
+ result );
+ } else
+ /* ok */
+ SB_JOURNAL_DEV( super ) =
+ to_kdev_t( jdev_inode -> i_bdev -> bd_dev );
+ } else {
+ result = PTR_ERR( journal -> j_dev_file );
+ journal -> j_dev_file = NULL;
+ printk( "journal_init_dev: Cannot open '%s': %i", jdev_name, result );
+ }
+ if( result != 0 ) {
+ release_journal_dev( super, journal );
+ }
+ printk( "journal_init_dev: journal device: %s", kdevname( SB_JOURNAL_DEV( super ) ) );
+ return result;
+}
+
+/*
+** must be called once on fs mount. calls journal_read for you
+*/
+int journal_init(struct super_block *p_s_sb, const char * j_dev_name,
+ int old_format) {
+ int num_cnodes = SB_ONDISK_JOURNAL_SIZE(p_s_sb) * 2 ;
+ struct buffer_head *bhjh;
+ struct reiserfs_super_block * rs;
+ struct reiserfs_journal_header *jh;
+ struct reiserfs_journal *journal;
+
+ if (sizeof(struct reiserfs_journal_commit) != 4096 ||
+ sizeof(struct reiserfs_journal_desc) != 4096) {
+ reiserfs_warning(p_s_sb, "journal-1249: commit or desc struct not 4096 %Zd %Zd\n",
+ sizeof(struct reiserfs_journal_commit),
+ sizeof(struct reiserfs_journal_desc)) ;
+ return 1 ;
+ }
+
+ if ( SB_ONDISK_JOURNAL_SIZE(p_s_sb) < 512 ) {
+ reiserfs_warning(p_s_sb, "Journal size %d is less than 512+1 blocks, which unsupported\n", SB_ONDISK_JOURNAL_SIZE(p_s_sb));
+ return 1 ;
+ }
+
+ journal = SB_JOURNAL(p_s_sb) = vmalloc(sizeof (struct reiserfs_journal)) ;
+ if (!journal) {
+ reiserfs_warning(p_s_sb, "journal-1256: unable to get memory for journal structure\n") ;
+ return 1 ;
+ }
+ memset(journal, 0, sizeof(struct reiserfs_journal)) ;
+ INIT_LIST_HEAD(&SB_JOURNAL(p_s_sb)->j_bitmap_nodes) ;
+ INIT_LIST_HEAD (&SB_JOURNAL(p_s_sb)->j_prealloc_list);
+
+ if (reiserfs_allocate_list_bitmaps(p_s_sb,
+ SB_JOURNAL(p_s_sb)->j_list_bitmap,
+ SB_BMAP_NR(p_s_sb)))
+ goto free_and_return ;
+
+ allocate_bitmap_nodes(p_s_sb) ;
+
+ /* reserved for journal area support */
+ SB_JOURNAL_1st_RESERVED_BLOCK(p_s_sb) = (old_format ?
+ REISERFS_OLD_DISK_OFFSET_IN_BYTES /
+ p_s_sb->s_blocksize +
+ SB_BMAP_NR(p_s_sb) + 1 :
+ REISERFS_DISK_OFFSET_IN_BYTES /
+ p_s_sb->s_blocksize + 2);
+
+ if( journal_init_dev( p_s_sb, journal, j_dev_name ) != 0 ) {
+ reiserfs_warning(p_s_sb, "journal-1259: unable to initialize jornal device\n");
+ goto free_and_return;
+ }
+
+ rs = SB_DISK_SUPER_BLOCK(p_s_sb);
+
+ /* read journal header */
+ bhjh = journal_bread (p_s_sb, SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) +
+ SB_ONDISK_JOURNAL_SIZE(p_s_sb));
+ if (!bhjh) {
+ reiserfs_warning(p_s_sb, "journal-459: unable to read journal header\n") ;
+ goto free_and_return;
+ }
+ jh = (struct reiserfs_journal_header *)(bhjh->b_data);
+
+ /* make sure that journal matches to the super block */
+ if (is_reiserfs_jr(rs) &&
+ jh->jh_journal.jp_journal_magic != sb_jp_journal_magic(rs)) {
+ char jname[ 32 ];
+ char fname[ 32 ];
+
+ strcpy( jname, kdevname( SB_JOURNAL_DEV(p_s_sb) ) );
+ strcpy( fname, kdevname( p_s_sb->s_dev ) );
+ printk("journal-460: journal header magic %x (device %s) does not "
+ "match magic found in super block %x (device %s)\n",
+ jh->jh_journal.jp_journal_magic, jname,
+ sb_jp_journal_magic(rs), fname);
+ brelse (bhjh);
+ goto free_and_return;
+ }
+
+ SB_JOURNAL_TRANS_MAX(p_s_sb) = le32_to_cpu (jh->jh_journal.jp_journal_trans_max);
+ SB_JOURNAL_MAX_BATCH(p_s_sb) = le32_to_cpu (jh->jh_journal.jp_journal_max_batch);
+ SB_JOURNAL_MAX_COMMIT_AGE(p_s_sb) = le32_to_cpu (jh->jh_journal.jp_journal_max_commit_age);
+ SB_JOURNAL_MAX_TRANS_AGE(p_s_sb) = JOURNAL_MAX_TRANS_AGE;
+
+ if (SB_JOURNAL_TRANS_MAX(p_s_sb)) {
+ /* make sure these parameters are available, assign if they are not */
+ __u32 initial = SB_JOURNAL_TRANS_MAX(p_s_sb);
+ __u32 ratio = 1;
+
+ if (p_s_sb->s_blocksize < 4096)
+ ratio = 4096 / p_s_sb->s_blocksize;
+
+ if (SB_ONDISK_JOURNAL_SIZE(p_s_sb)/SB_JOURNAL_TRANS_MAX(p_s_sb) <
+ JOURNAL_MIN_RATIO)
+ {
+ SB_JOURNAL_TRANS_MAX(p_s_sb) = SB_ONDISK_JOURNAL_SIZE(p_s_sb) /
+ JOURNAL_MIN_RATIO;
+ }
+ if (SB_JOURNAL_TRANS_MAX(p_s_sb) > JOURNAL_TRANS_MAX_DEFAULT / ratio)
+ SB_JOURNAL_TRANS_MAX(p_s_sb) = JOURNAL_TRANS_MAX_DEFAULT / ratio;
+ if (SB_JOURNAL_TRANS_MAX(p_s_sb) < JOURNAL_TRANS_MIN_DEFAULT / ratio)
+ SB_JOURNAL_TRANS_MAX(p_s_sb) = JOURNAL_TRANS_MIN_DEFAULT / ratio;
+
+ if (SB_JOURNAL_TRANS_MAX(p_s_sb) != initial) {
+ printk ("reiserfs warning: wrong transaction max size (%u). "
+ "Changed to %u\n", initial, SB_JOURNAL_TRANS_MAX(p_s_sb));
+ }
+ SB_JOURNAL_MAX_BATCH(p_s_sb) = SB_JOURNAL_TRANS_MAX(p_s_sb) *
+ JOURNAL_MAX_BATCH_DEFAULT /
+ JOURNAL_TRANS_MAX_DEFAULT;
+ }
+
+ if (!SB_JOURNAL_TRANS_MAX(p_s_sb)) {
+ /*we have the file system was created by old version of mkreiserfs
+ so this field contains zero value */
+ SB_JOURNAL_TRANS_MAX(p_s_sb) = JOURNAL_TRANS_MAX_DEFAULT ;
+ SB_JOURNAL_MAX_BATCH(p_s_sb) = JOURNAL_MAX_BATCH_DEFAULT ;
+ SB_JOURNAL_MAX_COMMIT_AGE(p_s_sb) = JOURNAL_MAX_COMMIT_AGE ;
+
+ /* for blocksize >= 4096 - max transaction size is 1024. For
+ block size < 4096 trans max size is decreased proportionally */
+ if (p_s_sb->s_blocksize < 4096) {
+ SB_JOURNAL_TRANS_MAX(p_s_sb) /= (4096 / p_s_sb->s_blocksize) ;
+ SB_JOURNAL_MAX_BATCH(p_s_sb) = SB_JOURNAL_TRANS_MAX(p_s_sb)*9 / 10;
+ }
+ }
+
+ brelse (bhjh);
+
+ SB_JOURNAL(p_s_sb)->j_list_bitmap_index = 0 ;
+ SB_JOURNAL_LIST_INDEX(p_s_sb) = -10000 ; /* make sure flush_old_commits does not try to flush a list while replay is on */
+
+ /* clear out the journal list array */
+ memset(SB_JOURNAL_LIST(p_s_sb), 0,
+ sizeof(struct reiserfs_journal_list) * JOURNAL_LIST_COUNT) ;
+
+ journal_list_init(p_s_sb) ;
+
+ memset(SB_JOURNAL(p_s_sb)->j_list_hash_table, 0,
+ JOURNAL_HASH_SIZE * sizeof(struct reiserfs_journal_cnode *)) ;
+ memset(journal_writers, 0, sizeof(char *) * 512) ; /* debug code */
+
+ INIT_LIST_HEAD(&(SB_JOURNAL(p_s_sb)->j_dirty_buffers)) ;
+
+ SB_JOURNAL(p_s_sb)->j_start = 0 ;
+ SB_JOURNAL(p_s_sb)->j_len = 0 ;
+ SB_JOURNAL(p_s_sb)->j_len_alloc = 0 ;
+ atomic_set(&(SB_JOURNAL(p_s_sb)->j_wcount), 0) ;
+ SB_JOURNAL(p_s_sb)->j_bcount = 0 ;
+ SB_JOURNAL(p_s_sb)->j_trans_start_time = 0 ;
+ SB_JOURNAL(p_s_sb)->j_last = NULL ;
+ SB_JOURNAL(p_s_sb)->j_first = NULL ;
+ init_waitqueue_head(&(SB_JOURNAL(p_s_sb)->j_join_wait)) ;
+ init_waitqueue_head(&(SB_JOURNAL(p_s_sb)->j_wait)) ;
+
+ SB_JOURNAL(p_s_sb)->j_trans_id = 10 ;
+ SB_JOURNAL(p_s_sb)->j_mount_id = 10 ;
+ SB_JOURNAL(p_s_sb)->j_state = 0 ;
+ atomic_set(&(SB_JOURNAL(p_s_sb)->j_jlock), 0) ;
+ atomic_set(&(SB_JOURNAL(p_s_sb)->j_wlock), 0) ;
+ SB_JOURNAL(p_s_sb)->j_cnode_free_list = allocate_cnodes(num_cnodes) ;
+ SB_JOURNAL(p_s_sb)->j_cnode_free_orig = SB_JOURNAL(p_s_sb)->j_cnode_free_list ;
+ SB_JOURNAL(p_s_sb)->j_cnode_free = SB_JOURNAL(p_s_sb)->j_cnode_free_list ?
+ num_cnodes : 0 ;
+ SB_JOURNAL(p_s_sb)->j_cnode_used = 0 ;
+ SB_JOURNAL(p_s_sb)->j_must_wait = 0 ;
+ init_journal_hash(p_s_sb) ;
+ SB_JOURNAL_LIST(p_s_sb)[0].j_list_bitmap = get_list_bitmap(p_s_sb, SB_JOURNAL_LIST(p_s_sb)) ;
+ if (!(SB_JOURNAL_LIST(p_s_sb)[0].j_list_bitmap)) {
+ reiserfs_warning(p_s_sb, "journal-2005, get_list_bitmap failed for journal list 0\n") ;
+ goto free_and_return;
+ }
+ if (journal_read(p_s_sb) < 0) {
+ reiserfs_warning(p_s_sb, "Replay Failure, unable to mount\n") ;
+ goto free_and_return;
+ }
+ /* once the read is done, we can set this where it belongs */
+ SB_JOURNAL_LIST_INDEX(p_s_sb) = 0 ;
+
+ if (reiserfs_dont_log (p_s_sb))
+ return 0;
+
+ reiserfs_mounted_fs_count++ ;
+ if (reiserfs_mounted_fs_count <= 1) {
+ kernel_thread((void *)(void *)reiserfs_journal_commit_thread, NULL,
+ CLONE_FS | CLONE_FILES | CLONE_VM) ;
+ }
+ return 0 ;
+
+free_and_return:
+ free_journal_ram(p_s_sb);
+ return 1;
+}
+
+/*
+** test for a polite end of the current transaction. Used by file_write, and should
+** be used by delete to make sure they don't write more than can fit inside a single
+** transaction
+*/
+int journal_transaction_should_end(struct reiserfs_transaction_handle *th, int new_alloc) {
+ time_t now = CURRENT_TIME ;
+ if (reiserfs_dont_log(th->t_super))
+ return 0 ;
+ if ( SB_JOURNAL(th->t_super)->j_must_wait > 0 ||
+ (SB_JOURNAL(th->t_super)->j_len_alloc + new_alloc) >= SB_JOURNAL_MAX_BATCH(th->t_super) ||
+ atomic_read(&(SB_JOURNAL(th->t_super)->j_jlock)) ||
+ (now - SB_JOURNAL(th->t_super)->j_trans_start_time) > SB_JOURNAL_MAX_TRANS_AGE(th->t_super) ||
+ SB_JOURNAL(th->t_super)->j_cnode_free < (SB_JOURNAL_TRANS_MAX(th->t_super) * 3)) {
+ return 1 ;
+ }
+ return 0 ;
+}
+
+/* this must be called inside a transaction, and requires the
+** kernel_lock to be held
+*/
+void reiserfs_block_writes(struct reiserfs_transaction_handle *th) {
+ struct super_block *s = th->t_super ;
+ SB_JOURNAL(s)->j_must_wait = 1 ;
+ set_bit(WRITERS_BLOCKED, &SB_JOURNAL(s)->j_state) ;
+ return ;
+}
+
+/* this must be called without a transaction started, and does not
+** require BKL
+*/
+void reiserfs_allow_writes(struct super_block *s) {
+ clear_bit(WRITERS_BLOCKED, &SB_JOURNAL(s)->j_state) ;
+ wake_up(&SB_JOURNAL(s)->j_join_wait) ;
+}
+
+/* this must be called without a transaction started, and does not
+** require BKL
+*/
+void reiserfs_wait_on_write_block(struct super_block *s) {
+ wait_event(SB_JOURNAL(s)->j_join_wait,
+ !test_bit(WRITERS_BLOCKED, &SB_JOURNAL(s)->j_state)) ;
+}
+
+/* join == true if you must join an existing transaction.
+** join == false if you can deal with waiting for others to finish
+**
+** this will block until the transaction is joinable. send the number of blocks you
+** expect to use in nblocks.
+*/
+static int do_journal_begin_r(struct reiserfs_transaction_handle *th, struct super_block * p_s_sb,unsigned long nblocks,int join) {
+ time_t now = CURRENT_TIME ;
+ int old_trans_id ;
+
+ reiserfs_check_lock_depth("journal_begin") ;
+ RFALSE( p_s_sb->s_flags & MS_RDONLY,
+ "clm-2078: calling journal_begin on readonly FS") ;
+
+ if (reiserfs_dont_log(p_s_sb)) {
+ th->t_super = p_s_sb ; /* others will check this for the don't log flag */
+ return 0 ;
+ }
+ PROC_INFO_INC( p_s_sb, journal.journal_being );
+
+relock:
+ lock_journal(p_s_sb) ;
+
+ if (test_bit(WRITERS_BLOCKED, &SB_JOURNAL(p_s_sb)->j_state)) {
+ unlock_journal(p_s_sb) ;
+ reiserfs_wait_on_write_block(p_s_sb) ;
+ PROC_INFO_INC( p_s_sb, journal.journal_relock_writers );
+ goto relock ;
+ }
+
+ /* if there is no room in the journal OR
+ ** if this transaction is too old, and we weren't called joinable, wait for it to finish before beginning
+ ** we don't sleep if there aren't other writers
+ */
+
+ if ( (!join && SB_JOURNAL(p_s_sb)->j_must_wait > 0) ||
+ ( !join && (SB_JOURNAL(p_s_sb)->j_len_alloc + nblocks + 2) >= SB_JOURNAL_MAX_BATCH(p_s_sb)) ||
+ (!join && atomic_read(&(SB_JOURNAL(p_s_sb)->j_wcount)) > 0 && SB_JOURNAL(p_s_sb)->j_trans_start_time > 0 &&
+ (now - SB_JOURNAL(p_s_sb)->j_trans_start_time) > SB_JOURNAL_MAX_TRANS_AGE(p_s_sb)) ||
+ (!join && atomic_read(&(SB_JOURNAL(p_s_sb)->j_jlock)) ) ||
+ (!join && SB_JOURNAL(p_s_sb)->j_cnode_free < (SB_JOURNAL_TRANS_MAX(p_s_sb) * 3))) {
+
+ unlock_journal(p_s_sb) ; /* allow others to finish this transaction */
+
+ /* if writer count is 0, we can just force this transaction to end, and start
+ ** a new one afterwards.
+ */
+ if (atomic_read(&(SB_JOURNAL(p_s_sb)->j_wcount)) <= 0) {
+ struct reiserfs_transaction_handle myth ;
+ journal_join(&myth, p_s_sb, 1) ;
+ reiserfs_prepare_for_journal(p_s_sb, SB_BUFFER_WITH_SB(p_s_sb), 1) ;
+ journal_mark_dirty(&myth, p_s_sb, SB_BUFFER_WITH_SB(p_s_sb)) ;
+ do_journal_end(&myth, p_s_sb,1,COMMIT_NOW) ;
+ } else {
+ /* but if the writer count isn't zero, we have to wait for the current writers to finish.
+ ** They won't batch on transaction end once we set j_jlock
+ */
+ atomic_set(&(SB_JOURNAL(p_s_sb)->j_jlock), 1) ;
+ old_trans_id = SB_JOURNAL(p_s_sb)->j_trans_id ;
+ while(atomic_read(&(SB_JOURNAL(p_s_sb)->j_jlock)) &&
+ SB_JOURNAL(p_s_sb)->j_trans_id == old_trans_id) {
+ sleep_on(&(SB_JOURNAL(p_s_sb)->j_join_wait)) ;
+ }
+ }
+ PROC_INFO_INC( p_s_sb, journal.journal_relock_wcount );
+ goto relock ;
+ }
+
+ if (SB_JOURNAL(p_s_sb)->j_trans_start_time == 0) { /* we are the first writer, set trans_id */
+ SB_JOURNAL(p_s_sb)->j_trans_start_time = now ;
+ }
+ atomic_inc(&(SB_JOURNAL(p_s_sb)->j_wcount)) ;
+ SB_JOURNAL(p_s_sb)->j_len_alloc += nblocks ;
+ th->t_blocks_logged = 0 ;
+ th->t_blocks_allocated = nblocks ;
+ th->t_super = p_s_sb ;
+ th->t_trans_id = SB_JOURNAL(p_s_sb)->j_trans_id ;
+ th->t_caller = "Unknown" ;
+ unlock_journal(p_s_sb) ;
+ p_s_sb->s_dirt = 1;
+ return 0 ;
+}
+
+
+static int journal_join(struct reiserfs_transaction_handle *th, struct super_block *p_s_sb, unsigned long nblocks) {
+ return do_journal_begin_r(th, p_s_sb, nblocks, 1) ;
+}
+
+int journal_begin(struct reiserfs_transaction_handle *th, struct super_block * p_s_sb, unsigned long nblocks) {
+ return do_journal_begin_r(th, p_s_sb, nblocks, 0) ;
+}
+
+/* not used at all */
+int journal_prepare(struct super_block * p_s_sb, struct buffer_head *bh) {
+ return 0 ;
+}
+
+/*
+** puts bh into the current transaction. If it was already there, reorders removes the
+** old pointers from the hash, and puts new ones in (to make sure replay happen in the right order).
+**
+** if it was dirty, cleans and files onto the clean list. I can't let it be dirty again until the
+** transaction is committed.
+**
+** if j_len, is bigger than j_len_alloc, it pushes j_len_alloc to 10 + j_len.
+*/
+int journal_mark_dirty(struct reiserfs_transaction_handle *th, struct super_block *p_s_sb, struct buffer_head *bh) {
+ struct reiserfs_journal_cnode *cn = NULL;
+ int count_already_incd = 0 ;
+ int prepared = 0 ;
+
+ PROC_INFO_INC( p_s_sb, journal.mark_dirty );
+ if (reiserfs_dont_log(th->t_super)) {
+ mark_buffer_dirty(bh) ;
+ return 0 ;
+ }
+
+ if (th->t_trans_id != SB_JOURNAL(p_s_sb)->j_trans_id) {
+ reiserfs_panic(th->t_super, "journal-1577: handle trans id %ld != current trans id %ld\n",
+ th->t_trans_id, SB_JOURNAL(p_s_sb)->j_trans_id);
+ }
+ p_s_sb->s_dirt = 1 ;
+
+ prepared = test_and_clear_bit(BH_JPrepared, &bh->b_state) ;
+ /* already in this transaction, we are done */
+ if (buffer_journaled(bh)) {
+ PROC_INFO_INC( p_s_sb, journal.mark_dirty_already );
+ return 0 ;
+ }
+
+ /* this must be turned into a panic instead of a warning. We can't allow
+ ** a dirty or journal_dirty or locked buffer to be logged, as some changes
+ ** could get to disk too early. NOT GOOD.
+ */
+ if (!prepared || buffer_locked(bh)) {
+ reiserfs_warning(p_s_sb, "journal-1777: buffer %lu bad state %cPREPARED %cLOCKED %cDIRTY %cJDIRTY_WAIT\n", bh->b_blocknr, prepared ? ' ' : '!',
+ buffer_locked(bh) ? ' ' : '!',
+ buffer_dirty(bh) ? ' ' : '!',
+ buffer_journal_dirty(bh) ? ' ' : '!') ;
+ show_reiserfs_locks() ;
+ }
+ count_already_incd = clear_prepared_bits(bh) ;
+
+ if (atomic_read(&(SB_JOURNAL(p_s_sb)->j_wcount)) <= 0) {
+ reiserfs_warning(p_s_sb, "journal-1409: journal_mark_dirty returning because j_wcount was %d\n", atomic_read(&(SB_JOURNAL(p_s_sb)->j_wcount))) ;
+ return 1 ;
+ }
+ /* this error means I've screwed up, and we've overflowed the transaction.
+ ** Nothing can be done here, except make the FS readonly or panic.
+ */
+ if (SB_JOURNAL(p_s_sb)->j_len >= SB_JOURNAL_TRANS_MAX(p_s_sb)) {
+ reiserfs_panic(th->t_super, "journal-1413: journal_mark_dirty: j_len (%lu) is too big\n", SB_JOURNAL(p_s_sb)->j_len) ;
+ }
+
+ if (buffer_journal_dirty(bh)) {
+ count_already_incd = 1 ;
+ PROC_INFO_INC( p_s_sb, journal.mark_dirty_notjournal );
+ mark_buffer_notjournal_dirty(bh) ;
+ }
+
+ if (buffer_dirty(bh)) {
+ clear_bit(BH_Dirty, &bh->b_state) ;
+ }
+
+ if (buffer_journaled(bh)) { /* must double check after getting lock */
+ goto done ;
+ }
+
+ if (SB_JOURNAL(p_s_sb)->j_len > SB_JOURNAL(p_s_sb)->j_len_alloc) {
+ SB_JOURNAL(p_s_sb)->j_len_alloc = SB_JOURNAL(p_s_sb)->j_len + JOURNAL_PER_BALANCE_CNT ;
+ }
+
+ set_bit(BH_JDirty, &bh->b_state) ;
+
+ /* now put this guy on the end */
+ if (!cn) {
+ cn = get_cnode(p_s_sb) ;
+ if (!cn) {
+ reiserfs_panic(p_s_sb, "get_cnode failed!\n");
+ }
+
+ if (th->t_blocks_logged == th->t_blocks_allocated) {
+ th->t_blocks_allocated += JOURNAL_PER_BALANCE_CNT ;
+ SB_JOURNAL(p_s_sb)->j_len_alloc += JOURNAL_PER_BALANCE_CNT ;
+ }
+ th->t_blocks_logged++ ;
+ SB_JOURNAL(p_s_sb)->j_len++ ;
+
+ cn->bh = bh ;
+ cn->blocknr = bh->b_blocknr ;
+ cn->dev = bh->b_dev ;
+ cn->jlist = NULL ;
+ insert_journal_hash(SB_JOURNAL(p_s_sb)->j_hash_table, cn) ;
+ if (!count_already_incd) {
+ get_bh(bh) ;
+ }
+ }
+ cn->next = NULL ;
+ cn->prev = SB_JOURNAL(p_s_sb)->j_last ;
+ cn->bh = bh ;
+ if (SB_JOURNAL(p_s_sb)->j_last) {
+ SB_JOURNAL(p_s_sb)->j_last->next = cn ;
+ SB_JOURNAL(p_s_sb)->j_last = cn ;
+ } else {
+ SB_JOURNAL(p_s_sb)->j_first = cn ;
+ SB_JOURNAL(p_s_sb)->j_last = cn ;
+ }
+done:
+ return 0 ;
+}
+
+/*
+** if buffer already in current transaction, do a journal_mark_dirty
+** otherwise, just mark it dirty and move on. Used for writes to meta blocks
+** that don't need journaling
+*/
+int journal_mark_dirty_nolog(struct reiserfs_transaction_handle *th, struct super_block *p_s_sb, struct buffer_head *bh) {
+ if (reiserfs_dont_log(th->t_super) || buffer_journaled(bh) ||
+ buffer_journal_dirty(bh)) {
+ return journal_mark_dirty(th, p_s_sb, bh) ;
+ }
+ if (get_journal_hash_dev(SB_JOURNAL(p_s_sb)->j_list_hash_table, bh->b_dev,bh->b_blocknr,bh->b_size)) {
+ return journal_mark_dirty(th, p_s_sb, bh) ;
+ }
+ mark_buffer_dirty(bh) ;
+ return 0 ;
+}
+
+int journal_end(struct reiserfs_transaction_handle *th, struct super_block *p_s_sb, unsigned long nblocks) {
+ return do_journal_end(th, p_s_sb, nblocks, 0) ;
+}
+
+/* removes from the current transaction, relsing and descrementing any counters.
+** also files the removed buffer directly onto the clean list
+**
+** called by journal_mark_freed when a block has been deleted
+**
+** returns 1 if it cleaned and relsed the buffer. 0 otherwise
+*/
+static int remove_from_transaction(struct super_block *p_s_sb, unsigned long blocknr, int already_cleaned) {
+ struct buffer_head *bh ;
+ struct reiserfs_journal_cnode *cn ;
+ int ret = 0;
+
+ cn = get_journal_hash_dev(SB_JOURNAL(p_s_sb)->j_hash_table, p_s_sb->s_dev, blocknr, p_s_sb->s_blocksize) ;
+ if (!cn || !cn->bh) {
+ return ret ;
+ }
+ bh = cn->bh ;
+ if (cn->prev) {
+ cn->prev->next = cn->next ;
+ }
+ if (cn->next) {
+ cn->next->prev = cn->prev ;
+ }
+ if (cn == SB_JOURNAL(p_s_sb)->j_first) {
+ SB_JOURNAL(p_s_sb)->j_first = cn->next ;
+ }
+ if (cn == SB_JOURNAL(p_s_sb)->j_last) {
+ SB_JOURNAL(p_s_sb)->j_last = cn->prev ;
+ }
+ remove_journal_hash(SB_JOURNAL(p_s_sb)->j_hash_table, NULL, bh, 0) ;
+ mark_buffer_not_journaled(bh) ; /* don't log this one */
+
+ if (!already_cleaned) {
+ mark_buffer_notjournal_dirty(bh) ;
+ put_bh(bh) ;
+ if (atomic_read(&(bh->b_count)) < 0) {
+ reiserfs_warning(p_s_sb, "journal-1752: remove from trans, b_count < 0\n") ;
+ }
+ if (!buffer_locked(bh)) reiserfs_clean_and_file_buffer(bh) ;
+ ret = 1 ;
+ }
+ SB_JOURNAL(p_s_sb)->j_len-- ;
+ SB_JOURNAL(p_s_sb)->j_len_alloc-- ;
+ free_cnode(p_s_sb, cn) ;
+ return ret ;
+}
+
+/* removes from a specific journal list hash */
+static int remove_from_journal_list(struct super_block *s, struct reiserfs_journal_list *jl, struct buffer_head *bh, int remove_freed) {
+ remove_journal_hash(SB_JOURNAL(s)->j_list_hash_table, jl, bh, remove_freed) ;
+ return 0 ;
+}
+
+/*
+** for any cnode in a journal list, it can only be dirtied of all the
+** transactions that include it are commited to disk.
+** this checks through each transaction, and returns 1 if you are allowed to dirty,
+** and 0 if you aren't
+**
+** it is called by dirty_journal_list, which is called after flush_commit_list has gotten all the log
+** blocks for a given transaction on disk
+**
+*/
+static int can_dirty(struct reiserfs_journal_cnode *cn) {
+ kdev_t dev = cn->dev ;
+ unsigned long blocknr = cn->blocknr ;
+ struct reiserfs_journal_cnode *cur = cn->hprev ;
+ int can_dirty = 1 ;
+
+ /* first test hprev. These are all newer than cn, so any node here
+ ** with the name block number and dev means this node can't be sent
+ ** to disk right now.
+ */
+ while(cur && can_dirty) {
+ if (cur->jlist && cur->bh && cur->blocknr && cur->dev == dev &&
+ cur->blocknr == blocknr) {
+ can_dirty = 0 ;
+ }
+ cur = cur->hprev ;
+ }
+ /* then test hnext. These are all older than cn. As long as they
+ ** are committed to the log, it is safe to write cn to disk
+ */
+ cur = cn->hnext ;
+ while(cur && can_dirty) {
+ if (cur->jlist && cur->jlist->j_len > 0 &&
+ atomic_read(&(cur->jlist->j_commit_left)) > 0 && cur->bh &&
+ cur->blocknr && cur->dev == dev && cur->blocknr == blocknr) {
+ can_dirty = 0 ;
+ }
+ cur = cur->hnext ;
+ }
+ return can_dirty ;
+}
+
+/* syncs the commit blocks, but does not force the real buffers to disk
+** will wait until the current transaction is done/commited before returning
+*/
+int journal_end_sync(struct reiserfs_transaction_handle *th, struct super_block *p_s_sb, unsigned long nblocks) {
+
+ if (SB_JOURNAL(p_s_sb)->j_len == 0) {
+ reiserfs_prepare_for_journal(p_s_sb, SB_BUFFER_WITH_SB(p_s_sb), 1) ;
+ journal_mark_dirty(th, p_s_sb, SB_BUFFER_WITH_SB(p_s_sb)) ;
+ }
+ return do_journal_end(th, p_s_sb, nblocks, COMMIT_NOW | WAIT) ;
+}
+
+int show_reiserfs_locks(void) {
+
+ dump_journal_writers() ;
+ return 0 ;
+}
+
+/*
+** used to get memory back from async commits that are floating around
+** and to reclaim any blocks deleted but unusable because their commits
+** haven't hit disk yet. called from bitmap.c
+**
+** if it starts flushing things, it ors SCHEDULE_OCCURRED into repeat.
+** note, this is just if schedule has a chance of occuring. I need to
+** change flush_commit_lists to have a repeat parameter too.
+**
+*/
+void flush_async_commits(struct super_block *p_s_sb) {
+ int i ;
+
+ for (i = 0 ; i < JOURNAL_LIST_COUNT ; i++) {
+ if (i != SB_JOURNAL_LIST_INDEX(p_s_sb)) {
+ flush_commit_list(p_s_sb, SB_JOURNAL_LIST(p_s_sb) + i, 1) ;
+ }
+ }
+}
+
+/*
+** flushes any old transactions to disk
+** ends the current transaction if it is too old
+**
+** also calls flush_journal_list with old_only == 1, which allows me to reclaim
+** memory and such from the journal lists whose real blocks are all on disk.
+**
+** called by sync_dev_journal from buffer.c
+*/
+int flush_old_commits(struct super_block *p_s_sb, int immediate) {
+ int i ;
+ int count = 0;
+ int start ;
+ time_t now ;
+ struct reiserfs_transaction_handle th ;
+
+ start = SB_JOURNAL_LIST_INDEX(p_s_sb) ;
+ now = CURRENT_TIME ;
+
+ /* safety check so we don't flush while we are replaying the log during mount */
+ if (SB_JOURNAL_LIST_INDEX(p_s_sb) < 0) {
+ return 0 ;
+ }
+ /* starting with oldest, loop until we get to the start */
+ i = (SB_JOURNAL_LIST_INDEX(p_s_sb) + 1) % JOURNAL_LIST_COUNT ;
+ while(i != start) {
+ if (SB_JOURNAL_LIST(p_s_sb)[i].j_len > 0 && ((now - SB_JOURNAL_LIST(p_s_sb)[i].j_timestamp) > SB_JOURNAL_MAX_COMMIT_AGE(p_s_sb) ||
+ immediate)) {
+ /* we have to check again to be sure the current transaction did not change */
+ if (i != SB_JOURNAL_LIST_INDEX(p_s_sb)) {
+ flush_commit_list(p_s_sb, SB_JOURNAL_LIST(p_s_sb) + i, 1) ;
+ }
+ }
+ i = (i + 1) % JOURNAL_LIST_COUNT ;
+ count++ ;
+ }
+ /* now, check the current transaction. If there are no writers, and it is too old, finish it, and
+ ** force the commit blocks to disk
+ */
+ if (!immediate && atomic_read(&(SB_JOURNAL(p_s_sb)->j_wcount)) <= 0 &&
+ SB_JOURNAL(p_s_sb)->j_trans_start_time > 0 &&
+ SB_JOURNAL(p_s_sb)->j_len > 0 &&
+ (now - SB_JOURNAL(p_s_sb)->j_trans_start_time) > SB_JOURNAL_MAX_TRANS_AGE(p_s_sb)) {
+ journal_join(&th, p_s_sb, 1) ;
+ reiserfs_prepare_for_journal(p_s_sb, SB_BUFFER_WITH_SB(p_s_sb), 1) ;
+ journal_mark_dirty(&th, p_s_sb, SB_BUFFER_WITH_SB(p_s_sb)) ;
+ do_journal_end(&th, p_s_sb,1, COMMIT_NOW) ;
+ } else if (immediate) { /* belongs above, but I wanted this to be very explicit as a special case. If they say to
+ flush, we must be sure old transactions hit the disk too. */
+ journal_join(&th, p_s_sb, 1) ;
+ reiserfs_prepare_for_journal(p_s_sb, SB_BUFFER_WITH_SB(p_s_sb), 1) ;
+ journal_mark_dirty(&th, p_s_sb, SB_BUFFER_WITH_SB(p_s_sb)) ;
+ do_journal_end(&th, p_s_sb,1, COMMIT_NOW | WAIT) ;
+ }
+ reiserfs_journal_kupdate(p_s_sb) ;
+ return 0 ;
+}
+
+/*
+** returns 0 if do_journal_end should return right away, returns 1 if do_journal_end should finish the commit
+**
+** if the current transaction is too old, but still has writers, this will wait on j_join_wait until all
+** the writers are done. By the time it wakes up, the transaction it was called has already ended, so it just
+** flushes the commit list and returns 0.
+**
+** Won't batch when flush or commit_now is set. Also won't batch when others are waiting on j_join_wait.
+**
+** Note, we can't allow the journal_end to proceed while there are still writers in the log.
+*/
+static int check_journal_end(struct reiserfs_transaction_handle *th, struct super_block * p_s_sb,
+ unsigned long nblocks, int flags) {
+
+ time_t now ;
+ int flush = flags & FLUSH_ALL ;
+ int commit_now = flags & COMMIT_NOW ;
+ int wait_on_commit = flags & WAIT ;
+
+ if (th->t_trans_id != SB_JOURNAL(p_s_sb)->j_trans_id) {
+ reiserfs_panic(th->t_super, "journal-1577: handle trans id %ld != current trans id %ld\n",
+ th->t_trans_id, SB_JOURNAL(p_s_sb)->j_trans_id);
+ }
+
+ SB_JOURNAL(p_s_sb)->j_len_alloc -= (th->t_blocks_allocated - th->t_blocks_logged) ;
+ if (atomic_read(&(SB_JOURNAL(p_s_sb)->j_wcount)) > 0) { /* <= 0 is allowed. unmounting might not call begin */
+ atomic_dec(&(SB_JOURNAL(p_s_sb)->j_wcount)) ;
+ }
+
+ /* BUG, deal with case where j_len is 0, but people previously freed blocks need to be released
+ ** will be dealt with by next transaction that actually writes something, but should be taken
+ ** care of in this trans
+ */
+ if (SB_JOURNAL(p_s_sb)->j_len == 0) {
+ int wcount = atomic_read(&(SB_JOURNAL(p_s_sb)->j_wcount)) ;
+ unlock_journal(p_s_sb) ;
+ if (atomic_read(&(SB_JOURNAL(p_s_sb)->j_jlock)) > 0 && wcount <= 0) {
+ atomic_dec(&(SB_JOURNAL(p_s_sb)->j_jlock)) ;
+ wake_up(&(SB_JOURNAL(p_s_sb)->j_join_wait)) ;
+ }
+ return 0 ;
+ }
+ /* if wcount > 0, and we are called to with flush or commit_now,
+ ** we wait on j_join_wait. We will wake up when the last writer has
+ ** finished the transaction, and started it on its way to the disk.
+ ** Then, we flush the commit or journal list, and just return 0
+ ** because the rest of journal end was already done for this transaction.
+ */
+ if (atomic_read(&(SB_JOURNAL(p_s_sb)->j_wcount)) > 0) {
+ if (flush || commit_now) {
+ int orig_jindex = SB_JOURNAL_LIST_INDEX(p_s_sb) ;
+ atomic_set(&(SB_JOURNAL(p_s_sb)->j_jlock), 1) ;
+ if (flush) {
+ SB_JOURNAL(p_s_sb)->j_next_full_flush = 1 ;
+ }
+ unlock_journal(p_s_sb) ;
+ /* sleep while the current transaction is still j_jlocked */
+ while(atomic_read(&(SB_JOURNAL(p_s_sb)->j_jlock)) &&
+ SB_JOURNAL(p_s_sb)->j_trans_id == th->t_trans_id) {
+ sleep_on(&(SB_JOURNAL(p_s_sb)->j_join_wait)) ;
+ }
+ if (commit_now) {
+ if (wait_on_commit) {
+ flush_commit_list(p_s_sb, SB_JOURNAL_LIST(p_s_sb) + orig_jindex, 1) ;
+ } else {
+ commit_flush_async(p_s_sb, orig_jindex) ;
+ }
+ }
+ return 0 ;
+ }
+ unlock_journal(p_s_sb) ;
+ return 0 ;
+ }
+
+ /* deal with old transactions where we are the last writers */
+ now = CURRENT_TIME ;
+ if ((now - SB_JOURNAL(p_s_sb)->j_trans_start_time) > SB_JOURNAL_MAX_TRANS_AGE(p_s_sb)) {
+ commit_now = 1 ;
+ SB_JOURNAL(p_s_sb)->j_next_async_flush = 1 ;
+ }
+ /* don't batch when someone is waiting on j_join_wait */
+ /* don't batch when syncing the commit or flushing the whole trans */
+ if (!(SB_JOURNAL(p_s_sb)->j_must_wait > 0) && !(atomic_read(&(SB_JOURNAL(p_s_sb)->j_jlock))) && !flush && !commit_now &&
+ (SB_JOURNAL(p_s_sb)->j_len < SB_JOURNAL_MAX_BATCH(p_s_sb)) &&
+ SB_JOURNAL(p_s_sb)->j_len_alloc < SB_JOURNAL_MAX_BATCH(p_s_sb) && SB_JOURNAL(p_s_sb)->j_cnode_free > (SB_JOURNAL_TRANS_MAX(p_s_sb) * 3)) {
+ SB_JOURNAL(p_s_sb)->j_bcount++ ;
+ unlock_journal(p_s_sb) ;
+ return 0 ;
+ }
+
+ if (SB_JOURNAL(p_s_sb)->j_start > SB_ONDISK_JOURNAL_SIZE(p_s_sb)) {
+ reiserfs_panic(p_s_sb, "journal-003: journal_end: j_start (%ld) is too high\n", SB_JOURNAL(p_s_sb)->j_start) ;
+ }
+ return 1 ;
+}
+
+/*
+** Does all the work that makes deleting blocks safe.
+** when deleting a block mark BH_JNew, just remove it from the current transaction, clean it's buffer_head and move on.
+**
+** otherwise:
+** set a bit for the block in the journal bitmap. That will prevent it from being allocated for unformatted nodes
+** before this transaction has finished.
+**
+** mark any cnodes for this block as BLOCK_FREED, and clear their bh pointers. That will prevent any old transactions with
+** this block from trying to flush to the real location. Since we aren't removing the cnode from the journal_list_hash,
+** the block can't be reallocated yet.
+**
+** Then remove it from the current transaction, decrementing any counters and filing it on the clean list.
+*/
+int journal_mark_freed(struct reiserfs_transaction_handle *th, struct super_block *p_s_sb, unsigned long blocknr) {
+ struct reiserfs_journal_cnode *cn = NULL ;
+ struct buffer_head *bh = NULL ;
+ struct reiserfs_list_bitmap *jb = NULL ;
+ int cleaned = 0 ;
+
+ if (reiserfs_dont_log(th->t_super)) {
+ bh = sb_get_hash_table(p_s_sb, blocknr) ;
+ if (bh && buffer_dirty (bh)) {
+ reiserfs_warning (p_s_sb, "journal_mark_freed(dont_log): dirty buffer on hash list: %lx %ld\n", bh->b_state, blocknr);
+ BUG ();
+ }
+ brelse (bh);
+ return 0 ;
+ }
+ bh = sb_get_hash_table(p_s_sb, blocknr) ;
+ /* if it is journal new, we just remove it from this transaction */
+ if (bh && buffer_journal_new(bh)) {
+ mark_buffer_notjournal_new(bh) ;
+ clear_prepared_bits(bh) ;
+ cleaned = remove_from_transaction(p_s_sb, blocknr, cleaned) ;
+ } else {
+ /* set the bit for this block in the journal bitmap for this transaction */
+ jb = SB_JOURNAL_LIST(p_s_sb)[SB_JOURNAL_LIST_INDEX(p_s_sb)].j_list_bitmap ;
+ if (!jb) {
+ reiserfs_panic(p_s_sb, "journal-1702: journal_mark_freed, journal_list_bitmap is NULL\n") ;
+ }
+ set_bit_in_list_bitmap(p_s_sb, blocknr, jb) ;
+
+ /* Note, the entire while loop is not allowed to schedule. */
+
+ if (bh) {
+ clear_prepared_bits(bh) ;
+ }
+ cleaned = remove_from_transaction(p_s_sb, blocknr, cleaned) ;
+
+ /* find all older transactions with this block, make sure they don't try to write it out */
+ cn = get_journal_hash_dev(SB_JOURNAL(p_s_sb)->j_list_hash_table, p_s_sb->s_dev, blocknr, p_s_sb->s_blocksize) ;
+ while (cn) {
+ if (p_s_sb->s_dev == cn->dev && blocknr == cn->blocknr) {
+ set_bit(BLOCK_FREED, &cn->state) ;
+ if (cn->bh) {
+ if (!cleaned) {
+ /* remove_from_transaction will brelse the buffer if it was
+ ** in the current trans
+ */
+ mark_buffer_notjournal_dirty(cn->bh) ;
+ cleaned = 1 ;
+ put_bh(cn->bh) ;
+ if (atomic_read(&(cn->bh->b_count)) < 0) {
+ reiserfs_warning(p_s_sb, "journal-2138: cn->bh->b_count < 0\n") ;
+ }
+ }
+ if (cn->jlist) { /* since we are clearing the bh, we MUST dec nonzerolen */
+ atomic_dec(&(cn->jlist->j_nonzerolen)) ;
+ }
+ cn->bh = NULL ;
+ }
+ }
+ cn = cn->hnext ;
+ }
+ }
+
+ if (bh) {
+ reiserfs_clean_and_file_buffer(bh) ;
+ put_bh(bh) ; /* get_hash grabs the buffer */
+ if (atomic_read(&(bh->b_count)) < 0) {
+ reiserfs_warning(p_s_sb, "journal-2165: bh->b_count < 0\n") ;
+ }
+ }
+ return 0 ;
+}
+
+void reiserfs_update_inode_transaction(struct inode *inode) {
+
+ inode->u.reiserfs_i.i_trans_index = SB_JOURNAL_LIST_INDEX(inode->i_sb);
+
+ inode->u.reiserfs_i.i_trans_id = SB_JOURNAL(inode->i_sb)->j_trans_id ;
+}
+
+void reiserfs_update_tail_transaction(struct inode *inode) {
+
+ inode->u.reiserfs_i.i_tail_trans_index = SB_JOURNAL_LIST_INDEX(inode->i_sb);
+
+ inode->u.reiserfs_i.i_tail_trans_id = SB_JOURNAL(inode->i_sb)->j_trans_id ;
+}
+
+static void __commit_trans_index(struct inode *inode, unsigned long id,
+ unsigned long index)
+{
+ struct reiserfs_journal_list *jl ;
+ struct reiserfs_transaction_handle th ;
+ struct super_block *sb = inode->i_sb ;
+
+ jl = SB_JOURNAL_LIST(sb) + index;
+
+ /* is it from the current transaction, or from an unknown transaction? */
+ if (id == SB_JOURNAL(sb)->j_trans_id) {
+ journal_join(&th, sb, 1) ;
+ journal_end_sync(&th, sb, 1) ;
+ } else if (jl->j_trans_id == id) {
+ flush_commit_list(sb, jl, 1) ;
+ }
+ /* if the transaction id does not match, this list is long since flushed
+ ** and we don't have to do anything here
+ */
+}
+void reiserfs_commit_for_tail(struct inode *inode) {
+ unsigned long id = inode->u.reiserfs_i.i_tail_trans_id;
+ unsigned long index = inode->u.reiserfs_i.i_tail_trans_index;
+
+ /* for tails, if this info is unset there's nothing to commit */
+ if (id && index)
+ __commit_trans_index(inode, id, index);
+}
+void reiserfs_commit_for_inode(struct inode *inode) {
+ unsigned long id = inode->u.reiserfs_i.i_trans_id;
+ unsigned long index = inode->u.reiserfs_i.i_trans_index;
+
+ /* for the whole inode, assume unset id or index means it was
+ * changed in the current transaction. More conservative
+ */
+ if (!id || !index)
+ reiserfs_update_inode_transaction(inode) ;
+
+ __commit_trans_index(inode, id, index);
+}
+
+void reiserfs_restore_prepared_buffer(struct super_block *p_s_sb,
+ struct buffer_head *bh) {
+ PROC_INFO_INC( p_s_sb, journal.restore_prepared );
+ if (reiserfs_dont_log (p_s_sb))
+ return;
+
+ if (!bh) {
+ return ;
+ }
+ clear_bit(BH_JPrepared, &bh->b_state) ;
+}
+
+extern struct tree_balance *cur_tb ;
+/*
+** before we can change a metadata block, we have to make sure it won't
+** be written to disk while we are altering it. So, we must:
+** clean it
+** wait on it.
+**
+*/
+void reiserfs_prepare_for_journal(struct super_block *p_s_sb,
+ struct buffer_head *bh, int wait) {
+ int retry_count = 0 ;
+
+ PROC_INFO_INC( p_s_sb, journal.prepare );
+ if (reiserfs_dont_log (p_s_sb))
+ return;
+
+ while(!test_bit(BH_JPrepared, &bh->b_state) ||
+ (wait && buffer_locked(bh))) {
+ if (buffer_journaled(bh)) {
+ set_bit(BH_JPrepared, &bh->b_state) ;
+ return ;
+ }
+ set_bit(BH_JPrepared, &bh->b_state) ;
+ if (wait) {
+ RFALSE( buffer_locked(bh) && cur_tb != NULL,
+ "waiting while do_balance was running\n") ;
+ wait_on_buffer(bh) ;
+ }
+ PROC_INFO_INC( p_s_sb, journal.prepare_retry );
+ retry_count++ ;
+ }
+}
+
+/*
+** long and ugly. If flush, will not return until all commit
+** blocks and all real buffers in the trans are on disk.
+** If no_async, won't return until all commit blocks are on disk.
+**
+** keep reading, there are comments as you go along
+*/
+static int do_journal_end(struct reiserfs_transaction_handle *th, struct super_block * p_s_sb, unsigned long nblocks,
+ int flags) {
+ struct reiserfs_journal_cnode *cn, *next, *jl_cn;
+ struct reiserfs_journal_cnode *last_cn = NULL;
+ struct reiserfs_journal_desc *desc ;
+ struct reiserfs_journal_commit *commit ;
+ struct buffer_head *c_bh ; /* commit bh */
+ struct buffer_head *d_bh ; /* desc bh */
+ int cur_write_start = 0 ; /* start index of current log write */
+ int cur_blocks_left = 0 ; /* number of journal blocks left to write */
+ int old_start ;
+ int i ;
+ int jindex ;
+ int orig_jindex ;
+ int flush = flags & FLUSH_ALL ;
+ int commit_now = flags & COMMIT_NOW ;
+ int wait_on_commit = flags & WAIT ;
+ struct reiserfs_super_block *rs ;
+
+ if (reiserfs_dont_log(th->t_super)) {
+ return 0 ;
+ }
+
+ lock_journal(p_s_sb) ;
+ if (SB_JOURNAL(p_s_sb)->j_next_full_flush) {
+ flags |= FLUSH_ALL ;
+ flush = 1 ;
+ }
+ if (SB_JOURNAL(p_s_sb)->j_next_async_flush) {
+ flags |= COMMIT_NOW ;
+ commit_now = 1 ;
+ }
+
+ /* check_journal_end locks the journal, and unlocks if it does not return 1
+ ** it tells us if we should continue with the journal_end, or just return
+ */
+ if (!check_journal_end(th, p_s_sb, nblocks, flags)) {
+ return 0 ;
+ }
+
+ /* check_journal_end might set these, check again */
+ if (SB_JOURNAL(p_s_sb)->j_next_full_flush) {
+ flush = 1 ;
+ }
+ if (SB_JOURNAL(p_s_sb)->j_next_async_flush) {
+ commit_now = 1 ;
+ }
+ /*
+ ** j must wait means we have to flush the log blocks, and the real blocks for
+ ** this transaction
+ */
+ if (SB_JOURNAL(p_s_sb)->j_must_wait > 0) {
+ flush = 1 ;
+ }
+
+#ifdef REISERFS_PREALLOCATE
+ reiserfs_discard_all_prealloc(th); /* it should not involve new blocks into
+ * the transaction */
+#endif
+
+ rs = SB_DISK_SUPER_BLOCK(p_s_sb) ;
+ /* setup description block */
+ d_bh = journal_getblk(p_s_sb, SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) + SB_JOURNAL(p_s_sb)->j_start) ;
+ mark_buffer_uptodate(d_bh, 1) ;
+ desc = (struct reiserfs_journal_desc *)(d_bh)->b_data ;
+ memset(desc, 0, sizeof(struct reiserfs_journal_desc)) ;
+ memcpy(desc->j_magic, JOURNAL_DESC_MAGIC, 8) ;
+ desc->j_trans_id = cpu_to_le32(SB_JOURNAL(p_s_sb)->j_trans_id) ;
+
+ /* setup commit block. Don't write (keep it clean too) this one until after everyone else is written */
+ c_bh = journal_getblk(p_s_sb, SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) +
+ ((SB_JOURNAL(p_s_sb)->j_start + SB_JOURNAL(p_s_sb)->j_len + 1) % SB_ONDISK_JOURNAL_SIZE(p_s_sb))) ;
+ commit = (struct reiserfs_journal_commit *)c_bh->b_data ;
+ memset(commit, 0, sizeof(struct reiserfs_journal_commit)) ;
+ commit->j_trans_id = cpu_to_le32(SB_JOURNAL(p_s_sb)->j_trans_id) ;
+ mark_buffer_uptodate(c_bh, 1) ;
+
+ /* init this journal list */
+ atomic_set(&(SB_JOURNAL_LIST(p_s_sb)[SB_JOURNAL_LIST_INDEX(p_s_sb)].j_older_commits_done), 0) ;
+ SB_JOURNAL_LIST(p_s_sb)[SB_JOURNAL_LIST_INDEX(p_s_sb)].j_trans_id = SB_JOURNAL(p_s_sb)->j_trans_id ;
+ SB_JOURNAL_LIST(p_s_sb)[SB_JOURNAL_LIST_INDEX(p_s_sb)].j_timestamp = SB_JOURNAL(p_s_sb)->j_trans_start_time ;
+ SB_JOURNAL_LIST(p_s_sb)[SB_JOURNAL_LIST_INDEX(p_s_sb)].j_commit_bh = c_bh ;
+ SB_JOURNAL_LIST(p_s_sb)[SB_JOURNAL_LIST_INDEX(p_s_sb)].j_start = SB_JOURNAL(p_s_sb)->j_start ;
+ SB_JOURNAL_LIST(p_s_sb)[SB_JOURNAL_LIST_INDEX(p_s_sb)].j_len = SB_JOURNAL(p_s_sb)->j_len ;
+ atomic_set(&(SB_JOURNAL_LIST(p_s_sb)[SB_JOURNAL_LIST_INDEX(p_s_sb)].j_nonzerolen), SB_JOURNAL(p_s_sb)->j_len) ;
+ atomic_set(&(SB_JOURNAL_LIST(p_s_sb)[SB_JOURNAL_LIST_INDEX(p_s_sb)].j_commit_left), SB_JOURNAL(p_s_sb)->j_len + 2);
+ SB_JOURNAL_LIST(p_s_sb)[SB_JOURNAL_LIST_INDEX(p_s_sb)].j_realblock = NULL ;
+ atomic_set(&(SB_JOURNAL_LIST(p_s_sb)[SB_JOURNAL_LIST_INDEX(p_s_sb)].j_commit_flushing), 1) ;
+ atomic_set(&(SB_JOURNAL_LIST(p_s_sb)[SB_JOURNAL_LIST_INDEX(p_s_sb)].j_flushing), 1) ;
+
+ /* which is faster, locking/unlocking at the start and end of the for
+ ** or locking once per iteration around the insert_journal_hash?
+ ** eitherway, we are write locking insert_journal_hash. The ENTIRE FOR
+ ** LOOP MUST not cause schedule to occur.
+ */
+
+ /* for each real block, add it to the journal list hash,
+ ** copy into real block index array in the commit or desc block
+ */
+ for (i = 0, cn = SB_JOURNAL(p_s_sb)->j_first ; cn ; cn = cn->next, i++) {
+ if (test_bit(BH_JDirty, &cn->bh->b_state) ) {
+ jl_cn = get_cnode(p_s_sb) ;
+ if (!jl_cn) {
+ reiserfs_panic(p_s_sb, "journal-1676, get_cnode returned NULL\n") ;
+ }
+ if (i == 0) {
+ SB_JOURNAL_LIST(p_s_sb)[SB_JOURNAL_LIST_INDEX(p_s_sb)].j_realblock = jl_cn ;
+ }
+ jl_cn->prev = last_cn ;
+ jl_cn->next = NULL ;
+ if (last_cn) {
+ last_cn->next = jl_cn ;
+ }
+ last_cn = jl_cn ;
+ /* make sure the block we are trying to log is not a block
+ of journal or reserved area */
+
+ if (is_block_in_log_or_reserved_area(p_s_sb, cn->bh->b_blocknr)) {
+ reiserfs_panic(p_s_sb, "journal-2332: Trying to log block %lu, which is a log block\n", cn->bh->b_blocknr) ;
+ }
+ jl_cn->blocknr = cn->bh->b_blocknr ;
+ jl_cn->state = 0 ;
+ jl_cn->dev = cn->bh->b_dev ;
+ jl_cn->bh = cn->bh ;
+ jl_cn->jlist = SB_JOURNAL_LIST(p_s_sb) + SB_JOURNAL_LIST_INDEX(p_s_sb) ;
+ insert_journal_hash(SB_JOURNAL(p_s_sb)->j_list_hash_table, jl_cn) ;
+ if (i < JOURNAL_TRANS_HALF) {
+ desc->j_realblock[i] = cpu_to_le32(cn->bh->b_blocknr) ;
+ } else {
+ commit->j_realblock[i - JOURNAL_TRANS_HALF] = cpu_to_le32(cn->bh->b_blocknr) ;
+ }
+ } else {
+ i-- ;
+ }
+ }
+
+ desc->j_len = cpu_to_le32(SB_JOURNAL(p_s_sb)->j_len) ;
+ desc->j_mount_id = cpu_to_le32(SB_JOURNAL(p_s_sb)->j_mount_id) ;
+ desc->j_trans_id = cpu_to_le32(SB_JOURNAL(p_s_sb)->j_trans_id) ;
+ commit->j_len = cpu_to_le32(SB_JOURNAL(p_s_sb)->j_len) ;
+
+ /* special check in case all buffers in the journal were marked for not logging */
+ if (SB_JOURNAL(p_s_sb)->j_len == 0) {
+ brelse(d_bh) ;
+ brelse(c_bh) ;
+ unlock_journal(p_s_sb) ;
+reiserfs_warning(p_s_sb, "journal-2020: do_journal_end: BAD desc->j_len is ZERO\n") ;
+ atomic_set(&(SB_JOURNAL(p_s_sb)->j_jlock), 0) ;
+ wake_up(&(SB_JOURNAL(p_s_sb)->j_join_wait)) ;
+ return 0 ;
+ }
+
+ /* first data block is j_start + 1, so add one to cur_write_start wherever you use it */
+ cur_write_start = SB_JOURNAL(p_s_sb)->j_start ;
+ cur_blocks_left = SB_JOURNAL(p_s_sb)->j_len ;
+ cn = SB_JOURNAL(p_s_sb)->j_first ;
+ jindex = 1 ; /* start at one so we don't get the desc again */
+ while(cur_blocks_left > 0) {
+ /* copy all the real blocks into log area. dirty log blocks */
+ if (test_bit(BH_JDirty, &cn->bh->b_state)) {
+ struct buffer_head *tmp_bh ;
+ tmp_bh = journal_getblk(p_s_sb, SB_ONDISK_JOURNAL_1st_BLOCK(p_s_sb) +
+ ((cur_write_start + jindex) % SB_ONDISK_JOURNAL_SIZE(p_s_sb))) ;
+ mark_buffer_uptodate(tmp_bh, 1) ;
+ memcpy(tmp_bh->b_data, cn->bh->b_data, cn->bh->b_size) ;
+ jindex++ ;
+ } else {
+ /* JDirty cleared sometime during transaction. don't log this one */
+ reiserfs_warning(p_s_sb, "journal-2048: do_journal_end: BAD, buffer in journal hash, but not JDirty!\n") ;
+ }
+ cn = cn->next ;
+ cur_blocks_left-- ;
+ }
+
+ /* we are done with both the c_bh and d_bh, but
+ ** c_bh must be written after all other commit blocks,
+ ** so we dirty/relse c_bh in flush_commit_list, with commit_left <= 1.
+ */
+
+ /* now loop through and mark all buffers from this transaction as JDirty_wait
+ ** clear the JDirty bit, clear BH_JNew too.
+ ** if they weren't JDirty, they weren't logged, just relse them and move on
+ */
+ cn = SB_JOURNAL(p_s_sb)->j_first ;
+ while(cn) {
+ clear_bit(BH_JNew, &(cn->bh->b_state)) ;
+ if (test_bit(BH_JDirty, &(cn->bh->b_state))) {
+ set_bit(BH_JDirty_wait, &(cn->bh->b_state)) ;
+ clear_bit(BH_JDirty, &(cn->bh->b_state)) ;
+ } else {
+ brelse(cn->bh) ;
+ }
+ next = cn->next ;
+ free_cnode(p_s_sb, cn) ;
+ cn = next ;
+ }
+
+ /* unlock the journal list for committing and flushing */
+ atomic_set(&(SB_JOURNAL_LIST(p_s_sb)[SB_JOURNAL_LIST_INDEX(p_s_sb)].j_commit_flushing), 0) ;
+ atomic_set(&(SB_JOURNAL_LIST(p_s_sb)[SB_JOURNAL_LIST_INDEX(p_s_sb)].j_flushing), 0) ;
+
+ orig_jindex = SB_JOURNAL_LIST_INDEX(p_s_sb) ;
+ jindex = (SB_JOURNAL_LIST_INDEX(p_s_sb) + 1) % JOURNAL_LIST_COUNT ;
+ SB_JOURNAL_LIST_INDEX(p_s_sb) = jindex ;
+
+ /* write any buffers that must hit disk before this commit is done */
+ fsync_buffers_list(&(SB_JOURNAL(p_s_sb)->j_dirty_buffers)) ;
+
+ /* honor the flush and async wishes from the caller */
+ if (flush) {
+
+ flush_commit_list(p_s_sb, SB_JOURNAL_LIST(p_s_sb) + orig_jindex, 1) ;
+ flush_journal_list(p_s_sb, SB_JOURNAL_LIST(p_s_sb) + orig_jindex , 1) ;
+ } else if (commit_now) {
+ if (wait_on_commit) {
+ flush_commit_list(p_s_sb, SB_JOURNAL_LIST(p_s_sb) + orig_jindex, 1) ;
+ } else {
+ commit_flush_async(p_s_sb, orig_jindex) ;
+ }
+ }
+
+ /* reset journal values for the next transaction */
+ old_start = SB_JOURNAL(p_s_sb)->j_start ;
+ SB_JOURNAL(p_s_sb)->j_start = (SB_JOURNAL(p_s_sb)->j_start + SB_JOURNAL(p_s_sb)->j_len + 2) % SB_ONDISK_JOURNAL_SIZE(p_s_sb);
+ atomic_set(&(SB_JOURNAL(p_s_sb)->j_wcount), 0) ;
+ SB_JOURNAL(p_s_sb)->j_bcount = 0 ;
+ SB_JOURNAL(p_s_sb)->j_last = NULL ;
+ SB_JOURNAL(p_s_sb)->j_first = NULL ;
+ SB_JOURNAL(p_s_sb)->j_len = 0 ;
+ SB_JOURNAL(p_s_sb)->j_trans_start_time = 0 ;
+ SB_JOURNAL(p_s_sb)->j_trans_id++ ;
+ SB_JOURNAL(p_s_sb)->j_must_wait = 0 ;
+ SB_JOURNAL(p_s_sb)->j_len_alloc = 0 ;
+ SB_JOURNAL(p_s_sb)->j_next_full_flush = 0 ;
+ SB_JOURNAL(p_s_sb)->j_next_async_flush = 0 ;
+ init_journal_hash(p_s_sb) ;
+
+ /* if the next transaction has any chance of wrapping, flush
+ ** transactions that might get overwritten. If any journal lists are very
+ ** old flush them as well.
+ */
+ for (i = 0 ; i < JOURNAL_LIST_COUNT ; i++) {
+ jindex = i ;
+ if (SB_JOURNAL_LIST(p_s_sb)[jindex].j_len > 0 && SB_JOURNAL(p_s_sb)->j_start <= SB_JOURNAL_LIST(p_s_sb)[jindex].j_start) {
+ if ((SB_JOURNAL(p_s_sb)->j_start + SB_JOURNAL_TRANS_MAX(p_s_sb) + 1) >= SB_JOURNAL_LIST(p_s_sb)[jindex].j_start) {
+ flush_journal_list(p_s_sb, SB_JOURNAL_LIST(p_s_sb) + jindex, 1) ;
+ }
+ } else if (SB_JOURNAL_LIST(p_s_sb)[jindex].j_len > 0 &&
+ (SB_JOURNAL(p_s_sb)->j_start + SB_JOURNAL_TRANS_MAX(p_s_sb) + 1) > SB_ONDISK_JOURNAL_SIZE(p_s_sb)) {
+ if (((SB_JOURNAL(p_s_sb)->j_start + SB_JOURNAL_TRANS_MAX(p_s_sb) + 1) % SB_ONDISK_JOURNAL_SIZE(p_s_sb)) >=
+ SB_JOURNAL_LIST(p_s_sb)[jindex].j_start) {
+ flush_journal_list(p_s_sb, SB_JOURNAL_LIST(p_s_sb) + jindex, 1 ) ;
+ }
+ }
+ /* this check should always be run, to send old lists to disk */
+ if (SB_JOURNAL_LIST(p_s_sb)[jindex].j_len > 0 &&
+ SB_JOURNAL_LIST(p_s_sb)[jindex].j_timestamp <
+ (CURRENT_TIME - (SB_JOURNAL_MAX_TRANS_AGE(p_s_sb) * 4))) {
+ flush_journal_list(p_s_sb, SB_JOURNAL_LIST(p_s_sb) + jindex, 1 ) ;
+ }
+ }
+
+ /* if the next journal_list is still in use, flush it */
+ if (SB_JOURNAL_LIST(p_s_sb)[SB_JOURNAL_LIST_INDEX(p_s_sb)].j_len != 0) {
+ flush_journal_list(p_s_sb, SB_JOURNAL_LIST(p_s_sb) + SB_JOURNAL_LIST_INDEX(p_s_sb), 1) ;
+ }
+
+ /* we don't want anyone flushing the new transaction's list */
+ atomic_set(&(SB_JOURNAL_LIST(p_s_sb)[SB_JOURNAL_LIST_INDEX(p_s_sb)].j_commit_flushing), 1) ;
+ atomic_set(&(SB_JOURNAL_LIST(p_s_sb)[SB_JOURNAL_LIST_INDEX(p_s_sb)].j_flushing), 1) ;
+ SB_JOURNAL_LIST(p_s_sb)[SB_JOURNAL_LIST_INDEX(p_s_sb)].j_list_bitmap = get_list_bitmap(p_s_sb, SB_JOURNAL_LIST(p_s_sb) +
+ SB_JOURNAL_LIST_INDEX(p_s_sb)) ;
+
+ if (!(SB_JOURNAL_LIST(p_s_sb)[SB_JOURNAL_LIST_INDEX(p_s_sb)].j_list_bitmap)) {
+ reiserfs_panic(p_s_sb, "journal-1996: do_journal_end, could not get a list bitmap\n") ;
+ }
+ unlock_journal(p_s_sb) ;
+ atomic_set(&(SB_JOURNAL(p_s_sb)->j_jlock), 0) ;
+ /* wake up any body waiting to join. */
+ wake_up(&(SB_JOURNAL(p_s_sb)->j_join_wait)) ;
+ return 0 ;
+}
+
+
+
diff --git a/fs/reiserfs/lbalance.c b/fs/reiserfs/lbalance.c
new file mode 100644
index 00000000000000..76a9e1a07badfa
--- /dev/null
+++ b/fs/reiserfs/lbalance.c
@@ -0,0 +1,1221 @@
+/*
+ * Copyright 2000-2002 by Hans Reiser, licensing governed by reiserfs/README
+ */
+
+#include <linux/config.h>
+#include <asm/uaccess.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/reiserfs_fs.h>
+
+/* these are used in do_balance.c */
+
+/* leaf_move_items
+ leaf_shift_left
+ leaf_shift_right
+ leaf_delete_items
+ leaf_insert_into_buf
+ leaf_paste_in_buffer
+ leaf_cut_from_buffer
+ leaf_paste_entries
+ */
+
+
+/* copy copy_count entries from source directory item to dest buffer (creating new item if needed) */
+static void leaf_copy_dir_entries (struct buffer_info * dest_bi, struct buffer_head * source,
+ int last_first, int item_num, int from, int copy_count)
+{
+ struct buffer_head * dest = dest_bi->bi_bh;
+ int item_num_in_dest; /* either the number of target item,
+ or if we must create a new item,
+ the number of the item we will
+ create it next to */
+ struct item_head * ih;
+ struct reiserfs_de_head * deh;
+ int copy_records_len; /* length of all records in item to be copied */
+ char * records;
+
+ ih = B_N_PITEM_HEAD (source, item_num);
+
+ RFALSE( !is_direntry_le_ih (ih), "vs-10000: item must be directory item");
+
+ /* length of all record to be copied and first byte of the last of them */
+ deh = B_I_DEH (source, ih);
+ if (copy_count) {
+ copy_records_len = (from ? deh_location( &(deh[from - 1]) ) :
+ ih_item_len(ih)) - deh_location( &(deh[from + copy_count - 1]));
+ records = source->b_data + ih_location(ih) +
+ deh_location( &(deh[from + copy_count - 1]));
+ } else {
+ copy_records_len = 0;
+ records = 0;
+ }
+
+ /* when copy last to first, dest buffer can contain 0 items */
+ item_num_in_dest = (last_first == LAST_TO_FIRST) ? (( B_NR_ITEMS(dest) ) ? 0 : -1) : (B_NR_ITEMS(dest) - 1);
+
+ /* if there are no items in dest or the first/last item in dest is not item of the same directory */
+ if ( (item_num_in_dest == - 1) ||
+ (last_first == FIRST_TO_LAST && le_ih_k_offset (ih) == DOT_OFFSET) ||
+ (last_first == LAST_TO_FIRST && comp_short_le_keys/*COMP_SHORT_KEYS*/ (&ih->ih_key, B_N_PKEY (dest, item_num_in_dest)))) {
+ /* create new item in dest */
+ struct item_head new_ih;
+
+ /* form item header */
+ memcpy (&new_ih.ih_key, &ih->ih_key, KEY_SIZE);
+ put_ih_version( &new_ih, KEY_FORMAT_3_5 );
+ /* calculate item len */
+ put_ih_item_len( &new_ih, DEH_SIZE * copy_count + copy_records_len );
+ put_ih_entry_count( &new_ih, 0 );
+
+ if (last_first == LAST_TO_FIRST) {
+ /* form key by the following way */
+ if (from < I_ENTRY_COUNT(ih)) {
+ set_le_ih_k_offset( &new_ih, deh_offset( &(deh[from]) ) );
+ /*memcpy (&new_ih.ih_key.k_offset, &deh[from].deh_offset, SHORT_KEY_SIZE);*/
+ } else {
+ /* no entries will be copied to this item in this function */
+ set_le_ih_k_offset (&new_ih, U32_MAX);
+ /* this item is not yet valid, but we want I_IS_DIRECTORY_ITEM to return 1 for it, so we -1 */
+ }
+ set_le_key_k_type (KEY_FORMAT_3_5, &(new_ih.ih_key), TYPE_DIRENTRY);
+ }
+
+ /* insert item into dest buffer */
+ leaf_insert_into_buf (dest_bi, (last_first == LAST_TO_FIRST) ? 0 : B_NR_ITEMS(dest), &new_ih, NULL, 0);
+ } else {
+ /* prepare space for entries */
+ leaf_paste_in_buffer (dest_bi, (last_first==FIRST_TO_LAST) ? (B_NR_ITEMS(dest) - 1) : 0, MAX_US_INT,
+ DEH_SIZE * copy_count + copy_records_len, records, 0
+ );
+ }
+
+ item_num_in_dest = (last_first == FIRST_TO_LAST) ? (B_NR_ITEMS(dest)-1) : 0;
+
+ leaf_paste_entries (dest_bi->bi_bh, item_num_in_dest,
+ (last_first == FIRST_TO_LAST) ? I_ENTRY_COUNT(B_N_PITEM_HEAD (dest, item_num_in_dest)) : 0,
+ copy_count, deh + from, records,
+ DEH_SIZE * copy_count + copy_records_len
+ );
+}
+
+
+/* Copy the first (if last_first == FIRST_TO_LAST) or last (last_first == LAST_TO_FIRST) item or
+ part of it or nothing (see the return 0 below) from SOURCE to the end
+ (if last_first) or beginning (!last_first) of the DEST */
+/* returns 1 if anything was copied, else 0 */
+static int leaf_copy_boundary_item (struct buffer_info * dest_bi, struct buffer_head * src, int last_first,
+ int bytes_or_entries)
+{
+ struct buffer_head * dest = dest_bi->bi_bh;
+ int dest_nr_item, src_nr_item; /* number of items in the source and destination buffers */
+ struct item_head * ih;
+ struct item_head * dih;
+
+ dest_nr_item = B_NR_ITEMS(dest);
+
+ if ( last_first == FIRST_TO_LAST ) {
+ /* if ( DEST is empty or first item of SOURCE and last item of DEST are the items of different objects
+ or of different types ) then there is no need to treat this item differently from the other items
+ that we copy, so we return */
+ ih = B_N_PITEM_HEAD (src, 0);
+ dih = B_N_PITEM_HEAD (dest, dest_nr_item - 1);
+ if (!dest_nr_item || (!op_is_left_mergeable (&(ih->ih_key), src->b_size)))
+ /* there is nothing to merge */
+ return 0;
+
+ RFALSE( ! ih_item_len(ih), "vs-10010: item can not have empty length");
+
+ if ( is_direntry_le_ih (ih) ) {
+ if ( bytes_or_entries == -1 )
+ /* copy all entries to dest */
+ bytes_or_entries = ih_entry_count(ih);
+ leaf_copy_dir_entries (dest_bi, src, FIRST_TO_LAST, 0, 0, bytes_or_entries);
+ return 1;
+ }
+
+ /* copy part of the body of the first item of SOURCE to the end of the body of the last item of the DEST
+ part defined by 'bytes_or_entries'; if bytes_or_entries == -1 copy whole body; don't create new item header
+ */
+ if ( bytes_or_entries == -1 )
+ bytes_or_entries = ih_item_len(ih);
+
+#ifdef CONFIG_REISERFS_CHECK
+ else {
+ if (bytes_or_entries == ih_item_len(ih) && is_indirect_le_ih(ih))
+ if (get_ih_free_space (ih))
+ reiserfs_panic (0, "vs-10020: leaf_copy_boundary_item: "
+ "last unformatted node must be filled entirely (%h)",
+ ih);
+ }
+#endif
+
+ /* merge first item (or its part) of src buffer with the last
+ item of dest buffer. Both are of the same file */
+ leaf_paste_in_buffer (dest_bi,
+ dest_nr_item - 1, ih_item_len(dih), bytes_or_entries, B_I_PITEM(src,ih), 0
+ );
+
+ if (is_indirect_le_ih (dih)) {
+ RFALSE( get_ih_free_space (dih),
+ "vs-10030: merge to left: last unformatted node of non-last indirect item %h must have zerto free space",
+ ih);
+ if (bytes_or_entries == ih_item_len(ih))
+ set_ih_free_space (dih, get_ih_free_space (ih));
+ }
+
+ return 1;
+ }
+
+
+ /* copy boundary item to right (last_first == LAST_TO_FIRST) */
+
+ /* ( DEST is empty or last item of SOURCE and first item of DEST
+ are the items of different object or of different types )
+ */
+ src_nr_item = B_NR_ITEMS (src);
+ ih = B_N_PITEM_HEAD (src, src_nr_item - 1);
+ dih = B_N_PITEM_HEAD (dest, 0);
+
+ if (!dest_nr_item || !op_is_left_mergeable (&(dih->ih_key), src->b_size))
+ return 0;
+
+ if ( is_direntry_le_ih (ih)) {
+ if ( bytes_or_entries == -1 )
+ /* bytes_or_entries = entries number in last item body of SOURCE */
+ bytes_or_entries = ih_entry_count(ih);
+
+ leaf_copy_dir_entries (dest_bi, src, LAST_TO_FIRST, src_nr_item - 1, ih_entry_count(ih) - bytes_or_entries, bytes_or_entries);
+ return 1;
+ }
+
+ /* copy part of the body of the last item of SOURCE to the begin of the body of the first item of the DEST;
+ part defined by 'bytes_or_entries'; if byte_or_entriess == -1 copy whole body; change first item key of the DEST;
+ don't create new item header
+ */
+
+ RFALSE( is_indirect_le_ih(ih) && get_ih_free_space (ih),
+ "vs-10040: merge to right: last unformatted node of non-last indirect item must be filled entirely (%h)",
+ ih);
+
+ if ( bytes_or_entries == -1 ) {
+ /* bytes_or_entries = length of last item body of SOURCE */
+ bytes_or_entries = ih_item_len(ih);
+
+ RFALSE( le_ih_k_offset (dih) !=
+ le_ih_k_offset (ih) + op_bytes_number (ih, src->b_size),
+ "vs-10050: items %h and %h do not match", ih, dih);
+
+ /* change first item key of the DEST */
+ set_le_ih_k_offset (dih, le_ih_k_offset (ih));
+
+ /* item becomes non-mergeable */
+ /* or mergeable if left item was */
+ set_le_ih_k_type (dih, le_ih_k_type (ih));
+ } else {
+ /* merge to right only part of item */
+ RFALSE( ih_item_len(ih) <= bytes_or_entries,
+ "vs-10060: no so much bytes %lu (needed %lu)",
+ ( unsigned long )ih_item_len(ih), ( unsigned long )bytes_or_entries);
+
+ /* change first item key of the DEST */
+ if ( is_direct_le_ih (dih) ) {
+ RFALSE( le_ih_k_offset (dih) <= (unsigned long)bytes_or_entries,
+ "vs-10070: dih %h, bytes_or_entries(%d)", dih, bytes_or_entries);
+ set_le_ih_k_offset (dih, le_ih_k_offset (dih) - bytes_or_entries);
+ } else {
+ RFALSE( le_ih_k_offset (dih) <=
+ (bytes_or_entries / UNFM_P_SIZE) * dest->b_size,
+ "vs-10080: dih %h, bytes_or_entries(%d)",
+ dih, (bytes_or_entries/UNFM_P_SIZE)*dest->b_size);
+ set_le_ih_k_offset (dih, le_ih_k_offset (dih) - ((bytes_or_entries / UNFM_P_SIZE) * dest->b_size));
+ }
+ }
+
+ leaf_paste_in_buffer (dest_bi, 0, 0, bytes_or_entries, B_I_PITEM(src,ih) + ih_item_len(ih) - bytes_or_entries, 0);
+ return 1;
+}
+
+
+/* copy cpy_mun items from buffer src to buffer dest
+ * last_first == FIRST_TO_LAST means, that we copy cpy_num items beginning from first-th item in src to tail of dest
+ * last_first == LAST_TO_FIRST means, that we copy cpy_num items beginning from first-th item in src to head of dest
+ */
+static void leaf_copy_items_entirely (struct buffer_info * dest_bi, struct buffer_head * src, int last_first,
+ int first, int cpy_num)
+{
+ struct buffer_head * dest;
+ int nr, free_space;
+ int dest_before;
+ int last_loc, last_inserted_loc, location;
+ int i, j;
+ struct block_head * blkh;
+ struct item_head * ih;
+
+ RFALSE( last_first != LAST_TO_FIRST && last_first != FIRST_TO_LAST,
+ "vs-10090: bad last_first parameter %d", last_first);
+ RFALSE( B_NR_ITEMS (src) - first < cpy_num,
+ "vs-10100: too few items in source %d, required %d from %d",
+ B_NR_ITEMS(src), cpy_num, first);
+ RFALSE( cpy_num < 0, "vs-10110: can not copy negative amount of items");
+ RFALSE( ! dest_bi, "vs-10120: can not copy negative amount of items");
+
+ dest = dest_bi->bi_bh;
+
+ RFALSE( ! dest, "vs-10130: can not copy negative amount of items");
+
+ if (cpy_num == 0)
+ return;
+
+ blkh = B_BLK_HEAD(dest);
+ nr = blkh_nr_item( blkh );
+ free_space = blkh_free_space(blkh);
+
+ /* we will insert items before 0-th or nr-th item in dest buffer. It depends of last_first parameter */
+ dest_before = (last_first == LAST_TO_FIRST) ? 0 : nr;
+
+ /* location of head of first new item */
+ ih = B_N_PITEM_HEAD (dest, dest_before);
+
+ RFALSE( blkh_free_space(blkh) < cpy_num * IH_SIZE,
+ "vs-10140: not enough free space for headers %d (needed %d)",
+ B_FREE_SPACE (dest), cpy_num * IH_SIZE);
+
+ /* prepare space for headers */
+ memmove (ih + cpy_num, ih, (nr-dest_before) * IH_SIZE);
+
+ /* copy item headers */
+ memcpy (ih, B_N_PITEM_HEAD (src, first), cpy_num * IH_SIZE);
+
+ free_space -= (IH_SIZE * cpy_num);
+ set_blkh_free_space( blkh, free_space );
+
+ /* location of unmovable item */
+ j = location = (dest_before == 0) ? dest->b_size : ih_location(ih-1);
+ for (i = dest_before; i < nr + cpy_num; i ++) {
+ location -= ih_item_len( ih + i - dest_before );
+ put_ih_location( ih + i - dest_before, location );
+ }
+
+ /* prepare space for items */
+ last_loc = ih_location( &(ih[nr+cpy_num-1-dest_before]) );
+ last_inserted_loc = ih_location( &(ih[cpy_num-1]) );
+
+ /* check free space */
+ RFALSE( free_space < j - last_inserted_loc,
+ "vs-10150: not enough free space for items %d (needed %d)",
+ free_space, j - last_inserted_loc);
+
+ memmove (dest->b_data + last_loc,
+ dest->b_data + last_loc + j - last_inserted_loc,
+ last_inserted_loc - last_loc);
+
+ /* copy items */
+ memcpy (dest->b_data + last_inserted_loc, B_N_PITEM(src,(first + cpy_num - 1)),
+ j - last_inserted_loc);
+
+ /* sizes, item number */
+ set_blkh_nr_item( blkh, nr + cpy_num );
+ set_blkh_free_space( blkh, free_space - (j - last_inserted_loc) );
+
+ do_balance_mark_leaf_dirty (dest_bi->tb, dest, 0);
+
+ if (dest_bi->bi_parent) {
+ struct disk_child *t_dc;
+ t_dc = B_N_CHILD (dest_bi->bi_parent, dest_bi->bi_position);
+ RFALSE( dc_block_number(t_dc) != dest->b_blocknr,
+ "vs-10160: block number in bh does not match to field in disk_child structure %lu and %lu",
+ ( long unsigned ) dest->b_blocknr,
+ ( long unsigned ) dc_block_number(t_dc));
+ put_dc_size( t_dc, dc_size(t_dc) + (j - last_inserted_loc + IH_SIZE * cpy_num ) );
+
+ do_balance_mark_internal_dirty (dest_bi->tb, dest_bi->bi_parent, 0);
+ }
+}
+
+
+/* This function splits the (liquid) item into two items (useful when
+ shifting part of an item into another node.) */
+static void leaf_item_bottle (struct buffer_info * dest_bi, struct buffer_head * src, int last_first,
+ int item_num, int cpy_bytes)
+{
+ struct buffer_head * dest = dest_bi->bi_bh;
+ struct item_head * ih;
+
+ RFALSE( cpy_bytes == -1, "vs-10170: bytes == - 1 means: do not split item");
+
+ if ( last_first == FIRST_TO_LAST ) {
+ /* if ( if item in position item_num in buffer SOURCE is directory item ) */
+ if (is_direntry_le_ih (ih = B_N_PITEM_HEAD(src,item_num)))
+ leaf_copy_dir_entries (dest_bi, src, FIRST_TO_LAST, item_num, 0, cpy_bytes);
+ else {
+ struct item_head n_ih;
+
+ /* copy part of the body of the item number 'item_num' of SOURCE to the end of the DEST
+ part defined by 'cpy_bytes'; create new item header; change old item_header (????);
+ n_ih = new item_header;
+ */
+ memcpy (&n_ih, ih, IH_SIZE);
+ put_ih_item_len( &n_ih, cpy_bytes );
+ if (is_indirect_le_ih (ih)) {
+ RFALSE( cpy_bytes == ih_item_len(ih) && get_ih_free_space(ih),
+ "vs-10180: when whole indirect item is bottle to left neighbor, it must have free_space==0 (not %lu)",
+ ( long unsigned ) get_ih_free_space (ih));
+ set_ih_free_space (&n_ih, 0);
+ }
+
+ RFALSE( op_is_left_mergeable (&(ih->ih_key), src->b_size),
+ "vs-10190: bad mergeability of item %h", ih);
+ n_ih.ih_version = ih->ih_version; /* JDM Endian safe, both le */
+ leaf_insert_into_buf (dest_bi, B_NR_ITEMS(dest), &n_ih, B_N_PITEM (src, item_num), 0);
+ }
+ } else {
+ /* if ( if item in position item_num in buffer SOURCE is directory item ) */
+ if (is_direntry_le_ih(ih = B_N_PITEM_HEAD (src, item_num)))
+ leaf_copy_dir_entries (dest_bi, src, LAST_TO_FIRST, item_num, I_ENTRY_COUNT(ih) - cpy_bytes, cpy_bytes);
+ else {
+ struct item_head n_ih;
+
+ /* copy part of the body of the item number 'item_num' of SOURCE to the begin of the DEST
+ part defined by 'cpy_bytes'; create new item header;
+ n_ih = new item_header;
+ */
+ memcpy (&n_ih, ih, SHORT_KEY_SIZE);
+
+ n_ih.ih_version = ih->ih_version; /* JDM Endian safe, both le */
+
+ if (is_direct_le_ih (ih)) {
+ set_le_ih_k_offset (&n_ih, le_ih_k_offset (ih) + ih_item_len(ih) - cpy_bytes);
+ set_le_ih_k_type (&n_ih, TYPE_DIRECT);
+ set_ih_free_space (&n_ih, MAX_US_INT);
+ } else {
+ /* indirect item */
+ RFALSE( !cpy_bytes && get_ih_free_space (ih),
+ "vs-10200: ih->ih_free_space must be 0 when indirect item will be appended");
+ set_le_ih_k_offset (&n_ih, le_ih_k_offset (ih) + (ih_item_len(ih) - cpy_bytes) / UNFM_P_SIZE * dest->b_size);
+ set_le_ih_k_type (&n_ih, TYPE_INDIRECT);
+ set_ih_free_space (&n_ih, get_ih_free_space (ih));
+ }
+
+ /* set item length */
+ put_ih_item_len( &n_ih, cpy_bytes );
+
+ n_ih.ih_version = ih->ih_version; /* JDM Endian safe, both le */
+
+ leaf_insert_into_buf (dest_bi, 0, &n_ih, B_N_PITEM(src,item_num) + ih_item_len(ih) - cpy_bytes, 0);
+ }
+ }
+}
+
+
+/* If cpy_bytes equals minus one than copy cpy_num whole items from SOURCE to DEST.
+ If cpy_bytes not equal to minus one than copy cpy_num-1 whole items from SOURCE to DEST.
+ From last item copy cpy_num bytes for regular item and cpy_num directory entries for
+ directory item. */
+static int leaf_copy_items (struct buffer_info * dest_bi, struct buffer_head * src, int last_first, int cpy_num,
+ int cpy_bytes)
+{
+ struct buffer_head * dest;
+ int pos, i, src_nr_item, bytes;
+
+ dest = dest_bi->bi_bh;
+ RFALSE( !dest || !src, "vs-10210: !dest || !src");
+ RFALSE( last_first != FIRST_TO_LAST && last_first != LAST_TO_FIRST,
+ "vs-10220:last_first != FIRST_TO_LAST && last_first != LAST_TO_FIRST");
+ RFALSE( B_NR_ITEMS(src) < cpy_num,
+ "vs-10230: No enough items: %d, req. %d", B_NR_ITEMS(src), cpy_num);
+ RFALSE( cpy_num < 0,"vs-10240: cpy_num < 0 (%d)", cpy_num);
+
+ if ( cpy_num == 0 )
+ return 0;
+
+ if ( last_first == FIRST_TO_LAST ) {
+ /* copy items to left */
+ pos = 0;
+ if ( cpy_num == 1 )
+ bytes = cpy_bytes;
+ else
+ bytes = -1;
+
+ /* copy the first item or it part or nothing to the end of the DEST (i = leaf_copy_boundary_item(DEST,SOURCE,0,bytes)) */
+ i = leaf_copy_boundary_item (dest_bi, src, FIRST_TO_LAST, bytes);
+ cpy_num -= i;
+ if ( cpy_num == 0 )
+ return i;
+ pos += i;
+ if ( cpy_bytes == -1 )
+ /* copy first cpy_num items starting from position 'pos' of SOURCE to end of DEST */
+ leaf_copy_items_entirely (dest_bi, src, FIRST_TO_LAST, pos, cpy_num);
+ else {
+ /* copy first cpy_num-1 items starting from position 'pos-1' of the SOURCE to the end of the DEST */
+ leaf_copy_items_entirely (dest_bi, src, FIRST_TO_LAST, pos, cpy_num-1);
+
+ /* copy part of the item which number is cpy_num+pos-1 to the end of the DEST */
+ leaf_item_bottle (dest_bi, src, FIRST_TO_LAST, cpy_num+pos-1, cpy_bytes);
+ }
+ } else {
+ /* copy items to right */
+ src_nr_item = B_NR_ITEMS (src);
+ if ( cpy_num == 1 )
+ bytes = cpy_bytes;
+ else
+ bytes = -1;
+
+ /* copy the last item or it part or nothing to the begin of the DEST (i = leaf_copy_boundary_item(DEST,SOURCE,1,bytes)); */
+ i = leaf_copy_boundary_item (dest_bi, src, LAST_TO_FIRST, bytes);
+
+ cpy_num -= i;
+ if ( cpy_num == 0 )
+ return i;
+
+ pos = src_nr_item - cpy_num - i;
+ if ( cpy_bytes == -1 ) {
+ /* starting from position 'pos' copy last cpy_num items of SOURCE to begin of DEST */
+ leaf_copy_items_entirely (dest_bi, src, LAST_TO_FIRST, pos, cpy_num);
+ } else {
+ /* copy last cpy_num-1 items starting from position 'pos+1' of the SOURCE to the begin of the DEST; */
+ leaf_copy_items_entirely (dest_bi, src, LAST_TO_FIRST, pos+1, cpy_num-1);
+
+ /* copy part of the item which number is pos to the begin of the DEST */
+ leaf_item_bottle (dest_bi, src, LAST_TO_FIRST, pos, cpy_bytes);
+ }
+ }
+ return i;
+}
+
+
+/* there are types of coping: from S[0] to L[0], from S[0] to R[0],
+ from R[0] to L[0]. for each of these we have to define parent and
+ positions of destination and source buffers */
+static void leaf_define_dest_src_infos (int shift_mode, struct tree_balance * tb, struct buffer_info * dest_bi,
+ struct buffer_info * src_bi, int * first_last,
+ struct buffer_head * Snew)
+{
+ memset (dest_bi, 0, sizeof (struct buffer_info));
+ memset (src_bi, 0, sizeof (struct buffer_info));
+
+ /* define dest, src, dest parent, dest position */
+ switch (shift_mode) {
+ case LEAF_FROM_S_TO_L: /* it is used in leaf_shift_left */
+ src_bi->tb = tb;
+ src_bi->bi_bh = PATH_PLAST_BUFFER (tb->tb_path);
+ src_bi->bi_parent = PATH_H_PPARENT (tb->tb_path, 0);
+ src_bi->bi_position = PATH_H_B_ITEM_ORDER (tb->tb_path, 0); /* src->b_item_order */
+ dest_bi->tb = tb;
+ dest_bi->bi_bh = tb->L[0];
+ dest_bi->bi_parent = tb->FL[0];
+ dest_bi->bi_position = get_left_neighbor_position (tb, 0);
+ *first_last = FIRST_TO_LAST;
+ break;
+
+ case LEAF_FROM_S_TO_R: /* it is used in leaf_shift_right */
+ src_bi->tb = tb;
+ src_bi->bi_bh = PATH_PLAST_BUFFER (tb->tb_path);
+ src_bi->bi_parent = PATH_H_PPARENT (tb->tb_path, 0);
+ src_bi->bi_position = PATH_H_B_ITEM_ORDER (tb->tb_path, 0);
+ dest_bi->tb = tb;
+ dest_bi->bi_bh = tb->R[0];
+ dest_bi->bi_parent = tb->FR[0];
+ dest_bi->bi_position = get_right_neighbor_position (tb, 0);
+ *first_last = LAST_TO_FIRST;
+ break;
+
+ case LEAF_FROM_R_TO_L: /* it is used in balance_leaf_when_delete */
+ src_bi->tb = tb;
+ src_bi->bi_bh = tb->R[0];
+ src_bi->bi_parent = tb->FR[0];
+ src_bi->bi_position = get_right_neighbor_position (tb, 0);
+ dest_bi->tb = tb;
+ dest_bi->bi_bh = tb->L[0];
+ dest_bi->bi_parent = tb->FL[0];
+ dest_bi->bi_position = get_left_neighbor_position (tb, 0);
+ *first_last = FIRST_TO_LAST;
+ break;
+
+ case LEAF_FROM_L_TO_R: /* it is used in balance_leaf_when_delete */
+ src_bi->tb = tb;
+ src_bi->bi_bh = tb->L[0];
+ src_bi->bi_parent = tb->FL[0];
+ src_bi->bi_position = get_left_neighbor_position (tb, 0);
+ dest_bi->tb = tb;
+ dest_bi->bi_bh = tb->R[0];
+ dest_bi->bi_parent = tb->FR[0];
+ dest_bi->bi_position = get_right_neighbor_position (tb, 0);
+ *first_last = LAST_TO_FIRST;
+ break;
+
+ case LEAF_FROM_S_TO_SNEW:
+ src_bi->tb = tb;
+ src_bi->bi_bh = PATH_PLAST_BUFFER (tb->tb_path);
+ src_bi->bi_parent = PATH_H_PPARENT (tb->tb_path, 0);
+ src_bi->bi_position = PATH_H_B_ITEM_ORDER (tb->tb_path, 0);
+ dest_bi->tb = tb;
+ dest_bi->bi_bh = Snew;
+ dest_bi->bi_parent = 0;
+ dest_bi->bi_position = 0;
+ *first_last = LAST_TO_FIRST;
+ break;
+
+ default:
+ reiserfs_panic (0, "vs-10250: leaf_define_dest_src_infos: shift type is unknown (%d)", shift_mode);
+ }
+ RFALSE( src_bi->bi_bh == 0 || dest_bi->bi_bh == 0,
+ "vs-10260: mode==%d, source (%p) or dest (%p) buffer is initialized incorrectly",
+ shift_mode, src_bi->bi_bh, dest_bi->bi_bh);
+}
+
+
+
+
+/* copy mov_num items and mov_bytes of the (mov_num-1)th item to
+ neighbor. Delete them from source */
+int leaf_move_items (int shift_mode, struct tree_balance * tb, int mov_num, int mov_bytes, struct buffer_head * Snew)
+{
+ int ret_value;
+ struct buffer_info dest_bi, src_bi;
+ int first_last;
+
+ leaf_define_dest_src_infos (shift_mode, tb, &dest_bi, &src_bi, &first_last, Snew);
+
+ ret_value = leaf_copy_items (&dest_bi, src_bi.bi_bh, first_last, mov_num, mov_bytes);
+
+ leaf_delete_items (&src_bi, first_last, (first_last == FIRST_TO_LAST) ? 0 : (B_NR_ITEMS(src_bi.bi_bh) - mov_num), mov_num, mov_bytes);
+
+
+ return ret_value;
+}
+
+
+/* Shift shift_num items (and shift_bytes of last shifted item if shift_bytes != -1)
+ from S[0] to L[0] and replace the delimiting key */
+int leaf_shift_left (struct tree_balance * tb, int shift_num, int shift_bytes)
+{
+ struct buffer_head * S0 = PATH_PLAST_BUFFER (tb->tb_path);
+ int i;
+
+ /* move shift_num (and shift_bytes bytes) items from S[0] to left neighbor L[0] */
+ i = leaf_move_items (LEAF_FROM_S_TO_L, tb, shift_num, shift_bytes, 0);
+
+ if ( shift_num ) {
+ if (B_NR_ITEMS (S0) == 0) { /* number of items in S[0] == 0 */
+
+ RFALSE( shift_bytes != -1,
+ "vs-10270: S0 is empty now, but shift_bytes != -1 (%d)",
+ shift_bytes);
+#ifdef CONFIG_REISERFS_CHECK
+ if (tb->tb_mode == M_PASTE || tb->tb_mode == M_INSERT) {
+ print_cur_tb ("vs-10275");
+ reiserfs_panic (tb->tb_sb, "vs-10275: leaf_shift_left: balance condition corrupted (%c)", tb->tb_mode);
+ }
+#endif
+
+ if (PATH_H_POSITION (tb->tb_path, 1) == 0)
+ replace_key (tb, tb->CFL[0], tb->lkey[0], PATH_H_PPARENT (tb->tb_path, 0), 0);
+
+ } else {
+ /* replace lkey in CFL[0] by 0-th key from S[0]; */
+ replace_key (tb, tb->CFL[0], tb->lkey[0], S0, 0);
+
+ RFALSE( (shift_bytes != -1 &&
+ !(is_direntry_le_ih (B_N_PITEM_HEAD (S0, 0))
+ && !I_ENTRY_COUNT (B_N_PITEM_HEAD (S0, 0)))) &&
+ (!op_is_left_mergeable (B_N_PKEY (S0, 0), S0->b_size)),
+ "vs-10280: item must be mergeable");
+ }
+ }
+
+ return i;
+}
+
+
+
+
+
+/* CLEANING STOPPED HERE */
+
+
+
+
+/* Shift shift_num (shift_bytes) items from S[0] to the right neighbor, and replace the delimiting key */
+int leaf_shift_right(
+ struct tree_balance * tb,
+ int shift_num,
+ int shift_bytes
+ )
+{
+ // struct buffer_head * S0 = PATH_PLAST_BUFFER (tb->tb_path);
+ int ret_value;
+
+ /* move shift_num (and shift_bytes) items from S[0] to right neighbor R[0] */
+ ret_value = leaf_move_items (LEAF_FROM_S_TO_R, tb, shift_num, shift_bytes, 0);
+
+ /* replace rkey in CFR[0] by the 0-th key from R[0] */
+ if (shift_num) {
+ replace_key (tb, tb->CFR[0], tb->rkey[0], tb->R[0], 0);
+
+ }
+
+ return ret_value;
+}
+
+
+
+static void leaf_delete_items_entirely (struct buffer_info * bi,
+ int first, int del_num);
+/* If del_bytes == -1, starting from position 'first' delete del_num items in whole in buffer CUR.
+ If not.
+ If last_first == 0. Starting from position 'first' delete del_num-1 items in whole. Delete part of body of
+ the first item. Part defined by del_bytes. Don't delete first item header
+ If last_first == 1. Starting from position 'first+1' delete del_num-1 items in whole. Delete part of body of
+ the last item . Part defined by del_bytes. Don't delete last item header.
+*/
+void leaf_delete_items (struct buffer_info * cur_bi, int last_first,
+ int first, int del_num, int del_bytes)
+{
+ struct buffer_head * bh;
+ int item_amount = B_NR_ITEMS (bh = cur_bi->bi_bh);
+
+ RFALSE( !bh, "10155: bh is not defined");
+ RFALSE( del_num < 0, "10160: del_num can not be < 0. del_num==%d", del_num);
+ RFALSE( first < 0 || first + del_num > item_amount,
+ "10165: invalid number of first item to be deleted (%d) or "
+ "no so much items (%d) to delete (only %d)",
+ first, first + del_num, item_amount);
+
+ if ( del_num == 0 )
+ return;
+
+ if ( first == 0 && del_num == item_amount && del_bytes == -1 ) {
+ make_empty_node (cur_bi);
+ do_balance_mark_leaf_dirty (cur_bi->tb, bh, 0);
+ return;
+ }
+
+ if ( del_bytes == -1 )
+ /* delete del_num items beginning from item in position first */
+ leaf_delete_items_entirely (cur_bi, first, del_num);
+ else {
+ if ( last_first == FIRST_TO_LAST ) {
+ /* delete del_num-1 items beginning from item in position first */
+ leaf_delete_items_entirely (cur_bi, first, del_num-1);
+
+ /* delete the part of the first item of the bh
+ do not delete item header
+ */
+ leaf_cut_from_buffer (cur_bi, 0, 0, del_bytes);
+ } else {
+ struct item_head * ih;
+ int len;
+
+ /* delete del_num-1 items beginning from item in position first+1 */
+ leaf_delete_items_entirely (cur_bi, first+1, del_num-1);
+
+ if (is_direntry_le_ih (ih = B_N_PITEM_HEAD(bh, B_NR_ITEMS(bh)-1))) /* the last item is directory */
+ /* len = numbers of directory entries in this item */
+ len = ih_entry_count(ih);
+ else
+ /* len = body len of item */
+ len = ih_item_len(ih);
+
+ /* delete the part of the last item of the bh
+ do not delete item header
+ */
+ leaf_cut_from_buffer (cur_bi, B_NR_ITEMS(bh)-1, len - del_bytes, del_bytes);
+ }
+ }
+}
+
+
+/* insert item into the leaf node in position before */
+void leaf_insert_into_buf (struct buffer_info * bi, int before,
+ struct item_head * inserted_item_ih,
+ const char * inserted_item_body,
+ int zeros_number)
+{
+ struct buffer_head * bh = bi->bi_bh;
+ int nr, free_space;
+ struct block_head * blkh;
+ struct item_head * ih;
+ int i;
+ int last_loc, unmoved_loc;
+ char * to;
+
+
+ blkh = B_BLK_HEAD(bh);
+ nr = blkh_nr_item(blkh);
+ free_space = blkh_free_space( blkh );
+
+ /* check free space */
+ RFALSE( free_space < ih_item_len(inserted_item_ih) + IH_SIZE,
+ "vs-10170: not enough free space in block %z, new item %h",
+ bh, inserted_item_ih);
+ RFALSE( zeros_number > ih_item_len(inserted_item_ih),
+ "vs-10172: zero number == %d, item length == %d",
+ zeros_number, ih_item_len(inserted_item_ih));
+
+
+ /* get item new item must be inserted before */
+ ih = B_N_PITEM_HEAD (bh, before);
+
+ /* prepare space for the body of new item */
+ last_loc = nr ? ih_location( &(ih[nr - before - 1]) ) : bh->b_size;
+ unmoved_loc = before ? ih_location( ih-1 ) : bh->b_size;
+
+
+ memmove (bh->b_data + last_loc - ih_item_len(inserted_item_ih),
+ bh->b_data + last_loc, unmoved_loc - last_loc);
+
+ to = bh->b_data + unmoved_loc - ih_item_len(inserted_item_ih);
+ memset (to, 0, zeros_number);
+ to += zeros_number;
+
+ /* copy body to prepared space */
+ if (inserted_item_body)
+ memmove (to, inserted_item_body, ih_item_len(inserted_item_ih) - zeros_number);
+ else
+ memset(to, '\0', ih_item_len(inserted_item_ih) - zeros_number);
+
+ /* insert item header */
+ memmove (ih + 1, ih, IH_SIZE * (nr - before));
+ memmove (ih, inserted_item_ih, IH_SIZE);
+
+ /* change locations */
+ for (i = before; i < nr + 1; i ++)
+ {
+ unmoved_loc -= ih_item_len( &(ih[i-before]));
+ put_ih_location( &(ih[i-before]), unmoved_loc );
+ }
+
+ /* sizes, free space, item number */
+ set_blkh_nr_item( blkh, blkh_nr_item(blkh) + 1 );
+ set_blkh_free_space( blkh,
+ free_space - (IH_SIZE + ih_item_len(inserted_item_ih ) ) );
+ do_balance_mark_leaf_dirty (bi->tb, bh, 1);
+
+ if (bi->bi_parent) {
+ struct disk_child *t_dc;
+ t_dc = B_N_CHILD (bi->bi_parent, bi->bi_position);
+ put_dc_size( t_dc, dc_size(t_dc) + (IH_SIZE + ih_item_len(inserted_item_ih)));
+ do_balance_mark_internal_dirty (bi->tb, bi->bi_parent, 0);
+ }
+}
+
+
+/* paste paste_size bytes to affected_item_num-th item.
+ When item is a directory, this only prepare space for new entries */
+void leaf_paste_in_buffer (struct buffer_info * bi, int affected_item_num,
+ int pos_in_item, int paste_size,
+ const char * body,
+ int zeros_number)
+{
+ struct buffer_head * bh = bi->bi_bh;
+ int nr, free_space;
+ struct block_head * blkh;
+ struct item_head * ih;
+ int i;
+ int last_loc, unmoved_loc;
+
+ blkh = B_BLK_HEAD(bh);
+ nr = blkh_nr_item(blkh);
+ free_space = blkh_free_space(blkh);
+
+
+ /* check free space */
+ RFALSE( free_space < paste_size,
+ "vs-10175: not enough free space: needed %d, available %d",
+ paste_size, free_space);
+
+#ifdef CONFIG_REISERFS_CHECK
+ if (zeros_number > paste_size) {
+ print_cur_tb ("10177");
+ reiserfs_panic ( 0, "vs-10177: leaf_paste_in_buffer: ero number == %d, paste_size == %d",
+ zeros_number, paste_size);
+ }
+#endif /* CONFIG_REISERFS_CHECK */
+
+
+ /* item to be appended */
+ ih = B_N_PITEM_HEAD(bh, affected_item_num);
+
+ last_loc = ih_location( &(ih[nr - affected_item_num - 1]) );
+ unmoved_loc = affected_item_num ? ih_location( ih-1 ) : bh->b_size;
+
+ /* prepare space */
+ memmove (bh->b_data + last_loc - paste_size, bh->b_data + last_loc,
+ unmoved_loc - last_loc);
+
+
+ /* change locations */
+ for (i = affected_item_num; i < nr; i ++)
+ put_ih_location( &(ih[i-affected_item_num]),
+ ih_location( &(ih[i-affected_item_num])) - paste_size );
+
+ if ( body ) {
+ if (!is_direntry_le_ih (ih)) {
+ if (!pos_in_item) {
+ /* shift data to right */
+ memmove (bh->b_data + ih_location(ih) + paste_size,
+ bh->b_data + ih_location(ih), ih_item_len(ih));
+ /* paste data in the head of item */
+ memset (bh->b_data + ih_location(ih), 0, zeros_number);
+ memcpy (bh->b_data + ih_location(ih) + zeros_number, body, paste_size - zeros_number);
+ } else {
+ memset (bh->b_data + unmoved_loc - paste_size, 0, zeros_number);
+ memcpy (bh->b_data + unmoved_loc - paste_size + zeros_number, body, paste_size - zeros_number);
+ }
+ }
+ }
+ else
+ memset(bh->b_data + unmoved_loc - paste_size, '\0', paste_size);
+
+ put_ih_item_len( ih, ih_item_len(ih) + paste_size );
+
+ /* change free space */
+ set_blkh_free_space( blkh, free_space - paste_size );
+
+ do_balance_mark_leaf_dirty (bi->tb, bh, 0);
+
+ if (bi->bi_parent) {
+ struct disk_child *t_dc = B_N_CHILD (bi->bi_parent, bi->bi_position);
+ put_dc_size( t_dc, dc_size(t_dc) + paste_size );
+ do_balance_mark_internal_dirty (bi->tb, bi->bi_parent, 0);
+ }
+}
+
+
+/* cuts DEL_COUNT entries beginning from FROM-th entry. Directory item
+ does not have free space, so it moves DEHs and remaining records as
+ necessary. Return value is size of removed part of directory item
+ in bytes. */
+static int leaf_cut_entries (
+ struct buffer_head * bh,
+ struct item_head * ih,
+ int from,
+ int del_count
+ )
+{
+ char * item;
+ struct reiserfs_de_head * deh;
+ int prev_record_offset; /* offset of record, that is (from-1)th */
+ char * prev_record; /* */
+ int cut_records_len; /* length of all removed records */
+ int i;
+
+
+ /* make sure, that item is directory and there are enough entries to
+ remove */
+ RFALSE( !is_direntry_le_ih (ih), "10180: item is not directory item");
+ RFALSE( I_ENTRY_COUNT(ih) < from + del_count,
+ "10185: item contains not enough entries: entry_cout = %d, from = %d, to delete = %d",
+ I_ENTRY_COUNT(ih), from, del_count);
+
+ if (del_count == 0)
+ return 0;
+
+ /* first byte of item */
+ item = bh->b_data + ih_location(ih);
+
+ /* entry head array */
+ deh = B_I_DEH (bh, ih);
+
+ /* first byte of remaining entries, those are BEFORE cut entries
+ (prev_record) and length of all removed records (cut_records_len) */
+ prev_record_offset = (from ? deh_location( &(deh[from - 1])) : ih_item_len(ih));
+ cut_records_len = prev_record_offset/*from_record*/ -
+ deh_location( &(deh[from + del_count - 1]));
+ prev_record = item + prev_record_offset;
+
+
+ /* adjust locations of remaining entries */
+ for (i = I_ENTRY_COUNT(ih) - 1; i > from + del_count - 1; i --)
+ put_deh_location( &(deh[i]),
+ deh_location( &deh[i] ) - (DEH_SIZE * del_count ) );
+
+ for (i = 0; i < from; i ++)
+ put_deh_location( &(deh[i]),
+ deh_location( &deh[i] ) - (DEH_SIZE * del_count + cut_records_len) );
+
+ put_ih_entry_count( ih, ih_entry_count(ih) - del_count );
+
+ /* shift entry head array and entries those are AFTER removed entries */
+ memmove ((char *)(deh + from),
+ deh + from + del_count,
+ prev_record - cut_records_len - (char *)(deh + from + del_count));
+
+ /* shift records, those are BEFORE removed entries */
+ memmove (prev_record - cut_records_len - DEH_SIZE * del_count,
+ prev_record, item + ih_item_len(ih) - prev_record);
+
+ return DEH_SIZE * del_count + cut_records_len;
+}
+
+
+/* when cut item is part of regular file
+ pos_in_item - first byte that must be cut
+ cut_size - number of bytes to be cut beginning from pos_in_item
+
+ when cut item is part of directory
+ pos_in_item - number of first deleted entry
+ cut_size - count of deleted entries
+ */
+void leaf_cut_from_buffer (struct buffer_info * bi, int cut_item_num,
+ int pos_in_item, int cut_size)
+{
+ int nr;
+ struct buffer_head * bh = bi->bi_bh;
+ struct block_head * blkh;
+ struct item_head * ih;
+ int last_loc, unmoved_loc;
+ int i;
+
+ blkh = B_BLK_HEAD(bh);
+ nr = blkh_nr_item(blkh);
+
+ /* item head of truncated item */
+ ih = B_N_PITEM_HEAD (bh, cut_item_num);
+
+ if (is_direntry_le_ih (ih)) {
+ /* first cut entry ()*/
+ cut_size = leaf_cut_entries (bh, ih, pos_in_item, cut_size);
+ if (pos_in_item == 0) {
+ /* change key */
+ RFALSE( cut_item_num,
+ "when 0-th enrty of item is cut, that item must be first in the node, not %d-th", cut_item_num);
+ /* change item key by key of first entry in the item */
+ set_le_ih_k_offset (ih, deh_offset(B_I_DEH (bh, ih)));
+ /*memcpy (&ih->ih_key.k_offset, &(B_I_DEH (bh, ih)->deh_offset), SHORT_KEY_SIZE);*/
+ }
+ } else {
+ /* item is direct or indirect */
+ RFALSE( is_statdata_le_ih (ih), "10195: item is stat data");
+ RFALSE( pos_in_item && pos_in_item + cut_size != ih_item_len(ih),
+ "10200: invalid offset (%lu) or trunc_size (%lu) or ih_item_len (%lu)",
+ ( long unsigned ) pos_in_item, ( long unsigned ) cut_size,
+ ( long unsigned ) ih_item_len (ih));
+
+ /* shift item body to left if cut is from the head of item */
+ if (pos_in_item == 0) {
+ memmove( bh->b_data + ih_location(ih),
+ bh->b_data + ih_location(ih) + cut_size,
+ ih_item_len(ih) - cut_size);
+
+ /* change key of item */
+ if (is_direct_le_ih (ih))
+ set_le_ih_k_offset (ih, le_ih_k_offset (ih) + cut_size);
+ else {
+ set_le_ih_k_offset (ih, le_ih_k_offset (ih) + (cut_size / UNFM_P_SIZE) * bh->b_size);
+ RFALSE( ih_item_len(ih) == cut_size && get_ih_free_space (ih),
+ "10205: invalid ih_free_space (%h)", ih);
+ }
+ }
+ }
+
+
+ /* location of the last item */
+ last_loc = ih_location( &(ih[nr - cut_item_num - 1]) );
+
+ /* location of the item, which is remaining at the same place */
+ unmoved_loc = cut_item_num ? ih_location(ih-1) : bh->b_size;
+
+
+ /* shift */
+ memmove (bh->b_data + last_loc + cut_size, bh->b_data + last_loc,
+ unmoved_loc - last_loc - cut_size);
+
+ /* change item length */
+ put_ih_item_len( ih, ih_item_len(ih) - cut_size );
+
+ if (is_indirect_le_ih (ih)) {
+ if (pos_in_item)
+ set_ih_free_space (ih, 0);
+ }
+
+ /* change locations */
+ for (i = cut_item_num; i < nr; i ++)
+ put_ih_location( &(ih[i-cut_item_num]), ih_location( &ih[i-cut_item_num]) + cut_size );
+
+ /* size, free space */
+ set_blkh_free_space( blkh, blkh_free_space(blkh) + cut_size );
+
+ do_balance_mark_leaf_dirty (bi->tb, bh, 0);
+
+ if (bi->bi_parent) {
+ struct disk_child *t_dc;
+ t_dc = B_N_CHILD (bi->bi_parent, bi->bi_position);
+ put_dc_size( t_dc, dc_size(t_dc) - cut_size );
+ do_balance_mark_internal_dirty (bi->tb, bi->bi_parent, 0);
+ }
+}
+
+
+/* delete del_num items from buffer starting from the first'th item */
+static void leaf_delete_items_entirely (struct buffer_info * bi,
+ int first, int del_num)
+{
+ struct buffer_head * bh = bi->bi_bh;
+ int nr;
+ int i, j;
+ int last_loc, last_removed_loc;
+ struct block_head * blkh;
+ struct item_head * ih;
+
+ RFALSE( bh == NULL, "10210: buffer is 0");
+ RFALSE( del_num < 0, "10215: del_num less than 0 (%d)", del_num);
+
+ if (del_num == 0)
+ return;
+
+ blkh = B_BLK_HEAD(bh);
+ nr = blkh_nr_item(blkh);
+
+ RFALSE( first < 0 || first + del_num > nr,
+ "10220: first=%d, number=%d, there is %d items", first, del_num, nr);
+
+ if (first == 0 && del_num == nr) {
+ /* this does not work */
+ make_empty_node (bi);
+
+ do_balance_mark_leaf_dirty (bi->tb, bh, 0);
+ return;
+ }
+
+ ih = B_N_PITEM_HEAD (bh, first);
+
+ /* location of unmovable item */
+ j = (first == 0) ? bh->b_size : ih_location(ih-1);
+
+ /* delete items */
+ last_loc = ih_location( &(ih[nr-1-first]) );
+ last_removed_loc = ih_location( &(ih[del_num-1]) );
+
+ memmove (bh->b_data + last_loc + j - last_removed_loc,
+ bh->b_data + last_loc, last_removed_loc - last_loc);
+
+ /* delete item headers */
+ memmove (ih, ih + del_num, (nr - first - del_num) * IH_SIZE);
+
+ /* change item location */
+ for (i = first; i < nr - del_num; i ++)
+ put_ih_location( &(ih[i-first]), ih_location( &(ih[i-first]) ) + (j - last_removed_loc) );
+
+ /* sizes, item number */
+ set_blkh_nr_item( blkh, blkh_nr_item(blkh) - del_num );
+ set_blkh_free_space( blkh, blkh_free_space(blkh) + (j - last_removed_loc + IH_SIZE * del_num) );
+
+ do_balance_mark_leaf_dirty (bi->tb, bh, 0);
+
+ if (bi->bi_parent) {
+ struct disk_child *t_dc = B_N_CHILD (bi->bi_parent, bi->bi_position);
+ put_dc_size( t_dc, dc_size(t_dc) -
+ (j - last_removed_loc + IH_SIZE * del_num));
+ do_balance_mark_internal_dirty (bi->tb, bi->bi_parent, 0);
+ }
+}
+
+
+
+
+
+/* paste new_entry_count entries (new_dehs, records) into position before to item_num-th item */
+void leaf_paste_entries (
+ struct buffer_head * bh,
+ int item_num,
+ int before,
+ int new_entry_count,
+ struct reiserfs_de_head * new_dehs,
+ const char * records,
+ int paste_size
+ )
+{
+ struct item_head * ih;
+ char * item;
+ struct reiserfs_de_head * deh;
+ char * insert_point;
+ int i, old_entry_num;
+
+ if (new_entry_count == 0)
+ return;
+
+ ih = B_N_PITEM_HEAD(bh, item_num);
+
+ /* make sure, that item is directory, and there are enough records in it */
+ RFALSE( !is_direntry_le_ih (ih), "10225: item is not directory item");
+ RFALSE( I_ENTRY_COUNT (ih) < before,
+ "10230: there are no entry we paste entries before. entry_count = %d, before = %d",
+ I_ENTRY_COUNT (ih), before);
+
+
+ /* first byte of dest item */
+ item = bh->b_data + ih_location(ih);
+
+ /* entry head array */
+ deh = B_I_DEH (bh, ih);
+
+ /* new records will be pasted at this point */
+ insert_point = item + (before ? deh_location( &(deh[before - 1])) : (ih_item_len(ih) - paste_size));
+
+ /* adjust locations of records that will be AFTER new records */
+ for (i = I_ENTRY_COUNT(ih) - 1; i >= before; i --)
+ put_deh_location( &(deh[i]),
+ deh_location(&(deh[i])) + (DEH_SIZE * new_entry_count ));
+
+ /* adjust locations of records that will be BEFORE new records */
+ for (i = 0; i < before; i ++)
+ put_deh_location( &(deh[i]), deh_location(&(deh[i])) + paste_size );
+
+ old_entry_num = I_ENTRY_COUNT(ih);
+ put_ih_entry_count( ih, ih_entry_count(ih) + new_entry_count );
+
+ /* prepare space for pasted records */
+ memmove (insert_point + paste_size, insert_point, item + (ih_item_len(ih) - paste_size) - insert_point);
+
+ /* copy new records */
+ memcpy (insert_point + DEH_SIZE * new_entry_count, records,
+ paste_size - DEH_SIZE * new_entry_count);
+
+ /* prepare space for new entry heads */
+ deh += before;
+ memmove ((char *)(deh + new_entry_count), deh, insert_point - (char *)deh);
+
+ /* copy new entry heads */
+ deh = (struct reiserfs_de_head *)((char *)deh);
+ memcpy (deh, new_dehs, DEH_SIZE * new_entry_count);
+
+ /* set locations of new records */
+ for (i = 0; i < new_entry_count; i ++)
+ {
+ put_deh_location( &(deh[i]),
+ deh_location( &(deh[i] )) +
+ (- deh_location( &(new_dehs[new_entry_count - 1])) +
+ insert_point + DEH_SIZE * new_entry_count - item));
+ }
+
+
+ /* change item key if neccessary (when we paste before 0-th entry */
+ if (!before)
+ {
+ set_le_ih_k_offset (ih, deh_offset(new_dehs));
+/* memcpy (&ih->ih_key.k_offset,
+ &new_dehs->deh_offset, SHORT_KEY_SIZE);*/
+ }
+
+#ifdef CONFIG_REISERFS_CHECK
+ {
+ int prev, next;
+ /* check record locations */
+ deh = B_I_DEH (bh, ih);
+ for (i = 0; i < I_ENTRY_COUNT(ih); i ++) {
+ next = (i < I_ENTRY_COUNT(ih) - 1) ? deh_location( &(deh[i + 1])) : 0;
+ prev = (i != 0) ? deh_location( &(deh[i - 1]) ) : 0;
+
+ if (prev && prev <= deh_location( &(deh[i])))
+ reiserfs_warning (NULL, "vs-10240: leaf_paste_entries: directory item (%h) corrupted (prev %a, cur(%d) %a)\n",
+ ih, deh + i - 1, i, deh + i);
+ if (next && next >= deh_location( &(deh[i])))
+ reiserfs_warning (NULL, "vs-10250: leaf_paste_entries: directory item (%h) corrupted (cur(%d) %a, next %a)\n",
+ ih, i, deh + i, deh + i + 1);
+ }
+ }
+#endif
+
+}
diff --git a/fs/reiserfs/namei.c b/fs/reiserfs/namei.c
new file mode 100644
index 00000000000000..c7e5668996f57f
--- /dev/null
+++ b/fs/reiserfs/namei.c
@@ -0,0 +1,1281 @@
+/*
+ * Copyright 2000-2002 by Hans Reiser, licensing governed by reiserfs/README
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/bitops.h>
+#include <linux/reiserfs_fs.h>
+#include <linux/smp_lock.h>
+
+#define INC_DIR_INODE_NLINK(i) if (i->i_nlink != 1) { i->i_nlink++; if (i->i_nlink >= REISERFS_LINK_MAX) i->i_nlink=1; }
+#define DEC_DIR_INODE_NLINK(i) if (i->i_nlink != 1) i->i_nlink--;
+
+// directory item contains array of entry headers. This performs
+// binary search through that array
+static int bin_search_in_dir_item (struct reiserfs_dir_entry * de, loff_t off)
+{
+ struct item_head * ih = de->de_ih;
+ struct reiserfs_de_head * deh = de->de_deh;
+ int rbound, lbound, j;
+
+ lbound = 0;
+ rbound = I_ENTRY_COUNT (ih) - 1;
+
+ for (j = (rbound + lbound) / 2; lbound <= rbound; j = (rbound + lbound) / 2) {
+ if (off < deh_offset (deh + j)) {
+ rbound = j - 1;
+ continue;
+ }
+ if (off > deh_offset (deh + j)) {
+ lbound = j + 1;
+ continue;
+ }
+ // this is not name found, but matched third key component
+ de->de_entry_num = j;
+ return NAME_FOUND;
+ }
+
+ de->de_entry_num = lbound;
+ return NAME_NOT_FOUND;
+}
+
+
+// comment? maybe something like set de to point to what the path points to?
+static inline void set_de_item_location (struct reiserfs_dir_entry * de, struct path * path)
+{
+ de->de_bh = get_last_bh (path);
+ de->de_ih = get_ih (path);
+ de->de_deh = B_I_DEH (de->de_bh, de->de_ih);
+ de->de_item_num = PATH_LAST_POSITION (path);
+}
+
+
+// de_bh, de_ih, de_deh (points to first element of array), de_item_num is set
+inline void set_de_name_and_namelen (struct reiserfs_dir_entry * de)
+{
+ struct reiserfs_de_head * deh = de->de_deh + de->de_entry_num;
+
+ if (de->de_entry_num >= ih_entry_count (de->de_ih))
+ BUG ();
+
+ de->de_entrylen = entry_length (de->de_bh, de->de_ih, de->de_entry_num);
+ de->de_namelen = de->de_entrylen - (de_with_sd (deh) ? SD_SIZE : 0);
+ de->de_name = B_I_PITEM (de->de_bh, de->de_ih) + deh_location(deh);
+ if (de->de_name[de->de_namelen - 1] == 0)
+ de->de_namelen = strlen (de->de_name);
+}
+
+
+// what entry points to
+static inline void set_de_object_key (struct reiserfs_dir_entry * de)
+{
+ if (de->de_entry_num >= ih_entry_count (de->de_ih))
+ BUG ();
+ de->de_dir_id = deh_dir_id( &(de->de_deh[de->de_entry_num]));
+ de->de_objectid = deh_objectid( &(de->de_deh[de->de_entry_num]));
+}
+
+
+static inline void store_de_entry_key (struct reiserfs_dir_entry * de)
+{
+ struct reiserfs_de_head * deh = de->de_deh + de->de_entry_num;
+
+ if (de->de_entry_num >= ih_entry_count (de->de_ih))
+ BUG ();
+
+ /* store key of the found entry */
+ de->de_entry_key.version = KEY_FORMAT_3_5;
+ de->de_entry_key.on_disk_key.k_dir_id = le32_to_cpu (de->de_ih->ih_key.k_dir_id);
+ de->de_entry_key.on_disk_key.k_objectid = le32_to_cpu (de->de_ih->ih_key.k_objectid);
+ set_cpu_key_k_offset (&(de->de_entry_key), deh_offset (deh));
+ set_cpu_key_k_type (&(de->de_entry_key), TYPE_DIRENTRY);
+}
+
+
+/* We assign a key to each directory item, and place multiple entries
+in a single directory item. A directory item has a key equal to the
+key of the first directory entry in it.
+
+This function first calls search_by_key, then, if item whose first
+entry matches is not found it looks for the entry inside directory
+item found by search_by_key. Fills the path to the entry, and to the
+entry position in the item
+
+*/
+
+/* The function is NOT SCHEDULE-SAFE! */
+int search_by_entry_key (struct super_block * sb, const struct cpu_key * key,
+ struct path * path, struct reiserfs_dir_entry * de)
+{
+ int retval;
+
+ retval = search_item (sb, key, path);
+ switch (retval) {
+ case ITEM_NOT_FOUND:
+ if (!PATH_LAST_POSITION (path)) {
+ reiserfs_warning (sb, "vs-7000: search_by_entry_key: search_by_key returned item position == 0\n");
+ pathrelse(path) ;
+ return IO_ERROR ;
+ }
+ PATH_LAST_POSITION (path) --;
+
+ case ITEM_FOUND:
+ break;
+
+ case IO_ERROR:
+ return retval;
+
+ default:
+ pathrelse (path);
+ reiserfs_warning (sb, "vs-7002: search_by_entry_key: no path to here\n ");
+ return IO_ERROR;
+ }
+
+ set_de_item_location (de, path);
+
+#ifdef CONFIG_REISERFS_CHECK
+ if (!is_direntry_le_ih (de->de_ih) ||
+ COMP_SHORT_KEYS (&(de->de_ih->ih_key), key)) {
+ print_block (de->de_bh, 0, -1, -1);
+ reiserfs_panic (sb, "vs-7005: search_by_entry_key: found item %h is not directory item or "
+ "does not belong to the same directory as key %K", de->de_ih, key);
+ }
+#endif /* CONFIG_REISERFS_CHECK */
+
+ /* binary search in directory item by third componen t of the
+ key. sets de->de_entry_num of de */
+ retval = bin_search_in_dir_item (de, cpu_key_k_offset (key));
+ path->pos_in_item = de->de_entry_num;
+ if (retval != NAME_NOT_FOUND) {
+ // ugly, but rename needs de_bh, de_deh, de_name, de_namelen, de_objectid set
+ set_de_name_and_namelen (de);
+ set_de_object_key (de);
+ }
+ return retval;
+}
+
+
+
+/* Keyed 32-bit hash function using TEA in a Davis-Meyer function */
+
+/* The third component is hashed, and you can choose from more than
+ one hash function. Per directory hashes are not yet implemented
+ but are thought about. This function should be moved to hashes.c
+ Jedi, please do so. -Hans */
+
+static __u32 get_third_component (struct super_block * s,
+ const char * name, int len)
+{
+ __u32 res;
+
+ if (!len || (len == 1 && name[0] == '.'))
+ return DOT_OFFSET;
+ if (len == 2 && name[0] == '.' && name[1] == '.')
+ return DOT_DOT_OFFSET;
+
+ res = s->u.reiserfs_sb.s_hash_function (name, len);
+
+ // take bits from 7-th to 30-th including both bounds
+ res = GET_HASH_VALUE(res);
+ if (res == 0)
+ // needed to have no names before "." and ".." those have hash
+ // value == 0 and generation conters 1 and 2 accordingly
+ res = 128;
+ return res + MAX_GENERATION_NUMBER;
+}
+
+
+static int reiserfs_match (struct reiserfs_dir_entry * de,
+ const char * name, int namelen)
+{
+ int retval = NAME_NOT_FOUND;
+
+ if ((namelen == de->de_namelen) &&
+ !memcmp(de->de_name, name, de->de_namelen))
+ retval = (de_visible (de->de_deh + de->de_entry_num) ? NAME_FOUND : NAME_FOUND_INVISIBLE);
+
+ return retval;
+}
+
+
+/* de's de_bh, de_ih, de_deh, de_item_num, de_entry_num are set already */
+
+ /* used when hash collisions exist */
+
+
+static int linear_search_in_dir_item (struct cpu_key * key, struct reiserfs_dir_entry * de,
+ const char * name, int namelen)
+{
+ struct reiserfs_de_head * deh = de->de_deh;
+ int retval;
+ int i;
+
+ i = de->de_entry_num;
+
+ if (i == I_ENTRY_COUNT (de->de_ih) ||
+ GET_HASH_VALUE (deh_offset (deh + i)) != GET_HASH_VALUE (cpu_key_k_offset (key))) {
+ i --;
+ }
+
+ RFALSE( de->de_deh != B_I_DEH (de->de_bh, de->de_ih),
+ "vs-7010: array of entry headers not found");
+
+ deh += i;
+
+ for (; i >= 0; i --, deh --) {
+ if (GET_HASH_VALUE (deh_offset (deh)) !=
+ GET_HASH_VALUE (cpu_key_k_offset (key))) {
+ // hash value does not match, no need to check whole name
+ return NAME_NOT_FOUND;
+ }
+
+ /* mark, that this generation number is used */
+ if (de->de_gen_number_bit_string)
+ set_bit (GET_GENERATION_NUMBER (deh_offset (deh)), (unsigned long *)de->de_gen_number_bit_string);
+
+ // calculate pointer to name and namelen
+ de->de_entry_num = i;
+ set_de_name_and_namelen (de);
+
+ if ((retval = reiserfs_match (de, name, namelen)) != NAME_NOT_FOUND) {
+ // de's de_name, de_namelen, de_recordlen are set. Fill the rest:
+
+ // key of pointed object
+ set_de_object_key (de);
+
+ store_de_entry_key (de);
+
+ // retval can be NAME_FOUND or NAME_FOUND_INVISIBLE
+ return retval;
+ }
+ }
+
+ if (GET_GENERATION_NUMBER (le_ih_k_offset (de->de_ih)) == 0)
+ /* we have reached left most entry in the node. In common we
+ have to go to the left neighbor, but if generation counter
+ is 0 already, we know for sure, that there is no name with
+ the same hash value */
+ // FIXME: this work correctly only because hash value can not
+ // be 0. Btw, in case of Yura's hash it is probably possible,
+ // so, this is a bug
+ return NAME_NOT_FOUND;
+
+ RFALSE( de->de_item_num,
+ "vs-7015: two diritems of the same directory in one node?");
+
+ return GOTO_PREVIOUS_ITEM;
+}
+
+
+// may return NAME_FOUND, NAME_FOUND_INVISIBLE, NAME_NOT_FOUND
+// FIXME: should add something like IOERROR
+static int reiserfs_find_entry (struct inode * dir, const char * name, int namelen,
+ struct path * path_to_entry, struct reiserfs_dir_entry * de)
+{
+ struct cpu_key key_to_search;
+ int retval;
+
+
+ if (namelen > REISERFS_MAX_NAME (dir->i_sb->s_blocksize))
+ return NAME_NOT_FOUND;
+
+ /* we will search for this key in the tree */
+ make_cpu_key (&key_to_search, dir,
+ get_third_component (dir->i_sb, name, namelen), TYPE_DIRENTRY, 3);
+
+ while (1) {
+ retval = search_by_entry_key (dir->i_sb, &key_to_search, path_to_entry, de);
+ if (retval == IO_ERROR) {
+ reiserfs_warning (dir->i_sb, "zam-7001: io error in %s\n", __FUNCTION__);
+ return IO_ERROR;
+ }
+
+ /* compare names for all entries having given hash value */
+ retval = linear_search_in_dir_item (&key_to_search, de, name, namelen);
+ if (retval != GOTO_PREVIOUS_ITEM) {
+ /* there is no need to scan directory anymore. Given entry found or does not exist */
+ path_to_entry->pos_in_item = de->de_entry_num;
+ return retval;
+ }
+
+ /* there is left neighboring item of this directory and given entry can be there */
+ set_cpu_key_k_offset (&key_to_search, le_ih_k_offset (de->de_ih) - 1);
+ pathrelse (path_to_entry);
+
+ } /* while (1) */
+}
+
+
+static struct dentry * reiserfs_lookup (struct inode * dir, struct dentry * dentry)
+{
+ int retval;
+ struct inode * inode = NULL;
+ struct reiserfs_dir_entry de;
+ INITIALIZE_PATH (path_to_entry);
+
+ reiserfs_check_lock_depth("lookup") ;
+
+ if (REISERFS_MAX_NAME (dir->i_sb->s_blocksize) < dentry->d_name.len)
+ return ERR_PTR(-ENAMETOOLONG);
+
+ de.de_gen_number_bit_string = 0;
+ retval = reiserfs_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &path_to_entry, &de);
+ pathrelse (&path_to_entry);
+ if (retval == NAME_FOUND) {
+ inode = reiserfs_iget (dir->i_sb, (struct cpu_key *)&(de.de_dir_id));
+ if (!inode || IS_ERR(inode)) {
+ return ERR_PTR(-EACCES);
+ }
+ }
+ if ( retval == IO_ERROR ) {
+ return ERR_PTR(-EIO);
+ }
+
+ d_add(dentry, inode);
+ return NULL;
+}
+
+/* add entry to the directory (entry can be hidden).
+
+insert definition of when hidden directories are used here -Hans
+
+ Does not mark dir inode dirty, do it after successesfull call to it */
+
+static int reiserfs_add_entry (struct reiserfs_transaction_handle *th, struct inode * dir,
+ const char * name, int namelen, struct inode * inode,
+ int visible)
+{
+ struct cpu_key entry_key;
+ struct reiserfs_de_head * deh;
+ INITIALIZE_PATH (path);
+ struct reiserfs_dir_entry de;
+ int bit_string [MAX_GENERATION_NUMBER / (sizeof(int) * 8) + 1];
+ int gen_number;
+ char small_buf[32+DEH_SIZE] ; /* 48 bytes now and we avoid kmalloc
+ if we create file with short name */
+ char * buffer;
+ int buflen, paste_size;
+ int retval;
+
+
+ /* cannot allow items to be added into a busy deleted directory */
+ if (!namelen)
+ return -EINVAL;
+
+ if (namelen > REISERFS_MAX_NAME (dir->i_sb->s_blocksize))
+ return -ENAMETOOLONG;
+
+ /* each entry has unique key. compose it */
+ make_cpu_key (&entry_key, dir,
+ get_third_component (dir->i_sb, name, namelen), TYPE_DIRENTRY, 3);
+
+ /* get memory for composing the entry */
+ buflen = DEH_SIZE + ROUND_UP (namelen);
+ if (buflen > sizeof (small_buf)) {
+ buffer = reiserfs_kmalloc (buflen, GFP_NOFS, dir->i_sb);
+ if (buffer == 0)
+ return -ENOMEM;
+ } else
+ buffer = small_buf;
+
+ paste_size = (get_inode_sd_version (dir) == STAT_DATA_V1) ? (DEH_SIZE + namelen) : buflen;
+
+ /* fill buffer : directory entry head, name[, dir objectid | , stat data | ,stat data, dir objectid ] */
+ deh = (struct reiserfs_de_head *)buffer;
+ deh->deh_location = 0; /* JDM Endian safe if 0 */
+ put_deh_offset( deh, cpu_key_k_offset( &entry_key ) );
+ deh->deh_state = 0; /* JDM Endian safe if 0 */
+ /* put key (ino analog) to de */
+ deh->deh_dir_id = INODE_PKEY (inode)->k_dir_id; /* safe: k_dir_id is le */
+ deh->deh_objectid = INODE_PKEY (inode)->k_objectid; /* safe: k_objectid is le */
+
+ /* copy name */
+ memcpy ((char *)(deh + 1), name, namelen);
+ /* padd by 0s to the 4 byte boundary */
+ padd_item ((char *)(deh + 1), ROUND_UP (namelen), namelen);
+
+ /* entry is ready to be pasted into tree, set 'visibility' and 'stat data in entry' attributes */
+ mark_de_without_sd (deh);
+ visible ? mark_de_visible (deh) : mark_de_hidden (deh);
+
+ /* find the proper place for the new entry */
+ memset (bit_string, 0, sizeof (bit_string));
+ de.de_gen_number_bit_string = (char *)bit_string;
+ retval = reiserfs_find_entry (dir, name, namelen, &path, &de);
+ if( retval != NAME_NOT_FOUND ) {
+ if (buffer != small_buf)
+ reiserfs_kfree (buffer, buflen, dir->i_sb);
+ pathrelse (&path);
+
+ if ( retval == IO_ERROR ) {
+ return -EIO;
+ }
+
+ if (retval != NAME_FOUND) {
+ reiserfs_warning (dir->i_sb, "zam-7002:%s: \"reiserfs_find_entry\" has returned"
+ " unexpected value (%d)\n", __FUNCTION__, retval);
+ }
+
+ return -EEXIST;
+ }
+
+ gen_number = find_first_zero_bit ((unsigned long *)bit_string, MAX_GENERATION_NUMBER + 1);
+ if (gen_number > MAX_GENERATION_NUMBER) {
+ /* there is no free generation number */
+ reiserfs_warning (dir->i_sb, "reiserfs_add_entry: Congratulations! we have got hash function screwed up\n");
+ if (buffer != small_buf)
+ reiserfs_kfree (buffer, buflen, dir->i_sb);
+ pathrelse (&path);
+/*
+ * Trivial changes by Alan Cox to remove EHASHCOLLISION for compatibility
+ *
+ * Trivial Changes:
+ * Rights granted to Hans Reiser to redistribute under other terms providing
+ * he accepts all liability including but not limited to patent, fitness
+ * for purpose, and direct or indirect claims arising from failure to perform.
+ *
+ * NO WARRANTY
+ * This is one of two lines that this fix consist of.
+ */
+ return -EBUSY;
+ /* I think it was better to have an error code with a name that says
+ what it means, but I choose not to fight over it. Persons porting to
+ other operating systems should consider keeping it as it was
+ (return -EHASHCOLLISION;). -Hans */
+ }
+ /* adjust offset of directory enrty */
+ put_deh_offset(deh, SET_GENERATION_NUMBER(deh_offset(deh), gen_number));
+ set_cpu_key_k_offset (&entry_key, deh_offset(deh));
+
+ /* update max-hash-collisions counter in reiserfs_sb_info */
+ PROC_INFO_MAX( th -> t_super, max_hash_collisions, gen_number );
+
+ if (gen_number != 0) { /* we need to re-search for the insertion point */
+ if (search_by_entry_key (dir->i_sb, &entry_key, &path, &de) != NAME_NOT_FOUND) {
+ reiserfs_warning (dir->i_sb, "vs-7032: reiserfs_add_entry: "
+ "entry with this key (%K) already exists\n", &entry_key);
+
+ if (buffer != small_buf)
+ reiserfs_kfree (buffer, buflen, dir->i_sb);
+ pathrelse (&path);
+ /* Following line is 2nd line touched by Alan Cox' trivial fix */
+ return -EBUSY;
+ /* I think it was better to have an error code with a name that says
+ what it means, but I choose not to fight over it. Persons porting to
+ other operating systems should consider keeping it as it was
+ (return -EHASHCOLLISION;). -Hans */
+ }
+ }
+
+ /* perform the insertion of the entry that we have prepared */
+ retval = reiserfs_paste_into_item (th, &path, &entry_key, buffer, paste_size);
+ if (buffer != small_buf)
+ reiserfs_kfree (buffer, buflen, dir->i_sb);
+ if (retval) {
+ reiserfs_check_path(&path) ;
+ return retval;
+ }
+
+ dir->i_size += paste_size;
+ dir->i_blocks = ((dir->i_size + 511) >> 9);
+ dir->i_mtime = dir->i_ctime = CURRENT_TIME;
+ if (!S_ISDIR (inode->i_mode) && visible)
+ // reiserfs_mkdir or reiserfs_rename will do that by itself
+ reiserfs_update_sd (th, dir);
+
+ reiserfs_check_path(&path) ;
+ return 0;
+}
+
+/* quota utility function, call if you've had to abort after calling
+** new_inode_init, and have not called reiserfs_new_inode yet.
+** This should only be called on inodes that do not hav stat data
+** inserted into the tree yet.
+*/
+static int drop_new_inode(struct inode *inode) {
+ make_bad_inode(inode) ;
+ iput(inode) ;
+ return 0 ;
+}
+
+/* utility function that does setup for reiserfs_new_inode.
+** DQUOT_ALLOC_INODE cannot be called inside a transaction, so we had
+** to pull some bits of reiserfs_new_inode out into this func.
+*/
+static int new_inode_init(struct inode *inode, struct inode *dir, int mode) {
+
+ /* the quota init calls have to know who to charge the quota to, so
+ ** we have to set uid and gid here
+ */
+ inode->i_uid = current->fsuid;
+ inode->i_mode = mode;
+
+ if (dir->i_mode & S_ISGID) {
+ inode->i_gid = dir->i_gid;
+ if (S_ISDIR(mode))
+ inode->i_mode |= S_ISGID;
+ } else
+ inode->i_gid = current->fsgid;
+
+ return 0 ;
+}
+
+static int reiserfs_create (struct inode * dir, struct dentry *dentry, int mode)
+{
+ int retval;
+ struct inode * inode;
+ int jbegin_count = JOURNAL_PER_BALANCE_CNT * 2 ;
+ struct reiserfs_transaction_handle th ;
+
+ if (!(inode = new_inode(dir->i_sb))) {
+ return -ENOMEM ;
+ }
+ retval = new_inode_init(inode, dir, mode) ;
+ if (retval)
+ return retval ;
+
+ journal_begin(&th, dir->i_sb, jbegin_count) ;
+ th.t_caller = "create" ;
+ retval = reiserfs_new_inode (&th, dir, mode, 0, 0/*i_size*/, dentry, inode);
+ if (retval) {
+ goto out_failed ;
+ }
+
+ inode->i_op = &reiserfs_file_inode_operations;
+ inode->i_fop = &reiserfs_file_operations;
+ inode->i_mapping->a_ops = &reiserfs_address_space_operations ;
+
+ retval = reiserfs_add_entry (&th, dir, dentry->d_name.name, dentry->d_name.len,
+ inode, 1/*visible*/);
+ if (retval) {
+ inode->i_nlink--;
+ reiserfs_update_sd (&th, inode);
+ journal_end(&th, dir->i_sb, jbegin_count) ;
+ iput(inode) ;
+ goto out_failed ;
+ }
+ reiserfs_update_inode_transaction(inode) ;
+ reiserfs_update_inode_transaction(dir) ;
+
+ d_instantiate(dentry, inode);
+ journal_end(&th, dir->i_sb, jbegin_count) ;
+ return 0;
+
+out_failed:
+ return retval ;
+}
+
+
+static int reiserfs_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev)
+{
+ int retval;
+ struct inode * inode;
+ struct reiserfs_transaction_handle th ;
+ int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3;
+
+ if (!(inode = new_inode(dir->i_sb))) {
+ return -ENOMEM ;
+ }
+ retval = new_inode_init(inode, dir, mode) ;
+ if (retval)
+ return retval ;
+
+ journal_begin(&th, dir->i_sb, jbegin_count) ;
+
+ retval = reiserfs_new_inode(&th, dir, mode, 0, 0/*i_size*/, dentry, inode);
+ if (retval) {
+ goto out_failed;
+ }
+
+ init_special_inode(inode, mode, rdev) ;
+
+ //FIXME: needed for block and char devices only
+ reiserfs_update_sd (&th, inode);
+
+ reiserfs_update_inode_transaction(inode) ;
+ reiserfs_update_inode_transaction(dir) ;
+
+ retval = reiserfs_add_entry (&th, dir, dentry->d_name.name, dentry->d_name.len,
+ inode, 1/*visible*/);
+ if (retval) {
+ inode->i_nlink--;
+ reiserfs_update_sd (&th, inode);
+ journal_end(&th, dir->i_sb, jbegin_count) ;
+ iput(inode) ;
+ goto out_failed;
+ }
+
+ d_instantiate(dentry, inode);
+ journal_end(&th, dir->i_sb, jbegin_count) ;
+ return 0;
+
+out_failed:
+ return retval ;
+}
+
+
+static int reiserfs_mkdir (struct inode * dir, struct dentry *dentry, int mode)
+{
+ int retval;
+ struct inode * inode;
+ struct reiserfs_transaction_handle th ;
+ int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3;
+
+ mode = S_IFDIR | mode;
+ if (!(inode = new_inode(dir->i_sb))) {
+ return -ENOMEM ;
+ }
+ retval = new_inode_init(inode, dir, mode) ;
+ if (retval)
+ return retval ;
+
+ journal_begin(&th, dir->i_sb, jbegin_count) ;
+
+ /* inc the link count now, so another writer doesn't overflow it while
+ ** we sleep later on.
+ */
+ INC_DIR_INODE_NLINK(dir)
+
+#ifdef DISPLACE_NEW_PACKING_LOCALITIES
+ /* set flag that new packing locality created and new blocks for the content * of that directory are not displaced yet */
+ dir->u.reiserfs_i.new_packing_locality = 1;
+#endif
+ retval = reiserfs_new_inode(&th, dir, mode, 0/*symlink*/,
+ old_format_only (dir->i_sb) ?
+ EMPTY_DIR_SIZE_V1 : EMPTY_DIR_SIZE,
+ dentry, inode) ;
+ if (retval) {
+ dir->i_nlink-- ;
+ goto out_failed ;
+ }
+ reiserfs_update_inode_transaction(inode) ;
+ reiserfs_update_inode_transaction(dir) ;
+
+ inode->i_op = &reiserfs_dir_inode_operations;
+ inode->i_fop = &reiserfs_dir_operations;
+
+ // note, _this_ add_entry will not update dir's stat data
+ retval = reiserfs_add_entry (&th, dir, dentry->d_name.name, dentry->d_name.len,
+ inode, 1/*visible*/);
+ if (retval) {
+ inode->i_nlink = 0;
+ DEC_DIR_INODE_NLINK(dir);
+ reiserfs_update_sd (&th, inode);
+ journal_end(&th, dir->i_sb, jbegin_count) ;
+ iput(inode) ;
+ goto out_failed ;
+ }
+
+ // the above add_entry did not update dir's stat data
+ reiserfs_update_sd (&th, dir);
+
+ d_instantiate(dentry, inode);
+ journal_end(&th, dir->i_sb, jbegin_count) ;
+ return 0;
+
+out_failed:
+ return retval ;
+}
+
+static inline int reiserfs_empty_dir(struct inode *inode) {
+ /* we can cheat because an old format dir cannot have
+ ** EMPTY_DIR_SIZE, and a new format dir cannot have
+ ** EMPTY_DIR_SIZE_V1. So, if the inode is either size,
+ ** regardless of disk format version, the directory is empty.
+ */
+ if (inode->i_size != EMPTY_DIR_SIZE &&
+ inode->i_size != EMPTY_DIR_SIZE_V1) {
+ return 0 ;
+ }
+ return 1 ;
+}
+
+
+static int reiserfs_rmdir (struct inode * dir, struct dentry *dentry)
+{
+ int retval;
+ struct inode * inode;
+ int windex ;
+ struct reiserfs_transaction_handle th ;
+ int jbegin_count;
+ INITIALIZE_PATH (path);
+ struct reiserfs_dir_entry de;
+
+
+ /* we will be doing 2 balancings and update 2 stat data */
+ jbegin_count = JOURNAL_PER_BALANCE_CNT * 2 + 2;
+
+ journal_begin(&th, dir->i_sb, jbegin_count) ;
+ windex = push_journal_writer("reiserfs_rmdir") ;
+
+ de.de_gen_number_bit_string = 0;
+ if ( (retval = reiserfs_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &path, &de)) == NAME_NOT_FOUND) {
+ retval = -ENOENT;
+ goto end_rmdir;
+ } else if ( retval == IO_ERROR) {
+ retval = -EIO;
+ goto end_rmdir;
+ }
+
+ inode = dentry->d_inode;
+
+ reiserfs_update_inode_transaction(inode) ;
+ reiserfs_update_inode_transaction(dir) ;
+
+ if (de.de_objectid != inode->i_ino) {
+ // FIXME: compare key of an object and a key found in the
+ // entry
+ retval = -EIO;
+ goto end_rmdir;
+ }
+ if (!reiserfs_empty_dir(inode)) {
+ retval = -ENOTEMPTY;
+ goto end_rmdir;
+ }
+
+ /* cut entry from dir directory */
+ retval = reiserfs_cut_from_item (&th, &path, &(de.de_entry_key), dir,
+ NULL, /* page */
+ 0/*new file size - not used here*/);
+ if (retval < 0)
+ goto end_rmdir;
+
+ if ( inode->i_nlink != 2 && inode->i_nlink != 1 )
+ reiserfs_warning ( inode->i_sb, "reiserfs_rmdir: empty directory has nlink != 2 (%d)\n", inode->i_nlink);
+
+ inode->i_nlink = 0;
+ inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ reiserfs_update_sd (&th, inode);
+
+ DEC_DIR_INODE_NLINK(dir)
+ dir->i_size -= (DEH_SIZE + de.de_entrylen);
+ dir->i_blocks = ((dir->i_size + 511) >> 9);
+ reiserfs_update_sd (&th, dir);
+
+ /* prevent empty directory from getting lost */
+ add_save_link (&th, inode, 0/* not truncate */);
+
+ pop_journal_writer(windex) ;
+ journal_end(&th, dir->i_sb, jbegin_count) ;
+ reiserfs_check_path(&path) ;
+ return 0;
+
+ end_rmdir:
+ /* we must release path, because we did not call
+ reiserfs_cut_from_item, or reiserfs_cut_from_item does not
+ release path if operation was not complete */
+ pathrelse (&path);
+ pop_journal_writer(windex) ;
+ journal_end(&th, dir->i_sb, jbegin_count) ;
+ return retval;
+}
+
+
+static int reiserfs_unlink (struct inode * dir, struct dentry *dentry)
+{
+ int retval;
+ struct inode * inode;
+ struct reiserfs_dir_entry de;
+ INITIALIZE_PATH (path);
+ int windex ;
+ struct reiserfs_transaction_handle th ;
+ int jbegin_count;
+ unsigned long savelink;
+
+ inode = dentry->d_inode;
+
+ /* in this transaction we can be doing at max two balancings and update
+ two stat datas */
+ jbegin_count = JOURNAL_PER_BALANCE_CNT * 2 + 2;
+
+ journal_begin(&th, dir->i_sb, jbegin_count) ;
+ windex = push_journal_writer("reiserfs_unlink") ;
+
+ de.de_gen_number_bit_string = 0;
+ if ( (retval = reiserfs_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &path, &de)) == NAME_NOT_FOUND) {
+ retval = -ENOENT;
+ goto end_unlink;
+ } else if (retval == IO_ERROR) {
+ retval = -EIO;
+ goto end_unlink;
+ }
+
+ reiserfs_update_inode_transaction(inode) ;
+ reiserfs_update_inode_transaction(dir) ;
+
+ if (de.de_objectid != inode->i_ino) {
+ // FIXME: compare key of an object and a key found in the
+ // entry
+ retval = -EIO;
+ goto end_unlink;
+ }
+
+ if (!inode->i_nlink) {
+ reiserfs_warning(inode->i_sb, "reiserfs_unlink: deleting nonexistent file (%s:%lu), %d\n",
+ kdevname(inode->i_dev), inode->i_ino, inode->i_nlink);
+ inode->i_nlink = 1;
+ }
+
+ inode->i_nlink--;
+
+ /*
+ * we schedule before doing the add_save_link call, save the link
+ * count so we don't race
+ */
+ savelink = inode->i_nlink;
+
+
+ retval = reiserfs_cut_from_item (&th, &path, &(de.de_entry_key), dir, NULL, 0);
+ if (retval < 0) {
+ inode->i_nlink++;
+ goto end_unlink;
+ }
+ inode->i_ctime = CURRENT_TIME;
+ reiserfs_update_sd (&th, inode);
+
+ dir->i_size -= (de.de_entrylen + DEH_SIZE);
+ dir->i_blocks = ((dir->i_size + 511) >> 9);
+ dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ reiserfs_update_sd (&th, dir);
+
+ if (!savelink)
+ /* prevent file from getting lost */
+ add_save_link (&th, inode, 0/* not truncate */);
+
+ pop_journal_writer(windex) ;
+ journal_end(&th, dir->i_sb, jbegin_count) ;
+ reiserfs_check_path(&path) ;
+ return 0;
+
+ end_unlink:
+ pathrelse (&path);
+ pop_journal_writer(windex) ;
+ journal_end(&th, dir->i_sb, jbegin_count) ;
+ reiserfs_check_path(&path) ;
+ return retval;
+}
+
+
+static int reiserfs_symlink (struct inode * parent_dir, struct dentry * dentry, const char * symname)
+{
+ int retval;
+ struct inode * inode;
+ char * name;
+ int item_len;
+ int mode = S_IFLNK | S_IRWXUGO ;
+ struct reiserfs_transaction_handle th ;
+ int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3;
+
+
+ if (!(inode = new_inode(parent_dir->i_sb))) {
+ return -ENOMEM ;
+ }
+ retval = new_inode_init(inode, parent_dir, mode) ;
+ if (retval) {
+ return retval ;
+ }
+
+ item_len = ROUND_UP (strlen (symname));
+ if (item_len > MAX_DIRECT_ITEM_LEN (parent_dir->i_sb->s_blocksize)) {
+ retval = -ENAMETOOLONG;
+ drop_new_inode(inode) ;
+ goto out_failed ;
+ }
+
+ name = reiserfs_kmalloc (item_len, GFP_NOFS, parent_dir->i_sb);
+ if (!name) {
+ retval = -ENOMEM;
+ drop_new_inode(inode) ;
+ goto out_failed ;
+ }
+ memcpy (name, symname, strlen (symname));
+ padd_item (name, item_len, strlen (symname));
+
+ journal_begin(&th, parent_dir->i_sb, jbegin_count) ;
+
+ retval = reiserfs_new_inode(&th, parent_dir, mode, name,
+ strlen(symname), dentry, inode) ;
+ reiserfs_kfree (name, item_len, parent_dir->i_sb);
+ if (retval) {
+ goto out_failed ;
+ }
+
+ reiserfs_update_inode_transaction(inode) ;
+ reiserfs_update_inode_transaction(parent_dir) ;
+
+ inode->i_op = &page_symlink_inode_operations;
+ inode->i_mapping->a_ops = &reiserfs_address_space_operations;
+
+ // must be sure this inode is written with this transaction
+ //
+ //reiserfs_update_sd (&th, inode, READ_BLOCKS);
+
+ retval = reiserfs_add_entry (&th, parent_dir, dentry->d_name.name, dentry->d_name.len,
+ inode, 1/*visible*/);
+ if (retval) {
+ inode->i_nlink--;
+ reiserfs_update_sd (&th, inode);
+ journal_end(&th, parent_dir->i_sb, jbegin_count) ;
+ iput(inode) ;
+ goto out_failed ;
+ }
+
+ d_instantiate(dentry, inode);
+ journal_end(&th, parent_dir->i_sb, jbegin_count) ;
+ return 0;
+
+out_failed:
+ return retval ;
+}
+
+
+static int reiserfs_link (struct dentry * old_dentry, struct inode * dir, struct dentry * dentry)
+{
+ int retval;
+ struct inode *inode = old_dentry->d_inode;
+ int windex ;
+ struct reiserfs_transaction_handle th ;
+ int jbegin_count = JOURNAL_PER_BALANCE_CNT * 3;
+ time_t ctime;
+
+
+ if (S_ISDIR(inode->i_mode))
+ return -EPERM;
+
+ if (inode->i_nlink >= REISERFS_LINK_MAX) {
+ //FIXME: sd_nlink is 32 bit for new files
+ return -EMLINK;
+ }
+ if (inode->i_nlink == 0) {
+ return -ENOENT;
+ }
+
+ /* inc before scheduling so reiserfs_unlink knows we are here */
+ inode->i_nlink++;
+
+ journal_begin(&th, dir->i_sb, jbegin_count) ;
+ windex = push_journal_writer("reiserfs_link") ;
+
+ /* create new entry */
+ retval = reiserfs_add_entry (&th, dir, dentry->d_name.name, dentry->d_name.len,
+ inode, 1/*visible*/);
+
+ reiserfs_update_inode_transaction(inode) ;
+ reiserfs_update_inode_transaction(dir) ;
+
+ if (retval) {
+ inode->i_nlink--;
+ pop_journal_writer(windex) ;
+ journal_end(&th, dir->i_sb, jbegin_count) ;
+ return retval;
+ }
+
+ ctime = CURRENT_TIME;
+ inode->i_ctime = ctime;
+ reiserfs_update_sd (&th, inode);
+
+ atomic_inc(&inode->i_count) ;
+ d_instantiate(dentry, inode);
+ pop_journal_writer(windex) ;
+ journal_end(&th, dir->i_sb, jbegin_count) ;
+ return 0;
+}
+
+
+// de contains information pointing to an entry which
+static int de_still_valid (const char * name, int len, struct reiserfs_dir_entry * de)
+{
+ struct reiserfs_dir_entry tmp = *de;
+
+ // recalculate pointer to name and name length
+ set_de_name_and_namelen (&tmp);
+ // FIXME: could check more
+ if (tmp.de_namelen != len || memcmp (name, de->de_name, len))
+ return 0;
+ return 1;
+}
+
+
+static int entry_points_to_object (const char * name, int len, struct reiserfs_dir_entry * de, struct inode * inode)
+{
+ if (!de_still_valid (name, len, de))
+ return 0;
+
+ if (inode) {
+ if (!de_visible (de->de_deh + de->de_entry_num))
+ reiserfs_panic (0, "vs-7042: entry_points_to_object: entry must be visible");
+ return (de->de_objectid == inode->i_ino) ? 1 : 0;
+ }
+
+ /* this must be added hidden entry */
+ if (de_visible (de->de_deh + de->de_entry_num))
+ reiserfs_panic (0, "vs-7043: entry_points_to_object: entry must be visible");
+
+ return 1;
+}
+
+
+/* sets key of objectid the entry has to point to */
+static void set_ino_in_dir_entry (struct reiserfs_dir_entry * de, struct key * key)
+{
+ /* JDM These operations are endian safe - both are le */
+ de->de_deh[de->de_entry_num].deh_dir_id = key->k_dir_id;
+ de->de_deh[de->de_entry_num].deh_objectid = key->k_objectid;
+}
+
+
+/*
+ * process, that is going to call fix_nodes/do_balance must hold only
+ * one path. If it holds 2 or more, it can get into endless waiting in
+ * get_empty_nodes or its clones
+ */
+static int reiserfs_rename (struct inode * old_dir, struct dentry *old_dentry,
+ struct inode * new_dir, struct dentry *new_dentry)
+{
+ int retval;
+ INITIALIZE_PATH (old_entry_path);
+ INITIALIZE_PATH (new_entry_path);
+ INITIALIZE_PATH (dot_dot_entry_path);
+ struct item_head new_entry_ih, old_entry_ih, dot_dot_ih ;
+ struct reiserfs_dir_entry old_de, new_de, dot_dot_de;
+ struct inode * old_inode, * new_dentry_inode;
+ int windex ;
+ struct reiserfs_transaction_handle th ;
+ int jbegin_count ;
+ umode_t old_inode_mode;
+ time_t ctime;
+ unsigned long savelink = 1;
+
+
+ /* two balancings: old name removal, new name insertion or "save" link,
+ stat data updates: old directory and new directory and maybe block
+ containing ".." of renamed directory */
+ jbegin_count = JOURNAL_PER_BALANCE_CNT * 3 + 3;
+
+ old_inode = old_dentry->d_inode;
+ new_dentry_inode = new_dentry->d_inode;
+
+ // make sure, that oldname still exists and points to an object we
+ // are going to rename
+ old_de.de_gen_number_bit_string = 0;
+ retval = reiserfs_find_entry (old_dir, old_dentry->d_name.name, old_dentry->d_name.len,
+ &old_entry_path, &old_de);
+ pathrelse (&old_entry_path);
+ if (retval == IO_ERROR)
+ return -EIO;
+
+ if (retval != NAME_FOUND || old_de.de_objectid != old_inode->i_ino) {
+ return -ENOENT;
+ }
+
+ old_inode_mode = old_inode->i_mode;
+ if (S_ISDIR(old_inode_mode)) {
+ // make sure, that directory being renamed has correct ".."
+ // and that its new parent directory has not too many links
+ // already
+
+ if (new_dentry_inode) {
+ if (!reiserfs_empty_dir(new_dentry_inode)) {
+ return -ENOTEMPTY;
+ }
+ }
+
+ /* directory is renamed, its parent directory will be changed,
+ ** so find ".." entry
+ */
+ dot_dot_de.de_gen_number_bit_string = 0;
+ retval = reiserfs_find_entry (old_inode, "..", 2, &dot_dot_entry_path, &dot_dot_de);
+ pathrelse (&dot_dot_entry_path);
+ if (retval != NAME_FOUND)
+ return -EIO;
+
+ /* inode number of .. must equal old_dir->i_ino */
+ if (dot_dot_de.de_objectid != old_dir->i_ino)
+ return -EIO;
+ }
+
+ journal_begin(&th, old_dir->i_sb, jbegin_count) ;
+ windex = push_journal_writer("reiserfs_rename") ;
+
+ /* add new entry (or find the existing one) */
+ retval = reiserfs_add_entry (&th, new_dir, new_dentry->d_name.name, new_dentry->d_name.len,
+ old_inode, 0);
+ if (retval == -EEXIST) {
+ if (!new_dentry_inode) {
+ reiserfs_panic (old_dir->i_sb,
+ "vs-7050: new entry is found, new inode == 0\n");
+ }
+ } else if (retval) {
+ pop_journal_writer(windex) ;
+ journal_end(&th, old_dir->i_sb, jbegin_count) ;
+ return retval;
+ }
+
+ reiserfs_update_inode_transaction(old_dir) ;
+ reiserfs_update_inode_transaction(new_dir) ;
+
+ /* this makes it so an fsync on an open fd for the old name will
+ ** commit the rename operation
+ */
+ reiserfs_update_inode_transaction(old_inode) ;
+
+ if (new_dentry_inode)
+ reiserfs_update_inode_transaction(new_dentry_inode) ;
+
+ while (1) {
+ // look for old name using corresponding entry key (found by reiserfs_find_entry)
+ if (search_by_entry_key (new_dir->i_sb, &old_de.de_entry_key, &old_entry_path, &old_de) != NAME_FOUND)
+ BUG ();
+
+ copy_item_head(&old_entry_ih, get_ih(&old_entry_path)) ;
+
+ reiserfs_prepare_for_journal(old_inode->i_sb, old_de.de_bh, 1) ;
+
+ // look for new name by reiserfs_find_entry
+ new_de.de_gen_number_bit_string = 0;
+ retval = reiserfs_find_entry (new_dir, new_dentry->d_name.name, new_dentry->d_name.len,
+ &new_entry_path, &new_de);
+ // reiserfs_add_entry should not return IO_ERROR, because it is called with essentially same parameters from
+ // reiserfs_add_entry above, and we'll catch any i/o errors before we get here.
+ if (retval != NAME_FOUND_INVISIBLE && retval != NAME_FOUND)
+ BUG ();
+
+ copy_item_head(&new_entry_ih, get_ih(&new_entry_path)) ;
+
+ reiserfs_prepare_for_journal(old_inode->i_sb, new_de.de_bh, 1) ;
+
+ if (S_ISDIR(old_inode->i_mode)) {
+ if (search_by_entry_key (new_dir->i_sb, &dot_dot_de.de_entry_key, &dot_dot_entry_path, &dot_dot_de) != NAME_FOUND)
+ BUG ();
+ copy_item_head(&dot_dot_ih, get_ih(&dot_dot_entry_path)) ;
+ // node containing ".." gets into transaction
+ reiserfs_prepare_for_journal(old_inode->i_sb, dot_dot_de.de_bh, 1) ;
+ }
+ /* we should check seals here, not do
+ this stuff, yes? Then, having
+ gathered everything into RAM we
+ should lock the buffers, yes? -Hans */
+ /* probably. our rename needs to hold more
+ ** than one path at once. The seals would
+ ** have to be written to deal with multi-path
+ ** issues -chris
+ */
+ /* sanity checking before doing the rename - avoid races many
+ ** of the above checks could have scheduled. We have to be
+ ** sure our items haven't been shifted by another process.
+ */
+ if (item_moved(&new_entry_ih, &new_entry_path) ||
+ !entry_points_to_object(new_dentry->d_name.name,
+ new_dentry->d_name.len,
+ &new_de, new_dentry_inode) ||
+ item_moved(&old_entry_ih, &old_entry_path) ||
+ !entry_points_to_object (old_dentry->d_name.name,
+ old_dentry->d_name.len,
+ &old_de, old_inode)) {
+ reiserfs_restore_prepared_buffer (old_inode->i_sb, new_de.de_bh);
+ reiserfs_restore_prepared_buffer (old_inode->i_sb, old_de.de_bh);
+ if (S_ISDIR(old_inode_mode))
+ reiserfs_restore_prepared_buffer (old_inode->i_sb, dot_dot_de.de_bh);
+ continue;
+ }
+ if (S_ISDIR(old_inode_mode)) {
+ if ( item_moved(&dot_dot_ih, &dot_dot_entry_path) ||
+ !entry_points_to_object ( "..", 2, &dot_dot_de, old_dir) ) {
+ reiserfs_restore_prepared_buffer (old_inode->i_sb, old_de.de_bh);
+ reiserfs_restore_prepared_buffer (old_inode->i_sb, new_de.de_bh);
+ reiserfs_restore_prepared_buffer (old_inode->i_sb, dot_dot_de.de_bh);
+ continue;
+ }
+ }
+
+
+ RFALSE( S_ISDIR(old_inode_mode) &&
+ !reiserfs_buffer_prepared(dot_dot_de.de_bh), "" );
+
+ break;
+ }
+
+ /* ok, all the changes can be done in one fell swoop when we
+ have claimed all the buffers needed.*/
+
+ mark_de_visible (new_de.de_deh + new_de.de_entry_num);
+ set_ino_in_dir_entry (&new_de, INODE_PKEY (old_inode));
+ journal_mark_dirty (&th, old_dir->i_sb, new_de.de_bh);
+
+ mark_de_hidden (old_de.de_deh + old_de.de_entry_num);
+ journal_mark_dirty (&th, old_dir->i_sb, old_de.de_bh);
+ old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
+ new_dir->i_ctime = new_dir->i_mtime = CURRENT_TIME;
+
+ if (new_dentry_inode) {
+ // adjust link number of the victim
+ if (S_ISDIR(new_dentry_inode->i_mode)) {
+ new_dentry_inode->i_nlink = 0;
+ } else {
+ new_dentry_inode->i_nlink--;
+ }
+ ctime = CURRENT_TIME;
+ new_dentry_inode->i_ctime = ctime;
+ savelink = new_dentry_inode->i_nlink;
+ }
+
+ if (S_ISDIR(old_inode_mode)) {
+ // adjust ".." of renamed directory
+ set_ino_in_dir_entry (&dot_dot_de, INODE_PKEY (new_dir));
+ journal_mark_dirty (&th, new_dir->i_sb, dot_dot_de.de_bh);
+
+ if (!new_dentry_inode)
+ /* there (in new_dir) was no directory, so it got new link
+ (".." of renamed directory) */
+ INC_DIR_INODE_NLINK(new_dir);
+
+ /* old directory lost one link - ".. " of renamed directory */
+ DEC_DIR_INODE_NLINK(old_dir);
+ }
+
+ // looks like in 2.3.99pre3 brelse is atomic. so we can use pathrelse
+ pathrelse (&new_entry_path);
+ pathrelse (&dot_dot_entry_path);
+
+ // FIXME: this reiserfs_cut_from_item's return value may screw up
+ // anybody, but it will panic if will not be able to find the
+ // entry. This needs one more clean up
+ if (reiserfs_cut_from_item (&th, &old_entry_path, &(old_de.de_entry_key), old_dir, NULL, 0) < 0)
+ reiserfs_warning ((&th)->t_super, "vs-7060: reiserfs_rename: couldn't not cut old name. Fsck later?\n");
+
+ old_dir->i_size -= DEH_SIZE + old_de.de_entrylen;
+ old_dir->i_blocks = ((old_dir->i_size + 511) >> 9);
+
+ reiserfs_update_sd (&th, old_dir);
+ reiserfs_update_sd (&th, new_dir);
+
+ if (new_dentry_inode) {
+ if (savelink == 0)
+ add_save_link (&th, new_dentry_inode, 0/* not truncate */);
+ reiserfs_update_sd (&th, new_dentry_inode);
+ }
+
+ pop_journal_writer(windex) ;
+ journal_end(&th, old_dir->i_sb, jbegin_count) ;
+ return 0;
+}
+
+
+
+/*
+ * directories can handle most operations...
+ */
+struct inode_operations reiserfs_dir_inode_operations = {
+ //&reiserfs_dir_operations, /* default_file_ops */
+ create: reiserfs_create,
+ lookup: reiserfs_lookup,
+ link: reiserfs_link,
+ unlink: reiserfs_unlink,
+ symlink: reiserfs_symlink,
+ mkdir: reiserfs_mkdir,
+ rmdir: reiserfs_rmdir,
+ mknod: reiserfs_mknod,
+ rename: reiserfs_rename,
+};
+
diff --git a/fs/reiserfs/objectid.c b/fs/reiserfs/objectid.c
new file mode 100644
index 00000000000000..5a81ba3f1e3bc5
--- /dev/null
+++ b/fs/reiserfs/objectid.c
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2000-2002 by Hans Reiser, licensing governed by reiserfs/README
+ */
+
+#include <linux/config.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/random.h>
+#include <linux/sched.h>
+#include <linux/reiserfs_fs.h>
+#include <linux/reiserfs_fs_sb.h>
+
+// find where objectid map starts
+#define objectid_map(s,rs) (old_format_only (s) ? \
+ (__u32 *)((struct reiserfs_super_block_v1 *)(rs) + 1) :\
+ (__u32 *)((rs) + 1))
+
+
+#ifdef CONFIG_REISERFS_CHECK
+
+static void check_objectid_map (struct super_block * s, __u32 * map)
+{
+ if (le32_to_cpu (map[0]) != 1)
+ reiserfs_panic (s, "vs-15010: check_objectid_map: map corrupted: %lx",
+ ( long unsigned int ) le32_to_cpu (map[0]));
+
+ // FIXME: add something else here
+}
+
+#else
+static void check_objectid_map (struct super_block * s, __u32 * map)
+{;}
+#endif
+
+
+/* When we allocate objectids we allocate the first unused objectid.
+ Each sequence of objectids in use (the odd sequences) is followed
+ by a sequence of objectids not in use (the even sequences). We
+ only need to record the last objectid in each of these sequences
+ (both the odd and even sequences) in order to fully define the
+ boundaries of the sequences. A consequence of allocating the first
+ objectid not in use is that under most conditions this scheme is
+ extremely compact. The exception is immediately after a sequence
+ of operations which deletes a large number of objects of
+ non-sequential objectids, and even then it will become compact
+ again as soon as more objects are created. Note that many
+ interesting optimizations of layout could result from complicating
+ objectid assignment, but we have deferred making them for now. */
+
+
+/* get unique object identifier */
+__u32 reiserfs_get_unused_objectid (struct reiserfs_transaction_handle *th)
+{
+ struct super_block * s = th->t_super;
+ struct reiserfs_super_block * rs = SB_DISK_SUPER_BLOCK (s);
+ __u32 * map = objectid_map (s, rs);
+ __u32 unused_objectid;
+
+
+ check_objectid_map (s, map);
+
+ reiserfs_prepare_for_journal(s, SB_BUFFER_WITH_SB(s), 1) ;
+ /* comment needed -Hans */
+ unused_objectid = le32_to_cpu (map[1]);
+ if (unused_objectid == U32_MAX) {
+ reiserfs_warning (s, "REISERFS: get_objectid: no more object ids\n");
+ reiserfs_restore_prepared_buffer(s, SB_BUFFER_WITH_SB(s)) ;
+ return 0;
+ }
+
+ /* This incrementation allocates the first unused objectid. That
+ is to say, the first entry on the objectid map is the first
+ unused objectid, and by incrementing it we use it. See below
+ where we check to see if we eliminated a sequence of unused
+ objectids.... */
+ map[1] = cpu_to_le32 (unused_objectid + 1);
+
+ /* Now we check to see if we eliminated the last remaining member of
+ the first even sequence (and can eliminate the sequence by
+ eliminating its last objectid from oids), and can collapse the
+ first two odd sequences into one sequence. If so, then the net
+ result is to eliminate a pair of objectids from oids. We do this
+ by shifting the entire map to the left. */
+ if (sb_oid_cursize(rs) > 2 && map[1] == map[2]) {
+ memmove (map + 1, map + 3, (sb_oid_cursize(rs) - 3) * sizeof(__u32));
+ set_sb_oid_cursize( rs, sb_oid_cursize(rs) - 2 );
+ }
+
+ journal_mark_dirty(th, s, SB_BUFFER_WITH_SB (s));
+ s->s_dirt = 1;
+ return unused_objectid;
+}
+
+
+/* makes object identifier unused */
+void reiserfs_release_objectid (struct reiserfs_transaction_handle *th,
+ __u32 objectid_to_release)
+{
+ struct super_block * s = th->t_super;
+ struct reiserfs_super_block * rs = SB_DISK_SUPER_BLOCK (s);
+ __u32 * map = objectid_map (s, rs);
+ int i = 0;
+
+ //return;
+ check_objectid_map (s, map);
+
+ reiserfs_prepare_for_journal(s, SB_BUFFER_WITH_SB(s), 1) ;
+ journal_mark_dirty(th, s, SB_BUFFER_WITH_SB (s));
+ s->s_dirt = 1;
+
+
+ /* start at the beginning of the objectid map (i = 0) and go to
+ the end of it (i = disk_sb->s_oid_cursize). Linear search is
+ what we use, though it is possible that binary search would be
+ more efficient after performing lots of deletions (which is
+ when oids is large.) We only check even i's. */
+ while (i < sb_oid_cursize(rs)) {
+ if (objectid_to_release == le32_to_cpu (map[i])) {
+ /* This incrementation unallocates the objectid. */
+ //map[i]++;
+ map[i] = cpu_to_le32 (le32_to_cpu (map[i]) + 1);
+
+ /* Did we unallocate the last member of an odd sequence, and can shrink oids? */
+ if (map[i] == map[i+1]) {
+ /* shrink objectid map */
+ memmove (map + i, map + i + 2,
+ (sb_oid_cursize(rs) - i - 2) * sizeof (__u32));
+ //disk_sb->s_oid_cursize -= 2;
+ set_sb_oid_cursize( rs, sb_oid_cursize(rs) - 2 );
+
+ RFALSE( sb_oid_cursize(rs) < 2 ||
+ sb_oid_cursize(rs) > sb_oid_maxsize(rs),
+ "vs-15005: objectid map corrupted cur_size == %d (max == %d)",
+ sb_oid_cursize(rs), sb_oid_maxsize(rs));
+ }
+ return;
+ }
+
+ if (objectid_to_release > le32_to_cpu (map[i]) &&
+ objectid_to_release < le32_to_cpu (map[i + 1])) {
+ /* size of objectid map is not changed */
+ if (objectid_to_release + 1 == le32_to_cpu (map[i + 1])) {
+ //objectid_map[i+1]--;
+ map[i + 1] = cpu_to_le32 (le32_to_cpu (map[i + 1]) - 1);
+ return;
+ }
+
+ /* JDM comparing two little-endian values for equality -- safe */
+ if (sb_oid_cursize(rs) == sb_oid_maxsize(rs)) {
+ /* objectid map must be expanded, but there is no space */
+ PROC_INFO_INC( s, leaked_oid );
+ return;
+ }
+
+ /* expand the objectid map*/
+ memmove (map + i + 3, map + i + 1,
+ (sb_oid_cursize(rs) - i - 1) * sizeof(__u32));
+ map[i + 1] = cpu_to_le32 (objectid_to_release);
+ map[i + 2] = cpu_to_le32 (objectid_to_release + 1);
+ set_sb_oid_cursize( rs, sb_oid_cursize(rs) + 2 );
+ return;
+ }
+ i += 2;
+ }
+
+ reiserfs_warning (s, "vs-15011: reiserfs_release_objectid: tried to free free object id (%lu)\n",
+ ( long unsigned ) objectid_to_release);
+}
+
+
+int reiserfs_convert_objectid_map_v1(struct super_block *s) {
+ struct reiserfs_super_block *disk_sb = SB_DISK_SUPER_BLOCK (s);
+ int cur_size = sb_oid_cursize(disk_sb);
+ int new_size = (s->s_blocksize - SB_SIZE) / sizeof(__u32) / 2 * 2 ;
+ int old_max = sb_oid_maxsize(disk_sb);
+ struct reiserfs_super_block_v1 *disk_sb_v1 ;
+ __u32 *objectid_map, *new_objectid_map ;
+ int i ;
+
+ disk_sb_v1=(struct reiserfs_super_block_v1 *)(SB_BUFFER_WITH_SB(s)->b_data);
+ objectid_map = (__u32 *)(disk_sb_v1 + 1) ;
+ new_objectid_map = (__u32 *)(disk_sb + 1) ;
+
+ if (cur_size > new_size) {
+ /* mark everyone used that was listed as free at the end of the objectid
+ ** map
+ */
+ objectid_map[new_size - 1] = objectid_map[cur_size - 1] ;
+ set_sb_oid_cursize(disk_sb,new_size) ;
+ }
+ /* move the smaller objectid map past the end of the new super */
+ for (i = new_size - 1 ; i >= 0 ; i--) {
+ objectid_map[i + (old_max - new_size)] = objectid_map[i] ;
+ }
+
+
+ /* set the max size so we don't overflow later */
+ set_sb_oid_maxsize(disk_sb,new_size) ;
+
+ /* Zero out label and generate random UUID */
+ memset(disk_sb->s_label, 0, sizeof(disk_sb->s_label)) ;
+ generate_random_uuid(disk_sb->s_uuid);
+
+ /* finally, zero out the unused chunk of the new super */
+ memset(disk_sb->s_unused, 0, sizeof(disk_sb->s_unused)) ;
+ return 0 ;
+}
+
diff --git a/fs/reiserfs/prints.c b/fs/reiserfs/prints.c
new file mode 100644
index 00000000000000..d54a58174851f4
--- /dev/null
+++ b/fs/reiserfs/prints.c
@@ -0,0 +1,733 @@
+/*
+ * Copyright 2000-2002 by Hans Reiser, licensing governed by reiserfs/README
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/reiserfs_fs.h>
+#include <linux/string.h>
+
+#include <stdarg.h>
+
+static char error_buf[1024];
+static char fmt_buf[1024];
+static char off_buf[80];
+
+
+static char * reiserfs_cpu_offset (struct cpu_key * key)
+{
+ if (cpu_key_k_type(key) == TYPE_DIRENTRY)
+ sprintf (off_buf, "%Lu(%Lu)",
+ (unsigned long long)GET_HASH_VALUE (cpu_key_k_offset (key)),
+ (unsigned long long)GET_GENERATION_NUMBER (cpu_key_k_offset (key)));
+ else
+ sprintf (off_buf, "0x%Lx", (unsigned long long)cpu_key_k_offset (key));
+ return off_buf;
+}
+
+
+static char * le_offset (struct key * key)
+{
+ int version;
+
+ version = le_key_version (key);
+ if (le_key_k_type (version, key) == TYPE_DIRENTRY)
+ sprintf (off_buf, "%Lu(%Lu)",
+ (unsigned long long)GET_HASH_VALUE (le_key_k_offset (version, key)),
+ (unsigned long long)GET_GENERATION_NUMBER (le_key_k_offset (version, key)));
+ else
+ sprintf (off_buf, "0x%Lx", (unsigned long long)le_key_k_offset (version, key));
+ return off_buf;
+}
+
+
+static char * cpu_type (struct cpu_key * key)
+{
+ if (cpu_key_k_type (key) == TYPE_STAT_DATA)
+ return "SD";
+ if (cpu_key_k_type (key) == TYPE_DIRENTRY)
+ return "DIR";
+ if (cpu_key_k_type (key) == TYPE_DIRECT)
+ return "DIRECT";
+ if (cpu_key_k_type (key) == TYPE_INDIRECT)
+ return "IND";
+ return "UNKNOWN";
+}
+
+
+static char * le_type (struct key * key)
+{
+ int version;
+
+ version = le_key_version (key);
+
+ if (le_key_k_type (version, key) == TYPE_STAT_DATA)
+ return "SD";
+ if (le_key_k_type (version, key) == TYPE_DIRENTRY)
+ return "DIR";
+ if (le_key_k_type (version, key) == TYPE_DIRECT)
+ return "DIRECT";
+ if (le_key_k_type (version, key) == TYPE_INDIRECT)
+ return "IND";
+ return "UNKNOWN";
+}
+
+
+/* %k */
+static void sprintf_le_key (char * buf, struct key * key)
+{
+ if (key)
+ sprintf (buf, "[%d %d %s %s]", le32_to_cpu (key->k_dir_id),
+ le32_to_cpu (key->k_objectid), le_offset (key), le_type (key));
+ else
+ sprintf (buf, "[NULL]");
+}
+
+
+/* %K */
+static void sprintf_cpu_key (char * buf, struct cpu_key * key)
+{
+ if (key)
+ sprintf (buf, "[%d %d %s %s]", key->on_disk_key.k_dir_id,
+ key->on_disk_key.k_objectid, reiserfs_cpu_offset (key),
+ cpu_type (key));
+ else
+ sprintf (buf, "[NULL]");
+}
+
+static void sprintf_de_head( char *buf, struct reiserfs_de_head *deh )
+{
+ if( deh )
+ sprintf( buf, "[offset=%d dir_id=%d objectid=%d location=%d state=%04x]", deh_offset(deh), deh_dir_id(deh),
+ deh_objectid(deh), deh_location(deh), deh_state(deh) );
+ else
+ sprintf( buf, "[NULL]" );
+
+}
+
+static void sprintf_item_head (char * buf, struct item_head * ih)
+{
+ if (ih) {
+ sprintf (buf, "%s", (ih_version (ih) == KEY_FORMAT_3_6) ? "*3.6* " : "*3.5*");
+ sprintf_le_key (buf + strlen (buf), &(ih->ih_key));
+ sprintf (buf + strlen (buf), ", item_len %d, item_location %d, "
+ "free_space(entry_count) %d",
+ ih_item_len(ih), ih_location(ih), ih_free_space (ih));
+ } else
+ sprintf (buf, "[NULL]");
+}
+
+
+static void sprintf_direntry (char * buf, struct reiserfs_dir_entry * de)
+{
+ char name[20];
+
+ memcpy (name, de->de_name, de->de_namelen > 19 ? 19 : de->de_namelen);
+ name [de->de_namelen > 19 ? 19 : de->de_namelen] = 0;
+ sprintf (buf, "\"%s\"==>[%d %d]", name, de->de_dir_id, de->de_objectid);
+}
+
+
+static void sprintf_block_head (char * buf, struct buffer_head * bh)
+{
+ sprintf (buf, "level=%d, nr_items=%d, free_space=%d rdkey ",
+ B_LEVEL (bh), B_NR_ITEMS (bh), B_FREE_SPACE (bh));
+}
+
+
+static void sprintf_buffer_head (char * buf, struct buffer_head * bh)
+{
+ sprintf (buf, "dev %s, size %d, blocknr %ld, count %d, list %d, state 0x%lx, page %p, (%s, %s, %s)",
+ kdevname (bh->b_dev), bh->b_size, bh->b_blocknr, atomic_read (&(bh->b_count)), bh->b_list,
+ bh->b_state, bh->b_page,
+ buffer_uptodate (bh) ? "UPTODATE" : "!UPTODATE",
+ buffer_dirty (bh) ? "DIRTY" : "CLEAN",
+ buffer_locked (bh) ? "LOCKED" : "UNLOCKED");
+}
+
+
+static void sprintf_disk_child (char * buf, struct disk_child * dc)
+{
+ sprintf (buf, "[dc_number=%d, dc_size=%u]", dc_block_number(dc), dc_size(dc));
+}
+
+
+static char * is_there_reiserfs_struct (char * fmt, int * what, int * skip)
+{
+ char * k = fmt;
+
+ *skip = 0;
+
+ while ((k = strchr (k, '%')) != NULL)
+ {
+ if (k[1] == 'k' || k[1] == 'K' || k[1] == 'h' || k[1] == 't' ||
+ k[1] == 'z' || k[1] == 'b' || k[1] == 'y' || k[1] == 'a' ) {
+ *what = k[1];
+ break;
+ }
+ (*skip) ++;
+ k ++;
+ }
+ return k;
+}
+
+
+/* debugging reiserfs we used to print out a lot of different
+ variables, like keys, item headers, buffer heads etc. Values of
+ most fields matter. So it took a long time just to write
+ appropriative printk. With this reiserfs_warning you can use format
+ specification for complex structures like you used to do with
+ printfs for integers, doubles and pointers. For instance, to print
+ out key structure you have to write just:
+ reiserfs_warning (NULL, "bad key %k", key);
+ instead of
+ printk ("bad key %lu %lu %lu %lu", key->k_dir_id, key->k_objectid,
+ key->k_offset, key->k_uniqueness);
+ Also if you'd specify a pointer to fs super block as the first argument,
+ device name will be prepended to the output.
+*/
+
+
+static void
+prepare_error_buf( const char *fmt, va_list args )
+{
+ char * fmt1 = fmt_buf;
+ char * k;
+ char * p = error_buf;
+ int i, j, what, skip;
+
+ strcpy (fmt1, fmt);
+
+ while( (k = is_there_reiserfs_struct( fmt1, &what, &skip )) != NULL )
+ {
+ *k = 0;
+
+ p += vsprintf (p, fmt1, args);
+
+ for (i = 0; i < skip; i ++)
+ j = va_arg (args, int);
+
+ switch (what) {
+ case 'k':
+ sprintf_le_key (p, va_arg(args, struct key *));
+ break;
+ case 'K':
+ sprintf_cpu_key (p, va_arg(args, struct cpu_key *));
+ break;
+ case 'h':
+ sprintf_item_head (p, va_arg(args, struct item_head *));
+ break;
+ case 't':
+ sprintf_direntry (p, va_arg(args, struct reiserfs_dir_entry *));
+ break;
+ case 'y':
+ sprintf_disk_child (p, va_arg(args, struct disk_child *));
+ break;
+ case 'z':
+ sprintf_block_head (p, va_arg(args, struct buffer_head *));
+ break;
+ case 'b':
+ sprintf_buffer_head (p, va_arg(args, struct buffer_head *));
+ break;
+ case 'a':
+ sprintf_de_head (p, va_arg(args, struct reiserfs_de_head *));
+ break;
+ }
+
+ p += strlen (p);
+ fmt1 = k + 2;
+ }
+ vsprintf (p, fmt1, args);
+
+}
+
+
+/* in addition to usual conversion specifiers this accepts reiserfs
+ specific conversion specifiers:
+ %k to print little endian key,
+ %K to print cpu key,
+ %h to print item_head,
+ %t to print directory entry
+ %z to print block head (arg must be struct buffer_head *
+ %b to print buffer_head
+*/
+
+#define do_reiserfs_warning(fmt)\
+{\
+ va_list args;\
+ va_start( args, fmt );\
+ prepare_error_buf( fmt, args );\
+ va_end( args );\
+}
+
+void reiserfs_warning (struct super_block * sb, const char * fmt, ...)
+{
+ do_reiserfs_warning(fmt);
+ /* console_print (error_buf); */
+ if (sb)
+ printk (KERN_WARNING "%s:", bdevname(sb->s_dev));
+ else
+ printk (KERN_WARNING);
+
+ printk ("%s", error_buf);
+}
+
+void reiserfs_debug (struct super_block *s, int level, const char * fmt, ...)
+{
+#ifdef CONFIG_REISERFS_CHECK
+ do_reiserfs_warning(fmt);
+ printk (KERN_DEBUG "%s: %s", bdevname(s->s_dev), error_buf);
+#else
+ ;
+#endif
+}
+
+/* The format:
+
+ maintainer-errorid: [function-name:] message
+
+ where errorid is unique to the maintainer and function-name is
+ optional, is recommended, so that anyone can easily find the bug
+ with a simple grep for the short to type string
+ maintainer-errorid. Don't bother with reusing errorids, there are
+ lots of numbers out there.
+
+ Example:
+
+ reiserfs_panic(
+ p_sb, "reiser-29: reiserfs_new_blocknrs: "
+ "one of search_start or rn(%d) is equal to MAX_B_NUM,"
+ "which means that we are optimizing location based on the bogus location of a temp buffer (%p).",
+ rn, bh
+ );
+
+ Regular panic()s sometimes clear the screen before the message can
+ be read, thus the need for the while loop.
+
+ Numbering scheme for panic used by Vladimir and Anatoly( Hans completely ignores this scheme, and considers it
+ pointless complexity):
+
+ panics in reiserfs_fs.h have numbers from 1000 to 1999
+ super.c 2000 to 2999
+ preserve.c (unused) 3000 to 3999
+ bitmap.c 4000 to 4999
+ stree.c 5000 to 5999
+ prints.c 6000 to 6999
+ namei.c 7000 to 7999
+ fix_nodes.c 8000 to 8999
+ dir.c 9000 to 9999
+ lbalance.c 10000 to 10999
+ ibalance.c 11000 to 11999 not ready
+ do_balan.c 12000 to 12999
+ inode.c 13000 to 13999
+ file.c 14000 to 14999
+ objectid.c 15000 - 15999
+ buffer.c 16000 - 16999
+ symlink.c 17000 - 17999
+
+ . */
+
+
+#ifdef CONFIG_REISERFS_CHECK
+extern struct tree_balance * cur_tb;
+#endif
+
+void reiserfs_panic (struct super_block * sb, const char * fmt, ...)
+{
+ show_reiserfs_locks() ;
+ do_reiserfs_warning(fmt);
+ printk ( KERN_EMERG "%s (device %s)\n", error_buf, bdevname(sb->s_dev));
+ BUG ();
+
+ /* this is not actually called, but makes reiserfs_panic() "noreturn" */
+ panic ("REISERFS: panic (device %s): %s\n",
+ sb ? kdevname(sb->s_dev) : "sb == 0", error_buf);
+}
+
+
+void print_virtual_node (struct virtual_node * vn)
+{
+ int i;
+ struct virtual_item * vi;
+
+ printk ("VIRTUAL NODE CONTAINS %d items, has size %d,%s,%s, ITEM_POS=%d POS_IN_ITEM=%d MODE=\'%c\'\n",
+ vn->vn_nr_item, vn->vn_size,
+ (vn->vn_vi[0].vi_type & VI_TYPE_LEFT_MERGEABLE )? "left mergeable" : "",
+ (vn->vn_vi[vn->vn_nr_item - 1].vi_type & VI_TYPE_RIGHT_MERGEABLE) ? "right mergeable" : "",
+ vn->vn_affected_item_num, vn->vn_pos_in_item, vn->vn_mode);
+
+ vi = vn->vn_vi;
+ for (i = 0; i < vn->vn_nr_item; i ++, vi ++)
+ op_print_vi (vi);
+
+}
+
+
+void print_path (struct tree_balance * tb, struct path * path)
+{
+ int h = 0;
+ struct buffer_head * bh;
+
+ if (tb) {
+ while (tb->insert_size[h]) {
+ bh = PATH_H_PBUFFER (path, h);
+ printk ("block %lu (level=%d), position %d\n", bh ? bh->b_blocknr : 0,
+ bh ? B_LEVEL (bh) : 0, PATH_H_POSITION (path, h));
+ h ++;
+ }
+ } else {
+ int offset = path->path_length;
+ struct buffer_head * bh;
+ printk ("Offset Bh (b_blocknr, b_count) Position Nr_item\n");
+ while ( offset > ILLEGAL_PATH_ELEMENT_OFFSET ) {
+ bh = PATH_OFFSET_PBUFFER (path, offset);
+ printk ("%6d %10p (%9lu, %7d) %8d %7d\n", offset,
+ bh, bh ? bh->b_blocknr : 0, bh ? atomic_read (&(bh->b_count)) : 0,
+ PATH_OFFSET_POSITION (path, offset), bh ? B_NR_ITEMS (bh) : -1);
+
+ offset --;
+ }
+ }
+
+}
+
+
+/* this prints internal nodes (4 keys/items in line) (dc_number,
+ dc_size)[k_dirid, k_objectid, k_offset, k_uniqueness](dc_number,
+ dc_size)...*/
+static int print_internal (struct buffer_head * bh, int first, int last)
+{
+ struct key * key;
+ struct disk_child * dc;
+ int i;
+ int from, to;
+
+ if (!B_IS_KEYS_LEVEL (bh))
+ return 1;
+
+ check_internal (bh);
+
+ if (first == -1) {
+ from = 0;
+ to = B_NR_ITEMS (bh);
+ } else {
+ from = first;
+ to = last < B_NR_ITEMS (bh) ? last : B_NR_ITEMS (bh);
+ }
+
+ reiserfs_warning (NULL, "INTERNAL NODE (%ld) contains %z\n", bh->b_blocknr, bh);
+
+ dc = B_N_CHILD (bh, from);
+ reiserfs_warning (NULL, "PTR %d: %y ", from, dc);
+
+ for (i = from, key = B_N_PDELIM_KEY (bh, from), dc ++; i < to; i ++, key ++, dc ++) {
+ reiserfs_warning (NULL, "KEY %d: %k PTR %d: %y ", i, key, i + 1, dc);
+ if (i && i % 4 == 0)
+ printk ("\n");
+ }
+ printk ("\n");
+ return 0;
+}
+
+
+
+
+
+static int print_leaf (struct buffer_head * bh, int print_mode, int first, int last)
+{
+ struct block_head * blkh;
+ struct item_head * ih;
+ int i, nr;
+ int from, to;
+
+ if (!B_IS_ITEMS_LEVEL (bh))
+ return 1;
+
+ check_leaf (bh);
+
+ blkh = B_BLK_HEAD (bh);
+ ih = B_N_PITEM_HEAD (bh,0);
+ nr = blkh_nr_item(blkh);
+
+ printk ("\n===================================================================\n");
+ reiserfs_warning (NULL, "LEAF NODE (%ld) contains %z\n", bh->b_blocknr, bh);
+
+ if (!(print_mode & PRINT_LEAF_ITEMS)) {
+ reiserfs_warning (NULL, "FIRST ITEM_KEY: %k, LAST ITEM KEY: %k\n",
+ &(ih->ih_key), &((ih + nr - 1)->ih_key));
+ return 0;
+ }
+
+ if (first < 0 || first > nr - 1)
+ from = 0;
+ else
+ from = first;
+
+ if (last < 0 || last > nr )
+ to = nr;
+ else
+ to = last;
+
+ ih += from;
+ printk ("-------------------------------------------------------------------------------\n");
+ printk ("|##| type | key | ilen | free_space | version | loc |\n");
+ for (i = from; i < to; i++, ih ++) {
+ printk ("-------------------------------------------------------------------------------\n");
+ reiserfs_warning (NULL, "|%2d| %h |\n", i, ih);
+ if (print_mode & PRINT_LEAF_ITEMS)
+ op_print_item (ih, B_I_PITEM (bh, ih));
+ }
+
+ printk ("===================================================================\n");
+
+ return 0;
+}
+
+char * reiserfs_hashname(int code)
+{
+ if ( code == YURA_HASH)
+ return "rupasov";
+ if ( code == TEA_HASH)
+ return "tea";
+ if ( code == R5_HASH)
+ return "r5";
+
+ return "unknown";
+}
+/* return 1 if this is not super block */
+static int print_super_block (struct buffer_head * bh)
+{
+ struct reiserfs_super_block * rs = (struct reiserfs_super_block *)(bh->b_data);
+ int skipped, data_blocks;
+ char *version;
+
+
+ if (is_reiserfs_3_5(rs)) {
+ version = "3.5";
+ } else if (is_reiserfs_3_6(rs)) {
+ version = "3.6";
+ } else if (is_reiserfs_jr(rs)) {
+ version = ((sb_version(rs) == REISERFS_VERSION_2) ?
+ "3.6" : "3.5");
+ } else {
+ return 1;
+ }
+
+ printk ("%s\'s super block in block %ld\n======================\n",
+ kdevname (bh->b_dev), bh->b_blocknr);
+ printk ("Reiserfs version %s\n", version );
+ printk ("Block count %u\n", sb_block_count(rs));
+ printk ("Blocksize %d\n", sb_blocksize(rs));
+ printk ("Free blocks %u\n", sb_free_blocks(rs));
+ // FIXME: this would be confusing if
+ // someone stores reiserfs super block in some data block ;)
+// skipped = (bh->b_blocknr * bh->b_size) / sb_blocksize(rs);
+ skipped = bh->b_blocknr;
+ data_blocks = sb_block_count(rs) - skipped - 1 - sb_bmap_nr(rs) -
+ (!is_reiserfs_jr(rs) ? sb_jp_journal_size(rs) + 1 : sb_reserved_for_journal(rs)) -
+ sb_free_blocks(rs);
+ printk ("Busy blocks (skipped %d, bitmaps - %d, journal (or reserved) blocks - %d\n"
+ "1 super block, %d data blocks\n",
+ skipped, sb_bmap_nr(rs), (!is_reiserfs_jr(rs) ? (sb_jp_journal_size(rs) + 1) :
+ sb_reserved_for_journal(rs)) , data_blocks);
+ printk ("Root block %u\n", sb_root_block(rs));
+ printk ("Journal block (first) %d\n", sb_jp_journal_1st_block(rs));
+ printk ("Journal dev %d\n", sb_jp_journal_dev(rs));
+ printk ("Journal orig size %d\n", sb_jp_journal_size(rs));
+ printk ("FS state %d\n", sb_fs_state(rs));
+ printk ("Hash function \"%s\"\n",
+ reiserfs_hashname(sb_hash_function_code(rs)));
+
+ printk ("Tree height %d\n", sb_tree_height(rs));
+ return 0;
+}
+
+
+static int print_desc_block (struct buffer_head * bh)
+{
+ struct reiserfs_journal_desc * desc;
+
+ desc = (struct reiserfs_journal_desc *)(bh->b_data);
+ if (memcmp(desc->j_magic, JOURNAL_DESC_MAGIC, 8))
+ return 1;
+
+ printk ("Desc block %lu (j_trans_id %d, j_mount_id %d, j_len %d)",
+ bh->b_blocknr, desc->j_trans_id, desc->j_mount_id, desc->j_len);
+
+ return 0;
+}
+
+
+void print_block (struct buffer_head * bh, ...)//int print_mode, int first, int last)
+{
+ va_list args;
+ int mode, first, last;
+
+ va_start (args, bh);
+
+ if ( ! bh ) {
+ printk("print_block: buffer is NULL\n");
+ return;
+ }
+
+ mode = va_arg (args, int);
+ first = va_arg (args, int);
+ last = va_arg (args, int);
+ if (print_leaf (bh, mode, first, last))
+ if (print_internal (bh, first, last))
+ if (print_super_block (bh))
+ if (print_desc_block (bh))
+ printk ("Block %ld contains unformatted data\n", bh->b_blocknr);
+}
+
+
+
+char print_tb_buf[2048];
+
+/* this stores initial state of tree balance in the print_tb_buf */
+void store_print_tb (struct tree_balance * tb)
+{
+ int h = 0;
+ int i;
+ struct buffer_head * tbSh, * tbFh;
+
+ if (!tb)
+ return;
+
+ sprintf (print_tb_buf, "\n"
+ "BALANCING %d\n"
+ "MODE=%c, ITEM_POS=%d POS_IN_ITEM=%d\n"
+ "=====================================================================\n"
+ "* h * S * L * R * F * FL * FR * CFL * CFR *\n",
+ tb->tb_sb->u.reiserfs_sb.s_do_balance,
+ tb->tb_mode, PATH_LAST_POSITION (tb->tb_path), tb->tb_path->pos_in_item);
+
+ for (h = 0; h < sizeof(tb->insert_size) / sizeof (tb->insert_size[0]); h ++) {
+ if (PATH_H_PATH_OFFSET (tb->tb_path, h) <= tb->tb_path->path_length &&
+ PATH_H_PATH_OFFSET (tb->tb_path, h) > ILLEGAL_PATH_ELEMENT_OFFSET) {
+ tbSh = PATH_H_PBUFFER (tb->tb_path, h);
+ tbFh = PATH_H_PPARENT (tb->tb_path, h);
+ } else {
+ tbSh = 0;
+ tbFh = 0;
+ }
+ sprintf (print_tb_buf + strlen (print_tb_buf),
+ "* %d * %3ld(%2d) * %3ld(%2d) * %3ld(%2d) * %5ld * %5ld * %5ld * %5ld * %5ld *\n",
+ h,
+ (tbSh) ? (tbSh->b_blocknr):(-1),
+ (tbSh) ? atomic_read (&(tbSh->b_count)) : -1,
+ (tb->L[h]) ? (tb->L[h]->b_blocknr):(-1),
+ (tb->L[h]) ? atomic_read (&(tb->L[h]->b_count)) : -1,
+ (tb->R[h]) ? (tb->R[h]->b_blocknr):(-1),
+ (tb->R[h]) ? atomic_read (&(tb->R[h]->b_count)) : -1,
+ (tbFh) ? (tbFh->b_blocknr):(-1),
+ (tb->FL[h]) ? (tb->FL[h]->b_blocknr):(-1),
+ (tb->FR[h]) ? (tb->FR[h]->b_blocknr):(-1),
+ (tb->CFL[h]) ? (tb->CFL[h]->b_blocknr):(-1),
+ (tb->CFR[h]) ? (tb->CFR[h]->b_blocknr):(-1));
+ }
+
+ sprintf (print_tb_buf + strlen (print_tb_buf),
+ "=====================================================================\n"
+ "* h * size * ln * lb * rn * rb * blkn * s0 * s1 * s1b * s2 * s2b * curb * lk * rk *\n"
+ "* 0 * %4d * %2d * %2d * %2d * %2d * %4d * %2d * %2d * %3d * %2d * %3d * %4d * %2d * %2d *\n",
+ tb->insert_size[0], tb->lnum[0], tb->lbytes, tb->rnum[0],tb->rbytes, tb->blknum[0],
+ tb->s0num, tb->s1num,tb->s1bytes, tb->s2num, tb->s2bytes, tb->cur_blknum, tb->lkey[0], tb->rkey[0]);
+
+ /* this prints balance parameters for non-leaf levels */
+ h = 0;
+ do {
+ h++;
+ sprintf (print_tb_buf + strlen (print_tb_buf),
+ "* %d * %4d * %2d * * %2d * * %2d *\n",
+ h, tb->insert_size[h], tb->lnum[h], tb->rnum[h], tb->blknum[h]);
+ } while (tb->insert_size[h]);
+
+ sprintf (print_tb_buf + strlen (print_tb_buf),
+ "=====================================================================\n"
+ "FEB list: ");
+
+ /* print FEB list (list of buffers in form (bh (b_blocknr, b_count), that will be used for new nodes) */
+ h = 0;
+ for (i = 0; i < sizeof (tb->FEB) / sizeof (tb->FEB[0]); i ++)
+ sprintf (print_tb_buf + strlen (print_tb_buf),
+ "%p (%lu %d)%s", tb->FEB[i], tb->FEB[i] ? tb->FEB[i]->b_blocknr : 0,
+ tb->FEB[i] ? atomic_read (&(tb->FEB[i]->b_count)) : 0,
+ (i == sizeof (tb->FEB) / sizeof (tb->FEB[0]) - 1) ? "\n" : ", ");
+
+ sprintf (print_tb_buf + strlen (print_tb_buf),
+ "======================== the end ====================================\n");
+}
+
+void print_cur_tb (char * mes)
+{
+ printk ("%s\n%s", mes, print_tb_buf);
+}
+
+static void check_leaf_block_head (struct buffer_head * bh)
+{
+ struct block_head * blkh;
+ int nr;
+
+ blkh = B_BLK_HEAD (bh);
+ nr = blkh_nr_item(blkh);
+ if ( nr > (bh->b_size - BLKH_SIZE) / IH_SIZE)
+ reiserfs_panic (0, "vs-6010: check_leaf_block_head: invalid item number %z", bh);
+ if ( blkh_free_space(blkh) >
+ bh->b_size - BLKH_SIZE - IH_SIZE * nr )
+ reiserfs_panic (0, "vs-6020: check_leaf_block_head: invalid free space %z", bh);
+
+}
+
+static void check_internal_block_head (struct buffer_head * bh)
+{
+ struct block_head * blkh;
+
+ blkh = B_BLK_HEAD (bh);
+ if (!(B_LEVEL (bh) > DISK_LEAF_NODE_LEVEL && B_LEVEL (bh) <= MAX_HEIGHT))
+ reiserfs_panic (0, "vs-6025: check_internal_block_head: invalid level %z", bh);
+
+ if (B_NR_ITEMS (bh) > (bh->b_size - BLKH_SIZE) / IH_SIZE)
+ reiserfs_panic (0, "vs-6030: check_internal_block_head: invalid item number %z", bh);
+
+ if (B_FREE_SPACE (bh) !=
+ bh->b_size - BLKH_SIZE - KEY_SIZE * B_NR_ITEMS (bh) - DC_SIZE * (B_NR_ITEMS (bh) + 1))
+ reiserfs_panic (0, "vs-6040: check_internal_block_head: invalid free space %z", bh);
+
+}
+
+
+void check_leaf (struct buffer_head * bh)
+{
+ int i;
+ struct item_head * ih;
+
+ if (!bh)
+ return;
+ check_leaf_block_head (bh);
+ for (i = 0, ih = B_N_PITEM_HEAD (bh, 0); i < B_NR_ITEMS (bh); i ++, ih ++)
+ op_check_item (ih, B_I_PITEM (bh, ih));
+}
+
+
+void check_internal (struct buffer_head * bh)
+{
+ if (!bh)
+ return;
+ check_internal_block_head (bh);
+}
+
+
+void print_statistics (struct super_block * s)
+{
+
+ /*
+ printk ("reiserfs_put_super: session statistics: balances %d, fix_nodes %d, \
+bmap with search %d, without %d, dir2ind %d, ind2dir %d\n",
+ s->u.reiserfs_sb.s_do_balance, s->u.reiserfs_sb.s_fix_nodes,
+ s->u.reiserfs_sb.s_bmaps, s->u.reiserfs_sb.s_bmaps_without_search,
+ s->u.reiserfs_sb.s_direct2indirect, s->u.reiserfs_sb.s_indirect2direct);
+ */
+
+}
diff --git a/fs/reiserfs/procfs.c b/fs/reiserfs/procfs.c
new file mode 100644
index 00000000000000..b12a8eac166a45
--- /dev/null
+++ b/fs/reiserfs/procfs.c
@@ -0,0 +1,743 @@
+/* -*- linux-c -*- */
+/* fs/reiserfs/procfs.c */
+/*
+ * Copyright 2000-2002 by Hans Reiser, licensing governed by reiserfs/README
+ */
+
+/* proc info support a la one created by Sizif@Botik.RU for PGC */
+
+/* $Id: procfs.c,v 1.1.8.2 2001/07/15 17:08:42 god Exp $ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <asm/uaccess.h>
+#include <linux/reiserfs_fs.h>
+#include <linux/reiserfs_fs_sb.h>
+#include <linux/smp_lock.h>
+#include <linux/locks.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+
+#if defined( REISERFS_PROC_INFO )
+
+/*
+ * LOCKING:
+ *
+ * We rely on new Alexander Viro's super-block locking.
+ *
+ */
+
+static struct super_block *procinfo_prologue( kdev_t dev )
+{
+ struct super_block *result;
+
+ /* get super-block by device */
+ result = get_super( dev );
+ if( result != NULL ) {
+ if( !reiserfs_is_super( result ) ) {
+ printk( KERN_DEBUG "reiserfs: procfs-52: "
+ "non-reiserfs super found\n" );
+ drop_super( result );
+ result = NULL;
+ }
+ } else
+ printk( KERN_DEBUG "reiserfs: procfs-74: "
+ "race between procinfo and umount\n" );
+ return result;
+}
+
+int procinfo_epilogue( struct super_block *super )
+{
+ drop_super( super );
+ return 0;
+}
+
+int reiserfs_proc_tail( int len, char *buffer, char **start,
+ off_t offset, int count, int *eof )
+{
+ /* this is black procfs magic */
+ if( offset >= len ) {
+ *start = buffer;
+ *eof = 1;
+ return 0;
+ }
+ *start = buffer + offset;
+ if( ( len -= offset ) > count ) {
+ return count;
+ }
+ *eof = 1;
+ return len;
+}
+
+int reiserfs_version_in_proc( char *buffer, char **start, off_t offset,
+ int count, int *eof, void *data )
+{
+ int len = 0;
+ struct super_block *sb;
+ char *format;
+
+ sb = procinfo_prologue( ( kdev_t ) ( long ) data );
+ if( sb == NULL )
+ return -ENOENT;
+ if ( sb->u.reiserfs_sb.s_properties & (1 << REISERFS_3_6) ) {
+ format = "3.6";
+ } else if ( sb->u.reiserfs_sb.s_properties & (1 << REISERFS_3_5) ) {
+ format = "3.5";
+ } else {
+ format = "unknown";
+ }
+ len += sprintf( &buffer[ len ], "%s format\twith checks %s\n",
+ format,
+#if defined( CONFIG_REISERFS_CHECK )
+ "on"
+#else
+ "off"
+#endif
+ );
+ procinfo_epilogue( sb );
+ return reiserfs_proc_tail( len, buffer, start, offset, count, eof );
+}
+
+int reiserfs_global_version_in_proc( char *buffer, char **start, off_t offset,
+ int count, int *eof, void *data )
+{
+ int len = 0;
+ return reiserfs_proc_tail( len, buffer, start, offset, count, eof );
+}
+
+#define SF( x ) ( r -> x )
+#define SFP( x ) SF( s_proc_info_data.x )
+#define SFPL( x ) SFP( x[ level ] )
+#define SFPF( x ) SFP( scan_bitmap.x )
+#define SFPJ( x ) SFP( journal.x )
+
+#define D2C( x ) le16_to_cpu( x )
+#define D4C( x ) le32_to_cpu( x )
+#define DF( x ) D2C( rs -> s_v1.x )
+#define DFL( x ) D4C( rs -> s_v1.x )
+#define DP( x ) D2C( rs -> x )
+#define DPL( x ) D4C( rs -> x )
+
+#define objectid_map( s, rs ) (old_format_only (s) ? \
+ (__u32 *)((struct reiserfs_super_block_v1 *)rs + 1) : \
+ (__u32 *)(rs + 1))
+#define MAP( i ) D4C( objectid_map( sb, rs )[ i ] )
+
+#define DJF( x ) le32_to_cpu( rs -> x )
+#define DJV( x ) le32_to_cpu( s_v1 -> x )
+#define DJP( x ) le32_to_cpu( jp -> x )
+#define JF( x ) ( r -> s_journal -> x )
+
+int reiserfs_super_in_proc( char *buffer, char **start, off_t offset,
+ int count, int *eof, void *data )
+{
+ struct super_block *sb;
+ struct reiserfs_sb_info *r;
+ int len = 0;
+
+ sb = procinfo_prologue( ( kdev_t ) ( long ) data );
+ if( sb == NULL )
+ return -ENOENT;
+ r = &sb->u.reiserfs_sb;
+ len += sprintf( &buffer[ len ],
+ "state: \t%s\n"
+ "mount options: \t%s%s%s%s%s%s%s%s%s%s%s%s\n"
+ "gen. counter: \t%i\n"
+ "s_kmallocs: \t%i\n"
+ "s_disk_reads: \t%i\n"
+ "s_disk_writes: \t%i\n"
+ "s_fix_nodes: \t%i\n"
+ "s_do_balance: \t%i\n"
+ "s_unneeded_left_neighbor: \t%i\n"
+ "s_good_search_by_key_reada: \t%i\n"
+ "s_bmaps: \t%i\n"
+ "s_bmaps_without_search: \t%i\n"
+ "s_direct2indirect: \t%i\n"
+ "s_indirect2direct: \t%i\n"
+ "\n"
+ "max_hash_collisions: \t%i\n"
+
+ "breads: \t%lu\n"
+ "bread_misses: \t%lu\n"
+
+ "search_by_key: \t%lu\n"
+ "search_by_key_fs_changed: \t%lu\n"
+ "search_by_key_restarted: \t%lu\n"
+
+ "insert_item_restarted: \t%lu\n"
+ "paste_into_item_restarted: \t%lu\n"
+ "cut_from_item_restarted: \t%lu\n"
+ "delete_solid_item_restarted: \t%lu\n"
+ "delete_item_restarted: \t%lu\n"
+
+ "leaked_oid: \t%lu\n"
+ "leaves_removable: \t%lu\n",
+
+ SF( s_mount_state ) == REISERFS_VALID_FS ?
+ "REISERFS_VALID_FS" : "REISERFS_ERROR_FS",
+ reiserfs_r5_hash( sb ) ? "FORCE_R5 " : "",
+ reiserfs_rupasov_hash( sb ) ? "FORCE_RUPASOV " : "",
+ reiserfs_tea_hash( sb ) ? "FORCE_TEA " : "",
+ reiserfs_hash_detect( sb ) ? "DETECT_HASH " : "",
+ reiserfs_no_border( sb ) ? "NO_BORDER " : "BORDER ",
+ reiserfs_no_unhashed_relocation( sb ) ? "NO_UNHASHED_RELOCATION " : "",
+ reiserfs_hashed_relocation( sb ) ? "UNHASHED_RELOCATION " : "",
+ reiserfs_test4( sb ) ? "TEST4 " : "",
+ have_large_tails( sb ) ? "TAILS " : have_small_tails(sb)?"SMALL_TAILS ":"NO_TAILS ",
+ replay_only( sb ) ? "REPLAY_ONLY " : "",
+ reiserfs_dont_log( sb ) ? "DONT_LOG " : "LOG ",
+ convert_reiserfs( sb ) ? "CONV " : "",
+
+ atomic_read( &r -> s_generation_counter ),
+ SF( s_kmallocs ),
+ SF( s_disk_reads ),
+ SF( s_disk_writes ),
+ SF( s_fix_nodes ),
+ SF( s_do_balance ),
+ SF( s_unneeded_left_neighbor ),
+ SF( s_good_search_by_key_reada ),
+ SF( s_bmaps ),
+ SF( s_bmaps_without_search ),
+ SF( s_direct2indirect ),
+ SF( s_indirect2direct ),
+ SFP( max_hash_collisions ),
+ SFP( breads ),
+ SFP( bread_miss ),
+ SFP( search_by_key ),
+ SFP( search_by_key_fs_changed ),
+ SFP( search_by_key_restarted ),
+
+ SFP( insert_item_restarted ),
+ SFP( paste_into_item_restarted ),
+ SFP( cut_from_item_restarted ),
+ SFP( delete_solid_item_restarted ),
+ SFP( delete_item_restarted ),
+
+ SFP( leaked_oid ),
+ SFP( leaves_removable ) );
+
+ procinfo_epilogue( sb );
+ return reiserfs_proc_tail( len, buffer, start, offset, count, eof );
+}
+
+int reiserfs_per_level_in_proc( char *buffer, char **start, off_t offset,
+ int count, int *eof, void *data )
+{
+ struct super_block *sb;
+ struct reiserfs_sb_info *r;
+ int len = 0;
+ int level;
+
+ sb = procinfo_prologue( ( kdev_t ) ( long ) data );
+ if( sb == NULL )
+ return -ENOENT;
+ r = &sb->u.reiserfs_sb;
+
+ len += sprintf( &buffer[ len ],
+ "level\t"
+ " balances"
+ " [sbk: reads"
+ " fs_changed"
+ " restarted]"
+ " free space"
+ " items"
+ " can_remove"
+ " lnum"
+ " rnum"
+ " lbytes"
+ " rbytes"
+ " get_neig"
+ " get_neig_res"
+ " need_l_neig"
+ " need_r_neig"
+ "\n"
+
+ );
+
+ for( level = 0 ; level < MAX_HEIGHT ; ++ level ) {
+ if( len > PAGE_SIZE - 240 ) {
+ len += sprintf( &buffer[ len ], "... and more\n" );
+ break;
+ }
+ len += sprintf( &buffer[ len ],
+ "%i\t"
+ " %12lu"
+ " %12lu"
+ " %12lu"
+ " %12lu"
+ " %12lu"
+ " %12lu"
+ " %12lu"
+ " %12li"
+ " %12li"
+ " %12li"
+ " %12li"
+ " %12lu"
+ " %12lu"
+ " %12lu"
+ " %12lu"
+ "\n",
+ level,
+ SFPL( balance_at ),
+ SFPL( sbk_read_at ),
+ SFPL( sbk_fs_changed ),
+ SFPL( sbk_restarted ),
+ SFPL( free_at ),
+ SFPL( items_at ),
+ SFPL( can_node_be_removed ),
+ SFPL( lnum ),
+ SFPL( rnum ),
+ SFPL( lbytes ),
+ SFPL( rbytes ),
+ SFPL( get_neighbors ),
+ SFPL( get_neighbors_restart ),
+ SFPL( need_l_neighbor ),
+ SFPL( need_r_neighbor )
+ );
+ }
+
+ procinfo_epilogue( sb );
+ return reiserfs_proc_tail( len, buffer, start, offset, count, eof );
+}
+
+int reiserfs_bitmap_in_proc( char *buffer, char **start, off_t offset,
+ int count, int *eof, void *data )
+{
+ struct super_block *sb;
+ struct reiserfs_sb_info *r = &sb->u.reiserfs_sb;
+ int len = 0;
+
+ sb = procinfo_prologue( ( kdev_t ) ( long ) data );
+ if( sb == NULL )
+ return -ENOENT;
+ r = &sb->u.reiserfs_sb;
+
+ len += sprintf( &buffer[ len ], "free_block: %lu\n"
+ " scan_bitmap:"
+ " wait"
+ " bmap"
+ " retry"
+ " stolen"
+ " journal_hint"
+ "journal_nohint"
+ "\n"
+ " %14lu"
+ " %14lu"
+ " %14lu"
+ " %14lu"
+ " %14lu"
+ " %14lu"
+ " %14lu"
+ "\n",
+ SFP( free_block ),
+ SFPF( call ),
+ SFPF( wait ),
+ SFPF( bmap ),
+ SFPF( retry ),
+ SFPF( stolen ),
+ SFPF( in_journal_hint ),
+ SFPF( in_journal_nohint ) );
+
+ procinfo_epilogue( sb );
+ return reiserfs_proc_tail( len, buffer, start, offset, count, eof );
+}
+
+int reiserfs_on_disk_super_in_proc( char *buffer, char **start, off_t offset,
+ int count, int *eof, void *data )
+{
+ struct super_block *sb;
+ struct reiserfs_sb_info *sb_info;
+ struct reiserfs_super_block *rs;
+ int hash_code;
+ int len = 0;
+
+ sb = procinfo_prologue( ( kdev_t ) ( long ) data );
+ if( sb == NULL )
+ return -ENOENT;
+ sb_info = &sb->u.reiserfs_sb;
+ rs = sb_info -> s_rs;
+ hash_code = DFL( s_hash_function_code );
+
+ len += sprintf( &buffer[ len ],
+ "block_count: \t%i\n"
+ "free_blocks: \t%i\n"
+ "root_block: \t%i\n"
+ "blocksize: \t%i\n"
+ "oid_maxsize: \t%i\n"
+ "oid_cursize: \t%i\n"
+ "umount_state: \t%i\n"
+ "magic: \t%10.10s\n"
+ "fs_state: \t%i\n"
+ "hash: \t%s\n"
+ "tree_height: \t%i\n"
+ "bmap_nr: \t%i\n"
+ "version: \t%i\n"
+ "reserved_for_journal: \t%i\n"
+ "inode_generation: \t%i\n"
+ "flags: \t%x[%s]\n",
+
+ DFL( s_block_count ),
+ DFL( s_free_blocks ),
+ DFL( s_root_block ),
+ DF( s_blocksize ),
+ DF( s_oid_maxsize ),
+ DF( s_oid_cursize ),
+ DF( s_umount_state ),
+ rs -> s_v1.s_magic,
+ DF( s_fs_state ),
+ hash_code == TEA_HASH ? "tea" :
+ ( hash_code == YURA_HASH ) ? "rupasov" :
+ ( hash_code == R5_HASH ) ? "r5" :
+ ( hash_code == UNSET_HASH ) ? "unset" : "unknown",
+ DF( s_tree_height ),
+ DF( s_bmap_nr ),
+ DF( s_version ),
+ DF( s_reserved_for_journal ),
+ DPL( s_inode_generation ),
+ DPL( s_flags ),
+ (DPL( s_flags ) & reiserfs_attrs_cleared
+ ? "attrs_cleared" : "" ));
+
+ procinfo_epilogue( sb );
+ return reiserfs_proc_tail( len, buffer, start, offset, count, eof );
+}
+
+int reiserfs_oidmap_in_proc( char *buffer, char **start, off_t offset,
+ int count, int *eof, void *data )
+{
+ struct super_block *sb;
+ struct reiserfs_sb_info *sb_info;
+ struct reiserfs_super_block *rs;
+ int i;
+ unsigned int mapsize;
+ unsigned long total_used;
+ int len = 0;
+ int exact;
+
+ sb = procinfo_prologue( ( kdev_t ) ( long ) data );
+ if( sb == NULL )
+ return -ENOENT;
+ sb_info = &sb->u.reiserfs_sb;
+ rs = sb_info -> s_rs;
+ mapsize = le16_to_cpu( rs -> s_v1.s_oid_cursize );
+ total_used = 0;
+
+ for( i = 0 ; i < mapsize ; ++i ) {
+ __u32 right;
+
+ right = ( i == mapsize - 1 ) ? MAX_KEY_OBJECTID : MAP( i + 1 );
+ len += sprintf( &buffer[ len ], "%s: [ %x .. %x )\n",
+ ( i & 1 ) ? "free" : "used", MAP( i ), right );
+ if( ! ( i & 1 ) ) {
+ total_used += right - MAP( i );
+ }
+ if( len > PAGE_SIZE - 100 ) {
+ len += sprintf( &buffer[ len ], "... and more\n" );
+ break;
+ }
+ }
+#if defined( REISERFS_USE_OIDMAPF )
+ if( sb_info -> oidmap.use_file && ( sb_info -> oidmap.mapf != NULL ) ) {
+ loff_t size;
+
+ size = sb_info -> oidmap.mapf -> f_dentry -> d_inode -> i_size;
+ total_used += size / sizeof( reiserfs_oidinterval_d_t );
+ exact = 1;
+ } else
+#endif
+ {
+ exact = ( i == mapsize );
+ }
+ len += sprintf( &buffer[ len ], "total: \t%i [%i/%i] used: %lu [%s]\n",
+ i,
+ mapsize, le16_to_cpu( rs -> s_v1.s_oid_maxsize ),
+ total_used, exact ? "exact" : "estimation" );
+
+ procinfo_epilogue( sb );
+ return reiserfs_proc_tail( len, buffer, start, offset, count, eof );
+}
+
+int reiserfs_journal_in_proc( char *buffer, char **start, off_t offset,
+ int count, int *eof, void *data )
+{
+ struct super_block *sb;
+ struct reiserfs_sb_info *r;
+ struct reiserfs_super_block *rs;
+ struct journal_params *jp;
+ int len = 0;
+
+ sb = procinfo_prologue( ( kdev_t ) ( long ) data );
+ if( sb == NULL )
+ return -ENOENT;
+ r = &sb->u.reiserfs_sb;
+ rs = r -> s_rs;
+ jp = &rs->s_v1.s_journal;
+
+ len += sprintf( &buffer[ len ],
+ /* on-disk fields */
+ "jp_journal_1st_block: \t%i\n"
+ "jp_journal_dev: \t%s[%x]\n"
+ "jp_journal_size: \t%i\n"
+ "jp_journal_trans_max: \t%i\n"
+ "jp_journal_magic: \t%i\n"
+ "jp_journal_max_batch: \t%i\n"
+ "jp_journal_max_commit_age: \t%i\n"
+ "jp_journal_max_trans_age: \t%i\n"
+ /* incore fields */
+ "j_1st_reserved_block: \t%i\n"
+ "j_state: \t%li\n"
+ "j_trans_id: \t%lu\n"
+ "j_mount_id: \t%lu\n"
+ "j_start: \t%lu\n"
+ "j_len: \t%lu\n"
+ "j_len_alloc: \t%lu\n"
+ "j_wcount: \t%i\n"
+ "j_bcount: \t%lu\n"
+ "j_first_unflushed_offset: \t%lu\n"
+ "j_last_flush_trans_id: \t%lu\n"
+ "j_trans_start_time: \t%li\n"
+ "j_journal_list_index: \t%i\n"
+ "j_list_bitmap_index: \t%i\n"
+ "j_must_wait: \t%i\n"
+ "j_next_full_flush: \t%i\n"
+ "j_next_async_flush: \t%i\n"
+ "j_cnode_used: \t%i\n"
+ "j_cnode_free: \t%i\n"
+ "\n"
+ /* reiserfs_proc_info_data_t.journal fields */
+ "in_journal: \t%12lu\n"
+ "in_journal_bitmap: \t%12lu\n"
+ "in_journal_reusable: \t%12lu\n"
+ "lock_journal: \t%12lu\n"
+ "lock_journal_wait: \t%12lu\n"
+ "journal_begin: \t%12lu\n"
+ "journal_relock_writers: \t%12lu\n"
+ "journal_relock_wcount: \t%12lu\n"
+ "mark_dirty: \t%12lu\n"
+ "mark_dirty_already: \t%12lu\n"
+ "mark_dirty_notjournal: \t%12lu\n"
+ "restore_prepared: \t%12lu\n"
+ "prepare: \t%12lu\n"
+ "prepare_retry: \t%12lu\n",
+
+ DJP( jp_journal_1st_block ),
+ DJP( jp_journal_dev ) == 0 ? "none" : bdevname(to_kdev_t(DJP( jp_journal_dev ))),
+ DJP( jp_journal_dev ),
+ DJP( jp_journal_size ),
+ DJP( jp_journal_trans_max ),
+ DJP( jp_journal_magic ),
+ DJP( jp_journal_max_batch ),
+ DJP( jp_journal_max_commit_age ),
+ DJP( jp_journal_max_trans_age ),
+
+ JF( j_1st_reserved_block ),
+ JF( j_state ),
+ JF( j_trans_id ),
+ JF( j_mount_id ),
+ JF( j_start ),
+ JF( j_len ),
+ JF( j_len_alloc ),
+ atomic_read( & r -> s_journal -> j_wcount ),
+ JF( j_bcount ),
+ JF( j_first_unflushed_offset ),
+ JF( j_last_flush_trans_id ),
+ JF( j_trans_start_time ),
+ JF( j_journal_list_index ),
+ JF( j_list_bitmap_index ),
+ JF( j_must_wait ),
+ JF( j_next_full_flush ),
+ JF( j_next_async_flush ),
+ JF( j_cnode_used ),
+ JF( j_cnode_free ),
+
+ SFPJ( in_journal ),
+ SFPJ( in_journal_bitmap ),
+ SFPJ( in_journal_reusable ),
+ SFPJ( lock_journal ),
+ SFPJ( lock_journal_wait ),
+ SFPJ( journal_being ),
+ SFPJ( journal_relock_writers ),
+ SFPJ( journal_relock_wcount ),
+ SFPJ( mark_dirty ),
+ SFPJ( mark_dirty_already ),
+ SFPJ( mark_dirty_notjournal ),
+ SFPJ( restore_prepared ),
+ SFPJ( prepare ),
+ SFPJ( prepare_retry )
+ );
+
+ procinfo_epilogue( sb );
+ return reiserfs_proc_tail( len, buffer, start, offset, count, eof );
+}
+
+
+static struct proc_dir_entry *proc_info_root = NULL;
+static const char *proc_info_root_name = "fs/reiserfs";
+
+int reiserfs_proc_info_init( struct super_block *sb )
+{
+ spin_lock_init( & __PINFO( sb ).lock );
+ sb->u.reiserfs_sb.procdir = proc_mkdir( bdevname( sb -> s_dev ),
+ proc_info_root );
+ if( sb->u.reiserfs_sb.procdir ) {
+ sb->u.reiserfs_sb.procdir -> owner = THIS_MODULE;
+ return 0;
+ }
+ reiserfs_warning( sb, "reiserfs: cannot create /proc/%s/%s\n",
+ proc_info_root_name, bdevname( sb -> s_dev ) );
+ return 1;
+}
+
+
+int reiserfs_proc_info_done( struct super_block *sb )
+{
+ spin_lock( & __PINFO( sb ).lock );
+ __PINFO( sb ).exiting = 1;
+ spin_unlock( & __PINFO( sb ).lock );
+ if ( proc_info_root ) {
+ remove_proc_entry( bdevname( sb -> s_dev ), proc_info_root );
+ sb->u.reiserfs_sb.procdir = NULL;
+ }
+ return 0;
+}
+
+/* Create /proc/fs/reiserfs/DEV/name and attach read procedure @func
+ to it. Other parts of reiserfs use this function to make their
+ per-device statistics available via /proc */
+
+struct proc_dir_entry *reiserfs_proc_register( struct super_block *sb,
+ char *name, read_proc_t *func )
+{
+ return ( sb->u.reiserfs_sb.procdir ) ? create_proc_read_entry
+ ( name, 0, sb->u.reiserfs_sb.procdir, func,
+ ( void * ) ( long ) sb -> s_dev ) : NULL;
+}
+
+void reiserfs_proc_unregister( struct super_block *sb, const char *name )
+{
+ remove_proc_entry( name, sb->u.reiserfs_sb.procdir );
+}
+
+struct proc_dir_entry *reiserfs_proc_register_global( char *name,
+ read_proc_t *func )
+{
+ return ( proc_info_root ) ? create_proc_read_entry( name, 0,
+ proc_info_root,
+ func, NULL ) : NULL;
+}
+
+void reiserfs_proc_unregister_global( const char *name )
+{
+ remove_proc_entry( name, proc_info_root );
+}
+
+int reiserfs_proc_info_global_init( void )
+{
+ if( proc_info_root == NULL ) {
+ proc_info_root = proc_mkdir( proc_info_root_name, 0 );
+ if( proc_info_root ) {
+ proc_info_root -> owner = THIS_MODULE;
+ } else {
+ reiserfs_warning( NULL, "reiserfs: cannot create /proc/%s\n",
+ proc_info_root_name );
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int reiserfs_proc_info_global_done( void )
+{
+ if ( proc_info_root != NULL ) {
+ proc_info_root = NULL;
+ remove_proc_entry( proc_info_root_name, 0 );
+ }
+ return 0;
+}
+
+/* REISERFS_PROC_INFO */
+#else
+
+int reiserfs_proc_info_init( struct super_block *sb ) { return 0; }
+int reiserfs_proc_info_done( struct super_block *sb ) { return 0; }
+
+struct proc_dir_entry *reiserfs_proc_register( struct super_block *sb,
+ char *name,
+ read_proc_t *func )
+{ return NULL; }
+
+void reiserfs_proc_unregister( struct super_block *sb, const char *name )
+{;}
+
+struct proc_dir_entry *reiserfs_proc_register_global( char *name,
+ read_proc_t *func )
+{ return NULL; }
+
+void reiserfs_proc_unregister_global( const char *name ) {;}
+
+int reiserfs_proc_info_global_init( void ) { return 0; }
+int reiserfs_proc_info_global_done( void ) { return 0; }
+
+int reiserfs_global_version_in_proc( char *buffer, char **start,
+ off_t offset,
+ int count, int *eof, void *data )
+{ return 0; }
+
+int reiserfs_version_in_proc( char *buffer, char **start, off_t offset,
+ int count, int *eof, void *data )
+{ return 0; }
+
+int reiserfs_super_in_proc( char *buffer, char **start, off_t offset,
+ int count, int *eof, void *data )
+{ return 0; }
+
+int reiserfs_per_level_in_proc( char *buffer, char **start, off_t offset,
+ int count, int *eof, void *data )
+{ return 0; }
+
+int reiserfs_bitmap_in_proc( char *buffer, char **start, off_t offset,
+ int count, int *eof, void *data )
+{ return 0; }
+
+int reiserfs_on_disk_super_in_proc( char *buffer, char **start, off_t offset,
+ int count, int *eof, void *data )
+{ return 0; }
+
+int reiserfs_oidmap_in_proc( char *buffer, char **start, off_t offset,
+ int count, int *eof, void *data )
+{ return 0; }
+
+int reiserfs_journal_in_proc( char *buffer, char **start, off_t offset,
+ int count, int *eof, void *data )
+{ return 0; }
+
+/* REISERFS_PROC_INFO */
+#endif
+
+/*
+ * $Log: procfs.c,v $
+ * Revision 1.1.8.2 2001/07/15 17:08:42 god
+ * . use get_super() in procfs.c
+ * . remove remove_save_link() from reiserfs_do_truncate()
+ *
+ * I accept terms and conditions stated in the Legal Agreement
+ * (available at http://www.namesys.com/legalese.html)
+ *
+ * Revision 1.1.8.1 2001/07/11 16:48:50 god
+ * proc info support
+ *
+ * I accept terms and conditions stated in the Legal Agreement
+ * (available at http://www.namesys.com/legalese.html)
+ *
+ */
+
+/*
+ * Make Linus happy.
+ * Local variables:
+ * c-indentation-style: "K&R"
+ * mode-name: "LC"
+ * c-basic-offset: 8
+ * tab-width: 8
+ * End:
+ */
diff --git a/fs/reiserfs/resize.c b/fs/reiserfs/resize.c
new file mode 100644
index 00000000000000..89aecc9d00c17a
--- /dev/null
+++ b/fs/reiserfs/resize.c
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2000-2002 by Hans Reiser, licensing governed by reiserfs/README
+ */
+
+/*
+ * Written by Alexander Zarochentcev.
+ *
+ * The kernel part of the (on-line) reiserfs resizer.
+ */
+
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/locks.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/reiserfs_fs.h>
+#include <linux/reiserfs_fs_sb.h>
+
+int reiserfs_resize (struct super_block * s, unsigned long block_count_new)
+{
+ struct reiserfs_super_block * sb;
+ struct reiserfs_bitmap_info *bitmap;
+ struct buffer_head * bh;
+ struct reiserfs_transaction_handle th;
+ unsigned int bmap_nr_new, bmap_nr;
+ unsigned int block_r_new, block_r;
+
+ struct reiserfs_list_bitmap * jb;
+ struct reiserfs_list_bitmap jbitmap[JOURNAL_NUM_BITMAPS];
+
+ unsigned long int block_count, free_blocks;
+ int i;
+ int copy_size ;
+
+ sb = SB_DISK_SUPER_BLOCK(s);
+
+ if (SB_BLOCK_COUNT(s) >= block_count_new) {
+ reiserfs_warning(s, "can\'t shrink filesystem on-line\n");
+ return -EINVAL;
+ }
+
+ /* check the device size */
+ bh = sb_bread(s, block_count_new - 1);
+ if (!bh) {
+ reiserfs_warning(s, "reiserfs_resize: can\'t read last block\n");
+ return -EINVAL;
+ }
+ bforget(bh);
+
+ /* old disk layout detection; those partitions can be mounted, but
+ * cannot be resized */
+ if (SB_BUFFER_WITH_SB(s)->b_blocknr * SB_BUFFER_WITH_SB(s)->b_size
+ != REISERFS_DISK_OFFSET_IN_BYTES ) {
+ reiserfs_warning(s, "reiserfs_resize: unable to resize a reiserfs without distributed bitmap (fs version < 3.5.12)\n");
+ return -ENOTSUPP;
+ }
+
+ /* count used bits in last bitmap block */
+ block_r = SB_BLOCK_COUNT(s) -
+ (SB_BMAP_NR(s) - 1) * s->s_blocksize * 8;
+
+ /* count bitmap blocks in new fs */
+ bmap_nr_new = block_count_new / ( s->s_blocksize * 8 );
+ block_r_new = block_count_new - bmap_nr_new * s->s_blocksize * 8;
+ if (block_r_new)
+ bmap_nr_new++;
+ else
+ block_r_new = s->s_blocksize * 8;
+
+ /* save old values */
+ block_count = SB_BLOCK_COUNT(s);
+ bmap_nr = SB_BMAP_NR(s);
+
+ /* resizing of reiserfs bitmaps (journal and real), if needed */
+ if (bmap_nr_new > bmap_nr) {
+ /* reallocate journal bitmaps */
+ if (reiserfs_allocate_list_bitmaps(s, jbitmap, bmap_nr_new) < 0) {
+ reiserfs_warning(s, "reiserfs_resize: unable to allocate memory for journal bitmaps\n");
+ unlock_super(s) ;
+ return -ENOMEM ;
+ }
+ /* the new journal bitmaps are zero filled, now we copy in the bitmap
+ ** node pointers from the old journal bitmap structs, and then
+ ** transfer the new data structures into the journal struct.
+ **
+ ** using the copy_size var below allows this code to work for
+ ** both shrinking and expanding the FS.
+ */
+ copy_size = bmap_nr_new < bmap_nr ? bmap_nr_new : bmap_nr ;
+ copy_size = copy_size * sizeof(struct reiserfs_list_bitmap_node *) ;
+ for (i = 0 ; i < JOURNAL_NUM_BITMAPS ; i++) {
+ struct reiserfs_bitmap_node **node_tmp ;
+ jb = SB_JOURNAL(s)->j_list_bitmap + i ;
+ memcpy(jbitmap[i].bitmaps, jb->bitmaps, copy_size) ;
+
+ /* just in case vfree schedules on us, copy the new
+ ** pointer into the journal struct before freeing the
+ ** old one
+ */
+ node_tmp = jb->bitmaps ;
+ jb->bitmaps = jbitmap[i].bitmaps ;
+ vfree(node_tmp) ;
+ }
+
+ /* allocate additional bitmap blocks, reallocate array of bitmap
+ * block pointers */
+ bitmap = vmalloc(sizeof(struct reiserfs_bitmap_info) * bmap_nr_new);
+ if (!bitmap) {
+ reiserfs_warning(s, "reiserfs_resize: unable to allocate memory.\n");
+ return -ENOMEM;
+ }
+ for (i = 0; i < bmap_nr; i++)
+ bitmap[i] = SB_AP_BITMAP(s)[i];
+ for (i = bmap_nr; i < bmap_nr_new; i++) {
+ bitmap[i].bh = sb_getblk(s, i * s->s_blocksize * 8);
+ memset(bitmap[i].bh->b_data, 0, sb_blocksize(sb));
+ reiserfs_test_and_set_le_bit(0, bitmap[i].bh->b_data);
+
+ mark_buffer_dirty(bitmap[i].bh) ;
+ mark_buffer_uptodate(bitmap[i].bh, 1);
+ ll_rw_block(WRITE, 1, &bitmap[i].bh);
+ wait_on_buffer(bitmap[i].bh);
+ bitmap[i].first_zero_hint=1;
+ bitmap[i].free_count = s->s_blocksize * 8 - 1;
+ }
+ /* free old bitmap blocks array */
+ vfree(SB_AP_BITMAP(s));
+
+ SB_AP_BITMAP(s) = bitmap;
+ }
+
+ /* begin transaction */
+ journal_begin(&th, s, 10);
+
+ /* correct last bitmap blocks in old and new disk layout */
+ reiserfs_prepare_for_journal(s, SB_AP_BITMAP(s)[bmap_nr - 1].bh, 1);
+ for (i = block_r; i < s->s_blocksize * 8; i++)
+ reiserfs_test_and_clear_le_bit(i,
+ SB_AP_BITMAP(s)[bmap_nr - 1].bh->b_data);
+ SB_AP_BITMAP(s)[bmap_nr - 1].free_count += s->s_blocksize * 8 - block_r;
+ if ( !SB_AP_BITMAP(s)[bmap_nr - 1].first_zero_hint)
+ SB_AP_BITMAP(s)[bmap_nr - 1].first_zero_hint = block_r;
+ journal_mark_dirty(&th, s, SB_AP_BITMAP(s)[bmap_nr - 1].bh);
+
+ reiserfs_prepare_for_journal(s, SB_AP_BITMAP(s)[bmap_nr_new - 1].bh, 1);
+ for (i = block_r_new; i < s->s_blocksize * 8; i++)
+ reiserfs_test_and_set_le_bit(i,
+ SB_AP_BITMAP(s)[bmap_nr_new - 1].bh->b_data);
+ journal_mark_dirty(&th, s, SB_AP_BITMAP(s)[bmap_nr_new - 1].bh);
+
+ SB_AP_BITMAP(s)[bmap_nr_new - 1].free_count -= s->s_blocksize * 8 - block_r_new;
+ /* Extreme case where last bitmap is the only valid block in itself. */
+ if ( !SB_AP_BITMAP(s)[bmap_nr_new - 1].free_count )
+ SB_AP_BITMAP(s)[bmap_nr_new - 1].first_zero_hint = 0;
+ /* update super */
+ reiserfs_prepare_for_journal(s, SB_BUFFER_WITH_SB(s), 1) ;
+ free_blocks = SB_FREE_BLOCKS(s);
+ PUT_SB_FREE_BLOCKS(s, free_blocks + (block_count_new - block_count - (bmap_nr_new - bmap_nr)));
+ PUT_SB_BLOCK_COUNT(s, block_count_new);
+ PUT_SB_BMAP_NR(s, bmap_nr_new);
+ s->s_dirt = 1;
+
+ journal_mark_dirty(&th, s, SB_BUFFER_WITH_SB(s));
+
+ SB_JOURNAL(s)->j_must_wait = 1;
+ journal_end(&th, s, 10);
+
+ return 0;
+}
+
diff --git a/fs/reiserfs/stree.c b/fs/reiserfs/stree.c
new file mode 100644
index 00000000000000..82042be0787a15
--- /dev/null
+++ b/fs/reiserfs/stree.c
@@ -0,0 +1,1921 @@
+/*
+ * Copyright 2000-2002 by Hans Reiser, licensing governed by reiserfs/README
+ */
+
+/*
+ * Written by Anatoly P. Pinchuk pap@namesys.botik.ru
+ * Programm System Institute
+ * Pereslavl-Zalessky Russia
+ */
+
+/*
+ * This file contains functions dealing with S+tree
+ *
+ * B_IS_IN_TREE
+ * copy_short_key
+ * copy_item_head
+ * comp_short_keys
+ * comp_keys
+ * comp_cpu_keys
+ * comp_short_le_keys
+ * comp_short_cpu_keys
+ * cpu_key2cpu_key
+ * le_key2cpu_key
+ * comp_le_keys
+ * bin_search
+ * get_lkey
+ * get_rkey
+ * key_in_buffer
+ * decrement_bcount
+ * decrement_counters_in_path
+ * reiserfs_check_path
+ * pathrelse_and_restore
+ * pathrelse
+ * search_by_key_reada
+ * search_by_key
+ * search_for_position_by_key
+ * comp_items
+ * prepare_for_direct_item
+ * prepare_for_direntry_item
+ * prepare_for_delete_or_cut
+ * calc_deleted_bytes_number
+ * init_tb_struct
+ * padd_item
+ * reiserfs_delete_item
+ * reiserfs_delete_solid_item
+ * reiserfs_delete_object
+ * maybe_indirect_to_direct
+ * indirect_to_direct_roll_back
+ * reiserfs_cut_from_item
+ * truncate_directory
+ * reiserfs_do_truncate
+ * reiserfs_paste_into_item
+ * reiserfs_insert_item
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/pagemap.h>
+#include <linux/reiserfs_fs.h>
+#include <linux/smp_lock.h>
+
+/* Does the buffer contain a disk block which is in the tree. */
+inline int B_IS_IN_TREE (const struct buffer_head * p_s_bh)
+{
+
+ RFALSE( B_LEVEL (p_s_bh) > MAX_HEIGHT,
+ "PAP-1010: block (%b) has too big level (%z)", p_s_bh, p_s_bh);
+
+ return ( B_LEVEL (p_s_bh) != FREE_LEVEL );
+}
+
+
+
+
+inline void copy_short_key (void * to, const void * from)
+{
+ memcpy (to, from, SHORT_KEY_SIZE);
+}
+
+//
+// to gets item head in le form
+//
+inline void copy_item_head(struct item_head * p_v_to,
+ const struct item_head * p_v_from)
+{
+ memcpy (p_v_to, p_v_from, IH_SIZE);
+}
+
+
+/* k1 is pointer to on-disk structure which is stored in little-endian
+ form. k2 is pointer to cpu variable. For key of items of the same
+ object this returns 0.
+ Returns: -1 if key1 < key2
+ 0 if key1 == key2
+ 1 if key1 > key2 */
+inline int comp_short_keys (const struct key * le_key,
+ const struct cpu_key * cpu_key)
+{
+ __u32 * p_s_le_u32, * p_s_cpu_u32;
+ int n_key_length = REISERFS_SHORT_KEY_LEN;
+
+ p_s_le_u32 = (__u32 *)le_key;
+ p_s_cpu_u32 = (__u32 *)cpu_key;
+ for( ; n_key_length--; ++p_s_le_u32, ++p_s_cpu_u32 ) {
+ if ( le32_to_cpu (*p_s_le_u32) < *p_s_cpu_u32 )
+ return -1;
+ if ( le32_to_cpu (*p_s_le_u32) > *p_s_cpu_u32 )
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/* k1 is pointer to on-disk structure which is stored in little-endian
+ form. k2 is pointer to cpu variable.
+ Compare keys using all 4 key fields.
+ Returns: -1 if key1 < key2 0
+ if key1 = key2 1 if key1 > key2 */
+inline int comp_keys (const struct key * le_key, const struct cpu_key * cpu_key)
+{
+ int retval;
+
+ retval = comp_short_keys (le_key, cpu_key);
+ if (retval)
+ return retval;
+ if (le_key_k_offset (le_key_version(le_key), le_key) < cpu_key_k_offset (cpu_key))
+ return -1;
+ if (le_key_k_offset (le_key_version(le_key), le_key) > cpu_key_k_offset (cpu_key))
+ return 1;
+
+ if (cpu_key->key_length == 3)
+ return 0;
+
+ /* this part is needed only when tail conversion is in progress */
+ if (le_key_k_type (le_key_version(le_key), le_key) < cpu_key_k_type (cpu_key))
+ return -1;
+
+ if (le_key_k_type (le_key_version(le_key), le_key) > cpu_key_k_type (cpu_key))
+ return 1;
+
+ return 0;
+}
+
+
+//
+// FIXME: not used yet
+//
+inline int comp_cpu_keys (const struct cpu_key * key1,
+ const struct cpu_key * key2)
+{
+ if (key1->on_disk_key.k_dir_id < key2->on_disk_key.k_dir_id)
+ return -1;
+ if (key1->on_disk_key.k_dir_id > key2->on_disk_key.k_dir_id)
+ return 1;
+
+ if (key1->on_disk_key.k_objectid < key2->on_disk_key.k_objectid)
+ return -1;
+ if (key1->on_disk_key.k_objectid > key2->on_disk_key.k_objectid)
+ return 1;
+
+ if (cpu_key_k_offset (key1) < cpu_key_k_offset (key2))
+ return -1;
+ if (cpu_key_k_offset (key1) > cpu_key_k_offset (key2))
+ return 1;
+
+ reiserfs_warning (NULL, "comp_cpu_keys: type are compared for %K and %K\n",
+ key1, key2);
+
+ if (cpu_key_k_type (key1) < cpu_key_k_type (key2))
+ return -1;
+ if (cpu_key_k_type (key1) > cpu_key_k_type (key2))
+ return 1;
+ return 0;
+}
+
+inline int comp_short_le_keys (const struct key * key1, const struct key * key2)
+{
+ __u32 * p_s_1_u32, * p_s_2_u32;
+ int n_key_length = REISERFS_SHORT_KEY_LEN;
+
+ p_s_1_u32 = (__u32 *)key1;
+ p_s_2_u32 = (__u32 *)key2;
+ for( ; n_key_length--; ++p_s_1_u32, ++p_s_2_u32 ) {
+ if ( le32_to_cpu (*p_s_1_u32) < le32_to_cpu (*p_s_2_u32) )
+ return -1;
+ if ( le32_to_cpu (*p_s_1_u32) > le32_to_cpu (*p_s_2_u32) )
+ return 1;
+ }
+ return 0;
+}
+
+inline int comp_short_cpu_keys (const struct cpu_key * key1,
+ const struct cpu_key * key2)
+{
+ __u32 * p_s_1_u32, * p_s_2_u32;
+ int n_key_length = REISERFS_SHORT_KEY_LEN;
+
+ p_s_1_u32 = (__u32 *)key1;
+ p_s_2_u32 = (__u32 *)key2;
+
+ for( ; n_key_length--; ++p_s_1_u32, ++p_s_2_u32 ) {
+ if ( *p_s_1_u32 < *p_s_2_u32 )
+ return -1;
+ if ( *p_s_1_u32 > *p_s_2_u32 )
+ return 1;
+ }
+ return 0;
+}
+
+
+
+inline void cpu_key2cpu_key (struct cpu_key * to, const struct cpu_key * from)
+{
+ memcpy (to, from, sizeof (struct cpu_key));
+}
+
+
+inline void le_key2cpu_key (struct cpu_key * to, const struct key * from)
+{
+ to->on_disk_key.k_dir_id = le32_to_cpu (from->k_dir_id);
+ to->on_disk_key.k_objectid = le32_to_cpu (from->k_objectid);
+
+ // find out version of the key
+ to->version = le_key_version (from);
+ if (to->version == KEY_FORMAT_3_5) {
+ to->on_disk_key.u.k_offset_v1.k_offset = le32_to_cpu (from->u.k_offset_v1.k_offset);
+ to->on_disk_key.u.k_offset_v1.k_uniqueness = le32_to_cpu (from->u.k_offset_v1.k_uniqueness);
+ } else {
+ to->on_disk_key.u.k_offset_v2.k_offset = offset_v2_k_offset(&from->u.k_offset_v2);
+ to->on_disk_key.u.k_offset_v2.k_type = offset_v2_k_type(&from->u.k_offset_v2);
+ }
+}
+
+
+
+// this does not say which one is bigger, it only returns 1 if keys
+// are not equal, 0 otherwise
+inline int comp_le_keys (const struct key * k1, const struct key * k2)
+{
+ return memcmp (k1, k2, sizeof (struct key));
+}
+
+/**************************************************************************
+ * Binary search toolkit function *
+ * Search for an item in the array by the item key *
+ * Returns: 1 if found, 0 if not found; *
+ * *p_n_pos = number of the searched element if found, else the *
+ * number of the first element that is larger than p_v_key. *
+ **************************************************************************/
+/* For those not familiar with binary search: n_lbound is the leftmost item that it
+ could be, n_rbound the rightmost item that it could be. We examine the item
+ halfway between n_lbound and n_rbound, and that tells us either that we can increase
+ n_lbound, or decrease n_rbound, or that we have found it, or if n_lbound <= n_rbound that
+ there are no possible items, and we have not found it. With each examination we
+ cut the number of possible items it could be by one more than half rounded down,
+ or we find it. */
+inline int bin_search (
+ const void * p_v_key, /* Key to search for. */
+ const void * p_v_base,/* First item in the array. */
+ int p_n_num, /* Number of items in the array. */
+ int p_n_width, /* Item size in the array.
+ searched. Lest the reader be
+ confused, note that this is crafted
+ as a general function, and when it
+ is applied specifically to the array
+ of item headers in a node, p_n_width
+ is actually the item header size not
+ the item size. */
+ int * p_n_pos /* Number of the searched for element. */
+ ) {
+ int n_rbound, n_lbound, n_j;
+
+ for ( n_j = ((n_rbound = p_n_num - 1) + (n_lbound = 0))/2; n_lbound <= n_rbound; n_j = (n_rbound + n_lbound)/2 )
+ switch( COMP_KEYS((struct key *)((char * )p_v_base + n_j * p_n_width), (struct cpu_key *)p_v_key) ) {
+ case -1: n_lbound = n_j + 1; continue;
+ case 1: n_rbound = n_j - 1; continue;
+ case 0: *p_n_pos = n_j; return ITEM_FOUND; /* Key found in the array. */
+ }
+
+ /* bin_search did not find given key, it returns position of key,
+ that is minimal and greater than the given one. */
+ *p_n_pos = n_lbound;
+ return ITEM_NOT_FOUND;
+}
+
+#ifdef CONFIG_REISERFS_CHECK
+extern struct tree_balance * cur_tb;
+#endif
+
+
+
+/* Minimal possible key. It is never in the tree. */
+const struct key MIN_KEY = {0, 0, {{0, 0},}};
+
+/* Maximal possible key. It is never in the tree. */
+const struct key MAX_KEY = {0xffffffff, 0xffffffff, {{0xffffffff, 0xffffffff},}};
+
+
+/* Get delimiting key of the buffer by looking for it in the buffers in the path, starting from the bottom
+ of the path, and going upwards. We must check the path's validity at each step. If the key is not in
+ the path, there is no delimiting key in the tree (buffer is first or last buffer in tree), and in this
+ case we return a special key, either MIN_KEY or MAX_KEY. */
+inline const struct key * get_lkey (
+ const struct path * p_s_chk_path,
+ const struct super_block * p_s_sb
+ ) {
+ int n_position, n_path_offset = p_s_chk_path->path_length;
+ struct buffer_head * p_s_parent;
+
+ RFALSE( n_path_offset < FIRST_PATH_ELEMENT_OFFSET,
+ "PAP-5010: illegal offset in the path");
+
+ /* While not higher in path than first element. */
+ while ( n_path_offset-- > FIRST_PATH_ELEMENT_OFFSET ) {
+
+ RFALSE( ! buffer_uptodate(PATH_OFFSET_PBUFFER(p_s_chk_path, n_path_offset)),
+ "PAP-5020: parent is not uptodate");
+
+ /* Parent at the path is not in the tree now. */
+ if ( ! B_IS_IN_TREE(p_s_parent = PATH_OFFSET_PBUFFER(p_s_chk_path, n_path_offset)) )
+ return &MAX_KEY;
+ /* Check whether position in the parent is correct. */
+ if ( (n_position = PATH_OFFSET_POSITION(p_s_chk_path, n_path_offset)) > B_NR_ITEMS(p_s_parent) )
+ return &MAX_KEY;
+ /* Check whether parent at the path really points to the child. */
+ if ( B_N_CHILD_NUM(p_s_parent, n_position) !=
+ PATH_OFFSET_PBUFFER(p_s_chk_path, n_path_offset + 1)->b_blocknr )
+ return &MAX_KEY;
+ /* Return delimiting key if position in the parent is not equal to zero. */
+ if ( n_position )
+ return B_N_PDELIM_KEY(p_s_parent, n_position - 1);
+ }
+ /* Return MIN_KEY if we are in the root of the buffer tree. */
+ if ( PATH_OFFSET_PBUFFER(p_s_chk_path, FIRST_PATH_ELEMENT_OFFSET)->b_blocknr ==
+ SB_ROOT_BLOCK (p_s_sb) )
+ return &MIN_KEY;
+ return &MAX_KEY;
+}
+
+
+/* Get delimiting key of the buffer at the path and its right neighbor. */
+inline const struct key * get_rkey (
+ const struct path * p_s_chk_path,
+ const struct super_block * p_s_sb
+ ) {
+ int n_position,
+ n_path_offset = p_s_chk_path->path_length;
+ struct buffer_head * p_s_parent;
+
+ RFALSE( n_path_offset < FIRST_PATH_ELEMENT_OFFSET,
+ "PAP-5030: illegal offset in the path");
+
+ while ( n_path_offset-- > FIRST_PATH_ELEMENT_OFFSET ) {
+
+ RFALSE( ! buffer_uptodate(PATH_OFFSET_PBUFFER(p_s_chk_path, n_path_offset)),
+ "PAP-5040: parent is not uptodate");
+
+ /* Parent at the path is not in the tree now. */
+ if ( ! B_IS_IN_TREE(p_s_parent = PATH_OFFSET_PBUFFER(p_s_chk_path, n_path_offset)) )
+ return &MIN_KEY;
+ /* Check whether position in the parent is correct. */
+ if ( (n_position = PATH_OFFSET_POSITION(p_s_chk_path, n_path_offset)) > B_NR_ITEMS(p_s_parent) )
+ return &MIN_KEY;
+ /* Check whether parent at the path really points to the child. */
+ if ( B_N_CHILD_NUM(p_s_parent, n_position) !=
+ PATH_OFFSET_PBUFFER(p_s_chk_path, n_path_offset + 1)->b_blocknr )
+ return &MIN_KEY;
+ /* Return delimiting key if position in the parent is not the last one. */
+ if ( n_position != B_NR_ITEMS(p_s_parent) )
+ return B_N_PDELIM_KEY(p_s_parent, n_position);
+ }
+ /* Return MAX_KEY if we are in the root of the buffer tree. */
+ if ( PATH_OFFSET_PBUFFER(p_s_chk_path, FIRST_PATH_ELEMENT_OFFSET)->b_blocknr ==
+ SB_ROOT_BLOCK (p_s_sb) )
+ return &MAX_KEY;
+ return &MIN_KEY;
+}
+
+
+/* Check whether a key is contained in the tree rooted from a buffer at a path. */
+/* This works by looking at the left and right delimiting keys for the buffer in the last path_element in
+ the path. These delimiting keys are stored at least one level above that buffer in the tree. If the
+ buffer is the first or last node in the tree order then one of the delimiting keys may be absent, and in
+ this case get_lkey and get_rkey return a special key which is MIN_KEY or MAX_KEY. */
+static inline int key_in_buffer (
+ struct path * p_s_chk_path, /* Path which should be checked. */
+ const struct cpu_key * p_s_key, /* Key which should be checked. */
+ struct super_block * p_s_sb /* Super block pointer. */
+ ) {
+
+ RFALSE( ! p_s_key || p_s_chk_path->path_length < FIRST_PATH_ELEMENT_OFFSET ||
+ p_s_chk_path->path_length > MAX_HEIGHT,
+ "PAP-5050: pointer to the key(%p) is NULL or illegal path length(%d)",
+ p_s_key, p_s_chk_path->path_length);
+ RFALSE( PATH_PLAST_BUFFER(p_s_chk_path)->b_dev == NODEV,
+ "PAP-5060: device must not be NODEV");
+
+ if ( COMP_KEYS(get_lkey(p_s_chk_path, p_s_sb), p_s_key) == 1 )
+ /* left delimiting key is bigger, that the key we look for */
+ return 0;
+ // if ( COMP_KEYS(p_s_key, get_rkey(p_s_chk_path, p_s_sb)) != -1 )
+ if ( COMP_KEYS(get_rkey(p_s_chk_path, p_s_sb), p_s_key) != 1 )
+ /* p_s_key must be less than right delimitiing key */
+ return 0;
+ return 1;
+}
+
+
+inline void decrement_bcount(
+ struct buffer_head * p_s_bh
+ ) {
+ if ( p_s_bh ) {
+ if ( atomic_read (&(p_s_bh->b_count)) ) {
+ put_bh(p_s_bh) ;
+ return;
+ }
+ reiserfs_panic(NULL, "PAP-5070: decrement_bcount: trying to free free buffer %b", p_s_bh);
+ }
+}
+
+
+/* Decrement b_count field of the all buffers in the path. */
+void decrement_counters_in_path (
+ struct path * p_s_search_path
+ ) {
+ int n_path_offset = p_s_search_path->path_length;
+
+ RFALSE( n_path_offset < ILLEGAL_PATH_ELEMENT_OFFSET ||
+ n_path_offset > EXTENDED_MAX_HEIGHT - 1,
+ "PAP-5080: illegal path offset of %d", n_path_offset);
+
+ while ( n_path_offset > ILLEGAL_PATH_ELEMENT_OFFSET ) {
+ struct buffer_head * bh;
+
+ bh = PATH_OFFSET_PBUFFER(p_s_search_path, n_path_offset--);
+ decrement_bcount (bh);
+ }
+ p_s_search_path->path_length = ILLEGAL_PATH_ELEMENT_OFFSET;
+}
+
+
+int reiserfs_check_path(struct path *p) {
+ RFALSE( p->path_length != ILLEGAL_PATH_ELEMENT_OFFSET,
+ "path not properly relsed") ;
+ return 0 ;
+}
+
+
+/* Release all buffers in the path. Restore dirty bits clean
+** when preparing the buffer for the log
+**
+** only called from fix_nodes()
+*/
+void pathrelse_and_restore (
+ struct super_block *s,
+ struct path * p_s_search_path
+ ) {
+ int n_path_offset = p_s_search_path->path_length;
+
+ RFALSE( n_path_offset < ILLEGAL_PATH_ELEMENT_OFFSET,
+ "clm-4000: illegal path offset");
+
+ while ( n_path_offset > ILLEGAL_PATH_ELEMENT_OFFSET ) {
+ reiserfs_restore_prepared_buffer(s, PATH_OFFSET_PBUFFER(p_s_search_path,
+ n_path_offset));
+ brelse(PATH_OFFSET_PBUFFER(p_s_search_path, n_path_offset--));
+ }
+ p_s_search_path->path_length = ILLEGAL_PATH_ELEMENT_OFFSET;
+}
+
+/* Release all buffers in the path. */
+void pathrelse (
+ struct path * p_s_search_path
+ ) {
+ int n_path_offset = p_s_search_path->path_length;
+
+ RFALSE( n_path_offset < ILLEGAL_PATH_ELEMENT_OFFSET,
+ "PAP-5090: illegal path offset");
+
+ while ( n_path_offset > ILLEGAL_PATH_ELEMENT_OFFSET )
+ brelse(PATH_OFFSET_PBUFFER(p_s_search_path, n_path_offset--));
+
+ p_s_search_path->path_length = ILLEGAL_PATH_ELEMENT_OFFSET;
+}
+
+
+
+static int is_leaf (char * buf, int blocksize, struct buffer_head * bh)
+{
+ struct block_head * blkh;
+ struct item_head * ih;
+ int used_space;
+ int prev_location;
+ int i;
+ int nr;
+
+ blkh = (struct block_head *)buf;
+ if ( blkh_level(blkh) != DISK_LEAF_NODE_LEVEL) {
+ printk ("is_leaf: this should be caught earlier\n");
+ return 0;
+ }
+
+ nr = blkh_nr_item(blkh);
+ if (nr < 1 || nr > ((blocksize - BLKH_SIZE) / (IH_SIZE + MIN_ITEM_LEN))) {
+ /* item number is too big or too small */
+ reiserfs_warning (NULL, "is_leaf: nr_item seems wrong: %z\n", bh);
+ return 0;
+ }
+ ih = (struct item_head *)(buf + BLKH_SIZE) + nr - 1;
+ used_space = BLKH_SIZE + IH_SIZE * nr + (blocksize - ih_location (ih));
+ if (used_space != blocksize - blkh_free_space(blkh)) {
+ /* free space does not match to calculated amount of use space */
+ reiserfs_warning (NULL, "is_leaf: free space seems wrong: %z\n", bh);
+ return 0;
+ }
+
+ // FIXME: it is_leaf will hit performance too much - we may have
+ // return 1 here
+
+ /* check tables of item heads */
+ ih = (struct item_head *)(buf + BLKH_SIZE);
+ prev_location = blocksize;
+ for (i = 0; i < nr; i ++, ih ++) {
+ if ( le_ih_k_type(ih) == TYPE_ANY) {
+ reiserfs_warning (NULL, "is_leaf: wrong item type for item %h\n",ih);
+ return 0;
+ }
+ if (ih_location (ih) >= blocksize || ih_location (ih) < IH_SIZE * nr) {
+ reiserfs_warning (NULL, "is_leaf: item location seems wrong: %h\n", ih);
+ return 0;
+ }
+ if (ih_item_len (ih) < 1 || ih_item_len (ih) > MAX_ITEM_LEN (blocksize)) {
+ reiserfs_warning (NULL, "is_leaf: item length seems wrong: %h\n", ih);
+ return 0;
+ }
+ if (prev_location - ih_location (ih) != ih_item_len (ih)) {
+ reiserfs_warning (NULL, "is_leaf: item location seems wrong (second one): %h\n", ih);
+ return 0;
+ }
+ prev_location = ih_location (ih);
+ }
+
+ // one may imagine much more checks
+ return 1;
+}
+
+
+/* returns 1 if buf looks like an internal node, 0 otherwise */
+static int is_internal (char * buf, int blocksize, struct buffer_head * bh)
+{
+ struct block_head * blkh;
+ int nr;
+ int used_space;
+
+ blkh = (struct block_head *)buf;
+ nr = blkh_level(blkh);
+ if (nr <= DISK_LEAF_NODE_LEVEL || nr > MAX_HEIGHT) {
+ /* this level is not possible for internal nodes */
+ printk ("is_internal: this should be caught earlier\n");
+ return 0;
+ }
+
+ nr = blkh_nr_item(blkh);
+ if (nr > (blocksize - BLKH_SIZE - DC_SIZE) / (KEY_SIZE + DC_SIZE)) {
+ /* for internal which is not root we might check min number of keys */
+ reiserfs_warning (NULL, "is_internal: number of key seems wrong: %z\n", bh);
+ return 0;
+ }
+
+ used_space = BLKH_SIZE + KEY_SIZE * nr + DC_SIZE * (nr + 1);
+ if (used_space != blocksize - blkh_free_space(blkh)) {
+ reiserfs_warning (NULL, "is_internal: free space seems wrong: %z\n", bh);
+ return 0;
+ }
+
+ // one may imagine much more checks
+ return 1;
+}
+
+
+// make sure that bh contains formatted node of reiserfs tree of
+// 'level'-th level
+static int is_tree_node (struct buffer_head * bh, int level)
+{
+ if (B_LEVEL (bh) != level) {
+ printk ("is_tree_node: node level %d does not match to the expected one %d\n",
+ B_LEVEL (bh), level);
+ return 0;
+ }
+ if (level == DISK_LEAF_NODE_LEVEL)
+ return is_leaf (bh->b_data, bh->b_size, bh);
+
+ return is_internal (bh->b_data, bh->b_size, bh);
+}
+
+
+
+#ifdef SEARCH_BY_KEY_READA
+
+/* The function is NOT SCHEDULE-SAFE! */
+static void search_by_key_reada (struct super_block * s, int blocknr)
+{
+ struct buffer_head * bh;
+
+ if (blocknr == 0)
+ return;
+
+ bh = getblk (s->s_dev, blocknr, s->s_blocksize);
+
+ if (!buffer_uptodate (bh)) {
+ ll_rw_block (READA, 1, &bh);
+ }
+ bh->b_count --;
+}
+
+#endif
+
+/**************************************************************************
+ * Algorithm SearchByKey *
+ * look for item in the Disk S+Tree by its key *
+ * Input: p_s_sb - super block *
+ * p_s_key - pointer to the key to search *
+ * Output: ITEM_FOUND, ITEM_NOT_FOUND or IO_ERROR *
+ * p_s_search_path - path from the root to the needed leaf *
+ **************************************************************************/
+
+/* This function fills up the path from the root to the leaf as it
+ descends the tree looking for the key. It uses reiserfs_bread to
+ try to find buffers in the cache given their block number. If it
+ does not find them in the cache it reads them from disk. For each
+ node search_by_key finds using reiserfs_bread it then uses
+ bin_search to look through that node. bin_search will find the
+ position of the block_number of the next node if it is looking
+ through an internal node. If it is looking through a leaf node
+ bin_search will find the position of the item which has key either
+ equal to given key, or which is the maximal key less than the given
+ key. search_by_key returns a path that must be checked for the
+ correctness of the top of the path but need not be checked for the
+ correctness of the bottom of the path */
+/* The function is NOT SCHEDULE-SAFE! */
+int search_by_key (struct super_block * p_s_sb,
+ const struct cpu_key * p_s_key, /* Key to search. */
+ struct path * p_s_search_path, /* This structure was
+ allocated and initialized
+ by the calling
+ function. It is filled up
+ by this function. */
+ int n_stop_level /* How far down the tree to search. To
+ stop at leaf level - set to
+ DISK_LEAF_NODE_LEVEL */
+ ) {
+ int n_block_number = SB_ROOT_BLOCK (p_s_sb),
+ expected_level = SB_TREE_HEIGHT (p_s_sb),
+ n_block_size = p_s_sb->s_blocksize;
+ struct buffer_head * p_s_bh;
+ struct path_element * p_s_last_element;
+ int n_node_level, n_retval;
+ int right_neighbor_of_leaf_node;
+ int fs_gen;
+
+#ifdef CONFIG_REISERFS_CHECK
+ int n_repeat_counter = 0;
+#endif
+
+ PROC_INFO_INC( p_s_sb, search_by_key );
+
+ /* As we add each node to a path we increase its count. This means that
+ we must be careful to release all nodes in a path before we either
+ discard the path struct or re-use the path struct, as we do here. */
+
+ decrement_counters_in_path(p_s_search_path);
+
+ right_neighbor_of_leaf_node = 0;
+
+ /* With each iteration of this loop we search through the items in the
+ current node, and calculate the next current node(next path element)
+ for the next iteration of this loop.. */
+ while ( 1 ) {
+
+#ifdef CONFIG_REISERFS_CHECK
+ if ( !(++n_repeat_counter % 50000) )
+ reiserfs_warning (p_s_sb, "PAP-5100: search_by_key: %s:"
+ "there were %d iterations of while loop "
+ "looking for key %K\n",
+ current->comm, n_repeat_counter, p_s_key);
+#endif
+
+ /* prep path to have another element added to it. */
+ p_s_last_element = PATH_OFFSET_PELEMENT(p_s_search_path, ++p_s_search_path->path_length);
+ fs_gen = get_generation (p_s_sb);
+ expected_level --;
+
+#ifdef SEARCH_BY_KEY_READA
+ /* schedule read of right neighbor */
+ search_by_key_reada (p_s_sb, right_neighbor_of_leaf_node);
+#endif
+
+ /* Read the next tree node, and set the last element in the path to
+ have a pointer to it. */
+ if ( ! (p_s_bh = p_s_last_element->pe_buffer =
+ reiserfs_bread(p_s_sb, n_block_number, n_block_size)) ) {
+ p_s_search_path->path_length --;
+ pathrelse(p_s_search_path);
+ return IO_ERROR;
+ }
+
+ if( fs_changed (fs_gen, p_s_sb) ) {
+ PROC_INFO_INC( p_s_sb, search_by_key_fs_changed );
+ PROC_INFO_INC( p_s_sb, sbk_fs_changed[ expected_level - 1 ] );
+ }
+
+ /* It is possible that schedule occurred. We must check whether the key
+ to search is still in the tree rooted from the current buffer. If
+ not then repeat search from the root. */
+ if ( fs_changed (fs_gen, p_s_sb) &&
+ (!B_IS_IN_TREE (p_s_bh) || !key_in_buffer(p_s_search_path, p_s_key, p_s_sb)) ) {
+ PROC_INFO_INC( p_s_sb, search_by_key_restarted );
+ PROC_INFO_INC( p_s_sb, sbk_restarted[ expected_level - 1 ] );
+ decrement_counters_in_path(p_s_search_path);
+
+ /* Get the root block number so that we can repeat the search
+ starting from the root. */
+ n_block_number = SB_ROOT_BLOCK (p_s_sb);
+ expected_level = SB_TREE_HEIGHT (p_s_sb);
+ right_neighbor_of_leaf_node = 0;
+
+ /* repeat search from the root */
+ continue;
+ }
+
+ /* only check that the key is in the buffer if p_s_key is not
+ equal to the MAX_KEY. Latter case is only possible in
+ "finish_unfinished()" processing during mount. */
+ RFALSE( COMP_KEYS( &MAX_KEY, p_s_key ) &&
+ ! key_in_buffer(p_s_search_path, p_s_key, p_s_sb),
+ "PAP-5130: key is not in the buffer");
+#ifdef CONFIG_REISERFS_CHECK
+ if ( cur_tb ) {
+ print_cur_tb ("5140");
+ reiserfs_panic(p_s_sb, "PAP-5140: search_by_key: schedule occurred in do_balance!");
+ }
+#endif
+
+ // make sure, that the node contents look like a node of
+ // certain level
+ if (!is_tree_node (p_s_bh, expected_level)) {
+ reiserfs_warning (p_s_sb, "vs-5150: search_by_key: "
+ "invalid format found in block %ld. Fsck?\n",
+ p_s_bh->b_blocknr);
+ pathrelse (p_s_search_path);
+ return IO_ERROR;
+ }
+
+ /* ok, we have acquired next formatted node in the tree */
+ n_node_level = B_LEVEL (p_s_bh);
+
+ PROC_INFO_BH_STAT( p_s_sb, p_s_bh, n_node_level - 1 );
+
+ RFALSE( n_node_level < n_stop_level,
+ "vs-5152: tree level (%d) is less than stop level (%d)",
+ n_node_level, n_stop_level);
+
+ n_retval = bin_search( p_s_key, B_N_PITEM_HEAD(p_s_bh, 0),
+ B_NR_ITEMS(p_s_bh),
+ ( n_node_level == DISK_LEAF_NODE_LEVEL ) ? IH_SIZE : KEY_SIZE,
+ &(p_s_last_element->pe_position));
+ if (n_node_level == n_stop_level) {
+ return n_retval;
+ }
+
+ /* we are not in the stop level */
+ if (n_retval == ITEM_FOUND)
+ /* item has been found, so we choose the pointer which is to the right of the found one */
+ p_s_last_element->pe_position++;
+
+ /* if item was not found we choose the position which is to
+ the left of the found item. This requires no code,
+ bin_search did it already.*/
+
+ /* So we have chosen a position in the current node which is
+ an internal node. Now we calculate child block number by
+ position in the node. */
+ n_block_number = B_N_CHILD_NUM(p_s_bh, p_s_last_element->pe_position);
+
+#ifdef SEARCH_BY_KEY_READA
+ /* if we are going to read leaf node, then calculate its right neighbor if possible */
+ if (n_node_level == DISK_LEAF_NODE_LEVEL + 1 && p_s_last_element->pe_position < B_NR_ITEMS (p_s_bh))
+ right_neighbor_of_leaf_node = B_N_CHILD_NUM(p_s_bh, p_s_last_element->pe_position + 1);
+#endif
+ }
+}
+
+
+/* Form the path to an item and position in this item which contains
+ file byte defined by p_s_key. If there is no such item
+ corresponding to the key, we point the path to the item with
+ maximal key less than p_s_key, and *p_n_pos_in_item is set to one
+ past the last entry/byte in the item. If searching for entry in a
+ directory item, and it is not found, *p_n_pos_in_item is set to one
+ entry more than the entry with maximal key which is less than the
+ sought key.
+
+ Note that if there is no entry in this same node which is one more,
+ then we point to an imaginary entry. for direct items, the
+ position is in units of bytes, for indirect items the position is
+ in units of blocknr entries, for directory items the position is in
+ units of directory entries. */
+
+/* The function is NOT SCHEDULE-SAFE! */
+int search_for_position_by_key (struct super_block * p_s_sb, /* Pointer to the super block. */
+ const struct cpu_key * p_cpu_key, /* Key to search (cpu variable) */
+ struct path * p_s_search_path /* Filled up by this function. */
+ ) {
+ struct item_head * p_le_ih; /* pointer to on-disk structure */
+ int n_blk_size;
+ loff_t item_offset, offset;
+ struct reiserfs_dir_entry de;
+ int retval;
+
+ /* If searching for directory entry. */
+ if ( is_direntry_cpu_key (p_cpu_key) )
+ return search_by_entry_key (p_s_sb, p_cpu_key, p_s_search_path, &de);
+
+ /* If not searching for directory entry. */
+
+ /* If item is found. */
+ retval = search_item (p_s_sb, p_cpu_key, p_s_search_path);
+ if (retval == IO_ERROR)
+ return retval;
+ if ( retval == ITEM_FOUND ) {
+
+ RFALSE( ! ih_item_len(
+ B_N_PITEM_HEAD(PATH_PLAST_BUFFER(p_s_search_path),
+ PATH_LAST_POSITION(p_s_search_path))),
+ "PAP-5165: item length equals zero");
+
+ pos_in_item(p_s_search_path) = 0;
+ return POSITION_FOUND;
+ }
+
+ RFALSE( ! PATH_LAST_POSITION(p_s_search_path),
+ "PAP-5170: position equals zero");
+
+ /* Item is not found. Set path to the previous item. */
+ p_le_ih = B_N_PITEM_HEAD(PATH_PLAST_BUFFER(p_s_search_path), --PATH_LAST_POSITION(p_s_search_path));
+ n_blk_size = p_s_sb->s_blocksize;
+
+ if (comp_short_keys (&(p_le_ih->ih_key), p_cpu_key)) {
+ return FILE_NOT_FOUND;
+ }
+
+ // FIXME: quite ugly this far
+
+ item_offset = le_ih_k_offset (p_le_ih);
+ offset = cpu_key_k_offset (p_cpu_key);
+
+ /* Needed byte is contained in the item pointed to by the path.*/
+ if (item_offset <= offset &&
+ item_offset + op_bytes_number (p_le_ih, n_blk_size) > offset) {
+ pos_in_item (p_s_search_path) = offset - item_offset;
+ if ( is_indirect_le_ih(p_le_ih) ) {
+ pos_in_item (p_s_search_path) /= n_blk_size;
+ }
+ return POSITION_FOUND;
+ }
+
+ /* Needed byte is not contained in the item pointed to by the
+ path. Set pos_in_item out of the item. */
+ if ( is_indirect_le_ih (p_le_ih) )
+ pos_in_item (p_s_search_path) = ih_item_len(p_le_ih) / UNFM_P_SIZE;
+ else
+ pos_in_item (p_s_search_path) = ih_item_len( p_le_ih );
+
+ return POSITION_NOT_FOUND;
+}
+
+
+/* Compare given item and item pointed to by the path. */
+int comp_items (const struct item_head * stored_ih, const struct path * p_s_path)
+{
+ struct buffer_head * p_s_bh;
+ struct item_head * ih;
+
+ /* Last buffer at the path is not in the tree. */
+ if ( ! B_IS_IN_TREE(p_s_bh = PATH_PLAST_BUFFER(p_s_path)) )
+ return 1;
+
+ /* Last path position is invalid. */
+ if ( PATH_LAST_POSITION(p_s_path) >= B_NR_ITEMS(p_s_bh) )
+ return 1;
+
+ /* we need only to know, whether it is the same item */
+ ih = get_ih (p_s_path);
+ return memcmp (stored_ih, ih, IH_SIZE);
+}
+
+
+/* unformatted nodes are not logged anymore, ever. This is safe
+** now
+*/
+#define held_by_others(bh) (atomic_read(&(bh)->b_count) > 1)
+
+// block can not be forgotten as it is in I/O or held by someone
+#define block_in_use(bh) (buffer_locked(bh) || (held_by_others(bh)))
+
+
+
+// prepare for delete or cut of direct item
+static inline int prepare_for_direct_item (struct path * path,
+ struct item_head * le_ih,
+ struct inode * inode,
+ loff_t new_file_length,
+ int * cut_size)
+{
+ loff_t round_len;
+
+
+ if ( new_file_length == max_reiserfs_offset (inode) ) {
+ /* item has to be deleted */
+ *cut_size = -(IH_SIZE + ih_item_len(le_ih));
+ return M_DELETE;
+ }
+
+ // new file gets truncated
+ if (get_inode_item_key_version (inode) == KEY_FORMAT_3_6) {
+ //
+ round_len = ROUND_UP (new_file_length);
+ /* this was n_new_file_length < le_ih ... */
+ if ( round_len < le_ih_k_offset (le_ih) ) {
+ *cut_size = -(IH_SIZE + ih_item_len(le_ih));
+ return M_DELETE; /* Delete this item. */
+ }
+ /* Calculate first position and size for cutting from item. */
+ pos_in_item (path) = round_len - (le_ih_k_offset (le_ih) - 1);
+ *cut_size = -(ih_item_len(le_ih) - pos_in_item(path));
+
+ return M_CUT; /* Cut from this item. */
+ }
+
+
+ // old file: items may have any length
+
+ if ( new_file_length < le_ih_k_offset (le_ih) ) {
+ *cut_size = -(IH_SIZE + ih_item_len(le_ih));
+ return M_DELETE; /* Delete this item. */
+ }
+ /* Calculate first position and size for cutting from item. */
+ *cut_size = -(ih_item_len(le_ih) -
+ (pos_in_item (path) = new_file_length + 1 - le_ih_k_offset (le_ih)));
+ return M_CUT; /* Cut from this item. */
+}
+
+
+static inline int prepare_for_direntry_item (struct path * path,
+ struct item_head * le_ih,
+ struct inode * inode,
+ loff_t new_file_length,
+ int * cut_size)
+{
+ if (le_ih_k_offset (le_ih) == DOT_OFFSET &&
+ new_file_length == max_reiserfs_offset (inode)) {
+ RFALSE( ih_entry_count (le_ih) != 2,
+ "PAP-5220: incorrect empty directory item (%h)", le_ih);
+ *cut_size = -(IH_SIZE + ih_item_len(le_ih));
+ return M_DELETE; /* Delete the directory item containing "." and ".." entry. */
+ }
+
+ if ( ih_entry_count (le_ih) == 1 ) {
+ /* Delete the directory item such as there is one record only
+ in this item*/
+ *cut_size = -(IH_SIZE + ih_item_len(le_ih));
+ return M_DELETE;
+ }
+
+ /* Cut one record from the directory item. */
+ *cut_size = -(DEH_SIZE + entry_length (get_last_bh (path), le_ih, pos_in_item (path)));
+ return M_CUT;
+}
+
+
+/* If the path points to a directory or direct item, calculate mode and the size cut, for balance.
+ If the path points to an indirect item, remove some number of its unformatted nodes.
+ In case of file truncate calculate whether this item must be deleted/truncated or last
+ unformatted node of this item will be converted to a direct item.
+ This function returns a determination of what balance mode the calling function should employ. */
+static char prepare_for_delete_or_cut(
+ struct reiserfs_transaction_handle *th,
+ struct inode * inode,
+ struct path * p_s_path,
+ const struct cpu_key * p_s_item_key,
+ int * p_n_removed, /* Number of unformatted nodes which were removed
+ from end of the file. */
+ int * p_n_cut_size,
+ unsigned long long n_new_file_length /* MAX_KEY_OFFSET in case of delete. */
+ ) {
+ struct super_block * p_s_sb = inode->i_sb;
+ struct item_head * p_le_ih = PATH_PITEM_HEAD(p_s_path);
+ struct buffer_head * p_s_bh = PATH_PLAST_BUFFER(p_s_path);
+
+ /* Stat_data item. */
+ if ( is_statdata_le_ih (p_le_ih) ) {
+
+ RFALSE( n_new_file_length != max_reiserfs_offset (inode),
+ "PAP-5210: mode must be M_DELETE");
+
+ *p_n_cut_size = -(IH_SIZE + ih_item_len(p_le_ih));
+ return M_DELETE;
+ }
+
+
+ /* Directory item. */
+ if ( is_direntry_le_ih (p_le_ih) )
+ return prepare_for_direntry_item (p_s_path, p_le_ih, inode, n_new_file_length, p_n_cut_size);
+
+ /* Direct item. */
+ if ( is_direct_le_ih (p_le_ih) )
+ return prepare_for_direct_item (p_s_path, p_le_ih, inode, n_new_file_length, p_n_cut_size);
+
+
+ /* Case of an indirect item. */
+ {
+ int n_unfm_number, /* Number of the item unformatted nodes. */
+ n_counter,
+ n_blk_size;
+ __u32 * p_n_unfm_pointer; /* Pointer to the unformatted node number. */
+ __u32 tmp;
+ struct item_head s_ih; /* Item header. */
+ char c_mode; /* Returned mode of the balance. */
+ int need_research;
+
+
+ n_blk_size = p_s_sb->s_blocksize;
+
+ /* Search for the needed object indirect item until there are no unformatted nodes to be removed. */
+ do {
+ need_research = 0;
+ p_s_bh = PATH_PLAST_BUFFER(p_s_path);
+ /* Copy indirect item header to a temp variable. */
+ copy_item_head(&s_ih, PATH_PITEM_HEAD(p_s_path));
+ /* Calculate number of unformatted nodes in this item. */
+ n_unfm_number = I_UNFM_NUM(&s_ih);
+
+ RFALSE( ! is_indirect_le_ih(&s_ih) || ! n_unfm_number ||
+ pos_in_item (p_s_path) + 1 != n_unfm_number,
+ "PAP-5240: illegal item %h "
+ "n_unfm_number = %d *p_n_pos_in_item = %d",
+ &s_ih, n_unfm_number, pos_in_item (p_s_path));
+
+ /* Calculate balance mode and position in the item to remove unformatted nodes. */
+ if ( n_new_file_length == max_reiserfs_offset (inode) ) {/* Case of delete. */
+ pos_in_item (p_s_path) = 0;
+ *p_n_cut_size = -(IH_SIZE + ih_item_len(&s_ih));
+ c_mode = M_DELETE;
+ }
+ else { /* Case of truncate. */
+ if ( n_new_file_length < le_ih_k_offset (&s_ih) ) {
+ pos_in_item (p_s_path) = 0;
+ *p_n_cut_size = -(IH_SIZE + ih_item_len(&s_ih));
+ c_mode = M_DELETE; /* Delete this item. */
+ }
+ else {
+ /* indirect item must be truncated starting from *p_n_pos_in_item-th position */
+ pos_in_item (p_s_path) = (n_new_file_length + n_blk_size - le_ih_k_offset (&s_ih) ) >> p_s_sb->s_blocksize_bits;
+
+ RFALSE( pos_in_item (p_s_path) > n_unfm_number,
+ "PAP-5250: illegal position in the item");
+
+ /* Either convert last unformatted node of indirect item to direct item or increase
+ its free space. */
+ if ( pos_in_item (p_s_path) == n_unfm_number ) {
+ *p_n_cut_size = 0; /* Nothing to cut. */
+ return M_CONVERT; /* Maybe convert last unformatted node to the direct item. */
+ }
+ /* Calculate size to cut. */
+ *p_n_cut_size = -(ih_item_len(&s_ih) - pos_in_item(p_s_path) * UNFM_P_SIZE);
+
+ c_mode = M_CUT; /* Cut from this indirect item. */
+ }
+ }
+
+ RFALSE( n_unfm_number <= pos_in_item (p_s_path),
+ "PAP-5260: illegal position in the indirect item");
+
+ /* pointers to be cut */
+ n_unfm_number -= pos_in_item (p_s_path);
+ /* Set pointer to the last unformatted node pointer that is to be cut. */
+ p_n_unfm_pointer = (__u32 *)B_I_PITEM(p_s_bh, &s_ih) + I_UNFM_NUM(&s_ih) - 1 - *p_n_removed;
+
+
+ /* We go through the unformatted nodes pointers of the indirect
+ item and look for the unformatted nodes in the cache. If we
+ found some of them we free it, zero corresponding indirect item
+ entry and log buffer containing that indirect item. For this we
+ need to prepare last path element for logging. If some
+ unformatted node has b_count > 1 we must not free this
+ unformatted node since it is in use. */
+ reiserfs_prepare_for_journal(p_s_sb, p_s_bh, 1);
+ // note: path could be changed, first line in for loop takes care
+ // of it
+
+ for (n_counter = *p_n_removed;
+ n_counter < n_unfm_number; n_counter++, p_n_unfm_pointer-- ) {
+
+ if (item_moved (&s_ih, p_s_path)) {
+ need_research = 1 ;
+ break;
+ }
+ RFALSE( p_n_unfm_pointer < (__u32 *)B_I_PITEM(p_s_bh, &s_ih) ||
+ p_n_unfm_pointer > (__u32 *)B_I_PITEM(p_s_bh, &s_ih) + I_UNFM_NUM(&s_ih) - 1,
+ "vs-5265: pointer out of range");
+
+ /* Hole, nothing to remove. */
+ if ( ! get_block_num(p_n_unfm_pointer,0) ) {
+ (*p_n_removed)++;
+ continue;
+ }
+
+ (*p_n_removed)++;
+
+ tmp = get_block_num(p_n_unfm_pointer,0);
+ put_block_num(p_n_unfm_pointer, 0, 0);
+ journal_mark_dirty (th, p_s_sb, p_s_bh);
+ inode->i_blocks -= p_s_sb->s_blocksize / 512;
+ reiserfs_free_block(th, tmp);
+ /* In case of big fragmentation it is possible that each block
+ freed will cause dirtying of one more bitmap and then we will
+ quickly overflow our transaction space. This is a
+ counter-measure against that scenario */
+ if (journal_transaction_should_end(th, th->t_blocks_allocated)) {
+ int orig_len_alloc = th->t_blocks_allocated ;
+ pathrelse(p_s_path) ;
+
+ journal_end(th, p_s_sb, orig_len_alloc) ;
+ journal_begin(th, p_s_sb, orig_len_alloc) ;
+ reiserfs_update_inode_transaction(inode) ;
+ need_research = 1;
+ break;
+ }
+
+ if ( item_moved (&s_ih, p_s_path) ) {
+ need_research = 1;
+ break ;
+ }
+ }
+
+ /* a trick. If the buffer has been logged, this
+ ** will do nothing. If we've broken the loop without
+ ** logging it, it will restore the buffer
+ **
+ */
+ reiserfs_restore_prepared_buffer(p_s_sb, p_s_bh);
+
+ /* This loop can be optimized. */
+ } while ( (*p_n_removed < n_unfm_number || need_research) &&
+ search_for_position_by_key(p_s_sb, p_s_item_key, p_s_path) == POSITION_FOUND );
+
+ RFALSE( *p_n_removed < n_unfm_number,
+ "PAP-5310: indirect item is not found");
+ RFALSE( item_moved (&s_ih, p_s_path),
+ "after while, comp failed, retry") ;
+
+ if (c_mode == M_CUT)
+ pos_in_item (p_s_path) *= UNFM_P_SIZE;
+ return c_mode;
+ }
+}
+
+
+/* Calculate bytes number which will be deleted or cutted in the balance. */
+int calc_deleted_bytes_number(
+ struct tree_balance * p_s_tb,
+ char c_mode
+ ) {
+ int n_del_size;
+ struct item_head * p_le_ih = PATH_PITEM_HEAD(p_s_tb->tb_path);
+
+ if ( is_statdata_le_ih (p_le_ih) )
+ return 0;
+
+ if ( is_direntry_le_ih (p_le_ih) ) {
+ // return EMPTY_DIR_SIZE; /* We delete emty directoris only. */
+ // we can't use EMPTY_DIR_SIZE, as old format dirs have a different
+ // empty size. ick. FIXME, is this right?
+ //
+ return ih_item_len(p_le_ih);
+ }
+ n_del_size = ( c_mode == M_DELETE ) ? ih_item_len(p_le_ih) : -p_s_tb->insert_size[0];
+
+ if ( is_indirect_le_ih (p_le_ih) )
+ n_del_size = (n_del_size/UNFM_P_SIZE)*
+ (PATH_PLAST_BUFFER(p_s_tb->tb_path)->b_size);// - get_ih_free_space (p_le_ih);
+ return n_del_size;
+}
+
+static void init_tb_struct(
+ struct reiserfs_transaction_handle *th,
+ struct tree_balance * p_s_tb,
+ struct super_block * p_s_sb,
+ struct path * p_s_path,
+ int n_size
+ ) {
+ memset (p_s_tb,'\0',sizeof(struct tree_balance));
+ p_s_tb->transaction_handle = th ;
+ p_s_tb->tb_sb = p_s_sb;
+ p_s_tb->tb_path = p_s_path;
+ PATH_OFFSET_PBUFFER(p_s_path, ILLEGAL_PATH_ELEMENT_OFFSET) = NULL;
+ PATH_OFFSET_POSITION(p_s_path, ILLEGAL_PATH_ELEMENT_OFFSET) = 0;
+ p_s_tb->insert_size[0] = n_size;
+}
+
+
+
+void padd_item (char * item, int total_length, int length)
+{
+ int i;
+
+ for (i = total_length; i > length; )
+ item [--i] = 0;
+}
+
+
+/* Delete object item. */
+int reiserfs_delete_item (struct reiserfs_transaction_handle *th,
+ struct path * p_s_path, /* Path to the deleted item. */
+ const struct cpu_key * p_s_item_key, /* Key to search for the deleted item. */
+ struct inode * p_s_inode,/* inode is here just to update i_blocks */
+ struct buffer_head * p_s_un_bh) /* NULL or unformatted node pointer. */
+{
+ struct super_block * p_s_sb = p_s_inode->i_sb;
+ struct tree_balance s_del_balance;
+ struct item_head s_ih;
+ int n_ret_value,
+ n_del_size,
+ n_removed;
+
+#ifdef CONFIG_REISERFS_CHECK
+ char c_mode;
+ int n_iter = 0;
+#endif
+
+ init_tb_struct(th, &s_del_balance, p_s_sb, p_s_path, 0/*size is unknown*/);
+
+ while ( 1 ) {
+ n_removed = 0;
+
+#ifdef CONFIG_REISERFS_CHECK
+ n_iter++;
+ c_mode =
+#endif
+ prepare_for_delete_or_cut(th, p_s_inode, p_s_path, p_s_item_key, &n_removed, &n_del_size, max_reiserfs_offset (p_s_inode));
+
+ RFALSE( c_mode != M_DELETE, "PAP-5320: mode must be M_DELETE");
+
+ copy_item_head(&s_ih, PATH_PITEM_HEAD(p_s_path));
+ s_del_balance.insert_size[0] = n_del_size;
+
+ n_ret_value = fix_nodes(M_DELETE, &s_del_balance, NULL, 0);
+ if ( n_ret_value != REPEAT_SEARCH )
+ break;
+
+ PROC_INFO_INC( p_s_sb, delete_item_restarted );
+
+ // file system changed, repeat search
+ n_ret_value = search_for_position_by_key(p_s_sb, p_s_item_key, p_s_path);
+ if (n_ret_value == IO_ERROR)
+ break;
+ if (n_ret_value == FILE_NOT_FOUND) {
+ reiserfs_warning (p_s_sb, "vs-5340: reiserfs_delete_item: "
+ "no items of the file %K found\n", p_s_item_key);
+ break;
+ }
+ } /* while (1) */
+
+ if ( n_ret_value != CARRY_ON ) {
+ unfix_nodes(&s_del_balance);
+ return 0;
+ }
+
+ // reiserfs_delete_item returns item length when success
+ n_ret_value = calc_deleted_bytes_number(&s_del_balance, M_DELETE);
+
+ if ( p_s_un_bh ) {
+ int off;
+ char *data ;
+
+ /* We are in direct2indirect conversion, so move tail contents
+ to the unformatted node */
+ /* note, we do the copy before preparing the buffer because we
+ ** don't care about the contents of the unformatted node yet.
+ ** the only thing we really care about is the direct item's data
+ ** is in the unformatted node.
+ **
+ ** Otherwise, we would have to call reiserfs_prepare_for_journal on
+ ** the unformatted node, which might schedule, meaning we'd have to
+ ** loop all the way back up to the start of the while loop.
+ **
+ ** The unformatted node must be dirtied later on. We can't be
+ ** sure here if the entire tail has been deleted yet.
+ **
+ ** p_s_un_bh is from the page cache (all unformatted nodes are
+ ** from the page cache) and might be a highmem page. So, we
+ ** can't use p_s_un_bh->b_data. But, the page has already been
+ ** kmapped, so we can use page_address()
+ ** -clm
+ */
+
+ data = page_address(p_s_un_bh->b_page) ;
+ off = ((le_ih_k_offset (&s_ih) - 1) & (PAGE_CACHE_SIZE - 1));
+ memcpy(data + off,
+ B_I_PITEM(PATH_PLAST_BUFFER(p_s_path), &s_ih), n_ret_value);
+ }
+
+ /* Perform balancing after all resources have been collected at once. */
+ do_balance(&s_del_balance, NULL, NULL, M_DELETE);
+
+ /* Return deleted body length */
+ return n_ret_value;
+}
+
+
+/* Summary Of Mechanisms For Handling Collisions Between Processes:
+
+ deletion of the body of the object is performed by iput(), with the
+ result that if multiple processes are operating on a file, the
+ deletion of the body of the file is deferred until the last process
+ that has an open inode performs its iput().
+
+ writes and truncates are protected from collisions by use of
+ semaphores.
+
+ creates, linking, and mknod are protected from collisions with other
+ processes by making the reiserfs_add_entry() the last step in the
+ creation, and then rolling back all changes if there was a collision.
+ - Hans
+*/
+
+
+/* this deletes item which never gets split */
+void reiserfs_delete_solid_item (struct reiserfs_transaction_handle *th,
+ struct key * key)
+{
+ struct tree_balance tb;
+ INITIALIZE_PATH (path);
+ int item_len;
+ int tb_init = 0 ;
+ struct cpu_key cpu_key;
+ int retval;
+
+ le_key2cpu_key (&cpu_key, key);
+
+ while (1) {
+ retval = search_item (th->t_super, &cpu_key, &path);
+ if (retval == IO_ERROR) {
+ reiserfs_warning (th->t_super, "vs-5350: reiserfs_delete_solid_item: "
+ "i/o failure occurred trying to delete %K\n", &cpu_key);
+ break;
+ }
+ if (retval != ITEM_FOUND) {
+ pathrelse (&path);
+ // No need for a warning, if there is just no free space to insert '..' item into the newly-created subdir
+ if ( !( (unsigned long long) GET_HASH_VALUE (le_key_k_offset (le_key_version (key), key)) == 0 && \
+ GET_GENERATION_NUMBER (le_key_k_offset (le_key_version (key), key)) == 1 ) )
+ reiserfs_warning (th->t_super, "vs-5355: reiserfs_delete_solid_item: %k not found\n", key);
+ break;
+ }
+ if (!tb_init) {
+ tb_init = 1 ;
+ item_len = ih_item_len( PATH_PITEM_HEAD(&path) );
+ init_tb_struct (th, &tb, th->t_super, &path, - (IH_SIZE + item_len));
+ }
+
+ retval = fix_nodes (M_DELETE, &tb, NULL, 0);
+ if (retval == REPEAT_SEARCH) {
+ PROC_INFO_INC( th -> t_super, delete_solid_item_restarted );
+ continue;
+ }
+
+ if (retval == CARRY_ON) {
+ do_balance (&tb, 0, 0, M_DELETE);
+ break;
+ }
+
+ // IO_ERROR, NO_DISK_SPACE, etc
+ reiserfs_warning (th->t_super, "vs-5360: reiserfs_delete_solid_item: "
+ "could not delete %K due to fix_nodes failure\n", &cpu_key);
+ unfix_nodes (&tb);
+ break;
+ }
+
+ reiserfs_check_path(&path) ;
+}
+
+
+void reiserfs_delete_object (struct reiserfs_transaction_handle *th, struct inode * inode)
+{
+ inode->i_size = 0;
+
+ /* for directory this deletes item containing "." and ".." */
+ reiserfs_do_truncate (th, inode, NULL, 0/*no timestamp updates*/);
+
+#if defined( USE_INODE_GENERATION_COUNTER )
+ if( !old_format_only ( th -> t_super ) )
+ {
+ __u32 *inode_generation;
+
+ inode_generation =
+ &th -> t_super -> u.reiserfs_sb.s_rs -> s_inode_generation;
+ *inode_generation = cpu_to_le32( le32_to_cpu( *inode_generation ) + 1 );
+ }
+/* USE_INODE_GENERATION_COUNTER */
+#endif
+ reiserfs_delete_solid_item (th, INODE_PKEY (inode));
+}
+
+
+static int maybe_indirect_to_direct (struct reiserfs_transaction_handle *th,
+ struct inode * p_s_inode,
+ struct page *page,
+ struct path * p_s_path,
+ const struct cpu_key * p_s_item_key,
+ loff_t n_new_file_size,
+ char * p_c_mode
+ ) {
+ struct super_block * p_s_sb = p_s_inode->i_sb;
+ int n_block_size = p_s_sb->s_blocksize;
+ int cut_bytes;
+
+ if (n_new_file_size != p_s_inode->i_size)
+ BUG ();
+
+ /* the page being sent in could be NULL if there was an i/o error
+ ** reading in the last block. The user will hit problems trying to
+ ** read the file, but for now we just skip the indirect2direct
+ */
+ if (atomic_read(&p_s_inode->i_count) > 1 ||
+ !tail_has_to_be_packed (p_s_inode) ||
+ !page || (p_s_inode->u.reiserfs_i.i_flags & i_nopack_mask)) {
+ // leave tail in an unformatted node
+ *p_c_mode = M_SKIP_BALANCING;
+ cut_bytes = n_block_size - (n_new_file_size & (n_block_size - 1));
+ pathrelse(p_s_path);
+ return cut_bytes;
+ }
+ /* Permorm the conversion to a direct_item. */
+ /*return indirect_to_direct (p_s_inode, p_s_path, p_s_item_key, n_new_file_size, p_c_mode);*/
+ return indirect2direct (th, p_s_inode, page, p_s_path, p_s_item_key, n_new_file_size, p_c_mode);
+}
+
+
+/* we did indirect_to_direct conversion. And we have inserted direct
+ item successesfully, but there were no disk space to cut unfm
+ pointer being converted. Therefore we have to delete inserted
+ direct item(s) */
+static void indirect_to_direct_roll_back (struct reiserfs_transaction_handle *th, struct inode * inode, struct path * path)
+{
+ struct cpu_key tail_key;
+ int tail_len;
+ int removed;
+
+ make_cpu_key (&tail_key, inode, inode->i_size + 1, TYPE_DIRECT, 4);// !!!!
+ tail_key.key_length = 4;
+
+ tail_len = (cpu_key_k_offset (&tail_key) & (inode->i_sb->s_blocksize - 1)) - 1;
+ while (tail_len) {
+ /* look for the last byte of the tail */
+ if (search_for_position_by_key (inode->i_sb, &tail_key, path) == POSITION_NOT_FOUND)
+ reiserfs_panic (inode->i_sb, "vs-5615: indirect_to_direct_roll_back: found invalid item");
+ RFALSE( path->pos_in_item != ih_item_len(PATH_PITEM_HEAD (path)) - 1,
+ "vs-5616: appended bytes found");
+ PATH_LAST_POSITION (path) --;
+
+ removed = reiserfs_delete_item (th, path, &tail_key, inode, 0/*unbh not needed*/);
+ RFALSE( removed <= 0 || removed > tail_len,
+ "vs-5617: there was tail %d bytes, removed item length %d bytes",
+ tail_len, removed);
+ tail_len -= removed;
+ set_cpu_key_k_offset (&tail_key, cpu_key_k_offset (&tail_key) - removed);
+ }
+ reiserfs_warning (inode->i_sb, "indirect_to_direct_roll_back: indirect_to_direct conversion has been rolled back due to lack of disk space\n");
+ //mark_file_without_tail (inode);
+ mark_inode_dirty (inode);
+}
+
+
+/* (Truncate or cut entry) or delete object item. Returns < 0 on failure */
+int reiserfs_cut_from_item (struct reiserfs_transaction_handle *th,
+ struct path * p_s_path,
+ struct cpu_key * p_s_item_key,
+ struct inode * p_s_inode,
+ struct page *page,
+ loff_t n_new_file_size)
+{
+ struct super_block * p_s_sb = p_s_inode->i_sb;
+ /* Every function which is going to call do_balance must first
+ create a tree_balance structure. Then it must fill up this
+ structure by using the init_tb_struct and fix_nodes functions.
+ After that we can make tree balancing. */
+ struct tree_balance s_cut_balance;
+ int n_cut_size = 0, /* Amount to be cut. */
+ n_ret_value = CARRY_ON,
+ n_removed = 0, /* Number of the removed unformatted nodes. */
+ n_is_inode_locked = 0;
+ char c_mode; /* Mode of the balance. */
+ int retval2 = -1;
+
+
+ init_tb_struct(th, &s_cut_balance, p_s_inode->i_sb, p_s_path, n_cut_size);
+
+
+ /* Repeat this loop until we either cut the item without needing
+ to balance, or we fix_nodes without schedule occuring */
+ while ( 1 ) {
+ /* Determine the balance mode, position of the first byte to
+ be cut, and size to be cut. In case of the indirect item
+ free unformatted nodes which are pointed to by the cut
+ pointers. */
+
+ c_mode = prepare_for_delete_or_cut(th, p_s_inode, p_s_path, p_s_item_key, &n_removed,
+ &n_cut_size, n_new_file_size);
+ if ( c_mode == M_CONVERT ) {
+ /* convert last unformatted node to direct item or leave
+ tail in the unformatted node */
+ RFALSE( n_ret_value != CARRY_ON, "PAP-5570: can not convert twice");
+
+ n_ret_value = maybe_indirect_to_direct (th, p_s_inode, page, p_s_path, p_s_item_key,
+ n_new_file_size, &c_mode);
+ if ( c_mode == M_SKIP_BALANCING )
+ /* tail has been left in the unformatted node */
+ return n_ret_value;
+
+ n_is_inode_locked = 1;
+
+ /* removing of last unformatted node will change value we
+ have to return to truncate. Save it */
+ retval2 = n_ret_value;
+ /*retval2 = p_s_sb->s_blocksize - (n_new_file_size & (p_s_sb->s_blocksize - 1));*/
+
+ /* So, we have performed the first part of the conversion:
+ inserting the new direct item. Now we are removing the
+ last unformatted node pointer. Set key to search for
+ it. */
+ set_cpu_key_k_type (p_s_item_key, TYPE_INDIRECT);
+ p_s_item_key->key_length = 4;
+ n_new_file_size -= (n_new_file_size & (p_s_sb->s_blocksize - 1));
+ set_cpu_key_k_offset (p_s_item_key, n_new_file_size + 1);
+ if ( search_for_position_by_key(p_s_sb, p_s_item_key, p_s_path) == POSITION_NOT_FOUND ){
+ print_block (PATH_PLAST_BUFFER (p_s_path), 3, PATH_LAST_POSITION (p_s_path) - 1, PATH_LAST_POSITION (p_s_path) + 1);
+ reiserfs_panic(p_s_sb, "PAP-5580: reiserfs_cut_from_item: item to convert does not exist (%K)", p_s_item_key);
+ }
+ continue;
+ }
+ if (n_cut_size == 0) {
+ pathrelse (p_s_path);
+ return 0;
+ }
+
+ s_cut_balance.insert_size[0] = n_cut_size;
+
+ n_ret_value = fix_nodes(c_mode, &s_cut_balance, NULL, 0);
+ if ( n_ret_value != REPEAT_SEARCH )
+ break;
+
+ PROC_INFO_INC( p_s_sb, cut_from_item_restarted );
+
+ n_ret_value = search_for_position_by_key(p_s_sb, p_s_item_key, p_s_path);
+ if (n_ret_value == POSITION_FOUND)
+ continue;
+
+ reiserfs_warning (p_s_sb, "PAP-5610: reiserfs_cut_from_item: item %K not found\n", p_s_item_key);
+ unfix_nodes (&s_cut_balance);
+ return (n_ret_value == IO_ERROR) ? -EIO : -ENOENT;
+ } /* while */
+
+ // check fix_nodes results (IO_ERROR or NO_DISK_SPACE)
+ if ( n_ret_value != CARRY_ON ) {
+ if ( n_is_inode_locked ) {
+ // FIXME: this seems to be not needed: we are always able
+ // to cut item
+ indirect_to_direct_roll_back (th, p_s_inode, p_s_path);
+ }
+ if (n_ret_value == NO_DISK_SPACE)
+ reiserfs_warning (p_s_sb, "NO_DISK_SPACE\n");
+ unfix_nodes (&s_cut_balance);
+ return -EIO;
+ }
+
+ /* go ahead and perform balancing */
+
+ RFALSE( c_mode == M_PASTE || c_mode == M_INSERT, "illegal mode");
+
+ /* Calculate number of bytes that need to be cut from the item. */
+ if (retval2 == -1)
+ n_ret_value = calc_deleted_bytes_number(&s_cut_balance, c_mode);
+ else
+ n_ret_value = retval2;
+
+ if ( c_mode == M_DELETE ) {
+ struct item_head * p_le_ih = PATH_PITEM_HEAD (s_cut_balance.tb_path);
+
+ if ( is_direct_le_ih (p_le_ih) && (le_ih_k_offset (p_le_ih) & (p_s_sb->s_blocksize - 1)) == 1 ) {
+ /* we delete first part of tail which was stored in direct
+ item(s) */
+ // FIXME: this is to keep 3.5 happy
+ p_s_inode->u.reiserfs_i.i_first_direct_byte = U32_MAX;
+ p_s_inode->i_blocks -= p_s_sb->s_blocksize / 512;
+ }
+ }
+
+#ifdef CONFIG_REISERFS_CHECK
+ if (n_is_inode_locked) {
+ struct item_head * le_ih = PATH_PITEM_HEAD (s_cut_balance.tb_path);
+ /* we are going to complete indirect2direct conversion. Make
+ sure, that we exactly remove last unformatted node pointer
+ of the item */
+ if (!is_indirect_le_ih (le_ih))
+ reiserfs_panic (p_s_sb, "vs-5652: reiserfs_cut_from_item: "
+ "item must be indirect %h", le_ih);
+
+ if (c_mode == M_DELETE && ih_item_len(le_ih) != UNFM_P_SIZE)
+ reiserfs_panic (p_s_sb, "vs-5653: reiserfs_cut_from_item: "
+ "completing indirect2direct conversion indirect item %h "
+ "being deleted must be of 4 byte long", le_ih);
+
+ if (c_mode == M_CUT && s_cut_balance.insert_size[0] != -UNFM_P_SIZE) {
+ reiserfs_panic (p_s_sb, "vs-5654: reiserfs_cut_from_item: "
+ "can not complete indirect2direct conversion of %h (CUT, insert_size==%d)",
+ le_ih, s_cut_balance.insert_size[0]);
+ }
+ /* it would be useful to make sure, that right neighboring
+ item is direct item of this file */
+ }
+#endif
+
+ do_balance(&s_cut_balance, NULL, NULL, c_mode);
+ if ( n_is_inode_locked ) {
+ /* we've done an indirect->direct conversion. when the data block
+ ** was freed, it was removed from the list of blocks that must
+ ** be flushed before the transaction commits, so we don't need to
+ ** deal with it here.
+ */
+ p_s_inode->u.reiserfs_i.i_flags &= ~i_pack_on_close_mask;
+ }
+ return n_ret_value;
+}
+
+
+static void truncate_directory (struct reiserfs_transaction_handle *th, struct inode * inode)
+{
+ if (inode->i_nlink)
+ reiserfs_warning (th->t_super, "vs-5655: truncate_directory: link count != 0\n");
+
+ set_le_key_k_offset (KEY_FORMAT_3_5, INODE_PKEY (inode), DOT_OFFSET);
+ set_le_key_k_type (KEY_FORMAT_3_5, INODE_PKEY (inode), TYPE_DIRENTRY);
+ reiserfs_delete_solid_item (th, INODE_PKEY (inode));
+
+ set_le_key_k_offset (KEY_FORMAT_3_5, INODE_PKEY (inode), SD_OFFSET);
+ set_le_key_k_type (KEY_FORMAT_3_5, INODE_PKEY (inode), TYPE_STAT_DATA);
+}
+
+
+
+
+/* Truncate file to the new size. Note, this must be called with a transaction
+ already started */
+void reiserfs_do_truncate (struct reiserfs_transaction_handle *th,
+ struct inode * p_s_inode, /* ->i_size contains new
+ size */
+ struct page *page, /* up to date for last block */
+ int update_timestamps /* when it is called by
+ file_release to convert
+ the tail - no timestamps
+ should be updated */
+ ) {
+ INITIALIZE_PATH (s_search_path); /* Path to the current object item. */
+ struct item_head * p_le_ih; /* Pointer to an item header. */
+ struct cpu_key s_item_key; /* Key to search for a previous file item. */
+ loff_t n_file_size, /* Old file size. */
+ n_new_file_size;/* New file size. */
+ int n_deleted; /* Number of deleted or truncated bytes. */
+ int retval;
+
+ if ( ! (S_ISREG(p_s_inode->i_mode) || S_ISDIR(p_s_inode->i_mode) || S_ISLNK(p_s_inode->i_mode)) )
+ return;
+
+ if (S_ISDIR(p_s_inode->i_mode)) {
+ // deletion of directory - no need to update timestamps
+ truncate_directory (th, p_s_inode);
+ return;
+ }
+
+ /* Get new file size. */
+ n_new_file_size = p_s_inode->i_size;
+
+ // FIXME: note, that key type is unimportant here
+ make_cpu_key (&s_item_key, p_s_inode, max_reiserfs_offset (p_s_inode), TYPE_DIRECT, 3);
+
+ retval = search_for_position_by_key(p_s_inode->i_sb, &s_item_key, &s_search_path);
+ if (retval == IO_ERROR) {
+ reiserfs_warning (p_s_inode->i_sb, "vs-5657: reiserfs_do_truncate: "
+ "i/o failure occurred trying to truncate %K\n", &s_item_key);
+ return;
+ }
+ if (retval == POSITION_FOUND || retval == FILE_NOT_FOUND) {
+ pathrelse (&s_search_path);
+ reiserfs_warning (p_s_inode->i_sb, "PAP-5660: reiserfs_do_truncate: "
+ "wrong result %d of search for %K\n", retval, &s_item_key);
+ return;
+ }
+
+ s_search_path.pos_in_item --;
+
+ /* Get real file size (total length of all file items) */
+ p_le_ih = PATH_PITEM_HEAD(&s_search_path);
+ if ( is_statdata_le_ih (p_le_ih) )
+ n_file_size = 0;
+ else {
+ loff_t offset = le_ih_k_offset (p_le_ih);
+ int bytes = op_bytes_number (p_le_ih,p_s_inode->i_sb->s_blocksize);
+
+ /* this may mismatch with real file size: if last direct item
+ had no padding zeros and last unformatted node had no free
+ space, this file would have this file size */
+ n_file_size = offset + bytes - 1;
+ }
+
+ if ( n_file_size == 0 || n_file_size < n_new_file_size ) {
+ goto update_and_out ;
+ }
+
+ /* Update key to search for the last file item. */
+ set_cpu_key_k_offset (&s_item_key, n_file_size);
+
+ do {
+ /* Cut or delete file item. */
+ n_deleted = reiserfs_cut_from_item(th, &s_search_path, &s_item_key, p_s_inode, page, n_new_file_size);
+ if (n_deleted < 0) {
+ reiserfs_warning (th->t_super, "vs-5665: reiserfs_truncate_file: cut_from_item failed\n");
+ reiserfs_check_path(&s_search_path) ;
+ return;
+ }
+
+ RFALSE( n_deleted > n_file_size,
+ "PAP-5670: reiserfs_truncate_file returns too big number: deleted %d, file_size %lu, item_key %K",
+ n_deleted, n_file_size, &s_item_key);
+
+ /* Change key to search the last file item. */
+ n_file_size -= n_deleted;
+
+ set_cpu_key_k_offset (&s_item_key, n_file_size);
+
+ /* While there are bytes to truncate and previous file item is presented in the tree. */
+
+ /*
+ ** This loop could take a really long time, and could log
+ ** many more blocks than a transaction can hold. So, we do a polite
+ ** journal end here, and if the transaction needs ending, we make
+ ** sure the file is consistent before ending the current trans
+ ** and starting a new one
+ */
+ if (journal_transaction_should_end(th, th->t_blocks_allocated)) {
+ int orig_len_alloc = th->t_blocks_allocated ;
+ decrement_counters_in_path(&s_search_path) ;
+
+ if (update_timestamps) {
+ p_s_inode->i_mtime = p_s_inode->i_ctime = CURRENT_TIME;
+ }
+ reiserfs_update_sd(th, p_s_inode) ;
+
+ journal_end(th, p_s_inode->i_sb, orig_len_alloc) ;
+ journal_begin(th, p_s_inode->i_sb, orig_len_alloc) ;
+ reiserfs_update_inode_transaction(p_s_inode) ;
+ }
+ } while ( n_file_size > ROUND_UP (n_new_file_size) &&
+ search_for_position_by_key(p_s_inode->i_sb, &s_item_key, &s_search_path) == POSITION_FOUND ) ;
+
+ RFALSE( n_file_size > ROUND_UP (n_new_file_size),
+ "PAP-5680: truncate did not finish: new_file_size %Ld, current %Ld, oid %d\n",
+ n_new_file_size, n_file_size, s_item_key.on_disk_key.k_objectid);
+
+update_and_out:
+ if (update_timestamps) {
+ // this is truncate, not file closing
+ p_s_inode->i_mtime = p_s_inode->i_ctime = CURRENT_TIME;
+ }
+ reiserfs_update_sd (th, p_s_inode);
+
+ pathrelse(&s_search_path) ;
+}
+
+
+#ifdef CONFIG_REISERFS_CHECK
+// this makes sure, that we __append__, not overwrite or add holes
+static void check_research_for_paste (struct path * path,
+ const struct cpu_key * p_s_key)
+{
+ struct item_head * found_ih = get_ih (path);
+
+ if (is_direct_le_ih (found_ih)) {
+ if (le_ih_k_offset (found_ih) + op_bytes_number (found_ih, get_last_bh (path)->b_size) !=
+ cpu_key_k_offset (p_s_key) ||
+ op_bytes_number (found_ih, get_last_bh (path)->b_size) != pos_in_item (path))
+ reiserfs_panic (0, "PAP-5720: check_research_for_paste: "
+ "found direct item %h or position (%d) does not match to key %K",
+ found_ih, pos_in_item (path), p_s_key);
+ }
+ if (is_indirect_le_ih (found_ih)) {
+ if (le_ih_k_offset (found_ih) + op_bytes_number (found_ih, get_last_bh (path)->b_size) != cpu_key_k_offset (p_s_key) ||
+ I_UNFM_NUM (found_ih) != pos_in_item (path) ||
+ get_ih_free_space (found_ih) != 0)
+ reiserfs_panic (0, "PAP-5730: check_research_for_paste: "
+ "found indirect item (%h) or position (%d) does not match to key (%K)",
+ found_ih, pos_in_item (path), p_s_key);
+ }
+}
+#endif /* config reiserfs check */
+
+
+/* Paste bytes to the existing item. Returns bytes number pasted into the item. */
+int reiserfs_paste_into_item (struct reiserfs_transaction_handle *th,
+ struct path * p_s_search_path, /* Path to the pasted item. */
+ const struct cpu_key * p_s_key, /* Key to search for the needed item.*/
+ const char * p_c_body, /* Pointer to the bytes to paste. */
+ int n_pasted_size) /* Size of pasted bytes. */
+{
+ struct tree_balance s_paste_balance;
+ int retval;
+
+ init_tb_struct(th, &s_paste_balance, th->t_super, p_s_search_path, n_pasted_size);
+#ifdef DISPLACE_NEW_PACKING_LOCALITIES
+ s_paste_balance.key = p_s_key->on_disk_key;
+#endif
+
+ while ( (retval = fix_nodes(M_PASTE, &s_paste_balance, NULL, p_c_body)) == REPEAT_SEARCH ) {
+ /* file system changed while we were in the fix_nodes */
+ PROC_INFO_INC( th -> t_super, paste_into_item_restarted );
+ retval = search_for_position_by_key (th->t_super, p_s_key, p_s_search_path);
+ if (retval == IO_ERROR) {
+ retval = -EIO ;
+ goto error_out ;
+ }
+ if (retval == POSITION_FOUND) {
+ reiserfs_warning (th->t_super, "PAP-5710: reiserfs_paste_into_item: entry or pasted byte (%K) exists\n", p_s_key);
+ retval = -EEXIST ;
+ goto error_out ;
+ }
+
+#ifdef CONFIG_REISERFS_CHECK
+ check_research_for_paste (p_s_search_path, p_s_key);
+#endif
+ }
+
+ /* Perform balancing after all resources are collected by fix_nodes, and
+ accessing them will not risk triggering schedule. */
+ if ( retval == CARRY_ON ) {
+ do_balance(&s_paste_balance, NULL/*ih*/, p_c_body, M_PASTE);
+ return 0;
+ }
+ retval = (retval == NO_DISK_SPACE) ? -ENOSPC : -EIO;
+error_out:
+ /* this also releases the path */
+ unfix_nodes(&s_paste_balance);
+ return retval ;
+}
+
+
+/* Insert new item into the buffer at the path. */
+int reiserfs_insert_item(struct reiserfs_transaction_handle *th,
+ struct path * p_s_path, /* Path to the inserteded item. */
+ const struct cpu_key * key,
+ struct item_head * p_s_ih, /* Pointer to the item header to insert.*/
+ const char * p_c_body) /* Pointer to the bytes to insert. */
+{
+ struct tree_balance s_ins_balance;
+ int retval;
+
+ init_tb_struct(th, &s_ins_balance, th->t_super, p_s_path, IH_SIZE + ih_item_len(p_s_ih));
+#ifdef DISPLACE_NEW_PACKING_LOCALITIES
+ s_ins_balance.key = key->on_disk_key;
+#endif
+
+ /*
+ if (p_c_body == 0)
+ n_zeros_num = ih_item_len(p_s_ih);
+ */
+ // le_key2cpu_key (&key, &(p_s_ih->ih_key));
+
+ while ( (retval = fix_nodes(M_INSERT, &s_ins_balance, p_s_ih, p_c_body)) == REPEAT_SEARCH) {
+ /* file system changed while we were in the fix_nodes */
+ PROC_INFO_INC( th -> t_super, insert_item_restarted );
+ retval = search_item (th->t_super, key, p_s_path);
+ if (retval == IO_ERROR) {
+ retval = -EIO;
+ goto error_out ;
+ }
+ if (retval == ITEM_FOUND) {
+ reiserfs_warning (th->t_super, "PAP-5760: reiserfs_insert_item: "
+ "key %K already exists in the tree\n", key);
+ retval = -EEXIST ;
+ goto error_out;
+ }
+ }
+
+ /* make balancing after all resources will be collected at a time */
+ if ( retval == CARRY_ON ) {
+ do_balance (&s_ins_balance, p_s_ih, p_c_body, M_INSERT);
+ return 0;
+ }
+
+ retval = (retval == NO_DISK_SPACE) ? -ENOSPC : -EIO;
+error_out:
+ /* also releases the path */
+ unfix_nodes(&s_ins_balance);
+ return retval;
+}
+
+
+
+
diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c
new file mode 100644
index 00000000000000..13321240e19764
--- /dev/null
+++ b/fs/reiserfs/super.c
@@ -0,0 +1,1390 @@
+/*
+ * Copyright 2000-2002 by Hans Reiser, licensing governed by reiserfs/README
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/blkdev.h>
+#include <linux/vmalloc.h>
+#include <asm/uaccess.h>
+#include <linux/reiserfs_fs.h>
+#include <linux/smp_lock.h>
+#include <linux/locks.h>
+#include <linux/init.h>
+
+#define REISERFS_OLD_BLOCKSIZE 4096
+#define REISERFS_SUPER_MAGIC_STRING_OFFSET_NJ 20
+
+const char reiserfs_3_5_magic_string[] = REISERFS_SUPER_MAGIC_STRING;
+const char reiserfs_3_6_magic_string[] = REISER2FS_SUPER_MAGIC_STRING;
+const char reiserfs_jr_magic_string[] = REISER2FS_JR_SUPER_MAGIC_STRING;
+
+int is_reiserfs_3_5 (struct reiserfs_super_block * rs)
+{
+ return !strncmp (rs->s_v1.s_magic, reiserfs_3_5_magic_string,
+ strlen (reiserfs_3_5_magic_string));
+}
+
+
+int is_reiserfs_3_6 (struct reiserfs_super_block * rs)
+{
+ return !strncmp (rs->s_v1.s_magic, reiserfs_3_6_magic_string,
+ strlen (reiserfs_3_6_magic_string));
+}
+
+
+int is_reiserfs_jr (struct reiserfs_super_block * rs)
+{
+ return !strncmp (rs->s_v1.s_magic, reiserfs_jr_magic_string,
+ strlen (reiserfs_jr_magic_string));
+}
+
+
+static int is_any_reiserfs_magic_string (struct reiserfs_super_block * rs)
+{
+ return (is_reiserfs_3_5 (rs) || is_reiserfs_3_6 (rs) ||
+ is_reiserfs_jr (rs));
+}
+
+static int reiserfs_remount (struct super_block * s, int * flags, char * data);
+static int reiserfs_statfs (struct super_block * s, struct statfs * buf);
+
+static void reiserfs_write_super (struct super_block * s)
+{
+
+ int dirty = 0 ;
+ lock_kernel() ;
+ if (!(s->s_flags & MS_RDONLY)) {
+ dirty = flush_old_commits(s, 1) ;
+ }
+ s->s_dirt = dirty;
+ unlock_kernel() ;
+}
+
+static void reiserfs_write_super_lockfs (struct super_block * s)
+{
+
+ int dirty = 0 ;
+ struct reiserfs_transaction_handle th ;
+ lock_kernel() ;
+ if (!(s->s_flags & MS_RDONLY)) {
+ journal_begin(&th, s, 1) ;
+ reiserfs_prepare_for_journal(s, SB_BUFFER_WITH_SB(s), 1);
+ journal_mark_dirty(&th, s, SB_BUFFER_WITH_SB (s));
+ reiserfs_block_writes(&th) ;
+ journal_end(&th, s, 1) ;
+ }
+ s->s_dirt = dirty;
+ unlock_kernel() ;
+}
+
+void reiserfs_unlockfs(struct super_block *s) {
+ reiserfs_allow_writes(s) ;
+}
+
+extern const struct key MAX_KEY;
+
+
+/* this is used to delete "save link" when there are no items of a
+ file it points to. It can either happen if unlink is completed but
+ "save unlink" removal, or if file has both unlink and truncate
+ pending and as unlink completes first (because key of "save link"
+ protecting unlink is bigger that a key lf "save link" which
+ protects truncate), so there left no items to make truncate
+ completion on */
+static void remove_save_link_only (struct super_block * s, struct key * key, int oid_free)
+{
+ struct reiserfs_transaction_handle th;
+
+ /* we are going to do one balancing */
+ journal_begin (&th, s, JOURNAL_PER_BALANCE_CNT);
+
+ reiserfs_delete_solid_item (&th, key);
+ if (oid_free)
+ /* removals are protected by direct items */
+ reiserfs_release_objectid (&th, le32_to_cpu (key->k_objectid));
+
+ journal_end (&th, s, JOURNAL_PER_BALANCE_CNT);
+}
+
+
+/* look for uncompleted unlinks and truncates and complete them */
+static void finish_unfinished (struct super_block * s)
+{
+ INITIALIZE_PATH (path);
+ struct cpu_key max_cpu_key, obj_key;
+ struct key save_link_key;
+ int retval;
+ struct item_head * ih;
+ struct buffer_head * bh;
+ int item_pos;
+ char * item;
+ int done;
+ struct inode * inode;
+ int truncate;
+
+
+ /* compose key to look for "save" links */
+ max_cpu_key.version = KEY_FORMAT_3_5;
+ max_cpu_key.on_disk_key = MAX_KEY;
+ max_cpu_key.key_length = 3;
+
+ done = 0;
+ s -> u.reiserfs_sb.s_is_unlinked_ok = 1;
+ while (1) {
+ retval = search_item (s, &max_cpu_key, &path);
+ if (retval != ITEM_NOT_FOUND) {
+ reiserfs_warning (s, "vs-2140: finish_unfinished: search_by_key returned %d\n",
+ retval);
+ break;
+ }
+
+ bh = get_last_bh (&path);
+ item_pos = get_item_pos (&path);
+ if (item_pos != B_NR_ITEMS (bh)) {
+ reiserfs_warning (s, "vs-2060: finish_unfinished: wrong position found\n");
+ break;
+ }
+ item_pos --;
+ ih = B_N_PITEM_HEAD (bh, item_pos);
+
+ if (le32_to_cpu (ih->ih_key.k_dir_id) != MAX_KEY_OBJECTID)
+ /* there are no "save" links anymore */
+ break;
+
+ save_link_key = ih->ih_key;
+ if (is_indirect_le_ih (ih))
+ truncate = 1;
+ else
+ truncate = 0;
+
+ /* reiserfs_iget needs k_dirid and k_objectid only */
+ item = B_I_PITEM (bh, ih);
+ obj_key.on_disk_key.k_dir_id = le32_to_cpu (*(__u32 *)item);
+ obj_key.on_disk_key.k_objectid = le32_to_cpu (ih->ih_key.k_objectid);
+ obj_key.on_disk_key.u.k_offset_v1.k_offset = 0;
+ obj_key.on_disk_key.u.k_offset_v1.k_uniqueness = 0;
+
+ pathrelse (&path);
+
+ inode = reiserfs_iget (s, &obj_key);
+ if (!inode) {
+ /* the unlink almost completed, it just did not manage to remove
+ "save" link and release objectid */
+ reiserfs_warning (s, "vs-2180: finish_unfinished: iget failed for %K\n",
+ &obj_key);
+ remove_save_link_only (s, &save_link_key, 1);
+ continue;
+ }
+
+ if (!truncate && inode->i_nlink) {
+ /* file is not unlinked */
+ reiserfs_warning (s, "vs-2185: finish_unfinished: file %K is not unlinked\n",
+ &obj_key);
+ remove_save_link_only (s, &save_link_key, 0);
+ continue;
+ }
+
+ if (truncate && S_ISDIR (inode->i_mode) ) {
+ /* We got a truncate request for a dir which is impossible.
+ The only imaginable way is to execute unfinished truncate request
+ then boot into old kernel, remove the file and create dir with
+ the same key. */
+ reiserfs_warning(s, "green-2101: impossible truncate on a directory %k. Please report\n", INODE_PKEY (inode));
+ remove_save_link_only (s, &save_link_key, 0);
+ truncate = 0;
+ iput (inode);
+ continue;
+ }
+
+ if (truncate) {
+ inode -> u.reiserfs_i.i_flags |= i_link_saved_truncate_mask;
+ /* not completed truncate found. New size was committed together
+ with "save" link */
+ reiserfs_warning (s, "Truncating %k to %Ld ..",
+ INODE_PKEY (inode), inode->i_size);
+ reiserfs_truncate_file (inode, 0/*don't update modification time*/);
+ remove_save_link (inode, truncate);
+ } else {
+ inode -> u.reiserfs_i.i_flags |= i_link_saved_unlink_mask;
+ /* not completed unlink (rmdir) found */
+ reiserfs_warning (s, "Removing %k..", INODE_PKEY (inode));
+ /* removal gets completed in iput */
+ }
+
+ iput (inode);
+ printk ("done\n");
+ done ++;
+ }
+ s -> u.reiserfs_sb.s_is_unlinked_ok = 0;
+
+ pathrelse (&path);
+ if (done)
+ reiserfs_warning (s, "There were %d uncompleted unlinks/truncates. "
+ "Completed\n", done);
+}
+
+/* to protect file being unlinked from getting lost we "safe" link files
+ being unlinked. This link will be deleted in the same transaction with last
+ item of file. mounting the filesytem we scan all these links and remove
+ files which almost got lost */
+void add_save_link (struct reiserfs_transaction_handle * th,
+ struct inode * inode, int truncate)
+{
+ INITIALIZE_PATH (path);
+ int retval;
+ struct cpu_key key;
+ struct item_head ih;
+ __u32 link;
+
+ /* file can only get one "save link" of each kind */
+ RFALSE( truncate &&
+ ( inode -> u.reiserfs_i.i_flags & i_link_saved_truncate_mask ),
+ "saved link already exists for truncated inode %lx",
+ ( long ) inode -> i_ino );
+ RFALSE( !truncate &&
+ ( inode -> u.reiserfs_i.i_flags & i_link_saved_unlink_mask ),
+ "saved link already exists for unlinked inode %lx",
+ ( long ) inode -> i_ino );
+
+ /* setup key of "save" link */
+ key.version = KEY_FORMAT_3_5;
+ key.on_disk_key.k_dir_id = MAX_KEY_OBJECTID;
+ key.on_disk_key.k_objectid = inode->i_ino;
+ if (!truncate) {
+ /* unlink, rmdir, rename */
+ set_cpu_key_k_offset (&key, 1 + inode->i_sb->s_blocksize);
+ set_cpu_key_k_type (&key, TYPE_DIRECT);
+
+ /* item head of "safe" link */
+ make_le_item_head (&ih, &key, key.version, 1 + inode->i_sb->s_blocksize, TYPE_DIRECT,
+ 4/*length*/, 0xffff/*free space*/);
+ } else {
+ /* truncate */
+ if (S_ISDIR (inode->i_mode))
+ reiserfs_warning(inode->i_sb, "green-2102: Adding a truncate savelink for a directory %k! Please report\n", INODE_PKEY(inode));
+ set_cpu_key_k_offset (&key, 1);
+ set_cpu_key_k_type (&key, TYPE_INDIRECT);
+
+ /* item head of "safe" link */
+ make_le_item_head (&ih, &key, key.version, 1, TYPE_INDIRECT,
+ 4/*length*/, 0/*free space*/);
+ }
+ key.key_length = 3;
+
+ /* look for its place in the tree */
+ retval = search_item (inode->i_sb, &key, &path);
+ if (retval != ITEM_NOT_FOUND) {
+ if ( retval != -ENOSPC )
+ reiserfs_warning (inode->i_sb, "vs-2100: add_save_link:"
+ "search_by_key (%K) returned %d\n", &key, retval);
+ pathrelse (&path);
+ return;
+ }
+
+ /* body of "save" link */
+ link = INODE_PKEY (inode)->k_dir_id;
+
+ /* put "save" link inot tree */
+ retval = reiserfs_insert_item (th, &path, &key, &ih, (char *)&link);
+ if (retval) {
+ if (retval != -ENOSPC)
+ reiserfs_warning (inode->i_sb, "vs-2120: add_save_link: insert_item returned %d\n",
+ retval);
+ } else {
+ if( truncate )
+ inode -> u.reiserfs_i.i_flags |= i_link_saved_truncate_mask;
+ else
+ inode -> u.reiserfs_i.i_flags |= i_link_saved_unlink_mask;
+ }
+}
+
+
+/* this opens transaction unlike add_save_link */
+void remove_save_link (struct inode * inode, int truncate)
+{
+ struct reiserfs_transaction_handle th;
+ struct key key;
+
+
+ /* we are going to do one balancing only */
+ journal_begin (&th, inode->i_sb, JOURNAL_PER_BALANCE_CNT);
+
+ /* setup key of "save" link */
+ key.k_dir_id = cpu_to_le32 (MAX_KEY_OBJECTID);
+ key.k_objectid = INODE_PKEY (inode)->k_objectid;
+ if (!truncate) {
+ /* unlink, rmdir, rename */
+ set_le_key_k_offset (KEY_FORMAT_3_5, &key,
+ 1 + inode->i_sb->s_blocksize);
+ set_le_key_k_type (KEY_FORMAT_3_5, &key, TYPE_DIRECT);
+ } else {
+ /* truncate */
+ set_le_key_k_offset (KEY_FORMAT_3_5, &key, 1);
+ set_le_key_k_type (KEY_FORMAT_3_5, &key, TYPE_INDIRECT);
+ }
+
+ if( ( truncate &&
+ ( inode -> u.reiserfs_i.i_flags & i_link_saved_truncate_mask ) ) ||
+ ( !truncate &&
+ ( inode -> u.reiserfs_i.i_flags & i_link_saved_unlink_mask ) ) )
+ reiserfs_delete_solid_item (&th, &key);
+ if (!truncate) {
+ reiserfs_release_objectid (&th, inode->i_ino);
+ inode -> u.reiserfs_i.i_flags &= ~i_link_saved_unlink_mask;
+ } else
+ inode -> u.reiserfs_i.i_flags &= ~i_link_saved_truncate_mask;
+
+ journal_end (&th, inode->i_sb, JOURNAL_PER_BALANCE_CNT);
+}
+
+
+static void reiserfs_put_super (struct super_block * s)
+{
+ int i;
+ struct reiserfs_transaction_handle th ;
+
+ /* change file system state to current state if it was mounted with read-write permissions */
+ if (!(s->s_flags & MS_RDONLY)) {
+ journal_begin(&th, s, 10) ;
+ reiserfs_prepare_for_journal(s, SB_BUFFER_WITH_SB(s), 1) ;
+ set_sb_umount_state( SB_DISK_SUPER_BLOCK(s), s->u.reiserfs_sb.s_mount_state );
+ journal_mark_dirty(&th, s, SB_BUFFER_WITH_SB (s));
+ }
+
+ /* note, journal_release checks for readonly mount, and can decide not
+ ** to do a journal_end
+ */
+ journal_release(&th, s) ;
+
+ for (i = 0; i < SB_BMAP_NR (s); i ++)
+ brelse (SB_AP_BITMAP (s)[i].bh);
+
+ vfree (SB_AP_BITMAP (s));
+
+ brelse (SB_BUFFER_WITH_SB (s));
+
+ print_statistics (s);
+
+ if (s->u.reiserfs_sb.s_kmallocs != 0) {
+ reiserfs_warning (s, "vs-2004: reiserfs_put_super: allocated memory left %d\n",
+ s->u.reiserfs_sb.s_kmallocs);
+ }
+
+ if (s->u.reiserfs_sb.reserved_blocks != 0) {
+ reiserfs_warning (s, "green-2005: reiserfs_put_super: reserved blocks left %d\n",
+ s->u.reiserfs_sb.reserved_blocks);
+ }
+
+ reiserfs_proc_unregister( s, "journal" );
+ reiserfs_proc_unregister( s, "oidmap" );
+ reiserfs_proc_unregister( s, "on-disk-super" );
+ reiserfs_proc_unregister( s, "bitmap" );
+ reiserfs_proc_unregister( s, "per-level" );
+ reiserfs_proc_unregister( s, "super" );
+ reiserfs_proc_unregister( s, "version" );
+ reiserfs_proc_info_done( s );
+ return;
+}
+
+/* we don't mark inodes dirty, we just log them */
+static void reiserfs_dirty_inode (struct inode * inode) {
+ struct reiserfs_transaction_handle th ;
+
+ if (inode->i_sb->s_flags & MS_RDONLY) {
+ reiserfs_warning(inode->i_sb, "clm-6006: writing inode %lu on readonly FS\n",
+ inode->i_ino) ;
+ return ;
+ }
+ lock_kernel() ;
+
+ /* this is really only used for atime updates, so they don't have
+ ** to be included in O_SYNC or fsync
+ */
+ journal_begin(&th, inode->i_sb, 1) ;
+ reiserfs_update_sd (&th, inode);
+ journal_end(&th, inode->i_sb, 1) ;
+ unlock_kernel() ;
+}
+
+struct super_operations reiserfs_sops =
+{
+ read_inode: reiserfs_read_inode,
+ read_inode2: reiserfs_read_inode2,
+ write_inode: reiserfs_write_inode,
+ dirty_inode: reiserfs_dirty_inode,
+ delete_inode: reiserfs_delete_inode,
+ put_super: reiserfs_put_super,
+ write_super: reiserfs_write_super,
+ write_super_lockfs: reiserfs_write_super_lockfs,
+ unlockfs: reiserfs_unlockfs,
+ statfs: reiserfs_statfs,
+ remount_fs: reiserfs_remount,
+
+ fh_to_dentry: reiserfs_fh_to_dentry,
+ dentry_to_fh: reiserfs_dentry_to_fh,
+
+};
+
+/* this struct is used in reiserfs_getopt () for containing the value for those
+ mount options that have values rather than being toggles. */
+typedef struct {
+ char * value;
+ int setmask; /* bitmask which is to set on mount_options bitmask when this
+ value is found, 0 is no bits are to be changed. */
+ int clrmask; /* bitmask which is to clear on mount_options bitmask when this
+ value is found, 0 is no bits are to be changed. This is
+ applied BEFORE setmask */
+} arg_desc_t;
+
+
+/* this struct is used in reiserfs_getopt() for describing the set of reiserfs
+ mount options */
+typedef struct {
+ char * option_name;
+ int arg_required; /* 0 is argument is not required, not 0 otherwise */
+ const arg_desc_t * values; /* list of values accepted by an option */
+ int setmask; /* bitmask which is to set on mount_options bitmask when this
+ value is found, 0 is no bits are to be changed. */
+ int clrmask; /* bitmask which is to clear on mount_options bitmask when this
+ value is found, 0 is no bits are to be changed. This is
+ applied BEFORE setmask */
+} opt_desc_t;
+
+
+/* possible values for "-o hash=" and bits which are to be set in s_mount_opt
+ of reiserfs specific part of in-core super block */
+static const arg_desc_t hash[] = {
+ {"rupasov", 1<<FORCE_RUPASOV_HASH,(1<<FORCE_TEA_HASH)|(1<<FORCE_R5_HASH)},
+ {"tea", 1<<FORCE_TEA_HASH,(1<<FORCE_RUPASOV_HASH)|(1<<FORCE_R5_HASH)},
+ {"r5", 1<<FORCE_R5_HASH,(1<<FORCE_RUPASOV_HASH)|(1<<FORCE_TEA_HASH)},
+ {"detect", 1<<FORCE_HASH_DETECT, (1<<FORCE_RUPASOV_HASH)|(1<<FORCE_TEA_HASH)|(1<<FORCE_R5_HASH)},
+ {NULL, 0, 0}
+};
+
+
+/* possible values for "-o block-allocator=" and bits which are to be set in
+ s_mount_opt of reiserfs specific part of in-core super block */
+static const arg_desc_t balloc[] = {
+ {"noborder", 1<<REISERFS_NO_BORDER, 0},
+ {"border", 0, 1<<REISERFS_NO_BORDER},
+ {"no_unhashed_relocation", 1<<REISERFS_NO_UNHASHED_RELOCATION, 0},
+ {"hashed_relocation", 1<<REISERFS_HASHED_RELOCATION, 0},
+ {"test4", 1<<REISERFS_TEST4, 0},
+ {"notest4", 0, 1<<REISERFS_TEST4},
+ {NULL, 0, 0}
+};
+
+static const arg_desc_t tails[] = {
+ {"on", 1<<REISERFS_LARGETAIL, 1<<REISERFS_SMALLTAIL},
+ {"off", 0, (1<<REISERFS_LARGETAIL)|(1<<REISERFS_SMALLTAIL)},
+ {"small", 1<<REISERFS_SMALLTAIL, 1<<REISERFS_LARGETAIL},
+ {NULL, 0, 0}
+};
+
+
+/* proceed only one option from a list *cur - string containing of mount options
+ opts - array of options which are accepted
+ opt_arg - if option is found and requires an argument and if it is specifed
+ in the input - pointer to the argument is stored here
+ bit_flags - if option requires to set a certain bit - it is set here
+ return -1 if unknown option is found, opt->arg_required otherwise */
+static int reiserfs_getopt ( struct super_block * s, char ** cur, opt_desc_t * opts, char ** opt_arg,
+ unsigned long * bit_flags)
+{
+ char * p;
+ /* foo=bar,
+ ^ ^ ^
+ | | +-- option_end
+ | +-- arg_start
+ +-- option_start
+ */
+ const opt_desc_t * opt;
+ const arg_desc_t * arg;
+
+
+ p = *cur;
+
+ /* assume argument cannot contain commas */
+ *cur = strchr (p, ',');
+ if (*cur) {
+ *(*cur) = '\0';
+ (*cur) ++;
+ }
+
+ if ( !strncmp (p, "alloc=", 6) ) {
+ /* Ugly special case, probably we should redo options parser so that
+ it can understand several arguments for some options, also so that
+ it can fill several bitfields with option values. */
+ if ( reiserfs_parse_alloc_options( s, p + 6) ) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+
+ /* for every option in the list */
+ for (opt = opts; opt->option_name; opt ++) {
+ if (!strncmp (p, opt->option_name, strlen (opt->option_name))) {
+ if (bit_flags) {
+ *bit_flags &= ~opt->clrmask;
+ *bit_flags |= opt->setmask;
+ }
+ break;
+ }
+ }
+ if (!opt->option_name) {
+ printk ("reiserfs_getopt: unknown option \"%s\"\n", p);
+ return -1;
+ }
+
+ p += strlen (opt->option_name);
+ switch (*p) {
+ case '=':
+ if (!opt->arg_required) {
+ printk ("reiserfs_getopt: the option \"%s\" does not require an argument\n",
+ opt->option_name);
+ return -1;
+ }
+ break;
+
+ case 0:
+ if (opt->arg_required) {
+ printk ("reiserfs_getopt: the option \"%s\" requires an argument\n", opt->option_name);
+ return -1;
+ }
+ break;
+ default:
+ printk ("reiserfs_getopt: head of option \"%s\" is only correct\n", opt->option_name);
+ return -1;
+ }
+
+ /* move to the argument, or to next option if argument is not required */
+ p ++;
+
+ if ( opt->arg_required && !strlen (p) ) {
+ /* this catches "option=," */
+ printk ("reiserfs_getopt: empty argument for \"%s\"\n", opt->option_name);
+ return -1;
+ }
+
+ if (!opt->values) {
+ /* *=NULLopt_arg contains pointer to argument */
+ *opt_arg = p;
+ return opt->arg_required;
+ }
+
+ /* values possible for this option are listed in opt->values */
+ for (arg = opt->values; arg->value; arg ++) {
+ if (!strcmp (p, arg->value)) {
+ if (bit_flags) {
+ *bit_flags &= ~arg->clrmask;
+ *bit_flags |= arg->setmask;
+ }
+ return opt->arg_required;
+ }
+ }
+
+ printk ("reiserfs_getopt: bad value \"%s\" for option \"%s\"\n", p, opt->option_name);
+ return -1;
+}
+
+/* returns 0 if something is wrong in option string, 1 - otherwise */
+static int reiserfs_parse_options (struct super_block * s, char * options, /* string given via mount's -o */
+ unsigned long * mount_options,
+ /* after the parsing phase, contains the
+ collection of bitflags defining what
+ mount options were selected. */
+ unsigned long * blocks) /* strtol-ed from NNN of resize=NNN */
+{
+ int c;
+ char * arg = NULL;
+ char * pos;
+ opt_desc_t opts[] = {
+ {"tails", 't', tails, 0, 0},
+ /* Compatibility stuff, so that -o notail
+ for old setups still work */
+ {"notail", 0, 0, 0, (1<<REISERFS_LARGETAIL)|(1<<REISERFS_SMALLTAIL)},
+ {"conv", 0, 0, 1<<REISERFS_CONVERT, 0},
+ {"nolog", 0, 0, 0, 0}, /* This is unsupported */
+ {"replayonly", 0, 0, 1<<REPLAYONLY, 0},
+
+ {"block-allocator", 'a', balloc, 0, 0},
+ {"hash", 'h', hash, 1<<FORCE_HASH_DETECT, 0},
+
+ {"resize", 'r', 0, 0, 0},
+ {"attrs", 0, 0, 1<<REISERFS_ATTRS, 0},
+ {"noattrs", 0, 0, 0, 1<<REISERFS_ATTRS},
+ {NULL, 0, 0, 0, 0}
+ };
+
+ *blocks = 0;
+ if (!options || !*options)
+ /* use default configuration: create tails, journaling on, no
+ conversion to newest format */
+ return 1;
+
+ for (pos = options; pos; ) {
+ c = reiserfs_getopt (s, &pos, opts, &arg, mount_options);
+ if (c == -1)
+ /* wrong option is given */
+ return 0;
+
+ if (c == 'r') {
+ char * p;
+
+ p = 0;
+ /* "resize=NNN" */
+ *blocks = simple_strtoul (arg, &p, 0);
+ if (*p != '\0') {
+ /* NNN does not look like a number */
+ printk ("reiserfs_parse_options: bad value %s\n", arg);
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+
+int reiserfs_is_super(struct super_block *s) {
+ return (s->s_dev != 0 && s->s_op == &reiserfs_sops) ;
+}
+
+
+static void handle_attrs( struct super_block *s )
+{
+ struct reiserfs_super_block * rs;
+
+ if( reiserfs_attrs( s ) ) {
+ rs = SB_DISK_SUPER_BLOCK (s);
+ if( old_format_only(s) ) {
+ reiserfs_warning(s, "reiserfs: cannot support attributes on 3.5.x disk format\n" );
+ s -> u.reiserfs_sb.s_mount_opt &= ~ ( 1 << REISERFS_ATTRS );
+ return;
+ }
+ if( !( le32_to_cpu( rs -> s_flags ) & reiserfs_attrs_cleared ) ) {
+ reiserfs_warning(s, "reiserfs: cannot support attributes until flag is set in super-block\n" );
+ s -> u.reiserfs_sb.s_mount_opt &= ~ ( 1 << REISERFS_ATTRS );
+ }
+ }
+}
+
+static int reiserfs_remount (struct super_block * s, int * mount_flags, char * data)
+{
+ struct reiserfs_super_block * rs;
+ struct reiserfs_transaction_handle th ;
+ unsigned long blocks;
+ unsigned long mount_options = s->u.reiserfs_sb.s_mount_opt;
+ unsigned long safe_mask = 0;
+
+ rs = SB_DISK_SUPER_BLOCK (s);
+ if (!reiserfs_parse_options(s, data, &mount_options, &blocks))
+ return -EINVAL;
+
+ /* Add options that are safe here */
+ safe_mask |= 1 << REISERFS_SMALLTAIL;
+ safe_mask |= 1 << REISERFS_LARGETAIL;
+ safe_mask |= 1 << REISERFS_NO_BORDER;
+ safe_mask |= 1 << REISERFS_NO_UNHASHED_RELOCATION;
+ safe_mask |= 1 << REISERFS_HASHED_RELOCATION;
+ safe_mask |= 1 << REISERFS_TEST4;
+ safe_mask |= 1 << REISERFS_ATTRS;
+
+ /* Update the bitmask, taking care to keep
+ * the bits we're not allowed to change here */
+ s->u.reiserfs_sb.s_mount_opt = (s->u.reiserfs_sb.s_mount_opt & ~safe_mask) | (mount_options & safe_mask);
+
+ handle_attrs( s );
+
+ if(blocks) {
+ int rc = reiserfs_resize(s, blocks);
+ if (rc != 0)
+ return rc;
+ }
+
+ if (*mount_flags & MS_RDONLY) {
+ /* remount read-only */
+ if (s->s_flags & MS_RDONLY)
+ /* it is read-only already */
+ return 0;
+ /* try to remount file system with read-only permissions */
+ if (sb_umount_state(rs) == REISERFS_VALID_FS || s->u.reiserfs_sb.s_mount_state != REISERFS_VALID_FS) {
+ return 0;
+ }
+
+ journal_begin(&th, s, 10) ;
+ /* Mounting a rw partition read-only. */
+ reiserfs_prepare_for_journal(s, SB_BUFFER_WITH_SB(s), 1) ;
+ set_sb_umount_state( rs, s->u.reiserfs_sb.s_mount_state );
+ journal_mark_dirty(&th, s, SB_BUFFER_WITH_SB (s));
+ s->s_dirt = 0;
+ } else {
+ /* remount read-write */
+ if (!(s->s_flags & MS_RDONLY))
+ return 0; /* We are read-write already */
+
+ s->s_flags &= ~MS_RDONLY ; /* now it is safe to call journal_begin */
+ journal_begin(&th, s, 10) ;
+
+ /* Mount a partition which is read-only, read-write */
+ reiserfs_prepare_for_journal(s, SB_BUFFER_WITH_SB(s), 1) ;
+ s->u.reiserfs_sb.s_mount_state = sb_umount_state(rs);
+ s->s_flags &= ~MS_RDONLY;
+ set_sb_umount_state( rs, REISERFS_ERROR_FS );
+ /* mark_buffer_dirty (SB_BUFFER_WITH_SB (s), 1); */
+ journal_mark_dirty(&th, s, SB_BUFFER_WITH_SB (s));
+ s->s_dirt = 0;
+ s->u.reiserfs_sb.s_mount_state = REISERFS_VALID_FS ;
+ }
+ /* this will force a full flush of all journal lists */
+ SB_JOURNAL(s)->j_must_wait = 1 ;
+ journal_end(&th, s, 10) ;
+
+ if (!( *mount_flags & MS_RDONLY ) )
+ finish_unfinished( s );
+
+ return 0;
+}
+
+/* load_bitmap_info_data - Sets up the reiserfs_bitmap_info structure from disk.
+ * @sb - superblock for this filesystem
+ * @bi - the bitmap info to be loaded. Requires that bi->bh is valid.
+ *
+ * This routine counts how many free bits there are, finding the first zero
+ * as a side effect. Could also be implemented as a loop of test_bit() calls, or
+ * a loop of find_first_zero_bit() calls. This implementation is similar to
+ * find_first_zero_bit(), but doesn't return after it finds the first bit.
+ * Should only be called on fs mount, but should be fairly efficient anyways.
+ *
+ * bi->first_zero_hint is considered unset if it == 0, since the bitmap itself
+ * will * invariably occupt block 0 represented in the bitmap. The only
+ * exception to this is when free_count also == 0, since there will be no
+ * free blocks at all.
+ */
+static void load_bitmap_info_data (struct super_block *sb,
+ struct reiserfs_bitmap_info *bi)
+{
+ unsigned long *cur = (unsigned long *)bi->bh->b_data;
+
+ while ((char *)cur < (bi->bh->b_data + sb->s_blocksize)) {
+
+ /* No need to scan if all 0's or all 1's.
+ * Since we're only counting 0's, we can simply ignore all 1's */
+ if (*cur == 0) {
+ if (bi->first_zero_hint == 0) {
+ bi->first_zero_hint = ((char *)cur - bi->bh->b_data) << 3;
+ }
+ bi->free_count += sizeof ( unsigned long ) * 8;
+ } else if (*cur != ~0L) {
+ int b;
+ for (b = 0; b < sizeof ( unsigned long ) * 8; b++) {
+ if (!reiserfs_test_le_bit (b, cur)) {
+ bi->free_count ++;
+ if (bi->first_zero_hint == 0)
+ bi->first_zero_hint =
+ (((char *)cur - bi->bh->b_data) << 3) + b;
+ }
+ }
+ }
+ cur ++;
+ }
+
+#ifdef CONFIG_REISERFS_CHECK
+// This outputs a lot of unneded info on big FSes
+// reiserfs_warning ("bitmap loaded from block %d: %d free blocks\n",
+// bi->bh->b_blocknr, bi->free_count);
+#endif
+}
+
+static int read_bitmaps (struct super_block * s)
+{
+ int i, bmp;
+
+ SB_AP_BITMAP (s) = vmalloc (sizeof (struct reiserfs_bitmap_info) * SB_BMAP_NR(s));
+ if (SB_AP_BITMAP (s) == 0)
+ return 1;
+ memset (SB_AP_BITMAP (s), 0, sizeof (struct reiserfs_bitmap_info) * SB_BMAP_NR(s));
+
+ for (i = 0, bmp = REISERFS_DISK_OFFSET_IN_BYTES / s->s_blocksize + 1;
+ i < SB_BMAP_NR(s); i++, bmp = s->s_blocksize * 8 * i) {
+ SB_AP_BITMAP (s)[i].bh = sb_getblk (s, bmp);
+ if (!buffer_uptodate(SB_AP_BITMAP(s)[i].bh))
+ ll_rw_block(READ, 1, &SB_AP_BITMAP(s)[i].bh);
+ }
+ for (i = 0; i < SB_BMAP_NR(s); i++) {
+ wait_on_buffer(SB_AP_BITMAP (s)[i].bh);
+ if (!buffer_uptodate(SB_AP_BITMAP(s)[i].bh)) {
+ reiserfs_warning(s, "sh-2029: reiserfs read_bitmaps: "
+ "bitmap block (#%lu) reading failed\n",
+ SB_AP_BITMAP(s)[i].bh->b_blocknr);
+ for (i = 0; i < SB_BMAP_NR(s); i++)
+ brelse(SB_AP_BITMAP(s)[i].bh);
+ vfree(SB_AP_BITMAP(s));
+ SB_AP_BITMAP(s) = NULL;
+ return 1;
+ }
+ load_bitmap_info_data (s, SB_AP_BITMAP (s) + i);
+ }
+ return 0;
+}
+
+static int read_old_bitmaps (struct super_block * s)
+{
+ int i ;
+ struct reiserfs_super_block * rs = SB_DISK_SUPER_BLOCK(s);
+ int bmp1 = (REISERFS_OLD_DISK_OFFSET_IN_BYTES / s->s_blocksize) + 1; /* first of bitmap blocks */
+
+ /* read true bitmap */
+ SB_AP_BITMAP (s) = vmalloc (sizeof (struct reiserfs_buffer_info *) * sb_bmap_nr(rs));
+ if (SB_AP_BITMAP (s) == 0)
+ return 1;
+
+ memset (SB_AP_BITMAP (s), 0, sizeof (struct reiserfs_buffer_info *) * sb_bmap_nr(rs));
+
+ for (i = 0; i < sb_bmap_nr(rs); i ++) {
+ SB_AP_BITMAP (s)[i].bh = reiserfs_bread (s, bmp1 + i, s->s_blocksize);
+ if (!SB_AP_BITMAP (s)[i].bh)
+ return 1;
+ load_bitmap_info_data (s, SB_AP_BITMAP (s) + i);
+ }
+
+ return 0;
+}
+
+void check_bitmap (struct super_block * s)
+{
+ int i = 0;
+ int free = 0;
+ char * buf;
+
+ while (i < SB_BLOCK_COUNT (s)) {
+ buf = SB_AP_BITMAP (s)[i / (s->s_blocksize * 8)].bh->b_data;
+ if (!reiserfs_test_le_bit (i % (s->s_blocksize * 8), buf))
+ free ++;
+ i ++;
+ }
+
+ if (free != SB_FREE_BLOCKS (s))
+ reiserfs_warning (s, "vs-4000: check_bitmap: %d free blocks, must be %d\n",
+ free, SB_FREE_BLOCKS (s));
+}
+
+static int read_super_block (struct super_block * s, int size, int offset)
+{
+ struct buffer_head * bh;
+ struct reiserfs_super_block * rs;
+
+
+ bh = bread (s->s_dev, offset / size, size);
+ if (!bh) {
+ printk ("sh-2006: reiserfs read_super_block: "
+ "bread failed (dev %s, block %u, size %u)\n",
+ kdevname (s->s_dev), offset / size, size);
+ return 1;
+ }
+
+ rs = (struct reiserfs_super_block *)bh->b_data;
+ if (!is_any_reiserfs_magic_string (rs)) {
+ brelse (bh);
+ return 1;
+ }
+
+ //
+ // ok, reiserfs signature (old or new) found in at the given offset
+ //
+ s->s_blocksize = sb_blocksize(rs);
+ s->s_blocksize_bits = 0;
+ while ((1 << s->s_blocksize_bits) != s->s_blocksize)
+ s->s_blocksize_bits ++;
+
+ brelse (bh);
+
+ if (s->s_blocksize != 4096) {
+ printk("Unsupported reiserfs blocksize: %ld on %s, only 4096 bytes "
+ "blocksize is supported.\n", s->s_blocksize, kdevname (s->s_dev));
+ return 1;
+ }
+
+ if (s->s_blocksize != size)
+ set_blocksize (s->s_dev, s->s_blocksize);
+
+ bh = reiserfs_bread (s, offset / s->s_blocksize, s->s_blocksize);
+ if (!bh) {
+ printk("sh-2007: reiserfs read_super_block: "
+ "bread failed (dev %s, block %u, size %u)\n",
+ kdevname (s->s_dev), offset / size, size);
+ return 1;
+ }
+
+ rs = (struct reiserfs_super_block *)bh->b_data;
+ if (!is_any_reiserfs_magic_string (rs) || sb_blocksize(rs) !=
+ s->s_blocksize) {
+ printk ("sh-2011: read_super_block: "
+ "can't find a reiserfs filesystem on (dev %s, block %lu, size %lu)\n",
+ kdevname(s->s_dev), bh->b_blocknr, s->s_blocksize);
+ brelse (bh);
+ return 1;
+ }
+
+ if (sb_root_block(rs) == -1) {
+ brelse(bh) ;
+ printk("dev %s: Unfinished reiserfsck --rebuild-tree run detected. Please run\n"
+ "reiserfsck --rebuild-tree and wait for a completion. If that fails\n"
+ "get newer reiserfsprogs package\n", kdevname (s->s_dev));
+ return 1;
+ }
+
+ SB_BUFFER_WITH_SB (s) = bh;
+ SB_DISK_SUPER_BLOCK (s) = rs;
+ if (is_reiserfs_jr (rs)) {
+ /* magic is of non-standard journal filesystem, look at s_version to
+ find which format is in use */
+ if (sb_version(rs) == REISERFS_VERSION_2)
+ printk ("reiserfs: found format \"3.6\" with non-standard journal\n");
+ else if (sb_version(rs) == REISERFS_VERSION_1)
+ printk ("reiserfs: found format \"3.5\" with non-standard journal\n");
+ else {
+ printk ("sh-2012: read_super_block: found unknown format \"%u\" "
+ "of reiserfs with non-standard magic\n", sb_version(rs));
+ return 1;
+ }
+ }
+ else
+ /* s_version may contain incorrect information. Look at the magic
+ string */
+ printk ("reiserfs: found format \"%s\" with standard journal\n",
+ is_reiserfs_3_5 (rs) ? "3.5" : "3.6");
+ s->s_op = &reiserfs_sops;
+
+ /* new format is limited by the 32 bit wide i_blocks field, want to
+ ** be one full block below that.
+ */
+ s->s_maxbytes = (512LL << 32) - s->s_blocksize ;
+ return 0;
+}
+
+
+
+/* after journal replay, reread all bitmap and super blocks */
+static int reread_meta_blocks(struct super_block *s) {
+ int i ;
+ ll_rw_block(READ, 1, &(SB_BUFFER_WITH_SB(s))) ;
+ wait_on_buffer(SB_BUFFER_WITH_SB(s)) ;
+ if (!buffer_uptodate(SB_BUFFER_WITH_SB(s))) {
+ reiserfs_warning(s, "sh-2016: reiserfs reread_meta_blocks, "
+ "error reading the super\n") ;
+ return 1 ;
+ }
+
+ for (i = 0; i < SB_BMAP_NR(s) ; i++) {
+ ll_rw_block(READ, 1, &(SB_AP_BITMAP(s)[i].bh)) ;
+ wait_on_buffer(SB_AP_BITMAP(s)[i].bh) ;
+ if (!buffer_uptodate(SB_AP_BITMAP(s)[i].bh)) {
+ reiserfs_warning(s, "reread_meta_blocks, error reading bitmap block number %d at %ld\n", i, SB_AP_BITMAP(s)[i].bh->b_blocknr) ;
+ return 1 ;
+ }
+ }
+ return 0 ;
+
+}
+
+
+/////////////////////////////////////////////////////
+// hash detection stuff
+
+
+// if root directory is empty - we set default - Yura's - hash and
+// warn about it
+// FIXME: we look for only one name in a directory. If tea and yura
+// bith have the same value - we ask user to send report to the
+// mailing list
+__u32 find_hash_out (struct super_block * s)
+{
+ int retval;
+ struct inode * inode;
+ struct cpu_key key;
+ INITIALIZE_PATH (path);
+ struct reiserfs_dir_entry de;
+ __u32 hash = DEFAULT_HASH;
+
+ inode = s->s_root->d_inode;
+
+ do { // Some serious "goto"-hater was there ;)
+ u32 teahash, r5hash, yurahash;
+
+ make_cpu_key (&key, inode, ~0, TYPE_DIRENTRY, 3);
+ retval = search_by_entry_key (s, &key, &path, &de);
+ if (retval == IO_ERROR) {
+ pathrelse (&path);
+ return UNSET_HASH ;
+ }
+ if (retval == NAME_NOT_FOUND)
+ de.de_entry_num --;
+ set_de_name_and_namelen (&de);
+ if (deh_offset( &(de.de_deh[de.de_entry_num]) ) == DOT_DOT_OFFSET) {
+ /* allow override in this case */
+ if (reiserfs_rupasov_hash(s)) {
+ hash = YURA_HASH ;
+ }
+ reiserfs_warning(s, "reiserfs: FS seems to be empty, autodetect "
+ "is using the default hash\n");
+ break;
+ }
+ r5hash=GET_HASH_VALUE (r5_hash (de.de_name, de.de_namelen));
+ teahash=GET_HASH_VALUE (keyed_hash (de.de_name, de.de_namelen));
+ yurahash=GET_HASH_VALUE (yura_hash (de.de_name, de.de_namelen));
+ if ( ( (teahash == r5hash) && (GET_HASH_VALUE( deh_offset(&(de.de_deh[de.de_entry_num]))) == r5hash) ) ||
+ ( (teahash == yurahash) && (yurahash == GET_HASH_VALUE( deh_offset(&(de.de_deh[de.de_entry_num])))) ) ||
+ ( (r5hash == yurahash) && (yurahash == GET_HASH_VALUE( deh_offset(&(de.de_deh[de.de_entry_num])))) ) ) {
+ reiserfs_warning(s, "reiserfs: Unable to automatically detect hash"
+ "function please mount with -o hash={tea,rupasov,r5}\n");
+ hash = UNSET_HASH;
+ break;
+ }
+ if (GET_HASH_VALUE( deh_offset(&(de.de_deh[de.de_entry_num])) ) == yurahash)
+ hash = YURA_HASH;
+ else if (GET_HASH_VALUE( deh_offset(&(de.de_deh[de.de_entry_num])) ) == teahash)
+ hash = TEA_HASH;
+ else if (GET_HASH_VALUE( deh_offset(&(de.de_deh[de.de_entry_num])) ) == r5hash)
+ hash = R5_HASH;
+ else {
+ reiserfs_warning(s, "reiserfs: Unrecognised hash function\n");
+ hash = UNSET_HASH;
+ }
+ } while (0);
+
+ pathrelse (&path);
+ return hash;
+}
+
+// finds out which hash names are sorted with
+static int what_hash (struct super_block * s)
+{
+ __u32 code;
+
+ code = sb_hash_function_code(SB_DISK_SUPER_BLOCK(s));
+
+ /* reiserfs_hash_detect() == true if any of the hash mount options
+ ** were used. We must check them to make sure the user isn't
+ ** using a bad hash value
+ */
+ if (code == UNSET_HASH || reiserfs_hash_detect(s))
+ code = find_hash_out (s);
+
+ if (code != UNSET_HASH && reiserfs_hash_detect(s)) {
+ /* detection has found the hash, and we must check against the
+ ** mount options
+ */
+ if (reiserfs_rupasov_hash(s) && code != YURA_HASH) {
+ printk("REISERFS: Error, %s hash detected, "
+ "unable to force rupasov hash\n", reiserfs_hashname(code)) ;
+ code = UNSET_HASH ;
+ } else if (reiserfs_tea_hash(s) && code != TEA_HASH) {
+ printk("REISERFS: Error, %s hash detected, "
+ "unable to force tea hash\n", reiserfs_hashname(code)) ;
+ code = UNSET_HASH ;
+ } else if (reiserfs_r5_hash(s) && code != R5_HASH) {
+ printk("REISERFS: Error, %s hash detected, "
+ "unable to force r5 hash\n", reiserfs_hashname(code)) ;
+ code = UNSET_HASH ;
+ }
+ } else {
+ /* find_hash_out was not called or could not determine the hash */
+ if (reiserfs_rupasov_hash(s)) {
+ code = YURA_HASH ;
+ } else if (reiserfs_tea_hash(s)) {
+ code = TEA_HASH ;
+ } else if (reiserfs_r5_hash(s)) {
+ code = R5_HASH ;
+ }
+ }
+
+ /* if we are mounted RW, and we have a new valid hash code, update
+ ** the super
+ */
+ if (code != UNSET_HASH &&
+ !(s->s_flags & MS_RDONLY) &&
+ code != sb_hash_function_code(SB_DISK_SUPER_BLOCK(s))) {
+ set_sb_hash_function_code(SB_DISK_SUPER_BLOCK(s), code);
+ }
+ return code;
+}
+
+// return pointer to appropriate function
+static hashf_t hash_function (struct super_block * s)
+{
+ switch (what_hash (s)) {
+ case TEA_HASH:
+ reiserfs_warning (s, "Using tea hash to sort names\n");
+ return keyed_hash;
+ case YURA_HASH:
+ reiserfs_warning (s, "Using rupasov hash to sort names\n");
+ return yura_hash;
+ case R5_HASH:
+ reiserfs_warning (s, "Using r5 hash to sort names\n");
+ return r5_hash;
+ }
+ return NULL;
+}
+
+// this is used to set up correct value for old partitions
+int function2code (hashf_t func)
+{
+ if (func == keyed_hash)
+ return TEA_HASH;
+ if (func == yura_hash)
+ return YURA_HASH;
+ if (func == r5_hash)
+ return R5_HASH;
+
+ BUG() ; // should never happen
+
+ return 0;
+}
+
+static struct super_block * reiserfs_read_super (struct super_block * s, void * data, int silent)
+{
+ int size;
+ struct inode *root_inode;
+ kdev_t dev = s->s_dev;
+ int j;
+ struct reiserfs_transaction_handle th ;
+ int old_format = 0;
+ unsigned long blocks;
+ int jinit_done = 0 ;
+ struct reiserfs_iget4_args args ;
+ char *jdev_name;
+ struct reiserfs_super_block * rs;
+
+
+ memset (&s->u.reiserfs_sb, 0, sizeof (struct reiserfs_sb_info));
+ /* Set default values for options: non-aggressive tails */
+ s->u.reiserfs_sb.s_mount_opt = ( 1 << REISERFS_SMALLTAIL );
+ /* default block allocator option: skip_busy */
+ s->u.reiserfs_sb.s_alloc_options.bits = ( 1 << 5);
+ /* If file grew past 4 blocks, start preallocation blocks for it. */
+ s->u.reiserfs_sb.s_alloc_options.preallocmin = 4;
+ /* Preallocate by 8 blocks (9-1) at once */
+ s->u.reiserfs_sb.s_alloc_options.preallocsize = 9;
+
+ if (reiserfs_parse_options (s, (char *) data, &(s->u.reiserfs_sb.s_mount_opt), &blocks) == 0) {
+ return NULL;
+
+
+
+ }
+
+ if (blocks) {
+ reiserfs_warning(s,"zam-2013: reserfs resize option for remount only\n");
+ return NULL;
+ }
+
+ if (blksize_size[MAJOR(dev)] && blksize_size[MAJOR(dev)][MINOR(dev)] != 0) {
+ /* as blocksize is set for partition we use it */
+ size = blksize_size[MAJOR(dev)][MINOR(dev)];
+ } else {
+ size = BLOCK_SIZE;
+ set_blocksize (s->s_dev, BLOCK_SIZE);
+ }
+ /* try old format (undistributed bitmap, super block in 8-th 1k block of a device) */
+ if (!read_super_block (s, size, REISERFS_OLD_DISK_OFFSET_IN_BYTES))
+ old_format = 1;
+ /* try new format (64-th 1k block), which can contain reiserfs super block */
+ else if (read_super_block (s, size, REISERFS_DISK_OFFSET_IN_BYTES)) {
+ printk("sh-2021: reiserfs_read_super: can not find reiserfs on %s\n", bdevname(s->s_dev));
+ goto error;
+ }
+
+ rs = SB_DISK_SUPER_BLOCK (s);
+
+ /* Let's do basic sanity check to verify that underlying device is not
+ smaller than the filesystem. If the check fails then abort and scream,
+ because bad stuff will happen otherwise. */
+ if ( blk_size[MAJOR(dev)][MINOR(dev)] < sb_block_count(rs)*(sb_blocksize(rs)>>10) ) {
+ printk("Filesystem on %s cannot be mounted because it is bigger than the device\n", kdevname(dev));
+ printk("You may need to run fsck or increase size of your LVM partition\n");
+ printk("Or may be you forgot to reboot after fdisk when it told you to\n");
+ return NULL;
+ }
+
+ s->u.reiserfs_sb.s_mount_state = SB_REISERFS_STATE(s);
+ s->u.reiserfs_sb.s_mount_state = REISERFS_VALID_FS ;
+
+ if (old_format ? read_old_bitmaps(s) : read_bitmaps(s)) {
+ reiserfs_warning (s, "sh-2014: reiserfs_read_super: unable to read bitmap\n");
+ goto error;
+ }
+#ifdef CONFIG_REISERFS_CHECK
+ printk("reiserfs:warning: CONFIG_REISERFS_CHECK is set ON\n");
+ printk("reiserfs:warning: - it is slow mode for debugging.\n");
+#endif
+
+ /* fixme */
+ jdev_name = NULL;
+
+ if( journal_init(s, jdev_name, old_format) ) {
+ reiserfs_warning(s, "sh-2022: reiserfs_read_super: unable to initialize journal space\n") ;
+ goto error ;
+ } else {
+ jinit_done = 1 ; /* once this is set, journal_release must be called
+ ** if we error out of the mount
+ */
+ }
+ if (reread_meta_blocks(s)) {
+ reiserfs_warning(s, "sh-2015: reiserfs_read_super: unable to reread meta blocks after journal init\n") ;
+ goto error ;
+ }
+
+ if (replay_only (s))
+ goto error;
+
+ if (is_read_only(s->s_dev) && !(s->s_flags & MS_RDONLY)) {
+ reiserfs_warning(s, "clm-7000: Detected readonly device, marking FS readonly\n") ;
+ s->s_flags |= MS_RDONLY ;
+ }
+ args.objectid = REISERFS_ROOT_PARENT_OBJECTID ;
+ root_inode = iget4 (s, REISERFS_ROOT_OBJECTID, 0, (void *)(&args));
+ if (!root_inode) {
+ reiserfs_warning (s, "reiserfs_read_super: get root inode failed\n");
+ goto error;
+ }
+
+ s->s_root = d_alloc_root(root_inode);
+ if (!s->s_root) {
+ iput(root_inode);
+ goto error;
+ }
+
+ // define and initialize hash function
+ s->u.reiserfs_sb.s_hash_function = hash_function (s);
+ if (s->u.reiserfs_sb.s_hash_function == NULL) {
+ dput(s->s_root) ;
+ s->s_root = NULL ;
+ goto error ;
+ }
+
+ if (is_reiserfs_3_5 (rs) || (is_reiserfs_jr (rs) && SB_VERSION (s) == REISERFS_VERSION_1))
+ set_bit(REISERFS_3_5, &(s->u.reiserfs_sb.s_properties));
+ else
+ set_bit(REISERFS_3_6, &(s->u.reiserfs_sb.s_properties));
+
+ if (!(s->s_flags & MS_RDONLY)) {
+
+ journal_begin(&th, s, 1) ;
+ reiserfs_prepare_for_journal(s, SB_BUFFER_WITH_SB(s), 1) ;
+
+ set_sb_umount_state( rs, REISERFS_ERROR_FS );
+ set_sb_fs_state (rs, 0);
+
+ if (old_format_only(s)) {
+ /* filesystem of format 3.5 either with standard or non-standard
+ journal */
+ if (convert_reiserfs (s)) {
+ /* and -o conv is given */
+ reiserfs_warning (s, "reiserfs: converting 3.5 filesystem to the 3.6 format\n") ;
+
+ if (is_reiserfs_3_5 (rs))
+ /* put magic string of 3.6 format. 2.2 will not be able to
+ mount this filesystem anymore */
+ memcpy (rs->s_v1.s_magic, reiserfs_3_6_magic_string,
+ sizeof (reiserfs_3_6_magic_string));
+
+ set_sb_version(rs,REISERFS_VERSION_2);
+ reiserfs_convert_objectid_map_v1(s) ;
+ set_bit(REISERFS_3_6, &(s->u.reiserfs_sb.s_properties));
+ clear_bit(REISERFS_3_5, &(s->u.reiserfs_sb.s_properties));
+ }
+ }
+
+ journal_mark_dirty(&th, s, SB_BUFFER_WITH_SB (s));
+ journal_end(&th, s, 1) ;
+
+ /* look for files which were to be removed in previous session */
+ finish_unfinished (s);
+
+ s->s_dirt = 0;
+ }
+
+ // mark hash in super block: it could be unset. overwrite should be ok
+ set_sb_hash_function_code( rs, function2code(s->u.reiserfs_sb.s_hash_function ) );
+
+ handle_attrs( s );
+
+ reiserfs_proc_info_init( s );
+ reiserfs_proc_register( s, "version", reiserfs_version_in_proc );
+ reiserfs_proc_register( s, "super", reiserfs_super_in_proc );
+ reiserfs_proc_register( s, "per-level", reiserfs_per_level_in_proc );
+ reiserfs_proc_register( s, "bitmap", reiserfs_bitmap_in_proc );
+ reiserfs_proc_register( s, "on-disk-super", reiserfs_on_disk_super_in_proc );
+ reiserfs_proc_register( s, "oidmap", reiserfs_oidmap_in_proc );
+ reiserfs_proc_register( s, "journal", reiserfs_journal_in_proc );
+ init_waitqueue_head (&(s->u.reiserfs_sb.s_wait));
+
+ return s;
+
+ error:
+ if (jinit_done) { /* kill the commit thread, free journal ram */
+ journal_release_error(NULL, s) ;
+ }
+ if (SB_DISK_SUPER_BLOCK (s)) {
+ for (j = 0; j < SB_BMAP_NR (s); j ++) {
+ if (SB_AP_BITMAP (s))
+ brelse (SB_AP_BITMAP (s)[j].bh);
+ }
+ if (SB_AP_BITMAP (s))
+ vfree (SB_AP_BITMAP (s));
+ }
+ if (SB_BUFFER_WITH_SB (s))
+ brelse(SB_BUFFER_WITH_SB (s));
+
+ return NULL;
+}
+
+
+static int reiserfs_statfs (struct super_block * s, struct statfs * buf)
+{
+ struct reiserfs_super_block * rs = SB_DISK_SUPER_BLOCK (s);
+
+ buf->f_namelen = (REISERFS_MAX_NAME (s->s_blocksize));
+ buf->f_ffree = -1;
+ buf->f_files = -1;
+ buf->f_bfree = sb_free_blocks(rs);
+ buf->f_bavail = buf->f_bfree;
+ buf->f_blocks = sb_block_count(rs) - sb_bmap_nr(rs) - 1;
+ buf->f_bsize = s->s_blocksize;
+ /* changed to accomodate gcc folks.*/
+ buf->f_type = REISERFS_SUPER_MAGIC;
+ return 0;
+}
+
+static DECLARE_FSTYPE_DEV(reiserfs_fs_type,"reiserfs",reiserfs_read_super);
+
+static int __init init_reiserfs_fs (void)
+{
+ reiserfs_proc_info_global_init();
+ reiserfs_proc_register_global( "version",
+ reiserfs_global_version_in_proc );
+ return register_filesystem(&reiserfs_fs_type);
+}
+
+MODULE_DESCRIPTION("ReiserFS journaled filesystem");
+MODULE_AUTHOR("Hans Reiser <reiser@namesys.com>");
+MODULE_LICENSE("GPL");
+EXPORT_NO_SYMBOLS;
+
+static void __exit exit_reiserfs_fs(void)
+{
+ reiserfs_proc_unregister_global( "version" );
+ reiserfs_proc_info_global_done();
+ unregister_filesystem(&reiserfs_fs_type);
+}
+
+
+module_init(init_reiserfs_fs) ;
+module_exit(exit_reiserfs_fs) ;
+
+
+
diff --git a/fs/reiserfs/tail_conversion.c b/fs/reiserfs/tail_conversion.c
new file mode 100644
index 00000000000000..df6d014ec9a53b
--- /dev/null
+++ b/fs/reiserfs/tail_conversion.c
@@ -0,0 +1,299 @@
+/*
+ * Copyright 1999-2002 Hans Reiser, see reiserfs/README for licensing and copyright details
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/pagemap.h>
+#include <linux/reiserfs_fs.h>
+#include <linux/locks.h>
+
+/* access to tail : when one is going to read tail it must make sure, that is not running.
+ direct2indirect and indirect2direct can not run concurrently */
+
+
+/* Converts direct items to an unformatted node. Panics if file has no
+ tail. -ENOSPC if no disk space for conversion */
+/* path points to first direct item of the file regarless of how many of
+ them are there */
+int direct2indirect (struct reiserfs_transaction_handle *th, struct inode * inode,
+ struct path * path, struct buffer_head * unbh,
+ loff_t tail_offset)
+{
+ struct super_block * sb = inode->i_sb;
+ struct buffer_head *up_to_date_bh ;
+ struct item_head * p_le_ih = PATH_PITEM_HEAD (path);
+ unsigned long total_tail = 0 ;
+ struct cpu_key end_key; /* Key to search for the last byte of the
+ converted item. */
+ struct item_head ind_ih; /* new indirect item to be inserted or
+ key of unfm pointer to be pasted */
+ int n_blk_size,
+ n_retval; /* returned value for reiserfs_insert_item and clones */
+ unp_t unfm_ptr; /* Handle on an unformatted node
+ that will be inserted in the
+ tree. */
+
+
+ sb->u.reiserfs_sb.s_direct2indirect ++;
+
+ n_blk_size = sb->s_blocksize;
+
+ /* and key to search for append or insert pointer to the new
+ unformatted node. */
+ copy_item_head (&ind_ih, p_le_ih);
+ set_le_ih_k_offset (&ind_ih, tail_offset);
+ set_le_ih_k_type (&ind_ih, TYPE_INDIRECT);
+
+ /* Set the key to search for the place for new unfm pointer */
+ make_cpu_key (&end_key, inode, tail_offset, TYPE_INDIRECT, 4);
+
+ // FIXME: we could avoid this
+ if ( search_for_position_by_key (sb, &end_key, path) == POSITION_FOUND ) {
+ reiserfs_warning (sb, "PAP-14030: direct2indirect: "
+ "pasted or inserted byte exists in the tree %K. "
+ "Use fsck to repair.\n", &end_key);
+ pathrelse(path);
+ return -EIO;
+ }
+
+ p_le_ih = PATH_PITEM_HEAD (path);
+
+ unfm_ptr = cpu_to_le32 (unbh->b_blocknr);
+
+ if ( is_statdata_le_ih (p_le_ih) ) {
+ /* Insert new indirect item. */
+ set_ih_free_space (&ind_ih, 0); /* delete at nearest future */
+ put_ih_item_len( &ind_ih, UNFM_P_SIZE );
+ PATH_LAST_POSITION (path)++;
+ n_retval = reiserfs_insert_item (th, path, &end_key, &ind_ih,
+ (char *)&unfm_ptr);
+ } else {
+ /* Paste into last indirect item of an object. */
+ n_retval = reiserfs_paste_into_item(th, path, &end_key,
+ (char *)&unfm_ptr, UNFM_P_SIZE);
+ }
+ if ( n_retval ) {
+ return n_retval;
+ }
+
+ // note: from here there are two keys which have matching first
+ // three key components. They only differ by the fourth one.
+
+
+ /* Set the key to search for the direct items of the file */
+ make_cpu_key (&end_key, inode, max_reiserfs_offset (inode), TYPE_DIRECT, 4);
+
+ /* Move bytes from the direct items to the new unformatted node
+ and delete them. */
+ while (1) {
+ int tail_size;
+
+ /* end_key.k_offset is set so, that we will always have found
+ last item of the file */
+ if ( search_for_position_by_key (sb, &end_key, path) == POSITION_FOUND )
+ reiserfs_panic (sb, "PAP-14050: direct2indirect: "
+ "direct item (%K) not found", &end_key);
+ p_le_ih = PATH_PITEM_HEAD (path);
+ RFALSE( !is_direct_le_ih (p_le_ih),
+ "vs-14055: direct item expected(%K), found %h",
+ &end_key, p_le_ih);
+ tail_size = (le_ih_k_offset (p_le_ih) & (n_blk_size - 1))
+ + ih_item_len(p_le_ih) - 1;
+
+ /* we only send the unbh pointer if the buffer is not up to date.
+ ** this avoids overwriting good data from writepage() with old data
+ ** from the disk or buffer cache
+ ** Special case: unbh->b_page will be NULL if we are coming through
+ ** DIRECT_IO handler here.
+ */
+ if ( !unbh->b_page || buffer_uptodate(unbh) || Page_Uptodate(unbh->b_page)) {
+ up_to_date_bh = NULL ;
+ } else {
+ up_to_date_bh = unbh ;
+ }
+ n_retval = reiserfs_delete_item (th, path, &end_key, inode,
+ up_to_date_bh) ;
+
+ total_tail += n_retval ;
+ if (tail_size == n_retval)
+ // done: file does not have direct items anymore
+ break;
+
+ }
+ /* if we've copied bytes from disk into the page, we need to zero
+ ** out the unused part of the block (it was not up to date before)
+ ** the page is still kmapped (by whoever called reiserfs_get_block)
+ */
+ if (up_to_date_bh) {
+ unsigned pgoff = (tail_offset + total_tail - 1) & (PAGE_CACHE_SIZE - 1);
+ memset(page_address(unbh->b_page) + pgoff, 0, n_blk_size - total_tail) ;
+ }
+
+ inode->u.reiserfs_i.i_first_direct_byte = U32_MAX;
+
+ reiserfs_update_tail_transaction(inode);
+ return 0;
+}
+
+
+/* stolen from fs/buffer.c */
+void reiserfs_unmap_buffer(struct buffer_head *bh) {
+ if (buffer_mapped(bh)) {
+ if (buffer_journaled(bh) || buffer_journal_dirty(bh)) {
+ BUG() ;
+ }
+ mark_buffer_clean(bh) ;
+ lock_buffer(bh) ;
+ clear_bit(BH_Mapped, &bh->b_state) ;
+ clear_bit(BH_Req, &bh->b_state) ;
+ clear_bit(BH_New, &bh->b_state) ;
+ unlock_buffer(bh) ;
+ }
+}
+
+static void
+unmap_buffers(struct page *page, loff_t pos) {
+ struct buffer_head *bh ;
+ struct buffer_head *head ;
+ struct buffer_head *next ;
+ unsigned long tail_index ;
+ unsigned long cur_index ;
+
+ if (page) {
+ if (page->buffers) {
+ tail_index = pos & (PAGE_CACHE_SIZE - 1) ;
+ cur_index = 0 ;
+ head = page->buffers ;
+ bh = head ;
+ do {
+ next = bh->b_this_page ;
+
+ /* we want to unmap the buffers that contain the tail, and
+ ** all the buffers after it (since the tail must be at the
+ ** end of the file). We don't want to unmap file data
+ ** before the tail, since it might be dirty and waiting to
+ ** reach disk
+ */
+ cur_index += bh->b_size ;
+ if (cur_index > tail_index) {
+ reiserfs_unmap_buffer(bh) ;
+ }
+ bh = next ;
+ } while (bh != head) ;
+ }
+ }
+}
+
+/* this first locks inode (neither reads nor sync are permitted),
+ reads tail through page cache, insert direct item. When direct item
+ inserted successfully inode is left locked. Return value is always
+ what we expect from it (number of cut bytes). But when tail remains
+ in the unformatted node, we set mode to SKIP_BALANCING and unlock
+ inode */
+int indirect2direct (struct reiserfs_transaction_handle *th,
+ struct inode * p_s_inode,
+ struct page *page,
+ struct path * p_s_path, /* path to the indirect item. */
+ const struct cpu_key * p_s_item_key, /* Key to look for unformatted node pointer to be cut. */
+ loff_t n_new_file_size, /* New file size. */
+ char * p_c_mode)
+{
+ struct super_block * p_s_sb = p_s_inode->i_sb;
+ struct item_head s_ih;
+ unsigned long n_block_size = p_s_sb->s_blocksize;
+ char * tail;
+ int tail_len, round_tail_len;
+ loff_t pos, pos1; /* position of first byte of the tail */
+ struct cpu_key key;
+
+ p_s_sb->u.reiserfs_sb.s_indirect2direct ++;
+
+ *p_c_mode = M_SKIP_BALANCING;
+
+ /* store item head path points to. */
+ copy_item_head (&s_ih, PATH_PITEM_HEAD(p_s_path));
+
+ tail_len = (n_new_file_size & (n_block_size - 1));
+ if (get_inode_sd_version (p_s_inode) == STAT_DATA_V2)
+ round_tail_len = ROUND_UP (tail_len);
+ else
+ round_tail_len = tail_len;
+
+ pos = le_ih_k_offset (&s_ih) - 1 + (ih_item_len(&s_ih) / UNFM_P_SIZE - 1) * p_s_sb->s_blocksize;
+ pos1 = pos;
+
+ // we are protected by i_sem. The tail can not disapper, not
+ // append can be done either
+ // we are in truncate or packing tail in file_release
+
+ tail = (char *)kmap(page) ; /* this can schedule */
+
+ if (path_changed (&s_ih, p_s_path)) {
+ /* re-search indirect item */
+ if ( search_for_position_by_key (p_s_sb, p_s_item_key, p_s_path) == POSITION_NOT_FOUND )
+ reiserfs_panic(p_s_sb, "PAP-5520: indirect2direct: "
+ "item to be converted %K does not exist", p_s_item_key);
+ copy_item_head(&s_ih, PATH_PITEM_HEAD(p_s_path));
+#ifdef CONFIG_REISERFS_CHECK
+ pos = le_ih_k_offset (&s_ih) - 1 +
+ (ih_item_len(&s_ih) / UNFM_P_SIZE - 1) * p_s_sb->s_blocksize;
+ if (pos != pos1)
+ reiserfs_panic (p_s_sb, "vs-5530: indirect2direct: "
+ "tail position changed while we were reading it");
+#endif
+ }
+
+
+ /* Set direct item header to insert. */
+ make_le_item_head (&s_ih, 0, get_inode_item_key_version (p_s_inode), pos1 + 1,
+ TYPE_DIRECT, round_tail_len, 0xffff/*ih_free_space*/);
+
+ /* we want a pointer to the first byte of the tail in the page.
+ ** the page was locked and this part of the page was up to date when
+ ** indirect2direct was called, so we know the bytes are still valid
+ */
+ tail = tail + (pos & (PAGE_CACHE_SIZE - 1)) ;
+
+ PATH_LAST_POSITION(p_s_path)++;
+
+ key = *p_s_item_key;
+ set_cpu_key_k_type (&key, TYPE_DIRECT);
+ key.key_length = 4;
+ /* Insert tail as new direct item in the tree */
+ if ( reiserfs_insert_item(th, p_s_path, &key, &s_ih,
+ tail ? tail : NULL) < 0 ) {
+ /* No disk memory. So we can not convert last unformatted node
+ to the direct item. In this case we used to adjust
+ indirect items's ih_free_space. Now ih_free_space is not
+ used, it would be ideal to write zeros to corresponding
+ unformatted node. For now i_size is considered as guard for
+ going out of file size */
+ kunmap(page) ;
+ return n_block_size - round_tail_len;
+ }
+ kunmap(page) ;
+
+ /* this will invalidate all the buffers in the page after
+ ** pos1
+ */
+ unmap_buffers(page, pos1) ;
+
+ // note: we have now the same as in above direct2indirect
+ // conversion: there are two keys which have matching first three
+ // key components. They only differ by the fouhth one.
+
+ /* We have inserted new direct item and must remove last
+ unformatted node. */
+ p_s_inode->i_blocks += (p_s_sb->s_blocksize / 512);
+ *p_c_mode = M_CUT;
+
+ /* we store position of first direct item in the in-core inode */
+ //mark_file_with_tail (p_s_inode, pos1 + 1);
+ p_s_inode->u.reiserfs_i.i_first_direct_byte = pos1 + 1;
+
+ return n_block_size - round_tail_len;
+}
+
+
+
diff --git a/fs/reiserfs/version.c b/fs/reiserfs/version.c
new file mode 100644
index 00000000000000..e06bd12be8977c
--- /dev/null
+++ b/fs/reiserfs/version.c
@@ -0,0 +1,7 @@
+/*
+ * Copyright 2000-2002 by Hans Reiser, licensing governed by reiserfs/README
+ */
+
+char *reiserfs_get_version_string(void) {
+ return "ReiserFS version 3.6.25" ;
+}
diff --git a/fs/romfs/Makefile b/fs/romfs/Makefile
new file mode 100644
index 00000000000000..65a88e40b5876c
--- /dev/null
+++ b/fs/romfs/Makefile
@@ -0,0 +1,15 @@
+#
+# Makefile for the linux romfs filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile.
+
+O_TARGET := romfs.o
+
+obj-y := inode.o
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/romfs/inode.c b/fs/romfs/inode.c
new file mode 100644
index 00000000000000..9c791cbce08465
--- /dev/null
+++ b/fs/romfs/inode.c
@@ -0,0 +1,551 @@
+/*
+ * ROMFS file system, Linux implementation
+ *
+ * Copyright (C) 1997-1999 Janos Farkas <chexum@shadow.banki.hu>
+ *
+ * Using parts of the minix filesystem
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * and parts of the affs filesystem additionally
+ * Copyright (C) 1993 Ray Burr
+ * Copyright (C) 1996 Hans-Joachim Widmaier
+ *
+ * This program 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 of the License, or (at your option) any later version.
+ *
+ * Changes
+ * Changed for 2.1.19 modules
+ * Jan 1997 Initial release
+ * Jun 1997 2.1.43+ changes
+ * Proper page locking in readpage
+ * Changed to work with 2.1.45+ fs
+ * Jul 1997 Fixed follow_link
+ * 2.1.47
+ * lookup shouldn't return -ENOENT
+ * from Horst von Brand:
+ * fail on wrong checksum
+ * double unlock_super was possible
+ * correct namelen for statfs
+ * spotted by Bill Hawes:
+ * readlink shouldn't iput()
+ * Jun 1998 2.1.106 from Avery Pennarun: glibc scandir()
+ * exposed a problem in readdir
+ * 2.1.107 code-freeze spellchecker run
+ * Aug 1998 2.1.118+ VFS changes
+ * Sep 1998 2.1.122 another VFS change (follow_link)
+ * Apr 1999 2.2.7 no more EBADF checking in
+ * lookup/readdir, use ERR_PTR
+ * Jun 1999 2.3.6 d_alloc_root use changed
+ * 2.3.9 clean up usage of ENOENT/negative
+ * dentries in lookup
+ * clean up page flags setting
+ * (error, uptodate, locking) in
+ * in readpage
+ * use init_special_inode for
+ * fifos/sockets (and streamline) in
+ * read_inode, fix _ops table order
+ * Aug 1999 2.3.16 __initfunc() => __init change
+ * Oct 1999 2.3.24 page->owner hack obsoleted
+ * Nov 1999 2.3.27 2.3.25+ page->offset => index change
+ */
+
+/* todo:
+ * - see Documentation/filesystems/romfs.txt
+ * - use allocated, not stack memory for file names?
+ * - considering write access...
+ * - network (tftp) files?
+ * - merge back some _op tables
+ */
+
+/*
+ * Sorry about some optimizations and for some goto's. I just wanted
+ * to squeeze some more bytes out of this code.. :)
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/romfs_fs.h>
+#include <linux/fs.h>
+#include <linux/locks.h>
+#include <linux/init.h>
+#include <linux/smp_lock.h>
+
+#include <asm/uaccess.h>
+
+static __s32
+romfs_checksum(void *data, int size)
+{
+ __s32 sum, *ptr;
+
+ sum = 0; ptr = data;
+ size>>=2;
+ while (size>0) {
+ sum += ntohl(*ptr++);
+ size--;
+ }
+ return sum;
+}
+
+static struct super_operations romfs_ops;
+
+static struct super_block *
+romfs_read_super(struct super_block *s, void *data, int silent)
+{
+ struct buffer_head *bh;
+ kdev_t dev = s->s_dev;
+ struct romfs_super_block *rsb;
+ int sz;
+
+ /* I would parse the options here, but there are none.. :) */
+
+ set_blocksize(dev, ROMBSIZE);
+ s->s_blocksize = ROMBSIZE;
+ s->s_blocksize_bits = ROMBSBITS;
+ s->u.generic_sbp = (void *) 0;
+ s->s_maxbytes = 0xFFFFFFFF;
+
+ bh = sb_bread(s, 0);
+ if (!bh) {
+ /* XXX merge with other printk? */
+ printk ("romfs: unable to read superblock\n");
+ goto outnobh;
+ }
+
+ rsb = (struct romfs_super_block *)bh->b_data;
+ sz = ntohl(rsb->size);
+ if (rsb->word0 != ROMSB_WORD0 || rsb->word1 != ROMSB_WORD1
+ || sz < ROMFH_SIZE) {
+ if (!silent)
+ printk ("VFS: Can't find a romfs filesystem on dev "
+ "%s.\n", kdevname(dev));
+ goto out;
+ }
+ if (romfs_checksum(rsb, min_t(int, sz, 512))) {
+ printk ("romfs: bad initial checksum on dev "
+ "%s.\n", kdevname(dev));
+ goto out;
+ }
+
+ s->s_magic = ROMFS_MAGIC;
+ s->u.romfs_sb.s_maxsize = sz;
+
+ s->s_flags |= MS_RDONLY;
+
+ /* Find the start of the fs */
+ sz = (ROMFH_SIZE +
+ strnlen(rsb->name, ROMFS_MAXFN) + 1 + ROMFH_PAD)
+ & ROMFH_MASK;
+
+ brelse(bh);
+
+ s->s_op = &romfs_ops;
+ s->s_root = d_alloc_root(iget(s, sz));
+
+ if (!s->s_root)
+ goto outnobh;
+
+ /* Ehrhm; sorry.. :) And thanks to Hans-Joachim Widmaier :) */
+ if (0) {
+out:
+ brelse(bh);
+outnobh:
+ s = NULL;
+ }
+
+ return s;
+}
+
+/* That's simple too. */
+
+static int
+romfs_statfs(struct super_block *sb, struct statfs *buf)
+{
+ buf->f_type = ROMFS_MAGIC;
+ buf->f_bsize = ROMBSIZE;
+ buf->f_bfree = buf->f_bavail = buf->f_ffree;
+ buf->f_blocks = (sb->u.romfs_sb.s_maxsize+ROMBSIZE-1)>>ROMBSBITS;
+ buf->f_namelen = ROMFS_MAXFN;
+ return 0;
+}
+
+/* some helper routines */
+
+static int
+romfs_strnlen(struct inode *i, unsigned long offset, unsigned long count)
+{
+ struct buffer_head *bh;
+ unsigned long avail, maxsize, res;
+
+ maxsize = i->i_sb->u.romfs_sb.s_maxsize;
+ if (offset >= maxsize)
+ return -1;
+
+ /* strnlen is almost always valid */
+ if (count > maxsize || offset+count > maxsize)
+ count = maxsize-offset;
+
+ bh = sb_bread(i->i_sb, offset>>ROMBSBITS);
+ if (!bh)
+ return -1; /* error */
+
+ avail = ROMBSIZE - (offset & ROMBMASK);
+ maxsize = min_t(unsigned long, count, avail);
+ res = strnlen(((char *)bh->b_data)+(offset&ROMBMASK), maxsize);
+ brelse(bh);
+
+ if (res < maxsize)
+ return res; /* found all of it */
+
+ while (res < count) {
+ offset += maxsize;
+
+ bh = sb_bread(i->i_sb, offset>>ROMBSBITS);
+ if (!bh)
+ return -1;
+ maxsize = min_t(unsigned long, count - res, ROMBSIZE);
+ avail = strnlen(bh->b_data, maxsize);
+ res += avail;
+ brelse(bh);
+ if (avail < maxsize)
+ return res;
+ }
+ return res;
+}
+
+static int
+romfs_copyfrom(struct inode *i, void *dest, unsigned long offset, unsigned long count)
+{
+ struct buffer_head *bh;
+ unsigned long avail, maxsize, res;
+
+ maxsize = i->i_sb->u.romfs_sb.s_maxsize;
+ if (offset >= maxsize || count > maxsize || offset+count>maxsize)
+ return -1;
+
+ bh = sb_bread(i->i_sb, offset>>ROMBSBITS);
+ if (!bh)
+ return -1; /* error */
+
+ avail = ROMBSIZE - (offset & ROMBMASK);
+ maxsize = min_t(unsigned long, count, avail);
+ memcpy(dest, ((char *)bh->b_data) + (offset & ROMBMASK), maxsize);
+ brelse(bh);
+
+ res = maxsize; /* all of it */
+
+ while (res < count) {
+ offset += maxsize;
+ dest += maxsize;
+
+ bh = sb_bread(i->i_sb, offset>>ROMBSBITS);
+ if (!bh)
+ return -1;
+ maxsize = min_t(unsigned long, count - res, ROMBSIZE);
+ memcpy(dest, bh->b_data, maxsize);
+ brelse(bh);
+ res += maxsize;
+ }
+ return res;
+}
+
+static unsigned char romfs_dtype_table[] = {
+ DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_SOCK, DT_FIFO
+};
+
+static int
+romfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+ struct inode *i = filp->f_dentry->d_inode;
+ struct romfs_inode ri;
+ unsigned long offset, maxoff;
+ int j, ino, nextfh;
+ int stored = 0;
+ char fsname[ROMFS_MAXFN]; /* XXX dynamic? */
+
+ maxoff = i->i_sb->u.romfs_sb.s_maxsize;
+
+ offset = filp->f_pos;
+ if (!offset) {
+ offset = i->i_ino & ROMFH_MASK;
+ if (romfs_copyfrom(i, &ri, offset, ROMFH_SIZE) <= 0)
+ return stored;
+ offset = ntohl(ri.spec) & ROMFH_MASK;
+ }
+
+ /* Not really failsafe, but we are read-only... */
+ for(;;) {
+ if (!offset || offset >= maxoff) {
+ offset = maxoff;
+ filp->f_pos = offset;
+ return stored;
+ }
+ filp->f_pos = offset;
+
+ /* Fetch inode info */
+ if (romfs_copyfrom(i, &ri, offset, ROMFH_SIZE) <= 0)
+ return stored;
+
+ j = romfs_strnlen(i, offset+ROMFH_SIZE, sizeof(fsname)-1);
+ if (j < 0)
+ return stored;
+
+ fsname[j]=0;
+ romfs_copyfrom(i, fsname, offset+ROMFH_SIZE, j);
+
+ ino = offset;
+ nextfh = ntohl(ri.next);
+ if ((nextfh & ROMFH_TYPE) == ROMFH_HRD)
+ ino = ntohl(ri.spec);
+ if (filldir(dirent, fsname, j, offset, ino,
+ romfs_dtype_table[nextfh & ROMFH_TYPE]) < 0) {
+ return stored;
+ }
+ stored++;
+ offset = nextfh & ROMFH_MASK;
+ }
+}
+
+static struct dentry *
+romfs_lookup(struct inode *dir, struct dentry *dentry)
+{
+ unsigned long offset, maxoff;
+ int fslen, res;
+ struct inode *inode;
+ char fsname[ROMFS_MAXFN]; /* XXX dynamic? */
+ struct romfs_inode ri;
+ const char *name; /* got from dentry */
+ int len;
+
+ res = -EACCES; /* placeholder for "no data here" */
+ offset = dir->i_ino & ROMFH_MASK;
+ if (romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE) <= 0)
+ goto out;
+
+ maxoff = dir->i_sb->u.romfs_sb.s_maxsize;
+ offset = ntohl(ri.spec) & ROMFH_MASK;
+
+ /* OK, now find the file whose name is in "dentry" in the
+ * directory specified by "dir". */
+
+ name = dentry->d_name.name;
+ len = dentry->d_name.len;
+
+ for(;;) {
+ if (!offset || offset >= maxoff)
+ goto out0;
+ if (romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE) <= 0)
+ goto out;
+
+ /* try to match the first 16 bytes of name */
+ fslen = romfs_strnlen(dir, offset+ROMFH_SIZE, ROMFH_SIZE);
+ if (len < ROMFH_SIZE) {
+ if (len == fslen) {
+ /* both are shorter, and same size */
+ romfs_copyfrom(dir, fsname, offset+ROMFH_SIZE, len+1);
+ if (strncmp (name, fsname, len) == 0)
+ break;
+ }
+ } else if (fslen >= ROMFH_SIZE) {
+ /* both are longer; XXX optimize max size */
+ fslen = romfs_strnlen(dir, offset+ROMFH_SIZE, sizeof(fsname)-1);
+ if (len == fslen) {
+ romfs_copyfrom(dir, fsname, offset+ROMFH_SIZE, len+1);
+ if (strncmp(name, fsname, len) == 0)
+ break;
+ }
+ }
+ /* next entry */
+ offset = ntohl(ri.next) & ROMFH_MASK;
+ }
+
+ /* Hard link handling */
+ if ((ntohl(ri.next) & ROMFH_TYPE) == ROMFH_HRD)
+ offset = ntohl(ri.spec) & ROMFH_MASK;
+
+ if ((inode = iget(dir->i_sb, offset)))
+ goto outi;
+
+ /*
+ * it's a bit funky, _lookup needs to return an error code
+ * (negative) or a NULL, both as a dentry. ENOENT should not
+ * be returned, instead we need to create a negative dentry by
+ * d_add(dentry, NULL); and return 0 as no error.
+ * (Although as I see, it only matters on writable file
+ * systems).
+ */
+
+out0: inode = NULL;
+outi: res = 0;
+ d_add (dentry, inode);
+
+out: return ERR_PTR(res);
+}
+
+/*
+ * Ok, we do readpage, to be able to execute programs. Unfortunately,
+ * we can't use bmap, since we may have looser alignments.
+ */
+
+static int
+romfs_readpage(struct file *file, struct page * page)
+{
+ struct inode *inode = page->mapping->host;
+ unsigned long offset, avail, readlen;
+ void *buf;
+ int result = -EIO;
+
+ page_cache_get(page);
+ lock_kernel();
+ buf = kmap(page);
+ if (!buf)
+ goto err_out;
+
+ /* 32 bit warning -- but not for us :) */
+ offset = page->index << PAGE_CACHE_SHIFT;
+ if (offset < inode->i_size) {
+ avail = inode->i_size-offset;
+ readlen = min_t(unsigned long, avail, PAGE_SIZE);
+ if (romfs_copyfrom(inode, buf, inode->u.romfs_i.i_dataoffset+offset, readlen) == readlen) {
+ if (readlen < PAGE_SIZE) {
+ memset(buf + readlen,0,PAGE_SIZE-readlen);
+ }
+ SetPageUptodate(page);
+ result = 0;
+ }
+ }
+ if (result) {
+ memset(buf, 0, PAGE_SIZE);
+ SetPageError(page);
+ }
+ flush_dcache_page(page);
+
+ UnlockPage(page);
+
+ kunmap(page);
+err_out:
+ page_cache_release(page);
+ unlock_kernel();
+
+ return result;
+}
+
+/* Mapping from our types to the kernel */
+
+static struct address_space_operations romfs_aops = {
+ readpage: romfs_readpage
+};
+
+static struct file_operations romfs_dir_operations = {
+ read: generic_read_dir,
+ readdir: romfs_readdir,
+};
+
+static struct inode_operations romfs_dir_inode_operations = {
+ lookup: romfs_lookup,
+};
+
+static mode_t romfs_modemap[] =
+{
+ 0, S_IFDIR+0644, S_IFREG+0644, S_IFLNK+0777,
+ S_IFBLK+0600, S_IFCHR+0600, S_IFSOCK+0644, S_IFIFO+0644
+};
+
+static void
+romfs_read_inode(struct inode *i)
+{
+ int nextfh, ino;
+ struct romfs_inode ri;
+
+ ino = i->i_ino & ROMFH_MASK;
+ i->i_mode = 0;
+
+ /* Loop for finding the real hard link */
+ for(;;) {
+ if (romfs_copyfrom(i, &ri, ino, ROMFH_SIZE) <= 0) {
+ printk("romfs: read error for inode 0x%x\n", ino);
+ return;
+ }
+ /* XXX: do romfs_checksum here too (with name) */
+
+ nextfh = ntohl(ri.next);
+ if ((nextfh & ROMFH_TYPE) != ROMFH_HRD)
+ break;
+
+ ino = ntohl(ri.spec) & ROMFH_MASK;
+ }
+
+ i->i_nlink = 1; /* Hard to decide.. */
+ i->i_size = ntohl(ri.size);
+ i->i_mtime = i->i_atime = i->i_ctime = 0;
+ i->i_uid = i->i_gid = 0;
+
+ /* Precalculate the data offset */
+ ino = romfs_strnlen(i, ino+ROMFH_SIZE, ROMFS_MAXFN);
+ if (ino >= 0)
+ ino = ((ROMFH_SIZE+ino+1+ROMFH_PAD)&ROMFH_MASK);
+ else
+ ino = 0;
+
+ i->u.romfs_i.i_metasize = ino;
+ i->u.romfs_i.i_dataoffset = ino+(i->i_ino&ROMFH_MASK);
+
+ /* Compute permissions */
+ ino = romfs_modemap[nextfh & ROMFH_TYPE];
+ /* only "normal" files have ops */
+ switch (nextfh & ROMFH_TYPE) {
+ case 1:
+ i->i_size = i->u.romfs_i.i_metasize;
+ i->i_op = &romfs_dir_inode_operations;
+ i->i_fop = &romfs_dir_operations;
+ if (nextfh & ROMFH_EXEC)
+ ino |= S_IXUGO;
+ i->i_mode = ino;
+ break;
+ case 2:
+ i->i_fop = &generic_ro_fops;
+ i->i_data.a_ops = &romfs_aops;
+ if (nextfh & ROMFH_EXEC)
+ ino |= S_IXUGO;
+ i->i_mode = ino;
+ break;
+ case 3:
+ i->i_op = &page_symlink_inode_operations;
+ i->i_data.a_ops = &romfs_aops;
+ i->i_mode = ino | S_IRWXUGO;
+ break;
+ default:
+ /* depending on MBZ for sock/fifos */
+ nextfh = ntohl(ri.spec);
+ nextfh = kdev_t_to_nr(MKDEV(nextfh>>16,nextfh&0xffff));
+ init_special_inode(i, ino, nextfh);
+ }
+}
+
+static struct super_operations romfs_ops = {
+ read_inode: romfs_read_inode,
+ statfs: romfs_statfs,
+};
+
+static DECLARE_FSTYPE_DEV(romfs_fs_type, "romfs", romfs_read_super);
+
+static int __init init_romfs_fs(void)
+{
+ return register_filesystem(&romfs_fs_type);
+}
+
+static void __exit exit_romfs_fs(void)
+{
+ unregister_filesystem(&romfs_fs_type);
+}
+
+/* Yes, works even as a module... :) */
+
+EXPORT_NO_SYMBOLS;
+
+module_init(init_romfs_fs)
+module_exit(exit_romfs_fs)
+MODULE_LICENSE("GPL");
diff --git a/fs/select.c b/fs/select.c
new file mode 100644
index 00000000000000..65ff400a89f62c
--- /dev/null
+++ b/fs/select.c
@@ -0,0 +1,496 @@
+/*
+ * This file contains the procedures for the handling of select and poll
+ *
+ * Created for Linux based loosely upon Mathius Lattner's minix
+ * patches by Peter MacDonald. Heavily edited by Linus.
+ *
+ * 4 February 1994
+ * COFF/ELF binary emulation. If the process has the STICKY_TIMEOUTS
+ * flag set in its personality we do *not* modify the given timeout
+ * parameter to reflect time remaining.
+ *
+ * 24 January 2000
+ * Changed sys_poll()/do_poll() to use PAGE_SIZE chunk-based allocation
+ * of fds to overcome nfds < 16390 descriptors limit (Tigran Aivazian).
+ */
+
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/poll.h>
+#include <linux/personality.h> /* for STICKY_TIMEOUTS */
+#include <linux/file.h>
+
+#include <asm/uaccess.h>
+
+#define ROUND_UP(x,y) (((x)+(y)-1)/(y))
+#define DEFAULT_POLLMASK (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)
+
+struct poll_table_entry {
+ struct file * filp;
+ wait_queue_t wait;
+ wait_queue_head_t * wait_address;
+};
+
+struct poll_table_page {
+ struct poll_table_page * next;
+ struct poll_table_entry * entry;
+ struct poll_table_entry entries[0];
+};
+
+#define POLL_TABLE_FULL(table) \
+ ((unsigned long)((table)->entry+1) > PAGE_SIZE + (unsigned long)(table))
+
+/*
+ * Ok, Peter made a complicated, but straightforward multiple_wait() function.
+ * I have rewritten this, taking some shortcuts: This code may not be easy to
+ * follow, but it should be free of race-conditions, and it's practical. If you
+ * understand what I'm doing here, then you understand how the linux
+ * sleep/wakeup mechanism works.
+ *
+ * Two very simple procedures, poll_wait() and poll_freewait() make all the
+ * work. poll_wait() is an inline-function defined in <linux/poll.h>,
+ * as all select/poll functions have to call it to add an entry to the
+ * poll table.
+ */
+
+void poll_freewait(poll_table* pt)
+{
+ struct poll_table_page * p = pt->table;
+ while (p) {
+ struct poll_table_entry * entry;
+ struct poll_table_page *old;
+
+ entry = p->entry;
+ do {
+ entry--;
+ remove_wait_queue(entry->wait_address,&entry->wait);
+ fput(entry->filp);
+ } while (entry > p->entries);
+ old = p;
+ p = p->next;
+ free_page((unsigned long) old);
+ }
+}
+
+void __pollwait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
+{
+ struct poll_table_page *table = p->table;
+
+ if (!table || POLL_TABLE_FULL(table)) {
+ struct poll_table_page *new_table;
+
+ new_table = (struct poll_table_page *) __get_free_page(GFP_KERNEL);
+ if (!new_table) {
+ p->error = -ENOMEM;
+ __set_current_state(TASK_RUNNING);
+ return;
+ }
+ new_table->entry = new_table->entries;
+ new_table->next = table;
+ p->table = new_table;
+ table = new_table;
+ }
+
+ /* Add a new entry */
+ {
+ struct poll_table_entry * entry = table->entry;
+ table->entry = entry+1;
+ get_file(filp);
+ entry->filp = filp;
+ entry->wait_address = wait_address;
+ init_waitqueue_entry(&entry->wait, current);
+ add_wait_queue(wait_address,&entry->wait);
+ }
+}
+
+#define __IN(fds, n) (fds->in + n)
+#define __OUT(fds, n) (fds->out + n)
+#define __EX(fds, n) (fds->ex + n)
+#define __RES_IN(fds, n) (fds->res_in + n)
+#define __RES_OUT(fds, n) (fds->res_out + n)
+#define __RES_EX(fds, n) (fds->res_ex + n)
+
+#define BITS(fds, n) (*__IN(fds, n)|*__OUT(fds, n)|*__EX(fds, n))
+
+static int max_select_fd(unsigned long n, fd_set_bits *fds)
+{
+ unsigned long *open_fds;
+ unsigned long set;
+ int max;
+
+ /* handle last in-complete long-word first */
+ set = ~(~0UL << (n & (__NFDBITS-1)));
+ n /= __NFDBITS;
+ open_fds = current->files->open_fds->fds_bits+n;
+ max = 0;
+ if (set) {
+ set &= BITS(fds, n);
+ if (set) {
+ if (!(set & ~*open_fds))
+ goto get_max;
+ return -EBADF;
+ }
+ }
+ while (n) {
+ open_fds--;
+ n--;
+ set = BITS(fds, n);
+ if (!set)
+ continue;
+ if (set & ~*open_fds)
+ return -EBADF;
+ if (max)
+ continue;
+get_max:
+ do {
+ max++;
+ set >>= 1;
+ } while (set);
+ max += n * __NFDBITS;
+ }
+
+ return max;
+}
+
+#define BIT(i) (1UL << ((i)&(__NFDBITS-1)))
+#define MEM(i,m) ((m)+(unsigned)(i)/__NFDBITS)
+#define ISSET(i,m) (((i)&*(m)) != 0)
+#define SET(i,m) (*(m) |= (i))
+
+#define POLLIN_SET (POLLRDNORM | POLLRDBAND | POLLIN | POLLHUP | POLLERR)
+#define POLLOUT_SET (POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR)
+#define POLLEX_SET (POLLPRI)
+
+int do_select(int n, fd_set_bits *fds, long *timeout)
+{
+ poll_table table, *wait;
+ int retval, i, off;
+ long __timeout = *timeout;
+
+ read_lock(&current->files->file_lock);
+ retval = max_select_fd(n, fds);
+ read_unlock(&current->files->file_lock);
+
+ if (retval < 0)
+ return retval;
+ n = retval;
+
+ poll_initwait(&table);
+ wait = &table;
+ if (!__timeout)
+ wait = NULL;
+ retval = 0;
+ for (;;) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ for (i = 0 ; i < n; i++) {
+ unsigned long bit = BIT(i);
+ unsigned long mask;
+ struct file *file;
+
+ off = i / __NFDBITS;
+ if (!(bit & BITS(fds, off)))
+ continue;
+ file = fget(i);
+ mask = POLLNVAL;
+ if (file) {
+ mask = DEFAULT_POLLMASK;
+ if (file->f_op && file->f_op->poll)
+ mask = file->f_op->poll(file, wait);
+ fput(file);
+ }
+ if ((mask & POLLIN_SET) && ISSET(bit, __IN(fds,off))) {
+ SET(bit, __RES_IN(fds,off));
+ retval++;
+ wait = NULL;
+ }
+ if ((mask & POLLOUT_SET) && ISSET(bit, __OUT(fds,off))) {
+ SET(bit, __RES_OUT(fds,off));
+ retval++;
+ wait = NULL;
+ }
+ if ((mask & POLLEX_SET) && ISSET(bit, __EX(fds,off))) {
+ SET(bit, __RES_EX(fds,off));
+ retval++;
+ wait = NULL;
+ }
+ }
+ wait = NULL;
+ if (retval || !__timeout || signal_pending(current))
+ break;
+ if(table.error) {
+ retval = table.error;
+ break;
+ }
+ __timeout = schedule_timeout(__timeout);
+ }
+ current->state = TASK_RUNNING;
+
+ poll_freewait(&table);
+
+ /*
+ * Up-to-date the caller timeout.
+ */
+ *timeout = __timeout;
+ return retval;
+}
+
+static void *select_bits_alloc(int size)
+{
+ return kmalloc(6 * size, GFP_KERNEL);
+}
+
+static void select_bits_free(void *bits, int size)
+{
+ kfree(bits);
+}
+
+/*
+ * We can actually return ERESTARTSYS instead of EINTR, but I'd
+ * like to be certain this leads to no problems. So I return
+ * EINTR just for safety.
+ *
+ * Update: ERESTARTSYS breaks at least the xview clock binary, so
+ * I'm trying ERESTARTNOHAND which restart only when you want to.
+ */
+#define MAX_SELECT_SECONDS \
+ ((unsigned long) (MAX_SCHEDULE_TIMEOUT / HZ)-1)
+
+asmlinkage long
+sys_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp)
+{
+ fd_set_bits fds;
+ char *bits;
+ long timeout;
+ int ret, size, max_fdset;
+
+ timeout = MAX_SCHEDULE_TIMEOUT;
+ if (tvp) {
+ time_t sec, usec;
+
+ if ((ret = verify_area(VERIFY_READ, tvp, sizeof(*tvp)))
+ || (ret = __get_user(sec, &tvp->tv_sec))
+ || (ret = __get_user(usec, &tvp->tv_usec)))
+ goto out_nofds;
+
+ ret = -EINVAL;
+ if (sec < 0 || usec < 0)
+ goto out_nofds;
+
+ if ((unsigned long) sec < MAX_SELECT_SECONDS) {
+ timeout = ROUND_UP(usec, 1000000/HZ);
+ timeout += sec * (unsigned long) HZ;
+ }
+ }
+
+ ret = -EINVAL;
+ if (n < 0)
+ goto out_nofds;
+
+ /* max_fdset can increase, so grab it once to avoid race */
+ max_fdset = current->files->max_fdset;
+ if (n > max_fdset)
+ n = max_fdset;
+
+ /*
+ * We need 6 bitmaps (in/out/ex for both incoming and outgoing),
+ * since we used fdset we need to allocate memory in units of
+ * long-words.
+ */
+ ret = -ENOMEM;
+ size = FDS_BYTES(n);
+ bits = select_bits_alloc(size);
+ if (!bits)
+ goto out_nofds;
+ fds.in = (unsigned long *) bits;
+ fds.out = (unsigned long *) (bits + size);
+ fds.ex = (unsigned long *) (bits + 2*size);
+ fds.res_in = (unsigned long *) (bits + 3*size);
+ fds.res_out = (unsigned long *) (bits + 4*size);
+ fds.res_ex = (unsigned long *) (bits + 5*size);
+
+ if ((ret = get_fd_set(n, inp, fds.in)) ||
+ (ret = get_fd_set(n, outp, fds.out)) ||
+ (ret = get_fd_set(n, exp, fds.ex)))
+ goto out;
+ zero_fd_set(n, fds.res_in);
+ zero_fd_set(n, fds.res_out);
+ zero_fd_set(n, fds.res_ex);
+
+ ret = do_select(n, &fds, &timeout);
+
+ if (tvp && !(current->personality & STICKY_TIMEOUTS)) {
+ time_t sec = 0, usec = 0;
+ if (timeout) {
+ sec = timeout / HZ;
+ usec = timeout % HZ;
+ usec *= (1000000/HZ);
+ }
+ put_user(sec, &tvp->tv_sec);
+ put_user(usec, &tvp->tv_usec);
+ }
+
+ if (ret < 0)
+ goto out;
+ if (!ret) {
+ ret = -ERESTARTNOHAND;
+ if (signal_pending(current))
+ goto out;
+ ret = 0;
+ }
+
+ set_fd_set(n, inp, fds.res_in);
+ set_fd_set(n, outp, fds.res_out);
+ set_fd_set(n, exp, fds.res_ex);
+
+out:
+ select_bits_free(bits, size);
+out_nofds:
+ return ret;
+}
+
+#define POLLFD_PER_PAGE ((PAGE_SIZE) / sizeof(struct pollfd))
+
+static void do_pollfd(unsigned int num, struct pollfd * fdpage,
+ poll_table ** pwait, int *count)
+{
+ int i;
+
+ for (i = 0; i < num; i++) {
+ int fd;
+ unsigned int mask;
+ struct pollfd *fdp;
+
+ mask = 0;
+ fdp = fdpage+i;
+ fd = fdp->fd;
+ if (fd >= 0) {
+ struct file * file = fget(fd);
+ mask = POLLNVAL;
+ if (file != NULL) {
+ mask = DEFAULT_POLLMASK;
+ if (file->f_op && file->f_op->poll)
+ mask = file->f_op->poll(file, *pwait);
+ mask &= fdp->events | POLLERR | POLLHUP;
+ fput(file);
+ }
+ if (mask) {
+ *pwait = NULL;
+ (*count)++;
+ }
+ }
+ fdp->revents = mask;
+ }
+}
+
+static int do_poll(unsigned int nfds, unsigned int nchunks, unsigned int nleft,
+ struct pollfd *fds[], poll_table *wait, long timeout)
+{
+ int count;
+ poll_table* pt = wait;
+
+ for (;;) {
+ unsigned int i;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ count = 0;
+ for (i=0; i < nchunks; i++)
+ do_pollfd(POLLFD_PER_PAGE, fds[i], &pt, &count);
+ if (nleft)
+ do_pollfd(nleft, fds[nchunks], &pt, &count);
+ pt = NULL;
+ if (count || !timeout || signal_pending(current))
+ break;
+ count = wait->error;
+ if (count)
+ break;
+ timeout = schedule_timeout(timeout);
+ }
+ current->state = TASK_RUNNING;
+ return count;
+}
+
+asmlinkage long sys_poll(struct pollfd * ufds, unsigned int nfds, long timeout)
+{
+ int i, j, fdcount, err;
+ struct pollfd **fds;
+ poll_table table, *wait;
+ int nchunks, nleft;
+
+ /* Do a sanity check on nfds ... */
+ if (nfds > current->files->max_fdset && nfds > OPEN_MAX)
+ return -EINVAL;
+
+ if (timeout) {
+ /* Careful about overflow in the intermediate values */
+ if ((unsigned long) timeout < MAX_SCHEDULE_TIMEOUT / HZ)
+ timeout = (unsigned long)(timeout*HZ+999)/1000+1;
+ else /* Negative or overflow */
+ timeout = MAX_SCHEDULE_TIMEOUT;
+ }
+
+ poll_initwait(&table);
+ wait = &table;
+ if (!timeout)
+ wait = NULL;
+
+ err = -ENOMEM;
+ fds = NULL;
+ if (nfds != 0) {
+ fds = (struct pollfd **)kmalloc(
+ (1 + (nfds - 1) / POLLFD_PER_PAGE) * sizeof(struct pollfd *),
+ GFP_KERNEL);
+ if (fds == NULL)
+ goto out;
+ }
+
+ nchunks = 0;
+ nleft = nfds;
+ while (nleft > POLLFD_PER_PAGE) { /* allocate complete PAGE_SIZE chunks */
+ fds[nchunks] = (struct pollfd *)__get_free_page(GFP_KERNEL);
+ if (fds[nchunks] == NULL)
+ goto out_fds;
+ nchunks++;
+ nleft -= POLLFD_PER_PAGE;
+ }
+ if (nleft) { /* allocate last PAGE_SIZE chunk, only nleft elements used */
+ fds[nchunks] = (struct pollfd *)__get_free_page(GFP_KERNEL);
+ if (fds[nchunks] == NULL)
+ goto out_fds;
+ }
+
+ err = -EFAULT;
+ for (i=0; i < nchunks; i++)
+ if (copy_from_user(fds[i], ufds + i*POLLFD_PER_PAGE, PAGE_SIZE))
+ goto out_fds1;
+ if (nleft) {
+ if (copy_from_user(fds[nchunks], ufds + nchunks*POLLFD_PER_PAGE,
+ nleft * sizeof(struct pollfd)))
+ goto out_fds1;
+ }
+
+ fdcount = do_poll(nfds, nchunks, nleft, fds, wait, timeout);
+
+ /* OK, now copy the revents fields back to user space. */
+ for(i=0; i < nchunks; i++)
+ for (j=0; j < POLLFD_PER_PAGE; j++, ufds++)
+ __put_user((fds[i] + j)->revents, &ufds->revents);
+ if (nleft)
+ for (j=0; j < nleft; j++, ufds++)
+ __put_user((fds[nchunks] + j)->revents, &ufds->revents);
+
+ err = fdcount;
+ if (!fdcount && signal_pending(current))
+ err = -EINTR;
+
+out_fds1:
+ if (nleft)
+ free_page((unsigned long)(fds[nchunks]));
+out_fds:
+ for (i=0; i < nchunks; i++)
+ free_page((unsigned long)(fds[i]));
+ if (nfds != 0)
+ kfree(fds);
+out:
+ poll_freewait(&table);
+ return err;
+}
diff --git a/fs/seq_file.c b/fs/seq_file.c
new file mode 100644
index 00000000000000..524bafeb8691eb
--- /dev/null
+++ b/fs/seq_file.c
@@ -0,0 +1,381 @@
+/*
+ * linux/fs/seq_file.c
+ *
+ * helper functions for making synthetic files from sequences of records.
+ * initial implementation -- AV, Oct 2001.
+ */
+
+#include <linux/fs.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+
+#include <asm/uaccess.h>
+#include <asm/page.h>
+
+/**
+ * seq_open - initialize sequential file
+ * @file: file we initialize
+ * @op: method table describing the sequence
+ *
+ * seq_open() sets @file, associating it with a sequence described
+ * by @op. @op->start() sets the iterator up and returns the first
+ * element of sequence. @op->stop() shuts it down. @op->next()
+ * returns the next element of sequence. @op->show() prints element
+ * into the buffer. In case of error ->start() and ->next() return
+ * ERR_PTR(error). In the end of sequence they return %NULL. ->show()
+ * returns 0 in case of success and negative number in case of error.
+ */
+int seq_open(struct file *file, struct seq_operations *op)
+{
+ struct seq_file *p = kmalloc(sizeof(*p), GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+ memset(p, 0, sizeof(*p));
+ sema_init(&p->sem, 1);
+ p->op = op;
+ file->private_data = p;
+ return 0;
+}
+
+/**
+ * seq_read - ->read() method for sequential files.
+ * @file, @buf, @size, @ppos: see file_operations method
+ *
+ * Ready-made ->f_op->read()
+ */
+ssize_t seq_read(struct file *file, char *buf, size_t size, loff_t *ppos)
+{
+ struct seq_file *m = (struct seq_file *)file->private_data;
+ size_t copied = 0;
+ loff_t pos;
+ size_t n;
+ void *p;
+ int err = 0;
+
+ if (ppos != &file->f_pos)
+ return -EPIPE;
+
+ down(&m->sem);
+ /* grab buffer if we didn't have one */
+ if (!m->buf) {
+ m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL);
+ if (!m->buf)
+ goto Enomem;
+ }
+ /* if not empty - flush it first */
+ if (m->count) {
+ n = min(m->count, size);
+ err = copy_to_user(buf, m->buf + m->from, n);
+ if (err)
+ goto Efault;
+ m->count -= n;
+ m->from += n;
+ size -= n;
+ buf += n;
+ copied += n;
+ if (!m->count)
+ m->index++;
+ if (!size)
+ goto Done;
+ }
+ /* we need at least one record in buffer */
+ while (1) {
+ pos = m->index;
+ p = m->op->start(m, &pos);
+ err = PTR_ERR(p);
+ if (!p || IS_ERR(p))
+ break;
+ err = m->op->show(m, p);
+ if (err)
+ break;
+ if (m->count < m->size)
+ goto Fill;
+ m->op->stop(m, p);
+ kfree(m->buf);
+ m->buf = kmalloc(m->size <<= 1, GFP_KERNEL);
+ if (!m->buf)
+ goto Enomem;
+ m->count = 0;
+ }
+ m->op->stop(m, p);
+ m->count = 0;
+ goto Done;
+Fill:
+ /* they want more? let's try to get some more */
+ while (m->count < size) {
+ size_t offs = m->count;
+ loff_t next = pos;
+ p = m->op->next(m, p, &next);
+ if (!p || IS_ERR(p)) {
+ err = PTR_ERR(p);
+ break;
+ }
+ err = m->op->show(m, p);
+ if (err || m->count == m->size) {
+ m->count = offs;
+ break;
+ }
+ pos = next;
+ }
+ m->op->stop(m, p);
+ n = min(m->count, size);
+ err = copy_to_user(buf, m->buf, n);
+ if (err)
+ goto Efault;
+ copied += n;
+ m->count -= n;
+ if (m->count)
+ m->from = n;
+ else
+ pos++;
+ m->index = pos;
+Done:
+ if (!copied)
+ copied = err;
+ else
+ *ppos += copied;
+ up(&m->sem);
+ return copied;
+Enomem:
+ err = -ENOMEM;
+ goto Done;
+Efault:
+ err = -EFAULT;
+ goto Done;
+}
+
+static int traverse(struct seq_file *m, loff_t offset)
+{
+ loff_t pos = 0;
+ int error = 0;
+ void *p;
+
+ m->index = 0;
+ m->count = m->from = 0;
+ if (!offset)
+ return 0;
+ if (!m->buf) {
+ m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL);
+ if (!m->buf)
+ return -ENOMEM;
+ }
+ p = m->op->start(m, &m->index);
+ while (p) {
+ error = PTR_ERR(p);
+ if (IS_ERR(p))
+ break;
+ error = m->op->show(m, p);
+ if (error)
+ break;
+ if (m->count == m->size)
+ goto Eoverflow;
+ if (pos + m->count > offset) {
+ m->from = offset - pos;
+ m->count -= m->from;
+ break;
+ }
+ pos += m->count;
+ m->count = 0;
+ if (pos == offset) {
+ m->index++;
+ break;
+ }
+ p = m->op->next(m, p, &m->index);
+ }
+ m->op->stop(m, p);
+ return error;
+
+Eoverflow:
+ m->op->stop(m, p);
+ kfree(m->buf);
+ m->buf = kmalloc(m->size <<= 1, GFP_KERNEL);
+ return !m->buf ? -ENOMEM : -EAGAIN;
+}
+
+/**
+ * seq_lseek - ->llseek() method for sequential files.
+ * @file, @offset, @origin: see file_operations method
+ *
+ * Ready-made ->f_op->llseek()
+ */
+loff_t seq_lseek(struct file *file, loff_t offset, int origin)
+{
+ struct seq_file *m = (struct seq_file *)file->private_data;
+ long long retval = -EINVAL;
+
+ down(&m->sem);
+ switch (origin) {
+ case 1:
+ offset += file->f_pos;
+ case 0:
+ if (offset < 0)
+ break;
+ retval = offset;
+ if (offset != file->f_pos) {
+ while ((retval=traverse(m, offset)) == -EAGAIN)
+ ;
+ if (retval) {
+ /* with extreme prejudice... */
+ file->f_pos = 0;
+ m->index = 0;
+ m->count = 0;
+ } else {
+ retval = file->f_pos = offset;
+ }
+ }
+ }
+ up(&m->sem);
+ return retval;
+}
+
+/**
+ * seq_release - free the structures associated with sequential file.
+ * @file: file in question
+ * @inode: file->f_dentry->d_inode
+ *
+ * Frees the structures associated with sequential file; can be used
+ * as ->f_op->release() if you don't have private data to destroy.
+ */
+int seq_release(struct inode *inode, struct file *file)
+{
+ struct seq_file *m = (struct seq_file *)file->private_data;
+ kfree(m->buf);
+ kfree(m);
+ return 0;
+}
+
+/**
+ * seq_escape - print string into buffer, escaping some characters
+ * @m: target buffer
+ * @s: string
+ * @esc: set of characters that need escaping
+ *
+ * Puts string into buffer, replacing each occurrence of character from
+ * @esc with usual octal escape. Returns 0 in case of success, -1 - in
+ * case of overflow.
+ */
+int seq_escape(struct seq_file *m, const char *s, const char *esc)
+{
+ char *end = m->buf + m->size;
+ char *p;
+ char c;
+
+ for (p = m->buf + m->count; (c = *s) != '\0' && p < end; s++) {
+ if (!strchr(esc, c)) {
+ *p++ = c;
+ continue;
+ }
+ if (p + 3 < end) {
+ *p++ = '\\';
+ *p++ = '0' + ((c & 0300) >> 6);
+ *p++ = '0' + ((c & 070) >> 3);
+ *p++ = '0' + (c & 07);
+ continue;
+ }
+ m->count = m->size;
+ return -1;
+ }
+ m->count = p - m->buf;
+ return 0;
+}
+
+int seq_printf(struct seq_file *m, const char *f, ...)
+{
+ va_list args;
+ int len;
+
+ if (m->count < m->size) {
+ va_start(args, f);
+ len = vsnprintf(m->buf + m->count, m->size - m->count, f, args);
+ va_end(args);
+ if (m->count + len < m->size) {
+ m->count += len;
+ return 0;
+ }
+ }
+ m->count = m->size;
+ return -1;
+}
+
+int seq_path(struct seq_file *m,
+ struct vfsmount *mnt, struct dentry *dentry,
+ char *esc)
+{
+ if (m->count < m->size) {
+ char *s = m->buf + m->count;
+ char *p = d_path(dentry, mnt, s, m->size - m->count);
+ if (!IS_ERR(p)) {
+ while (s <= p) {
+ char c = *p++;
+ if (!c) {
+ p = m->buf + m->count;
+ m->count = s - m->buf;
+ return s - p;
+ } else if (!strchr(esc, c)) {
+ *s++ = c;
+ } else if (s + 4 > p) {
+ break;
+ } else {
+ *s++ = '\\';
+ *s++ = '0' + ((c & 0300) >> 6);
+ *s++ = '0' + ((c & 070) >> 3);
+ *s++ = '0' + (c & 07);
+ }
+ }
+ }
+ }
+ m->count = m->size;
+ return -1;
+}
+
+static void *single_start(struct seq_file *p, loff_t *pos)
+{
+ return NULL + (*pos == 0);
+}
+
+static void *single_next(struct seq_file *p, void *v, loff_t *pos)
+{
+ ++*pos;
+ return NULL;
+}
+
+static void single_stop(struct seq_file *p, void *v)
+{
+}
+
+int single_open(struct file *file, int (*show)(struct seq_file *, void*), void *data)
+{
+ struct seq_operations *op = kmalloc(sizeof(*op), GFP_KERNEL);
+ int res = -ENOMEM;
+
+ if (op) {
+ op->start = single_start;
+ op->next = single_next;
+ op->stop = single_stop;
+ op->show = show;
+ res = seq_open(file, op);
+ if (!res)
+ ((struct seq_file *)file->private_data)->private = data;
+ else
+ kfree(op);
+ }
+ return res;
+}
+
+int single_release(struct inode *inode, struct file *file)
+{
+ struct seq_operations *op = ((struct seq_file *)file->private_data)->op;
+ int res = seq_release(inode, file);
+ kfree(op);
+ return res;
+}
+
+int seq_release_private(struct inode *inode, struct file *file)
+{
+ struct seq_file *seq = file->private_data;
+
+ kfree(seq->private);
+ seq->private = NULL;
+ return seq_release(inode, file);
+}
+
diff --git a/fs/smbfs/ChangeLog b/fs/smbfs/ChangeLog
new file mode 100644
index 00000000000000..064c53073f2c8a
--- /dev/null
+++ b/fs/smbfs/ChangeLog
@@ -0,0 +1,150 @@
+ChangeLog for smbfs.
+
+2004-01-24 Urban Widmark <urban@teststation.com>
+
+ * file.c, inode.c, proc.c: Large File Support
+
+2004-01-24 John Newbigin <jn@it.swin.edu.au>
+
+ * *.c: Implementation of CIFS Extensions for UNIX systems
+
+2001-12-31 René Scharfe <l.s.r@web.de>
+
+ * inode.c: added smb_show_options to show mount options in /proc/mounts
+ * inode.c, getopt.c, getopt.h: merged flag and has_arg in struct option
+ * inode.c: use S_IRWXUGO where appropriate
+
+2001-12-22 Urban Widmark <urban@teststation.com>
+
+ * file.c, proc.c: Fix problems triggered by the "fsx test"
+
+2001-09-17 Urban Widmark <urban@teststation.com>
+
+ * proc.c: Use 4096 (was 512) as the blocksize for better write
+ performance (patch originally by Jan Kratochvil)
+ * proc.c: Skip disconnect smb, allows umount on unreachable servers.
+ * proc.c: Go back to the interruptible sleep as reconnects seem to
+ handle it now.
+ * *.c: use autogenerated and private proto.h
+
+2000-11-22 Igor Zhbanov <bsg@uniyar.ac.ru>
+
+ * proc.c: fixed date_unix2dos for dates earlier than 01/01/1980
+ and date_dos2unix for date==0 (from 2.2)
+
+2001-07-13 Rob Radez <rob@osinvestor.com>
+
+ * proc.c: make smb_errno return negative error values
+
+2001-07-09 Jochen Dolze <dolze@epcnet.de>
+
+ * inode.c: smb_statfs always returned success.
+ * proc.c, ioctl.c: Allow smbmount to signal failure to reconnect with
+ a NULL argument to SMB_IOC_NEWCONN (speeds up error detection).
+ * proc.c: Add some of the missing error codes to smb_errno
+
+2001-06-12 Urban Widmark <urban@teststation.com>
+
+ * proc.c: replace the win95-flush fix with smb_seek, when needed.
+ * proc.c: readdir 'lastname' bug (NetApp dir listing fix)
+
+2001-05-08 Urban Widmark <urban@teststation.com>
+
+ * inode.c: Fix for changes on the server side not being detected
+ properly. Must always drop cached pages when updating an inode with
+ new size.
+
+2001-05-05 Urban Widmark <urban@teststation.com>
+
+ * file.c, proc.c: Drop SMB_F_LOCALWRITE to detect changes made on
+ both server and client, using flush with to force win9x to remember
+ the right filesize.
+
+2001-04-25 René Scharfe <l.s.r@web.de>
+
+ * inode.c: Don't clear s_flags and allow ro mounts
+
+2001-04-21 Urban Widmark <urban@teststation.com>
+
+ * dir.c, proc.c: replace tests on conn_pid with tests on state to
+ fix smbmount reconnect on smb_retry timeout and up the timeout to 30s.
+ * proc.c: smb_newconn must have the server locked while updating it.
+ * inode.c, proc.c: need flush after truncate on some servers (win9x)
+ * file.c: add call to send SMBflush on fsync
+ (as suggested by Jochen Dolze <dolze@epcnet.de>)
+
+2001-03-06 Urban Widmark <urban@teststation.com>
+
+ * cache.c: d_add on hashed dentries corrupts d_hash list and
+ causes loops in d_lookup. Inherited bug. :)
+ * inode.c: tail -f fix for non-readonly opened files
+ (related to the smb_proc_open change).
+ * inode.c: tail -f fix for fast size changes with the same mtime.
+
+2001-03-02 Michael Kockelkorn <m.kockelkorn@biodata.com>
+
+ * proc.c: fix smb_proc_open to allow open being called more than once
+ with different modes (O_RDONLY -> O_WRONLY) without closing.
+
+2001-02-10 Urban Widmark <urban@teststation.com>
+
+ * dir.c, cache.c: replace non-bigmem safe cache with cache code
+ from ncpfs and fix some other bigmem bugs in smbfs.
+ * inode.c: root dentry not properly initialized
+ * proc.c, sock.c: adjust max parameters & max data to follow max_xmit
+ lots of servers were having find_next trouble with this.
+ * proc.c: use documented write method of truncating (NetApp fix)
+
+2000-08-14 Urban Widmark <urban@svenskatest.se>
+
+ * dir.c: support case sensitive shares
+ * inode.c: ascii mount options
+ * proc.c: check length of paths to avoid buffer overflow
+ * proc.c: don't do interruptable_sleep in smb_retry to avoid signal
+ problem/race.
+ * proc.c: O_RDONLY & smb_revalidate_inode fix (tail -f)
+ * proc.c: add nls support
+ * sock.c: attempt to fix smb_data_callback (avoid infinite loop)
+
+2000-07-25 Urban Widmark <urban@svenskatest.se>
+
+ * proc.c: fix 3 places where bad server responses could cause an Oops.
+
+2000-07-15 Urban Widmark <urban@svenskatest.se>
+
+ * *.c: more debug (%.*s) & indent fixes
+
+2000-06-24: Matt Maynard <matthewm@corel.com>
+
+ * dir.c: dentry->d_inode->i_mtime isn't updated for all servers
+ (NT?) and all operations (mv oldfile.txt newfile.txt) Removed for
+ less efficient but better working directory cache.
+ * proc.c: included aDIR smbclient fix for renaming directories on
+ OS/2 servers (win95/98?) (orig by John Janosik)
+
+2000-07-01 Urban Widmark <urban@svenskatest.se>
+
+ * *.c: replace ugly #ifdef's with less ugly debug macros.
+
+2000-01-03 Christian Groessler <cpg@aladdin.de>
+
+ * proc.c: added posix semantics for unlink
+
+1999-11-16 Andrew Tridgell
+
+ * proc.c: use level 260 for most conns, or level 1 for <NT1
+ * proc.c: don't sleep every time with win95 on a FINDNEXT
+ * proc.c: fixed loop_count bug
+ * proc.c: got rid of resume_key
+
+[there are a few missing here :) ]
+
+1997-09-28 Riccardo Facchetti
+
+ * proc.c: Fixed smb_d_path [now smb_build_path()] to be non-recursive
+
+1996-06-28 Yuri Per
+
+ * proc.c: Fixed long file name support (smb_proc_readdir_long)
+
+You are in the wrong end for adding new entries. New entries at the top.
diff --git a/fs/smbfs/Makefile b/fs/smbfs/Makefile
new file mode 100644
index 00000000000000..a4f19705d0179f
--- /dev/null
+++ b/fs/smbfs/Makefile
@@ -0,0 +1,41 @@
+#
+# Makefile for the linux smb-filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+O_TARGET := smbfs.o
+
+obj-y := proc.o dir.o cache.o sock.o inode.o file.o ioctl.o getopt.o symlink.o
+obj-m := $(O_TARGET)
+
+# If you want debugging output, you may add these flags to the EXTRA_CFLAGS
+# SMBFS_PARANOIA should normally be enabled.
+
+EXTRA_CFLAGS += -DSMBFS_PARANOIA
+#EXTRA_CFLAGS += -DSMBFS_DEBUG
+#EXTRA_CFLAGS += -DSMBFS_DEBUG_VERBOSE
+#EXTRA_CFLAGS += -DDEBUG_SMB_MALLOC
+#EXTRA_CFLAGS += -DDEBUG_SMB_TIMESTAMP
+#EXTRA_CFLAGS += -Werror
+
+include $(TOPDIR)/Rules.make
+
+#
+# Maintainer rules
+#
+
+# getopt.c not included. It is intentionally separate
+SRC = proc.c dir.c cache.c sock.c inode.c file.c ioctl.c symlink.c
+
+proto:
+ -rm -f proto.h
+ @echo > proto2.h "/*"
+ @echo >> proto2.h " * Autogenerated with cproto on: " `date`
+ @echo >> proto2.h " */"
+ @echo >> proto2.h ""
+ cproto -E "gcc -E" -e -v -I $(TOPDIR)/include -DMAKING_PROTO -D__KERNEL__ $(SRC) >> proto2.h
+ mv proto2.h proto.h
diff --git a/fs/smbfs/cache.c b/fs/smbfs/cache.c
new file mode 100644
index 00000000000000..67b82ec2bd60e4
--- /dev/null
+++ b/fs/smbfs/cache.c
@@ -0,0 +1,208 @@
+/*
+ * cache.c
+ *
+ * Copyright (C) 1997 by Bill Hawes
+ *
+ * Routines to support directory cacheing using the page cache.
+ * This cache code is almost directly taken from ncpfs.
+ *
+ * Please add a note about your changes to smbfs in the ChangeLog file.
+ */
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/dirent.h>
+#include <linux/smb_fs.h>
+#include <linux/pagemap.h>
+
+#include <asm/page.h>
+
+#include "smb_debug.h"
+#include "proto.h"
+
+/*
+ * Force the next attempt to use the cache to be a timeout.
+ * If we can't find the page that's fine, it will cause a refresh.
+ */
+void
+smb_invalid_dir_cache(struct inode * dir)
+{
+ struct smb_sb_info *server = server_from_inode(dir);
+ union smb_dir_cache *cache = NULL;
+ struct page *page = NULL;
+
+ page = grab_cache_page(&dir->i_data, 0);
+ if (!page)
+ goto out;
+
+ if (!Page_Uptodate(page))
+ goto out_unlock;
+
+ cache = kmap(page);
+ cache->head.time = jiffies - SMB_MAX_AGE(server);
+
+ kunmap(page);
+ SetPageUptodate(page);
+out_unlock:
+ UnlockPage(page);
+ page_cache_release(page);
+out:
+ return;
+}
+
+/*
+ * Mark all dentries for 'parent' as invalid, forcing them to be re-read
+ */
+void
+smb_invalidate_dircache_entries(struct dentry *parent)
+{
+ struct smb_sb_info *server = server_from_dentry(parent);
+ struct list_head *next;
+ struct dentry *dentry;
+
+ spin_lock(&dcache_lock);
+ next = parent->d_subdirs.next;
+ while (next != &parent->d_subdirs) {
+ dentry = list_entry(next, struct dentry, d_child);
+ dentry->d_fsdata = NULL;
+ smb_age_dentry(server, dentry);
+ next = next->next;
+ }
+ spin_unlock(&dcache_lock);
+}
+
+/*
+ * dget, but require that fpos and parent matches what the dentry contains.
+ * dentry is not known to be a valid pointer at entry.
+ */
+struct dentry *
+smb_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos)
+{
+ struct dentry *dent = dentry;
+ struct list_head *next;
+
+ if (d_validate(dent, parent)) {
+ if (dent->d_name.len <= SMB_MAXNAMELEN &&
+ (unsigned long)dent->d_fsdata == fpos) {
+ if (!dent->d_inode) {
+ dput(dent);
+ dent = NULL;
+ }
+ return dent;
+ }
+ dput(dent);
+ }
+
+ /* If a pointer is invalid, we search the dentry. */
+ spin_lock(&dcache_lock);
+ next = parent->d_subdirs.next;
+ while (next != &parent->d_subdirs) {
+ dent = list_entry(next, struct dentry, d_child);
+ if ((unsigned long)dent->d_fsdata == fpos) {
+ if (dent->d_inode)
+ dget_locked(dent);
+ else
+ dent = NULL;
+ goto out_unlock;
+ }
+ next = next->next;
+ }
+ dent = NULL;
+out_unlock:
+ spin_unlock(&dcache_lock);
+ return dent;
+}
+
+
+/*
+ * Create dentry/inode for this file and add it to the dircache.
+ */
+int
+smb_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
+ struct smb_cache_control *ctrl, struct qstr *qname,
+ struct smb_fattr *entry)
+{
+ struct dentry *newdent, *dentry = filp->f_dentry;
+ struct inode *newino, *inode = dentry->d_inode;
+ struct smb_cache_control ctl = *ctrl;
+ int valid = 0;
+ int hashed = 0;
+ ino_t ino = 0;
+
+ qname->hash = full_name_hash(qname->name, qname->len);
+
+ if (dentry->d_op && dentry->d_op->d_hash)
+ if (dentry->d_op->d_hash(dentry, qname) != 0)
+ goto end_advance;
+
+ newdent = d_lookup(dentry, qname);
+
+ if (!newdent) {
+ newdent = d_alloc(dentry, qname);
+ if (!newdent)
+ goto end_advance;
+ } else {
+ hashed = 1;
+ memcpy((char *) newdent->d_name.name, qname->name,
+ newdent->d_name.len);
+ }
+
+ if (!newdent->d_inode) {
+ smb_renew_times(newdent);
+ entry->f_ino = iunique(inode->i_sb, 2);
+ newino = smb_iget(inode->i_sb, entry);
+ if (newino) {
+ smb_new_dentry(newdent);
+ d_instantiate(newdent, newino);
+ if (!hashed)
+ d_rehash(newdent);
+ }
+ } else
+ smb_set_inode_attr(newdent->d_inode, entry);
+
+ if (newdent->d_inode) {
+ ino = newdent->d_inode->i_ino;
+ newdent->d_fsdata = (void *) ctl.fpos;
+ smb_new_dentry(newdent);
+ }
+
+ if (ctl.idx >= SMB_DIRCACHE_SIZE) {
+ if (ctl.page) {
+ kunmap(ctl.page);
+ SetPageUptodate(ctl.page);
+ UnlockPage(ctl.page);
+ page_cache_release(ctl.page);
+ }
+ ctl.cache = NULL;
+ ctl.idx -= SMB_DIRCACHE_SIZE;
+ ctl.ofs += 1;
+ ctl.page = grab_cache_page(&inode->i_data, ctl.ofs);
+ if (ctl.page)
+ ctl.cache = kmap(ctl.page);
+ }
+ if (ctl.cache) {
+ ctl.cache->dentry[ctl.idx] = newdent;
+ valid = 1;
+ }
+ dput(newdent);
+
+end_advance:
+ if (!valid)
+ ctl.valid = 0;
+ if (!ctl.filled && (ctl.fpos == filp->f_pos)) {
+ if (!ino)
+ ino = find_inode_number(dentry, qname);
+ if (!ino)
+ ino = iunique(inode->i_sb, 2);
+ ctl.filled = filldir(dirent, qname->name, qname->len,
+ filp->f_pos, ino, DT_UNKNOWN);
+ if (!ctl.filled)
+ filp->f_pos += 1;
+ }
+ ctl.fpos += 1;
+ ctl.idx += 1;
+ *ctrl = ctl;
+ return (ctl.valid || !ctl.filled);
+}
diff --git a/fs/smbfs/dir.c b/fs/smbfs/dir.c
new file mode 100644
index 00000000000000..7c0a0de7678cad
--- /dev/null
+++ b/fs/smbfs/dir.c
@@ -0,0 +1,661 @@
+/*
+ * dir.c
+ *
+ * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
+ * Copyright (C) 1997 by Volker Lendecke
+ *
+ * Please add a note about your changes to smbfs in the ChangeLog file.
+ */
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/smp_lock.h>
+#include <linux/ctype.h>
+
+#include <linux/smb_fs.h>
+#include <linux/smb_mount.h>
+#include <linux/smbno.h>
+
+#include "smb_debug.h"
+#include "proto.h"
+
+static int smb_readdir(struct file *, void *, filldir_t);
+static int smb_dir_open(struct inode *, struct file *);
+
+static struct dentry *smb_lookup(struct inode *, struct dentry *);
+static int smb_create(struct inode *, struct dentry *, int);
+static int smb_mkdir(struct inode *, struct dentry *, int);
+static int smb_rmdir(struct inode *, struct dentry *);
+static int smb_unlink(struct inode *, struct dentry *);
+static int smb_rename(struct inode *, struct dentry *,
+ struct inode *, struct dentry *);
+static int smb_make_node(struct inode *,struct dentry *, int, int);
+static int smb_link(struct dentry *, struct inode *, struct dentry *);
+
+struct file_operations smb_dir_operations =
+{
+ read: generic_read_dir,
+ readdir: smb_readdir,
+ ioctl: smb_ioctl,
+ open: smb_dir_open,
+};
+
+struct inode_operations smb_dir_inode_operations =
+{
+ create: smb_create,
+ lookup: smb_lookup,
+ unlink: smb_unlink,
+ mkdir: smb_mkdir,
+ rmdir: smb_rmdir,
+ rename: smb_rename,
+ revalidate: smb_revalidate_inode,
+ setattr: smb_notify_change,
+};
+
+struct inode_operations smb_dir_inode_operations_unix =
+{
+ create: smb_create,
+ lookup: smb_lookup,
+ unlink: smb_unlink,
+ mkdir: smb_mkdir,
+ rmdir: smb_rmdir,
+ rename: smb_rename,
+ revalidate: smb_revalidate_inode,
+ setattr: smb_notify_change,
+ symlink: smb_symlink,
+ mknod: smb_make_node,
+ link: smb_link,
+};
+
+/*
+ * Read a directory, using filldir to fill the dirent memory.
+ * smb_proc_readdir does the actual reading from the smb server.
+ *
+ * The cache code is almost directly taken from ncpfs
+ */
+static int
+smb_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+ struct dentry *dentry = filp->f_dentry;
+ struct inode *dir = dentry->d_inode;
+ struct smb_sb_info *server = server_from_dentry(dentry);
+ union smb_dir_cache *cache = NULL;
+ struct smb_cache_control ctl;
+ struct page *page = NULL;
+ int result;
+
+ ctl.page = NULL;
+ ctl.cache = NULL;
+
+ VERBOSE("reading %s/%s, f_pos=%d\n",
+ DENTRY_PATH(dentry), (int) filp->f_pos);
+
+ result = 0;
+ switch ((unsigned int) filp->f_pos) {
+ case 0:
+ if (filldir(dirent, ".", 1, 0, dir->i_ino, DT_DIR) < 0)
+ goto out;
+ filp->f_pos = 1;
+ /* fallthrough */
+ case 1:
+ if (filldir(dirent, "..", 2, 1,
+ dentry->d_parent->d_inode->i_ino, DT_DIR) < 0)
+ goto out;
+ filp->f_pos = 2;
+ }
+
+ /*
+ * Make sure our inode is up-to-date.
+ */
+ result = smb_revalidate_inode(dentry);
+ if (result)
+ goto out;
+
+
+ page = grab_cache_page(&dir->i_data, 0);
+ if (!page)
+ goto read_really;
+
+ ctl.cache = cache = kmap(page);
+ ctl.head = cache->head;
+
+ if (!Page_Uptodate(page) || !ctl.head.eof) {
+ VERBOSE("%s/%s, page uptodate=%d, eof=%d\n",
+ DENTRY_PATH(dentry), Page_Uptodate(page),ctl.head.eof);
+ goto init_cache;
+ }
+
+ if (filp->f_pos == 2) {
+ if (jiffies - ctl.head.time >= SMB_MAX_AGE(server))
+ goto init_cache;
+
+ /*
+ * N.B. ncpfs checks mtime of dentry too here, we don't.
+ * 1. common smb servers do not update mtime on dir changes
+ * 2. it requires an extra smb request
+ * (revalidate has the same timeout as ctl.head.time)
+ *
+ * Instead smbfs invalidates its own cache on local changes
+ * and remote changes are not seen until timeout.
+ */
+ }
+
+ if (filp->f_pos > ctl.head.end)
+ goto finished;
+
+ ctl.fpos = filp->f_pos + (SMB_DIRCACHE_START - 2);
+ ctl.ofs = ctl.fpos / SMB_DIRCACHE_SIZE;
+ ctl.idx = ctl.fpos % SMB_DIRCACHE_SIZE;
+
+ for (;;) {
+ if (ctl.ofs != 0) {
+ ctl.page = find_lock_page(&dir->i_data, ctl.ofs);
+ if (!ctl.page)
+ goto invalid_cache;
+ ctl.cache = kmap(ctl.page);
+ if (!Page_Uptodate(ctl.page))
+ goto invalid_cache;
+ }
+ while (ctl.idx < SMB_DIRCACHE_SIZE) {
+ struct dentry *dent;
+ int res;
+
+ dent = smb_dget_fpos(ctl.cache->dentry[ctl.idx],
+ dentry, filp->f_pos);
+ if (!dent)
+ goto invalid_cache;
+
+ res = filldir(dirent, dent->d_name.name,
+ dent->d_name.len, filp->f_pos,
+ dent->d_inode->i_ino, DT_UNKNOWN);
+ dput(dent);
+ if (res)
+ goto finished;
+ filp->f_pos += 1;
+ ctl.idx += 1;
+ if (filp->f_pos > ctl.head.end)
+ goto finished;
+ }
+ if (ctl.page) {
+ kunmap(ctl.page);
+ SetPageUptodate(ctl.page);
+ UnlockPage(ctl.page);
+ page_cache_release(ctl.page);
+ ctl.page = NULL;
+ }
+ ctl.idx = 0;
+ ctl.ofs += 1;
+ }
+invalid_cache:
+ if (ctl.page) {
+ kunmap(ctl.page);
+ UnlockPage(ctl.page);
+ page_cache_release(ctl.page);
+ ctl.page = NULL;
+ }
+ ctl.cache = cache;
+init_cache:
+ smb_invalidate_dircache_entries(dentry);
+ ctl.head.time = jiffies;
+ ctl.head.eof = 0;
+ ctl.fpos = 2;
+ ctl.ofs = 0;
+ ctl.idx = SMB_DIRCACHE_START;
+ ctl.filled = 0;
+ ctl.valid = 1;
+read_really:
+ result = server->ops->readdir(filp, dirent, filldir, &ctl);
+ if (ctl.idx == -1)
+ goto invalid_cache; /* retry */
+ ctl.head.end = ctl.fpos - 1;
+ ctl.head.eof = ctl.valid;
+finished:
+ if (page) {
+ cache->head = ctl.head;
+ kunmap(page);
+ SetPageUptodate(page);
+ UnlockPage(page);
+ page_cache_release(page);
+ }
+ if (ctl.page) {
+ kunmap(ctl.page);
+ SetPageUptodate(ctl.page);
+ UnlockPage(ctl.page);
+ page_cache_release(ctl.page);
+ }
+out:
+ return result;
+}
+
+static int
+smb_dir_open(struct inode *dir, struct file *file)
+{
+ struct dentry *dentry = file->f_dentry;
+ struct smb_sb_info *server;
+ int error = 0;
+
+ VERBOSE("(%s/%s)\n", dentry->d_parent->d_name.name,
+ file->f_dentry->d_name.name);
+
+ /*
+ * Directory timestamps in the core protocol aren't updated
+ * when a file is added, so we give them a very short TTL.
+ */
+ lock_kernel();
+ server = server_from_dentry(dentry);
+ if (server->opt.protocol < SMB_PROTOCOL_LANMAN2) {
+ unsigned long age = jiffies - dir->u.smbfs_i.oldmtime;
+ if (age > 2*HZ)
+ smb_invalid_dir_cache(dir);
+ }
+
+ /*
+ * Note: in order to allow the smbmount process to open the
+ * mount point, we only revalidate if the connection is valid or
+ * if the process is trying to access something other than the root.
+ */
+ if (server->state == CONN_VALID || !IS_ROOT(dentry))
+ error = smb_revalidate_inode(dentry);
+ unlock_kernel();
+ return error;
+}
+
+/*
+ * Dentry operations routines
+ */
+static int smb_lookup_validate(struct dentry *, int);
+static int smb_hash_dentry(struct dentry *, struct qstr *);
+static int smb_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
+static int smb_delete_dentry(struct dentry *);
+
+static struct dentry_operations smbfs_dentry_operations =
+{
+ d_revalidate: smb_lookup_validate,
+ d_hash: smb_hash_dentry,
+ d_compare: smb_compare_dentry,
+ d_delete: smb_delete_dentry,
+};
+
+static struct dentry_operations smbfs_dentry_operations_case =
+{
+ d_revalidate: smb_lookup_validate,
+ d_delete: smb_delete_dentry,
+};
+
+
+/*
+ * This is the callback when the dcache has a lookup hit.
+ */
+static int
+smb_lookup_validate(struct dentry * dentry, int flags)
+{
+ struct smb_sb_info *server = server_from_dentry(dentry);
+ struct inode * inode = dentry->d_inode;
+ unsigned long age = jiffies - dentry->d_time;
+ int valid;
+
+ /*
+ * The default validation is based on dentry age:
+ * we believe in dentries for a few seconds. (But each
+ * successful server lookup renews the timestamp.)
+ */
+ valid = (age <= SMB_MAX_AGE(server));
+#ifdef SMBFS_DEBUG_VERBOSE
+ if (!valid)
+ VERBOSE("%s/%s not valid, age=%lu\n",
+ DENTRY_PATH(dentry), age);
+#endif
+
+ if (inode) {
+ lock_kernel();
+ if (is_bad_inode(inode)) {
+ PARANOIA("%s/%s has dud inode\n", DENTRY_PATH(dentry));
+ valid = 0;
+ } else if (!valid)
+ valid = (smb_revalidate_inode(dentry) == 0);
+ unlock_kernel();
+ } else {
+ /*
+ * What should we do for negative dentries?
+ */
+ }
+ return valid;
+}
+
+static int
+smb_hash_dentry(struct dentry *dir, struct qstr *this)
+{
+ unsigned long hash;
+ int i;
+
+ hash = init_name_hash();
+ for (i=0; i < this->len ; i++)
+ hash = partial_name_hash(tolower(this->name[i]), hash);
+ this->hash = end_name_hash(hash);
+
+ return 0;
+}
+
+static int
+smb_compare_dentry(struct dentry *dir, struct qstr *a, struct qstr *b)
+{
+ int i, result = 1;
+
+ if (a->len != b->len)
+ goto out;
+ for (i=0; i < a->len; i++) {
+ if (tolower(a->name[i]) != tolower(b->name[i]))
+ goto out;
+ }
+ result = 0;
+out:
+ return result;
+}
+
+/*
+ * This is the callback from dput() when d_count is going to 0.
+ * We use this to unhash dentries with bad inodes.
+ */
+static int
+smb_delete_dentry(struct dentry * dentry)
+{
+ if (dentry->d_inode) {
+ if (is_bad_inode(dentry->d_inode)) {
+ PARANOIA("bad inode, unhashing %s/%s\n",
+ DENTRY_PATH(dentry));
+ return 1;
+ }
+ } else {
+ /* N.B. Unhash negative dentries? */
+ }
+ return 0;
+}
+
+/*
+ * Initialize a new dentry
+ */
+void
+smb_new_dentry(struct dentry *dentry)
+{
+ struct smb_sb_info *server = server_from_dentry(dentry);
+
+ if (server->mnt->flags & SMB_MOUNT_CASE)
+ dentry->d_op = &smbfs_dentry_operations_case;
+ else
+ dentry->d_op = &smbfs_dentry_operations;
+ dentry->d_time = jiffies;
+}
+
+
+/*
+ * Whenever a lookup succeeds, we know the parent directories
+ * are all valid, so we want to update the dentry timestamps.
+ * N.B. Move this to dcache?
+ */
+void
+smb_renew_times(struct dentry * dentry)
+{
+ for (;;) {
+ dentry->d_time = jiffies;
+ if (IS_ROOT(dentry))
+ break;
+ dentry = dentry->d_parent;
+ }
+}
+
+static struct dentry *
+smb_lookup(struct inode *dir, struct dentry *dentry)
+{
+ struct smb_fattr finfo;
+ struct inode *inode;
+ int error;
+ struct smb_sb_info *server;
+
+ error = -ENAMETOOLONG;
+ if (dentry->d_name.len > SMB_MAXNAMELEN)
+ goto out;
+
+ error = smb_proc_getattr(dentry, &finfo);
+#ifdef SMBFS_PARANOIA
+ if (error && error != -ENOENT)
+ PARANOIA("find %s/%s failed, error=%d\n",
+ DENTRY_PATH(dentry), error);
+#endif
+
+ inode = NULL;
+ if (error == -ENOENT)
+ goto add_entry;
+ if (!error) {
+ error = -EACCES;
+ finfo.f_ino = iunique(dentry->d_sb, 2);
+ inode = smb_iget(dir->i_sb, &finfo);
+ if (inode) {
+ add_entry:
+ server = server_from_dentry(dentry);
+ if (server->mnt->flags & SMB_MOUNT_CASE)
+ dentry->d_op = &smbfs_dentry_operations_case;
+ else
+ dentry->d_op = &smbfs_dentry_operations;
+
+ d_add(dentry, inode);
+ smb_renew_times(dentry);
+ error = 0;
+ }
+ }
+out:
+ return ERR_PTR(error);
+}
+
+/*
+ * This code is common to all routines creating a new inode.
+ */
+static int
+smb_instantiate(struct dentry *dentry, __u16 fileid, int have_id)
+{
+ struct smb_sb_info *server = server_from_dentry(dentry);
+ struct inode *inode;
+ int error;
+ struct smb_fattr fattr;
+
+ VERBOSE("file %s/%s, fileid=%u\n", DENTRY_PATH(dentry), fileid);
+
+ error = smb_proc_getattr(dentry, &fattr);
+ if (error)
+ goto out_close;
+
+ smb_renew_times(dentry);
+ fattr.f_ino = iunique(dentry->d_sb, 2);
+ inode = smb_iget(dentry->d_sb, &fattr);
+ if (!inode)
+ goto out_no_inode;
+
+ if (have_id) {
+ inode->u.smbfs_i.fileid = fileid;
+ inode->u.smbfs_i.access = SMB_O_RDWR;
+ inode->u.smbfs_i.open = server->generation;
+ }
+ d_instantiate(dentry, inode);
+out:
+ return error;
+
+out_no_inode:
+ error = -EACCES;
+out_close:
+ if (have_id) {
+ PARANOIA("%s/%s failed, error=%d, closing %u\n",
+ DENTRY_PATH(dentry), error, fileid);
+ smb_close_fileid(dentry, fileid);
+ }
+ goto out;
+}
+
+/* N.B. How should the mode argument be used? */
+static int
+smb_create(struct inode *dir, struct dentry *dentry, int mode)
+{
+ struct smb_sb_info *server = server_from_dentry(dentry);
+ __u16 fileid;
+ int error;
+ struct iattr attr;
+
+ VERBOSE("creating %s/%s, mode=%d\n", DENTRY_PATH(dentry), mode);
+
+ smb_invalid_dir_cache(dir);
+ error = smb_proc_create(dentry, 0, CURRENT_TIME, &fileid);
+ if (!error) {
+ if (server->opt.capabilities & SMB_CAP_UNIX) {
+ /* Set attributes for new file */
+ attr.ia_valid = ATTR_MODE;
+ attr.ia_mode = mode;
+ error = smb_proc_setattr_unix(dentry, &attr, 0, 0);
+ }
+ error = smb_instantiate(dentry, fileid, 1);
+ } else {
+ PARANOIA("%s/%s failed, error=%d\n",
+ DENTRY_PATH(dentry), error);
+ }
+ return error;
+}
+
+/* N.B. How should the mode argument be used? */
+static int
+smb_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+ struct smb_sb_info *server = server_from_dentry(dentry);
+ int error;
+ struct iattr attr;
+
+ smb_invalid_dir_cache(dir);
+ error = smb_proc_mkdir(dentry);
+ if (!error) {
+ if (server->opt.capabilities & SMB_CAP_UNIX) {
+ /* Set attributes for new directory */
+ attr.ia_valid = ATTR_MODE;
+ attr.ia_mode = mode;
+ error = smb_proc_setattr_unix(dentry, &attr, 0, 0);
+ }
+ error = smb_instantiate(dentry, 0, 0);
+ }
+ return error;
+}
+
+static int
+smb_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ struct inode *inode = dentry->d_inode;
+ int error;
+
+ /*
+ * Close the directory if it's open.
+ */
+ smb_close(inode);
+
+ /*
+ * Check that nobody else is using the directory..
+ */
+ error = -EBUSY;
+ if (!d_unhashed(dentry))
+ goto out;
+
+ smb_invalid_dir_cache(dir);
+ error = smb_proc_rmdir(dentry);
+
+out:
+ return error;
+}
+
+static int
+smb_unlink(struct inode *dir, struct dentry *dentry)
+{
+ int error;
+
+ /*
+ * Close the file if it's open.
+ */
+ smb_close(dentry->d_inode);
+
+ smb_invalid_dir_cache(dir);
+ error = smb_proc_unlink(dentry);
+ if (!error)
+ smb_renew_times(dentry);
+ return error;
+}
+
+static int
+smb_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ int error;
+
+ /*
+ * Close any open files, and check whether to delete the
+ * target before attempting the rename.
+ */
+ if (old_dentry->d_inode)
+ smb_close(old_dentry->d_inode);
+ if (new_dentry->d_inode) {
+ smb_close(new_dentry->d_inode);
+ error = smb_proc_unlink(new_dentry);
+ if (error) {
+ VERBOSE("unlink %s/%s, error=%d\n",
+ DENTRY_PATH(new_dentry), error);
+ goto out;
+ }
+ /* FIXME */
+ d_delete(new_dentry);
+ }
+
+ smb_invalid_dir_cache(old_dir);
+ smb_invalid_dir_cache(new_dir);
+ error = smb_proc_mv(old_dentry, new_dentry);
+ if (!error) {
+ smb_renew_times(old_dentry);
+ smb_renew_times(new_dentry);
+ }
+out:
+ return error;
+}
+
+/*
+ * FIXME: samba servers won't let you create device nodes unless uid/gid
+ * matches the connection credentials (and we don't know which those are ...)
+ */
+static int
+smb_make_node(struct inode *dir, struct dentry *dentry, int mode, int dev)
+{
+ int error;
+ struct iattr attr;
+
+ attr.ia_valid = ATTR_MODE | ATTR_UID | ATTR_GID;
+ attr.ia_mode = mode;
+ attr.ia_uid = current->euid;
+ attr.ia_gid = current->egid;
+
+ smb_invalid_dir_cache(dir);
+ error = smb_proc_setattr_unix(dentry, &attr, MAJOR(dev), MINOR(dev));
+ if (!error) {
+ error = smb_instantiate(dentry, 0, 0);
+ }
+ return error;
+}
+
+/*
+ * dentry = existing file
+ * new_dentry = new file
+ */
+static int
+smb_link(struct dentry *dentry, struct inode *dir, struct dentry *new_dentry)
+{
+ int error;
+
+ DEBUG1("smb_link old=%s/%s new=%s/%s\n",
+ DENTRY_PATH(dentry), DENTRY_PATH(new_dentry));
+ smb_invalid_dir_cache(dir);
+ error = smb_proc_link(server_from_dentry(dentry), dentry, new_dentry);
+ if (!error) {
+ smb_renew_times(dentry);
+ error = smb_instantiate(new_dentry, 0, 0);
+ }
+ return error;
+}
diff --git a/fs/smbfs/file.c b/fs/smbfs/file.c
new file mode 100644
index 00000000000000..8ae5ea8c948b7a
--- /dev/null
+++ b/fs/smbfs/file.c
@@ -0,0 +1,402 @@
+/*
+ * file.c
+ *
+ * Copyright (C) 1995, 1996, 1997 by Paal-Kr. Engstad and Volker Lendecke
+ * Copyright (C) 1997 by Volker Lendecke
+ *
+ * Please add a note about your changes to smbfs in the ChangeLog file.
+ */
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/smp_lock.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#include <linux/smbno.h>
+#include <linux/smb_fs.h>
+
+#include "smb_debug.h"
+#include "proto.h"
+
+static int
+smb_fsync(struct file *file, struct dentry * dentry, int datasync)
+{
+ struct smb_sb_info *server = server_from_dentry(dentry);
+ int result;
+
+ VERBOSE("sync file %s/%s\n", DENTRY_PATH(dentry));
+
+ /*
+ * The VFS will writepage() all dirty pages for us, but we
+ * should send a SMBflush to the server, letting it know that
+ * we want things synchronized with actual storage.
+ *
+ * Note: this function requires all pages to have been written already
+ * (should be ok with writepage_sync)
+ */
+ smb_lock_server(server);
+ result = smb_proc_flush(server, dentry->d_inode->u.smbfs_i.fileid);
+ smb_unlock_server(server);
+ return result;
+}
+
+/*
+ * Read a page synchronously.
+ */
+static int
+smb_readpage_sync(struct dentry *dentry, struct page *page)
+{
+ char *buffer = kmap(page);
+ loff_t offset = (loff_t)page->index << PAGE_CACHE_SHIFT;
+ struct smb_sb_info *server = server_from_dentry(dentry);
+ unsigned int rsize = smb_get_rsize(server);
+ int count = PAGE_SIZE;
+ int result;
+
+ VERBOSE("file %s/%s, count=%d@%ld, rsize=%d\n",
+ DENTRY_PATH(dentry), count, offset, rsize);
+
+ result = smb_open(dentry, SMB_O_RDONLY);
+ if (result < 0) {
+ PARANOIA("%s/%s open failed, error=%d\n",
+ DENTRY_PATH(dentry), result);
+ goto io_error;
+ }
+
+ do {
+ if (count < rsize)
+ rsize = count;
+
+ result = server->ops->read(dentry->d_inode,offset,rsize,buffer);
+ if (result < 0)
+ goto io_error;
+
+ count -= result;
+ offset += result;
+ buffer += result;
+ dentry->d_inode->i_atime = CURRENT_TIME;
+ if (result < rsize)
+ break;
+ } while (count);
+
+ memset(buffer, 0, count);
+ flush_dcache_page(page);
+ SetPageUptodate(page);
+ result = 0;
+
+io_error:
+ kunmap(page);
+ UnlockPage(page);
+ return result;
+}
+
+/*
+ * We are called with the page locked and we unlock it when done.
+ */
+static int
+smb_readpage(struct file *file, struct page *page)
+{
+ int error;
+ struct dentry *dentry = file->f_dentry;
+
+ get_page(page);
+ error = smb_readpage_sync(dentry, page);
+ put_page(page);
+ return error;
+}
+
+/*
+ * Write a page synchronously.
+ * Offset is the data offset within the page.
+ */
+static int
+smb_writepage_sync(struct inode *inode, struct page *page,
+ unsigned long pageoffset, unsigned int count)
+{
+ loff_t offset;
+ char *buffer = kmap(page) + pageoffset;
+ struct smb_sb_info *server = server_from_inode(inode);
+ unsigned int wsize = smb_get_wsize(server);
+ int result, written = 0;
+
+ offset = ((loff_t)page->index << PAGE_CACHE_SHIFT) + pageoffset;
+ VERBOSE("file ino=%ld, fileid=%d, count=%d@%Ld, wsize=%d\n",
+ inode->i_ino, inode->u.smbfs_i.fileid, count, offset, wsize);
+
+ do {
+ if (count < wsize)
+ wsize = count;
+
+ result = server->ops->write(inode, offset, wsize, buffer);
+ if (result < 0) {
+ PARANOIA("failed write, wsize=%d, result=%d\n",
+ wsize, result);
+ break;
+ }
+ /* N.B. what if result < wsize?? */
+#ifdef SMBFS_PARANOIA
+ if (result < wsize)
+ PARANOIA("short write, wsize=%d, result=%d\n",
+ wsize, result);
+#endif
+ buffer += wsize;
+ offset += wsize;
+ written += wsize;
+ count -= wsize;
+ /*
+ * Update the inode now rather than waiting for a refresh.
+ */
+ inode->i_mtime = inode->i_atime = CURRENT_TIME;
+ inode->u.smbfs_i.flags |= SMB_F_LOCALWRITE;
+ if (offset > inode->i_size)
+ inode->i_size = offset;
+ } while (count);
+
+ kunmap(page);
+ return written ? written : result;
+}
+
+/*
+ * Write a page to the server. This will be used for NFS swapping only
+ * (for now), and we currently do this synchronously only.
+ *
+ * We are called with the page locked and we unlock it when done.
+ */
+static int
+smb_writepage(struct page *page)
+{
+ struct address_space *mapping = page->mapping;
+ struct inode *inode;
+ unsigned long end_index;
+ unsigned offset = PAGE_CACHE_SIZE;
+ int err;
+
+ if (!mapping)
+ BUG();
+ inode = mapping->host;
+ if (!inode)
+ BUG();
+
+ end_index = inode->i_size >> PAGE_CACHE_SHIFT;
+
+ /* easy case */
+ if (page->index < end_index)
+ goto do_it;
+ /* things got complicated... */
+ offset = inode->i_size & (PAGE_CACHE_SIZE-1);
+ /* OK, are we completely out? */
+ if (page->index >= end_index+1 || !offset)
+ return -EIO;
+do_it:
+ get_page(page);
+ err = smb_writepage_sync(inode, page, 0, offset);
+ SetPageUptodate(page);
+ UnlockPage(page);
+ put_page(page);
+ return err;
+}
+
+static int
+smb_updatepage(struct file *file, struct page *page, unsigned long offset,
+ unsigned int count)
+{
+ struct dentry *dentry = file->f_dentry;
+
+ DEBUG1("(%s/%s %d@%ld)\n", DENTRY_PATH(dentry),
+ count, (page->index << PAGE_CACHE_SHIFT)+offset);
+
+ return smb_writepage_sync(dentry->d_inode, page, offset, count);
+}
+
+static ssize_t
+smb_file_read(struct file * file, char * buf, size_t count, loff_t *ppos)
+{
+ struct dentry * dentry = file->f_dentry;
+ ssize_t status;
+
+ VERBOSE("file %s/%s, count=%lu@%lu\n", DENTRY_PATH(dentry),
+ (unsigned long) count, (unsigned long) *ppos);
+
+ status = smb_revalidate_inode(dentry);
+ if (status) {
+ PARANOIA("%s/%s validation failed, error=%Zd\n",
+ DENTRY_PATH(dentry), status);
+ goto out;
+ }
+
+ VERBOSE("before read, size=%ld, flags=%x, atime=%ld\n",
+ (long)dentry->d_inode->i_size,
+ dentry->d_inode->i_flags, dentry->d_inode->i_atime);
+
+ status = generic_file_read(file, buf, count, ppos);
+out:
+ return status;
+}
+
+static int
+smb_file_mmap(struct file * file, struct vm_area_struct * vma)
+{
+ struct dentry * dentry = file->f_dentry;
+ int status;
+
+ VERBOSE("file %s/%s, address %lu - %lu\n",
+ DENTRY_PATH(dentry), vma->vm_start, vma->vm_end);
+
+ status = smb_revalidate_inode(dentry);
+ if (status) {
+ PARANOIA("%s/%s validation failed, error=%d\n",
+ DENTRY_PATH(dentry), status);
+ goto out;
+ }
+ status = generic_file_mmap(file, vma);
+out:
+ return status;
+}
+
+/*
+ * This does the "real" work of the write. The generic routine has
+ * allocated the page, locked it, done all the page alignment stuff
+ * calculations etc. Now we should just copy the data from user
+ * space and write it back to the real medium..
+ *
+ * If the writer ends up delaying the write, the writer needs to
+ * increment the page use counts until he is done with the page.
+ */
+static int smb_prepare_write(struct file *file, struct page *page,
+ unsigned offset, unsigned to)
+{
+ return 0;
+}
+
+static int smb_commit_write(struct file *file, struct page *page,
+ unsigned offset, unsigned to)
+{
+ int status;
+
+ status = -EFAULT;
+ lock_kernel();
+ status = smb_updatepage(file, page, offset, to-offset);
+ unlock_kernel();
+ return status;
+}
+
+struct address_space_operations smb_file_aops = {
+ readpage: smb_readpage,
+ writepage: smb_writepage,
+ prepare_write: smb_prepare_write,
+ commit_write: smb_commit_write
+};
+
+/*
+ * Write to a file (through the page cache).
+ */
+static ssize_t
+smb_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
+{
+ struct dentry * dentry = file->f_dentry;
+ ssize_t result;
+
+ VERBOSE("file %s/%s, count=%lu@%lu\n",
+ DENTRY_PATH(dentry),
+ (unsigned long) count, (unsigned long) *ppos);
+
+ result = smb_revalidate_inode(dentry);
+ if (result) {
+ PARANOIA("%s/%s validation failed, error=%Zd\n",
+ DENTRY_PATH(dentry), result);
+ goto out;
+ }
+
+ result = smb_open(dentry, SMB_O_WRONLY);
+ if (result)
+ goto out;
+
+ if (count > 0) {
+ result = generic_file_write(file, buf, count, ppos);
+ VERBOSE("pos=%ld, size=%ld, mtime=%ld, atime=%ld\n",
+ (long) file->f_pos, (long) dentry->d_inode->i_size,
+ dentry->d_inode->i_mtime, dentry->d_inode->i_atime);
+ }
+out:
+ return result;
+}
+
+static int
+smb_file_open(struct inode *inode, struct file * file)
+{
+ int result;
+ struct dentry *dentry = file->f_dentry;
+ int smb_mode = (file->f_mode & O_ACCMODE) - 1;
+
+ lock_kernel();
+ result = smb_open(dentry, smb_mode);
+ if (result)
+ goto out;
+ inode->u.smbfs_i.openers++;
+out:
+ unlock_kernel();
+ return 0;
+}
+
+static int
+smb_file_release(struct inode *inode, struct file * file)
+{
+ lock_kernel();
+ if (!--inode->u.smbfs_i.openers) {
+ /* We must flush any dirty pages now as we won't be able to
+ write anything after close. mmap can trigger this.
+ "openers" should perhaps include mmap'ers ... */
+ filemap_fdatasync(inode->i_mapping);
+ filemap_fdatawait(inode->i_mapping);
+ smb_close(inode);
+ }
+ unlock_kernel();
+ return 0;
+}
+
+/*
+ * Check whether the required access is compatible with
+ * an inode's permission. SMB doesn't recognize superuser
+ * privileges, so we need our own check for this.
+ */
+static int
+smb_file_permission(struct inode *inode, int mask)
+{
+ int mode = inode->i_mode;
+ int error = 0;
+
+ VERBOSE("mode=%x, mask=%x\n", mode, mask);
+
+ /* Look at user permissions */
+ mode >>= 6;
+ if ((mode & 7 & mask) != mask)
+ error = -EACCES;
+ return error;
+}
+
+struct file_operations smb_file_operations =
+{
+ llseek: generic_file_llseek,
+ read: smb_file_read,
+ write: smb_file_write,
+ ioctl: smb_ioctl,
+ mmap: smb_file_mmap,
+ open: smb_file_open,
+ release: smb_file_release,
+ fsync: smb_fsync,
+};
+
+struct inode_operations smb_file_inode_operations =
+{
+ permission: smb_file_permission,
+ revalidate: smb_revalidate_inode,
+ setattr: smb_notify_change,
+};
diff --git a/fs/smbfs/getopt.c b/fs/smbfs/getopt.c
new file mode 100644
index 00000000000000..626543fb8ac6ee
--- /dev/null
+++ b/fs/smbfs/getopt.c
@@ -0,0 +1,63 @@
+/*
+ * getopt.c
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+#include "getopt.h"
+
+/**
+ * smb_getopt - option parser
+ * @caller: name of the caller, for error messages
+ * @options: the options string
+ * @opts: an array of &struct option entries controlling parser operations
+ * @optopt: output; will contain the current option
+ * @optarg: output; will contain the value (if one exists)
+ * @flag: output; may be NULL; should point to a long for or'ing flags
+ * @value: output; may be NULL; will be overwritten with the integer value
+ * of the current argument.
+ *
+ * Helper to parse options on the format used by mount ("a=b,c=d,e,f").
+ * Returns opts->val if a matching entry in the 'opts' array is found,
+ * 0 when no more tokens are found, -1 if an error is encountered.
+ */
+int smb_getopt(char *caller, char **options, struct option *opts,
+ char **optopt, char **optarg, unsigned long *flag,
+ unsigned long *value)
+{
+ char *token;
+ char *val;
+ int i;
+
+ do {
+ if ((token = strsep(options, ",")) == NULL)
+ return 0;
+ } while (*token == '\0');
+ *optopt = token;
+
+ *optarg = NULL;
+ if ((val = strchr (token, '=')) != NULL) {
+ *val++ = 0;
+ if (value)
+ *value = simple_strtoul(val, NULL, 0);
+ *optarg = val;
+ }
+
+ for (i = 0; opts[i].name != NULL; i++) {
+ if (!strcmp(opts[i].name, token)) {
+ if (!opts[i].flag && (!val || !*val)) {
+ printk("%s: the %s option requires an argument\n",
+ caller, token);
+ return -1;
+ }
+
+ if (flag && opts[i].flag)
+ *flag |= opts[i].flag;
+
+ return opts[i].val;
+ }
+ }
+ printk("%s: Unrecognized mount option %s\n", caller, token);
+ return -1;
+}
diff --git a/fs/smbfs/getopt.h b/fs/smbfs/getopt.h
new file mode 100644
index 00000000000000..146219ac7c46cb
--- /dev/null
+++ b/fs/smbfs/getopt.h
@@ -0,0 +1,14 @@
+#ifndef _LINUX_GETOPT_H
+#define _LINUX_GETOPT_H
+
+struct option {
+ const char *name;
+ unsigned long flag;
+ int val;
+};
+
+extern int smb_getopt(char *caller, char **options, struct option *opts,
+ char **optopt, char **optarg, unsigned long *flag,
+ unsigned long *value);
+
+#endif /* _LINUX_GETOPT_H */
diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c
new file mode 100644
index 00000000000000..3f218228f0ae54
--- /dev/null
+++ b/fs/smbfs/inode.c
@@ -0,0 +1,722 @@
+/*
+ * inode.c
+ *
+ * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
+ * Copyright (C) 1997 by Volker Lendecke
+ *
+ * Please add a note about your changes to smbfs in the ChangeLog file.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/file.h>
+#include <linux/dcache.h>
+#include <linux/smp_lock.h>
+#include <linux/nls.h>
+#include <linux/seq_file.h>
+
+#include <linux/smb_fs.h>
+#include <linux/smbno.h>
+#include <linux/smb_mount.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include "smb_debug.h"
+#include "getopt.h"
+#include "proto.h"
+
+/* Always pick a default string */
+#ifdef CONFIG_SMB_NLS_REMOTE
+#define SMB_NLS_REMOTE CONFIG_SMB_NLS_REMOTE
+#else
+#define SMB_NLS_REMOTE ""
+#endif
+
+#define SMB_TTL_DEFAULT 1000
+#define SMB_TIMEO_DEFAULT 30
+
+static void smb_delete_inode(struct inode *);
+static void smb_put_super(struct super_block *);
+static int smb_statfs(struct super_block *, struct statfs *);
+static int smb_show_options(struct seq_file *, struct vfsmount *);
+
+static struct super_operations smb_sops =
+{
+ put_inode: force_delete,
+ delete_inode: smb_delete_inode,
+ put_super: smb_put_super,
+ statfs: smb_statfs,
+ show_options: smb_show_options,
+};
+
+
+/* We are always generating a new inode here */
+struct inode *
+smb_iget(struct super_block *sb, struct smb_fattr *fattr)
+{
+ struct inode *result;
+
+ DEBUG1("smb_iget: %p\n", fattr);
+
+ result = new_inode(sb);
+ if (!result)
+ return result;
+ result->i_ino = fattr->f_ino;
+ memset(&(result->u.smbfs_i), 0, sizeof(result->u.smbfs_i));
+ smb_set_inode_attr(result, fattr);
+ if (S_ISREG(result->i_mode)) {
+ result->i_op = &smb_file_inode_operations;
+ result->i_fop = &smb_file_operations;
+ result->i_data.a_ops = &smb_file_aops;
+ } else if (S_ISDIR(result->i_mode)) {
+ struct smb_sb_info *server = &(sb->u.smbfs_sb);
+ if (server->opt.capabilities & SMB_CAP_UNIX)
+ result->i_op = &smb_dir_inode_operations_unix;
+ else
+ result->i_op = &smb_dir_inode_operations;
+ result->i_fop = &smb_dir_operations;
+ } else if(S_ISLNK(result->i_mode)) {
+ result->i_op = &smb_link_inode_operations;
+ } else {
+ init_special_inode(result, result->i_mode, fattr->f_rdev);
+ }
+ insert_inode_hash(result);
+ return result;
+}
+
+/*
+ * Copy the inode data to a smb_fattr structure.
+ */
+void
+smb_get_inode_attr(struct inode *inode, struct smb_fattr *fattr)
+{
+ memset(fattr, 0, sizeof(struct smb_fattr));
+ fattr->f_mode = inode->i_mode;
+ fattr->f_nlink = inode->i_nlink;
+ fattr->f_ino = inode->i_ino;
+ fattr->f_uid = inode->i_uid;
+ fattr->f_gid = inode->i_gid;
+ fattr->f_rdev = inode->i_rdev;
+ fattr->f_size = inode->i_size;
+ fattr->f_mtime = inode->i_mtime;
+ fattr->f_ctime = inode->i_ctime;
+ fattr->f_atime = inode->i_atime;
+ fattr->f_blksize= inode->i_blksize;
+ fattr->f_blocks = inode->i_blocks;
+
+ fattr->attr = inode->u.smbfs_i.attr;
+ /*
+ * Keep the attributes in sync with the inode permissions.
+ */
+ if (fattr->f_mode & S_IWUSR)
+ fattr->attr &= ~aRONLY;
+ else
+ fattr->attr |= aRONLY;
+}
+
+/*
+ * Update the inode, possibly causing it to invalidate its pages if mtime/size
+ * is different from last time.
+ */
+void
+smb_set_inode_attr(struct inode *inode, struct smb_fattr *fattr)
+{
+ /*
+ * A size change should have a different mtime, or same mtime
+ * but different size.
+ */
+ time_t last_time = inode->i_mtime;
+ loff_t last_sz = inode->i_size;
+
+ inode->i_mode = fattr->f_mode;
+ inode->i_nlink = fattr->f_nlink;
+ inode->i_uid = fattr->f_uid;
+ inode->i_gid = fattr->f_gid;
+ inode->i_rdev = fattr->f_rdev;
+ inode->i_ctime = fattr->f_ctime;
+ inode->i_blksize= fattr->f_blksize;
+ inode->i_blocks = fattr->f_blocks;
+ inode->i_size = fattr->f_size;
+ inode->i_mtime = fattr->f_mtime;
+ inode->i_atime = fattr->f_atime;
+ inode->u.smbfs_i.attr = fattr->attr;
+ /*
+ * Update the "last time refreshed" field for revalidation.
+ */
+ inode->u.smbfs_i.oldmtime = jiffies;
+
+ if (inode->i_mtime != last_time || inode->i_size != last_sz) {
+ VERBOSE("%ld changed, old=%ld, new=%ld, oz=%ld, nz=%ld\n",
+ inode->i_ino,
+ (long) last_time, (long) inode->i_mtime,
+ (long) last_sz, (long) inode->i_size);
+
+ if (!S_ISDIR(inode->i_mode))
+ invalidate_inode_pages(inode);
+ }
+}
+
+/*
+ * This is called if the connection has gone bad ...
+ * try to kill off all the current inodes.
+ */
+void
+smb_invalidate_inodes(struct smb_sb_info *server)
+{
+ VERBOSE("\n");
+ shrink_dcache_sb(SB_of(server));
+ invalidate_inodes(SB_of(server));
+}
+
+/*
+ * This is called to update the inode attributes after
+ * we've made changes to a file or directory.
+ */
+static int
+smb_refresh_inode(struct dentry *dentry)
+{
+ struct inode *inode = dentry->d_inode;
+ int error;
+ struct smb_fattr fattr;
+
+ error = smb_proc_getattr(dentry, &fattr);
+ if (!error) {
+ smb_renew_times(dentry);
+ /*
+ * Check whether the type part of the mode changed,
+ * and don't update the attributes if it did.
+ *
+ * And don't dick with the root inode
+ */
+ if (inode->i_ino == 2)
+ return error;
+ if (S_ISLNK(inode->i_mode))
+ return error; /* VFS will deal with it */
+
+ if ((inode->i_mode & S_IFMT) == (fattr.f_mode & S_IFMT)) {
+ smb_set_inode_attr(inode, &fattr);
+ } else {
+ /*
+ * Big trouble! The inode has become a new object,
+ * so any operations attempted on it are invalid.
+ *
+ * To limit damage, mark the inode as bad so that
+ * subsequent lookup validations will fail.
+ */
+ PARANOIA("%s/%s changed mode, %07o to %07o\n",
+ DENTRY_PATH(dentry),
+ inode->i_mode, fattr.f_mode);
+
+ fattr.f_mode = inode->i_mode; /* save mode */
+ make_bad_inode(inode);
+ inode->i_mode = fattr.f_mode; /* restore mode */
+ /*
+ * No need to worry about unhashing the dentry: the
+ * lookup validation will see that the inode is bad.
+ * But we do want to invalidate the caches ...
+ */
+ if (!S_ISDIR(inode->i_mode))
+ invalidate_inode_pages(inode);
+ else
+ smb_invalid_dir_cache(inode);
+ error = -EIO;
+ }
+ }
+ return error;
+}
+
+/*
+ * This is called when we want to check whether the inode
+ * has changed on the server. If it has changed, we must
+ * invalidate our local caches.
+ */
+int
+smb_revalidate_inode(struct dentry *dentry)
+{
+ struct smb_sb_info *s = server_from_dentry(dentry);
+ struct inode *inode = dentry->d_inode;
+ int error = 0;
+
+ DEBUG1("smb_revalidate_inode\n");
+ lock_kernel();
+
+ /*
+ * Check whether we've recently refreshed the inode.
+ */
+ if (time_before(jiffies, inode->u.smbfs_i.oldmtime + SMB_MAX_AGE(s))) {
+ VERBOSE("up-to-date, ino=%ld, jiffies=%lu, oldtime=%lu\n",
+ inode->i_ino, jiffies, inode->u.smbfs_i.oldmtime);
+ goto out;
+ }
+
+ error = smb_refresh_inode(dentry);
+out:
+ unlock_kernel();
+ return error;
+}
+
+/*
+ * This routine is called when i_nlink == 0 and i_count goes to 0.
+ * All blocking cleanup operations need to go here to avoid races.
+ */
+static void
+smb_delete_inode(struct inode *ino)
+{
+ DEBUG1("ino=%ld\n", ino->i_ino);
+ lock_kernel();
+ if (smb_close(ino))
+ PARANOIA("could not close inode %ld\n", ino->i_ino);
+ unlock_kernel();
+ clear_inode(ino);
+}
+
+static struct option opts[] = {
+ { "version", 0, 'v' },
+ { "win95", SMB_MOUNT_WIN95, 1 },
+ { "oldattr", SMB_MOUNT_OLDATTR, 1 },
+ { "dirattr", SMB_MOUNT_DIRATTR, 1 },
+ { "case", SMB_MOUNT_CASE, 1 },
+ { "uid", 0, 'u' },
+ { "gid", 0, 'g' },
+ { "file_mode", 0, 'f' },
+ { "dir_mode", 0, 'd' },
+ { "iocharset", 0, 'i' },
+ { "codepage", 0, 'c' },
+ { "ttl", 0, 't' },
+ { "timeo", 0, 'o' },
+ { NULL, 0, 0}
+};
+
+static int
+parse_options(struct smb_mount_data_kernel *mnt, char *options)
+{
+ int c;
+ unsigned long flags;
+ unsigned long value;
+ char *optarg;
+ char *optopt;
+
+ flags = 0;
+ while ( (c = smb_getopt("smbfs", &options, opts,
+ &optopt, &optarg, &flags, &value)) > 0) {
+
+ VERBOSE("'%s' -> '%s'\n", optopt, optarg ? optarg : "<none>");
+
+ switch (c) {
+ case 1:
+ /* got a "flag" option */
+ break;
+ case 'v':
+ if (value != SMB_MOUNT_VERSION) {
+ printk ("smbfs: Bad mount version %ld, expected %d\n",
+ value, SMB_MOUNT_VERSION);
+ return 0;
+ }
+ mnt->version = value;
+ break;
+ case 'u':
+ mnt->uid = value;
+ break;
+ case 'g':
+ mnt->gid = value;
+ break;
+ case 'f':
+ mnt->file_mode = (value & S_IRWXUGO) | S_IFREG;
+ break;
+ case 'd':
+ mnt->dir_mode = (value & S_IRWXUGO) | S_IFDIR;
+ break;
+ case 'i':
+ strncpy(mnt->codepage.local_name, optarg,
+ SMB_NLS_MAXNAMELEN);
+ break;
+ case 'c':
+ strncpy(mnt->codepage.remote_name, optarg,
+ SMB_NLS_MAXNAMELEN);
+ break;
+ case 't':
+ mnt->ttl = value;
+ break;
+ case 'o':
+ mnt->timeo = value;
+ break;
+ default:
+ printk ("smbfs: Unrecognized mount option %s\n",
+ optopt);
+ return -1;
+ }
+ }
+ mnt->flags = flags;
+ return c;
+}
+
+/*
+ * smb_show_options() is for displaying mount options in /proc/mounts.
+ * It tries to avoid showing settings that were not changed from their
+ * defaults.
+ */
+static int
+smb_show_options(struct seq_file *s, struct vfsmount *m)
+{
+ struct smb_mount_data_kernel *mnt = m->mnt_sb->u.smbfs_sb.mnt;
+ int i;
+
+ for (i = 0; opts[i].name != NULL; i++)
+ if (mnt->flags & opts[i].flag)
+ seq_printf(s, ",%s", opts[i].name);
+
+ if (mnt->uid != 0)
+ seq_printf(s, ",uid=%d", mnt->uid);
+ if (mnt->gid != 0)
+ seq_printf(s, ",gid=%d", mnt->gid);
+ if (mnt->mounted_uid != 0)
+ seq_printf(s, ",mounted_uid=%d", mnt->mounted_uid);
+
+ /*
+ * Defaults for file_mode and dir_mode are unknown to us; they
+ * depend on the current umask of the user doing the mount.
+ */
+ seq_printf(s, ",file_mode=%04o", mnt->file_mode & S_IRWXUGO);
+ seq_printf(s, ",dir_mode=%04o", mnt->dir_mode & S_IRWXUGO);
+
+ if (strcmp(mnt->codepage.local_name, CONFIG_NLS_DEFAULT))
+ seq_printf(s, ",iocharset=%s", mnt->codepage.local_name);
+ if (strcmp(mnt->codepage.remote_name, SMB_NLS_REMOTE))
+ seq_printf(s, ",codepage=%s", mnt->codepage.remote_name);
+
+ if (mnt->ttl != SMB_TTL_DEFAULT)
+ seq_printf(s, ",ttl=%d", mnt->ttl);
+ if (mnt->timeo != SMB_TIMEO_DEFAULT)
+ seq_printf(s, ",timeo=%d", mnt->timeo);
+
+ return 0;
+}
+
+static void
+smb_put_super(struct super_block *sb)
+{
+ struct smb_sb_info *server = &(sb->u.smbfs_sb);
+
+ if (server->sock_file) {
+ smb_dont_catch_keepalive(server);
+ fput(server->sock_file);
+ }
+
+ if (server->conn_pid)
+ kill_proc(server->conn_pid, SIGTERM, 1);
+
+ smb_kfree(server->mnt);
+ smb_kfree(server->temp_buf);
+ if (server->packet)
+ smb_vfree(server->packet);
+
+ if (server->remote_nls) {
+ unload_nls(server->remote_nls);
+ server->remote_nls = NULL;
+ }
+ if (server->local_nls) {
+ unload_nls(server->local_nls);
+ server->local_nls = NULL;
+ }
+}
+
+struct super_block *
+smb_read_super(struct super_block *sb, void *raw_data, int silent)
+{
+ struct smb_sb_info *server = &sb->u.smbfs_sb;
+ struct smb_mount_data_kernel *mnt;
+ struct smb_mount_data *oldmnt;
+ struct inode *root_inode;
+ struct smb_fattr root;
+ int ver;
+ void *mem;
+
+ if (!raw_data)
+ goto out_no_data;
+
+ oldmnt = (struct smb_mount_data *) raw_data;
+ ver = oldmnt->version;
+ if (ver != SMB_MOUNT_OLDVERSION && cpu_to_be32(ver) != SMB_MOUNT_ASCII)
+ goto out_wrong_data;
+
+ sb->s_blocksize = 1024; /* Eh... Is this correct? */
+ sb->s_blocksize_bits = 10;
+ sb->s_maxbytes = MAX_NON_LFS;
+ sb->s_magic = SMB_SUPER_MAGIC;
+ sb->s_op = &smb_sops;
+
+ server->mnt = NULL;
+ server->sock_file = NULL;
+ init_MUTEX(&server->sem);
+ init_waitqueue_head(&server->wait);
+ server->conn_pid = 0;
+ server->state = CONN_INVALID; /* no connection yet */
+ server->generation = 0;
+ server->packet_size = smb_round_length(SMB_INITIAL_PACKET_SIZE);
+ server->packet = smb_vmalloc(server->packet_size);
+ if (!server->packet)
+ goto out_no_mem;
+
+ /* Allocate the global temp buffer */
+ server->temp_buf = smb_kmalloc(2*SMB_MAXPATHLEN+20, GFP_KERNEL);
+ if (!server->temp_buf)
+ goto out_no_temp;
+
+ /* Setup NLS stuff */
+ server->remote_nls = NULL;
+ server->local_nls = NULL;
+ server->name_buf = server->temp_buf + SMB_MAXPATHLEN + 20;
+
+ /* Allocate the mount data structure */
+ /* FIXME: merge this with the other malloc and get a whole page? */
+ mem = smb_kmalloc(sizeof(struct smb_ops) +
+ sizeof(struct smb_mount_data_kernel), GFP_KERNEL);
+ if (!mem)
+ goto out_no_mount;
+ server->mnt = mnt = mem;
+ server->ops = mem + sizeof(struct smb_mount_data_kernel);
+ smb_install_null_ops(server->ops);
+
+ memset(mnt, 0, sizeof(struct smb_mount_data_kernel));
+ strncpy(mnt->codepage.local_name, CONFIG_NLS_DEFAULT,
+ SMB_NLS_MAXNAMELEN);
+ strncpy(mnt->codepage.remote_name, SMB_NLS_REMOTE,
+ SMB_NLS_MAXNAMELEN);
+
+ mnt->ttl = SMB_TTL_DEFAULT;
+ mnt->timeo = SMB_TIMEO_DEFAULT;
+ if (ver == SMB_MOUNT_OLDVERSION) {
+ mnt->version = oldmnt->version;
+
+ /* FIXME: is this enough to convert uid/gid's ? */
+ mnt->uid = oldmnt->uid;
+ mnt->gid = oldmnt->gid;
+
+ mnt->file_mode = (oldmnt->file_mode & S_IRWXUGO) | S_IFREG;
+ mnt->dir_mode = (oldmnt->dir_mode & S_IRWXUGO) | S_IFDIR;
+
+ mnt->flags = (oldmnt->file_mode >> 9);
+ } else {
+ if (parse_options(mnt, raw_data))
+ goto out_bad_option;
+ }
+ smb_setcodepage(server, &mnt->codepage);
+ mnt->mounted_uid = current->uid;
+
+ /*
+ * Display the enabled options
+ * Note: smb_proc_getattr uses these in 2.4 (but was changed in 2.2)
+ */
+ if (mnt->flags & SMB_MOUNT_OLDATTR)
+ printk("SMBFS: Using core getattr (Win 95 speedup)\n");
+ else if (mnt->flags & SMB_MOUNT_DIRATTR)
+ printk("SMBFS: Using dir ff getattr\n");
+
+ /*
+ * Keep the super block locked while we get the root inode.
+ */
+ smb_init_root_dirent(server, &root);
+ root_inode = smb_iget(sb, &root);
+ if (!root_inode)
+ goto out_no_root;
+
+ sb->s_root = d_alloc_root(root_inode);
+ if (!sb->s_root)
+ goto out_no_root;
+ smb_new_dentry(sb->s_root);
+
+ return sb;
+
+out_no_root:
+ iput(root_inode);
+out_bad_option:
+ smb_kfree(server->mnt);
+out_no_mount:
+ smb_kfree(server->temp_buf);
+out_no_temp:
+ smb_vfree(server->packet);
+out_no_mem:
+ if (!server->mnt)
+ printk(KERN_ERR "smb_read_super: allocation failure\n");
+ goto out_fail;
+out_wrong_data:
+ printk(KERN_ERR "smbfs: mount_data version %d is not supported\n", ver);
+ goto out_fail;
+out_no_data:
+ printk(KERN_ERR "smb_read_super: missing data argument\n");
+out_fail:
+ return NULL;
+}
+
+static int
+smb_statfs(struct super_block *sb, struct statfs *buf)
+{
+ int result = smb_proc_dskattr(sb, buf);
+
+ buf->f_type = SMB_SUPER_MAGIC;
+ buf->f_namelen = SMB_MAXPATHLEN;
+ return result;
+}
+
+int
+smb_notify_change(struct dentry *dentry, struct iattr *attr)
+{
+ struct inode *inode = dentry->d_inode;
+ struct smb_sb_info *server = server_from_dentry(dentry);
+ unsigned int mask = (S_IFREG | S_IFDIR | S_IRWXUGO);
+ int error, changed, refresh = 0;
+ struct smb_fattr fattr;
+
+ error = smb_revalidate_inode(dentry);
+ if (error)
+ goto out;
+
+ if ((error = inode_change_ok(inode, attr)) < 0)
+ goto out;
+
+ error = -EPERM;
+ if ((attr->ia_valid & ATTR_UID) && (attr->ia_uid != server->mnt->uid))
+ goto out;
+
+ if ((attr->ia_valid & ATTR_GID) && (attr->ia_uid != server->mnt->gid))
+ goto out;
+
+ if ((attr->ia_valid & ATTR_MODE) && (attr->ia_mode & ~mask))
+ goto out;
+
+ if ((attr->ia_valid & ATTR_SIZE) != 0) {
+ VERBOSE("changing %s/%s, old size=%ld, new size=%ld\n",
+ DENTRY_PATH(dentry),
+ (long) inode->i_size, (long) attr->ia_size);
+
+ filemap_fdatasync(inode->i_mapping);
+ filemap_fdatawait(inode->i_mapping);
+
+ error = smb_open(dentry, O_WRONLY);
+ if (error)
+ goto out;
+ error = server->ops->truncate(inode, attr->ia_size);
+ if (error)
+ goto out;
+ error = vmtruncate(inode, attr->ia_size);
+ if (error)
+ goto out;
+ refresh = 1;
+ }
+
+ if (server->opt.capabilities & SMB_CAP_UNIX) {
+ /* For now we don't want to set the size with setattr_unix */
+ attr->ia_valid &= ~ATTR_SIZE;
+ /* FIXME: only call if we actually want to set something? */
+ error = smb_proc_setattr_unix(dentry, attr, 0, 0);
+ if (!error)
+ refresh = 1;
+ goto out;
+ }
+
+ /*
+ * Initialize the fattr and check for changed fields.
+ * Note: CTIME under SMB is creation time rather than
+ * change time, so we don't attempt to change it.
+ */
+ smb_get_inode_attr(inode, &fattr);
+
+ changed = 0;
+ if ((attr->ia_valid & ATTR_MTIME) != 0) {
+ fattr.f_mtime = attr->ia_mtime;
+ changed = 1;
+ }
+ if ((attr->ia_valid & ATTR_ATIME) != 0) {
+ fattr.f_atime = attr->ia_atime;
+ /* Earlier protocols don't have an access time */
+ if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2)
+ changed = 1;
+ }
+ if (changed) {
+ error = smb_proc_settime(dentry, &fattr);
+ if (error)
+ goto out;
+ refresh = 1;
+ }
+
+ /*
+ * Check for mode changes ... we're extremely limited in
+ * what can be set for SMB servers: just the read-only bit.
+ */
+ if ((attr->ia_valid & ATTR_MODE) != 0) {
+ VERBOSE("%s/%s mode change, old=%x, new=%x\n",
+ DENTRY_PATH(dentry), fattr.f_mode, attr->ia_mode);
+ changed = 0;
+ if (attr->ia_mode & S_IWUSR) {
+ if (fattr.attr & aRONLY) {
+ fattr.attr &= ~aRONLY;
+ changed = 1;
+ }
+ } else {
+ if (!(fattr.attr & aRONLY)) {
+ fattr.attr |= aRONLY;
+ changed = 1;
+ }
+ }
+ if (changed) {
+ error = smb_proc_setattr(dentry, &fattr);
+ if (error)
+ goto out;
+ refresh = 1;
+ }
+ }
+ error = 0;
+
+out:
+ if (refresh)
+ smb_refresh_inode(dentry);
+ return error;
+}
+
+#ifdef DEBUG_SMB_MALLOC
+int smb_malloced;
+int smb_current_kmalloced;
+int smb_current_vmalloced;
+#endif
+
+static DECLARE_FSTYPE( smb_fs_type, "smbfs", smb_read_super, 0);
+
+static int __init init_smb_fs(void)
+{
+ DEBUG1("registering ...\n");
+
+#ifdef DEBUG_SMB_MALLOC
+ smb_malloced = 0;
+ smb_current_kmalloced = 0;
+ smb_current_vmalloced = 0;
+#endif
+
+ return register_filesystem(&smb_fs_type);
+}
+
+static void __exit exit_smb_fs(void)
+{
+ DEBUG1("unregistering ...\n");
+ unregister_filesystem(&smb_fs_type);
+#ifdef DEBUG_SMB_MALLOC
+ printk(KERN_DEBUG "smb_malloced: %d\n", smb_malloced);
+ printk(KERN_DEBUG "smb_current_kmalloced: %d\n",smb_current_kmalloced);
+ printk(KERN_DEBUG "smb_current_vmalloced: %d\n",smb_current_vmalloced);
+#endif
+}
+
+EXPORT_NO_SYMBOLS;
+
+module_init(init_smb_fs)
+module_exit(exit_smb_fs)
+MODULE_LICENSE("GPL");
diff --git a/fs/smbfs/ioctl.c b/fs/smbfs/ioctl.c
new file mode 100644
index 00000000000000..afc45b4f7fd9eb
--- /dev/null
+++ b/fs/smbfs/ioctl.c
@@ -0,0 +1,57 @@
+/*
+ * ioctl.c
+ *
+ * Copyright (C) 1995, 1996 by Volker Lendecke
+ * Copyright (C) 1997 by Volker Lendecke
+ *
+ * Please add a note about your changes to smbfs in the ChangeLog file.
+ */
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/ioctl.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/highuid.h>
+
+#include <linux/smb_fs.h>
+#include <linux/smb_mount.h>
+
+#include <asm/uaccess.h>
+
+#include "proto.h"
+
+int
+smb_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct smb_sb_info *server = server_from_inode(inode);
+ struct smb_conn_opt opt;
+ int result = -EINVAL;
+
+ switch (cmd) {
+ case SMB_IOC_GETMOUNTUID:
+ result = put_user(NEW_TO_OLD_UID(server->mnt->mounted_uid),
+ (uid16_t *) arg);
+ break;
+ case SMB_IOC_GETMOUNTUID32:
+ result = put_user(server->mnt->mounted_uid, (uid_t *) arg);
+ break;
+
+ case SMB_IOC_NEWCONN:
+ /* arg is smb_conn_opt, or NULL if no connection was made */
+ if (!arg) {
+ result = smb_wakeup(server);
+ break;
+ }
+
+ result = -EFAULT;
+ if (!copy_from_user(&opt, (void *)arg, sizeof(opt)))
+ result = smb_newconn(server, &opt);
+ break;
+ default:
+ break;
+ }
+
+ return result;
+}
diff --git a/fs/smbfs/proc.c b/fs/smbfs/proc.c
new file mode 100644
index 00000000000000..6842cb61a7340a
--- /dev/null
+++ b/fs/smbfs/proc.c
@@ -0,0 +1,3366 @@
+/*
+ * proc.c
+ *
+ * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
+ * Copyright (C) 1997 by Volker Lendecke
+ *
+ * Please add a note about your changes to smbfs in the ChangeLog file.
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/dcache.h>
+#include <linux/dirent.h>
+#include <linux/nls.h>
+
+#include <linux/smb_fs.h>
+#include <linux/smbno.h>
+#include <linux/smb_mount.h>
+
+#include <asm/string.h>
+#include <asm/div64.h>
+
+#include "smb_debug.h"
+#include "proto.h"
+
+
+/* Features. Undefine if they cause problems, this should perhaps be a
+ config option. */
+#define SMBFS_POSIX_UNLINK 1
+
+#define SMB_VWV(packet) ((packet) + SMB_HEADER_LEN)
+#define SMB_CMD(packet) (*(packet+8))
+#define SMB_WCT(packet) (*(packet+SMB_HEADER_LEN - 1))
+#define SMB_BCC(packet) smb_bcc(packet)
+#define SMB_BUF(packet) ((packet) + SMB_HEADER_LEN + SMB_WCT(packet) * 2 + 2)
+
+#define SMB_DIRINFO_SIZE 43
+#define SMB_STATUS_SIZE 21
+
+#define SMB_ST_BLKSIZE (PAGE_SIZE)
+#define SMB_ST_BLKSHIFT (PAGE_SHIFT)
+
+static struct smb_ops smb_ops_core;
+static struct smb_ops smb_ops_os2;
+static struct smb_ops smb_ops_win95;
+static struct smb_ops smb_ops_winNT;
+static struct smb_ops smb_ops_unix;
+
+static void
+smb_init_dirent(struct smb_sb_info *server, struct smb_fattr *fattr);
+static void
+smb_finish_dirent(struct smb_sb_info *server, struct smb_fattr *fattr);
+static int
+smb_proc_getattr_core(struct smb_sb_info *server, struct dentry *dir,
+ struct smb_fattr *fattr);
+static int
+smb_proc_getattr_ff(struct smb_sb_info *server, struct dentry *dir,
+ struct smb_fattr *fattr);
+static int
+smb_proc_setattr_ext(struct smb_sb_info *, struct inode *,
+ struct smb_fattr *);
+static int
+smb_proc_setattr_core(struct smb_sb_info *server, struct dentry *dentry,
+ __u16 attr);
+static void
+install_ops(struct smb_ops *dst, struct smb_ops *src);
+static int
+smb_proc_query_cifsunix(struct smb_sb_info *server);
+
+
+static void
+str_upper(char *name, int len)
+{
+ while (len--)
+ {
+ if (*name >= 'a' && *name <= 'z')
+ *name -= ('a' - 'A');
+ name++;
+ }
+}
+
+#if 0
+static void
+str_lower(char *name, int len)
+{
+ while (len--)
+ {
+ if (*name >= 'A' && *name <= 'Z')
+ *name += ('a' - 'A');
+ name++;
+ }
+}
+#endif
+
+/* reverse a string inline. This is used by the dircache walking routines */
+static void reverse_string(char *buf, int len)
+{
+ char c;
+ char *end = buf+len-1;
+
+ while(buf < end) {
+ c = *buf;
+ *(buf++) = *end;
+ *(end--) = c;
+ }
+}
+
+/* no conversion, just a wrapper for memcpy. */
+static int convert_memcpy(unsigned char *output, int olen,
+ const unsigned char *input, int ilen,
+ struct nls_table *nls_from,
+ struct nls_table *nls_to)
+{
+ if (olen < ilen)
+ return -ENAMETOOLONG;
+ memcpy(output, input, ilen);
+ return ilen;
+}
+
+static inline int write_char(unsigned char ch, char *output, int olen)
+{
+ if (olen < 4)
+ return -ENAMETOOLONG;
+ sprintf(output, ":x%02x", ch);
+ return 4;
+}
+
+static inline int write_unichar(wchar_t ch, char *output, int olen)
+{
+ if (olen < 5)
+ return -ENAMETOOLONG;
+ sprintf(output, ":%04x", ch);
+ return 5;
+}
+
+/* convert from one "codepage" to another (possibly being utf8). */
+static int convert_cp(unsigned char *output, int olen,
+ const unsigned char *input, int ilen,
+ struct nls_table *nls_from,
+ struct nls_table *nls_to)
+{
+ int len = 0;
+ int n;
+ wchar_t ch;
+
+ while (ilen > 0) {
+ /* convert by changing to unicode and back to the new cp */
+ n = nls_from->char2uni((unsigned char *)input, ilen, &ch);
+ if (n == -EINVAL) {
+ ilen--;
+ n = write_char(*input++, output, olen);
+ if (n < 0)
+ goto fail;
+ output += n;
+ olen -= n;
+ len += n;
+ continue;
+ } else if (n < 0)
+ goto fail;
+ input += n;
+ ilen -= n;
+
+ n = nls_to->uni2char(ch, output, olen);
+ if (n == -EINVAL)
+ n = write_unichar(ch, output, olen);
+ if (n < 0)
+ goto fail;
+ output += n;
+ olen -= n;
+
+ len += n;
+ }
+ return len;
+fail:
+ return n;
+}
+
+static int setcodepage(struct nls_table **p, char *name)
+{
+ struct nls_table *nls;
+
+ if (!name || !*name) {
+ nls = NULL;
+ } else if ( (nls = load_nls(name)) == NULL) {
+ printk (KERN_ERR "smbfs: failed to load nls '%s'\n", name);
+ return -EINVAL;
+ }
+
+ /* if already set, unload the previous one. */
+ if (*p)
+ unload_nls(*p);
+ *p = nls;
+
+ return 0;
+}
+
+/* Handles all changes to codepage settings. */
+int smb_setcodepage(struct smb_sb_info *server, struct smb_nls_codepage *cp)
+{
+ int n = 0;
+
+ smb_lock_server(server);
+
+ /* Don't load any nls_* at all, if no remote is requested */
+ if (!*cp->remote_name)
+ goto out;
+
+ n = setcodepage(&server->local_nls, cp->local_name);
+ if (n != 0)
+ goto out;
+ n = setcodepage(&server->remote_nls, cp->remote_name);
+ if (n != 0)
+ setcodepage(&server->local_nls, NULL);
+
+out:
+ if (server->local_nls != NULL && server->remote_nls != NULL)
+ server->ops->convert = convert_cp;
+ else
+ server->ops->convert = convert_memcpy;
+
+ smb_unlock_server(server);
+ return n;
+}
+
+
+/*****************************************************************************/
+/* */
+/* Encoding/Decoding section */
+/* */
+/*****************************************************************************/
+
+static __u8 *
+smb_encode_smb_length(__u8 * p, __u32 len)
+{
+ *p = 0;
+ *(p+1) = 0;
+ *(p+2) = (len & 0xFF00) >> 8;
+ *(p+3) = (len & 0xFF);
+ if (len > 0xFFFF)
+ {
+ *(p+1) = 1;
+ }
+ return p + 4;
+}
+
+/*
+ * smb_build_path: build the path to entry and name storing it in buf.
+ * The path returned will have the trailing '\0'.
+ */
+static int smb_build_path(struct smb_sb_info *server, char * buf, int maxlen,
+ struct dentry * entry, struct qstr * name)
+{
+ char *path = buf;
+ int len;
+
+ if (maxlen < 2)
+ return -ENAMETOOLONG;
+
+ if (maxlen > SMB_MAXPATHLEN + 1)
+ maxlen = SMB_MAXPATHLEN + 1;
+
+ if (entry == NULL)
+ goto test_name_and_out;
+
+ /*
+ * If IS_ROOT, we have to do no walking at all.
+ */
+ if (IS_ROOT(entry) && !name) {
+ *path++ = '\\';
+ *path++ = '\0';
+ return 2;
+ }
+
+ /*
+ * Build the path string walking the tree backward from end to ROOT
+ * and store it in reversed order [see reverse_string()]
+ */
+ while (!IS_ROOT(entry)) {
+ if (maxlen < 3)
+ return -ENAMETOOLONG;
+
+ len = server->ops->convert(path, maxlen-2,
+ entry->d_name.name, entry->d_name.len,
+ server->local_nls, server->remote_nls);
+ if (len < 0)
+ return len;
+ reverse_string(path, len);
+ path += len;
+ *path++ = '\\';
+ maxlen -= len+1;
+
+ entry = entry->d_parent;
+ if (IS_ROOT(entry))
+ break;
+ }
+ reverse_string(buf, path-buf);
+
+ /* maxlen is at least 1 */
+test_name_and_out:
+ if (name) {
+ if (maxlen < 3)
+ return -ENAMETOOLONG;
+ *path++ = '\\';
+ len = server->ops->convert(path, maxlen-2,
+ name->name, name->len,
+ server->local_nls, server->remote_nls);
+ if (len < 0)
+ return len;
+ path += len;
+ maxlen -= len+1;
+ }
+ /* maxlen is at least 1 */
+ *path++ = '\0';
+ return path-buf;
+}
+
+static int smb_encode_path(struct smb_sb_info *server, char *buf, int maxlen,
+ struct dentry *dir, struct qstr *name)
+{
+ int result;
+
+ result = smb_build_path(server, buf, maxlen, dir, name);
+ if (result < 0)
+ goto out;
+ if (server->opt.protocol <= SMB_PROTOCOL_COREPLUS)
+ str_upper(buf, result);
+out:
+ return result;
+}
+
+static int smb_simple_encode_path(struct smb_sb_info *server, char **p,
+ struct dentry * entry, struct qstr * name)
+{
+ char *s = *p;
+ int res;
+ int maxlen = ((char *)server->packet + server->packet_size) - s;
+
+ if (!maxlen)
+ return -ENAMETOOLONG;
+ *s++ = 4;
+ res = smb_encode_path(server, s, maxlen-1, entry, name);
+ if (res < 0)
+ return res;
+ *p = s + res;
+ return 0;
+}
+
+/* The following are taken directly from msdos-fs */
+
+/* Linear day numbers of the respective 1sts in non-leap years. */
+
+static int day_n[] =
+{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, 0};
+ /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */
+
+
+static time_t
+utc2local(struct smb_sb_info *server, time_t time)
+{
+ return time - server->opt.serverzone*60;
+}
+
+static time_t
+local2utc(struct smb_sb_info *server, time_t time)
+{
+ return time + server->opt.serverzone*60;
+}
+
+/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
+
+static time_t
+date_dos2unix(struct smb_sb_info *server, __u16 date, __u16 time)
+{
+ int month, year;
+ time_t secs;
+
+ /* first subtract and mask after that... Otherwise, if
+ date == 0, bad things happen */
+ month = ((date >> 5) - 1) & 15;
+ year = date >> 9;
+ secs = (time & 31) * 2 + 60 * ((time >> 5) & 63) + (time >> 11) * 3600 + 86400 *
+ ((date & 31) - 1 + day_n[month] + (year / 4) + year * 365 - ((year & 3) == 0 &&
+ month < 2 ? 1 : 0) + 3653);
+ /* days since 1.1.70 plus 80's leap day */
+ return local2utc(server, secs);
+}
+
+
+/* Convert linear UNIX date to a MS-DOS time/date pair. */
+
+static void
+date_unix2dos(struct smb_sb_info *server,
+ int unix_date, __u16 *date, __u16 *time)
+{
+ int day, year, nl_day, month;
+
+ unix_date = utc2local(server, unix_date);
+ if (unix_date < 315532800)
+ unix_date = 315532800;
+
+ *time = (unix_date % 60) / 2 +
+ (((unix_date / 60) % 60) << 5) +
+ (((unix_date / 3600) % 24) << 11);
+
+ day = unix_date / 86400 - 3652;
+ year = day / 365;
+ if ((year + 3) / 4 + 365 * year > day)
+ year--;
+ day -= (year + 3) / 4 + 365 * year;
+ if (day == 59 && !(year & 3)) {
+ nl_day = day;
+ month = 2;
+ } else {
+ nl_day = (year & 3) || day <= 59 ? day : day - 1;
+ for (month = 0; month < 12; month++)
+ if (day_n[month] > nl_day)
+ break;
+ }
+ *date = nl_day - day_n[month - 1] + 1 + (month << 5) + (year << 9);
+}
+
+/* The following are taken from fs/ntfs/util.c */
+
+#define NTFS_TIME_OFFSET ((u64)(369*365 + 89) * 24 * 3600 * 10000000)
+
+/*
+ * Convert the NT UTC (based 1601-01-01, in hundred nanosecond units)
+ * into Unix UTC (based 1970-01-01, in seconds).
+ */
+static time_t
+smb_ntutc2unixutc(u64 ntutc)
+{
+ /* FIXME: what about the timezone difference? */
+ /* Subtract the NTFS time offset, then convert to 1s intervals. */
+ u64 t = ntutc - NTFS_TIME_OFFSET;
+ do_div(t, 10000000);
+ return (time_t)t;
+}
+
+static u64
+smb_unixutc2ntutc(time_t t)
+{
+ /* Note: timezone conversion is probably wrong. */
+ return ((u64)t) * 10000000 + NTFS_TIME_OFFSET;
+}
+
+#define MAX_FILE_MODE 6
+static mode_t file_mode[] = {
+ S_IFREG, S_IFDIR, S_IFLNK, S_IFCHR, S_IFBLK, S_IFIFO, S_IFSOCK
+};
+
+static int smb_filetype_to_mode(u32 filetype)
+{
+ if (filetype > MAX_FILE_MODE) {
+ PARANOIA("Filetype out of range: %d\n", filetype);
+ return S_IFREG;
+ }
+ return file_mode[filetype];
+}
+
+static u32 smb_filetype_from_mode(int mode)
+{
+ if (mode & S_IFREG)
+ return UNIX_TYPE_FILE;
+ if (mode & S_IFDIR)
+ return UNIX_TYPE_DIR;
+ if (mode & S_IFLNK)
+ return UNIX_TYPE_SYMLINK;
+ if (mode & S_IFCHR)
+ return UNIX_TYPE_CHARDEV;
+ if (mode & S_IFBLK)
+ return UNIX_TYPE_BLKDEV;
+ if (mode & S_IFIFO)
+ return UNIX_TYPE_FIFO;
+ if (mode & S_IFSOCK)
+ return UNIX_TYPE_SOCKET;
+ return UNIX_TYPE_UNKNOWN;
+}
+
+
+/*****************************************************************************/
+/* */
+/* Support section. */
+/* */
+/*****************************************************************************/
+
+__u32
+smb_len(__u8 * p)
+{
+ return ((*(p+1) & 0x1) << 16L) | (*(p+2) << 8L) | *(p+3);
+}
+
+static __u16
+smb_bcc(__u8 * packet)
+{
+ int pos = SMB_HEADER_LEN + SMB_WCT(packet) * sizeof(__u16);
+ return WVAL(packet, pos);
+}
+
+/* smb_valid_packet: We check if packet fulfills the basic
+ requirements of a smb packet */
+
+static int
+smb_valid_packet(__u8 * packet)
+{
+ return (packet[4] == 0xff
+ && packet[5] == 'S'
+ && packet[6] == 'M'
+ && packet[7] == 'B'
+ && (smb_len(packet) + 4 == SMB_HEADER_LEN
+ + SMB_WCT(packet) * 2 + SMB_BCC(packet)));
+}
+
+/* smb_verify: We check if we got the answer we expected, and if we
+ got enough data. If bcc == -1, we don't care. */
+
+static int
+smb_verify(__u8 * packet, int command, int wct, int bcc)
+{
+ if (SMB_CMD(packet) != command)
+ goto bad_command;
+ if (SMB_WCT(packet) < wct)
+ goto bad_wct;
+ if (bcc != -1 && SMB_BCC(packet) < bcc)
+ goto bad_bcc;
+ return 0;
+
+bad_command:
+ printk(KERN_ERR "smb_verify: command=%x, SMB_CMD=%x??\n",
+ command, SMB_CMD(packet));
+ goto fail;
+bad_wct:
+ printk(KERN_ERR "smb_verify: command=%x, wct=%d, SMB_WCT=%d??\n",
+ command, wct, SMB_WCT(packet));
+ goto fail;
+bad_bcc:
+ printk(KERN_ERR "smb_verify: command=%x, bcc=%d, SMB_BCC=%d??\n",
+ command, bcc, SMB_BCC(packet));
+fail:
+ return -EIO;
+}
+
+/*
+ * Returns the maximum read or write size for the "payload". Making all of the
+ * packet fit within the negotiated max_xmit size.
+ *
+ * N.B. Since this value is usually computed before locking the server,
+ * the server's packet size must never be decreased!
+ */
+static inline int
+smb_get_xmitsize(struct smb_sb_info *server, int overhead)
+{
+ return server->opt.max_xmit - overhead;
+}
+
+/*
+ * Calculate the maximum read size
+ */
+int
+smb_get_rsize(struct smb_sb_info *server)
+{
+ /* readX has 12 parameters, read has 5 */
+ int overhead = SMB_HEADER_LEN + 12 * sizeof(__u16) + 2 + 1 + 2;
+ int size = smb_get_xmitsize(server, overhead);
+
+ VERBOSE("packet=%d, xmit=%d, size=%d\n",
+ server->packet_size, server->opt.max_xmit, size);
+
+ return size;
+}
+
+/*
+ * Calculate the maximum write size
+ */
+int
+smb_get_wsize(struct smb_sb_info *server)
+{
+ /* writeX has 14 parameters, write has 5 */
+ int overhead = SMB_HEADER_LEN + 14 * sizeof(__u16) + 2 + 1 + 2;
+ int size = smb_get_xmitsize(server, overhead);
+
+ VERBOSE("packet=%d, xmit=%d, size=%d\n",
+ server->packet_size, server->opt.max_xmit, size);
+
+ return size;
+}
+
+/*
+ * Convert SMB error codes to -E... errno values.
+ */
+int
+smb_errno(struct smb_sb_info *server)
+{
+ int errcls = server->rcls;
+ int error = server->err;
+ char *class = "Unknown";
+
+ VERBOSE("errcls %d code %d from command 0x%x\n",
+ errcls, error, SMB_CMD(server->packet));
+
+ if (errcls == ERRDOS) {
+ switch (error) {
+ case ERRbadfunc:
+ return -EINVAL;
+ case ERRbadfile:
+ case ERRbadpath:
+ return -ENOENT;
+ case ERRnofids:
+ return -EMFILE;
+ case ERRnoaccess:
+ return -EACCES;
+ case ERRbadfid:
+ return -EBADF;
+ case ERRbadmcb:
+ return -EREMOTEIO;
+ case ERRnomem:
+ return -ENOMEM;
+ case ERRbadmem:
+ return -EFAULT;
+ case ERRbadenv:
+ case ERRbadformat:
+ return -EREMOTEIO;
+ case ERRbadaccess:
+ return -EACCES;
+ case ERRbaddata:
+ return -E2BIG;
+ case ERRbaddrive:
+ return -ENXIO;
+ case ERRremcd:
+ return -EREMOTEIO;
+ case ERRdiffdevice:
+ return -EXDEV;
+ case ERRnofiles:
+ return -ENOENT;
+ case ERRbadshare:
+ return -ETXTBSY;
+ case ERRlock:
+ return -EDEADLK;
+ case ERRfilexists:
+ return -EEXIST;
+ case ERROR_INVALID_PARAMETER:
+ return -EINVAL;
+ case ERROR_DISK_FULL:
+ return -ENOSPC;
+ case ERROR_INVALID_NAME:
+ return -ENOENT;
+ case ERROR_DIR_NOT_EMPTY:
+ return -ENOTEMPTY;
+ case ERROR_NOT_LOCKED:
+ return -ENOLCK;
+ case ERROR_ALREADY_EXISTS:
+ return -EEXIST;
+ default:
+ class = "ERRDOS";
+ goto err_unknown;
+ }
+ } else if (errcls == ERRSRV) {
+ switch (error) {
+ /* N.B. This is wrong ... EIO ? */
+ case ERRerror:
+ return -ENFILE;
+ case ERRbadpw:
+ return -EINVAL;
+ case ERRbadtype:
+ return -EIO;
+ case ERRaccess:
+ return -EACCES;
+ /*
+ * This is a fatal error, as it means the "tree ID"
+ * for this connection is no longer valid. We map
+ * to a special error code and get a new connection.
+ */
+ case ERRinvnid:
+ return -EBADSLT;
+ default:
+ class = "ERRSRV";
+ goto err_unknown;
+ }
+ } else if (errcls == ERRHRD) {
+ switch (error) {
+ case ERRnowrite:
+ return -EROFS;
+ case ERRbadunit:
+ return -ENODEV;
+ case ERRnotready:
+ return -EUCLEAN;
+ case ERRbadcmd:
+ case ERRdata:
+ return -EIO;
+ case ERRbadreq:
+ return -ERANGE;
+ case ERRbadshare:
+ return -ETXTBSY;
+ case ERRlock:
+ return -EDEADLK;
+ case ERRdiskfull:
+ return -ENOSPC;
+ default:
+ class = "ERRHRD";
+ goto err_unknown;
+ }
+ } else if (errcls == ERRCMD) {
+ class = "ERRCMD";
+ } else if (errcls == SUCCESS) {
+ return 0; /* This is the only valid 0 return */
+ }
+
+err_unknown:
+ printk(KERN_ERR "smb_errno: class %s, code %d from command 0x%x\n",
+ class, error, SMB_CMD(server->packet));
+ return -EIO;
+}
+
+/*
+ * smb_retry: This function should be called when smb_request_ok has
+ * indicated an error. If the error was indicated because the
+ * connection was killed, we try to reconnect. If smb_retry returns 0,
+ * the error was indicated for another reason, so a retry would not be
+ * of any use.
+ * N.B. The server must be locked for this call.
+ */
+static int
+smb_retry(struct smb_sb_info *server)
+{
+ pid_t pid = server->conn_pid;
+ int error, result = 0;
+
+ if (server->state == CONN_VALID || server->state == CONN_RETRYING)
+ goto out;
+
+ smb_close_socket(server);
+
+ if (pid == 0) {
+ printk(KERN_ERR "smb_retry: no connection process\n");
+ server->state = CONN_RETRIED;
+ goto out;
+ }
+
+ /*
+ * Change state so that only one retry per server will be started.
+ */
+ server->state = CONN_RETRYING;
+
+ /*
+ * Note: use the "priv" flag, as a user process may need to reconnect.
+ */
+ error = kill_proc(pid, SIGUSR1, 1);
+ if (error) {
+ /* FIXME: this is fatal */
+ printk(KERN_ERR "smb_retry: signal failed, error=%d\n", error);
+ goto out;
+ }
+ VERBOSE("signalled pid %d, waiting for new connection\n", pid);
+
+ /*
+ * Wait for the new connection.
+ */
+ smb_unlock_server(server);
+ interruptible_sleep_on_timeout(&server->wait, server->mnt->timeo*HZ);
+ smb_lock_server(server);
+ if (signal_pending(current))
+ printk(KERN_INFO "smb_retry: caught signal\n");
+
+ /*
+ * Check for a valid connection.
+ */
+ if (server->state == CONN_VALID) {
+ /* This should be changed to VERBOSE, except many smbfs
+ problems is with the userspace daemon not reconnecting. */
+ PARANOIA("successful, new pid=%d, generation=%d\n",
+ server->conn_pid, server->generation);
+ result = 1;
+ } else if (server->state == CONN_RETRYING) {
+ /* allow further attempts later */
+ server->state = CONN_RETRIED;
+ }
+
+out:
+ return result;
+}
+
+/* smb_request_ok: We expect the server to be locked. Then we do the
+ request and check the answer completely. When smb_request_ok
+ returns 0, you can be quite sure that everything went well. When
+ the answer is <=0, the returned number is a valid unix errno. */
+
+static int
+smb_request_ok(struct smb_sb_info *s, int command, int wct, int bcc)
+{
+ int result = -EIO;
+
+ s->rcls = 0;
+ s->err = 0;
+
+ /* Make sure we have a connection */
+ if (s->state != CONN_VALID) {
+ if (!smb_retry(s))
+ goto out;
+ }
+
+ if (smb_request(s) < 0) {
+ DEBUG1("smb_request failed\n");
+ goto out;
+ }
+ if (smb_valid_packet(s->packet) != 0) {
+ PARANOIA("invalid packet!\n");
+ goto out;
+ }
+
+ /*
+ * Check for server errors.
+ */
+ if (s->rcls != 0) {
+ result = smb_errno(s);
+ if (!result)
+ printk(KERN_DEBUG "smb_request_ok: rcls=%d, err=%d mapped to 0\n",
+ s->rcls, s->err);
+ /*
+ * Exit now even if the error was squashed ...
+ * packet verify will fail anyway.
+ */
+ goto out;
+ }
+ result = smb_verify(s->packet, command, wct, bcc);
+
+out:
+ return result;
+}
+
+/*
+ * This implements the NEWCONN ioctl. It installs the server pid,
+ * sets server->state to CONN_VALID, and wakes up the waiting process.
+ */
+int
+smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt)
+{
+ struct file *filp;
+ int error;
+
+ VERBOSE("fd=%d, pid=%d\n", opt->fd, current->pid);
+
+ smb_lock_server(server);
+
+ /*
+ * Make sure we don't already have a valid connection ...
+ */
+ error = -EINVAL;
+ if (server->state == CONN_VALID)
+ goto out;
+
+ error = -EACCES;
+ if (current->uid != server->mnt->mounted_uid &&
+ !capable(CAP_SYS_ADMIN))
+ goto out;
+
+ error = -EBADF;
+ filp = fget(opt->fd);
+ if (!filp)
+ goto out;
+ if (!smb_valid_socket(filp->f_dentry->d_inode))
+ goto out_putf;
+
+ server->sock_file = filp;
+ server->conn_pid = current->pid;
+ smb_catch_keepalive(server);
+ server->opt = *opt;
+ server->generation += 1;
+ server->state = CONN_VALID;
+ error = 0;
+
+ /* check if we have an old smbmount that uses seconds for the
+ serverzone */
+ if (server->opt.serverzone > 12*60 || server->opt.serverzone < -12*60)
+ server->opt.serverzone /= 60;
+
+ /* now that we have an established connection we can detect the server
+ type and enable bug workarounds */
+ if (server->opt.protocol < SMB_PROTOCOL_LANMAN2)
+ install_ops(server->ops, &smb_ops_core);
+ else if (server->opt.protocol == SMB_PROTOCOL_LANMAN2)
+ install_ops(server->ops, &smb_ops_os2);
+ else if (server->opt.protocol == SMB_PROTOCOL_NT1 &&
+ (server->opt.max_xmit < 0x1000) &&
+ !(server->opt.capabilities & SMB_CAP_NT_SMBS)) {
+ /* FIXME: can we kill the WIN95 flag now? */
+ server->mnt->flags |= SMB_MOUNT_WIN95;
+ VERBOSE("detected WIN95 server\n");
+ install_ops(server->ops, &smb_ops_win95);
+ } else {
+ /*
+ * Samba has max_xmit 65535
+ * NT4spX has max_xmit 4536 (or something like that)
+ * win2k has ...
+ */
+ VERBOSE("detected NT1 (Samba, NT4/5) server\n");
+ install_ops(server->ops, &smb_ops_winNT);
+ }
+
+ /* FIXME: the win9x code wants to modify these ... (seek/trunc bug) */
+ if (server->mnt->flags & SMB_MOUNT_OLDATTR) {
+ server->ops->getattr = smb_proc_getattr_core;
+ } else if (server->mnt->flags & SMB_MOUNT_DIRATTR) {
+ server->ops->getattr = smb_proc_getattr_ff;
+ }
+
+ /* Decode server capabilities */
+ if (server->opt.capabilities & SMB_CAP_LARGE_FILES) {
+ /* Should be ok to set this now, as no one can access the
+ mount until the connection has been established. */
+ SB_of(server)->s_maxbytes = ~0ULL >> 1;
+ VERBOSE("LFS enabled\n");
+ }
+#ifndef CONFIG_SMB_UNIX
+ server->opt.capabilities &= ~SMB_CAP_UNIX;
+#endif
+ if (server->opt.capabilities & SMB_CAP_UNIX) {
+ struct inode *inode;
+ VERBOSE("Using UNIX CIFS extensions\n");
+ install_ops(server->ops, &smb_ops_unix);
+ inode = SB_of(server)->s_root->d_inode;
+ if (inode)
+ inode->i_op = &smb_dir_inode_operations_unix;
+ }
+
+ VERBOSE("protocol=%d, max_xmit=%d, pid=%d capabilities=0x%x\n",
+ server->opt.protocol, server->opt.max_xmit, server->conn_pid,
+ server->opt.capabilities);
+
+ /* Make sure we can fit a message of the negotiated size in our
+ packet buffer. */
+ if (server->opt.max_xmit > server->packet_size) {
+ int len = smb_round_length(server->opt.max_xmit);
+ char *buf = smb_vmalloc(len);
+ if (buf) {
+ if (server->packet)
+ smb_vfree(server->packet);
+ server->packet = buf;
+ server->packet_size = len;
+ } else {
+ /* else continue with the too small buffer? */
+ PARANOIA("Failed to allocate new packet buffer: "
+ "max_xmit=%d, packet_size=%d\n",
+ server->opt.max_xmit, server->packet_size);
+ server->opt.max_xmit = server->packet_size;
+ }
+ }
+
+ if (server->opt.capabilities & SMB_CAP_UNIX)
+ smb_proc_query_cifsunix(server);
+
+out:
+ smb_unlock_server(server);
+ smb_wakeup(server);
+ return error;
+
+out_putf:
+ fput(filp);
+ goto out;
+}
+
+int
+smb_wakeup(struct smb_sb_info *server)
+{
+ wake_up_interruptible(&server->wait);
+ return 0;
+}
+
+/* smb_setup_header: We completely set up the packet. You only have to
+ insert the command-specific fields */
+
+__u8 *
+smb_setup_header(struct smb_sb_info * server, __u8 command, __u16 wct, __u16 bcc)
+{
+ __u32 xmit_len = SMB_HEADER_LEN + wct * sizeof(__u16) + bcc + 2;
+ __u8 *p = server->packet;
+ __u8 *buf = server->packet;
+
+ if (xmit_len > server->packet_size)
+ printk(KERN_DEBUG "smb_setup_header: "
+ "Aieee, xmit len > packet! len=%d, size=%d\n",
+ xmit_len, server->packet_size);
+
+ p = smb_encode_smb_length(p, xmit_len - 4);
+
+ *p++ = 0xff;
+ *p++ = 'S';
+ *p++ = 'M';
+ *p++ = 'B';
+ *p++ = command;
+
+ memset(p, '\0', 19);
+ p += 19;
+ p += 8;
+
+ WSET(buf, smb_tid, server->opt.tid);
+ WSET(buf, smb_pid, 1);
+ WSET(buf, smb_uid, server->opt.server_uid);
+ WSET(buf, smb_mid, 1);
+
+ if (server->opt.protocol > SMB_PROTOCOL_CORE)
+ {
+ *(buf+smb_flg) = 0x8;
+ WSET(buf, smb_flg2, 0x3);
+ }
+ *p++ = wct; /* wct */
+ p += 2 * wct;
+ WSET(p, 0, bcc);
+ return p + 2;
+}
+
+static void
+smb_setup_bcc(struct smb_sb_info *server, __u8 * p)
+{
+ __u8 *packet = server->packet;
+ __u8 *pbcc = packet + SMB_HEADER_LEN + 2 * SMB_WCT(packet);
+ __u16 bcc = p - (pbcc + 2);
+
+ WSET(pbcc, 0, bcc);
+ smb_encode_smb_length(packet,
+ SMB_HEADER_LEN + 2 * SMB_WCT(packet) - 2 + bcc);
+}
+
+/*
+ * Called with the server locked
+ */
+static int
+smb_proc_seek(struct smb_sb_info *server, __u16 fileid,
+ __u16 mode, off_t offset)
+{
+ int result;
+
+ smb_setup_header(server, SMBlseek, 4, 0);
+ WSET(server->packet, smb_vwv0, fileid);
+ WSET(server->packet, smb_vwv1, mode);
+ DSET(server->packet, smb_vwv2, offset);
+
+ result = smb_request_ok(server, SMBlseek, 2, 0);
+ if (result < 0) {
+ result = 0;
+ goto out;
+ }
+
+ result = DVAL(server->packet, smb_vwv0);
+out:
+ return result;
+}
+
+/*
+ * We're called with the server locked, and we leave it that way.
+ */
+static int
+smb_proc_open(struct smb_sb_info *server, struct dentry *dentry, int wish)
+{
+ struct inode *ino = dentry->d_inode;
+ int mode, read_write = 0x42, read_only = 0x40;
+ int res;
+ char *p;
+
+ /*
+ * Attempt to open r/w, unless there are no write privileges.
+ */
+ mode = read_write;
+ if (!(ino->i_mode & (S_IWUSR | S_IWGRP | S_IWOTH)))
+ mode = read_only;
+#if 0
+ /* FIXME: why is this code not in? below we fix it so that a caller
+ wanting RO doesn't get RW. smb_revalidate_inode does some
+ optimization based on access mode. tail -f needs it to be correct.
+
+ We must open rw since we don't do the open if called a second time
+ with different 'wish'. Is that not supported by smb servers? */
+ if (!(wish & (O_WRONLY | O_RDWR)))
+ mode = read_only;
+#endif
+
+ retry:
+ p = smb_setup_header(server, SMBopen, 2, 0);
+ WSET(server->packet, smb_vwv0, mode);
+ WSET(server->packet, smb_vwv1, aSYSTEM | aHIDDEN | aDIR);
+ res = smb_simple_encode_path(server, &p, dentry, NULL);
+ if (res < 0)
+ goto out;
+ smb_setup_bcc(server, p);
+
+ res = smb_request_ok(server, SMBopen, 7, 0);
+ if (res != 0) {
+ if (smb_retry(server))
+ goto retry;
+
+ if (mode == read_write &&
+ (res == -EACCES || res == -ETXTBSY || res == -EROFS))
+ {
+ VERBOSE("%s/%s R/W failed, error=%d, retrying R/O\n",
+ DENTRY_PATH(dentry), res);
+ mode = read_only;
+ goto retry;
+ }
+ goto out;
+ }
+ /* We should now have data in vwv[0..6]. */
+
+ ino->u.smbfs_i.fileid = WVAL(server->packet, smb_vwv0);
+ ino->u.smbfs_i.attr = WVAL(server->packet, smb_vwv1);
+ /* smb_vwv2 has mtime */
+ /* smb_vwv4 has size */
+ ino->u.smbfs_i.access = (WVAL(server->packet, smb_vwv6) & SMB_ACCMASK);
+ ino->u.smbfs_i.open = server->generation;
+
+out:
+ return res;
+}
+
+/*
+ * Make sure the file is open, and check that the access
+ * is compatible with the desired access.
+ */
+int
+smb_open(struct dentry *dentry, int wish)
+{
+ struct inode *inode = dentry->d_inode;
+ int result;
+
+ result = -ENOENT;
+ if (!inode) {
+ printk(KERN_ERR "smb_open: no inode for dentry %s/%s\n",
+ DENTRY_PATH(dentry));
+ goto out;
+ }
+
+ if (!smb_is_open(inode)) {
+ struct smb_sb_info *server = server_from_inode(inode);
+ smb_lock_server(server);
+ result = 0;
+ if (!smb_is_open(inode))
+ result = smb_proc_open(server, dentry, wish);
+ smb_unlock_server(server);
+ if (result) {
+ PARANOIA("%s/%s open failed, result=%d\n",
+ DENTRY_PATH(dentry), result);
+ goto out;
+ }
+ /*
+ * A successful open means the path is still valid ...
+ */
+ smb_renew_times(dentry);
+ }
+
+ /*
+ * Check whether the access is compatible with the desired mode.
+ */
+ result = 0;
+ if (inode->u.smbfs_i.access != wish &&
+ inode->u.smbfs_i.access != SMB_O_RDWR)
+ {
+ PARANOIA("%s/%s access denied, access=%x, wish=%x\n",
+ DENTRY_PATH(dentry), inode->u.smbfs_i.access, wish);
+ result = -EACCES;
+ }
+out:
+ return result;
+}
+
+/* We're called with the server locked */
+
+static int
+smb_proc_close(struct smb_sb_info *server, __u16 fileid, __u32 mtime)
+{
+ smb_setup_header(server, SMBclose, 3, 0);
+ WSET(server->packet, smb_vwv0, fileid);
+ DSET(server->packet, smb_vwv1, utc2local(server, mtime));
+ return smb_request_ok(server, SMBclose, 0, 0);
+}
+
+/*
+ * Called with the server locked.
+ *
+ * Win NT 4.0 has an apparent bug in that it fails to update the
+ * modify time when writing to a file. As a workaround, we update
+ * both modify and access time locally, and post the times to the
+ * server when closing the file.
+ */
+static int
+smb_proc_close_inode(struct smb_sb_info *server, struct inode * ino)
+{
+ int result = 0;
+ if (smb_is_open(ino))
+ {
+ /*
+ * We clear the open flag in advance, in case another
+ * process observes the value while we block below.
+ */
+ ino->u.smbfs_i.open = 0;
+
+ /*
+ * Kludge alert: SMB timestamps are accurate only to
+ * two seconds ... round the times to avoid needless
+ * cache invalidations!
+ */
+ if (ino->i_mtime & 1)
+ ino->i_mtime--;
+ if (ino->i_atime & 1)
+ ino->i_atime--;
+ /*
+ * If the file is open with write permissions,
+ * update the time stamps to sync mtime and atime.
+ */
+ if ((server->opt.capabilities & SMB_CAP_UNIX) == 0 &&
+ (server->opt.protocol >= SMB_PROTOCOL_LANMAN2) &&
+ !(ino->u.smbfs_i.access == SMB_O_RDONLY))
+ {
+ struct smb_fattr fattr;
+ smb_get_inode_attr(ino, &fattr);
+ smb_proc_setattr_ext(server, ino, &fattr);
+ }
+
+ result = smb_proc_close(server, ino->u.smbfs_i.fileid,
+ ino->i_mtime);
+ /*
+ * Force a revalidation after closing ... some servers
+ * don't post the size until the file has been closed.
+ */
+ if (server->opt.protocol < SMB_PROTOCOL_NT1)
+ ino->u.smbfs_i.oldmtime = 0;
+ ino->u.smbfs_i.closed = jiffies;
+ }
+ return result;
+}
+
+int
+smb_close(struct inode *ino)
+{
+ int result = 0;
+
+ if (smb_is_open(ino)) {
+ struct smb_sb_info *server = server_from_inode(ino);
+ smb_lock_server(server);
+ result = smb_proc_close_inode(server, ino);
+ smb_unlock_server(server);
+ }
+ return result;
+}
+
+/*
+ * This is used to close a file following a failed instantiate.
+ * Since we don't have an inode, we can't use any of the above.
+ */
+int
+smb_close_fileid(struct dentry *dentry, __u16 fileid)
+{
+ struct smb_sb_info *server = server_from_dentry(dentry);
+ int result;
+
+ smb_lock_server(server);
+ result = smb_proc_close(server, fileid, CURRENT_TIME);
+ smb_unlock_server(server);
+ return result;
+}
+
+/* In smb_proc_read and smb_proc_write we do not retry, because the
+ file-id would not be valid after a reconnection. */
+
+static int
+smb_proc_read(struct inode *inode, loff_t offset, int count, char *data)
+{
+ struct smb_sb_info *server = server_from_inode(inode);
+ __u16 returned_count, data_len;
+ unsigned char *buf;
+ int result;
+
+ smb_lock_server(server);
+ smb_setup_header(server, SMBread, 5, 0);
+ buf = server->packet;
+ WSET(buf, smb_vwv0, inode->u.smbfs_i.fileid);
+ WSET(buf, smb_vwv1, count);
+ DSET(buf, smb_vwv2, offset);
+ WSET(buf, smb_vwv4, 0);
+
+ result = smb_request_ok(server, SMBread, 5, -1);
+ if (result < 0)
+ goto out;
+ returned_count = WVAL(server->packet, smb_vwv0);
+
+ buf = SMB_BUF(server->packet);
+ data_len = WVAL(buf, 1);
+
+ /* we can NOT simply trust the data_len given by the server ... */
+ if (data_len > count ||
+ (buf+3 - server->packet) + data_len > server->packet_size) {
+ printk(KERN_ERR "smb_proc_read: invalid data length/offset!! "
+ "%d > %d || (%p - %p) + %d > %d\n",
+ data_len, count,
+ buf+3, server->packet, data_len, server->packet_size);
+ result = -EIO;
+ goto out;
+ }
+
+ memcpy(data, buf+3, data_len);
+
+ if (returned_count != data_len) {
+ printk(KERN_NOTICE "smb_proc_read: returned != data_len\n");
+ printk(KERN_NOTICE "smb_proc_read: ret_c=%d, data_len=%d\n",
+ returned_count, data_len);
+ }
+ result = data_len;
+
+out:
+ VERBOSE("ino=%ld, fileid=%d, count=%d, result=%d\n",
+ inode->i_ino, inode->u.smbfs_i.fileid, count, result);
+ smb_unlock_server(server);
+ return result;
+}
+
+static int
+smb_proc_write(struct inode *inode, loff_t offset, int count, const char *data)
+{
+ struct smb_sb_info *server = server_from_inode(inode);
+ int result;
+ __u8 *p;
+ __u16 fileid = inode->u.smbfs_i.fileid;
+
+ VERBOSE("ino=%ld, fileid=%d, count=%d@%Ld, packet_size=%d\n",
+ inode->i_ino, inode->u.smbfs_i.fileid, count, offset,
+ server->packet_size);
+
+ smb_lock_server(server);
+ p = smb_setup_header(server, SMBwrite, 5, count + 3);
+ WSET(server->packet, smb_vwv0, fileid);
+ WSET(server->packet, smb_vwv1, count);
+ DSET(server->packet, smb_vwv2, offset);
+ WSET(server->packet, smb_vwv4, 0);
+
+ *p++ = 1;
+ WSET(p, 0, count);
+ memcpy(p+2, data, count);
+
+ result = smb_request_ok(server, SMBwrite, 1, 0);
+ if (result >= 0)
+ result = WVAL(server->packet, smb_vwv0);
+
+ smb_unlock_server(server);
+ return result;
+}
+
+/*
+ * In smb_proc_readX and smb_proc_writeX we do not retry, because the
+ * file-id would not be valid after a reconnection.
+ */
+static int
+smb_proc_readX(struct inode *inode, loff_t offset, int count, char *data)
+{
+ struct smb_sb_info *server = server_from_inode(inode);
+ u16 data_len, data_off;
+ unsigned char *buf;
+ int result;
+
+ smb_lock_server(server);
+ smb_setup_header(server, SMBreadX, 12, 0);
+ buf = server->packet;
+ WSET(buf, smb_vwv0, 0x00ff);
+ WSET(buf, smb_vwv1, 0);
+ WSET(buf, smb_vwv2, inode->u.smbfs_i.fileid);
+ DSET(buf, smb_vwv3, (u32)offset); /* low 32 bits */
+ WSET(buf, smb_vwv5, count);
+ WSET(buf, smb_vwv6, 0);
+ DSET(buf, smb_vwv7, 0);
+ WSET(buf, smb_vwv9, 0);
+ DSET(buf, smb_vwv10, (u32)(offset >> 32)); /* high 32 bits */
+ WSET(buf, smb_vwv11, 0);
+
+ result = smb_request_ok(server, SMBreadX, 12, -1);
+ if (result < 0)
+ goto out;
+ data_len = WVAL(server->packet, smb_vwv5);
+ data_off = WVAL(server->packet, smb_vwv6);
+ buf = smb_base(server->packet) + data_off;
+
+ /* we can NOT simply trust the info given by the server ... */
+ if (data_len > count ||
+ (buf - server->packet) + data_len > server->packet_size) {
+ printk(KERN_ERR "smb_proc_readX: invalid data length/offset!! "
+ "%d > %d || (%p - %p) + %d > %d\n",
+ data_len, count,
+ buf, server->packet, data_len, server->packet_size);
+ result = -EIO;
+ goto out;
+ }
+
+ memcpy(data, buf, data_len);
+ result = data_len;
+
+out:
+ smb_unlock_server(server);
+ VERBOSE("ino=%ld, fileid=%d, count=%d, result=%d\n",
+ inode->i_ino, inode->u.smbfs_i.fileid, count, result);
+ return result;
+}
+
+static int
+smb_proc_writeX(struct inode *inode, loff_t offset, int count, const char *data)
+{
+ struct smb_sb_info *server = server_from_inode(inode);
+ int result;
+ u8 *p;
+
+ VERBOSE("ino=%ld, fileid=%d, count=%d@%Ld, packet_size=%d\n",
+ inode->i_ino, inode->u.smbfs_i.fileid, count, offset,
+ server->packet_size);
+
+ smb_lock_server(server);
+ p = smb_setup_header(server, SMBwriteX, 14, count + 2);
+ WSET(server->packet, smb_vwv0, 0x00ff);
+ WSET(server->packet, smb_vwv1, 0);
+ WSET(server->packet, smb_vwv2, inode->u.smbfs_i.fileid);
+ DSET(server->packet, smb_vwv3, (u32)offset); /* low 32 bits */
+ DSET(server->packet, smb_vwv5, 0);
+ WSET(server->packet, smb_vwv7, 0); /* write mode */
+ WSET(server->packet, smb_vwv8, 0);
+ WSET(server->packet, smb_vwv9, 0);
+ WSET(server->packet, smb_vwv10, count); /* data length */
+ WSET(server->packet, smb_vwv11, p + 2 - smb_base(server->packet));
+ DSET(server->packet, smb_vwv12, (u32)(offset >> 32));
+ *p++ = 0; /* FIXME: pad to short or long ... change +2 above also */
+ *p++ = 0;
+ memcpy(p, data, count);
+
+ result = smb_request_ok(server, SMBwriteX, 6, 0);
+ if (result >= 0)
+ result = WVAL(server->packet, smb_vwv2);
+
+ smb_unlock_server(server);
+ return result;
+}
+
+int
+smb_proc_create(struct dentry *dentry, __u16 attr, time_t ctime, __u16 *fileid)
+{
+ struct smb_sb_info *server = server_from_dentry(dentry);
+ char *p;
+ int result;
+
+ smb_lock_server(server);
+
+ retry:
+ p = smb_setup_header(server, SMBcreate, 3, 0);
+ WSET(server->packet, smb_vwv0, attr);
+ DSET(server->packet, smb_vwv1, utc2local(server, ctime));
+ result = smb_simple_encode_path(server, &p, dentry, NULL);
+ if (result < 0)
+ goto out;
+ smb_setup_bcc(server, p);
+
+ result = smb_request_ok(server, SMBcreate, 1, 0);
+ if (result < 0) {
+ if (smb_retry(server))
+ goto retry;
+ goto out;
+ }
+ *fileid = WVAL(server->packet, smb_vwv0);
+ result = 0;
+
+out:
+ smb_unlock_server(server);
+ return result;
+}
+
+int
+smb_proc_mv(struct dentry *old_dentry, struct dentry *new_dentry)
+{
+ struct smb_sb_info *server = server_from_dentry(old_dentry);
+ char *p;
+ int result;
+
+ smb_lock_server(server);
+
+ retry:
+ p = smb_setup_header(server, SMBmv, 1, 0);
+ WSET(server->packet, smb_vwv0, aSYSTEM | aHIDDEN | aDIR);
+ result = smb_simple_encode_path(server, &p, old_dentry, NULL);
+ if (result < 0)
+ goto out;
+ result = smb_simple_encode_path(server, &p, new_dentry, NULL);
+ if (result < 0)
+ goto out;
+ smb_setup_bcc(server, p);
+
+ if ((result = smb_request_ok(server, SMBmv, 0, 0)) < 0) {
+ if (smb_retry(server))
+ goto retry;
+ goto out;
+ }
+ result = 0;
+out:
+ smb_unlock_server(server);
+ return result;
+}
+
+/*
+ * Code common to mkdir and rmdir.
+ */
+static int
+smb_proc_generic_command(struct dentry *dentry, __u8 command)
+{
+ struct smb_sb_info *server = server_from_dentry(dentry);
+ char *p;
+ int result;
+
+ smb_lock_server(server);
+
+ retry:
+ p = smb_setup_header(server, command, 0, 0);
+ result = smb_simple_encode_path(server, &p, dentry, NULL);
+ if (result < 0)
+ goto out;
+ smb_setup_bcc(server, p);
+
+ result = smb_request_ok(server, command, 0, 0);
+ if (result < 0) {
+ if (smb_retry(server))
+ goto retry;
+ goto out;
+ }
+ result = 0;
+out:
+ smb_unlock_server(server);
+ return result;
+}
+
+int
+smb_proc_mkdir(struct dentry *dentry)
+{
+ return smb_proc_generic_command(dentry, SMBmkdir);
+}
+
+int
+smb_proc_rmdir(struct dentry *dentry)
+{
+ return smb_proc_generic_command(dentry, SMBrmdir);
+}
+
+#if SMBFS_POSIX_UNLINK
+/*
+ * Removes readonly attribute from a file. Used by unlink to give posix
+ * semantics.
+ * Note: called with the server locked.
+ */
+static int
+smb_set_rw(struct dentry *dentry,struct smb_sb_info *server)
+{
+ int result;
+ struct smb_fattr fattr;
+
+ /* first get current attribute */
+ smb_init_dirent(server, &fattr);
+ result = server->ops->getattr(server, dentry, &fattr);
+ smb_finish_dirent(server, &fattr);
+ if (result < 0)
+ return result;
+
+ /* if RONLY attribute is set, remove it */
+ if (fattr.attr & aRONLY) { /* read only attribute is set */
+ fattr.attr &= ~aRONLY;
+ result = smb_proc_setattr_core(server, dentry, fattr.attr);
+ }
+ return result;
+}
+#endif
+
+int
+smb_proc_unlink(struct dentry *dentry)
+{
+ struct smb_sb_info *server = server_from_dentry(dentry);
+ int flag = 0;
+ char *p;
+ int result;
+
+ smb_lock_server(server);
+
+ retry:
+ p = smb_setup_header(server, SMBunlink, 1, 0);
+ WSET(server->packet, smb_vwv0, aSYSTEM | aHIDDEN);
+ result = smb_simple_encode_path(server, &p, dentry, NULL);
+ if (result < 0)
+ goto out;
+ smb_setup_bcc(server, p);
+
+ if ((result = smb_request_ok(server, SMBunlink, 0, 0)) < 0) {
+#if SMBFS_POSIX_UNLINK
+ if (result == -EACCES && !flag) {
+ /* Posix semantics is for the read-only state
+ of a file to be ignored in unlink(). In the
+ SMB world a unlink() is refused on a
+ read-only file. To make things easier for
+ unix users we try to override the files
+ permission if the unlink fails with the
+ right error.
+ This introduces a race condition that could
+ lead to a file being written by someone who
+ shouldn't have access, but as far as I can
+ tell that is unavoidable */
+
+ /* remove RONLY attribute and try again */
+ result = smb_set_rw(dentry,server);
+ if (result == 0) {
+ flag = 1;
+ goto retry;
+ }
+ }
+#endif
+ if (smb_retry(server))
+ goto retry;
+ goto out;
+ }
+ result = 0;
+out:
+ smb_unlock_server(server);
+ return result;
+}
+
+/*
+ * Called with the server locked
+ */
+int
+smb_proc_flush(struct smb_sb_info *server, __u16 fileid)
+{
+ smb_setup_header(server, SMBflush, 1, 0);
+ WSET(server->packet, smb_vwv0, fileid);
+ return smb_request_ok(server, SMBflush, 0, 0);
+}
+
+static int
+smb_proc_trunc32(struct inode *inode, loff_t length)
+{
+ /*
+ * Writing 0bytes is old-SMB magic for truncating files.
+ * MAX_NON_LFS should prevent this from being called with a too
+ * large offset.
+ */
+ return smb_proc_write(inode, length, 0, NULL);
+}
+
+static int
+smb_proc_trunc64(struct inode *inode, loff_t length)
+{
+ struct smb_sb_info *server = server_from_inode(inode);
+ int command;
+ int result;
+ char *param = server->temp_buf;
+ char data[8];
+ unsigned char *resp_data = NULL;
+ unsigned char *resp_param = NULL;
+ int resp_data_len = 0;
+ int resp_param_len = 0;
+
+ smb_lock_server(server);
+ command = TRANSACT2_SETFILEINFO;
+
+ /* FIXME: must we also set allocation size? winNT seems to do that */
+ retry:
+ WSET(param, 0, inode->u.smbfs_i.fileid);
+ WSET(param, 2, SMB_SET_FILE_END_OF_FILE_INFO);
+ WSET(param, 4, 0);
+ LSET(data, 0, length);
+ result = smb_trans2_request(server, command,
+ 8, data, 6, param,
+ &resp_data_len, &resp_data,
+ &resp_param_len, &resp_param);
+ if (result < 0) {
+ if (smb_retry(server))
+ goto retry;
+ goto out;
+ }
+ result = 0;
+ if (server->rcls != 0)
+ result = smb_errno(server);
+
+out:
+ smb_unlock_server(server);
+ return result;
+}
+
+static int
+smb_proc_trunc95(struct inode *inode, loff_t length)
+{
+ struct smb_sb_info *server = server_from_inode(inode);
+ int result = smb_proc_trunc32(inode, length);
+
+ /*
+ * win9x doesn't appear to update the size immediately.
+ * It will return the old file size after the truncate,
+ * confusing smbfs. So we force an update.
+ *
+ * FIXME: is this still necessary?
+ */
+ smb_lock_server(server);
+ smb_proc_flush(server, inode->u.smbfs_i.fileid);
+ smb_unlock_server(server);
+ return result;
+}
+
+static void
+smb_init_dirent(struct smb_sb_info *server, struct smb_fattr *fattr)
+{
+ memset(fattr, 0, sizeof(*fattr));
+
+ fattr->f_nlink = 1;
+ fattr->f_uid = server->mnt->uid;
+ fattr->f_gid = server->mnt->gid;
+ fattr->f_blksize = SMB_ST_BLKSIZE;
+ fattr->f_unix = 0;
+}
+
+static void
+smb_finish_dirent(struct smb_sb_info *server, struct smb_fattr *fattr)
+{
+ if (fattr->f_unix)
+ return;
+
+ fattr->f_mode = server->mnt->file_mode;
+ if (fattr->attr & aDIR) {
+ fattr->f_mode = server->mnt->dir_mode;
+ fattr->f_size = SMB_ST_BLKSIZE;
+ }
+ /* Check the read-only flag */
+ if (fattr->attr & aRONLY)
+ fattr->f_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
+
+ /* How many 512 byte blocks do we need for this file? */
+ fattr->f_blocks = 0;
+ if (fattr->f_size != 0)
+ fattr->f_blocks = 1 + ((fattr->f_size-1) >> 9);
+ return;
+}
+
+void
+smb_init_root_dirent(struct smb_sb_info *server, struct smb_fattr *fattr)
+{
+ smb_init_dirent(server, fattr);
+ fattr->attr = aDIR;
+ fattr->f_ino = 2; /* traditional root inode number */
+ fattr->f_mtime = CURRENT_TIME;
+ smb_finish_dirent(server, fattr);
+}
+
+/*
+ * Decode a dirent for old protocols
+ *
+ * qname is filled with the decoded, and possibly translated, name.
+ * fattr receives decoded attributes
+ *
+ * Bugs Noted:
+ * (1) Pathworks servers may pad the name with extra spaces.
+ */
+static char *
+smb_decode_short_dirent(struct smb_sb_info *server, char *p,
+ struct qstr *qname, struct smb_fattr *fattr)
+{
+ int len;
+
+ /*
+ * SMB doesn't have a concept of inode numbers ...
+ */
+ smb_init_dirent(server, fattr);
+ fattr->f_ino = 0; /* FIXME: do we need this? */
+
+ p += SMB_STATUS_SIZE; /* reserved (search_status) */
+ fattr->attr = *p;
+ fattr->f_mtime = date_dos2unix(server, WVAL(p, 3), WVAL(p, 1));
+ fattr->f_size = DVAL(p, 5);
+ fattr->f_ctime = fattr->f_mtime;
+ fattr->f_atime = fattr->f_mtime;
+ qname->name = p + 9;
+ len = strnlen(qname->name, 12);
+
+ /*
+ * Trim trailing blanks for Pathworks servers
+ */
+ while (len > 2 && qname->name[len-1] == ' ')
+ len--;
+ qname->len = len;
+
+ smb_finish_dirent(server, fattr);
+
+#if 0
+ /* FIXME: These only work for ascii chars, and recent smbmount doesn't
+ allow the flag to be set anyway. It kills const. Remove? */
+ switch (server->opt.case_handling) {
+ case SMB_CASE_UPPER:
+ str_upper(entry->name, len);
+ break;
+ case SMB_CASE_LOWER:
+ str_lower(entry->name, len);
+ break;
+ default:
+ break;
+ }
+#endif
+
+ qname->len = 0;
+ len = server->ops->convert(server->name_buf, SMB_MAXNAMELEN,
+ qname->name, len,
+ server->remote_nls, server->local_nls);
+ if (len > 0) {
+ qname->len = len;
+ qname->name = server->name_buf;
+ DEBUG1("len=%d, name=%.*s\n",qname->len,qname->len,qname->name);
+ }
+
+ return p + 22;
+}
+
+/*
+ * This routine is used to read in directory entries from the network.
+ * Note that it is for short directory name seeks, i.e.: protocol <
+ * SMB_PROTOCOL_LANMAN2
+ */
+static int
+smb_proc_readdir_short(struct file *filp, void *dirent, filldir_t filldir,
+ struct smb_cache_control *ctl)
+{
+ struct dentry *dir = filp->f_dentry;
+ struct smb_sb_info *server = server_from_dentry(dir);
+ struct qstr qname;
+ struct smb_fattr fattr;
+ char *p;
+ int result;
+ int i, first, entries_seen, entries;
+ int entries_asked = (server->opt.max_xmit - 100) / SMB_DIRINFO_SIZE;
+ __u16 bcc;
+ __u16 count;
+ char status[SMB_STATUS_SIZE];
+ static struct qstr mask = { "*.*", 3, 0 };
+ unsigned char *last_status;
+
+ VERBOSE("%s/%s\n", DENTRY_PATH(dir));
+
+ smb_lock_server(server);
+
+ first = 1;
+ entries = 0;
+ entries_seen = 2; /* implicit . and .. */
+
+ while (1) {
+ p = smb_setup_header(server, SMBsearch, 2, 0);
+ WSET(server->packet, smb_vwv0, entries_asked);
+ WSET(server->packet, smb_vwv1, aDIR);
+ if (first == 1) {
+ result = smb_simple_encode_path(server, &p, dir, &mask);
+ if (result < 0)
+ goto unlock_return;
+ if (p + 3 > (char*)server->packet+server->packet_size) {
+ result = -ENAMETOOLONG;
+ goto unlock_return;
+ }
+ *p++ = 5;
+ WSET(p, 0, 0);
+ p += 2;
+ first = 0;
+ } else {
+ if (p + 5 + SMB_STATUS_SIZE >
+ (char*)server->packet + server->packet_size) {
+ result = -ENAMETOOLONG;
+ goto unlock_return;
+ }
+
+ *p++ = 4;
+ *p++ = 0;
+ *p++ = 5;
+ WSET(p, 0, SMB_STATUS_SIZE);
+ p += 2;
+ memcpy(p, status, SMB_STATUS_SIZE);
+ p += SMB_STATUS_SIZE;
+ }
+
+ smb_setup_bcc(server, p);
+
+ result = smb_request_ok(server, SMBsearch, 1, -1);
+ if (result < 0) {
+ if ((server->rcls == ERRDOS) &&
+ (server->err == ERRnofiles))
+ break;
+ if (smb_retry(server)) {
+ ctl->idx = -1; /* retry */
+ result = 0;
+ }
+ goto unlock_return;
+ }
+ p = SMB_VWV(server->packet);
+ count = WVAL(p, 0);
+ if (count <= 0)
+ break;
+
+ result = -EIO;
+ bcc = WVAL(p, 2);
+ if (bcc != count * SMB_DIRINFO_SIZE + 3)
+ goto unlock_return;
+ p += 7;
+
+
+ /* Make sure the response fits in the buffer. Fixed sized
+ entries means we don't have to check in the decode loop. */
+
+ last_status = SMB_BUF(server->packet) + 3 + (count - 1) *
+ SMB_DIRINFO_SIZE;
+
+ if (last_status + SMB_DIRINFO_SIZE >=
+ server->packet + server->packet_size) {
+ printk(KERN_ERR "smb_proc_readdir_short: "
+ "last dir entry outside buffer! "
+ "%d@%p %d@%p\n", SMB_DIRINFO_SIZE, last_status,
+ server->packet_size, server->packet);
+ goto unlock_return;
+ }
+
+ /* Read the last entry into the status field. */
+ memcpy(status, last_status, SMB_STATUS_SIZE);
+
+
+ /* Now we are ready to parse smb directory entries. */
+
+ for (i = 0; i < count; i++) {
+ p = smb_decode_short_dirent(server, p,
+ &qname, &fattr);
+ if (qname.len == 0)
+ continue;
+ if (entries_seen == 2 && qname.name[0] == '.') {
+ if (qname.len == 1)
+ continue;
+ if (qname.name[1] == '.' && qname.len == 2)
+ continue;
+ }
+ if (!smb_fill_cache(filp, dirent, filldir, ctl,
+ &qname, &fattr))
+ ; /* stop reading? */
+ entries_seen++;
+ }
+ }
+ result = entries;
+
+unlock_return:
+ smb_unlock_server(server);
+ return result;
+}
+
+void smb_decode_unix_basic(struct smb_fattr *fattr, char *p)
+{
+ /* FIXME: verify nls support. all is sent as utf8? */
+
+ fattr->f_unix = 1;
+ fattr->f_mode = 0;
+
+ /* FIXME: use the uniqueID from the remote instead? */
+ /* 0 L file size in bytes */
+ /* 8 L file size on disk in bytes (block count) */
+ /* 40 L uid */
+ /* 48 L gid */
+ /* 56 W file type */
+ /* 60 L devmajor */
+ /* 68 L devminor */
+ /* 76 L unique ID (inode) */
+ /* 84 L permissions */
+ /* 92 L link count */
+
+ fattr->f_size = LVAL(p, 0);
+ fattr->f_blocks = LVAL(p, 8);
+ fattr->f_ctime = smb_ntutc2unixutc(LVAL(p, 16));
+ fattr->f_atime = smb_ntutc2unixutc(LVAL(p, 24));
+ fattr->f_mtime = smb_ntutc2unixutc(LVAL(p, 32));
+ fattr->f_uid = LVAL(p, 40);
+ fattr->f_gid = LVAL(p, 48);
+ fattr->f_mode |= smb_filetype_to_mode(WVAL(p, 56));
+
+ if (S_ISBLK(fattr->f_mode) || S_ISCHR(fattr->f_mode)) {
+ __u64 major = LVAL(p, 60);
+ __u64 minor = LVAL(p, 68);
+
+ fattr->f_rdev = MKDEV(major & 0xffffffff, minor & 0xffffffff);
+ }
+ fattr->f_mode |= LVAL(p, 84);
+}
+
+/*
+ * Interpret a long filename structure using the specified info level:
+ * level 1 for anything below NT1 protocol
+ * level 260 for NT1 protocol
+ *
+ * qname is filled with the decoded, and possibly translated, name
+ * fattr receives decoded attributes.
+ *
+ * Bugs Noted:
+ * (1) Win NT 4.0 appends a null byte to names and counts it in the length!
+ */
+static char *
+smb_decode_long_dirent(struct smb_sb_info *server, char *p, int level,
+ struct qstr *qname, struct smb_fattr *fattr)
+{
+ char *result;
+ unsigned int len = 0;
+ int n;
+ __u16 date, time;
+
+ /*
+ * SMB doesn't have a concept of inode numbers ...
+ */
+ smb_init_dirent(server, fattr);
+ fattr->f_ino = 0; /* FIXME: do we need this? */
+
+ switch (level) {
+ case 1:
+ len = *((unsigned char *) p + 22);
+ qname->name = p + 23;
+ result = p + 24 + len;
+
+ date = WVAL(p, 0);
+ time = WVAL(p, 2);
+ fattr->f_ctime = date_dos2unix(server, date, time);
+
+ date = WVAL(p, 4);
+ time = WVAL(p, 6);
+ fattr->f_atime = date_dos2unix(server, date, time);
+
+ date = WVAL(p, 8);
+ time = WVAL(p, 10);
+ fattr->f_mtime = date_dos2unix(server, date, time);
+ fattr->f_size = DVAL(p, 12);
+ /* ULONG allocation size */
+ fattr->attr = WVAL(p, 20);
+
+ VERBOSE("info 1 at %p, len=%d, name=%.*s\n",
+ p, len, len, qname->name);
+ break;
+ case 260:
+ result = p + WVAL(p, 0);
+ len = DVAL(p, 60);
+ if (len > 255) len = 255;
+ /* NT4 null terminates */
+ qname->name = p + 94;
+ if (len && qname->name[len-1] == '\0')
+ len--;
+
+ fattr->f_ctime = smb_ntutc2unixutc(LVAL(p, 8));
+ fattr->f_atime = smb_ntutc2unixutc(LVAL(p, 16));
+ fattr->f_mtime = smb_ntutc2unixutc(LVAL(p, 24));
+ /* change time (32) */
+ fattr->f_size = LVAL(p, 40);
+ /* alloc size (48) */
+ fattr->attr = DVAL(p, 56);
+
+ VERBOSE("info 260 at %p, len=%d, name=%.*s\n",
+ p, len, len, qname->name);
+ break;
+ case SMB_FIND_FILE_UNIX:
+ result = p + WVAL(p, 0);
+ qname->name = p + 108;
+
+ len = strlen(qname->name);
+ /* FIXME: should we check the length?? */
+
+ p += 8;
+ smb_decode_unix_basic(fattr, p);
+ VERBOSE("info SMB_FIND_FILE_UNIX at %p, len=%d, name=%.*s\n",
+ p, len, len, qname->name);
+ break;
+ default:
+ PARANOIA("Unknown info level %d\n", level);
+ result = p + WVAL(p, 0);
+ goto out;
+ }
+
+ smb_finish_dirent(server, fattr);
+
+#if 0
+ /* FIXME: These only work for ascii chars, and recent smbmount doesn't
+ allow the flag to be set anyway. Remove? */
+ switch (server->opt.case_handling) {
+ case SMB_CASE_UPPER:
+ str_upper(qname->name, len);
+ break;
+ case SMB_CASE_LOWER:
+ str_lower(qname->name, len);
+ break;
+ default:
+ break;
+ }
+#endif
+
+ qname->len = 0;
+ n = server->ops->convert(server->name_buf, SMB_MAXNAMELEN,
+ qname->name, len,
+ server->remote_nls, server->local_nls);
+ if (n > 0) {
+ qname->len = n;
+ qname->name = server->name_buf;
+ }
+
+out:
+ return result;
+}
+
+/* findfirst/findnext flags */
+#define SMB_CLOSE_AFTER_FIRST (1<<0)
+#define SMB_CLOSE_IF_END (1<<1)
+#define SMB_REQUIRE_RESUME_KEY (1<<2)
+#define SMB_CONTINUE_BIT (1<<3)
+
+/*
+ * Note: samba-2.0.7 (at least) has a very similar routine, cli_list, in
+ * source/libsmb/clilist.c. When looking for smb bugs in the readdir code,
+ * go there for advise.
+ *
+ * Bugs Noted:
+ * (1) When using Info Level 1 Win NT 4.0 truncates directory listings
+ * for certain patterns of names and/or lengths. The breakage pattern
+ * is completely reproducible and can be toggled by the creation of a
+ * single file. (E.g. echo hi >foo breaks, rm -f foo works.)
+ */
+static int
+smb_proc_readdir_long(struct file *filp, void *dirent, filldir_t filldir,
+ struct smb_cache_control *ctl)
+{
+ struct dentry *dir = filp->f_dentry;
+ struct smb_sb_info *server = server_from_dentry(dir);
+ struct qstr qname;
+ struct smb_fattr fattr;
+
+ unsigned char *p, *lastname;
+ char *mask, *param = server->temp_buf;
+ __u16 command;
+ int first, entries_seen;
+
+ /* Both NT and OS/2 accept info level 1 (but see note below). */
+ int info_level = 260;
+ const int max_matches = 512;
+
+ unsigned char *resp_data = NULL;
+ unsigned char *resp_param = NULL;
+ int resp_data_len = 0;
+ int resp_param_len = 0;
+ int ff_searchcount = 0;
+ int ff_eos = 0;
+ int ff_lastname = 0;
+ int ff_dir_handle = 0;
+ int loop_count = 0;
+ int mask_len, i, result;
+ static struct qstr star = { "*", 1, 0 };
+
+ /*
+ * use info level 1 for older servers that don't do 260
+ */
+ if (server->opt.capabilities & SMB_CAP_UNIX)
+ info_level = SMB_FIND_FILE_UNIX;
+ else if (server->opt.protocol < SMB_PROTOCOL_NT1)
+ info_level = 1;
+
+ smb_lock_server(server);
+
+ /*
+ * Encode the initial path
+ */
+ mask = param + 12;
+
+ mask_len = smb_encode_path(server, mask, SMB_MAXPATHLEN+1, dir, &star);
+ if (mask_len < 0) {
+ result = mask_len;
+ goto unlock_return;
+ }
+ mask_len--; /* mask_len is strlen, not #bytes */
+ first = 1;
+ VERBOSE("starting mask_len=%d, mask=%s\n", mask_len, mask);
+
+ result = 0;
+ entries_seen = 2;
+ ff_eos = 0;
+
+ while (ff_eos == 0) {
+ loop_count += 1;
+ if (loop_count > 10) {
+ printk(KERN_WARNING "smb_proc_readdir_long: "
+ "Looping in FIND_NEXT??\n");
+ result = -EIO;
+ break;
+ }
+
+ if (first != 0) {
+ command = TRANSACT2_FINDFIRST;
+ WSET(param, 0, aSYSTEM | aHIDDEN | aDIR);
+ WSET(param, 2, max_matches); /* max count */
+ WSET(param, 4, SMB_CLOSE_IF_END);
+ WSET(param, 6, info_level);
+ DSET(param, 8, 0);
+ } else {
+ command = TRANSACT2_FINDNEXT;
+
+ VERBOSE("handle=0x%X, lastname=%d, mask=%s\n",
+ ff_dir_handle, ff_lastname, mask);
+
+ WSET(param, 0, ff_dir_handle); /* search handle */
+ WSET(param, 2, max_matches); /* max count */
+ WSET(param, 4, info_level);
+ DSET(param, 6, 0);
+ WSET(param, 10, SMB_CONTINUE_BIT|SMB_CLOSE_IF_END);
+ }
+
+ result = smb_trans2_request(server, command,
+ 0, NULL, 12 + mask_len + 1, param,
+ &resp_data_len, &resp_data,
+ &resp_param_len, &resp_param);
+
+ if (result < 0) {
+ if (smb_retry(server)) {
+ PARANOIA("error=%d, retrying\n", result);
+ ctl->idx = -1; /* retry */
+ result = 0;
+ goto unlock_return;
+ }
+ PARANOIA("error=%d, breaking\n", result);
+ break;
+ }
+
+ if (server->rcls == ERRSRV && server->err == ERRerror) {
+ /* a damn Win95 bug - sometimes it clags if you
+ ask it too fast */
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(HZ/5);
+ continue;
+ }
+
+ if (server->rcls != 0) {
+ result = smb_errno(server);
+ PARANOIA("name=%s, result=%d, rcls=%d, err=%d\n",
+ mask, result, server->rcls, server->err);
+ break;
+ }
+
+ /* parse out some important return info */
+ if (first != 0) {
+ ff_dir_handle = WVAL(resp_param, 0);
+ ff_searchcount = WVAL(resp_param, 2);
+ ff_eos = WVAL(resp_param, 4);
+ ff_lastname = WVAL(resp_param, 8);
+ } else {
+ ff_searchcount = WVAL(resp_param, 0);
+ ff_eos = WVAL(resp_param, 2);
+ ff_lastname = WVAL(resp_param, 6);
+ }
+
+ if (ff_searchcount == 0)
+ break;
+
+ /*
+ * We might need the lastname for continuations.
+ *
+ * Note that some servers (win95?) point to the filename and
+ * others (NT4, Samba using NT1) to the dir entry. We assume
+ * here that those who do not point to a filename do not need
+ * this info to continue the listing.
+ *
+ * OS/2 needs this and talks infolevel 1
+ * NetApps want lastname with infolevel 260
+ * win2k want lastname with infolevel 260, and points to
+ * the record not to the name.
+ * Samba+CifsUnixExt doesn't need lastname.
+ *
+ * All are happy if we return the data they point to. So we do.
+ */
+ mask_len = 0;
+ if (info_level != SMB_FIND_FILE_UNIX &&
+ ff_lastname > 0 && ff_lastname < resp_data_len) {
+ lastname = resp_data + ff_lastname;
+
+ switch (info_level) {
+ case 260:
+ mask_len = resp_data_len - ff_lastname;
+ break;
+ case 1:
+ /* lastname points to a length byte */
+ mask_len = *lastname++;
+ if (ff_lastname + 1 + mask_len > resp_data_len)
+ mask_len = resp_data_len - ff_lastname - 1;
+ break;
+ }
+
+ /*
+ * Update the mask string for the next message.
+ */
+ if (mask_len < 0)
+ mask_len = 0;
+ if (mask_len > 255)
+ mask_len = 255;
+ if (mask_len)
+ strncpy(mask, lastname, mask_len);
+ }
+ mask_len = strnlen(mask, mask_len);
+ VERBOSE("new mask, len=%d@%d of %d, mask=%.*s\n",
+ mask_len, ff_lastname, resp_data_len, mask_len, mask);
+
+ /* Now we are ready to parse smb directory entries. */
+
+ /* point to the data bytes */
+ p = resp_data;
+ for (i = 0; i < ff_searchcount; i++) {
+ /* make sure we stay within the buffer */
+ if (p >= resp_data + resp_data_len) {
+ printk(KERN_ERR "smb_proc_readdir_long: "
+ "dirent pointer outside buffer! "
+ "%p %d@%p %d@%p\n",
+ p, resp_data_len, resp_data,
+ server->packet_size, server->packet);
+ result = -EIO; /* always a comm. error? */
+ goto unlock_return;
+ }
+
+ p = smb_decode_long_dirent(server, p, info_level,
+ &qname, &fattr);
+ if (qname.len == 0)
+ continue;
+
+ /* ignore . and .. from the server */
+ if (entries_seen == 2 && qname.name[0] == '.') {
+ if (qname.len == 1)
+ continue;
+ if (qname.name[1] == '.' && qname.len == 2)
+ continue;
+ }
+
+ if (!smb_fill_cache(filp, dirent, filldir, ctl,
+ &qname, &fattr))
+ ; /* stop reading? */
+ entries_seen++;
+ }
+
+ VERBOSE("received %d entries, eos=%d\n", ff_searchcount,ff_eos);
+
+ first = 0;
+ loop_count = 0;
+ }
+
+unlock_return:
+ smb_unlock_server(server);
+ return result;
+}
+
+/*
+ * This version uses the trans2 TRANSACT2_FINDFIRST message
+ * to get the attribute data.
+ * Note: called with the server locked.
+ *
+ * Bugs Noted:
+ */
+static int
+smb_proc_getattr_ff(struct smb_sb_info *server, struct dentry *dentry,
+ struct smb_fattr *fattr)
+{
+ char *param = server->temp_buf, *mask = param + 12;
+ __u16 date, time;
+ unsigned char *resp_data = NULL;
+ unsigned char *resp_param = NULL;
+ int resp_data_len = 0;
+ int resp_param_len = 0;
+ int mask_len, result;
+
+retry:
+ mask_len = smb_encode_path(server, mask, SMB_MAXPATHLEN+1, dentry, NULL);
+ if (mask_len < 0) {
+ result = mask_len;
+ goto out;
+ }
+ VERBOSE("name=%s, len=%d\n", mask, mask_len);
+ WSET(param, 0, aSYSTEM | aHIDDEN | aDIR);
+ WSET(param, 2, 1); /* max count */
+ WSET(param, 4, 1); /* close after this call */
+ WSET(param, 6, 1); /* info_level */
+ DSET(param, 8, 0);
+
+ result = smb_trans2_request(server, TRANSACT2_FINDFIRST,
+ 0, NULL, 12 + mask_len, param,
+ &resp_data_len, &resp_data,
+ &resp_param_len, &resp_param);
+ if (result < 0)
+ {
+ if (smb_retry(server))
+ goto retry;
+ goto out;
+ }
+ if (server->rcls != 0)
+ {
+ result = smb_errno(server);
+#ifdef SMBFS_PARANOIA
+ if (result != -ENOENT)
+ PARANOIA("error for %s, rcls=%d, err=%d\n",
+ mask, server->rcls, server->err);
+#endif
+ goto out;
+ }
+ /* Make sure we got enough data ... */
+ result = -EINVAL;
+ if (resp_data_len < 22 || WVAL(resp_param, 2) != 1)
+ {
+ PARANOIA("bad result for %s, len=%d, count=%d\n",
+ mask, resp_data_len, WVAL(resp_param, 2));
+ goto out;
+ }
+
+ /*
+ * Decode the response into the fattr ...
+ */
+ date = WVAL(resp_data, 0);
+ time = WVAL(resp_data, 2);
+ fattr->f_ctime = date_dos2unix(server, date, time);
+
+ date = WVAL(resp_data, 4);
+ time = WVAL(resp_data, 6);
+ fattr->f_atime = date_dos2unix(server, date, time);
+
+ date = WVAL(resp_data, 8);
+ time = WVAL(resp_data, 10);
+ fattr->f_mtime = date_dos2unix(server, date, time);
+ VERBOSE("name=%s, date=%x, time=%x, mtime=%ld\n",
+ mask, date, time, fattr->f_mtime);
+ fattr->f_size = DVAL(resp_data, 12);
+ /* ULONG allocation size */
+ fattr->attr = WVAL(resp_data, 20);
+ result = 0;
+
+out:
+ return result;
+}
+
+/*
+ * Note: called with the server locked.
+ */
+static int
+smb_proc_getattr_core(struct smb_sb_info *server, struct dentry *dir,
+ struct smb_fattr *fattr)
+{
+ int result;
+ char *p;
+
+ retry:
+ p = smb_setup_header(server, SMBgetatr, 0, 0);
+ result = smb_simple_encode_path(server, &p, dir, NULL);
+ if (result < 0)
+ goto out;
+ smb_setup_bcc(server, p);
+
+ if ((result = smb_request_ok(server, SMBgetatr, 10, 0)) < 0)
+ {
+ if (smb_retry(server))
+ goto retry;
+ goto out;
+ }
+ fattr->attr = WVAL(server->packet, smb_vwv0);
+ fattr->f_mtime = local2utc(server, DVAL(server->packet, smb_vwv1));
+ fattr->f_size = DVAL(server->packet, smb_vwv3);
+ fattr->f_ctime = fattr->f_mtime;
+ fattr->f_atime = fattr->f_mtime;
+#ifdef SMBFS_DEBUG_TIMESTAMP
+ printk("getattr_core: %s/%s, mtime=%ld\n",
+ DENTRY_PATH(dir), fattr->f_mtime);
+#endif
+ result = 0;
+
+out:
+ return result;
+}
+
+/*
+ * Note: called with the server locked.
+ */
+static int
+smb_proc_getattr_trans2_all(struct smb_sb_info *server, struct dentry *dir,
+ struct smb_fattr *attr)
+{
+ char *p, *param = server->temp_buf;
+ unsigned char *resp_data = NULL;
+ unsigned char *resp_param = NULL;
+ int resp_data_len = 0;
+ int resp_param_len = 0;
+ int result;
+
+ retry:
+ WSET(param, 0, SMB_QUERY_FILE_ALL_INFO);
+ DSET(param, 2, 0);
+ result = smb_encode_path(server, param+6, SMB_MAXPATHLEN+1, dir, NULL);
+ if (result < 0)
+ goto out;
+ p = param + 6 + result;
+
+ result = smb_trans2_request(server, TRANSACT2_QPATHINFO,
+ 0, NULL, p - param, param,
+ &resp_data_len, &resp_data,
+ &resp_param_len, &resp_param);
+ if (result < 0) {
+ if (smb_retry(server))
+ goto retry;
+ goto out;
+ }
+ if (server->rcls != 0) {
+ VERBOSE("for %s: result=%d, rcls=%d, err=%d\n",
+ &param[6], result, server->rcls, server->err);
+ result = smb_errno(server);
+ goto out;
+ }
+
+ result = -ENOENT;
+ if (resp_data_len < 56) {
+ PARANOIA("not enough data for %s, len=%d\n",
+ &param[6], resp_data_len);
+ goto out;
+ }
+
+ attr->f_ctime = smb_ntutc2unixutc(LVAL(resp_data, 0));
+ attr->f_atime = smb_ntutc2unixutc(LVAL(resp_data, 8));
+ attr->f_mtime = smb_ntutc2unixutc(LVAL(resp_data, 16));
+ /* change (24) */
+ attr->attr = WVAL(resp_data, 32);
+ /* pad? (34) */
+ /* allocated size (40) */
+ attr->f_size = LVAL(resp_data, 48);
+ result = 0;
+
+out:
+ return result;
+}
+
+/*
+ * Note: called with the server locked.
+ *
+ * Bugs Noted:
+ * (1) Win 95 swaps the date and time fields in the standard info level.
+ */
+static int
+smb_proc_getattr_trans2_std(struct smb_sb_info *server, struct dentry *dir,
+ struct smb_fattr *attr)
+{
+ char *p, *param = server->temp_buf;
+ __u16 date, time;
+ int off_date = 0, off_time = 2;
+ unsigned char *resp_data = NULL;
+ unsigned char *resp_param = NULL;
+ int resp_data_len = 0;
+ int resp_param_len = 0;
+ int result;
+
+ retry:
+ WSET(param, 0, SMB_INFO_STANDARD);
+ DSET(param, 2, 0);
+ result = smb_encode_path(server, param+6, SMB_MAXPATHLEN+1, dir, NULL);
+ if (result < 0)
+ goto out;
+ p = param + 6 + result;
+
+ result = smb_trans2_request(server, TRANSACT2_QPATHINFO,
+ 0, NULL, p - param, param,
+ &resp_data_len, &resp_data,
+ &resp_param_len, &resp_param);
+ if (result < 0)
+ {
+ if (smb_retry(server))
+ goto retry;
+ goto out;
+ }
+ if (server->rcls != 0)
+ {
+ VERBOSE("for %s: result=%d, rcls=%d, err=%d\n",
+ &param[6], result, server->rcls, server->err);
+ result = smb_errno(server);
+ goto out;
+ }
+
+ result = -ENOENT;
+ if (resp_data_len < 22)
+ {
+ PARANOIA("not enough data for %s, len=%d\n",
+ &param[6], resp_data_len);
+ goto out;
+ }
+
+ /*
+ * Kludge alert: Win 95 swaps the date and time field,
+ * contrary to the CIFS docs and Win NT practice.
+ */
+ if (server->mnt->flags & SMB_MOUNT_WIN95) {
+ off_date = 2;
+ off_time = 0;
+ }
+ date = WVAL(resp_data, off_date);
+ time = WVAL(resp_data, off_time);
+ attr->f_ctime = date_dos2unix(server, date, time);
+
+ date = WVAL(resp_data, 4 + off_date);
+ time = WVAL(resp_data, 4 + off_time);
+ attr->f_atime = date_dos2unix(server, date, time);
+
+ date = WVAL(resp_data, 8 + off_date);
+ time = WVAL(resp_data, 8 + off_time);
+ attr->f_mtime = date_dos2unix(server, date, time);
+#ifdef SMBFS_DEBUG_TIMESTAMP
+ printk(KERN_DEBUG "getattr_trans2: %s/%s, date=%x, time=%x, mtime=%ld\n",
+ DENTRY_PATH(dir), date, time, attr->f_mtime);
+#endif
+ attr->f_size = DVAL(resp_data, 12);
+ attr->attr = WVAL(resp_data, 20);
+ result = 0;
+
+out:
+ return result;
+}
+
+static int
+smb_proc_getattr_95(struct smb_sb_info *server, struct dentry *dir,
+ struct smb_fattr *attr)
+{
+ struct inode *inode = dir->d_inode;
+ int result;
+
+retry:
+ result = smb_proc_getattr_trans2_std(server, dir, attr);
+ if (result < 0) {
+ if (server->rcls == ERRSRV && server->err == ERRerror) {
+ /* a damn Win95 bug - sometimes it clags if you
+ ask it too fast */
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(HZ/5);
+ goto retry;
+ }
+ goto out;
+ }
+
+ /*
+ * None of the other getattr versions here can make win9x
+ * return the right filesize if there are changes made to an
+ * open file. A seek-to-end does return the right size, but
+ * we only need to do that on files we have written.
+ */
+ if (inode && inode->u.smbfs_i.flags & SMB_F_LOCALWRITE &&
+ smb_is_open(inode))
+ {
+ __u16 fileid = inode->u.smbfs_i.fileid;
+ attr->f_size = smb_proc_seek(server, fileid, 2, 0);
+ }
+
+out:
+ return result;
+}
+
+/*
+ * Note: called with the server locked.
+ */
+static int
+smb_proc_getattr_unix(struct smb_sb_info *server, struct dentry *dir,
+ struct smb_fattr *attr)
+{
+ char *p, *param = server->temp_buf;
+ unsigned char *resp_data = NULL;
+ unsigned char *resp_param = NULL;
+ int resp_data_len = 0;
+ int resp_param_len = 0;
+ int result;
+
+ retry:
+ WSET(param, 0, SMB_QUERY_FILE_UNIX_BASIC);
+ DSET(param, 2, 0);
+ result = smb_encode_path(server, param+6, SMB_MAXPATHLEN+1, dir, NULL);
+ if (result < 0)
+ goto out;
+ p = param + 6 + result;
+
+ result = smb_trans2_request(server, TRANSACT2_QPATHINFO,
+ 0, NULL, p - param, param,
+ &resp_data_len, &resp_data,
+ &resp_param_len, &resp_param);
+ if (result < 0) {
+ if (smb_retry(server))
+ goto retry;
+ goto out;
+ }
+ if (server->rcls != 0) {
+ VERBOSE("for %s: result=%d, rcls=%d, err=%d\n",
+ &param[6], result, server->rcls, server->err);
+ result = smb_errno(server);
+ goto out;
+ }
+
+ smb_decode_unix_basic(attr, resp_data);
+ result = 0;
+
+out:
+ return result;
+}
+
+static int
+smb_proc_getattr_null(struct smb_sb_info *server, struct dentry *dir,
+ struct smb_fattr *attr)
+{
+ return -EIO;
+}
+
+int
+smb_proc_getattr(struct dentry *dir, struct smb_fattr *fattr)
+{
+ struct smb_sb_info *server = server_from_dentry(dir);
+ int result;
+
+ smb_lock_server(server);
+ smb_init_dirent(server, fattr);
+ result = server->ops->getattr(server, dir, fattr);
+ smb_finish_dirent(server, fattr);
+ smb_unlock_server(server);
+ return result;
+}
+
+
+/*
+ * Called with the server locked. Because of bugs in the
+ * core protocol, we use this only to set attributes. See
+ * smb_proc_settime() below for timestamp handling.
+ *
+ * Bugs Noted:
+ * (1) If mtime is non-zero, both Win 3.1 and Win 95 fail
+ * with an undocumented error (ERRDOS code 50). Setting
+ * mtime to 0 allows the attributes to be set.
+ * (2) The extra parameters following the name string aren't
+ * in the CIFS docs, but seem to be necessary for operation.
+ */
+static int
+smb_proc_setattr_core(struct smb_sb_info *server, struct dentry *dentry,
+ __u16 attr)
+{
+ char *p;
+ int result;
+
+ retry:
+ p = smb_setup_header(server, SMBsetatr, 8, 0);
+ WSET(server->packet, smb_vwv0, attr);
+ DSET(server->packet, smb_vwv1, 0); /* mtime */
+ WSET(server->packet, smb_vwv3, 0); /* reserved values */
+ WSET(server->packet, smb_vwv4, 0);
+ WSET(server->packet, smb_vwv5, 0);
+ WSET(server->packet, smb_vwv6, 0);
+ WSET(server->packet, smb_vwv7, 0);
+ result = smb_simple_encode_path(server, &p, dentry, NULL);
+ if (result < 0)
+ goto out;
+ if (p + 2 > (char *)server->packet + server->packet_size) {
+ result = -ENAMETOOLONG;
+ goto out;
+ }
+ *p++ = 4;
+ *p++ = 0;
+ smb_setup_bcc(server, p);
+
+ result = smb_request_ok(server, SMBsetatr, 0, 0);
+ if (result < 0) {
+ if (smb_retry(server))
+ goto retry;
+ goto out;
+ }
+ result = 0;
+out:
+ return result;
+}
+
+/*
+ * Because of bugs in the trans2 setattr messages, we must set
+ * attributes and timestamps separately. The core SMBsetatr
+ * message seems to be the only reliable way to set attributes.
+ */
+int
+smb_proc_setattr(struct dentry *dir, struct smb_fattr *fattr)
+{
+ struct smb_sb_info *server = server_from_dentry(dir);
+ int result;
+
+ VERBOSE("setting %s/%s, open=%d\n",
+ DENTRY_PATH(dir), smb_is_open(dir->d_inode));
+ smb_lock_server(server);
+ result = smb_proc_setattr_core(server, dir, fattr->attr);
+ smb_unlock_server(server);
+ return result;
+}
+
+/*
+ * Called with the server locked. Sets the timestamps for an
+ * file open with write permissions.
+ */
+static int
+smb_proc_setattr_ext(struct smb_sb_info *server,
+ struct inode *inode, struct smb_fattr *fattr)
+{
+ __u16 date, time;
+ int result;
+
+ retry:
+ smb_setup_header(server, SMBsetattrE, 7, 0);
+ WSET(server->packet, smb_vwv0, inode->u.smbfs_i.fileid);
+ /* We don't change the creation time */
+ WSET(server->packet, smb_vwv1, 0);
+ WSET(server->packet, smb_vwv2, 0);
+ date_unix2dos(server, fattr->f_atime, &date, &time);
+ WSET(server->packet, smb_vwv3, date);
+ WSET(server->packet, smb_vwv4, time);
+ date_unix2dos(server, fattr->f_mtime, &date, &time);
+ WSET(server->packet, smb_vwv5, date);
+ WSET(server->packet, smb_vwv6, time);
+#ifdef SMBFS_DEBUG_TIMESTAMP
+ printk(KERN_DEBUG "smb_proc_setattr_ext: date=%d, time=%d, mtime=%ld\n",
+ date, time, fattr->f_mtime);
+#endif
+
+ result = smb_request_ok(server, SMBsetattrE, 0, 0);
+ if (result < 0) {
+ if (smb_retry(server))
+ goto retry;
+ goto out;
+ }
+ result = 0;
+out:
+ return result;
+}
+
+/*
+ * Note: called with the server locked.
+ *
+ * Bugs Noted:
+ * (1) The TRANSACT2_SETPATHINFO message under Win NT 4.0 doesn't
+ * set the file's attribute flags.
+ */
+static int
+smb_proc_setattr_trans2(struct smb_sb_info *server,
+ struct dentry *dir, struct smb_fattr *fattr)
+{
+ __u16 date, time;
+ char *p, *param = server->temp_buf;
+ unsigned char *resp_data = NULL;
+ unsigned char *resp_param = NULL;
+ int resp_data_len = 0;
+ int resp_param_len = 0;
+ int result;
+ char data[26];
+
+ retry:
+ WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */
+ DSET(param, 2, 0);
+ result = smb_encode_path(server, param+6, SMB_MAXPATHLEN+1, dir, NULL);
+ if (result < 0)
+ goto out;
+ p = param + 6 + result;
+
+ WSET(data, 0, 0); /* creation time */
+ WSET(data, 2, 0);
+ date_unix2dos(server, fattr->f_atime, &date, &time);
+ WSET(data, 4, date);
+ WSET(data, 6, time);
+ date_unix2dos(server, fattr->f_mtime, &date, &time);
+ WSET(data, 8, date);
+ WSET(data, 10, time);
+#ifdef SMBFS_DEBUG_TIMESTAMP
+ printk(KERN_DEBUG "setattr_trans2: %s/%s, date=%x, time=%x, mtime=%ld\n",
+ DENTRY_PATH(dir), date, time, fattr->f_mtime);
+#endif
+ DSET(data, 12, 0); /* size */
+ DSET(data, 16, 0); /* blksize */
+ WSET(data, 20, 0); /* attr */
+ DSET(data, 22, 0); /* ULONG EA size */
+
+ result = smb_trans2_request(server, TRANSACT2_SETPATHINFO,
+ 26, data, p - param, param,
+ &resp_data_len, &resp_data,
+ &resp_param_len, &resp_param);
+ if (result < 0)
+ {
+ if (smb_retry(server))
+ goto retry;
+ goto out;
+ }
+ result = 0;
+ if (server->rcls != 0)
+ result = smb_errno(server);
+
+out:
+ return result;
+}
+
+/*
+ * ATTR_MODE 0x001
+ * ATTR_UID 0x002
+ * ATTR_GID 0x004
+ * ATTR_SIZE 0x008
+ * ATTR_ATIME 0x010
+ * ATTR_MTIME 0x020
+ * ATTR_CTIME 0x040
+ * ATTR_ATIME_SET 0x080
+ * ATTR_MTIME_SET 0x100
+ * ATTR_FORCE 0x200
+ * ATTR_ATTR_FLAG 0x400
+ *
+ * major/minor should only be set by mknod.
+ */
+int
+smb_proc_setattr_unix(struct dentry *dentry, struct iattr *attr,
+ unsigned int major, unsigned int minor)
+{
+ struct smb_sb_info *server = server_from_dentry(dentry);
+ u64 nttime;
+ char *p, *param;
+ unsigned char *resp_data = NULL;
+ unsigned char *resp_param = NULL;
+ int resp_data_len = 0;
+ int resp_param_len = 0;
+ int result;
+ char data[100];
+
+ smb_lock_server(server);
+ param = server->temp_buf;
+
+ DEBUG1("valid flags = 0x%04x\n", attr->ia_valid);
+
+retry:
+ WSET(param, 0, SMB_SET_FILE_UNIX_BASIC);
+ DSET(param, 2, 0);
+ result = smb_encode_path(server, param + 6, SMB_MAXPATHLEN+1, dentry, NULL);
+ if (result < 0)
+ goto out;
+ p = param + 6 + result;
+
+ /* 0 L file size in bytes */
+ /* 8 L file size on disk in bytes (block count) */
+ /* 40 L uid */
+ /* 48 L gid */
+ /* 56 W file type enum */
+ /* 60 L devmajor */
+ /* 68 L devminor */
+ /* 76 L unique ID (inode) */
+ /* 84 L permissions */
+ /* 92 L link count */
+ LSET(data, 0, SMB_SIZE_NO_CHANGE);
+ LSET(data, 8, SMB_SIZE_NO_CHANGE);
+ LSET(data, 16, SMB_TIME_NO_CHANGE);
+ LSET(data, 24, SMB_TIME_NO_CHANGE);
+ LSET(data, 32, SMB_TIME_NO_CHANGE);
+ LSET(data, 40, SMB_UID_NO_CHANGE);
+ LSET(data, 48, SMB_GID_NO_CHANGE);
+ LSET(data, 56, smb_filetype_from_mode(attr->ia_mode));
+ LSET(data, 60, major);
+ LSET(data, 68, minor);
+ LSET(data, 76, 0);
+ LSET(data, 84, SMB_MODE_NO_CHANGE);
+ LSET(data, 92, 0);
+
+ if (attr->ia_valid & ATTR_SIZE) {
+ LSET(data, 0, attr->ia_size);
+ LSET(data, 8, 0); /* can't set anyway */
+ }
+
+ /*
+ * FIXME: check the conversion function it the correct one
+ *
+ * we can't set ctime but we might as well pass this to the server
+ * and let it ignore it.
+ */
+ if (attr->ia_valid & ATTR_CTIME) {
+ nttime = smb_unixutc2ntutc(attr->ia_ctime);
+ LSET(data, 16, nttime);
+ }
+ if (attr->ia_valid & ATTR_ATIME) {
+ nttime = smb_unixutc2ntutc(attr->ia_atime);
+ LSET(data, 24, nttime);
+ }
+ if (attr->ia_valid & ATTR_MTIME) {
+ nttime = smb_unixutc2ntutc(attr->ia_mtime);
+ LSET(data, 32, nttime);
+ }
+
+ if (attr->ia_valid & ATTR_UID) {
+ LSET(data, 40, attr->ia_uid);
+ }
+ if (attr->ia_valid & ATTR_GID) {
+ LSET(data, 48, attr->ia_gid);
+ }
+
+ if (attr->ia_valid & ATTR_MODE) {
+ LSET(data, 84, attr->ia_mode);
+ }
+
+ result = smb_trans2_request(server, TRANSACT2_SETPATHINFO,
+ sizeof(data), data, p - param, param,
+ &resp_data_len, &resp_data,
+ &resp_param_len, &resp_param);
+ if (result < 0)
+ {
+ if (smb_retry(server))
+ goto retry;
+ goto out;
+ }
+ result = 0;
+ if (server->rcls != 0)
+ result = smb_errno(server);
+
+out:
+ smb_unlock_server(server);
+ return result;
+}
+
+/*
+ * Set the modify and access timestamps for a file.
+ *
+ * Incredibly enough, in all of SMB there is no message to allow
+ * setting both attributes and timestamps at once.
+ *
+ * Bugs Noted:
+ * (1) Win 95 doesn't support the TRANSACT2_SETFILEINFO message
+ * with info level 1 (INFO_STANDARD).
+ * (2) Win 95 seems not to support setting directory timestamps.
+ * (3) Under the core protocol apparently the only way to set the
+ * timestamp is to open and close the file.
+ */
+int
+smb_proc_settime(struct dentry *dentry, struct smb_fattr *fattr)
+{
+ struct smb_sb_info *server = server_from_dentry(dentry);
+ struct inode *inode = dentry->d_inode;
+ int result;
+
+ VERBOSE("setting %s/%s, open=%d\n",
+ DENTRY_PATH(dentry), smb_is_open(inode));
+
+ smb_lock_server(server);
+ /* setting the time on a Win95 server fails (tridge) */
+ if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2 &&
+ !(server->mnt->flags & SMB_MOUNT_WIN95)) {
+ if (smb_is_open(inode) &&
+ inode->u.smbfs_i.access != SMB_O_RDONLY)
+ result = smb_proc_setattr_ext(server, inode, fattr);
+ else
+ result = smb_proc_setattr_trans2(server, dentry, fattr);
+ } else {
+ /*
+ * Fail silently on directories ... timestamp can't be set?
+ */
+ result = 0;
+ if (S_ISREG(inode->i_mode)) {
+ /*
+ * Set the mtime by opening and closing the file.
+ * Note that the file is opened read-only, but this
+ * still allows us to set the date (tridge)
+ */
+ result = -EACCES;
+ if (!smb_is_open(inode))
+ smb_proc_open(server, dentry, SMB_O_RDONLY);
+ if (smb_is_open(inode)) {
+ inode->i_mtime = fattr->f_mtime;
+ result = smb_proc_close_inode(server, inode);
+ }
+ }
+ }
+
+ smb_unlock_server(server);
+ return result;
+}
+
+int
+smb_proc_dskattr(struct super_block *sb, struct statfs *attr)
+{
+ struct smb_sb_info *server = &(sb->u.smbfs_sb);
+ int result;
+ char *p;
+ long unit;
+
+ smb_lock_server(server);
+
+ retry:
+ smb_setup_header(server, SMBdskattr, 0, 0);
+
+ if ((result = smb_request_ok(server, SMBdskattr, 5, 0)) < 0) {
+ if (smb_retry(server))
+ goto retry;
+ goto out;
+ }
+ p = SMB_VWV(server->packet);
+ unit = (WVAL(p, 2) * WVAL(p, 4)) >> SMB_ST_BLKSHIFT;
+ attr->f_blocks = WVAL(p, 0) * unit;
+ attr->f_bsize = SMB_ST_BLKSIZE;
+ attr->f_bavail = attr->f_bfree = WVAL(p, 6) * unit;
+ result = 0;
+
+out:
+ smb_unlock_server(server);
+ return result;
+}
+
+int
+smb_proc_read_link(struct smb_sb_info *server, struct dentry *dentry,
+ char *buffer, int len)
+{
+ char *p, *param = server->temp_buf;
+ unsigned char *resp_data = NULL;
+ unsigned char *resp_param = NULL;
+ int resp_data_len = 0;
+ int resp_param_len = 0;
+ int result;
+
+ DEBUG1("readlink of %s/%s\n", DENTRY_PATH(dentry));
+
+ smb_lock_server(server);
+ retry:
+ WSET(param, 0, SMB_QUERY_FILE_UNIX_LINK);
+ DSET(param, 2, 0);
+ result = smb_encode_path(server, param + 6, SMB_MAXPATHLEN+1, dentry, NULL);
+ if (result < 0)
+ goto out;
+ p = param + 6 + result;
+
+ result = smb_trans2_request(server, TRANSACT2_QPATHINFO,
+ 0, NULL, p - param, param,
+ &resp_data_len, &resp_data,
+ &resp_param_len, &resp_param);
+ DEBUG1("for %s: result=%d, rcls=%d, err=%d\n",
+ &param[6], result, server->rcls, server->err);
+ if (result < 0) {
+ if (smb_retry(server))
+ goto retry;
+ goto out;
+ }
+ if (server->rcls != 0) {
+ VERBOSE("for %s: result=%d, rcls=%d, err=%d\n",
+ &param[6], result, server->rcls, server->err);
+ result = smb_errno(server);
+ goto out;
+ }
+
+ /* copy data up to the \0 or buffer length */
+ result = len;
+ if (resp_data_len < len)
+ result = resp_data_len;
+ strncpy(buffer, resp_data, result);
+
+out:
+ smb_unlock_server(server);
+ return result;
+}
+
+
+/*
+ * Create a symlink object called dentry which points to oldpath.
+ * Samba does not permit dangling links but returns a suitable error message.
+ */
+int
+smb_proc_symlink(struct smb_sb_info *server, struct dentry *dentry,
+ const char *oldpath)
+{
+ char *p, *param = server->temp_buf;
+ unsigned char *resp_data = NULL;
+ unsigned char *resp_param = NULL;
+ int resp_data_len = 0;
+ int resp_param_len = 0;
+ int result;
+
+ smb_lock_server(server);
+ retry:
+ WSET(param, 0, SMB_SET_FILE_UNIX_LINK);
+ DSET(param, 2, 0);
+ result = smb_encode_path(server, param + 6, SMB_MAXPATHLEN+1, dentry, NULL);
+ if (result < 0)
+ goto out;
+ p = param + 6 + result;
+
+ result = smb_trans2_request(server, TRANSACT2_SETPATHINFO,
+ strlen(oldpath) + 1,
+ (char *)oldpath, p - param, param,
+ &resp_data_len, &resp_data,
+ &resp_param_len, &resp_param);
+ DEBUG1("for %s: result=%d, rcls=%d, err=%d\n",
+ &param[6], result, server->rcls, server->err);
+ if (result < 0) {
+ if (smb_retry(server))
+ goto retry;
+ goto out;
+ }
+ if (server->rcls != 0) {
+ VERBOSE("for %s: result=%d, rcls=%d, err=%d\n",
+ &param[6], result, server->rcls, server->err);
+ result = smb_errno(server);
+ goto out;
+ }
+
+ result = 0;
+
+out:
+ smb_unlock_server(server);
+ return result;
+}
+
+/*
+ * Create a hard link object called new_dentry which points to dentry.
+ */
+int
+smb_proc_link(struct smb_sb_info *server, struct dentry *dentry,
+ struct dentry *new_dentry)
+{
+ char *p, *param = server->temp_buf;
+ unsigned char *resp_data = NULL;
+ unsigned char *resp_param = NULL;
+ int resp_data_len = 0;
+ int resp_param_len = 0;
+ int result;
+ int newpathlen = 0;
+ char newpath[SMB_MAXPATHLEN+1]; /* FIXME: ugh! */
+
+ smb_lock_server(server);
+
+ newpathlen = smb_encode_path(server, newpath, SMB_MAXPATHLEN+1, dentry, NULL);
+
+ DEBUG1("newpathlen = %d newpath=\"%s\"\n", newpathlen, newpath);
+retry:
+ WSET(param, 0, SMB_SET_FILE_UNIX_HLINK);
+ DSET(param, 2, 0);
+ result = smb_encode_path(server, param + 6, SMB_MAXPATHLEN+1, new_dentry, NULL);
+ if (result < 0)
+ goto out;
+ p = param + 6 + result;
+ DEBUG1("pathlen = %d oldpath=\"%s\"\n", result, param + 6);
+
+
+ result = smb_trans2_request(server, TRANSACT2_SETPATHINFO,
+ newpathlen + 1,
+ (char *)newpath, p - param, param,
+ &resp_data_len, &resp_data,
+ &resp_param_len, &resp_param);
+ DEBUG1("for %s: result=%d, rcls=%d, err=%d\n",
+ &param[6], result, server->rcls, server->err);
+ if (result < 0) {
+ if (smb_retry(server))
+ goto retry;
+ goto out;
+ }
+ if (server->rcls != 0) {
+ VERBOSE("for %s: result=%d, rcls=%d, err=%d\n",
+ &param[6], result, server->rcls, server->err);
+ result = smb_errno(server);
+ goto out;
+ }
+
+ result = 0;
+
+out:
+ smb_unlock_server(server);
+ return result;
+}
+
+/*
+ * We are called with the server locked
+ */
+static int
+smb_proc_query_cifsunix(struct smb_sb_info *server)
+{
+ char *param = server->temp_buf;
+ unsigned char *resp_data = NULL;
+ unsigned char *resp_param = NULL;
+ int resp_data_len = 0;
+ int resp_param_len = 0;
+ int result;
+ int major, minor;
+ u64 caps;
+
+ retry:
+ WSET(param, 0, SMB_QUERY_CIFS_UNIX_INFO);
+ result = smb_trans2_request(server, TRANSACT2_QFSINFO,
+ 0, NULL, 2, param,
+ &resp_data_len, &resp_data,
+ &resp_param_len, &resp_param);
+ if (result < 0) {
+ if (smb_retry(server))
+ goto retry;
+ goto out;
+ }
+
+ if (resp_data_len < 12) {
+ PARANOIA("Not enough data\n");
+ goto out;
+ }
+
+ major = WVAL(resp_data, 0);
+ minor = WVAL(resp_data, 2);
+ VERBOSE("Server implements CIFS Extensions for UNIX systems v%d.%d\n",
+ major, minor);
+ /* FIXME: verify that we are ok with this major/minor? */
+
+ caps = LVAL(resp_data, 4);
+ DEBUG1("Server capabilities 0x%016llx\n", caps);
+
+out:
+ return result;
+}
+
+
+static void
+install_ops(struct smb_ops *dst, struct smb_ops *src)
+{
+ memcpy(dst, src, sizeof(void *) * SMB_OPS_NUM_STATIC);
+}
+
+/* < LANMAN2 */
+static struct smb_ops smb_ops_core =
+{
+ .read = smb_proc_read,
+ .write = smb_proc_write,
+ .readdir = smb_proc_readdir_short,
+ .getattr = smb_proc_getattr_core,
+ .truncate = smb_proc_trunc32,
+};
+
+/* LANMAN2, OS/2, others? */
+static struct smb_ops smb_ops_os2 =
+{
+ .read = smb_proc_read,
+ .write = smb_proc_write,
+ .readdir = smb_proc_readdir_long,
+ .getattr = smb_proc_getattr_trans2_std,
+ .truncate = smb_proc_trunc32,
+};
+
+/* Win95, and possibly some NetApp versions too */
+static struct smb_ops smb_ops_win95 =
+{
+ .read = smb_proc_read, /* does not support 12word readX */
+ .write = smb_proc_write,
+ .readdir = smb_proc_readdir_long,
+ .getattr = smb_proc_getattr_95,
+ .truncate = smb_proc_trunc95,
+};
+
+/* Samba, NT4 and NT5 */
+static struct smb_ops smb_ops_winNT =
+{
+ .read = smb_proc_readX,
+ .write = smb_proc_writeX,
+ .readdir = smb_proc_readdir_long,
+ .getattr = smb_proc_getattr_trans2_all,
+ .truncate = smb_proc_trunc64,
+};
+
+/* Samba w/ unix extensions. Others? */
+static struct smb_ops smb_ops_unix =
+{
+ .read = smb_proc_readX,
+ .write = smb_proc_writeX,
+ .readdir = smb_proc_readdir_long,
+ .getattr = smb_proc_getattr_unix,
+ .truncate = smb_proc_trunc64,
+};
+
+/* Place holder until real ops are in place */
+static struct smb_ops smb_ops_null =
+{
+ .getattr = smb_proc_getattr_null,
+};
+
+void smb_install_null_ops(struct smb_ops *ops)
+{
+ install_ops(ops, &smb_ops_null);
+}
diff --git a/fs/smbfs/proto.h b/fs/smbfs/proto.h
new file mode 100644
index 00000000000000..56d528e6a08762
--- /dev/null
+++ b/fs/smbfs/proto.h
@@ -0,0 +1,71 @@
+/*
+ * Autogenerated with cproto on: Sat Jan 24 14:28:52 CET 2004
+ */
+
+/* proc.c */
+extern int smb_setcodepage(struct smb_sb_info *server, struct smb_nls_codepage *cp);
+extern __u32 smb_len(__u8 *p);
+extern int smb_get_rsize(struct smb_sb_info *server);
+extern int smb_get_wsize(struct smb_sb_info *server);
+extern int smb_errno(struct smb_sb_info *server);
+extern int smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt);
+extern int smb_wakeup(struct smb_sb_info *server);
+extern __u8 *smb_setup_header(struct smb_sb_info *server, __u8 command, __u16 wct, __u16 bcc);
+extern int smb_open(struct dentry *dentry, int wish);
+extern int smb_close(struct inode *ino);
+extern int smb_close_fileid(struct dentry *dentry, __u16 fileid);
+extern int smb_proc_create(struct dentry *dentry, __u16 attr, time_t ctime, __u16 *fileid);
+extern int smb_proc_mv(struct dentry *old_dentry, struct dentry *new_dentry);
+extern int smb_proc_mkdir(struct dentry *dentry);
+extern int smb_proc_rmdir(struct dentry *dentry);
+extern int smb_proc_unlink(struct dentry *dentry);
+extern int smb_proc_flush(struct smb_sb_info *server, __u16 fileid);
+extern void smb_init_root_dirent(struct smb_sb_info *server, struct smb_fattr *fattr);
+extern void smb_decode_unix_basic(struct smb_fattr *fattr, char *p);
+extern int smb_proc_getattr(struct dentry *dir, struct smb_fattr *fattr);
+extern int smb_proc_setattr(struct dentry *dir, struct smb_fattr *fattr);
+extern int smb_proc_setattr_unix(struct dentry *dentry, struct iattr *attr, unsigned int major, unsigned int minor);
+extern int smb_proc_settime(struct dentry *dentry, struct smb_fattr *fattr);
+extern int smb_proc_dskattr(struct super_block *sb, struct statfs *attr);
+extern int smb_proc_read_link(struct smb_sb_info *server, struct dentry *dentry, char *buffer, int len);
+extern int smb_proc_symlink(struct smb_sb_info *server, struct dentry *dentry, const char *oldpath);
+extern int smb_proc_link(struct smb_sb_info *server, struct dentry *dentry, struct dentry *new_dentry);
+extern void smb_install_null_ops(struct smb_ops *ops);
+/* dir.c */
+extern struct file_operations smb_dir_operations;
+extern struct inode_operations smb_dir_inode_operations;
+extern struct inode_operations smb_dir_inode_operations_unix;
+extern void smb_new_dentry(struct dentry *dentry);
+extern void smb_renew_times(struct dentry *dentry);
+/* cache.c */
+extern void smb_invalid_dir_cache(struct inode *dir);
+extern void smb_invalidate_dircache_entries(struct dentry *parent);
+extern struct dentry *smb_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos);
+extern int smb_fill_cache(struct file *filp, void *dirent, filldir_t filldir, struct smb_cache_control *ctrl, struct qstr *qname, struct smb_fattr *entry);
+/* sock.c */
+extern int smb_valid_socket(struct inode *inode);
+extern int smb_catch_keepalive(struct smb_sb_info *server);
+extern int smb_dont_catch_keepalive(struct smb_sb_info *server);
+extern void smb_close_socket(struct smb_sb_info *server);
+extern int smb_round_length(int len);
+extern int smb_request(struct smb_sb_info *server);
+extern int smb_trans2_request(struct smb_sb_info *server, __u16 trans2_command, int ldata, unsigned char *data, int lparam, unsigned char *param, int *lrdata, unsigned char **rdata, int *lrparam, unsigned char **rparam);
+/* inode.c */
+extern struct inode *smb_iget(struct super_block *sb, struct smb_fattr *fattr);
+extern void smb_get_inode_attr(struct inode *inode, struct smb_fattr *fattr);
+extern void smb_set_inode_attr(struct inode *inode, struct smb_fattr *fattr);
+extern void smb_invalidate_inodes(struct smb_sb_info *server);
+extern int smb_revalidate_inode(struct dentry *dentry);
+extern struct super_block *smb_read_super(struct super_block *sb, void *raw_data, int silent);
+extern int smb_notify_change(struct dentry *dentry, struct iattr *attr);
+/* file.c */
+extern struct address_space_operations smb_file_aops;
+extern struct file_operations smb_file_operations;
+extern struct inode_operations smb_file_inode_operations;
+/* ioctl.c */
+extern int smb_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
+/* symlink.c */
+extern int smb_read_link(struct dentry *dentry, char *buffer, int len);
+extern int smb_symlink(struct inode *inode, struct dentry *dentry, const char *oldname);
+extern int smb_follow_link(struct dentry *dentry, struct nameidata *nd);
+extern struct inode_operations smb_link_inode_operations;
diff --git a/fs/smbfs/smb_debug.h b/fs/smbfs/smb_debug.h
new file mode 100644
index 00000000000000..e3f8fdd1f7f9c3
--- /dev/null
+++ b/fs/smbfs/smb_debug.h
@@ -0,0 +1,34 @@
+/*
+ * Defines some debug macros for smbfs.
+ */
+
+/* This makes a dentry parent/child name pair. Useful for debugging printk's */
+#define DENTRY_PATH(dentry) \
+ (dentry)->d_parent->d_name.name,(dentry)->d_name.name
+
+/*
+ * safety checks that should never happen ???
+ * these are normally enabled.
+ */
+#ifdef SMBFS_PARANOIA
+# define PARANOIA(f, a...) printk(KERN_NOTICE "%s: " f, __FUNCTION__ , ## a)
+#else
+# define PARANOIA(f, a...) do { ; } while(0)
+#endif
+
+/* lots of debug messages */
+#ifdef SMBFS_DEBUG_VERBOSE
+# define VERBOSE(f, a...) printk(KERN_DEBUG "%s: " f, __FUNCTION__ , ## a)
+#else
+# define VERBOSE(f, a...) do { ; } while(0)
+#endif
+
+/*
+ * "normal" debug messages, but not with a normal DEBUG define ... way
+ * too common name.
+ */
+#ifdef SMBFS_DEBUG
+# define DEBUG1(f, a...) printk(KERN_DEBUG "%s: " f, __FUNCTION__ , ## a)
+#else
+# define DEBUG1(f, a...) do { ; } while(0)
+#endif
diff --git a/fs/smbfs/sock.c b/fs/smbfs/sock.c
new file mode 100644
index 00000000000000..ef28809bfcf2b1
--- /dev/null
+++ b/fs/smbfs/sock.c
@@ -0,0 +1,956 @@
+/*
+ * sock.c
+ *
+ * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
+ * Copyright (C) 1997 by Volker Lendecke
+ *
+ * Please add a note about your changes to smbfs in the ChangeLog file.
+ */
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/socket.h>
+#include <linux/fcntl.h>
+#include <linux/file.h>
+#include <linux/poll.h>
+#include <linux/in.h>
+#include <linux/net.h>
+#include <linux/mm.h>
+#include <linux/netdevice.h>
+#include <linux/smp_lock.h>
+#include <net/scm.h>
+#include <net/ip.h>
+
+#include <linux/smb_fs.h>
+#include <linux/smb.h>
+#include <linux/smbno.h>
+
+#include <asm/uaccess.h>
+
+#include "smb_debug.h"
+#include "proto.h"
+
+
+static int
+_recvfrom(struct socket *socket, unsigned char *ubuf, int size,
+ unsigned flags)
+{
+ struct iovec iov;
+ struct msghdr msg;
+ struct scm_cookie scm;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = NULL;
+ iov.iov_base = ubuf;
+ iov.iov_len = size;
+
+ memset(&scm, 0,sizeof(scm));
+ size=socket->ops->recvmsg(socket, &msg, size, flags, &scm);
+ if(size>=0)
+ scm_recv(socket,&msg,&scm,flags);
+ return size;
+}
+
+static int
+_send(struct socket *socket, const void *buff, int len)
+{
+ struct iovec iov;
+ struct msghdr msg;
+ struct scm_cookie scm;
+ int err;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+
+ iov.iov_base = (void *)buff;
+ iov.iov_len = len;
+
+ msg.msg_flags = 0;
+
+ err = scm_send(socket, &msg, &scm);
+ if (err >= 0)
+ {
+ err = socket->ops->sendmsg(socket, &msg, len, &scm);
+ scm_destroy(&scm);
+ }
+ return err;
+}
+
+struct data_callback {
+ struct tq_struct cb;
+ struct sock *sk;
+};
+/*
+ * N.B. What happens if we're in here when the socket closes??
+ */
+static void
+found_data(struct sock *sk)
+{
+ /*
+ * FIXME: copied from sock_def_readable, it should be a call to
+ * server->data_ready() -- manfreds@colorfullife.com
+ */
+ read_lock(&sk->callback_lock);
+ if(!sk->dead) {
+ wake_up_interruptible(sk->sleep);
+ sock_wake_async(sk->socket,1,POLL_IN);
+ }
+ read_unlock(&sk->callback_lock);
+}
+
+static void
+smb_data_callback(void* ptr)
+{
+ struct data_callback* job=ptr;
+ struct socket *socket = job->sk->socket;
+ unsigned char peek_buf[4];
+ int result = 0;
+ mm_segment_t fs;
+ int count = 100; /* this is a lot, we should have some data waiting */
+ int found = 0;
+
+ fs = get_fs();
+ set_fs(get_ds());
+
+ lock_kernel();
+ while (count-- > 0) {
+ peek_buf[0] = 0;
+
+ result = -EIO;
+ if (job->sk->dead) {
+ PARANOIA("sock dead!\n");
+ break;
+ }
+
+ result = _recvfrom(socket, (void *) peek_buf, 1,
+ MSG_PEEK | MSG_DONTWAIT);
+ if (result < 0)
+ break;
+ if (peek_buf[0] != 0x85)
+ break;
+
+ /* got SESSION KEEP ALIVE */
+ result = _recvfrom(socket, (void *) peek_buf, 4,
+ MSG_DONTWAIT);
+
+ DEBUG1("got SESSION KEEPALIVE\n");
+
+ if (result < 0)
+ break;
+ found = 1;
+ }
+ unlock_kernel();
+ set_fs(fs);
+
+ DEBUG1("found=%d, count=%d, result=%d\n", found, count, result);
+ if (found)
+ found_data(job->sk);
+ smb_kfree(ptr);
+}
+
+static void
+smb_data_ready(struct sock *sk, int len)
+{
+ struct data_callback* job;
+ job = smb_kmalloc(sizeof(struct data_callback),GFP_ATOMIC);
+ if(job == 0) {
+ printk("smb_data_ready: lost SESSION KEEPALIVE due to OOM.\n");
+ found_data(sk);
+ return;
+ }
+ INIT_LIST_HEAD(&job->cb.list);
+ job->cb.sync = 0;
+ job->cb.routine = smb_data_callback;
+ job->cb.data = job;
+ job->sk = sk;
+ schedule_task(&job->cb);
+}
+
+int
+smb_valid_socket(struct inode * inode)
+{
+ return (inode && S_ISSOCK(inode->i_mode) &&
+ inode->u.socket_i.type == SOCK_STREAM);
+}
+
+static struct socket *
+server_sock(struct smb_sb_info *server)
+{
+ struct file *file;
+
+ if (server && (file = server->sock_file))
+ {
+#ifdef SMBFS_PARANOIA
+ if (!smb_valid_socket(file->f_dentry->d_inode))
+ PARANOIA("bad socket!\n");
+#endif
+ return &file->f_dentry->d_inode->u.socket_i;
+ }
+ return NULL;
+}
+
+int
+smb_catch_keepalive(struct smb_sb_info *server)
+{
+ struct socket *socket;
+ struct sock *sk;
+ void *data_ready;
+ int error;
+
+ error = -EINVAL;
+ socket = server_sock(server);
+ if (!socket)
+ {
+ printk(KERN_DEBUG "smb_catch_keepalive: did not get valid server!\n");
+ server->data_ready = NULL;
+ goto out;
+ }
+
+ sk = socket->sk;
+ if (sk == NULL)
+ {
+ DEBUG1("sk == NULL");
+ server->data_ready = NULL;
+ goto out;
+ }
+ DEBUG1("sk->d_r = %x, server->d_r = %x\n",
+ (unsigned int) (sk->data_ready),
+ (unsigned int) (server->data_ready));
+
+ /*
+ * Install the callback atomically to avoid races ...
+ */
+ data_ready = xchg(&sk->data_ready, smb_data_ready);
+ if (data_ready != smb_data_ready) {
+ server->data_ready = data_ready;
+ error = 0;
+ } else
+ printk(KERN_ERR "smb_catch_keepalive: already done\n");
+out:
+ return error;
+}
+
+int
+smb_dont_catch_keepalive(struct smb_sb_info *server)
+{
+ struct socket *socket;
+ struct sock *sk;
+ void * data_ready;
+ int error;
+
+ error = -EINVAL;
+ socket = server_sock(server);
+ if (!socket)
+ {
+ printk(KERN_DEBUG "smb_dont_catch_keepalive: did not get valid server!\n");
+ goto out;
+ }
+
+ sk = socket->sk;
+ if (sk == NULL)
+ {
+ DEBUG1("sk == NULL");
+ goto out;
+ }
+
+ /* Is this really an error?? */
+ if (server->data_ready == NULL)
+ {
+ printk(KERN_DEBUG "smb_dont_catch_keepalive: "
+ "server->data_ready == NULL\n");
+ goto out;
+ }
+ DEBUG1("smb_dont_catch_keepalive: sk->d_r = %x, server->d_r = %x\n",
+ (unsigned int) (sk->data_ready),
+ (unsigned int) (server->data_ready));
+
+ /*
+ * Restore the original callback atomically to avoid races ...
+ */
+ data_ready = xchg(&sk->data_ready, server->data_ready);
+ server->data_ready = NULL;
+ if (data_ready != smb_data_ready)
+ {
+ printk(KERN_ERR "smb_dont_catch_keepalive: "
+ "sk->data_ready != smb_data_ready\n");
+ }
+ error = 0;
+out:
+ return error;
+}
+
+/*
+ * Called with the server locked.
+ */
+void
+smb_close_socket(struct smb_sb_info *server)
+{
+ struct file * file = server->sock_file;
+
+ if (file)
+ {
+ VERBOSE("closing socket %p\n", server_sock(server));
+#ifdef SMBFS_PARANOIA
+ if (server_sock(server)->sk->data_ready == smb_data_ready)
+ PARANOIA("still catching keepalives!\n");
+#endif
+ server->sock_file = NULL;
+ fput(file);
+ }
+}
+
+/*
+ * Poll the server->socket to allow receives to time out.
+ * returns 0 when ok to continue, <0 on errors.
+ */
+static int
+smb_receive_poll(struct smb_sb_info *server)
+{
+ struct file *file = server->sock_file;
+ poll_table wait_table;
+ int result = 0;
+ int timeout = server->mnt->timeo * HZ;
+ int mask;
+
+ for (;;) {
+ poll_initwait(&wait_table);
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ mask = file->f_op->poll(file, &wait_table);
+ if (mask & POLLIN) {
+ poll_freewait(&wait_table);
+ current->state = TASK_RUNNING;
+ break;
+ }
+
+ timeout = schedule_timeout(timeout);
+ poll_freewait(&wait_table);
+ set_current_state(TASK_RUNNING);
+
+ if (wait_table.error) {
+ result = wait_table.error;
+ break;
+ }
+
+ if (signal_pending(current)) {
+ /* we got a signal (which?) tell the caller to
+ try again (on all signals?). */
+ DEBUG1("got signal_pending()\n");
+ result = -ERESTARTSYS;
+ break;
+ }
+ if (!timeout) {
+ printk(KERN_WARNING "SMB server not responding\n");
+ result = -EIO;
+ break;
+ }
+ }
+ return result;
+}
+
+static int
+smb_send_raw(struct socket *socket, unsigned char *source, int length)
+{
+ int result;
+ int already_sent = 0;
+
+ while (already_sent < length)
+ {
+ result = _send(socket,
+ (void *) (source + already_sent),
+ length - already_sent);
+
+ if (result == 0)
+ {
+ return -EIO;
+ }
+ if (result < 0)
+ {
+ DEBUG1("smb_send_raw: sendto error = %d\n", -result);
+ return result;
+ }
+ already_sent += result;
+ }
+ return already_sent;
+}
+
+static int
+smb_receive_raw(struct smb_sb_info *server, unsigned char *target, int length)
+{
+ int result;
+ int already_read = 0;
+ struct socket *socket = server_sock(server);
+
+ while (already_read < length)
+ {
+ result = smb_receive_poll(server);
+ if (result < 0) {
+ DEBUG1("poll error = %d\n", -result);
+ return result;
+ }
+ result = _recvfrom(socket,
+ (void *) (target + already_read),
+ length - already_read, 0);
+
+ if (result == 0)
+ {
+ return -EIO;
+ }
+ if (result < 0)
+ {
+ DEBUG1("recvfrom error = %d\n", -result);
+ return result;
+ }
+ already_read += result;
+ }
+ return already_read;
+}
+
+static int
+smb_get_length(struct smb_sb_info *server, unsigned char *header)
+{
+ int result;
+ unsigned char peek_buf[4];
+ mm_segment_t fs;
+
+ re_recv:
+ fs = get_fs();
+ set_fs(get_ds());
+ result = smb_receive_raw(server, peek_buf, 4);
+ set_fs(fs);
+
+ if (result < 0)
+ {
+ PARANOIA("recv error = %d\n", -result);
+ return result;
+ }
+ switch (peek_buf[0])
+ {
+ case 0x00:
+ case 0x82:
+ break;
+
+ case 0x85:
+ DEBUG1("Got SESSION KEEP ALIVE\n");
+ goto re_recv;
+
+ default:
+ PARANOIA("Invalid NBT packet, code=%x\n", peek_buf[0]);
+ return -EIO;
+ }
+
+ if (header != NULL)
+ {
+ memcpy(header, peek_buf, 4);
+ }
+ /* The length in the RFC NB header is the raw data length */
+ return smb_len(peek_buf);
+}
+
+/*
+ * Since we allocate memory in increments of PAGE_SIZE,
+ * round up the packet length to the next multiple.
+ */
+int
+smb_round_length(int len)
+{
+ return (len + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
+}
+
+/*
+ * smb_receive
+ * fs points to the correct segment
+ */
+static int
+smb_receive(struct smb_sb_info *server)
+{
+ unsigned char * packet = server->packet;
+ int len, result;
+ unsigned char peek_buf[4];
+
+ result = smb_get_length(server, peek_buf);
+ if (result < 0)
+ goto out;
+ len = result;
+ /*
+ * Some servers do not respect our max_xmit and send
+ * larger packets. Try to allocate a new packet,
+ * but don't free the old one unless we succeed.
+ */
+ if (len + 4 > server->packet_size)
+ {
+ int new_len = smb_round_length(len + 4);
+
+ result = -ENOMEM;
+ packet = smb_vmalloc(new_len);
+ if (packet == NULL)
+ goto out;
+ smb_vfree(server->packet);
+ server->packet = packet;
+ server->packet_size = new_len;
+ }
+ memcpy(packet, peek_buf, 4);
+ result = smb_receive_raw(server, packet + 4, len);
+ if (result < 0)
+ {
+ VERBOSE("receive error: %d\n", result);
+ goto out;
+ }
+ server->rcls = *(packet + smb_rcls);
+ server->err = WVAL(packet, smb_err);
+
+#ifdef SMBFS_DEBUG_VERBOSE
+ if (server->rcls != 0)
+ VERBOSE("rcls=%d, err=%d\n", server->rcls, server->err);
+#endif
+out:
+ return result;
+}
+
+/*
+ * This routine checks first for "fast track" processing, as most
+ * packets won't need to be copied. Otherwise, it allocates a new
+ * packet to hold the incoming data.
+ *
+ * Note that the final server packet must be the larger of the two;
+ * server packets aren't allowed to shrink.
+ */
+static int
+smb_receive_trans2(struct smb_sb_info *server,
+ int *ldata, unsigned char **data,
+ int *lparm, unsigned char **parm)
+{
+ unsigned char *inbuf, *base, *rcv_buf = NULL;
+ unsigned int parm_disp, parm_offset, parm_count, parm_tot, parm_len = 0;
+ unsigned int data_disp, data_offset, data_count, data_tot, data_len = 0;
+ unsigned int total_p = 0, total_d = 0, buf_len = 0;
+ int result;
+
+ while (1) {
+ result = smb_receive(server);
+ if (result < 0)
+ goto out;
+ inbuf = server->packet;
+ if (server->rcls != 0) {
+ *parm = *data = inbuf;
+ *ldata = *lparm = 0;
+ goto out;
+ }
+ /*
+ * Extract the control data from the packet.
+ */
+ data_tot = WVAL(inbuf, smb_tdrcnt);
+ parm_tot = WVAL(inbuf, smb_tprcnt);
+ parm_disp = WVAL(inbuf, smb_prdisp);
+ parm_offset = WVAL(inbuf, smb_proff);
+ parm_count = WVAL(inbuf, smb_prcnt);
+ data_disp = WVAL(inbuf, smb_drdisp);
+ data_offset = WVAL(inbuf, smb_droff);
+ data_count = WVAL(inbuf, smb_drcnt);
+ base = smb_base(inbuf);
+
+ /*
+ * Assume success and increment lengths.
+ */
+ parm_len += parm_count;
+ data_len += data_count;
+
+ if (!rcv_buf) {
+ /*
+ * Check for fast track processing ... just this packet.
+ */
+ if (parm_count == parm_tot && data_count == data_tot) {
+ VERBOSE("fast track, parm=%u %u %u, data=%u %u %u\n",
+ parm_disp, parm_offset, parm_count,
+ data_disp, data_offset, data_count);
+ *parm = base + parm_offset;
+ if (*parm - inbuf + parm_tot > server->packet_size)
+ goto out_bad_parm;
+ *data = base + data_offset;
+ if (*data - inbuf + data_tot > server->packet_size)
+ goto out_bad_data;
+ goto success;
+ }
+
+ /*
+ * Save the total parameter and data length.
+ */
+ total_d = data_tot;
+ total_p = parm_tot;
+
+ buf_len = total_d + total_p;
+ if (server->packet_size > buf_len)
+ buf_len = server->packet_size;
+ buf_len = smb_round_length(buf_len);
+ if (buf_len > SMB_MAX_PACKET_SIZE)
+ goto out_too_long;
+
+ rcv_buf = smb_vmalloc(buf_len);
+ if (!rcv_buf)
+ goto out_no_mem;
+ memset(rcv_buf, 0, buf_len);
+
+ *parm = rcv_buf;
+ *data = rcv_buf + total_p;
+ } else if (data_tot > total_d || parm_tot > total_p)
+ goto out_data_grew;
+
+ if (parm_disp + parm_count > total_p)
+ goto out_bad_parm;
+ if (parm_offset + parm_count > server->packet_size)
+ goto out_bad_parm;
+ if (data_disp + data_count > total_d)
+ goto out_bad_data;
+ if (data_offset + data_count > server->packet_size)
+ goto out_bad_data;
+ memcpy(*parm + parm_disp, base + parm_offset, parm_count);
+ memcpy(*data + data_disp, base + data_offset, data_count);
+
+ PARANOIA("copied, parm=%u of %u, data=%u of %u\n",
+ parm_len, parm_tot, data_len, data_tot);
+
+ /*
+ * Check whether we've received all of the data. Note that
+ * we use the packet totals -- total lengths might shrink!
+ */
+ if (data_len >= data_tot && parm_len >= parm_tot) {
+ data_len = data_tot;
+ parm_len = parm_tot;
+ break;
+ }
+ }
+
+ /*
+ * Install the new packet. Note that it's possible, though
+ * unlikely, that the new packet could be smaller than the
+ * old one, in which case we just copy the data.
+ */
+ inbuf = server->packet;
+ if (buf_len >= server->packet_size) {
+ server->packet_size = buf_len;
+ server->packet = rcv_buf;
+ rcv_buf = inbuf;
+ } else {
+ if (parm_len + data_len > buf_len)
+ goto out_data_grew;
+
+ PARANOIA("copying data, old size=%d, new size=%u\n",
+ server->packet_size, buf_len);
+ memcpy(inbuf, rcv_buf, parm_len + data_len);
+ }
+
+success:
+ *ldata = data_len;
+ *lparm = parm_len;
+out:
+ if (rcv_buf)
+ smb_vfree(rcv_buf);
+ return result;
+
+out_no_mem:
+ PARANOIA("couldn't allocate data area\n");
+ result = -ENOMEM;
+ goto out;
+out_too_long:
+ printk(KERN_ERR "smb_receive_trans2: data/param too long, data=%d, parm=%d\n",
+ data_tot, parm_tot);
+ goto out_error;
+out_data_grew:
+ printk(KERN_ERR "smb_receive_trans2: data/params grew!\n");
+ goto out_error;
+out_bad_parm:
+ printk(KERN_ERR "smb_receive_trans2: invalid parms, disp=%d, cnt=%d, tot=%d\n",
+ parm_disp, parm_count, parm_tot);
+ goto out_error;
+out_bad_data:
+ printk(KERN_ERR "smb_receive_trans2: invalid data, disp=%d, cnt=%d, tot=%d\n",
+ data_disp, data_count, data_tot);
+out_error:
+ result = -EIO;
+ goto out;
+}
+
+/*
+ * Called with the server locked
+ */
+int
+smb_request(struct smb_sb_info *server)
+{
+ unsigned long flags, sigpipe;
+ mm_segment_t fs;
+ sigset_t old_set;
+ int len, result;
+ unsigned char *buffer;
+
+ result = -EBADF;
+ buffer = server->packet;
+ if (!buffer)
+ goto bad_no_packet;
+
+ result = -EIO;
+ if (server->state != CONN_VALID)
+ goto bad_no_conn;
+
+ if ((result = smb_dont_catch_keepalive(server)) != 0)
+ goto bad_conn;
+
+ len = smb_len(buffer) + 4;
+ DEBUG1("len = %d cmd = 0x%X\n", len, buffer[8]);
+
+ spin_lock_irqsave(&current->sigmask_lock, flags);
+ sigpipe = sigismember(&current->pending.signal, SIGPIPE);
+ old_set = current->blocked;
+ siginitsetinv(&current->blocked, sigmask(SIGKILL)|sigmask(SIGSTOP));
+ recalc_sigpending(current);
+ spin_unlock_irqrestore(&current->sigmask_lock, flags);
+
+ fs = get_fs();
+ set_fs(get_ds());
+
+ result = smb_send_raw(server_sock(server), (void *) buffer, len);
+ if (result > 0)
+ {
+ result = smb_receive(server);
+ }
+
+ /* read/write errors are handled by errno */
+ spin_lock_irqsave(&current->sigmask_lock, flags);
+ if (result == -EPIPE && !sigpipe)
+ sigdelset(&current->pending.signal, SIGPIPE);
+ current->blocked = old_set;
+ recalc_sigpending(current);
+ spin_unlock_irqrestore(&current->sigmask_lock, flags);
+
+ set_fs(fs);
+
+ if (result >= 0)
+ {
+ int result2 = smb_catch_keepalive(server);
+ if (result2 < 0)
+ {
+ printk(KERN_ERR "smb_request: catch keepalive failed\n");
+ result = result2;
+ }
+ }
+ if (result < 0)
+ goto bad_conn;
+ /*
+ * Check for fatal server errors ...
+ */
+ if (server->rcls) {
+ int error = smb_errno(server);
+ if (error == -EBADSLT) {
+ printk(KERN_ERR "smb_request: tree ID invalid\n");
+ result = error;
+ goto bad_conn;
+ }
+ }
+
+out:
+ DEBUG1("result = %d\n", result);
+ return result;
+
+bad_conn:
+ PARANOIA("result %d, setting invalid\n", result);
+ server->state = CONN_INVALID;
+ smb_invalidate_inodes(server);
+ goto out;
+bad_no_packet:
+ printk(KERN_ERR "smb_request: no packet!\n");
+ goto out;
+bad_no_conn:
+ printk(KERN_ERR "smb_request: connection %d not valid!\n",
+ server->state);
+ goto out;
+}
+
+#define ROUND_UP(x) (((x)+3) & ~3)
+static int
+smb_send_trans2(struct smb_sb_info *server, __u16 trans2_command,
+ int ldata, unsigned char *data,
+ int lparam, unsigned char *param)
+{
+ struct socket *sock = server_sock(server);
+ struct scm_cookie scm;
+ int err;
+ int mparam, mdata;
+
+ /* I know the following is very ugly, but I want to build the
+ smb packet as efficiently as possible. */
+
+ const int smb_parameters = 15;
+ const int oparam =
+ ROUND_UP(SMB_HEADER_LEN + 2 * smb_parameters + 2 + 3);
+ const int odata =
+ ROUND_UP(oparam + lparam);
+ const int bcc =
+ odata + ldata - (SMB_HEADER_LEN + 2 * smb_parameters + 2);
+ const int packet_length =
+ SMB_HEADER_LEN + 2 * smb_parameters + bcc + 2;
+
+ unsigned char padding[4] =
+ {0,};
+ char *p;
+
+ struct iovec iov[4];
+ struct msghdr msg;
+
+ /* FIXME! this test needs to include SMB overhead too, I think ... */
+ if ((bcc + oparam) > server->opt.max_xmit)
+ return -ENOMEM;
+ p = smb_setup_header(server, SMBtrans2, smb_parameters, bcc);
+
+ /*
+ * max parameters + max data + max setup == max_xmit to make NT4 happy
+ * and not abort the transfer or split into multiple responses.
+ *
+ * -100 is to make room for headers, which OS/2 seems to include in the
+ * size calculation while NT4 does not?
+ */
+ mparam = SMB_TRANS2_MAX_PARAM;
+ mdata = server->opt.max_xmit - mparam - 100;
+ if (mdata < 1024) {
+ mdata = 1024;
+ mparam = 20;
+ }
+
+ WSET(server->packet, smb_tpscnt, lparam);
+ WSET(server->packet, smb_tdscnt, ldata);
+ WSET(server->packet, smb_mprcnt, mparam);
+ WSET(server->packet, smb_mdrcnt, mdata);
+ WSET(server->packet, smb_msrcnt, 0); /* max setup always 0 ? */
+ WSET(server->packet, smb_flags, 0);
+ DSET(server->packet, smb_timeout, 0);
+ WSET(server->packet, smb_pscnt, lparam);
+ WSET(server->packet, smb_psoff, oparam - 4);
+ WSET(server->packet, smb_dscnt, ldata);
+ WSET(server->packet, smb_dsoff, odata - 4);
+ WSET(server->packet, smb_suwcnt, 1);
+ WSET(server->packet, smb_setup0, trans2_command);
+ *p++ = 0; /* null smb_name for trans2 */
+ *p++ = 'D'; /* this was added because OS/2 does it */
+ *p++ = ' ';
+
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 4;
+ msg.msg_flags = 0;
+
+ iov[0].iov_base = (void *) server->packet;
+ iov[0].iov_len = oparam;
+ iov[1].iov_base = (param == NULL) ? padding : param;
+ iov[1].iov_len = lparam;
+ iov[2].iov_base = padding;
+ iov[2].iov_len = odata - oparam - lparam;
+ iov[3].iov_base = (data == NULL) ? padding : data;
+ iov[3].iov_len = ldata;
+
+ err = scm_send(sock, &msg, &scm);
+ if (err >= 0) {
+ err = sock->ops->sendmsg(sock, &msg, packet_length, &scm);
+ scm_destroy(&scm);
+ }
+ return err;
+}
+
+/*
+ * This is not really a trans2 request, we assume that you only have
+ * one packet to send.
+ */
+int
+smb_trans2_request(struct smb_sb_info *server, __u16 trans2_command,
+ int ldata, unsigned char *data,
+ int lparam, unsigned char *param,
+ int *lrdata, unsigned char **rdata,
+ int *lrparam, unsigned char **rparam)
+{
+ sigset_t old_set;
+ unsigned long flags, sigpipe;
+ mm_segment_t fs;
+ int result;
+
+ DEBUG1("com=%d, ld=%d, lp=%d\n", trans2_command, ldata, lparam);
+
+ /*
+ * These are initialized in smb_request_ok, but not here??
+ */
+ server->rcls = 0;
+ server->err = 0;
+
+ result = -EIO;
+ if (server->state != CONN_VALID)
+ goto out;
+
+ if ((result = smb_dont_catch_keepalive(server)) != 0)
+ goto bad_conn;
+
+ spin_lock_irqsave(&current->sigmask_lock, flags);
+ sigpipe = sigismember(&current->pending.signal, SIGPIPE);
+ old_set = current->blocked;
+ siginitsetinv(&current->blocked, sigmask(SIGKILL)|sigmask(SIGSTOP));
+ recalc_sigpending(current);
+ spin_unlock_irqrestore(&current->sigmask_lock, flags);
+
+ fs = get_fs();
+ set_fs(get_ds());
+
+ result = smb_send_trans2(server, trans2_command,
+ ldata, data, lparam, param);
+ if (result >= 0)
+ {
+ result = smb_receive_trans2(server,
+ lrdata, rdata, lrparam, rparam);
+ }
+
+ /* read/write errors are handled by errno */
+ spin_lock_irqsave(&current->sigmask_lock, flags);
+ if (result == -EPIPE && !sigpipe)
+ sigdelset(&current->pending.signal, SIGPIPE);
+ current->blocked = old_set;
+ recalc_sigpending(current);
+ spin_unlock_irqrestore(&current->sigmask_lock, flags);
+
+ set_fs(fs);
+
+ if (result >= 0)
+ {
+ int result2 = smb_catch_keepalive(server);
+ if (result2 < 0)
+ {
+ result = result2;
+ }
+ }
+ if (result < 0)
+ goto bad_conn;
+ /*
+ * Check for fatal server errors ...
+ */
+ if (server->rcls) {
+ int error = smb_errno(server);
+ if (error == -EBADSLT) {
+ printk(KERN_ERR "smb_request: tree ID invalid\n");
+ result = error;
+ goto bad_conn;
+ }
+ }
+
+out:
+ return result;
+
+bad_conn:
+ PARANOIA("result=%d, setting invalid\n", result);
+ server->state = CONN_INVALID;
+ smb_invalidate_inodes(server);
+ goto out;
+}
diff --git a/fs/smbfs/symlink.c b/fs/smbfs/symlink.c
new file mode 100644
index 00000000000000..360ea998a9f640
--- /dev/null
+++ b/fs/smbfs/symlink.c
@@ -0,0 +1,90 @@
+/*
+ * symlink.c
+ *
+ * Copyright (C) 2002 by John Newbigin
+ *
+ * Please add a note about your changes to smbfs in the ChangeLog file.
+ */
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/smp_lock.h>
+#include <linux/net.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#include <linux/smbno.h>
+#include <linux/smb_fs.h>
+
+#include "smb_debug.h"
+#include "proto.h"
+
+int smb_read_link(struct dentry *dentry, char *buffer, int len)
+{
+ char *link;
+ int result;
+ DEBUG1("read link buffer len = %d\n", len);
+
+ result = -ENOMEM;
+ link = kmalloc(SMB_MAXNAMELEN + 1, GFP_KERNEL);
+ if (!link)
+ goto out;
+
+ result = smb_proc_read_link(server_from_dentry(dentry), dentry, link,
+ SMB_MAXNAMELEN);
+ if (result < 0)
+ goto out_free;
+ result = vfs_readlink(dentry, buffer, len, link);
+
+out_free:
+ kfree(link);
+out:
+ return result;
+}
+
+int smb_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
+{
+ DEBUG1("create symlink %s -> %s/%s\n", oldname, DENTRY_PATH(dentry));
+
+ smb_invalid_dir_cache(dir);
+ return smb_proc_symlink(server_from_dentry(dentry), dentry, oldname);
+}
+
+int smb_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+ char *link;
+ int result;
+
+ DEBUG1("followlink of %s/%s\n", DENTRY_PATH(dentry));
+
+ result = -ENOMEM;
+ link = kmalloc(SMB_MAXNAMELEN + 1, GFP_KERNEL);
+ if (!link)
+ goto out;
+
+ result = smb_proc_read_link(server_from_dentry(dentry), dentry, link,
+ SMB_MAXNAMELEN);
+ if (result < 0 || result >= SMB_MAXNAMELEN)
+ goto out_free;
+ link[result] = 0;
+
+ result = vfs_follow_link(nd, link);
+
+out_free:
+ kfree(link);
+out:
+ return result;
+}
+
+struct inode_operations smb_link_inode_operations =
+{
+ .readlink = smb_read_link,
+ .follow_link = smb_follow_link,
+};
diff --git a/fs/stat.c b/fs/stat.c
new file mode 100644
index 00000000000000..8392356a230703
--- /dev/null
+++ b/fs/stat.c
@@ -0,0 +1,385 @@
+/*
+ * linux/fs/stat.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+#include <linux/config.h>
+#include <linux/mm.h>
+#include <linux/errno.h>
+#include <linux/file.h>
+#include <linux/smp_lock.h>
+#include <linux/highuid.h>
+
+#include <asm/uaccess.h>
+
+/*
+ * Revalidate the inode. This is required for proper NFS attribute caching.
+ */
+static __inline__ int
+do_revalidate(struct dentry *dentry)
+{
+ struct inode * inode = dentry->d_inode;
+ if (inode->i_op && inode->i_op->revalidate)
+ return inode->i_op->revalidate(dentry);
+ return 0;
+}
+
+
+#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__) && !defined(CONFIG_ARCH_S390) && !defined(__hppa__) && !defined(__x86_64__) && !defined(__mips__)
+
+/*
+ * For backward compatibility? Maybe this should be moved
+ * into arch/i386 instead?
+ */
+static int cp_old_stat(struct inode * inode, struct __old_kernel_stat * statbuf)
+{
+ static int warncount = 5;
+ struct __old_kernel_stat tmp;
+
+ memset(&tmp, 0, sizeof(struct __old_kernel_stat));
+
+ if (warncount > 0) {
+ warncount--;
+ printk(KERN_WARNING "VFS: Warning: %s using old stat() call. Recompile your binary.\n",
+ current->comm);
+ } else if (warncount < 0) {
+ /* it's laughable, but... */
+ warncount = 0;
+ }
+
+ tmp.st_dev = kdev_t_to_nr(inode->i_dev);
+ tmp.st_ino = inode->i_ino;
+ tmp.st_mode = inode->i_mode;
+ tmp.st_nlink = inode->i_nlink;
+ if (tmp.st_nlink != inode->i_nlink)
+ return -EOVERFLOW;
+ SET_OLDSTAT_UID(tmp, inode->i_uid);
+ SET_OLDSTAT_GID(tmp, inode->i_gid);
+ tmp.st_rdev = kdev_t_to_nr(inode->i_rdev);
+#if BITS_PER_LONG == 32
+ if (inode->i_size > MAX_NON_LFS)
+ return -EOVERFLOW;
+#endif
+ tmp.st_size = inode->i_size;
+ tmp.st_atime = inode->i_atime;
+ tmp.st_mtime = inode->i_mtime;
+ tmp.st_ctime = inode->i_ctime;
+ return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0;
+}
+
+#endif
+
+static int cp_new_stat(struct inode * inode, struct stat * statbuf)
+{
+ struct stat tmp;
+ unsigned int blocks, indirect;
+
+ memset(&tmp, 0, sizeof(tmp));
+ tmp.st_dev = kdev_t_to_nr(inode->i_dev);
+ tmp.st_ino = inode->i_ino;
+ tmp.st_mode = inode->i_mode;
+ tmp.st_nlink = inode->i_nlink;
+ if (tmp.st_nlink != inode->i_nlink)
+ return -EOVERFLOW;
+ SET_STAT_UID(tmp, inode->i_uid);
+ SET_STAT_GID(tmp, inode->i_gid);
+ tmp.st_rdev = kdev_t_to_nr(inode->i_rdev);
+#if BITS_PER_LONG == 32
+ if (inode->i_size > MAX_NON_LFS)
+ return -EOVERFLOW;
+#endif
+ tmp.st_size = inode->i_size;
+ tmp.st_atime = inode->i_atime;
+ tmp.st_mtime = inode->i_mtime;
+ tmp.st_ctime = inode->i_ctime;
+/*
+ * st_blocks and st_blksize are approximated with a simple algorithm if
+ * they aren't supported directly by the filesystem. The minix and msdos
+ * filesystems don't keep track of blocks, so they would either have to
+ * be counted explicitly (by delving into the file itself), or by using
+ * this simple algorithm to get a reasonable (although not 100% accurate)
+ * value.
+ */
+
+/*
+ * Use minix fs values for the number of direct and indirect blocks. The
+ * count is now exact for the minix fs except that it counts zero blocks.
+ * Everything is in units of BLOCK_SIZE until the assignment to
+ * tmp.st_blksize.
+ */
+#define D_B 7
+#define I_B (BLOCK_SIZE / sizeof(unsigned short))
+
+ if (!inode->i_blksize) {
+ blocks = (tmp.st_size + BLOCK_SIZE - 1) / BLOCK_SIZE;
+ if (blocks > D_B) {
+ indirect = (blocks - D_B + I_B - 1) / I_B;
+ blocks += indirect;
+ if (indirect > 1) {
+ indirect = (indirect - 1 + I_B - 1) / I_B;
+ blocks += indirect;
+ if (indirect > 1)
+ blocks++;
+ }
+ }
+ tmp.st_blocks = (BLOCK_SIZE / 512) * blocks;
+ tmp.st_blksize = BLOCK_SIZE;
+ } else {
+ tmp.st_blocks = inode->i_blocks;
+ tmp.st_blksize = inode->i_blksize;
+ }
+ return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0;
+}
+
+
+#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__) && !defined(CONFIG_ARCH_S390) && !defined(__hppa__) && !defined(__x86_64__) && !defined(__mips__)
+/*
+ * For backward compatibility? Maybe this should be moved
+ * into arch/i386 instead?
+ */
+asmlinkage long sys_stat(char * filename, struct __old_kernel_stat * statbuf)
+{
+ struct nameidata nd;
+ int error;
+
+ error = user_path_walk(filename, &nd);
+ if (!error) {
+ error = do_revalidate(nd.dentry);
+ if (!error)
+ error = cp_old_stat(nd.dentry->d_inode, statbuf);
+ path_release(&nd);
+ }
+ return error;
+}
+#endif
+
+asmlinkage long sys_newstat(char * filename, struct stat * statbuf)
+{
+ struct nameidata nd;
+ int error;
+
+ error = user_path_walk(filename, &nd);
+ if (!error) {
+ error = do_revalidate(nd.dentry);
+ if (!error)
+ error = cp_new_stat(nd.dentry->d_inode, statbuf);
+ path_release(&nd);
+ }
+ return error;
+}
+
+#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__) && !defined(CONFIG_ARCH_S390) && !defined(__hppa__) && !defined(__x86_64__) && !defined(__mips__)
+
+/*
+ * For backward compatibility? Maybe this should be moved
+ * into arch/i386 instead?
+ */
+asmlinkage long sys_lstat(char * filename, struct __old_kernel_stat * statbuf)
+{
+ struct nameidata nd;
+ int error;
+
+ error = user_path_walk_link(filename, &nd);
+ if (!error) {
+ error = do_revalidate(nd.dentry);
+ if (!error)
+ error = cp_old_stat(nd.dentry->d_inode, statbuf);
+ path_release(&nd);
+ }
+ return error;
+}
+
+#endif
+
+asmlinkage long sys_newlstat(char * filename, struct stat * statbuf)
+{
+ struct nameidata nd;
+ int error;
+
+ error = user_path_walk_link(filename, &nd);
+ if (!error) {
+ error = do_revalidate(nd.dentry);
+ if (!error)
+ error = cp_new_stat(nd.dentry->d_inode, statbuf);
+ path_release(&nd);
+ }
+ return error;
+}
+
+#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__) && !defined(CONFIG_ARCH_S390) && !defined(__hppa__) && !defined(__x86_64__) && !defined(__mips__)
+
+/*
+ * For backward compatibility? Maybe this should be moved
+ * into arch/i386 instead?
+ */
+asmlinkage long sys_fstat(unsigned int fd, struct __old_kernel_stat * statbuf)
+{
+ struct file * f;
+ int err = -EBADF;
+
+ f = fget(fd);
+ if (f) {
+ struct dentry * dentry = f->f_dentry;
+
+ err = do_revalidate(dentry);
+ if (!err)
+ err = cp_old_stat(dentry->d_inode, statbuf);
+ fput(f);
+ }
+ return err;
+}
+
+#endif
+
+asmlinkage long sys_newfstat(unsigned int fd, struct stat * statbuf)
+{
+ struct file * f;
+ int err = -EBADF;
+
+ f = fget(fd);
+ if (f) {
+ struct dentry * dentry = f->f_dentry;
+
+ err = do_revalidate(dentry);
+ if (!err)
+ err = cp_new_stat(dentry->d_inode, statbuf);
+ fput(f);
+ }
+ return err;
+}
+
+asmlinkage long sys_readlink(const char * path, char * buf, int bufsiz)
+{
+ struct nameidata nd;
+ int error;
+
+ if (bufsiz <= 0)
+ return -EINVAL;
+
+ error = user_path_walk_link(path, &nd);
+ if (!error) {
+ struct inode * inode = nd.dentry->d_inode;
+
+ error = -EINVAL;
+ if (inode->i_op && inode->i_op->readlink &&
+ !(error = do_revalidate(nd.dentry))) {
+ UPDATE_ATIME(inode);
+ error = inode->i_op->readlink(nd.dentry, buf, bufsiz);
+ }
+ path_release(&nd);
+ }
+ return error;
+}
+
+
+/* ---------- LFS-64 ----------- */
+#if !defined(__alpha__) && !defined(__ia64__) && !defined(__mips64) && !defined(__x86_64__) && !defined(CONFIG_ARCH_S390X)
+
+static long cp_new_stat64(struct inode * inode, struct stat64 * statbuf)
+{
+ struct stat64 tmp;
+ unsigned int blocks, indirect;
+
+ memset(&tmp, 0, sizeof(tmp));
+ tmp.st_dev = kdev_t_to_nr(inode->i_dev);
+ tmp.st_ino = inode->i_ino;
+#ifdef STAT64_HAS_BROKEN_ST_INO
+ tmp.__st_ino = inode->i_ino;
+#endif
+ tmp.st_mode = inode->i_mode;
+ tmp.st_nlink = inode->i_nlink;
+ tmp.st_uid = inode->i_uid;
+ tmp.st_gid = inode->i_gid;
+ tmp.st_rdev = kdev_t_to_nr(inode->i_rdev);
+ tmp.st_atime = inode->i_atime;
+ tmp.st_mtime = inode->i_mtime;
+ tmp.st_ctime = inode->i_ctime;
+ tmp.st_size = inode->i_size;
+/*
+ * st_blocks and st_blksize are approximated with a simple algorithm if
+ * they aren't supported directly by the filesystem. The minix and msdos
+ * filesystems don't keep track of blocks, so they would either have to
+ * be counted explicitly (by delving into the file itself), or by using
+ * this simple algorithm to get a reasonable (although not 100% accurate)
+ * value.
+ */
+
+/*
+ * Use minix fs values for the number of direct and indirect blocks. The
+ * count is now exact for the minix fs except that it counts zero blocks.
+ * Everything is in units of BLOCK_SIZE until the assignment to
+ * tmp.st_blksize.
+ */
+#define D_B 7
+#define I_B (BLOCK_SIZE / sizeof(unsigned short))
+
+ if (!inode->i_blksize) {
+ blocks = (tmp.st_size + BLOCK_SIZE - 1) >> BLOCK_SIZE_BITS;
+ if (blocks > D_B) {
+ indirect = (blocks - D_B + I_B - 1) / I_B;
+ blocks += indirect;
+ if (indirect > 1) {
+ indirect = (indirect - 1 + I_B - 1) / I_B;
+ blocks += indirect;
+ if (indirect > 1)
+ blocks++;
+ }
+ }
+ tmp.st_blocks = (BLOCK_SIZE / 512) * blocks;
+ tmp.st_blksize = BLOCK_SIZE;
+ } else {
+ tmp.st_blocks = inode->i_blocks;
+ tmp.st_blksize = inode->i_blksize;
+ }
+ return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0;
+}
+
+asmlinkage long sys_stat64(char * filename, struct stat64 * statbuf, long flags)
+{
+ struct nameidata nd;
+ int error;
+
+ error = user_path_walk(filename, &nd);
+ if (!error) {
+ error = do_revalidate(nd.dentry);
+ if (!error)
+ error = cp_new_stat64(nd.dentry->d_inode, statbuf);
+ path_release(&nd);
+ }
+ return error;
+}
+
+asmlinkage long sys_lstat64(char * filename, struct stat64 * statbuf, long flags)
+{
+ struct nameidata nd;
+ int error;
+
+ error = user_path_walk_link(filename, &nd);
+ if (!error) {
+ error = do_revalidate(nd.dentry);
+ if (!error)
+ error = cp_new_stat64(nd.dentry->d_inode, statbuf);
+ path_release(&nd);
+ }
+ return error;
+}
+
+asmlinkage long sys_fstat64(unsigned long fd, struct stat64 * statbuf, long flags)
+{
+ struct file * f;
+ int err = -EBADF;
+
+ f = fget(fd);
+ if (f) {
+ struct dentry * dentry = f->f_dentry;
+
+ err = do_revalidate(dentry);
+ if (!err)
+ err = cp_new_stat64(dentry->d_inode, statbuf);
+ fput(f);
+ }
+ return err;
+}
+
+#endif /* LFS-64 */
diff --git a/fs/super.c b/fs/super.c
new file mode 100644
index 00000000000000..94e95b875a7506
--- /dev/null
+++ b/fs/super.c
@@ -0,0 +1,869 @@
+/*
+ * linux/fs/super.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * super.c contains code to handle: - mount structures
+ * - super-block tables
+ * - filesystem drivers list
+ * - mount system call
+ * - umount system call
+ * - ustat system call
+ *
+ * GK 2/5/95 - Changed to support mounting the root fs via NFS
+ *
+ * Added kerneld support: Jacques Gelinas and Bjorn Ekwall
+ * Added change_root: Werner Almesberger & Hans Lermen, Feb '96
+ * Added options to /proc/mounts:
+ * Torbjörn Lindh (torbjorn.lindh@gopta.se), April 14, 1996.
+ * Added devfs support: Richard Gooch <rgooch@atnf.csiro.au>, 13-JAN-1998
+ * Heavily rewritten for 'one fs - one tree' dcache architecture. AV, Mar 2000
+ */
+
+#include <linux/config.h>
+#include <linux/slab.h>
+#include <linux/locks.h>
+#include <linux/smp_lock.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/major.h>
+#include <linux/acct.h>
+#include <linux/quotaops.h>
+
+#include <asm/uaccess.h>
+
+#include <linux/kmod.h>
+#define __NO_VERSION__
+#include <linux/module.h>
+
+LIST_HEAD(super_blocks);
+spinlock_t sb_lock = SPIN_LOCK_UNLOCKED;
+
+/*
+ * Handling of filesystem drivers list.
+ * Rules:
+ * Inclusion to/removals from/scanning of list are protected by spinlock.
+ * During the unload module must call unregister_filesystem().
+ * We can access the fields of list element if:
+ * 1) spinlock is held or
+ * 2) we hold the reference to the module.
+ * The latter can be guaranteed by call of try_inc_mod_count(); if it
+ * returned 0 we must skip the element, otherwise we got the reference.
+ * Once the reference is obtained we can drop the spinlock.
+ */
+
+static struct file_system_type *file_systems;
+static rwlock_t file_systems_lock = RW_LOCK_UNLOCKED;
+
+/* WARNING: This can be used only if we _already_ own a reference */
+static void get_filesystem(struct file_system_type *fs)
+{
+ if (fs->owner)
+ __MOD_INC_USE_COUNT(fs->owner);
+}
+
+static void put_filesystem(struct file_system_type *fs)
+{
+ if (fs->owner)
+ __MOD_DEC_USE_COUNT(fs->owner);
+}
+
+static struct file_system_type **find_filesystem(const char *name)
+{
+ struct file_system_type **p;
+ for (p=&file_systems; *p; p=&(*p)->next)
+ if (strcmp((*p)->name,name) == 0)
+ break;
+ return p;
+}
+
+/**
+ * register_filesystem - register a new filesystem
+ * @fs: the file system structure
+ *
+ * Adds the file system passed to the list of file systems the kernel
+ * is aware of for mount and other syscalls. Returns 0 on success,
+ * or a negative errno code on an error.
+ *
+ * The &struct file_system_type that is passed is linked into the kernel
+ * structures and must not be freed until the file system has been
+ * unregistered.
+ */
+
+int register_filesystem(struct file_system_type * fs)
+{
+ int res = 0;
+ struct file_system_type ** p;
+
+ if (!fs)
+ return -EINVAL;
+ if (fs->next)
+ return -EBUSY;
+ INIT_LIST_HEAD(&fs->fs_supers);
+ write_lock(&file_systems_lock);
+ p = find_filesystem(fs->name);
+ if (*p)
+ res = -EBUSY;
+ else
+ *p = fs;
+ write_unlock(&file_systems_lock);
+ return res;
+}
+
+/**
+ * unregister_filesystem - unregister a file system
+ * @fs: filesystem to unregister
+ *
+ * Remove a file system that was previously successfully registered
+ * with the kernel. An error is returned if the file system is not found.
+ * Zero is returned on a success.
+ *
+ * Once this function has returned the &struct file_system_type structure
+ * may be freed or reused.
+ */
+
+int unregister_filesystem(struct file_system_type * fs)
+{
+ struct file_system_type ** tmp;
+
+ write_lock(&file_systems_lock);
+ tmp = &file_systems;
+ while (*tmp) {
+ if (fs == *tmp) {
+ *tmp = fs->next;
+ fs->next = NULL;
+ write_unlock(&file_systems_lock);
+ return 0;
+ }
+ tmp = &(*tmp)->next;
+ }
+ write_unlock(&file_systems_lock);
+ return -EINVAL;
+}
+
+static int fs_index(const char * __name)
+{
+ struct file_system_type * tmp;
+ char * name;
+ int err, index;
+
+ name = getname(__name);
+ err = PTR_ERR(name);
+ if (IS_ERR(name))
+ return err;
+
+ err = -EINVAL;
+ read_lock(&file_systems_lock);
+ for (tmp=file_systems, index=0 ; tmp ; tmp=tmp->next, index++) {
+ if (strcmp(tmp->name,name) == 0) {
+ err = index;
+ break;
+ }
+ }
+ read_unlock(&file_systems_lock);
+ putname(name);
+ return err;
+}
+
+static int fs_name(unsigned int index, char * buf)
+{
+ struct file_system_type * tmp;
+ int len, res;
+
+ read_lock(&file_systems_lock);
+ for (tmp = file_systems; tmp; tmp = tmp->next, index--)
+ if (index <= 0 && try_inc_mod_count(tmp->owner))
+ break;
+ read_unlock(&file_systems_lock);
+ if (!tmp)
+ return -EINVAL;
+
+ /* OK, we got the reference, so we can safely block */
+ len = strlen(tmp->name) + 1;
+ res = copy_to_user(buf, tmp->name, len) ? -EFAULT : 0;
+ put_filesystem(tmp);
+ return res;
+}
+
+static int fs_maxindex(void)
+{
+ struct file_system_type * tmp;
+ int index;
+
+ read_lock(&file_systems_lock);
+ for (tmp = file_systems, index = 0 ; tmp ; tmp = tmp->next, index++)
+ ;
+ read_unlock(&file_systems_lock);
+ return index;
+}
+
+/*
+ * Whee.. Weird sysv syscall.
+ */
+asmlinkage long sys_sysfs(int option, unsigned long arg1, unsigned long arg2)
+{
+ int retval = -EINVAL;
+
+ switch (option) {
+ case 1:
+ retval = fs_index((const char *) arg1);
+ break;
+
+ case 2:
+ retval = fs_name(arg1, (char *) arg2);
+ break;
+
+ case 3:
+ retval = fs_maxindex();
+ break;
+ }
+ return retval;
+}
+
+int get_filesystem_list(char * buf)
+{
+ int len = 0;
+ struct file_system_type * tmp;
+
+ read_lock(&file_systems_lock);
+ tmp = file_systems;
+ while (tmp && len < PAGE_SIZE - 80) {
+ len += sprintf(buf+len, "%s\t%s\n",
+ (tmp->fs_flags & FS_REQUIRES_DEV) ? "" : "nodev",
+ tmp->name);
+ tmp = tmp->next;
+ }
+ read_unlock(&file_systems_lock);
+ return len;
+}
+
+struct file_system_type *get_fs_type(const char *name)
+{
+ struct file_system_type *fs;
+
+ read_lock(&file_systems_lock);
+ fs = *(find_filesystem(name));
+ if (fs && !try_inc_mod_count(fs->owner))
+ fs = NULL;
+ read_unlock(&file_systems_lock);
+ if (!fs && (request_module(name) == 0)) {
+ read_lock(&file_systems_lock);
+ fs = *(find_filesystem(name));
+ if (fs && !try_inc_mod_count(fs->owner))
+ fs = NULL;
+ read_unlock(&file_systems_lock);
+ }
+ return fs;
+}
+
+/**
+ * alloc_super - create new superblock
+ *
+ * Allocates and initializes a new &struct super_block. alloc_super()
+ * returns a pointer new superblock or %NULL if allocation had failed.
+ */
+static struct super_block *alloc_super(void)
+{
+ static struct super_operations empty_sops = {};
+ struct super_block *s = kmalloc(sizeof(struct super_block), GFP_USER);
+ if (s) {
+ memset(s, 0, sizeof(struct super_block));
+ INIT_LIST_HEAD(&s->s_dirty);
+ INIT_LIST_HEAD(&s->s_locked_inodes);
+ INIT_LIST_HEAD(&s->s_files);
+ INIT_LIST_HEAD(&s->s_instances);
+ init_rwsem(&s->s_umount);
+ sema_init(&s->s_lock, 1);
+ down_write(&s->s_umount);
+ s->s_count = S_BIAS;
+ atomic_set(&s->s_active, 1);
+ sema_init(&s->s_vfs_rename_sem,1);
+ sema_init(&s->s_nfsd_free_path_sem,1);
+ sema_init(&s->s_dquot.dqio_sem, 1);
+ sema_init(&s->s_dquot.dqoff_sem, 1);
+ s->s_maxbytes = MAX_NON_LFS;
+ s->s_op = &empty_sops;
+ s->dq_op = sb_dquot_ops;
+ s->s_qcop = sb_quotactl_ops;
+ }
+ return s;
+}
+
+/**
+ * destroy_super - frees a superblock
+ * @s: superblock to free
+ *
+ * Frees a superblock.
+ */
+static inline void destroy_super(struct super_block *s)
+{
+ kfree(s);
+}
+
+/* Superblock refcounting */
+
+/**
+ * deactivate_super - turn an active reference into temporary
+ * @s: superblock to deactivate
+ *
+ * Turns an active reference into temporary one. Returns 0 if there are
+ * other active references, 1 if we had deactivated the last one.
+ */
+static inline int deactivate_super(struct super_block *s)
+{
+ if (!atomic_dec_and_lock(&s->s_active, &sb_lock))
+ return 0;
+ s->s_count -= S_BIAS-1;
+ spin_unlock(&sb_lock);
+ return 1;
+}
+
+/**
+ * put_super - drop a temporary reference to superblock
+ * @s: superblock in question
+ *
+ * Drops a temporary reference, frees superblock if there's no
+ * references left.
+ */
+static inline void put_super(struct super_block *s)
+{
+ spin_lock(&sb_lock);
+ if (!--s->s_count)
+ destroy_super(s);
+ spin_unlock(&sb_lock);
+}
+
+/**
+ * grab_super - acquire an active reference
+ * @s - reference we are trying to make active
+ *
+ * Tries to acquire an active reference. grab_super() is used when we
+ * had just found a superblock in super_blocks or fs_type->fs_supers
+ * and want to turn it into a full-blown active reference. grab_super()
+ * is called with sb_lock held and drops it. Returns 1 in case of
+ * success, 0 if we had failed (superblock contents was already dead or
+ * dying when grab_super() had been called).
+ */
+static int grab_super(struct super_block *s)
+{
+ s->s_count++;
+ spin_unlock(&sb_lock);
+ down_write(&s->s_umount);
+ if (s->s_root) {
+ spin_lock(&sb_lock);
+ if (s->s_count > S_BIAS) {
+ atomic_inc(&s->s_active);
+ s->s_count--;
+ spin_unlock(&sb_lock);
+ return 1;
+ }
+ spin_unlock(&sb_lock);
+ }
+ up_write(&s->s_umount);
+ put_super(s);
+ return 0;
+}
+
+/**
+ * insert_super - put superblock on the lists
+ * @s: superblock in question
+ * @type: filesystem type it will belong to
+ *
+ * Associates superblock with fs type and puts it on per-type and global
+ * superblocks' lists. Should be called with sb_lock held; drops it.
+ */
+static void insert_super(struct super_block *s, struct file_system_type *type)
+{
+ s->s_type = type;
+ list_add(&s->s_list, super_blocks.prev);
+ list_add(&s->s_instances, &type->fs_supers);
+ spin_unlock(&sb_lock);
+ get_filesystem(type);
+}
+
+static void put_anon_dev(kdev_t dev);
+
+/**
+ * remove_super - makes superblock unreachable
+ * @s: superblock in question
+ *
+ * Removes superblock from the lists, unlocks it, drop the reference
+ * and releases the hosting device. @s should have no active
+ * references by that time and after remove_super() it's essentially
+ * in rundown mode - all remaining references are temporary, no new
+ * reference of any sort are going to appear and all holders of
+ * temporary ones will eventually drop them. At that point superblock
+ * itself will be destroyed; all its contents is already gone.
+ */
+static void remove_super(struct super_block *s)
+{
+ kdev_t dev = s->s_dev;
+ struct block_device *bdev = s->s_bdev;
+ struct file_system_type *fs = s->s_type;
+
+ spin_lock(&sb_lock);
+ list_del(&s->s_list);
+ list_del(&s->s_instances);
+ spin_unlock(&sb_lock);
+ up_write(&s->s_umount);
+ put_super(s);
+ put_filesystem(fs);
+ if (bdev)
+ blkdev_put(bdev, BDEV_FS);
+ else
+ put_anon_dev(dev);
+}
+
+struct vfsmount *alloc_vfsmnt(char *name);
+void free_vfsmnt(struct vfsmount *mnt);
+
+static inline struct super_block * find_super(kdev_t dev)
+{
+ struct list_head *p;
+
+ list_for_each(p, &super_blocks) {
+ struct super_block * s = sb_entry(p);
+ if (s->s_dev == dev) {
+ s->s_count++;
+ return s;
+ }
+ }
+ return NULL;
+}
+
+void drop_super(struct super_block *sb)
+{
+ up_read(&sb->s_umount);
+ put_super(sb);
+}
+
+static inline void write_super(struct super_block *sb)
+{
+ lock_super(sb);
+ if (sb->s_root && sb->s_dirt)
+ if (sb->s_op && sb->s_op->write_super)
+ sb->s_op->write_super(sb);
+ unlock_super(sb);
+}
+
+/*
+ * Note: check the dirty flag before waiting, so we don't
+ * hold up the sync while mounting a device. (The newly
+ * mounted device won't need syncing.)
+ */
+void sync_supers(kdev_t dev, int wait)
+{
+ struct super_block * sb;
+
+ if (dev) {
+ sb = get_super(dev);
+ if (sb) {
+ if (sb->s_dirt)
+ write_super(sb);
+ if (wait && sb->s_op && sb->s_op->sync_fs)
+ sb->s_op->sync_fs(sb);
+ drop_super(sb);
+ }
+ return;
+ }
+restart:
+ spin_lock(&sb_lock);
+ sb = sb_entry(super_blocks.next);
+ while (sb != sb_entry(&super_blocks))
+ if (sb->s_dirt) {
+ sb->s_count++;
+ spin_unlock(&sb_lock);
+ down_read(&sb->s_umount);
+ write_super(sb);
+ if (wait && sb->s_root && sb->s_op && sb->s_op->sync_fs)
+ sb->s_op->sync_fs(sb);
+ drop_super(sb);
+ goto restart;
+ } else
+ sb = sb_entry(sb->s_list.next);
+ spin_unlock(&sb_lock);
+}
+
+/**
+ * get_super - get the superblock of a device
+ * @dev: device to get the superblock for
+ *
+ * Scans the superblock list and finds the superblock of the file system
+ * mounted on the device given. %NULL is returned if no match is found.
+ */
+
+struct super_block * get_super(kdev_t dev)
+{
+ struct super_block * s;
+
+ if (!dev)
+ return NULL;
+restart:
+ spin_lock(&sb_lock);
+ s = find_super(dev);
+ if (s) {
+ spin_unlock(&sb_lock);
+ down_read(&s->s_umount);
+ if (s->s_root)
+ return s;
+ drop_super(s);
+ goto restart;
+ }
+ spin_unlock(&sb_lock);
+ return NULL;
+}
+
+asmlinkage long sys_ustat(dev_t dev, struct ustat * ubuf)
+{
+ struct super_block *s;
+ struct ustat tmp;
+ struct statfs sbuf;
+ int err = -EINVAL;
+
+ s = get_super(to_kdev_t(dev));
+ if (s == NULL)
+ goto out;
+ err = vfs_statfs(s, &sbuf);
+ drop_super(s);
+ if (err)
+ goto out;
+
+ memset(&tmp,0,sizeof(struct ustat));
+ tmp.f_tfree = sbuf.f_bfree;
+ tmp.f_tinode = sbuf.f_ffree;
+
+ err = copy_to_user(ubuf,&tmp,sizeof(struct ustat)) ? -EFAULT : 0;
+out:
+ return err;
+}
+
+/**
+ * do_remount_sb - asks filesystem to change mount options.
+ * @sb: superblock in question
+ * @flags: numeric part of options
+ * @data: the rest of options
+ *
+ * Alters the mount options of a mounted file system.
+ */
+int do_remount_sb(struct super_block *sb, int flags, void *data)
+{
+ int retval;
+
+ if (!(flags & MS_RDONLY) && sb->s_dev && is_read_only(sb->s_dev))
+ return -EACCES;
+ /*flags |= MS_RDONLY;*/
+ if (flags & MS_RDONLY)
+ acct_auto_close(sb->s_dev);
+ shrink_dcache_sb(sb);
+ fsync_super(sb);
+ /* If we are remounting RDONLY, make sure there are no rw files open */
+ if ((flags & MS_RDONLY) && !(sb->s_flags & MS_RDONLY))
+ if (!fs_may_remount_ro(sb))
+ return -EBUSY;
+ if (sb->s_op && sb->s_op->remount_fs) {
+ lock_super(sb);
+ retval = sb->s_op->remount_fs(sb, &flags, data);
+ unlock_super(sb);
+ if (retval)
+ return retval;
+ }
+ sb->s_flags = (sb->s_flags & ~MS_RMT_MASK) | (flags & MS_RMT_MASK);
+ return 0;
+}
+
+/*
+ * Unnamed block devices are dummy devices used by virtual
+ * filesystems which don't use real block-devices. -- jrs
+ */
+
+enum {Max_anon = 256};
+static unsigned long unnamed_dev_in_use[Max_anon/(8*sizeof(unsigned long))];
+static spinlock_t unnamed_dev_lock = SPIN_LOCK_UNLOCKED;/* protects the above */
+
+/**
+ * put_anon_dev - release anonymous device number.
+ * @dev: device in question
+ */
+static void put_anon_dev(kdev_t dev)
+{
+ spin_lock(&unnamed_dev_lock);
+ clear_bit(MINOR(dev), unnamed_dev_in_use);
+ spin_unlock(&unnamed_dev_lock);
+}
+
+/**
+ * get_anon_super - allocate a superblock for non-device fs
+ * @type: filesystem type
+ * @compare: check if existing superblock is what we want
+ * @data: argument for @compare.
+ *
+ * get_anon_super is a helper for non-blockdevice filesystems.
+ * It either finds and returns one of the superblocks of given type
+ * (if it can find one that would satisfy caller) or creates a new
+ * one. In the either case we return an active reference to superblock
+ * with ->s_umount locked. If superblock is new it gets a new
+ * anonymous device allocated for it and is inserted into lists -
+ * other initialization is left to caller.
+ *
+ * Rather than duplicating all that logics every time when
+ * we want something that doesn't fit "nodev" and "single" we pull
+ * the relevant code into common helper and let get_sb_...() call
+ * it.
+ *
+ * NB: get_sb_...() is going to become an fs type method, with
+ * current ->read_super() becoming a callback used by common instances.
+ */
+struct super_block *get_anon_super(struct file_system_type *type,
+ int (*compare)(struct super_block *,void *), void *data)
+{
+ struct super_block *s = alloc_super();
+ kdev_t dev;
+ struct list_head *p;
+
+ if (!s)
+ return ERR_PTR(-ENOMEM);
+
+retry:
+ spin_lock(&sb_lock);
+ if (compare) list_for_each(p, &type->fs_supers) {
+ struct super_block *old;
+ old = list_entry(p, struct super_block, s_instances);
+ if (!compare(old, data))
+ continue;
+ if (!grab_super(old))
+ goto retry;
+ destroy_super(s);
+ return old;
+ }
+
+ spin_lock(&unnamed_dev_lock);
+ dev = find_first_zero_bit(unnamed_dev_in_use, Max_anon);
+ if (dev == Max_anon) {
+ spin_unlock(&unnamed_dev_lock);
+ spin_unlock(&sb_lock);
+ destroy_super(s);
+ return ERR_PTR(-EMFILE);
+ }
+ set_bit(dev, unnamed_dev_in_use);
+ spin_unlock(&unnamed_dev_lock);
+
+ s->s_dev = dev;
+ insert_super(s, type);
+ return s;
+}
+
+static struct super_block *get_sb_bdev(struct file_system_type *fs_type,
+ int flags, char *dev_name, void * data)
+{
+ struct inode *inode;
+ struct block_device *bdev;
+ struct block_device_operations *bdops;
+ devfs_handle_t de;
+ struct super_block * s;
+ struct nameidata nd;
+ struct list_head *p;
+ kdev_t dev;
+ int error = 0;
+ mode_t mode = FMODE_READ; /* we always need it ;-) */
+
+ /* What device it is? */
+ if (!dev_name || !*dev_name)
+ return ERR_PTR(-EINVAL);
+ error = path_lookup(dev_name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, &nd);
+ if (error)
+ return ERR_PTR(error);
+ inode = nd.dentry->d_inode;
+ error = -ENOTBLK;
+ if (!S_ISBLK(inode->i_mode))
+ goto out;
+ error = -EACCES;
+ if (nd.mnt->mnt_flags & MNT_NODEV)
+ goto out;
+ bd_acquire(inode);
+ bdev = inode->i_bdev;
+ de = devfs_get_handle_from_inode (inode);
+ bdops = devfs_get_ops (de); /* Increments module use count */
+ if (bdops) bdev->bd_op = bdops;
+ /* Done with lookups, semaphore down */
+ dev = to_kdev_t(bdev->bd_dev);
+ if (!(flags & MS_RDONLY))
+ mode |= FMODE_WRITE;
+ error = blkdev_get(bdev, mode, 0, BDEV_FS);
+ devfs_put_ops (de); /* Decrement module use count now we're safe */
+ if (error)
+ goto out;
+ check_disk_change(dev);
+ error = -EACCES;
+ if (!(flags & MS_RDONLY) && is_read_only(dev))
+ goto out1;
+
+ error = -ENOMEM;
+ s = alloc_super();
+ if (!s)
+ goto out1;
+
+ error = -EBUSY;
+restart:
+ spin_lock(&sb_lock);
+
+ list_for_each(p, &super_blocks) {
+ struct super_block *old = sb_entry(p);
+ if (old->s_dev != dev)
+ continue;
+ if (old->s_type != fs_type ||
+ ((flags ^ old->s_flags) & MS_RDONLY)) {
+ spin_unlock(&sb_lock);
+ destroy_super(s);
+ goto out1;
+ }
+ if (!grab_super(old))
+ goto restart;
+ destroy_super(s);
+ blkdev_put(bdev, BDEV_FS);
+ path_release(&nd);
+ return old;
+ }
+ s->s_dev = dev;
+ s->s_bdev = bdev;
+ s->s_flags = flags;
+ insert_super(s, fs_type);
+ if (!fs_type->read_super(s, data, flags & MS_VERBOSE ? 1 : 0))
+ goto Einval;
+ s->s_flags |= MS_ACTIVE;
+ path_release(&nd);
+ return s;
+
+Einval:
+ deactivate_super(s);
+ remove_super(s);
+ error = -EINVAL;
+ goto out;
+out1:
+ blkdev_put(bdev, BDEV_FS);
+out:
+ path_release(&nd);
+ return ERR_PTR(error);
+}
+
+static struct super_block *get_sb_nodev(struct file_system_type *fs_type,
+ int flags, char *dev_name, void *data)
+{
+ struct super_block *s = get_anon_super(fs_type, NULL, NULL);
+
+ if (IS_ERR(s))
+ return s;
+
+ s->s_flags = flags;
+ if (!fs_type->read_super(s, data, flags & MS_VERBOSE ? 1 : 0)) {
+ deactivate_super(s);
+ remove_super(s);
+ return ERR_PTR(-EINVAL);
+ }
+ s->s_flags |= MS_ACTIVE;
+ return s;
+}
+
+static int compare_single(struct super_block *s, void *p)
+{
+ return 1;
+}
+
+static struct super_block *get_sb_single(struct file_system_type *fs_type,
+ int flags, char *dev_name, void *data)
+{
+ struct super_block *s = get_anon_super(fs_type, compare_single, NULL);
+
+ if (IS_ERR(s))
+ return s;
+ if (!s->s_root) {
+ s->s_flags = flags;
+ if (!fs_type->read_super(s, data, flags & MS_VERBOSE ? 1 : 0)) {
+ deactivate_super(s);
+ remove_super(s);
+ return ERR_PTR(-EINVAL);
+ }
+ s->s_flags |= MS_ACTIVE;
+ }
+ do_remount_sb(s, flags, data);
+ return s;
+}
+
+struct vfsmount *
+do_kern_mount(const char *fstype, int flags, char *name, void *data)
+{
+ struct file_system_type *type = get_fs_type(fstype);
+ struct super_block *sb = ERR_PTR(-ENOMEM);
+ struct vfsmount *mnt;
+
+ if (!type)
+ return ERR_PTR(-ENODEV);
+
+ mnt = alloc_vfsmnt(name);
+ if (!mnt)
+ goto out;
+ if (type->fs_flags & FS_REQUIRES_DEV)
+ sb = get_sb_bdev(type, flags, name, data);
+ else if (type->fs_flags & FS_SINGLE)
+ sb = get_sb_single(type, flags, name, data);
+ else
+ sb = get_sb_nodev(type, flags, name, data);
+ if (IS_ERR(sb))
+ goto out_mnt;
+ if (type->fs_flags & FS_NOMOUNT)
+ sb->s_flags |= MS_NOUSER;
+ mnt->mnt_sb = sb;
+ mnt->mnt_root = dget(sb->s_root);
+ mnt->mnt_mountpoint = sb->s_root;
+ mnt->mnt_parent = mnt;
+ up_write(&sb->s_umount);
+ put_filesystem(type);
+ return mnt;
+out_mnt:
+ free_vfsmnt(mnt);
+out:
+ put_filesystem(type);
+ return (struct vfsmount *)sb;
+}
+
+void kill_super(struct super_block *sb)
+{
+ struct dentry *root = sb->s_root;
+ struct file_system_type *fs = sb->s_type;
+ struct super_operations *sop = sb->s_op;
+
+ if (!deactivate_super(sb))
+ return;
+
+ down_write(&sb->s_umount);
+ sb->s_root = NULL;
+ /* Need to clean after the sucker */
+ if (fs->fs_flags & FS_LITTER)
+ d_genocide(root);
+ shrink_dcache_parent(root);
+ dput(root);
+ fsync_super(sb);
+ lock_super(sb);
+ lock_kernel();
+ sb->s_flags &= ~MS_ACTIVE;
+ invalidate_inodes(sb); /* bad name - it should be evict_inodes() */
+ if (sop) {
+ if (sop->write_super && sb->s_dirt)
+ sop->write_super(sb);
+ if (sop->put_super)
+ sop->put_super(sb);
+ }
+
+ /* Forget any remaining inodes */
+ if (invalidate_inodes(sb)) {
+ printk(KERN_ERR "VFS: Busy inodes after unmount. "
+ "Self-destruct in 5 seconds. Have a nice day...\n");
+ }
+
+ unlock_kernel();
+ unlock_super(sb);
+ remove_super(sb);
+}
+
+struct vfsmount *kern_mount(struct file_system_type *type)
+{
+ return do_kern_mount(type->name, 0, (char *)type->name, NULL);
+}
diff --git a/fs/sysv/CHANGES b/fs/sysv/CHANGES
new file mode 100644
index 00000000000000..3cbcd7b9da18bd
--- /dev/null
+++ b/fs/sysv/CHANGES
@@ -0,0 +1,60 @@
+Mon, 15 Dec 1997 Krzysztof G. Baranowski <kgb@manjak.knm.org.pl>
+ * namei.c: struct sysv_dir_inode_operations updated to use dentries.
+
+Fri, 23 Jan 1998 Krzysztof G. Baranowski <kgb@manjak.knm.org.pl>
+ * inode.c: corrected 1 track offset setting (in sb->sv_block_base).
+ Originally it was overridden (by setting to zero)
+ in detected_[xenix,sysv4,sysv2,coherent]. Thanks
+ to Andrzej Krzysztofowicz <ankry@mif.pg.gda.pl>
+ for identifying the problem.
+
+Tue, 27 Jan 1998 Krzysztof G. Baranowski <kgb@manjak.knm.org.pl>
+ * inode.c: added 2048-byte block support to SystemV FS.
+ Merged detected_bs[512,1024,2048]() into one function:
+ void detected_bs (u_char type, struct super_block *sb).
+ Thanks to Andrzej Krzysztofowicz <ankry@mif.pg.gda.pl>
+ for the patch.
+
+Wed, 4 Feb 1998 Krzysztof G. Baranowski <kgb@manjak.knm.org.pl>
+ * namei.c: removed static subdir(); is_subdir() from dcache.c
+ is used instead. Cosmetic changes.
+
+Thu, 3 Dec 1998 Al Viro (viro@math.psu.edu)
+ * namei.c (sysv_rmdir):
+ Bugectomy: old check for victim being busy
+ (inode->i_count) wasn't replaced (with checking
+ dentry->d_count) and escaped Linus in the last round
+ of changes. Shot and buried.
+
+Wed, 9 Dec 1998 AV
+ * namei.c (do_sysv_rename):
+ Fixed incorrect check for other owners + race.
+ Removed checks that went to VFS.
+ * namei.c (sysv_unlink):
+ Removed checks that went to VFS.
+
+Thu, 10 Dec 1998 AV
+ * namei.c (do_mknod):
+ Removed dead code - mknod is never asked to
+ create a symlink or directory. Incidentially,
+ it wouldn't do it right if it would be called.
+
+Sat, 26 Dec 1998 KGB
+ * inode.c (detect_sysv4):
+ Added detection of expanded s_type field (0x10,
+ 0x20 and 0x30). Forced read-only access in this case.
+
+Sun, 21 Mar 1999 AV
+ * namei.c (sysv_link):
+ Fixed i_count usage that resulted in dcache corruption.
+ * inode.c:
+ Filled ->delete_inode() method with sysv_delete_inode().
+ sysv_put_inode() is gone, as it tried to do ->delete_
+ _inode()'s job.
+ * ialloc.c: (sysv_free_inode):
+ Fixed race.
+
+Sun, 30 Apr 1999 AV
+ * namei.c (sysv_mknod):
+ Removed dead code (S_IFREG case is now passed to
+ ->create() by VFS).
diff --git a/fs/sysv/ChangeLog b/fs/sysv/ChangeLog
new file mode 100644
index 00000000000000..931045ae37ca42
--- /dev/null
+++ b/fs/sysv/ChangeLog
@@ -0,0 +1,29 @@
+Thu Jan 10 2002 Andrew Morton <akpm@zip.com.au>
+
+ * dir_commit_chunk(): call writeout_one_page() as well as
+ waitfor_one_page() for IS_SYNC directories, so that we
+ actually do sync the directory.
+
+Sat Dec 15 2001 Christoph Hellwig <hch@infradead.org>
+
+ * inode.c (sysv_read_inode): Mark inode as bad in case of failure.
+ * super.c (complete_read_super): Check for bad root inode.
+
+Wed Nov 21 2001 Andrew Morton <andrewm@uow.edu.au>
+
+ * file.c (sysv_sync_file): Call fsync_inode_data_buffers.
+
+Fri Oct 26 2001 Christoph Hellwig <hch@infradead.org>
+
+ * dir.c, ialloc.c, namei.c, include/linux/sysv_fs_i.h:
+ Implement per-Inode lookup offset cache.
+ Modelled after Ted's ext2 patch.
+
+Fri Oct 26 2001 Christoph Hellwig <hch@infradead.org>
+
+ * inode.c, super.c, include/linux/sysv_fs.h,
+ include/linux/sysv_fs_sb.h:
+ Remove symlink faking. Noone really wants to use these as
+ linux filesystems and native OSes don't support it anyway.
+
+
diff --git a/fs/sysv/INTRO b/fs/sysv/INTRO
new file mode 100644
index 00000000000000..de4e4d17cac6f7
--- /dev/null
+++ b/fs/sysv/INTRO
@@ -0,0 +1,182 @@
+This is the implementation of the SystemV/Coherent filesystem for Linux.
+It grew out of separate filesystem implementations
+
+ Xenix FS Doug Evans <dje@cygnus.com> June 1992
+ SystemV FS Paul B. Monday <pmonday@eecs.wsu.edu> March-June 1993
+ Coherent FS B. Haible <haible@ma2s2.mathematik.uni-karlsruhe.de> June 1993
+
+and was merged together in July 1993.
+
+These filesystems are rather similar. Here is a comparison with Minix FS:
+
+* Linux fdisk reports on partitions
+ - Minix FS 0x81 Linux/Minix
+ - Xenix FS ??
+ - SystemV FS ??
+ - Coherent FS 0x08 AIX bootable
+
+* Size of a block or zone (data allocation unit on disk)
+ - Minix FS 1024
+ - Xenix FS 1024 (also 512 ??)
+ - SystemV FS 1024 (also 512 and 2048)
+ - Coherent FS 512
+
+* General layout: all have one boot block, one super block and
+ separate areas for inodes and for directories/data.
+ On SystemV Release 2 FS (e.g. Microport) the first track is reserved and
+ all the block numbers (including the super block) are offset by one track.
+
+* Byte ordering of "short" (16 bit entities) on disk:
+ - Minix FS little endian 0 1
+ - Xenix FS little endian 0 1
+ - SystemV FS little endian 0 1
+ - Coherent FS little endian 0 1
+ Of course, this affects only the file system, not the data of files on it!
+
+* Byte ordering of "long" (32 bit entities) on disk:
+ - Minix FS little endian 0 1 2 3
+ - Xenix FS little endian 0 1 2 3
+ - SystemV FS little endian 0 1 2 3
+ - Coherent FS PDP-11 2 3 0 1
+ Of course, this affects only the file system, not the data of files on it!
+
+* Inode on disk: "short", 0 means non-existent, the root dir ino is:
+ - Minix FS 1
+ - Xenix FS, SystemV FS, Coherent FS 2
+
+* Maximum number of hard links to a file:
+ - Minix FS 250
+ - Xenix FS ??
+ - SystemV FS ??
+ - Coherent FS >=10000
+
+* Free inode management:
+ - Minix FS a bitmap
+ - Xenix FS, SystemV FS, Coherent FS
+ There is a cache of a certain number of free inodes in the super-block.
+ When it is exhausted, new free inodes are found using a linear search.
+
+* Free block management:
+ - Minix FS a bitmap
+ - Xenix FS, SystemV FS, Coherent FS
+ Free blocks are organized in a "free list". Maybe a misleading term,
+ since it is not true that every free block contains a pointer to
+ the next free block. Rather, the free blocks are organized in chunks
+ of limited size, and every now and then a free block contains pointers
+ to the free blocks pertaining to the next chunk; the first of these
+ contains pointers and so on. The list terminates with a "block number"
+ 0 on Xenix FS and SystemV FS, with a block zeroed out on Coherent FS.
+
+* Super-block location:
+ - Minix FS block 1 = bytes 1024..2047
+ - Xenix FS block 1 = bytes 1024..2047
+ - SystemV FS bytes 512..1023
+ - Coherent FS block 1 = bytes 512..1023
+
+* Super-block layout:
+ - Minix FS
+ unsigned short s_ninodes;
+ unsigned short s_nzones;
+ unsigned short s_imap_blocks;
+ unsigned short s_zmap_blocks;
+ unsigned short s_firstdatazone;
+ unsigned short s_log_zone_size;
+ unsigned long s_max_size;
+ unsigned short s_magic;
+ - Xenix FS, SystemV FS, Coherent FS
+ unsigned short s_firstdatazone;
+ unsigned long s_nzones;
+ unsigned short s_fzone_count;
+ unsigned long s_fzones[NICFREE];
+ unsigned short s_finode_count;
+ unsigned short s_finodes[NICINOD];
+ char s_flock;
+ char s_ilock;
+ char s_modified;
+ char s_rdonly;
+ unsigned long s_time;
+ short s_dinfo[4]; -- SystemV FS only
+ unsigned long s_free_zones;
+ unsigned short s_free_inodes;
+ short s_dinfo[4]; -- Xenix FS only
+ unsigned short s_interleave_m,s_interleave_n; -- Coherent FS only
+ char s_fname[6];
+ char s_fpack[6];
+ then they differ considerably:
+ Xenix FS
+ char s_clean;
+ char s_fill[371];
+ long s_magic;
+ long s_type;
+ SystemV FS
+ long s_fill[12 or 14];
+ long s_state;
+ long s_magic;
+ long s_type;
+ Coherent FS
+ unsigned long s_unique;
+ Note that Coherent FS has no magic.
+
+* Inode layout:
+ - Minix FS
+ unsigned short i_mode;
+ unsigned short i_uid;
+ unsigned long i_size;
+ unsigned long i_time;
+ unsigned char i_gid;
+ unsigned char i_nlinks;
+ unsigned short i_zone[7+1+1];
+ - Xenix FS, SystemV FS, Coherent FS
+ unsigned short i_mode;
+ unsigned short i_nlink;
+ unsigned short i_uid;
+ unsigned short i_gid;
+ unsigned long i_size;
+ unsigned char i_zone[3*(10+1+1+1)];
+ unsigned long i_atime;
+ unsigned long i_mtime;
+ unsigned long i_ctime;
+
+* Regular file data blocks are organized as
+ - Minix FS
+ 7 direct blocks
+ 1 indirect block (pointers to blocks)
+ 1 double-indirect block (pointer to pointers to blocks)
+ - Xenix FS, SystemV FS, Coherent FS
+ 10 direct blocks
+ 1 indirect block (pointers to blocks)
+ 1 double-indirect block (pointer to pointers to blocks)
+ 1 triple-indirect block (pointer to pointers to pointers to blocks)
+
+* Inode size, inodes per block
+ - Minix FS 32 32
+ - Xenix FS 64 16
+ - SystemV FS 64 16
+ - Coherent FS 64 8
+
+* Directory entry on disk
+ - Minix FS
+ unsigned short inode;
+ char name[14/30];
+ - Xenix FS, SystemV FS, Coherent FS
+ unsigned short inode;
+ char name[14];
+
+* Dir entry size, dir entries per block
+ - Minix FS 16/32 64/32
+ - Xenix FS 16 64
+ - SystemV FS 16 64
+ - Coherent FS 16 32
+
+* How to implement symbolic links such that the host fsck doesn't scream:
+ - Minix FS normal
+ - Xenix FS kludge: as regular files with chmod 1000
+ - SystemV FS ??
+ - Coherent FS kludge: as regular files with chmod 1000
+
+
+Notation: We often speak of a "block" but mean a zone (the allocation unit)
+and not the disk driver's notion of "block".
+
+
+Bruno Haible <haible@ma2s2.mathematik.uni-karlsruhe.de>
diff --git a/fs/sysv/Makefile b/fs/sysv/Makefile
new file mode 100644
index 00000000000000..4c574eda024218
--- /dev/null
+++ b/fs/sysv/Makefile
@@ -0,0 +1,16 @@
+#
+# Makefile for the Linux SystemV/Coherent filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile.
+
+O_TARGET := sysv.o
+
+obj-y := ialloc.o balloc.o inode.o itree.o file.o dir.o \
+ namei.o super.o symlink.o
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/sysv/balloc.c b/fs/sysv/balloc.c
new file mode 100644
index 00000000000000..2f3df11dd2079c
--- /dev/null
+++ b/fs/sysv/balloc.c
@@ -0,0 +1,235 @@
+/*
+ * linux/fs/sysv/balloc.c
+ *
+ * minix/bitmap.c
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * ext/freelists.c
+ * Copyright (C) 1992 Remy Card (card@masi.ibp.fr)
+ *
+ * xenix/alloc.c
+ * Copyright (C) 1992 Doug Evans
+ *
+ * coh/alloc.c
+ * Copyright (C) 1993 Pascal Haible, Bruno Haible
+ *
+ * sysv/balloc.c
+ * Copyright (C) 1993 Bruno Haible
+ *
+ * This file contains code for allocating/freeing blocks.
+ */
+
+#include <linux/fs.h>
+#include <linux/sysv_fs.h>
+#include <linux/locks.h>
+
+/* We don't trust the value of
+ sb->sv_sbd2->s_tfree = *sb->sv_free_blocks
+ but we nevertheless keep it up to date. */
+
+static inline u32 *get_chunk(struct super_block *sb, struct buffer_head *bh)
+{
+ char *bh_data = bh->b_data;
+
+ if (sb->sv_type == FSTYPE_SYSV4)
+ return (u32*)(bh_data+4);
+ else
+ return (u32*)(bh_data+2);
+}
+
+/* NOTE NOTE NOTE: nr is a block number _as_ _stored_ _on_ _disk_ */
+
+void sysv_free_block(struct super_block * sb, u32 nr)
+{
+ struct buffer_head * bh;
+ u32 *blocks = sb->sv_bcache;
+ unsigned count;
+ unsigned block = fs32_to_cpu(sb, nr);
+
+ /*
+ * This code does not work at all for AFS (it has a bitmap
+ * free list). As AFS is supposed to be read-only no one
+ * should call this for an AFS filesystem anyway...
+ */
+ if (sb->sv_type == FSTYPE_AFS)
+ return;
+
+ if (block < sb->sv_firstdatazone || block >= sb->sv_nzones) {
+ printk("sysv_free_block: trying to free block not in datazone\n");
+ return;
+ }
+
+ lock_super(sb);
+ count = fs16_to_cpu(sb, *sb->sv_bcache_count);
+
+ if (count > sb->sv_flc_size) {
+ printk("sysv_free_block: flc_count > flc_size\n");
+ unlock_super(sb);
+ return;
+ }
+ /* If the free list head in super-block is full, it is copied
+ * into this block being freed, ditto if it's completely empty
+ * (applies only on Coherent).
+ */
+ if (count == sb->sv_flc_size || count == 0) {
+ block += sb->sv_block_base;
+ bh = sb_getblk(sb, block);
+ if (!bh) {
+ printk("sysv_free_block: getblk() failed\n");
+ unlock_super(sb);
+ return;
+ }
+ memset(bh->b_data, 0, sb->s_blocksize);
+ *(u16*)bh->b_data = cpu_to_fs16(sb, count);
+ memcpy(get_chunk(sb,bh), blocks, count * sizeof(sysv_zone_t));
+ mark_buffer_dirty(bh);
+ mark_buffer_uptodate(bh, 1);
+ brelse(bh);
+ count = 0;
+ }
+ sb->sv_bcache[count++] = nr;
+
+ *sb->sv_bcache_count = cpu_to_fs16(sb, count);
+ fs32_add(sb, sb->sv_free_blocks, 1);
+ dirty_sb(sb);
+ unlock_super(sb);
+}
+
+u32 sysv_new_block(struct super_block * sb)
+{
+ unsigned int block;
+ u32 nr;
+ struct buffer_head * bh;
+ unsigned count;
+
+ lock_super(sb);
+ count = fs16_to_cpu(sb, *sb->sv_bcache_count);
+
+ if (count == 0) /* Applies only to Coherent FS */
+ goto Enospc;
+ nr = sb->sv_bcache[--count];
+ if (nr == 0) /* Applies only to Xenix FS, SystemV FS */
+ goto Enospc;
+
+ block = fs32_to_cpu(sb, nr);
+
+ *sb->sv_bcache_count = cpu_to_fs16(sb, count);
+
+ if (block < sb->sv_firstdatazone || block >= sb->sv_nzones) {
+ printk("sysv_new_block: new block %d is not in data zone\n",
+ block);
+ goto Enospc;
+ }
+
+ if (count == 0) { /* the last block continues the free list */
+ unsigned count;
+
+ block += sb->sv_block_base;
+ if (!(bh = sb_bread(sb, block))) {
+ printk("sysv_new_block: cannot read free-list block\n");
+ /* retry this same block next time */
+ *sb->sv_bcache_count = cpu_to_fs16(sb, 1);
+ goto Enospc;
+ }
+ count = fs16_to_cpu(sb, *(u16*)bh->b_data);
+ if (count > sb->sv_flc_size) {
+ printk("sysv_new_block: free-list block with >flc_size entries\n");
+ brelse(bh);
+ goto Enospc;
+ }
+ *sb->sv_bcache_count = cpu_to_fs16(sb, count);
+ memcpy(sb->sv_bcache, get_chunk(sb, bh),
+ count * sizeof(sysv_zone_t));
+ brelse(bh);
+ }
+ /* Now the free list head in the superblock is valid again. */
+ fs32_add(sb, sb->sv_free_blocks, -1);
+ dirty_sb(sb);
+ unlock_super(sb);
+ return nr;
+
+Enospc:
+ unlock_super(sb);
+ return 0;
+}
+
+unsigned long sysv_count_free_blocks(struct super_block * sb)
+{
+ int sb_count;
+ int count;
+ struct buffer_head * bh = NULL;
+ u32 *blocks;
+ unsigned block;
+ int n;
+
+ /*
+ * This code does not work at all for AFS (it has a bitmap
+ * free list). As AFS is supposed to be read-only we just
+ * lie and say it has no free block at all.
+ */
+ if (sb->sv_type == FSTYPE_AFS)
+ return 0;
+
+ lock_super(sb);
+ sb_count = fs32_to_cpu(sb, *sb->sv_free_blocks);
+
+ if (0)
+ goto trust_sb;
+
+ /* this causes a lot of disk traffic ... */
+ count = 0;
+ n = fs16_to_cpu(sb, *sb->sv_bcache_count);
+ blocks = sb->sv_bcache;
+ while (1) {
+ if (n > sb->sv_flc_size)
+ goto E2big;
+ block = 0;
+ while (n && (block = blocks[--n]) != 0)
+ count++;
+ if (block == 0)
+ break;
+
+ block = fs32_to_cpu(sb, block);
+ if (bh)
+ brelse(bh);
+
+ if (block < sb->sv_firstdatazone || block >= sb->sv_nzones)
+ goto Einval;
+ block += sb->sv_block_base;
+ bh = sb_bread(sb, block);
+ if (!bh)
+ goto Eio;
+ n = fs16_to_cpu(sb, *(u16*)bh->b_data);
+ blocks = get_chunk(sb, bh);
+ }
+ if (bh)
+ brelse(bh);
+ if (count != sb_count)
+ goto Ecount;
+done:
+ unlock_super(sb);
+ return count;
+
+Einval:
+ printk("sysv_count_free_blocks: new block %d is not in data zone\n",
+ block);
+ goto trust_sb;
+Eio:
+ printk("sysv_count_free_blocks: cannot read free-list block\n");
+ goto trust_sb;
+E2big:
+ printk("sysv_count_free_blocks: >flc_size entries in free-list block\n");
+ if (bh)
+ brelse(bh);
+trust_sb:
+ count = sb_count;
+ goto done;
+Ecount:
+ printk("sysv_count_free_blocks: free block count was %d, "
+ "correcting to %d\n", sb_count, count);
+ if (!(sb->s_flags & MS_RDONLY)) {
+ *sb->sv_free_blocks = cpu_to_fs32(sb, count);
+ dirty_sb(sb);
+ }
+ goto done;
+}
diff --git a/fs/sysv/dir.c b/fs/sysv/dir.c
new file mode 100644
index 00000000000000..2a826bcc0f227c
--- /dev/null
+++ b/fs/sysv/dir.c
@@ -0,0 +1,386 @@
+/*
+ * linux/fs/sysv/dir.c
+ *
+ * minix/dir.c
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * coh/dir.c
+ * Copyright (C) 1993 Pascal Haible, Bruno Haible
+ *
+ * sysv/dir.c
+ * Copyright (C) 1993 Bruno Haible
+ *
+ * SystemV/Coherent directory handling functions
+ */
+
+#include <linux/fs.h>
+#include <linux/sysv_fs.h>
+#include <linux/pagemap.h>
+
+static int sysv_readdir(struct file *, void *, filldir_t);
+
+struct file_operations sysv_dir_operations = {
+ read: generic_read_dir,
+ readdir: sysv_readdir,
+ fsync: sysv_sync_file,
+};
+
+static inline void dir_put_page(struct page *page)
+{
+ kunmap(page);
+ page_cache_release(page);
+}
+
+static inline unsigned long dir_pages(struct inode *inode)
+{
+ return (inode->i_size+PAGE_CACHE_SIZE-1)>>PAGE_CACHE_SHIFT;
+}
+
+static int dir_commit_chunk(struct page *page, unsigned from, unsigned to)
+{
+ struct inode *dir = (struct inode *)page->mapping->host;
+ int err = 0;
+
+ dir->i_version = ++event;
+ page->mapping->a_ops->commit_write(NULL, page, from, to);
+ if (IS_SYNC(dir)) {
+ int err2;
+ err = writeout_one_page(page);
+ err2 = waitfor_one_page(page);
+ if (err == 0)
+ err = err2;
+ }
+ return err;
+}
+
+static struct page * dir_get_page(struct inode *dir, unsigned long n)
+{
+ struct address_space *mapping = dir->i_mapping;
+ struct page *page = read_cache_page(mapping, n,
+ (filler_t*)mapping->a_ops->readpage, NULL);
+ if (!IS_ERR(page)) {
+ wait_on_page(page);
+ kmap(page);
+ if (!Page_Uptodate(page))
+ goto fail;
+ }
+ return page;
+
+fail:
+ dir_put_page(page);
+ return ERR_PTR(-EIO);
+}
+
+static int sysv_readdir(struct file * filp, void * dirent, filldir_t filldir)
+{
+ unsigned long pos = filp->f_pos;
+ struct inode *inode = filp->f_dentry->d_inode;
+ struct super_block *sb = inode->i_sb;
+ unsigned offset = pos & ~PAGE_CACHE_MASK;
+ unsigned long n = pos >> PAGE_CACHE_SHIFT;
+ unsigned long npages = dir_pages(inode);
+
+ pos = (pos + SYSV_DIRSIZE-1) & ~(SYSV_DIRSIZE-1);
+ if (pos >= inode->i_size)
+ goto done;
+
+ for ( ; n < npages; n++, offset = 0) {
+ char *kaddr, *limit;
+ struct sysv_dir_entry *de;
+ struct page *page = dir_get_page(inode, n);
+
+ if (IS_ERR(page))
+ continue;
+ kaddr = (char *)page_address(page);
+ de = (struct sysv_dir_entry *)(kaddr+offset);
+ limit = kaddr + PAGE_CACHE_SIZE - SYSV_DIRSIZE;
+ for ( ;(char*)de <= limit; de++) {
+ char *name = de->name;
+ int over;
+
+ if (!de->inode)
+ continue;
+
+ offset = (char *)de - kaddr;
+
+ over = filldir(dirent, name, strnlen(name,SYSV_NAMELEN),
+ (n<<PAGE_CACHE_SHIFT) | offset,
+ fs16_to_cpu(sb, de->inode), DT_UNKNOWN);
+ if (over) {
+ dir_put_page(page);
+ goto done;
+ }
+ }
+ dir_put_page(page);
+ }
+
+done:
+ filp->f_pos = (n << PAGE_CACHE_SHIFT) | offset;
+ filp->f_version = inode->i_version;
+ UPDATE_ATIME(inode);
+ return 0;
+}
+
+/* compare strings: name[0..len-1] (not zero-terminated) and
+ * buffer[0..] (filled with zeroes up to buffer[0..maxlen-1])
+ */
+static inline int namecompare(int len, int maxlen,
+ const char * name, const char * buffer)
+{
+ if (len < maxlen && buffer[len])
+ return 0;
+ return !memcmp(name, buffer, len);
+}
+
+/*
+ * sysv_find_entry()
+ *
+ * finds an entry in the specified directory with the wanted name. It
+ * returns the cache buffer in which the entry was found, and the entry
+ * itself (as a parameter - res_dir). It does NOT read the inode of the
+ * entry - you'll have to do that yourself if you want to.
+ */
+struct sysv_dir_entry *sysv_find_entry(struct dentry *dentry, struct page **res_page)
+{
+ const char * name = dentry->d_name.name;
+ int namelen = dentry->d_name.len;
+ struct inode * dir = dentry->d_parent->d_inode;
+ unsigned long start, n;
+ unsigned long npages = dir_pages(dir);
+ struct page *page = NULL;
+ struct sysv_dir_entry *de;
+
+ *res_page = NULL;
+
+ start = dir->u.sysv_i.i_dir_start_lookup;
+ if (start >= npages)
+ start = 0;
+ n = start;
+
+ do {
+ char *kaddr;
+ page = dir_get_page(dir, n);
+ if (!IS_ERR(page)) {
+ kaddr = (char*)page_address(page);
+ de = (struct sysv_dir_entry *) kaddr;
+ kaddr += PAGE_CACHE_SIZE - SYSV_DIRSIZE;
+ for ( ; (char *) de <= kaddr ; de++) {
+ if (!de->inode)
+ continue;
+ if (namecompare(namelen, SYSV_NAMELEN,
+ name, de->name))
+ goto found;
+ }
+ }
+ dir_put_page(page);
+
+ if (++n >= npages)
+ n = 0;
+ } while (n != start);
+
+ return NULL;
+
+found:
+ dir->u.sysv_i.i_dir_start_lookup = n;
+ *res_page = page;
+ return de;
+}
+
+int sysv_add_link(struct dentry *dentry, struct inode *inode)
+{
+ struct inode *dir = dentry->d_parent->d_inode;
+ const char * name = dentry->d_name.name;
+ int namelen = dentry->d_name.len;
+ struct page *page = NULL;
+ struct sysv_dir_entry * de;
+ unsigned long npages = dir_pages(dir);
+ unsigned long n;
+ char *kaddr;
+ unsigned from, to;
+ int err;
+
+ /* We take care of directory expansion in the same loop */
+ for (n = 0; n <= npages; n++) {
+ page = dir_get_page(dir, n);
+ err = PTR_ERR(page);
+ if (IS_ERR(page))
+ goto out;
+ kaddr = (char*)page_address(page);
+ de = (struct sysv_dir_entry *)kaddr;
+ kaddr += PAGE_CACHE_SIZE - SYSV_DIRSIZE;
+ while ((char *)de <= kaddr) {
+ if (!de->inode)
+ goto got_it;
+ err = -EEXIST;
+ if (namecompare(namelen, SYSV_NAMELEN, name, de->name))
+ goto out_page;
+ de++;
+ }
+ dir_put_page(page);
+ }
+ BUG();
+ return -EINVAL;
+
+got_it:
+ from = (char*)de - (char*)page_address(page);
+ to = from + SYSV_DIRSIZE;
+ lock_page(page);
+ err = page->mapping->a_ops->prepare_write(NULL, page, from, to);
+ if (err)
+ goto out_unlock;
+ memcpy (de->name, name, namelen);
+ memset (de->name + namelen, 0, SYSV_DIRSIZE - namelen - 2);
+ de->inode = cpu_to_fs16(inode->i_sb, inode->i_ino);
+ err = dir_commit_chunk(page, from, to);
+ dir->i_mtime = dir->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(dir);
+out_unlock:
+ UnlockPage(page);
+out_page:
+ dir_put_page(page);
+out:
+ return err;
+}
+
+int sysv_delete_entry(struct sysv_dir_entry *de, struct page *page)
+{
+ struct address_space *mapping = page->mapping;
+ struct inode *inode = (struct inode*)mapping->host;
+ char *kaddr = (char*)page_address(page);
+ unsigned from = (char*)de - kaddr;
+ unsigned to = from + SYSV_DIRSIZE;
+ int err;
+
+ lock_page(page);
+ err = mapping->a_ops->prepare_write(NULL, page, from, to);
+ if (err)
+ BUG();
+ de->inode = 0;
+ err = dir_commit_chunk(page, from, to);
+ UnlockPage(page);
+ dir_put_page(page);
+ inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+ return err;
+}
+
+int sysv_make_empty(struct inode *inode, struct inode *dir)
+{
+ struct address_space *mapping = inode->i_mapping;
+ struct page *page = grab_cache_page(mapping, 0);
+ struct sysv_dir_entry * de;
+ char *base;
+ int err;
+
+ if (!page)
+ return -ENOMEM;
+ err = mapping->a_ops->prepare_write(NULL, page, 0, 2 * SYSV_DIRSIZE);
+ if (err)
+ goto fail;
+
+ base = (char*)page_address(page);
+ memset(base, 0, PAGE_CACHE_SIZE);
+
+ de = (struct sysv_dir_entry *) base;
+ de->inode = cpu_to_fs16(inode->i_sb, inode->i_ino);
+ strcpy(de->name,".");
+ de++;
+ de->inode = cpu_to_fs16(inode->i_sb, dir->i_ino);
+ strcpy(de->name,"..");
+
+ err = dir_commit_chunk(page, 0, 2 * SYSV_DIRSIZE);
+fail:
+ UnlockPage(page);
+ page_cache_release(page);
+ return err;
+}
+
+/*
+ * routine to check that the specified directory is empty (for rmdir)
+ */
+int sysv_empty_dir(struct inode * inode)
+{
+ struct super_block *sb = inode->i_sb;
+ struct page *page = NULL;
+ unsigned long i, npages = dir_pages(inode);
+
+ for (i = 0; i < npages; i++) {
+ char *kaddr;
+ struct sysv_dir_entry * de;
+ page = dir_get_page(inode, i);
+
+ if (IS_ERR(page))
+ continue;
+
+ kaddr = (char *)page_address(page);
+ de = (struct sysv_dir_entry *)kaddr;
+ kaddr += PAGE_CACHE_SIZE-SYSV_DIRSIZE;
+
+ for ( ;(char *)de <= kaddr; de++) {
+ if (!de->inode)
+ continue;
+ /* check for . and .. */
+ if (de->name[0] != '.')
+ goto not_empty;
+ if (!de->name[1]) {
+ if (de->inode == cpu_to_fs16(sb, inode->i_ino))
+ continue;
+ goto not_empty;
+ }
+ if (de->name[1] != '.' || de->name[2])
+ goto not_empty;
+ }
+ dir_put_page(page);
+ }
+ return 1;
+
+not_empty:
+ dir_put_page(page);
+ return 0;
+}
+
+/* Releases the page */
+void sysv_set_link(struct sysv_dir_entry *de, struct page *page,
+ struct inode *inode)
+{
+ struct inode *dir = (struct inode*)page->mapping->host;
+ unsigned from = (char *)de-(char*)page_address(page);
+ unsigned to = from + SYSV_DIRSIZE;
+ int err;
+
+ lock_page(page);
+ err = page->mapping->a_ops->prepare_write(NULL, page, from, to);
+ if (err)
+ BUG();
+ de->inode = cpu_to_fs16(inode->i_sb, inode->i_ino);
+ err = dir_commit_chunk(page, from, to);
+ UnlockPage(page);
+ dir_put_page(page);
+ dir->i_mtime = dir->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(dir);
+}
+
+struct sysv_dir_entry * sysv_dotdot (struct inode *dir, struct page **p)
+{
+ struct page *page = dir_get_page(dir, 0);
+ struct sysv_dir_entry *de = NULL;
+
+ if (!IS_ERR(page)) {
+ de = (struct sysv_dir_entry*) page_address(page) + 1;
+ *p = page;
+ }
+ return de;
+}
+
+ino_t sysv_inode_by_name(struct dentry *dentry)
+{
+ struct page *page;
+ struct sysv_dir_entry *de = sysv_find_entry (dentry, &page);
+ ino_t res = 0;
+
+ if (de) {
+ res = fs16_to_cpu(dentry->d_sb, de->inode);
+ dir_put_page(page);
+ }
+ return res;
+}
diff --git a/fs/sysv/file.c b/fs/sysv/file.c
new file mode 100644
index 00000000000000..ee1ec8bf62e398
--- /dev/null
+++ b/fs/sysv/file.c
@@ -0,0 +1,49 @@
+/*
+ * linux/fs/sysv/file.c
+ *
+ * minix/file.c
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * coh/file.c
+ * Copyright (C) 1993 Pascal Haible, Bruno Haible
+ *
+ * sysv/file.c
+ * Copyright (C) 1993 Bruno Haible
+ *
+ * SystemV/Coherent regular file handling primitives
+ */
+
+#include <linux/fs.h>
+#include <linux/sysv_fs.h>
+
+/*
+ * We have mostly NULLs here: the current defaults are OK for
+ * the coh filesystem.
+ */
+struct file_operations sysv_file_operations = {
+ llseek: generic_file_llseek,
+ read: generic_file_read,
+ write: generic_file_write,
+ mmap: generic_file_mmap,
+ fsync: sysv_sync_file,
+};
+
+struct inode_operations sysv_file_inode_operations = {
+ truncate: sysv_truncate,
+};
+
+int sysv_sync_file(struct file * file, struct dentry *dentry, int datasync)
+{
+ struct inode *inode = dentry->d_inode;
+ int err;
+
+ err = fsync_inode_buffers(inode);
+ err |= fsync_inode_data_buffers(inode);
+ if (!(inode->i_state & I_DIRTY))
+ return err;
+ if (datasync && !(inode->i_state & I_DIRTY_DATASYNC))
+ return err;
+
+ err |= sysv_sync_inode(inode);
+ return err ? -EIO : 0;
+}
diff --git a/fs/sysv/ialloc.c b/fs/sysv/ialloc.c
new file mode 100644
index 00000000000000..474e67ec6501b4
--- /dev/null
+++ b/fs/sysv/ialloc.c
@@ -0,0 +1,232 @@
+/*
+ * linux/fs/sysv/ialloc.c
+ *
+ * minix/bitmap.c
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * ext/freelists.c
+ * Copyright (C) 1992 Remy Card (card@masi.ibp.fr)
+ *
+ * xenix/alloc.c
+ * Copyright (C) 1992 Doug Evans
+ *
+ * coh/alloc.c
+ * Copyright (C) 1993 Pascal Haible, Bruno Haible
+ *
+ * sysv/ialloc.c
+ * Copyright (C) 1993 Bruno Haible
+ *
+ * This file contains code for allocating/freeing inodes.
+ */
+
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/sysv_fs.h>
+#include <linux/stddef.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+
+/* We don't trust the value of
+ sb->sv_sbd2->s_tinode = *sb->sv_sb_total_free_inodes
+ but we nevertheless keep it up to date. */
+
+/* An inode on disk is considered free if both i_mode == 0 and i_nlink == 0. */
+
+/* return &sb->sv_sb_fic_inodes[i] = &sbd->s_inode[i]; */
+static inline sysv_ino_t *
+sv_sb_fic_inode(struct super_block * sb, unsigned int i)
+{
+ if (sb->sv_bh1 == sb->sv_bh2)
+ return &sb->sv_sb_fic_inodes[i];
+ else {
+ /* 512 byte Xenix FS */
+ unsigned int offset = offsetof(struct xenix_super_block, s_inode[i]);
+ if (offset < 512)
+ return (sysv_ino_t*)(sb->sv_sbd1 + offset);
+ else
+ return (sysv_ino_t*)(sb->sv_sbd2 + offset);
+ }
+}
+
+struct sysv_inode *
+sysv_raw_inode(struct super_block *sb, unsigned ino, struct buffer_head **bh)
+{
+ struct sysv_inode *res;
+ int block = sb->sv_firstinodezone + sb->sv_block_base;
+ block += (ino-1) >> sb->sv_inodes_per_block_bits;
+ *bh = sb_bread(sb, block);
+ if (!*bh)
+ return NULL;
+ res = (struct sysv_inode *) (*bh)->b_data;
+ return res + ((ino-1) & sb->sv_inodes_per_block_1);
+}
+
+static int refill_free_cache(struct super_block *sb)
+{
+ struct buffer_head * bh;
+ struct sysv_inode * raw_inode;
+ int i = 0, ino;
+
+ ino = SYSV_ROOT_INO+1;
+ raw_inode = sysv_raw_inode(sb, ino, &bh);
+ if (!raw_inode)
+ goto out;
+ while (ino <= sb->sv_ninodes) {
+ if (raw_inode->i_mode == 0 && raw_inode->i_nlink == 0) {
+ *sv_sb_fic_inode(sb,i++) = cpu_to_fs16(sb, ino);
+ if (i == sb->sv_fic_size)
+ break;
+ }
+ if ((ino++ & sb->sv_inodes_per_block_1) == 0) {
+ brelse(bh);
+ raw_inode = sysv_raw_inode(sb, ino, &bh);
+ if (!raw_inode)
+ goto out;
+ } else
+ raw_inode++;
+ }
+ brelse(bh);
+out:
+ return i;
+}
+
+void sysv_free_inode(struct inode * inode)
+{
+ struct super_block * sb;
+ unsigned int ino;
+ struct buffer_head * bh;
+ struct sysv_inode * raw_inode;
+ unsigned count;
+
+ sb = inode->i_sb;
+ ino = inode->i_ino;
+ if (ino <= SYSV_ROOT_INO || ino > sb->sv_ninodes) {
+ printk("sysv_free_inode: inode 0,1,2 or nonexistent inode\n");
+ return;
+ }
+ raw_inode = sysv_raw_inode(sb, ino, &bh);
+ clear_inode(inode);
+ if (!raw_inode) {
+ printk("sysv_free_inode: unable to read inode block on device "
+ "%s\n", bdevname(inode->i_dev));
+ return;
+ }
+ lock_super(sb);
+ count = fs16_to_cpu(sb, *sb->sv_sb_fic_count);
+ if (count < sb->sv_fic_size) {
+ *sv_sb_fic_inode(sb,count++) = cpu_to_fs16(sb, ino);
+ *sb->sv_sb_fic_count = cpu_to_fs16(sb, count);
+ }
+ fs16_add(sb, sb->sv_sb_total_free_inodes, 1);
+ dirty_sb(sb);
+ memset(raw_inode, 0, sizeof(struct sysv_inode));
+ mark_buffer_dirty(bh);
+ unlock_super(sb);
+ brelse(bh);
+}
+
+struct inode * sysv_new_inode(const struct inode * dir, mode_t mode)
+{
+ struct inode * inode;
+ struct super_block * sb;
+ u16 ino;
+ unsigned count;
+
+ sb = dir->i_sb;
+ inode = new_inode(sb);
+ if (!inode)
+ return ERR_PTR(-ENOMEM);
+
+ lock_super(sb);
+ count = fs16_to_cpu(sb, *sb->sv_sb_fic_count);
+ if (count == 0 || (*sv_sb_fic_inode(sb,count-1) == 0)) {
+ count = refill_free_cache(sb);
+ if (count == 0) {
+ iput(inode);
+ unlock_super(sb);
+ return ERR_PTR(-ENOSPC);
+ }
+ }
+ /* Now count > 0. */
+ ino = *sv_sb_fic_inode(sb,--count);
+ *sb->sv_sb_fic_count = cpu_to_fs16(sb, count);
+ fs16_add(sb, sb->sv_sb_total_free_inodes, -1);
+ dirty_sb(sb);
+
+ if (dir->i_mode & S_ISGID) {
+ inode->i_gid = dir->i_gid;
+ if (S_ISDIR(mode))
+ mode |= S_ISGID;
+ } else
+ inode->i_gid = current->fsgid;
+
+ inode->i_uid = current->fsuid;
+ inode->i_ino = fs16_to_cpu(sb, ino);
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+ inode->i_blocks = inode->i_blksize = 0;
+ inode->u.sysv_i.i_dir_start_lookup = 0;
+ insert_inode_hash(inode);
+ mark_inode_dirty(inode);
+
+ inode->i_mode = mode; /* for sysv_write_inode() */
+ sysv_write_inode(inode, 0); /* ensure inode not allocated again */
+ mark_inode_dirty(inode); /* cleared by sysv_write_inode() */
+ /* That's it. */
+ unlock_super(sb);
+ return inode;
+}
+
+unsigned long sysv_count_free_inodes(struct super_block * sb)
+{
+ struct buffer_head * bh;
+ struct sysv_inode * raw_inode;
+ int ino, count, sb_count;
+
+ lock_super(sb);
+
+ sb_count = fs16_to_cpu(sb, *sb->sv_sb_total_free_inodes);
+
+ if (0)
+ goto trust_sb;
+
+ /* this causes a lot of disk traffic ... */
+ count = 0;
+ ino = SYSV_ROOT_INO+1;
+ raw_inode = sysv_raw_inode(sb, ino, &bh);
+ if (!raw_inode)
+ goto Eio;
+ while (ino <= sb->sv_ninodes) {
+ if (raw_inode->i_mode == 0 && raw_inode->i_nlink == 0)
+ count++;
+ if ((ino++ & sb->sv_inodes_per_block_1) == 0) {
+ brelse(bh);
+ raw_inode = sysv_raw_inode(sb, ino, &bh);
+ if (!raw_inode)
+ goto Eio;
+ } else
+ raw_inode++;
+ }
+ brelse(bh);
+ if (count != sb_count)
+ goto Einval;
+out:
+ unlock_super(sb);
+ return count;
+
+Einval:
+ printk("sysv_count_free_inodes: "
+ "free inode count was %d, correcting to %d\n",
+ sb_count, count);
+ if (!(sb->s_flags & MS_RDONLY)) {
+ *sb->sv_sb_total_free_inodes = cpu_to_fs16(sb, count);
+ dirty_sb(sb);
+ }
+ goto out;
+
+Eio:
+ printk("sysv_count_free_inodes: unable to read inode table\n");
+trust_sb:
+ count = sb_count;
+ goto out;
+}
diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c
new file mode 100644
index 00000000000000..d1ee3e89567d69
--- /dev/null
+++ b/fs/sysv/inode.c
@@ -0,0 +1,271 @@
+/*
+ * linux/fs/sysv/inode.c
+ *
+ * minix/inode.c
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * xenix/inode.c
+ * Copyright (C) 1992 Doug Evans
+ *
+ * coh/inode.c
+ * Copyright (C) 1993 Pascal Haible, Bruno Haible
+ *
+ * sysv/inode.c
+ * Copyright (C) 1993 Paul B. Monday
+ *
+ * sysv/inode.c
+ * Copyright (C) 1993 Bruno Haible
+ * Copyright (C) 1997, 1998 Krzysztof G. Baranowski
+ *
+ * This file contains code for allocating/freeing inodes and for read/writing
+ * the superblock.
+ */
+
+#include <linux/fs.h>
+#include <linux/sysv_fs.h>
+#include <linux/locks.h>
+#include <linux/smp_lock.h>
+#include <linux/highuid.h>
+#include <asm/byteorder.h>
+
+/* This is only called on sync() and umount(), when s_dirt=1. */
+static void sysv_write_super(struct super_block *sb)
+{
+ if (!(sb->s_flags & MS_RDONLY)) {
+ /* If we are going to write out the super block,
+ then attach current time stamp.
+ But if the filesystem was marked clean, keep it clean. */
+ unsigned long time = CURRENT_TIME;
+ unsigned long old_time = fs32_to_cpu(sb, *sb->sv_sb_time);
+ if (sb->sv_type == FSTYPE_SYSV4)
+ if (*sb->sv_sb_state == cpu_to_fs32(sb, 0x7c269d38 - old_time))
+ *sb->sv_sb_state = cpu_to_fs32(sb, 0x7c269d38 - time);
+ *sb->sv_sb_time = cpu_to_fs32(sb, time);
+ mark_buffer_dirty(sb->sv_bh2);
+ }
+ sb->s_dirt = 0;
+}
+
+static void sysv_put_super(struct super_block *sb)
+{
+ if (!(sb->s_flags & MS_RDONLY)) {
+ /* XXX ext2 also updates the state here */
+ mark_buffer_dirty(sb->sv_bh1);
+ if (sb->sv_bh1 != sb->sv_bh2)
+ mark_buffer_dirty(sb->sv_bh2);
+ }
+
+ brelse(sb->sv_bh1);
+ if (sb->sv_bh1 != sb->sv_bh2)
+ brelse(sb->sv_bh2);
+}
+
+static int sysv_statfs(struct super_block *sb, struct statfs *buf)
+{
+ buf->f_type = sb->s_magic;
+ buf->f_bsize = sb->s_blocksize;
+ buf->f_blocks = sb->sv_ndatazones;
+ buf->f_bavail = buf->f_bfree = sysv_count_free_blocks(sb);
+ buf->f_files = sb->sv_ninodes;
+ buf->f_ffree = sysv_count_free_inodes(sb);
+ buf->f_namelen = SYSV_NAMELEN;
+ return 0;
+}
+
+/*
+ * NXI <-> N0XI for PDP, XIN <-> XIN0 for le32, NIX <-> 0NIX for be32
+ */
+static inline void read3byte(struct super_block *sb,
+ unsigned char * from, unsigned char * to)
+{
+ if (sb->sv_bytesex == BYTESEX_PDP) {
+ to[0] = from[0];
+ to[1] = 0;
+ to[2] = from[1];
+ to[3] = from[2];
+ } else if (sb->sv_bytesex == BYTESEX_LE) {
+ to[0] = from[0];
+ to[1] = from[1];
+ to[2] = from[2];
+ to[3] = 0;
+ } else {
+ to[0] = 0;
+ to[1] = from[0];
+ to[2] = from[1];
+ to[3] = from[2];
+ }
+}
+
+static inline void write3byte(struct super_block *sb,
+ unsigned char * from, unsigned char * to)
+{
+ if (sb->sv_bytesex == BYTESEX_PDP) {
+ to[0] = from[0];
+ to[1] = from[2];
+ to[2] = from[3];
+ } else if (sb->sv_bytesex == BYTESEX_LE) {
+ to[0] = from[0];
+ to[1] = from[1];
+ to[2] = from[2];
+ } else {
+ to[0] = from[1];
+ to[1] = from[2];
+ to[2] = from[3];
+ }
+}
+
+static struct inode_operations sysv_symlink_inode_operations = {
+ readlink: page_readlink,
+ follow_link: page_follow_link,
+};
+
+void sysv_set_inode(struct inode *inode, dev_t rdev)
+{
+ if (S_ISREG(inode->i_mode)) {
+ inode->i_op = &sysv_file_inode_operations;
+ inode->i_fop = &sysv_file_operations;
+ inode->i_mapping->a_ops = &sysv_aops;
+ } else if (S_ISDIR(inode->i_mode)) {
+ inode->i_op = &sysv_dir_inode_operations;
+ inode->i_fop = &sysv_dir_operations;
+ inode->i_mapping->a_ops = &sysv_aops;
+ } else if (S_ISLNK(inode->i_mode)) {
+ if (inode->i_blocks) {
+ inode->i_op = &sysv_symlink_inode_operations;
+ inode->i_mapping->a_ops = &sysv_aops;
+ } else
+ inode->i_op = &sysv_fast_symlink_inode_operations;
+ } else
+ init_special_inode(inode, inode->i_mode, rdev);
+}
+
+static void sysv_read_inode(struct inode *inode)
+{
+ struct super_block * sb = inode->i_sb;
+ struct buffer_head * bh;
+ struct sysv_inode * raw_inode;
+ unsigned int block, ino;
+ dev_t rdev = 0;
+
+ ino = inode->i_ino;
+ if (!ino || ino > sb->sv_ninodes) {
+ printk("Bad inode number on dev %s"
+ ": %d is out of range\n",
+ kdevname(inode->i_dev), ino);
+ goto bad_inode;
+ }
+ raw_inode = sysv_raw_inode(sb, ino, &bh);
+ if (!raw_inode) {
+ printk("Major problem: unable to read inode from dev %s\n",
+ bdevname(inode->i_dev));
+ goto bad_inode;
+ }
+ /* SystemV FS: kludge permissions if ino==SYSV_ROOT_INO ?? */
+ inode->i_mode = fs16_to_cpu(sb, raw_inode->i_mode);
+ inode->i_uid = (uid_t)fs16_to_cpu(sb, raw_inode->i_uid);
+ inode->i_gid = (gid_t)fs16_to_cpu(sb, raw_inode->i_gid);
+ inode->i_nlink = fs16_to_cpu(sb, raw_inode->i_nlink);
+ inode->i_size = fs32_to_cpu(sb, raw_inode->i_size);
+ inode->i_atime = fs32_to_cpu(sb, raw_inode->i_atime);
+ inode->i_mtime = fs32_to_cpu(sb, raw_inode->i_mtime);
+ inode->i_ctime = fs32_to_cpu(sb, raw_inode->i_ctime);
+ inode->i_blocks = inode->i_blksize = 0;
+ for (block = 0; block < 10+1+1+1; block++)
+ read3byte(sb, &raw_inode->i_a.i_addb[3*block],
+ (unsigned char*)&inode->u.sysv_i.i_data[block]);
+ brelse(bh);
+ if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+ rdev = (u16)fs32_to_cpu(sb, inode->u.sysv_i.i_data[0]);
+ inode->u.sysv_i.i_dir_start_lookup = 0;
+ sysv_set_inode(inode, rdev);
+ return;
+
+bad_inode:
+ make_bad_inode(inode);
+ return;
+}
+
+static struct buffer_head * sysv_update_inode(struct inode * inode)
+{
+ struct super_block * sb = inode->i_sb;
+ struct buffer_head * bh;
+ struct sysv_inode * raw_inode;
+ unsigned int ino, block;
+
+ ino = inode->i_ino;
+ if (!ino || ino > sb->sv_ninodes) {
+ printk("Bad inode number on dev %s: %d is out of range\n",
+ bdevname(inode->i_dev), ino);
+ return 0;
+ }
+ raw_inode = sysv_raw_inode(sb, ino, &bh);
+ if (!raw_inode) {
+ printk("unable to read i-node block\n");
+ return 0;
+ }
+
+ raw_inode->i_mode = cpu_to_fs16(sb, inode->i_mode);
+ raw_inode->i_uid = cpu_to_fs16(sb, fs_high2lowuid(inode->i_uid));
+ raw_inode->i_gid = cpu_to_fs16(sb, fs_high2lowgid(inode->i_gid));
+ raw_inode->i_nlink = cpu_to_fs16(sb, inode->i_nlink);
+ raw_inode->i_size = cpu_to_fs32(sb, inode->i_size);
+ raw_inode->i_atime = cpu_to_fs32(sb, inode->i_atime);
+ raw_inode->i_mtime = cpu_to_fs32(sb, inode->i_mtime);
+ raw_inode->i_ctime = cpu_to_fs32(sb, inode->i_ctime);
+ if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+ inode->u.sysv_i.i_data[0] =
+ cpu_to_fs32(sb, kdev_t_to_nr(inode->i_rdev));
+ for (block = 0; block < 10+1+1+1; block++)
+ write3byte(sb, (unsigned char*)&inode->u.sysv_i.i_data[block],
+ &raw_inode->i_a.i_addb[3*block]);
+ mark_buffer_dirty(bh);
+ return bh;
+}
+
+void sysv_write_inode(struct inode * inode, int wait)
+{
+ struct buffer_head *bh;
+ lock_kernel();
+ bh = sysv_update_inode(inode);
+ brelse(bh);
+ unlock_kernel();
+}
+
+int sysv_sync_inode(struct inode * inode)
+{
+ int err = 0;
+ struct buffer_head *bh;
+
+ bh = sysv_update_inode(inode);
+ if (bh && buffer_dirty(bh)) {
+ ll_rw_block(WRITE, 1, &bh);
+ wait_on_buffer(bh);
+ if (buffer_req(bh) && !buffer_uptodate(bh)) {
+ printk ("IO error syncing sysv inode [%s:%08lx]\n",
+ bdevname(inode->i_dev), inode->i_ino);
+ err = -1;
+ }
+ }
+ else if (!bh)
+ err = -1;
+ brelse (bh);
+ return err;
+}
+
+static void sysv_delete_inode(struct inode *inode)
+{
+ lock_kernel();
+ inode->i_size = 0;
+ sysv_truncate(inode);
+ sysv_free_inode(inode);
+ unlock_kernel();
+}
+
+struct super_operations sysv_sops = {
+ read_inode: sysv_read_inode,
+ write_inode: sysv_write_inode,
+ delete_inode: sysv_delete_inode,
+ put_super: sysv_put_super,
+ write_super: sysv_write_super,
+ statfs: sysv_statfs,
+};
diff --git a/fs/sysv/itree.c b/fs/sysv/itree.c
new file mode 100644
index 00000000000000..7a5294ce8b02ad
--- /dev/null
+++ b/fs/sysv/itree.c
@@ -0,0 +1,439 @@
+/*
+ * linux/fs/sysv/itree.c
+ *
+ * Handling of indirect blocks' trees.
+ * AV, Sep--Dec 2000
+ */
+
+#include <linux/fs.h>
+#include <linux/sysv_fs.h>
+#include <linux/locks.h>
+#include <linux/smp_lock.h>
+
+enum {DIRECT = 10, DEPTH = 4}; /* Have triple indirect */
+
+static inline void dirty_indirect(struct buffer_head *bh, struct inode *inode)
+{
+ mark_buffer_dirty_inode(bh, inode);
+ if (IS_SYNC(inode)) {
+ ll_rw_block (WRITE, 1, &bh);
+ wait_on_buffer (bh);
+ }
+}
+
+static int block_to_path(struct inode *inode, long block, int offsets[DEPTH])
+{
+ struct super_block *sb = inode->i_sb;
+ int ptrs_bits = sb->sv_ind_per_block_bits;
+ unsigned long indirect_blocks = sb->sv_ind_per_block,
+ double_blocks = sb->sv_ind_per_block_2;
+ int n = 0;
+
+ if (block < 0) {
+ printk("sysv_block_map: block < 0\n");
+ } else if (block < DIRECT) {
+ offsets[n++] = block;
+ } else if ( (block -= DIRECT) < indirect_blocks) {
+ offsets[n++] = DIRECT;
+ offsets[n++] = block;
+ } else if ((block -= indirect_blocks) < double_blocks) {
+ offsets[n++] = DIRECT+1;
+ offsets[n++] = block >> ptrs_bits;
+ offsets[n++] = block & (indirect_blocks - 1);
+ } else if (((block -= double_blocks) >> (ptrs_bits * 2)) < indirect_blocks) {
+ offsets[n++] = DIRECT+2;
+ offsets[n++] = block >> (ptrs_bits * 2);
+ offsets[n++] = (block >> ptrs_bits) & (indirect_blocks - 1);
+ offsets[n++] = block & (indirect_blocks - 1);
+ } else {
+ /* nothing */;
+ }
+ return n;
+}
+
+static inline int block_to_cpu(struct super_block *sb, u32 nr)
+{
+ return sb->sv_block_base + fs32_to_cpu(sb, nr);
+}
+
+typedef struct {
+ u32 *p;
+ u32 key;
+ struct buffer_head *bh;
+} Indirect;
+
+static inline void add_chain(Indirect *p, struct buffer_head *bh, u32 *v)
+{
+ p->key = *(p->p = v);
+ p->bh = bh;
+}
+
+static inline int verify_chain(Indirect *from, Indirect *to)
+{
+ while (from <= to && from->key == *from->p)
+ from++;
+ return (from > to);
+}
+
+static inline u32 *block_end(struct buffer_head *bh)
+{
+ return (u32*)((char*)bh->b_data + bh->b_size);
+}
+
+static Indirect *get_branch(struct inode *inode,
+ int depth,
+ int offsets[],
+ Indirect chain[],
+ int *err)
+{
+ struct super_block *sb = inode->i_sb;
+ Indirect *p = chain;
+ struct buffer_head *bh;
+
+ *err = 0;
+ add_chain (chain, NULL, inode->u.sysv_i.i_data + *offsets);
+ if (!p->key)
+ goto no_block;
+ while (--depth) {
+ int block = block_to_cpu(sb, p->key);
+ bh = sb_bread(sb, block);
+ if (!bh)
+ goto failure;
+ if (!verify_chain(chain, p))
+ goto changed;
+ add_chain(++p, bh, (u32*)bh->b_data + *++offsets);
+ if (!p->key)
+ goto no_block;
+ }
+ return NULL;
+
+changed:
+ *err = -EAGAIN;
+ goto no_block;
+failure:
+ *err = -EIO;
+no_block:
+ return p;
+}
+
+static int alloc_branch(struct inode *inode,
+ int num,
+ int *offsets,
+ Indirect *branch)
+{
+ int blocksize = inode->i_sb->s_blocksize;
+ int n = 0;
+ int i;
+
+ branch[0].key = sysv_new_block(inode->i_sb);
+ if (branch[0].key) for (n = 1; n < num; n++) {
+ struct buffer_head *bh;
+ int parent;
+ /* Allocate the next block */
+ branch[n].key = sysv_new_block(inode->i_sb);
+ if (!branch[n].key)
+ break;
+ /*
+ * Get buffer_head for parent block, zero it out and set
+ * the pointer to new one, then send parent to disk.
+ */
+ parent = block_to_cpu(inode->i_sb, branch[n-1].key);
+ bh = sb_getblk(inode->i_sb, parent);
+ lock_buffer(bh);
+ memset(bh->b_data, 0, blocksize);
+ branch[n].bh = bh;
+ branch[n].p = (u32*) bh->b_data + offsets[n];
+ *branch[n].p = branch[n].key;
+ mark_buffer_uptodate(bh, 1);
+ unlock_buffer(bh);
+ dirty_indirect(bh, inode);
+ }
+ if (n == num)
+ return 0;
+
+ /* Allocation failed, free what we already allocated */
+ for (i = 1; i < n; i++)
+ bforget(branch[i].bh);
+ for (i = 0; i < n; i++)
+ sysv_free_block(inode->i_sb, branch[i].key);
+ return -ENOSPC;
+}
+
+static inline int splice_branch(struct inode *inode,
+ Indirect chain[],
+ Indirect *where,
+ int num)
+{
+ int i;
+ /* Verify that place we are splicing to is still there and vacant */
+
+ if (!verify_chain(chain, where-1) || *where->p)
+ goto changed;
+
+ *where->p = where->key;
+ inode->i_ctime = CURRENT_TIME;
+
+ /* had we spliced it onto indirect block? */
+ if (where->bh)
+ dirty_indirect(where->bh, inode);
+
+ if (IS_SYNC(inode))
+ sysv_sync_inode(inode);
+ else
+ mark_inode_dirty(inode);
+ return 0;
+
+changed:
+ for (i = 1; i < num; i++)
+ bforget(where[i].bh);
+ for (i = 0; i < num; i++)
+ sysv_free_block(inode->i_sb, where[i].key);
+ return -EAGAIN;
+}
+
+static int get_block(struct inode *inode, long iblock, struct buffer_head *bh_result, int create)
+{
+ int err = -EIO;
+ int offsets[DEPTH];
+ Indirect chain[DEPTH];
+ struct super_block *sb = inode->i_sb;
+ Indirect *partial;
+ int left;
+ int depth = block_to_path(inode, iblock, offsets);
+
+ if (depth == 0)
+ goto out;
+
+ lock_kernel();
+reread:
+ partial = get_branch(inode, depth, offsets, chain, &err);
+
+ /* Simplest case - block found, no allocation needed */
+ if (!partial) {
+got_it:
+ bh_result->b_dev = sb->s_dev;
+ bh_result->b_blocknr = block_to_cpu(sb, chain[depth-1].key);
+ bh_result->b_state |= (1UL << BH_Mapped);
+ /* Clean up and exit */
+ partial = chain+depth-1; /* the whole chain */
+ goto cleanup;
+ }
+
+ /* Next simple case - plain lookup or failed read of indirect block */
+ if (!create || err == -EIO) {
+cleanup:
+ while (partial > chain) {
+ brelse(partial->bh);
+ partial--;
+ }
+ unlock_kernel();
+out:
+ return err;
+ }
+
+ /*
+ * Indirect block might be removed by truncate while we were
+ * reading it. Handling of that case (forget what we've got and
+ * reread) is taken out of the main path.
+ */
+ if (err == -EAGAIN)
+ goto changed;
+
+ left = (chain + depth) - partial;
+ err = alloc_branch(inode, left, offsets+(partial-chain), partial);
+ if (err)
+ goto cleanup;
+
+ if (splice_branch(inode, chain, partial, left) < 0)
+ goto changed;
+
+ bh_result->b_state |= (1UL << BH_New);
+ goto got_it;
+
+changed:
+ while (partial > chain) {
+ brelse(partial->bh);
+ partial--;
+ }
+ goto reread;
+}
+
+static inline int all_zeroes(u32 *p, u32 *q)
+{
+ while (p < q)
+ if (*p++)
+ return 0;
+ return 1;
+}
+
+static Indirect *find_shared(struct inode *inode,
+ int depth,
+ int offsets[],
+ Indirect chain[],
+ u32 *top)
+{
+ Indirect *partial, *p;
+ int k, err;
+
+ *top = 0;
+ for (k = depth; k > 1 && !offsets[k-1]; k--)
+ ;
+ partial = get_branch(inode, k, offsets, chain, &err);
+ if (!partial)
+ partial = chain + k-1;
+ /*
+ * If the branch acquired continuation since we've looked at it -
+ * fine, it should all survive and (new) top doesn't belong to us.
+ */
+ if (!partial->key && *partial->p)
+ goto no_top;
+ for (p=partial; p>chain && all_zeroes((u32*)p->bh->b_data,p->p); p--)
+ ;
+ /*
+ * OK, we've found the last block that must survive. The rest of our
+ * branch should be detached before unlocking. However, if that rest
+ * of branch is all ours and does not grow immediately from the inode
+ * it's easier to cheat and just decrement partial->p.
+ */
+ if (p == chain + k - 1 && p > chain) {
+ p->p--;
+ } else {
+ *top = *p->p;
+ *p->p = 0;
+ }
+
+ while(partial > p) {
+ brelse(partial->bh);
+ partial--;
+ }
+no_top:
+ return partial;
+}
+
+static inline void free_data(struct inode *inode, u32 *p, u32 *q)
+{
+ for ( ; p < q ; p++) {
+ u32 nr = *p;
+ if (nr) {
+ *p = 0;
+ sysv_free_block(inode->i_sb, nr);
+ mark_inode_dirty(inode);
+ }
+ }
+}
+
+static void free_branches(struct inode *inode, u32 *p, u32 *q, int depth)
+{
+ struct buffer_head * bh;
+ struct super_block *sb = inode->i_sb;
+
+ if (depth--) {
+ for ( ; p < q ; p++) {
+ int block;
+ u32 nr = *p;
+ if (!nr)
+ continue;
+ *p = 0;
+ block = block_to_cpu(sb, nr);
+ bh = sb_bread(sb, block);
+ if (!bh)
+ continue;
+ free_branches(inode, (u32*)bh->b_data,
+ block_end(bh), depth);
+ bforget(bh);
+ sysv_free_block(sb, nr);
+ mark_inode_dirty(inode);
+ }
+ } else
+ free_data(inode, p, q);
+}
+
+void sysv_truncate (struct inode * inode)
+{
+ u32 *i_data = inode->u.sysv_i.i_data;
+ int offsets[DEPTH];
+ Indirect chain[DEPTH];
+ Indirect *partial;
+ int nr = 0;
+ int n;
+ long iblock;
+ unsigned blocksize;
+
+ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+ S_ISLNK(inode->i_mode)))
+ return;
+
+ blocksize = inode->i_sb->s_blocksize;
+ iblock = (inode->i_size + blocksize-1)
+ >> inode->i_sb->s_blocksize_bits;
+
+ block_truncate_page(inode->i_mapping, inode->i_size, get_block);
+
+ n = block_to_path(inode, iblock, offsets);
+ if (n == 0)
+ return;
+
+ if (n == 1) {
+ free_data(inode, i_data+offsets[0], i_data + DIRECT);
+ goto do_indirects;
+ }
+
+ partial = find_shared(inode, n, offsets, chain, &nr);
+ /* Kill the top of shared branch (already detached) */
+ if (nr) {
+ if (partial == chain)
+ mark_inode_dirty(inode);
+ else
+ dirty_indirect(partial->bh, inode);
+ free_branches(inode, &nr, &nr+1, (chain+n-1) - partial);
+ }
+ /* Clear the ends of indirect blocks on the shared branch */
+ while (partial > chain) {
+ free_branches(inode, partial->p + 1, block_end(partial->bh),
+ (chain+n-1) - partial);
+ dirty_indirect(partial->bh, inode);
+ brelse (partial->bh);
+ partial--;
+ }
+do_indirects:
+ /* Kill the remaining (whole) subtrees (== subtrees deeper than...) */
+ while (n < DEPTH) {
+ nr = i_data[DIRECT + n - 1];
+ if (nr) {
+ i_data[DIRECT + n - 1] = 0;
+ mark_inode_dirty(inode);
+ free_branches(inode, &nr, &nr+1, n);
+ }
+ n++;
+ }
+ inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ if (IS_SYNC(inode))
+ sysv_sync_inode (inode);
+ else
+ mark_inode_dirty(inode);
+}
+
+static int sysv_writepage(struct page *page)
+{
+ return block_write_full_page(page,get_block);
+}
+static int sysv_readpage(struct file *file, struct page *page)
+{
+ return block_read_full_page(page,get_block);
+}
+static int sysv_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
+{
+ return block_prepare_write(page,from,to,get_block);
+}
+static int sysv_bmap(struct address_space *mapping, long block)
+{
+ return generic_block_bmap(mapping,block,get_block);
+}
+struct address_space_operations sysv_aops = {
+ readpage: sysv_readpage,
+ writepage: sysv_writepage,
+ sync_page: block_sync_page,
+ prepare_write: sysv_prepare_write,
+ commit_write: generic_commit_write,
+ bmap: sysv_bmap
+};
diff --git a/fs/sysv/namei.c b/fs/sysv/namei.c
new file mode 100644
index 00000000000000..33f5e3b5613fee
--- /dev/null
+++ b/fs/sysv/namei.c
@@ -0,0 +1,323 @@
+/*
+ * linux/fs/sysv/namei.c
+ *
+ * minix/namei.c
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * coh/namei.c
+ * Copyright (C) 1993 Pascal Haible, Bruno Haible
+ *
+ * sysv/namei.c
+ * Copyright (C) 1993 Bruno Haible
+ * Copyright (C) 1997, 1998 Krzysztof G. Baranowski
+ */
+
+#include <linux/fs.h>
+#include <linux/sysv_fs.h>
+#include <linux/pagemap.h>
+
+static inline void inc_count(struct inode *inode)
+{
+ inode->i_nlink++;
+ mark_inode_dirty(inode);
+}
+
+static inline void dec_count(struct inode *inode)
+{
+ inode->i_nlink--;
+ mark_inode_dirty(inode);
+}
+
+static int add_nondir(struct dentry *dentry, struct inode *inode)
+{
+ int err = sysv_add_link(dentry, inode);
+ if (!err) {
+ d_instantiate(dentry, inode);
+ return 0;
+ }
+ dec_count(inode);
+ iput(inode);
+ return err;
+}
+
+static int sysv_hash(struct dentry *dentry, struct qstr *qstr)
+{
+ unsigned long hash;
+ int i;
+ const unsigned char *name;
+
+ i = SYSV_NAMELEN;
+ if (i >= qstr->len)
+ return 0;
+ /* Truncate the name in place, avoids having to define a compare
+ function. */
+ qstr->len = i;
+ name = qstr->name;
+ hash = init_name_hash();
+ while (i--)
+ hash = partial_name_hash(*name++, hash);
+ qstr->hash = end_name_hash(hash);
+ return 0;
+}
+
+struct dentry_operations sysv_dentry_operations = {
+ d_hash: sysv_hash,
+};
+
+static struct dentry *sysv_lookup(struct inode * dir, struct dentry * dentry)
+{
+ struct inode * inode = NULL;
+ ino_t ino;
+
+ dentry->d_op = dir->i_sb->s_root->d_op;
+ if (dentry->d_name.len > SYSV_NAMELEN)
+ return ERR_PTR(-ENAMETOOLONG);
+ ino = sysv_inode_by_name(dentry);
+
+ if (ino) {
+ inode = iget(dir->i_sb, ino);
+ if (!inode)
+ return ERR_PTR(-EACCES);
+ }
+ d_add(dentry, inode);
+ return NULL;
+}
+
+static int sysv_mknod(struct inode * dir, struct dentry * dentry, int mode, int rdev)
+{
+ struct inode * inode = sysv_new_inode(dir, mode);
+ int err = PTR_ERR(inode);
+
+ if (!IS_ERR(inode)) {
+ sysv_set_inode(inode, rdev);
+ mark_inode_dirty(inode);
+ err = add_nondir(dentry, inode);
+ }
+ return err;
+}
+
+static int sysv_create(struct inode * dir, struct dentry * dentry, int mode)
+{
+ return sysv_mknod(dir, dentry, mode, 0);
+}
+
+static int sysv_symlink(struct inode * dir, struct dentry * dentry,
+ const char * symname)
+{
+ int err = -ENAMETOOLONG;
+ int l = strlen(symname)+1;
+ struct inode * inode;
+
+ if (l > dir->i_sb->s_blocksize)
+ goto out;
+
+ inode = sysv_new_inode(dir, S_IFLNK|0777);
+ err = PTR_ERR(inode);
+ if (IS_ERR(inode))
+ goto out;
+
+ sysv_set_inode(inode, 0);
+ err = block_symlink(inode, symname, l);
+ if (err)
+ goto out_fail;
+
+ mark_inode_dirty(inode);
+ err = add_nondir(dentry, inode);
+out:
+ return err;
+
+out_fail:
+ dec_count(inode);
+ iput(inode);
+ goto out;
+}
+
+static int sysv_link(struct dentry * old_dentry, struct inode * dir,
+ struct dentry * dentry)
+{
+ struct inode *inode = old_dentry->d_inode;
+
+ if (S_ISDIR(inode->i_mode))
+ return -EPERM;
+
+ if (inode->i_nlink >= inode->i_sb->sv_link_max)
+ return -EMLINK;
+
+ inode->i_ctime = CURRENT_TIME;
+ inc_count(inode);
+ atomic_inc(&inode->i_count);
+
+ return add_nondir(dentry, inode);
+}
+
+static int sysv_mkdir(struct inode * dir, struct dentry *dentry, int mode)
+{
+ struct inode * inode;
+ int err = -EMLINK;
+
+ if (dir->i_nlink >= dir->i_sb->sv_link_max)
+ goto out;
+ inc_count(dir);
+
+ inode = sysv_new_inode(dir, S_IFDIR|mode);
+ err = PTR_ERR(inode);
+ if (IS_ERR(inode))
+ goto out_dir;
+
+ sysv_set_inode(inode, 0);
+
+ inc_count(inode);
+
+ err = sysv_make_empty(inode, dir);
+ if (err)
+ goto out_fail;
+
+ err = sysv_add_link(dentry, inode);
+ if (err)
+ goto out_fail;
+
+ d_instantiate(dentry, inode);
+out:
+ return err;
+
+out_fail:
+ dec_count(inode);
+ dec_count(inode);
+ iput(inode);
+out_dir:
+ dec_count(dir);
+ goto out;
+}
+
+static int sysv_unlink(struct inode * dir, struct dentry * dentry)
+{
+ struct inode * inode = dentry->d_inode;
+ struct page * page;
+ struct sysv_dir_entry * de;
+ int err = -ENOENT;
+
+ de = sysv_find_entry(dentry, &page);
+ if (!de)
+ goto out;
+
+ err = sysv_delete_entry (de, page);
+ if (err)
+ goto out;
+
+ inode->i_ctime = dir->i_ctime;
+ dec_count(inode);
+out:
+ return err;
+}
+
+static int sysv_rmdir(struct inode * dir, struct dentry * dentry)
+{
+ struct inode *inode = dentry->d_inode;
+ int err = -ENOTEMPTY;
+
+ if (sysv_empty_dir(inode)) {
+ err = sysv_unlink(dir, dentry);
+ if (!err) {
+ inode->i_size = 0;
+ dec_count(inode);
+ dec_count(dir);
+ }
+ }
+ return err;
+}
+
+/*
+ * Anybody can rename anything with this: the permission checks are left to the
+ * higher-level routines.
+ */
+static int sysv_rename(struct inode * old_dir, struct dentry * old_dentry,
+ struct inode * new_dir, struct dentry * new_dentry)
+{
+ struct inode * old_inode = old_dentry->d_inode;
+ struct inode * new_inode = new_dentry->d_inode;
+ struct page * dir_page = NULL;
+ struct sysv_dir_entry * dir_de = NULL;
+ struct page * old_page;
+ struct sysv_dir_entry * old_de;
+ int err = -ENOENT;
+
+ old_de = sysv_find_entry(old_dentry, &old_page);
+ if (!old_de)
+ goto out;
+
+ if (S_ISDIR(old_inode->i_mode)) {
+ err = -EIO;
+ dir_de = sysv_dotdot(old_inode, &dir_page);
+ if (!dir_de)
+ goto out_old;
+ }
+
+ if (new_inode) {
+ struct page * new_page;
+ struct sysv_dir_entry * new_de;
+
+ err = -ENOTEMPTY;
+ if (dir_de && !sysv_empty_dir(new_inode))
+ goto out_dir;
+
+ err = -ENOENT;
+ new_de = sysv_find_entry(new_dentry, &new_page);
+ if (!new_de)
+ goto out_dir;
+ inc_count(old_inode);
+ sysv_set_link(new_de, new_page, old_inode);
+ new_inode->i_ctime = CURRENT_TIME;
+ if (dir_de)
+ new_inode->i_nlink--;
+ dec_count(new_inode);
+ } else {
+ if (dir_de) {
+ err = -EMLINK;
+ if (new_dir->i_nlink >= new_dir->i_sb->sv_link_max)
+ goto out_dir;
+ }
+ inc_count(old_inode);
+ err = sysv_add_link(new_dentry, old_inode);
+ if (err) {
+ dec_count(old_inode);
+ goto out_dir;
+ }
+ if (dir_de)
+ inc_count(new_dir);
+ }
+
+ sysv_delete_entry(old_de, old_page);
+ dec_count(old_inode);
+
+ if (dir_de) {
+ sysv_set_link(dir_de, dir_page, new_dir);
+ dec_count(old_dir);
+ }
+ return 0;
+
+out_dir:
+ if (dir_de) {
+ kunmap(dir_page);
+ page_cache_release(dir_page);
+ }
+out_old:
+ kunmap(old_page);
+ page_cache_release(old_page);
+out:
+ return err;
+}
+
+/*
+ * directories can handle most operations...
+ */
+struct inode_operations sysv_dir_inode_operations = {
+ create: sysv_create,
+ lookup: sysv_lookup,
+ link: sysv_link,
+ unlink: sysv_unlink,
+ symlink: sysv_symlink,
+ mkdir: sysv_mkdir,
+ rmdir: sysv_rmdir,
+ mknod: sysv_mknod,
+ rename: sysv_rename,
+};
diff --git a/fs/sysv/super.c b/fs/sysv/super.c
new file mode 100644
index 00000000000000..2f79a13b4b059d
--- /dev/null
+++ b/fs/sysv/super.c
@@ -0,0 +1,508 @@
+/*
+ * linux/fs/sysv/inode.c
+ *
+ * minix/inode.c
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * xenix/inode.c
+ * Copyright (C) 1992 Doug Evans
+ *
+ * coh/inode.c
+ * Copyright (C) 1993 Pascal Haible, Bruno Haible
+ *
+ * sysv/inode.c
+ * Copyright (C) 1993 Paul B. Monday
+ *
+ * sysv/inode.c
+ * Copyright (C) 1993 Bruno Haible
+ * Copyright (C) 1997, 1998 Krzysztof G. Baranowski
+ *
+ * This file contains code for read/parsing the superblock.
+ */
+
+#include <linux/module.h>
+
+#include <linux/fs.h>
+#include <linux/sysv_fs.h>
+#include <linux/init.h>
+
+/*
+ * The following functions try to recognize specific filesystems.
+ *
+ * We recognize:
+ * - Xenix FS by its magic number.
+ * - SystemV FS by its magic number.
+ * - Coherent FS by its funny fname/fpack field.
+ * - SCO AFS by s_nfree == 0xffff
+ * - V7 FS has no distinguishing features.
+ *
+ * We discriminate among SystemV4 and SystemV2 FS by the assumption that
+ * the time stamp is not < 01-01-1980.
+ */
+
+enum {
+ JAN_1_1980 = (10*365 + 2) * 24 * 60 * 60
+};
+
+static void detected_xenix(struct super_block *sb)
+{
+ struct buffer_head *bh1 = sb->sv_bh1;
+ struct buffer_head *bh2 = sb->sv_bh2;
+ struct xenix_super_block * sbd1;
+ struct xenix_super_block * sbd2;
+
+ if (bh1 != bh2)
+ sbd1 = sbd2 = (struct xenix_super_block *) bh1->b_data;
+ else {
+ /* block size = 512, so bh1 != bh2 */
+ sbd1 = (struct xenix_super_block *) bh1->b_data;
+ sbd2 = (struct xenix_super_block *) (bh2->b_data - 512);
+ }
+
+ sb->sv_link_max = XENIX_LINK_MAX;
+ sb->sv_fic_size = XENIX_NICINOD;
+ sb->sv_flc_size = XENIX_NICFREE;
+ sb->sv_sbd1 = (char *) sbd1;
+ sb->sv_sbd2 = (char *) sbd2;
+ sb->sv_sb_fic_count = &sbd1->s_ninode;
+ sb->sv_sb_fic_inodes = &sbd1->s_inode[0];
+ sb->sv_sb_total_free_inodes = &sbd2->s_tinode;
+ sb->sv_bcache_count = &sbd1->s_nfree;
+ sb->sv_bcache = &sbd1->s_free[0];
+ sb->sv_free_blocks = &sbd2->s_tfree;
+ sb->sv_sb_time = &sbd2->s_time;
+ sb->sv_firstdatazone = fs16_to_cpu(sb, sbd1->s_isize);
+ sb->sv_nzones = fs32_to_cpu(sb, sbd1->s_fsize);
+}
+
+static void detected_sysv4(struct super_block *sb)
+{
+ struct sysv4_super_block * sbd;
+ struct buffer_head *bh1 = sb->sv_bh1;
+ struct buffer_head *bh2 = sb->sv_bh2;
+
+ if (bh1 == bh2)
+ sbd = (struct sysv4_super_block *) (bh1->b_data + BLOCK_SIZE/2);
+ else
+ sbd = (struct sysv4_super_block *) bh2->b_data;
+
+ sb->sv_link_max = SYSV_LINK_MAX;
+ sb->sv_fic_size = SYSV_NICINOD;
+ sb->sv_flc_size = SYSV_NICFREE;
+ sb->sv_sbd1 = (char *) sbd;
+ sb->sv_sbd2 = (char *) sbd;
+ sb->sv_sb_fic_count = &sbd->s_ninode;
+ sb->sv_sb_fic_inodes = &sbd->s_inode[0];
+ sb->sv_sb_total_free_inodes = &sbd->s_tinode;
+ sb->sv_bcache_count = &sbd->s_nfree;
+ sb->sv_bcache = &sbd->s_free[0];
+ sb->sv_free_blocks = &sbd->s_tfree;
+ sb->sv_sb_time = &sbd->s_time;
+ sb->sv_sb_state = &sbd->s_state;
+ sb->sv_firstdatazone = fs16_to_cpu(sb, sbd->s_isize);
+ sb->sv_nzones = fs32_to_cpu(sb, sbd->s_fsize);
+}
+
+static void detected_sysv2(struct super_block *sb)
+{
+ struct sysv2_super_block * sbd;
+ struct buffer_head *bh1 = sb->sv_bh1;
+ struct buffer_head *bh2 = sb->sv_bh2;
+
+ if (bh1 == bh2)
+ sbd = (struct sysv2_super_block *) (bh1->b_data + BLOCK_SIZE/2);
+ else
+ sbd = (struct sysv2_super_block *) bh2->b_data;
+
+ sb->sv_link_max = SYSV_LINK_MAX;
+ sb->sv_fic_size = SYSV_NICINOD;
+ sb->sv_flc_size = SYSV_NICFREE;
+ sb->sv_sbd1 = (char *) sbd;
+ sb->sv_sbd2 = (char *) sbd;
+ sb->sv_sb_fic_count = &sbd->s_ninode;
+ sb->sv_sb_fic_inodes = &sbd->s_inode[0];
+ sb->sv_sb_total_free_inodes = &sbd->s_tinode;
+ sb->sv_bcache_count = &sbd->s_nfree;
+ sb->sv_bcache = &sbd->s_free[0];
+ sb->sv_free_blocks = &sbd->s_tfree;
+ sb->sv_sb_time = &sbd->s_time;
+ sb->sv_sb_state = &sbd->s_state;
+ sb->sv_firstdatazone = fs16_to_cpu(sb, sbd->s_isize);
+ sb->sv_nzones = fs32_to_cpu(sb, sbd->s_fsize);
+}
+
+static void detected_coherent(struct super_block *sb)
+{
+ struct coh_super_block * sbd;
+ struct buffer_head *bh1 = sb->sv_bh1;
+
+ sbd = (struct coh_super_block *) bh1->b_data;
+
+ sb->sv_link_max = COH_LINK_MAX;
+ sb->sv_fic_size = COH_NICINOD;
+ sb->sv_flc_size = COH_NICFREE;
+ sb->sv_sbd1 = (char *) sbd;
+ sb->sv_sbd2 = (char *) sbd;
+ sb->sv_sb_fic_count = &sbd->s_ninode;
+ sb->sv_sb_fic_inodes = &sbd->s_inode[0];
+ sb->sv_sb_total_free_inodes = &sbd->s_tinode;
+ sb->sv_bcache_count = &sbd->s_nfree;
+ sb->sv_bcache = &sbd->s_free[0];
+ sb->sv_free_blocks = &sbd->s_tfree;
+ sb->sv_sb_time = &sbd->s_time;
+ sb->sv_firstdatazone = fs16_to_cpu(sb, sbd->s_isize);
+ sb->sv_nzones = fs32_to_cpu(sb, sbd->s_fsize);
+}
+
+static void detected_v7(struct super_block *sb)
+{
+ struct buffer_head *bh2 = sb->sv_bh2;
+ struct v7_super_block *sbd = (struct v7_super_block *)bh2->b_data;
+
+ sb->sv_link_max = V7_LINK_MAX;
+ sb->sv_fic_size = V7_NICINOD;
+ sb->sv_flc_size = V7_NICFREE;
+ sb->sv_sbd1 = (char *)sbd;
+ sb->sv_sbd2 = (char *)sbd;
+ sb->sv_sb_fic_count = &sbd->s_ninode;
+ sb->sv_sb_fic_inodes = &sbd->s_inode[0];
+ sb->sv_sb_total_free_inodes = &sbd->s_tinode;
+ sb->sv_bcache_count = &sbd->s_nfree;
+ sb->sv_bcache = &sbd->s_free[0];
+ sb->sv_free_blocks = &sbd->s_tfree;
+ sb->sv_sb_time = &sbd->s_time;
+ sb->sv_firstdatazone = fs16_to_cpu(sb, sbd->s_isize);
+ sb->sv_nzones = fs32_to_cpu(sb, sbd->s_fsize);
+}
+
+static int detect_xenix (struct super_block *sb, struct buffer_head *bh)
+{
+ struct xenix_super_block * sbd = (struct xenix_super_block *)bh->b_data;
+ if (sbd->s_magic == cpu_to_le32(0x2b5544))
+ sb->sv_bytesex = BYTESEX_LE;
+ else if (sbd->s_magic == cpu_to_be32(0x2b5544))
+ sb->sv_bytesex = BYTESEX_BE;
+ else
+ return 0;
+ if (sbd->s_type > 2 || sbd->s_type < 1)
+ return 0;
+ sb->sv_type = FSTYPE_XENIX;
+ return sbd->s_type;
+}
+
+static int detect_sysv (struct super_block *sb, struct buffer_head *bh)
+{
+ /* All relevant fields are at the same offsets in R2 and R4 */
+ struct sysv4_super_block * sbd;
+
+ sbd = (struct sysv4_super_block *) (bh->b_data + BLOCK_SIZE/2);
+ if (sbd->s_magic == cpu_to_le32(0xfd187e20))
+ sb->sv_bytesex = BYTESEX_LE;
+ else if (sbd->s_magic == cpu_to_be32(0xfd187e20))
+ sb->sv_bytesex = BYTESEX_BE;
+ else
+ return 0;
+
+ if (fs16_to_cpu(sb, sbd->s_nfree) == 0xffff) {
+ sb->sv_type = FSTYPE_AFS;
+ if (!(sb->s_flags & MS_RDONLY)) {
+ printk("SysV FS: SCO EAFS on %s detected, "
+ "forcing read-only mode.\n",
+ bdevname(sb->s_dev));
+ sb->s_flags |= MS_RDONLY;
+ }
+ return sbd->s_type;
+ }
+
+ if (fs32_to_cpu(sb, sbd->s_time) < JAN_1_1980) {
+ /* this is likely to happen on SystemV2 FS */
+ if (sbd->s_type > 3 || sbd->s_type < 1)
+ return 0;
+ sb->sv_type = FSTYPE_SYSV2;
+ return sbd->s_type;
+ }
+ if ((sbd->s_type > 3 || sbd->s_type < 1) &&
+ (sbd->s_type > 0x30 || sbd->s_type < 0x10))
+ return 0;
+
+ /* On Interactive Unix (ISC) Version 4.0/3.x s_type field = 0x10,
+ 0x20 or 0x30 indicates that symbolic links and the 14-character
+ filename limit is gone. Due to lack of information about this
+ feature read-only mode seems to be a reasonable approach... -KGB */
+
+ if (sbd->s_type >= 0x10) {
+ printk("SysV FS: can't handle long file names on %s, "
+ "forcing read-only mode.\n", kdevname(sb->s_dev));
+ sb->s_flags |= MS_RDONLY;
+ }
+
+ sb->sv_type = FSTYPE_SYSV4;
+ return sbd->s_type >= 0x10 ? (sbd->s_type >> 4) : sbd->s_type;
+}
+
+static int detect_coherent (struct super_block *sb, struct buffer_head *bh)
+{
+ struct coh_super_block * sbd;
+
+ sbd = (struct coh_super_block *) (bh->b_data + BLOCK_SIZE/2);
+ if ((memcmp(sbd->s_fname,"noname",6) && memcmp(sbd->s_fname,"xxxxx ",6))
+ || (memcmp(sbd->s_fpack,"nopack",6) && memcmp(sbd->s_fpack,"xxxxx\n",6)))
+ return 0;
+ sb->sv_bytesex = BYTESEX_PDP;
+ sb->sv_type = FSTYPE_COH;
+ return 1;
+}
+
+static int detect_sysv_odd(struct super_block *sb, struct buffer_head *bh)
+{
+ int size = detect_sysv(sb, bh);
+
+ return size>2 ? 0 : size;
+}
+
+static struct {
+ int block;
+ int (*test)(struct super_block *, struct buffer_head *);
+} flavours[] = {
+ {1, detect_xenix},
+ {0, detect_sysv},
+ {0, detect_coherent},
+ {9, detect_sysv_odd},
+ {15,detect_sysv_odd},
+ {18,detect_sysv},
+};
+
+static char *flavour_names[] = {
+ [FSTYPE_XENIX] "Xenix",
+ [FSTYPE_SYSV4] "SystemV",
+ [FSTYPE_SYSV2] "SystemV Release 2",
+ [FSTYPE_COH] "Coherent",
+ [FSTYPE_V7] "V7",
+ [FSTYPE_AFS] "AFS",
+};
+
+static void (*flavour_setup[])(struct super_block *) = {
+ [FSTYPE_XENIX] detected_xenix,
+ [FSTYPE_SYSV4] detected_sysv4,
+ [FSTYPE_SYSV2] detected_sysv2,
+ [FSTYPE_COH] detected_coherent,
+ [FSTYPE_V7] detected_v7,
+ [FSTYPE_AFS] detected_sysv4,
+};
+
+static int complete_read_super(struct super_block *sb, int silent, int size)
+{
+ struct inode *root_inode;
+ char *found = flavour_names[sb->sv_type];
+ u_char n_bits = size+8;
+ int bsize = 1 << n_bits;
+ int bsize_4 = bsize >> 2;
+
+ sb->sv_firstinodezone = 2;
+
+ flavour_setup[sb->sv_type](sb);
+
+ sb->sv_truncate = 1;
+ sb->sv_ndatazones = sb->sv_nzones - sb->sv_firstdatazone;
+ sb->sv_inodes_per_block = bsize >> 6;
+ sb->sv_inodes_per_block_1 = (bsize >> 6)-1;
+ sb->sv_inodes_per_block_bits = n_bits-6;
+ sb->sv_ind_per_block = bsize_4;
+ sb->sv_ind_per_block_2 = bsize_4*bsize_4;
+ sb->sv_toobig_block = 10 + bsize_4 * (1 + bsize_4 * (1 + bsize_4));
+ sb->sv_ind_per_block_bits = n_bits-2;
+
+ sb->sv_ninodes = (sb->sv_firstdatazone - sb->sv_firstinodezone)
+ << sb->sv_inodes_per_block_bits;
+
+ sb->s_blocksize = bsize;
+ sb->s_blocksize_bits = n_bits;
+ if (!silent)
+ printk("VFS: Found a %s FS (block size = %ld) on device %s\n",
+ found, sb->s_blocksize, bdevname(sb->s_dev));
+
+ sb->s_magic = SYSV_MAGIC_BASE + sb->sv_type;
+ /* set up enough so that it can read an inode */
+ sb->s_op = &sysv_sops;
+ root_inode = iget(sb,SYSV_ROOT_INO);
+ if (!root_inode || is_bad_inode(root_inode)) {
+ printk("SysV FS: get root inode failed\n");
+ return 0;
+ }
+ sb->s_root = d_alloc_root(root_inode);
+ if (!sb->s_root) {
+ iput(root_inode);
+ printk("SysV FS: get root dentry failed\n");
+ return 0;
+ }
+ if (sb->sv_truncate)
+ sb->s_root->d_op = &sysv_dentry_operations;
+ sb->s_flags |= MS_RDONLY;
+ sb->s_dirt = 1;
+ return 1;
+}
+
+static struct super_block *sysv_read_super(struct super_block *sb,
+ void *data, int silent)
+{
+ struct buffer_head *bh1;
+ struct buffer_head *bh = NULL;
+ kdev_t dev = sb->s_dev;
+ unsigned long blocknr;
+ int size = 0;
+ int i;
+
+ if (1024 != sizeof (struct xenix_super_block))
+ panic("Xenix FS: bad super-block size");
+ if ((512 != sizeof (struct sysv4_super_block))
+ || (512 != sizeof (struct sysv2_super_block)))
+ panic("SystemV FS: bad super-block size");
+ if (500 != sizeof (struct coh_super_block))
+ panic("Coherent FS: bad super-block size");
+ if (64 != sizeof (struct sysv_inode))
+ panic("sysv fs: bad i-node size");
+ set_blocksize(dev,BLOCK_SIZE);
+ sb->s_blocksize = BLOCK_SIZE;
+ sb->sv_block_base = 0;
+
+ for (i = 0; i < sizeof(flavours)/sizeof(flavours[0]) && !size; i++) {
+ brelse(bh);
+ bh = sb_bread(sb, flavours[i].block);
+ if (!bh)
+ continue;
+ size = flavours[i].test(sb, bh);
+ }
+
+ if (!size)
+ goto Eunknown;
+
+ switch (size) {
+ case 1:
+ blocknr = bh->b_blocknr << 1;
+ brelse(bh);
+ set_blocksize(dev, 512);
+ sb->s_blocksize = 512;
+ bh1 = sb_bread(sb, blocknr);
+ bh = sb_bread(sb, blocknr + 1);
+ break;
+ case 2:
+ bh1 = bh;
+ break;
+ case 3:
+ blocknr = bh->b_blocknr >> 1;
+ brelse(bh);
+ set_blocksize(dev, 2048);
+ sb->s_blocksize = 2048;
+ bh1 = bh = sb_bread(sb, blocknr);
+ break;
+ default:
+ goto Ebadsize;
+ }
+
+ if (bh && bh1) {
+ sb->sv_bh1 = bh1;
+ sb->sv_bh2 = bh;
+ if (complete_read_super(sb, silent, size))
+ return sb;
+ }
+
+ brelse(bh1);
+ brelse(bh);
+ set_blocksize(sb->s_dev,BLOCK_SIZE);
+ printk("oldfs: cannot read superblock\n");
+failed:
+ return NULL;
+
+Eunknown:
+ brelse(bh);
+ if (!silent)
+ printk("VFS: unable to find oldfs superblock on device %s\n",
+ bdevname(dev));
+ goto failed;
+Ebadsize:
+ brelse(bh);
+ if (!silent)
+ printk("VFS: oldfs: unsupported block size (%dKb)\n",
+ 1<<(size-2));
+ goto failed;
+}
+
+static struct super_block *v7_read_super(struct super_block *sb,void *data,
+ int silent)
+{
+ struct buffer_head *bh, *bh2 = NULL;
+ kdev_t dev = sb->s_dev;
+ struct v7_super_block *v7sb;
+ struct sysv_inode *v7i;
+
+ if (440 != sizeof (struct v7_super_block))
+ panic("V7 FS: bad super-block size");
+ if (64 != sizeof (struct sysv_inode))
+ panic("sysv fs: bad i-node size");
+
+ sb->sv_type = FSTYPE_V7;
+ sb->sv_bytesex = BYTESEX_PDP;
+
+ set_blocksize(dev, 512);
+ sb->s_blocksize = 512;
+
+ if ((bh = sb_bread(sb, 1)) == NULL) {
+ if (!silent)
+ printk("VFS: unable to read V7 FS superblock on "
+ "device %s.\n", bdevname(dev));
+ goto failed;
+ }
+
+ /* plausibility check on superblock */
+ v7sb = (struct v7_super_block *) bh->b_data;
+ if (fs16_to_cpu(sb,v7sb->s_nfree) > V7_NICFREE ||
+ fs16_to_cpu(sb,v7sb->s_ninode) > V7_NICINOD ||
+ fs32_to_cpu(sb,v7sb->s_time) == 0)
+ goto failed;
+
+ /* plausibility check on root inode: it is a directory,
+ with a nonzero size that is a multiple of 16 */
+ if ((bh2 = sb_bread(sb, 2)) == NULL)
+ goto failed;
+ v7i = (struct sysv_inode *)(bh2->b_data + 64);
+ if ((fs16_to_cpu(sb,v7i->i_mode) & ~0777) != S_IFDIR ||
+ (fs32_to_cpu(sb,v7i->i_size) == 0) ||
+ (fs32_to_cpu(sb,v7i->i_size) & 017) != 0)
+ goto failed;
+ brelse(bh2);
+
+ sb->sv_bh1 = bh;
+ sb->sv_bh2 = bh;
+ if (complete_read_super(sb, silent, 1))
+ return sb;
+
+failed:
+ brelse(bh2);
+ brelse(bh);
+ return NULL;
+}
+
+/* Every kernel module contains stuff like this. */
+
+static DECLARE_FSTYPE_DEV(sysv_fs_type, "sysv", sysv_read_super);
+static DECLARE_FSTYPE_DEV(v7_fs_type, "v7", v7_read_super);
+
+static int __init init_sysv_fs(void)
+{
+ int err = register_filesystem(&sysv_fs_type);
+ if (!err)
+ err = register_filesystem(&v7_fs_type);
+ return err;
+}
+
+static void __exit exit_sysv_fs(void)
+{
+ unregister_filesystem(&sysv_fs_type);
+ unregister_filesystem(&v7_fs_type);
+}
+
+EXPORT_NO_SYMBOLS;
+
+module_init(init_sysv_fs)
+module_exit(exit_sysv_fs)
+MODULE_LICENSE("GPL");
diff --git a/fs/sysv/symlink.c b/fs/sysv/symlink.c
new file mode 100644
index 00000000000000..bc09a374ddd4c7
--- /dev/null
+++ b/fs/sysv/symlink.c
@@ -0,0 +1,25 @@
+/*
+ * linux/fs/sysv/symlink.c
+ *
+ * Handling of System V filesystem fast symlinks extensions.
+ * Aug 2001, Christoph Hellwig (hch@infradead.org)
+ */
+
+#include <linux/fs.h>
+
+static int sysv_readlink(struct dentry *dentry, char *buffer, int buflen)
+{
+ char *s = (char *)dentry->d_inode->u.sysv_i.i_data;
+ return vfs_readlink(dentry, buffer, buflen, s);
+}
+
+static int sysv_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+ char *s = (char *)dentry->d_inode->u.sysv_i.i_data;
+ return vfs_follow_link(nd, s);
+}
+
+struct inode_operations sysv_fast_symlink_inode_operations = {
+ readlink: sysv_readlink,
+ follow_link: sysv_follow_link,
+};
diff --git a/fs/udf/Makefile b/fs/udf/Makefile
new file mode 100644
index 00000000000000..e44aed01ad9ffc
--- /dev/null
+++ b/fs/udf/Makefile
@@ -0,0 +1,17 @@
+#
+# Makefile for the linux udf-filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .o file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile..
+
+O_TARGET := udf.o
+
+obj-y := balloc.o dir.o file.o ialloc.o inode.o lowlevel.o namei.o \
+ partition.o super.o truncate.o symlink.o fsync.o \
+ crc.o directory.o misc.o udftime.o unicode.o
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/udf/balloc.c b/fs/udf/balloc.c
new file mode 100644
index 00000000000000..eab06b2999d75b
--- /dev/null
+++ b/fs/udf/balloc.c
@@ -0,0 +1,948 @@
+/*
+ * balloc.c
+ *
+ * PURPOSE
+ * Block allocation handling routines for the OSTA-UDF(tm) filesystem.
+ *
+ * CONTACTS
+ * E-mail regarding any portion of the Linux UDF file system should be
+ * directed to the development team mailing list (run by majordomo):
+ * linux_udf@hpesjro.fc.hp.com
+ *
+ * COPYRIGHT
+ * This file is distributed under the terms of the GNU General Public
+ * License (GPL). Copies of the GPL can be obtained from:
+ * ftp://prep.ai.mit.edu/pub/gnu/GPL
+ * Each contributing author retains all rights to their own work.
+ *
+ * (C) 1999-2001 Ben Fennema
+ * (C) 1999 Stelias Computing Inc
+ *
+ * HISTORY
+ *
+ * 02/24/99 blf Created.
+ *
+ */
+
+#include "udfdecl.h"
+
+#include <linux/locks.h>
+#include <linux/quotaops.h>
+#include <asm/bitops.h>
+
+#include "udf_i.h"
+#include "udf_sb.h"
+
+#define udf_clear_bit(nr,addr) ext2_clear_bit(nr,addr)
+#define udf_set_bit(nr,addr) ext2_set_bit(nr,addr)
+#define udf_test_bit(nr, addr) ext2_test_bit(nr, addr)
+#define udf_find_first_one_bit(addr, size) find_first_one_bit(addr, size)
+#define udf_find_next_one_bit(addr, size, offset) find_next_one_bit(addr, size, offset)
+
+#define leBPL_to_cpup(x) leNUM_to_cpup(BITS_PER_LONG, x)
+#define leNUM_to_cpup(x,y) xleNUM_to_cpup(x,y)
+#define xleNUM_to_cpup(x,y) (le ## x ## _to_cpup(y))
+#define uintBPL_t uint(BITS_PER_LONG)
+#define uint(x) xuint(x)
+#define xuint(x) uint ## x ## _t
+
+extern inline int find_next_one_bit (void * addr, int size, int offset)
+{
+ uintBPL_t * p = ((uintBPL_t *) addr) + (offset / BITS_PER_LONG);
+ uintBPL_t result = offset & ~(BITS_PER_LONG-1);
+ uintBPL_t tmp;
+
+ if (offset >= size)
+ return size;
+ size -= result;
+ offset &= (BITS_PER_LONG-1);
+ if (offset)
+ {
+ tmp = leBPL_to_cpup(p++);
+ tmp &= ~0UL << offset;
+ if (size < BITS_PER_LONG)
+ goto found_first;
+ if (tmp)
+ goto found_middle;
+ size -= BITS_PER_LONG;
+ result += BITS_PER_LONG;
+ }
+ while (size & ~(BITS_PER_LONG-1))
+ {
+ if ((tmp = leBPL_to_cpup(p++)))
+ goto found_middle;
+ result += BITS_PER_LONG;
+ size -= BITS_PER_LONG;
+ }
+ if (!size)
+ return result;
+ tmp = leBPL_to_cpup(p);
+found_first:
+ tmp &= ~0UL >> (BITS_PER_LONG-size);
+found_middle:
+ return result + ffz(~tmp);
+}
+
+#define find_first_one_bit(addr, size)\
+ find_next_one_bit((addr), (size), 0)
+
+static int read_block_bitmap(struct super_block * sb,
+ struct udf_bitmap *bitmap, unsigned int block, unsigned long bitmap_nr)
+{
+ struct buffer_head *bh = NULL;
+ int retval = 0;
+ lb_addr loc;
+
+ loc.logicalBlockNum = bitmap->s_extPosition;
+ loc.partitionReferenceNum = UDF_SB_PARTITION(sb);
+
+ bh = udf_tread(sb, udf_get_lb_pblock(sb, loc, block));
+ if (!bh)
+ {
+ retval = -EIO;
+ }
+ bitmap->s_block_bitmap[bitmap_nr] = bh;
+ return retval;
+}
+
+static int __load_block_bitmap(struct super_block * sb,
+ struct udf_bitmap *bitmap, unsigned int block_group)
+{
+ int retval = 0;
+ int nr_groups = bitmap->s_nr_groups;
+
+ if (block_group >= nr_groups)
+ {
+ udf_debug("block_group (%d) > nr_groups (%d)\n", block_group, nr_groups);
+ }
+
+ if (bitmap->s_block_bitmap[block_group])
+ return block_group;
+ else
+ {
+ retval = read_block_bitmap(sb, bitmap, block_group, block_group);
+ if (retval < 0)
+ return retval;
+ return block_group;
+ }
+}
+
+static inline int load_block_bitmap(struct super_block * sb,
+ struct udf_bitmap *bitmap, unsigned int block_group)
+{
+ int slot;
+
+ slot = __load_block_bitmap(sb, bitmap, block_group);
+
+ if (slot < 0)
+ return slot;
+
+ if (!bitmap->s_block_bitmap[slot])
+ return -EIO;
+
+ return slot;
+}
+
+static void udf_bitmap_free_blocks(struct super_block * sb,
+ struct inode * inode,
+ struct udf_bitmap *bitmap, lb_addr bloc, uint32_t offset, uint32_t count)
+{
+ struct buffer_head * bh = NULL;
+ unsigned long block;
+ unsigned long block_group;
+ unsigned long bit;
+ unsigned long i;
+ int bitmap_nr;
+ unsigned long overflow;
+
+ lock_super(sb);
+ if (bloc.logicalBlockNum < 0 ||
+ (bloc.logicalBlockNum + count) > UDF_SB_PARTLEN(sb, bloc.partitionReferenceNum))
+ {
+ udf_debug("%d < %d || %d + %d > %d\n",
+ bloc.logicalBlockNum, 0, bloc.logicalBlockNum, count,
+ UDF_SB_PARTLEN(sb, bloc.partitionReferenceNum));
+ goto error_return;
+ }
+
+ block = bloc.logicalBlockNum + offset + (sizeof(struct spaceBitmapDesc) << 3);
+
+do_more:
+ overflow = 0;
+ block_group = block >> (sb->s_blocksize_bits + 3);
+ bit = block % (sb->s_blocksize << 3);
+
+ /*
+ * Check to see if we are freeing blocks across a group boundary.
+ */
+ if (bit + count > (sb->s_blocksize << 3))
+ {
+ overflow = bit + count - (sb->s_blocksize << 3);
+ count -= overflow;
+ }
+ bitmap_nr = load_block_bitmap(sb, bitmap, block_group);
+ if (bitmap_nr < 0)
+ goto error_return;
+
+ bh = bitmap->s_block_bitmap[bitmap_nr];
+ for (i=0; i < count; i++)
+ {
+ if (udf_set_bit(bit + i, bh->b_data))
+ {
+ udf_debug("bit %ld already set\n", bit + i);
+ udf_debug("byte=%2x\n", ((char *)bh->b_data)[(bit + i) >> 3]);
+ }
+ else
+ {
+ if (inode)
+ DQUOT_FREE_BLOCK(inode, 1);
+ if (UDF_SB_LVIDBH(sb))
+ {
+ UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)] =
+ cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)])+1);
+ }
+ }
+ }
+ mark_buffer_dirty(bh);
+ if (overflow)
+ {
+ block += count;
+ count = overflow;
+ goto do_more;
+ }
+error_return:
+ sb->s_dirt = 1;
+ if (UDF_SB_LVIDBH(sb))
+ mark_buffer_dirty(UDF_SB_LVIDBH(sb));
+ unlock_super(sb);
+ return;
+}
+
+static int udf_bitmap_prealloc_blocks(struct super_block * sb,
+ struct inode * inode,
+ struct udf_bitmap *bitmap, uint16_t partition, uint32_t first_block,
+ uint32_t block_count)
+{
+ int alloc_count = 0;
+ int bit, block, block_group, group_start;
+ int nr_groups, bitmap_nr;
+ struct buffer_head *bh;
+
+ lock_super(sb);
+
+ if (first_block < 0 || first_block >= UDF_SB_PARTLEN(sb, partition))
+ goto out;
+
+ if (first_block + block_count > UDF_SB_PARTLEN(sb, partition))
+ block_count = UDF_SB_PARTLEN(sb, partition) - first_block;
+
+repeat:
+ nr_groups = (UDF_SB_PARTLEN(sb, partition) +
+ (sizeof(struct spaceBitmapDesc) << 3) + (sb->s_blocksize * 8) - 1) / (sb->s_blocksize * 8);
+ block = first_block + (sizeof(struct spaceBitmapDesc) << 3);
+ block_group = block >> (sb->s_blocksize_bits + 3);
+ group_start = block_group ? 0 : sizeof(struct spaceBitmapDesc);
+
+ bitmap_nr = load_block_bitmap(sb, bitmap, block_group);
+ if (bitmap_nr < 0)
+ goto out;
+ bh = bitmap->s_block_bitmap[bitmap_nr];
+
+ bit = block % (sb->s_blocksize << 3);
+
+ while (bit < (sb->s_blocksize << 3) && block_count > 0)
+ {
+ if (!udf_test_bit(bit, bh->b_data))
+ goto out;
+ else if (DQUOT_PREALLOC_BLOCK(inode, 1))
+ goto out;
+ else if (!udf_clear_bit(bit, bh->b_data))
+ {
+ udf_debug("bit already cleared for block %d\n", bit);
+ DQUOT_FREE_BLOCK(inode, 1);
+ goto out;
+ }
+ block_count --;
+ alloc_count ++;
+ bit ++;
+ block ++;
+ }
+ mark_buffer_dirty(bh);
+ if (block_count > 0)
+ goto repeat;
+out:
+ if (UDF_SB_LVIDBH(sb))
+ {
+ UDF_SB_LVID(sb)->freeSpaceTable[partition] =
+ cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[partition])-alloc_count);
+ mark_buffer_dirty(UDF_SB_LVIDBH(sb));
+ }
+ sb->s_dirt = 1;
+ unlock_super(sb);
+ return alloc_count;
+}
+
+static int udf_bitmap_new_block(struct super_block * sb,
+ struct inode * inode,
+ struct udf_bitmap *bitmap, uint16_t partition, uint32_t goal, int *err)
+{
+ int newbit, bit=0, block, block_group, group_start;
+ int end_goal, nr_groups, bitmap_nr, i;
+ struct buffer_head *bh = NULL;
+ char *ptr;
+ int newblock = 0;
+
+ *err = -ENOSPC;
+ lock_super(sb);
+
+repeat:
+ if (goal < 0 || goal >= UDF_SB_PARTLEN(sb, partition))
+ goal = 0;
+
+ nr_groups = bitmap->s_nr_groups;
+ block = goal + (sizeof(struct spaceBitmapDesc) << 3);
+ block_group = block >> (sb->s_blocksize_bits + 3);
+ group_start = block_group ? 0 : sizeof(struct spaceBitmapDesc);
+
+ bitmap_nr = load_block_bitmap(sb, bitmap, block_group);
+ if (bitmap_nr < 0)
+ goto error_return;
+ bh = bitmap->s_block_bitmap[bitmap_nr];
+ ptr = memscan((char *)bh->b_data + group_start, 0xFF, sb->s_blocksize - group_start);
+
+ if ((ptr - ((char *)bh->b_data)) < sb->s_blocksize)
+ {
+ bit = block % (sb->s_blocksize << 3);
+
+ if (udf_test_bit(bit, bh->b_data))
+ {
+ goto got_block;
+ }
+ end_goal = (bit + 63) & ~63;
+ bit = udf_find_next_one_bit(bh->b_data, end_goal, bit);
+ if (bit < end_goal)
+ goto got_block;
+ ptr = memscan((char *)bh->b_data + (bit >> 3), 0xFF, sb->s_blocksize - ((bit + 7) >> 3));
+ newbit = (ptr - ((char *)bh->b_data)) << 3;
+ if (newbit < sb->s_blocksize << 3)
+ {
+ bit = newbit;
+ goto search_back;
+ }
+ newbit = udf_find_next_one_bit(bh->b_data, sb->s_blocksize << 3, bit);
+ if (newbit < sb->s_blocksize << 3)
+ {
+ bit = newbit;
+ goto got_block;
+ }
+ }
+
+ for (i=0; i<(nr_groups*2); i++)
+ {
+ block_group ++;
+ if (block_group >= nr_groups)
+ block_group = 0;
+ group_start = block_group ? 0 : sizeof(struct spaceBitmapDesc);
+
+ bitmap_nr = load_block_bitmap(sb, bitmap, block_group);
+ if (bitmap_nr < 0)
+ goto error_return;
+ bh = bitmap->s_block_bitmap[bitmap_nr];
+ if (i < nr_groups)
+ {
+ ptr = memscan((char *)bh->b_data + group_start, 0xFF, sb->s_blocksize - group_start);
+ if ((ptr - ((char *)bh->b_data)) < sb->s_blocksize)
+ {
+ bit = (ptr - ((char *)bh->b_data)) << 3;
+ break;
+ }
+ }
+ else
+ {
+ bit = udf_find_next_one_bit((char *)bh->b_data, sb->s_blocksize << 3, group_start << 3);
+ if (bit < sb->s_blocksize << 3)
+ break;
+ }
+ }
+ if (i >= (nr_groups*2))
+ {
+ unlock_super(sb);
+ return newblock;
+ }
+ if (bit < sb->s_blocksize << 3)
+ goto search_back;
+ else
+ bit = udf_find_next_one_bit(bh->b_data, sb->s_blocksize << 3, group_start << 3);
+ if (bit >= sb->s_blocksize << 3)
+ {
+ unlock_super(sb);
+ return 0;
+ }
+
+search_back:
+ for (i=0; i<7 && bit > (group_start << 3) && udf_test_bit(bit - 1, bh->b_data); i++, bit--);
+
+got_block:
+
+ /*
+ * Check quota for allocation of this block.
+ */
+ if (inode && DQUOT_ALLOC_BLOCK(inode, 1))
+ {
+ unlock_super(sb);
+ *err = -EDQUOT;
+ return 0;
+ }
+
+ newblock = bit + (block_group << (sb->s_blocksize_bits + 3)) -
+ (sizeof(struct spaceBitmapDesc) << 3);
+
+ if (!udf_clear_bit(bit, bh->b_data))
+ {
+ udf_debug("bit already cleared for block %d\n", bit);
+ goto repeat;
+ }
+
+ mark_buffer_dirty(bh);
+
+ if (UDF_SB_LVIDBH(sb))
+ {
+ UDF_SB_LVID(sb)->freeSpaceTable[partition] =
+ cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[partition])-1);
+ mark_buffer_dirty(UDF_SB_LVIDBH(sb));
+ }
+ sb->s_dirt = 1;
+ unlock_super(sb);
+ *err = 0;
+ return newblock;
+
+error_return:
+ *err = -EIO;
+ unlock_super(sb);
+ return 0;
+}
+
+static void udf_table_free_blocks(struct super_block * sb,
+ struct inode * inode,
+ struct inode * table, lb_addr bloc, uint32_t offset, uint32_t count)
+{
+ uint32_t start, end;
+ uint32_t nextoffset, oextoffset, elen;
+ lb_addr nbloc, obloc, eloc;
+ struct buffer_head *obh, *nbh;
+ int8_t etype;
+ int i;
+
+ lock_super(sb);
+ if (bloc.logicalBlockNum < 0 ||
+ (bloc.logicalBlockNum + count) > UDF_SB_PARTLEN(sb, bloc.partitionReferenceNum))
+ {
+ udf_debug("%d < %d || %d + %d > %d\n",
+ bloc.logicalBlockNum, 0, bloc.logicalBlockNum, count,
+ UDF_SB_PARTLEN(sb, bloc.partitionReferenceNum));
+ goto error_return;
+ }
+
+ /* We do this up front - There are some error conditions that could occure,
+ but.. oh well */
+ if (inode)
+ DQUOT_FREE_BLOCK(inode, count);
+ if (UDF_SB_LVIDBH(sb))
+ {
+ UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)] =
+ cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)])+count);
+ mark_buffer_dirty(UDF_SB_LVIDBH(sb));
+ }
+
+ start = bloc.logicalBlockNum + offset;
+ end = bloc.logicalBlockNum + offset + count - 1;
+
+ oextoffset = nextoffset = sizeof(struct unallocSpaceEntry);
+ elen = 0;
+ obloc = nbloc = UDF_I_LOCATION(table);
+
+ obh = nbh = udf_tread(sb, udf_get_lb_pblock(sb, nbloc, 0));
+ atomic_inc(&nbh->b_count);
+
+ while (count && (etype =
+ udf_next_aext(table, &nbloc, &nextoffset, &eloc, &elen, &nbh, 1)) != -1)
+ {
+ if (((eloc.logicalBlockNum + (elen >> sb->s_blocksize_bits)) ==
+ start))
+ {
+ if ((0x3FFFFFFF - elen) < (count << sb->s_blocksize_bits))
+ {
+ count -= ((0x3FFFFFFF - elen) >> sb->s_blocksize_bits);
+ start += ((0x3FFFFFFF - elen) >> sb->s_blocksize_bits);
+ elen = (etype << 30) | (0x40000000 - sb->s_blocksize);
+ }
+ else
+ {
+ elen = (etype << 30) |
+ (elen + (count << sb->s_blocksize_bits));
+ start += count;
+ count = 0;
+ }
+ udf_write_aext(table, obloc, &oextoffset, eloc, elen, obh, 1);
+ }
+ else if (eloc.logicalBlockNum == (end + 1))
+ {
+ if ((0x3FFFFFFF - elen) < (count << sb->s_blocksize_bits))
+ {
+ count -= ((0x3FFFFFFF - elen) >> sb->s_blocksize_bits);
+ end -= ((0x3FFFFFFF - elen) >> sb->s_blocksize_bits);
+ eloc.logicalBlockNum -=
+ ((0x3FFFFFFF - elen) >> sb->s_blocksize_bits);
+ elen = (etype << 30) | (0x40000000 - sb->s_blocksize);
+ }
+ else
+ {
+ eloc.logicalBlockNum = start;
+ elen = (etype << 30) |
+ (elen + (count << sb->s_blocksize_bits));
+ end -= count;
+ count = 0;
+ }
+ udf_write_aext(table, obloc, &oextoffset, eloc, elen, obh, 1);
+ }
+
+ if (memcmp(&nbloc, &obloc, sizeof(lb_addr)))
+ {
+ i = -1;
+ obloc = nbloc;
+ udf_release_data(obh);
+ atomic_inc(&nbh->b_count);
+ obh = nbh;
+ oextoffset = 0;
+ }
+ else
+ oextoffset = nextoffset;
+ }
+
+ if (count)
+ {
+ /* NOTE: we CANNOT use udf_add_aext here, as it can try to allocate
+ a new block, and since we hold the super block lock already
+ very bad things would happen :)
+
+ We copy the behavior of udf_add_aext, but instead of
+ trying to allocate a new block close to the existing one,
+ we just steal a block from the extent we are trying to add.
+
+ It would be nice if the blocks were close together, but it
+ isn't required.
+ */
+
+ int adsize;
+ short_ad *sad = NULL;
+ long_ad *lad = NULL;
+ struct allocExtDesc *aed;
+
+ eloc.logicalBlockNum = start;
+ elen = EXT_RECORDED_ALLOCATED |
+ (count << sb->s_blocksize_bits);
+
+ if (UDF_I_ALLOCTYPE(table) == ICBTAG_FLAG_AD_SHORT)
+ adsize = sizeof(short_ad);
+ else if (UDF_I_ALLOCTYPE(table) == ICBTAG_FLAG_AD_LONG)
+ adsize = sizeof(long_ad);
+ else
+ {
+ udf_release_data(obh);
+ udf_release_data(nbh);
+ goto error_return;
+ }
+
+ if (nextoffset + (2 * adsize) > sb->s_blocksize)
+ {
+ char *sptr, *dptr;
+ int loffset;
+
+ udf_release_data(obh);
+ obh = nbh;
+ obloc = nbloc;
+ oextoffset = nextoffset;
+
+ /* Steal a block from the extent being free'd */
+ nbloc.logicalBlockNum = eloc.logicalBlockNum;
+ eloc.logicalBlockNum ++;
+ elen -= sb->s_blocksize;
+
+ if (!(nbh = udf_tread(sb,
+ udf_get_lb_pblock(sb, nbloc, 0))))
+ {
+ udf_release_data(obh);
+ goto error_return;
+ }
+ aed = (struct allocExtDesc *)(nbh->b_data);
+ aed->previousAllocExtLocation = cpu_to_le32(obloc.logicalBlockNum);
+ if (nextoffset + adsize > sb->s_blocksize)
+ {
+ loffset = nextoffset;
+ aed->lengthAllocDescs = cpu_to_le32(adsize);
+ sptr = (obh)->b_data + nextoffset - adsize;
+ dptr = nbh->b_data + sizeof(struct allocExtDesc);
+ memcpy(dptr, sptr, adsize);
+ nextoffset = sizeof(struct allocExtDesc) + adsize;
+ }
+ else
+ {
+ loffset = nextoffset + adsize;
+ aed->lengthAllocDescs = cpu_to_le32(0);
+ sptr = (obh)->b_data + nextoffset;
+ nextoffset = sizeof(struct allocExtDesc);
+
+ if (memcmp(&UDF_I_LOCATION(table), &obloc, sizeof(lb_addr)))
+ {
+ aed = (struct allocExtDesc *)(obh)->b_data;
+ aed->lengthAllocDescs =
+ cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) + adsize);
+ }
+ else
+ {
+ UDF_I_LENALLOC(table) += adsize;
+ mark_inode_dirty(table);
+ }
+ }
+ if (UDF_SB_UDFREV(sb) >= 0x0200)
+ udf_new_tag(nbh->b_data, TAG_IDENT_AED, 3, 1,
+ nbloc.logicalBlockNum, sizeof(tag));
+ else
+ udf_new_tag(nbh->b_data, TAG_IDENT_AED, 2, 1,
+ nbloc.logicalBlockNum, sizeof(tag));
+ switch (UDF_I_ALLOCTYPE(table))
+ {
+ case ICBTAG_FLAG_AD_SHORT:
+ {
+ sad = (short_ad *)sptr;
+ sad->extLength = cpu_to_le32(
+ EXT_NEXT_EXTENT_ALLOCDECS |
+ sb->s_blocksize);
+ sad->extPosition = cpu_to_le32(nbloc.logicalBlockNum);
+ break;
+ }
+ case ICBTAG_FLAG_AD_LONG:
+ {
+ lad = (long_ad *)sptr;
+ lad->extLength = cpu_to_le32(
+ EXT_NEXT_EXTENT_ALLOCDECS |
+ sb->s_blocksize);
+ lad->extLocation = cpu_to_lelb(nbloc);
+ break;
+ }
+ }
+ udf_update_tag(obh->b_data, loffset);
+ mark_buffer_dirty(obh);
+ }
+
+ if (elen) /* It's possible that stealing the block emptied the extent */
+ {
+ udf_write_aext(table, nbloc, &nextoffset, eloc, elen, nbh, 1);
+
+ if (!memcmp(&UDF_I_LOCATION(table), &nbloc, sizeof(lb_addr)))
+ {
+ UDF_I_LENALLOC(table) += adsize;
+ mark_inode_dirty(table);
+ }
+ else
+ {
+ aed = (struct allocExtDesc *)nbh->b_data;
+ aed->lengthAllocDescs =
+ cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) + adsize);
+ udf_update_tag(nbh->b_data, nextoffset);
+ mark_buffer_dirty(nbh);
+ }
+ }
+ }
+
+ udf_release_data(nbh);
+ udf_release_data(obh);
+
+error_return:
+ sb->s_dirt = 1;
+ unlock_super(sb);
+ return;
+}
+
+static int udf_table_prealloc_blocks(struct super_block * sb,
+ struct inode * inode,
+ struct inode *table, uint16_t partition, uint32_t first_block,
+ uint32_t block_count)
+{
+ int alloc_count = 0;
+ uint32_t extoffset, elen, adsize;
+ lb_addr bloc, eloc;
+ struct buffer_head *bh;
+ int8_t etype = -1;
+
+ if (first_block < 0 || first_block >= UDF_SB_PARTLEN(sb, partition))
+ return 0;
+
+ if (UDF_I_ALLOCTYPE(table) == ICBTAG_FLAG_AD_SHORT)
+ adsize = sizeof(short_ad);
+ else if (UDF_I_ALLOCTYPE(table) == ICBTAG_FLAG_AD_LONG)
+ adsize = sizeof(long_ad);
+ else
+ return 0;
+
+ lock_super(sb);
+
+ extoffset = sizeof(struct unallocSpaceEntry);
+ bloc = UDF_I_LOCATION(table);
+
+ bh = udf_tread(sb, udf_get_lb_pblock(sb, bloc, 0));
+ eloc.logicalBlockNum = 0xFFFFFFFF;
+
+ while (first_block != eloc.logicalBlockNum && (etype =
+ udf_next_aext(table, &bloc, &extoffset, &eloc, &elen, &bh, 1)) != -1)
+ {
+ udf_debug("eloc=%d, elen=%d, first_block=%d\n",
+ eloc.logicalBlockNum, elen, first_block);
+ ; /* empty loop body */
+ }
+
+ if (first_block == eloc.logicalBlockNum)
+ {
+ extoffset -= adsize;
+
+ alloc_count = (elen >> sb->s_blocksize_bits);
+ if (inode && DQUOT_PREALLOC_BLOCK(inode, alloc_count > block_count ? block_count : alloc_count))
+ alloc_count = 0;
+ else if (alloc_count > block_count)
+ {
+ alloc_count = block_count;
+ eloc.logicalBlockNum += alloc_count;
+ elen -= (alloc_count << sb->s_blocksize_bits);
+ udf_write_aext(table, bloc, &extoffset, eloc, (etype << 30) | elen, bh, 1);
+ }
+ else
+ udf_delete_aext(table, bloc, extoffset, eloc, (etype << 30) | elen, bh);
+ }
+ else
+ alloc_count = 0;
+
+ udf_release_data(bh);
+
+ if (alloc_count && UDF_SB_LVIDBH(sb))
+ {
+ UDF_SB_LVID(sb)->freeSpaceTable[partition] =
+ cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[partition])-alloc_count);
+ mark_buffer_dirty(UDF_SB_LVIDBH(sb));
+ sb->s_dirt = 1;
+ }
+ unlock_super(sb);
+ return alloc_count;
+}
+
+static int udf_table_new_block(struct super_block * sb,
+ struct inode * inode,
+ struct inode *table, uint16_t partition, uint32_t goal, int *err)
+{
+ uint32_t spread = 0xFFFFFFFF, nspread = 0xFFFFFFFF;
+ uint32_t newblock = 0, adsize;
+ uint32_t extoffset, goal_extoffset, elen, goal_elen = 0;
+ lb_addr bloc, goal_bloc, eloc, goal_eloc;
+ struct buffer_head *bh, *goal_bh;
+ int8_t etype;
+
+ *err = -ENOSPC;
+
+ if (UDF_I_ALLOCTYPE(table) == ICBTAG_FLAG_AD_SHORT)
+ adsize = sizeof(short_ad);
+ else if (UDF_I_ALLOCTYPE(table) == ICBTAG_FLAG_AD_LONG)
+ adsize = sizeof(long_ad);
+ else
+ return newblock;
+
+ lock_super(sb);
+
+ if (goal < 0 || goal >= UDF_SB_PARTLEN(sb, partition))
+ goal = 0;
+
+ /* We search for the closest matching block to goal. If we find a exact hit,
+ we stop. Otherwise we keep going till we run out of extents.
+ We store the buffer_head, bloc, and extoffset of the current closest
+ match and use that when we are done.
+ */
+
+ extoffset = sizeof(struct unallocSpaceEntry);
+ bloc = UDF_I_LOCATION(table);
+
+ goal_bh = bh = udf_tread(sb, udf_get_lb_pblock(sb, bloc, 0));
+ atomic_inc(&goal_bh->b_count);
+
+ while (spread && (etype =
+ udf_next_aext(table, &bloc, &extoffset, &eloc, &elen, &bh, 1)) != -1)
+ {
+ if (goal >= eloc.logicalBlockNum)
+ {
+ if (goal < eloc.logicalBlockNum + (elen >> sb->s_blocksize_bits))
+ nspread = 0;
+ else
+ nspread = goal - eloc.logicalBlockNum -
+ (elen >> sb->s_blocksize_bits);
+ }
+ else
+ nspread = eloc.logicalBlockNum - goal;
+
+ if (nspread < spread)
+ {
+ spread = nspread;
+ if (goal_bh != bh)
+ {
+ udf_release_data(goal_bh);
+ goal_bh = bh;
+ atomic_inc(&goal_bh->b_count);
+ }
+ goal_bloc = bloc;
+ goal_extoffset = extoffset - adsize;
+ goal_eloc = eloc;
+ goal_elen = (etype << 30) | elen;
+ }
+ }
+
+ udf_release_data(bh);
+
+ if (spread == 0xFFFFFFFF)
+ {
+ udf_release_data(goal_bh);
+ unlock_super(sb);
+ return 0;
+ }
+
+ /* Only allocate blocks from the beginning of the extent.
+ That way, we only delete (empty) extents, never have to insert an
+ extent because of splitting */
+ /* This works, but very poorly.... */
+
+ newblock = goal_eloc.logicalBlockNum;
+ goal_eloc.logicalBlockNum ++;
+ goal_elen -= sb->s_blocksize;
+
+ if (inode && DQUOT_ALLOC_BLOCK(inode, 1))
+ {
+ udf_release_data(goal_bh);
+ unlock_super(sb);
+ *err = -EDQUOT;
+ return 0;
+ }
+
+ if (goal_elen)
+ udf_write_aext(table, goal_bloc, &goal_extoffset, goal_eloc, goal_elen, goal_bh, 1);
+ else
+ udf_delete_aext(table, goal_bloc, goal_extoffset, goal_eloc, goal_elen, goal_bh);
+ udf_release_data(goal_bh);
+
+ if (UDF_SB_LVIDBH(sb))
+ {
+ UDF_SB_LVID(sb)->freeSpaceTable[partition] =
+ cpu_to_le32(le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[partition])-1);
+ mark_buffer_dirty(UDF_SB_LVIDBH(sb));
+ }
+
+ sb->s_dirt = 1;
+ unlock_super(sb);
+ *err = 0;
+ return newblock;
+}
+
+inline void udf_free_blocks(struct super_block * sb,
+ struct inode * inode,
+ lb_addr bloc, uint32_t offset, uint32_t count)
+{
+ uint16_t partition = bloc.partitionReferenceNum;
+
+ if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_BITMAP)
+ {
+ return udf_bitmap_free_blocks(sb, inode,
+ UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_bitmap,
+ bloc, offset, count);
+ }
+ else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_TABLE)
+ {
+ return udf_table_free_blocks(sb, inode,
+ UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_table,
+ bloc, offset, count);
+ }
+ else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_BITMAP)
+ {
+ return udf_bitmap_free_blocks(sb, inode,
+ UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_bitmap,
+ bloc, offset, count);
+ }
+ else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_TABLE)
+ {
+ return udf_table_free_blocks(sb, inode,
+ UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_table,
+ bloc, offset, count);
+ }
+ else
+ return;
+}
+
+inline int udf_prealloc_blocks(struct super_block * sb,
+ struct inode * inode,
+ uint16_t partition, uint32_t first_block, uint32_t block_count)
+{
+ if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_BITMAP)
+ {
+ return udf_bitmap_prealloc_blocks(sb, inode,
+ UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_bitmap,
+ partition, first_block, block_count);
+ }
+ else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_TABLE)
+ {
+ return udf_table_prealloc_blocks(sb, inode,
+ UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_table,
+ partition, first_block, block_count);
+ }
+ else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_BITMAP)
+ {
+ return udf_bitmap_prealloc_blocks(sb, inode,
+ UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_bitmap,
+ partition, first_block, block_count);
+ }
+ else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_TABLE)
+ {
+ return udf_table_prealloc_blocks(sb, inode,
+ UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_table,
+ partition, first_block, block_count);
+ }
+ else
+ return 0;
+}
+
+inline int udf_new_block(struct super_block * sb,
+ struct inode * inode,
+ uint16_t partition, uint32_t goal, int *err)
+{
+ if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_BITMAP)
+ {
+ return udf_bitmap_new_block(sb, inode,
+ UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_bitmap,
+ partition, goal, err);
+ }
+ else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_UNALLOC_TABLE)
+ {
+ return udf_table_new_block(sb, inode,
+ UDF_SB_PARTMAPS(sb)[partition].s_uspace.s_table,
+ partition, goal, err);
+ }
+ else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_BITMAP)
+ {
+ return udf_bitmap_new_block(sb, inode,
+ UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_bitmap,
+ partition, goal, err);
+ }
+ else if (UDF_SB_PARTFLAGS(sb, partition) & UDF_PART_FLAG_FREED_TABLE)
+ {
+ return udf_table_new_block(sb, inode,
+ UDF_SB_PARTMAPS(sb)[partition].s_fspace.s_table,
+ partition, goal, err);
+ }
+ else
+ {
+ *err = -EIO;
+ return 0;
+ }
+}
diff --git a/fs/udf/crc.c b/fs/udf/crc.c
new file mode 100644
index 00000000000000..1dc900e7af31ab
--- /dev/null
+++ b/fs/udf/crc.c
@@ -0,0 +1,178 @@
+/*
+ * crc.c
+ *
+ * PURPOSE
+ * Routines to generate, calculate, and test a 16-bit CRC.
+ *
+ * DESCRIPTION
+ * The CRC code was devised by Don P. Mitchell of AT&T Bell Laboratories
+ * and Ned W. Rhodes of Software Systems Group. It has been published in
+ * "Design and Validation of Computer Protocols", Prentice Hall,
+ * Englewood Cliffs, NJ, 1991, Chapter 3, ISBN 0-13-539925-4.
+ *
+ * Copyright is held by AT&T.
+ *
+ * AT&T gives permission for the free use of the CRC source code.
+ *
+ * CONTACTS
+ * E-mail regarding any portion of the Linux UDF file system should be
+ * directed to the development team mailing list (run by majordomo):
+ * linux_udf@hpesjro.fc.hp.com
+ *
+ * COPYRIGHT
+ * This file is distributed under the terms of the GNU General Public
+ * License (GPL). Copies of the GPL can be obtained from:
+ * ftp://prep.ai.mit.edu/pub/gnu/GPL
+ * Each contributing author retains all rights to their own work.
+ */
+
+#include "udfdecl.h"
+
+static uint16_t crc_table[256] = {
+ 0x0000U, 0x1021U, 0x2042U, 0x3063U, 0x4084U, 0x50a5U, 0x60c6U, 0x70e7U,
+ 0x8108U, 0x9129U, 0xa14aU, 0xb16bU, 0xc18cU, 0xd1adU, 0xe1ceU, 0xf1efU,
+ 0x1231U, 0x0210U, 0x3273U, 0x2252U, 0x52b5U, 0x4294U, 0x72f7U, 0x62d6U,
+ 0x9339U, 0x8318U, 0xb37bU, 0xa35aU, 0xd3bdU, 0xc39cU, 0xf3ffU, 0xe3deU,
+ 0x2462U, 0x3443U, 0x0420U, 0x1401U, 0x64e6U, 0x74c7U, 0x44a4U, 0x5485U,
+ 0xa56aU, 0xb54bU, 0x8528U, 0x9509U, 0xe5eeU, 0xf5cfU, 0xc5acU, 0xd58dU,
+ 0x3653U, 0x2672U, 0x1611U, 0x0630U, 0x76d7U, 0x66f6U, 0x5695U, 0x46b4U,
+ 0xb75bU, 0xa77aU, 0x9719U, 0x8738U, 0xf7dfU, 0xe7feU, 0xd79dU, 0xc7bcU,
+ 0x48c4U, 0x58e5U, 0x6886U, 0x78a7U, 0x0840U, 0x1861U, 0x2802U, 0x3823U,
+ 0xc9ccU, 0xd9edU, 0xe98eU, 0xf9afU, 0x8948U, 0x9969U, 0xa90aU, 0xb92bU,
+ 0x5af5U, 0x4ad4U, 0x7ab7U, 0x6a96U, 0x1a71U, 0x0a50U, 0x3a33U, 0x2a12U,
+ 0xdbfdU, 0xcbdcU, 0xfbbfU, 0xeb9eU, 0x9b79U, 0x8b58U, 0xbb3bU, 0xab1aU,
+ 0x6ca6U, 0x7c87U, 0x4ce4U, 0x5cc5U, 0x2c22U, 0x3c03U, 0x0c60U, 0x1c41U,
+ 0xedaeU, 0xfd8fU, 0xcdecU, 0xddcdU, 0xad2aU, 0xbd0bU, 0x8d68U, 0x9d49U,
+ 0x7e97U, 0x6eb6U, 0x5ed5U, 0x4ef4U, 0x3e13U, 0x2e32U, 0x1e51U, 0x0e70U,
+ 0xff9fU, 0xefbeU, 0xdfddU, 0xcffcU, 0xbf1bU, 0xaf3aU, 0x9f59U, 0x8f78U,
+ 0x9188U, 0x81a9U, 0xb1caU, 0xa1ebU, 0xd10cU, 0xc12dU, 0xf14eU, 0xe16fU,
+ 0x1080U, 0x00a1U, 0x30c2U, 0x20e3U, 0x5004U, 0x4025U, 0x7046U, 0x6067U,
+ 0x83b9U, 0x9398U, 0xa3fbU, 0xb3daU, 0xc33dU, 0xd31cU, 0xe37fU, 0xf35eU,
+ 0x02b1U, 0x1290U, 0x22f3U, 0x32d2U, 0x4235U, 0x5214U, 0x6277U, 0x7256U,
+ 0xb5eaU, 0xa5cbU, 0x95a8U, 0x8589U, 0xf56eU, 0xe54fU, 0xd52cU, 0xc50dU,
+ 0x34e2U, 0x24c3U, 0x14a0U, 0x0481U, 0x7466U, 0x6447U, 0x5424U, 0x4405U,
+ 0xa7dbU, 0xb7faU, 0x8799U, 0x97b8U, 0xe75fU, 0xf77eU, 0xc71dU, 0xd73cU,
+ 0x26d3U, 0x36f2U, 0x0691U, 0x16b0U, 0x6657U, 0x7676U, 0x4615U, 0x5634U,
+ 0xd94cU, 0xc96dU, 0xf90eU, 0xe92fU, 0x99c8U, 0x89e9U, 0xb98aU, 0xa9abU,
+ 0x5844U, 0x4865U, 0x7806U, 0x6827U, 0x18c0U, 0x08e1U, 0x3882U, 0x28a3U,
+ 0xcb7dU, 0xdb5cU, 0xeb3fU, 0xfb1eU, 0x8bf9U, 0x9bd8U, 0xabbbU, 0xbb9aU,
+ 0x4a75U, 0x5a54U, 0x6a37U, 0x7a16U, 0x0af1U, 0x1ad0U, 0x2ab3U, 0x3a92U,
+ 0xfd2eU, 0xed0fU, 0xdd6cU, 0xcd4dU, 0xbdaaU, 0xad8bU, 0x9de8U, 0x8dc9U,
+ 0x7c26U, 0x6c07U, 0x5c64U, 0x4c45U, 0x3ca2U, 0x2c83U, 0x1ce0U, 0x0cc1U,
+ 0xef1fU, 0xff3eU, 0xcf5dU, 0xdf7cU, 0xaf9bU, 0xbfbaU, 0x8fd9U, 0x9ff8U,
+ 0x6e17U, 0x7e36U, 0x4e55U, 0x5e74U, 0x2e93U, 0x3eb2U, 0x0ed1U, 0x1ef0U
+};
+
+/*
+ * udf_crc
+ *
+ * PURPOSE
+ * Calculate a 16-bit CRC checksum using ITU-T V.41 polynomial.
+ *
+ * DESCRIPTION
+ * The OSTA-UDF(tm) 1.50 standard states that using CRCs is mandatory.
+ * The polynomial used is: x^16 + x^12 + x^15 + 1
+ *
+ * PRE-CONDITIONS
+ * data Pointer to the data block.
+ * size Size of the data block.
+ *
+ * POST-CONDITIONS
+ * <return> CRC of the data block.
+ *
+ * HISTORY
+ * July 21, 1997 - Andrew E. Mileski
+ * Adapted from OSTA-UDF(tm) 1.50 standard.
+ */
+extern uint16_t
+udf_crc(uint8_t *data, uint32_t size, uint16_t crc)
+{
+ while (size--)
+ crc = crc_table[(crc >> 8 ^ *(data++)) & 0xffU] ^ (crc << 8);
+
+ return crc;
+}
+
+/****************************************************************************/
+#if defined(TEST)
+
+/*
+ * PURPOSE
+ * Test udf_crc()
+ *
+ * HISTORY
+ * July 21, 1997 - Andrew E. Mileski
+ * Adapted from OSTA-UDF(tm) 1.50 standard.
+ */
+
+unsigned char bytes[] = { 0x70U, 0x6AU, 0x77U };
+
+int main(void)
+{
+ unsigned short x;
+
+ x = udf_crc16(bytes, sizeof bytes);
+ printf("udf_crc16: calculated = %4.4x, correct = %4.4x\n", x, 0x3299U);
+
+ return 0;
+}
+
+#endif /* defined(TEST) */
+
+/****************************************************************************/
+#if defined(GENERATE)
+
+/*
+ * PURPOSE
+ * Generate a table for fast 16-bit CRC calculations (any polynomial).
+ *
+ * DESCRIPTION
+ * The ITU-T V.41 polynomial is 010041.
+ *
+ * HISTORY
+ * July 21, 1997 - Andrew E. Mileski
+ * Adapted from OSTA-UDF(tm) 1.50 standard.
+ */
+
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+ unsigned long crc, poly;
+ int n, i;
+
+ /* Get the polynomial */
+ sscanf(argv[1], "%lo", &poly);
+ if (poly & 0xffff0000U){
+ fprintf(stderr, "polynomial is too large\en");
+ exit(1);
+ }
+
+ printf("/* CRC 0%o */\n", poly);
+
+ /* Create a table */
+ printf("static unsigned short crc_table[256] = {\n");
+ for (n = 0; n < 256; n++){
+ if (n % 8 == 0)
+ printf("\t");
+ crc = n << 8;
+ for (i = 0; i < 8; i++){
+ if(crc & 0x8000U)
+ crc = (crc << 1) ^ poly;
+ else
+ crc <<= 1;
+ crc &= 0xFFFFU;
+ }
+ if (n == 255)
+ printf("0x%04xU ", crc);
+ else
+ printf("0x%04xU, ", crc);
+ if(n % 8 == 7)
+ printf("\n");
+ }
+ printf("};\n");
+
+ return 0;
+}
+
+#endif /* defined(GENERATE) */
diff --git a/fs/udf/dir.c b/fs/udf/dir.c
new file mode 100644
index 00000000000000..0d1623d0996e51
--- /dev/null
+++ b/fs/udf/dir.c
@@ -0,0 +1,257 @@
+/*
+ * dir.c
+ *
+ * PURPOSE
+ * Directory handling routines for the OSTA-UDF(tm) filesystem.
+ *
+ * CONTACTS
+ * E-mail regarding any portion of the Linux UDF file system should be
+ * directed to the development team mailing list (run by majordomo):
+ * linux_udf@hpesjro.fc.hp.com
+ *
+ * COPYRIGHT
+ * This file is distributed under the terms of the GNU General Public
+ * License (GPL). Copies of the GPL can be obtained from:
+ * ftp://prep.ai.mit.edu/pub/gnu/GPL
+ * Each contributing author retains all rights to their own work.
+ *
+ * (C) 1998-2001 Ben Fennema
+ *
+ * HISTORY
+ *
+ * 10/05/98 dgb Split directory operations into it's own file
+ * Implemented directory reads via do_udf_readdir
+ * 10/06/98 Made directory operations work!
+ * 11/17/98 Rewrote directory to support ICBTAG_FLAG_AD_LONG
+ * 11/25/98 blf Rewrote directory handling (readdir+lookup) to support reading
+ * across blocks.
+ * 12/12/98 Split out the lookup code to namei.c. bulk of directory
+ * code now in directory.c:udf_fileident_read.
+ */
+
+#include "udfdecl.h"
+
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+
+#include "udf_i.h"
+#include "udf_sb.h"
+
+/* Prototypes for file operations */
+static int udf_readdir(struct file *, void *, filldir_t);
+static int do_udf_readdir(struct inode *, struct file *, filldir_t, void *);
+
+/* readdir and lookup functions */
+
+struct file_operations udf_dir_operations = {
+ read: generic_read_dir,
+ readdir: udf_readdir,
+ ioctl: udf_ioctl,
+ fsync: udf_fsync_file,
+};
+
+/*
+ * udf_readdir
+ *
+ * PURPOSE
+ * Read a directory entry.
+ *
+ * DESCRIPTION
+ * Optional - sys_getdents() will return -ENOTDIR if this routine is not
+ * available.
+ *
+ * Refer to sys_getdents() in fs/readdir.c
+ * sys_getdents() -> .
+ *
+ * PRE-CONDITIONS
+ * filp Pointer to directory file.
+ * buf Pointer to directory entry buffer.
+ * filldir Pointer to filldir function.
+ *
+ * POST-CONDITIONS
+ * <return> >=0 on success.
+ *
+ * HISTORY
+ * July 1, 1997 - Andrew E. Mileski
+ * Written, tested, and released.
+ */
+
+int udf_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+ struct inode *dir = filp->f_dentry->d_inode;
+ int result;
+
+ if ( filp->f_pos == 0 )
+ {
+ if (filldir(dirent, ".", 1, filp->f_pos, dir->i_ino, DT_DIR) < 0)
+ return 0;
+ filp->f_pos ++;
+ }
+
+ result = do_udf_readdir(dir, filp, filldir, dirent);
+ UPDATE_ATIME(dir);
+ return result;
+}
+
+static int
+do_udf_readdir(struct inode * dir, struct file *filp, filldir_t filldir, void *dirent)
+{
+ struct udf_fileident_bh fibh;
+ struct fileIdentDesc *fi=NULL;
+ struct fileIdentDesc cfi;
+ int block, iblock;
+ loff_t nf_pos = filp->f_pos - 1;
+ int flen;
+ char fname[255];
+ char *nameptr;
+ uint16_t liu;
+ uint8_t lfi;
+ loff_t size = (udf_ext0_offset(dir) + dir->i_size) >> 2;
+ struct buffer_head * bh = NULL, * tmp, * bha[16];
+ lb_addr bloc, eloc;
+ uint32_t extoffset, elen, offset;
+ int i, num;
+ unsigned int dt_type;
+
+ if (nf_pos >= size)
+ return 0;
+
+ if (nf_pos == 0)
+ nf_pos = (udf_ext0_offset(dir) >> 2);
+
+ fibh.soffset = fibh.eoffset = (nf_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2;
+ if (inode_bmap(dir, nf_pos >> (dir->i_sb->s_blocksize_bits - 2),
+ &bloc, &extoffset, &eloc, &elen, &offset, &bh) == (EXT_RECORDED_ALLOCATED >> 30))
+ {
+ offset >>= dir->i_sb->s_blocksize_bits;
+ block = udf_get_lb_pblock(dir->i_sb, eloc, offset);
+ if ((++offset << dir->i_sb->s_blocksize_bits) < elen)
+ {
+ if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_SHORT)
+ extoffset -= sizeof(short_ad);
+ else if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_LONG)
+ extoffset -= sizeof(long_ad);
+ }
+ else
+ offset = 0;
+ }
+ else
+ {
+ udf_release_data(bh);
+ return -ENOENT;
+ }
+
+ if (!(fibh.sbh = fibh.ebh = udf_tread(dir->i_sb, block)))
+ {
+ udf_release_data(bh);
+ return -EIO;
+ }
+
+ if (!(offset & ((16 >> (dir->i_sb->s_blocksize_bits - 9))-1)))
+ {
+ i = 16 >> (dir->i_sb->s_blocksize_bits - 9);
+ if (i+offset > (elen >> dir->i_sb->s_blocksize_bits))
+ i = (elen >> dir->i_sb->s_blocksize_bits)-offset;
+ for (num=0; i>0; i--)
+ {
+ block = udf_get_lb_pblock(dir->i_sb, eloc, offset+i);
+ tmp = udf_tgetblk(dir->i_sb, block);
+ if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp))
+ bha[num++] = tmp;
+ else
+ brelse(tmp);
+ }
+ if (num)
+ {
+ ll_rw_block(READA, num, bha);
+ for (i=0; i<num; i++)
+ brelse(bha[i]);
+ }
+ }
+
+ while ( nf_pos < size )
+ {
+ filp->f_pos = nf_pos + 1;
+
+ fi = udf_fileident_read(dir, &nf_pos, &fibh, &cfi, &bloc, &extoffset, &eloc, &elen, &offset, &bh);
+
+ if (!fi)
+ {
+ if (fibh.sbh != fibh.ebh)
+ udf_release_data(fibh.ebh);
+ udf_release_data(fibh.sbh);
+ udf_release_data(bh);
+ return 0;
+ }
+
+ liu = le16_to_cpu(cfi.lengthOfImpUse);
+ lfi = cfi.lengthFileIdent;
+
+ if (fibh.sbh == fibh.ebh)
+ nameptr = fi->fileIdent + liu;
+ else
+ {
+ int poffset; /* Unpaded ending offset */
+
+ poffset = fibh.soffset + sizeof(struct fileIdentDesc) + liu + lfi;
+
+ if (poffset >= lfi)
+ nameptr = (char *)(fibh.ebh->b_data + poffset - lfi);
+ else
+ {
+ nameptr = fname;
+ memcpy(nameptr, fi->fileIdent + liu, lfi - poffset);
+ memcpy(nameptr + lfi - poffset, fibh.ebh->b_data, poffset);
+ }
+ }
+
+ if ( (cfi.fileCharacteristics & FID_FILE_CHAR_DELETED) != 0 )
+ {
+ if ( !UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNDELETE) )
+ continue;
+ }
+
+ if ( (cfi.fileCharacteristics & FID_FILE_CHAR_HIDDEN) != 0 )
+ {
+ if ( !UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNHIDE) )
+ continue;
+ }
+
+ if ( cfi.fileCharacteristics & FID_FILE_CHAR_PARENT )
+ {
+ iblock = udf_get_lb_pblock(dir->i_sb, UDF_I_LOCATION(filp->f_dentry->d_parent->d_inode), 0);
+ flen = 2;
+ memcpy(fname, "..", flen);
+ dt_type = DT_DIR;
+ }
+ else
+ {
+ iblock = udf_get_lb_pblock(dir->i_sb, lelb_to_cpu(cfi.icb.extLocation), 0);
+ flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi);
+ dt_type = DT_UNKNOWN;
+ }
+
+ if (flen)
+ {
+ if (filldir(dirent, fname, flen, filp->f_pos, iblock, dt_type) < 0)
+ {
+ if (fibh.sbh != fibh.ebh)
+ udf_release_data(fibh.ebh);
+ udf_release_data(fibh.sbh);
+ udf_release_data(bh);
+ return 0;
+ }
+ }
+ } /* end while */
+
+ filp->f_pos = nf_pos + 1;
+
+ if (fibh.sbh != fibh.ebh)
+ udf_release_data(fibh.ebh);
+ udf_release_data(fibh.sbh);
+ udf_release_data(bh);
+
+ return 0;
+}
diff --git a/fs/udf/directory.c b/fs/udf/directory.c
new file mode 100644
index 00000000000000..b17105c947cfb0
--- /dev/null
+++ b/fs/udf/directory.c
@@ -0,0 +1,327 @@
+/*
+ * directory.c
+ *
+ * PURPOSE
+ * Directory related functions
+ *
+ * CONTACTS
+ * E-mail regarding any portion of the Linux UDF file system should be
+ * directed to the development team mailing list (run by majordomo):
+ * linux_udf@hpesjro.fc.hp.com
+ *
+ * COPYRIGHT
+ * This file is distributed under the terms of the GNU General Public
+ * License (GPL). Copies of the GPL can be obtained from:
+ * ftp://prep.ai.mit.edu/pub/gnu/GPL
+ * Each contributing author retains all rights to their own work.
+ */
+
+#include "udfdecl.h"
+
+#include <linux/fs.h>
+#include <linux/string.h>
+
+uint8_t * udf_filead_read(struct inode *dir, uint8_t *tmpad, uint8_t ad_size,
+ lb_addr fe_loc, int *pos, int *offset, struct buffer_head **bh, int *error)
+{
+ int loffset = *offset;
+ int block;
+ uint8_t *ad;
+ int remainder;
+
+ *error = 0;
+
+ ad = (uint8_t *)(*bh)->b_data + *offset;
+ *offset += ad_size;
+
+ if (!ad)
+ {
+ udf_release_data(*bh);
+ *error = 1;
+ return NULL;
+ }
+
+ if (*offset == dir->i_sb->s_blocksize)
+ {
+ udf_release_data(*bh);
+ block = udf_get_lb_pblock(dir->i_sb, fe_loc, ++*pos);
+ if (!block)
+ return NULL;
+ if (!(*bh = udf_tread(dir->i_sb, block)))
+ return NULL;
+ }
+ else if (*offset > dir->i_sb->s_blocksize)
+ {
+ ad = tmpad;
+
+ remainder = dir->i_sb->s_blocksize - loffset;
+ memcpy((uint8_t *)ad, (*bh)->b_data + loffset, remainder);
+
+ udf_release_data(*bh);
+ block = udf_get_lb_pblock(dir->i_sb, fe_loc, ++*pos);
+ if (!block)
+ return NULL;
+ if (!((*bh) = udf_tread(dir->i_sb, block)))
+ return NULL;
+
+ memcpy((uint8_t *)ad + remainder, (*bh)->b_data, ad_size - remainder);
+ *offset = ad_size - remainder;
+ }
+ return ad;
+}
+
+struct fileIdentDesc *
+udf_fileident_read(struct inode *dir, loff_t *nf_pos,
+ struct udf_fileident_bh *fibh,
+ struct fileIdentDesc *cfi,
+ lb_addr *bloc, uint32_t *extoffset,
+ lb_addr *eloc, uint32_t *elen,
+ uint32_t *offset, struct buffer_head **bh)
+{
+ struct fileIdentDesc *fi;
+ int i, num, block;
+ struct buffer_head * tmp, * bha[16];
+
+ fibh->soffset = fibh->eoffset;
+
+ if (fibh->eoffset == dir->i_sb->s_blocksize)
+ {
+ int lextoffset = *extoffset;
+
+ if (udf_next_aext(dir, bloc, extoffset, eloc, elen, bh, 1) !=
+ (EXT_RECORDED_ALLOCATED >> 30))
+ {
+ return NULL;
+ }
+
+ block = udf_get_lb_pblock(dir->i_sb, *eloc, *offset);
+
+ (*offset) ++;
+
+ if ((*offset << dir->i_sb->s_blocksize_bits) >= *elen)
+ *offset = 0;
+ else
+ *extoffset = lextoffset;
+
+ udf_release_data(fibh->sbh);
+ if (!(fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block)))
+ return NULL;
+ fibh->soffset = fibh->eoffset = 0;
+
+ if (!(*offset & ((16 >> (dir->i_sb->s_blocksize_bits - 9))-1)))
+ {
+ i = 16 >> (dir->i_sb->s_blocksize_bits - 9);
+ if (i+*offset > (*elen >> dir->i_sb->s_blocksize_bits))
+ i = (*elen >> dir->i_sb->s_blocksize_bits)-*offset;
+ for (num=0; i>0; i--)
+ {
+ block = udf_get_lb_pblock(dir->i_sb, *eloc, *offset+i);
+ tmp = udf_tgetblk(dir->i_sb, block);
+ if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp))
+ bha[num++] = tmp;
+ else
+ brelse(tmp);
+ }
+ if (num)
+ {
+ ll_rw_block(READA, num, bha);
+ for (i=0; i<num; i++)
+ brelse(bha[i]);
+ }
+ }
+ }
+ else if (fibh->sbh != fibh->ebh)
+ {
+ udf_release_data(fibh->sbh);
+ fibh->sbh = fibh->ebh;
+ }
+
+ fi = udf_get_fileident(fibh->sbh->b_data, dir->i_sb->s_blocksize,
+ &(fibh->eoffset));
+
+ if (!fi)
+ return NULL;
+
+ *nf_pos += ((fibh->eoffset - fibh->soffset) >> 2);
+
+ if (fibh->eoffset <= dir->i_sb->s_blocksize)
+ {
+ memcpy((uint8_t *)cfi, (uint8_t *)fi, sizeof(struct fileIdentDesc));
+ }
+ else if (fibh->eoffset > dir->i_sb->s_blocksize)
+ {
+ int lextoffset = *extoffset;
+
+ if (udf_next_aext(dir, bloc, extoffset, eloc, elen, bh, 1) !=
+ (EXT_RECORDED_ALLOCATED >> 30))
+ {
+ return NULL;
+ }
+
+ block = udf_get_lb_pblock(dir->i_sb, *eloc, *offset);
+
+ (*offset) ++;
+
+ if ((*offset << dir->i_sb->s_blocksize_bits) >= *elen)
+ *offset = 0;
+ else
+ *extoffset = lextoffset;
+
+ fibh->soffset -= dir->i_sb->s_blocksize;
+ fibh->eoffset -= dir->i_sb->s_blocksize;
+
+ if (!(fibh->ebh = udf_tread(dir->i_sb, block)))
+ return NULL;
+
+ if (sizeof(struct fileIdentDesc) > - fibh->soffset)
+ {
+ int fi_len;
+
+ memcpy((uint8_t *)cfi, (uint8_t *)fi, - fibh->soffset);
+ memcpy((uint8_t *)cfi - fibh->soffset, fibh->ebh->b_data,
+ sizeof(struct fileIdentDesc) + fibh->soffset);
+
+ fi_len = (sizeof(struct fileIdentDesc) + cfi->lengthFileIdent +
+ le16_to_cpu(cfi->lengthOfImpUse) + 3) & ~3;
+
+ *nf_pos += ((fi_len - (fibh->eoffset - fibh->soffset)) >> 2);
+ fibh->eoffset = fibh->soffset + fi_len;
+ }
+ else
+ {
+ memcpy((uint8_t *)cfi, (uint8_t *)fi, sizeof(struct fileIdentDesc));
+ }
+ }
+ return fi;
+}
+
+struct fileIdentDesc *
+udf_get_fileident(void * buffer, int bufsize, int * offset)
+{
+ struct fileIdentDesc *fi;
+ int lengthThisIdent;
+ uint8_t * ptr;
+ int padlen;
+
+ if ( (!buffer) || (!offset) ) {
+ udf_debug("invalidparms\n, buffer=%p, offset=%p\n", buffer, offset);
+ return NULL;
+ }
+
+ ptr = buffer;
+
+ if ( (*offset > 0) && (*offset < bufsize) ) {
+ ptr += *offset;
+ }
+ fi=(struct fileIdentDesc *)ptr;
+ if (le16_to_cpu(fi->descTag.tagIdent) != TAG_IDENT_FID)
+ {
+ udf_debug("0x%x != TAG_IDENT_FID\n",
+ le16_to_cpu(fi->descTag.tagIdent));
+ udf_debug("offset: %u sizeof: %lu bufsize: %u\n",
+ *offset, (unsigned long)sizeof(struct fileIdentDesc), bufsize);
+ return NULL;
+ }
+ if ( (*offset + sizeof(struct fileIdentDesc)) > bufsize )
+ {
+ lengthThisIdent = sizeof(struct fileIdentDesc);
+ }
+ else
+ lengthThisIdent = sizeof(struct fileIdentDesc) +
+ fi->lengthFileIdent + le16_to_cpu(fi->lengthOfImpUse);
+
+ /* we need to figure padding, too! */
+ padlen = lengthThisIdent % UDF_NAME_PAD;
+ if (padlen)
+ lengthThisIdent += (UDF_NAME_PAD - padlen);
+ *offset = *offset + lengthThisIdent;
+
+ return fi;
+}
+
+extent_ad *
+udf_get_fileextent(void * buffer, int bufsize, int * offset)
+{
+ extent_ad * ext;
+ struct fileEntry *fe;
+ uint8_t * ptr;
+
+ if ( (!buffer) || (!offset) )
+ {
+ printk(KERN_ERR "udf: udf_get_fileextent() invalidparms\n");
+ return NULL;
+ }
+
+ fe = (struct fileEntry *)buffer;
+
+ if ( le16_to_cpu(fe->descTag.tagIdent) != TAG_IDENT_FE )
+ {
+ udf_debug("0x%x != TAG_IDENT_FE\n",
+ le16_to_cpu(fe->descTag.tagIdent));
+ return NULL;
+ }
+
+ ptr=(uint8_t *)(fe->extendedAttr) + le32_to_cpu(fe->lengthExtendedAttr);
+
+ if ( (*offset > 0) && (*offset < le32_to_cpu(fe->lengthAllocDescs)) )
+ {
+ ptr += *offset;
+ }
+
+ ext = (extent_ad *)ptr;
+
+ *offset = *offset + sizeof(extent_ad);
+ return ext;
+}
+
+short_ad *
+udf_get_fileshortad(void * buffer, int maxoffset, int *offset, int inc)
+{
+ short_ad * sa;
+ uint8_t * ptr;
+
+ if ( (!buffer) || (!offset) )
+ {
+ printk(KERN_ERR "udf: udf_get_fileshortad() invalidparms\n");
+ return NULL;
+ }
+
+ ptr = (uint8_t *)buffer;
+
+ if ( (*offset > 0) && (*offset < maxoffset) )
+ ptr += *offset;
+ else
+ return NULL;
+
+ if ((sa = (short_ad *)ptr)->extLength == 0)
+ return NULL;
+ else if (inc)
+ (*offset) += sizeof(short_ad);
+ return sa;
+}
+
+long_ad *
+udf_get_filelongad(void * buffer, int maxoffset, int * offset, int inc)
+{
+ long_ad * la;
+ uint8_t * ptr;
+
+ if ( (!buffer) || !(offset) )
+ {
+ printk(KERN_ERR "udf: udf_get_filelongad() invalidparms\n");
+ return NULL;
+ }
+
+ ptr = (uint8_t *)buffer;
+
+ if ( (*offset > 0) && (*offset < maxoffset) )
+ ptr += *offset;
+ else
+ return NULL;
+
+ if ((la = (long_ad *)ptr)->extLength == 0)
+ return NULL;
+ else if (inc)
+ (*offset) += sizeof(long_ad);
+ return la;
+}
diff --git a/fs/udf/ecma_167.h b/fs/udf/ecma_167.h
new file mode 100644
index 00000000000000..6e42f6fd6dc8aa
--- /dev/null
+++ b/fs/udf/ecma_167.h
@@ -0,0 +1,822 @@
+/*
+ * ecma_167.h
+ *
+ * This file is based on ECMA-167 3rd edition (June 1997)
+ * http://www.ecma.ch
+ *
+ * Copyright (c) 2001-2002 Ben Fennema <bfennema@falcon.csc.calpoly.edu>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <linux/types.h>
+
+#ifndef _ECMA_167_H
+#define _ECMA_167_H 1
+
+/* Character set specification (ECMA 167r3 1/7.2.1) */
+typedef struct
+{
+ uint8_t charSetType;
+ uint8_t charSetInfo[63];
+} __attribute__ ((packed)) charspec;
+
+/* Character Set Type (ECMA 167r3 1/7.2.1.1) */
+#define CHARSPEC_TYPE_CS0 0x00 /* (1/7.2.2) */
+#define CHARSPEC_TYPE_CS1 0x01 /* (1/7.2.3) */
+#define CHARSPEC_TYPE_CS2 0x02 /* (1/7.2.4) */
+#define CHARSPEC_TYPE_CS3 0x03 /* (1/7.2.5) */
+#define CHARSPEC_TYPE_CS4 0x04 /* (1/7.2.6) */
+#define CHARSPEC_TYPE_CS5 0x05 /* (1/7.2.7) */
+#define CHARSPEC_TYPE_CS6 0x06 /* (1/7.2.8) */
+#define CHARSPEC_TYPE_CS7 0x07 /* (1/7.2.9) */
+#define CHARSPEC_TYPE_CS8 0x08 /* (1/7.2.10) */
+
+typedef uint8_t dstring;
+
+/* Timestamp (ECMA 167r3 1/7.3) */
+typedef struct
+{
+ uint16_t typeAndTimezone;
+ int16_t year;
+ uint8_t month;
+ uint8_t day;
+ uint8_t hour;
+ uint8_t minute;
+ uint8_t second;
+ uint8_t centiseconds;
+ uint8_t hundredsOfMicroseconds;
+ uint8_t microseconds;
+} __attribute__ ((packed)) timestamp;
+
+/* Type and Time Zone (ECMA 167r3 1/7.3.1) */
+#define TIMESTAMP_TYPE_MASK 0xF000
+#define TIMESTAMP_TYPE_CUT 0x0000
+#define TIMESTAMP_TYPE_LOCAL 0x1000
+#define TIMESTAMP_TYPE_AGREEMENT 0x2000
+#define TIMESTAMP_TIMEZONE_MASK 0x0FFF
+
+/* Entity identifier (ECMA 167r3 1/7.4) */
+typedef struct
+{
+ uint8_t flags;
+ uint8_t ident[23];
+ uint8_t identSuffix[8];
+} __attribute__ ((packed)) regid;
+
+/* Flags (ECMA 167r3 1/7.4.1) */
+#define ENTITYID_FLAGS_DIRTY 0x00
+#define ENTITYID_FLAGS_PROTECTED 0x01
+
+/* Volume Structure Descriptor (ECMA 167r3 2/9.1) */
+#define VSD_STD_ID_LEN 5
+struct volStructDesc
+{
+ uint8_t structType;
+ uint8_t stdIdent[VSD_STD_ID_LEN];
+ uint8_t structVersion;
+ uint8_t structData[2041];
+} __attribute__ ((packed));
+
+/* Standard Identifier (EMCA 167r2 2/9.1.2) */
+#define VSD_STD_ID_NSR02 "NSR02" /* (3/9.1) */
+
+/* Standard Identifier (ECMA 167r3 2/9.1.2) */
+#define VSD_STD_ID_BEA01 "BEA01" /* (2/9.2) */
+#define VSD_STD_ID_BOOT2 "BOOT2" /* (2/9.4) */
+#define VSD_STD_ID_CD001 "CD001" /* (ECMA-119) */
+#define VSD_STD_ID_CDW02 "CDW02" /* (ECMA-168) */
+#define VSD_STD_ID_NSR03 "NSR03" /* (3/9.1) */
+#define VSD_STD_ID_TEA01 "TEA01" /* (2/9.3) */
+
+/* Beginning Extended Area Descriptor (ECMA 167r3 2/9.2) */
+struct beginningExtendedAreaDesc
+{
+ uint8_t structType;
+ uint8_t stdIdent[VSD_STD_ID_LEN];
+ uint8_t structVersion;
+ uint8_t structData[2041];
+} __attribute__ ((packed));
+
+/* Terminating Extended Area Descriptor (ECMA 167r3 2/9.3) */
+struct terminatingExtendedAreaDesc
+{
+ uint8_t structType;
+ uint8_t stdIdent[VSD_STD_ID_LEN];
+ uint8_t structVersion;
+ uint8_t structData[2041];
+} __attribute__ ((packed));
+
+/* Boot Descriptor (ECMA 167r3 2/9.4) */
+struct bootDesc
+{
+ uint8_t structType;
+ uint8_t stdIdent[VSD_STD_ID_LEN];
+ uint8_t structVersion;
+ uint8_t reserved1;
+ regid archType;
+ regid bootIdent;
+ uint32_t bootExtLocation;
+ uint32_t bootExtLength;
+ uint64_t loadAddress;
+ uint64_t startAddress;
+ timestamp descCreationDateAndTime;
+ uint16_t flags;
+ uint8_t reserved2[32];
+ uint8_t bootUse[1906];
+} __attribute__ ((packed));
+
+/* Flags (ECMA 167r3 2/9.4.12) */
+#define BOOT_FLAGS_ERASE 0x01
+
+/* Extent Descriptor (ECMA 167r3 3/7.1) */
+typedef struct
+{
+ uint32_t extLength;
+ uint32_t extLocation;
+} __attribute__ ((packed)) extent_ad;
+
+/* Descriptor Tag (ECMA 167r3 3/7.2) */
+typedef struct
+{
+ uint16_t tagIdent;
+ uint16_t descVersion;
+ uint8_t tagChecksum;
+ uint8_t reserved;
+ uint16_t tagSerialNum;
+ uint16_t descCRC;
+ uint16_t descCRCLength;
+ uint32_t tagLocation;
+} __attribute__ ((packed)) tag;
+
+/* Tag Identifier (ECMA 167r3 3/7.2.1) */
+#define TAG_IDENT_PVD 0x0001
+#define TAG_IDENT_AVDP 0x0002
+#define TAG_IDENT_VDP 0x0003
+#define TAG_IDENT_IUVD 0x0004
+#define TAG_IDENT_PD 0x0005
+#define TAG_IDENT_LVD 0x0006
+#define TAG_IDENT_USD 0x0007
+#define TAG_IDENT_TD 0x0008
+#define TAG_IDENT_LVID 0x0009
+
+/* NSR Descriptor (ECMA 167r3 3/9.1) */
+struct NSRDesc
+{
+ uint8_t structType;
+ uint8_t stdIdent[VSD_STD_ID_LEN];
+ uint8_t structVersion;
+ uint8_t reserved;
+ uint8_t structData[2040];
+} __attribute__ ((packed));
+
+/* Primary Volume Descriptor (ECMA 167r3 3/10.1) */
+struct primaryVolDesc
+{
+ tag descTag;
+ uint32_t volDescSeqNum;
+ uint32_t primaryVolDescNum;
+ dstring volIdent[32];
+ uint16_t volSeqNum;
+ uint16_t maxVolSeqNum;
+ uint16_t interchangeLvl;
+ uint16_t maxInterchangeLvl;
+ uint32_t charSetList;
+ uint32_t maxCharSetList;
+ dstring volSetIdent[128];
+ charspec descCharSet;
+ charspec explanatoryCharSet;
+ extent_ad volAbstract;
+ extent_ad volCopyright;
+ regid appIdent;
+ timestamp recordingDateAndTime;
+ regid impIdent;
+ uint8_t impUse[64];
+ uint32_t predecessorVolDescSeqLocation;
+ uint16_t flags;
+ uint8_t reserved[22];
+} __attribute__ ((packed));
+
+/* Flags (ECMA 167r3 3/10.1.21) */
+#define PVD_FLAGS_VSID_COMMON 0x0001
+
+/* Anchor Volume Descriptor Pointer (ECMA 167r3 3/10.2) */
+struct anchorVolDescPtr
+{
+ tag descTag;
+ extent_ad mainVolDescSeqExt;
+ extent_ad reserveVolDescSeqExt;
+ uint8_t reserved[480];
+} __attribute__ ((packed));
+
+/* Volume Descriptor Pointer (ECMA 167r3 3/10.3) */
+struct volDescPtr
+{
+ tag descTag;
+ uint32_t volDescSeqNum;
+ extent_ad nextVolDescSeqExt;
+ uint8_t reserved[484];
+} __attribute__ ((packed));
+
+/* Implementation Use Volume Descriptor (ECMA 167r3 3/10.4) */
+struct impUseVolDesc
+{
+ tag descTag;
+ uint32_t volDescSeqNum;
+ regid impIdent;
+ uint8_t impUse[460];
+} __attribute__ ((packed));
+
+/* Partition Descriptor (ECMA 167r3 3/10.5) */
+struct partitionDesc
+{
+ tag descTag;
+ uint32_t volDescSeqNum;
+ uint16_t partitionFlags;
+ uint16_t partitionNumber;
+ regid partitionContents;
+ uint8_t partitionContentsUse[128];
+ uint32_t accessType;
+ uint32_t partitionStartingLocation;
+ uint32_t partitionLength;
+ regid impIdent;
+ uint8_t impUse[128];
+ uint8_t reserved[156];
+} __attribute__ ((packed));
+
+/* Partition Flags (ECMA 167r3 3/10.5.3) */
+#define PD_PARTITION_FLAGS_ALLOC 0x0001
+
+/* Partition Contents (ECMA 167r2 3/10.5.3) */
+#define PD_PARTITION_CONTENTS_NSR02 "+NSR02"
+
+/* Partition Contents (ECMA 167r3 3/10.5.5) */
+#define PD_PARTITION_CONTENTS_FDC01 "+FDC01"
+#define PD_PARTITION_CONTENTS_CD001 "+CD001"
+#define PD_PARTITION_CONTENTS_CDW02 "+CDW02"
+#define PD_PARTITION_CONTENTS_NSR03 "+NSR03"
+
+/* Access Type (ECMA 167r3 3/10.5.7) */
+#define PD_ACCESS_TYPE_NONE 0x00000000
+#define PD_ACCESS_TYPE_READ_ONLY 0x00000001
+#define PD_ACCESS_TYPE_WRITE_ONCE 0x00000002
+#define PD_ACCESS_TYPE_REWRITABLE 0x00000003
+#define PD_ACCESS_TYPE_OVERWRITABLE 0x00000004
+
+/* Logical Volume Descriptor (ECMA 167r3 3/10.6) */
+struct logicalVolDesc
+{
+ tag descTag;
+ uint32_t volDescSeqNum;
+ charspec descCharSet;
+ dstring logicalVolIdent[128];
+ uint32_t logicalBlockSize;
+ regid domainIdent;
+ uint8_t logicalVolContentsUse[16];
+ uint32_t mapTableLength;
+ uint32_t numPartitionMaps;
+ regid impIdent;
+ uint8_t impUse[128];
+ extent_ad integritySeqExt;
+ uint8_t partitionMaps[0];
+} __attribute__ ((packed));
+
+/* Generic Partition Map (ECMA 167r3 3/10.7.1) */
+struct genericPartitionMap
+{
+ uint8_t partitionMapType;
+ uint8_t partitionMapLength;
+ uint8_t partitionMapping[0];
+} __attribute__ ((packed));
+
+/* Partition Map Type (ECMA 167r3 3/10.7.1.1) */
+#define GP_PARTITION_MAP_TYPE_UNDEF 0x00
+#define GP_PARTIITON_MAP_TYPE_1 0x01
+#define GP_PARTITION_MAP_TYPE_2 0x02
+
+/* Type 1 Partition Map (ECMA 167r3 3/10.7.2) */
+struct genericPartitionMap1
+{
+ uint8_t partitionMapType;
+ uint8_t partitionMapLength;
+ uint16_t volSeqNum;
+ uint16_t partitionNum;
+} __attribute__ ((packed));
+
+/* Type 2 Partition Map (ECMA 167r3 3/10.7.3) */
+struct genericPartitionMap2
+{
+ uint8_t partitionMapType;
+ uint8_t partitionMapLength;
+ uint8_t partitionIdent[62];
+} __attribute__ ((packed));
+
+/* Unallocated Space Descriptor (ECMA 167r3 3/10.8) */
+struct unallocSpaceDesc
+{
+ tag descTag;
+ uint32_t volDescSeqNum;
+ uint32_t numAllocDescs;
+ extent_ad allocDescs[0];
+} __attribute__ ((packed));
+
+/* Terminating Descriptor (ECMA 167r3 3/10.9) */
+struct terminatingDesc
+{
+ tag descTag;
+ uint8_t reserved[496];
+} __attribute__ ((packed));
+
+/* Logical Volume Integrity Descriptor (ECMA 167r3 3/10.10) */
+struct logicalVolIntegrityDesc
+{
+ tag descTag;
+ timestamp recordingDateAndTime;
+ uint32_t integrityType;
+ extent_ad nextIntegrityExt;
+ uint8_t logicalVolContentsUse[32];
+ uint32_t numOfPartitions;
+ uint32_t lengthOfImpUse;
+ uint32_t freeSpaceTable[0];
+ uint32_t sizeTable[0];
+ uint8_t impUse[0];
+} __attribute__ ((packed));
+
+/* Integrity Type (ECMA 167r3 3/10.10.3) */
+#define LVID_INTEGRITY_TYPE_OPEN 0x00000000
+#define LVID_INTEGRITY_TYPE_CLOSE 0x00000001
+
+/* Recorded Address (ECMA 167r3 4/7.1) */
+typedef struct
+{
+ uint32_t logicalBlockNum;
+ uint16_t partitionReferenceNum;
+} __attribute__ ((packed)) lb_addr;
+
+/* Short Allocation Descriptor (ECMA 167r3 4/14.14.1) */
+typedef struct
+{
+ uint32_t extLength;
+ uint32_t extPosition;
+} __attribute__ ((packed)) short_ad;
+
+/* Long Allocation Descriptor (ECMA 167r3 4/14.14.2) */
+typedef struct
+{
+ uint32_t extLength;
+ lb_addr extLocation;
+ uint8_t impUse[6];
+} __attribute__ ((packed)) long_ad;
+
+/* Extended Allocation Descriptor (ECMA 167r3 4/14.14.3) */
+typedef struct
+{
+ uint32_t extLength;
+ uint32_t recordedLength;
+ uint32_t informationLength;
+ lb_addr extLocation;
+} __attribute__ ((packed)) ext_ad;
+
+/* Descriptor Tag (ECMA 167r3 4/7.2 - See 3/7.2) */
+
+/* Tag Identifier (ECMA 167r3 4/7.2.1) */
+#define TAG_IDENT_FSD 0x0100
+#define TAG_IDENT_FID 0x0101
+#define TAG_IDENT_AED 0x0102
+#define TAG_IDENT_IE 0x0103
+#define TAG_IDENT_TE 0x0104
+#define TAG_IDENT_FE 0x0105
+#define TAG_IDENT_EAHD 0x0106
+#define TAG_IDENT_USE 0x0107
+#define TAG_IDENT_SBD 0x0108
+#define TAG_IDENT_PIE 0x0109
+#define TAG_IDENT_EFE 0x010A
+
+/* File Set Descriptor (ECMA 167r3 4/14.1) */
+struct fileSetDesc
+{
+ tag descTag;
+ timestamp recordingDateAndTime;
+ uint16_t interchangeLvl;
+ uint16_t maxInterchangeLvl;
+ uint32_t charSetList;
+ uint32_t maxCharSetList;
+ uint32_t fileSetNum;
+ uint32_t fileSetDescNum;
+ charspec logicalVolIdentCharSet;
+ dstring logicalVolIdent[128];
+ charspec fileSetCharSet;
+ dstring fileSetIdent[32];
+ dstring copyrightFileIdent[32];
+ dstring abstractFileIdent[32];
+ long_ad rootDirectoryICB;
+ regid domainIdent;
+ long_ad nextExt;
+ long_ad streamDirectoryICB;
+ uint8_t reserved[32];
+} __attribute__ ((packed));
+
+/* Partition Header Descriptor (ECMA 167r3 4/14.3) */
+struct partitionHeaderDesc
+{
+ short_ad unallocSpaceTable;
+ short_ad unallocSpaceBitmap;
+ short_ad partitionIntegrityTable;
+ short_ad freedSpaceTable;
+ short_ad freedSpaceBitmap;
+ uint8_t reserved[88];
+} __attribute__ ((packed));
+
+/* File Identifier Descriptor (ECMA 167r3 4/14.4) */
+struct fileIdentDesc
+{
+ tag descTag;
+ uint16_t fileVersionNum;
+ uint8_t fileCharacteristics;
+ uint8_t lengthFileIdent;
+ long_ad icb;
+ uint16_t lengthOfImpUse;
+ uint8_t impUse[0];
+ uint8_t fileIdent[0];
+ uint8_t padding[0];
+} __attribute__ ((packed));
+
+/* File Characteristics (ECMA 167r3 4/14.4.3) */
+#define FID_FILE_CHAR_HIDDEN 0x01
+#define FID_FILE_CHAR_DIRECTORY 0x02
+#define FID_FILE_CHAR_DELETED 0x04
+#define FID_FILE_CHAR_PARENT 0x08
+#define FID_FILE_CHAR_METADATA 0x10
+
+/* Allocation Ext Descriptor (ECMA 167r3 4/14.5) */
+struct allocExtDesc
+{
+ tag descTag;
+ uint32_t previousAllocExtLocation;
+ uint32_t lengthAllocDescs;
+} __attribute__ ((packed));
+
+/* ICB Tag (ECMA 167r3 4/14.6) */
+typedef struct
+{
+ uint32_t priorRecordedNumDirectEntries;
+ uint16_t strategyType;
+ uint16_t strategyParameter;
+ uint16_t numEntries;
+ uint8_t reserved;
+ uint8_t fileType;
+ lb_addr parentICBLocation;
+ uint16_t flags;
+} __attribute__ ((packed)) icbtag;
+
+/* Strategy Type (ECMA 167r3 4/14.6.2) */
+#define ICBTAG_STRATEGY_TYPE_UNDEF 0x0000
+#define ICBTAG_STRATEGY_TYPE_1 0x0001
+#define ICBTAG_STRATEGY_TYPE_2 0x0002
+#define ICBTAG_STRATEGY_TYPE_3 0x0003
+#define ICBTAG_STRATEGY_TYPE_4 0x0004
+
+/* File Type (ECMA 167r3 4/14.6.6) */
+#define ICBTAG_FILE_TYPE_UNDEF 0x00
+#define ICBTAG_FILE_TYPE_USE 0x01
+#define ICBTAG_FILE_TYPE_PIE 0x02
+#define ICBTAG_FILE_TYPE_IE 0x03
+#define ICBTAG_FILE_TYPE_DIRECTORY 0x04
+#define ICBTAG_FILE_TYPE_REGULAR 0x05
+#define ICBTAG_FILE_TYPE_BLOCK 0x06
+#define ICBTAG_FILE_TYPE_CHAR 0x07
+#define ICBTAG_FILE_TYPE_EA 0x08
+#define ICBTAG_FILE_TYPE_FIFO 0x09
+#define ICBTAG_FILE_TYPE_SOCKET 0x0A
+#define ICBTAG_FILE_TYPE_TE 0x0B
+#define ICBTAG_FILE_TYPE_SYMLINK 0x0C
+#define ICBTAG_FILE_TYPE_STREAMDIR 0x0D
+
+/* Flags (ECMA 167r3 4/14.6.8) */
+#define ICBTAG_FLAG_AD_MASK 0x0007
+#define ICBTAG_FLAG_AD_SHORT 0x0000
+#define ICBTAG_FLAG_AD_LONG 0x0001
+#define ICBTAG_FLAG_AD_EXTENDED 0x0002
+#define ICBTAG_FLAG_AD_IN_ICB 0x0003
+#define ICBTAG_FLAG_SORTED 0x0008
+#define ICBTAG_FLAG_NONRELOCATABLE 0x0010
+#define ICBTAG_FLAG_ARCHIVE 0x0020
+#define ICBTAG_FLAG_SETUID 0x0040
+#define ICBTAG_FLAG_SETGID 0x0080
+#define ICBTAG_FLAG_STICKY 0x0100
+#define ICBTAG_FLAG_CONTIGUOUS 0x0200
+#define ICBTAG_FLAG_SYSTEM 0x0400
+#define ICBTAG_FLAG_TRANSFORMED 0x0800
+#define ICBTAG_FLAG_MULTIVERSIONS 0x1000
+#define ICBTAG_FLAG_STREAM 0x2000
+
+/* Indirect Entry (ECMA 167r3 4/14.7) */
+struct indirectEntry
+{
+ tag descTag;
+ icbtag icbTag;
+ long_ad indirectICB;
+} __attribute__ ((packed));
+
+/* Terminal Entry (ECMA 167r3 4/14.8) */
+struct terminalEntry
+{
+ tag descTag;
+ icbtag icbTag;
+} __attribute__ ((packed));
+
+/* File Entry (ECMA 167r3 4/14.9) */
+struct fileEntry
+{
+ tag descTag;
+ icbtag icbTag;
+ uint32_t uid;
+ uint32_t gid;
+ uint32_t permissions;
+ uint16_t fileLinkCount;
+ uint8_t recordFormat;
+ uint8_t recordDisplayAttr;
+ uint32_t recordLength;
+ uint64_t informationLength;
+ uint64_t logicalBlocksRecorded;
+ timestamp accessTime;
+ timestamp modificationTime;
+ timestamp attrTime;
+ uint32_t checkpoint;
+ long_ad extendedAttrICB;
+ regid impIdent;
+ uint64_t uniqueID;
+ uint32_t lengthExtendedAttr;
+ uint32_t lengthAllocDescs;
+ uint8_t extendedAttr[0];
+ uint8_t allocDescs[0];
+} __attribute__ ((packed));
+
+/* Permissions (ECMA 167r3 4/14.9.5) */
+#define FE_PERM_O_EXEC 0x00000001U
+#define FE_PERM_O_WRITE 0x00000002U
+#define FE_PERM_O_READ 0x00000004U
+#define FE_PERM_O_CHATTR 0x00000008U
+#define FE_PERM_O_DELETE 0x00000010U
+#define FE_PERM_G_EXEC 0x00000020U
+#define FE_PERM_G_WRITE 0x00000040U
+#define FE_PERM_G_READ 0x00000080U
+#define FE_PERM_G_CHATTR 0x00000100U
+#define FE_PERM_G_DELETE 0x00000200U
+#define FE_PERM_U_EXEC 0x00000400U
+#define FE_PERM_U_WRITE 0x00000800U
+#define FE_PERM_U_READ 0x00001000U
+#define FE_PERM_U_CHATTR 0x00002000U
+#define FE_PERM_U_DELETE 0x00004000U
+
+/* Record Format (ECMA 167r3 4/14.9.7) */
+#define FE_RECORD_FMT_UNDEF 0x00
+#define FE_RECORD_FMT_FIXED_PAD 0x01
+#define FE_RECORD_FMT_FIXED 0x02
+#define FE_RECORD_FMT_VARIABLE8 0x03
+#define FE_RECORD_FMT_VARIABLE16 0x04
+#define FE_RECORD_FMT_VARIABLE16_MSB 0x05
+#define FE_RECORD_FMT_VARIABLE32 0x06
+#define FE_RECORD_FMT_PRINT 0x07
+#define FE_RECORD_FMT_LF 0x08
+#define FE_RECORD_FMT_CR 0x09
+#define FE_RECORD_FMT_CRLF 0x0A
+#define FE_RECORD_FMT_LFCR 0x0B
+
+#define Record Display Attributes (ECMA 167r3 4/14.9.8) */
+#define FE_RECORD_DISPLAY_ATTR_UNDEF 0x00
+#define FE_RECORD_DISPLAY_ATTR_1 0x01
+#define FE_RECORD_DISPLAY_ATTR_2 0x02
+#define FE_RECORD_DISPLAY_ATTR_3 0x03
+
+/* Extended Attribute Header Descriptor (ECMA 167r3 4/14.10.1) */
+struct extendedAttrHeaderDesc
+{
+ tag descTag;
+ uint32_t impAttrLocation;
+ uint32_t appAttrLocation;
+} __attribute__ ((packed));
+
+/* Generic Format (ECMA 167r3 4/14.10.2) */
+struct genericFormat
+{
+ uint32_t attrType;
+ uint8_t attrSubtype;
+ uint8_t reserved[3];
+ uint32_t attrLength;
+ uint8_t attrData[0];
+} __attribute__ ((packed));
+
+/* Character Set Information (ECMA 167r3 4/14.10.3) */
+struct charSetInfo
+{
+ uint32_t attrType;
+ uint8_t attrSubtype;
+ uint8_t reserved[3];
+ uint32_t attrLength;
+ uint32_t escapeSeqLength;
+ uint8_t charSetType;
+ uint8_t escapeSeq[0];
+} __attribute__ ((packed));
+
+/* Alternate Permissions (ECMA 167r3 4/14.10.4) */
+struct altPerms
+{
+ uint32_t attrType;
+ uint8_t attrSubtype;
+ uint8_t reserved[3];
+ uint32_t attrLength;
+ uint16_t ownerIdent;
+ uint16_t groupIdent;
+ uint16_t permission;
+} __attribute__ ((packed));
+
+/* File Times Extended Attribute (ECMA 167r3 4/14.10.5) */
+struct fileTimesExtAttr
+{
+ uint32_t attrType;
+ uint8_t attrSubtype;
+ uint8_t reserved[3];
+ uint32_t attrLength;
+ uint32_t dataLength;
+ uint32_t fileTimeExistence;
+ uint8_t fileTimes;
+} __attribute__ ((packed));
+
+/* FileTimeExistence (ECMA 167r3 4/14.10.5.6) */
+#define FTE_CREATION 0x00000001
+#define FTE_DELETION 0x00000004
+#define FTE_EFFECTIVE 0x00000008
+#define FTE_BACKUP 0x00000002
+
+/* Information Times Extended Attribute (ECMA 167r3 4/14.10.6) */
+struct infoTimesExtAttr
+{
+ uint32_t attrType;
+ uint8_t attrSubtype;
+ uint8_t reserved[3];
+ uint32_t attrLength;
+ uint32_t dataLength;
+ uint32_t infoTimeExistence;
+ uint8_t infoTimes[0];
+} __attribute__ ((packed));
+
+/* Device Specification (ECMA 167r3 4/14.10.7) */
+struct deviceSpec
+{
+ uint32_t attrType;
+ uint8_t attrSubtype;
+ uint8_t reserved[3];
+ uint32_t attrLength;
+ uint32_t impUseLength;
+ uint32_t majorDeviceIdent;
+ uint32_t minorDeviceIdent;
+ uint8_t impUse[0];
+} __attribute__ ((packed));
+
+/* Implementation Use Extended Attr (ECMA 167r3 4/14.10.8) */
+struct impUseExtAttr
+{
+ uint32_t attrType;
+ uint8_t attrSubtype;
+ uint8_t reserved[3];
+ uint32_t attrLength;
+ uint32_t impUseLength;
+ regid impIdent;
+ uint8_t impUse[0];
+} __attribute__ ((packed));
+
+/* Application Use Extended Attribute (ECMA 167r3 4/14.10.9) */
+struct appUseExtAttr
+{
+ uint32_t attrType;
+ uint8_t attrSubtype;
+ uint8_t reserved[3];
+ uint32_t attrLength;
+ uint32_t appUseLength;
+ regid appIdent;
+ uint8_t appUse[0];
+} __attribute__ ((packed));
+
+#define EXTATTR_CHAR_SET 1
+#define EXTATTR_ALT_PERMS 3
+#define EXTATTR_FILE_TIMES 5
+#define EXTATTR_INFO_TIMES 6
+#define EXTATTR_DEV_SPEC 12
+#define EXTATTR_IMP_USE 2048
+#define EXTATTR_APP_USE 65536
+
+
+/* Unallocated Space Entry (ECMA 167r3 4/14.11) */
+struct unallocSpaceEntry
+{
+ tag descTag;
+ icbtag icbTag;
+ uint32_t lengthAllocDescs;
+ uint8_t allocDescs[0];
+} __attribute__ ((packed));
+
+/* Space Bitmap Descriptor (ECMA 167r3 4/14.12) */
+struct spaceBitmapDesc
+{
+ tag descTag;
+ uint32_t numOfBits;
+ uint32_t numOfBytes;
+ uint8_t bitmap[0];
+} __attribute__ ((packed));
+
+/* Partition Integrity Entry (ECMA 167r3 4/14.13) */
+struct partitionIntegrityEntry
+{
+ tag descTag;
+ icbtag icbTag;
+ timestamp recordingDateAndTime;
+ uint8_t integrityType;
+ uint8_t reserved[175];
+ regid impIdent;
+ uint8_t impUse[256];
+} __attribute__ ((packed));
+
+/* Short Allocation Descriptor (ECMA 167r3 4/14.14.1) */
+
+/* Extent Length (ECMA 167r3 4/14.14.1.1) */
+#define EXT_RECORDED_ALLOCATED 0x00000000
+#define EXT_NOT_RECORDED_ALLOCATED 0x40000000
+#define EXT_NOT_RECORDED_NOT_ALLOCATED 0x80000000
+#define EXT_NEXT_EXTENT_ALLOCDECS 0xC0000000
+
+/* Long Allocation Descriptor (ECMA 167r3 4/14.14.2) */
+
+/* Extended Allocation Descriptor (ECMA 167r3 4/14.14.3) */
+
+/* Logical Volume Header Descriptor (ECMA 167r3 4/14.15) */
+struct logicalVolHeaderDesc
+{
+ uint64_t uniqueID;
+ uint8_t reserved[24];
+} __attribute__ ((packed));
+
+/* Path Component (ECMA 167r3 4/14.16.1) */
+struct pathComponent
+{
+ uint8_t componentType;
+ uint8_t lengthComponentIdent;
+ uint16_t componentFileVersionNum;
+ dstring componentIdent[0];
+} __attribute__ ((packed));
+
+/* File Entry (ECMA 167r3 4/14.17) */
+struct extendedFileEntry
+{
+ tag descTag;
+ icbtag icbTag;
+ uint32_t uid;
+ uint32_t gid;
+ uint32_t permissions;
+ uint16_t fileLinkCount;
+ uint8_t recordFormat;
+ uint8_t recordDisplayAttr;
+ uint32_t recordLength;
+ uint64_t informationLength;
+ uint64_t objectSize;
+ uint64_t logicalBlocksRecorded;
+ timestamp accessTime;
+ timestamp modificationTime;
+ timestamp createTime;
+ timestamp attrTime;
+ uint32_t checkpoint;
+ uint32_t reserved;
+ long_ad extendedAttrICB;
+ long_ad streamDirectoryICB;
+ regid impIdent;
+ uint64_t uniqueID;
+ uint32_t lengthExtendedAttr;
+ uint32_t lengthAllocDescs;
+ uint8_t extendedAttr[0];
+ uint8_t allocDescs[0];
+} __attribute__ ((packed));
+
+#endif /* _ECMA_167_H */
diff --git a/fs/udf/file.c b/fs/udf/file.c
new file mode 100644
index 00000000000000..1cb047724b0bdf
--- /dev/null
+++ b/fs/udf/file.c
@@ -0,0 +1,375 @@
+/*
+ * file.c
+ *
+ * PURPOSE
+ * File handling routines for the OSTA-UDF(tm) filesystem.
+ *
+ * CONTACTS
+ * E-mail regarding any portion of the Linux UDF file system should be
+ * directed to the development team mailing list (run by majordomo):
+ * linux_udf@hpesjro.fc.hp.com
+ *
+ * COPYRIGHT
+ * This file is distributed under the terms of the GNU General Public
+ * License (GPL). Copies of the GPL can be obtained from:
+ * ftp://prep.ai.mit.edu/pub/gnu/GPL
+ * Each contributing author retains all rights to their own work.
+ *
+ * (C) 1998-1999 Dave Boynton
+ * (C) 1998-2001 Ben Fennema
+ * (C) 1999-2000 Stelias Computing Inc
+ *
+ * HISTORY
+ *
+ * 10/02/98 dgb Attempt to integrate into udf.o
+ * 10/07/98 Switched to using generic_readpage, etc., like isofs
+ * And it works!
+ * 12/06/98 blf Added udf_file_read. uses generic_file_read for all cases but
+ * ICBTAG_FLAG_AD_IN_ICB.
+ * 04/06/99 64 bit file handling on 32 bit systems taken from ext2 file.c
+ * 05/12/99 Preliminary file write support
+ */
+
+#include "udfdecl.h"
+#include <linux/fs.h>
+#include <linux/udf_fs.h>
+#include <asm/uaccess.h>
+#include <linux/kernel.h>
+#include <linux/string.h> /* memset */
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <linux/smp_lock.h>
+
+#include "udf_i.h"
+#include "udf_sb.h"
+
+static int udf_adinicb_readpage(struct file *file, struct page * page)
+{
+ struct inode *inode = page->mapping->host;
+
+ struct buffer_head *bh;
+ int block;
+ char *kaddr;
+ int err = 0;
+
+ if (!PageLocked(page))
+ PAGE_BUG(page);
+
+ kaddr = kmap(page);
+ memset(kaddr, 0, PAGE_CACHE_SIZE);
+ block = udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0);
+ bh = sb_bread(inode->i_sb, block);
+ if (!bh)
+ {
+ SetPageError(page);
+ err = -EIO;
+ goto out;
+ }
+ memcpy(kaddr, bh->b_data + udf_ext0_offset(inode), inode->i_size);
+ brelse(bh);
+ flush_dcache_page(page);
+ SetPageUptodate(page);
+out:
+ kunmap(page);
+ UnlockPage(page);
+ return err;
+}
+
+static int udf_adinicb_writepage(struct page *page)
+{
+ struct inode *inode = page->mapping->host;
+
+ struct buffer_head *bh;
+ int block;
+ char *kaddr;
+ int err = 0;
+
+ if (!PageLocked(page))
+ PAGE_BUG(page);
+
+ kaddr = kmap(page);
+ block = udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0);
+ bh = sb_bread(inode->i_sb, block);
+ if (!bh)
+ {
+ SetPageError(page);
+ err = -EIO;
+ goto out;
+ }
+ memcpy(bh->b_data + udf_ext0_offset(inode), kaddr, inode->i_size);
+ mark_buffer_dirty(bh);
+ brelse(bh);
+ SetPageUptodate(page);
+out:
+ kunmap(page);
+ UnlockPage(page);
+ return err;
+}
+
+static int udf_adinicb_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to)
+{
+ kmap(page);
+ return 0;
+}
+
+static int udf_adinicb_commit_write(struct file *file, struct page *page, unsigned offset, unsigned to)
+{
+ struct inode *inode = page->mapping->host;
+
+ struct buffer_head *bh;
+ int block;
+ char *kaddr = page_address(page);
+ int err = 0;
+
+ block = udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0);
+ bh = sb_bread(inode->i_sb, block);
+ if (!bh)
+ {
+ SetPageError(page);
+ err = -EIO;
+ goto out;
+ }
+ memcpy(bh->b_data + udf_file_entry_alloc_offset(inode) + offset,
+ kaddr + offset, to - offset);
+ mark_buffer_dirty(bh);
+ brelse(bh);
+ SetPageUptodate(page);
+out:
+ kunmap(page);
+ /* only one page here */
+ if (to > inode->i_size)
+ inode->i_size = to;
+ return err;
+}
+
+struct address_space_operations udf_adinicb_aops = {
+ readpage: udf_adinicb_readpage,
+ writepage: udf_adinicb_writepage,
+ sync_page: block_sync_page,
+ prepare_write: udf_adinicb_prepare_write,
+ commit_write: udf_adinicb_commit_write,
+};
+
+static ssize_t udf_file_write(struct file * file, const char * buf,
+ size_t count, loff_t *ppos)
+{
+ ssize_t retval;
+ struct inode *inode = file->f_dentry->d_inode;
+ int err;
+ loff_t pos;
+
+ if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB)
+ {
+ if (file->f_flags & O_APPEND)
+ pos = inode->i_size;
+ else
+ pos = *ppos;
+
+ if (pos < 0 || pos + count < pos)
+ return 0;
+
+ if (inode->i_sb->s_blocksize - udf_file_entry_alloc_offset(inode) <
+ pos + count)
+ {
+ udf_expand_file_adinicb(inode, pos + count, &err);
+ if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB)
+ {
+ udf_debug("udf_expand_adinicb: err=%d\n", err);
+ return err;
+ }
+ }
+ else
+ {
+ if (pos + count > inode->i_size)
+ UDF_I_LENALLOC(inode) = pos + count;
+ else
+ UDF_I_LENALLOC(inode) = inode->i_size;
+ }
+ }
+
+ retval = generic_file_write(file, buf, count, ppos);
+
+ if (retval > 0)
+ {
+ UDF_I_UCTIME(inode) = UDF_I_UMTIME(inode) = CURRENT_UTIME;
+ mark_inode_dirty(inode);
+ }
+ return retval;
+}
+
+/*
+ * udf_ioctl
+ *
+ * PURPOSE
+ * Issue an ioctl.
+ *
+ * DESCRIPTION
+ * Optional - sys_ioctl() will return -ENOTTY if this routine is not
+ * available, and the ioctl cannot be handled without filesystem help.
+ *
+ * sys_ioctl() handles these ioctls that apply only to regular files:
+ * FIBMAP [requires udf_block_map()], FIGETBSZ, FIONREAD
+ * These ioctls are also handled by sys_ioctl():
+ * FIOCLEX, FIONCLEX, FIONBIO, FIOASYNC
+ * All other ioctls are passed to the filesystem.
+ *
+ * Refer to sys_ioctl() in fs/ioctl.c
+ * sys_ioctl() -> .
+ *
+ * PRE-CONDITIONS
+ * inode Pointer to inode that ioctl was issued on.
+ * filp Pointer to file that ioctl was issued on.
+ * cmd The ioctl command.
+ * arg The ioctl argument [can be interpreted as a
+ * user-space pointer if desired].
+ *
+ * POST-CONDITIONS
+ * <return> Success (>=0) or an error code (<=0) that
+ * sys_ioctl() will return.
+ *
+ * HISTORY
+ * July 1, 1997 - Andrew E. Mileski
+ * Written, tested, and released.
+ */
+int udf_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ int result = -EINVAL;
+ struct buffer_head *bh = NULL;
+ long_ad eaicb;
+ uint8_t *ea = NULL;
+
+ if ( permission(inode, MAY_READ) != 0 )
+ {
+ udf_debug("no permission to access inode %lu\n",
+ inode->i_ino);
+ return -EPERM;
+ }
+
+ if ( !arg )
+ {
+ udf_debug("invalid argument to udf_ioctl\n");
+ return -EINVAL;
+ }
+
+ /* first, do ioctls that don't need to udf_read */
+ switch (cmd)
+ {
+ case UDF_GETVOLIDENT:
+ return copy_to_user((char *)arg,
+ UDF_SB_VOLIDENT(inode->i_sb), 32) ? -EFAULT : 0;
+ case UDF_RELOCATE_BLOCKS:
+ {
+ long old, new;
+
+ if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+ if (get_user(old, (long *)arg)) return -EFAULT;
+ if ((result = udf_relocate_blocks(inode->i_sb,
+ old, &new)) == 0)
+ result = put_user(new, (long *)arg);
+
+ return result;
+ }
+ }
+
+ /* ok, we need to read the inode */
+ bh = udf_tread(inode->i_sb,
+ udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0));
+
+ if (!bh)
+ {
+ udf_debug("bread failed (inode=%ld)\n", inode->i_ino);
+ return -EIO;
+ }
+
+ if (UDF_I_EXTENDED_FE(inode) == 0)
+ {
+ struct fileEntry *fe;
+
+ fe = (struct fileEntry *)bh->b_data;
+ eaicb = lela_to_cpu(fe->extendedAttrICB);
+ if (UDF_I_LENEATTR(inode))
+ ea = fe->extendedAttr;
+ }
+ else
+ {
+ struct extendedFileEntry *efe;
+
+ efe = (struct extendedFileEntry *)bh->b_data;
+ eaicb = lela_to_cpu(efe->extendedAttrICB);
+ if (UDF_I_LENEATTR(inode))
+ ea = efe->extendedAttr;
+ }
+
+ switch (cmd)
+ {
+ case UDF_GETEASIZE:
+ result = put_user(UDF_I_LENEATTR(inode), (int *)arg);
+ break;
+
+ case UDF_GETEABLOCK:
+ result = copy_to_user((char *)arg, ea,
+ UDF_I_LENEATTR(inode)) ? -EFAULT : 0;
+ break;
+ }
+
+ udf_release_data(bh);
+ return result;
+}
+
+/*
+ * udf_release_file
+ *
+ * PURPOSE
+ * Called when all references to the file are closed
+ *
+ * DESCRIPTION
+ * Discard prealloced blocks
+ *
+ * HISTORY
+ *
+ */
+static int udf_release_file(struct inode * inode, struct file * filp)
+{
+ if (filp->f_mode & FMODE_WRITE)
+ {
+ lock_kernel();
+ udf_discard_prealloc(inode);
+ unlock_kernel();
+ }
+ return 0;
+}
+
+/*
+ * udf_open_file
+ *
+ * PURPOSE
+ * Called when an inode is about to be open.
+ *
+ * DESCRIPTION
+ * Use this to disallow opening RW large files on 32 bit systems.
+ * On 64 bit systems we force on O_LARGEFILE in sys_open.
+ *
+ * HISTORY
+ *
+ */
+static int udf_open_file(struct inode * inode, struct file * filp)
+{
+ if ((inode->i_size & 0xFFFFFFFF80000000ULL) && !(filp->f_flags & O_LARGEFILE))
+ return -EFBIG;
+ return 0;
+}
+
+struct file_operations udf_file_operations = {
+ read: generic_file_read,
+ ioctl: udf_ioctl,
+ open: udf_open_file,
+ mmap: generic_file_mmap,
+ write: udf_file_write,
+ release: udf_release_file,
+ fsync: udf_fsync_file,
+};
+
+struct inode_operations udf_file_inode_operations = {
+ truncate: udf_truncate,
+};
diff --git a/fs/udf/fsync.c b/fs/udf/fsync.c
new file mode 100644
index 00000000000000..00d4c207f4fe8d
--- /dev/null
+++ b/fs/udf/fsync.c
@@ -0,0 +1,56 @@
+/*
+ * fsync.c
+ *
+ * PURPOSE
+ * Fsync handling routines for the OSTA-UDF(tm) filesystem.
+ *
+ * CONTACTS
+ * E-mail regarding any portion of the Linux UDF file system should be
+ * directed to the development team mailing list (run by majordomo):
+ * linux_udf@hpesjro.fc.hp.com
+ *
+ * COPYRIGHT
+ * This file is distributed under the terms of the GNU General Public
+ * License (GPL). Copies of the GPL can be obtained from:
+ * ftp://prep.ai.mit.edu/pub/gnu/GPL
+ * Each contributing author retains all rights to their own work.
+ *
+ * (C) 1999-2001 Ben Fennema
+ * (C) 1999-2000 Stelias Computing Inc
+ *
+ * HISTORY
+ *
+ * 05/22/99 blf Created.
+ */
+
+#include "udfdecl.h"
+
+#include <linux/fs.h>
+#include <linux/locks.h>
+#include <linux/smp_lock.h>
+
+/*
+ * File may be NULL when we are called. Perhaps we shouldn't
+ * even pass file to fsync ?
+ */
+
+int udf_fsync_file(struct file * file, struct dentry *dentry, int datasync)
+{
+ struct inode *inode = dentry->d_inode;
+ return udf_fsync_inode(inode, datasync);
+}
+
+int udf_fsync_inode(struct inode *inode, int datasync)
+{
+ int err;
+
+ err = fsync_inode_buffers(inode);
+ err |= fsync_inode_data_buffers(inode);
+ if (!(inode->i_state & I_DIRTY))
+ return err;
+ if (datasync && !(inode->i_state & I_DIRTY_DATASYNC))
+ return err;
+
+ err |= udf_sync_inode (inode);
+ return err ? -EIO : 0;
+}
diff --git a/fs/udf/ialloc.c b/fs/udf/ialloc.c
new file mode 100644
index 00000000000000..a533774e96f4ac
--- /dev/null
+++ b/fs/udf/ialloc.c
@@ -0,0 +1,167 @@
+/*
+ * ialloc.c
+ *
+ * PURPOSE
+ * Inode allocation handling routines for the OSTA-UDF(tm) filesystem.
+ *
+ * CONTACTS
+ * E-mail regarding any portion of the Linux UDF file system should be
+ * directed to the development team mailing list (run by majordomo):
+ * linux_udf@hpesjro.fc.hp.com
+ *
+ * COPYRIGHT
+ * This file is distributed under the terms of the GNU General Public
+ * License (GPL). Copies of the GPL can be obtained from:
+ * ftp://prep.ai.mit.edu/pub/gnu/GPL
+ * Each contributing author retains all rights to their own work.
+ *
+ * (C) 1998-2001 Ben Fennema
+ *
+ * HISTORY
+ *
+ * 02/24/99 blf Created.
+ *
+ */
+
+#include "udfdecl.h"
+#include <linux/fs.h>
+#include <linux/locks.h>
+#include <linux/quotaops.h>
+#include <linux/udf_fs.h>
+
+#include "udf_i.h"
+#include "udf_sb.h"
+
+void udf_free_inode(struct inode * inode)
+{
+ struct super_block * sb = inode->i_sb;
+ int is_directory;
+ unsigned long ino;
+
+ ino = inode->i_ino;
+
+ /*
+ * Note: we must free any quota before locking the superblock,
+ * as writing the quota to disk may need the lock as well.
+ */
+ DQUOT_FREE_INODE(inode);
+ DQUOT_DROP(inode);
+
+ lock_super(sb);
+
+ is_directory = S_ISDIR(inode->i_mode);
+
+ clear_inode(inode);
+
+ if (UDF_SB_LVIDBH(sb))
+ {
+ if (is_directory)
+ UDF_SB_LVIDIU(sb)->numDirs =
+ cpu_to_le32(le32_to_cpu(UDF_SB_LVIDIU(sb)->numDirs) - 1);
+ else
+ UDF_SB_LVIDIU(sb)->numFiles =
+ cpu_to_le32(le32_to_cpu(UDF_SB_LVIDIU(sb)->numFiles) - 1);
+
+ mark_buffer_dirty(UDF_SB_LVIDBH(sb));
+ }
+ unlock_super(sb);
+
+ udf_free_blocks(sb, NULL, UDF_I_LOCATION(inode), 0, 1);
+}
+
+struct inode * udf_new_inode (struct inode *dir, int mode, int * err)
+{
+ struct super_block *sb;
+ struct inode * inode;
+ int block;
+ uint32_t start = UDF_I_LOCATION(dir).logicalBlockNum;
+
+ sb = dir->i_sb;
+ inode = new_inode(sb);
+
+ if (!inode)
+ {
+ *err = -ENOMEM;
+ return NULL;
+ }
+ *err = -ENOSPC;
+
+ block = udf_new_block(dir->i_sb, NULL, UDF_I_LOCATION(dir).partitionReferenceNum,
+ start, err);
+ if (*err)
+ {
+ iput(inode);
+ return NULL;
+ }
+ lock_super(sb);
+
+ if (UDF_SB_LVIDBH(sb))
+ {
+ struct logicalVolHeaderDesc *lvhd;
+ uint64_t uniqueID;
+ lvhd = (struct logicalVolHeaderDesc *)(UDF_SB_LVID(sb)->logicalVolContentsUse);
+ if (S_ISDIR(mode))
+ UDF_SB_LVIDIU(sb)->numDirs =
+ cpu_to_le32(le32_to_cpu(UDF_SB_LVIDIU(sb)->numDirs) + 1);
+ else
+ UDF_SB_LVIDIU(sb)->numFiles =
+ cpu_to_le32(le32_to_cpu(UDF_SB_LVIDIU(sb)->numFiles) + 1);
+ UDF_I_UNIQUE(inode) = uniqueID = le64_to_cpu(lvhd->uniqueID);
+ if (!(++uniqueID & 0x00000000FFFFFFFFUL))
+ uniqueID += 16;
+ lvhd->uniqueID = cpu_to_le64(uniqueID);
+ mark_buffer_dirty(UDF_SB_LVIDBH(sb));
+ }
+ inode->i_mode = mode;
+ inode->i_uid = current->fsuid;
+ if (dir->i_mode & S_ISGID)
+ {
+ inode->i_gid = dir->i_gid;
+ if (S_ISDIR(mode))
+ mode |= S_ISGID;
+ }
+ else
+ inode->i_gid = current->fsgid;
+
+ UDF_I_LOCATION(inode).logicalBlockNum = block;
+ UDF_I_LOCATION(inode).partitionReferenceNum = UDF_I_LOCATION(dir).partitionReferenceNum;
+ inode->i_ino = udf_get_lb_pblock(sb, UDF_I_LOCATION(inode), 0);
+ inode->i_blksize = PAGE_SIZE;
+ inode->i_blocks = 0;
+ UDF_I_LENEATTR(inode) = 0;
+ UDF_I_LENALLOC(inode) = 0;
+ if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_EXTENDED_FE))
+ {
+ UDF_I_EXTENDED_FE(inode) = 1;
+ UDF_UPDATE_UDFREV(inode->i_sb, UDF_VERS_USE_EXTENDED_FE);
+ }
+ else
+ UDF_I_EXTENDED_FE(inode) = 0;
+ if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_AD_IN_ICB))
+ UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_IN_ICB;
+ else if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD))
+ UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_SHORT;
+ else
+ UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_LONG;
+ inode->i_mtime = inode->i_atime = inode->i_ctime =
+ UDF_I_CRTIME(inode) = CURRENT_TIME;
+ UDF_I_UMTIME(inode) = UDF_I_UCTIME(inode) =
+ UDF_I_UCRTIME(inode) = CURRENT_UTIME;
+ UDF_I_NEW_INODE(inode) = 1;
+ insert_inode_hash(inode);
+ mark_inode_dirty(inode);
+
+ unlock_super(sb);
+ if (DQUOT_ALLOC_INODE(inode))
+ {
+ DQUOT_DROP(inode);
+ inode->i_flags |= S_NOQUOTA;
+ inode->i_nlink = 0;
+ iput(inode);
+ *err = -EDQUOT;
+ return NULL;
+ }
+
+ *err = 0;
+ return inode;
+}
diff --git a/fs/udf/inode.c b/fs/udf/inode.c
new file mode 100644
index 00000000000000..72cdc308c8981d
--- /dev/null
+++ b/fs/udf/inode.c
@@ -0,0 +1,2217 @@
+/*
+ * inode.c
+ *
+ * PURPOSE
+ * Inode handling routines for the OSTA-UDF(tm) filesystem.
+ *
+ * CONTACTS
+ * E-mail regarding any portion of the Linux UDF file system should be
+ * directed to the development team mailing list (run by majordomo):
+ * linux_udf@hpesjro.fc.hp.com
+ *
+ * COPYRIGHT
+ * This file is distributed under the terms of the GNU General Public
+ * License (GPL). Copies of the GPL can be obtained from:
+ * ftp://prep.ai.mit.edu/pub/gnu/GPL
+ * Each contributing author retains all rights to their own work.
+ *
+ * (C) 1998 Dave Boynton
+ * (C) 1998-2001 Ben Fennema
+ * (C) 1999-2000 Stelias Computing Inc
+ *
+ * HISTORY
+ *
+ * 10/04/98 dgb Added rudimentary directory functions
+ * 10/07/98 Fully working udf_block_map! It works!
+ * 11/25/98 bmap altered to better support extents
+ * 12/06/98 blf partition support in udf_iget, udf_block_map and udf_read_inode
+ * 12/12/98 rewrote udf_block_map to handle next extents and descs across
+ * block boundaries (which is not actually allowed)
+ * 12/20/98 added support for strategy 4096
+ * 03/07/99 rewrote udf_block_map (again)
+ * New funcs, inode_bmap, udf_next_aext
+ * 04/19/99 Support for writing device EA's for major/minor #
+ */
+
+#include "udfdecl.h"
+#include <linux/locks.h>
+#include <linux/mm.h>
+#include <linux/smp_lock.h>
+#include <linux/module.h>
+
+#include "udf_i.h"
+#include "udf_sb.h"
+
+MODULE_AUTHOR("Ben Fennema");
+MODULE_DESCRIPTION("Universal Disk Format Filesystem");
+MODULE_LICENSE("GPL");
+
+#define EXTENT_MERGE_SIZE 5
+
+static mode_t udf_convert_permissions(struct fileEntry *);
+static int udf_update_inode(struct inode *, int);
+static void udf_fill_inode(struct inode *, struct buffer_head *);
+static struct buffer_head *inode_getblk(struct inode *, long, int *, long *, int *);
+static void udf_split_extents(struct inode *, int *, int, int,
+ long_ad [EXTENT_MERGE_SIZE], int *);
+static void udf_prealloc_extents(struct inode *, int, int,
+ long_ad [EXTENT_MERGE_SIZE], int *);
+static void udf_merge_extents(struct inode *,
+ long_ad [EXTENT_MERGE_SIZE], int *);
+static void udf_update_extents(struct inode *,
+ long_ad [EXTENT_MERGE_SIZE], int, int,
+ lb_addr, uint32_t, struct buffer_head **);
+static int udf_get_block(struct inode *, long, struct buffer_head *, int);
+
+/*
+ * udf_put_inode
+ *
+ * PURPOSE
+ *
+ * DESCRIPTION
+ * This routine is called whenever the kernel no longer needs the inode.
+ *
+ * HISTORY
+ * July 1, 1997 - Andrew E. Mileski
+ * Written, tested, and released.
+ *
+ * Called at each iput()
+ */
+void udf_put_inode(struct inode * inode)
+{
+ if (!(inode->i_sb->s_flags & MS_RDONLY))
+ {
+ lock_kernel();
+ udf_discard_prealloc(inode);
+ unlock_kernel();
+ }
+}
+
+/*
+ * udf_delete_inode
+ *
+ * PURPOSE
+ * Clean-up before the specified inode is destroyed.
+ *
+ * DESCRIPTION
+ * This routine is called when the kernel destroys an inode structure
+ * ie. when iput() finds i_count == 0.
+ *
+ * HISTORY
+ * July 1, 1997 - Andrew E. Mileski
+ * Written, tested, and released.
+ *
+ * Called at the last iput() if i_nlink is zero.
+ */
+void udf_delete_inode(struct inode * inode)
+{
+ lock_kernel();
+
+ if (is_bad_inode(inode))
+ goto no_delete;
+
+ inode->i_size = 0;
+ udf_truncate(inode);
+ udf_update_inode(inode, IS_SYNC(inode));
+ udf_free_inode(inode);
+
+ unlock_kernel();
+ return;
+no_delete:
+ unlock_kernel();
+ clear_inode(inode);
+}
+
+void udf_discard_prealloc(struct inode * inode)
+{
+ if (inode->i_size && inode->i_size != UDF_I_LENEXTENTS(inode) &&
+ UDF_I_ALLOCTYPE(inode) != ICBTAG_FLAG_AD_IN_ICB)
+ {
+ udf_truncate_extents(inode);
+ }
+}
+
+static int udf_writepage(struct page *page)
+{
+ return block_write_full_page(page, udf_get_block);
+}
+
+static int udf_readpage(struct file *file, struct page *page)
+{
+ return block_read_full_page(page, udf_get_block);
+}
+
+static int udf_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
+{
+ return block_prepare_write(page, from, to, udf_get_block);
+}
+
+static int udf_bmap(struct address_space *mapping, long block)
+{
+ return generic_block_bmap(mapping,block,udf_get_block);
+}
+
+struct address_space_operations udf_aops = {
+ readpage: udf_readpage,
+ writepage: udf_writepage,
+ sync_page: block_sync_page,
+ prepare_write: udf_prepare_write,
+ commit_write: generic_commit_write,
+ bmap: udf_bmap,
+};
+
+void udf_expand_file_adinicb(struct inode * inode, int newsize, int * err)
+{
+ struct buffer_head *bh = NULL;
+ struct page *page;
+ char *kaddr;
+ int block;
+
+ /* from now on we have normal address_space methods */
+ inode->i_data.a_ops = &udf_aops;
+
+ if (!UDF_I_LENALLOC(inode))
+ {
+ if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD))
+ UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_SHORT;
+ else
+ UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_LONG;
+ mark_inode_dirty(inode);
+ return;
+ }
+
+ block = udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0);
+ bh = udf_tread(inode->i_sb, block);
+ if (!bh)
+ return;
+ page = grab_cache_page(inode->i_mapping, 0);
+ if (!PageLocked(page))
+ PAGE_BUG(page);
+ if (!Page_Uptodate(page))
+ {
+ kaddr = kmap(page);
+ memset(kaddr + UDF_I_LENALLOC(inode), 0x00,
+ PAGE_CACHE_SIZE - UDF_I_LENALLOC(inode));
+ memcpy(kaddr, bh->b_data + udf_file_entry_alloc_offset(inode),
+ UDF_I_LENALLOC(inode));
+ flush_dcache_page(page);
+ SetPageUptodate(page);
+ kunmap(page);
+ }
+ memset(bh->b_data + udf_file_entry_alloc_offset(inode),
+ 0, UDF_I_LENALLOC(inode));
+ UDF_I_LENALLOC(inode) = 0;
+ if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD))
+ UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_SHORT;
+ else
+ UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_LONG;
+ mark_buffer_dirty_inode(bh, inode);
+ udf_release_data(bh);
+
+ inode->i_data.a_ops->writepage(page);
+ page_cache_release(page);
+
+ mark_inode_dirty(inode);
+ inode->i_version ++;
+}
+
+struct buffer_head * udf_expand_dir_adinicb(struct inode *inode, int *block, int *err)
+{
+ int newblock;
+ struct buffer_head *sbh = NULL, *dbh = NULL;
+ lb_addr bloc, eloc;
+ uint32_t elen, extoffset;
+
+ struct udf_fileident_bh sfibh, dfibh;
+ loff_t f_pos = udf_ext0_offset(inode) >> 2;
+ int size = (udf_ext0_offset(inode) + inode->i_size) >> 2;
+ struct fileIdentDesc cfi, *sfi, *dfi;
+
+ if (!inode->i_size)
+ {
+ if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD))
+ UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_SHORT;
+ else
+ UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_LONG;
+ mark_inode_dirty(inode);
+ return NULL;
+ }
+
+ /* alloc block, and copy data to it */
+ *block = udf_new_block(inode->i_sb, inode,
+ UDF_I_LOCATION(inode).partitionReferenceNum,
+ UDF_I_LOCATION(inode).logicalBlockNum, err);
+
+ if (!(*block))
+ return NULL;
+ newblock = udf_get_pblock(inode->i_sb, *block,
+ UDF_I_LOCATION(inode).partitionReferenceNum, 0);
+ if (!newblock)
+ return NULL;
+ sbh = udf_tread(inode->i_sb, inode->i_ino);
+ if (!sbh)
+ return NULL;
+ dbh = udf_tgetblk(inode->i_sb, newblock);
+ if (!dbh)
+ return NULL;
+ lock_buffer(dbh);
+ memset(dbh->b_data, 0x00, inode->i_sb->s_blocksize);
+ mark_buffer_uptodate(dbh, 1);
+ unlock_buffer(dbh);
+ mark_buffer_dirty_inode(dbh, inode);
+
+ sfibh.soffset = sfibh.eoffset = (f_pos & ((inode->i_sb->s_blocksize - 1) >> 2)) << 2;
+ sfibh.sbh = sfibh.ebh = sbh;
+ dfibh.soffset = dfibh.eoffset = 0;
+ dfibh.sbh = dfibh.ebh = dbh;
+ while ( (f_pos < size) )
+ {
+ sfi = udf_fileident_read(inode, &f_pos, &sfibh, &cfi, NULL, NULL, NULL, NULL, NULL, NULL);
+ if (!sfi)
+ {
+ udf_release_data(sbh);
+ udf_release_data(dbh);
+ return NULL;
+ }
+ sfi->descTag.tagLocation = *block;
+ dfibh.soffset = dfibh.eoffset;
+ dfibh.eoffset += (sfibh.eoffset - sfibh.soffset);
+ dfi = (struct fileIdentDesc *)(dbh->b_data + dfibh.soffset);
+ if (udf_write_fi(inode, sfi, dfi, &dfibh, sfi->impUse,
+ sfi->fileIdent + sfi->lengthOfImpUse))
+ {
+ udf_release_data(sbh);
+ udf_release_data(dbh);
+ return NULL;
+ }
+ }
+ mark_buffer_dirty_inode(dbh, inode);
+
+ memset(sbh->b_data + udf_file_entry_alloc_offset(inode),
+ 0, UDF_I_LENALLOC(inode));
+
+ UDF_I_LENALLOC(inode) = 0;
+ if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD))
+ UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_SHORT;
+ else
+ UDF_I_ALLOCTYPE(inode) = ICBTAG_FLAG_AD_LONG;
+ bloc = UDF_I_LOCATION(inode);
+ eloc.logicalBlockNum = *block;
+ eloc.partitionReferenceNum = UDF_I_LOCATION(inode).partitionReferenceNum;
+ elen = inode->i_size;
+ UDF_I_LENEXTENTS(inode) = elen;
+ extoffset = udf_file_entry_alloc_offset(inode);
+ udf_add_aext(inode, &bloc, &extoffset, eloc, elen, &sbh, 0);
+ /* UniqueID stuff */
+
+ mark_buffer_dirty(sbh);
+ udf_release_data(sbh);
+ mark_inode_dirty(inode);
+ inode->i_version ++;
+ return dbh;
+}
+
+static int udf_get_block(struct inode *inode, long block, struct buffer_head *bh_result, int create)
+{
+ int err, new;
+ struct buffer_head *bh;
+ unsigned long phys;
+
+ if (!create)
+ {
+ phys = udf_block_map(inode, block);
+ if (phys)
+ {
+ bh_result->b_dev = inode->i_dev;
+ bh_result->b_blocknr = phys;
+ bh_result->b_state |= (1UL << BH_Mapped);
+ }
+ return 0;
+ }
+
+ err = -EIO;
+ new = 0;
+ bh = NULL;
+
+ lock_kernel();
+
+ if (block < 0)
+ goto abort_negative;
+
+ if (block == UDF_I_NEXT_ALLOC_BLOCK(inode) + 1)
+ {
+ UDF_I_NEXT_ALLOC_BLOCK(inode) ++;
+ UDF_I_NEXT_ALLOC_GOAL(inode) ++;
+ }
+
+ err = 0;
+
+ bh = inode_getblk(inode, block, &err, &phys, &new);
+ if (bh)
+ BUG();
+ if (err)
+ goto abort;
+ if (!phys)
+ BUG();
+
+ bh_result->b_dev = inode->i_dev;
+ bh_result->b_blocknr = phys;
+ bh_result->b_state |= (1UL << BH_Mapped);
+ if (new)
+ bh_result->b_state |= (1UL << BH_New);
+abort:
+ unlock_kernel();
+ return err;
+
+abort_negative:
+ udf_warning(inode->i_sb, "udf_get_block", "block < 0");
+ goto abort;
+}
+
+struct buffer_head * udf_getblk(struct inode * inode, long block,
+ int create, int * err)
+{
+ struct buffer_head dummy;
+
+ dummy.b_state = 0;
+ dummy.b_blocknr = -1000;
+ *err = udf_get_block(inode, block, &dummy, create);
+ if (!*err && buffer_mapped(&dummy))
+ {
+ struct buffer_head *bh;
+ bh = sb_getblk(inode->i_sb, dummy.b_blocknr);
+ if (buffer_new(&dummy))
+ {
+ lock_buffer(bh);
+ memset(bh->b_data, 0x00, inode->i_sb->s_blocksize);
+ mark_buffer_uptodate(bh, 1);
+ unlock_buffer(bh);
+ mark_buffer_dirty_inode(bh, inode);
+ }
+ return bh;
+ }
+ return NULL;
+}
+
+static struct buffer_head * inode_getblk(struct inode * inode, long block,
+ int *err, long *phys, int *new)
+{
+ struct buffer_head *pbh = NULL, *cbh = NULL, *nbh = NULL, *result = NULL;
+ long_ad laarr[EXTENT_MERGE_SIZE];
+ uint32_t pextoffset = 0, cextoffset = 0, nextoffset = 0;
+ int count = 0, startnum = 0, endnum = 0;
+ uint32_t elen = 0;
+ lb_addr eloc, pbloc, cbloc, nbloc;
+ int c = 1;
+ uint64_t lbcount = 0, b_off = 0;
+ uint32_t newblocknum, newblock, offset = 0;
+ int8_t etype;
+ int goal = 0, pgoal = UDF_I_LOCATION(inode).logicalBlockNum;
+ char lastblock = 0;
+
+ pextoffset = cextoffset = nextoffset = udf_file_entry_alloc_offset(inode);
+ b_off = (uint64_t)block << inode->i_sb->s_blocksize_bits;
+ pbloc = cbloc = nbloc = UDF_I_LOCATION(inode);
+
+ /* find the extent which contains the block we are looking for.
+ alternate between laarr[0] and laarr[1] for locations of the
+ current extent, and the previous extent */
+ do
+ {
+ if (pbh != cbh)
+ {
+ udf_release_data(pbh);
+ atomic_inc(&cbh->b_count);
+ pbh = cbh;
+ }
+ if (cbh != nbh)
+ {
+ udf_release_data(cbh);
+ atomic_inc(&nbh->b_count);
+ cbh = nbh;
+ }
+
+ lbcount += elen;
+
+ pbloc = cbloc;
+ cbloc = nbloc;
+
+ pextoffset = cextoffset;
+ cextoffset = nextoffset;
+
+ if ((etype = udf_next_aext(inode, &nbloc, &nextoffset, &eloc, &elen, &nbh, 1)) == -1)
+ break;
+
+ c = !c;
+
+ laarr[c].extLength = (etype << 30) | elen;
+ laarr[c].extLocation = eloc;
+
+ if (etype != (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30))
+ pgoal = eloc.logicalBlockNum +
+ ((elen + inode->i_sb->s_blocksize - 1) >>
+ inode->i_sb->s_blocksize_bits);
+
+ count ++;
+ } while (lbcount + elen <= b_off);
+
+ b_off -= lbcount;
+ offset = b_off >> inode->i_sb->s_blocksize_bits;
+
+ /* if the extent is allocated and recorded, return the block
+ if the extent is not a multiple of the blocksize, round up */
+
+ if (etype == (EXT_RECORDED_ALLOCATED >> 30))
+ {
+ if (elen & (inode->i_sb->s_blocksize - 1))
+ {
+ elen = EXT_RECORDED_ALLOCATED |
+ ((elen + inode->i_sb->s_blocksize - 1) &
+ ~(inode->i_sb->s_blocksize - 1));
+ etype = udf_write_aext(inode, nbloc, &cextoffset, eloc, elen, nbh, 1);
+ }
+ udf_release_data(pbh);
+ udf_release_data(cbh);
+ udf_release_data(nbh);
+ newblock = udf_get_lb_pblock(inode->i_sb, eloc, offset);
+ *phys = newblock;
+ return NULL;
+ }
+
+ if (etype == -1)
+ {
+ endnum = startnum = ((count > 1) ? 1 : count);
+ if (laarr[c].extLength & (inode->i_sb->s_blocksize - 1))
+ {
+ laarr[c].extLength =
+ (laarr[c].extLength & UDF_EXTENT_FLAG_MASK) |
+ (((laarr[c].extLength & UDF_EXTENT_LENGTH_MASK) +
+ inode->i_sb->s_blocksize - 1) &
+ ~(inode->i_sb->s_blocksize - 1));
+ UDF_I_LENEXTENTS(inode) =
+ (UDF_I_LENEXTENTS(inode) + inode->i_sb->s_blocksize - 1) &
+ ~(inode->i_sb->s_blocksize - 1);
+ }
+ c = !c;
+ laarr[c].extLength = EXT_NOT_RECORDED_NOT_ALLOCATED |
+ ((offset + 1) << inode->i_sb->s_blocksize_bits);
+ memset(&laarr[c].extLocation, 0x00, sizeof(lb_addr));
+ count ++;
+ endnum ++;
+ lastblock = 1;
+ }
+ else
+ endnum = startnum = ((count > 2) ? 2 : count);
+
+ /* if the current extent is in position 0, swap it with the previous */
+ if (!c && count != 1)
+ {
+ laarr[2] = laarr[0];
+ laarr[0] = laarr[1];
+ laarr[1] = laarr[2];
+ c = 1;
+ }
+
+ /* if the current block is located in a extent, read the next extent */
+ if (etype != -1)
+ {
+ if ((etype = udf_next_aext(inode, &nbloc, &nextoffset, &eloc, &elen, &nbh, 0)) != -1)
+ {
+ laarr[c+1].extLength = (etype << 30) | elen;
+ laarr[c+1].extLocation = eloc;
+ count ++;
+ startnum ++;
+ endnum ++;
+ }
+ else
+ lastblock = 1;
+ }
+ udf_release_data(nbh);
+ if (!pbh)
+ pbh = cbh;
+ else
+ udf_release_data(cbh);
+
+ /* if the current extent is not recorded but allocated, get the
+ block in the extent corresponding to the requested block */
+ if ((laarr[c].extLength >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30))
+ newblocknum = laarr[c].extLocation.logicalBlockNum + offset;
+ else /* otherwise, allocate a new block */
+ {
+ if (UDF_I_NEXT_ALLOC_BLOCK(inode) == block)
+ goal = UDF_I_NEXT_ALLOC_GOAL(inode);
+
+ if (!goal)
+ {
+ if (!(goal = pgoal))
+ goal = UDF_I_LOCATION(inode).logicalBlockNum + 1;
+ }
+
+ if (!(newblocknum = udf_new_block(inode->i_sb, inode,
+ UDF_I_LOCATION(inode).partitionReferenceNum, goal, err)))
+ {
+ udf_release_data(pbh);
+ *err = -ENOSPC;
+ return NULL;
+ }
+ UDF_I_LENEXTENTS(inode) += inode->i_sb->s_blocksize;
+ }
+
+ /* if the extent the requsted block is located in contains multiple blocks,
+ split the extent into at most three extents. blocks prior to requested
+ block, requested block, and blocks after requested block */
+ udf_split_extents(inode, &c, offset, newblocknum, laarr, &endnum);
+
+#ifdef UDF_PREALLOCATE
+ /* preallocate blocks */
+ udf_prealloc_extents(inode, c, lastblock, laarr, &endnum);
+#endif
+
+ /* merge any continuous blocks in laarr */
+ udf_merge_extents(inode, laarr, &endnum);
+
+ /* write back the new extents, inserting new extents if the new number
+ of extents is greater than the old number, and deleting extents if
+ the new number of extents is less than the old number */
+ udf_update_extents(inode, laarr, startnum, endnum, pbloc, pextoffset, &pbh);
+
+ udf_release_data(pbh);
+
+ if (!(newblock = udf_get_pblock(inode->i_sb, newblocknum,
+ UDF_I_LOCATION(inode).partitionReferenceNum, 0)))
+ {
+ return NULL;
+ }
+ *phys = newblock;
+ *err = 0;
+ *new = 1;
+ UDF_I_NEXT_ALLOC_BLOCK(inode) = block;
+ UDF_I_NEXT_ALLOC_GOAL(inode) = newblocknum;
+ inode->i_ctime = CURRENT_TIME;
+ UDF_I_UCTIME(inode) = CURRENT_UTIME;
+
+ if (IS_SYNC(inode))
+ udf_sync_inode(inode);
+ else
+ mark_inode_dirty(inode);
+ return result;
+}
+
+static void udf_split_extents(struct inode *inode, int *c, int offset, int newblocknum,
+ long_ad laarr[EXTENT_MERGE_SIZE], int *endnum)
+{
+ if ((laarr[*c].extLength >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30) ||
+ (laarr[*c].extLength >> 30) == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30))
+ {
+ int curr = *c;
+ int blen = ((laarr[curr].extLength & UDF_EXTENT_LENGTH_MASK) +
+ inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits;
+ int type = laarr[curr].extLength & ~UDF_EXTENT_LENGTH_MASK;
+
+ if (blen == 1)
+ ;
+ else if (!offset || blen == offset + 1)
+ {
+ laarr[curr+2] = laarr[curr+1];
+ laarr[curr+1] = laarr[curr];
+ }
+ else
+ {
+ laarr[curr+3] = laarr[curr+1];
+ laarr[curr+2] = laarr[curr+1] = laarr[curr];
+ }
+
+ if (offset)
+ {
+ if ((type >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30))
+ {
+ udf_free_blocks(inode->i_sb, inode, laarr[curr].extLocation, 0, offset);
+ laarr[curr].extLength = EXT_NOT_RECORDED_NOT_ALLOCATED |
+ (offset << inode->i_sb->s_blocksize_bits);
+ laarr[curr].extLocation.logicalBlockNum = 0;
+ laarr[curr].extLocation.partitionReferenceNum = 0;
+ }
+ else
+ laarr[curr].extLength = type |
+ (offset << inode->i_sb->s_blocksize_bits);
+ curr ++;
+ (*c) ++;
+ (*endnum) ++;
+ }
+
+ laarr[curr].extLocation.logicalBlockNum = newblocknum;
+ if ((type >> 30) == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30))
+ laarr[curr].extLocation.partitionReferenceNum =
+ UDF_I_LOCATION(inode).partitionReferenceNum;
+ laarr[curr].extLength = EXT_RECORDED_ALLOCATED |
+ inode->i_sb->s_blocksize;
+ curr ++;
+
+ if (blen != offset + 1)
+ {
+ if ((type >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30))
+ laarr[curr].extLocation.logicalBlockNum += (offset + 1);
+ laarr[curr].extLength = type |
+ ((blen - (offset + 1)) << inode->i_sb->s_blocksize_bits);
+ curr ++;
+ (*endnum) ++;
+ }
+ }
+}
+
+static void udf_prealloc_extents(struct inode *inode, int c, int lastblock,
+ long_ad laarr[EXTENT_MERGE_SIZE], int *endnum)
+{
+ int start, length = 0, currlength = 0, i;
+
+ if (*endnum >= (c+1))
+ {
+ if (!lastblock)
+ return;
+ else
+ start = c;
+ }
+ else
+ {
+ if ((laarr[c+1].extLength >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30))
+ {
+ start = c+1;
+ length = currlength = (((laarr[c+1].extLength & UDF_EXTENT_LENGTH_MASK) +
+ inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits);
+ }
+ else
+ start = c;
+ }
+
+ for (i=start+1; i<=*endnum; i++)
+ {
+ if (i == *endnum)
+ {
+ if (lastblock)
+ length += UDF_DEFAULT_PREALLOC_BLOCKS;
+ }
+ else if ((laarr[i].extLength >> 30) == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30))
+ length += (((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) +
+ inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits);
+ else
+ break;
+ }
+
+ if (length)
+ {
+ int next = laarr[start].extLocation.logicalBlockNum +
+ (((laarr[start].extLength & UDF_EXTENT_LENGTH_MASK) +
+ inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits);
+ int numalloc = udf_prealloc_blocks(inode->i_sb, inode,
+ laarr[start].extLocation.partitionReferenceNum,
+ next, (UDF_DEFAULT_PREALLOC_BLOCKS > length ? length :
+ UDF_DEFAULT_PREALLOC_BLOCKS) - currlength);
+
+ if (numalloc)
+ {
+ if (start == (c+1))
+ laarr[start].extLength +=
+ (numalloc << inode->i_sb->s_blocksize_bits);
+ else
+ {
+ memmove(&laarr[c+2], &laarr[c+1],
+ sizeof(long_ad) * (*endnum - (c+1)));
+ (*endnum) ++;
+ laarr[c+1].extLocation.logicalBlockNum = next;
+ laarr[c+1].extLocation.partitionReferenceNum =
+ laarr[c].extLocation.partitionReferenceNum;
+ laarr[c+1].extLength = EXT_NOT_RECORDED_ALLOCATED |
+ (numalloc << inode->i_sb->s_blocksize_bits);
+ start = c+1;
+ }
+
+ for (i=start+1; numalloc && i<*endnum; i++)
+ {
+ int elen = ((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) +
+ inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits;
+
+ if (elen > numalloc)
+ {
+ laarr[c].extLength -=
+ (numalloc << inode->i_sb->s_blocksize_bits);
+ numalloc = 0;
+ }
+ else
+ {
+ numalloc -= elen;
+ if (*endnum > (i+1))
+ memmove(&laarr[i], &laarr[i+1],
+ sizeof(long_ad) * (*endnum - (i+1)));
+ i --;
+ (*endnum) --;
+ }
+ }
+ UDF_I_LENEXTENTS(inode) += numalloc << inode->i_sb->s_blocksize_bits;
+ }
+ }
+}
+
+static void udf_merge_extents(struct inode *inode,
+ long_ad laarr[EXTENT_MERGE_SIZE], int *endnum)
+{
+ int i;
+
+ for (i=0; i<(*endnum-1); i++)
+ {
+ if ((laarr[i].extLength >> 30) == (laarr[i+1].extLength >> 30))
+ {
+ if (((laarr[i].extLength >> 30) == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30)) ||
+ ((laarr[i+1].extLocation.logicalBlockNum - laarr[i].extLocation.logicalBlockNum) ==
+ (((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) +
+ inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits)))
+ {
+ if (((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) +
+ (laarr[i+1].extLength & UDF_EXTENT_LENGTH_MASK) +
+ inode->i_sb->s_blocksize - 1) & ~UDF_EXTENT_LENGTH_MASK)
+ {
+ laarr[i+1].extLength = (laarr[i+1].extLength -
+ (laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) +
+ UDF_EXTENT_LENGTH_MASK) & ~(inode->i_sb->s_blocksize-1);
+ laarr[i].extLength = (UDF_EXTENT_LENGTH_MASK + 1) -
+ inode->i_sb->s_blocksize;
+ laarr[i+1].extLocation.logicalBlockNum =
+ laarr[i].extLocation.logicalBlockNum +
+ ((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) >>
+ inode->i_sb->s_blocksize_bits);
+ }
+ else
+ {
+ laarr[i].extLength = laarr[i+1].extLength +
+ (((laarr[i].extLength & UDF_EXTENT_LENGTH_MASK) +
+ inode->i_sb->s_blocksize - 1) & ~(inode->i_sb->s_blocksize-1));
+ if (*endnum > (i+2))
+ memmove(&laarr[i+1], &laarr[i+2],
+ sizeof(long_ad) * (*endnum - (i+2)));
+ i --;
+ (*endnum) --;
+ }
+ }
+ }
+ }
+}
+
+static void udf_update_extents(struct inode *inode,
+ long_ad laarr[EXTENT_MERGE_SIZE], int startnum, int endnum,
+ lb_addr pbloc, uint32_t pextoffset, struct buffer_head **pbh)
+{
+ int start = 0, i;
+ lb_addr tmploc;
+ uint32_t tmplen;
+
+ if (startnum > endnum)
+ {
+ for (i=0; i<(startnum-endnum); i++)
+ {
+ udf_delete_aext(inode, pbloc, pextoffset, laarr[i].extLocation,
+ laarr[i].extLength, *pbh);
+ }
+ }
+ else if (startnum < endnum)
+ {
+ for (i=0; i<(endnum-startnum); i++)
+ {
+ udf_insert_aext(inode, pbloc, pextoffset, laarr[i].extLocation,
+ laarr[i].extLength, *pbh);
+ udf_next_aext(inode, &pbloc, &pextoffset, &laarr[i].extLocation,
+ &laarr[i].extLength, pbh, 1);
+ start ++;
+ }
+ }
+
+ for (i=start; i<endnum; i++)
+ {
+ udf_next_aext(inode, &pbloc, &pextoffset, &tmploc, &tmplen, pbh, 0);
+ udf_write_aext(inode, pbloc, &pextoffset, laarr[i].extLocation,
+ laarr[i].extLength, *pbh, 1);
+ }
+}
+
+struct buffer_head * udf_bread(struct inode * inode, int block,
+ int create, int * err)
+{
+ struct buffer_head * bh = NULL;
+
+ bh = udf_getblk(inode, block, create, err);
+ if (!bh)
+ return NULL;
+
+ if (buffer_uptodate(bh))
+ return bh;
+ ll_rw_block(READ, 1, &bh);
+ wait_on_buffer(bh);
+ if (buffer_uptodate(bh))
+ return bh;
+ brelse(bh);
+ *err = -EIO;
+ return NULL;
+}
+
+void udf_truncate(struct inode * inode)
+{
+ int offset;
+ struct buffer_head *bh;
+ int err;
+
+ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+ S_ISLNK(inode->i_mode)))
+ return;
+ if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
+ return;
+
+ if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB)
+ {
+ if (inode->i_sb->s_blocksize < (udf_file_entry_alloc_offset(inode) +
+ inode->i_size))
+ {
+ udf_expand_file_adinicb(inode, inode->i_size, &err);
+ if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB)
+ {
+ inode->i_size = UDF_I_LENALLOC(inode);
+ return;
+ }
+ else
+ udf_truncate_extents(inode);
+ }
+ else
+ {
+ offset = (inode->i_size & (inode->i_sb->s_blocksize - 1)) +
+ udf_file_entry_alloc_offset(inode);
+
+ if ((bh = udf_tread(inode->i_sb,
+ udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0))))
+ {
+ memset(bh->b_data + offset, 0x00, inode->i_sb->s_blocksize - offset);
+ mark_buffer_dirty(bh);
+ udf_release_data(bh);
+ }
+ UDF_I_LENALLOC(inode) = inode->i_size;
+ }
+ }
+ else
+ {
+ block_truncate_page(inode->i_mapping, inode->i_size, udf_get_block);
+ udf_truncate_extents(inode);
+ }
+
+ inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ UDF_I_UMTIME(inode) = UDF_I_UCTIME(inode) = CURRENT_UTIME;
+ if (IS_SYNC(inode))
+ udf_sync_inode (inode);
+ else
+ mark_inode_dirty(inode);
+}
+
+/*
+ * udf_read_inode
+ *
+ * PURPOSE
+ * Read an inode.
+ *
+ * DESCRIPTION
+ * This routine is called by iget() [which is called by udf_iget()]
+ * (clean_inode() will have been called first)
+ * when an inode is first read into memory.
+ *
+ * HISTORY
+ * July 1, 1997 - Andrew E. Mileski
+ * Written, tested, and released.
+ *
+ * 12/19/98 dgb Updated to fix size problems.
+ */
+
+void
+udf_read_inode(struct inode *inode)
+{
+ memset(&UDF_I_LOCATION(inode), 0xFF, sizeof(lb_addr));
+}
+
+void
+__udf_read_inode(struct inode *inode)
+{
+ struct buffer_head *bh = NULL;
+ struct fileEntry *fe;
+ uint16_t ident;
+
+ /*
+ * Set defaults, but the inode is still incomplete!
+ * Note: get_new_inode() sets the following on a new inode:
+ * i_sb = sb
+ * i_dev = sb->s_dev;
+ * i_no = ino
+ * i_flags = sb->s_flags
+ * i_state = 0
+ * clean_inode(): zero fills and sets
+ * i_count = 1
+ * i_nlink = 1
+ * i_op = NULL;
+ */
+
+ inode->i_blksize = PAGE_SIZE;
+
+ bh = udf_read_ptagged(inode->i_sb, UDF_I_LOCATION(inode), 0, &ident);
+
+ if (!bh)
+ {
+ printk(KERN_ERR "udf: udf_read_inode(ino %ld) failed !bh\n",
+ inode->i_ino);
+ make_bad_inode(inode);
+ return;
+ }
+
+ if (ident != TAG_IDENT_FE && ident != TAG_IDENT_EFE &&
+ ident != TAG_IDENT_USE)
+ {
+ printk(KERN_ERR "udf: udf_read_inode(ino %ld) failed ident=%d\n",
+ inode->i_ino, ident);
+ udf_release_data(bh);
+ make_bad_inode(inode);
+ return;
+ }
+
+ fe = (struct fileEntry *)bh->b_data;
+
+ if (le16_to_cpu(fe->icbTag.strategyType) == 4096)
+ {
+ struct buffer_head *ibh = NULL, *nbh = NULL;
+ struct indirectEntry *ie;
+
+ ibh = udf_read_ptagged(inode->i_sb, UDF_I_LOCATION(inode), 1, &ident);
+ if (ident == TAG_IDENT_IE)
+ {
+ if (ibh)
+ {
+ lb_addr loc;
+ ie = (struct indirectEntry *)ibh->b_data;
+
+ loc = lelb_to_cpu(ie->indirectICB.extLocation);
+
+ if (ie->indirectICB.extLength &&
+ (nbh = udf_read_ptagged(inode->i_sb, loc, 0, &ident)))
+ {
+ if (ident == TAG_IDENT_FE ||
+ ident == TAG_IDENT_EFE)
+ {
+ memcpy(&UDF_I_LOCATION(inode), &loc, sizeof(lb_addr));
+ udf_release_data(bh);
+ udf_release_data(ibh);
+ udf_release_data(nbh);
+ __udf_read_inode(inode);
+ return;
+ }
+ else
+ {
+ udf_release_data(nbh);
+ udf_release_data(ibh);
+ }
+ }
+ else
+ udf_release_data(ibh);
+ }
+ }
+ else
+ udf_release_data(ibh);
+ }
+ else if (le16_to_cpu(fe->icbTag.strategyType) != 4)
+ {
+ printk(KERN_ERR "udf: unsupported strategy type: %d\n",
+ le16_to_cpu(fe->icbTag.strategyType));
+ udf_release_data(bh);
+ make_bad_inode(inode);
+ return;
+ }
+ udf_fill_inode(inode, bh);
+ udf_release_data(bh);
+}
+
+static void udf_fill_inode(struct inode *inode, struct buffer_head *bh)
+{
+ struct fileEntry *fe;
+ struct extendedFileEntry *efe;
+ time_t convtime;
+ long convtime_usec;
+ int offset, alen;
+
+ inode->i_version = ++event;
+ UDF_I_NEW_INODE(inode) = 0;
+
+ fe = (struct fileEntry *)bh->b_data;
+ efe = (struct extendedFileEntry *)bh->b_data;
+
+ if (le16_to_cpu(fe->icbTag.strategyType) == 4)
+ UDF_I_STRAT4096(inode) = 0;
+ else /* if (le16_to_cpu(fe->icbTag.strategyType) == 4096) */
+ UDF_I_STRAT4096(inode) = 1;
+
+ UDF_I_ALLOCTYPE(inode) = le16_to_cpu(fe->icbTag.flags) & ICBTAG_FLAG_AD_MASK;
+ if (le16_to_cpu(fe->descTag.tagIdent) == TAG_IDENT_EFE)
+ UDF_I_EXTENDED_FE(inode) = 1;
+ else if (le16_to_cpu(fe->descTag.tagIdent) == TAG_IDENT_FE)
+ UDF_I_EXTENDED_FE(inode) = 0;
+ else if (le16_to_cpu(fe->descTag.tagIdent) == TAG_IDENT_USE)
+ {
+ UDF_I_LENALLOC(inode) =
+ le32_to_cpu(
+ ((struct unallocSpaceEntry *)bh->b_data)->lengthAllocDescs);
+ return;
+ }
+
+ inode->i_uid = le32_to_cpu(fe->uid);
+ if ( inode->i_uid == -1 ) inode->i_uid = UDF_SB(inode->i_sb)->s_uid;
+
+ inode->i_gid = le32_to_cpu(fe->gid);
+ if ( inode->i_gid == -1 ) inode->i_gid = UDF_SB(inode->i_sb)->s_gid;
+
+ inode->i_nlink = le16_to_cpu(fe->fileLinkCount);
+ if (!inode->i_nlink)
+ inode->i_nlink = 1;
+
+ inode->i_size = le64_to_cpu(fe->informationLength);
+ UDF_I_LENEXTENTS(inode) = inode->i_size;
+
+ inode->i_mode = udf_convert_permissions(fe);
+ inode->i_mode &= ~UDF_SB(inode->i_sb)->s_umask;
+
+ UDF_I_NEXT_ALLOC_BLOCK(inode) = 0;
+ UDF_I_NEXT_ALLOC_GOAL(inode) = 0;
+
+ if (UDF_I_EXTENDED_FE(inode) == 0)
+ {
+ inode->i_blocks = le64_to_cpu(fe->logicalBlocksRecorded) <<
+ (inode->i_sb->s_blocksize_bits - 9);
+
+ if ( udf_stamp_to_time(&convtime, &convtime_usec,
+ lets_to_cpu(fe->accessTime)) )
+ {
+ inode->i_atime = convtime;
+ }
+ else
+ {
+ inode->i_atime = UDF_SB_RECORDTIME(inode->i_sb);
+ }
+
+ if ( udf_stamp_to_time(&convtime, &convtime_usec,
+ lets_to_cpu(fe->modificationTime)) )
+ {
+ inode->i_mtime = convtime;
+ UDF_I_UMTIME(inode) = convtime_usec;
+ }
+ else
+ {
+ inode->i_mtime = UDF_SB_RECORDTIME(inode->i_sb);
+ UDF_I_UMTIME(inode) = 0;
+ }
+
+ if ( udf_stamp_to_time(&convtime, &convtime_usec,
+ lets_to_cpu(fe->attrTime)) )
+ {
+ inode->i_ctime = convtime;
+ UDF_I_UCTIME(inode) = convtime_usec;
+ }
+ else
+ {
+ inode->i_ctime = UDF_SB_RECORDTIME(inode->i_sb);
+ UDF_I_UCTIME(inode) = 0;
+ }
+
+ UDF_I_UNIQUE(inode) = le64_to_cpu(fe->uniqueID);
+ UDF_I_LENEATTR(inode) = le32_to_cpu(fe->lengthExtendedAttr);
+ UDF_I_LENALLOC(inode) = le32_to_cpu(fe->lengthAllocDescs);
+ offset = sizeof(struct fileEntry) + UDF_I_LENEATTR(inode);
+ alen = offset + UDF_I_LENALLOC(inode);
+ }
+ else
+ {
+ inode->i_blocks = le64_to_cpu(efe->logicalBlocksRecorded) <<
+ (inode->i_sb->s_blocksize_bits - 9);
+
+ if ( udf_stamp_to_time(&convtime, &convtime_usec,
+ lets_to_cpu(efe->accessTime)) )
+ {
+ inode->i_atime = convtime;
+ }
+ else
+ {
+ inode->i_atime = UDF_SB_RECORDTIME(inode->i_sb);
+ }
+
+ if ( udf_stamp_to_time(&convtime, &convtime_usec,
+ lets_to_cpu(efe->modificationTime)) )
+ {
+ inode->i_mtime = convtime;
+ UDF_I_UMTIME(inode) = convtime_usec;
+ }
+ else
+ {
+ inode->i_mtime = UDF_SB_RECORDTIME(inode->i_sb);
+ UDF_I_UMTIME(inode) = 0;
+ }
+
+ if ( udf_stamp_to_time(&convtime, &convtime_usec,
+ lets_to_cpu(efe->createTime)) )
+ {
+ UDF_I_CRTIME(inode) = convtime;
+ UDF_I_UCRTIME(inode) = convtime_usec;
+ }
+ else
+ {
+ UDF_I_CRTIME(inode) = UDF_SB_RECORDTIME(inode->i_sb);
+ UDF_I_UCRTIME(inode) = 0;
+ }
+
+ if ( udf_stamp_to_time(&convtime, &convtime_usec,
+ lets_to_cpu(efe->attrTime)) )
+ {
+ inode->i_ctime = convtime;
+ UDF_I_UCTIME(inode) = convtime_usec;
+ }
+ else
+ {
+ inode->i_ctime = UDF_SB_RECORDTIME(inode->i_sb);
+ UDF_I_UCTIME(inode) = 0;
+ }
+
+ UDF_I_UNIQUE(inode) = le64_to_cpu(efe->uniqueID);
+ UDF_I_LENEATTR(inode) = le32_to_cpu(efe->lengthExtendedAttr);
+ UDF_I_LENALLOC(inode) = le32_to_cpu(efe->lengthAllocDescs);
+ offset = sizeof(struct extendedFileEntry) + UDF_I_LENEATTR(inode);
+ alen = offset + UDF_I_LENALLOC(inode);
+ }
+
+ switch (fe->icbTag.fileType)
+ {
+ case ICBTAG_FILE_TYPE_DIRECTORY:
+ {
+ inode->i_op = &udf_dir_inode_operations;
+ inode->i_fop = &udf_dir_operations;
+ inode->i_mode |= S_IFDIR;
+ inode->i_nlink ++;
+ break;
+ }
+ case ICBTAG_FILE_TYPE_REALTIME:
+ case ICBTAG_FILE_TYPE_REGULAR:
+ case ICBTAG_FILE_TYPE_UNDEF:
+ {
+ if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB)
+ inode->i_data.a_ops = &udf_adinicb_aops;
+ else
+ inode->i_data.a_ops = &udf_aops;
+ inode->i_op = &udf_file_inode_operations;
+ inode->i_fop = &udf_file_operations;
+ inode->i_mode |= S_IFREG;
+ break;
+ }
+ case ICBTAG_FILE_TYPE_BLOCK:
+ {
+ inode->i_mode |= S_IFBLK;
+ break;
+ }
+ case ICBTAG_FILE_TYPE_CHAR:
+ {
+ inode->i_mode |= S_IFCHR;
+ break;
+ }
+ case ICBTAG_FILE_TYPE_FIFO:
+ {
+ init_special_inode(inode, inode->i_mode | S_IFIFO, 0);
+ break;
+ }
+ case ICBTAG_FILE_TYPE_SYMLINK:
+ {
+ inode->i_data.a_ops = &udf_symlink_aops;
+ inode->i_op = &page_symlink_inode_operations;
+ inode->i_mode = S_IFLNK|S_IRWXUGO;
+ break;
+ }
+ default:
+ {
+ printk(KERN_ERR "udf: udf_fill_inode(ino %ld) failed unknown file type=%d\n",
+ inode->i_ino, fe->icbTag.fileType);
+ make_bad_inode(inode);
+ return;
+ }
+ }
+ if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+ {
+ struct buffer_head *tbh = NULL;
+ struct deviceSpec *dsea =
+ (struct deviceSpec *)
+ udf_get_extendedattr(inode, 12, 1, &tbh);
+
+ if (dsea)
+ {
+ init_special_inode(inode, inode->i_mode,
+ ((le32_to_cpu(dsea->majorDeviceIdent)) << 8) |
+ (le32_to_cpu(dsea->minorDeviceIdent) & 0xFF));
+ /* Developer ID ??? */
+ udf_release_data(tbh);
+ }
+ else
+ {
+ make_bad_inode(inode);
+ }
+ }
+}
+
+static mode_t
+udf_convert_permissions(struct fileEntry *fe)
+{
+ mode_t mode;
+ uint32_t permissions;
+ uint32_t flags;
+
+ permissions = le32_to_cpu(fe->permissions);
+ flags = le16_to_cpu(fe->icbTag.flags);
+
+ mode = (( permissions ) & S_IRWXO) |
+ (( permissions >> 2 ) & S_IRWXG) |
+ (( permissions >> 4 ) & S_IRWXU) |
+ (( flags & ICBTAG_FLAG_SETUID) ? S_ISUID : 0) |
+ (( flags & ICBTAG_FLAG_SETGID) ? S_ISGID : 0) |
+ (( flags & ICBTAG_FLAG_STICKY) ? S_ISVTX : 0);
+
+ return mode;
+}
+
+/*
+ * udf_write_inode
+ *
+ * PURPOSE
+ * Write out the specified inode.
+ *
+ * DESCRIPTION
+ * This routine is called whenever an inode is synced.
+ * Currently this routine is just a placeholder.
+ *
+ * HISTORY
+ * July 1, 1997 - Andrew E. Mileski
+ * Written, tested, and released.
+ */
+
+void udf_write_inode(struct inode * inode, int sync)
+{
+ lock_kernel();
+ udf_update_inode(inode, sync);
+ unlock_kernel();
+}
+
+int udf_sync_inode(struct inode * inode)
+{
+ return udf_update_inode(inode, 1);
+}
+
+static int
+udf_update_inode(struct inode *inode, int do_sync)
+{
+ struct buffer_head *bh = NULL;
+ struct fileEntry *fe;
+ struct extendedFileEntry *efe;
+ uint32_t udfperms;
+ uint16_t icbflags;
+ uint16_t crclen;
+ int i;
+ timestamp cpu_time;
+ int err = 0;
+
+ bh = udf_tread(inode->i_sb,
+ udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0));
+
+ if (!bh)
+ {
+ udf_debug("bread failure\n");
+ return -EIO;
+ }
+ fe = (struct fileEntry *)bh->b_data;
+ efe = (struct extendedFileEntry *)bh->b_data;
+ if (UDF_I_NEW_INODE(inode) == 1)
+ {
+ if (UDF_I_EXTENDED_FE(inode) == 0)
+ memset(bh->b_data, 0x00, sizeof(struct fileEntry));
+ else
+ memset(bh->b_data, 0x00, sizeof(struct extendedFileEntry));
+ memset(bh->b_data + udf_file_entry_alloc_offset(inode) +
+ UDF_I_LENALLOC(inode), 0x0, inode->i_sb->s_blocksize -
+ udf_file_entry_alloc_offset(inode) - UDF_I_LENALLOC(inode));
+ UDF_I_NEW_INODE(inode) = 0;
+ }
+
+ if (le16_to_cpu(fe->descTag.tagIdent) == TAG_IDENT_USE)
+ {
+ struct unallocSpaceEntry *use =
+ (struct unallocSpaceEntry *)bh->b_data;
+
+ use->lengthAllocDescs = cpu_to_le32(UDF_I_LENALLOC(inode));
+ crclen = sizeof(struct unallocSpaceEntry) + UDF_I_LENALLOC(inode) -
+ sizeof(tag);
+ use->descTag.tagLocation = cpu_to_le32(UDF_I_LOCATION(inode).logicalBlockNum);
+ use->descTag.descCRCLength = cpu_to_le16(crclen);
+ use->descTag.descCRC = cpu_to_le16(udf_crc((char *)use + sizeof(tag), crclen, 0));
+
+ use->descTag.tagChecksum = 0;
+ for (i=0; i<16; i++)
+ if (i != 4)
+ use->descTag.tagChecksum += ((uint8_t *)&(use->descTag))[i];
+
+ mark_buffer_dirty(bh);
+ udf_release_data(bh);
+ return err;
+ }
+
+ if (inode->i_uid != UDF_SB(inode->i_sb)->s_uid)
+ fe->uid = cpu_to_le32(inode->i_uid);
+
+ if (inode->i_gid != UDF_SB(inode->i_sb)->s_gid)
+ fe->gid = cpu_to_le32(inode->i_gid);
+
+ udfperms = ((inode->i_mode & S_IRWXO) ) |
+ ((inode->i_mode & S_IRWXG) << 2) |
+ ((inode->i_mode & S_IRWXU) << 4);
+
+ udfperms |= (le32_to_cpu(fe->permissions) &
+ (FE_PERM_O_DELETE | FE_PERM_O_CHATTR |
+ FE_PERM_G_DELETE | FE_PERM_G_CHATTR |
+ FE_PERM_U_DELETE | FE_PERM_U_CHATTR));
+ fe->permissions = cpu_to_le32(udfperms);
+
+ if (S_ISDIR(inode->i_mode))
+ fe->fileLinkCount = cpu_to_le16(inode->i_nlink - 1);
+ else
+ fe->fileLinkCount = cpu_to_le16(inode->i_nlink);
+
+ fe->informationLength = cpu_to_le64(inode->i_size);
+
+ if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+ {
+ regid *eid;
+ struct buffer_head *tbh = NULL;
+ struct deviceSpec *dsea =
+ (struct deviceSpec *)
+ udf_get_extendedattr(inode, 12, 1, &tbh);
+
+ if (!dsea)
+ {
+ dsea = (struct deviceSpec *)
+ udf_add_extendedattr(inode,
+ sizeof(struct deviceSpec) +
+ sizeof(regid), 12, 0x3, &tbh);
+ dsea->attrType = 12;
+ dsea->attrSubtype = 1;
+ dsea->attrLength = sizeof(struct deviceSpec) +
+ sizeof(regid);
+ dsea->impUseLength = sizeof(regid);
+ }
+ eid = (regid *)dsea->impUse;
+ memset(eid, 0, sizeof(regid));
+ strcpy(eid->ident, UDF_ID_DEVELOPER);
+ eid->identSuffix[0] = UDF_OS_CLASS_UNIX;
+ eid->identSuffix[1] = UDF_OS_ID_LINUX;
+ dsea->majorDeviceIdent = kdev_t_to_nr(inode->i_rdev) >> 8;
+ dsea->minorDeviceIdent = kdev_t_to_nr(inode->i_rdev) & 0xFF;
+ mark_buffer_dirty_inode(tbh, inode);
+ udf_release_data(tbh);
+ }
+
+ if (UDF_I_EXTENDED_FE(inode) == 0)
+ {
+ fe->logicalBlocksRecorded = cpu_to_le64(
+ (inode->i_blocks + (1 << (inode->i_sb->s_blocksize_bits - 9)) - 1) >>
+ (inode->i_sb->s_blocksize_bits - 9));
+
+ if (udf_time_to_stamp(&cpu_time, inode->i_atime, 0))
+ fe->accessTime = cpu_to_lets(cpu_time);
+ if (udf_time_to_stamp(&cpu_time, inode->i_mtime, UDF_I_UMTIME(inode)))
+ fe->modificationTime = cpu_to_lets(cpu_time);
+ if (udf_time_to_stamp(&cpu_time, inode->i_ctime, UDF_I_UCTIME(inode)))
+ fe->attrTime = cpu_to_lets(cpu_time);
+ memset(&(fe->impIdent), 0, sizeof(regid));
+ strcpy(fe->impIdent.ident, UDF_ID_DEVELOPER);
+ fe->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX;
+ fe->impIdent.identSuffix[1] = UDF_OS_ID_LINUX;
+ fe->uniqueID = cpu_to_le64(UDF_I_UNIQUE(inode));
+ fe->lengthExtendedAttr = cpu_to_le32(UDF_I_LENEATTR(inode));
+ fe->lengthAllocDescs = cpu_to_le32(UDF_I_LENALLOC(inode));
+ fe->descTag.tagIdent = le16_to_cpu(TAG_IDENT_FE);
+ crclen = sizeof(struct fileEntry);
+ }
+ else
+ {
+ efe->objectSize = cpu_to_le64(inode->i_size);
+ efe->logicalBlocksRecorded = cpu_to_le64(
+ (inode->i_blocks + (1 << (inode->i_sb->s_blocksize_bits - 9)) - 1) >>
+ (inode->i_sb->s_blocksize_bits - 9));
+
+ if (UDF_I_CRTIME(inode) >= inode->i_atime)
+ {
+ UDF_I_CRTIME(inode) = inode->i_atime;
+ UDF_I_UCRTIME(inode) = 0;
+ }
+ if (UDF_I_CRTIME(inode) > inode->i_mtime ||
+ (UDF_I_CRTIME(inode) == inode->i_mtime &&
+ UDF_I_UCRTIME(inode) > UDF_I_UMTIME(inode)))
+ {
+ UDF_I_CRTIME(inode) = inode->i_mtime;
+ UDF_I_UCRTIME(inode) = UDF_I_UMTIME(inode);
+ }
+ if (UDF_I_CRTIME(inode) > inode->i_ctime ||
+ (UDF_I_CRTIME(inode) == inode->i_ctime &&
+ UDF_I_UCRTIME(inode) > UDF_I_UCTIME(inode)))
+ {
+ UDF_I_CRTIME(inode) = inode->i_ctime;
+ UDF_I_UCRTIME(inode) = UDF_I_UCTIME(inode);
+ }
+
+ if (udf_time_to_stamp(&cpu_time, inode->i_atime, 0))
+ efe->accessTime = cpu_to_lets(cpu_time);
+ if (udf_time_to_stamp(&cpu_time, inode->i_mtime, UDF_I_UMTIME(inode)))
+ efe->modificationTime = cpu_to_lets(cpu_time);
+ if (udf_time_to_stamp(&cpu_time, UDF_I_CRTIME(inode), UDF_I_UCRTIME(inode)))
+ efe->createTime = cpu_to_lets(cpu_time);
+ if (udf_time_to_stamp(&cpu_time, inode->i_ctime, UDF_I_UCTIME(inode)))
+ efe->attrTime = cpu_to_lets(cpu_time);
+
+ memset(&(efe->impIdent), 0, sizeof(regid));
+ strcpy(efe->impIdent.ident, UDF_ID_DEVELOPER);
+ efe->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX;
+ efe->impIdent.identSuffix[1] = UDF_OS_ID_LINUX;
+ efe->uniqueID = cpu_to_le64(UDF_I_UNIQUE(inode));
+ efe->lengthExtendedAttr = cpu_to_le32(UDF_I_LENEATTR(inode));
+ efe->lengthAllocDescs = cpu_to_le32(UDF_I_LENALLOC(inode));
+ efe->descTag.tagIdent = le16_to_cpu(TAG_IDENT_EFE);
+ crclen = sizeof(struct extendedFileEntry);
+ }
+ if (UDF_I_STRAT4096(inode))
+ {
+ fe->icbTag.strategyType = cpu_to_le16(4096);
+ fe->icbTag.strategyParameter = cpu_to_le16(1);
+ fe->icbTag.numEntries = cpu_to_le16(2);
+ }
+ else
+ {
+ fe->icbTag.strategyType = cpu_to_le16(4);
+ fe->icbTag.numEntries = cpu_to_le16(1);
+ }
+
+ if (S_ISDIR(inode->i_mode))
+ fe->icbTag.fileType = ICBTAG_FILE_TYPE_DIRECTORY;
+ else if (S_ISREG(inode->i_mode))
+ fe->icbTag.fileType = ICBTAG_FILE_TYPE_REGULAR;
+ else if (S_ISLNK(inode->i_mode))
+ fe->icbTag.fileType = ICBTAG_FILE_TYPE_SYMLINK;
+ else if (S_ISBLK(inode->i_mode))
+ fe->icbTag.fileType = ICBTAG_FILE_TYPE_BLOCK;
+ else if (S_ISCHR(inode->i_mode))
+ fe->icbTag.fileType = ICBTAG_FILE_TYPE_CHAR;
+ else if (S_ISFIFO(inode->i_mode))
+ fe->icbTag.fileType = ICBTAG_FILE_TYPE_FIFO;
+
+ icbflags = UDF_I_ALLOCTYPE(inode) |
+ ((inode->i_mode & S_ISUID) ? ICBTAG_FLAG_SETUID : 0) |
+ ((inode->i_mode & S_ISGID) ? ICBTAG_FLAG_SETGID : 0) |
+ ((inode->i_mode & S_ISVTX) ? ICBTAG_FLAG_STICKY : 0) |
+ (le16_to_cpu(fe->icbTag.flags) &
+ ~(ICBTAG_FLAG_AD_MASK | ICBTAG_FLAG_SETUID |
+ ICBTAG_FLAG_SETGID | ICBTAG_FLAG_STICKY));
+
+ fe->icbTag.flags = cpu_to_le16(icbflags);
+ if (UDF_SB_UDFREV(inode->i_sb) >= 0x0200)
+ fe->descTag.descVersion = cpu_to_le16(3);
+ else
+ fe->descTag.descVersion = cpu_to_le16(2);
+ fe->descTag.tagSerialNum = cpu_to_le16(UDF_SB_SERIALNUM(inode->i_sb));
+ fe->descTag.tagLocation = cpu_to_le32(UDF_I_LOCATION(inode).logicalBlockNum);
+ crclen += UDF_I_LENEATTR(inode) + UDF_I_LENALLOC(inode) - sizeof(tag);
+ fe->descTag.descCRCLength = cpu_to_le16(crclen);
+ fe->descTag.descCRC = cpu_to_le16(udf_crc((char *)fe + sizeof(tag), crclen, 0));
+
+ fe->descTag.tagChecksum = 0;
+ for (i=0; i<16; i++)
+ if (i != 4)
+ fe->descTag.tagChecksum += ((uint8_t *)&(fe->descTag))[i];
+
+ /* write the data blocks */
+ mark_buffer_dirty(bh);
+ if (do_sync)
+ {
+ ll_rw_block(WRITE, 1, &bh);
+ wait_on_buffer(bh);
+ if (buffer_req(bh) && !buffer_uptodate(bh))
+ {
+ printk("IO error syncing udf inode [%s:%08lx]\n",
+ bdevname(inode->i_dev), inode->i_ino);
+ err = -EIO;
+ }
+ }
+ udf_release_data(bh);
+ return err;
+}
+
+/*
+ * udf_iget
+ *
+ * PURPOSE
+ * Get an inode.
+ *
+ * DESCRIPTION
+ * This routine replaces iget() and read_inode().
+ *
+ * HISTORY
+ * October 3, 1997 - Andrew E. Mileski
+ * Written, tested, and released.
+ *
+ * 12/19/98 dgb Added semaphore and changed to be a wrapper of iget
+ */
+struct inode *
+udf_iget(struct super_block *sb, lb_addr ino)
+{
+ struct inode *inode;
+ unsigned long block;
+
+ block = udf_get_lb_pblock(sb, ino, 0);
+
+ /* Get the inode */
+
+ inode = iget(sb, block);
+ /* calls udf_read_inode() ! */
+
+ if (!inode)
+ {
+ printk(KERN_ERR "udf: iget() failed\n");
+ return NULL;
+ }
+ else if (is_bad_inode(inode))
+ {
+ iput(inode);
+ return NULL;
+ }
+ else if (UDF_I_LOCATION(inode).logicalBlockNum == 0xFFFFFFFF &&
+ UDF_I_LOCATION(inode).partitionReferenceNum == 0xFFFF)
+ {
+ memcpy(&UDF_I_LOCATION(inode), &ino, sizeof(lb_addr));
+ __udf_read_inode(inode);
+ if (is_bad_inode(inode))
+ {
+ iput(inode);
+ return NULL;
+ }
+ }
+
+ if ( ino.logicalBlockNum >= UDF_SB_PARTLEN(sb, ino.partitionReferenceNum) )
+ {
+ udf_debug("block=%d, partition=%d out of range\n",
+ ino.logicalBlockNum, ino.partitionReferenceNum);
+ make_bad_inode(inode);
+ iput(inode);
+ return NULL;
+ }
+
+ return inode;
+}
+
+int8_t udf_add_aext(struct inode *inode, lb_addr *bloc, int *extoffset,
+ lb_addr eloc, uint32_t elen, struct buffer_head **bh, int inc)
+{
+ int adsize;
+ short_ad *sad = NULL;
+ long_ad *lad = NULL;
+ struct allocExtDesc *aed;
+ int8_t etype;
+
+ if (!(*bh))
+ {
+ if (!(*bh = udf_tread(inode->i_sb,
+ udf_get_lb_pblock(inode->i_sb, *bloc, 0))))
+ {
+ udf_debug("reading block %d failed!\n",
+ udf_get_lb_pblock(inode->i_sb, *bloc, 0));
+ return -1;
+ }
+ }
+
+ if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT)
+ adsize = sizeof(short_ad);
+ else if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_LONG)
+ adsize = sizeof(long_ad);
+ else
+ return -1;
+
+ if (*extoffset + (2 * adsize) > inode->i_sb->s_blocksize)
+ {
+ char *sptr, *dptr;
+ struct buffer_head *nbh;
+ int err, loffset;
+ lb_addr obloc = *bloc;
+
+ if (!(bloc->logicalBlockNum = udf_new_block(inode->i_sb, inode,
+ obloc.partitionReferenceNum, obloc.logicalBlockNum, &err)))
+ {
+ return -1;
+ }
+ if (!(nbh = udf_tgetblk(inode->i_sb, udf_get_lb_pblock(inode->i_sb,
+ *bloc, 0))))
+ {
+ return -1;
+ }
+ lock_buffer(nbh);
+ memset(nbh->b_data, 0x00, inode->i_sb->s_blocksize);
+ mark_buffer_uptodate(nbh, 1);
+ unlock_buffer(nbh);
+ mark_buffer_dirty_inode(nbh, inode);
+
+ aed = (struct allocExtDesc *)(nbh->b_data);
+ if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT))
+ aed->previousAllocExtLocation = cpu_to_le32(obloc.logicalBlockNum);
+ if (*extoffset + adsize > inode->i_sb->s_blocksize)
+ {
+ loffset = *extoffset;
+ aed->lengthAllocDescs = cpu_to_le32(adsize);
+ sptr = (*bh)->b_data + *extoffset - adsize;
+ dptr = nbh->b_data + sizeof(struct allocExtDesc);
+ memcpy(dptr, sptr, adsize);
+ *extoffset = sizeof(struct allocExtDesc) + adsize;
+ }
+ else
+ {
+ loffset = *extoffset + adsize;
+ aed->lengthAllocDescs = cpu_to_le32(0);
+ sptr = (*bh)->b_data + *extoffset;
+ *extoffset = sizeof(struct allocExtDesc);
+
+ if (memcmp(&UDF_I_LOCATION(inode), &obloc, sizeof(lb_addr)))
+ {
+ aed = (struct allocExtDesc *)(*bh)->b_data;
+ aed->lengthAllocDescs =
+ cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) + adsize);
+ }
+ else
+ {
+ UDF_I_LENALLOC(inode) += adsize;
+ mark_inode_dirty(inode);
+ }
+ }
+ if (UDF_SB_UDFREV(inode->i_sb) >= 0x0200)
+ udf_new_tag(nbh->b_data, TAG_IDENT_AED, 3, 1,
+ bloc->logicalBlockNum, sizeof(tag));
+ else
+ udf_new_tag(nbh->b_data, TAG_IDENT_AED, 2, 1,
+ bloc->logicalBlockNum, sizeof(tag));
+ switch (UDF_I_ALLOCTYPE(inode))
+ {
+ case ICBTAG_FLAG_AD_SHORT:
+ {
+ sad = (short_ad *)sptr;
+ sad->extLength = cpu_to_le32(
+ EXT_NEXT_EXTENT_ALLOCDECS |
+ inode->i_sb->s_blocksize);
+ sad->extPosition = cpu_to_le32(bloc->logicalBlockNum);
+ break;
+ }
+ case ICBTAG_FLAG_AD_LONG:
+ {
+ lad = (long_ad *)sptr;
+ lad->extLength = cpu_to_le32(
+ EXT_NEXT_EXTENT_ALLOCDECS |
+ inode->i_sb->s_blocksize);
+ lad->extLocation = cpu_to_lelb(*bloc);
+ memset(lad->impUse, 0x00, sizeof(lad->impUse));
+ break;
+ }
+ }
+ if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201)
+ udf_update_tag((*bh)->b_data, loffset);
+ else
+ udf_update_tag((*bh)->b_data, sizeof(struct allocExtDesc));
+ mark_buffer_dirty_inode(*bh, inode);
+ udf_release_data(*bh);
+ *bh = nbh;
+ }
+
+ etype = udf_write_aext(inode, *bloc, extoffset, eloc, elen, *bh, inc);
+
+ if (!memcmp(&UDF_I_LOCATION(inode), bloc, sizeof(lb_addr)))
+ {
+ UDF_I_LENALLOC(inode) += adsize;
+ mark_inode_dirty(inode);
+ }
+ else
+ {
+ aed = (struct allocExtDesc *)(*bh)->b_data;
+ aed->lengthAllocDescs =
+ cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) + adsize);
+ if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201)
+ udf_update_tag((*bh)->b_data, *extoffset + (inc ? 0 : adsize));
+ else
+ udf_update_tag((*bh)->b_data, sizeof(struct allocExtDesc));
+ mark_buffer_dirty_inode(*bh, inode);
+ }
+
+ return etype;
+}
+
+int8_t udf_write_aext(struct inode *inode, lb_addr bloc, int *extoffset,
+ lb_addr eloc, uint32_t elen, struct buffer_head *bh, int inc)
+{
+ int adsize;
+ short_ad *sad = NULL;
+ long_ad *lad = NULL;
+
+ if (!(bh))
+ {
+ if (!(bh = udf_tread(inode->i_sb,
+ udf_get_lb_pblock(inode->i_sb, bloc, 0))))
+ {
+ udf_debug("reading block %d failed!\n",
+ udf_get_lb_pblock(inode->i_sb, bloc, 0));
+ return -1;
+ }
+ }
+ else
+ atomic_inc(&bh->b_count);
+
+ if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT)
+ adsize = sizeof(short_ad);
+ else if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_LONG)
+ adsize = sizeof(long_ad);
+ else
+ return -1;
+
+ switch (UDF_I_ALLOCTYPE(inode))
+ {
+ case ICBTAG_FLAG_AD_SHORT:
+ {
+ sad = (short_ad *)((bh)->b_data + *extoffset);
+ sad->extLength = cpu_to_le32(elen);
+ sad->extPosition = cpu_to_le32(eloc.logicalBlockNum);
+ break;
+ }
+ case ICBTAG_FLAG_AD_LONG:
+ {
+ lad = (long_ad *)((bh)->b_data + *extoffset);
+ lad->extLength = cpu_to_le32(elen);
+ lad->extLocation = cpu_to_lelb(eloc);
+ memset(lad->impUse, 0x00, sizeof(lad->impUse));
+ break;
+ }
+ }
+
+ if (memcmp(&UDF_I_LOCATION(inode), &bloc, sizeof(lb_addr)))
+ {
+ if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201)
+ {
+ struct allocExtDesc *aed = (struct allocExtDesc *)(bh)->b_data;
+ udf_update_tag((bh)->b_data,
+ le32_to_cpu(aed->lengthAllocDescs) + sizeof(struct allocExtDesc));
+ }
+ mark_buffer_dirty_inode(bh, inode);
+ }
+ else
+ {
+ mark_inode_dirty(inode);
+ mark_buffer_dirty(bh);
+ }
+
+ if (inc)
+ *extoffset += adsize;
+ udf_release_data(bh);
+ return (elen >> 30);
+}
+
+int8_t udf_next_aext(struct inode *inode, lb_addr *bloc, int *extoffset,
+ lb_addr *eloc, uint32_t *elen, struct buffer_head **bh, int inc)
+{
+ uint16_t tagIdent;
+ int pos, alen;
+ int8_t etype;
+
+ if (!(*bh))
+ {
+ if (!(*bh = udf_tread(inode->i_sb,
+ udf_get_lb_pblock(inode->i_sb, *bloc, 0))))
+ {
+ udf_debug("reading block %d failed!\n",
+ udf_get_lb_pblock(inode->i_sb, *bloc, 0));
+ return -1;
+ }
+ }
+
+ tagIdent = le16_to_cpu(((tag *)(*bh)->b_data)->tagIdent);
+
+ if (!memcmp(&UDF_I_LOCATION(inode), bloc, sizeof(lb_addr)))
+ {
+ if (tagIdent == TAG_IDENT_FE || tagIdent == TAG_IDENT_EFE ||
+ UDF_I_NEW_INODE(inode))
+ {
+ pos = udf_file_entry_alloc_offset(inode);
+ alen = UDF_I_LENALLOC(inode) + pos;
+ }
+ else if (tagIdent == TAG_IDENT_USE)
+ {
+ pos = sizeof(struct unallocSpaceEntry);
+ alen = UDF_I_LENALLOC(inode) + pos;
+ }
+ else
+ return -1;
+ }
+ else if (tagIdent == TAG_IDENT_AED)
+ {
+ struct allocExtDesc *aed = (struct allocExtDesc *)(*bh)->b_data;
+
+ pos = sizeof(struct allocExtDesc);
+ alen = le32_to_cpu(aed->lengthAllocDescs) + pos;
+ }
+ else
+ return -1;
+
+ if (!(*extoffset))
+ *extoffset = pos;
+
+ switch (UDF_I_ALLOCTYPE(inode))
+ {
+ case ICBTAG_FLAG_AD_SHORT:
+ {
+ short_ad *sad;
+
+ if (!(sad = udf_get_fileshortad((*bh)->b_data, alen, extoffset, inc)))
+ return -1;
+
+ if ((etype = le32_to_cpu(sad->extLength) >> 30) == (EXT_NEXT_EXTENT_ALLOCDECS >> 30))
+ {
+ bloc->logicalBlockNum = le32_to_cpu(sad->extPosition);
+ *extoffset = 0;
+ udf_release_data(*bh);
+ *bh = NULL;
+ return udf_next_aext(inode, bloc, extoffset, eloc, elen, bh, inc);
+ }
+ else
+ {
+ eloc->logicalBlockNum = le32_to_cpu(sad->extPosition);
+ eloc->partitionReferenceNum = UDF_I_LOCATION(inode).partitionReferenceNum;
+ *elen = le32_to_cpu(sad->extLength) & UDF_EXTENT_LENGTH_MASK;
+ }
+ break;
+ }
+ case ICBTAG_FLAG_AD_LONG:
+ {
+ long_ad *lad;
+
+ if (!(lad = udf_get_filelongad((*bh)->b_data, alen, extoffset, inc)))
+ return -1;
+
+ if ((etype = le32_to_cpu(lad->extLength) >> 30) == (EXT_NEXT_EXTENT_ALLOCDECS >> 30))
+ {
+ *bloc = lelb_to_cpu(lad->extLocation);
+ *extoffset = 0;
+ udf_release_data(*bh);
+ *bh = NULL;
+ return udf_next_aext(inode, bloc, extoffset, eloc, elen, bh, inc);
+ }
+ else
+ {
+ *eloc = lelb_to_cpu(lad->extLocation);
+ *elen = le32_to_cpu(lad->extLength) & UDF_EXTENT_LENGTH_MASK;
+ }
+ break;
+ }
+ case ICBTAG_FLAG_AD_IN_ICB:
+ {
+ if (UDF_I_LENALLOC(inode) == 0)
+ return -1;
+ etype = (EXT_RECORDED_ALLOCATED >> 30);
+ *eloc = UDF_I_LOCATION(inode);
+ *elen = UDF_I_LENALLOC(inode);
+ break;
+ }
+ default:
+ {
+ udf_debug("alloc_type = %d unsupported\n", UDF_I_ALLOCTYPE(inode));
+ return -1;
+ }
+ }
+ if (*elen)
+ return etype;
+
+ udf_debug("Empty Extent, inode=%ld, alloctype=%d, eloc=%d, elen=%d, etype=%d, extoffset=%d\n",
+ inode->i_ino, UDF_I_ALLOCTYPE(inode), eloc->logicalBlockNum, *elen, etype, *extoffset);
+ if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT)
+ *extoffset -= sizeof(short_ad);
+ else if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_LONG)
+ *extoffset -= sizeof(long_ad);
+ return -1;
+}
+
+int8_t udf_current_aext(struct inode *inode, lb_addr *bloc, int *extoffset,
+ lb_addr *eloc, uint32_t *elen, struct buffer_head **bh, int inc)
+{
+ int pos, alen;
+ int8_t etype;
+
+ if (!(*bh))
+ {
+ if (!(*bh = udf_tread(inode->i_sb,
+ udf_get_lb_pblock(inode->i_sb, *bloc, 0))))
+ {
+ udf_debug("reading block %d failed!\n",
+ udf_get_lb_pblock(inode->i_sb, *bloc, 0));
+ return -1;
+ }
+ }
+
+ if (!memcmp(&UDF_I_LOCATION(inode), bloc, sizeof(lb_addr)))
+ {
+ if (!(UDF_I_EXTENDED_FE(inode)))
+ pos = sizeof(struct fileEntry) + UDF_I_LENEATTR(inode);
+ else
+ pos = sizeof(struct extendedFileEntry) + UDF_I_LENEATTR(inode);
+ alen = UDF_I_LENALLOC(inode) + pos;
+ }
+ else
+ {
+ struct allocExtDesc *aed = (struct allocExtDesc *)(*bh)->b_data;
+
+ pos = sizeof(struct allocExtDesc);
+ alen = le32_to_cpu(aed->lengthAllocDescs) + pos;
+ }
+
+ if (!(*extoffset))
+ *extoffset = pos;
+
+ switch (UDF_I_ALLOCTYPE(inode))
+ {
+ case ICBTAG_FLAG_AD_SHORT:
+ {
+ short_ad *sad;
+
+ if (!(sad = udf_get_fileshortad((*bh)->b_data, alen, extoffset, inc)))
+ return -1;
+
+ etype = le32_to_cpu(sad->extLength) >> 30;
+ eloc->logicalBlockNum = le32_to_cpu(sad->extPosition);
+ eloc->partitionReferenceNum = UDF_I_LOCATION(inode).partitionReferenceNum;
+ *elen = le32_to_cpu(sad->extLength) & UDF_EXTENT_LENGTH_MASK;
+ break;
+ }
+ case ICBTAG_FLAG_AD_LONG:
+ {
+ long_ad *lad;
+
+ if (!(lad = udf_get_filelongad((*bh)->b_data, alen, extoffset, inc)))
+ return -1;
+
+ etype = le32_to_cpu(lad->extLength) >> 30;
+ *eloc = lelb_to_cpu(lad->extLocation);
+ *elen = le32_to_cpu(lad->extLength) & UDF_EXTENT_LENGTH_MASK;
+ break;
+ }
+ default:
+ {
+ udf_debug("alloc_type = %d unsupported\n", UDF_I_ALLOCTYPE(inode));
+ return -1;
+ }
+ }
+ if (*elen)
+ return etype;
+
+ udf_debug("Empty Extent!\n");
+ if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT)
+ *extoffset -= sizeof(short_ad);
+ else if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_LONG)
+ *extoffset -= sizeof(long_ad);
+ return -1;
+}
+
+int8_t udf_insert_aext(struct inode *inode, lb_addr bloc, int extoffset,
+ lb_addr neloc, uint32_t nelen, struct buffer_head *bh)
+{
+ lb_addr oeloc;
+ uint32_t oelen;
+ int8_t etype;
+
+ if (!bh)
+ {
+ if (!(bh = udf_tread(inode->i_sb,
+ udf_get_lb_pblock(inode->i_sb, bloc, 0))))
+ {
+ udf_debug("reading block %d failed!\n",
+ udf_get_lb_pblock(inode->i_sb, bloc, 0));
+ return -1;
+ }
+ }
+ else
+ atomic_inc(&bh->b_count);
+
+ while ((etype = udf_next_aext(inode, &bloc, &extoffset, &oeloc, &oelen, &bh, 0)) != -1)
+ {
+ udf_write_aext(inode, bloc, &extoffset, neloc, nelen, bh, 1);
+
+ neloc = oeloc;
+ nelen = (etype << 30) | oelen;
+ }
+ udf_add_aext(inode, &bloc, &extoffset, neloc, nelen, &bh, 1);
+ udf_release_data(bh);
+ return (nelen >> 30);
+}
+
+int8_t udf_delete_aext(struct inode *inode, lb_addr nbloc, int nextoffset,
+ lb_addr eloc, uint32_t elen, struct buffer_head *nbh)
+{
+ struct buffer_head *obh;
+ lb_addr obloc;
+ int oextoffset, adsize;
+ int8_t etype;
+ struct allocExtDesc *aed;
+
+ if (!(nbh))
+ {
+ if (!(nbh = udf_tread(inode->i_sb,
+ udf_get_lb_pblock(inode->i_sb, nbloc, 0))))
+ {
+ udf_debug("reading block %d failed!\n",
+ udf_get_lb_pblock(inode->i_sb, nbloc, 0));
+ return -1;
+ }
+ }
+ else
+ atomic_inc(&nbh->b_count);
+ atomic_inc(&nbh->b_count);
+
+ if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT)
+ adsize = sizeof(short_ad);
+ else if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_LONG)
+ adsize = sizeof(long_ad);
+ else
+ adsize = 0;
+
+ obh = nbh;
+ obloc = nbloc;
+ oextoffset = nextoffset;
+
+ if (udf_next_aext(inode, &nbloc, &nextoffset, &eloc, &elen, &nbh, 1) == -1)
+ return -1;
+
+ while ((etype = udf_next_aext(inode, &nbloc, &nextoffset, &eloc, &elen, &nbh, 1)) != -1)
+ {
+ udf_write_aext(inode, obloc, &oextoffset, eloc, (etype << 30) | elen, obh, 1);
+ if (memcmp(&nbloc, &obloc, sizeof(lb_addr)))
+ {
+ obloc = nbloc;
+ udf_release_data(obh);
+ atomic_inc(&nbh->b_count);
+ obh = nbh;
+ oextoffset = nextoffset - adsize;
+ }
+ }
+ memset(&eloc, 0x00, sizeof(lb_addr));
+ elen = 0;
+
+ if (memcmp(&nbloc, &obloc, sizeof(lb_addr)))
+ {
+ udf_free_blocks(inode->i_sb, inode, nbloc, 0, 1);
+ udf_write_aext(inode, obloc, &oextoffset, eloc, elen, obh, 1);
+ udf_write_aext(inode, obloc, &oextoffset, eloc, elen, obh, 1);
+ if (!memcmp(&UDF_I_LOCATION(inode), &obloc, sizeof(lb_addr)))
+ {
+ UDF_I_LENALLOC(inode) -= (adsize * 2);
+ mark_inode_dirty(inode);
+ }
+ else
+ {
+ aed = (struct allocExtDesc *)(obh)->b_data;
+ aed->lengthAllocDescs =
+ cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) - (2*adsize));
+ if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201)
+ udf_update_tag((obh)->b_data, oextoffset - (2*adsize));
+ else
+ udf_update_tag((obh)->b_data, sizeof(struct allocExtDesc));
+ mark_buffer_dirty_inode(obh, inode);
+ }
+ }
+ else
+ {
+ udf_write_aext(inode, obloc, &oextoffset, eloc, elen, obh, 1);
+ if (!memcmp(&UDF_I_LOCATION(inode), &obloc, sizeof(lb_addr)))
+ {
+ UDF_I_LENALLOC(inode) -= adsize;
+ mark_inode_dirty(inode);
+ }
+ else
+ {
+ aed = (struct allocExtDesc *)(obh)->b_data;
+ aed->lengthAllocDescs =
+ cpu_to_le32(le32_to_cpu(aed->lengthAllocDescs) - adsize);
+ if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201)
+ udf_update_tag((obh)->b_data, oextoffset - adsize);
+ else
+ udf_update_tag((obh)->b_data, sizeof(struct allocExtDesc));
+ mark_buffer_dirty_inode(obh, inode);
+ }
+ }
+
+ udf_release_data(nbh);
+ udf_release_data(obh);
+ return (elen >> 30);
+}
+
+int8_t inode_bmap(struct inode *inode, int block, lb_addr *bloc, uint32_t *extoffset,
+ lb_addr *eloc, uint32_t *elen, uint32_t *offset, struct buffer_head **bh)
+{
+ uint64_t lbcount = 0, bcount = (uint64_t)block << inode->i_sb->s_blocksize_bits;
+ int8_t etype;
+
+ if (block < 0)
+ {
+ printk(KERN_ERR "udf: inode_bmap: block < 0\n");
+ return -1;
+ }
+ if (!inode)
+ {
+ printk(KERN_ERR "udf: inode_bmap: NULL inode\n");
+ return -1;
+ }
+
+ *extoffset = 0;
+ *elen = 0;
+ *bloc = UDF_I_LOCATION(inode);
+
+ do
+ {
+ if ((etype = udf_next_aext(inode, bloc, extoffset, eloc, elen, bh, 1)) == -1)
+ {
+ *offset = bcount - lbcount;
+ UDF_I_LENEXTENTS(inode) = lbcount;
+ return -1;
+ }
+ lbcount += *elen;
+ } while (lbcount <= bcount);
+
+ *offset = bcount + *elen - lbcount;
+
+ return etype;
+}
+
+long udf_block_map(struct inode *inode, long block)
+{
+ lb_addr eloc, bloc;
+ uint32_t offset, extoffset, elen;
+ struct buffer_head *bh = NULL;
+ int ret;
+
+ lock_kernel();
+
+ if (inode_bmap(inode, block, &bloc, &extoffset, &eloc, &elen, &offset, &bh) == (EXT_RECORDED_ALLOCATED >> 30))
+ ret = udf_get_lb_pblock(inode->i_sb, eloc, offset >> inode->i_sb->s_blocksize_bits);
+ else
+ ret = 0;
+
+ unlock_kernel();
+
+ if (bh)
+ udf_release_data(bh);
+
+ if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_VARCONV))
+ return udf_fixed_to_variable(ret);
+ else
+ return ret;
+}
diff --git a/fs/udf/lowlevel.c b/fs/udf/lowlevel.c
new file mode 100644
index 00000000000000..b89a322820ef3e
--- /dev/null
+++ b/fs/udf/lowlevel.c
@@ -0,0 +1,91 @@
+/*
+ * lowlevel.c
+ *
+ * PURPOSE
+ * Low Level Device Routines for the UDF filesystem
+ *
+ * CONTACTS
+ * E-mail regarding any portion of the Linux UDF file system should be
+ * directed to the development team mailing list (run by majordomo):
+ * linux_udf@hpesjro.fc.hp.com
+ *
+ * COPYRIGHT
+ * This file is distributed under the terms of the GNU General Public
+ * License (GPL). Copies of the GPL can be obtained from:
+ * ftp://prep.ai.mit.edu/pub/gnu/GPL
+ * Each contributing author retains all rights to their own work.
+ *
+ * (C) 1999-2001 Ben Fennema
+ *
+ * HISTORY
+ *
+ * 03/26/99 blf Created.
+ */
+
+#include "udfdecl.h"
+
+#include <linux/blkdev.h>
+#include <linux/cdrom.h>
+#include <asm/uaccess.h>
+#include <scsi/scsi.h>
+
+typedef struct scsi_device Scsi_Device;
+typedef struct scsi_cmnd Scsi_Cmnd;
+
+#include <scsi/scsi_ioctl.h>
+
+#include <linux/udf_fs.h>
+#include "udf_sb.h"
+
+unsigned int
+udf_get_last_session(struct super_block *sb)
+{
+ struct cdrom_multisession ms_info;
+ unsigned int vol_desc_start;
+ struct block_device *bdev = sb->s_bdev;
+ int i;
+
+ vol_desc_start=0;
+ ms_info.addr_format=CDROM_LBA;
+ i = ioctl_by_bdev(bdev, CDROMMULTISESSION, (unsigned long) &ms_info);
+
+#define WE_OBEY_THE_WRITTEN_STANDARDS 1
+
+ if (i == 0)
+ {
+ udf_debug("XA disk: %s, vol_desc_start=%d\n",
+ (ms_info.xa_flag ? "yes" : "no"), ms_info.addr.lba);
+#if WE_OBEY_THE_WRITTEN_STANDARDS
+ if (ms_info.xa_flag) /* necessary for a valid ms_info.addr */
+#endif
+ vol_desc_start = ms_info.addr.lba;
+ }
+ else
+ {
+ udf_debug("CDROMMULTISESSION not supported: rc=%d\n", i);
+ }
+ return vol_desc_start;
+}
+
+unsigned long
+udf_get_last_block(struct super_block *sb)
+{
+ struct block_device *bdev = sb->s_bdev;
+ int ret;
+ unsigned long lblock = 0;
+
+ ret = ioctl_by_bdev(bdev, CDROM_LAST_WRITTEN, (unsigned long) &lblock);
+
+ if (ret) /* Hard Disk */
+ {
+ ret = ioctl_by_bdev(bdev, BLKGETSIZE, (unsigned long) &lblock);
+
+ if (!ret && lblock != 0x7FFFFFFF)
+ lblock = ((512 * lblock) / sb->s_blocksize);
+ }
+
+ if (!ret && lblock)
+ return lblock - 1;
+ else
+ return 0;
+}
diff --git a/fs/udf/misc.c b/fs/udf/misc.c
new file mode 100644
index 00000000000000..6a7751b0e5e2b2
--- /dev/null
+++ b/fs/udf/misc.c
@@ -0,0 +1,362 @@
+/*
+ * misc.c
+ *
+ * PURPOSE
+ * Miscellaneous routines for the OSTA-UDF(tm) filesystem.
+ *
+ * CONTACTS
+ * E-mail regarding any portion of the Linux UDF file system should be
+ * directed to the development team mailing list (run by majordomo):
+ * linux_udf@hpesjro.fc.hp.com
+ *
+ * COPYRIGHT
+ * This file is distributed under the terms of the GNU General Public
+ * License (GPL). Copies of the GPL can be obtained from:
+ * ftp://prep.ai.mit.edu/pub/gnu/GPL
+ * Each contributing author retains all rights to their own work.
+ *
+ * (C) 1998 Dave Boynton
+ * (C) 1998-2001 Ben Fennema
+ * (C) 1999-2000 Stelias Computing Inc
+ *
+ * HISTORY
+ *
+ * 04/19/99 blf partial support for reading/writing specific EA's
+ */
+
+#include "udfdecl.h"
+
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/udf_fs.h>
+
+#include "udf_i.h"
+#include "udf_sb.h"
+
+uint32_t
+udf64_low32(uint64_t indat)
+{
+ return indat & 0x00000000FFFFFFFFULL;
+}
+
+uint32_t
+udf64_high32(uint64_t indat)
+{
+ return indat >> 32;
+}
+
+extern struct buffer_head *
+udf_tgetblk(struct super_block *sb, int block)
+{
+ if (UDF_QUERY_FLAG(sb, UDF_FLAG_VARCONV))
+ return sb_getblk(sb, udf_fixed_to_variable(block));
+ else
+ return sb_getblk(sb, block);
+}
+
+extern struct buffer_head *
+udf_tread(struct super_block *sb, int block)
+{
+ if (UDF_QUERY_FLAG(sb, UDF_FLAG_VARCONV))
+ return sb_bread(sb, udf_fixed_to_variable(block));
+ else
+ return sb_bread(sb, block);
+}
+
+extern struct genericFormat *
+udf_add_extendedattr(struct inode * inode, uint32_t size, uint32_t type,
+ uint8_t loc, struct buffer_head **bh)
+{
+ uint8_t *ea = NULL, *ad = NULL;
+ long_ad eaicb;
+ int offset;
+
+ *bh = udf_tread(inode->i_sb, inode->i_ino);
+
+ if (UDF_I_EXTENDED_FE(inode) == 0)
+ {
+ struct fileEntry *fe;
+
+ fe = (struct fileEntry *)(*bh)->b_data;
+ eaicb = lela_to_cpu(fe->extendedAttrICB);
+ offset = sizeof(struct fileEntry);
+ }
+ else
+ {
+ struct extendedFileEntry *efe;
+
+ efe = (struct extendedFileEntry *)(*bh)->b_data;
+ eaicb = lela_to_cpu(efe->extendedAttrICB);
+ offset = sizeof(struct extendedFileEntry);
+ }
+
+ ea = &(*bh)->b_data[offset];
+ if (UDF_I_LENEATTR(inode))
+ offset += UDF_I_LENEATTR(inode);
+ else
+ size += sizeof(struct extendedAttrHeaderDesc);
+
+ ad = &(*bh)->b_data[offset];
+ if (UDF_I_LENALLOC(inode))
+ offset += UDF_I_LENALLOC(inode);
+
+ offset = inode->i_sb->s_blocksize - offset;
+
+ /* TODO - Check for FreeEASpace */
+
+ if (loc & 0x01 && offset >= size)
+ {
+ struct extendedAttrHeaderDesc *eahd;
+ eahd = (struct extendedAttrHeaderDesc *)ea;
+
+ if (UDF_I_LENALLOC(inode))
+ {
+ memmove(&ad[size], ad, UDF_I_LENALLOC(inode));
+ }
+
+ if (UDF_I_LENEATTR(inode))
+ {
+ /* check checksum/crc */
+ if (le16_to_cpu(eahd->descTag.tagIdent) != TAG_IDENT_EAHD ||
+ le32_to_cpu(eahd->descTag.tagLocation) != UDF_I_LOCATION(inode).logicalBlockNum)
+ {
+ udf_release_data(*bh);
+ return NULL;
+ }
+ }
+ else
+ {
+ size -= sizeof(struct extendedAttrHeaderDesc);
+ UDF_I_LENEATTR(inode) += sizeof(struct extendedAttrHeaderDesc);
+ eahd->descTag.tagIdent = cpu_to_le16(TAG_IDENT_EAHD);
+ eahd->descTag.descVersion = cpu_to_le16(2);
+ eahd->descTag.tagSerialNum = cpu_to_le16(1);
+ eahd->descTag.tagLocation = cpu_to_le32(UDF_I_LOCATION(inode).logicalBlockNum);
+ eahd->impAttrLocation = cpu_to_le32(0xFFFFFFFF);
+ eahd->appAttrLocation = cpu_to_le32(0xFFFFFFFF);
+ }
+
+ offset = UDF_I_LENEATTR(inode);
+ if (type < 2048)
+ {
+ if (le32_to_cpu(eahd->appAttrLocation) < UDF_I_LENEATTR(inode))
+ {
+ uint32_t aal = le32_to_cpu(eahd->appAttrLocation);
+ memmove(&ea[offset - aal + size],
+ &ea[aal], offset - aal);
+ offset -= aal;
+ eahd->appAttrLocation = cpu_to_le32(aal + size);
+ }
+ if (le32_to_cpu(eahd->impAttrLocation) < UDF_I_LENEATTR(inode))
+ {
+ uint32_t ial = le32_to_cpu(eahd->impAttrLocation);
+ memmove(&ea[offset - ial + size],
+ &ea[ial], offset - ial);
+ offset -= ial;
+ eahd->impAttrLocation = cpu_to_le32(ial + size);
+ }
+ }
+ else if (type < 65536)
+ {
+ if (le32_to_cpu(eahd->appAttrLocation) < UDF_I_LENEATTR(inode))
+ {
+ uint32_t aal = le32_to_cpu(eahd->appAttrLocation);
+ memmove(&ea[offset - aal + size],
+ &ea[aal], offset - aal);
+ offset -= aal;
+ eahd->appAttrLocation = cpu_to_le32(aal + size);
+ }
+ }
+ /* rewrite CRC + checksum of eahd */
+ UDF_I_LENEATTR(inode) += size;
+ return (struct genericFormat *)&ea[offset];
+ }
+ if (loc & 0x02)
+ {
+ }
+ udf_release_data(*bh);
+ return NULL;
+}
+
+extern struct genericFormat *
+udf_get_extendedattr(struct inode * inode, uint32_t type, uint8_t subtype,
+ struct buffer_head **bh)
+{
+ struct genericFormat *gaf;
+ uint8_t *ea = NULL;
+ long_ad eaicb;
+ uint32_t offset;
+
+ *bh = udf_tread(inode->i_sb, inode->i_ino);
+
+ if (UDF_I_EXTENDED_FE(inode) == 0)
+ {
+ struct fileEntry *fe;
+
+ fe = (struct fileEntry *)(*bh)->b_data;
+ eaicb = lela_to_cpu(fe->extendedAttrICB);
+ if (UDF_I_LENEATTR(inode))
+ ea = fe->extendedAttr;
+ }
+ else
+ {
+ struct extendedFileEntry *efe;
+
+ efe = (struct extendedFileEntry *)(*bh)->b_data;
+ eaicb = lela_to_cpu(efe->extendedAttrICB);
+ if (UDF_I_LENEATTR(inode))
+ ea = efe->extendedAttr;
+ }
+
+ if (UDF_I_LENEATTR(inode))
+ {
+ struct extendedAttrHeaderDesc *eahd;
+ eahd = (struct extendedAttrHeaderDesc *)ea;
+
+ /* check checksum/crc */
+ if (le16_to_cpu(eahd->descTag.tagIdent) != TAG_IDENT_EAHD ||
+ le32_to_cpu(eahd->descTag.tagLocation) != UDF_I_LOCATION(inode).logicalBlockNum)
+ {
+ udf_release_data(*bh);
+ return NULL;
+ }
+
+ if (type < 2048)
+ offset = sizeof(struct extendedAttrHeaderDesc);
+ else if (type < 65536)
+ offset = le32_to_cpu(eahd->impAttrLocation);
+ else
+ offset = le32_to_cpu(eahd->appAttrLocation);
+
+ while (offset < UDF_I_LENEATTR(inode))
+ {
+ gaf = (struct genericFormat *)&ea[offset];
+ if (le32_to_cpu(gaf->attrType) == type && gaf->attrSubtype == subtype)
+ return gaf;
+ else
+ offset += le32_to_cpu(gaf->attrLength);
+ }
+ }
+
+ udf_release_data(*bh);
+ if (eaicb.extLength)
+ {
+ /* TODO */
+ }
+ return NULL;
+}
+
+/*
+ * udf_read_tagged
+ *
+ * PURPOSE
+ * Read the first block of a tagged descriptor.
+ *
+ * HISTORY
+ * July 1, 1997 - Andrew E. Mileski
+ * Written, tested, and released.
+ */
+extern struct buffer_head *
+udf_read_tagged(struct super_block *sb, uint32_t block, uint32_t location, uint16_t *ident)
+{
+ tag *tag_p;
+ struct buffer_head *bh = NULL;
+ register uint8_t checksum;
+ register int i;
+
+ /* Read the block */
+ if (block == 0xFFFFFFFF)
+ return NULL;
+
+ bh = udf_tread(sb, block);
+ if (!bh)
+ {
+ udf_debug("block=%d, location=%d: read failed\n", block, location);
+ return NULL;
+ }
+
+ tag_p = (tag *)(bh->b_data);
+
+ *ident = le16_to_cpu(tag_p->tagIdent);
+
+ if ( location != le32_to_cpu(tag_p->tagLocation) )
+ {
+ udf_debug("location mismatch block %u, tag %u != %u\n",
+ block, le32_to_cpu(tag_p->tagLocation), location);
+ goto error_out;
+ }
+
+ /* Verify the tag checksum */
+ checksum = 0U;
+ for (i = 0; i < 4; i++)
+ checksum += (uint8_t)(bh->b_data[i]);
+ for (i = 5; i < 16; i++)
+ checksum += (uint8_t)(bh->b_data[i]);
+ if (checksum != tag_p->tagChecksum) {
+ printk(KERN_ERR "udf: tag checksum failed block %d\n", block);
+ goto error_out;
+ }
+
+ /* Verify the tag version */
+ if (le16_to_cpu(tag_p->descVersion) != 0x0002U &&
+ le16_to_cpu(tag_p->descVersion) != 0x0003U)
+ {
+ udf_debug("tag version 0x%04x != 0x0002 || 0x0003 block %d\n",
+ le16_to_cpu(tag_p->descVersion), block);
+ goto error_out;
+ }
+
+ /* Verify the descriptor CRC */
+ if (le16_to_cpu(tag_p->descCRCLength) + sizeof(tag) > sb->s_blocksize ||
+ le16_to_cpu(tag_p->descCRC) == udf_crc(bh->b_data + sizeof(tag),
+ le16_to_cpu(tag_p->descCRCLength), 0))
+ {
+ return bh;
+ }
+ udf_debug("Crc failure block %d: crc = %d, crclen = %d\n",
+ block, le16_to_cpu(tag_p->descCRC), le16_to_cpu(tag_p->descCRCLength));
+
+error_out:
+ brelse(bh);
+ return NULL;
+}
+
+extern struct buffer_head *
+udf_read_ptagged(struct super_block *sb, lb_addr loc, uint32_t offset, uint16_t *ident)
+{
+ return udf_read_tagged(sb, udf_get_lb_pblock(sb, loc, offset),
+ loc.logicalBlockNum + offset, ident);
+}
+
+void udf_release_data(struct buffer_head *bh)
+{
+ if (bh)
+ brelse(bh);
+}
+
+void udf_update_tag(char *data, int length)
+{
+ tag *tptr = (tag *)data;
+ int i;
+
+ length -= sizeof(tag);
+
+ tptr->tagChecksum = 0;
+ tptr->descCRCLength = le16_to_cpu(length);
+ tptr->descCRC = le16_to_cpu(udf_crc(data + sizeof(tag), length, 0));
+
+ for (i=0; i<16; i++)
+ if (i != 4)
+ tptr->tagChecksum += (uint8_t)(data[i]);
+}
+
+void udf_new_tag(char *data, uint16_t ident, uint16_t version, uint16_t snum,
+ uint32_t loc, int length)
+{
+ tag *tptr = (tag *)data;
+ tptr->tagIdent = le16_to_cpu(ident);
+ tptr->descVersion = le16_to_cpu(version);
+ tptr->tagSerialNum = le16_to_cpu(snum);
+ tptr->tagLocation = le32_to_cpu(loc);
+ udf_update_tag(data, length);
+}
diff --git a/fs/udf/namei.c b/fs/udf/namei.c
new file mode 100644
index 00000000000000..f7d959789daf8d
--- /dev/null
+++ b/fs/udf/namei.c
@@ -0,0 +1,1300 @@
+/*
+ * namei.c
+ *
+ * PURPOSE
+ * Inode name handling routines for the OSTA-UDF(tm) filesystem.
+ *
+ * CONTACTS
+ * E-mail regarding any portion of the Linux UDF file system should be
+ * directed to the development team mailing list (run by majordomo):
+ * linux_udf@hpesjro.fc.hp.com
+ *
+ * COPYRIGHT
+ * This file is distributed under the terms of the GNU General Public
+ * License (GPL). Copies of the GPL can be obtained from:
+ * ftp://prep.ai.mit.edu/pub/gnu/GPL
+ * Each contributing author retains all rights to their own work.
+ *
+ * (C) 1998-2001 Ben Fennema
+ * (C) 1999-2000 Stelias Computing Inc
+ *
+ * HISTORY
+ *
+ * 12/12/98 blf Created. Split out the lookup code from dir.c
+ * 04/19/99 blf link, mknod, symlink support
+ */
+
+#include "udfdecl.h"
+
+#include "udf_i.h"
+#include "udf_sb.h"
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/quotaops.h>
+#include <linux/locks.h>
+#include <linux/smp_lock.h>
+
+static inline int udf_match(int len, const char * const name, struct qstr *qs)
+{
+ if (len != qs->len)
+ return 0;
+ return !memcmp(name, qs->name, len);
+}
+
+int udf_write_fi(struct inode *inode, struct fileIdentDesc *cfi,
+ struct fileIdentDesc *sfi, struct udf_fileident_bh *fibh,
+ uint8_t *impuse, uint8_t *fileident)
+{
+ uint16_t crclen = fibh->eoffset - fibh->soffset - sizeof(tag);
+ uint16_t crc;
+ uint8_t checksum = 0;
+ int i;
+ int offset;
+ uint16_t liu = le16_to_cpu(cfi->lengthOfImpUse);
+ uint8_t lfi = cfi->lengthFileIdent;
+ int padlen = fibh->eoffset - fibh->soffset - liu - lfi -
+ sizeof(struct fileIdentDesc);
+
+ offset = fibh->soffset + sizeof(struct fileIdentDesc);
+
+ if (impuse)
+ {
+ if (offset + liu < 0)
+ memcpy((uint8_t *)sfi->impUse, impuse, liu);
+ else if (offset >= 0)
+ memcpy(fibh->ebh->b_data + offset, impuse, liu);
+ else
+ {
+ memcpy((uint8_t *)sfi->impUse, impuse, -offset);
+ memcpy(fibh->ebh->b_data, impuse - offset, liu + offset);
+ }
+ }
+
+ offset += liu;
+
+ if (fileident)
+ {
+ if (offset + lfi < 0)
+ memcpy((uint8_t *)sfi->fileIdent + liu, fileident, lfi);
+ else if (offset >= 0)
+ memcpy(fibh->ebh->b_data + offset, fileident, lfi);
+ else
+ {
+ memcpy((uint8_t *)sfi->fileIdent + liu, fileident, -offset);
+ memcpy(fibh->ebh->b_data, fileident - offset, lfi + offset);
+ }
+ }
+
+ offset += lfi;
+
+ if (offset + padlen < 0)
+ memset((uint8_t *)sfi->padding + liu + lfi, 0x00, padlen);
+ else if (offset >= 0)
+ memset(fibh->ebh->b_data + offset, 0x00, padlen);
+ else
+ {
+ memset((uint8_t *)sfi->padding + liu + lfi, 0x00, -offset);
+ memset(fibh->ebh->b_data, 0x00, padlen + offset);
+ }
+
+ crc = udf_crc((uint8_t *)cfi + sizeof(tag), sizeof(struct fileIdentDesc) -
+ sizeof(tag), 0);
+
+ if (fibh->sbh == fibh->ebh)
+ crc = udf_crc((uint8_t *)sfi->impUse,
+ crclen + sizeof(tag) - sizeof(struct fileIdentDesc), crc);
+ else if (sizeof(struct fileIdentDesc) >= -fibh->soffset)
+ crc = udf_crc(fibh->ebh->b_data + sizeof(struct fileIdentDesc) + fibh->soffset,
+ crclen + sizeof(tag) - sizeof(struct fileIdentDesc), crc);
+ else
+ {
+ crc = udf_crc((uint8_t *)sfi->impUse,
+ -fibh->soffset - sizeof(struct fileIdentDesc), crc);
+ crc = udf_crc(fibh->ebh->b_data, fibh->eoffset, crc);
+ }
+
+ cfi->descTag.descCRC = cpu_to_le32(crc);
+ cfi->descTag.descCRCLength = cpu_to_le16(crclen);
+
+ for (i=0; i<16; i++)
+ if (i != 4)
+ checksum += ((uint8_t *)&cfi->descTag)[i];
+
+ cfi->descTag.tagChecksum = checksum;
+ if (sizeof(struct fileIdentDesc) <= -fibh->soffset)
+ memcpy((uint8_t *)sfi, (uint8_t *)cfi, sizeof(struct fileIdentDesc));
+ else
+ {
+ memcpy((uint8_t *)sfi, (uint8_t *)cfi, -fibh->soffset);
+ memcpy(fibh->ebh->b_data, (uint8_t *)cfi - fibh->soffset,
+ sizeof(struct fileIdentDesc) + fibh->soffset);
+ }
+
+ if (fibh->sbh != fibh->ebh)
+ mark_buffer_dirty_inode(fibh->ebh, inode);
+ mark_buffer_dirty_inode(fibh->sbh, inode);
+ return 0;
+}
+
+static struct fileIdentDesc *
+udf_find_entry(struct inode *dir, struct dentry *dentry,
+ struct udf_fileident_bh *fibh,
+ struct fileIdentDesc *cfi)
+{
+ struct fileIdentDesc *fi=NULL;
+ loff_t f_pos;
+ int block, flen;
+ char fname[255];
+ char *nameptr;
+ uint8_t lfi;
+ uint16_t liu;
+ loff_t size = (udf_ext0_offset(dir) + dir->i_size) >> 2;
+ lb_addr bloc, eloc;
+ uint32_t extoffset, elen, offset;
+ struct buffer_head *bh = NULL;
+
+ if (!dir)
+ return NULL;
+
+ f_pos = (udf_ext0_offset(dir) >> 2);
+
+ fibh->soffset = fibh->eoffset = (f_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2;
+ if (inode_bmap(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2),
+ &bloc, &extoffset, &eloc, &elen, &offset, &bh) == (EXT_RECORDED_ALLOCATED >> 30))
+ {
+ offset >>= dir->i_sb->s_blocksize_bits;
+ block = udf_get_lb_pblock(dir->i_sb, eloc, offset);
+ if ((++offset << dir->i_sb->s_blocksize_bits) < elen)
+ {
+ if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_SHORT)
+ extoffset -= sizeof(short_ad);
+ else if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_LONG)
+ extoffset -= sizeof(long_ad);
+ }
+ else
+ offset = 0;
+ }
+ else
+ {
+ udf_release_data(bh);
+ return NULL;
+ }
+
+ if (!(fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block)))
+ {
+ udf_release_data(bh);
+ return NULL;
+ }
+
+ while ( (f_pos < size) )
+ {
+ fi = udf_fileident_read(dir, &f_pos, fibh, cfi, &bloc, &extoffset, &eloc, &elen, &offset, &bh);
+
+ if (!fi)
+ {
+ if (fibh->sbh != fibh->ebh)
+ udf_release_data(fibh->ebh);
+ udf_release_data(fibh->sbh);
+ udf_release_data(bh);
+ return NULL;
+ }
+
+ liu = le16_to_cpu(cfi->lengthOfImpUse);
+ lfi = cfi->lengthFileIdent;
+
+ if (fibh->sbh == fibh->ebh)
+ {
+ nameptr = fi->fileIdent + liu;
+ }
+ else
+ {
+ int poffset; /* Unpaded ending offset */
+
+ poffset = fibh->soffset + sizeof(struct fileIdentDesc) + liu + lfi;
+
+ if (poffset >= lfi)
+ nameptr = (uint8_t *)(fibh->ebh->b_data + poffset - lfi);
+ else
+ {
+ nameptr = fname;
+ memcpy(nameptr, fi->fileIdent + liu, lfi - poffset);
+ memcpy(nameptr + lfi - poffset, fibh->ebh->b_data, poffset);
+ }
+ }
+
+ if ( (cfi->fileCharacteristics & FID_FILE_CHAR_DELETED) != 0 )
+ {
+ if ( !UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNDELETE) )
+ continue;
+ }
+
+ if ( (cfi->fileCharacteristics & FID_FILE_CHAR_HIDDEN) != 0 )
+ {
+ if ( !UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNHIDE) )
+ continue;
+ }
+
+ if (!lfi)
+ continue;
+
+ if ((flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi)))
+ {
+ if (udf_match(flen, fname, &(dentry->d_name)))
+ {
+ udf_release_data(bh);
+ return fi;
+ }
+ }
+ }
+ if (fibh->sbh != fibh->ebh)
+ udf_release_data(fibh->ebh);
+ udf_release_data(fibh->sbh);
+ udf_release_data(bh);
+ return NULL;
+}
+
+/*
+ * udf_lookup
+ *
+ * PURPOSE
+ * Look-up the inode for a given name.
+ *
+ * DESCRIPTION
+ * Required - lookup_dentry() will return -ENOTDIR if this routine is not
+ * available for a directory. The filesystem is useless if this routine is
+ * not available for at least the filesystem's root directory.
+ *
+ * This routine is passed an incomplete dentry - it must be completed by
+ * calling d_add(dentry, inode). If the name does not exist, then the
+ * specified inode must be set to null. An error should only be returned
+ * when the lookup fails for a reason other than the name not existing.
+ * Note that the directory inode semaphore is held during the call.
+ *
+ * Refer to lookup_dentry() in fs/namei.c
+ * lookup_dentry() -> lookup() -> real_lookup() -> .
+ *
+ * PRE-CONDITIONS
+ * dir Pointer to inode of parent directory.
+ * dentry Pointer to dentry to complete.
+ *
+ * POST-CONDITIONS
+ * <return> Zero on success.
+ *
+ * HISTORY
+ * July 1, 1997 - Andrew E. Mileski
+ * Written, tested, and released.
+ */
+
+static struct dentry *
+udf_lookup(struct inode *dir, struct dentry *dentry)
+{
+ struct inode *inode = NULL;
+ struct fileIdentDesc cfi, *fi;
+ struct udf_fileident_bh fibh;
+
+ if (dentry->d_name.len > UDF_NAME_LEN)
+ return ERR_PTR(-ENAMETOOLONG);
+
+#ifdef UDF_RECOVERY
+ /* temporary shorthand for specifying files by inode number */
+ if (!strncmp(dentry->d_name.name, ".B=", 3) )
+ {
+ lb_addr lb = { 0, simple_strtoul(dentry->d_name.name+3, NULL, 0) };
+ inode = udf_iget(dir->i_sb, lb);
+ if (!inode)
+ return ERR_PTR(-EACCES);
+ }
+ else
+#endif /* UDF_RECOVERY */
+
+ if ((fi = udf_find_entry(dir, dentry, &fibh, &cfi)))
+ {
+ if (fibh.sbh != fibh.ebh)
+ udf_release_data(fibh.ebh);
+ udf_release_data(fibh.sbh);
+
+ inode = udf_iget(dir->i_sb, lelb_to_cpu(cfi.icb.extLocation));
+ if ( !inode )
+ return ERR_PTR(-EACCES);
+ }
+ d_add(dentry, inode);
+ return NULL;
+}
+
+static struct fileIdentDesc *
+udf_add_entry(struct inode *dir, struct dentry *dentry,
+ struct udf_fileident_bh *fibh,
+ struct fileIdentDesc *cfi, int *err)
+{
+ struct super_block *sb;
+ struct fileIdentDesc *fi=NULL;
+ struct ustr unifilename;
+ char name[UDF_NAME_LEN], fname[UDF_NAME_LEN];
+ int namelen;
+ loff_t f_pos;
+ int flen;
+ char *nameptr;
+ loff_t size = (udf_ext0_offset(dir) + dir->i_size) >> 2;
+ int nfidlen;
+ uint8_t lfi;
+ uint16_t liu;
+ int block;
+ lb_addr bloc, eloc;
+ uint32_t extoffset, elen, offset;
+ struct buffer_head *bh = NULL;
+
+ sb = dir->i_sb;
+
+ if (dentry)
+ {
+ if (!dentry->d_name.len)
+ {
+ *err = -EINVAL;
+ return NULL;
+ }
+
+ if ( !(udf_char_to_ustr(&unifilename, dentry->d_name.name, dentry->d_name.len)) )
+ {
+ *err = -ENAMETOOLONG;
+ return NULL;
+ }
+
+ if (UDF_QUERY_FLAG(sb, UDF_FLAG_UTF8))
+ {
+ if ( !(namelen = udf_UTF8toCS0(name, &unifilename, UDF_NAME_LEN)) )
+ {
+ *err = -ENAMETOOLONG;
+ return NULL;
+ }
+ }
+ else if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP))
+ {
+ if ( !(namelen = udf_NLStoCS0(UDF_SB(sb)->s_nls_map, name, &unifilename, UDF_NAME_LEN)) )
+ {
+ *err = -ENAMETOOLONG;
+ return NULL;
+ }
+ }
+ else
+ return NULL;
+ }
+ else
+ namelen = 0;
+
+ nfidlen = (sizeof(struct fileIdentDesc) + namelen + 3) & ~3;
+
+ f_pos = (udf_ext0_offset(dir) >> 2);
+
+ fibh->soffset = fibh->eoffset = (f_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2;
+ if (inode_bmap(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2),
+ &bloc, &extoffset, &eloc, &elen, &offset, &bh) == (EXT_RECORDED_ALLOCATED >> 30))
+ {
+ offset >>= dir->i_sb->s_blocksize_bits;
+ block = udf_get_lb_pblock(dir->i_sb, eloc, offset);
+ if ((++offset << dir->i_sb->s_blocksize_bits) < elen)
+ {
+ if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_SHORT)
+ extoffset -= sizeof(short_ad);
+ else if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_LONG)
+ extoffset -= sizeof(long_ad);
+ }
+ else
+ offset = 0;
+
+ if (!(fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block)))
+ {
+ udf_release_data(bh);
+ *err = -EIO;
+ return NULL;
+ }
+
+ block = UDF_I_LOCATION(dir).logicalBlockNum;
+
+ while ( (f_pos < size) )
+ {
+ fi = udf_fileident_read(dir, &f_pos, fibh, cfi, &bloc, &extoffset, &eloc, &elen, &offset, &bh);
+
+ if (!fi)
+ {
+ if (fibh->sbh != fibh->ebh)
+ udf_release_data(fibh->ebh);
+ udf_release_data(fibh->sbh);
+ udf_release_data(bh);
+ *err = -EIO;
+ return NULL;
+ }
+
+ liu = le16_to_cpu(cfi->lengthOfImpUse);
+ lfi = cfi->lengthFileIdent;
+
+ if (fibh->sbh == fibh->ebh)
+ nameptr = fi->fileIdent + liu;
+ else
+ {
+ int poffset; /* Unpaded ending offset */
+
+ poffset = fibh->soffset + sizeof(struct fileIdentDesc) + liu + lfi;
+
+ if (poffset >= lfi)
+ nameptr = (char *)(fibh->ebh->b_data + poffset - lfi);
+ else
+ {
+ nameptr = fname;
+ memcpy(nameptr, fi->fileIdent + liu, lfi - poffset);
+ memcpy(nameptr + lfi - poffset, fibh->ebh->b_data, poffset);
+ }
+ }
+
+ if ( (cfi->fileCharacteristics & FID_FILE_CHAR_DELETED) != 0 )
+ {
+ if (((sizeof(struct fileIdentDesc) + liu + lfi + 3) & ~3) == nfidlen)
+ {
+ udf_release_data(bh);
+ cfi->descTag.tagSerialNum = cpu_to_le16(1);
+ cfi->fileVersionNum = cpu_to_le16(1);
+ cfi->fileCharacteristics = 0;
+ cfi->lengthFileIdent = namelen;
+ cfi->lengthOfImpUse = cpu_to_le16(0);
+ if (!udf_write_fi(dir, cfi, fi, fibh, NULL, name))
+ return fi;
+ else
+ {
+ *err = -EIO;
+ return NULL;
+ }
+ }
+ }
+
+ if (!lfi || !dentry)
+ continue;
+
+ if ((flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi)) &&
+ udf_match(flen, fname, &(dentry->d_name)))
+ {
+ if (fibh->sbh != fibh->ebh)
+ udf_release_data(fibh->ebh);
+ udf_release_data(fibh->sbh);
+ udf_release_data(bh);
+ *err = -EEXIST;
+ return NULL;
+ }
+ }
+ }
+ else
+ {
+ block = udf_get_lb_pblock(dir->i_sb, UDF_I_LOCATION(dir), 0);
+ if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB)
+ {
+ fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block);
+ fibh->soffset = fibh->eoffset = udf_file_entry_alloc_offset(dir);
+ }
+ else
+ {
+ fibh->sbh = fibh->ebh = NULL;
+ fibh->soffset = fibh->eoffset = sb->s_blocksize;
+ }
+ }
+
+ f_pos += nfidlen;
+
+ if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB &&
+ sb->s_blocksize - fibh->eoffset < nfidlen)
+ {
+ udf_release_data(bh);
+ bh = NULL;
+ fibh->soffset -= udf_ext0_offset(dir);
+ fibh->eoffset -= udf_ext0_offset(dir);
+ f_pos -= (udf_ext0_offset(dir) >> 2);
+ if (fibh->sbh != fibh->ebh)
+ udf_release_data(fibh->ebh);
+ udf_release_data(fibh->sbh);
+ if (!(fibh->sbh = fibh->ebh = udf_expand_dir_adinicb(dir, &block, err)))
+ return NULL;
+ bloc = UDF_I_LOCATION(dir);
+ eloc.logicalBlockNum = block;
+ eloc.partitionReferenceNum = UDF_I_LOCATION(dir).partitionReferenceNum;
+ elen = dir->i_sb->s_blocksize;
+ extoffset = udf_file_entry_alloc_offset(dir);
+ if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_SHORT)
+ extoffset += sizeof(short_ad);
+ else if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_LONG)
+ extoffset += sizeof(long_ad);
+ }
+
+ if (sb->s_blocksize - fibh->eoffset >= nfidlen)
+ {
+ fibh->soffset = fibh->eoffset;
+ fibh->eoffset += nfidlen;
+ if (fibh->sbh != fibh->ebh)
+ {
+ udf_release_data(fibh->sbh);
+ fibh->sbh = fibh->ebh;
+ }
+
+ if (UDF_I_ALLOCTYPE(dir) != ICBTAG_FLAG_AD_IN_ICB)
+ block = eloc.logicalBlockNum + ((elen - 1) >>
+ dir->i_sb->s_blocksize_bits);
+ else
+ block = UDF_I_LOCATION(dir).logicalBlockNum;
+
+ fi = (struct fileIdentDesc *)(fibh->sbh->b_data + fibh->soffset);
+ }
+ else
+ {
+ fibh->soffset = fibh->eoffset - sb->s_blocksize;
+ fibh->eoffset += nfidlen - sb->s_blocksize;
+ if (fibh->sbh != fibh->ebh)
+ {
+ udf_release_data(fibh->sbh);
+ fibh->sbh = fibh->ebh;
+ }
+
+ block = eloc.logicalBlockNum + ((elen - 1) >>
+ dir->i_sb->s_blocksize_bits);
+
+ if (!(fibh->ebh = udf_bread(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2), 1, err)))
+ {
+ udf_release_data(bh);
+ udf_release_data(fibh->sbh);
+ return NULL;
+ }
+
+ if (!(fibh->soffset))
+ {
+ if (udf_next_aext(dir, &bloc, &extoffset, &eloc, &elen, &bh, 1) ==
+ (EXT_RECORDED_ALLOCATED >> 30))
+ {
+ block = eloc.logicalBlockNum + ((elen - 1) >>
+ dir->i_sb->s_blocksize_bits);
+ }
+ else
+ block ++;
+
+ udf_release_data(fibh->sbh);
+ fibh->sbh = fibh->ebh;
+ fi = (struct fileIdentDesc *)(fibh->sbh->b_data);
+ }
+ else
+ {
+ fi = (struct fileIdentDesc *)
+ (fibh->sbh->b_data + sb->s_blocksize + fibh->soffset);
+ }
+ }
+
+ memset(cfi, 0, sizeof(struct fileIdentDesc));
+ if (UDF_SB_UDFREV(sb) >= 0x0200)
+ udf_new_tag((char *)cfi, TAG_IDENT_FID, 3, 1, block, sizeof(tag));
+ else
+ udf_new_tag((char *)cfi, TAG_IDENT_FID, 2, 1, block, sizeof(tag));
+ cfi->fileVersionNum = cpu_to_le16(1);
+ cfi->lengthFileIdent = namelen;
+ cfi->lengthOfImpUse = cpu_to_le16(0);
+ if (!udf_write_fi(dir, cfi, fi, fibh, NULL, name))
+ {
+ udf_release_data(bh);
+ dir->i_size += nfidlen;
+ if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB)
+ UDF_I_LENALLOC(dir) += nfidlen;
+ dir->i_version = ++event;
+ mark_inode_dirty(dir);
+ return fi;
+ }
+ else
+ {
+ udf_release_data(bh);
+ if (fibh->sbh != fibh->ebh)
+ udf_release_data(fibh->ebh);
+ udf_release_data(fibh->sbh);
+ *err = -EIO;
+ return NULL;
+ }
+}
+
+static int udf_delete_entry(struct inode *inode, struct fileIdentDesc *fi,
+ struct udf_fileident_bh *fibh, struct fileIdentDesc *cfi)
+{
+ cfi->fileCharacteristics |= FID_FILE_CHAR_DELETED;
+ if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT))
+ memset(&(cfi->icb), 0x00, sizeof(long_ad));
+ return udf_write_fi(inode, cfi, fi, fibh, NULL, NULL);
+}
+
+static int udf_create(struct inode *dir, struct dentry *dentry, int mode)
+{
+ struct udf_fileident_bh fibh;
+ struct inode *inode;
+ struct fileIdentDesc cfi, *fi;
+ int err;
+
+ inode = udf_new_inode(dir, mode, &err);
+ if (!inode)
+ return err;
+
+ if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB)
+ inode->i_data.a_ops = &udf_adinicb_aops;
+ else
+ inode->i_data.a_ops = &udf_aops;
+ inode->i_op = &udf_file_inode_operations;
+ inode->i_fop = &udf_file_operations;
+ inode->i_mode = mode;
+ mark_inode_dirty(inode);
+
+ if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err)))
+ {
+ inode->i_nlink --;
+ mark_inode_dirty(inode);
+ iput(inode);
+ return err;
+ }
+ cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize);
+ cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(inode));
+ *(uint32_t *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse =
+ cpu_to_le32(UDF_I_UNIQUE(inode) & 0x00000000FFFFFFFFUL);
+ udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL);
+ if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB)
+ {
+ mark_inode_dirty(dir);
+ dir->i_version = ++event;
+ }
+ if (fibh.sbh != fibh.ebh)
+ udf_release_data(fibh.ebh);
+ udf_release_data(fibh.sbh);
+ d_instantiate(dentry, inode);
+ return 0;
+}
+
+static int udf_mknod(struct inode * dir, struct dentry * dentry, int mode, int rdev)
+{
+ struct inode * inode;
+ struct udf_fileident_bh fibh;
+ int err;
+ struct fileIdentDesc cfi, *fi;
+
+ err = -EIO;
+ inode = udf_new_inode(dir, mode, &err);
+ if (!inode)
+ goto out;
+
+ inode->i_uid = current->fsuid;
+ init_special_inode(inode, mode, rdev);
+ if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err)))
+ {
+ inode->i_nlink --;
+ mark_inode_dirty(inode);
+ iput(inode);
+ return err;
+ }
+ cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize);
+ cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(inode));
+ *(uint32_t *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse =
+ cpu_to_le32(UDF_I_UNIQUE(inode) & 0x00000000FFFFFFFFUL);
+ udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL);
+ if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB)
+ {
+ mark_inode_dirty(dir);
+ dir->i_version = ++event;
+ }
+ mark_inode_dirty(inode);
+
+ if (fibh.sbh != fibh.ebh)
+ udf_release_data(fibh.ebh);
+ udf_release_data(fibh.sbh);
+ d_instantiate(dentry, inode);
+ err = 0;
+out:
+ return err;
+}
+
+static int udf_mkdir(struct inode * dir, struct dentry * dentry, int mode)
+{
+ struct inode * inode;
+ struct udf_fileident_bh fibh;
+ int err;
+ struct fileIdentDesc cfi, *fi;
+
+ err = -EMLINK;
+ if (dir->i_nlink >= (256<<sizeof(dir->i_nlink))-1)
+ goto out;
+
+ err = -EIO;
+ inode = udf_new_inode(dir, S_IFDIR, &err);
+ if (!inode)
+ goto out;
+
+ inode->i_op = &udf_dir_inode_operations;
+ inode->i_fop = &udf_dir_operations;
+ if (!(fi = udf_add_entry(inode, NULL, &fibh, &cfi, &err)))
+ {
+ inode->i_nlink--;
+ mark_inode_dirty(inode);
+ iput(inode);
+ goto out;
+ }
+ inode->i_nlink = 2;
+ cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize);
+ cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(dir));
+ *(uint32_t *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse =
+ cpu_to_le32(UDF_I_UNIQUE(dir) & 0x00000000FFFFFFFFUL);
+ cfi.fileCharacteristics = FID_FILE_CHAR_DIRECTORY | FID_FILE_CHAR_PARENT;
+ udf_write_fi(inode, &cfi, fi, &fibh, NULL, NULL);
+ udf_release_data(fibh.sbh);
+ inode->i_mode = S_IFDIR | mode;
+ if (dir->i_mode & S_ISGID)
+ inode->i_mode |= S_ISGID;
+ mark_inode_dirty(inode);
+
+ if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err)))
+ {
+ inode->i_nlink = 0;
+ mark_inode_dirty(inode);
+ iput(inode);
+ goto out;
+ }
+ cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize);
+ cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(inode));
+ *(uint32_t *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse =
+ cpu_to_le32(UDF_I_UNIQUE(inode) & 0x00000000FFFFFFFFUL);
+ cfi.fileCharacteristics |= FID_FILE_CHAR_DIRECTORY;
+ udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL);
+ dir->i_version = ++event;
+ dir->i_nlink++;
+ mark_inode_dirty(dir);
+ d_instantiate(dentry, inode);
+ if (fibh.sbh != fibh.ebh)
+ udf_release_data(fibh.ebh);
+ udf_release_data(fibh.sbh);
+ err = 0;
+out:
+ return err;
+}
+
+static int empty_dir(struct inode *dir)
+{
+ struct fileIdentDesc *fi, cfi;
+ struct udf_fileident_bh fibh;
+ loff_t f_pos;
+ loff_t size = (udf_ext0_offset(dir) + dir->i_size) >> 2;
+ int block;
+ lb_addr bloc, eloc;
+ uint32_t extoffset, elen, offset;
+ struct buffer_head *bh = NULL;
+
+ f_pos = (udf_ext0_offset(dir) >> 2);
+
+ fibh.soffset = fibh.eoffset = (f_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2;
+ if (inode_bmap(dir, f_pos >> (dir->i_sb->s_blocksize_bits - 2),
+ &bloc, &extoffset, &eloc, &elen, &offset, &bh) == (EXT_RECORDED_ALLOCATED >> 30))
+ {
+ offset >>= dir->i_sb->s_blocksize_bits;
+ block = udf_get_lb_pblock(dir->i_sb, eloc, offset);
+ if ((++offset << dir->i_sb->s_blocksize_bits) < elen)
+ {
+ if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_SHORT)
+ extoffset -= sizeof(short_ad);
+ else if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_LONG)
+ extoffset -= sizeof(long_ad);
+ }
+ else
+ offset = 0;
+ }
+ else
+ {
+ udf_release_data(bh);
+ return 0;
+ }
+
+ if (!(fibh.sbh = fibh.ebh = udf_tread(dir->i_sb, block)))
+ return 0;
+
+ while ( (f_pos < size) )
+ {
+ fi = udf_fileident_read(dir, &f_pos, &fibh, &cfi, &bloc, &extoffset, &eloc, &elen, &offset, &bh);
+
+ if (!fi)
+ {
+ if (fibh.sbh != fibh.ebh)
+ udf_release_data(fibh.ebh);
+ udf_release_data(fibh.sbh);
+ udf_release_data(bh);
+ return 0;
+ }
+
+ if (cfi.lengthFileIdent && (cfi.fileCharacteristics & FID_FILE_CHAR_DELETED) == 0)
+ {
+ udf_release_data(bh);
+ return 0;
+ }
+ }
+ if (fibh.sbh != fibh.ebh)
+ udf_release_data(fibh.ebh);
+ udf_release_data(fibh.sbh);
+ udf_release_data(bh);
+ return 1;
+}
+
+static int udf_rmdir(struct inode * dir, struct dentry * dentry)
+{
+ int retval;
+ struct inode * inode = dentry->d_inode;
+ struct udf_fileident_bh fibh;
+ struct fileIdentDesc *fi, cfi;
+
+ retval = -ENOENT;
+ fi = udf_find_entry(dir, dentry, &fibh, &cfi);
+ if (!fi)
+ goto out;
+
+ retval = -EIO;
+ if (udf_get_lb_pblock(dir->i_sb, lelb_to_cpu(cfi.icb.extLocation), 0) != inode->i_ino)
+ goto end_rmdir;
+ retval = -ENOTEMPTY;
+ if (!empty_dir(inode))
+ goto end_rmdir;
+ retval = udf_delete_entry(dir, fi, &fibh, &cfi);
+ dir->i_version = ++event;
+ if (retval)
+ goto end_rmdir;
+ if (inode->i_nlink != 2)
+ udf_warning(inode->i_sb, "udf_rmdir",
+ "empty directory has nlink != 2 (%d)",
+ inode->i_nlink);
+ inode->i_version = ++event;
+ inode->i_nlink = 0;
+ inode->i_size = 0;
+ mark_inode_dirty(inode);
+ dir->i_nlink --;
+ inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ UDF_I_UCTIME(inode) = UDF_I_UCTIME(dir) = UDF_I_UMTIME(dir) = CURRENT_UTIME;
+ mark_inode_dirty(dir);
+
+end_rmdir:
+ if (fibh.sbh != fibh.ebh)
+ udf_release_data(fibh.ebh);
+ udf_release_data(fibh.sbh);
+out:
+ return retval;
+}
+
+static int udf_unlink(struct inode * dir, struct dentry * dentry)
+{
+ int retval;
+ struct inode * inode = dentry->d_inode;
+ struct udf_fileident_bh fibh;
+ struct fileIdentDesc *fi;
+ struct fileIdentDesc cfi;
+
+ retval = -ENOENT;
+ fi = udf_find_entry(dir, dentry, &fibh, &cfi);
+ if (!fi)
+ goto out;
+
+ retval = -EIO;
+
+ if (udf_get_lb_pblock(dir->i_sb, lelb_to_cpu(cfi.icb.extLocation), 0) !=
+ inode->i_ino)
+ {
+ goto end_unlink;
+ }
+
+ if (!inode->i_nlink)
+ {
+ udf_debug("Deleting nonexistent file (%lu), %d\n",
+ inode->i_ino, inode->i_nlink);
+ inode->i_nlink = 1;
+ }
+ retval = udf_delete_entry(dir, fi, &fibh, &cfi);
+ if (retval)
+ goto end_unlink;
+ dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ UDF_I_UCTIME(dir) = UDF_I_UMTIME(dir) = CURRENT_UTIME;
+ mark_inode_dirty(dir);
+ inode->i_nlink--;
+ mark_inode_dirty(inode);
+ inode->i_ctime = dir->i_ctime;
+ retval = 0;
+
+end_unlink:
+ if (fibh.sbh != fibh.ebh)
+ udf_release_data(fibh.ebh);
+ udf_release_data(fibh.sbh);
+out:
+ return retval;
+}
+
+static int udf_symlink(struct inode * dir, struct dentry * dentry, const char * symname)
+{
+ struct inode * inode;
+ struct pathComponent *pc;
+ char *compstart;
+ struct udf_fileident_bh fibh;
+ struct buffer_head *bh = NULL;
+ int eoffset, elen = 0;
+ struct fileIdentDesc *fi;
+ struct fileIdentDesc cfi;
+ char *ea;
+ int err;
+ int block;
+
+ if (!(inode = udf_new_inode(dir, S_IFLNK, &err)))
+ goto out;
+
+ inode->i_mode = S_IFLNK | S_IRWXUGO;
+ inode->i_data.a_ops = &udf_symlink_aops;
+ inode->i_op = &page_symlink_inode_operations;
+
+ if (UDF_I_ALLOCTYPE(inode) != ICBTAG_FLAG_AD_IN_ICB)
+ {
+ struct buffer_head *bh = NULL;
+ lb_addr bloc, eloc;
+ uint32_t elen, extoffset;
+
+ block = udf_new_block(inode->i_sb, inode,
+ UDF_I_LOCATION(inode).partitionReferenceNum,
+ UDF_I_LOCATION(inode).logicalBlockNum, &err);
+ if (!block)
+ goto out_no_entry;
+ bloc = UDF_I_LOCATION(inode);
+ eloc.logicalBlockNum = block;
+ eloc.partitionReferenceNum = UDF_I_LOCATION(inode).partitionReferenceNum;
+ elen = inode->i_sb->s_blocksize;
+ UDF_I_LENEXTENTS(inode) = elen;
+ extoffset = udf_file_entry_alloc_offset(inode);
+ udf_add_aext(inode, &bloc, &extoffset, eloc, elen, &bh, 0);
+ udf_release_data(bh);
+
+ block = udf_get_pblock(inode->i_sb, block,
+ UDF_I_LOCATION(inode).partitionReferenceNum, 0);
+ bh = udf_tread(inode->i_sb, block);
+ lock_buffer(bh);
+ memset(bh->b_data, 0x00, inode->i_sb->s_blocksize);
+ mark_buffer_uptodate(bh, 1);
+ unlock_buffer(bh);
+ mark_buffer_dirty_inode(bh, inode);
+ }
+ else
+ {
+ block = udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0);
+ bh = udf_tread(inode->i_sb, block);
+ }
+ ea = bh->b_data + udf_ext0_offset(inode);
+
+ eoffset = inode->i_sb->s_blocksize - udf_ext0_offset(inode);
+ pc = (struct pathComponent *)ea;
+
+ if (*symname == '/')
+ {
+ do
+ {
+ symname++;
+ } while (*symname == '/');
+
+ pc->componentType = 1;
+ pc->lengthComponentIdent = 0;
+ pc->componentFileVersionNum = 0;
+ pc += sizeof(struct pathComponent);
+ elen += sizeof(struct pathComponent);
+ }
+
+ err = -ENAMETOOLONG;
+
+ while (*symname)
+ {
+ if (elen + sizeof(struct pathComponent) > eoffset)
+ goto out_no_entry;
+
+ pc = (struct pathComponent *)(ea + elen);
+
+ compstart = (char *)symname;
+
+ do
+ {
+ symname++;
+ } while (*symname && *symname != '/');
+
+ pc->componentType = 5;
+ pc->lengthComponentIdent = 0;
+ pc->componentFileVersionNum = 0;
+ if (pc->componentIdent[0] == '.')
+ {
+ if (pc->lengthComponentIdent == 1)
+ pc->componentType = 4;
+ else if (pc->lengthComponentIdent == 2 && pc->componentIdent[1] == '.')
+ pc->componentType = 3;
+ }
+
+ if (pc->componentType == 5)
+ {
+ if (elen + sizeof(struct pathComponent) + symname - compstart > eoffset)
+ goto out_no_entry;
+ else
+ pc->lengthComponentIdent = symname - compstart;
+
+ memcpy(pc->componentIdent, compstart, pc->lengthComponentIdent);
+ }
+
+ elen += sizeof(struct pathComponent) + pc->lengthComponentIdent;
+
+ if (*symname)
+ {
+ do
+ {
+ symname++;
+ } while (*symname == '/');
+ }
+ }
+
+ udf_release_data(bh);
+ inode->i_size = elen;
+ if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB)
+ UDF_I_LENALLOC(inode) = inode->i_size;
+ mark_inode_dirty(inode);
+
+ if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err)))
+ goto out_no_entry;
+ cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize);
+ cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(inode));
+ if (UDF_SB_LVIDBH(inode->i_sb))
+ {
+ struct logicalVolHeaderDesc *lvhd;
+ uint64_t uniqueID;
+ lvhd = (struct logicalVolHeaderDesc *)(UDF_SB_LVID(inode->i_sb)->logicalVolContentsUse);
+ uniqueID = le64_to_cpu(lvhd->uniqueID);
+ *(uint32_t *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse =
+ cpu_to_le32(uniqueID & 0x00000000FFFFFFFFUL);
+ if (!(++uniqueID & 0x00000000FFFFFFFFUL))
+ uniqueID += 16;
+ lvhd->uniqueID = cpu_to_le64(uniqueID);
+ mark_buffer_dirty(UDF_SB_LVIDBH(inode->i_sb));
+ }
+ udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL);
+ if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB)
+ {
+ mark_inode_dirty(dir);
+ dir->i_version = ++event;
+ }
+ if (fibh.sbh != fibh.ebh)
+ udf_release_data(fibh.ebh);
+ udf_release_data(fibh.sbh);
+ d_instantiate(dentry, inode);
+ err = 0;
+
+out:
+ return err;
+
+out_no_entry:
+ inode->i_nlink--;
+ mark_inode_dirty(inode);
+ iput(inode);
+ goto out;
+}
+
+static int udf_link(struct dentry * old_dentry, struct inode * dir,
+ struct dentry *dentry)
+{
+ struct inode *inode = old_dentry->d_inode;
+ struct udf_fileident_bh fibh;
+ int err;
+ struct fileIdentDesc cfi, *fi;
+
+ if (S_ISDIR(inode->i_mode))
+ return -EPERM;
+
+ if (inode->i_nlink >= (256<<sizeof(inode->i_nlink))-1)
+ return -EMLINK;
+
+ if (!(fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err)))
+ return err;
+ cfi.icb.extLength = cpu_to_le32(inode->i_sb->s_blocksize);
+ cfi.icb.extLocation = cpu_to_lelb(UDF_I_LOCATION(inode));
+ if (UDF_SB_LVIDBH(inode->i_sb))
+ {
+ struct logicalVolHeaderDesc *lvhd;
+ uint64_t uniqueID;
+ lvhd = (struct logicalVolHeaderDesc *)(UDF_SB_LVID(inode->i_sb)->logicalVolContentsUse);
+ uniqueID = le64_to_cpu(lvhd->uniqueID);
+ *(uint32_t *)((struct allocDescImpUse *)cfi.icb.impUse)->impUse =
+ cpu_to_le32(uniqueID & 0x00000000FFFFFFFFUL);
+ if (!(++uniqueID & 0x00000000FFFFFFFFUL))
+ uniqueID += 16;
+ lvhd->uniqueID = cpu_to_le64(uniqueID);
+ mark_buffer_dirty(UDF_SB_LVIDBH(inode->i_sb));
+ }
+ udf_write_fi(dir, &cfi, fi, &fibh, NULL, NULL);
+ if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB)
+ {
+ mark_inode_dirty(dir);
+ dir->i_version = ++event;
+ }
+ if (fibh.sbh != fibh.ebh)
+ udf_release_data(fibh.ebh);
+ udf_release_data(fibh.sbh);
+ inode->i_nlink ++;
+ inode->i_ctime = CURRENT_TIME;
+ UDF_I_UCTIME(inode) = CURRENT_UTIME;
+ mark_inode_dirty(inode);
+ atomic_inc(&inode->i_count);
+ d_instantiate(dentry, inode);
+ return 0;
+}
+
+/* Anybody can rename anything with this: the permission checks are left to the
+ * higher-level routines.
+ */
+static int udf_rename (struct inode * old_dir, struct dentry * old_dentry,
+ struct inode * new_dir, struct dentry * new_dentry)
+{
+ struct inode * old_inode = old_dentry->d_inode;
+ struct inode * new_inode = new_dentry->d_inode;
+ struct udf_fileident_bh ofibh, nfibh;
+ struct fileIdentDesc *ofi = NULL, *nfi = NULL, *dir_fi = NULL, ocfi, ncfi;
+ struct buffer_head *dir_bh = NULL;
+ int retval = -ENOENT;
+
+ if ((ofi = udf_find_entry(old_dir, old_dentry, &ofibh, &ocfi)))
+ {
+ if (ofibh.sbh != ofibh.ebh)
+ udf_release_data(ofibh.ebh);
+ udf_release_data(ofibh.sbh);
+ }
+ if (!ofi || udf_get_lb_pblock(old_dir->i_sb, lelb_to_cpu(ocfi.icb.extLocation), 0) !=
+ old_inode->i_ino)
+ {
+ goto end_rename;
+ }
+
+ nfi = udf_find_entry(new_dir, new_dentry, &nfibh, &ncfi);
+ if (nfi)
+ {
+ if (!new_inode)
+ {
+ if (nfibh.sbh != nfibh.ebh)
+ udf_release_data(nfibh.ebh);
+ udf_release_data(nfibh.sbh);
+ nfi = NULL;
+ }
+ }
+ if (S_ISDIR(old_inode->i_mode))
+ {
+ uint32_t offset = udf_ext0_offset(old_inode);
+
+ if (new_inode)
+ {
+ retval = -ENOTEMPTY;
+ if (!empty_dir(new_inode))
+ goto end_rename;
+ }
+ retval = -EIO;
+ dir_bh = udf_bread(old_inode, 0, 0, &retval);
+ if (!dir_bh)
+ goto end_rename;
+ dir_fi = udf_get_fileident(dir_bh->b_data, old_inode->i_sb->s_blocksize, &offset);
+ if (!dir_fi)
+ goto end_rename;
+ if (udf_get_lb_pblock(old_inode->i_sb, cpu_to_lelb(dir_fi->icb.extLocation), 0) !=
+ old_dir->i_ino)
+ {
+ goto end_rename;
+ }
+ retval = -EMLINK;
+ if (!new_inode && new_dir->i_nlink >= (256<<sizeof(new_dir->i_nlink))-1)
+ goto end_rename;
+ }
+ if (!nfi)
+ {
+ nfi = udf_add_entry(new_dir, new_dentry, &nfibh, &ncfi, &retval);
+ if (!nfi)
+ goto end_rename;
+ }
+ new_dir->i_version = ++event;
+
+ /*
+ * Like most other Unix systems, set the ctime for inodes on a
+ * rename.
+ */
+ old_inode->i_ctime = CURRENT_TIME;
+ UDF_I_UCTIME(old_inode) = CURRENT_UTIME;
+ mark_inode_dirty(old_inode);
+
+ /*
+ * ok, that's it
+ */
+ ncfi.fileVersionNum = ocfi.fileVersionNum;
+ ncfi.fileCharacteristics = ocfi.fileCharacteristics;
+ memcpy(&(ncfi.icb), &(ocfi.icb), sizeof(long_ad));
+ udf_write_fi(new_dir, &ncfi, nfi, &nfibh, NULL, NULL);
+
+ /* The old fid may have moved - find it again */
+ ofi = udf_find_entry(old_dir, old_dentry, &ofibh, &ocfi);
+ udf_delete_entry(old_dir, ofi, &ofibh, &ocfi);
+
+ old_dir->i_version = ++event;
+ if (new_inode)
+ {
+ new_inode->i_nlink--;
+ new_inode->i_ctime = CURRENT_TIME;
+ UDF_I_UCTIME(new_inode) = CURRENT_UTIME;
+ mark_inode_dirty(new_inode);
+ }
+ old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
+ UDF_I_UCTIME(old_dir) = UDF_I_UMTIME(old_dir) = CURRENT_UTIME;
+ mark_inode_dirty(old_dir);
+
+ if (dir_bh)
+ {
+ dir_fi->icb.extLocation = lelb_to_cpu(UDF_I_LOCATION(new_dir));
+ udf_update_tag((char *)dir_fi, (sizeof(struct fileIdentDesc) +
+ cpu_to_le16(dir_fi->lengthOfImpUse) + 3) & ~3);
+ if (UDF_I_ALLOCTYPE(old_inode) == ICBTAG_FLAG_AD_IN_ICB)
+ {
+ old_inode->i_version = ++event;
+ mark_inode_dirty(old_inode);
+ }
+ else
+ mark_buffer_dirty_inode(dir_bh, old_inode);
+ old_dir->i_nlink --;
+ mark_inode_dirty(old_dir);
+ if (new_inode)
+ {
+ new_inode->i_nlink --;
+ mark_inode_dirty(new_inode);
+ }
+ else
+ {
+ new_dir->i_nlink ++;
+ mark_inode_dirty(new_dir);
+ }
+ }
+
+ if (ofi)
+ {
+ if (ofibh.sbh != ofibh.ebh)
+ udf_release_data(ofibh.ebh);
+ udf_release_data(ofibh.sbh);
+ }
+
+ retval = 0;
+
+end_rename:
+ udf_release_data(dir_bh);
+ if (nfi)
+ {
+ if (nfibh.sbh != nfibh.ebh)
+ udf_release_data(nfibh.ebh);
+ udf_release_data(nfibh.sbh);
+ }
+ return retval;
+}
+
+struct inode_operations udf_dir_inode_operations = {
+ lookup: udf_lookup,
+ create: udf_create,
+ link: udf_link,
+ unlink: udf_unlink,
+ symlink: udf_symlink,
+ mkdir: udf_mkdir,
+ rmdir: udf_rmdir,
+ mknod: udf_mknod,
+ rename: udf_rename,
+};
diff --git a/fs/udf/osta_udf.h b/fs/udf/osta_udf.h
new file mode 100644
index 00000000000000..ab3b6b67da24e3
--- /dev/null
+++ b/fs/udf/osta_udf.h
@@ -0,0 +1,271 @@
+/*
+ * osta_udf.h
+ *
+ * This file is based on OSTA UDF(tm) 2.01 (March 15, 2000)
+ * http://www.osta.org
+ *
+ * Copyright (c) 2001-2002 Ben Fennema <bfennema@falcon.csc.calpoly.edu>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "ecma_167.h"
+
+#ifndef _OSTA_UDF_H
+#define _OSTA_UDF_H 1
+
+/* OSTA CS0 Charspec (UDF 2.01 2.1.2) */
+#define UDF_CHAR_SET_TYPE 0
+#define UDF_CHAR_SET_INFO "OSTA Compressed Unicode"
+
+/* Entity Identifier (UDF 2.01 2.1.5) */
+/* Identifiers (UDF 2.01 2.1.5.2) */
+#define UDF_ID_DEVELOPER "*Linux UDFFS"
+#define UDF_ID_COMPLIANT "*OSTA UDF Compliant"
+#define UDF_ID_LV_INFO "*UDF LV Info"
+#define UDF_ID_FREE_EA "*UDF FreeEASpace"
+#define UDF_ID_FREE_APP_EA "*UDF FreeAppEASpace"
+#define UDF_ID_DVD_CGMS "*UDF DVD CGMS Info"
+#define UDF_ID_OS2_EA "*UDF OS/2 EA"
+#define UDF_ID_OS2_EA_LENGTH "*UDF OS/2 EALength"
+#define UDF_ID_MAC_VOLUME "*UDF Mac VolumeInfo"
+#define UDF_ID_MAC_FINDER "*UDF Mac FinderInfo"
+#define UDF_ID_MAC_UNIQUE "*UDF Mac UniqueIDTable"
+#define UDF_ID_MAC_RESOURCE "*UDF Mac ResourceFork"
+#define UDF_ID_VIRTUAL "*UDF Virtual Partition"
+#define UDF_ID_SPARABLE "*UDF Sparable Partition"
+#define UDF_ID_ALLOC "*UDF Virtual Alloc Tbl"
+#define UDF_ID_SPARING "*UDF Sparing Table"
+
+/* Identifier Suffix (UDF 2.01 2.1.5.3) */
+#define IS_DF_HARD_WRITE_PROTECT 0x01
+#define IS_DF_SOFT_WRITE_PROTECT 0x02
+
+struct UDFIdentSuffix
+{
+ uint16_t UDFRevision;
+ uint8_t OSClass;
+ uint8_t OSIdentifier;
+ uint8_t reserved[4];
+} __attribute__ ((packed));
+
+struct impIdentSuffix
+{
+ uint8_t OSClass;
+ uint8_t OSIdentifier;
+ uint8_t reserved[6];
+} __attribute__ ((packed));
+
+struct appIdentSuffix
+{
+ uint8_t impUse[8];
+} __attribute__ ((packed));
+
+/* Logical Volume Integrity Descriptor (UDF 2.01 2.2.6) */
+/* Implementation Use (UDF 2.01 2.2.6.4) */
+struct logicalVolIntegrityDescImpUse
+{
+ regid impIdent;
+ uint32_t numFiles;
+ uint32_t numDirs;
+ uint16_t minUDFReadRev;
+ uint16_t minUDFWriteRev;
+ uint16_t maxUDFWriteRev;
+ uint8_t impUse[0];
+} __attribute__ ((packed));
+
+/* Implementation Use Volume Descriptor (UDF 2.01 2.2.7) */
+/* Implementation Use (UDF 2.01 2.2.7.2) */
+struct impUseVolDescImpUse
+{
+ charspec LVICharset;
+ dstring logicalVolIdent[128];
+ dstring LVInfo1[36];
+ dstring LVInfo2[36];
+ dstring LVInfo3[36];
+ regid impIdent;
+ uint8_t impUse[128];
+} __attribute__ ((packed));
+
+struct udfPartitionMap2
+{
+ uint8_t partitionMapType;
+ uint8_t partitionMapLength;
+ uint8_t reserved1[2];
+ regid partIdent;
+ uint16_t volSeqNum;
+ uint16_t partitionNum;
+} __attribute__ ((packed));
+
+/* Virtual Partition Map (UDF 2.01 2.2.8) */
+struct virtualPartitionMap
+{
+ uint8_t partitionMapType;
+ uint8_t partitionMapLength;
+ uint8_t reserved1[2];
+ regid partIdent;
+ uint16_t volSeqNum;
+ uint16_t partitionNum;
+ uint8_t reserved2[24];
+} __attribute__ ((packed));
+
+/* Sparable Partition Map (UDF 2.01 2.2.9) */
+struct sparablePartitionMap
+{
+ uint8_t partitionMapType;
+ uint8_t partitionMapLength;
+ uint8_t reserved1[2];
+ regid partIdent;
+ uint16_t volSeqNum;
+ uint16_t partitionNum;
+ uint16_t packetLength;
+ uint8_t numSparingTables;
+ uint8_t reserved2[1];
+ uint32_t sizeSparingTable;
+ uint32_t locSparingTable[4];
+} __attribute__ ((packed));
+
+/* Virtual Allocation Table (UDF 1.5 2.2.10) */
+struct virtualAllocationTable15
+{
+ uint32_t VirtualSector[0];
+ regid ident;
+ uint32_t previousVATICB;
+} __attribute__ ((packed));
+
+#define ICBTAG_FILE_TYPE_VAT15 0x00U
+
+/* Virtual Allocation Table (UDF 2.01 2.2.10) */
+struct virtualAllocationTable20
+{
+ uint16_t lengthHeader;
+ uint16_t lengthImpUse;
+ dstring logicalVolIdent[128];
+ uint32_t previousVatICBLoc;
+ uint32_t numFIDSFiles;
+ uint32_t numFIDSDirectories;
+ uint16_t minReadRevision;
+ uint16_t minWriteRevision;
+ uint16_t maxWriteRevision;
+ uint16_t reserved;
+ uint8_t impUse[0];
+ uint32_t vatEntry[0];
+} __attribute__ ((packed));
+
+#define ICBTAG_FILE_TYPE_VAT20 0xF8U
+
+/* Sparing Table (UDF 2.01 2.2.11) */
+struct sparingEntry
+{
+ uint32_t origLocation;
+ uint32_t mappedLocation;
+} __attribute__ ((packed));
+
+struct sparingTable
+{
+ tag descTag;
+ regid sparingIdent;
+ uint16_t reallocationTableLen;
+ uint16_t reserved;
+ uint32_t sequenceNum;
+ struct sparingEntry
+ mapEntry[0];
+} __attribute__ ((packed));
+
+/* struct long_ad ICB - ADImpUse (UDF 2.01 2.2.4.3) */
+struct allocDescImpUse
+{
+ uint16_t flags;
+ uint8_t impUse[4];
+} __attribute__ ((packed));
+
+#define AD_IU_EXT_ERASED 0x0001
+
+/* Real-Time Files (UDF 2.01 6.11) */
+#define ICBTAG_FILE_TYPE_REALTIME 0xF9U
+
+/* Implementation Use Extended Attribute (UDF 2.01 3.3.4.5) */
+/* FreeEASpace (UDF 2.01 3.3.4.5.1.1) */
+struct freeEaSpace
+{
+ uint16_t headerChecksum;
+ uint8_t freeEASpace[0];
+} __attribute__ ((packed));
+
+/* DVD Copyright Management Information (UDF 2.01 3.3.4.5.1.2) */
+struct DVDCopyrightImpUse
+{
+ uint16_t headerChecksum;
+ uint8_t CGMSInfo;
+ uint8_t dataType;
+ uint8_t protectionSystemInfo[4];
+} __attribute__ ((packed));
+
+/* Application Use Extended Attribute (UDF 2.01 3.3.4.6) */
+/* FreeAppEASpace (UDF 2.01 3.3.4.6.1) */
+struct freeAppEASpace
+{
+ uint16_t headerChecksum;
+ uint8_t freeEASpace[0];
+} __attribute__ ((packed));
+
+/* UDF Defined System Stream (UDF 2.01 3.3.7) */
+#define UDF_ID_UNIQUE_ID "*UDF Unique ID Mapping Data"
+#define UDF_ID_NON_ALLOC "*UDF Non-Allocatable Space"
+#define UDF_ID_POWER_CAL "*UDF Power Cal Table"
+#define UDF_ID_BACKUP "*UDF Backup"
+
+/* Operating System Identifiers (UDF 2.01 6.3) */
+#define UDF_OS_CLASS_UNDEF 0x00U
+#define UDF_OS_CLASS_DOS 0x01U
+#define UDF_OS_CLASS_OS2 0x02U
+#define UDF_OS_CLASS_MAC 0x03U
+#define UDF_OS_CLASS_UNIX 0x04U
+#define UDF_OS_CLASS_WIN9X 0x05U
+#define UDF_OS_CLASS_WINNT 0x06U
+#define UDF_OS_CLASS_OS400 0x07U
+#define UDF_OS_CLASS_BEOS 0x08U
+#define UDF_OS_CLASS_WINCE 0x09U
+
+#define UDF_OS_ID_UNDEF 0x00U
+#define UDF_OS_ID_DOS 0x00U
+#define UDF_OS_ID_OS2 0x00U
+#define UDF_OS_ID_MAC 0x00U
+#define UDF_OS_ID_UNIX 0x00U
+#define UDF_OS_ID_AIX 0x01U
+#define UDF_OS_ID_SOLARIS 0x02U
+#define UDF_OS_ID_HPUX 0x03U
+#define UDF_OS_ID_IRIX 0x04U
+#define UDF_OS_ID_LINUX 0x05U
+#define UDF_OS_ID_MKLINUX 0x06U
+#define UDF_OS_ID_FREEBSD 0x07U
+#define UDF_OS_ID_WIN9X 0x00U
+#define UDF_OS_ID_WINNT 0x00U
+#define UDF_OS_ID_OS400 0x00U
+#define UDF_OS_ID_BEOS 0x00U
+#define UDF_OS_ID_WINCE 0x00U
+
+#endif /* _OSTA_UDF_H */
diff --git a/fs/udf/partition.c b/fs/udf/partition.c
new file mode 100644
index 00000000000000..ba50f93a3c7c6c
--- /dev/null
+++ b/fs/udf/partition.c
@@ -0,0 +1,225 @@
+/*
+ * partition.c
+ *
+ * PURPOSE
+ * Partition handling routines for the OSTA-UDF(tm) filesystem.
+ *
+ * CONTACTS
+ * E-mail regarding any portion of the Linux UDF file system should be
+ * directed to the development team mailing list (run by majordomo):
+ * linux_udf@hpesjro.fc.hp.com
+ *
+ * COPYRIGHT
+ * This file is distributed under the terms of the GNU General Public
+ * License (GPL). Copies of the GPL can be obtained from:
+ * ftp://prep.ai.mit.edu/pub/gnu/GPL
+ * Each contributing author retains all rights to their own work.
+ *
+ * (C) 1998-2001 Ben Fennema
+ *
+ * HISTORY
+ *
+ * 12/06/98 blf Created file.
+ *
+ */
+
+#include "udfdecl.h"
+#include "udf_sb.h"
+#include "udf_i.h"
+
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/udf_fs.h>
+#include <linux/slab.h>
+
+inline uint32_t udf_get_pblock(struct super_block *sb, uint32_t block, uint16_t partition, uint32_t offset)
+{
+ if (partition >= UDF_SB_NUMPARTS(sb))
+ {
+ udf_debug("block=%d, partition=%d, offset=%d: invalid partition\n",
+ block, partition, offset);
+ return 0xFFFFFFFF;
+ }
+ if (UDF_SB_PARTFUNC(sb, partition))
+ return UDF_SB_PARTFUNC(sb, partition)(sb, block, partition, offset);
+ else
+ return UDF_SB_PARTROOT(sb, partition) + block + offset;
+}
+
+uint32_t udf_get_pblock_virt15(struct super_block *sb, uint32_t block, uint16_t partition, uint32_t offset)
+{
+ struct buffer_head *bh = NULL;
+ uint32_t newblock;
+ uint32_t index;
+ uint32_t loc;
+
+ index = (sb->s_blocksize - UDF_SB_TYPEVIRT(sb,partition).s_start_offset) / sizeof(uint32_t);
+
+ if (block > UDF_SB_TYPEVIRT(sb,partition).s_num_entries)
+ {
+ udf_debug("Trying to access block beyond end of VAT (%d max %d)\n",
+ block, UDF_SB_TYPEVIRT(sb,partition).s_num_entries);
+ return 0xFFFFFFFF;
+ }
+
+ if (block >= index)
+ {
+ block -= index;
+ newblock = 1 + (block / (sb->s_blocksize / sizeof(uint32_t)));
+ index = block % (sb->s_blocksize / sizeof(uint32_t));
+ }
+ else
+ {
+ newblock = 0;
+ index = UDF_SB_TYPEVIRT(sb,partition).s_start_offset / sizeof(uint32_t) + block;
+ }
+
+ loc = udf_block_map(UDF_SB_VAT(sb), newblock);
+
+ if (!(bh = sb_bread(sb, loc)))
+ {
+ udf_debug("get_pblock(UDF_VIRTUAL_MAP:%p,%d,%d) VAT: %d[%d]\n",
+ sb, block, partition, loc, index);
+ return 0xFFFFFFFF;
+ }
+
+ loc = le32_to_cpu(((uint32_t *)bh->b_data)[index]);
+
+ udf_release_data(bh);
+
+ if (UDF_I_LOCATION(UDF_SB_VAT(sb)).partitionReferenceNum == partition)
+ {
+ udf_debug("recursive call to udf_get_pblock!\n");
+ return 0xFFFFFFFF;
+ }
+
+ return udf_get_pblock(sb, loc, UDF_I_LOCATION(UDF_SB_VAT(sb)).partitionReferenceNum, offset);
+}
+
+inline uint32_t udf_get_pblock_virt20(struct super_block *sb, uint32_t block, uint16_t partition, uint32_t offset)
+{
+ return udf_get_pblock_virt15(sb, block, partition, offset);
+}
+
+uint32_t udf_get_pblock_spar15(struct super_block *sb, uint32_t block, uint16_t partition, uint32_t offset)
+{
+ int i;
+ struct sparingTable *st = NULL;
+ uint32_t packet = (block + offset) & ~(UDF_SB_TYPESPAR(sb,partition).s_packet_len - 1);
+
+ for (i=0; i<4; i++)
+ {
+ if (UDF_SB_TYPESPAR(sb,partition).s_spar_map[i] != NULL)
+ {
+ st = (struct sparingTable *)UDF_SB_TYPESPAR(sb,partition).s_spar_map[i]->b_data;
+ break;
+ }
+ }
+
+ if (st)
+ {
+ for (i=0; i<st->reallocationTableLen; i++)
+ {
+ if (le32_to_cpu(st->mapEntry[i].origLocation) >= 0xFFFFFFF0)
+ break;
+ else if (le32_to_cpu(st->mapEntry[i].origLocation) == packet)
+ {
+ return le32_to_cpu(st->mapEntry[i].mappedLocation) +
+ ((block + offset) & (UDF_SB_TYPESPAR(sb,partition).s_packet_len - 1));
+ }
+ else if (le32_to_cpu(st->mapEntry[i].origLocation) > packet)
+ break;
+ }
+ }
+ return UDF_SB_PARTROOT(sb,partition) + block + offset;
+}
+
+int udf_relocate_blocks(struct super_block *sb, long old_block, long *new_block)
+{
+ struct udf_sparing_data *sdata;
+ struct sparingTable *st = NULL;
+ struct sparingEntry mapEntry;
+ uint32_t packet;
+ int i, j, k, l;
+
+ for (i=0; i<UDF_SB_NUMPARTS(sb); i++)
+ {
+ if (old_block > UDF_SB_PARTROOT(sb,i) &&
+ old_block < UDF_SB_PARTROOT(sb,i) + UDF_SB_PARTLEN(sb,i))
+ {
+ sdata = &UDF_SB_TYPESPAR(sb,i);
+ packet = (old_block - UDF_SB_PARTROOT(sb,i)) & ~(sdata->s_packet_len - 1);
+
+ for (j=0; j<4; j++)
+ {
+ if (UDF_SB_TYPESPAR(sb,i).s_spar_map[j] != NULL)
+ {
+ st = (struct sparingTable *)sdata->s_spar_map[j]->b_data;
+ break;
+ }
+ }
+
+ if (!st)
+ return 1;
+
+ for (k=0; k<st->reallocationTableLen; k++)
+ {
+ if (le32_to_cpu(st->mapEntry[k].origLocation) == 0xFFFFFFFF)
+ {
+ for (; j<4; j++)
+ {
+ if (sdata->s_spar_map[j])
+ {
+ st = (struct sparingTable *)sdata->s_spar_map[j]->b_data;
+ st->mapEntry[k].origLocation = cpu_to_le32(packet);
+ udf_update_tag((char *)st, sizeof(struct sparingTable) + st->reallocationTableLen * sizeof(struct sparingEntry));
+ mark_buffer_dirty(sdata->s_spar_map[j]);
+ }
+ }
+ *new_block = le32_to_cpu(st->mapEntry[k].mappedLocation) +
+ ((old_block - UDF_SB_PARTROOT(sb,i)) & (sdata->s_packet_len - 1));
+ return 0;
+ }
+ else if (le32_to_cpu(st->mapEntry[k].origLocation) == packet)
+ {
+ *new_block = le32_to_cpu(st->mapEntry[k].mappedLocation) +
+ ((old_block - UDF_SB_PARTROOT(sb,i)) & (sdata->s_packet_len - 1));
+ return 0;
+ }
+ else if (le32_to_cpu(st->mapEntry[k].origLocation) > packet)
+ break;
+ }
+ for (l=k; l<st->reallocationTableLen; l++)
+ {
+ if (le32_to_cpu(st->mapEntry[l].origLocation) == 0xFFFFFFFF)
+ {
+ for (; j<4; j++)
+ {
+ if (sdata->s_spar_map[j])
+ {
+ st = (struct sparingTable *)sdata->s_spar_map[j]->b_data;
+ mapEntry = st->mapEntry[l];
+ mapEntry.origLocation = cpu_to_le32(packet);
+ memmove(&st->mapEntry[k+1], &st->mapEntry[k], (l-k)*sizeof(struct sparingEntry));
+ st->mapEntry[k] = mapEntry;
+ udf_update_tag((char *)st, sizeof(struct sparingTable) + st->reallocationTableLen * sizeof(struct sparingEntry));
+ mark_buffer_dirty(sdata->s_spar_map[j]);
+ }
+ }
+ *new_block = le32_to_cpu(st->mapEntry[k].mappedLocation) +
+ ((old_block - UDF_SB_PARTROOT(sb,i)) & (sdata->s_packet_len - 1));
+ return 0;
+ }
+ }
+ return 1;
+ }
+ }
+ if (i == UDF_SB_NUMPARTS(sb))
+ {
+ /* outside of partitions */
+ /* for now, fail =) */
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/fs/udf/super.c b/fs/udf/super.c
new file mode 100644
index 00000000000000..9df2fa2bae28d6
--- /dev/null
+++ b/fs/udf/super.c
@@ -0,0 +1,1816 @@
+/*
+ * super.c
+ *
+ * PURPOSE
+ * Super block routines for the OSTA-UDF(tm) filesystem.
+ *
+ * DESCRIPTION
+ * OSTA-UDF(tm) = Optical Storage Technology Association
+ * Universal Disk Format.
+ *
+ * This code is based on version 2.00 of the UDF specification,
+ * and revision 3 of the ECMA 167 standard [equivalent to ISO 13346].
+ * http://www.osta.org/
+ * http://www.ecma.ch/
+ * http://www.iso.org/
+ *
+ * CONTACTS
+ * E-mail regarding any portion of the Linux UDF file system should be
+ * directed to the development team mailing list (run by majordomo):
+ * linux_udf@hpesjro.fc.hp.com
+ *
+ * COPYRIGHT
+ * This file is distributed under the terms of the GNU General Public
+ * License (GPL). Copies of the GPL can be obtained from:
+ * ftp://prep.ai.mit.edu/pub/gnu/GPL
+ * Each contributing author retains all rights to their own work.
+ *
+ * (C) 1998 Dave Boynton
+ * (C) 1998-2001 Ben Fennema
+ * (C) 2000 Stelias Computing Inc
+ *
+ * HISTORY
+ *
+ * 09/24/98 dgb changed to allow compiling outside of kernel, and
+ * added some debugging.
+ * 10/01/98 dgb updated to allow (some) possibility of compiling w/2.0.34
+ * 10/16/98 attempting some multi-session support
+ * 10/17/98 added freespace count for "df"
+ * 11/11/98 gr added novrs option
+ * 11/26/98 dgb added fileset,anchor mount options
+ * 12/06/98 blf really hosed things royally. vat/sparing support. sequenced vol descs
+ * rewrote option handling based on isofs
+ * 12/20/98 find the free space bitmap (if it exists)
+ */
+
+#include "udfdecl.h"
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/blkdev.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/locks.h>
+#include <linux/module.h>
+#include <linux/stat.h>
+#include <linux/cdrom.h>
+#include <linux/nls.h>
+#include <asm/byteorder.h>
+
+#include <linux/udf_fs.h>
+#include "udf_sb.h"
+#include "udf_i.h"
+
+#include <linux/init.h>
+#include <asm/uaccess.h>
+
+#define VDS_POS_PRIMARY_VOL_DESC 0
+#define VDS_POS_UNALLOC_SPACE_DESC 1
+#define VDS_POS_LOGICAL_VOL_DESC 2
+#define VDS_POS_PARTITION_DESC 3
+#define VDS_POS_IMP_USE_VOL_DESC 4
+#define VDS_POS_VOL_DESC_PTR 5
+#define VDS_POS_TERMINATING_DESC 6
+#define VDS_POS_LENGTH 7
+
+static char error_buf[1024];
+
+/* These are the "meat" - everything else is stuffing */
+static struct super_block *udf_read_super(struct super_block *, void *, int);
+static void udf_put_super(struct super_block *);
+static void udf_write_super(struct super_block *);
+static int udf_remount_fs(struct super_block *, int *, char *);
+static int udf_check_valid(struct super_block *, int, int);
+static int udf_vrs(struct super_block *sb, int silent);
+static int udf_load_partition(struct super_block *, lb_addr *);
+static int udf_load_logicalvol(struct super_block *, struct buffer_head *, lb_addr *);
+static void udf_load_logicalvolint(struct super_block *, extent_ad);
+static void udf_find_anchor(struct super_block *);
+static int udf_find_fileset(struct super_block *, lb_addr *, lb_addr *);
+static void udf_load_pvoldesc(struct super_block *, struct buffer_head *);
+static void udf_load_fileset(struct super_block *, struct buffer_head *, lb_addr *);
+static void udf_load_partdesc(struct super_block *, struct buffer_head *);
+static void udf_open_lvid(struct super_block *);
+static void udf_close_lvid(struct super_block *);
+static unsigned int udf_count_free(struct super_block *);
+static int udf_statfs(struct super_block *, struct statfs *);
+
+/* UDF filesystem type */
+static DECLARE_FSTYPE_DEV(udf_fstype, "udf", udf_read_super);
+
+/* Superblock operations */
+static struct super_operations udf_sb_ops = {
+ read_inode: udf_read_inode,
+ write_inode: udf_write_inode,
+ put_inode: udf_put_inode,
+ delete_inode: udf_delete_inode,
+ put_super: udf_put_super,
+ write_super: udf_write_super,
+ statfs: udf_statfs,
+ remount_fs: udf_remount_fs,
+};
+
+struct udf_options
+{
+ unsigned char novrs;
+ unsigned int blocksize;
+ unsigned int session;
+ unsigned int lastblock;
+ unsigned int anchor;
+ unsigned int volume;
+ unsigned short partition;
+ unsigned int fileset;
+ unsigned int rootdir;
+ unsigned int flags;
+ mode_t umask;
+ gid_t gid;
+ uid_t uid;
+ struct nls_table *nls_map;
+};
+
+static int __init init_udf_fs(void)
+{
+ printk(KERN_NOTICE "udf: registering filesystem\n");
+ return register_filesystem(&udf_fstype);
+}
+
+static void __exit exit_udf_fs(void)
+{
+ printk(KERN_NOTICE "udf: unregistering filesystem\n");
+ unregister_filesystem(&udf_fstype);
+}
+
+EXPORT_NO_SYMBOLS;
+
+module_init(init_udf_fs)
+module_exit(exit_udf_fs)
+
+/*
+ * udf_parse_options
+ *
+ * PURPOSE
+ * Parse mount options.
+ *
+ * DESCRIPTION
+ * The following mount options are supported:
+ *
+ * gid= Set the default group.
+ * umask= Set the default umask.
+ * uid= Set the default user.
+ * bs= Set the block size.
+ * unhide Show otherwise hidden files.
+ * undelete Show deleted files in lists.
+ * adinicb Embed data in the inode (default)
+ * noadinicb Don't embed data in the inode
+ * shortad Use short ad's
+ * longad Use long ad's (default)
+ * nostrict Unset strict conformance
+ * iocharset= Set the NLS character set
+ *
+ * The remaining are for debugging and disaster recovery:
+ *
+ * novrs Skip volume sequence recognition
+ *
+ * The following expect a offset from 0.
+ *
+ * session= Set the CDROM session (default= last session)
+ * anchor= Override standard anchor location. (default= 256)
+ * volume= Override the VolumeDesc location. (unused)
+ * partition= Override the PartitionDesc location. (unused)
+ * lastblock= Set the last block of the filesystem/
+ *
+ * The following expect a offset from the partition root.
+ *
+ * fileset= Override the fileset block location. (unused)
+ * rootdir= Override the root directory location. (unused)
+ * WARNING: overriding the rootdir to a non-directory may
+ * yield highly unpredictable results.
+ *
+ * PRE-CONDITIONS
+ * options Pointer to mount options string.
+ * uopts Pointer to mount options variable.
+ *
+ * POST-CONDITIONS
+ * <return> 0 Mount options parsed okay.
+ * <return> -1 Error parsing mount options.
+ *
+ * HISTORY
+ * July 1, 1997 - Andrew E. Mileski
+ * Written, tested, and released.
+ */
+
+static int
+udf_parse_options(char *options, struct udf_options *uopt)
+{
+ char *opt, *val;
+
+ uopt->novrs = 0;
+ uopt->blocksize = 2048;
+ uopt->partition = 0xFFFF;
+ uopt->session = 0xFFFFFFFF;
+ uopt->lastblock = 0;
+ uopt->anchor = 0;
+ uopt->volume = 0xFFFFFFFF;
+ uopt->rootdir = 0xFFFFFFFF;
+ uopt->fileset = 0xFFFFFFFF;
+ uopt->nls_map = NULL;
+
+ if (!options)
+ return 1;
+
+ for (opt = strtok(options, ","); opt; opt = strtok(NULL, ","))
+ {
+ /* Make "opt=val" into two strings */
+ val = strchr(opt, '=');
+ if (val)
+ *(val++) = 0;
+ if (!strcmp(opt, "novrs") && !val)
+ uopt->novrs = 1;
+ else if (!strcmp(opt, "bs") && val)
+ uopt->blocksize = simple_strtoul(val, NULL, 0);
+ else if (!strcmp(opt, "unhide") && !val)
+ uopt->flags |= (1 << UDF_FLAG_UNHIDE);
+ else if (!strcmp(opt, "undelete") && !val)
+ uopt->flags |= (1 << UDF_FLAG_UNDELETE);
+ else if (!strcmp(opt, "noadinicb") && !val)
+ uopt->flags &= ~(1 << UDF_FLAG_USE_AD_IN_ICB);
+ else if (!strcmp(opt, "adinicb") && !val)
+ uopt->flags |= (1 << UDF_FLAG_USE_AD_IN_ICB);
+ else if (!strcmp(opt, "shortad") && !val)
+ uopt->flags |= (1 << UDF_FLAG_USE_SHORT_AD);
+ else if (!strcmp(opt, "longad") && !val)
+ uopt->flags &= ~(1 << UDF_FLAG_USE_SHORT_AD);
+ else if (!strcmp(opt, "gid") && val)
+ uopt->gid = simple_strtoul(val, NULL, 0);
+ else if (!strcmp(opt, "umask") && val)
+ uopt->umask = simple_strtoul(val, NULL, 0);
+ else if (!strcmp(opt, "nostrict") && !val)
+ uopt->flags &= ~(1 << UDF_FLAG_STRICT);
+ else if (!strcmp(opt, "uid") && val)
+ uopt->uid = simple_strtoul(val, NULL, 0);
+ else if (!strcmp(opt, "session") && val)
+ uopt->session = simple_strtoul(val, NULL, 0);
+ else if (!strcmp(opt, "lastblock") && val)
+ uopt->lastblock = simple_strtoul(val, NULL, 0);
+ else if (!strcmp(opt, "anchor") && val)
+ uopt->anchor = simple_strtoul(val, NULL, 0);
+ else if (!strcmp(opt, "volume") && val)
+ uopt->volume = simple_strtoul(val, NULL, 0);
+ else if (!strcmp(opt, "partition") && val)
+ uopt->partition = simple_strtoul(val, NULL, 0);
+ else if (!strcmp(opt, "fileset") && val)
+ uopt->fileset = simple_strtoul(val, NULL, 0);
+ else if (!strcmp(opt, "rootdir") && val)
+ uopt->rootdir = simple_strtoul(val, NULL, 0);
+#ifdef CONFIG_NLS
+ else if (!strcmp(opt, "iocharset") && val)
+ {
+ uopt->nls_map = load_nls(val);
+ uopt->flags |= (1 << UDF_FLAG_NLS_MAP);
+ }
+#endif
+ else if (!strcmp(opt, "utf8") && !val)
+ uopt->flags |= (1 << UDF_FLAG_UTF8);
+ else if (val)
+ {
+ printk(KERN_ERR "udf: bad mount option \"%s=%s\"\n",
+ opt, val);
+ return 0;
+ }
+ else
+ {
+ printk(KERN_ERR "udf: bad mount option \"%s\"\n",
+ opt);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+void
+udf_write_super(struct super_block *sb)
+{
+ if (!(sb->s_flags & MS_RDONLY))
+ udf_open_lvid(sb);
+ sb->s_dirt = 0;
+}
+
+static int
+udf_remount_fs(struct super_block *sb, int *flags, char *options)
+{
+ struct udf_options uopt;
+
+ uopt.flags = UDF_SB(sb)->s_flags ;
+ uopt.uid = UDF_SB(sb)->s_uid ;
+ uopt.gid = UDF_SB(sb)->s_gid ;
+ uopt.umask = UDF_SB(sb)->s_umask ;
+
+ if ( !udf_parse_options(options, &uopt) )
+ return -EINVAL;
+
+ UDF_SB(sb)->s_flags = uopt.flags;
+ UDF_SB(sb)->s_uid = uopt.uid;
+ UDF_SB(sb)->s_gid = uopt.gid;
+ UDF_SB(sb)->s_umask = uopt.umask;
+
+#if UDFFS_RW != 1
+ *flags |= MS_RDONLY;
+#endif
+
+ if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
+ return 0;
+ if (*flags & MS_RDONLY)
+ udf_close_lvid(sb);
+ else
+ udf_open_lvid(sb);
+
+ return 0;
+}
+
+/*
+ * udf_set_blocksize
+ *
+ * PURPOSE
+ * Set the block size to be used in all transfers.
+ *
+ * DESCRIPTION
+ * To allow room for a DMA transfer, it is best to guess big when unsure.
+ * This routine picks 2048 bytes as the blocksize when guessing. This
+ * should be adequate until devices with larger block sizes become common.
+ *
+ * Note that the Linux kernel can currently only deal with blocksizes of
+ * 512, 1024, 2048, 4096, and 8192 bytes.
+ *
+ * PRE-CONDITIONS
+ * sb Pointer to _locked_ superblock.
+ *
+ * POST-CONDITIONS
+ * sb->s_blocksize Blocksize.
+ * sb->s_blocksize_bits log2 of blocksize.
+ * <return> 0 Blocksize is valid.
+ * <return> 1 Blocksize is invalid.
+ *
+ * HISTORY
+ * July 1, 1997 - Andrew E. Mileski
+ * Written, tested, and released.
+ */
+static int
+udf_set_blocksize(struct super_block *sb, int bsize)
+{
+ /* Use specified block size if specified */
+ if (bsize)
+ sb->s_blocksize = bsize;
+ if (get_hardsect_size(sb->s_dev) > sb->s_blocksize)
+ sb->s_blocksize = get_hardsect_size(sb->s_dev);
+
+ /* Block size must be an even multiple of 512 */
+ switch (sb->s_blocksize)
+ {
+ case 512: sb->s_blocksize_bits = 9; break;
+ case 1024: sb->s_blocksize_bits = 10; break;
+ case 2048: sb->s_blocksize_bits = 11; break;
+ case 4096: sb->s_blocksize_bits = 12; break;
+ case 8192: sb->s_blocksize_bits = 13; break;
+ default:
+ {
+ udf_debug("Bad block size (%ld)\n", sb->s_blocksize);
+ printk(KERN_ERR "udf: bad block size (%ld)\n", sb->s_blocksize);
+ return 0;
+ }
+ }
+
+ /* Set the block size */
+ set_blocksize(sb->s_dev, sb->s_blocksize);
+ return sb->s_blocksize;
+}
+
+static int
+udf_vrs(struct super_block *sb, int silent)
+{
+ struct volStructDesc *vsd = NULL;
+ int sector = 32768;
+ int sectorsize;
+ struct buffer_head *bh = NULL;
+ int iso9660=0;
+ int nsr02=0;
+ int nsr03=0;
+
+ /* Block size must be a multiple of 512 */
+ if (sb->s_blocksize & 511)
+ return 0;
+
+ if (sb->s_blocksize < sizeof(struct volStructDesc))
+ sectorsize = sizeof(struct volStructDesc);
+ else
+ sectorsize = sb->s_blocksize;
+
+ sector += (UDF_SB_SESSION(sb) << sb->s_blocksize_bits);
+
+ udf_debug("Starting at sector %u (%ld byte sectors)\n",
+ (sector >> sb->s_blocksize_bits), sb->s_blocksize);
+ /* Process the sequence (if applicable) */
+ for (;!nsr02 && !nsr03; sector += sectorsize)
+ {
+ /* Read a block */
+ bh = udf_tread(sb, sector >> sb->s_blocksize_bits);
+ if (!bh)
+ break;
+
+ /* Look for ISO descriptors */
+ vsd = (struct volStructDesc *)(bh->b_data +
+ (sector & (sb->s_blocksize - 1)));
+
+ if (vsd->stdIdent[0] == 0)
+ {
+ udf_release_data(bh);
+ break;
+ }
+ else if (!strncmp(vsd->stdIdent, VSD_STD_ID_CD001, VSD_STD_ID_LEN))
+ {
+ iso9660 = sector;
+ switch (vsd->structType)
+ {
+ case 0:
+ udf_debug("ISO9660 Boot Record found\n");
+ break;
+ case 1:
+ udf_debug("ISO9660 Primary Volume Descriptor found\n");
+ break;
+ case 2:
+ udf_debug("ISO9660 Supplementary Volume Descriptor found\n");
+ break;
+ case 3:
+ udf_debug("ISO9660 Volume Partition Descriptor found\n");
+ break;
+ case 255:
+ udf_debug("ISO9660 Volume Descriptor Set Terminator found\n");
+ break;
+ default:
+ udf_debug("ISO9660 VRS (%u) found\n", vsd->structType);
+ break;
+ }
+ }
+ else if (!strncmp(vsd->stdIdent, VSD_STD_ID_BEA01, VSD_STD_ID_LEN))
+ {
+ }
+ else if (!strncmp(vsd->stdIdent, VSD_STD_ID_TEA01, VSD_STD_ID_LEN))
+ {
+ udf_release_data(bh);
+ break;
+ }
+ else if (!strncmp(vsd->stdIdent, VSD_STD_ID_NSR02, VSD_STD_ID_LEN))
+ {
+ nsr02 = sector;
+ }
+ else if (!strncmp(vsd->stdIdent, VSD_STD_ID_NSR03, VSD_STD_ID_LEN))
+ {
+ nsr03 = sector;
+ }
+ udf_release_data(bh);
+ }
+
+ if (nsr03)
+ return nsr03;
+ else if (nsr02)
+ return nsr02;
+ else if (sector - (UDF_SB_SESSION(sb) << sb->s_blocksize_bits) == 32768)
+ return -1;
+ else
+ return 0;
+}
+
+/*
+ * udf_find_anchor
+ *
+ * PURPOSE
+ * Find an anchor volume descriptor.
+ *
+ * PRE-CONDITIONS
+ * sb Pointer to _locked_ superblock.
+ * lastblock Last block on media.
+ *
+ * POST-CONDITIONS
+ * <return> 1 if not found, 0 if ok
+ *
+ * HISTORY
+ * July 1, 1997 - Andrew E. Mileski
+ * Written, tested, and released.
+ */
+static void
+udf_find_anchor(struct super_block *sb)
+{
+ int lastblock = UDF_SB_LASTBLOCK(sb);
+ struct buffer_head *bh = NULL;
+ uint16_t ident;
+ uint32_t location;
+ int i;
+
+ if (lastblock)
+ {
+ int varlastblock = udf_variable_to_fixed(lastblock);
+ int last[] = { lastblock, lastblock - 2,
+ lastblock - 150, lastblock - 152,
+ varlastblock, varlastblock - 2,
+ varlastblock - 150, varlastblock - 152 };
+
+ lastblock = 0;
+
+ /* Search for an anchor volume descriptor pointer */
+
+ /* according to spec, anchor is in either:
+ * block 256
+ * lastblock-256
+ * lastblock
+ * however, if the disc isn't closed, it could be 512 */
+
+ for (i=0; (!lastblock && i<sizeof(last)/sizeof(int)); i++)
+ {
+ if (last[i] < 0 || !(bh = sb_bread(sb, last[i])))
+ {
+ ident = location = 0;
+ }
+ else
+ {
+ ident = le16_to_cpu(((tag *)bh->b_data)->tagIdent);
+ location = le32_to_cpu(((tag *)bh->b_data)->tagLocation);
+ udf_release_data(bh);
+ }
+
+ if (ident == TAG_IDENT_AVDP)
+ {
+ if (location == last[i] - UDF_SB_SESSION(sb))
+ {
+ lastblock = UDF_SB_ANCHOR(sb)[0] = last[i];
+ UDF_SB_ANCHOR(sb)[1] = last[i] - 256;
+ }
+ else if (location == udf_variable_to_fixed(last[i]) - UDF_SB_SESSION(sb))
+ {
+ UDF_SET_FLAG(sb, UDF_FLAG_VARCONV);
+ lastblock = UDF_SB_ANCHOR(sb)[0] = udf_variable_to_fixed(last[i]);
+ UDF_SB_ANCHOR(sb)[1] = lastblock - 256;
+ }
+ else
+ udf_debug("Anchor found at block %d, location mismatch %d.\n",
+ last[i], location);
+ }
+ else if (ident == TAG_IDENT_FE || ident == TAG_IDENT_EFE)
+ {
+ lastblock = last[i];
+ UDF_SB_ANCHOR(sb)[3] = 512 + UDF_SB_SESSION(sb);
+ }
+ else
+ {
+ if (last[i] < 256 || !(bh = sb_bread(sb, last[i] - 256)))
+ {
+ ident = location = 0;
+ }
+ else
+ {
+ ident = le16_to_cpu(((tag *)bh->b_data)->tagIdent);
+ location = le32_to_cpu(((tag *)bh->b_data)->tagLocation);
+ udf_release_data(bh);
+ }
+
+ if (ident == TAG_IDENT_AVDP &&
+ location == last[i] - 256 - UDF_SB_SESSION(sb))
+ {
+ lastblock = last[i];
+ UDF_SB_ANCHOR(sb)[1] = last[i] - 256;
+ }
+ else
+ {
+ if (last[i] < 312 + UDF_SB_SESSION(sb) || !(bh = sb_bread(sb, last[i] - 312 - UDF_SB_SESSION(sb))))
+ {
+ ident = location = 0;
+ }
+ else
+ {
+ ident = le16_to_cpu(((tag *)bh->b_data)->tagIdent);
+ location = le32_to_cpu(((tag *)bh->b_data)->tagLocation);
+ udf_release_data(bh);
+ }
+
+ if (ident == TAG_IDENT_AVDP &&
+ location == udf_variable_to_fixed(last[i]) - 256)
+ {
+ UDF_SET_FLAG(sb, UDF_FLAG_VARCONV);
+ lastblock = udf_variable_to_fixed(last[i]);
+ UDF_SB_ANCHOR(sb)[1] = lastblock - 256;
+ }
+ }
+ }
+ }
+ }
+
+ if (!lastblock)
+ {
+ /* We havn't found the lastblock. check 312 */
+ if ((bh = sb_bread(sb, 312 + UDF_SB_SESSION(sb))))
+ {
+ ident = le16_to_cpu(((tag *)bh->b_data)->tagIdent);
+ location = le32_to_cpu(((tag *)bh->b_data)->tagLocation);
+ udf_release_data(bh);
+
+ if (ident == TAG_IDENT_AVDP && location == 256)
+ UDF_SET_FLAG(sb, UDF_FLAG_VARCONV);
+ }
+ }
+
+ for (i=0; i<sizeof(UDF_SB_ANCHOR(sb))/sizeof(int); i++)
+ {
+ if (UDF_SB_ANCHOR(sb)[i])
+ {
+ if (!(bh = udf_read_tagged(sb,
+ UDF_SB_ANCHOR(sb)[i], UDF_SB_ANCHOR(sb)[i], &ident)))
+ {
+ UDF_SB_ANCHOR(sb)[i] = 0;
+ }
+ else
+ {
+ udf_release_data(bh);
+ if ((ident != TAG_IDENT_AVDP) && (i ||
+ (ident != TAG_IDENT_FE && ident != TAG_IDENT_EFE)))
+ {
+ UDF_SB_ANCHOR(sb)[i] = 0;
+ }
+ }
+ }
+ }
+
+ UDF_SB_LASTBLOCK(sb) = lastblock;
+}
+
+static int
+udf_find_fileset(struct super_block *sb, lb_addr *fileset, lb_addr *root)
+{
+ struct buffer_head *bh = NULL;
+ long lastblock;
+ uint16_t ident;
+
+ if (fileset->logicalBlockNum != 0xFFFFFFFF ||
+ fileset->partitionReferenceNum != 0xFFFF)
+ {
+ bh = udf_read_ptagged(sb, *fileset, 0, &ident);
+
+ if (!bh)
+ return 1;
+ else if (ident != TAG_IDENT_FSD)
+ {
+ udf_release_data(bh);
+ return 1;
+ }
+
+ }
+
+ if (!bh) /* Search backwards through the partitions */
+ {
+ lb_addr newfileset;
+
+ return 1;
+
+ for (newfileset.partitionReferenceNum=UDF_SB_NUMPARTS(sb)-1;
+ (newfileset.partitionReferenceNum != 0xFFFF &&
+ fileset->logicalBlockNum == 0xFFFFFFFF &&
+ fileset->partitionReferenceNum == 0xFFFF);
+ newfileset.partitionReferenceNum--)
+ {
+ lastblock = UDF_SB_PARTLEN(sb, newfileset.partitionReferenceNum);
+ newfileset.logicalBlockNum = 0;
+
+ do
+ {
+ bh = udf_read_ptagged(sb, newfileset, 0, &ident);
+ if (!bh)
+ {
+ newfileset.logicalBlockNum ++;
+ continue;
+ }
+
+ switch (ident)
+ {
+ case TAG_IDENT_SBD:
+ {
+ struct spaceBitmapDesc *sp;
+ sp = (struct spaceBitmapDesc *)bh->b_data;
+ newfileset.logicalBlockNum += 1 +
+ ((le32_to_cpu(sp->numOfBytes) + sizeof(struct spaceBitmapDesc) - 1)
+ >> sb->s_blocksize_bits);
+ udf_release_data(bh);
+ break;
+ }
+ case TAG_IDENT_FSD:
+ {
+ *fileset = newfileset;
+ break;
+ }
+ default:
+ {
+ newfileset.logicalBlockNum ++;
+ udf_release_data(bh);
+ bh = NULL;
+ break;
+ }
+ }
+ }
+ while (newfileset.logicalBlockNum < lastblock &&
+ fileset->logicalBlockNum == 0xFFFFFFFF &&
+ fileset->partitionReferenceNum == 0xFFFF);
+ }
+ }
+
+ if ((fileset->logicalBlockNum != 0xFFFFFFFF ||
+ fileset->partitionReferenceNum != 0xFFFF) && bh)
+ {
+ udf_debug("Fileset at block=%d, partition=%d\n",
+ fileset->logicalBlockNum, fileset->partitionReferenceNum);
+
+ UDF_SB_PARTITION(sb) = fileset->partitionReferenceNum;
+ udf_load_fileset(sb, bh, root);
+ udf_release_data(bh);
+ return 0;
+ }
+ return 1;
+}
+
+static void
+udf_load_pvoldesc(struct super_block *sb, struct buffer_head *bh)
+{
+ struct primaryVolDesc *pvoldesc;
+ time_t recording;
+ long recording_usec;
+ struct ustr instr;
+ struct ustr outstr;
+
+ pvoldesc = (struct primaryVolDesc *)bh->b_data;
+
+ if ( udf_stamp_to_time(&recording, &recording_usec,
+ lets_to_cpu(pvoldesc->recordingDateAndTime)) )
+ {
+ timestamp ts;
+ ts = lets_to_cpu(pvoldesc->recordingDateAndTime);
+ udf_debug("recording time %ld/%ld, %04u/%02u/%02u %02u:%02u (%x)\n",
+ recording, recording_usec,
+ ts.year, ts.month, ts.day, ts.hour, ts.minute, ts.typeAndTimezone);
+ UDF_SB_RECORDTIME(sb) = recording;
+ }
+
+ if ( !udf_build_ustr(&instr, pvoldesc->volIdent, 32) )
+ {
+ if (udf_CS0toUTF8(&outstr, &instr))
+ {
+ strncpy( UDF_SB_VOLIDENT(sb), outstr.u_name,
+ outstr.u_len > 31 ? 31 : outstr.u_len);
+ udf_debug("volIdent[] = '%s'\n", UDF_SB_VOLIDENT(sb));
+ }
+ }
+
+ if ( !udf_build_ustr(&instr, pvoldesc->volSetIdent, 128) )
+ {
+ if (udf_CS0toUTF8(&outstr, &instr))
+ udf_debug("volSetIdent[] = '%s'\n", outstr.u_name);
+ }
+}
+
+static void
+udf_load_fileset(struct super_block *sb, struct buffer_head *bh, lb_addr *root)
+{
+ struct fileSetDesc *fset;
+
+ fset = (struct fileSetDesc *)bh->b_data;
+
+ *root = lelb_to_cpu(fset->rootDirectoryICB.extLocation);
+
+ UDF_SB_SERIALNUM(sb) = le16_to_cpu(fset->descTag.tagSerialNum);
+
+ udf_debug("Rootdir at block=%d, partition=%d\n",
+ root->logicalBlockNum, root->partitionReferenceNum);
+}
+
+static void
+udf_load_partdesc(struct super_block *sb, struct buffer_head *bh)
+{
+ struct partitionDesc *p;
+ int i;
+
+ p = (struct partitionDesc *)bh->b_data;
+
+ for (i=0; i<UDF_SB_NUMPARTS(sb); i++)
+ {
+ udf_debug("Searching map: (%d == %d)\n",
+ UDF_SB_PARTMAPS(sb)[i].s_partition_num, le16_to_cpu(p->partitionNumber));
+ if (UDF_SB_PARTMAPS(sb)[i].s_partition_num == le16_to_cpu(p->partitionNumber))
+ {
+ UDF_SB_PARTLEN(sb,i) = le32_to_cpu(p->partitionLength); /* blocks */
+ UDF_SB_PARTROOT(sb,i) = le32_to_cpu(p->partitionStartingLocation) + UDF_SB_SESSION(sb);
+ if (le32_to_cpu(p->accessType) == PD_ACCESS_TYPE_READ_ONLY)
+ UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_READ_ONLY;
+ if (le32_to_cpu(p->accessType) == PD_ACCESS_TYPE_WRITE_ONCE)
+ UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_WRITE_ONCE;
+ if (le32_to_cpu(p->accessType) == PD_ACCESS_TYPE_REWRITABLE)
+ UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_REWRITABLE;
+ if (le32_to_cpu(p->accessType) == PD_ACCESS_TYPE_OVERWRITABLE)
+ UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_OVERWRITABLE;
+
+ if (!strcmp(p->partitionContents.ident, PD_PARTITION_CONTENTS_NSR02) ||
+ !strcmp(p->partitionContents.ident, PD_PARTITION_CONTENTS_NSR03))
+ {
+ struct partitionHeaderDesc *phd;
+
+ phd = (struct partitionHeaderDesc *)(p->partitionContentsUse);
+ if (phd->unallocSpaceTable.extLength)
+ {
+ lb_addr loc = { le32_to_cpu(phd->unallocSpaceTable.extPosition), i };
+
+ UDF_SB_PARTMAPS(sb)[i].s_uspace.s_table =
+ udf_iget(sb, loc);
+ UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_UNALLOC_TABLE;
+ udf_debug("unallocSpaceTable (part %d) @ %ld\n",
+ i, UDF_SB_PARTMAPS(sb)[i].s_uspace.s_table->i_ino);
+ }
+ if (phd->unallocSpaceBitmap.extLength)
+ {
+ UDF_SB_ALLOC_BITMAP(sb, i, s_uspace);
+ if (UDF_SB_PARTMAPS(sb)[i].s_uspace.s_bitmap != NULL)
+ {
+ UDF_SB_PARTMAPS(sb)[i].s_uspace.s_bitmap->s_extLength =
+ le32_to_cpu(phd->unallocSpaceBitmap.extLength);
+ UDF_SB_PARTMAPS(sb)[i].s_uspace.s_bitmap->s_extPosition =
+ le32_to_cpu(phd->unallocSpaceBitmap.extPosition);
+ UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_UNALLOC_BITMAP;
+ udf_debug("unallocSpaceBitmap (part %d) @ %d\n",
+ i, UDF_SB_PARTMAPS(sb)[i].s_uspace.s_bitmap->s_extPosition);
+ }
+ }
+ if (phd->partitionIntegrityTable.extLength)
+ udf_debug("partitionIntegrityTable (part %d)\n", i);
+ if (phd->freedSpaceTable.extLength)
+ {
+ lb_addr loc = { le32_to_cpu(phd->freedSpaceTable.extPosition), i };
+
+ UDF_SB_PARTMAPS(sb)[i].s_fspace.s_table =
+ udf_iget(sb, loc);
+ UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_FREED_TABLE;
+ udf_debug("freedSpaceTable (part %d) @ %ld\n",
+ i, UDF_SB_PARTMAPS(sb)[i].s_fspace.s_table->i_ino);
+ }
+ if (phd->freedSpaceBitmap.extLength)
+ {
+ UDF_SB_ALLOC_BITMAP(sb, i, s_fspace);
+ if (UDF_SB_PARTMAPS(sb)[i].s_fspace.s_bitmap != NULL)
+ {
+ UDF_SB_PARTMAPS(sb)[i].s_fspace.s_bitmap->s_extLength =
+ le32_to_cpu(phd->freedSpaceBitmap.extLength);
+ UDF_SB_PARTMAPS(sb)[i].s_fspace.s_bitmap->s_extPosition =
+ le32_to_cpu(phd->freedSpaceBitmap.extPosition);
+ UDF_SB_PARTFLAGS(sb,i) |= UDF_PART_FLAG_FREED_BITMAP;
+ udf_debug("freedSpaceBitmap (part %d) @ %d\n",
+ i, UDF_SB_PARTMAPS(sb)[i].s_fspace.s_bitmap->s_extPosition);
+ }
+ }
+ }
+ break;
+ }
+ }
+ if (i == UDF_SB_NUMPARTS(sb))
+ {
+ udf_debug("Partition (%d) not found in partition map\n", le16_to_cpu(p->partitionNumber));
+ }
+ else
+ {
+ udf_debug("Partition (%d:%d type %x) starts at physical %d, block length %d\n",
+ le16_to_cpu(p->partitionNumber), i, UDF_SB_PARTTYPE(sb,i),
+ UDF_SB_PARTROOT(sb,i), UDF_SB_PARTLEN(sb,i));
+ }
+}
+
+static int
+udf_load_logicalvol(struct super_block *sb, struct buffer_head * bh, lb_addr *fileset)
+{
+ struct logicalVolDesc *lvd;
+ int i, j, offset;
+ uint8_t type;
+
+ lvd = (struct logicalVolDesc *)bh->b_data;
+
+ UDF_SB_ALLOC_PARTMAPS(sb, le32_to_cpu(lvd->numPartitionMaps));
+
+ for (i=0,offset=0;
+ i<UDF_SB_NUMPARTS(sb) && offset<le32_to_cpu(lvd->mapTableLength);
+ i++,offset+=((struct genericPartitionMap *)&(lvd->partitionMaps[offset]))->partitionMapLength)
+ {
+ type = ((struct genericPartitionMap *)&(lvd->partitionMaps[offset]))->partitionMapType;
+ if (type == 1)
+ {
+ struct genericPartitionMap1 *gpm1 = (struct genericPartitionMap1 *)&(lvd->partitionMaps[offset]);
+ UDF_SB_PARTTYPE(sb,i) = UDF_TYPE1_MAP15;
+ UDF_SB_PARTVSN(sb,i) = le16_to_cpu(gpm1->volSeqNum);
+ UDF_SB_PARTNUM(sb,i) = le16_to_cpu(gpm1->partitionNum);
+ UDF_SB_PARTFUNC(sb,i) = NULL;
+ }
+ else if (type == 2)
+ {
+ struct udfPartitionMap2 *upm2 = (struct udfPartitionMap2 *)&(lvd->partitionMaps[offset]);
+ if (!strncmp(upm2->partIdent.ident, UDF_ID_VIRTUAL, strlen(UDF_ID_VIRTUAL)))
+ {
+ if (le16_to_cpu(((uint16_t *)upm2->partIdent.identSuffix)[0]) == 0x0150)
+ {
+ UDF_SB_PARTTYPE(sb,i) = UDF_VIRTUAL_MAP15;
+ UDF_SB_PARTFUNC(sb,i) = udf_get_pblock_virt15;
+ }
+ else if (le16_to_cpu(((uint16_t *)upm2->partIdent.identSuffix)[0]) == 0x0200)
+ {
+ UDF_SB_PARTTYPE(sb,i) = UDF_VIRTUAL_MAP20;
+ UDF_SB_PARTFUNC(sb,i) = udf_get_pblock_virt20;
+ }
+ }
+ else if (!strncmp(upm2->partIdent.ident, UDF_ID_SPARABLE, strlen(UDF_ID_SPARABLE)))
+ {
+ uint32_t loc;
+ uint16_t ident;
+ struct sparingTable *st;
+ struct sparablePartitionMap *spm = (struct sparablePartitionMap *)&(lvd->partitionMaps[offset]);
+
+ UDF_SB_PARTTYPE(sb,i) = UDF_SPARABLE_MAP15;
+ UDF_SB_TYPESPAR(sb,i).s_packet_len = le16_to_cpu(spm->packetLength);
+ for (j=0; j<spm->numSparingTables; j++)
+ {
+ loc = le32_to_cpu(spm->locSparingTable[j]);
+ UDF_SB_TYPESPAR(sb,i).s_spar_map[j] =
+ udf_read_tagged(sb, loc, loc, &ident);
+ if (UDF_SB_TYPESPAR(sb,i).s_spar_map[j] != NULL)
+ {
+ st = (struct sparingTable *)UDF_SB_TYPESPAR(sb,i).s_spar_map[j]->b_data;
+ if (ident != 0 ||
+ strncmp(st->sparingIdent.ident, UDF_ID_SPARING, strlen(UDF_ID_SPARING)))
+ {
+ udf_release_data(UDF_SB_TYPESPAR(sb,i).s_spar_map[j]);
+ UDF_SB_TYPESPAR(sb,i).s_spar_map[j] = NULL;
+ }
+ }
+ }
+ UDF_SB_PARTFUNC(sb,i) = udf_get_pblock_spar15;
+ }
+ else
+ {
+ udf_debug("Unknown ident: %s\n", upm2->partIdent.ident);
+ continue;
+ }
+ UDF_SB_PARTVSN(sb,i) = le16_to_cpu(upm2->volSeqNum);
+ UDF_SB_PARTNUM(sb,i) = le16_to_cpu(upm2->partitionNum);
+ }
+ udf_debug("Partition (%d:%d) type %d on volume %d\n",
+ i, UDF_SB_PARTNUM(sb,i), type, UDF_SB_PARTVSN(sb,i));
+ }
+
+ if (fileset)
+ {
+ long_ad *la = (long_ad *)&(lvd->logicalVolContentsUse[0]);
+
+ *fileset = lelb_to_cpu(la->extLocation);
+ udf_debug("FileSet found in LogicalVolDesc at block=%d, partition=%d\n",
+ fileset->logicalBlockNum,
+ fileset->partitionReferenceNum);
+ }
+ if (lvd->integritySeqExt.extLength)
+ udf_load_logicalvolint(sb, leea_to_cpu(lvd->integritySeqExt));
+ return 0;
+}
+
+/*
+ * udf_load_logicalvolint
+ *
+ */
+static void
+udf_load_logicalvolint(struct super_block *sb, extent_ad loc)
+{
+ struct buffer_head *bh = NULL;
+ uint16_t ident;
+
+ while (loc.extLength > 0 &&
+ (bh = udf_read_tagged(sb, loc.extLocation,
+ loc.extLocation, &ident)) &&
+ ident == TAG_IDENT_LVID)
+ {
+ UDF_SB_LVIDBH(sb) = bh;
+
+ if (UDF_SB_LVID(sb)->nextIntegrityExt.extLength)
+ udf_load_logicalvolint(sb, leea_to_cpu(UDF_SB_LVID(sb)->nextIntegrityExt));
+
+ if (UDF_SB_LVIDBH(sb) != bh)
+ udf_release_data(bh);
+ loc.extLength -= sb->s_blocksize;
+ loc.extLocation ++;
+ }
+ if (UDF_SB_LVIDBH(sb) != bh)
+ udf_release_data(bh);
+}
+
+/*
+ * udf_process_sequence
+ *
+ * PURPOSE
+ * Process a main/reserve volume descriptor sequence.
+ *
+ * PRE-CONDITIONS
+ * sb Pointer to _locked_ superblock.
+ * block First block of first extent of the sequence.
+ * lastblock Lastblock of first extent of the sequence.
+ *
+ * HISTORY
+ * July 1, 1997 - Andrew E. Mileski
+ * Written, tested, and released.
+ */
+static int
+udf_process_sequence(struct super_block *sb, long block, long lastblock, lb_addr *fileset)
+{
+ struct buffer_head *bh = NULL;
+ struct udf_vds_record vds[VDS_POS_LENGTH];
+ struct generic_desc *gd;
+ struct volDescPtr *vdp;
+ int done=0;
+ int i,j;
+ uint32_t vdsn;
+ uint16_t ident;
+ long next_s = 0, next_e = 0;
+
+ memset(vds, 0, sizeof(struct udf_vds_record) * VDS_POS_LENGTH);
+
+ /* Read the main descriptor sequence */
+ for (;(!done && block <= lastblock); block++)
+ {
+
+ bh = udf_read_tagged(sb, block, block, &ident);
+ if (!bh)
+ break;
+
+ /* Process each descriptor (ISO 13346 3/8.3-8.4) */
+ gd = (struct generic_desc *)bh->b_data;
+ vdsn = le32_to_cpu(gd->volDescSeqNum);
+ switch (ident)
+ {
+ case TAG_IDENT_PVD: /* ISO 13346 3/10.1 */
+ if (vdsn >= vds[VDS_POS_PRIMARY_VOL_DESC].volDescSeqNum)
+ {
+ vds[VDS_POS_PRIMARY_VOL_DESC].volDescSeqNum = vdsn;
+ vds[VDS_POS_PRIMARY_VOL_DESC].block = block;
+ }
+ break;
+ case TAG_IDENT_VDP: /* ISO 13346 3/10.3 */
+ if (vdsn >= vds[VDS_POS_VOL_DESC_PTR].volDescSeqNum)
+ {
+ vds[VDS_POS_VOL_DESC_PTR].volDescSeqNum = vdsn;
+ vds[VDS_POS_VOL_DESC_PTR].block = block;
+
+ vdp = (struct volDescPtr *)bh->b_data;
+ next_s = le32_to_cpu(vdp->nextVolDescSeqExt.extLocation);
+ next_e = le32_to_cpu(vdp->nextVolDescSeqExt.extLength);
+ next_e = next_e >> sb->s_blocksize_bits;
+ next_e += next_s;
+ }
+ break;
+ case TAG_IDENT_IUVD: /* ISO 13346 3/10.4 */
+ if (vdsn >= vds[VDS_POS_IMP_USE_VOL_DESC].volDescSeqNum)
+ {
+ vds[VDS_POS_IMP_USE_VOL_DESC].volDescSeqNum = vdsn;
+ vds[VDS_POS_IMP_USE_VOL_DESC].block = block;
+ }
+ break;
+ case TAG_IDENT_PD: /* ISO 13346 3/10.5 */
+ if (!vds[VDS_POS_PARTITION_DESC].block)
+ vds[VDS_POS_PARTITION_DESC].block = block;
+ break;
+ case TAG_IDENT_LVD: /* ISO 13346 3/10.6 */
+ if (vdsn >= vds[VDS_POS_LOGICAL_VOL_DESC].volDescSeqNum)
+ {
+ vds[VDS_POS_LOGICAL_VOL_DESC].volDescSeqNum = vdsn;
+ vds[VDS_POS_LOGICAL_VOL_DESC].block = block;
+ }
+ break;
+ case TAG_IDENT_USD: /* ISO 13346 3/10.8 */
+ if (vdsn >= vds[VDS_POS_UNALLOC_SPACE_DESC].volDescSeqNum)
+ {
+ vds[VDS_POS_UNALLOC_SPACE_DESC].volDescSeqNum = vdsn;
+ vds[VDS_POS_UNALLOC_SPACE_DESC].block = block;
+ }
+ break;
+ case TAG_IDENT_TD: /* ISO 13346 3/10.9 */
+ vds[VDS_POS_TERMINATING_DESC].block = block;
+ if (next_e)
+ {
+ block = next_s;
+ lastblock = next_e;
+ next_s = next_e = 0;
+ }
+ else
+ done = 1;
+ break;
+ }
+ udf_release_data(bh);
+ }
+ for (i=0; i<VDS_POS_LENGTH; i++)
+ {
+ if (vds[i].block)
+ {
+ bh = udf_read_tagged(sb, vds[i].block, vds[i].block, &ident);
+
+ if (i == VDS_POS_PRIMARY_VOL_DESC)
+ udf_load_pvoldesc(sb, bh);
+ else if (i == VDS_POS_LOGICAL_VOL_DESC)
+ udf_load_logicalvol(sb, bh, fileset);
+ else if (i == VDS_POS_PARTITION_DESC)
+ {
+ struct buffer_head *bh2 = NULL;
+ udf_load_partdesc(sb, bh);
+ for (j=vds[i].block+1; j<vds[VDS_POS_TERMINATING_DESC].block; j++)
+ {
+ bh2 = udf_read_tagged(sb, j, j, &ident);
+ gd = (struct generic_desc *)bh2->b_data;
+ if (ident == TAG_IDENT_PD)
+ udf_load_partdesc(sb, bh2);
+ udf_release_data(bh2);
+ }
+ }
+ udf_release_data(bh);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * udf_check_valid()
+ */
+static int
+udf_check_valid(struct super_block *sb, int novrs, int silent)
+{
+ long block;
+
+ if (novrs)
+ {
+ udf_debug("Validity check skipped because of novrs option\n");
+ return 0;
+ }
+ /* Check that it is NSR02 compliant */
+ /* Process any "CD-ROM Volume Descriptor Set" (ECMA 167 2/8.3.1) */
+ else if ((block = udf_vrs(sb, silent)) == -1)
+ {
+ udf_debug("Failed to read byte 32768. Assuming open disc. Skipping validity check\n");
+ if (!UDF_SB_LASTBLOCK(sb))
+ UDF_SB_LASTBLOCK(sb) = udf_get_last_block(sb);
+ return 0;
+ }
+ else
+ return !block;
+}
+
+static int
+udf_load_partition(struct super_block *sb, lb_addr *fileset)
+{
+ struct anchorVolDescPtr *anchor;
+ uint16_t ident;
+ struct buffer_head *bh;
+ long main_s, main_e, reserve_s, reserve_e;
+ int i, j;
+
+ if (!sb)
+ return 1;
+
+ for (i=0; i<sizeof(UDF_SB_ANCHOR(sb))/sizeof(int); i++)
+ {
+ if (UDF_SB_ANCHOR(sb)[i] && (bh = udf_read_tagged(sb,
+ UDF_SB_ANCHOR(sb)[i], UDF_SB_ANCHOR(sb)[i], &ident)))
+ {
+ anchor = (struct anchorVolDescPtr *)bh->b_data;
+
+ /* Locate the main sequence */
+ main_s = le32_to_cpu( anchor->mainVolDescSeqExt.extLocation );
+ main_e = le32_to_cpu( anchor->mainVolDescSeqExt.extLength );
+ main_e = main_e >> sb->s_blocksize_bits;
+ main_e += main_s;
+
+ /* Locate the reserve sequence */
+ reserve_s = le32_to_cpu(anchor->reserveVolDescSeqExt.extLocation);
+ reserve_e = le32_to_cpu(anchor->reserveVolDescSeqExt.extLength);
+ reserve_e = reserve_e >> sb->s_blocksize_bits;
+ reserve_e += reserve_s;
+
+ udf_release_data(bh);
+
+ /* Process the main & reserve sequences */
+ /* responsible for finding the PartitionDesc(s) */
+ if (!(udf_process_sequence(sb, main_s, main_e, fileset) &&
+ udf_process_sequence(sb, reserve_s, reserve_e, fileset)))
+ {
+ break;
+ }
+ }
+ }
+
+ if (i == sizeof(UDF_SB_ANCHOR(sb))/sizeof(int))
+ {
+ udf_debug("No Anchor block found\n");
+ return 1;
+ }
+ else
+ udf_debug("Using anchor in block %d\n", UDF_SB_ANCHOR(sb)[i]);
+
+ for (i=0; i<UDF_SB_NUMPARTS(sb); i++)
+ {
+ switch UDF_SB_PARTTYPE(sb, i)
+ {
+ case UDF_VIRTUAL_MAP15:
+ case UDF_VIRTUAL_MAP20:
+ {
+ lb_addr ino;
+
+ if (!UDF_SB_LASTBLOCK(sb))
+ {
+ UDF_SB_LASTBLOCK(sb) = udf_get_last_block(sb);
+ udf_find_anchor(sb);
+ }
+
+ if (!UDF_SB_LASTBLOCK(sb))
+ {
+ udf_debug("Unable to determine Lastblock (For Virtual Partition)\n");
+ return 1;
+ }
+
+ for (j=0; j<UDF_SB_NUMPARTS(sb); j++)
+ {
+ if (j != i &&
+ UDF_SB_PARTVSN(sb,i) == UDF_SB_PARTVSN(sb,j) &&
+ UDF_SB_PARTNUM(sb,i) == UDF_SB_PARTNUM(sb,j))
+ {
+ ino.partitionReferenceNum = j;
+ ino.logicalBlockNum = UDF_SB_LASTBLOCK(sb) -
+ UDF_SB_PARTROOT(sb,j);
+ break;
+ }
+ }
+
+ if (j == UDF_SB_NUMPARTS(sb))
+ return 1;
+
+ if (!(UDF_SB_VAT(sb) = udf_iget(sb, ino)))
+ return 1;
+
+ if (UDF_SB_PARTTYPE(sb,i) == UDF_VIRTUAL_MAP15)
+ {
+ UDF_SB_TYPEVIRT(sb,i).s_start_offset = udf_ext0_offset(UDF_SB_VAT(sb));
+ UDF_SB_TYPEVIRT(sb,i).s_num_entries = (UDF_SB_VAT(sb)->i_size - 36) >> 2;
+ }
+ else if (UDF_SB_PARTTYPE(sb,i) == UDF_VIRTUAL_MAP20)
+ {
+ struct buffer_head *bh = NULL;
+ uint32_t pos;
+
+ pos = udf_block_map(UDF_SB_VAT(sb), 0);
+ bh = sb_bread(sb, pos);
+ UDF_SB_TYPEVIRT(sb,i).s_start_offset =
+ le16_to_cpu(((struct virtualAllocationTable20 *)bh->b_data + udf_ext0_offset(UDF_SB_VAT(sb)))->lengthHeader) +
+ udf_ext0_offset(UDF_SB_VAT(sb));
+ UDF_SB_TYPEVIRT(sb,i).s_num_entries = (UDF_SB_VAT(sb)->i_size -
+ UDF_SB_TYPEVIRT(sb,i).s_start_offset) >> 2;
+ udf_release_data(bh);
+ }
+ UDF_SB_PARTROOT(sb,i) = udf_get_pblock(sb, 0, i, 0);
+ UDF_SB_PARTLEN(sb,i) = UDF_SB_PARTLEN(sb,ino.partitionReferenceNum);
+ }
+ }
+ }
+ return 0;
+}
+
+static void udf_open_lvid(struct super_block *sb)
+{
+ if (UDF_SB_LVIDBH(sb))
+ {
+ int i;
+ timestamp cpu_time;
+
+ UDF_SB_LVIDIU(sb)->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX;
+ UDF_SB_LVIDIU(sb)->impIdent.identSuffix[1] = UDF_OS_ID_LINUX;
+ if (udf_time_to_stamp(&cpu_time, CURRENT_TIME, CURRENT_UTIME))
+ UDF_SB_LVID(sb)->recordingDateAndTime = cpu_to_lets(cpu_time);
+ UDF_SB_LVID(sb)->integrityType = LVID_INTEGRITY_TYPE_OPEN;
+
+ UDF_SB_LVID(sb)->descTag.descCRC =
+ cpu_to_le16(udf_crc((char *)UDF_SB_LVID(sb) + sizeof(tag),
+ le16_to_cpu(UDF_SB_LVID(sb)->descTag.descCRCLength), 0));
+
+ UDF_SB_LVID(sb)->descTag.tagChecksum = 0;
+ for (i=0; i<16; i++)
+ if (i != 4)
+ UDF_SB_LVID(sb)->descTag.tagChecksum +=
+ ((uint8_t *)&(UDF_SB_LVID(sb)->descTag))[i];
+
+ mark_buffer_dirty(UDF_SB_LVIDBH(sb));
+ }
+}
+
+static void udf_close_lvid(struct super_block *sb)
+{
+ if (UDF_SB_LVIDBH(sb) &&
+ UDF_SB_LVID(sb)->integrityType == LVID_INTEGRITY_TYPE_OPEN)
+ {
+ int i;
+ timestamp cpu_time;
+
+ UDF_SB_LVIDIU(sb)->impIdent.identSuffix[0] = UDF_OS_CLASS_UNIX;
+ UDF_SB_LVIDIU(sb)->impIdent.identSuffix[1] = UDF_OS_ID_LINUX;
+ if (udf_time_to_stamp(&cpu_time, CURRENT_TIME, CURRENT_UTIME))
+ UDF_SB_LVID(sb)->recordingDateAndTime = cpu_to_lets(cpu_time);
+ if (UDF_MAX_WRITE_VERSION > le16_to_cpu(UDF_SB_LVIDIU(sb)->maxUDFWriteRev))
+ UDF_SB_LVIDIU(sb)->maxUDFWriteRev = cpu_to_le16(UDF_MAX_WRITE_VERSION);
+ if (UDF_SB_UDFREV(sb) > le16_to_cpu(UDF_SB_LVIDIU(sb)->minUDFReadRev))
+ UDF_SB_LVIDIU(sb)->minUDFReadRev = cpu_to_le16(UDF_SB_UDFREV(sb));
+ if (UDF_SB_UDFREV(sb) > le16_to_cpu(UDF_SB_LVIDIU(sb)->minUDFWriteRev))
+ UDF_SB_LVIDIU(sb)->minUDFWriteRev = cpu_to_le16(UDF_SB_UDFREV(sb));
+ UDF_SB_LVID(sb)->integrityType = LVID_INTEGRITY_TYPE_CLOSE;
+
+ UDF_SB_LVID(sb)->descTag.descCRC =
+ cpu_to_le16(udf_crc((char *)UDF_SB_LVID(sb) + sizeof(tag),
+ le16_to_cpu(UDF_SB_LVID(sb)->descTag.descCRCLength), 0));
+
+ UDF_SB_LVID(sb)->descTag.tagChecksum = 0;
+ for (i=0; i<16; i++)
+ if (i != 4)
+ UDF_SB_LVID(sb)->descTag.tagChecksum +=
+ ((uint8_t *)&(UDF_SB_LVID(sb)->descTag))[i];
+
+ mark_buffer_dirty(UDF_SB_LVIDBH(sb));
+ }
+}
+
+/*
+ * udf_read_super
+ *
+ * PURPOSE
+ * Complete the specified super block.
+ *
+ * PRE-CONDITIONS
+ * sb Pointer to superblock to complete - never NULL.
+ * sb->s_dev Device to read suberblock from.
+ * options Pointer to mount options.
+ * silent Silent flag.
+ *
+ * HISTORY
+ * July 1, 1997 - Andrew E. Mileski
+ * Written, tested, and released.
+ */
+static struct super_block *
+udf_read_super(struct super_block *sb, void *options, int silent)
+{
+ int i;
+ struct inode *inode=NULL;
+ struct udf_options uopt;
+ lb_addr rootdir, fileset;
+
+ uopt.flags = (1 << UDF_FLAG_USE_AD_IN_ICB) | (1 << UDF_FLAG_STRICT);
+ uopt.uid = -1;
+ uopt.gid = -1;
+ uopt.umask = 0;
+
+ memset(UDF_SB(sb), 0x00, sizeof(struct udf_sb_info));
+
+#if UDFFS_RW != 1
+ sb->s_flags |= MS_RDONLY;
+#endif
+
+ if (!udf_parse_options((char *)options, &uopt))
+ goto error_out;
+
+ if (uopt.flags & (1 << UDF_FLAG_UTF8) &&
+ uopt.flags & (1 << UDF_FLAG_NLS_MAP))
+ {
+ udf_error(sb, "udf_read_super",
+ "utf8 cannot be combined with iocharset\n");
+ goto error_out;
+ }
+#ifdef CONFIG_NLS
+ if ((uopt.flags & (1 << UDF_FLAG_NLS_MAP)) && !uopt.nls_map)
+ {
+ uopt.nls_map = load_nls_default();
+ if (!uopt.nls_map)
+ uopt.flags &= ~(1 << UDF_FLAG_NLS_MAP);
+ else
+ udf_debug("Using default NLS map\n");
+ }
+#endif
+ if (!(uopt.flags & (1 << UDF_FLAG_NLS_MAP)))
+ uopt.flags |= (1 << UDF_FLAG_UTF8);
+
+ fileset.logicalBlockNum = 0xFFFFFFFF;
+ fileset.partitionReferenceNum = 0xFFFF;
+
+ UDF_SB(sb)->s_flags = uopt.flags;
+ UDF_SB(sb)->s_uid = uopt.uid;
+ UDF_SB(sb)->s_gid = uopt.gid;
+ UDF_SB(sb)->s_umask = uopt.umask;
+ UDF_SB(sb)->s_nls_map = uopt.nls_map;
+
+ /* Set the block size for all transfers */
+ if (!udf_set_blocksize(sb, uopt.blocksize))
+ goto error_out;
+
+ if ( uopt.session == 0xFFFFFFFF )
+ UDF_SB_SESSION(sb) = udf_get_last_session(sb);
+ else
+ UDF_SB_SESSION(sb) = uopt.session;
+
+ udf_debug("Multi-session=%d\n", UDF_SB_SESSION(sb));
+
+ UDF_SB_LASTBLOCK(sb) = uopt.lastblock;
+ UDF_SB_ANCHOR(sb)[0] = UDF_SB_ANCHOR(sb)[1] = 0;
+ UDF_SB_ANCHOR(sb)[2] = uopt.anchor;
+ UDF_SB_ANCHOR(sb)[3] = UDF_SB_SESSION(sb) + 256;
+
+ if (udf_check_valid(sb, uopt.novrs, silent)) /* read volume recognition sequences */
+ {
+ printk("UDF-fs: No VRS found\n");
+ goto error_out;
+ }
+
+ udf_find_anchor(sb);
+
+ /* Fill in the rest of the superblock */
+ sb->s_op = &udf_sb_ops;
+ sb->dq_op = NULL;
+ sb->s_dirt = 0;
+ sb->s_magic = UDF_SUPER_MAGIC;
+
+ if (udf_load_partition(sb, &fileset))
+ {
+ printk("UDF-fs: No partition found (1)\n");
+ goto error_out;
+ }
+
+ udf_debug("Lastblock=%d\n", UDF_SB_LASTBLOCK(sb));
+
+ if ( UDF_SB_LVIDBH(sb) )
+ {
+ uint16_t minUDFReadRev = le16_to_cpu(UDF_SB_LVIDIU(sb)->minUDFReadRev);
+ uint16_t minUDFWriteRev = le16_to_cpu(UDF_SB_LVIDIU(sb)->minUDFWriteRev);
+ /* uint16_t maxUDFWriteRev = le16_to_cpu(UDF_SB_LVIDIU(sb)->maxUDFWriteRev); */
+
+ if (minUDFReadRev > UDF_MAX_READ_VERSION)
+ {
+ printk("UDF-fs: minUDFReadRev=%x (max is %x)\n",
+ UDF_SB_LVIDIU(sb)->minUDFReadRev, UDF_MAX_READ_VERSION);
+ goto error_out;
+ }
+ else if (minUDFWriteRev > UDF_MAX_WRITE_VERSION)
+ {
+ sb->s_flags |= MS_RDONLY;
+ }
+
+ UDF_SB_UDFREV(sb) = minUDFWriteRev;
+
+ if (minUDFReadRev >= UDF_VERS_USE_EXTENDED_FE)
+ UDF_SET_FLAG(sb, UDF_FLAG_USE_EXTENDED_FE);
+ if (minUDFReadRev >= UDF_VERS_USE_STREAMS)
+ UDF_SET_FLAG(sb, UDF_FLAG_USE_STREAMS);
+ }
+
+ if ( !UDF_SB_NUMPARTS(sb) )
+ {
+ printk("UDF-fs: No partition found (2)\n");
+ goto error_out;
+ }
+
+ if ( udf_find_fileset(sb, &fileset, &rootdir) )
+ {
+ printk("UDF-fs: No fileset found\n");
+ goto error_out;
+ }
+
+ if (!silent)
+ {
+ timestamp ts;
+ udf_time_to_stamp(&ts, UDF_SB_RECORDTIME(sb), 0);
+ udf_info("UDF %s-%s (%s) Mounting volume '%s', timestamp %04u/%02u/%02u %02u:%02u (%x)\n",
+ UDFFS_VERSION, UDFFS_RW ? "rw" : "ro", UDFFS_DATE,
+ UDF_SB_VOLIDENT(sb), ts.year, ts.month, ts.day, ts.hour, ts.minute,
+ ts.typeAndTimezone);
+ }
+ if (!(sb->s_flags & MS_RDONLY))
+ udf_open_lvid(sb);
+
+ /* Assign the root inode */
+ /* assign inodes by physical block number */
+ /* perhaps it's not extensible enough, but for now ... */
+ inode = udf_iget(sb, rootdir);
+ if (!inode)
+ {
+ printk("UDF-fs: Error in udf_iget, block=%d, partition=%d\n",
+ rootdir.logicalBlockNum, rootdir.partitionReferenceNum);
+ goto error_out;
+ }
+
+ /* Allocate a dentry for the root inode */
+ sb->s_root = d_alloc_root(inode);
+ if (!sb->s_root)
+ {
+ printk("UDF-fs: Couldn't allocate root dentry\n");
+ iput(inode);
+ goto error_out;
+ }
+ sb->s_maxbytes = MAX_LFS_FILESIZE;
+ return sb;
+
+error_out:
+ if (UDF_SB_VAT(sb))
+ iput(UDF_SB_VAT(sb));
+ if (UDF_SB_NUMPARTS(sb))
+ {
+ if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_TABLE)
+ iput(UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace.s_table);
+ if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_TABLE)
+ iput(UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_fspace.s_table);
+ if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_BITMAP)
+ {
+ for (i=0; i<UDF_SB_BITMAP_NR_GROUPS(sb,UDF_SB_PARTITION(sb),s_uspace); i++)
+ {
+ if (UDF_SB_BITMAP(sb,UDF_SB_PARTITION(sb),s_uspace,i))
+ udf_release_data(UDF_SB_BITMAP(sb,UDF_SB_PARTITION(sb),s_uspace,i));
+ }
+ kfree(UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace.s_bitmap);
+ }
+ if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_BITMAP)
+ {
+ for (i=0; i<UDF_SB_BITMAP_NR_GROUPS(sb,UDF_SB_PARTITION(sb),s_fspace); i++)
+ {
+ if (UDF_SB_BITMAP(sb,UDF_SB_PARTITION(sb),s_fspace,i))
+ udf_release_data(UDF_SB_BITMAP(sb,UDF_SB_PARTITION(sb),s_fspace,i));
+ }
+ kfree(UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_fspace.s_bitmap);
+ }
+ if (UDF_SB_PARTTYPE(sb, UDF_SB_PARTITION(sb)) == UDF_SPARABLE_MAP15)
+ {
+ for (i=0; i<4; i++)
+ udf_release_data(UDF_SB_TYPESPAR(sb, UDF_SB_PARTITION(sb)).s_spar_map[i]);
+ }
+ }
+#ifdef CONFIG_NLS
+ if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP))
+ unload_nls(UDF_SB(sb)->s_nls_map);
+#endif
+ if (!(sb->s_flags & MS_RDONLY))
+ udf_close_lvid(sb);
+ udf_release_data(UDF_SB_LVIDBH(sb));
+ UDF_SB_FREE(sb);
+ return NULL;
+}
+
+void udf_error(struct super_block *sb, const char *function,
+ const char *fmt, ...)
+{
+ va_list args;
+
+ if (!(sb->s_flags & MS_RDONLY))
+ {
+ /* mark sb error */
+ sb->s_dirt = 1;
+ }
+ va_start(args, fmt);
+ vsprintf(error_buf, fmt, args);
+ va_end(args);
+ printk (KERN_CRIT "UDF-fs error (device %s): %s: %s\n",
+ bdevname(sb->s_dev), function, error_buf);
+}
+
+void udf_warning(struct super_block *sb, const char *function,
+ const char *fmt, ...)
+{
+ va_list args;
+
+ va_start (args, fmt);
+ vsprintf(error_buf, fmt, args);
+ va_end(args);
+ printk(KERN_WARNING "UDF-fs warning (device %s): %s: %s\n",
+ bdevname(sb->s_dev), function, error_buf);
+}
+
+/*
+ * udf_put_super
+ *
+ * PURPOSE
+ * Prepare for destruction of the superblock.
+ *
+ * DESCRIPTION
+ * Called before the filesystem is unmounted.
+ *
+ * HISTORY
+ * July 1, 1997 - Andrew E. Mileski
+ * Written, tested, and released.
+ */
+static void
+udf_put_super(struct super_block *sb)
+{
+ int i;
+
+ if (UDF_SB_VAT(sb))
+ iput(UDF_SB_VAT(sb));
+ if (UDF_SB_NUMPARTS(sb))
+ {
+ if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_TABLE)
+ iput(UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace.s_table);
+ if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_TABLE)
+ iput(UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_fspace.s_table);
+ if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_BITMAP)
+ {
+ for (i=0; i<UDF_SB_BITMAP_NR_GROUPS(sb,UDF_SB_PARTITION(sb),s_uspace); i++)
+ {
+ if (UDF_SB_BITMAP(sb,UDF_SB_PARTITION(sb),s_uspace,i))
+ udf_release_data(UDF_SB_BITMAP(sb,UDF_SB_PARTITION(sb),s_uspace,i));
+ }
+ kfree(UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace.s_bitmap);
+ }
+ if (UDF_SB_PARTFLAGS(sb, UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_BITMAP)
+ {
+ for (i=0; i<UDF_SB_BITMAP_NR_GROUPS(sb,UDF_SB_PARTITION(sb),s_fspace); i++)
+ {
+ if (UDF_SB_BITMAP(sb,UDF_SB_PARTITION(sb),s_fspace,i))
+ udf_release_data(UDF_SB_BITMAP(sb,UDF_SB_PARTITION(sb),s_fspace,i));
+ }
+ kfree(UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_fspace.s_bitmap);
+ }
+ if (UDF_SB_PARTTYPE(sb, UDF_SB_PARTITION(sb)) == UDF_SPARABLE_MAP15)
+ {
+ for (i=0; i<4; i++)
+ udf_release_data(UDF_SB_TYPESPAR(sb, UDF_SB_PARTITION(sb)).s_spar_map[i]);
+ }
+ }
+#ifdef CONFIG_NLS
+ if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP))
+ unload_nls(UDF_SB(sb)->s_nls_map);
+#endif
+ if (!(sb->s_flags & MS_RDONLY))
+ udf_close_lvid(sb);
+ udf_release_data(UDF_SB_LVIDBH(sb));
+ UDF_SB_FREE(sb);
+}
+
+/*
+ * udf_stat_fs
+ *
+ * PURPOSE
+ * Return info about the filesystem.
+ *
+ * DESCRIPTION
+ * Called by sys_statfs()
+ *
+ * HISTORY
+ * July 1, 1997 - Andrew E. Mileski
+ * Written, tested, and released.
+ */
+static int
+udf_statfs(struct super_block *sb, struct statfs *buf)
+{
+ buf->f_type = UDF_SUPER_MAGIC;
+ buf->f_bsize = sb->s_blocksize;
+ buf->f_blocks = UDF_SB_PARTLEN(sb, UDF_SB_PARTITION(sb));
+ buf->f_bfree = udf_count_free(sb);
+ buf->f_bavail = buf->f_bfree;
+ buf->f_files = (UDF_SB_LVIDBH(sb) ?
+ (le32_to_cpu(UDF_SB_LVIDIU(sb)->numFiles) +
+ le32_to_cpu(UDF_SB_LVIDIU(sb)->numDirs)) : 0) + buf->f_bfree;
+ buf->f_ffree = buf->f_bfree;
+ /* __kernel_fsid_t f_fsid */
+ buf->f_namelen = UDF_NAME_LEN;
+
+ return 0;
+}
+
+static unsigned char udf_bitmap_lookup[16] = {
+ 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4
+};
+
+static unsigned int
+udf_count_free_bitmap(struct super_block *sb, struct udf_bitmap *bitmap)
+{
+ struct buffer_head *bh = NULL;
+ unsigned int accum = 0;
+ int index;
+ int block = 0, newblock;
+ lb_addr loc;
+ uint32_t bytes;
+ uint8_t value;
+ uint8_t *ptr;
+ uint16_t ident;
+ struct spaceBitmapDesc *bm;
+
+ loc.logicalBlockNum = bitmap->s_extPosition;
+ loc.partitionReferenceNum = UDF_SB_PARTITION(sb);
+ bh = udf_read_ptagged(sb, loc, 0, &ident);
+
+ if (!bh)
+ {
+ printk(KERN_ERR "udf: udf_count_free failed\n");
+ return 0;
+ }
+ else if (ident != TAG_IDENT_SBD)
+ {
+ udf_release_data(bh);
+ printk(KERN_ERR "udf: udf_count_free failed\n");
+ return 0;
+ }
+
+ bm = (struct spaceBitmapDesc *)bh->b_data;
+ bytes = bm->numOfBytes;
+ index = sizeof(struct spaceBitmapDesc); /* offset in first block only */
+ ptr = (uint8_t *)bh->b_data;
+
+ while ( bytes > 0 )
+ {
+ while ((bytes > 0) && (index < sb->s_blocksize))
+ {
+ value = ptr[index];
+ accum += udf_bitmap_lookup[ value & 0x0f ];
+ accum += udf_bitmap_lookup[ value >> 4 ];
+ index++;
+ bytes--;
+ }
+ if ( bytes )
+ {
+ udf_release_data(bh);
+ newblock = udf_get_lb_pblock(sb, loc, ++block);
+ bh = udf_tread(sb, newblock);
+ if (!bh)
+ {
+ udf_debug("read failed\n");
+ return accum;
+ }
+ index = 0;
+ ptr = (uint8_t *)bh->b_data;
+ }
+ }
+ udf_release_data(bh);
+ return accum;
+}
+
+static unsigned int
+udf_count_free_table(struct super_block *sb, struct inode * table)
+{
+ unsigned int accum = 0;
+ uint32_t extoffset, elen;
+ lb_addr bloc, eloc;
+ int8_t etype;
+ struct buffer_head *bh = NULL;
+
+ bloc = UDF_I_LOCATION(table);
+ extoffset = sizeof(struct unallocSpaceEntry);
+
+ while ((etype = udf_next_aext(table, &bloc, &extoffset, &eloc, &elen, &bh, 1)) != -1)
+ {
+ accum += (elen >> table->i_sb->s_blocksize_bits);
+ }
+ udf_release_data(bh);
+ return accum;
+}
+
+static unsigned int
+udf_count_free(struct super_block *sb)
+{
+ unsigned int accum = 0;
+
+ if (UDF_SB_LVIDBH(sb))
+ {
+ if (le32_to_cpu(UDF_SB_LVID(sb)->numOfPartitions) > UDF_SB_PARTITION(sb))
+ {
+ accum = le32_to_cpu(UDF_SB_LVID(sb)->freeSpaceTable[UDF_SB_PARTITION(sb)]);
+
+ if (accum == 0xFFFFFFFF)
+ accum = 0;
+ }
+ }
+
+ if (accum)
+ return accum;
+
+ if (UDF_SB_PARTFLAGS(sb,UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_BITMAP)
+ {
+ accum += udf_count_free_bitmap(sb,
+ UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace.s_bitmap);
+ }
+ if (UDF_SB_PARTFLAGS(sb,UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_BITMAP)
+ {
+ accum += udf_count_free_bitmap(sb,
+ UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_fspace.s_bitmap);
+ }
+ if (accum)
+ return accum;
+
+ if (UDF_SB_PARTFLAGS(sb,UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_UNALLOC_TABLE)
+ {
+ accum += udf_count_free_table(sb,
+ UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_uspace.s_table);
+ }
+ if (UDF_SB_PARTFLAGS(sb,UDF_SB_PARTITION(sb)) & UDF_PART_FLAG_FREED_TABLE)
+ {
+ accum += udf_count_free_table(sb,
+ UDF_SB_PARTMAPS(sb)[UDF_SB_PARTITION(sb)].s_fspace.s_table);
+ }
+
+ return accum;
+}
diff --git a/fs/udf/symlink.c b/fs/udf/symlink.c
new file mode 100644
index 00000000000000..4c72e690c94796
--- /dev/null
+++ b/fs/udf/symlink.c
@@ -0,0 +1,129 @@
+/*
+ * symlink.c
+ *
+ * PURPOSE
+ * Symlink handling routines for the OSTA-UDF(tm) filesystem.
+ *
+ * CONTACTS
+ * E-mail regarding any portion of the Linux UDF file system should be
+ * directed to the development team mailing list (run by majordomo):
+ * linux_udf@hpesjro.fc.hp.com
+ *
+ * COPYRIGHT
+ * This file is distributed under the terms of the GNU General Public
+ * License (GPL). Copies of the GPL can be obtained from:
+ * ftp://prep.ai.mit.edu/pub/gnu/GPL
+ * Each contributing author retains all rights to their own work.
+ *
+ * (C) 1998-2001 Ben Fennema
+ * (C) 1999 Stelias Computing Inc
+ *
+ * HISTORY
+ *
+ * 04/16/99 blf Created.
+ *
+ */
+
+#include "udfdecl.h"
+#include <asm/uaccess.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/udf_fs.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/stat.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/smp_lock.h>
+#include "udf_i.h"
+
+static void udf_pc_to_char(char *from, int fromlen, char *to)
+{
+ struct pathComponent *pc;
+ int elen = 0;
+ char *p = to;
+
+ while (elen < fromlen)
+ {
+ pc = (struct pathComponent *)(from + elen);
+ switch (pc->componentType)
+ {
+ case 1:
+ if (pc->lengthComponentIdent == 0)
+ {
+ p = to;
+ *p++ = '/';
+ }
+ break;
+ case 3:
+ memcpy(p, "../", 3);
+ p += 3;
+ break;
+ case 4:
+ memcpy(p, "./", 2);
+ p += 2;
+ /* that would be . - just ignore */
+ break;
+ case 5:
+ memcpy(p, pc->componentIdent, pc->lengthComponentIdent);
+ p += pc->lengthComponentIdent;
+ *p++ = '/';
+ }
+ elen += sizeof(struct pathComponent) + pc->lengthComponentIdent;
+ }
+ if (p > to+1)
+ p[-1] = '\0';
+ else
+ p[0] = '\0';
+}
+
+static int udf_symlink_filler(struct file *file, struct page *page)
+{
+ struct inode *inode = page->mapping->host;
+ struct buffer_head *bh = NULL;
+ char *symlink;
+ int err = -EIO;
+ char *p = kmap(page);
+
+ lock_kernel();
+ if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB)
+ {
+ bh = udf_tread(inode->i_sb, inode->i_ino);
+
+ if (!bh)
+ goto out;
+
+ symlink = bh->b_data + udf_file_entry_alloc_offset(inode);
+ }
+ else
+ {
+ bh = sb_bread(inode->i_sb, udf_block_map(inode, 0));
+
+ if (!bh)
+ goto out;
+
+ symlink = bh->b_data;
+ }
+
+ udf_pc_to_char(symlink, inode->i_size, p);
+ udf_release_data(bh);
+
+ unlock_kernel();
+ SetPageUptodate(page);
+ kunmap(page);
+ UnlockPage(page);
+ return 0;
+out:
+ unlock_kernel();
+ SetPageError(page);
+ kunmap(page);
+ UnlockPage(page);
+ return err;
+}
+
+/*
+ * symlinks can't do much...
+ */
+struct address_space_operations udf_symlink_aops = {
+ readpage: udf_symlink_filler,
+};
diff --git a/fs/udf/truncate.c b/fs/udf/truncate.c
new file mode 100644
index 00000000000000..0ae7e965b1c997
--- /dev/null
+++ b/fs/udf/truncate.c
@@ -0,0 +1,222 @@
+/*
+ * truncate.c
+ *
+ * PURPOSE
+ * Truncate handling routines for the OSTA-UDF(tm) filesystem.
+ *
+ * CONTACTS
+ * E-mail regarding any portion of the Linux UDF file system should be
+ * directed to the development team mailing list (run by majordomo):
+ * linux_udf@hpesjro.fc.hp.com
+ *
+ * COPYRIGHT
+ * This file is distributed under the terms of the GNU General Public
+ * License (GPL). Copies of the GPL can be obtained from:
+ * ftp://prep.ai.mit.edu/pub/gnu/GPL
+ * Each contributing author retains all rights to their own work.
+ *
+ * (C) 1999-2001 Ben Fennema
+ * (C) 1999 Stelias Computing Inc
+ *
+ * HISTORY
+ *
+ * 02/24/99 blf Created.
+ *
+ */
+
+#include "udfdecl.h"
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/udf_fs.h>
+
+#include "udf_i.h"
+#include "udf_sb.h"
+
+static void extent_trunc(struct inode * inode, lb_addr bloc, int extoffset,
+ lb_addr eloc, int8_t etype, uint32_t elen, struct buffer_head *bh, uint32_t nelen)
+{
+ lb_addr neloc = { 0, 0 };
+ int last_block = (elen + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits;
+ int first_block = (nelen + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits;
+
+ if (nelen)
+ {
+ if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30))
+ {
+ udf_free_blocks(inode->i_sb, inode, eloc, 0, last_block);
+ etype = (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30);
+ }
+ else
+ neloc = eloc;
+ nelen = (etype << 30) | nelen;
+ }
+
+ if (elen != nelen)
+ {
+ udf_write_aext(inode, bloc, &extoffset, neloc, nelen, bh, 0);
+ if (last_block - first_block > 0)
+ {
+ if (etype == (EXT_RECORDED_ALLOCATED >> 30))
+ mark_inode_dirty(inode);
+
+ if (etype != (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30))
+ udf_free_blocks(inode->i_sb, inode, eloc, first_block, last_block - first_block);
+ }
+ }
+}
+
+void udf_truncate_extents(struct inode * inode)
+{
+ lb_addr bloc, eloc, neloc = { 0, 0 };
+ uint32_t extoffset, elen, offset, nelen = 0, lelen = 0, lenalloc;
+ int8_t etype;
+ int first_block = inode->i_size >> inode->i_sb->s_blocksize_bits;
+ struct buffer_head *bh = NULL;
+ int adsize;
+
+ if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT)
+ adsize = sizeof(short_ad);
+ else if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_LONG)
+ adsize = sizeof(long_ad);
+ else
+ adsize = 0;
+
+ etype = inode_bmap(inode, first_block, &bloc, &extoffset, &eloc, &elen, &offset, &bh);
+ offset += (inode->i_size & (inode->i_sb->s_blocksize - 1));
+ if (etype != -1)
+ {
+ extoffset -= adsize;
+ extent_trunc(inode, bloc, extoffset, eloc, etype, elen, bh, offset);
+ extoffset += adsize;
+
+ if (offset)
+ lenalloc = extoffset;
+ else
+ lenalloc = extoffset - adsize;
+
+ if (!memcmp(&UDF_I_LOCATION(inode), &bloc, sizeof(lb_addr)))
+ lenalloc -= udf_file_entry_alloc_offset(inode);
+ else
+ lenalloc -= sizeof(struct allocExtDesc);
+
+ while ((etype = udf_current_aext(inode, &bloc, &extoffset, &eloc, &elen, &bh, 0)) != -1)
+ {
+ if (etype == (EXT_NEXT_EXTENT_ALLOCDECS >> 30))
+ {
+ udf_write_aext(inode, bloc, &extoffset, neloc, nelen, bh, 0);
+ extoffset = 0;
+ if (lelen)
+ {
+ if (!memcmp(&UDF_I_LOCATION(inode), &bloc, sizeof(lb_addr)))
+ memset(bh->b_data, 0x00, udf_file_entry_alloc_offset(inode));
+ else
+ memset(bh->b_data, 0x00, sizeof(struct allocExtDesc));
+ udf_free_blocks(inode->i_sb, inode, bloc, 0, lelen);
+ }
+ else
+ {
+ if (!memcmp(&UDF_I_LOCATION(inode), &bloc, sizeof(lb_addr)))
+ {
+ UDF_I_LENALLOC(inode) = lenalloc;
+ mark_inode_dirty(inode);
+ }
+ else
+ {
+ struct allocExtDesc *aed = (struct allocExtDesc *)(bh->b_data);
+ aed->lengthAllocDescs = cpu_to_le32(lenalloc);
+ if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201)
+ udf_update_tag(bh->b_data, lenalloc +
+ sizeof(struct allocExtDesc));
+ else
+ udf_update_tag(bh->b_data, sizeof(struct allocExtDesc));
+ mark_buffer_dirty_inode(bh, inode);
+ }
+ }
+
+ udf_release_data(bh);
+ bh = NULL;
+
+ bloc = eloc;
+ if (elen)
+ lelen = (elen + inode->i_sb->s_blocksize - 1) >>
+ inode->i_sb->s_blocksize_bits;
+ else
+ lelen = 1;
+ }
+ else
+ {
+ extent_trunc(inode, bloc, extoffset, eloc, etype, elen, bh, 0);
+ extoffset += adsize;
+ }
+ }
+
+ if (lelen)
+ {
+ if (!memcmp(&UDF_I_LOCATION(inode), &bloc, sizeof(lb_addr)))
+ memset(bh->b_data, 0x00, udf_file_entry_alloc_offset(inode));
+ else
+ memset(bh->b_data, 0x00, sizeof(struct allocExtDesc));
+ udf_free_blocks(inode->i_sb, inode, bloc, 0, lelen);
+ }
+ else
+ {
+ if (!memcmp(&UDF_I_LOCATION(inode), &bloc, sizeof(lb_addr)))
+ {
+ UDF_I_LENALLOC(inode) = lenalloc;
+ mark_inode_dirty(inode);
+ }
+ else
+ {
+ struct allocExtDesc *aed = (struct allocExtDesc *)(bh->b_data);
+ aed->lengthAllocDescs = cpu_to_le32(lenalloc);
+ if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201)
+ udf_update_tag(bh->b_data, lenalloc +
+ sizeof(struct allocExtDesc));
+ else
+ udf_update_tag(bh->b_data, sizeof(struct allocExtDesc));
+ mark_buffer_dirty_inode(bh, inode);
+ }
+ }
+ }
+ else if (inode->i_size)
+ {
+ if (offset)
+ {
+ extoffset -= adsize;
+ etype = udf_next_aext(inode, &bloc, &extoffset, &eloc, &elen, &bh, 1);
+ if (etype == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30))
+ {
+ extoffset -= adsize;
+ elen = EXT_NOT_RECORDED_NOT_ALLOCATED | (elen + offset);
+ udf_write_aext(inode, bloc, &extoffset, eloc, elen, bh, 0);
+ }
+ else if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30))
+ {
+ lb_addr neloc = { 0, 0 };
+ extoffset -= adsize;
+ nelen = EXT_NOT_RECORDED_NOT_ALLOCATED |
+ ((elen + offset + inode->i_sb->s_blocksize - 1) &
+ ~(inode->i_sb->s_blocksize - 1));
+ udf_write_aext(inode, bloc, &extoffset, neloc, nelen, bh, 1);
+ udf_add_aext(inode, &bloc, &extoffset, eloc, (etype << 30) | elen, &bh, 1);
+ }
+ else
+ {
+ if (elen & (inode->i_sb->s_blocksize - 1))
+ {
+ extoffset -= adsize;
+ elen = EXT_RECORDED_ALLOCATED |
+ ((elen + inode->i_sb->s_blocksize - 1) &
+ ~(inode->i_sb->s_blocksize - 1));
+ udf_write_aext(inode, bloc, &extoffset, eloc, elen, bh, 1);
+ }
+ memset(&eloc, 0x00, sizeof(lb_addr));
+ elen = EXT_NOT_RECORDED_NOT_ALLOCATED | offset;
+ udf_add_aext(inode, &bloc, &extoffset, eloc, elen, &bh, 1);
+ }
+ }
+ }
+ UDF_I_LENEXTENTS(inode) = inode->i_size;
+
+ udf_release_data(bh);
+}
diff --git a/fs/udf/udf_i.h b/fs/udf/udf_i.h
new file mode 100644
index 00000000000000..dd5b9719a050c2
--- /dev/null
+++ b/fs/udf/udf_i.h
@@ -0,0 +1,22 @@
+#ifndef __LINUX_UDF_I_H
+#define __LINUX_UDF_I_H
+
+#define UDF_I(X) (&((X)->u.udf_i))
+
+#define UDF_I_LOCATION(X) ( UDF_I(X)->i_location )
+#define UDF_I_LENEATTR(X) ( UDF_I(X)->i_lenEAttr )
+#define UDF_I_LENALLOC(X) ( UDF_I(X)->i_lenAlloc )
+#define UDF_I_LENEXTENTS(X) ( UDF_I(X)->i_lenExtents )
+#define UDF_I_UNIQUE(X) ( UDF_I(X)->i_unique )
+#define UDF_I_ALLOCTYPE(X) ( UDF_I(X)->i_alloc_type )
+#define UDF_I_EXTENDED_FE(X) ( UDF_I(X)->i_extended_fe )
+#define UDF_I_STRAT4096(X) ( UDF_I(X)->i_strat_4096 )
+#define UDF_I_NEW_INODE(X) ( UDF_I(X)->i_new_inode )
+#define UDF_I_NEXT_ALLOC_BLOCK(X) ( UDF_I(X)->i_next_alloc_block )
+#define UDF_I_NEXT_ALLOC_GOAL(X) ( UDF_I(X)->i_next_alloc_goal )
+#define UDF_I_UMTIME(X) ( UDF_I(X)->i_umtime )
+#define UDF_I_UCTIME(X) ( UDF_I(X)->i_uctime )
+#define UDF_I_CRTIME(X) ( UDF_I(X)->i_crtime )
+#define UDF_I_UCRTIME(X) ( UDF_I(X)->i_ucrtime )
+
+#endif /* !defined(_LINUX_UDF_I_H) */
diff --git a/fs/udf/udf_sb.h b/fs/udf/udf_sb.h
new file mode 100644
index 00000000000000..0dde99415c976c
--- /dev/null
+++ b/fs/udf/udf_sb.h
@@ -0,0 +1,119 @@
+#ifndef __LINUX_UDF_SB_H
+#define __LINUX_UDF_SB_H
+
+/* Since UDF 2.01 is ISO 13346 based... */
+#define UDF_SUPER_MAGIC 0x15013346
+
+#define UDF_MAX_READ_VERSION 0x0201
+#define UDF_MAX_WRITE_VERSION 0x0201
+
+#define UDF_FLAG_USE_EXTENDED_FE 0
+#define UDF_VERS_USE_EXTENDED_FE 0x0200
+#define UDF_FLAG_USE_STREAMS 1
+#define UDF_VERS_USE_STREAMS 0x0200
+#define UDF_FLAG_USE_SHORT_AD 2
+#define UDF_FLAG_USE_AD_IN_ICB 3
+#define UDF_FLAG_USE_FILE_CTIME_EA 4
+#define UDF_FLAG_STRICT 5
+#define UDF_FLAG_UNDELETE 6
+#define UDF_FLAG_UNHIDE 7
+#define UDF_FLAG_VARCONV 8
+#define UDF_FLAG_NLS_MAP 9
+#define UDF_FLAG_UTF8 10
+
+#define UDF_PART_FLAG_UNALLOC_BITMAP 0x0001
+#define UDF_PART_FLAG_UNALLOC_TABLE 0x0002
+#define UDF_PART_FLAG_FREED_BITMAP 0x0004
+#define UDF_PART_FLAG_FREED_TABLE 0x0008
+#define UDF_PART_FLAG_READ_ONLY 0x0010
+#define UDF_PART_FLAG_WRITE_ONCE 0x0020
+#define UDF_PART_FLAG_REWRITABLE 0x0040
+#define UDF_PART_FLAG_OVERWRITABLE 0x0080
+
+#define UDF_SB_FREE(X)\
+{\
+ if (UDF_SB(X))\
+ {\
+ if (UDF_SB_PARTMAPS(X))\
+ kfree(UDF_SB_PARTMAPS(X));\
+ UDF_SB_PARTMAPS(X) = NULL;\
+ }\
+}
+#define UDF_SB(X) (&((X)->u.udf_sb))
+
+#define UDF_SB_ALLOC_PARTMAPS(X,Y)\
+{\
+ UDF_SB_PARTMAPS(X) = kmalloc(sizeof(struct udf_part_map) * Y, GFP_KERNEL);\
+ if (UDF_SB_PARTMAPS(X) != NULL)\
+ {\
+ UDF_SB_NUMPARTS(X) = Y;\
+ memset(UDF_SB_PARTMAPS(X), 0x00, sizeof(struct udf_part_map) * Y);\
+ }\
+ else\
+ {\
+ UDF_SB_NUMPARTS(X) = 0;\
+ udf_error(X, __FUNCTION__, "Unable to allocate space for %d partition maps", Y);\
+ }\
+}
+
+#define UDF_SB_ALLOC_BITMAP(X,Y,Z)\
+{\
+ int nr_groups = ((UDF_SB_PARTLEN((X),(Y)) + (sizeof(struct spaceBitmapDesc) << 3) +\
+ ((X)->s_blocksize * 8) - 1) / ((X)->s_blocksize * 8));\
+ UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap = kmalloc(sizeof(struct udf_bitmap) +\
+ sizeof(struct buffer_head *) * nr_groups,\
+ GFP_KERNEL);\
+ if (UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap != NULL)\
+ {\
+ memset(UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap, 0x00,\
+ sizeof(struct udf_bitmap) + sizeof(struct buffer_head *) * nr_groups);\
+ UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap->s_block_bitmap =\
+ (struct buffer_head **)(UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap + 1);\
+ UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap->s_nr_groups = nr_groups;\
+ }\
+ else\
+ {\
+ udf_error(X, __FUNCTION__, "Unable to allocate space for bitmap and %d buffer_head pointers", nr_groups);\
+ }\
+}
+
+
+#define UDF_QUERY_FLAG(X,Y) ( UDF_SB(X)->s_flags & ( 1 << (Y) ) )
+#define UDF_SET_FLAG(X,Y) ( UDF_SB(X)->s_flags |= ( 1 << (Y) ) )
+#define UDF_CLEAR_FLAG(X,Y) ( UDF_SB(X)->s_flags &= ~( 1 << (Y) ) )
+
+#define UDF_UPDATE_UDFREV(X,Y) ( ((Y) > UDF_SB_UDFREV(X)) ? UDF_SB_UDFREV(X) = (Y) : UDF_SB_UDFREV(X) )
+
+#define UDF_SB_PARTMAPS(X) ( UDF_SB(X)->s_partmaps )
+#define UDF_SB_PARTTYPE(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_partition_type )
+#define UDF_SB_PARTROOT(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_partition_root )
+#define UDF_SB_PARTLEN(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_partition_len )
+#define UDF_SB_PARTVSN(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_volumeseqnum )
+#define UDF_SB_PARTNUM(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_partition_num )
+#define UDF_SB_TYPESPAR(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_type_specific.s_sparing )
+#define UDF_SB_TYPEVIRT(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_type_specific.s_virtual )
+#define UDF_SB_PARTFUNC(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_partition_func )
+#define UDF_SB_PARTFLAGS(X,Y) ( UDF_SB_PARTMAPS(X)[(Y)].s_partition_flags )
+#define UDF_SB_BITMAP(X,Y,Z,I) ( UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap->s_block_bitmap[I] )
+#define UDF_SB_BITMAP_NR_GROUPS(X,Y,Z) ( UDF_SB_PARTMAPS(X)[(Y)].Z.s_bitmap->s_nr_groups )
+
+#define UDF_SB_VOLIDENT(X) ( UDF_SB(X)->s_volident )
+#define UDF_SB_NUMPARTS(X) ( UDF_SB(X)->s_partitions )
+#define UDF_SB_PARTITION(X) ( UDF_SB(X)->s_partition )
+#define UDF_SB_SESSION(X) ( UDF_SB(X)->s_session )
+#define UDF_SB_ANCHOR(X) ( UDF_SB(X)->s_anchor )
+#define UDF_SB_LASTBLOCK(X) ( UDF_SB(X)->s_lastblock )
+#define UDF_SB_LVIDBH(X) ( UDF_SB(X)->s_lvidbh )
+#define UDF_SB_LVID(X) ( (struct logicalVolIntegrityDesc *)UDF_SB_LVIDBH(X)->b_data )
+#define UDF_SB_LVIDIU(X) ( (struct logicalVolIntegrityDescImpUse *)&(UDF_SB_LVID(X)->impUse[le32_to_cpu(UDF_SB_LVID(X)->numOfPartitions) * 2 * sizeof(uint32_t)/sizeof(uint8_t)]) )
+
+#define UDF_SB_UMASK(X) ( UDF_SB(X)->s_umask )
+#define UDF_SB_GID(X) ( UDF_SB(X)->s_gid )
+#define UDF_SB_UID(X) ( UDF_SB(X)->s_uid )
+#define UDF_SB_RECORDTIME(X) ( UDF_SB(X)->s_recordtime )
+#define UDF_SB_SERIALNUM(X) ( UDF_SB(X)->s_serialnum )
+#define UDF_SB_UDFREV(X) ( UDF_SB(X)->s_udfrev )
+#define UDF_SB_FLAGS(X) ( UDF_SB(X)->s_flags )
+#define UDF_SB_VAT(X) ( UDF_SB(X)->s_vat )
+
+#endif /* __LINUX_UDF_SB_H */
diff --git a/fs/udf/udfdecl.h b/fs/udf/udfdecl.h
new file mode 100644
index 00000000000000..836aef50cdeddf
--- /dev/null
+++ b/fs/udf/udfdecl.h
@@ -0,0 +1,206 @@
+#ifndef __UDF_DECL_H
+#define __UDF_DECL_H
+
+#include <linux/udf_fs.h>
+#include "ecma_167.h"
+#include "osta_udf.h"
+
+#include <linux/fs.h>
+#include <linux/config.h>
+#include <linux/types.h>
+
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+#endif
+
+#if !defined(CONFIG_UDF_FS) && !defined(CONFIG_UDF_FS_MODULE)
+#define CONFIG_UDF_FS_MODULE
+#include <linux/udf_fs_i.h>
+#include <linux/udf_fs_sb.h>
+#endif
+
+#include "udfend.h"
+
+#define udf_fixed_to_variable(x) ( ( ( (x) >> 5 ) * 39 ) + ( (x) & 0x0000001F ) )
+#define udf_variable_to_fixed(x) ( ( ( (x) / 39 ) << 5 ) + ( (x) % 39 ) )
+
+#define UDF_EXTENT_LENGTH_MASK 0x3FFFFFFF
+#define UDF_EXTENT_FLAG_MASK 0xC0000000
+
+#define UDF_NAME_PAD 4
+#define UDF_NAME_LEN 255
+#define UDF_PATH_LEN 1023
+
+#define CURRENT_UTIME (xtime.tv_usec)
+
+#define udf_file_entry_alloc_offset(inode)\
+ ((UDF_I_EXTENDED_FE(inode) ?\
+ sizeof(struct extendedFileEntry) :\
+ sizeof(struct fileEntry)) + UDF_I_LENEATTR(inode))
+
+#define udf_ext0_offset(inode)\
+ (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB ?\
+ udf_file_entry_alloc_offset(inode) : 0)
+
+#define udf_get_lb_pblock(sb,loc,offset) udf_get_pblock((sb), (loc).logicalBlockNum, (loc).partitionReferenceNum, (offset))
+
+struct dentry;
+struct inode;
+struct task_struct;
+struct buffer_head;
+struct super_block;
+
+extern struct inode_operations udf_dir_inode_operations;
+extern struct file_operations udf_dir_operations;
+extern struct inode_operations udf_file_inode_operations;
+extern struct file_operations udf_file_operations;
+extern struct address_space_operations udf_aops;
+extern struct address_space_operations udf_adinicb_aops;
+extern struct address_space_operations udf_symlink_aops;
+
+struct udf_fileident_bh
+{
+ struct buffer_head *sbh;
+ struct buffer_head *ebh;
+ int soffset;
+ int eoffset;
+};
+
+struct udf_directory_record
+{
+ uint32_t d_parent;
+ uint32_t d_inode;
+ uint32_t d_name[255];
+};
+
+struct udf_vds_record
+{
+ uint32_t block;
+ uint32_t volDescSeqNum;
+};
+
+struct generic_desc
+{
+ tag descTag;
+ uint32_t volDescSeqNum;
+};
+
+struct ustr
+{
+ uint8_t u_cmpID;
+ uint8_t u_name[UDF_NAME_LEN];
+ uint8_t u_len;
+};
+
+/* super.c */
+extern void udf_error(struct super_block *, const char *, const char *, ...);
+extern void udf_warning(struct super_block *, const char *, const char *, ...);
+
+/* namei.c */
+extern int udf_write_fi(struct inode *inode, struct fileIdentDesc *, struct fileIdentDesc *, struct udf_fileident_bh *, uint8_t *, uint8_t *);
+
+/* file.c */
+extern int udf_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
+
+/* inode.c */
+extern struct inode *udf_iget(struct super_block *, lb_addr);
+extern int udf_sync_inode(struct inode *);
+extern void udf_expand_file_adinicb(struct inode *, int, int *);
+extern struct buffer_head * udf_expand_dir_adinicb(struct inode *, int *, int *);
+extern struct buffer_head * udf_getblk(struct inode *, long, int, int *);
+extern struct buffer_head * udf_bread(struct inode *, int, int, int *);
+extern void udf_truncate(struct inode *);
+extern void udf_read_inode(struct inode *);
+extern void udf_put_inode(struct inode *);
+extern void udf_delete_inode(struct inode *);
+extern void udf_write_inode(struct inode *, int);
+extern long udf_block_map(struct inode *, long);
+extern int8_t inode_bmap(struct inode *, int, lb_addr *, uint32_t *, lb_addr *, uint32_t *, uint32_t *, struct buffer_head **);
+extern int8_t udf_add_aext(struct inode *, lb_addr *, int *, lb_addr, uint32_t, struct buffer_head **, int);
+extern int8_t udf_write_aext(struct inode *, lb_addr, int *, lb_addr, uint32_t, struct buffer_head *, int);
+extern int8_t udf_insert_aext(struct inode *, lb_addr, int, lb_addr, uint32_t, struct buffer_head *);
+extern int8_t udf_delete_aext(struct inode *, lb_addr, int, lb_addr, uint32_t, struct buffer_head *);
+extern int8_t udf_next_aext(struct inode *, lb_addr *, int *, lb_addr *, uint32_t *, struct buffer_head **, int);
+extern int8_t udf_current_aext(struct inode *, lb_addr *, int *, lb_addr *, uint32_t *, struct buffer_head **, int);
+extern void udf_discard_prealloc(struct inode *);
+
+/* misc.c */
+extern int udf_read_tagged_data(char *, int size, int fd, int block, int partref);
+extern struct buffer_head *udf_tgetblk(struct super_block *, int);
+extern struct buffer_head *udf_tread(struct super_block *, int);
+extern struct genericFormat *udf_add_extendedattr(struct inode *, uint32_t, uint32_t, uint8_t, struct buffer_head **);
+extern struct genericFormat *udf_get_extendedattr(struct inode *, uint32_t, uint8_t, struct buffer_head **);
+extern struct buffer_head *udf_read_tagged(struct super_block *, uint32_t, uint32_t, uint16_t *);
+extern struct buffer_head *udf_read_ptagged(struct super_block *, lb_addr, uint32_t, uint16_t *);
+extern void udf_release_data(struct buffer_head *);
+
+/* lowlevel.c */
+extern unsigned int udf_get_last_session(struct super_block *);
+extern unsigned long udf_get_last_block(struct super_block *);
+
+/* partition.c */
+extern uint32_t udf_get_pblock(struct super_block *, uint32_t, uint16_t, uint32_t);
+extern uint32_t udf_get_pblock_virt15(struct super_block *, uint32_t, uint16_t, uint32_t);
+extern uint32_t udf_get_pblock_virt20(struct super_block *, uint32_t, uint16_t, uint32_t);
+extern uint32_t udf_get_pblock_spar15(struct super_block *, uint32_t, uint16_t, uint32_t);
+extern int udf_relocate_blocks(struct super_block *, long, long *);
+
+/* unicode.c */
+extern int udf_get_filename(struct super_block *, uint8_t *, uint8_t *, int);
+
+/* ialloc.c */
+extern void udf_free_inode(struct inode *);
+extern struct inode * udf_new_inode (struct inode *, int, int *);
+
+/* truncate.c */
+extern void udf_truncate_extents(struct inode *);
+
+/* balloc.c */
+extern void udf_free_blocks(struct super_block *, struct inode *, lb_addr, uint32_t, uint32_t);
+extern int udf_prealloc_blocks(struct super_block *, struct inode *, uint16_t, uint32_t, uint32_t);
+extern int udf_new_block(struct super_block *, struct inode *, uint16_t, uint32_t, int *);
+
+/* fsync.c */
+extern int udf_fsync_file(struct file *, struct dentry *, int);
+extern int udf_fsync_inode(struct inode *, int);
+
+/* directory.c */
+extern uint8_t * udf_filead_read(struct inode *, uint8_t *, uint8_t, lb_addr, int *, int *, struct buffer_head **, int *);
+extern struct fileIdentDesc * udf_fileident_read(struct inode *, loff_t *, struct udf_fileident_bh *, struct fileIdentDesc *, lb_addr *, uint32_t *, lb_addr *, uint32_t *, uint32_t *, struct buffer_head **);
+
+/* unicode.c */
+extern int udf_ustr_to_dchars(uint8_t *, const struct ustr *, int);
+extern int udf_ustr_to_char(uint8_t *, const struct ustr *, int);
+extern int udf_ustr_to_dstring(dstring *, const struct ustr *, int);
+extern int udf_dchars_to_ustr(struct ustr *, const uint8_t *, int);
+extern int udf_char_to_ustr(struct ustr *, const uint8_t *, int);
+extern int udf_dstring_to_ustr(struct ustr *, const dstring *, int);
+extern int udf_translate_to_linux(uint8_t *, uint8_t *, int, uint8_t *, int);
+extern int udf_build_ustr(struct ustr *, dstring *, int);
+extern int udf_build_ustr_exact(struct ustr *, dstring *, int);
+extern int udf_CS0toUTF8(struct ustr *, struct ustr *);
+extern int udf_UTF8toCS0(dstring *, struct ustr *, int);
+extern int udf_CS0toNLS(struct nls_table *, struct ustr *, struct ustr *);
+extern int udf_NLStoCS0(struct nls_table *, dstring *, struct ustr *, int);
+
+/* crc.c */
+extern uint16_t udf_crc(uint8_t *, uint32_t, uint16_t);
+
+/* misc.c */
+extern uint32_t udf64_low32(uint64_t);
+extern uint32_t udf64_high32(uint64_t);
+extern void udf_update_tag(char *, int);
+extern void udf_new_tag(char *, uint16_t, uint16_t, uint16_t, uint32_t, int);
+
+/* udftime.c */
+extern time_t *udf_stamp_to_time(time_t *, long *, timestamp);
+extern timestamp *udf_time_to_stamp(timestamp *, time_t, long);
+
+/* directory.c */
+extern struct fileIdentDesc * udf_get_fileident(void * buffer, int bufsize, int * offset);
+extern extent_ad * udf_get_fileextent(void * buffer, int bufsize, int * offset);
+extern long_ad * udf_get_filelongad(void * buffer, int bufsize, int * offset, int);
+extern short_ad * udf_get_fileshortad(void * buffer, int bufsize, int * offset, int);
+extern uint8_t * udf_get_filead(struct fileEntry *, uint8_t *, int, int, int, int *);
+
+#endif /* __UDF_DECL_H */
diff --git a/fs/udf/udfend.h b/fs/udf/udfend.h
new file mode 100644
index 00000000000000..6313daab090e71
--- /dev/null
+++ b/fs/udf/udfend.h
@@ -0,0 +1,81 @@
+#ifndef __UDF_ENDIAN_H
+#define __UDF_ENDIAN_H
+
+#include <asm/byteorder.h>
+#include <linux/string.h>
+
+static inline lb_addr lelb_to_cpu(lb_addr in)
+{
+ lb_addr out;
+ out.logicalBlockNum = le32_to_cpu(in.logicalBlockNum);
+ out.partitionReferenceNum = le16_to_cpu(in.partitionReferenceNum);
+ return out;
+}
+
+static inline lb_addr cpu_to_lelb(lb_addr in)
+{
+ lb_addr out;
+ out.logicalBlockNum = cpu_to_le32(in.logicalBlockNum);
+ out.partitionReferenceNum = cpu_to_le16(in.partitionReferenceNum);
+ return out;
+}
+
+static inline timestamp lets_to_cpu(timestamp in)
+{
+ timestamp out;
+ memcpy(&out, &in, sizeof(timestamp));
+ out.typeAndTimezone = le16_to_cpu(in.typeAndTimezone);
+ out.year = le16_to_cpu(in.year);
+ return out;
+}
+
+static inline short_ad lesa_to_cpu(short_ad in)
+{
+ short_ad out;
+ out.extLength = le32_to_cpu(in.extLength);
+ out.extPosition = le32_to_cpu(in.extPosition);
+ return out;
+}
+
+static inline short_ad cpu_to_lesa(short_ad in)
+{
+ short_ad out;
+ out.extLength = cpu_to_le32(in.extLength);
+ out.extPosition = cpu_to_le32(in.extPosition);
+ return out;
+}
+
+static inline long_ad lela_to_cpu(long_ad in)
+{
+ long_ad out;
+ out.extLength = le32_to_cpu(in.extLength);
+ out.extLocation = lelb_to_cpu(in.extLocation);
+ return out;
+}
+
+static inline long_ad cpu_to_lela(long_ad in)
+{
+ long_ad out;
+ out.extLength = cpu_to_le32(in.extLength);
+ out.extLocation = cpu_to_lelb(in.extLocation);
+ return out;
+}
+
+static inline extent_ad leea_to_cpu(extent_ad in)
+{
+ extent_ad out;
+ out.extLength = le32_to_cpu(in.extLength);
+ out.extLocation = le32_to_cpu(in.extLocation);
+ return out;
+}
+
+static inline timestamp cpu_to_lets(timestamp in)
+{
+ timestamp out;
+ memcpy(&out, &in, sizeof(timestamp));
+ out.typeAndTimezone = cpu_to_le16(in.typeAndTimezone);
+ out.year = cpu_to_le16(in.year);
+ return out;
+}
+
+#endif /* __UDF_ENDIAN_H */
diff --git a/fs/udf/udftime.c b/fs/udf/udftime.c
new file mode 100644
index 00000000000000..decaede92657f6
--- /dev/null
+++ b/fs/udf/udftime.c
@@ -0,0 +1,174 @@
+/* Copyright (C) 1993, 1994, 1995, 1996, 1997 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Paul Eggert (eggert@twinsun.com).
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+/*
+ * dgb 10/02/98: ripped this from glibc source to help convert timestamps to unix time
+ * 10/04/98: added new table-based lookup after seeing how ugly the gnu code is
+ * blf 09/27/99: ripped out all the old code and inserted new table from
+ * John Brockmeyer (without leap second corrections)
+ * rewrote udf_stamp_to_time and fixed timezone accounting in
+ udf_time_to_stamp.
+ */
+
+/*
+ * We don't take into account leap seconds. This may be correct or incorrect.
+ * For more NIST information (especially dealing with leap seconds), see:
+ * http://www.boulder.nist.gov/timefreq/pubs/bulletin/leapsecond.htm
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include "udfdecl.h"
+
+#define EPOCH_YEAR 1970
+
+#ifndef __isleap
+/* Nonzero if YEAR is a leap year (every 4 years,
+ except every 100th isn't, and every 400th is). */
+#define __isleap(year) \
+ ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
+#endif
+
+/* How many days come before each month (0-12). */
+const unsigned short int __mon_yday[2][13] =
+{
+ /* Normal years. */
+ { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
+ /* Leap years. */
+ { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
+};
+
+#define MAX_YEAR_SECONDS 69
+#define SPD 0x15180 /*3600*24*/
+#define SPY(y,l,s) (SPD * (365*y+l)+s)
+
+time_t year_seconds[MAX_YEAR_SECONDS]= {
+/*1970*/ SPY( 0, 0,0), SPY( 1, 0,0), SPY( 2, 0,0), SPY( 3, 1,0),
+/*1974*/ SPY( 4, 1,0), SPY( 5, 1,0), SPY( 6, 1,0), SPY( 7, 2,0),
+/*1978*/ SPY( 8, 2,0), SPY( 9, 2,0), SPY(10, 2,0), SPY(11, 3,0),
+/*1982*/ SPY(12, 3,0), SPY(13, 3,0), SPY(14, 3,0), SPY(15, 4,0),
+/*1986*/ SPY(16, 4,0), SPY(17, 4,0), SPY(18, 4,0), SPY(19, 5,0),
+/*1990*/ SPY(20, 5,0), SPY(21, 5,0), SPY(22, 5,0), SPY(23, 6,0),
+/*1994*/ SPY(24, 6,0), SPY(25, 6,0), SPY(26, 6,0), SPY(27, 7,0),
+/*1998*/ SPY(28, 7,0), SPY(29, 7,0), SPY(30, 7,0), SPY(31, 8,0),
+/*2002*/ SPY(32, 8,0), SPY(33, 8,0), SPY(34, 8,0), SPY(35, 9,0),
+/*2006*/ SPY(36, 9,0), SPY(37, 9,0), SPY(38, 9,0), SPY(39,10,0),
+/*2010*/ SPY(40,10,0), SPY(41,10,0), SPY(42,10,0), SPY(43,11,0),
+/*2014*/ SPY(44,11,0), SPY(45,11,0), SPY(46,11,0), SPY(47,12,0),
+/*2018*/ SPY(48,12,0), SPY(49,12,0), SPY(50,12,0), SPY(51,13,0),
+/*2022*/ SPY(52,13,0), SPY(53,13,0), SPY(54,13,0), SPY(55,14,0),
+/*2026*/ SPY(56,14,0), SPY(57,14,0), SPY(58,14,0), SPY(59,15,0),
+/*2030*/ SPY(60,15,0), SPY(61,15,0), SPY(62,15,0), SPY(63,16,0),
+/*2034*/ SPY(64,16,0), SPY(65,16,0), SPY(66,16,0), SPY(67,17,0),
+/*2038*/ SPY(68,17,0)
+};
+
+extern struct timezone sys_tz;
+
+#define SECS_PER_HOUR (60 * 60)
+#define SECS_PER_DAY (SECS_PER_HOUR * 24)
+
+time_t *
+udf_stamp_to_time(time_t *dest, long *dest_usec, timestamp src)
+{
+ int yday;
+ uint8_t type = src.typeAndTimezone >> 12;
+ int16_t offset;
+
+ if (type == 1)
+ {
+ offset = src.typeAndTimezone << 4;
+ /* sign extent offset */
+ offset = (offset >> 4);
+ if (offset == -2047) /* unspecified offset */
+ offset = 0;
+ }
+ else
+ offset = 0;
+
+ if ((src.year < EPOCH_YEAR) ||
+ (src.year > EPOCH_YEAR+MAX_YEAR_SECONDS))
+ {
+ *dest = -1;
+ *dest_usec = -1;
+ return NULL;
+ }
+ *dest = year_seconds[src.year - EPOCH_YEAR];
+ *dest -= offset * 60;
+
+ yday = ((__mon_yday[__isleap (src.year)]
+ [src.month-1]) + (src.day-1));
+ *dest += ( ( (yday* 24) + src.hour ) * 60 + src.minute ) * 60 + src.second;
+ *dest_usec = src.centiseconds * 10000 + src.hundredsOfMicroseconds * 100 + src.microseconds;
+ return dest;
+}
+
+
+timestamp *
+udf_time_to_stamp(timestamp *dest, time_t tv_sec, long tv_usec)
+{
+ long int days, rem, y;
+ const unsigned short int *ip;
+ int16_t offset;
+
+ offset = -sys_tz.tz_minuteswest;
+
+ if (!dest)
+ return NULL;
+
+ dest->typeAndTimezone = 0x1000 | (offset & 0x0FFF);
+
+ tv_sec += offset * 60;
+ days = tv_sec / SECS_PER_DAY;
+ rem = tv_sec % SECS_PER_DAY;
+ dest->hour = rem / SECS_PER_HOUR;
+ rem %= SECS_PER_HOUR;
+ dest->minute = rem / 60;
+ dest->second = rem % 60;
+ y = 1970;
+
+#define DIV(a,b) ((a) / (b) - ((a) % (b) < 0))
+#define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400))
+
+ while (days < 0 || days >= (__isleap(y) ? 366 : 365))
+ {
+ long int yg = y + days / 365 - (days % 365 < 0);
+
+ /* Adjust DAYS and Y to match the guessed year. */
+ days -= ((yg - y) * 365
+ + LEAPS_THRU_END_OF (yg - 1)
+ - LEAPS_THRU_END_OF (y - 1));
+ y = yg;
+ }
+ dest->year = y;
+ ip = __mon_yday[__isleap(y)];
+ for (y = 11; days < (long int) ip[y]; --y)
+ continue;
+ days -= ip[y];
+ dest->month = y + 1;
+ dest->day = days + 1;
+
+ dest->centiseconds = tv_usec / 10000;
+ dest->hundredsOfMicroseconds = (tv_usec - dest->centiseconds * 10000) / 100;
+ dest->microseconds = (tv_usec - dest->centiseconds * 10000 -
+ dest->hundredsOfMicroseconds * 100);
+ return dest;
+}
+
+/* EOF */
diff --git a/fs/udf/unicode.c b/fs/udf/unicode.c
new file mode 100644
index 00000000000000..eb88a0173f1702
--- /dev/null
+++ b/fs/udf/unicode.c
@@ -0,0 +1,534 @@
+/*
+ * unicode.c
+ *
+ * PURPOSE
+ * Routines for converting between UTF-8 and OSTA Compressed Unicode.
+ * Also handles filename mangling
+ *
+ * DESCRIPTION
+ * OSTA Compressed Unicode is explained in the OSTA UDF specification.
+ * http://www.osta.org/
+ * UTF-8 is explained in the IETF RFC XXXX.
+ * ftp://ftp.internic.net/rfc/rfcxxxx.txt
+ *
+ * CONTACTS
+ * E-mail regarding any portion of the Linux UDF file system should be
+ * directed to the development team's mailing list (run by majordomo):
+ * linux_udf@hpesjro.fc.hp.com
+ *
+ * COPYRIGHT
+ * This file is distributed under the terms of the GNU General Public
+ * License (GPL). Copies of the GPL can be obtained from:
+ * ftp://prep.ai.mit.edu/pub/gnu/GPL
+ * Each contributing author retains all rights to their own work.
+ */
+
+#include "udfdecl.h"
+
+#include <linux/kernel.h>
+#include <linux/string.h> /* for memset */
+#include <linux/nls.h>
+#include <linux/udf_fs.h>
+
+#include "udf_sb.h"
+
+int udf_ustr_to_dchars(uint8_t *dest, const struct ustr *src, int strlen)
+{
+ if ( (!dest) || (!src) || (!strlen) || (src->u_len > strlen) )
+ return 0;
+ memcpy(dest+1, src->u_name, src->u_len);
+ dest[0] = src->u_cmpID;
+ return src->u_len + 1;
+}
+
+int udf_ustr_to_char(uint8_t *dest, const struct ustr *src, int strlen)
+{
+ if ( (!dest) || (!src) || (!strlen) || (src->u_len >= strlen) )
+ return 0;
+ memcpy(dest, src->u_name, src->u_len);
+ return src->u_len;
+}
+
+int udf_ustr_to_dstring(dstring *dest, const struct ustr *src, int dlength)
+{
+ if ( udf_ustr_to_dchars(dest, src, dlength-1) )
+ {
+ dest[dlength-1] = src->u_len + 1;
+ return dlength;
+ }
+ else
+ return 0;
+}
+
+int udf_dchars_to_ustr(struct ustr *dest, const uint8_t *src, int strlen)
+{
+ if ( (!dest) || (!src) || (!strlen) || (strlen > UDF_NAME_LEN) )
+ return 0;
+ memset(dest, 0, sizeof(struct ustr));
+ memcpy(dest->u_name, src+1, strlen-1);
+ dest->u_cmpID = src[0];
+ dest->u_len = strlen-1;
+ return strlen-1;
+}
+
+int udf_char_to_ustr(struct ustr *dest, const uint8_t *src, int strlen)
+{
+ if ( (!dest) || (!src) || (!strlen) || (strlen >= UDF_NAME_LEN) )
+ return 0;
+ memset(dest, 0, sizeof(struct ustr));
+ memcpy(dest->u_name, src, strlen);
+ dest->u_cmpID = 0x08;
+ dest->u_len = strlen;
+ return strlen;
+}
+
+
+int udf_dstring_to_ustr(struct ustr *dest, const dstring *src, int dlength)
+{
+ if ( dlength && udf_dchars_to_ustr(dest, src, src[dlength-1]) )
+ return dlength;
+ else
+ return 0;
+}
+
+/*
+ * udf_build_ustr
+ */
+int udf_build_ustr(struct ustr *dest, dstring *ptr, int size)
+{
+ int usesize;
+
+ if ( (!dest) || (!ptr) || (!size) )
+ return -1;
+
+ memset(dest, 0, sizeof(struct ustr));
+ usesize= (size > UDF_NAME_LEN) ? UDF_NAME_LEN : size;
+ dest->u_cmpID=ptr[0];
+ dest->u_len=ptr[size-1];
+ memcpy(dest->u_name, ptr+1, usesize-1);
+ return 0;
+}
+
+/*
+ * udf_build_ustr_exact
+ */
+int udf_build_ustr_exact(struct ustr *dest, dstring *ptr, int exactsize)
+{
+ if ( (!dest) || (!ptr) || (!exactsize) )
+ return -1;
+
+ memset(dest, 0, sizeof(struct ustr));
+ dest->u_cmpID=ptr[0];
+ dest->u_len=exactsize-1;
+ memcpy(dest->u_name, ptr+1, exactsize-1);
+ return 0;
+}
+
+/*
+ * udf_ocu_to_utf8
+ *
+ * PURPOSE
+ * Convert OSTA Compressed Unicode to the UTF-8 equivalent.
+ *
+ * DESCRIPTION
+ * This routine is only called by udf_filldir().
+ *
+ * PRE-CONDITIONS
+ * utf Pointer to UTF-8 output buffer.
+ * ocu Pointer to OSTA Compressed Unicode input buffer
+ * of size UDF_NAME_LEN bytes.
+ * both of type "struct ustr *"
+ *
+ * POST-CONDITIONS
+ * <return> Zero on success.
+ *
+ * HISTORY
+ * November 12, 1997 - Andrew E. Mileski
+ * Written, tested, and released.
+ */
+int udf_CS0toUTF8(struct ustr *utf_o, struct ustr *ocu_i)
+{
+ uint8_t *ocu;
+ uint32_t c;
+ uint8_t cmp_id, ocu_len;
+ int i;
+
+ ocu = ocu_i->u_name;
+
+ ocu_len = ocu_i->u_len;
+ cmp_id = ocu_i->u_cmpID;
+ utf_o->u_len = 0;
+
+ if (ocu_len == 0)
+ {
+ memset(utf_o, 0, sizeof(struct ustr));
+ utf_o->u_cmpID = 0;
+ utf_o->u_len = 0;
+ return 0;
+ }
+
+ if ((cmp_id != 8) && (cmp_id != 16))
+ {
+ printk(KERN_ERR "udf: unknown compression code (%d) stri=%s\n", cmp_id, ocu_i->u_name);
+ return 0;
+ }
+
+ for (i = 0; (i < ocu_len) && (utf_o->u_len <= (UDF_NAME_LEN-3)) ;)
+ {
+
+ /* Expand OSTA compressed Unicode to Unicode */
+ c = ocu[i++];
+ if (cmp_id == 16)
+ c = (c << 8) | ocu[i++];
+
+ /* Compress Unicode to UTF-8 */
+ if (c < 0x80U)
+ utf_o->u_name[utf_o->u_len++] = (uint8_t)c;
+ else if (c < 0x800U)
+ {
+ utf_o->u_name[utf_o->u_len++] = (uint8_t)(0xc0 | (c >> 6));
+ utf_o->u_name[utf_o->u_len++] = (uint8_t)(0x80 | (c & 0x3f));
+ }
+ else
+ {
+ utf_o->u_name[utf_o->u_len++] = (uint8_t)(0xe0 | (c >> 12));
+ utf_o->u_name[utf_o->u_len++] = (uint8_t)(0x80 | ((c >> 6) & 0x3f));
+ utf_o->u_name[utf_o->u_len++] = (uint8_t)(0x80 | (c & 0x3f));
+ }
+ }
+ utf_o->u_cmpID=8;
+
+ return utf_o->u_len;
+}
+
+/*
+ *
+ * udf_utf8_to_ocu
+ *
+ * PURPOSE
+ * Convert UTF-8 to the OSTA Compressed Unicode equivalent.
+ *
+ * DESCRIPTION
+ * This routine is only called by udf_lookup().
+ *
+ * PRE-CONDITIONS
+ * ocu Pointer to OSTA Compressed Unicode output
+ * buffer of size UDF_NAME_LEN bytes.
+ * utf Pointer to UTF-8 input buffer.
+ * utf_len Length of UTF-8 input buffer in bytes.
+ *
+ * POST-CONDITIONS
+ * <return> Zero on success.
+ *
+ * HISTORY
+ * November 12, 1997 - Andrew E. Mileski
+ * Written, tested, and released.
+ */
+int udf_UTF8toCS0(dstring *ocu, struct ustr *utf, int length)
+{
+ unsigned c, i, max_val, utf_char;
+ int utf_cnt;
+ int u_len = 0;
+
+ memset(ocu, 0, sizeof(dstring) * length);
+ ocu[0] = 8;
+ max_val = 0xffU;
+
+try_again:
+ utf_char = 0U;
+ utf_cnt = 0U;
+ for (i = 0U; i < utf->u_len; i++)
+ {
+ c = (uint8_t)utf->u_name[i];
+
+ /* Complete a multi-byte UTF-8 character */
+ if (utf_cnt)
+ {
+ utf_char = (utf_char << 6) | (c & 0x3fU);
+ if (--utf_cnt)
+ continue;
+ }
+ else
+ {
+ /* Check for a multi-byte UTF-8 character */
+ if (c & 0x80U)
+ {
+ /* Start a multi-byte UTF-8 character */
+ if ((c & 0xe0U) == 0xc0U)
+ {
+ utf_char = c & 0x1fU;
+ utf_cnt = 1;
+ }
+ else if ((c & 0xf0U) == 0xe0U)
+ {
+ utf_char = c & 0x0fU;
+ utf_cnt = 2;
+ }
+ else if ((c & 0xf8U) == 0xf0U)
+ {
+ utf_char = c & 0x07U;
+ utf_cnt = 3;
+ }
+ else if ((c & 0xfcU) == 0xf8U)
+ {
+ utf_char = c & 0x03U;
+ utf_cnt = 4;
+ }
+ else if ((c & 0xfeU) == 0xfcU)
+ {
+ utf_char = c & 0x01U;
+ utf_cnt = 5;
+ }
+ else
+ goto error_out;
+ continue;
+ } else
+ /* Single byte UTF-8 character (most common) */
+ utf_char = c;
+ }
+
+ /* Choose no compression if necessary */
+ if (utf_char > max_val)
+ {
+ if ( 0xffU == max_val )
+ {
+ max_val = 0xffffU;
+ ocu[0] = (uint8_t)0x10U;
+ goto try_again;
+ }
+ goto error_out;
+ }
+
+ if (max_val == 0xffffU)
+ {
+ ocu[++u_len] = (uint8_t)(utf_char >> 8);
+ }
+ ocu[++u_len] = (uint8_t)(utf_char & 0xffU);
+ }
+
+
+ if (utf_cnt)
+ {
+error_out:
+ printk(KERN_ERR "udf: bad UTF-8 character\n");
+ return 0;
+ }
+
+ ocu[length - 1] = (uint8_t)u_len + 1;
+ return u_len + 1;
+}
+
+int udf_CS0toNLS(struct nls_table *nls, struct ustr *utf_o, struct ustr *ocu_i)
+{
+ uint8_t *ocu;
+ uint32_t c;
+ uint8_t cmp_id, ocu_len;
+ int i;
+
+ ocu = ocu_i->u_name;
+
+ ocu_len = ocu_i->u_len;
+ cmp_id = ocu_i->u_cmpID;
+ utf_o->u_len = 0;
+
+ if (ocu_len == 0)
+ {
+ memset(utf_o, 0, sizeof(struct ustr));
+ utf_o->u_cmpID = 0;
+ utf_o->u_len = 0;
+ return 0;
+ }
+
+ if ((cmp_id != 8) && (cmp_id != 16))
+ {
+ printk(KERN_ERR "udf: unknown compression code (%d) stri=%s\n", cmp_id, ocu_i->u_name);
+ return 0;
+ }
+
+ for (i = 0; (i < ocu_len) && (utf_o->u_len <= (UDF_NAME_LEN-3)) ;)
+ {
+ /* Expand OSTA compressed Unicode to Unicode */
+ c = ocu[i++];
+ if (cmp_id == 16)
+ c = (c << 8) | ocu[i++];
+
+ utf_o->u_len += nls->uni2char(c, &utf_o->u_name[utf_o->u_len],
+ UDF_NAME_LEN - utf_o->u_len);
+ }
+ utf_o->u_cmpID=8;
+
+ return utf_o->u_len;
+}
+
+int udf_NLStoCS0(struct nls_table *nls, dstring *ocu, struct ustr *uni, int length)
+{
+ unsigned len, i, max_val;
+ uint16_t uni_char;
+ int uni_cnt;
+ int u_len = 0;
+
+ memset(ocu, 0, sizeof(dstring) * length);
+ ocu[0] = 8;
+ max_val = 0xffU;
+
+try_again:
+ uni_char = 0U;
+ uni_cnt = 0U;
+ for (i = 0U; i < uni->u_len; i++)
+ {
+ len = nls->char2uni(&uni->u_name[i], uni->u_len-i, &uni_char);
+
+ if (len == 2 && max_val == 0xff)
+ {
+ max_val = 0xffffU;
+ ocu[0] = (uint8_t)0x10U;
+ goto try_again;
+ }
+
+ if (max_val == 0xffffU)
+ {
+ ocu[++u_len] = (uint8_t)(uni_char >> 8);
+ i++;
+ }
+ ocu[++u_len] = (uint8_t)(uni_char & 0xffU);
+ }
+
+ ocu[length - 1] = (uint8_t)u_len + 1;
+ return u_len + 1;
+}
+
+int udf_get_filename(struct super_block *sb, uint8_t *sname, uint8_t *dname, int flen)
+{
+ struct ustr filename, unifilename;
+ int len;
+
+ if (udf_build_ustr_exact(&unifilename, sname, flen))
+ {
+ return 0;
+ }
+
+ if (UDF_QUERY_FLAG(sb, UDF_FLAG_UTF8))
+ {
+ if (!udf_CS0toUTF8(&filename, &unifilename) )
+ {
+ udf_debug("Failed in udf_get_filename: sname = %s\n", sname);
+ return 0;
+ }
+ }
+ else if (UDF_QUERY_FLAG(sb, UDF_FLAG_NLS_MAP))
+ {
+ if (!udf_CS0toNLS(UDF_SB(sb)->s_nls_map, &filename, &unifilename) )
+ {
+ udf_debug("Failed in udf_get_filename: sname = %s\n", sname);
+ return 0;
+ }
+ }
+ else
+ return 0;
+
+ if ((len = udf_translate_to_linux(dname, filename.u_name, filename.u_len,
+ unifilename.u_name, unifilename.u_len)))
+ {
+ return len;
+ }
+ return 0;
+}
+
+#define ILLEGAL_CHAR_MARK '_'
+#define EXT_MARK '.'
+#define CRC_MARK '#'
+#define EXT_SIZE 5
+
+int udf_translate_to_linux(uint8_t *newName, uint8_t *udfName, int udfLen, uint8_t *fidName, int fidNameLen)
+{
+ int index, newIndex = 0, needsCRC = 0;
+ int extIndex = 0, newExtIndex = 0, hasExt = 0;
+ unsigned short valueCRC;
+ uint8_t curr;
+ const uint8_t hexChar[] = "0123456789ABCDEF";
+
+ if (udfName[0] == '.' && (udfLen == 1 ||
+ (udfLen == 2 && udfName[1] == '.')))
+ {
+ needsCRC = 1;
+ newIndex = udfLen;
+ memcpy(newName, udfName, udfLen);
+ }
+ else
+ {
+ for (index = 0; index < udfLen; index++)
+ {
+ curr = udfName[index];
+ if (curr == '/' || curr == 0)
+ {
+ needsCRC = 1;
+ curr = ILLEGAL_CHAR_MARK;
+ while (index+1 < udfLen && (udfName[index+1] == '/' ||
+ udfName[index+1] == 0))
+ index++;
+ }
+ if (curr == EXT_MARK && (udfLen - index - 1) <= EXT_SIZE)
+ {
+ if (udfLen == index + 1)
+ hasExt = 0;
+ else
+ {
+ hasExt = 1;
+ extIndex = index;
+ newExtIndex = newIndex;
+ }
+ }
+ if (newIndex < 256)
+ newName[newIndex++] = curr;
+ else
+ needsCRC = 1;
+ }
+ }
+ if (needsCRC)
+ {
+ uint8_t ext[EXT_SIZE];
+ int localExtIndex = 0;
+
+ if (hasExt)
+ {
+ int maxFilenameLen;
+ for(index = 0; index<EXT_SIZE && extIndex + index +1 < udfLen;
+ index++ )
+ {
+ curr = udfName[extIndex + index + 1];
+
+ if (curr == '/' || curr == 0)
+ {
+ needsCRC = 1;
+ curr = ILLEGAL_CHAR_MARK;
+ while(extIndex + index + 2 < udfLen && (index + 1 < EXT_SIZE
+ && (udfName[extIndex + index + 2] == '/' ||
+ udfName[extIndex + index + 2] == 0)))
+ index++;
+ }
+ ext[localExtIndex++] = curr;
+ }
+ maxFilenameLen = 250 - localExtIndex;
+ if (newIndex > maxFilenameLen)
+ newIndex = maxFilenameLen;
+ else
+ newIndex = newExtIndex;
+ }
+ else if (newIndex > 250)
+ newIndex = 250;
+ newName[newIndex++] = CRC_MARK;
+ valueCRC = udf_crc(fidName, fidNameLen, 0);
+ newName[newIndex++] = hexChar[(valueCRC & 0xf000) >> 12];
+ newName[newIndex++] = hexChar[(valueCRC & 0x0f00) >> 8];
+ newName[newIndex++] = hexChar[(valueCRC & 0x00f0) >> 4];
+ newName[newIndex++] = hexChar[(valueCRC & 0x000f)];
+
+ if (hasExt)
+ {
+ newName[newIndex++] = EXT_MARK;
+ for (index = 0;index < localExtIndex ;index++ )
+ newName[newIndex++] = ext[index];
+ }
+ }
+ return newIndex;
+}
diff --git a/fs/ufs/Makefile b/fs/ufs/Makefile
new file mode 100644
index 00000000000000..4e74b2459bacab
--- /dev/null
+++ b/fs/ufs/Makefile
@@ -0,0 +1,16 @@
+#
+# Makefile for the Linux ufs filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile.
+
+O_TARGET := ufs.o
+
+obj-y := balloc.o cylinder.o dir.o file.o ialloc.o inode.o \
+ namei.o super.o symlink.o truncate.o util.o
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/ufs/balloc.c b/fs/ufs/balloc.c
new file mode 100644
index 00000000000000..734d7d210c2e37
--- /dev/null
+++ b/fs/ufs/balloc.c
@@ -0,0 +1,815 @@
+/*
+ * linux/fs/ufs/balloc.c
+ *
+ * Copyright (C) 1998
+ * Daniel Pirkl <daniel.pirkl@email.cz>
+ * Charles University, Faculty of Mathematics and Physics
+ */
+
+#include <linux/fs.h>
+#include <linux/ufs_fs.h>
+#include <linux/stat.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/quotaops.h>
+#include <asm/bitops.h>
+#include <asm/byteorder.h>
+
+#include "swab.h"
+#include "util.h"
+
+#undef UFS_BALLOC_DEBUG
+
+#ifdef UFS_BALLOC_DEBUG
+#define UFSD(x) printk("(%s, %d), %s:", __FILE__, __LINE__, __FUNCTION__); printk x;
+#else
+#define UFSD(x)
+#endif
+
+unsigned ufs_add_fragments (struct inode *, unsigned, unsigned, unsigned, int *);
+unsigned ufs_alloc_fragments (struct inode *, unsigned, unsigned, unsigned, int *);
+unsigned ufs_alloccg_block (struct inode *, struct ufs_cg_private_info *, unsigned, int *);
+unsigned ufs_bitmap_search (struct super_block *, struct ufs_cg_private_info *, unsigned, unsigned);
+static unsigned char ufs_fragtable_8fpb[], ufs_fragtable_other[];
+void ufs_clusteracct(struct super_block *, struct ufs_cg_private_info *, unsigned, int);
+
+/*
+ * Free 'count' fragments from fragment number 'fragment'
+ */
+void ufs_free_fragments (struct inode * inode, unsigned fragment, unsigned count) {
+ struct super_block * sb;
+ struct ufs_sb_private_info * uspi;
+ struct ufs_super_block_first * usb1;
+ struct ufs_cg_private_info * ucpi;
+ struct ufs_cylinder_group * ucg;
+ unsigned cgno, bit, end_bit, bbase, blkmap, i, blkno, cylno;
+
+ sb = inode->i_sb;
+ uspi = sb->u.ufs_sb.s_uspi;
+ usb1 = ubh_get_usb_first(USPI_UBH);
+
+ UFSD(("ENTER, fragment %u, count %u\n", fragment, count))
+
+ if (ufs_fragnum(fragment) + count > uspi->s_fpg)
+ ufs_error (sb, "ufs_free_fragments", "internal error");
+
+ lock_super(sb);
+
+ cgno = ufs_dtog(fragment);
+ bit = ufs_dtogd(fragment);
+ if (cgno >= uspi->s_ncg) {
+ ufs_panic (sb, "ufs_free_fragments", "freeing blocks are outside device");
+ goto failed;
+ }
+
+ ucpi = ufs_load_cylinder (sb, cgno);
+ if (!ucpi)
+ goto failed;
+ ucg = ubh_get_ucg (UCPI_UBH);
+ if (!ufs_cg_chkmagic(sb, ucg)) {
+ ufs_panic (sb, "ufs_free_fragments", "internal error, bad magic number on cg %u", cgno);
+ goto failed;
+ }
+
+ end_bit = bit + count;
+ bbase = ufs_blknum (bit);
+ blkmap = ubh_blkmap (UCPI_UBH, ucpi->c_freeoff, bbase);
+ ufs_fragacct (sb, blkmap, ucg->cg_frsum, -1);
+ for (i = bit; i < end_bit; i++) {
+ if (ubh_isclr (UCPI_UBH, ucpi->c_freeoff, i))
+ ubh_setbit (UCPI_UBH, ucpi->c_freeoff, i);
+ else ufs_error (sb, "ufs_free_fragments",
+ "bit already cleared for fragment %u", i);
+ }
+
+ DQUOT_FREE_BLOCK (inode, count);
+
+
+ fs32_add(sb, &ucg->cg_cs.cs_nffree, count);
+ fs32_add(sb, &usb1->fs_cstotal.cs_nffree, count);
+ fs32_add(sb, &sb->fs_cs(cgno).cs_nffree, count);
+ blkmap = ubh_blkmap (UCPI_UBH, ucpi->c_freeoff, bbase);
+ ufs_fragacct(sb, blkmap, ucg->cg_frsum, 1);
+
+ /*
+ * Trying to reassemble free fragments into block
+ */
+ blkno = ufs_fragstoblks (bbase);
+ if (ubh_isblockset(UCPI_UBH, ucpi->c_freeoff, blkno)) {
+ fs32_sub(sb, &ucg->cg_cs.cs_nffree, uspi->s_fpb);
+ fs32_sub(sb, &usb1->fs_cstotal.cs_nffree, uspi->s_fpb);
+ fs32_sub(sb, &sb->fs_cs(cgno).cs_nffree, uspi->s_fpb);
+ if ((sb->u.ufs_sb.s_flags & UFS_CG_MASK) == UFS_CG_44BSD)
+ ufs_clusteracct (sb, ucpi, blkno, 1);
+ fs32_add(sb, &ucg->cg_cs.cs_nbfree, 1);
+ fs32_add(sb, &usb1->fs_cstotal.cs_nbfree, 1);
+ fs32_add(sb, &sb->fs_cs(cgno).cs_nbfree, 1);
+ cylno = ufs_cbtocylno (bbase);
+ fs16_add(sb, &ubh_cg_blks(ucpi, cylno, ufs_cbtorpos(bbase)), 1);
+ fs32_add(sb, &ubh_cg_blktot(ucpi, cylno), 1);
+ }
+
+ ubh_mark_buffer_dirty (USPI_UBH);
+ ubh_mark_buffer_dirty (UCPI_UBH);
+ if (sb->s_flags & MS_SYNCHRONOUS) {
+ ubh_ll_rw_block (WRITE, 1, (struct ufs_buffer_head **)&ucpi);
+ ubh_wait_on_buffer (UCPI_UBH);
+ }
+ sb->s_dirt = 1;
+
+ unlock_super (sb);
+ UFSD(("EXIT\n"))
+ return;
+
+failed:
+ unlock_super (sb);
+ UFSD(("EXIT (FAILED)\n"))
+ return;
+}
+
+/*
+ * Free 'count' fragments from fragment number 'fragment' (free whole blocks)
+ */
+void ufs_free_blocks (struct inode * inode, unsigned fragment, unsigned count) {
+ struct super_block * sb;
+ struct ufs_sb_private_info * uspi;
+ struct ufs_super_block_first * usb1;
+ struct ufs_cg_private_info * ucpi;
+ struct ufs_cylinder_group * ucg;
+ unsigned overflow, cgno, bit, end_bit, blkno, i, cylno;
+
+ sb = inode->i_sb;
+ uspi = sb->u.ufs_sb.s_uspi;
+ usb1 = ubh_get_usb_first(USPI_UBH);
+
+ UFSD(("ENTER, fragment %u, count %u\n", fragment, count))
+
+ if ((fragment & uspi->s_fpbmask) || (count & uspi->s_fpbmask)) {
+ ufs_error (sb, "ufs_free_blocks", "internal error, "
+ "fragment %u, count %u\n", fragment, count);
+ goto failed;
+ }
+
+ lock_super(sb);
+
+do_more:
+ overflow = 0;
+ cgno = ufs_dtog (fragment);
+ bit = ufs_dtogd (fragment);
+ if (cgno >= uspi->s_ncg) {
+ ufs_panic (sb, "ufs_free_blocks", "freeing blocks are outside device");
+ goto failed;
+ }
+ end_bit = bit + count;
+ if (end_bit > uspi->s_fpg) {
+ overflow = bit + count - uspi->s_fpg;
+ count -= overflow;
+ end_bit -= overflow;
+ }
+
+ ucpi = ufs_load_cylinder (sb, cgno);
+ if (!ucpi)
+ goto failed;
+ ucg = ubh_get_ucg (UCPI_UBH);
+ if (!ufs_cg_chkmagic(sb, ucg)) {
+ ufs_panic (sb, "ufs_free_blocks", "internal error, bad magic number on cg %u", cgno);
+ goto failed;
+ }
+
+ for (i = bit; i < end_bit; i += uspi->s_fpb) {
+ blkno = ufs_fragstoblks(i);
+ if (ubh_isblockset(UCPI_UBH, ucpi->c_freeoff, blkno)) {
+ ufs_error(sb, "ufs_free_blocks", "freeing free fragment");
+ }
+ ubh_setblock(UCPI_UBH, ucpi->c_freeoff, blkno);
+ if ((sb->u.ufs_sb.s_flags & UFS_CG_MASK) == UFS_CG_44BSD)
+ ufs_clusteracct (sb, ucpi, blkno, 1);
+ DQUOT_FREE_BLOCK(inode, uspi->s_fpb);
+
+ fs32_add(sb, &ucg->cg_cs.cs_nbfree, 1);
+ fs32_add(sb, &usb1->fs_cstotal.cs_nbfree, 1);
+ fs32_add(sb, &sb->fs_cs(cgno).cs_nbfree, 1);
+ cylno = ufs_cbtocylno(i);
+ fs16_add(sb, &ubh_cg_blks(ucpi, cylno, ufs_cbtorpos(i)), 1);
+ fs32_add(sb, &ubh_cg_blktot(ucpi, cylno), 1);
+ }
+
+ ubh_mark_buffer_dirty (USPI_UBH);
+ ubh_mark_buffer_dirty (UCPI_UBH);
+ if (sb->s_flags & MS_SYNCHRONOUS) {
+ ubh_ll_rw_block (WRITE, 1, (struct ufs_buffer_head **)&ucpi);
+ ubh_wait_on_buffer (UCPI_UBH);
+ }
+
+ if (overflow) {
+ fragment += count;
+ count = overflow;
+ goto do_more;
+ }
+
+ sb->s_dirt = 1;
+ unlock_super (sb);
+ UFSD(("EXIT\n"))
+ return;
+
+failed:
+ unlock_super (sb);
+ UFSD(("EXIT (FAILED)\n"))
+ return;
+}
+
+
+
+#define NULLIFY_FRAGMENTS \
+ for (i = oldcount; i < newcount; i++) { \
+ bh = sb_getblk(sb, result + i); \
+ memset (bh->b_data, 0, sb->s_blocksize); \
+ mark_buffer_uptodate(bh, 1); \
+ mark_buffer_dirty (bh); \
+ if (IS_SYNC(inode)) { \
+ ll_rw_block (WRITE, 1, &bh); \
+ wait_on_buffer (bh); \
+ } \
+ brelse (bh); \
+ }
+
+unsigned ufs_new_fragments (struct inode * inode, u32 * p, unsigned fragment,
+ unsigned goal, unsigned count, int * err )
+{
+ struct super_block * sb;
+ struct ufs_sb_private_info * uspi;
+ struct ufs_super_block_first * usb1;
+ struct buffer_head * bh;
+ unsigned cgno, oldcount, newcount, tmp, request, i, result;
+
+ UFSD(("ENTER, ino %lu, fragment %u, goal %u, count %u\n", inode->i_ino, fragment, goal, count))
+
+ sb = inode->i_sb;
+ uspi = sb->u.ufs_sb.s_uspi;
+ usb1 = ubh_get_usb_first(USPI_UBH);
+ *err = -ENOSPC;
+
+ lock_super (sb);
+
+ tmp = fs32_to_cpu(sb, *p);
+ if (count + ufs_fragnum(fragment) > uspi->s_fpb) {
+ ufs_warning (sb, "ufs_new_fragments", "internal warning"
+ " fragment %u, count %u", fragment, count);
+ count = uspi->s_fpb - ufs_fragnum(fragment);
+ }
+ oldcount = ufs_fragnum (fragment);
+ newcount = oldcount + count;
+
+ /*
+ * Somebody else has just allocated our fragments
+ */
+ if (oldcount) {
+ if (!tmp) {
+ ufs_error (sb, "ufs_new_fragments", "internal error, "
+ "fragment %u, tmp %u\n", fragment, tmp);
+ unlock_super (sb);
+ return (unsigned)-1;
+ }
+ if (fragment < inode->u.ufs_i.i_lastfrag) {
+ UFSD(("EXIT (ALREADY ALLOCATED)\n"))
+ unlock_super (sb);
+ return 0;
+ }
+ }
+ else {
+ if (tmp) {
+ UFSD(("EXIT (ALREADY ALLOCATED)\n"))
+ unlock_super(sb);
+ return 0;
+ }
+ }
+
+ /*
+ * There is not enough space for user on the device
+ */
+ if (!capable(CAP_SYS_RESOURCE) && ufs_freespace(usb1, UFS_MINFREE) <= 0) {
+ unlock_super (sb);
+ UFSD(("EXIT (FAILED)\n"))
+ return 0;
+ }
+
+ if (goal >= uspi->s_size)
+ goal = 0;
+ if (goal == 0)
+ cgno = ufs_inotocg (inode->i_ino);
+ else
+ cgno = ufs_dtog (goal);
+
+ /*
+ * allocate new fragment
+ */
+ if (oldcount == 0) {
+ result = ufs_alloc_fragments (inode, cgno, goal, count, err);
+ if (result) {
+ *p = cpu_to_fs32(sb, result);
+ *err = 0;
+ inode->i_blocks += count << uspi->s_nspfshift;
+ inode->u.ufs_i.i_lastfrag = max_t(u32, inode->u.ufs_i.i_lastfrag, fragment + count);
+ NULLIFY_FRAGMENTS
+ }
+ unlock_super(sb);
+ UFSD(("EXIT, result %u\n", result))
+ return result;
+ }
+
+ /*
+ * resize block
+ */
+ result = ufs_add_fragments (inode, tmp, oldcount, newcount, err);
+ if (result) {
+ *err = 0;
+ inode->i_blocks += count << uspi->s_nspfshift;
+ inode->u.ufs_i.i_lastfrag = max_t(u32, inode->u.ufs_i.i_lastfrag, fragment + count);
+ NULLIFY_FRAGMENTS
+ unlock_super(sb);
+ UFSD(("EXIT, result %u\n", result))
+ return result;
+ }
+
+ /*
+ * allocate new block and move data
+ */
+ switch (fs32_to_cpu(sb, usb1->fs_optim)) {
+ case UFS_OPTSPACE:
+ request = newcount;
+ if (uspi->s_minfree < 5 || fs32_to_cpu(sb, usb1->fs_cstotal.cs_nffree)
+ > uspi->s_dsize * uspi->s_minfree / (2 * 100) )
+ break;
+ usb1->fs_optim = cpu_to_fs32(sb, UFS_OPTTIME);
+ break;
+ default:
+ usb1->fs_optim = cpu_to_fs32(sb, UFS_OPTTIME);
+
+ case UFS_OPTTIME:
+ request = uspi->s_fpb;
+ if (fs32_to_cpu(sb, usb1->fs_cstotal.cs_nffree) < uspi->s_dsize *
+ (uspi->s_minfree - 2) / 100)
+ break;
+ usb1->fs_optim = cpu_to_fs32(sb, UFS_OPTTIME);
+ break;
+ }
+ result = ufs_alloc_fragments (inode, cgno, goal, request, err);
+ if (result) {
+ for (i = 0; i < oldcount; i++) {
+ bh = sb_bread(sb, tmp + i);
+ if(bh)
+ {
+ mark_buffer_clean (bh);
+ bh->b_blocknr = result + i;
+ mark_buffer_dirty (bh);
+ if (IS_SYNC(inode)) {
+ ll_rw_block (WRITE, 1, &bh);
+ wait_on_buffer (bh);
+ }
+ brelse (bh);
+ }
+ else
+ {
+ printk(KERN_ERR "ufs_new_fragments: bread fail\n");
+ return 0;
+ }
+ }
+ *p = cpu_to_fs32(sb, result);
+ *err = 0;
+ inode->i_blocks += count << uspi->s_nspfshift;
+ inode->u.ufs_i.i_lastfrag = max_t(u32, inode->u.ufs_i.i_lastfrag, fragment + count);
+ NULLIFY_FRAGMENTS
+ unlock_super(sb);
+ if (newcount < request)
+ ufs_free_fragments (inode, result + newcount, request - newcount);
+ ufs_free_fragments (inode, tmp, oldcount);
+ UFSD(("EXIT, result %u\n", result))
+ return result;
+ }
+
+ unlock_super(sb);
+ UFSD(("EXIT (FAILED)\n"))
+ return 0;
+}
+
+unsigned ufs_add_fragments (struct inode * inode, unsigned fragment,
+ unsigned oldcount, unsigned newcount, int * err)
+{
+ struct super_block * sb;
+ struct ufs_sb_private_info * uspi;
+ struct ufs_super_block_first * usb1;
+ struct ufs_cg_private_info * ucpi;
+ struct ufs_cylinder_group * ucg;
+ unsigned cgno, fragno, fragoff, count, fragsize, i;
+
+ UFSD(("ENTER, fragment %u, oldcount %u, newcount %u\n", fragment, oldcount, newcount))
+
+ sb = inode->i_sb;
+ uspi = sb->u.ufs_sb.s_uspi;
+ usb1 = ubh_get_usb_first (USPI_UBH);
+ count = newcount - oldcount;
+
+ cgno = ufs_dtog(fragment);
+ if (sb->fs_cs(cgno).cs_nffree < count)
+ return 0;
+ if ((ufs_fragnum (fragment) + newcount) > uspi->s_fpb)
+ return 0;
+ ucpi = ufs_load_cylinder (sb, cgno);
+ if (!ucpi)
+ return 0;
+ ucg = ubh_get_ucg (UCPI_UBH);
+ if (!ufs_cg_chkmagic(sb, ucg)) {
+ ufs_panic (sb, "ufs_add_fragments",
+ "internal error, bad magic number on cg %u", cgno);
+ return 0;
+ }
+
+ fragno = ufs_dtogd (fragment);
+ fragoff = ufs_fragnum (fragno);
+ for (i = oldcount; i < newcount; i++)
+ if (ubh_isclr (UCPI_UBH, ucpi->c_freeoff, fragno + i))
+ return 0;
+ /*
+ * Block can be extended
+ */
+ ucg->cg_time = cpu_to_fs32(sb, CURRENT_TIME);
+ for (i = newcount; i < (uspi->s_fpb - fragoff); i++)
+ if (ubh_isclr (UCPI_UBH, ucpi->c_freeoff, fragno + i))
+ break;
+ fragsize = i - oldcount;
+ if (!fs32_to_cpu(sb, ucg->cg_frsum[fragsize]))
+ ufs_panic (sb, "ufs_add_fragments",
+ "internal error or corrupted bitmap on cg %u", cgno);
+ fs32_sub(sb, &ucg->cg_frsum[fragsize], 1);
+ if (fragsize != count)
+ fs32_add(sb, &ucg->cg_frsum[fragsize - count], 1);
+ for (i = oldcount; i < newcount; i++)
+ ubh_clrbit (UCPI_UBH, ucpi->c_freeoff, fragno + i);
+ if(DQUOT_ALLOC_BLOCK(inode, count)) {
+ *err = -EDQUOT;
+ return 0;
+ }
+
+ fs32_sub(sb, &ucg->cg_cs.cs_nffree, count);
+ fs32_sub(sb, &sb->fs_cs(cgno).cs_nffree, count);
+ fs32_sub(sb, &usb1->fs_cstotal.cs_nffree, count);
+
+ ubh_mark_buffer_dirty (USPI_UBH);
+ ubh_mark_buffer_dirty (UCPI_UBH);
+ if (sb->s_flags & MS_SYNCHRONOUS) {
+ ubh_ll_rw_block (WRITE, 1, (struct ufs_buffer_head **)&ucpi);
+ ubh_wait_on_buffer (UCPI_UBH);
+ }
+ sb->s_dirt = 1;
+
+ UFSD(("EXIT, fragment %u\n", fragment))
+
+ return fragment;
+}
+
+#define UFS_TEST_FREE_SPACE_CG \
+ ucg = (struct ufs_cylinder_group *) sb->u.ufs_sb.s_ucg[cgno]->b_data; \
+ if (fs32_to_cpu(sb, ucg->cg_cs.cs_nbfree)) \
+ goto cg_found; \
+ for (k = count; k < uspi->s_fpb; k++) \
+ if (fs32_to_cpu(sb, ucg->cg_frsum[k])) \
+ goto cg_found;
+
+unsigned ufs_alloc_fragments (struct inode * inode, unsigned cgno,
+ unsigned goal, unsigned count, int * err)
+{
+ struct super_block * sb;
+ struct ufs_sb_private_info * uspi;
+ struct ufs_super_block_first * usb1;
+ struct ufs_cg_private_info * ucpi;
+ struct ufs_cylinder_group * ucg;
+ unsigned oldcg, i, j, k, result, allocsize;
+
+ UFSD(("ENTER, ino %lu, cgno %u, goal %u, count %u\n", inode->i_ino, cgno, goal, count))
+
+ sb = inode->i_sb;
+ uspi = sb->u.ufs_sb.s_uspi;
+ usb1 = ubh_get_usb_first(USPI_UBH);
+ oldcg = cgno;
+
+ /*
+ * 1. searching on preferred cylinder group
+ */
+ UFS_TEST_FREE_SPACE_CG
+
+ /*
+ * 2. quadratic rehash
+ */
+ for (j = 1; j < uspi->s_ncg; j *= 2) {
+ cgno += j;
+ if (cgno >= uspi->s_ncg)
+ cgno -= uspi->s_ncg;
+ UFS_TEST_FREE_SPACE_CG
+ }
+
+ /*
+ * 3. brute force search
+ * We start at i = 2 ( 0 is checked at 1.step, 1 at 2.step )
+ */
+ cgno = (oldcg + 1) % uspi->s_ncg;
+ for (j = 2; j < uspi->s_ncg; j++) {
+ cgno++;
+ if (cgno >= uspi->s_ncg)
+ cgno = 0;
+ UFS_TEST_FREE_SPACE_CG
+ }
+
+ UFSD(("EXIT (FAILED)\n"))
+ return 0;
+
+cg_found:
+ ucpi = ufs_load_cylinder (sb, cgno);
+ if (!ucpi)
+ return 0;
+ ucg = ubh_get_ucg (UCPI_UBH);
+ if (!ufs_cg_chkmagic(sb, ucg))
+ ufs_panic (sb, "ufs_alloc_fragments",
+ "internal error, bad magic number on cg %u", cgno);
+ ucg->cg_time = cpu_to_fs32(sb, CURRENT_TIME);
+
+ if (count == uspi->s_fpb) {
+ result = ufs_alloccg_block (inode, ucpi, goal, err);
+ if (result == (unsigned)-1)
+ return 0;
+ goto succed;
+ }
+
+ for (allocsize = count; allocsize < uspi->s_fpb; allocsize++)
+ if (fs32_to_cpu(sb, ucg->cg_frsum[allocsize]) != 0)
+ break;
+
+ if (allocsize == uspi->s_fpb) {
+ result = ufs_alloccg_block (inode, ucpi, goal, err);
+ if (result == (unsigned)-1)
+ return 0;
+ goal = ufs_dtogd (result);
+ for (i = count; i < uspi->s_fpb; i++)
+ ubh_setbit (UCPI_UBH, ucpi->c_freeoff, goal + i);
+ i = uspi->s_fpb - count;
+ DQUOT_FREE_BLOCK(inode, i);
+
+ fs32_add(sb, &ucg->cg_cs.cs_nffree, i);
+ fs32_add(sb, &usb1->fs_cstotal.cs_nffree, i);
+ fs32_add(sb, &sb->fs_cs(cgno).cs_nffree, i);
+ fs32_add(sb, &ucg->cg_frsum[i], 1);
+ goto succed;
+ }
+
+ result = ufs_bitmap_search (sb, ucpi, goal, allocsize);
+ if (result == (unsigned)-1)
+ return 0;
+ if(DQUOT_ALLOC_BLOCK(inode, count)) {
+ *err = -EDQUOT;
+ return 0;
+ }
+ for (i = 0; i < count; i++)
+ ubh_clrbit (UCPI_UBH, ucpi->c_freeoff, result + i);
+
+ fs32_sub(sb, &ucg->cg_cs.cs_nffree, count);
+ fs32_sub(sb, &usb1->fs_cstotal.cs_nffree, count);
+ fs32_sub(sb, &sb->fs_cs(cgno).cs_nffree, count);
+ fs32_sub(sb, &ucg->cg_frsum[allocsize], 1);
+
+ if (count != allocsize)
+ fs32_add(sb, &ucg->cg_frsum[allocsize - count], 1);
+
+succed:
+ ubh_mark_buffer_dirty (USPI_UBH);
+ ubh_mark_buffer_dirty (UCPI_UBH);
+ if (sb->s_flags & MS_SYNCHRONOUS) {
+ ubh_ll_rw_block (WRITE, 1, (struct ufs_buffer_head **)&ucpi);
+ ubh_wait_on_buffer (UCPI_UBH);
+ }
+ sb->s_dirt = 1;
+
+ result += cgno * uspi->s_fpg;
+ UFSD(("EXIT3, result %u\n", result))
+ return result;
+}
+
+unsigned ufs_alloccg_block (struct inode * inode,
+ struct ufs_cg_private_info * ucpi, unsigned goal, int * err)
+{
+ struct super_block * sb;
+ struct ufs_sb_private_info * uspi;
+ struct ufs_super_block_first * usb1;
+ struct ufs_cylinder_group * ucg;
+ unsigned result, cylno, blkno;
+
+ UFSD(("ENTER, goal %u\n", goal))
+
+ sb = inode->i_sb;
+ uspi = sb->u.ufs_sb.s_uspi;
+ usb1 = ubh_get_usb_first(USPI_UBH);
+ ucg = ubh_get_ucg(UCPI_UBH);
+
+ if (goal == 0) {
+ goal = ucpi->c_rotor;
+ goto norot;
+ }
+ goal = ufs_blknum (goal);
+ goal = ufs_dtogd (goal);
+
+ /*
+ * If the requested block is available, use it.
+ */
+ if (ubh_isblockset(UCPI_UBH, ucpi->c_freeoff, ufs_fragstoblks(goal))) {
+ result = goal;
+ goto gotit;
+ }
+
+norot:
+ result = ufs_bitmap_search (sb, ucpi, goal, uspi->s_fpb);
+ if (result == (unsigned)-1)
+ return (unsigned)-1;
+ ucpi->c_rotor = result;
+gotit:
+ blkno = ufs_fragstoblks(result);
+ ubh_clrblock (UCPI_UBH, ucpi->c_freeoff, blkno);
+ if ((sb->u.ufs_sb.s_flags & UFS_CG_MASK) == UFS_CG_44BSD)
+ ufs_clusteracct (sb, ucpi, blkno, -1);
+ if(DQUOT_ALLOC_BLOCK(inode, uspi->s_fpb)) {
+ *err = -EDQUOT;
+ return (unsigned)-1;
+ }
+
+ fs32_sub(sb, &ucg->cg_cs.cs_nbfree, 1);
+ fs32_sub(sb, &usb1->fs_cstotal.cs_nbfree, 1);
+ fs32_sub(sb, &sb->fs_cs(ucpi->c_cgx).cs_nbfree, 1);
+ cylno = ufs_cbtocylno(result);
+ fs16_sub(sb, &ubh_cg_blks(ucpi, cylno, ufs_cbtorpos(result)), 1);
+ fs32_sub(sb, &ubh_cg_blktot(ucpi, cylno), 1);
+
+ UFSD(("EXIT, result %u\n", result))
+
+ return result;
+}
+
+unsigned ufs_bitmap_search (struct super_block * sb,
+ struct ufs_cg_private_info * ucpi, unsigned goal, unsigned count)
+{
+ struct ufs_sb_private_info * uspi;
+ struct ufs_super_block_first * usb1;
+ struct ufs_cylinder_group * ucg;
+ unsigned start, length, location, result;
+ unsigned possition, fragsize, blockmap, mask;
+
+ UFSD(("ENTER, cg %u, goal %u, count %u\n", ucpi->c_cgx, goal, count))
+
+ uspi = sb->u.ufs_sb.s_uspi;
+ usb1 = ubh_get_usb_first (USPI_UBH);
+ ucg = ubh_get_ucg(UCPI_UBH);
+
+ if (goal)
+ start = ufs_dtogd(goal) >> 3;
+ else
+ start = ucpi->c_frotor >> 3;
+
+ length = ((uspi->s_fpg + 7) >> 3) - start;
+ location = ubh_scanc(UCPI_UBH, ucpi->c_freeoff + start, length,
+ (uspi->s_fpb == 8) ? ufs_fragtable_8fpb : ufs_fragtable_other,
+ 1 << (count - 1 + (uspi->s_fpb & 7)));
+ if (location == 0) {
+ length = start + 1;
+ location = ubh_scanc(UCPI_UBH, ucpi->c_freeoff, length,
+ (uspi->s_fpb == 8) ? ufs_fragtable_8fpb : ufs_fragtable_other,
+ 1 << (count - 1 + (uspi->s_fpb & 7)));
+ if (location == 0) {
+ ufs_error (sb, "ufs_bitmap_search",
+ "bitmap corrupted on cg %u, start %u, length %u, count %u, freeoff %u\n",
+ ucpi->c_cgx, start, length, count, ucpi->c_freeoff);
+ return (unsigned)-1;
+ }
+ start = 0;
+ }
+ result = (start + length - location) << 3;
+ ucpi->c_frotor = result;
+
+ /*
+ * found the byte in the map
+ */
+ blockmap = ubh_blkmap(UCPI_UBH, ucpi->c_freeoff, result);
+ fragsize = 0;
+ for (possition = 0, mask = 1; possition < 8; possition++, mask <<= 1) {
+ if (blockmap & mask) {
+ if (!(possition & uspi->s_fpbmask))
+ fragsize = 1;
+ else
+ fragsize++;
+ }
+ else {
+ if (fragsize == count) {
+ result += possition - count;
+ UFSD(("EXIT, result %u\n", result))
+ return result;
+ }
+ fragsize = 0;
+ }
+ }
+ if (fragsize == count) {
+ result += possition - count;
+ UFSD(("EXIT, result %u\n", result))
+ return result;
+ }
+ ufs_error (sb, "ufs_bitmap_search", "block not in map on cg %u\n", ucpi->c_cgx);
+ UFSD(("EXIT (FAILED)\n"))
+ return (unsigned)-1;
+}
+
+void ufs_clusteracct(struct super_block * sb,
+ struct ufs_cg_private_info * ucpi, unsigned blkno, int cnt)
+{
+ struct ufs_sb_private_info * uspi;
+ int i, start, end, forw, back;
+
+ uspi = sb->u.ufs_sb.s_uspi;
+ if (uspi->s_contigsumsize <= 0)
+ return;
+
+ if (cnt > 0)
+ ubh_setbit(UCPI_UBH, ucpi->c_clusteroff, blkno);
+ else
+ ubh_clrbit(UCPI_UBH, ucpi->c_clusteroff, blkno);
+
+ /*
+ * Find the size of the cluster going forward.
+ */
+ start = blkno + 1;
+ end = start + uspi->s_contigsumsize;
+ if ( end >= ucpi->c_nclusterblks)
+ end = ucpi->c_nclusterblks;
+ i = ubh_find_next_zero_bit (UCPI_UBH, ucpi->c_clusteroff, end, start);
+ if (i > end)
+ i = end;
+ forw = i - start;
+
+ /*
+ * Find the size of the cluster going backward.
+ */
+ start = blkno - 1;
+ end = start - uspi->s_contigsumsize;
+ if (end < 0 )
+ end = -1;
+ i = ubh_find_last_zero_bit (UCPI_UBH, ucpi->c_clusteroff, start, end);
+ if ( i < end)
+ i = end;
+ back = start - i;
+
+ /*
+ * Account for old cluster and the possibly new forward and
+ * back clusters.
+ */
+ i = back + forw + 1;
+ if (i > uspi->s_contigsumsize)
+ i = uspi->s_contigsumsize;
+ fs32_add(sb, (u32*)ubh_get_addr(UCPI_UBH, ucpi->c_clustersumoff + (i << 2)), cnt);
+ if (back > 0)
+ fs32_sub(sb, (u32*)ubh_get_addr(UCPI_UBH, ucpi->c_clustersumoff + (back << 2)), cnt);
+ if (forw > 0)
+ fs32_sub(sb, (u32*)ubh_get_addr(UCPI_UBH, ucpi->c_clustersumoff + (forw << 2)), cnt);
+}
+
+
+static unsigned char ufs_fragtable_8fpb[] = {
+ 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x04, 0x01, 0x01, 0x01, 0x03, 0x02, 0x03, 0x04, 0x08,
+ 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x02, 0x03, 0x03, 0x02, 0x04, 0x05, 0x08, 0x10,
+ 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09,
+ 0x02, 0x03, 0x03, 0x02, 0x03, 0x03, 0x02, 0x06, 0x04, 0x05, 0x05, 0x06, 0x08, 0x09, 0x10, 0x20,
+ 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09,
+ 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x03, 0x03, 0x03, 0x03, 0x05, 0x05, 0x09, 0x11,
+ 0x02, 0x03, 0x03, 0x02, 0x03, 0x03, 0x02, 0x06, 0x03, 0x03, 0x03, 0x03, 0x02, 0x03, 0x06, 0x0A,
+ 0x04, 0x05, 0x05, 0x06, 0x05, 0x05, 0x06, 0x04, 0x08, 0x09, 0x09, 0x0A, 0x10, 0x11, 0x20, 0x40,
+ 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09,
+ 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x03, 0x03, 0x03, 0x03, 0x05, 0x05, 0x09, 0x11,
+ 0x01, 0x01, 0x01, 0x03, 0x01, 0x01, 0x03, 0x05, 0x01, 0x01, 0x01, 0x03, 0x03, 0x03, 0x05, 0x09,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x07, 0x05, 0x05, 0x05, 0x07, 0x09, 0x09, 0x11, 0x21,
+ 0x02, 0x03, 0x03, 0x02, 0x03, 0x03, 0x02, 0x06, 0x03, 0x03, 0x03, 0x03, 0x02, 0x03, 0x06, 0x0A,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x07, 0x02, 0x03, 0x03, 0x02, 0x06, 0x07, 0x0A, 0x12,
+ 0x04, 0x05, 0x05, 0x06, 0x05, 0x05, 0x06, 0x04, 0x05, 0x05, 0x05, 0x07, 0x06, 0x07, 0x04, 0x0C,
+ 0x08, 0x09, 0x09, 0x0A, 0x09, 0x09, 0x0A, 0x0C, 0x10, 0x11, 0x11, 0x12, 0x20, 0x21, 0x40, 0x80,
+};
+
+static unsigned char ufs_fragtable_other[] = {
+ 0x00, 0x16, 0x16, 0x2A, 0x16, 0x16, 0x26, 0x4E, 0x16, 0x16, 0x16, 0x3E, 0x2A, 0x3E, 0x4E, 0x8A,
+ 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
+ 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
+ 0x2A, 0x3E, 0x3E, 0x2A, 0x3E, 0x3E, 0x2E, 0x6E, 0x3E, 0x3E, 0x3E, 0x3E, 0x2A, 0x3E, 0x6E, 0xAA,
+ 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
+ 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
+ 0x26, 0x36, 0x36, 0x2E, 0x36, 0x36, 0x26, 0x6E, 0x36, 0x36, 0x36, 0x3E, 0x2E, 0x3E, 0x6E, 0xAE,
+ 0x4E, 0x5E, 0x5E, 0x6E, 0x5E, 0x5E, 0x6E, 0x4E, 0x5E, 0x5E, 0x5E, 0x7E, 0x6E, 0x7E, 0x4E, 0xCE,
+ 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
+ 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
+ 0x16, 0x16, 0x16, 0x3E, 0x16, 0x16, 0x36, 0x5E, 0x16, 0x16, 0x16, 0x3E, 0x3E, 0x3E, 0x5E, 0x9E,
+ 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x7E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x7E, 0xBE,
+ 0x2A, 0x3E, 0x3E, 0x2A, 0x3E, 0x3E, 0x2E, 0x6E, 0x3E, 0x3E, 0x3E, 0x3E, 0x2A, 0x3E, 0x6E, 0xAA,
+ 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x7E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x7E, 0xBE,
+ 0x4E, 0x5E, 0x5E, 0x6E, 0x5E, 0x5E, 0x6E, 0x4E, 0x5E, 0x5E, 0x5E, 0x7E, 0x6E, 0x7E, 0x4E, 0xCE,
+ 0x8A, 0x9E, 0x9E, 0xAA, 0x9E, 0x9E, 0xAE, 0xCE, 0x9E, 0x9E, 0x9E, 0xBE, 0xAA, 0xBE, 0xCE, 0x8A,
+};
diff --git a/fs/ufs/cylinder.c b/fs/ufs/cylinder.c
new file mode 100644
index 00000000000000..97391b4d66c02b
--- /dev/null
+++ b/fs/ufs/cylinder.c
@@ -0,0 +1,207 @@
+/*
+ * linux/fs/ufs/cylinder.c
+ *
+ * Copyright (C) 1998
+ * Daniel Pirkl <daniel.pirkl@email.cz>
+ * Charles University, Faculty of Mathematics and Physics
+ *
+ * ext2 - inode (block) bitmap caching inspired
+ */
+
+#include <linux/fs.h>
+#include <linux/ufs_fs.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+
+#include <asm/bitops.h>
+#include <asm/byteorder.h>
+
+#include "swab.h"
+#include "util.h"
+
+#undef UFS_CYLINDER_DEBUG
+
+#ifdef UFS_CYLINDER_DEBUG
+#define UFSD(x) printk("(%s, %d), %s:", __FILE__, __LINE__, __FUNCTION__); printk x;
+#else
+#define UFSD(x)
+#endif
+
+
+/*
+ * Read cylinder group into cache. The memory space for ufs_cg_private_info
+ * structure is already allocated during ufs_read_super.
+ */
+static void ufs_read_cylinder (struct super_block * sb,
+ unsigned cgno, unsigned bitmap_nr)
+{
+ struct ufs_sb_private_info * uspi;
+ struct ufs_cg_private_info * ucpi;
+ struct ufs_cylinder_group * ucg;
+ unsigned i, j;
+
+ UFSD(("ENTER, cgno %u, bitmap_nr %u\n", cgno, bitmap_nr))
+ uspi = sb->u.ufs_sb.s_uspi;
+ ucpi = sb->u.ufs_sb.s_ucpi[bitmap_nr];
+ ucg = (struct ufs_cylinder_group *)sb->u.ufs_sb.s_ucg[cgno]->b_data;
+
+ UCPI_UBH->fragment = ufs_cgcmin(cgno);
+ UCPI_UBH->count = uspi->s_cgsize >> sb->s_blocksize_bits;
+ /*
+ * We have already the first fragment of cylinder group block in buffer
+ */
+ UCPI_UBH->bh[0] = sb->u.ufs_sb.s_ucg[cgno];
+ for (i = 1; i < UCPI_UBH->count; i++)
+ if (!(UCPI_UBH->bh[i] = sb_bread(sb, UCPI_UBH->fragment + i)))
+ goto failed;
+ sb->u.ufs_sb.s_cgno[bitmap_nr] = cgno;
+
+ ucpi->c_cgx = fs32_to_cpu(sb, ucg->cg_cgx);
+ ucpi->c_ncyl = fs16_to_cpu(sb, ucg->cg_ncyl);
+ ucpi->c_niblk = fs16_to_cpu(sb, ucg->cg_niblk);
+ ucpi->c_ndblk = fs32_to_cpu(sb, ucg->cg_ndblk);
+ ucpi->c_rotor = fs32_to_cpu(sb, ucg->cg_rotor);
+ ucpi->c_frotor = fs32_to_cpu(sb, ucg->cg_frotor);
+ ucpi->c_irotor = fs32_to_cpu(sb, ucg->cg_irotor);
+ ucpi->c_btotoff = fs32_to_cpu(sb, ucg->cg_btotoff);
+ ucpi->c_boff = fs32_to_cpu(sb, ucg->cg_boff);
+ ucpi->c_iusedoff = fs32_to_cpu(sb, ucg->cg_iusedoff);
+ ucpi->c_freeoff = fs32_to_cpu(sb, ucg->cg_freeoff);
+ ucpi->c_nextfreeoff = fs32_to_cpu(sb, ucg->cg_nextfreeoff);
+ ucpi->c_clustersumoff = fs32_to_cpu(sb, ucg->cg_u.cg_44.cg_clustersumoff);
+ ucpi->c_clusteroff = fs32_to_cpu(sb, ucg->cg_u.cg_44.cg_clusteroff);
+ ucpi->c_nclusterblks = fs32_to_cpu(sb, ucg->cg_u.cg_44.cg_nclusterblks);
+ UFSD(("EXIT\n"))
+ return;
+
+failed:
+ for (j = 1; j < i; j++)
+ brelse (sb->u.ufs_sb.s_ucg[j]);
+ sb->u.ufs_sb.s_cgno[bitmap_nr] = UFS_CGNO_EMPTY;
+ ufs_error (sb, "ufs_read_cylinder", "can't read cylinder group block %u", cgno);
+}
+
+/*
+ * Remove cylinder group from cache, doesn't release memory
+ * allocated for cylinder group (this is done at ufs_put_super only).
+ */
+void ufs_put_cylinder (struct super_block * sb, unsigned bitmap_nr)
+{
+ struct ufs_sb_private_info * uspi;
+ struct ufs_cg_private_info * ucpi;
+ struct ufs_cylinder_group * ucg;
+ unsigned i;
+
+ UFSD(("ENTER, bitmap_nr %u\n", bitmap_nr))
+
+ uspi = sb->u.ufs_sb.s_uspi;
+ if (sb->u.ufs_sb.s_cgno[bitmap_nr] == UFS_CGNO_EMPTY) {
+ UFSD(("EXIT\n"))
+ return;
+ }
+ ucpi = sb->u.ufs_sb.s_ucpi[bitmap_nr];
+ ucg = ubh_get_ucg(UCPI_UBH);
+
+ if (uspi->s_ncg > UFS_MAX_GROUP_LOADED && bitmap_nr >= sb->u.ufs_sb.s_cg_loaded) {
+ ufs_panic (sb, "ufs_put_cylinder", "internal error");
+ return;
+ }
+ /*
+ * rotor is not so important data, so we put it to disk
+ * at the end of working with cylinder
+ */
+ ucg->cg_rotor = cpu_to_fs32(sb, ucpi->c_rotor);
+ ucg->cg_frotor = cpu_to_fs32(sb, ucpi->c_frotor);
+ ucg->cg_irotor = cpu_to_fs32(sb, ucpi->c_irotor);
+ ubh_mark_buffer_dirty (UCPI_UBH);
+ for (i = 1; i < UCPI_UBH->count; i++) {
+ brelse (UCPI_UBH->bh[i]);
+ }
+
+ sb->u.ufs_sb.s_cgno[bitmap_nr] = UFS_CGNO_EMPTY;
+ UFSD(("EXIT\n"))
+}
+
+/*
+ * Find cylinder group in cache and return it as pointer.
+ * If cylinder group is not in cache, we will load it from disk.
+ *
+ * The cache is managed by LRU algorithm.
+ */
+struct ufs_cg_private_info * ufs_load_cylinder (
+ struct super_block * sb, unsigned cgno)
+{
+ struct ufs_sb_private_info * uspi;
+ struct ufs_cg_private_info * ucpi;
+ unsigned cg, i, j;
+
+ UFSD(("ENTER, cgno %u\n", cgno))
+
+ uspi = sb->u.ufs_sb.s_uspi;
+ if (cgno >= uspi->s_ncg) {
+ ufs_panic (sb, "ufs_load_cylinder", "internal error, high number of cg");
+ return NULL;
+ }
+ /*
+ * Cylinder group number cg it in cache and it was last used
+ */
+ if (sb->u.ufs_sb.s_cgno[0] == cgno) {
+ UFSD(("EXIT\n"))
+ return sb->u.ufs_sb.s_ucpi[0];
+ }
+ /*
+ * Number of cylinder groups is not higher than UFS_MAX_GROUP_LOADED
+ */
+ if (uspi->s_ncg <= UFS_MAX_GROUP_LOADED) {
+ if (sb->u.ufs_sb.s_cgno[cgno] != UFS_CGNO_EMPTY) {
+ if (sb->u.ufs_sb.s_cgno[cgno] != cgno) {
+ ufs_panic (sb, "ufs_load_cylinder", "internal error, wrong number of cg in cache");
+ UFSD(("EXIT (FAILED)\n"))
+ return NULL;
+ }
+ else {
+ UFSD(("EXIT\n"))
+ return sb->u.ufs_sb.s_ucpi[cgno];
+ }
+ } else {
+ ufs_read_cylinder (sb, cgno, cgno);
+ UFSD(("EXIT\n"))
+ return sb->u.ufs_sb.s_ucpi[cgno];
+ }
+ }
+ /*
+ * Cylinder group number cg is in cache but it was not last used,
+ * we will move to the first position
+ */
+ for (i = 0; i < sb->u.ufs_sb.s_cg_loaded && sb->u.ufs_sb.s_cgno[i] != cgno; i++);
+ if (i < sb->u.ufs_sb.s_cg_loaded && sb->u.ufs_sb.s_cgno[i] == cgno) {
+ cg = sb->u.ufs_sb.s_cgno[i];
+ ucpi = sb->u.ufs_sb.s_ucpi[i];
+ for (j = i; j > 0; j--) {
+ sb->u.ufs_sb.s_cgno[j] = sb->u.ufs_sb.s_cgno[j-1];
+ sb->u.ufs_sb.s_ucpi[j] = sb->u.ufs_sb.s_ucpi[j-1];
+ }
+ sb->u.ufs_sb.s_cgno[0] = cg;
+ sb->u.ufs_sb.s_ucpi[0] = ucpi;
+ /*
+ * Cylinder group number cg is not in cache, we will read it from disk
+ * and put it to the first position
+ */
+ } else {
+ if (sb->u.ufs_sb.s_cg_loaded < UFS_MAX_GROUP_LOADED)
+ sb->u.ufs_sb.s_cg_loaded++;
+ else
+ ufs_put_cylinder (sb, UFS_MAX_GROUP_LOADED-1);
+ ucpi = sb->u.ufs_sb.s_ucpi[sb->u.ufs_sb.s_cg_loaded - 1];
+ for (j = sb->u.ufs_sb.s_cg_loaded - 1; j > 0; j--) {
+ sb->u.ufs_sb.s_cgno[j] = sb->u.ufs_sb.s_cgno[j-1];
+ sb->u.ufs_sb.s_ucpi[j] = sb->u.ufs_sb.s_ucpi[j-1];
+ }
+ sb->u.ufs_sb.s_ucpi[0] = ucpi;
+ ufs_read_cylinder (sb, cgno, 0);
+ }
+ UFSD(("EXIT\n"))
+ return sb->u.ufs_sb.s_ucpi[0];
+}
diff --git a/fs/ufs/dir.c b/fs/ufs/dir.c
new file mode 100644
index 00000000000000..3dca14b360e940
--- /dev/null
+++ b/fs/ufs/dir.c
@@ -0,0 +1,622 @@
+/*
+ * linux/fs/ufs/ufs_dir.c
+ *
+ * Copyright (C) 1996
+ * Adrian Rodriguez (adrian@franklins-tower.rutgers.edu)
+ * Laboratory for Computer Science Research Computing Facility
+ * Rutgers, The State University of New Jersey
+ *
+ * swab support by Francois-Rene Rideau <fare@tunes.org> 19970406
+ *
+ * 4.4BSD (FreeBSD) support added on February 1st 1998 by
+ * Niels Kristian Bech Jensen <nkbj@image.dk> partially based
+ * on code by Martin von Loewis <martin@mira.isdn.cs.tu-berlin.de>.
+ */
+
+#include <linux/sched.h>
+#include <linux/locks.h>
+#include <linux/fs.h>
+#include <linux/ufs_fs.h>
+
+#include "swab.h"
+#include "util.h"
+
+#undef UFS_DIR_DEBUG
+
+#ifdef UFS_DIR_DEBUG
+#define UFSD(x) printk("(%s, %d), %s: ", __FILE__, __LINE__, __FUNCTION__); printk x;
+#else
+#define UFSD(x)
+#endif
+
+
+
+/*
+ * NOTE! unlike strncmp, ufs_match returns 1 for success, 0 for failure.
+ *
+ * len <= UFS_MAXNAMLEN and de != NULL are guaranteed by caller.
+ */
+static inline int ufs_match(struct super_block *sb, int len,
+ const char * const name, struct ufs_dir_entry * de)
+{
+ if (len != ufs_get_de_namlen(sb, de))
+ return 0;
+ if (!de->d_ino)
+ return 0;
+ return !memcmp(name, de->d_name, len);
+}
+
+/*
+ * This is blatantly stolen from ext2fs
+ */
+static int
+ufs_readdir (struct file * filp, void * dirent, filldir_t filldir)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ int error = 0;
+ unsigned long offset, lblk, blk;
+ int i, stored;
+ struct buffer_head * bh;
+ struct ufs_dir_entry * de;
+ struct super_block * sb;
+ int de_reclen;
+ unsigned flags;
+
+ sb = inode->i_sb;
+ flags = sb->u.ufs_sb.s_flags;
+
+ UFSD(("ENTER, ino %lu f_pos %lu\n", inode->i_ino, (unsigned long) filp->f_pos))
+
+ stored = 0;
+ bh = NULL;
+ offset = filp->f_pos & (sb->s_blocksize - 1);
+
+ while (!error && !stored && filp->f_pos < inode->i_size) {
+ lblk = (filp->f_pos) >> sb->s_blocksize_bits;
+ blk = ufs_frag_map(inode, lblk);
+ if (!blk || !(bh = sb_bread(sb, blk))) {
+ /* XXX - error - skip to the next block */
+ printk("ufs_readdir: "
+ "dir inode %lu has a hole at offset %lu\n",
+ inode->i_ino, (unsigned long int)filp->f_pos);
+ filp->f_pos += sb->s_blocksize - offset;
+ continue;
+ }
+
+revalidate:
+ /* If the dir block has changed since the last call to
+ * readdir(2), then we might be pointing to an invalid
+ * dirent right now. Scan from the start of the block
+ * to make sure. */
+ if (filp->f_version != inode->i_version) {
+ for (i = 0; i < sb->s_blocksize && i < offset; ) {
+ de = (struct ufs_dir_entry *)(bh->b_data + i);
+ /* It's too expensive to do a full
+ * dirent test each time round this
+ * loop, but we do have to test at
+ * least that it is non-zero. A
+ * failure will be detected in the
+ * dirent test below. */
+ de_reclen = fs16_to_cpu(sb, de->d_reclen);
+ if (de_reclen < 1)
+ break;
+ i += de_reclen;
+ }
+ offset = i;
+ filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1))
+ | offset;
+ filp->f_version = inode->i_version;
+ }
+
+ while (!error && filp->f_pos < inode->i_size
+ && offset < sb->s_blocksize) {
+ de = (struct ufs_dir_entry *) (bh->b_data + offset);
+ /* XXX - put in a real ufs_check_dir_entry() */
+ if ((de->d_reclen == 0) || (ufs_get_de_namlen(sb, de) == 0)) {
+ filp->f_pos = (filp->f_pos &
+ (sb->s_blocksize - 1)) +
+ sb->s_blocksize;
+ brelse(bh);
+ return stored;
+ }
+ if (!ufs_check_dir_entry ("ufs_readdir", inode, de,
+ bh, offset)) {
+ /* On error, skip the f_pos to the
+ next block. */
+ filp->f_pos = (filp->f_pos |
+ (sb->s_blocksize - 1)) +
+ 1;
+ brelse (bh);
+ return stored;
+ }
+ offset += fs16_to_cpu(sb, de->d_reclen);
+ if (de->d_ino) {
+ /* We might block in the next section
+ * if the data destination is
+ * currently swapped out. So, use a
+ * version stamp to detect whether or
+ * not the directory has been modified
+ * during the copy operation. */
+ unsigned long version = filp->f_version;
+ unsigned char d_type = DT_UNKNOWN;
+
+ UFSD(("filldir(%s,%u)\n", de->d_name,
+ fs32_to_cpu(sb, de->d_ino)))
+ UFSD(("namlen %u\n", ufs_get_de_namlen(sb, de)))
+
+ if ((flags & UFS_DE_MASK) == UFS_DE_44BSD)
+ d_type = de->d_u.d_44.d_type;
+ error = filldir(dirent, de->d_name,
+ ufs_get_de_namlen(sb, de), filp->f_pos,
+ fs32_to_cpu(sb, de->d_ino), d_type);
+ if (error)
+ break;
+ if (version != filp->f_version)
+ goto revalidate;
+ stored ++;
+ }
+ filp->f_pos += fs16_to_cpu(sb, de->d_reclen);
+ }
+ offset = 0;
+ brelse (bh);
+ }
+ UPDATE_ATIME(inode);
+ return 0;
+}
+
+/*
+ * define how far ahead to read directories while searching them.
+ */
+#define NAMEI_RA_CHUNKS 2
+#define NAMEI_RA_BLOCKS 4
+#define NAMEI_RA_SIZE (NAMEI_RA_CHUNKS * NAMEI_RA_BLOCKS)
+#define NAMEI_RA_INDEX(c,b) (((c) * NAMEI_RA_BLOCKS) + (b))
+
+/*
+ * ufs_find_entry()
+ *
+ * finds an entry in the specified directory with the wanted name. It
+ * returns the cache buffer in which the entry was found, and the entry
+ * itself (as a parameter - res_bh). It does NOT read the inode of the
+ * entry - you'll have to do that yourself if you want to.
+ */
+struct ufs_dir_entry * ufs_find_entry (struct dentry *dentry,
+ struct buffer_head ** res_bh)
+{
+ struct super_block * sb;
+ struct buffer_head * bh_use[NAMEI_RA_SIZE];
+ struct buffer_head * bh_read[NAMEI_RA_SIZE];
+ unsigned long offset;
+ int block, toread, i, err;
+ struct inode *dir = dentry->d_parent->d_inode;
+ const char *name = dentry->d_name.name;
+ int namelen = dentry->d_name.len;
+
+ UFSD(("ENTER, dir_ino %lu, name %s, namlen %u\n", dir->i_ino, name, namelen))
+
+ *res_bh = NULL;
+
+ sb = dir->i_sb;
+
+ if (namelen > UFS_MAXNAMLEN)
+ return NULL;
+
+ memset (bh_use, 0, sizeof (bh_use));
+ toread = 0;
+ for (block = 0; block < NAMEI_RA_SIZE; ++block) {
+ struct buffer_head * bh;
+
+ if ((block << sb->s_blocksize_bits) >= dir->i_size)
+ break;
+ bh = ufs_getfrag (dir, block, 0, &err);
+ bh_use[block] = bh;
+ if (bh && !buffer_uptodate(bh))
+ bh_read[toread++] = bh;
+ }
+
+ for (block = 0, offset = 0; offset < dir->i_size; block++) {
+ struct buffer_head * bh;
+ struct ufs_dir_entry * de;
+ char * dlimit;
+
+ if ((block % NAMEI_RA_BLOCKS) == 0 && toread) {
+ ll_rw_block (READ, toread, bh_read);
+ toread = 0;
+ }
+ bh = bh_use[block % NAMEI_RA_SIZE];
+ if (!bh) {
+ ufs_error (sb, "ufs_find_entry",
+ "directory #%lu contains a hole at offset %lu",
+ dir->i_ino, offset);
+ offset += sb->s_blocksize;
+ continue;
+ }
+ wait_on_buffer (bh);
+ if (!buffer_uptodate(bh)) {
+ /*
+ * read error: all bets are off
+ */
+ break;
+ }
+
+ de = (struct ufs_dir_entry *) bh->b_data;
+ dlimit = bh->b_data + sb->s_blocksize;
+ while ((char *) de < dlimit && offset < dir->i_size) {
+ /* this code is executed quadratically often */
+ /* do minimal checking by hand */
+ int de_len;
+
+ if ((char *) de + namelen <= dlimit &&
+ ufs_match(sb, namelen, name, de)) {
+ /* found a match -
+ just to be sure, do a full check */
+ if (!ufs_check_dir_entry("ufs_find_entry",
+ dir, de, bh, offset))
+ goto failed;
+ for (i = 0; i < NAMEI_RA_SIZE; ++i) {
+ if (bh_use[i] != bh)
+ brelse (bh_use[i]);
+ }
+ *res_bh = bh;
+ return de;
+ }
+ /* prevent looping on a bad block */
+ de_len = fs16_to_cpu(sb, de->d_reclen);
+ if (de_len <= 0)
+ goto failed;
+ offset += de_len;
+ de = (struct ufs_dir_entry *) ((char *) de + de_len);
+ }
+
+ brelse (bh);
+ if (((block + NAMEI_RA_SIZE) << sb->s_blocksize_bits ) >=
+ dir->i_size)
+ bh = NULL;
+ else
+ bh = ufs_getfrag (dir, block + NAMEI_RA_SIZE, 0, &err);
+ bh_use[block % NAMEI_RA_SIZE] = bh;
+ if (bh && !buffer_uptodate(bh))
+ bh_read[toread++] = bh;
+ }
+
+failed:
+ for (i = 0; i < NAMEI_RA_SIZE; ++i) brelse (bh_use[i]);
+ UFSD(("EXIT\n"))
+ return NULL;
+}
+
+int ufs_check_dir_entry (const char * function, struct inode * dir,
+ struct ufs_dir_entry * de, struct buffer_head * bh,
+ unsigned long offset)
+{
+ struct super_block *sb = dir->i_sb;
+ const char *error_msg = NULL;
+ int rlen = fs16_to_cpu(sb, de->d_reclen);
+
+ if (rlen < UFS_DIR_REC_LEN(1))
+ error_msg = "reclen is smaller than minimal";
+ else if (rlen % 4 != 0)
+ error_msg = "reclen % 4 != 0";
+ else if (rlen < UFS_DIR_REC_LEN(ufs_get_de_namlen(sb, de)))
+ error_msg = "reclen is too small for namlen";
+ else if (((char *) de - bh->b_data) + rlen > dir->i_sb->s_blocksize)
+ error_msg = "directory entry across blocks";
+ else if (fs32_to_cpu(sb, de->d_ino) > (sb->u.ufs_sb.s_uspi->s_ipg *
+ sb->u.ufs_sb.s_uspi->s_ncg))
+ error_msg = "inode out of bounds";
+
+ if (error_msg != NULL)
+ ufs_error (sb, function, "bad entry in directory #%lu, size %Lu: %s - "
+ "offset=%lu, inode=%lu, reclen=%d, namlen=%d",
+ dir->i_ino, dir->i_size, error_msg, offset,
+ (unsigned long)fs32_to_cpu(sb, de->d_ino),
+ rlen, ufs_get_de_namlen(sb, de));
+
+ return (error_msg == NULL ? 1 : 0);
+}
+
+struct ufs_dir_entry *ufs_dotdot(struct inode *dir, struct buffer_head **p)
+{
+ int err;
+ struct buffer_head *bh = ufs_bread (dir, 0, 0, &err);
+ struct ufs_dir_entry *res = NULL;
+
+ if (bh) {
+ res = (struct ufs_dir_entry *) bh->b_data;
+ res = (struct ufs_dir_entry *)((char *)res +
+ fs16_to_cpu(dir->i_sb, res->d_reclen));
+ }
+ *p = bh;
+ return res;
+}
+ino_t ufs_inode_by_name(struct inode * dir, struct dentry *dentry)
+{
+ ino_t res = 0;
+ struct ufs_dir_entry * de;
+ struct buffer_head *bh;
+
+ de = ufs_find_entry (dentry, &bh);
+ if (de) {
+ res = fs32_to_cpu(dir->i_sb, de->d_ino);
+ brelse(bh);
+ }
+ return res;
+}
+
+void ufs_set_link(struct inode *dir, struct ufs_dir_entry *de,
+ struct buffer_head *bh, struct inode *inode)
+{
+ dir->i_version = ++event;
+ de->d_ino = cpu_to_fs32(dir->i_sb, inode->i_ino);
+ mark_buffer_dirty(bh);
+ if (IS_SYNC(dir)) {
+ ll_rw_block (WRITE, 1, &bh);
+ wait_on_buffer(bh);
+ }
+ brelse (bh);
+}
+
+/*
+ * ufs_add_entry()
+ *
+ * adds a file entry to the specified directory, using the same
+ * semantics as ufs_find_entry(). It returns NULL if it failed.
+ */
+int ufs_add_link(struct dentry *dentry, struct inode *inode)
+{
+ struct super_block * sb;
+ struct ufs_sb_private_info * uspi;
+ unsigned long offset;
+ unsigned fragoff;
+ unsigned short rec_len;
+ struct buffer_head * bh;
+ struct ufs_dir_entry * de, * de1;
+ struct inode *dir = dentry->d_parent->d_inode;
+ const char *name = dentry->d_name.name;
+ int namelen = dentry->d_name.len;
+ int err;
+
+ UFSD(("ENTER, name %s, namelen %u\n", name, namelen))
+
+ sb = dir->i_sb;
+ uspi = sb->u.ufs_sb.s_uspi;
+
+ if (!namelen)
+ return -EINVAL;
+ bh = ufs_bread (dir, 0, 0, &err);
+ if (!bh)
+ return err;
+ rec_len = UFS_DIR_REC_LEN(namelen);
+ offset = 0;
+ de = (struct ufs_dir_entry *) bh->b_data;
+ while (1) {
+ if ((char *)de >= UFS_SECTOR_SIZE + bh->b_data) {
+ fragoff = offset & ~uspi->s_fmask;
+ if (fragoff != 0 && fragoff != UFS_SECTOR_SIZE)
+ ufs_error (sb, "ufs_add_entry", "internal error"
+ " fragoff %u", fragoff);
+ if (!fragoff) {
+ brelse (bh);
+ bh = ufs_bread (dir, offset >> sb->s_blocksize_bits, 1, &err);
+ if (!bh)
+ return err;
+ }
+ if (dir->i_size <= offset) {
+ if (dir->i_size == 0) {
+ brelse(bh);
+ return -ENOENT;
+ }
+ de = (struct ufs_dir_entry *) (bh->b_data + fragoff);
+ de->d_ino = 0;
+ de->d_reclen = cpu_to_fs16(sb, UFS_SECTOR_SIZE);
+ ufs_set_de_namlen(sb, de, 0);
+ dir->i_size = offset + UFS_SECTOR_SIZE;
+ mark_inode_dirty(dir);
+ } else {
+ de = (struct ufs_dir_entry *) bh->b_data;
+ }
+ }
+ if (!ufs_check_dir_entry ("ufs_add_entry", dir, de, bh, offset)) {
+ brelse (bh);
+ return -ENOENT;
+ }
+ if (ufs_match(sb, namelen, name, de)) {
+ brelse (bh);
+ return -EEXIST;
+ }
+ if (de->d_ino == 0 && fs16_to_cpu(sb, de->d_reclen) >= rec_len)
+ break;
+
+ if (fs16_to_cpu(sb, de->d_reclen) >=
+ UFS_DIR_REC_LEN(ufs_get_de_namlen(sb, de)) + rec_len)
+ break;
+ offset += fs16_to_cpu(sb, de->d_reclen);
+ de = (struct ufs_dir_entry *) ((char *) de + fs16_to_cpu(sb, de->d_reclen));
+ }
+
+ if (de->d_ino) {
+ de1 = (struct ufs_dir_entry *) ((char *) de +
+ UFS_DIR_REC_LEN(ufs_get_de_namlen(sb, de)));
+ de1->d_reclen =
+ cpu_to_fs16(sb, fs16_to_cpu(sb, de->d_reclen) -
+ UFS_DIR_REC_LEN(ufs_get_de_namlen(sb, de)));
+ de->d_reclen =
+ cpu_to_fs16(sb, UFS_DIR_REC_LEN(ufs_get_de_namlen(sb, de)));
+ de = de1;
+ }
+ de->d_ino = 0;
+ ufs_set_de_namlen(sb, de, namelen);
+ memcpy (de->d_name, name, namelen + 1);
+ de->d_ino = cpu_to_fs32(sb, inode->i_ino);
+ ufs_set_de_type(sb, de, inode->i_mode);
+ mark_buffer_dirty(bh);
+ if (IS_SYNC(dir)) {
+ ll_rw_block (WRITE, 1, &bh);
+ wait_on_buffer (bh);
+ }
+ brelse (bh);
+ dir->i_mtime = dir->i_ctime = CURRENT_TIME;
+ dir->i_version = ++event;
+ mark_inode_dirty(dir);
+
+ UFSD(("EXIT\n"))
+ return 0;
+}
+
+/*
+ * ufs_delete_entry deletes a directory entry by merging it with the
+ * previous entry.
+ */
+int ufs_delete_entry (struct inode * inode, struct ufs_dir_entry * dir,
+ struct buffer_head * bh )
+
+{
+ struct super_block * sb;
+ struct ufs_dir_entry * de, * pde;
+ unsigned i;
+
+ UFSD(("ENTER\n"))
+
+ sb = inode->i_sb;
+ i = 0;
+ pde = NULL;
+ de = (struct ufs_dir_entry *) bh->b_data;
+
+ UFSD(("ino %u, reclen %u, namlen %u, name %s\n",
+ fs32_to_cpu(sb, de->d_ino),
+ fs16to_cpu(sb, de->d_reclen),
+ ufs_get_de_namlen(sb, de), de->d_name))
+
+ while (i < bh->b_size) {
+ if (!ufs_check_dir_entry ("ufs_delete_entry", inode, de, bh, i)) {
+ brelse(bh);
+ return -EIO;
+ }
+ if (de == dir) {
+ if (pde)
+ fs16_add(sb, &pde->d_reclen,
+ fs16_to_cpu(sb, dir->d_reclen));
+ dir->d_ino = 0;
+ inode->i_version = ++event;
+ inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+ mark_buffer_dirty(bh);
+ if (IS_SYNC(inode)) {
+ ll_rw_block(WRITE, 1, &bh);
+ wait_on_buffer(bh);
+ }
+ brelse(bh);
+ UFSD(("EXIT\n"))
+ return 0;
+ }
+ i += fs16_to_cpu(sb, de->d_reclen);
+ if (i == UFS_SECTOR_SIZE) pde = NULL;
+ else pde = de;
+ de = (struct ufs_dir_entry *)
+ ((char *) de + fs16_to_cpu(sb, de->d_reclen));
+ if (i == UFS_SECTOR_SIZE && de->d_reclen == 0)
+ break;
+ }
+ UFSD(("EXIT\n"))
+ brelse(bh);
+ return -ENOENT;
+}
+
+int ufs_make_empty(struct inode * inode, struct inode *dir)
+{
+ struct super_block * sb = dir->i_sb;
+ struct buffer_head * dir_block;
+ struct ufs_dir_entry * de;
+ int err;
+
+ dir_block = ufs_bread (inode, 0, 1, &err);
+ if (!dir_block)
+ return err;
+
+ inode->i_blocks = sb->s_blocksize / UFS_SECTOR_SIZE;
+ de = (struct ufs_dir_entry *) dir_block->b_data;
+ de->d_ino = cpu_to_fs32(sb, inode->i_ino);
+ ufs_set_de_type(sb, de, inode->i_mode);
+ ufs_set_de_namlen(sb, de, 1);
+ de->d_reclen = cpu_to_fs16(sb, UFS_DIR_REC_LEN(1));
+ strcpy (de->d_name, ".");
+ de = (struct ufs_dir_entry *)
+ ((char *)de + fs16_to_cpu(sb, de->d_reclen));
+ de->d_ino = cpu_to_fs32(sb, dir->i_ino);
+ ufs_set_de_type(sb, de, dir->i_mode);
+ de->d_reclen = cpu_to_fs16(sb, UFS_SECTOR_SIZE - UFS_DIR_REC_LEN(1));
+ ufs_set_de_namlen(sb, de, 2);
+ strcpy (de->d_name, "..");
+ mark_buffer_dirty(dir_block);
+ brelse (dir_block);
+ mark_inode_dirty(inode);
+ return 0;
+}
+
+/*
+ * routine to check that the specified directory is empty (for rmdir)
+ */
+int ufs_empty_dir (struct inode * inode)
+{
+ struct super_block * sb;
+ unsigned long offset;
+ struct buffer_head * bh;
+ struct ufs_dir_entry * de, * de1;
+ int err;
+
+ sb = inode->i_sb;
+
+ if (inode->i_size < UFS_DIR_REC_LEN(1) + UFS_DIR_REC_LEN(2) ||
+ !(bh = ufs_bread (inode, 0, 0, &err))) {
+ ufs_warning (inode->i_sb, "empty_dir",
+ "bad directory (dir #%lu) - no data block",
+ inode->i_ino);
+ return 1;
+ }
+ de = (struct ufs_dir_entry *) bh->b_data;
+ de1 = (struct ufs_dir_entry *)
+ ((char *)de + fs16_to_cpu(sb, de->d_reclen));
+ if (fs32_to_cpu(sb, de->d_ino) != inode->i_ino || de1->d_ino == 0 ||
+ strcmp (".", de->d_name) || strcmp ("..", de1->d_name)) {
+ ufs_warning (inode->i_sb, "empty_dir",
+ "bad directory (dir #%lu) - no `.' or `..'",
+ inode->i_ino);
+ return 1;
+ }
+ offset = fs16_to_cpu(sb, de->d_reclen) + fs16_to_cpu(sb, de1->d_reclen);
+ de = (struct ufs_dir_entry *)
+ ((char *)de1 + fs16_to_cpu(sb, de1->d_reclen));
+ while (offset < inode->i_size ) {
+ if (!bh || (void *) de >= (void *) (bh->b_data + sb->s_blocksize)) {
+ brelse (bh);
+ bh = ufs_bread (inode, offset >> sb->s_blocksize_bits, 1, &err);
+ if (!bh) {
+ ufs_error (sb, "empty_dir",
+ "directory #%lu contains a hole at offset %lu",
+ inode->i_ino, offset);
+ offset += sb->s_blocksize;
+ continue;
+ }
+ de = (struct ufs_dir_entry *) bh->b_data;
+ }
+ if (!ufs_check_dir_entry ("empty_dir", inode, de, bh, offset)) {
+ brelse (bh);
+ return 1;
+ }
+ if (de->d_ino) {
+ brelse (bh);
+ return 0;
+ }
+ offset += fs16_to_cpu(sb, de->d_reclen);
+ de = (struct ufs_dir_entry *)
+ ((char *)de + fs16_to_cpu(sb, de->d_reclen));
+ }
+ brelse (bh);
+ return 1;
+}
+
+struct file_operations ufs_dir_operations = {
+ read: generic_read_dir,
+ readdir: ufs_readdir,
+ fsync: file_fsync,
+};
diff --git a/fs/ufs/file.c b/fs/ufs/file.c
new file mode 100644
index 00000000000000..2560b59ddbb315
--- /dev/null
+++ b/fs/ufs/file.c
@@ -0,0 +1,54 @@
+/*
+ * linux/fs/ufs/file.c
+ *
+ * Copyright (C) 1998
+ * Daniel Pirkl <daniel.pirkl@email.cz>
+ * Charles University, Faculty of Mathematics and Physics
+ *
+ * from
+ *
+ * linux/fs/ext2/file.c
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * from
+ *
+ * linux/fs/minix/file.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * ext2 fs regular file handling primitives
+ */
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/ufs_fs.h>
+#include <linux/fcntl.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/locks.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+
+/*
+ * We have mostly NULL's here: the current defaults are ok for
+ * the ufs filesystem.
+ */
+
+struct file_operations ufs_file_operations = {
+ llseek: generic_file_llseek,
+ read: generic_file_read,
+ write: generic_file_write,
+ mmap: generic_file_mmap,
+ open: generic_file_open,
+};
+
+struct inode_operations ufs_file_inode_operations = {
+ truncate: ufs_truncate,
+};
diff --git a/fs/ufs/ialloc.c b/fs/ufs/ialloc.c
new file mode 100644
index 00000000000000..25f22378bba93a
--- /dev/null
+++ b/fs/ufs/ialloc.c
@@ -0,0 +1,290 @@
+/*
+ * linux/fs/ufs/ialloc.c
+ *
+ * Copyright (c) 1998
+ * Daniel Pirkl <daniel.pirkl@email.cz>
+ * Charles University, Faculty of Mathematics and Physics
+ *
+ * from
+ *
+ * linux/fs/ext2/ialloc.c
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * BSD ufs-inspired inode and directory allocation by
+ * Stephen Tweedie (sct@dcs.ed.ac.uk), 1993
+ * Big-endian to little-endian byte-swapping/bitmaps by
+ * David S. Miller (davem@caip.rutgers.edu), 1995
+ */
+
+#include <linux/fs.h>
+#include <linux/ufs_fs.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/quotaops.h>
+#include <asm/bitops.h>
+#include <asm/byteorder.h>
+
+#include "swab.h"
+#include "util.h"
+
+#undef UFS_IALLOC_DEBUG
+
+#ifdef UFS_IALLOC_DEBUG
+#define UFSD(x) printk("(%s, %d), %s: ", __FILE__, __LINE__, __FUNCTION__); printk x;
+#else
+#define UFSD(x)
+#endif
+
+/*
+ * NOTE! When we get the inode, we're the only people
+ * that have access to it, and as such there are no
+ * race conditions we have to worry about. The inode
+ * is not on the hash-lists, and it cannot be reached
+ * through the filesystem because the directory entry
+ * has been deleted earlier.
+ *
+ * HOWEVER: we must make sure that we get no aliases,
+ * which means that we have to call "clear_inode()"
+ * _before_ we mark the inode not in use in the inode
+ * bitmaps. Otherwise a newly created file might use
+ * the same inode number (not actually the same pointer
+ * though), and then we'd have two inodes sharing the
+ * same inode number and space on the harddisk.
+ */
+void ufs_free_inode (struct inode * inode)
+{
+ struct super_block * sb;
+ struct ufs_sb_private_info * uspi;
+ struct ufs_super_block_first * usb1;
+ struct ufs_cg_private_info * ucpi;
+ struct ufs_cylinder_group * ucg;
+ int is_directory;
+ unsigned ino, cg, bit;
+
+ UFSD(("ENTER, ino %lu\n", inode->i_ino))
+
+ sb = inode->i_sb;
+ uspi = sb->u.ufs_sb.s_uspi;
+ usb1 = ubh_get_usb_first(USPI_UBH);
+
+ ino = inode->i_ino;
+
+ lock_super (sb);
+
+ if (!((ino > 1) && (ino < (uspi->s_ncg * uspi->s_ipg )))) {
+ ufs_warning(sb, "ufs_free_inode", "reserved inode or nonexistent inode %u\n", ino);
+ unlock_super (sb);
+ return;
+ }
+
+ cg = ufs_inotocg (ino);
+ bit = ufs_inotocgoff (ino);
+ ucpi = ufs_load_cylinder (sb, cg);
+ if (!ucpi) {
+ unlock_super (sb);
+ return;
+ }
+ ucg = ubh_get_ucg(UCPI_UBH);
+ if (!ufs_cg_chkmagic(sb, ucg))
+ ufs_panic (sb, "ufs_free_fragments", "internal error, bad cg magic number");
+
+ ucg->cg_time = cpu_to_fs32(sb, CURRENT_TIME);
+
+ is_directory = S_ISDIR(inode->i_mode);
+
+ DQUOT_FREE_INODE(inode);
+ DQUOT_DROP(inode);
+
+ clear_inode (inode);
+
+ if (ubh_isclr (UCPI_UBH, ucpi->c_iusedoff, bit))
+ ufs_error(sb, "ufs_free_inode", "bit already cleared for inode %u", ino);
+ else {
+ ubh_clrbit (UCPI_UBH, ucpi->c_iusedoff, bit);
+ if (ino < ucpi->c_irotor)
+ ucpi->c_irotor = ino;
+ fs32_add(sb, &ucg->cg_cs.cs_nifree, 1);
+ fs32_add(sb, &usb1->fs_cstotal.cs_nifree, 1);
+ fs32_add(sb, &sb->fs_cs(cg).cs_nifree, 1);
+
+ if (is_directory) {
+ fs32_sub(sb, &ucg->cg_cs.cs_ndir, 1);
+ fs32_sub(sb, &usb1->fs_cstotal.cs_ndir, 1);
+ fs32_sub(sb, &sb->fs_cs(cg).cs_ndir, 1);
+ }
+ }
+
+ ubh_mark_buffer_dirty (USPI_UBH);
+ ubh_mark_buffer_dirty (UCPI_UBH);
+ if (sb->s_flags & MS_SYNCHRONOUS) {
+ ubh_ll_rw_block (WRITE, 1, (struct ufs_buffer_head **) &ucpi);
+ ubh_wait_on_buffer (UCPI_UBH);
+ }
+
+ sb->s_dirt = 1;
+ unlock_super (sb);
+ UFSD(("EXIT\n"))
+}
+
+/*
+ * There are two policies for allocating an inode. If the new inode is
+ * a directory, then a forward search is made for a block group with both
+ * free space and a low directory-to-inode ratio; if that fails, then of
+ * the groups with above-average free space, that group with the fewest
+ * directories already is chosen.
+ *
+ * For other inodes, search forward from the parent directory's block
+ * group to find a free inode.
+ */
+struct inode * ufs_new_inode (const struct inode * dir, int mode)
+{
+ struct super_block * sb;
+ struct ufs_sb_private_info * uspi;
+ struct ufs_super_block_first * usb1;
+ struct ufs_cg_private_info * ucpi;
+ struct ufs_cylinder_group * ucg;
+ struct inode * inode;
+ unsigned cg, bit, i, j, start;
+
+ UFSD(("ENTER\n"))
+
+ /* Cannot create files in a deleted directory */
+ if (!dir || !dir->i_nlink)
+ return ERR_PTR(-EPERM);
+ sb = dir->i_sb;
+ inode = new_inode(sb);
+ if (!inode)
+ return ERR_PTR(-ENOMEM);
+ uspi = sb->u.ufs_sb.s_uspi;
+ usb1 = ubh_get_usb_first(USPI_UBH);
+
+ lock_super (sb);
+
+ /*
+ * Try to place the inode in its parent directory
+ */
+ i = ufs_inotocg(dir->i_ino);
+ if (sb->fs_cs(i).cs_nifree) {
+ cg = i;
+ goto cg_found;
+ }
+
+ /*
+ * Use a quadratic hash to find a group with a free inode
+ */
+ for ( j = 1; j < uspi->s_ncg; j <<= 1 ) {
+ i += j;
+ if (i >= uspi->s_ncg)
+ i -= uspi->s_ncg;
+ if (sb->fs_cs(i).cs_nifree) {
+ cg = i;
+ goto cg_found;
+ }
+ }
+
+ /*
+ * That failed: try linear search for a free inode
+ */
+ i = ufs_inotocg(dir->i_ino) + 1;
+ for (j = 2; j < uspi->s_ncg; j++) {
+ i++;
+ if (i >= uspi->s_ncg)
+ i = 0;
+ if (sb->fs_cs(i).cs_nifree) {
+ cg = i;
+ goto cg_found;
+ }
+ }
+
+ goto failed;
+
+cg_found:
+ ucpi = ufs_load_cylinder (sb, cg);
+ if (!ucpi)
+ goto failed;
+ ucg = ubh_get_ucg(UCPI_UBH);
+ if (!ufs_cg_chkmagic(sb, ucg))
+ ufs_panic (sb, "ufs_new_inode", "internal error, bad cg magic number");
+
+ start = ucpi->c_irotor;
+ bit = ubh_find_next_zero_bit (UCPI_UBH, ucpi->c_iusedoff, uspi->s_ipg, start);
+ if (!(bit < uspi->s_ipg)) {
+ bit = ubh_find_first_zero_bit (UCPI_UBH, ucpi->c_iusedoff, start);
+ if (!(bit < start)) {
+ ufs_error (sb, "ufs_new_inode",
+ "cylinder group %u corrupted - error in inode bitmap\n", cg);
+ goto failed;
+ }
+ }
+ UFSD(("start = %u, bit = %u, ipg = %u\n", start, bit, uspi->s_ipg))
+ if (ubh_isclr (UCPI_UBH, ucpi->c_iusedoff, bit))
+ ubh_setbit (UCPI_UBH, ucpi->c_iusedoff, bit);
+ else {
+ ufs_panic (sb, "ufs_new_inode", "internal error");
+ goto failed;
+ }
+
+ fs32_sub(sb, &ucg->cg_cs.cs_nifree, 1);
+ fs32_sub(sb, &usb1->fs_cstotal.cs_nifree, 1);
+ fs32_sub(sb, &sb->fs_cs(cg).cs_nifree, 1);
+
+ if (S_ISDIR(mode)) {
+ fs32_add(sb, &ucg->cg_cs.cs_ndir, 1);
+ fs32_add(sb, &usb1->fs_cstotal.cs_ndir, 1);
+ fs32_add(sb, &sb->fs_cs(cg).cs_ndir, 1);
+ }
+
+ ubh_mark_buffer_dirty (USPI_UBH);
+ ubh_mark_buffer_dirty (UCPI_UBH);
+ if (sb->s_flags & MS_SYNCHRONOUS) {
+ ubh_ll_rw_block (WRITE, 1, (struct ufs_buffer_head **) &ucpi);
+ ubh_wait_on_buffer (UCPI_UBH);
+ }
+ sb->s_dirt = 1;
+
+ inode->i_mode = mode;
+ inode->i_uid = current->fsuid;
+ if (dir->i_mode & S_ISGID) {
+ inode->i_gid = dir->i_gid;
+ if (S_ISDIR(mode))
+ inode->i_mode |= S_ISGID;
+ } else
+ inode->i_gid = current->fsgid;
+
+ inode->i_ino = cg * uspi->s_ipg + bit;
+ inode->i_blksize = PAGE_SIZE; /* This is the optimal IO size (for stat), not the fs block size */
+ inode->i_blocks = 0;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+ inode->u.ufs_i.i_flags = dir->u.ufs_i.i_flags;
+ inode->u.ufs_i.i_lastfrag = 0;
+
+ insert_inode_hash(inode);
+ mark_inode_dirty(inode);
+
+ unlock_super (sb);
+
+ if (DQUOT_ALLOC_INODE(inode)) {
+ DQUOT_DROP(inode);
+ inode->i_flags |= S_NOQUOTA;
+ inode->i_nlink = 0;
+ iput(inode);
+ return ERR_PTR(-EDQUOT);
+ }
+
+ UFSD(("allocating inode %lu\n", inode->i_ino))
+ UFSD(("EXIT\n"))
+ return inode;
+
+failed:
+ unlock_super (sb);
+ make_bad_inode(inode);
+ iput (inode);
+ UFSD(("EXIT (FAILED)\n"))
+ return ERR_PTR(-ENOSPC);
+}
diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c
new file mode 100644
index 00000000000000..bcfff57dd119e7
--- /dev/null
+++ b/fs/ufs/inode.c
@@ -0,0 +1,673 @@
+/*
+ * linux/fs/ufs/inode.c
+ *
+ * Copyright (C) 1998
+ * Daniel Pirkl <daniel.pirkl@email.cz>
+ * Charles University, Faculty of Mathematics and Physics
+ *
+ * from
+ *
+ * linux/fs/ext2/inode.c
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * from
+ *
+ * linux/fs/minix/inode.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * Goal-directed block allocation by Stephen Tweedie (sct@dcs.ed.ac.uk), 1993
+ * Big-endian to little-endian byte-swapping/bitmaps by
+ * David S. Miller (davem@caip.rutgers.edu), 1995
+ */
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/ufs_fs.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/mm.h>
+#include <linux/smp_lock.h>
+
+#include "swab.h"
+#include "util.h"
+
+#undef UFS_INODE_DEBUG
+#undef UFS_INODE_DEBUG_MORE
+
+#ifdef UFS_INODE_DEBUG
+#define UFSD(x) printk("(%s, %d), %s: ", __FILE__, __LINE__, __FUNCTION__); printk x;
+#else
+#define UFSD(x)
+#endif
+
+static int ufs_block_to_path(struct inode *inode, long i_block, int offsets[4])
+{
+ struct ufs_sb_private_info *uspi = inode->i_sb->u.ufs_sb.s_uspi;
+ int ptrs = uspi->s_apb;
+ int ptrs_bits = uspi->s_apbshift;
+ const long direct_blocks = UFS_NDADDR,
+ indirect_blocks = ptrs,
+ double_blocks = (1 << (ptrs_bits * 2));
+ int n = 0;
+
+ if (i_block < 0) {
+ ufs_warning(inode->i_sb, "ufs_block_to_path", "block < 0");
+ } else if (i_block < direct_blocks) {
+ offsets[n++] = i_block;
+ } else if ((i_block -= direct_blocks) < indirect_blocks) {
+ offsets[n++] = UFS_IND_BLOCK;
+ offsets[n++] = i_block;
+ } else if ((i_block -= indirect_blocks) < double_blocks) {
+ offsets[n++] = UFS_DIND_BLOCK;
+ offsets[n++] = i_block >> ptrs_bits;
+ offsets[n++] = i_block & (ptrs - 1);
+ } else if (((i_block -= double_blocks) >> (ptrs_bits * 2)) < ptrs) {
+ offsets[n++] = UFS_TIND_BLOCK;
+ offsets[n++] = i_block >> (ptrs_bits * 2);
+ offsets[n++] = (i_block >> ptrs_bits) & (ptrs - 1);
+ offsets[n++] = i_block & (ptrs - 1);
+ } else {
+ ufs_warning(inode->i_sb, "ufs_block_to_path", "block > big");
+ }
+ return n;
+}
+
+int ufs_frag_map(struct inode *inode, int frag)
+{
+ struct super_block *sb = inode->i_sb;
+ struct ufs_sb_private_info *uspi = sb->u.ufs_sb.s_uspi;
+ int mask = uspi->s_apbmask>>uspi->s_fpbshift;
+ int shift = uspi->s_apbshift-uspi->s_fpbshift;
+ int offsets[4], *p;
+ int depth = ufs_block_to_path(inode, frag >> uspi->s_fpbshift, offsets);
+ int ret = 0;
+ u32 block;
+
+ if (depth == 0)
+ return 0;
+
+ p = offsets;
+
+ lock_kernel();
+ block = inode->u.ufs_i.i_u1.i_data[*p++];
+ if (!block)
+ goto out;
+ while (--depth) {
+ struct buffer_head *bh;
+ int n = *p++;
+
+ bh = sb_bread(sb, uspi->s_sbbase + fs32_to_cpu(sb, block)+(n>>shift));
+ if (!bh)
+ goto out;
+ block = ((u32*) bh->b_data)[n & mask];
+ brelse (bh);
+ if (!block)
+ goto out;
+ }
+ ret = uspi->s_sbbase + fs32_to_cpu(sb, block) + (frag & uspi->s_fpbmask);
+out:
+ unlock_kernel();
+ return ret;
+}
+
+static struct buffer_head * ufs_inode_getfrag (struct inode *inode,
+ unsigned int fragment, unsigned int new_fragment,
+ unsigned int required, int *err, int metadata, long *phys, int *new)
+{
+ struct super_block * sb;
+ struct ufs_sb_private_info * uspi;
+ struct buffer_head * result;
+ unsigned block, blockoff, lastfrag, lastblock, lastblockoff;
+ unsigned tmp, goal;
+ u32 * p, * p2;
+
+ UFSD(("ENTER, ino %lu, fragment %u, new_fragment %u, required %u\n",
+ inode->i_ino, fragment, new_fragment, required))
+
+ sb = inode->i_sb;
+ uspi = sb->u.ufs_sb.s_uspi;
+ block = ufs_fragstoblks (fragment);
+ blockoff = ufs_fragnum (fragment);
+ p = inode->u.ufs_i.i_u1.i_data + block;
+ goal = 0;
+
+repeat:
+ tmp = fs32_to_cpu(sb, *p);
+ lastfrag = inode->u.ufs_i.i_lastfrag;
+ if (tmp && fragment < lastfrag) {
+ if (metadata) {
+ result = sb_getblk(sb, uspi->s_sbbase + tmp + blockoff);
+ if (tmp == fs32_to_cpu(sb, *p)) {
+ UFSD(("EXIT, result %u\n", tmp + blockoff))
+ return result;
+ }
+ brelse (result);
+ goto repeat;
+ } else {
+ *phys = tmp;
+ return NULL;
+ }
+ }
+
+ lastblock = ufs_fragstoblks (lastfrag);
+ lastblockoff = ufs_fragnum (lastfrag);
+ /*
+ * We will extend file into new block beyond last allocated block
+ */
+ if (lastblock < block) {
+ /*
+ * We must reallocate last allocated block
+ */
+ if (lastblockoff) {
+ p2 = inode->u.ufs_i.i_u1.i_data + lastblock;
+ tmp = ufs_new_fragments (inode, p2, lastfrag,
+ fs32_to_cpu(sb, *p2), uspi->s_fpb - lastblockoff, err);
+ if (!tmp) {
+ if (lastfrag != inode->u.ufs_i.i_lastfrag)
+ goto repeat;
+ else
+ return NULL;
+ }
+ lastfrag = inode->u.ufs_i.i_lastfrag;
+
+ }
+ goal = fs32_to_cpu(sb, inode->u.ufs_i.i_u1.i_data[lastblock]) + uspi->s_fpb;
+ tmp = ufs_new_fragments (inode, p, fragment - blockoff,
+ goal, required + blockoff, err);
+ }
+ /*
+ * We will extend last allocated block
+ */
+ else if (lastblock == block) {
+ tmp = ufs_new_fragments (inode, p, fragment - (blockoff - lastblockoff),
+ fs32_to_cpu(sb, *p), required + (blockoff - lastblockoff), err);
+ }
+ /*
+ * We will allocate new block before last allocated block
+ */
+ else /* (lastblock > block) */ {
+ if (lastblock && (tmp = fs32_to_cpu(sb, inode->u.ufs_i.i_u1.i_data[lastblock-1])))
+ goal = tmp + uspi->s_fpb;
+ tmp = ufs_new_fragments (inode, p, fragment - blockoff,
+ goal, uspi->s_fpb, err);
+ }
+ if (!tmp) {
+ if ((!blockoff && *p) ||
+ (blockoff && lastfrag != inode->u.ufs_i.i_lastfrag))
+ goto repeat;
+ *err = -ENOSPC;
+ return NULL;
+ }
+
+ /* The nullification of framgents done in ufs/balloc.c is
+ * something I don't have the stomache to move into here right
+ * now. -DaveM
+ */
+ if (metadata) {
+ result = sb_getblk(inode->i_sb, tmp + blockoff);
+ } else {
+ *phys = tmp;
+ result = NULL;
+ *err = 0;
+ *new = 1;
+ }
+
+ inode->i_ctime = CURRENT_TIME;
+ if (IS_SYNC(inode))
+ ufs_sync_inode (inode);
+ mark_inode_dirty(inode);
+ UFSD(("EXIT, result %u\n", tmp + blockoff))
+ return result;
+}
+
+static struct buffer_head * ufs_block_getfrag (struct inode *inode,
+ struct buffer_head *bh, unsigned int fragment, unsigned int new_fragment,
+ unsigned int blocksize, int * err, int metadata, long *phys, int *new)
+{
+ struct super_block * sb;
+ struct ufs_sb_private_info * uspi;
+ struct buffer_head * result;
+ unsigned tmp, goal, block, blockoff;
+ u32 * p;
+
+ sb = inode->i_sb;
+ uspi = sb->u.ufs_sb.s_uspi;
+ block = ufs_fragstoblks (fragment);
+ blockoff = ufs_fragnum (fragment);
+
+ UFSD(("ENTER, ino %lu, fragment %u, new_fragment %u\n", inode->i_ino, fragment, new_fragment))
+
+ result = NULL;
+ if (!bh)
+ goto out;
+ if (!buffer_uptodate(bh)) {
+ ll_rw_block (READ, 1, &bh);
+ wait_on_buffer (bh);
+ if (!buffer_uptodate(bh))
+ goto out;
+ }
+
+ p = (u32 *) bh->b_data + block;
+repeat:
+ tmp = fs32_to_cpu(sb, *p);
+ if (tmp) {
+ if (metadata) {
+ result = sb_getblk(sb, uspi->s_sbbase + tmp + blockoff);
+ if (tmp == fs32_to_cpu(sb, *p))
+ goto out;
+ brelse (result);
+ goto repeat;
+ } else {
+ *phys = tmp;
+ goto out;
+ }
+ }
+
+ if (block && (tmp = fs32_to_cpu(sb, ((u32*)bh->b_data)[block-1]) + uspi->s_fpb))
+ goal = tmp + uspi->s_fpb;
+ else
+ goal = bh->b_blocknr + uspi->s_fpb;
+ tmp = ufs_new_fragments (inode, p, ufs_blknum(new_fragment), goal, uspi->s_fpb, err);
+ if (!tmp) {
+ if (fs32_to_cpu(sb, *p))
+ goto repeat;
+ goto out;
+ }
+
+ /* The nullification of framgents done in ufs/balloc.c is
+ * something I don't have the stomache to move into here right
+ * now. -DaveM
+ */
+ if (metadata) {
+ result = sb_getblk(sb, tmp + blockoff);
+ } else {
+ *phys = tmp;
+ *new = 1;
+ }
+
+ mark_buffer_dirty(bh);
+ if (IS_SYNC(inode)) {
+ ll_rw_block (WRITE, 1, &bh);
+ wait_on_buffer (bh);
+ }
+ inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+out:
+ brelse (bh);
+ UFSD(("EXIT, result %u\n", tmp + blockoff))
+ return result;
+}
+
+static int ufs_getfrag_block (struct inode *inode, long fragment, struct buffer_head *bh_result, int create)
+{
+ struct super_block * sb;
+ struct ufs_sb_private_info * uspi;
+ struct buffer_head * bh;
+ int ret, err, new;
+ unsigned long ptr, phys;
+
+ sb = inode->i_sb;
+ uspi = sb->u.ufs_sb.s_uspi;
+
+ if (!create) {
+ phys = ufs_frag_map(inode, fragment);
+ if (phys) {
+ bh_result->b_dev = inode->i_dev;
+ bh_result->b_blocknr = phys;
+ bh_result->b_state |= (1UL << BH_Mapped);
+ }
+ return 0;
+ }
+
+ err = -EIO;
+ new = 0;
+ ret = 0;
+ bh = NULL;
+
+ lock_kernel();
+
+ UFSD(("ENTER, ino %lu, fragment %u\n", inode->i_ino, fragment))
+ if (fragment < 0)
+ goto abort_negative;
+ if (fragment >
+ ((UFS_NDADDR + uspi->s_apb + uspi->s_2apb + uspi->s_3apb)
+ << uspi->s_fpbshift))
+ goto abort_too_big;
+
+ err = 0;
+ ptr = fragment;
+
+ /*
+ * ok, these macros clean the logic up a bit and make
+ * it much more readable:
+ */
+#define GET_INODE_DATABLOCK(x) \
+ ufs_inode_getfrag(inode, x, fragment, 1, &err, 0, &phys, &new)
+#define GET_INODE_PTR(x) \
+ ufs_inode_getfrag(inode, x, fragment, uspi->s_fpb, &err, 1, NULL, NULL)
+#define GET_INDIRECT_DATABLOCK(x) \
+ ufs_block_getfrag(inode, bh, x, fragment, sb->s_blocksize, \
+ &err, 0, &phys, &new);
+#define GET_INDIRECT_PTR(x) \
+ ufs_block_getfrag(inode, bh, x, fragment, sb->s_blocksize, \
+ &err, 1, NULL, NULL);
+
+ if (ptr < UFS_NDIR_FRAGMENT) {
+ bh = GET_INODE_DATABLOCK(ptr);
+ goto out;
+ }
+ ptr -= UFS_NDIR_FRAGMENT;
+ if (ptr < (1 << (uspi->s_apbshift + uspi->s_fpbshift))) {
+ bh = GET_INODE_PTR(UFS_IND_FRAGMENT + (ptr >> uspi->s_apbshift));
+ goto get_indirect;
+ }
+ ptr -= 1 << (uspi->s_apbshift + uspi->s_fpbshift);
+ if (ptr < (1 << (uspi->s_2apbshift + uspi->s_fpbshift))) {
+ bh = GET_INODE_PTR(UFS_DIND_FRAGMENT + (ptr >> uspi->s_2apbshift));
+ goto get_double;
+ }
+ ptr -= 1 << (uspi->s_2apbshift + uspi->s_fpbshift);
+ bh = GET_INODE_PTR(UFS_TIND_FRAGMENT + (ptr >> uspi->s_3apbshift));
+ bh = GET_INDIRECT_PTR((ptr >> uspi->s_2apbshift) & uspi->s_apbmask);
+get_double:
+ bh = GET_INDIRECT_PTR((ptr >> uspi->s_apbshift) & uspi->s_apbmask);
+get_indirect:
+ bh = GET_INDIRECT_DATABLOCK(ptr & uspi->s_apbmask);
+
+#undef GET_INODE_DATABLOCK
+#undef GET_INODE_PTR
+#undef GET_INDIRECT_DATABLOCK
+#undef GET_INDIRECT_PTR
+
+out:
+ if (err)
+ goto abort;
+ bh_result->b_dev = inode->i_dev;
+ bh_result->b_blocknr = phys;
+ bh_result->b_state |= (1UL << BH_Mapped);
+ if (new)
+ bh_result->b_state |= (1UL << BH_New);
+abort:
+ unlock_kernel();
+ return err;
+
+abort_negative:
+ ufs_warning(sb, "ufs_get_block", "block < 0");
+ goto abort;
+
+abort_too_big:
+ ufs_warning(sb, "ufs_get_block", "block > big");
+ goto abort;
+}
+
+struct buffer_head *ufs_getfrag(struct inode *inode, unsigned int fragment,
+ int create, int *err)
+{
+ struct buffer_head dummy;
+ int error;
+
+ dummy.b_state = 0;
+ dummy.b_blocknr = -1000;
+ error = ufs_getfrag_block(inode, fragment, &dummy, create);
+ *err = error;
+ if (!error && buffer_mapped(&dummy)) {
+ struct buffer_head *bh;
+ bh = sb_getblk(inode->i_sb, dummy.b_blocknr);
+ if (buffer_new(&dummy)) {
+ memset(bh->b_data, 0, inode->i_sb->s_blocksize);
+ mark_buffer_uptodate(bh, 1);
+ mark_buffer_dirty(bh);
+ }
+ return bh;
+ }
+ return NULL;
+}
+
+struct buffer_head * ufs_bread (struct inode * inode, unsigned fragment,
+ int create, int * err)
+{
+ struct buffer_head * bh;
+
+ UFSD(("ENTER, ino %lu, fragment %u\n", inode->i_ino, fragment))
+ bh = ufs_getfrag (inode, fragment, create, err);
+ if (!bh || buffer_uptodate(bh))
+ return bh;
+ ll_rw_block (READ, 1, &bh);
+ wait_on_buffer (bh);
+ if (buffer_uptodate(bh))
+ return bh;
+ brelse (bh);
+ *err = -EIO;
+ return NULL;
+}
+
+static int ufs_writepage(struct page *page)
+{
+ return block_write_full_page(page,ufs_getfrag_block);
+}
+static int ufs_readpage(struct file *file, struct page *page)
+{
+ return block_read_full_page(page,ufs_getfrag_block);
+}
+static int ufs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
+{
+ return block_prepare_write(page,from,to,ufs_getfrag_block);
+}
+static int ufs_bmap(struct address_space *mapping, long block)
+{
+ return generic_block_bmap(mapping,block,ufs_getfrag_block);
+}
+struct address_space_operations ufs_aops = {
+ readpage: ufs_readpage,
+ writepage: ufs_writepage,
+ sync_page: block_sync_page,
+ prepare_write: ufs_prepare_write,
+ commit_write: generic_commit_write,
+ bmap: ufs_bmap
+};
+
+void ufs_read_inode (struct inode * inode)
+{
+ struct super_block * sb;
+ struct ufs_sb_private_info * uspi;
+ struct ufs_inode * ufs_inode;
+ struct buffer_head * bh;
+ unsigned i;
+ unsigned flags;
+
+ UFSD(("ENTER, ino %lu\n", inode->i_ino))
+
+ sb = inode->i_sb;
+ uspi = sb->u.ufs_sb.s_uspi;
+ flags = sb->u.ufs_sb.s_flags;
+
+ if (inode->i_ino < UFS_ROOTINO ||
+ inode->i_ino > (uspi->s_ncg * uspi->s_ipg)) {
+ ufs_warning (sb, "ufs_read_inode", "bad inode number (%lu)\n", inode->i_ino);
+ goto bad_inode;
+ }
+
+ bh = sb_bread(sb, uspi->s_sbbase + ufs_inotofsba(inode->i_ino));
+ if (!bh) {
+ ufs_warning (sb, "ufs_read_inode", "unable to read inode %lu\n", inode->i_ino);
+ goto bad_inode;
+ }
+ ufs_inode = (struct ufs_inode *) (bh->b_data + sizeof(struct ufs_inode) * ufs_inotofsbo(inode->i_ino));
+
+ /*
+ * Copy data to the in-core inode.
+ */
+ inode->i_mode = fs16_to_cpu(sb, ufs_inode->ui_mode);
+ inode->i_nlink = fs16_to_cpu(sb, ufs_inode->ui_nlink);
+ if (inode->i_nlink == 0)
+ ufs_error (sb, "ufs_read_inode", "inode %lu has zero nlink\n", inode->i_ino);
+
+ /*
+ * Linux now has 32-bit uid and gid, so we can support EFT.
+ */
+ inode->i_uid = ufs_get_inode_uid(sb, ufs_inode);
+ inode->i_gid = ufs_get_inode_gid(sb, ufs_inode);
+
+ inode->i_size = fs64_to_cpu(sb, ufs_inode->ui_size);
+ inode->i_atime = fs32_to_cpu(sb, ufs_inode->ui_atime.tv_sec);
+ inode->i_ctime = fs32_to_cpu(sb, ufs_inode->ui_ctime.tv_sec);
+ inode->i_mtime = fs32_to_cpu(sb, ufs_inode->ui_mtime.tv_sec);
+ inode->i_blocks = fs32_to_cpu(sb, ufs_inode->ui_blocks);
+ inode->i_blksize = PAGE_SIZE; /* This is the optimal IO size (for stat) */
+ inode->i_version = ++event;
+
+ inode->u.ufs_i.i_flags = fs32_to_cpu(sb, ufs_inode->ui_flags);
+ inode->u.ufs_i.i_gen = fs32_to_cpu(sb, ufs_inode->ui_gen);
+ inode->u.ufs_i.i_shadow = fs32_to_cpu(sb, ufs_inode->ui_u3.ui_sun.ui_shadow);
+ inode->u.ufs_i.i_oeftflag = fs32_to_cpu(sb, ufs_inode->ui_u3.ui_sun.ui_oeftflag);
+ inode->u.ufs_i.i_lastfrag = (inode->i_size + uspi->s_fsize - 1) >> uspi->s_fshift;
+
+ if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+ ;
+ else if (inode->i_blocks) {
+ for (i = 0; i < (UFS_NDADDR + UFS_NINDIR); i++)
+ inode->u.ufs_i.i_u1.i_data[i] = ufs_inode->ui_u2.ui_addr.ui_db[i];
+ }
+ else {
+ for (i = 0; i < (UFS_NDADDR + UFS_NINDIR) * 4; i++)
+ inode->u.ufs_i.i_u1.i_symlink[i] = ufs_inode->ui_u2.ui_symlink[i];
+ }
+
+
+ if (S_ISREG(inode->i_mode)) {
+ inode->i_op = &ufs_file_inode_operations;
+ inode->i_fop = &ufs_file_operations;
+ inode->i_mapping->a_ops = &ufs_aops;
+ } else if (S_ISDIR(inode->i_mode)) {
+ inode->i_op = &ufs_dir_inode_operations;
+ inode->i_fop = &ufs_dir_operations;
+ } else if (S_ISLNK(inode->i_mode)) {
+ if (!inode->i_blocks)
+ inode->i_op = &ufs_fast_symlink_inode_operations;
+ else {
+ inode->i_op = &page_symlink_inode_operations;
+ inode->i_mapping->a_ops = &ufs_aops;
+ }
+ } else
+ init_special_inode(inode, inode->i_mode,
+ fs32_to_cpu(sb, ufs_inode->ui_u2.ui_addr.ui_db[0]));
+
+ brelse (bh);
+
+ UFSD(("EXIT\n"))
+ return;
+
+bad_inode:
+ make_bad_inode(inode);
+ return;
+}
+
+static int ufs_update_inode(struct inode * inode, int do_sync)
+{
+ struct super_block * sb;
+ struct ufs_sb_private_info * uspi;
+ struct buffer_head * bh;
+ struct ufs_inode * ufs_inode;
+ unsigned i;
+ unsigned flags;
+
+ UFSD(("ENTER, ino %lu\n", inode->i_ino))
+
+ sb = inode->i_sb;
+ uspi = sb->u.ufs_sb.s_uspi;
+ flags = sb->u.ufs_sb.s_flags;
+
+ if (inode->i_ino < UFS_ROOTINO ||
+ inode->i_ino > (uspi->s_ncg * uspi->s_ipg)) {
+ ufs_warning (sb, "ufs_read_inode", "bad inode number (%lu)\n", inode->i_ino);
+ return -1;
+ }
+
+ bh = sb_bread(sb, ufs_inotofsba(inode->i_ino));
+ if (!bh) {
+ ufs_warning (sb, "ufs_read_inode", "unable to read inode %lu\n", inode->i_ino);
+ return -1;
+ }
+ ufs_inode = (struct ufs_inode *) (bh->b_data + ufs_inotofsbo(inode->i_ino) * sizeof(struct ufs_inode));
+
+ ufs_inode->ui_mode = cpu_to_fs16(sb, inode->i_mode);
+ ufs_inode->ui_nlink = cpu_to_fs16(sb, inode->i_nlink);
+
+ ufs_set_inode_uid(sb, ufs_inode, inode->i_uid);
+ ufs_set_inode_gid(sb, ufs_inode, inode->i_gid);
+
+ ufs_inode->ui_size = cpu_to_fs64(sb, inode->i_size);
+ ufs_inode->ui_atime.tv_sec = cpu_to_fs32(sb, inode->i_atime);
+ ufs_inode->ui_atime.tv_usec = 0;
+ ufs_inode->ui_ctime.tv_sec = cpu_to_fs32(sb, inode->i_ctime);
+ ufs_inode->ui_ctime.tv_usec = 0;
+ ufs_inode->ui_mtime.tv_sec = cpu_to_fs32(sb, inode->i_mtime);
+ ufs_inode->ui_mtime.tv_usec = 0;
+ ufs_inode->ui_blocks = cpu_to_fs32(sb, inode->i_blocks);
+ ufs_inode->ui_flags = cpu_to_fs32(sb, inode->u.ufs_i.i_flags);
+ ufs_inode->ui_gen = cpu_to_fs32(sb, inode->u.ufs_i.i_gen);
+
+ if ((flags & UFS_UID_MASK) == UFS_UID_EFT) {
+ ufs_inode->ui_u3.ui_sun.ui_shadow = cpu_to_fs32(sb, inode->u.ufs_i.i_shadow);
+ ufs_inode->ui_u3.ui_sun.ui_oeftflag = cpu_to_fs32(sb, inode->u.ufs_i.i_oeftflag);
+ }
+
+ if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+ ufs_inode->ui_u2.ui_addr.ui_db[0] = cpu_to_fs32(sb, kdev_t_to_nr(inode->i_rdev));
+ else if (inode->i_blocks) {
+ for (i = 0; i < (UFS_NDADDR + UFS_NINDIR); i++)
+ ufs_inode->ui_u2.ui_addr.ui_db[i] = inode->u.ufs_i.i_u1.i_data[i];
+ }
+ else {
+ for (i = 0; i < (UFS_NDADDR + UFS_NINDIR) * 4; i++)
+ ufs_inode->ui_u2.ui_symlink[i] = inode->u.ufs_i.i_u1.i_symlink[i];
+ }
+
+ if (!inode->i_nlink)
+ memset (ufs_inode, 0, sizeof(struct ufs_inode));
+
+ mark_buffer_dirty(bh);
+ if (do_sync) {
+ ll_rw_block (WRITE, 1, &bh);
+ wait_on_buffer (bh);
+ }
+ brelse (bh);
+
+ UFSD(("EXIT\n"))
+ return 0;
+}
+
+void ufs_write_inode (struct inode * inode, int wait)
+{
+ lock_kernel();
+ ufs_update_inode (inode, wait);
+ unlock_kernel();
+}
+
+int ufs_sync_inode (struct inode *inode)
+{
+ return ufs_update_inode (inode, 1);
+}
+
+void ufs_delete_inode (struct inode * inode)
+{
+ /*inode->u.ufs_i.i_dtime = CURRENT_TIME;*/
+ lock_kernel();
+ mark_inode_dirty(inode);
+ ufs_update_inode(inode, IS_SYNC(inode));
+ inode->i_size = 0;
+ if (inode->i_blocks)
+ ufs_truncate (inode);
+ ufs_free_inode (inode);
+ unlock_kernel();
+}
diff --git a/fs/ufs/namei.c b/fs/ufs/namei.c
new file mode 100644
index 00000000000000..b6b1833c43963a
--- /dev/null
+++ b/fs/ufs/namei.c
@@ -0,0 +1,337 @@
+/*
+ * linux/fs/ufs/namei.c
+ *
+ * Copyright (C) 1998
+ * Daniel Pirkl <daniel.pirkl@email.cz>
+ * Charles University, Faculty of Mathematics and Physics
+ *
+ * from
+ *
+ * linux/fs/ext2/namei.c
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * from
+ *
+ * linux/fs/minix/namei.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * Big-endian to little-endian byte-swapping/bitmaps by
+ * David S. Miller (davem@caip.rutgers.edu), 1995
+ */
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/ufs_fs.h>
+
+#undef UFS_NAMEI_DEBUG
+
+#ifdef UFS_NAMEI_DEBUG
+#define UFSD(x) printk("(%s, %d), %s: ", __FILE__, __LINE__, __FUNCTION__); printk x;
+#else
+#define UFSD(x)
+#endif
+
+static inline void ufs_inc_count(struct inode *inode)
+{
+ inode->i_nlink++;
+ mark_inode_dirty(inode);
+}
+
+static inline void ufs_dec_count(struct inode *inode)
+{
+ inode->i_nlink--;
+ mark_inode_dirty(inode);
+}
+
+static inline int ufs_add_nondir(struct dentry *dentry, struct inode *inode)
+{
+ int err = ufs_add_link(dentry, inode);
+ if (!err) {
+ d_instantiate(dentry, inode);
+ return 0;
+ }
+ ufs_dec_count(inode);
+ iput(inode);
+ return err;
+}
+
+static struct dentry *ufs_lookup(struct inode * dir, struct dentry *dentry)
+{
+ struct inode * inode = NULL;
+ ino_t ino;
+
+ if (dentry->d_name.len > UFS_MAXNAMLEN)
+ return ERR_PTR(-ENAMETOOLONG);
+
+ ino = ufs_inode_by_name(dir, dentry);
+ if (ino) {
+ inode = iget(dir->i_sb, ino);
+ if (!inode)
+ return ERR_PTR(-EACCES);
+ }
+ d_add(dentry, inode);
+ return NULL;
+}
+
+/*
+ * By the time this is called, we already have created
+ * the directory cache entry for the new file, but it
+ * is so far negative - it has no inode.
+ *
+ * If the create succeeds, we fill in the inode information
+ * with d_instantiate().
+ */
+static int ufs_create (struct inode * dir, struct dentry * dentry, int mode)
+{
+ struct inode * inode = ufs_new_inode(dir, mode);
+ int err = PTR_ERR(inode);
+ if (!IS_ERR(inode)) {
+ inode->i_op = &ufs_file_inode_operations;
+ inode->i_fop = &ufs_file_operations;
+ inode->i_mapping->a_ops = &ufs_aops;
+ mark_inode_dirty(inode);
+ err = ufs_add_nondir(dentry, inode);
+ }
+ return err;
+}
+
+static int ufs_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev)
+{
+ struct inode * inode = ufs_new_inode(dir, mode);
+ int err = PTR_ERR(inode);
+ if (!IS_ERR(inode)) {
+ init_special_inode(inode, mode, rdev);
+ mark_inode_dirty(inode);
+ err = ufs_add_nondir(dentry, inode);
+ }
+ return err;
+}
+
+static int ufs_symlink (struct inode * dir, struct dentry * dentry,
+ const char * symname)
+{
+ struct super_block * sb = dir->i_sb;
+ int err = -ENAMETOOLONG;
+ unsigned l = strlen(symname)+1;
+ struct inode * inode;
+
+ if (l > sb->s_blocksize)
+ goto out;
+
+ inode = ufs_new_inode(dir, S_IFLNK | S_IRWXUGO);
+ err = PTR_ERR(inode);
+ if (IS_ERR(inode))
+ goto out;
+
+ if (l > sb->u.ufs_sb.s_uspi->s_maxsymlinklen) {
+ /* slow symlink */
+ inode->i_op = &page_symlink_inode_operations;
+ inode->i_mapping->a_ops = &ufs_aops;
+ err = block_symlink(inode, symname, l);
+ if (err)
+ goto out_fail;
+ } else {
+ /* fast symlink */
+ inode->i_op = &ufs_fast_symlink_inode_operations;
+ memcpy((char*)&inode->u.ufs_i.i_u1.i_data,symname,l);
+ inode->i_size = l-1;
+ }
+ mark_inode_dirty(inode);
+
+ err = ufs_add_nondir(dentry, inode);
+out:
+ return err;
+
+out_fail:
+ ufs_dec_count(inode);
+ iput(inode);
+ goto out;
+}
+
+static int ufs_link (struct dentry * old_dentry, struct inode * dir,
+ struct dentry *dentry)
+{
+ struct inode *inode = old_dentry->d_inode;
+
+ if (S_ISDIR(inode->i_mode))
+ return -EPERM;
+
+ if (inode->i_nlink >= UFS_LINK_MAX)
+ return -EMLINK;
+
+ inode->i_ctime = CURRENT_TIME;
+ ufs_inc_count(inode);
+ atomic_inc(&inode->i_count);
+
+ return ufs_add_nondir(dentry, inode);
+}
+
+static int ufs_mkdir(struct inode * dir, struct dentry * dentry, int mode)
+{
+ struct inode * inode;
+ int err = -EMLINK;
+
+ if (dir->i_nlink >= UFS_LINK_MAX)
+ goto out;
+
+ ufs_inc_count(dir);
+
+ inode = ufs_new_inode(dir, S_IFDIR|mode);
+ err = PTR_ERR(inode);
+ if (IS_ERR(inode))
+ goto out_dir;
+
+ inode->i_op = &ufs_dir_inode_operations;
+ inode->i_fop = &ufs_dir_operations;
+
+ ufs_inc_count(inode);
+
+ err = ufs_make_empty(inode, dir);
+ if (err)
+ goto out_fail;
+
+ err = ufs_add_link(dentry, inode);
+ if (err)
+ goto out_fail;
+
+ d_instantiate(dentry, inode);
+out:
+ return err;
+
+out_fail:
+ ufs_dec_count(inode);
+ ufs_dec_count(inode);
+ iput (inode);
+out_dir:
+ ufs_dec_count(dir);
+ goto out;
+}
+
+static int ufs_unlink(struct inode * dir, struct dentry *dentry)
+{
+ struct inode * inode = dentry->d_inode;
+ struct buffer_head * bh;
+ struct ufs_dir_entry * de;
+ int err = -ENOENT;
+
+ de = ufs_find_entry (dentry, &bh);
+ if (!de)
+ goto out;
+
+ err = ufs_delete_entry (dir, de, bh);
+ if (err)
+ goto out;
+
+ inode->i_ctime = dir->i_ctime;
+ ufs_dec_count(inode);
+ err = 0;
+out:
+ return err;
+}
+
+static int ufs_rmdir (struct inode * dir, struct dentry *dentry)
+{
+ struct inode * inode = dentry->d_inode;
+ int err= -ENOTEMPTY;
+
+ if (ufs_empty_dir (inode)) {
+ err = ufs_unlink(dir, dentry);
+ if (!err) {
+ inode->i_size = 0;
+ ufs_dec_count(inode);
+ ufs_dec_count(dir);
+ }
+ }
+ return err;
+}
+
+static int ufs_rename (struct inode * old_dir, struct dentry * old_dentry,
+ struct inode * new_dir, struct dentry * new_dentry )
+{
+ struct inode *old_inode = old_dentry->d_inode;
+ struct inode *new_inode = new_dentry->d_inode;
+ struct buffer_head *dir_bh = NULL;
+ struct ufs_dir_entry *dir_de = NULL;
+ struct buffer_head *old_bh;
+ struct ufs_dir_entry *old_de;
+ int err = -ENOENT;
+
+ old_de = ufs_find_entry (old_dentry, &old_bh);
+ if (!old_de)
+ goto out;
+
+ if (S_ISDIR(old_inode->i_mode)) {
+ err = -EIO;
+ dir_de = ufs_dotdot(old_inode, &dir_bh);
+ if (!dir_de)
+ goto out_old;
+ }
+
+ if (new_inode) {
+ struct buffer_head *new_bh;
+ struct ufs_dir_entry *new_de;
+
+ err = -ENOTEMPTY;
+ if (dir_de && !ufs_empty_dir (new_inode))
+ goto out_dir;
+ err = -ENOENT;
+ new_de = ufs_find_entry (new_dentry, &new_bh);
+ if (!new_de)
+ goto out_dir;
+ ufs_inc_count(old_inode);
+ ufs_set_link(new_dir, new_de, new_bh, old_inode);
+ new_inode->i_ctime = CURRENT_TIME;
+ if (dir_de)
+ new_inode->i_nlink--;
+ ufs_dec_count(new_inode);
+ } else {
+ if (dir_de) {
+ err = -EMLINK;
+ if (new_dir->i_nlink >= UFS_LINK_MAX)
+ goto out_dir;
+ }
+ ufs_inc_count(old_inode);
+ err = ufs_add_link(new_dentry, old_inode);
+ if (err) {
+ ufs_dec_count(old_inode);
+ goto out_dir;
+ }
+ if (dir_de)
+ ufs_inc_count(new_dir);
+ }
+
+ ufs_delete_entry (old_dir, old_de, old_bh);
+
+ ufs_dec_count(old_inode);
+
+ if (dir_de) {
+ ufs_set_link(old_inode, dir_de, dir_bh, new_dir);
+ ufs_dec_count(old_dir);
+ }
+ return 0;
+
+out_dir:
+ if (dir_de)
+ brelse(dir_bh);
+out_old:
+ brelse (old_bh);
+out:
+ return err;
+}
+
+struct inode_operations ufs_dir_inode_operations = {
+ create: ufs_create,
+ lookup: ufs_lookup,
+ link: ufs_link,
+ unlink: ufs_unlink,
+ symlink: ufs_symlink,
+ mkdir: ufs_mkdir,
+ rmdir: ufs_rmdir,
+ mknod: ufs_mknod,
+ rename: ufs_rename,
+};
diff --git a/fs/ufs/super.c b/fs/ufs/super.c
new file mode 100644
index 00000000000000..6ac2400ed9c83e
--- /dev/null
+++ b/fs/ufs/super.c
@@ -0,0 +1,1009 @@
+/*
+ * linux/fs/ufs/super.c
+ *
+ * Copyright (C) 1998
+ * Daniel Pirkl <daniel.pirkl@email.cz>
+ * Charles University, Faculty of Mathematics and Physics
+ */
+
+/* Derived from
+ *
+ * linux/fs/ext2/super.c
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * from
+ *
+ * linux/fs/minix/inode.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * Big-endian to little-endian byte-swapping/bitmaps by
+ * David S. Miller (davem@caip.rutgers.edu), 1995
+ */
+
+/*
+ * Inspired by
+ *
+ * linux/fs/ufs/super.c
+ *
+ * Copyright (C) 1996
+ * Adrian Rodriguez (adrian@franklins-tower.rutgers.edu)
+ * Laboratory for Computer Science Research Computing Facility
+ * Rutgers, The State University of New Jersey
+ *
+ * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
+ *
+ * Kernel module support added on 96/04/26 by
+ * Stefan Reinauer <stepan@home.culture.mipt.ru>
+ *
+ * Module usage counts added on 96/04/29 by
+ * Gertjan van Wingerde <gertjan@cs.vu.nl>
+ *
+ * Clean swab support on 19970406 by
+ * Francois-Rene Rideau <fare@tunes.org>
+ *
+ * 4.4BSD (FreeBSD) support added on February 1st 1998 by
+ * Niels Kristian Bech Jensen <nkbj@image.dk> partially based
+ * on code by Martin von Loewis <martin@mira.isdn.cs.tu-berlin.de>.
+ *
+ * NeXTstep support added on February 5th 1998 by
+ * Niels Kristian Bech Jensen <nkbj@image.dk>.
+ *
+ * write support Daniel Pirkl <daniel.pirkl@email.cz> 1998
+ *
+ * HP/UX hfs filesystem support added by
+ * Martin K. Petersen <mkp@mkp.net>, August 1999
+ *
+ */
+
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <stdarg.h>
+
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/ufs_fs.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/locks.h>
+#include <linux/blkdev.h>
+#include <linux/init.h>
+
+#include "swab.h"
+#include "util.h"
+
+#undef UFS_SUPER_DEBUG
+#undef UFS_SUPER_DEBUG_MORE
+
+#ifdef UFS_SUPER_DEBUG
+#define UFSD(x) printk("(%s, %d), %s: ", __FILE__, __LINE__, __FUNCTION__); printk x;
+#else
+#define UFSD(x)
+#endif
+
+#ifdef UFS_SUPER_DEBUG_MORE
+/*
+ * Print contents of ufs_super_block, useful for debugging
+ */
+void ufs_print_super_stuff(struct super_block *sb,
+ struct ufs_super_block_first * usb1,
+ struct ufs_super_block_second * usb2,
+ struct ufs_super_block_third * usb3)
+{
+ printk("ufs_print_super_stuff\n");
+ printk("size of usb: %u\n", sizeof(struct ufs_super_block));
+ printk(" magic: 0x%x\n", fs32_to_cpu(sb, usb3->fs_magic));
+ printk(" sblkno: %u\n", fs32_to_cpu(sb, usb1->fs_sblkno));
+ printk(" cblkno: %u\n", fs32_to_cpu(sb, usb1->fs_cblkno));
+ printk(" iblkno: %u\n", fs32_to_cpu(sb, usb1->fs_iblkno));
+ printk(" dblkno: %u\n", fs32_to_cpu(sb, usb1->fs_dblkno));
+ printk(" cgoffset: %u\n", fs32_to_cpu(sb, usb1->fs_cgoffset));
+ printk(" ~cgmask: 0x%x\n", ~fs32_to_cpu(sb, usb1->fs_cgmask));
+ printk(" size: %u\n", fs32_to_cpu(sb, usb1->fs_size));
+ printk(" dsize: %u\n", fs32_to_cpu(sb, usb1->fs_dsize));
+ printk(" ncg: %u\n", fs32_to_cpu(sb, usb1->fs_ncg));
+ printk(" bsize: %u\n", fs32_to_cpu(sb, usb1->fs_bsize));
+ printk(" fsize: %u\n", fs32_to_cpu(sb, usb1->fs_fsize));
+ printk(" frag: %u\n", fs32_to_cpu(sb, usb1->fs_frag));
+ printk(" fragshift: %u\n", fs32_to_cpu(sb, usb1->fs_fragshift));
+ printk(" ~fmask: %u\n", ~fs32_to_cpu(sb, usb1->fs_fmask));
+ printk(" fshift: %u\n", fs32_to_cpu(sb, usb1->fs_fshift));
+ printk(" sbsize: %u\n", fs32_to_cpu(sb, usb1->fs_sbsize));
+ printk(" spc: %u\n", fs32_to_cpu(sb, usb1->fs_spc));
+ printk(" cpg: %u\n", fs32_to_cpu(sb, usb1->fs_cpg));
+ printk(" ipg: %u\n", fs32_to_cpu(sb, usb1->fs_ipg));
+ printk(" fpg: %u\n", fs32_to_cpu(sb, usb1->fs_fpg));
+ printk(" csaddr: %u\n", fs32_to_cpu(sb, usb1->fs_csaddr));
+ printk(" cssize: %u\n", fs32_to_cpu(sb, usb1->fs_cssize));
+ printk(" cgsize: %u\n", fs32_to_cpu(sb, usb1->fs_cgsize));
+ printk(" fstodb: %u\n", fs32_to_cpu(sb, usb1->fs_fsbtodb));
+ printk(" contigsumsize: %d\n", fs32_to_cpu(sb, usb3->fs_u2.fs_44.fs_contigsumsize));
+ printk(" postblformat: %u\n", fs32_to_cpu(sb, usb3->fs_postblformat));
+ printk(" nrpos: %u\n", fs32_to_cpu(sb, usb3->fs_nrpos));
+ printk(" ndir %u\n", fs32_to_cpu(sb, usb1->fs_cstotal.cs_ndir));
+ printk(" nifree %u\n", fs32_to_cpu(sb, usb1->fs_cstotal.cs_nifree));
+ printk(" nbfree %u\n", fs32_to_cpu(sb, usb1->fs_cstotal.cs_nbfree));
+ printk(" nffree %u\n", fs32_to_cpu(sb, usb1->fs_cstotal.cs_nffree));
+ printk("\n");
+}
+
+
+/*
+ * Print contents of ufs_cylinder_group, useful for debugging
+ */
+void ufs_print_cylinder_stuff(struct super_block *sb, struct ufs_cylinder_group *cg)
+{
+ printk("\nufs_print_cylinder_stuff\n");
+ printk("size of ucg: %u\n", sizeof(struct ufs_cylinder_group));
+ printk(" magic: %x\n", fs32_to_cpu(sb, cg->cg_magic));
+ printk(" time: %u\n", fs32_to_cpu(sb, cg->cg_time));
+ printk(" cgx: %u\n", fs32_to_cpu(sb, cg->cg_cgx));
+ printk(" ncyl: %u\n", fs16_to_cpu(sb, cg->cg_ncyl));
+ printk(" niblk: %u\n", fs16_to_cpu(sb, cg->cg_niblk));
+ printk(" ndblk: %u\n", fs32_to_cpu(sb, cg->cg_ndblk));
+ printk(" cs_ndir: %u\n", fs32_to_cpu(sb, cg->cg_cs.cs_ndir));
+ printk(" cs_nbfree: %u\n", fs32_to_cpu(sb, cg->cg_cs.cs_nbfree));
+ printk(" cs_nifree: %u\n", fs32_to_cpu(sb, cg->cg_cs.cs_nifree));
+ printk(" cs_nffree: %u\n", fs32_to_cpu(sb, cg->cg_cs.cs_nffree));
+ printk(" rotor: %u\n", fs32_to_cpu(sb, cg->cg_rotor));
+ printk(" frotor: %u\n", fs32_to_cpu(sb, cg->cg_frotor));
+ printk(" irotor: %u\n", fs32_to_cpu(sb, cg->cg_irotor));
+ printk(" frsum: %u, %u, %u, %u, %u, %u, %u, %u\n",
+ fs32_to_cpu(sb, cg->cg_frsum[0]), fs32_to_cpu(sb, cg->cg_frsum[1]),
+ fs32_to_cpu(sb, cg->cg_frsum[2]), fs32_to_cpu(sb, cg->cg_frsum[3]),
+ fs32_to_cpu(sb, cg->cg_frsum[4]), fs32_to_cpu(sb, cg->cg_frsum[5]),
+ fs32_to_cpu(sb, cg->cg_frsum[6]), fs32_to_cpu(sb, cg->cg_frsum[7]));
+ printk(" btotoff: %u\n", fs32_to_cpu(sb, cg->cg_btotoff));
+ printk(" boff: %u\n", fs32_to_cpu(sb, cg->cg_boff));
+ printk(" iuseoff: %u\n", fs32_to_cpu(sb, cg->cg_iusedoff));
+ printk(" freeoff: %u\n", fs32_to_cpu(sb, cg->cg_freeoff));
+ printk(" nextfreeoff: %u\n", fs32_to_cpu(sb, cg->cg_nextfreeoff));
+ printk(" clustersumoff %u\n", fs32_to_cpu(sb, cg->cg_u.cg_44.cg_clustersumoff));
+ printk(" clusteroff %u\n", fs32_to_cpu(sb, cg->cg_u.cg_44.cg_clusteroff));
+ printk(" nclusterblks %u\n", fs32_to_cpu(sb, cg->cg_u.cg_44.cg_nclusterblks));
+ printk("\n");
+}
+#endif /* UFS_SUPER_DEBUG_MORE */
+
+static struct super_operations ufs_super_ops;
+
+static char error_buf[1024];
+
+void ufs_error (struct super_block * sb, const char * function,
+ const char * fmt, ...)
+{
+ struct ufs_sb_private_info * uspi;
+ struct ufs_super_block_first * usb1;
+ va_list args;
+
+ uspi = sb->u.ufs_sb.s_uspi;
+ usb1 = ubh_get_usb_first(USPI_UBH);
+
+ if (!(sb->s_flags & MS_RDONLY)) {
+ usb1->fs_clean = UFS_FSBAD;
+ ubh_mark_buffer_dirty(USPI_UBH);
+ sb->s_dirt = 1;
+ sb->s_flags |= MS_RDONLY;
+ }
+ va_start (args, fmt);
+ vsprintf (error_buf, fmt, args);
+ va_end (args);
+ switch (sb->u.ufs_sb.s_mount_opt & UFS_MOUNT_ONERROR) {
+ case UFS_MOUNT_ONERROR_PANIC:
+ panic ("UFS-fs panic (device %s): %s: %s\n",
+ kdevname(sb->s_dev), function, error_buf);
+
+ case UFS_MOUNT_ONERROR_LOCK:
+ case UFS_MOUNT_ONERROR_UMOUNT:
+ case UFS_MOUNT_ONERROR_REPAIR:
+ printk (KERN_CRIT "UFS-fs error (device %s): %s: %s\n",
+ kdevname(sb->s_dev), function, error_buf);
+ }
+}
+
+void ufs_panic (struct super_block * sb, const char * function,
+ const char * fmt, ...)
+{
+ struct ufs_sb_private_info * uspi;
+ struct ufs_super_block_first * usb1;
+ va_list args;
+
+ uspi = sb->u.ufs_sb.s_uspi;
+ usb1 = ubh_get_usb_first(USPI_UBH);
+
+ if (!(sb->s_flags & MS_RDONLY)) {
+ usb1->fs_clean = UFS_FSBAD;
+ ubh_mark_buffer_dirty(USPI_UBH);
+ sb->s_dirt = 1;
+ }
+ va_start (args, fmt);
+ vsprintf (error_buf, fmt, args);
+ va_end (args);
+ sb->s_flags |= MS_RDONLY;
+ printk (KERN_CRIT "UFS-fs panic (device %s): %s: %s\n",
+ kdevname(sb->s_dev), function, error_buf);
+}
+
+void ufs_warning (struct super_block * sb, const char * function,
+ const char * fmt, ...)
+{
+ va_list args;
+
+ va_start (args, fmt);
+ vsprintf (error_buf, fmt, args);
+ va_end (args);
+ printk (KERN_WARNING "UFS-fs warning (device %s): %s: %s\n",
+ kdevname(sb->s_dev), function, error_buf);
+}
+
+static int ufs_parse_options (char * options, unsigned * mount_options)
+{
+ char * this_char;
+ char * value;
+
+ UFSD(("ENTER\n"))
+
+ if (!options)
+ return 1;
+
+ for (this_char = strtok (options, ",");
+ this_char != NULL;
+ this_char = strtok (NULL, ",")) {
+
+ if ((value = strchr (this_char, '=')) != NULL)
+ *value++ = 0;
+ if (!strcmp (this_char, "ufstype")) {
+ ufs_clear_opt (*mount_options, UFSTYPE);
+ if (!strcmp (value, "old"))
+ ufs_set_opt (*mount_options, UFSTYPE_OLD);
+ else if (!strcmp (value, "sun"))
+ ufs_set_opt (*mount_options, UFSTYPE_SUN);
+ else if (!strcmp (value, "44bsd"))
+ ufs_set_opt (*mount_options, UFSTYPE_44BSD);
+ else if (!strcmp (value, "nextstep"))
+ ufs_set_opt (*mount_options, UFSTYPE_NEXTSTEP);
+ else if (!strcmp (value, "nextstep-cd"))
+ ufs_set_opt (*mount_options, UFSTYPE_NEXTSTEP_CD);
+ else if (!strcmp (value, "openstep"))
+ ufs_set_opt (*mount_options, UFSTYPE_OPENSTEP);
+ else if (!strcmp (value, "sunx86"))
+ ufs_set_opt (*mount_options, UFSTYPE_SUNx86);
+ else if (!strcmp (value, "hp"))
+ ufs_set_opt (*mount_options, UFSTYPE_HP);
+ else {
+ printk ("UFS-fs: Invalid type option: %s\n", value);
+ return 0;
+ }
+ }
+ else if (!strcmp (this_char, "onerror")) {
+ ufs_clear_opt (*mount_options, ONERROR);
+ if (!strcmp (value, "panic"))
+ ufs_set_opt (*mount_options, ONERROR_PANIC);
+ else if (!strcmp (value, "lock"))
+ ufs_set_opt (*mount_options, ONERROR_LOCK);
+ else if (!strcmp (value, "umount"))
+ ufs_set_opt (*mount_options, ONERROR_UMOUNT);
+ else if (!strcmp (value, "repair")) {
+ printk("UFS-fs: Unable to do repair on error, "
+ "will lock lock instead \n");
+ ufs_set_opt (*mount_options, ONERROR_REPAIR);
+ }
+ else {
+ printk ("UFS-fs: Invalid action onerror: %s\n", value);
+ return 0;
+ }
+ }
+ else {
+ printk("UFS-fs: Invalid option: %s\n", this_char);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/*
+ * Read on-disk structures associated with cylinder groups
+ */
+int ufs_read_cylinder_structures (struct super_block * sb) {
+ struct ufs_sb_private_info * uspi;
+ struct ufs_buffer_head * ubh;
+ unsigned char * base, * space;
+ unsigned size, blks, i;
+
+ UFSD(("ENTER\n"))
+
+ uspi = sb->u.ufs_sb.s_uspi;
+
+ /*
+ * Read cs structures from (usually) first data block
+ * on the device.
+ */
+ size = uspi->s_cssize;
+ blks = (size + uspi->s_fsize - 1) >> uspi->s_fshift;
+ base = space = kmalloc(size, GFP_KERNEL);
+ if (!base)
+ goto failed;
+ for (i = 0; i < blks; i += uspi->s_fpb) {
+ size = uspi->s_bsize;
+ if (i + uspi->s_fpb > blks)
+ size = (blks - i) * uspi->s_fsize;
+ ubh = ubh_bread(sb, uspi->s_csaddr + i, size);
+ if (!ubh)
+ goto failed;
+ ubh_ubhcpymem (space, ubh, size);
+ sb->u.ufs_sb.s_csp[ufs_fragstoblks(i)] = (struct ufs_csum *)space;
+ space += size;
+ ubh_brelse (ubh);
+ ubh = NULL;
+ }
+
+ /*
+ * Read cylinder group (we read only first fragment from block
+ * at this time) and prepare internal data structures for cg caching.
+ */
+ if (!(sb->u.ufs_sb.s_ucg = kmalloc (sizeof(struct buffer_head *) * uspi->s_ncg, GFP_KERNEL)))
+ goto failed;
+ for (i = 0; i < uspi->s_ncg; i++)
+ sb->u.ufs_sb.s_ucg[i] = NULL;
+ for (i = 0; i < UFS_MAX_GROUP_LOADED; i++) {
+ sb->u.ufs_sb.s_ucpi[i] = NULL;
+ sb->u.ufs_sb.s_cgno[i] = UFS_CGNO_EMPTY;
+ }
+ for (i = 0; i < uspi->s_ncg; i++) {
+ UFSD(("read cg %u\n", i))
+ if (!(sb->u.ufs_sb.s_ucg[i] = sb_bread(sb, ufs_cgcmin(i))))
+ goto failed;
+ if (!ufs_cg_chkmagic (sb, (struct ufs_cylinder_group *) sb->u.ufs_sb.s_ucg[i]->b_data))
+ goto failed;
+#ifdef UFS_SUPER_DEBUG_MORE
+ ufs_print_cylinder_stuff(sb, (struct ufs_cylinder_group *) sb->u.ufs_sb.s_ucg[i]->b_data);
+#endif
+ }
+ for (i = 0; i < UFS_MAX_GROUP_LOADED; i++) {
+ if (!(sb->u.ufs_sb.s_ucpi[i] = kmalloc (sizeof(struct ufs_cg_private_info), GFP_KERNEL)))
+ goto failed;
+ sb->u.ufs_sb.s_cgno[i] = UFS_CGNO_EMPTY;
+ }
+ sb->u.ufs_sb.s_cg_loaded = 0;
+ UFSD(("EXIT\n"))
+ return 1;
+
+failed:
+ if (base) kfree (base);
+ if (sb->u.ufs_sb.s_ucg) {
+ for (i = 0; i < uspi->s_ncg; i++)
+ if (sb->u.ufs_sb.s_ucg[i]) brelse (sb->u.ufs_sb.s_ucg[i]);
+ kfree (sb->u.ufs_sb.s_ucg);
+ for (i = 0; i < UFS_MAX_GROUP_LOADED; i++)
+ if (sb->u.ufs_sb.s_ucpi[i]) kfree (sb->u.ufs_sb.s_ucpi[i]);
+ }
+ UFSD(("EXIT (FAILED)\n"))
+ return 0;
+}
+
+/*
+ * Put on-disk structures associated with cylinder groups and
+ * write them back to disk
+ */
+void ufs_put_cylinder_structures (struct super_block * sb) {
+ struct ufs_sb_private_info * uspi;
+ struct ufs_buffer_head * ubh;
+ unsigned char * base, * space;
+ unsigned blks, size, i;
+
+ UFSD(("ENTER\n"))
+
+ uspi = sb->u.ufs_sb.s_uspi;
+
+ size = uspi->s_cssize;
+ blks = (size + uspi->s_fsize - 1) >> uspi->s_fshift;
+ base = space = (char*) sb->u.ufs_sb.s_csp[0];
+ for (i = 0; i < blks; i += uspi->s_fpb) {
+ size = uspi->s_bsize;
+ if (i + uspi->s_fpb > blks)
+ size = (blks - i) * uspi->s_fsize;
+ ubh = ubh_bread(sb, uspi->s_csaddr + i, size);
+ ubh_memcpyubh (ubh, space, size);
+ space += size;
+ ubh_mark_buffer_uptodate (ubh, 1);
+ ubh_mark_buffer_dirty (ubh);
+ ubh_brelse (ubh);
+ }
+ for (i = 0; i < sb->u.ufs_sb.s_cg_loaded; i++) {
+ ufs_put_cylinder (sb, i);
+ kfree (sb->u.ufs_sb.s_ucpi[i]);
+ }
+ for (; i < UFS_MAX_GROUP_LOADED; i++)
+ kfree (sb->u.ufs_sb.s_ucpi[i]);
+ for (i = 0; i < uspi->s_ncg; i++)
+ brelse (sb->u.ufs_sb.s_ucg[i]);
+ kfree (sb->u.ufs_sb.s_ucg);
+ kfree (base);
+ UFSD(("EXIT\n"))
+}
+
+struct super_block * ufs_read_super (struct super_block * sb, void * data,
+ int silent)
+{
+ struct ufs_sb_private_info * uspi;
+ struct ufs_super_block_first * usb1;
+ struct ufs_super_block_second * usb2;
+ struct ufs_super_block_third * usb3;
+ struct ufs_buffer_head * ubh;
+ struct inode *inode;
+ unsigned block_size, super_block_size;
+ unsigned flags;
+
+ uspi = NULL;
+ ubh = NULL;
+ flags = 0;
+
+ UFSD(("ENTER\n"))
+
+ UFSD(("flag %u\n", (int)(sb->s_flags & MS_RDONLY)))
+
+#ifndef CONFIG_UFS_FS_WRITE
+ if (!(sb->s_flags & MS_RDONLY)) {
+ printk("ufs was compiled with read-only support, "
+ "can't be mounted as read-write\n");
+ goto failed;
+ }
+#endif
+ /*
+ * Set default mount options
+ * Parse mount options
+ */
+ sb->u.ufs_sb.s_mount_opt = 0;
+ ufs_set_opt (sb->u.ufs_sb.s_mount_opt, ONERROR_LOCK);
+ if (!ufs_parse_options ((char *) data, &sb->u.ufs_sb.s_mount_opt)) {
+ printk("wrong mount options\n");
+ goto failed;
+ }
+ if (!(sb->u.ufs_sb.s_mount_opt & UFS_MOUNT_UFSTYPE)) {
+ printk("You didn't specify the type of your ufs filesystem\n\n"
+ "mount -t ufs -o ufstype="
+ "sun|sunx86|44bsd|old|hp|nextstep|netxstep-cd|openstep ...\n\n"
+ ">>>WARNING<<< Wrong ufstype may corrupt your filesystem, "
+ "default is ufstype=old\n");
+ ufs_set_opt (sb->u.ufs_sb.s_mount_opt, UFSTYPE_OLD);
+ }
+
+ sb->u.ufs_sb.s_uspi = uspi =
+ kmalloc (sizeof(struct ufs_sb_private_info), GFP_KERNEL);
+ if (!uspi)
+ goto failed;
+
+ /* Keep 2Gig file limit. Some UFS variants need to override
+ this but as I don't know which I'll let those in the know loosen
+ the rules */
+
+ switch (sb->u.ufs_sb.s_mount_opt & UFS_MOUNT_UFSTYPE) {
+ case UFS_MOUNT_UFSTYPE_44BSD:
+ UFSD(("ufstype=44bsd\n"))
+ uspi->s_fsize = block_size = 512;
+ uspi->s_fmask = ~(512 - 1);
+ uspi->s_fshift = 9;
+ uspi->s_sbsize = super_block_size = 1536;
+ uspi->s_sbbase = 0;
+ flags |= UFS_DE_44BSD | UFS_UID_44BSD | UFS_ST_44BSD | UFS_CG_44BSD;
+ break;
+
+ case UFS_MOUNT_UFSTYPE_SUN:
+ UFSD(("ufstype=sun\n"))
+ uspi->s_fsize = block_size = 1024;
+ uspi->s_fmask = ~(1024 - 1);
+ uspi->s_fshift = 10;
+ uspi->s_sbsize = super_block_size = 2048;
+ uspi->s_sbbase = 0;
+ uspi->s_maxsymlinklen = 56;
+ flags |= UFS_DE_OLD | UFS_UID_EFT | UFS_ST_SUN | UFS_CG_SUN;
+ break;
+
+ case UFS_MOUNT_UFSTYPE_SUNx86:
+ UFSD(("ufstype=sunx86\n"))
+ uspi->s_fsize = block_size = 1024;
+ uspi->s_fmask = ~(1024 - 1);
+ uspi->s_fshift = 10;
+ uspi->s_sbsize = super_block_size = 2048;
+ uspi->s_sbbase = 0;
+ uspi->s_maxsymlinklen = 56;
+ flags |= UFS_DE_OLD | UFS_UID_EFT | UFS_ST_SUNx86 | UFS_CG_SUN;
+ break;
+
+ case UFS_MOUNT_UFSTYPE_OLD:
+ UFSD(("ufstype=old\n"))
+ uspi->s_fsize = block_size = 1024;
+ uspi->s_fmask = ~(1024 - 1);
+ uspi->s_fshift = 10;
+ uspi->s_sbsize = super_block_size = 2048;
+ uspi->s_sbbase = 0;
+ flags |= UFS_DE_OLD | UFS_UID_OLD | UFS_ST_OLD | UFS_CG_OLD;
+ if (!(sb->s_flags & MS_RDONLY)) {
+ printk(KERN_INFO "ufstype=old is supported read-only\n");
+ sb->s_flags |= MS_RDONLY;
+ }
+ break;
+
+ case UFS_MOUNT_UFSTYPE_NEXTSTEP:
+ UFSD(("ufstype=nextstep\n"))
+ uspi->s_fsize = block_size = 1024;
+ uspi->s_fmask = ~(1024 - 1);
+ uspi->s_fshift = 10;
+ uspi->s_sbsize = super_block_size = 2048;
+ uspi->s_sbbase = 0;
+ flags |= UFS_DE_OLD | UFS_UID_OLD | UFS_ST_OLD | UFS_CG_OLD;
+ if (!(sb->s_flags & MS_RDONLY)) {
+ printk(KERN_INFO "ufstype=nextstep is supported read-only\n");
+ sb->s_flags |= MS_RDONLY;
+ }
+ break;
+
+ case UFS_MOUNT_UFSTYPE_NEXTSTEP_CD:
+ UFSD(("ufstype=nextstep-cd\n"))
+ uspi->s_fsize = block_size = 2048;
+ uspi->s_fmask = ~(2048 - 1);
+ uspi->s_fshift = 11;
+ uspi->s_sbsize = super_block_size = 2048;
+ uspi->s_sbbase = 0;
+ flags |= UFS_DE_OLD | UFS_UID_OLD | UFS_ST_OLD | UFS_CG_OLD;
+ if (!(sb->s_flags & MS_RDONLY)) {
+ printk(KERN_INFO "ufstype=nextstep-cd is supported read-only\n");
+ sb->s_flags |= MS_RDONLY;
+ }
+ break;
+
+ case UFS_MOUNT_UFSTYPE_OPENSTEP:
+ UFSD(("ufstype=openstep\n"))
+ uspi->s_fsize = block_size = 1024;
+ uspi->s_fmask = ~(1024 - 1);
+ uspi->s_fshift = 10;
+ uspi->s_sbsize = super_block_size = 2048;
+ uspi->s_sbbase = 0;
+ flags |= UFS_DE_44BSD | UFS_UID_44BSD | UFS_ST_44BSD | UFS_CG_44BSD;
+ if (!(sb->s_flags & MS_RDONLY)) {
+ printk(KERN_INFO "ufstype=openstep is supported read-only\n");
+ sb->s_flags |= MS_RDONLY;
+ }
+ break;
+
+ case UFS_MOUNT_UFSTYPE_HP:
+ UFSD(("ufstype=hp\n"))
+ uspi->s_fsize = block_size = 1024;
+ uspi->s_fmask = ~(1024 - 1);
+ uspi->s_fshift = 10;
+ uspi->s_sbsize = super_block_size = 2048;
+ uspi->s_sbbase = 0;
+ flags |= UFS_DE_OLD | UFS_UID_OLD | UFS_ST_OLD | UFS_CG_OLD;
+ if (!(sb->s_flags & MS_RDONLY)) {
+ printk(KERN_INFO "ufstype=hp is supported read-only\n");
+ sb->s_flags |= MS_RDONLY;
+ }
+ break;
+ default:
+ printk("unknown ufstype\n");
+ goto failed;
+ }
+
+again:
+ if (set_blocksize (sb->s_dev, block_size)) {
+ printk(KERN_ERR "UFS: failed to set blocksize\n");
+ goto failed;
+ }
+
+ sb->s_blocksize = block_size;
+
+ /*
+ * read ufs super block from device
+ */
+ ubh = ubh_bread_uspi (uspi, sb, uspi->s_sbbase + UFS_SBLOCK/block_size, super_block_size);
+ if (!ubh)
+ goto failed;
+
+ usb1 = ubh_get_usb_first(USPI_UBH);
+ usb2 = ubh_get_usb_second(USPI_UBH);
+ usb3 = ubh_get_usb_third(USPI_UBH);
+
+ /*
+ * Check ufs magic number
+ */
+ switch (__constant_le32_to_cpu(usb3->fs_magic)) {
+ case UFS_MAGIC:
+ case UFS_MAGIC_LFN:
+ case UFS_MAGIC_FEA:
+ case UFS_MAGIC_4GB:
+ sb->u.ufs_sb.s_bytesex = BYTESEX_LE;
+ goto magic_found;
+ }
+ switch (__constant_be32_to_cpu(usb3->fs_magic)) {
+ case UFS_MAGIC:
+ case UFS_MAGIC_LFN:
+ case UFS_MAGIC_FEA:
+ case UFS_MAGIC_4GB:
+ sb->u.ufs_sb.s_bytesex = BYTESEX_BE;
+ goto magic_found;
+ }
+
+ if ((((sb->u.ufs_sb.s_mount_opt & UFS_MOUNT_UFSTYPE) == UFS_MOUNT_UFSTYPE_NEXTSTEP)
+ || ((sb->u.ufs_sb.s_mount_opt & UFS_MOUNT_UFSTYPE) == UFS_MOUNT_UFSTYPE_NEXTSTEP_CD)
+ || ((sb->u.ufs_sb.s_mount_opt & UFS_MOUNT_UFSTYPE) == UFS_MOUNT_UFSTYPE_OPENSTEP))
+ && uspi->s_sbbase < 256) {
+ ubh_brelse_uspi(uspi);
+ ubh = NULL;
+ uspi->s_sbbase += 8;
+ goto again;
+ }
+ printk("ufs_read_super: bad magic number\n");
+ goto failed;
+
+magic_found:
+ /*
+ * Check block and fragment sizes
+ */
+ uspi->s_bsize = fs32_to_cpu(sb, usb1->fs_bsize);
+ uspi->s_fsize = fs32_to_cpu(sb, usb1->fs_fsize);
+ uspi->s_sbsize = fs32_to_cpu(sb, usb1->fs_sbsize);
+ uspi->s_fmask = fs32_to_cpu(sb, usb1->fs_fmask);
+ uspi->s_fshift = fs32_to_cpu(sb, usb1->fs_fshift);
+
+ if (uspi->s_fsize & (uspi->s_fsize - 1)) {
+ printk(KERN_ERR "ufs_read_super: fragment size %u is not a power of 2\n",
+ uspi->s_fsize);
+ goto failed;
+ }
+ if (uspi->s_fsize < 512) {
+ printk(KERN_ERR "ufs_read_super: fragment size %u is too small\n",
+ uspi->s_fsize);
+ goto failed;
+ }
+ if (uspi->s_fsize > 4096) {
+ printk(KERN_ERR "ufs_read_super: fragment size %u is too large\n",
+ uspi->s_fsize);
+ goto failed;
+ }
+ if (uspi->s_bsize & (uspi->s_bsize - 1)) {
+ printk(KERN_ERR "ufs_read_super: block size %u is not a power of 2\n",
+ uspi->s_bsize);
+ goto failed;
+ }
+ if (uspi->s_bsize < 4096) {
+ printk(KERN_ERR "ufs_read_super: block size %u is too small\n",
+ uspi->s_bsize);
+ goto failed;
+ }
+ if (uspi->s_bsize / uspi->s_fsize > 8) {
+ printk(KERN_ERR "ufs_read_super: too many fragments per block (%u)\n",
+ uspi->s_bsize / uspi->s_fsize);
+ goto failed;
+ }
+ if (uspi->s_fsize != block_size || uspi->s_sbsize != super_block_size) {
+ ubh_brelse_uspi(uspi);
+ ubh = NULL;
+ block_size = uspi->s_fsize;
+ super_block_size = uspi->s_sbsize;
+ UFSD(("another value of block_size or super_block_size %u, %u\n", block_size, super_block_size))
+ goto again;
+ }
+
+#ifdef UFS_SUPER_DEBUG_MORE
+ ufs_print_super_stuff(sb, usb1, usb2, usb3);
+#endif
+
+ /*
+ * Check, if file system was correctly unmounted.
+ * If not, make it read only.
+ */
+ if (((flags & UFS_ST_MASK) == UFS_ST_44BSD) ||
+ ((flags & UFS_ST_MASK) == UFS_ST_OLD) ||
+ (((flags & UFS_ST_MASK) == UFS_ST_SUN ||
+ (flags & UFS_ST_MASK) == UFS_ST_SUNx86) &&
+ (ufs_get_fs_state(sb, usb1, usb3) == (UFS_FSOK - fs32_to_cpu(sb, usb1->fs_time))))) {
+ switch(usb1->fs_clean) {
+ case UFS_FSCLEAN:
+ UFSD(("fs is clean\n"))
+ break;
+ case UFS_FSSTABLE:
+ UFSD(("fs is stable\n"))
+ break;
+ case UFS_FSOSF1:
+ UFSD(("fs is DEC OSF/1\n"))
+ break;
+ case UFS_FSACTIVE:
+ printk("ufs_read_super: fs is active\n");
+ sb->s_flags |= MS_RDONLY;
+ break;
+ case UFS_FSBAD:
+ printk("ufs_read_super: fs is bad\n");
+ sb->s_flags |= MS_RDONLY;
+ break;
+ default:
+ printk("ufs_read_super: can't grok fs_clean 0x%x\n", usb1->fs_clean);
+ sb->s_flags |= MS_RDONLY;
+ break;
+ }
+ }
+ else {
+ printk("ufs_read_super: fs needs fsck\n");
+ sb->s_flags |= MS_RDONLY;
+ }
+
+ /*
+ * Read ufs_super_block into internal data structures
+ */
+ sb->s_blocksize = fs32_to_cpu(sb, usb1->fs_fsize);
+ sb->s_blocksize_bits = fs32_to_cpu(sb, usb1->fs_fshift);
+ sb->s_op = &ufs_super_ops;
+ sb->dq_op = NULL; /***/
+ sb->s_magic = fs32_to_cpu(sb, usb3->fs_magic);
+
+ uspi->s_sblkno = fs32_to_cpu(sb, usb1->fs_sblkno);
+ uspi->s_cblkno = fs32_to_cpu(sb, usb1->fs_cblkno);
+ uspi->s_iblkno = fs32_to_cpu(sb, usb1->fs_iblkno);
+ uspi->s_dblkno = fs32_to_cpu(sb, usb1->fs_dblkno);
+ uspi->s_cgoffset = fs32_to_cpu(sb, usb1->fs_cgoffset);
+ uspi->s_cgmask = fs32_to_cpu(sb, usb1->fs_cgmask);
+ uspi->s_size = fs32_to_cpu(sb, usb1->fs_size);
+ uspi->s_dsize = fs32_to_cpu(sb, usb1->fs_dsize);
+ uspi->s_ncg = fs32_to_cpu(sb, usb1->fs_ncg);
+ /* s_bsize already set */
+ /* s_fsize already set */
+ uspi->s_fpb = fs32_to_cpu(sb, usb1->fs_frag);
+ uspi->s_minfree = fs32_to_cpu(sb, usb1->fs_minfree);
+ uspi->s_bmask = fs32_to_cpu(sb, usb1->fs_bmask);
+ uspi->s_fmask = fs32_to_cpu(sb, usb1->fs_fmask);
+ uspi->s_bshift = fs32_to_cpu(sb, usb1->fs_bshift);
+ uspi->s_fshift = fs32_to_cpu(sb, usb1->fs_fshift);
+ uspi->s_fpbshift = fs32_to_cpu(sb, usb1->fs_fragshift);
+ uspi->s_fsbtodb = fs32_to_cpu(sb, usb1->fs_fsbtodb);
+ /* s_sbsize already set */
+ uspi->s_csmask = fs32_to_cpu(sb, usb1->fs_csmask);
+ uspi->s_csshift = fs32_to_cpu(sb, usb1->fs_csshift);
+ uspi->s_nindir = fs32_to_cpu(sb, usb1->fs_nindir);
+ uspi->s_inopb = fs32_to_cpu(sb, usb1->fs_inopb);
+ uspi->s_nspf = fs32_to_cpu(sb, usb1->fs_nspf);
+ uspi->s_npsect = ufs_get_fs_npsect(sb, usb1, usb3);
+ uspi->s_interleave = fs32_to_cpu(sb, usb1->fs_interleave);
+ uspi->s_trackskew = fs32_to_cpu(sb, usb1->fs_trackskew);
+ uspi->s_csaddr = fs32_to_cpu(sb, usb1->fs_csaddr);
+ uspi->s_cssize = fs32_to_cpu(sb, usb1->fs_cssize);
+ uspi->s_cgsize = fs32_to_cpu(sb, usb1->fs_cgsize);
+ uspi->s_ntrak = fs32_to_cpu(sb, usb1->fs_ntrak);
+ uspi->s_nsect = fs32_to_cpu(sb, usb1->fs_nsect);
+ uspi->s_spc = fs32_to_cpu(sb, usb1->fs_spc);
+ uspi->s_ipg = fs32_to_cpu(sb, usb1->fs_ipg);
+ uspi->s_fpg = fs32_to_cpu(sb, usb1->fs_fpg);
+ uspi->s_cpc = fs32_to_cpu(sb, usb2->fs_cpc);
+ uspi->s_contigsumsize = fs32_to_cpu(sb, usb3->fs_u2.fs_44.fs_contigsumsize);
+ uspi->s_qbmask = ufs_get_fs_qbmask(sb, usb3);
+ uspi->s_qfmask = ufs_get_fs_qfmask(sb, usb3);
+ uspi->s_postblformat = fs32_to_cpu(sb, usb3->fs_postblformat);
+ uspi->s_nrpos = fs32_to_cpu(sb, usb3->fs_nrpos);
+ uspi->s_postbloff = fs32_to_cpu(sb, usb3->fs_postbloff);
+ uspi->s_rotbloff = fs32_to_cpu(sb, usb3->fs_rotbloff);
+
+ /*
+ * Compute another frequently used values
+ */
+ uspi->s_fpbmask = uspi->s_fpb - 1;
+ uspi->s_apbshift = uspi->s_bshift - 2;
+ uspi->s_2apbshift = uspi->s_apbshift * 2;
+ uspi->s_3apbshift = uspi->s_apbshift * 3;
+ uspi->s_apb = 1 << uspi->s_apbshift;
+ uspi->s_2apb = 1 << uspi->s_2apbshift;
+ uspi->s_3apb = 1 << uspi->s_3apbshift;
+ uspi->s_apbmask = uspi->s_apb - 1;
+ uspi->s_nspfshift = uspi->s_fshift - UFS_SECTOR_BITS;
+ uspi->s_nspb = uspi->s_nspf << uspi->s_fpbshift;
+ uspi->s_inopf = uspi->s_inopb >> uspi->s_fpbshift;
+ uspi->s_bpf = uspi->s_fsize << 3;
+ uspi->s_bpfshift = uspi->s_fshift + 3;
+ uspi->s_bpfmask = uspi->s_bpf - 1;
+ if ((sb->u.ufs_sb.s_mount_opt & UFS_MOUNT_UFSTYPE) ==
+ UFS_MOUNT_UFSTYPE_44BSD)
+ uspi->s_maxsymlinklen =
+ fs32_to_cpu(sb, usb3->fs_u2.fs_44.fs_maxsymlinklen);
+
+ sb->u.ufs_sb.s_flags = flags;
+
+ inode = iget(sb, UFS_ROOTINO);
+ if (!inode || is_bad_inode(inode))
+ goto failed;
+ sb->s_root = d_alloc_root(inode);
+ if (!sb->s_root)
+ goto dalloc_failed;
+
+
+ /*
+ * Read cylinder group structures
+ */
+ if (!(sb->s_flags & MS_RDONLY))
+ if (!ufs_read_cylinder_structures(sb))
+ goto failed;
+
+ UFSD(("EXIT\n"))
+ return(sb);
+
+dalloc_failed:
+ iput(inode);
+failed:
+ if (ubh) ubh_brelse_uspi (uspi);
+ if (uspi) kfree (uspi);
+ UFSD(("EXIT (FAILED)\n"))
+ return(NULL);
+}
+
+void ufs_write_super (struct super_block * sb) {
+ struct ufs_sb_private_info * uspi;
+ struct ufs_super_block_first * usb1;
+ struct ufs_super_block_third * usb3;
+ unsigned flags;
+
+ UFSD(("ENTER\n"))
+ flags = sb->u.ufs_sb.s_flags;
+ uspi = sb->u.ufs_sb.s_uspi;
+ usb1 = ubh_get_usb_first(USPI_UBH);
+ usb3 = ubh_get_usb_third(USPI_UBH);
+
+ if (!(sb->s_flags & MS_RDONLY)) {
+ usb1->fs_time = cpu_to_fs32(sb, CURRENT_TIME);
+ if ((flags & UFS_ST_MASK) == UFS_ST_SUN
+ || (flags & UFS_ST_MASK) == UFS_ST_SUNx86)
+ ufs_set_fs_state(sb, usb1, usb3,
+ UFS_FSOK - fs32_to_cpu(sb, usb1->fs_time));
+ ubh_mark_buffer_dirty (USPI_UBH);
+ }
+ sb->s_dirt = 0;
+ UFSD(("EXIT\n"))
+}
+
+void ufs_put_super (struct super_block * sb)
+{
+ struct ufs_sb_private_info * uspi;
+
+ UFSD(("ENTER\n"))
+
+ uspi = sb->u.ufs_sb.s_uspi;
+
+ if (!(sb->s_flags & MS_RDONLY))
+ ufs_put_cylinder_structures (sb);
+
+ ubh_brelse_uspi (uspi);
+ kfree (sb->u.ufs_sb.s_uspi);
+ return;
+}
+
+
+int ufs_remount (struct super_block * sb, int * mount_flags, char * data)
+{
+ struct ufs_sb_private_info * uspi;
+ struct ufs_super_block_first * usb1;
+ struct ufs_super_block_third * usb3;
+ unsigned new_mount_opt, ufstype;
+ unsigned flags;
+
+ uspi = sb->u.ufs_sb.s_uspi;
+ flags = sb->u.ufs_sb.s_flags;
+ usb1 = ubh_get_usb_first(USPI_UBH);
+ usb3 = ubh_get_usb_third(USPI_UBH);
+
+ /*
+ * Allow the "check" option to be passed as a remount option.
+ * It is not possible to change ufstype option during remount
+ */
+ ufstype = sb->u.ufs_sb.s_mount_opt & UFS_MOUNT_UFSTYPE;
+ new_mount_opt = 0;
+ ufs_set_opt (new_mount_opt, ONERROR_LOCK);
+ if (!ufs_parse_options (data, &new_mount_opt))
+ return -EINVAL;
+ if (!(new_mount_opt & UFS_MOUNT_UFSTYPE)) {
+ new_mount_opt |= ufstype;
+ }
+ else if ((new_mount_opt & UFS_MOUNT_UFSTYPE) != ufstype) {
+ printk("ufstype can't be changed during remount\n");
+ return -EINVAL;
+ }
+
+ if ((*mount_flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY)) {
+ sb->u.ufs_sb.s_mount_opt = new_mount_opt;
+ return 0;
+ }
+
+ /*
+ * fs was mouted as rw, remounting ro
+ */
+ if (*mount_flags & MS_RDONLY) {
+ ufs_put_cylinder_structures(sb);
+ usb1->fs_time = cpu_to_fs32(sb, CURRENT_TIME);
+ if ((flags & UFS_ST_MASK) == UFS_ST_SUN
+ || (flags & UFS_ST_MASK) == UFS_ST_SUNx86)
+ ufs_set_fs_state(sb, usb1, usb3,
+ UFS_FSOK - fs32_to_cpu(sb, usb1->fs_time));
+ ubh_mark_buffer_dirty (USPI_UBH);
+ sb->s_dirt = 0;
+ sb->s_flags |= MS_RDONLY;
+ }
+ /*
+ * fs was mounted as ro, remounting rw
+ */
+ else {
+#ifndef CONFIG_UFS_FS_WRITE
+ printk("ufs was compiled with read-only support, "
+ "can't be mounted as read-write\n");
+ return -EINVAL;
+#else
+ if (ufstype != UFS_MOUNT_UFSTYPE_SUN &&
+ ufstype != UFS_MOUNT_UFSTYPE_44BSD &&
+ ufstype != UFS_MOUNT_UFSTYPE_SUNx86) {
+ printk("this ufstype is read-only supported\n");
+ return -EINVAL;
+ }
+ if (!ufs_read_cylinder_structures (sb)) {
+ printk("failed during remounting\n");
+ return -EPERM;
+ }
+ sb->s_flags &= ~MS_RDONLY;
+#endif
+ }
+ sb->u.ufs_sb.s_mount_opt = new_mount_opt;
+ return 0;
+}
+
+int ufs_statfs (struct super_block * sb, struct statfs * buf)
+{
+ struct ufs_sb_private_info * uspi;
+ struct ufs_super_block_first * usb1;
+
+ uspi = sb->u.ufs_sb.s_uspi;
+ usb1 = ubh_get_usb_first (USPI_UBH);
+
+ buf->f_type = UFS_MAGIC;
+ buf->f_bsize = sb->s_blocksize;
+ buf->f_blocks = uspi->s_dsize;
+ buf->f_bfree = ufs_blkstofrags(fs32_to_cpu(sb, usb1->fs_cstotal.cs_nbfree)) +
+ fs32_to_cpu(sb, usb1->fs_cstotal.cs_nffree);
+ buf->f_bavail = (buf->f_bfree > ((buf->f_blocks / 100) * uspi->s_minfree))
+ ? (buf->f_bfree - ((buf->f_blocks / 100) * uspi->s_minfree)) : 0;
+ buf->f_files = uspi->s_ncg * uspi->s_ipg;
+ buf->f_ffree = fs32_to_cpu(sb, usb1->fs_cstotal.cs_nifree);
+ buf->f_namelen = UFS_MAXNAMLEN;
+ return 0;
+}
+
+static struct super_operations ufs_super_ops = {
+ read_inode: ufs_read_inode,
+ write_inode: ufs_write_inode,
+ delete_inode: ufs_delete_inode,
+ put_super: ufs_put_super,
+ write_super: ufs_write_super,
+ statfs: ufs_statfs,
+ remount_fs: ufs_remount,
+};
+
+static DECLARE_FSTYPE_DEV(ufs_fs_type, "ufs", ufs_read_super);
+
+static int __init init_ufs_fs(void)
+{
+ return register_filesystem(&ufs_fs_type);
+}
+
+static void __exit exit_ufs_fs(void)
+{
+ unregister_filesystem(&ufs_fs_type);
+}
+
+EXPORT_NO_SYMBOLS;
+
+module_init(init_ufs_fs)
+module_exit(exit_ufs_fs)
+MODULE_LICENSE("GPL");
diff --git a/fs/ufs/swab.h b/fs/ufs/swab.h
new file mode 100644
index 00000000000000..4fc781eba71f6c
--- /dev/null
+++ b/fs/ufs/swab.h
@@ -0,0 +1,133 @@
+/*
+ * linux/fs/ufs/swab.h
+ *
+ * Copyright (C) 1997, 1998 Francois-Rene Rideau <fare@tunes.org>
+ * Copyright (C) 1998 Jakub Jelinek <jj@ultra.linux.cz>
+ * Copyright (C) 2001 Christoph Hellwig <hch@infradead.org>
+ */
+
+#ifndef _UFS_SWAB_H
+#define _UFS_SWAB_H
+
+/*
+ * Notes:
+ * HERE WE ASSUME EITHER BIG OR LITTLE ENDIAN UFSes
+ * in case there are ufs implementations that have strange bytesexes,
+ * you'll need to modify code here as well as in ufs_super.c and ufs_fs.h
+ * to support them.
+ */
+
+enum {
+ BYTESEX_LE,
+ BYTESEX_BE
+};
+
+static __inline u64
+fs64_to_cpu(struct super_block *sbp, u64 n)
+{
+ if (sbp->u.ufs_sb.s_bytesex == BYTESEX_LE)
+ return le64_to_cpu(n);
+ else
+ return be64_to_cpu(n);
+}
+
+static __inline u64
+cpu_to_fs64(struct super_block *sbp, u64 n)
+{
+ if (sbp->u.ufs_sb.s_bytesex == BYTESEX_LE)
+ return cpu_to_le64(n);
+ else
+ return cpu_to_be64(n);
+}
+
+static __inline u32
+fs64_add(struct super_block *sbp, u32 *n, int d)
+{
+ if (sbp->u.ufs_sb.s_bytesex == BYTESEX_LE)
+ return *n = cpu_to_le64(le64_to_cpu(*n)+d);
+ else
+ return *n = cpu_to_be64(be64_to_cpu(*n)+d);
+}
+
+static __inline u32
+fs64_sub(struct super_block *sbp, u32 *n, int d)
+{
+ if (sbp->u.ufs_sb.s_bytesex == BYTESEX_LE)
+ return *n = cpu_to_le64(le64_to_cpu(*n)-d);
+ else
+ return *n = cpu_to_be64(be64_to_cpu(*n)-d);
+}
+
+static __inline u32
+fs32_to_cpu(struct super_block *sbp, u32 n)
+{
+ if (sbp->u.ufs_sb.s_bytesex == BYTESEX_LE)
+ return le32_to_cpu(n);
+ else
+ return be32_to_cpu(n);
+}
+
+static __inline u32
+cpu_to_fs32(struct super_block *sbp, u32 n)
+{
+ if (sbp->u.ufs_sb.s_bytesex == BYTESEX_LE)
+ return cpu_to_le32(n);
+ else
+ return cpu_to_be32(n);
+}
+
+static __inline u32
+fs32_add(struct super_block *sbp, u32 *n, int d)
+{
+ if (sbp->u.ufs_sb.s_bytesex == BYTESEX_LE)
+ return *n = cpu_to_le32(le32_to_cpu(*n)+d);
+ else
+ return *n = cpu_to_be32(be32_to_cpu(*n)+d);
+}
+
+static __inline u32
+fs32_sub(struct super_block *sbp, u32 *n, int d)
+{
+ if (sbp->u.ufs_sb.s_bytesex == BYTESEX_LE)
+ return *n = cpu_to_le32(le32_to_cpu(*n)-d);
+ else
+ return *n = cpu_to_be32(be32_to_cpu(*n)-d);
+}
+
+static __inline u16
+fs16_to_cpu(struct super_block *sbp, u16 n)
+{
+ if (sbp->u.ufs_sb.s_bytesex == BYTESEX_LE)
+ return le16_to_cpu(n);
+ else
+ return be16_to_cpu(n);
+}
+
+static __inline u16
+cpu_to_fs16(struct super_block *sbp, u16 n)
+{
+ if (sbp->u.ufs_sb.s_bytesex == BYTESEX_LE)
+ return cpu_to_le16(n);
+ else
+ return cpu_to_be16(n);
+}
+
+static __inline u16
+fs16_add(struct super_block *sbp, u16 *n, int d)
+{
+ if (sbp->u.ufs_sb.s_bytesex == BYTESEX_LE)
+ return *n = cpu_to_le16(le16_to_cpu(*n)+d);
+ else
+ return *n = cpu_to_be16(be16_to_cpu(*n)+d);
+}
+
+static __inline u16
+fs16_sub(struct super_block *sbp, u16 *n, int d)
+{
+ if (sbp->u.ufs_sb.s_bytesex == BYTESEX_LE)
+ return *n = cpu_to_le16(le16_to_cpu(*n)-d);
+ else
+ return *n = cpu_to_be16(be16_to_cpu(*n)-d);
+}
+
+#endif /* _UFS_SWAB_H */
diff --git a/fs/ufs/symlink.c b/fs/ufs/symlink.c
new file mode 100644
index 00000000000000..59a44e22e94b1b
--- /dev/null
+++ b/fs/ufs/symlink.c
@@ -0,0 +1,45 @@
+/*
+ * linux/fs/ufs/symlink.c
+ *
+ * Only fast symlinks left here - the rest is done by generic code. AV, 1999
+ *
+ * Copyright (C) 1998
+ * Daniel Pirkl <daniel.pirkl@emai.cz>
+ * Charles University, Faculty of Mathematics and Physics
+ *
+ * from
+ *
+ * linux/fs/ext2/symlink.c
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * from
+ *
+ * linux/fs/minix/symlink.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * ext2 symlink handling code
+ */
+
+#include <linux/fs.h>
+
+static int ufs_readlink(struct dentry *dentry, char *buffer, int buflen)
+{
+ char *s = (char *)dentry->d_inode->u.ufs_i.i_u1.i_symlink;
+ return vfs_readlink(dentry, buffer, buflen, s);
+}
+
+static int ufs_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+ char *s = (char *)dentry->d_inode->u.ufs_i.i_u1.i_symlink;
+ return vfs_follow_link(nd, s);
+}
+
+struct inode_operations ufs_fast_symlink_inode_operations = {
+ readlink: ufs_readlink,
+ follow_link: ufs_follow_link,
+};
diff --git a/fs/ufs/truncate.c b/fs/ufs/truncate.c
new file mode 100644
index 00000000000000..6fd8df56254737
--- /dev/null
+++ b/fs/ufs/truncate.c
@@ -0,0 +1,466 @@
+/*
+ * linux/fs/ufs/truncate.c
+ *
+ * Copyright (C) 1998
+ * Daniel Pirkl <daniel.pirkl@email.cz>
+ * Charles University, Faculty of Mathematics and Physics
+ *
+ * from
+ *
+ * linux/fs/ext2/truncate.c
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * from
+ *
+ * linux/fs/minix/truncate.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * Big-endian to little-endian byte-swapping/bitmaps by
+ * David S. Miller (davem@caip.rutgers.edu), 1995
+ */
+
+/*
+ * Real random numbers for secure rm added 94/02/18
+ * Idea from Pierre del Perugia <delperug@gla.ecoledoc.ibp.fr>
+ */
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/ufs_fs.h>
+#include <linux/fcntl.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/locks.h>
+#include <linux/string.h>
+
+#include "swab.h"
+#include "util.h"
+
+#undef UFS_TRUNCATE_DEBUG
+
+#ifdef UFS_TRUNCATE_DEBUG
+#define UFSD(x) printk("(%s, %d), %s: ", __FILE__, __LINE__, __FUNCTION__); printk x;
+#else
+#define UFSD(x)
+#endif
+
+/*
+ * Secure deletion currently doesn't work. It interacts very badly
+ * with buffers shared with memory mappings, and for that reason
+ * can't be done in the truncate() routines. It should instead be
+ * done separately in "release()" before calling the truncate routines
+ * that will release the actual file blocks.
+ *
+ * Linus
+ */
+
+#define DIRECT_BLOCK ((inode->i_size + uspi->s_bsize - 1) >> uspi->s_bshift)
+#define DIRECT_FRAGMENT ((inode->i_size + uspi->s_fsize - 1) >> uspi->s_fshift)
+
+#define DATA_BUFFER_USED(bh) \
+ (atomic_read(&bh->b_count)>1 || buffer_locked(bh))
+
+static int ufs_trunc_direct (struct inode * inode)
+{
+ struct super_block * sb;
+ struct ufs_sb_private_info * uspi;
+ struct buffer_head * bh;
+ u32 * p;
+ unsigned frag1, frag2, frag3, frag4, block1, block2;
+ unsigned frag_to_free, free_count;
+ unsigned i, j, tmp;
+ int retry;
+
+ UFSD(("ENTER\n"))
+
+ sb = inode->i_sb;
+ uspi = sb->u.ufs_sb.s_uspi;
+
+ frag_to_free = 0;
+ free_count = 0;
+ retry = 0;
+
+ frag1 = DIRECT_FRAGMENT;
+ frag4 = min_t(u32, UFS_NDIR_FRAGMENT, inode->u.ufs_i.i_lastfrag);
+ frag2 = ((frag1 & uspi->s_fpbmask) ? ((frag1 | uspi->s_fpbmask) + 1) : frag1);
+ frag3 = frag4 & ~uspi->s_fpbmask;
+ block1 = block2 = 0;
+ if (frag2 > frag3) {
+ frag2 = frag4;
+ frag3 = frag4 = 0;
+ }
+ else if (frag2 < frag3) {
+ block1 = ufs_fragstoblks (frag2);
+ block2 = ufs_fragstoblks (frag3);
+ }
+
+ UFSD(("frag1 %u, frag2 %u, block1 %u, block2 %u, frag3 %u, frag4 %u\n", frag1, frag2, block1, block2, frag3, frag4))
+
+ if (frag1 >= frag2)
+ goto next1;
+
+ /*
+ * Free first free fragments
+ */
+ p = inode->u.ufs_i.i_u1.i_data + ufs_fragstoblks (frag1);
+ tmp = fs32_to_cpu(sb, *p);
+ if (!tmp )
+ ufs_panic (sb, "ufs_trunc_direct", "internal error");
+ frag1 = ufs_fragnum (frag1);
+ frag2 = ufs_fragnum (frag2);
+ for (j = frag1; j < frag2; j++) {
+ bh = sb_get_hash_table (sb, tmp + j);
+ if ((bh && DATA_BUFFER_USED(bh)) || tmp != fs32_to_cpu(sb, *p)) {
+ retry = 1;
+ brelse (bh);
+ goto next1;
+ }
+ bforget (bh);
+ }
+ inode->i_blocks -= (frag2-frag1) << uspi->s_nspfshift;
+ mark_inode_dirty(inode);
+ ufs_free_fragments (inode, tmp + frag1, frag2 - frag1);
+ frag_to_free = tmp + frag1;
+
+next1:
+ /*
+ * Free whole blocks
+ */
+ for (i = block1 ; i < block2; i++) {
+ p = inode->u.ufs_i.i_u1.i_data + i;
+ tmp = fs32_to_cpu(sb, *p);
+ if (!tmp)
+ continue;
+ for (j = 0; j < uspi->s_fpb; j++) {
+ bh = sb_get_hash_table(sb, tmp + j);
+ if ((bh && DATA_BUFFER_USED(bh)) || tmp != fs32_to_cpu(sb, *p)) {
+ retry = 1;
+ brelse (bh);
+ goto next2;
+ }
+ bforget (bh);
+ }
+ *p = 0;
+ inode->i_blocks -= uspi->s_nspb;
+ mark_inode_dirty(inode);
+ if (free_count == 0) {
+ frag_to_free = tmp;
+ free_count = uspi->s_fpb;
+ } else if (free_count > 0 && frag_to_free == tmp - free_count)
+ free_count += uspi->s_fpb;
+ else {
+ ufs_free_blocks (inode, frag_to_free, free_count);
+ frag_to_free = tmp;
+ free_count = uspi->s_fpb;
+ }
+next2:;
+ }
+
+ if (free_count > 0)
+ ufs_free_blocks (inode, frag_to_free, free_count);
+
+ if (frag3 >= frag4)
+ goto next3;
+
+ /*
+ * Free last free fragments
+ */
+ p = inode->u.ufs_i.i_u1.i_data + ufs_fragstoblks (frag3);
+ tmp = fs32_to_cpu(sb, *p);
+ if (!tmp )
+ ufs_panic(sb, "ufs_truncate_direct", "internal error");
+ frag4 = ufs_fragnum (frag4);
+ for (j = 0; j < frag4; j++) {
+ bh = sb_get_hash_table (sb, tmp + j);
+ if ((bh && DATA_BUFFER_USED(bh)) || tmp != fs32_to_cpu(sb, *p)) {
+ retry = 1;
+ brelse (bh);
+ goto next1;
+ }
+ bforget (bh);
+ }
+ *p = 0;
+ inode->i_blocks -= frag4 << uspi->s_nspfshift;
+ mark_inode_dirty(inode);
+ ufs_free_fragments (inode, tmp, frag4);
+ next3:
+
+ UFSD(("EXIT\n"))
+ return retry;
+}
+
+
+static int ufs_trunc_indirect (struct inode * inode, unsigned offset, u32 * p)
+{
+ struct super_block * sb;
+ struct ufs_sb_private_info * uspi;
+ struct ufs_buffer_head * ind_ubh;
+ struct buffer_head * bh;
+ u32 * ind;
+ unsigned indirect_block, i, j, tmp;
+ unsigned frag_to_free, free_count;
+ int retry;
+
+ UFSD(("ENTER\n"))
+
+ sb = inode->i_sb;
+ uspi = sb->u.ufs_sb.s_uspi;
+
+ frag_to_free = 0;
+ free_count = 0;
+ retry = 0;
+
+ tmp = fs32_to_cpu(sb, *p);
+ if (!tmp)
+ return 0;
+ ind_ubh = ubh_bread(sb, tmp, uspi->s_bsize);
+ if (tmp != fs32_to_cpu(sb, *p)) {
+ ubh_brelse (ind_ubh);
+ return 1;
+ }
+ if (!ind_ubh) {
+ *p = 0;
+ return 0;
+ }
+
+ indirect_block = (DIRECT_BLOCK > offset) ? (DIRECT_BLOCK - offset) : 0;
+ for (i = indirect_block; i < uspi->s_apb; i++) {
+ ind = ubh_get_addr32 (ind_ubh, i);
+ tmp = fs32_to_cpu(sb, *ind);
+ if (!tmp)
+ continue;
+ for (j = 0; j < uspi->s_fpb; j++) {
+ bh = sb_get_hash_table(sb, tmp + j);
+ if ((bh && DATA_BUFFER_USED(bh)) || tmp != fs32_to_cpu(sb, *ind)) {
+ retry = 1;
+ brelse (bh);
+ goto next;
+ }
+ bforget (bh);
+ }
+ *ind = 0;
+ ubh_mark_buffer_dirty(ind_ubh);
+ if (free_count == 0) {
+ frag_to_free = tmp;
+ free_count = uspi->s_fpb;
+ } else if (free_count > 0 && frag_to_free == tmp - free_count)
+ free_count += uspi->s_fpb;
+ else {
+ ufs_free_blocks (inode, frag_to_free, free_count);
+ frag_to_free = tmp;
+ free_count = uspi->s_fpb;
+ }
+ inode->i_blocks -= uspi->s_nspb;
+ mark_inode_dirty(inode);
+next:;
+ }
+
+ if (free_count > 0) {
+ ufs_free_blocks (inode, frag_to_free, free_count);
+ }
+ for (i = 0; i < uspi->s_apb; i++)
+ if (*ubh_get_addr32(ind_ubh,i))
+ break;
+ if (i >= uspi->s_apb) {
+ if (ubh_max_bcount(ind_ubh) != 1) {
+ retry = 1;
+ }
+ else {
+ tmp = fs32_to_cpu(sb, *p);
+ *p = 0;
+ inode->i_blocks -= uspi->s_nspb;
+ mark_inode_dirty(inode);
+ ufs_free_blocks (inode, tmp, uspi->s_fpb);
+ ubh_bforget(ind_ubh);
+ ind_ubh = NULL;
+ }
+ }
+ if (IS_SYNC(inode) && ind_ubh && ubh_buffer_dirty(ind_ubh)) {
+ ubh_ll_rw_block (WRITE, 1, &ind_ubh);
+ ubh_wait_on_buffer (ind_ubh);
+ }
+ ubh_brelse (ind_ubh);
+
+ UFSD(("EXIT\n"))
+
+ return retry;
+}
+
+static int ufs_trunc_dindirect (struct inode * inode, unsigned offset, u32 * p)
+{
+ struct super_block * sb;
+ struct ufs_sb_private_info * uspi;
+ struct ufs_buffer_head * dind_bh;
+ unsigned i, tmp, dindirect_block;
+ u32 * dind;
+ int retry = 0;
+
+ UFSD(("ENTER\n"))
+
+ sb = inode->i_sb;
+ uspi = sb->u.ufs_sb.s_uspi;
+
+ dindirect_block = (DIRECT_BLOCK > offset)
+ ? ((DIRECT_BLOCK - offset) >> uspi->s_apbshift) : 0;
+ retry = 0;
+
+ tmp = fs32_to_cpu(sb, *p);
+ if (!tmp)
+ return 0;
+ dind_bh = ubh_bread(sb, tmp, uspi->s_bsize);
+ if (tmp != fs32_to_cpu(sb, *p)) {
+ ubh_brelse (dind_bh);
+ return 1;
+ }
+ if (!dind_bh) {
+ *p = 0;
+ return 0;
+ }
+
+ for (i = dindirect_block ; i < uspi->s_apb ; i++) {
+ dind = ubh_get_addr32 (dind_bh, i);
+ tmp = fs32_to_cpu(sb, *dind);
+ if (!tmp)
+ continue;
+ retry |= ufs_trunc_indirect (inode, offset + (i << uspi->s_apbshift), dind);
+ ubh_mark_buffer_dirty(dind_bh);
+ }
+
+ for (i = 0; i < uspi->s_apb; i++)
+ if (*ubh_get_addr32 (dind_bh, i))
+ break;
+ if (i >= uspi->s_apb) {
+ if (ubh_max_bcount(dind_bh) != 1)
+ retry = 1;
+ else {
+ tmp = fs32_to_cpu(sb, *p);
+ *p = 0;
+ inode->i_blocks -= uspi->s_nspb;
+ mark_inode_dirty(inode);
+ ufs_free_blocks (inode, tmp, uspi->s_fpb);
+ ubh_bforget(dind_bh);
+ dind_bh = NULL;
+ }
+ }
+ if (IS_SYNC(inode) && dind_bh && ubh_buffer_dirty(dind_bh)) {
+ ubh_ll_rw_block (WRITE, 1, &dind_bh);
+ ubh_wait_on_buffer (dind_bh);
+ }
+ ubh_brelse (dind_bh);
+
+ UFSD(("EXIT\n"))
+
+ return retry;
+}
+
+static int ufs_trunc_tindirect (struct inode * inode)
+{
+ struct super_block * sb;
+ struct ufs_sb_private_info * uspi;
+ struct ufs_buffer_head * tind_bh;
+ unsigned tindirect_block, tmp, i;
+ u32 * tind, * p;
+ int retry;
+
+ UFSD(("ENTER\n"))
+
+ sb = inode->i_sb;
+ uspi = sb->u.ufs_sb.s_uspi;
+ retry = 0;
+
+ tindirect_block = (DIRECT_BLOCK > (UFS_NDADDR + uspi->s_apb + uspi->s_2apb))
+ ? ((DIRECT_BLOCK - UFS_NDADDR - uspi->s_apb - uspi->s_2apb) >> uspi->s_2apbshift) : 0;
+ p = inode->u.ufs_i.i_u1.i_data + UFS_TIND_BLOCK;
+ if (!(tmp = fs32_to_cpu(sb, *p)))
+ return 0;
+ tind_bh = ubh_bread (sb, tmp, uspi->s_bsize);
+ if (tmp != fs32_to_cpu(sb, *p)) {
+ ubh_brelse (tind_bh);
+ return 1;
+ }
+ if (!tind_bh) {
+ *p = 0;
+ return 0;
+ }
+
+ for (i = tindirect_block ; i < uspi->s_apb ; i++) {
+ tind = ubh_get_addr32 (tind_bh, i);
+ retry |= ufs_trunc_dindirect(inode, UFS_NDADDR +
+ uspi->s_apb + ((i + 1) << uspi->s_2apbshift), tind);
+ ubh_mark_buffer_dirty(tind_bh);
+ }
+ for (i = 0; i < uspi->s_apb; i++)
+ if (*ubh_get_addr32 (tind_bh, i))
+ break;
+ if (i >= uspi->s_apb) {
+ if (ubh_max_bcount(tind_bh) != 1)
+ retry = 1;
+ else {
+ tmp = fs32_to_cpu(sb, *p);
+ *p = 0;
+ inode->i_blocks -= uspi->s_nspb;
+ mark_inode_dirty(inode);
+ ufs_free_blocks (inode, tmp, uspi->s_fpb);
+ ubh_bforget(tind_bh);
+ tind_bh = NULL;
+ }
+ }
+ if (IS_SYNC(inode) && tind_bh && ubh_buffer_dirty(tind_bh)) {
+ ubh_ll_rw_block (WRITE, 1, &tind_bh);
+ ubh_wait_on_buffer (tind_bh);
+ }
+ ubh_brelse (tind_bh);
+
+ UFSD(("EXIT\n"))
+ return retry;
+}
+
+void ufs_truncate (struct inode * inode)
+{
+ struct super_block * sb;
+ struct ufs_sb_private_info * uspi;
+ struct buffer_head * bh;
+ unsigned offset;
+ int err, retry;
+
+ UFSD(("ENTER\n"))
+ sb = inode->i_sb;
+ uspi = sb->u.ufs_sb.s_uspi;
+
+ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)))
+ return;
+ if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
+ return;
+ while (1) {
+ retry = ufs_trunc_direct(inode);
+ retry |= ufs_trunc_indirect (inode, UFS_IND_BLOCK,
+ (u32 *) &inode->u.ufs_i.i_u1.i_data[UFS_IND_BLOCK]);
+ retry |= ufs_trunc_dindirect (inode, UFS_IND_BLOCK + uspi->s_apb,
+ (u32 *) &inode->u.ufs_i.i_u1.i_data[UFS_DIND_BLOCK]);
+ retry |= ufs_trunc_tindirect (inode);
+ if (!retry)
+ break;
+ if (IS_SYNC(inode) && (inode->i_state & I_DIRTY))
+ ufs_sync_inode (inode);
+ run_task_queue(&tq_disk);
+ yield();
+ }
+ offset = inode->i_size & uspi->s_fshift;
+ if (offset) {
+ bh = ufs_bread (inode, inode->i_size >> uspi->s_fshift, 0, &err);
+ if (bh) {
+ memset (bh->b_data + offset, 0, uspi->s_fsize - offset);
+ mark_buffer_dirty (bh);
+ brelse (bh);
+ }
+ }
+ inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ inode->u.ufs_i.i_lastfrag = DIRECT_FRAGMENT;
+ mark_inode_dirty(inode);
+ UFSD(("EXIT\n"))
+}
diff --git a/fs/ufs/util.c b/fs/ufs/util.c
new file mode 100644
index 00000000000000..065ebb2f8e2c11
--- /dev/null
+++ b/fs/ufs/util.c
@@ -0,0 +1,196 @@
+/*
+ * linux/fs/ufs/util.c
+ *
+ * Copyright (C) 1998
+ * Daniel Pirkl <daniel.pirkl@email.cz>
+ * Charles University, Faculty of Mathematics and Physics
+ */
+
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/locks.h>
+
+#include "swab.h"
+#include "util.h"
+
+#undef UFS_UTILS_DEBUG
+
+#ifdef UFS_UTILS_DEBUG
+#define UFSD(x) printk("(%s, %d), %s: ", __FILE__, __LINE__, __FUNCTION__); printk x;
+#else
+#define UFSD(x)
+#endif
+
+
+struct ufs_buffer_head * _ubh_bread_ (struct ufs_sb_private_info * uspi,
+ struct super_block *sb, unsigned fragment, unsigned size)
+{
+ struct ufs_buffer_head * ubh;
+ unsigned i, j, count;
+ if (size & ~uspi->s_fmask)
+ return NULL;
+ count = size >> uspi->s_fshift;
+ if (count > UFS_MAXFRAG)
+ return NULL;
+ ubh = (struct ufs_buffer_head *)
+ kmalloc (sizeof (struct ufs_buffer_head), GFP_KERNEL);
+ if (!ubh)
+ return NULL;
+ ubh->fragment = fragment;
+ ubh->count = count;
+ for (i = 0; i < count; i++)
+ if (!(ubh->bh[i] = sb_bread(sb, fragment + i)))
+ goto failed;
+ for (; i < UFS_MAXFRAG; i++)
+ ubh->bh[i] = NULL;
+ return ubh;
+failed:
+ for (j = 0; j < i; j++)
+ brelse (ubh->bh[j]);
+ kfree(ubh);
+ return NULL;
+}
+
+struct ufs_buffer_head * ubh_bread_uspi (struct ufs_sb_private_info * uspi,
+ struct super_block *sb, unsigned fragment, unsigned size)
+{
+ unsigned i, j, count;
+ if (size & ~uspi->s_fmask)
+ return NULL;
+ count = size >> uspi->s_fshift;
+ if (count <= 0 || count > UFS_MAXFRAG)
+ return NULL;
+ USPI_UBH->fragment = fragment;
+ USPI_UBH->count = count;
+ for (i = 0; i < count; i++)
+ if (!(USPI_UBH->bh[i] = sb_bread(sb, fragment + i)))
+ goto failed;
+ for (; i < UFS_MAXFRAG; i++)
+ USPI_UBH->bh[i] = NULL;
+ return USPI_UBH;
+failed:
+ for (j = 0; j < i; j++)
+ brelse (USPI_UBH->bh[j]);
+ return NULL;
+}
+
+void ubh_brelse (struct ufs_buffer_head * ubh)
+{
+ unsigned i;
+ if (!ubh)
+ return;
+ for (i = 0; i < ubh->count; i++)
+ brelse (ubh->bh[i]);
+ kfree (ubh);
+}
+
+void ubh_brelse_uspi (struct ufs_sb_private_info * uspi)
+{
+ unsigned i;
+ if (!USPI_UBH)
+ return;
+ for ( i = 0; i < USPI_UBH->count; i++ ) {
+ brelse (USPI_UBH->bh[i]);
+ USPI_UBH->bh[i] = NULL;
+ }
+}
+
+void ubh_mark_buffer_dirty (struct ufs_buffer_head * ubh)
+{
+ unsigned i;
+ if (!ubh)
+ return;
+ for ( i = 0; i < ubh->count; i++ )
+ mark_buffer_dirty (ubh->bh[i]);
+}
+
+void ubh_mark_buffer_uptodate (struct ufs_buffer_head * ubh, int flag)
+{
+ unsigned i;
+ if (!ubh)
+ return;
+ for ( i = 0; i < ubh->count; i++ )
+ mark_buffer_uptodate (ubh->bh[i], flag);
+}
+
+void ubh_ll_rw_block (int rw, unsigned nr, struct ufs_buffer_head * ubh[])
+{
+ unsigned i;
+ if (!ubh)
+ return;
+ for ( i = 0; i < nr; i++ )
+ ll_rw_block (rw, ubh[i]->count, ubh[i]->bh);
+}
+
+void ubh_wait_on_buffer (struct ufs_buffer_head * ubh)
+{
+ unsigned i;
+ if (!ubh)
+ return;
+ for ( i = 0; i < ubh->count; i++ )
+ wait_on_buffer (ubh->bh[i]);
+}
+
+unsigned ubh_max_bcount (struct ufs_buffer_head * ubh)
+{
+ unsigned i;
+ unsigned max = 0;
+ if (!ubh)
+ return 0;
+ for ( i = 0; i < ubh->count; i++ )
+ if ( atomic_read(&ubh->bh[i]->b_count) > max )
+ max = atomic_read(&ubh->bh[i]->b_count);
+ return max;
+}
+
+void ubh_bforget (struct ufs_buffer_head * ubh)
+{
+ unsigned i;
+ if (!ubh)
+ return;
+ for ( i = 0; i < ubh->count; i++ ) if ( ubh->bh[i] )
+ bforget (ubh->bh[i]);
+}
+
+int ubh_buffer_dirty (struct ufs_buffer_head * ubh)
+{
+ unsigned i;
+ unsigned result = 0;
+ if (!ubh)
+ return 0;
+ for ( i = 0; i < ubh->count; i++ )
+ result |= buffer_dirty(ubh->bh[i]);
+ return result;
+}
+
+void _ubh_ubhcpymem_(struct ufs_sb_private_info * uspi,
+ unsigned char * mem, struct ufs_buffer_head * ubh, unsigned size)
+{
+ unsigned len, bhno;
+ if (size > (ubh->count << uspi->s_fshift))
+ size = ubh->count << uspi->s_fshift;
+ bhno = 0;
+ while (size) {
+ len = min_t(unsigned int, size, uspi->s_fsize);
+ memcpy (mem, ubh->bh[bhno]->b_data, len);
+ mem += uspi->s_fsize;
+ size -= len;
+ bhno++;
+ }
+}
+
+void _ubh_memcpyubh_(struct ufs_sb_private_info * uspi,
+ struct ufs_buffer_head * ubh, unsigned char * mem, unsigned size)
+{
+ unsigned len, bhno;
+ if (size > (ubh->count << uspi->s_fshift))
+ size = ubh->count << uspi->s_fshift;
+ bhno = 0;
+ while (size) {
+ len = min_t(unsigned int, size, uspi->s_fsize);
+ memcpy (ubh->bh[bhno]->b_data, mem, len);
+ mem += uspi->s_fsize;
+ size -= len;
+ bhno++;
+ }
+}
diff --git a/fs/ufs/util.h b/fs/ufs/util.h
new file mode 100644
index 00000000000000..2e5d4760282941
--- /dev/null
+++ b/fs/ufs/util.h
@@ -0,0 +1,523 @@
+/*
+ * linux/fs/ufs/util.h
+ *
+ * Copyright (C) 1998
+ * Daniel Pirkl <daniel.pirkl@email.cz>
+ * Charles University, Faculty of Mathematics and Physics
+ */
+
+#include <linux/fs.h>
+#include "swab.h"
+
+
+/*
+ * some useful macros
+ */
+#define in_range(b,first,len) ((b)>=(first)&&(b)<(first)+(len))
+
+/*
+ * macros used for retyping
+ */
+#define UCPI_UBH ((struct ufs_buffer_head *)ucpi)
+#define USPI_UBH ((struct ufs_buffer_head *)uspi)
+
+
+
+/*
+ * macros used for accesing structures
+ */
+static inline s32
+ufs_get_fs_state(struct super_block *sb, struct ufs_super_block_first *usb1,
+ struct ufs_super_block_third *usb3)
+{
+ switch (sb->u.ufs_sb.s_flags & UFS_ST_MASK) {
+ case UFS_ST_SUN:
+ return fs32_to_cpu(sb, usb3->fs_u2.fs_sun.fs_state);
+ case UFS_ST_SUNx86:
+ return fs32_to_cpu(sb, usb1->fs_u1.fs_sunx86.fs_state);
+ case UFS_ST_44BSD:
+ default:
+ return fs32_to_cpu(sb, usb3->fs_u2.fs_44.fs_state);
+ }
+}
+
+static inline void
+ufs_set_fs_state(struct super_block *sb, struct ufs_super_block_first *usb1,
+ struct ufs_super_block_third *usb3, s32 value)
+{
+ switch (sb->u.ufs_sb.s_flags & UFS_ST_MASK) {
+ case UFS_ST_SUN:
+ usb3->fs_u2.fs_sun.fs_state = cpu_to_fs32(sb, value);
+ break;
+ case UFS_ST_SUNx86:
+ usb1->fs_u1.fs_sunx86.fs_state = cpu_to_fs32(sb, value);
+ break;
+ case UFS_ST_44BSD:
+ usb3->fs_u2.fs_44.fs_state = cpu_to_fs32(sb, value);
+ break;
+ }
+}
+
+static inline u32
+ufs_get_fs_npsect(struct super_block *sb, struct ufs_super_block_first *usb1,
+ struct ufs_super_block_third *usb3)
+{
+ if ((sb->u.ufs_sb.s_flags & UFS_ST_MASK) == UFS_ST_SUNx86)
+ return fs32_to_cpu(sb, usb3->fs_u2.fs_sunx86.fs_npsect);
+ else
+ return fs32_to_cpu(sb, usb1->fs_u1.fs_sun.fs_npsect);
+}
+
+static inline u64
+ufs_get_fs_qbmask(struct super_block *sb, struct ufs_super_block_third *usb3)
+{
+ u64 tmp;
+
+ switch (sb->u.ufs_sb.s_flags & UFS_ST_MASK) {
+ case UFS_ST_SUN:
+ ((u32 *)&tmp)[0] = usb3->fs_u2.fs_sun.fs_qbmask[0];
+ ((u32 *)&tmp)[1] = usb3->fs_u2.fs_sun.fs_qbmask[1];
+ break;
+ case UFS_ST_SUNx86:
+ ((u32 *)&tmp)[0] = usb3->fs_u2.fs_sunx86.fs_qbmask[0];
+ ((u32 *)&tmp)[1] = usb3->fs_u2.fs_sunx86.fs_qbmask[1];
+ break;
+ case UFS_ST_44BSD:
+ ((u32 *)&tmp)[0] = usb3->fs_u2.fs_44.fs_qbmask[0];
+ ((u32 *)&tmp)[1] = usb3->fs_u2.fs_44.fs_qbmask[1];
+ break;
+ }
+
+ return fs64_to_cpu(sb, tmp);
+}
+
+static inline u64
+ufs_get_fs_qfmask(struct super_block *sb, struct ufs_super_block_third *usb3)
+{
+ u64 tmp;
+
+ switch (sb->u.ufs_sb.s_flags & UFS_ST_MASK) {
+ case UFS_ST_SUN:
+ ((u32 *)&tmp)[0] = usb3->fs_u2.fs_sun.fs_qfmask[0];
+ ((u32 *)&tmp)[1] = usb3->fs_u2.fs_sun.fs_qfmask[1];
+ break;
+ case UFS_ST_SUNx86:
+ ((u32 *)&tmp)[0] = usb3->fs_u2.fs_sunx86.fs_qfmask[0];
+ ((u32 *)&tmp)[1] = usb3->fs_u2.fs_sunx86.fs_qfmask[1];
+ break;
+ case UFS_ST_44BSD:
+ ((u32 *)&tmp)[0] = usb3->fs_u2.fs_44.fs_qfmask[0];
+ ((u32 *)&tmp)[1] = usb3->fs_u2.fs_44.fs_qfmask[1];
+ break;
+ }
+
+ return fs64_to_cpu(sb, tmp);
+}
+
+static inline u16
+ufs_get_de_namlen(struct super_block *sb, struct ufs_dir_entry *de)
+{
+ if ((sb->u.ufs_sb.s_flags & UFS_DE_MASK) == UFS_DE_OLD)
+ return fs16_to_cpu(sb, de->d_u.d_namlen);
+ else
+ return de->d_u.d_44.d_namlen; /* XXX this seems wrong */
+}
+
+static inline void
+ufs_set_de_namlen(struct super_block *sb, struct ufs_dir_entry *de, u16 value)
+{
+ if ((sb->u.ufs_sb.s_flags & UFS_DE_MASK) == UFS_DE_OLD)
+ de->d_u.d_namlen = cpu_to_fs16(sb, value);
+ else
+ de->d_u.d_44.d_namlen = value; /* XXX this seems wrong */
+}
+
+static inline void
+ufs_set_de_type(struct super_block *sb, struct ufs_dir_entry *de, int mode)
+{
+ if ((sb->u.ufs_sb.s_flags & UFS_DE_MASK) != UFS_DE_44BSD)
+ return;
+
+ /*
+ * TODO turn this into a table lookup
+ */
+ switch (mode & S_IFMT) {
+ case S_IFSOCK:
+ de->d_u.d_44.d_type = DT_SOCK;
+ break;
+ case S_IFLNK:
+ de->d_u.d_44.d_type = DT_LNK;
+ break;
+ case S_IFREG:
+ de->d_u.d_44.d_type = DT_REG;
+ break;
+ case S_IFBLK:
+ de->d_u.d_44.d_type = DT_BLK;
+ break;
+ case S_IFDIR:
+ de->d_u.d_44.d_type = DT_DIR;
+ break;
+ case S_IFCHR:
+ de->d_u.d_44.d_type = DT_CHR;
+ break;
+ case S_IFIFO:
+ de->d_u.d_44.d_type = DT_FIFO;
+ break;
+ default:
+ de->d_u.d_44.d_type = DT_UNKNOWN;
+ }
+}
+
+static inline u32
+ufs_get_inode_uid(struct super_block *sb, struct ufs_inode *inode)
+{
+ switch (sb->u.ufs_sb.s_flags & UFS_UID_MASK) {
+ case UFS_UID_EFT:
+ return fs32_to_cpu(sb, inode->ui_u3.ui_sun.ui_uid);
+ case UFS_UID_44BSD:
+ return fs32_to_cpu(sb, inode->ui_u3.ui_44.ui_uid);
+ default:
+ return fs16_to_cpu(sb, inode->ui_u1.oldids.ui_suid);
+ }
+}
+
+static inline void
+ufs_set_inode_uid(struct super_block *sb, struct ufs_inode *inode, u32 value)
+{
+ switch (sb->u.ufs_sb.s_flags & UFS_UID_MASK) {
+ case UFS_UID_EFT:
+ inode->ui_u3.ui_sun.ui_uid = cpu_to_fs32(sb, value);
+ break;
+ case UFS_UID_44BSD:
+ inode->ui_u3.ui_44.ui_uid = cpu_to_fs32(sb, value);
+ break;
+ }
+ inode->ui_u1.oldids.ui_suid = cpu_to_fs16(sb, value);
+}
+
+static inline u32
+ufs_get_inode_gid(struct super_block *sb, struct ufs_inode *inode)
+{
+ switch (sb->u.ufs_sb.s_flags & UFS_UID_MASK) {
+ case UFS_UID_EFT:
+ return fs32_to_cpu(sb, inode->ui_u3.ui_sun.ui_gid);
+ case UFS_UID_44BSD:
+ return fs32_to_cpu(sb, inode->ui_u3.ui_44.ui_gid);
+ default:
+ return fs16_to_cpu(sb, inode->ui_u1.oldids.ui_sgid);
+ }
+}
+
+static inline void
+ufs_set_inode_gid(struct super_block *sb, struct ufs_inode *inode, u32 value)
+{
+ switch (sb->u.ufs_sb.s_flags & UFS_UID_MASK) {
+ case UFS_UID_EFT:
+ inode->ui_u3.ui_sun.ui_gid = cpu_to_fs32(sb, value);
+ break;
+ case UFS_UID_44BSD:
+ inode->ui_u3.ui_44.ui_gid = cpu_to_fs32(sb, value);
+ break;
+ }
+ inode->ui_u1.oldids.ui_sgid = cpu_to_fs16(sb, value);
+}
+
+
+/*
+ * These functions manipulate ufs buffers
+ */
+#define ubh_bread(sb,fragment,size) _ubh_bread_(uspi,sb,fragment,size)
+extern struct ufs_buffer_head * _ubh_bread_(struct ufs_sb_private_info *, struct super_block *, unsigned, unsigned);
+extern struct ufs_buffer_head * ubh_bread_uspi(struct ufs_sb_private_info *, struct super_block *, unsigned, unsigned);
+extern void ubh_brelse (struct ufs_buffer_head *);
+extern void ubh_brelse_uspi (struct ufs_sb_private_info *);
+extern void ubh_mark_buffer_dirty (struct ufs_buffer_head *);
+extern void ubh_mark_buffer_uptodate (struct ufs_buffer_head *, int);
+extern void ubh_ll_rw_block (int, unsigned, struct ufs_buffer_head **);
+extern void ubh_wait_on_buffer (struct ufs_buffer_head *);
+extern unsigned ubh_max_bcount (struct ufs_buffer_head *);
+extern void ubh_bforget (struct ufs_buffer_head *);
+extern int ubh_buffer_dirty (struct ufs_buffer_head *);
+#define ubh_ubhcpymem(mem,ubh,size) _ubh_ubhcpymem_(uspi,mem,ubh,size)
+extern void _ubh_ubhcpymem_(struct ufs_sb_private_info *, unsigned char *, struct ufs_buffer_head *, unsigned);
+#define ubh_memcpyubh(ubh,mem,size) _ubh_memcpyubh_(uspi,ubh,mem,size)
+extern void _ubh_memcpyubh_(struct ufs_sb_private_info *, struct ufs_buffer_head *, unsigned char *, unsigned);
+
+
+
+/*
+ * macros to get important structures from ufs_buffer_head
+ */
+#define ubh_get_usb_first(ubh) \
+ ((struct ufs_super_block_first *)((ubh)->bh[0]->b_data))
+
+#define ubh_get_usb_second(ubh) \
+ ((struct ufs_super_block_second *)(ubh)-> \
+ bh[UFS_SECTOR_SIZE >> uspi->s_fshift]->b_data + (UFS_SECTOR_SIZE & ~uspi->s_fmask))
+
+#define ubh_get_usb_third(ubh) \
+ ((struct ufs_super_block_third *)((ubh)-> \
+ bh[UFS_SECTOR_SIZE*2 >> uspi->s_fshift]->b_data + (UFS_SECTOR_SIZE*2 & ~uspi->s_fmask)))
+
+#define ubh_get_ucg(ubh) \
+ ((struct ufs_cylinder_group *)((ubh)->bh[0]->b_data))
+
+
+/*
+ * Extract byte from ufs_buffer_head
+ * Extract the bits for a block from a map inside ufs_buffer_head
+ */
+#define ubh_get_addr8(ubh,begin) \
+ ((u8*)(ubh)->bh[(begin) >> uspi->s_fshift]->b_data + \
+ ((begin) & ~uspi->s_fmask))
+
+#define ubh_get_addr16(ubh,begin) \
+ (((u16*)((ubh)->bh[(begin) >> (uspi->s_fshift-1)]->b_data)) + \
+ ((begin) & (uspi->fsize>>1) - 1)))
+
+#define ubh_get_addr32(ubh,begin) \
+ (((u32*)((ubh)->bh[(begin) >> (uspi->s_fshift-2)]->b_data)) + \
+ ((begin) & ((uspi->s_fsize>>2) - 1)))
+
+#define ubh_get_addr ubh_get_addr8
+
+#define ubh_blkmap(ubh,begin,bit) \
+ ((*ubh_get_addr(ubh, (begin) + ((bit) >> 3)) >> ((bit) & 7)) & (0xff >> (UFS_MAXFRAG - uspi->s_fpb)))
+
+
+/*
+ * Macros for access to superblock array structures
+ */
+#define ubh_postbl(ubh,cylno,i) \
+ ((uspi->s_postblformat != UFS_DYNAMICPOSTBLFMT) \
+ ? (*(__s16*)(ubh_get_addr(ubh, \
+ (unsigned)(&((struct ufs_super_block *)0)->fs_opostbl) \
+ + (((cylno) * 16 + (i)) << 1) ) )) \
+ : (*(__s16*)(ubh_get_addr(ubh, \
+ uspi->s_postbloff + (((cylno) * uspi->s_nrpos + (i)) << 1) ))))
+
+#define ubh_rotbl(ubh,i) \
+ ((uspi->s_postblformat != UFS_DYNAMICPOSTBLFMT) \
+ ? (*(__u8*)(ubh_get_addr(ubh, \
+ (unsigned)(&((struct ufs_super_block *)0)->fs_space) + (i)))) \
+ : (*(__u8*)(ubh_get_addr(ubh, uspi->s_rotbloff + (i)))))
+
+/*
+ * Determine the number of available frags given a
+ * percentage to hold in reserve.
+ */
+#define ufs_freespace(usb, percentreserved) \
+ (ufs_blkstofrags(fs32_to_cpu(sb, (usb)->fs_cstotal.cs_nbfree)) + \
+ fs32_to_cpu(sb, (usb)->fs_cstotal.cs_nffree) - (uspi->s_dsize * (percentreserved) / 100))
+
+/*
+ * Macros to access cylinder group array structures
+ */
+#define ubh_cg_blktot(ucpi,cylno) \
+ (*((__u32*)ubh_get_addr(UCPI_UBH, (ucpi)->c_btotoff + ((cylno) << 2))))
+
+#define ubh_cg_blks(ucpi,cylno,rpos) \
+ (*((__u16*)ubh_get_addr(UCPI_UBH, \
+ (ucpi)->c_boff + (((cylno) * uspi->s_nrpos + (rpos)) << 1 ))))
+
+/*
+ * Bitmap operations
+ * These functions work like classical bitmap operations.
+ * The difference is that we don't have the whole bitmap
+ * in one contiguous chunk of memory, but in several buffers.
+ * The parameters of each function are super_block, ufs_buffer_head and
+ * position of the beginning of the bitmap.
+ */
+#define ubh_setbit(ubh,begin,bit) \
+ (*ubh_get_addr(ubh, (begin) + ((bit) >> 3)) |= (1 << ((bit) & 7)))
+
+#define ubh_clrbit(ubh,begin,bit) \
+ (*ubh_get_addr (ubh, (begin) + ((bit) >> 3)) &= ~(1 << ((bit) & 7)))
+
+#define ubh_isset(ubh,begin,bit) \
+ (*ubh_get_addr (ubh, (begin) + ((bit) >> 3)) & (1 << ((bit) & 7)))
+
+#define ubh_isclr(ubh,begin,bit) (!ubh_isset(ubh,begin,bit))
+
+#define ubh_find_first_zero_bit(ubh,begin,size) _ubh_find_next_zero_bit_(uspi,ubh,begin,size,0)
+
+#define ubh_find_next_zero_bit(ubh,begin,size,offset) _ubh_find_next_zero_bit_(uspi,ubh,begin,size,offset)
+static inline unsigned _ubh_find_next_zero_bit_(
+ struct ufs_sb_private_info * uspi, struct ufs_buffer_head * ubh,
+ unsigned begin, unsigned size, unsigned offset)
+{
+ unsigned base, count, pos;
+
+ size -= offset;
+ begin <<= 3;
+ offset += begin;
+ base = offset >> uspi->s_bpfshift;
+ offset &= uspi->s_bpfmask;
+ for (;;) {
+ count = min_t(unsigned int, size + offset, uspi->s_bpf);
+ size -= count - offset;
+ pos = ext2_find_next_zero_bit (ubh->bh[base]->b_data, count, offset);
+ if (pos < count || !size)
+ break;
+ base++;
+ offset = 0;
+ }
+ return (base << uspi->s_bpfshift) + pos - begin;
+}
+
+static inline unsigned find_last_zero_bit (unsigned char * bitmap,
+ unsigned size, unsigned offset)
+{
+ unsigned bit, i;
+ unsigned char * mapp;
+ unsigned char map;
+
+ mapp = bitmap + (size >> 3);
+ map = *mapp--;
+ bit = 1 << (size & 7);
+ for (i = size; i > offset; i--) {
+ if ((map & bit) == 0)
+ break;
+ if ((i & 7) != 0) {
+ bit >>= 1;
+ } else {
+ map = *mapp--;
+ bit = 1 << 7;
+ }
+ }
+ return i;
+}
+
+#define ubh_find_last_zero_bit(ubh,begin,size,offset) _ubh_find_last_zero_bit_(uspi,ubh,begin,size,offset)
+static inline unsigned _ubh_find_last_zero_bit_(
+ struct ufs_sb_private_info * uspi, struct ufs_buffer_head * ubh,
+ unsigned begin, unsigned start, unsigned end)
+{
+ unsigned base, count, pos, size;
+
+ size = start - end;
+ begin <<= 3;
+ start += begin;
+ base = start >> uspi->s_bpfshift;
+ start &= uspi->s_bpfmask;
+ for (;;) {
+ count = min_t(unsigned int,
+ size + (uspi->s_bpf - start), uspi->s_bpf)
+ - (uspi->s_bpf - start);
+ size -= count;
+ pos = find_last_zero_bit (ubh->bh[base]->b_data,
+ start, start - count);
+ if (pos > start - count || !size)
+ break;
+ base--;
+ start = uspi->s_bpf;
+ }
+ return (base << uspi->s_bpfshift) + pos - begin;
+}
+
+#define ubh_isblockclear(ubh,begin,block) (!_ubh_isblockset_(uspi,ubh,begin,block))
+
+#define ubh_isblockset(ubh,begin,block) _ubh_isblockset_(uspi,ubh,begin,block)
+static inline int _ubh_isblockset_(struct ufs_sb_private_info * uspi,
+ struct ufs_buffer_head * ubh, unsigned begin, unsigned block)
+{
+ switch (uspi->s_fpb) {
+ case 8:
+ return (*ubh_get_addr (ubh, begin + block) == 0xff);
+ case 4:
+ return (*ubh_get_addr (ubh, begin + (block >> 1)) == (0x0f << ((block & 0x01) << 2)));
+ case 2:
+ return (*ubh_get_addr (ubh, begin + (block >> 2)) == (0x03 << ((block & 0x03) << 1)));
+ case 1:
+ return (*ubh_get_addr (ubh, begin + (block >> 3)) == (0x01 << (block & 0x07)));
+ }
+ return 0;
+}
+
+#define ubh_clrblock(ubh,begin,block) _ubh_clrblock_(uspi,ubh,begin,block)
+static inline void _ubh_clrblock_(struct ufs_sb_private_info * uspi,
+ struct ufs_buffer_head * ubh, unsigned begin, unsigned block)
+{
+ switch (uspi->s_fpb) {
+ case 8:
+ *ubh_get_addr (ubh, begin + block) = 0x00;
+ return;
+ case 4:
+ *ubh_get_addr (ubh, begin + (block >> 1)) &= ~(0x0f << ((block & 0x01) << 2));
+ return;
+ case 2:
+ *ubh_get_addr (ubh, begin + (block >> 2)) &= ~(0x03 << ((block & 0x03) << 1));
+ return;
+ case 1:
+ *ubh_get_addr (ubh, begin + (block >> 3)) &= ~(0x01 << ((block & 0x07)));
+ return;
+ }
+}
+
+#define ubh_setblock(ubh,begin,block) _ubh_setblock_(uspi,ubh,begin,block)
+static inline void _ubh_setblock_(struct ufs_sb_private_info * uspi,
+ struct ufs_buffer_head * ubh, unsigned begin, unsigned block)
+{
+ switch (uspi->s_fpb) {
+ case 8:
+ *ubh_get_addr(ubh, begin + block) = 0xff;
+ return;
+ case 4:
+ *ubh_get_addr(ubh, begin + (block >> 1)) |= (0x0f << ((block & 0x01) << 2));
+ return;
+ case 2:
+ *ubh_get_addr(ubh, begin + (block >> 2)) |= (0x03 << ((block & 0x03) << 1));
+ return;
+ case 1:
+ *ubh_get_addr(ubh, begin + (block >> 3)) |= (0x01 << ((block & 0x07)));
+ return;
+ }
+}
+
+static inline void ufs_fragacct (struct super_block * sb, unsigned blockmap,
+ unsigned * fraglist, int cnt)
+{
+ struct ufs_sb_private_info * uspi;
+ unsigned fragsize, pos;
+
+ uspi = sb->u.ufs_sb.s_uspi;
+
+ fragsize = 0;
+ for (pos = 0; pos < uspi->s_fpb; pos++) {
+ if (blockmap & (1 << pos)) {
+ fragsize++;
+ }
+ else if (fragsize > 0) {
+ fs32_add(sb, &fraglist[fragsize], cnt);
+ fragsize = 0;
+ }
+ }
+ if (fragsize > 0 && fragsize < uspi->s_fpb)
+ fs32_add(sb, &fraglist[fragsize], cnt);
+}
+
+#define ubh_scanc(ubh,begin,size,table,mask) _ubh_scanc_(uspi,ubh,begin,size,table,mask)
+static inline unsigned _ubh_scanc_(struct ufs_sb_private_info * uspi, struct ufs_buffer_head * ubh,
+ unsigned begin, unsigned size, unsigned char * table, unsigned char mask)
+{
+ unsigned rest, offset;
+ unsigned char * cp;
+
+
+ offset = begin & ~uspi->s_fmask;
+ begin >>= uspi->s_fshift;
+ for (;;) {
+ if ((offset + size) < uspi->s_fsize)
+ rest = size;
+ else
+ rest = uspi->s_fsize - offset;
+ size -= rest;
+ cp = ubh->bh[begin]->b_data + offset;
+ while ((table[*cp++] & mask) == 0 && --rest);
+ if (rest || !size)
+ break;
+ begin++;
+ offset = 0;
+ }
+ return (size + rest);
+}
diff --git a/fs/umsdos/Makefile b/fs/umsdos/Makefile
new file mode 100644
index 00000000000000..a894146ba59b47
--- /dev/null
+++ b/fs/umsdos/Makefile
@@ -0,0 +1,24 @@
+#
+# Makefile for the umsdos Unix-like filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (not a .c file).
+#
+# Note 2: the CFLAGS definitions are now in the main makefile.
+O_TARGET := umsdos.o
+
+obj-y := dir.o inode.o ioctl.o mangle.o namei.o rdir.o emd.o
+
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
+
+clean:
+ rm -f core *.o *.a *.s
+
+p:
+ proto *.c >/usr/include/linux/umsdos_fs.p
+
+doc:
+ nadoc -i -p umsdos.doc - /tmp/umsdos.mpg
diff --git a/fs/umsdos/README-WIP.txt b/fs/umsdos/README-WIP.txt
new file mode 100644
index 00000000000000..14643c738f6e41
--- /dev/null
+++ b/fs/umsdos/README-WIP.txt
@@ -0,0 +1,114 @@
+Changes by Matija Nalis (mnalis@jagor.srce.hr) on umsdos dentry fixing
+(started by Peter T. Waltenberg <peterw@karaka.chch.cri.nz>)
+(Final conversion to dentries Bill Hawes <whawes@star.net>)
+
+There is no warning any more.
+Both read-only and read-write stuff is fixed, both in
+msdos-compatibile mode, and in umsdos EMD mode, and it seems stable.
+
+Userland NOTE: new umsdos_progs (umssync, umssetup, udosctl & friends) that
+will compile and work on 2.2.x+ kernels and glibc based systems, as well as
+kernel patches and other umsdos related information may be found at
+http://linux.voyager.hr/umsdos/
+
+Information below is getting outdated slowly -- I'll fix it one day when I
+get enough time - there are more important things to fix right now.
+
+Legend: those lines marked with '+' on the beggining of line indicates it
+passed all of my tests, and performed perfect in all of them.
+
+Current status (010125) - UMSDOS 0.86j:
+
+(1) pure MSDOS (no --linux-.--- EMD file):
+
+READ:
++ readdir - works
++ lookup - works
++ read file - works
+
+WRITE:
++ creat file - works
++ unlink file - works
++ write file - works
++ rename file (same dir) - works
++ rename file (dif. dir) - works
++ rename dir (same dir) - works
++ rename dir (dif. dir) - works
++ mkdir - works
++ rmdir - works
+
+
+(2) umsdos (with --linux-.--- EMD file):
+
+READ:
++ readdir - works
++ lookup - works
++ permissions/owners stuff - works
++ long file names - works
++ read file - works
++ switching MSDOS/UMSDOS - works
++ switching UMSDOS/MSDOS - works
+- pseudo root things - works mostly. See notes below.
++ resolve symlink - works
++ dereference symlink - works
++ dangling symlink - works
++ hard links - works
++ special files (block/char devices, FIFOs, sockets...) - works
++ various umsdos ioctls - works
+
+
+WRITE:
++ create symlink - works
++ create hardlink - works
++ create file - works
++ create special file - works
++ write to file - works
++ rename file (same dir) - works
++ rename file (dif. dir) - works
++ rename hardlink (same dir) - works
+- rename hardlink (dif. dir) - works, but see notes below.
++ rename symlink (same dir) - works
++ rename symlink (dif. dir) - works
++ rename dir (same dir) - works
++ rename dir (dif. dir) - works
++ unlink file - works
++ notify_change (chown,perms) - works
++ notify_change for hardlinks - works
++ unlink hardlink - works
++ mkdir - works
++ rmdir - works
++ umssyncing (many ioctls) - works
+
+
+- CVF-FAT stuff (compressed DOS filesystem) - there is some support from Frank
+ Gockel <gockel@sent13.uni-duisburg.de> to use it even under umsdosfs, but I
+ have no way of testing it -- please let me know if there are problems specific
+ to umsdos (for instance, it works under msdosfs, but not under umsdosfs).
+
+
+Some current notes:
+
+Note: creating and using pseudo-hardlinks is always non-perfect, especially
+in filesystems that might be externally modified like umsdos. There is
+example is specs file about it. Specifically, moving directory which
+contains hardlinks will break them.
+
+Note: (about creating hardlinks in pseudoroot mode) - hardlinks created in
+pseudoroot mode are now again compatibile with 'normal' hardlinks, and vice
+versa. Thanks to Sorin Iordachescu <sorin@rodae.ro> for providing fix.
+See http://linux.voyager.hr/umsdos/hlbug.html for more info and upgrade
+procedure if you used broken versions...
+
+------------------------------------------------------------------------------
+
+Some general notes:
+
+Good idea when running development kernels is to have SysRq support compiled
+in kernel, and use Sync/Emergency-remount-RO if you bump into problems (like
+not being able to umount(2) umsdosfs, and because of it root partition also,
+or panics which force you to reboot etc.)
+
+I'm unfortunately somewhat out of time to read linux-kernel@vger, but I do
+check for messages having "UMSDOS" in the subject, and read them. I might
+miss some in all that volume, though. I should reply to any direct e-mail
+in few days. If I don't, probably I never got your message.
diff --git a/fs/umsdos/dir.c b/fs/umsdos/dir.c
new file mode 100644
index 00000000000000..8a54536a8beb84
--- /dev/null
+++ b/fs/umsdos/dir.c
@@ -0,0 +1,809 @@
+/*
+ * linux/fs/umsdos/dir.c
+ *
+ * Written 1993 by Jacques Gelinas
+ * Inspired from linux/fs/msdos/... : Werner Almesberger
+ *
+ * Extended MS-DOS directory handling functions
+ */
+
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/errno.h>
+#include <linux/stat.h>
+#include <linux/limits.h>
+#include <linux/umsdos_fs.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+
+#define UMSDOS_SPECIAL_DIRFPOS 3
+extern struct dentry *saved_root;
+extern struct inode *pseudo_root;
+
+/* #define UMSDOS_DEBUG_VERBOSE 1 */
+
+/*
+ * Dentry operations routines
+ */
+
+/* nothing for now ... */
+static int umsdos_dentry_validate(struct dentry *dentry, int flags)
+{
+ return 1;
+}
+
+/* for now, drop everything to force lookups ... */
+/* ITYM s/everything/& positive/... */
+static int umsdos_dentry_dput(struct dentry *dentry)
+{
+ struct inode *inode = dentry->d_inode;
+ if (inode) {
+ return 1;
+ }
+ return 0;
+}
+
+struct dentry_operations umsdos_dentry_operations =
+{
+ d_revalidate: umsdos_dentry_validate,
+ d_delete: umsdos_dentry_dput,
+};
+
+struct UMSDOS_DIR_ONCE {
+ void *dirbuf;
+ filldir_t filldir;
+ int count;
+ int stop;
+};
+
+/*
+ * Record a single entry the first call.
+ * Return -EINVAL the next one.
+ * NOTE: filldir DOES NOT use a dentry
+ */
+
+static int umsdos_dir_once ( void *buf,
+ const char *name,
+ int len,
+ loff_t offset,
+ ino_t ino,
+ unsigned type)
+{
+ int ret = -EINVAL;
+ struct UMSDOS_DIR_ONCE *d = (struct UMSDOS_DIR_ONCE *) buf;
+
+ if (d->count == 0) {
+ PRINTK ((KERN_DEBUG "dir_once :%.*s: offset %Ld\n",
+ len, name, offset));
+ ret = d->filldir (d->dirbuf, name, len, offset, ino, DT_UNKNOWN);
+ d->stop = ret < 0;
+ d->count = 1;
+ }
+ return ret;
+}
+
+
+/*
+ * Read count directory entries from directory filp
+ * Return a negative value from linux/errno.h.
+ * Return > 0 if success (the number of bytes written by filldir).
+ *
+ * This function is used by the normal readdir VFS entry point,
+ * and in order to get the directory entry from a file's dentry.
+ * See umsdos_dentry_to_entry() below.
+ */
+
+static int umsdos_readdir_x (struct inode *dir, struct file *filp,
+ void *dirbuf, struct umsdos_dirent *u_entry,
+ filldir_t filldir)
+{
+ struct dentry *demd;
+ off_t start_fpos;
+ int ret = 0;
+ loff_t pos;
+
+ umsdos_startlookup (dir);
+
+ if (filp->f_pos == UMSDOS_SPECIAL_DIRFPOS && dir == pseudo_root) {
+
+ /*
+ * We don't need to simulate this pseudo directory
+ * when umsdos_readdir_x is called for internal operation
+ * of umsdos. This is why dirent_in_fs is tested
+ */
+ /* #Specification: pseudo root / directory /DOS
+ * When umsdos operates in pseudo root mode (C:\linux is the
+ * linux root), it simulate a directory /DOS which points to
+ * the real root of the file system.
+ */
+
+ Printk ((KERN_WARNING "umsdos_readdir_x: pseudo_root thing UMSDOS_SPECIAL_DIRFPOS\n"));
+ if (filldir (dirbuf, "DOS", 3,
+ UMSDOS_SPECIAL_DIRFPOS, UMSDOS_ROOT_INO, DT_DIR) == 0) {
+ filp->f_pos++;
+ }
+ goto out_end;
+ }
+
+ if (filp->f_pos < 2 ||
+ (dir->i_ino != UMSDOS_ROOT_INO && filp->f_pos == 32)) {
+
+ int last_f_pos = filp->f_pos;
+ struct UMSDOS_DIR_ONCE bufk;
+
+ Printk (("umsdos_readdir_x: . or .. /mn/?\n"));
+
+ bufk.dirbuf = dirbuf;
+ bufk.filldir = filldir;
+ bufk.count = 0;
+
+ ret = fat_readdir (filp, &bufk, umsdos_dir_once);
+ if (last_f_pos > 0 && filp->f_pos > last_f_pos)
+ filp->f_pos = UMSDOS_SPECIAL_DIRFPOS;
+ if (u_entry != NULL)
+ u_entry->flags = 0;
+ goto out_end;
+ }
+
+ Printk (("umsdos_readdir_x: normal file /mn/?\n"));
+
+ /* get the EMD dentry */
+ demd = umsdos_get_emd_dentry(filp->f_dentry);
+ ret = PTR_ERR(demd);
+ if (IS_ERR(demd))
+ goto out_end;
+ ret = -EIO;
+ if (!demd->d_inode) {
+ printk(KERN_WARNING
+ "umsdos_readir_x: EMD file %s/%s not found\n",
+ demd->d_parent->d_name.name, demd->d_name.name);
+ goto out_dput;
+ }
+
+ pos = filp->f_pos;
+ start_fpos = filp->f_pos;
+
+ if (pos <= UMSDOS_SPECIAL_DIRFPOS + 1)
+ pos = 0;
+ ret = 0;
+ while (pos < demd->d_inode->i_size) {
+ off_t cur_f_pos = pos;
+ struct dentry *dret;
+ struct inode *inode;
+ struct umsdos_dirent entry;
+ struct umsdos_info info;
+
+ ret = -EIO;
+ if (umsdos_emd_dir_readentry (demd, &pos, &entry) != 0)
+ break;
+ if (entry.name_len == 0)
+ continue;
+#ifdef UMSDOS_DEBUG_VERBOSE
+if (entry.flags & UMSDOS_HLINK)
+printk("umsdos_readdir_x: %s/%s is hardlink\n",
+filp->f_dentry->d_name.name, entry.name);
+#endif
+
+ umsdos_parse (entry.name, entry.name_len, &info);
+ info.f_pos = cur_f_pos;
+ umsdos_manglename (&info);
+ /*
+ * Do a real lookup on the short name.
+ */
+ dret = umsdos_covered(filp->f_dentry, info.fake.fname,
+ info.fake.len);
+ ret = PTR_ERR(dret);
+ if (IS_ERR(dret))
+ break;
+ /*
+ * If the file wasn't found, remove it from the EMD.
+ */
+ inode = dret->d_inode;
+ if (!inode)
+ goto remove_name;
+#ifdef UMSDOS_DEBUG_VERBOSE
+if (inode->u.umsdos_i.i_is_hlink)
+printk("umsdos_readdir_x: %s/%s already resolved, ino=%ld\n",
+dret->d_parent->d_name.name, dret->d_name.name, inode->i_ino);
+#endif
+
+Printk (("Found %s/%s, ino=%ld, flags=%x\n",
+dret->d_parent->d_name.name, info.fake.fname, dret->d_inode->i_ino,
+entry.flags));
+ /* check whether to resolve a hard-link */
+ if ((entry.flags & UMSDOS_HLINK) &&
+ !inode->u.umsdos_i.i_is_hlink) {
+ dret = umsdos_solve_hlink (dret);
+ ret = PTR_ERR(dret);
+ if (IS_ERR(dret))
+ break;
+ inode = dret->d_inode;
+ if (!inode) {
+printk("umsdos_readdir_x: %s/%s negative after link\n",
+dret->d_parent->d_name.name, dret->d_name.name);
+ goto clean_up;
+ }
+ }
+
+ /* #Specification: pseudo root / reading real root
+ * The pseudo root (/linux) is logically
+ * erased from the real root. This means that
+ * ls /DOS, won't show "linux". This avoids
+ * infinite recursion (/DOS/linux/DOS/linux/...) while
+ * walking the file system.
+ */
+ if (inode != pseudo_root && !(entry.flags & UMSDOS_HIDDEN)) {
+ if (filldir (dirbuf, entry.name, entry.name_len,
+ cur_f_pos, inode->i_ino, DT_UNKNOWN) < 0) {
+ pos = cur_f_pos;
+ }
+Printk(("umsdos_readdir_x: got %s/%s, ino=%ld\n",
+dret->d_parent->d_name.name, dret->d_name.name, inode->i_ino));
+ if (u_entry != NULL)
+ *u_entry = entry;
+ dput(dret);
+ ret = 0;
+ break;
+ }
+ clean_up:
+ dput(dret);
+ continue;
+
+ remove_name:
+ /* #Specification: umsdos / readdir / not in MSDOS
+ * During a readdir operation, if the file is not
+ * in the MS-DOS directory any more, the entry is
+ * removed from the EMD file silently.
+ */
+#ifdef UMSDOS_PARANOIA
+printk("umsdos_readdir_x: %s/%s out of sync, erasing\n",
+filp->f_dentry->d_name.name, info.entry.name);
+#endif
+ ret = umsdos_delentry(filp->f_dentry, &info,
+ S_ISDIR(info.entry.mode));
+ if (ret)
+ printk(KERN_WARNING
+ "umsdos_readdir_x: delentry %s, err=%d\n",
+ info.entry.name, ret);
+ goto clean_up;
+ }
+ /*
+ * If the fillbuf has failed, f_pos is back to 0.
+ * To avoid getting back into the . and .. state
+ * (see comments at the beginning), we put back
+ * the special offset.
+ */
+ filp->f_pos = pos;
+ if (filp->f_pos == 0)
+ filp->f_pos = start_fpos;
+out_dput:
+ dput(demd);
+
+out_end:
+ umsdos_endlookup (dir);
+
+ Printk ((KERN_DEBUG "read dir %p pos %Ld ret %d\n",
+ dir, filp->f_pos, ret));
+ return ret;
+}
+
+
+/*
+ * Read count directory entries from directory filp.
+ * Return a negative value from linux/errno.h.
+ * Return 0 or positive if successful.
+ */
+
+static int UMSDOS_readdir (struct file *filp, void *dirbuf, filldir_t filldir)
+{
+ struct inode *dir = filp->f_dentry->d_inode;
+ int ret = 0, count = 0;
+ struct UMSDOS_DIR_ONCE bufk;
+
+ bufk.dirbuf = dirbuf;
+ bufk.filldir = filldir;
+ bufk.stop = 0;
+
+ Printk (("UMSDOS_readdir in\n"));
+ while (ret == 0 && bufk.stop == 0) {
+ struct umsdos_dirent entry;
+
+ bufk.count = 0;
+ ret = umsdos_readdir_x (dir, filp, &bufk, &entry,
+ umsdos_dir_once);
+ if (bufk.count == 0)
+ break;
+ count += bufk.count;
+ }
+ Printk (("UMSDOS_readdir out %d count %d pos %Ld\n",
+ ret, count, filp->f_pos));
+ return count ? : ret;
+}
+
+
+/*
+ * Complete the inode content with info from the EMD file.
+ *
+ * This function modifies the state of a dir inode. It decides
+ * whether the dir is a UMSDOS or DOS directory. This is done
+ * deeper in umsdos_patch_inode() called at the end of this function.
+ *
+ * Because it is does disk access, umsdos_patch_inode() may block.
+ * At the same time, another process may get here to initialise
+ * the same directory inode. There are three cases.
+ *
+ * 1) The inode is already initialised. We do nothing.
+ * 2) The inode is not initialised. We lock access and do it.
+ * 3) Like 2 but another process has locked the inode, so we try
+ * to lock it and check right afterward check whether
+ * initialisation is still needed.
+ *
+ *
+ * Thanks to the "mem" option of the kernel command line, it was
+ * possible to consistently reproduce this problem by limiting
+ * my memory to 4 MB and running X.
+ *
+ * Do this only if the inode is freshly read, because we will lose
+ * the current (updated) content.
+ *
+ * A lookup of a mount point directory yield the inode into
+ * the other fs, so we don't care about initialising it. iget()
+ * does this automatically.
+ */
+
+void umsdos_lookup_patch_new(struct dentry *dentry, struct umsdos_info *info)
+{
+ struct inode *inode = dentry->d_inode;
+ struct umsdos_dirent *entry = &info->entry;
+
+ /*
+ * This part of the initialization depends only on i_patched.
+ */
+ if (inode->u.umsdos_i.i_patched)
+ goto out;
+ inode->u.umsdos_i.i_patched = 1;
+ if (S_ISREG (entry->mode))
+ entry->mtime = inode->i_mtime;
+ inode->i_mode = entry->mode;
+ inode->i_rdev = to_kdev_t (entry->rdev);
+ inode->i_atime = entry->atime;
+ inode->i_ctime = entry->ctime;
+ inode->i_mtime = entry->mtime;
+ inode->i_uid = entry->uid;
+ inode->i_gid = entry->gid;
+
+ /* #Specification: umsdos / i_nlink
+ * The nlink field of an inode is maintained by the MSDOS file system
+ * for directory and by UMSDOS for other files. The logic is that
+ * MSDOS is already figuring out what to do for directories and
+ * does nothing for other files. For MSDOS, there are no hard links
+ * so all file carry nlink==1. UMSDOS use some info in the
+ * EMD file to plug the correct value.
+ */
+ if (!S_ISDIR (entry->mode)) {
+ if (entry->nlink > 0) {
+ inode->i_nlink = entry->nlink;
+ } else {
+ printk (KERN_ERR
+ "UMSDOS: lookup_patch entry->nlink < 1 ???\n");
+ }
+ }
+ /*
+ * The mode may have changed, so patch the inode again.
+ */
+ umsdos_patch_dentry_inode(dentry, info->f_pos);
+ umsdos_set_dirinfo_new(dentry, info->f_pos);
+
+out:
+ return;
+}
+
+
+/*
+ * Return != 0 if an entry is the pseudo DOS entry in the pseudo root.
+ */
+
+int umsdos_is_pseudodos (struct inode *dir, struct dentry *dentry)
+{
+ /* #Specification: pseudo root / DOS hard coded
+ * The pseudo sub-directory DOS in the pseudo root is hard coded.
+ * The name is DOS. This is done this way to help standardised
+ * the umsdos layout. The idea is that from now on /DOS is
+ * a reserved path and nobody will think of using such a path
+ * for a package.
+ */
+ return dir == pseudo_root
+ && dentry->d_name.len == 3
+ && dentry->d_name.name[0] == 'D'
+ && dentry->d_name.name[1] == 'O'
+ && dentry->d_name.name[2] == 'S';
+}
+
+
+/*
+ * Check whether a file exists in the current directory.
+ * Return 0 if OK, negative error code if not (ex: -ENOENT).
+ *
+ * fills dentry->d_inode with found inode, and increments its count.
+ * if not found, return -ENOENT.
+ */
+/* #Specification: umsdos / lookup
+ * A lookup for a file is done in two steps. First, we
+ * locate the file in the EMD file. If not present, we
+ * return an error code (-ENOENT). If it is there, we
+ * repeat the operation on the msdos file system. If
+ * this fails, it means that the file system is not in
+ * sync with the EMD file. We silently remove this
+ * entry from the EMD file, and return ENOENT.
+ */
+
+struct dentry *umsdos_lookup_x (struct inode *dir, struct dentry *dentry, int nopseudo)
+{
+ struct dentry *dret = NULL;
+ struct inode *inode;
+ int ret = -ENOENT;
+ struct umsdos_info info;
+
+#ifdef UMSDOS_DEBUG_VERBOSE
+printk("umsdos_lookup_x: looking for %s/%s\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+#endif
+
+ umsdos_startlookup (dir);
+ if (umsdos_is_pseudodos (dir, dentry)) {
+ /* #Specification: pseudo root / lookup(DOS)
+ * A lookup of DOS in the pseudo root will always succeed
+ * and return the inode of the real root.
+ */
+ Printk ((KERN_DEBUG "umsdos_lookup_x: following /DOS\n"));
+ inode = saved_root->d_inode;
+ goto out_add;
+ }
+
+ ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
+ if (ret) {
+printk("umsdos_lookup_x: %s/%s parse failed, ret=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, ret);
+ goto out;
+ }
+
+ ret = umsdos_findentry (dentry->d_parent, &info, 0);
+ if (ret) {
+if (ret != -ENOENT)
+printk("umsdos_lookup_x: %s/%s findentry failed, ret=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, ret);
+ goto out;
+ }
+Printk (("lookup %.*s pos %lu ret %d len %d ",
+info.fake.len, info.fake.fname, info.f_pos, ret, info.fake.len));
+
+ /* do a real lookup to get the short name ... */
+ dret = umsdos_covered(dentry->d_parent, info.fake.fname, info.fake.len);
+ ret = PTR_ERR(dret);
+ if (IS_ERR(dret)) {
+printk("umsdos_lookup_x: %s/%s real lookup failed, ret=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, ret);
+ goto out;
+ }
+ inode = dret->d_inode;
+ if (!inode)
+ goto out_remove;
+ umsdos_lookup_patch_new(dret, &info);
+#ifdef UMSDOS_DEBUG_VERBOSE
+printk("umsdos_lookup_x: found %s/%s, ino=%ld\n",
+dret->d_parent->d_name.name, dret->d_name.name, dret->d_inode->i_ino);
+#endif
+
+ /* Check for a hard link */
+ if ((info.entry.flags & UMSDOS_HLINK) &&
+ !inode->u.umsdos_i.i_is_hlink) {
+ dret = umsdos_solve_hlink (dret);
+ ret = PTR_ERR(dret);
+ if (IS_ERR(dret))
+ goto out;
+ ret = -ENOENT;
+ inode = dret->d_inode;
+ if (!inode) {
+printk("umsdos_lookup_x: %s/%s negative after link\n",
+dret->d_parent->d_name.name, dret->d_name.name);
+ goto out_dput;
+ }
+ }
+
+ if (inode == pseudo_root && !nopseudo) {
+ /* #Specification: pseudo root / dir lookup
+ * For the same reason as readdir, a lookup in /DOS for
+ * the pseudo root directory (linux) will fail.
+ */
+ /*
+ * This has to be allowed for resolving hard links
+ * which are recorded independently of the pseudo-root
+ * mode.
+ */
+printk("umsdos_lookup_x: skipping DOS/linux\n");
+ ret = -ENOENT;
+ goto out_dput;
+ }
+
+ /*
+ * We've found it OK. Now hash the dentry with the inode.
+ */
+out_add:
+ atomic_inc(&inode->i_count);
+ d_add (dentry, inode);
+ dentry->d_op = &umsdos_dentry_operations;
+ ret = 0;
+
+out_dput:
+ if (dret && dret != dentry)
+ d_drop(dret);
+ dput(dret);
+out:
+ umsdos_endlookup (dir);
+ return ERR_PTR(ret);
+
+out_remove:
+ printk(KERN_WARNING "UMSDOS: entry %s/%s out of sync, erased\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
+ umsdos_delentry (dentry->d_parent, &info, S_ISDIR (info.entry.mode));
+ ret = -ENOENT;
+ goto out_dput;
+}
+
+
+/*
+ * Check whether a file exists in the current directory.
+ * Return 0 if OK, negative error code if not (ex: -ENOENT).
+ *
+ * Called by VFS; should fill dentry->d_inode via d_add.
+ */
+
+struct dentry *UMSDOS_lookup (struct inode *dir, struct dentry *dentry)
+{
+ struct dentry *ret;
+
+ ret = umsdos_lookup_x (dir, dentry, 0);
+
+ /* Create negative dentry if not found. */
+ if (ret == ERR_PTR(-ENOENT)) {
+ Printk ((KERN_DEBUG
+ "UMSDOS_lookup: converting -ENOENT to negative\n"));
+ d_add (dentry, NULL);
+ dentry->d_op = &umsdos_dentry_operations;
+ ret = NULL;
+ }
+ return ret;
+}
+
+struct dentry *umsdos_covered(struct dentry *parent, char *name, int len)
+{
+ struct dentry *result, *dentry;
+ struct qstr qstr;
+
+ qstr.name = name;
+ qstr.len = len;
+ qstr.hash = full_name_hash(name, len);
+ result = ERR_PTR(-ENOMEM);
+ dentry = d_alloc(parent, &qstr);
+ if (dentry) {
+ /* XXXXXXXXXXXXXXXXXXX Race alert! */
+ result = UMSDOS_rlookup(parent->d_inode, dentry);
+ d_drop(dentry);
+ if (result)
+ goto out_fail;
+ return dentry;
+ }
+out:
+ return result;
+
+out_fail:
+ dput(dentry);
+ goto out;
+}
+
+/*
+ * Lookup or create a dentry from within the filesystem.
+ *
+ * We need to use this instead of lookup_dentry, as the
+ * directory semaphore lock is already held.
+ */
+struct dentry *umsdos_lookup_dentry(struct dentry *parent, char *name, int len,
+ int real)
+{
+ struct dentry *result, *dentry;
+ struct qstr qstr;
+
+ qstr.name = name;
+ qstr.len = len;
+ qstr.hash = full_name_hash(name, len);
+ result = d_lookup(parent, &qstr);
+ if (!result) {
+ result = ERR_PTR(-ENOMEM);
+ dentry = d_alloc(parent, &qstr);
+ if (dentry) {
+ result = real ?
+ UMSDOS_rlookup(parent->d_inode, dentry) :
+ UMSDOS_lookup(parent->d_inode, dentry);
+ if (result)
+ goto out_fail;
+ return dentry;
+ }
+ }
+out:
+ return result;
+
+out_fail:
+ dput(dentry);
+ goto out;
+}
+
+/*
+ * Return a path relative to our root.
+ */
+char * umsdos_d_path(struct dentry *dentry, char * buffer, int len)
+{
+ struct dentry * old_root;
+ char * path;
+
+ read_lock(&current->fs->lock);
+ old_root = dget(current->fs->root);
+ read_unlock(&current->fs->lock);
+ spin_lock(&dcache_lock);
+ path = __d_path(dentry, current->fs->rootmnt, dentry->d_sb->s_root, current->fs->rootmnt, buffer, len); /* FIXME: current->fs->rootmnt */
+ spin_unlock(&dcache_lock);
+
+ if (IS_ERR(path))
+ return path;
+
+ if (*path == '/')
+ path++; /* skip leading '/' */
+
+ if (current->fs->root->d_inode == pseudo_root)
+ {
+ *(path-1) = '/';
+ path -= (UMSDOS_PSDROOT_LEN+1);
+ memcpy(path, UMSDOS_PSDROOT_NAME, UMSDOS_PSDROOT_LEN);
+ }
+ dput(old_root);
+
+ return path;
+}
+
+/*
+ * Return the dentry which points to a pseudo-hardlink.
+ *
+ * it should try to find file it points to
+ * if file is found, return new dentry/inode
+ * The resolved inode will have i_is_hlink set.
+ *
+ * Note: the original dentry is always dput(), even if an error occurs.
+ */
+
+struct dentry *umsdos_solve_hlink (struct dentry *hlink)
+{
+ /* root is our root for resolving pseudo-hardlink */
+ struct dentry *base = hlink->d_sb->s_root;
+ struct dentry *dentry_dst;
+ char *path, *pt;
+ int len;
+ struct address_space *mapping = hlink->d_inode->i_mapping;
+ struct page *page;
+
+ page=read_cache_page(mapping,0,(filler_t *)mapping->a_ops->readpage,NULL);
+ dentry_dst=(struct dentry *)page;
+ if (IS_ERR(page))
+ goto out;
+ wait_on_page(page);
+ if (!Page_Uptodate(page))
+ goto async_fail;
+
+ dentry_dst = ERR_PTR(-ENOMEM);
+ path = (char *) kmalloc (PATH_MAX, GFP_KERNEL);
+ if (path == NULL)
+ goto out_release;
+ memcpy(path, kmap(page), hlink->d_inode->i_size);
+ kunmap(page);
+ page_cache_release(page);
+
+ len = hlink->d_inode->i_size;
+
+ /* start at root dentry */
+ dentry_dst = dget(base);
+ path[len] = '\0';
+
+ pt = path;
+ if (*path == '/')
+ pt++; /* skip leading '/' */
+
+ if (base->d_inode == pseudo_root)
+ pt += (UMSDOS_PSDROOT_LEN + 1);
+
+ while (1) {
+ struct dentry *dir = dentry_dst, *demd;
+ char *start = pt;
+ int real;
+
+ while (*pt != '\0' && *pt != '/') pt++;
+ len = (int) (pt - start);
+ if (*pt == '/') *pt++ = '\0';
+
+ real = 1;
+ demd = umsdos_get_emd_dentry(dir);
+ if (!IS_ERR(demd)) {
+ if (demd->d_inode)
+ real = 0;
+ dput(demd);
+ }
+
+#ifdef UMSDOS_DEBUG_VERBOSE
+printk ("umsdos_solve_hlink: dir %s/%s, name=%s, real=%d\n",
+dir->d_parent->d_name.name, dir->d_name.name, start, real);
+#endif
+ dentry_dst = umsdos_lookup_dentry(dir, start, len, real);
+/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
+ if (real)
+ d_drop(dir);
+ dput (dir);
+ if (IS_ERR(dentry_dst))
+ break;
+ /* not found? stop search ... */
+ if (!dentry_dst->d_inode) {
+ break;
+ }
+ if (*pt == '\0') /* we're finished! */
+ break;
+ } /* end while */
+
+ if (!IS_ERR(dentry_dst)) {
+ struct inode *inode = dentry_dst->d_inode;
+ if (inode) {
+ inode->u.umsdos_i.i_is_hlink = 1;
+#ifdef UMSDOS_DEBUG_VERBOSE
+printk ("umsdos_solve_hlink: resolved link %s/%s, ino=%ld\n",
+dentry_dst->d_parent->d_name.name, dentry_dst->d_name.name, inode->i_ino);
+#endif
+ } else {
+#ifdef UMSDOS_DEBUG_VERBOSE
+printk ("umsdos_solve_hlink: resolved link %s/%s negative!\n",
+dentry_dst->d_parent->d_name.name, dentry_dst->d_name.name);
+#endif
+ }
+ } else
+ printk(KERN_WARNING
+ "umsdos_solve_hlink: err=%ld\n", PTR_ERR(dentry_dst));
+ kfree (path);
+
+out:
+ dput(hlink); /* original hlink no longer needed */
+ return dentry_dst;
+
+async_fail:
+ dentry_dst = ERR_PTR(-EIO);
+out_release:
+ page_cache_release(page);
+ goto out;
+}
+
+
+struct file_operations umsdos_dir_operations =
+{
+ read: generic_read_dir,
+ readdir: UMSDOS_readdir,
+ ioctl: UMSDOS_ioctl_dir,
+};
+
+struct inode_operations umsdos_dir_inode_operations =
+{
+ create: UMSDOS_create,
+ lookup: UMSDOS_lookup,
+ link: UMSDOS_link,
+ unlink: UMSDOS_unlink,
+ symlink: UMSDOS_symlink,
+ mkdir: UMSDOS_mkdir,
+ rmdir: UMSDOS_rmdir,
+ mknod: UMSDOS_mknod,
+ rename: UMSDOS_rename,
+ setattr: UMSDOS_notify_change,
+};
diff --git a/fs/umsdos/emd.c b/fs/umsdos/emd.c
new file mode 100644
index 00000000000000..51d76ca2400913
--- /dev/null
+++ b/fs/umsdos/emd.c
@@ -0,0 +1,660 @@
+/*
+ * linux/fs/umsdos/emd.c
+ *
+ * Written 1993 by Jacques Gelinas
+ *
+ * Extended MS-DOS directory handling functions
+ */
+
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/msdos_fs.h>
+#include <linux/umsdos_fs.h>
+#include <linux/dcache.h>
+#include <linux/pagemap.h>
+#include <linux/delay.h>
+
+void put_entry (struct umsdos_dirent *p, struct umsdos_dirent *q)
+{
+ p->name_len = q->name_len;
+ p->flags = q->flags;
+ p->nlink = cpu_to_le16(q->nlink);
+ p->uid = cpu_to_le16(q->uid);
+ p->gid = cpu_to_le16(q->gid);
+ p->atime = cpu_to_le32(q->atime);
+ p->mtime = cpu_to_le32(q->mtime);
+ p->ctime = cpu_to_le32(q->ctime);
+ p->rdev = cpu_to_le16(q->rdev);
+ p->mode = cpu_to_le16(q->mode);
+}
+
+static void get_entry(struct umsdos_dirent *p, struct umsdos_dirent *q)
+{
+ p->name_len = q->name_len;
+ p->name[p->name_len]='\0';
+ p->flags = q->flags;
+ p->nlink = le16_to_cpu (q->nlink);
+ /* FIXME -- 32bit UID/GID issues */
+ p->uid = le16_to_cpu (q->uid);
+ p->gid = le16_to_cpu (q->gid);
+ p->atime = le32_to_cpu (q->atime);
+ p->mtime = le32_to_cpu (q->mtime);
+ p->ctime = le32_to_cpu (q->ctime);
+ p->rdev = le16_to_cpu (q->rdev);
+ p->mode = le16_to_cpu (q->mode);
+}
+
+/*
+ * Lookup the EMD dentry for a directory.
+ *
+ * Note: the caller must hold a lock on the parent directory.
+ */
+struct dentry *umsdos_get_emd_dentry(struct dentry *parent)
+{
+ struct dentry *demd;
+
+ demd = umsdos_lookup_dentry(parent, UMSDOS_EMD_FILE,
+ UMSDOS_EMD_NAMELEN, 1);
+ return demd;
+}
+
+/*
+ * Check whether a directory has an EMD file.
+ *
+ * Note: the caller must hold a lock on the parent directory.
+ */
+int umsdos_have_emd(struct dentry *dir)
+{
+ struct dentry *demd = umsdos_get_emd_dentry (dir);
+ int found = 0;
+
+ if (!IS_ERR(demd)) {
+ if (demd->d_inode)
+ found = 1;
+ dput(demd);
+ }
+ return found;
+}
+
+/*
+ * Create the EMD file for a directory if it doesn't
+ * already exist. Returns 0 or an error code.
+ *
+ * Note: the caller must hold a lock on the parent directory.
+ */
+int umsdos_make_emd(struct dentry *parent)
+{
+ struct dentry *demd = umsdos_get_emd_dentry(parent);
+ int err = PTR_ERR(demd);
+
+ if (IS_ERR(demd)) {
+ printk("umsdos_make_emd: can't get dentry in %s, err=%d\n",
+ parent->d_name.name, err);
+ goto out;
+ }
+
+ /* already created? */
+ err = 0;
+ if (demd->d_inode)
+ goto out_set;
+
+Printk(("umsdos_make_emd: creating EMD %s/%s\n",
+parent->d_name.name, demd->d_name.name));
+
+ err = msdos_create(parent->d_inode, demd, S_IFREG | 0777);
+ if (err) {
+ printk (KERN_WARNING
+ "umsdos_make_emd: create %s/%s failed, err=%d\n",
+ parent->d_name.name, demd->d_name.name, err);
+ }
+out_set:
+ dput(demd);
+out:
+ return err;
+}
+
+
+/*
+ * Read an entry from the EMD file.
+ * Support variable length record.
+ * Return -EIO if error, 0 if OK.
+ *
+ * does not change {d,i}_count
+ */
+
+int umsdos_emd_dir_readentry (struct dentry *demd, loff_t *pos, struct umsdos_dirent *entry)
+{
+ struct address_space *mapping = demd->d_inode->i_mapping;
+ struct page *page;
+ struct umsdos_dirent *p;
+ int offs = *pos & ~PAGE_CACHE_MASK;
+ int recsize;
+ int ret = 0;
+
+ page = read_cache_page(mapping, *pos>>PAGE_CACHE_SHIFT,
+ (filler_t*)mapping->a_ops->readpage, NULL);
+ if (IS_ERR(page))
+ goto sync_fail;
+ wait_on_page(page);
+ if (!Page_Uptodate(page))
+ goto async_fail;
+ p = (struct umsdos_dirent*)(kmap(page)+offs);
+
+ /* if this is an invalid entry (invalid name length), ignore it */
+ if( p->name_len > UMSDOS_MAXNAME )
+ {
+ printk (KERN_WARNING "Ignoring invalid EMD entry with size %d\n", entry->name_len);
+ p->name_len = 0;
+ ret = -ENAMETOOLONG; /* notify umssync(8) code that something is wrong */
+ /* FIXME: does not work if we did 'ls -l' before 'udosctl uls' ?! */
+ }
+
+ recsize = umsdos_evalrecsize(p->name_len);
+ if (offs + recsize > PAGE_CACHE_SIZE) {
+ struct page *page2;
+ int part = (char *)(page_address(page) + PAGE_CACHE_SIZE) - p->spare;
+ page2 = read_cache_page(mapping, 1+(*pos>>PAGE_CACHE_SHIFT),
+ (filler_t*)mapping->a_ops->readpage, NULL);
+ if (IS_ERR(page2)) {
+ kunmap(page);
+ page_cache_release(page);
+ page = page2;
+ goto sync_fail;
+ }
+ wait_on_page(page2);
+ if (!Page_Uptodate(page2)) {
+ kunmap(page);
+ page_cache_release(page2);
+ goto async_fail;
+ }
+ memcpy(entry->spare,p->spare,part);
+ memcpy(entry->spare+part,kmap(page2),
+ recsize+offs-PAGE_CACHE_SIZE);
+ kunmap(page2);
+ page_cache_release(page2);
+ } else
+ memcpy(entry->spare,p->spare,((char*)p+recsize)-p->spare);
+ get_entry(entry, p);
+ kunmap(page);
+ page_cache_release(page);
+ *pos += recsize;
+ return ret;
+async_fail:
+ page_cache_release(page);
+ page = ERR_PTR(-EIO);
+sync_fail:
+ return PTR_ERR(page);
+}
+
+
+/*
+ * Write an entry in the EMD file.
+ * Return 0 if OK, -EIO if some error.
+ *
+ * Note: the caller must hold a lock on the parent directory.
+ */
+int umsdos_writeentry (struct dentry *parent, struct umsdos_info *info,
+ int free_entry)
+{
+ struct inode *dir = parent->d_inode;
+ struct umsdos_dirent *entry = &info->entry;
+ struct dentry *emd_dentry;
+ int ret;
+ struct umsdos_dirent entry0,*p;
+ struct address_space *mapping;
+ struct page *page, *page2 = NULL;
+ int offs;
+
+ emd_dentry = umsdos_get_emd_dentry(parent);
+ ret = PTR_ERR(emd_dentry);
+ if (IS_ERR(emd_dentry))
+ goto out;
+ /* make sure there's an EMD file */
+ ret = -EIO;
+ if (!emd_dentry->d_inode) {
+ printk(KERN_WARNING
+ "umsdos_writeentry: no EMD file in %s/%s\n",
+ parent->d_parent->d_name.name, parent->d_name.name);
+ goto out_dput;
+ }
+
+ if (free_entry) {
+ /* #Specification: EMD file / empty entries
+ * Unused entries in the EMD file are identified
+ * by the name_len field equal to 0. However to
+ * help future extension (or bug correction :-( ),
+ * empty entries are filled with 0.
+ */
+ memset (&entry0, 0, sizeof (entry0));
+ entry = &entry0;
+ } else if (entry->name_len > 0) {
+ memset (entry->name + entry->name_len, '\0',
+ sizeof (entry->name) - entry->name_len);
+ /* #Specification: EMD file / spare bytes
+ * 10 bytes are unused in each record of the EMD. They
+ * are set to 0 all the time, so it will be possible
+ * to do new stuff and rely on the state of those
+ * bytes in old EMD files.
+ */
+ memset (entry->spare, 0, sizeof (entry->spare));
+ }
+
+ /* write the entry and update the parent timestamps */
+ mapping = emd_dentry->d_inode->i_mapping;
+ offs = info->f_pos & ~PAGE_CACHE_MASK;
+ ret = -ENOMEM;
+ page = grab_cache_page(mapping, info->f_pos>>PAGE_CACHE_SHIFT);
+ if (!page)
+ goto out_dput;
+ p = (struct umsdos_dirent *) (page_address(page) + offs);
+ if (offs + info->recsize > PAGE_CACHE_SIZE) {
+ ret = mapping->a_ops->prepare_write(NULL,page,offs,
+ PAGE_CACHE_SIZE);
+ if (ret)
+ goto out_unlock;
+ page2 = grab_cache_page(mapping,
+ (info->f_pos>>PAGE_CACHE_SHIFT)+1);
+ if (!page2)
+ goto out_unlock2;
+ ret = mapping->a_ops->prepare_write(NULL,page2,0,
+ offs+info->recsize-PAGE_CACHE_SIZE);
+ if (ret)
+ goto out_unlock3;
+ put_entry (p, entry);
+ memcpy(p->spare,entry->spare,
+ (char *)(page_address(page) + PAGE_CACHE_SIZE) - p->spare);
+ memcpy(page_address(page2),
+ ((char*)entry)+PAGE_CACHE_SIZE-offs,
+ offs+info->recsize-PAGE_CACHE_SIZE);
+ ret = mapping->a_ops->commit_write(NULL,page2,0,
+ offs+info->recsize-PAGE_CACHE_SIZE);
+ if (ret)
+ goto out_unlock3;
+ ret = mapping->a_ops->commit_write(NULL,page,offs,
+ PAGE_CACHE_SIZE);
+ UnlockPage(page2);
+ page_cache_release(page2);
+ if (ret)
+ goto out_unlock;
+ } else {
+ ret = mapping->a_ops->prepare_write(NULL,page,offs,
+ offs + info->recsize);
+ if (ret)
+ goto out_unlock;
+ put_entry (p, entry);
+ memcpy(p->spare,entry->spare,((char*)p+info->recsize)-p->spare);
+ ret = mapping->a_ops->commit_write(NULL,page,offs,
+ offs + info->recsize);
+ if (ret)
+ goto out_unlock;
+ }
+ UnlockPage(page);
+ page_cache_release(page);
+
+ dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+ mark_inode_dirty(dir);
+
+out_dput:
+ dput(emd_dentry);
+out:
+ Printk (("umsdos_writeentry /mn/: returning %d...\n", ret));
+ return ret;
+out_unlock3:
+ UnlockPage(page2);
+ page_cache_release(page2);
+out_unlock2:
+ ClearPageUptodate(page);
+ kunmap(page);
+out_unlock:
+ UnlockPage(page);
+ page_cache_release(page);
+ printk ("UMSDOS: problem with EMD file: can't write\n");
+ goto out_dput;
+}
+
+/*
+ * General search, locate a name in the EMD file or an empty slot to
+ * store it. if info->entry.name_len == 0, search the first empty
+ * slot (of the proper size).
+ *
+ * Return 0 if found, -ENOENT if not found, another error code if
+ * other problem.
+ *
+ * So this routine is used to either find an existing entry or to
+ * create a new one, while making sure it is a new one. After you
+ * get -ENOENT, you make sure the entry is stuffed correctly and
+ * call umsdos_writeentry().
+ *
+ * To delete an entry, you find it, zero out the entry (memset)
+ * and call umsdos_writeentry().
+ *
+ * All this to say that umsdos_writeentry must be called after this
+ * function since it relies on the f_pos field of info.
+ *
+ * Note: the caller must hold a lock on the parent directory.
+ */
+/* #Specification: EMD file structure
+ * The EMD file uses a fairly simple layout. It is made of records
+ * (UMSDOS_REC_SIZE == 64). When a name can't be written in a single
+ * record, multiple contiguous records are allocated.
+ */
+
+static int umsdos_find (struct dentry *demd, struct umsdos_info *info)
+{
+ struct umsdos_dirent *entry = &info->entry;
+ int recsize = info->recsize;
+ struct inode *emd_dir;
+ int ret = -ENOENT;
+ struct {
+ off_t posok; /* Position available to store the entry */
+ off_t one; /* One empty position -> maybe <- large enough */
+ } empty;
+ int found = 0;
+ int empty_size = 0;
+ struct address_space *mapping;
+ filler_t *readpage;
+ struct page *page = NULL;
+ int index = -1;
+ int offs = PAGE_CACHE_SIZE,max_offs = PAGE_CACHE_SIZE;
+ char *p = NULL;
+ loff_t pos = 0;
+
+ /* make sure there's an EMD file ... */
+ ret = -ENOENT;
+ emd_dir = demd->d_inode;
+ if (!emd_dir)
+ goto out_dput;
+ mapping = emd_dir->i_mapping;
+ readpage = (filler_t*)mapping->a_ops->readpage;
+
+ empty.posok = emd_dir->i_size;
+ while (1) {
+ struct umsdos_dirent *rentry;
+ int entry_size;
+
+ if (offs >= max_offs) {
+ if (page) {
+ kunmap(page);
+ page_cache_release(page);
+ page = NULL;
+ }
+ if (pos >= emd_dir->i_size) {
+ info->f_pos = empty.posok;
+ break;
+ }
+ if (++index == (emd_dir->i_size>>PAGE_CACHE_SHIFT))
+ max_offs = emd_dir->i_size & ~PAGE_CACHE_MASK;
+ offs -= PAGE_CACHE_SIZE;
+ page = read_cache_page(mapping,index,readpage,NULL);
+ if (IS_ERR(page))
+ goto sync_fail;
+ wait_on_page(page);
+ if (!Page_Uptodate(page))
+ goto async_fail;
+ p = kmap(page);
+ }
+
+ rentry = (struct umsdos_dirent *)(p+offs);
+
+ if (rentry->name_len == 0) {
+ /* We are looking for an empty section at least */
+ /* as large as recsize. */
+ if (entry->name_len == 0) {
+ info->f_pos = pos;
+ ret = 0;
+ break;
+ }
+ offs += UMSDOS_REC_SIZE;
+ pos += UMSDOS_REC_SIZE;
+ if (found)
+ continue;
+ if (!empty_size)
+ empty.one = pos-UMSDOS_REC_SIZE;
+ empty_size += UMSDOS_REC_SIZE;
+ if (empty_size == recsize) {
+ /* Here is a large enough section. */
+ empty.posok = empty.one;
+ found = 1;
+ }
+ continue;
+ }
+
+ entry_size = umsdos_evalrecsize(rentry->name_len);
+ if (entry_size > PAGE_CACHE_SIZE)
+ goto async_fail;
+ empty_size = 0;
+ if (entry->name_len != rentry->name_len)
+ goto skip_it;
+
+ if (entry_size + offs > PAGE_CACHE_SIZE) {
+ /* Sucker spans the page boundary */
+ int len = (p+PAGE_CACHE_SIZE)-rentry->name;
+ struct page *next_page;
+ char *q;
+ next_page = read_cache_page(mapping,index+1,readpage,NULL);
+ if (IS_ERR(next_page)) {
+ page_cache_release(page);
+ page = next_page;
+ goto sync_fail;
+ }
+ wait_on_page(next_page);
+ if (!Page_Uptodate(next_page)) {
+ page_cache_release(page);
+ page = next_page;
+ goto async_fail;
+ }
+ q = kmap(next_page);
+ if (memcmp(entry->name, rentry->name, len) ||
+ memcmp(entry->name+len, q, entry->name_len-len)) {
+ kunmap(next_page);
+ page_cache_release(next_page);
+ goto skip_it;
+ }
+ kunmap(next_page);
+ page_cache_release(next_page);
+ } else if (memcmp (entry->name, rentry->name, entry->name_len))
+ goto skip_it;
+
+ info->f_pos = pos;
+ get_entry(entry, rentry);
+ ret = 0;
+ break;
+skip_it:
+ offs+=entry_size;
+ pos+=entry_size;
+ }
+ if (page) {
+ kunmap(page);
+ page_cache_release(page);
+ }
+ umsdos_manglename (info);
+
+out_dput:
+ dput(demd);
+ return ret;
+
+async_fail:
+ page_cache_release(page);
+ page = ERR_PTR(-EIO);
+sync_fail:
+ return PTR_ERR(page);
+}
+
+
+/*
+ * Add a new entry in the EMD file.
+ * Return 0 if OK or a negative error code.
+ * Return -EEXIST if the entry already exists.
+ *
+ * Complete the information missing in info.
+ *
+ * N.B. What if the EMD file doesn't exist?
+ */
+
+int umsdos_newentry (struct dentry *parent, struct umsdos_info *info)
+{
+ int err, ret = -EEXIST;
+ struct dentry *demd = umsdos_get_emd_dentry(parent);
+
+ ret = PTR_ERR(demd);
+ if (IS_ERR(demd))
+ goto out;
+ err = umsdos_find (demd, info);
+ if (err && err == -ENOENT) {
+ ret = umsdos_writeentry (parent, info, 0);
+ Printk (("umsdos_writeentry EMD ret = %d\n", ret));
+ }
+out:
+ return ret;
+}
+
+
+/*
+ * Create a new hidden link.
+ * Return 0 if OK, an error code if not.
+ */
+
+/* #Specification: hard link / hidden name
+ * When a hard link is created, the original file is renamed
+ * to a hidden name. The name is "..LINKNNN" where NNN is a
+ * number define from the entry offset in the EMD file.
+ */
+int umsdos_newhidden (struct dentry *parent, struct umsdos_info *info)
+{
+ int ret;
+ struct dentry *demd = umsdos_get_emd_dentry(parent);
+ ret = PTR_ERR(demd);
+ if (IS_ERR(demd))
+ goto out;
+
+ umsdos_parse ("..LINK", 6, info);
+ info->entry.name_len = 0;
+ ret = umsdos_find (demd, info);
+ if (ret == -ENOENT || ret == 0) {
+ info->entry.name_len = sprintf (info->entry.name,
+ "..LINK%ld", info->f_pos);
+ ret = 0;
+ }
+out:
+ return ret;
+}
+
+
+/*
+ * Remove an entry from the EMD file.
+ * Return 0 if OK, a negative error code otherwise.
+ *
+ * Complete the information missing in info.
+ */
+
+int umsdos_delentry (struct dentry *parent, struct umsdos_info *info, int isdir)
+{
+ int ret;
+ struct dentry *demd = umsdos_get_emd_dentry(parent);
+
+ ret = PTR_ERR(demd);
+ if (IS_ERR(demd))
+ goto out;
+ ret = umsdos_find (demd, info);
+ if (ret)
+ goto out;
+ if (info->entry.name_len == 0)
+ goto out;
+
+ if ((isdir != 0) != (S_ISDIR (info->entry.mode) != 0)) {
+ if (S_ISDIR (info->entry.mode)) {
+ ret = -EISDIR;
+ } else {
+ ret = -ENOTDIR;
+ }
+ goto out;
+ }
+ ret = umsdos_writeentry (parent, info, 1);
+
+out:
+ return ret;
+}
+
+
+/*
+ * Verify that an EMD directory is empty.
+ * Return:
+ * 0 if not empty,
+ * 1 if empty (except for EMD file),
+ * 2 if empty or no EMD file.
+ */
+
+int umsdos_isempty (struct dentry *dentry)
+{
+ struct dentry *demd;
+ int ret = 2;
+ loff_t pos = 0;
+
+ demd = umsdos_get_emd_dentry(dentry);
+ if (IS_ERR(demd))
+ goto out;
+ /* If the EMD file does not exist, it is certainly empty. :-) */
+ if (!demd->d_inode)
+ goto out_dput;
+
+ ret = 1;
+ while (pos < demd->d_inode->i_size) {
+ struct umsdos_dirent entry;
+
+ if (umsdos_emd_dir_readentry (demd, &pos, &entry) != 0) {
+ ret = 0;
+ break;
+ }
+ if (entry.name_len != 0) {
+ ret = 0;
+ break;
+ }
+ }
+
+out_dput:
+ dput(demd);
+out:
+ return ret;
+}
+
+/*
+ * Locate an entry in a EMD directory.
+ * Return 0 if OK, error code if not, generally -ENOENT.
+ *
+ * expect argument:
+ * 0: anything
+ * 1: file
+ * 2: directory
+ */
+
+int umsdos_findentry (struct dentry *parent, struct umsdos_info *info,
+ int expect)
+{
+ int ret;
+ struct dentry *demd = umsdos_get_emd_dentry(parent);
+
+ ret = PTR_ERR(demd);
+ if (IS_ERR(demd))
+ goto out;
+ ret = umsdos_find (demd, info);
+ if (ret)
+ goto out;
+
+ switch (expect) {
+ case 1:
+ if (S_ISDIR (info->entry.mode))
+ ret = -EISDIR;
+ break;
+ case 2:
+ if (!S_ISDIR (info->entry.mode))
+ ret = -ENOTDIR;
+ }
+
+out:
+ Printk (("umsdos_findentry: returning %d\n", ret));
+ return ret;
+}
diff --git a/fs/umsdos/inode.c b/fs/umsdos/inode.c
new file mode 100644
index 00000000000000..98009bff861fed
--- /dev/null
+++ b/fs/umsdos/inode.c
@@ -0,0 +1,478 @@
+/*
+ * linux/fs/umsdos/inode.c
+ *
+ * Written 1993 by Jacques Gelinas
+ * Inspired from linux/fs/msdos/... by Werner Almesberger
+ */
+
+#include <linux/module.h>
+
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <asm/uaccess.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/umsdos_fs.h>
+#include <linux/list.h>
+#include <linux/pagemap.h>
+
+extern struct dentry_operations umsdos_dentry_operations;
+
+struct dentry *saved_root; /* Original root if changed */
+struct inode *pseudo_root; /* Useful to simulate the pseudo DOS */
+ /* directory. See UMSDOS_readdir_x() */
+
+static struct dentry *check_pseudo_root(struct super_block *);
+
+
+void UMSDOS_put_inode (struct inode *inode)
+{
+ PRINTK ((KERN_DEBUG
+ "put inode %p (%lu) pos %lu count=%d\n"
+ ,inode, inode->i_ino
+ ,inode->u.umsdos_i.pos
+ ,atomic_read(&inode->i_count)));
+
+ if (inode == pseudo_root) {
+ Printk ((KERN_ERR "Umsdos: debug: releasing pseudo_root - ino=%lu count=%d\n", inode->i_ino, atomic_read(&inode->i_count)));
+ }
+
+ if (atomic_read(&inode->i_count) == 1)
+ inode->u.umsdos_i.i_patched = 0;
+}
+
+
+void UMSDOS_put_super (struct super_block *sb)
+{
+ Printk ((KERN_DEBUG "UMSDOS_put_super: entering\n"));
+ if (saved_root && pseudo_root && sb->s_dev == ROOT_DEV) {
+ shrink_dcache_parent(saved_root);
+ dput(saved_root);
+ saved_root = NULL;
+ pseudo_root = NULL;
+ }
+ msdos_put_super (sb);
+}
+
+
+/*
+ * Complete the setup of a directory dentry based on its
+ * EMD/non-EMD status. If it has an EMD, then plug the
+ * umsdos function table. If not, use the msdos one.
+ */
+void umsdos_setup_dir(struct dentry *dir)
+{
+ struct inode *inode = dir->d_inode;
+
+ if (!S_ISDIR(inode->i_mode))
+ printk(KERN_ERR "umsdos_setup_dir: %s/%s not a dir!\n",
+ dir->d_parent->d_name.name, dir->d_name.name);
+
+ init_waitqueue_head (&inode->u.umsdos_i.dir_info.p);
+ inode->u.umsdos_i.dir_info.looking = 0;
+ inode->u.umsdos_i.dir_info.creating = 0;
+ inode->u.umsdos_i.dir_info.pid = 0;
+
+ inode->i_op = &umsdos_rdir_inode_operations;
+ inode->i_fop = &umsdos_rdir_operations;
+ if (umsdos_have_emd(dir)) {
+Printk((KERN_DEBUG "umsdos_setup_dir: %s/%s using EMD\n",
+dir->d_parent->d_name.name, dir->d_name.name));
+ inode->i_op = &umsdos_dir_inode_operations;
+ inode->i_fop = &umsdos_dir_operations;
+ }
+}
+
+
+/*
+ * Add some info into an inode so it can find its owner quickly
+ */
+void umsdos_set_dirinfo_new (struct dentry *dentry, off_t f_pos)
+{
+ struct inode *inode = dentry->d_inode;
+ struct dentry *demd;
+
+ inode->u.umsdos_i.pos = f_pos;
+
+ /* now check the EMD file */
+ demd = umsdos_get_emd_dentry(dentry->d_parent);
+ if (!IS_ERR(demd)) {
+ dput(demd);
+ }
+ return;
+}
+
+static struct inode_operations umsdos_file_inode_operations = {
+ truncate: fat_truncate,
+ setattr: UMSDOS_notify_change,
+};
+
+static struct inode_operations umsdos_symlink_inode_operations = {
+ readlink: page_readlink,
+ follow_link: page_follow_link,
+ setattr: UMSDOS_notify_change,
+};
+
+/*
+ * Connect the proper tables in the inode and add some info.
+ */
+/* #Specification: inode / umsdos info
+ * The first time an inode is seen (inode->i_count == 1),
+ * the inode number of the EMD file which controls this inode
+ * is tagged to this inode. It allows operations such as
+ * notify_change to be handled.
+ */
+void umsdos_patch_dentry_inode(struct dentry *dentry, off_t f_pos)
+{
+ struct inode *inode = dentry->d_inode;
+
+PRINTK (("umsdos_patch_dentry_inode: inode=%lu\n", inode->i_ino));
+
+ /*
+ * Classify the inode based on EMD/non-EMD status.
+ */
+PRINTK (("umsdos_patch_inode: call umsdos_set_dirinfo_new(%p,%lu)\n",
+dentry, f_pos));
+ umsdos_set_dirinfo_new(dentry, f_pos);
+
+ inode->i_op = &umsdos_file_inode_operations;
+ if (S_ISREG (inode->i_mode)) {
+ /* address_space operations already set */
+ } else if (S_ISDIR (inode->i_mode)) {
+ umsdos_setup_dir(dentry);
+ } else if (S_ISLNK (inode->i_mode)) {
+ /* address_space operations already set */
+ inode->i_op = &umsdos_symlink_inode_operations;
+ } else
+ init_special_inode(inode, inode->i_mode,
+ kdev_t_to_nr(inode->i_rdev));
+}
+
+
+/*
+ * lock the parent dir before starting ...
+ * also handles hardlink converting
+ */
+int UMSDOS_notify_change (struct dentry *dentry, struct iattr *attr)
+{
+ struct inode *dir, *inode;
+ struct umsdos_info info;
+ struct dentry *temp, *old_dentry = NULL;
+ int ret;
+
+ ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len,
+ &info);
+ if (ret)
+ goto out;
+ ret = umsdos_findentry (dentry->d_parent, &info, 0);
+ if (ret) {
+printk("UMSDOS_notify_change: %s/%s not in EMD, ret=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, ret);
+ goto out;
+ }
+
+ if (info.entry.flags & UMSDOS_HLINK) {
+ /*
+ * In order to get the correct (real) inode, we just drop
+ * the original dentry.
+ */
+ d_drop(dentry);
+Printk(("UMSDOS_notify_change: hard link %s/%s, fake=%s\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, info.fake.fname));
+
+ /* Do a real lookup to get the short name dentry */
+ temp = umsdos_covered(dentry->d_parent, info.fake.fname,
+ info.fake.len);
+ ret = PTR_ERR(temp);
+ if (IS_ERR(temp))
+ goto out;
+
+ /* now resolve the link ... */
+ temp = umsdos_solve_hlink(temp);
+ ret = PTR_ERR(temp);
+ if (IS_ERR(temp))
+ goto out;
+ old_dentry = dentry;
+ dentry = temp; /* so umsdos_notify_change_locked will operate on that */
+ }
+
+ dir = dentry->d_parent->d_inode;
+ inode = dentry->d_inode;
+
+ ret = inode_change_ok (inode, attr);
+ if (ret)
+ goto out;
+
+ down(&dir->i_sem);
+ ret = umsdos_notify_change_locked(dentry, attr);
+ up(&dir->i_sem);
+ if (ret == 0)
+ ret = inode_setattr (inode, attr);
+out:
+ if (old_dentry)
+ dput (dentry); /* if we had to use fake dentry for hardlinks, dput() it now */
+ return ret;
+}
+
+
+/*
+ * Must be called with the parent lock held.
+ */
+int umsdos_notify_change_locked(struct dentry *dentry, struct iattr *attr)
+{
+ struct inode *inode = dentry->d_inode;
+ struct dentry *demd;
+ struct address_space *mapping;
+ struct page *page;
+ int ret = 0;
+ struct umsdos_dirent *entry;
+ int offs;
+
+Printk(("UMSDOS_notify_change: entering for %s/%s (%d)\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, inode->u.umsdos_i.i_patched));
+
+ if (inode->i_nlink == 0)
+ goto out;
+ if (inode->i_ino == UMSDOS_ROOT_INO)
+ goto out;
+
+ /* get the EMD file dentry */
+ demd = umsdos_get_emd_dentry(dentry->d_parent);
+ ret = PTR_ERR(demd);
+ if (IS_ERR(demd))
+ goto out;
+ ret = 0;
+ /* don't do anything if directory is not promoted to umsdos yet */
+ if (!demd->d_inode) {
+ Printk((KERN_DEBUG
+ "UMSDOS_notify_change: no EMD file %s/%s\n",
+ demd->d_parent->d_name.name, demd->d_name.name));
+ goto out_dput;
+ }
+
+ /* don't do anything if this is the EMD itself */
+ if (inode == demd->d_inode)
+ goto out_dput;
+
+ /* This inode is not a EMD file nor an inode used internally
+ * by MSDOS, so we can update its status.
+ * See emd.c
+ */
+
+ /* Read only the start of the entry since we don't touch the name */
+ mapping = demd->d_inode->i_mapping;
+ offs = inode->u.umsdos_i.pos & ~PAGE_CACHE_MASK;
+ ret = -ENOMEM;
+ page=grab_cache_page(mapping,inode->u.umsdos_i.pos>>PAGE_CACHE_SHIFT);
+ if (!page)
+ goto out_dput;
+ ret=mapping->a_ops->prepare_write(NULL,page,offs,offs+UMSDOS_REC_SIZE);
+ if (ret)
+ goto out_unlock;
+ entry = (struct umsdos_dirent *) (page_address(page) + offs);
+ if (attr->ia_valid & ATTR_UID)
+ entry->uid = cpu_to_le16(attr->ia_uid);
+ if (attr->ia_valid & ATTR_GID)
+ entry->gid = cpu_to_le16(attr->ia_gid);
+ if (attr->ia_valid & ATTR_MODE)
+ entry->mode = cpu_to_le16(attr->ia_mode);
+ if (attr->ia_valid & ATTR_ATIME)
+ entry->atime = cpu_to_le32(attr->ia_atime);
+ if (attr->ia_valid & ATTR_MTIME)
+ entry->mtime = cpu_to_le32(attr->ia_mtime);
+ if (attr->ia_valid & ATTR_CTIME)
+ entry->ctime = cpu_to_le32(attr->ia_ctime);
+ entry->nlink = cpu_to_le16(inode->i_nlink);
+ ret=mapping->a_ops->commit_write(NULL,page,offs,offs+UMSDOS_REC_SIZE);
+ if (ret)
+ printk(KERN_WARNING
+ "umsdos_notify_change: %s/%s EMD write error, ret=%d\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,ret);
+
+ /* #Specification: notify_change / msdos fs
+ * notify_change operation are done only on the
+ * EMD file. The msdos fs is not even called.
+ */
+out_unlock:
+ UnlockPage(page);
+ page_cache_release(page);
+out_dput:
+ dput(demd);
+out:
+ return ret;
+}
+
+
+/*
+ * Update the disk with the inode content
+ */
+void UMSDOS_write_inode (struct inode *inode, int wait)
+{
+ struct iattr newattrs;
+
+ fat_write_inode (inode, wait);
+ newattrs.ia_mtime = inode->i_mtime;
+ newattrs.ia_atime = inode->i_atime;
+ newattrs.ia_ctime = inode->i_ctime;
+ newattrs.ia_valid = ATTR_MTIME | ATTR_ATIME | ATTR_CTIME;
+ /*
+ * UMSDOS_notify_change is convenient to call here
+ * to update the EMD entry associated with this inode.
+ * But it has the side effect to re"dirt" the inode.
+ */
+/*
+ * UMSDOS_notify_change (inode, &newattrs);
+
+ * inode->i_state &= ~I_DIRTY; / * FIXME: this doesn't work. We need to remove ourselves from list on dirty inodes. /mn/ */
+}
+
+
+static struct super_operations umsdos_sops =
+{
+ write_inode: UMSDOS_write_inode,
+ put_inode: UMSDOS_put_inode,
+ delete_inode: fat_delete_inode,
+ put_super: UMSDOS_put_super,
+ statfs: UMSDOS_statfs,
+ clear_inode: fat_clear_inode,
+};
+
+int UMSDOS_statfs(struct super_block *sb,struct statfs *buf)
+{
+ int ret;
+ ret = fat_statfs (sb, buf);
+ if (!ret)
+ buf->f_namelen = UMSDOS_MAXNAME;
+ return ret;
+}
+
+/*
+ * Read the super block of an Extended MS-DOS FS.
+ */
+struct super_block *UMSDOS_read_super (struct super_block *sb, void *data,
+ int silent)
+{
+ struct super_block *res;
+ struct dentry *new_root;
+
+ /*
+ * Call msdos-fs to mount the disk.
+ * Note: this returns res == sb or NULL
+ */
+ res = msdos_read_super (sb, data, silent);
+
+ if (!res)
+ goto out_fail;
+
+ printk (KERN_INFO "UMSDOS 0.86k "
+ "(compatibility level %d.%d, fast msdos)\n",
+ UMSDOS_VERSION, UMSDOS_RELEASE);
+
+ sb->s_op = &umsdos_sops;
+ MSDOS_SB(sb)->options.dotsOK = 0; /* disable hidden==dotfile */
+
+ /* install our dentry operations ... */
+ sb->s_root->d_op = &umsdos_dentry_operations;
+
+ umsdos_patch_dentry_inode(sb->s_root, 0);
+
+ /* Check whether to change to the /linux root */
+ new_root = check_pseudo_root(sb);
+
+ if (new_root) {
+ /* sanity check */
+ if (new_root->d_op != &umsdos_dentry_operations)
+ printk("umsdos_read_super: pseudo-root wrong ops!\n");
+
+ pseudo_root = new_root->d_inode;
+ saved_root = sb->s_root;
+ printk(KERN_INFO "UMSDOS: changed to alternate root\n");
+ dget (sb->s_root); sb->s_root = dget(new_root);
+ }
+ return sb;
+
+out_fail:
+ printk(KERN_INFO "UMSDOS: msdos_read_super failed, mount aborted.\n");
+ return NULL;
+}
+
+/*
+ * Check for an alternate root if we're the root device.
+ */
+
+extern kdev_t ROOT_DEV;
+static struct dentry *check_pseudo_root(struct super_block *sb)
+{
+ struct dentry *root, *sbin, *init;
+
+ /*
+ * Check whether we're mounted as the root device.
+ * must check like this, because we can be used with initrd
+ */
+
+ if (sb->s_dev != ROOT_DEV)
+ goto out_noroot;
+
+ /*
+ * lookup_dentry needs a (so far non-existent) root.
+ */
+ printk(KERN_INFO "check_pseudo_root: mounted as root\n");
+ root = lookup_one_len(UMSDOS_PSDROOT_NAME, sb->s_root,UMSDOS_PSDROOT_LEN);
+ if (IS_ERR(root))
+ goto out_noroot;
+
+ if (!root->d_inode || !S_ISDIR(root->d_inode->i_mode))
+ goto out_dput;
+
+printk(KERN_INFO "check_pseudo_root: found %s/%s\n",
+root->d_parent->d_name.name, root->d_name.name);
+
+ /* look for /sbin/init */
+ sbin = lookup_one_len("sbin", root, 4);
+ if (IS_ERR(sbin))
+ goto out_dput;
+ if (!sbin->d_inode || !S_ISDIR(sbin->d_inode->i_mode))
+ goto out_dput_sbin;
+ init = lookup_one_len("init", sbin, 4);
+ if (IS_ERR(init))
+ goto out_dput_sbin;
+ if (!init->d_inode)
+ goto out_dput_init;
+ printk(KERN_INFO "check_pseudo_root: found %s/%s, enabling pseudo-root\n", init->d_parent->d_name.name, init->d_name.name);
+ dput(sbin);
+ dput(init);
+ return root;
+
+ /* Alternate root not found ... */
+out_dput_init:
+ dput(init);
+out_dput_sbin:
+ dput(sbin);
+out_dput:
+ dput(root);
+out_noroot:
+ return NULL;
+}
+
+
+static DECLARE_FSTYPE_DEV(umsdos_fs_type, "umsdos", UMSDOS_read_super);
+
+static int __init init_umsdos_fs (void)
+{
+ return register_filesystem (&umsdos_fs_type);
+}
+
+static void __exit exit_umsdos_fs (void)
+{
+ unregister_filesystem (&umsdos_fs_type);
+}
+
+EXPORT_NO_SYMBOLS;
+
+module_init(init_umsdos_fs)
+module_exit(exit_umsdos_fs)
+MODULE_LICENSE("GPL");
diff --git a/fs/umsdos/ioctl.c b/fs/umsdos/ioctl.c
new file mode 100644
index 00000000000000..13cd83d756f4ff
--- /dev/null
+++ b/fs/umsdos/ioctl.c
@@ -0,0 +1,439 @@
+/*
+ * linux/fs/umsdos/ioctl.c
+ *
+ * Written 1993 by Jacques Gelinas
+ *
+ * Extended MS-DOS ioctl directory handling functions
+ */
+
+#include <asm/uaccess.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/umsdos_fs.h>
+
+struct UMSDOS_DIR_ONCE {
+ struct dirent *ent;
+ int count;
+};
+
+/*
+ * Record a single entry the first call.
+ * Return -EINVAL the next one.
+ */
+static int umsdos_ioctl_fill (
+ void *buf,
+ const char *name,
+ int name_len,
+ loff_t offset,
+ ino_t ino,
+ unsigned type)
+{
+ int ret = -EINVAL;
+ struct UMSDOS_DIR_ONCE *d = (struct UMSDOS_DIR_ONCE *) buf;
+
+ if (d->count == 0) {
+ copy_to_user (d->ent->d_name, name, name_len);
+ put_user ('\0', d->ent->d_name + name_len);
+ put_user (name_len, &d->ent->d_reclen);
+ put_user (ino, &d->ent->d_ino);
+ put_user (offset, &d->ent->d_off);
+ d->count = 1;
+ ret = 0;
+ }
+ return ret;
+}
+
+
+/*
+ * Perform special function on a directory
+ */
+/* #Specification: ioctl / prototypes
+ * The official prototype for the umsdos ioctl on directory
+ * is:
+ *
+ * int ioctl (
+ * int fd, // File handle of the directory
+ * int cmd, // command
+ * struct umsdos_ioctl *data)
+ *
+ * The struct and the commands are defined in linux/umsdos_fs.h.
+ *
+ * umsdos_progs/umsdosio.c provide an interface in C++ to all
+ * these ioctl. umsdos_progs/udosctl is a small utility showing
+ * all this.
+ *
+ * These ioctl generally allow one to work on the EMD or the
+ * DOS directory independently. These are essential to implement
+ * the synchronise.
+ */
+int UMSDOS_ioctl_dir(struct inode *dir, struct file *filp, unsigned int cmd,
+ unsigned long data_ptr)
+{
+ struct dentry *dentry = filp->f_dentry;
+ struct umsdos_ioctl *idata = (struct umsdos_ioctl *) data_ptr;
+ int ret;
+ struct umsdos_ioctl data;
+
+Printk(("UMSDOS_ioctl_dir: %s/%s, cmd=%d, data=%08lx\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, cmd, data_ptr));
+
+ /* forward non-umsdos ioctls - this hopefully doesn't cause conflicts */
+ if (cmd != UMSDOS_GETVERSION
+ && cmd != UMSDOS_READDIR_DOS
+ && cmd != UMSDOS_READDIR_EMD
+ && cmd != UMSDOS_INIT_EMD
+ && cmd != UMSDOS_CREAT_EMD
+ && cmd != UMSDOS_RENAME_DOS
+ && cmd != UMSDOS_UNLINK_EMD
+ && cmd != UMSDOS_UNLINK_DOS
+ && cmd != UMSDOS_RMDIR_DOS
+ && cmd != UMSDOS_STAT_DOS
+ && cmd != UMSDOS_DOS_SETUP)
+ return fat_dir_ioctl (dir, filp, cmd, data_ptr);
+
+ /* #Specification: ioctl / access
+ * Only root (effective id) is allowed to do IOCTL on directory
+ * in UMSDOS. EPERM is returned for other user.
+ */
+ /*
+ * Well, not all cases require write access, but it simplifies
+ * the code, and let's face it, there is only one client (umssync)
+ * for all this.
+ */
+ ret = verify_area (VERIFY_WRITE, (void *) data_ptr,
+ sizeof (struct umsdos_ioctl));
+ if (ret < 0)
+ goto out;
+
+ ret = -EPERM;
+ if (current->euid != 0 && cmd != UMSDOS_GETVERSION)
+ goto out;
+
+ ret = -EINVAL;
+ if (cmd == UMSDOS_GETVERSION) {
+ /* #Specification: ioctl / UMSDOS_GETVERSION
+ * The field version and release of the structure
+ * umsdos_ioctl are filled with the version and release
+ * number of the fs code in the kernel. This will allow
+ * some form of checking. Users won't be able to run
+ * incompatible utility such as the synchroniser (umssync).
+ * umsdos_progs/umsdosio.c enforce this checking.
+ *
+ * Return always 0.
+ */
+ put_user (UMSDOS_VERSION, &idata->version);
+ put_user (UMSDOS_RELEASE, &idata->release);
+ ret = 0;
+ goto out;
+ }
+ if (cmd == UMSDOS_READDIR_DOS) {
+ /* #Specification: ioctl / UMSDOS_READDIR_DOS
+ * One entry is read from the DOS directory at the current
+ * file position. The entry is put as is in the dos_dirent
+ * field of struct umsdos_ioctl.
+ *
+ * Return > 0 if success.
+ */
+ struct UMSDOS_DIR_ONCE bufk;
+
+ bufk.count = 0;
+ bufk.ent = &idata->dos_dirent;
+
+ fat_readdir (filp, &bufk, umsdos_ioctl_fill);
+
+ ret = bufk.count == 1 ? 1 : 0;
+ goto out;
+ }
+ if (cmd == UMSDOS_READDIR_EMD) {
+ /* #Specification: ioctl / UMSDOS_READDIR_EMD
+ * One entry is read from the EMD at the current
+ * file position. The entry is put as is in the umsdos_dirent
+ * field of struct umsdos_ioctl. The corresponding mangled
+ * DOS entry name is put in the dos_dirent field.
+ *
+ * All entries are read including hidden links. Blank
+ * entries are skipped.
+ *
+ * Return > 0 if success.
+ */
+ struct dentry *demd;
+ loff_t pos = filp->f_pos;
+
+ /* The absence of the EMD is simply seen as an EOF */
+ demd = umsdos_get_emd_dentry(dentry);
+ ret = PTR_ERR(demd);
+ if (IS_ERR(demd))
+ goto out;
+ ret = 0;
+ if (!demd->d_inode)
+ goto read_dput;
+
+ while (pos < demd->d_inode->i_size) {
+ off_t f_pos = pos;
+ struct umsdos_dirent entry;
+ struct umsdos_info info;
+
+ ret = umsdos_emd_dir_readentry (demd, &pos, &entry);
+
+ if (ret == -ENAMETOOLONG) {
+ printk (KERN_INFO "Fixing EMD entry with invalid size -- zeroing out\n");
+ memset (&info, 0, sizeof (info));
+ info.f_pos = f_pos;
+ info.recsize = UMSDOS_REC_SIZE;
+ ret = umsdos_writeentry (dentry, &info, 1);
+ continue;
+ }
+
+ if (ret)
+ break;
+ if (entry.name_len <= 0)
+ continue;
+
+ umsdos_parse (entry.name, entry.name_len, &info);
+ info.f_pos = f_pos;
+ umsdos_manglename (&info);
+ ret = -EFAULT;
+ if (copy_to_user (&idata->umsdos_dirent, &entry,
+ sizeof (entry)))
+ break;
+ if (copy_to_user (&idata->dos_dirent.d_name,
+ info.fake.fname,
+ info.fake.len + 1))
+ break;
+ ret = entry.name_len;
+ break;
+ }
+ /* update the original f_pos */
+ filp->f_pos = pos;
+ read_dput:
+ d_drop(demd);
+ dput(demd);
+ goto out;
+ }
+ if (cmd == UMSDOS_INIT_EMD) {
+ /* #Specification: ioctl / UMSDOS_INIT_EMD
+ * The UMSDOS_INIT_EMD command makes sure the EMD
+ * exists for a directory. If it does not, it is
+ * created. Also, it makes sure the directory function
+ * table (struct inode_operations) is set to the UMSDOS
+ * semantic. This mean that umssync may be applied to
+ * an "opened" msdos directory, and it will change behavior
+ * on the fly.
+ *
+ * Return 0 if success.
+ */
+
+ ret = umsdos_make_emd(dentry);
+Printk(("UMSDOS_ioctl_dir: INIT_EMD %s/%s, ret=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, ret));
+ umsdos_setup_dir (dentry);
+ goto out;
+ }
+
+ ret = -EFAULT;
+ if (copy_from_user (&data, idata, sizeof (data)))
+ goto out;
+
+ if (cmd == UMSDOS_CREAT_EMD) {
+ /* #Specification: ioctl / UMSDOS_CREAT_EMD
+ * The umsdos_dirent field of the struct umsdos_ioctl is used
+ * as is to create a new entry in the EMD of the directory.
+ * The DOS directory is not modified.
+ * No validation is done (yet).
+ *
+ * Return 0 if success.
+ */
+ struct umsdos_info info;
+
+ /* This makes sure info.entry and info in general
+ * is correctly initialised
+ */
+ memcpy (&info.entry, &data.umsdos_dirent,
+ sizeof (data.umsdos_dirent));
+ umsdos_parse (data.umsdos_dirent.name
+ ,data.umsdos_dirent.name_len, &info);
+ ret = umsdos_newentry (dentry, &info);
+ goto out;
+ }
+ else if (cmd == UMSDOS_RENAME_DOS) {
+ struct dentry *old_dentry, *new_dentry; /* FIXME */
+
+ /* #Specification: ioctl / UMSDOS_RENAME_DOS
+ * A file or directory is renamed in a DOS directory
+ * (not moved across directory). The source name
+ * is in the dos_dirent.name field and the destination
+ * is in umsdos_dirent.name field.
+ *
+ * This ioctl allows umssync to rename a mangled file
+ * name before syncing it back in the EMD.
+ */
+ old_dentry = umsdos_lookup_dentry (dentry,
+ data.dos_dirent.d_name,
+ data.dos_dirent.d_reclen ,1);
+ ret = PTR_ERR(old_dentry);
+ if (IS_ERR(old_dentry))
+ goto out;
+ new_dentry = umsdos_lookup_dentry (dentry,
+ data.umsdos_dirent.name,
+ data.umsdos_dirent.name_len, 1);
+ ret = PTR_ERR(new_dentry);
+ if (!IS_ERR(new_dentry)) {
+printk("umsdos_ioctl: renaming %s/%s to %s/%s\n",
+old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
+new_dentry->d_parent->d_name.name, new_dentry->d_name.name);
+ ret = msdos_rename (dir, old_dentry, dir, new_dentry);
+ d_drop(new_dentry);
+ d_drop(old_dentry);
+ dput(new_dentry);
+ }
+ dput(old_dentry);
+ goto out;
+ }
+ else if (cmd == UMSDOS_UNLINK_EMD) {
+ /* #Specification: ioctl / UMSDOS_UNLINK_EMD
+ * The umsdos_dirent field of the struct umsdos_ioctl is used
+ * as is to remove an entry from the EMD of the directory.
+ * No validation is done (yet). The mode field is used
+ * to validate S_ISDIR or S_ISREG.
+ *
+ * Return 0 if success.
+ */
+ struct umsdos_info info;
+
+ /* This makes sure info.entry and info in general
+ * is correctly initialised
+ */
+ memcpy (&info.entry, &data.umsdos_dirent,
+ sizeof (data.umsdos_dirent));
+ umsdos_parse (data.umsdos_dirent.name,
+ data.umsdos_dirent.name_len, &info);
+ ret = umsdos_delentry (dentry, &info,
+ S_ISDIR (data.umsdos_dirent.mode));
+ if (ret) {
+ printk(KERN_WARNING
+ "umsdos_ioctl: delentry %s/%s failed, ret=%d\n",
+ dentry->d_name.name, info.entry.name, ret);
+ }
+ goto out;
+ }
+ else if (cmd == UMSDOS_UNLINK_DOS) {
+ struct dentry *temp;
+
+ /* #Specification: ioctl / UMSDOS_UNLINK_DOS
+ * The dos_dirent field of the struct umsdos_ioctl is used to
+ * execute a msdos_unlink operation. The d_name and d_reclen
+ * fields are used.
+ *
+ * Return 0 if success.
+ */
+ temp = umsdos_lookup_dentry(dentry, data.dos_dirent.d_name,
+ data.dos_dirent.d_reclen, 1);
+ ret = PTR_ERR(temp);
+ if (IS_ERR(temp))
+ goto out;
+ ret = -ENOENT;
+ if (temp->d_inode) {
+ ret = -EISDIR;
+ if (!S_ISDIR(temp->d_inode->i_mode))
+ ret = msdos_unlink (dir, temp);
+ if (!ret)
+ d_delete(temp);
+ }
+ dput (temp);
+ goto out;
+ }
+ else if (cmd == UMSDOS_RMDIR_DOS) {
+ struct dentry *temp;
+
+ /* #Specification: ioctl / UMSDOS_RMDIR_DOS
+ * The dos_dirent field of the struct umsdos_ioctl is used to
+ * execute a msdos_rmdir operation. The d_name and d_reclen
+ * fields are used.
+ *
+ * Return 0 if success.
+ */
+ temp = umsdos_lookup_dentry(dentry, data.dos_dirent.d_name,
+ data.dos_dirent.d_reclen, 1);
+ ret = PTR_ERR(temp);
+ if (IS_ERR(temp))
+ goto out;
+ ret = -ENOENT;
+ if (temp->d_inode) {
+ ret = -ENOTDIR;
+ if (S_ISDIR(temp->d_inode->i_mode))
+ ret = msdos_rmdir (dir, temp);
+ if (!ret)
+ d_delete(temp);
+ }
+ dput (temp);
+ goto out;
+
+ } else if (cmd == UMSDOS_STAT_DOS) {
+ /* #Specification: ioctl / UMSDOS_STAT_DOS
+ * The dos_dirent field of the struct umsdos_ioctl is
+ * used to execute a stat operation in the DOS directory.
+ * The d_name and d_reclen fields are used.
+ *
+ * The following field of umsdos_ioctl.stat are filled.
+ *
+ * st_ino,st_mode,st_size,st_atime,st_mtime,st_ctime,
+ * Return 0 if success.
+ */
+ struct dentry *dret;
+ struct inode *inode;
+
+ dret = umsdos_lookup_dentry(dentry, data.dos_dirent.d_name,
+ data.dos_dirent.d_reclen, 1);
+ ret = PTR_ERR(dret);
+ if (IS_ERR(dret))
+ goto out;
+ ret = -ENOENT;
+ inode = dret->d_inode;
+ if (inode) {
+ data.stat.st_ino = inode->i_ino;
+ data.stat.st_mode = inode->i_mode;
+ data.stat.st_size = inode->i_size;
+ data.stat.st_atime = inode->i_atime;
+ data.stat.st_ctime = inode->i_ctime;
+ data.stat.st_mtime = inode->i_mtime;
+ ret = -EFAULT;
+ if (!copy_to_user (&idata->stat, &data.stat,
+ sizeof (data.stat)))
+ ret = 0;
+ }
+ dput(dret);
+ goto out;
+ }
+ else if (cmd == UMSDOS_DOS_SETUP) {
+ /* #Specification: ioctl / UMSDOS_DOS_SETUP
+ * The UMSDOS_DOS_SETUP ioctl allow changing the
+ * default permission of the MS-DOS filesystem driver
+ * on the fly. The MS-DOS driver applies global permissions
+ * to every file and directory. Normally these permissions
+ * are controlled by a mount option. This is not
+ * available for root partition, so a special utility
+ * (umssetup) is provided to do this, normally in
+ * /etc/rc.local.
+ *
+ * Be aware that this applies ONLY to MS-DOS directories
+ * (those without EMD --linux-.---). Umsdos directory
+ * have independent (standard) permission for each
+ * and every file.
+ *
+ * The field umsdos_dirent provide the information needed.
+ * umsdos_dirent.uid and gid sets the owner and group.
+ * umsdos_dirent.mode set the permissions flags.
+ */
+ dir->i_sb->u.msdos_sb.options.fs_uid = data.umsdos_dirent.uid;
+ dir->i_sb->u.msdos_sb.options.fs_gid = data.umsdos_dirent.gid;
+ dir->i_sb->u.msdos_sb.options.fs_umask = data.umsdos_dirent.mode;
+ ret = 0;
+ }
+out:
+ Printk (("ioctl %d, returning %d\n", cmd, ret));
+ return ret;
+}
diff --git a/fs/umsdos/mangle.c b/fs/umsdos/mangle.c
new file mode 100644
index 00000000000000..a1feba0eaae121
--- /dev/null
+++ b/fs/umsdos/mangle.c
@@ -0,0 +1,522 @@
+/*
+ * linux/fs/umsdos/mangle.c
+ *
+ * Written 1993 by Jacques Gelinas
+ *
+ * Control the mangling of file name to fit msdos name space.
+ * Many optimisations by GLU == dglaude@is1.vub.ac.be (Glaude David)
+ */
+
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/umsdos_fs.h>
+
+/* (This file is used outside of the kernel) */
+#ifndef __KERNEL__
+#define KERN_WARNING
+#endif
+
+/*
+ * Complete the mangling of the MSDOS fake name
+ * based on the position of the entry in the EMD file.
+ *
+ * Simply complete the job of umsdos_parse; fill the extension.
+ *
+ * Beware that info->f_pos must be set.
+ */
+void umsdos_manglename (struct umsdos_info *info)
+{
+ if (info->msdos_reject) {
+ /* #Specification: file name / non MSDOS conforming / mangling
+ * Each non MSDOS conforming file has a special extension
+ * build from the entry position in the EMD file.
+ *
+ * This number is then transform in a base 32 number, where
+ * each digit is expressed like hexadecimal number, using
+ * digit and letter, except it uses 22 letters from 'a' to 'v'.
+ * The number 32 comes from 2**5. It is faster to split a binary
+ * number using a base which is a power of two. And I was 32
+ * when I started this project. Pick your answer :-) .
+ *
+ * If the result is '0', it is replace with '_', simply
+ * to make it odd.
+ *
+ * This is true for the first two character of the extension.
+ * The last one is taken from a list of odd character, which
+ * are:
+ *
+ * { } ( ) ! ` ^ & @
+ *
+ * With this scheme, we can produce 9216 ( 9* 32 * 32)
+ * different extensions which should not clash with any useful
+ * extension already popular or meaningful. Since most directory
+ * have much less than 32 * 32 files in it, the first character
+ * of the extension of any mangled name will be {.
+ *
+ * Here are the reason to do this (this kind of mangling).
+ *
+ * -The mangling is deterministic. Just by the extension, we
+ * are able to locate the entry in the EMD file.
+ *
+ * -By keeping to beginning of the file name almost unchanged,
+ * we are helping the MSDOS user.
+ *
+ * -The mangling produces names not too ugly, so an msdos user
+ * may live with it (remember it, type it, etc...).
+ *
+ * -The mangling produces names ugly enough so no one will
+ * ever think of using such a name in real life. This is not
+ * fool proof. I don't think there is a total solution to this.
+ */
+ int entry_num;
+ char *pt = info->fake.fname + info->fake.len;
+ /* lookup for encoding the last character of the extension
+ * It contains valid character after the ugly one to make sure
+ * even if someone overflows the 32 * 32 * 9 limit, it still
+ * does something
+ */
+#define SPECIAL_MANGLING '{','}','(',')','!','`','^','&','@'
+ static char lookup3[] =
+ {
+ SPECIAL_MANGLING,
+ /* This is the start of lookup12 */
+ '_', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+ 'p', 'q', 'r', 's', 't', 'u', 'v'
+ };
+
+#define lookup12 (lookup3+9)
+ entry_num = info->f_pos / UMSDOS_REC_SIZE;
+ if (entry_num > (9* 32 * 32)){
+ printk (KERN_WARNING "UMSDOS: more than 9216 files in a directory.\n"
+ "This may break the mangling strategy.\n"
+ "Not a killer problem. See doc.\n");
+ }
+ *pt++ = '.';
+ *pt++ = lookup3 [(entry_num >> 10) & 31];
+ *pt++ = lookup12[(entry_num >> 5) & 31];
+ *pt++ = lookup12[entry_num & 31];
+ *pt = '\0'; /* help doing printk */
+ info->fake.len += 4;
+ info->msdos_reject = 0; /* Avoid mangling twice */
+ }
+}
+
+/*
+ * Evaluate the record size needed to store of name of len character.
+ * The value returned is a multiple of UMSDOS_REC_SIZE.
+ */
+int umsdos_evalrecsize (int len)
+{
+ struct umsdos_dirent dirent;
+ int nbrec = 1 + ((len - 1 + (dirent.name - (char *) &dirent))
+ / UMSDOS_REC_SIZE);
+
+ return nbrec * UMSDOS_REC_SIZE;
+ /*
+ * GLU This should be inlined or something to speed it up to the max.
+ * GLU nbrec is absolutely not needed to return the value.
+ */
+}
+#ifdef TEST
+int umsdos_evalrecsize_old (int len)
+{
+ struct umsdos_dirent dirent;
+ int size = len + (dirent.name - (char *) &dirent);
+ int nbrec = size / UMSDOS_REC_SIZE;
+ int extra = size % UMSDOS_REC_SIZE;
+
+ if (extra > 0)
+ nbrec++;
+ return nbrec * UMSDOS_REC_SIZE;
+}
+#endif
+
+
+/*
+ * Fill the struct info with the full and msdos name of a file
+ * Return 0 if all is OK, a negative error code otherwise.
+ */
+int umsdos_parse (
+ const char *fname,
+ int len,
+ struct umsdos_info *info)
+{
+ int ret = -ENAMETOOLONG;
+
+ /* #Specification: file name / too long
+ * If a file name exceed UMSDOS maxima, the file name is silently
+ * truncated. This makes it conformant with the other file system
+ * of Linux (minix and ext2 at least).
+ */
+ if (len > UMSDOS_MAXNAME)
+ len = UMSDOS_MAXNAME;
+ {
+ const char *firstpt = NULL; /* First place we saw a "." in fname */
+
+ /* #Specification: file name / non MSDOS conforming / base length 0
+ * file names beginning with a period '.' are invalid for MS-DOS.
+ * It needs absolutely a base name. So the file name is mangled
+ */
+ int ivldchar = fname[0] == '.'; /* At least one invalid character */
+ int msdos_len = len;
+ int base_len;
+
+ /*
+ * cardinal_per_size tells if there exists at least one
+ * DOS pseudo device on length n. See the test below.
+ */
+ static const char cardinal_per_size[9] =
+ {
+ 0, 0, 0, 1, 1, 0, 1, 0, 1
+ };
+
+ /*
+ * lkp translate all character to acceptable character (for DOS).
+ * When lkp[n] == n, it means also it is an acceptable one.
+ * So it serves both as a flag and as a translator.
+ */
+ static char lkp[256];
+ static char is_init = 0;
+
+ if (!is_init) {
+ /*
+ * Initialisation of the array is easier and less error
+ * prone like this.
+ */
+ int i;
+ static const char *spc = "\"*+,/:;<=>?[\\]|~";
+
+ is_init = 1;
+ for (i = 0; i <= 32; i++)
+ lkp[i] = '#';
+ for (i = 33; i < 'A'; i++)
+ lkp[i] = (char) i;
+ for (i = 'A'; i <= 'Z'; i++)
+ lkp[i] = (char) (i + ('a' - 'A'));
+ for (i = 'Z' + 1; i < 127; i++)
+ lkp[i] = (char) i;
+ for (i = 128; i < 256; i++)
+ lkp[i] = '#';
+
+ lkp['.'] = '_';
+ while (*spc != '\0')
+ lkp[(unsigned char) (*spc++)] = '#';
+ }
+ /* GLU
+ * File names longer than 8+'.'+3 are invalid for MS-DOS,
+ * so the file name is to be mangled--no further test is needed.
+ * This speeds up handling of long names.
+ * The position of the last point is no more necessary anyway.
+ */
+ if (len <= (8 + 1 + 3)) {
+ const char *pt = fname;
+ const char *endpt = fname + len;
+
+ while (pt < endpt) {
+ if (*pt == '.') {
+ if (firstpt != NULL) {
+ /* 2 . in a file name. Reject */
+ ivldchar = 1;
+ break;
+ } else {
+ int extlen = (int) (endpt - pt);
+
+ firstpt = pt;
+ if (firstpt - fname > 8) {
+ /* base name longer than 8: reject */
+ ivldchar = 1;
+ break;
+ } else if (extlen > 4) {
+ /* Extension longer than 4 (including .): reject */
+ ivldchar = 1;
+ break;
+ } else if (extlen == 1) {
+ /* #Specification: file name / non MSDOS conforming / last char == .
+ * If the last character of a file name is
+ * a period, mangling is applied. MS-DOS does
+ * not support those file names.
+ */
+ ivldchar = 1;
+ break;
+ } else if (extlen == 4) {
+ /* #Specification: file name / non MSDOS conforming / mangling clash
+ * To avoid clash with the umsdos mangling, any file
+ * with a special character as the first character
+ * of the extension will be mangled. This solves the
+ * following problem:
+ *
+ * #
+ * touch FILE
+ * # FILE is invalid for DOS, so mangling is applied
+ * # file.{_1 is created in the DOS directory
+ * touch file.{_1
+ * # To UMSDOS file point to a single DOS entry.
+ * # So file.{_1 has to be mangled.
+ * #
+ */
+ static char special[] =
+ {
+ SPECIAL_MANGLING, '\0'
+ };
+
+ if (strchr (special, firstpt[1]) != NULL) {
+ ivldchar = 1;
+ break;
+ }
+ }
+ }
+ } else if (lkp[(unsigned char) (*pt)] != *pt) {
+ ivldchar = 1;
+ break;
+ }
+ pt++;
+ }
+ } else {
+ ivldchar = 1;
+ }
+ if (ivldchar
+ || (firstpt == NULL && len > 8)
+ || (len == UMSDOS_EMD_NAMELEN
+ && memcmp (fname, UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN) == 0)) {
+ /* #Specification: file name / --linux-.---
+ * The name of the EMD file --linux-.--- is map to a mangled
+ * name. So UMSDOS does not restrict its use.
+ */
+ /* #Specification: file name / non MSDOS conforming / mangling
+ * Non MSDOS conforming file names must use some alias to fit
+ * in the MSDOS name space.
+ *
+ * The strategy is simple. The name is simply truncated to
+ * 8 char. points are replace with underscore and a
+ * number is given as an extension. This number correspond
+ * to the entry number in the EMD file. The EMD file
+ * only need to carry the real name.
+ *
+ * Upper case is also converted to lower case.
+ * Control character are converted to #.
+ * Spaces are converted to #.
+ * The following characters are also converted to #.
+ * #
+ * " * + , / : ; < = > ? [ \ ] | ~
+ * #
+ *
+ * Sometimes the problem is not in MS-DOS itself but in
+ * command.com.
+ */
+ int i;
+ char *pt = info->fake.fname;
+
+ base_len = msdos_len = (msdos_len > 8) ? 8 : msdos_len;
+ /*
+ * There is no '.' any more so we know for a fact that
+ * the base length is the length.
+ */
+ memcpy (info->fake.fname, fname, msdos_len);
+ for (i = 0; i < msdos_len; i++, pt++)
+ *pt = lkp[(unsigned char) (*pt)];
+ *pt = '\0'; /* GLU We force null termination. */
+ info->msdos_reject = 1;
+ /*
+ * The numeric extension is added only when we know
+ * the position in the EMD file, in umsdos_newentry(),
+ * umsdos_delentry(), and umsdos_findentry().
+ * See umsdos_manglename().
+ */
+ } else {
+ /* Conforming MSDOS file name */
+ strncpy (info->fake.fname, fname, len);
+ info->msdos_reject = 0;
+ base_len = firstpt != NULL ? (int) (firstpt - fname) : len;
+ }
+ if (cardinal_per_size[base_len]) {
+ /* #Specification: file name / MSDOS devices / mangling
+ * To avoid unreachable file from MS-DOS, any MS-DOS conforming
+ * file with a basename equal to one of the MS-DOS pseudo
+ * devices will be mangled.
+ *
+ * If a file such as "prn" was created, it would be unreachable
+ * under MS-DOS because "prn" is assumed to be the printer, even
+ * if the file does have an extension.
+ *
+ * Since the extension is unimportant to MS-DOS, we must patch
+ * the basename also. We simply insert a minus '-'. To avoid
+ * conflict with valid file with a minus in front (such as
+ * "-prn"), we add an mangled extension like any other
+ * mangled file name.
+ *
+ * Here is the list of DOS pseudo devices:
+ *
+ * #
+ * "prn","con","aux","nul",
+ * "lpt1","lpt2","lpt3","lpt4",
+ * "com1","com2","com3","com4",
+ * "clock$"
+ * #
+ *
+ * and some standard ones for common DOS programs
+ *
+ * "emmxxxx0","xmsxxxx0","setverxx"
+ *
+ * (Thanks to Chris Hall <cah17@phoenix.cambridge.ac.uk>
+ * for pointing these out to me).
+ *
+ * Is there one missing?
+ */
+ /* This table must be ordered by length */
+ static const char *tbdev[] =
+ {
+ "prn", "con", "aux", "nul",
+ "lpt1", "lpt2", "lpt3", "lpt4",
+ "com1", "com2", "com3", "com4",
+ "clock$",
+ "emmxxxx0", "xmsxxxx0", "setverxx"
+ };
+
+ /* Tell where to find in tbdev[], the first name of */
+ /* a certain length */
+ static const char start_ind_dev[9] =
+ {
+ 0, 0, 0, 4, 12, 12, 13, 13, 16
+ };
+ char basen[9];
+ int i;
+
+ for (i = start_ind_dev[base_len - 1]; i < start_ind_dev[base_len]; i++) {
+ if (memcmp (info->fake.fname, tbdev[i], base_len) == 0) {
+ memcpy (basen, info->fake.fname, base_len);
+ basen[base_len] = '\0'; /* GLU We force null termination. */
+ /*
+ * GLU We do that only if necessary; we try to do the
+ * GLU simple thing in the usual circumstance.
+ */
+ info->fake.fname[0] = '-';
+ strcpy (info->fake.fname + 1, basen); /* GLU We already guaranteed a null would be at the end. */
+ msdos_len = (base_len == 8) ? 8 : base_len + 1;
+ info->msdos_reject = 1;
+ break;
+ }
+ }
+ }
+ info->fake.fname[msdos_len] = '\0'; /* Help doing printk */
+ /* GLU This zero should (always?) be there already. */
+ info->fake.len = msdos_len;
+ /* Why not use info->fake.len everywhere? Is it longer?
+ */
+ memcpy (info->entry.name, fname, len);
+ info->entry.name[len] = '\0'; /* for printk */
+ info->entry.name_len = len;
+ ret = 0;
+ }
+ /*
+ * Evaluate how many records are needed to store this entry.
+ */
+ info->recsize = umsdos_evalrecsize (len);
+ return ret;
+}
+
+#ifdef TEST
+
+struct MANG_TEST {
+ char *fname; /* Name to validate */
+ int msdos_reject; /* Expected msdos_reject flag */
+ char *msname; /* Expected msdos name */
+};
+
+struct MANG_TEST tb[] =
+{
+ "hello", 0, "hello",
+ "hello.1", 0, "hello.1",
+ "hello.1_", 0, "hello.1_",
+ "prm", 0, "prm",
+
+#ifdef PROPOSITION
+ "HELLO", 1, "hello",
+ "Hello.1", 1, "hello.1",
+ "Hello.c", 1, "hello.c",
+#else
+/*
+ * I find the three examples below very unfortunate. I propose to
+ * convert them to lower case in a quick preliminary pass, then test
+ * whether there are other troublesome characters. I have not made
+ * this change, because it is not easy, but I wanted to mention the
+ * principle. Obviously something like that would increase the chance
+ * of collisions, for example between "HELLO" and "Hello", but these
+ * can be treated elsewhere along with the other collisions.
+ */
+
+ "HELLO", 1, "hello",
+ "Hello.1", 1, "hello_1",
+ "Hello.c", 1, "hello_c",
+#endif
+
+ "hello.{_1", 1, "hello_{_",
+ "hello\t", 1, "hello#",
+ "hello.1.1", 1, "hello_1_",
+ "hel,lo", 1, "hel#lo",
+ "Salut.Tu.vas.bien?", 1, "salut_tu",
+ ".profile", 1, "_profile",
+ ".xv", 1, "_xv",
+ "toto.", 1, "toto_",
+ "clock$.x", 1, "-clock$",
+ "emmxxxx0", 1, "-emmxxxx",
+ "emmxxxx0.abcd", 1, "-emmxxxx",
+ "aux", 1, "-aux",
+ "prn", 1, "-prn",
+ "prn.abc", 1, "-prn",
+ "PRN", 1, "-prn",
+ /*
+ * GLU WARNING: the results of these are different with my version
+ * GLU of mangling compared to the original one.
+ * GLU CAUSE: the manner of calculating the baselen variable.
+ * GLU For you they are always 3.
+ * GLU For me they are respectively 7, 8, and 8.
+
+ */
+ "PRN.abc", 1, "prn_abc",
+ "Prn.abcd", 1, "prn_abcd",
+ "prn.abcd", 1, "prn_abcd",
+ "Prn.abcdefghij", 1, "prn_abcd"
+};
+
+int main (int argc, char *argv[])
+{
+ int i, rold, rnew;
+
+ printf ("Testing the umsdos_parse.\n");
+ for (i = 0; i < sizeof (tb) / sizeof (tb[0]); i++) {
+ struct MANG_TEST *pttb = tb + i;
+ struct umsdos_info info;
+ int ok = umsdos_parse (pttb->fname, strlen (pttb->fname), &info);
+
+ if (strcmp (info.fake.fname, pttb->msname) != 0) {
+ printf ("**** %s -> ", pttb->fname);
+ printf ("%s <> %s\n", info.fake.fname, pttb->msname);
+ } else if (info.msdos_reject != pttb->msdos_reject) {
+ printf ("**** %s -> %s ", pttb->fname, pttb->msname);
+ printf ("%d <> %d\n", info.msdos_reject, pttb->msdos_reject);
+ } else {
+ printf (" %s -> %s %d\n", pttb->fname, pttb->msname
+ ,pttb->msdos_reject);
+ }
+ }
+ printf ("Testing the new umsdos_evalrecsize.");
+ for (i = 0; i < UMSDOS_MAXNAME; i++) {
+ rnew = umsdos_evalrecsize (i);
+ rold = umsdos_evalrecsize_old (i);
+ if (!(i % UMSDOS_REC_SIZE)) {
+ printf ("\n%d:\t", i);
+ }
+ if (rnew != rold) {
+ printf ("**** %d newres: %d != %d \n", i, rnew, rold);
+ } else {
+ printf (".");
+ }
+ }
+ printf ("\nEnd of Testing.\n");
+
+ return 0;
+}
+
+#endif
diff --git a/fs/umsdos/namei.c b/fs/umsdos/namei.c
new file mode 100644
index 00000000000000..04a76c3a83a5ab
--- /dev/null
+++ b/fs/umsdos/namei.c
@@ -0,0 +1,1130 @@
+/*
+ * linux/fs/umsdos/namei.c
+ *
+ * Written 1993 by Jacques Gelinas
+ * Inspired from linux/fs/msdos/... by Werner Almesberger
+ *
+ * Maintain and access the --linux alternate directory file.
+ */
+ /*
+ * You are in the maze of twisted functions - half of them shouldn't
+ * be here...
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/msdos_fs.h>
+#include <linux/umsdos_fs.h>
+#include <linux/slab.h>
+
+#define UMSDOS_DIR_LOCK
+
+#ifdef UMSDOS_DIR_LOCK
+
+static inline void u_sleep_on (struct inode *dir)
+{
+ sleep_on (&dir->u.umsdos_i.dir_info.p);
+}
+
+static inline void u_wake_up (struct inode *dir)
+{
+ wake_up (&dir->u.umsdos_i.dir_info.p);
+}
+
+/*
+ * Wait for creation exclusivity.
+ * Return 0 if the dir was already available.
+ * Return 1 if a wait was necessary.
+ * When 1 is return, it means a wait was done. It does not
+ * mean the directory is available.
+ */
+static int umsdos_waitcreate (struct inode *dir)
+{
+ int ret = 0;
+
+ if (dir->u.umsdos_i.dir_info.creating
+ && dir->u.umsdos_i.dir_info.pid != current->pid) {
+ PRINTK (("creating && dir_info.pid=%lu, current->pid=%u\n", dir->u.umsdos_i.dir_info.pid, current->pid));
+ u_sleep_on (dir);
+ ret = 1;
+ }
+ return ret;
+}
+
+/*
+ * Wait for any lookup process to finish
+ */
+static void umsdos_waitlookup (struct inode *dir)
+{
+ while (dir->u.umsdos_i.dir_info.looking) {
+ u_sleep_on (dir);
+ }
+}
+
+/*
+ * Lock all other process out of this directory.
+ */
+/* #Specification: file creation / not atomic
+ * File creation is a two step process. First we create (allocate)
+ * an entry in the EMD file and then (using the entry offset) we
+ * build a unique name for MSDOS. We create this name in the msdos
+ * space.
+ *
+ * We have to use semaphore (sleep_on/wake_up) to prevent lookup
+ * into a directory when we create a file or directory and to
+ * prevent creation while a lookup is going on. Since many lookup
+ * may happen at the same time, the semaphore is a counter.
+ *
+ * Only one creation is allowed at the same time. This protection
+ * may not be necessary. The problem arise mainly when a lookup
+ * or a readdir is done while a file is partially created. The
+ * lookup process see that as a "normal" problem and silently
+ * erase the file from the EMD file. Normal because a file
+ * may be erased during a MSDOS session, but not removed from
+ * the EMD file.
+ *
+ * The locking is done on a directory per directory basis. Each
+ * directory inode has its wait_queue.
+ *
+ * For some operation like hard link, things even get worse. Many
+ * creation must occur at once (atomic). To simplify the design
+ * a process is allowed to recursively lock the directory for
+ * creation. The pid of the locking process is kept along with
+ * a counter so a second level of locking is granted or not.
+ */
+void umsdos_lockcreate (struct inode *dir)
+{
+ /*
+ * Wait for any creation process to finish except
+ * if we (the process) own the lock
+ */
+ while (umsdos_waitcreate (dir) != 0);
+ dir->u.umsdos_i.dir_info.creating++;
+ dir->u.umsdos_i.dir_info.pid = current->pid;
+ umsdos_waitlookup (dir);
+}
+
+/*
+ * Lock all other process out of those two directories.
+ */
+static void umsdos_lockcreate2 (struct inode *dir1, struct inode *dir2)
+{
+ /*
+ * We must check that both directory are available before
+ * locking anyone of them. This is to avoid some deadlock.
+ * Thanks to dglaude@is1.vub.ac.be (GLAUDE DAVID) for pointing
+ * this to me.
+ */
+ while (1) {
+ if (umsdos_waitcreate (dir1) == 0
+ && umsdos_waitcreate (dir2) == 0) {
+ /* We own both now */
+ dir1->u.umsdos_i.dir_info.creating++;
+ dir1->u.umsdos_i.dir_info.pid = current->pid;
+ dir2->u.umsdos_i.dir_info.creating++;
+ dir2->u.umsdos_i.dir_info.pid = current->pid;
+ break;
+ }
+ }
+ umsdos_waitlookup (dir1);
+ umsdos_waitlookup (dir2);
+}
+
+/*
+ * Wait until creation is finish in this directory.
+ */
+void umsdos_startlookup (struct inode *dir)
+{
+ while (umsdos_waitcreate (dir) != 0);
+ dir->u.umsdos_i.dir_info.looking++;
+}
+
+/*
+ * Unlock the directory.
+ */
+void umsdos_unlockcreate (struct inode *dir)
+{
+ dir->u.umsdos_i.dir_info.creating--;
+ if (dir->u.umsdos_i.dir_info.creating < 0) {
+ printk ("UMSDOS: dir->u.umsdos_i.dir_info.creating < 0: %d"
+ ,dir->u.umsdos_i.dir_info.creating);
+ }
+ u_wake_up (dir);
+}
+
+/*
+ * Tell directory lookup is over.
+ */
+void umsdos_endlookup (struct inode *dir)
+{
+ dir->u.umsdos_i.dir_info.looking--;
+ if (dir->u.umsdos_i.dir_info.looking < 0) {
+ printk ("UMSDOS: dir->u.umsdos_i.dir_info.looking < 0: %d"
+ ,dir->u.umsdos_i.dir_info.looking);
+ }
+ u_wake_up (dir);
+}
+
+#else
+static void umsdos_lockcreate (struct inode *dir)
+{
+}
+static void umsdos_lockcreate2 (struct inode *dir1, struct inode *dir2)
+{
+}
+void umsdos_startlookup (struct inode *dir)
+{
+}
+static void umsdos_unlockcreate (struct inode *dir)
+{
+}
+void umsdos_endlookup (struct inode *dir)
+{
+}
+
+#endif
+
+static int umsdos_nevercreat (struct inode *dir, struct dentry *dentry,
+ int errcod)
+{
+ int ret = 0;
+
+ if (umsdos_is_pseudodos (dir, dentry)) {
+ /* #Specification: pseudo root / any file creation /DOS
+ * The pseudo sub-directory /DOS can't be created!
+ * EEXIST is returned.
+ *
+ * The pseudo sub-directory /DOS can't be removed!
+ * EPERM is returned.
+ */
+ ret = errcod;
+ }
+ return ret;
+}
+
+/*
+ * Add a new file (ordinary or special) into the alternate directory.
+ * The file is added to the real MSDOS directory. If successful, it
+ * is then added to the EMD file.
+ *
+ * Return the status of the operation. 0 mean success.
+ *
+ * #Specification: create / file exists in DOS
+ * Here is a situation: we are trying to create a file with
+ * UMSDOS. The file is unknown to UMSDOS but already
+ * exists in the DOS directory.
+ *
+ * Here is what we are NOT doing:
+ *
+ * We could silently assume that everything is fine
+ * and allows the creation to succeed.
+ *
+ * It is possible not all files in the partition
+ * are meant to be visible from linux. By trying to create
+ * those file in some directory, one user may get access
+ * to those file without proper permissions. Looks like
+ * a security hole to me. Off course sharing a file system
+ * with DOS is some kind of security hole :-)
+ *
+ * So ?
+ *
+ * We return EEXIST in this case.
+ * The same is true for directory creation.
+ */
+static int umsdos_create_any (struct inode *dir, struct dentry *dentry,
+ int mode, int rdev, char flags)
+{
+ struct dentry *fake;
+ struct inode *inode;
+ int ret;
+ struct umsdos_info info;
+
+ ret = umsdos_nevercreat (dir, dentry, -EEXIST);
+ if (ret)
+ goto out;
+
+ ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
+ if (ret)
+ goto out;
+
+ info.entry.mode = mode;
+ info.entry.rdev = rdev;
+ info.entry.flags = flags;
+ info.entry.uid = current->fsuid;
+ info.entry.gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid;
+ info.entry.ctime = info.entry.atime = info.entry.mtime = CURRENT_TIME;
+ info.entry.nlink = 1;
+ ret = umsdos_newentry (dentry->d_parent, &info);
+ if (ret)
+ goto out;
+
+ /* do a real lookup to get the short name dentry */
+ fake = umsdos_covered(dentry->d_parent, info.fake.fname, info.fake.len);
+ ret = PTR_ERR(fake);
+ if (IS_ERR(fake))
+ goto out_remove;
+
+ /* should not exist yet ... */
+ ret = -EEXIST;
+ if (fake->d_inode)
+ goto out_remove_dput;
+
+ ret = msdos_create (dir, fake, S_IFREG | 0777);
+ if (ret)
+ goto out_remove_dput;
+
+ inode = fake->d_inode;
+ atomic_inc(&inode->i_count);
+ d_instantiate (dentry, inode);
+ dput(fake);
+ if (atomic_read(&inode->i_count) > 1) {
+ printk(KERN_WARNING
+ "umsdos_create_any: %s/%s, ino=%ld, icount=%d??\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ inode->i_ino, atomic_read(&inode->i_count));
+ }
+ umsdos_lookup_patch_new(dentry, &info);
+
+out:
+ return ret;
+
+ /* Creation failed ... remove the EMD entry */
+out_remove_dput:
+ dput(fake);
+out_remove:
+ if (ret == -EEXIST)
+ printk(KERN_WARNING "UMSDOS: out of sync, deleting %s/%s\n",
+ dentry->d_parent->d_name.name, info.fake.fname);
+ umsdos_delentry (dentry->d_parent, &info, S_ISDIR (info.entry.mode));
+ goto out;
+}
+
+/*
+ * Add a new file into the alternate directory.
+ * The file is added to the real MSDOS directory. If successful, it
+ * is then added to the EMD file.
+ *
+ * Return the status of the operation. 0 mean success.
+ */
+int UMSDOS_create (struct inode *dir, struct dentry *dentry, int mode)
+{
+ return umsdos_create_any (dir, dentry, mode, 0, 0);
+}
+
+
+/*
+ * Initialise the new_entry from the old for a rename operation.
+ * (Only useful for umsdos_rename_f() below).
+ */
+static void umsdos_ren_init (struct umsdos_info *new_info,
+ struct umsdos_info *old_info)
+{
+ new_info->entry.mode = old_info->entry.mode;
+ new_info->entry.rdev = old_info->entry.rdev;
+ new_info->entry.uid = old_info->entry.uid;
+ new_info->entry.gid = old_info->entry.gid;
+ new_info->entry.ctime = old_info->entry.ctime;
+ new_info->entry.atime = old_info->entry.atime;
+ new_info->entry.mtime = old_info->entry.mtime;
+ new_info->entry.flags = old_info->entry.flags;
+ new_info->entry.nlink = old_info->entry.nlink;
+}
+
+/*
+ * Rename a file (move) in the file system.
+ */
+
+static int umsdos_rename_f (struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry,
+ int flags)
+{
+ struct inode *old_inode = old_dentry->d_inode;
+ struct dentry *old, *new, *old_emd;
+ int err, ret;
+ struct umsdos_info old_info;
+ struct umsdos_info new_info;
+
+ ret = -EPERM;
+ err = umsdos_parse (old_dentry->d_name.name,
+ old_dentry->d_name.len, &old_info);
+ if (err)
+ goto out;
+ err = umsdos_parse (new_dentry->d_name.name,
+ new_dentry->d_name.len, &new_info);
+ if (err)
+ goto out;
+
+ /* Get the EMD dentry for the old parent */
+ old_emd = umsdos_get_emd_dentry(old_dentry->d_parent);
+ ret = PTR_ERR(old_emd);
+ if (IS_ERR(old_emd))
+ goto out;
+
+ umsdos_lockcreate2 (old_dir, new_dir);
+
+ ret = umsdos_findentry(old_emd->d_parent, &old_info, 0);
+ if (ret)
+ goto out_unlock;
+
+ err = umsdos_findentry(new_dentry->d_parent, &new_info, 0);
+ if (err == 0) {
+ /* check whether it _really_ exists ... */
+ ret = -EEXIST;
+ if (new_dentry->d_inode)
+ goto out_unlock;
+
+ /* bogus lookup? complain and fix up the EMD ... */
+ printk(KERN_WARNING
+ "umsdos_rename_f: entry %s/%s exists, inode NULL??\n",
+ new_dentry->d_parent->d_name.name, new_info.entry.name);
+ err = umsdos_delentry(new_dentry->d_parent, &new_info,
+ S_ISDIR(new_info.entry.mode));
+ }
+
+ umsdos_ren_init (&new_info, &old_info);
+ if (flags)
+ new_info.entry.flags = flags;
+ ret = umsdos_newentry (new_dentry->d_parent, &new_info);
+ if (ret)
+ goto out_unlock;
+
+ /* If we're moving a hardlink, drop it first */
+ if (old_info.entry.flags & UMSDOS_HLINK) {
+ d_drop(old_dentry);
+ }
+
+ old = umsdos_covered(old_dentry->d_parent, old_info.fake.fname,
+ old_info.fake.len);
+ ret = PTR_ERR(old);
+ if (IS_ERR(old))
+ goto out_unlock;
+ /* make sure it's the same inode! */
+ ret = -ENOENT;
+ /*
+ * note: for hardlinks they will be different!
+ * old_inode will contain inode of .LINKxxx file containing data, and
+ * old->d_inode will contain inode of file containing path to .LINKxxx file
+ */
+ if (!(old_info.entry.flags & UMSDOS_HLINK)) {
+ if (old->d_inode != old_inode)
+ goto out_dput;
+ }
+
+ new = umsdos_covered(new_dentry->d_parent, new_info.fake.fname,
+ new_info.fake.len);
+ ret = PTR_ERR(new);
+ if (IS_ERR(new))
+ goto out_dput;
+
+ /* Do the msdos-level rename */
+ ret = msdos_rename (old_dir, old, new_dir, new);
+
+ dput(new);
+
+ /* If the rename failed, remove the new EMD entry */
+ if (ret != 0) {
+ umsdos_delentry (new_dentry->d_parent, &new_info,
+ S_ISDIR (new_info.entry.mode));
+ goto out_dput;
+ }
+
+ /*
+ * Rename successful ... remove the old name from the EMD.
+ * Note that we use the EMD parent here, as the old dentry
+ * may have moved to a new parent ...
+ */
+ err = umsdos_delentry (old_emd->d_parent, &old_info,
+ S_ISDIR (old_info.entry.mode));
+ if (err) {
+ /* Failed? Complain a bit, but don't fail the operation */
+ printk(KERN_WARNING
+ "umsdos_rename_f: delentry %s/%s failed, error=%d\n",
+ old_emd->d_parent->d_name.name, old_info.entry.name,
+ err);
+ }
+
+ /*
+ * Update f_pos so notify_change will succeed
+ * if the file was already in use.
+ */
+ umsdos_set_dirinfo_new(old_dentry, new_info.f_pos);
+
+ /* dput() the dentry if we haven't already */
+out_dput:
+ dput(old);
+
+out_unlock:
+ dput(old_emd);
+ umsdos_unlockcreate (old_dir);
+ umsdos_unlockcreate (new_dir);
+
+out:
+ Printk ((" _ret=%d\n", ret));
+ return ret;
+}
+
+/*
+ * Setup a Symbolic link or a (pseudo) hard link
+ * Return a negative error code or 0 if OK.
+ */
+/* #Specification: symbolic links / strategy
+ * A symbolic link is simply a file which holds a path. It is
+ * implemented as a normal MSDOS file (not very space efficient :-()
+ *
+ * I see two different ways to do this: One is to place the link data
+ * in unused entries of the EMD file; the other is to have a separate
+ * file dedicated to hold all symbolic links data.
+ *
+ * Let's go for simplicity...
+ */
+
+/*
+ * AV. Should be called with dir->i_sem down.
+ */
+static int umsdos_symlink_x (struct inode *dir, struct dentry *dentry,
+ const char *symname, int mode, char flags)
+{
+ int ret, len;
+
+ ret = umsdos_create_any (dir, dentry, mode, 0, flags);
+ if (ret) {
+ printk(KERN_WARNING
+ "umsdos_symlink: create failed, ret=%d\n", ret);
+ goto out;
+ }
+
+ len = strlen (symname) + 1;
+ ret = block_symlink(dentry->d_inode, symname, len);
+ if (ret < 0)
+ goto out_unlink;
+out:
+ return ret;
+
+out_unlink:
+ printk(KERN_WARNING "umsdos_symlink: write failed, unlinking\n");
+ UMSDOS_unlink (dir, dentry);
+ d_drop(dentry);
+ goto out;
+}
+
+/*
+ * Setup a Symbolic link.
+ * Return a negative error code or 0 if OK.
+ */
+int UMSDOS_symlink ( struct inode *dir, struct dentry *dentry,
+ const char *symname)
+{
+ return umsdos_symlink_x (dir, dentry, symname, S_IFLNK | 0777, 0);
+}
+
+/*
+ * Add a link to an inode in a directory
+ */
+int UMSDOS_link (struct dentry *olddentry, struct inode *dir,
+ struct dentry *dentry)
+{
+ struct inode *oldinode = olddentry->d_inode;
+ struct inode *olddir = olddentry->d_parent->d_inode;
+ struct dentry *temp;
+ char *path;
+ unsigned long buffer;
+ int ret;
+ struct umsdos_info old_info;
+ struct umsdos_info hid_info;
+
+#ifdef UMSDOS_DEBUG_VERBOSE
+printk("umsdos_link: new %s/%s -> %s/%s\n",
+dentry->d_parent->d_name.name, dentry->d_name.name,
+olddentry->d_parent->d_name.name, olddentry->d_name.name);
+#endif
+
+ ret = -EPERM;
+ if (S_ISDIR (oldinode->i_mode))
+ goto out;
+
+ ret = umsdos_nevercreat (dir, dentry, -EPERM);
+ if (ret)
+ goto out;
+
+ ret = -ENOMEM;
+ buffer = get_free_page(GFP_KERNEL);
+ if (!buffer)
+ goto out;
+
+ /*
+ * Lock the link parent if it's not the same directory.
+ */
+ ret = -EDEADLOCK;
+ if (olddir != dir) {
+ if (atomic_read(&olddir->i_sem.count) < 1)
+ goto out_free;
+ down(&olddir->i_sem);
+ }
+
+ /*
+ * Parse the name and get the visible directory entry.
+ */
+ ret = umsdos_parse (olddentry->d_name.name, olddentry->d_name.len,
+ &old_info);
+ if (ret)
+ goto out_unlock;
+ ret = umsdos_findentry (olddentry->d_parent, &old_info, 1);
+ if (ret) {
+printk("UMSDOS_link: %s/%s not in EMD, ret=%d\n",
+olddentry->d_parent->d_name.name, olddentry->d_name.name, ret);
+ goto out_unlock;
+ }
+
+ /*
+ * If the visible dentry is a pseudo-hardlink, the original
+ * file must be already hidden.
+ */
+ if (!(old_info.entry.flags & UMSDOS_HLINK)) {
+ int err;
+
+ /* create a hidden link name */
+ ret = umsdos_newhidden (olddentry->d_parent, &hid_info);
+ if (ret) {
+printk("umsdos_link: can't make hidden %s/%s, ret=%d\n",
+olddentry->d_parent->d_name.name, hid_info.entry.name, ret);
+ goto out_unlock;
+ }
+
+ /*
+ * Make a dentry and rename the original file ...
+ */
+ temp = umsdos_lookup_dentry(olddentry->d_parent,
+ hid_info.entry.name,
+ hid_info.entry.name_len, 0);
+ ret = PTR_ERR(temp);
+ if (IS_ERR(temp)) {
+printk("umsdos_link: lookup %s/%s failed, ret=%d\n",
+dentry->d_parent->d_name.name, hid_info.entry.name, ret);
+ goto cleanup;
+ }
+ /* rename the link to the hidden location ... */
+ ret = umsdos_rename_f(olddir, olddentry, olddir, temp,
+ UMSDOS_HIDDEN);
+ d_move(olddentry, temp);
+ dput(temp);
+ if (ret) {
+printk("umsdos_link: rename to %s/%s failed, ret=%d\n",
+temp->d_parent->d_name.name, temp->d_name.name, ret);
+ goto cleanup;
+ }
+ /*
+ * Capture the path to the hidden link.
+ */
+ path = umsdos_d_path(olddentry, (char *) buffer, PAGE_SIZE);
+ if (IS_ERR(path)) {
+ ret = PTR_ERR(path);
+ goto cleanup;
+ }
+Printk(("umsdos_link: hidden link path=%s\n", path));
+
+ /* mark the inode as a hardlink */
+ oldinode->u.umsdos_i.i_is_hlink = 1;
+
+ /*
+ * Recreate a dentry for the original name and symlink it,
+ * then symlink the new dentry. Don't give up if one fails,
+ * or we'll lose the file completely!
+ *
+ * Note: this counts as the "original" reference, so we
+ * don't increment i_nlink for this one.
+ */
+ temp = umsdos_lookup_dentry(olddentry->d_parent,
+ old_info.entry.name,
+ old_info.entry.name_len, 0);
+ ret = PTR_ERR(temp);
+ if (!IS_ERR(temp)) {
+ ret = umsdos_symlink_x (olddir, temp, path,
+ S_IFREG | 0777, UMSDOS_HLINK);
+ dput(temp);
+ }
+
+ /* This symlink increments i_nlink (see below.) */
+ err = umsdos_symlink_x (dir, dentry, path,
+ S_IFREG | 0777, UMSDOS_HLINK);
+ /* fold the two errors */
+ if (!ret)
+ ret = err;
+ goto out_unlock;
+
+ /* creation failed ... remove the link entry */
+ cleanup:
+printk("umsdos_link: link failed, ret=%d, removing %s/%s\n",
+ret, olddentry->d_parent->d_name.name, hid_info.entry.name);
+ err = umsdos_delentry(olddentry->d_parent, &hid_info, 0);
+ goto out_unlock;
+ }
+
+Printk(("UMSDOS_link: %s/%s already hidden\n",
+olddentry->d_parent->d_name.name, olddentry->d_name.name));
+ /*
+ * The original file is already hidden, and we need to get
+ * the dentry for its real name, not the visible name.
+ * N.B. make sure it's the hidden inode ...
+ */
+ if (!oldinode->u.umsdos_i.i_is_hlink)
+ printk("UMSDOS_link: %s/%s hidden, ino=%ld not hlink??\n",
+ olddentry->d_parent->d_name.name,
+ olddentry->d_name.name, oldinode->i_ino);
+
+ /*
+ * In order to get the correct (real) inode, we just drop
+ * the original dentry.
+ */
+ d_drop(olddentry);
+Printk(("UMSDOS_link: hard link %s/%s, fake=%s\n",
+olddentry->d_parent->d_name.name, olddentry->d_name.name, old_info.fake.fname));
+
+ /* Do a real lookup to get the short name dentry */
+ temp = umsdos_covered(olddentry->d_parent, old_info.fake.fname,
+ old_info.fake.len);
+ ret = PTR_ERR(temp);
+ if (IS_ERR(temp))
+ goto out_unlock;
+
+ /* now resolve the link ... */
+ temp = umsdos_solve_hlink(temp);
+ ret = PTR_ERR(temp);
+ if (IS_ERR(temp))
+ goto out_unlock;
+ path = umsdos_d_path(temp, (char *) buffer, PAGE_SIZE);
+ dput(temp);
+ if (IS_ERR(path))
+ goto out_unlock;
+Printk(("umsdos_link: %s/%s already hidden, path=%s\n",
+olddentry->d_parent->d_name.name, olddentry->d_name.name, path));
+
+ /* finally we can symlink it ... */
+ ret = umsdos_symlink_x (dir, dentry, path, S_IFREG | 0777,UMSDOS_HLINK);
+
+out_unlock:
+ /* remain locked for the call to notify_change ... */
+ if (ret == 0) {
+ struct iattr newattrs;
+
+ /* Do a real lookup to get the short name dentry */
+ temp = umsdos_covered(olddentry->d_parent,
+ old_info.fake.fname,
+ old_info.fake.len);
+ ret = PTR_ERR(temp);
+ if (IS_ERR(temp))
+ goto out_unlock2;
+
+ /* now resolve the link ... */
+ temp = umsdos_solve_hlink(temp);
+ ret = PTR_ERR(temp);
+ if (IS_ERR(temp))
+ goto out_unlock2;
+
+
+#ifdef UMSDOS_PARANOIA
+if (!oldinode->u.umsdos_i.i_is_hlink)
+printk("UMSDOS_link: %s/%s, ino=%ld, not marked as hlink!\n",
+olddentry->d_parent->d_name.name, olddentry->d_name.name, oldinode->i_ino);
+#endif
+ temp->d_inode->i_nlink++;
+Printk(("UMSDOS_link: linked %s/%s, ino=%ld, nlink=%d\n",
+olddentry->d_parent->d_name.name, olddentry->d_name.name,
+oldinode->i_ino, oldinode->i_nlink));
+ newattrs.ia_valid = 0;
+ ret = umsdos_notify_change_locked(temp, &newattrs);
+ if (ret == 0)
+ mark_inode_dirty(temp->d_inode);
+ dput(temp);
+out_unlock2:
+ if (ret == 0)
+ mark_inode_dirty(olddentry->d_inode);
+ }
+ if (olddir != dir)
+ up(&olddir->i_sem);
+
+out_free:
+ free_page(buffer);
+out:
+ Printk (("umsdos_link %d\n", ret));
+ return ret;
+}
+
+
+/*
+ * Add a sub-directory in a directory
+ */
+/* #Specification: mkdir / Directory already exist in DOS
+ * We do the same thing as for file creation.
+ * For all user it is an error.
+ */
+/* #Specification: mkdir / umsdos directory / create EMD
+ * When we created a new sub-directory in a UMSDOS
+ * directory (one with full UMSDOS semantics), we
+ * create immediately an EMD file in the new
+ * sub-directory so it inherits UMSDOS semantics.
+ */
+int UMSDOS_mkdir (struct inode *dir, struct dentry *dentry, int mode)
+{
+ struct dentry *temp;
+ struct inode *inode;
+ int ret, err;
+ struct umsdos_info info;
+
+ ret = umsdos_nevercreat (dir, dentry, -EEXIST);
+ if (ret)
+ goto out;
+
+ ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
+ if (ret)
+ goto out;
+
+ info.entry.mode = mode | S_IFDIR;
+ info.entry.rdev = 0;
+ info.entry.uid = current->fsuid;
+ info.entry.gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid;
+ info.entry.ctime = info.entry.atime = info.entry.mtime = CURRENT_TIME;
+ info.entry.flags = 0;
+ info.entry.nlink = 1;
+ ret = umsdos_newentry (dentry->d_parent, &info);
+ if (ret)
+ goto out;
+
+ /* lookup the short name dentry */
+ temp = umsdos_covered(dentry->d_parent, info.fake.fname, info.fake.len);
+ ret = PTR_ERR(temp);
+ if (IS_ERR(temp))
+ goto out_remove;
+
+ /* Make sure the short name doesn't exist */
+ ret = -EEXIST;
+ if (temp->d_inode) {
+printk("umsdos_mkdir: short name %s/%s exists\n",
+dentry->d_parent->d_name.name, info.fake.fname);
+ goto out_remove_dput;
+ }
+
+ ret = msdos_mkdir (dir, temp, mode);
+ if (ret)
+ goto out_remove_dput;
+
+ /*
+ * Lock the inode to protect the EMD creation ...
+ */
+ inode = temp->d_inode;
+ down(&inode->i_sem);
+
+ atomic_inc(&inode->i_count);
+ d_instantiate(dentry, inode);
+
+ /* N.B. this should have an option to create the EMD ... */
+ umsdos_lookup_patch_new(dentry, &info);
+
+ /*
+ * Create the EMD file, and set up the dir so it is
+ * promoted to EMD with the EMD file invisible.
+ *
+ * N.B. error return if EMD fails?
+ */
+ err = umsdos_make_emd(dentry);
+ umsdos_setup_dir(dentry);
+
+ up(&inode->i_sem);
+ dput(temp);
+
+out:
+ Printk(("umsdos_mkdir: %s/%s, ret=%d\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name, ret));
+ return ret;
+
+ /* an error occurred ... remove EMD entry. */
+out_remove_dput:
+ dput(temp);
+out_remove:
+ umsdos_delentry (dentry->d_parent, &info, 1);
+ goto out;
+}
+
+/*
+ * Add a new device special file into a directory.
+ *
+ * #Specification: Special files / strategy
+ * Device special file, pipes, etc ... are created like normal
+ * file in the msdos file system. Of course they remain empty.
+ *
+ * One strategy was to create those files only in the EMD file
+ * since they were not important for MSDOS. The problem with
+ * that, is that there were not getting inode number allocated.
+ * The MSDOS filesystems is playing a nice game to fake inode
+ * number, so why not use it.
+ *
+ * The absence of inode number compatible with those allocated
+ * for ordinary files was causing major trouble with hard link
+ * in particular and other parts of the kernel I guess.
+ */
+int UMSDOS_mknod (struct inode *dir, struct dentry *dentry,
+ int mode, int rdev)
+{
+ return umsdos_create_any (dir, dentry, mode, rdev, 0);
+}
+
+/*
+ * Remove a sub-directory.
+ */
+int UMSDOS_rmdir (struct inode *dir, struct dentry *dentry)
+{
+ struct dentry *temp;
+ int ret, err, empty;
+ struct umsdos_info info;
+
+ ret = umsdos_nevercreat (dir, dentry, -EPERM);
+ if (ret)
+ goto out;
+
+ ret = -EBUSY;
+ if (!d_unhashed(dentry))
+ goto out;
+
+ /* check whether the EMD is empty */
+ ret = -ENOTEMPTY;
+ empty = umsdos_isempty (dentry);
+
+ /* Have to remove the EMD file? */
+ if (empty == 1) {
+ struct dentry *demd;
+
+ demd = umsdos_get_emd_dentry(dentry);
+ if (!IS_ERR(demd)) {
+ err = -ENOENT;
+ if (demd->d_inode)
+ err = msdos_unlink (dentry->d_inode, demd);
+Printk (("UMSDOS_rmdir: unlinking empty EMD err=%d", err));
+#ifdef UMSDOS_PARANOIA
+if (err)
+printk("umsdos_rmdir: EMD %s/%s unlink failed, err=%d\n",
+demd->d_parent->d_name.name, demd->d_name.name, err);
+#endif
+ if (!err) {
+ d_delete(demd);
+ ret = 0;
+ }
+ dput(demd);
+ }
+ } else if (empty == 2)
+ ret = 0;
+ if (ret)
+ goto out;
+
+ umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
+ /* Call findentry to complete the mangling */
+ umsdos_findentry (dentry->d_parent, &info, 2);
+ temp = umsdos_covered(dentry->d_parent, info.fake.fname, info.fake.len);
+ ret = PTR_ERR(temp);
+ if (IS_ERR(temp))
+ goto out;
+ /*
+ * Attempt to remove the msdos name.
+ */
+ ret = msdos_rmdir (dir, temp);
+ if (ret && ret != -ENOENT)
+ goto out_dput;
+
+ d_delete(temp);
+ /* OK so far ... remove the name from the EMD */
+ ret = umsdos_delentry (dentry->d_parent, &info, 1);
+#ifdef UMSDOS_PARANOIA
+if (ret)
+printk("umsdos_rmdir: delentry %s failed, ret=%d\n", info.entry.name, ret);
+#endif
+
+ /* dput() temp if we didn't do it above */
+out_dput:
+ dput(temp);
+
+out:
+ Printk (("umsdos_rmdir %d\n", ret));
+ return ret;
+}
+
+
+/*
+ * Remove a file from the directory.
+ *
+ * #Specification: hard link / deleting a link
+ * When we delete a file and this file is a link,
+ * we must subtract 1 from the nlink field of the
+ * hidden link.
+ *
+ * If the count goes to 0, we delete this hidden
+ * link too.
+ */
+int UMSDOS_unlink (struct inode *dir, struct dentry *dentry)
+{
+ struct dentry *temp, *link = NULL;
+ struct inode *inode;
+ int ret;
+ struct umsdos_info info;
+
+Printk(("UMSDOS_unlink: entering %s/%s\n",
+dentry->d_parent->d_name.name, dentry->d_name.name));
+
+ ret = umsdos_nevercreat (dir, dentry, -EPERM);
+ if (ret)
+ goto out;
+
+ ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
+ if (ret)
+ goto out;
+
+ umsdos_lockcreate (dir);
+ ret = umsdos_findentry (dentry->d_parent, &info, 1);
+ if (ret) {
+printk("UMSDOS_unlink: %s/%s not in EMD, ret=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, ret);
+ goto out_unlock;
+ }
+
+Printk (("UMSDOS_unlink %.*s ", info.fake.len, info.fake.fname));
+
+ /*
+ * Note! If this is a hardlink and the names are aliased,
+ * the short-name lookup will return the hardlink dentry.
+ * In order to get the correct (real) inode, we just drop
+ * the original dentry.
+ */
+ if (info.entry.flags & UMSDOS_HLINK) {
+ d_drop(dentry);
+ }
+
+ /* Do a real lookup to get the short name dentry */
+ temp = umsdos_covered(dentry->d_parent, info.fake.fname, info.fake.len);
+ ret = PTR_ERR(temp);
+ if (IS_ERR(temp))
+ goto out_unlock;
+
+ /*
+ * Resolve hardlinks now, but defer processing until later.
+ */
+ if (info.entry.flags & UMSDOS_HLINK) {
+ link = umsdos_solve_hlink(dget(temp));
+ }
+
+ /* Delete the EMD entry */
+ ret = umsdos_delentry (dentry->d_parent, &info, 0);
+ if (ret && ret != -ENOENT) {
+ printk(KERN_WARNING "UMSDOS_unlink: delentry %s, error=%d\n",
+ info.entry.name, ret);
+ goto out_dput;
+ }
+
+ ret = msdos_unlink(dir, temp);
+ if (!ret)
+ d_delete(temp);
+#ifdef UMSDOS_PARANOIA
+if (ret)
+printk("umsdos_unlink: %s/%s unlink failed, ret=%d\n",
+temp->d_parent->d_name.name, temp->d_name.name, ret);
+#endif
+
+ /* dput() temp if we didn't do it above */
+out_dput:
+ dput(temp);
+
+out_unlock:
+ umsdos_unlockcreate (dir);
+
+ /*
+ * Now check for deferred handling of a hardlink.
+ */
+ if (!link)
+ goto out;
+
+ if (IS_ERR(link)) {
+printk("umsdos_unlink: failed to resolve %s/%s\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+ if (!ret)
+ ret = PTR_ERR(link);
+ goto out;
+ }
+
+Printk(("umsdos_unlink: link %s/%s deferred, pending ret=%d\n",
+link->d_parent->d_name.name, link->d_name.name, ret));
+
+ /* already have an error? */
+ if (ret)
+ goto out_cleanup;
+
+ /* make sure the link exists ... */
+ inode = link->d_inode;
+ if (!inode) {
+ printk(KERN_WARNING "umsdos_unlink: hard link not found\n");
+ goto out_cleanup;
+ }
+
+ /*
+ * If this was the last linked reference, delete it now.
+ *
+ * N.B. Deadlock problem? We should be holding the lock
+ * for the hardlink's parent, but another process might
+ * be holding that lock waiting for us to finish ...
+ */
+ if (inode->i_nlink <= 1) {
+ ret = UMSDOS_unlink (link->d_parent->d_inode, link);
+ if (ret) {
+ printk(KERN_WARNING
+ "umsdos_unlink: link removal failed, ret=%d\n",
+ ret);
+ } else
+ d_delete(link);
+ } else {
+ struct iattr newattrs;
+ inode->i_nlink--;
+ newattrs.ia_valid = 0;
+ ret = umsdos_notify_change_locked(link, &newattrs);
+ if (!ret)
+ mark_inode_dirty(link->d_inode);
+ }
+
+out_cleanup:
+ d_drop(link);
+ dput(link);
+
+out:
+ Printk (("umsdos_unlink %d\n", ret));
+ return ret;
+}
+
+/*
+ * Rename (move) a file.
+ */
+int UMSDOS_rename (struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ int ret;
+
+ ret = umsdos_nevercreat (new_dir, new_dentry, -EEXIST);
+ if (ret)
+ return ret;
+
+ /*
+ * If the target already exists, delete it first.
+ */
+ if (new_dentry->d_inode) {
+ dget(new_dentry);
+ if (S_ISDIR(old_dentry->d_inode->i_mode))
+ ret = UMSDOS_rmdir (new_dir, new_dentry);
+ else
+ ret = UMSDOS_unlink (new_dir, new_dentry);
+ if (!ret)
+ d_drop(new_dentry);
+ dput(new_dentry);
+ if (ret)
+ return ret;
+ }
+ ret = umsdos_rename_f(old_dir, old_dentry, new_dir, new_dentry, 0);
+ return ret;
+}
diff --git a/fs/umsdos/notes b/fs/umsdos/notes
new file mode 100644
index 00000000000000..3c47d1f4fc4732
--- /dev/null
+++ b/fs/umsdos/notes
@@ -0,0 +1,17 @@
+This file contain idea and things I don't want to forget
+
+Possible bug in fs/read_write.c
+Function sys_readdir()
+
+ There is a call the verify_area that does not take in account
+ the count parameter. I guess it should read
+
+ error = verify_area(VERIFY_WRITE, dirent, count*sizeof (*dirent));
+
+ instead of
+
+ error = verify_area(VERIFY_WRITE, dirent, sizeof (*dirent));
+
+ Of course, now , count is always 1
+
+
diff --git a/fs/umsdos/rdir.c b/fs/umsdos/rdir.c
new file mode 100644
index 00000000000000..9fbca394193446
--- /dev/null
+++ b/fs/umsdos/rdir.c
@@ -0,0 +1,243 @@
+/*
+ * linux/fs/umsdos/rdir.c
+ *
+ * Written 1994 by Jacques Gelinas
+ *
+ * Extended MS-DOS directory pure MS-DOS handling functions
+ * (For directory without EMD file).
+ */
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/errno.h>
+#include <linux/stat.h>
+#include <linux/limits.h>
+#include <linux/umsdos_fs.h>
+#include <linux/slab.h>
+
+#include <asm/uaccess.h>
+
+
+extern struct dentry *saved_root;
+extern struct inode *pseudo_root;
+extern struct dentry_operations umsdos_dentry_operations;
+
+struct RDIR_FILLDIR {
+ void *dirbuf;
+ filldir_t filldir;
+ int real_root;
+};
+
+static int rdir_filldir ( void *buf,
+ const char *name,
+ int name_len,
+ loff_t offset,
+ ino_t ino,
+ unsigned int d_type)
+{
+ int ret = 0;
+ struct RDIR_FILLDIR *d = (struct RDIR_FILLDIR *) buf;
+
+ if (d->real_root) {
+ PRINTK ((KERN_DEBUG "rdir_filldir /mn/: real root!\n"));
+ /* real root of a pseudo_rooted partition */
+ if (name_len != UMSDOS_PSDROOT_LEN
+ || memcmp (name, UMSDOS_PSDROOT_NAME, UMSDOS_PSDROOT_LEN) != 0) {
+ /* So it is not the /linux directory */
+ if (name_len == 2 && name[0] == '.' && name[1] == '.') {
+ /* Make sure the .. entry points back to the pseudo_root */
+ ino = pseudo_root->i_ino;
+ }
+ ret = d->filldir (d->dirbuf, name, name_len, offset, ino, DT_UNKNOWN);
+ }
+ } else {
+ /* Any DOS directory */
+ ret = d->filldir (d->dirbuf, name, name_len, offset, ino, DT_UNKNOWN);
+ }
+ return ret;
+}
+
+
+static int UMSDOS_rreaddir (struct file *filp, void *dirbuf, filldir_t filldir)
+{
+ struct inode *dir = filp->f_dentry->d_inode;
+ struct RDIR_FILLDIR bufk;
+
+ bufk.filldir = filldir;
+ bufk.dirbuf = dirbuf;
+ bufk.real_root = pseudo_root && (dir == saved_root->d_inode);
+ return fat_readdir (filp, &bufk, rdir_filldir);
+}
+
+
+/*
+ * Lookup into a non promoted directory.
+ * If the result is a directory, make sure we find out if it is
+ * a promoted one or not (calling umsdos_setup_dir_inode(inode)).
+ */
+/* #Specification: pseudo root / DOS/..
+ * In the real root directory (c:\), the directory ..
+ * is the pseudo root (c:\linux).
+ */
+struct dentry *umsdos_rlookup_x ( struct inode *dir, struct dentry *dentry, int nopseudo)
+{
+ struct dentry *ret;
+
+ if (saved_root && dir == saved_root->d_inode && !nopseudo &&
+ dentry->d_name.len == UMSDOS_PSDROOT_LEN &&
+ memcmp (dentry->d_name.name, UMSDOS_PSDROOT_NAME, UMSDOS_PSDROOT_LEN) == 0) {
+ /* #Specification: pseudo root / DOS/linux
+ * Even in the real root directory (c:\), the directory
+ * /linux won't show
+ */
+
+ ret = ERR_PTR(-ENOENT);
+ goto out;
+ }
+
+ ret = msdos_lookup (dir, dentry);
+ if (ret) {
+ printk(KERN_WARNING
+ "umsdos_rlookup_x: %s/%s failed, ret=%ld\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ PTR_ERR(ret));
+ goto out;
+ }
+ if (dentry->d_inode) {
+ /* We must install the proper function table
+ * depending on whether this is an MS-DOS or
+ * a UMSDOS directory
+ */
+Printk ((KERN_DEBUG "umsdos_rlookup_x: patch_dentry_inode %s/%s\n",
+dentry->d_parent->d_name.name, dentry->d_name.name));
+/* only patch if needed (because we get called even for lookup
+ (not only rlookup) stuff sometimes, like in umsdos_covered() */
+ if (dentry->d_inode->u.umsdos_i.i_patched == 0)
+ umsdos_patch_dentry_inode(dentry, 0);
+
+ }
+out:
+ /* always install our dentry ops ... */
+ dentry->d_op = &umsdos_dentry_operations;
+ return ret;
+}
+
+
+struct dentry *UMSDOS_rlookup ( struct inode *dir, struct dentry *dentry)
+{
+ return umsdos_rlookup_x (dir, dentry, 0);
+}
+
+
+/* #Specification: dual mode / rmdir in a DOS directory
+ * In a DOS (not EMD in it) directory, we use a reverse strategy
+ * compared with a UMSDOS directory. We assume that a subdirectory
+ * of a DOS directory is also a DOS directory. This is not always
+ * true (umssync may be used anywhere), but makes sense.
+ *
+ * So we call msdos_rmdir() directly. If it failed with a -ENOTEMPTY
+ * then we check if it is a Umsdos directory. We check if it is
+ * really empty (only . .. and --linux-.--- in it). If it is true
+ * we remove the EMD and do a msdos_rmdir() again.
+ *
+ * In a Umsdos directory, we assume all subdirectories are also
+ * Umsdos directories, so we check the EMD file first.
+ */
+/* #Specification: pseudo root / rmdir /DOS
+ * The pseudo sub-directory /DOS can't be removed!
+ * This is done even if the pseudo root is not a Umsdos
+ * directory anymore (very unlikely), but an accident (under
+ * MS-DOS) is always possible.
+ *
+ * EPERM is returned.
+ */
+static int UMSDOS_rrmdir ( struct inode *dir, struct dentry *dentry)
+{
+ int ret, empty;
+
+ ret = -EPERM;
+ if (umsdos_is_pseudodos (dir, dentry))
+ goto out;
+
+ ret = -EBUSY;
+ if (!d_unhashed(dentry))
+ goto out;
+
+ ret = msdos_rmdir (dir, dentry);
+ if (ret != -ENOTEMPTY)
+ goto out;
+
+ empty = umsdos_isempty (dentry);
+ if (empty == 1) {
+ struct dentry *demd;
+ /* We have to remove the EMD file. */
+ demd = umsdos_get_emd_dentry(dentry);
+ ret = PTR_ERR(demd);
+ if (!IS_ERR(demd)) {
+ ret = 0;
+ if (demd->d_inode)
+ ret = msdos_unlink (dentry->d_inode, demd);
+ if (!ret)
+ d_delete(demd);
+ dput(demd);
+ }
+ }
+ if (ret)
+ goto out;
+
+ /* now retry the original ... */
+ ret = msdos_rmdir (dir, dentry);
+
+out:
+ return ret;
+}
+
+/* #Specification: dual mode / introduction
+ * One goal of UMSDOS is to allow a practical and simple coexistence
+ * between MS-DOS and Linux in a single partition. Using the EMD file
+ * in each directory, UMSDOS adds Unix semantics and capabilities to
+ * a normal DOS filesystem. To help and simplify coexistence, here is
+ * the logic related to the EMD file.
+ *
+ * If it is missing, then the directory is managed by the MS-DOS driver.
+ * The names are limited to DOS limits (8.3). No links, no device special
+ * and pipe and so on.
+ *
+ * If it is there, it is the directory. If it is there but empty, then
+ * the directory looks empty. The utility umssync allows synchronisation
+ * of the real DOS directory and the EMD.
+ *
+ * Whenever umssync is applied to a directory without EMD, one is
+ * created on the fly. The directory is promoted to full Unix semantics.
+ * Of course, the ls command will show exactly the same content as before
+ * the umssync session.
+ *
+ * It is believed that the user/admin will promote directories to Unix
+ * semantics as needed.
+ *
+ * The strategy to implement this is to use two function table (struct
+ * inode_operations). One for true UMSDOS directory and one for directory
+ * with missing EMD.
+ *
+ * Functions related to the DOS semantic (but aware of UMSDOS) generally
+ * have a "r" prefix (r for real) such as UMSDOS_rlookup, to differentiate
+ * from the one with full UMSDOS semantics.
+ */
+struct file_operations umsdos_rdir_operations =
+{
+ read: generic_read_dir,
+ readdir: UMSDOS_rreaddir,
+ ioctl: UMSDOS_ioctl_dir,
+};
+
+struct inode_operations umsdos_rdir_inode_operations =
+{
+ create: msdos_create,
+ lookup: UMSDOS_rlookup,
+ unlink: msdos_unlink,
+ mkdir: msdos_mkdir,
+ rmdir: UMSDOS_rrmdir,
+ rename: msdos_rename,
+ setattr: UMSDOS_notify_change,
+};
diff --git a/fs/umsdos/specs b/fs/umsdos/specs
new file mode 100644
index 00000000000000..0f7d68c0aef610
--- /dev/null
+++ b/fs/umsdos/specs
@@ -0,0 +1,289 @@
+/* #Specification: umsdos / readdir
+ * umsdos_readdir() should fill a struct dirent with
+ * an inode number. The cheap way to get it is to
+ * do a lookup in the MSDOS directory for each
+ * entry processed by the readdir() function.
+ * This is not very efficient, but very simple. The
+ * other way around is to maintain a copy of the inode
+ * number in the EMD file. This is a problem because
+ * this has to be maintained in sync using tricks.
+ * Remember that MSDOS (the OS) does not update the
+ * modification time (mtime) of a directory. There is
+ * no easy way to tell that a directory was modified
+ * during a DOS session and synchronise the EMD file.
+ */
+ /* #Specification: readdir / . and ..
+ * The msdos filesystem manages the . and .. entry properly
+ * so the EMD file won't hold any info about it.
+ *
+ * In readdir, we assume that for the root directory
+ * the read position will be 0 for ".", 1 for "..". For
+ * a non root directory, the read position will be 0 for "."
+ * and 32 for "..".
+ */
+ /*
+ * This is a trick used by the msdos file system (fs/msdos/dir.c)
+ * to manage . and .. for the root directory of a file system.
+ * Since there is no such entry in the root, fs/msdos/dir.c
+ * use the following:
+ *
+ * if f_pos == 0, return ".".
+ * if f_pos == 1, return "..".
+ *
+ * So let msdos handle it
+ *
+ * Since umsdos entries are much larger, we share the same f_pos.
+ * if f_pos is 0 or 1 or 32, we are clearly looking at . and
+ * ..
+ *
+ * As soon as we get f_pos == 2 or f_pos == 64, then back to
+ * 0, but this time we are reading the EMD file.
+ *
+ * Well, not so true. The problem, is that UMSDOS_REC_SIZE is
+ * also 64, so as soon as we read the first record in the
+ * EMD, we are back at offset 64. So we set the offset
+ * to UMSDOS_SPECIAL_DIRFPOS(3) as soon as we have read the
+ * .. entry from msdos.
+ *
+ * Now (linux 1.3), umsdos_readdir can read more than one
+ * entry even if we limit (umsdos_dir_once) to only one:
+ * It skips over hidden file. So we switch to
+ * UMSDOS_SPECIAL_DIRFPOS as soon as we have read successfully
+ * the .. entry.
+ */
+ /* #Specification: umsdos / lookup / inode info
+ * After successfully reading an inode from the MSDOS
+ * filesystem, we use the EMD file to complete it.
+ * We update the following field.
+ *
+ * uid, gid, atime, ctime, mtime, mode.
+ *
+ * We rely on MSDOS for mtime. If the file
+ * was modified during an MSDOS session, at least
+ * mtime will be meaningful. We do this only for regular
+ * file.
+ *
+ * We don't rely on MS-DOS for mtime for directories
+ * because the MS-DOS date on a directory is its
+ * creation time (strange MSDOS behavior) which
+ * corresponds to none of the three Unix time stamps.
+ */
+ /* #Specification: umsdos / conversion mode
+ * The msdos filesystem can do some inline conversion
+ * of the data of a file. It can translate silently
+ * from the MS-DOS text file format to the Unix one
+ * (CRLF -> LF) while reading, and the reverse
+ * while writing. This is activated using the mount
+ * option conv=....
+ *
+ * This is not useful for Linux files in a promoted
+ * directory. It can even be harmful. For this
+ * reason, the binary (no conversion) mode is
+ * always activated.
+ */
+ /* #Specification: umsdos / conversion mode / todo
+ * A flag could be added to file and directories
+ * forcing an automatic conversion mode (as
+ * done with the msdos filesystem).
+ *
+ * This flag could be setup on a directory basis
+ * (instead of file) and all files in it would
+ * logically inherit it. If the conversion mode
+ * is active (conv=) then the i_binary flag would
+ * be left untouched in those directories.
+ *
+ * It was proposed that the sticky bit be used to set
+ * this. A problem with that is that new files would
+ * be written incorrectly. The other problem is that
+ * the sticky bit has a meaning for directories. So
+ * another bit should be used (there is some space
+ * in the EMD file for it) and a special utility
+ * would be used to assign the flag to a directory).
+ * I don't think it is useful to assign this flag
+ * on a single file.
+ */
+ * #Specification: weakness / rename
+ * There is a case where UMSDOS rename has a different behavior
+ * than a normal Unix file system. Renaming an open file across
+ * directory boundary does not work. Renaming an open file within
+ * a directory does work, however.
+ *
+ * The problem may is in Linux VFS driver for msdos.
+ * I believe this is not a bug but a design feature, because
+ * an inode number represents some sort of directory address
+ * in the MSDOS directory structure, so moving the file into
+ * another directory does not preserve the inode number.
+ */
+/* #Specification: rename / new name exist
+ * If the destination name already exists, it will
+ * silently be removed. EXT2 does it this way
+ * and this is the spec of SunOS. So does UMSDOS.
+ *
+ * If the destination is an empty directory it will
+ * also be removed.
+ */
+/* #Specification: rename / new name exist / possible flaw
+ * The code to handle the deletion of the target (file
+ * and directory) use to be in umsdos_rename_f, surrounded
+ * by proper directory locking. This was ensuring that only
+ * one process could achieve a rename (modification) operation
+ * in the source and destination directory. This was also
+ * ensuring the operation was "atomic".
+ *
+ * This has been changed because this was creating a
+ * stack overflow (the stack is only 4 kB) in the kernel. To avoid
+ * the code doing the deletion of the target (if exist) has
+ * been moved to a upper layer. umsdos_rename_f is tried
+ * once and if it fails with EEXIST, the target is removed
+ * and umsdos_rename_f is done again.
+ *
+ * This makes the code cleaner and may solve a
+ * deadlock problem one tester was experiencing.
+ *
+ * The point is to mention that possibly, the semantic of
+ * "rename" may be wrong. Anyone dare to check that :-)
+ * Be aware that IF it is wrong, to produce the problem you
+ * will need two process trying to rename a file to the
+ * same target at the same time. Again, I am not sure it
+ * is a problem at all.
+ */
+
+/* #Specification: hard link / strategy
+ * Hard links are difficult to implement on top of an MS-DOS FAT file
+ * system. Unlike Unix file systems, there are no inodes. A directory
+ * entry holds the functionality of the inode and the entry.
+ *
+ * We will used the same strategy as a normal Unix file system
+ * (with inodes) except we will do it symbolically (using paths).
+ *
+ * Because anything can happen during a DOS session (defragment,
+ * directory sorting, etc.), we can't rely on an MS-DOS pseudo
+ * inode number to record the link. For this reason, the link
+ * will be done using hidden symbolic links. The following
+ * scenario illustrates how it works.
+ *
+ * Given a file /foo/file
+ *
+ * #
+ * ln /foo/file /tmp/file2
+ *
+ * become internally
+ *
+ * mv /foo/file /foo/-LINK1
+ * ln -s /foo/-LINK1 /foo/file
+ * ln -s /foo/-LINK1 /tmp/file2
+ * #
+ *
+ * Using this strategy, we can operate on /foo/file or /foo/file2.
+ * We can remove one and keep the other, like a normal Unix hard link.
+ * We can rename /foo/file or /tmp/file2 independently.
+ *
+ * The entry -LINK1 will be hidden. It will hold a link count.
+ * When all link are erased, the hidden file is erased too.
+ */
+
+/* #Specification: weakness / hard link
+ * The strategy for hard link introduces a side effect that
+ * may or may not be acceptable. Here is the sequence
+ *
+ * #
+ * mkdir subdir1
+ * touch subdir1/file
+ * mkdir subdir2
+ * ln subdir1/file subdir2/file
+ * rm subdir1/file
+ * rmdir subdir1
+ * rmdir: subdir1: Directory not empty
+ * #
+ *
+ * This happen because there is an invisible file (--link) in
+ * subdir1 which is referenced by subdir2/file.
+ *
+ * Any idea ?
+ */
+/* #Specification: weakness / hard link / rename directory
+ * Another weakness of hard link come from the fact that
+ * it is based on hidden symbolic links. Here is an example.
+ *
+ * #
+ * mkdir /subdir1
+ * touch /subdir1/file
+ * mkdir /subdir2
+ * ln /subdir1/file subdir2/file
+ * mv /subdir1 subdir3
+ * ls -l /subdir2/file
+ * #
+ *
+ * Since /subdir2/file is a hidden symbolic link
+ * to /subdir1/..hlinkNNN, accessing it will fail since
+ * /subdir1 does not exist anymore (has been renamed).
+ */
+/* #Specification: hard link / directory
+ * A hard link can't be made on a directory. EPERM is returned
+ * in this case.
+ */
+/* #Specification: hard link / first hard link
+ * The first time a hard link is done on a file, this
+ * file must be renamed and hidden. Then an internal
+ * symbolic link must be done on the hidden file.
+ *
+ * The second link is done after on this hidden file.
+ *
+ * It is expected that the Linux MSDOS file system
+ * keeps the same pseudo inode when a rename operation
+ * is done on a file in the same directory.
+ */
+/* #Specification: function name / convention
+ * A simple convention for function names has been used in
+ * the UMSDOS filesystem. First, all functions use the prefix
+ * umsdos_ to avoid name clashes with other parts of the kernel.
+ *
+ * Standard VFS entry points use the prefix UMSDOS (upper case)
+ * so it's easier to tell them apart.
+ * N.B. (FIXME) PTW, the order and contents of this struct changed.
+ */
+
+/* #Specification: mount / options
+ * Umsdos run on top of msdos. Currently, it supports no
+ * mount option, but happily pass all option received to
+ * the msdos driver. I am not sure if all msdos mount option
+ * make sense with Umsdos. Here are at least those who
+ * are useful.
+ * uid=
+ * gid=
+ *
+ * These options affect the operation of umsdos in directories
+ * which do not have an EMD file. They behave like normal
+ * msdos directory, with all limitation of msdos.
+ */
+
+/* #Specification: pseudo root / mount
+ * When a umsdos fs is mounted, a special handling is done
+ * if it is the root partition. We check for the presence
+ * of the file /linux/etc/init or /linux/etc/rc or
+ * /linux/sbin/init. If one is there, we do a chroot("/linux").
+ *
+ * We check both because (see init/main.c) the kernel
+ * try to exec init at different place and if it fails
+ * it tries /bin/sh /etc/rc. To be consistent with
+ * init/main.c, many more test would have to be done
+ * to locate init. Any complain ?
+ *
+ * The chroot is done manually in init/main.c but the
+ * info (the inode) is located at mount time and store
+ * in a global variable (pseudo_root) which is used at
+ * different place in the umsdos driver. There is no
+ * need to store this variable elsewhere because it
+ * will always be one, not one per mount.
+ *
+ * This feature allows the installation
+ * of a linux system within a DOS system in a subdirectory.
+ *
+ * A user may install its linux stuff in c:\linux
+ * avoiding any clash with existing DOS file and subdirectory.
+ * When linux boots, it hides this fact, showing a normal
+ * root directory with /etc /bin /tmp ...
+ *
+ * The word "linux" is hardcoded in /usr/include/linux/umsdos_fs.h
+ * in the macro UMSDOS_PSDROOT_NAME.
+ */
diff --git a/fs/vfat/Makefile b/fs/vfat/Makefile
new file mode 100644
index 00000000000000..55e0ea9151d604
--- /dev/null
+++ b/fs/vfat/Makefile
@@ -0,0 +1,17 @@
+#
+# Makefile for the linux vfat-filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+O_TARGET := vfat.o
+
+export-objs := vfatfs_syms.o
+
+obj-y := namei.o vfatfs_syms.o
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/vfat/namei.c b/fs/vfat/namei.c
new file mode 100644
index 00000000000000..4437d5db9c2d85
--- /dev/null
+++ b/fs/vfat/namei.c
@@ -0,0 +1,1288 @@
+/*
+ * linux/fs/vfat/namei.c
+ *
+ * Written 1992,1993 by Werner Almesberger
+ *
+ * Windows95/Windows NT compatible extended MSDOS filesystem
+ * by Gordon Chaffee Copyright (C) 1995. Send bug reports for the
+ * VFAT filesystem to <chaffee@cs.berkeley.edu>. Specify
+ * what file operation caused you trouble and if you can duplicate
+ * the problem, send a script that demonstrates it.
+ *
+ * Short name translation 1999, 2001 by Wolfram Pienkoss <wp@bszh.de>
+ *
+ * Support Multibyte character and cleanup by
+ * OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
+ */
+
+#include <linux/module.h>
+
+#include <linux/sched.h>
+#include <linux/msdos_fs.h>
+#include <linux/nls.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+
+#define DEBUG_LEVEL 0
+#if (DEBUG_LEVEL >= 1)
+# define PRINTK1(x) printk x
+#else
+# define PRINTK1(x)
+#endif
+#if (DEBUG_LEVEL >= 2)
+# define PRINTK2(x) printk x
+#else
+# define PRINTK2(x)
+#endif
+#if (DEBUG_LEVEL >= 3)
+# define PRINTK3(x) printk x
+#else
+# define PRINTK3(x)
+#endif
+
+static int vfat_hashi(struct dentry *parent, struct qstr *qstr);
+static int vfat_hash(struct dentry *parent, struct qstr *qstr);
+static int vfat_cmpi(struct dentry *dentry, struct qstr *a, struct qstr *b);
+static int vfat_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b);
+static int vfat_revalidate(struct dentry *dentry, int);
+
+static struct dentry_operations vfat_dentry_ops[4] = {
+ {
+ d_hash: vfat_hashi,
+ d_compare: vfat_cmpi,
+ },
+ {
+ d_revalidate: vfat_revalidate,
+ d_hash: vfat_hashi,
+ d_compare: vfat_cmpi,
+ },
+ {
+ d_hash: vfat_hash,
+ d_compare: vfat_cmp,
+ },
+ {
+ d_revalidate: vfat_revalidate,
+ d_hash: vfat_hash,
+ d_compare: vfat_cmp,
+ }
+};
+
+static int vfat_revalidate(struct dentry *dentry, int flags)
+{
+ PRINTK1(("vfat_revalidate: %s\n", dentry->d_name.name));
+ spin_lock(&dcache_lock);
+ if (dentry->d_time == dentry->d_parent->d_inode->i_version) {
+ spin_unlock(&dcache_lock);
+ return 1;
+ }
+ spin_unlock(&dcache_lock);
+ return 0;
+}
+
+static int simple_getbool(char *s, int *setval)
+{
+ if (s) {
+ if (!strcmp(s,"1") || !strcmp(s,"yes") || !strcmp(s,"true")) {
+ *setval = 1;
+ } else if (!strcmp(s,"0") || !strcmp(s,"no") || !strcmp(s,"false")) {
+ *setval = 0;
+ } else {
+ return 0;
+ }
+ } else {
+ *setval = 1;
+ }
+ return 1;
+}
+
+static int parse_options(char *options, struct fat_mount_options *opts)
+{
+ char *this_char,*value,save,*savep;
+ int ret, val;
+
+ opts->unicode_xlate = opts->posixfs = 0;
+ opts->numtail = 1;
+ opts->utf8 = 0;
+ opts->shortname = VFAT_SFN_DISPLAY_LOWER | VFAT_SFN_CREATE_WIN95;
+ /* for backward compatible */
+ if (opts->nocase) {
+ opts->nocase = 0;
+ opts->shortname = VFAT_SFN_DISPLAY_WIN95
+ | VFAT_SFN_CREATE_WIN95;
+ }
+
+ if (!options) return 1;
+ save = 0;
+ savep = NULL;
+ ret = 1;
+ for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) {
+ if ((value = strchr(this_char,'=')) != NULL) {
+ save = *value;
+ savep = value;
+ *value++ = 0;
+ }
+ if (!strcmp(this_char,"utf8")) {
+ ret = simple_getbool(value, &val);
+ if (ret) opts->utf8 = val;
+ } else if (!strcmp(this_char,"uni_xlate")) {
+ ret = simple_getbool(value, &val);
+ if (ret) opts->unicode_xlate = val;
+ } else if (!strcmp(this_char,"posix")) {
+ ret = simple_getbool(value, &val);
+ if (ret) opts->posixfs = val;
+ } else if (!strcmp(this_char,"nonumtail")) {
+ ret = simple_getbool(value, &val);
+ if (ret) {
+ opts->numtail = !val;
+ }
+ } else if (!strcmp(this_char, "shortname")) {
+ if (!strcmp(value, "lower"))
+ opts->shortname = VFAT_SFN_DISPLAY_LOWER
+ | VFAT_SFN_CREATE_WIN95;
+ else if (!strcmp(value, "win95"))
+ opts->shortname = VFAT_SFN_DISPLAY_WIN95
+ | VFAT_SFN_CREATE_WIN95;
+ else if (!strcmp(value, "winnt"))
+ opts->shortname = VFAT_SFN_DISPLAY_WINNT
+ | VFAT_SFN_CREATE_WINNT;
+ else if (!strcmp(value, "mixed"))
+ opts->shortname = VFAT_SFN_DISPLAY_WINNT
+ | VFAT_SFN_CREATE_WIN95;
+ else
+ ret = 0;
+ }
+ if (this_char != options)
+ *(this_char-1) = ',';
+ if (value) {
+ *savep = save;
+ }
+ if (ret == 0) {
+ return 0;
+ }
+ }
+ if (opts->unicode_xlate) {
+ opts->utf8 = 0;
+ }
+ return 1;
+}
+
+static inline unsigned char
+vfat_tolower(struct nls_table *t, unsigned char c)
+{
+ unsigned char nc = t->charset2lower[c];
+
+ return nc ? nc : c;
+}
+
+static inline unsigned char
+vfat_toupper(struct nls_table *t, unsigned char c)
+{
+ unsigned char nc = t->charset2upper[c];
+
+ return nc ? nc : c;
+}
+
+static int
+vfat_strnicmp(struct nls_table *t, const unsigned char *s1,
+ const unsigned char *s2, int len)
+{
+ while(len--)
+ if (vfat_tolower(t, *s1++) != vfat_tolower(t, *s2++))
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Compute the hash for the vfat name corresponding to the dentry.
+ * Note: if the name is invalid, we leave the hash code unchanged so
+ * that the existing dentry can be used. The vfat fs routines will
+ * return ENOENT or EINVAL as appropriate.
+ */
+static int vfat_hash(struct dentry *dentry, struct qstr *qstr)
+{
+ const char *name;
+ int len;
+
+ len = qstr->len;
+ name = qstr->name;
+ while (len && name[len-1] == '.')
+ len--;
+
+ qstr->hash = full_name_hash(name, len);
+
+ return 0;
+}
+
+/*
+ * Compute the hash for the vfat name corresponding to the dentry.
+ * Note: if the name is invalid, we leave the hash code unchanged so
+ * that the existing dentry can be used. The vfat fs routines will
+ * return ENOENT or EINVAL as appropriate.
+ */
+static int vfat_hashi(struct dentry *dentry, struct qstr *qstr)
+{
+ struct nls_table *t = MSDOS_SB(dentry->d_inode->i_sb)->nls_io;
+ const char *name;
+ int len;
+ unsigned long hash;
+
+ len = qstr->len;
+ name = qstr->name;
+ while (len && name[len-1] == '.')
+ len--;
+
+ hash = init_name_hash();
+ while (len--)
+ hash = partial_name_hash(vfat_tolower(t, *name++), hash);
+ qstr->hash = end_name_hash(hash);
+
+ return 0;
+}
+
+/*
+ * Case insensitive compare of two vfat names.
+ */
+static int vfat_cmpi(struct dentry *dentry, struct qstr *a, struct qstr *b)
+{
+ struct nls_table *t = MSDOS_SB(dentry->d_inode->i_sb)->nls_io;
+ int alen, blen;
+
+ /* A filename cannot end in '.' or we treat it like it has none */
+ alen = a->len;
+ blen = b->len;
+ while (alen && a->name[alen-1] == '.')
+ alen--;
+ while (blen && b->name[blen-1] == '.')
+ blen--;
+ if (alen == blen) {
+ if (vfat_strnicmp(t, a->name, b->name, alen) == 0)
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Case sensitive compare of two vfat names.
+ */
+static int vfat_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b)
+{
+ int alen, blen;
+
+ /* A filename cannot end in '.' or we treat it like it has none */
+ alen = a->len;
+ blen = b->len;
+ while (alen && a->name[alen-1] == '.')
+ alen--;
+ while (blen && b->name[blen-1] == '.')
+ blen--;
+ if (alen == blen) {
+ if (strncmp(a->name, b->name, alen) == 0)
+ return 0;
+ }
+ return 1;
+}
+
+#ifdef DEBUG
+
+static void dump_fat(struct super_block *sb,int start)
+{
+ printk("[");
+ while (start) {
+ printk("%d ",start);
+ start = fat_access(sb,start,-1);
+ if (!start) {
+ printk("ERROR");
+ break;
+ }
+ if (start == -1) break;
+ }
+ printk("]\n");
+}
+
+static void dump_de(struct msdos_dir_entry *de)
+{
+ int i;
+ unsigned char *p = (unsigned char *) de;
+ printk("[");
+
+ for (i = 0; i < 32; i++, p++) {
+ printk("%02x ", *p);
+ }
+ printk("]\n");
+}
+
+#endif
+
+/* MS-DOS "device special files" */
+
+static const char *reserved3_names[] = {
+ "con ", "prn ", "nul ", "aux ", NULL
+};
+
+static const char *reserved4_names[] = {
+ "com1 ", "com2 ", "com3 ", "com4 ", "com5 ",
+ "com6 ", "com7 ", "com8 ", "com9 ",
+ "lpt1 ", "lpt2 ", "lpt3 ", "lpt4 ", "lpt5 ",
+ "lpt6 ", "lpt7 ", "lpt8 ", "lpt9 ",
+ NULL };
+
+
+/* Characters that are undesirable in an MS-DOS file name */
+
+static wchar_t bad_chars[] = {
+ /* `*' `?' `<' `>' `|' `"' `:' `/' */
+ 0x002A, 0x003F, 0x003C, 0x003E, 0x007C, 0x0022, 0x003A, 0x002F,
+ /* `\' */
+ 0x005C, 0,
+};
+#define IS_BADCHAR(uni) (vfat_unistrchr(bad_chars, (uni)) != NULL)
+
+static wchar_t replace_chars[] = {
+ /* `[' `]' `;' `,' `+' `=' */
+ 0x005B, 0x005D, 0x003B, 0x002C, 0x002B, 0x003D, 0,
+};
+#define IS_REPLACECHAR(uni) (vfat_unistrchr(replace_chars, (uni)) != NULL)
+
+static wchar_t skip_chars[] = {
+ /* `.' ` ' */
+ 0x002E, 0x0020, 0,
+};
+#define IS_SKIPCHAR(uni) \
+ ((wchar_t)(uni) == skip_chars[0] || (wchar_t)(uni) == skip_chars[1])
+
+static inline wchar_t *vfat_unistrchr(const wchar_t *s, const wchar_t c)
+{
+ for(; *s != c; ++s)
+ if (*s == 0)
+ return NULL;
+ return (wchar_t *) s;
+}
+
+static inline int vfat_is_used_badchars(const wchar_t *s, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ if (s[i] < 0x0020 || IS_BADCHAR(s[i]))
+ return -EINVAL;
+ return 0;
+}
+
+/* Checks the validity of a long MS-DOS filename */
+/* Returns negative number on error, 0 for a normal
+ * return, and 1 for . or .. */
+
+static int vfat_valid_longname(const char *name, int len, int xlate)
+{
+ const char **reserved, *walk;
+ int baselen;
+
+ if (len && name[len-1] == ' ') return -EINVAL;
+ if (len >= 256) return -EINVAL;
+ if (len < 3) return 0;
+
+ for (walk = name; *walk != 0 && *walk != '.'; walk++);
+ baselen = walk - name;
+
+ if (baselen == 3) {
+ for (reserved = reserved3_names; *reserved; reserved++) {
+ if (!strnicmp(name,*reserved,baselen))
+ return -EINVAL;
+ }
+ } else if (baselen == 4) {
+ for (reserved = reserved4_names; *reserved; reserved++) {
+ if (!strnicmp(name,*reserved,baselen))
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+static int vfat_find_form(struct inode *dir,char *name)
+{
+ struct msdos_dir_entry *de;
+ struct buffer_head *bh = NULL;
+ loff_t i_pos;
+ int res;
+
+ res = fat_scan(dir, name, &bh, &de, &i_pos);
+ fat_brelse(dir->i_sb, bh);
+ if (res<0)
+ return -ENOENT;
+ return 0;
+}
+
+/*
+ * 1) Valid characters for the 8.3 format alias are any combination of
+ * letters, uppercase alphabets, digits, any of the
+ * following special characters:
+ * $ % ' ` - @ { } ~ ! # ( ) & _ ^
+ * In this case Longfilename is not stored in disk.
+ *
+ * WinNT's Extension:
+ * File name and extension name is contain uppercase/lowercase
+ * only. And it is expressed by CASE_LOWER_BASE and CASE_LOWER_EXT.
+ *
+ * 2) File name is 8.3 format, but it contain the uppercase and
+ * lowercase char, muliti bytes char, etc. In this case numtail is not
+ * added, but Longfilename is stored.
+ *
+ * 3) When the one except for the above, or the following special
+ * character are contained:
+ * . [ ] ; , + =
+ * numtail is added, and Longfilename must be stored in disk .
+ */
+struct shortname_info {
+ unsigned char lower:1,
+ upper:1,
+ valid:1;
+};
+#define INIT_SHORTNAME_INFO(x) do { \
+ (x)->lower = 1; \
+ (x)->upper = 1; \
+ (x)->valid = 1; \
+} while (0)
+
+static inline unsigned char
+shortname_info_to_lcase(struct shortname_info *base,
+ struct shortname_info *ext)
+{
+ unsigned char lcase = 0;
+
+ if (base->valid && ext->valid) {
+ if (!base->upper && base->lower && (ext->lower || ext->upper))
+ lcase |= CASE_LOWER_BASE;
+ if (!ext->upper && ext->lower && (base->lower || base->upper))
+ lcase |= CASE_LOWER_EXT;
+ }
+
+ return lcase;
+}
+
+static inline int to_shortname_char(struct nls_table *nls,
+ char *buf, int buf_size, wchar_t *src,
+ struct shortname_info *info)
+{
+ int len;
+
+ if (IS_SKIPCHAR(*src)) {
+ info->valid = 0;
+ return 0;
+ }
+ if (IS_REPLACECHAR(*src)) {
+ info->valid = 0;
+ buf[0] = '_';
+ return 1;
+ }
+
+ len = nls->uni2char(*src, buf, buf_size);
+ if (len <= 0) {
+ info->valid = 0;
+ buf[0] = '_';
+ len = 1;
+ } else if (len == 1) {
+ unsigned char prev = buf[0];
+
+ if (buf[0] >= 0x7F) {
+ info->lower = 0;
+ info->upper = 0;
+ }
+
+ buf[0] = vfat_toupper(nls, buf[0]);
+ if (isalpha(buf[0])) {
+ if (buf[0] == prev)
+ info->lower = 0;
+ else
+ info->upper = 0;
+ }
+ } else {
+ info->lower = 0;
+ info->upper = 0;
+ }
+
+ return len;
+}
+
+/*
+ * Given a valid longname, create a unique shortname. Make sure the
+ * shortname does not exist
+ * Returns negative number on error, 0 for a normal
+ * return, and 1 for valid shortname
+ */
+static int vfat_create_shortname(struct inode *dir, struct nls_table *nls,
+ wchar_t *uname, int ulen,
+ char *name_res, unsigned char *lcase)
+{
+ wchar_t *ip, *ext_start, *end, *name_start;
+ unsigned char base[9], ext[4], buf[8], *p;
+ unsigned char charbuf[NLS_MAX_CHARSET_SIZE];
+ int chl, chi;
+ int sz = 0, extlen, baselen, i, numtail_baselen, numtail2_baselen;
+ int is_shortname;
+ struct shortname_info base_info, ext_info;
+ unsigned short opt_shortname = MSDOS_SB(dir->i_sb)->options.shortname;
+
+ is_shortname = 1;
+ INIT_SHORTNAME_INFO(&base_info);
+ INIT_SHORTNAME_INFO(&ext_info);
+
+ /* Now, we need to create a shortname from the long name */
+ ext_start = end = &uname[ulen];
+ while (--ext_start >= uname) {
+ if (*ext_start == 0x002E) { /* is `.' */
+ if (ext_start == end - 1) {
+ sz = ulen;
+ ext_start = NULL;
+ }
+ break;
+ }
+ }
+
+ if (ext_start == uname - 1) {
+ sz = ulen;
+ ext_start = NULL;
+ } else if (ext_start) {
+ /*
+ * Names which start with a dot could be just
+ * an extension eg. "...test". In this case Win95
+ * uses the extension as the name and sets no extension.
+ */
+ name_start = &uname[0];
+ while (name_start < ext_start) {
+ if (!IS_SKIPCHAR(*name_start))
+ break;
+ name_start++;
+ }
+ if (name_start != ext_start) {
+ sz = ext_start - uname;
+ ext_start++;
+ } else {
+ sz = ulen;
+ ext_start=NULL;
+ }
+ }
+
+ numtail_baselen = 6;
+ numtail2_baselen = 2;
+ for (baselen = i = 0, p = base, ip = uname; i < sz; i++, ip++) {
+ chl = to_shortname_char(nls, charbuf, sizeof(charbuf),
+ ip, &base_info);
+ if (chl == 0)
+ continue;
+
+ if (baselen < 2 && (baselen + chl) > 2)
+ numtail2_baselen = baselen;
+ if (baselen < 6 && (baselen + chl) > 6)
+ numtail_baselen = baselen;
+ for (chi = 0; chi < chl; chi++){
+ *p++ = charbuf[chi];
+ baselen++;
+ if (baselen >= 8)
+ break;
+ }
+ if (baselen >= 8) {
+ if ((chi < chl - 1) || (ip + 1) - uname < sz)
+ is_shortname = 0;
+ break;
+ }
+ }
+ if (baselen == 0) {
+ return -EINVAL;
+ }
+
+ extlen = 0;
+ if (ext_start) {
+ for (p = ext, ip = ext_start; extlen < 3 && ip < end; ip++) {
+ chl = to_shortname_char(nls, charbuf, sizeof(charbuf),
+ ip, &ext_info);
+ if (chl == 0)
+ continue;
+
+ if ((extlen + chl) > 3) {
+ is_shortname = 0;
+ break;
+ }
+ for (chi = 0; chi < chl; chi++) {
+ *p++ = charbuf[chi];
+ extlen++;
+ }
+ if (extlen >= 3) {
+ if (ip + 1 != end)
+ is_shortname = 0;
+ break;
+ }
+ }
+ }
+ ext[extlen] = '\0';
+ base[baselen] = '\0';
+
+ /* Yes, it can happen. ".\xe5" would do it. */
+ if (base[0] == DELETED_FLAG)
+ base[0] = 0x05;
+
+ /* OK, at this point we know that base is not longer than 8 symbols,
+ * ext is not longer than 3, base is nonempty, both don't contain
+ * any bad symbols (lowercase transformed to uppercase).
+ */
+
+ memset(name_res, ' ', MSDOS_NAME);
+ memcpy(name_res, base, baselen);
+ memcpy(name_res + 8, ext, extlen);
+ *lcase = 0;
+ if (is_shortname && base_info.valid && ext_info.valid) {
+ if (vfat_find_form(dir, name_res) == 0)
+ return -EEXIST;
+
+ if (opt_shortname & VFAT_SFN_CREATE_WIN95) {
+ return (base_info.upper && ext_info.upper);
+ } else if (opt_shortname & VFAT_SFN_CREATE_WINNT) {
+ if ((base_info.upper || base_info.lower)
+ && (ext_info.upper || ext_info.lower)) {
+ *lcase = shortname_info_to_lcase(&base_info,
+ &ext_info);
+ return 1;
+ }
+ return 0;
+ } else {
+ BUG();
+ }
+ }
+
+ if (MSDOS_SB(dir->i_sb)->options.numtail == 0)
+ if (vfat_find_form(dir, name_res) < 0)
+ return 0;
+
+ /*
+ * Try to find a unique extension. This used to
+ * iterate through all possibilities sequentially,
+ * but that gave extremely bad performance. Windows
+ * only tries a few cases before using random
+ * values for part of the base.
+ */
+
+ if (baselen>6) {
+ baselen = numtail_baselen;
+ name_res[7] = ' ';
+ }
+ name_res[baselen] = '~';
+ for (i = 1; i < 10; i++) {
+ name_res[baselen+1] = i + '0';
+ if (vfat_find_form(dir, name_res) < 0)
+ return 0;
+ }
+
+ i = jiffies & 0xffff;
+ sz = (jiffies >> 16) & 0x7;
+ if (baselen>2) {
+ baselen = numtail2_baselen;
+ name_res[7] = ' ';
+ }
+ name_res[baselen+4] = '~';
+ name_res[baselen+5] = '1' + sz;
+ while (1) {
+ sprintf(buf, "%04X", i);
+ memcpy(&name_res[baselen], buf, 4);
+ if (vfat_find_form(dir, name_res) < 0)
+ break;
+ i -= 11;
+ }
+ return 0;
+}
+
+/* Translate a string, including coded sequences into Unicode */
+static int
+xlate_to_uni(const char *name, int len, char *outname, int *longlen, int *outlen,
+ int escape, int utf8, struct nls_table *nls)
+{
+ const unsigned char *ip;
+ unsigned char nc;
+ char *op;
+ unsigned int ec;
+ int i, k, fill;
+ int charlen;
+
+ if (utf8) {
+ *outlen = utf8_mbstowcs((__u16 *) outname, name, PAGE_SIZE);
+ if (name[len-1] == '.')
+ *outlen-=2;
+ op = &outname[*outlen * sizeof(__u16)];
+ } else {
+ if (name[len-1] == '.')
+ len--;
+ if (nls) {
+ for (i = 0, ip = name, op = outname, *outlen = 0;
+ i < len && *outlen <= 260; *outlen += 1)
+ {
+ if (escape && (*ip == ':')) {
+ if (i > len - 5)
+ return -EINVAL;
+ ec = 0;
+ for (k = 1; k < 5; k++) {
+ nc = ip[k];
+ ec <<= 4;
+ if (nc >= '0' && nc <= '9') {
+ ec |= nc - '0';
+ continue;
+ }
+ if (nc >= 'a' && nc <= 'f') {
+ ec |= nc - ('a' - 10);
+ continue;
+ }
+ if (nc >= 'A' && nc <= 'F') {
+ ec |= nc - ('A' - 10);
+ continue;
+ }
+ return -EINVAL;
+ }
+ *op++ = ec & 0xFF;
+ *op++ = ec >> 8;
+ ip += 5;
+ i += 5;
+ } else {
+ if ((charlen = nls->char2uni(ip, len-i, (wchar_t *)op)) < 0)
+ return -EINVAL;
+ ip += charlen;
+ i += charlen;
+ op += 2;
+ }
+ }
+ } else {
+ for (i = 0, ip = name, op = outname, *outlen = 0;
+ i < len && *outlen <= 260; i++, *outlen += 1)
+ {
+ *op++ = *ip++;
+ *op++ = 0;
+ }
+ }
+ }
+ if (*outlen > 260)
+ return -ENAMETOOLONG;
+
+ *longlen = *outlen;
+ if (*outlen % 13) {
+ *op++ = 0;
+ *op++ = 0;
+ *outlen += 1;
+ if (*outlen % 13) {
+ fill = 13 - (*outlen % 13);
+ for (i = 0; i < fill; i++) {
+ *op++ = 0xff;
+ *op++ = 0xff;
+ }
+ *outlen += fill;
+ }
+ }
+
+ return 0;
+}
+
+static int
+vfat_fill_slots(struct inode *dir, struct msdos_dir_slot *ds, const char *name,
+ int len, int *slots, int is_dir, int uni_xlate)
+{
+ struct nls_table *nls_io, *nls_disk;
+ wchar_t *uname;
+ struct msdos_dir_slot *ps;
+ struct msdos_dir_entry *de;
+ unsigned long page;
+ unsigned char cksum, lcase;
+ char *uniname, msdos_name[MSDOS_NAME];
+ int res, utf8, slot, ulen, unilen, i;
+ loff_t offset;
+
+ *slots = 0;
+ utf8 = MSDOS_SB(dir->i_sb)->options.utf8;
+ nls_io = MSDOS_SB(dir->i_sb)->nls_io;
+ nls_disk = MSDOS_SB(dir->i_sb)->nls_disk;
+
+ if (name[len-1] == '.')
+ len--;
+ if(!(page = __get_free_page(GFP_KERNEL)))
+ return -ENOMEM;
+
+ uniname = (char *) page;
+ res = xlate_to_uni(name, len, uniname, &ulen, &unilen, uni_xlate,
+ utf8, nls_io);
+ if (res < 0)
+ goto out_free;
+
+ uname = (wchar_t *) page;
+ res = vfat_is_used_badchars(uname, ulen);
+ if (res < 0)
+ goto out_free;
+
+ res = vfat_create_shortname(dir, nls_disk, uname, ulen,
+ msdos_name, &lcase);
+ if (res < 0)
+ goto out_free;
+ else if (res == 1) {
+ de = (struct msdos_dir_entry *)ds;
+ res = 0;
+ goto shortname;
+ }
+
+ /* build the entry of long file name */
+ *slots = unilen / 13;
+ for (cksum = i = 0; i < 11; i++) {
+ cksum = (((cksum&1)<<7)|((cksum&0xfe)>>1)) + msdos_name[i];
+ }
+ PRINTK3(("vfat_fill_slots 3: slots=%d\n",*slots));
+
+ for (ps = ds, slot = *slots; slot > 0; slot--, ps++) {
+ ps->id = slot;
+ ps->attr = ATTR_EXT;
+ ps->reserved = 0;
+ ps->alias_checksum = cksum;
+ ps->start = 0;
+ offset = (slot - 1) * 13;
+ fatwchar_to16(ps->name0_4, uname + offset, 5);
+ fatwchar_to16(ps->name5_10, uname + offset + 5, 6);
+ fatwchar_to16(ps->name11_12, uname + offset + 11, 2);
+ }
+ ds[0].id |= 0x40;
+ de = (struct msdos_dir_entry *) ps;
+
+shortname:
+ PRINTK3(("vfat_fill_slots 9\n"));
+ /* build the entry of 8.3 alias name */
+ (*slots)++;
+ strncpy(de->name, msdos_name, MSDOS_NAME);
+ de->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
+ de->lcase = lcase;
+ de->adate = de->cdate = de->date = 0;
+ de->ctime_ms = de->ctime = de->time = 0;
+ de->start = 0;
+ de->starthi = 0;
+ de->size = 0;
+
+out_free:
+ free_page(page);
+ return res;
+}
+
+/* We can't get "." or ".." here - VFS takes care of those cases */
+
+static int vfat_build_slots(struct inode *dir, const char *name, int len,
+ struct msdos_dir_slot *ds, int *slots, int is_dir)
+{
+ int res, xlate;
+
+ xlate = MSDOS_SB(dir->i_sb)->options.unicode_xlate;
+ res = vfat_valid_longname(name, len, xlate);
+ if (res < 0)
+ return res;
+
+ return vfat_fill_slots(dir, ds, name, len, slots, is_dir, xlate);
+}
+
+static int vfat_add_entry(struct inode *dir,struct qstr* qname,
+ int is_dir, struct vfat_slot_info *sinfo_out,
+ struct buffer_head **bh, struct msdos_dir_entry **de)
+{
+ struct super_block *sb = dir->i_sb;
+ struct msdos_dir_slot *dir_slots;
+ loff_t offset;
+ int slots, slot;
+ int res, len;
+ struct msdos_dir_entry *dummy_de;
+ struct buffer_head *dummy_bh;
+ loff_t dummy_i_pos;
+ loff_t dummy;
+
+ dir_slots = (struct msdos_dir_slot *)
+ kmalloc(sizeof(struct msdos_dir_slot) * MSDOS_SLOTS, GFP_KERNEL);
+ if (dir_slots == NULL)
+ return -ENOMEM;
+
+ len = qname->len;
+ while (len && qname->name[len-1] == '.')
+ len--;
+ res = fat_search_long(dir, qname->name, len,
+ (MSDOS_SB(sb)->options.name_check != 's')
+ || !MSDOS_SB(sb)->options.posixfs,
+ &dummy, &dummy);
+ if (res > 0) /* found */
+ res = -EEXIST;
+ if (res)
+ goto cleanup;
+
+ res = vfat_build_slots(dir, qname->name, len,
+ dir_slots, &slots, is_dir);
+ if (res < 0)
+ goto cleanup;
+
+ /* build the empty directory entry of number of slots */
+ offset = fat_add_entries(dir, slots, &dummy_bh, &dummy_de, &dummy_i_pos);
+ if (offset < 0) {
+ res = offset;
+ goto cleanup;
+ }
+ fat_brelse(sb, dummy_bh);
+
+ /* Now create the new entry */
+ *bh = NULL;
+ for (slot = 0; slot < slots; slot++) {
+ if (fat_get_entry(dir, &offset, bh, de, &sinfo_out->i_pos) < 0) {
+ res = -EIO;
+ goto cleanup;
+ }
+ memcpy(*de, dir_slots + slot, sizeof(struct msdos_dir_slot));
+ fat_mark_buffer_dirty(sb, *bh);
+ }
+
+ res = 0;
+ /* update timestamp */
+ dir->i_ctime = dir->i_mtime = dir->i_atime = CURRENT_TIME;
+ mark_inode_dirty(dir);
+
+ fat_date_unix2dos(dir->i_mtime, &(*de)->time, &(*de)->date);
+ (*de)->ctime = (*de)->time;
+ (*de)->adate = (*de)->cdate = (*de)->date;
+
+ fat_mark_buffer_dirty(sb, *bh);
+
+ /* slots can't be less than 1 */
+ sinfo_out->long_slots = slots - 1;
+ sinfo_out->longname_offset =
+ offset - sizeof(struct msdos_dir_slot) * slots;
+
+cleanup:
+ kfree(dir_slots);
+ return res;
+}
+
+static int vfat_find(struct inode *dir,struct qstr* qname,
+ struct vfat_slot_info *sinfo, struct buffer_head **last_bh,
+ struct msdos_dir_entry **last_de)
+{
+ struct super_block *sb = dir->i_sb;
+ loff_t offset;
+ int res,len;
+
+ len = qname->len;
+ while (len && qname->name[len-1] == '.')
+ len--;
+ res = fat_search_long(dir, qname->name, len,
+ (MSDOS_SB(sb)->options.name_check != 's'),
+ &offset,&sinfo->longname_offset);
+ if (res>0) {
+ sinfo->long_slots = res-1;
+ if (fat_get_entry(dir,&offset,last_bh,last_de,&sinfo->i_pos)>=0)
+ return 0;
+ res = -EIO;
+ }
+ return res ? res : -ENOENT;
+}
+
+struct dentry *vfat_lookup(struct inode *dir,struct dentry *dentry)
+{
+ int res;
+ struct vfat_slot_info sinfo;
+ struct inode *inode;
+ struct dentry *alias;
+ struct buffer_head *bh = NULL;
+ struct msdos_dir_entry *de;
+ int table;
+
+ PRINTK2(("vfat_lookup: name=%s, len=%d\n",
+ dentry->d_name.name, dentry->d_name.len));
+
+ table = (MSDOS_SB(dir->i_sb)->options.name_check == 's') ? 2 : 0;
+ dentry->d_op = &vfat_dentry_ops[table];
+
+ inode = NULL;
+ res = vfat_find(dir,&dentry->d_name,&sinfo,&bh,&de);
+ if (res < 0) {
+ table++;
+ goto error;
+ }
+ inode = fat_build_inode(dir->i_sb, de, sinfo.i_pos, &res);
+ fat_brelse(dir->i_sb, bh);
+ if (res)
+ return ERR_PTR(res);
+ alias = d_find_alias(inode);
+ if (alias) {
+ if (d_invalidate(alias)==0)
+ dput(alias);
+ else {
+ iput(inode);
+ return alias;
+ }
+
+ }
+error:
+ dentry->d_op = &vfat_dentry_ops[table];
+ dentry->d_time = dentry->d_parent->d_inode->i_version;
+ d_add(dentry,inode);
+ return NULL;
+}
+
+int vfat_create(struct inode *dir,struct dentry* dentry,int mode)
+{
+ struct super_block *sb = dir->i_sb;
+ struct inode *inode = NULL;
+ struct buffer_head *bh = NULL;
+ struct msdos_dir_entry *de;
+ struct vfat_slot_info sinfo;
+ int res;
+
+ res = vfat_add_entry(dir, &dentry->d_name, 0, &sinfo, &bh, &de);
+ if (res < 0)
+ return res;
+ inode = fat_build_inode(sb, de, sinfo.i_pos, &res);
+ fat_brelse(sb, bh);
+ if (!inode)
+ return res;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+ inode->i_version = ++event;
+ dir->i_version = event;
+ dentry->d_time = dentry->d_parent->d_inode->i_version;
+ d_instantiate(dentry,inode);
+ return 0;
+}
+
+static void vfat_remove_entry(struct inode *dir,struct vfat_slot_info *sinfo,
+ struct buffer_head *bh, struct msdos_dir_entry *de)
+{
+ struct super_block *sb = dir->i_sb;
+ loff_t offset, i_pos;
+ int i;
+
+ /* remove the shortname */
+ dir->i_mtime = CURRENT_TIME;
+ dir->i_atime = CURRENT_TIME;
+ dir->i_version = ++event;
+ mark_inode_dirty(dir);
+ de->name[0] = DELETED_FLAG;
+ fat_mark_buffer_dirty(sb, bh);
+ /* remove the longname */
+ offset = sinfo->longname_offset; de = NULL;
+ for (i = sinfo->long_slots; i > 0; --i) {
+ if (fat_get_entry(dir, &offset, &bh, &de, &i_pos) < 0)
+ continue;
+ de->name[0] = DELETED_FLAG;
+ de->attr = 0;
+ fat_mark_buffer_dirty(sb, bh);
+ }
+ if (bh) fat_brelse(sb, bh);
+}
+
+int vfat_rmdir(struct inode *dir,struct dentry* dentry)
+{
+ int res;
+ struct vfat_slot_info sinfo;
+ struct buffer_head *bh = NULL;
+ struct msdos_dir_entry *de;
+
+ res = fat_dir_empty(dentry->d_inode);
+ if (res)
+ return res;
+
+ res = vfat_find(dir,&dentry->d_name,&sinfo, &bh, &de);
+ if (res<0)
+ return res;
+ dentry->d_inode->i_nlink = 0;
+ dentry->d_inode->i_mtime = CURRENT_TIME;
+ dentry->d_inode->i_atime = CURRENT_TIME;
+ fat_detach(dentry->d_inode);
+ mark_inode_dirty(dentry->d_inode);
+ /* releases bh */
+ vfat_remove_entry(dir,&sinfo,bh,de);
+ dir->i_nlink--;
+ return 0;
+}
+
+int vfat_unlink(struct inode *dir, struct dentry* dentry)
+{
+ int res;
+ struct vfat_slot_info sinfo;
+ struct buffer_head *bh = NULL;
+ struct msdos_dir_entry *de;
+
+ PRINTK1(("vfat_unlink: %s\n", dentry->d_name.name));
+ res = vfat_find(dir,&dentry->d_name,&sinfo,&bh,&de);
+ if (res < 0)
+ return res;
+ dentry->d_inode->i_nlink = 0;
+ dentry->d_inode->i_mtime = CURRENT_TIME;
+ dentry->d_inode->i_atime = CURRENT_TIME;
+ fat_detach(dentry->d_inode);
+ mark_inode_dirty(dentry->d_inode);
+ /* releases bh */
+ vfat_remove_entry(dir,&sinfo,bh,de);
+
+ return res;
+}
+
+
+int vfat_mkdir(struct inode *dir,struct dentry* dentry,int mode)
+{
+ struct super_block *sb = dir->i_sb;
+ struct inode *inode = NULL;
+ struct vfat_slot_info sinfo;
+ struct buffer_head *bh = NULL;
+ struct msdos_dir_entry *de;
+ int res;
+
+ res = vfat_add_entry(dir, &dentry->d_name, 1, &sinfo, &bh, &de);
+ if (res < 0)
+ return res;
+ inode = fat_build_inode(sb, de, sinfo.i_pos, &res);
+ if (!inode)
+ goto out;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+ inode->i_version = ++event;
+ dir->i_version = event;
+ dir->i_nlink++;
+ inode->i_nlink = 2; /* no need to mark them dirty */
+ res = fat_new_dir(inode, dir, 1);
+ if (res < 0)
+ goto mkdir_failed;
+ dentry->d_time = dentry->d_parent->d_inode->i_version;
+ d_instantiate(dentry,inode);
+out:
+ fat_brelse(sb, bh);
+ return res;
+
+mkdir_failed:
+ inode->i_nlink = 0;
+ inode->i_mtime = CURRENT_TIME;
+ inode->i_atime = CURRENT_TIME;
+ fat_detach(inode);
+ mark_inode_dirty(inode);
+ /* releases bh */
+ vfat_remove_entry(dir,&sinfo,bh,de);
+ iput(inode);
+ dir->i_nlink--;
+ return res;
+}
+
+int vfat_rename(struct inode *old_dir,struct dentry *old_dentry,
+ struct inode *new_dir,struct dentry *new_dentry)
+{
+ struct super_block *sb = old_dir->i_sb;
+ struct buffer_head *old_bh,*new_bh,*dotdot_bh;
+ struct msdos_dir_entry *old_de,*new_de,*dotdot_de;
+ loff_t dotdot_i_pos;
+ struct inode *old_inode, *new_inode;
+ int res, is_dir;
+ struct vfat_slot_info old_sinfo,sinfo;
+
+ old_bh = new_bh = dotdot_bh = NULL;
+ old_inode = old_dentry->d_inode;
+ new_inode = new_dentry->d_inode;
+ res = vfat_find(old_dir,&old_dentry->d_name,&old_sinfo,&old_bh,&old_de);
+ PRINTK3(("vfat_rename 2\n"));
+ if (res < 0) goto rename_done;
+
+ is_dir = S_ISDIR(old_inode->i_mode);
+
+ if (is_dir && (res = fat_scan(old_inode,MSDOS_DOTDOT,&dotdot_bh,
+ &dotdot_de,&dotdot_i_pos)) < 0)
+ goto rename_done;
+
+ if (new_dentry->d_inode) {
+ res = vfat_find(new_dir,&new_dentry->d_name,&sinfo,&new_bh,
+ &new_de);
+ if (res < 0 || MSDOS_I(new_inode)->i_pos != sinfo.i_pos) {
+ /* WTF??? Cry and fail. */
+ printk(KERN_WARNING "vfat_rename: fs corrupted\n");
+ goto rename_done;
+ }
+
+ if (is_dir) {
+ res = fat_dir_empty(new_inode);
+ if (res)
+ goto rename_done;
+ }
+ fat_detach(new_inode);
+ } else {
+ res = vfat_add_entry(new_dir,&new_dentry->d_name,is_dir,&sinfo,
+ &new_bh,&new_de);
+ if (res < 0) goto rename_done;
+ }
+
+ new_dir->i_version = ++event;
+
+ /* releases old_bh */
+ vfat_remove_entry(old_dir,&old_sinfo,old_bh,old_de);
+ old_bh=NULL;
+ fat_detach(old_inode);
+ fat_attach(old_inode, sinfo.i_pos);
+ mark_inode_dirty(old_inode);
+
+ old_dir->i_version = ++event;
+ old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
+ mark_inode_dirty(old_dir);
+ if (new_inode) {
+ new_inode->i_nlink--;
+ new_inode->i_ctime=CURRENT_TIME;
+ }
+
+ if (is_dir) {
+ int start = MSDOS_I(new_dir)->i_logstart;
+ dotdot_de->start = CT_LE_W(start);
+ dotdot_de->starthi = CT_LE_W(start>>16);
+ fat_mark_buffer_dirty(sb, dotdot_bh);
+ old_dir->i_nlink--;
+ if (new_inode) {
+ new_inode->i_nlink--;
+ } else {
+ new_dir->i_nlink++;
+ mark_inode_dirty(new_dir);
+ }
+ }
+
+rename_done:
+ fat_brelse(sb, dotdot_bh);
+ fat_brelse(sb, old_bh);
+ fat_brelse(sb, new_bh);
+ return res;
+
+}
+
+
+/* Public inode operations for the VFAT fs */
+struct inode_operations vfat_dir_inode_operations = {
+ create: vfat_create,
+ lookup: vfat_lookup,
+ unlink: vfat_unlink,
+ mkdir: vfat_mkdir,
+ rmdir: vfat_rmdir,
+ rename: vfat_rename,
+ setattr: fat_notify_change,
+};
+
+struct super_block *vfat_read_super(struct super_block *sb,void *data,
+ int silent)
+{
+ struct super_block *res;
+
+ MSDOS_SB(sb)->options.isvfat = 1;
+
+ res = fat_read_super(sb, data, silent, &vfat_dir_inode_operations);
+ if (res == NULL)
+ return NULL;
+
+ if (parse_options((char *) data, &(MSDOS_SB(sb)->options))) {
+ MSDOS_SB(sb)->options.dotsOK = 0;
+ if (MSDOS_SB(sb)->options.posixfs) {
+ MSDOS_SB(sb)->options.name_check = 's';
+ }
+ if (MSDOS_SB(sb)->options.name_check != 's') {
+ sb->s_root->d_op = &vfat_dentry_ops[0];
+ } else {
+ sb->s_root->d_op = &vfat_dentry_ops[2];
+ }
+ }
+
+ return res;
+}
diff --git a/fs/vfat/vfatfs_syms.c b/fs/vfat/vfatfs_syms.c
new file mode 100644
index 00000000000000..2feb1222db3465
--- /dev/null
+++ b/fs/vfat/vfatfs_syms.c
@@ -0,0 +1,36 @@
+/*
+ * linux/fs/msdos/vfatfs_syms.c
+ *
+ * Exported kernel symbols for the VFAT filesystem.
+ * These symbols are used by dmsdos.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <linux/mm.h>
+#include <linux/msdos_fs.h>
+
+DECLARE_FSTYPE_DEV(vfat_fs_type, "vfat", vfat_read_super);
+
+EXPORT_SYMBOL(vfat_create);
+EXPORT_SYMBOL(vfat_unlink);
+EXPORT_SYMBOL(vfat_mkdir);
+EXPORT_SYMBOL(vfat_rmdir);
+EXPORT_SYMBOL(vfat_rename);
+EXPORT_SYMBOL(vfat_read_super);
+EXPORT_SYMBOL(vfat_lookup);
+
+static int __init init_vfat_fs(void)
+{
+ return register_filesystem(&vfat_fs_type);
+}
+
+static void __exit exit_vfat_fs(void)
+{
+ unregister_filesystem(&vfat_fs_type);
+}
+
+module_init(init_vfat_fs)
+module_exit(exit_vfat_fs)
+MODULE_LICENSE("GPL");
diff --git a/fs/xattr.c b/fs/xattr.c
new file mode 100644
index 00000000000000..b6a52027873603
--- /dev/null
+++ b/fs/xattr.c
@@ -0,0 +1,355 @@
+/*
+ File: fs/xattr.c
+
+ Extended attribute handling.
+
+ Copyright (C) 2001 by Andreas Gruenbacher <a.gruenbacher@computer.org>
+ Copyright (C) 2001 SGI - Silicon Graphics, Inc <linux-xfs@oss.sgi.com>
+ */
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/smp_lock.h>
+#include <linux/file.h>
+#include <linux/xattr.h>
+#include <asm/uaccess.h>
+
+/*
+ * Extended attribute memory allocation wrappers, originally
+ * based on the Intermezzo PRESTO_ALLOC/PRESTO_FREE macros.
+ * The vmalloc use here is very uncommon - extended attributes
+ * are supposed to be small chunks of metadata, and it is quite
+ * unusual to have very many extended attributes, so lists tend
+ * to be quite short as well. The 64K upper limit is derived
+ * from the extended attribute size limit used by XFS.
+ * Intentionally allow zero @size for value/list size requests.
+ */
+static void *
+xattr_alloc(size_t size, size_t limit)
+{
+ void *ptr;
+
+ if (size > limit)
+ return ERR_PTR(-E2BIG);
+
+ if (!size) /* size request, no buffer is needed */
+ return NULL;
+ else if (size <= PAGE_SIZE)
+ ptr = kmalloc((unsigned long) size, GFP_KERNEL);
+ else
+ ptr = vmalloc((unsigned long) size);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+ return ptr;
+}
+
+static void
+xattr_free(void *ptr, size_t size)
+{
+ if (!size) /* size request, no buffer was needed */
+ return;
+ else if (size <= PAGE_SIZE)
+ kfree(ptr);
+ else
+ vfree(ptr);
+}
+
+/*
+ * Extended attribute SET operations
+ */
+static long
+setxattr(struct dentry *d, char *name, void *value, size_t size, int flags)
+{
+ int error;
+ void *kvalue;
+ char kname[XATTR_NAME_MAX + 1];
+
+ if (flags & ~(XATTR_CREATE|XATTR_REPLACE))
+ return -EINVAL;
+
+ error = strncpy_from_user(kname, name, sizeof(kname));
+ if (error == 0 || error == sizeof(kname))
+ error = -ERANGE;
+ if (error < 0)
+ return error;
+
+ kvalue = xattr_alloc(size, XATTR_SIZE_MAX);
+ if (IS_ERR(kvalue))
+ return PTR_ERR(kvalue);
+
+ if (size > 0 && copy_from_user(kvalue, value, size)) {
+ xattr_free(kvalue, size);
+ return -EFAULT;
+ }
+
+ error = -EOPNOTSUPP;
+ if (d->d_inode->i_op && d->d_inode->i_op->setxattr) {
+ down(&d->d_inode->i_sem);
+ lock_kernel();
+ error = d->d_inode->i_op->setxattr(d, kname, kvalue, size, flags);
+ unlock_kernel();
+ up(&d->d_inode->i_sem);
+ }
+
+ xattr_free(kvalue, size);
+ return error;
+}
+
+asmlinkage long
+sys_setxattr(char *path, char *name, void *value, size_t size, int flags)
+{
+ struct nameidata nd;
+ int error;
+
+ error = user_path_walk(path, &nd);
+ if (error)
+ return error;
+ error = setxattr(nd.dentry, name, value, size, flags);
+ path_release(&nd);
+ return error;
+}
+
+asmlinkage long
+sys_lsetxattr(char *path, char *name, void *value, size_t size, int flags)
+{
+ struct nameidata nd;
+ int error;
+
+ error = user_path_walk_link(path, &nd);
+ if (error)
+ return error;
+ error = setxattr(nd.dentry, name, value, size, flags);
+ path_release(&nd);
+ return error;
+}
+
+asmlinkage long
+sys_fsetxattr(int fd, char *name, void *value, size_t size, int flags)
+{
+ struct file *f;
+ int error = -EBADF;
+
+ f = fget(fd);
+ if (!f)
+ return error;
+ error = setxattr(f->f_dentry, name, value, size, flags);
+ fput(f);
+ return error;
+}
+
+/*
+ * Extended attribute GET operations
+ */
+static ssize_t
+getxattr(struct dentry *d, char *name, void *value, size_t size)
+{
+ ssize_t error;
+ void *kvalue;
+ char kname[XATTR_NAME_MAX + 1];
+
+ error = strncpy_from_user(kname, name, sizeof(kname));
+ if (error == 0 || error == sizeof(kname))
+ error = -ERANGE;
+ if (error < 0)
+ return error;
+
+ kvalue = xattr_alloc(size, XATTR_SIZE_MAX);
+ if (IS_ERR(kvalue))
+ return PTR_ERR(kvalue);
+
+ error = -EOPNOTSUPP;
+ if (d->d_inode->i_op && d->d_inode->i_op->getxattr) {
+ down(&d->d_inode->i_sem);
+ lock_kernel();
+ error = d->d_inode->i_op->getxattr(d, kname, kvalue, size);
+ unlock_kernel();
+ up(&d->d_inode->i_sem);
+ }
+
+ if (kvalue && error > 0)
+ if (copy_to_user(value, kvalue, error))
+ error = -EFAULT;
+ xattr_free(kvalue, size);
+ return error;
+}
+
+asmlinkage ssize_t
+sys_getxattr(char *path, char *name, void *value, size_t size)
+{
+ struct nameidata nd;
+ ssize_t error;
+
+ error = user_path_walk(path, &nd);
+ if (error)
+ return error;
+ error = getxattr(nd.dentry, name, value, size);
+ path_release(&nd);
+ return error;
+}
+
+asmlinkage ssize_t
+sys_lgetxattr(char *path, char *name, void *value, size_t size)
+{
+ struct nameidata nd;
+ ssize_t error;
+
+ error = user_path_walk_link(path, &nd);
+ if (error)
+ return error;
+ error = getxattr(nd.dentry, name, value, size);
+ path_release(&nd);
+ return error;
+}
+
+asmlinkage ssize_t
+sys_fgetxattr(int fd, char *name, void *value, size_t size)
+{
+ struct file *f;
+ ssize_t error = -EBADF;
+
+ f = fget(fd);
+ if (!f)
+ return error;
+ error = getxattr(f->f_dentry, name, value, size);
+ fput(f);
+ return error;
+}
+
+/*
+ * Extended attribute LIST operations
+ */
+static ssize_t
+listxattr(struct dentry *d, char *list, size_t size)
+{
+ ssize_t error;
+ char *klist;
+
+ klist = (char *)xattr_alloc(size, XATTR_LIST_MAX);
+ if (IS_ERR(klist))
+ return PTR_ERR(klist);
+
+ error = -EOPNOTSUPP;
+ if (d->d_inode->i_op && d->d_inode->i_op->listxattr) {
+ down(&d->d_inode->i_sem);
+ lock_kernel();
+ error = d->d_inode->i_op->listxattr(d, klist, size);
+ unlock_kernel();
+ up(&d->d_inode->i_sem);
+ }
+
+ if (klist && error > 0)
+ if (copy_to_user(list, klist, error))
+ error = -EFAULT;
+ xattr_free(klist, size);
+ return error;
+}
+
+asmlinkage ssize_t
+sys_listxattr(char *path, char *list, size_t size)
+{
+ struct nameidata nd;
+ ssize_t error;
+
+ error = user_path_walk(path, &nd);
+ if (error)
+ return error;
+ error = listxattr(nd.dentry, list, size);
+ path_release(&nd);
+ return error;
+}
+
+asmlinkage ssize_t
+sys_llistxattr(char *path, char *list, size_t size)
+{
+ struct nameidata nd;
+ ssize_t error;
+
+ error = user_path_walk_link(path, &nd);
+ if (error)
+ return error;
+ error = listxattr(nd.dentry, list, size);
+ path_release(&nd);
+ return error;
+}
+
+asmlinkage ssize_t
+sys_flistxattr(int fd, char *list, size_t size)
+{
+ struct file *f;
+ ssize_t error = -EBADF;
+
+ f = fget(fd);
+ if (!f)
+ return error;
+ error = listxattr(f->f_dentry, list, size);
+ fput(f);
+ return error;
+}
+
+/*
+ * Extended attribute REMOVE operations
+ */
+static long
+removexattr(struct dentry *d, char *name)
+{
+ int error;
+ char kname[XATTR_NAME_MAX + 1];
+
+ error = strncpy_from_user(kname, name, sizeof(kname));
+ if (error == 0 || error == sizeof(kname))
+ error = -ERANGE;
+ if (error < 0)
+ return error;
+
+ error = -EOPNOTSUPP;
+ if (d->d_inode->i_op && d->d_inode->i_op->removexattr) {
+ down(&d->d_inode->i_sem);
+ lock_kernel();
+ error = d->d_inode->i_op->removexattr(d, kname);
+ unlock_kernel();
+ up(&d->d_inode->i_sem);
+ }
+ return error;
+}
+
+asmlinkage long
+sys_removexattr(char *path, char *name)
+{
+ struct nameidata nd;
+ int error;
+
+ error = user_path_walk(path, &nd);
+ if (error)
+ return error;
+ error = removexattr(nd.dentry, name);
+ path_release(&nd);
+ return error;
+}
+
+asmlinkage long
+sys_lremovexattr(char *path, char *name)
+{
+ struct nameidata nd;
+ int error;
+
+ error = user_path_walk_link(path, &nd);
+ if (error)
+ return error;
+ error = removexattr(nd.dentry, name);
+ path_release(&nd);
+ return error;
+}
+
+asmlinkage long
+sys_fremovexattr(int fd, char *name)
+{
+ struct file *f;
+ int error = -EBADF;
+
+ f = fget(fd);
+ if (!f)
+ return error;
+ error = removexattr(f->f_dentry, name);
+ fput(f);
+ return error;
+}
diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
new file mode 100644
index 00000000000000..4ab74b92e265f4
--- /dev/null
+++ b/fs/xfs/Makefile
@@ -0,0 +1,134 @@
+#
+# Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write the Free Software Foundation, Inc., 59
+# Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+# Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+# Mountain View, CA 94043, or:
+#
+# http://www.sgi.com
+#
+# For further information regarding this notice, see:
+#
+# http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+#
+
+EXTRA_CFLAGS += -I. -I linux-2.4 -funsigned-char
+
+ifeq ($(CONFIG_XFS_DEBUG),y)
+ EXTRA_CFLAGS += -g -DSTATIC="" -DDEBUG
+ EXTRA_CFLAGS += -DPAGEBUF_LOCK_TRACKING
+endif
+ifeq ($(CONFIG_XFS_TRACE),y)
+ EXTRA_CFLAGS += -DXFS_ALLOC_TRACE
+ EXTRA_CFLAGS += -DXFS_ATTR_TRACE
+ EXTRA_CFLAGS += -DXFS_BLI_TRACE
+ EXTRA_CFLAGS += -DXFS_BMAP_TRACE
+ EXTRA_CFLAGS += -DXFS_BMBT_TRACE
+ EXTRA_CFLAGS += -DXFS_DIR_TRACE
+ EXTRA_CFLAGS += -DXFS_DQUOT_TRACE
+ EXTRA_CFLAGS += -DXFS_ILOCK_TRACE
+ EXTRA_CFLAGS += -DXFS_LOG_TRACE
+ EXTRA_CFLAGS += -DXFS_RW_TRACE
+ EXTRA_CFLAGS += -DXFS_DIR2_TRACE
+ EXTRA_CFLAGS += -DPAGEBUF_TRACE
+ # EXTRA_CFLAGS += -DXFS_VNODE_TRACE
+endif
+
+O_TARGET := xfs.o
+obj-m := $(O_TARGET)
+
+obj-$(CONFIG_XFS_RT) += xfs_rtalloc.o
+obj-$(CONFIG_XFS_POSIX_ACL) += xfs_acl.o
+
+obj-$(CONFIG_XFS_TRACE) += xfs_dir2_trace.o
+
+obj-y += xfs_alloc.o \
+ xfs_alloc_btree.o \
+ xfs_attr.o \
+ xfs_attr_leaf.o \
+ xfs_behavior.o \
+ xfs_bit.o \
+ xfs_bmap.o \
+ xfs_bmap_btree.o \
+ xfs_btree.o \
+ xfs_buf_item.o \
+ xfs_da_btree.o \
+ xfs_dir.o \
+ xfs_dir2.o \
+ xfs_dir2_block.o \
+ xfs_dir2_data.o \
+ xfs_dir2_leaf.o \
+ xfs_dir2_node.o \
+ xfs_dir2_sf.o \
+ xfs_dir_leaf.o \
+ xfs_error.o \
+ xfs_extfree_item.o \
+ xfs_fsops.o \
+ xfs_ialloc.o \
+ xfs_ialloc_btree.o \
+ xfs_iget.o \
+ xfs_inode.o \
+ xfs_inode_item.o \
+ xfs_iocore.o \
+ xfs_iomap.o \
+ xfs_itable.o \
+ xfs_dfrag.o \
+ xfs_log.o \
+ xfs_log_recover.o \
+ xfs_macros.o \
+ xfs_mount.o \
+ xfs_rename.o \
+ xfs_trans.o \
+ xfs_trans_ail.o \
+ xfs_trans_buf.o \
+ xfs_trans_extfree.o \
+ xfs_trans_inode.o \
+ xfs_trans_item.o \
+ xfs_utils.o \
+ xfs_vfsops.o \
+ xfs_vnodeops.o \
+ xfs_refcache.o \
+ xfs_rw.o \
+ xfs_dmops.o \
+ xfs_qmops.o
+
+# Objects not built in this directory
+obj-y += linux-2.4/linux_xfs.o \
+ support/support_xfs.o
+
+subdir-$(CONFIG_XFS_FS) += linux-2.4 support
+
+ifeq ($(CONFIG_XFS_DMAPI),y)
+ subdir-$(CONFIG_XFS_FS) += dmapi
+ obj-y += dmapi/xfs_dmapi.o
+endif
+
+ifeq ($(CONFIG_XFS_QUOTA),y)
+ subdir-$(CONFIG_XFS_FS) += quota
+ obj-y += quota/xfs_quota.o
+endif
+
+include $(TOPDIR)/Rules.make
+
+# This is really nasty, but Rules.make was never designed for multi directory
+# modules. Keith Owens.
+
+xfs.o: $(patsubst %,_modsubdir_%,$(subdir-m))
diff --git a/fs/xfs/linux-2.4/Makefile b/fs/xfs/linux-2.4/Makefile
new file mode 100644
index 00000000000000..05218df092ae0e
--- /dev/null
+++ b/fs/xfs/linux-2.4/Makefile
@@ -0,0 +1,77 @@
+#
+# Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write the Free Software Foundation, Inc., 59
+# Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+# Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+# Mountain View, CA 94043, or:
+#
+# http://www.sgi.com
+#
+# For further information regarding this notice, see:
+#
+# http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+#
+# Makefile for XFS on Linux.
+
+EXTRA_CFLAGS += -I.. -I. -funsigned-char
+
+ifeq ($(CONFIG_XFS_DEBUG),y)
+ EXTRA_CFLAGS += -g -DSTATIC="" -DDEBUG
+ EXTRA_CFLAGS += -DPAGEBUF_LOCK_TRACKING
+endif
+ifeq ($(CONFIG_XFS_TRACE),y)
+ EXTRA_CFLAGS += -DXFS_ALLOC_TRACE
+ EXTRA_CFLAGS += -DXFS_ATTR_TRACE
+ EXTRA_CFLAGS += -DXFS_BLI_TRACE
+ EXTRA_CFLAGS += -DXFS_BMAP_TRACE
+ EXTRA_CFLAGS += -DXFS_BMBT_TRACE
+ EXTRA_CFLAGS += -DXFS_DIR_TRACE
+ EXTRA_CFLAGS += -DXFS_DQUOT_TRACE
+ EXTRA_CFLAGS += -DXFS_ILOCK_TRACE
+ EXTRA_CFLAGS += -DXFS_LOG_TRACE
+ EXTRA_CFLAGS += -DXFS_RW_TRACE
+ EXTRA_CFLAGS += -DXFS_DIR2_TRACE
+ EXTRA_CFLAGS += -DPAGEBUF_TRACE
+ # EXTRA_CFLAGS += -DXFS_VNODE_TRACE
+endif
+
+O_TARGET := linux_xfs.o
+ifneq ($(MAKECMDGOALS),modules_install)
+ obj-m := $(O_TARGET)
+endif
+
+obj-$(CONFIG_PROC_FS) += xfs_stats.o
+obj-$(CONFIG_SYSCTL) += xfs_sysctl.o
+obj-y += xfs_aops.o \
+ xfs_file.o \
+ xfs_fs_subr.o \
+ xfs_globals.o \
+ xfs_ioctl.o \
+ xfs_iops.o \
+ xfs_lrw.o \
+ xfs_super.o \
+ xfs_vfs.o \
+ xfs_vnode.o \
+ xfs_buf.o \
+ mrlock.o \
+ kmem.o
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/xfs/linux-2.4/kmem.c b/fs/xfs/linux-2.4/kmem.c
new file mode 100644
index 00000000000000..04b7429fe53941
--- /dev/null
+++ b/fs/xfs/linux-2.4/kmem.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/swap.h>
+
+#include "time.h"
+#include "kmem.h"
+
+#define MAX_VMALLOCS 6
+#define MAX_SLAB_SIZE 0x20000
+#define MAX_SHAKE 8
+
+static kmem_shake_func_t shake_list[MAX_SHAKE];
+static DECLARE_MUTEX(shake_sem);
+
+/*
+ * Old-school memory shaking for vanilla 2.4 kernels (which
+ * have no VM shake callback/registration infrastructure).
+ */
+
+kmem_shaker_t
+kmem_shake_register(kmem_shake_func_t sfunc)
+{
+ int i;
+
+ down(&shake_sem);
+ for (i = 0; i < MAX_SHAKE; i++) {
+ if (shake_list[i] == NULL) {
+ shake_list[i] = sfunc;
+ break;
+ }
+ }
+ if (i == MAX_SHAKE)
+ BUG();
+ up(&shake_sem);
+
+ return (kmem_shaker_t)sfunc;
+}
+
+void
+kmem_shake_deregister(kmem_shaker_t sfunc)
+{
+ int i;
+
+ down(&shake_sem);
+ for (i = 0; i < MAX_SHAKE; i++) {
+ if (shake_list[i] == (kmem_shake_func_t)sfunc)
+ break;
+ }
+ if (i == MAX_SHAKE)
+ BUG();
+ for (; i < MAX_SHAKE - 1; i++) {
+ shake_list[i] = shake_list[i+1];
+ }
+ shake_list[i] = NULL;
+ up(&shake_sem);
+}
+
+static __inline__ void kmem_shake(void)
+{
+ int i;
+
+ down(&shake_sem);
+ for (i = 0; i < MAX_SHAKE && shake_list[i]; i++)
+ (*shake_list[i])(0, 0);
+ up(&shake_sem);
+ delay(10);
+}
+
+void *
+kmem_alloc(size_t size, int flags)
+{
+ int retries = 0, lflags = kmem_flags_convert(flags);
+ void *ptr;
+
+ do {
+ if (size < MAX_SLAB_SIZE || retries > MAX_VMALLOCS)
+ ptr = kmalloc(size, lflags);
+ else
+ ptr = __vmalloc(size, lflags, PAGE_KERNEL);
+ if (ptr || (flags & (KM_MAYFAIL|KM_NOSLEEP)))
+ return ptr;
+ kmem_shake();
+ if (!(++retries % 100))
+ printk(KERN_ERR "possible deadlock in %s (mode:0x%x)\n",
+ __FUNCTION__, lflags);
+ } while (1);
+}
+
+void *
+kmem_zalloc(size_t size, int flags)
+{
+ void *ptr;
+
+ ptr = kmem_alloc(size, flags);
+ if (ptr)
+ memset((char *)ptr, 0, (int)size);
+ return ptr;
+}
+
+void
+kmem_free(void *ptr, size_t size)
+{
+ if (((unsigned long)ptr < VMALLOC_START) ||
+ ((unsigned long)ptr >= VMALLOC_END)) {
+ kfree(ptr);
+ } else {
+ vfree(ptr);
+ }
+}
+
+void *
+kmem_realloc(void *ptr, size_t newsize, size_t oldsize, int flags)
+{
+ void *new;
+
+ new = kmem_alloc(newsize, flags);
+ if (ptr) {
+ if (new)
+ memcpy(new, ptr,
+ ((oldsize < newsize) ? oldsize : newsize));
+ kmem_free(ptr, oldsize);
+ }
+ return new;
+}
+
+void *
+kmem_zone_alloc(kmem_zone_t *zone, int flags)
+{
+ int retries = 0, lflags = kmem_flags_convert(flags);
+ void *ptr;
+
+ do {
+ ptr = kmem_cache_alloc(zone, lflags);
+ if (ptr || (flags & (KM_MAYFAIL|KM_NOSLEEP)))
+ return ptr;
+ kmem_shake();
+ if (!(++retries % 100))
+ printk(KERN_ERR "possible deadlock in %s (mode:0x%x)\n",
+ __FUNCTION__, lflags);
+ } while (1);
+}
+
+void *
+kmem_zone_zalloc(kmem_zone_t *zone, int flags)
+{
+ void *ptr;
+
+ ptr = kmem_zone_alloc(zone, flags);
+ if (ptr)
+ memset((char *)ptr, 0, kmem_cache_size(zone));
+ return ptr;
+}
diff --git a/fs/xfs/linux-2.4/kmem.h b/fs/xfs/linux-2.4/kmem.h
new file mode 100644
index 00000000000000..13d40ebfb55693
--- /dev/null
+++ b/fs/xfs/linux-2.4/kmem.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_SUPPORT_KMEM_H__
+#define __XFS_SUPPORT_KMEM_H__
+
+#include <linux/slab.h>
+
+/*
+ * memory management routines
+ */
+#define KM_SLEEP 0x0001
+#define KM_NOSLEEP 0x0002
+#define KM_NOFS 0x0004
+#define KM_MAYFAIL 0x0008
+
+#define kmem_zone kmem_cache_s
+#define kmem_zone_t kmem_cache_t
+
+typedef unsigned long xfs_pflags_t;
+
+#define PFLAGS_TEST_NOIO() (current->flags & PF_NOIO)
+#define PFLAGS_TEST_FSTRANS() (current->flags & PF_FSTRANS)
+
+#define PFLAGS_SET_NOIO() do { \
+ current->flags |= PF_NOIO; \
+} while (0)
+
+#define PFLAGS_CLEAR_NOIO() do { \
+ current->flags &= ~PF_NOIO; \
+} while (0)
+
+/* these could be nested, so we save state */
+#define PFLAGS_SET_FSTRANS(STATEP) do { \
+ *(STATEP) = current->flags; \
+ current->flags |= PF_FSTRANS; \
+} while (0)
+
+#define PFLAGS_CLEAR_FSTRANS(STATEP) do { \
+ *(STATEP) = current->flags; \
+ current->flags &= ~PF_FSTRANS; \
+} while (0)
+
+/* Restore the PF_FSTRANS state to what was saved in STATEP */
+#define PFLAGS_RESTORE_FSTRANS(STATEP) do { \
+ current->flags = ((current->flags & ~PF_FSTRANS) | \
+ (*(STATEP) & PF_FSTRANS)); \
+} while (0)
+
+#define PFLAGS_DUP(OSTATEP, NSTATEP) do { \
+ *(NSTATEP) = *(OSTATEP); \
+} while (0)
+
+static __inline unsigned int kmem_flags_convert(int flags)
+{
+ int lflags;
+
+#ifdef DEBUG
+ if (unlikely(flags & ~(KM_SLEEP|KM_NOSLEEP|KM_NOFS|KM_MAYFAIL))) {
+ printk(KERN_WARNING
+ "XFS: memory allocation with wrong flags (%x)\n", flags);
+ BUG();
+ }
+#endif
+
+ if (flags & KM_NOSLEEP) {
+ lflags = GFP_ATOMIC;
+ } else {
+ lflags = GFP_KERNEL;
+
+ /* avoid recusive callbacks to filesystem during transactions */
+ if (PFLAGS_TEST_FSTRANS() || (flags & KM_NOFS))
+ lflags &= ~__GFP_FS;
+ }
+
+ return lflags;
+}
+
+static __inline kmem_zone_t *
+kmem_zone_init(int size, char *zone_name)
+{
+ return kmem_cache_create(zone_name, size, 0, 0, NULL, NULL);
+}
+
+static __inline void
+kmem_zone_free(kmem_zone_t *zone, void *ptr)
+{
+ kmem_cache_free(zone, ptr);
+}
+
+static __inline void
+kmem_zone_destroy(kmem_zone_t *zone)
+{
+ if (zone && kmem_cache_destroy(zone))
+ BUG();
+}
+
+static __inline int
+kmem_zone_shrink(kmem_zone_t *zone)
+{
+ return kmem_cache_shrink(zone);
+}
+
+extern void *kmem_zone_zalloc(kmem_zone_t *, int);
+extern void *kmem_zone_alloc(kmem_zone_t *, int);
+
+extern void *kmem_alloc(size_t, int);
+extern void *kmem_realloc(void *, size_t, size_t, int);
+extern void *kmem_zalloc(size_t, int);
+extern void kmem_free(void *, size_t);
+
+typedef void *kmem_shaker_t;
+typedef int (*kmem_shake_func_t)(int, unsigned int);
+
+extern kmem_shaker_t kmem_shake_register(kmem_shake_func_t);
+extern void kmem_shake_deregister(kmem_shaker_t);
+
+static __inline int
+kmem_shake_allow(unsigned int gfp_mask)
+{
+ return (gfp_mask & __GFP_WAIT);
+}
+
+#endif /* __XFS_SUPPORT_KMEM_H__ */
diff --git a/fs/xfs/linux-2.4/mrlock.c b/fs/xfs/linux-2.4/mrlock.c
new file mode 100644
index 00000000000000..f6de257f0945f4
--- /dev/null
+++ b/fs/xfs/linux-2.4/mrlock.c
@@ -0,0 +1,274 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include <linux/time.h>
+#include <linux/sched.h>
+#include <asm/system.h>
+#include <linux/interrupt.h>
+#include <asm/current.h>
+
+#include "mrlock.h"
+
+
+#if USE_RW_WAIT_QUEUE_SPINLOCK
+# define wq_write_lock write_lock
+#else
+# define wq_write_lock spin_lock
+#endif
+
+/*
+ * We don't seem to need lock_type (only one supported), name, or
+ * sequence. But, XFS will pass it so let's leave them here for now.
+ */
+/* ARGSUSED */
+void
+mrlock_init(mrlock_t *mrp, int lock_type, char *name, long sequence)
+{
+ mrp->mr_count = 0;
+ mrp->mr_reads_waiting = 0;
+ mrp->mr_writes_waiting = 0;
+ init_waitqueue_head(&mrp->mr_readerq);
+ init_waitqueue_head(&mrp->mr_writerq);
+ mrp->mr_lock = SPIN_LOCK_UNLOCKED;
+}
+
+/*
+ * Macros to lock/unlock the mrlock_t.
+ */
+
+#define MRLOCK(m) spin_lock(&(m)->mr_lock);
+#define MRUNLOCK(m) spin_unlock(&(m)->mr_lock);
+
+
+/*
+ * lock_wait should never be called in an interrupt thread.
+ *
+ * mrlocks can sleep (i.e. call schedule) and so they can't ever
+ * be called from an interrupt thread.
+ *
+ * threads that wake-up should also never be invoked from interrupt threads.
+ *
+ * But, waitqueue_lock is locked from interrupt threads - and we are
+ * called with interrupts disabled, so it is all OK.
+ */
+
+/* ARGSUSED */
+void
+lock_wait(wait_queue_head_t *q, spinlock_t *lock, int rw)
+{
+ DECLARE_WAITQUEUE( wait, current );
+
+ __set_current_state(TASK_UNINTERRUPTIBLE);
+
+ wq_write_lock(&q->lock);
+ if (rw) {
+ __add_wait_queue_tail(q, &wait);
+ } else {
+ __add_wait_queue(q, &wait);
+ }
+
+ wq_write_unlock(&q->lock);
+ spin_unlock(lock);
+
+ schedule();
+
+ wq_write_lock(&q->lock);
+ __remove_wait_queue(q, &wait);
+ wq_write_unlock(&q->lock);
+
+ spin_lock(lock);
+
+ /* return with lock held */
+}
+
+/* ARGSUSED */
+void
+mrfree(mrlock_t *mrp)
+{
+}
+
+/* ARGSUSED */
+void
+mrlock(mrlock_t *mrp, int type, int flags)
+{
+ if (type == MR_ACCESS)
+ mraccess(mrp);
+ else
+ mrupdate(mrp);
+}
+
+/* ARGSUSED */
+void
+mraccessf(mrlock_t *mrp, int flags)
+{
+ MRLOCK(mrp);
+ if(mrp->mr_writes_waiting > 0) {
+ mrp->mr_reads_waiting++;
+ lock_wait(&mrp->mr_readerq, &mrp->mr_lock, 0);
+ mrp->mr_reads_waiting--;
+ }
+ while (mrp->mr_count < 0) {
+ mrp->mr_reads_waiting++;
+ lock_wait(&mrp->mr_readerq, &mrp->mr_lock, 0);
+ mrp->mr_reads_waiting--;
+ }
+ mrp->mr_count++;
+ MRUNLOCK(mrp);
+}
+
+/* ARGSUSED */
+void
+mrupdatef(mrlock_t *mrp, int flags)
+{
+ MRLOCK(mrp);
+ while(mrp->mr_count) {
+ mrp->mr_writes_waiting++;
+ lock_wait(&mrp->mr_writerq, &mrp->mr_lock, 1);
+ mrp->mr_writes_waiting--;
+ }
+
+ mrp->mr_count = -1; /* writer on it */
+ MRUNLOCK(mrp);
+}
+
+int
+mrtryaccess(mrlock_t *mrp)
+{
+ MRLOCK(mrp);
+ /*
+ * If anyone is waiting for update access or the lock is held for update
+ * fail the request.
+ */
+ if(mrp->mr_writes_waiting > 0 || mrp->mr_count < 0) {
+ MRUNLOCK(mrp);
+ return 0;
+ }
+ mrp->mr_count++;
+ MRUNLOCK(mrp);
+ return 1;
+}
+
+int
+mrtrypromote(mrlock_t *mrp)
+{
+ MRLOCK(mrp);
+
+ if(mrp->mr_count == 1) { /* We are the only thread with the lock */
+ mrp->mr_count = -1; /* writer on it */
+ MRUNLOCK(mrp);
+ return 1;
+ }
+
+ MRUNLOCK(mrp);
+ return 0;
+}
+
+int
+mrtryupdate(mrlock_t *mrp)
+{
+ MRLOCK(mrp);
+
+ if(mrp->mr_count) {
+ MRUNLOCK(mrp);
+ return 0;
+ }
+
+ mrp->mr_count = -1; /* writer on it */
+ MRUNLOCK(mrp);
+ return 1;
+}
+
+static __inline__ void mrwake(mrlock_t *mrp)
+{
+ /*
+ * First, if the count is now 0, we need to wake-up anyone waiting.
+ */
+ if (!mrp->mr_count) {
+ if (mrp->mr_writes_waiting) { /* Wake-up first writer waiting */
+ wake_up(&mrp->mr_writerq);
+ } else if (mrp->mr_reads_waiting) { /* Wakeup any readers waiting */
+ wake_up(&mrp->mr_readerq);
+ }
+ }
+}
+
+void
+mraccunlock(mrlock_t *mrp)
+{
+ MRLOCK(mrp);
+ mrp->mr_count--;
+ mrwake(mrp);
+ MRUNLOCK(mrp);
+}
+
+void
+mrunlock(mrlock_t *mrp)
+{
+ MRLOCK(mrp);
+ if (mrp->mr_count < 0) {
+ mrp->mr_count = 0;
+ } else {
+ mrp->mr_count--;
+ }
+ mrwake(mrp);
+ MRUNLOCK(mrp);
+}
+
+int
+ismrlocked(mrlock_t *mrp, int type) /* No need to lock since info can change */
+{
+ if (type == MR_ACCESS)
+ return (mrp->mr_count > 0); /* Read lock */
+ else if (type == MR_UPDATE)
+ return (mrp->mr_count < 0); /* Write lock */
+ else if (type == (MR_UPDATE | MR_ACCESS))
+ return (mrp->mr_count); /* Any type of lock held */
+ else /* Any waiters */
+ return (mrp->mr_reads_waiting | mrp->mr_writes_waiting);
+}
+
+/*
+ * Demote from update to access. We better be the only thread with the
+ * lock in update mode so it should be easy to set to 1.
+ * Wake-up any readers waiting.
+ */
+
+void
+mrdemote(mrlock_t *mrp)
+{
+ MRLOCK(mrp);
+ mrp->mr_count = 1;
+ if (mrp->mr_reads_waiting) { /* Wakeup all readers waiting */
+ wake_up(&mrp->mr_readerq);
+ }
+ MRUNLOCK(mrp);
+}
diff --git a/fs/xfs/linux-2.4/mrlock.h b/fs/xfs/linux-2.4/mrlock.h
new file mode 100644
index 00000000000000..b2a7b3ad5efff7
--- /dev/null
+++ b/fs/xfs/linux-2.4/mrlock.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_SUPPORT_MRLOCK_H__
+#define __XFS_SUPPORT_MRLOCK_H__
+
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <asm/atomic.h>
+#include <asm/semaphore.h>
+
+/*
+ * Implement mrlocks on Linux that work for XFS.
+ *
+ * These are sleep locks and not spinlocks. If one wants read/write spinlocks,
+ * use read_lock, write_lock, ... see spinlock.h.
+ */
+
+typedef struct mrlock_s {
+ int mr_count;
+ unsigned short mr_reads_waiting;
+ unsigned short mr_writes_waiting;
+ wait_queue_head_t mr_readerq;
+ wait_queue_head_t mr_writerq;
+ spinlock_t mr_lock;
+} mrlock_t;
+
+#define MR_ACCESS 1
+#define MR_UPDATE 2
+
+#define MRLOCK_BARRIER 0x1
+#define MRLOCK_ALLOW_EQUAL_PRI 0x8
+
+/*
+ * mraccessf/mrupdatef take flags to be passed in while sleeping;
+ * only PLTWAIT is currently supported.
+ */
+
+extern void mraccessf(mrlock_t *, int);
+extern void mrupdatef(mrlock_t *, int);
+extern void mrlock(mrlock_t *, int, int);
+extern void mrunlock(mrlock_t *);
+extern void mraccunlock(mrlock_t *);
+extern int mrtryupdate(mrlock_t *);
+extern int mrtryaccess(mrlock_t *);
+extern int mrtrypromote(mrlock_t *);
+extern void mrdemote(mrlock_t *);
+
+extern int ismrlocked(mrlock_t *, int);
+extern void mrlock_init(mrlock_t *, int type, char *name, long sequence);
+extern void mrfree(mrlock_t *);
+
+#define mrinit(mrp, name) mrlock_init(mrp, MRLOCK_BARRIER, name, -1)
+#define mraccess(mrp) mraccessf(mrp, 0) /* grab for READ/ACCESS */
+#define mrupdate(mrp) mrupdatef(mrp, 0) /* grab for WRITE/UPDATE */
+#define mrislocked_access(mrp) ((mrp)->mr_count > 0)
+#define mrislocked_update(mrp) ((mrp)->mr_count < 0)
+
+#endif /* __XFS_SUPPORT_MRLOCK_H__ */
diff --git a/fs/xfs/linux-2.4/mutex.h b/fs/xfs/linux-2.4/mutex.h
new file mode 100644
index 00000000000000..0b296bb944cbee
--- /dev/null
+++ b/fs/xfs/linux-2.4/mutex.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_SUPPORT_MUTEX_H__
+#define __XFS_SUPPORT_MUTEX_H__
+
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+
+/*
+ * Map the mutex'es from IRIX to Linux semaphores.
+ *
+ * Destroy just simply initializes to -99 which should block all other
+ * callers.
+ */
+#define MUTEX_DEFAULT 0x0
+typedef struct semaphore mutex_t;
+
+#define mutex_init(lock, type, name) sema_init(lock, 1)
+#define mutex_destroy(lock) sema_init(lock, -99)
+#define mutex_lock(lock, num) down(lock)
+#define mutex_trylock(lock) (down_trylock(lock) ? 0 : 1)
+#define mutex_unlock(lock) up(lock)
+
+#endif /* __XFS_SUPPORT_MUTEX_H__ */
diff --git a/fs/xfs/linux-2.4/sema.h b/fs/xfs/linux-2.4/sema.h
new file mode 100644
index 00000000000000..30b67b4e1cbfb4
--- /dev/null
+++ b/fs/xfs/linux-2.4/sema.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_SUPPORT_SEMA_H__
+#define __XFS_SUPPORT_SEMA_H__
+
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <asm/atomic.h>
+#include <asm/semaphore.h>
+
+/*
+ * sema_t structure just maps to struct semaphore in Linux kernel.
+ */
+
+typedef struct semaphore sema_t;
+
+#define init_sema(sp, val, c, d) sema_init(sp, val)
+#define initsema(sp, val) sema_init(sp, val)
+#define initnsema(sp, val, name) sema_init(sp, val)
+#define psema(sp, b) down(sp)
+#define vsema(sp) up(sp)
+#define valusema(sp) (atomic_read(&(sp)->count))
+#define freesema(sema)
+
+/*
+ * Map cpsema (try to get the sema) to down_trylock. We need to switch
+ * the return values since cpsema returns 1 (acquired) 0 (failed) and
+ * down_trylock returns the reverse 0 (acquired) 1 (failed).
+ */
+
+#define cpsema(sp) (down_trylock(sp) ? 0 : 1)
+
+/*
+ * Didn't do cvsema(sp). Not sure how to map this to up/down/...
+ * It does a vsema if the values is < 0 other wise nothing.
+ */
+
+#endif /* __XFS_SUPPORT_SEMA_H__ */
diff --git a/fs/xfs/linux-2.4/spin.h b/fs/xfs/linux-2.4/spin.h
new file mode 100644
index 00000000000000..bcf60a0b8df0ee
--- /dev/null
+++ b/fs/xfs/linux-2.4/spin.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_SUPPORT_SPIN_H__
+#define __XFS_SUPPORT_SPIN_H__
+
+#include <linux/sched.h> /* preempt needs this */
+#include <linux/spinlock.h>
+
+/*
+ * Map lock_t from IRIX to Linux spinlocks.
+ *
+ * We do not make use of lock_t from interrupt context, so we do not
+ * have to worry about disabling interrupts at all (unlike IRIX).
+ */
+
+typedef spinlock_t lock_t;
+
+#define SPLDECL(s) unsigned long s
+
+#define spinlock_init(lock, name) spin_lock_init(lock)
+#define spinlock_destroy(lock)
+#define mutex_spinlock(lock) ({ spin_lock(lock); 0; })
+#define mutex_spinunlock(lock, s) do { spin_unlock(lock); (void)s; } while (0)
+#define nested_spinlock(lock) spin_lock(lock)
+#define nested_spinunlock(lock) spin_unlock(lock)
+
+#endif /* __XFS_SUPPORT_SPIN_H__ */
diff --git a/fs/xfs/linux-2.4/sv.h b/fs/xfs/linux-2.4/sv.h
new file mode 100644
index 00000000000000..821d3167e05b73
--- /dev/null
+++ b/fs/xfs/linux-2.4/sv.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_SUPPORT_SV_H__
+#define __XFS_SUPPORT_SV_H__
+
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+
+/*
+ * Synchronisation variables.
+ *
+ * (Parameters "pri", "svf" and "rts" are not implemented)
+ */
+
+typedef struct sv_s {
+ wait_queue_head_t waiters;
+} sv_t;
+
+#define SV_FIFO 0x0 /* sv_t is FIFO type */
+#define SV_LIFO 0x2 /* sv_t is LIFO type */
+#define SV_PRIO 0x4 /* sv_t is PRIO type */
+#define SV_KEYED 0x6 /* sv_t is KEYED type */
+#define SV_DEFAULT SV_FIFO
+
+
+static inline void _sv_wait(sv_t *sv, spinlock_t *lock, int state,
+ unsigned long timeout)
+{
+ DECLARE_WAITQUEUE(wait, current);
+
+ add_wait_queue_exclusive(&sv->waiters, &wait);
+ __set_current_state(state);
+ spin_unlock(lock);
+
+ schedule_timeout(timeout);
+
+ remove_wait_queue(&sv->waiters, &wait);
+}
+
+#define init_sv(sv,type,name,flag) \
+ init_waitqueue_head(&(sv)->waiters)
+#define sv_init(sv,flag,name) \
+ init_waitqueue_head(&(sv)->waiters)
+#define sv_destroy(sv) \
+ /*NOTHING*/
+#define sv_wait(sv, pri, lock, s) \
+ _sv_wait(sv, lock, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT)
+#define sv_wait_sig(sv, pri, lock, s) \
+ _sv_wait(sv, lock, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT)
+#define sv_timedwait(sv, pri, lock, s, svf, ts, rts) \
+ _sv_wait(sv, lock, TASK_UNINTERRUPTIBLE, timespec_to_jiffies(ts))
+#define sv_timedwait_sig(sv, pri, lock, s, svf, ts, rts) \
+ _sv_wait(sv, lock, TASK_INTERRUPTIBLE, timespec_to_jiffies(ts))
+#define sv_signal(sv) \
+ wake_up(&(sv)->waiters)
+#define sv_broadcast(sv) \
+ wake_up_all(&(sv)->waiters)
+
+#endif /* __XFS_SUPPORT_SV_H__ */
diff --git a/fs/xfs/linux-2.4/time.h b/fs/xfs/linux-2.4/time.h
new file mode 100644
index 00000000000000..63fcb9e4ab15f0
--- /dev/null
+++ b/fs/xfs/linux-2.4/time.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_SUPPORT_TIME_H__
+#define __XFS_SUPPORT_TIME_H__
+
+#include <linux/sched.h>
+#include <linux/time.h>
+
+typedef struct timespec timespec_t;
+
+static inline void delay(long ticks)
+{
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(ticks);
+}
+
+static inline void nanotime(struct timespec *tvp)
+{
+ struct timeval tv;
+
+ do_gettimeofday(&tv);
+ tvp->tv_sec = tv.tv_sec;
+ tvp->tv_nsec = tv.tv_usec * 1000;
+}
+
+#endif /* __XFS_SUPPORT_TIME_H__ */
diff --git a/fs/xfs/linux-2.4/xfs_aops.c b/fs/xfs/linux-2.4/xfs_aops.c
new file mode 100644
index 00000000000000..860ccf8e1bedf5
--- /dev/null
+++ b/fs/xfs/linux-2.4/xfs_aops.c
@@ -0,0 +1,1299 @@
+/*
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_sb.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_trans.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_alloc.h"
+#include "xfs_btree.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode.h"
+#include "xfs_error.h"
+#include "xfs_rw.h"
+#include "xfs_iomap.h"
+#include <linux/iobuf.h>
+
+STATIC void xfs_count_page_state(struct page *, int *, int *, int *);
+STATIC void xfs_convert_page(struct inode *, struct page *,
+ xfs_iomap_t *, void *, int, int);
+
+#if defined(XFS_RW_TRACE)
+void
+xfs_page_trace(
+ int tag,
+ struct inode *inode,
+ struct page *page,
+ int mask)
+{
+ xfs_inode_t *ip;
+ bhv_desc_t *bdp;
+ vnode_t *vp = LINVFS_GET_VP(inode);
+ loff_t isize = i_size_read(inode);
+ loff_t offset = page->index << PAGE_CACHE_SHIFT;
+ int delalloc = -1, unmapped = -1, unwritten = -1;
+
+ if (page_has_buffers(page))
+ xfs_count_page_state(page, &delalloc, &unmapped, &unwritten);
+
+ bdp = vn_bhv_lookup(VN_BHV_HEAD(vp), &xfs_vnodeops);
+ ip = XFS_BHVTOI(bdp);
+ if (!ip->i_rwtrace)
+ return;
+
+ ktrace_enter(ip->i_rwtrace,
+ (void *)((unsigned long)tag),
+ (void *)ip,
+ (void *)inode,
+ (void *)page,
+ (void *)((unsigned long)mask),
+ (void *)((unsigned long)((ip->i_d.di_size >> 32) & 0xffffffff)),
+ (void *)((unsigned long)(ip->i_d.di_size & 0xffffffff)),
+ (void *)((unsigned long)((isize >> 32) & 0xffffffff)),
+ (void *)((unsigned long)(isize & 0xffffffff)),
+ (void *)((unsigned long)((offset >> 32) & 0xffffffff)),
+ (void *)((unsigned long)(offset & 0xffffffff)),
+ (void *)((unsigned long)delalloc),
+ (void *)((unsigned long)unmapped),
+ (void *)((unsigned long)unwritten),
+ (void *)NULL,
+ (void *)NULL);
+}
+#else
+#define xfs_page_trace(tag, inode, page, mask)
+#endif
+
+void
+linvfs_unwritten_done(
+ struct buffer_head *bh,
+ int uptodate)
+{
+ xfs_buf_t *pb = (xfs_buf_t *)bh->b_private;
+
+ ASSERT(buffer_unwritten(bh));
+ bh->b_end_io = NULL;
+ clear_buffer_unwritten(bh);
+ if (!uptodate)
+ pagebuf_ioerror(pb, EIO);
+ if (atomic_dec_and_test(&pb->pb_io_remaining) == 1) {
+ pagebuf_iodone(pb, 1, 1);
+ }
+ end_buffer_io_async(bh, uptodate);
+}
+
+/*
+ * Issue transactions to convert a buffer range from unwritten
+ * to written extents.
+ */
+STATIC void
+linvfs_unwritten_convert(
+ xfs_buf_t *bp)
+{
+ vnode_t *vp = XFS_BUF_FSPRIVATE(bp, vnode_t *);
+ int error;
+
+ BUG_ON(atomic_read(&bp->pb_hold) < 1);
+ VOP_BMAP(vp, XFS_BUF_OFFSET(bp), XFS_BUF_SIZE(bp),
+ BMAPI_UNWRITTEN, NULL, NULL, error);
+ XFS_BUF_SET_FSPRIVATE(bp, NULL);
+ XFS_BUF_CLR_IODONE_FUNC(bp);
+ XFS_BUF_UNDATAIO(bp);
+ iput(LINVFS_GET_IP(vp));
+ pagebuf_iodone(bp, 0, 0);
+}
+
+STATIC int
+xfs_map_blocks(
+ struct inode *inode,
+ loff_t offset,
+ ssize_t count,
+ xfs_iomap_t *iomapp,
+ int flags)
+{
+ vnode_t *vp = LINVFS_GET_VP(inode);
+ int error, niomaps = 1;
+
+ VOP_BMAP(vp, offset, count, flags, iomapp, &niomaps, error);
+ if (!error && (flags & (BMAPI_WRITE|BMAPI_ALLOCATE)))
+ VMODIFY(vp);
+ return -error;
+}
+
+/*
+ * Finds the corresponding mapping in block @map array of the
+ * given @offset within a @page.
+ */
+STATIC xfs_iomap_t *
+xfs_offset_to_map(
+ struct page *page,
+ xfs_iomap_t *iomapp,
+ unsigned long offset)
+{
+ loff_t full_offset; /* offset from start of file */
+
+ ASSERT(offset < PAGE_CACHE_SIZE);
+
+ full_offset = page->index; /* NB: using 64bit number */
+ full_offset <<= PAGE_CACHE_SHIFT; /* offset from file start */
+ full_offset += offset; /* offset from page start */
+
+ if (full_offset < iomapp->iomap_offset)
+ return NULL;
+ if (iomapp->iomap_offset + (iomapp->iomap_bsize -1) >= full_offset)
+ return iomapp;
+ return NULL;
+}
+
+STATIC void
+xfs_map_at_offset(
+ struct page *page,
+ struct buffer_head *bh,
+ unsigned long offset,
+ int block_bits,
+ xfs_iomap_t *iomapp)
+{
+ xfs_daddr_t bn;
+ loff_t delta;
+ int sector_shift;
+
+ ASSERT(!(iomapp->iomap_flags & IOMAP_HOLE));
+ ASSERT(!(iomapp->iomap_flags & IOMAP_DELAY));
+ ASSERT(iomapp->iomap_bn != IOMAP_DADDR_NULL);
+
+ delta = page->index;
+ delta <<= PAGE_CACHE_SHIFT;
+ delta += offset;
+ delta -= iomapp->iomap_offset;
+ delta >>= block_bits;
+
+ sector_shift = block_bits - BBSHIFT;
+ bn = iomapp->iomap_bn >> sector_shift;
+ bn += delta;
+ BUG_ON(!bn && !(iomapp->iomap_flags & IOMAP_REALTIME));
+ ASSERT((bn << sector_shift) >= iomapp->iomap_bn);
+
+ lock_buffer(bh);
+ bh->b_blocknr = bn;
+ bh->b_dev = iomapp->iomap_target->pbr_kdev;
+ set_buffer_mapped(bh);
+ clear_buffer_delay(bh);
+}
+
+/*
+ * Look for a page at index which is unlocked and contains our
+ * unwritten extent flagged buffers at its head. Returns page
+ * locked and with an extra reference count, and length of the
+ * unwritten extent component on this page that we can write,
+ * in units of filesystem blocks.
+ */
+STATIC struct page *
+xfs_probe_unwritten_page(
+ struct address_space *mapping,
+ pgoff_t index,
+ xfs_iomap_t *iomapp,
+ xfs_buf_t *pb,
+ unsigned long max_offset,
+ unsigned long *fsbs,
+ unsigned int bbits)
+{
+ struct page *page;
+
+ page = find_trylock_page(mapping, index);
+ if (!page)
+ return 0;
+
+ if (page->mapping && page_has_buffers(page)) {
+ struct buffer_head *bh, *head;
+ unsigned long p_offset = 0;
+
+ *fsbs = 0;
+ bh = head = page_buffers(page);
+ do {
+ if (!buffer_unwritten(bh) || !buffer_uptodate(bh))
+ break;
+ if (!xfs_offset_to_map(page, iomapp, p_offset))
+ break;
+ if (p_offset >= max_offset)
+ break;
+ xfs_map_at_offset(page, bh, p_offset, bbits, iomapp);
+ set_buffer_unwritten_io(bh);
+ bh->b_private = pb;
+ p_offset += bh->b_size;
+ (*fsbs)++;
+ } while ((bh = bh->b_this_page) != head);
+
+ if (p_offset)
+ return page;
+ }
+
+ unlock_page(page);
+ return NULL;
+}
+
+/*
+ * Look for a page at index which is unlocked and not mapped
+ * yet - clustering for mmap write case.
+ */
+STATIC unsigned int
+xfs_probe_unmapped_page(
+ struct address_space *mapping,
+ pgoff_t index,
+ unsigned int pg_offset)
+{
+ struct page *page;
+ int ret = 0;
+
+ page = find_trylock_page(mapping, index);
+ if (!page)
+ return 0;
+
+ if (page->mapping && PageDirty(page)) {
+ if (page_has_buffers(page)) {
+ struct buffer_head *bh, *head;
+
+ bh = head = page_buffers(page);
+ do {
+ if (buffer_mapped(bh) || !buffer_uptodate(bh))
+ break;
+ ret += bh->b_size;
+ if (ret >= pg_offset)
+ break;
+ } while ((bh = bh->b_this_page) != head);
+ } else
+ ret = PAGE_CACHE_SIZE;
+ }
+
+ unlock_page(page);
+ return ret;
+}
+
+STATIC unsigned int
+xfs_probe_unmapped_cluster(
+ struct inode *inode,
+ struct page *startpage,
+ struct buffer_head *bh,
+ struct buffer_head *head)
+{
+ pgoff_t tindex, tlast, tloff;
+ unsigned int pg_offset, len, total = 0;
+ struct address_space *mapping = inode->i_mapping;
+
+ /* First sum forwards in this page */
+ do {
+ if (buffer_mapped(bh))
+ break;
+ total += bh->b_size;
+ } while ((bh = bh->b_this_page) != head);
+
+ /* If we reached the end of the page, sum forwards in
+ * following pages.
+ */
+ if (bh == head) {
+ tlast = i_size_read(inode) >> PAGE_CACHE_SHIFT;
+ /* Prune this back to avoid pathological behavior */
+ tloff = min(tlast, startpage->index + 64);
+ for (tindex = startpage->index + 1; tindex < tloff; tindex++) {
+ len = xfs_probe_unmapped_page(mapping, tindex,
+ PAGE_CACHE_SIZE);
+ if (!len)
+ return total;
+ total += len;
+ }
+ if (tindex == tlast &&
+ (pg_offset = i_size_read(inode) & (PAGE_CACHE_SIZE - 1))) {
+ total += xfs_probe_unmapped_page(mapping,
+ tindex, pg_offset);
+ }
+ }
+ return total;
+}
+
+/*
+ * Probe for a given page (index) in the inode and test if it is delayed
+ * and without unwritten buffers. Returns page locked and with an extra
+ * reference count.
+ */
+STATIC struct page *
+xfs_probe_delalloc_page(
+ struct inode *inode,
+ pgoff_t index)
+{
+ struct page *page;
+
+ page = find_trylock_page(inode->i_mapping, index);
+ if (!page)
+ return NULL;
+
+ if (page->mapping && page_has_buffers(page)) {
+ struct buffer_head *bh, *head;
+ int acceptable = 0;
+
+ bh = head = page_buffers(page);
+ do {
+ if (buffer_unwritten(bh)) {
+ acceptable = 0;
+ break;
+ } else if (buffer_delay(bh)) {
+ acceptable = 1;
+ }
+ } while ((bh = bh->b_this_page) != head);
+
+ if (acceptable)
+ return page;
+ }
+
+ unlock_page(page);
+ return NULL;
+}
+
+STATIC int
+xfs_map_unwritten(
+ struct inode *inode,
+ struct page *start_page,
+ struct buffer_head *head,
+ struct buffer_head *curr,
+ unsigned long p_offset,
+ int block_bits,
+ xfs_iomap_t *iomapp,
+ int startio,
+ int all_bh)
+{
+ struct buffer_head *bh = curr;
+ xfs_iomap_t *tmp;
+ xfs_buf_t *pb;
+ loff_t offset, size;
+ unsigned long nblocks = 0;
+
+ offset = start_page->index;
+ offset <<= PAGE_CACHE_SHIFT;
+ offset += p_offset;
+
+ /* get an "empty" pagebuf to manage IO completion
+ * Proper values will be set before returning */
+ pb = pagebuf_lookup(iomapp->iomap_target, 0, 0, 0);
+ if (!pb)
+ return -EAGAIN;
+
+ /* Take a reference to the inode to prevent it from
+ * being reclaimed while we have outstanding unwritten
+ * extent IO on it.
+ */
+ if ((igrab(inode)) != inode) {
+ pagebuf_free(pb);
+ return -EAGAIN;
+ }
+
+ /* Set the count to 1 initially, this will stop an I/O
+ * completion callout which happens before we have started
+ * all the I/O from calling pagebuf_iodone too early.
+ */
+ atomic_set(&pb->pb_io_remaining, 1);
+
+ /* First map forwards in the page consecutive buffers
+ * covering this unwritten extent
+ */
+ do {
+ if (!buffer_unwritten(bh))
+ break;
+ tmp = xfs_offset_to_map(start_page, iomapp, p_offset);
+ if (!tmp)
+ break;
+ xfs_map_at_offset(start_page, bh, p_offset, block_bits, iomapp);
+ set_buffer_unwritten_io(bh);
+ bh->b_private = pb;
+ p_offset += bh->b_size;
+ nblocks++;
+ } while ((bh = bh->b_this_page) != head);
+
+ atomic_add(nblocks, &pb->pb_io_remaining);
+
+ /* If we reached the end of the page, map forwards in any
+ * following pages which are also covered by this extent.
+ */
+ if (bh == head) {
+ struct address_space *mapping = inode->i_mapping;
+ pgoff_t tindex, tloff, tlast;
+ unsigned long bs;
+ unsigned int pg_offset, bbits = inode->i_blkbits;
+ struct page *page;
+
+ tlast = i_size_read(inode) >> PAGE_CACHE_SHIFT;
+ tloff = (iomapp->iomap_offset + iomapp->iomap_bsize) >> PAGE_CACHE_SHIFT;
+ tloff = min(tlast, tloff);
+ for (tindex = start_page->index + 1; tindex < tloff; tindex++) {
+ page = xfs_probe_unwritten_page(mapping,
+ tindex, iomapp, pb,
+ PAGE_CACHE_SIZE, &bs, bbits);
+ if (!page)
+ break;
+ nblocks += bs;
+ atomic_add(bs, &pb->pb_io_remaining);
+ xfs_convert_page(inode, page, iomapp, pb,
+ startio, all_bh);
+ /* stop if converting the next page might add
+ * enough blocks that the corresponding byte
+ * count won't fit in our ulong page buf length */
+ if (nblocks >= ((ULONG_MAX - PAGE_SIZE) >> block_bits))
+ goto enough;
+ }
+
+ if (tindex == tlast &&
+ (pg_offset = (i_size_read(inode) & (PAGE_CACHE_SIZE - 1)))) {
+ page = xfs_probe_unwritten_page(mapping,
+ tindex, iomapp, pb,
+ pg_offset, &bs, bbits);
+ if (page) {
+ nblocks += bs;
+ atomic_add(bs, &pb->pb_io_remaining);
+ xfs_convert_page(inode, page, iomapp, pb,
+ startio, all_bh);
+ if (nblocks >= ((ULONG_MAX - PAGE_SIZE) >> block_bits))
+ goto enough;
+ }
+ }
+ }
+
+enough:
+ size = nblocks; /* NB: using 64bit number here */
+ size <<= block_bits; /* convert fsb's to byte range */
+
+ XFS_BUF_DATAIO(pb);
+ XFS_BUF_ASYNC(pb);
+ XFS_BUF_SET_SIZE(pb, size);
+ XFS_BUF_SET_COUNT(pb, size);
+ XFS_BUF_SET_OFFSET(pb, offset);
+ XFS_BUF_SET_FSPRIVATE(pb, LINVFS_GET_VP(inode));
+ XFS_BUF_SET_IODONE_FUNC(pb, linvfs_unwritten_convert);
+
+ if (atomic_dec_and_test(&pb->pb_io_remaining) == 1) {
+ pagebuf_iodone(pb, 1, 1);
+ }
+
+ return 0;
+}
+
+STATIC void
+xfs_submit_page(
+ struct page *page,
+ struct buffer_head *bh_arr[],
+ int bh_count)
+{
+ struct buffer_head *bh;
+ int i;
+
+ if (bh_count) {
+ for (i = 0; i < bh_count; i++) {
+ bh = bh_arr[i];
+ set_buffer_async_io(bh);
+ if (buffer_unwritten(bh))
+ set_buffer_unwritten_io(bh);
+ set_buffer_uptodate(bh);
+ clear_buffer_dirty(bh);
+ }
+
+ for (i = 0; i < bh_count; i++) {
+ refile_buffer(bh_arr[i]);
+ submit_bh(WRITE, bh_arr[i]);
+ }
+ } else {
+ unlock_page(page);
+ }
+}
+
+/*
+ * Allocate & map buffers for page given the extent map. Write it out.
+ * except for the original page of a writepage, this is called on
+ * delalloc/unwritten pages only, for the original page it is possible
+ * that the page has no mapping at all.
+ */
+STATIC void
+xfs_convert_page(
+ struct inode *inode,
+ struct page *page,
+ xfs_iomap_t *iomapp,
+ void *private,
+ int startio,
+ int all_bh)
+{
+ struct buffer_head *bh_arr[MAX_BUF_PER_PAGE], *bh, *head;
+ xfs_iomap_t *mp = iomapp, *tmp;
+ unsigned long end, offset;
+ pgoff_t end_index;
+ int i = 0, index = 0;
+ int bbits = inode->i_blkbits;
+
+ end_index = i_size_read(inode) >> PAGE_CACHE_SHIFT;
+ if (page->index < end_index) {
+ end = PAGE_CACHE_SIZE;
+ } else {
+ end = i_size_read(inode) & (PAGE_CACHE_SIZE-1);
+ }
+ bh = head = page_buffers(page);
+ do {
+ offset = i << bbits;
+ if (offset >= end)
+ break;
+ if (!(PageUptodate(page) || buffer_uptodate(bh)))
+ continue;
+ if (buffer_mapped(bh) && all_bh &&
+ !buffer_unwritten(bh) && !buffer_delay(bh)) {
+ if (startio) {
+ lock_buffer(bh);
+ bh_arr[index++] = bh;
+ }
+ continue;
+ }
+ tmp = xfs_offset_to_map(page, mp, offset);
+ if (!tmp)
+ continue;
+ ASSERT(!(tmp->iomap_flags & IOMAP_HOLE));
+ ASSERT(!(tmp->iomap_flags & IOMAP_DELAY));
+
+ /* If this is a new unwritten extent buffer (i.e. one
+ * that we haven't passed in private data for, we must
+ * now map this buffer too.
+ */
+ if (buffer_unwritten(bh) && !bh->b_end_io) {
+ ASSERT(tmp->iomap_flags & IOMAP_UNWRITTEN);
+ xfs_map_unwritten(inode, page, head, bh,
+ offset, bbits, tmp, startio, all_bh);
+ } else if (! (buffer_unwritten(bh) && buffer_locked(bh))) {
+ xfs_map_at_offset(page, bh, offset, bbits, tmp);
+ if (buffer_unwritten(bh)) {
+ set_buffer_unwritten_io(bh);
+ bh->b_private = private;
+ ASSERT(private);
+ }
+ }
+ if (startio) {
+ bh_arr[index++] = bh;
+ } else {
+ unlock_buffer(bh);
+ mark_buffer_dirty(bh);
+ }
+ } while (i++, (bh = bh->b_this_page) != head);
+
+ if (startio) {
+ xfs_submit_page(page, bh_arr, index);
+ } else {
+ unlock_page(page);
+ }
+}
+
+/*
+ * Convert & write out a cluster of pages in the same extent as defined
+ * by mp and following the start page.
+ */
+STATIC void
+xfs_cluster_write(
+ struct inode *inode,
+ pgoff_t tindex,
+ xfs_iomap_t *iomapp,
+ int startio,
+ int all_bh,
+ pgoff_t tlast)
+{
+ struct page *page;
+
+ for (; tindex <= tlast; tindex++) {
+ page = xfs_probe_delalloc_page(inode, tindex);
+ if (!page)
+ break;
+ xfs_convert_page(inode, page, iomapp, NULL, startio, all_bh);
+ }
+}
+
+/*
+ * Calling this without startio set means we are being asked to make a dirty
+ * page ready for freeing it's buffers. When called with startio set then
+ * we are coming from writepage.
+ *
+ * When called with startio set it is important that we write the WHOLE
+ * page if possible.
+ * The bh->b_state's cannot know if any of the blocks or which block for
+ * that matter are dirty due to mmap writes, and therefore bh uptodate is
+ * only vaild if the page itself isn't completely uptodate. Some layers
+ * may clear the page dirty flag prior to calling write page, under the
+ * assumption the entire page will be written out; by not writing out the
+ * whole page the page can be reused before all valid dirty data is
+ * written out. Note: in the case of a page that has been dirty'd by
+ * mapwrite and but partially setup by block_prepare_write the
+ * bh->b_states's will not agree and only ones setup by BPW/BCW will have
+ * valid state, thus the whole page must be written out thing.
+ */
+
+STATIC int
+xfs_page_state_convert(
+ struct inode *inode,
+ struct page *page,
+ int startio,
+ int unmapped) /* also implies page uptodate */
+{
+ struct buffer_head *bh_arr[MAX_BUF_PER_PAGE], *bh, *head;
+ xfs_iomap_t *iomp, iomap;
+ loff_t offset;
+ unsigned long p_offset = 0;
+ __uint64_t end_offset;
+ pgoff_t end_index, last_index, tlast;
+ int len, err, i, cnt = 0, uptodate = 1;
+ int flags = startio ? 0 : BMAPI_TRYLOCK;
+ int page_dirty = 1;
+ int delalloc = 0;
+
+
+ /* Are we off the end of the file ? */
+ offset = i_size_read(inode);
+ end_index = offset >> PAGE_CACHE_SHIFT;
+ last_index = (offset - 1) >> PAGE_CACHE_SHIFT;
+ if (page->index >= end_index) {
+ if ((page->index >= end_index + 1) ||
+ !(i_size_read(inode) & (PAGE_CACHE_SIZE - 1))) {
+ err = -EIO;
+ goto error;
+ }
+ }
+
+ offset = (loff_t)page->index << PAGE_CACHE_SHIFT;
+ end_offset = min_t(unsigned long long,
+ offset + PAGE_CACHE_SIZE, i_size_read(inode));
+
+ bh = head = page_buffers(page);
+ iomp = NULL;
+
+ len = bh->b_size;
+ do {
+ if (offset >= end_offset)
+ break;
+ if (!buffer_uptodate(bh))
+ uptodate = 0;
+ if (!(PageUptodate(page) || buffer_uptodate(bh)) && !startio)
+ continue;
+
+ if (iomp) {
+ iomp = xfs_offset_to_map(page, &iomap, p_offset);
+ }
+
+ /*
+ * First case, map an unwritten extent and prepare for
+ * extent state conversion transaction on completion.
+ */
+ if (buffer_unwritten(bh)) {
+ if (!startio)
+ continue;
+ if (!iomp) {
+ err = xfs_map_blocks(inode, offset, len, &iomap,
+ BMAPI_READ|BMAPI_IGNSTATE);
+ if (err) {
+ goto error;
+ }
+ iomp = xfs_offset_to_map(page, &iomap,
+ p_offset);
+ }
+ if (iomp) {
+ if (!bh->b_end_io) {
+ err = xfs_map_unwritten(inode, page,
+ head, bh, p_offset,
+ inode->i_blkbits, iomp,
+ startio, unmapped);
+ if (err) {
+ goto error;
+ }
+ } else {
+ set_bit(BH_Lock, &bh->b_state);
+ }
+ BUG_ON(!buffer_locked(bh));
+ bh_arr[cnt++] = bh;
+ page_dirty = 0;
+ }
+ /*
+ * Second case, allocate space for a delalloc buffer.
+ * We can return EAGAIN here in the release page case.
+ */
+ } else if (buffer_delay(bh)) {
+ if (!iomp) {
+ delalloc = 1;
+ err = xfs_map_blocks(inode, offset, len, &iomap,
+ BMAPI_ALLOCATE | flags);
+ if (err) {
+ goto error;
+ }
+ iomp = xfs_offset_to_map(page, &iomap,
+ p_offset);
+ }
+ if (iomp) {
+ xfs_map_at_offset(page, bh, p_offset,
+ inode->i_blkbits, iomp);
+ if (startio) {
+ bh_arr[cnt++] = bh;
+ } else {
+ unlock_buffer(bh);
+ mark_buffer_dirty(bh);
+ }
+ page_dirty = 0;
+ }
+ } else if ((buffer_uptodate(bh) || PageUptodate(page)) &&
+ (unmapped || startio)) {
+
+ if (!buffer_mapped(bh)) {
+ int size;
+
+ /*
+ * Getting here implies an unmapped buffer
+ * was found, and we are in a path where we
+ * need to write the whole page out.
+ */
+ if (!iomp) {
+ size = xfs_probe_unmapped_cluster(
+ inode, page, bh, head);
+ err = xfs_map_blocks(inode, offset,
+ size, &iomap,
+ BMAPI_WRITE|BMAPI_MMAP);
+ if (err) {
+ goto error;
+ }
+ iomp = xfs_offset_to_map(page, &iomap,
+ p_offset);
+ }
+ if (iomp) {
+ xfs_map_at_offset(page,
+ bh, p_offset,
+ inode->i_blkbits, iomp);
+ if (startio) {
+ bh_arr[cnt++] = bh;
+ } else {
+ unlock_buffer(bh);
+ mark_buffer_dirty(bh);
+ }
+ page_dirty = 0;
+ }
+ } else if (startio) {
+ if (buffer_uptodate(bh) &&
+ !test_and_set_bit(BH_Lock, &bh->b_state)) {
+ bh_arr[cnt++] = bh;
+ page_dirty = 0;
+ }
+ }
+ }
+ } while (offset += len, p_offset += len,
+ ((bh = bh->b_this_page) != head));
+
+ if (uptodate && bh == head)
+ SetPageUptodate(page);
+
+ if (startio)
+ xfs_submit_page(page, bh_arr, cnt);
+
+ if (iomp) {
+ tlast = (iomp->iomap_offset + iomp->iomap_bsize - 1) >>
+ PAGE_CACHE_SHIFT;
+ if (delalloc && (tlast > last_index))
+ tlast = last_index;
+ xfs_cluster_write(inode, page->index + 1, iomp,
+ startio, unmapped, tlast);
+ }
+
+ return page_dirty;
+
+error:
+ for (i = 0; i < cnt; i++) {
+ unlock_buffer(bh_arr[i]);
+ }
+
+ /*
+ * If it's delalloc and we have nowhere to put it,
+ * throw it away, unless the lower layers told
+ * us to try again.
+ */
+ if (err != -EAGAIN) {
+ if (!unmapped) {
+ block_flushpage(page, 0);
+ }
+ ClearPageUptodate(page);
+ }
+ return err;
+}
+
+STATIC int
+linvfs_get_block_core(
+ struct inode *inode,
+ long iblock,
+ struct buffer_head *bh_result,
+ int create,
+ int direct,
+ bmapi_flags_t flags)
+{
+ vnode_t *vp = LINVFS_GET_VP(inode);
+ xfs_iomap_t iomap;
+ int retpbbm = 1;
+ int error;
+ ssize_t size;
+ loff_t offset = (loff_t)iblock << inode->i_blkbits;
+
+ size = 1 << inode->i_blkbits;
+ VOP_BMAP(vp, offset, size,
+ create ? flags : BMAPI_READ, &iomap, &retpbbm, error);
+ if (error)
+ return -error;
+
+ if (retpbbm == 0)
+ return 0;
+
+ if (iomap.iomap_bn != IOMAP_DADDR_NULL) {
+ xfs_daddr_t bn;
+ loff_t delta;
+
+ /* For unwritten extents do not report a disk address on
+ * the read case (treat as if we're reading into a hole).
+ */
+ if (create || !(iomap.iomap_flags & IOMAP_UNWRITTEN)) {
+ delta = offset - iomap.iomap_offset;
+ delta >>= inode->i_blkbits;
+
+ bn = iomap.iomap_bn >> (inode->i_blkbits - BBSHIFT);
+ bn += delta;
+ BUG_ON(!bn && !(iomap.iomap_flags & IOMAP_REALTIME));
+ bh_result->b_blocknr = bn;
+ set_buffer_mapped(bh_result);
+ }
+ if (create && (iomap.iomap_flags & IOMAP_UNWRITTEN)) {
+ set_buffer_unwritten(bh_result);
+ set_buffer_delay(bh_result);
+ }
+ }
+
+ /* If this is a realtime file, data might be on a new device */
+ bh_result->b_dev = iomap.iomap_target->pbr_kdev;
+
+ /* If we previously allocated a block out beyond eof and
+ * we are now coming back to use it then we will need to
+ * flag it as new even if it has a disk address.
+ */
+ if (create &&
+ ((!buffer_mapped(bh_result) && !buffer_uptodate(bh_result)) ||
+ (offset >= i_size_read(inode)))) {
+ set_buffer_new(bh_result);
+ }
+
+ if (iomap.iomap_flags & IOMAP_DELAY) {
+ BUG_ON(direct);
+ if (create) {
+ set_buffer_mapped(bh_result);
+ set_buffer_uptodate(bh_result);
+ }
+ set_buffer_delay(bh_result);
+ }
+
+ return 0;
+}
+
+int
+linvfs_get_block(
+ struct inode *inode,
+ long iblock,
+ struct buffer_head *bh_result,
+ int create)
+{
+ return linvfs_get_block_core(inode, iblock, bh_result,
+ create, 0, BMAPI_WRITE);
+}
+
+STATIC int
+linvfs_get_block_direct(
+ struct inode *inode,
+ long iblock,
+ struct buffer_head *bh_result,
+ int create)
+{
+ return linvfs_get_block_core(inode, iblock, bh_result,
+ create, 1, BMAPI_WRITE|BMAPI_DIRECT);
+}
+
+STATIC int
+linvfs_bmap(
+ struct address_space *mapping,
+ long block)
+{
+ struct inode *inode = (struct inode *)mapping->host;
+ vnode_t *vp = LINVFS_GET_VP(inode);
+ int error;
+
+ vn_trace_entry(vp, "linvfs_bmap", (inst_t *)__return_address);
+
+ VOP_RWLOCK(vp, VRWLOCK_READ);
+ VOP_FLUSH_PAGES(vp, (xfs_off_t)0, -1, 0, FI_REMAPF, error);
+ VOP_RWUNLOCK(vp, VRWLOCK_READ);
+ return generic_block_bmap(mapping, block, linvfs_get_block_direct);
+}
+
+STATIC int
+linvfs_readpage(
+ struct file *unused,
+ struct page *page)
+{
+ return block_read_full_page(page, linvfs_get_block);
+}
+
+STATIC void
+xfs_count_page_state(
+ struct page *page,
+ int *delalloc,
+ int *unmapped,
+ int *unwritten)
+{
+ struct buffer_head *bh, *head;
+
+ *delalloc = *unmapped = *unwritten = 0;
+
+ bh = head = page_buffers(page);
+ do {
+ if (buffer_uptodate(bh) && !buffer_mapped(bh))
+ (*unmapped) = 1;
+ else if (buffer_unwritten(bh) && !buffer_delay(bh))
+ clear_buffer_unwritten(bh);
+ else if (buffer_unwritten(bh))
+ (*unwritten) = 1;
+ else if (buffer_delay(bh))
+ (*delalloc) = 1;
+ } while ((bh = bh->b_this_page) != head);
+}
+
+
+/*
+ * writepage: Called from one of two places:
+ *
+ * 1. we are flushing a delalloc buffer head.
+ *
+ * 2. we are writing out a dirty page. Typically the page dirty
+ * state is cleared before we get here. In this case is it
+ * conceivable we have no buffer heads.
+ *
+ * For delalloc space on the page we need to allocate space and
+ * flush it. For unmapped buffer heads on the page we should
+ * allocate space if the page is uptodate. For any other dirty
+ * buffer heads on the page we should flush them.
+ *
+ * If we detect that a transaction would be required to flush
+ * the page, we have to check the process flags first, if we
+ * are already in a transaction or disk I/O during allocations
+ * is off, we need to fail the writepage and redirty the page.
+ */
+
+STATIC int
+linvfs_writepage(
+ struct page *page)
+{
+ int error;
+ int need_trans;
+ int delalloc, unmapped, unwritten;
+ struct inode *inode = page->mapping->host;
+
+ xfs_page_trace(XFS_WRITEPAGE_ENTER, inode, page, 0);
+
+ /*
+ * We need a transaction if:
+ * 1. There are delalloc buffers on the page
+ * 2. The page is uptodate and we have unmapped buffers
+ * 3. The page is uptodate and we have no buffers
+ * 4. There are unwritten buffers on the page
+ */
+
+ if (!page_has_buffers(page)) {
+ unmapped = 1;
+ need_trans = 1;
+ } else {
+ xfs_count_page_state(page, &delalloc, &unmapped, &unwritten);
+ if (!PageUptodate(page))
+ unmapped = 0;
+ need_trans = delalloc + unmapped + unwritten;
+ }
+
+ /*
+ * If we need a transaction and the process flags say
+ * we are already in a transaction, or no IO is allowed
+ * then mark the page dirty again and leave the page
+ * as is.
+ */
+
+ if ((PFLAGS_TEST_FSTRANS() || PFLAGS_TEST_NOIO()) && need_trans)
+ goto out_fail;
+
+ /*
+ * Delay hooking up buffer heads until we have
+ * made our go/no-go decision.
+ */
+ if (!page_has_buffers(page))
+ create_empty_buffers(page, inode->i_dev, 1 << inode->i_blkbits);
+
+ /*
+ * Convert delayed allocate, unwritten or unmapped space
+ * to real space and flush out to disk.
+ */
+ if (need_trans)
+ PFLAGS_SET_NOIO();
+ error = xfs_page_state_convert(inode, page, 1, unmapped);
+ if (need_trans)
+ PFLAGS_CLEAR_NOIO();
+ if (error == -EAGAIN)
+ goto out_fail;
+
+ if (unlikely(error < 0)) {
+ unlock_page(page);
+ return error;
+ }
+
+ return 0;
+
+out_fail:
+ SetPageDirty(page);
+ unlock_page(page);
+ return 0;
+}
+
+/*
+ * Called to move a page into cleanable state - and from there
+ * to be released. Possibly the page is already clean. We always
+ * have buffer heads in this call.
+ *
+ * Returns 0 if the page is ok to release, 1 otherwise.
+ *
+ * Possible scenarios are:
+ *
+ * 1. We are being called to release a page which has been written
+ * to via regular I/O. buffer heads will be dirty and possibly
+ * delalloc. If no delalloc buffer heads in this case then we
+ * can just return zero.
+ *
+ * 2. We are called to release a page which has been written via
+ * mmap, all we need to do is ensure there is no delalloc
+ * state in the buffer heads, if not we can let the caller
+ * free them and we should come back later via writepage.
+ */
+STATIC int
+linvfs_release_page(
+ struct page *page,
+ int gfp_mask)
+{
+ struct inode *inode = page->mapping->host;
+ int dirty, delalloc, unmapped, unwritten;
+
+ xfs_page_trace(XFS_RELEASEPAGE_ENTER, inode, page, gfp_mask);
+
+ xfs_count_page_state(page, &delalloc, &unmapped, &unwritten);
+ if (!delalloc && !unwritten)
+ return 1;
+
+ if (!(gfp_mask & __GFP_FS))
+ return 0;
+
+ /* If we are already inside a transaction or the thread cannot
+ * do I/O, we cannot release this page.
+ */
+ if (PFLAGS_TEST_FSTRANS() || PFLAGS_TEST_NOIO())
+ return 0;
+
+ /*
+ * Convert delalloc space to real space, do not flush the
+ * data out to disk, that will be done by the caller.
+ * Never need to allocate space here - we will always
+ * come back to writepage in that case.
+ */
+ dirty = xfs_page_state_convert(inode, page, 0, 0);
+ return (dirty == 0 && !unwritten) ? 1 : 0;
+}
+
+STATIC int
+linvfs_prepare_write(
+ struct file *file,
+ struct page *page,
+ unsigned int from,
+ unsigned int to)
+{
+ return block_prepare_write(page, from, to, linvfs_get_block);
+}
+
+/*
+ * Initiate I/O on a kiobuf of user memory
+ */
+STATIC int
+linvfs_direct_IO(
+ int rw,
+ struct inode *inode,
+ struct kiobuf *iobuf,
+ unsigned long blocknr,
+ int blocksize)
+{
+ struct page **maplist;
+ size_t page_offset;
+ xfs_buf_t *pb;
+ xfs_iomap_t iomap;
+ int niomap, error = 0;
+ int pb_flags, map_flags, pg_index = 0;
+ size_t length, total;
+ loff_t offset, map_size;
+ size_t size;
+ vnode_t *vp = LINVFS_GET_VP(inode);
+
+ /* Note - although the iomap could have a 64-bit size,
+ * kiobuf->length is only an int, so the min(map_size, length)
+ * test will keep us from overflowing the pagebuf size_t size.
+ */
+ total = length = iobuf->length;
+ offset = blocknr;
+ offset <<= inode->i_blkbits;
+
+ maplist = iobuf->maplist;
+ page_offset = iobuf->offset;
+
+ map_flags = (rw ? BMAPI_WRITE : BMAPI_READ) | BMAPI_DIRECT;
+ pb_flags = (rw ? PBF_WRITE : PBF_READ) | PBF_FORCEIO | PBF_DIRECTIO;
+ while (length) {
+ niomap = 1;
+ VOP_BMAP(vp, offset, length, map_flags, &iomap, &niomap, error);
+ if (unlikely(!error && niomap &&
+ (iomap.iomap_flags & IOMAP_DELAY))) {
+#ifdef DEBUG
+ printk(
+ "XFS: %s - direct IO (%lld:%ld) into a delayed allocate extent?\n",
+ __FUNCTION__, (long long)offset, (long)length);
+ xfs_stack_trace();
+#endif
+ VOP_BMAP(vp, offset, length, BMAPI_ALLOCATE,
+ &iomap, &niomap, error);
+ }
+ if (error)
+ break;
+
+ if (rw == WRITE)
+ VMODIFY(vp);
+
+ BUG_ON(niomap != 1);
+ BUG_ON(iomap.iomap_flags & IOMAP_DELAY);
+
+ map_size = iomap.iomap_bsize - iomap.iomap_delta;
+ size = (size_t)min(map_size, (loff_t)length);
+
+ if ((iomap.iomap_flags & IOMAP_HOLE) ||
+ ((iomap.iomap_flags & IOMAP_UNWRITTEN) && rw == READ)) {
+ size_t zero_len = size;
+
+ if (rw == WRITE)
+ break;
+
+ /* Need to zero it all */
+ while (zero_len) {
+ struct page *page;
+ size_t pg_len;
+
+ pg_len = min((size_t)
+ (PAGE_CACHE_SIZE - page_offset),
+ zero_len);
+
+ page = maplist[pg_index];
+
+ memset(kmap(page) + page_offset, 0, pg_len);
+ flush_dcache_page(page);
+ kunmap(page);
+
+ zero_len -= pg_len;
+ if ((pg_len + page_offset) == PAGE_CACHE_SIZE) {
+ pg_index++;
+ page_offset = 0;
+ } else {
+ page_offset = (page_offset + pg_len) &
+ ~PAGE_CACHE_MASK;
+ }
+ }
+ } else {
+ int pg_count;
+
+ pg_count = (size + page_offset + PAGE_CACHE_SIZE - 1)
+ >> PAGE_CACHE_SHIFT;
+ if ((pb = pagebuf_lookup(iomap.iomap_target, offset,
+ size, pb_flags)) == NULL) {
+ error = ENOMEM;
+ break;
+ }
+ /* Need to hook up pagebuf to kiobuf pages */
+ pb->pb_pages = &maplist[pg_index];
+ pb->pb_offset = page_offset;
+ pb->pb_page_count = pg_count;
+ pb->pb_bn = iomap.iomap_bn + (iomap.iomap_delta >> BBSHIFT);
+
+ error = pagebuf_iostart(pb, pb_flags);
+ if (!error && (iomap.iomap_flags & IOMAP_UNWRITTEN)) {
+ VOP_BMAP(vp, XFS_BUF_OFFSET(pb),
+ XFS_BUF_SIZE(pb),
+ BMAPI_UNWRITTEN, NULL, NULL, error);
+ }
+
+ pagebuf_rele(pb);
+
+ if (error)
+ break;
+
+ page_offset = (page_offset + size) & ~PAGE_CACHE_MASK;
+ if (page_offset)
+ pg_count--;
+ pg_index += pg_count;
+ }
+
+ offset += size;
+ length -= size;
+ }
+
+ if (error)
+ return -error;
+ return (int)(total - length);
+}
+
+
+struct address_space_operations linvfs_aops = {
+ .readpage = linvfs_readpage,
+ .writepage = linvfs_writepage,
+ .sync_page = block_sync_page,
+ .releasepage = linvfs_release_page,
+ .prepare_write = linvfs_prepare_write,
+ .commit_write = generic_commit_write,
+ .bmap = linvfs_bmap,
+ .direct_IO = linvfs_direct_IO,
+};
diff --git a/fs/xfs/linux-2.4/xfs_buf.c b/fs/xfs/linux-2.4/xfs_buf.c
new file mode 100644
index 00000000000000..1c26c3cc8659b0
--- /dev/null
+++ b/fs/xfs/linux-2.4/xfs_buf.c
@@ -0,0 +1,2368 @@
+/*
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+/*
+ * The xfs_buf.c code provides an abstract buffer cache model on top
+ * of the Linux page cache. Cached metadata blocks for a file system
+ * are hashed to the inode for the block device. xfs_buf.c assembles
+ * buffers (xfs_buf_t) on demand to aggregate such cached pages for I/O.
+ *
+ * Written by Steve Lord, Jim Mostek, Russell Cattelan
+ * and Rajagopal Ananthanarayanan ("ananth") at SGI.
+ *
+ */
+
+#include <linux/stddef.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <linux/blkdev.h>
+#include <linux/locks.h>
+#include <linux/sysctl.h>
+#include <linux/proc_fs.h>
+
+#include "xfs_linux.h"
+
+#define BN_ALIGN_MASK ((1 << (PAGE_CACHE_SHIFT - BBSHIFT)) - 1)
+
+#ifndef GFP_READAHEAD
+#define GFP_READAHEAD 0
+#endif
+
+/*
+ * A backport of the 2.5 scheduler is used by many vendors of 2.4-based
+ * distributions.
+ * We can only guess it's presences by the lack of the SCHED_YIELD flag.
+ * If the heuristic doesn't work, change this define by hand.
+ */
+#ifndef SCHED_YIELD
+#define __HAVE_NEW_SCHEDULER 1
+#endif
+
+/*
+ * cpumask_t is used for supporting NR_CPUS > BITS_PER_LONG.
+ * If support for this is present, migrate_to_cpu exists and provides
+ * a wrapper around the set_cpus_allowed routine.
+ */
+#ifdef copy_cpumask
+#define __HAVE_CPUMASK_T 1
+#endif
+
+#ifndef __HAVE_CPUMASK_T
+# ifndef __HAVE_NEW_SCHEDULER
+# define migrate_to_cpu(cpu) \
+ do { current->cpus_allowed = 1UL << (cpu); } while (0)
+# else
+# define migrate_to_cpu(cpu) \
+ set_cpus_allowed(current, 1UL << (cpu))
+# endif
+#endif
+
+#ifndef VM_MAP
+#define VM_MAP VM_ALLOC
+#endif
+
+/*
+ * File wide globals
+ */
+
+STATIC kmem_cache_t *pagebuf_cache;
+STATIC kmem_shaker_t pagebuf_shake;
+
+#define MAX_IO_DAEMONS NR_CPUS
+#define CPU_TO_DAEMON(cpu) (cpu)
+STATIC int pb_logio_daemons[MAX_IO_DAEMONS];
+STATIC struct list_head pagebuf_logiodone_tq[MAX_IO_DAEMONS];
+STATIC wait_queue_head_t pagebuf_logiodone_wait[MAX_IO_DAEMONS];
+STATIC int pb_dataio_daemons[MAX_IO_DAEMONS];
+STATIC struct list_head pagebuf_dataiodone_tq[MAX_IO_DAEMONS];
+STATIC wait_queue_head_t pagebuf_dataiodone_wait[MAX_IO_DAEMONS];
+
+/*
+ * For pre-allocated buffer head pool
+ */
+
+#define NR_RESERVED_BH 64
+static wait_queue_head_t pb_resv_bh_wait;
+static spinlock_t pb_resv_bh_lock = SPIN_LOCK_UNLOCKED;
+struct buffer_head *pb_resv_bh = NULL; /* list of bh */
+int pb_resv_bh_cnt = 0; /* # of bh available */
+
+STATIC void _pagebuf_ioapply(xfs_buf_t *);
+STATIC int pagebuf_daemon_wakeup(int, unsigned int);
+STATIC void pagebuf_delwri_queue(xfs_buf_t *, int);
+STATIC void pagebuf_runall_queues(struct list_head[]);
+
+/*
+ * Pagebuf debugging
+ */
+
+#ifdef PAGEBUF_TRACE
+void
+pagebuf_trace(
+ xfs_buf_t *pb,
+ char *id,
+ void *data,
+ void *ra)
+{
+ ktrace_enter(pagebuf_trace_buf,
+ pb, id,
+ (void *)(unsigned long)pb->pb_flags,
+ (void *)(unsigned long)pb->pb_hold.counter,
+ (void *)(unsigned long)pb->pb_sema.count.counter,
+ (void *)current,
+ data, ra,
+ (void *)(unsigned long)((pb->pb_file_offset>>32) & 0xffffffff),
+ (void *)(unsigned long)(pb->pb_file_offset & 0xffffffff),
+ (void *)(unsigned long)pb->pb_buffer_length,
+ NULL, NULL, NULL, NULL, NULL);
+}
+ktrace_t *pagebuf_trace_buf;
+#define PAGEBUF_TRACE_SIZE 4096
+#define PB_TRACE(pb, id, data) \
+ pagebuf_trace(pb, id, (void *)data, (void *)__builtin_return_address(0))
+#else
+#define PB_TRACE(pb, id, data) do { } while (0)
+#endif
+
+#ifdef PAGEBUF_LOCK_TRACKING
+# define PB_SET_OWNER(pb) ((pb)->pb_last_holder = current->pid)
+# define PB_CLEAR_OWNER(pb) ((pb)->pb_last_holder = -1)
+# define PB_GET_OWNER(pb) ((pb)->pb_last_holder)
+#else
+# define PB_SET_OWNER(pb) do { } while (0)
+# define PB_CLEAR_OWNER(pb) do { } while (0)
+# define PB_GET_OWNER(pb) do { } while (0)
+#endif
+
+/*
+ * Pagebuf allocation / freeing.
+ */
+
+#define pb_to_gfp(flags) \
+ (((flags) & PBF_READ_AHEAD) ? GFP_READAHEAD : \
+ ((flags) & PBF_DONT_BLOCK) ? GFP_NOFS : GFP_KERNEL)
+
+#define pb_to_km(flags) \
+ (((flags) & PBF_DONT_BLOCK) ? KM_NOFS : KM_SLEEP)
+
+
+#define pagebuf_allocate(flags) \
+ kmem_zone_alloc(pagebuf_cache, pb_to_km(flags))
+#define pagebuf_deallocate(pb) \
+ kmem_zone_free(pagebuf_cache, (pb));
+
+/*
+ * Pagebuf hashing
+ */
+
+#define NBITS 8
+#define NHASH (1<<NBITS)
+
+typedef struct {
+ struct list_head pb_hash;
+ spinlock_t pb_hash_lock;
+} pb_hash_t;
+
+STATIC pb_hash_t pbhash[NHASH];
+#define pb_hash(pb) &pbhash[pb->pb_hash_index]
+
+STATIC int
+_bhash(
+ struct block_device *bdev,
+ loff_t base)
+{
+ int bit, hval;
+
+ base >>= 9;
+ base ^= (unsigned long)bdev / L1_CACHE_BYTES;
+ for (bit = hval = 0; base && bit < sizeof(base) * 8; bit += NBITS) {
+ hval ^= (int)base & (NHASH-1);
+ base >>= NBITS;
+ }
+ return hval;
+}
+
+/*
+ * Mapping of multi-page buffers into contiguous virtual space
+ */
+
+typedef struct a_list {
+ void *vm_addr;
+ struct a_list *next;
+} a_list_t;
+
+STATIC a_list_t *as_free_head;
+STATIC int as_list_len;
+STATIC spinlock_t as_lock = SPIN_LOCK_UNLOCKED;
+
+/*
+ * Try to batch vunmaps because they are costly.
+ */
+STATIC void
+free_address(
+ void *addr)
+{
+ a_list_t *aentry;
+
+ aentry = kmalloc(sizeof(a_list_t), GFP_ATOMIC);
+ if (aentry) {
+ spin_lock(&as_lock);
+ aentry->next = as_free_head;
+ aentry->vm_addr = addr;
+ as_free_head = aentry;
+ as_list_len++;
+ spin_unlock(&as_lock);
+ } else {
+ vunmap(addr);
+ }
+}
+
+STATIC void
+purge_addresses(void)
+{
+ a_list_t *aentry, *old;
+
+ if (as_free_head == NULL)
+ return;
+
+ spin_lock(&as_lock);
+ aentry = as_free_head;
+ as_free_head = NULL;
+ as_list_len = 0;
+ spin_unlock(&as_lock);
+
+ while ((old = aentry) != NULL) {
+ vunmap(aentry->vm_addr);
+ aentry = aentry->next;
+ kfree(old);
+ }
+}
+
+/*
+ * Internal pagebuf object manipulation
+ */
+
+STATIC void
+_pagebuf_initialize(
+ xfs_buf_t *pb,
+ xfs_buftarg_t *target,
+ loff_t range_base,
+ size_t range_length,
+ page_buf_flags_t flags)
+{
+ /*
+ * We don't want certain flags to appear in pb->pb_flags.
+ */
+ flags &= ~(PBF_LOCK|PBF_MAPPED|PBF_DONT_BLOCK|PBF_READ_AHEAD);
+
+ memset(pb, 0, sizeof(xfs_buf_t));
+ atomic_set(&pb->pb_hold, 1);
+ init_MUTEX_LOCKED(&pb->pb_iodonesema);
+ INIT_LIST_HEAD(&pb->pb_list);
+ INIT_LIST_HEAD(&pb->pb_hash_list);
+ init_MUTEX_LOCKED(&pb->pb_sema); /* held, no waiters */
+ PB_SET_OWNER(pb);
+ pb->pb_target = target;
+ pb->pb_file_offset = range_base;
+ /*
+ * Set buffer_length and count_desired to the same value initially.
+ * I/O routines should use count_desired, which will be the same in
+ * most cases but may be reset (e.g. XFS recovery).
+ */
+ pb->pb_buffer_length = pb->pb_count_desired = range_length;
+ pb->pb_flags = flags | PBF_NONE;
+ pb->pb_bn = XFS_BUF_DADDR_NULL;
+ atomic_set(&pb->pb_pin_count, 0);
+ init_waitqueue_head(&pb->pb_waiters);
+
+ XFS_STATS_INC(pb_create);
+ PB_TRACE(pb, "initialize", target);
+}
+
+/*
+ * Allocate a page array capable of holding a specified number
+ * of pages, and point the page buf at it.
+ */
+STATIC int
+_pagebuf_get_pages(
+ xfs_buf_t *pb,
+ int page_count,
+ page_buf_flags_t flags)
+{
+ /* Make sure that we have a page list */
+ if (pb->pb_pages == NULL) {
+ pb->pb_offset = page_buf_poff(pb->pb_file_offset);
+ pb->pb_page_count = page_count;
+ if (page_count <= PB_PAGES) {
+ pb->pb_pages = pb->pb_page_array;
+ } else {
+ pb->pb_pages = kmem_alloc(sizeof(struct page *) *
+ page_count, pb_to_km(flags));
+ if (pb->pb_pages == NULL)
+ return -ENOMEM;
+ }
+ memset(pb->pb_pages, 0, sizeof(struct page *) * page_count);
+ }
+ return 0;
+}
+
+/*
+ * Frees pb_pages if it was malloced.
+ */
+STATIC void
+_pagebuf_free_pages(
+ xfs_buf_t *bp)
+{
+ if (bp->pb_pages != bp->pb_page_array) {
+ kmem_free(bp->pb_pages,
+ bp->pb_page_count * sizeof(struct page *));
+ }
+}
+
+/*
+ * Releases the specified buffer.
+ *
+ * The modification state of any associated pages is left unchanged.
+ * The buffer most not be on any hash - use pagebuf_rele instead for
+ * hashed and refcounted buffers
+ */
+void
+pagebuf_free(
+ xfs_buf_t *bp)
+{
+ PB_TRACE(bp, "free", 0);
+
+ ASSERT(list_empty(&bp->pb_hash_list));
+
+ if (bp->pb_flags & _PBF_PAGE_CACHE) {
+ uint i;
+
+ if ((bp->pb_flags & PBF_MAPPED) && (bp->pb_page_count > 1))
+ free_address(bp->pb_addr - bp->pb_offset);
+
+ for (i = 0; i < bp->pb_page_count; i++)
+ page_cache_release(bp->pb_pages[i]);
+ _pagebuf_free_pages(bp);
+ } else if (bp->pb_flags & _PBF_KMEM_ALLOC) {
+ /*
+ * XXX(hch): bp->pb_count_desired might be incorrect (see
+ * pagebuf_associate_memory for details), but fortunately
+ * the Linux version of kmem_free ignores the len argument..
+ */
+ kmem_free(bp->pb_addr, bp->pb_count_desired);
+ _pagebuf_free_pages(bp);
+ }
+
+ pagebuf_deallocate(bp);
+}
+
+/*
+ * Finds all pages for buffer in question and builds it's page list.
+ */
+STATIC int
+_pagebuf_lookup_pages(
+ xfs_buf_t *bp,
+ uint flags)
+{
+ struct address_space *mapping = bp->pb_target->pbr_mapping;
+ size_t blocksize = bp->pb_target->pbr_bsize;
+ int gfp_mask = pb_to_gfp(flags);
+ unsigned short page_count, i;
+ pgoff_t first;
+ loff_t end;
+ int error;
+
+ end = bp->pb_file_offset + bp->pb_buffer_length;
+ page_count = page_buf_btoc(end) - page_buf_btoct(bp->pb_file_offset);
+
+ error = _pagebuf_get_pages(bp, page_count, flags);
+ if (unlikely(error))
+ return error;
+ bp->pb_flags |= _PBF_PAGE_CACHE;
+
+ first = bp->pb_file_offset >> PAGE_CACHE_SHIFT;
+
+ for (i = 0; i < bp->pb_page_count; i++) {
+ struct page *page;
+ uint retries = 0;
+
+ retry:
+ page = find_or_create_page(mapping, first + i, gfp_mask);
+ if (unlikely(page == NULL)) {
+ if (flags & PBF_READ_AHEAD) {
+ bp->pb_page_count = i;
+ for (i = 0; i < bp->pb_page_count; i++)
+ unlock_page(bp->pb_pages[i]);
+ return -ENOMEM;
+ }
+
+ /*
+ * This could deadlock.
+ *
+ * But until all the XFS lowlevel code is revamped to
+ * handle buffer allocation failures we can't do much.
+ */
+ if (!(++retries % 100))
+ printk(KERN_ERR
+ "possible deadlock in %s (mode:0x%x)\n",
+ __FUNCTION__, gfp_mask);
+
+ XFS_STATS_INC(pb_page_retries);
+ pagebuf_daemon_wakeup(0, gfp_mask);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(10);
+ goto retry;
+ }
+
+ XFS_STATS_INC(pb_page_found);
+
+ /* if we need to do I/O on a page record the fact */
+ if (!Page_Uptodate(page)) {
+ page_count--;
+ if (blocksize == PAGE_CACHE_SIZE && (flags & PBF_READ))
+ bp->pb_locked = 1;
+ }
+
+ bp->pb_pages[i] = page;
+ }
+
+ if (!bp->pb_locked) {
+ for (i = 0; i < bp->pb_page_count; i++)
+ unlock_page(bp->pb_pages[i]);
+ }
+
+ if (page_count) {
+ /* if we have any uptodate pages, mark that in the buffer */
+ bp->pb_flags &= ~PBF_NONE;
+
+ /* if some pages aren't uptodate, mark that in the buffer */
+ if (page_count != bp->pb_page_count)
+ bp->pb_flags |= PBF_PARTIAL;
+ }
+
+ PB_TRACE(bp, "lookup_pages", (long)page_count);
+ return error;
+}
+
+/*
+ * Map buffer into kernel address-space if nessecary.
+ */
+STATIC int
+_pagebuf_map_pages(
+ xfs_buf_t *bp,
+ uint flags)
+{
+ /* A single page buffer is always mappable */
+ if (bp->pb_page_count == 1) {
+ bp->pb_addr = page_address(bp->pb_pages[0]) + bp->pb_offset;
+ bp->pb_flags |= PBF_MAPPED;
+ } else if (flags & PBF_MAPPED) {
+ if (as_list_len > 64)
+ purge_addresses();
+ bp->pb_addr = vmap(bp->pb_pages, bp->pb_page_count,
+ VM_MAP, PAGE_KERNEL);
+ if (unlikely(bp->pb_addr == NULL))
+ return -ENOMEM;
+ bp->pb_addr += bp->pb_offset;
+ bp->pb_flags |= PBF_MAPPED;
+ }
+
+ return 0;
+}
+
+/*
+ * Pre-allocation of a pool of buffer heads for use in
+ * low-memory situations.
+ */
+
+/*
+ * _pagebuf_prealloc_bh
+ *
+ * Pre-allocate a pool of "count" buffer heads at startup.
+ * Puts them on a list at "pb_resv_bh"
+ * Returns number of bh actually allocated to pool.
+ */
+STATIC int
+_pagebuf_prealloc_bh(
+ int count)
+{
+ struct buffer_head *bh;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ bh = kmem_cache_alloc(bh_cachep, SLAB_KERNEL);
+ if (!bh)
+ break;
+ bh->b_pprev = &pb_resv_bh;
+ bh->b_next = pb_resv_bh;
+ pb_resv_bh = bh;
+ pb_resv_bh_cnt++;
+ }
+ return i;
+}
+
+/*
+ * _pagebuf_get_prealloc_bh
+ *
+ * Get one buffer head from our pre-allocated pool.
+ * If pool is empty, sleep 'til one comes back in.
+ * Returns aforementioned buffer head.
+ */
+STATIC struct buffer_head *
+_pagebuf_get_prealloc_bh(void)
+{
+ unsigned long flags;
+ struct buffer_head *bh;
+ DECLARE_WAITQUEUE (wait, current);
+
+ spin_lock_irqsave(&pb_resv_bh_lock, flags);
+
+ if (pb_resv_bh_cnt < 1) {
+ add_wait_queue(&pb_resv_bh_wait, &wait);
+ do {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ spin_unlock_irqrestore(&pb_resv_bh_lock, flags);
+ run_task_queue(&tq_disk);
+ schedule();
+ spin_lock_irqsave(&pb_resv_bh_lock, flags);
+ } while (pb_resv_bh_cnt < 1);
+ __set_current_state(TASK_RUNNING);
+ remove_wait_queue(&pb_resv_bh_wait, &wait);
+ }
+
+ BUG_ON(pb_resv_bh_cnt < 1);
+ BUG_ON(!pb_resv_bh);
+
+ bh = pb_resv_bh;
+ pb_resv_bh = bh->b_next;
+ pb_resv_bh_cnt--;
+
+ spin_unlock_irqrestore(&pb_resv_bh_lock, flags);
+ return bh;
+}
+
+/*
+ * _pagebuf_free_bh
+ *
+ * Take care of buffer heads that we're finished with.
+ * Call this instead of just kmem_cache_free(bh_cachep, bh)
+ * when you're done with a bh.
+ *
+ * If our pre-allocated pool is full, just free the buffer head.
+ * Otherwise, put it back in the pool, and wake up anybody
+ * waiting for one.
+ */
+STATIC inline void
+_pagebuf_free_bh(
+ struct buffer_head *bh)
+{
+ unsigned long flags;
+ int free;
+
+ if (! (free = pb_resv_bh_cnt >= NR_RESERVED_BH)) {
+ spin_lock_irqsave(&pb_resv_bh_lock, flags);
+
+ if (! (free = pb_resv_bh_cnt >= NR_RESERVED_BH)) {
+ bh->b_pprev = &pb_resv_bh;
+ bh->b_next = pb_resv_bh;
+ pb_resv_bh = bh;
+ pb_resv_bh_cnt++;
+
+ if (waitqueue_active(&pb_resv_bh_wait)) {
+ wake_up(&pb_resv_bh_wait);
+ }
+ }
+
+ spin_unlock_irqrestore(&pb_resv_bh_lock, flags);
+ }
+ if (free) {
+ kmem_cache_free(bh_cachep, bh);
+ }
+}
+
+/*
+ * Finding and Reading Buffers
+ */
+
+/*
+ * _pagebuf_find
+ *
+ * Looks up, and creates if absent, a lockable buffer for
+ * a given range of an inode. The buffer is returned
+ * locked. If other overlapping buffers exist, they are
+ * released before the new buffer is created and locked,
+ * which may imply that this call will block until those buffers
+ * are unlocked. No I/O is implied by this call.
+ */
+xfs_buf_t *
+_pagebuf_find( /* find buffer for block */
+ xfs_buftarg_t *target,/* target for block */
+ loff_t ioff, /* starting offset of range */
+ size_t isize, /* length of range */
+ page_buf_flags_t flags, /* PBF_TRYLOCK */
+ xfs_buf_t *new_pb)/* newly allocated buffer */
+{
+ loff_t range_base;
+ size_t range_length;
+ int hval;
+ pb_hash_t *h;
+ xfs_buf_t *pb, *n;
+ int not_locked;
+
+ range_base = (ioff << BBSHIFT);
+ range_length = (isize << BBSHIFT);
+
+ /* Ensure we never do IOs smaller than the sector size */
+ BUG_ON(range_length < (1 << target->pbr_sshift));
+
+ /* Ensure we never do IOs that are not sector aligned */
+ BUG_ON(range_base & (loff_t)target->pbr_smask);
+
+ hval = _bhash(target->pbr_bdev, range_base);
+ h = &pbhash[hval];
+
+ spin_lock(&h->pb_hash_lock);
+
+ list_for_each_entry_safe(pb, n, &h->pb_hash, pb_hash_list) {
+ if (pb->pb_target == target &&
+ pb->pb_file_offset == range_base &&
+ pb->pb_buffer_length == range_length) {
+ /* If we look at something bring it to the
+ * front of the list for next time
+ */
+ atomic_inc(&pb->pb_hold);
+ list_move(&pb->pb_hash_list, &h->pb_hash);
+ goto found;
+ }
+ }
+
+ /* No match found */
+ if (new_pb) {
+ _pagebuf_initialize(new_pb, target, range_base,
+ range_length, flags);
+ new_pb->pb_hash_index = hval;
+ list_add(&new_pb->pb_hash_list, &h->pb_hash);
+ } else {
+ XFS_STATS_INC(pb_miss_locked);
+ }
+
+ spin_unlock(&h->pb_hash_lock);
+ return (new_pb);
+
+found:
+ spin_unlock(&h->pb_hash_lock);
+
+ /* Attempt to get the semaphore without sleeping,
+ * if this does not work then we need to drop the
+ * spinlock and do a hard attempt on the semaphore.
+ */
+ not_locked = down_trylock(&pb->pb_sema);
+ if (not_locked) {
+ if (!(flags & PBF_TRYLOCK)) {
+ /* wait for buffer ownership */
+ PB_TRACE(pb, "get_lock", 0);
+ pagebuf_lock(pb);
+ XFS_STATS_INC(pb_get_locked_waited);
+ } else {
+ /* We asked for a trylock and failed, no need
+ * to look at file offset and length here, we
+ * know that this pagebuf at least overlaps our
+ * pagebuf and is locked, therefore our buffer
+ * either does not exist, or is this buffer
+ */
+
+ pagebuf_rele(pb);
+ XFS_STATS_INC(pb_busy_locked);
+ return (NULL);
+ }
+ } else {
+ /* trylock worked */
+ PB_SET_OWNER(pb);
+ }
+
+ if (pb->pb_flags & PBF_STALE)
+ pb->pb_flags &= PBF_MAPPED;
+ PB_TRACE(pb, "got_lock", 0);
+ XFS_STATS_INC(pb_get_locked);
+ return (pb);
+}
+
+/*
+ * xfs_buf_get_flags assembles a buffer covering the specified range.
+ *
+ * Storage in memory for all portions of the buffer will be allocated,
+ * although backing storage may not be.
+ */
+xfs_buf_t *
+xfs_buf_get_flags( /* allocate a buffer */
+ xfs_buftarg_t *target,/* target for buffer */
+ loff_t ioff, /* starting offset of range */
+ size_t isize, /* length of range */
+ page_buf_flags_t flags) /* PBF_TRYLOCK */
+{
+ xfs_buf_t *pb, *new_pb;
+ int error = 0, i;
+
+ new_pb = pagebuf_allocate(flags);
+ if (unlikely(!new_pb))
+ return NULL;
+
+ pb = _pagebuf_find(target, ioff, isize, flags, new_pb);
+ if (pb == new_pb) {
+ error = _pagebuf_lookup_pages(pb, flags);
+ if (error)
+ goto no_buffer;
+ } else {
+ pagebuf_deallocate(new_pb);
+ if (unlikely(pb == NULL))
+ return NULL;
+ }
+
+ for (i = 0; i < pb->pb_page_count; i++)
+ mark_page_accessed(pb->pb_pages[i]);
+
+ if (!(pb->pb_flags & PBF_MAPPED)) {
+ error = _pagebuf_map_pages(pb, flags);
+ if (unlikely(error)) {
+ printk(KERN_WARNING "%s: failed to map pages\n",
+ __FUNCTION__);
+ goto no_buffer;
+ }
+ }
+
+ XFS_STATS_INC(pb_get);
+
+ /*
+ * Always fill in the block number now, the mapped cases can do
+ * their own overlay of this later.
+ */
+ pb->pb_bn = ioff;
+ pb->pb_count_desired = pb->pb_buffer_length;
+
+ PB_TRACE(pb, "get", (unsigned long)flags);
+ return pb;
+
+ no_buffer:
+ if (flags & (PBF_LOCK | PBF_TRYLOCK))
+ pagebuf_unlock(pb);
+ pagebuf_rele(pb);
+ return NULL;
+}
+
+xfs_buf_t *
+xfs_buf_read_flags(
+ xfs_buftarg_t *target,
+ loff_t ioff,
+ size_t isize,
+ page_buf_flags_t flags)
+{
+ xfs_buf_t *pb;
+
+ flags |= PBF_READ;
+
+ pb = xfs_buf_get_flags(target, ioff, isize, flags);
+ if (pb) {
+ if (PBF_NOT_DONE(pb)) {
+ PB_TRACE(pb, "read", (unsigned long)flags);
+ XFS_STATS_INC(pb_get_read);
+ pagebuf_iostart(pb, flags);
+ } else if (flags & PBF_ASYNC) {
+ PB_TRACE(pb, "read_async", (unsigned long)flags);
+ /*
+ * Read ahead call which is already satisfied,
+ * drop the buffer
+ */
+ goto no_buffer;
+ } else {
+ PB_TRACE(pb, "read_done", (unsigned long)flags);
+ /* We do not want read in the flags */
+ pb->pb_flags &= ~PBF_READ;
+ }
+ }
+
+ return pb;
+
+ no_buffer:
+ if (flags & (PBF_LOCK | PBF_TRYLOCK))
+ pagebuf_unlock(pb);
+ pagebuf_rele(pb);
+ return NULL;
+}
+
+/*
+ * Create a skeletal pagebuf (no pages associated with it).
+ */
+xfs_buf_t *
+pagebuf_lookup(
+ xfs_buftarg_t *target,
+ loff_t ioff,
+ size_t isize,
+ page_buf_flags_t flags)
+{
+ xfs_buf_t *pb;
+
+ flags |= _PBF_PRIVATE_BH;
+ pb = pagebuf_allocate(flags);
+ if (pb) {
+ _pagebuf_initialize(pb, target, ioff, isize, flags);
+ }
+ return pb;
+}
+
+/*
+ * If we are not low on memory then do the readahead in a deadlock
+ * safe manner.
+ */
+void
+pagebuf_readahead(
+ xfs_buftarg_t *target,
+ loff_t ioff,
+ size_t isize,
+ page_buf_flags_t flags)
+{
+ flags |= (PBF_TRYLOCK|PBF_ASYNC|PBF_READ_AHEAD);
+ xfs_buf_read_flags(target, ioff, isize, flags);
+}
+
+xfs_buf_t *
+pagebuf_get_empty(
+ size_t len,
+ xfs_buftarg_t *target)
+{
+ xfs_buf_t *pb;
+
+ pb = pagebuf_allocate(0);
+ if (pb)
+ _pagebuf_initialize(pb, target, 0, len, 0);
+ return pb;
+}
+
+static inline struct page *
+mem_to_page(
+ void *addr)
+{
+ if (((unsigned long)addr < VMALLOC_START) ||
+ ((unsigned long)addr >= VMALLOC_END)) {
+ return virt_to_page(addr);
+ } else {
+ return vmalloc_to_page(addr);
+ }
+}
+
+int
+pagebuf_associate_memory(
+ xfs_buf_t *pb,
+ void *mem,
+ size_t len)
+{
+ int rval;
+ int i = 0;
+ size_t ptr;
+ size_t end, end_cur;
+ off_t offset;
+ int page_count;
+
+ page_count = PAGE_CACHE_ALIGN(len) >> PAGE_CACHE_SHIFT;
+ offset = (off_t) mem - ((off_t)mem & PAGE_CACHE_MASK);
+ if (offset && (len > PAGE_CACHE_SIZE))
+ page_count++;
+
+ /* Free any previous set of page pointers */
+ if (pb->pb_pages)
+ _pagebuf_free_pages(pb);
+
+ pb->pb_pages = NULL;
+ pb->pb_addr = mem;
+
+ rval = _pagebuf_get_pages(pb, page_count, 0);
+ if (rval)
+ return rval;
+
+ pb->pb_offset = offset;
+ ptr = (size_t) mem & PAGE_CACHE_MASK;
+ end = PAGE_CACHE_ALIGN((size_t) mem + len);
+ end_cur = end;
+ /* set up first page */
+ pb->pb_pages[0] = mem_to_page(mem);
+
+ ptr += PAGE_CACHE_SIZE;
+ pb->pb_page_count = ++i;
+ while (ptr < end) {
+ pb->pb_pages[i] = mem_to_page((void *)ptr);
+ pb->pb_page_count = ++i;
+ ptr += PAGE_CACHE_SIZE;
+ }
+ pb->pb_locked = 0;
+
+ pb->pb_count_desired = pb->pb_buffer_length = len;
+ pb->pb_flags |= PBF_MAPPED | _PBF_PRIVATE_BH;
+
+ return 0;
+}
+
+xfs_buf_t *
+pagebuf_get_no_daddr(
+ size_t len,
+ xfs_buftarg_t *target)
+{
+ size_t malloc_len = len;
+ xfs_buf_t *bp;
+ void *data;
+ int error;
+
+ bp = pagebuf_allocate(0);
+ if (unlikely(bp == NULL))
+ goto fail;
+ _pagebuf_initialize(bp, target, 0, len, PBF_FORCEIO);
+
+ try_again:
+ data = kmem_alloc(malloc_len, KM_SLEEP | KM_MAYFAIL);
+ if (unlikely(data == NULL))
+ goto fail_free_buf;
+
+ /* check whether alignment matches.. */
+ if ((__psunsigned_t)data !=
+ ((__psunsigned_t)data & ~target->pbr_smask)) {
+ /* .. else double the size and try again */
+ kmem_free(data, malloc_len);
+ malloc_len <<= 1;
+ goto try_again;
+ }
+
+ error = pagebuf_associate_memory(bp, data, len);
+ if (error)
+ goto fail_free_mem;
+ bp->pb_flags |= _PBF_KMEM_ALLOC;
+
+ pagebuf_unlock(bp);
+
+ PB_TRACE(bp, "no_daddr", data);
+ return bp;
+ fail_free_mem:
+ kmem_free(data, malloc_len);
+ fail_free_buf:
+ pagebuf_free(bp);
+ fail:
+ return NULL;
+}
+
+/*
+ * pagebuf_hold
+ *
+ * Increment reference count on buffer, to hold the buffer concurrently
+ * with another thread which may release (free) the buffer asynchronously.
+ *
+ * Must hold the buffer already to call this function.
+ */
+void
+pagebuf_hold(
+ xfs_buf_t *pb)
+{
+ atomic_inc(&pb->pb_hold);
+ PB_TRACE(pb, "hold", 0);
+}
+
+/*
+ * pagebuf_rele
+ *
+ * pagebuf_rele releases a hold on the specified buffer. If the
+ * the hold count is 1, pagebuf_rele calls pagebuf_free.
+ */
+void
+pagebuf_rele(
+ xfs_buf_t *pb)
+{
+ pb_hash_t *hash = pb_hash(pb);
+
+ PB_TRACE(pb, "rele", pb->pb_relse);
+
+ if (atomic_dec_and_lock(&pb->pb_hold, &hash->pb_hash_lock)) {
+ int do_free = 1;
+
+ if (pb->pb_relse) {
+ atomic_inc(&pb->pb_hold);
+ spin_unlock(&hash->pb_hash_lock);
+ (*(pb->pb_relse)) (pb);
+ spin_lock(&hash->pb_hash_lock);
+ do_free = 0;
+ }
+
+ if (pb->pb_flags & PBF_DELWRI) {
+ pb->pb_flags |= PBF_ASYNC;
+ atomic_inc(&pb->pb_hold);
+ pagebuf_delwri_queue(pb, 0);
+ do_free = 0;
+ } else if (pb->pb_flags & PBF_FS_MANAGED) {
+ do_free = 0;
+ }
+
+ if (do_free) {
+ list_del_init(&pb->pb_hash_list);
+ spin_unlock(&hash->pb_hash_lock);
+ xfs_buf_free(pb);
+ } else {
+ spin_unlock(&hash->pb_hash_lock);
+ }
+ }
+}
+
+
+/*
+ * Mutual exclusion on buffers. Locking model:
+ *
+ * Buffers associated with inodes for which buffer locking
+ * is not enabled are not protected by semaphores, and are
+ * assumed to be exclusively owned by the caller. There is a
+ * spinlock in the buffer, used by the caller when concurrent
+ * access is possible.
+ */
+
+/*
+ * pagebuf_cond_lock
+ *
+ * pagebuf_cond_lock locks a buffer object, if it is not already locked.
+ * Note that this in no way
+ * locks the underlying pages, so it is only useful for synchronizing
+ * concurrent use of page buffer objects, not for synchronizing independent
+ * access to the underlying pages.
+ */
+int
+pagebuf_cond_lock( /* lock buffer, if not locked */
+ /* returns -EBUSY if locked) */
+ xfs_buf_t *pb)
+{
+ int locked;
+
+ locked = down_trylock(&pb->pb_sema) == 0;
+ if (locked) {
+ PB_SET_OWNER(pb);
+ }
+ PB_TRACE(pb, "cond_lock", (long)locked);
+ return(locked ? 0 : -EBUSY);
+}
+
+#if defined(DEBUG) || defined(XFS_BLI_TRACE)
+/*
+ * pagebuf_lock_value
+ *
+ * Return lock value for a pagebuf
+ */
+int
+pagebuf_lock_value(
+ xfs_buf_t *pb)
+{
+ return(atomic_read(&pb->pb_sema.count));
+}
+#endif
+
+/*
+ * pagebuf_lock
+ *
+ * pagebuf_lock locks a buffer object. Note that this in no way
+ * locks the underlying pages, so it is only useful for synchronizing
+ * concurrent use of page buffer objects, not for synchronizing independent
+ * access to the underlying pages.
+ */
+int
+pagebuf_lock(
+ xfs_buf_t *pb)
+{
+ PB_TRACE(pb, "lock", 0);
+ if (atomic_read(&pb->pb_io_remaining))
+ run_task_queue(&tq_disk);
+ down(&pb->pb_sema);
+ PB_SET_OWNER(pb);
+ PB_TRACE(pb, "locked", 0);
+ return 0;
+}
+
+/*
+ * pagebuf_unlock
+ *
+ * pagebuf_unlock releases the lock on the buffer object created by
+ * pagebuf_lock or pagebuf_cond_lock (not any
+ * pinning of underlying pages created by pagebuf_pin).
+ */
+void
+pagebuf_unlock( /* unlock buffer */
+ xfs_buf_t *pb) /* buffer to unlock */
+{
+ PB_CLEAR_OWNER(pb);
+ up(&pb->pb_sema);
+ PB_TRACE(pb, "unlock", 0);
+}
+
+
+/*
+ * Pinning Buffer Storage in Memory
+ */
+
+/*
+ * pagebuf_pin
+ *
+ * pagebuf_pin locks all of the memory represented by a buffer in
+ * memory. Multiple calls to pagebuf_pin and pagebuf_unpin, for
+ * the same or different buffers affecting a given page, will
+ * properly count the number of outstanding "pin" requests. The
+ * buffer may be released after the pagebuf_pin and a different
+ * buffer used when calling pagebuf_unpin, if desired.
+ * pagebuf_pin should be used by the file system when it wants be
+ * assured that no attempt will be made to force the affected
+ * memory to disk. It does not assure that a given logical page
+ * will not be moved to a different physical page.
+ */
+void
+pagebuf_pin(
+ xfs_buf_t *pb)
+{
+ atomic_inc(&pb->pb_pin_count);
+ PB_TRACE(pb, "pin", (long)pb->pb_pin_count.counter);
+}
+
+/*
+ * pagebuf_unpin
+ *
+ * pagebuf_unpin reverses the locking of memory performed by
+ * pagebuf_pin. Note that both functions affected the logical
+ * pages associated with the buffer, not the buffer itself.
+ */
+void
+pagebuf_unpin(
+ xfs_buf_t *pb)
+{
+ if (atomic_dec_and_test(&pb->pb_pin_count)) {
+ wake_up_all(&pb->pb_waiters);
+ }
+ PB_TRACE(pb, "unpin", (long)pb->pb_pin_count.counter);
+}
+
+int
+pagebuf_ispin(
+ xfs_buf_t *pb)
+{
+ return atomic_read(&pb->pb_pin_count);
+}
+
+/*
+ * pagebuf_wait_unpin
+ *
+ * pagebuf_wait_unpin waits until all of the memory associated
+ * with the buffer is not longer locked in memory. It returns
+ * immediately if none of the affected pages are locked.
+ */
+static inline void
+_pagebuf_wait_unpin(
+ xfs_buf_t *pb)
+{
+ DECLARE_WAITQUEUE (wait, current);
+
+ if (atomic_read(&pb->pb_pin_count) == 0)
+ return;
+
+ add_wait_queue(&pb->pb_waiters, &wait);
+ for (;;) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ if (atomic_read(&pb->pb_pin_count) == 0)
+ break;
+ if (atomic_read(&pb->pb_io_remaining))
+ run_task_queue(&tq_disk);
+ schedule();
+ }
+ remove_wait_queue(&pb->pb_waiters, &wait);
+ set_current_state(TASK_RUNNING);
+}
+
+
+/*
+ * Buffer Utility Routines
+ */
+
+/*
+ * pagebuf_iodone
+ *
+ * pagebuf_iodone marks a buffer for which I/O is in progress
+ * done with respect to that I/O. The pb_iodone routine, if
+ * present, will be called as a side-effect.
+ */
+void
+pagebuf_iodone_sched(
+ void *v)
+{
+ xfs_buf_t *bp = (xfs_buf_t *)v;
+
+ if (bp->pb_iodone)
+ (*(bp->pb_iodone))(bp);
+ else if (bp->pb_flags & PBF_ASYNC)
+ xfs_buf_relse(bp);
+}
+
+void
+pagebuf_iodone(
+ xfs_buf_t *pb,
+ int dataio,
+ int schedule)
+{
+ pb->pb_flags &= ~(PBF_READ | PBF_WRITE);
+ if (pb->pb_error == 0) {
+ pb->pb_flags &= ~(PBF_PARTIAL | PBF_NONE);
+ }
+
+ PB_TRACE(pb, "iodone", pb->pb_iodone);
+
+ if ((pb->pb_iodone) || (pb->pb_flags & PBF_ASYNC)) {
+ if (schedule) {
+ int daemon = CPU_TO_DAEMON(smp_processor_id());
+
+ INIT_TQUEUE(&pb->pb_iodone_sched,
+ pagebuf_iodone_sched, (void *)pb);
+ queue_task(&pb->pb_iodone_sched, dataio ?
+ &pagebuf_dataiodone_tq[daemon] :
+ &pagebuf_logiodone_tq[daemon]);
+ wake_up(dataio ?
+ &pagebuf_dataiodone_wait[daemon] :
+ &pagebuf_logiodone_wait[daemon]);
+ } else {
+ pagebuf_iodone_sched(pb);
+ }
+ } else {
+ up(&pb->pb_iodonesema);
+ }
+}
+
+/*
+ * pagebuf_ioerror
+ *
+ * pagebuf_ioerror sets the error code for a buffer.
+ */
+void
+pagebuf_ioerror( /* mark/clear buffer error flag */
+ xfs_buf_t *pb, /* buffer to mark */
+ int error) /* error to store (0 if none) */
+{
+ ASSERT(error >= 0 && error <= 0xffff);
+ pb->pb_error = (unsigned short)error;
+ PB_TRACE(pb, "ioerror", (unsigned long)error);
+}
+
+/*
+ * pagebuf_iostart
+ *
+ * pagebuf_iostart initiates I/O on a buffer, based on the flags supplied.
+ * If necessary, it will arrange for any disk space allocation required,
+ * and it will break up the request if the block mappings require it.
+ * The pb_iodone routine in the buffer supplied will only be called
+ * when all of the subsidiary I/O requests, if any, have been completed.
+ * pagebuf_iostart calls the pagebuf_ioinitiate routine or
+ * pagebuf_iorequest, if the former routine is not defined, to start
+ * the I/O on a given low-level request.
+ */
+int
+pagebuf_iostart( /* start I/O on a buffer */
+ xfs_buf_t *pb, /* buffer to start */
+ page_buf_flags_t flags) /* PBF_LOCK, PBF_ASYNC, PBF_READ, */
+ /* PBF_WRITE, PBF_DELWRI, */
+ /* PBF_DONT_BLOCK */
+{
+ int status = 0;
+
+ PB_TRACE(pb, "iostart", (unsigned long)flags);
+
+ if (flags & PBF_DELWRI) {
+ pb->pb_flags &= ~(PBF_READ | PBF_WRITE | PBF_ASYNC);
+ pb->pb_flags |= flags & (PBF_DELWRI | PBF_ASYNC);
+ pagebuf_delwri_queue(pb, 1);
+ return status;
+ }
+
+ pb->pb_flags &= ~(PBF_READ | PBF_WRITE | PBF_ASYNC | PBF_DELWRI | \
+ PBF_READ_AHEAD | _PBF_RUN_QUEUES);
+ pb->pb_flags |= flags & (PBF_READ | PBF_WRITE | PBF_ASYNC | \
+ PBF_READ_AHEAD | _PBF_RUN_QUEUES);
+
+ BUG_ON(pb->pb_bn == XFS_BUF_DADDR_NULL);
+
+ /* For writes allow an alternate strategy routine to precede
+ * the actual I/O request (which may not be issued at all in
+ * a shutdown situation, for example).
+ */
+ status = (flags & PBF_WRITE) ?
+ pagebuf_iostrategy(pb) : pagebuf_iorequest(pb);
+
+ /* Wait for I/O if we are not an async request.
+ * Note: async I/O request completion will release the buffer,
+ * and that can already be done by this point. So using the
+ * buffer pointer from here on, after async I/O, is invalid.
+ */
+ if (!status && !(flags & PBF_ASYNC))
+ status = pagebuf_iowait(pb);
+
+ return status;
+}
+
+
+/*
+ * Helper routines for pagebuf_iorequest (pagebuf I/O completion)
+ */
+
+STATIC __inline__ int
+_pagebuf_iolocked(
+ xfs_buf_t *pb)
+{
+ ASSERT(pb->pb_flags & (PBF_READ|PBF_WRITE));
+ if (pb->pb_target->pbr_bsize < PAGE_CACHE_SIZE)
+ return pb->pb_locked;
+ if (pb->pb_flags & PBF_READ)
+ return pb->pb_locked;
+ return (pb->pb_flags & _PBF_PAGE_CACHE);
+}
+
+STATIC void
+_pagebuf_iodone(
+ xfs_buf_t *pb,
+ int schedule)
+{
+ int i;
+
+ if (atomic_dec_and_test(&pb->pb_io_remaining) != 1)
+ return;
+
+ if (_pagebuf_iolocked(pb))
+ for (i = 0; i < pb->pb_page_count; i++)
+ unlock_page(pb->pb_pages[i]);
+ pb->pb_locked = 0;
+ pagebuf_iodone(pb, (pb->pb_flags & PBF_FS_DATAIOD), schedule);
+}
+
+STATIC void
+_end_io_pagebuf(
+ struct buffer_head *bh,
+ int uptodate,
+ int fullpage)
+{
+ struct page *page = bh->b_page;
+ xfs_buf_t *pb = (xfs_buf_t *)bh->b_private;
+
+ mark_buffer_uptodate(bh, uptodate);
+ put_bh(bh);
+
+ if (!uptodate) {
+ SetPageError(page);
+ pb->pb_error = EIO;
+ }
+
+ if (fullpage) {
+ unlock_buffer(bh);
+ _pagebuf_free_bh(bh);
+ if (!PageError(page))
+ SetPageUptodate(page);
+ } else {
+ static spinlock_t page_uptodate_lock = SPIN_LOCK_UNLOCKED;
+ struct buffer_head *bp;
+ unsigned long flags;
+
+ ASSERT(PageLocked(page));
+ spin_lock_irqsave(&page_uptodate_lock, flags);
+ clear_buffer_async(bh);
+ unlock_buffer(bh);
+ for (bp = bh->b_this_page; bp != bh; bp = bp->b_this_page) {
+ if (buffer_locked(bp)) {
+ if (buffer_async(bp))
+ break;
+ } else if (!buffer_uptodate(bp))
+ break;
+ }
+ spin_unlock_irqrestore(&page_uptodate_lock, flags);
+ if (bp == bh && !PageError(page))
+ SetPageUptodate(page);
+ }
+
+ _pagebuf_iodone(pb, 1);
+}
+
+STATIC void
+_pagebuf_end_io_complete_pages(
+ struct buffer_head *bh,
+ int uptodate)
+{
+ _end_io_pagebuf(bh, uptodate, 1);
+}
+
+STATIC void
+_pagebuf_end_io_partial_pages(
+ struct buffer_head *bh,
+ int uptodate)
+{
+ _end_io_pagebuf(bh, uptodate, 0);
+}
+
+/*
+ * Handling of buftargs.
+ */
+
+/*
+ * Wait for any bufs with callbacks that have been submitted but
+ * have not yet returned... walk the hash list for the target.
+ */
+void
+xfs_wait_buftarg(
+ xfs_buftarg_t *target)
+{
+ xfs_buf_t *pb, *n;
+ pb_hash_t *h;
+ int i;
+
+ for (i = 0; i < NHASH; i++) {
+ h = &pbhash[i];
+again:
+ spin_lock(&h->pb_hash_lock);
+ list_for_each_entry_safe(pb, n, &h->pb_hash, pb_hash_list) {
+ if (pb->pb_target == target &&
+ !(pb->pb_flags & PBF_FS_MANAGED)) {
+ spin_unlock(&h->pb_hash_lock);
+ delay(100);
+ goto again;
+ }
+ }
+ spin_unlock(&h->pb_hash_lock);
+ }
+}
+
+void
+xfs_free_buftarg(
+ xfs_buftarg_t *btp,
+ int external)
+{
+ xfs_flush_buftarg(btp, 1);
+ if (external)
+ xfs_blkdev_put(btp->pbr_bdev);
+ iput(btp->pbr_mapping->host);
+ kmem_free(btp, sizeof(*btp));
+}
+
+void
+xfs_incore_relse(
+ xfs_buftarg_t *btp,
+ int delwri_only,
+ int wait)
+{
+ destroy_buffers(btp->pbr_kdev);
+ truncate_inode_pages(btp->pbr_mapping, 0LL);
+}
+
+int
+xfs_setsize_buftarg(
+ xfs_buftarg_t *btp,
+ unsigned int blocksize,
+ unsigned int sectorsize)
+{
+ btp->pbr_bsize = blocksize;
+ btp->pbr_sshift = ffs(sectorsize) - 1;
+ btp->pbr_smask = sectorsize - 1;
+
+ if (set_blocksize(btp->pbr_kdev, sectorsize)) {
+ printk(KERN_WARNING
+ "XFS: Cannot set_blocksize to %u on device 0x%x\n",
+ sectorsize, kdev_t_to_nr(btp->pbr_kdev));
+ return EINVAL;
+ }
+ return 0;
+}
+
+STATIC int
+xfs_mapping_buftarg(
+ xfs_buftarg_t *btp,
+ struct block_device *bdev)
+{
+ kdev_t kdev;
+ struct inode *inode;
+ struct address_space *mapping;
+ static struct address_space_operations mapping_aops = {
+ .sync_page = block_sync_page,
+ };
+
+ kdev = to_kdev_t(bdev->bd_dev);
+ inode = new_inode(bdev->bd_inode->i_sb);
+ if (!inode) {
+ printk(KERN_WARNING
+ "XFS: Cannot allocate mapping inode for device %s\n",
+ XFS_BUFTARG_NAME(btp));
+ return ENOMEM;
+ }
+ inode->i_mode = S_IFBLK;
+ inode->i_dev = kdev;
+ inode->i_rdev = kdev;
+ inode->i_bdev = bdev;
+ mapping = &inode->i_data;
+ mapping->a_ops = &mapping_aops;
+ mapping->gfp_mask = GFP_KERNEL;
+ btp->pbr_mapping = mapping;
+ return 0;
+}
+
+xfs_buftarg_t *
+xfs_alloc_buftarg(
+ struct block_device *bdev)
+{
+ xfs_buftarg_t *btp;
+ kdev_t kdev;
+
+ btp = kmem_zalloc(sizeof(*btp), KM_SLEEP);
+
+ kdev = to_kdev_t(bdev->bd_dev);
+ btp->pbr_dev = bdev->bd_dev;
+ btp->pbr_kdev = kdev;
+ btp->pbr_bdev = bdev;
+ switch (MAJOR(btp->pbr_dev)) {
+ case MD_MAJOR:
+ case EVMS_MAJOR:
+ btp->pbr_flags = PBR_ALIGNED_ONLY;
+ break;
+ case LOOP_MAJOR:
+ case LVM_BLK_MAJOR:
+ btp->pbr_flags = PBR_SECTOR_ONLY;
+ break;
+ }
+ if (xfs_setsize_buftarg(btp, PAGE_CACHE_SIZE, get_hardsect_size(kdev)))
+ goto error;
+ if (xfs_mapping_buftarg(btp, bdev))
+ goto error;
+ return btp;
+
+error:
+ kmem_free(btp, sizeof(*btp));
+ return NULL;
+}
+
+/*
+ * Initiate I/O on part of a page we are interested in
+ */
+STATIC int
+_pagebuf_page_io(
+ struct page *page, /* Page structure we are dealing with */
+ xfs_buftarg_t *pbr, /* device parameters (bsz, ssz, dev) */
+ xfs_buf_t *pb, /* pagebuf holding it, can be NULL */
+ xfs_daddr_t bn, /* starting block number */
+ size_t pg_offset, /* starting offset in page */
+ size_t pg_length, /* count of data to process */
+ int rw, /* read/write operation */
+ int flush)
+{
+ size_t sector;
+ size_t blk_length = 0;
+ struct buffer_head *bh, *head, *bufferlist[MAX_BUF_PER_PAGE];
+ int sector_shift = pbr->pbr_sshift;
+ int i = 0, cnt = 0;
+ int public_bh = 0;
+ int multi_ok;
+
+ if ((pbr->pbr_bsize < PAGE_CACHE_SIZE) &&
+ !(pb->pb_flags & _PBF_PRIVATE_BH)) {
+ int cache_ok;
+
+ cache_ok = !((pb->pb_flags & PBF_FORCEIO) || (rw == WRITE));
+ public_bh = multi_ok = 1;
+ sector = 1 << sector_shift;
+
+ ASSERT(PageLocked(page));
+ if (!page_has_buffers(page))
+ create_empty_buffers(page, pbr->pbr_kdev, sector);
+
+ i = sector >> BBSHIFT;
+ bn -= (pg_offset >> BBSHIFT);
+
+ /* Find buffer_heads belonging to just this pagebuf */
+ bh = head = page_buffers(page);
+ do {
+ if (buffer_uptodate(bh) && cache_ok)
+ continue;
+ if (blk_length < pg_offset)
+ continue;
+ if (blk_length >= pg_offset + pg_length)
+ break;
+
+ lock_buffer(bh);
+ get_bh(bh);
+ bh->b_size = sector;
+ bh->b_blocknr = bn;
+ bufferlist[cnt++] = bh;
+
+ } while ((bn += i),
+ (blk_length += sector),
+ (bh = bh->b_this_page) != head);
+
+ goto request;
+ }
+
+ /* Calculate the block offsets and length we will be using */
+ if (pg_offset) {
+ size_t block_offset;
+
+ block_offset = pg_offset >> sector_shift;
+ block_offset = pg_offset - (block_offset << sector_shift);
+ blk_length = (pg_length + block_offset + pbr->pbr_smask) >>
+ sector_shift;
+ } else {
+ blk_length = (pg_length + pbr->pbr_smask) >> sector_shift;
+ }
+
+ /* This will attempt to make a request bigger than the sector
+ * size if we are well aligned.
+ */
+ switch (pb->pb_target->pbr_flags) {
+ case 0:
+ sector = blk_length << sector_shift;
+ blk_length = 1;
+ break;
+ case PBR_ALIGNED_ONLY:
+ if ((pg_offset == 0) && (pg_length == PAGE_CACHE_SIZE) &&
+ (((unsigned int) bn) & BN_ALIGN_MASK) == 0) {
+ sector = blk_length << sector_shift;
+ blk_length = 1;
+ break;
+ }
+ case PBR_SECTOR_ONLY:
+ /* Fallthrough, same as default */
+ default:
+ sector = 1 << sector_shift;
+ }
+
+ /* If we are doing I/O larger than the bh->b_size field then
+ * we need to split this request up.
+ */
+ while (sector > ((1ULL << NBBY * sizeof(bh->b_size)) - 1)) {
+ sector >>= 1;
+ blk_length++;
+ }
+
+ multi_ok = (blk_length != 1);
+ i = sector >> BBSHIFT;
+
+ for (; blk_length > 0; bn += i, blk_length--, pg_offset += sector) {
+ bh = kmem_cache_alloc(bh_cachep, SLAB_NOFS);
+ if (!bh)
+ bh = _pagebuf_get_prealloc_bh();
+ memset(bh, 0, sizeof(*bh));
+ bh->b_blocknr = bn;
+ bh->b_size = sector;
+ bh->b_dev = pbr->pbr_kdev;
+ set_buffer_locked(bh);
+ set_bh_page(bh, page, pg_offset);
+ init_waitqueue_head(&bh->b_wait);
+ atomic_set(&bh->b_count, 1);
+ bufferlist[cnt++] = bh;
+ }
+
+request:
+ if (cnt) {
+ void (*callback)(struct buffer_head *, int);
+
+ callback = (multi_ok && public_bh) ?
+ _pagebuf_end_io_partial_pages :
+ _pagebuf_end_io_complete_pages;
+
+ /* Account for additional buffers in progress */
+ atomic_add(cnt, &pb->pb_io_remaining);
+
+#ifdef RQ_WRITE_ORDERED
+ if (flush)
+ set_bit(BH_Ordered_Flush, &bufferlist[cnt-1]->b_state);
+#endif
+
+ for (i = 0; i < cnt; i++) {
+ bh = bufferlist[i];
+ init_buffer(bh, callback, pb);
+ bh->b_rdev = bh->b_dev;
+ bh->b_rsector = bh->b_blocknr;
+ set_buffer_mapped(bh);
+ set_buffer_async(bh);
+ set_buffer_req(bh);
+ if (rw == WRITE)
+ set_buffer_uptodate(bh);
+ generic_make_request(rw, bh);
+ }
+ return 0;
+ }
+
+ /*
+ * We have no I/O to submit, let the caller know that
+ * we have skipped over this page entirely.
+ */
+ return 1;
+}
+
+STATIC void
+_pagebuf_page_apply(
+ xfs_buf_t *pb,
+ loff_t offset,
+ struct page *page,
+ size_t pg_offset,
+ size_t pg_length,
+ int last)
+{
+ xfs_daddr_t bn = pb->pb_bn;
+ xfs_buftarg_t *pbr = pb->pb_target;
+ loff_t pb_offset;
+ int status, locking;
+
+ ASSERT(page);
+ ASSERT(pb->pb_flags & (PBF_READ|PBF_WRITE));
+
+ if ((pbr->pbr_bsize == PAGE_CACHE_SIZE) &&
+ (pb->pb_buffer_length < PAGE_CACHE_SIZE) &&
+ (pb->pb_flags & PBF_READ) && pb->pb_locked) {
+ bn -= (pb->pb_offset >> BBSHIFT);
+ pg_offset = 0;
+ pg_length = PAGE_CACHE_SIZE;
+ } else {
+ pb_offset = offset - pb->pb_file_offset;
+ if (pb_offset) {
+ bn += (pb_offset + BBMASK) >> BBSHIFT;
+ }
+ }
+
+ locking = _pagebuf_iolocked(pb);
+ if (pb->pb_flags & PBF_WRITE) {
+ if (locking && !pb->pb_locked)
+ lock_page(page);
+ status = _pagebuf_page_io(page, pbr, pb, bn,
+ pg_offset, pg_length, WRITE,
+ last && (pb->pb_flags & PBF_FLUSH));
+ } else {
+ status = _pagebuf_page_io(page, pbr, pb, bn,
+ pg_offset, pg_length, READ, 0);
+ }
+ if (status && locking && !(pb->pb_target->pbr_bsize < PAGE_CACHE_SIZE))
+ unlock_page(page);
+}
+
+/*
+ * pagebuf_iorequest -- the core I/O request routine.
+ */
+int
+pagebuf_iorequest( /* start real I/O */
+ xfs_buf_t *pb) /* buffer to convey to device */
+{
+ PB_TRACE(pb, "iorequest", 0);
+
+ if (pb->pb_flags & PBF_DELWRI) {
+ pagebuf_delwri_queue(pb, 1);
+ return 0;
+ }
+
+ if (pb->pb_flags & PBF_WRITE) {
+ _pagebuf_wait_unpin(pb);
+ }
+
+ pagebuf_hold(pb);
+
+ /* Set the count to 1 initially, this will stop an I/O
+ * completion callout which happens before we have started
+ * all the I/O from calling pagebuf_iodone too early.
+ */
+ atomic_set(&pb->pb_io_remaining, 1);
+ _pagebuf_ioapply(pb);
+ _pagebuf_iodone(pb, 0);
+
+ pagebuf_rele(pb);
+ return 0;
+}
+
+/*
+ * pagebuf_iowait
+ *
+ * pagebuf_iowait waits for I/O to complete on the buffer supplied.
+ * It returns immediately if no I/O is pending. In any case, it returns
+ * the error code, if any, or 0 if there is no error.
+ */
+int
+pagebuf_iowait(
+ xfs_buf_t *pb)
+{
+ PB_TRACE(pb, "iowait", 0);
+ if (atomic_read(&pb->pb_io_remaining))
+ run_task_queue(&tq_disk);
+ if ((pb->pb_flags & PBF_FS_DATAIOD))
+ pagebuf_runall_queues(pagebuf_dataiodone_tq);
+ down(&pb->pb_iodonesema);
+ PB_TRACE(pb, "iowaited", (long)pb->pb_error);
+ return pb->pb_error;
+}
+
+caddr_t
+pagebuf_offset(
+ xfs_buf_t *pb,
+ size_t offset)
+{
+ struct page *page;
+
+ offset += pb->pb_offset;
+
+ page = pb->pb_pages[offset >> PAGE_CACHE_SHIFT];
+ return (caddr_t) page_address(page) + (offset & (PAGE_CACHE_SIZE - 1));
+}
+
+/*
+ * pagebuf_iomove
+ *
+ * Move data into or out of a buffer.
+ */
+void
+pagebuf_iomove(
+ xfs_buf_t *pb, /* buffer to process */
+ size_t boff, /* starting buffer offset */
+ size_t bsize, /* length to copy */
+ caddr_t data, /* data address */
+ page_buf_rw_t mode) /* read/write flag */
+{
+ size_t bend, cpoff, csize;
+ struct page *page;
+
+ bend = boff + bsize;
+ while (boff < bend) {
+ page = pb->pb_pages[page_buf_btoct(boff + pb->pb_offset)];
+ cpoff = page_buf_poff(boff + pb->pb_offset);
+ csize = min_t(size_t,
+ PAGE_CACHE_SIZE-cpoff, pb->pb_count_desired-boff);
+
+ ASSERT(((csize + cpoff) <= PAGE_CACHE_SIZE));
+
+ switch (mode) {
+ case PBRW_ZERO:
+ memset(page_address(page) + cpoff, 0, csize);
+ break;
+ case PBRW_READ:
+ memcpy(data, page_address(page) + cpoff, csize);
+ break;
+ case PBRW_WRITE:
+ memcpy(page_address(page) + cpoff, data, csize);
+ }
+
+ boff += csize;
+ data += csize;
+ }
+}
+
+/*
+ * _pagebuf_ioapply
+ *
+ * Applies _pagebuf_page_apply to each page of the xfs_buf_t.
+ */
+STATIC void
+_pagebuf_ioapply( /* apply function to pages */
+ xfs_buf_t *pb) /* buffer to examine */
+{
+ int index;
+ loff_t buffer_offset = pb->pb_file_offset;
+ size_t buffer_len = pb->pb_count_desired;
+ size_t page_offset, len;
+ size_t cur_offset, cur_len;
+
+ cur_offset = pb->pb_offset;
+ cur_len = buffer_len;
+
+ if (!pb->pb_locked && !(pb->pb_flags & PBF_DIRECTIO) &&
+ (pb->pb_target->pbr_bsize < PAGE_CACHE_SIZE)) {
+ for (index = 0; index < pb->pb_page_count; index++)
+ lock_page(pb->pb_pages[index]);
+ pb->pb_locked = 1;
+ }
+
+ for (index = 0; index < pb->pb_page_count; index++) {
+ if (cur_len == 0)
+ break;
+ if (cur_offset >= PAGE_CACHE_SIZE) {
+ cur_offset -= PAGE_CACHE_SIZE;
+ continue;
+ }
+
+ page_offset = cur_offset;
+ cur_offset = 0;
+
+ len = PAGE_CACHE_SIZE - page_offset;
+ if (len > cur_len)
+ len = cur_len;
+ cur_len -= len;
+
+ _pagebuf_page_apply(pb, buffer_offset,
+ pb->pb_pages[index], page_offset, len,
+ index + 1 == pb->pb_page_count);
+ buffer_offset += len;
+ buffer_len -= len;
+ }
+
+ /*
+ * Run the block device task queue here, while we have
+ * a hold on the pagebuf (important to have that hold).
+ */
+ if (pb->pb_flags & _PBF_RUN_QUEUES) {
+ pb->pb_flags &= ~_PBF_RUN_QUEUES;
+ if (atomic_read(&pb->pb_io_remaining) > 1)
+ run_task_queue(&tq_disk);
+ }
+}
+
+
+/*
+ * Delayed write buffer list handling
+ */
+
+STATIC LIST_HEAD(pbd_delwrite_queue);
+STATIC spinlock_t pbd_delwrite_lock = SPIN_LOCK_UNLOCKED;
+
+STATIC void
+pagebuf_delwri_queue(
+ xfs_buf_t *pb,
+ int unlock)
+{
+ PB_TRACE(pb, "delwri_q", (long)unlock);
+ ASSERT(pb->pb_flags & PBF_DELWRI);
+
+ spin_lock(&pbd_delwrite_lock);
+ /* If already in the queue, dequeue and place at tail */
+ if (!list_empty(&pb->pb_list)) {
+ if (unlock)
+ atomic_dec(&pb->pb_hold);
+ list_del(&pb->pb_list);
+ }
+
+ list_add_tail(&pb->pb_list, &pbd_delwrite_queue);
+ pb->pb_queuetime = jiffies;
+ spin_unlock(&pbd_delwrite_lock);
+
+ if (unlock)
+ pagebuf_unlock(pb);
+}
+
+void
+pagebuf_delwri_dequeue(
+ xfs_buf_t *pb)
+{
+ int dequeued = 0;
+
+ spin_lock(&pbd_delwrite_lock);
+ if ((pb->pb_flags & PBF_DELWRI) && !list_empty(&pb->pb_list)) {
+ list_del_init(&pb->pb_list);
+ dequeued = 1;
+ }
+ pb->pb_flags &= ~PBF_DELWRI;
+ spin_unlock(&pbd_delwrite_lock);
+
+ if (dequeued)
+ pagebuf_rele(pb);
+
+ PB_TRACE(pb, "delwri_dq", (long)dequeued);
+}
+
+
+/*
+ * The pagebuf iodone daemons
+ */
+
+STATIC int
+pagebuf_iodone_daemon(
+ void *__bind_cpu,
+ const char *name,
+ int pagebuf_daemons[],
+ struct list_head pagebuf_iodone_tq[],
+ wait_queue_head_t pagebuf_iodone_wait[])
+{
+ int bind_cpu, cpu;
+ DECLARE_WAITQUEUE (wait, current);
+
+ bind_cpu = (int) (long)__bind_cpu;
+ cpu = CPU_TO_DAEMON(cpu_logical_map(bind_cpu));
+
+ /* Set up the thread */
+ daemonize();
+
+ /* Avoid signals */
+ sigmask_lock();
+ sigfillset(&current->blocked);
+ __recalc_sigpending(current);
+ sigmask_unlock();
+
+ /* Migrate to the right CPU */
+ migrate_to_cpu(cpu);
+#ifdef __HAVE_NEW_SCHEDULER
+ if (smp_processor_id() != cpu)
+ BUG();
+#else
+ while (smp_processor_id() != cpu)
+ schedule();
+#endif
+
+ sprintf(current->comm, "%s/%d", name, bind_cpu);
+ INIT_LIST_HEAD(&pagebuf_iodone_tq[cpu]);
+ init_waitqueue_head(&pagebuf_iodone_wait[cpu]);
+ __set_current_state(TASK_INTERRUPTIBLE);
+ mb();
+
+ pagebuf_daemons[cpu] = 1;
+
+ for (;;) {
+ add_wait_queue(&pagebuf_iodone_wait[cpu], &wait);
+
+ if (TQ_ACTIVE(pagebuf_iodone_tq[cpu]))
+ __set_task_state(current, TASK_RUNNING);
+ schedule();
+ remove_wait_queue(&pagebuf_iodone_wait[cpu], &wait);
+ run_task_queue(&pagebuf_iodone_tq[cpu]);
+ if (pagebuf_daemons[cpu] == 0)
+ break;
+ __set_current_state(TASK_INTERRUPTIBLE);
+ }
+
+ pagebuf_daemons[cpu] = -1;
+ wake_up_interruptible(&pagebuf_iodone_wait[cpu]);
+ return 0;
+}
+
+STATIC void
+pagebuf_runall_queues(
+ struct list_head pagebuf_iodone_tq[])
+{
+ int pcpu, cpu;
+
+ for (cpu = 0; cpu < min(smp_num_cpus, MAX_IO_DAEMONS); cpu++) {
+ pcpu = CPU_TO_DAEMON(cpu_logical_map(cpu));
+
+ run_task_queue(&pagebuf_iodone_tq[pcpu]);
+ }
+}
+
+STATIC int
+pagebuf_logiodone_daemon(
+ void *__bind_cpu)
+{
+ return pagebuf_iodone_daemon(__bind_cpu, "xfslogd", pb_logio_daemons,
+ pagebuf_logiodone_tq, pagebuf_logiodone_wait);
+}
+
+STATIC int
+pagebuf_dataiodone_daemon(
+ void *__bind_cpu)
+{
+ return pagebuf_iodone_daemon(__bind_cpu, "xfsdatad", pb_dataio_daemons,
+ pagebuf_dataiodone_tq, pagebuf_dataiodone_wait);
+}
+
+
+/* Defines for pagebuf daemon */
+STATIC DECLARE_COMPLETION(pagebuf_daemon_done);
+STATIC struct task_struct *pagebuf_daemon_task;
+STATIC int pagebuf_daemon_active;
+STATIC int force_flush;
+
+
+STATIC int
+pagebuf_daemon_wakeup(
+ int priority,
+ unsigned int mask)
+{
+ force_flush = 1;
+ barrier();
+ wake_up_process(pagebuf_daemon_task);
+ return 0;
+}
+
+STATIC int
+pagebuf_daemon(
+ void *data)
+{
+ struct list_head tmp;
+ unsigned long age;
+ xfs_buf_t *pb, *n;
+ int count;
+
+ /* Set up the thread */
+ daemonize();
+
+ /* Mark it active */
+ pagebuf_daemon_task = current;
+ pagebuf_daemon_active = 1;
+ barrier();
+
+ /* Avoid signals */
+ sigmask_lock();
+ sigfillset(&current->blocked);
+ __recalc_sigpending(current);
+ sigmask_unlock();
+
+ strcpy(current->comm, "xfsbufd");
+ current->flags |= PF_MEMALLOC;
+
+ INIT_LIST_HEAD(&tmp);
+ do {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout((xfs_buf_timer_centisecs * HZ) / 100);
+
+ count = 0;
+ age = (xfs_buf_age_centisecs * HZ) / 100;
+ spin_lock(&pbd_delwrite_lock);
+ list_for_each_entry_safe(pb, n, &pbd_delwrite_queue, pb_list) {
+ PB_TRACE(pb, "walkq1", (long)pagebuf_ispin(pb));
+ ASSERT(pb->pb_flags & PBF_DELWRI);
+
+ if (!pagebuf_ispin(pb) && !pagebuf_cond_lock(pb)) {
+ if (!force_flush &&
+ time_before(jiffies,
+ pb->pb_queuetime + age)) {
+ pagebuf_unlock(pb);
+ break;
+ }
+
+ pb->pb_flags &= ~PBF_DELWRI;
+ pb->pb_flags |= PBF_WRITE;
+ list_move(&pb->pb_list, &tmp);
+ count++;
+ }
+ }
+ spin_unlock(&pbd_delwrite_lock);
+
+ while (!list_empty(&tmp)) {
+ pb = list_entry(tmp.next, xfs_buf_t, pb_list);
+ list_del_init(&pb->pb_list);
+ pagebuf_iostrategy(pb);
+ }
+
+ if (as_list_len > 0)
+ purge_addresses();
+ if (count)
+ run_task_queue(&tq_disk);
+
+ force_flush = 0;
+ } while (pagebuf_daemon_active);
+
+ complete_and_exit(&pagebuf_daemon_done, 0);
+}
+
+/*
+ * Go through all incore buffers, and release buffers if they belong to
+ * the given device. This is used in filesystem error handling to
+ * preserve the consistency of its metadata.
+ */
+int
+xfs_flush_buftarg(
+ xfs_buftarg_t *target,
+ int wait)
+{
+ struct list_head tmp;
+ xfs_buf_t *pb, *n;
+ int pincount = 0;
+ int flush_cnt = 0;
+
+ pagebuf_runall_queues(pagebuf_dataiodone_tq);
+ pagebuf_runall_queues(pagebuf_logiodone_tq);
+
+ INIT_LIST_HEAD(&tmp);
+ spin_lock(&pbd_delwrite_lock);
+ list_for_each_entry_safe(pb, n, &pbd_delwrite_queue, pb_list) {
+
+ if (pb->pb_target != target)
+ continue;
+
+ ASSERT(pb->pb_flags & PBF_DELWRI);
+ PB_TRACE(pb, "walkq2", (long)pagebuf_ispin(pb));
+ if (pagebuf_ispin(pb)) {
+ pincount++;
+ continue;
+ }
+
+ pb->pb_flags &= ~PBF_DELWRI;
+ pb->pb_flags |= PBF_WRITE;
+ list_move(&pb->pb_list, &tmp);
+ }
+ spin_unlock(&pbd_delwrite_lock);
+
+ /*
+ * Dropped the delayed write list lock, now walk the temporary list
+ */
+ list_for_each_entry_safe(pb, n, &tmp, pb_list) {
+
+ if (wait)
+ pb->pb_flags &= ~PBF_ASYNC;
+ else
+ list_del_init(&pb->pb_list);
+
+ pagebuf_lock(pb);
+ pagebuf_iostrategy(pb);
+
+ if (++flush_cnt > 32) {
+ run_task_queue(&tq_disk);
+ flush_cnt = 0;
+ }
+ }
+
+ run_task_queue(&tq_disk);
+
+ /*
+ * Remaining list items must be flushed before returning
+ */
+ while (!list_empty(&tmp)) {
+ pb = list_entry(tmp.next, xfs_buf_t, pb_list);
+
+ list_del_init(&pb->pb_list);
+
+ xfs_iowait(pb);
+ xfs_buf_relse(pb);
+ }
+
+ return pincount;
+}
+
+STATIC int
+pagebuf_daemon_start(void)
+{
+ int cpu, pcpu;
+
+ kernel_thread(pagebuf_daemon, NULL, CLONE_FS|CLONE_FILES|CLONE_VM);
+
+ for (cpu = 0; cpu < min(smp_num_cpus, MAX_IO_DAEMONS); cpu++) {
+ pcpu = CPU_TO_DAEMON(cpu_logical_map(cpu));
+
+ if (kernel_thread(pagebuf_logiodone_daemon,
+ (void *)(long) cpu,
+ CLONE_FS|CLONE_FILES|CLONE_VM) < 0) {
+ printk("pagebuf_logiodone daemon failed to start\n");
+ } else {
+ while (!pb_logio_daemons[pcpu])
+ yield();
+ }
+ }
+ for (cpu = 0; cpu < min(smp_num_cpus, MAX_IO_DAEMONS); cpu++) {
+ pcpu = CPU_TO_DAEMON(cpu_logical_map(cpu));
+
+ if (kernel_thread(pagebuf_dataiodone_daemon,
+ (void *)(long) cpu,
+ CLONE_FS|CLONE_FILES|CLONE_VM) < 0) {
+ printk("pagebuf_dataiodone daemon failed to start\n");
+ } else {
+ while (!pb_dataio_daemons[pcpu])
+ yield();
+ }
+ }
+ return 0;
+}
+
+/*
+ * pagebuf_daemon_stop
+ *
+ * Note: do not mark as __exit, it is called from pagebuf_terminate.
+ */
+STATIC void
+pagebuf_daemon_stop(void)
+{
+ int cpu, pcpu;
+
+ pagebuf_daemon_active = 0;
+ barrier();
+ wait_for_completion(&pagebuf_daemon_done);
+
+ for (pcpu = 0; pcpu < min(smp_num_cpus, MAX_IO_DAEMONS); pcpu++) {
+ cpu = CPU_TO_DAEMON(cpu_logical_map(pcpu));
+
+ pb_logio_daemons[cpu] = 0;
+ wake_up(&pagebuf_logiodone_wait[cpu]);
+ wait_event_interruptible(pagebuf_logiodone_wait[cpu],
+ pb_logio_daemons[cpu] == -1);
+
+ pb_dataio_daemons[cpu] = 0;
+ wake_up(&pagebuf_dataiodone_wait[cpu]);
+ wait_event_interruptible(pagebuf_dataiodone_wait[cpu],
+ pb_dataio_daemons[cpu] == -1);
+ }
+}
+
+/*
+ * Initialization and Termination
+ */
+
+int __init
+pagebuf_init(void)
+{
+ int i;
+
+ pagebuf_cache = kmem_cache_create("xfs_buf_t", sizeof(xfs_buf_t), 0,
+ SLAB_HWCACHE_ALIGN, NULL, NULL);
+ if (pagebuf_cache == NULL) {
+ printk("XFS: couldn't init xfs_buf_t cache\n");
+ return -ENOMEM;
+ }
+
+ if (_pagebuf_prealloc_bh(NR_RESERVED_BH) < NR_RESERVED_BH) {
+ printk("XFS: couldn't allocate %d reserved buffers\n",
+ NR_RESERVED_BH);
+ kmem_zone_destroy(pagebuf_cache);
+ return -ENOMEM;
+ }
+ init_waitqueue_head(&pb_resv_bh_wait);
+
+#ifdef PAGEBUF_TRACE
+ pagebuf_trace_buf = ktrace_alloc(PAGEBUF_TRACE_SIZE, KM_SLEEP);
+#endif
+
+ pagebuf_daemon_start();
+
+ pagebuf_shake = kmem_shake_register(pagebuf_daemon_wakeup);
+ if (pagebuf_shake == NULL) {
+ pagebuf_terminate();
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < NHASH; i++) {
+ spin_lock_init(&pbhash[i].pb_hash_lock);
+ INIT_LIST_HEAD(&pbhash[i].pb_hash);
+ }
+
+ return 0;
+}
+
+/*
+ * pagebuf_terminate.
+ *
+ * Note: do not mark as __exit, this is also called from the __init code.
+ */
+void
+pagebuf_terminate(void)
+{
+ pagebuf_daemon_stop();
+
+#ifdef PAGEBUF_TRACE
+ ktrace_free(pagebuf_trace_buf);
+#endif
+
+ kmem_zone_destroy(pagebuf_cache);
+ kmem_shake_deregister(pagebuf_shake);
+}
diff --git a/fs/xfs/linux-2.4/xfs_buf.h b/fs/xfs/linux-2.4/xfs_buf.h
new file mode 100644
index 00000000000000..4c1a084b089251
--- /dev/null
+++ b/fs/xfs/linux-2.4/xfs_buf.h
@@ -0,0 +1,634 @@
+/*
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+/*
+ * Written by Steve Lord, Jim Mostek, Russell Cattelan at SGI
+ */
+
+#ifndef __XFS_BUF_H__
+#define __XFS_BUF_H__
+
+#include <linux/version.h>
+#include <linux/config.h>
+#include <linux/list.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <asm/system.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/uio.h>
+
+/* nptl patch changes where the sigmask_lock is defined */
+#ifdef CLONE_SIGNAL /* stock */
+#define sigmask_lock() spin_lock_irq(&current->sigmask_lock);
+#define sigmask_unlock() spin_unlock_irq(&current->sigmask_lock);
+#define __recalc_sigpending(x) recalc_sigpending(x)
+#else /* nptl */
+#define sigmask_lock() spin_lock_irq(&current->sighand->siglock);
+#define sigmask_unlock() spin_unlock_irq(&current->sighand->siglock);
+#define __recalc_sigpending(x) recalc_sigpending()
+#endif
+/*
+ * Base types
+ */
+
+#define XFS_BUF_DADDR_NULL ((xfs_daddr_t) (-1LL))
+
+#define page_buf_ctob(pp) ((pp) * PAGE_CACHE_SIZE)
+#define page_buf_btoc(dd) (((dd) + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT)
+#define page_buf_btoct(dd) ((dd) >> PAGE_CACHE_SHIFT)
+#define page_buf_poff(aa) ((aa) & ~PAGE_CACHE_MASK)
+
+typedef enum page_buf_rw_e {
+ PBRW_READ = 1, /* transfer into target memory */
+ PBRW_WRITE = 2, /* transfer from target memory */
+ PBRW_ZERO = 3 /* Zero target memory */
+} page_buf_rw_t;
+
+
+typedef enum page_buf_flags_e { /* pb_flags values */
+ PBF_READ = (1 << 0), /* buffer intended for reading from device */
+ PBF_WRITE = (1 << 1), /* buffer intended for writing to device */
+ PBF_MAPPED = (1 << 2), /* buffer mapped (pb_addr valid) */
+ PBF_PARTIAL = (1 << 3), /* buffer partially read */
+ PBF_ASYNC = (1 << 4), /* initiator will not wait for completion */
+ PBF_NONE = (1 << 5), /* buffer not read at all */
+ PBF_DELWRI = (1 << 6), /* buffer has dirty pages */
+ PBF_STALE = (1 << 7), /* buffer has been staled, do not find it */
+ PBF_FS_MANAGED = (1 << 8), /* filesystem controls freeing memory */
+ PBF_FS_DATAIOD = (1 << 9), /* schedule IO completion on fs datad */
+ PBF_FORCEIO = (1 << 10), /* ignore any cache state */
+ PBF_FLUSH = (1 << 11), /* flush disk write cache */
+ PBF_READ_AHEAD = (1 << 12), /* asynchronous read-ahead */
+ PBF_DIRECTIO = (1 << 13), /* used for a direct IO mapping */
+
+ /* flags used only as arguments to access routines */
+ PBF_LOCK = (1 << 14), /* lock requested */
+ PBF_TRYLOCK = (1 << 15), /* lock requested, but do not wait */
+ PBF_DONT_BLOCK = (1 << 16), /* do not block in current thread */
+
+ /* flags used only internally */
+ _PBF_PAGE_CACHE = (1 << 17),/* backed by pagecache */
+ _PBF_KMEM_ALLOC = (1 << 18),/* backed by kmem_alloc() */
+ _PBF_RUN_QUEUES = (1 << 19),/* run block device task queue */
+ _PBF_PRIVATE_BH = (1 << 20),/* do not use public buffer heads */
+} page_buf_flags_t;
+
+#define PBF_UPDATE (PBF_READ | PBF_WRITE)
+#define PBF_NOT_DONE(pb) (((pb)->pb_flags & (PBF_PARTIAL|PBF_NONE)) != 0)
+#define PBF_DONE(pb) (((pb)->pb_flags & (PBF_PARTIAL|PBF_NONE)) == 0)
+
+#define PBR_SECTOR_ONLY 1 /* only use sector size buffer heads */
+#define PBR_ALIGNED_ONLY 2 /* only use aligned I/O */
+
+typedef struct xfs_buftarg {
+ int pbr_flags;
+ dev_t pbr_dev;
+ kdev_t pbr_kdev;
+ struct block_device *pbr_bdev;
+ struct address_space *pbr_mapping;
+ unsigned int pbr_bsize;
+ unsigned int pbr_sshift;
+ size_t pbr_smask;
+} xfs_buftarg_t;
+
+/*
+ * xfs_buf_t: Buffer structure for page cache-based buffers
+ *
+ * This buffer structure is used by the page cache buffer management routines
+ * to refer to an assembly of pages forming a logical buffer. The actual I/O
+ * is performed with buffer_head structures, as required by drivers.
+ *
+ * The buffer structure is used on temporary basis only, and discarded when
+ * released. The real data storage is recorded in the page cache. Metadata is
+ * hashed to the block device on which the file system resides.
+ */
+
+struct xfs_buf;
+
+/* call-back function on I/O completion */
+typedef void (*page_buf_iodone_t)(struct xfs_buf *);
+/* call-back function on I/O completion */
+typedef void (*page_buf_relse_t)(struct xfs_buf *);
+/* pre-write function */
+typedef int (*page_buf_bdstrat_t)(struct xfs_buf *);
+
+#define PB_PAGES 4
+
+typedef struct xfs_buf {
+ struct semaphore pb_sema; /* semaphore for lockables */
+ unsigned long pb_queuetime; /* time buffer was queued */
+ atomic_t pb_pin_count; /* pin count */
+ wait_queue_head_t pb_waiters; /* unpin waiters */
+ struct list_head pb_list;
+ page_buf_flags_t pb_flags; /* status flags */
+ struct list_head pb_hash_list;
+ xfs_buftarg_t *pb_target; /* logical object */
+ atomic_t pb_hold; /* reference count */
+ xfs_daddr_t pb_bn; /* block number for I/O */
+ loff_t pb_file_offset; /* offset in file */
+ size_t pb_buffer_length; /* size of buffer in bytes */
+ size_t pb_count_desired; /* desired transfer size */
+ void *pb_addr; /* virtual address of buffer */
+ struct tq_struct pb_iodone_sched;
+ atomic_t pb_io_remaining;/* #outstanding I/O requests */
+ page_buf_iodone_t pb_iodone; /* I/O completion function */
+ page_buf_relse_t pb_relse; /* releasing function */
+ page_buf_bdstrat_t pb_strat; /* pre-write function */
+ struct semaphore pb_iodonesema; /* Semaphore for I/O waiters */
+ void *pb_fspriv;
+ void *pb_fspriv2;
+ void *pb_fspriv3;
+ unsigned short pb_error; /* error code on I/O */
+ unsigned short pb_page_count; /* size of page array */
+ unsigned short pb_offset; /* page offset in first page */
+ unsigned char pb_locked; /* page array is locked */
+ unsigned char pb_hash_index; /* hash table index */
+ struct page **pb_pages; /* array of page pointers */
+ struct page *pb_page_array[PB_PAGES]; /* inline pages */
+#ifdef PAGEBUF_LOCK_TRACKING
+ int pb_last_holder;
+#endif
+} xfs_buf_t;
+
+
+/* Finding and Reading Buffers */
+
+extern xfs_buf_t *_pagebuf_find( /* find buffer for block if */
+ /* the block is in memory */
+ xfs_buftarg_t *, /* inode for block */
+ loff_t, /* starting offset of range */
+ size_t, /* length of range */
+ page_buf_flags_t, /* PBF_LOCK */
+ xfs_buf_t *); /* newly allocated buffer */
+
+#define xfs_incore(buftarg,blkno,len,lockit) \
+ _pagebuf_find(buftarg, blkno ,len, lockit, NULL)
+
+extern xfs_buf_t *xfs_buf_get_flags( /* allocate a buffer */
+ xfs_buftarg_t *, /* inode for buffer */
+ loff_t, /* starting offset of range */
+ size_t, /* length of range */
+ page_buf_flags_t); /* PBF_LOCK, PBF_READ, */
+ /* PBF_ASYNC */
+
+#define xfs_buf_get(target, blkno, len, flags) \
+ xfs_buf_get_flags((target), (blkno), (len), PBF_LOCK | PBF_MAPPED)
+
+extern xfs_buf_t *xfs_buf_read_flags( /* allocate and read a buffer */
+ xfs_buftarg_t *, /* inode for buffer */
+ loff_t, /* starting offset of range */
+ size_t, /* length of range */
+ page_buf_flags_t); /* PBF_LOCK, PBF_ASYNC */
+
+#define xfs_buf_read(target, blkno, len, flags) \
+ xfs_buf_read_flags((target), (blkno), (len), PBF_LOCK | PBF_MAPPED)
+
+extern xfs_buf_t *pagebuf_lookup(
+ xfs_buftarg_t *,
+ loff_t, /* starting offset of range */
+ size_t, /* length of range */
+ page_buf_flags_t); /* PBF_READ, PBF_WRITE, */
+ /* PBF_FORCEIO, */
+
+extern xfs_buf_t *pagebuf_get_empty( /* allocate pagebuf struct with */
+ /* no memory or disk address */
+ size_t len,
+ xfs_buftarg_t *); /* mount point "fake" inode */
+
+extern xfs_buf_t *pagebuf_get_no_daddr(/* allocate pagebuf struct */
+ /* without disk address */
+ size_t len,
+ xfs_buftarg_t *); /* mount point "fake" inode */
+
+extern int pagebuf_associate_memory(
+ xfs_buf_t *,
+ void *,
+ size_t);
+
+extern void pagebuf_hold( /* increment reference count */
+ xfs_buf_t *); /* buffer to hold */
+
+extern void pagebuf_readahead( /* read ahead into cache */
+ xfs_buftarg_t *, /* target for buffer (or NULL) */
+ loff_t, /* starting offset of range */
+ size_t, /* length of range */
+ page_buf_flags_t); /* additional read flags */
+
+/* Releasing Buffers */
+
+extern void pagebuf_free( /* deallocate a buffer */
+ xfs_buf_t *); /* buffer to deallocate */
+
+extern void pagebuf_rele( /* release hold on a buffer */
+ xfs_buf_t *); /* buffer to release */
+
+/* Locking and Unlocking Buffers */
+
+extern int pagebuf_cond_lock( /* lock buffer, if not locked */
+ /* (returns -EBUSY if locked) */
+ xfs_buf_t *); /* buffer to lock */
+
+extern int pagebuf_lock_value( /* return count on lock */
+ xfs_buf_t *); /* buffer to check */
+
+extern int pagebuf_lock( /* lock buffer */
+ xfs_buf_t *); /* buffer to lock */
+
+extern void pagebuf_unlock( /* unlock buffer */
+ xfs_buf_t *); /* buffer to unlock */
+
+/* Buffer Read and Write Routines */
+
+extern void pagebuf_iodone( /* mark buffer I/O complete */
+ xfs_buf_t *, /* buffer to mark */
+ int, /* use data/log helper thread. */
+ int); /* run completion locally, or in
+ * a helper thread. */
+
+extern void pagebuf_ioerror( /* mark buffer in error (or not) */
+ xfs_buf_t *, /* buffer to mark */
+ int); /* error to store (0 if none) */
+
+extern int pagebuf_iostart( /* start I/O on a buffer */
+ xfs_buf_t *, /* buffer to start */
+ page_buf_flags_t); /* PBF_LOCK, PBF_ASYNC, */
+ /* PBF_READ, PBF_WRITE, */
+ /* PBF_DELWRI */
+
+extern int pagebuf_iorequest( /* start real I/O */
+ xfs_buf_t *); /* buffer to convey to device */
+
+extern int pagebuf_iowait( /* wait for buffer I/O done */
+ xfs_buf_t *); /* buffer to wait on */
+
+extern void pagebuf_iomove( /* move data in/out of pagebuf */
+ xfs_buf_t *, /* buffer to manipulate */
+ size_t, /* starting buffer offset */
+ size_t, /* length in buffer */
+ caddr_t, /* data pointer */
+ page_buf_rw_t); /* direction */
+
+static inline int pagebuf_iostrategy(xfs_buf_t *pb)
+{
+ return pb->pb_strat ? pb->pb_strat(pb) : pagebuf_iorequest(pb);
+}
+
+static inline int pagebuf_geterror(xfs_buf_t *pb)
+{
+ return pb ? pb->pb_error : ENOMEM;
+}
+
+/* Buffer Utility Routines */
+
+extern caddr_t pagebuf_offset( /* pointer at offset in buffer */
+ xfs_buf_t *, /* buffer to offset into */
+ size_t); /* offset */
+
+/* Pinning Buffer Storage in Memory */
+
+extern void pagebuf_pin( /* pin buffer in memory */
+ xfs_buf_t *); /* buffer to pin */
+
+extern void pagebuf_unpin( /* unpin buffered data */
+ xfs_buf_t *); /* buffer to unpin */
+
+extern int pagebuf_ispin( /* check if buffer is pinned */
+ xfs_buf_t *); /* buffer to check */
+
+/* Delayed Write Buffer Routines */
+
+extern void pagebuf_delwri_dequeue(xfs_buf_t *);
+
+/* Buffer Daemon Setup Routines */
+
+extern int pagebuf_init(void);
+extern void pagebuf_terminate(void);
+
+
+#ifdef PAGEBUF_TRACE
+extern ktrace_t *pagebuf_trace_buf;
+extern void pagebuf_trace(
+ xfs_buf_t *, /* buffer being traced */
+ char *, /* description of operation */
+ void *, /* arbitrary diagnostic value */
+ void *); /* return address */
+#else
+# define pagebuf_trace(pb, id, ptr, ra) do { } while (0)
+#endif
+
+#define pagebuf_target_name(target) bdevname((target)->pbr_kdev)
+
+/*
+ * Kernel version compatibility macros
+ */
+
+#define page_buffers(page) ((page)->buffers)
+#define page_has_buffers(page) ((page)->buffers)
+#define PageUptodate(x) Page_Uptodate(x)
+/*
+ * macro tricks to expand the set_buffer_foo() and clear_buffer_foo()
+ * functions.
+ */
+#define BUFFER_FNS(bit, name) \
+static inline void set_buffer_##name(struct buffer_head *bh) \
+{ \
+ set_bit(BH_##bit, &(bh)->b_state); \
+} \
+static inline void clear_buffer_##name(struct buffer_head *bh) \
+{ \
+ clear_bit(BH_##bit, &(bh)->b_state); \
+} \
+
+/*
+ * Emit the buffer bitops functions. Note that there are also functions
+ * of the form "mark_buffer_foo()". These are higher-level functions which
+ * do something in addition to setting a b_state bit.
+ */
+BUFFER_FNS(Uptodate, uptodate)
+BUFFER_FNS(Dirty, dirty)
+BUFFER_FNS(Lock, locked)
+BUFFER_FNS(Req, req)
+BUFFER_FNS(Mapped, mapped)
+BUFFER_FNS(New, new)
+BUFFER_FNS(Async, async)
+BUFFER_FNS(Wait_IO, wait_io)
+BUFFER_FNS(Launder, launder)
+BUFFER_FNS(Sync, sync)
+BUFFER_FNS(Delay, delay)
+
+#define get_seconds() CURRENT_TIME
+#define i_size_read(inode) ((inode)->i_size)
+#define i_size_write(inode, sz) ((inode)->i_size = (sz))
+
+/* These are just for xfs_syncsub... it sets an internal variable
+ * then passes it to VOP_FLUSH_PAGES or adds the flags to a newly gotten buf_t
+ */
+#define XFS_B_ASYNC PBF_ASYNC
+#define XFS_B_DELWRI PBF_DELWRI
+#define XFS_B_READ PBF_READ
+#define XFS_B_WRITE PBF_WRITE
+#define XFS_B_STALE PBF_STALE
+
+#define XFS_BUF_TRYLOCK PBF_TRYLOCK
+#define XFS_INCORE_TRYLOCK PBF_TRYLOCK
+#define XFS_BUF_LOCK PBF_LOCK
+#define XFS_BUF_MAPPED PBF_MAPPED
+
+#define BUF_BUSY PBF_DONT_BLOCK
+
+#define XFS_BUF_BFLAGS(x) ((x)->pb_flags)
+#define XFS_BUF_ZEROFLAGS(x) \
+ ((x)->pb_flags &= ~(PBF_READ|PBF_WRITE|PBF_ASYNC|PBF_DELWRI))
+
+#define XFS_BUF_STALE(x) ((x)->pb_flags |= XFS_B_STALE)
+#define XFS_BUF_UNSTALE(x) ((x)->pb_flags &= ~XFS_B_STALE)
+#define XFS_BUF_ISSTALE(x) ((x)->pb_flags & XFS_B_STALE)
+#define XFS_BUF_SUPER_STALE(x) do { \
+ XFS_BUF_STALE(x); \
+ pagebuf_delwri_dequeue(x); \
+ XFS_BUF_DONE(x); \
+ } while (0)
+
+#define XFS_BUF_MANAGE PBF_FS_MANAGED
+#define XFS_BUF_UNMANAGE(x) ((x)->pb_flags &= ~PBF_FS_MANAGED)
+
+#define XFS_BUF_DELAYWRITE(x) ((x)->pb_flags |= PBF_DELWRI)
+#define XFS_BUF_UNDELAYWRITE(x) pagebuf_delwri_dequeue(x)
+#define XFS_BUF_ISDELAYWRITE(x) ((x)->pb_flags & PBF_DELWRI)
+
+#define XFS_BUF_ERROR(x,no) pagebuf_ioerror(x,no)
+#define XFS_BUF_GETERROR(x) pagebuf_geterror(x)
+#define XFS_BUF_ISERROR(x) (pagebuf_geterror(x)?1:0)
+
+#define XFS_BUF_DONE(x) ((x)->pb_flags &= ~(PBF_PARTIAL|PBF_NONE))
+#define XFS_BUF_UNDONE(x) ((x)->pb_flags |= PBF_PARTIAL|PBF_NONE)
+#define XFS_BUF_ISDONE(x) (!(PBF_NOT_DONE(x)))
+
+#define XFS_BUF_BUSY(x) ((x)->pb_flags |= PBF_FORCEIO)
+#define XFS_BUF_UNBUSY(x) ((x)->pb_flags &= ~PBF_FORCEIO)
+#define XFS_BUF_ISBUSY(x) (1)
+
+#define XFS_BUF_ASYNC(x) ((x)->pb_flags |= PBF_ASYNC)
+#define XFS_BUF_UNASYNC(x) ((x)->pb_flags &= ~PBF_ASYNC)
+#define XFS_BUF_ISASYNC(x) ((x)->pb_flags & PBF_ASYNC)
+
+#define XFS_BUF_FLUSH(x) ((x)->pb_flags |= PBF_FLUSH)
+#define XFS_BUF_UNFLUSH(x) ((x)->pb_flags &= ~PBF_FLUSH)
+#define XFS_BUF_ISFLUSH(x) ((x)->pb_flags & PBF_FLUSH)
+
+#define XFS_BUF_SHUT(x) printk("XFS_BUF_SHUT not implemented yet\n")
+#define XFS_BUF_UNSHUT(x) printk("XFS_BUF_UNSHUT not implemented yet\n")
+#define XFS_BUF_ISSHUT(x) (0)
+
+#define XFS_BUF_HOLD(x) pagebuf_hold(x)
+#define XFS_BUF_READ(x) ((x)->pb_flags |= PBF_READ)
+#define XFS_BUF_UNREAD(x) ((x)->pb_flags &= ~PBF_READ)
+#define XFS_BUF_ISREAD(x) ((x)->pb_flags & PBF_READ)
+
+#define XFS_BUF_WRITE(x) ((x)->pb_flags |= PBF_WRITE)
+#define XFS_BUF_UNWRITE(x) ((x)->pb_flags &= ~PBF_WRITE)
+#define XFS_BUF_ISWRITE(x) ((x)->pb_flags & PBF_WRITE)
+
+#define XFS_BUF_ISUNINITIAL(x) (0)
+#define XFS_BUF_UNUNINITIAL(x) (0)
+
+#define XFS_BUF_BP_ISMAPPED(bp) 1
+
+#define XFS_BUF_DATAIO(x) ((x)->pb_flags |= PBF_FS_DATAIOD)
+#define XFS_BUF_UNDATAIO(x) ((x)->pb_flags &= ~PBF_FS_DATAIOD)
+
+#define XFS_BUF_IODONE_FUNC(buf) (buf)->pb_iodone
+#define XFS_BUF_SET_IODONE_FUNC(buf, func) \
+ (buf)->pb_iodone = (func)
+#define XFS_BUF_CLR_IODONE_FUNC(buf) \
+ (buf)->pb_iodone = NULL
+#define XFS_BUF_SET_BDSTRAT_FUNC(buf, func) \
+ (buf)->pb_strat = (func)
+#define XFS_BUF_CLR_BDSTRAT_FUNC(buf) \
+ (buf)->pb_strat = NULL
+
+#define XFS_BUF_FSPRIVATE(buf, type) \
+ ((type)(buf)->pb_fspriv)
+#define XFS_BUF_SET_FSPRIVATE(buf, value) \
+ (buf)->pb_fspriv = (void *)(value)
+#define XFS_BUF_FSPRIVATE2(buf, type) \
+ ((type)(buf)->pb_fspriv2)
+#define XFS_BUF_SET_FSPRIVATE2(buf, value) \
+ (buf)->pb_fspriv2 = (void *)(value)
+#define XFS_BUF_FSPRIVATE3(buf, type) \
+ ((type)(buf)->pb_fspriv3)
+#define XFS_BUF_SET_FSPRIVATE3(buf, value) \
+ (buf)->pb_fspriv3 = (void *)(value)
+#define XFS_BUF_SET_START(buf)
+
+#define XFS_BUF_SET_BRELSE_FUNC(buf, value) \
+ (buf)->pb_relse = (value)
+
+#define XFS_BUF_PTR(bp) (xfs_caddr_t)((bp)->pb_addr)
+
+extern inline xfs_caddr_t xfs_buf_offset(xfs_buf_t *bp, size_t offset)
+{
+ if (bp->pb_flags & PBF_MAPPED)
+ return XFS_BUF_PTR(bp) + offset;
+ return (xfs_caddr_t) pagebuf_offset(bp, offset);
+}
+
+#define XFS_BUF_SET_PTR(bp, val, count) \
+ pagebuf_associate_memory(bp, val, count)
+#define XFS_BUF_ADDR(bp) ((bp)->pb_bn)
+#define XFS_BUF_SET_ADDR(bp, blk) \
+ ((bp)->pb_bn = (xfs_daddr_t)(blk))
+#define XFS_BUF_OFFSET(bp) ((bp)->pb_file_offset)
+#define XFS_BUF_SET_OFFSET(bp, off) \
+ ((bp)->pb_file_offset = (off))
+#define XFS_BUF_COUNT(bp) ((bp)->pb_count_desired)
+#define XFS_BUF_SET_COUNT(bp, cnt) \
+ ((bp)->pb_count_desired = (cnt))
+#define XFS_BUF_SIZE(bp) ((bp)->pb_buffer_length)
+#define XFS_BUF_SET_SIZE(bp, cnt) \
+ ((bp)->pb_buffer_length = (cnt))
+#define XFS_BUF_SET_VTYPE_REF(bp, type, ref)
+#define XFS_BUF_SET_VTYPE(bp, type)
+#define XFS_BUF_SET_REF(bp, ref)
+
+#define XFS_BUF_ISPINNED(bp) pagebuf_ispin(bp)
+
+#define XFS_BUF_VALUSEMA(bp) pagebuf_lock_value(bp)
+#define XFS_BUF_CPSEMA(bp) (pagebuf_cond_lock(bp) == 0)
+#define XFS_BUF_VSEMA(bp) pagebuf_unlock(bp)
+#define XFS_BUF_PSEMA(bp,x) pagebuf_lock(bp)
+#define XFS_BUF_V_IODONESEMA(bp) up(&bp->pb_iodonesema);
+
+/* setup the buffer target from a buftarg structure */
+#define XFS_BUF_SET_TARGET(bp, target) \
+ (bp)->pb_target = (target)
+#define XFS_BUF_TARGET(bp) ((bp)->pb_target)
+#define XFS_BUFTARG_NAME(target) \
+ pagebuf_target_name(target)
+
+#define XFS_BUF_SET_VTYPE_REF(bp, type, ref)
+#define XFS_BUF_SET_VTYPE(bp, type)
+#define XFS_BUF_SET_REF(bp, ref)
+
+static inline int xfs_bawrite(void *mp, xfs_buf_t *bp)
+{
+ bp->pb_fspriv3 = mp;
+ bp->pb_strat = xfs_bdstrat_cb;
+ pagebuf_delwri_dequeue(bp);
+ return pagebuf_iostart(bp, PBF_WRITE | PBF_ASYNC | _PBF_RUN_QUEUES);
+}
+
+static inline void xfs_buf_relse(xfs_buf_t *bp)
+{
+ if (!bp->pb_relse)
+ pagebuf_unlock(bp);
+ pagebuf_rele(bp);
+}
+
+#define xfs_bpin(bp) pagebuf_pin(bp)
+#define xfs_bunpin(bp) pagebuf_unpin(bp)
+
+#define xfs_buftrace(id, bp) \
+ pagebuf_trace(bp, id, NULL, (void *)__builtin_return_address(0))
+
+#define xfs_biodone(pb) \
+ pagebuf_iodone(pb, (pb->pb_flags & PBF_FS_DATAIOD), 0)
+
+#define xfs_biomove(pb, off, len, data, rw) \
+ pagebuf_iomove((pb), (off), (len), (data), \
+ ((rw) == XFS_B_WRITE) ? PBRW_WRITE : PBRW_READ)
+
+#define xfs_biozero(pb, off, len) \
+ pagebuf_iomove((pb), (off), (len), NULL, PBRW_ZERO)
+
+
+static inline int XFS_bwrite(xfs_buf_t *pb)
+{
+ int iowait = (pb->pb_flags & PBF_ASYNC) == 0;
+ int error = 0;
+
+ if (!iowait)
+ pb->pb_flags |= _PBF_RUN_QUEUES;
+
+ pagebuf_delwri_dequeue(pb);
+ pagebuf_iostrategy(pb);
+ if (iowait) {
+ error = pagebuf_iowait(pb);
+ xfs_buf_relse(pb);
+ }
+ return error;
+}
+
+#define XFS_bdwrite(pb) \
+ pagebuf_iostart(pb, PBF_DELWRI | PBF_ASYNC)
+
+static inline int xfs_bdwrite(void *mp, xfs_buf_t *bp)
+{
+ bp->pb_strat = xfs_bdstrat_cb;
+ bp->pb_fspriv3 = mp;
+
+ return pagebuf_iostart(bp, PBF_DELWRI | PBF_ASYNC);
+}
+
+#define XFS_bdstrat(bp) pagebuf_iorequest(bp)
+
+#define xfs_iowait(pb) pagebuf_iowait(pb)
+
+#define xfs_baread(target, rablkno, ralen) \
+ pagebuf_readahead((target), (rablkno), (ralen), PBF_DONT_BLOCK)
+
+#define xfs_buf_get_empty(len, target) pagebuf_get_empty((len), (target))
+#define xfs_buf_get_noaddr(len, target) pagebuf_get_no_daddr((len), (target))
+#define xfs_buf_free(bp) pagebuf_free(bp)
+
+ /*
+ * Handling of buftargs.
+ */
+
+extern xfs_buftarg_t *xfs_alloc_buftarg(struct block_device *);
+extern void xfs_free_buftarg(xfs_buftarg_t *, int);
+extern void xfs_wait_buftarg(xfs_buftarg_t *);
+extern int xfs_setsize_buftarg(xfs_buftarg_t *, unsigned int, unsigned int);
+extern void xfs_incore_relse(xfs_buftarg_t *, int, int);
+extern int xfs_flush_buftarg(xfs_buftarg_t *, int);
+
+#define xfs_getsize_buftarg(buftarg) \
+ block_size((buftarg)->pbr_kdev)
+#define xfs_readonly_buftarg(buftarg) \
+ is_read_only((buftarg)->pbr_kdev)
+#define xfs_binval(buftarg) \
+ xfs_flush_buftarg(buftarg, 1)
+#define XFS_bflush(buftarg) \
+ xfs_flush_buftarg(buftarg, 1)
+
+#endif /* __XFS_BUF_H__ */
diff --git a/fs/xfs/linux-2.4/xfs_cred.h b/fs/xfs/linux-2.4/xfs_cred.h
new file mode 100644
index 00000000000000..00c45849d41afc
--- /dev/null
+++ b/fs/xfs/linux-2.4/xfs_cred.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_CRED_H__
+#define __XFS_CRED_H__
+
+/*
+ * Credentials
+ */
+typedef struct cred {
+ /* EMPTY */
+} cred_t;
+
+extern struct cred *sys_cred;
+
+/* this is a hack.. (assums sys_cred is the only cred_t in the system) */
+static __inline int capable_cred(cred_t *cr, int cid)
+{
+ return (cr == sys_cred) ? 1 : capable(cid);
+}
+
+#endif /* __XFS_CRED_H__ */
diff --git a/fs/xfs/linux-2.4/xfs_file.c b/fs/xfs/linux-2.4/xfs_file.c
new file mode 100644
index 00000000000000..e1d0649ef244db
--- /dev/null
+++ b/fs/xfs/linux-2.4/xfs_file.c
@@ -0,0 +1,447 @@
+/*
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_sb.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_trans.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_alloc.h"
+#include "xfs_btree.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode.h"
+#include "xfs_error.h"
+#include "xfs_rw.h"
+#include <linux/iobuf.h>
+
+#include <linux/dcache.h>
+#include <linux/smp_lock.h>
+#include <linux/mman.h> /* for PROT_WRITE */
+
+static struct vm_operations_struct linvfs_file_vm_ops;
+
+STATIC inline ssize_t
+__linvfs_read(
+ struct file *file,
+ char *buf,
+ int ioflags,
+ size_t size,
+ loff_t *offset)
+{
+ struct inode *inode = file->f_dentry->d_inode;
+ vnode_t *vp = LINVFS_GET_VP(inode);
+ ssize_t rval;
+
+ if (unlikely(file->f_flags & O_DIRECT)) {
+ ioflags |= IO_ISDIRECT;
+ down_read(&inode->i_alloc_sem);
+ VOP_READ(vp, file, buf, size, offset, ioflags, NULL, rval);
+ up_read(&inode->i_alloc_sem);
+ } else {
+ VOP_READ(vp, file, buf, size, offset, ioflags, NULL, rval);
+ }
+
+ return rval;
+}
+
+STATIC ssize_t
+linvfs_read(
+ struct file *file,
+ char *buf,
+ size_t size,
+ loff_t *offset)
+{
+ return __linvfs_read(file, buf, 0, size, offset);
+}
+
+STATIC ssize_t
+linvfs_read_invis(
+ struct file *file,
+ char *buf,
+ size_t size,
+ loff_t *offset)
+{
+ return __linvfs_read(file, buf, IO_INVIS, size, offset);
+}
+
+
+STATIC inline ssize_t
+__linvfs_write(
+ struct file *file,
+ const char *buf,
+ int ioflags,
+ size_t count,
+ loff_t *ppos)
+{
+ struct inode *inode = file->f_dentry->d_inode;
+ vnode_t *vp = LINVFS_GET_VP(inode);
+ loff_t pos;
+ ssize_t rval; /* Use negative errors in this f'n */
+
+ if ((ssize_t) count < 0)
+ return -EINVAL;
+
+ if (!access_ok(VERIFY_READ, buf, count))
+ return -EFAULT;
+
+ pos = *ppos;
+ if (pos < 0)
+ return -EINVAL;
+
+ rval = file->f_error;
+ if (rval) {
+ file->f_error = 0;
+ return rval;
+ }
+
+ /* We allow multiple direct writers in, there is no
+ * potential call to vmtruncate in that path.
+ */
+ if (unlikely(file->f_flags & O_DIRECT)) {
+ ioflags |= IO_ISDIRECT;
+ down_read(&inode->i_alloc_sem);
+ VOP_WRITE(vp, file, buf, count, &pos, ioflags, NULL, rval);
+ *ppos = pos;
+ up_read(&inode->i_alloc_sem);
+ } else {
+ down(&inode->i_sem);
+ VOP_WRITE(vp, file, buf, count, &pos, ioflags, NULL, rval);
+ *ppos = pos;
+ up(&inode->i_sem);
+ }
+
+ return rval;
+}
+
+STATIC inline ssize_t
+linvfs_write(
+ struct file *file,
+ const char *buf,
+ size_t count,
+ loff_t *ppos)
+{
+ return __linvfs_write(file, buf, 0, count, ppos);
+}
+
+STATIC inline ssize_t
+linvfs_write_invis(
+ struct file *file,
+ const char *buf,
+ size_t count,
+ loff_t *ppos)
+{
+ return __linvfs_write(file, buf, IO_INVIS, count, ppos);
+}
+
+STATIC int
+linvfs_open(
+ struct inode *inode,
+ struct file *filp)
+{
+ vnode_t *vp = LINVFS_GET_VP(inode);
+ int error;
+
+ if (!(filp->f_flags & O_LARGEFILE) && i_size_read(inode) > MAX_NON_LFS)
+ return -EFBIG;
+
+ ASSERT(vp);
+ VOP_OPEN(vp, NULL, error);
+ return -error;
+}
+
+
+STATIC int
+linvfs_release(
+ struct inode *inode,
+ struct file *filp)
+{
+ vnode_t *vp = LINVFS_GET_VP(inode);
+ int error = 0;
+
+ if (vp)
+ VOP_RELEASE(vp, error);
+ return -error;
+}
+
+
+STATIC int
+linvfs_fsync(
+ struct file *filp,
+ struct dentry *dentry,
+ int datasync)
+{
+ struct inode *inode = dentry->d_inode;
+ vnode_t *vp = LINVFS_GET_VP(inode);
+ int error;
+ int flags = FSYNC_WAIT;
+
+ error = fsync_inode_data_buffers(inode);
+ if (error)
+ return error;
+
+ if (datasync)
+ flags |= FSYNC_DATA;
+
+ ASSERT(vp);
+ VOP_FSYNC(vp, flags, NULL, (xfs_off_t)0, (xfs_off_t)-1, error);
+ return -error;
+}
+
+/*
+ * linvfs_readdir maps to VOP_READDIR().
+ * We need to build a uio, cred, ...
+ */
+
+#define nextdp(dp) ((struct xfs_dirent *)((char *)(dp) + (dp)->d_reclen))
+
+STATIC int
+linvfs_readdir(
+ struct file *filp,
+ void *dirent,
+ filldir_t filldir)
+{
+ int error = 0;
+ vnode_t *vp;
+ uio_t uio;
+ iovec_t iov;
+ int eof = 0;
+ caddr_t read_buf;
+ int namelen, size = 0;
+ size_t rlen = PAGE_CACHE_SIZE;
+ xfs_off_t start_offset, curr_offset;
+ xfs_dirent_t *dbp = NULL;
+
+ vp = LINVFS_GET_VP(filp->f_dentry->d_inode);
+ ASSERT(vp);
+
+ /* Try fairly hard to get memory */
+ do {
+ if ((read_buf = (caddr_t)kmalloc(rlen, GFP_KERNEL)))
+ break;
+ rlen >>= 1;
+ } while (rlen >= 1024);
+
+ if (read_buf == NULL)
+ return -ENOMEM;
+
+ uio.uio_iov = &iov;
+ uio.uio_segflg = UIO_SYSSPACE;
+ curr_offset = filp->f_pos;
+ if (filp->f_pos != 0x7fffffff)
+ uio.uio_offset = filp->f_pos;
+ else
+ uio.uio_offset = 0xffffffff;
+
+ while (!eof) {
+ uio.uio_resid = iov.iov_len = rlen;
+ iov.iov_base = read_buf;
+ uio.uio_iovcnt = 1;
+
+ start_offset = uio.uio_offset;
+
+ VOP_READDIR(vp, &uio, NULL, &eof, error);
+ if ((uio.uio_offset == start_offset) || error) {
+ size = 0;
+ break;
+ }
+
+ size = rlen - uio.uio_resid;
+ dbp = (xfs_dirent_t *)read_buf;
+ while (size > 0) {
+ namelen = strlen(dbp->d_name);
+
+ if (filldir(dirent, dbp->d_name, namelen,
+ (loff_t) curr_offset & 0x7fffffff,
+ (ino_t) dbp->d_ino,
+ DT_UNKNOWN)) {
+ goto done;
+ }
+ size -= dbp->d_reclen;
+ curr_offset = (loff_t)dbp->d_off /* & 0x7fffffff */;
+ dbp = nextdp(dbp);
+ }
+ }
+done:
+ if (!error) {
+ if (size == 0)
+ filp->f_pos = uio.uio_offset & 0x7fffffff;
+ else if (dbp)
+ filp->f_pos = curr_offset;
+ }
+
+ kfree(read_buf);
+ return -error;
+}
+
+STATIC int
+linvfs_file_mmap(
+ struct file *filp,
+ struct vm_area_struct *vma)
+{
+ struct inode *ip = filp->f_dentry->d_inode;
+ vnode_t *vp = LINVFS_GET_VP(ip);
+ vattr_t va = { .va_mask = XFS_AT_UPDATIME };
+ int error;
+
+ if (vp->v_vfsp->vfs_flag & VFS_DMI) {
+ xfs_mount_t *mp = XFS_VFSTOM(vp->v_vfsp);
+
+ error = -XFS_SEND_MMAP(mp, vma, 0);
+ if (error)
+ return error;
+ }
+
+ vma->vm_ops = &linvfs_file_vm_ops;
+
+ VOP_SETATTR(vp, &va, XFS_AT_UPDATIME, NULL, error);
+ if (!error)
+ vn_revalidate(vp); /* update Linux inode flags */
+ return 0;
+}
+
+
+STATIC int
+linvfs_ioctl(
+ struct inode *inode,
+ struct file *filp,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ int error;
+ vnode_t *vp = LINVFS_GET_VP(inode);
+
+ unlock_kernel();
+ VOP_IOCTL(vp, inode, filp, 0, cmd, arg, error);
+ VMODIFY(vp);
+ lock_kernel();
+
+ /* NOTE: some of the ioctl's return positive #'s as a
+ * byte count indicating success, such as
+ * readlink_by_handle. So we don't "sign flip"
+ * like most other routines. This means true
+ * errors need to be returned as a negative value.
+ */
+ return error;
+}
+
+STATIC int
+linvfs_ioctl_invis(
+ struct inode *inode,
+ struct file *filp,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ int error;
+ vnode_t *vp = LINVFS_GET_VP(inode);
+
+ unlock_kernel();
+ VOP_IOCTL(vp, inode, filp, IO_INVIS, cmd, arg, error);
+ VMODIFY(vp);
+ lock_kernel();
+
+ /* NOTE: some of the ioctl's return positive #'s as a
+ * byte count indicating success, such as
+ * readlink_by_handle. So we don't "sign flip"
+ * like most other routines. This means true
+ * errors need to be returned as a negative value.
+ */
+ return error;
+}
+
+#ifdef HAVE_VMOP_MPROTECT
+STATIC int
+linvfs_mprotect(
+ struct vm_area_struct *vma,
+ unsigned int newflags)
+{
+ vnode_t *vp = LINVFS_GET_VP(vma->vm_file->f_dentry->d_inode);
+ int error = 0;
+
+ if (vp->v_vfsp->vfs_flag & VFS_DMI) {
+ if ((vma->vm_flags & VM_MAYSHARE) &&
+ (newflags & PROT_WRITE) && !(vma->vm_flags & PROT_WRITE)) {
+ xfs_mount_t *mp = XFS_VFSTOM(vp->v_vfsp);
+
+ error = XFS_SEND_MMAP(mp, vma, VM_WRITE);
+ }
+ }
+ return error;
+}
+#endif /* HAVE_VMOP_MPROTECT */
+
+
+struct file_operations linvfs_file_operations = {
+ .llseek = generic_file_llseek,
+ .read = linvfs_read,
+ .write = linvfs_write,
+ .ioctl = linvfs_ioctl,
+ .mmap = linvfs_file_mmap,
+ .open = linvfs_open,
+ .release = linvfs_release,
+ .fsync = linvfs_fsync,
+};
+
+struct file_operations linvfs_invis_file_operations = {
+ .llseek = generic_file_llseek,
+ .read = linvfs_read_invis,
+ .write = linvfs_write_invis,
+ .ioctl = linvfs_ioctl_invis,
+ .mmap = linvfs_file_mmap,
+ .open = linvfs_open,
+ .release = linvfs_release,
+ .fsync = linvfs_fsync,
+};
+
+
+struct file_operations linvfs_dir_operations = {
+ .read = generic_read_dir,
+ .readdir = linvfs_readdir,
+ .ioctl = linvfs_ioctl,
+ .fsync = linvfs_fsync,
+};
+
+static struct vm_operations_struct linvfs_file_vm_ops = {
+ .nopage = filemap_nopage,
+#ifdef HAVE_VMOP_MPROTECT
+ .mprotect = linvfs_mprotect,
+#endif
+};
diff --git a/fs/xfs/linux-2.4/xfs_fs_subr.c b/fs/xfs/linux-2.4/xfs_fs_subr.c
new file mode 100644
index 00000000000000..69a60ee6140e45
--- /dev/null
+++ b/fs/xfs/linux-2.4/xfs_fs_subr.c
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+
+/*
+ * Stub for no-op vnode operations that return error status.
+ */
+int
+fs_noerr(void)
+{
+ return 0;
+}
+
+/*
+ * Operation unsupported under this file system.
+ */
+int
+fs_nosys(void)
+{
+ return ENOSYS;
+}
+
+/*
+ * Stub for inactive, strategy, and read/write lock/unlock. Does nothing.
+ */
+/* ARGSUSED */
+void
+fs_noval(void)
+{
+}
+
+/*
+ * vnode pcache layer for vnode_tosspages.
+ * 'last' parameter unused but left in for IRIX compatibility
+ */
+void
+fs_tosspages(
+ bhv_desc_t *bdp,
+ xfs_off_t first,
+ xfs_off_t last,
+ int fiopt)
+{
+ vnode_t *vp = BHV_TO_VNODE(bdp);
+ struct inode *ip = LINVFS_GET_IP(vp);
+
+ if (VN_CACHED(vp))
+ truncate_inode_pages(ip->i_mapping, first);
+}
+
+
+/*
+ * vnode pcache layer for vnode_flushinval_pages.
+ * 'last' parameter unused but left in for IRIX compatibility
+ */
+void
+fs_flushinval_pages(
+ bhv_desc_t *bdp,
+ xfs_off_t first,
+ xfs_off_t last,
+ int fiopt)
+{
+ vnode_t *vp = BHV_TO_VNODE(bdp);
+ struct inode *ip = LINVFS_GET_IP(vp);
+
+ if (VN_CACHED(vp)) {
+ filemap_fdatasync(ip->i_mapping);
+ fsync_inode_data_buffers(ip);
+ filemap_fdatawait(ip->i_mapping);
+
+ truncate_inode_pages(ip->i_mapping, first);
+ }
+}
+
+/*
+ * vnode pcache layer for vnode_flush_pages.
+ * 'last' parameter unused but left in for IRIX compatibility
+ */
+int
+fs_flush_pages(
+ bhv_desc_t *bdp,
+ xfs_off_t first,
+ xfs_off_t last,
+ uint64_t flags,
+ int fiopt)
+{
+ vnode_t *vp = BHV_TO_VNODE(bdp);
+ struct inode *ip = LINVFS_GET_IP(vp);
+
+ if (VN_CACHED(vp)) {
+ filemap_fdatasync(ip->i_mapping);
+ fsync_inode_data_buffers(ip);
+ filemap_fdatawait(ip->i_mapping);
+ }
+
+ return 0;
+}
diff --git a/fs/xfs/linux-2.4/xfs_fs_subr.h b/fs/xfs/linux-2.4/xfs_fs_subr.h
new file mode 100644
index 00000000000000..2db9ddbd456754
--- /dev/null
+++ b/fs/xfs/linux-2.4/xfs_fs_subr.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2000, 2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_SUBR_H__
+#define __XFS_SUBR_H__
+
+/*
+ * Utilities shared among file system implementations.
+ */
+
+struct cred;
+
+extern int fs_noerr(void);
+extern int fs_nosys(void);
+extern void fs_noval(void);
+extern void fs_tosspages(bhv_desc_t *, xfs_off_t, xfs_off_t, int);
+extern void fs_flushinval_pages(bhv_desc_t *, xfs_off_t, xfs_off_t, int);
+extern int fs_flush_pages(bhv_desc_t *, xfs_off_t, xfs_off_t, uint64_t, int);
+
+#endif /* __XFS_FS_SUBR_H__ */
diff --git a/fs/xfs/linux-2.4/xfs_globals.c b/fs/xfs/linux-2.4/xfs_globals.c
new file mode 100644
index 00000000000000..a07a49fff84d5c
--- /dev/null
+++ b/fs/xfs/linux-2.4/xfs_globals.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+/*
+ * This file contains globals needed by XFS that were normally defined
+ * somewhere else in IRIX.
+ */
+
+#include "xfs.h"
+#include "xfs_cred.h"
+#include "xfs_sysctl.h"
+#include "xfs_refcache.h"
+
+/*
+ * System memory size - used to scale certain data structures in XFS.
+ */
+unsigned long xfs_physmem;
+
+/*
+ * Tunable XFS parameters. xfs_params is required even when CONFIG_SYSCTL=n,
+ * other XFS code uses these values. Times are measured in centisecs (i.e.
+ * 100ths of a second).
+ */
+xfs_param_t xfs_params = {
+ /* MIN DFLT MAX */
+ .refcache_size = { 0, 128, XFS_REFCACHE_SIZE_MAX },
+ .refcache_purge = { 0, 32, XFS_REFCACHE_SIZE_MAX },
+ .restrict_chown = { 0, 1, 1 },
+ .sgid_inherit = { 0, 0, 1 },
+ .symlink_mode = { 0, 0, 1 },
+ .panic_mask = { 0, 0, 127 },
+ .error_level = { 0, 3, 11 },
+ .syncd_timer = { 1*100, 30*100, 60*100 },
+ .stats_clear = { 0, 0, 1 },
+ .inherit_sync = { 0, 1, 1 },
+ .inherit_nodump = { 0, 1, 1 },
+ .inherit_noatim = { 0, 1, 1 },
+ .xfs_buf_timer = { 100/2, 100, 30*100 },
+ .xfs_buf_age = { 1*100, 15*100, 300*100 },
+ .inherit_nosym = { 0, 0, 1 },
+ .rotorstep = { 1, 1, 255 },
+};
+
+/*
+ * Global system credential structure.
+ */
+cred_t sys_cred_val, *sys_cred = &sys_cred_val;
+
diff --git a/fs/xfs/linux-2.4/xfs_globals.h b/fs/xfs/linux-2.4/xfs_globals.h
new file mode 100644
index 00000000000000..e81e2f38a8534b
--- /dev/null
+++ b/fs/xfs/linux-2.4/xfs_globals.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_GLOBALS_H__
+#define __XFS_GLOBALS_H__
+
+/*
+ * This file declares globals needed by XFS that were normally defined
+ * somewhere else in IRIX.
+ */
+
+extern uint64_t xfs_panic_mask; /* set to cause more panics */
+extern unsigned long xfs_physmem;
+extern struct cred *sys_cred;
+
+#endif /* __XFS_GLOBALS_H__ */
diff --git a/fs/xfs/linux-2.4/xfs_ioctl.c b/fs/xfs/linux-2.4/xfs_ioctl.c
new file mode 100644
index 00000000000000..859319fe5ec201
--- /dev/null
+++ b/fs/xfs/linux-2.4/xfs_ioctl.c
@@ -0,0 +1,1254 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+
+#include "xfs_fs.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_alloc.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode.h"
+#include "xfs_bmap.h"
+#include "xfs_bit.h"
+#include "xfs_rtalloc.h"
+#include "xfs_error.h"
+#include "xfs_itable.h"
+#include "xfs_rw.h"
+#include "xfs_acl.h"
+#include "xfs_cap.h"
+#include "xfs_mac.h"
+#include "xfs_attr.h"
+#include "xfs_buf_item.h"
+#include "xfs_utils.h"
+#include "xfs_dfrag.h"
+#include "xfs_fsops.h"
+
+#include <linux/dcache.h>
+#include <linux/iobuf.h>
+
+/*
+ * xfs_find_handle maps from userspace xfs_fsop_handlereq structure to
+ * a file or fs handle.
+ *
+ * XFS_IOC_PATH_TO_FSHANDLE
+ * returns fs handle for a mount point or path within that mount point
+ * XFS_IOC_FD_TO_HANDLE
+ * returns full handle for a FD opened in user space
+ * XFS_IOC_PATH_TO_HANDLE
+ * returns full handle for a path
+ */
+STATIC int
+xfs_find_handle(
+ unsigned int cmd,
+ unsigned long arg)
+{
+ int hsize;
+ xfs_handle_t handle;
+ xfs_fsop_handlereq_t hreq;
+ struct inode *inode;
+ struct vnode *vp;
+
+ if (copy_from_user(&hreq, (xfs_fsop_handlereq_t *)arg, sizeof(hreq)))
+ return -XFS_ERROR(EFAULT);
+
+ memset((char *)&handle, 0, sizeof(handle));
+
+ switch (cmd) {
+ case XFS_IOC_PATH_TO_FSHANDLE:
+ case XFS_IOC_PATH_TO_HANDLE: {
+ struct nameidata nd;
+ int error;
+
+ error = user_path_walk_link(hreq.path, &nd);
+ if (error)
+ return error;
+
+ ASSERT(nd.dentry);
+ ASSERT(nd.dentry->d_inode);
+ inode = igrab(nd.dentry->d_inode);
+ path_release(&nd);
+ break;
+ }
+
+ case XFS_IOC_FD_TO_HANDLE: {
+ struct file *file;
+
+ file = fget(hreq.fd);
+ if (!file)
+ return -EBADF;
+
+ ASSERT(file->f_dentry);
+ ASSERT(file->f_dentry->d_inode);
+ inode = igrab(file->f_dentry->d_inode);
+ fput(file);
+ break;
+ }
+
+ default:
+ ASSERT(0);
+ return -XFS_ERROR(EINVAL);
+ }
+
+ if (inode->i_sb->s_magic != XFS_SB_MAGIC) {
+ /* we're not in XFS anymore, Toto */
+ iput(inode);
+ return -XFS_ERROR(EINVAL);
+ }
+
+ /* we need the vnode */
+ vp = LINVFS_GET_VP(inode);
+ if (vp->v_type != VREG && vp->v_type != VDIR && vp->v_type != VLNK) {
+ iput(inode);
+ return -XFS_ERROR(EBADF);
+ }
+
+ /* now we can grab the fsid */
+ memcpy(&handle.ha_fsid, vp->v_vfsp->vfs_altfsid, sizeof(xfs_fsid_t));
+ hsize = sizeof(xfs_fsid_t);
+
+ if (cmd != XFS_IOC_PATH_TO_FSHANDLE) {
+ xfs_inode_t *ip;
+ bhv_desc_t *bhv;
+ int lock_mode;
+
+ /* need to get access to the xfs_inode to read the generation */
+ bhv = vn_bhv_lookup_unlocked(VN_BHV_HEAD(vp), &xfs_vnodeops);
+ ASSERT(bhv);
+ ip = XFS_BHVTOI(bhv);
+ ASSERT(ip);
+ lock_mode = xfs_ilock_map_shared(ip);
+
+ /* fill in fid section of handle from inode */
+ handle.ha_fid.xfs_fid_len = sizeof(xfs_fid_t) -
+ sizeof(handle.ha_fid.xfs_fid_len);
+ handle.ha_fid.xfs_fid_pad = 0;
+ handle.ha_fid.xfs_fid_gen = ip->i_d.di_gen;
+ handle.ha_fid.xfs_fid_ino = ip->i_ino;
+
+ xfs_iunlock_map_shared(ip, lock_mode);
+
+ hsize = XFS_HSIZE(handle);
+ }
+
+ /* now copy our handle into the user buffer & write out the size */
+ if (copy_to_user((xfs_handle_t *)hreq.ohandle, &handle, hsize) ||
+ copy_to_user(hreq.ohandlen, &hsize, sizeof(__s32))) {
+ iput(inode);
+ return -XFS_ERROR(EFAULT);
+ }
+
+ iput(inode);
+ return 0;
+}
+
+
+/*
+ * Convert userspace handle data into vnode (and inode).
+ * We [ab]use the fact that all the fsop_handlereq ioctl calls
+ * have a data structure argument whose first component is always
+ * a xfs_fsop_handlereq_t, so we can cast to and from this type.
+ * This allows us to optimise the copy_from_user calls and gives
+ * a handy, shared routine.
+ *
+ * If no error, caller must always VN_RELE the returned vp.
+ */
+STATIC int
+xfs_vget_fsop_handlereq(
+ xfs_mount_t *mp,
+ struct inode *parinode, /* parent inode pointer */
+ int cap, /* capability level for op */
+ unsigned long arg, /* userspace data pointer */
+ unsigned long size, /* size of expected struct */
+ /* output arguments */
+ xfs_fsop_handlereq_t *hreq,
+ vnode_t **vp,
+ struct inode **inode)
+{
+ void *hanp;
+ size_t hlen;
+ xfs_fid_t *xfid;
+ xfs_handle_t *handlep;
+ xfs_handle_t handle;
+ xfs_inode_t *ip;
+ struct inode *inodep;
+ vnode_t *vpp;
+ xfs_ino_t ino;
+ __u32 igen;
+ int error;
+
+ if (!capable(cap))
+ return XFS_ERROR(EPERM);
+
+ /*
+ * Only allow handle opens under a directory.
+ */
+ if (!S_ISDIR(parinode->i_mode))
+ return XFS_ERROR(ENOTDIR);
+
+ /*
+ * Copy the handle down from the user and validate
+ * that it looks to be in the correct format.
+ */
+ if (copy_from_user(hreq, (struct xfs_fsop_handlereq *)arg, size))
+ return XFS_ERROR(EFAULT);
+
+ hanp = hreq->ihandle;
+ hlen = hreq->ihandlen;
+ handlep = &handle;
+
+ if (hlen < sizeof(handlep->ha_fsid) || hlen > sizeof(*handlep))
+ return XFS_ERROR(EINVAL);
+ if (copy_from_user(handlep, hanp, hlen))
+ return XFS_ERROR(EFAULT);
+ if (hlen < sizeof(*handlep))
+ memset(((char *)handlep) + hlen, 0, sizeof(*handlep) - hlen);
+ if (hlen > sizeof(handlep->ha_fsid)) {
+ if (handlep->ha_fid.xfs_fid_len !=
+ (hlen - sizeof(handlep->ha_fsid)
+ - sizeof(handlep->ha_fid.xfs_fid_len))
+ || handlep->ha_fid.xfs_fid_pad)
+ return XFS_ERROR(EINVAL);
+ }
+
+ /*
+ * Crack the handle, obtain the inode # & generation #
+ */
+ xfid = (struct xfs_fid *)&handlep->ha_fid;
+ if (xfid->xfs_fid_len == sizeof(*xfid) - sizeof(xfid->xfs_fid_len)) {
+ ino = xfid->xfs_fid_ino;
+ igen = xfid->xfs_fid_gen;
+ } else {
+ return XFS_ERROR(EINVAL);
+ }
+
+ /*
+ * Get the XFS inode, building a vnode to go with it.
+ */
+ error = xfs_iget(mp, NULL, ino, 0, XFS_ILOCK_SHARED, &ip, 0);
+ if (error)
+ return error;
+ if (ip == NULL)
+ return XFS_ERROR(EIO);
+ if (ip->i_d.di_mode == 0 || ip->i_d.di_gen != igen) {
+ xfs_iput_new(ip, XFS_ILOCK_SHARED);
+ return XFS_ERROR(ENOENT);
+ }
+
+ vpp = XFS_ITOV(ip);
+ inodep = LINVFS_GET_IP(vpp);
+ xfs_iunlock(ip, XFS_ILOCK_SHARED);
+
+ *vp = vpp;
+ *inode = inodep;
+ return 0;
+}
+
+STATIC int
+xfs_open_by_handle(
+ xfs_mount_t *mp,
+ unsigned long arg,
+ struct file *parfilp,
+ struct inode *parinode)
+{
+ int error;
+ int new_fd;
+ int permflag;
+ struct file *filp;
+ struct inode *inode;
+ struct dentry *dentry;
+ vnode_t *vp;
+ xfs_fsop_handlereq_t hreq;
+
+ error = xfs_vget_fsop_handlereq(mp, parinode, CAP_SYS_ADMIN, arg,
+ sizeof(xfs_fsop_handlereq_t),
+ &hreq, &vp, &inode);
+ if (error)
+ return -error;
+
+ /* Restrict xfs_open_by_handle to directories & regular files. */
+ if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))) {
+ iput(inode);
+ return -XFS_ERROR(EINVAL);
+ }
+
+#if BITS_PER_LONG != 32
+ hreq.oflags |= O_LARGEFILE;
+#endif
+ /* Put open permission in namei format. */
+ permflag = hreq.oflags;
+ if ((permflag+1) & O_ACCMODE)
+ permflag++;
+ if (permflag & O_TRUNC)
+ permflag |= 2;
+
+ if ((!(permflag & O_APPEND) || (permflag & O_TRUNC)) &&
+ (permflag & FMODE_WRITE) && IS_APPEND(inode)) {
+ iput(inode);
+ return -XFS_ERROR(EPERM);
+ }
+
+ if ((permflag & FMODE_WRITE) && IS_IMMUTABLE(inode)) {
+ iput(inode);
+ return -XFS_ERROR(EACCES);
+ }
+
+ /* Can't write directories. */
+ if ( S_ISDIR(inode->i_mode) && (permflag & FMODE_WRITE)) {
+ iput(inode);
+ return -XFS_ERROR(EISDIR);
+ }
+
+ if ((new_fd = get_unused_fd()) < 0) {
+ iput(inode);
+ return new_fd;
+ }
+
+ dentry = d_alloc_anon(inode);
+ if (dentry == NULL) {
+ iput(inode);
+ put_unused_fd(new_fd);
+ return -XFS_ERROR(ENOMEM);
+ }
+
+ /* Ensure umount returns EBUSY on umounts while this file is open. */
+ mntget(parfilp->f_vfsmnt);
+
+ /* Create file pointer. */
+ filp = dentry_open(dentry, parfilp->f_vfsmnt, hreq.oflags);
+ if (IS_ERR(filp)) {
+ put_unused_fd(new_fd);
+ return -XFS_ERROR(-PTR_ERR(filp));
+ }
+ if (inode->i_mode & S_IFREG)
+ filp->f_op = &linvfs_invis_file_operations;
+
+ fd_install(new_fd, filp);
+ return new_fd;
+}
+
+STATIC int
+xfs_readlink_by_handle(
+ xfs_mount_t *mp,
+ unsigned long arg,
+ struct file *parfilp,
+ struct inode *parinode)
+{
+ int error;
+ struct iovec aiov;
+ struct uio auio;
+ struct inode *inode;
+ xfs_fsop_handlereq_t hreq;
+ vnode_t *vp;
+ __u32 olen;
+
+ error = xfs_vget_fsop_handlereq(mp, parinode, CAP_SYS_ADMIN, arg,
+ sizeof(xfs_fsop_handlereq_t),
+ &hreq, &vp, &inode);
+ if (error)
+ return -error;
+
+ /* Restrict this handle operation to symlinks only. */
+ if (vp->v_type != VLNK) {
+ VN_RELE(vp);
+ return -XFS_ERROR(EINVAL);
+ }
+
+ if (copy_from_user(&olen, hreq.ohandlen, sizeof(__u32))) {
+ VN_RELE(vp);
+ return -XFS_ERROR(EFAULT);
+ }
+ aiov.iov_len = olen;
+ aiov.iov_base = hreq.ohandle;
+
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_offset = 0;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_resid = olen;
+
+ VOP_READLINK(vp, &auio, IO_INVIS, NULL, error);
+
+ VN_RELE(vp);
+ return (olen - auio.uio_resid);
+}
+
+STATIC int
+xfs_fssetdm_by_handle(
+ xfs_mount_t *mp,
+ unsigned long arg,
+ struct file *parfilp,
+ struct inode *parinode)
+{
+ int error;
+ struct fsdmidata fsd;
+ xfs_fsop_setdm_handlereq_t dmhreq;
+ struct inode *inode;
+ bhv_desc_t *bdp;
+ vnode_t *vp;
+
+ error = xfs_vget_fsop_handlereq(mp, parinode, CAP_MKNOD, arg,
+ sizeof(xfs_fsop_setdm_handlereq_t),
+ (xfs_fsop_handlereq_t *)&dmhreq,
+ &vp, &inode);
+ if (error)
+ return -error;
+
+ if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) {
+ VN_RELE(vp);
+ return -XFS_ERROR(EPERM);
+ }
+
+ if (copy_from_user(&fsd, dmhreq.data, sizeof(fsd))) {
+ VN_RELE(vp);
+ return -XFS_ERROR(EFAULT);
+ }
+
+ bdp = bhv_base_unlocked(VN_BHV_HEAD(vp));
+ error = xfs_set_dmattrs(bdp, fsd.fsd_dmevmask, fsd.fsd_dmstate, NULL);
+
+ VN_RELE(vp);
+ if (error)
+ return -error;
+ return 0;
+}
+
+STATIC int
+xfs_attrlist_by_handle(
+ xfs_mount_t *mp,
+ unsigned long arg,
+ struct file *parfilp,
+ struct inode *parinode)
+{
+ int error;
+ attrlist_cursor_kern_t *cursor;
+ xfs_fsop_attrlist_handlereq_t al_hreq;
+ struct inode *inode;
+ vnode_t *vp;
+
+ error = xfs_vget_fsop_handlereq(mp, parinode, CAP_SYS_ADMIN, arg,
+ sizeof(xfs_fsop_attrlist_handlereq_t),
+ (xfs_fsop_handlereq_t *)&al_hreq,
+ &vp, &inode);
+ if (error)
+ return -error;
+
+ cursor = (attrlist_cursor_kern_t *)&al_hreq.pos;
+ VOP_ATTR_LIST(vp, al_hreq.buffer, al_hreq.buflen, al_hreq.flags,
+ cursor, NULL, error);
+ VN_RELE(vp);
+ if (error)
+ return -error;
+ return 0;
+}
+
+STATIC int
+xfs_attrmulti_by_handle(
+ xfs_mount_t *mp,
+ unsigned long arg,
+ struct file *parfilp,
+ struct inode *parinode)
+{
+ int error;
+ xfs_attr_multiop_t *ops;
+ xfs_fsop_attrmulti_handlereq_t am_hreq;
+ struct inode *inode;
+ vnode_t *vp;
+ unsigned int i, size;
+
+ error = xfs_vget_fsop_handlereq(mp, parinode, CAP_SYS_ADMIN, arg,
+ sizeof(xfs_fsop_attrmulti_handlereq_t),
+ (xfs_fsop_handlereq_t *)&am_hreq,
+ &vp, &inode);
+ if (error)
+ return -error;
+
+ size = am_hreq.opcount * sizeof(attr_multiop_t);
+ if (!size || size > 16 * PAGE_SIZE) {
+ VN_RELE(vp);
+ return -XFS_ERROR(E2BIG);
+ }
+
+ ops = (xfs_attr_multiop_t *)kmalloc(size, GFP_KERNEL);
+ if (!ops) {
+ VN_RELE(vp);
+ return -XFS_ERROR(ENOMEM);
+ }
+
+ if (copy_from_user(ops, am_hreq.ops, size)) {
+ kfree(ops);
+ VN_RELE(vp);
+ return -XFS_ERROR(EFAULT);
+ }
+
+ for (i = 0; i < am_hreq.opcount; i++) {
+ switch(ops[i].am_opcode) {
+ case ATTR_OP_GET:
+ VOP_ATTR_GET(vp,ops[i].am_attrname, ops[i].am_attrvalue,
+ &ops[i].am_length, ops[i].am_flags,
+ NULL, ops[i].am_error);
+ break;
+ case ATTR_OP_SET:
+ if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) {
+ ops[i].am_error = EPERM;
+ break;
+ }
+ VOP_ATTR_SET(vp,ops[i].am_attrname, ops[i].am_attrvalue,
+ ops[i].am_length, ops[i].am_flags,
+ NULL, ops[i].am_error);
+ break;
+ case ATTR_OP_REMOVE:
+ if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) {
+ ops[i].am_error = EPERM;
+ break;
+ }
+ VOP_ATTR_REMOVE(vp, ops[i].am_attrname, ops[i].am_flags,
+ NULL, ops[i].am_error);
+ break;
+ default:
+ ops[i].am_error = EINVAL;
+ }
+ }
+
+ if (copy_to_user(am_hreq.ops, ops, size))
+ error = -XFS_ERROR(EFAULT);
+
+ kfree(ops);
+ VN_RELE(vp);
+ return error;
+}
+
+/* prototypes for a few of the stack-hungry cases that have
+ * their own functions. Functions are defined after their use
+ * so gcc doesn't get fancy and inline them with -03 */
+
+STATIC int
+xfs_ioc_space(
+ bhv_desc_t *bdp,
+ vnode_t *vp,
+ struct file *filp,
+ int flags,
+ unsigned int cmd,
+ unsigned long arg);
+
+STATIC int
+xfs_ioc_bulkstat(
+ xfs_mount_t *mp,
+ unsigned int cmd,
+ unsigned long arg);
+
+STATIC int
+xfs_ioc_fsgeometry_v1(
+ xfs_mount_t *mp,
+ unsigned long arg);
+
+STATIC int
+xfs_ioc_fsgeometry(
+ xfs_mount_t *mp,
+ unsigned long arg);
+
+STATIC int
+xfs_ioc_xattr(
+ vnode_t *vp,
+ xfs_inode_t *ip,
+ struct file *filp,
+ unsigned int cmd,
+ unsigned long arg);
+
+STATIC int
+xfs_ioc_getbmap(
+ bhv_desc_t *bdp,
+ struct file *filp,
+ int flags,
+ unsigned int cmd,
+ unsigned long arg);
+
+STATIC int
+xfs_ioc_getbmapx(
+ bhv_desc_t *bdp,
+ unsigned long arg);
+
+int
+xfs_ioctl(
+ bhv_desc_t *bdp,
+ struct inode *inode,
+ struct file *filp,
+ int ioflags,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ int error;
+ vnode_t *vp;
+ xfs_inode_t *ip;
+ xfs_mount_t *mp;
+
+ vp = LINVFS_GET_VP(inode);
+
+ vn_trace_entry(vp, "xfs_ioctl", (inst_t *)__return_address);
+
+ ip = XFS_BHVTOI(bdp);
+ mp = ip->i_mount;
+
+ switch (cmd) {
+
+ case XFS_IOC_ALLOCSP:
+ case XFS_IOC_FREESP:
+ case XFS_IOC_RESVSP:
+ case XFS_IOC_UNRESVSP:
+ case XFS_IOC_ALLOCSP64:
+ case XFS_IOC_FREESP64:
+ case XFS_IOC_RESVSP64:
+ case XFS_IOC_UNRESVSP64:
+ /*
+ * Only allow the sys admin to reserve space unless
+ * unwritten extents are enabled.
+ */
+ if (!XFS_SB_VERSION_HASEXTFLGBIT(&mp->m_sb) &&
+ !capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ return xfs_ioc_space(bdp, vp, filp, ioflags, cmd, arg);
+
+ case XFS_IOC_DIOINFO: {
+ struct dioattr da;
+
+ da.d_miniosz = mp->m_sb.sb_blocksize;
+ da.d_mem = mp->m_sb.sb_blocksize;
+
+ /*
+ * this only really needs to be BBSIZE.
+ * it is set to the file system block size to
+ * avoid having to do block zeroing on short writes.
+ */
+ da.d_maxiosz = XFS_FSB_TO_B(mp,
+ XFS_B_TO_FSBT(mp, KIO_MAX_ATOMIC_IO << 10));
+
+ if (copy_to_user((struct dioattr *)arg, &da, sizeof(da)))
+ return -XFS_ERROR(EFAULT);
+ return 0;
+ }
+
+ case XFS_IOC_FSBULKSTAT_SINGLE:
+ case XFS_IOC_FSBULKSTAT:
+ case XFS_IOC_FSINUMBERS:
+ return xfs_ioc_bulkstat(mp, cmd, arg);
+
+ case XFS_IOC_FSGEOMETRY_V1:
+ return xfs_ioc_fsgeometry_v1(mp, arg);
+
+ case XFS_IOC_FSGEOMETRY:
+ return xfs_ioc_fsgeometry(mp, arg);
+
+ case XFS_IOC_GETVERSION:
+ case XFS_IOC_GETXFLAGS:
+ case XFS_IOC_SETXFLAGS:
+ case XFS_IOC_FSGETXATTR:
+ case XFS_IOC_FSSETXATTR:
+ case XFS_IOC_FSGETXATTRA:
+ return xfs_ioc_xattr(vp, ip, filp, cmd, arg);
+
+ case XFS_IOC_FSSETDM: {
+ struct fsdmidata dmi;
+
+ if (copy_from_user(&dmi, (struct fsdmidata *)arg, sizeof(dmi)))
+ return -XFS_ERROR(EFAULT);
+
+ error = xfs_set_dmattrs(bdp, dmi.fsd_dmevmask, dmi.fsd_dmstate,
+ NULL);
+ return -error;
+ }
+
+ case XFS_IOC_GETBMAP:
+ case XFS_IOC_GETBMAPA:
+ return xfs_ioc_getbmap(bdp, filp, ioflags, cmd, arg);
+
+ case XFS_IOC_GETBMAPX:
+ return xfs_ioc_getbmapx(bdp, arg);
+
+ case XFS_IOC_FD_TO_HANDLE:
+ case XFS_IOC_PATH_TO_HANDLE:
+ case XFS_IOC_PATH_TO_FSHANDLE:
+ return xfs_find_handle(cmd, arg);
+
+ case XFS_IOC_OPEN_BY_HANDLE:
+ return xfs_open_by_handle(mp, arg, filp, inode);
+
+ case XFS_IOC_FSSETDM_BY_HANDLE:
+ return xfs_fssetdm_by_handle(mp, arg, filp, inode);
+
+ case XFS_IOC_READLINK_BY_HANDLE:
+ return xfs_readlink_by_handle(mp, arg, filp, inode);
+
+ case XFS_IOC_ATTRLIST_BY_HANDLE:
+ return xfs_attrlist_by_handle(mp, arg, filp, inode);
+
+ case XFS_IOC_ATTRMULTI_BY_HANDLE:
+ return xfs_attrmulti_by_handle(mp, arg, filp, inode);
+
+ case XFS_IOC_SWAPEXT: {
+ error = xfs_swapext((struct xfs_swapext *)arg);
+ return -error;
+ }
+
+ case XFS_IOC_FSCOUNTS: {
+ xfs_fsop_counts_t out;
+
+ error = xfs_fs_counts(mp, &out);
+ if (error)
+ return -error;
+
+ if (copy_to_user((char *)arg, &out, sizeof(out)))
+ return -XFS_ERROR(EFAULT);
+ return 0;
+ }
+
+ case XFS_IOC_SET_RESBLKS: {
+ xfs_fsop_resblks_t inout;
+ __uint64_t in;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (copy_from_user(&inout, (char *)arg, sizeof(inout)))
+ return -XFS_ERROR(EFAULT);
+
+ /* input parameter is passed in resblks field of structure */
+ in = inout.resblks;
+ error = xfs_reserve_blocks(mp, &in, &inout);
+ if (error)
+ return -error;
+
+ if (copy_to_user((char *)arg, &inout, sizeof(inout)))
+ return -XFS_ERROR(EFAULT);
+ return 0;
+ }
+
+ case XFS_IOC_GET_RESBLKS: {
+ xfs_fsop_resblks_t out;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ error = xfs_reserve_blocks(mp, NULL, &out);
+ if (error)
+ return -error;
+
+ if (copy_to_user((char *)arg, &out, sizeof(out)))
+ return -XFS_ERROR(EFAULT);
+
+ return 0;
+ }
+
+ case XFS_IOC_FSGROWFSDATA: {
+ xfs_growfs_data_t in;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (copy_from_user(&in, (char *)arg, sizeof(in)))
+ return -XFS_ERROR(EFAULT);
+
+ error = xfs_growfs_data(mp, &in);
+ return -error;
+ }
+
+ case XFS_IOC_FSGROWFSLOG: {
+ xfs_growfs_log_t in;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (copy_from_user(&in, (char *)arg, sizeof(in)))
+ return -XFS_ERROR(EFAULT);
+
+ error = xfs_growfs_log(mp, &in);
+ return -error;
+ }
+
+ case XFS_IOC_FSGROWFSRT: {
+ xfs_growfs_rt_t in;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (copy_from_user(&in, (char *)arg, sizeof(in)))
+ return -XFS_ERROR(EFAULT);
+
+ error = xfs_growfs_rt(mp, &in);
+ return -error;
+ }
+
+ case XFS_IOC_FREEZE:
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (vp->v_vfsp->vfs_frozen == SB_UNFROZEN) {
+ freeze_bdev(mp->m_ddev_targp->pbr_bdev);
+ if (mp->m_rtdev_targp)
+ freeze_bdev(mp->m_rtdev_targp->pbr_bdev);
+ }
+ return 0;
+
+ case XFS_IOC_THAW:
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (vp->v_vfsp->vfs_frozen != SB_UNFROZEN) {
+ thaw_bdev(mp->m_ddev_targp->pbr_bdev, inode->i_sb);
+ if (mp->m_rtdev_targp)
+ thaw_bdev(mp->m_ddev_targp->pbr_bdev, NULL);
+ }
+ return 0;
+
+ case XFS_IOC_GOINGDOWN: {
+ __uint32_t in;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (get_user(in, (__uint32_t *)arg))
+ return -XFS_ERROR(EFAULT);
+
+ error = xfs_fs_goingdown(mp, in);
+ return -error;
+ }
+
+ case XFS_IOC_ERROR_INJECTION: {
+ xfs_error_injection_t in;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return EPERM;
+
+ if (copy_from_user(&in, (char *)arg, sizeof(in)))
+ return -XFS_ERROR(EFAULT);
+
+ error = xfs_errortag_add(in.errtag, mp);
+ return -error;
+ }
+
+ case XFS_IOC_ERROR_CLEARALL:
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ error = xfs_errortag_clearall(mp);
+ return -error;
+
+ default:
+ return -ENOTTY;
+ }
+}
+
+STATIC int
+xfs_ioc_space(
+ bhv_desc_t *bdp,
+ vnode_t *vp,
+ struct file *filp,
+ int ioflags,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ xfs_flock64_t bf;
+ int attr_flags = 0;
+ int error;
+
+ if (vp->v_inode.i_flags & (S_IMMUTABLE|S_APPEND))
+ return -XFS_ERROR(EPERM);
+
+ if (!(filp->f_flags & FMODE_WRITE))
+ return -XFS_ERROR(EBADF);
+
+ if (vp->v_type != VREG)
+ return -XFS_ERROR(EINVAL);
+
+ if (copy_from_user(&bf, (xfs_flock64_t *)arg, sizeof(bf)))
+ return -XFS_ERROR(EFAULT);
+
+ if (filp->f_flags & (O_NDELAY|O_NONBLOCK))
+ attr_flags |= ATTR_NONBLOCK;
+ if (ioflags & IO_INVIS)
+ attr_flags |= ATTR_DMI;
+
+ error = xfs_change_file_space(bdp, cmd, &bf, filp->f_pos,
+ NULL, attr_flags);
+ return -error;
+}
+
+STATIC int
+xfs_ioc_bulkstat(
+ xfs_mount_t *mp,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ xfs_fsop_bulkreq_t bulkreq;
+ int count; /* # of records returned */
+ xfs_ino_t inlast; /* last inode number */
+ int done;
+ int error;
+
+ /* done = 1 if there are more stats to get and if bulkstat */
+ /* should be called again (unused here, but used in dmapi) */
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (XFS_FORCED_SHUTDOWN(mp))
+ return -XFS_ERROR(EIO);
+
+ if (copy_from_user(&bulkreq, (xfs_fsop_bulkreq_t *)arg,
+ sizeof(xfs_fsop_bulkreq_t)))
+ return -XFS_ERROR(EFAULT);
+
+ if (copy_from_user(&inlast, (__s64 *)bulkreq.lastip,
+ sizeof(__s64)))
+ return -XFS_ERROR(EFAULT);
+
+ if ((count = bulkreq.icount) <= 0)
+ return -XFS_ERROR(EINVAL);
+
+ if (cmd == XFS_IOC_FSINUMBERS)
+ error = xfs_inumbers(mp, &inlast, &count,
+ bulkreq.ubuffer);
+ else if (cmd == XFS_IOC_FSBULKSTAT_SINGLE)
+ error = xfs_bulkstat_single(mp, &inlast,
+ bulkreq.ubuffer, &done);
+ else { /* XFS_IOC_FSBULKSTAT */
+ if (count == 1 && inlast != 0) {
+ inlast++;
+ error = xfs_bulkstat_single(mp, &inlast,
+ bulkreq.ubuffer, &done);
+ } else {
+ error = xfs_bulkstat(mp, &inlast, &count,
+ (bulkstat_one_pf)xfs_bulkstat_one, NULL,
+ sizeof(xfs_bstat_t), bulkreq.ubuffer,
+ BULKSTAT_FG_QUICK, &done);
+ }
+ }
+
+ if (error)
+ return -error;
+
+ if (bulkreq.ocount != NULL) {
+ if (copy_to_user((xfs_ino_t *)bulkreq.lastip, &inlast,
+ sizeof(xfs_ino_t)))
+ return -XFS_ERROR(EFAULT);
+
+ if (copy_to_user((__s32 *)bulkreq.ocount, &count,
+ sizeof(count)))
+ return -XFS_ERROR(EFAULT);
+ }
+
+ return 0;
+}
+
+STATIC int
+xfs_ioc_fsgeometry_v1(
+ xfs_mount_t *mp,
+ unsigned long arg)
+{
+ xfs_fsop_geom_v1_t fsgeo;
+ int error;
+
+ error = xfs_fs_geometry(mp, (xfs_fsop_geom_t *)&fsgeo, 3);
+ if (error)
+ return -error;
+
+ if (copy_to_user((xfs_fsop_geom_t *)arg, &fsgeo, sizeof(fsgeo)))
+ return -XFS_ERROR(EFAULT);
+ return 0;
+}
+
+STATIC int
+xfs_ioc_fsgeometry(
+ xfs_mount_t *mp,
+ unsigned long arg)
+{
+ xfs_fsop_geom_t fsgeo;
+ int error;
+
+ error = xfs_fs_geometry(mp, &fsgeo, 4);
+ if (error)
+ return -error;
+
+ if (copy_to_user((xfs_fsop_geom_t *)arg, &fsgeo, sizeof(fsgeo)))
+ return -XFS_ERROR(EFAULT);
+ return 0;
+}
+
+/*
+ * Linux extended inode flags interface.
+ */
+#define LINUX_XFLAG_SYNC 0x00000008 /* Synchronous updates */
+#define LINUX_XFLAG_IMMUTABLE 0x00000010 /* Immutable file */
+#define LINUX_XFLAG_APPEND 0x00000020 /* writes to file may only append */
+#define LINUX_XFLAG_NODUMP 0x00000040 /* do not dump file */
+#define LINUX_XFLAG_NOATIME 0x00000080 /* do not update atime */
+
+STATIC unsigned int
+xfs_merge_ioc_xflags(
+ unsigned int flags,
+ unsigned int start)
+{
+ unsigned int xflags = start;
+
+ if (flags & LINUX_XFLAG_IMMUTABLE)
+ xflags |= XFS_XFLAG_IMMUTABLE;
+ else
+ xflags &= ~XFS_XFLAG_IMMUTABLE;
+ if (flags & LINUX_XFLAG_APPEND)
+ xflags |= XFS_XFLAG_APPEND;
+ else
+ xflags &= ~XFS_XFLAG_APPEND;
+ if (flags & LINUX_XFLAG_SYNC)
+ xflags |= XFS_XFLAG_SYNC;
+ else
+ xflags &= ~XFS_XFLAG_SYNC;
+ if (flags & LINUX_XFLAG_NOATIME)
+ xflags |= XFS_XFLAG_NOATIME;
+ else
+ xflags &= ~XFS_XFLAG_NOATIME;
+ if (flags & LINUX_XFLAG_NODUMP)
+ xflags |= XFS_XFLAG_NODUMP;
+ else
+ xflags &= ~XFS_XFLAG_NODUMP;
+
+ return xflags;
+}
+
+STATIC unsigned int
+xfs_di2lxflags(
+ __uint16_t di_flags)
+{
+ unsigned int flags = 0;
+
+ if (di_flags & XFS_DIFLAG_IMMUTABLE)
+ flags |= LINUX_XFLAG_IMMUTABLE;
+ if (di_flags & XFS_DIFLAG_APPEND)
+ flags |= LINUX_XFLAG_APPEND;
+ if (di_flags & XFS_DIFLAG_SYNC)
+ flags |= LINUX_XFLAG_SYNC;
+ if (di_flags & XFS_DIFLAG_NOATIME)
+ flags |= LINUX_XFLAG_NOATIME;
+ if (di_flags & XFS_DIFLAG_NODUMP)
+ flags |= LINUX_XFLAG_NODUMP;
+ return flags;
+}
+
+STATIC int
+xfs_ioc_xattr(
+ vnode_t *vp,
+ xfs_inode_t *ip,
+ struct file *filp,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ struct fsxattr fa;
+ vattr_t va;
+ int error;
+ int attr_flags;
+ unsigned int flags;
+
+ switch (cmd) {
+ case XFS_IOC_FSGETXATTR: {
+ va.va_mask = XFS_AT_XFLAGS|XFS_AT_EXTSIZE|XFS_AT_NEXTENTS;
+ VOP_GETATTR(vp, &va, 0, NULL, error);
+ if (error)
+ return -error;
+
+ fa.fsx_xflags = va.va_xflags;
+ fa.fsx_extsize = va.va_extsize;
+ fa.fsx_nextents = va.va_nextents;
+
+ if (copy_to_user((struct fsxattr *)arg, &fa, sizeof(fa)))
+ return -XFS_ERROR(EFAULT);
+ return 0;
+ }
+
+ case XFS_IOC_FSSETXATTR: {
+ if (copy_from_user(&fa, (struct fsxattr *)arg, sizeof(fa)))
+ return -XFS_ERROR(EFAULT);
+
+ attr_flags = 0;
+ if (filp->f_flags & (O_NDELAY|O_NONBLOCK))
+ attr_flags |= ATTR_NONBLOCK;
+
+ va.va_mask = XFS_AT_XFLAGS | XFS_AT_EXTSIZE;
+ va.va_xflags = fa.fsx_xflags;
+ va.va_extsize = fa.fsx_extsize;
+
+ VOP_SETATTR(vp, &va, attr_flags, NULL, error);
+ if (!error)
+ vn_revalidate(vp); /* update Linux inode flags */
+ return -error;
+ }
+
+ case XFS_IOC_FSGETXATTRA: {
+ va.va_mask = XFS_AT_XFLAGS|XFS_AT_EXTSIZE|XFS_AT_ANEXTENTS;
+ VOP_GETATTR(vp, &va, 0, NULL, error);
+ if (error)
+ return -error;
+
+ fa.fsx_xflags = va.va_xflags;
+ fa.fsx_extsize = va.va_extsize;
+ fa.fsx_nextents = va.va_anextents;
+
+ if (copy_to_user((struct fsxattr *)arg, &fa, sizeof(fa)))
+ return -XFS_ERROR(EFAULT);
+ return 0;
+ }
+
+ case XFS_IOC_GETXFLAGS: {
+ flags = xfs_di2lxflags(ip->i_d.di_flags);
+ if (copy_to_user((unsigned int *)arg, &flags, sizeof(flags)))
+ return -XFS_ERROR(EFAULT);
+ return 0;
+ }
+
+ case XFS_IOC_SETXFLAGS: {
+ if (copy_from_user(&flags, (unsigned int *)arg, sizeof(flags)))
+ return -XFS_ERROR(EFAULT);
+
+ if (flags & ~(LINUX_XFLAG_IMMUTABLE | LINUX_XFLAG_APPEND | \
+ LINUX_XFLAG_NOATIME | LINUX_XFLAG_NODUMP | \
+ LINUX_XFLAG_SYNC))
+ return -XFS_ERROR(EOPNOTSUPP);
+
+ attr_flags = 0;
+ if (filp->f_flags & (O_NDELAY|O_NONBLOCK))
+ attr_flags |= ATTR_NONBLOCK;
+
+ va.va_mask = XFS_AT_XFLAGS;
+ va.va_xflags = xfs_merge_ioc_xflags(flags,
+ xfs_dic2xflags(&ip->i_d, ARCH_NOCONVERT));
+
+ VOP_SETATTR(vp, &va, attr_flags, NULL, error);
+ if (!error)
+ vn_revalidate(vp); /* update Linux inode flags */
+ return -error;
+ }
+
+ case XFS_IOC_GETVERSION: {
+ flags = LINVFS_GET_IP(vp)->i_generation;
+ if (copy_to_user((unsigned int *)arg, &flags, sizeof(flags)))
+ return -XFS_ERROR(EFAULT);
+ return 0;
+ }
+
+ default:
+ return -ENOTTY;
+ }
+}
+
+STATIC int
+xfs_ioc_getbmap(
+ bhv_desc_t *bdp,
+ struct file *filp,
+ int ioflags,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ struct getbmap bm;
+ int iflags;
+ int error;
+
+ if (copy_from_user(&bm, (struct getbmap *)arg, sizeof(bm)))
+ return -XFS_ERROR(EFAULT);
+
+ if (bm.bmv_count < 2)
+ return -XFS_ERROR(EINVAL);
+
+ iflags = (cmd == XFS_IOC_GETBMAPA ? BMV_IF_ATTRFORK : 0);
+ if (ioflags & IO_INVIS)
+ iflags |= BMV_IF_NO_DMAPI_READ;
+
+ error = xfs_getbmap(bdp, &bm, (struct getbmap *)arg+1, iflags);
+ if (error)
+ return -error;
+
+ if (copy_to_user((struct getbmap *)arg, &bm, sizeof(bm)))
+ return -XFS_ERROR(EFAULT);
+ return 0;
+}
+
+STATIC int
+xfs_ioc_getbmapx(
+ bhv_desc_t *bdp,
+ unsigned long arg)
+{
+ struct getbmapx bmx;
+ struct getbmap bm;
+ int iflags;
+ int error;
+
+ if (copy_from_user(&bmx, (struct getbmapx *)arg, sizeof(bmx)))
+ return -XFS_ERROR(EFAULT);
+
+ if (bmx.bmv_count < 2)
+ return -XFS_ERROR(EINVAL);
+
+ /*
+ * Map input getbmapx structure to a getbmap
+ * structure for xfs_getbmap.
+ */
+ GETBMAP_CONVERT(bmx, bm);
+
+ iflags = bmx.bmv_iflags;
+
+ if (iflags & (~BMV_IF_VALID))
+ return -XFS_ERROR(EINVAL);
+
+ iflags |= BMV_IF_EXTENDED;
+
+ error = xfs_getbmap(bdp, &bm, (struct getbmapx *)arg+1, iflags);
+ if (error)
+ return -error;
+
+ GETBMAP_CONVERT(bm, bmx);
+
+ if (copy_to_user((struct getbmapx *)arg, &bmx, sizeof(bmx)))
+ return -XFS_ERROR(EFAULT);
+
+ return 0;
+}
diff --git a/fs/xfs/linux-2.4/xfs_iops.c b/fs/xfs/linux-2.4/xfs_iops.c
new file mode 100644
index 00000000000000..ed93f6ee204593
--- /dev/null
+++ b/fs/xfs/linux-2.4/xfs_iops.c
@@ -0,0 +1,690 @@
+/*
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_alloc.h"
+#include "xfs_dmapi.h"
+#include "xfs_quota.h"
+#include "xfs_mount.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode.h"
+#include "xfs_bmap.h"
+#include "xfs_bit.h"
+#include "xfs_rtalloc.h"
+#include "xfs_error.h"
+#include "xfs_itable.h"
+#include "xfs_rw.h"
+#include "xfs_acl.h"
+#include "xfs_cap.h"
+#include "xfs_mac.h"
+#include "xfs_attr.h"
+#include "xfs_buf_item.h"
+#include "xfs_utils.h"
+
+#include <linux/xattr.h>
+
+
+/*
+ * Pull the link count and size up from the xfs inode to the linux inode
+ */
+STATIC void
+validate_fields(
+ struct inode *ip)
+{
+ vnode_t *vp = LINVFS_GET_VP(ip);
+ vattr_t va;
+ int error;
+
+ va.va_mask = XFS_AT_NLINK|XFS_AT_SIZE|XFS_AT_NBLOCKS;
+ VOP_GETATTR(vp, &va, ATTR_LAZY, NULL, error);
+ if (likely(!error)) {
+ ip->i_nlink = va.va_nlink;
+ ip->i_blocks = va.va_nblocks;
+
+ /* we're under i_sem so i_size can't change under us */
+ if (i_size_read(ip) != va.va_size)
+ i_size_write(ip, va.va_size);
+ }
+}
+
+#ifdef CONFIG_XFS_POSIX_ACL
+/*
+ * Determine whether a process has a valid fs_struct (kernel daemons
+ * like knfsd don't have an fs_struct).
+ */
+STATIC int inline
+has_fs_struct(struct task_struct *task)
+{
+ return (task->fs != init_task.fs);
+}
+#endif
+
+STATIC int
+linvfs_mknod(
+ struct inode *dir,
+ struct dentry *dentry,
+ int mode,
+ int rdev)
+{
+ struct inode *ip;
+ vattr_t va;
+ vnode_t *vp = NULL, *dvp = LINVFS_GET_VP(dir);
+ xfs_acl_t *default_acl = NULL;
+ attrexists_t test_default_acl = _ACL_DEFAULT_EXISTS;
+ int error;
+
+ if (test_default_acl && test_default_acl(dvp)) {
+ if (!_ACL_ALLOC(default_acl))
+ return -ENOMEM;
+ if (!_ACL_GET_DEFAULT(dvp, default_acl)) {
+ _ACL_FREE(default_acl);
+ default_acl = NULL;
+ }
+ }
+
+#ifdef CONFIG_XFS_POSIX_ACL
+ /*
+ * Conditionally compiled so that the ACL base kernel changes can be
+ * split out into separate patches - remove this once MS_POSIXACL is
+ * accepted, or some other way to implement this exists.
+ */
+ if (IS_POSIXACL(dir) && !default_acl && has_fs_struct(current))
+ mode &= ~current->fs->umask;
+#endif
+
+ memset(&va, 0, sizeof(va));
+ va.va_mask = XFS_AT_TYPE|XFS_AT_MODE;
+ va.va_type = IFTOVT(mode);
+ va.va_mode = mode;
+
+ switch (mode & S_IFMT) {
+ case S_IFCHR: case S_IFBLK: case S_IFIFO: case S_IFSOCK:
+ va.va_rdev = XFS_MKDEV(MAJOR(rdev), MINOR(rdev));
+ va.va_mask |= XFS_AT_RDEV;
+ /*FALLTHROUGH*/
+ case S_IFREG:
+ VOP_CREATE(dvp, dentry, &va, &vp, NULL, error);
+ break;
+ case S_IFDIR:
+ VOP_MKDIR(dvp, dentry, &va, &vp, NULL, error);
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ if (default_acl) {
+ if (!error) {
+ error = _ACL_INHERIT(vp, &va, default_acl);
+ if (!error) {
+ VMODIFY(vp);
+ } else {
+ struct dentry teardown = {};
+ int err2;
+
+ /* Oh, the horror.
+ * If we can't add the ACL we must back out.
+ * ENOSPC can hit here, among other things.
+ */
+ teardown.d_inode = ip = LINVFS_GET_IP(vp);
+ teardown.d_name = dentry->d_name;
+
+ vn_mark_bad(vp);
+
+ if (S_ISDIR(mode))
+ VOP_RMDIR(dvp, &teardown, NULL, err2);
+ else
+ VOP_REMOVE(dvp, &teardown, NULL, err2);
+ VN_RELE(vp);
+ }
+ }
+ _ACL_FREE(default_acl);
+ }
+
+ if (!error) {
+ ASSERT(vp);
+ ip = LINVFS_GET_IP(vp);
+
+ if (S_ISCHR(mode) || S_ISBLK(mode))
+ ip->i_rdev = to_kdev_t(rdev);
+ else if (S_ISDIR(mode))
+ validate_fields(ip);
+ d_instantiate(dentry, ip);
+ validate_fields(dir);
+ }
+ return -error;
+}
+
+STATIC int
+linvfs_create(
+ struct inode *dir,
+ struct dentry *dentry,
+ int mode)
+{
+ return linvfs_mknod(dir, dentry, mode, 0);
+}
+
+STATIC int
+linvfs_mkdir(
+ struct inode *dir,
+ struct dentry *dentry,
+ int mode)
+{
+ return linvfs_mknod(dir, dentry, mode|S_IFDIR, 0);
+}
+
+STATIC struct dentry *
+linvfs_lookup(
+ struct inode *dir,
+ struct dentry *dentry)
+{
+ struct inode *ip = NULL;
+ vnode_t *vp, *cvp = NULL;
+ int error;
+
+ if (dentry->d_name.len >= MAXNAMELEN)
+ return ERR_PTR(-ENAMETOOLONG);
+
+ vp = LINVFS_GET_VP(dir);
+ VOP_LOOKUP(vp, dentry, &cvp, 0, NULL, NULL, error);
+ if (!error) {
+ ASSERT(cvp);
+ ip = LINVFS_GET_IP(cvp);
+ if (!ip) {
+ VN_RELE(cvp);
+ return ERR_PTR(-EACCES);
+ }
+ }
+ if (error && (error != ENOENT))
+ return ERR_PTR(-error);
+ d_add(dentry, ip); /* Negative entry goes in if ip is NULL */
+ return NULL;
+}
+
+STATIC int
+linvfs_link(
+ struct dentry *old_dentry,
+ struct inode *dir,
+ struct dentry *dentry)
+{
+ struct inode *ip; /* inode of guy being linked to */
+ vnode_t *tdvp; /* target directory for new name/link */
+ vnode_t *vp; /* vp of name being linked */
+ int error;
+
+ ip = old_dentry->d_inode; /* inode being linked to */
+ if (S_ISDIR(ip->i_mode))
+ return -EPERM;
+
+ tdvp = LINVFS_GET_VP(dir);
+ vp = LINVFS_GET_VP(ip);
+
+ VOP_LINK(tdvp, vp, dentry, NULL, error);
+ if (!error) {
+ VMODIFY(tdvp);
+ VN_HOLD(vp);
+ validate_fields(ip);
+ d_instantiate(dentry, ip);
+ }
+ return -error;
+}
+
+STATIC int
+linvfs_unlink(
+ struct inode *dir,
+ struct dentry *dentry)
+{
+ struct inode *inode;
+ vnode_t *dvp; /* directory containing name to remove */
+ int error;
+
+ inode = dentry->d_inode;
+ dvp = LINVFS_GET_VP(dir);
+
+ VOP_REMOVE(dvp, dentry, NULL, error);
+ if (!error) {
+ validate_fields(dir); /* For size only */
+ validate_fields(inode);
+ }
+
+ return -error;
+}
+
+STATIC int
+linvfs_symlink(
+ struct inode *dir,
+ struct dentry *dentry,
+ const char *symname)
+{
+ struct inode *ip;
+ vattr_t va;
+ vnode_t *dvp; /* directory containing name of symlink */
+ vnode_t *cvp; /* used to lookup symlink to put in dentry */
+ int error;
+
+ dvp = LINVFS_GET_VP(dir);
+ cvp = NULL;
+
+ memset(&va, 0, sizeof(va));
+ va.va_type = VLNK;
+ va.va_mode = irix_symlink_mode ? 0777 & ~current->fs->umask : S_IRWXUGO;
+ va.va_mask = XFS_AT_TYPE|XFS_AT_MODE;
+
+ error = 0;
+ VOP_SYMLINK(dvp, dentry, &va, (char *)symname, &cvp, NULL, error);
+ if (!error && cvp) {
+ ASSERT(cvp->v_type == VLNK);
+ ip = LINVFS_GET_IP(cvp);
+ d_instantiate(dentry, ip);
+ validate_fields(dir);
+ validate_fields(ip); /* size needs update */
+ }
+ return -error;
+}
+
+STATIC int
+linvfs_rmdir(
+ struct inode *dir,
+ struct dentry *dentry)
+{
+ struct inode *inode = dentry->d_inode;
+ vnode_t *dvp = LINVFS_GET_VP(dir);
+ int error;
+
+ VOP_RMDIR(dvp, dentry, NULL, error);
+ if (!error) {
+ validate_fields(inode);
+ validate_fields(dir);
+ }
+ return -error;
+}
+
+STATIC int
+linvfs_rename(
+ struct inode *odir,
+ struct dentry *odentry,
+ struct inode *ndir,
+ struct dentry *ndentry)
+{
+ struct inode *new_inode = ndentry->d_inode;
+ vnode_t *fvp; /* from directory */
+ vnode_t *tvp; /* target directory */
+ int error;
+
+ fvp = LINVFS_GET_VP(odir);
+ tvp = LINVFS_GET_VP(ndir);
+
+ VOP_RENAME(fvp, odentry, tvp, ndentry, NULL, error);
+ if (error)
+ return -error;
+
+ if (new_inode)
+ validate_fields(new_inode);
+
+ validate_fields(odir);
+ if (ndir != odir)
+ validate_fields(ndir);
+ return 0;
+}
+
+STATIC int
+linvfs_readlink(
+ struct dentry *dentry,
+ char *buf,
+ int size)
+{
+ vnode_t *vp = LINVFS_GET_VP(dentry->d_inode);
+ uio_t uio;
+ iovec_t iov;
+ int error;
+
+ iov.iov_base = buf;
+ iov.iov_len = size;
+
+ uio.uio_iov = &iov;
+ uio.uio_offset = 0;
+ uio.uio_segflg = UIO_USERSPACE;
+ uio.uio_resid = size;
+ uio.uio_iovcnt = 1;
+
+ VOP_READLINK(vp, &uio, 0, NULL, error);
+ if (error)
+ return -error;
+
+ return (size - uio.uio_resid);
+}
+
+/*
+ * careful here - this function can get called recursively, so
+ * we need to be very careful about how much stack we use.
+ * uio is kmalloced for this reason...
+ */
+STATIC int
+linvfs_follow_link(
+ struct dentry *dentry,
+ struct nameidata *nd)
+{
+ vnode_t *vp;
+ uio_t *uio;
+ iovec_t iov;
+ int error;
+ char *link;
+
+ ASSERT(dentry);
+ ASSERT(nd);
+
+ link = (char *)kmalloc(MAXNAMELEN+1, GFP_KERNEL);
+ if (!link)
+ return -ENOMEM;
+
+ uio = (uio_t *)kmalloc(sizeof(uio_t), GFP_KERNEL);
+ if (!uio) {
+ kfree(link);
+ return -ENOMEM;
+ }
+
+ vp = LINVFS_GET_VP(dentry->d_inode);
+
+ iov.iov_base = link;
+ iov.iov_len = MAXNAMELEN;
+
+ uio->uio_iov = &iov;
+ uio->uio_offset = 0;
+ uio->uio_segflg = UIO_SYSSPACE;
+ uio->uio_resid = MAXNAMELEN;
+ uio->uio_iovcnt = 1;
+
+ VOP_READLINK(vp, uio, 0, NULL, error);
+ if (error) {
+ kfree(uio);
+ kfree(link);
+ return -error;
+ }
+
+ link[MAXNAMELEN - uio->uio_resid] = '\0';
+ kfree(uio);
+
+ /* vfs_follow_link returns (-) errors */
+ error = vfs_follow_link(nd, link);
+ kfree(link);
+ return error;
+}
+
+STATIC int
+linvfs_permission(
+ struct inode *inode,
+ int mode)
+{
+ vnode_t *vp = LINVFS_GET_VP(inode);
+ int error;
+
+ mode <<= 6; /* convert from linux to vnode access bits */
+ VOP_ACCESS(vp, mode, NULL, error);
+ return -error;
+}
+
+STATIC int
+linvfs_revalidate(
+ struct dentry *dentry)
+{
+ vnode_t *vp = LINVFS_GET_VP(dentry->d_inode);
+
+ if (unlikely(vp->v_flag & VMODIFIED))
+ return vn_revalidate(vp);
+ return 0;
+}
+
+STATIC int
+linvfs_setattr(
+ struct dentry *dentry,
+ struct iattr *attr)
+{
+ struct inode *inode = dentry->d_inode;
+ unsigned int ia_valid = attr->ia_valid;
+ vnode_t *vp = LINVFS_GET_VP(inode);
+ vattr_t vattr;
+ int flags = 0;
+ int error;
+
+ memset(&vattr, 0, sizeof(vattr_t));
+ if (ia_valid & ATTR_UID) {
+ vattr.va_mask |= XFS_AT_UID;
+ vattr.va_uid = attr->ia_uid;
+ }
+ if (ia_valid & ATTR_GID) {
+ vattr.va_mask |= XFS_AT_GID;
+ vattr.va_gid = attr->ia_gid;
+ }
+ if (ia_valid & ATTR_SIZE) {
+ vattr.va_mask |= XFS_AT_SIZE;
+ vattr.va_size = attr->ia_size;
+ }
+ if (ia_valid & ATTR_ATIME) {
+ vattr.va_mask |= XFS_AT_ATIME;
+ vattr.va_atime.tv_sec = attr->ia_atime;
+ vattr.va_atime.tv_nsec = 0;
+ }
+ if (ia_valid & ATTR_MTIME) {
+ vattr.va_mask |= XFS_AT_MTIME;
+ vattr.va_mtime.tv_sec = attr->ia_mtime;
+ vattr.va_mtime.tv_nsec = 0;
+ }
+ if (ia_valid & ATTR_CTIME) {
+ vattr.va_mask |= XFS_AT_CTIME;
+ vattr.va_ctime.tv_sec = attr->ia_ctime;
+ vattr.va_ctime.tv_nsec = 0;
+ }
+ if (ia_valid & ATTR_MODE) {
+ vattr.va_mask |= XFS_AT_MODE;
+ vattr.va_mode = attr->ia_mode;
+ if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID))
+ inode->i_mode &= ~S_ISGID;
+ }
+
+ if (ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET))
+ flags |= ATTR_UTIME;
+
+ VOP_SETATTR(vp, &vattr, flags, NULL, error);
+ if (error)
+ return -error;
+ vn_revalidate(vp);
+ return error;
+}
+
+STATIC void
+linvfs_truncate(
+ struct inode *inode)
+{
+ block_truncate_page(inode->i_mapping, inode->i_size, linvfs_get_block);
+}
+
+STATIC int
+linvfs_setxattr(
+ struct dentry *dentry,
+ const char *name,
+ void *data,
+ size_t size,
+ int flags)
+{
+ vnode_t *vp = LINVFS_GET_VP(dentry->d_inode);
+ char *attr = (char *)name;
+ attrnames_t *namesp;
+ int xflags = 0;
+ int error;
+
+ namesp = attr_lookup_namespace(attr, attr_namespaces, ATTR_NAMECOUNT);
+ if (!namesp)
+ return -EOPNOTSUPP;
+ attr += namesp->attr_namelen;
+ error = namesp->attr_capable(vp, NULL);
+ if (error)
+ return error;
+
+ /* Convert Linux syscall to XFS internal ATTR flags */
+ if (flags & XATTR_CREATE)
+ xflags |= ATTR_CREATE;
+ if (flags & XATTR_REPLACE)
+ xflags |= ATTR_REPLACE;
+ xflags |= namesp->attr_flag;
+ return namesp->attr_set(vp, attr, (void *)data, size, xflags);
+}
+
+STATIC ssize_t
+linvfs_getxattr(
+ struct dentry *dentry,
+ const char *name,
+ void *data,
+ size_t size)
+{
+ vnode_t *vp = LINVFS_GET_VP(dentry->d_inode);
+ char *attr = (char *)name;
+ attrnames_t *namesp;
+ int xflags = 0;
+ ssize_t error;
+
+ namesp = attr_lookup_namespace(attr, attr_namespaces, ATTR_NAMECOUNT);
+ if (!namesp)
+ return -EOPNOTSUPP;
+ attr += namesp->attr_namelen;
+ error = namesp->attr_capable(vp, NULL);
+ if (error)
+ return error;
+
+ /* Convert Linux syscall to XFS internal ATTR flags */
+ if (!size) {
+ xflags |= ATTR_KERNOVAL;
+ data = NULL;
+ }
+ xflags |= namesp->attr_flag;
+ return namesp->attr_get(vp, attr, (void *)data, size, xflags);
+}
+
+STATIC ssize_t
+linvfs_listxattr(
+ struct dentry *dentry,
+ char *data,
+ size_t size)
+{
+ vnode_t *vp = LINVFS_GET_VP(dentry->d_inode);
+ int error, xflags = ATTR_KERNAMELS;
+ ssize_t result;
+
+ if (!size)
+ xflags |= ATTR_KERNOVAL;
+ xflags |= capable(CAP_SYS_ADMIN) ? ATTR_KERNFULLS : ATTR_KERNORMALS;
+
+ error = attr_generic_list(vp, data, size, xflags, &result);
+ if (error < 0)
+ return error;
+ return result;
+}
+
+STATIC int
+linvfs_removexattr(
+ struct dentry *dentry,
+ const char *name)
+{
+ vnode_t *vp = LINVFS_GET_VP(dentry->d_inode);
+ char *attr = (char *)name;
+ attrnames_t *namesp;
+ int xflags = 0;
+ int error;
+
+ namesp = attr_lookup_namespace(attr, attr_namespaces, ATTR_NAMECOUNT);
+ if (!namesp)
+ return -EOPNOTSUPP;
+ attr += namesp->attr_namelen;
+ error = namesp->attr_capable(vp, NULL);
+ if (error)
+ return error;
+ xflags |= namesp->attr_flag;
+ return namesp->attr_remove(vp, attr, xflags);
+}
+
+
+struct inode_operations linvfs_file_inode_operations = {
+ .permission = linvfs_permission,
+ .truncate = linvfs_truncate,
+ .revalidate = linvfs_revalidate,
+ .setattr = linvfs_setattr,
+ .setxattr = linvfs_setxattr,
+ .getxattr = linvfs_getxattr,
+ .listxattr = linvfs_listxattr,
+ .removexattr = linvfs_removexattr,
+};
+
+struct inode_operations linvfs_dir_inode_operations = {
+ .create = linvfs_create,
+ .lookup = linvfs_lookup,
+ .link = linvfs_link,
+ .unlink = linvfs_unlink,
+ .symlink = linvfs_symlink,
+ .mkdir = linvfs_mkdir,
+ .rmdir = linvfs_rmdir,
+ .mknod = linvfs_mknod,
+ .rename = linvfs_rename,
+ .permission = linvfs_permission,
+ .revalidate = linvfs_revalidate,
+ .setattr = linvfs_setattr,
+ .setxattr = linvfs_setxattr,
+ .getxattr = linvfs_getxattr,
+ .listxattr = linvfs_listxattr,
+ .removexattr = linvfs_removexattr,
+};
+
+struct inode_operations linvfs_symlink_inode_operations = {
+ .readlink = linvfs_readlink,
+ .follow_link = linvfs_follow_link,
+ .permission = linvfs_permission,
+ .revalidate = linvfs_revalidate,
+ .setattr = linvfs_setattr,
+ .setxattr = linvfs_setxattr,
+ .getxattr = linvfs_getxattr,
+ .listxattr = linvfs_listxattr,
+ .removexattr = linvfs_removexattr,
+};
diff --git a/fs/xfs/linux-2.4/xfs_iops.h b/fs/xfs/linux-2.4/xfs_iops.h
new file mode 100644
index 00000000000000..bb2f3de96fecd9
--- /dev/null
+++ b/fs/xfs/linux-2.4/xfs_iops.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_IOPS_H__
+#define __XFS_IOPS_H__
+
+extern struct inode_operations linvfs_file_inode_operations;
+extern struct inode_operations linvfs_dir_inode_operations;
+extern struct inode_operations linvfs_symlink_inode_operations;
+
+extern struct file_operations linvfs_file_operations;
+extern struct file_operations linvfs_invis_file_operations;
+extern struct file_operations linvfs_dir_operations;
+
+extern struct address_space_operations linvfs_aops;
+
+extern int linvfs_get_block(struct inode *, long, struct buffer_head *, int);
+extern void linvfs_unwritten_done(struct buffer_head *, int);
+
+extern int xfs_ioctl(struct bhv_desc *, struct inode *, struct file *,
+ int, unsigned int, unsigned long);
+
+#endif /* __XFS_IOPS_H__ */
diff --git a/fs/xfs/linux-2.4/xfs_linux.h b/fs/xfs/linux-2.4/xfs_linux.h
new file mode 100644
index 00000000000000..df560d559aee23
--- /dev/null
+++ b/fs/xfs/linux-2.4/xfs_linux.h
@@ -0,0 +1,388 @@
+/*
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_LINUX__
+#define __XFS_LINUX__
+
+#include <linux/types.h>
+#include <linux/config.h>
+
+#undef __user
+#define __user
+
+/*
+ * Some types are conditional depending on the target system.
+ * XFS_BIG_BLKNOS needs block layer disk addresses to be 64 bits.
+ * XFS_BIG_INUMS needs the VFS inode number to be 64 bits, as well
+ * as requiring XFS_BIG_BLKNOS to be set.
+ */
+#define XFS_BIG_BLKNOS 0
+#define XFS_BIG_INUMS 0
+
+#include <xfs_types.h>
+#include <xfs_arch.h>
+
+#include <kmem.h>
+#include <mrlock.h>
+#include <spin.h>
+#include <sv.h>
+#include <mutex.h>
+#include <sema.h>
+#include <time.h>
+
+#include <support/qsort.h>
+#include <support/ktrace.h>
+#include <support/debug.h>
+#include <support/move.h>
+#include <support/uuid.h>
+
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/blkdev.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/file.h>
+#include <linux/swap.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <linux/sched.h>
+#include <linux/bitops.h>
+#include <linux/major.h>
+#include <linux/pagemap.h>
+#include <linux/seq_file.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/proc_fs.h>
+
+#include <asm/page.h>
+#include <asm/div64.h>
+#include <asm/param.h>
+#include <asm/uaccess.h>
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
+
+#include <xfs_behavior.h>
+#include <xfs_vfs.h>
+#include <xfs_cred.h>
+#include <xfs_vnode.h>
+#include <xfs_stats.h>
+#include <xfs_sysctl.h>
+#include <xfs_iops.h>
+#include <xfs_super.h>
+#include <xfs_globals.h>
+#include <xfs_fs_subr.h>
+#include <xfs_lrw.h>
+#include <xfs_buf.h>
+
+/*
+ * Feature macros (disable/enable)
+ */
+#define HAVE_REFCACHE /* 2.4 uses a NFS reference cache; 2.6+ does not */
+#undef HAVE_SENDFILE /* sendfile(2) is a 2.6+ system call, not in 2.4 */
+
+#ifndef EVMS_MAJOR
+#define EVMS_MAJOR 117
+#endif
+
+/*
+ * State flag for unwritten extent buffers.
+ *
+ * We need to be able to distinguish between these and delayed
+ * allocate buffers within XFS. The generic IO path code does
+ * not need to distinguish - we use the BH_Delay flag for both
+ * delalloc and these ondisk-uninitialised buffers.
+ */
+#define BH_Unwritten BH_PrivateStart
+#define buffer_unwritten(bh) __buffer_state(bh, Unwritten)
+static inline void set_buffer_unwritten_io(struct buffer_head *bh)
+{
+ bh->b_end_io = linvfs_unwritten_done;
+}
+BUFFER_FNS(Unwritten, unwritten)
+
+#define xfs_refcache_size xfs_params.refcache_size.val
+#define xfs_refcache_purge_count xfs_params.refcache_purge.val
+#define restricted_chown xfs_params.restrict_chown.val
+#define irix_sgid_inherit xfs_params.sgid_inherit.val
+#define irix_symlink_mode xfs_params.symlink_mode.val
+#define xfs_panic_mask xfs_params.panic_mask.val
+#define xfs_error_level xfs_params.error_level.val
+#define xfs_syncd_centisecs xfs_params.syncd_timer.val
+#define xfs_stats_clear xfs_params.stats_clear.val
+#define xfs_inherit_sync xfs_params.inherit_sync.val
+#define xfs_inherit_nodump xfs_params.inherit_nodump.val
+#define xfs_inherit_noatime xfs_params.inherit_noatim.val
+#define xfs_buf_timer_centisecs xfs_params.xfs_buf_timer.val
+#define xfs_buf_age_centisecs xfs_params.xfs_buf_age.val
+#define xfs_inherit_nosymlinks xfs_params.inherit_nosym.val
+#define xfs_rotorstep xfs_params.rotorstep.val
+
+#define current_cpu() smp_processor_id()
+#define current_pid() (current->pid)
+#define current_fsuid(cred) (current->fsuid)
+#define current_fsgid(cred) (current->fsgid)
+
+#define NBPP PAGE_SIZE
+#define DPPSHFT (PAGE_SHIFT - 9)
+#define NDPP (1 << (PAGE_SHIFT - 9))
+#define dtop(DD) (((DD) + NDPP - 1) >> DPPSHFT)
+#define dtopt(DD) ((DD) >> DPPSHFT)
+#define dpoff(DD) ((DD) & (NDPP-1))
+
+#define NBBY 8 /* number of bits per byte */
+#define NBPC PAGE_SIZE /* Number of bytes per click */
+#define BPCSHIFT PAGE_SHIFT /* LOG2(NBPC) if exact */
+
+/*
+ * Size of block device i/o is parameterized here.
+ * Currently the system supports page-sized i/o.
+ */
+#define BLKDEV_IOSHIFT BPCSHIFT
+#define BLKDEV_IOSIZE (1<<BLKDEV_IOSHIFT)
+/* number of BB's per block device block */
+#define BLKDEV_BB BTOBB(BLKDEV_IOSIZE)
+
+/* bytes to clicks */
+#define btoc(x) (((__psunsigned_t)(x)+(NBPC-1))>>BPCSHIFT)
+#define btoct(x) ((__psunsigned_t)(x)>>BPCSHIFT)
+#define btoc64(x) (((__uint64_t)(x)+(NBPC-1))>>BPCSHIFT)
+#define btoct64(x) ((__uint64_t)(x)>>BPCSHIFT)
+#define io_btoc(x) (((__psunsigned_t)(x)+(IO_NBPC-1))>>IO_BPCSHIFT)
+#define io_btoct(x) ((__psunsigned_t)(x)>>IO_BPCSHIFT)
+
+/* off_t bytes to clicks */
+#define offtoc(x) (((__uint64_t)(x)+(NBPC-1))>>BPCSHIFT)
+#define offtoct(x) ((xfs_off_t)(x)>>BPCSHIFT)
+
+/* clicks to off_t bytes */
+#define ctooff(x) ((xfs_off_t)(x)<<BPCSHIFT)
+
+/* clicks to bytes */
+#define ctob(x) ((__psunsigned_t)(x)<<BPCSHIFT)
+#define btoct(x) ((__psunsigned_t)(x)>>BPCSHIFT)
+#define ctob64(x) ((__uint64_t)(x)<<BPCSHIFT)
+#define io_ctob(x) ((__psunsigned_t)(x)<<IO_BPCSHIFT)
+
+/* bytes to clicks */
+#define btoc(x) (((__psunsigned_t)(x)+(NBPC-1))>>BPCSHIFT)
+
+#ifndef CELL_CAPABLE
+#define FSC_NOTIFY_NAME_CHANGED(vp)
+#endif
+
+#ifndef ENOATTR
+#define ENOATTR ENODATA /* Attribute not found */
+#endif
+
+/* Note: EWRONGFS never visible outside the kernel */
+#define EWRONGFS EINVAL /* Mount with wrong filesystem type */
+
+/*
+ * XXX EFSCORRUPTED needs a real value in errno.h. asm-i386/errno.h won't
+ * return codes out of its known range in errno.
+ * XXX Also note: needs to be < 1000 and fairly unique on Linux (mustn't
+ * conflict with any code we use already or any code a driver may use)
+ * XXX Some options (currently we do #2):
+ * 1/ New error code ["Filesystem is corrupted", _after_ glibc updated]
+ * 2/ 990 ["Unknown error 990"]
+ * 3/ EUCLEAN ["Structure needs cleaning"]
+ * 4/ Convert EFSCORRUPTED to EIO [just prior to return into userspace]
+ */
+#define EFSCORRUPTED 990 /* Filesystem is corrupted */
+
+#define SYNCHRONIZE() barrier()
+#define __return_address __builtin_return_address(0)
+
+/*
+ * IRIX (BSD) quotactl makes use of separate commands for user/group,
+ * whereas on Linux the syscall encodes this information into the cmd
+ * field (see the QCMD macro in quota.h). These macros help keep the
+ * code portable - they are not visible from the syscall interface.
+ */
+#define Q_XSETGQLIM XQM_CMD(0x8) /* set groups disk limits */
+#define Q_XGETGQUOTA XQM_CMD(0x9) /* get groups disk limits */
+
+/* IRIX uses a dynamic sizing algorithm (ndquot = 200 + numprocs*2) */
+/* we may well need to fine-tune this if it ever becomes an issue. */
+#define DQUOT_MAX_HEURISTIC 1024 /* NR_DQUOTS */
+#define ndquot DQUOT_MAX_HEURISTIC
+
+/* IRIX uses the current size of the name cache to guess a good value */
+/* - this isn't the same but is a good enough starting point for now. */
+#define DQUOT_HASH_HEURISTIC files_stat.nr_files
+
+/* IRIX inodes maintain the project ID also, zero this field on Linux */
+#define DEFAULT_PROJID 0
+#define dfltprid DEFAULT_PROJID
+
+#ifndef pgoff_t /* 2.6 compat */
+#define pgoff_t unsigned long
+#endif
+
+#define MAXPATHLEN 1024
+
+#define MIN(a,b) (min(a,b))
+#define MAX(a,b) (max(a,b))
+#define howmany(x, y) (((x)+((y)-1))/(y))
+#define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
+
+/*
+ * Juggle IRIX device numbers - still used in ondisk structures
+ */
+#define XFS_DEV_BITSMAJOR 14
+#define XFS_DEV_BITSMINOR 18
+#define XFS_DEV_MAXMAJ 0x1ff
+#define XFS_DEV_MAXMIN 0x3ffff
+#define XFS_DEV_MAJOR(dev) ((int)(((unsigned)(dev)>>XFS_DEV_BITSMINOR) \
+ & XFS_DEV_MAXMAJ))
+#define XFS_DEV_MINOR(dev) ((int)((dev)&XFS_DEV_MAXMIN))
+#define XFS_MKDEV(major,minor) ((xfs_dev_t)(((major)<<XFS_DEV_BITSMINOR) \
+ | (minor&XFS_DEV_MAXMIN)))
+
+#define XFS_DEV_TO_KDEVT(dev) mk_kdev(XFS_DEV_MAJOR(dev),XFS_DEV_MINOR(dev))
+
+#define xfs_stack_trace() dump_stack()
+
+#define xfs_itruncate_data(ip, off) \
+ (-vmtruncate(LINVFS_GET_IP(XFS_ITOV(ip)), (off)))
+
+
+/* Move the kernel do_div definition off to one side */
+
+#if defined __i386__
+/* For ia32 we need to pull some tricks to get past various versions
+ * of the compiler which do not like us using do_div in the middle
+ * of large functions.
+ */
+static inline __u32 xfs_do_div(void *a, __u32 b, int n)
+{
+ __u32 mod;
+
+ switch (n) {
+ case 4:
+ mod = *(__u32 *)a % b;
+ *(__u32 *)a = *(__u32 *)a / b;
+ return mod;
+ case 8:
+ {
+ unsigned long __upper, __low, __high, __mod;
+ __u64 c = *(__u64 *)a;
+ __upper = __high = c >> 32;
+ __low = c;
+ if (__high) {
+ __upper = __high % (b);
+ __high = __high / (b);
+ }
+ asm("divl %2":"=a" (__low), "=d" (__mod):"rm" (b), "0" (__low), "1" (__upper));
+ asm("":"=A" (c):"a" (__low),"d" (__high));
+ *(__u64 *)a = c;
+ return __mod;
+ }
+ }
+
+ /* NOTREACHED */
+ return 0;
+}
+
+/* Side effect free 64 bit mod operation */
+static inline __u32 xfs_do_mod(void *a, __u32 b, int n)
+{
+ switch (n) {
+ case 4:
+ return *(__u32 *)a % b;
+ case 8:
+ {
+ unsigned long __upper, __low, __high, __mod;
+ __u64 c = *(__u64 *)a;
+ __upper = __high = c >> 32;
+ __low = c;
+ if (__high) {
+ __upper = __high % (b);
+ __high = __high / (b);
+ }
+ asm("divl %2":"=a" (__low), "=d" (__mod):"rm" (b), "0" (__low), "1" (__upper));
+ asm("":"=A" (c):"a" (__low),"d" (__high));
+ return __mod;
+ }
+ }
+
+ /* NOTREACHED */
+ return 0;
+}
+#else
+static inline __u32 xfs_do_div(void *a, __u32 b, int n)
+{
+ __u32 mod;
+
+ switch (n) {
+ case 4:
+ mod = *(__u32 *)a % b;
+ *(__u32 *)a = *(__u32 *)a / b;
+ return mod;
+ case 8:
+ mod = do_div(*(__u64 *)a, b);
+ return mod;
+ }
+
+ /* NOTREACHED */
+ return 0;
+}
+
+/* Side effect free 64 bit mod operation */
+static inline __u32 xfs_do_mod(void *a, __u32 b, int n)
+{
+ switch (n) {
+ case 4:
+ return *(__u32 *)a % b;
+ case 8:
+ {
+ __u64 c = *(__u64 *)a;
+ return do_div(c, b);
+ }
+ }
+
+ /* NOTREACHED */
+ return 0;
+}
+#endif
+
+#undef do_div
+#define do_div(a, b) xfs_do_div(&(a), (b), sizeof(a))
+#define do_mod(a, b) xfs_do_mod(&(a), (b), sizeof(a))
+
+static inline __uint64_t roundup_64(__uint64_t x, __uint32_t y)
+{
+ x += y - 1;
+ do_div(x, y);
+ return(x * y);
+}
+
+#endif /* __XFS_LINUX__ */
diff --git a/fs/xfs/linux-2.4/xfs_lrw.c b/fs/xfs/linux-2.4/xfs_lrw.c
new file mode 100644
index 00000000000000..ad65b6bf26173a
--- /dev/null
+++ b/fs/xfs/linux-2.4/xfs_lrw.c
@@ -0,0 +1,981 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+/*
+ * fs/xfs/linux/xfs_lrw.c (Linux Read Write stuff)
+ *
+ */
+
+#include "xfs.h"
+
+#include "xfs_fs.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_alloc.h"
+#include "xfs_dmapi.h"
+#include "xfs_quota.h"
+#include "xfs_mount.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode.h"
+#include "xfs_bmap.h"
+#include "xfs_bit.h"
+#include "xfs_rtalloc.h"
+#include "xfs_error.h"
+#include "xfs_itable.h"
+#include "xfs_rw.h"
+#include "xfs_refcache.h"
+#include "xfs_acl.h"
+#include "xfs_cap.h"
+#include "xfs_mac.h"
+#include "xfs_attr.h"
+#include "xfs_inode_item.h"
+#include "xfs_buf_item.h"
+#include "xfs_utils.h"
+#include "xfs_iomap.h"
+
+#include <linux/capability.h>
+
+
+#if defined(XFS_RW_TRACE)
+void
+xfs_rw_enter_trace(
+ int tag,
+ xfs_iocore_t *io,
+ const char *buf,
+ size_t size,
+ loff_t offset,
+ int ioflags)
+{
+ xfs_inode_t *ip = XFS_IO_INODE(io);
+
+ if (ip->i_rwtrace == NULL)
+ return;
+ ktrace_enter(ip->i_rwtrace,
+ (void *)(unsigned long)tag,
+ (void *)ip,
+ (void *)((unsigned long)((ip->i_d.di_size >> 32) & 0xffffffff)),
+ (void *)((unsigned long)(ip->i_d.di_size & 0xffffffff)),
+ (void *)(__psint_t)buf,
+ (void *)((unsigned long)size),
+ (void *)((unsigned long)((offset >> 32) & 0xffffffff)),
+ (void *)((unsigned long)(offset & 0xffffffff)),
+ (void *)((unsigned long)ioflags),
+ (void *)((unsigned long)((io->io_new_size >> 32) & 0xffffffff)),
+ (void *)((unsigned long)(io->io_new_size & 0xffffffff)),
+ (void *)NULL,
+ (void *)NULL,
+ (void *)NULL,
+ (void *)NULL,
+ (void *)NULL);
+}
+
+void
+xfs_inval_cached_trace(
+ xfs_iocore_t *io,
+ xfs_off_t offset,
+ xfs_off_t len,
+ xfs_off_t first,
+ xfs_off_t last)
+{
+ xfs_inode_t *ip = XFS_IO_INODE(io);
+
+ if (ip->i_rwtrace == NULL)
+ return;
+ ktrace_enter(ip->i_rwtrace,
+ (void *)(__psint_t)XFS_INVAL_CACHED,
+ (void *)ip,
+ (void *)((unsigned long)((offset >> 32) & 0xffffffff)),
+ (void *)((unsigned long)(offset & 0xffffffff)),
+ (void *)((unsigned long)((len >> 32) & 0xffffffff)),
+ (void *)((unsigned long)(len & 0xffffffff)),
+ (void *)((unsigned long)((first >> 32) & 0xffffffff)),
+ (void *)((unsigned long)(first & 0xffffffff)),
+ (void *)((unsigned long)((last >> 32) & 0xffffffff)),
+ (void *)((unsigned long)(last & 0xffffffff)),
+ (void *)NULL,
+ (void *)NULL,
+ (void *)NULL,
+ (void *)NULL,
+ (void *)NULL,
+ (void *)NULL);
+}
+#endif
+
+/*
+ * xfs_iozero
+ *
+ * xfs_iozero clears the specified range of buffer supplied,
+ * and marks all the affected blocks as valid and modified. If
+ * an affected block is not allocated, it will be allocated. If
+ * an affected block is not completely overwritten, and is not
+ * valid before the operation, it will be read from disk before
+ * being partially zeroed.
+ */
+STATIC int
+xfs_iozero(
+ struct inode *ip, /* inode */
+ loff_t pos, /* offset in file */
+ size_t count, /* size of data to zero */
+ loff_t end_size) /* max file size to set */
+{
+ unsigned bytes;
+ struct page *page;
+ struct address_space *mapping;
+ char *kaddr;
+ int status;
+
+ mapping = ip->i_mapping;
+ do {
+ unsigned long index, offset;
+
+ offset = (pos & (PAGE_CACHE_SIZE -1)); /* Within page */
+ index = pos >> PAGE_CACHE_SHIFT;
+ bytes = PAGE_CACHE_SIZE - offset;
+ if (bytes > count)
+ bytes = count;
+
+ status = -ENOMEM;
+ page = grab_cache_page(mapping, index);
+ if (!page)
+ break;
+
+ kaddr = kmap(page);
+ status = mapping->a_ops->prepare_write(NULL, page, offset,
+ offset + bytes);
+ if (status) {
+ goto unlock;
+ }
+
+ memset((void *) (kaddr + offset), 0, bytes);
+ flush_dcache_page(page);
+ status = mapping->a_ops->commit_write(NULL, page, offset,
+ offset + bytes);
+ if (!status) {
+ pos += bytes;
+ count -= bytes;
+ if (pos > i_size_read(ip))
+ i_size_write(ip, pos < end_size ? pos : end_size);
+ }
+
+unlock:
+ kunmap(page);
+ unlock_page(page);
+ page_cache_release(page);
+ if (status)
+ break;
+ } while (count);
+
+ return (-status);
+}
+
+/*
+ * xfs_inval_cached_pages
+ *
+ * This routine is responsible for keeping direct I/O and buffered I/O
+ * somewhat coherent. From here we make sure that we're at least
+ * temporarily holding the inode I/O lock exclusively and then call
+ * the page cache to flush and invalidate any cached pages. If there
+ * are no cached pages this routine will be very quick.
+ */
+void
+xfs_inval_cached_pages(
+ vnode_t *vp,
+ xfs_iocore_t *io,
+ xfs_off_t offset,
+ int write,
+ int relock)
+{
+ xfs_mount_t *mp;
+
+ if (!VN_CACHED(vp)) {
+ return;
+ }
+
+ mp = io->io_mount;
+
+ /*
+ * We need to get the I/O lock exclusively in order
+ * to safely invalidate pages and mappings.
+ */
+ if (relock) {
+ XFS_IUNLOCK(mp, io, XFS_IOLOCK_SHARED);
+ XFS_ILOCK(mp, io, XFS_IOLOCK_EXCL);
+ }
+
+ /* Writing beyond EOF creates a hole that must be zeroed */
+ if (write && (offset > XFS_SIZE(mp, io))) {
+ xfs_fsize_t isize;
+
+ XFS_ILOCK(mp, io, XFS_ILOCK_EXCL|XFS_EXTSIZE_RD);
+ isize = XFS_SIZE(mp, io);
+ if (offset > isize) {
+ xfs_zero_eof(vp, io, offset, isize, offset);
+ }
+ XFS_IUNLOCK(mp, io, XFS_ILOCK_EXCL|XFS_EXTSIZE_RD);
+ }
+
+ xfs_inval_cached_trace(io, offset, -1, ctooff(offtoct(offset)), -1);
+ VOP_FLUSHINVAL_PAGES(vp, ctooff(offtoct(offset)), -1, FI_REMAPF_LOCKED);
+ if (relock) {
+ XFS_ILOCK_DEMOTE(mp, io, XFS_IOLOCK_EXCL);
+ }
+}
+
+ssize_t /* bytes read, or (-) error */
+xfs_read(
+ bhv_desc_t *bdp,
+ struct file *file,
+ char *buf,
+ size_t size,
+ loff_t *offset,
+ int ioflags,
+ cred_t *credp)
+{
+ ssize_t ret;
+ xfs_fsize_t n;
+ xfs_inode_t *ip;
+ xfs_mount_t *mp;
+
+ ip = XFS_BHVTOI(bdp);
+ mp = ip->i_mount;
+
+ XFS_STATS_INC(xs_read_calls);
+
+ if (unlikely(ioflags & IO_ISDIRECT)) {
+ if ((ssize_t)size < 0)
+ return -XFS_ERROR(EINVAL);
+ if (((__psint_t)buf & BBMASK) ||
+ (*offset & mp->m_blockmask) ||
+ (size & mp->m_blockmask)) {
+ if (*offset >= ip->i_d.di_size) {
+ return (0);
+ }
+ return -XFS_ERROR(EINVAL);
+ }
+ }
+
+ n = XFS_MAXIOFFSET(mp) - *offset;
+ if ((n <= 0) || (size == 0))
+ return 0;
+
+ if (n < size)
+ size = n;
+
+ if (XFS_FORCED_SHUTDOWN(mp)) {
+ return -EIO;
+ }
+
+ if (!(ioflags & IO_ISLOCKED))
+ xfs_ilock(ip, XFS_IOLOCK_SHARED);
+
+ if (DM_EVENT_ENABLED(BHV_TO_VNODE(bdp)->v_vfsp, ip, DM_EVENT_READ) &&
+ !(ioflags & IO_INVIS)) {
+ int error;
+ vrwlock_t locktype = VRWLOCK_READ;
+ int dmflags = FILP_DELAY_FLAG(file) | DM_SEM_FLAG_RD(ioflags);
+
+ error = XFS_SEND_DATA(mp, DM_EVENT_READ, BHV_TO_VNODE(bdp), *offset, size,
+ dmflags, &locktype);
+ if (error) {
+ if (!(ioflags & IO_ISLOCKED))
+ xfs_iunlock(ip, XFS_IOLOCK_SHARED);
+ return -error;
+ }
+ }
+
+ if (unlikely(ioflags & IO_ISDIRECT)) {
+ xfs_rw_enter_trace(XFS_DIORD_ENTER, &ip->i_iocore,
+ buf, size, *offset, ioflags);
+ ret = (*offset < ip->i_d.di_size) ?
+ do_generic_direct_read(file, buf, size, offset) : 0;
+ UPDATE_ATIME(file->f_dentry->d_inode);
+ } else {
+ xfs_rw_enter_trace(XFS_READ_ENTER, &ip->i_iocore,
+ buf, size, *offset, ioflags);
+ ret = generic_file_read(file, buf, size, offset);
+ }
+
+ if (ret > 0)
+ XFS_STATS_ADD(xs_read_bytes, ret);
+
+ if (!(ioflags & IO_ISLOCKED))
+ xfs_iunlock(ip, XFS_IOLOCK_SHARED);
+
+ if (unlikely(ioflags & IO_INVIS)) {
+ /* generic_file_read updates the atime but we need to
+ * undo that because this I/O was supposed to be invisible.
+ */
+ struct inode *inode = LINVFS_GET_IP(BHV_TO_VNODE(bdp));
+ inode->i_atime = ip->i_d.di_atime.t_sec;
+ } else {
+ xfs_ichgtime(ip, XFS_ICHGTIME_ACC);
+ }
+
+ return ret;
+}
+
+/*
+ * This routine is called to handle zeroing any space in the last
+ * block of the file that is beyond the EOF. We do this since the
+ * size is being increased without writing anything to that block
+ * and we don't want anyone to read the garbage on the disk.
+ */
+STATIC int /* error (positive) */
+xfs_zero_last_block(
+ struct inode *ip,
+ xfs_iocore_t *io,
+ xfs_off_t offset,
+ xfs_fsize_t isize,
+ xfs_fsize_t end_size)
+{
+ xfs_fileoff_t last_fsb;
+ xfs_mount_t *mp;
+ int nimaps;
+ int zero_offset;
+ int zero_len;
+ int isize_fsb_offset;
+ int error = 0;
+ xfs_bmbt_irec_t imap;
+ loff_t loff;
+ size_t lsize;
+
+ ASSERT(ismrlocked(io->io_lock, MR_UPDATE) != 0);
+ ASSERT(offset > isize);
+
+ mp = io->io_mount;
+
+ isize_fsb_offset = XFS_B_FSB_OFFSET(mp, isize);
+ if (isize_fsb_offset == 0) {
+ /*
+ * There are no extra bytes in the last block on disk to
+ * zero, so return.
+ */
+ return 0;
+ }
+
+ last_fsb = XFS_B_TO_FSBT(mp, isize);
+ nimaps = 1;
+ error = XFS_BMAPI(mp, NULL, io, last_fsb, 1, 0, NULL, 0, &imap,
+ &nimaps, NULL);
+ if (error) {
+ return error;
+ }
+ ASSERT(nimaps > 0);
+ /*
+ * If the block underlying isize is just a hole, then there
+ * is nothing to zero.
+ */
+ if (imap.br_startblock == HOLESTARTBLOCK) {
+ return 0;
+ }
+ /*
+ * Zero the part of the last block beyond the EOF, and write it
+ * out sync. We need to drop the ilock while we do this so we
+ * don't deadlock when the buffer cache calls back to us.
+ */
+ XFS_IUNLOCK(mp, io, XFS_ILOCK_EXCL| XFS_EXTSIZE_RD);
+ loff = XFS_FSB_TO_B(mp, last_fsb);
+ lsize = XFS_FSB_TO_B(mp, 1);
+
+ zero_offset = isize_fsb_offset;
+ zero_len = mp->m_sb.sb_blocksize - isize_fsb_offset;
+
+ error = xfs_iozero(ip, loff + zero_offset, zero_len, end_size);
+
+ XFS_ILOCK(mp, io, XFS_ILOCK_EXCL|XFS_EXTSIZE_RD);
+ ASSERT(error >= 0);
+ return error;
+}
+
+/*
+ * Zero any on disk space between the current EOF and the new,
+ * larger EOF. This handles the normal case of zeroing the remainder
+ * of the last block in the file and the unusual case of zeroing blocks
+ * out beyond the size of the file. This second case only happens
+ * with fixed size extents and when the system crashes before the inode
+ * size was updated but after blocks were allocated. If fill is set,
+ * then any holes in the range are filled and zeroed. If not, the holes
+ * are left alone as holes.
+ */
+
+int /* error (positive) */
+xfs_zero_eof(
+ vnode_t *vp,
+ xfs_iocore_t *io,
+ xfs_off_t offset, /* starting I/O offset */
+ xfs_fsize_t isize, /* current inode size */
+ xfs_fsize_t end_size) /* terminal inode size */
+{
+ struct inode *ip = LINVFS_GET_IP(vp);
+ xfs_fileoff_t start_zero_fsb;
+ xfs_fileoff_t end_zero_fsb;
+ xfs_fileoff_t prev_zero_fsb;
+ xfs_fileoff_t zero_count_fsb;
+ xfs_fileoff_t last_fsb;
+ xfs_extlen_t buf_len_fsb;
+ xfs_extlen_t prev_zero_count;
+ xfs_mount_t *mp;
+ int nimaps;
+ int error = 0;
+ xfs_bmbt_irec_t imap;
+ loff_t loff;
+ size_t lsize;
+
+ ASSERT(ismrlocked(io->io_lock, MR_UPDATE));
+ ASSERT(ismrlocked(io->io_iolock, MR_UPDATE));
+
+ mp = io->io_mount;
+
+ /*
+ * First handle zeroing the block on which isize resides.
+ * We only zero a part of that block so it is handled specially.
+ */
+ error = xfs_zero_last_block(ip, io, offset, isize, end_size);
+ if (error) {
+ ASSERT(ismrlocked(io->io_lock, MR_UPDATE));
+ ASSERT(ismrlocked(io->io_iolock, MR_UPDATE));
+ return error;
+ }
+
+ /*
+ * Calculate the range between the new size and the old
+ * where blocks needing to be zeroed may exist. To get the
+ * block where the last byte in the file currently resides,
+ * we need to subtract one from the size and truncate back
+ * to a block boundary. We subtract 1 in case the size is
+ * exactly on a block boundary.
+ */
+ last_fsb = isize ? XFS_B_TO_FSBT(mp, isize - 1) : (xfs_fileoff_t)-1;
+ start_zero_fsb = XFS_B_TO_FSB(mp, (xfs_ufsize_t)isize);
+ end_zero_fsb = XFS_B_TO_FSBT(mp, offset - 1);
+ ASSERT((xfs_sfiloff_t)last_fsb < (xfs_sfiloff_t)start_zero_fsb);
+ if (last_fsb == end_zero_fsb) {
+ /*
+ * The size was only incremented on its last block.
+ * We took care of that above, so just return.
+ */
+ return 0;
+ }
+
+ ASSERT(start_zero_fsb <= end_zero_fsb);
+ prev_zero_fsb = NULLFILEOFF;
+ prev_zero_count = 0;
+ while (start_zero_fsb <= end_zero_fsb) {
+ nimaps = 1;
+ zero_count_fsb = end_zero_fsb - start_zero_fsb + 1;
+ error = XFS_BMAPI(mp, NULL, io, start_zero_fsb, zero_count_fsb,
+ 0, NULL, 0, &imap, &nimaps, NULL);
+ if (error) {
+ ASSERT(ismrlocked(io->io_lock, MR_UPDATE));
+ ASSERT(ismrlocked(io->io_iolock, MR_UPDATE));
+ return error;
+ }
+ ASSERT(nimaps > 0);
+
+ if (imap.br_state == XFS_EXT_UNWRITTEN ||
+ imap.br_startblock == HOLESTARTBLOCK) {
+ /*
+ * This loop handles initializing pages that were
+ * partially initialized by the code below this
+ * loop. It basically zeroes the part of the page
+ * that sits on a hole and sets the page as P_HOLE
+ * and calls remapf if it is a mapped file.
+ */
+ prev_zero_fsb = NULLFILEOFF;
+ prev_zero_count = 0;
+ start_zero_fsb = imap.br_startoff +
+ imap.br_blockcount;
+ ASSERT(start_zero_fsb <= (end_zero_fsb + 1));
+ continue;
+ }
+
+ /*
+ * There are blocks in the range requested.
+ * Zero them a single write at a time. We actually
+ * don't zero the entire range returned if it is
+ * too big and simply loop around to get the rest.
+ * That is not the most efficient thing to do, but it
+ * is simple and this path should not be exercised often.
+ */
+ buf_len_fsb = XFS_FILBLKS_MIN(imap.br_blockcount,
+ mp->m_writeio_blocks << 8);
+ /*
+ * Drop the inode lock while we're doing the I/O.
+ * We'll still have the iolock to protect us.
+ */
+ XFS_IUNLOCK(mp, io, XFS_ILOCK_EXCL|XFS_EXTSIZE_RD);
+
+ loff = XFS_FSB_TO_B(mp, start_zero_fsb);
+ lsize = XFS_FSB_TO_B(mp, buf_len_fsb);
+
+ error = xfs_iozero(ip, loff, lsize, end_size);
+
+ if (error) {
+ goto out_lock;
+ }
+
+ prev_zero_fsb = start_zero_fsb;
+ prev_zero_count = buf_len_fsb;
+ start_zero_fsb = imap.br_startoff + buf_len_fsb;
+ ASSERT(start_zero_fsb <= (end_zero_fsb + 1));
+
+ XFS_ILOCK(mp, io, XFS_ILOCK_EXCL|XFS_EXTSIZE_RD);
+ }
+
+ return 0;
+
+out_lock:
+
+ XFS_ILOCK(mp, io, XFS_ILOCK_EXCL|XFS_EXTSIZE_RD);
+ ASSERT(error >= 0);
+ return error;
+}
+
+ssize_t /* bytes written, or (-) error */
+xfs_write(
+ bhv_desc_t *bdp,
+ struct file *file,
+ const char *buf,
+ size_t size,
+ loff_t *offset,
+ int ioflags,
+ cred_t *credp)
+{
+ xfs_inode_t *xip;
+ xfs_mount_t *mp;
+ ssize_t ret;
+ int error = 0;
+ xfs_fsize_t isize, new_size;
+ xfs_fsize_t n, limit;
+ xfs_iocore_t *io;
+ vnode_t *vp;
+ int iolock;
+ int eventsent = 0;
+ vrwlock_t locktype;
+
+ XFS_STATS_INC(xs_write_calls);
+
+ vp = BHV_TO_VNODE(bdp);
+ xip = XFS_BHVTOI(bdp);
+
+ if (size == 0)
+ return 0;
+
+ io = &xip->i_iocore;
+ mp = io->io_mount;
+
+ fs_check_frozen(vp->v_vfsp, SB_FREEZE_WRITE);
+
+ if (XFS_FORCED_SHUTDOWN(xip->i_mount)) {
+ return -EIO;
+ }
+
+ if (unlikely(ioflags & IO_ISDIRECT)) {
+ if (((__psint_t)buf & BBMASK) ||
+ (*offset & mp->m_blockmask) ||
+ (size & mp->m_blockmask)) {
+ return XFS_ERROR(-EINVAL);
+ }
+ iolock = XFS_IOLOCK_SHARED;
+ locktype = VRWLOCK_WRITE_DIRECT;
+ } else {
+ iolock = XFS_IOLOCK_EXCL;
+ locktype = VRWLOCK_WRITE;
+ }
+
+ if (ioflags & IO_ISLOCKED)
+ iolock = 0;
+
+ xfs_ilock(xip, XFS_ILOCK_EXCL|iolock);
+
+ isize = xip->i_d.di_size;
+ limit = XFS_MAXIOFFSET(mp);
+
+ if (file->f_flags & O_APPEND)
+ *offset = isize;
+
+start:
+ n = limit - *offset;
+ if (n <= 0) {
+ xfs_iunlock(xip, XFS_ILOCK_EXCL|iolock);
+ return -EFBIG;
+ }
+ if (n < size)
+ size = n;
+
+ new_size = *offset + size;
+ if (new_size > isize) {
+ io->io_new_size = new_size;
+ }
+
+ if ((DM_EVENT_ENABLED(vp->v_vfsp, xip, DM_EVENT_WRITE) &&
+ !(ioflags & IO_INVIS) && !eventsent)) {
+ loff_t savedsize = *offset;
+ int dmflags = FILP_DELAY_FLAG(file) | DM_SEM_FLAG_RD(ioflags);
+
+ xfs_iunlock(xip, XFS_ILOCK_EXCL);
+ error = XFS_SEND_DATA(xip->i_mount, DM_EVENT_WRITE, vp,
+ *offset, size,
+ dmflags, &locktype);
+ if (error) {
+ if (iolock) xfs_iunlock(xip, iolock);
+ return -error;
+ }
+ xfs_ilock(xip, XFS_ILOCK_EXCL);
+ eventsent = 1;
+
+ /*
+ * The iolock was dropped and reaquired in XFS_SEND_DATA
+ * so we have to recheck the size when appending.
+ * We will only "goto start;" once, since having sent the
+ * event prevents another call to XFS_SEND_DATA, which is
+ * what allows the size to change in the first place.
+ */
+ if ((file->f_flags & O_APPEND) &&
+ savedsize != xip->i_d.di_size) {
+ *offset = isize = xip->i_d.di_size;
+ goto start;
+ }
+ }
+
+ /*
+ * If the offset is beyond the size of the file, we have a couple
+ * of things to do. First, if there is already space allocated
+ * we need to either create holes or zero the disk or ...
+ *
+ * If there is a page where the previous size lands, we need
+ * to zero it out up to the new size.
+ */
+
+ if (!(ioflags & IO_ISDIRECT) && (*offset > isize && isize)) {
+ error = xfs_zero_eof(BHV_TO_VNODE(bdp), io, *offset,
+ isize, *offset + size);
+ if (error) {
+ xfs_iunlock(xip, XFS_ILOCK_EXCL|iolock);
+ return(-error);
+ }
+ }
+ xfs_iunlock(xip, XFS_ILOCK_EXCL);
+
+ /*
+ * If we're writing the file then make sure to clear the
+ * setuid and setgid bits if the process is not being run
+ * by root. This keeps people from modifying setuid and
+ * setgid binaries.
+ */
+
+ if (((xip->i_d.di_mode & S_ISUID) ||
+ ((xip->i_d.di_mode & (S_ISGID | S_IXGRP)) ==
+ (S_ISGID | S_IXGRP))) &&
+ !capable(CAP_FSETID)) {
+ error = xfs_write_clear_setuid(xip);
+ if (error) {
+ xfs_iunlock(xip, iolock);
+ return -error;
+ }
+ }
+
+
+ if ((ssize_t) size < 0) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (!access_ok(VERIFY_READ, buf, size)) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+retry:
+ if (unlikely(ioflags & IO_ISDIRECT)) {
+ xfs_inval_cached_pages(vp, io, *offset, 1, 1);
+ xfs_rw_enter_trace(XFS_DIOWR_ENTER,
+ io, buf, size, *offset, ioflags);
+ ret = do_generic_direct_write(file, buf, size, offset);
+ } else {
+ xfs_rw_enter_trace(XFS_WRITE_ENTER,
+ io, buf, size, *offset, ioflags);
+ ret = do_generic_file_write(file, buf, size, offset);
+ }
+
+ if (unlikely(ioflags & IO_INVIS)) {
+ /* generic_file_write updates the mtime/ctime but we need
+ * to undo that because this I/O was supposed to be
+ * invisible.
+ */
+ struct inode *inode = LINVFS_GET_IP(vp);
+ inode->i_mtime = xip->i_d.di_mtime.t_sec;
+ inode->i_ctime = xip->i_d.di_ctime.t_sec;
+ } else {
+ xfs_ichgtime(xip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
+ }
+
+ if ((ret == -ENOSPC) &&
+ DM_EVENT_ENABLED(vp->v_vfsp, xip, DM_EVENT_NOSPACE) &&
+ !(ioflags & IO_INVIS)) {
+
+ xfs_rwunlock(bdp, locktype);
+ error = XFS_SEND_NAMESP(xip->i_mount, DM_EVENT_NOSPACE, vp,
+ DM_RIGHT_NULL, vp, DM_RIGHT_NULL, NULL, NULL,
+ 0, 0, 0); /* Delay flag intentionally unused */
+ if (error)
+ return -error;
+ xfs_rwlock(bdp, locktype);
+ *offset = xip->i_d.di_size;
+ goto retry;
+ }
+
+error:
+ if (ret <= 0) {
+ if (iolock)
+ xfs_rwunlock(bdp, locktype);
+ return ret;
+ }
+
+ XFS_STATS_ADD(xs_write_bytes, ret);
+
+ if (*offset > xip->i_d.di_size) {
+ xfs_ilock(xip, XFS_ILOCK_EXCL);
+ if (*offset > xip->i_d.di_size) {
+ struct inode *inode = LINVFS_GET_IP(vp);
+
+ xip->i_d.di_size = *offset;
+ i_size_write(inode, *offset);
+ xip->i_update_core = 1;
+ xip->i_update_size = 1;
+ mark_inode_dirty_sync(inode);
+ }
+ xfs_iunlock(xip, XFS_ILOCK_EXCL);
+ }
+
+ /* Handle various SYNC-type writes */
+ if ((file->f_flags & O_SYNC) || IS_SYNC(file->f_dentry->d_inode)) {
+
+ /*
+ * If we're treating this as O_DSYNC and we have not updated the
+ * size, force the log.
+ */
+
+ if (!(mp->m_flags & XFS_MOUNT_OSYNCISOSYNC)
+ && !(xip->i_update_size)) {
+ /*
+ * If an allocation transaction occurred
+ * without extending the size, then we have to force
+ * the log up the proper point to ensure that the
+ * allocation is permanent. We can't count on
+ * the fact that buffered writes lock out direct I/O
+ * writes - the direct I/O write could have extended
+ * the size nontransactionally, then finished before
+ * we started. xfs_write_file will think that the file
+ * didn't grow but the update isn't safe unless the
+ * size change is logged.
+ *
+ * Force the log if we've committed a transaction
+ * against the inode or if someone else has and
+ * the commit record hasn't gone to disk (e.g.
+ * the inode is pinned). This guarantees that
+ * all changes affecting the inode are permanent
+ * when we return.
+ */
+
+ xfs_inode_log_item_t *iip;
+ xfs_lsn_t lsn;
+
+ iip = xip->i_itemp;
+ if (iip && iip->ili_last_lsn) {
+ lsn = iip->ili_last_lsn;
+ xfs_log_force(mp, lsn,
+ XFS_LOG_FORCE | XFS_LOG_SYNC);
+ } else if (xfs_ipincount(xip) > 0) {
+ xfs_log_force(mp, (xfs_lsn_t)0,
+ XFS_LOG_FORCE | XFS_LOG_SYNC);
+ }
+
+ } else {
+ xfs_trans_t *tp;
+
+ /*
+ * O_SYNC or O_DSYNC _with_ a size update are handled
+ * the same way.
+ *
+ * If the write was synchronous then we need to make
+ * sure that the inode modification time is permanent.
+ * We'll have updated the timestamp above, so here
+ * we use a synchronous transaction to log the inode.
+ * It's not fast, but it's necessary.
+ *
+ * If this a dsync write and the size got changed
+ * non-transactionally, then we need to ensure that
+ * the size change gets logged in a synchronous
+ * transaction.
+ */
+
+ tp = xfs_trans_alloc(mp, XFS_TRANS_WRITE_SYNC);
+ if ((error = xfs_trans_reserve(tp, 0,
+ XFS_SWRITE_LOG_RES(mp),
+ 0, 0, 0))) {
+ /* Transaction reserve failed */
+ xfs_trans_cancel(tp, 0);
+ } else {
+ /* Transaction reserve successful */
+ xfs_ilock(xip, XFS_ILOCK_EXCL);
+ xfs_trans_ijoin(tp, xip, XFS_ILOCK_EXCL);
+ xfs_trans_ihold(tp, xip);
+ xfs_trans_log_inode(tp, xip, XFS_ILOG_CORE);
+ xfs_trans_set_sync(tp);
+ error = xfs_trans_commit(tp, 0, NULL);
+ xfs_iunlock(xip, XFS_ILOCK_EXCL);
+ }
+ }
+ } /* (ioflags & O_SYNC) */
+
+ /*
+ * If we are coming from an nfsd thread then insert into the
+ * reference cache.
+ */
+
+ if (!strcmp(current->comm, "nfsd"))
+ xfs_refcache_insert(xip);
+
+ /* Drop lock this way - the old refcache release is in here */
+ if (iolock)
+ xfs_rwunlock(bdp, locktype);
+
+ return(ret);
+}
+
+/*
+ * All xfs metadata buffers except log state machine buffers
+ * get this attached as their b_bdstrat callback function.
+ * This is so that we can catch a buffer
+ * after prematurely unpinning it to forcibly shutdown the filesystem.
+ */
+int
+xfs_bdstrat_cb(struct xfs_buf *bp)
+{
+ xfs_mount_t *mp;
+
+ mp = XFS_BUF_FSPRIVATE3(bp, xfs_mount_t *);
+ if (!XFS_FORCED_SHUTDOWN(mp)) {
+ pagebuf_iorequest(bp);
+ return 0;
+ } else {
+ xfs_buftrace("XFS__BDSTRAT IOERROR", bp);
+ /*
+ * Metadata write that didn't get logged but
+ * written delayed anyway. These aren't associated
+ * with a transaction, and can be ignored.
+ */
+ if (XFS_BUF_IODONE_FUNC(bp) == NULL &&
+ (XFS_BUF_ISREAD(bp)) == 0)
+ return (xfs_bioerror_relse(bp));
+ else
+ return (xfs_bioerror(bp));
+ }
+}
+
+
+int
+xfs_bmap(bhv_desc_t *bdp,
+ xfs_off_t offset,
+ ssize_t count,
+ int flags,
+ xfs_iomap_t *iomapp,
+ int *niomaps)
+{
+ xfs_inode_t *ip = XFS_BHVTOI(bdp);
+ xfs_iocore_t *io = &ip->i_iocore;
+
+ ASSERT((ip->i_d.di_mode & S_IFMT) == S_IFREG);
+ ASSERT(((ip->i_d.di_flags & XFS_DIFLAG_REALTIME) != 0) ==
+ ((ip->i_iocore.io_flags & XFS_IOCORE_RT) != 0));
+
+ return xfs_iomap(io, offset, count, flags, iomapp, niomaps);
+}
+
+/*
+ * Wrapper around bdstrat so that we can stop data
+ * from going to disk in case we are shutting down the filesystem.
+ * Typically user data goes thru this path; one of the exceptions
+ * is the superblock.
+ */
+int
+xfsbdstrat(
+ struct xfs_mount *mp,
+ struct xfs_buf *bp)
+{
+ ASSERT(mp);
+ if (!XFS_FORCED_SHUTDOWN(mp)) {
+ /* Grio redirection would go here
+ * if (XFS_BUF_IS_GRIO(bp)) {
+ */
+
+ pagebuf_iorequest(bp);
+ return 0;
+ }
+
+ xfs_buftrace("XFSBDSTRAT IOERROR", bp);
+ return (xfs_bioerror_relse(bp));
+}
+
+/*
+ * If the underlying (data/log/rt) device is readonly, there are some
+ * operations that cannot proceed.
+ */
+int
+xfs_dev_is_read_only(
+ xfs_mount_t *mp,
+ char *message)
+{
+ if (xfs_readonly_buftarg(mp->m_ddev_targp) ||
+ xfs_readonly_buftarg(mp->m_logdev_targp) ||
+ (mp->m_rtdev_targp && xfs_readonly_buftarg(mp->m_rtdev_targp))) {
+ cmn_err(CE_NOTE,
+ "XFS: %s required on read-only device.", message);
+ cmn_err(CE_NOTE,
+ "XFS: write access unavailable, cannot proceed.");
+ return EROFS;
+ }
+ return 0;
+}
diff --git a/fs/xfs/linux-2.4/xfs_lrw.h b/fs/xfs/linux-2.4/xfs_lrw.h
new file mode 100644
index 00000000000000..1ee62d3af5de3f
--- /dev/null
+++ b/fs/xfs/linux-2.4/xfs_lrw.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_LRW_H__
+#define __XFS_LRW_H__
+
+struct vnode;
+struct bhv_desc;
+struct xfs_mount;
+struct xfs_iocore;
+struct xfs_inode;
+struct xfs_bmbt_irec;
+struct xfs_buf;
+struct xfs_iomap;
+
+#if defined(XFS_RW_TRACE)
+/*
+ * Defines for the trace mechanisms in xfs_lrw.c.
+ */
+#define XFS_RW_KTRACE_SIZE 128
+
+#define XFS_READ_ENTER 1
+#define XFS_WRITE_ENTER 2
+#define XFS_IOMAP_READ_ENTER 3
+#define XFS_IOMAP_WRITE_ENTER 4
+#define XFS_IOMAP_READ_MAP 5
+#define XFS_IOMAP_WRITE_MAP 6
+#define XFS_IOMAP_WRITE_NOSPACE 7
+#define XFS_ITRUNC_START 8
+#define XFS_ITRUNC_FINISH1 9
+#define XFS_ITRUNC_FINISH2 10
+#define XFS_CTRUNC1 11
+#define XFS_CTRUNC2 12
+#define XFS_CTRUNC3 13
+#define XFS_CTRUNC4 14
+#define XFS_CTRUNC5 15
+#define XFS_CTRUNC6 16
+#define XFS_BUNMAPI 17
+#define XFS_INVAL_CACHED 18
+#define XFS_DIORD_ENTER 19
+#define XFS_DIOWR_ENTER 20
+#define XFS_SENDFILE_ENTER 21
+#define XFS_WRITEPAGE_ENTER 22
+#define XFS_RELEASEPAGE_ENTER 23
+#define XFS_IOMAP_ALLOC_ENTER 24
+#define XFS_IOMAP_ALLOC_MAP 25
+#define XFS_IOMAP_UNWRITTEN 26
+extern void xfs_rw_enter_trace(int, struct xfs_iocore *,
+ const char *, size_t, loff_t, int);
+extern void xfs_inval_cached_trace(struct xfs_iocore *,
+ xfs_off_t, xfs_off_t, xfs_off_t, xfs_off_t);
+#else
+#define xfs_rw_enter_trace(tag, io, buf, size, offset, ioflags)
+#define xfs_inval_cached_trace(io, offset, len, first, last)
+#endif
+
+/*
+ * Maximum count of bmaps used by read and write paths.
+ */
+#define XFS_MAX_RW_NBMAPS 4
+
+extern int xfs_bmap(struct bhv_desc *, xfs_off_t, ssize_t, int,
+ struct xfs_iomap *, int *);
+extern int xfsbdstrat(struct xfs_mount *, struct xfs_buf *);
+extern int xfs_bdstrat_cb(struct xfs_buf *);
+
+extern int xfs_zero_eof(struct vnode *, struct xfs_iocore *, xfs_off_t,
+ xfs_fsize_t, xfs_fsize_t);
+extern void xfs_inval_cached_pages(struct vnode *, struct xfs_iocore *,
+ xfs_off_t, int, int);
+extern ssize_t xfs_read(struct bhv_desc *, struct file *, char *,
+ size_t, loff_t *, int, struct cred *);
+extern ssize_t xfs_write(struct bhv_desc *, struct file *, const char *,
+ size_t, loff_t *, int, struct cred *);
+
+extern int xfs_dev_is_read_only(struct xfs_mount *, char *);
+
+#define XFS_FSB_TO_DB_IO(io,fsb) \
+ (((io)->io_flags & XFS_IOCORE_RT) ? \
+ XFS_FSB_TO_BB((io)->io_mount, (fsb)) : \
+ XFS_FSB_TO_DADDR((io)->io_mount, (fsb)))
+
+#endif /* __XFS_LRW_H__ */
diff --git a/fs/xfs/linux-2.4/xfs_stats.c b/fs/xfs/linux-2.4/xfs_stats.c
new file mode 100644
index 00000000000000..f6b5ee2de4cf6b
--- /dev/null
+++ b/fs/xfs/linux-2.4/xfs_stats.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+#include <linux/proc_fs.h>
+
+struct xfsstats xfsstats;
+
+STATIC int
+xfs_read_xfsstats(
+ char *buffer,
+ char **start,
+ off_t offset,
+ int count,
+ int *eof,
+ void *data)
+{
+ int i, j, len;
+ static struct xstats_entry {
+ char *desc;
+ int endpoint;
+ } xstats[] = {
+ { "extent_alloc", XFSSTAT_END_EXTENT_ALLOC },
+ { "abt", XFSSTAT_END_ALLOC_BTREE },
+ { "blk_map", XFSSTAT_END_BLOCK_MAPPING },
+ { "bmbt", XFSSTAT_END_BLOCK_MAP_BTREE },
+ { "dir", XFSSTAT_END_DIRECTORY_OPS },
+ { "trans", XFSSTAT_END_TRANSACTIONS },
+ { "ig", XFSSTAT_END_INODE_OPS },
+ { "log", XFSSTAT_END_LOG_OPS },
+ { "push_ail", XFSSTAT_END_TAIL_PUSHING },
+ { "xstrat", XFSSTAT_END_WRITE_CONVERT },
+ { "rw", XFSSTAT_END_READ_WRITE_OPS },
+ { "attr", XFSSTAT_END_ATTRIBUTE_OPS },
+ { "icluster", XFSSTAT_END_INODE_CLUSTER },
+ { "vnodes", XFSSTAT_END_VNODE_OPS },
+ { "buf", XFSSTAT_END_BUF },
+ };
+
+ for (i=j=len = 0; i < sizeof(xstats)/sizeof(struct xstats_entry); i++) {
+ len += sprintf(buffer + len, xstats[i].desc);
+ /* inner loop does each group */
+ while (j < xstats[i].endpoint) {
+ len += sprintf(buffer + len, " %u",
+ *(((__u32*)&xfsstats) + j));
+ j++;
+ }
+ buffer[len++] = '\n';
+ }
+ /* extra precision counters */
+ len += sprintf(buffer + len, "xpc %Lu %Lu %Lu\n",
+ xfsstats.xs_xstrat_bytes,
+ xfsstats.xs_write_bytes,
+ xfsstats.xs_read_bytes);
+ len += sprintf(buffer + len, "debug %u\n",
+#if defined(DEBUG)
+ 1);
+#else
+ 0);
+#endif
+
+ if (offset >= len) {
+ *start = buffer;
+ *eof = 1;
+ return 0;
+ }
+ *start = buffer + offset;
+ if ((len -= offset) > count)
+ return count;
+ *eof = 1;
+
+ return len;
+}
+
+void
+xfs_init_procfs(void)
+{
+ if (!proc_mkdir("fs/xfs", 0))
+ return;
+ create_proc_read_entry("fs/xfs/stat", 0, 0, xfs_read_xfsstats, NULL);
+}
+
+void
+xfs_cleanup_procfs(void)
+{
+ remove_proc_entry("fs/xfs/stat", NULL);
+ remove_proc_entry("fs/xfs", NULL);
+}
diff --git a/fs/xfs/linux-2.4/xfs_stats.h b/fs/xfs/linux-2.4/xfs_stats.h
new file mode 100644
index 00000000000000..11a8c4850612d1
--- /dev/null
+++ b/fs/xfs/linux-2.4/xfs_stats.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_STATS_H__
+#define __XFS_STATS_H__
+
+
+#if defined(CONFIG_PROC_FS) && !defined(XFS_STATS_OFF)
+
+/*
+ * XFS global statistics
+ */
+struct xfsstats {
+# define XFSSTAT_END_EXTENT_ALLOC 4
+ __uint32_t xs_allocx;
+ __uint32_t xs_allocb;
+ __uint32_t xs_freex;
+ __uint32_t xs_freeb;
+# define XFSSTAT_END_ALLOC_BTREE (XFSSTAT_END_EXTENT_ALLOC+4)
+ __uint32_t xs_abt_lookup;
+ __uint32_t xs_abt_compare;
+ __uint32_t xs_abt_insrec;
+ __uint32_t xs_abt_delrec;
+# define XFSSTAT_END_BLOCK_MAPPING (XFSSTAT_END_ALLOC_BTREE+7)
+ __uint32_t xs_blk_mapr;
+ __uint32_t xs_blk_mapw;
+ __uint32_t xs_blk_unmap;
+ __uint32_t xs_add_exlist;
+ __uint32_t xs_del_exlist;
+ __uint32_t xs_look_exlist;
+ __uint32_t xs_cmp_exlist;
+# define XFSSTAT_END_BLOCK_MAP_BTREE (XFSSTAT_END_BLOCK_MAPPING+4)
+ __uint32_t xs_bmbt_lookup;
+ __uint32_t xs_bmbt_compare;
+ __uint32_t xs_bmbt_insrec;
+ __uint32_t xs_bmbt_delrec;
+# define XFSSTAT_END_DIRECTORY_OPS (XFSSTAT_END_BLOCK_MAP_BTREE+4)
+ __uint32_t xs_dir_lookup;
+ __uint32_t xs_dir_create;
+ __uint32_t xs_dir_remove;
+ __uint32_t xs_dir_getdents;
+# define XFSSTAT_END_TRANSACTIONS (XFSSTAT_END_DIRECTORY_OPS+3)
+ __uint32_t xs_trans_sync;
+ __uint32_t xs_trans_async;
+ __uint32_t xs_trans_empty;
+# define XFSSTAT_END_INODE_OPS (XFSSTAT_END_TRANSACTIONS+7)
+ __uint32_t xs_ig_attempts;
+ __uint32_t xs_ig_found;
+ __uint32_t xs_ig_frecycle;
+ __uint32_t xs_ig_missed;
+ __uint32_t xs_ig_dup;
+ __uint32_t xs_ig_reclaims;
+ __uint32_t xs_ig_attrchg;
+# define XFSSTAT_END_LOG_OPS (XFSSTAT_END_INODE_OPS+5)
+ __uint32_t xs_log_writes;
+ __uint32_t xs_log_blocks;
+ __uint32_t xs_log_noiclogs;
+ __uint32_t xs_log_force;
+ __uint32_t xs_log_force_sleep;
+# define XFSSTAT_END_TAIL_PUSHING (XFSSTAT_END_LOG_OPS+10)
+ __uint32_t xs_try_logspace;
+ __uint32_t xs_sleep_logspace;
+ __uint32_t xs_push_ail;
+ __uint32_t xs_push_ail_success;
+ __uint32_t xs_push_ail_pushbuf;
+ __uint32_t xs_push_ail_pinned;
+ __uint32_t xs_push_ail_locked;
+ __uint32_t xs_push_ail_flushing;
+ __uint32_t xs_push_ail_restarts;
+ __uint32_t xs_push_ail_flush;
+# define XFSSTAT_END_WRITE_CONVERT (XFSSTAT_END_TAIL_PUSHING+2)
+ __uint32_t xs_xstrat_quick;
+ __uint32_t xs_xstrat_split;
+# define XFSSTAT_END_READ_WRITE_OPS (XFSSTAT_END_WRITE_CONVERT+2)
+ __uint32_t xs_write_calls;
+ __uint32_t xs_read_calls;
+# define XFSSTAT_END_ATTRIBUTE_OPS (XFSSTAT_END_READ_WRITE_OPS+4)
+ __uint32_t xs_attr_get;
+ __uint32_t xs_attr_set;
+ __uint32_t xs_attr_remove;
+ __uint32_t xs_attr_list;
+# define XFSSTAT_END_INODE_CLUSTER (XFSSTAT_END_ATTRIBUTE_OPS+3)
+ __uint32_t xs_iflush_count;
+ __uint32_t xs_icluster_flushcnt;
+ __uint32_t xs_icluster_flushinode;
+# define XFSSTAT_END_VNODE_OPS (XFSSTAT_END_INODE_CLUSTER+8)
+ __uint32_t vn_active; /* # vnodes not on free lists */
+ __uint32_t vn_alloc; /* # times vn_alloc called */
+ __uint32_t vn_get; /* # times vn_get called */
+ __uint32_t vn_hold; /* # times vn_hold called */
+ __uint32_t vn_rele; /* # times vn_rele called */
+ __uint32_t vn_reclaim; /* # times vn_reclaim called */
+ __uint32_t vn_remove; /* # times vn_remove called */
+ __uint32_t vn_free; /* # times vn_free called */
+#define XFSSTAT_END_BUF (XFSSTAT_END_VNODE_OPS+9)
+ __uint32_t pb_get;
+ __uint32_t pb_create;
+ __uint32_t pb_get_locked;
+ __uint32_t pb_get_locked_waited;
+ __uint32_t pb_busy_locked;
+ __uint32_t pb_miss_locked;
+ __uint32_t pb_page_retries;
+ __uint32_t pb_page_found;
+ __uint32_t pb_get_read;
+/* Extra precision counters */
+ __uint64_t xs_xstrat_bytes;
+ __uint64_t xs_write_bytes;
+ __uint64_t xs_read_bytes;
+};
+
+extern struct xfsstats xfsstats;
+
+# define XFS_STATS_INC(count) ( xfsstats.count++ )
+# define XFS_STATS_DEC(count) ( xfsstats.count-- )
+# define XFS_STATS_ADD(count, inc) ( xfsstats.count += (inc) )
+
+extern void xfs_init_procfs(void);
+extern void xfs_cleanup_procfs(void);
+
+
+#else /* !CONFIG_PROC_FS */
+
+# define XFS_STATS_INC(count)
+# define XFS_STATS_DEC(count)
+# define XFS_STATS_ADD(count, inc)
+
+static __inline void xfs_init_procfs(void) { };
+static __inline void xfs_cleanup_procfs(void) { };
+
+#endif /* !CONFIG_PROC_FS */
+
+#endif /* __XFS_STATS_H__ */
diff --git a/fs/xfs/linux-2.4/xfs_super.c b/fs/xfs/linux-2.4/xfs_super.c
new file mode 100644
index 00000000000000..7ce75cdcdd02c8
--- /dev/null
+++ b/fs/xfs/linux-2.4/xfs_super.c
@@ -0,0 +1,1066 @@
+/*
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_clnt.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_alloc.h"
+#include "xfs_dmapi.h"
+#include "xfs_quota.h"
+#include "xfs_mount.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode.h"
+#include "xfs_bmap.h"
+#include "xfs_bit.h"
+#include "xfs_rtalloc.h"
+#include "xfs_error.h"
+#include "xfs_itable.h"
+#include "xfs_rw.h"
+#include "xfs_acl.h"
+#include "xfs_cap.h"
+#include "xfs_mac.h"
+#include "xfs_attr.h"
+#include "xfs_buf_item.h"
+#include "xfs_utils.h"
+#include "xfs_version.h"
+
+#include <linux/init.h>
+
+STATIC struct quotactl_ops linvfs_qops;
+STATIC struct super_operations linvfs_sops;
+STATIC kmem_zone_t *linvfs_inode_zone;
+STATIC kmem_shaker_t xfs_inode_shaker;
+
+STATIC struct xfs_mount_args *
+xfs_args_allocate(
+ struct super_block *sb)
+{
+ struct xfs_mount_args *args;
+
+ args = kmem_zalloc(sizeof(struct xfs_mount_args), KM_SLEEP);
+ args->logbufs = args->logbufsize = -1;
+ strncpy(args->fsname, bdevname(sb->s_dev), MAXNAMELEN);
+
+ /* Copy the already-parsed mount(2) flags we're interested in */
+ if (sb->s_flags & MS_NOATIME)
+ args->flags |= XFSMNT_NOATIME;
+
+ /* Default to 32 bit inodes on Linux all the time */
+ args->flags |= XFSMNT_32BITINODES;
+
+ return args;
+}
+
+__uint64_t
+xfs_max_file_offset(
+ unsigned int blockshift)
+{
+ unsigned int pagefactor = 1;
+ unsigned int bitshift = BITS_PER_LONG - 1;
+
+ /* Figure out maximum filesize, on Linux this can depend on
+ * the filesystem blocksize (on 32 bit platforms).
+ * __block_prepare_write does this in an [unsigned] long...
+ * page->index << (PAGE_CACHE_SHIFT - bbits)
+ * So, for page sized blocks (4K on 32 bit platforms),
+ * this wraps at around 8Tb (hence MAX_LFS_FILESIZE which is
+ * (((u64)PAGE_CACHE_SIZE << (BITS_PER_LONG-1))-1)
+ * but for smaller blocksizes it is less (bbits = log2 bsize).
+ * Note1: get_block_t takes a long (implicit cast from above)
+ * Note2: The Large Block Device (LBD and HAVE_SECTOR_T) patch
+ * can optionally convert the [unsigned] long from above into
+ * an [unsigned] long long.
+ */
+
+#if BITS_PER_LONG == 32
+ pagefactor = PAGE_CACHE_SIZE >> (PAGE_CACHE_SHIFT - blockshift);
+#endif
+
+ return (((__uint64_t)pagefactor) << bitshift) - 1;
+}
+
+STATIC __inline__ void
+xfs_set_inodeops(
+ struct inode *inode)
+{
+ vnode_t *vp = LINVFS_GET_VP(inode);
+
+ if (vp->v_type == VNON) {
+ vn_mark_bad(vp);
+ } else if (S_ISREG(inode->i_mode)) {
+ inode->i_op = &linvfs_file_inode_operations;
+ inode->i_fop = &linvfs_file_operations;
+ inode->i_mapping->a_ops = &linvfs_aops;
+ } else if (S_ISDIR(inode->i_mode)) {
+ inode->i_op = &linvfs_dir_inode_operations;
+ inode->i_fop = &linvfs_dir_operations;
+ } else if (S_ISLNK(inode->i_mode)) {
+ inode->i_op = &linvfs_symlink_inode_operations;
+ if (inode->i_blocks)
+ inode->i_mapping->a_ops = &linvfs_aops;
+ } else {
+ inode->i_op = &linvfs_file_inode_operations;
+ init_special_inode(inode, inode->i_mode,
+ kdev_t_to_nr(inode->i_rdev));
+ }
+}
+
+STATIC __inline__ void
+xfs_revalidate_inode(
+ xfs_mount_t *mp,
+ vnode_t *vp,
+ xfs_inode_t *ip)
+{
+ struct inode *inode = LINVFS_GET_IP(vp);
+
+ inode->i_mode = (ip->i_d.di_mode & MODEMASK) | VTTOIF(vp->v_type);
+ inode->i_nlink = ip->i_d.di_nlink;
+ inode->i_uid = ip->i_d.di_uid;
+ inode->i_gid = ip->i_d.di_gid;
+ if (((1 << vp->v_type) & ((1<<VBLK) | (1<<VCHR))) == 0) {
+ inode->i_rdev = NODEV;
+ } else {
+ xfs_dev_t dev = ip->i_df.if_u2.if_rdev;
+ inode->i_rdev = XFS_DEV_TO_KDEVT(dev);
+ }
+ inode->i_blksize = PAGE_CACHE_SIZE;
+ inode->i_generation = ip->i_d.di_gen;
+ i_size_write(inode, ip->i_d.di_size);
+ inode->i_blocks =
+ XFS_FSB_TO_BB(mp, ip->i_d.di_nblocks + ip->i_delayed_blks);
+ inode->i_atime = ip->i_d.di_atime.t_sec;
+ inode->i_mtime = ip->i_d.di_mtime.t_sec;
+ inode->i_ctime = ip->i_d.di_ctime.t_sec;
+ if (ip->i_d.di_flags & XFS_DIFLAG_IMMUTABLE)
+ inode->i_flags |= S_IMMUTABLE;
+ else
+ inode->i_flags &= ~S_IMMUTABLE;
+ if (ip->i_d.di_flags & XFS_DIFLAG_APPEND)
+ inode->i_flags |= S_APPEND;
+ else
+ inode->i_flags &= ~S_APPEND;
+ if (ip->i_d.di_flags & XFS_DIFLAG_SYNC)
+ inode->i_flags |= S_SYNC;
+ else
+ inode->i_flags &= ~S_SYNC;
+ if (ip->i_d.di_flags & XFS_DIFLAG_NOATIME)
+ inode->i_flags |= S_NOATIME;
+ else
+ inode->i_flags &= ~S_NOATIME;
+
+ vp->v_flag &= ~VMODIFIED;
+}
+
+void
+xfs_initialize_vnode(
+ bhv_desc_t *bdp,
+ vnode_t *vp,
+ bhv_desc_t *inode_bhv,
+ int unlock)
+{
+ xfs_inode_t *ip = XFS_BHVTOI(inode_bhv);
+ struct inode *inode = LINVFS_GET_IP(vp);
+
+ if (!inode_bhv->bd_vobj) {
+ vp->v_vfsp = bhvtovfs(bdp);
+ bhv_desc_init(inode_bhv, ip, vp, &xfs_vnodeops);
+ bhv_insert(VN_BHV_HEAD(vp), inode_bhv);
+ }
+
+ /*
+ * We need to set the ops vectors, and unlock the inode, but if
+ * we have been called during the new inode create process, it is
+ * too early to fill in the Linux inode. We will get called a
+ * second time once the inode is properly set up, and then we can
+ * finish our work.
+ */
+ if (ip->i_d.di_mode != 0 && unlock && (inode->i_state & I_NEW)) {
+ vp->v_type = IFTOVT(ip->i_d.di_mode);
+ xfs_revalidate_inode(XFS_BHVTOM(bdp), vp, ip);
+ xfs_set_inodeops(inode);
+
+ ip->i_flags &= ~XFS_INEW;
+ barrier();
+
+ unlock_new_inode(inode);
+ }
+}
+
+int
+xfs_inode_shake(
+ int priority,
+ unsigned int gfp_mask)
+{
+ int pages;
+
+ pages = kmem_zone_shrink(linvfs_inode_zone);
+ pages += kmem_zone_shrink(xfs_inode_zone);
+ return pages;
+}
+
+struct inode *
+xfs_get_inode(
+ bhv_desc_t *bdp,
+ xfs_ino_t ino,
+ int flags)
+{
+ struct vfs *vfsp = bhvtovfs(bdp);
+
+ return iget_locked(vfsp->vfs_super, ino);
+}
+
+struct dentry *
+d_alloc_anon(struct inode *inode)
+{
+ struct dentry *dentry;
+
+ spin_lock(&dcache_lock);
+ list_for_each_entry(dentry, &inode->i_dentry, d_alias) {
+ if (!(dentry->d_flags & DCACHE_NFSD_DISCONNECTED))
+ goto found;
+ }
+ spin_unlock(&dcache_lock);
+
+ dentry = d_alloc_root(inode);
+ if (likely(dentry != NULL))
+ dentry->d_flags |= DCACHE_NFSD_DISCONNECTED;
+ return dentry;
+ found:
+ dget_locked(dentry);
+ dentry->d_vfs_flags |= DCACHE_REFERENCED;
+ spin_unlock(&dcache_lock);
+ iput(inode);
+ return dentry;
+}
+
+/*ARGSUSED*/
+int
+xfs_blkdev_get(
+ xfs_mount_t *mp,
+ const char *name,
+ struct block_device **bdevp)
+{
+ struct nameidata nd;
+ int error;
+
+ error = path_lookup(name, LOOKUP_POSITIVE|LOOKUP_FOLLOW, &nd);
+ if (error) {
+ printk("XFS: Invalid device [%s], error=%d\n", name, error);
+ return -error;
+ }
+
+ /* I think we actually want bd_acquire here.. --hch */
+ *bdevp = bdget(kdev_t_to_nr(nd.dentry->d_inode->i_rdev));
+ if (*bdevp)
+ error = blkdev_get(*bdevp, FMODE_READ|FMODE_WRITE, 0, BDEV_FS);
+ else
+ error = -ENOMEM;
+
+ path_release(&nd);
+ return -error;
+}
+
+void
+xfs_blkdev_put(
+ struct block_device *bdev)
+{
+ if (bdev)
+ blkdev_put(bdev, BDEV_FS);
+}
+
+STATIC struct inode *
+linvfs_alloc_inode(
+ struct super_block *sb)
+{
+ vnode_t *vp;
+
+ vp = (vnode_t *)kmem_cache_alloc(linvfs_inode_zone,
+ kmem_flags_convert(KM_SLEEP));
+ if (!vp)
+ return NULL;
+ return LINVFS_GET_IP(vp);
+}
+
+STATIC void
+linvfs_destroy_inode(
+ struct inode *inode)
+{
+ kmem_cache_free(linvfs_inode_zone, LINVFS_GET_VP(inode));
+}
+
+#define VNODE_SIZE \
+ (sizeof(vnode_t) - sizeof(struct inode) + offsetof(struct inode, u))
+
+STATIC void
+init_once(
+ void *data,
+ kmem_cache_t *cachep,
+ unsigned long flags)
+{
+ vnode_t *vp = (vnode_t *)data;
+
+ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+ SLAB_CTOR_CONSTRUCTOR) {
+ struct inode *inode = LINVFS_GET_IP(vp);
+ memset(vp, 0, VNODE_SIZE);
+ __inode_init_once(inode);
+ }
+}
+
+STATIC int
+init_inodecache( void )
+{
+ linvfs_inode_zone = kmem_cache_create("linvfs_icache",
+ VNODE_SIZE, 0, SLAB_HWCACHE_ALIGN,
+ init_once, NULL);
+ if (linvfs_inode_zone == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+STATIC void
+destroy_inodecache( void )
+{
+ if (kmem_cache_destroy(linvfs_inode_zone))
+ printk(KERN_WARNING "%s: cache still in use!\n", __FUNCTION__);
+}
+
+/*
+ * Attempt to flush the inode, this will actually fail
+ * if the inode is pinned, but we dirty the inode again
+ * at the point when it is unpinned after a log write,
+ * since this is when the inode itself becomes flushable.
+ */
+STATIC void
+linvfs_write_inode(
+ struct inode *inode,
+ int sync)
+{
+ vnode_t *vp = LINVFS_GET_VP(inode);
+ int error, flags = FLUSH_INODE;
+
+ if (vp) {
+ vn_trace_entry(vp, __FUNCTION__, (inst_t *)__return_address);
+ if (sync)
+ flags |= FLUSH_SYNC;
+ VOP_IFLUSH(vp, flags, error);
+ }
+}
+
+STATIC void
+linvfs_clear_inode(
+ struct inode *inode)
+{
+ vnode_t *vp = LINVFS_GET_VP(inode);
+
+ if (vp) {
+ vn_rele(vp);
+ vn_trace_entry(vp, __FUNCTION__, (inst_t *)__return_address);
+ /*
+ * Do all our cleanup, and remove this vnode.
+ */
+ vn_remove(vp);
+ }
+}
+
+
+/*
+ * Enqueue a work item to be picked up by the vfs xfssyncd thread.
+ * Doing this has two advantages:
+ * - It saves on stack space, which is tight in certain situations
+ * - It can be used (with care) as a mechanism to avoid deadlocks.
+ * Flushing while allocating in a full filesystem requires both.
+ */
+STATIC void
+xfs_syncd_queue_work(
+ struct vfs *vfs,
+ void *data,
+ void (*syncer)(vfs_t *, void *))
+{
+ vfs_sync_work_t *work;
+
+ work = kmem_alloc(sizeof(struct vfs_sync_work), KM_SLEEP);
+ INIT_LIST_HEAD(&work->w_list);
+ work->w_syncer = syncer;
+ work->w_data = data;
+ work->w_vfs = vfs;
+ spin_lock(&vfs->vfs_sync_lock);
+ list_add_tail(&work->w_list, &vfs->vfs_sync_list);
+ spin_unlock(&vfs->vfs_sync_lock);
+ wake_up_process(vfs->vfs_sync_task);
+}
+
+/*
+ * Flush delayed allocate data, attempting to free up reserved space
+ * from existing allocations. At this point a new allocation attempt
+ * has failed with ENOSPC and we are in the process of scratching our
+ * heads, looking about for more room...
+ */
+STATIC void
+xfs_flush_inode_work(
+ vfs_t *vfs,
+ void *inode)
+{
+ filemap_fdatawrite(((struct inode *)inode)->i_mapping);
+ iput((struct inode *)inode);
+}
+
+void
+xfs_flush_inode(
+ xfs_inode_t *ip)
+{
+ struct inode *inode = LINVFS_GET_IP(XFS_ITOV(ip));
+ struct vfs *vfs = XFS_MTOVFS(ip->i_mount);
+
+ igrab(inode);
+ xfs_syncd_queue_work(vfs, inode, xfs_flush_inode_work);
+ delay(HZ/2);
+}
+
+/*
+ * This is the "bigger hammer" version of xfs_flush_inode_work...
+ * (IOW, "If at first you don't succeed, use a Bigger Hammer").
+ */
+STATIC void
+xfs_flush_device_work(
+ vfs_t *vfs,
+ void *inode)
+{
+ fsync_no_super(((struct inode *)inode)->i_dev);
+ iput((struct inode *)inode);
+}
+
+void
+xfs_flush_device(
+ xfs_inode_t *ip)
+{
+ struct inode *inode = LINVFS_GET_IP(XFS_ITOV(ip));
+ struct vfs *vfs = XFS_MTOVFS(ip->i_mount);
+
+ igrab(inode);
+ xfs_syncd_queue_work(vfs, inode, xfs_flush_device_work);
+ delay(HZ/2);
+ xfs_log_force(ip->i_mount, (xfs_lsn_t)0, XFS_LOG_FORCE|XFS_LOG_SYNC);
+}
+
+#define SYNCD_FLAGS (SYNC_FSDATA|SYNC_BDFLUSH|SYNC_ATTR|SYNC_REFCACHE)
+STATIC void
+vfs_sync_worker(
+ vfs_t *vfsp,
+ void *unused)
+{
+ int error;
+
+ if (!(vfsp->vfs_flag & VFS_RDONLY))
+ VFS_SYNC(vfsp, SYNCD_FLAGS, NULL, error);
+}
+
+STATIC int
+xfssyncd(
+ void *arg)
+{
+ long timeleft;
+ vfs_t *vfsp = (vfs_t *) arg;
+ struct list_head tmp;
+ struct vfs_sync_work *work, *n;
+
+ daemonize();
+ reparent_to_init();
+ sigmask_lock();
+ sigfillset(&current->blocked);
+ __recalc_sigpending(current);
+ sigmask_unlock();
+
+ sprintf(current->comm, "xfssyncd");
+
+ vfsp->vfs_sync_work.w_vfs = vfsp;
+ vfsp->vfs_sync_work.w_syncer = vfs_sync_worker;
+ vfsp->vfs_sync_task = current;
+ wmb();
+ wake_up(&vfsp->vfs_wait_sync_task);
+
+ INIT_LIST_HEAD(&tmp);
+ timeleft = (xfs_syncd_centisecs * HZ) / 100;
+ for (;;) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ timeleft = schedule_timeout(timeleft);
+ if (vfsp->vfs_flag & VFS_UMOUNT)
+ break;
+
+ spin_lock(&vfsp->vfs_sync_lock);
+ if (!timeleft) {
+ timeleft = (xfs_syncd_centisecs * HZ) / 100;
+ INIT_LIST_HEAD(&vfsp->vfs_sync_work.w_list);
+ list_add_tail(&vfsp->vfs_sync_work.w_list,
+ &vfsp->vfs_sync_list);
+ }
+ list_for_each_entry_safe(work, n, &vfsp->vfs_sync_list, w_list)
+ list_move(&work->w_list, &tmp);
+ spin_unlock(&vfsp->vfs_sync_lock);
+
+ list_for_each_entry_safe(work, n, &tmp, w_list) {
+ (*work->w_syncer)(vfsp, work->w_data);
+ list_del(&work->w_list);
+ if (work == &vfsp->vfs_sync_work)
+ continue;
+ kmem_free(work, sizeof(struct vfs_sync_work));
+ }
+ }
+
+ vfsp->vfs_sync_task = NULL;
+ wmb();
+ wake_up(&vfsp->vfs_wait_sync_task);
+
+ return 0;
+}
+
+STATIC int
+linvfs_start_syncd(
+ vfs_t *vfsp)
+{
+ int pid;
+
+ pid = kernel_thread(xfssyncd, (void *) vfsp,
+ CLONE_VM | CLONE_FS | CLONE_FILES);
+ if (pid < 0)
+ return pid;
+ wait_event(vfsp->vfs_wait_sync_task, vfsp->vfs_sync_task);
+ return 0;
+}
+
+STATIC void
+linvfs_stop_syncd(
+ vfs_t *vfsp)
+{
+ vfsp->vfs_flag |= VFS_UMOUNT;
+ wmb();
+
+ wake_up_process(vfsp->vfs_sync_task);
+ wait_event(vfsp->vfs_wait_sync_task, !vfsp->vfs_sync_task);
+}
+
+STATIC void
+linvfs_put_super(
+ struct super_block *sb)
+{
+ vfs_t *vfsp = LINVFS_GET_VFS(sb);
+ int error;
+
+ linvfs_stop_syncd(vfsp);
+ VFS_SYNC(vfsp, SYNC_ATTR|SYNC_DELWRI, NULL, error);
+ if (!error)
+ VFS_UNMOUNT(vfsp, 0, NULL, error);
+ if (error) {
+ printk("XFS unmount got error %d\n", error);
+ printk("%s: vfsp/0x%p left dangling!\n", __FUNCTION__, vfsp);
+ return;
+ }
+
+ vfs_deallocate(vfsp);
+}
+
+STATIC void
+linvfs_write_super(
+ struct super_block *sb)
+{
+ vfs_t *vfsp = LINVFS_GET_VFS(sb);
+ int error;
+
+ if (sb->s_flags & MS_RDONLY) {
+ sb->s_dirt = 0; /* paranoia */
+ return;
+ }
+ /* Push the log and superblock a little */
+ VFS_SYNC(vfsp, SYNC_FSDATA, NULL, error);
+ sb->s_dirt = 0;
+}
+
+STATIC int
+linvfs_sync_super(
+ struct super_block *sb)
+{
+ vfs_t *vfsp = LINVFS_GET_VFS(sb);
+ int error;
+
+ VFS_SYNC(vfsp, SYNC_FSDATA|SYNC_WAIT, NULL, error);
+ sb->s_dirt = 0;
+ return -error;
+}
+
+STATIC int
+linvfs_statfs(
+ struct super_block *sb,
+ struct statfs *statp)
+{
+ vfs_t *vfsp = LINVFS_GET_VFS(sb);
+ int error;
+
+ VFS_STATVFS(vfsp, statp, NULL, error);
+ return -error;
+}
+
+STATIC int
+linvfs_remount(
+ struct super_block *sb,
+ int *flags,
+ char *options)
+{
+ vfs_t *vfsp = LINVFS_GET_VFS(sb);
+ struct xfs_mount_args *args = xfs_args_allocate(sb);
+ int error;
+
+ VFS_PARSEARGS(vfsp, options, args, 1, error);
+ if (!error)
+ VFS_MNTUPDATE(vfsp, flags, args, error);
+ kmem_free(args, sizeof(*args));
+ return -error;
+}
+
+struct super_block *freeze_bdev(struct block_device *bdev)
+{
+ struct super_block *sb;
+ struct vfs *vfsp;
+ int error;
+
+ sb = get_super(to_kdev_t(bdev->bd_dev));
+ if (sb && !(sb->s_flags & MS_RDONLY)) {
+ vfsp = LINVFS_GET_VFS(sb);
+
+ /* Stop new writers */
+ vfsp->vfs_frozen = SB_FREEZE_WRITE;
+ wmb();
+
+ /* Flush the refcache */
+ VFS_SYNC(vfsp, SYNC_REFCACHE|SYNC_WAIT, NULL, error);
+
+ /* Flush delalloc and delwri data */
+ VFS_SYNC(vfsp, SYNC_DELWRI|SYNC_WAIT, NULL, error);
+
+ /* Pause transaction subsystem */
+ vfsp->vfs_frozen = SB_FREEZE_TRANS;
+ wmb();
+
+ /* Flush any remaining inodes into buffers */
+ VFS_SYNC(vfsp, SYNC_ATTR|SYNC_WAIT, NULL, error);
+
+ /* Push all buffers out to disk */
+ sync_buffers(sb->s_dev, 1);
+
+ /* Push the superblock and write an unmount record */
+ VFS_FREEZE(vfsp);
+ }
+
+ sync_buffers(to_kdev_t(bdev->bd_dev), 1);
+ return sb; /* thaw_bdev releases sb->s_umount */
+}
+
+void thaw_bdev(struct block_device *bdev, struct super_block *sb)
+{
+ if (sb) {
+ struct vfs *vfsp = LINVFS_GET_VFS(sb);
+
+ BUG_ON(sb->s_bdev != bdev);
+
+ vfsp->vfs_frozen = SB_UNFROZEN;
+ wmb();
+ wake_up(&vfsp->vfs_wait_unfrozen);
+
+ drop_super(sb);
+ }
+}
+
+STATIC void
+linvfs_freeze_fs(
+ struct super_block *sb)
+{
+ if (sb->s_flags & MS_RDONLY)
+ return;
+ freeze_bdev(sb->s_bdev);
+}
+
+STATIC void
+linvfs_unfreeze_fs(
+ struct super_block *sb)
+{
+ thaw_bdev(sb->s_bdev, sb);
+}
+
+STATIC int
+linvfs_dentry_to_fh(
+ struct dentry *dentry,
+ __u32 *data,
+ int *lenp,
+ int need_parent)
+{
+ struct inode *inode = dentry->d_inode ;
+ vnode_t *vp = LINVFS_GET_VP(inode);
+ int maxlen = *lenp;
+ xfs_fid2_t fid;
+ int error;
+
+ if (maxlen < 3)
+ return 255 ;
+
+ VOP_FID2(vp, (struct fid *)&fid, error);
+ data[0] = (__u32)fid.fid_ino; /* 32 bits of inode is OK */
+ data[1] = fid.fid_gen;
+
+ *lenp = 2 ;
+ if (maxlen < 4 || ! need_parent)
+ return 2 ;
+
+ inode = dentry->d_parent->d_inode ;
+ vp = LINVFS_GET_VP(inode);
+
+ VOP_FID2(vp, (struct fid *)&fid, error);
+ data[2] = (__u32)fid.fid_ino; /* 32 bits of inode is OK */
+ *lenp = 3 ;
+ if (maxlen < 4)
+ return 3 ;
+ data[3] = fid.fid_gen;
+ *lenp = 4 ;
+ return 4 ;
+}
+
+STATIC struct dentry *
+linvfs_fh_to_dentry(
+ struct super_block *sb,
+ __u32 *data,
+ int len,
+ int fhtype,
+ int parent)
+{
+ vnode_t *vp;
+ struct inode *inode = NULL;
+ struct dentry *result;
+ xfs_fid2_t xfid;
+ vfs_t *vfsp = LINVFS_GET_VFS(sb);
+ int error;
+
+ xfid.fid_len = sizeof(xfs_fid2_t) - sizeof(xfid.fid_len);
+ xfid.fid_pad = 0;
+
+ if (!parent) {
+ xfid.fid_gen = data[1];
+ xfid.fid_ino = (__u64)data[0];
+ } else {
+ if (fhtype != 4) {
+ printk(KERN_WARNING
+ "XFS: detected filehandle without "
+ "parent inode generation information.");
+ return ERR_PTR(-ESTALE);
+ }
+
+ xfid.fid_gen = data[3];
+ xfid.fid_ino = (__u64)data[2];
+ }
+
+ VFS_VGET(vfsp, &vp, (fid_t *)&xfid, error);
+ if (error || vp == NULL)
+ return ERR_PTR(-ESTALE) ;
+
+ inode = LINVFS_GET_IP(vp);
+
+ result = d_alloc_anon(inode);
+ if (unlikely(result == NULL)) {
+ iput(inode);
+ return ERR_PTR(-ENOMEM);
+ }
+ return result;
+}
+
+STATIC int
+linvfs_show_options(
+ struct seq_file *m,
+ struct vfsmount *mnt)
+{
+ struct vfs *vfsp = LINVFS_GET_VFS(mnt->mnt_sb);
+ int error;
+
+ VFS_SHOWARGS(vfsp, m, error);
+ return error;
+}
+
+STATIC int
+linvfs_getxstate(
+ struct super_block *sb,
+ struct fs_quota_stat *fqs)
+{
+ struct vfs *vfsp = LINVFS_GET_VFS(sb);
+ int error;
+
+ VFS_QUOTACTL(vfsp, Q_XGETQSTAT, 0, (caddr_t)fqs, error);
+ return -error;
+}
+
+STATIC int
+linvfs_setxstate(
+ struct super_block *sb,
+ unsigned int flags,
+ int op)
+{
+ struct vfs *vfsp = LINVFS_GET_VFS(sb);
+ int error;
+
+ VFS_QUOTACTL(vfsp, op, 0, (caddr_t)&flags, error);
+ return -error;
+}
+
+STATIC int
+linvfs_getxquota(
+ struct super_block *sb,
+ int type,
+ qid_t id,
+ struct fs_disk_quota *fdq)
+{
+ struct vfs *vfsp = LINVFS_GET_VFS(sb);
+ int error, getmode;
+
+ getmode = (type == GRPQUOTA) ? Q_XGETGQUOTA : Q_XGETQUOTA;
+ VFS_QUOTACTL(vfsp, getmode, id, (caddr_t)fdq, error);
+ return -error;
+}
+
+STATIC int
+linvfs_setxquota(
+ struct super_block *sb,
+ int type,
+ qid_t id,
+ struct fs_disk_quota *fdq)
+{
+ struct vfs *vfsp = LINVFS_GET_VFS(sb);
+ int error, setmode;
+
+ setmode = (type == GRPQUOTA) ? Q_XSETGQLIM : Q_XSETQLIM;
+ VFS_QUOTACTL(vfsp, setmode, id, (caddr_t)fdq, error);
+ return -error;
+}
+
+STATIC struct super_block *
+linvfs_read_super(
+ struct super_block *sb,
+ void *data,
+ int silent)
+{
+ vnode_t *rootvp;
+ struct vfs *vfsp = vfs_allocate();
+ struct xfs_mount_args *args = xfs_args_allocate(sb);
+ struct statfs statvfs;
+ int error;
+
+ vfsp->vfs_super = sb;
+ LINVFS_SET_VFS(sb, vfsp);
+ if (sb->s_flags & MS_RDONLY)
+ vfsp->vfs_flag |= VFS_RDONLY;
+ bhv_insert_all_vfsops(vfsp);
+
+ VFS_PARSEARGS(vfsp, (char *)data, args, 0, error);
+ if (error) {
+ bhv_remove_all_vfsops(vfsp, 1);
+ goto fail_vfsop;
+ }
+
+ sb_min_blocksize(sb, BBSIZE);
+ sb->s_qcop = &linvfs_qops;
+ sb->s_op = &linvfs_sops;
+
+ VFS_MOUNT(vfsp, args, NULL, error);
+ if (error) {
+ bhv_remove_all_vfsops(vfsp, 1);
+ goto fail_vfsop;
+ }
+
+ VFS_STATVFS(vfsp, &statvfs, NULL, error);
+ if (error)
+ goto fail_unmount;
+
+ sb->s_dirt = 1;
+ sb->s_magic = statvfs.f_type;
+ sb->s_blocksize = statvfs.f_bsize;
+ sb->s_blocksize_bits = ffs(statvfs.f_bsize) - 1;
+ sb->s_maxbytes = xfs_max_file_offset(sb->s_blocksize_bits);
+ set_posix_acl_flag(sb);
+
+ VFS_ROOT(vfsp, &rootvp, error);
+ if (error)
+ goto fail_unmount;
+
+ sb->s_root = d_alloc_root(LINVFS_GET_IP(rootvp));
+ if (!sb->s_root)
+ goto fail_vnrele;
+ if (is_bad_inode(sb->s_root->d_inode))
+ goto fail_vnrele;
+ if (linvfs_start_syncd(vfsp))
+ goto fail_vnrele;
+ vn_trace_exit(rootvp, __FUNCTION__, (inst_t *)__return_address);
+
+ kmem_free(args, sizeof(*args));
+ return sb;
+
+fail_vnrele:
+ if (sb->s_root) {
+ dput(sb->s_root);
+ sb->s_root = NULL;
+ } else {
+ VN_RELE(rootvp);
+ }
+
+fail_unmount:
+ VFS_UNMOUNT(vfsp, 0, NULL, error);
+
+fail_vfsop:
+ vfs_deallocate(vfsp);
+ kmem_free(args, sizeof(*args));
+ return NULL;
+}
+
+
+STATIC struct super_operations linvfs_sops = {
+ .alloc_inode = linvfs_alloc_inode,
+ .destroy_inode = linvfs_destroy_inode,
+ .write_inode = linvfs_write_inode,
+ .clear_inode = linvfs_clear_inode,
+ .put_super = linvfs_put_super,
+ .write_super = linvfs_write_super,
+ .sync_fs = linvfs_sync_super,
+ .write_super_lockfs = linvfs_freeze_fs,
+ .unlockfs = linvfs_unfreeze_fs,
+ .statfs = linvfs_statfs,
+ .remount_fs = linvfs_remount,
+ .fh_to_dentry = linvfs_fh_to_dentry,
+ .dentry_to_fh = linvfs_dentry_to_fh,
+ .show_options = linvfs_show_options,
+};
+
+STATIC struct quotactl_ops linvfs_qops = {
+ .get_xstate = linvfs_getxstate,
+ .set_xstate = linvfs_setxstate,
+ .get_xquota = linvfs_getxquota,
+ .set_xquota = linvfs_setxquota,
+};
+
+STATIC struct file_system_type xfs_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "xfs",
+ .read_super = linvfs_read_super,
+ .fs_flags = FS_REQUIRES_DEV,
+};
+
+
+STATIC int __init
+init_xfs_fs( void )
+{
+ int error;
+ struct sysinfo si;
+ static char message[] __initdata = KERN_INFO \
+ XFS_VERSION_STRING " with " XFS_BUILD_OPTIONS " enabled\n";
+
+ printk(message);
+
+ si_meminfo(&si);
+ xfs_physmem = si.totalram;
+
+ ktrace_init(64);
+
+ error = init_inodecache();
+ if (error < 0)
+ goto undo_inodecache;
+
+ error = pagebuf_init();
+ if (error < 0)
+ goto undo_pagebuf;
+
+ vn_init();
+ xfs_init();
+ uuid_init();
+ vfs_initdmapi();
+ vfs_initquota();
+
+ xfs_inode_shaker = kmem_shake_register(xfs_inode_shake);
+ if (!xfs_inode_shaker) {
+ error = -ENOMEM;
+ goto undo_shaker;
+ }
+
+ error = register_filesystem(&xfs_fs_type);
+ if (error)
+ goto undo_register;
+ XFS_DM_INIT(&xfs_fs_type);
+ return 0;
+
+undo_register:
+ kmem_shake_deregister(xfs_inode_shaker);
+
+undo_shaker:
+ pagebuf_terminate();
+
+undo_pagebuf:
+ destroy_inodecache();
+
+undo_inodecache:
+ return error;
+}
+
+STATIC void __exit
+exit_xfs_fs( void )
+{
+ XFS_DM_EXIT(&xfs_fs_type);
+ unregister_filesystem(&xfs_fs_type);
+ kmem_shake_deregister(xfs_inode_shaker);
+ xfs_cleanup();
+ vfs_exitquota();
+ vfs_exitdmapi();
+ pagebuf_terminate();
+ destroy_inodecache();
+ ktrace_uninit();
+}
+
+module_init(init_xfs_fs);
+module_exit(exit_xfs_fs);
+
+MODULE_AUTHOR("Silicon Graphics, Inc.");
+MODULE_DESCRIPTION(XFS_VERSION_STRING " with " XFS_BUILD_OPTIONS " enabled");
+MODULE_LICENSE("GPL");
diff --git a/fs/xfs/linux-2.4/xfs_super.h b/fs/xfs/linux-2.4/xfs_super.h
new file mode 100644
index 00000000000000..199f77640f55f7
--- /dev/null
+++ b/fs/xfs/linux-2.4/xfs_super.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_SUPER_H__
+#define __XFS_SUPER_H__
+
+#ifdef CONFIG_XFS_DMAPI
+# define vfs_insertdmapi(vfs) vfs_insertops(vfsp, &xfs_dmops)
+# define vfs_initdmapi() dmapi_init()
+# define vfs_exitdmapi() dmapi_uninit()
+#else
+# define vfs_insertdmapi(vfs) do { } while (0)
+# define vfs_initdmapi() do { } while (0)
+# define vfs_exitdmapi() do { } while (0)
+#endif
+
+#ifdef CONFIG_XFS_QUOTA
+# define vfs_insertquota(vfs) vfs_insertops(vfsp, &xfs_qmops)
+extern void xfs_qm_init(void);
+extern void xfs_qm_exit(void);
+# define vfs_initquota() xfs_qm_init()
+# define vfs_exitquota() xfs_qm_exit()
+#else
+# define vfs_insertquota(vfs) do { } while (0)
+# define vfs_initquota() do { } while (0)
+# define vfs_exitquota() do { } while (0)
+#endif
+
+#ifdef CONFIG_XFS_POSIX_ACL
+# define XFS_ACL_STRING "ACLs, "
+# define set_posix_acl_flag(sb) ((sb)->s_flags |= MS_POSIXACL)
+#else
+# define XFS_ACL_STRING
+# define set_posix_acl_flag(sb) do { } while (0)
+#endif
+
+#ifdef CONFIG_XFS_SECURITY
+# define XFS_SECURITY_STRING "security attributes, "
+# define ENOSECURITY 0
+#else
+# define XFS_SECURITY_STRING
+# define ENOSECURITY EOPNOTSUPP
+#endif
+
+#ifdef CONFIG_XFS_RT
+# define XFS_REALTIME_STRING "realtime, "
+#else
+# define XFS_REALTIME_STRING
+#endif
+
+#if XFS_BIG_BLKNOS
+# if XFS_BIG_INUMS
+# define XFS_BIGFS_STRING "large block/inode numbers, "
+# else
+# define XFS_BIGFS_STRING "large block numbers, "
+# endif
+#else
+# define XFS_BIGFS_STRING
+#endif
+
+#ifdef CONFIG_XFS_TRACE
+# define XFS_TRACE_STRING "tracing, "
+#else
+# define XFS_TRACE_STRING
+#endif
+
+#ifdef CONFIG_XFS_DMAPI
+# define XFS_DMAPI_STRING "dmapi support, "
+#else
+# define XFS_DMAPI_STRING
+#endif
+
+#ifdef DEBUG
+# define XFS_DBG_STRING "debug"
+#else
+# define XFS_DBG_STRING "no debug"
+#endif
+
+#define XFS_BUILD_OPTIONS XFS_ACL_STRING \
+ XFS_SECURITY_STRING \
+ XFS_REALTIME_STRING \
+ XFS_BIGFS_STRING \
+ XFS_TRACE_STRING \
+ XFS_DMAPI_STRING \
+ XFS_DBG_STRING /* DBG must be last */
+
+#define LINVFS_GET_VFS(s) \
+ (vfs_t *)((s)->u.generic_sbp)
+#define LINVFS_SET_VFS(s, vfsp) \
+ ((s)->u.generic_sbp = vfsp)
+
+struct xfs_inode;
+struct xfs_mount;
+struct xfs_buftarg;
+struct block_device;
+
+extern __uint64_t xfs_max_file_offset(unsigned int);
+
+extern struct inode *xfs_get_inode(bhv_desc_t *, xfs_ino_t, int);
+extern void xfs_initialize_vnode(bhv_desc_t *, vnode_t *, bhv_desc_t *, int);
+
+extern void xfs_flush_inode(struct xfs_inode *);
+extern void xfs_flush_device(struct xfs_inode *);
+
+extern int xfs_blkdev_get(struct xfs_mount *, const char *,
+ struct block_device **);
+extern void xfs_blkdev_put(struct block_device *);
+
+/* matching a 2.6 kernel export, thus no xfs_ prefix */
+extern struct dentry *d_alloc_anon(struct inode *inode);
+extern struct super_block *freeze_bdev(struct block_device *bdev);
+extern void thaw_bdev(struct block_device *bdev, struct super_block *sb);
+
+#endif /* __XFS_SUPER_H__ */
diff --git a/fs/xfs/linux-2.4/xfs_sysctl.c b/fs/xfs/linux-2.4/xfs_sysctl.c
new file mode 100644
index 00000000000000..22048376e0ff45
--- /dev/null
+++ b/fs/xfs/linux-2.4/xfs_sysctl.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2001-2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+#include "xfs_refcache.h"
+#include <linux/sysctl.h>
+#include <linux/proc_fs.h>
+
+
+static struct ctl_table_header *xfs_table_header;
+
+
+/* Custom proc handlers */
+
+STATIC int
+xfs_refcache_resize_proc_handler(
+ ctl_table *ctl,
+ int write,
+ struct file *filp,
+ void *buffer,
+ size_t *lenp)
+{
+ int ret, *valp = ctl->data;
+ int xfs_refcache_new_size;
+ int xfs_refcache_old_size = *valp;
+
+ ret = proc_dointvec_minmax(ctl, write, filp, buffer, lenp);
+ xfs_refcache_new_size = *valp;
+
+ if (!ret && write && xfs_refcache_new_size != xfs_refcache_old_size) {
+ xfs_refcache_resize(xfs_refcache_new_size);
+ /* Don't purge more than size of the cache */
+ if (xfs_refcache_new_size < xfs_refcache_purge_count)
+ xfs_refcache_purge_count = xfs_refcache_new_size;
+ }
+
+ return ret;
+}
+
+#ifdef CONFIG_PROC_FS
+STATIC int
+xfs_stats_clear_proc_handler(
+ ctl_table *ctl,
+ int write,
+ struct file *filp,
+ void *buffer,
+ size_t *lenp)
+{
+ int ret, *valp = ctl->data;
+ __uint32_t vn_active;
+
+ ret = proc_dointvec_minmax(ctl, write, filp, buffer, lenp);
+
+ if (!ret && write && *valp) {
+ printk("XFS Clearing xfsstats\n");
+ /* save vn_active, it's a universal truth! */
+ vn_active = xfsstats.vn_active;
+ memset(&xfsstats, 0, sizeof(xfsstats));
+ xfsstats.vn_active = vn_active;
+ xfs_stats_clear = 0;
+ }
+
+ return ret;
+}
+#endif /* CONFIG_PROC_FS */
+
+STATIC ctl_table xfs_table[] = {
+ {XFS_REFCACHE_SIZE, "refcache_size", &xfs_params.refcache_size.val,
+ sizeof(int), 0644, NULL, &xfs_refcache_resize_proc_handler,
+ &sysctl_intvec, NULL,
+ &xfs_params.refcache_size.min, &xfs_params.refcache_size.max},
+
+ /* Note, the max here is different, it is the current refcache size */
+ {XFS_REFCACHE_PURGE, "refcache_purge", &xfs_params.refcache_purge.val,
+ sizeof(int), 0644, NULL, &proc_dointvec_minmax,
+ &sysctl_intvec, NULL,
+ &xfs_params.refcache_purge.min, &xfs_params.refcache_size.val},
+
+ {XFS_RESTRICT_CHOWN, "restrict_chown", &xfs_params.restrict_chown.val,
+ sizeof(int), 0644, NULL, &proc_dointvec_minmax,
+ &sysctl_intvec, NULL,
+ &xfs_params.restrict_chown.min, &xfs_params.restrict_chown.max},
+
+ {XFS_SGID_INHERIT, "irix_sgid_inherit", &xfs_params.sgid_inherit.val,
+ sizeof(int), 0644, NULL, &proc_dointvec_minmax,
+ &sysctl_intvec, NULL,
+ &xfs_params.sgid_inherit.min, &xfs_params.sgid_inherit.max},
+
+ {XFS_SYMLINK_MODE, "irix_symlink_mode", &xfs_params.symlink_mode.val,
+ sizeof(int), 0644, NULL, &proc_dointvec_minmax,
+ &sysctl_intvec, NULL,
+ &xfs_params.symlink_mode.min, &xfs_params.symlink_mode.max},
+
+ {XFS_PANIC_MASK, "panic_mask", &xfs_params.panic_mask.val,
+ sizeof(int), 0644, NULL, &proc_dointvec_minmax,
+ &sysctl_intvec, NULL,
+ &xfs_params.panic_mask.min, &xfs_params.panic_mask.max},
+
+ {XFS_ERRLEVEL, "error_level", &xfs_params.error_level.val,
+ sizeof(int), 0644, NULL, &proc_dointvec_minmax,
+ &sysctl_intvec, NULL,
+ &xfs_params.error_level.min, &xfs_params.error_level.max},
+
+ {XFS_SYNCD_TIMER, "xfssyncd_centisecs", &xfs_params.syncd_timer.val,
+ sizeof(int), 0644, NULL, &proc_dointvec_minmax,
+ &sysctl_intvec, NULL,
+ &xfs_params.syncd_timer.min, &xfs_params.syncd_timer.max},
+
+ {XFS_INHERIT_SYNC, "inherit_sync", &xfs_params.inherit_sync.val,
+ sizeof(int), 0644, NULL, &proc_dointvec_minmax,
+ &sysctl_intvec, NULL,
+ &xfs_params.inherit_sync.min, &xfs_params.inherit_sync.max},
+
+ {XFS_INHERIT_NODUMP, "inherit_nodump", &xfs_params.inherit_nodump.val,
+ sizeof(int), 0644, NULL, &proc_dointvec_minmax,
+ &sysctl_intvec, NULL,
+ &xfs_params.inherit_nodump.min, &xfs_params.inherit_nodump.max},
+
+ {XFS_INHERIT_NOATIME, "inherit_noatime", &xfs_params.inherit_noatim.val,
+ sizeof(int), 0644, NULL, &proc_dointvec_minmax,
+ &sysctl_intvec, NULL,
+ &xfs_params.inherit_noatim.min, &xfs_params.inherit_noatim.max},
+
+ {XFS_BUF_TIMER, "xfsbufd_centisecs", &xfs_params.xfs_buf_timer.val,
+ sizeof(int), 0644, NULL, &proc_dointvec_minmax,
+ &sysctl_intvec, NULL,
+ &xfs_params.xfs_buf_timer.min, &xfs_params.xfs_buf_timer.max},
+
+ {XFS_BUF_AGE, "age_buffer_centisecs", &xfs_params.xfs_buf_age.val,
+ sizeof(int), 0644, NULL, &proc_dointvec_minmax,
+ &sysctl_intvec, NULL,
+ &xfs_params.xfs_buf_age.min, &xfs_params.xfs_buf_age.max},
+
+ {XFS_INHERIT_NOSYM, "inherit_nosymlinks", &xfs_params.inherit_nosym.val,
+ sizeof(int), 0644, NULL, &proc_dointvec_minmax,
+ &sysctl_intvec, NULL,
+ &xfs_params.inherit_nosym.min, &xfs_params.inherit_nosym.max},
+
+ {XFS_ROTORSTEP, "rotorstep", &xfs_params.rotorstep.val,
+ sizeof(int), 0644, NULL, &proc_dointvec_minmax,
+ &sysctl_intvec, NULL,
+ &xfs_params.rotorstep.min, &xfs_params.rotorstep.max},
+
+ /* please keep this the last entry */
+#ifdef CONFIG_PROC_FS
+ {XFS_STATS_CLEAR, "stats_clear", &xfs_params.stats_clear.val,
+ sizeof(int), 0644, NULL, &xfs_stats_clear_proc_handler,
+ &sysctl_intvec, NULL,
+ &xfs_params.stats_clear.min, &xfs_params.stats_clear.max},
+#endif /* CONFIG_PROC_FS */
+
+ {0}
+};
+
+STATIC ctl_table xfs_dir_table[] = {
+ {FS_XFS, "xfs", NULL, 0, 0555, xfs_table},
+ {0}
+};
+
+STATIC ctl_table xfs_root_table[] = {
+ {CTL_FS, "fs", NULL, 0, 0555, xfs_dir_table},
+ {0}
+};
+
+void
+xfs_sysctl_register(void)
+{
+ xfs_table_header = register_sysctl_table(xfs_root_table, 1);
+}
+
+void
+xfs_sysctl_unregister(void)
+{
+ if (xfs_table_header)
+ unregister_sysctl_table(xfs_table_header);
+}
diff --git a/fs/xfs/linux-2.4/xfs_sysctl.h b/fs/xfs/linux-2.4/xfs_sysctl.h
new file mode 100644
index 00000000000000..f1fa4d807c28c9
--- /dev/null
+++ b/fs/xfs/linux-2.4/xfs_sysctl.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2001-2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#ifndef __XFS_SYSCTL_H__
+#define __XFS_SYSCTL_H__
+
+#include <linux/sysctl.h>
+
+/*
+ * Tunable xfs parameters
+ */
+
+typedef struct xfs_sysctl_val {
+ int min;
+ int val;
+ int max;
+} xfs_sysctl_val_t;
+
+typedef struct xfs_param {
+ xfs_sysctl_val_t refcache_size; /* Size of NFS reference cache. */
+ xfs_sysctl_val_t refcache_purge;/* # of entries to purge each time. */
+ xfs_sysctl_val_t restrict_chown;/* Root/non-root can give away files.*/
+ xfs_sysctl_val_t sgid_inherit; /* Inherit S_ISGID if process' GID is
+ * not a member of parent dir GID. */
+ xfs_sysctl_val_t symlink_mode; /* Link creat mode affected by umask */
+ xfs_sysctl_val_t panic_mask; /* bitmask to cause panic on errors. */
+ xfs_sysctl_val_t error_level; /* Degree of reporting for problems */
+ xfs_sysctl_val_t syncd_timer; /* Interval between xfssyncd wakeups */
+ xfs_sysctl_val_t stats_clear; /* Reset all XFS statistics to zero. */
+ xfs_sysctl_val_t inherit_sync; /* Inherit the "sync" inode flag. */
+ xfs_sysctl_val_t inherit_nodump;/* Inherit the "nodump" inode flag. */
+ xfs_sysctl_val_t inherit_noatim;/* Inherit the "noatime" inode flag. */
+ xfs_sysctl_val_t xfs_buf_timer; /* Interval between xfsbufd wakeups. */
+ xfs_sysctl_val_t xfs_buf_age; /* Metadata buffer age before flush. */
+ xfs_sysctl_val_t inherit_nosym; /* Inherit the "nosymlinks" flag. */
+ xfs_sysctl_val_t rotorstep; /* inode32 AG rotoring control knob */
+} xfs_param_t;
+
+/*
+ * xfs_error_level:
+ *
+ * How much error reporting will be done when internal problems are
+ * encountered. These problems normally return an EFSCORRUPTED to their
+ * caller, with no other information reported.
+ *
+ * 0 No error reports
+ * 1 Report EFSCORRUPTED errors that will cause a filesystem shutdown
+ * 5 Report all EFSCORRUPTED errors (all of the above errors, plus any
+ * additional errors that are known to not cause shutdowns)
+ *
+ * xfs_panic_mask bit 0x8 turns the error reports into panics
+ */
+
+enum {
+ XFS_REFCACHE_SIZE = 1,
+ XFS_REFCACHE_PURGE = 2,
+ XFS_RESTRICT_CHOWN = 3,
+ XFS_SGID_INHERIT = 4,
+ XFS_SYMLINK_MODE = 5,
+ XFS_PANIC_MASK = 6,
+ XFS_ERRLEVEL = 7,
+ XFS_SYNCD_TIMER = 8,
+ /* XFS_PROBE_DMAPI = 9 */
+ /* XFS_PROBE_IOOPS = 10 */
+ /* XFS_PROBE_QUOTA = 11 */
+ XFS_STATS_CLEAR = 12,
+ XFS_INHERIT_SYNC = 13,
+ XFS_INHERIT_NODUMP = 14,
+ XFS_INHERIT_NOATIME = 15,
+ XFS_BUF_TIMER = 16,
+ XFS_BUF_AGE = 17,
+ /* XFS_IO_BYPASS = 18, */
+ XFS_INHERIT_NOSYM = 19,
+ XFS_ROTORSTEP = 20,
+};
+
+extern xfs_param_t xfs_params;
+
+#ifdef CONFIG_SYSCTL
+extern void xfs_sysctl_register(void);
+extern void xfs_sysctl_unregister(void);
+#else
+# define xfs_sysctl_register() do { } while (0)
+# define xfs_sysctl_unregister() do { } while (0)
+#endif /* CONFIG_SYSCTL */
+
+#endif /* __XFS_SYSCTL_H__ */
diff --git a/fs/xfs/linux-2.4/xfs_version.h b/fs/xfs/linux-2.4/xfs_version.h
new file mode 100644
index 00000000000000..96f96394417e92
--- /dev/null
+++ b/fs/xfs/linux-2.4/xfs_version.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2001-2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+/*
+ * Dummy file that can contain a timestamp to put into the
+ * XFS init string, to help users keep track of what they're
+ * running
+ */
+
+#ifndef __XFS_VERSION_H__
+#define __XFS_VERSION_H__
+
+#define XFS_VERSION_STRING "SGI XFS"
+
+#endif /* __XFS_VERSION_H__ */
diff --git a/fs/xfs/linux-2.4/xfs_vfs.c b/fs/xfs/linux-2.4/xfs_vfs.c
new file mode 100644
index 00000000000000..17836d994dc809
--- /dev/null
+++ b/fs/xfs/linux-2.4/xfs_vfs.c
@@ -0,0 +1,343 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_macros.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_clnt.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_imap.h"
+#include "xfs_alloc.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_quota.h"
+
+int
+vfs_mount(
+ struct bhv_desc *bdp,
+ struct xfs_mount_args *args,
+ struct cred *cr)
+{
+ struct bhv_desc *next = bdp;
+
+ ASSERT(next);
+ while (! (bhvtovfsops(next))->vfs_mount)
+ next = BHV_NEXT(next);
+ return ((*bhvtovfsops(next)->vfs_mount)(next, args, cr));
+}
+
+int
+vfs_parseargs(
+ struct bhv_desc *bdp,
+ char *s,
+ struct xfs_mount_args *args,
+ int f)
+{
+ struct bhv_desc *next = bdp;
+
+ ASSERT(next);
+ while (! (bhvtovfsops(next))->vfs_parseargs)
+ next = BHV_NEXT(next);
+ return ((*bhvtovfsops(next)->vfs_parseargs)(next, s, args, f));
+}
+
+int
+vfs_showargs(
+ struct bhv_desc *bdp,
+ struct seq_file *m)
+{
+ struct bhv_desc *next = bdp;
+
+ ASSERT(next);
+ while (! (bhvtovfsops(next))->vfs_showargs)
+ next = BHV_NEXT(next);
+ return ((*bhvtovfsops(next)->vfs_showargs)(next, m));
+}
+
+int
+vfs_unmount(
+ struct bhv_desc *bdp,
+ int fl,
+ struct cred *cr)
+{
+ struct bhv_desc *next = bdp;
+
+ ASSERT(next);
+ while (! (bhvtovfsops(next))->vfs_unmount)
+ next = BHV_NEXT(next);
+ return ((*bhvtovfsops(next)->vfs_unmount)(next, fl, cr));
+}
+
+int
+vfs_mntupdate(
+ struct bhv_desc *bdp,
+ int *fl,
+ struct xfs_mount_args *args)
+{
+ struct bhv_desc *next = bdp;
+
+ ASSERT(next);
+ while (! (bhvtovfsops(next))->vfs_mntupdate)
+ next = BHV_NEXT(next);
+ return ((*bhvtovfsops(next)->vfs_mntupdate)(next, fl, args));
+}
+
+int
+vfs_root(
+ struct bhv_desc *bdp,
+ struct vnode **vpp)
+{
+ struct bhv_desc *next = bdp;
+
+ ASSERT(next);
+ while (! (bhvtovfsops(next))->vfs_root)
+ next = BHV_NEXT(next);
+ return ((*bhvtovfsops(next)->vfs_root)(next, vpp));
+}
+
+int
+vfs_statvfs(
+ struct bhv_desc *bdp,
+ xfs_statfs_t *sp,
+ struct vnode *vp)
+{
+ struct bhv_desc *next = bdp;
+
+ ASSERT(next);
+ while (! (bhvtovfsops(next))->vfs_statvfs)
+ next = BHV_NEXT(next);
+ return ((*bhvtovfsops(next)->vfs_statvfs)(next, sp, vp));
+}
+
+int
+vfs_sync(
+ struct bhv_desc *bdp,
+ int fl,
+ struct cred *cr)
+{
+ struct bhv_desc *next = bdp;
+
+ ASSERT(next);
+ while (! (bhvtovfsops(next))->vfs_sync)
+ next = BHV_NEXT(next);
+ return ((*bhvtovfsops(next)->vfs_sync)(next, fl, cr));
+}
+
+int
+vfs_vget(
+ struct bhv_desc *bdp,
+ struct vnode **vpp,
+ struct fid *fidp)
+{
+ struct bhv_desc *next = bdp;
+
+ ASSERT(next);
+ while (! (bhvtovfsops(next))->vfs_vget)
+ next = BHV_NEXT(next);
+ return ((*bhvtovfsops(next)->vfs_vget)(next, vpp, fidp));
+}
+
+int
+vfs_dmapiops(
+ struct bhv_desc *bdp,
+ caddr_t addr)
+{
+ struct bhv_desc *next = bdp;
+
+ ASSERT(next);
+ while (! (bhvtovfsops(next))->vfs_dmapiops)
+ next = BHV_NEXT(next);
+ return ((*bhvtovfsops(next)->vfs_dmapiops)(next, addr));
+}
+
+int
+vfs_quotactl(
+ struct bhv_desc *bdp,
+ int cmd,
+ int id,
+ caddr_t addr)
+{
+ struct bhv_desc *next = bdp;
+
+ ASSERT(next);
+ while (! (bhvtovfsops(next))->vfs_quotactl)
+ next = BHV_NEXT(next);
+ return ((*bhvtovfsops(next)->vfs_quotactl)(next, cmd, id, addr));
+}
+
+struct inode *
+vfs_get_inode(
+ struct bhv_desc *bdp,
+ xfs_ino_t ino,
+ int fl)
+{
+ struct bhv_desc *next = bdp;
+
+ while (! (bhvtovfsops(next))->vfs_get_inode)
+ next = BHV_NEXTNULL(next);
+ return ((*bhvtovfsops(next)->vfs_get_inode)(next, ino, fl));
+}
+
+void
+vfs_init_vnode(
+ struct bhv_desc *bdp,
+ struct vnode *vp,
+ struct bhv_desc *bp,
+ int unlock)
+{
+ struct bhv_desc *next = bdp;
+
+ ASSERT(next);
+ while (! (bhvtovfsops(next))->vfs_init_vnode)
+ next = BHV_NEXT(next);
+ ((*bhvtovfsops(next)->vfs_init_vnode)(next, vp, bp, unlock));
+}
+
+void
+vfs_force_shutdown(
+ struct bhv_desc *bdp,
+ int fl,
+ char *file,
+ int line)
+{
+ struct bhv_desc *next = bdp;
+
+ ASSERT(next);
+ while (! (bhvtovfsops(next))->vfs_force_shutdown)
+ next = BHV_NEXT(next);
+ ((*bhvtovfsops(next)->vfs_force_shutdown)(next, fl, file, line));
+}
+
+void
+vfs_freeze(
+ struct bhv_desc *bdp)
+{
+ struct bhv_desc *next = bdp;
+
+ ASSERT(next);
+ while (! (bhvtovfsops(next))->vfs_freeze)
+ next = BHV_NEXT(next);
+ ((*bhvtovfsops(next)->vfs_freeze)(next));
+}
+
+vfs_t *
+vfs_allocate( void )
+{
+ struct vfs *vfsp;
+
+ vfsp = kmem_zalloc(sizeof(vfs_t), KM_SLEEP);
+ bhv_head_init(VFS_BHVHEAD(vfsp), "vfs");
+ INIT_LIST_HEAD(&vfsp->vfs_sync_list);
+ vfsp->vfs_sync_lock = SPIN_LOCK_UNLOCKED;
+ init_waitqueue_head(&vfsp->vfs_wait_sync_task);
+ init_waitqueue_head(&vfsp->vfs_wait_unfrozen);
+ return vfsp;
+}
+
+void
+vfs_deallocate(
+ struct vfs *vfsp)
+{
+ bhv_head_destroy(VFS_BHVHEAD(vfsp));
+ kmem_free(vfsp, sizeof(vfs_t));
+}
+
+void
+vfs_insertops(
+ struct vfs *vfsp,
+ struct bhv_vfsops *vfsops)
+{
+ struct bhv_desc *bdp;
+
+ bdp = kmem_alloc(sizeof(struct bhv_desc), KM_SLEEP);
+ bhv_desc_init(bdp, NULL, vfsp, vfsops);
+ bhv_insert(&vfsp->vfs_bh, bdp);
+}
+
+void
+vfs_insertbhv(
+ struct vfs *vfsp,
+ struct bhv_desc *bdp,
+ struct vfsops *vfsops,
+ void *mount)
+{
+ bhv_desc_init(bdp, mount, vfsp, vfsops);
+ bhv_insert_initial(&vfsp->vfs_bh, bdp);
+}
+
+void
+bhv_remove_vfsops(
+ struct vfs *vfsp,
+ int pos)
+{
+ struct bhv_desc *bhv;
+
+ bhv = bhv_lookup_range(&vfsp->vfs_bh, pos, pos);
+ if (!bhv)
+ return;
+ bhv_remove(&vfsp->vfs_bh, bhv);
+ kmem_free(bhv, sizeof(*bhv));
+}
+
+void
+bhv_remove_all_vfsops(
+ struct vfs *vfsp,
+ int freebase)
+{
+ struct xfs_mount *mp;
+
+ bhv_remove_vfsops(vfsp, VFS_POSITION_QM);
+ bhv_remove_vfsops(vfsp, VFS_POSITION_DM);
+ if (!freebase)
+ return;
+ mp = XFS_BHVTOM(bhv_lookup(VFS_BHVHEAD(vfsp), &xfs_vfsops));
+ VFS_REMOVEBHV(vfsp, &mp->m_bhv);
+ xfs_mount_free(mp, 0);
+}
+
+void
+bhv_insert_all_vfsops(
+ struct vfs *vfsp)
+{
+ struct xfs_mount *mp;
+
+ mp = xfs_mount_init();
+ vfs_insertbhv(vfsp, &mp->m_bhv, &xfs_vfsops, mp);
+ vfs_insertdmapi(vfsp);
+ vfs_insertquota(vfsp);
+}
diff --git a/fs/xfs/linux-2.4/xfs_vfs.h b/fs/xfs/linux-2.4/xfs_vfs.h
new file mode 100644
index 00000000000000..4bdb297fd28a4f
--- /dev/null
+++ b/fs/xfs/linux-2.4/xfs_vfs.h
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_VFS_H__
+#define __XFS_VFS_H__
+
+#include <linux/vfs.h>
+#include "xfs_fs.h"
+
+struct fid;
+struct vfs;
+struct cred;
+struct vnode;
+struct statfs;
+struct seq_file;
+struct super_block;
+struct xfs_mount_args;
+
+typedef struct statfs xfs_statfs_t;
+
+typedef struct vfs_sync_work {
+ struct list_head w_list;
+ struct vfs *w_vfs;
+ void *w_data; /* syncer routine argument */
+ void (*w_syncer)(struct vfs *, void *);
+} vfs_sync_work_t;
+
+typedef struct vfs {
+ u_int vfs_flag; /* flags */
+ xfs_fsid_t vfs_fsid; /* file system ID */
+ xfs_fsid_t *vfs_altfsid; /* An ID fixed for life of FS */
+ bhv_head_t vfs_bh; /* head of vfs behavior chain */
+ struct super_block *vfs_super; /* generic superblock pointer */
+ struct task_struct *vfs_sync_task; /* generalised sync thread */
+ vfs_sync_work_t vfs_sync_work; /* work item for VFS_SYNC */
+ struct list_head vfs_sync_list; /* sync thread work item list */
+ spinlock_t vfs_sync_lock; /* work item list lock */
+ wait_queue_head_t vfs_wait_sync_task;
+ int vfs_frozen;
+ wait_queue_head_t vfs_wait_unfrozen;
+} vfs_t;
+
+#define vfs_fbhv vfs_bh.bh_first /* 1st on vfs behavior chain */
+
+#define bhvtovfs(bdp) ( (struct vfs *)BHV_VOBJ(bdp) )
+#define bhvtovfsops(bdp) ( (struct vfsops *)BHV_OPS(bdp) )
+#define VFS_BHVHEAD(vfs) ( &(vfs)->vfs_bh )
+#define VFS_REMOVEBHV(vfs, bdp) ( bhv_remove(VFS_BHVHEAD(vfs), bdp) )
+
+#define VFS_POSITION_BASE BHV_POSITION_BASE /* chain bottom */
+#define VFS_POSITION_TOP BHV_POSITION_TOP /* chain top */
+#define VFS_POSITION_INVALID BHV_POSITION_INVALID /* invalid pos. num */
+
+typedef enum {
+ VFS_BHV_UNKNOWN, /* not specified */
+ VFS_BHV_XFS, /* xfs */
+ VFS_BHV_DM, /* data migration */
+ VFS_BHV_QM, /* quota manager */
+ VFS_BHV_IO, /* IO path */
+ VFS_BHV_END /* housekeeping end-of-range */
+} vfs_bhv_t;
+
+#define VFS_POSITION_XFS (BHV_POSITION_BASE)
+#define VFS_POSITION_DM (VFS_POSITION_BASE+10)
+#define VFS_POSITION_QM (VFS_POSITION_BASE+20)
+#define VFS_POSITION_IO (VFS_POSITION_BASE+30)
+
+#define VFS_RDONLY 0x0001 /* read-only vfs */
+#define VFS_GRPID 0x0002 /* group-ID assigned from directory */
+#define VFS_DMI 0x0004 /* filesystem has the DMI enabled */
+#define VFS_UMOUNT 0x0008 /* unmount in progress */
+#define VFS_END 0x0008 /* max flag */
+
+#define SYNC_ATTR 0x0001 /* sync attributes */
+#define SYNC_CLOSE 0x0002 /* close file system down */
+#define SYNC_DELWRI 0x0004 /* look at delayed writes */
+#define SYNC_WAIT 0x0008 /* wait for i/o to complete */
+#define SYNC_BDFLUSH 0x0010 /* BDFLUSH is calling -- don't block */
+#define SYNC_FSDATA 0x0020 /* flush fs data (e.g. superblocks) */
+#define SYNC_REFCACHE 0x0040 /* prune some of the nfs ref cache */
+#define SYNC_REMOUNT 0x0080 /* remount readonly, no dummy LRs */
+
+#define IGET_NOALLOC 0x0001 /* vfs_get_inode may return NULL */
+
+typedef int (*vfs_mount_t)(bhv_desc_t *,
+ struct xfs_mount_args *, struct cred *);
+typedef int (*vfs_parseargs_t)(bhv_desc_t *, char *,
+ struct xfs_mount_args *, int);
+typedef int (*vfs_showargs_t)(bhv_desc_t *, struct seq_file *);
+typedef int (*vfs_unmount_t)(bhv_desc_t *, int, struct cred *);
+typedef int (*vfs_mntupdate_t)(bhv_desc_t *, int *,
+ struct xfs_mount_args *);
+typedef int (*vfs_root_t)(bhv_desc_t *, struct vnode **);
+typedef int (*vfs_statvfs_t)(bhv_desc_t *, xfs_statfs_t *, struct vnode *);
+typedef int (*vfs_sync_t)(bhv_desc_t *, int, struct cred *);
+typedef int (*vfs_vget_t)(bhv_desc_t *, struct vnode **, struct fid *);
+typedef int (*vfs_dmapiops_t)(bhv_desc_t *, caddr_t);
+typedef int (*vfs_quotactl_t)(bhv_desc_t *, int, int, caddr_t);
+typedef void (*vfs_init_vnode_t)(bhv_desc_t *,
+ struct vnode *, bhv_desc_t *, int);
+typedef void (*vfs_force_shutdown_t)(bhv_desc_t *, int, char *, int);
+typedef void (*vfs_freeze_t)(bhv_desc_t *);
+typedef struct inode * (*vfs_get_inode_t)(bhv_desc_t *, xfs_ino_t, int);
+
+typedef struct vfsops {
+ bhv_position_t vf_position; /* behavior chain position */
+ vfs_mount_t vfs_mount; /* mount file system */
+ vfs_parseargs_t vfs_parseargs; /* parse mount options */
+ vfs_showargs_t vfs_showargs; /* unparse mount options */
+ vfs_unmount_t vfs_unmount; /* unmount file system */
+ vfs_mntupdate_t vfs_mntupdate; /* update file system options */
+ vfs_root_t vfs_root; /* get root vnode */
+ vfs_statvfs_t vfs_statvfs; /* file system statistics */
+ vfs_sync_t vfs_sync; /* flush files */
+ vfs_vget_t vfs_vget; /* get vnode from fid */
+ vfs_dmapiops_t vfs_dmapiops; /* data migration */
+ vfs_quotactl_t vfs_quotactl; /* disk quota */
+ vfs_get_inode_t vfs_get_inode; /* bhv specific iget */
+ vfs_init_vnode_t vfs_init_vnode; /* initialize a new vnode */
+ vfs_force_shutdown_t vfs_force_shutdown; /* crash and burn */
+ vfs_freeze_t vfs_freeze; /* freeze fs for snapshot */
+} vfsops_t;
+
+/*
+ * VFS's. Operates on vfs structure pointers (starts at bhv head).
+ */
+#define VHEAD(v) ((v)->vfs_fbhv)
+#define VFS_MOUNT(v, ma,cr, rv) ((rv) = vfs_mount(VHEAD(v), ma,cr))
+#define VFS_PARSEARGS(v, o,ma,f, rv) ((rv) = vfs_parseargs(VHEAD(v), o,ma,f))
+#define VFS_SHOWARGS(v, m, rv) ((rv) = vfs_showargs(VHEAD(v), m))
+#define VFS_UNMOUNT(v, f, cr, rv) ((rv) = vfs_unmount(VHEAD(v), f,cr))
+#define VFS_MNTUPDATE(v, fl, args, rv) ((rv) = vfs_mntupdate(VHEAD(v), fl, args))
+#define VFS_ROOT(v, vpp, rv) ((rv) = vfs_root(VHEAD(v), vpp))
+#define VFS_STATVFS(v, sp,vp, rv) ((rv) = vfs_statvfs(VHEAD(v), sp,vp))
+#define VFS_SYNC(v, flag,cr, rv) ((rv) = vfs_sync(VHEAD(v), flag,cr))
+#define VFS_VGET(v, vpp,fidp, rv) ((rv) = vfs_vget(VHEAD(v), vpp,fidp))
+#define VFS_DMAPIOPS(v, p, rv) ((rv) = vfs_dmapiops(VHEAD(v), p))
+#define VFS_QUOTACTL(v, c,id,p, rv) ((rv) = vfs_quotactl(VHEAD(v), c,id,p))
+#define VFS_GET_INODE(v, ino, fl) ( vfs_get_inode(VHEAD(v), ino,fl) )
+#define VFS_INIT_VNODE(v, vp,b,ul) ( vfs_init_vnode(VHEAD(v), vp,b,ul) )
+#define VFS_FORCE_SHUTDOWN(v, fl,f,l) ( vfs_force_shutdown(VHEAD(v), fl,f,l) )
+#define VFS_FREEZE(v) ( vfs_freeze(VHEAD(v)) )
+
+/*
+ * PVFS's. Operates on behavior descriptor pointers.
+ */
+#define PVFS_MOUNT(b, ma,cr, rv) ((rv) = vfs_mount(b, ma,cr))
+#define PVFS_PARSEARGS(b, o,ma,f, rv) ((rv) = vfs_parseargs(b, o,ma,f))
+#define PVFS_SHOWARGS(b, m, rv) ((rv) = vfs_showargs(b, m))
+#define PVFS_UNMOUNT(b, f,cr, rv) ((rv) = vfs_unmount(b, f,cr))
+#define PVFS_MNTUPDATE(b, fl, args, rv) ((rv) = vfs_mntupdate(b, fl, args))
+#define PVFS_ROOT(b, vpp, rv) ((rv) = vfs_root(b, vpp))
+#define PVFS_STATVFS(b, sp,vp, rv) ((rv) = vfs_statvfs(b, sp,vp))
+#define PVFS_SYNC(b, flag,cr, rv) ((rv) = vfs_sync(b, flag,cr))
+#define PVFS_VGET(b, vpp,fidp, rv) ((rv) = vfs_vget(b, vpp,fidp))
+#define PVFS_DMAPIOPS(b, p, rv) ((rv) = vfs_dmapiops(b, p))
+#define PVFS_QUOTACTL(b, c,id,p, rv) ((rv) = vfs_quotactl(b, c,id,p))
+#define PVFS_GET_INODE(b, ino,fl) ( vfs_get_inode(b, ino,fl) )
+#define PVFS_INIT_VNODE(b, vp,b2,ul) ( vfs_init_vnode(b, vp,b2,ul) )
+#define PVFS_FORCE_SHUTDOWN(b, fl,f,l) ( vfs_force_shutdown(b, fl,f,l) )
+#define PVFS_FREEZE(b) ( vfs_freeze(b) )
+
+extern int vfs_mount(bhv_desc_t *, struct xfs_mount_args *, struct cred *);
+extern int vfs_parseargs(bhv_desc_t *, char *, struct xfs_mount_args *, int);
+extern int vfs_showargs(bhv_desc_t *, struct seq_file *);
+extern int vfs_unmount(bhv_desc_t *, int, struct cred *);
+extern int vfs_mntupdate(bhv_desc_t *, int *, struct xfs_mount_args *);
+extern int vfs_root(bhv_desc_t *, struct vnode **);
+extern int vfs_statvfs(bhv_desc_t *, xfs_statfs_t *, struct vnode *);
+extern int vfs_sync(bhv_desc_t *, int, struct cred *);
+extern int vfs_vget(bhv_desc_t *, struct vnode **, struct fid *);
+extern int vfs_dmapiops(bhv_desc_t *, caddr_t);
+extern int vfs_quotactl(bhv_desc_t *, int, int, caddr_t);
+extern struct inode *vfs_get_inode(bhv_desc_t *, xfs_ino_t, int);
+extern void vfs_init_vnode(bhv_desc_t *, struct vnode *, bhv_desc_t *, int);
+extern void vfs_force_shutdown(bhv_desc_t *, int, char *, int);
+extern void vfs_freeze(bhv_desc_t *);
+
+typedef struct bhv_vfsops {
+ struct vfsops bhv_common;
+ void * bhv_custom;
+} bhv_vfsops_t;
+
+#define vfs_bhv_lookup(v, id) ( bhv_lookup_range(&(v)->vfs_bh, (id), (id)) )
+#define vfs_bhv_custom(b) ( ((bhv_vfsops_t *)BHV_OPS(b))->bhv_custom )
+#define vfs_bhv_set_custom(b,o) ( (b)->bhv_custom = (void *)(o))
+#define vfs_bhv_clr_custom(b) ( (b)->bhv_custom = NULL )
+
+extern vfs_t *vfs_allocate(void);
+extern void vfs_deallocate(vfs_t *);
+extern void vfs_insertops(vfs_t *, bhv_vfsops_t *);
+extern void vfs_insertbhv(vfs_t *, bhv_desc_t *, vfsops_t *, void *);
+
+extern void bhv_insert_all_vfsops(struct vfs *);
+extern void bhv_remove_all_vfsops(struct vfs *, int);
+extern void bhv_remove_vfsops(struct vfs *, int);
+
+enum {
+ SB_UNFROZEN = 0,
+ SB_FREEZE_WRITE = 1,
+ SB_FREEZE_TRANS = 2,
+};
+
+#define fs_frozen(vfsp) ((vfsp)->vfs_frozen)
+#define fs_check_frozen(vfsp, level) \
+ wait_event((vfsp)->vfs_wait_unfrozen, ((vfsp)->vfs_frozen < (level)))
+
+#endif /* __XFS_VFS_H__ */
diff --git a/fs/xfs/linux-2.4/xfs_vnode.c b/fs/xfs/linux-2.4/xfs_vnode.c
new file mode 100644
index 00000000000000..b47bfc24a1169c
--- /dev/null
+++ b/fs/xfs/linux-2.4/xfs_vnode.c
@@ -0,0 +1,465 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+
+
+uint64_t vn_generation; /* vnode generation number */
+spinlock_t vnumber_lock = SPIN_LOCK_UNLOCKED;
+
+/*
+ * Dedicated vnode inactive/reclaim sync semaphores.
+ * Prime number of hash buckets since address is used as the key.
+ */
+#define NVSYNC 37
+#define vptosync(v) (&vsync[((unsigned long)v) % NVSYNC])
+sv_t vsync[NVSYNC];
+
+/*
+ * Translate stat(2) file types to vnode types and vice versa.
+ * Aware of numeric order of S_IFMT and vnode type values.
+ */
+enum vtype iftovt_tab[] = {
+ VNON, VFIFO, VCHR, VNON, VDIR, VNON, VBLK, VNON,
+ VREG, VNON, VLNK, VNON, VSOCK, VNON, VNON, VNON
+};
+
+u_short vttoif_tab[] = {
+ 0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, S_IFIFO, 0, S_IFSOCK
+};
+
+
+void
+vn_init(void)
+{
+ register sv_t *svp;
+ register int i;
+
+ for (svp = vsync, i = 0; i < NVSYNC; i++, svp++)
+ init_sv(svp, SV_DEFAULT, "vsy", i);
+}
+
+/*
+ * Clean a vnode of filesystem-specific data and prepare it for reuse.
+ */
+STATIC int
+vn_reclaim(
+ struct vnode *vp)
+{
+ int error;
+
+ XFS_STATS_INC(vn_reclaim);
+ vn_trace_entry(vp, "vn_reclaim", (inst_t *)__return_address);
+
+ /*
+ * Only make the VOP_RECLAIM call if there are behaviors
+ * to call.
+ */
+ if (vp->v_fbhv) {
+ VOP_RECLAIM(vp, error);
+ if (error)
+ return -error;
+ }
+ ASSERT(vp->v_fbhv == NULL);
+
+ VN_LOCK(vp);
+ vp->v_flag &= (VRECLM|VWAIT);
+ VN_UNLOCK(vp, 0);
+
+ vp->v_type = VNON;
+ vp->v_fbhv = NULL;
+
+#ifdef XFS_VNODE_TRACE
+ ktrace_free(vp->v_trace);
+ vp->v_trace = NULL;
+#endif
+
+ return 0;
+}
+
+STATIC void
+vn_wakeup(
+ struct vnode *vp)
+{
+ VN_LOCK(vp);
+ if (vp->v_flag & VWAIT)
+ sv_broadcast(vptosync(vp));
+ vp->v_flag &= ~(VRECLM|VWAIT|VMODIFIED);
+ VN_UNLOCK(vp, 0);
+}
+
+int
+vn_wait(
+ struct vnode *vp)
+{
+ VN_LOCK(vp);
+ if (vp->v_flag & (VINACT | VRECLM)) {
+ vp->v_flag |= VWAIT;
+ sv_wait(vptosync(vp), PINOD, &vp->v_lock, 0);
+ return 1;
+ }
+ VN_UNLOCK(vp, 0);
+ return 0;
+}
+
+struct vnode *
+vn_initialize(
+ struct inode *inode)
+{
+ struct vnode *vp = LINVFS_GET_VP(inode);
+
+ XFS_STATS_INC(vn_active);
+ XFS_STATS_INC(vn_alloc);
+
+ vp->v_flag = VMODIFIED;
+ spinlock_init(&vp->v_lock, "v_lock");
+
+ spin_lock(&vnumber_lock);
+ if (!++vn_generation) /* v_number shouldn't be zero */
+ vn_generation++;
+ vp->v_number = vn_generation;
+ spin_unlock(&vnumber_lock);
+
+ ASSERT(VN_CACHED(vp) == 0);
+
+ /* Initialize the first behavior and the behavior chain head. */
+ vn_bhv_head_init(VN_BHV_HEAD(vp), "vnode");
+
+#ifdef XFS_VNODE_TRACE
+ vp->v_trace = ktrace_alloc(VNODE_TRACE_SIZE, KM_SLEEP);
+ printk("Allocated VNODE_TRACE at 0x%p\n", vp->v_trace);
+#endif /* XFS_VNODE_TRACE */
+
+ vn_trace_exit(vp, "vn_initialize", (inst_t *)__return_address);
+ return vp;
+}
+
+/*
+ * Get a reference on a vnode.
+ */
+vnode_t *
+vn_get(
+ struct vnode *vp,
+ vmap_t *vmap)
+{
+ struct inode *inode;
+
+ XFS_STATS_INC(vn_get);
+ inode = LINVFS_GET_IP(vp);
+ if (inode->i_state & I_FREEING)
+ return NULL;
+
+ inode = VFS_GET_INODE(vmap->v_vfsp, vmap->v_ino, IGET_NOALLOC);
+ if (!inode) /* Inode not present */
+ return NULL;
+
+ /* We do not want to create new inodes via vn_get,
+ * returning NULL here is OK.
+ */
+ if (inode->i_state & I_NEW) {
+ vn_mark_bad(vp);
+ unlock_new_inode(inode);
+ iput(inode);
+ return NULL;
+ }
+
+ vn_trace_exit(vp, "vn_get", (inst_t *)__return_address);
+
+ return vp;
+}
+
+/*
+ * Revalidate the Linux inode from the vattr.
+ * Note: i_size _not_ updated; we must hold the inode
+ * semaphore when doing that - callers responsibility.
+ */
+void
+vn_revalidate_core(
+ struct vnode *vp,
+ vattr_t *vap)
+{
+ struct inode *inode = LINVFS_GET_IP(vp);
+
+ inode->i_mode = VTTOIF(vap->va_type) | vap->va_mode;
+ inode->i_nlink = vap->va_nlink;
+ inode->i_uid = vap->va_uid;
+ inode->i_gid = vap->va_gid;
+ inode->i_blocks = vap->va_nblocks;
+ inode->i_mtime = vap->va_mtime.tv_sec;
+ inode->i_ctime = vap->va_ctime.tv_sec;
+ inode->i_atime = vap->va_atime.tv_sec;
+ if (vap->va_xflags & XFS_XFLAG_IMMUTABLE)
+ inode->i_flags |= S_IMMUTABLE;
+ else
+ inode->i_flags &= ~S_IMMUTABLE;
+ if (vap->va_xflags & XFS_XFLAG_APPEND)
+ inode->i_flags |= S_APPEND;
+ else
+ inode->i_flags &= ~S_APPEND;
+ if (vap->va_xflags & XFS_XFLAG_SYNC)
+ inode->i_flags |= S_SYNC;
+ else
+ inode->i_flags &= ~S_SYNC;
+ if (vap->va_xflags & XFS_XFLAG_NOATIME)
+ inode->i_flags |= S_NOATIME;
+ else
+ inode->i_flags &= ~S_NOATIME;
+}
+
+/*
+ * Revalidate the Linux inode from the vnode.
+ */
+int
+vn_revalidate(
+ struct vnode *vp)
+{
+ vattr_t va;
+ int error;
+
+ vn_trace_entry(vp, "vn_revalidate", (inst_t *)__return_address);
+ ASSERT(vp->v_fbhv != NULL);
+
+ va.va_mask = XFS_AT_STAT|XFS_AT_XFLAGS;
+ VOP_GETATTR(vp, &va, 0, NULL, error);
+ if (!error) {
+ vn_revalidate_core(vp, &va);
+ VUNMODIFY(vp);
+ }
+ return -error;
+}
+
+/*
+ * purge a vnode from the cache
+ * At this point the vnode is guaranteed to have no references (vn_count == 0)
+ * The caller has to make sure that there are no ways someone could
+ * get a handle (via vn_get) on the vnode (usually done via a mount/vfs lock).
+ */
+void
+vn_purge(
+ struct vnode *vp,
+ vmap_t *vmap)
+{
+ vn_trace_entry(vp, "vn_purge", (inst_t *)__return_address);
+
+again:
+ /*
+ * Check whether vp has already been reclaimed since our caller
+ * sampled its version while holding a filesystem cache lock that
+ * its VOP_RECLAIM function acquires.
+ */
+ VN_LOCK(vp);
+ if (vp->v_number != vmap->v_number) {
+ VN_UNLOCK(vp, 0);
+ return;
+ }
+
+ /*
+ * If vp is being reclaimed or inactivated, wait until it is inert,
+ * then proceed. Can't assume that vnode is actually reclaimed
+ * just because the reclaimed flag is asserted -- a vn_alloc
+ * reclaim can fail.
+ */
+ if (vp->v_flag & (VINACT | VRECLM)) {
+ ASSERT(vn_count(vp) == 0);
+ vp->v_flag |= VWAIT;
+ sv_wait(vptosync(vp), PINOD, &vp->v_lock, 0);
+ goto again;
+ }
+
+ /*
+ * Another process could have raced in and gotten this vnode...
+ */
+ if (vn_count(vp) > 0) {
+ VN_UNLOCK(vp, 0);
+ return;
+ }
+
+ XFS_STATS_DEC(vn_active);
+ vp->v_flag |= VRECLM;
+ VN_UNLOCK(vp, 0);
+
+ /*
+ * Call VOP_RECLAIM and clean vp. The FSYNC_INVAL flag tells
+ * vp's filesystem to flush and invalidate all cached resources.
+ * When vn_reclaim returns, vp should have no private data,
+ * either in a system cache or attached to v_data.
+ */
+ if (vn_reclaim(vp) != 0)
+ panic("vn_purge: cannot reclaim");
+
+ /*
+ * Wakeup anyone waiting for vp to be reclaimed.
+ */
+ vn_wakeup(vp);
+}
+
+/*
+ * Add a reference to a referenced vnode.
+ */
+struct vnode *
+vn_hold(
+ struct vnode *vp)
+{
+ struct inode *inode;
+
+ XFS_STATS_INC(vn_hold);
+
+ VN_LOCK(vp);
+ inode = igrab(LINVFS_GET_IP(vp));
+ ASSERT(inode);
+ VN_UNLOCK(vp, 0);
+
+ return vp;
+}
+
+/*
+ * Call VOP_INACTIVE on last reference.
+ */
+void
+vn_rele(
+ struct vnode *vp)
+{
+ int vcnt;
+ int cache;
+
+ XFS_STATS_INC(vn_rele);
+
+ VN_LOCK(vp);
+
+ vn_trace_entry(vp, "vn_rele", (inst_t *)__return_address);
+ vcnt = vn_count(vp);
+
+ /*
+ * Since we always get called from put_inode we know
+ * that i_count won't be decremented after we
+ * return.
+ */
+ if (!vcnt) {
+ /*
+ * As soon as we turn this on, noone can find us in vn_get
+ * until we turn off VINACT or VRECLM
+ */
+ vp->v_flag |= VINACT;
+ VN_UNLOCK(vp, 0);
+
+ /*
+ * Do not make the VOP_INACTIVE call if there
+ * are no behaviors attached to the vnode to call.
+ */
+ if (vp->v_fbhv)
+ VOP_INACTIVE(vp, NULL, cache);
+
+ VN_LOCK(vp);
+ if (vp->v_flag & VWAIT)
+ sv_broadcast(vptosync(vp));
+
+ vp->v_flag &= ~(VINACT|VWAIT|VRECLM|VMODIFIED);
+ }
+
+ VN_UNLOCK(vp, 0);
+
+ vn_trace_exit(vp, "vn_rele", (inst_t *)__return_address);
+}
+
+/*
+ * Finish the removal of a vnode.
+ */
+void
+vn_remove(
+ struct vnode *vp)
+{
+ vmap_t vmap;
+
+ /* Make sure we don't do this to the same vnode twice */
+ if (!(vp->v_fbhv))
+ return;
+
+ XFS_STATS_INC(vn_remove);
+ vn_trace_exit(vp, "vn_remove", (inst_t *)__return_address);
+
+ /*
+ * After the following purge the vnode
+ * will no longer exist.
+ */
+ VMAP(vp, vmap);
+ vn_purge(vp, &vmap);
+}
+
+
+#ifdef XFS_VNODE_TRACE
+
+#define KTRACE_ENTER(vp, vk, s, line, ra) \
+ ktrace_enter( (vp)->v_trace, \
+/* 0 */ (void *)(__psint_t)(vk), \
+/* 1 */ (void *)(s), \
+/* 2 */ (void *)(__psint_t) line, \
+/* 3 */ (void *)(vn_count(vp)), \
+/* 4 */ (void *)(ra), \
+/* 5 */ (void *)(__psunsigned_t)(vp)->v_flag, \
+/* 6 */ (void *)(__psint_t)smp_processor_id(), \
+/* 7 */ (void *)(__psint_t)(current->pid), \
+/* 8 */ (void *)__return_address, \
+/* 9 */ 0, 0, 0, 0, 0, 0, 0)
+
+/*
+ * Vnode tracing code.
+ */
+void
+vn_trace_entry(vnode_t *vp, char *func, inst_t *ra)
+{
+ KTRACE_ENTER(vp, VNODE_KTRACE_ENTRY, func, 0, ra);
+}
+
+void
+vn_trace_exit(vnode_t *vp, char *func, inst_t *ra)
+{
+ KTRACE_ENTER(vp, VNODE_KTRACE_EXIT, func, 0, ra);
+}
+
+void
+vn_trace_hold(vnode_t *vp, char *file, int line, inst_t *ra)
+{
+ KTRACE_ENTER(vp, VNODE_KTRACE_HOLD, file, line, ra);
+}
+
+void
+vn_trace_ref(vnode_t *vp, char *file, int line, inst_t *ra)
+{
+ KTRACE_ENTER(vp, VNODE_KTRACE_REF, file, line, ra);
+}
+
+void
+vn_trace_rele(vnode_t *vp, char *file, int line, inst_t *ra)
+{
+ KTRACE_ENTER(vp, VNODE_KTRACE_RELE, file, line, ra);
+}
+#endif /* XFS_VNODE_TRACE */
diff --git a/fs/xfs/linux-2.4/xfs_vnode.h b/fs/xfs/linux-2.4/xfs_vnode.h
new file mode 100644
index 00000000000000..cf3d36f92d62d0
--- /dev/null
+++ b/fs/xfs/linux-2.4/xfs_vnode.h
@@ -0,0 +1,668 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ *
+ * Portions Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#ifndef __XFS_VNODE_H__
+#define __XFS_VNODE_H__
+
+struct uio;
+struct file;
+struct vattr;
+struct xfs_iomap;
+struct attrlist_cursor_kern;
+
+/*
+ * Vnode types. VNON means no type.
+ */
+enum vtype { VNON, VREG, VDIR, VBLK, VCHR, VLNK, VFIFO, VBAD, VSOCK };
+
+typedef xfs_ino_t vnumber_t;
+typedef struct dentry vname_t;
+typedef bhv_head_t vn_bhv_head_t;
+
+/*
+ * MP locking protocols:
+ * v_flag, v_vfsp VN_LOCK/VN_UNLOCK
+ * v_type read-only or fs-dependent
+ */
+typedef struct vnode {
+ __u32 v_flag; /* vnode flags (see below) */
+ enum vtype v_type; /* vnode type */
+ struct vfs *v_vfsp; /* ptr to containing VFS */
+ vnumber_t v_number; /* in-core vnode number */
+ vn_bhv_head_t v_bh; /* behavior head */
+ spinlock_t v_lock; /* VN_LOCK/VN_UNLOCK */
+ struct inode v_inode; /* Linux inode */
+#ifdef XFS_VNODE_TRACE
+ struct ktrace *v_trace; /* trace header structure */
+#endif
+} vnode_t;
+
+#define v_fbhv v_bh.bh_first /* first behavior */
+#define v_fops v_bh.bh_first->bd_ops /* first behavior ops */
+
+#define VNODE_POSITION_BASE BHV_POSITION_BASE /* chain bottom */
+#define VNODE_POSITION_TOP BHV_POSITION_TOP /* chain top */
+#define VNODE_POSITION_INVALID BHV_POSITION_INVALID /* invalid pos. num */
+
+typedef enum {
+ VN_BHV_UNKNOWN, /* not specified */
+ VN_BHV_XFS, /* xfs */
+ VN_BHV_DM, /* data migration */
+ VN_BHV_QM, /* quota manager */
+ VN_BHV_IO, /* IO path */
+ VN_BHV_END /* housekeeping end-of-range */
+} vn_bhv_t;
+
+#define VNODE_POSITION_XFS (VNODE_POSITION_BASE)
+#define VNODE_POSITION_DM (VNODE_POSITION_BASE+10)
+#define VNODE_POSITION_QM (VNODE_POSITION_BASE+20)
+#define VNODE_POSITION_IO (VNODE_POSITION_BASE+30)
+
+/*
+ * Macros for dealing with the behavior descriptor inside of the vnode.
+ */
+#define BHV_TO_VNODE(bdp) ((vnode_t *)BHV_VOBJ(bdp))
+#define BHV_TO_VNODE_NULL(bdp) ((vnode_t *)BHV_VOBJNULL(bdp))
+
+#define VN_BHV_HEAD(vp) ((bhv_head_t *)(&((vp)->v_bh)))
+#define vn_bhv_head_init(bhp,name) bhv_head_init(bhp,name)
+#define vn_bhv_remove(bhp,bdp) bhv_remove(bhp,bdp)
+#define vn_bhv_lookup(bhp,ops) bhv_lookup(bhp,ops)
+#define vn_bhv_lookup_unlocked(bhp,ops) bhv_lookup_unlocked(bhp,ops)
+
+/*
+ * Vnode to Linux inode mapping.
+ */
+#define LINVFS_GET_VP(inode) ((vnode_t *)list_entry(inode, vnode_t, v_inode))
+#define LINVFS_GET_IP(vp) (&(vp)->v_inode)
+
+/*
+ * Convert between vnode types and inode formats (since POSIX.1
+ * defines mode word of stat structure in terms of inode formats).
+ */
+extern enum vtype iftovt_tab[];
+extern u_short vttoif_tab[];
+#define IFTOVT(mode) (iftovt_tab[((mode) & S_IFMT) >> 12])
+#define VTTOIF(indx) (vttoif_tab[(int)(indx)])
+#define MAKEIMODE(indx, mode) (int)(VTTOIF(indx) | (mode))
+
+
+/*
+ * Vnode flags.
+ */
+#define VINACT 0x1 /* vnode is being inactivated */
+#define VRECLM 0x2 /* vnode is being reclaimed */
+#define VWAIT 0x4 /* waiting for VINACT/VRECLM to end */
+#define VMODIFIED 0x8 /* XFS inode state possibly differs */
+ /* to the Linux inode state. */
+
+/*
+ * Values for the VOP_RWLOCK and VOP_RWUNLOCK flags parameter.
+ */
+typedef enum vrwlock {
+ VRWLOCK_NONE,
+ VRWLOCK_READ,
+ VRWLOCK_WRITE,
+ VRWLOCK_WRITE_DIRECT,
+ VRWLOCK_TRY_READ,
+ VRWLOCK_TRY_WRITE
+} vrwlock_t;
+
+/*
+ * Return values for VOP_INACTIVE. A return value of
+ * VN_INACTIVE_NOCACHE implies that the file system behavior
+ * has disassociated its state and bhv_desc_t from the vnode.
+ */
+#define VN_INACTIVE_CACHE 0
+#define VN_INACTIVE_NOCACHE 1
+
+/*
+ * Values for the cmd code given to VOP_VNODE_CHANGE.
+ */
+typedef enum vchange {
+ VCHANGE_FLAGS_FRLOCKS = 0,
+ VCHANGE_FLAGS_ENF_LOCKING = 1,
+ VCHANGE_FLAGS_TRUNCATED = 2,
+ VCHANGE_FLAGS_PAGE_DIRTY = 3,
+ VCHANGE_FLAGS_IOEXCL_COUNT = 4
+} vchange_t;
+
+
+typedef int (*vop_open_t)(bhv_desc_t *, struct cred *);
+typedef ssize_t (*vop_read_t)(bhv_desc_t *, struct file *, char *,
+ size_t, loff_t *, int, struct cred *);
+typedef ssize_t (*vop_write_t)(bhv_desc_t *, struct file *, const char *,
+ size_t, loff_t *, int, struct cred *);
+typedef int (*vop_ioctl_t)(bhv_desc_t *, struct inode *, struct file *,
+ int, unsigned int, unsigned long);
+typedef int (*vop_getattr_t)(bhv_desc_t *, struct vattr *, int,
+ struct cred *);
+typedef int (*vop_setattr_t)(bhv_desc_t *, struct vattr *, int,
+ struct cred *);
+typedef int (*vop_access_t)(bhv_desc_t *, int, struct cred *);
+typedef int (*vop_lookup_t)(bhv_desc_t *, vname_t *, vnode_t **,
+ int, vnode_t *, struct cred *);
+typedef int (*vop_create_t)(bhv_desc_t *, vname_t *, struct vattr *,
+ vnode_t **, struct cred *);
+typedef int (*vop_remove_t)(bhv_desc_t *, vname_t *, struct cred *);
+typedef int (*vop_link_t)(bhv_desc_t *, vnode_t *, vname_t *,
+ struct cred *);
+typedef int (*vop_rename_t)(bhv_desc_t *, vname_t *, vnode_t *, vname_t *,
+ struct cred *);
+typedef int (*vop_mkdir_t)(bhv_desc_t *, vname_t *, struct vattr *,
+ vnode_t **, struct cred *);
+typedef int (*vop_rmdir_t)(bhv_desc_t *, vname_t *, struct cred *);
+typedef int (*vop_readdir_t)(bhv_desc_t *, struct uio *, struct cred *,
+ int *);
+typedef int (*vop_symlink_t)(bhv_desc_t *, vname_t *, struct vattr *,
+ char *, vnode_t **, struct cred *);
+typedef int (*vop_readlink_t)(bhv_desc_t *, struct uio *, int,
+ struct cred *);
+typedef int (*vop_fsync_t)(bhv_desc_t *, int, struct cred *,
+ xfs_off_t, xfs_off_t);
+typedef int (*vop_inactive_t)(bhv_desc_t *, struct cred *);
+typedef int (*vop_fid2_t)(bhv_desc_t *, struct fid *);
+typedef int (*vop_release_t)(bhv_desc_t *);
+typedef int (*vop_rwlock_t)(bhv_desc_t *, vrwlock_t);
+typedef void (*vop_rwunlock_t)(bhv_desc_t *, vrwlock_t);
+typedef int (*vop_frlock_t)(bhv_desc_t *, int, struct file_lock *,int,
+ xfs_off_t, struct cred *);
+typedef int (*vop_bmap_t)(bhv_desc_t *, xfs_off_t, ssize_t, int,
+ struct xfs_iomap *, int *);
+typedef int (*vop_reclaim_t)(bhv_desc_t *);
+typedef int (*vop_attr_get_t)(bhv_desc_t *, char *, char *, int *, int,
+ struct cred *);
+typedef int (*vop_attr_set_t)(bhv_desc_t *, char *, char *, int, int,
+ struct cred *);
+typedef int (*vop_attr_remove_t)(bhv_desc_t *, char *, int, struct cred *);
+typedef int (*vop_attr_list_t)(bhv_desc_t *, char *, int, int,
+ struct attrlist_cursor_kern *, struct cred *);
+typedef void (*vop_link_removed_t)(bhv_desc_t *, vnode_t *, int);
+typedef void (*vop_vnode_change_t)(bhv_desc_t *, vchange_t, __psint_t);
+typedef void (*vop_ptossvp_t)(bhv_desc_t *, xfs_off_t, xfs_off_t, int);
+typedef void (*vop_pflushinvalvp_t)(bhv_desc_t *, xfs_off_t, xfs_off_t, int);
+typedef int (*vop_pflushvp_t)(bhv_desc_t *, xfs_off_t, xfs_off_t,
+ uint64_t, int);
+typedef int (*vop_iflush_t)(bhv_desc_t *, int);
+
+
+typedef struct vnodeops {
+ bhv_position_t vn_position; /* position within behavior chain */
+ vop_open_t vop_open;
+ vop_read_t vop_read;
+ vop_write_t vop_write;
+ vop_ioctl_t vop_ioctl;
+ vop_getattr_t vop_getattr;
+ vop_setattr_t vop_setattr;
+ vop_access_t vop_access;
+ vop_lookup_t vop_lookup;
+ vop_create_t vop_create;
+ vop_remove_t vop_remove;
+ vop_link_t vop_link;
+ vop_rename_t vop_rename;
+ vop_mkdir_t vop_mkdir;
+ vop_rmdir_t vop_rmdir;
+ vop_readdir_t vop_readdir;
+ vop_symlink_t vop_symlink;
+ vop_readlink_t vop_readlink;
+ vop_fsync_t vop_fsync;
+ vop_inactive_t vop_inactive;
+ vop_fid2_t vop_fid2;
+ vop_rwlock_t vop_rwlock;
+ vop_rwunlock_t vop_rwunlock;
+ vop_frlock_t vop_frlock;
+ vop_bmap_t vop_bmap;
+ vop_reclaim_t vop_reclaim;
+ vop_attr_get_t vop_attr_get;
+ vop_attr_set_t vop_attr_set;
+ vop_attr_remove_t vop_attr_remove;
+ vop_attr_list_t vop_attr_list;
+ vop_link_removed_t vop_link_removed;
+ vop_vnode_change_t vop_vnode_change;
+ vop_ptossvp_t vop_tosspages;
+ vop_pflushinvalvp_t vop_flushinval_pages;
+ vop_pflushvp_t vop_flush_pages;
+ vop_release_t vop_release;
+ vop_iflush_t vop_iflush;
+} vnodeops_t;
+
+/*
+ * VOP's.
+ */
+#define _VOP_(op, vp) (*((vnodeops_t *)(vp)->v_fops)->op)
+
+#define VOP_READ(vp,file,buf,size,offset,ioflags,cr,rv) \
+ rv = _VOP_(vop_read, vp)((vp)->v_fbhv,file,buf,size,offset,ioflags,cr)
+#define VOP_WRITE(vp,file,buf,size,offset,ioflags,cr,rv) \
+ rv = _VOP_(vop_write, vp)((vp)->v_fbhv,file,buf,size,offset,ioflags,cr)
+#define VOP_BMAP(vp,of,sz,rw,b,n,rv) \
+ rv = _VOP_(vop_bmap, vp)((vp)->v_fbhv,of,sz,rw,b,n)
+#define VOP_OPEN(vp, cr, rv) \
+ rv = _VOP_(vop_open, vp)((vp)->v_fbhv, cr)
+#define VOP_GETATTR(vp, vap, f, cr, rv) \
+ rv = _VOP_(vop_getattr, vp)((vp)->v_fbhv, vap, f, cr)
+#define VOP_SETATTR(vp, vap, f, cr, rv) \
+ rv = _VOP_(vop_setattr, vp)((vp)->v_fbhv, vap, f, cr)
+#define VOP_ACCESS(vp, mode, cr, rv) \
+ rv = _VOP_(vop_access, vp)((vp)->v_fbhv, mode, cr)
+#define VOP_LOOKUP(vp,d,vpp,f,rdir,cr,rv) \
+ rv = _VOP_(vop_lookup, vp)((vp)->v_fbhv,d,vpp,f,rdir,cr)
+#define VOP_CREATE(dvp,d,vap,vpp,cr,rv) \
+ rv = _VOP_(vop_create, dvp)((dvp)->v_fbhv,d,vap,vpp,cr)
+#define VOP_REMOVE(dvp,d,cr,rv) \
+ rv = _VOP_(vop_remove, dvp)((dvp)->v_fbhv,d,cr)
+#define VOP_LINK(tdvp,fvp,d,cr,rv) \
+ rv = _VOP_(vop_link, tdvp)((tdvp)->v_fbhv,fvp,d,cr)
+#define VOP_RENAME(fvp,fnm,tdvp,tnm,cr,rv) \
+ rv = _VOP_(vop_rename, fvp)((fvp)->v_fbhv,fnm,tdvp,tnm,cr)
+#define VOP_MKDIR(dp,d,vap,vpp,cr,rv) \
+ rv = _VOP_(vop_mkdir, dp)((dp)->v_fbhv,d,vap,vpp,cr)
+#define VOP_RMDIR(dp,d,cr,rv) \
+ rv = _VOP_(vop_rmdir, dp)((dp)->v_fbhv,d,cr)
+#define VOP_READDIR(vp,uiop,cr,eofp,rv) \
+ rv = _VOP_(vop_readdir, vp)((vp)->v_fbhv,uiop,cr,eofp)
+#define VOP_SYMLINK(dvp,d,vap,tnm,vpp,cr,rv) \
+ rv = _VOP_(vop_symlink, dvp) ((dvp)->v_fbhv,d,vap,tnm,vpp,cr)
+#define VOP_READLINK(vp,uiop,fl,cr,rv) \
+ rv = _VOP_(vop_readlink, vp)((vp)->v_fbhv,uiop,fl,cr)
+#define VOP_FSYNC(vp,f,cr,b,e,rv) \
+ rv = _VOP_(vop_fsync, vp)((vp)->v_fbhv,f,cr,b,e)
+#define VOP_INACTIVE(vp, cr, rv) \
+ rv = _VOP_(vop_inactive, vp)((vp)->v_fbhv, cr)
+#define VOP_RELEASE(vp, rv) \
+ rv = _VOP_(vop_release, vp)((vp)->v_fbhv)
+#define VOP_FID2(vp, fidp, rv) \
+ rv = _VOP_(vop_fid2, vp)((vp)->v_fbhv, fidp)
+#define VOP_RWLOCK(vp,i) \
+ (void)_VOP_(vop_rwlock, vp)((vp)->v_fbhv, i)
+#define VOP_RWLOCK_TRY(vp,i) \
+ _VOP_(vop_rwlock, vp)((vp)->v_fbhv, i)
+#define VOP_RWUNLOCK(vp,i) \
+ (void)_VOP_(vop_rwunlock, vp)((vp)->v_fbhv, i)
+#define VOP_FRLOCK(vp,c,fl,flags,offset,fr,rv) \
+ rv = _VOP_(vop_frlock, vp)((vp)->v_fbhv,c,fl,flags,offset,fr)
+#define VOP_RECLAIM(vp, rv) \
+ rv = _VOP_(vop_reclaim, vp)((vp)->v_fbhv)
+#define VOP_ATTR_GET(vp, name, val, vallenp, fl, cred, rv) \
+ rv = _VOP_(vop_attr_get, vp)((vp)->v_fbhv,name,val,vallenp,fl,cred)
+#define VOP_ATTR_SET(vp, name, val, vallen, fl, cred, rv) \
+ rv = _VOP_(vop_attr_set, vp)((vp)->v_fbhv,name,val,vallen,fl,cred)
+#define VOP_ATTR_REMOVE(vp, name, flags, cred, rv) \
+ rv = _VOP_(vop_attr_remove, vp)((vp)->v_fbhv,name,flags,cred)
+#define VOP_ATTR_LIST(vp, buf, buflen, fl, cursor, cred, rv) \
+ rv = _VOP_(vop_attr_list, vp)((vp)->v_fbhv,buf,buflen,fl,cursor,cred)
+#define VOP_LINK_REMOVED(vp, dvp, linkzero) \
+ (void)_VOP_(vop_link_removed, vp)((vp)->v_fbhv, dvp, linkzero)
+#define VOP_VNODE_CHANGE(vp, cmd, val) \
+ (void)_VOP_(vop_vnode_change, vp)((vp)->v_fbhv,cmd,val)
+/*
+ * These are page cache functions that now go thru VOPs.
+ * 'last' parameter is unused and left in for IRIX compatibility
+ */
+#define VOP_TOSS_PAGES(vp, first, last, fiopt) \
+ _VOP_(vop_tosspages, vp)((vp)->v_fbhv,first, last, fiopt)
+/*
+ * 'last' parameter is unused and left in for IRIX compatibility
+ */
+#define VOP_FLUSHINVAL_PAGES(vp, first, last, fiopt) \
+ _VOP_(vop_flushinval_pages, vp)((vp)->v_fbhv,first,last,fiopt)
+/*
+ * 'last' parameter is unused and left in for IRIX compatibility
+ */
+#define VOP_FLUSH_PAGES(vp, first, last, flags, fiopt, rv) \
+ rv = _VOP_(vop_flush_pages, vp)((vp)->v_fbhv,first,last,flags,fiopt)
+#define VOP_IOCTL(vp, inode, filp, fl, cmd, arg, rv) \
+ rv = _VOP_(vop_ioctl, vp)((vp)->v_fbhv,inode,filp,fl,cmd,arg)
+#define VOP_IFLUSH(vp, flags, rv) \
+ rv = _VOP_(vop_iflush, vp)((vp)->v_fbhv, flags)
+
+/*
+ * Flags for read/write calls - same values as IRIX
+ */
+#define IO_ISDIRECT 0x00004 /* bypass page cache */
+#define IO_INVIS 0x00020 /* don't update inode timestamps */
+#define IO_ISLOCKED 0x00800 /* don't do inode locking */
+
+/*
+ * Flags for VOP_IFLUSH call
+ */
+#define FLUSH_SYNC 1 /* wait for flush to complete */
+#define FLUSH_INODE 2 /* flush the inode itself */
+#define FLUSH_LOG 4 /* force the last log entry for
+ * this inode out to disk */
+
+/*
+ * Flush/Invalidate options for VOP_TOSS_PAGES, VOP_FLUSHINVAL_PAGES and
+ * VOP_FLUSH_PAGES.
+ */
+#define FI_NONE 0 /* none */
+#define FI_REMAPF 1 /* Do a remapf prior to the operation */
+#define FI_REMAPF_LOCKED 2 /* Do a remapf prior to the operation.
+ Prevent VM access to the pages until
+ the operation completes. */
+
+/*
+ * Vnode attributes. va_mask indicates those attributes the caller
+ * wants to set or extract.
+ */
+typedef struct vattr {
+ int va_mask; /* bit-mask of attributes present */
+ enum vtype va_type; /* vnode type (for create) */
+ mode_t va_mode; /* file access mode and type */
+ nlink_t va_nlink; /* number of references to file */
+ uid_t va_uid; /* owner user id */
+ gid_t va_gid; /* owner group id */
+ xfs_ino_t va_nodeid; /* file id */
+ xfs_off_t va_size; /* file size in bytes */
+ u_long va_blocksize; /* blocksize preferred for i/o */
+ struct timespec va_atime; /* time of last access */
+ struct timespec va_mtime; /* time of last modification */
+ struct timespec va_ctime; /* time file changed */
+ u_int va_gen; /* generation number of file */
+ xfs_dev_t va_rdev; /* device the special file represents */
+ __int64_t va_nblocks; /* number of blocks allocated */
+ u_long va_xflags; /* random extended file flags */
+ u_long va_extsize; /* file extent size */
+ u_long va_nextents; /* number of extents in file */
+ u_long va_anextents; /* number of attr extents in file */
+ int va_projid; /* project id */
+} vattr_t;
+
+/*
+ * setattr or getattr attributes
+ */
+#define XFS_AT_TYPE 0x00000001
+#define XFS_AT_MODE 0x00000002
+#define XFS_AT_UID 0x00000004
+#define XFS_AT_GID 0x00000008
+#define XFS_AT_FSID 0x00000010
+#define XFS_AT_NODEID 0x00000020
+#define XFS_AT_NLINK 0x00000040
+#define XFS_AT_SIZE 0x00000080
+#define XFS_AT_ATIME 0x00000100
+#define XFS_AT_MTIME 0x00000200
+#define XFS_AT_CTIME 0x00000400
+#define XFS_AT_RDEV 0x00000800
+#define XFS_AT_BLKSIZE 0x00001000
+#define XFS_AT_NBLOCKS 0x00002000
+#define XFS_AT_VCODE 0x00004000
+#define XFS_AT_MAC 0x00008000
+#define XFS_AT_UPDATIME 0x00010000
+#define XFS_AT_UPDMTIME 0x00020000
+#define XFS_AT_UPDCTIME 0x00040000
+#define XFS_AT_ACL 0x00080000
+#define XFS_AT_CAP 0x00100000
+#define XFS_AT_INF 0x00200000
+#define XFS_AT_XFLAGS 0x00400000
+#define XFS_AT_EXTSIZE 0x00800000
+#define XFS_AT_NEXTENTS 0x01000000
+#define XFS_AT_ANEXTENTS 0x02000000
+#define XFS_AT_PROJID 0x04000000
+#define XFS_AT_SIZE_NOPERM 0x08000000
+#define XFS_AT_GENCOUNT 0x10000000
+
+#define XFS_AT_ALL (XFS_AT_TYPE|XFS_AT_MODE|XFS_AT_UID|XFS_AT_GID|\
+ XFS_AT_FSID|XFS_AT_NODEID|XFS_AT_NLINK|XFS_AT_SIZE|\
+ XFS_AT_ATIME|XFS_AT_MTIME|XFS_AT_CTIME|XFS_AT_RDEV|\
+ XFS_AT_BLKSIZE|XFS_AT_NBLOCKS|XFS_AT_VCODE|XFS_AT_MAC|\
+ XFS_AT_ACL|XFS_AT_CAP|XFS_AT_INF|XFS_AT_XFLAGS|XFS_AT_EXTSIZE|\
+ XFS_AT_NEXTENTS|XFS_AT_ANEXTENTS|XFS_AT_PROJID|XFS_AT_GENCOUNT)
+
+#define XFS_AT_STAT (XFS_AT_TYPE|XFS_AT_MODE|XFS_AT_UID|XFS_AT_GID|\
+ XFS_AT_FSID|XFS_AT_NODEID|XFS_AT_NLINK|XFS_AT_SIZE|\
+ XFS_AT_ATIME|XFS_AT_MTIME|XFS_AT_CTIME|XFS_AT_RDEV|\
+ XFS_AT_BLKSIZE|XFS_AT_NBLOCKS|XFS_AT_PROJID)
+
+#define XFS_AT_TIMES (XFS_AT_ATIME|XFS_AT_MTIME|XFS_AT_CTIME)
+
+#define XFS_AT_UPDTIMES (XFS_AT_UPDATIME|XFS_AT_UPDMTIME|XFS_AT_UPDCTIME)
+
+#define XFS_AT_NOSET (XFS_AT_NLINK|XFS_AT_RDEV|XFS_AT_FSID|XFS_AT_NODEID|\
+ XFS_AT_TYPE|XFS_AT_BLKSIZE|XFS_AT_NBLOCKS|XFS_AT_VCODE|\
+ XFS_AT_NEXTENTS|XFS_AT_ANEXTENTS|XFS_AT_GENCOUNT)
+
+/*
+ * Modes.
+ */
+#define VSUID S_ISUID /* set user id on execution */
+#define VSGID S_ISGID /* set group id on execution */
+#define VSVTX S_ISVTX /* save swapped text even after use */
+#define VREAD S_IRUSR /* read, write, execute permissions */
+#define VWRITE S_IWUSR
+#define VEXEC S_IXUSR
+
+#define MODEMASK S_IALLUGO /* mode bits plus permission bits */
+
+/*
+ * Check whether mandatory file locking is enabled.
+ */
+#define MANDLOCK(vp, mode) \
+ ((vp)->v_type == VREG && ((mode) & (VSGID|(VEXEC>>3))) == VSGID)
+
+extern void vn_init(void);
+extern int vn_wait(struct vnode *);
+extern vnode_t *vn_initialize(struct inode *);
+
+/*
+ * Acquiring and invalidating vnodes:
+ *
+ * if (vn_get(vp, version, 0))
+ * ...;
+ * vn_purge(vp, version);
+ *
+ * vn_get and vn_purge must be called with vmap_t arguments, sampled
+ * while a lock that the vnode's VOP_RECLAIM function acquires is
+ * held, to ensure that the vnode sampled with the lock held isn't
+ * recycled (VOP_RECLAIMed) or deallocated between the release of the lock
+ * and the subsequent vn_get or vn_purge.
+ */
+
+/*
+ * vnode_map structures _must_ match vn_epoch and vnode structure sizes.
+ */
+typedef struct vnode_map {
+ vfs_t *v_vfsp;
+ vnumber_t v_number; /* in-core vnode number */
+ xfs_ino_t v_ino; /* inode # */
+} vmap_t;
+
+#define VMAP(vp, vmap) {(vmap).v_vfsp = (vp)->v_vfsp, \
+ (vmap).v_number = (vp)->v_number, \
+ (vmap).v_ino = (vp)->v_inode.i_ino; }
+
+extern void vn_purge(struct vnode *, vmap_t *);
+extern vnode_t *vn_get(struct vnode *, vmap_t *);
+extern int vn_revalidate(struct vnode *);
+extern void vn_revalidate_core(struct vnode *, vattr_t *);
+extern void vn_remove(struct vnode *);
+
+static inline int vn_count(struct vnode *vp)
+{
+ return atomic_read(&LINVFS_GET_IP(vp)->i_count);
+}
+
+/*
+ * Vnode reference counting functions (and macros for compatibility).
+ */
+extern vnode_t *vn_hold(struct vnode *);
+extern void vn_rele(struct vnode *);
+
+#if defined(XFS_VNODE_TRACE)
+#define VN_HOLD(vp) \
+ ((void)vn_hold(vp), \
+ vn_trace_hold(vp, __FILE__, __LINE__, (inst_t *)__return_address))
+#define VN_RELE(vp) \
+ (vn_trace_rele(vp, __FILE__, __LINE__, (inst_t *)__return_address), \
+ iput(LINVFS_GET_IP(vp)))
+#else
+#define VN_HOLD(vp) ((void)vn_hold(vp))
+#define VN_RELE(vp) (iput(LINVFS_GET_IP(vp)))
+#endif
+
+/*
+ * Vname handling macros.
+ */
+#define VNAME(dentry) ((char *) (dentry)->d_name.name)
+#define VNAMELEN(dentry) ((dentry)->d_name.len)
+#define VNAME_TO_VNODE(dentry) (LINVFS_GET_VP((dentry)->d_inode))
+
+/*
+ * Vnode spinlock manipulation.
+ */
+#define VN_LOCK(vp) mutex_spinlock(&(vp)->v_lock)
+#define VN_UNLOCK(vp, s) mutex_spinunlock(&(vp)->v_lock, s)
+#define VN_FLAGSET(vp,b) vn_flagset(vp,b)
+#define VN_FLAGCLR(vp,b) vn_flagclr(vp,b)
+
+static __inline__ void vn_flagset(struct vnode *vp, uint flag)
+{
+ spin_lock(&vp->v_lock);
+ vp->v_flag |= flag;
+ spin_unlock(&vp->v_lock);
+}
+
+static __inline__ void vn_flagclr(struct vnode *vp, uint flag)
+{
+ spin_lock(&vp->v_lock);
+ vp->v_flag &= ~flag;
+ spin_unlock(&vp->v_lock);
+}
+
+/*
+ * Update modify/access/change times on the vnode
+ */
+#define VN_MTIMESET(vp, tvp) \
+ (LINVFS_GET_IP(vp)->i_mtime = (__int32_t)(tvp)->tv_sec)
+#define VN_ATIMESET(vp, tvp) \
+ (LINVFS_GET_IP(vp)->i_atime = (__int32_t)(tvp)->tv_sec)
+#define VN_CTIMESET(vp, tvp) \
+ (LINVFS_GET_IP(vp)->i_ctime = (__int32_t)(tvp)->tv_sec)
+
+/*
+ * Dealing with bad inodes
+ */
+static inline void vn_mark_bad(struct vnode *vp)
+{
+ struct inode *inode = LINVFS_GET_IP(vp);
+
+ remove_inode_hash(inode);
+ make_bad_inode(inode);
+}
+
+static inline int VN_BAD(struct vnode *vp)
+{
+ return is_bad_inode(LINVFS_GET_IP(vp));
+}
+
+
+/*
+ * Some useful predicates.
+ */
+#define VN_MAPPED(vp) ((LINVFS_GET_IP(vp)->i_mapping->i_mmap != NULL) || \
+ (LINVFS_GET_IP(vp)->i_mapping->i_mmap_shared != NULL))
+#define VN_CACHED(vp) (LINVFS_GET_IP(vp)->i_mapping->nrpages)
+#define VN_DIRTY(vp) (!list_empty(&(LINVFS_GET_IP(vp)->i_dirty_data_buffers)))
+#define VMODIFY(vp) VN_FLAGSET(vp, VMODIFIED)
+#define VUNMODIFY(vp) VN_FLAGCLR(vp, VMODIFIED)
+
+/*
+ * Flags to VOP_SETATTR/VOP_GETATTR.
+ */
+#define ATTR_UTIME 0x01 /* non-default utime(2) request */
+#define ATTR_DMI 0x08 /* invocation from a DMI function */
+#define ATTR_LAZY 0x80 /* set/get attributes lazily */
+#define ATTR_NONBLOCK 0x100 /* return EAGAIN if operation would block */
+
+/*
+ * Flags to VOP_FSYNC and VOP_RECLAIM.
+ */
+#define FSYNC_NOWAIT 0 /* asynchronous flush */
+#define FSYNC_WAIT 0x1 /* synchronous fsync or forced reclaim */
+#define FSYNC_INVAL 0x2 /* flush and invalidate cached data */
+#define FSYNC_DATA 0x4 /* synchronous fsync of data only */
+
+/*
+ * Tracking vnode activity.
+ */
+#if defined(XFS_VNODE_TRACE)
+
+#define VNODE_TRACE_SIZE 16 /* number of trace entries */
+#define VNODE_KTRACE_ENTRY 1
+#define VNODE_KTRACE_EXIT 2
+#define VNODE_KTRACE_HOLD 3
+#define VNODE_KTRACE_REF 4
+#define VNODE_KTRACE_RELE 5
+
+extern void vn_trace_entry(struct vnode *, char *, inst_t *);
+extern void vn_trace_exit(struct vnode *, char *, inst_t *);
+extern void vn_trace_hold(struct vnode *, char *, int, inst_t *);
+extern void vn_trace_ref(struct vnode *, char *, int, inst_t *);
+extern void vn_trace_rele(struct vnode *, char *, int, inst_t *);
+
+#define VN_TRACE(vp) \
+ vn_trace_ref(vp, __FILE__, __LINE__, (inst_t *)__return_address)
+#else
+#define vn_trace_entry(a,b,c)
+#define vn_trace_exit(a,b,c)
+#define vn_trace_hold(a,b,c,d)
+#define vn_trace_ref(a,b,c,d)
+#define vn_trace_rele(a,b,c,d)
+#define VN_TRACE(vp)
+#endif
+
+#endif /* __XFS_VNODE_H__ */
diff --git a/fs/xfs/quota/Makefile b/fs/xfs/quota/Makefile
new file mode 100644
index 00000000000000..626d1a98892419
--- /dev/null
+++ b/fs/xfs/quota/Makefile
@@ -0,0 +1,57 @@
+#
+# Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write the Free Software Foundation, Inc., 59
+# Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+# Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+# Mountain View, CA 94043, or:
+#
+# http://www.sgi.com
+#
+# For further information regarding this notice, see:
+#
+# http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+#
+
+EXTRA_CFLAGS += -I $(TOPDIR)/fs/xfs -I $(TOPDIR)/fs/xfs/linux-2.4
+
+ifeq ($(CONFIG_XFS_DEBUG),y)
+ EXTRA_CFLAGS += -g -DDEBUG
+ #EXTRA_CFLAGS += -DQUOTADEBUG
+endif
+ifeq ($(CONFIG_XFS_TRACE),y)
+ EXTRA_CFLAGS += -DXFS_DQUOT_TRACE
+endif
+
+O_TARGET := xfs_quota.o
+ifneq ($(MAKECMDGOALS),modules_install)
+ obj-m := $(O_TARGET)
+endif
+
+obj-$(CONFIG_PROC_FS) += xfs_qm_stats.o
+
+obj-y += xfs_dquot.o \
+ xfs_dquot_item.o \
+ xfs_trans_dquot.o \
+ xfs_qm_syscalls.o \
+ xfs_qm_bhv.o \
+ xfs_qm.o
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/xfs/quota/xfs_dquot.c b/fs/xfs/quota/xfs_dquot.c
new file mode 100644
index 00000000000000..fbfb24c7066ed2
--- /dev/null
+++ b/fs/xfs/quota/xfs_dquot.c
@@ -0,0 +1,1650 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_alloc.h"
+#include "xfs_dmapi.h"
+#include "xfs_quota.h"
+#include "xfs_mount.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode.h"
+#include "xfs_bmap.h"
+#include "xfs_bit.h"
+#include "xfs_rtalloc.h"
+#include "xfs_error.h"
+#include "xfs_itable.h"
+#include "xfs_rw.h"
+#include "xfs_acl.h"
+#include "xfs_cap.h"
+#include "xfs_mac.h"
+#include "xfs_attr.h"
+#include "xfs_buf_item.h"
+#include "xfs_trans_space.h"
+#include "xfs_trans_priv.h"
+
+#include "xfs_qm.h"
+
+
+/*
+ LOCK ORDER
+
+ inode lock (ilock)
+ dquot hash-chain lock (hashlock)
+ xqm dquot freelist lock (freelistlock
+ mount's dquot list lock (mplistlock)
+ user dquot lock - lock ordering among dquots is based on the uid or gid
+ group dquot lock - similar to udquots. Between the two dquots, the udquot
+ has to be locked first.
+ pin lock - the dquot lock must be held to take this lock.
+ flush lock - ditto.
+*/
+
+STATIC void xfs_qm_dqflush_done(xfs_buf_t *, xfs_dq_logitem_t *);
+
+#ifdef DEBUG
+xfs_buftarg_t *xfs_dqerror_target;
+int xfs_do_dqerror;
+int xfs_dqreq_num;
+int xfs_dqerror_mod = 33;
+#endif
+
+/*
+ * Allocate and initialize a dquot. We don't always allocate fresh memory;
+ * we try to reclaim a free dquot if the number of incore dquots are above
+ * a threshold.
+ * The only field inside the core that gets initialized at this point
+ * is the d_id field. The idea is to fill in the entire q_core
+ * when we read in the on disk dquot.
+ */
+xfs_dquot_t *
+xfs_qm_dqinit(
+ xfs_mount_t *mp,
+ xfs_dqid_t id,
+ uint type)
+{
+ xfs_dquot_t *dqp;
+ boolean_t brandnewdquot;
+
+ brandnewdquot = xfs_qm_dqalloc_incore(&dqp);
+ dqp->dq_flags = type;
+ INT_SET(dqp->q_core.d_id, ARCH_CONVERT, id);
+ dqp->q_mount = mp;
+
+ /*
+ * No need to re-initialize these if this is a reclaimed dquot.
+ */
+ if (brandnewdquot) {
+ dqp->dq_flnext = dqp->dq_flprev = dqp;
+ mutex_init(&dqp->q_qlock, MUTEX_DEFAULT, "xdq");
+ initnsema(&dqp->q_flock, 1, "fdq");
+ sv_init(&dqp->q_pinwait, SV_DEFAULT, "pdq");
+
+#ifdef XFS_DQUOT_TRACE
+ dqp->q_trace = ktrace_alloc(DQUOT_TRACE_SIZE, KM_SLEEP);
+ xfs_dqtrace_entry(dqp, "DQINIT");
+#endif
+ } else {
+ /*
+ * Only the q_core portion was zeroed in dqreclaim_one().
+ * So, we need to reset others.
+ */
+ dqp->q_nrefs = 0;
+ dqp->q_blkno = 0;
+ dqp->MPL_NEXT = dqp->HL_NEXT = NULL;
+ dqp->HL_PREVP = dqp->MPL_PREVP = NULL;
+ dqp->q_bufoffset = 0;
+ dqp->q_fileoffset = 0;
+ dqp->q_transp = NULL;
+ dqp->q_gdquot = NULL;
+ dqp->q_res_bcount = 0;
+ dqp->q_res_icount = 0;
+ dqp->q_res_rtbcount = 0;
+ dqp->q_pincount = 0;
+ dqp->q_hash = NULL;
+ ASSERT(dqp->dq_flnext == dqp->dq_flprev);
+
+#ifdef XFS_DQUOT_TRACE
+ ASSERT(dqp->q_trace);
+ xfs_dqtrace_entry(dqp, "DQRECLAIMED_INIT");
+#endif
+ }
+
+ /*
+ * log item gets initialized later
+ */
+ return (dqp);
+}
+
+/*
+ * This is called to free all the memory associated with a dquot
+ */
+void
+xfs_qm_dqdestroy(
+ xfs_dquot_t *dqp)
+{
+ ASSERT(! XFS_DQ_IS_ON_FREELIST(dqp));
+
+ mutex_destroy(&dqp->q_qlock);
+ freesema(&dqp->q_flock);
+ sv_destroy(&dqp->q_pinwait);
+
+#ifdef XFS_DQUOT_TRACE
+ if (dqp->q_trace)
+ ktrace_free(dqp->q_trace);
+ dqp->q_trace = NULL;
+#endif
+ kmem_zone_free(xfs_Gqm->qm_dqzone, dqp);
+ atomic_dec(&xfs_Gqm->qm_totaldquots);
+}
+
+/*
+ * This is what a 'fresh' dquot inside a dquot chunk looks like on disk.
+ */
+STATIC void
+xfs_qm_dqinit_core(
+ xfs_dqid_t id,
+ uint type,
+ xfs_dqblk_t *d)
+{
+ /*
+ * Caller has zero'd the entire dquot 'chunk' already.
+ */
+ INT_SET(d->dd_diskdq.d_magic, ARCH_CONVERT, XFS_DQUOT_MAGIC);
+ INT_SET(d->dd_diskdq.d_version, ARCH_CONVERT, XFS_DQUOT_VERSION);
+ INT_SET(d->dd_diskdq.d_id, ARCH_CONVERT, id);
+ INT_SET(d->dd_diskdq.d_flags, ARCH_CONVERT, type);
+}
+
+
+#ifdef XFS_DQUOT_TRACE
+/*
+ * Dquot tracing for debugging.
+ */
+/* ARGSUSED */
+void
+__xfs_dqtrace_entry(
+ xfs_dquot_t *dqp,
+ char *func,
+ void *retaddr,
+ xfs_inode_t *ip)
+{
+ xfs_dquot_t *udqp = NULL;
+ xfs_ino_t ino = 0;
+
+ ASSERT(dqp->q_trace);
+ if (ip) {
+ ino = ip->i_ino;
+ udqp = ip->i_udquot;
+ }
+ ktrace_enter(dqp->q_trace,
+ (void *)(__psint_t)DQUOT_KTRACE_ENTRY,
+ (void *)func,
+ (void *)(__psint_t)dqp->q_nrefs,
+ (void *)(__psint_t)dqp->dq_flags,
+ (void *)(__psint_t)dqp->q_res_bcount,
+ (void *)(__psint_t)INT_GET(dqp->q_core.d_bcount,
+ ARCH_CONVERT),
+ (void *)(__psint_t)INT_GET(dqp->q_core.d_icount,
+ ARCH_CONVERT),
+ (void *)(__psint_t)INT_GET(dqp->q_core.d_blk_hardlimit,
+ ARCH_CONVERT),
+ (void *)(__psint_t)INT_GET(dqp->q_core.d_blk_softlimit,
+ ARCH_CONVERT),
+ (void *)(__psint_t)INT_GET(dqp->q_core.d_ino_hardlimit,
+ ARCH_CONVERT),
+ (void *)(__psint_t)INT_GET(dqp->q_core.d_ino_softlimit,
+ ARCH_CONVERT),
+ (void *)(__psint_t)INT_GET(dqp->q_core.d_id, ARCH_CONVERT),
+ (void *)(__psint_t)current_pid(),
+ (void *)(__psint_t)ino,
+ (void *)(__psint_t)retaddr,
+ (void *)(__psint_t)udqp);
+ return;
+}
+#endif
+
+
+/*
+ * If default limits are in force, push them into the dquot now.
+ * We overwrite the dquot limits only if they are zero and this
+ * is not the root dquot.
+ */
+void
+xfs_qm_adjust_dqlimits(
+ xfs_mount_t *mp,
+ xfs_disk_dquot_t *d)
+{
+ xfs_quotainfo_t *q = mp->m_quotainfo;
+
+ ASSERT(!INT_ISZERO(d->d_id, ARCH_CONVERT));
+
+ if (q->qi_bsoftlimit && INT_ISZERO(d->d_blk_softlimit, ARCH_CONVERT))
+ INT_SET(d->d_blk_softlimit, ARCH_CONVERT, q->qi_bsoftlimit);
+ if (q->qi_bhardlimit && INT_ISZERO(d->d_blk_hardlimit, ARCH_CONVERT))
+ INT_SET(d->d_blk_hardlimit, ARCH_CONVERT, q->qi_bhardlimit);
+ if (q->qi_isoftlimit && INT_ISZERO(d->d_ino_softlimit, ARCH_CONVERT))
+ INT_SET(d->d_ino_softlimit, ARCH_CONVERT, q->qi_isoftlimit);
+ if (q->qi_ihardlimit && INT_ISZERO(d->d_ino_hardlimit, ARCH_CONVERT))
+ INT_SET(d->d_ino_hardlimit, ARCH_CONVERT, q->qi_ihardlimit);
+ if (q->qi_rtbsoftlimit &&
+ INT_ISZERO(d->d_rtb_softlimit, ARCH_CONVERT))
+ INT_SET(d->d_rtb_softlimit, ARCH_CONVERT, q->qi_rtbsoftlimit);
+ if (q->qi_rtbhardlimit &&
+ INT_ISZERO(d->d_rtb_hardlimit, ARCH_CONVERT))
+ INT_SET(d->d_rtb_hardlimit, ARCH_CONVERT, q->qi_rtbhardlimit);
+}
+
+/*
+ * Check the limits and timers of a dquot and start or reset timers
+ * if necessary.
+ * This gets called even when quota enforcement is OFF, which makes our
+ * life a little less complicated. (We just don't reject any quota
+ * reservations in that case, when enforcement is off).
+ * We also return 0 as the values of the timers in Q_GETQUOTA calls, when
+ * enforcement's off.
+ * In contrast, warnings are a little different in that they don't
+ * 'automatically' get started when limits get exceeded.
+ */
+void
+xfs_qm_adjust_dqtimers(
+ xfs_mount_t *mp,
+ xfs_disk_dquot_t *d)
+{
+ ASSERT(!INT_ISZERO(d->d_id, ARCH_CONVERT));
+
+#ifdef QUOTADEBUG
+ if (INT_GET(d->d_blk_hardlimit, ARCH_CONVERT))
+ ASSERT(INT_GET(d->d_blk_softlimit, ARCH_CONVERT) <=
+ INT_GET(d->d_blk_hardlimit, ARCH_CONVERT));
+ if (INT_GET(d->d_ino_hardlimit, ARCH_CONVERT))
+ ASSERT(INT_GET(d->d_ino_softlimit, ARCH_CONVERT) <=
+ INT_GET(d->d_ino_hardlimit, ARCH_CONVERT));
+ if (INT_GET(d->d_rtb_hardlimit, ARCH_CONVERT))
+ ASSERT(INT_GET(d->d_rtb_softlimit, ARCH_CONVERT) <=
+ INT_GET(d->d_rtb_hardlimit, ARCH_CONVERT));
+#endif
+ if (INT_ISZERO(d->d_btimer, ARCH_CONVERT)) {
+ if ((INT_GET(d->d_blk_softlimit, ARCH_CONVERT) &&
+ (INT_GET(d->d_bcount, ARCH_CONVERT) >=
+ INT_GET(d->d_blk_softlimit, ARCH_CONVERT))) ||
+ (INT_GET(d->d_blk_hardlimit, ARCH_CONVERT) &&
+ (INT_GET(d->d_bcount, ARCH_CONVERT) >=
+ INT_GET(d->d_blk_hardlimit, ARCH_CONVERT)))) {
+ INT_SET(d->d_btimer, ARCH_CONVERT,
+ get_seconds() + XFS_QI_BTIMELIMIT(mp));
+ }
+ } else {
+ if ((INT_ISZERO(d->d_blk_softlimit, ARCH_CONVERT) ||
+ (INT_GET(d->d_bcount, ARCH_CONVERT) <
+ INT_GET(d->d_blk_softlimit, ARCH_CONVERT))) &&
+ (INT_ISZERO(d->d_blk_hardlimit, ARCH_CONVERT) ||
+ (INT_GET(d->d_bcount, ARCH_CONVERT) <
+ INT_GET(d->d_blk_hardlimit, ARCH_CONVERT)))) {
+ INT_ZERO(d->d_btimer, ARCH_CONVERT);
+ }
+ }
+
+ if (INT_ISZERO(d->d_itimer, ARCH_CONVERT)) {
+ if ((INT_GET(d->d_ino_softlimit, ARCH_CONVERT) &&
+ (INT_GET(d->d_icount, ARCH_CONVERT) >=
+ INT_GET(d->d_ino_softlimit, ARCH_CONVERT))) ||
+ (INT_GET(d->d_ino_hardlimit, ARCH_CONVERT) &&
+ (INT_GET(d->d_icount, ARCH_CONVERT) >=
+ INT_GET(d->d_ino_hardlimit, ARCH_CONVERT)))) {
+ INT_SET(d->d_itimer, ARCH_CONVERT,
+ get_seconds() + XFS_QI_ITIMELIMIT(mp));
+ }
+ } else {
+ if ((INT_ISZERO(d->d_ino_softlimit, ARCH_CONVERT) ||
+ (INT_GET(d->d_icount, ARCH_CONVERT) <
+ INT_GET(d->d_ino_softlimit, ARCH_CONVERT))) &&
+ (INT_ISZERO(d->d_ino_hardlimit, ARCH_CONVERT) ||
+ (INT_GET(d->d_icount, ARCH_CONVERT) <
+ INT_GET(d->d_ino_hardlimit, ARCH_CONVERT)))) {
+ INT_ZERO(d->d_itimer, ARCH_CONVERT);
+ }
+ }
+
+ if (INT_ISZERO(d->d_rtbtimer, ARCH_CONVERT)) {
+ if ((INT_GET(d->d_rtb_softlimit, ARCH_CONVERT) &&
+ (INT_GET(d->d_rtbcount, ARCH_CONVERT) >=
+ INT_GET(d->d_rtb_softlimit, ARCH_CONVERT))) ||
+ (INT_GET(d->d_rtb_hardlimit, ARCH_CONVERT) &&
+ (INT_GET(d->d_rtbcount, ARCH_CONVERT) >=
+ INT_GET(d->d_rtb_hardlimit, ARCH_CONVERT)))) {
+ INT_SET(d->d_rtbtimer, ARCH_CONVERT,
+ get_seconds() + XFS_QI_RTBTIMELIMIT(mp));
+ }
+ } else {
+ if ((INT_ISZERO(d->d_rtb_softlimit, ARCH_CONVERT) ||
+ (INT_GET(d->d_rtbcount, ARCH_CONVERT) <
+ INT_GET(d->d_rtb_softlimit, ARCH_CONVERT))) &&
+ (INT_ISZERO(d->d_rtb_hardlimit, ARCH_CONVERT) ||
+ (INT_GET(d->d_rtbcount, ARCH_CONVERT) <
+ INT_GET(d->d_rtb_hardlimit, ARCH_CONVERT)))) {
+ INT_ZERO(d->d_rtbtimer, ARCH_CONVERT);
+ }
+ }
+}
+
+/*
+ * Increment or reset warnings of a given dquot.
+ */
+int
+xfs_qm_dqwarn(
+ xfs_disk_dquot_t *d,
+ uint flags)
+{
+ int warned;
+
+ /*
+ * root's limits are not real limits.
+ */
+ if (INT_ISZERO(d->d_id, ARCH_CONVERT))
+ return (0);
+
+ warned = 0;
+ if (INT_GET(d->d_blk_softlimit, ARCH_CONVERT) &&
+ (INT_GET(d->d_bcount, ARCH_CONVERT) >=
+ INT_GET(d->d_blk_softlimit, ARCH_CONVERT))) {
+ if (flags & XFS_QMOPT_DOWARN) {
+ INT_MOD(d->d_bwarns, ARCH_CONVERT, +1);
+ warned++;
+ }
+ } else {
+ if (INT_ISZERO(d->d_blk_softlimit, ARCH_CONVERT) ||
+ (INT_GET(d->d_bcount, ARCH_CONVERT) <
+ INT_GET(d->d_blk_softlimit, ARCH_CONVERT))) {
+ INT_ZERO(d->d_bwarns, ARCH_CONVERT);
+ }
+ }
+
+ if (INT_GET(d->d_ino_softlimit, ARCH_CONVERT) > 0 &&
+ (INT_GET(d->d_icount, ARCH_CONVERT) >=
+ INT_GET(d->d_ino_softlimit, ARCH_CONVERT))) {
+ if (flags & XFS_QMOPT_DOWARN) {
+ INT_MOD(d->d_iwarns, ARCH_CONVERT, +1);
+ warned++;
+ }
+ } else {
+ if ((INT_ISZERO(d->d_ino_softlimit, ARCH_CONVERT)) ||
+ (INT_GET(d->d_icount, ARCH_CONVERT) <
+ INT_GET(d->d_ino_softlimit, ARCH_CONVERT))) {
+ INT_ZERO(d->d_iwarns, ARCH_CONVERT);
+ }
+ }
+#ifdef QUOTADEBUG
+ if (INT_GET(d->d_iwarns, ARCH_CONVERT))
+ cmn_err(CE_DEBUG,
+ "--------@@Inode warnings running : %Lu >= %Lu",
+ INT_GET(d->d_icount, ARCH_CONVERT),
+ INT_GET(d->d_ino_softlimit, ARCH_CONVERT));
+ if (INT_GET(d->d_bwarns, ARCH_CONVERT))
+ cmn_err(CE_DEBUG,
+ "--------@@Blks warnings running : %Lu >= %Lu",
+ INT_GET(d->d_bcount, ARCH_CONVERT),
+ INT_GET(d->d_blk_softlimit, ARCH_CONVERT));
+#endif
+ return (warned);
+}
+
+
+/*
+ * initialize a buffer full of dquots and log the whole thing
+ */
+STATIC void
+xfs_qm_init_dquot_blk(
+ xfs_trans_t *tp,
+ xfs_mount_t *mp,
+ xfs_dqid_t id,
+ uint type,
+ xfs_buf_t *bp)
+{
+ xfs_dqblk_t *d;
+ int curid, i;
+
+ ASSERT(tp);
+ ASSERT(XFS_BUF_ISBUSY(bp));
+ ASSERT(XFS_BUF_VALUSEMA(bp) <= 0);
+
+ d = (xfs_dqblk_t *)XFS_BUF_PTR(bp);
+
+ /*
+ * ID of the first dquot in the block - id's are zero based.
+ */
+ curid = id - (id % XFS_QM_DQPERBLK(mp));
+ ASSERT(curid >= 0);
+ memset(d, 0, BBTOB(XFS_QI_DQCHUNKLEN(mp)));
+ for (i = 0; i < XFS_QM_DQPERBLK(mp); i++, d++, curid++)
+ xfs_qm_dqinit_core(curid, type, d);
+ xfs_trans_dquot_buf(tp, bp,
+ type & XFS_DQ_USER ?
+ XFS_BLI_UDQUOT_BUF :
+ XFS_BLI_GDQUOT_BUF);
+ xfs_trans_log_buf(tp, bp, 0, BBTOB(XFS_QI_DQCHUNKLEN(mp)) - 1);
+}
+
+
+
+/*
+ * Allocate a block and fill it with dquots.
+ * This is called when the bmapi finds a hole.
+ */
+STATIC int
+xfs_qm_dqalloc(
+ xfs_trans_t *tp,
+ xfs_mount_t *mp,
+ xfs_dquot_t *dqp,
+ xfs_inode_t *quotip,
+ xfs_fileoff_t offset_fsb,
+ xfs_buf_t **O_bpp)
+{
+ xfs_fsblock_t firstblock;
+ xfs_bmap_free_t flist;
+ xfs_bmbt_irec_t map;
+ int nmaps, error, committed;
+ xfs_buf_t *bp;
+
+ ASSERT(tp != NULL);
+ xfs_dqtrace_entry(dqp, "DQALLOC");
+
+ /*
+ * Initialize the bmap freelist prior to calling bmapi code.
+ */
+ XFS_BMAP_INIT(&flist, &firstblock);
+ xfs_ilock(quotip, XFS_ILOCK_EXCL);
+ /*
+ * Return if this type of quotas is turned off while we didn't
+ * have an inode lock
+ */
+ if (XFS_IS_THIS_QUOTA_OFF(dqp)) {
+ xfs_iunlock(quotip, XFS_ILOCK_EXCL);
+ return (ESRCH);
+ }
+
+ /*
+ * xfs_trans_commit normally decrements the vnode ref count
+ * when it unlocks the inode. Since we want to keep the quota
+ * inode around, we bump the vnode ref count now.
+ */
+ VN_HOLD(XFS_ITOV(quotip));
+
+ xfs_trans_ijoin(tp, quotip, XFS_ILOCK_EXCL);
+ nmaps = 1;
+ if ((error = xfs_bmapi(tp, quotip,
+ offset_fsb, XFS_DQUOT_CLUSTER_SIZE_FSB,
+ XFS_BMAPI_METADATA | XFS_BMAPI_WRITE,
+ &firstblock,
+ XFS_QM_DQALLOC_SPACE_RES(mp),
+ &map, &nmaps, &flist))) {
+ goto error0;
+ }
+ ASSERT(map.br_blockcount == XFS_DQUOT_CLUSTER_SIZE_FSB);
+ ASSERT(nmaps == 1);
+ ASSERT((map.br_startblock != DELAYSTARTBLOCK) &&
+ (map.br_startblock != HOLESTARTBLOCK));
+
+ /*
+ * Keep track of the blkno to save a lookup later
+ */
+ dqp->q_blkno = XFS_FSB_TO_DADDR(mp, map.br_startblock);
+
+ /* now we can just get the buffer (there's nothing to read yet) */
+ bp = xfs_trans_get_buf(tp, mp->m_ddev_targp,
+ dqp->q_blkno,
+ XFS_QI_DQCHUNKLEN(mp),
+ 0);
+ if (!bp || (error = XFS_BUF_GETERROR(bp)))
+ goto error1;
+ /*
+ * Make a chunk of dquots out of this buffer and log
+ * the entire thing.
+ */
+ xfs_qm_init_dquot_blk(tp, mp, INT_GET(dqp->q_core.d_id, ARCH_CONVERT),
+ dqp->dq_flags & (XFS_DQ_USER|XFS_DQ_GROUP),
+ bp);
+
+ if ((error = xfs_bmap_finish(&tp, &flist, firstblock, &committed))) {
+ goto error1;
+ }
+
+ *O_bpp = bp;
+ return 0;
+
+ error1:
+ xfs_bmap_cancel(&flist);
+ error0:
+ xfs_iunlock(quotip, XFS_ILOCK_EXCL);
+
+ return (error);
+}
+
+/*
+ * Maps a dquot to the buffer containing its on-disk version.
+ * This returns a ptr to the buffer containing the on-disk dquot
+ * in the bpp param, and a ptr to the on-disk dquot within that buffer
+ */
+STATIC int
+xfs_qm_dqtobp(
+ xfs_trans_t *tp,
+ xfs_dquot_t *dqp,
+ xfs_disk_dquot_t **O_ddpp,
+ xfs_buf_t **O_bpp,
+ uint flags)
+{
+ xfs_bmbt_irec_t map;
+ int nmaps, error;
+ xfs_buf_t *bp;
+ xfs_inode_t *quotip;
+ xfs_mount_t *mp;
+ xfs_disk_dquot_t *ddq;
+ xfs_dqid_t id;
+ boolean_t newdquot;
+
+ mp = dqp->q_mount;
+ id = INT_GET(dqp->q_core.d_id, ARCH_CONVERT);
+ nmaps = 1;
+ newdquot = B_FALSE;
+
+ /*
+ * If we don't know where the dquot lives, find out.
+ */
+ if (dqp->q_blkno == (xfs_daddr_t) 0) {
+ /* We use the id as an index */
+ dqp->q_fileoffset = (xfs_fileoff_t) ((uint)id /
+ XFS_QM_DQPERBLK(mp));
+ nmaps = 1;
+ quotip = XFS_DQ_TO_QIP(dqp);
+ xfs_ilock(quotip, XFS_ILOCK_SHARED);
+ /*
+ * Return if this type of quotas is turned off while we didn't
+ * have an inode lock
+ */
+ if (XFS_IS_THIS_QUOTA_OFF(dqp)) {
+ xfs_iunlock(quotip, XFS_ILOCK_SHARED);
+ return (ESRCH);
+ }
+ /*
+ * Find the block map; no allocations yet
+ */
+ error = xfs_bmapi(NULL, quotip, dqp->q_fileoffset,
+ XFS_DQUOT_CLUSTER_SIZE_FSB,
+ XFS_BMAPI_METADATA,
+ NULL, 0, &map, &nmaps, NULL);
+
+ xfs_iunlock(quotip, XFS_ILOCK_SHARED);
+ if (error)
+ return (error);
+ ASSERT(nmaps == 1);
+ ASSERT(map.br_blockcount == 1);
+
+ /*
+ * offset of dquot in the (fixed sized) dquot chunk.
+ */
+ dqp->q_bufoffset = (id % XFS_QM_DQPERBLK(mp)) *
+ sizeof(xfs_dqblk_t);
+ if (map.br_startblock == HOLESTARTBLOCK) {
+ /*
+ * We don't allocate unless we're asked to
+ */
+ if (!(flags & XFS_QMOPT_DQALLOC))
+ return (ENOENT);
+
+ ASSERT(tp);
+ if ((error = xfs_qm_dqalloc(tp, mp, dqp, quotip,
+ dqp->q_fileoffset, &bp)))
+ return (error);
+ newdquot = B_TRUE;
+ } else {
+ /*
+ * store the blkno etc so that we don't have to do the
+ * mapping all the time
+ */
+ dqp->q_blkno = XFS_FSB_TO_DADDR(mp, map.br_startblock);
+ }
+ }
+ ASSERT(dqp->q_blkno != DELAYSTARTBLOCK);
+ ASSERT(dqp->q_blkno != HOLESTARTBLOCK);
+
+ /*
+ * Read in the buffer, unless we've just done the allocation
+ * (in which case we already have the buf).
+ */
+ if (! newdquot) {
+ xfs_dqtrace_entry(dqp, "DQTOBP READBUF");
+ if ((error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp,
+ dqp->q_blkno,
+ XFS_QI_DQCHUNKLEN(mp),
+ 0, &bp))) {
+ return (error);
+ }
+ if (error || !bp)
+ return XFS_ERROR(error);
+ }
+ ASSERT(XFS_BUF_ISBUSY(bp));
+ ASSERT(XFS_BUF_VALUSEMA(bp) <= 0);
+
+ /*
+ * calculate the location of the dquot inside the buffer.
+ */
+ ddq = (xfs_disk_dquot_t *)((char *)XFS_BUF_PTR(bp) + dqp->q_bufoffset);
+
+ /*
+ * A simple sanity check in case we got a corrupted dquot...
+ */
+ if (xfs_qm_dqcheck(ddq, id,
+ dqp->dq_flags & (XFS_DQ_USER|XFS_DQ_GROUP),
+ flags & (XFS_QMOPT_DQREPAIR|XFS_QMOPT_DOWARN),
+ "dqtobp")) {
+ if (!(flags & XFS_QMOPT_DQREPAIR)) {
+ xfs_trans_brelse(tp, bp);
+ return XFS_ERROR(EIO);
+ }
+ XFS_BUF_BUSY(bp); /* We dirtied this */
+ }
+
+ *O_bpp = bp;
+ *O_ddpp = ddq;
+
+ return (0);
+}
+
+
+/*
+ * Read in the ondisk dquot using dqtobp() then copy it to an incore version,
+ * and release the buffer immediately.
+ *
+ */
+/* ARGSUSED */
+STATIC int
+xfs_qm_dqread(
+ xfs_trans_t *tp,
+ xfs_dqid_t id,
+ xfs_dquot_t *dqp, /* dquot to get filled in */
+ uint flags)
+{
+ xfs_disk_dquot_t *ddqp;
+ xfs_buf_t *bp;
+ int error;
+
+ /*
+ * get a pointer to the on-disk dquot and the buffer containing it
+ * dqp already knows its own type (GROUP/USER).
+ */
+ xfs_dqtrace_entry(dqp, "DQREAD");
+ if ((error = xfs_qm_dqtobp(tp, dqp, &ddqp, &bp, flags))) {
+ return (error);
+ }
+
+ /* copy everything from disk dquot to the incore dquot */
+ memcpy(&dqp->q_core, ddqp, sizeof(xfs_disk_dquot_t));
+ ASSERT(INT_GET(dqp->q_core.d_id, ARCH_CONVERT) == id);
+ xfs_qm_dquot_logitem_init(dqp);
+
+ /*
+ * Reservation counters are defined as reservation plus current usage
+ * to avoid having to add everytime.
+ */
+ dqp->q_res_bcount = INT_GET(ddqp->d_bcount, ARCH_CONVERT);
+ dqp->q_res_icount = INT_GET(ddqp->d_icount, ARCH_CONVERT);
+ dqp->q_res_rtbcount = INT_GET(ddqp->d_rtbcount, ARCH_CONVERT);
+
+ /* Mark the buf so that this will stay incore a little longer */
+ XFS_BUF_SET_VTYPE_REF(bp, B_FS_DQUOT, XFS_DQUOT_REF);
+
+ /*
+ * We got the buffer with a xfs_trans_read_buf() (in dqtobp())
+ * So we need to release with xfs_trans_brelse().
+ * The strategy here is identical to that of inodes; we lock
+ * the dquot in xfs_qm_dqget() before making it accessible to
+ * others. This is because dquots, like inodes, need a good level of
+ * concurrency, and we don't want to take locks on the entire buffers
+ * for dquot accesses.
+ * Note also that the dquot buffer may even be dirty at this point, if
+ * this particular dquot was repaired. We still aren't afraid to
+ * brelse it because we have the changes incore.
+ */
+ ASSERT(XFS_BUF_ISBUSY(bp));
+ ASSERT(XFS_BUF_VALUSEMA(bp) <= 0);
+ xfs_trans_brelse(tp, bp);
+
+ return (error);
+}
+
+
+/*
+ * allocate an incore dquot from the kernel heap,
+ * and fill its core with quota information kept on disk.
+ * If XFS_QMOPT_DQALLOC is set, it'll allocate a dquot on disk
+ * if it wasn't already allocated.
+ */
+STATIC int
+xfs_qm_idtodq(
+ xfs_mount_t *mp,
+ xfs_dqid_t id, /* gid or uid, depending on type */
+ uint type, /* UDQUOT or GDQUOT */
+ uint flags, /* DQALLOC, DQREPAIR */
+ xfs_dquot_t **O_dqpp)/* OUT : incore dquot, not locked */
+{
+ xfs_dquot_t *dqp;
+ int error;
+ xfs_trans_t *tp;
+ int cancelflags=0;
+
+ dqp = xfs_qm_dqinit(mp, id, type);
+ tp = NULL;
+ if (flags & XFS_QMOPT_DQALLOC) {
+ tp = xfs_trans_alloc(mp, XFS_TRANS_QM_DQALLOC);
+ if ((error = xfs_trans_reserve(tp,
+ XFS_QM_DQALLOC_SPACE_RES(mp),
+ XFS_WRITE_LOG_RES(mp) +
+ BBTOB(XFS_QI_DQCHUNKLEN(mp)) - 1 +
+ 128,
+ 0,
+ XFS_TRANS_PERM_LOG_RES,
+ XFS_WRITE_LOG_COUNT))) {
+ cancelflags = 0;
+ goto error0;
+ }
+ cancelflags = XFS_TRANS_RELEASE_LOG_RES;
+ }
+
+ /*
+ * Read it from disk; xfs_dqread() takes care of
+ * all the necessary initialization of dquot's fields (locks, etc)
+ */
+ if ((error = xfs_qm_dqread(tp, id, dqp, flags))) {
+ /*
+ * This can happen if quotas got turned off (ESRCH),
+ * or if the dquot didn't exist on disk and we ask to
+ * allocate (ENOENT).
+ */
+ xfs_dqtrace_entry(dqp, "DQREAD FAIL");
+ cancelflags |= XFS_TRANS_ABORT;
+ goto error0;
+ }
+ if (tp) {
+ if ((error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES,
+ NULL)))
+ goto error1;
+ }
+
+ *O_dqpp = dqp;
+ return (0);
+
+ error0:
+ ASSERT(error);
+ if (tp)
+ xfs_trans_cancel(tp, cancelflags);
+ error1:
+ xfs_qm_dqdestroy(dqp);
+ *O_dqpp = NULL;
+ return (error);
+}
+
+/*
+ * Lookup a dquot in the incore dquot hashtable. We keep two separate
+ * hashtables for user and group dquots; and, these are global tables
+ * inside the XQM, not per-filesystem tables.
+ * The hash chain must be locked by caller, and it is left locked
+ * on return. Returning dquot is locked.
+ */
+STATIC int
+xfs_qm_dqlookup(
+ xfs_mount_t *mp,
+ xfs_dqid_t id,
+ xfs_dqhash_t *qh,
+ xfs_dquot_t **O_dqpp)
+{
+ xfs_dquot_t *dqp;
+ uint flist_locked;
+ xfs_dquot_t *d;
+
+ ASSERT(XFS_DQ_IS_HASH_LOCKED(qh));
+
+ flist_locked = B_FALSE;
+
+ /*
+ * Traverse the hashchain looking for a match
+ */
+ for (dqp = qh->qh_next; dqp != NULL; dqp = dqp->HL_NEXT) {
+ /*
+ * We already have the hashlock. We don't need the
+ * dqlock to look at the id field of the dquot, since the
+ * id can't be modified without the hashlock anyway.
+ */
+ if (INT_GET(dqp->q_core.d_id, ARCH_CONVERT) == id && dqp->q_mount == mp) {
+ xfs_dqtrace_entry(dqp, "DQFOUND BY LOOKUP");
+ /*
+ * All in core dquots must be on the dqlist of mp
+ */
+ ASSERT(dqp->MPL_PREVP != NULL);
+
+ xfs_dqlock(dqp);
+ if (dqp->q_nrefs == 0) {
+ ASSERT (XFS_DQ_IS_ON_FREELIST(dqp));
+ if (! xfs_qm_freelist_lock_nowait(xfs_Gqm)) {
+ xfs_dqtrace_entry(dqp, "DQLOOKUP: WANT");
+
+ /*
+ * We may have raced with dqreclaim_one()
+ * (and lost). So, flag that we don't
+ * want the dquot to be reclaimed.
+ */
+ dqp->dq_flags |= XFS_DQ_WANT;
+ xfs_dqunlock(dqp);
+ xfs_qm_freelist_lock(xfs_Gqm);
+ xfs_dqlock(dqp);
+ dqp->dq_flags &= ~(XFS_DQ_WANT);
+ }
+ flist_locked = B_TRUE;
+ }
+
+ /*
+ * id couldn't have changed; we had the hashlock all
+ * along
+ */
+ ASSERT(INT_GET(dqp->q_core.d_id, ARCH_CONVERT) == id);
+
+ if (flist_locked) {
+ if (dqp->q_nrefs != 0) {
+ xfs_qm_freelist_unlock(xfs_Gqm);
+ flist_locked = B_FALSE;
+ } else {
+ /*
+ * take it off the freelist
+ */
+ xfs_dqtrace_entry(dqp,
+ "DQLOOKUP: TAKEOFF FL");
+ XQM_FREELIST_REMOVE(dqp);
+ /* xfs_qm_freelist_print(&(xfs_Gqm->
+ qm_dqfreelist),
+ "after removal"); */
+ }
+ }
+
+ /*
+ * grab a reference
+ */
+ XFS_DQHOLD(dqp);
+
+ if (flist_locked)
+ xfs_qm_freelist_unlock(xfs_Gqm);
+ /*
+ * move the dquot to the front of the hashchain
+ */
+ ASSERT(XFS_DQ_IS_HASH_LOCKED(qh));
+ if (dqp->HL_PREVP != &qh->qh_next) {
+ xfs_dqtrace_entry(dqp,
+ "DQLOOKUP: HASH MOVETOFRONT");
+ if ((d = dqp->HL_NEXT))
+ d->HL_PREVP = dqp->HL_PREVP;
+ *(dqp->HL_PREVP) = d;
+ d = qh->qh_next;
+ d->HL_PREVP = &dqp->HL_NEXT;
+ dqp->HL_NEXT = d;
+ dqp->HL_PREVP = &qh->qh_next;
+ qh->qh_next = dqp;
+ }
+ xfs_dqtrace_entry(dqp, "LOOKUP END");
+ *O_dqpp = dqp;
+ ASSERT(XFS_DQ_IS_HASH_LOCKED(qh));
+ return (0);
+ }
+ }
+
+ *O_dqpp = NULL;
+ ASSERT(XFS_DQ_IS_HASH_LOCKED(qh));
+ return (1);
+}
+
+/*
+ * Given the file system, inode OR id, and type (UDQUOT/GDQUOT), return a
+ * a locked dquot, doing an allocation (if requested) as needed.
+ * When both an inode and an id are given, the inode's id takes precedence.
+ * That is, if the id changes while we don't hold the ilock inside this
+ * function, the new dquot is returned, not necessarily the one requested
+ * in the id argument.
+ */
+int
+xfs_qm_dqget(
+ xfs_mount_t *mp,
+ xfs_inode_t *ip, /* locked inode (optional) */
+ xfs_dqid_t id, /* gid or uid, depending on type */
+ uint type, /* UDQUOT or GDQUOT */
+ uint flags, /* DQALLOC, DQSUSER, DQREPAIR, DOWARN */
+ xfs_dquot_t **O_dqpp) /* OUT : locked incore dquot */
+{
+ xfs_dquot_t *dqp;
+ xfs_dqhash_t *h;
+ uint version;
+ int error;
+
+ ASSERT(XFS_IS_QUOTA_RUNNING(mp));
+ if ((! XFS_IS_UQUOTA_ON(mp) && type == XFS_DQ_USER) ||
+ (! XFS_IS_GQUOTA_ON(mp) && type == XFS_DQ_GROUP)) {
+ return (ESRCH);
+ }
+ h = XFS_DQ_HASH(mp, id, type);
+
+#ifdef DEBUG
+ if (xfs_do_dqerror) {
+ if ((xfs_dqerror_target == mp->m_ddev_targp) &&
+ (xfs_dqreq_num++ % xfs_dqerror_mod) == 0) {
+ cmn_err(CE_DEBUG, "Returning error in dqget");
+ return (EIO);
+ }
+ }
+#endif
+
+ again:
+
+#ifdef DEBUG
+ ASSERT(type == XFS_DQ_USER || type == XFS_DQ_GROUP);
+ if (ip) {
+ ASSERT(XFS_ISLOCKED_INODE_EXCL(ip));
+ if (type == XFS_DQ_USER)
+ ASSERT(ip->i_udquot == NULL);
+ else
+ ASSERT(ip->i_gdquot == NULL);
+ }
+#endif
+ XFS_DQ_HASH_LOCK(h);
+
+ /*
+ * Look in the cache (hashtable).
+ * The chain is kept locked during lookup.
+ */
+ if (xfs_qm_dqlookup(mp, id, h, O_dqpp) == 0) {
+ XQM_STATS_INC(xqmstats.xs_qm_dqcachehits);
+ /*
+ * The dquot was found, moved to the front of the chain,
+ * taken off the freelist if it was on it, and locked
+ * at this point. Just unlock the hashchain and return.
+ */
+ ASSERT(*O_dqpp);
+ ASSERT(XFS_DQ_IS_LOCKED(*O_dqpp));
+ XFS_DQ_HASH_UNLOCK(h);
+ xfs_dqtrace_entry(*O_dqpp, "DQGET DONE (FROM CACHE)");
+ return (0); /* success */
+ }
+ XQM_STATS_INC(xqmstats.xs_qm_dqcachemisses);
+
+ /*
+ * Dquot cache miss. We don't want to keep the inode lock across
+ * a (potential) disk read. Also we don't want to deal with the lock
+ * ordering between quotainode and this inode. OTOH, dropping the inode
+ * lock here means dealing with a chown that can happen before
+ * we re-acquire the lock.
+ */
+ if (ip)
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ /*
+ * Save the hashchain version stamp, and unlock the chain, so that
+ * we don't keep the lock across a disk read
+ */
+ version = h->qh_version;
+ XFS_DQ_HASH_UNLOCK(h);
+
+ /*
+ * Allocate the dquot on the kernel heap, and read the ondisk
+ * portion off the disk. Also, do all the necessary initialization
+ * This can return ENOENT if dquot didn't exist on disk and we didn't
+ * ask it to allocate; ESRCH if quotas got turned off suddenly.
+ */
+ if ((error = xfs_qm_idtodq(mp, id, type,
+ flags & (XFS_QMOPT_DQALLOC|XFS_QMOPT_DQREPAIR|
+ XFS_QMOPT_DOWARN),
+ &dqp))) {
+ if (ip)
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
+ return (error);
+ }
+
+ /*
+ * See if this is mount code calling to look at the overall quota limits
+ * which are stored in the id == 0 user or group's dquot.
+ * Since we may not have done a quotacheck by this point, just return
+ * the dquot without attaching it to any hashtables, lists, etc, or even
+ * taking a reference.
+ * The caller must dqdestroy this once done.
+ */
+ if (flags & XFS_QMOPT_DQSUSER) {
+ ASSERT(id == 0);
+ ASSERT(! ip);
+ goto dqret;
+ }
+
+ /*
+ * Dquot lock comes after hashlock in the lock ordering
+ */
+ if (ip) {
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
+ if (! XFS_IS_DQTYPE_ON(mp, type)) {
+ /* inode stays locked on return */
+ xfs_qm_dqdestroy(dqp);
+ return XFS_ERROR(ESRCH);
+ }
+ /*
+ * A dquot could be attached to this inode by now, since
+ * we had dropped the ilock.
+ */
+ if (type == XFS_DQ_USER) {
+ if (ip->i_udquot) {
+ xfs_qm_dqdestroy(dqp);
+ dqp = ip->i_udquot;
+ xfs_dqlock(dqp);
+ goto dqret;
+ }
+ } else {
+ if (ip->i_gdquot) {
+ xfs_qm_dqdestroy(dqp);
+ dqp = ip->i_gdquot;
+ xfs_dqlock(dqp);
+ goto dqret;
+ }
+ }
+ }
+
+ /*
+ * Hashlock comes after ilock in lock order
+ */
+ XFS_DQ_HASH_LOCK(h);
+ if (version != h->qh_version) {
+ xfs_dquot_t *tmpdqp;
+ /*
+ * Now, see if somebody else put the dquot in the
+ * hashtable before us. This can happen because we didn't
+ * keep the hashchain lock. We don't have to worry about
+ * lock order between the two dquots here since dqp isn't
+ * on any findable lists yet.
+ */
+ if (xfs_qm_dqlookup(mp, id, h, &tmpdqp) == 0) {
+ /*
+ * Duplicate found. Just throw away the new dquot
+ * and start over.
+ */
+ xfs_qm_dqput(tmpdqp);
+ XFS_DQ_HASH_UNLOCK(h);
+ xfs_qm_dqdestroy(dqp);
+ XQM_STATS_INC(xqmstats.xs_qm_dquot_dups);
+ goto again;
+ }
+ }
+
+ /*
+ * Put the dquot at the beginning of the hash-chain and mp's list
+ * LOCK ORDER: hashlock, freelistlock, mplistlock, udqlock, gdqlock ..
+ */
+ ASSERT(XFS_DQ_IS_HASH_LOCKED(h));
+ dqp->q_hash = h;
+ XQM_HASHLIST_INSERT(h, dqp);
+
+ /*
+ * Attach this dquot to this filesystem's list of all dquots,
+ * kept inside the mount structure in m_quotainfo field
+ */
+ xfs_qm_mplist_lock(mp);
+
+ /*
+ * We return a locked dquot to the caller, with a reference taken
+ */
+ xfs_dqlock(dqp);
+ dqp->q_nrefs = 1;
+
+ XQM_MPLIST_INSERT(&(XFS_QI_MPL_LIST(mp)), dqp);
+
+ xfs_qm_mplist_unlock(mp);
+ XFS_DQ_HASH_UNLOCK(h);
+ dqret:
+ ASSERT((ip == NULL) || XFS_ISLOCKED_INODE_EXCL(ip));
+ xfs_dqtrace_entry(dqp, "DQGET DONE");
+ *O_dqpp = dqp;
+ return (0);
+}
+
+
+/*
+ * Release a reference to the dquot (decrement ref-count)
+ * and unlock it. If there is a group quota attached to this
+ * dquot, carefully release that too without tripping over
+ * deadlocks'n'stuff.
+ */
+void
+xfs_qm_dqput(
+ xfs_dquot_t *dqp)
+{
+ xfs_dquot_t *gdqp;
+
+ ASSERT(dqp->q_nrefs > 0);
+ ASSERT(XFS_DQ_IS_LOCKED(dqp));
+ xfs_dqtrace_entry(dqp, "DQPUT");
+
+ if (dqp->q_nrefs != 1) {
+ dqp->q_nrefs--;
+ xfs_dqunlock(dqp);
+ return;
+ }
+
+ /*
+ * drop the dqlock and acquire the freelist and dqlock
+ * in the right order; but try to get it out-of-order first
+ */
+ if (! xfs_qm_freelist_lock_nowait(xfs_Gqm)) {
+ xfs_dqtrace_entry(dqp, "DQPUT: FLLOCK-WAIT");
+ xfs_dqunlock(dqp);
+ xfs_qm_freelist_lock(xfs_Gqm);
+ xfs_dqlock(dqp);
+ }
+
+ while (1) {
+ gdqp = NULL;
+
+ /* We can't depend on nrefs being == 1 here */
+ if (--dqp->q_nrefs == 0) {
+ xfs_dqtrace_entry(dqp, "DQPUT: ON FREELIST");
+ /*
+ * insert at end of the freelist.
+ */
+ XQM_FREELIST_INSERT(&(xfs_Gqm->qm_dqfreelist), dqp);
+
+ /*
+ * If we just added a udquot to the freelist, then
+ * we want to release the gdquot reference that
+ * it (probably) has. Otherwise it'll keep the
+ * gdquot from getting reclaimed.
+ */
+ if ((gdqp = dqp->q_gdquot)) {
+ /*
+ * Avoid a recursive dqput call
+ */
+ xfs_dqlock(gdqp);
+ dqp->q_gdquot = NULL;
+ }
+
+ /* xfs_qm_freelist_print(&(xfs_Gqm->qm_dqfreelist),
+ "@@@@@++ Free list (after append) @@@@@+");
+ */
+ }
+ xfs_dqunlock(dqp);
+
+ /*
+ * If we had a group quota inside the user quota as a hint,
+ * release it now.
+ */
+ if (! gdqp)
+ break;
+ dqp = gdqp;
+ }
+ xfs_qm_freelist_unlock(xfs_Gqm);
+}
+
+/*
+ * Release a dquot. Flush it if dirty, then dqput() it.
+ * dquot must not be locked.
+ */
+void
+xfs_qm_dqrele(
+ xfs_dquot_t *dqp)
+{
+ ASSERT(dqp);
+ xfs_dqtrace_entry(dqp, "DQRELE");
+
+ xfs_dqlock(dqp);
+ /*
+ * We don't care to flush it if the dquot is dirty here.
+ * That will create stutters that we want to avoid.
+ * Instead we do a delayed write when we try to reclaim
+ * a dirty dquot. Also xfs_sync will take part of the burden...
+ */
+ xfs_qm_dqput(dqp);
+}
+
+
+/*
+ * Write a modified dquot to disk.
+ * The dquot must be locked and the flush lock too taken by caller.
+ * The flush lock will not be unlocked until the dquot reaches the disk,
+ * but the dquot is free to be unlocked and modified by the caller
+ * in the interim. Dquot is still locked on return. This behavior is
+ * identical to that of inodes.
+ */
+int
+xfs_qm_dqflush(
+ xfs_dquot_t *dqp,
+ uint flags)
+{
+ xfs_mount_t *mp;
+ xfs_buf_t *bp;
+ xfs_disk_dquot_t *ddqp;
+ int error;
+ SPLDECL(s);
+
+ ASSERT(XFS_DQ_IS_LOCKED(dqp));
+ ASSERT(XFS_DQ_IS_FLUSH_LOCKED(dqp));
+ xfs_dqtrace_entry(dqp, "DQFLUSH");
+
+ /*
+ * If not dirty, nada.
+ */
+ if (!XFS_DQ_IS_DIRTY(dqp)) {
+ xfs_dqfunlock(dqp);
+ return (0);
+ }
+
+ /*
+ * Cant flush a pinned dquot. Wait for it.
+ */
+ xfs_qm_dqunpin_wait(dqp);
+
+ /*
+ * This may have been unpinned because the filesystem is shutting
+ * down forcibly. If that's the case we must not write this dquot
+ * to disk, because the log record didn't make it to disk!
+ */
+ if (XFS_FORCED_SHUTDOWN(dqp->q_mount)) {
+ dqp->dq_flags &= ~(XFS_DQ_DIRTY);
+ xfs_dqfunlock(dqp);
+ return XFS_ERROR(EIO);
+ }
+
+ /*
+ * Get the buffer containing the on-disk dquot
+ * We don't need a transaction envelope because we know that the
+ * the ondisk-dquot has already been allocated for.
+ */
+ if ((error = xfs_qm_dqtobp(NULL, dqp, &ddqp, &bp, XFS_QMOPT_DOWARN))) {
+ xfs_dqtrace_entry(dqp, "DQTOBP FAIL");
+ ASSERT(error != ENOENT);
+ /*
+ * Quotas could have gotten turned off (ESRCH)
+ */
+ xfs_dqfunlock(dqp);
+ return (error);
+ }
+
+ if (xfs_qm_dqcheck(&dqp->q_core, INT_GET(ddqp->d_id, ARCH_CONVERT), 0, XFS_QMOPT_DOWARN,
+ "dqflush (incore copy)")) {
+ xfs_force_shutdown(dqp->q_mount, XFS_CORRUPT_INCORE);
+ return XFS_ERROR(EIO);
+ }
+
+ /* This is the only portion of data that needs to persist */
+ memcpy(ddqp, &(dqp->q_core), sizeof(xfs_disk_dquot_t));
+
+ /*
+ * Clear the dirty field and remember the flush lsn for later use.
+ */
+ dqp->dq_flags &= ~(XFS_DQ_DIRTY);
+ mp = dqp->q_mount;
+
+ /* lsn is 64 bits */
+ AIL_LOCK(mp, s);
+ dqp->q_logitem.qli_flush_lsn = dqp->q_logitem.qli_item.li_lsn;
+ AIL_UNLOCK(mp, s);
+
+ /*
+ * Attach an iodone routine so that we can remove this dquot from the
+ * AIL and release the flush lock once the dquot is synced to disk.
+ */
+ xfs_buf_attach_iodone(bp, (void(*)(xfs_buf_t *, xfs_log_item_t *))
+ xfs_qm_dqflush_done, &(dqp->q_logitem.qli_item));
+ /*
+ * If the buffer is pinned then push on the log so we won't
+ * get stuck waiting in the write for too long.
+ */
+ if (XFS_BUF_ISPINNED(bp)) {
+ xfs_dqtrace_entry(dqp, "DQFLUSH LOG FORCE");
+ xfs_log_force(mp, (xfs_lsn_t)0, XFS_LOG_FORCE);
+ }
+
+ if (flags & XFS_QMOPT_DELWRI) {
+ xfs_bdwrite(mp, bp);
+ } else if (flags & XFS_QMOPT_ASYNC) {
+ xfs_bawrite(mp, bp);
+ } else {
+ error = xfs_bwrite(mp, bp);
+ }
+ xfs_dqtrace_entry(dqp, "DQFLUSH END");
+ /*
+ * dqp is still locked, but caller is free to unlock it now.
+ */
+ return (error);
+
+}
+
+/*
+ * This is the dquot flushing I/O completion routine. It is called
+ * from interrupt level when the buffer containing the dquot is
+ * flushed to disk. It is responsible for removing the dquot logitem
+ * from the AIL if it has not been re-logged, and unlocking the dquot's
+ * flush lock. This behavior is very similar to that of inodes..
+ */
+/*ARGSUSED*/
+STATIC void
+xfs_qm_dqflush_done(
+ xfs_buf_t *bp,
+ xfs_dq_logitem_t *qip)
+{
+ xfs_dquot_t *dqp;
+ SPLDECL(s);
+
+ dqp = qip->qli_dquot;
+
+ /*
+ * We only want to pull the item from the AIL if its
+ * location in the log has not changed since we started the flush.
+ * Thus, we only bother if the dquot's lsn has
+ * not changed. First we check the lsn outside the lock
+ * since it's cheaper, and then we recheck while
+ * holding the lock before removing the dquot from the AIL.
+ */
+ if ((qip->qli_item.li_flags & XFS_LI_IN_AIL) &&
+ qip->qli_item.li_lsn == qip->qli_flush_lsn) {
+
+ AIL_LOCK(dqp->q_mount, s);
+ /*
+ * xfs_trans_delete_ail() drops the AIL lock.
+ */
+ if (qip->qli_item.li_lsn == qip->qli_flush_lsn)
+ xfs_trans_delete_ail(dqp->q_mount,
+ (xfs_log_item_t*)qip, s);
+ else
+ AIL_UNLOCK(dqp->q_mount, s);
+ }
+
+ /*
+ * Release the dq's flush lock since we're done with it.
+ */
+ xfs_dqfunlock(dqp);
+}
+
+
+int
+xfs_qm_dqflock_nowait(
+ xfs_dquot_t *dqp)
+{
+ int locked;
+
+ locked = cpsema(&((dqp)->q_flock));
+
+ /* XXX ifdef these out */
+ if (locked)
+ (dqp)->dq_flags |= XFS_DQ_FLOCKED;
+ return (locked);
+}
+
+
+int
+xfs_qm_dqlock_nowait(
+ xfs_dquot_t *dqp)
+{
+ return (mutex_trylock(&((dqp)->q_qlock)));
+}
+
+void
+xfs_dqlock(
+ xfs_dquot_t *dqp)
+{
+ mutex_lock(&(dqp->q_qlock), PINOD);
+}
+
+void
+xfs_dqunlock(
+ xfs_dquot_t *dqp)
+{
+ mutex_unlock(&(dqp->q_qlock));
+ if (dqp->q_logitem.qli_dquot == dqp) {
+ /* Once was dqp->q_mount, but might just have been cleared */
+ xfs_trans_unlocked_item(dqp->q_logitem.qli_item.li_mountp,
+ (xfs_log_item_t*)&(dqp->q_logitem));
+ }
+}
+
+
+void
+xfs_dqunlock_nonotify(
+ xfs_dquot_t *dqp)
+{
+ mutex_unlock(&(dqp->q_qlock));
+}
+
+void
+xfs_dqlock2(
+ xfs_dquot_t *d1,
+ xfs_dquot_t *d2)
+{
+ if (d1 && d2) {
+ ASSERT(d1 != d2);
+ if (INT_GET(d1->q_core.d_id, ARCH_CONVERT) > INT_GET(d2->q_core.d_id, ARCH_CONVERT)) {
+ xfs_dqlock(d2);
+ xfs_dqlock(d1);
+ } else {
+ xfs_dqlock(d1);
+ xfs_dqlock(d2);
+ }
+ } else {
+ if (d1) {
+ xfs_dqlock(d1);
+ } else if (d2) {
+ xfs_dqlock(d2);
+ }
+ }
+}
+
+
+/*
+ * Take a dquot out of the mount's dqlist as well as the hashlist.
+ * This is called via unmount as well as quotaoff, and the purge
+ * will always succeed unless there are soft (temp) references
+ * outstanding.
+ *
+ * This returns 0 if it was purged, 1 if it wasn't. It's not an error code
+ * that we're returning! XXXsup - not cool.
+ */
+/* ARGSUSED */
+int
+xfs_qm_dqpurge(
+ xfs_dquot_t *dqp,
+ uint flags)
+{
+ xfs_dqhash_t *thishash;
+ xfs_mount_t *mp;
+
+ mp = dqp->q_mount;
+
+ ASSERT(XFS_QM_IS_MPLIST_LOCKED(mp));
+ ASSERT(XFS_DQ_IS_HASH_LOCKED(dqp->q_hash));
+
+ xfs_dqlock(dqp);
+ /*
+ * We really can't afford to purge a dquot that is
+ * referenced, because these are hard refs.
+ * It shouldn't happen in general because we went thru _all_ inodes in
+ * dqrele_all_inodes before calling this and didn't let the mountlock go.
+ * However it is possible that we have dquots with temporary
+ * references that are not attached to an inode. e.g. see xfs_setattr().
+ */
+ if (dqp->q_nrefs != 0) {
+ xfs_dqunlock(dqp);
+ XFS_DQ_HASH_UNLOCK(dqp->q_hash);
+ return (1);
+ }
+
+ ASSERT(XFS_DQ_IS_ON_FREELIST(dqp));
+
+ /*
+ * If we're turning off quotas, we have to make sure that, for
+ * example, we don't delete quota disk blocks while dquots are
+ * in the process of getting written to those disk blocks.
+ * This dquot might well be on AIL, and we can't leave it there
+ * if we're turning off quotas. Basically, we need this flush
+ * lock, and are willing to block on it.
+ */
+ if (! xfs_qm_dqflock_nowait(dqp)) {
+ /*
+ * Block on the flush lock after nudging dquot buffer,
+ * if it is incore.
+ */
+ xfs_qm_dqflock_pushbuf_wait(dqp);
+ }
+
+ /*
+ * XXXIf we're turning this type of quotas off, we don't care
+ * about the dirty metadata sitting in this dquot. OTOH, if
+ * we're unmounting, we do care, so we flush it and wait.
+ */
+ if (XFS_DQ_IS_DIRTY(dqp)) {
+ xfs_dqtrace_entry(dqp, "DQPURGE ->DQFLUSH: DQDIRTY");
+ /* dqflush unlocks dqflock */
+ /*
+ * Given that dqpurge is a very rare occurrence, it is OK
+ * that we're holding the hashlist and mplist locks
+ * across the disk write. But, ... XXXsup
+ *
+ * We don't care about getting disk errors here. We need
+ * to purge this dquot anyway, so we go ahead regardless.
+ */
+ (void) xfs_qm_dqflush(dqp, XFS_QMOPT_SYNC);
+ xfs_dqflock(dqp);
+ }
+ ASSERT(dqp->q_pincount == 0);
+ ASSERT(XFS_FORCED_SHUTDOWN(mp) ||
+ !(dqp->q_logitem.qli_item.li_flags & XFS_LI_IN_AIL));
+
+ thishash = dqp->q_hash;
+ XQM_HASHLIST_REMOVE(thishash, dqp);
+ XQM_MPLIST_REMOVE(&(XFS_QI_MPL_LIST(mp)), dqp);
+ /*
+ * XXX Move this to the front of the freelist, if we can get the
+ * freelist lock.
+ */
+ ASSERT(XFS_DQ_IS_ON_FREELIST(dqp));
+
+ dqp->q_mount = NULL;
+ dqp->q_hash = NULL;
+ dqp->dq_flags = XFS_DQ_INACTIVE;
+ memset(&dqp->q_core, 0, sizeof(dqp->q_core));
+ xfs_dqfunlock(dqp);
+ xfs_dqunlock(dqp);
+ XFS_DQ_HASH_UNLOCK(thishash);
+ return (0);
+}
+
+
+#ifdef QUOTADEBUG
+void
+xfs_qm_dqprint(xfs_dquot_t *dqp)
+{
+ cmn_err(CE_DEBUG, "-----------KERNEL DQUOT----------------");
+ cmn_err(CE_DEBUG, "---- dquotID = %d",
+ (int)INT_GET(dqp->q_core.d_id, ARCH_CONVERT));
+ cmn_err(CE_DEBUG, "---- type = %s",
+ XFS_QM_ISUDQ(dqp) ? "USR" : "GRP");
+ cmn_err(CE_DEBUG, "---- fs = 0x%p", dqp->q_mount);
+ cmn_err(CE_DEBUG, "---- blkno = 0x%x", (int) dqp->q_blkno);
+ cmn_err(CE_DEBUG, "---- boffset = 0x%x", (int) dqp->q_bufoffset);
+ cmn_err(CE_DEBUG, "---- blkhlimit = %Lu (0x%x)",
+ INT_GET(dqp->q_core.d_blk_hardlimit, ARCH_CONVERT),
+ (int) INT_GET(dqp->q_core.d_blk_hardlimit, ARCH_CONVERT));
+ cmn_err(CE_DEBUG, "---- blkslimit = %Lu (0x%x)",
+ INT_GET(dqp->q_core.d_blk_softlimit, ARCH_CONVERT),
+ (int)INT_GET(dqp->q_core.d_blk_softlimit, ARCH_CONVERT));
+ cmn_err(CE_DEBUG, "---- inohlimit = %Lu (0x%x)",
+ INT_GET(dqp->q_core.d_ino_hardlimit, ARCH_CONVERT),
+ (int)INT_GET(dqp->q_core.d_ino_hardlimit, ARCH_CONVERT));
+ cmn_err(CE_DEBUG, "---- inoslimit = %Lu (0x%x)",
+ INT_GET(dqp->q_core.d_ino_softlimit, ARCH_CONVERT),
+ (int)INT_GET(dqp->q_core.d_ino_softlimit, ARCH_CONVERT));
+ cmn_err(CE_DEBUG, "---- bcount = %Lu (0x%x)",
+ INT_GET(dqp->q_core.d_bcount, ARCH_CONVERT),
+ (int)INT_GET(dqp->q_core.d_bcount, ARCH_CONVERT));
+ cmn_err(CE_DEBUG, "---- icount = %Lu (0x%x)",
+ INT_GET(dqp->q_core.d_icount, ARCH_CONVERT),
+ (int)INT_GET(dqp->q_core.d_icount, ARCH_CONVERT));
+ cmn_err(CE_DEBUG, "---- btimer = %d",
+ (int)INT_GET(dqp->q_core.d_btimer, ARCH_CONVERT));
+ cmn_err(CE_DEBUG, "---- itimer = %d",
+ (int)INT_GET(dqp->q_core.d_itimer, ARCH_CONVERT));
+ cmn_err(CE_DEBUG, "---------------------------");
+}
+#endif
+
+/*
+ * Give the buffer a little push if it is incore and
+ * wait on the flush lock.
+ */
+void
+xfs_qm_dqflock_pushbuf_wait(
+ xfs_dquot_t *dqp)
+{
+ xfs_buf_t *bp;
+
+ /*
+ * Check to see if the dquot has been flushed delayed
+ * write. If so, grab its buffer and send it
+ * out immediately. We'll be able to acquire
+ * the flush lock when the I/O completes.
+ */
+ bp = xfs_incore(dqp->q_mount->m_ddev_targp, dqp->q_blkno,
+ XFS_QI_DQCHUNKLEN(dqp->q_mount),
+ XFS_INCORE_TRYLOCK);
+ if (bp != NULL) {
+ if (XFS_BUF_ISDELAYWRITE(bp)) {
+ if (XFS_BUF_ISPINNED(bp)) {
+ xfs_log_force(dqp->q_mount,
+ (xfs_lsn_t)0,
+ XFS_LOG_FORCE);
+ }
+ xfs_bawrite(dqp->q_mount, bp);
+ } else {
+ xfs_buf_relse(bp);
+ }
+ }
+ xfs_dqflock(dqp);
+}
diff --git a/fs/xfs/quota/xfs_dquot.h b/fs/xfs/quota/xfs_dquot.h
new file mode 100644
index 00000000000000..0c3fe3175baa4a
--- /dev/null
+++ b/fs/xfs/quota/xfs_dquot.h
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_DQUOT_H__
+#define __XFS_DQUOT_H__
+
+/*
+ * Dquots are structures that hold quota information about a user or a group,
+ * much like inodes are for files. In fact, dquots share many characteristics
+ * with inodes. However, dquots can also be a centralized resource, relative
+ * to a collection of inodes. In this respect, dquots share some characteristics
+ * of the superblock.
+ * XFS dquots exploit both those in its algorithms. They make every attempt
+ * to not be a bottleneck when quotas are on and have minimal impact, if any,
+ * when quotas are off.
+ */
+
+/*
+ * The hash chain headers (hash buckets)
+ */
+typedef struct xfs_dqhash {
+ struct xfs_dquot *qh_next;
+ mutex_t qh_lock;
+ uint qh_version; /* ever increasing version */
+ uint qh_nelems; /* number of dquots on the list */
+} xfs_dqhash_t;
+
+typedef struct xfs_dqlink {
+ struct xfs_dquot *ql_next; /* forward link */
+ struct xfs_dquot **ql_prevp; /* pointer to prev ql_next */
+} xfs_dqlink_t;
+
+struct xfs_mount;
+struct xfs_trans;
+
+/*
+ * This is the marker which is designed to occupy the first few
+ * bytes of the xfs_dquot_t structure. Even inside this, the freelist pointers
+ * must come first.
+ * This serves as the marker ("sentinel") when we have to restart list
+ * iterations because of locking considerations.
+ */
+typedef struct xfs_dqmarker {
+ struct xfs_dquot*dqm_flnext; /* link to freelist: must be first */
+ struct xfs_dquot*dqm_flprev;
+ xfs_dqlink_t dqm_mplist; /* link to mount's list of dquots */
+ xfs_dqlink_t dqm_hashlist; /* link to the hash chain */
+ uint dqm_flags; /* various flags (XFS_DQ_*) */
+} xfs_dqmarker_t;
+
+/*
+ * The incore dquot structure
+ */
+typedef struct xfs_dquot {
+ xfs_dqmarker_t q_lists; /* list ptrs, q_flags (marker) */
+ xfs_dqhash_t *q_hash; /* the hashchain header */
+ struct xfs_mount*q_mount; /* filesystem this relates to */
+ struct xfs_trans*q_transp; /* trans this belongs to currently */
+ uint q_nrefs; /* # active refs from inodes */
+ xfs_daddr_t q_blkno; /* blkno of dquot buffer */
+ int q_bufoffset; /* off of dq in buffer (# dquots) */
+ xfs_fileoff_t q_fileoffset; /* offset in quotas file */
+
+ struct xfs_dquot*q_gdquot; /* group dquot, hint only */
+ xfs_disk_dquot_t q_core; /* actual usage & quotas */
+ xfs_dq_logitem_t q_logitem; /* dquot log item */
+ xfs_qcnt_t q_res_bcount; /* total regular nblks used+reserved */
+ xfs_qcnt_t q_res_icount; /* total inos allocd+reserved */
+ xfs_qcnt_t q_res_rtbcount;/* total realtime blks used+reserved */
+ mutex_t q_qlock; /* quota lock */
+ sema_t q_flock; /* flush lock */
+ uint q_pincount; /* pin count for this dquot */
+ sv_t q_pinwait; /* sync var for pinning */
+#ifdef XFS_DQUOT_TRACE
+ struct ktrace *q_trace; /* trace header structure */
+#endif
+} xfs_dquot_t;
+
+
+#define dq_flnext q_lists.dqm_flnext
+#define dq_flprev q_lists.dqm_flprev
+#define dq_mplist q_lists.dqm_mplist
+#define dq_hashlist q_lists.dqm_hashlist
+#define dq_flags q_lists.dqm_flags
+
+#define XFS_DQHOLD(dqp) ((dqp)->q_nrefs++)
+
+/*
+ * Quota Accounting flags
+ */
+#define XFS_ALL_QUOTA_ACCT (XFS_UQUOTA_ACCT | XFS_GQUOTA_ACCT)
+#define XFS_ALL_QUOTA_ENFD (XFS_UQUOTA_ENFD | XFS_GQUOTA_ENFD)
+#define XFS_ALL_QUOTA_CHKD (XFS_UQUOTA_CHKD | XFS_GQUOTA_CHKD)
+#define XFS_ALL_QUOTA_ACTV (XFS_UQUOTA_ACTIVE | XFS_GQUOTA_ACTIVE)
+#define XFS_ALL_QUOTA_ACCT_ENFD (XFS_UQUOTA_ACCT|XFS_UQUOTA_ENFD|\
+ XFS_GQUOTA_ACCT|XFS_GQUOTA_ENFD)
+
+#define XFS_IS_QUOTA_RUNNING(mp) ((mp)->m_qflags & XFS_ALL_QUOTA_ACCT)
+#define XFS_IS_UQUOTA_RUNNING(mp) ((mp)->m_qflags & XFS_UQUOTA_ACCT)
+#define XFS_IS_GQUOTA_RUNNING(mp) ((mp)->m_qflags & XFS_GQUOTA_ACCT)
+
+/*
+ * Quota Limit Enforcement flags
+ */
+#define XFS_IS_QUOTA_ENFORCED(mp) ((mp)->m_qflags & XFS_ALL_QUOTA_ENFD)
+#define XFS_IS_UQUOTA_ENFORCED(mp) ((mp)->m_qflags & XFS_UQUOTA_ENFD)
+#define XFS_IS_GQUOTA_ENFORCED(mp) ((mp)->m_qflags & XFS_GQUOTA_ENFD)
+
+#ifdef DEBUG
+static inline int
+XFS_DQ_IS_LOCKED(xfs_dquot_t *dqp)
+{
+ if (mutex_trylock(&dqp->q_qlock)) {
+ mutex_unlock(&dqp->q_qlock);
+ return 0;
+ }
+ return 1;
+}
+#endif
+
+
+/*
+ * The following three routines simply manage the q_flock
+ * semaphore embedded in the dquot. This semaphore synchronizes
+ * processes attempting to flush the in-core dquot back to disk.
+ */
+#define xfs_dqflock(dqp) { psema(&((dqp)->q_flock), PINOD | PRECALC);\
+ (dqp)->dq_flags |= XFS_DQ_FLOCKED; }
+#define xfs_dqfunlock(dqp) { ASSERT(valusema(&((dqp)->q_flock)) <= 0); \
+ vsema(&((dqp)->q_flock)); \
+ (dqp)->dq_flags &= ~(XFS_DQ_FLOCKED); }
+
+#define XFS_DQ_PINLOCK(dqp) mutex_spinlock( \
+ &(XFS_DQ_TO_QINF(dqp)->qi_pinlock))
+#define XFS_DQ_PINUNLOCK(dqp, s) mutex_spinunlock( \
+ &(XFS_DQ_TO_QINF(dqp)->qi_pinlock), s)
+
+#define XFS_DQ_IS_FLUSH_LOCKED(dqp) (valusema(&((dqp)->q_flock)) <= 0)
+#define XFS_DQ_IS_ON_FREELIST(dqp) ((dqp)->dq_flnext != (dqp))
+#define XFS_DQ_IS_DIRTY(dqp) ((dqp)->dq_flags & XFS_DQ_DIRTY)
+#define XFS_QM_ISUDQ(dqp) ((dqp)->dq_flags & XFS_DQ_USER)
+#define XFS_DQ_TO_QINF(dqp) ((dqp)->q_mount->m_quotainfo)
+#define XFS_DQ_TO_QIP(dqp) (XFS_QM_ISUDQ(dqp) ? \
+ XFS_DQ_TO_QINF(dqp)->qi_uquotaip : \
+ XFS_DQ_TO_QINF(dqp)->qi_gquotaip)
+
+#define XFS_IS_THIS_QUOTA_OFF(d) (! (XFS_QM_ISUDQ(d) ? \
+ (XFS_IS_UQUOTA_ON((d)->q_mount)) : \
+ (XFS_IS_GQUOTA_ON((d)->q_mount))))
+
+#ifdef XFS_DQUOT_TRACE
+/*
+ * Dquot Tracing stuff.
+ */
+#define DQUOT_TRACE_SIZE 64
+#define DQUOT_KTRACE_ENTRY 1
+
+extern void __xfs_dqtrace_entry(xfs_dquot_t *dqp, char *func,
+ void *, xfs_inode_t *);
+#define xfs_dqtrace_entry_ino(a,b,ip) \
+ __xfs_dqtrace_entry((a), (b), (void*)__return_address, (ip))
+#define xfs_dqtrace_entry(a,b) \
+ __xfs_dqtrace_entry((a), (b), (void*)__return_address, NULL)
+#else
+#define xfs_dqtrace_entry(a,b)
+#define xfs_dqtrace_entry_ino(a,b,ip)
+#endif
+
+#ifdef QUOTADEBUG
+extern void xfs_qm_dqprint(xfs_dquot_t *);
+#else
+#define xfs_qm_dqprint(a)
+#endif
+
+extern void xfs_qm_dqdestroy(xfs_dquot_t *);
+extern int xfs_qm_dqflush(xfs_dquot_t *, uint);
+extern int xfs_qm_dqpurge(xfs_dquot_t *, uint);
+extern void xfs_qm_dqunpin_wait(xfs_dquot_t *);
+extern int xfs_qm_dqlock_nowait(xfs_dquot_t *);
+extern int xfs_qm_dqflock_nowait(xfs_dquot_t *);
+extern void xfs_qm_dqflock_pushbuf_wait(xfs_dquot_t *dqp);
+extern void xfs_qm_adjust_dqtimers(xfs_mount_t *,
+ xfs_disk_dquot_t *);
+extern void xfs_qm_adjust_dqlimits(xfs_mount_t *,
+ xfs_disk_dquot_t *);
+extern int xfs_qm_dqwarn(xfs_disk_dquot_t *, uint);
+extern int xfs_qm_dqget(xfs_mount_t *, xfs_inode_t *,
+ xfs_dqid_t, uint, uint, xfs_dquot_t **);
+extern void xfs_qm_dqput(xfs_dquot_t *);
+extern void xfs_qm_dqrele(xfs_dquot_t *);
+extern void xfs_dqlock(xfs_dquot_t *);
+extern void xfs_dqlock2(xfs_dquot_t *, xfs_dquot_t *);
+extern void xfs_dqunlock(xfs_dquot_t *);
+extern void xfs_dqunlock_nonotify(xfs_dquot_t *);
+
+#endif /* __XFS_DQUOT_H__ */
diff --git a/fs/xfs/quota/xfs_dquot_item.c b/fs/xfs/quota/xfs_dquot_item.c
new file mode 100644
index 00000000000000..a5425ee6e7bd53
--- /dev/null
+++ b/fs/xfs/quota/xfs_dquot_item.c
@@ -0,0 +1,715 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_alloc.h"
+#include "xfs_dmapi.h"
+#include "xfs_quota.h"
+#include "xfs_mount.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode.h"
+#include "xfs_bmap.h"
+#include "xfs_bit.h"
+#include "xfs_rtalloc.h"
+#include "xfs_error.h"
+#include "xfs_itable.h"
+#include "xfs_rw.h"
+#include "xfs_acl.h"
+#include "xfs_cap.h"
+#include "xfs_mac.h"
+#include "xfs_attr.h"
+#include "xfs_buf_item.h"
+#include "xfs_trans_priv.h"
+
+#include "xfs_qm.h"
+
+
+/*
+ * returns the number of iovecs needed to log the given dquot item.
+ */
+/* ARGSUSED */
+STATIC uint
+xfs_qm_dquot_logitem_size(
+ xfs_dq_logitem_t *logitem)
+{
+ /*
+ * we need only two iovecs, one for the format, one for the real thing
+ */
+ return (2);
+}
+
+/*
+ * fills in the vector of log iovecs for the given dquot log item.
+ */
+STATIC void
+xfs_qm_dquot_logitem_format(
+ xfs_dq_logitem_t *logitem,
+ xfs_log_iovec_t *logvec)
+{
+ ASSERT(logitem);
+ ASSERT(logitem->qli_dquot);
+
+ logvec->i_addr = (xfs_caddr_t)&logitem->qli_format;
+ logvec->i_len = sizeof(xfs_dq_logformat_t);
+ logvec++;
+ logvec->i_addr = (xfs_caddr_t)&logitem->qli_dquot->q_core;
+ logvec->i_len = sizeof(xfs_disk_dquot_t);
+
+ ASSERT(2 == logitem->qli_item.li_desc->lid_size);
+ logitem->qli_format.qlf_size = 2;
+
+}
+
+/*
+ * Increment the pin count of the given dquot.
+ * This value is protected by pinlock spinlock in the xQM structure.
+ */
+STATIC void
+xfs_qm_dquot_logitem_pin(
+ xfs_dq_logitem_t *logitem)
+{
+ unsigned long s;
+ xfs_dquot_t *dqp;
+
+ dqp = logitem->qli_dquot;
+ ASSERT(XFS_DQ_IS_LOCKED(dqp));
+ s = XFS_DQ_PINLOCK(dqp);
+ dqp->q_pincount++;
+ XFS_DQ_PINUNLOCK(dqp, s);
+}
+
+/*
+ * Decrement the pin count of the given dquot, and wake up
+ * anyone in xfs_dqwait_unpin() if the count goes to 0. The
+ * dquot must have been previously pinned with a call to xfs_dqpin().
+ */
+/* ARGSUSED */
+STATIC void
+xfs_qm_dquot_logitem_unpin(
+ xfs_dq_logitem_t *logitem,
+ int stale)
+{
+ unsigned long s;
+ xfs_dquot_t *dqp;
+
+ dqp = logitem->qli_dquot;
+ ASSERT(dqp->q_pincount > 0);
+ s = XFS_DQ_PINLOCK(dqp);
+ dqp->q_pincount--;
+ if (dqp->q_pincount == 0) {
+ sv_broadcast(&dqp->q_pinwait);
+ }
+ XFS_DQ_PINUNLOCK(dqp, s);
+}
+
+/* ARGSUSED */
+STATIC void
+xfs_qm_dquot_logitem_unpin_remove(
+ xfs_dq_logitem_t *logitem,
+ xfs_trans_t *tp)
+{
+ xfs_qm_dquot_logitem_unpin(logitem, 0);
+}
+
+/*
+ * Given the logitem, this writes the corresponding dquot entry to disk
+ * asynchronously. This is called with the dquot entry securely locked;
+ * we simply get xfs_qm_dqflush() to do the work, and unlock the dquot
+ * at the end.
+ */
+STATIC void
+xfs_qm_dquot_logitem_push(
+ xfs_dq_logitem_t *logitem)
+{
+ xfs_dquot_t *dqp;
+
+ dqp = logitem->qli_dquot;
+
+ ASSERT(XFS_DQ_IS_LOCKED(dqp));
+ ASSERT(XFS_DQ_IS_FLUSH_LOCKED(dqp));
+
+ /*
+ * Since we were able to lock the dquot's flush lock and
+ * we found it on the AIL, the dquot must be dirty. This
+ * is because the dquot is removed from the AIL while still
+ * holding the flush lock in xfs_dqflush_done(). Thus, if
+ * we found it in the AIL and were able to obtain the flush
+ * lock without sleeping, then there must not have been
+ * anyone in the process of flushing the dquot.
+ */
+ xfs_qm_dqflush(dqp, XFS_B_DELWRI);
+ xfs_dqunlock(dqp);
+}
+
+/*ARGSUSED*/
+STATIC xfs_lsn_t
+xfs_qm_dquot_logitem_committed(
+ xfs_dq_logitem_t *l,
+ xfs_lsn_t lsn)
+{
+ /*
+ * We always re-log the entire dquot when it becomes dirty,
+ * so, the latest copy _is_ the only one that matters.
+ */
+ return (lsn);
+}
+
+
+/*
+ * This is called to wait for the given dquot to be unpinned.
+ * Most of these pin/unpin routines are plagiarized from inode code.
+ */
+void
+xfs_qm_dqunpin_wait(
+ xfs_dquot_t *dqp)
+{
+ SPLDECL(s);
+
+ ASSERT(XFS_DQ_IS_LOCKED(dqp));
+ if (dqp->q_pincount == 0) {
+ return;
+ }
+
+ /*
+ * Give the log a push so we don't wait here too long.
+ */
+ xfs_log_force(dqp->q_mount, (xfs_lsn_t)0, XFS_LOG_FORCE);
+ s = XFS_DQ_PINLOCK(dqp);
+ if (dqp->q_pincount == 0) {
+ XFS_DQ_PINUNLOCK(dqp, s);
+ return;
+ }
+ sv_wait(&(dqp->q_pinwait), PINOD,
+ &(XFS_DQ_TO_QINF(dqp)->qi_pinlock), s);
+}
+
+/*
+ * This is called when IOP_TRYLOCK returns XFS_ITEM_PUSHBUF to indicate that
+ * the dquot is locked by us, but the flush lock isn't. So, here we are
+ * going to see if the relevant dquot buffer is incore, waiting on DELWRI.
+ * If so, we want to push it out to help us take this item off the AIL as soon
+ * as possible.
+ *
+ * We must not be holding the AIL_LOCK at this point. Calling incore() to
+ * search the buffercache can be a time consuming thing, and AIL_LOCK is a
+ * spinlock.
+ */
+STATIC void
+xfs_qm_dquot_logitem_pushbuf(
+ xfs_dq_logitem_t *qip)
+{
+ xfs_dquot_t *dqp;
+ xfs_mount_t *mp;
+ xfs_buf_t *bp;
+ uint dopush;
+
+ dqp = qip->qli_dquot;
+ ASSERT(XFS_DQ_IS_LOCKED(dqp));
+
+ /*
+ * The qli_pushbuf_flag keeps others from
+ * trying to duplicate our effort.
+ */
+ ASSERT(qip->qli_pushbuf_flag != 0);
+ ASSERT(qip->qli_push_owner == get_thread_id());
+
+ /*
+ * If flushlock isn't locked anymore, chances are that the
+ * inode flush completed and the inode was taken off the AIL.
+ * So, just get out.
+ */
+ if ((valusema(&(dqp->q_flock)) > 0) ||
+ ((qip->qli_item.li_flags & XFS_LI_IN_AIL) == 0)) {
+ qip->qli_pushbuf_flag = 0;
+ xfs_dqunlock(dqp);
+ return;
+ }
+ mp = dqp->q_mount;
+ bp = xfs_incore(mp->m_ddev_targp, qip->qli_format.qlf_blkno,
+ XFS_QI_DQCHUNKLEN(mp),
+ XFS_INCORE_TRYLOCK);
+ if (bp != NULL) {
+ if (XFS_BUF_ISDELAYWRITE(bp)) {
+ dopush = ((qip->qli_item.li_flags & XFS_LI_IN_AIL) &&
+ (valusema(&(dqp->q_flock)) <= 0));
+ qip->qli_pushbuf_flag = 0;
+ xfs_dqunlock(dqp);
+
+ if (XFS_BUF_ISPINNED(bp)) {
+ xfs_log_force(mp, (xfs_lsn_t)0,
+ XFS_LOG_FORCE);
+ }
+ if (dopush) {
+#ifdef XFSRACEDEBUG
+ delay_for_intr();
+ delay(300);
+#endif
+ xfs_bawrite(mp, bp);
+ } else {
+ xfs_buf_relse(bp);
+ }
+ } else {
+ qip->qli_pushbuf_flag = 0;
+ xfs_dqunlock(dqp);
+ xfs_buf_relse(bp);
+ }
+ return;
+ }
+
+ qip->qli_pushbuf_flag = 0;
+ xfs_dqunlock(dqp);
+}
+
+/*
+ * This is called to attempt to lock the dquot associated with this
+ * dquot log item. Don't sleep on the dquot lock or the flush lock.
+ * If the flush lock is already held, indicating that the dquot has
+ * been or is in the process of being flushed, then see if we can
+ * find the dquot's buffer in the buffer cache without sleeping. If
+ * we can and it is marked delayed write, then we want to send it out.
+ * We delay doing so until the push routine, though, to avoid sleeping
+ * in any device strategy routines.
+ */
+STATIC uint
+xfs_qm_dquot_logitem_trylock(
+ xfs_dq_logitem_t *qip)
+{
+ xfs_dquot_t *dqp;
+ uint retval;
+
+ dqp = qip->qli_dquot;
+ if (dqp->q_pincount > 0)
+ return (XFS_ITEM_PINNED);
+
+ if (! xfs_qm_dqlock_nowait(dqp))
+ return (XFS_ITEM_LOCKED);
+
+ retval = XFS_ITEM_SUCCESS;
+ if (! xfs_qm_dqflock_nowait(dqp)) {
+ /*
+ * The dquot is already being flushed. It may have been
+ * flushed delayed write, however, and we don't want to
+ * get stuck waiting for that to complete. So, we want to check
+ * to see if we can lock the dquot's buffer without sleeping.
+ * If we can and it is marked for delayed write, then we
+ * hold it and send it out from the push routine. We don't
+ * want to do that now since we might sleep in the device
+ * strategy routine. We also don't want to grab the buffer lock
+ * here because we'd like not to call into the buffer cache
+ * while holding the AIL_LOCK.
+ * Make sure to only return PUSHBUF if we set pushbuf_flag
+ * ourselves. If someone else is doing it then we don't
+ * want to go to the push routine and duplicate their efforts.
+ */
+ if (qip->qli_pushbuf_flag == 0) {
+ qip->qli_pushbuf_flag = 1;
+ ASSERT(qip->qli_format.qlf_blkno == dqp->q_blkno);
+#ifdef DEBUG
+ qip->qli_push_owner = get_thread_id();
+#endif
+ /*
+ * The dquot is left locked.
+ */
+ retval = XFS_ITEM_PUSHBUF;
+ } else {
+ retval = XFS_ITEM_FLUSHING;
+ xfs_dqunlock_nonotify(dqp);
+ }
+ }
+
+ ASSERT(qip->qli_item.li_flags & XFS_LI_IN_AIL);
+ return (retval);
+}
+
+
+/*
+ * Unlock the dquot associated with the log item.
+ * Clear the fields of the dquot and dquot log item that
+ * are specific to the current transaction. If the
+ * hold flags is set, do not unlock the dquot.
+ */
+STATIC void
+xfs_qm_dquot_logitem_unlock(
+ xfs_dq_logitem_t *ql)
+{
+ xfs_dquot_t *dqp;
+
+ ASSERT(ql != NULL);
+ dqp = ql->qli_dquot;
+ ASSERT(XFS_DQ_IS_LOCKED(dqp));
+
+ /*
+ * Clear the transaction pointer in the dquot
+ */
+ dqp->q_transp = NULL;
+
+ /*
+ * dquots are never 'held' from getting unlocked at the end of
+ * a transaction. Their locking and unlocking is hidden inside the
+ * transaction layer, within trans_commit. Hence, no LI_HOLD flag
+ * for the logitem.
+ */
+ xfs_dqunlock(dqp);
+}
+
+
+/*
+ * The transaction with the dquot locked has aborted. The dquot
+ * must not be dirty within the transaction. We simply unlock just
+ * as if the transaction had been cancelled.
+ */
+STATIC void
+xfs_qm_dquot_logitem_abort(
+ xfs_dq_logitem_t *ql)
+{
+ xfs_qm_dquot_logitem_unlock(ql);
+}
+
+/*
+ * this needs to stamp an lsn into the dquot, I think.
+ * rpc's that look at user dquot's would then have to
+ * push on the dependency recorded in the dquot
+ */
+/* ARGSUSED */
+STATIC void
+xfs_qm_dquot_logitem_committing(
+ xfs_dq_logitem_t *l,
+ xfs_lsn_t lsn)
+{
+ return;
+}
+
+
+/*
+ * This is the ops vector for dquots
+ */
+struct xfs_item_ops xfs_dquot_item_ops = {
+ .iop_size = (uint(*)(xfs_log_item_t*))xfs_qm_dquot_logitem_size,
+ .iop_format = (void(*)(xfs_log_item_t*, xfs_log_iovec_t*))
+ xfs_qm_dquot_logitem_format,
+ .iop_pin = (void(*)(xfs_log_item_t*))xfs_qm_dquot_logitem_pin,
+ .iop_unpin = (void(*)(xfs_log_item_t*, int))
+ xfs_qm_dquot_logitem_unpin,
+ .iop_unpin_remove = (void(*)(xfs_log_item_t*, xfs_trans_t*))
+ xfs_qm_dquot_logitem_unpin_remove,
+ .iop_trylock = (uint(*)(xfs_log_item_t*))
+ xfs_qm_dquot_logitem_trylock,
+ .iop_unlock = (void(*)(xfs_log_item_t*))xfs_qm_dquot_logitem_unlock,
+ .iop_committed = (xfs_lsn_t(*)(xfs_log_item_t*, xfs_lsn_t))
+ xfs_qm_dquot_logitem_committed,
+ .iop_push = (void(*)(xfs_log_item_t*))xfs_qm_dquot_logitem_push,
+ .iop_abort = (void(*)(xfs_log_item_t*))xfs_qm_dquot_logitem_abort,
+ .iop_pushbuf = (void(*)(xfs_log_item_t*))
+ xfs_qm_dquot_logitem_pushbuf,
+ .iop_committing = (void(*)(xfs_log_item_t*, xfs_lsn_t))
+ xfs_qm_dquot_logitem_committing
+};
+
+/*
+ * Initialize the dquot log item for a newly allocated dquot.
+ * The dquot isn't locked at this point, but it isn't on any of the lists
+ * either, so we don't care.
+ */
+void
+xfs_qm_dquot_logitem_init(
+ struct xfs_dquot *dqp)
+{
+ xfs_dq_logitem_t *lp;
+ lp = &dqp->q_logitem;
+
+ lp->qli_item.li_type = XFS_LI_DQUOT;
+ lp->qli_item.li_ops = &xfs_dquot_item_ops;
+ lp->qli_item.li_mountp = dqp->q_mount;
+ lp->qli_dquot = dqp;
+ lp->qli_format.qlf_type = XFS_LI_DQUOT;
+ lp->qli_format.qlf_id = INT_GET(dqp->q_core.d_id, ARCH_CONVERT);
+ lp->qli_format.qlf_blkno = dqp->q_blkno;
+ lp->qli_format.qlf_len = 1;
+ /*
+ * This is just the offset of this dquot within its buffer
+ * (which is currently 1 FSB and probably won't change).
+ * Hence 32 bits for this offset should be just fine.
+ * Alternatively, we can store (bufoffset / sizeof(xfs_dqblk_t))
+ * here, and recompute it at recovery time.
+ */
+ lp->qli_format.qlf_boffset = (__uint32_t)dqp->q_bufoffset;
+}
+
+/*------------------ QUOTAOFF LOG ITEMS -------------------*/
+
+/*
+ * This returns the number of iovecs needed to log the given quotaoff item.
+ * We only need 1 iovec for an quotaoff item. It just logs the
+ * quotaoff_log_format structure.
+ */
+/*ARGSUSED*/
+STATIC uint
+xfs_qm_qoff_logitem_size(xfs_qoff_logitem_t *qf)
+{
+ return (1);
+}
+
+/*
+ * This is called to fill in the vector of log iovecs for the
+ * given quotaoff log item. We use only 1 iovec, and we point that
+ * at the quotaoff_log_format structure embedded in the quotaoff item.
+ * It is at this point that we assert that all of the extent
+ * slots in the quotaoff item have been filled.
+ */
+STATIC void
+xfs_qm_qoff_logitem_format(xfs_qoff_logitem_t *qf,
+ xfs_log_iovec_t *log_vector)
+{
+ ASSERT(qf->qql_format.qf_type == XFS_LI_QUOTAOFF);
+
+ log_vector->i_addr = (xfs_caddr_t)&(qf->qql_format);
+ log_vector->i_len = sizeof(xfs_qoff_logitem_t);
+ qf->qql_format.qf_size = 1;
+}
+
+
+/*
+ * Pinning has no meaning for an quotaoff item, so just return.
+ */
+/*ARGSUSED*/
+STATIC void
+xfs_qm_qoff_logitem_pin(xfs_qoff_logitem_t *qf)
+{
+ return;
+}
+
+
+/*
+ * Since pinning has no meaning for an quotaoff item, unpinning does
+ * not either.
+ */
+/*ARGSUSED*/
+STATIC void
+xfs_qm_qoff_logitem_unpin(xfs_qoff_logitem_t *qf, int stale)
+{
+ return;
+}
+
+/*ARGSUSED*/
+STATIC void
+xfs_qm_qoff_logitem_unpin_remove(xfs_qoff_logitem_t *qf, xfs_trans_t *tp)
+{
+ return;
+}
+
+/*
+ * Quotaoff items have no locking, so just return success.
+ */
+/*ARGSUSED*/
+STATIC uint
+xfs_qm_qoff_logitem_trylock(xfs_qoff_logitem_t *qf)
+{
+ return XFS_ITEM_LOCKED;
+}
+
+/*
+ * Quotaoff items have no locking or pushing, so return failure
+ * so that the caller doesn't bother with us.
+ */
+/*ARGSUSED*/
+STATIC void
+xfs_qm_qoff_logitem_unlock(xfs_qoff_logitem_t *qf)
+{
+ return;
+}
+
+/*
+ * The quotaoff-start-item is logged only once and cannot be moved in the log,
+ * so simply return the lsn at which it's been logged.
+ */
+/*ARGSUSED*/
+STATIC xfs_lsn_t
+xfs_qm_qoff_logitem_committed(xfs_qoff_logitem_t *qf, xfs_lsn_t lsn)
+{
+ return (lsn);
+}
+
+/*
+ * The transaction of which this QUOTAOFF is a part has been aborted.
+ * Just clean up after ourselves.
+ * Shouldn't this never happen in the case of qoffend logitems? XXX
+ */
+STATIC void
+xfs_qm_qoff_logitem_abort(xfs_qoff_logitem_t *qf)
+{
+ kmem_free(qf, sizeof(xfs_qoff_logitem_t));
+}
+
+/*
+ * There isn't much you can do to push on an quotaoff item. It is simply
+ * stuck waiting for the log to be flushed to disk.
+ */
+/*ARGSUSED*/
+STATIC void
+xfs_qm_qoff_logitem_push(xfs_qoff_logitem_t *qf)
+{
+ return;
+}
+
+
+/*ARGSUSED*/
+STATIC xfs_lsn_t
+xfs_qm_qoffend_logitem_committed(
+ xfs_qoff_logitem_t *qfe,
+ xfs_lsn_t lsn)
+{
+ xfs_qoff_logitem_t *qfs;
+ SPLDECL(s);
+
+ qfs = qfe->qql_start_lip;
+ AIL_LOCK(qfs->qql_item.li_mountp,s);
+ /*
+ * Delete the qoff-start logitem from the AIL.
+ * xfs_trans_delete_ail() drops the AIL lock.
+ */
+ xfs_trans_delete_ail(qfs->qql_item.li_mountp, (xfs_log_item_t *)qfs, s);
+ kmem_free(qfs, sizeof(xfs_qoff_logitem_t));
+ kmem_free(qfe, sizeof(xfs_qoff_logitem_t));
+ return (xfs_lsn_t)-1;
+}
+
+/*
+ * XXX rcc - don't know quite what to do with this. I think we can
+ * just ignore it. The only time that isn't the case is if we allow
+ * the client to somehow see that quotas have been turned off in which
+ * we can't allow that to get back until the quotaoff hits the disk.
+ * So how would that happen? Also, do we need different routines for
+ * quotaoff start and quotaoff end? I suspect the answer is yes but
+ * to be sure, I need to look at the recovery code and see how quota off
+ * recovery is handled (do we roll forward or back or do something else).
+ * If we roll forwards or backwards, then we need two separate routines,
+ * one that does nothing and one that stamps in the lsn that matters
+ * (truly makes the quotaoff irrevocable). If we do something else,
+ * then maybe we don't need two.
+ */
+/* ARGSUSED */
+STATIC void
+xfs_qm_qoff_logitem_committing(xfs_qoff_logitem_t *qip, xfs_lsn_t commit_lsn)
+{
+ return;
+}
+
+/* ARGSUSED */
+STATIC void
+xfs_qm_qoffend_logitem_committing(xfs_qoff_logitem_t *qip, xfs_lsn_t commit_lsn)
+{
+ return;
+}
+
+struct xfs_item_ops xfs_qm_qoffend_logitem_ops = {
+ .iop_size = (uint(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_size,
+ .iop_format = (void(*)(xfs_log_item_t*, xfs_log_iovec_t*))
+ xfs_qm_qoff_logitem_format,
+ .iop_pin = (void(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_pin,
+ .iop_unpin = (void(*)(xfs_log_item_t* ,int))
+ xfs_qm_qoff_logitem_unpin,
+ .iop_unpin_remove = (void(*)(xfs_log_item_t*,xfs_trans_t*))
+ xfs_qm_qoff_logitem_unpin_remove,
+ .iop_trylock = (uint(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_trylock,
+ .iop_unlock = (void(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_unlock,
+ .iop_committed = (xfs_lsn_t(*)(xfs_log_item_t*, xfs_lsn_t))
+ xfs_qm_qoffend_logitem_committed,
+ .iop_push = (void(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_push,
+ .iop_abort = (void(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_abort,
+ .iop_pushbuf = NULL,
+ .iop_committing = (void(*)(xfs_log_item_t*, xfs_lsn_t))
+ xfs_qm_qoffend_logitem_committing
+};
+
+/*
+ * This is the ops vector shared by all quotaoff-start log items.
+ */
+struct xfs_item_ops xfs_qm_qoff_logitem_ops = {
+ .iop_size = (uint(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_size,
+ .iop_format = (void(*)(xfs_log_item_t*, xfs_log_iovec_t*))
+ xfs_qm_qoff_logitem_format,
+ .iop_pin = (void(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_pin,
+ .iop_unpin = (void(*)(xfs_log_item_t*, int))
+ xfs_qm_qoff_logitem_unpin,
+ .iop_unpin_remove = (void(*)(xfs_log_item_t*,xfs_trans_t*))
+ xfs_qm_qoff_logitem_unpin_remove,
+ .iop_trylock = (uint(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_trylock,
+ .iop_unlock = (void(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_unlock,
+ .iop_committed = (xfs_lsn_t(*)(xfs_log_item_t*, xfs_lsn_t))
+ xfs_qm_qoff_logitem_committed,
+ .iop_push = (void(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_push,
+ .iop_abort = (void(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_abort,
+ .iop_pushbuf = NULL,
+ .iop_committing = (void(*)(xfs_log_item_t*, xfs_lsn_t))
+ xfs_qm_qoff_logitem_committing
+};
+
+/*
+ * Allocate and initialize an quotaoff item of the correct quota type(s).
+ */
+xfs_qoff_logitem_t *
+xfs_qm_qoff_logitem_init(
+ struct xfs_mount *mp,
+ xfs_qoff_logitem_t *start,
+ uint flags)
+{
+ xfs_qoff_logitem_t *qf;
+
+ qf = (xfs_qoff_logitem_t*) kmem_zalloc(sizeof(xfs_qoff_logitem_t), KM_SLEEP);
+
+ qf->qql_item.li_type = XFS_LI_QUOTAOFF;
+ if (start)
+ qf->qql_item.li_ops = &xfs_qm_qoffend_logitem_ops;
+ else
+ qf->qql_item.li_ops = &xfs_qm_qoff_logitem_ops;
+ qf->qql_item.li_mountp = mp;
+ qf->qql_format.qf_type = XFS_LI_QUOTAOFF;
+ qf->qql_format.qf_flags = flags;
+ qf->qql_start_lip = start;
+ return (qf);
+}
diff --git a/fs/xfs/quota/xfs_dquot_item.h b/fs/xfs/quota/xfs_dquot_item.h
new file mode 100644
index 00000000000000..9c6500dabcaacf
--- /dev/null
+++ b/fs/xfs/quota/xfs_dquot_item.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_DQUOT_ITEM_H__
+#define __XFS_DQUOT_ITEM_H__
+
+struct xfs_dquot;
+struct xfs_trans;
+struct xfs_mount;
+struct xfs_qoff_logitem;
+
+typedef struct xfs_dq_logitem {
+ xfs_log_item_t qli_item; /* common portion */
+ struct xfs_dquot *qli_dquot; /* dquot ptr */
+ xfs_lsn_t qli_flush_lsn; /* lsn at last flush */
+ unsigned short qli_pushbuf_flag; /* 1 bit used in push_ail */
+#ifdef DEBUG
+ uint64_t qli_push_owner;
+#endif
+ xfs_dq_logformat_t qli_format; /* logged structure */
+} xfs_dq_logitem_t;
+
+typedef struct xfs_qoff_logitem {
+ xfs_log_item_t qql_item; /* common portion */
+ struct xfs_qoff_logitem *qql_start_lip; /* qoff-start logitem, if any */
+ xfs_qoff_logformat_t qql_format; /* logged structure */
+} xfs_qoff_logitem_t;
+
+
+extern void xfs_qm_dquot_logitem_init(struct xfs_dquot *);
+extern xfs_qoff_logitem_t *xfs_qm_qoff_logitem_init(struct xfs_mount *,
+ struct xfs_qoff_logitem *, uint);
+extern xfs_qoff_logitem_t *xfs_trans_get_qoff_item(struct xfs_trans *,
+ struct xfs_qoff_logitem *, uint);
+extern void xfs_trans_log_quotaoff_item(struct xfs_trans *,
+ struct xfs_qoff_logitem *);
+
+#endif /* __XFS_DQUOT_ITEM_H__ */
diff --git a/fs/xfs/quota/xfs_qm.c b/fs/xfs/quota/xfs_qm.c
new file mode 100644
index 00000000000000..89f2cd656ebfd3
--- /dev/null
+++ b/fs/xfs/quota/xfs_qm.c
@@ -0,0 +1,2848 @@
+/*
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_clnt.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_alloc.h"
+#include "xfs_dmapi.h"
+#include "xfs_quota.h"
+#include "xfs_mount.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode.h"
+#include "xfs_bmap.h"
+#include "xfs_bit.h"
+#include "xfs_rtalloc.h"
+#include "xfs_error.h"
+#include "xfs_itable.h"
+#include "xfs_rw.h"
+#include "xfs_acl.h"
+#include "xfs_cap.h"
+#include "xfs_mac.h"
+#include "xfs_attr.h"
+#include "xfs_buf_item.h"
+#include "xfs_trans_space.h"
+#include "xfs_utils.h"
+
+#include "xfs_qm.h"
+
+/*
+ * The global quota manager. There is only one of these for the entire
+ * system, _not_ one per file system. XQM keeps track of the overall
+ * quota functionality, including maintaining the freelist and hash
+ * tables of dquots.
+ */
+mutex_t xfs_Gqm_lock;
+struct xfs_qm *xfs_Gqm;
+
+kmem_zone_t *qm_dqzone;
+kmem_zone_t *qm_dqtrxzone;
+kmem_shaker_t xfs_qm_shaker;
+
+STATIC void xfs_qm_list_init(xfs_dqlist_t *, char *, int);
+STATIC void xfs_qm_list_destroy(xfs_dqlist_t *);
+
+STATIC int xfs_qm_init_quotainos(xfs_mount_t *);
+STATIC int xfs_qm_shake(int, unsigned int);
+
+#ifdef DEBUG
+extern mutex_t qcheck_lock;
+#endif
+
+#ifdef QUOTADEBUG
+#define XQM_LIST_PRINT(l, NXT, title) \
+{ \
+ xfs_dquot_t *dqp; int i = 0; \
+ cmn_err(CE_DEBUG, "%s (#%d)", title, (int) (l)->qh_nelems); \
+ for (dqp = (l)->qh_next; dqp != NULL; dqp = dqp->NXT) { \
+ cmn_err(CE_DEBUG, " %d. \"%d (%s)\" " \
+ "bcnt = %d, icnt = %d, refs = %d", \
+ ++i, (int) INT_GET(dqp->q_core.d_id, ARCH_CONVERT), \
+ DQFLAGTO_TYPESTR(dqp), \
+ (int) INT_GET(dqp->q_core.d_bcount, ARCH_CONVERT), \
+ (int) INT_GET(dqp->q_core.d_icount, ARCH_CONVERT), \
+ (int) dqp->q_nrefs); } \
+}
+#else
+#define XQM_LIST_PRINT(l, NXT, title) do { } while (0)
+#endif
+
+/*
+ * Initialize the XQM structure.
+ * Note that there is not one quota manager per file system.
+ */
+STATIC struct xfs_qm *
+xfs_Gqm_init(void)
+{
+ xfs_qm_t *xqm;
+ int hsize, i;
+
+ xqm = kmem_zalloc(sizeof(xfs_qm_t), KM_SLEEP);
+ ASSERT(xqm);
+
+ /*
+ * Initialize the dquot hash tables.
+ */
+ hsize = (DQUOT_HASH_HEURISTIC < XFS_QM_NCSIZE_THRESHOLD) ?
+ XFS_QM_HASHSIZE_LOW : XFS_QM_HASHSIZE_HIGH;
+ xqm->qm_dqhashmask = hsize - 1;
+
+ xqm->qm_usr_dqhtable = (xfs_dqhash_t *)kmem_zalloc(hsize *
+ sizeof(xfs_dqhash_t),
+ KM_SLEEP);
+ xqm->qm_grp_dqhtable = (xfs_dqhash_t *)kmem_zalloc(hsize *
+ sizeof(xfs_dqhash_t),
+ KM_SLEEP);
+ ASSERT(xqm->qm_usr_dqhtable != NULL);
+ ASSERT(xqm->qm_grp_dqhtable != NULL);
+
+ for (i = 0; i < hsize; i++) {
+ xfs_qm_list_init(&(xqm->qm_usr_dqhtable[i]), "uxdqh", i);
+ xfs_qm_list_init(&(xqm->qm_grp_dqhtable[i]), "gxdqh", i);
+ }
+
+ /*
+ * Freelist of all dquots of all file systems
+ */
+ xfs_qm_freelist_init(&(xqm->qm_dqfreelist));
+
+ /*
+ * dquot zone. we register our own low-memory callback.
+ */
+ if (!qm_dqzone) {
+ xqm->qm_dqzone = kmem_zone_init(sizeof(xfs_dquot_t),
+ "xfs_dquots");
+ qm_dqzone = xqm->qm_dqzone;
+ } else
+ xqm->qm_dqzone = qm_dqzone;
+
+ xfs_qm_shaker = kmem_shake_register(xfs_qm_shake);
+
+ /*
+ * The t_dqinfo portion of transactions.
+ */
+ if (!qm_dqtrxzone) {
+ xqm->qm_dqtrxzone = kmem_zone_init(sizeof(xfs_dquot_acct_t),
+ "xfs_dqtrx");
+ qm_dqtrxzone = xqm->qm_dqtrxzone;
+ } else
+ xqm->qm_dqtrxzone = qm_dqtrxzone;
+
+ atomic_set(&xqm->qm_totaldquots, 0);
+ xqm->qm_dqfree_ratio = XFS_QM_DQFREE_RATIO;
+ xqm->qm_nrefs = 0;
+#ifdef DEBUG
+ mutex_init(&qcheck_lock, MUTEX_DEFAULT, "qchk");
+#endif
+ return xqm;
+}
+
+/*
+ * Destroy the global quota manager when its reference count goes to zero.
+ */
+void
+xfs_qm_destroy(
+ struct xfs_qm *xqm)
+{
+ int hsize, i;
+
+ ASSERT(xqm != NULL);
+ ASSERT(xqm->qm_nrefs == 0);
+ kmem_shake_deregister(xfs_qm_shaker);
+ hsize = xqm->qm_dqhashmask + 1;
+ for (i = 0; i < hsize; i++) {
+ xfs_qm_list_destroy(&(xqm->qm_usr_dqhtable[i]));
+ xfs_qm_list_destroy(&(xqm->qm_grp_dqhtable[i]));
+ }
+ kmem_free(xqm->qm_usr_dqhtable, hsize * sizeof(xfs_dqhash_t));
+ kmem_free(xqm->qm_grp_dqhtable, hsize * sizeof(xfs_dqhash_t));
+ xqm->qm_usr_dqhtable = NULL;
+ xqm->qm_grp_dqhtable = NULL;
+ xqm->qm_dqhashmask = 0;
+ xfs_qm_freelist_destroy(&(xqm->qm_dqfreelist));
+#ifdef DEBUG
+ mutex_destroy(&qcheck_lock);
+#endif
+ kmem_free(xqm, sizeof(xfs_qm_t));
+}
+
+/*
+ * Called at mount time to let XQM know that another file system is
+ * starting quotas. This isn't crucial information as the individual mount
+ * structures are pretty independent, but it helps the XQM keep a
+ * global view of what's going on.
+ */
+/* ARGSUSED */
+STATIC int
+xfs_qm_hold_quotafs_ref(
+ struct xfs_mount *mp)
+{
+ /*
+ * Need to lock the xfs_Gqm structure for things like this. For example,
+ * the structure could disappear between the entry to this routine and
+ * a HOLD operation if not locked.
+ */
+ XFS_QM_LOCK(xfs_Gqm);
+
+ if (xfs_Gqm == NULL)
+ xfs_Gqm = xfs_Gqm_init();
+ /*
+ * We can keep a list of all filesystems with quotas mounted for
+ * debugging and statistical purposes, but ...
+ * Just take a reference and get out.
+ */
+ XFS_QM_HOLD(xfs_Gqm);
+ XFS_QM_UNLOCK(xfs_Gqm);
+
+ return 0;
+}
+
+
+/*
+ * Release the reference that a filesystem took at mount time,
+ * so that we know when we need to destroy the entire quota manager.
+ */
+/* ARGSUSED */
+STATIC void
+xfs_qm_rele_quotafs_ref(
+ struct xfs_mount *mp)
+{
+ xfs_dquot_t *dqp, *nextdqp;
+
+ ASSERT(xfs_Gqm);
+ ASSERT(xfs_Gqm->qm_nrefs > 0);
+
+ /*
+ * Go thru the freelist and destroy all inactive dquots.
+ */
+ xfs_qm_freelist_lock(xfs_Gqm);
+
+ for (dqp = xfs_Gqm->qm_dqfreelist.qh_next;
+ dqp != (xfs_dquot_t *)&(xfs_Gqm->qm_dqfreelist); ) {
+ xfs_dqlock(dqp);
+ nextdqp = dqp->dq_flnext;
+ if (dqp->dq_flags & XFS_DQ_INACTIVE) {
+ ASSERT(dqp->q_mount == NULL);
+ ASSERT(! XFS_DQ_IS_DIRTY(dqp));
+ ASSERT(dqp->HL_PREVP == NULL);
+ ASSERT(dqp->MPL_PREVP == NULL);
+ XQM_FREELIST_REMOVE(dqp);
+ xfs_dqunlock(dqp);
+ xfs_qm_dqdestroy(dqp);
+ } else {
+ xfs_dqunlock(dqp);
+ }
+ dqp = nextdqp;
+ }
+ xfs_qm_freelist_unlock(xfs_Gqm);
+
+ /*
+ * Destroy the entire XQM. If somebody mounts with quotaon, this'll
+ * be restarted.
+ */
+ XFS_QM_LOCK(xfs_Gqm);
+ XFS_QM_RELE(xfs_Gqm);
+ if (xfs_Gqm->qm_nrefs == 0) {
+ xfs_qm_destroy(xfs_Gqm);
+ xfs_Gqm = NULL;
+ }
+ XFS_QM_UNLOCK(xfs_Gqm);
+}
+
+/*
+ * This is called at mount time from xfs_mountfs to initialize the quotainfo
+ * structure and start the global quotamanager (xfs_Gqm) if it hasn't done
+ * so already. Note that the superblock has not been read in yet.
+ */
+void
+xfs_qm_mount_quotainit(
+ xfs_mount_t *mp,
+ uint flags)
+{
+ /*
+ * User or group quotas has to be on.
+ */
+ ASSERT(flags & (XFSMNT_UQUOTA | XFSMNT_GQUOTA));
+
+ /*
+ * Initialize the flags in the mount structure. From this point
+ * onwards we look at m_qflags to figure out if quotas's ON/OFF, etc.
+ * Note that we enforce nothing if accounting is off.
+ * ie. XFSMNT_*QUOTA must be ON for XFSMNT_*QUOTAENF.
+ * It isn't necessary to take the quotaoff lock to do this; this is
+ * called from mount.
+ */
+ if (flags & XFSMNT_UQUOTA) {
+ mp->m_qflags |= (XFS_UQUOTA_ACCT | XFS_UQUOTA_ACTIVE);
+ if (flags & XFSMNT_UQUOTAENF)
+ mp->m_qflags |= XFS_UQUOTA_ENFD;
+ }
+ if (flags & XFSMNT_GQUOTA) {
+ mp->m_qflags |= (XFS_GQUOTA_ACCT | XFS_GQUOTA_ACTIVE);
+ if (flags & XFSMNT_GQUOTAENF)
+ mp->m_qflags |= XFS_GQUOTA_ENFD;
+ }
+}
+
+/*
+ * Just destroy the quotainfo structure.
+ */
+void
+xfs_qm_unmount_quotadestroy(
+ xfs_mount_t *mp)
+{
+ if (mp->m_quotainfo)
+ xfs_qm_destroy_quotainfo(mp);
+}
+
+
+/*
+ * This is called from xfs_mountfs to start quotas and initialize all
+ * necessary data structures like quotainfo. This is also responsible for
+ * running a quotacheck as necessary. We are guaranteed that the superblock
+ * is consistently read in at this point.
+ */
+int
+xfs_qm_mount_quotas(
+ xfs_mount_t *mp,
+ int mfsi_flags)
+{
+ unsigned long s;
+ int error = 0;
+ uint sbf;
+
+ /*
+ * If a file system had quotas running earlier, but decided to
+ * mount without -o quota/uquota/gquota options, revoke the
+ * quotachecked license, and bail out.
+ */
+ if (! XFS_IS_QUOTA_ON(mp) &&
+ (mp->m_sb.sb_qflags & (XFS_UQUOTA_ACCT|XFS_GQUOTA_ACCT))) {
+ mp->m_qflags = 0;
+ goto write_changes;
+ }
+
+ /*
+ * If quotas on realtime volumes is not supported, we disable
+ * quotas immediately.
+ */
+ if (mp->m_sb.sb_rextents) {
+ cmn_err(CE_NOTE,
+ "Cannot turn on quotas for realtime filesystem %s",
+ mp->m_fsname);
+ mp->m_qflags = 0;
+ goto write_changes;
+ }
+
+#if defined(DEBUG) && defined(XFS_LOUD_RECOVERY)
+ cmn_err(CE_NOTE, "Attempting to turn on disk quotas.");
+#endif
+
+ ASSERT(XFS_IS_QUOTA_RUNNING(mp));
+ /*
+ * Allocate the quotainfo structure inside the mount struct, and
+ * create quotainode(s), and change/rev superblock if necessary.
+ */
+ if ((error = xfs_qm_init_quotainfo(mp))) {
+ /*
+ * We must turn off quotas.
+ */
+ ASSERT(mp->m_quotainfo == NULL);
+ mp->m_qflags = 0;
+ goto write_changes;
+ }
+ /*
+ * If any of the quotas are not consistent, do a quotacheck.
+ */
+ if (XFS_QM_NEED_QUOTACHECK(mp) &&
+ !(mfsi_flags & XFS_MFSI_NO_QUOTACHECK)) {
+#ifdef DEBUG
+ cmn_err(CE_NOTE, "Doing a quotacheck. Please wait.");
+#endif
+ if ((error = xfs_qm_quotacheck(mp))) {
+ /* Quotacheck has failed and quotas have
+ * been disabled.
+ */
+ return XFS_ERROR(error);
+ }
+#ifdef DEBUG
+ cmn_err(CE_NOTE, "Done quotacheck.");
+#endif
+ }
+ write_changes:
+ /*
+ * We actually don't have to acquire the SB_LOCK at all.
+ * This can only be called from mount, and that's single threaded. XXX
+ */
+ s = XFS_SB_LOCK(mp);
+ sbf = mp->m_sb.sb_qflags;
+ mp->m_sb.sb_qflags = mp->m_qflags & XFS_MOUNT_QUOTA_ALL;
+ XFS_SB_UNLOCK(mp, s);
+
+ if (sbf != (mp->m_qflags & XFS_MOUNT_QUOTA_ALL)) {
+ if (xfs_qm_write_sb_changes(mp, XFS_SB_QFLAGS)) {
+ /*
+ * We could only have been turning quotas off.
+ * We aren't in very good shape actually because
+ * the incore structures are convinced that quotas are
+ * off, but the on disk superblock doesn't know that !
+ */
+ ASSERT(!(XFS_IS_QUOTA_RUNNING(mp)));
+ xfs_fs_cmn_err(CE_ALERT, mp,
+ "XFS mount_quotas: Superblock update failed!");
+ }
+ }
+
+ if (error) {
+ xfs_fs_cmn_err(CE_WARN, mp,
+ "Failed to initialize disk quotas.");
+ }
+ return XFS_ERROR(error);
+}
+
+/*
+ * Called from the vfsops layer.
+ */
+int
+xfs_qm_unmount_quotas(
+ xfs_mount_t *mp)
+{
+ xfs_inode_t *uqp, *gqp;
+ int error = 0;
+
+ /*
+ * Release the dquots that root inode, et al might be holding,
+ * before we flush quotas and blow away the quotainfo structure.
+ */
+ ASSERT(mp->m_rootip);
+ xfs_qm_dqdetach(mp->m_rootip);
+ if (mp->m_rbmip)
+ xfs_qm_dqdetach(mp->m_rbmip);
+ if (mp->m_rsumip)
+ xfs_qm_dqdetach(mp->m_rsumip);
+
+ /*
+ * Flush out the quota inodes.
+ */
+ uqp = gqp = NULL;
+ if (mp->m_quotainfo) {
+ if ((uqp = mp->m_quotainfo->qi_uquotaip) != NULL) {
+ xfs_ilock(uqp, XFS_ILOCK_EXCL);
+ xfs_iflock(uqp);
+ error = xfs_iflush(uqp, XFS_IFLUSH_SYNC);
+ xfs_iunlock(uqp, XFS_ILOCK_EXCL);
+ if (unlikely(error == EFSCORRUPTED)) {
+ XFS_ERROR_REPORT("xfs_qm_unmount_quotas(1)",
+ XFS_ERRLEVEL_LOW, mp);
+ goto out;
+ }
+ }
+ if ((gqp = mp->m_quotainfo->qi_gquotaip) != NULL) {
+ xfs_ilock(gqp, XFS_ILOCK_EXCL);
+ xfs_iflock(gqp);
+ error = xfs_iflush(gqp, XFS_IFLUSH_SYNC);
+ xfs_iunlock(gqp, XFS_ILOCK_EXCL);
+ if (unlikely(error == EFSCORRUPTED)) {
+ XFS_ERROR_REPORT("xfs_qm_unmount_quotas(2)",
+ XFS_ERRLEVEL_LOW, mp);
+ goto out;
+ }
+ }
+ }
+ if (uqp) {
+ XFS_PURGE_INODE(uqp);
+ mp->m_quotainfo->qi_uquotaip = NULL;
+ }
+ if (gqp) {
+ XFS_PURGE_INODE(gqp);
+ mp->m_quotainfo->qi_gquotaip = NULL;
+ }
+out:
+ return XFS_ERROR(error);
+}
+
+/*
+ * Flush all dquots of the given file system to disk. The dquots are
+ * _not_ purged from memory here, just their data written to disk.
+ */
+int
+xfs_qm_dqflush_all(
+ xfs_mount_t *mp,
+ int flags)
+{
+ int recl;
+ xfs_dquot_t *dqp;
+ int niters;
+ int error;
+
+ if (mp->m_quotainfo == NULL)
+ return (0);
+ niters = 0;
+again:
+ xfs_qm_mplist_lock(mp);
+ FOREACH_DQUOT_IN_MP(dqp, mp) {
+ xfs_dqlock(dqp);
+ if (! XFS_DQ_IS_DIRTY(dqp)) {
+ xfs_dqunlock(dqp);
+ continue;
+ }
+ xfs_dqtrace_entry(dqp, "FLUSHALL: DQDIRTY");
+ /* XXX a sentinel would be better */
+ recl = XFS_QI_MPLRECLAIMS(mp);
+ if (! xfs_qm_dqflock_nowait(dqp)) {
+ /*
+ * If we can't grab the flush lock then check
+ * to see if the dquot has been flushed delayed
+ * write. If so, grab its buffer and send it
+ * out immediately. We'll be able to acquire
+ * the flush lock when the I/O completes.
+ */
+ xfs_qm_dqflock_pushbuf_wait(dqp);
+ }
+ /*
+ * Let go of the mplist lock. We don't want to hold it
+ * across a disk write.
+ */
+ xfs_qm_mplist_unlock(mp);
+ error = xfs_qm_dqflush(dqp, flags);
+ xfs_dqunlock(dqp);
+ if (error)
+ return (error);
+
+ xfs_qm_mplist_lock(mp);
+ if (recl != XFS_QI_MPLRECLAIMS(mp)) {
+ xfs_qm_mplist_unlock(mp);
+ /* XXX restart limit */
+ goto again;
+ }
+ }
+
+ xfs_qm_mplist_unlock(mp);
+ /* return ! busy */
+ return (0);
+}
+/*
+ * Release the group dquot pointers the user dquots may be
+ * carrying around as a hint. mplist is locked on entry and exit.
+ */
+STATIC void
+xfs_qm_detach_gdquots(
+ xfs_mount_t *mp)
+{
+ xfs_dquot_t *dqp, *gdqp;
+ int nrecl;
+
+ again:
+ ASSERT(XFS_QM_IS_MPLIST_LOCKED(mp));
+ dqp = XFS_QI_MPLNEXT(mp);
+ while (dqp) {
+ xfs_dqlock(dqp);
+ if ((gdqp = dqp->q_gdquot)) {
+ xfs_dqlock(gdqp);
+ dqp->q_gdquot = NULL;
+ }
+ xfs_dqunlock(dqp);
+
+ if (gdqp) {
+ /*
+ * Can't hold the mplist lock across a dqput.
+ * XXXmust convert to marker based iterations here.
+ */
+ nrecl = XFS_QI_MPLRECLAIMS(mp);
+ xfs_qm_mplist_unlock(mp);
+ xfs_qm_dqput(gdqp);
+
+ xfs_qm_mplist_lock(mp);
+ if (nrecl != XFS_QI_MPLRECLAIMS(mp))
+ goto again;
+ }
+ dqp = dqp->MPL_NEXT;
+ }
+}
+
+/*
+ * Go through all the incore dquots of this file system and take them
+ * off the mplist and hashlist, if the dquot type matches the dqtype
+ * parameter. This is used when turning off quota accounting for
+ * users and/or groups, as well as when the filesystem is unmounting.
+ */
+STATIC int
+xfs_qm_dqpurge_int(
+ xfs_mount_t *mp,
+ uint flags) /* QUOTAOFF/UMOUNTING/UQUOTA/GQUOTA */
+{
+ xfs_dquot_t *dqp;
+ uint dqtype;
+ int nrecl;
+ xfs_dquot_t *nextdqp;
+ int nmisses;
+
+ if (mp->m_quotainfo == NULL)
+ return (0);
+
+ dqtype = (flags & XFS_QMOPT_UQUOTA) ? XFS_DQ_USER : 0;
+ dqtype |= (flags & XFS_QMOPT_GQUOTA) ? XFS_DQ_GROUP : 0;
+
+ xfs_qm_mplist_lock(mp);
+
+ /*
+ * In the first pass through all incore dquots of this filesystem,
+ * we release the group dquot pointers the user dquots may be
+ * carrying around as a hint. We need to do this irrespective of
+ * what's being turned off.
+ */
+ xfs_qm_detach_gdquots(mp);
+
+ again:
+ nmisses = 0;
+ ASSERT(XFS_QM_IS_MPLIST_LOCKED(mp));
+ /*
+ * Try to get rid of all of the unwanted dquots. The idea is to
+ * get them off mplist and hashlist, but leave them on freelist.
+ */
+ dqp = XFS_QI_MPLNEXT(mp);
+ while (dqp) {
+ /*
+ * It's OK to look at the type without taking dqlock here.
+ * We're holding the mplist lock here, and that's needed for
+ * a dqreclaim.
+ */
+ if ((dqp->dq_flags & dqtype) == 0) {
+ dqp = dqp->MPL_NEXT;
+ continue;
+ }
+
+ if (! xfs_qm_dqhashlock_nowait(dqp)) {
+ nrecl = XFS_QI_MPLRECLAIMS(mp);
+ xfs_qm_mplist_unlock(mp);
+ XFS_DQ_HASH_LOCK(dqp->q_hash);
+ xfs_qm_mplist_lock(mp);
+
+ /*
+ * XXXTheoretically, we can get into a very long
+ * ping pong game here.
+ * No one can be adding dquots to the mplist at
+ * this point, but somebody might be taking things off.
+ */
+ if (nrecl != XFS_QI_MPLRECLAIMS(mp)) {
+ XFS_DQ_HASH_UNLOCK(dqp->q_hash);
+ goto again;
+ }
+ }
+
+ /*
+ * Take the dquot off the mplist and hashlist. It may remain on
+ * freelist in INACTIVE state.
+ */
+ nextdqp = dqp->MPL_NEXT;
+ nmisses += xfs_qm_dqpurge(dqp, flags);
+ dqp = nextdqp;
+ }
+ xfs_qm_mplist_unlock(mp);
+ return nmisses;
+}
+
+int
+xfs_qm_dqpurge_all(
+ xfs_mount_t *mp,
+ uint flags)
+{
+ int ndquots;
+
+ /*
+ * Purge the dquot cache.
+ * None of the dquots should really be busy at this point.
+ */
+ if (mp->m_quotainfo) {
+ while ((ndquots = xfs_qm_dqpurge_int(mp, flags))) {
+ delay(ndquots * 10);
+ }
+ }
+ return 0;
+}
+
+STATIC int
+xfs_qm_dqattach_one(
+ xfs_inode_t *ip,
+ xfs_dqid_t id,
+ uint type,
+ uint doalloc,
+ uint dolock,
+ xfs_dquot_t *udqhint, /* hint */
+ xfs_dquot_t **IO_idqpp)
+{
+ xfs_dquot_t *dqp;
+ int error;
+
+ ASSERT(XFS_ISLOCKED_INODE_EXCL(ip));
+ error = 0;
+ /*
+ * See if we already have it in the inode itself. IO_idqpp is
+ * &i_udquot or &i_gdquot. This made the code look weird, but
+ * made the logic a lot simpler.
+ */
+ if ((dqp = *IO_idqpp)) {
+ if (dolock)
+ xfs_dqlock(dqp);
+ xfs_dqtrace_entry(dqp, "DQATTACH: found in ip");
+ goto done;
+ }
+
+ /*
+ * udqhint is the i_udquot field in inode, and is non-NULL only
+ * when the type arg is XFS_DQ_GROUP. Its purpose is to save a
+ * lookup by dqid (xfs_qm_dqget) by caching a group dquot inside
+ * the user dquot.
+ */
+ ASSERT(!udqhint || type == XFS_DQ_GROUP);
+ if (udqhint && !dolock)
+ xfs_dqlock(udqhint);
+
+ /*
+ * No need to take dqlock to look at the id.
+ * The ID can't change until it gets reclaimed, and it won't
+ * be reclaimed as long as we have a ref from inode and we hold
+ * the ilock.
+ */
+ if (udqhint &&
+ (dqp = udqhint->q_gdquot) &&
+ (INT_GET(dqp->q_core.d_id, ARCH_CONVERT) == id)) {
+ ASSERT(XFS_DQ_IS_LOCKED(udqhint));
+ xfs_dqlock(dqp);
+ XFS_DQHOLD(dqp);
+ ASSERT(*IO_idqpp == NULL);
+ *IO_idqpp = dqp;
+ if (!dolock) {
+ xfs_dqunlock(dqp);
+ xfs_dqunlock(udqhint);
+ }
+ goto done;
+ }
+ /*
+ * We can't hold a dquot lock when we call the dqget code.
+ * We'll deadlock in no time, because of (not conforming to)
+ * lock ordering - the inodelock comes before any dquot lock,
+ * and we may drop and reacquire the ilock in xfs_qm_dqget().
+ */
+ if (udqhint)
+ xfs_dqunlock(udqhint);
+ /*
+ * Find the dquot from somewhere. This bumps the
+ * reference count of dquot and returns it locked.
+ * This can return ENOENT if dquot didn't exist on
+ * disk and we didn't ask it to allocate;
+ * ESRCH if quotas got turned off suddenly.
+ */
+ if ((error = xfs_qm_dqget(ip->i_mount, ip, id, type,
+ doalloc|XFS_QMOPT_DOWARN, &dqp))) {
+ if (udqhint && dolock)
+ xfs_dqlock(udqhint);
+ goto done;
+ }
+
+ xfs_dqtrace_entry(dqp, "DQATTACH: found by dqget");
+ /*
+ * dqget may have dropped and re-acquired the ilock, but it guarantees
+ * that the dquot returned is the one that should go in the inode.
+ */
+ *IO_idqpp = dqp;
+ ASSERT(dqp);
+ ASSERT(XFS_DQ_IS_LOCKED(dqp));
+ if (! dolock) {
+ xfs_dqunlock(dqp);
+ goto done;
+ }
+ if (! udqhint)
+ goto done;
+
+ ASSERT(udqhint);
+ ASSERT(dolock);
+ ASSERT(XFS_DQ_IS_LOCKED(dqp));
+ if (! xfs_qm_dqlock_nowait(udqhint)) {
+ xfs_dqunlock(dqp);
+ xfs_dqlock(udqhint);
+ xfs_dqlock(dqp);
+ }
+ done:
+#ifdef QUOTADEBUG
+ if (udqhint) {
+ if (dolock)
+ ASSERT(XFS_DQ_IS_LOCKED(udqhint));
+ }
+ if (! error) {
+ if (dolock)
+ ASSERT(XFS_DQ_IS_LOCKED(dqp));
+ }
+#endif
+ return (error);
+}
+
+
+/*
+ * Given a udquot and gdquot, attach a ptr to the group dquot in the
+ * udquot as a hint for future lookups. The idea sounds simple, but the
+ * execution isn't, because the udquot might have a group dquot attached
+ * already and getting rid of that gets us into lock ordering contraints.
+ * The process is complicated more by the fact that the dquots may or may not
+ * be locked on entry.
+ */
+STATIC void
+xfs_qm_dqattach_grouphint(
+ xfs_dquot_t *udq,
+ xfs_dquot_t *gdq,
+ uint locked)
+{
+ xfs_dquot_t *tmp;
+
+#ifdef QUOTADEBUG
+ if (locked) {
+ ASSERT(XFS_DQ_IS_LOCKED(udq));
+ ASSERT(XFS_DQ_IS_LOCKED(gdq));
+ }
+#endif
+ if (! locked)
+ xfs_dqlock(udq);
+
+ if ((tmp = udq->q_gdquot)) {
+ if (tmp == gdq) {
+ if (! locked)
+ xfs_dqunlock(udq);
+ return;
+ }
+
+ udq->q_gdquot = NULL;
+ /*
+ * We can't keep any dqlocks when calling dqrele,
+ * because the freelist lock comes before dqlocks.
+ */
+ xfs_dqunlock(udq);
+ if (locked)
+ xfs_dqunlock(gdq);
+ /*
+ * we took a hard reference once upon a time in dqget,
+ * so give it back when the udquot no longer points at it
+ * dqput() does the unlocking of the dquot.
+ */
+ xfs_qm_dqrele(tmp);
+
+ xfs_dqlock(udq);
+ xfs_dqlock(gdq);
+
+ } else {
+ ASSERT(XFS_DQ_IS_LOCKED(udq));
+ if (! locked) {
+ xfs_dqlock(gdq);
+ }
+ }
+
+ ASSERT(XFS_DQ_IS_LOCKED(udq));
+ ASSERT(XFS_DQ_IS_LOCKED(gdq));
+ /*
+ * Somebody could have attached a gdquot here,
+ * when we dropped the uqlock. If so, just do nothing.
+ */
+ if (udq->q_gdquot == NULL) {
+ XFS_DQHOLD(gdq);
+ udq->q_gdquot = gdq;
+ }
+ if (! locked) {
+ xfs_dqunlock(gdq);
+ xfs_dqunlock(udq);
+ }
+}
+
+
+/*
+ * Given a locked inode, attach dquot(s) to it, taking UQUOTAON / GQUOTAON
+ * in to account.
+ * If XFS_QMOPT_DQALLOC, the dquot(s) will be allocated if needed.
+ * If XFS_QMOPT_DQLOCK, the dquot(s) will be returned locked. This option pretty
+ * much made this code a complete mess, but it has been pretty useful.
+ * If XFS_QMOPT_ILOCKED, then inode sent is already locked EXCL.
+ * Inode may get unlocked and relocked in here, and the caller must deal with
+ * the consequences.
+ */
+int
+xfs_qm_dqattach(
+ xfs_inode_t *ip,
+ uint flags)
+{
+ xfs_mount_t *mp = ip->i_mount;
+ uint nquotas = 0;
+ int error = 0;
+
+ if ((! XFS_IS_QUOTA_ON(mp)) ||
+ (! XFS_NOT_DQATTACHED(mp, ip)) ||
+ (ip->i_ino == mp->m_sb.sb_uquotino) ||
+ (ip->i_ino == mp->m_sb.sb_gquotino))
+ return (0);
+
+ ASSERT((flags & XFS_QMOPT_ILOCKED) == 0 ||
+ XFS_ISLOCKED_INODE_EXCL(ip));
+
+ if (! (flags & XFS_QMOPT_ILOCKED))
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
+
+ if (XFS_IS_UQUOTA_ON(mp)) {
+ error = xfs_qm_dqattach_one(ip, ip->i_d.di_uid, XFS_DQ_USER,
+ flags & XFS_QMOPT_DQALLOC,
+ flags & XFS_QMOPT_DQLOCK,
+ NULL, &ip->i_udquot);
+ if (error)
+ goto done;
+ nquotas++;
+ }
+ ASSERT(XFS_ISLOCKED_INODE_EXCL(ip));
+ if (XFS_IS_GQUOTA_ON(mp)) {
+ error = xfs_qm_dqattach_one(ip, ip->i_d.di_gid, XFS_DQ_GROUP,
+ flags & XFS_QMOPT_DQALLOC,
+ flags & XFS_QMOPT_DQLOCK,
+ ip->i_udquot, &ip->i_gdquot);
+ /*
+ * Don't worry about the udquot that we may have
+ * attached above. It'll get detached, if not already.
+ */
+ if (error)
+ goto done;
+ nquotas++;
+ }
+
+ /*
+ * Attach this group quota to the user quota as a hint.
+ * This WON'T, in general, result in a thrash.
+ */
+ if (nquotas == 2) {
+ ASSERT(XFS_ISLOCKED_INODE_EXCL(ip));
+ ASSERT(ip->i_udquot);
+ ASSERT(ip->i_gdquot);
+
+ /*
+ * We may or may not have the i_udquot locked at this point,
+ * but this check is OK since we don't depend on the i_gdquot to
+ * be accurate 100% all the time. It is just a hint, and this
+ * will succeed in general.
+ */
+ if (ip->i_udquot->q_gdquot == ip->i_gdquot)
+ goto done;
+ /*
+ * Attach i_gdquot to the gdquot hint inside the i_udquot.
+ */
+ xfs_qm_dqattach_grouphint(ip->i_udquot, ip->i_gdquot,
+ flags & XFS_QMOPT_DQLOCK);
+ }
+
+ done:
+
+#ifdef QUOTADEBUG
+ if (! error) {
+ if (ip->i_udquot) {
+ if (flags & XFS_QMOPT_DQLOCK)
+ ASSERT(XFS_DQ_IS_LOCKED(ip->i_udquot));
+ }
+ if (ip->i_gdquot) {
+ if (flags & XFS_QMOPT_DQLOCK)
+ ASSERT(XFS_DQ_IS_LOCKED(ip->i_gdquot));
+ }
+ if (XFS_IS_UQUOTA_ON(mp))
+ ASSERT(ip->i_udquot);
+ if (XFS_IS_GQUOTA_ON(mp))
+ ASSERT(ip->i_gdquot);
+ }
+#endif
+
+ if (! (flags & XFS_QMOPT_ILOCKED))
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+
+#ifdef QUOTADEBUG
+ else
+ ASSERT(XFS_ISLOCKED_INODE_EXCL(ip));
+#endif
+ return (error);
+}
+
+/*
+ * Release dquots (and their references) if any.
+ * The inode should be locked EXCL except when this's called by
+ * xfs_ireclaim.
+ */
+void
+xfs_qm_dqdetach(
+ xfs_inode_t *ip)
+{
+ if (!(ip->i_udquot || ip->i_gdquot))
+ return;
+
+ ASSERT(ip->i_ino != ip->i_mount->m_sb.sb_uquotino);
+ ASSERT(ip->i_ino != ip->i_mount->m_sb.sb_gquotino);
+ if (ip->i_udquot)
+ xfs_dqtrace_entry_ino(ip->i_udquot, "DQDETTACH", ip);
+ if (ip->i_udquot) {
+ xfs_qm_dqrele(ip->i_udquot);
+ ip->i_udquot = NULL;
+ }
+ if (ip->i_gdquot) {
+ xfs_qm_dqrele(ip->i_gdquot);
+ ip->i_gdquot = NULL;
+ }
+}
+
+/*
+ * This is called by VFS_SYNC and flags arg determines the caller,
+ * and its motives, as done in xfs_sync.
+ *
+ * vfs_sync: SYNC_FSDATA|SYNC_ATTR|SYNC_BDFLUSH 0x31
+ * syscall sync: SYNC_FSDATA|SYNC_ATTR|SYNC_DELWRI 0x25
+ * umountroot : SYNC_WAIT | SYNC_CLOSE | SYNC_ATTR | SYNC_FSDATA
+ */
+
+int
+xfs_qm_sync(
+ xfs_mount_t *mp,
+ short flags)
+{
+ int recl, restarts;
+ xfs_dquot_t *dqp;
+ uint flush_flags;
+ boolean_t nowait;
+ int error;
+
+ restarts = 0;
+ /*
+ * We won't block unless we are asked to.
+ */
+ nowait = (boolean_t)(flags & SYNC_BDFLUSH || (flags & SYNC_WAIT) == 0);
+
+ again:
+ xfs_qm_mplist_lock(mp);
+ /*
+ * dqpurge_all() also takes the mplist lock and iterate thru all dquots
+ * in quotaoff. However, if the QUOTA_ACTIVE bits are not cleared
+ * when we have the mplist lock, we know that dquots will be consistent
+ * as long as we have it locked.
+ */
+ if (! XFS_IS_QUOTA_ON(mp)) {
+ xfs_qm_mplist_unlock(mp);
+ return (0);
+ }
+ FOREACH_DQUOT_IN_MP(dqp, mp) {
+ /*
+ * If this is vfs_sync calling, then skip the dquots that
+ * don't 'seem' to be dirty. ie. don't acquire dqlock.
+ * This is very similar to what xfs_sync does with inodes.
+ */
+ if (flags & SYNC_BDFLUSH) {
+ if (! XFS_DQ_IS_DIRTY(dqp))
+ continue;
+ }
+
+ if (nowait) {
+ /*
+ * Try to acquire the dquot lock. We are NOT out of
+ * lock order, but we just don't want to wait for this
+ * lock, unless somebody wanted us to.
+ */
+ if (! xfs_qm_dqlock_nowait(dqp))
+ continue;
+ } else {
+ xfs_dqlock(dqp);
+ }
+
+ /*
+ * Now, find out for sure if this dquot is dirty or not.
+ */
+ if (! XFS_DQ_IS_DIRTY(dqp)) {
+ xfs_dqunlock(dqp);
+ continue;
+ }
+
+ /* XXX a sentinel would be better */
+ recl = XFS_QI_MPLRECLAIMS(mp);
+ if (! xfs_qm_dqflock_nowait(dqp)) {
+ if (nowait) {
+ xfs_dqunlock(dqp);
+ continue;
+ }
+ /*
+ * If we can't grab the flush lock then if the caller
+ * really wanted us to give this our best shot,
+ * see if we can give a push to the buffer before we wait
+ * on the flush lock. At this point, we know that
+ * eventhough the dquot is being flushed,
+ * it has (new) dirty data.
+ */
+ xfs_qm_dqflock_pushbuf_wait(dqp);
+ }
+ /*
+ * Let go of the mplist lock. We don't want to hold it
+ * across a disk write
+ */
+ flush_flags = (nowait) ? XFS_QMOPT_DELWRI : XFS_QMOPT_SYNC;
+ xfs_qm_mplist_unlock(mp);
+ xfs_dqtrace_entry(dqp, "XQM_SYNC: DQFLUSH");
+ error = xfs_qm_dqflush(dqp, flush_flags);
+ xfs_dqunlock(dqp);
+ if (error && XFS_FORCED_SHUTDOWN(mp))
+ return(0); /* Need to prevent umount failure */
+ else if (error)
+ return (error);
+
+ xfs_qm_mplist_lock(mp);
+ if (recl != XFS_QI_MPLRECLAIMS(mp)) {
+ if (++restarts >= XFS_QM_SYNC_MAX_RESTARTS)
+ break;
+
+ xfs_qm_mplist_unlock(mp);
+ goto again;
+ }
+ }
+
+ xfs_qm_mplist_unlock(mp);
+ return (0);
+}
+
+
+/*
+ * This initializes all the quota information that's kept in the
+ * mount structure
+ */
+int
+xfs_qm_init_quotainfo(
+ xfs_mount_t *mp)
+{
+ xfs_quotainfo_t *qinf;
+ int error;
+ xfs_dquot_t *dqp;
+
+ ASSERT(XFS_IS_QUOTA_RUNNING(mp));
+
+ /*
+ * Tell XQM that we exist as soon as possible.
+ */
+ if ((error = xfs_qm_hold_quotafs_ref(mp))) {
+ return (error);
+ }
+
+ qinf = mp->m_quotainfo = kmem_zalloc(sizeof(xfs_quotainfo_t), KM_SLEEP);
+
+ /*
+ * See if quotainodes are setup, and if not, allocate them,
+ * and change the superblock accordingly.
+ */
+ if ((error = xfs_qm_init_quotainos(mp))) {
+ kmem_free(qinf, sizeof(xfs_quotainfo_t));
+ mp->m_quotainfo = NULL;
+ return (error);
+ }
+
+ spinlock_init(&qinf->qi_pinlock, "xfs_qinf_pin");
+ xfs_qm_list_init(&qinf->qi_dqlist, "mpdqlist", 0);
+ qinf->qi_dqreclaims = 0;
+
+ /* mutex used to serialize quotaoffs */
+ mutex_init(&qinf->qi_quotaofflock, MUTEX_DEFAULT, "qoff");
+
+ /* Precalc some constants */
+ qinf->qi_dqchunklen = XFS_FSB_TO_BB(mp, XFS_DQUOT_CLUSTER_SIZE_FSB);
+ ASSERT(qinf->qi_dqchunklen);
+ qinf->qi_dqperchunk = BBTOB(qinf->qi_dqchunklen);
+ do_div(qinf->qi_dqperchunk, sizeof(xfs_dqblk_t));
+
+ mp->m_qflags |= (mp->m_sb.sb_qflags & XFS_ALL_QUOTA_CHKD);
+
+ /*
+ * We try to get the limits from the superuser's limits fields.
+ * This is quite hacky, but it is standard quota practice.
+ * We look at the USR dquot with id == 0 first, but if user quotas
+ * are not enabled we goto the GRP dquot with id == 0.
+ * We don't really care to keep separate default limits for user
+ * and group quotas, at least not at this point.
+ */
+ error = xfs_qm_dqget(mp, NULL, (xfs_dqid_t)0,
+ (XFS_IS_UQUOTA_RUNNING(mp)) ?
+ XFS_DQ_USER : XFS_DQ_GROUP,
+ XFS_QMOPT_DQSUSER|XFS_QMOPT_DOWARN,
+ &dqp);
+ if (! error) {
+ xfs_disk_dquot_t *ddqp = &dqp->q_core;
+
+ /*
+ * The warnings and timers set the grace period given to
+ * a user or group before he or she can not perform any
+ * more writing. If it is zero, a default is used.
+ */
+ qinf->qi_btimelimit =
+ INT_GET(ddqp->d_btimer, ARCH_CONVERT) ?
+ INT_GET(ddqp->d_btimer, ARCH_CONVERT) :
+ XFS_QM_BTIMELIMIT;
+ qinf->qi_itimelimit =
+ INT_GET(ddqp->d_itimer, ARCH_CONVERT) ?
+ INT_GET(ddqp->d_itimer, ARCH_CONVERT) :
+ XFS_QM_ITIMELIMIT;
+ qinf->qi_rtbtimelimit =
+ INT_GET(ddqp->d_rtbtimer, ARCH_CONVERT) ?
+ INT_GET(ddqp->d_rtbtimer, ARCH_CONVERT) :
+ XFS_QM_RTBTIMELIMIT;
+ qinf->qi_bwarnlimit =
+ INT_GET(ddqp->d_bwarns, ARCH_CONVERT) ?
+ INT_GET(ddqp->d_bwarns, ARCH_CONVERT) :
+ XFS_QM_BWARNLIMIT;
+ qinf->qi_iwarnlimit =
+ INT_GET(ddqp->d_iwarns, ARCH_CONVERT) ?
+ INT_GET(ddqp->d_iwarns, ARCH_CONVERT) :
+ XFS_QM_IWARNLIMIT;
+ qinf->qi_bhardlimit =
+ INT_GET(ddqp->d_blk_hardlimit, ARCH_CONVERT);
+ qinf->qi_bsoftlimit =
+ INT_GET(ddqp->d_blk_softlimit, ARCH_CONVERT);
+ qinf->qi_ihardlimit =
+ INT_GET(ddqp->d_ino_hardlimit, ARCH_CONVERT);
+ qinf->qi_isoftlimit =
+ INT_GET(ddqp->d_ino_softlimit, ARCH_CONVERT);
+ qinf->qi_rtbhardlimit =
+ INT_GET(ddqp->d_rtb_hardlimit, ARCH_CONVERT);
+ qinf->qi_rtbsoftlimit =
+ INT_GET(ddqp->d_rtb_softlimit, ARCH_CONVERT);
+
+ /*
+ * We sent the XFS_QMOPT_DQSUSER flag to dqget because
+ * we don't want this dquot cached. We haven't done a
+ * quotacheck yet, and quotacheck doesn't like incore dquots.
+ */
+ xfs_qm_dqdestroy(dqp);
+ } else {
+ qinf->qi_btimelimit = XFS_QM_BTIMELIMIT;
+ qinf->qi_itimelimit = XFS_QM_ITIMELIMIT;
+ qinf->qi_rtbtimelimit = XFS_QM_RTBTIMELIMIT;
+ qinf->qi_bwarnlimit = XFS_QM_BWARNLIMIT;
+ qinf->qi_iwarnlimit = XFS_QM_IWARNLIMIT;
+ }
+
+ return (0);
+}
+
+
+/*
+ * Gets called when unmounting a filesystem or when all quotas get
+ * turned off.
+ * This purges the quota inodes, destroys locks and frees itself.
+ */
+void
+xfs_qm_destroy_quotainfo(
+ xfs_mount_t *mp)
+{
+ xfs_quotainfo_t *qi;
+
+ qi = mp->m_quotainfo;
+ ASSERT(qi != NULL);
+ ASSERT(xfs_Gqm != NULL);
+
+ /*
+ * Release the reference that XQM kept, so that we know
+ * when the XQM structure should be freed. We cannot assume
+ * that xfs_Gqm is non-null after this point.
+ */
+ xfs_qm_rele_quotafs_ref(mp);
+
+ spinlock_destroy(&qi->qi_pinlock);
+ xfs_qm_list_destroy(&qi->qi_dqlist);
+
+ if (qi->qi_uquotaip) {
+ XFS_PURGE_INODE(qi->qi_uquotaip);
+ qi->qi_uquotaip = NULL; /* paranoia */
+ }
+ if (qi->qi_gquotaip) {
+ XFS_PURGE_INODE(qi->qi_gquotaip);
+ qi->qi_gquotaip = NULL;
+ }
+ mutex_destroy(&qi->qi_quotaofflock);
+ kmem_free(qi, sizeof(xfs_quotainfo_t));
+ mp->m_quotainfo = NULL;
+}
+
+
+
+/* ------------------- PRIVATE STATIC FUNCTIONS ----------------------- */
+
+/* ARGSUSED */
+STATIC void
+xfs_qm_list_init(
+ xfs_dqlist_t *list,
+ char *str,
+ int n)
+{
+ mutex_init(&list->qh_lock, MUTEX_DEFAULT, str);
+ list->qh_next = NULL;
+ list->qh_version = 0;
+ list->qh_nelems = 0;
+}
+
+STATIC void
+xfs_qm_list_destroy(
+ xfs_dqlist_t *list)
+{
+ mutex_destroy(&(list->qh_lock));
+}
+
+
+/*
+ * Stripped down version of dqattach. This doesn't attach, or even look at the
+ * dquots attached to the inode. The rationale is that there won't be any
+ * attached at the time this is called from quotacheck.
+ */
+STATIC int
+xfs_qm_dqget_noattach(
+ xfs_inode_t *ip,
+ xfs_dquot_t **O_udqpp,
+ xfs_dquot_t **O_gdqpp)
+{
+ int error;
+ xfs_mount_t *mp;
+ xfs_dquot_t *udqp, *gdqp;
+
+ ASSERT(XFS_ISLOCKED_INODE_EXCL(ip));
+ mp = ip->i_mount;
+ udqp = NULL;
+ gdqp = NULL;
+
+ if (XFS_IS_UQUOTA_ON(mp)) {
+ ASSERT(ip->i_udquot == NULL);
+ /*
+ * We want the dquot allocated if it doesn't exist.
+ */
+ if ((error = xfs_qm_dqget(mp, ip, ip->i_d.di_uid, XFS_DQ_USER,
+ XFS_QMOPT_DQALLOC | XFS_QMOPT_DOWARN,
+ &udqp))) {
+ /*
+ * Shouldn't be able to turn off quotas here.
+ */
+ ASSERT(error != ESRCH);
+ ASSERT(error != ENOENT);
+ return (error);
+ }
+ ASSERT(udqp);
+ }
+
+ if (XFS_IS_GQUOTA_ON(mp)) {
+ ASSERT(ip->i_gdquot == NULL);
+ if (udqp)
+ xfs_dqunlock(udqp);
+ if ((error = xfs_qm_dqget(mp, ip, ip->i_d.di_gid, XFS_DQ_GROUP,
+ XFS_QMOPT_DQALLOC|XFS_QMOPT_DOWARN,
+ &gdqp))) {
+ if (udqp)
+ xfs_qm_dqrele(udqp);
+ ASSERT(error != ESRCH);
+ ASSERT(error != ENOENT);
+ return (error);
+ }
+ ASSERT(gdqp);
+
+ /* Reacquire the locks in the right order */
+ if (udqp) {
+ if (! xfs_qm_dqlock_nowait(udqp)) {
+ xfs_dqunlock(gdqp);
+ xfs_dqlock(udqp);
+ xfs_dqlock(gdqp);
+ }
+ }
+ }
+
+ *O_udqpp = udqp;
+ *O_gdqpp = gdqp;
+
+#ifdef QUOTADEBUG
+ if (udqp) ASSERT(XFS_DQ_IS_LOCKED(udqp));
+ if (gdqp) ASSERT(XFS_DQ_IS_LOCKED(gdqp));
+#endif
+ return (0);
+}
+
+/*
+ * Create an inode and return with a reference already taken, but unlocked
+ * This is how we create quota inodes
+ */
+STATIC int
+xfs_qm_qino_alloc(
+ xfs_mount_t *mp,
+ xfs_inode_t **ip,
+ __int64_t sbfields,
+ uint flags)
+{
+ xfs_trans_t *tp;
+ int error;
+ unsigned long s;
+ cred_t zerocr;
+ int committed;
+
+ tp = xfs_trans_alloc(mp,XFS_TRANS_QM_QINOCREATE);
+ if ((error = xfs_trans_reserve(tp,
+ XFS_QM_QINOCREATE_SPACE_RES(mp),
+ XFS_CREATE_LOG_RES(mp), 0,
+ XFS_TRANS_PERM_LOG_RES,
+ XFS_CREATE_LOG_COUNT))) {
+ xfs_trans_cancel(tp, 0);
+ return (error);
+ }
+ memset(&zerocr, 0, sizeof(zerocr));
+
+ if ((error = xfs_dir_ialloc(&tp, mp->m_rootip, S_IFREG, 1, 0,
+ &zerocr, 0, 1, ip, &committed))) {
+ xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES |
+ XFS_TRANS_ABORT);
+ return (error);
+ }
+
+ /*
+ * Keep an extra reference to this quota inode. This inode is
+ * locked exclusively and joined to the transaction already.
+ */
+ ASSERT(XFS_ISLOCKED_INODE_EXCL(*ip));
+ VN_HOLD(XFS_ITOV((*ip)));
+
+ /*
+ * Make the changes in the superblock, and log those too.
+ * sbfields arg may contain fields other than *QUOTINO;
+ * VERSIONNUM for example.
+ */
+ s = XFS_SB_LOCK(mp);
+ if (flags & XFS_QMOPT_SBVERSION) {
+#if defined(DEBUG) && defined(XFS_LOUD_RECOVERY)
+ unsigned oldv = mp->m_sb.sb_versionnum;
+#endif
+ ASSERT(!XFS_SB_VERSION_HASQUOTA(&mp->m_sb));
+ ASSERT((sbfields & (XFS_SB_VERSIONNUM | XFS_SB_UQUOTINO |
+ XFS_SB_GQUOTINO | XFS_SB_QFLAGS)) ==
+ (XFS_SB_VERSIONNUM | XFS_SB_UQUOTINO |
+ XFS_SB_GQUOTINO | XFS_SB_QFLAGS));
+
+ XFS_SB_VERSION_ADDQUOTA(&mp->m_sb);
+ mp->m_sb.sb_uquotino = NULLFSINO;
+ mp->m_sb.sb_gquotino = NULLFSINO;
+
+ /* qflags will get updated _after_ quotacheck */
+ mp->m_sb.sb_qflags = 0;
+#if defined(DEBUG) && defined(XFS_LOUD_RECOVERY)
+ cmn_err(CE_NOTE,
+ "Old superblock version %x, converting to %x.",
+ oldv, mp->m_sb.sb_versionnum);
+#endif
+ }
+ if (flags & XFS_QMOPT_UQUOTA)
+ mp->m_sb.sb_uquotino = (*ip)->i_ino;
+ else
+ mp->m_sb.sb_gquotino = (*ip)->i_ino;
+ XFS_SB_UNLOCK(mp, s);
+ xfs_mod_sb(tp, sbfields);
+
+ if ((error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES,
+ NULL))) {
+ xfs_fs_cmn_err(CE_ALERT, mp, "XFS qino_alloc failed!");
+ return (error);
+ }
+ return (0);
+}
+
+
+STATIC int
+xfs_qm_reset_dqcounts(
+ xfs_mount_t *mp,
+ xfs_buf_t *bp,
+ xfs_dqid_t id,
+ uint type)
+{
+ xfs_disk_dquot_t *ddq;
+ int j;
+
+ xfs_buftrace("RESET DQUOTS", bp);
+ /*
+ * Reset all counters and timers. They'll be
+ * started afresh by xfs_qm_quotacheck.
+ */
+#ifdef DEBUG
+ j = XFS_FSB_TO_B(mp, XFS_DQUOT_CLUSTER_SIZE_FSB);
+ do_div(j, sizeof(xfs_dqblk_t));
+ ASSERT(XFS_QM_DQPERBLK(mp) == j);
+#endif
+ ddq = (xfs_disk_dquot_t *)XFS_BUF_PTR(bp);
+ for (j = 0; j < XFS_QM_DQPERBLK(mp); j++) {
+ /*
+ * Do a sanity check, and if needed, repair the dqblk. Don't
+ * output any warnings because it's perfectly possible to
+ * find unitialized dquot blks. See comment in xfs_qm_dqcheck.
+ */
+ (void) xfs_qm_dqcheck(ddq, id+j, type, XFS_QMOPT_DQREPAIR,
+ "xfs_quotacheck");
+ INT_SET(ddq->d_bcount, ARCH_CONVERT, 0ULL);
+ INT_SET(ddq->d_icount, ARCH_CONVERT, 0ULL);
+ INT_SET(ddq->d_rtbcount, ARCH_CONVERT, 0ULL);
+ INT_SET(ddq->d_btimer, ARCH_CONVERT, (time_t)0);
+ INT_SET(ddq->d_itimer, ARCH_CONVERT, (time_t)0);
+ INT_SET(ddq->d_bwarns, ARCH_CONVERT, 0UL);
+ INT_SET(ddq->d_iwarns, ARCH_CONVERT, 0UL);
+ ddq = (xfs_disk_dquot_t *) ((xfs_dqblk_t *)ddq + 1);
+ }
+
+ return (0);
+}
+
+STATIC int
+xfs_qm_dqiter_bufs(
+ xfs_mount_t *mp,
+ xfs_dqid_t firstid,
+ xfs_fsblock_t bno,
+ xfs_filblks_t blkcnt,
+ uint flags)
+{
+ xfs_buf_t *bp;
+ int error;
+ int notcommitted;
+ int incr;
+
+ ASSERT(blkcnt > 0);
+ notcommitted = 0;
+ incr = (blkcnt > XFS_QM_MAX_DQCLUSTER_LOGSZ) ?
+ XFS_QM_MAX_DQCLUSTER_LOGSZ : blkcnt;
+ error = 0;
+
+ /*
+ * Blkcnt arg can be a very big number, and might even be
+ * larger than the log itself. So, we have to break it up into
+ * manageable-sized transactions.
+ * Note that we don't start a permanent transaction here; we might
+ * not be able to get a log reservation for the whole thing up front,
+ * and we don't really care to either, because we just discard
+ * everything if we were to crash in the middle of this loop.
+ */
+ while (blkcnt--) {
+ error = xfs_trans_read_buf(mp, NULL, mp->m_ddev_targp,
+ XFS_FSB_TO_DADDR(mp, bno),
+ (int)XFS_QI_DQCHUNKLEN(mp), 0, &bp);
+ if (error)
+ break;
+
+ (void) xfs_qm_reset_dqcounts(mp, bp, firstid,
+ flags & XFS_QMOPT_UQUOTA ?
+ XFS_DQ_USER : XFS_DQ_GROUP);
+ xfs_bdwrite(mp, bp);
+ /*
+ * goto the next block.
+ */
+ bno++;
+ firstid += XFS_QM_DQPERBLK(mp);
+ }
+ return (error);
+}
+
+/*
+ * Iterate over all allocated USR/GRP dquots in the system, calling a
+ * caller supplied function for every chunk of dquots that we find.
+ */
+STATIC int
+xfs_qm_dqiterate(
+ xfs_mount_t *mp,
+ xfs_inode_t *qip,
+ uint flags)
+{
+ xfs_bmbt_irec_t *map;
+ int i, nmaps; /* number of map entries */
+ int error; /* return value */
+ xfs_fileoff_t lblkno;
+ xfs_filblks_t maxlblkcnt;
+ xfs_dqid_t firstid;
+ xfs_fsblock_t rablkno;
+ xfs_filblks_t rablkcnt;
+
+ error = 0;
+ /*
+ * This looks racey, but we can't keep an inode lock across a
+ * trans_reserve. But, this gets called during quotacheck, and that
+ * happens only at mount time which is single threaded.
+ */
+ if (qip->i_d.di_nblocks == 0)
+ return (0);
+
+ map = kmem_alloc(XFS_DQITER_MAP_SIZE * sizeof(*map), KM_SLEEP);
+
+ lblkno = 0;
+ maxlblkcnt = XFS_B_TO_FSB(mp, (xfs_ufsize_t)XFS_MAXIOFFSET(mp));
+ do {
+ nmaps = XFS_DQITER_MAP_SIZE;
+ /*
+ * We aren't changing the inode itself. Just changing
+ * some of its data. No new blocks are added here, and
+ * the inode is never added to the transaction.
+ */
+ xfs_ilock(qip, XFS_ILOCK_SHARED);
+ error = xfs_bmapi(NULL, qip, lblkno,
+ maxlblkcnt - lblkno,
+ XFS_BMAPI_METADATA,
+ NULL,
+ 0, map, &nmaps, NULL);
+ xfs_iunlock(qip, XFS_ILOCK_SHARED);
+ if (error)
+ break;
+
+ ASSERT(nmaps <= XFS_DQITER_MAP_SIZE);
+ for (i = 0; i < nmaps; i++) {
+ ASSERT(map[i].br_startblock != DELAYSTARTBLOCK);
+ ASSERT(map[i].br_blockcount);
+
+
+ lblkno += map[i].br_blockcount;
+
+ if (map[i].br_startblock == HOLESTARTBLOCK)
+ continue;
+
+ firstid = (xfs_dqid_t) map[i].br_startoff *
+ XFS_QM_DQPERBLK(mp);
+ /*
+ * Do a read-ahead on the next extent.
+ */
+ if ((i+1 < nmaps) &&
+ (map[i+1].br_startblock != HOLESTARTBLOCK)) {
+ rablkcnt = map[i+1].br_blockcount;
+ rablkno = map[i+1].br_startblock;
+ while (rablkcnt--) {
+ xfs_baread(mp->m_ddev_targp,
+ XFS_FSB_TO_DADDR(mp, rablkno),
+ (int)XFS_QI_DQCHUNKLEN(mp));
+ rablkno++;
+ }
+ }
+ /*
+ * Iterate thru all the blks in the extent and
+ * reset the counters of all the dquots inside them.
+ */
+ if ((error = xfs_qm_dqiter_bufs(mp,
+ firstid,
+ map[i].br_startblock,
+ map[i].br_blockcount,
+ flags))) {
+ break;
+ }
+ }
+
+ if (error)
+ break;
+ } while (nmaps > 0);
+
+ kmem_free(map, XFS_DQITER_MAP_SIZE * sizeof(*map));
+
+ return (error);
+}
+
+/*
+ * Called by dqusage_adjust in doing a quotacheck.
+ * Given the inode, and a dquot (either USR or GRP, doesn't matter),
+ * this updates its incore copy as well as the buffer copy. This is
+ * so that once the quotacheck is done, we can just log all the buffers,
+ * as opposed to logging numerous updates to individual dquots.
+ */
+STATIC void
+xfs_qm_quotacheck_dqadjust(
+ xfs_dquot_t *dqp,
+ xfs_qcnt_t nblks,
+ xfs_qcnt_t rtblks)
+{
+ ASSERT(XFS_DQ_IS_LOCKED(dqp));
+ xfs_dqtrace_entry(dqp, "QCHECK DQADJUST");
+ /*
+ * Adjust the inode count and the block count to reflect this inode's
+ * resource usage.
+ */
+ INT_MOD(dqp->q_core.d_icount, ARCH_CONVERT, +1);
+ dqp->q_res_icount++;
+ if (nblks) {
+ INT_MOD(dqp->q_core.d_bcount, ARCH_CONVERT, nblks);
+ dqp->q_res_bcount += nblks;
+ }
+ if (rtblks) {
+ INT_MOD(dqp->q_core.d_rtbcount, ARCH_CONVERT, rtblks);
+ dqp->q_res_rtbcount += rtblks;
+ }
+
+ /*
+ * Set default limits, adjust timers (since we changed usages)
+ */
+ if (! XFS_IS_SUSER_DQUOT(dqp)) {
+ xfs_qm_adjust_dqlimits(dqp->q_mount, &dqp->q_core);
+ xfs_qm_adjust_dqtimers(dqp->q_mount, &dqp->q_core);
+ }
+
+ dqp->dq_flags |= XFS_DQ_DIRTY;
+}
+
+STATIC int
+xfs_qm_get_rtblks(
+ xfs_inode_t *ip,
+ xfs_qcnt_t *O_rtblks)
+{
+ xfs_filblks_t rtblks; /* total rt blks */
+ xfs_ifork_t *ifp; /* inode fork pointer */
+ xfs_extnum_t nextents; /* number of extent entries */
+ xfs_bmbt_rec_t *base; /* base of extent array */
+ xfs_bmbt_rec_t *ep; /* pointer to an extent entry */
+ int error;
+
+ ASSERT(XFS_IS_REALTIME_INODE(ip));
+ ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
+ if (!(ifp->if_flags & XFS_IFEXTENTS)) {
+ if ((error = xfs_iread_extents(NULL, ip, XFS_DATA_FORK)))
+ return (error);
+ }
+ rtblks = 0;
+ nextents = ifp->if_bytes / sizeof(xfs_bmbt_rec_t);
+ base = &ifp->if_u1.if_extents[0];
+ for (ep = base; ep < &base[nextents]; ep++)
+ rtblks += xfs_bmbt_get_blockcount(ep);
+ *O_rtblks = (xfs_qcnt_t)rtblks;
+ return (0);
+}
+
+/*
+ * callback routine supplied to bulkstat(). Given an inumber, find its
+ * dquots and update them to account for resources taken by that inode.
+ */
+/* ARGSUSED */
+STATIC int
+xfs_qm_dqusage_adjust(
+ xfs_mount_t *mp, /* mount point for filesystem */
+ xfs_ino_t ino, /* inode number to get data for */
+ void __user *buffer, /* not used */
+ int ubsize, /* not used */
+ void *private_data, /* not used */
+ xfs_daddr_t bno, /* starting block of inode cluster */
+ int *ubused, /* not used */
+ void *dip, /* on-disk inode pointer (not used) */
+ int *res) /* result code value */
+{
+ xfs_inode_t *ip;
+ xfs_dquot_t *udqp, *gdqp;
+ xfs_qcnt_t nblks, rtblks;
+ int error;
+
+ ASSERT(XFS_IS_QUOTA_RUNNING(mp));
+
+ /*
+ * rootino must have its resources accounted for, not so with the quota
+ * inodes.
+ */
+ if (ino == mp->m_sb.sb_uquotino || ino == mp->m_sb.sb_gquotino) {
+ *res = BULKSTAT_RV_NOTHING;
+ return XFS_ERROR(EINVAL);
+ }
+
+ /*
+ * We don't _need_ to take the ilock EXCL. However, the xfs_qm_dqget
+ * interface expects the inode to be exclusively locked because that's
+ * the case in all other instances. It's OK that we do this because
+ * quotacheck is done only at mount time.
+ */
+ if ((error = xfs_iget(mp, NULL, ino, 0, XFS_ILOCK_EXCL, &ip, bno))) {
+ *res = BULKSTAT_RV_NOTHING;
+ return (error);
+ }
+
+ if (ip->i_d.di_mode == 0) {
+ xfs_iput_new(ip, XFS_ILOCK_EXCL);
+ *res = BULKSTAT_RV_NOTHING;
+ return XFS_ERROR(ENOENT);
+ }
+
+ /*
+ * Obtain the locked dquots. In case of an error (eg. allocation
+ * fails for ENOSPC), we return the negative of the error number
+ * to bulkstat, so that it can get propagated to quotacheck() and
+ * making us disable quotas for the file system.
+ */
+ if ((error = xfs_qm_dqget_noattach(ip, &udqp, &gdqp))) {
+ xfs_iput(ip, XFS_ILOCK_EXCL);
+ *res = BULKSTAT_RV_GIVEUP;
+ return (error);
+ }
+
+ rtblks = 0;
+ if (! XFS_IS_REALTIME_INODE(ip)) {
+ nblks = (xfs_qcnt_t)ip->i_d.di_nblocks;
+ } else {
+ /*
+ * Walk thru the extent list and count the realtime blocks.
+ */
+ if ((error = xfs_qm_get_rtblks(ip, &rtblks))) {
+ xfs_iput(ip, XFS_ILOCK_EXCL);
+ if (udqp)
+ xfs_qm_dqput(udqp);
+ if (gdqp)
+ xfs_qm_dqput(gdqp);
+ *res = BULKSTAT_RV_GIVEUP;
+ return (error);
+ }
+ nblks = (xfs_qcnt_t)ip->i_d.di_nblocks - rtblks;
+ }
+ ASSERT(ip->i_delayed_blks == 0);
+
+ /*
+ * We can't release the inode while holding its dquot locks.
+ * The inode can go into inactive and might try to acquire the dquotlocks.
+ * So, just unlock here and do a vn_rele at the end.
+ */
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+
+ /*
+ * Add the (disk blocks and inode) resources occupied by this
+ * inode to its dquots. We do this adjustment in the incore dquot,
+ * and also copy the changes to its buffer.
+ * We don't care about putting these changes in a transaction
+ * envelope because if we crash in the middle of a 'quotacheck'
+ * we have to start from the beginning anyway.
+ * Once we're done, we'll log all the dquot bufs.
+ *
+ * The *QUOTA_ON checks below may look pretty racey, but quotachecks
+ * and quotaoffs don't race. (Quotachecks happen at mount time only).
+ */
+ if (XFS_IS_UQUOTA_ON(mp)) {
+ ASSERT(udqp);
+ xfs_qm_quotacheck_dqadjust(udqp, nblks, rtblks);
+ xfs_qm_dqput(udqp);
+ }
+ if (XFS_IS_GQUOTA_ON(mp)) {
+ ASSERT(gdqp);
+ xfs_qm_quotacheck_dqadjust(gdqp, nblks, rtblks);
+ xfs_qm_dqput(gdqp);
+ }
+ /*
+ * Now release the inode. This will send it to 'inactive', and
+ * possibly even free blocks.
+ */
+ VN_RELE(XFS_ITOV(ip));
+
+ /*
+ * Goto next inode.
+ */
+ *res = BULKSTAT_RV_DIDONE;
+ return (0);
+}
+
+/*
+ * Walk thru all the filesystem inodes and construct a consistent view
+ * of the disk quota world. If the quotacheck fails, disable quotas.
+ */
+int
+xfs_qm_quotacheck(
+ xfs_mount_t *mp)
+{
+ int done, count, error;
+ xfs_ino_t lastino;
+ size_t structsz;
+ xfs_inode_t *uip, *gip;
+ uint flags;
+
+ count = INT_MAX;
+ structsz = 1;
+ lastino = 0;
+ flags = 0;
+
+ ASSERT(XFS_QI_UQIP(mp) || XFS_QI_GQIP(mp));
+ ASSERT(XFS_IS_QUOTA_RUNNING(mp));
+
+ /*
+ * There should be no cached dquots. The (simplistic) quotacheck
+ * algorithm doesn't like that.
+ */
+ ASSERT(XFS_QI_MPLNDQUOTS(mp) == 0);
+
+ cmn_err(CE_NOTE, "XFS quotacheck %s: Please wait.", mp->m_fsname);
+
+ /*
+ * First we go thru all the dquots on disk, USR and GRP, and reset
+ * their counters to zero. We need a clean slate.
+ * We don't log our changes till later.
+ */
+ if ((uip = XFS_QI_UQIP(mp))) {
+ if ((error = xfs_qm_dqiterate(mp, uip, XFS_QMOPT_UQUOTA)))
+ goto error_return;
+ flags |= XFS_UQUOTA_CHKD;
+ }
+
+ if ((gip = XFS_QI_GQIP(mp))) {
+ if ((error = xfs_qm_dqiterate(mp, gip, XFS_QMOPT_GQUOTA)))
+ goto error_return;
+ flags |= XFS_GQUOTA_CHKD;
+ }
+
+ do {
+ /*
+ * Iterate thru all the inodes in the file system,
+ * adjusting the corresponding dquot counters in core.
+ */
+ if ((error = xfs_bulkstat(mp, &lastino, &count,
+ xfs_qm_dqusage_adjust, NULL,
+ structsz, NULL,
+ BULKSTAT_FG_IGET|BULKSTAT_FG_VFSLOCKED,
+ &done)))
+ break;
+
+ } while (! done);
+
+ /*
+ * We can get this error if we couldn't do a dquot allocation inside
+ * xfs_qm_dqusage_adjust (via bulkstat). We don't care about the
+ * dirty dquots that might be cached, we just want to get rid of them
+ * and turn quotaoff. The dquots won't be attached to any of the inodes
+ * at this point (because we intentionally didn't in dqget_noattach).
+ */
+ if (error) {
+ xfs_qm_dqpurge_all(mp,
+ XFS_QMOPT_UQUOTA|XFS_QMOPT_GQUOTA|
+ XFS_QMOPT_QUOTAOFF);
+ goto error_return;
+ }
+ /*
+ * We've made all the changes that we need to make incore.
+ * Now flush_them down to disk buffers.
+ */
+ xfs_qm_dqflush_all(mp, XFS_QMOPT_DELWRI);
+
+ /*
+ * We didn't log anything, because if we crashed, we'll have to
+ * start the quotacheck from scratch anyway. However, we must make
+ * sure that our dquot changes are secure before we put the
+ * quotacheck'd stamp on the superblock. So, here we do a synchronous
+ * flush.
+ */
+ XFS_bflush(mp->m_ddev_targp);
+
+ /*
+ * If one type of quotas is off, then it will lose its
+ * quotachecked status, since we won't be doing accounting for
+ * that type anymore.
+ */
+ mp->m_qflags &= ~(XFS_GQUOTA_CHKD | XFS_UQUOTA_CHKD);
+ mp->m_qflags |= flags;
+
+ XQM_LIST_PRINT(&(XFS_QI_MPL_LIST(mp)), MPL_NEXT, "++++ Mp list +++");
+
+ error_return:
+ if (error) {
+ cmn_err(CE_WARN, "XFS quotacheck %s: Unsuccessful (Error %d): "
+ "Disabling quotas.",
+ mp->m_fsname, error);
+ /*
+ * We must turn off quotas.
+ */
+ ASSERT(mp->m_quotainfo != NULL);
+ ASSERT(xfs_Gqm != NULL);
+ xfs_qm_destroy_quotainfo(mp);
+ xfs_mount_reset_sbqflags(mp);
+ } else {
+ cmn_err(CE_NOTE, "XFS quotacheck %s: Done.", mp->m_fsname);
+ }
+ return (error);
+}
+
+/*
+ * This is called after the superblock has been read in and we're ready to
+ * iget the quota inodes.
+ */
+STATIC int
+xfs_qm_init_quotainos(
+ xfs_mount_t *mp)
+{
+ xfs_inode_t *uip, *gip;
+ int error;
+ __int64_t sbflags;
+ uint flags;
+
+ ASSERT(mp->m_quotainfo);
+ uip = gip = NULL;
+ sbflags = 0;
+ flags = 0;
+
+ /*
+ * Get the uquota and gquota inodes
+ */
+ if (XFS_SB_VERSION_HASQUOTA(&mp->m_sb)) {
+ if (XFS_IS_UQUOTA_ON(mp) &&
+ mp->m_sb.sb_uquotino != NULLFSINO) {
+ ASSERT(mp->m_sb.sb_uquotino > 0);
+ if ((error = xfs_iget(mp, NULL, mp->m_sb.sb_uquotino,
+ 0, 0, &uip, 0)))
+ return XFS_ERROR(error);
+ }
+ if (XFS_IS_GQUOTA_ON(mp) &&
+ mp->m_sb.sb_gquotino != NULLFSINO) {
+ ASSERT(mp->m_sb.sb_gquotino > 0);
+ if ((error = xfs_iget(mp, NULL, mp->m_sb.sb_gquotino,
+ 0, 0, &gip, 0))) {
+ if (uip)
+ VN_RELE(XFS_ITOV(uip));
+ return XFS_ERROR(error);
+ }
+ }
+ } else {
+ flags |= XFS_QMOPT_SBVERSION;
+ sbflags |= (XFS_SB_VERSIONNUM | XFS_SB_UQUOTINO |
+ XFS_SB_GQUOTINO | XFS_SB_QFLAGS);
+ }
+
+ /*
+ * Create the two inodes, if they don't exist already. The changes
+ * made above will get added to a transaction and logged in one of
+ * the qino_alloc calls below. If the device is readonly,
+ * temporarily switch to read-write to do this.
+ */
+ if (XFS_IS_UQUOTA_ON(mp) && uip == NULL) {
+ if ((error = xfs_qm_qino_alloc(mp, &uip,
+ sbflags | XFS_SB_UQUOTINO,
+ flags | XFS_QMOPT_UQUOTA)))
+ return XFS_ERROR(error);
+
+ flags &= ~XFS_QMOPT_SBVERSION;
+ }
+ if (XFS_IS_GQUOTA_ON(mp) && gip == NULL) {
+ if ((error = xfs_qm_qino_alloc(mp, &gip,
+ sbflags | XFS_SB_GQUOTINO,
+ flags | XFS_QMOPT_GQUOTA))) {
+ if (uip)
+ VN_RELE(XFS_ITOV(uip));
+
+ return XFS_ERROR(error);
+ }
+ }
+
+ XFS_QI_UQIP(mp) = uip;
+ XFS_QI_GQIP(mp) = gip;
+
+ return (0);
+}
+
+
+/*
+ * Traverse the freelist of dquots and attempt to reclaim a maximum of
+ * 'howmany' dquots. This operation races with dqlookup(), and attempts to
+ * favor the lookup function ...
+ * XXXsup merge this with qm_reclaim_one().
+ */
+STATIC int
+xfs_qm_shake_freelist(
+ int howmany)
+{
+ int nreclaimed;
+ xfs_dqhash_t *hash;
+ xfs_dquot_t *dqp, *nextdqp;
+ int restarts;
+ int nflushes;
+
+ if (howmany <= 0)
+ return (0);
+
+ nreclaimed = 0;
+ restarts = 0;
+ nflushes = 0;
+
+#ifdef QUOTADEBUG
+ cmn_err(CE_DEBUG, "Shake free 0x%x", howmany);
+#endif
+ /* lock order is : hashchainlock, freelistlock, mplistlock */
+ tryagain:
+ xfs_qm_freelist_lock(xfs_Gqm);
+
+ for (dqp = xfs_Gqm->qm_dqfreelist.qh_next;
+ ((dqp != (xfs_dquot_t *) &xfs_Gqm->qm_dqfreelist) &&
+ nreclaimed < howmany); ) {
+ xfs_dqlock(dqp);
+
+ /*
+ * We are racing with dqlookup here. Naturally we don't
+ * want to reclaim a dquot that lookup wants.
+ */
+ if (dqp->dq_flags & XFS_DQ_WANT) {
+ xfs_dqunlock(dqp);
+ xfs_qm_freelist_unlock(xfs_Gqm);
+ if (++restarts >= XFS_QM_RECLAIM_MAX_RESTARTS)
+ return (nreclaimed);
+ XQM_STATS_INC(xqmstats.xs_qm_dqwants);
+ goto tryagain;
+ }
+
+ /*
+ * If the dquot is inactive, we are assured that it is
+ * not on the mplist or the hashlist, and that makes our
+ * life easier.
+ */
+ if (dqp->dq_flags & XFS_DQ_INACTIVE) {
+ ASSERT(dqp->q_mount == NULL);
+ ASSERT(! XFS_DQ_IS_DIRTY(dqp));
+ ASSERT(dqp->HL_PREVP == NULL);
+ ASSERT(dqp->MPL_PREVP == NULL);
+ XQM_STATS_INC(xqmstats.xs_qm_dqinact_reclaims);
+ nextdqp = dqp->dq_flnext;
+ goto off_freelist;
+ }
+
+ ASSERT(dqp->MPL_PREVP);
+ /*
+ * Try to grab the flush lock. If this dquot is in the process of
+ * getting flushed to disk, we don't want to reclaim it.
+ */
+ if (! xfs_qm_dqflock_nowait(dqp)) {
+ xfs_dqunlock(dqp);
+ dqp = dqp->dq_flnext;
+ continue;
+ }
+
+ /*
+ * We have the flush lock so we know that this is not in the
+ * process of being flushed. So, if this is dirty, flush it
+ * DELWRI so that we don't get a freelist infested with
+ * dirty dquots.
+ */
+ if (XFS_DQ_IS_DIRTY(dqp)) {
+ xfs_dqtrace_entry(dqp, "DQSHAKE: DQDIRTY");
+ /*
+ * We flush it delayed write, so don't bother
+ * releasing the mplock.
+ */
+ (void) xfs_qm_dqflush(dqp, XFS_QMOPT_DELWRI);
+ xfs_dqunlock(dqp); /* dqflush unlocks dqflock */
+ dqp = dqp->dq_flnext;
+ continue;
+ }
+ /*
+ * We're trying to get the hashlock out of order. This races
+ * with dqlookup; so, we giveup and goto the next dquot if
+ * we couldn't get the hashlock. This way, we won't starve
+ * a dqlookup process that holds the hashlock that is
+ * waiting for the freelist lock.
+ */
+ if (! xfs_qm_dqhashlock_nowait(dqp)) {
+ xfs_dqfunlock(dqp);
+ xfs_dqunlock(dqp);
+ dqp = dqp->dq_flnext;
+ continue;
+ }
+ /*
+ * This races with dquot allocation code as well as dqflush_all
+ * and reclaim code. So, if we failed to grab the mplist lock,
+ * giveup everything and start over.
+ */
+ hash = dqp->q_hash;
+ ASSERT(hash);
+ if (! xfs_qm_mplist_nowait(dqp->q_mount)) {
+ /* XXX put a sentinel so that we can come back here */
+ xfs_dqfunlock(dqp);
+ xfs_dqunlock(dqp);
+ XFS_DQ_HASH_UNLOCK(hash);
+ xfs_qm_freelist_unlock(xfs_Gqm);
+ if (++restarts >= XFS_QM_RECLAIM_MAX_RESTARTS)
+ return (nreclaimed);
+ goto tryagain;
+ }
+ xfs_dqtrace_entry(dqp, "DQSHAKE: UNLINKING");
+#ifdef QUOTADEBUG
+ cmn_err(CE_DEBUG, "Shake 0x%p, ID 0x%x\n",
+ dqp, INT_GET(dqp->q_core.d_id, ARCH_CONVERT));
+#endif
+ ASSERT(dqp->q_nrefs == 0);
+ nextdqp = dqp->dq_flnext;
+ XQM_MPLIST_REMOVE(&(XFS_QI_MPL_LIST(dqp->q_mount)), dqp);
+ XQM_HASHLIST_REMOVE(hash, dqp);
+ xfs_dqfunlock(dqp);
+ xfs_qm_mplist_unlock(dqp->q_mount);
+ XFS_DQ_HASH_UNLOCK(hash);
+
+ off_freelist:
+ XQM_FREELIST_REMOVE(dqp);
+ xfs_dqunlock(dqp);
+ nreclaimed++;
+ XQM_STATS_INC(xqmstats.xs_qm_dqshake_reclaims);
+ xfs_qm_dqdestroy(dqp);
+ dqp = nextdqp;
+ }
+ xfs_qm_freelist_unlock(xfs_Gqm);
+ return (nreclaimed);
+}
+
+
+/*
+ * The kmem_shake interface is invoked when memory is running low.
+ */
+/* ARGSUSED */
+STATIC int
+xfs_qm_shake(int nr_to_scan, unsigned int gfp_mask)
+{
+ int ndqused, nfree, n;
+
+ if (!kmem_shake_allow(gfp_mask))
+ return (0);
+ if (!xfs_Gqm)
+ return (0);
+
+ nfree = xfs_Gqm->qm_dqfreelist.qh_nelems; /* free dquots */
+ /* incore dquots in all f/s's */
+ ndqused = atomic_read(&xfs_Gqm->qm_totaldquots) - nfree;
+
+ ASSERT(ndqused >= 0);
+
+ if (nfree <= ndqused && nfree < ndquot)
+ return (0);
+
+ ndqused *= xfs_Gqm->qm_dqfree_ratio; /* target # of free dquots */
+ n = nfree - ndqused - ndquot; /* # over target */
+
+ return xfs_qm_shake_freelist(MAX(nfree, n));
+}
+
+
+/*
+ * Just pop the least recently used dquot off the freelist and
+ * recycle it. The returned dquot is locked.
+ */
+STATIC xfs_dquot_t *
+xfs_qm_dqreclaim_one(void)
+{
+ xfs_dquot_t *dqpout;
+ xfs_dquot_t *dqp;
+ int restarts;
+ int nflushes;
+
+ restarts = 0;
+ dqpout = NULL;
+ nflushes = 0;
+
+ /* lockorder: hashchainlock, freelistlock, mplistlock, dqlock, dqflock */
+ startagain:
+ xfs_qm_freelist_lock(xfs_Gqm);
+
+ FOREACH_DQUOT_IN_FREELIST(dqp, &(xfs_Gqm->qm_dqfreelist)) {
+ xfs_dqlock(dqp);
+
+ /*
+ * We are racing with dqlookup here. Naturally we don't
+ * want to reclaim a dquot that lookup wants. We release the
+ * freelist lock and start over, so that lookup will grab
+ * both the dquot and the freelistlock.
+ */
+ if (dqp->dq_flags & XFS_DQ_WANT) {
+ ASSERT(! (dqp->dq_flags & XFS_DQ_INACTIVE));
+ xfs_dqtrace_entry(dqp, "DQRECLAIM: DQWANT");
+ xfs_dqunlock(dqp);
+ xfs_qm_freelist_unlock(xfs_Gqm);
+ if (++restarts >= XFS_QM_RECLAIM_MAX_RESTARTS)
+ return (NULL);
+ XQM_STATS_INC(xqmstats.xs_qm_dqwants);
+ goto startagain;
+ }
+
+ /*
+ * If the dquot is inactive, we are assured that it is
+ * not on the mplist or the hashlist, and that makes our
+ * life easier.
+ */
+ if (dqp->dq_flags & XFS_DQ_INACTIVE) {
+ ASSERT(dqp->q_mount == NULL);
+ ASSERT(! XFS_DQ_IS_DIRTY(dqp));
+ ASSERT(dqp->HL_PREVP == NULL);
+ ASSERT(dqp->MPL_PREVP == NULL);
+ XQM_FREELIST_REMOVE(dqp);
+ xfs_dqunlock(dqp);
+ dqpout = dqp;
+ XQM_STATS_INC(xqmstats.xs_qm_dqinact_reclaims);
+ break;
+ }
+
+ ASSERT(dqp->q_hash);
+ ASSERT(dqp->MPL_PREVP);
+
+ /*
+ * Try to grab the flush lock. If this dquot is in the process of
+ * getting flushed to disk, we don't want to reclaim it.
+ */
+ if (! xfs_qm_dqflock_nowait(dqp)) {
+ xfs_dqunlock(dqp);
+ continue;
+ }
+
+ /*
+ * We have the flush lock so we know that this is not in the
+ * process of being flushed. So, if this is dirty, flush it
+ * DELWRI so that we don't get a freelist infested with
+ * dirty dquots.
+ */
+ if (XFS_DQ_IS_DIRTY(dqp)) {
+ xfs_dqtrace_entry(dqp, "DQRECLAIM: DQDIRTY");
+ /*
+ * We flush it delayed write, so don't bother
+ * releasing the freelist lock.
+ */
+ (void) xfs_qm_dqflush(dqp, XFS_QMOPT_DELWRI);
+ xfs_dqunlock(dqp); /* dqflush unlocks dqflock */
+ continue;
+ }
+
+ if (! xfs_qm_mplist_nowait(dqp->q_mount)) {
+ xfs_dqfunlock(dqp);
+ xfs_dqunlock(dqp);
+ continue;
+ }
+
+ if (! xfs_qm_dqhashlock_nowait(dqp))
+ goto mplistunlock;
+
+ ASSERT(dqp->q_nrefs == 0);
+ xfs_dqtrace_entry(dqp, "DQRECLAIM: UNLINKING");
+ XQM_MPLIST_REMOVE(&(XFS_QI_MPL_LIST(dqp->q_mount)), dqp);
+ XQM_HASHLIST_REMOVE(dqp->q_hash, dqp);
+ XQM_FREELIST_REMOVE(dqp);
+ dqpout = dqp;
+ XFS_DQ_HASH_UNLOCK(dqp->q_hash);
+ mplistunlock:
+ xfs_qm_mplist_unlock(dqp->q_mount);
+ xfs_dqfunlock(dqp);
+ xfs_dqunlock(dqp);
+ if (dqpout)
+ break;
+ }
+
+ xfs_qm_freelist_unlock(xfs_Gqm);
+ return (dqpout);
+}
+
+
+/*------------------------------------------------------------------*/
+
+/*
+ * Return a new incore dquot. Depending on the number of
+ * dquots in the system, we either allocate a new one on the kernel heap,
+ * or reclaim a free one.
+ * Return value is B_TRUE if we allocated a new dquot, B_FALSE if we managed
+ * to reclaim an existing one from the freelist.
+ */
+boolean_t
+xfs_qm_dqalloc_incore(
+ xfs_dquot_t **O_dqpp)
+{
+ xfs_dquot_t *dqp;
+
+ /*
+ * Check against high water mark to see if we want to pop
+ * a nincompoop dquot off the freelist.
+ */
+ if (atomic_read(&xfs_Gqm->qm_totaldquots) >= ndquot) {
+ /*
+ * Try to recycle a dquot from the freelist.
+ */
+ if ((dqp = xfs_qm_dqreclaim_one())) {
+ XQM_STATS_INC(xqmstats.xs_qm_dqreclaims);
+ /*
+ * Just zero the core here. The rest will get
+ * reinitialized by caller. XXX we shouldn't even
+ * do this zero ...
+ */
+ memset(&dqp->q_core, 0, sizeof(dqp->q_core));
+ *O_dqpp = dqp;
+ return (B_FALSE);
+ }
+ XQM_STATS_INC(xqmstats.xs_qm_dqreclaim_misses);
+ }
+
+ /*
+ * Allocate a brand new dquot on the kernel heap and return it
+ * to the caller to initialize.
+ */
+ ASSERT(xfs_Gqm->qm_dqzone != NULL);
+ *O_dqpp = kmem_zone_zalloc(xfs_Gqm->qm_dqzone, KM_SLEEP);
+ atomic_inc(&xfs_Gqm->qm_totaldquots);
+
+ return (B_TRUE);
+}
+
+
+/*
+ * Start a transaction and write the incore superblock changes to
+ * disk. flags parameter indicates which fields have changed.
+ */
+int
+xfs_qm_write_sb_changes(
+ xfs_mount_t *mp,
+ __int64_t flags)
+{
+ xfs_trans_t *tp;
+ int error;
+
+#ifdef QUOTADEBUG
+ cmn_err(CE_NOTE, "Writing superblock quota changes :%s", mp->m_fsname);
+#endif
+ tp = xfs_trans_alloc(mp, XFS_TRANS_QM_SBCHANGE);
+ if ((error = xfs_trans_reserve(tp, 0,
+ mp->m_sb.sb_sectsize + 128, 0,
+ 0,
+ XFS_DEFAULT_LOG_COUNT))) {
+ xfs_trans_cancel(tp, 0);
+ return (error);
+ }
+
+ xfs_mod_sb(tp, flags);
+ (void) xfs_trans_commit(tp, 0, NULL);
+
+ return (0);
+}
+
+
+/* --------------- utility functions for vnodeops ---------------- */
+
+
+/*
+ * Given an inode, a uid and gid (from cred_t) make sure that we have
+ * allocated relevant dquot(s) on disk, and that we won't exceed inode
+ * quotas by creating this file.
+ * This also attaches dquot(s) to the given inode after locking it,
+ * and returns the dquots corresponding to the uid and/or gid.
+ *
+ * in : inode (unlocked)
+ * out : udquot, gdquot with references taken and unlocked
+ */
+int
+xfs_qm_vop_dqalloc(
+ xfs_mount_t *mp,
+ xfs_inode_t *ip,
+ uid_t uid,
+ gid_t gid,
+ uint flags,
+ xfs_dquot_t **O_udqpp,
+ xfs_dquot_t **O_gdqpp)
+{
+ int error;
+ xfs_dquot_t *uq, *gq;
+ uint lockflags;
+
+ if (!XFS_IS_QUOTA_ON(mp))
+ return 0;
+
+ lockflags = XFS_ILOCK_EXCL;
+ xfs_ilock(ip, lockflags);
+
+ if ((flags & XFS_QMOPT_INHERIT) &&
+ XFS_INHERIT_GID(ip, XFS_MTOVFS(mp)))
+ gid = ip->i_d.di_gid;
+
+ /*
+ * Attach the dquot(s) to this inode, doing a dquot allocation
+ * if necessary. The dquot(s) will not be locked.
+ */
+ if (XFS_NOT_DQATTACHED(mp, ip)) {
+ if ((error = xfs_qm_dqattach(ip, XFS_QMOPT_DQALLOC |
+ XFS_QMOPT_ILOCKED))) {
+ xfs_iunlock(ip, lockflags);
+ return (error);
+ }
+ }
+
+ uq = gq = NULL;
+ if ((flags & XFS_QMOPT_UQUOTA) &&
+ XFS_IS_UQUOTA_ON(mp)) {
+ if (ip->i_d.di_uid != uid) {
+ /*
+ * What we need is the dquot that has this uid, and
+ * if we send the inode to dqget, the uid of the inode
+ * takes priority over what's sent in the uid argument.
+ * We must unlock inode here before calling dqget if
+ * we're not sending the inode, because otherwise
+ * we'll deadlock by doing trans_reserve while
+ * holding ilock.
+ */
+ xfs_iunlock(ip, lockflags);
+ if ((error = xfs_qm_dqget(mp, NULL, (xfs_dqid_t) uid,
+ XFS_DQ_USER,
+ XFS_QMOPT_DQALLOC |
+ XFS_QMOPT_DOWARN,
+ &uq))) {
+ ASSERT(error != ENOENT);
+ return (error);
+ }
+ /*
+ * Get the ilock in the right order.
+ */
+ xfs_dqunlock(uq);
+ lockflags = XFS_ILOCK_SHARED;
+ xfs_ilock(ip, lockflags);
+ } else {
+ /*
+ * Take an extra reference, because we'll return
+ * this to caller
+ */
+ ASSERT(ip->i_udquot);
+ uq = ip->i_udquot;
+ xfs_dqlock(uq);
+ XFS_DQHOLD(uq);
+ xfs_dqunlock(uq);
+ }
+ }
+ if ((flags & XFS_QMOPT_GQUOTA) &&
+ XFS_IS_GQUOTA_ON(mp)) {
+ if (ip->i_d.di_gid != gid) {
+ xfs_iunlock(ip, lockflags);
+ if ((error = xfs_qm_dqget(mp, NULL, (xfs_dqid_t)gid,
+ XFS_DQ_GROUP,
+ XFS_QMOPT_DQALLOC |
+ XFS_QMOPT_DOWARN,
+ &gq))) {
+ if (uq)
+ xfs_qm_dqrele(uq);
+ ASSERT(error != ENOENT);
+ return (error);
+ }
+ xfs_dqunlock(gq);
+ lockflags = XFS_ILOCK_SHARED;
+ xfs_ilock(ip, lockflags);
+ } else {
+ ASSERT(ip->i_gdquot);
+ gq = ip->i_gdquot;
+ xfs_dqlock(gq);
+ XFS_DQHOLD(gq);
+ xfs_dqunlock(gq);
+ }
+ }
+ if (uq)
+ xfs_dqtrace_entry_ino(uq, "DQALLOC", ip);
+
+ xfs_iunlock(ip, lockflags);
+ if (O_udqpp)
+ *O_udqpp = uq;
+ else if (uq)
+ xfs_qm_dqrele(uq);
+ if (O_gdqpp)
+ *O_gdqpp = gq;
+ else if (gq)
+ xfs_qm_dqrele(gq);
+ return (0);
+}
+
+/*
+ * Actually transfer ownership, and do dquot modifications.
+ * These were already reserved.
+ */
+xfs_dquot_t *
+xfs_qm_vop_chown(
+ xfs_trans_t *tp,
+ xfs_inode_t *ip,
+ xfs_dquot_t **IO_olddq,
+ xfs_dquot_t *newdq)
+{
+ xfs_dquot_t *prevdq;
+ ASSERT(XFS_ISLOCKED_INODE_EXCL(ip));
+ ASSERT(XFS_IS_QUOTA_RUNNING(ip->i_mount));
+
+ /* old dquot */
+ prevdq = *IO_olddq;
+ ASSERT(prevdq);
+ ASSERT(prevdq != newdq);
+
+ xfs_trans_mod_dquot(tp, prevdq,
+ XFS_TRANS_DQ_BCOUNT,
+ -(ip->i_d.di_nblocks));
+ xfs_trans_mod_dquot(tp, prevdq,
+ XFS_TRANS_DQ_ICOUNT,
+ -1);
+
+ /* the sparkling new dquot */
+ xfs_trans_mod_dquot(tp, newdq,
+ XFS_TRANS_DQ_BCOUNT,
+ ip->i_d.di_nblocks);
+ xfs_trans_mod_dquot(tp, newdq,
+ XFS_TRANS_DQ_ICOUNT,
+ 1);
+
+ /*
+ * Take an extra reference, because the inode
+ * is going to keep this dquot pointer even
+ * after the trans_commit.
+ */
+ xfs_dqlock(newdq);
+ XFS_DQHOLD(newdq);
+ xfs_dqunlock(newdq);
+ *IO_olddq = newdq;
+
+ return (prevdq);
+}
+
+/*
+ * Quota reservations for setattr(AT_UID|AT_GID).
+ */
+int
+xfs_qm_vop_chown_reserve(
+ xfs_trans_t *tp,
+ xfs_inode_t *ip,
+ xfs_dquot_t *udqp,
+ xfs_dquot_t *gdqp,
+ uint flags)
+{
+ int error;
+ xfs_mount_t *mp;
+ uint delblks;
+ xfs_dquot_t *unresudq, *unresgdq, *delblksudq, *delblksgdq;
+
+ ASSERT(XFS_ISLOCKED_INODE(ip));
+ mp = ip->i_mount;
+ ASSERT(XFS_IS_QUOTA_RUNNING(mp));
+
+ delblks = ip->i_delayed_blks;
+ delblksudq = delblksgdq = unresudq = unresgdq = NULL;
+
+ if (XFS_IS_UQUOTA_ON(mp) && udqp &&
+ ip->i_d.di_uid != (uid_t)INT_GET(udqp->q_core.d_id, ARCH_CONVERT)) {
+ delblksudq = udqp;
+ /*
+ * If there are delayed allocation blocks, then we have to
+ * unreserve those from the old dquot, and add them to the
+ * new dquot.
+ */
+ if (delblks) {
+ ASSERT(ip->i_udquot);
+ unresudq = ip->i_udquot;
+ }
+ }
+ if (XFS_IS_GQUOTA_ON(ip->i_mount) && gdqp &&
+ ip->i_d.di_gid != INT_GET(gdqp->q_core.d_id, ARCH_CONVERT)) {
+ delblksgdq = gdqp;
+ if (delblks) {
+ ASSERT(ip->i_gdquot);
+ unresgdq = ip->i_gdquot;
+ }
+ }
+
+ if ((error = xfs_trans_reserve_quota_bydquots(tp, ip->i_mount,
+ delblksudq, delblksgdq, ip->i_d.di_nblocks, 1,
+ flags | XFS_QMOPT_RES_REGBLKS)))
+ return (error);
+
+ /*
+ * Do the delayed blks reservations/unreservations now. Since, these
+ * are done without the help of a transaction, if a reservation fails
+ * its previous reservations won't be automatically undone by trans
+ * code. So, we have to do it manually here.
+ */
+ if (delblks) {
+ /*
+ * Do the reservations first. Unreservation can't fail.
+ */
+ ASSERT(delblksudq || delblksgdq);
+ ASSERT(unresudq || unresgdq);
+ if ((error = xfs_trans_reserve_quota_bydquots(NULL, ip->i_mount,
+ delblksudq, delblksgdq, (xfs_qcnt_t)delblks, 0,
+ flags | XFS_QMOPT_RES_REGBLKS)))
+ return (error);
+ xfs_trans_reserve_quota_bydquots(NULL, ip->i_mount,
+ unresudq, unresgdq, -((xfs_qcnt_t)delblks), 0,
+ XFS_QMOPT_RES_REGBLKS);
+ }
+
+ return (0);
+}
+
+int
+xfs_qm_vop_rename_dqattach(
+ xfs_inode_t **i_tab)
+{
+ xfs_inode_t *ip;
+ int i;
+ int error;
+
+ ip = i_tab[0];
+
+ if (! XFS_IS_QUOTA_ON(ip->i_mount))
+ return (0);
+
+ if (XFS_NOT_DQATTACHED(ip->i_mount, ip)) {
+ error = xfs_qm_dqattach(ip, 0);
+ if (error)
+ return (error);
+ }
+ for (i = 1; (i < 4 && i_tab[i]); i++) {
+ /*
+ * Watch out for duplicate entries in the table.
+ */
+ if ((ip = i_tab[i]) != i_tab[i-1]) {
+ if (XFS_NOT_DQATTACHED(ip->i_mount, ip)) {
+ error = xfs_qm_dqattach(ip, 0);
+ if (error)
+ return (error);
+ }
+ }
+ }
+ return (0);
+}
+
+void
+xfs_qm_vop_dqattach_and_dqmod_newinode(
+ xfs_trans_t *tp,
+ xfs_inode_t *ip,
+ xfs_dquot_t *udqp,
+ xfs_dquot_t *gdqp)
+{
+ if (!XFS_IS_QUOTA_ON(tp->t_mountp))
+ return;
+
+ ASSERT(XFS_ISLOCKED_INODE_EXCL(ip));
+ ASSERT(XFS_IS_QUOTA_RUNNING(tp->t_mountp));
+
+ if (udqp) {
+ xfs_dqlock(udqp);
+ XFS_DQHOLD(udqp);
+ xfs_dqunlock(udqp);
+ ASSERT(ip->i_udquot == NULL);
+ ip->i_udquot = udqp;
+ ASSERT(ip->i_d.di_uid == INT_GET(udqp->q_core.d_id, ARCH_CONVERT));
+ xfs_trans_mod_dquot(tp, udqp, XFS_TRANS_DQ_ICOUNT, 1);
+ }
+ if (gdqp) {
+ xfs_dqlock(gdqp);
+ XFS_DQHOLD(gdqp);
+ xfs_dqunlock(gdqp);
+ ASSERT(ip->i_gdquot == NULL);
+ ip->i_gdquot = gdqp;
+ ASSERT(ip->i_d.di_gid == INT_GET(gdqp->q_core.d_id, ARCH_CONVERT));
+ xfs_trans_mod_dquot(tp, gdqp, XFS_TRANS_DQ_ICOUNT, 1);
+ }
+}
+
+/* ------------- list stuff -----------------*/
+void
+xfs_qm_freelist_init(xfs_frlist_t *ql)
+{
+ ql->qh_next = ql->qh_prev = (xfs_dquot_t *) ql;
+ mutex_init(&ql->qh_lock, MUTEX_DEFAULT, "dqf");
+ ql->qh_version = 0;
+ ql->qh_nelems = 0;
+}
+
+void
+xfs_qm_freelist_destroy(xfs_frlist_t *ql)
+{
+ xfs_dquot_t *dqp, *nextdqp;
+
+ mutex_lock(&ql->qh_lock, PINOD);
+ for (dqp = ql->qh_next;
+ dqp != (xfs_dquot_t *)ql; ) {
+ xfs_dqlock(dqp);
+ nextdqp = dqp->dq_flnext;
+#ifdef QUOTADEBUG
+ cmn_err(CE_DEBUG, "FREELIST destroy 0x%p", dqp);
+#endif
+ XQM_FREELIST_REMOVE(dqp);
+ xfs_dqunlock(dqp);
+ xfs_qm_dqdestroy(dqp);
+ dqp = nextdqp;
+ }
+ /*
+ * Don't bother about unlocking.
+ */
+ mutex_destroy(&ql->qh_lock);
+
+ ASSERT(ql->qh_nelems == 0);
+}
+
+void
+xfs_qm_freelist_insert(xfs_frlist_t *ql, xfs_dquot_t *dq)
+{
+ dq->dq_flnext = ql->qh_next;
+ dq->dq_flprev = (xfs_dquot_t *)ql;
+ ql->qh_next = dq;
+ dq->dq_flnext->dq_flprev = dq;
+ xfs_Gqm->qm_dqfreelist.qh_nelems++;
+ xfs_Gqm->qm_dqfreelist.qh_version++;
+}
+
+void
+xfs_qm_freelist_unlink(xfs_dquot_t *dq)
+{
+ xfs_dquot_t *next = dq->dq_flnext;
+ xfs_dquot_t *prev = dq->dq_flprev;
+
+ next->dq_flprev = prev;
+ prev->dq_flnext = next;
+ dq->dq_flnext = dq->dq_flprev = dq;
+ xfs_Gqm->qm_dqfreelist.qh_nelems--;
+ xfs_Gqm->qm_dqfreelist.qh_version++;
+}
+
+void
+xfs_qm_freelist_append(xfs_frlist_t *ql, xfs_dquot_t *dq)
+{
+ xfs_qm_freelist_insert((xfs_frlist_t *)ql->qh_prev, dq);
+}
+
+int
+xfs_qm_dqhashlock_nowait(
+ xfs_dquot_t *dqp)
+{
+ int locked;
+
+ locked = mutex_trylock(&((dqp)->q_hash->qh_lock));
+ return (locked);
+}
+
+int
+xfs_qm_freelist_lock_nowait(
+ xfs_qm_t *xqm)
+{
+ int locked;
+
+ locked = mutex_trylock(&(xqm->qm_dqfreelist.qh_lock));
+ return (locked);
+}
+
+int
+xfs_qm_mplist_nowait(
+ xfs_mount_t *mp)
+{
+ int locked;
+
+ ASSERT(mp->m_quotainfo);
+ locked = mutex_trylock(&(XFS_QI_MPLLOCK(mp)));
+ return (locked);
+}
diff --git a/fs/xfs/quota/xfs_qm.h b/fs/xfs/quota/xfs_qm.h
new file mode 100644
index 00000000000000..dcf1a7a831d8ab
--- /dev/null
+++ b/fs/xfs/quota/xfs_qm.h
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_QM_H__
+#define __XFS_QM_H__
+
+#include "xfs_dquot_item.h"
+#include "xfs_dquot.h"
+#include "xfs_quota_priv.h"
+#include "xfs_qm_stats.h"
+
+struct xfs_qm;
+struct xfs_inode;
+
+extern mutex_t xfs_Gqm_lock;
+extern struct xfs_qm *xfs_Gqm;
+extern kmem_zone_t *qm_dqzone;
+extern kmem_zone_t *qm_dqtrxzone;
+
+/*
+ * Used in xfs_qm_sync called by xfs_sync to count the max times that it can
+ * iterate over the mountpt's dquot list in one call.
+ */
+#define XFS_QM_SYNC_MAX_RESTARTS 7
+
+/*
+ * Ditto, for xfs_qm_dqreclaim_one.
+ */
+#define XFS_QM_RECLAIM_MAX_RESTARTS 4
+
+/*
+ * Ideal ratio of free to in use dquots. Quota manager makes an attempt
+ * to keep this balance.
+ */
+#define XFS_QM_DQFREE_RATIO 2
+
+/*
+ * Dquot hashtable constants/threshold values.
+ */
+#define XFS_QM_NCSIZE_THRESHOLD 5000
+#define XFS_QM_HASHSIZE_LOW 32
+#define XFS_QM_HASHSIZE_HIGH 64
+
+/*
+ * We output a cmn_err when quotachecking a quota file with more than
+ * this many fsbs.
+ */
+#define XFS_QM_BIG_QCHECK_NBLKS 500
+
+/*
+ * This defines the unit of allocation of dquots.
+ * Currently, it is just one file system block, and a 4K blk contains 30
+ * (136 * 30 = 4080) dquots. It's probably not worth trying to make
+ * this more dynamic.
+ * XXXsup However, if this number is changed, we have to make sure that we don't
+ * implicitly assume that we do allocations in chunks of a single filesystem
+ * block in the dquot/xqm code.
+ */
+#define XFS_DQUOT_CLUSTER_SIZE_FSB (xfs_filblks_t)1
+/*
+ * When doing a quotacheck, we log dquot clusters of this many FSBs at most
+ * in a single transaction. We don't want to ask for too huge a log reservation.
+ */
+#define XFS_QM_MAX_DQCLUSTER_LOGSZ 3
+
+typedef xfs_dqhash_t xfs_dqlist_t;
+/*
+ * The freelist head. The first two fields match the first two in the
+ * xfs_dquot_t structure (in xfs_dqmarker_t)
+ */
+typedef struct xfs_frlist {
+ struct xfs_dquot *qh_next;
+ struct xfs_dquot *qh_prev;
+ mutex_t qh_lock;
+ uint qh_version;
+ uint qh_nelems;
+} xfs_frlist_t;
+
+/*
+ * Quota Manager (global) structure. Lives only in core.
+ */
+typedef struct xfs_qm {
+ xfs_dqlist_t *qm_usr_dqhtable;/* udquot hash table */
+ xfs_dqlist_t *qm_grp_dqhtable;/* gdquot hash table */
+ uint qm_dqhashmask; /* # buckets in dq hashtab - 1 */
+ xfs_frlist_t qm_dqfreelist; /* freelist of dquots */
+ atomic_t qm_totaldquots; /* total incore dquots */
+ uint qm_nrefs; /* file systems with quota on */
+ int qm_dqfree_ratio;/* ratio of free to inuse dquots */
+ kmem_zone_t *qm_dqzone; /* dquot mem-alloc zone */
+ kmem_zone_t *qm_dqtrxzone; /* t_dqinfo of transactions */
+} xfs_qm_t;
+
+/*
+ * Various quota information for individual filesystems.
+ * The mount structure keeps a pointer to this.
+ */
+typedef struct xfs_quotainfo {
+ xfs_inode_t *qi_uquotaip; /* user quota inode */
+ xfs_inode_t *qi_gquotaip; /* group quota inode */
+ lock_t qi_pinlock; /* dquot pinning mutex */
+ xfs_dqlist_t qi_dqlist; /* all dquots in filesys */
+ int qi_dqreclaims; /* a change here indicates
+ a removal in the dqlist */
+ time_t qi_btimelimit; /* limit for blks timer */
+ time_t qi_itimelimit; /* limit for inodes timer */
+ time_t qi_rtbtimelimit;/* limit for rt blks timer */
+ xfs_qwarncnt_t qi_bwarnlimit; /* limit for num warnings */
+ xfs_qwarncnt_t qi_iwarnlimit; /* limit for num warnings */
+ mutex_t qi_quotaofflock;/* to serialize quotaoff */
+ xfs_filblks_t qi_dqchunklen; /* # BBs in a chunk of dqs */
+ uint qi_dqperchunk; /* # ondisk dqs in above chunk */
+ xfs_qcnt_t qi_bhardlimit; /* default data blk hard limit */
+ xfs_qcnt_t qi_bsoftlimit; /* default data blk soft limit */
+ xfs_qcnt_t qi_ihardlimit; /* default inode count hard limit */
+ xfs_qcnt_t qi_isoftlimit; /* default inode count soft limit */
+ xfs_qcnt_t qi_rtbhardlimit;/* default realtime blk hard limit */
+ xfs_qcnt_t qi_rtbsoftlimit;/* default realtime blk soft limit */
+} xfs_quotainfo_t;
+
+
+extern xfs_dqtrxops_t xfs_trans_dquot_ops;
+
+extern void xfs_trans_mod_dquot(xfs_trans_t *, xfs_dquot_t *, uint, long);
+extern int xfs_trans_reserve_quota_bydquots(xfs_trans_t *, xfs_mount_t *,
+ xfs_dquot_t *, xfs_dquot_t *, long, long, uint);
+extern void xfs_trans_dqjoin(xfs_trans_t *, xfs_dquot_t *);
+extern void xfs_trans_log_dquot(xfs_trans_t *, xfs_dquot_t *);
+
+/*
+ * We keep the usr and grp dquots separately so that locking will be easier
+ * to do at commit time. All transactions that we know of at this point
+ * affect no more than two dquots of one type. Hence, the TRANS_MAXDQS value.
+ */
+#define XFS_QM_TRANS_MAXDQS 2
+typedef struct xfs_dquot_acct {
+ xfs_dqtrx_t dqa_usrdquots[XFS_QM_TRANS_MAXDQS];
+ xfs_dqtrx_t dqa_grpdquots[XFS_QM_TRANS_MAXDQS];
+} xfs_dquot_acct_t;
+
+/*
+ * Users are allowed to have a usage exceeding their softlimit for
+ * a period this long.
+ */
+#define XFS_QM_BTIMELIMIT (7 * 24*60*60) /* 1 week */
+#define XFS_QM_RTBTIMELIMIT (7 * 24*60*60) /* 1 week */
+#define XFS_QM_ITIMELIMIT (7 * 24*60*60) /* 1 week */
+
+#define XFS_QM_BWARNLIMIT 5
+#define XFS_QM_IWARNLIMIT 5
+
+#define XFS_QM_LOCK(xqm) (mutex_lock(&xqm##_lock, PINOD))
+#define XFS_QM_UNLOCK(xqm) (mutex_unlock(&xqm##_lock))
+#define XFS_QM_HOLD(xqm) ((xqm)->qm_nrefs++)
+#define XFS_QM_RELE(xqm) ((xqm)->qm_nrefs--)
+
+extern void xfs_mount_reset_sbqflags(xfs_mount_t *);
+
+extern int xfs_qm_init_quotainfo(xfs_mount_t *);
+extern void xfs_qm_destroy_quotainfo(xfs_mount_t *);
+extern int xfs_qm_mount_quotas(xfs_mount_t *, int);
+extern void xfs_qm_mount_quotainit(xfs_mount_t *, uint);
+extern int xfs_qm_quotacheck(xfs_mount_t *);
+extern void xfs_qm_unmount_quotadestroy(xfs_mount_t *);
+extern int xfs_qm_unmount_quotas(xfs_mount_t *);
+extern int xfs_qm_write_sb_changes(xfs_mount_t *, __int64_t);
+extern int xfs_qm_sync(xfs_mount_t *, short);
+
+/* dquot stuff */
+extern boolean_t xfs_qm_dqalloc_incore(xfs_dquot_t **);
+extern int xfs_qm_dqattach(xfs_inode_t *, uint);
+extern void xfs_qm_dqdetach(xfs_inode_t *);
+extern int xfs_qm_dqpurge_all(xfs_mount_t *, uint);
+extern void xfs_qm_dqrele_all_inodes(xfs_mount_t *, uint);
+
+/* vop stuff */
+extern int xfs_qm_vop_dqalloc(xfs_mount_t *, xfs_inode_t *,
+ uid_t, gid_t, uint,
+ xfs_dquot_t **, xfs_dquot_t **);
+extern void xfs_qm_vop_dqattach_and_dqmod_newinode(
+ xfs_trans_t *, xfs_inode_t *,
+ xfs_dquot_t *, xfs_dquot_t *);
+extern int xfs_qm_vop_rename_dqattach(xfs_inode_t **);
+extern xfs_dquot_t * xfs_qm_vop_chown(xfs_trans_t *, xfs_inode_t *,
+ xfs_dquot_t **, xfs_dquot_t *);
+extern int xfs_qm_vop_chown_reserve(xfs_trans_t *, xfs_inode_t *,
+ xfs_dquot_t *, xfs_dquot_t *, uint);
+
+/* list stuff */
+extern void xfs_qm_freelist_init(xfs_frlist_t *);
+extern void xfs_qm_freelist_destroy(xfs_frlist_t *);
+extern void xfs_qm_freelist_insert(xfs_frlist_t *, xfs_dquot_t *);
+extern void xfs_qm_freelist_append(xfs_frlist_t *, xfs_dquot_t *);
+extern void xfs_qm_freelist_unlink(xfs_dquot_t *);
+extern int xfs_qm_freelist_lock_nowait(xfs_qm_t *);
+extern int xfs_qm_mplist_nowait(xfs_mount_t *);
+extern int xfs_qm_dqhashlock_nowait(xfs_dquot_t *);
+
+/* system call interface */
+extern int xfs_qm_quotactl(bhv_desc_t *, int, int, xfs_caddr_t);
+
+#ifdef DEBUG
+extern int xfs_qm_internalqcheck(xfs_mount_t *);
+#else
+#define xfs_qm_internalqcheck(mp) (0)
+#endif
+
+#endif /* __XFS_QM_H__ */
diff --git a/fs/xfs/quota/xfs_qm_bhv.c b/fs/xfs/quota/xfs_qm_bhv.c
new file mode 100644
index 00000000000000..be67d9c265f8ff
--- /dev/null
+++ b/fs/xfs/quota/xfs_qm_bhv.c
@@ -0,0 +1,410 @@
+/*
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_clnt.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_alloc.h"
+#include "xfs_dmapi.h"
+#include "xfs_quota.h"
+#include "xfs_mount.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode.h"
+#include "xfs_bmap.h"
+#include "xfs_bit.h"
+#include "xfs_rtalloc.h"
+#include "xfs_error.h"
+#include "xfs_itable.h"
+#include "xfs_rw.h"
+#include "xfs_acl.h"
+#include "xfs_cap.h"
+#include "xfs_mac.h"
+#include "xfs_attr.h"
+#include "xfs_buf_item.h"
+
+#include "xfs_qm.h"
+
+#define MNTOPT_QUOTA "quota" /* disk quotas (user) */
+#define MNTOPT_NOQUOTA "noquota" /* no quotas */
+#define MNTOPT_USRQUOTA "usrquota" /* user quota enabled */
+#define MNTOPT_GRPQUOTA "grpquota" /* group quota enabled */
+#define MNTOPT_UQUOTA "uquota" /* user quota (IRIX variant) */
+#define MNTOPT_GQUOTA "gquota" /* group quota (IRIX variant) */
+#define MNTOPT_UQUOTANOENF "uqnoenforce"/* user quota limit enforcement */
+#define MNTOPT_GQUOTANOENF "gqnoenforce"/* group quota limit enforcement */
+#define MNTOPT_QUOTANOENF "qnoenforce" /* same as uqnoenforce */
+
+STATIC int
+xfs_qm_parseargs(
+ struct bhv_desc *bhv,
+ char *options,
+ struct xfs_mount_args *args,
+ int update)
+{
+ size_t length;
+ char *local_options = options;
+ char *this_char;
+ int error;
+ int referenced = update;
+
+ while ((this_char = strsep(&local_options, ",")) != NULL) {
+ length = strlen(this_char);
+ if (local_options)
+ length++;
+
+ if (!strcmp(this_char, MNTOPT_NOQUOTA)) {
+ args->flags &= ~(XFSMNT_UQUOTAENF|XFSMNT_UQUOTA);
+ args->flags &= ~(XFSMNT_GQUOTAENF|XFSMNT_GQUOTA);
+ referenced = update;
+ } else if (!strcmp(this_char, MNTOPT_QUOTA) ||
+ !strcmp(this_char, MNTOPT_UQUOTA) ||
+ !strcmp(this_char, MNTOPT_USRQUOTA)) {
+ args->flags |= XFSMNT_UQUOTA | XFSMNT_UQUOTAENF;
+ referenced = 1;
+ } else if (!strcmp(this_char, MNTOPT_QUOTANOENF) ||
+ !strcmp(this_char, MNTOPT_UQUOTANOENF)) {
+ args->flags |= XFSMNT_UQUOTA;
+ args->flags &= ~XFSMNT_UQUOTAENF;
+ referenced = 1;
+ } else if (!strcmp(this_char, MNTOPT_GQUOTA) ||
+ !strcmp(this_char, MNTOPT_GRPQUOTA)) {
+ args->flags |= XFSMNT_GQUOTA | XFSMNT_GQUOTAENF;
+ referenced = 1;
+ } else if (!strcmp(this_char, MNTOPT_GQUOTANOENF)) {
+ args->flags |= XFSMNT_GQUOTA;
+ args->flags &= ~XFSMNT_GQUOTAENF;
+ referenced = 1;
+ } else {
+ if (local_options)
+ *(local_options-1) = ',';
+ continue;
+ }
+
+ while (length--)
+ *this_char++ = ',';
+ }
+
+ PVFS_PARSEARGS(BHV_NEXT(bhv), options, args, update, error);
+ if (!error && !referenced)
+ bhv_remove_vfsops(bhvtovfs(bhv), VFS_POSITION_QM);
+ return error;
+}
+
+STATIC int
+xfs_qm_showargs(
+ struct bhv_desc *bhv,
+ struct seq_file *m)
+{
+ struct vfs *vfsp = bhvtovfs(bhv);
+ struct xfs_mount *mp = XFS_VFSTOM(vfsp);
+ int error;
+
+ if (mp->m_qflags & XFS_UQUOTA_ACCT) {
+ (mp->m_qflags & XFS_UQUOTA_ENFD) ?
+ seq_puts(m, "," MNTOPT_USRQUOTA) :
+ seq_puts(m, "," MNTOPT_UQUOTANOENF);
+ }
+
+ if (mp->m_qflags & XFS_GQUOTA_ACCT) {
+ (mp->m_qflags & XFS_GQUOTA_ENFD) ?
+ seq_puts(m, "," MNTOPT_GRPQUOTA) :
+ seq_puts(m, "," MNTOPT_GQUOTANOENF);
+ }
+
+ if (!(mp->m_qflags & (XFS_UQUOTA_ACCT|XFS_GQUOTA_ACCT)))
+ seq_puts(m, "," MNTOPT_NOQUOTA);
+
+ PVFS_SHOWARGS(BHV_NEXT(bhv), m, error);
+ return error;
+}
+
+STATIC int
+xfs_qm_mount(
+ struct bhv_desc *bhv,
+ struct xfs_mount_args *args,
+ struct cred *cr)
+{
+ struct vfs *vfsp = bhvtovfs(bhv);
+ struct xfs_mount *mp = XFS_VFSTOM(vfsp);
+ int error;
+
+ if (args->flags & (XFSMNT_UQUOTA | XFSMNT_GQUOTA))
+ xfs_qm_mount_quotainit(mp, args->flags);
+ PVFS_MOUNT(BHV_NEXT(bhv), args, cr, error);
+ return error;
+}
+
+STATIC int
+xfs_qm_syncall(
+ struct bhv_desc *bhv,
+ int flags,
+ cred_t *credp)
+{
+ struct vfs *vfsp = bhvtovfs(bhv);
+ struct xfs_mount *mp = XFS_VFSTOM(vfsp);
+ int error;
+
+ /*
+ * Get the Quota Manager to flush the dquots.
+ */
+ if (XFS_IS_QUOTA_ON(mp)) {
+ if ((error = xfs_qm_sync(mp, flags))) {
+ /*
+ * If we got an IO error, we will be shutting down.
+ * So, there's nothing more for us to do here.
+ */
+ ASSERT(error != EIO || XFS_FORCED_SHUTDOWN(mp));
+ if (XFS_FORCED_SHUTDOWN(mp)) {
+ return XFS_ERROR(error);
+ }
+ }
+ }
+ PVFS_SYNC(BHV_NEXT(bhv), flags, credp, error);
+ return error;
+}
+
+/*
+ * Clear the quotaflags in memory and in the superblock.
+ */
+void
+xfs_mount_reset_sbqflags(
+ xfs_mount_t *mp)
+{
+ xfs_trans_t *tp;
+ unsigned long s;
+
+ mp->m_qflags = 0;
+ /*
+ * It is OK to look at sb_qflags here in mount path,
+ * without SB_LOCK.
+ */
+ if (mp->m_sb.sb_qflags == 0)
+ return;
+ s = XFS_SB_LOCK(mp);
+ mp->m_sb.sb_qflags = 0;
+ XFS_SB_UNLOCK(mp, s);
+
+ /*
+ * if the fs is readonly, let the incore superblock run
+ * with quotas off but don't flush the update out to disk
+ */
+ if (XFS_MTOVFS(mp)->vfs_flag & VFS_RDONLY)
+ return;
+#ifdef QUOTADEBUG
+ xfs_fs_cmn_err(CE_NOTE, mp, "Writing superblock quota changes");
+#endif
+ tp = xfs_trans_alloc(mp, XFS_TRANS_QM_SBCHANGE);
+ if (xfs_trans_reserve(tp, 0, mp->m_sb.sb_sectsize + 128, 0, 0,
+ XFS_DEFAULT_LOG_COUNT)) {
+ xfs_trans_cancel(tp, 0);
+ xfs_fs_cmn_err(CE_ALERT, mp,
+ "xfs_mount_reset_sbqflags: Superblock update failed!");
+ return;
+ }
+ xfs_mod_sb(tp, XFS_SB_QFLAGS);
+ xfs_trans_commit(tp, 0, NULL);
+}
+
+STATIC int
+xfs_qm_newmount(
+ xfs_mount_t *mp,
+ uint *needquotamount,
+ uint *quotaflags)
+{
+ uint quotaondisk;
+ uint uquotaondisk = 0, gquotaondisk = 0;
+
+ *quotaflags = 0;
+ *needquotamount = B_FALSE;
+
+ quotaondisk = XFS_SB_VERSION_HASQUOTA(&mp->m_sb) &&
+ mp->m_sb.sb_qflags & (XFS_UQUOTA_ACCT|XFS_GQUOTA_ACCT);
+
+ if (quotaondisk) {
+ uquotaondisk = mp->m_sb.sb_qflags & XFS_UQUOTA_ACCT;
+ gquotaondisk = mp->m_sb.sb_qflags & XFS_GQUOTA_ACCT;
+ }
+
+ /*
+ * If the device itself is read-only, we can't allow
+ * the user to change the state of quota on the mount -
+ * this would generate a transaction on the ro device,
+ * which would lead to an I/O error and shutdown
+ */
+
+ if (((uquotaondisk && !XFS_IS_UQUOTA_ON(mp)) ||
+ (!uquotaondisk && XFS_IS_UQUOTA_ON(mp)) ||
+ (gquotaondisk && !XFS_IS_GQUOTA_ON(mp)) ||
+ (!gquotaondisk && XFS_IS_GQUOTA_ON(mp))) &&
+ xfs_dev_is_read_only(mp, "changing quota state")) {
+ cmn_err(CE_WARN,
+ "XFS: please mount with%s%s%s.",
+ (!quotaondisk ? "out quota" : ""),
+ (uquotaondisk ? " usrquota" : ""),
+ (gquotaondisk ? " grpquota" : ""));
+ return XFS_ERROR(EPERM);
+ }
+
+ if (XFS_IS_QUOTA_ON(mp) || quotaondisk) {
+ /*
+ * Call mount_quotas at this point only if we won't have to do
+ * a quotacheck.
+ */
+ if (quotaondisk && !XFS_QM_NEED_QUOTACHECK(mp)) {
+ /*
+ * If an error occured, qm_mount_quotas code
+ * has already disabled quotas. So, just finish
+ * mounting, and get on with the boring life
+ * without disk quotas.
+ */
+ xfs_qm_mount_quotas(mp, 0);
+ } else {
+ /*
+ * Clear the quota flags, but remember them. This
+ * is so that the quota code doesn't get invoked
+ * before we're ready. This can happen when an
+ * inode goes inactive and wants to free blocks,
+ * or via xfs_log_mount_finish.
+ */
+ *needquotamount = B_TRUE;
+ *quotaflags = mp->m_qflags;
+ mp->m_qflags = 0;
+ }
+ }
+
+ return 0;
+}
+
+STATIC int
+xfs_qm_endmount(
+ xfs_mount_t *mp,
+ uint needquotamount,
+ uint quotaflags,
+ int mfsi_flags)
+{
+ if (needquotamount) {
+ ASSERT(mp->m_qflags == 0);
+ mp->m_qflags = quotaflags;
+ xfs_qm_mount_quotas(mp, mfsi_flags);
+ }
+
+#if defined(DEBUG) && defined(XFS_LOUD_RECOVERY)
+ if (! (XFS_IS_QUOTA_ON(mp)))
+ xfs_fs_cmn_err(CE_NOTE, mp, "Disk quotas not turned on");
+ else
+ xfs_fs_cmn_err(CE_NOTE, mp, "Disk quotas turned on");
+#endif
+
+#ifdef QUOTADEBUG
+ if (XFS_IS_QUOTA_ON(mp) && xfs_qm_internalqcheck(mp))
+ cmn_err(CE_WARN, "XFS: mount internalqcheck failed");
+#endif
+
+ return 0;
+}
+
+STATIC void
+xfs_qm_dqrele_null(
+ xfs_dquot_t *dq)
+{
+ /*
+ * Called from XFS, where we always check first for a NULL dquot.
+ */
+ if (!dq)
+ return;
+ xfs_qm_dqrele(dq);
+}
+
+
+struct xfs_qmops xfs_qmcore_xfs = {
+ .xfs_qminit = xfs_qm_newmount,
+ .xfs_qmdone = xfs_qm_unmount_quotadestroy,
+ .xfs_qmmount = xfs_qm_endmount,
+ .xfs_qmunmount = xfs_qm_unmount_quotas,
+ .xfs_dqrele = xfs_qm_dqrele_null,
+ .xfs_dqattach = xfs_qm_dqattach,
+ .xfs_dqdetach = xfs_qm_dqdetach,
+ .xfs_dqpurgeall = xfs_qm_dqpurge_all,
+ .xfs_dqvopalloc = xfs_qm_vop_dqalloc,
+ .xfs_dqvopcreate = xfs_qm_vop_dqattach_and_dqmod_newinode,
+ .xfs_dqvoprename = xfs_qm_vop_rename_dqattach,
+ .xfs_dqvopchown = xfs_qm_vop_chown,
+ .xfs_dqvopchownresv = xfs_qm_vop_chown_reserve,
+ .xfs_dqtrxops = &xfs_trans_dquot_ops,
+};
+
+struct bhv_vfsops xfs_qmops = { {
+ BHV_IDENTITY_INIT(VFS_BHV_QM, VFS_POSITION_QM),
+ .vfs_parseargs = xfs_qm_parseargs,
+ .vfs_showargs = xfs_qm_showargs,
+ .vfs_mount = xfs_qm_mount,
+ .vfs_sync = xfs_qm_syncall,
+ .vfs_quotactl = xfs_qm_quotactl, },
+};
+
+
+void __init
+xfs_qm_init(void)
+{
+ static char message[] __initdata =
+ KERN_INFO "SGI XFS Quota Management subsystem\n";
+
+ printk(message);
+ mutex_init(&xfs_Gqm_lock, MUTEX_DEFAULT, "xfs_qmlock");
+ vfs_bhv_set_custom(&xfs_qmops, &xfs_qmcore_xfs);
+ xfs_qm_init_procfs();
+}
+
+void __exit
+xfs_qm_exit(void)
+{
+ vfs_bhv_clr_custom(&xfs_qmops);
+ xfs_qm_cleanup_procfs();
+ if (qm_dqzone)
+ kmem_cache_destroy(qm_dqzone);
+ if (qm_dqtrxzone)
+ kmem_cache_destroy(qm_dqtrxzone);
+}
diff --git a/fs/xfs/quota/xfs_qm_stats.c b/fs/xfs/quota/xfs_qm_stats.c
new file mode 100644
index 00000000000000..29978e037fee49
--- /dev/null
+++ b/fs/xfs/quota/xfs_qm_stats.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_alloc.h"
+#include "xfs_dmapi.h"
+#include "xfs_quota.h"
+#include "xfs_mount.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode.h"
+#include "xfs_bmap.h"
+#include "xfs_bit.h"
+#include "xfs_rtalloc.h"
+#include "xfs_error.h"
+#include "xfs_itable.h"
+#include "xfs_rw.h"
+#include "xfs_acl.h"
+#include "xfs_cap.h"
+#include "xfs_mac.h"
+#include "xfs_attr.h"
+#include "xfs_buf_item.h"
+
+#include "xfs_qm.h"
+
+struct xqmstats xqmstats;
+
+STATIC int
+xfs_qm_read_xfsquota(
+ char *buffer,
+ char **start,
+ off_t offset,
+ int count,
+ int *eof,
+ void *data)
+{
+ int len;
+
+ /* maximum; incore; ratio free to inuse; freelist */
+ len = sprintf(buffer, "%d\t%d\t%d\t%u\n",
+ ndquot,
+ xfs_Gqm? atomic_read(&xfs_Gqm->qm_totaldquots) : 0,
+ xfs_Gqm? xfs_Gqm->qm_dqfree_ratio : 0,
+ xfs_Gqm? xfs_Gqm->qm_dqfreelist.qh_nelems : 0);
+
+ if (offset >= len) {
+ *start = buffer;
+ *eof = 1;
+ return 0;
+ }
+ *start = buffer + offset;
+ if ((len -= offset) > count)
+ return count;
+ *eof = 1;
+
+ return len;
+}
+
+STATIC int
+xfs_qm_read_stats(
+ char *buffer,
+ char **start,
+ off_t offset,
+ int count,
+ int *eof,
+ void *data)
+{
+ int len;
+
+ /* quota performance statistics */
+ len = sprintf(buffer, "qm %u %u %u %u %u %u %u %u\n",
+ xqmstats.xs_qm_dqreclaims,
+ xqmstats.xs_qm_dqreclaim_misses,
+ xqmstats.xs_qm_dquot_dups,
+ xqmstats.xs_qm_dqcachemisses,
+ xqmstats.xs_qm_dqcachehits,
+ xqmstats.xs_qm_dqwants,
+ xqmstats.xs_qm_dqshake_reclaims,
+ xqmstats.xs_qm_dqinact_reclaims);
+
+ if (offset >= len) {
+ *start = buffer;
+ *eof = 1;
+ return 0;
+ }
+ *start = buffer + offset;
+ if ((len -= offset) > count)
+ return count;
+ *eof = 1;
+
+ return len;
+}
+
+void
+xfs_qm_init_procfs(void)
+{
+ create_proc_read_entry("fs/xfs/xqmstat", 0, NULL, xfs_qm_read_stats, NULL);
+ create_proc_read_entry("fs/xfs/xqm", 0, NULL, xfs_qm_read_xfsquota, NULL);
+}
+
+void
+xfs_qm_cleanup_procfs(void)
+{
+ remove_proc_entry("fs/xfs/xqm", NULL);
+ remove_proc_entry("fs/xfs/xqmstat", NULL);
+}
diff --git a/fs/xfs/quota/xfs_qm_stats.h b/fs/xfs/quota/xfs_qm_stats.h
new file mode 100644
index 00000000000000..8093c5c284ecbe
--- /dev/null
+++ b/fs/xfs/quota/xfs_qm_stats.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_QM_STATS_H__
+#define __XFS_QM_STATS_H__
+
+
+#if defined(CONFIG_PROC_FS) && !defined(XFS_STATS_OFF)
+
+/*
+ * XQM global statistics
+ */
+struct xqmstats {
+ __uint32_t xs_qm_dqreclaims;
+ __uint32_t xs_qm_dqreclaim_misses;
+ __uint32_t xs_qm_dquot_dups;
+ __uint32_t xs_qm_dqcachemisses;
+ __uint32_t xs_qm_dqcachehits;
+ __uint32_t xs_qm_dqwants;
+ __uint32_t xs_qm_dqshake_reclaims;
+ __uint32_t xs_qm_dqinact_reclaims;
+};
+
+extern struct xqmstats xqmstats;
+
+# define XQM_STATS_INC(count) ( (count)++ )
+
+extern void xfs_qm_init_procfs(void);
+extern void xfs_qm_cleanup_procfs(void);
+
+#else
+
+# define XQM_STATS_INC(count) do { } while (0)
+
+static __inline void xfs_qm_init_procfs(void) { };
+static __inline void xfs_qm_cleanup_procfs(void) { };
+
+#endif
+
+#endif /* __XFS_QM_STATS_H__ */
diff --git a/fs/xfs/quota/xfs_qm_syscalls.c b/fs/xfs/quota/xfs_qm_syscalls.c
new file mode 100644
index 00000000000000..3548ced3272434
--- /dev/null
+++ b/fs/xfs/quota/xfs_qm_syscalls.c
@@ -0,0 +1,1460 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_alloc.h"
+#include "xfs_dmapi.h"
+#include "xfs_quota.h"
+#include "xfs_mount.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode.h"
+#include "xfs_bmap.h"
+#include "xfs_bit.h"
+#include "xfs_rtalloc.h"
+#include "xfs_error.h"
+#include "xfs_itable.h"
+#include "xfs_rw.h"
+#include "xfs_acl.h"
+#include "xfs_cap.h"
+#include "xfs_mac.h"
+#include "xfs_attr.h"
+#include "xfs_buf_item.h"
+#include "xfs_utils.h"
+
+#include "xfs_qm.h"
+
+#ifdef DEBUG
+# define qdprintk(s, args...) cmn_err(CE_DEBUG, s, ## args)
+#else
+# define qdprintk(s, args...) do { } while (0)
+#endif
+
+STATIC int xfs_qm_scall_trunc_qfiles(xfs_mount_t *, uint);
+STATIC int xfs_qm_scall_getquota(xfs_mount_t *, xfs_dqid_t, uint,
+ fs_disk_quota_t *);
+STATIC int xfs_qm_scall_getqstat(xfs_mount_t *, fs_quota_stat_t *);
+STATIC int xfs_qm_scall_setqlim(xfs_mount_t *, xfs_dqid_t, uint,
+ fs_disk_quota_t *);
+STATIC int xfs_qm_scall_quotaon(xfs_mount_t *, uint);
+STATIC int xfs_qm_scall_quotaoff(xfs_mount_t *, uint, boolean_t);
+STATIC int xfs_qm_log_quotaoff(xfs_mount_t *, xfs_qoff_logitem_t **, uint);
+STATIC int xfs_qm_log_quotaoff_end(xfs_mount_t *, xfs_qoff_logitem_t *,
+ uint);
+STATIC uint xfs_qm_import_flags(uint);
+STATIC uint xfs_qm_export_flags(uint);
+STATIC uint xfs_qm_import_qtype_flags(uint);
+STATIC uint xfs_qm_export_qtype_flags(uint);
+STATIC void xfs_qm_export_dquot(xfs_mount_t *, xfs_disk_dquot_t *,
+ fs_disk_quota_t *);
+
+
+/*
+ * The main distribution switch of all XFS quotactl system calls.
+ */
+int
+xfs_qm_quotactl(
+ struct bhv_desc *bdp,
+ int cmd,
+ int id,
+ xfs_caddr_t addr)
+{
+ xfs_mount_t *mp;
+ int error;
+ struct vfs *vfsp;
+
+ vfsp = bhvtovfs(bdp);
+ mp = XFS_VFSTOM(vfsp);
+
+ if (addr == NULL && cmd != Q_SYNC)
+ return XFS_ERROR(EINVAL);
+ if (id < 0 && cmd != Q_SYNC)
+ return XFS_ERROR(EINVAL);
+
+ /*
+ * The following commands are valid even when quotaoff.
+ */
+ switch (cmd) {
+ /*
+ * truncate quota files. quota must be off.
+ */
+ case Q_XQUOTARM:
+ if (XFS_IS_QUOTA_ON(mp) || addr == NULL)
+ return XFS_ERROR(EINVAL);
+ if (vfsp->vfs_flag & VFS_RDONLY)
+ return XFS_ERROR(EROFS);
+ return (xfs_qm_scall_trunc_qfiles(mp,
+ xfs_qm_import_qtype_flags(*(uint *)addr)));
+ /*
+ * Get quota status information.
+ */
+ case Q_XGETQSTAT:
+ return (xfs_qm_scall_getqstat(mp, (fs_quota_stat_t *)addr));
+
+ /*
+ * QUOTAON for root f/s and quota enforcement on others..
+ * Quota accounting for non-root f/s's must be turned on
+ * at mount time.
+ */
+ case Q_XQUOTAON:
+ if (addr == NULL)
+ return XFS_ERROR(EINVAL);
+ if (vfsp->vfs_flag & VFS_RDONLY)
+ return XFS_ERROR(EROFS);
+ return (xfs_qm_scall_quotaon(mp,
+ xfs_qm_import_flags(*(uint *)addr)));
+ case Q_XQUOTAOFF:
+ if (vfsp->vfs_flag & VFS_RDONLY)
+ return XFS_ERROR(EROFS);
+ break;
+
+ default:
+ break;
+ }
+
+ if (! XFS_IS_QUOTA_ON(mp))
+ return XFS_ERROR(ESRCH);
+
+ switch (cmd) {
+ case Q_XQUOTAOFF:
+ if (vfsp->vfs_flag & VFS_RDONLY)
+ return XFS_ERROR(EROFS);
+ error = xfs_qm_scall_quotaoff(mp,
+ xfs_qm_import_flags(*(uint *)addr),
+ B_FALSE);
+ break;
+
+ /*
+ * Defaults to XFS_GETUQUOTA.
+ */
+ case Q_XGETQUOTA:
+ error = xfs_qm_scall_getquota(mp, (xfs_dqid_t)id, XFS_DQ_USER,
+ (fs_disk_quota_t *)addr);
+ break;
+ /*
+ * Set limits, both hard and soft. Defaults to Q_SETUQLIM.
+ */
+ case Q_XSETQLIM:
+ if (vfsp->vfs_flag & VFS_RDONLY)
+ return XFS_ERROR(EROFS);
+ error = xfs_qm_scall_setqlim(mp, (xfs_dqid_t)id, XFS_DQ_USER,
+ (fs_disk_quota_t *)addr);
+ break;
+
+ case Q_XSETGQLIM:
+ if (vfsp->vfs_flag & VFS_RDONLY)
+ return XFS_ERROR(EROFS);
+ error = xfs_qm_scall_setqlim(mp, (xfs_dqid_t)id, XFS_DQ_GROUP,
+ (fs_disk_quota_t *)addr);
+ break;
+
+
+ case Q_XGETGQUOTA:
+ error = xfs_qm_scall_getquota(mp, (xfs_dqid_t)id, XFS_DQ_GROUP,
+ (fs_disk_quota_t *)addr);
+ break;
+
+ /*
+ * Quotas are entirely undefined after quotaoff in XFS quotas.
+ * For instance, there's no way to set limits when quotaoff.
+ */
+
+ default:
+ error = XFS_ERROR(EINVAL);
+ break;
+ }
+
+ return (error);
+}
+
+/*
+ * Turn off quota accounting and/or enforcement for all udquots and/or
+ * gdquots. Called only at unmount time.
+ *
+ * This assumes that there are no dquots of this file system cached
+ * incore, and modifies the ondisk dquot directly. Therefore, for example,
+ * it is an error to call this twice, without purging the cache.
+ */
+STATIC int
+xfs_qm_scall_quotaoff(
+ xfs_mount_t *mp,
+ uint flags,
+ boolean_t force)
+{
+ uint dqtype;
+ unsigned long s;
+ int error;
+ uint inactivate_flags;
+ xfs_qoff_logitem_t *qoffstart;
+ int nculprits;
+
+ if (!force && !capable(CAP_SYS_ADMIN))
+ return XFS_ERROR(EPERM);
+ /*
+ * No file system can have quotas enabled on disk but not in core.
+ * Note that quota utilities (like quotaoff) _expect_
+ * errno == EEXIST here.
+ */
+ if ((mp->m_qflags & flags) == 0)
+ return XFS_ERROR(EEXIST);
+ error = 0;
+
+ flags &= (XFS_ALL_QUOTA_ACCT | XFS_ALL_QUOTA_ENFD);
+
+ /*
+ * We don't want to deal with two quotaoffs messing up each other,
+ * so we're going to serialize it. quotaoff isn't exactly a performance
+ * critical thing.
+ * If quotaoff, then we must be dealing with the root filesystem.
+ */
+ ASSERT(mp->m_quotainfo);
+ if (mp->m_quotainfo)
+ mutex_lock(&(XFS_QI_QOFFLOCK(mp)), PINOD);
+
+ ASSERT(mp->m_quotainfo);
+
+ /*
+ * If we're just turning off quota enforcement, change mp and go.
+ */
+ if ((flags & XFS_ALL_QUOTA_ACCT) == 0) {
+ mp->m_qflags &= ~(flags);
+
+ s = XFS_SB_LOCK(mp);
+ mp->m_sb.sb_qflags = mp->m_qflags;
+ XFS_SB_UNLOCK(mp, s);
+ mutex_unlock(&(XFS_QI_QOFFLOCK(mp)));
+
+ /* XXX what to do if error ? Revert back to old vals incore ? */
+ error = xfs_qm_write_sb_changes(mp, XFS_SB_QFLAGS);
+ return (error);
+ }
+
+ dqtype = 0;
+ inactivate_flags = 0;
+ /*
+ * If accounting is off, we must turn enforcement off, clear the
+ * quota 'CHKD' certificate to make it known that we have to
+ * do a quotacheck the next time this quota is turned on.
+ */
+ if (flags & XFS_UQUOTA_ACCT) {
+ dqtype |= XFS_QMOPT_UQUOTA;
+ flags |= (XFS_UQUOTA_CHKD | XFS_UQUOTA_ENFD);
+ inactivate_flags |= XFS_UQUOTA_ACTIVE;
+ }
+ if (flags & XFS_GQUOTA_ACCT) {
+ dqtype |= XFS_QMOPT_GQUOTA;
+ flags |= (XFS_GQUOTA_CHKD | XFS_GQUOTA_ENFD);
+ inactivate_flags |= XFS_GQUOTA_ACTIVE;
+ }
+
+ /*
+ * Nothing to do? Don't complain. This happens when we're just
+ * turning off quota enforcement.
+ */
+ if ((mp->m_qflags & flags) == 0) {
+ mutex_unlock(&(XFS_QI_QOFFLOCK(mp)));
+ return (0);
+ }
+
+ /*
+ * Write the LI_QUOTAOFF log record, and do SB changes atomically,
+ * and synchronously.
+ */
+ xfs_qm_log_quotaoff(mp, &qoffstart, flags);
+
+ /*
+ * Next we clear the XFS_MOUNT_*DQ_ACTIVE bit(s) in the mount struct
+ * to take care of the race between dqget and quotaoff. We don't take
+ * any special locks to reset these bits. All processes need to check
+ * these bits *after* taking inode lock(s) to see if the particular
+ * quota type is in the process of being turned off. If *ACTIVE, it is
+ * guaranteed that all dquot structures and all quotainode ptrs will all
+ * stay valid as long as that inode is kept locked.
+ *
+ * There is no turning back after this.
+ */
+ mp->m_qflags &= ~inactivate_flags;
+
+ /*
+ * Give back all the dquot reference(s) held by inodes.
+ * Here we go thru every single incore inode in this file system, and
+ * do a dqrele on the i_udquot/i_gdquot that it may have.
+ * Essentially, as long as somebody has an inode locked, this guarantees
+ * that quotas will not be turned off. This is handy because in a
+ * transaction once we lock the inode(s) and check for quotaon, we can
+ * depend on the quota inodes (and other things) being valid as long as
+ * we keep the lock(s).
+ */
+ xfs_qm_dqrele_all_inodes(mp, flags);
+
+ /*
+ * Next we make the changes in the quota flag in the mount struct.
+ * This isn't protected by a particular lock directly, because we
+ * don't want to take a mrlock everytime we depend on quotas being on.
+ */
+ mp->m_qflags &= ~(flags);
+
+ /*
+ * Go through all the dquots of this file system and purge them,
+ * according to what was turned off. We may not be able to get rid
+ * of all dquots, because dquots can have temporary references that
+ * are not attached to inodes. eg. xfs_setattr, xfs_create.
+ * So, if we couldn't purge all the dquots from the filesystem,
+ * we can't get rid of the incore data structures.
+ */
+ while ((nculprits = xfs_qm_dqpurge_all(mp, dqtype|XFS_QMOPT_QUOTAOFF)))
+ delay(10 * nculprits);
+
+ /*
+ * Transactions that had started before ACTIVE state bit was cleared
+ * could have logged many dquots, so they'd have higher LSNs than
+ * the first QUOTAOFF log record does. If we happen to crash when
+ * the tail of the log has gone past the QUOTAOFF record, but
+ * before the last dquot modification, those dquots __will__
+ * recover, and that's not good.
+ *
+ * So, we have QUOTAOFF start and end logitems; the start
+ * logitem won't get overwritten until the end logitem appears...
+ */
+ xfs_qm_log_quotaoff_end(mp, qoffstart, flags);
+
+ /*
+ * If quotas is completely disabled, close shop.
+ */
+ if ((flags & XFS_MOUNT_QUOTA_ALL) == XFS_MOUNT_QUOTA_ALL) {
+ mutex_unlock(&(XFS_QI_QOFFLOCK(mp)));
+ xfs_qm_destroy_quotainfo(mp);
+ return (0);
+ }
+
+ /*
+ * Release our quotainode references, and vn_purge them,
+ * if we don't need them anymore.
+ */
+ if ((dqtype & XFS_QMOPT_UQUOTA) && XFS_QI_UQIP(mp)) {
+ XFS_PURGE_INODE(XFS_QI_UQIP(mp));
+ XFS_QI_UQIP(mp) = NULL;
+ }
+ if ((dqtype & XFS_QMOPT_GQUOTA) && XFS_QI_GQIP(mp)) {
+ XFS_PURGE_INODE(XFS_QI_GQIP(mp));
+ XFS_QI_GQIP(mp) = NULL;
+ }
+ mutex_unlock(&(XFS_QI_QOFFLOCK(mp)));
+
+ return (error);
+}
+
+STATIC int
+xfs_qm_scall_trunc_qfiles(
+ xfs_mount_t *mp,
+ uint flags)
+{
+ int error;
+ xfs_inode_t *qip;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return XFS_ERROR(EPERM);
+ error = 0;
+ if (!XFS_SB_VERSION_HASQUOTA(&mp->m_sb) || flags == 0) {
+ qdprintk("qtrunc flags=%x m_qflags=%x\n", flags, mp->m_qflags);
+ return XFS_ERROR(EINVAL);
+ }
+
+ if ((flags & XFS_DQ_USER) && mp->m_sb.sb_uquotino != NULLFSINO) {
+ error = xfs_iget(mp, NULL, mp->m_sb.sb_uquotino, 0, 0, &qip, 0);
+ if (! error) {
+ (void) xfs_truncate_file(mp, qip);
+ VN_RELE(XFS_ITOV(qip));
+ }
+ }
+
+ if ((flags & XFS_DQ_GROUP) && mp->m_sb.sb_gquotino != NULLFSINO) {
+ error = xfs_iget(mp, NULL, mp->m_sb.sb_gquotino, 0, 0, &qip, 0);
+ if (! error) {
+ (void) xfs_truncate_file(mp, qip);
+ VN_RELE(XFS_ITOV(qip));
+ }
+ }
+
+ return (error);
+}
+
+
+/*
+ * Switch on (a given) quota enforcement for a filesystem. This takes
+ * effect immediately.
+ * (Switching on quota accounting must be done at mount time.)
+ */
+STATIC int
+xfs_qm_scall_quotaon(
+ xfs_mount_t *mp,
+ uint flags)
+{
+ int error;
+ unsigned long s;
+ uint qf;
+ uint accflags;
+ __int64_t sbflags;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return XFS_ERROR(EPERM);
+
+ flags &= (XFS_ALL_QUOTA_ACCT | XFS_ALL_QUOTA_ENFD);
+ /*
+ * Switching on quota accounting must be done at mount time.
+ */
+ accflags = flags & XFS_ALL_QUOTA_ACCT;
+ flags &= ~(XFS_ALL_QUOTA_ACCT);
+
+ sbflags = 0;
+
+ if (flags == 0) {
+ qdprintk("quotaon: zero flags, m_qflags=%x\n", mp->m_qflags);
+ return XFS_ERROR(EINVAL);
+ }
+
+ /* No fs can turn on quotas with a delayed effect */
+ ASSERT((flags & XFS_ALL_QUOTA_ACCT) == 0);
+
+ /*
+ * Can't enforce without accounting. We check the superblock
+ * qflags here instead of m_qflags because rootfs can have
+ * quota acct on ondisk without m_qflags' knowing.
+ */
+ if (((flags & XFS_UQUOTA_ACCT) == 0 &&
+ (mp->m_sb.sb_qflags & XFS_UQUOTA_ACCT) == 0 &&
+ (flags & XFS_UQUOTA_ENFD))
+ ||
+ ((flags & XFS_GQUOTA_ACCT) == 0 &&
+ (mp->m_sb.sb_qflags & XFS_GQUOTA_ACCT) == 0 &&
+ (flags & XFS_GQUOTA_ENFD))) {
+ qdprintk("Can't enforce without acct, flags=%x sbflags=%x\n",
+ flags, mp->m_sb.sb_qflags);
+ return XFS_ERROR(EINVAL);
+ }
+ /*
+ * If everything's upto-date incore, then don't waste time.
+ */
+ if ((mp->m_qflags & flags) == flags)
+ return XFS_ERROR(EEXIST);
+
+ /*
+ * Change sb_qflags on disk but not incore mp->qflags
+ * if this is the root filesystem.
+ */
+ s = XFS_SB_LOCK(mp);
+ qf = mp->m_sb.sb_qflags;
+ mp->m_sb.sb_qflags = qf | flags;
+ XFS_SB_UNLOCK(mp, s);
+
+ /*
+ * There's nothing to change if it's the same.
+ */
+ if ((qf & flags) == flags && sbflags == 0)
+ return XFS_ERROR(EEXIST);
+ sbflags |= XFS_SB_QFLAGS;
+
+ if ((error = xfs_qm_write_sb_changes(mp, sbflags)))
+ return (error);
+ /*
+ * If we aren't trying to switch on quota enforcement, we are done.
+ */
+ if (((mp->m_sb.sb_qflags & XFS_UQUOTA_ACCT) !=
+ (mp->m_qflags & XFS_UQUOTA_ACCT)) ||
+ (flags & XFS_ALL_QUOTA_ENFD) == 0)
+ return (0);
+
+ if (! XFS_IS_QUOTA_RUNNING(mp))
+ return XFS_ERROR(ESRCH);
+
+ /*
+ * Switch on quota enforcement in core.
+ */
+ mutex_lock(&(XFS_QI_QOFFLOCK(mp)), PINOD);
+ mp->m_qflags |= (flags & XFS_ALL_QUOTA_ENFD);
+ mutex_unlock(&(XFS_QI_QOFFLOCK(mp)));
+
+ return (0);
+}
+
+
+
+/*
+ * Return quota status information, such as uquota-off, enforcements, etc.
+ */
+STATIC int
+xfs_qm_scall_getqstat(
+ xfs_mount_t *mp,
+ fs_quota_stat_t *out)
+{
+ xfs_inode_t *uip, *gip;
+ boolean_t tempuqip, tempgqip;
+
+ uip = gip = NULL;
+ tempuqip = tempgqip = B_FALSE;
+ memset(out, 0, sizeof(fs_quota_stat_t));
+
+ out->qs_version = FS_QSTAT_VERSION;
+ if (! XFS_SB_VERSION_HASQUOTA(&mp->m_sb)) {
+ out->qs_uquota.qfs_ino = NULLFSINO;
+ out->qs_gquota.qfs_ino = NULLFSINO;
+ return (0);
+ }
+ out->qs_flags = (__uint16_t) xfs_qm_export_flags(mp->m_qflags &
+ (XFS_ALL_QUOTA_ACCT|
+ XFS_ALL_QUOTA_ENFD));
+ out->qs_pad = 0;
+ out->qs_uquota.qfs_ino = mp->m_sb.sb_uquotino;
+ out->qs_gquota.qfs_ino = mp->m_sb.sb_gquotino;
+
+ if (mp->m_quotainfo) {
+ uip = mp->m_quotainfo->qi_uquotaip;
+ gip = mp->m_quotainfo->qi_gquotaip;
+ }
+ if (!uip && mp->m_sb.sb_uquotino != NULLFSINO) {
+ if (xfs_iget(mp, NULL, mp->m_sb.sb_uquotino,
+ 0, 0, &uip, 0) == 0)
+ tempuqip = B_TRUE;
+ }
+ if (!gip && mp->m_sb.sb_gquotino != NULLFSINO) {
+ if (xfs_iget(mp, NULL, mp->m_sb.sb_gquotino,
+ 0, 0, &gip, 0) == 0)
+ tempgqip = B_TRUE;
+ }
+ if (uip) {
+ out->qs_uquota.qfs_nblks = uip->i_d.di_nblocks;
+ out->qs_uquota.qfs_nextents = uip->i_d.di_nextents;
+ if (tempuqip)
+ VN_RELE(XFS_ITOV(uip));
+ }
+ if (gip) {
+ out->qs_gquota.qfs_nblks = gip->i_d.di_nblocks;
+ out->qs_gquota.qfs_nextents = gip->i_d.di_nextents;
+ if (tempgqip)
+ VN_RELE(XFS_ITOV(gip));
+ }
+ if (mp->m_quotainfo) {
+ out->qs_incoredqs = XFS_QI_MPLNDQUOTS(mp);
+ out->qs_btimelimit = XFS_QI_BTIMELIMIT(mp);
+ out->qs_itimelimit = XFS_QI_ITIMELIMIT(mp);
+ out->qs_rtbtimelimit = XFS_QI_RTBTIMELIMIT(mp);
+ out->qs_bwarnlimit = XFS_QI_BWARNLIMIT(mp);
+ out->qs_iwarnlimit = XFS_QI_IWARNLIMIT(mp);
+ }
+ return (0);
+}
+
+/*
+ * Adjust quota limits, and start/stop timers accordingly.
+ */
+STATIC int
+xfs_qm_scall_setqlim(
+ xfs_mount_t *mp,
+ xfs_dqid_t id,
+ uint type,
+ fs_disk_quota_t *newlim)
+{
+ xfs_disk_dquot_t *ddq;
+ xfs_dquot_t *dqp;
+ xfs_trans_t *tp;
+ int error;
+ xfs_qcnt_t hard, soft;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return XFS_ERROR(EPERM);
+
+ if ((newlim->d_fieldmask & (FS_DQ_LIMIT_MASK|FS_DQ_TIMER_MASK)) == 0)
+ return (0);
+
+ tp = xfs_trans_alloc(mp, XFS_TRANS_QM_SETQLIM);
+ if ((error = xfs_trans_reserve(tp, 0, sizeof(xfs_disk_dquot_t) + 128,
+ 0, 0, XFS_DEFAULT_LOG_COUNT))) {
+ xfs_trans_cancel(tp, 0);
+ return (error);
+ }
+
+ /*
+ * We don't want to race with a quotaoff so take the quotaoff lock.
+ * (We don't hold an inode lock, so there's nothing else to stop
+ * a quotaoff from happening). (XXXThis doesn't currently happen
+ * because we take the vfslock before calling xfs_qm_sysent).
+ */
+ mutex_lock(&(XFS_QI_QOFFLOCK(mp)), PINOD);
+
+ /*
+ * Get the dquot (locked), and join it to the transaction.
+ * Allocate the dquot if this doesn't exist.
+ */
+ if ((error = xfs_qm_dqget(mp, NULL, id, type, XFS_QMOPT_DQALLOC, &dqp))) {
+ xfs_trans_cancel(tp, XFS_TRANS_ABORT);
+ mutex_unlock(&(XFS_QI_QOFFLOCK(mp)));
+ ASSERT(error != ENOENT);
+ return (error);
+ }
+ xfs_dqtrace_entry(dqp, "Q_SETQLIM: AFT DQGET");
+ xfs_trans_dqjoin(tp, dqp);
+ ddq = &dqp->q_core;
+
+ /*
+ * Make sure that hardlimits are >= soft limits before changing.
+ */
+ hard = (newlim->d_fieldmask & FS_DQ_BHARD) ?
+ (xfs_qcnt_t) XFS_BB_TO_FSB(mp, newlim->d_blk_hardlimit) :
+ INT_GET(ddq->d_blk_hardlimit, ARCH_CONVERT);
+ soft = (newlim->d_fieldmask & FS_DQ_BSOFT) ?
+ (xfs_qcnt_t) XFS_BB_TO_FSB(mp, newlim->d_blk_softlimit) :
+ INT_GET(ddq->d_blk_softlimit, ARCH_CONVERT);
+ if (hard == 0 || hard >= soft) {
+ INT_SET(ddq->d_blk_hardlimit, ARCH_CONVERT, hard);
+ INT_SET(ddq->d_blk_softlimit, ARCH_CONVERT, soft);
+ if (id == 0) {
+ mp->m_quotainfo->qi_bhardlimit = hard;
+ mp->m_quotainfo->qi_bsoftlimit = soft;
+ }
+ } else {
+ qdprintk("blkhard %Ld < blksoft %Ld\n", hard, soft);
+ }
+ hard = (newlim->d_fieldmask & FS_DQ_RTBHARD) ?
+ (xfs_qcnt_t) XFS_BB_TO_FSB(mp, newlim->d_rtb_hardlimit) :
+ INT_GET(ddq->d_rtb_hardlimit, ARCH_CONVERT);
+ soft = (newlim->d_fieldmask & FS_DQ_RTBSOFT) ?
+ (xfs_qcnt_t) XFS_BB_TO_FSB(mp, newlim->d_rtb_softlimit) :
+ INT_GET(ddq->d_rtb_softlimit, ARCH_CONVERT);
+ if (hard == 0 || hard >= soft) {
+ INT_SET(ddq->d_rtb_hardlimit, ARCH_CONVERT, hard);
+ INT_SET(ddq->d_rtb_softlimit, ARCH_CONVERT, soft);
+ if (id == 0) {
+ mp->m_quotainfo->qi_rtbhardlimit = hard;
+ mp->m_quotainfo->qi_rtbsoftlimit = soft;
+ }
+ } else {
+ qdprintk("rtbhard %Ld < rtbsoft %Ld\n", hard, soft);
+ }
+
+ hard = (newlim->d_fieldmask & FS_DQ_IHARD) ?
+ (xfs_qcnt_t) newlim->d_ino_hardlimit :
+ INT_GET(ddq->d_ino_hardlimit, ARCH_CONVERT);
+ soft = (newlim->d_fieldmask & FS_DQ_ISOFT) ?
+ (xfs_qcnt_t) newlim->d_ino_softlimit :
+ INT_GET(ddq->d_ino_softlimit, ARCH_CONVERT);
+ if (hard == 0 || hard >= soft) {
+ INT_SET(ddq->d_ino_hardlimit, ARCH_CONVERT, hard);
+ INT_SET(ddq->d_ino_softlimit, ARCH_CONVERT, soft);
+ if (id == 0) {
+ mp->m_quotainfo->qi_ihardlimit = hard;
+ mp->m_quotainfo->qi_isoftlimit = soft;
+ }
+ } else {
+ qdprintk("ihard %Ld < isoft %Ld\n", hard, soft);
+ }
+
+ if (id == 0) {
+ /*
+ * Timelimits for the super user set the relative time
+ * the other users can be over quota for this file system.
+ * If it is zero a default is used. Ditto for the default
+ * soft and hard limit values (already done, above).
+ */
+ if (newlim->d_fieldmask & FS_DQ_BTIMER) {
+ mp->m_quotainfo->qi_btimelimit = newlim->d_btimer;
+ INT_SET(ddq->d_btimer, ARCH_CONVERT, newlim->d_btimer);
+ }
+ if (newlim->d_fieldmask & FS_DQ_ITIMER) {
+ mp->m_quotainfo->qi_itimelimit = newlim->d_itimer;
+ INT_SET(ddq->d_itimer, ARCH_CONVERT, newlim->d_itimer);
+ }
+ if (newlim->d_fieldmask & FS_DQ_RTBTIMER) {
+ mp->m_quotainfo->qi_rtbtimelimit = newlim->d_rtbtimer;
+ INT_SET(ddq->d_rtbtimer, ARCH_CONVERT, newlim->d_rtbtimer);
+ }
+ } else /* if (XFS_IS_QUOTA_ENFORCED(mp)) */ {
+ /*
+ * If the user is now over quota, start the timelimit.
+ * The user will not be 'warned'.
+ * Note that we keep the timers ticking, whether enforcement
+ * is on or off. We don't really want to bother with iterating
+ * over all ondisk dquots and turning the timers on/off.
+ */
+ xfs_qm_adjust_dqtimers(mp, ddq);
+ }
+ dqp->dq_flags |= XFS_DQ_DIRTY;
+ xfs_trans_log_dquot(tp, dqp);
+
+ xfs_dqtrace_entry(dqp, "Q_SETQLIM: COMMIT");
+ xfs_trans_commit(tp, 0, NULL);
+ xfs_qm_dqprint(dqp);
+ xfs_qm_dqrele(dqp);
+ mutex_unlock(&(XFS_QI_QOFFLOCK(mp)));
+
+ return (0);
+}
+
+STATIC int
+xfs_qm_scall_getquota(
+ xfs_mount_t *mp,
+ xfs_dqid_t id,
+ uint type,
+ fs_disk_quota_t *out)
+{
+ xfs_dquot_t *dqp;
+ int error;
+
+ /*
+ * Try to get the dquot. We don't want it allocated on disk, so
+ * we aren't passing the XFS_QMOPT_DOALLOC flag. If it doesn't
+ * exist, we'll get ENOENT back.
+ */
+ if ((error = xfs_qm_dqget(mp, NULL, id, type, 0, &dqp))) {
+ return (error);
+ }
+
+ xfs_dqtrace_entry(dqp, "Q_GETQUOTA SUCCESS");
+ /*
+ * If everything's NULL, this dquot doesn't quite exist as far as
+ * our utility programs are concerned.
+ */
+ if (XFS_IS_DQUOT_UNINITIALIZED(dqp)) {
+ xfs_qm_dqput(dqp);
+ return XFS_ERROR(ENOENT);
+ }
+ /* xfs_qm_dqprint(dqp); */
+ /*
+ * Convert the disk dquot to the exportable format
+ */
+ xfs_qm_export_dquot(mp, &dqp->q_core, out);
+ xfs_qm_dqput(dqp);
+ return (error ? XFS_ERROR(EFAULT) : 0);
+}
+
+
+STATIC int
+xfs_qm_log_quotaoff_end(
+ xfs_mount_t *mp,
+ xfs_qoff_logitem_t *startqoff,
+ uint flags)
+{
+ xfs_trans_t *tp;
+ int error;
+ xfs_qoff_logitem_t *qoffi;
+
+ tp = xfs_trans_alloc(mp, XFS_TRANS_QM_QUOTAOFF_END);
+
+ if ((error = xfs_trans_reserve(tp, 0, sizeof(xfs_qoff_logitem_t) * 2,
+ 0, 0, XFS_DEFAULT_LOG_COUNT))) {
+ xfs_trans_cancel(tp, 0);
+ return (error);
+ }
+
+ qoffi = xfs_trans_get_qoff_item(tp, startqoff,
+ flags & XFS_ALL_QUOTA_ACCT);
+ xfs_trans_log_quotaoff_item(tp, qoffi);
+
+ /*
+ * We have to make sure that the transaction is secure on disk before we
+ * return and actually stop quota accounting. So, make it synchronous.
+ * We don't care about quotoff's performance.
+ */
+ xfs_trans_set_sync(tp);
+ error = xfs_trans_commit(tp, 0, NULL);
+ return (error);
+}
+
+
+STATIC int
+xfs_qm_log_quotaoff(
+ xfs_mount_t *mp,
+ xfs_qoff_logitem_t **qoffstartp,
+ uint flags)
+{
+ xfs_trans_t *tp;
+ int error;
+ unsigned long s;
+ xfs_qoff_logitem_t *qoffi=NULL;
+ uint oldsbqflag=0;
+
+ tp = xfs_trans_alloc(mp, XFS_TRANS_QM_QUOTAOFF);
+ if ((error = xfs_trans_reserve(tp, 0,
+ sizeof(xfs_qoff_logitem_t) * 2 +
+ mp->m_sb.sb_sectsize + 128,
+ 0,
+ 0,
+ XFS_DEFAULT_LOG_COUNT))) {
+ goto error0;
+ }
+
+ qoffi = xfs_trans_get_qoff_item(tp, NULL, flags & XFS_ALL_QUOTA_ACCT);
+ xfs_trans_log_quotaoff_item(tp, qoffi);
+
+ s = XFS_SB_LOCK(mp);
+ oldsbqflag = mp->m_sb.sb_qflags;
+ mp->m_sb.sb_qflags = (mp->m_qflags & ~(flags)) & XFS_MOUNT_QUOTA_ALL;
+ XFS_SB_UNLOCK(mp, s);
+
+ xfs_mod_sb(tp, XFS_SB_QFLAGS);
+
+ /*
+ * We have to make sure that the transaction is secure on disk before we
+ * return and actually stop quota accounting. So, make it synchronous.
+ * We don't care about quotoff's performance.
+ */
+ xfs_trans_set_sync(tp);
+ error = xfs_trans_commit(tp, 0, NULL);
+
+error0:
+ if (error) {
+ xfs_trans_cancel(tp, 0);
+ /*
+ * No one else is modifying sb_qflags, so this is OK.
+ * We still hold the quotaofflock.
+ */
+ s = XFS_SB_LOCK(mp);
+ mp->m_sb.sb_qflags = oldsbqflag;
+ XFS_SB_UNLOCK(mp, s);
+ }
+ *qoffstartp = qoffi;
+ return (error);
+}
+
+
+/*
+ * Translate an internal style on-disk-dquot to the exportable format.
+ * The main differences are that the counters/limits are all in Basic
+ * Blocks (BBs) instead of the internal FSBs, and all on-disk data has
+ * to be converted to the native endianness.
+ */
+STATIC void
+xfs_qm_export_dquot(
+ xfs_mount_t *mp,
+ xfs_disk_dquot_t *src,
+ struct fs_disk_quota *dst)
+{
+ memset(dst, 0, sizeof(*dst));
+ dst->d_version = FS_DQUOT_VERSION; /* different from src->d_version */
+ dst->d_flags =
+ xfs_qm_export_qtype_flags(INT_GET(src->d_flags, ARCH_CONVERT));
+ dst->d_id = INT_GET(src->d_id, ARCH_CONVERT);
+ dst->d_blk_hardlimit = (__uint64_t)
+ XFS_FSB_TO_BB(mp, INT_GET(src->d_blk_hardlimit, ARCH_CONVERT));
+ dst->d_blk_softlimit = (__uint64_t)
+ XFS_FSB_TO_BB(mp, INT_GET(src->d_blk_softlimit, ARCH_CONVERT));
+ dst->d_ino_hardlimit = (__uint64_t)
+ INT_GET(src->d_ino_hardlimit, ARCH_CONVERT);
+ dst->d_ino_softlimit = (__uint64_t)
+ INT_GET(src->d_ino_softlimit, ARCH_CONVERT);
+ dst->d_bcount = (__uint64_t)
+ XFS_FSB_TO_BB(mp, INT_GET(src->d_bcount, ARCH_CONVERT));
+ dst->d_icount = (__uint64_t) INT_GET(src->d_icount, ARCH_CONVERT);
+ dst->d_btimer = (__uint32_t) INT_GET(src->d_btimer, ARCH_CONVERT);
+ dst->d_itimer = (__uint32_t) INT_GET(src->d_itimer, ARCH_CONVERT);
+ dst->d_iwarns = INT_GET(src->d_iwarns, ARCH_CONVERT);
+ dst->d_bwarns = INT_GET(src->d_bwarns, ARCH_CONVERT);
+
+ dst->d_rtb_hardlimit = (__uint64_t)
+ XFS_FSB_TO_BB(mp, INT_GET(src->d_rtb_hardlimit, ARCH_CONVERT));
+ dst->d_rtb_softlimit = (__uint64_t)
+ XFS_FSB_TO_BB(mp, INT_GET(src->d_rtb_softlimit, ARCH_CONVERT));
+ dst->d_rtbcount = (__uint64_t)
+ XFS_FSB_TO_BB(mp, INT_GET(src->d_rtbcount, ARCH_CONVERT));
+ dst->d_rtbtimer = (__uint32_t) INT_GET(src->d_rtbtimer, ARCH_CONVERT);
+ dst->d_rtbwarns = INT_GET(src->d_rtbwarns, ARCH_CONVERT);
+
+ /*
+ * Internally, we don't reset all the timers when quota enforcement
+ * gets turned off. No need to confuse the userlevel code,
+ * so return zeroes in that case.
+ */
+ if (! XFS_IS_QUOTA_ENFORCED(mp)) {
+ dst->d_btimer = 0;
+ dst->d_itimer = 0;
+ dst->d_rtbtimer = 0;
+ }
+
+#ifdef DEBUG
+ if (XFS_IS_QUOTA_ENFORCED(mp) && dst->d_id != 0) {
+ if (((int) dst->d_bcount >= (int) dst->d_blk_softlimit) &&
+ (dst->d_blk_softlimit > 0)) {
+ ASSERT(dst->d_btimer != 0);
+ }
+ if (((int) dst->d_icount >= (int) dst->d_ino_softlimit) &&
+ (dst->d_ino_softlimit > 0)) {
+ ASSERT(dst->d_itimer != 0);
+ }
+ }
+#endif
+}
+
+STATIC uint
+xfs_qm_import_qtype_flags(
+ uint uflags)
+{
+ /*
+ * Can't be both at the same time.
+ */
+ if (((uflags & (XFS_GROUP_QUOTA | XFS_USER_QUOTA)) ==
+ (XFS_GROUP_QUOTA | XFS_USER_QUOTA)) ||
+ ((uflags & (XFS_GROUP_QUOTA | XFS_USER_QUOTA)) == 0))
+ return (0);
+
+ return (uflags & XFS_USER_QUOTA) ?
+ XFS_DQ_USER : XFS_DQ_GROUP;
+}
+
+STATIC uint
+xfs_qm_export_qtype_flags(
+ uint flags)
+{
+ /*
+ * Can't be both at the same time.
+ */
+ ASSERT((flags & (XFS_GROUP_QUOTA | XFS_USER_QUOTA)) !=
+ (XFS_GROUP_QUOTA | XFS_USER_QUOTA));
+ ASSERT((flags & (XFS_GROUP_QUOTA | XFS_USER_QUOTA)) != 0);
+
+ return (flags & XFS_DQ_USER) ?
+ XFS_USER_QUOTA : XFS_GROUP_QUOTA;
+}
+
+STATIC uint
+xfs_qm_import_flags(
+ uint uflags)
+{
+ uint flags = 0;
+
+ if (uflags & XFS_QUOTA_UDQ_ACCT)
+ flags |= XFS_UQUOTA_ACCT;
+ if (uflags & XFS_QUOTA_GDQ_ACCT)
+ flags |= XFS_GQUOTA_ACCT;
+ if (uflags & XFS_QUOTA_UDQ_ENFD)
+ flags |= XFS_UQUOTA_ENFD;
+ if (uflags & XFS_QUOTA_GDQ_ENFD)
+ flags |= XFS_GQUOTA_ENFD;
+ return (flags);
+}
+
+
+STATIC uint
+xfs_qm_export_flags(
+ uint flags)
+{
+ uint uflags;
+
+ uflags = 0;
+ if (flags & XFS_UQUOTA_ACCT)
+ uflags |= XFS_QUOTA_UDQ_ACCT;
+ if (flags & XFS_GQUOTA_ACCT)
+ uflags |= XFS_QUOTA_GDQ_ACCT;
+ if (flags & XFS_UQUOTA_ENFD)
+ uflags |= XFS_QUOTA_UDQ_ENFD;
+ if (flags & XFS_GQUOTA_ENFD)
+ uflags |= XFS_QUOTA_GDQ_ENFD;
+ return (uflags);
+}
+
+
+/*
+ * Go thru all the inodes in the file system, releasing their dquots.
+ * Note that the mount structure gets modified to indicate that quotas are off
+ * AFTER this, in the case of quotaoff. This also gets called from
+ * xfs_rootumount.
+ */
+void
+xfs_qm_dqrele_all_inodes(
+ struct xfs_mount *mp,
+ uint flags)
+{
+ vmap_t vmap;
+ xfs_inode_t *ip, *topino;
+ uint ireclaims;
+ vnode_t *vp;
+ boolean_t vnode_refd;
+
+ ASSERT(mp->m_quotainfo);
+
+again:
+ XFS_MOUNT_ILOCK(mp);
+ ip = mp->m_inodes;
+ if (ip == NULL) {
+ XFS_MOUNT_IUNLOCK(mp);
+ return;
+ }
+ do {
+ /* Skip markers inserted by xfs_sync */
+ if (ip->i_mount == NULL) {
+ ip = ip->i_mnext;
+ continue;
+ }
+ /* Root inode, rbmip and rsumip have associated blocks */
+ if (ip == XFS_QI_UQIP(mp) || ip == XFS_QI_GQIP(mp)) {
+ ASSERT(ip->i_udquot == NULL);
+ ASSERT(ip->i_gdquot == NULL);
+ ip = ip->i_mnext;
+ continue;
+ }
+ vp = XFS_ITOV_NULL(ip);
+ if (!vp) {
+ ASSERT(ip->i_udquot == NULL);
+ ASSERT(ip->i_gdquot == NULL);
+ ip = ip->i_mnext;
+ continue;
+ }
+ vnode_refd = B_FALSE;
+ if (xfs_ilock_nowait(ip, XFS_ILOCK_EXCL) == 0) {
+ /*
+ * Sample vp mapping while holding the mplock, lest
+ * we come across a non-existent vnode.
+ */
+ VMAP(vp, vmap);
+ ireclaims = mp->m_ireclaims;
+ topino = mp->m_inodes;
+ XFS_MOUNT_IUNLOCK(mp);
+
+ /* XXX restart limit ? */
+ if ( ! (vp = vn_get(vp, &vmap)))
+ goto again;
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
+ vnode_refd = B_TRUE;
+ } else {
+ ireclaims = mp->m_ireclaims;
+ topino = mp->m_inodes;
+ XFS_MOUNT_IUNLOCK(mp);
+ }
+
+ /*
+ * We don't keep the mountlock across the dqrele() call,
+ * since it can take a while..
+ */
+ if ((flags & XFS_UQUOTA_ACCT) && ip->i_udquot) {
+ xfs_qm_dqrele(ip->i_udquot);
+ ip->i_udquot = NULL;
+ }
+ if ((flags & XFS_GQUOTA_ACCT) && ip->i_gdquot) {
+ xfs_qm_dqrele(ip->i_gdquot);
+ ip->i_gdquot = NULL;
+ }
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ /*
+ * Wait until we've dropped the ilock and mountlock to
+ * do the vn_rele. Or be condemned to an eternity in the
+ * inactive code in hell.
+ */
+ if (vnode_refd)
+ VN_RELE(vp);
+ XFS_MOUNT_ILOCK(mp);
+ /*
+ * If an inode was inserted or removed, we gotta
+ * start over again.
+ */
+ if (topino != mp->m_inodes || mp->m_ireclaims != ireclaims) {
+ /* XXX use a sentinel */
+ XFS_MOUNT_IUNLOCK(mp);
+ goto again;
+ }
+ ip = ip->i_mnext;
+ } while (ip != mp->m_inodes);
+
+ XFS_MOUNT_IUNLOCK(mp);
+}
+
+/*------------------------------------------------------------------------*/
+#ifdef DEBUG
+/*
+ * This contains all the test functions for XFS disk quotas.
+ * Currently it does a quota accounting check. ie. it walks through
+ * all inodes in the file system, calculating the dquot accounting fields,
+ * and prints out any inconsistencies.
+ */
+xfs_dqhash_t *qmtest_udqtab;
+xfs_dqhash_t *qmtest_gdqtab;
+int qmtest_hashmask;
+int qmtest_nfails;
+mutex_t qcheck_lock;
+
+#define DQTEST_HASHVAL(mp, id) (((__psunsigned_t)(mp) + \
+ (__psunsigned_t)(id)) & \
+ (qmtest_hashmask - 1))
+
+#define DQTEST_HASH(mp, id, type) ((type & XFS_DQ_USER) ? \
+ (qmtest_udqtab + \
+ DQTEST_HASHVAL(mp, id)) : \
+ (qmtest_gdqtab + \
+ DQTEST_HASHVAL(mp, id)))
+
+#define DQTEST_LIST_PRINT(l, NXT, title) \
+{ \
+ xfs_dqtest_t *dqp; int i = 0;\
+ cmn_err(CE_DEBUG, "%s (#%d)", title, (int) (l)->qh_nelems); \
+ for (dqp = (xfs_dqtest_t *)(l)->qh_next; dqp != NULL; \
+ dqp = (xfs_dqtest_t *)dqp->NXT) { \
+ cmn_err(CE_DEBUG, " %d. \"%d (%s)\" bcnt = %d, icnt = %d", \
+ ++i, dqp->d_id, DQFLAGTO_TYPESTR(dqp), \
+ dqp->d_bcount, dqp->d_icount); } \
+}
+
+typedef struct dqtest {
+ xfs_dqmarker_t q_lists;
+ xfs_dqhash_t *q_hash; /* the hashchain header */
+ xfs_mount_t *q_mount; /* filesystem this relates to */
+ xfs_dqid_t d_id; /* user id or group id */
+ xfs_qcnt_t d_bcount; /* # disk blocks owned by the user */
+ xfs_qcnt_t d_icount; /* # inodes owned by the user */
+} xfs_dqtest_t;
+
+STATIC void
+xfs_qm_hashinsert(xfs_dqhash_t *h, xfs_dqtest_t *dqp)
+{
+ xfs_dquot_t *d;
+ if (((d) = (h)->qh_next))
+ (d)->HL_PREVP = &((dqp)->HL_NEXT);
+ (dqp)->HL_NEXT = d;
+ (dqp)->HL_PREVP = &((h)->qh_next);
+ (h)->qh_next = (xfs_dquot_t *)dqp;
+ (h)->qh_version++;
+ (h)->qh_nelems++;
+}
+STATIC void
+xfs_qm_dqtest_print(
+ xfs_dqtest_t *d)
+{
+ cmn_err(CE_DEBUG, "-----------DQTEST DQUOT----------------");
+ cmn_err(CE_DEBUG, "---- dquot ID = %d", d->d_id);
+ cmn_err(CE_DEBUG, "---- type = %s", XFS_QM_ISUDQ(d)? "USR" : "GRP");
+ cmn_err(CE_DEBUG, "---- fs = 0x%p", d->q_mount);
+ cmn_err(CE_DEBUG, "---- bcount = %Lu (0x%x)",
+ d->d_bcount, (int)d->d_bcount);
+ cmn_err(CE_DEBUG, "---- icount = %Lu (0x%x)",
+ d->d_icount, (int)d->d_icount);
+ cmn_err(CE_DEBUG, "---------------------------");
+}
+
+STATIC void
+xfs_qm_dqtest_failed(
+ xfs_dqtest_t *d,
+ xfs_dquot_t *dqp,
+ char *reason,
+ xfs_qcnt_t a,
+ xfs_qcnt_t b,
+ int error)
+{
+ qmtest_nfails++;
+ if (error)
+ cmn_err(CE_DEBUG, "quotacheck failed id=%d, err=%d\nreason: %s",
+ INT_GET(d->d_id, ARCH_CONVERT), error, reason);
+ else
+ cmn_err(CE_DEBUG, "quotacheck failed id=%d (%s) [%d != %d]",
+ INT_GET(d->d_id, ARCH_CONVERT), reason, (int)a, (int)b);
+ xfs_qm_dqtest_print(d);
+ if (dqp)
+ xfs_qm_dqprint(dqp);
+}
+
+STATIC int
+xfs_dqtest_cmp2(
+ xfs_dqtest_t *d,
+ xfs_dquot_t *dqp)
+{
+ int err = 0;
+ if (INT_GET(dqp->q_core.d_icount, ARCH_CONVERT) != d->d_icount) {
+ xfs_qm_dqtest_failed(d, dqp, "icount mismatch",
+ INT_GET(dqp->q_core.d_icount, ARCH_CONVERT),
+ d->d_icount, 0);
+ err++;
+ }
+ if (INT_GET(dqp->q_core.d_bcount, ARCH_CONVERT) != d->d_bcount) {
+ xfs_qm_dqtest_failed(d, dqp, "bcount mismatch",
+ INT_GET(dqp->q_core.d_bcount, ARCH_CONVERT),
+ d->d_bcount, 0);
+ err++;
+ }
+ if (INT_GET(dqp->q_core.d_blk_softlimit, ARCH_CONVERT) &&
+ INT_GET(dqp->q_core.d_bcount, ARCH_CONVERT) >=
+ INT_GET(dqp->q_core.d_blk_softlimit, ARCH_CONVERT)) {
+ if (INT_ISZERO(dqp->q_core.d_btimer, ARCH_CONVERT) &&
+ !INT_ISZERO(dqp->q_core.d_id, ARCH_CONVERT)) {
+ cmn_err(CE_DEBUG,
+ "%d [%s] [0x%p] BLK TIMER NOT STARTED",
+ d->d_id, DQFLAGTO_TYPESTR(d), d->q_mount);
+ err++;
+ }
+ }
+ if (INT_GET(dqp->q_core.d_ino_softlimit, ARCH_CONVERT) &&
+ INT_GET(dqp->q_core.d_icount, ARCH_CONVERT) >=
+ INT_GET(dqp->q_core.d_ino_softlimit, ARCH_CONVERT)) {
+ if (INT_ISZERO(dqp->q_core.d_itimer, ARCH_CONVERT) &&
+ !INT_ISZERO(dqp->q_core.d_id, ARCH_CONVERT)) {
+ cmn_err(CE_DEBUG,
+ "%d [%s] [0x%p] INO TIMER NOT STARTED",
+ d->d_id, DQFLAGTO_TYPESTR(d), d->q_mount);
+ err++;
+ }
+ }
+#ifdef QUOTADEBUG
+ if (!err) {
+ cmn_err(CE_DEBUG, "%d [%s] [0x%p] qchecked",
+ d->d_id, XFS_QM_ISUDQ(d) ? "USR" : "GRP", d->q_mount);
+ }
+#endif
+ return (err);
+}
+
+STATIC void
+xfs_dqtest_cmp(
+ xfs_dqtest_t *d)
+{
+ xfs_dquot_t *dqp;
+ int error;
+
+ /* xfs_qm_dqtest_print(d); */
+ if ((error = xfs_qm_dqget(d->q_mount, NULL, d->d_id, d->dq_flags, 0,
+ &dqp))) {
+ xfs_qm_dqtest_failed(d, NULL, "dqget failed", 0, 0, error);
+ return;
+ }
+ xfs_dqtest_cmp2(d, dqp);
+ xfs_qm_dqput(dqp);
+}
+
+STATIC int
+xfs_qm_internalqcheck_dqget(
+ xfs_mount_t *mp,
+ xfs_dqid_t id,
+ uint type,
+ xfs_dqtest_t **O_dq)
+{
+ xfs_dqtest_t *d;
+ xfs_dqhash_t *h;
+
+ h = DQTEST_HASH(mp, id, type);
+ for (d = (xfs_dqtest_t *) h->qh_next; d != NULL;
+ d = (xfs_dqtest_t *) d->HL_NEXT) {
+ /* DQTEST_LIST_PRINT(h, HL_NEXT, "@@@@@ dqtestlist @@@@@"); */
+ if (d->d_id == id && mp == d->q_mount) {
+ *O_dq = d;
+ return (0);
+ }
+ }
+ d = kmem_zalloc(sizeof(xfs_dqtest_t), KM_SLEEP);
+ d->dq_flags = type;
+ d->d_id = id;
+ d->q_mount = mp;
+ d->q_hash = h;
+ xfs_qm_hashinsert(h, d);
+ *O_dq = d;
+ return (0);
+}
+
+STATIC void
+xfs_qm_internalqcheck_get_dquots(
+ xfs_mount_t *mp,
+ xfs_dqid_t uid,
+ xfs_dqid_t gid,
+ xfs_dqtest_t **ud,
+ xfs_dqtest_t **gd)
+{
+ if (XFS_IS_UQUOTA_ON(mp))
+ xfs_qm_internalqcheck_dqget(mp, uid, XFS_DQ_USER, ud);
+ if (XFS_IS_GQUOTA_ON(mp))
+ xfs_qm_internalqcheck_dqget(mp, gid, XFS_DQ_GROUP, gd);
+}
+
+
+STATIC void
+xfs_qm_internalqcheck_dqadjust(
+ xfs_inode_t *ip,
+ xfs_dqtest_t *d)
+{
+ d->d_icount++;
+ d->d_bcount += (xfs_qcnt_t)ip->i_d.di_nblocks;
+}
+
+STATIC int
+xfs_qm_internalqcheck_adjust(
+ xfs_mount_t *mp, /* mount point for filesystem */
+ xfs_ino_t ino, /* inode number to get data for */
+ void __user *buffer, /* not used */
+ int ubsize, /* not used */
+ void *private_data, /* not used */
+ xfs_daddr_t bno, /* starting block of inode cluster */
+ int *ubused, /* not used */
+ void *dip, /* not used */
+ int *res) /* bulkstat result code */
+{
+ xfs_inode_t *ip;
+ xfs_dqtest_t *ud, *gd;
+ uint lock_flags;
+ boolean_t ipreleased;
+ int error;
+
+ ASSERT(XFS_IS_QUOTA_RUNNING(mp));
+
+ if (ino == mp->m_sb.sb_uquotino || ino == mp->m_sb.sb_gquotino) {
+ *res = BULKSTAT_RV_NOTHING;
+ qdprintk("internalqcheck: ino=%llu, uqino=%llu, gqino=%llu\n",
+ (unsigned long long) ino,
+ (unsigned long long) mp->m_sb.sb_uquotino,
+ (unsigned long long) mp->m_sb.sb_gquotino);
+ return XFS_ERROR(EINVAL);
+ }
+ ipreleased = B_FALSE;
+ again:
+ lock_flags = XFS_ILOCK_SHARED;
+ if ((error = xfs_iget(mp, NULL, ino, 0, lock_flags, &ip, bno))) {
+ *res = BULKSTAT_RV_NOTHING;
+ return (error);
+ }
+
+ if (ip->i_d.di_mode == 0) {
+ xfs_iput_new(ip, lock_flags);
+ *res = BULKSTAT_RV_NOTHING;
+ return XFS_ERROR(ENOENT);
+ }
+
+ /*
+ * This inode can have blocks after eof which can get released
+ * when we send it to inactive. Since we don't check the dquot
+ * until the after all our calculations are done, we must get rid
+ * of those now.
+ */
+ if (! ipreleased) {
+ xfs_iput(ip, lock_flags);
+ ipreleased = B_TRUE;
+ goto again;
+ }
+ xfs_qm_internalqcheck_get_dquots(mp,
+ (xfs_dqid_t) ip->i_d.di_uid,
+ (xfs_dqid_t) ip->i_d.di_gid,
+ &ud, &gd);
+ if (XFS_IS_UQUOTA_ON(mp)) {
+ ASSERT(ud);
+ xfs_qm_internalqcheck_dqadjust(ip, ud);
+ }
+ if (XFS_IS_GQUOTA_ON(mp)) {
+ ASSERT(gd);
+ xfs_qm_internalqcheck_dqadjust(ip, gd);
+ }
+ xfs_iput(ip, lock_flags);
+ *res = BULKSTAT_RV_DIDONE;
+ return (0);
+}
+
+
+/* PRIVATE, debugging */
+int
+xfs_qm_internalqcheck(
+ xfs_mount_t *mp)
+{
+ xfs_ino_t lastino;
+ int done, count;
+ int i;
+ xfs_dqtest_t *d, *e;
+ xfs_dqhash_t *h1;
+ int error;
+
+ lastino = 0;
+ qmtest_hashmask = 32;
+ count = 5;
+ done = 0;
+ qmtest_nfails = 0;
+
+ if (! XFS_IS_QUOTA_ON(mp))
+ return XFS_ERROR(ESRCH);
+
+ xfs_log_force(mp, (xfs_lsn_t)0, XFS_LOG_FORCE | XFS_LOG_SYNC);
+ XFS_bflush(mp->m_ddev_targp);
+ xfs_log_force(mp, (xfs_lsn_t)0, XFS_LOG_FORCE | XFS_LOG_SYNC);
+ XFS_bflush(mp->m_ddev_targp);
+
+ mutex_lock(&qcheck_lock, PINOD);
+ /* There should be absolutely no quota activity while this
+ is going on. */
+ qmtest_udqtab = kmem_zalloc(qmtest_hashmask *
+ sizeof(xfs_dqhash_t), KM_SLEEP);
+ qmtest_gdqtab = kmem_zalloc(qmtest_hashmask *
+ sizeof(xfs_dqhash_t), KM_SLEEP);
+ do {
+ /*
+ * Iterate thru all the inodes in the file system,
+ * adjusting the corresponding dquot counters
+ */
+ if ((error = xfs_bulkstat(mp, &lastino, &count,
+ xfs_qm_internalqcheck_adjust, NULL,
+ 0, NULL, BULKSTAT_FG_IGET, &done))) {
+ break;
+ }
+ } while (! done);
+ if (error) {
+ cmn_err(CE_DEBUG, "Bulkstat returned error 0x%x", error);
+ }
+ cmn_err(CE_DEBUG, "Checking results against system dquots");
+ for (i = 0; i < qmtest_hashmask; i++) {
+ h1 = &qmtest_udqtab[i];
+ for (d = (xfs_dqtest_t *) h1->qh_next; d != NULL; ) {
+ xfs_dqtest_cmp(d);
+ e = (xfs_dqtest_t *) d->HL_NEXT;
+ kmem_free(d, sizeof(xfs_dqtest_t));
+ d = e;
+ }
+ h1 = &qmtest_gdqtab[i];
+ for (d = (xfs_dqtest_t *) h1->qh_next; d != NULL; ) {
+ xfs_dqtest_cmp(d);
+ e = (xfs_dqtest_t *) d->HL_NEXT;
+ kmem_free(d, sizeof(xfs_dqtest_t));
+ d = e;
+ }
+ }
+
+ if (qmtest_nfails) {
+ cmn_err(CE_DEBUG, "******** quotacheck failed ********");
+ cmn_err(CE_DEBUG, "failures = %d", qmtest_nfails);
+ } else {
+ cmn_err(CE_DEBUG, "******** quotacheck successful! ********");
+ }
+ kmem_free(qmtest_udqtab, qmtest_hashmask * sizeof(xfs_dqhash_t));
+ kmem_free(qmtest_gdqtab, qmtest_hashmask * sizeof(xfs_dqhash_t));
+ mutex_unlock(&qcheck_lock);
+ return (qmtest_nfails);
+}
+
+#endif /* DEBUG */
diff --git a/fs/xfs/quota/xfs_quota_priv.h b/fs/xfs/quota/xfs_quota_priv.h
new file mode 100644
index 00000000000000..eeda93a2b95108
--- /dev/null
+++ b/fs/xfs/quota/xfs_quota_priv.h
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_QUOTA_PRIV_H__
+#define __XFS_QUOTA_PRIV_H__
+
+/*
+ * Number of bmaps that we ask from bmapi when doing a quotacheck.
+ * We make this restriction to keep the memory usage to a minimum.
+ */
+#define XFS_DQITER_MAP_SIZE 10
+
+/* Number of dquots that fit in to a dquot block */
+#define XFS_QM_DQPERBLK(mp) ((mp)->m_quotainfo->qi_dqperchunk)
+
+#define XFS_ISLOCKED_INODE(ip) (ismrlocked(&(ip)->i_lock, \
+ MR_UPDATE | MR_ACCESS) != 0)
+#define XFS_ISLOCKED_INODE_EXCL(ip) (ismrlocked(&(ip)->i_lock, \
+ MR_UPDATE) != 0)
+
+#define XFS_DQ_IS_ADDEDTO_TRX(t, d) ((d)->q_transp == (t))
+
+#define XFS_QI_MPLRECLAIMS(mp) ((mp)->m_quotainfo->qi_dqreclaims)
+#define XFS_QI_UQIP(mp) ((mp)->m_quotainfo->qi_uquotaip)
+#define XFS_QI_GQIP(mp) ((mp)->m_quotainfo->qi_gquotaip)
+#define XFS_QI_DQCHUNKLEN(mp) ((mp)->m_quotainfo->qi_dqchunklen)
+#define XFS_QI_BTIMELIMIT(mp) ((mp)->m_quotainfo->qi_btimelimit)
+#define XFS_QI_RTBTIMELIMIT(mp) ((mp)->m_quotainfo->qi_rtbtimelimit)
+#define XFS_QI_ITIMELIMIT(mp) ((mp)->m_quotainfo->qi_itimelimit)
+#define XFS_QI_BWARNLIMIT(mp) ((mp)->m_quotainfo->qi_bwarnlimit)
+#define XFS_QI_IWARNLIMIT(mp) ((mp)->m_quotainfo->qi_iwarnlimit)
+#define XFS_QI_QOFFLOCK(mp) ((mp)->m_quotainfo->qi_quotaofflock)
+
+#define XFS_QI_MPL_LIST(mp) ((mp)->m_quotainfo->qi_dqlist)
+#define XFS_QI_MPLLOCK(mp) ((mp)->m_quotainfo->qi_dqlist.qh_lock)
+#define XFS_QI_MPLNEXT(mp) ((mp)->m_quotainfo->qi_dqlist.qh_next)
+#define XFS_QI_MPLNDQUOTS(mp) ((mp)->m_quotainfo->qi_dqlist.qh_nelems)
+
+#define XQMLCK(h) (mutex_lock(&((h)->qh_lock), PINOD))
+#define XQMUNLCK(h) (mutex_unlock(&((h)->qh_lock)))
+#ifdef DEBUG
+struct xfs_dqhash;
+static inline int XQMISLCKD(struct xfs_dqhash *h)
+{
+ if (mutex_trylock(&h->qh_lock)) {
+ mutex_unlock(&h->qh_lock);
+ return 0;
+ }
+ return 1;
+}
+#endif
+
+#define XFS_DQ_HASH_LOCK(h) XQMLCK(h)
+#define XFS_DQ_HASH_UNLOCK(h) XQMUNLCK(h)
+#define XFS_DQ_IS_HASH_LOCKED(h) XQMISLCKD(h)
+
+#define xfs_qm_mplist_lock(mp) XQMLCK(&(XFS_QI_MPL_LIST(mp)))
+#define xfs_qm_mplist_unlock(mp) XQMUNLCK(&(XFS_QI_MPL_LIST(mp)))
+#define XFS_QM_IS_MPLIST_LOCKED(mp) XQMISLCKD(&(XFS_QI_MPL_LIST(mp)))
+
+#define xfs_qm_freelist_lock(qm) XQMLCK(&((qm)->qm_dqfreelist))
+#define xfs_qm_freelist_unlock(qm) XQMUNLCK(&((qm)->qm_dqfreelist))
+#define XFS_QM_IS_FREELIST_LOCKED(qm) XQMISLCKD(&((qm)->qm_dqfreelist))
+
+/*
+ * Hash into a bucket in the dquot hash table, based on <mp, id>.
+ */
+#define XFS_DQ_HASHVAL(mp, id) (((__psunsigned_t)(mp) + \
+ (__psunsigned_t)(id)) & \
+ (xfs_Gqm->qm_dqhashmask - 1))
+#define XFS_DQ_HASH(mp, id, type) (type == XFS_DQ_USER ? \
+ (xfs_Gqm->qm_usr_dqhtable + \
+ XFS_DQ_HASHVAL(mp, id)) : \
+ (xfs_Gqm->qm_grp_dqhtable + \
+ XFS_DQ_HASHVAL(mp, id)))
+#define XFS_IS_DQTYPE_ON(mp, type) (type == XFS_DQ_USER ? \
+ XFS_IS_UQUOTA_ON(mp):XFS_IS_GQUOTA_ON(mp))
+#define XFS_IS_DQUOT_UNINITIALIZED(dqp) ( \
+ INT_ISZERO(dqp->q_core.d_blk_hardlimit, ARCH_CONVERT) && \
+ INT_ISZERO(dqp->q_core.d_blk_softlimit, ARCH_CONVERT) && \
+ INT_ISZERO(dqp->q_core.d_rtb_hardlimit, ARCH_CONVERT) && \
+ INT_ISZERO(dqp->q_core.d_rtb_softlimit, ARCH_CONVERT) && \
+ INT_ISZERO(dqp->q_core.d_ino_hardlimit, ARCH_CONVERT) && \
+ INT_ISZERO(dqp->q_core.d_ino_softlimit, ARCH_CONVERT) && \
+ INT_ISZERO(dqp->q_core.d_bcount, ARCH_CONVERT) && \
+ INT_ISZERO(dqp->q_core.d_rtbcount, ARCH_CONVERT) && \
+ INT_ISZERO(dqp->q_core.d_icount, ARCH_CONVERT))
+
+#define HL_PREVP dq_hashlist.ql_prevp
+#define HL_NEXT dq_hashlist.ql_next
+#define MPL_PREVP dq_mplist.ql_prevp
+#define MPL_NEXT dq_mplist.ql_next
+
+
+#define _LIST_REMOVE(h, dqp, PVP, NXT) \
+ { \
+ xfs_dquot_t *d; \
+ if (((d) = (dqp)->NXT)) \
+ (d)->PVP = (dqp)->PVP; \
+ *((dqp)->PVP) = d; \
+ (dqp)->NXT = NULL; \
+ (dqp)->PVP = NULL; \
+ (h)->qh_version++; \
+ (h)->qh_nelems--; \
+ }
+
+#define _LIST_INSERT(h, dqp, PVP, NXT) \
+ { \
+ xfs_dquot_t *d; \
+ if (((d) = (h)->qh_next)) \
+ (d)->PVP = &((dqp)->NXT); \
+ (dqp)->NXT = d; \
+ (dqp)->PVP = &((h)->qh_next); \
+ (h)->qh_next = dqp; \
+ (h)->qh_version++; \
+ (h)->qh_nelems++; \
+ }
+
+#define FOREACH_DQUOT_IN_MP(dqp, mp) \
+ for ((dqp) = XFS_QI_MPLNEXT(mp); (dqp) != NULL; (dqp) = (dqp)->MPL_NEXT)
+
+#define FOREACH_DQUOT_IN_FREELIST(dqp, qlist) \
+for ((dqp) = (qlist)->qh_next; (dqp) != (xfs_dquot_t *)(qlist); \
+ (dqp) = (dqp)->dq_flnext)
+
+#define XQM_HASHLIST_INSERT(h, dqp) \
+ _LIST_INSERT(h, dqp, HL_PREVP, HL_NEXT)
+
+#define XQM_FREELIST_INSERT(h, dqp) \
+ xfs_qm_freelist_append(h, dqp)
+
+#define XQM_MPLIST_INSERT(h, dqp) \
+ _LIST_INSERT(h, dqp, MPL_PREVP, MPL_NEXT)
+
+#define XQM_HASHLIST_REMOVE(h, dqp) \
+ _LIST_REMOVE(h, dqp, HL_PREVP, HL_NEXT)
+#define XQM_FREELIST_REMOVE(dqp) \
+ xfs_qm_freelist_unlink(dqp)
+#define XQM_MPLIST_REMOVE(h, dqp) \
+ { _LIST_REMOVE(h, dqp, MPL_PREVP, MPL_NEXT); \
+ XFS_QI_MPLRECLAIMS((dqp)->q_mount)++; }
+
+#define XFS_DQ_IS_LOGITEM_INITD(dqp) ((dqp)->q_logitem.qli_dquot == (dqp))
+
+#define XFS_QM_DQP_TO_DQACCT(tp, dqp) (XFS_QM_ISUDQ(dqp) ? \
+ (tp)->t_dqinfo->dqa_usrdquots : \
+ (tp)->t_dqinfo->dqa_grpdquots)
+#define XFS_IS_SUSER_DQUOT(dqp) \
+ (INT_ISZERO((dqp)->q_core.d_id, ARCH_CONVERT))
+
+#define XFS_PURGE_INODE(ip) \
+ { \
+ vmap_t dqvmap; \
+ vnode_t *dqvp; \
+ dqvp = XFS_ITOV(ip); \
+ VMAP(dqvp, dqvmap); \
+ VN_RELE(dqvp); \
+ }
+
+#define DQFLAGTO_TYPESTR(d) (((d)->dq_flags & XFS_DQ_USER) ? "USR" : \
+ (((d)->dq_flags & XFS_DQ_GROUP) ? "GRP" : "???"))
+#define DQFLAGTO_DIRTYSTR(d) (XFS_DQ_IS_DIRTY(d) ? "DIRTY" : "NOTDIRTY")
+
+#endif /* __XFS_QUOTA_PRIV_H__ */
diff --git a/fs/xfs/quota/xfs_trans_dquot.c b/fs/xfs/quota/xfs_trans_dquot.c
new file mode 100644
index 00000000000000..25079b5727e443
--- /dev/null
+++ b/fs/xfs/quota/xfs_trans_dquot.c
@@ -0,0 +1,941 @@
+/*
+ * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_alloc.h"
+#include "xfs_dmapi.h"
+#include "xfs_quota.h"
+#include "xfs_mount.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode.h"
+#include "xfs_bmap.h"
+#include "xfs_bit.h"
+#include "xfs_rtalloc.h"
+#include "xfs_error.h"
+#include "xfs_itable.h"
+#include "xfs_rw.h"
+#include "xfs_acl.h"
+#include "xfs_cap.h"
+#include "xfs_mac.h"
+#include "xfs_attr.h"
+#include "xfs_buf_item.h"
+#include "xfs_trans_priv.h"
+
+#include "xfs_qm.h"
+
+STATIC void xfs_trans_alloc_dqinfo(xfs_trans_t *);
+
+/*
+ * Add the locked dquot to the transaction.
+ * The dquot must be locked, and it cannot be associated with any
+ * transaction.
+ */
+void
+xfs_trans_dqjoin(
+ xfs_trans_t *tp,
+ xfs_dquot_t *dqp)
+{
+ xfs_dq_logitem_t *lp;
+
+ ASSERT(! XFS_DQ_IS_ADDEDTO_TRX(tp, dqp));
+ ASSERT(XFS_DQ_IS_LOCKED(dqp));
+ ASSERT(XFS_DQ_IS_LOGITEM_INITD(dqp));
+ lp = &dqp->q_logitem;
+
+ /*
+ * Get a log_item_desc to point at the new item.
+ */
+ (void) xfs_trans_add_item(tp, (xfs_log_item_t*)(lp));
+
+ /*
+ * Initialize i_transp so we can later determine if this dquot is
+ * associated with this transaction.
+ */
+ dqp->q_transp = tp;
+}
+
+
+/*
+ * This is called to mark the dquot as needing
+ * to be logged when the transaction is committed. The dquot must
+ * already be associated with the given transaction.
+ * Note that it marks the entire transaction as dirty. In the ordinary
+ * case, this gets called via xfs_trans_commit, after the transaction
+ * is already dirty. However, there's nothing stop this from getting
+ * called directly, as done by xfs_qm_scall_setqlim. Hence, the TRANS_DIRTY
+ * flag.
+ */
+void
+xfs_trans_log_dquot(
+ xfs_trans_t *tp,
+ xfs_dquot_t *dqp)
+{
+ xfs_log_item_desc_t *lidp;
+
+ ASSERT(XFS_DQ_IS_ADDEDTO_TRX(tp, dqp));
+ ASSERT(XFS_DQ_IS_LOCKED(dqp));
+
+ lidp = xfs_trans_find_item(tp, (xfs_log_item_t*)(&dqp->q_logitem));
+ ASSERT(lidp != NULL);
+
+ tp->t_flags |= XFS_TRANS_DIRTY;
+ lidp->lid_flags |= XFS_LID_DIRTY;
+}
+
+/*
+ * Carry forward whatever is left of the quota blk reservation to
+ * the spanky new transaction
+ */
+STATIC void
+xfs_trans_dup_dqinfo(
+ xfs_trans_t *otp,
+ xfs_trans_t *ntp)
+{
+ xfs_dqtrx_t *oq, *nq;
+ int i,j;
+ xfs_dqtrx_t *oqa, *nqa;
+
+ if (!otp->t_dqinfo)
+ return;
+
+ xfs_trans_alloc_dqinfo(ntp);
+ oqa = otp->t_dqinfo->dqa_usrdquots;
+ nqa = ntp->t_dqinfo->dqa_usrdquots;
+
+ /*
+ * Because the quota blk reservation is carried forward,
+ * it is also necessary to carry forward the DQ_DIRTY flag.
+ */
+ if(otp->t_flags & XFS_TRANS_DQ_DIRTY)
+ ntp->t_flags |= XFS_TRANS_DQ_DIRTY;
+
+ for (j = 0; j < 2; j++) {
+ for (i = 0; i < XFS_QM_TRANS_MAXDQS; i++) {
+ if (oqa[i].qt_dquot == NULL)
+ break;
+ oq = &oqa[i];
+ nq = &nqa[i];
+
+ nq->qt_dquot = oq->qt_dquot;
+ nq->qt_bcount_delta = nq->qt_icount_delta = 0;
+ nq->qt_rtbcount_delta = 0;
+
+ /*
+ * Transfer whatever is left of the reservations.
+ */
+ nq->qt_blk_res = oq->qt_blk_res - oq->qt_blk_res_used;
+ oq->qt_blk_res = oq->qt_blk_res_used;
+
+ nq->qt_rtblk_res = oq->qt_rtblk_res -
+ oq->qt_rtblk_res_used;
+ oq->qt_rtblk_res = oq->qt_rtblk_res_used;
+
+ nq->qt_ino_res = oq->qt_ino_res - oq->qt_ino_res_used;
+ oq->qt_ino_res = oq->qt_ino_res_used;
+
+ }
+ oqa = otp->t_dqinfo->dqa_grpdquots;
+ nqa = ntp->t_dqinfo->dqa_grpdquots;
+ }
+}
+
+/*
+ * Wrap around mod_dquot to account for both user and group quotas.
+ */
+void
+xfs_trans_mod_dquot_byino(
+ xfs_trans_t *tp,
+ xfs_inode_t *ip,
+ uint field,
+ long delta)
+{
+ xfs_mount_t *mp;
+
+ ASSERT(tp);
+ mp = tp->t_mountp;
+
+ if (!XFS_IS_QUOTA_ON(mp) ||
+ ip->i_ino == mp->m_sb.sb_uquotino ||
+ ip->i_ino == mp->m_sb.sb_gquotino)
+ return;
+
+ if (tp->t_dqinfo == NULL)
+ xfs_trans_alloc_dqinfo(tp);
+
+ if (XFS_IS_UQUOTA_ON(mp) && ip->i_udquot) {
+ (void) xfs_trans_mod_dquot(tp, ip->i_udquot, field, delta);
+ }
+ if (XFS_IS_GQUOTA_ON(mp) && ip->i_gdquot) {
+ (void) xfs_trans_mod_dquot(tp, ip->i_gdquot, field, delta);
+ }
+}
+
+STATIC xfs_dqtrx_t *
+xfs_trans_get_dqtrx(
+ xfs_trans_t *tp,
+ xfs_dquot_t *dqp)
+{
+ int i;
+ xfs_dqtrx_t *qa;
+
+ for (i = 0; i < XFS_QM_TRANS_MAXDQS; i++) {
+ qa = XFS_QM_DQP_TO_DQACCT(tp, dqp);
+
+ if (qa[i].qt_dquot == NULL ||
+ qa[i].qt_dquot == dqp) {
+ return (&qa[i]);
+ }
+ }
+
+ return (NULL);
+}
+
+/*
+ * Make the changes in the transaction structure.
+ * The moral equivalent to xfs_trans_mod_sb().
+ * We don't touch any fields in the dquot, so we don't care
+ * if it's locked or not (most of the time it won't be).
+ */
+void
+xfs_trans_mod_dquot(
+ xfs_trans_t *tp,
+ xfs_dquot_t *dqp,
+ uint field,
+ long delta)
+{
+ xfs_dqtrx_t *qtrx;
+
+ ASSERT(tp);
+ qtrx = NULL;
+
+ if (tp->t_dqinfo == NULL)
+ xfs_trans_alloc_dqinfo(tp);
+ /*
+ * Find either the first free slot or the slot that belongs
+ * to this dquot.
+ */
+ qtrx = xfs_trans_get_dqtrx(tp, dqp);
+ ASSERT(qtrx);
+ if (qtrx->qt_dquot == NULL)
+ qtrx->qt_dquot = dqp;
+
+ switch (field) {
+
+ /*
+ * regular disk blk reservation
+ */
+ case XFS_TRANS_DQ_RES_BLKS:
+ qtrx->qt_blk_res += (ulong)delta;
+ break;
+
+ /*
+ * inode reservation
+ */
+ case XFS_TRANS_DQ_RES_INOS:
+ qtrx->qt_ino_res += (ulong)delta;
+ break;
+
+ /*
+ * disk blocks used.
+ */
+ case XFS_TRANS_DQ_BCOUNT:
+ if (qtrx->qt_blk_res && delta > 0) {
+ qtrx->qt_blk_res_used += (ulong)delta;
+ ASSERT(qtrx->qt_blk_res >= qtrx->qt_blk_res_used);
+ }
+ qtrx->qt_bcount_delta += delta;
+ break;
+
+ case XFS_TRANS_DQ_DELBCOUNT:
+ qtrx->qt_delbcnt_delta += delta;
+ break;
+
+ /*
+ * Inode Count
+ */
+ case XFS_TRANS_DQ_ICOUNT:
+ if (qtrx->qt_ino_res && delta > 0) {
+ qtrx->qt_ino_res_used += (ulong)delta;
+ ASSERT(qtrx->qt_ino_res >= qtrx->qt_ino_res_used);
+ }
+ qtrx->qt_icount_delta += delta;
+ break;
+
+ /*
+ * rtblk reservation
+ */
+ case XFS_TRANS_DQ_RES_RTBLKS:
+ qtrx->qt_rtblk_res += (ulong)delta;
+ break;
+
+ /*
+ * rtblk count
+ */
+ case XFS_TRANS_DQ_RTBCOUNT:
+ if (qtrx->qt_rtblk_res && delta > 0) {
+ qtrx->qt_rtblk_res_used += (ulong)delta;
+ ASSERT(qtrx->qt_rtblk_res >= qtrx->qt_rtblk_res_used);
+ }
+ qtrx->qt_rtbcount_delta += delta;
+ break;
+
+ case XFS_TRANS_DQ_DELRTBCOUNT:
+ qtrx->qt_delrtb_delta += delta;
+ break;
+
+ default:
+ ASSERT(0);
+ }
+ tp->t_flags |= XFS_TRANS_DQ_DIRTY;
+}
+
+
+/*
+ * Given an array of dqtrx structures, lock all the dquots associated
+ * and join them to the transaction, provided they have been modified.
+ * We know that the highest number of dquots (of one type - usr OR grp),
+ * involved in a transaction is 2 and that both usr and grp combined - 3.
+ * So, we don't attempt to make this very generic.
+ */
+STATIC void
+xfs_trans_dqlockedjoin(
+ xfs_trans_t *tp,
+ xfs_dqtrx_t *q)
+{
+ ASSERT(q[0].qt_dquot != NULL);
+ if (q[1].qt_dquot == NULL) {
+ xfs_dqlock(q[0].qt_dquot);
+ xfs_trans_dqjoin(tp, q[0].qt_dquot);
+ } else {
+ ASSERT(XFS_QM_TRANS_MAXDQS == 2);
+ xfs_dqlock2(q[0].qt_dquot, q[1].qt_dquot);
+ xfs_trans_dqjoin(tp, q[0].qt_dquot);
+ xfs_trans_dqjoin(tp, q[1].qt_dquot);
+ }
+}
+
+
+/*
+ * Called by xfs_trans_commit() and similar in spirit to
+ * xfs_trans_apply_sb_deltas().
+ * Go thru all the dquots belonging to this transaction and modify the
+ * INCORE dquot to reflect the actual usages.
+ * Unreserve just the reservations done by this transaction.
+ * dquot is still left locked at exit.
+ */
+void
+xfs_trans_apply_dquot_deltas(
+ xfs_trans_t *tp)
+{
+ int i, j;
+ xfs_dquot_t *dqp;
+ xfs_dqtrx_t *qtrx, *qa;
+ xfs_disk_dquot_t *d;
+ long totalbdelta;
+ long totalrtbdelta;
+
+ if (! (tp->t_flags & XFS_TRANS_DQ_DIRTY))
+ return;
+
+ ASSERT(tp->t_dqinfo);
+ qa = tp->t_dqinfo->dqa_usrdquots;
+ for (j = 0; j < 2; j++) {
+ if (qa[0].qt_dquot == NULL) {
+ qa = tp->t_dqinfo->dqa_grpdquots;
+ continue;
+ }
+
+ /*
+ * Lock all of the dquots and join them to the transaction.
+ */
+ xfs_trans_dqlockedjoin(tp, qa);
+
+ for (i = 0; i < XFS_QM_TRANS_MAXDQS; i++) {
+ qtrx = &qa[i];
+ /*
+ * The array of dquots is filled
+ * sequentially, not sparsely.
+ */
+ if ((dqp = qtrx->qt_dquot) == NULL)
+ break;
+
+ ASSERT(XFS_DQ_IS_LOCKED(dqp));
+ ASSERT(XFS_DQ_IS_ADDEDTO_TRX(tp, dqp));
+
+ /*
+ * adjust the actual number of blocks used
+ */
+ d = &dqp->q_core;
+
+ /*
+ * The issue here is - sometimes we don't make a blkquota
+ * reservation intentionally to be fair to users
+ * (when the amount is small). On the other hand,
+ * delayed allocs do make reservations, but that's
+ * outside of a transaction, so we have no
+ * idea how much was really reserved.
+ * So, here we've accumulated delayed allocation blks and
+ * non-delay blks. The assumption is that the
+ * delayed ones are always reserved (outside of a
+ * transaction), and the others may or may not have
+ * quota reservations.
+ */
+ totalbdelta = qtrx->qt_bcount_delta +
+ qtrx->qt_delbcnt_delta;
+ totalrtbdelta = qtrx->qt_rtbcount_delta +
+ qtrx->qt_delrtb_delta;
+#ifdef QUOTADEBUG
+ if (totalbdelta < 0)
+ ASSERT(INT_GET(d->d_bcount, ARCH_CONVERT) >=
+ (xfs_qcnt_t) -totalbdelta);
+
+ if (totalrtbdelta < 0)
+ ASSERT(INT_GET(d->d_rtbcount, ARCH_CONVERT) >=
+ (xfs_qcnt_t) -totalrtbdelta);
+
+ if (qtrx->qt_icount_delta < 0)
+ ASSERT(INT_GET(d->d_icount, ARCH_CONVERT) >=
+ (xfs_qcnt_t) -qtrx->qt_icount_delta);
+#endif
+ if (totalbdelta)
+ INT_MOD(d->d_bcount, ARCH_CONVERT, (xfs_qcnt_t)totalbdelta);
+
+ if (qtrx->qt_icount_delta)
+ INT_MOD(d->d_icount, ARCH_CONVERT, (xfs_qcnt_t)qtrx->qt_icount_delta);
+
+ if (totalrtbdelta)
+ INT_MOD(d->d_rtbcount, ARCH_CONVERT, (xfs_qcnt_t)totalrtbdelta);
+
+ /*
+ * Get any default limits in use.
+ * Start/reset the timer(s) if needed.
+ */
+ if (!INT_ISZERO(d->d_id, ARCH_CONVERT)) {
+ xfs_qm_adjust_dqlimits(tp->t_mountp, d);
+ xfs_qm_adjust_dqtimers(tp->t_mountp, d);
+ }
+
+ dqp->dq_flags |= XFS_DQ_DIRTY;
+ /*
+ * add this to the list of items to get logged
+ */
+ xfs_trans_log_dquot(tp, dqp);
+ /*
+ * Take off what's left of the original reservation.
+ * In case of delayed allocations, there's no
+ * reservation that a transaction structure knows of.
+ */
+ if (qtrx->qt_blk_res != 0) {
+ if (qtrx->qt_blk_res != qtrx->qt_blk_res_used) {
+ if (qtrx->qt_blk_res >
+ qtrx->qt_blk_res_used)
+ dqp->q_res_bcount -= (xfs_qcnt_t)
+ (qtrx->qt_blk_res -
+ qtrx->qt_blk_res_used);
+ else
+ dqp->q_res_bcount -= (xfs_qcnt_t)
+ (qtrx->qt_blk_res_used -
+ qtrx->qt_blk_res);
+ }
+ } else {
+ /*
+ * These blks were never reserved, either inside
+ * a transaction or outside one (in a delayed
+ * allocation). Also, this isn't always a
+ * negative number since we sometimes
+ * deliberately skip quota reservations.
+ */
+ if (qtrx->qt_bcount_delta) {
+ dqp->q_res_bcount +=
+ (xfs_qcnt_t)qtrx->qt_bcount_delta;
+ }
+ }
+ /*
+ * Adjust the RT reservation.
+ */
+ if (qtrx->qt_rtblk_res != 0) {
+ if (qtrx->qt_blk_res != qtrx->qt_blk_res_used) {
+ if (qtrx->qt_rtblk_res >
+ qtrx->qt_rtblk_res_used)
+ dqp->q_res_rtbcount -= (xfs_qcnt_t)
+ (qtrx->qt_rtblk_res -
+ qtrx->qt_rtblk_res_used);
+ else
+ dqp->q_res_rtbcount -= (xfs_qcnt_t)
+ (qtrx->qt_rtblk_res_used -
+ qtrx->qt_rtblk_res);
+ }
+ } else {
+ if (qtrx->qt_rtbcount_delta)
+ dqp->q_res_rtbcount +=
+ (xfs_qcnt_t)qtrx->qt_rtbcount_delta;
+ }
+
+ /*
+ * Adjust the inode reservation.
+ */
+ if (qtrx->qt_ino_res != 0) {
+ ASSERT(qtrx->qt_ino_res >=
+ qtrx->qt_ino_res_used);
+ if (qtrx->qt_ino_res > qtrx->qt_ino_res_used)
+ dqp->q_res_icount -= (xfs_qcnt_t)
+ (qtrx->qt_ino_res -
+ qtrx->qt_ino_res_used);
+ } else {
+ if (qtrx->qt_icount_delta)
+ dqp->q_res_icount +=
+ (xfs_qcnt_t)qtrx->qt_icount_delta;
+ }
+
+
+#ifdef QUOTADEBUG
+ if (qtrx->qt_rtblk_res != 0)
+ cmn_err(CE_DEBUG, "RT res %d for 0x%p\n",
+ (int) qtrx->qt_rtblk_res, dqp);
+#endif
+ ASSERT(dqp->q_res_bcount >=
+ INT_GET(dqp->q_core.d_bcount, ARCH_CONVERT));
+ ASSERT(dqp->q_res_icount >=
+ INT_GET(dqp->q_core.d_icount, ARCH_CONVERT));
+ ASSERT(dqp->q_res_rtbcount >=
+ INT_GET(dqp->q_core.d_rtbcount, ARCH_CONVERT));
+ }
+ /*
+ * Do the group quotas next
+ */
+ qa = tp->t_dqinfo->dqa_grpdquots;
+ }
+}
+
+/*
+ * Release the reservations, and adjust the dquots accordingly.
+ * This is called only when the transaction is being aborted. If by
+ * any chance we have done dquot modifications incore (ie. deltas) already,
+ * we simply throw those away, since that's the expected behavior
+ * when a transaction is curtailed without a commit.
+ */
+STATIC void
+xfs_trans_unreserve_and_mod_dquots(
+ xfs_trans_t *tp)
+{
+ int i, j;
+ xfs_dquot_t *dqp;
+ xfs_dqtrx_t *qtrx, *qa;
+ boolean_t locked;
+
+ if (!tp->t_dqinfo || !(tp->t_flags & XFS_TRANS_DQ_DIRTY))
+ return;
+
+ qa = tp->t_dqinfo->dqa_usrdquots;
+
+ for (j = 0; j < 2; j++) {
+ for (i = 0; i < XFS_QM_TRANS_MAXDQS; i++) {
+ qtrx = &qa[i];
+ /*
+ * We assume that the array of dquots is filled
+ * sequentially, not sparsely.
+ */
+ if ((dqp = qtrx->qt_dquot) == NULL)
+ break;
+ /*
+ * Unreserve the original reservation. We don't care
+ * about the number of blocks used field, or deltas.
+ * Also we don't bother to zero the fields.
+ */
+ locked = B_FALSE;
+ if (qtrx->qt_blk_res) {
+ xfs_dqlock(dqp);
+ locked = B_TRUE;
+ dqp->q_res_bcount -=
+ (xfs_qcnt_t)qtrx->qt_blk_res;
+ }
+ if (qtrx->qt_ino_res) {
+ if (!locked) {
+ xfs_dqlock(dqp);
+ locked = B_TRUE;
+ }
+ dqp->q_res_icount -=
+ (xfs_qcnt_t)qtrx->qt_ino_res;
+ }
+
+ if (qtrx->qt_rtblk_res) {
+ if (!locked) {
+ xfs_dqlock(dqp);
+ locked = B_TRUE;
+ }
+ dqp->q_res_rtbcount -=
+ (xfs_qcnt_t)qtrx->qt_rtblk_res;
+ }
+ if (locked)
+ xfs_dqunlock(dqp);
+
+ }
+ qa = tp->t_dqinfo->dqa_grpdquots;
+ }
+}
+
+/*
+ * This reserves disk blocks and inodes against a dquot.
+ * Flags indicate if the dquot is to be locked here and also
+ * if the blk reservation is for RT or regular blocks.
+ * Sending in XFS_QMOPT_FORCE_RES flag skips the quota check.
+ * Returns EDQUOT if quota is exceeded.
+ */
+STATIC int
+xfs_trans_dqresv(
+ xfs_trans_t *tp,
+ xfs_mount_t *mp,
+ xfs_dquot_t *dqp,
+ long nblks,
+ long ninos,
+ uint flags)
+{
+ int error;
+ xfs_qcnt_t hardlimit;
+ xfs_qcnt_t softlimit;
+ time_t btimer;
+ xfs_qcnt_t *resbcountp;
+ xfs_quotainfo_t *q = mp->m_quotainfo;
+
+ if (! (flags & XFS_QMOPT_DQLOCK)) {
+ xfs_dqlock(dqp);
+ }
+ ASSERT(XFS_DQ_IS_LOCKED(dqp));
+ if (flags & XFS_TRANS_DQ_RES_BLKS) {
+ hardlimit = INT_GET(dqp->q_core.d_blk_hardlimit, ARCH_CONVERT);
+ if (!hardlimit)
+ hardlimit = q->qi_bhardlimit;
+ softlimit = INT_GET(dqp->q_core.d_blk_softlimit, ARCH_CONVERT);
+ if (!softlimit)
+ softlimit = q->qi_bsoftlimit;
+ btimer = INT_GET(dqp->q_core.d_btimer, ARCH_CONVERT);
+ resbcountp = &dqp->q_res_bcount;
+ } else {
+ ASSERT(flags & XFS_TRANS_DQ_RES_RTBLKS);
+ hardlimit = INT_GET(dqp->q_core.d_rtb_hardlimit, ARCH_CONVERT);
+ if (!hardlimit)
+ hardlimit = q->qi_rtbhardlimit;
+ softlimit = INT_GET(dqp->q_core.d_rtb_softlimit, ARCH_CONVERT);
+ if (!softlimit)
+ softlimit = q->qi_rtbsoftlimit;
+ btimer = INT_GET(dqp->q_core.d_rtbtimer, ARCH_CONVERT);
+ resbcountp = &dqp->q_res_rtbcount;
+ }
+ error = 0;
+
+ if ((flags & XFS_QMOPT_FORCE_RES) == 0 &&
+ !INT_ISZERO(dqp->q_core.d_id, ARCH_CONVERT) &&
+ XFS_IS_QUOTA_ENFORCED(dqp->q_mount)) {
+#ifdef QUOTADEBUG
+ cmn_err(CE_DEBUG, "BLK Res: nblks=%ld + resbcount=%Ld"
+ " > hardlimit=%Ld?", nblks, *resbcountp, hardlimit);
+#endif
+ if (nblks > 0) {
+ /*
+ * dquot is locked already. See if we'd go over the
+ * hardlimit or exceed the timelimit if we allocate
+ * nblks.
+ */
+ if (hardlimit > 0ULL &&
+ (hardlimit <= nblks + *resbcountp)) {
+ error = EDQUOT;
+ goto error_return;
+ }
+
+ if (softlimit > 0ULL &&
+ (softlimit <= nblks + *resbcountp)) {
+ /*
+ * If timer or warnings has expired,
+ * return EDQUOT
+ */
+ if ((btimer != 0 && get_seconds() > btimer) ||
+ (!INT_ISZERO(dqp->q_core.d_bwarns, ARCH_CONVERT) &&
+ INT_GET(dqp->q_core.d_bwarns, ARCH_CONVERT) >=
+ XFS_QI_BWARNLIMIT(dqp->q_mount))) {
+ error = EDQUOT;
+ goto error_return;
+ }
+ }
+ }
+ if (ninos > 0) {
+ hardlimit = INT_GET(dqp->q_core.d_ino_hardlimit, ARCH_CONVERT);
+ if (!hardlimit)
+ hardlimit = q->qi_ihardlimit;
+ softlimit = INT_GET(dqp->q_core.d_ino_softlimit, ARCH_CONVERT);
+ if (!softlimit)
+ softlimit = q->qi_isoftlimit;
+ if (hardlimit > 0ULL &&
+ INT_GET(dqp->q_core.d_icount, ARCH_CONVERT) >= hardlimit) {
+ error = EDQUOT;
+ goto error_return;
+ } else if (softlimit > 0ULL &&
+ INT_GET(dqp->q_core.d_icount, ARCH_CONVERT) >= softlimit) {
+ /*
+ * If timer or warnings has expired,
+ * return EDQUOT
+ */
+ if ((!INT_ISZERO(dqp->q_core.d_itimer, ARCH_CONVERT) &&
+ get_seconds() > INT_GET(dqp->q_core.d_itimer, ARCH_CONVERT)) ||
+ (!INT_ISZERO(dqp->q_core.d_iwarns, ARCH_CONVERT) &&
+ INT_GET(dqp->q_core.d_iwarns, ARCH_CONVERT) >=
+ XFS_QI_IWARNLIMIT(dqp->q_mount))) {
+ error = EDQUOT;
+ goto error_return;
+ }
+ }
+ }
+ }
+
+ /*
+ * Change the reservation, but not the actual usage.
+ * Note that q_res_bcount = q_core.d_bcount + resv
+ */
+ (*resbcountp) += (xfs_qcnt_t)nblks;
+ if (ninos != 0)
+ dqp->q_res_icount += (xfs_qcnt_t)ninos;
+
+ /*
+ * note the reservation amt in the trans struct too,
+ * so that the transaction knows how much was reserved by
+ * it against this particular dquot.
+ * We don't do this when we are reserving for a delayed allocation,
+ * because we don't have the luxury of a transaction envelope then.
+ */
+ if (tp) {
+ ASSERT(tp->t_dqinfo);
+ ASSERT(flags & XFS_QMOPT_RESBLK_MASK);
+ if (nblks != 0)
+ xfs_trans_mod_dquot(tp, dqp,
+ flags & XFS_QMOPT_RESBLK_MASK,
+ nblks);
+ if (ninos != 0)
+ xfs_trans_mod_dquot(tp, dqp,
+ XFS_TRANS_DQ_RES_INOS,
+ ninos);
+ }
+ ASSERT(dqp->q_res_bcount >= INT_GET(dqp->q_core.d_bcount, ARCH_CONVERT));
+ ASSERT(dqp->q_res_rtbcount >= INT_GET(dqp->q_core.d_rtbcount, ARCH_CONVERT));
+ ASSERT(dqp->q_res_icount >= INT_GET(dqp->q_core.d_icount, ARCH_CONVERT));
+
+error_return:
+ if (! (flags & XFS_QMOPT_DQLOCK)) {
+ xfs_dqunlock(dqp);
+ }
+ return (error);
+}
+
+
+/*
+ * Given a dquot(s), make disk block and/or inode reservations against them.
+ * The fact that this does the reservation against both the usr and
+ * grp quotas is important, because this follows a both-or-nothing
+ * approach.
+ *
+ * flags = XFS_QMOPT_DQLOCK indicate if dquot(s) need to be locked.
+ * XFS_QMOPT_FORCE_RES evades limit enforcement. Used by chown.
+ * XFS_TRANS_DQ_RES_BLKS reserves regular disk blocks
+ * XFS_TRANS_DQ_RES_RTBLKS reserves realtime disk blocks
+ * dquots are unlocked on return, if they were not locked by caller.
+ */
+int
+xfs_trans_reserve_quota_bydquots(
+ xfs_trans_t *tp,
+ xfs_mount_t *mp,
+ xfs_dquot_t *udqp,
+ xfs_dquot_t *gdqp,
+ long nblks,
+ long ninos,
+ uint flags)
+{
+ int resvd;
+
+ if (! XFS_IS_QUOTA_ON(mp))
+ return (0);
+
+ if (tp && tp->t_dqinfo == NULL)
+ xfs_trans_alloc_dqinfo(tp);
+
+ ASSERT(flags & XFS_QMOPT_RESBLK_MASK);
+ resvd = 0;
+
+ if (udqp) {
+ if (xfs_trans_dqresv(tp, mp, udqp, nblks, ninos, flags))
+ return (EDQUOT);
+ resvd = 1;
+ }
+
+ if (gdqp) {
+ if (xfs_trans_dqresv(tp, mp, gdqp, nblks, ninos, flags)) {
+ /*
+ * can't do it, so backout previous reservation
+ */
+ if (resvd) {
+ flags |= XFS_QMOPT_FORCE_RES;
+ xfs_trans_dqresv(tp, mp, udqp,
+ -nblks, -ninos, flags);
+ }
+ return (EDQUOT);
+ }
+ }
+
+ /*
+ * Didnt change anything critical, so, no need to log
+ */
+ return (0);
+}
+
+
+/*
+ * Lock the dquot and change the reservation if we can.
+ * This doesn't change the actual usage, just the reservation.
+ * The inode sent in is locked.
+ *
+ * Returns 0 on success, EDQUOT or other errors otherwise
+ */
+STATIC int
+xfs_trans_reserve_quota_nblks(
+ xfs_trans_t *tp,
+ xfs_mount_t *mp,
+ xfs_inode_t *ip,
+ long nblks,
+ long ninos,
+ uint type)
+{
+ int error;
+
+ if (!XFS_IS_QUOTA_ON(mp))
+ return (0);
+
+ ASSERT(ip->i_ino != mp->m_sb.sb_uquotino);
+ ASSERT(ip->i_ino != mp->m_sb.sb_gquotino);
+
+ ASSERT(XFS_ISLOCKED_INODE_EXCL(ip));
+ ASSERT(XFS_IS_QUOTA_RUNNING(ip->i_mount));
+ ASSERT((type & ~XFS_QMOPT_FORCE_RES) == XFS_TRANS_DQ_RES_RTBLKS ||
+ (type & ~XFS_QMOPT_FORCE_RES) == XFS_TRANS_DQ_RES_BLKS);
+
+ /*
+ * Reserve nblks against these dquots, with trans as the mediator.
+ */
+ error = xfs_trans_reserve_quota_bydquots(tp, mp,
+ ip->i_udquot, ip->i_gdquot,
+ nblks, ninos,
+ type);
+ return (error);
+}
+
+/*
+ * This routine is called to allocate a quotaoff log item.
+ */
+xfs_qoff_logitem_t *
+xfs_trans_get_qoff_item(
+ xfs_trans_t *tp,
+ xfs_qoff_logitem_t *startqoff,
+ uint flags)
+{
+ xfs_qoff_logitem_t *q;
+
+ ASSERT(tp != NULL);
+
+ q = xfs_qm_qoff_logitem_init(tp->t_mountp, startqoff, flags);
+ ASSERT(q != NULL);
+
+ /*
+ * Get a log_item_desc to point at the new item.
+ */
+ (void) xfs_trans_add_item(tp, (xfs_log_item_t*)q);
+
+ return (q);
+}
+
+
+/*
+ * This is called to mark the quotaoff logitem as needing
+ * to be logged when the transaction is committed. The logitem must
+ * already be associated with the given transaction.
+ */
+void
+xfs_trans_log_quotaoff_item(
+ xfs_trans_t *tp,
+ xfs_qoff_logitem_t *qlp)
+{
+ xfs_log_item_desc_t *lidp;
+
+ lidp = xfs_trans_find_item(tp, (xfs_log_item_t *)qlp);
+ ASSERT(lidp != NULL);
+
+ tp->t_flags |= XFS_TRANS_DIRTY;
+ lidp->lid_flags |= XFS_LID_DIRTY;
+}
+
+STATIC void
+xfs_trans_alloc_dqinfo(
+ xfs_trans_t *tp)
+{
+ (tp)->t_dqinfo = kmem_zone_zalloc(xfs_Gqm->qm_dqtrxzone, KM_SLEEP);
+}
+
+STATIC void
+xfs_trans_free_dqinfo(
+ xfs_trans_t *tp)
+{
+ if (!tp->t_dqinfo)
+ return;
+ kmem_zone_free(xfs_Gqm->qm_dqtrxzone, (tp)->t_dqinfo);
+ (tp)->t_dqinfo = NULL;
+}
+
+xfs_dqtrxops_t xfs_trans_dquot_ops = {
+ .qo_dup_dqinfo = xfs_trans_dup_dqinfo,
+ .qo_free_dqinfo = xfs_trans_free_dqinfo,
+ .qo_mod_dquot_byino = xfs_trans_mod_dquot_byino,
+ .qo_apply_dquot_deltas = xfs_trans_apply_dquot_deltas,
+ .qo_reserve_quota_nblks = xfs_trans_reserve_quota_nblks,
+ .qo_reserve_quota_bydquots = xfs_trans_reserve_quota_bydquots,
+ .qo_unreserve_and_mod_dquots = xfs_trans_unreserve_and_mod_dquots,
+};
diff --git a/fs/xfs/support/Makefile b/fs/xfs/support/Makefile
new file mode 100644
index 00000000000000..fb039f59312d22
--- /dev/null
+++ b/fs/xfs/support/Makefile
@@ -0,0 +1,51 @@
+#
+# Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of version 2 of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like. Any license provided herein, whether implied or
+# otherwise, applies only to this software file. Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write the Free Software Foundation, Inc., 59
+# Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+# Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+# Mountain View, CA 94043, or:
+#
+# http://www.sgi.com
+#
+# For further information regarding this notice, see:
+#
+# http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+#
+
+EXTRA_CFLAGS += -I.. -I $(TOPDIR)/fs/xfs/linux-2.4
+
+ifeq ($(CONFIG_XFS_DEBUG),y)
+ EXTRA_CFLAGS += -DDEBUG
+endif
+
+O_TARGET := support_xfs.o
+ifneq ($(MAKECMDGOALS),modules_install)
+ obj-m := $(O_TARGET)
+endif
+
+obj-y := debug.o \
+ move.o \
+ qsort.o \
+ uuid.o
+
+obj-$(CONFIG_XFS_TRACE) += ktrace.o
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/xfs/support/debug.c b/fs/xfs/support/debug.c
new file mode 100644
index 00000000000000..2e5a3e97b41eec
--- /dev/null
+++ b/fs/xfs/support/debug.c
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "debug.h"
+
+#include <asm/page.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+
+int doass = 1;
+static char message[256]; /* keep it off the stack */
+static spinlock_t xfs_err_lock = SPIN_LOCK_UNLOCKED;
+
+/* Translate from CE_FOO to KERN_FOO, err_level(CE_FOO) == KERN_FOO */
+#define XFS_MAX_ERR_LEVEL 7
+#define XFS_ERR_MASK ((1 << 3) - 1)
+static char *err_level[XFS_MAX_ERR_LEVEL+1] =
+ {KERN_EMERG, KERN_ALERT, KERN_CRIT,
+ KERN_ERR, KERN_WARNING, KERN_NOTICE,
+ KERN_INFO, KERN_DEBUG};
+
+void
+assfail(char *a, char *f, int l)
+{
+ printk("XFS assertion failed: %s, file: %s, line: %d\n", a, f, l);
+ BUG();
+}
+
+#if ((defined(DEBUG) || defined(INDUCE_IO_ERRROR)) && !defined(NO_WANT_RANDOM))
+
+unsigned long
+random(void)
+{
+ static unsigned long RandomValue = 1;
+ /* cycles pseudo-randomly through all values between 1 and 2^31 - 2 */
+ register long rv = RandomValue;
+ register long lo;
+ register long hi;
+
+ hi = rv / 127773;
+ lo = rv % 127773;
+ rv = 16807 * lo - 2836 * hi;
+ if( rv <= 0 ) rv += 2147483647;
+ return( RandomValue = rv );
+}
+
+int
+get_thread_id(void)
+{
+ return current->pid;
+}
+
+#endif /* DEBUG || INDUCE_IO_ERRROR || !NO_WANT_RANDOM */
+
+void
+cmn_err(register int level, char *fmt, ...)
+{
+ char *fp = fmt;
+ int len;
+ ulong flags;
+ va_list ap;
+
+ level &= XFS_ERR_MASK;
+ if (level > XFS_MAX_ERR_LEVEL)
+ level = XFS_MAX_ERR_LEVEL;
+ spin_lock_irqsave(&xfs_err_lock,flags);
+ va_start(ap, fmt);
+ if (*fmt == '!') fp++;
+ len = vsprintf(message, fp, ap);
+ if (message[len-1] != '\n')
+ strcat(message, "\n");
+ printk("%s%s", err_level[level], message);
+ va_end(ap);
+ spin_unlock_irqrestore(&xfs_err_lock,flags);
+
+ if (level == CE_PANIC)
+ BUG();
+}
+
+
+void
+icmn_err(register int level, char *fmt, va_list ap)
+{
+ ulong flags;
+ int len;
+
+ level &= XFS_ERR_MASK;
+ if(level > XFS_MAX_ERR_LEVEL)
+ level = XFS_MAX_ERR_LEVEL;
+ spin_lock_irqsave(&xfs_err_lock,flags);
+ len = vsprintf(message, fmt, ap);
+ if (message[len-1] != '\n')
+ strcat(message, "\n");
+ spin_unlock_irqrestore(&xfs_err_lock,flags);
+ printk("%s%s", err_level[level], message);
+ if (level == CE_PANIC)
+ BUG();
+}
diff --git a/fs/xfs/support/debug.h b/fs/xfs/support/debug.h
new file mode 100644
index 00000000000000..40b0f4c54d9e5f
--- /dev/null
+++ b/fs/xfs/support/debug.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_SUPPORT_DEBUG_H__
+#define __XFS_SUPPORT_DEBUG_H__
+
+#include <stdarg.h>
+
+#define CE_DEBUG 7 /* debug */
+#define CE_CONT 6 /* continuation */
+#define CE_NOTE 5 /* notice */
+#define CE_WARN 4 /* warning */
+#define CE_ALERT 1 /* alert */
+#define CE_PANIC 0 /* panic */
+
+extern void icmn_err(int, char *, va_list);
+/* PRINTFLIKE2 */
+extern void cmn_err(int, char *, ...);
+
+#ifndef STATIC
+# define STATIC static
+#endif
+
+#ifdef DEBUG
+# ifdef lint
+# define ASSERT(EX) ((void)0) /* avoid "constant in conditional" babble */
+# else
+# define ASSERT(EX) ((!doass||(EX))?((void)0):assfail(#EX, __FILE__, __LINE__))
+# endif /* lint */
+#else
+# define ASSERT(x) ((void)0)
+#endif
+
+extern int doass; /* dynamically turn off asserts */
+extern void assfail(char *, char *, int);
+#ifdef DEBUG
+extern unsigned long random(void);
+extern int get_thread_id(void);
+#endif
+
+#define ASSERT_ALWAYS(EX) ((EX)?((void)0):assfail(#EX, __FILE__, __LINE__))
+#define debug_stop_all_cpus(param) /* param is "cpumask_t *" */
+
+#endif /* __XFS_SUPPORT_DEBUG_H__ */
diff --git a/fs/xfs/support/ktrace.c b/fs/xfs/support/ktrace.c
new file mode 100644
index 00000000000000..3dae14c8c55a14
--- /dev/null
+++ b/fs/xfs/support/ktrace.c
@@ -0,0 +1,346 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include <xfs.h>
+
+static kmem_zone_t *ktrace_hdr_zone;
+static kmem_zone_t *ktrace_ent_zone;
+static int ktrace_zentries;
+
+void
+ktrace_init(int zentries)
+{
+ ktrace_zentries = zentries;
+
+ ktrace_hdr_zone = kmem_zone_init(sizeof(ktrace_t),
+ "ktrace_hdr");
+ ASSERT(ktrace_hdr_zone);
+
+ ktrace_ent_zone = kmem_zone_init(ktrace_zentries
+ * sizeof(ktrace_entry_t),
+ "ktrace_ent");
+ ASSERT(ktrace_ent_zone);
+}
+
+void
+ktrace_uninit(void)
+{
+ kmem_cache_destroy(ktrace_hdr_zone);
+ kmem_cache_destroy(ktrace_ent_zone);
+}
+
+/*
+ * ktrace_alloc()
+ *
+ * Allocate a ktrace header and enough buffering for the given
+ * number of entries.
+ */
+ktrace_t *
+ktrace_alloc(int nentries, int sleep)
+{
+ ktrace_t *ktp;
+ ktrace_entry_t *ktep;
+
+ ktp = (ktrace_t*)kmem_zone_alloc(ktrace_hdr_zone, sleep);
+
+ if (ktp == (ktrace_t*)NULL) {
+ /*
+ * KM_SLEEP callers don't expect failure.
+ */
+ if (sleep & KM_SLEEP)
+ panic("ktrace_alloc: NULL memory on KM_SLEEP request!");
+
+ return NULL;
+ }
+
+ /*
+ * Special treatment for buffers with the ktrace_zentries entries
+ */
+ if (nentries == ktrace_zentries) {
+ ktep = (ktrace_entry_t*)kmem_zone_zalloc(ktrace_ent_zone,
+ sleep);
+ } else {
+ ktep = (ktrace_entry_t*)kmem_zalloc((nentries * sizeof(*ktep)),
+ sleep);
+ }
+
+ if (ktep == NULL) {
+ /*
+ * KM_SLEEP callers don't expect failure.
+ */
+ if (sleep & KM_SLEEP)
+ panic("ktrace_alloc: NULL memory on KM_SLEEP request!");
+
+ kmem_free(ktp, sizeof(*ktp));
+
+ return NULL;
+ }
+
+ spinlock_init(&(ktp->kt_lock), "kt_lock");
+
+ ktp->kt_entries = ktep;
+ ktp->kt_nentries = nentries;
+ ktp->kt_index = 0;
+ ktp->kt_rollover = 0;
+ return ktp;
+}
+
+
+/*
+ * ktrace_free()
+ *
+ * Free up the ktrace header and buffer. It is up to the caller
+ * to ensure that no-one is referencing it.
+ */
+void
+ktrace_free(ktrace_t *ktp)
+{
+ int entries_size;
+
+ if (ktp == (ktrace_t *)NULL)
+ return;
+
+ spinlock_destroy(&ktp->kt_lock);
+
+ /*
+ * Special treatment for the Vnode trace buffer.
+ */
+ if (ktp->kt_nentries == ktrace_zentries) {
+ kmem_zone_free(ktrace_ent_zone, ktp->kt_entries);
+ } else {
+ entries_size = (int)(ktp->kt_nentries * sizeof(ktrace_entry_t));
+
+ kmem_free(ktp->kt_entries, entries_size);
+ }
+
+ kmem_zone_free(ktrace_hdr_zone, ktp);
+}
+
+
+/*
+ * Enter the given values into the "next" entry in the trace buffer.
+ * kt_index is always the index of the next entry to be filled.
+ */
+void
+ktrace_enter(
+ ktrace_t *ktp,
+ void *val0,
+ void *val1,
+ void *val2,
+ void *val3,
+ void *val4,
+ void *val5,
+ void *val6,
+ void *val7,
+ void *val8,
+ void *val9,
+ void *val10,
+ void *val11,
+ void *val12,
+ void *val13,
+ void *val14,
+ void *val15)
+{
+ static lock_t wrap_lock = SPIN_LOCK_UNLOCKED;
+ unsigned long flags;
+ int index;
+ ktrace_entry_t *ktep;
+
+ ASSERT(ktp != NULL);
+
+ /*
+ * Grab an entry by pushing the index up to the next one.
+ */
+ spin_lock_irqsave(&wrap_lock, flags);
+ index = ktp->kt_index;
+ if (++ktp->kt_index == ktp->kt_nentries)
+ ktp->kt_index = 0;
+ spin_unlock_irqrestore(&wrap_lock, flags);
+
+ if (!ktp->kt_rollover && index == ktp->kt_nentries - 1)
+ ktp->kt_rollover = 1;
+
+ ASSERT((index >= 0) && (index < ktp->kt_nentries));
+
+ ktep = &(ktp->kt_entries[index]);
+
+ ktep->val[0] = val0;
+ ktep->val[1] = val1;
+ ktep->val[2] = val2;
+ ktep->val[3] = val3;
+ ktep->val[4] = val4;
+ ktep->val[5] = val5;
+ ktep->val[6] = val6;
+ ktep->val[7] = val7;
+ ktep->val[8] = val8;
+ ktep->val[9] = val9;
+ ktep->val[10] = val10;
+ ktep->val[11] = val11;
+ ktep->val[12] = val12;
+ ktep->val[13] = val13;
+ ktep->val[14] = val14;
+ ktep->val[15] = val15;
+}
+
+/*
+ * Return the number of entries in the trace buffer.
+ */
+int
+ktrace_nentries(
+ ktrace_t *ktp)
+{
+ if (ktp == NULL) {
+ return 0;
+ }
+
+ return (ktp->kt_rollover ? ktp->kt_nentries : ktp->kt_index);
+}
+
+/*
+ * ktrace_first()
+ *
+ * This is used to find the start of the trace buffer.
+ * In conjunction with ktrace_next() it can be used to
+ * iterate through the entire trace buffer. This code does
+ * not do any locking because it is assumed that it is called
+ * from the debugger.
+ *
+ * The caller must pass in a pointer to a ktrace_snap
+ * structure in which we will keep some state used to
+ * iterate through the buffer. This state must not touched
+ * by any code outside of this module.
+ */
+ktrace_entry_t *
+ktrace_first(ktrace_t *ktp, ktrace_snap_t *ktsp)
+{
+ ktrace_entry_t *ktep;
+ int index;
+ int nentries;
+
+ if (ktp->kt_rollover)
+ index = ktp->kt_index;
+ else
+ index = 0;
+
+ ktsp->ks_start = index;
+ ktep = &(ktp->kt_entries[index]);
+
+ nentries = ktrace_nentries(ktp);
+ index++;
+ if (index < nentries) {
+ ktsp->ks_index = index;
+ } else {
+ ktsp->ks_index = 0;
+ if (index > nentries)
+ ktep = NULL;
+ }
+ return ktep;
+}
+
+/*
+ * ktrace_next()
+ *
+ * This is used to iterate through the entries of the given
+ * trace buffer. The caller must pass in the ktrace_snap_t
+ * structure initialized by ktrace_first(). The return value
+ * will be either a pointer to the next ktrace_entry or NULL
+ * if all of the entries have been traversed.
+ */
+ktrace_entry_t *
+ktrace_next(
+ ktrace_t *ktp,
+ ktrace_snap_t *ktsp)
+{
+ int index;
+ ktrace_entry_t *ktep;
+
+ index = ktsp->ks_index;
+ if (index == ktsp->ks_start) {
+ ktep = NULL;
+ } else {
+ ktep = &ktp->kt_entries[index];
+ }
+
+ index++;
+ if (index == ktrace_nentries(ktp)) {
+ ktsp->ks_index = 0;
+ } else {
+ ktsp->ks_index = index;
+ }
+
+ return ktep;
+}
+
+/*
+ * ktrace_skip()
+ *
+ * Skip the next "count" entries and return the entry after that.
+ * Return NULL if this causes us to iterate past the beginning again.
+ */
+ktrace_entry_t *
+ktrace_skip(
+ ktrace_t *ktp,
+ int count,
+ ktrace_snap_t *ktsp)
+{
+ int index;
+ int new_index;
+ ktrace_entry_t *ktep;
+ int nentries = ktrace_nentries(ktp);
+
+ index = ktsp->ks_index;
+ new_index = index + count;
+ while (new_index >= nentries) {
+ new_index -= nentries;
+ }
+ if (index == ktsp->ks_start) {
+ /*
+ * We've iterated around to the start, so we're done.
+ */
+ ktep = NULL;
+ } else if ((new_index < index) && (index < ktsp->ks_index)) {
+ /*
+ * We've skipped past the start again, so we're done.
+ */
+ ktep = NULL;
+ ktsp->ks_index = ktsp->ks_start;
+ } else {
+ ktep = &(ktp->kt_entries[new_index]);
+ new_index++;
+ if (new_index == nentries) {
+ ktsp->ks_index = 0;
+ } else {
+ ktsp->ks_index = new_index;
+ }
+ }
+ return ktep;
+}
diff --git a/fs/xfs/support/ktrace.h b/fs/xfs/support/ktrace.h
new file mode 100644
index 00000000000000..92d1a1a5d04b64
--- /dev/null
+++ b/fs/xfs/support/ktrace.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_SUPPORT_KTRACE_H__
+#define __XFS_SUPPORT_KTRACE_H__
+
+#include <spin.h>
+
+/*
+ * Trace buffer entry structure.
+ */
+typedef struct ktrace_entry {
+ void *val[16];
+} ktrace_entry_t;
+
+/*
+ * Trace buffer header structure.
+ */
+typedef struct ktrace {
+ lock_t kt_lock; /* mutex to guard counters */
+ int kt_nentries; /* number of entries in trace buf */
+ int kt_index; /* current index in entries */
+ int kt_rollover;
+ ktrace_entry_t *kt_entries; /* buffer of entries */
+} ktrace_t;
+
+/*
+ * Trace buffer snapshot structure.
+ */
+typedef struct ktrace_snap {
+ int ks_start; /* kt_index at time of snap */
+ int ks_index; /* current index */
+} ktrace_snap_t;
+
+
+#ifdef CONFIG_XFS_TRACE
+
+extern void ktrace_init(int zentries);
+extern void ktrace_uninit(void);
+
+extern ktrace_t *ktrace_alloc(int, int);
+extern void ktrace_free(ktrace_t *);
+
+extern void ktrace_enter(
+ ktrace_t *,
+ void *,
+ void *,
+ void *,
+ void *,
+ void *,
+ void *,
+ void *,
+ void *,
+ void *,
+ void *,
+ void *,
+ void *,
+ void *,
+ void *,
+ void *,
+ void *);
+
+extern ktrace_entry_t *ktrace_first(ktrace_t *, ktrace_snap_t *);
+extern int ktrace_nentries(ktrace_t *);
+extern ktrace_entry_t *ktrace_next(ktrace_t *, ktrace_snap_t *);
+extern ktrace_entry_t *ktrace_skip(ktrace_t *, int, ktrace_snap_t *);
+
+#else
+#define ktrace_init(x) do { } while (0)
+#define ktrace_uninit() do { } while (0)
+#endif /* CONFIG_XFS_TRACE */
+
+#endif /* __XFS_SUPPORT_KTRACE_H__ */
diff --git a/fs/xfs/support/move.c b/fs/xfs/support/move.c
new file mode 100644
index 00000000000000..15b5194f16b29d
--- /dev/null
+++ b/fs/xfs/support/move.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include <xfs.h>
+
+/* Read from kernel buffer at src to user/kernel buffer defined
+ * by the uio structure. Advance the pointer in the uio struct
+ * as we go.
+ */
+int
+uio_read(caddr_t src, size_t len, struct uio *uio)
+{
+ size_t count;
+
+ if (!len || !uio->uio_resid)
+ return 0;
+
+ count = uio->uio_iov->iov_len;
+ if (!count)
+ return 0;
+ if (count > len)
+ count = len;
+
+ if (uio->uio_segflg == UIO_USERSPACE) {
+ if (copy_to_user(uio->uio_iov->iov_base, src, count))
+ return EFAULT;
+ } else {
+ ASSERT(uio->uio_segflg == UIO_SYSSPACE);
+ memcpy(uio->uio_iov->iov_base, src, count);
+ }
+
+ uio->uio_iov->iov_base = (void*)((char*)uio->uio_iov->iov_base + count);
+ uio->uio_iov->iov_len -= count;
+ uio->uio_offset += count;
+ uio->uio_resid -= count;
+ return 0;
+}
diff --git a/fs/xfs/support/move.h b/fs/xfs/support/move.h
new file mode 100644
index 00000000000000..3d406dc1c89e9e
--- /dev/null
+++ b/fs/xfs/support/move.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ *
+ * Portions Copyright (c) 1982, 1986, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#ifndef __XFS_SUPPORT_MOVE_H__
+#define __XFS_SUPPORT_MOVE_H__
+
+#include <linux/uio.h>
+#include <asm/uaccess.h>
+
+/* Segment flag values. */
+enum uio_seg {
+ UIO_USERSPACE, /* from user data space */
+ UIO_SYSSPACE, /* from system space */
+};
+
+struct uio {
+ struct iovec *uio_iov; /* pointer to array of iovecs */
+ int uio_iovcnt; /* number of iovecs in array */
+ xfs_off_t uio_offset; /* offset in file this uio corresponds to */
+ int uio_resid; /* residual i/o count */
+ enum uio_seg uio_segflg; /* see above */
+};
+
+typedef struct uio uio_t;
+typedef struct iovec iovec_t;
+
+extern int uio_read (caddr_t, size_t, uio_t *);
+
+#endif /* __XFS_SUPPORT_MOVE_H__ */
diff --git a/fs/xfs/support/qsort.c b/fs/xfs/support/qsort.c
new file mode 100644
index 00000000000000..1ec824140cf72e
--- /dev/null
+++ b/fs/xfs/support/qsort.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+/*
+ * Qsort routine from Bentley & McIlroy's "Engineering a Sort Function".
+ */
+#define swapcode(TYPE, parmi, parmj, n) { \
+ long i = (n) / sizeof (TYPE); \
+ register TYPE *pi = (TYPE *) (parmi); \
+ register TYPE *pj = (TYPE *) (parmj); \
+ do { \
+ register TYPE t = *pi; \
+ *pi++ = *pj; \
+ *pj++ = t; \
+ } while (--i > 0); \
+}
+
+#define SWAPINIT(a, es) swaptype = ((char *)a - (char *)0) % sizeof(long) || \
+ es % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1;
+
+static __inline void
+swapfunc(char *a, char *b, int n, int swaptype)
+{
+ if (swaptype <= 1)
+ swapcode(long, a, b, n)
+ else
+ swapcode(char, a, b, n)
+}
+
+#define swap(a, b) \
+ if (swaptype == 0) { \
+ long t = *(long *)(a); \
+ *(long *)(a) = *(long *)(b); \
+ *(long *)(b) = t; \
+ } else \
+ swapfunc(a, b, es, swaptype)
+
+#define vecswap(a, b, n) if ((n) > 0) swapfunc(a, b, n, swaptype)
+
+static __inline char *
+med3(char *a, char *b, char *c, int (*cmp)(const void *, const void *))
+{
+ return cmp(a, b) < 0 ?
+ (cmp(b, c) < 0 ? b : (cmp(a, c) < 0 ? c : a ))
+ :(cmp(b, c) > 0 ? b : (cmp(a, c) < 0 ? a : c ));
+}
+
+void
+qsort(void *aa, size_t n, size_t es, int (*cmp)(const void *, const void *))
+{
+ char *pa, *pb, *pc, *pd, *pl, *pm, *pn;
+ int d, r, swaptype, swap_cnt;
+ register char *a = aa;
+
+loop: SWAPINIT(a, es);
+ swap_cnt = 0;
+ if (n < 7) {
+ for (pm = (char *)a + es; pm < (char *) a + n * es; pm += es)
+ for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0;
+ pl -= es)
+ swap(pl, pl - es);
+ return;
+ }
+ pm = (char *)a + (n / 2) * es;
+ if (n > 7) {
+ pl = (char *)a;
+ pn = (char *)a + (n - 1) * es;
+ if (n > 40) {
+ d = (n / 8) * es;
+ pl = med3(pl, pl + d, pl + 2 * d, cmp);
+ pm = med3(pm - d, pm, pm + d, cmp);
+ pn = med3(pn - 2 * d, pn - d, pn, cmp);
+ }
+ pm = med3(pl, pm, pn, cmp);
+ }
+ swap(a, pm);
+ pa = pb = (char *)a + es;
+
+ pc = pd = (char *)a + (n - 1) * es;
+ for (;;) {
+ while (pb <= pc && (r = cmp(pb, a)) <= 0) {
+ if (r == 0) {
+ swap_cnt = 1;
+ swap(pa, pb);
+ pa += es;
+ }
+ pb += es;
+ }
+ while (pb <= pc && (r = cmp(pc, a)) >= 0) {
+ if (r == 0) {
+ swap_cnt = 1;
+ swap(pc, pd);
+ pd -= es;
+ }
+ pc -= es;
+ }
+ if (pb > pc)
+ break;
+ swap(pb, pc);
+ swap_cnt = 1;
+ pb += es;
+ pc -= es;
+ }
+ if (swap_cnt == 0) { /* Switch to insertion sort */
+ for (pm = (char *) a + es; pm < (char *) a + n * es; pm += es)
+ for (pl = pm; pl > (char *) a && cmp(pl - es, pl) > 0;
+ pl -= es)
+ swap(pl, pl - es);
+ return;
+ }
+
+ pn = (char *)a + n * es;
+ r = min(pa - (char *)a, pb - pa);
+ vecswap(a, pb - r, r);
+ r = min((long)(pd - pc), (long)(pn - pd - es));
+ vecswap(pb, pn - r, r);
+ if ((r = pb - pa) > es)
+ qsort(a, r / es, es, cmp);
+ if ((r = pd - pc) > es) {
+ /* Iterate rather than recurse to save stack space */
+ a = pn - r;
+ n = r / es;
+ goto loop;
+ }
+/* qsort(pn - r, r / es, es, cmp);*/
+}
diff --git a/fs/xfs/support/qsort.h b/fs/xfs/support/qsort.h
new file mode 100644
index 00000000000000..94263106d716d8
--- /dev/null
+++ b/fs/xfs/support/qsort.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#ifndef QSORT_H
+#define QSORT_H
+
+extern void qsort (void *const pbase,
+ size_t total_elems,
+ size_t size,
+ int (*cmp)(const void *, const void *));
+
+#endif
diff --git a/fs/xfs/support/uuid.c b/fs/xfs/support/uuid.c
new file mode 100644
index 00000000000000..81f40cfcb2679a
--- /dev/null
+++ b/fs/xfs/support/uuid.c
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include <xfs.h>
+
+static mutex_t uuid_monitor;
+static int uuid_table_size;
+static uuid_t *uuid_table;
+
+void
+uuid_init(void)
+{
+ mutex_init(&uuid_monitor, MUTEX_DEFAULT, "uuid_monitor");
+}
+
+/*
+ * uuid_getnodeuniq - obtain the node unique fields of a UUID.
+ *
+ * This is not in any way a standard or condoned UUID function;
+ * it just something that's needed for user-level file handles.
+ */
+void
+uuid_getnodeuniq(uuid_t *uuid, int fsid [2])
+{
+ char *uu = (char *)uuid;
+
+ /* on IRIX, this function assumes big-endian fields within
+ * the uuid, so we use INT_GET to get the same result on
+ * little-endian systems
+ */
+
+ fsid[0] = (INT_GET(*(u_int16_t*)(uu+8), ARCH_CONVERT) << 16) +
+ INT_GET(*(u_int16_t*)(uu+4), ARCH_CONVERT);
+ fsid[1] = INT_GET(*(u_int32_t*)(uu ), ARCH_CONVERT);
+}
+
+void
+uuid_create_nil(uuid_t *uuid)
+{
+ memset(uuid, 0, sizeof(*uuid));
+}
+
+int
+uuid_is_nil(uuid_t *uuid)
+{
+ int i;
+ char *cp = (char *)uuid;
+
+ if (uuid == NULL)
+ return 0;
+ /* implied check of version number here... */
+ for (i = 0; i < sizeof *uuid; i++)
+ if (*cp++) return 0; /* not nil */
+ return 1; /* is nil */
+}
+
+int
+uuid_equal(uuid_t *uuid1, uuid_t *uuid2)
+{
+ return memcmp(uuid1, uuid2, sizeof(uuid_t)) ? 0 : 1;
+}
+
+/*
+ * Given a 128-bit uuid, return a 64-bit value by adding the top and bottom
+ * 64-bit words. NOTE: This function can not be changed EVER. Although
+ * brain-dead, some applications depend on this 64-bit value remaining
+ * persistent. Specifically, DMI vendors store the value as a persistent
+ * filehandle.
+ */
+__uint64_t
+uuid_hash64(uuid_t *uuid)
+{
+ __uint64_t *sp = (__uint64_t *)uuid;
+
+ return sp[0] + sp[1];
+}
+
+int
+uuid_table_insert(uuid_t *uuid)
+{
+ int i, hole;
+
+ mutex_lock(&uuid_monitor, PVFS);
+ for (i = 0, hole = -1; i < uuid_table_size; i++) {
+ if (uuid_is_nil(&uuid_table[i])) {
+ hole = i;
+ continue;
+ }
+ if (uuid_equal(uuid, &uuid_table[i])) {
+ mutex_unlock(&uuid_monitor);
+ return 0;
+ }
+ }
+ if (hole < 0) {
+ uuid_table = kmem_realloc(uuid_table,
+ (uuid_table_size + 1) * sizeof(*uuid_table),
+ uuid_table_size * sizeof(*uuid_table),
+ KM_SLEEP);
+ hole = uuid_table_size++;
+ }
+ uuid_table[hole] = *uuid;
+ mutex_unlock(&uuid_monitor);
+ return 1;
+}
+
+void
+uuid_table_remove(uuid_t *uuid)
+{
+ int i;
+
+ mutex_lock(&uuid_monitor, PVFS);
+ for (i = 0; i < uuid_table_size; i++) {
+ if (uuid_is_nil(&uuid_table[i]))
+ continue;
+ if (!uuid_equal(uuid, &uuid_table[i]))
+ continue;
+ uuid_create_nil(&uuid_table[i]);
+ break;
+ }
+ ASSERT(i < uuid_table_size);
+ mutex_unlock(&uuid_monitor);
+}
diff --git a/fs/xfs/support/uuid.h b/fs/xfs/support/uuid.h
new file mode 100644
index 00000000000000..5220ea58ba2b62
--- /dev/null
+++ b/fs/xfs/support/uuid.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_SUPPORT_UUID_H__
+#define __XFS_SUPPORT_UUID_H__
+
+typedef struct {
+ unsigned char __u_bits[16];
+} uuid_t;
+
+void uuid_init(void);
+void uuid_create_nil(uuid_t *uuid);
+int uuid_is_nil(uuid_t *uuid);
+int uuid_equal(uuid_t *uuid1, uuid_t *uuid2);
+void uuid_getnodeuniq(uuid_t *uuid, int fsid [2]);
+__uint64_t uuid_hash64(uuid_t *uuid);
+int uuid_table_insert(uuid_t *uuid);
+void uuid_table_remove(uuid_t *uuid);
+
+#endif /* __XFS_SUPPORT_UUID_H__ */
diff --git a/fs/xfs/xfs.h b/fs/xfs/xfs.h
new file mode 100644
index 00000000000000..844048e4fbeea7
--- /dev/null
+++ b/fs/xfs/xfs.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_H__
+#define __XFS_H__
+
+#include <linux-2.4/xfs_linux.h>
+
+#include <xfs_fs.h>
+#include <xfs_macros.h>
+
+#endif /* __XFS_H__ */
diff --git a/fs/xfs/xfs_acl.c b/fs/xfs/xfs_acl.c
new file mode 100644
index 00000000000000..b800f8f4e058d9
--- /dev/null
+++ b/fs/xfs/xfs_acl.c
@@ -0,0 +1,974 @@
+/*
+ * Copyright (c) 2001-2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+
+#include "xfs_inum.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_btree.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode.h"
+#include "xfs_acl.h"
+#include "xfs_mac.h"
+#include "xfs_attr.h"
+
+#include <linux/posix_acl_xattr.h>
+
+STATIC int xfs_acl_setmode(vnode_t *, xfs_acl_t *, int *);
+STATIC void xfs_acl_filter_mode(mode_t, xfs_acl_t *);
+STATIC void xfs_acl_get_endian(xfs_acl_t *);
+STATIC int xfs_acl_access(uid_t, gid_t, xfs_acl_t *, mode_t, cred_t *);
+STATIC int xfs_acl_invalid(xfs_acl_t *);
+STATIC void xfs_acl_sync_mode(mode_t, xfs_acl_t *);
+STATIC void xfs_acl_get_attr(vnode_t *, xfs_acl_t *, int, int, int *);
+STATIC void xfs_acl_set_attr(vnode_t *, xfs_acl_t *, int, int *);
+STATIC int xfs_acl_allow_set(vnode_t *, int);
+
+kmem_zone_t *xfs_acl_zone;
+
+
+/*
+ * Test for existence of access ACL attribute as efficiently as possible.
+ */
+int
+xfs_acl_vhasacl_access(
+ vnode_t *vp)
+{
+ int error;
+
+ xfs_acl_get_attr(vp, NULL, _ACL_TYPE_ACCESS, ATTR_KERNOVAL, &error);
+ return (error == 0);
+}
+
+/*
+ * Test for existence of default ACL attribute as efficiently as possible.
+ */
+int
+xfs_acl_vhasacl_default(
+ vnode_t *vp)
+{
+ int error;
+
+ if (vp->v_type != VDIR)
+ return 0;
+ xfs_acl_get_attr(vp, NULL, _ACL_TYPE_DEFAULT, ATTR_KERNOVAL, &error);
+ return (error == 0);
+}
+
+/*
+ * Convert from extended attribute representation to in-memory for XFS.
+ */
+STATIC int
+posix_acl_xattr_to_xfs(
+ posix_acl_xattr_header *src,
+ size_t size,
+ xfs_acl_t *dest)
+{
+ posix_acl_xattr_entry *src_entry;
+ xfs_acl_entry_t *dest_entry;
+ int n;
+
+ if (!src || !dest)
+ return EINVAL;
+
+ if (size < sizeof(posix_acl_xattr_header))
+ return EINVAL;
+
+ if (src->a_version != cpu_to_le32(POSIX_ACL_XATTR_VERSION))
+ return EOPNOTSUPP;
+
+ memset(dest, 0, sizeof(xfs_acl_t));
+ dest->acl_cnt = posix_acl_xattr_count(size);
+ if (dest->acl_cnt < 0 || dest->acl_cnt > XFS_ACL_MAX_ENTRIES)
+ return EINVAL;
+
+ /*
+ * acl_set_file(3) may request that we set default ACLs with
+ * zero length -- defend (gracefully) against that here.
+ */
+ if (!dest->acl_cnt)
+ return 0;
+
+ src_entry = (posix_acl_xattr_entry *)((char *)src + sizeof(*src));
+ dest_entry = &dest->acl_entry[0];
+
+ for (n = 0; n < dest->acl_cnt; n++, src_entry++, dest_entry++) {
+ dest_entry->ae_perm = le16_to_cpu(src_entry->e_perm);
+ if (_ACL_PERM_INVALID(dest_entry->ae_perm))
+ return EINVAL;
+ dest_entry->ae_tag = le16_to_cpu(src_entry->e_tag);
+ switch(dest_entry->ae_tag) {
+ case ACL_USER:
+ case ACL_GROUP:
+ dest_entry->ae_id = le32_to_cpu(src_entry->e_id);
+ break;
+ case ACL_USER_OBJ:
+ case ACL_GROUP_OBJ:
+ case ACL_MASK:
+ case ACL_OTHER:
+ dest_entry->ae_id = ACL_UNDEFINED_ID;
+ break;
+ default:
+ return EINVAL;
+ }
+ }
+ if (xfs_acl_invalid(dest))
+ return EINVAL;
+
+ return 0;
+}
+
+/*
+ * Comparison function called from qsort().
+ * Primary key is ae_tag, secondary key is ae_id.
+ */
+STATIC int
+xfs_acl_entry_compare(
+ const void *va,
+ const void *vb)
+{
+ xfs_acl_entry_t *a = (xfs_acl_entry_t *)va,
+ *b = (xfs_acl_entry_t *)vb;
+
+ if (a->ae_tag == b->ae_tag)
+ return (a->ae_id - b->ae_id);
+ return (a->ae_tag - b->ae_tag);
+}
+
+/*
+ * Convert from in-memory XFS to extended attribute representation.
+ */
+STATIC int
+posix_acl_xfs_to_xattr(
+ xfs_acl_t *src,
+ posix_acl_xattr_header *dest,
+ size_t size)
+{
+ int n;
+ size_t new_size = posix_acl_xattr_size(src->acl_cnt);
+ posix_acl_xattr_entry *dest_entry;
+ xfs_acl_entry_t *src_entry;
+
+ if (size < new_size)
+ return -ERANGE;
+
+ /* Need to sort src XFS ACL by <ae_tag,ae_id> */
+ qsort(src->acl_entry, src->acl_cnt, sizeof(src->acl_entry[0]),
+ xfs_acl_entry_compare);
+
+ dest->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION);
+ dest_entry = &dest->a_entries[0];
+ src_entry = &src->acl_entry[0];
+ for (n = 0; n < src->acl_cnt; n++, dest_entry++, src_entry++) {
+ dest_entry->e_perm = cpu_to_le16(src_entry->ae_perm);
+ if (_ACL_PERM_INVALID(src_entry->ae_perm))
+ return -EINVAL;
+ dest_entry->e_tag = cpu_to_le16(src_entry->ae_tag);
+ switch (src_entry->ae_tag) {
+ case ACL_USER:
+ case ACL_GROUP:
+ dest_entry->e_id = cpu_to_le32(src_entry->ae_id);
+ break;
+ case ACL_USER_OBJ:
+ case ACL_GROUP_OBJ:
+ case ACL_MASK:
+ case ACL_OTHER:
+ dest_entry->e_id = cpu_to_le32(ACL_UNDEFINED_ID);
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ return new_size;
+}
+
+int
+xfs_acl_vget(
+ vnode_t *vp,
+ void *acl,
+ size_t size,
+ int kind)
+{
+ int error;
+ xfs_acl_t *xfs_acl = NULL;
+ posix_acl_xattr_header *ext_acl = acl;
+ int flags = 0;
+
+ VN_HOLD(vp);
+ if(size) {
+ if (!(_ACL_ALLOC(xfs_acl))) {
+ error = ENOMEM;
+ goto out;
+ }
+ memset(xfs_acl, 0, sizeof(xfs_acl_t));
+ } else
+ flags = ATTR_KERNOVAL;
+
+ xfs_acl_get_attr(vp, xfs_acl, kind, flags, &error);
+ if (error)
+ goto out;
+
+ if (!size) {
+ error = -posix_acl_xattr_size(XFS_ACL_MAX_ENTRIES);
+ } else {
+ if (xfs_acl_invalid(xfs_acl)) {
+ error = EINVAL;
+ goto out;
+ }
+ if (kind == _ACL_TYPE_ACCESS) {
+ vattr_t va;
+
+ va.va_mask = XFS_AT_MODE;
+ VOP_GETATTR(vp, &va, 0, sys_cred, error);
+ if (error)
+ goto out;
+ xfs_acl_sync_mode(va.va_mode, xfs_acl);
+ }
+ error = -posix_acl_xfs_to_xattr(xfs_acl, ext_acl, size);
+ }
+out:
+ VN_RELE(vp);
+ if(xfs_acl)
+ _ACL_FREE(xfs_acl);
+ return -error;
+}
+
+int
+xfs_acl_vremove(
+ vnode_t *vp,
+ int kind)
+{
+ int error;
+
+ VN_HOLD(vp);
+ error = xfs_acl_allow_set(vp, kind);
+ if (!error) {
+ VOP_ATTR_REMOVE(vp, kind == _ACL_TYPE_DEFAULT?
+ SGI_ACL_DEFAULT: SGI_ACL_FILE,
+ ATTR_ROOT, sys_cred, error);
+ if (error == ENOATTR)
+ error = 0; /* 'scool */
+ }
+ VN_RELE(vp);
+ return -error;
+}
+
+int
+xfs_acl_vset(
+ vnode_t *vp,
+ void *acl,
+ size_t size,
+ int kind)
+{
+ posix_acl_xattr_header *ext_acl = acl;
+ xfs_acl_t *xfs_acl;
+ int error;
+ int basicperms = 0; /* more than std unix perms? */
+
+ if (!acl)
+ return -EINVAL;
+
+ if (!(_ACL_ALLOC(xfs_acl)))
+ return -ENOMEM;
+
+ error = posix_acl_xattr_to_xfs(ext_acl, size, xfs_acl);
+ if (error) {
+ _ACL_FREE(xfs_acl);
+ return -error;
+ }
+ if (!xfs_acl->acl_cnt) {
+ _ACL_FREE(xfs_acl);
+ return 0;
+ }
+
+ VN_HOLD(vp);
+ error = xfs_acl_allow_set(vp, kind);
+ if (error)
+ goto out;
+
+ /* Incoming ACL exists, set file mode based on its value */
+ if (kind == _ACL_TYPE_ACCESS)
+ xfs_acl_setmode(vp, xfs_acl, &basicperms);
+
+ /*
+ * If we have more than std unix permissions, set up the actual attr.
+ * Otherwise, delete any existing attr. This prevents us from
+ * having actual attrs for permissions that can be stored in the
+ * standard permission bits.
+ */
+ if (!basicperms) {
+ xfs_acl_set_attr(vp, xfs_acl, kind, &error);
+ } else {
+ xfs_acl_vremove(vp, _ACL_TYPE_ACCESS);
+ }
+
+out:
+ VN_RELE(vp);
+ _ACL_FREE(xfs_acl);
+ return -error;
+}
+
+int
+xfs_acl_iaccess(
+ xfs_inode_t *ip,
+ mode_t mode,
+ cred_t *cr)
+{
+ xfs_acl_t *acl;
+ int rval;
+
+ if (!(_ACL_ALLOC(acl)))
+ return -1;
+
+ /* If the file has no ACL return -1. */
+ rval = sizeof(xfs_acl_t);
+ if (xfs_attr_fetch(ip, SGI_ACL_FILE, SGI_ACL_FILE_SIZE,
+ (char *)acl, &rval, ATTR_ROOT | ATTR_KERNACCESS, cr)) {
+ _ACL_FREE(acl);
+ return -1;
+ }
+ xfs_acl_get_endian(acl);
+
+ /* If the file has an empty ACL return -1. */
+ if (acl->acl_cnt == XFS_ACL_NOT_PRESENT) {
+ _ACL_FREE(acl);
+ return -1;
+ }
+
+ /* Synchronize ACL with mode bits */
+ xfs_acl_sync_mode(ip->i_d.di_mode, acl);
+
+ rval = xfs_acl_access(ip->i_d.di_uid, ip->i_d.di_gid, acl, mode, cr);
+ _ACL_FREE(acl);
+ return rval;
+}
+
+STATIC int
+xfs_acl_allow_set(
+ vnode_t *vp,
+ int kind)
+{
+ vattr_t va;
+ int error;
+
+ if (vp->v_inode.i_flags & (S_IMMUTABLE|S_APPEND))
+ return EPERM;
+ if (kind == _ACL_TYPE_DEFAULT && vp->v_type != VDIR)
+ return ENOTDIR;
+ if (vp->v_vfsp->vfs_flag & VFS_RDONLY)
+ return EROFS;
+ va.va_mask = XFS_AT_UID;
+ VOP_GETATTR(vp, &va, 0, NULL, error);
+ if (error)
+ return error;
+ if (va.va_uid != current->fsuid && !capable(CAP_FOWNER))
+ return EPERM;
+ return error;
+}
+
+/*
+ * Look for any effective exec access, to allow CAP_DAC_OVERRIDE for exec.
+ * Ignore checking for exec in USER_OBJ when there is no mask, because
+ * in this "minimal acl" case we don't have any actual acls, and we
+ * won't even be here.
+ */
+STATIC int
+xfs_acl_find_any_exec(
+ xfs_acl_t *fap)
+{
+ int i;
+ int masked_aces = 0;
+ int mask = 0;
+
+ for (i = 0; i < fap->acl_cnt; i++) {
+ if (fap->acl_entry[i].ae_perm & ACL_EXECUTE) {
+ if (fap->acl_entry[i].ae_tag & (ACL_USER_OBJ|ACL_OTHER))
+ return 1;
+
+ if (fap->acl_entry[i].ae_tag == ACL_MASK)
+ mask = fap->acl_entry[i].ae_perm;
+ else
+ masked_aces |= fap->acl_entry[i].ae_perm;
+
+ if ((mask & masked_aces) & ACL_EXECUTE)
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * The access control process to determine the access permission:
+ * if uid == file owner id, use the file owner bits.
+ * if gid == file owner group id, use the file group bits.
+ * scan ACL for a maching user or group, and use matched entry
+ * permission. Use total permissions of all matching group entries,
+ * until all acl entries are exhausted. The final permission produced
+ * by matching acl entry or entries needs to be & with group permission.
+ * if not owner, owning group, or matching entry in ACL, use file
+ * other bits. Don't allow CAP_DAC_OVERRIDE on exec access unless
+ * there is some effective exec access somewhere.
+ */
+STATIC int
+xfs_acl_capability_check(
+ mode_t mode,
+ cred_t *cr,
+ xfs_acl_t *fap)
+{
+ if ((mode & ACL_READ) && !capable_cred(cr, CAP_DAC_READ_SEARCH))
+ return EACCES;
+ if ((mode & ACL_WRITE) && !capable_cred(cr, CAP_DAC_OVERRIDE))
+ return EACCES;
+ if ((mode & ACL_EXECUTE) &&
+ (!capable_cred(cr, CAP_DAC_OVERRIDE) ||
+ !xfs_acl_find_any_exec(fap))) {
+ return EACCES;
+ }
+
+ return 0;
+}
+
+/*
+ * Note: cr is only used here for the capability check if the ACL test fails.
+ * It is not used to find out the credentials uid or groups etc, as was
+ * done in IRIX. It is assumed that the uid and groups for the current
+ * thread are taken from "current" instead of the cr parameter.
+ */
+STATIC int
+xfs_acl_access(
+ uid_t fuid,
+ gid_t fgid,
+ xfs_acl_t *fap,
+ mode_t md,
+ cred_t *cr)
+{
+ xfs_acl_entry_t matched;
+ int i, allows;
+ int maskallows = -1; /* true, but not 1, either */
+ int seen_userobj = 0;
+
+ matched.ae_tag = 0; /* Invalid type */
+ md >>= 6; /* Normalize the bits for comparison */
+
+ for (i = 0; i < fap->acl_cnt; i++) {
+ /*
+ * Break out if we've got a user_obj entry or
+ * a user entry and the mask (and have processed USER_OBJ)
+ */
+ if (matched.ae_tag == ACL_USER_OBJ)
+ break;
+ if (matched.ae_tag == ACL_USER) {
+ if (maskallows != -1 && seen_userobj)
+ break;
+ if (fap->acl_entry[i].ae_tag != ACL_MASK &&
+ fap->acl_entry[i].ae_tag != ACL_USER_OBJ)
+ continue;
+ }
+ /* True if this entry allows the requested access */
+ allows = ((fap->acl_entry[i].ae_perm & md) == md);
+
+ switch (fap->acl_entry[i].ae_tag) {
+ case ACL_USER_OBJ:
+ seen_userobj = 1;
+ if (fuid != current->fsuid)
+ continue;
+ matched.ae_tag = ACL_USER_OBJ;
+ matched.ae_perm = allows;
+ break;
+ case ACL_USER:
+ if (fap->acl_entry[i].ae_id != current->fsuid)
+ continue;
+ matched.ae_tag = ACL_USER;
+ matched.ae_perm = allows;
+ break;
+ case ACL_GROUP_OBJ:
+ if ((matched.ae_tag == ACL_GROUP_OBJ ||
+ matched.ae_tag == ACL_GROUP) && !allows)
+ continue;
+ if (!in_group_p(fgid))
+ continue;
+ matched.ae_tag = ACL_GROUP_OBJ;
+ matched.ae_perm = allows;
+ break;
+ case ACL_GROUP:
+ if ((matched.ae_tag == ACL_GROUP_OBJ ||
+ matched.ae_tag == ACL_GROUP) && !allows)
+ continue;
+ if (!in_group_p(fap->acl_entry[i].ae_id))
+ continue;
+ matched.ae_tag = ACL_GROUP;
+ matched.ae_perm = allows;
+ break;
+ case ACL_MASK:
+ maskallows = allows;
+ break;
+ case ACL_OTHER:
+ if (matched.ae_tag != 0)
+ continue;
+ matched.ae_tag = ACL_OTHER;
+ matched.ae_perm = allows;
+ break;
+ }
+ }
+ /*
+ * First possibility is that no matched entry allows access.
+ * The capability to override DAC may exist, so check for it.
+ */
+ switch (matched.ae_tag) {
+ case ACL_OTHER:
+ case ACL_USER_OBJ:
+ if (matched.ae_perm)
+ return 0;
+ break;
+ case ACL_USER:
+ case ACL_GROUP_OBJ:
+ case ACL_GROUP:
+ if (maskallows && matched.ae_perm)
+ return 0;
+ break;
+ case 0:
+ break;
+ }
+
+ return xfs_acl_capability_check(md, cr, fap);
+}
+
+/*
+ * ACL validity checker.
+ * This acl validation routine checks each ACL entry read in makes sense.
+ */
+STATIC int
+xfs_acl_invalid(
+ xfs_acl_t *aclp)
+{
+ xfs_acl_entry_t *entry, *e;
+ int user = 0, group = 0, other = 0, mask = 0;
+ int mask_required = 0;
+ int i, j;
+
+ if (!aclp)
+ goto acl_invalid;
+
+ if (aclp->acl_cnt > XFS_ACL_MAX_ENTRIES)
+ goto acl_invalid;
+
+ for (i = 0; i < aclp->acl_cnt; i++) {
+ entry = &aclp->acl_entry[i];
+ switch (entry->ae_tag) {
+ case ACL_USER_OBJ:
+ if (user++)
+ goto acl_invalid;
+ break;
+ case ACL_GROUP_OBJ:
+ if (group++)
+ goto acl_invalid;
+ break;
+ case ACL_OTHER:
+ if (other++)
+ goto acl_invalid;
+ break;
+ case ACL_USER:
+ case ACL_GROUP:
+ for (j = i + 1; j < aclp->acl_cnt; j++) {
+ e = &aclp->acl_entry[j];
+ if (e->ae_id == entry->ae_id &&
+ e->ae_tag == entry->ae_tag)
+ goto acl_invalid;
+ }
+ mask_required++;
+ break;
+ case ACL_MASK:
+ if (mask++)
+ goto acl_invalid;
+ break;
+ default:
+ goto acl_invalid;
+ }
+ }
+ if (!user || !group || !other || (mask_required && !mask))
+ goto acl_invalid;
+ else
+ return 0;
+acl_invalid:
+ return EINVAL;
+}
+
+/*
+ * Do ACL endian conversion.
+ */
+STATIC void
+xfs_acl_get_endian(
+ xfs_acl_t *aclp)
+{
+ xfs_acl_entry_t *ace, *end;
+
+ INT_SET(aclp->acl_cnt, ARCH_CONVERT, aclp->acl_cnt);
+ end = &aclp->acl_entry[0]+aclp->acl_cnt;
+ for (ace = &aclp->acl_entry[0]; ace < end; ace++) {
+ INT_SET(ace->ae_tag, ARCH_CONVERT, ace->ae_tag);
+ INT_SET(ace->ae_id, ARCH_CONVERT, ace->ae_id);
+ INT_SET(ace->ae_perm, ARCH_CONVERT, ace->ae_perm);
+ }
+}
+
+/*
+ * Get the ACL from the EA and do endian conversion.
+ */
+STATIC void
+xfs_acl_get_attr(
+ vnode_t *vp,
+ xfs_acl_t *aclp,
+ int kind,
+ int flags,
+ int *error)
+{
+ int len = sizeof(xfs_acl_t);
+
+ ASSERT((flags & ATTR_KERNOVAL) ? (aclp == NULL) : 1);
+ flags |= ATTR_ROOT;
+ VOP_ATTR_GET(vp,
+ kind == _ACL_TYPE_ACCESS ? SGI_ACL_FILE : SGI_ACL_DEFAULT,
+ (char *)aclp, &len, flags, sys_cred, *error);
+ if (*error || (flags & ATTR_KERNOVAL))
+ return;
+ xfs_acl_get_endian(aclp);
+}
+
+/*
+ * Set the EA with the ACL and do endian conversion.
+ */
+STATIC void
+xfs_acl_set_attr(
+ vnode_t *vp,
+ xfs_acl_t *aclp,
+ int kind,
+ int *error)
+{
+ xfs_acl_entry_t *ace, *newace, *end;
+ xfs_acl_t *newacl;
+ int len;
+
+ if (!(_ACL_ALLOC(newacl))) {
+ *error = ENOMEM;
+ return;
+ }
+
+ len = sizeof(xfs_acl_t) -
+ (sizeof(xfs_acl_entry_t) * (XFS_ACL_MAX_ENTRIES - aclp->acl_cnt));
+ end = &aclp->acl_entry[0]+aclp->acl_cnt;
+ for (ace = &aclp->acl_entry[0], newace = &newacl->acl_entry[0];
+ ace < end;
+ ace++, newace++) {
+ INT_SET(newace->ae_tag, ARCH_CONVERT, ace->ae_tag);
+ INT_SET(newace->ae_id, ARCH_CONVERT, ace->ae_id);
+ INT_SET(newace->ae_perm, ARCH_CONVERT, ace->ae_perm);
+ }
+ INT_SET(newacl->acl_cnt, ARCH_CONVERT, aclp->acl_cnt);
+ VOP_ATTR_SET(vp,
+ kind == _ACL_TYPE_ACCESS ? SGI_ACL_FILE: SGI_ACL_DEFAULT,
+ (char *)newacl, len, ATTR_ROOT, sys_cred, *error);
+ _ACL_FREE(newacl);
+}
+
+int
+xfs_acl_vtoacl(
+ vnode_t *vp,
+ xfs_acl_t *access_acl,
+ xfs_acl_t *default_acl)
+{
+ vattr_t va;
+ int error = 0;
+
+ if (access_acl) {
+ /*
+ * Get the Access ACL and the mode. If either cannot
+ * be obtained for some reason, invalidate the access ACL.
+ */
+ xfs_acl_get_attr(vp, access_acl, _ACL_TYPE_ACCESS, 0, &error);
+ if (!error) {
+ /* Got the ACL, need the mode... */
+ va.va_mask = XFS_AT_MODE;
+ VOP_GETATTR(vp, &va, 0, sys_cred, error);
+ }
+
+ if (error)
+ access_acl->acl_cnt = XFS_ACL_NOT_PRESENT;
+ else /* We have a good ACL and the file mode, synchronize. */
+ xfs_acl_sync_mode(va.va_mode, access_acl);
+ }
+
+ if (default_acl) {
+ xfs_acl_get_attr(vp, default_acl, _ACL_TYPE_DEFAULT, 0, &error);
+ if (error)
+ default_acl->acl_cnt = XFS_ACL_NOT_PRESENT;
+ }
+ return error;
+}
+
+/*
+ * This function retrieves the parent directory's acl, processes it
+ * and lets the child inherit the acl(s) that it should.
+ */
+int
+xfs_acl_inherit(
+ vnode_t *vp,
+ vattr_t *vap,
+ xfs_acl_t *pdaclp)
+{
+ xfs_acl_t *cacl;
+ int error = 0;
+ int basicperms = 0;
+
+ /*
+ * If the parent does not have a default ACL, or it's an
+ * invalid ACL, we're done.
+ */
+ if (!vp)
+ return 0;
+ if (!pdaclp || xfs_acl_invalid(pdaclp))
+ return 0;
+
+ /*
+ * Copy the default ACL of the containing directory to
+ * the access ACL of the new file and use the mode that
+ * was passed in to set up the correct initial values for
+ * the u::,g::[m::], and o:: entries. This is what makes
+ * umask() "work" with ACL's.
+ */
+
+ if (!(_ACL_ALLOC(cacl)))
+ return ENOMEM;
+
+ memcpy(cacl, pdaclp, sizeof(xfs_acl_t));
+ xfs_acl_filter_mode(vap->va_mode, cacl);
+ xfs_acl_setmode(vp, cacl, &basicperms);
+
+ /*
+ * Set the Default and Access ACL on the file. The mode is already
+ * set on the file, so we don't need to worry about that.
+ *
+ * If the new file is a directory, its default ACL is a copy of
+ * the containing directory's default ACL.
+ */
+ if (vp->v_type == VDIR)
+ xfs_acl_set_attr(vp, pdaclp, _ACL_TYPE_DEFAULT, &error);
+ if (!error && !basicperms)
+ xfs_acl_set_attr(vp, cacl, _ACL_TYPE_ACCESS, &error);
+ _ACL_FREE(cacl);
+ return error;
+}
+
+/*
+ * Set up the correct mode on the file based on the supplied ACL. This
+ * makes sure that the mode on the file reflects the state of the
+ * u::,g::[m::], and o:: entries in the ACL. Since the mode is where
+ * the ACL is going to get the permissions for these entries, we must
+ * synchronize the mode whenever we set the ACL on a file.
+ */
+STATIC int
+xfs_acl_setmode(
+ vnode_t *vp,
+ xfs_acl_t *acl,
+ int *basicperms)
+{
+ vattr_t va;
+ xfs_acl_entry_t *ap;
+ xfs_acl_entry_t *gap = NULL;
+ int i, error, nomask = 1;
+
+ *basicperms = 1;
+
+ if (acl->acl_cnt == XFS_ACL_NOT_PRESENT)
+ return 0;
+
+ /*
+ * Copy the u::, g::, o::, and m:: bits from the ACL into the
+ * mode. The m:: bits take precedence over the g:: bits.
+ */
+ va.va_mask = XFS_AT_MODE;
+ VOP_GETATTR(vp, &va, 0, sys_cred, error);
+ if (error)
+ return error;
+
+ va.va_mask = XFS_AT_MODE;
+ va.va_mode &= ~(S_IRWXU|S_IRWXG|S_IRWXO);
+ ap = acl->acl_entry;
+ for (i = 0; i < acl->acl_cnt; ++i) {
+ switch (ap->ae_tag) {
+ case ACL_USER_OBJ:
+ va.va_mode |= ap->ae_perm << 6;
+ break;
+ case ACL_GROUP_OBJ:
+ gap = ap;
+ break;
+ case ACL_MASK: /* more than just standard modes */
+ nomask = 0;
+ va.va_mode |= ap->ae_perm << 3;
+ *basicperms = 0;
+ break;
+ case ACL_OTHER:
+ va.va_mode |= ap->ae_perm;
+ break;
+ default: /* more than just standard modes */
+ *basicperms = 0;
+ break;
+ }
+ ap++;
+ }
+
+ /* Set the group bits from ACL_GROUP_OBJ if there's no ACL_MASK */
+ if (gap && nomask)
+ va.va_mode |= gap->ae_perm << 3;
+
+ VOP_SETATTR(vp, &va, 0, sys_cred, error);
+ return error;
+}
+
+/*
+ * The permissions for the special ACL entries (u::, g::[m::], o::) are
+ * actually stored in the file mode (if there is both a group and a mask,
+ * the group is stored in the ACL entry and the mask is stored on the file).
+ * This allows the mode to remain automatically in sync with the ACL without
+ * the need for a call-back to the ACL system at every point where the mode
+ * could change. This function takes the permissions from the specified mode
+ * and places it in the supplied ACL.
+ *
+ * This implementation draws its validity from the fact that, when the ACL
+ * was assigned, the mode was copied from the ACL.
+ * If the mode did not change, therefore, the mode remains exactly what was
+ * taken from the special ACL entries at assignment.
+ * If a subsequent chmod() was done, the POSIX spec says that the change in
+ * mode must cause an update to the ACL seen at user level and used for
+ * access checks. Before and after a mode change, therefore, the file mode
+ * most accurately reflects what the special ACL entries should permit/deny.
+ *
+ * CAVEAT: If someone sets the SGI_ACL_FILE attribute directly,
+ * the existing mode bits will override whatever is in the
+ * ACL. Similarly, if there is a pre-existing ACL that was
+ * never in sync with its mode (owing to a bug in 6.5 and
+ * before), it will now magically (or mystically) be
+ * synchronized. This could cause slight astonishment, but
+ * it is better than inconsistent permissions.
+ *
+ * The supplied ACL is a template that may contain any combination
+ * of special entries. These are treated as place holders when we fill
+ * out the ACL. This routine does not add or remove special entries, it
+ * simply unites each special entry with its associated set of permissions.
+ */
+STATIC void
+xfs_acl_sync_mode(
+ mode_t mode,
+ xfs_acl_t *acl)
+{
+ int i, nomask = 1;
+ xfs_acl_entry_t *ap;
+ xfs_acl_entry_t *gap = NULL;
+
+ /*
+ * Set ACL entries. POSIX1003.1eD16 requires that the MASK
+ * be set instead of the GROUP entry, if there is a MASK.
+ */
+ for (ap = acl->acl_entry, i = 0; i < acl->acl_cnt; ap++, i++) {
+ switch (ap->ae_tag) {
+ case ACL_USER_OBJ:
+ ap->ae_perm = (mode >> 6) & 0x7;
+ break;
+ case ACL_GROUP_OBJ:
+ gap = ap;
+ break;
+ case ACL_MASK:
+ nomask = 0;
+ ap->ae_perm = (mode >> 3) & 0x7;
+ break;
+ case ACL_OTHER:
+ ap->ae_perm = mode & 0x7;
+ break;
+ default:
+ break;
+ }
+ }
+ /* Set the ACL_GROUP_OBJ if there's no ACL_MASK */
+ if (gap && nomask)
+ gap->ae_perm = (mode >> 3) & 0x7;
+}
+
+/*
+ * When inheriting an Access ACL from a directory Default ACL,
+ * the ACL bits are set to the intersection of the ACL default
+ * permission bits and the file permission bits in mode. If there
+ * are no permission bits on the file then we must not give them
+ * the ACL. This is what what makes umask() work with ACLs.
+ */
+STATIC void
+xfs_acl_filter_mode(
+ mode_t mode,
+ xfs_acl_t *acl)
+{
+ int i, nomask = 1;
+ xfs_acl_entry_t *ap;
+ xfs_acl_entry_t *gap = NULL;
+
+ /*
+ * Set ACL entries. POSIX1003.1eD16 requires that the MASK
+ * be merged with GROUP entry, if there is a MASK.
+ */
+ for (ap = acl->acl_entry, i = 0; i < acl->acl_cnt; ap++, i++) {
+ switch (ap->ae_tag) {
+ case ACL_USER_OBJ:
+ ap->ae_perm &= (mode >> 6) & 0x7;
+ break;
+ case ACL_GROUP_OBJ:
+ gap = ap;
+ break;
+ case ACL_MASK:
+ nomask = 0;
+ ap->ae_perm &= (mode >> 3) & 0x7;
+ break;
+ case ACL_OTHER:
+ ap->ae_perm &= mode & 0x7;
+ break;
+ default:
+ break;
+ }
+ }
+ /* Set the ACL_GROUP_OBJ if there's no ACL_MASK */
+ if (gap && nomask)
+ gap->ae_perm &= (mode >> 3) & 0x7;
+}
diff --git a/fs/xfs/xfs_acl.h b/fs/xfs/xfs_acl.h
new file mode 100644
index 00000000000000..0363eb46d35720
--- /dev/null
+++ b/fs/xfs/xfs_acl.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2001-2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_ACL_H__
+#define __XFS_ACL_H__
+
+/*
+ * Access Control Lists
+ */
+typedef __uint16_t xfs_acl_perm_t;
+typedef __int32_t xfs_acl_type_t;
+typedef __int32_t xfs_acl_tag_t;
+typedef __int32_t xfs_acl_id_t;
+
+#define XFS_ACL_MAX_ENTRIES 25
+#define XFS_ACL_NOT_PRESENT (-1)
+
+typedef struct xfs_acl_entry {
+ xfs_acl_tag_t ae_tag;
+ xfs_acl_id_t ae_id;
+ xfs_acl_perm_t ae_perm;
+} xfs_acl_entry_t;
+
+typedef struct xfs_acl {
+ __int32_t acl_cnt;
+ xfs_acl_entry_t acl_entry[XFS_ACL_MAX_ENTRIES];
+} xfs_acl_t;
+
+/* On-disk XFS extended attribute names */
+#define SGI_ACL_FILE "SGI_ACL_FILE"
+#define SGI_ACL_DEFAULT "SGI_ACL_DEFAULT"
+#define SGI_ACL_FILE_SIZE (sizeof(SGI_ACL_FILE)-1)
+#define SGI_ACL_DEFAULT_SIZE (sizeof(SGI_ACL_DEFAULT)-1)
+
+
+#ifdef CONFIG_XFS_POSIX_ACL
+
+struct vattr;
+struct vnode;
+struct xfs_inode;
+
+extern struct kmem_zone *xfs_acl_zone;
+#define xfs_acl_zone_init(zone, name) \
+ (zone) = kmem_zone_init(sizeof(xfs_acl_t), name)
+#define xfs_acl_zone_destroy(zone) kmem_cache_destroy(zone)
+
+extern int xfs_acl_inherit(struct vnode *, struct vattr *, xfs_acl_t *);
+extern int xfs_acl_iaccess(struct xfs_inode *, mode_t, cred_t *);
+extern int xfs_acl_vtoacl(struct vnode *, xfs_acl_t *, xfs_acl_t *);
+extern int xfs_acl_vhasacl_access(struct vnode *);
+extern int xfs_acl_vhasacl_default(struct vnode *);
+extern int xfs_acl_vset(struct vnode *, void *, size_t, int);
+extern int xfs_acl_vget(struct vnode *, void *, size_t, int);
+extern int xfs_acl_vremove(struct vnode *vp, int);
+
+#define _ACL_TYPE_ACCESS 1
+#define _ACL_TYPE_DEFAULT 2
+#define _ACL_PERM_INVALID(perm) ((perm) & ~(ACL_READ|ACL_WRITE|ACL_EXECUTE))
+
+#define _ACL_INHERIT(c,v,d) (xfs_acl_inherit(c,v,d))
+#define _ACL_GET_ACCESS(pv,pa) (xfs_acl_vtoacl(pv,pa,NULL) == 0)
+#define _ACL_GET_DEFAULT(pv,pd) (xfs_acl_vtoacl(pv,NULL,pd) == 0)
+#define _ACL_ACCESS_EXISTS xfs_acl_vhasacl_access
+#define _ACL_DEFAULT_EXISTS xfs_acl_vhasacl_default
+#define _ACL_XFS_IACCESS(i,m,c) (XFS_IFORK_Q(i) ? xfs_acl_iaccess(i,m,c) : -1)
+
+#define _ACL_ALLOC(a) ((a) = kmem_zone_alloc(xfs_acl_zone, KM_SLEEP))
+#define _ACL_FREE(a) ((a)? kmem_zone_free(xfs_acl_zone, (a)):(void)0)
+
+#else
+#define xfs_acl_zone_init(zone,name)
+#define xfs_acl_zone_destroy(zone)
+#define xfs_acl_vset(v,p,sz,t) (-EOPNOTSUPP)
+#define xfs_acl_vget(v,p,sz,t) (-EOPNOTSUPP)
+#define xfs_acl_vremove(v,t) (-EOPNOTSUPP)
+#define xfs_acl_vhasacl_access(v) (0)
+#define xfs_acl_vhasacl_default(v) (0)
+#define _ACL_ALLOC(a) (1) /* successfully allocate nothing */
+#define _ACL_FREE(a) ((void)0)
+#define _ACL_INHERIT(c,v,d) (0)
+#define _ACL_GET_ACCESS(pv,pa) (0)
+#define _ACL_GET_DEFAULT(pv,pd) (0)
+#define _ACL_ACCESS_EXISTS (NULL)
+#define _ACL_DEFAULT_EXISTS (NULL)
+#define _ACL_XFS_IACCESS(i,m,c) (-1)
+#endif
+
+#endif /* __XFS_ACL_H__ */
diff --git a/fs/xfs/xfs_ag.h b/fs/xfs/xfs_ag.h
new file mode 100644
index 00000000000000..96b70f7fba397b
--- /dev/null
+++ b/fs/xfs/xfs_ag.h
@@ -0,0 +1,345 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_AG_H__
+#define __XFS_AG_H__
+
+/*
+ * Allocation group header
+ * This is divided into three structures, placed in sequential 512-byte
+ * buffers after a copy of the superblock (also in a 512-byte buffer).
+ */
+
+struct xfs_buf;
+struct xfs_mount;
+struct xfs_trans;
+
+#define XFS_AGF_MAGIC 0x58414746 /* 'XAGF' */
+#define XFS_AGI_MAGIC 0x58414749 /* 'XAGI' */
+#define XFS_AGF_VERSION 1
+#define XFS_AGI_VERSION 1
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_AGF_GOOD_VERSION)
+int xfs_agf_good_version(unsigned v);
+#define XFS_AGF_GOOD_VERSION(v) xfs_agf_good_version(v)
+#else
+#define XFS_AGF_GOOD_VERSION(v) ((v) == XFS_AGF_VERSION)
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_AGI_GOOD_VERSION)
+int xfs_agi_good_version(unsigned v);
+#define XFS_AGI_GOOD_VERSION(v) xfs_agi_good_version(v)
+#else
+#define XFS_AGI_GOOD_VERSION(v) ((v) == XFS_AGI_VERSION)
+#endif
+
+/*
+ * Btree number 0 is bno, 1 is cnt. This value gives the size of the
+ * arrays below.
+ */
+#define XFS_BTNUM_AGF ((int)XFS_BTNUM_CNTi + 1)
+
+/*
+ * The second word of agf_levels in the first a.g. overlaps the EFS
+ * superblock's magic number. Since the magic numbers valid for EFS
+ * are > 64k, our value cannot be confused for an EFS superblock's.
+ */
+
+typedef struct xfs_agf
+{
+ /*
+ * Common allocation group header information
+ */
+ __uint32_t agf_magicnum; /* magic number == XFS_AGF_MAGIC */
+ __uint32_t agf_versionnum; /* header version == XFS_AGF_VERSION */
+ xfs_agnumber_t agf_seqno; /* sequence # starting from 0 */
+ xfs_agblock_t agf_length; /* size in blocks of a.g. */
+ /*
+ * Freespace information
+ */
+ xfs_agblock_t agf_roots[XFS_BTNUM_AGF]; /* root blocks */
+ __uint32_t agf_spare0; /* spare field */
+ __uint32_t agf_levels[XFS_BTNUM_AGF]; /* btree levels */
+ __uint32_t agf_spare1; /* spare field */
+ __uint32_t agf_flfirst; /* first freelist block's index */
+ __uint32_t agf_fllast; /* last freelist block's index */
+ __uint32_t agf_flcount; /* count of blocks in freelist */
+ xfs_extlen_t agf_freeblks; /* total free blocks */
+ xfs_extlen_t agf_longest; /* longest free space */
+} xfs_agf_t;
+
+#define XFS_AGF_MAGICNUM 0x00000001
+#define XFS_AGF_VERSIONNUM 0x00000002
+#define XFS_AGF_SEQNO 0x00000004
+#define XFS_AGF_LENGTH 0x00000008
+#define XFS_AGF_ROOTS 0x00000010
+#define XFS_AGF_LEVELS 0x00000020
+#define XFS_AGF_FLFIRST 0x00000040
+#define XFS_AGF_FLLAST 0x00000080
+#define XFS_AGF_FLCOUNT 0x00000100
+#define XFS_AGF_FREEBLKS 0x00000200
+#define XFS_AGF_LONGEST 0x00000400
+#define XFS_AGF_NUM_BITS 11
+#define XFS_AGF_ALL_BITS ((1 << XFS_AGF_NUM_BITS) - 1)
+
+/* disk block (xfs_daddr_t) in the AG */
+#define XFS_AGF_DADDR(mp) ((xfs_daddr_t)(1 << (mp)->m_sectbb_log))
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_AGF_BLOCK)
+xfs_agblock_t xfs_agf_block(struct xfs_mount *mp);
+#define XFS_AGF_BLOCK(mp) xfs_agf_block(mp)
+#else
+#define XFS_AGF_BLOCK(mp) XFS_HDR_BLOCK(mp, XFS_AGF_DADDR(mp))
+#endif
+
+/*
+ * Size of the unlinked inode hash table in the agi.
+ */
+#define XFS_AGI_UNLINKED_BUCKETS 64
+
+typedef struct xfs_agi
+{
+ /*
+ * Common allocation group header information
+ */
+ __uint32_t agi_magicnum; /* magic number == XFS_AGI_MAGIC */
+ __uint32_t agi_versionnum; /* header version == XFS_AGI_VERSION */
+ xfs_agnumber_t agi_seqno; /* sequence # starting from 0 */
+ xfs_agblock_t agi_length; /* size in blocks of a.g. */
+ /*
+ * Inode information
+ * Inodes are mapped by interpreting the inode number, so no
+ * mapping data is needed here.
+ */
+ xfs_agino_t agi_count; /* count of allocated inodes */
+ xfs_agblock_t agi_root; /* root of inode btree */
+ __uint32_t agi_level; /* levels in inode btree */
+ xfs_agino_t agi_freecount; /* number of free inodes */
+ xfs_agino_t agi_newino; /* new inode just allocated */
+ xfs_agino_t agi_dirino; /* last directory inode chunk */
+ /*
+ * Hash table of inodes which have been unlinked but are
+ * still being referenced.
+ */
+ xfs_agino_t agi_unlinked[XFS_AGI_UNLINKED_BUCKETS];
+} xfs_agi_t;
+
+#define XFS_AGI_MAGICNUM 0x00000001
+#define XFS_AGI_VERSIONNUM 0x00000002
+#define XFS_AGI_SEQNO 0x00000004
+#define XFS_AGI_LENGTH 0x00000008
+#define XFS_AGI_COUNT 0x00000010
+#define XFS_AGI_ROOT 0x00000020
+#define XFS_AGI_LEVEL 0x00000040
+#define XFS_AGI_FREECOUNT 0x00000080
+#define XFS_AGI_NEWINO 0x00000100
+#define XFS_AGI_DIRINO 0x00000200
+#define XFS_AGI_UNLINKED 0x00000400
+#define XFS_AGI_NUM_BITS 11
+#define XFS_AGI_ALL_BITS ((1 << XFS_AGI_NUM_BITS) - 1)
+
+/* disk block (xfs_daddr_t) in the AG */
+#define XFS_AGI_DADDR(mp) ((xfs_daddr_t)(2 << (mp)->m_sectbb_log))
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_AGI_BLOCK)
+xfs_agblock_t xfs_agi_block(struct xfs_mount *mp);
+#define XFS_AGI_BLOCK(mp) xfs_agi_block(mp)
+#else
+#define XFS_AGI_BLOCK(mp) XFS_HDR_BLOCK(mp, XFS_AGI_DADDR(mp))
+#endif
+
+/*
+ * The third a.g. block contains the a.g. freelist, an array
+ * of block pointers to blocks owned by the allocation btree code.
+ */
+#define XFS_AGFL_DADDR(mp) ((xfs_daddr_t)(3 << (mp)->m_sectbb_log))
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_AGFL_BLOCK)
+xfs_agblock_t xfs_agfl_block(struct xfs_mount *mp);
+#define XFS_AGFL_BLOCK(mp) xfs_agfl_block(mp)
+#else
+#define XFS_AGFL_BLOCK(mp) XFS_HDR_BLOCK(mp, XFS_AGFL_DADDR(mp))
+#endif
+#define XFS_AGFL_SIZE(mp) ((mp)->m_sb.sb_sectsize / sizeof(xfs_agblock_t))
+
+typedef struct xfs_agfl {
+ xfs_agblock_t agfl_bno[1]; /* actually XFS_AGFL_SIZE(mp) */
+} xfs_agfl_t;
+
+/*
+ * Busy block/extent entry. Used in perag to mark blocks that have been freed
+ * but whose transactions aren't committed to disk yet.
+ */
+typedef struct xfs_perag_busy {
+ xfs_agblock_t busy_start;
+ xfs_extlen_t busy_length;
+ struct xfs_trans *busy_tp; /* transaction that did the free */
+} xfs_perag_busy_t;
+
+/*
+ * Per-ag incore structure, copies of information in agf and agi,
+ * to improve the performance of allocation group selection.
+ *
+ * pick sizes which fit in allocation buckets well
+ */
+#if (BITS_PER_LONG == 32)
+#define XFS_PAGB_NUM_SLOTS 84
+#elif (BITS_PER_LONG == 64)
+#define XFS_PAGB_NUM_SLOTS 128
+#endif
+
+typedef struct xfs_perag
+{
+ char pagf_init; /* this agf's entry is initialized */
+ char pagi_init; /* this agi's entry is initialized */
+ char pagf_metadata; /* the agf is prefered to be metadata */
+ char pagi_inodeok; /* The agi is ok for inodes */
+ __uint8_t pagf_levels[XFS_BTNUM_AGF];
+ /* # of levels in bno & cnt btree */
+ __uint32_t pagf_flcount; /* count of blocks in freelist */
+ xfs_extlen_t pagf_freeblks; /* total free blocks */
+ xfs_extlen_t pagf_longest; /* longest free space */
+ xfs_agino_t pagi_freecount; /* number of free inodes */
+#ifdef __KERNEL__
+ lock_t pagb_lock; /* lock for pagb_list */
+#endif
+ int pagb_count; /* pagb slots in use */
+ xfs_perag_busy_t *pagb_list; /* unstable blocks */
+} xfs_perag_t;
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_AG_MAXLEVELS)
+int xfs_ag_maxlevels(struct xfs_mount *mp);
+#define XFS_AG_MAXLEVELS(mp) xfs_ag_maxlevels(mp)
+#else
+#define XFS_AG_MAXLEVELS(mp) ((mp)->m_ag_maxlevels)
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_MIN_FREELIST)
+int xfs_min_freelist(xfs_agf_t *a, struct xfs_mount *mp);
+#define XFS_MIN_FREELIST(a,mp) xfs_min_freelist(a,mp)
+#else
+#define XFS_MIN_FREELIST(a,mp) \
+ XFS_MIN_FREELIST_RAW( \
+ INT_GET((a)->agf_levels[XFS_BTNUM_BNOi], ARCH_CONVERT), \
+ INT_GET((a)->agf_levels[XFS_BTNUM_CNTi], ARCH_CONVERT), mp)
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_MIN_FREELIST_PAG)
+int xfs_min_freelist_pag(xfs_perag_t *pag, struct xfs_mount *mp);
+#define XFS_MIN_FREELIST_PAG(pag,mp) xfs_min_freelist_pag(pag,mp)
+#else
+#define XFS_MIN_FREELIST_PAG(pag,mp) \
+ XFS_MIN_FREELIST_RAW((uint_t)(pag)->pagf_levels[XFS_BTNUM_BNOi], \
+ (uint_t)(pag)->pagf_levels[XFS_BTNUM_CNTi], mp)
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_MIN_FREELIST_RAW)
+int xfs_min_freelist_raw(int bl, int cl, struct xfs_mount *mp);
+#define XFS_MIN_FREELIST_RAW(bl,cl,mp) xfs_min_freelist_raw(bl,cl,mp)
+#else
+#define XFS_MIN_FREELIST_RAW(bl,cl,mp) \
+ (MIN(bl + 1, XFS_AG_MAXLEVELS(mp)) + \
+ MIN(cl + 1, XFS_AG_MAXLEVELS(mp)))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_AGB_TO_FSB)
+xfs_fsblock_t xfs_agb_to_fsb(struct xfs_mount *mp, xfs_agnumber_t agno,
+ xfs_agblock_t agbno);
+#define XFS_AGB_TO_FSB(mp,agno,agbno) xfs_agb_to_fsb(mp,agno,agbno)
+#else
+#define XFS_AGB_TO_FSB(mp,agno,agbno) \
+ (((xfs_fsblock_t)(agno) << (mp)->m_sb.sb_agblklog) | (agbno))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_FSB_TO_AGNO)
+xfs_agnumber_t xfs_fsb_to_agno(struct xfs_mount *mp, xfs_fsblock_t fsbno);
+#define XFS_FSB_TO_AGNO(mp,fsbno) xfs_fsb_to_agno(mp,fsbno)
+#else
+#define XFS_FSB_TO_AGNO(mp,fsbno) \
+ ((xfs_agnumber_t)((fsbno) >> (mp)->m_sb.sb_agblklog))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_FSB_TO_AGBNO)
+xfs_agblock_t xfs_fsb_to_agbno(struct xfs_mount *mp, xfs_fsblock_t fsbno);
+#define XFS_FSB_TO_AGBNO(mp,fsbno) xfs_fsb_to_agbno(mp,fsbno)
+#else
+#define XFS_FSB_TO_AGBNO(mp,fsbno) \
+ ((xfs_agblock_t)((fsbno) & XFS_MASK32LO((mp)->m_sb.sb_agblklog)))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_AGB_TO_DADDR)
+xfs_daddr_t xfs_agb_to_daddr(struct xfs_mount *mp, xfs_agnumber_t agno,
+ xfs_agblock_t agbno);
+#define XFS_AGB_TO_DADDR(mp,agno,agbno) xfs_agb_to_daddr(mp,agno,agbno)
+#else
+#define XFS_AGB_TO_DADDR(mp,agno,agbno) \
+ ((xfs_daddr_t)(XFS_FSB_TO_BB(mp, \
+ (xfs_fsblock_t)(agno) * (mp)->m_sb.sb_agblocks + (agbno))))
+#endif
+/*
+ * XFS_DADDR_TO_AGNO and XFS_DADDR_TO_AGBNO moved to xfs_mount.h
+ * to avoid header file ordering change
+ */
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_AG_DADDR)
+xfs_daddr_t xfs_ag_daddr(struct xfs_mount *mp, xfs_agnumber_t agno,
+ xfs_daddr_t d);
+#define XFS_AG_DADDR(mp,agno,d) xfs_ag_daddr(mp,agno,d)
+#else
+#define XFS_AG_DADDR(mp,agno,d) (XFS_AGB_TO_DADDR(mp, agno, 0) + (d))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BUF_TO_AGF)
+xfs_agf_t *xfs_buf_to_agf(struct xfs_buf *bp);
+#define XFS_BUF_TO_AGF(bp) xfs_buf_to_agf(bp)
+#else
+#define XFS_BUF_TO_AGF(bp) ((xfs_agf_t *)XFS_BUF_PTR(bp))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BUF_TO_AGI)
+xfs_agi_t *xfs_buf_to_agi(struct xfs_buf *bp);
+#define XFS_BUF_TO_AGI(bp) xfs_buf_to_agi(bp)
+#else
+#define XFS_BUF_TO_AGI(bp) ((xfs_agi_t *)XFS_BUF_PTR(bp))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BUF_TO_AGFL)
+xfs_agfl_t *xfs_buf_to_agfl(struct xfs_buf *bp);
+#define XFS_BUF_TO_AGFL(bp) xfs_buf_to_agfl(bp)
+#else
+#define XFS_BUF_TO_AGFL(bp) ((xfs_agfl_t *)XFS_BUF_PTR(bp))
+#endif
+
+/*
+ * For checking for bad ranges of xfs_daddr_t's, covering multiple
+ * allocation groups or a single xfs_daddr_t that's a superblock copy.
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_AG_CHECK_DADDR)
+void xfs_ag_check_daddr(struct xfs_mount *mp, xfs_daddr_t d, xfs_extlen_t len);
+#define XFS_AG_CHECK_DADDR(mp,d,len) xfs_ag_check_daddr(mp,d,len)
+#else
+#define XFS_AG_CHECK_DADDR(mp,d,len) \
+ ((len) == 1 ? \
+ ASSERT((d) == XFS_SB_DADDR || \
+ XFS_DADDR_TO_AGBNO(mp, d) != XFS_SB_DADDR) : \
+ ASSERT(XFS_DADDR_TO_AGNO(mp, d) == \
+ XFS_DADDR_TO_AGNO(mp, (d) + (len) - 1)))
+#endif
+
+#endif /* __XFS_AG_H__ */
diff --git a/fs/xfs/xfs_alloc.c b/fs/xfs/xfs_alloc.c
new file mode 100644
index 00000000000000..fa8799d55fb782
--- /dev/null
+++ b/fs/xfs/xfs_alloc.c
@@ -0,0 +1,2623 @@
+/*
+ * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+/*
+ * Free space allocation for XFS.
+ */
+#include "xfs.h"
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_alloc.h"
+#include "xfs_bit.h"
+#include "xfs_error.h"
+
+
+#define XFS_ABSDIFF(a,b) (((a) <= (b)) ? ((b) - (a)) : ((a) - (b)))
+
+#define XFSA_FIXUP_BNO_OK 1
+#define XFSA_FIXUP_CNT_OK 2
+
+int
+xfs_alloc_search_busy(xfs_trans_t *tp,
+ xfs_agnumber_t agno,
+ xfs_agblock_t bno,
+ xfs_extlen_t len);
+
+#if defined(XFS_ALLOC_TRACE)
+ktrace_t *xfs_alloc_trace_buf;
+
+#define TRACE_ALLOC(s,a) \
+ xfs_alloc_trace_alloc(fname, s, a, __LINE__)
+#define TRACE_FREE(s,a,b,x,f) \
+ xfs_alloc_trace_free(fname, s, mp, a, b, x, f, __LINE__)
+#define TRACE_MODAGF(s,a,f) \
+ xfs_alloc_trace_modagf(fname, s, mp, a, f, __LINE__)
+#define TRACE_BUSY(fname,s,ag,agb,l,sl,tp) \
+ xfs_alloc_trace_busy(fname, s, mp, ag, agb, l, sl, tp, XFS_ALLOC_KTRACE_BUSY, __LINE__)
+#define TRACE_UNBUSY(fname,s,ag,sl,tp) \
+ xfs_alloc_trace_busy(fname, s, mp, ag, -1, -1, sl, tp, XFS_ALLOC_KTRACE_UNBUSY, __LINE__)
+#define TRACE_BUSYSEARCH(fname,s,ag,agb,l,sl,tp) \
+ xfs_alloc_trace_busy(fname, s, mp, ag, agb, l, sl, tp, XFS_ALLOC_KTRACE_BUSYSEARCH, __LINE__)
+#else
+#define TRACE_ALLOC(s,a)
+#define TRACE_FREE(s,a,b,x,f)
+#define TRACE_MODAGF(s,a,f)
+#define TRACE_BUSY(s,a,ag,agb,l,sl,tp)
+#define TRACE_UNBUSY(fname,s,ag,sl,tp)
+#define TRACE_BUSYSEARCH(fname,s,ag,agb,l,sl,tp)
+#endif /* XFS_ALLOC_TRACE */
+
+/*
+ * Prototypes for per-ag allocation routines
+ */
+
+STATIC int xfs_alloc_ag_vextent_exact(xfs_alloc_arg_t *);
+STATIC int xfs_alloc_ag_vextent_near(xfs_alloc_arg_t *);
+STATIC int xfs_alloc_ag_vextent_size(xfs_alloc_arg_t *);
+STATIC int xfs_alloc_ag_vextent_small(xfs_alloc_arg_t *,
+ xfs_btree_cur_t *, xfs_agblock_t *, xfs_extlen_t *, int *);
+
+/*
+ * Internal functions.
+ */
+
+/*
+ * Compute aligned version of the found extent.
+ * Takes alignment and min length into account.
+ */
+STATIC int /* success (>= minlen) */
+xfs_alloc_compute_aligned(
+ xfs_agblock_t foundbno, /* starting block in found extent */
+ xfs_extlen_t foundlen, /* length in found extent */
+ xfs_extlen_t alignment, /* alignment for allocation */
+ xfs_extlen_t minlen, /* minimum length for allocation */
+ xfs_agblock_t *resbno, /* result block number */
+ xfs_extlen_t *reslen) /* result length */
+{
+ xfs_agblock_t bno;
+ xfs_extlen_t diff;
+ xfs_extlen_t len;
+
+ if (alignment > 1 && foundlen >= minlen) {
+ bno = roundup(foundbno, alignment);
+ diff = bno - foundbno;
+ len = diff >= foundlen ? 0 : foundlen - diff;
+ } else {
+ bno = foundbno;
+ len = foundlen;
+ }
+ *resbno = bno;
+ *reslen = len;
+ return len >= minlen;
+}
+
+/*
+ * Compute best start block and diff for "near" allocations.
+ * freelen >= wantlen already checked by caller.
+ */
+STATIC xfs_extlen_t /* difference value (absolute) */
+xfs_alloc_compute_diff(
+ xfs_agblock_t wantbno, /* target starting block */
+ xfs_extlen_t wantlen, /* target length */
+ xfs_extlen_t alignment, /* target alignment */
+ xfs_agblock_t freebno, /* freespace's starting block */
+ xfs_extlen_t freelen, /* freespace's length */
+ xfs_agblock_t *newbnop) /* result: best start block from free */
+{
+ xfs_agblock_t freeend; /* end of freespace extent */
+ xfs_agblock_t newbno1; /* return block number */
+ xfs_agblock_t newbno2; /* other new block number */
+ xfs_extlen_t newlen1=0; /* length with newbno1 */
+ xfs_extlen_t newlen2=0; /* length with newbno2 */
+ xfs_agblock_t wantend; /* end of target extent */
+
+ ASSERT(freelen >= wantlen);
+ freeend = freebno + freelen;
+ wantend = wantbno + wantlen;
+ if (freebno >= wantbno) {
+ if ((newbno1 = roundup(freebno, alignment)) >= freeend)
+ newbno1 = NULLAGBLOCK;
+ } else if (freeend >= wantend && alignment > 1) {
+ newbno1 = roundup(wantbno, alignment);
+ newbno2 = newbno1 - alignment;
+ if (newbno1 >= freeend)
+ newbno1 = NULLAGBLOCK;
+ else
+ newlen1 = XFS_EXTLEN_MIN(wantlen, freeend - newbno1);
+ if (newbno2 < freebno)
+ newbno2 = NULLAGBLOCK;
+ else
+ newlen2 = XFS_EXTLEN_MIN(wantlen, freeend - newbno2);
+ if (newbno1 != NULLAGBLOCK && newbno2 != NULLAGBLOCK) {
+ if (newlen1 < newlen2 ||
+ (newlen1 == newlen2 &&
+ XFS_ABSDIFF(newbno1, wantbno) >
+ XFS_ABSDIFF(newbno2, wantbno)))
+ newbno1 = newbno2;
+ } else if (newbno2 != NULLAGBLOCK)
+ newbno1 = newbno2;
+ } else if (freeend >= wantend) {
+ newbno1 = wantbno;
+ } else if (alignment > 1) {
+ newbno1 = roundup(freeend - wantlen, alignment);
+ if (newbno1 > freeend - wantlen &&
+ newbno1 - alignment >= freebno)
+ newbno1 -= alignment;
+ else if (newbno1 >= freeend)
+ newbno1 = NULLAGBLOCK;
+ } else
+ newbno1 = freeend - wantlen;
+ *newbnop = newbno1;
+ return newbno1 == NULLAGBLOCK ? 0 : XFS_ABSDIFF(newbno1, wantbno);
+}
+
+/*
+ * Fix up the length, based on mod and prod.
+ * len should be k * prod + mod for some k.
+ * If len is too small it is returned unchanged.
+ * If len hits maxlen it is left alone.
+ */
+STATIC void
+xfs_alloc_fix_len(
+ xfs_alloc_arg_t *args) /* allocation argument structure */
+{
+ xfs_extlen_t k;
+ xfs_extlen_t rlen;
+
+ ASSERT(args->mod < args->prod);
+ rlen = args->len;
+ ASSERT(rlen >= args->minlen);
+ ASSERT(rlen <= args->maxlen);
+ if (args->prod <= 1 || rlen < args->mod || rlen == args->maxlen ||
+ (args->mod == 0 && rlen < args->prod))
+ return;
+ k = rlen % args->prod;
+ if (k == args->mod)
+ return;
+ if (k > args->mod) {
+ if ((int)(rlen = rlen - k - args->mod) < (int)args->minlen)
+ return;
+ } else {
+ if ((int)(rlen = rlen - args->prod - (args->mod - k)) <
+ (int)args->minlen)
+ return;
+ }
+ ASSERT(rlen >= args->minlen);
+ ASSERT(rlen <= args->maxlen);
+ args->len = rlen;
+}
+
+/*
+ * Fix up length if there is too little space left in the a.g.
+ * Return 1 if ok, 0 if too little, should give up.
+ */
+STATIC int
+xfs_alloc_fix_minleft(
+ xfs_alloc_arg_t *args) /* allocation argument structure */
+{
+ xfs_agf_t *agf; /* a.g. freelist header */
+ int diff; /* free space difference */
+
+ if (args->minleft == 0)
+ return 1;
+ agf = XFS_BUF_TO_AGF(args->agbp);
+ diff = INT_GET(agf->agf_freeblks, ARCH_CONVERT)
+ + INT_GET(agf->agf_flcount, ARCH_CONVERT)
+ - args->len - args->minleft;
+ if (diff >= 0)
+ return 1;
+ args->len += diff; /* shrink the allocated space */
+ if (args->len >= args->minlen)
+ return 1;
+ args->agbno = NULLAGBLOCK;
+ return 0;
+}
+
+/*
+ * Update the two btrees, logically removing from freespace the extent
+ * starting at rbno, rlen blocks. The extent is contained within the
+ * actual (current) free extent fbno for flen blocks.
+ * Flags are passed in indicating whether the cursors are set to the
+ * relevant records.
+ */
+STATIC int /* error code */
+xfs_alloc_fixup_trees(
+ xfs_btree_cur_t *cnt_cur, /* cursor for by-size btree */
+ xfs_btree_cur_t *bno_cur, /* cursor for by-block btree */
+ xfs_agblock_t fbno, /* starting block of free extent */
+ xfs_extlen_t flen, /* length of free extent */
+ xfs_agblock_t rbno, /* starting block of returned extent */
+ xfs_extlen_t rlen, /* length of returned extent */
+ int flags) /* flags, XFSA_FIXUP_... */
+{
+ int error; /* error code */
+ int i; /* operation results */
+ xfs_agblock_t nfbno1; /* first new free startblock */
+ xfs_agblock_t nfbno2; /* second new free startblock */
+ xfs_extlen_t nflen1=0; /* first new free length */
+ xfs_extlen_t nflen2=0; /* second new free length */
+
+ /*
+ * Look up the record in the by-size tree if necessary.
+ */
+ if (flags & XFSA_FIXUP_CNT_OK) {
+#ifdef DEBUG
+ if ((error = xfs_alloc_get_rec(cnt_cur, &nfbno1, &nflen1, &i)))
+ return error;
+ XFS_WANT_CORRUPTED_RETURN(
+ i == 1 && nfbno1 == fbno && nflen1 == flen);
+#endif
+ } else {
+ if ((error = xfs_alloc_lookup_eq(cnt_cur, fbno, flen, &i)))
+ return error;
+ XFS_WANT_CORRUPTED_RETURN(i == 1);
+ }
+ /*
+ * Look up the record in the by-block tree if necessary.
+ */
+ if (flags & XFSA_FIXUP_BNO_OK) {
+#ifdef DEBUG
+ if ((error = xfs_alloc_get_rec(bno_cur, &nfbno1, &nflen1, &i)))
+ return error;
+ XFS_WANT_CORRUPTED_RETURN(
+ i == 1 && nfbno1 == fbno && nflen1 == flen);
+#endif
+ } else {
+ if ((error = xfs_alloc_lookup_eq(bno_cur, fbno, flen, &i)))
+ return error;
+ XFS_WANT_CORRUPTED_RETURN(i == 1);
+ }
+#ifdef DEBUG
+ {
+ xfs_alloc_block_t *bnoblock;
+ xfs_alloc_block_t *cntblock;
+
+ if (bno_cur->bc_nlevels == 1 &&
+ cnt_cur->bc_nlevels == 1) {
+ bnoblock = XFS_BUF_TO_ALLOC_BLOCK(bno_cur->bc_bufs[0]);
+ cntblock = XFS_BUF_TO_ALLOC_BLOCK(cnt_cur->bc_bufs[0]);
+ XFS_WANT_CORRUPTED_RETURN(
+ INT_GET(bnoblock->bb_numrecs, ARCH_CONVERT) == INT_GET(cntblock->bb_numrecs, ARCH_CONVERT));
+ }
+ }
+#endif
+ /*
+ * Deal with all four cases: the allocated record is contained
+ * within the freespace record, so we can have new freespace
+ * at either (or both) end, or no freespace remaining.
+ */
+ if (rbno == fbno && rlen == flen)
+ nfbno1 = nfbno2 = NULLAGBLOCK;
+ else if (rbno == fbno) {
+ nfbno1 = rbno + rlen;
+ nflen1 = flen - rlen;
+ nfbno2 = NULLAGBLOCK;
+ } else if (rbno + rlen == fbno + flen) {
+ nfbno1 = fbno;
+ nflen1 = flen - rlen;
+ nfbno2 = NULLAGBLOCK;
+ } else {
+ nfbno1 = fbno;
+ nflen1 = rbno - fbno;
+ nfbno2 = rbno + rlen;
+ nflen2 = (fbno + flen) - nfbno2;
+ }
+ /*
+ * Delete the entry from the by-size btree.
+ */
+ if ((error = xfs_alloc_delete(cnt_cur, &i)))
+ return error;
+ XFS_WANT_CORRUPTED_RETURN(i == 1);
+ /*
+ * Add new by-size btree entry(s).
+ */
+ if (nfbno1 != NULLAGBLOCK) {
+ if ((error = xfs_alloc_lookup_eq(cnt_cur, nfbno1, nflen1, &i)))
+ return error;
+ XFS_WANT_CORRUPTED_RETURN(i == 0);
+ if ((error = xfs_alloc_insert(cnt_cur, &i)))
+ return error;
+ XFS_WANT_CORRUPTED_RETURN(i == 1);
+ }
+ if (nfbno2 != NULLAGBLOCK) {
+ if ((error = xfs_alloc_lookup_eq(cnt_cur, nfbno2, nflen2, &i)))
+ return error;
+ XFS_WANT_CORRUPTED_RETURN(i == 0);
+ if ((error = xfs_alloc_insert(cnt_cur, &i)))
+ return error;
+ XFS_WANT_CORRUPTED_RETURN(i == 1);
+ }
+ /*
+ * Fix up the by-block btree entry(s).
+ */
+ if (nfbno1 == NULLAGBLOCK) {
+ /*
+ * No remaining freespace, just delete the by-block tree entry.
+ */
+ if ((error = xfs_alloc_delete(bno_cur, &i)))
+ return error;
+ XFS_WANT_CORRUPTED_RETURN(i == 1);
+ } else {
+ /*
+ * Update the by-block entry to start later|be shorter.
+ */
+ if ((error = xfs_alloc_update(bno_cur, nfbno1, nflen1)))
+ return error;
+ }
+ if (nfbno2 != NULLAGBLOCK) {
+ /*
+ * 2 resulting free entries, need to add one.
+ */
+ if ((error = xfs_alloc_lookup_eq(bno_cur, nfbno2, nflen2, &i)))
+ return error;
+ XFS_WANT_CORRUPTED_RETURN(i == 0);
+ if ((error = xfs_alloc_insert(bno_cur, &i)))
+ return error;
+ XFS_WANT_CORRUPTED_RETURN(i == 1);
+ }
+ return 0;
+}
+
+/*
+ * Read in the allocation group free block array.
+ */
+STATIC int /* error */
+xfs_alloc_read_agfl(
+ xfs_mount_t *mp, /* mount point structure */
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_agnumber_t agno, /* allocation group number */
+ xfs_buf_t **bpp) /* buffer for the ag free block array */
+{
+ xfs_buf_t *bp; /* return value */
+ int error;
+
+ ASSERT(agno != NULLAGNUMBER);
+ error = xfs_trans_read_buf(
+ mp, tp, mp->m_ddev_targp,
+ XFS_AG_DADDR(mp, agno, XFS_AGFL_DADDR(mp)),
+ XFS_FSS_TO_BB(mp, 1), 0, &bp);
+ if (error)
+ return error;
+ ASSERT(bp);
+ ASSERT(!XFS_BUF_GETERROR(bp));
+ XFS_BUF_SET_VTYPE_REF(bp, B_FS_AGFL, XFS_AGFL_REF);
+ *bpp = bp;
+ return 0;
+}
+
+#if defined(XFS_ALLOC_TRACE)
+/*
+ * Add an allocation trace entry for an alloc call.
+ */
+STATIC void
+xfs_alloc_trace_alloc(
+ char *name, /* function tag string */
+ char *str, /* additional string */
+ xfs_alloc_arg_t *args, /* allocation argument structure */
+ int line) /* source line number */
+{
+ ktrace_enter(xfs_alloc_trace_buf,
+ (void *)(__psint_t)(XFS_ALLOC_KTRACE_ALLOC | (line << 16)),
+ (void *)name,
+ (void *)str,
+ (void *)args->mp,
+ (void *)(__psunsigned_t)args->agno,
+ (void *)(__psunsigned_t)args->agbno,
+ (void *)(__psunsigned_t)args->minlen,
+ (void *)(__psunsigned_t)args->maxlen,
+ (void *)(__psunsigned_t)args->mod,
+ (void *)(__psunsigned_t)args->prod,
+ (void *)(__psunsigned_t)args->minleft,
+ (void *)(__psunsigned_t)args->total,
+ (void *)(__psunsigned_t)args->alignment,
+ (void *)(__psunsigned_t)args->len,
+ (void *)((((__psint_t)args->type) << 16) |
+ (__psint_t)args->otype),
+ (void *)(__psint_t)((args->wasdel << 3) |
+ (args->wasfromfl << 2) |
+ (args->isfl << 1) |
+ (args->userdata << 0)));
+}
+
+/*
+ * Add an allocation trace entry for a free call.
+ */
+STATIC void
+xfs_alloc_trace_free(
+ char *name, /* function tag string */
+ char *str, /* additional string */
+ xfs_mount_t *mp, /* file system mount point */
+ xfs_agnumber_t agno, /* allocation group number */
+ xfs_agblock_t agbno, /* a.g. relative block number */
+ xfs_extlen_t len, /* length of extent */
+ int isfl, /* set if is freelist allocation/free */
+ int line) /* source line number */
+{
+ ktrace_enter(xfs_alloc_trace_buf,
+ (void *)(__psint_t)(XFS_ALLOC_KTRACE_FREE | (line << 16)),
+ (void *)name,
+ (void *)str,
+ (void *)mp,
+ (void *)(__psunsigned_t)agno,
+ (void *)(__psunsigned_t)agbno,
+ (void *)(__psunsigned_t)len,
+ (void *)(__psint_t)isfl,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+}
+
+/*
+ * Add an allocation trace entry for modifying an agf.
+ */
+STATIC void
+xfs_alloc_trace_modagf(
+ char *name, /* function tag string */
+ char *str, /* additional string */
+ xfs_mount_t *mp, /* file system mount point */
+ xfs_agf_t *agf, /* new agf value */
+ int flags, /* logging flags for agf */
+ int line) /* source line number */
+{
+ ktrace_enter(xfs_alloc_trace_buf,
+ (void *)(__psint_t)(XFS_ALLOC_KTRACE_MODAGF | (line << 16)),
+ (void *)name,
+ (void *)str,
+ (void *)mp,
+ (void *)(__psint_t)flags,
+ (void *)(__psunsigned_t)INT_GET(agf->agf_seqno, ARCH_CONVERT),
+ (void *)(__psunsigned_t)INT_GET(agf->agf_length, ARCH_CONVERT),
+ (void *)(__psunsigned_t)INT_GET(agf->agf_roots[XFS_BTNUM_BNO],
+ ARCH_CONVERT),
+ (void *)(__psunsigned_t)INT_GET(agf->agf_roots[XFS_BTNUM_CNT],
+ ARCH_CONVERT),
+ (void *)(__psunsigned_t)INT_GET(agf->agf_levels[XFS_BTNUM_BNO],
+ ARCH_CONVERT),
+ (void *)(__psunsigned_t)INT_GET(agf->agf_levels[XFS_BTNUM_CNT],
+ ARCH_CONVERT),
+ (void *)(__psunsigned_t)INT_GET(agf->agf_flfirst, ARCH_CONVERT),
+ (void *)(__psunsigned_t)INT_GET(agf->agf_fllast, ARCH_CONVERT),
+ (void *)(__psunsigned_t)INT_GET(agf->agf_flcount, ARCH_CONVERT),
+ (void *)(__psunsigned_t)INT_GET(agf->agf_freeblks, ARCH_CONVERT),
+ (void *)(__psunsigned_t)INT_GET(agf->agf_longest, ARCH_CONVERT));
+}
+
+STATIC void
+xfs_alloc_trace_busy(
+ char *name, /* function tag string */
+ char *str, /* additional string */
+ xfs_mount_t *mp, /* file system mount poing */
+ xfs_agnumber_t agno, /* allocation group number */
+ xfs_agblock_t agbno, /* a.g. relative block number */
+ xfs_extlen_t len, /* length of extent */
+ int slot, /* perag Busy slot */
+ xfs_trans_t *tp,
+ int trtype, /* type: add, delete, search */
+ int line) /* source line number */
+{
+ ktrace_enter(xfs_alloc_trace_buf,
+ (void *)(__psint_t)(trtype | (line << 16)),
+ (void *)name,
+ (void *)str,
+ (void *)mp,
+ (void *)(__psunsigned_t)agno,
+ (void *)(__psunsigned_t)agbno,
+ (void *)(__psunsigned_t)len,
+ (void *)(__psint_t)slot,
+ (void *)tp,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+}
+#endif /* XFS_ALLOC_TRACE */
+
+/*
+ * Allocation group level functions.
+ */
+
+/*
+ * Allocate a variable extent in the allocation group agno.
+ * Type and bno are used to determine where in the allocation group the
+ * extent will start.
+ * Extent's length (returned in *len) will be between minlen and maxlen,
+ * and of the form k * prod + mod unless there's nothing that large.
+ * Return the starting a.g. block, or NULLAGBLOCK if we can't do it.
+ */
+STATIC int /* error */
+xfs_alloc_ag_vextent(
+ xfs_alloc_arg_t *args) /* argument structure for allocation */
+{
+ int error=0;
+#ifdef XFS_ALLOC_TRACE
+ static char fname[] = "xfs_alloc_ag_vextent";
+#endif
+
+ ASSERT(args->minlen > 0);
+ ASSERT(args->maxlen > 0);
+ ASSERT(args->minlen <= args->maxlen);
+ ASSERT(args->mod < args->prod);
+ ASSERT(args->alignment > 0);
+ /*
+ * Branch to correct routine based on the type.
+ */
+ args->wasfromfl = 0;
+ switch (args->type) {
+ case XFS_ALLOCTYPE_THIS_AG:
+ error = xfs_alloc_ag_vextent_size(args);
+ break;
+ case XFS_ALLOCTYPE_NEAR_BNO:
+ error = xfs_alloc_ag_vextent_near(args);
+ break;
+ case XFS_ALLOCTYPE_THIS_BNO:
+ error = xfs_alloc_ag_vextent_exact(args);
+ break;
+ default:
+ ASSERT(0);
+ /* NOTREACHED */
+ }
+ if (error)
+ return error;
+ /*
+ * If the allocation worked, need to change the agf structure
+ * (and log it), and the superblock.
+ */
+ if (args->agbno != NULLAGBLOCK) {
+ xfs_agf_t *agf; /* allocation group freelist header */
+#ifdef XFS_ALLOC_TRACE
+ xfs_mount_t *mp = args->mp;
+#endif
+ long slen = (long)args->len;
+
+ ASSERT(args->len >= args->minlen && args->len <= args->maxlen);
+ ASSERT(!(args->wasfromfl) || !args->isfl);
+ ASSERT(args->agbno % args->alignment == 0);
+ if (!(args->wasfromfl)) {
+
+ agf = XFS_BUF_TO_AGF(args->agbp);
+ INT_MOD(agf->agf_freeblks, ARCH_CONVERT, -(args->len));
+ xfs_trans_agblocks_delta(args->tp,
+ -((long)(args->len)));
+ args->pag->pagf_freeblks -= args->len;
+ ASSERT(INT_GET(agf->agf_freeblks, ARCH_CONVERT)
+ <= INT_GET(agf->agf_length, ARCH_CONVERT));
+ TRACE_MODAGF(NULL, agf, XFS_AGF_FREEBLKS);
+ xfs_alloc_log_agf(args->tp, args->agbp,
+ XFS_AGF_FREEBLKS);
+ /* search the busylist for these blocks */
+ xfs_alloc_search_busy(args->tp, args->agno,
+ args->agbno, args->len);
+ }
+ if (!args->isfl)
+ xfs_trans_mod_sb(args->tp,
+ args->wasdel ? XFS_TRANS_SB_RES_FDBLOCKS :
+ XFS_TRANS_SB_FDBLOCKS, -slen);
+ XFS_STATS_INC(xs_allocx);
+ XFS_STATS_ADD(xs_allocb, args->len);
+ }
+ return 0;
+}
+
+/*
+ * Allocate a variable extent at exactly agno/bno.
+ * Extent's length (returned in *len) will be between minlen and maxlen,
+ * and of the form k * prod + mod unless there's nothing that large.
+ * Return the starting a.g. block (bno), or NULLAGBLOCK if we can't do it.
+ */
+STATIC int /* error */
+xfs_alloc_ag_vextent_exact(
+ xfs_alloc_arg_t *args) /* allocation argument structure */
+{
+ xfs_btree_cur_t *bno_cur;/* by block-number btree cursor */
+ xfs_btree_cur_t *cnt_cur;/* by count btree cursor */
+ xfs_agblock_t end; /* end of allocated extent */
+ int error;
+ xfs_agblock_t fbno; /* start block of found extent */
+ xfs_agblock_t fend; /* end block of found extent */
+ xfs_extlen_t flen; /* length of found extent */
+#ifdef XFS_ALLOC_TRACE
+ static char fname[] = "xfs_alloc_ag_vextent_exact";
+#endif
+ int i; /* success/failure of operation */
+ xfs_agblock_t maxend; /* end of maximal extent */
+ xfs_agblock_t minend; /* end of minimal extent */
+ xfs_extlen_t rlen; /* length of returned extent */
+
+ ASSERT(args->alignment == 1);
+ /*
+ * Allocate/initialize a cursor for the by-number freespace btree.
+ */
+ bno_cur = xfs_btree_init_cursor(args->mp, args->tp, args->agbp,
+ args->agno, XFS_BTNUM_BNO, NULL, 0);
+ /*
+ * Lookup bno and minlen in the btree (minlen is irrelevant, really).
+ * Look for the closest free block <= bno, it must contain bno
+ * if any free block does.
+ */
+ if ((error = xfs_alloc_lookup_le(bno_cur, args->agbno, args->minlen, &i)))
+ goto error0;
+ if (!i) {
+ /*
+ * Didn't find it, return null.
+ */
+ xfs_btree_del_cursor(bno_cur, XFS_BTREE_NOERROR);
+ args->agbno = NULLAGBLOCK;
+ return 0;
+ }
+ /*
+ * Grab the freespace record.
+ */
+ if ((error = xfs_alloc_get_rec(bno_cur, &fbno, &flen, &i)))
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ ASSERT(fbno <= args->agbno);
+ minend = args->agbno + args->minlen;
+ maxend = args->agbno + args->maxlen;
+ fend = fbno + flen;
+ /*
+ * Give up if the freespace isn't long enough for the minimum request.
+ */
+ if (fend < minend) {
+ xfs_btree_del_cursor(bno_cur, XFS_BTREE_NOERROR);
+ args->agbno = NULLAGBLOCK;
+ return 0;
+ }
+ /*
+ * End of extent will be smaller of the freespace end and the
+ * maximal requested end.
+ */
+ end = XFS_AGBLOCK_MIN(fend, maxend);
+ /*
+ * Fix the length according to mod and prod if given.
+ */
+ args->len = end - args->agbno;
+ xfs_alloc_fix_len(args);
+ if (!xfs_alloc_fix_minleft(args)) {
+ xfs_btree_del_cursor(bno_cur, XFS_BTREE_NOERROR);
+ return 0;
+ }
+ rlen = args->len;
+ ASSERT(args->agbno + rlen <= fend);
+ end = args->agbno + rlen;
+ /*
+ * We are allocating agbno for rlen [agbno .. end]
+ * Allocate/initialize a cursor for the by-size btree.
+ */
+ cnt_cur = xfs_btree_init_cursor(args->mp, args->tp, args->agbp,
+ args->agno, XFS_BTNUM_CNT, NULL, 0);
+ ASSERT(args->agbno + args->len <=
+ INT_GET(XFS_BUF_TO_AGF(args->agbp)->agf_length,
+ ARCH_CONVERT));
+ if ((error = xfs_alloc_fixup_trees(cnt_cur, bno_cur, fbno, flen,
+ args->agbno, args->len, XFSA_FIXUP_BNO_OK))) {
+ xfs_btree_del_cursor(cnt_cur, XFS_BTREE_ERROR);
+ goto error0;
+ }
+ xfs_btree_del_cursor(bno_cur, XFS_BTREE_NOERROR);
+ xfs_btree_del_cursor(cnt_cur, XFS_BTREE_NOERROR);
+ TRACE_ALLOC("normal", args);
+ args->wasfromfl = 0;
+ return 0;
+
+error0:
+ xfs_btree_del_cursor(bno_cur, XFS_BTREE_ERROR);
+ TRACE_ALLOC("error", args);
+ return error;
+}
+
+/*
+ * Allocate a variable extent near bno in the allocation group agno.
+ * Extent's length (returned in len) will be between minlen and maxlen,
+ * and of the form k * prod + mod unless there's nothing that large.
+ * Return the starting a.g. block, or NULLAGBLOCK if we can't do it.
+ */
+STATIC int /* error */
+xfs_alloc_ag_vextent_near(
+ xfs_alloc_arg_t *args) /* allocation argument structure */
+{
+ xfs_btree_cur_t *bno_cur_gt; /* cursor for bno btree, right side */
+ xfs_btree_cur_t *bno_cur_lt; /* cursor for bno btree, left side */
+ xfs_btree_cur_t *cnt_cur; /* cursor for count btree */
+#ifdef XFS_ALLOC_TRACE
+ static char fname[] = "xfs_alloc_ag_vextent_near";
+#endif
+ xfs_agblock_t gtbno; /* start bno of right side entry */
+ xfs_agblock_t gtbnoa; /* aligned ... */
+ xfs_extlen_t gtdiff; /* difference to right side entry */
+ xfs_extlen_t gtlen; /* length of right side entry */
+ xfs_extlen_t gtlena; /* aligned ... */
+ xfs_agblock_t gtnew; /* useful start bno of right side */
+ int error; /* error code */
+ int i; /* result code, temporary */
+ int j; /* result code, temporary */
+ xfs_agblock_t ltbno; /* start bno of left side entry */
+ xfs_agblock_t ltbnoa; /* aligned ... */
+ xfs_extlen_t ltdiff; /* difference to left side entry */
+ /*REFERENCED*/
+ xfs_agblock_t ltend; /* end bno of left side entry */
+ xfs_extlen_t ltlen; /* length of left side entry */
+ xfs_extlen_t ltlena; /* aligned ... */
+ xfs_agblock_t ltnew; /* useful start bno of left side */
+ xfs_extlen_t rlen; /* length of returned extent */
+#if defined(DEBUG) && defined(__KERNEL__)
+ /*
+ * Randomly don't execute the first algorithm.
+ */
+ int dofirst; /* set to do first algorithm */
+
+ dofirst = random() & 1;
+#endif
+ /*
+ * Get a cursor for the by-size btree.
+ */
+ cnt_cur = xfs_btree_init_cursor(args->mp, args->tp, args->agbp,
+ args->agno, XFS_BTNUM_CNT, NULL, 0);
+ ltlen = 0;
+ bno_cur_lt = bno_cur_gt = NULL;
+ /*
+ * See if there are any free extents as big as maxlen.
+ */
+ if ((error = xfs_alloc_lookup_ge(cnt_cur, 0, args->maxlen, &i)))
+ goto error0;
+ /*
+ * If none, then pick up the last entry in the tree unless the
+ * tree is empty.
+ */
+ if (!i) {
+ if ((error = xfs_alloc_ag_vextent_small(args, cnt_cur, &ltbno,
+ &ltlen, &i)))
+ goto error0;
+ if (i == 0 || ltlen == 0) {
+ xfs_btree_del_cursor(cnt_cur, XFS_BTREE_NOERROR);
+ return 0;
+ }
+ ASSERT(i == 1);
+ }
+ args->wasfromfl = 0;
+ /*
+ * First algorithm.
+ * If the requested extent is large wrt the freespaces available
+ * in this a.g., then the cursor will be pointing to a btree entry
+ * near the right edge of the tree. If it's in the last btree leaf
+ * block, then we just examine all the entries in that block
+ * that are big enough, and pick the best one.
+ * This is written as a while loop so we can break out of it,
+ * but we never loop back to the top.
+ */
+ while (xfs_btree_islastblock(cnt_cur, 0)) {
+ xfs_extlen_t bdiff;
+ int besti=0;
+ xfs_extlen_t blen=0;
+ xfs_agblock_t bnew=0;
+
+#if defined(DEBUG) && defined(__KERNEL__)
+ if (!dofirst)
+ break;
+#endif
+ /*
+ * Start from the entry that lookup found, sequence through
+ * all larger free blocks. If we're actually pointing at a
+ * record smaller than maxlen, go to the start of this block,
+ * and skip all those smaller than minlen.
+ */
+ if (ltlen || args->alignment > 1) {
+ cnt_cur->bc_ptrs[0] = 1;
+ do {
+ if ((error = xfs_alloc_get_rec(cnt_cur, &ltbno,
+ &ltlen, &i)))
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ if (ltlen >= args->minlen)
+ break;
+ if ((error = xfs_alloc_increment(cnt_cur, 0, &i)))
+ goto error0;
+ } while (i);
+ ASSERT(ltlen >= args->minlen);
+ if (!i)
+ break;
+ }
+ i = cnt_cur->bc_ptrs[0];
+ for (j = 1, blen = 0, bdiff = 0;
+ !error && j && (blen < args->maxlen || bdiff > 0);
+ error = xfs_alloc_increment(cnt_cur, 0, &j)) {
+ /*
+ * For each entry, decide if it's better than
+ * the previous best entry.
+ */
+ if ((error = xfs_alloc_get_rec(cnt_cur, &ltbno, &ltlen, &i)))
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ if (!xfs_alloc_compute_aligned(ltbno, ltlen,
+ args->alignment, args->minlen,
+ &ltbnoa, &ltlena))
+ continue;
+ args->len = XFS_EXTLEN_MIN(ltlena, args->maxlen);
+ xfs_alloc_fix_len(args);
+ ASSERT(args->len >= args->minlen);
+ if (args->len < blen)
+ continue;
+ ltdiff = xfs_alloc_compute_diff(args->agbno, args->len,
+ args->alignment, ltbno, ltlen, &ltnew);
+ if (ltnew != NULLAGBLOCK &&
+ (args->len > blen || ltdiff < bdiff)) {
+ bdiff = ltdiff;
+ bnew = ltnew;
+ blen = args->len;
+ besti = cnt_cur->bc_ptrs[0];
+ }
+ }
+ /*
+ * It didn't work. We COULD be in a case where
+ * there's a good record somewhere, so try again.
+ */
+ if (blen == 0)
+ break;
+ /*
+ * Point at the best entry, and retrieve it again.
+ */
+ cnt_cur->bc_ptrs[0] = besti;
+ if ((error = xfs_alloc_get_rec(cnt_cur, &ltbno, &ltlen, &i)))
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ ltend = ltbno + ltlen;
+ ASSERT(ltend <= INT_GET(XFS_BUF_TO_AGF(args->agbp)->agf_length,
+ ARCH_CONVERT));
+ args->len = blen;
+ if (!xfs_alloc_fix_minleft(args)) {
+ xfs_btree_del_cursor(cnt_cur, XFS_BTREE_NOERROR);
+ TRACE_ALLOC("nominleft", args);
+ return 0;
+ }
+ blen = args->len;
+ /*
+ * We are allocating starting at bnew for blen blocks.
+ */
+ args->agbno = bnew;
+ ASSERT(bnew >= ltbno);
+ ASSERT(bnew + blen <= ltend);
+ /*
+ * Set up a cursor for the by-bno tree.
+ */
+ bno_cur_lt = xfs_btree_init_cursor(args->mp, args->tp,
+ args->agbp, args->agno, XFS_BTNUM_BNO, NULL, 0);
+ /*
+ * Fix up the btree entries.
+ */
+ if ((error = xfs_alloc_fixup_trees(cnt_cur, bno_cur_lt, ltbno,
+ ltlen, bnew, blen, XFSA_FIXUP_CNT_OK)))
+ goto error0;
+ xfs_btree_del_cursor(cnt_cur, XFS_BTREE_NOERROR);
+ xfs_btree_del_cursor(bno_cur_lt, XFS_BTREE_NOERROR);
+ TRACE_ALLOC("first", args);
+ return 0;
+ }
+ /*
+ * Second algorithm.
+ * Search in the by-bno tree to the left and to the right
+ * simultaneously, until in each case we find a space big enough,
+ * or run into the edge of the tree. When we run into the edge,
+ * we deallocate that cursor.
+ * If both searches succeed, we compare the two spaces and pick
+ * the better one.
+ * With alignment, it's possible for both to fail; the upper
+ * level algorithm that picks allocation groups for allocations
+ * is not supposed to do this.
+ */
+ /*
+ * Allocate and initialize the cursor for the leftward search.
+ */
+ bno_cur_lt = xfs_btree_init_cursor(args->mp, args->tp, args->agbp,
+ args->agno, XFS_BTNUM_BNO, NULL, 0);
+ /*
+ * Lookup <= bno to find the leftward search's starting point.
+ */
+ if ((error = xfs_alloc_lookup_le(bno_cur_lt, args->agbno, args->maxlen, &i)))
+ goto error0;
+ if (!i) {
+ /*
+ * Didn't find anything; use this cursor for the rightward
+ * search.
+ */
+ bno_cur_gt = bno_cur_lt;
+ bno_cur_lt = NULL;
+ }
+ /*
+ * Found something. Duplicate the cursor for the rightward search.
+ */
+ else if ((error = xfs_btree_dup_cursor(bno_cur_lt, &bno_cur_gt)))
+ goto error0;
+ /*
+ * Increment the cursor, so we will point at the entry just right
+ * of the leftward entry if any, or to the leftmost entry.
+ */
+ if ((error = xfs_alloc_increment(bno_cur_gt, 0, &i)))
+ goto error0;
+ if (!i) {
+ /*
+ * It failed, there are no rightward entries.
+ */
+ xfs_btree_del_cursor(bno_cur_gt, XFS_BTREE_NOERROR);
+ bno_cur_gt = NULL;
+ }
+ /*
+ * Loop going left with the leftward cursor, right with the
+ * rightward cursor, until either both directions give up or
+ * we find an entry at least as big as minlen.
+ */
+ do {
+ if (bno_cur_lt) {
+ if ((error = xfs_alloc_get_rec(bno_cur_lt, &ltbno, &ltlen, &i)))
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ if (xfs_alloc_compute_aligned(ltbno, ltlen,
+ args->alignment, args->minlen,
+ &ltbnoa, &ltlena))
+ break;
+ if ((error = xfs_alloc_decrement(bno_cur_lt, 0, &i)))
+ goto error0;
+ if (!i) {
+ xfs_btree_del_cursor(bno_cur_lt,
+ XFS_BTREE_NOERROR);
+ bno_cur_lt = NULL;
+ }
+ }
+ if (bno_cur_gt) {
+ if ((error = xfs_alloc_get_rec(bno_cur_gt, &gtbno, &gtlen, &i)))
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ if (xfs_alloc_compute_aligned(gtbno, gtlen,
+ args->alignment, args->minlen,
+ &gtbnoa, &gtlena))
+ break;
+ if ((error = xfs_alloc_increment(bno_cur_gt, 0, &i)))
+ goto error0;
+ if (!i) {
+ xfs_btree_del_cursor(bno_cur_gt,
+ XFS_BTREE_NOERROR);
+ bno_cur_gt = NULL;
+ }
+ }
+ } while (bno_cur_lt || bno_cur_gt);
+ /*
+ * Got both cursors still active, need to find better entry.
+ */
+ if (bno_cur_lt && bno_cur_gt) {
+ /*
+ * Left side is long enough, look for a right side entry.
+ */
+ if (ltlena >= args->minlen) {
+ /*
+ * Fix up the length.
+ */
+ args->len = XFS_EXTLEN_MIN(ltlena, args->maxlen);
+ xfs_alloc_fix_len(args);
+ rlen = args->len;
+ ltdiff = xfs_alloc_compute_diff(args->agbno, rlen,
+ args->alignment, ltbno, ltlen, &ltnew);
+ /*
+ * Not perfect.
+ */
+ if (ltdiff) {
+ /*
+ * Look until we find a better one, run out of
+ * space, or run off the end.
+ */
+ while (bno_cur_lt && bno_cur_gt) {
+ if ((error = xfs_alloc_get_rec(
+ bno_cur_gt, &gtbno,
+ &gtlen, &i)))
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ xfs_alloc_compute_aligned(gtbno, gtlen,
+ args->alignment, args->minlen,
+ &gtbnoa, &gtlena);
+ /*
+ * The left one is clearly better.
+ */
+ if (gtbnoa >= args->agbno + ltdiff) {
+ xfs_btree_del_cursor(
+ bno_cur_gt,
+ XFS_BTREE_NOERROR);
+ bno_cur_gt = NULL;
+ break;
+ }
+ /*
+ * If we reach a big enough entry,
+ * compare the two and pick the best.
+ */
+ if (gtlena >= args->minlen) {
+ args->len =
+ XFS_EXTLEN_MIN(gtlena,
+ args->maxlen);
+ xfs_alloc_fix_len(args);
+ rlen = args->len;
+ gtdiff = xfs_alloc_compute_diff(
+ args->agbno, rlen,
+ args->alignment,
+ gtbno, gtlen, &gtnew);
+ /*
+ * Right side is better.
+ */
+ if (gtdiff < ltdiff) {
+ xfs_btree_del_cursor(
+ bno_cur_lt,
+ XFS_BTREE_NOERROR);
+ bno_cur_lt = NULL;
+ }
+ /*
+ * Left side is better.
+ */
+ else {
+ xfs_btree_del_cursor(
+ bno_cur_gt,
+ XFS_BTREE_NOERROR);
+ bno_cur_gt = NULL;
+ }
+ break;
+ }
+ /*
+ * Fell off the right end.
+ */
+ if ((error = xfs_alloc_increment(
+ bno_cur_gt, 0, &i)))
+ goto error0;
+ if (!i) {
+ xfs_btree_del_cursor(
+ bno_cur_gt,
+ XFS_BTREE_NOERROR);
+ bno_cur_gt = NULL;
+ break;
+ }
+ }
+ }
+ /*
+ * The left side is perfect, trash the right side.
+ */
+ else {
+ xfs_btree_del_cursor(bno_cur_gt,
+ XFS_BTREE_NOERROR);
+ bno_cur_gt = NULL;
+ }
+ }
+ /*
+ * It's the right side that was found first, look left.
+ */
+ else {
+ /*
+ * Fix up the length.
+ */
+ args->len = XFS_EXTLEN_MIN(gtlena, args->maxlen);
+ xfs_alloc_fix_len(args);
+ rlen = args->len;
+ gtdiff = xfs_alloc_compute_diff(args->agbno, rlen,
+ args->alignment, gtbno, gtlen, &gtnew);
+ /*
+ * Right side entry isn't perfect.
+ */
+ if (gtdiff) {
+ /*
+ * Look until we find a better one, run out of
+ * space, or run off the end.
+ */
+ while (bno_cur_lt && bno_cur_gt) {
+ if ((error = xfs_alloc_get_rec(
+ bno_cur_lt, &ltbno,
+ &ltlen, &i)))
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ xfs_alloc_compute_aligned(ltbno, ltlen,
+ args->alignment, args->minlen,
+ &ltbnoa, &ltlena);
+ /*
+ * The right one is clearly better.
+ */
+ if (ltbnoa <= args->agbno - gtdiff) {
+ xfs_btree_del_cursor(
+ bno_cur_lt,
+ XFS_BTREE_NOERROR);
+ bno_cur_lt = NULL;
+ break;
+ }
+ /*
+ * If we reach a big enough entry,
+ * compare the two and pick the best.
+ */
+ if (ltlena >= args->minlen) {
+ args->len = XFS_EXTLEN_MIN(
+ ltlena, args->maxlen);
+ xfs_alloc_fix_len(args);
+ rlen = args->len;
+ ltdiff = xfs_alloc_compute_diff(
+ args->agbno, rlen,
+ args->alignment,
+ ltbno, ltlen, &ltnew);
+ /*
+ * Left side is better.
+ */
+ if (ltdiff < gtdiff) {
+ xfs_btree_del_cursor(
+ bno_cur_gt,
+ XFS_BTREE_NOERROR);
+ bno_cur_gt = NULL;
+ }
+ /*
+ * Right side is better.
+ */
+ else {
+ xfs_btree_del_cursor(
+ bno_cur_lt,
+ XFS_BTREE_NOERROR);
+ bno_cur_lt = NULL;
+ }
+ break;
+ }
+ /*
+ * Fell off the left end.
+ */
+ if ((error = xfs_alloc_decrement(
+ bno_cur_lt, 0, &i)))
+ goto error0;
+ if (!i) {
+ xfs_btree_del_cursor(bno_cur_lt,
+ XFS_BTREE_NOERROR);
+ bno_cur_lt = NULL;
+ break;
+ }
+ }
+ }
+ /*
+ * The right side is perfect, trash the left side.
+ */
+ else {
+ xfs_btree_del_cursor(bno_cur_lt,
+ XFS_BTREE_NOERROR);
+ bno_cur_lt = NULL;
+ }
+ }
+ }
+ /*
+ * If we couldn't get anything, give up.
+ */
+ if (bno_cur_lt == NULL && bno_cur_gt == NULL) {
+ TRACE_ALLOC("neither", args);
+ args->agbno = NULLAGBLOCK;
+ return 0;
+ }
+ /*
+ * At this point we have selected a freespace entry, either to the
+ * left or to the right. If it's on the right, copy all the
+ * useful variables to the "left" set so we only have one
+ * copy of this code.
+ */
+ if (bno_cur_gt) {
+ bno_cur_lt = bno_cur_gt;
+ bno_cur_gt = NULL;
+ ltbno = gtbno;
+ ltbnoa = gtbnoa;
+ ltlen = gtlen;
+ ltlena = gtlena;
+ j = 1;
+ } else
+ j = 0;
+ /*
+ * Fix up the length and compute the useful address.
+ */
+ ltend = ltbno + ltlen;
+ args->len = XFS_EXTLEN_MIN(ltlena, args->maxlen);
+ xfs_alloc_fix_len(args);
+ if (!xfs_alloc_fix_minleft(args)) {
+ TRACE_ALLOC("nominleft", args);
+ xfs_btree_del_cursor(bno_cur_lt, XFS_BTREE_NOERROR);
+ xfs_btree_del_cursor(cnt_cur, XFS_BTREE_NOERROR);
+ return 0;
+ }
+ rlen = args->len;
+ (void)xfs_alloc_compute_diff(args->agbno, rlen, args->alignment, ltbno,
+ ltlen, &ltnew);
+ ASSERT(ltnew >= ltbno);
+ ASSERT(ltnew + rlen <= ltend);
+ ASSERT(ltnew + rlen <= INT_GET(XFS_BUF_TO_AGF(args->agbp)->agf_length,
+ ARCH_CONVERT));
+ args->agbno = ltnew;
+ if ((error = xfs_alloc_fixup_trees(cnt_cur, bno_cur_lt, ltbno, ltlen,
+ ltnew, rlen, XFSA_FIXUP_BNO_OK)))
+ goto error0;
+ TRACE_ALLOC(j ? "gt" : "lt", args);
+ xfs_btree_del_cursor(cnt_cur, XFS_BTREE_NOERROR);
+ xfs_btree_del_cursor(bno_cur_lt, XFS_BTREE_NOERROR);
+ return 0;
+
+ error0:
+ TRACE_ALLOC("error", args);
+ if (cnt_cur != NULL)
+ xfs_btree_del_cursor(cnt_cur, XFS_BTREE_ERROR);
+ if (bno_cur_lt != NULL)
+ xfs_btree_del_cursor(bno_cur_lt, XFS_BTREE_ERROR);
+ if (bno_cur_gt != NULL)
+ xfs_btree_del_cursor(bno_cur_gt, XFS_BTREE_ERROR);
+ return error;
+}
+
+/*
+ * Allocate a variable extent anywhere in the allocation group agno.
+ * Extent's length (returned in len) will be between minlen and maxlen,
+ * and of the form k * prod + mod unless there's nothing that large.
+ * Return the starting a.g. block, or NULLAGBLOCK if we can't do it.
+ */
+STATIC int /* error */
+xfs_alloc_ag_vextent_size(
+ xfs_alloc_arg_t *args) /* allocation argument structure */
+{
+ xfs_btree_cur_t *bno_cur; /* cursor for bno btree */
+ xfs_btree_cur_t *cnt_cur; /* cursor for cnt btree */
+ int error; /* error result */
+ xfs_agblock_t fbno; /* start of found freespace */
+ xfs_extlen_t flen; /* length of found freespace */
+#ifdef XFS_ALLOC_TRACE
+ static char fname[] = "xfs_alloc_ag_vextent_size";
+#endif
+ int i; /* temp status variable */
+ xfs_agblock_t rbno; /* returned block number */
+ xfs_extlen_t rlen; /* length of returned extent */
+
+ /*
+ * Allocate and initialize a cursor for the by-size btree.
+ */
+ cnt_cur = xfs_btree_init_cursor(args->mp, args->tp, args->agbp,
+ args->agno, XFS_BTNUM_CNT, NULL, 0);
+ bno_cur = NULL;
+ /*
+ * Look for an entry >= maxlen+alignment-1 blocks.
+ */
+ if ((error = xfs_alloc_lookup_ge(cnt_cur, 0,
+ args->maxlen + args->alignment - 1, &i)))
+ goto error0;
+ /*
+ * If none, then pick up the last entry in the tree unless the
+ * tree is empty.
+ */
+ if (!i) {
+ if ((error = xfs_alloc_ag_vextent_small(args, cnt_cur, &fbno,
+ &flen, &i)))
+ goto error0;
+ if (i == 0 || flen == 0) {
+ xfs_btree_del_cursor(cnt_cur, XFS_BTREE_NOERROR);
+ TRACE_ALLOC("noentry", args);
+ return 0;
+ }
+ ASSERT(i == 1);
+ }
+ /*
+ * There's a freespace as big as maxlen+alignment-1, get it.
+ */
+ else {
+ if ((error = xfs_alloc_get_rec(cnt_cur, &fbno, &flen, &i)))
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ }
+ /*
+ * In the first case above, we got the last entry in the
+ * by-size btree. Now we check to see if the space hits maxlen
+ * once aligned; if not, we search left for something better.
+ * This can't happen in the second case above.
+ */
+ xfs_alloc_compute_aligned(fbno, flen, args->alignment, args->minlen,
+ &rbno, &rlen);
+ rlen = XFS_EXTLEN_MIN(args->maxlen, rlen);
+ XFS_WANT_CORRUPTED_GOTO(rlen == 0 ||
+ (rlen <= flen && rbno + rlen <= fbno + flen), error0);
+ if (rlen < args->maxlen) {
+ xfs_agblock_t bestfbno;
+ xfs_extlen_t bestflen;
+ xfs_agblock_t bestrbno;
+ xfs_extlen_t bestrlen;
+
+ bestrlen = rlen;
+ bestrbno = rbno;
+ bestflen = flen;
+ bestfbno = fbno;
+ for (;;) {
+ if ((error = xfs_alloc_decrement(cnt_cur, 0, &i)))
+ goto error0;
+ if (i == 0)
+ break;
+ if ((error = xfs_alloc_get_rec(cnt_cur, &fbno, &flen,
+ &i)))
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ if (flen < bestrlen)
+ break;
+ xfs_alloc_compute_aligned(fbno, flen, args->alignment,
+ args->minlen, &rbno, &rlen);
+ rlen = XFS_EXTLEN_MIN(args->maxlen, rlen);
+ XFS_WANT_CORRUPTED_GOTO(rlen == 0 ||
+ (rlen <= flen && rbno + rlen <= fbno + flen),
+ error0);
+ if (rlen > bestrlen) {
+ bestrlen = rlen;
+ bestrbno = rbno;
+ bestflen = flen;
+ bestfbno = fbno;
+ if (rlen == args->maxlen)
+ break;
+ }
+ }
+ if ((error = xfs_alloc_lookup_eq(cnt_cur, bestfbno, bestflen,
+ &i)))
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ rlen = bestrlen;
+ rbno = bestrbno;
+ flen = bestflen;
+ fbno = bestfbno;
+ }
+ args->wasfromfl = 0;
+ /*
+ * Fix up the length.
+ */
+ args->len = rlen;
+ xfs_alloc_fix_len(args);
+ if (rlen < args->minlen || !xfs_alloc_fix_minleft(args)) {
+ xfs_btree_del_cursor(cnt_cur, XFS_BTREE_NOERROR);
+ TRACE_ALLOC("nominleft", args);
+ args->agbno = NULLAGBLOCK;
+ return 0;
+ }
+ rlen = args->len;
+ XFS_WANT_CORRUPTED_GOTO(rlen <= flen, error0);
+ /*
+ * Allocate and initialize a cursor for the by-block tree.
+ */
+ bno_cur = xfs_btree_init_cursor(args->mp, args->tp, args->agbp,
+ args->agno, XFS_BTNUM_BNO, NULL, 0);
+ if ((error = xfs_alloc_fixup_trees(cnt_cur, bno_cur, fbno, flen,
+ rbno, rlen, XFSA_FIXUP_CNT_OK)))
+ goto error0;
+ xfs_btree_del_cursor(cnt_cur, XFS_BTREE_NOERROR);
+ xfs_btree_del_cursor(bno_cur, XFS_BTREE_NOERROR);
+ cnt_cur = bno_cur = NULL;
+ args->len = rlen;
+ args->agbno = rbno;
+ XFS_WANT_CORRUPTED_GOTO(
+ args->agbno + args->len <=
+ INT_GET(XFS_BUF_TO_AGF(args->agbp)->agf_length,
+ ARCH_CONVERT),
+ error0);
+ TRACE_ALLOC("normal", args);
+ return 0;
+
+error0:
+ TRACE_ALLOC("error", args);
+ if (cnt_cur)
+ xfs_btree_del_cursor(cnt_cur, XFS_BTREE_ERROR);
+ if (bno_cur)
+ xfs_btree_del_cursor(bno_cur, XFS_BTREE_ERROR);
+ return error;
+}
+
+/*
+ * Deal with the case where only small freespaces remain.
+ * Either return the contents of the last freespace record,
+ * or allocate space from the freelist if there is nothing in the tree.
+ */
+STATIC int /* error */
+xfs_alloc_ag_vextent_small(
+ xfs_alloc_arg_t *args, /* allocation argument structure */
+ xfs_btree_cur_t *ccur, /* by-size cursor */
+ xfs_agblock_t *fbnop, /* result block number */
+ xfs_extlen_t *flenp, /* result length */
+ int *stat) /* status: 0-freelist, 1-normal/none */
+{
+ int error;
+ xfs_agblock_t fbno;
+ xfs_extlen_t flen;
+#ifdef XFS_ALLOC_TRACE
+ static char fname[] = "xfs_alloc_ag_vextent_small";
+#endif
+ int i;
+
+ if ((error = xfs_alloc_decrement(ccur, 0, &i)))
+ goto error0;
+ if (i) {
+ if ((error = xfs_alloc_get_rec(ccur, &fbno, &flen, &i)))
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ }
+ /*
+ * Nothing in the btree, try the freelist. Make sure
+ * to respect minleft even when pulling from the
+ * freelist.
+ */
+ else if (args->minlen == 1 && args->alignment == 1 && !args->isfl &&
+ (INT_GET(XFS_BUF_TO_AGF(args->agbp)->agf_flcount,
+ ARCH_CONVERT) > args->minleft)) {
+ if ((error = xfs_alloc_get_freelist(args->tp, args->agbp, &fbno)))
+ goto error0;
+ if (fbno != NULLAGBLOCK) {
+ if (args->userdata) {
+ xfs_buf_t *bp;
+
+ bp = xfs_btree_get_bufs(args->mp, args->tp,
+ args->agno, fbno, 0);
+ xfs_trans_binval(args->tp, bp);
+ }
+ args->len = 1;
+ args->agbno = fbno;
+ XFS_WANT_CORRUPTED_GOTO(
+ args->agbno + args->len <=
+ INT_GET(XFS_BUF_TO_AGF(args->agbp)->agf_length,
+ ARCH_CONVERT),
+ error0);
+ args->wasfromfl = 1;
+ TRACE_ALLOC("freelist", args);
+ *stat = 0;
+ return 0;
+ }
+ /*
+ * Nothing in the freelist.
+ */
+ else
+ flen = 0;
+ }
+ /*
+ * Can't allocate from the freelist for some reason.
+ */
+ else
+ flen = 0;
+ /*
+ * Can't do the allocation, give up.
+ */
+ if (flen < args->minlen) {
+ args->agbno = NULLAGBLOCK;
+ TRACE_ALLOC("notenough", args);
+ flen = 0;
+ }
+ *fbnop = fbno;
+ *flenp = flen;
+ *stat = 1;
+ TRACE_ALLOC("normal", args);
+ return 0;
+
+error0:
+ TRACE_ALLOC("error", args);
+ return error;
+}
+
+/*
+ * Free the extent starting at agno/bno for length.
+ */
+STATIC int /* error */
+xfs_free_ag_extent(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_buf_t *agbp, /* buffer for a.g. freelist header */
+ xfs_agnumber_t agno, /* allocation group number */
+ xfs_agblock_t bno, /* starting block number */
+ xfs_extlen_t len, /* length of extent */
+ int isfl) /* set if is freelist blocks - no sb acctg */
+{
+ xfs_btree_cur_t *bno_cur; /* cursor for by-block btree */
+ xfs_btree_cur_t *cnt_cur; /* cursor for by-size btree */
+ int error; /* error return value */
+#ifdef XFS_ALLOC_TRACE
+ static char fname[] = "xfs_free_ag_extent";
+#endif
+ xfs_agblock_t gtbno; /* start of right neighbor block */
+ xfs_extlen_t gtlen; /* length of right neighbor block */
+ int haveleft; /* have a left neighbor block */
+ int haveright; /* have a right neighbor block */
+ int i; /* temp, result code */
+ xfs_agblock_t ltbno; /* start of left neighbor block */
+ xfs_extlen_t ltlen; /* length of left neighbor block */
+ xfs_mount_t *mp; /* mount point struct for filesystem */
+ xfs_agblock_t nbno; /* new starting block of freespace */
+ xfs_extlen_t nlen; /* new length of freespace */
+
+ mp = tp->t_mountp;
+ /*
+ * Allocate and initialize a cursor for the by-block btree.
+ */
+ bno_cur = xfs_btree_init_cursor(mp, tp, agbp, agno, XFS_BTNUM_BNO, NULL,
+ 0);
+ cnt_cur = NULL;
+ /*
+ * Look for a neighboring block on the left (lower block numbers)
+ * that is contiguous with this space.
+ */
+ if ((error = xfs_alloc_lookup_le(bno_cur, bno, len, &haveleft)))
+ goto error0;
+ if (haveleft) {
+ /*
+ * There is a block to our left.
+ */
+ if ((error = xfs_alloc_get_rec(bno_cur, &ltbno, &ltlen, &i)))
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ /*
+ * It's not contiguous, though.
+ */
+ if (ltbno + ltlen < bno)
+ haveleft = 0;
+ else {
+ /*
+ * If this failure happens the request to free this
+ * space was invalid, it's (partly) already free.
+ * Very bad.
+ */
+ XFS_WANT_CORRUPTED_GOTO(ltbno + ltlen <= bno, error0);
+ }
+ }
+ /*
+ * Look for a neighboring block on the right (higher block numbers)
+ * that is contiguous with this space.
+ */
+ if ((error = xfs_alloc_increment(bno_cur, 0, &haveright)))
+ goto error0;
+ if (haveright) {
+ /*
+ * There is a block to our right.
+ */
+ if ((error = xfs_alloc_get_rec(bno_cur, &gtbno, &gtlen, &i)))
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ /*
+ * It's not contiguous, though.
+ */
+ if (bno + len < gtbno)
+ haveright = 0;
+ else {
+ /*
+ * If this failure happens the request to free this
+ * space was invalid, it's (partly) already free.
+ * Very bad.
+ */
+ XFS_WANT_CORRUPTED_GOTO(gtbno >= bno + len, error0);
+ }
+ }
+ /*
+ * Now allocate and initialize a cursor for the by-size tree.
+ */
+ cnt_cur = xfs_btree_init_cursor(mp, tp, agbp, agno, XFS_BTNUM_CNT, NULL,
+ 0);
+ /*
+ * Have both left and right contiguous neighbors.
+ * Merge all three into a single free block.
+ */
+ if (haveleft && haveright) {
+ /*
+ * Delete the old by-size entry on the left.
+ */
+ if ((error = xfs_alloc_lookup_eq(cnt_cur, ltbno, ltlen, &i)))
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ if ((error = xfs_alloc_delete(cnt_cur, &i)))
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ /*
+ * Delete the old by-size entry on the right.
+ */
+ if ((error = xfs_alloc_lookup_eq(cnt_cur, gtbno, gtlen, &i)))
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ if ((error = xfs_alloc_delete(cnt_cur, &i)))
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ /*
+ * Delete the old by-block entry for the right block.
+ */
+ if ((error = xfs_alloc_delete(bno_cur, &i)))
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ /*
+ * Move the by-block cursor back to the left neighbor.
+ */
+ if ((error = xfs_alloc_decrement(bno_cur, 0, &i)))
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+#ifdef DEBUG
+ /*
+ * Check that this is the right record: delete didn't
+ * mangle the cursor.
+ */
+ {
+ xfs_agblock_t xxbno;
+ xfs_extlen_t xxlen;
+
+ if ((error = xfs_alloc_get_rec(bno_cur, &xxbno, &xxlen,
+ &i)))
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(
+ i == 1 && xxbno == ltbno && xxlen == ltlen,
+ error0);
+ }
+#endif
+ /*
+ * Update remaining by-block entry to the new, joined block.
+ */
+ nbno = ltbno;
+ nlen = len + ltlen + gtlen;
+ if ((error = xfs_alloc_update(bno_cur, nbno, nlen)))
+ goto error0;
+ }
+ /*
+ * Have only a left contiguous neighbor.
+ * Merge it together with the new freespace.
+ */
+ else if (haveleft) {
+ /*
+ * Delete the old by-size entry on the left.
+ */
+ if ((error = xfs_alloc_lookup_eq(cnt_cur, ltbno, ltlen, &i)))
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ if ((error = xfs_alloc_delete(cnt_cur, &i)))
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ /*
+ * Back up the by-block cursor to the left neighbor, and
+ * update its length.
+ */
+ if ((error = xfs_alloc_decrement(bno_cur, 0, &i)))
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ nbno = ltbno;
+ nlen = len + ltlen;
+ if ((error = xfs_alloc_update(bno_cur, nbno, nlen)))
+ goto error0;
+ }
+ /*
+ * Have only a right contiguous neighbor.
+ * Merge it together with the new freespace.
+ */
+ else if (haveright) {
+ /*
+ * Delete the old by-size entry on the right.
+ */
+ if ((error = xfs_alloc_lookup_eq(cnt_cur, gtbno, gtlen, &i)))
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ if ((error = xfs_alloc_delete(cnt_cur, &i)))
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ /*
+ * Update the starting block and length of the right
+ * neighbor in the by-block tree.
+ */
+ nbno = bno;
+ nlen = len + gtlen;
+ if ((error = xfs_alloc_update(bno_cur, nbno, nlen)))
+ goto error0;
+ }
+ /*
+ * No contiguous neighbors.
+ * Insert the new freespace into the by-block tree.
+ */
+ else {
+ nbno = bno;
+ nlen = len;
+ if ((error = xfs_alloc_insert(bno_cur, &i)))
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ }
+ xfs_btree_del_cursor(bno_cur, XFS_BTREE_NOERROR);
+ bno_cur = NULL;
+ /*
+ * In all cases we need to insert the new freespace in the by-size tree.
+ */
+ if ((error = xfs_alloc_lookup_eq(cnt_cur, nbno, nlen, &i)))
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 0, error0);
+ if ((error = xfs_alloc_insert(cnt_cur, &i)))
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ xfs_btree_del_cursor(cnt_cur, XFS_BTREE_NOERROR);
+ cnt_cur = NULL;
+ /*
+ * Update the freespace totals in the ag and superblock.
+ */
+ {
+ xfs_agf_t *agf;
+ xfs_perag_t *pag; /* per allocation group data */
+
+ agf = XFS_BUF_TO_AGF(agbp);
+ pag = &mp->m_perag[agno];
+ INT_MOD(agf->agf_freeblks, ARCH_CONVERT, len);
+ xfs_trans_agblocks_delta(tp, len);
+ pag->pagf_freeblks += len;
+ XFS_WANT_CORRUPTED_GOTO(
+ INT_GET(agf->agf_freeblks, ARCH_CONVERT)
+ <= INT_GET(agf->agf_length, ARCH_CONVERT),
+ error0);
+ TRACE_MODAGF(NULL, agf, XFS_AGF_FREEBLKS);
+ xfs_alloc_log_agf(tp, agbp, XFS_AGF_FREEBLKS);
+ if (!isfl)
+ xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, (long)len);
+ XFS_STATS_INC(xs_freex);
+ XFS_STATS_ADD(xs_freeb, len);
+ }
+ TRACE_FREE(haveleft ?
+ (haveright ? "both" : "left") :
+ (haveright ? "right" : "none"),
+ agno, bno, len, isfl);
+
+ /*
+ * Since blocks move to the free list without the coordination
+ * used in xfs_bmap_finish, we can't allow block to be available
+ * for reallocation and non-transaction writing (user data)
+ * until we know that the transaction that moved it to the free
+ * list is permanently on disk. We track the blocks by declaring
+ * these blocks as "busy"; the busy list is maintained on a per-ag
+ * basis and each transaction records which entries should be removed
+ * when the iclog commits to disk. If a busy block is allocated,
+ * the iclog is pushed up to the LSN that freed the block.
+ */
+ xfs_alloc_mark_busy(tp, agno, bno, len);
+ return 0;
+
+ error0:
+ TRACE_FREE("error", agno, bno, len, isfl);
+ if (bno_cur)
+ xfs_btree_del_cursor(bno_cur, XFS_BTREE_ERROR);
+ if (cnt_cur)
+ xfs_btree_del_cursor(cnt_cur, XFS_BTREE_ERROR);
+ return error;
+}
+
+/*
+ * Visible (exported) allocation/free functions.
+ * Some of these are used just by xfs_alloc_btree.c and this file.
+ */
+
+/*
+ * Compute and fill in value of m_ag_maxlevels.
+ */
+void
+xfs_alloc_compute_maxlevels(
+ xfs_mount_t *mp) /* file system mount structure */
+{
+ int level;
+ uint maxblocks;
+ uint maxleafents;
+ int minleafrecs;
+ int minnoderecs;
+
+ maxleafents = (mp->m_sb.sb_agblocks + 1) / 2;
+ minleafrecs = mp->m_alloc_mnr[0];
+ minnoderecs = mp->m_alloc_mnr[1];
+ maxblocks = (maxleafents + minleafrecs - 1) / minleafrecs;
+ for (level = 1; maxblocks > 1; level++)
+ maxblocks = (maxblocks + minnoderecs - 1) / minnoderecs;
+ mp->m_ag_maxlevels = level;
+}
+
+/*
+ * Decide whether to use this allocation group for this allocation.
+ * If so, fix up the btree freelist's size.
+ */
+STATIC int /* error */
+xfs_alloc_fix_freelist(
+ xfs_alloc_arg_t *args, /* allocation argument structure */
+ int flags) /* XFS_ALLOC_FLAG_... */
+{
+ xfs_buf_t *agbp; /* agf buffer pointer */
+ xfs_agf_t *agf; /* a.g. freespace structure pointer */
+ xfs_buf_t *agflbp;/* agfl buffer pointer */
+ xfs_agblock_t bno; /* freelist block */
+ xfs_extlen_t delta; /* new blocks needed in freelist */
+ int error; /* error result code */
+ xfs_extlen_t longest;/* longest extent in allocation group */
+ xfs_mount_t *mp; /* file system mount point structure */
+ xfs_extlen_t need; /* total blocks needed in freelist */
+ xfs_perag_t *pag; /* per-ag information structure */
+ xfs_alloc_arg_t targs; /* local allocation arguments */
+ xfs_trans_t *tp; /* transaction pointer */
+
+ mp = args->mp;
+
+ pag = args->pag;
+ tp = args->tp;
+ if (!pag->pagf_init) {
+ if ((error = xfs_alloc_read_agf(mp, tp, args->agno, flags,
+ &agbp)))
+ return error;
+ if (!pag->pagf_init) {
+ args->agbp = NULL;
+ return 0;
+ }
+ } else
+ agbp = NULL;
+
+ /* If this is a metadata prefered pag and we are user data
+ * then try somewhere else if we are not being asked to
+ * try harder at this point
+ */
+ if (pag->pagf_metadata && args->userdata && flags) {
+ args->agbp = NULL;
+ return 0;
+ }
+
+ need = XFS_MIN_FREELIST_PAG(pag, mp);
+ delta = need > pag->pagf_flcount ? need - pag->pagf_flcount : 0;
+ /*
+ * If it looks like there isn't a long enough extent, or enough
+ * total blocks, reject it.
+ */
+ longest = (pag->pagf_longest > delta) ?
+ (pag->pagf_longest - delta) :
+ (pag->pagf_flcount > 0 || pag->pagf_longest > 0);
+ if (args->minlen + args->alignment + args->minalignslop - 1 > longest ||
+ (args->minleft &&
+ (int)(pag->pagf_freeblks + pag->pagf_flcount -
+ need - args->total) <
+ (int)args->minleft)) {
+ if (agbp)
+ xfs_trans_brelse(tp, agbp);
+ args->agbp = NULL;
+ return 0;
+ }
+ /*
+ * Get the a.g. freespace buffer.
+ * Can fail if we're not blocking on locks, and it's held.
+ */
+ if (agbp == NULL) {
+ if ((error = xfs_alloc_read_agf(mp, tp, args->agno, flags,
+ &agbp)))
+ return error;
+ if (agbp == NULL) {
+ args->agbp = NULL;
+ return 0;
+ }
+ }
+ /*
+ * Figure out how many blocks we should have in the freelist.
+ */
+ agf = XFS_BUF_TO_AGF(agbp);
+ need = XFS_MIN_FREELIST(agf, mp);
+ delta = need > INT_GET(agf->agf_flcount, ARCH_CONVERT) ?
+ (need - INT_GET(agf->agf_flcount, ARCH_CONVERT)) : 0;
+ /*
+ * If there isn't enough total or single-extent, reject it.
+ */
+ longest = INT_GET(agf->agf_longest, ARCH_CONVERT);
+ longest = (longest > delta) ? (longest - delta) :
+ (INT_GET(agf->agf_flcount, ARCH_CONVERT) > 0 || longest > 0);
+ if (args->minlen + args->alignment + args->minalignslop - 1 > longest ||
+ (args->minleft &&
+ (int)(INT_GET(agf->agf_freeblks, ARCH_CONVERT) +
+ INT_GET(agf->agf_flcount, ARCH_CONVERT) - need - args->total) <
+ (int)args->minleft)) {
+ xfs_trans_brelse(tp, agbp);
+ args->agbp = NULL;
+ return 0;
+ }
+ /*
+ * Make the freelist shorter if it's too long.
+ */
+ while (INT_GET(agf->agf_flcount, ARCH_CONVERT) > need) {
+ xfs_buf_t *bp;
+
+ if ((error = xfs_alloc_get_freelist(tp, agbp, &bno)))
+ return error;
+ if ((error = xfs_free_ag_extent(tp, agbp, args->agno, bno, 1, 1)))
+ return error;
+ bp = xfs_btree_get_bufs(mp, tp, args->agno, bno, 0);
+ xfs_trans_binval(tp, bp);
+ }
+ /*
+ * Initialize the args structure.
+ */
+ targs.tp = tp;
+ targs.mp = mp;
+ targs.agbp = agbp;
+ targs.agno = args->agno;
+ targs.mod = targs.minleft = targs.wasdel = targs.userdata =
+ targs.minalignslop = 0;
+ targs.alignment = targs.minlen = targs.prod = targs.isfl = 1;
+ targs.type = XFS_ALLOCTYPE_THIS_AG;
+ targs.pag = pag;
+ if ((error = xfs_alloc_read_agfl(mp, tp, targs.agno, &agflbp)))
+ return error;
+ /*
+ * Make the freelist longer if it's too short.
+ */
+ while (INT_GET(agf->agf_flcount, ARCH_CONVERT) < need) {
+ targs.agbno = 0;
+ targs.maxlen = need - INT_GET(agf->agf_flcount, ARCH_CONVERT);
+ /*
+ * Allocate as many blocks as possible at once.
+ */
+ if ((error = xfs_alloc_ag_vextent(&targs)))
+ return error;
+ /*
+ * Stop if we run out. Won't happen if callers are obeying
+ * the restrictions correctly. Can happen for free calls
+ * on a completely full ag.
+ */
+ if (targs.agbno == NULLAGBLOCK)
+ break;
+ /*
+ * Put each allocated block on the list.
+ */
+ for (bno = targs.agbno; bno < targs.agbno + targs.len; bno++) {
+ if ((error = xfs_alloc_put_freelist(tp, agbp, agflbp,
+ bno)))
+ return error;
+ }
+ }
+ args->agbp = agbp;
+ return 0;
+}
+
+/*
+ * Get a block from the freelist.
+ * Returns with the buffer for the block gotten.
+ */
+int /* error */
+xfs_alloc_get_freelist(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_buf_t *agbp, /* buffer containing the agf structure */
+ xfs_agblock_t *bnop) /* block address retrieved from freelist */
+{
+ xfs_agf_t *agf; /* a.g. freespace structure */
+ xfs_agfl_t *agfl; /* a.g. freelist structure */
+ xfs_buf_t *agflbp;/* buffer for a.g. freelist structure */
+ xfs_agblock_t bno; /* block number returned */
+ int error;
+#ifdef XFS_ALLOC_TRACE
+ static char fname[] = "xfs_alloc_get_freelist";
+#endif
+ xfs_mount_t *mp; /* mount structure */
+ xfs_perag_t *pag; /* per allocation group data */
+
+ agf = XFS_BUF_TO_AGF(agbp);
+ /*
+ * Freelist is empty, give up.
+ */
+ if (INT_ISZERO(agf->agf_flcount, ARCH_CONVERT)) {
+ *bnop = NULLAGBLOCK;
+ return 0;
+ }
+ /*
+ * Read the array of free blocks.
+ */
+ mp = tp->t_mountp;
+ if ((error = xfs_alloc_read_agfl(mp, tp,
+ INT_GET(agf->agf_seqno, ARCH_CONVERT), &agflbp)))
+ return error;
+ agfl = XFS_BUF_TO_AGFL(agflbp);
+ /*
+ * Get the block number and update the data structures.
+ */
+ bno = INT_GET(agfl->agfl_bno[INT_GET(agf->agf_flfirst, ARCH_CONVERT)], ARCH_CONVERT);
+ INT_MOD(agf->agf_flfirst, ARCH_CONVERT, 1);
+ xfs_trans_brelse(tp, agflbp);
+ if (INT_GET(agf->agf_flfirst, ARCH_CONVERT) == XFS_AGFL_SIZE(mp))
+ INT_ZERO(agf->agf_flfirst, ARCH_CONVERT);
+ pag = &mp->m_perag[INT_GET(agf->agf_seqno, ARCH_CONVERT)];
+ INT_MOD(agf->agf_flcount, ARCH_CONVERT, -1);
+ xfs_trans_agflist_delta(tp, -1);
+ pag->pagf_flcount--;
+ TRACE_MODAGF(NULL, agf, XFS_AGF_FLFIRST | XFS_AGF_FLCOUNT);
+ xfs_alloc_log_agf(tp, agbp, XFS_AGF_FLFIRST | XFS_AGF_FLCOUNT);
+ *bnop = bno;
+
+ /*
+ * As blocks are freed, they are added to the per-ag busy list
+ * and remain there until the freeing transaction is committed to
+ * disk. Now that we have allocated blocks, this list must be
+ * searched to see if a block is being reused. If one is, then
+ * the freeing transaction must be pushed to disk NOW by forcing
+ * to disk all iclogs up that transaction's LSN.
+ */
+ xfs_alloc_search_busy(tp, INT_GET(agf->agf_seqno, ARCH_CONVERT), bno, 1);
+ return 0;
+}
+
+/*
+ * Log the given fields from the agf structure.
+ */
+void
+xfs_alloc_log_agf(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_buf_t *bp, /* buffer for a.g. freelist header */
+ int fields) /* mask of fields to be logged (XFS_AGF_...) */
+{
+ int first; /* first byte offset */
+ int last; /* last byte offset */
+ static const short offsets[] = {
+ offsetof(xfs_agf_t, agf_magicnum),
+ offsetof(xfs_agf_t, agf_versionnum),
+ offsetof(xfs_agf_t, agf_seqno),
+ offsetof(xfs_agf_t, agf_length),
+ offsetof(xfs_agf_t, agf_roots[0]),
+ offsetof(xfs_agf_t, agf_levels[0]),
+ offsetof(xfs_agf_t, agf_flfirst),
+ offsetof(xfs_agf_t, agf_fllast),
+ offsetof(xfs_agf_t, agf_flcount),
+ offsetof(xfs_agf_t, agf_freeblks),
+ offsetof(xfs_agf_t, agf_longest),
+ sizeof(xfs_agf_t)
+ };
+
+ xfs_btree_offsets(fields, offsets, XFS_AGF_NUM_BITS, &first, &last);
+ xfs_trans_log_buf(tp, bp, (uint)first, (uint)last);
+}
+
+/*
+ * Interface for inode allocation to force the pag data to be initialized.
+ */
+int /* error */
+xfs_alloc_pagf_init(
+ xfs_mount_t *mp, /* file system mount structure */
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_agnumber_t agno, /* allocation group number */
+ int flags) /* XFS_ALLOC_FLAGS_... */
+{
+ xfs_buf_t *bp;
+ int error;
+
+ if ((error = xfs_alloc_read_agf(mp, tp, agno, flags, &bp)))
+ return error;
+ if (bp)
+ xfs_trans_brelse(tp, bp);
+ return 0;
+}
+
+/*
+ * Put the block on the freelist for the allocation group.
+ */
+int /* error */
+xfs_alloc_put_freelist(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_buf_t *agbp, /* buffer for a.g. freelist header */
+ xfs_buf_t *agflbp,/* buffer for a.g. free block array */
+ xfs_agblock_t bno) /* block being freed */
+{
+ xfs_agf_t *agf; /* a.g. freespace structure */
+ xfs_agfl_t *agfl; /* a.g. free block array */
+ xfs_agblock_t *blockp;/* pointer to array entry */
+ int error;
+#ifdef XFS_ALLOC_TRACE
+ static char fname[] = "xfs_alloc_put_freelist";
+#endif
+ xfs_mount_t *mp; /* mount structure */
+ xfs_perag_t *pag; /* per allocation group data */
+
+ agf = XFS_BUF_TO_AGF(agbp);
+ mp = tp->t_mountp;
+
+ if (!agflbp && (error = xfs_alloc_read_agfl(mp, tp,
+ INT_GET(agf->agf_seqno, ARCH_CONVERT), &agflbp)))
+ return error;
+ agfl = XFS_BUF_TO_AGFL(agflbp);
+ INT_MOD(agf->agf_fllast, ARCH_CONVERT, 1);
+ if (INT_GET(agf->agf_fllast, ARCH_CONVERT) == XFS_AGFL_SIZE(mp))
+ INT_ZERO(agf->agf_fllast, ARCH_CONVERT);
+ pag = &mp->m_perag[INT_GET(agf->agf_seqno, ARCH_CONVERT)];
+ INT_MOD(agf->agf_flcount, ARCH_CONVERT, 1);
+ xfs_trans_agflist_delta(tp, 1);
+ pag->pagf_flcount++;
+ ASSERT(INT_GET(agf->agf_flcount, ARCH_CONVERT) <= XFS_AGFL_SIZE(mp));
+ blockp = &agfl->agfl_bno[INT_GET(agf->agf_fllast, ARCH_CONVERT)];
+ INT_SET(*blockp, ARCH_CONVERT, bno);
+ TRACE_MODAGF(NULL, agf, XFS_AGF_FLLAST | XFS_AGF_FLCOUNT);
+ xfs_alloc_log_agf(tp, agbp, XFS_AGF_FLLAST | XFS_AGF_FLCOUNT);
+ xfs_trans_log_buf(tp, agflbp,
+ (int)((xfs_caddr_t)blockp - (xfs_caddr_t)agfl),
+ (int)((xfs_caddr_t)blockp - (xfs_caddr_t)agfl +
+ sizeof(xfs_agblock_t) - 1));
+ return 0;
+}
+
+/*
+ * Read in the allocation group header (free/alloc section).
+ */
+int /* error */
+xfs_alloc_read_agf(
+ xfs_mount_t *mp, /* mount point structure */
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_agnumber_t agno, /* allocation group number */
+ int flags, /* XFS_ALLOC_FLAG_... */
+ xfs_buf_t **bpp) /* buffer for the ag freelist header */
+{
+ xfs_agf_t *agf; /* ag freelist header */
+ int agf_ok; /* set if agf is consistent */
+ xfs_buf_t *bp; /* return value */
+ xfs_perag_t *pag; /* per allocation group data */
+ int error;
+
+ ASSERT(agno != NULLAGNUMBER);
+ error = xfs_trans_read_buf(
+ mp, tp, mp->m_ddev_targp,
+ XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)),
+ XFS_FSS_TO_BB(mp, 1),
+ (flags & XFS_ALLOC_FLAG_TRYLOCK) ? XFS_BUF_TRYLOCK : 0U,
+ &bp);
+ if (error)
+ return error;
+ ASSERT(!bp || !XFS_BUF_GETERROR(bp));
+ if (!bp) {
+ *bpp = NULL;
+ return 0;
+ }
+ /*
+ * Validate the magic number of the agf block.
+ */
+ agf = XFS_BUF_TO_AGF(bp);
+ agf_ok =
+ INT_GET(agf->agf_magicnum, ARCH_CONVERT) == XFS_AGF_MAGIC &&
+ XFS_AGF_GOOD_VERSION(
+ INT_GET(agf->agf_versionnum, ARCH_CONVERT)) &&
+ INT_GET(agf->agf_freeblks, ARCH_CONVERT) <=
+ INT_GET(agf->agf_length, ARCH_CONVERT) &&
+ INT_GET(agf->agf_flfirst, ARCH_CONVERT) < XFS_AGFL_SIZE(mp) &&
+ INT_GET(agf->agf_fllast, ARCH_CONVERT) < XFS_AGFL_SIZE(mp) &&
+ INT_GET(agf->agf_flcount, ARCH_CONVERT) <= XFS_AGFL_SIZE(mp);
+ if (unlikely(XFS_TEST_ERROR(!agf_ok, mp, XFS_ERRTAG_ALLOC_READ_AGF,
+ XFS_RANDOM_ALLOC_READ_AGF))) {
+ XFS_CORRUPTION_ERROR("xfs_alloc_read_agf",
+ XFS_ERRLEVEL_LOW, mp, agf);
+ xfs_trans_brelse(tp, bp);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ pag = &mp->m_perag[agno];
+ if (!pag->pagf_init) {
+ pag->pagf_freeblks = INT_GET(agf->agf_freeblks, ARCH_CONVERT);
+ pag->pagf_flcount = INT_GET(agf->agf_flcount, ARCH_CONVERT);
+ pag->pagf_longest = INT_GET(agf->agf_longest, ARCH_CONVERT);
+ pag->pagf_levels[XFS_BTNUM_BNOi] =
+ INT_GET(agf->agf_levels[XFS_BTNUM_BNOi], ARCH_CONVERT);
+ pag->pagf_levels[XFS_BTNUM_CNTi] =
+ INT_GET(agf->agf_levels[XFS_BTNUM_CNTi], ARCH_CONVERT);
+ spinlock_init(&pag->pagb_lock, "xfspagb");
+ pag->pagb_list = kmem_zalloc(XFS_PAGB_NUM_SLOTS *
+ sizeof(xfs_perag_busy_t), KM_SLEEP);
+ pag->pagf_init = 1;
+ }
+#ifdef DEBUG
+ else if (!XFS_FORCED_SHUTDOWN(mp)) {
+ ASSERT(pag->pagf_freeblks == INT_GET(agf->agf_freeblks, ARCH_CONVERT));
+ ASSERT(pag->pagf_flcount == INT_GET(agf->agf_flcount, ARCH_CONVERT));
+ ASSERT(pag->pagf_longest == INT_GET(agf->agf_longest, ARCH_CONVERT));
+ ASSERT(pag->pagf_levels[XFS_BTNUM_BNOi] ==
+ INT_GET(agf->agf_levels[XFS_BTNUM_BNOi], ARCH_CONVERT));
+ ASSERT(pag->pagf_levels[XFS_BTNUM_CNTi] ==
+ INT_GET(agf->agf_levels[XFS_BTNUM_CNTi], ARCH_CONVERT));
+ }
+#endif
+ XFS_BUF_SET_VTYPE_REF(bp, B_FS_AGF, XFS_AGF_REF);
+ *bpp = bp;
+ return 0;
+}
+
+/*
+ * Allocate an extent (variable-size).
+ * Depending on the allocation type, we either look in a single allocation
+ * group or loop over the allocation groups to find the result.
+ */
+int /* error */
+xfs_alloc_vextent(
+ xfs_alloc_arg_t *args) /* allocation argument structure */
+{
+ xfs_agblock_t agsize; /* allocation group size */
+ int error;
+ int flags; /* XFS_ALLOC_FLAG_... locking flags */
+#ifdef XFS_ALLOC_TRACE
+ static char fname[] = "xfs_alloc_vextent";
+#endif
+ xfs_extlen_t minleft;/* minimum left value, temp copy */
+ xfs_mount_t *mp; /* mount structure pointer */
+ xfs_agnumber_t sagno; /* starting allocation group number */
+ xfs_alloctype_t type; /* input allocation type */
+ int bump_rotor = 0;
+ int no_min = 0;
+ xfs_agnumber_t rotorstep = xfs_rotorstep; /* inode32 agf stepper */
+
+ mp = args->mp;
+ type = args->otype = args->type;
+ args->agbno = NULLAGBLOCK;
+ /*
+ * Just fix this up, for the case where the last a.g. is shorter
+ * (or there's only one a.g.) and the caller couldn't easily figure
+ * that out (xfs_bmap_alloc).
+ */
+ agsize = mp->m_sb.sb_agblocks;
+ if (args->maxlen > agsize)
+ args->maxlen = agsize;
+ if (args->alignment == 0)
+ args->alignment = 1;
+ ASSERT(XFS_FSB_TO_AGNO(mp, args->fsbno) < mp->m_sb.sb_agcount);
+ ASSERT(XFS_FSB_TO_AGBNO(mp, args->fsbno) < agsize);
+ ASSERT(args->minlen <= args->maxlen);
+ ASSERT(args->minlen <= agsize);
+ ASSERT(args->mod < args->prod);
+ if (XFS_FSB_TO_AGNO(mp, args->fsbno) >= mp->m_sb.sb_agcount ||
+ XFS_FSB_TO_AGBNO(mp, args->fsbno) >= agsize ||
+ args->minlen > args->maxlen || args->minlen > agsize ||
+ args->mod >= args->prod) {
+ args->fsbno = NULLFSBLOCK;
+ TRACE_ALLOC("badargs", args);
+ return 0;
+ }
+ minleft = args->minleft;
+
+ switch (type) {
+ case XFS_ALLOCTYPE_THIS_AG:
+ case XFS_ALLOCTYPE_NEAR_BNO:
+ case XFS_ALLOCTYPE_THIS_BNO:
+ /*
+ * These three force us into a single a.g.
+ */
+ args->agno = XFS_FSB_TO_AGNO(mp, args->fsbno);
+ down_read(&mp->m_peraglock);
+ args->pag = &mp->m_perag[args->agno];
+ args->minleft = 0;
+ error = xfs_alloc_fix_freelist(args, 0);
+ args->minleft = minleft;
+ if (error) {
+ TRACE_ALLOC("nofix", args);
+ goto error0;
+ }
+ if (!args->agbp) {
+ up_read(&mp->m_peraglock);
+ TRACE_ALLOC("noagbp", args);
+ break;
+ }
+ args->agbno = XFS_FSB_TO_AGBNO(mp, args->fsbno);
+ if ((error = xfs_alloc_ag_vextent(args)))
+ goto error0;
+ up_read(&mp->m_peraglock);
+ break;
+ case XFS_ALLOCTYPE_START_BNO:
+ /*
+ * Try near allocation first, then anywhere-in-ag after
+ * the first a.g. fails.
+ */
+ if ((args->userdata == XFS_ALLOC_INITIAL_USER_DATA) &&
+ (mp->m_flags & XFS_MOUNT_32BITINODES)) {
+ args->fsbno = XFS_AGB_TO_FSB(mp,
+ ((mp->m_agfrotor / rotorstep) %
+ mp->m_sb.sb_agcount), 0);
+ bump_rotor = 1;
+ }
+ args->agbno = XFS_FSB_TO_AGBNO(mp, args->fsbno);
+ args->type = XFS_ALLOCTYPE_NEAR_BNO;
+ /* FALLTHROUGH */
+ case XFS_ALLOCTYPE_ANY_AG:
+ case XFS_ALLOCTYPE_START_AG:
+ case XFS_ALLOCTYPE_FIRST_AG:
+ /*
+ * Rotate through the allocation groups looking for a winner.
+ */
+ if (type == XFS_ALLOCTYPE_ANY_AG) {
+ /*
+ * Start with the last place we left off.
+ */
+ args->agno = sagno = (mp->m_agfrotor / rotorstep) %
+ mp->m_sb.sb_agcount;
+ args->type = XFS_ALLOCTYPE_THIS_AG;
+ flags = XFS_ALLOC_FLAG_TRYLOCK;
+ } else if (type == XFS_ALLOCTYPE_FIRST_AG) {
+ /*
+ * Start with allocation group given by bno.
+ */
+ args->agno = XFS_FSB_TO_AGNO(mp, args->fsbno);
+ args->type = XFS_ALLOCTYPE_THIS_AG;
+ sagno = 0;
+ flags = 0;
+ } else {
+ if (type == XFS_ALLOCTYPE_START_AG)
+ args->type = XFS_ALLOCTYPE_THIS_AG;
+ /*
+ * Start with the given allocation group.
+ */
+ args->agno = sagno = XFS_FSB_TO_AGNO(mp, args->fsbno);
+ flags = XFS_ALLOC_FLAG_TRYLOCK;
+ }
+ /*
+ * Loop over allocation groups twice; first time with
+ * trylock set, second time without.
+ */
+ down_read(&mp->m_peraglock);
+ for (;;) {
+ args->pag = &mp->m_perag[args->agno];
+ if (no_min) args->minleft = 0;
+ error = xfs_alloc_fix_freelist(args, flags);
+ args->minleft = minleft;
+ if (error) {
+ TRACE_ALLOC("nofix", args);
+ goto error0;
+ }
+ /*
+ * If we get a buffer back then the allocation will fly.
+ */
+ if (args->agbp) {
+ if ((error = xfs_alloc_ag_vextent(args)))
+ goto error0;
+ break;
+ }
+ TRACE_ALLOC("loopfailed", args);
+ /*
+ * Didn't work, figure out the next iteration.
+ */
+ if (args->agno == sagno &&
+ type == XFS_ALLOCTYPE_START_BNO)
+ args->type = XFS_ALLOCTYPE_THIS_AG;
+ if (++(args->agno) == mp->m_sb.sb_agcount)
+ args->agno = 0;
+ /*
+ * Reached the starting a.g., must either be done
+ * or switch to non-trylock mode.
+ */
+ if (args->agno == sagno) {
+ if (no_min == 1) {
+ args->agbno = NULLAGBLOCK;
+ TRACE_ALLOC("allfailed", args);
+ break;
+ }
+ if (flags == 0) {
+ no_min = 1;
+ } else {
+ flags = 0;
+ if (type == XFS_ALLOCTYPE_START_BNO) {
+ args->agbno = XFS_FSB_TO_AGBNO(mp,
+ args->fsbno);
+ args->type = XFS_ALLOCTYPE_NEAR_BNO;
+ }
+ }
+ }
+ }
+ up_read(&mp->m_peraglock);
+ if (bump_rotor || (type == XFS_ALLOCTYPE_ANY_AG)) {
+ if (args->agno == sagno)
+ mp->m_agfrotor = (mp->m_agfrotor + 1) %
+ (mp->m_sb.sb_agcount * rotorstep);
+ else
+ mp->m_agfrotor = (args->agno * rotorstep + 1) %
+ (mp->m_sb.sb_agcount * rotorstep);
+ }
+ break;
+ default:
+ ASSERT(0);
+ /* NOTREACHED */
+ }
+ if (args->agbno == NULLAGBLOCK)
+ args->fsbno = NULLFSBLOCK;
+ else {
+ args->fsbno = XFS_AGB_TO_FSB(mp, args->agno, args->agbno);
+#ifdef DEBUG
+ ASSERT(args->len >= args->minlen);
+ ASSERT(args->len <= args->maxlen);
+ ASSERT(args->agbno % args->alignment == 0);
+ XFS_AG_CHECK_DADDR(mp, XFS_FSB_TO_DADDR(mp, args->fsbno),
+ args->len);
+#endif
+ }
+ return 0;
+error0:
+ up_read(&mp->m_peraglock);
+ return error;
+}
+
+/*
+ * Free an extent.
+ * Just break up the extent address and hand off to xfs_free_ag_extent
+ * after fixing up the freelist.
+ */
+int /* error */
+xfs_free_extent(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_fsblock_t bno, /* starting block number of extent */
+ xfs_extlen_t len) /* length of extent */
+{
+#ifdef DEBUG
+ xfs_agf_t *agf; /* a.g. freespace header */
+#endif
+ xfs_alloc_arg_t args; /* allocation argument structure */
+ int error;
+
+ ASSERT(len != 0);
+ args.tp = tp;
+ args.mp = tp->t_mountp;
+ args.agno = XFS_FSB_TO_AGNO(args.mp, bno);
+ ASSERT(args.agno < args.mp->m_sb.sb_agcount);
+ args.agbno = XFS_FSB_TO_AGBNO(args.mp, bno);
+ args.alignment = 1;
+ args.minlen = args.minleft = args.minalignslop = 0;
+ down_read(&args.mp->m_peraglock);
+ args.pag = &args.mp->m_perag[args.agno];
+ if ((error = xfs_alloc_fix_freelist(&args, 0)))
+ goto error0;
+#ifdef DEBUG
+ ASSERT(args.agbp != NULL);
+ agf = XFS_BUF_TO_AGF(args.agbp);
+ ASSERT(args.agbno + len <= INT_GET(agf->agf_length, ARCH_CONVERT));
+#endif
+ error = xfs_free_ag_extent(tp, args.agbp, args.agno, args.agbno,
+ len, 0);
+error0:
+ up_read(&args.mp->m_peraglock);
+ return error;
+}
+
+
+/*
+ * AG Busy list management
+ * The busy list contains block ranges that have been freed but whose
+ * transacations have not yet hit disk. If any block listed in a busy
+ * list is reused, the transaction that freed it must be forced to disk
+ * before continuing to use the block.
+ *
+ * xfs_alloc_mark_busy - add to the per-ag busy list
+ * xfs_alloc_clear_busy - remove an item from the per-ag busy list
+ */
+void
+xfs_alloc_mark_busy(xfs_trans_t *tp,
+ xfs_agnumber_t agno,
+ xfs_agblock_t bno,
+ xfs_extlen_t len)
+{
+ xfs_mount_t *mp;
+ xfs_perag_busy_t *bsy;
+ int n;
+ SPLDECL(s);
+
+ mp = tp->t_mountp;
+ s = mutex_spinlock(&mp->m_perag[agno].pagb_lock);
+
+ /* search pagb_list for an open slot */
+ for (bsy = mp->m_perag[agno].pagb_list, n = 0;
+ n < XFS_PAGB_NUM_SLOTS;
+ bsy++, n++) {
+ if (bsy->busy_tp == NULL) {
+ break;
+ }
+ }
+
+ if (n < XFS_PAGB_NUM_SLOTS) {
+ bsy = &mp->m_perag[agno].pagb_list[n];
+ mp->m_perag[agno].pagb_count++;
+ TRACE_BUSY("xfs_alloc_mark_busy", "got", agno, bno, len, n, tp);
+ bsy->busy_start = bno;
+ bsy->busy_length = len;
+ bsy->busy_tp = tp;
+ xfs_trans_add_busy(tp, agno, n);
+ } else {
+ TRACE_BUSY("xfs_alloc_mark_busy", "FULL", agno, bno, len, -1, tp);
+ /*
+ * The busy list is full! Since it is now not possible to
+ * track the free block, make this a synchronous transaction
+ * to insure that the block is not reused before this
+ * transaction commits.
+ */
+ xfs_trans_set_sync(tp);
+ }
+
+ mutex_spinunlock(&mp->m_perag[agno].pagb_lock, s);
+}
+
+void
+xfs_alloc_clear_busy(xfs_trans_t *tp,
+ xfs_agnumber_t agno,
+ int idx)
+{
+ xfs_mount_t *mp;
+ xfs_perag_busy_t *list;
+ SPLDECL(s);
+
+ mp = tp->t_mountp;
+
+ s = mutex_spinlock(&mp->m_perag[agno].pagb_lock);
+ list = mp->m_perag[agno].pagb_list;
+
+ ASSERT(idx < XFS_PAGB_NUM_SLOTS);
+ if (list[idx].busy_tp == tp) {
+ TRACE_UNBUSY("xfs_alloc_clear_busy", "found", agno, idx, tp);
+ list[idx].busy_tp = NULL;
+ mp->m_perag[agno].pagb_count--;
+ } else {
+ TRACE_UNBUSY("xfs_alloc_clear_busy", "missing", agno, idx, tp);
+ }
+
+ mutex_spinunlock(&mp->m_perag[agno].pagb_lock, s);
+}
+
+
+/*
+ * returns non-zero if any of (agno,bno):len is in a busy list
+ */
+int
+xfs_alloc_search_busy(xfs_trans_t *tp,
+ xfs_agnumber_t agno,
+ xfs_agblock_t bno,
+ xfs_extlen_t len)
+{
+ xfs_mount_t *mp;
+ xfs_perag_busy_t *bsy;
+ int n;
+ xfs_agblock_t uend, bend;
+ xfs_lsn_t lsn;
+ int cnt;
+ SPLDECL(s);
+
+ mp = tp->t_mountp;
+
+ s = mutex_spinlock(&mp->m_perag[agno].pagb_lock);
+ cnt = mp->m_perag[agno].pagb_count;
+
+ uend = bno + len - 1;
+
+ /* search pagb_list for this slot, skipping open slots */
+ for (bsy = mp->m_perag[agno].pagb_list, n = 0;
+ cnt; bsy++, n++) {
+
+ /*
+ * (start1,length1) within (start2, length2)
+ */
+ if (bsy->busy_tp != NULL) {
+ bend = bsy->busy_start + bsy->busy_length - 1;
+ if ((bno > bend) ||
+ (uend < bsy->busy_start)) {
+ cnt--;
+ } else {
+ TRACE_BUSYSEARCH("xfs_alloc_search_busy",
+ "found1", agno, bno, len, n,
+ tp);
+ break;
+ }
+ }
+ }
+
+ /*
+ * If a block was found, force the log through the LSN of the
+ * transaction that freed the block
+ */
+ if (cnt) {
+ TRACE_BUSYSEARCH("xfs_alloc_search_busy", "found", agno, bno, len, n, tp);
+ lsn = bsy->busy_tp->t_commit_lsn;
+ mutex_spinunlock(&mp->m_perag[agno].pagb_lock, s);
+ xfs_log_force(mp, lsn, XFS_LOG_FORCE|XFS_LOG_SYNC);
+ } else {
+ TRACE_BUSYSEARCH("xfs_alloc_search_busy", "not-found", agno, bno, len, n, tp);
+ n = -1;
+ mutex_spinunlock(&mp->m_perag[agno].pagb_lock, s);
+ }
+
+ return n;
+}
diff --git a/fs/xfs/xfs_alloc.h b/fs/xfs/xfs_alloc.h
new file mode 100644
index 00000000000000..72329c86351cdb
--- /dev/null
+++ b/fs/xfs/xfs_alloc.h
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_ALLOC_H__
+#define __XFS_ALLOC_H__
+
+struct xfs_buf;
+struct xfs_mount;
+struct xfs_perag;
+struct xfs_trans;
+
+/*
+ * Freespace allocation types. Argument to xfs_alloc_[v]extent.
+ */
+typedef enum xfs_alloctype
+{
+ XFS_ALLOCTYPE_ANY_AG, /* allocate anywhere, use rotor */
+ XFS_ALLOCTYPE_FIRST_AG, /* ... start at ag 0 */
+ XFS_ALLOCTYPE_START_AG, /* anywhere, start in this a.g. */
+ XFS_ALLOCTYPE_THIS_AG, /* anywhere in this a.g. */
+ XFS_ALLOCTYPE_START_BNO, /* near this block else anywhere */
+ XFS_ALLOCTYPE_NEAR_BNO, /* in this a.g. and near this block */
+ XFS_ALLOCTYPE_THIS_BNO /* at exactly this block */
+} xfs_alloctype_t;
+
+/*
+ * Flags for xfs_alloc_fix_freelist.
+ */
+#define XFS_ALLOC_FLAG_TRYLOCK 0x00000001 /* use trylock for buffer locking */
+
+/*
+ * Argument structure for xfs_alloc routines.
+ * This is turned into a structure to avoid having 20 arguments passed
+ * down several levels of the stack.
+ */
+typedef struct xfs_alloc_arg {
+ struct xfs_trans *tp; /* transaction pointer */
+ struct xfs_mount *mp; /* file system mount point */
+ struct xfs_buf *agbp; /* buffer for a.g. freelist header */
+ struct xfs_perag *pag; /* per-ag struct for this agno */
+ xfs_fsblock_t fsbno; /* file system block number */
+ xfs_agnumber_t agno; /* allocation group number */
+ xfs_agblock_t agbno; /* allocation group-relative block # */
+ xfs_extlen_t minlen; /* minimum size of extent */
+ xfs_extlen_t maxlen; /* maximum size of extent */
+ xfs_extlen_t mod; /* mod value for extent size */
+ xfs_extlen_t prod; /* prod value for extent size */
+ xfs_extlen_t minleft; /* min blocks must be left after us */
+ xfs_extlen_t total; /* total blocks needed in xaction */
+ xfs_extlen_t alignment; /* align answer to multiple of this */
+ xfs_extlen_t minalignslop; /* slop for minlen+alignment calcs */
+ xfs_extlen_t len; /* output: actual size of extent */
+ xfs_alloctype_t type; /* allocation type XFS_ALLOCTYPE_... */
+ xfs_alloctype_t otype; /* original allocation type */
+ char wasdel; /* set if allocation was prev delayed */
+ char wasfromfl; /* set if allocation is from freelist */
+ char isfl; /* set if is freelist blocks - !actg */
+ char userdata; /* set if this is user data */
+} xfs_alloc_arg_t;
+
+/*
+ * Defines for userdata
+ */
+#define XFS_ALLOC_USERDATA 1 /* allocation is for user data*/
+#define XFS_ALLOC_INITIAL_USER_DATA 2 /* special case start of file */
+
+
+#ifdef __KERNEL__
+
+#if defined(XFS_ALLOC_TRACE)
+/*
+ * Allocation tracing buffer size.
+ */
+#define XFS_ALLOC_TRACE_SIZE 4096
+extern ktrace_t *xfs_alloc_trace_buf;
+
+/*
+ * Types for alloc tracing.
+ */
+#define XFS_ALLOC_KTRACE_ALLOC 1
+#define XFS_ALLOC_KTRACE_FREE 2
+#define XFS_ALLOC_KTRACE_MODAGF 3
+#define XFS_ALLOC_KTRACE_BUSY 4
+#define XFS_ALLOC_KTRACE_UNBUSY 5
+#define XFS_ALLOC_KTRACE_BUSYSEARCH 6
+#endif
+
+/*
+ * Compute and fill in value of m_ag_maxlevels.
+ */
+void
+xfs_alloc_compute_maxlevels(
+ struct xfs_mount *mp); /* file system mount structure */
+
+/*
+ * Get a block from the freelist.
+ * Returns with the buffer for the block gotten.
+ */
+int /* error */
+xfs_alloc_get_freelist(
+ struct xfs_trans *tp, /* transaction pointer */
+ struct xfs_buf *agbp, /* buffer containing the agf structure */
+ xfs_agblock_t *bnop); /* block address retrieved from freelist */
+
+/*
+ * Log the given fields from the agf structure.
+ */
+void
+xfs_alloc_log_agf(
+ struct xfs_trans *tp, /* transaction pointer */
+ struct xfs_buf *bp, /* buffer for a.g. freelist header */
+ int fields);/* mask of fields to be logged (XFS_AGF_...) */
+
+/*
+ * Interface for inode allocation to force the pag data to be initialized.
+ */
+int /* error */
+xfs_alloc_pagf_init(
+ struct xfs_mount *mp, /* file system mount structure */
+ struct xfs_trans *tp, /* transaction pointer */
+ xfs_agnumber_t agno, /* allocation group number */
+ int flags); /* XFS_ALLOC_FLAGS_... */
+
+/*
+ * Put the block on the freelist for the allocation group.
+ */
+int /* error */
+xfs_alloc_put_freelist(
+ struct xfs_trans *tp, /* transaction pointer */
+ struct xfs_buf *agbp, /* buffer for a.g. freelist header */
+ struct xfs_buf *agflbp,/* buffer for a.g. free block array */
+ xfs_agblock_t bno); /* block being freed */
+
+/*
+ * Read in the allocation group header (free/alloc section).
+ */
+int /* error */
+xfs_alloc_read_agf(
+ struct xfs_mount *mp, /* mount point structure */
+ struct xfs_trans *tp, /* transaction pointer */
+ xfs_agnumber_t agno, /* allocation group number */
+ int flags, /* XFS_ALLOC_FLAG_... */
+ struct xfs_buf **bpp); /* buffer for the ag freelist header */
+
+/*
+ * Allocate an extent (variable-size).
+ */
+int /* error */
+xfs_alloc_vextent(
+ xfs_alloc_arg_t *args); /* allocation argument structure */
+
+/*
+ * Free an extent.
+ */
+int /* error */
+xfs_free_extent(
+ struct xfs_trans *tp, /* transaction pointer */
+ xfs_fsblock_t bno, /* starting block number of extent */
+ xfs_extlen_t len); /* length of extent */
+
+void
+xfs_alloc_mark_busy(xfs_trans_t *tp,
+ xfs_agnumber_t agno,
+ xfs_agblock_t bno,
+ xfs_extlen_t len);
+
+void
+xfs_alloc_clear_busy(xfs_trans_t *tp,
+ xfs_agnumber_t ag,
+ int idx);
+
+
+#endif /* __KERNEL__ */
+
+#endif /* __XFS_ALLOC_H__ */
diff --git a/fs/xfs/xfs_alloc_btree.c b/fs/xfs/xfs_alloc_btree.c
new file mode 100644
index 00000000000000..6b5de3a284e3f0
--- /dev/null
+++ b/fs/xfs/xfs_alloc_btree.c
@@ -0,0 +1,2204 @@
+/*
+ * Copyright (c) 2000-2001 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+/*
+ * Free space allocation for XFS.
+ */
+
+#include "xfs.h"
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_alloc.h"
+#include "xfs_error.h"
+
+/*
+ * Prototypes for internal functions.
+ */
+
+STATIC void xfs_alloc_log_block(xfs_trans_t *, xfs_buf_t *, int);
+STATIC void xfs_alloc_log_keys(xfs_btree_cur_t *, xfs_buf_t *, int, int);
+STATIC void xfs_alloc_log_ptrs(xfs_btree_cur_t *, xfs_buf_t *, int, int);
+STATIC void xfs_alloc_log_recs(xfs_btree_cur_t *, xfs_buf_t *, int, int);
+STATIC int xfs_alloc_lshift(xfs_btree_cur_t *, int, int *);
+STATIC int xfs_alloc_newroot(xfs_btree_cur_t *, int *);
+STATIC int xfs_alloc_rshift(xfs_btree_cur_t *, int, int *);
+STATIC int xfs_alloc_split(xfs_btree_cur_t *, int, xfs_agblock_t *,
+ xfs_alloc_key_t *, xfs_btree_cur_t **, int *);
+STATIC int xfs_alloc_updkey(xfs_btree_cur_t *, xfs_alloc_key_t *, int);
+
+/*
+ * Internal functions.
+ */
+
+/*
+ * Single level of the xfs_alloc_delete record deletion routine.
+ * Delete record pointed to by cur/level.
+ * Remove the record from its block then rebalance the tree.
+ * Return 0 for error, 1 for done, 2 to go on to the next level.
+ */
+STATIC int /* error */
+xfs_alloc_delrec(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ int level, /* level removing record from */
+ int *stat) /* fail/done/go-on */
+{
+ xfs_agf_t *agf; /* allocation group freelist header */
+ xfs_alloc_block_t *block; /* btree block record/key lives in */
+ xfs_agblock_t bno; /* btree block number */
+ xfs_buf_t *bp; /* buffer for block */
+ int error; /* error return value */
+ int i; /* loop index */
+ xfs_alloc_key_t key; /* kp points here if block is level 0 */
+ xfs_agblock_t lbno; /* left block's block number */
+ xfs_buf_t *lbp; /* left block's buffer pointer */
+ xfs_alloc_block_t *left; /* left btree block */
+ xfs_alloc_key_t *lkp=NULL; /* left block key pointer */
+ xfs_alloc_ptr_t *lpp=NULL; /* left block address pointer */
+ int lrecs=0; /* number of records in left block */
+ xfs_alloc_rec_t *lrp; /* left block record pointer */
+ xfs_mount_t *mp; /* mount structure */
+ int ptr; /* index in btree block for this rec */
+ xfs_agblock_t rbno; /* right block's block number */
+ xfs_buf_t *rbp; /* right block's buffer pointer */
+ xfs_alloc_block_t *right; /* right btree block */
+ xfs_alloc_key_t *rkp; /* right block key pointer */
+ xfs_alloc_ptr_t *rpp; /* right block address pointer */
+ int rrecs=0; /* number of records in right block */
+ xfs_alloc_rec_t *rrp; /* right block record pointer */
+ xfs_btree_cur_t *tcur; /* temporary btree cursor */
+
+ /*
+ * Get the index of the entry being deleted, check for nothing there.
+ */
+ ptr = cur->bc_ptrs[level];
+ if (ptr == 0) {
+ *stat = 0;
+ return 0;
+ }
+ /*
+ * Get the buffer & block containing the record or key/ptr.
+ */
+ bp = cur->bc_bufs[level];
+ block = XFS_BUF_TO_ALLOC_BLOCK(bp);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_sblock(cur, block, level, bp)))
+ return error;
+#endif
+ /*
+ * Fail if we're off the end of the block.
+ */
+ if (ptr > INT_GET(block->bb_numrecs, ARCH_CONVERT)) {
+ *stat = 0;
+ return 0;
+ }
+ XFS_STATS_INC(xs_abt_delrec);
+ /*
+ * It's a nonleaf. Excise the key and ptr being deleted, by
+ * sliding the entries past them down one.
+ * Log the changed areas of the block.
+ */
+ if (level > 0) {
+ lkp = XFS_ALLOC_KEY_ADDR(block, 1, cur);
+ lpp = XFS_ALLOC_PTR_ADDR(block, 1, cur);
+#ifdef DEBUG
+ for (i = ptr; i < INT_GET(block->bb_numrecs, ARCH_CONVERT); i++) {
+ if ((error = xfs_btree_check_sptr(cur, INT_GET(lpp[i], ARCH_CONVERT), level)))
+ return error;
+ }
+#endif
+ if (ptr < INT_GET(block->bb_numrecs, ARCH_CONVERT)) {
+ memmove(&lkp[ptr - 1], &lkp[ptr],
+ (INT_GET(block->bb_numrecs, ARCH_CONVERT) - ptr) * sizeof(*lkp)); /* INT_: mem copy */
+ memmove(&lpp[ptr - 1], &lpp[ptr],
+ (INT_GET(block->bb_numrecs, ARCH_CONVERT) - ptr) * sizeof(*lpp)); /* INT_: mem copy */
+ xfs_alloc_log_ptrs(cur, bp, ptr, INT_GET(block->bb_numrecs, ARCH_CONVERT) - 1);
+ xfs_alloc_log_keys(cur, bp, ptr, INT_GET(block->bb_numrecs, ARCH_CONVERT) - 1);
+ }
+ }
+ /*
+ * It's a leaf. Excise the record being deleted, by sliding the
+ * entries past it down one. Log the changed areas of the block.
+ */
+ else {
+ lrp = XFS_ALLOC_REC_ADDR(block, 1, cur);
+ if (ptr < INT_GET(block->bb_numrecs, ARCH_CONVERT)) {
+ memmove(&lrp[ptr - 1], &lrp[ptr],
+ (INT_GET(block->bb_numrecs, ARCH_CONVERT) - ptr) * sizeof(*lrp));
+ xfs_alloc_log_recs(cur, bp, ptr, INT_GET(block->bb_numrecs, ARCH_CONVERT) - 1);
+ }
+ /*
+ * If it's the first record in the block, we'll need a key
+ * structure to pass up to the next level (updkey).
+ */
+ if (ptr == 1) {
+ key.ar_startblock = lrp->ar_startblock; /* INT_: direct copy */
+ key.ar_blockcount = lrp->ar_blockcount; /* INT_: direct copy */
+ lkp = &key;
+ }
+ }
+ /*
+ * Decrement and log the number of entries in the block.
+ */
+ INT_MOD(block->bb_numrecs, ARCH_CONVERT, -1);
+ xfs_alloc_log_block(cur->bc_tp, bp, XFS_BB_NUMRECS);
+ /*
+ * See if the longest free extent in the allocation group was
+ * changed by this operation. True if it's the by-size btree, and
+ * this is the leaf level, and there is no right sibling block,
+ * and this was the last record.
+ */
+ agf = XFS_BUF_TO_AGF(cur->bc_private.a.agbp);
+ mp = cur->bc_mp;
+
+ if (level == 0 &&
+ cur->bc_btnum == XFS_BTNUM_CNT &&
+ INT_GET(block->bb_rightsib, ARCH_CONVERT) == NULLAGBLOCK &&
+ ptr > INT_GET(block->bb_numrecs, ARCH_CONVERT)) {
+ ASSERT(ptr == INT_GET(block->bb_numrecs, ARCH_CONVERT) + 1);
+ /*
+ * There are still records in the block. Grab the size
+ * from the last one.
+ */
+ if (INT_GET(block->bb_numrecs, ARCH_CONVERT)) {
+ rrp = XFS_ALLOC_REC_ADDR(block, INT_GET(block->bb_numrecs, ARCH_CONVERT), cur);
+ INT_COPY(agf->agf_longest, rrp->ar_blockcount, ARCH_CONVERT);
+ }
+ /*
+ * No free extents left.
+ */
+ else
+ INT_ZERO(agf->agf_longest, ARCH_CONVERT);
+ mp->m_perag[INT_GET(agf->agf_seqno, ARCH_CONVERT)].pagf_longest =
+ INT_GET(agf->agf_longest, ARCH_CONVERT);
+ xfs_alloc_log_agf(cur->bc_tp, cur->bc_private.a.agbp,
+ XFS_AGF_LONGEST);
+ }
+ /*
+ * Is this the root level? If so, we're almost done.
+ */
+ if (level == cur->bc_nlevels - 1) {
+ /*
+ * If this is the root level,
+ * and there's only one entry left,
+ * and it's NOT the leaf level,
+ * then we can get rid of this level.
+ */
+ if (INT_GET(block->bb_numrecs, ARCH_CONVERT) == 1 && level > 0) {
+ /*
+ * lpp is still set to the first pointer in the block.
+ * Make it the new root of the btree.
+ */
+ bno = INT_GET(agf->agf_roots[cur->bc_btnum], ARCH_CONVERT);
+ INT_COPY(agf->agf_roots[cur->bc_btnum], *lpp, ARCH_CONVERT);
+ INT_MOD(agf->agf_levels[cur->bc_btnum], ARCH_CONVERT, -1);
+ mp->m_perag[INT_GET(agf->agf_seqno, ARCH_CONVERT)].pagf_levels[cur->bc_btnum]--;
+ /*
+ * Put this buffer/block on the ag's freelist.
+ */
+ if ((error = xfs_alloc_put_freelist(cur->bc_tp,
+ cur->bc_private.a.agbp, NULL, bno)))
+ return error;
+ /*
+ * Since blocks move to the free list without the
+ * coordination used in xfs_bmap_finish, we can't allow
+ * block to be available for reallocation and
+ * non-transaction writing (user data) until we know
+ * that the transaction that moved it to the free list
+ * is permanently on disk. We track the blocks by
+ * declaring these blocks as "busy"; the busy list is
+ * maintained on a per-ag basis and each transaction
+ * records which entries should be removed when the
+ * iclog commits to disk. If a busy block is
+ * allocated, the iclog is pushed up to the LSN
+ * that freed the block.
+ */
+ xfs_alloc_mark_busy(cur->bc_tp,
+ INT_GET(agf->agf_seqno, ARCH_CONVERT), bno, 1);
+
+ xfs_trans_agbtree_delta(cur->bc_tp, -1);
+ xfs_alloc_log_agf(cur->bc_tp, cur->bc_private.a.agbp,
+ XFS_AGF_ROOTS | XFS_AGF_LEVELS);
+ /*
+ * Update the cursor so there's one fewer level.
+ */
+ xfs_btree_setbuf(cur, level, NULL);
+ cur->bc_nlevels--;
+ } else if (level > 0 &&
+ (error = xfs_alloc_decrement(cur, level, &i)))
+ return error;
+ *stat = 1;
+ return 0;
+ }
+ /*
+ * If we deleted the leftmost entry in the block, update the
+ * key values above us in the tree.
+ */
+ if (ptr == 1 && (error = xfs_alloc_updkey(cur, lkp, level + 1)))
+ return error;
+ /*
+ * If the number of records remaining in the block is at least
+ * the minimum, we're done.
+ */
+ if (INT_GET(block->bb_numrecs, ARCH_CONVERT) >= XFS_ALLOC_BLOCK_MINRECS(level, cur)) {
+ if (level > 0 && (error = xfs_alloc_decrement(cur, level, &i)))
+ return error;
+ *stat = 1;
+ return 0;
+ }
+ /*
+ * Otherwise, we have to move some records around to keep the
+ * tree balanced. Look at the left and right sibling blocks to
+ * see if we can re-balance by moving only one record.
+ */
+ rbno = INT_GET(block->bb_rightsib, ARCH_CONVERT);
+ lbno = INT_GET(block->bb_leftsib, ARCH_CONVERT);
+ bno = NULLAGBLOCK;
+ ASSERT(rbno != NULLAGBLOCK || lbno != NULLAGBLOCK);
+ /*
+ * Duplicate the cursor so our btree manipulations here won't
+ * disrupt the next level up.
+ */
+ if ((error = xfs_btree_dup_cursor(cur, &tcur)))
+ return error;
+ /*
+ * If there's a right sibling, see if it's ok to shift an entry
+ * out of it.
+ */
+ if (rbno != NULLAGBLOCK) {
+ /*
+ * Move the temp cursor to the last entry in the next block.
+ * Actually any entry but the first would suffice.
+ */
+ i = xfs_btree_lastrec(tcur, level);
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ if ((error = xfs_alloc_increment(tcur, level, &i)))
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ i = xfs_btree_lastrec(tcur, level);
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ /*
+ * Grab a pointer to the block.
+ */
+ rbp = tcur->bc_bufs[level];
+ right = XFS_BUF_TO_ALLOC_BLOCK(rbp);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_sblock(cur, right, level, rbp)))
+ goto error0;
+#endif
+ /*
+ * Grab the current block number, for future use.
+ */
+ bno = INT_GET(right->bb_leftsib, ARCH_CONVERT);
+ /*
+ * If right block is full enough so that removing one entry
+ * won't make it too empty, and left-shifting an entry out
+ * of right to us works, we're done.
+ */
+ if (INT_GET(right->bb_numrecs, ARCH_CONVERT) - 1 >=
+ XFS_ALLOC_BLOCK_MINRECS(level, cur)) {
+ if ((error = xfs_alloc_lshift(tcur, level, &i)))
+ goto error0;
+ if (i) {
+ ASSERT(INT_GET(block->bb_numrecs, ARCH_CONVERT) >=
+ XFS_ALLOC_BLOCK_MINRECS(level, cur));
+ xfs_btree_del_cursor(tcur,
+ XFS_BTREE_NOERROR);
+ if (level > 0 &&
+ (error = xfs_alloc_decrement(cur, level,
+ &i)))
+ return error;
+ *stat = 1;
+ return 0;
+ }
+ }
+ /*
+ * Otherwise, grab the number of records in right for
+ * future reference, and fix up the temp cursor to point
+ * to our block again (last record).
+ */
+ rrecs = INT_GET(right->bb_numrecs, ARCH_CONVERT);
+ if (lbno != NULLAGBLOCK) {
+ i = xfs_btree_firstrec(tcur, level);
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ if ((error = xfs_alloc_decrement(tcur, level, &i)))
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ }
+ }
+ /*
+ * If there's a left sibling, see if it's ok to shift an entry
+ * out of it.
+ */
+ if (lbno != NULLAGBLOCK) {
+ /*
+ * Move the temp cursor to the first entry in the
+ * previous block.
+ */
+ i = xfs_btree_firstrec(tcur, level);
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ if ((error = xfs_alloc_decrement(tcur, level, &i)))
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ xfs_btree_firstrec(tcur, level);
+ /*
+ * Grab a pointer to the block.
+ */
+ lbp = tcur->bc_bufs[level];
+ left = XFS_BUF_TO_ALLOC_BLOCK(lbp);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_sblock(cur, left, level, lbp)))
+ goto error0;
+#endif
+ /*
+ * Grab the current block number, for future use.
+ */
+ bno = INT_GET(left->bb_rightsib, ARCH_CONVERT);
+ /*
+ * If left block is full enough so that removing one entry
+ * won't make it too empty, and right-shifting an entry out
+ * of left to us works, we're done.
+ */
+ if (INT_GET(left->bb_numrecs, ARCH_CONVERT) - 1 >=
+ XFS_ALLOC_BLOCK_MINRECS(level, cur)) {
+ if ((error = xfs_alloc_rshift(tcur, level, &i)))
+ goto error0;
+ if (i) {
+ ASSERT(INT_GET(block->bb_numrecs, ARCH_CONVERT) >=
+ XFS_ALLOC_BLOCK_MINRECS(level, cur));
+ xfs_btree_del_cursor(tcur,
+ XFS_BTREE_NOERROR);
+ if (level == 0)
+ cur->bc_ptrs[0]++;
+ *stat = 1;
+ return 0;
+ }
+ }
+ /*
+ * Otherwise, grab the number of records in right for
+ * future reference.
+ */
+ lrecs = INT_GET(left->bb_numrecs, ARCH_CONVERT);
+ }
+ /*
+ * Delete the temp cursor, we're done with it.
+ */
+ xfs_btree_del_cursor(tcur, XFS_BTREE_NOERROR);
+ /*
+ * If here, we need to do a join to keep the tree balanced.
+ */
+ ASSERT(bno != NULLAGBLOCK);
+ /*
+ * See if we can join with the left neighbor block.
+ */
+ if (lbno != NULLAGBLOCK &&
+ lrecs + INT_GET(block->bb_numrecs, ARCH_CONVERT) <= XFS_ALLOC_BLOCK_MAXRECS(level, cur)) {
+ /*
+ * Set "right" to be the starting block,
+ * "left" to be the left neighbor.
+ */
+ rbno = bno;
+ right = block;
+ rbp = bp;
+ if ((error = xfs_btree_read_bufs(mp, cur->bc_tp,
+ cur->bc_private.a.agno, lbno, 0, &lbp,
+ XFS_ALLOC_BTREE_REF)))
+ return error;
+ left = XFS_BUF_TO_ALLOC_BLOCK(lbp);
+ if ((error = xfs_btree_check_sblock(cur, left, level, lbp)))
+ return error;
+ }
+ /*
+ * If that won't work, see if we can join with the right neighbor block.
+ */
+ else if (rbno != NULLAGBLOCK &&
+ rrecs + INT_GET(block->bb_numrecs, ARCH_CONVERT) <=
+ XFS_ALLOC_BLOCK_MAXRECS(level, cur)) {
+ /*
+ * Set "left" to be the starting block,
+ * "right" to be the right neighbor.
+ */
+ lbno = bno;
+ left = block;
+ lbp = bp;
+ if ((error = xfs_btree_read_bufs(mp, cur->bc_tp,
+ cur->bc_private.a.agno, rbno, 0, &rbp,
+ XFS_ALLOC_BTREE_REF)))
+ return error;
+ right = XFS_BUF_TO_ALLOC_BLOCK(rbp);
+ if ((error = xfs_btree_check_sblock(cur, right, level, rbp)))
+ return error;
+ }
+ /*
+ * Otherwise, we can't fix the imbalance.
+ * Just return. This is probably a logic error, but it's not fatal.
+ */
+ else {
+ if (level > 0 && (error = xfs_alloc_decrement(cur, level, &i)))
+ return error;
+ *stat = 1;
+ return 0;
+ }
+ /*
+ * We're now going to join "left" and "right" by moving all the stuff
+ * in "right" to "left" and deleting "right".
+ */
+ if (level > 0) {
+ /*
+ * It's a non-leaf. Move keys and pointers.
+ */
+ lkp = XFS_ALLOC_KEY_ADDR(left, INT_GET(left->bb_numrecs, ARCH_CONVERT) + 1, cur);
+ lpp = XFS_ALLOC_PTR_ADDR(left, INT_GET(left->bb_numrecs, ARCH_CONVERT) + 1, cur);
+ rkp = XFS_ALLOC_KEY_ADDR(right, 1, cur);
+ rpp = XFS_ALLOC_PTR_ADDR(right, 1, cur);
+#ifdef DEBUG
+ for (i = 0; i < INT_GET(right->bb_numrecs, ARCH_CONVERT); i++) {
+ if ((error = xfs_btree_check_sptr(cur, INT_GET(rpp[i], ARCH_CONVERT), level)))
+ return error;
+ }
+#endif
+ memcpy(lkp, rkp, INT_GET(right->bb_numrecs, ARCH_CONVERT) * sizeof(*lkp)); /* INT_: structure copy */
+ memcpy(lpp, rpp, INT_GET(right->bb_numrecs, ARCH_CONVERT) * sizeof(*lpp)); /* INT_: structure copy */
+ xfs_alloc_log_keys(cur, lbp, INT_GET(left->bb_numrecs, ARCH_CONVERT) + 1,
+ INT_GET(left->bb_numrecs, ARCH_CONVERT) + INT_GET(right->bb_numrecs, ARCH_CONVERT));
+ xfs_alloc_log_ptrs(cur, lbp, INT_GET(left->bb_numrecs, ARCH_CONVERT) + 1,
+ INT_GET(left->bb_numrecs, ARCH_CONVERT) + INT_GET(right->bb_numrecs, ARCH_CONVERT));
+ } else {
+ /*
+ * It's a leaf. Move records.
+ */
+ lrp = XFS_ALLOC_REC_ADDR(left, INT_GET(left->bb_numrecs, ARCH_CONVERT) + 1, cur);
+ rrp = XFS_ALLOC_REC_ADDR(right, 1, cur);
+ memcpy(lrp, rrp, INT_GET(right->bb_numrecs, ARCH_CONVERT) * sizeof(*lrp));
+ xfs_alloc_log_recs(cur, lbp, INT_GET(left->bb_numrecs, ARCH_CONVERT) + 1,
+ INT_GET(left->bb_numrecs, ARCH_CONVERT) + INT_GET(right->bb_numrecs, ARCH_CONVERT));
+ }
+ /*
+ * If we joined with the left neighbor, set the buffer in the
+ * cursor to the left block, and fix up the index.
+ */
+ if (bp != lbp) {
+ xfs_btree_setbuf(cur, level, lbp);
+ cur->bc_ptrs[level] += INT_GET(left->bb_numrecs, ARCH_CONVERT);
+ }
+ /*
+ * If we joined with the right neighbor and there's a level above
+ * us, increment the cursor at that level.
+ */
+ else if (level + 1 < cur->bc_nlevels &&
+ (error = xfs_alloc_increment(cur, level + 1, &i)))
+ return error;
+ /*
+ * Fix up the number of records in the surviving block.
+ */
+ INT_MOD(left->bb_numrecs, ARCH_CONVERT, INT_GET(right->bb_numrecs, ARCH_CONVERT));
+ /*
+ * Fix up the right block pointer in the surviving block, and log it.
+ */
+ left->bb_rightsib = right->bb_rightsib; /* INT_: direct copy */
+ xfs_alloc_log_block(cur->bc_tp, lbp, XFS_BB_NUMRECS | XFS_BB_RIGHTSIB);
+ /*
+ * If there is a right sibling now, make it point to the
+ * remaining block.
+ */
+ if (INT_GET(left->bb_rightsib, ARCH_CONVERT) != NULLAGBLOCK) {
+ xfs_alloc_block_t *rrblock;
+ xfs_buf_t *rrbp;
+
+ if ((error = xfs_btree_read_bufs(mp, cur->bc_tp,
+ cur->bc_private.a.agno, INT_GET(left->bb_rightsib, ARCH_CONVERT), 0,
+ &rrbp, XFS_ALLOC_BTREE_REF)))
+ return error;
+ rrblock = XFS_BUF_TO_ALLOC_BLOCK(rrbp);
+ if ((error = xfs_btree_check_sblock(cur, rrblock, level, rrbp)))
+ return error;
+ INT_SET(rrblock->bb_leftsib, ARCH_CONVERT, lbno);
+ xfs_alloc_log_block(cur->bc_tp, rrbp, XFS_BB_LEFTSIB);
+ }
+ /*
+ * Free the deleting block by putting it on the freelist.
+ */
+ if ((error = xfs_alloc_put_freelist(cur->bc_tp, cur->bc_private.a.agbp,
+ NULL, rbno)))
+ return error;
+ /*
+ * Since blocks move to the free list without the coordination
+ * used in xfs_bmap_finish, we can't allow block to be available
+ * for reallocation and non-transaction writing (user data)
+ * until we know that the transaction that moved it to the free
+ * list is permanently on disk. We track the blocks by declaring
+ * these blocks as "busy"; the busy list is maintained on a
+ * per-ag basis and each transaction records which entries
+ * should be removed when the iclog commits to disk. If a
+ * busy block is allocated, the iclog is pushed up to the
+ * LSN that freed the block.
+ */
+ xfs_alloc_mark_busy(cur->bc_tp,
+ INT_GET(agf->agf_seqno, ARCH_CONVERT), bno, 1);
+
+ xfs_trans_agbtree_delta(cur->bc_tp, -1);
+ /*
+ * Adjust the current level's cursor so that we're left referring
+ * to the right node, after we're done.
+ * If this leaves the ptr value 0 our caller will fix it up.
+ */
+ if (level > 0)
+ cur->bc_ptrs[level]--;
+ /*
+ * Return value means the next level up has something to do.
+ */
+ *stat = 2;
+ return 0;
+
+error0:
+ xfs_btree_del_cursor(tcur, XFS_BTREE_ERROR);
+ return error;
+}
+
+/*
+ * Insert one record/level. Return information to the caller
+ * allowing the next level up to proceed if necessary.
+ */
+STATIC int /* error */
+xfs_alloc_insrec(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ int level, /* level to insert record at */
+ xfs_agblock_t *bnop, /* i/o: block number inserted */
+ xfs_alloc_rec_t *recp, /* i/o: record data inserted */
+ xfs_btree_cur_t **curp, /* output: new cursor replacing cur */
+ int *stat) /* output: success/failure */
+{
+ xfs_agf_t *agf; /* allocation group freelist header */
+ xfs_alloc_block_t *block; /* btree block record/key lives in */
+ xfs_buf_t *bp; /* buffer for block */
+ int error; /* error return value */
+ int i; /* loop index */
+ xfs_alloc_key_t key; /* key value being inserted */
+ xfs_alloc_key_t *kp; /* pointer to btree keys */
+ xfs_agblock_t nbno; /* block number of allocated block */
+ xfs_btree_cur_t *ncur; /* new cursor to be used at next lvl */
+ xfs_alloc_key_t nkey; /* new key value, from split */
+ xfs_alloc_rec_t nrec; /* new record value, for caller */
+ int optr; /* old ptr value */
+ xfs_alloc_ptr_t *pp; /* pointer to btree addresses */
+ int ptr; /* index in btree block for this rec */
+ xfs_alloc_rec_t *rp; /* pointer to btree records */
+
+ ASSERT(INT_GET(recp->ar_blockcount, ARCH_CONVERT) > 0);
+ /*
+ * If we made it to the root level, allocate a new root block
+ * and we're done.
+ */
+ if (level >= cur->bc_nlevels) {
+ XFS_STATS_INC(xs_abt_insrec);
+ if ((error = xfs_alloc_newroot(cur, &i)))
+ return error;
+ *bnop = NULLAGBLOCK;
+ *stat = i;
+ return 0;
+ }
+ /*
+ * Make a key out of the record data to be inserted, and save it.
+ */
+ key.ar_startblock = recp->ar_startblock; /* INT_: direct copy */
+ key.ar_blockcount = recp->ar_blockcount; /* INT_: direct copy */
+ optr = ptr = cur->bc_ptrs[level];
+ /*
+ * If we're off the left edge, return failure.
+ */
+ if (ptr == 0) {
+ *stat = 0;
+ return 0;
+ }
+ XFS_STATS_INC(xs_abt_insrec);
+ /*
+ * Get pointers to the btree buffer and block.
+ */
+ bp = cur->bc_bufs[level];
+ block = XFS_BUF_TO_ALLOC_BLOCK(bp);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_sblock(cur, block, level, bp)))
+ return error;
+ /*
+ * Check that the new entry is being inserted in the right place.
+ */
+ if (ptr <= INT_GET(block->bb_numrecs, ARCH_CONVERT)) {
+ if (level == 0) {
+ rp = XFS_ALLOC_REC_ADDR(block, ptr, cur);
+ xfs_btree_check_rec(cur->bc_btnum, recp, rp);
+ } else {
+ kp = XFS_ALLOC_KEY_ADDR(block, ptr, cur);
+ xfs_btree_check_key(cur->bc_btnum, &key, kp);
+ }
+ }
+#endif
+ nbno = NULLAGBLOCK;
+ ncur = (xfs_btree_cur_t *)0;
+ /*
+ * If the block is full, we can't insert the new entry until we
+ * make the block un-full.
+ */
+ if (INT_GET(block->bb_numrecs, ARCH_CONVERT) == XFS_ALLOC_BLOCK_MAXRECS(level, cur)) {
+ /*
+ * First, try shifting an entry to the right neighbor.
+ */
+ if ((error = xfs_alloc_rshift(cur, level, &i)))
+ return error;
+ if (i) {
+ /* nothing */
+ }
+ /*
+ * Next, try shifting an entry to the left neighbor.
+ */
+ else {
+ if ((error = xfs_alloc_lshift(cur, level, &i)))
+ return error;
+ if (i)
+ optr = ptr = cur->bc_ptrs[level];
+ else {
+ /*
+ * Next, try splitting the current block in
+ * half. If this works we have to re-set our
+ * variables because we could be in a
+ * different block now.
+ */
+ if ((error = xfs_alloc_split(cur, level, &nbno,
+ &nkey, &ncur, &i)))
+ return error;
+ if (i) {
+ bp = cur->bc_bufs[level];
+ block = XFS_BUF_TO_ALLOC_BLOCK(bp);
+#ifdef DEBUG
+ if ((error =
+ xfs_btree_check_sblock(cur,
+ block, level, bp)))
+ return error;
+#endif
+ ptr = cur->bc_ptrs[level];
+ nrec.ar_startblock = nkey.ar_startblock; /* INT_: direct copy */
+ nrec.ar_blockcount = nkey.ar_blockcount; /* INT_: direct copy */
+ }
+ /*
+ * Otherwise the insert fails.
+ */
+ else {
+ *stat = 0;
+ return 0;
+ }
+ }
+ }
+ }
+ /*
+ * At this point we know there's room for our new entry in the block
+ * we're pointing at.
+ */
+ if (level > 0) {
+ /*
+ * It's a non-leaf entry. Make a hole for the new data
+ * in the key and ptr regions of the block.
+ */
+ kp = XFS_ALLOC_KEY_ADDR(block, 1, cur);
+ pp = XFS_ALLOC_PTR_ADDR(block, 1, cur);
+#ifdef DEBUG
+ for (i = INT_GET(block->bb_numrecs, ARCH_CONVERT); i >= ptr; i--) {
+ if ((error = xfs_btree_check_sptr(cur, INT_GET(pp[i - 1], ARCH_CONVERT), level)))
+ return error;
+ }
+#endif
+ memmove(&kp[ptr], &kp[ptr - 1],
+ (INT_GET(block->bb_numrecs, ARCH_CONVERT) - ptr + 1) * sizeof(*kp)); /* INT_: copy */
+ memmove(&pp[ptr], &pp[ptr - 1],
+ (INT_GET(block->bb_numrecs, ARCH_CONVERT) - ptr + 1) * sizeof(*pp)); /* INT_: copy */
+#ifdef DEBUG
+ if ((error = xfs_btree_check_sptr(cur, *bnop, level)))
+ return error;
+#endif
+ /*
+ * Now stuff the new data in, bump numrecs and log the new data.
+ */
+ kp[ptr - 1] = key;
+ INT_SET(pp[ptr - 1], ARCH_CONVERT, *bnop);
+ INT_MOD(block->bb_numrecs, ARCH_CONVERT, +1);
+ xfs_alloc_log_keys(cur, bp, ptr, INT_GET(block->bb_numrecs, ARCH_CONVERT));
+ xfs_alloc_log_ptrs(cur, bp, ptr, INT_GET(block->bb_numrecs, ARCH_CONVERT));
+#ifdef DEBUG
+ if (ptr < INT_GET(block->bb_numrecs, ARCH_CONVERT))
+ xfs_btree_check_key(cur->bc_btnum, kp + ptr - 1,
+ kp + ptr);
+#endif
+ } else {
+ /*
+ * It's a leaf entry. Make a hole for the new record.
+ */
+ rp = XFS_ALLOC_REC_ADDR(block, 1, cur);
+ memmove(&rp[ptr], &rp[ptr - 1],
+ (INT_GET(block->bb_numrecs, ARCH_CONVERT) - ptr + 1) * sizeof(*rp));
+ /*
+ * Now stuff the new record in, bump numrecs
+ * and log the new data.
+ */
+ rp[ptr - 1] = *recp; /* INT_: struct copy */
+ INT_MOD(block->bb_numrecs, ARCH_CONVERT, +1);
+ xfs_alloc_log_recs(cur, bp, ptr, INT_GET(block->bb_numrecs, ARCH_CONVERT));
+#ifdef DEBUG
+ if (ptr < INT_GET(block->bb_numrecs, ARCH_CONVERT))
+ xfs_btree_check_rec(cur->bc_btnum, rp + ptr - 1,
+ rp + ptr);
+#endif
+ }
+ /*
+ * Log the new number of records in the btree header.
+ */
+ xfs_alloc_log_block(cur->bc_tp, bp, XFS_BB_NUMRECS);
+ /*
+ * If we inserted at the start of a block, update the parents' keys.
+ */
+ if (optr == 1 && (error = xfs_alloc_updkey(cur, &key, level + 1)))
+ return error;
+ /*
+ * Look to see if the longest extent in the allocation group
+ * needs to be updated.
+ */
+
+ agf = XFS_BUF_TO_AGF(cur->bc_private.a.agbp);
+ if (level == 0 &&
+ cur->bc_btnum == XFS_BTNUM_CNT &&
+ INT_GET(block->bb_rightsib, ARCH_CONVERT) == NULLAGBLOCK &&
+ INT_GET(recp->ar_blockcount, ARCH_CONVERT) > INT_GET(agf->agf_longest, ARCH_CONVERT)) {
+ /*
+ * If this is a leaf in the by-size btree and there
+ * is no right sibling block and this block is bigger
+ * than the previous longest block, update it.
+ */
+ INT_COPY(agf->agf_longest, recp->ar_blockcount, ARCH_CONVERT);
+ cur->bc_mp->m_perag[INT_GET(agf->agf_seqno, ARCH_CONVERT)].pagf_longest
+ = INT_GET(recp->ar_blockcount, ARCH_CONVERT);
+ xfs_alloc_log_agf(cur->bc_tp, cur->bc_private.a.agbp,
+ XFS_AGF_LONGEST);
+ }
+ /*
+ * Return the new block number, if any.
+ * If there is one, give back a record value and a cursor too.
+ */
+ *bnop = nbno;
+ if (nbno != NULLAGBLOCK) {
+ *recp = nrec; /* INT_: struct copy */
+ *curp = ncur; /* INT_: struct copy */
+ }
+ *stat = 1;
+ return 0;
+}
+
+/*
+ * Log header fields from a btree block.
+ */
+STATIC void
+xfs_alloc_log_block(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_buf_t *bp, /* buffer containing btree block */
+ int fields) /* mask of fields: XFS_BB_... */
+{
+ int first; /* first byte offset logged */
+ int last; /* last byte offset logged */
+ static const short offsets[] = { /* table of offsets */
+ offsetof(xfs_alloc_block_t, bb_magic),
+ offsetof(xfs_alloc_block_t, bb_level),
+ offsetof(xfs_alloc_block_t, bb_numrecs),
+ offsetof(xfs_alloc_block_t, bb_leftsib),
+ offsetof(xfs_alloc_block_t, bb_rightsib),
+ sizeof(xfs_alloc_block_t)
+ };
+
+ xfs_btree_offsets(fields, offsets, XFS_BB_NUM_BITS, &first, &last);
+ xfs_trans_log_buf(tp, bp, first, last);
+}
+
+/*
+ * Log keys from a btree block (nonleaf).
+ */
+STATIC void
+xfs_alloc_log_keys(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ xfs_buf_t *bp, /* buffer containing btree block */
+ int kfirst, /* index of first key to log */
+ int klast) /* index of last key to log */
+{
+ xfs_alloc_block_t *block; /* btree block to log from */
+ int first; /* first byte offset logged */
+ xfs_alloc_key_t *kp; /* key pointer in btree block */
+ int last; /* last byte offset logged */
+
+ block = XFS_BUF_TO_ALLOC_BLOCK(bp);
+ kp = XFS_ALLOC_KEY_ADDR(block, 1, cur);
+ first = (int)((xfs_caddr_t)&kp[kfirst - 1] - (xfs_caddr_t)block);
+ last = (int)(((xfs_caddr_t)&kp[klast] - 1) - (xfs_caddr_t)block);
+ xfs_trans_log_buf(cur->bc_tp, bp, first, last);
+}
+
+/*
+ * Log block pointer fields from a btree block (nonleaf).
+ */
+STATIC void
+xfs_alloc_log_ptrs(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ xfs_buf_t *bp, /* buffer containing btree block */
+ int pfirst, /* index of first pointer to log */
+ int plast) /* index of last pointer to log */
+{
+ xfs_alloc_block_t *block; /* btree block to log from */
+ int first; /* first byte offset logged */
+ int last; /* last byte offset logged */
+ xfs_alloc_ptr_t *pp; /* block-pointer pointer in btree blk */
+
+ block = XFS_BUF_TO_ALLOC_BLOCK(bp);
+ pp = XFS_ALLOC_PTR_ADDR(block, 1, cur);
+ first = (int)((xfs_caddr_t)&pp[pfirst - 1] - (xfs_caddr_t)block);
+ last = (int)(((xfs_caddr_t)&pp[plast] - 1) - (xfs_caddr_t)block);
+ xfs_trans_log_buf(cur->bc_tp, bp, first, last);
+}
+
+/*
+ * Log records from a btree block (leaf).
+ */
+STATIC void
+xfs_alloc_log_recs(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ xfs_buf_t *bp, /* buffer containing btree block */
+ int rfirst, /* index of first record to log */
+ int rlast) /* index of last record to log */
+{
+ xfs_alloc_block_t *block; /* btree block to log from */
+ int first; /* first byte offset logged */
+ int last; /* last byte offset logged */
+ xfs_alloc_rec_t *rp; /* record pointer for btree block */
+
+
+ block = XFS_BUF_TO_ALLOC_BLOCK(bp);
+ rp = XFS_ALLOC_REC_ADDR(block, 1, cur);
+#ifdef DEBUG
+ {
+ xfs_agf_t *agf;
+ xfs_alloc_rec_t *p;
+
+ agf = XFS_BUF_TO_AGF(cur->bc_private.a.agbp);
+ for (p = &rp[rfirst - 1]; p <= &rp[rlast - 1]; p++)
+ ASSERT(INT_GET(p->ar_startblock, ARCH_CONVERT) + INT_GET(p->ar_blockcount, ARCH_CONVERT) <=
+ INT_GET(agf->agf_length, ARCH_CONVERT));
+ }
+#endif
+ first = (int)((xfs_caddr_t)&rp[rfirst - 1] - (xfs_caddr_t)block);
+ last = (int)(((xfs_caddr_t)&rp[rlast] - 1) - (xfs_caddr_t)block);
+ xfs_trans_log_buf(cur->bc_tp, bp, first, last);
+}
+
+/*
+ * Lookup the record. The cursor is made to point to it, based on dir.
+ * Return 0 if can't find any such record, 1 for success.
+ */
+STATIC int /* error */
+xfs_alloc_lookup(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ xfs_lookup_t dir, /* <=, ==, or >= */
+ int *stat) /* success/failure */
+{
+ xfs_agblock_t agbno; /* a.g. relative btree block number */
+ xfs_agnumber_t agno; /* allocation group number */
+ xfs_alloc_block_t *block=NULL; /* current btree block */
+ int diff; /* difference for the current key */
+ int error; /* error return value */
+ int keyno=0; /* current key number */
+ int level; /* level in the btree */
+ xfs_mount_t *mp; /* file system mount point */
+
+ XFS_STATS_INC(xs_abt_lookup);
+ /*
+ * Get the allocation group header, and the root block number.
+ */
+ mp = cur->bc_mp;
+
+ {
+ xfs_agf_t *agf; /* a.g. freespace header */
+
+ agf = XFS_BUF_TO_AGF(cur->bc_private.a.agbp);
+ agno = INT_GET(agf->agf_seqno, ARCH_CONVERT);
+ agbno = INT_GET(agf->agf_roots[cur->bc_btnum], ARCH_CONVERT);
+ }
+ /*
+ * Iterate over each level in the btree, starting at the root.
+ * For each level above the leaves, find the key we need, based
+ * on the lookup record, then follow the corresponding block
+ * pointer down to the next level.
+ */
+ for (level = cur->bc_nlevels - 1, diff = 1; level >= 0; level--) {
+ xfs_buf_t *bp; /* buffer pointer for btree block */
+ xfs_daddr_t d; /* disk address of btree block */
+
+ /*
+ * Get the disk address we're looking for.
+ */
+ d = XFS_AGB_TO_DADDR(mp, agno, agbno);
+ /*
+ * If the old buffer at this level is for a different block,
+ * throw it away, otherwise just use it.
+ */
+ bp = cur->bc_bufs[level];
+ if (bp && XFS_BUF_ADDR(bp) != d)
+ bp = (xfs_buf_t *)0;
+ if (!bp) {
+ /*
+ * Need to get a new buffer. Read it, then
+ * set it in the cursor, releasing the old one.
+ */
+ if ((error = xfs_btree_read_bufs(mp, cur->bc_tp, agno,
+ agbno, 0, &bp, XFS_ALLOC_BTREE_REF)))
+ return error;
+ xfs_btree_setbuf(cur, level, bp);
+ /*
+ * Point to the btree block, now that we have the buffer
+ */
+ block = XFS_BUF_TO_ALLOC_BLOCK(bp);
+ if ((error = xfs_btree_check_sblock(cur, block, level,
+ bp)))
+ return error;
+ } else
+ block = XFS_BUF_TO_ALLOC_BLOCK(bp);
+ /*
+ * If we already had a key match at a higher level, we know
+ * we need to use the first entry in this block.
+ */
+ if (diff == 0)
+ keyno = 1;
+ /*
+ * Otherwise we need to search this block. Do a binary search.
+ */
+ else {
+ int high; /* high entry number */
+ xfs_alloc_key_t *kkbase=NULL;/* base of keys in block */
+ xfs_alloc_rec_t *krbase=NULL;/* base of records in block */
+ int low; /* low entry number */
+
+ /*
+ * Get a pointer to keys or records.
+ */
+ if (level > 0)
+ kkbase = XFS_ALLOC_KEY_ADDR(block, 1, cur);
+ else
+ krbase = XFS_ALLOC_REC_ADDR(block, 1, cur);
+ /*
+ * Set low and high entry numbers, 1-based.
+ */
+ low = 1;
+ if (!(high = INT_GET(block->bb_numrecs, ARCH_CONVERT))) {
+ /*
+ * If the block is empty, the tree must
+ * be an empty leaf.
+ */
+ ASSERT(level == 0 && cur->bc_nlevels == 1);
+ cur->bc_ptrs[0] = dir != XFS_LOOKUP_LE;
+ *stat = 0;
+ return 0;
+ }
+ /*
+ * Binary search the block.
+ */
+ while (low <= high) {
+ xfs_extlen_t blockcount; /* key value */
+ xfs_agblock_t startblock; /* key value */
+
+ XFS_STATS_INC(xs_abt_compare);
+ /*
+ * keyno is average of low and high.
+ */
+ keyno = (low + high) >> 1;
+ /*
+ * Get startblock & blockcount.
+ */
+ if (level > 0) {
+ xfs_alloc_key_t *kkp;
+
+ kkp = kkbase + keyno - 1;
+ startblock = INT_GET(kkp->ar_startblock, ARCH_CONVERT);
+ blockcount = INT_GET(kkp->ar_blockcount, ARCH_CONVERT);
+ } else {
+ xfs_alloc_rec_t *krp;
+
+ krp = krbase + keyno - 1;
+ startblock = INT_GET(krp->ar_startblock, ARCH_CONVERT);
+ blockcount = INT_GET(krp->ar_blockcount, ARCH_CONVERT);
+ }
+ /*
+ * Compute difference to get next direction.
+ */
+ if (cur->bc_btnum == XFS_BTNUM_BNO)
+ diff = (int)startblock -
+ (int)cur->bc_rec.a.ar_startblock;
+ else if (!(diff = (int)blockcount -
+ (int)cur->bc_rec.a.ar_blockcount))
+ diff = (int)startblock -
+ (int)cur->bc_rec.a.ar_startblock;
+ /*
+ * Less than, move right.
+ */
+ if (diff < 0)
+ low = keyno + 1;
+ /*
+ * Greater than, move left.
+ */
+ else if (diff > 0)
+ high = keyno - 1;
+ /*
+ * Equal, we're done.
+ */
+ else
+ break;
+ }
+ }
+ /*
+ * If there are more levels, set up for the next level
+ * by getting the block number and filling in the cursor.
+ */
+ if (level > 0) {
+ /*
+ * If we moved left, need the previous key number,
+ * unless there isn't one.
+ */
+ if (diff > 0 && --keyno < 1)
+ keyno = 1;
+ agbno = INT_GET(*XFS_ALLOC_PTR_ADDR(block, keyno, cur), ARCH_CONVERT);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_sptr(cur, agbno, level)))
+ return error;
+#endif
+ cur->bc_ptrs[level] = keyno;
+ }
+ }
+ /*
+ * Done with the search.
+ * See if we need to adjust the results.
+ */
+ if (dir != XFS_LOOKUP_LE && diff < 0) {
+ keyno++;
+ /*
+ * If ge search and we went off the end of the block, but it's
+ * not the last block, we're in the wrong block.
+ */
+ if (dir == XFS_LOOKUP_GE &&
+ keyno > INT_GET(block->bb_numrecs, ARCH_CONVERT) &&
+ INT_GET(block->bb_rightsib, ARCH_CONVERT) != NULLAGBLOCK) {
+ int i;
+
+ cur->bc_ptrs[0] = keyno;
+ if ((error = xfs_alloc_increment(cur, 0, &i)))
+ return error;
+ XFS_WANT_CORRUPTED_RETURN(i == 1);
+ *stat = 1;
+ return 0;
+ }
+ }
+ else if (dir == XFS_LOOKUP_LE && diff > 0)
+ keyno--;
+ cur->bc_ptrs[0] = keyno;
+ /*
+ * Return if we succeeded or not.
+ */
+ if (keyno == 0 || keyno > INT_GET(block->bb_numrecs, ARCH_CONVERT))
+ *stat = 0;
+ else
+ *stat = ((dir != XFS_LOOKUP_EQ) || (diff == 0));
+ return 0;
+}
+
+/*
+ * Move 1 record left from cur/level if possible.
+ * Update cur to reflect the new path.
+ */
+STATIC int /* error */
+xfs_alloc_lshift(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ int level, /* level to shift record on */
+ int *stat) /* success/failure */
+{
+ int error; /* error return value */
+#ifdef DEBUG
+ int i; /* loop index */
+#endif
+ xfs_alloc_key_t key; /* key value for leaf level upward */
+ xfs_buf_t *lbp; /* buffer for left neighbor block */
+ xfs_alloc_block_t *left; /* left neighbor btree block */
+ int nrec; /* new number of left block entries */
+ xfs_buf_t *rbp; /* buffer for right (current) block */
+ xfs_alloc_block_t *right; /* right (current) btree block */
+ xfs_alloc_key_t *rkp=NULL; /* key pointer for right block */
+ xfs_alloc_ptr_t *rpp=NULL; /* address pointer for right block */
+ xfs_alloc_rec_t *rrp=NULL; /* record pointer for right block */
+
+ /*
+ * Set up variables for this block as "right".
+ */
+ rbp = cur->bc_bufs[level];
+ right = XFS_BUF_TO_ALLOC_BLOCK(rbp);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_sblock(cur, right, level, rbp)))
+ return error;
+#endif
+ /*
+ * If we've got no left sibling then we can't shift an entry left.
+ */
+ if (INT_GET(right->bb_leftsib, ARCH_CONVERT) == NULLAGBLOCK) {
+ *stat = 0;
+ return 0;
+ }
+ /*
+ * If the cursor entry is the one that would be moved, don't
+ * do it... it's too complicated.
+ */
+ if (cur->bc_ptrs[level] <= 1) {
+ *stat = 0;
+ return 0;
+ }
+ /*
+ * Set up the left neighbor as "left".
+ */
+ if ((error = xfs_btree_read_bufs(cur->bc_mp, cur->bc_tp,
+ cur->bc_private.a.agno, INT_GET(right->bb_leftsib, ARCH_CONVERT), 0, &lbp,
+ XFS_ALLOC_BTREE_REF)))
+ return error;
+ left = XFS_BUF_TO_ALLOC_BLOCK(lbp);
+ if ((error = xfs_btree_check_sblock(cur, left, level, lbp)))
+ return error;
+ /*
+ * If it's full, it can't take another entry.
+ */
+ if (INT_GET(left->bb_numrecs, ARCH_CONVERT) == XFS_ALLOC_BLOCK_MAXRECS(level, cur)) {
+ *stat = 0;
+ return 0;
+ }
+ nrec = INT_GET(left->bb_numrecs, ARCH_CONVERT) + 1;
+ /*
+ * If non-leaf, copy a key and a ptr to the left block.
+ */
+ if (level > 0) {
+ xfs_alloc_key_t *lkp; /* key pointer for left block */
+ xfs_alloc_ptr_t *lpp; /* address pointer for left block */
+
+ lkp = XFS_ALLOC_KEY_ADDR(left, nrec, cur);
+ rkp = XFS_ALLOC_KEY_ADDR(right, 1, cur);
+ *lkp = *rkp;
+ xfs_alloc_log_keys(cur, lbp, nrec, nrec);
+ lpp = XFS_ALLOC_PTR_ADDR(left, nrec, cur);
+ rpp = XFS_ALLOC_PTR_ADDR(right, 1, cur);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_sptr(cur, INT_GET(*rpp, ARCH_CONVERT), level)))
+ return error;
+#endif
+ *lpp = *rpp; /* INT_: copy */
+ xfs_alloc_log_ptrs(cur, lbp, nrec, nrec);
+ xfs_btree_check_key(cur->bc_btnum, lkp - 1, lkp);
+ }
+ /*
+ * If leaf, copy a record to the left block.
+ */
+ else {
+ xfs_alloc_rec_t *lrp; /* record pointer for left block */
+
+ lrp = XFS_ALLOC_REC_ADDR(left, nrec, cur);
+ rrp = XFS_ALLOC_REC_ADDR(right, 1, cur);
+ *lrp = *rrp;
+ xfs_alloc_log_recs(cur, lbp, nrec, nrec);
+ xfs_btree_check_rec(cur->bc_btnum, lrp - 1, lrp);
+ }
+ /*
+ * Bump and log left's numrecs, decrement and log right's numrecs.
+ */
+ INT_MOD(left->bb_numrecs, ARCH_CONVERT, +1);
+ xfs_alloc_log_block(cur->bc_tp, lbp, XFS_BB_NUMRECS);
+ INT_MOD(right->bb_numrecs, ARCH_CONVERT, -1);
+ xfs_alloc_log_block(cur->bc_tp, rbp, XFS_BB_NUMRECS);
+ /*
+ * Slide the contents of right down one entry.
+ */
+ if (level > 0) {
+#ifdef DEBUG
+ for (i = 0; i < INT_GET(right->bb_numrecs, ARCH_CONVERT); i++) {
+ if ((error = xfs_btree_check_sptr(cur, INT_GET(rpp[i + 1], ARCH_CONVERT),
+ level)))
+ return error;
+ }
+#endif
+ memmove(rkp, rkp + 1, INT_GET(right->bb_numrecs, ARCH_CONVERT) * sizeof(*rkp));
+ memmove(rpp, rpp + 1, INT_GET(right->bb_numrecs, ARCH_CONVERT) * sizeof(*rpp));
+ xfs_alloc_log_keys(cur, rbp, 1, INT_GET(right->bb_numrecs, ARCH_CONVERT));
+ xfs_alloc_log_ptrs(cur, rbp, 1, INT_GET(right->bb_numrecs, ARCH_CONVERT));
+ } else {
+ memmove(rrp, rrp + 1, INT_GET(right->bb_numrecs, ARCH_CONVERT) * sizeof(*rrp));
+ xfs_alloc_log_recs(cur, rbp, 1, INT_GET(right->bb_numrecs, ARCH_CONVERT));
+ key.ar_startblock = rrp->ar_startblock; /* INT_: direct copy */
+ key.ar_blockcount = rrp->ar_blockcount; /* INT_: direct copy */
+ rkp = &key;
+ }
+ /*
+ * Update the parent key values of right.
+ */
+ if ((error = xfs_alloc_updkey(cur, rkp, level + 1)))
+ return error;
+ /*
+ * Slide the cursor value left one.
+ */
+ cur->bc_ptrs[level]--;
+ *stat = 1;
+ return 0;
+}
+
+/*
+ * Allocate a new root block, fill it in.
+ */
+STATIC int /* error */
+xfs_alloc_newroot(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ int *stat) /* success/failure */
+{
+ int error; /* error return value */
+ xfs_agblock_t lbno; /* left block number */
+ xfs_buf_t *lbp; /* left btree buffer */
+ xfs_alloc_block_t *left; /* left btree block */
+ xfs_mount_t *mp; /* mount structure */
+ xfs_agblock_t nbno; /* new block number */
+ xfs_buf_t *nbp; /* new (root) buffer */
+ xfs_alloc_block_t *new; /* new (root) btree block */
+ int nptr; /* new value for key index, 1 or 2 */
+ xfs_agblock_t rbno; /* right block number */
+ xfs_buf_t *rbp; /* right btree buffer */
+ xfs_alloc_block_t *right; /* right btree block */
+
+ mp = cur->bc_mp;
+
+ ASSERT(cur->bc_nlevels < XFS_AG_MAXLEVELS(mp));
+ /*
+ * Get a buffer from the freelist blocks, for the new root.
+ */
+ if ((error = xfs_alloc_get_freelist(cur->bc_tp, cur->bc_private.a.agbp,
+ &nbno)))
+ return error;
+ /*
+ * None available, we fail.
+ */
+ if (nbno == NULLAGBLOCK) {
+ *stat = 0;
+ return 0;
+ }
+ xfs_trans_agbtree_delta(cur->bc_tp, 1);
+ nbp = xfs_btree_get_bufs(mp, cur->bc_tp, cur->bc_private.a.agno, nbno,
+ 0);
+ new = XFS_BUF_TO_ALLOC_BLOCK(nbp);
+ /*
+ * Set the root data in the a.g. freespace structure.
+ */
+ {
+ xfs_agf_t *agf; /* a.g. freespace header */
+ xfs_agnumber_t seqno;
+
+ agf = XFS_BUF_TO_AGF(cur->bc_private.a.agbp);
+ INT_SET(agf->agf_roots[cur->bc_btnum], ARCH_CONVERT, nbno);
+ INT_MOD(agf->agf_levels[cur->bc_btnum], ARCH_CONVERT, 1);
+ seqno = INT_GET(agf->agf_seqno, ARCH_CONVERT);
+ mp->m_perag[seqno].pagf_levels[cur->bc_btnum]++;
+ xfs_alloc_log_agf(cur->bc_tp, cur->bc_private.a.agbp,
+ XFS_AGF_ROOTS | XFS_AGF_LEVELS);
+ }
+ /*
+ * At the previous root level there are now two blocks: the old
+ * root, and the new block generated when it was split.
+ * We don't know which one the cursor is pointing at, so we
+ * set up variables "left" and "right" for each case.
+ */
+ lbp = cur->bc_bufs[cur->bc_nlevels - 1];
+ left = XFS_BUF_TO_ALLOC_BLOCK(lbp);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_sblock(cur, left, cur->bc_nlevels - 1, lbp)))
+ return error;
+#endif
+ if (INT_GET(left->bb_rightsib, ARCH_CONVERT) != NULLAGBLOCK) {
+ /*
+ * Our block is left, pick up the right block.
+ */
+ lbno = XFS_DADDR_TO_AGBNO(mp, XFS_BUF_ADDR(lbp));
+ rbno = INT_GET(left->bb_rightsib, ARCH_CONVERT);
+ if ((error = xfs_btree_read_bufs(mp, cur->bc_tp,
+ cur->bc_private.a.agno, rbno, 0, &rbp,
+ XFS_ALLOC_BTREE_REF)))
+ return error;
+ right = XFS_BUF_TO_ALLOC_BLOCK(rbp);
+ if ((error = xfs_btree_check_sblock(cur, right,
+ cur->bc_nlevels - 1, rbp)))
+ return error;
+ nptr = 1;
+ } else {
+ /*
+ * Our block is right, pick up the left block.
+ */
+ rbp = lbp;
+ right = left;
+ rbno = XFS_DADDR_TO_AGBNO(mp, XFS_BUF_ADDR(rbp));
+ lbno = INT_GET(right->bb_leftsib, ARCH_CONVERT);
+ if ((error = xfs_btree_read_bufs(mp, cur->bc_tp,
+ cur->bc_private.a.agno, lbno, 0, &lbp,
+ XFS_ALLOC_BTREE_REF)))
+ return error;
+ left = XFS_BUF_TO_ALLOC_BLOCK(lbp);
+ if ((error = xfs_btree_check_sblock(cur, left,
+ cur->bc_nlevels - 1, lbp)))
+ return error;
+ nptr = 2;
+ }
+ /*
+ * Fill in the new block's btree header and log it.
+ */
+ INT_SET(new->bb_magic, ARCH_CONVERT, xfs_magics[cur->bc_btnum]);
+ INT_SET(new->bb_level, ARCH_CONVERT, (__uint16_t)cur->bc_nlevels);
+ INT_SET(new->bb_numrecs, ARCH_CONVERT, 2);
+ INT_SET(new->bb_leftsib, ARCH_CONVERT, NULLAGBLOCK);
+ INT_SET(new->bb_rightsib, ARCH_CONVERT, NULLAGBLOCK);
+ xfs_alloc_log_block(cur->bc_tp, nbp, XFS_BB_ALL_BITS);
+ ASSERT(lbno != NULLAGBLOCK && rbno != NULLAGBLOCK);
+ /*
+ * Fill in the key data in the new root.
+ */
+ {
+ xfs_alloc_key_t *kp; /* btree key pointer */
+
+ kp = XFS_ALLOC_KEY_ADDR(new, 1, cur);
+ if (INT_GET(left->bb_level, ARCH_CONVERT) > 0) {
+ kp[0] = *XFS_ALLOC_KEY_ADDR(left, 1, cur); /* INT_: structure copy */
+ kp[1] = *XFS_ALLOC_KEY_ADDR(right, 1, cur);/* INT_: structure copy */
+ } else {
+ xfs_alloc_rec_t *rp; /* btree record pointer */
+
+ rp = XFS_ALLOC_REC_ADDR(left, 1, cur);
+ kp[0].ar_startblock = rp->ar_startblock; /* INT_: direct copy */
+ kp[0].ar_blockcount = rp->ar_blockcount; /* INT_: direct copy */
+ rp = XFS_ALLOC_REC_ADDR(right, 1, cur);
+ kp[1].ar_startblock = rp->ar_startblock; /* INT_: direct copy */
+ kp[1].ar_blockcount = rp->ar_blockcount; /* INT_: direct copy */
+ }
+ }
+ xfs_alloc_log_keys(cur, nbp, 1, 2);
+ /*
+ * Fill in the pointer data in the new root.
+ */
+ {
+ xfs_alloc_ptr_t *pp; /* btree address pointer */
+
+ pp = XFS_ALLOC_PTR_ADDR(new, 1, cur);
+ INT_SET(pp[0], ARCH_CONVERT, lbno);
+ INT_SET(pp[1], ARCH_CONVERT, rbno);
+ }
+ xfs_alloc_log_ptrs(cur, nbp, 1, 2);
+ /*
+ * Fix up the cursor.
+ */
+ xfs_btree_setbuf(cur, cur->bc_nlevels, nbp);
+ cur->bc_ptrs[cur->bc_nlevels] = nptr;
+ cur->bc_nlevels++;
+ *stat = 1;
+ return 0;
+}
+
+/*
+ * Move 1 record right from cur/level if possible.
+ * Update cur to reflect the new path.
+ */
+STATIC int /* error */
+xfs_alloc_rshift(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ int level, /* level to shift record on */
+ int *stat) /* success/failure */
+{
+ int error; /* error return value */
+ int i; /* loop index */
+ xfs_alloc_key_t key; /* key value for leaf level upward */
+ xfs_buf_t *lbp; /* buffer for left (current) block */
+ xfs_alloc_block_t *left; /* left (current) btree block */
+ xfs_buf_t *rbp; /* buffer for right neighbor block */
+ xfs_alloc_block_t *right; /* right neighbor btree block */
+ xfs_alloc_key_t *rkp; /* key pointer for right block */
+ xfs_btree_cur_t *tcur; /* temporary cursor */
+
+ /*
+ * Set up variables for this block as "left".
+ */
+ lbp = cur->bc_bufs[level];
+ left = XFS_BUF_TO_ALLOC_BLOCK(lbp);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_sblock(cur, left, level, lbp)))
+ return error;
+#endif
+ /*
+ * If we've got no right sibling then we can't shift an entry right.
+ */
+ if (INT_GET(left->bb_rightsib, ARCH_CONVERT) == NULLAGBLOCK) {
+ *stat = 0;
+ return 0;
+ }
+ /*
+ * If the cursor entry is the one that would be moved, don't
+ * do it... it's too complicated.
+ */
+ if (cur->bc_ptrs[level] >= INT_GET(left->bb_numrecs, ARCH_CONVERT)) {
+ *stat = 0;
+ return 0;
+ }
+ /*
+ * Set up the right neighbor as "right".
+ */
+ if ((error = xfs_btree_read_bufs(cur->bc_mp, cur->bc_tp,
+ cur->bc_private.a.agno, INT_GET(left->bb_rightsib, ARCH_CONVERT), 0, &rbp,
+ XFS_ALLOC_BTREE_REF)))
+ return error;
+ right = XFS_BUF_TO_ALLOC_BLOCK(rbp);
+ if ((error = xfs_btree_check_sblock(cur, right, level, rbp)))
+ return error;
+ /*
+ * If it's full, it can't take another entry.
+ */
+ if (INT_GET(right->bb_numrecs, ARCH_CONVERT) == XFS_ALLOC_BLOCK_MAXRECS(level, cur)) {
+ *stat = 0;
+ return 0;
+ }
+ /*
+ * Make a hole at the start of the right neighbor block, then
+ * copy the last left block entry to the hole.
+ */
+ if (level > 0) {
+ xfs_alloc_key_t *lkp; /* key pointer for left block */
+ xfs_alloc_ptr_t *lpp; /* address pointer for left block */
+ xfs_alloc_ptr_t *rpp; /* address pointer for right block */
+
+ lkp = XFS_ALLOC_KEY_ADDR(left, INT_GET(left->bb_numrecs, ARCH_CONVERT), cur);
+ lpp = XFS_ALLOC_PTR_ADDR(left, INT_GET(left->bb_numrecs, ARCH_CONVERT), cur);
+ rkp = XFS_ALLOC_KEY_ADDR(right, 1, cur);
+ rpp = XFS_ALLOC_PTR_ADDR(right, 1, cur);
+#ifdef DEBUG
+ for (i = INT_GET(right->bb_numrecs, ARCH_CONVERT) - 1; i >= 0; i--) {
+ if ((error = xfs_btree_check_sptr(cur, INT_GET(rpp[i], ARCH_CONVERT), level)))
+ return error;
+ }
+#endif
+ memmove(rkp + 1, rkp, INT_GET(right->bb_numrecs, ARCH_CONVERT) * sizeof(*rkp));
+ memmove(rpp + 1, rpp, INT_GET(right->bb_numrecs, ARCH_CONVERT) * sizeof(*rpp));
+#ifdef DEBUG
+ if ((error = xfs_btree_check_sptr(cur, INT_GET(*lpp, ARCH_CONVERT), level)))
+ return error;
+#endif
+ *rkp = *lkp; /* INT_: copy */
+ *rpp = *lpp; /* INT_: copy */
+ xfs_alloc_log_keys(cur, rbp, 1, INT_GET(right->bb_numrecs, ARCH_CONVERT) + 1);
+ xfs_alloc_log_ptrs(cur, rbp, 1, INT_GET(right->bb_numrecs, ARCH_CONVERT) + 1);
+ xfs_btree_check_key(cur->bc_btnum, rkp, rkp + 1);
+ } else {
+ xfs_alloc_rec_t *lrp; /* record pointer for left block */
+ xfs_alloc_rec_t *rrp; /* record pointer for right block */
+
+ lrp = XFS_ALLOC_REC_ADDR(left, INT_GET(left->bb_numrecs, ARCH_CONVERT), cur);
+ rrp = XFS_ALLOC_REC_ADDR(right, 1, cur);
+ memmove(rrp + 1, rrp, INT_GET(right->bb_numrecs, ARCH_CONVERT) * sizeof(*rrp));
+ *rrp = *lrp;
+ xfs_alloc_log_recs(cur, rbp, 1, INT_GET(right->bb_numrecs, ARCH_CONVERT) + 1);
+ key.ar_startblock = rrp->ar_startblock; /* INT_: direct copy */
+ key.ar_blockcount = rrp->ar_blockcount; /* INT_: direct copy */
+ rkp = &key;
+ xfs_btree_check_rec(cur->bc_btnum, rrp, rrp + 1);
+ }
+ /*
+ * Decrement and log left's numrecs, bump and log right's numrecs.
+ */
+ INT_MOD(left->bb_numrecs, ARCH_CONVERT, -1);
+ xfs_alloc_log_block(cur->bc_tp, lbp, XFS_BB_NUMRECS);
+ INT_MOD(right->bb_numrecs, ARCH_CONVERT, +1);
+ xfs_alloc_log_block(cur->bc_tp, rbp, XFS_BB_NUMRECS);
+ /*
+ * Using a temporary cursor, update the parent key values of the
+ * block on the right.
+ */
+ if ((error = xfs_btree_dup_cursor(cur, &tcur)))
+ return error;
+ i = xfs_btree_lastrec(tcur, level);
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ if ((error = xfs_alloc_increment(tcur, level, &i)) ||
+ (error = xfs_alloc_updkey(tcur, rkp, level + 1)))
+ goto error0;
+ xfs_btree_del_cursor(tcur, XFS_BTREE_NOERROR);
+ *stat = 1;
+ return 0;
+error0:
+ xfs_btree_del_cursor(tcur, XFS_BTREE_ERROR);
+ return error;
+}
+
+/*
+ * Split cur/level block in half.
+ * Return new block number and its first record (to be inserted into parent).
+ */
+STATIC int /* error */
+xfs_alloc_split(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ int level, /* level to split */
+ xfs_agblock_t *bnop, /* output: block number allocated */
+ xfs_alloc_key_t *keyp, /* output: first key of new block */
+ xfs_btree_cur_t **curp, /* output: new cursor */
+ int *stat) /* success/failure */
+{
+ int error; /* error return value */
+ int i; /* loop index/record number */
+ xfs_agblock_t lbno; /* left (current) block number */
+ xfs_buf_t *lbp; /* buffer for left block */
+ xfs_alloc_block_t *left; /* left (current) btree block */
+ xfs_agblock_t rbno; /* right (new) block number */
+ xfs_buf_t *rbp; /* buffer for right block */
+ xfs_alloc_block_t *right; /* right (new) btree block */
+
+ /*
+ * Allocate the new block from the freelist.
+ * If we can't do it, we're toast. Give up.
+ */
+ if ((error = xfs_alloc_get_freelist(cur->bc_tp, cur->bc_private.a.agbp,
+ &rbno)))
+ return error;
+ if (rbno == NULLAGBLOCK) {
+ *stat = 0;
+ return 0;
+ }
+ xfs_trans_agbtree_delta(cur->bc_tp, 1);
+ rbp = xfs_btree_get_bufs(cur->bc_mp, cur->bc_tp, cur->bc_private.a.agno,
+ rbno, 0);
+ /*
+ * Set up the new block as "right".
+ */
+ right = XFS_BUF_TO_ALLOC_BLOCK(rbp);
+ /*
+ * "Left" is the current (according to the cursor) block.
+ */
+ lbp = cur->bc_bufs[level];
+ left = XFS_BUF_TO_ALLOC_BLOCK(lbp);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_sblock(cur, left, level, lbp)))
+ return error;
+#endif
+ /*
+ * Fill in the btree header for the new block.
+ */
+ INT_SET(right->bb_magic, ARCH_CONVERT, xfs_magics[cur->bc_btnum]);
+ right->bb_level = left->bb_level; /* INT_: direct copy */
+ INT_SET(right->bb_numrecs, ARCH_CONVERT, (__uint16_t)(INT_GET(left->bb_numrecs, ARCH_CONVERT) / 2));
+ /*
+ * Make sure that if there's an odd number of entries now, that
+ * each new block will have the same number of entries.
+ */
+ if ((INT_GET(left->bb_numrecs, ARCH_CONVERT) & 1) &&
+ cur->bc_ptrs[level] <= INT_GET(right->bb_numrecs, ARCH_CONVERT) + 1)
+ INT_MOD(right->bb_numrecs, ARCH_CONVERT, +1);
+ i = INT_GET(left->bb_numrecs, ARCH_CONVERT) - INT_GET(right->bb_numrecs, ARCH_CONVERT) + 1;
+ /*
+ * For non-leaf blocks, copy keys and addresses over to the new block.
+ */
+ if (level > 0) {
+ xfs_alloc_key_t *lkp; /* left btree key pointer */
+ xfs_alloc_ptr_t *lpp; /* left btree address pointer */
+ xfs_alloc_key_t *rkp; /* right btree key pointer */
+ xfs_alloc_ptr_t *rpp; /* right btree address pointer */
+
+ lkp = XFS_ALLOC_KEY_ADDR(left, i, cur);
+ lpp = XFS_ALLOC_PTR_ADDR(left, i, cur);
+ rkp = XFS_ALLOC_KEY_ADDR(right, 1, cur);
+ rpp = XFS_ALLOC_PTR_ADDR(right, 1, cur);
+#ifdef DEBUG
+ for (i = 0; i < INT_GET(right->bb_numrecs, ARCH_CONVERT); i++) {
+ if ((error = xfs_btree_check_sptr(cur, INT_GET(lpp[i], ARCH_CONVERT), level)))
+ return error;
+ }
+#endif
+ memcpy(rkp, lkp, INT_GET(right->bb_numrecs, ARCH_CONVERT) * sizeof(*rkp)); /* INT_: copy */
+ memcpy(rpp, lpp, INT_GET(right->bb_numrecs, ARCH_CONVERT) * sizeof(*rpp)); /* INT_: copy */
+ xfs_alloc_log_keys(cur, rbp, 1, INT_GET(right->bb_numrecs, ARCH_CONVERT));
+ xfs_alloc_log_ptrs(cur, rbp, 1, INT_GET(right->bb_numrecs, ARCH_CONVERT));
+ *keyp = *rkp;
+ }
+ /*
+ * For leaf blocks, copy records over to the new block.
+ */
+ else {
+ xfs_alloc_rec_t *lrp; /* left btree record pointer */
+ xfs_alloc_rec_t *rrp; /* right btree record pointer */
+
+ lrp = XFS_ALLOC_REC_ADDR(left, i, cur);
+ rrp = XFS_ALLOC_REC_ADDR(right, 1, cur);
+ memcpy(rrp, lrp, INT_GET(right->bb_numrecs, ARCH_CONVERT) * sizeof(*rrp));
+ xfs_alloc_log_recs(cur, rbp, 1, INT_GET(right->bb_numrecs, ARCH_CONVERT));
+ keyp->ar_startblock = rrp->ar_startblock; /* INT_: direct copy */
+ keyp->ar_blockcount = rrp->ar_blockcount; /* INT_: direct copy */
+ }
+ /*
+ * Find the left block number by looking in the buffer.
+ * Adjust numrecs, sibling pointers.
+ */
+ lbno = XFS_DADDR_TO_AGBNO(cur->bc_mp, XFS_BUF_ADDR(lbp));
+ INT_MOD(left->bb_numrecs, ARCH_CONVERT, -(INT_GET(right->bb_numrecs, ARCH_CONVERT)));
+ right->bb_rightsib = left->bb_rightsib; /* INT_: direct copy */
+ INT_SET(left->bb_rightsib, ARCH_CONVERT, rbno);
+ INT_SET(right->bb_leftsib, ARCH_CONVERT, lbno);
+ xfs_alloc_log_block(cur->bc_tp, rbp, XFS_BB_ALL_BITS);
+ xfs_alloc_log_block(cur->bc_tp, lbp, XFS_BB_NUMRECS | XFS_BB_RIGHTSIB);
+ /*
+ * If there's a block to the new block's right, make that block
+ * point back to right instead of to left.
+ */
+ if (INT_GET(right->bb_rightsib, ARCH_CONVERT) != NULLAGBLOCK) {
+ xfs_alloc_block_t *rrblock; /* rr btree block */
+ xfs_buf_t *rrbp; /* buffer for rrblock */
+
+ if ((error = xfs_btree_read_bufs(cur->bc_mp, cur->bc_tp,
+ cur->bc_private.a.agno, INT_GET(right->bb_rightsib, ARCH_CONVERT), 0,
+ &rrbp, XFS_ALLOC_BTREE_REF)))
+ return error;
+ rrblock = XFS_BUF_TO_ALLOC_BLOCK(rrbp);
+ if ((error = xfs_btree_check_sblock(cur, rrblock, level, rrbp)))
+ return error;
+ INT_SET(rrblock->bb_leftsib, ARCH_CONVERT, rbno);
+ xfs_alloc_log_block(cur->bc_tp, rrbp, XFS_BB_LEFTSIB);
+ }
+ /*
+ * If the cursor is really in the right block, move it there.
+ * If it's just pointing past the last entry in left, then we'll
+ * insert there, so don't change anything in that case.
+ */
+ if (cur->bc_ptrs[level] > INT_GET(left->bb_numrecs, ARCH_CONVERT) + 1) {
+ xfs_btree_setbuf(cur, level, rbp);
+ cur->bc_ptrs[level] -= INT_GET(left->bb_numrecs, ARCH_CONVERT);
+ }
+ /*
+ * If there are more levels, we'll need another cursor which refers to
+ * the right block, no matter where this cursor was.
+ */
+ if (level + 1 < cur->bc_nlevels) {
+ if ((error = xfs_btree_dup_cursor(cur, curp)))
+ return error;
+ (*curp)->bc_ptrs[level + 1]++;
+ }
+ *bnop = rbno;
+ *stat = 1;
+ return 0;
+}
+
+/*
+ * Update keys at all levels from here to the root along the cursor's path.
+ */
+STATIC int /* error */
+xfs_alloc_updkey(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ xfs_alloc_key_t *keyp, /* new key value to update to */
+ int level) /* starting level for update */
+{
+ int ptr; /* index of key in block */
+
+ /*
+ * Go up the tree from this level toward the root.
+ * At each level, update the key value to the value input.
+ * Stop when we reach a level where the cursor isn't pointing
+ * at the first entry in the block.
+ */
+ for (ptr = 1; ptr == 1 && level < cur->bc_nlevels; level++) {
+ xfs_alloc_block_t *block; /* btree block */
+ xfs_buf_t *bp; /* buffer for block */
+#ifdef DEBUG
+ int error; /* error return value */
+#endif
+ xfs_alloc_key_t *kp; /* ptr to btree block keys */
+
+ bp = cur->bc_bufs[level];
+ block = XFS_BUF_TO_ALLOC_BLOCK(bp);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_sblock(cur, block, level, bp)))
+ return error;
+#endif
+ ptr = cur->bc_ptrs[level];
+ kp = XFS_ALLOC_KEY_ADDR(block, ptr, cur);
+ *kp = *keyp;
+ xfs_alloc_log_keys(cur, bp, ptr, ptr);
+ }
+ return 0;
+}
+
+/*
+ * Externally visible routines.
+ */
+
+/*
+ * Decrement cursor by one record at the level.
+ * For nonzero levels the leaf-ward information is untouched.
+ */
+int /* error */
+xfs_alloc_decrement(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ int level, /* level in btree, 0 is leaf */
+ int *stat) /* success/failure */
+{
+ xfs_alloc_block_t *block; /* btree block */
+ int error; /* error return value */
+ int lev; /* btree level */
+
+ ASSERT(level < cur->bc_nlevels);
+ /*
+ * Read-ahead to the left at this level.
+ */
+ xfs_btree_readahead(cur, level, XFS_BTCUR_LEFTRA);
+ /*
+ * Decrement the ptr at this level. If we're still in the block
+ * then we're done.
+ */
+ if (--cur->bc_ptrs[level] > 0) {
+ *stat = 1;
+ return 0;
+ }
+ /*
+ * Get a pointer to the btree block.
+ */
+ block = XFS_BUF_TO_ALLOC_BLOCK(cur->bc_bufs[level]);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_sblock(cur, block, level,
+ cur->bc_bufs[level])))
+ return error;
+#endif
+ /*
+ * If we just went off the left edge of the tree, return failure.
+ */
+ if (INT_GET(block->bb_leftsib, ARCH_CONVERT) == NULLAGBLOCK) {
+ *stat = 0;
+ return 0;
+ }
+ /*
+ * March up the tree decrementing pointers.
+ * Stop when we don't go off the left edge of a block.
+ */
+ for (lev = level + 1; lev < cur->bc_nlevels; lev++) {
+ if (--cur->bc_ptrs[lev] > 0)
+ break;
+ /*
+ * Read-ahead the left block, we're going to read it
+ * in the next loop.
+ */
+ xfs_btree_readahead(cur, lev, XFS_BTCUR_LEFTRA);
+ }
+ /*
+ * If we went off the root then we are seriously confused.
+ */
+ ASSERT(lev < cur->bc_nlevels);
+ /*
+ * Now walk back down the tree, fixing up the cursor's buffer
+ * pointers and key numbers.
+ */
+ for (block = XFS_BUF_TO_ALLOC_BLOCK(cur->bc_bufs[lev]); lev > level; ) {
+ xfs_agblock_t agbno; /* block number of btree block */
+ xfs_buf_t *bp; /* buffer pointer for block */
+
+ agbno = INT_GET(*XFS_ALLOC_PTR_ADDR(block, cur->bc_ptrs[lev], cur), ARCH_CONVERT);
+ if ((error = xfs_btree_read_bufs(cur->bc_mp, cur->bc_tp,
+ cur->bc_private.a.agno, agbno, 0, &bp,
+ XFS_ALLOC_BTREE_REF)))
+ return error;
+ lev--;
+ xfs_btree_setbuf(cur, lev, bp);
+ block = XFS_BUF_TO_ALLOC_BLOCK(bp);
+ if ((error = xfs_btree_check_sblock(cur, block, lev, bp)))
+ return error;
+ cur->bc_ptrs[lev] = INT_GET(block->bb_numrecs, ARCH_CONVERT);
+ }
+ *stat = 1;
+ return 0;
+}
+
+/*
+ * Delete the record pointed to by cur.
+ * The cursor refers to the place where the record was (could be inserted)
+ * when the operation returns.
+ */
+int /* error */
+xfs_alloc_delete(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ int *stat) /* success/failure */
+{
+ int error; /* error return value */
+ int i; /* result code */
+ int level; /* btree level */
+
+ /*
+ * Go up the tree, starting at leaf level.
+ * If 2 is returned then a join was done; go to the next level.
+ * Otherwise we are done.
+ */
+ for (level = 0, i = 2; i == 2; level++) {
+ if ((error = xfs_alloc_delrec(cur, level, &i)))
+ return error;
+ }
+ if (i == 0) {
+ for (level = 1; level < cur->bc_nlevels; level++) {
+ if (cur->bc_ptrs[level] == 0) {
+ if ((error = xfs_alloc_decrement(cur, level, &i)))
+ return error;
+ break;
+ }
+ }
+ }
+ *stat = i;
+ return 0;
+}
+
+/*
+ * Get the data from the pointed-to record.
+ */
+int /* error */
+xfs_alloc_get_rec(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ xfs_agblock_t *bno, /* output: starting block of extent */
+ xfs_extlen_t *len, /* output: length of extent */
+ int *stat) /* output: success/failure */
+{
+ xfs_alloc_block_t *block; /* btree block */
+#ifdef DEBUG
+ int error; /* error return value */
+#endif
+ int ptr; /* record number */
+
+ ptr = cur->bc_ptrs[0];
+ block = XFS_BUF_TO_ALLOC_BLOCK(cur->bc_bufs[0]);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_sblock(cur, block, 0, cur->bc_bufs[0])))
+ return error;
+#endif
+ /*
+ * Off the right end or left end, return failure.
+ */
+ if (ptr > INT_GET(block->bb_numrecs, ARCH_CONVERT) || ptr <= 0) {
+ *stat = 0;
+ return 0;
+ }
+ /*
+ * Point to the record and extract its data.
+ */
+ {
+ xfs_alloc_rec_t *rec; /* record data */
+
+ rec = XFS_ALLOC_REC_ADDR(block, ptr, cur);
+ *bno = INT_GET(rec->ar_startblock, ARCH_CONVERT);
+ *len = INT_GET(rec->ar_blockcount, ARCH_CONVERT);
+ }
+ *stat = 1;
+ return 0;
+}
+
+/*
+ * Increment cursor by one record at the level.
+ * For nonzero levels the leaf-ward information is untouched.
+ */
+int /* error */
+xfs_alloc_increment(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ int level, /* level in btree, 0 is leaf */
+ int *stat) /* success/failure */
+{
+ xfs_alloc_block_t *block; /* btree block */
+ xfs_buf_t *bp; /* tree block buffer */
+ int error; /* error return value */
+ int lev; /* btree level */
+
+ ASSERT(level < cur->bc_nlevels);
+ /*
+ * Read-ahead to the right at this level.
+ */
+ xfs_btree_readahead(cur, level, XFS_BTCUR_RIGHTRA);
+ /*
+ * Get a pointer to the btree block.
+ */
+ bp = cur->bc_bufs[level];
+ block = XFS_BUF_TO_ALLOC_BLOCK(bp);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_sblock(cur, block, level, bp)))
+ return error;
+#endif
+ /*
+ * Increment the ptr at this level. If we're still in the block
+ * then we're done.
+ */
+ if (++cur->bc_ptrs[level] <= INT_GET(block->bb_numrecs, ARCH_CONVERT)) {
+ *stat = 1;
+ return 0;
+ }
+ /*
+ * If we just went off the right edge of the tree, return failure.
+ */
+ if (INT_GET(block->bb_rightsib, ARCH_CONVERT) == NULLAGBLOCK) {
+ *stat = 0;
+ return 0;
+ }
+ /*
+ * March up the tree incrementing pointers.
+ * Stop when we don't go off the right edge of a block.
+ */
+ for (lev = level + 1; lev < cur->bc_nlevels; lev++) {
+ bp = cur->bc_bufs[lev];
+ block = XFS_BUF_TO_ALLOC_BLOCK(bp);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_sblock(cur, block, lev, bp)))
+ return error;
+#endif
+ if (++cur->bc_ptrs[lev] <= INT_GET(block->bb_numrecs, ARCH_CONVERT))
+ break;
+ /*
+ * Read-ahead the right block, we're going to read it
+ * in the next loop.
+ */
+ xfs_btree_readahead(cur, lev, XFS_BTCUR_RIGHTRA);
+ }
+ /*
+ * If we went off the root then we are seriously confused.
+ */
+ ASSERT(lev < cur->bc_nlevels);
+ /*
+ * Now walk back down the tree, fixing up the cursor's buffer
+ * pointers and key numbers.
+ */
+ for (bp = cur->bc_bufs[lev], block = XFS_BUF_TO_ALLOC_BLOCK(bp);
+ lev > level; ) {
+ xfs_agblock_t agbno; /* block number of btree block */
+
+ agbno = INT_GET(*XFS_ALLOC_PTR_ADDR(block, cur->bc_ptrs[lev], cur), ARCH_CONVERT);
+ if ((error = xfs_btree_read_bufs(cur->bc_mp, cur->bc_tp,
+ cur->bc_private.a.agno, agbno, 0, &bp,
+ XFS_ALLOC_BTREE_REF)))
+ return error;
+ lev--;
+ xfs_btree_setbuf(cur, lev, bp);
+ block = XFS_BUF_TO_ALLOC_BLOCK(bp);
+ if ((error = xfs_btree_check_sblock(cur, block, lev, bp)))
+ return error;
+ cur->bc_ptrs[lev] = 1;
+ }
+ *stat = 1;
+ return 0;
+}
+
+/*
+ * Insert the current record at the point referenced by cur.
+ * The cursor may be inconsistent on return if splits have been done.
+ */
+int /* error */
+xfs_alloc_insert(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ int *stat) /* success/failure */
+{
+ int error; /* error return value */
+ int i; /* result value, 0 for failure */
+ int level; /* current level number in btree */
+ xfs_agblock_t nbno; /* new block number (split result) */
+ xfs_btree_cur_t *ncur; /* new cursor (split result) */
+ xfs_alloc_rec_t nrec; /* record being inserted this level */
+ xfs_btree_cur_t *pcur; /* previous level's cursor */
+
+ level = 0;
+ nbno = NULLAGBLOCK;
+ INT_SET(nrec.ar_startblock, ARCH_CONVERT, cur->bc_rec.a.ar_startblock);
+ INT_SET(nrec.ar_blockcount, ARCH_CONVERT, cur->bc_rec.a.ar_blockcount);
+ ncur = (xfs_btree_cur_t *)0;
+ pcur = cur;
+ /*
+ * Loop going up the tree, starting at the leaf level.
+ * Stop when we don't get a split block, that must mean that
+ * the insert is finished with this level.
+ */
+ do {
+ /*
+ * Insert nrec/nbno into this level of the tree.
+ * Note if we fail, nbno will be null.
+ */
+ if ((error = xfs_alloc_insrec(pcur, level++, &nbno, &nrec, &ncur,
+ &i))) {
+ if (pcur != cur)
+ xfs_btree_del_cursor(pcur, XFS_BTREE_ERROR);
+ return error;
+ }
+ /*
+ * See if the cursor we just used is trash.
+ * Can't trash the caller's cursor, but otherwise we should
+ * if ncur is a new cursor or we're about to be done.
+ */
+ if (pcur != cur && (ncur || nbno == NULLAGBLOCK)) {
+ cur->bc_nlevels = pcur->bc_nlevels;
+ xfs_btree_del_cursor(pcur, XFS_BTREE_NOERROR);
+ }
+ /*
+ * If we got a new cursor, switch to it.
+ */
+ if (ncur) {
+ pcur = ncur;
+ ncur = (xfs_btree_cur_t *)0;
+ }
+ } while (nbno != NULLAGBLOCK);
+ *stat = i;
+ return 0;
+}
+
+/*
+ * Lookup the record equal to [bno, len] in the btree given by cur.
+ */
+int /* error */
+xfs_alloc_lookup_eq(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ xfs_agblock_t bno, /* starting block of extent */
+ xfs_extlen_t len, /* length of extent */
+ int *stat) /* success/failure */
+{
+ cur->bc_rec.a.ar_startblock = bno;
+ cur->bc_rec.a.ar_blockcount = len;
+ return xfs_alloc_lookup(cur, XFS_LOOKUP_EQ, stat);
+}
+
+/*
+ * Lookup the first record greater than or equal to [bno, len]
+ * in the btree given by cur.
+ */
+int /* error */
+xfs_alloc_lookup_ge(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ xfs_agblock_t bno, /* starting block of extent */
+ xfs_extlen_t len, /* length of extent */
+ int *stat) /* success/failure */
+{
+ cur->bc_rec.a.ar_startblock = bno;
+ cur->bc_rec.a.ar_blockcount = len;
+ return xfs_alloc_lookup(cur, XFS_LOOKUP_GE, stat);
+}
+
+/*
+ * Lookup the first record less than or equal to [bno, len]
+ * in the btree given by cur.
+ */
+int /* error */
+xfs_alloc_lookup_le(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ xfs_agblock_t bno, /* starting block of extent */
+ xfs_extlen_t len, /* length of extent */
+ int *stat) /* success/failure */
+{
+ cur->bc_rec.a.ar_startblock = bno;
+ cur->bc_rec.a.ar_blockcount = len;
+ return xfs_alloc_lookup(cur, XFS_LOOKUP_LE, stat);
+}
+
+/*
+ * Update the record referred to by cur, to the value given by [bno, len].
+ * This either works (return 0) or gets an EFSCORRUPTED error.
+ */
+int /* error */
+xfs_alloc_update(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ xfs_agblock_t bno, /* starting block of extent */
+ xfs_extlen_t len) /* length of extent */
+{
+ xfs_alloc_block_t *block; /* btree block to update */
+ int error; /* error return value */
+ int ptr; /* current record number (updating) */
+
+ ASSERT(len > 0);
+ /*
+ * Pick up the a.g. freelist struct and the current block.
+ */
+ block = XFS_BUF_TO_ALLOC_BLOCK(cur->bc_bufs[0]);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_sblock(cur, block, 0, cur->bc_bufs[0])))
+ return error;
+#endif
+ /*
+ * Get the address of the rec to be updated.
+ */
+ ptr = cur->bc_ptrs[0];
+ {
+ xfs_alloc_rec_t *rp; /* pointer to updated record */
+
+ rp = XFS_ALLOC_REC_ADDR(block, ptr, cur);
+ /*
+ * Fill in the new contents and log them.
+ */
+ INT_SET(rp->ar_startblock, ARCH_CONVERT, bno);
+ INT_SET(rp->ar_blockcount, ARCH_CONVERT, len);
+ xfs_alloc_log_recs(cur, cur->bc_bufs[0], ptr, ptr);
+ }
+ /*
+ * If it's the by-size btree and it's the last leaf block and
+ * it's the last record... then update the size of the longest
+ * extent in the a.g., which we cache in the a.g. freelist header.
+ */
+ if (cur->bc_btnum == XFS_BTNUM_CNT &&
+ INT_GET(block->bb_rightsib, ARCH_CONVERT) == NULLAGBLOCK &&
+ ptr == INT_GET(block->bb_numrecs, ARCH_CONVERT)) {
+ xfs_agf_t *agf; /* a.g. freespace header */
+ xfs_agnumber_t seqno;
+
+ agf = XFS_BUF_TO_AGF(cur->bc_private.a.agbp);
+ seqno = INT_GET(agf->agf_seqno, ARCH_CONVERT);
+ cur->bc_mp->m_perag[seqno].pagf_longest = len;
+ INT_SET(agf->agf_longest, ARCH_CONVERT, len);
+ xfs_alloc_log_agf(cur->bc_tp, cur->bc_private.a.agbp,
+ XFS_AGF_LONGEST);
+ }
+ /*
+ * Updating first record in leaf. Pass new key value up to our parent.
+ */
+ if (ptr == 1) {
+ xfs_alloc_key_t key; /* key containing [bno, len] */
+
+ INT_SET(key.ar_startblock, ARCH_CONVERT, bno);
+ INT_SET(key.ar_blockcount, ARCH_CONVERT, len);
+ if ((error = xfs_alloc_updkey(cur, &key, 1)))
+ return error;
+ }
+ return 0;
+}
diff --git a/fs/xfs/xfs_alloc_btree.h b/fs/xfs/xfs_alloc_btree.h
new file mode 100644
index 00000000000000..ed5161a572ef68
--- /dev/null
+++ b/fs/xfs/xfs_alloc_btree.h
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_ALLOC_BTREE_H__
+#define __XFS_ALLOC_BTREE_H__
+
+/*
+ * Freespace on-disk structures
+ */
+
+struct xfs_buf;
+struct xfs_btree_cur;
+struct xfs_btree_sblock;
+struct xfs_mount;
+
+/*
+ * There are two on-disk btrees, one sorted by blockno and one sorted
+ * by blockcount and blockno. All blocks look the same to make the code
+ * simpler; if we have time later, we'll make the optimizations.
+ */
+#define XFS_ABTB_MAGIC 0x41425442 /* 'ABTB' for bno tree */
+#define XFS_ABTC_MAGIC 0x41425443 /* 'ABTC' for cnt tree */
+
+/*
+ * Data record/key structure
+ */
+typedef struct xfs_alloc_rec
+{
+ xfs_agblock_t ar_startblock; /* starting block number */
+ xfs_extlen_t ar_blockcount; /* count of free blocks */
+} xfs_alloc_rec_t, xfs_alloc_key_t;
+
+typedef xfs_agblock_t xfs_alloc_ptr_t; /* btree pointer type */
+ /* btree block header type */
+typedef struct xfs_btree_sblock xfs_alloc_block_t;
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BUF_TO_ALLOC_BLOCK)
+xfs_alloc_block_t *xfs_buf_to_alloc_block(struct xfs_buf *bp);
+#define XFS_BUF_TO_ALLOC_BLOCK(bp) xfs_buf_to_alloc_block(bp)
+#else
+#define XFS_BUF_TO_ALLOC_BLOCK(bp) ((xfs_alloc_block_t *)(XFS_BUF_PTR(bp)))
+#endif
+
+/*
+ * Real block structures have a size equal to the disk block size.
+ */
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_ALLOC_BLOCK_SIZE)
+int xfs_alloc_block_size(int lev, struct xfs_btree_cur *cur);
+#define XFS_ALLOC_BLOCK_SIZE(lev,cur) xfs_alloc_block_size(lev,cur)
+#else
+#define XFS_ALLOC_BLOCK_SIZE(lev,cur) (1 << (cur)->bc_blocklog)
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_ALLOC_BLOCK_MAXRECS)
+int xfs_alloc_block_maxrecs(int lev, struct xfs_btree_cur *cur);
+#define XFS_ALLOC_BLOCK_MAXRECS(lev,cur) xfs_alloc_block_maxrecs(lev,cur)
+#else
+#define XFS_ALLOC_BLOCK_MAXRECS(lev,cur) \
+ ((cur)->bc_mp->m_alloc_mxr[lev != 0])
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_ALLOC_BLOCK_MINRECS)
+int xfs_alloc_block_minrecs(int lev, struct xfs_btree_cur *cur);
+#define XFS_ALLOC_BLOCK_MINRECS(lev,cur) xfs_alloc_block_minrecs(lev,cur)
+#else
+#define XFS_ALLOC_BLOCK_MINRECS(lev,cur) \
+ ((cur)->bc_mp->m_alloc_mnr[lev != 0])
+#endif
+
+/*
+ * Minimum and maximum blocksize and sectorsize.
+ * The blocksize upper limit is pretty much arbitrary.
+ * The sectorsize upper limit is due to sizeof(sb_sectsize).
+ */
+#define XFS_MIN_BLOCKSIZE_LOG 9 /* i.e. 512 bytes */
+#define XFS_MAX_BLOCKSIZE_LOG 16 /* i.e. 65536 bytes */
+#define XFS_MIN_BLOCKSIZE (1 << XFS_MIN_BLOCKSIZE_LOG)
+#define XFS_MAX_BLOCKSIZE (1 << XFS_MAX_BLOCKSIZE_LOG)
+#define XFS_MIN_SECTORSIZE_LOG 9 /* i.e. 512 bytes */
+#define XFS_MAX_SECTORSIZE_LOG 15 /* i.e. 32768 bytes */
+#define XFS_MIN_SECTORSIZE (1 << XFS_MIN_SECTORSIZE_LOG)
+#define XFS_MAX_SECTORSIZE (1 << XFS_MAX_SECTORSIZE_LOG)
+
+/*
+ * Block numbers in the AG:
+ * SB is sector 0, AGF is sector 1, AGI is sector 2, AGFL is sector 3.
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BNO_BLOCK)
+xfs_agblock_t xfs_bno_block(struct xfs_mount *mp);
+#define XFS_BNO_BLOCK(mp) xfs_bno_block(mp)
+#else
+#define XFS_BNO_BLOCK(mp) ((xfs_agblock_t)(XFS_AGFL_BLOCK(mp) + 1))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_CNT_BLOCK)
+xfs_agblock_t xfs_cnt_block(struct xfs_mount *mp);
+#define XFS_CNT_BLOCK(mp) xfs_cnt_block(mp)
+#else
+#define XFS_CNT_BLOCK(mp) ((xfs_agblock_t)(XFS_BNO_BLOCK(mp) + 1))
+#endif
+
+/*
+ * Record, key, and pointer address macros for btree blocks.
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_ALLOC_REC_ADDR)
+xfs_alloc_rec_t *xfs_alloc_rec_addr(xfs_alloc_block_t *bb, int i,
+ struct xfs_btree_cur *cur);
+#define XFS_ALLOC_REC_ADDR(bb,i,cur) xfs_alloc_rec_addr(bb,i,cur)
+#else
+#define XFS_ALLOC_REC_ADDR(bb,i,cur) \
+ XFS_BTREE_REC_ADDR(XFS_ALLOC_BLOCK_SIZE(0,cur), xfs_alloc, bb, i, \
+ XFS_ALLOC_BLOCK_MAXRECS(0, cur))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_ALLOC_KEY_ADDR)
+xfs_alloc_key_t *xfs_alloc_key_addr(xfs_alloc_block_t *bb, int i,
+ struct xfs_btree_cur *cur);
+#define XFS_ALLOC_KEY_ADDR(bb,i,cur) xfs_alloc_key_addr(bb,i,cur)
+#else
+#define XFS_ALLOC_KEY_ADDR(bb,i,cur) \
+ XFS_BTREE_KEY_ADDR(XFS_ALLOC_BLOCK_SIZE(1,cur), xfs_alloc, bb, i, \
+ XFS_ALLOC_BLOCK_MAXRECS(1, cur))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_ALLOC_PTR_ADDR)
+xfs_alloc_ptr_t *xfs_alloc_ptr_addr(xfs_alloc_block_t *bb, int i,
+ struct xfs_btree_cur *cur);
+#define XFS_ALLOC_PTR_ADDR(bb,i,cur) xfs_alloc_ptr_addr(bb,i,cur)
+#else
+#define XFS_ALLOC_PTR_ADDR(bb,i,cur) \
+ XFS_BTREE_PTR_ADDR(XFS_ALLOC_BLOCK_SIZE(1,cur), xfs_alloc, bb, i, \
+ XFS_ALLOC_BLOCK_MAXRECS(1, cur))
+#endif
+
+/*
+ * Prototypes for externally visible routines.
+ */
+
+/*
+ * Decrement cursor by one record at the level.
+ * For nonzero levels the leaf-ward information is untouched.
+ */
+int /* error */
+xfs_alloc_decrement(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ int level, /* level in btree, 0 is leaf */
+ int *stat); /* success/failure */
+
+/*
+ * Delete the record pointed to by cur.
+ * The cursor refers to the place where the record was (could be inserted)
+ * when the operation returns.
+ */
+int /* error */
+xfs_alloc_delete(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ int *stat); /* success/failure */
+
+/*
+ * Get the data from the pointed-to record.
+ */
+int /* error */
+xfs_alloc_get_rec(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ xfs_agblock_t *bno, /* output: starting block of extent */
+ xfs_extlen_t *len, /* output: length of extent */
+ int *stat); /* output: success/failure */
+
+/*
+ * Increment cursor by one record at the level.
+ * For nonzero levels the leaf-ward information is untouched.
+ */
+int /* error */
+xfs_alloc_increment(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ int level, /* level in btree, 0 is leaf */
+ int *stat); /* success/failure */
+
+/*
+ * Insert the current record at the point referenced by cur.
+ * The cursor may be inconsistent on return if splits have been done.
+ */
+int /* error */
+xfs_alloc_insert(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ int *stat); /* success/failure */
+
+/*
+ * Lookup the record equal to [bno, len] in the btree given by cur.
+ */
+int /* error */
+xfs_alloc_lookup_eq(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ xfs_agblock_t bno, /* starting block of extent */
+ xfs_extlen_t len, /* length of extent */
+ int *stat); /* success/failure */
+
+/*
+ * Lookup the first record greater than or equal to [bno, len]
+ * in the btree given by cur.
+ */
+int /* error */
+xfs_alloc_lookup_ge(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ xfs_agblock_t bno, /* starting block of extent */
+ xfs_extlen_t len, /* length of extent */
+ int *stat); /* success/failure */
+
+/*
+ * Lookup the first record less than or equal to [bno, len]
+ * in the btree given by cur.
+ */
+int /* error */
+xfs_alloc_lookup_le(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ xfs_agblock_t bno, /* starting block of extent */
+ xfs_extlen_t len, /* length of extent */
+ int *stat); /* success/failure */
+
+/*
+ * Update the record referred to by cur, to the value given by [bno, len].
+ * This either works (return 0) or gets an EFSCORRUPTED error.
+ */
+int /* error */
+xfs_alloc_update(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ xfs_agblock_t bno, /* starting block of extent */
+ xfs_extlen_t len); /* length of extent */
+
+#endif /* __XFS_ALLOC_BTREE_H__ */
diff --git a/fs/xfs/xfs_arch.h b/fs/xfs/xfs_arch.h
new file mode 100644
index 00000000000000..3c7a90bfb0e31e
--- /dev/null
+++ b/fs/xfs/xfs_arch.h
@@ -0,0 +1,276 @@
+/*
+ * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_ARCH_H__
+#define __XFS_ARCH_H__
+
+#ifndef XFS_BIG_INUMS
+# error XFS_BIG_INUMS must be defined true or false
+#endif
+
+#ifdef __KERNEL__
+
+#include <asm/byteorder.h>
+
+#ifdef __LITTLE_ENDIAN
+# define __BYTE_ORDER __LITTLE_ENDIAN
+#endif
+#ifdef __BIG_ENDIAN
+# define __BYTE_ORDER __BIG_ENDIAN
+#endif
+
+#endif /* __KERNEL__ */
+
+/* do we need conversion? */
+
+#define ARCH_NOCONVERT 1
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+# define ARCH_CONVERT 0
+#else
+# define ARCH_CONVERT ARCH_NOCONVERT
+#endif
+
+/* generic swapping macros */
+
+#ifndef HAVE_SWABMACROS
+#define INT_SWAP16(type,var) ((typeof(type))(__swab16((__u16)(var))))
+#define INT_SWAP32(type,var) ((typeof(type))(__swab32((__u32)(var))))
+#define INT_SWAP64(type,var) ((typeof(type))(__swab64((__u64)(var))))
+#endif
+
+#define INT_SWAP(type, var) \
+ ((sizeof(type) == 8) ? INT_SWAP64(type,var) : \
+ ((sizeof(type) == 4) ? INT_SWAP32(type,var) : \
+ ((sizeof(type) == 2) ? INT_SWAP16(type,var) : \
+ (var))))
+
+#define INT_SWAP_UNALIGNED_32(from,to) \
+ { \
+ ((__u8*)(to))[0] = ((__u8*)(from))[3]; \
+ ((__u8*)(to))[1] = ((__u8*)(from))[2]; \
+ ((__u8*)(to))[2] = ((__u8*)(from))[1]; \
+ ((__u8*)(to))[3] = ((__u8*)(from))[0]; \
+ }
+
+#define INT_SWAP_UNALIGNED_64(from,to) \
+ { \
+ INT_SWAP_UNALIGNED_32( ((__u8*)(from)) + 4, ((__u8*)(to))); \
+ INT_SWAP_UNALIGNED_32( ((__u8*)(from)), ((__u8*)(to)) + 4); \
+ }
+
+/*
+ * get and set integers from potentially unaligned locations
+ */
+
+#define INT_GET_UNALIGNED_16_LE(pointer) \
+ ((__u16)((((__u8*)(pointer))[0] ) | (((__u8*)(pointer))[1] << 8 )))
+#define INT_GET_UNALIGNED_16_BE(pointer) \
+ ((__u16)((((__u8*)(pointer))[0] << 8) | (((__u8*)(pointer))[1])))
+#define INT_SET_UNALIGNED_16_LE(pointer,value) \
+ { \
+ ((__u8*)(pointer))[0] = (((value) ) & 0xff); \
+ ((__u8*)(pointer))[1] = (((value) >> 8) & 0xff); \
+ }
+#define INT_SET_UNALIGNED_16_BE(pointer,value) \
+ { \
+ ((__u8*)(pointer))[0] = (((value) >> 8) & 0xff); \
+ ((__u8*)(pointer))[1] = (((value) ) & 0xff); \
+ }
+
+#define INT_GET_UNALIGNED_32_LE(pointer) \
+ ((__u32)((((__u8*)(pointer))[0] ) | (((__u8*)(pointer))[1] << 8 ) \
+ |(((__u8*)(pointer))[2] << 16) | (((__u8*)(pointer))[3] << 24)))
+#define INT_GET_UNALIGNED_32_BE(pointer) \
+ ((__u32)((((__u8*)(pointer))[0] << 24) | (((__u8*)(pointer))[1] << 16) \
+ |(((__u8*)(pointer))[2] << 8) | (((__u8*)(pointer))[3] )))
+
+#define INT_GET_UNALIGNED_64_LE(pointer) \
+ (((__u64)(INT_GET_UNALIGNED_32_LE(((__u8*)(pointer))+4)) << 32 ) \
+ |((__u64)(INT_GET_UNALIGNED_32_LE(((__u8*)(pointer)) )) ))
+#define INT_GET_UNALIGNED_64_BE(pointer) \
+ (((__u64)(INT_GET_UNALIGNED_32_BE(((__u8*)(pointer)) )) << 32 ) \
+ |((__u64)(INT_GET_UNALIGNED_32_BE(((__u8*)(pointer))+4)) ))
+
+/*
+ * now pick the right ones for our MACHINE ARCHITECTURE
+ */
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define INT_GET_UNALIGNED_16(pointer) INT_GET_UNALIGNED_16_LE(pointer)
+#define INT_SET_UNALIGNED_16(pointer,value) INT_SET_UNALIGNED_16_LE(pointer,value)
+#define INT_GET_UNALIGNED_32(pointer) INT_GET_UNALIGNED_32_LE(pointer)
+#define INT_GET_UNALIGNED_64(pointer) INT_GET_UNALIGNED_64_LE(pointer)
+#else
+#define INT_GET_UNALIGNED_16(pointer) INT_GET_UNALIGNED_16_BE(pointer)
+#define INT_SET_UNALIGNED_16(pointer,value) INT_SET_UNALIGNED_16_BE(pointer,value)
+#define INT_GET_UNALIGNED_32(pointer) INT_GET_UNALIGNED_32_BE(pointer)
+#define INT_GET_UNALIGNED_64(pointer) INT_GET_UNALIGNED_64_BE(pointer)
+#endif
+
+/* define generic INT_ macros */
+
+#define INT_GET(reference,arch) \
+ (((arch) == ARCH_NOCONVERT) \
+ ? \
+ (reference) \
+ : \
+ INT_SWAP((reference),(reference)) \
+ )
+
+/* does not return a value */
+#define INT_SET(reference,arch,valueref) \
+ (__builtin_constant_p(valueref) ? \
+ (void)( (reference) = ( ((arch) != ARCH_NOCONVERT) ? (INT_SWAP((reference),(valueref))) : (valueref)) ) : \
+ (void)( \
+ ((reference) = (valueref)), \
+ ( ((arch) != ARCH_NOCONVERT) ? (reference) = INT_SWAP((reference),(reference)) : 0 ) \
+ ) \
+ )
+
+/* does not return a value */
+#define INT_MOD_EXPR(reference,arch,code) \
+ (((arch) == ARCH_NOCONVERT) \
+ ? \
+ (void)((reference) code) \
+ : \
+ (void)( \
+ (reference) = INT_GET((reference),arch) , \
+ ((reference) code), \
+ INT_SET(reference, arch, reference) \
+ ) \
+ )
+
+/* does not return a value */
+#define INT_MOD(reference,arch,delta) \
+ (void)( \
+ INT_MOD_EXPR(reference,arch,+=(delta)) \
+ )
+
+/*
+ * INT_COPY - copy a value between two locations with the
+ * _same architecture_ but _potentially different sizes_
+ *
+ * if the types of the two parameters are equal or they are
+ * in native architecture, a simple copy is done
+ *
+ * otherwise, architecture conversions are done
+ *
+ */
+
+/* does not return a value */
+#define INT_COPY(dst,src,arch) \
+ ( \
+ ((sizeof(dst) == sizeof(src)) || ((arch) == ARCH_NOCONVERT)) \
+ ? \
+ (void)((dst) = (src)) \
+ : \
+ INT_SET(dst, arch, INT_GET(src, arch)) \
+ )
+
+/*
+ * INT_XLATE - copy a value in either direction between two locations
+ * with different architectures
+ *
+ * dir < 0 - copy from memory to buffer (native to arch)
+ * dir > 0 - copy from buffer to memory (arch to native)
+ */
+
+/* does not return a value */
+#define INT_XLATE(buf,mem,dir,arch) {\
+ ASSERT(dir); \
+ if (dir>0) { \
+ (mem)=INT_GET(buf, arch); \
+ } else { \
+ INT_SET(buf, arch, mem); \
+ } \
+}
+
+#define INT_ISZERO(reference,arch) \
+ ((reference) == 0)
+
+#define INT_ZERO(reference,arch) \
+ ((reference) = 0)
+
+#define INT_GET_UNALIGNED_16_ARCH(pointer,arch) \
+ ( ((arch) == ARCH_NOCONVERT) \
+ ? \
+ (INT_GET_UNALIGNED_16(pointer)) \
+ : \
+ (INT_GET_UNALIGNED_16_BE(pointer)) \
+ )
+#define INT_SET_UNALIGNED_16_ARCH(pointer,value,arch) \
+ if ((arch) == ARCH_NOCONVERT) { \
+ INT_SET_UNALIGNED_16(pointer,value); \
+ } else { \
+ INT_SET_UNALIGNED_16_BE(pointer,value); \
+ }
+
+#define DIRINO4_GET_ARCH(pointer,arch) \
+ ( ((arch) == ARCH_NOCONVERT) \
+ ? \
+ (INT_GET_UNALIGNED_32(pointer)) \
+ : \
+ (INT_GET_UNALIGNED_32_BE(pointer)) \
+ )
+
+#if XFS_BIG_INUMS
+#define DIRINO_GET_ARCH(pointer,arch) \
+ ( ((arch) == ARCH_NOCONVERT) \
+ ? \
+ (INT_GET_UNALIGNED_64(pointer)) \
+ : \
+ (INT_GET_UNALIGNED_64_BE(pointer)) \
+ )
+#else
+/* MACHINE ARCHITECTURE dependent */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define DIRINO_GET_ARCH(pointer,arch) \
+ DIRINO4_GET_ARCH((((__u8*)pointer)+4),arch)
+#else
+#define DIRINO_GET_ARCH(pointer,arch) \
+ DIRINO4_GET_ARCH(pointer,arch)
+#endif
+#endif
+
+#define DIRINO_COPY_ARCH(from,to,arch) \
+ if ((arch) == ARCH_NOCONVERT) { \
+ memcpy(to,from,sizeof(xfs_ino_t)); \
+ } else { \
+ INT_SWAP_UNALIGNED_64(from,to); \
+ }
+#define DIRINO4_COPY_ARCH(from,to,arch) \
+ if ((arch) == ARCH_NOCONVERT) { \
+ memcpy(to,(((__u8*)from+4)),sizeof(xfs_dir2_ino4_t)); \
+ } else { \
+ INT_SWAP_UNALIGNED_32(from,to); \
+ }
+
+#endif /* __XFS_ARCH_H__ */
diff --git a/fs/xfs/xfs_attr.c b/fs/xfs/xfs_attr.c
new file mode 100644
index 00000000000000..6cd3b58467b1aa
--- /dev/null
+++ b/fs/xfs/xfs_attr.c
@@ -0,0 +1,2660 @@
+/*
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_alloc.h"
+#include "xfs_btree.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode_item.h"
+#include "xfs_inode.h"
+#include "xfs_bmap.h"
+#include "xfs_da_btree.h"
+#include "xfs_attr.h"
+#include "xfs_attr_leaf.h"
+#include "xfs_error.h"
+#include "xfs_bit.h"
+#include "xfs_quota.h"
+#include "xfs_rw.h"
+#include "xfs_trans_space.h"
+#include "xfs_acl.h"
+
+/*
+ * xfs_attr.c
+ *
+ * Provide the external interfaces to manage attribute lists.
+ */
+
+/*========================================================================
+ * Function prototypes for the kernel.
+ *========================================================================*/
+
+/*
+ * Internal routines when attribute list fits inside the inode.
+ */
+STATIC int xfs_attr_shortform_addname(xfs_da_args_t *args);
+
+/*
+ * Internal routines when attribute list is one block.
+ */
+STATIC int xfs_attr_leaf_addname(xfs_da_args_t *args);
+STATIC int xfs_attr_leaf_removename(xfs_da_args_t *args);
+STATIC int xfs_attr_leaf_list(xfs_attr_list_context_t *context);
+
+/*
+ * Internal routines when attribute list is more than one block.
+ */
+STATIC int xfs_attr_node_addname(xfs_da_args_t *args);
+STATIC int xfs_attr_node_removename(xfs_da_args_t *args);
+STATIC int xfs_attr_node_list(xfs_attr_list_context_t *context);
+STATIC int xfs_attr_fillstate(xfs_da_state_t *state);
+STATIC int xfs_attr_refillstate(xfs_da_state_t *state);
+
+/*
+ * Routines to manipulate out-of-line attribute values.
+ */
+STATIC int xfs_attr_rmtval_get(xfs_da_args_t *args);
+STATIC int xfs_attr_rmtval_set(xfs_da_args_t *args);
+STATIC int xfs_attr_rmtval_remove(xfs_da_args_t *args);
+
+#define ATTR_RMTVALUE_MAPSIZE 1 /* # of map entries at once */
+
+#if defined(XFS_ATTR_TRACE)
+ktrace_t *xfs_attr_trace_buf;
+#endif
+
+
+/*========================================================================
+ * Overall external interface routines.
+ *========================================================================*/
+
+int
+xfs_attr_fetch(xfs_inode_t *ip, char *name, int namelen,
+ char *value, int *valuelenp, int flags, struct cred *cred)
+{
+ xfs_da_args_t args;
+ int error;
+
+ if ((XFS_IFORK_Q(ip) == 0) ||
+ (ip->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS &&
+ ip->i_d.di_anextents == 0))
+ return(ENOATTR);
+
+ if (!(flags & (ATTR_KERNACCESS|ATTR_SECURE))) {
+ if ((error = xfs_iaccess(ip, S_IRUSR, cred)))
+ return(XFS_ERROR(error));
+ }
+
+ /*
+ * Fill in the arg structure for this request.
+ */
+ memset((char *)&args, 0, sizeof(args));
+ args.name = name;
+ args.namelen = namelen;
+ args.value = value;
+ args.valuelen = *valuelenp;
+ args.flags = flags;
+ args.hashval = xfs_da_hashname(args.name, args.namelen);
+ args.dp = ip;
+ args.whichfork = XFS_ATTR_FORK;
+
+ /*
+ * Decide on what work routines to call based on the inode size.
+ */
+ if (XFS_IFORK_Q(ip) == 0 ||
+ (ip->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS &&
+ ip->i_d.di_anextents == 0)) {
+ error = XFS_ERROR(ENOATTR);
+ } else if (ip->i_d.di_aformat == XFS_DINODE_FMT_LOCAL) {
+ error = xfs_attr_shortform_getvalue(&args);
+ } else if (xfs_bmap_one_block(ip, XFS_ATTR_FORK)) {
+ error = xfs_attr_leaf_get(&args);
+ } else {
+ error = xfs_attr_node_get(&args);
+ }
+
+ /*
+ * Return the number of bytes in the value to the caller.
+ */
+ *valuelenp = args.valuelen;
+
+ if (error == EEXIST)
+ error = 0;
+ return(error);
+}
+
+int
+xfs_attr_get(bhv_desc_t *bdp, char *name, char *value, int *valuelenp,
+ int flags, struct cred *cred)
+{
+ xfs_inode_t *ip = XFS_BHVTOI(bdp);
+ int error, namelen;
+
+ XFS_STATS_INC(xs_attr_get);
+
+ if (!name)
+ return(EINVAL);
+ namelen = strlen(name);
+ if (namelen >= MAXNAMELEN)
+ return(EFAULT); /* match IRIX behaviour */
+
+ if (XFS_FORCED_SHUTDOWN(ip->i_mount))
+ return(EIO);
+
+ xfs_ilock(ip, XFS_ILOCK_SHARED);
+ error = xfs_attr_fetch(ip, name, namelen, value, valuelenp, flags, cred);
+ xfs_iunlock(ip, XFS_ILOCK_SHARED);
+ return(error);
+}
+
+/*ARGSUSED*/
+int /* error */
+xfs_attr_set(bhv_desc_t *bdp, char *name, char *value, int valuelen, int flags,
+ struct cred *cred)
+{
+ xfs_da_args_t args;
+ xfs_inode_t *dp;
+ xfs_fsblock_t firstblock;
+ xfs_bmap_free_t flist;
+ int error, err2, committed;
+ int local, size;
+ uint nblks;
+ xfs_mount_t *mp;
+ int rsvd = (flags & ATTR_ROOT) != 0;
+ int namelen;
+
+ namelen = strlen(name);
+ if (namelen >= MAXNAMELEN)
+ return EFAULT; /* match IRIX behaviour */
+
+ XFS_STATS_INC(xs_attr_set);
+
+ dp = XFS_BHVTOI(bdp);
+ mp = dp->i_mount;
+ if (XFS_FORCED_SHUTDOWN(mp))
+ return (EIO);
+
+ xfs_ilock(dp, XFS_ILOCK_SHARED);
+ if (!(flags & ATTR_SECURE) &&
+ (error = xfs_iaccess(dp, S_IWUSR, cred))) {
+ xfs_iunlock(dp, XFS_ILOCK_SHARED);
+ return(XFS_ERROR(error));
+ }
+ xfs_iunlock(dp, XFS_ILOCK_SHARED);
+
+ /*
+ * Attach the dquots to the inode.
+ */
+ if ((error = XFS_QM_DQATTACH(mp, dp, 0)))
+ return (error);
+
+ /*
+ * If the inode doesn't have an attribute fork, add one.
+ * (inode must not be locked when we call this routine)
+ */
+ if (XFS_IFORK_Q(dp) == 0) {
+ error = xfs_bmap_add_attrfork(dp, rsvd);
+ if (error)
+ return(error);
+ }
+
+ /*
+ * Fill in the arg structure for this request.
+ */
+ memset((char *)&args, 0, sizeof(args));
+ args.name = name;
+ args.namelen = namelen;
+ args.value = value;
+ args.valuelen = valuelen;
+ args.flags = flags;
+ args.hashval = xfs_da_hashname(args.name, args.namelen);
+ args.dp = dp;
+ args.firstblock = &firstblock;
+ args.flist = &flist;
+ args.whichfork = XFS_ATTR_FORK;
+ args.oknoent = 1;
+
+ /* Determine space new attribute will use, and if it will be inline
+ * or out of line.
+ */
+ size = xfs_attr_leaf_newentsize(&args, mp->m_sb.sb_blocksize, &local);
+
+ nblks = XFS_DAENTER_SPACE_RES(mp, XFS_ATTR_FORK);
+ if (local) {
+ if (size > (mp->m_sb.sb_blocksize >> 1)) {
+ /* Double split possible */
+ nblks <<= 1;
+ }
+ } else {
+ uint dblocks = XFS_B_TO_FSB(mp, valuelen);
+ /* Out of line attribute, cannot double split, but make
+ * room for the attribute value itself.
+ */
+ nblks += dblocks;
+ nblks += XFS_NEXTENTADD_SPACE_RES(mp, dblocks, XFS_ATTR_FORK);
+ }
+
+ /* Size is now blocks for attribute data */
+ args.total = nblks;
+
+ /*
+ * Start our first transaction of the day.
+ *
+ * All future transactions during this code must be "chained" off
+ * this one via the trans_dup() call. All transactions will contain
+ * the inode, and the inode will always be marked with trans_ihold().
+ * Since the inode will be locked in all transactions, we must log
+ * the inode in every transaction to let it float upward through
+ * the log.
+ */
+ args.trans = xfs_trans_alloc(mp, XFS_TRANS_ATTR_SET);
+
+ /*
+ * Root fork attributes can use reserved data blocks for this
+ * operation if necessary
+ */
+
+ if (rsvd)
+ args.trans->t_flags |= XFS_TRANS_RESERVE;
+
+ if ((error = xfs_trans_reserve(args.trans, (uint) nblks,
+ XFS_ATTRSET_LOG_RES(mp, nblks),
+ 0, XFS_TRANS_PERM_LOG_RES,
+ XFS_ATTRSET_LOG_COUNT))) {
+ xfs_trans_cancel(args.trans, 0);
+ return(error);
+ }
+ xfs_ilock(dp, XFS_ILOCK_EXCL);
+
+ error = XFS_TRANS_RESERVE_QUOTA_NBLKS(mp, args.trans, dp, nblks, 0,
+ rsvd ? XFS_QMOPT_RES_REGBLKS | XFS_QMOPT_FORCE_RES :
+ XFS_QMOPT_RES_REGBLKS);
+ if (error) {
+ xfs_iunlock(dp, XFS_ILOCK_EXCL);
+ xfs_trans_cancel(args.trans, XFS_TRANS_RELEASE_LOG_RES);
+ return (error);
+ }
+
+ xfs_trans_ijoin(args.trans, dp, XFS_ILOCK_EXCL);
+ xfs_trans_ihold(args.trans, dp);
+
+ /*
+ * If the attribute list is non-existant or a shortform list,
+ * upgrade it to a single-leaf-block attribute list.
+ */
+ if ((dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL) ||
+ ((dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS) &&
+ (dp->i_d.di_anextents == 0))) {
+
+ /*
+ * Build initial attribute list (if required).
+ */
+ if (dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS)
+ (void)xfs_attr_shortform_create(&args);
+
+ /*
+ * Try to add the attr to the attribute list in
+ * the inode.
+ */
+ error = xfs_attr_shortform_addname(&args);
+ if (error != ENOSPC) {
+ /*
+ * Commit the shortform mods, and we're done.
+ * NOTE: this is also the error path (EEXIST, etc).
+ */
+ ASSERT(args.trans != NULL);
+
+ /*
+ * If this is a synchronous mount, make sure that
+ * the transaction goes to disk before returning
+ * to the user.
+ */
+ if (mp->m_flags & XFS_MOUNT_WSYNC) {
+ xfs_trans_set_sync(args.trans);
+ }
+ err2 = xfs_trans_commit(args.trans,
+ XFS_TRANS_RELEASE_LOG_RES,
+ NULL);
+ xfs_iunlock(dp, XFS_ILOCK_EXCL);
+
+ /*
+ * Hit the inode change time.
+ */
+ if (!error && (flags & ATTR_KERNOTIME) == 0) {
+ xfs_ichgtime(dp, XFS_ICHGTIME_CHG);
+ }
+ return(error == 0 ? err2 : error);
+ }
+
+ /*
+ * It won't fit in the shortform, transform to a leaf block.
+ * GROT: another possible req'mt for a double-split btree op.
+ */
+ XFS_BMAP_INIT(args.flist, args.firstblock);
+ error = xfs_attr_shortform_to_leaf(&args);
+ if (!error) {
+ error = xfs_bmap_finish(&args.trans, args.flist,
+ *args.firstblock, &committed);
+ }
+ if (error) {
+ ASSERT(committed);
+ args.trans = NULL;
+ xfs_bmap_cancel(&flist);
+ goto out;
+ }
+
+ /*
+ * bmap_finish() may have committed the last trans and started
+ * a new one. We need the inode to be in all transactions.
+ */
+ if (committed) {
+ xfs_trans_ijoin(args.trans, dp, XFS_ILOCK_EXCL);
+ xfs_trans_ihold(args.trans, dp);
+ }
+
+ /*
+ * Commit the leaf transformation. We'll need another (linked)
+ * transaction to add the new attribute to the leaf.
+ */
+ if ((error = xfs_attr_rolltrans(&args.trans, dp)))
+ goto out;
+
+ }
+
+ if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
+ error = xfs_attr_leaf_addname(&args);
+ } else {
+ error = xfs_attr_node_addname(&args);
+ }
+ if (error) {
+ goto out;
+ }
+
+ /*
+ * If this is a synchronous mount, make sure that the
+ * transaction goes to disk before returning to the user.
+ */
+ if (mp->m_flags & XFS_MOUNT_WSYNC) {
+ xfs_trans_set_sync(args.trans);
+ }
+
+ /*
+ * Commit the last in the sequence of transactions.
+ */
+ xfs_trans_log_inode(args.trans, dp, XFS_ILOG_CORE);
+ error = xfs_trans_commit(args.trans, XFS_TRANS_RELEASE_LOG_RES,
+ NULL);
+ xfs_iunlock(dp, XFS_ILOCK_EXCL);
+
+ /*
+ * Hit the inode change time.
+ */
+ if (!error && (flags & ATTR_KERNOTIME) == 0) {
+ xfs_ichgtime(dp, XFS_ICHGTIME_CHG);
+ }
+
+ return(error);
+
+out:
+ if (args.trans)
+ xfs_trans_cancel(args.trans,
+ XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_ABORT);
+ xfs_iunlock(dp, XFS_ILOCK_EXCL);
+ return(error);
+}
+
+/*
+ * Generic handler routine to remove a name from an attribute list.
+ * Transitions attribute list from Btree to shortform as necessary.
+ */
+/*ARGSUSED*/
+int /* error */
+xfs_attr_remove(bhv_desc_t *bdp, char *name, int flags, struct cred *cred)
+{
+ xfs_da_args_t args;
+ xfs_inode_t *dp;
+ xfs_fsblock_t firstblock;
+ xfs_bmap_free_t flist;
+ int error;
+ xfs_mount_t *mp;
+ int namelen;
+
+ ASSERT(MAXNAMELEN-1<=0xff); /* length is stored in uint8 */
+ namelen = strlen(name);
+ if (namelen>=MAXNAMELEN)
+ return EFAULT; /* match irix behaviour */
+
+ XFS_STATS_INC(xs_attr_remove);
+
+ dp = XFS_BHVTOI(bdp);
+ mp = dp->i_mount;
+ if (XFS_FORCED_SHUTDOWN(mp))
+ return (EIO);
+
+ xfs_ilock(dp, XFS_ILOCK_SHARED);
+ if (!(flags & ATTR_SECURE) &&
+ (error = xfs_iaccess(dp, S_IWUSR, cred))) {
+ xfs_iunlock(dp, XFS_ILOCK_SHARED);
+ return(XFS_ERROR(error));
+ } else if (XFS_IFORK_Q(dp) == 0 ||
+ (dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS &&
+ dp->i_d.di_anextents == 0)) {
+ xfs_iunlock(dp, XFS_ILOCK_SHARED);
+ return(XFS_ERROR(ENOATTR));
+ }
+ xfs_iunlock(dp, XFS_ILOCK_SHARED);
+
+ /*
+ * Fill in the arg structure for this request.
+ */
+ memset((char *)&args, 0, sizeof(args));
+ args.name = name;
+ args.namelen = namelen;
+ args.flags = flags;
+ args.hashval = xfs_da_hashname(args.name, args.namelen);
+ args.dp = dp;
+ args.firstblock = &firstblock;
+ args.flist = &flist;
+ args.total = 0;
+ args.whichfork = XFS_ATTR_FORK;
+
+ /*
+ * Attach the dquots to the inode.
+ */
+ if ((error = XFS_QM_DQATTACH(mp, dp, 0)))
+ return (error);
+
+ /*
+ * Start our first transaction of the day.
+ *
+ * All future transactions during this code must be "chained" off
+ * this one via the trans_dup() call. All transactions will contain
+ * the inode, and the inode will always be marked with trans_ihold().
+ * Since the inode will be locked in all transactions, we must log
+ * the inode in every transaction to let it float upward through
+ * the log.
+ */
+ args.trans = xfs_trans_alloc(mp, XFS_TRANS_ATTR_RM);
+
+ /*
+ * Root fork attributes can use reserved data blocks for this
+ * operation if necessary
+ */
+
+ if (flags & ATTR_ROOT)
+ args.trans->t_flags |= XFS_TRANS_RESERVE;
+
+ if ((error = xfs_trans_reserve(args.trans,
+ XFS_ATTRRM_SPACE_RES(mp),
+ XFS_ATTRRM_LOG_RES(mp),
+ 0, XFS_TRANS_PERM_LOG_RES,
+ XFS_ATTRRM_LOG_COUNT))) {
+ xfs_trans_cancel(args.trans, 0);
+ return(error);
+
+ }
+
+ xfs_ilock(dp, XFS_ILOCK_EXCL);
+ /*
+ * No need to make quota reservations here. We expect to release some
+ * blocks not allocate in the common case.
+ */
+ xfs_trans_ijoin(args.trans, dp, XFS_ILOCK_EXCL);
+ xfs_trans_ihold(args.trans, dp);
+
+ /*
+ * Decide on what work routines to call based on the inode size.
+ */
+ if (XFS_IFORK_Q(dp) == 0 ||
+ (dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS &&
+ dp->i_d.di_anextents == 0)) {
+ error = XFS_ERROR(ENOATTR);
+ goto out;
+ }
+ if (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL) {
+ ASSERT(dp->i_afp->if_flags & XFS_IFINLINE);
+ error = xfs_attr_shortform_remove(&args);
+ if (error) {
+ goto out;
+ }
+ } else if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
+ error = xfs_attr_leaf_removename(&args);
+ } else {
+ error = xfs_attr_node_removename(&args);
+ }
+ if (error) {
+ goto out;
+ }
+
+ /*
+ * If this is a synchronous mount, make sure that the
+ * transaction goes to disk before returning to the user.
+ */
+ if (mp->m_flags & XFS_MOUNT_WSYNC) {
+ xfs_trans_set_sync(args.trans);
+ }
+
+ /*
+ * Commit the last in the sequence of transactions.
+ */
+ xfs_trans_log_inode(args.trans, dp, XFS_ILOG_CORE);
+ error = xfs_trans_commit(args.trans, XFS_TRANS_RELEASE_LOG_RES,
+ NULL);
+ xfs_iunlock(dp, XFS_ILOCK_EXCL);
+
+ /*
+ * Hit the inode change time.
+ */
+ if (!error && (flags & ATTR_KERNOTIME) == 0) {
+ xfs_ichgtime(dp, XFS_ICHGTIME_CHG);
+ }
+
+ return(error);
+
+out:
+ if (args.trans)
+ xfs_trans_cancel(args.trans,
+ XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_ABORT);
+ xfs_iunlock(dp, XFS_ILOCK_EXCL);
+ return(error);
+}
+
+/*
+ * Generate a list of extended attribute names and optionally
+ * also value lengths. Positive return value follows the XFS
+ * convention of being an error, zero or negative return code
+ * is the length of the buffer returned (negated), indicating
+ * success.
+ */
+int
+xfs_attr_list(bhv_desc_t *bdp, char *buffer, int bufsize, int flags,
+ attrlist_cursor_kern_t *cursor, struct cred *cred)
+{
+ xfs_attr_list_context_t context;
+ xfs_inode_t *dp;
+ int error;
+
+ XFS_STATS_INC(xs_attr_list);
+
+ /*
+ * Validate the cursor.
+ */
+ if (cursor->pad1 || cursor->pad2)
+ return(XFS_ERROR(EINVAL));
+ if ((cursor->initted == 0) &&
+ (cursor->hashval || cursor->blkno || cursor->offset))
+ return(XFS_ERROR(EINVAL));
+
+ /*
+ * Check for a properly aligned buffer.
+ */
+ if (((long)buffer) & (sizeof(int)-1))
+ return(XFS_ERROR(EFAULT));
+ if (flags & ATTR_KERNOVAL)
+ bufsize = 0;
+
+ /*
+ * Initialize the output buffer.
+ */
+ context.dp = dp = XFS_BHVTOI(bdp);
+ context.cursor = cursor;
+ context.count = 0;
+ context.dupcnt = 0;
+ context.resynch = 1;
+ context.flags = flags;
+ if (!(flags & ATTR_KERNAMELS)) {
+ context.bufsize = (bufsize & ~(sizeof(int)-1)); /* align */
+ context.firstu = context.bufsize;
+ context.alist = (attrlist_t *)buffer;
+ context.alist->al_count = 0;
+ context.alist->al_more = 0;
+ context.alist->al_offset[0] = context.bufsize;
+ }
+ else {
+ context.bufsize = bufsize;
+ context.firstu = context.bufsize;
+ context.alist = (attrlist_t *)buffer;
+ }
+
+ if (XFS_FORCED_SHUTDOWN(dp->i_mount))
+ return (EIO);
+
+ xfs_ilock(dp, XFS_ILOCK_SHARED);
+ if (!(flags & ATTR_SECURE) &&
+ (error = xfs_iaccess(dp, S_IRUSR, cred))) {
+ xfs_iunlock(dp, XFS_ILOCK_SHARED);
+ return(XFS_ERROR(error));
+ }
+
+ /*
+ * Decide on what work routines to call based on the inode size.
+ */
+ xfs_attr_trace_l_c("syscall start", &context);
+ if (XFS_IFORK_Q(dp) == 0 ||
+ (dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS &&
+ dp->i_d.di_anextents == 0)) {
+ error = 0;
+ } else if (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL) {
+ error = xfs_attr_shortform_list(&context);
+ } else if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
+ error = xfs_attr_leaf_list(&context);
+ } else {
+ error = xfs_attr_node_list(&context);
+ }
+ xfs_iunlock(dp, XFS_ILOCK_SHARED);
+ xfs_attr_trace_l_c("syscall end", &context);
+
+ if (!(context.flags & (ATTR_KERNOVAL|ATTR_KERNAMELS))) {
+ ASSERT(error >= 0);
+ }
+ else { /* must return negated buffer size or the error */
+ if (context.count < 0)
+ error = XFS_ERROR(ERANGE);
+ else
+ error = -context.count;
+ }
+
+ return(error);
+}
+
+int /* error */
+xfs_attr_inactive(xfs_inode_t *dp)
+{
+ xfs_trans_t *trans;
+ xfs_mount_t *mp;
+ int error;
+
+ mp = dp->i_mount;
+ ASSERT(! XFS_NOT_DQATTACHED(mp, dp));
+
+ xfs_ilock(dp, XFS_ILOCK_SHARED);
+ if ((XFS_IFORK_Q(dp) == 0) ||
+ (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL) ||
+ (dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS &&
+ dp->i_d.di_anextents == 0)) {
+ xfs_iunlock(dp, XFS_ILOCK_SHARED);
+ return(0);
+ }
+ xfs_iunlock(dp, XFS_ILOCK_SHARED);
+
+ /*
+ * Start our first transaction of the day.
+ *
+ * All future transactions during this code must be "chained" off
+ * this one via the trans_dup() call. All transactions will contain
+ * the inode, and the inode will always be marked with trans_ihold().
+ * Since the inode will be locked in all transactions, we must log
+ * the inode in every transaction to let it float upward through
+ * the log.
+ */
+ trans = xfs_trans_alloc(mp, XFS_TRANS_ATTRINVAL);
+ if ((error = xfs_trans_reserve(trans, 0, XFS_ATTRINVAL_LOG_RES(mp), 0,
+ XFS_TRANS_PERM_LOG_RES,
+ XFS_ATTRINVAL_LOG_COUNT))) {
+ xfs_trans_cancel(trans, 0);
+ return(error);
+ }
+ xfs_ilock(dp, XFS_ILOCK_EXCL);
+
+ /*
+ * No need to make quota reservations here. We expect to release some
+ * blocks, not allocate, in the common case.
+ */
+ xfs_trans_ijoin(trans, dp, XFS_ILOCK_EXCL);
+ xfs_trans_ihold(trans, dp);
+
+ /*
+ * Decide on what work routines to call based on the inode size.
+ */
+ if ((XFS_IFORK_Q(dp) == 0) ||
+ (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL) ||
+ (dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS &&
+ dp->i_d.di_anextents == 0)) {
+ error = 0;
+ goto out;
+ }
+ error = xfs_attr_root_inactive(&trans, dp);
+ if (error)
+ goto out;
+ /*
+ * signal synchronous inactive transactions unless this
+ * is a synchronous mount filesystem in which case we
+ * know that we're here because we've been called out of
+ * xfs_inactive which means that the last reference is gone
+ * and the unlink transaction has already hit the disk so
+ * async inactive transactions are safe.
+ */
+ if ((error = xfs_itruncate_finish(&trans, dp, 0LL, XFS_ATTR_FORK,
+ (!(mp->m_flags & XFS_MOUNT_WSYNC)
+ ? 1 : 0))))
+ goto out;
+
+ /*
+ * Commit the last in the sequence of transactions.
+ */
+ xfs_trans_log_inode(trans, dp, XFS_ILOG_CORE);
+ error = xfs_trans_commit(trans, XFS_TRANS_RELEASE_LOG_RES,
+ NULL);
+ xfs_iunlock(dp, XFS_ILOCK_EXCL);
+
+ return(error);
+
+out:
+ xfs_trans_cancel(trans, XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_ABORT);
+ xfs_iunlock(dp, XFS_ILOCK_EXCL);
+ return(error);
+}
+
+
+
+/*========================================================================
+ * External routines when attribute list is inside the inode
+ *========================================================================*/
+
+/*
+ * Add a name to the shortform attribute list structure
+ * This is the external routine.
+ */
+STATIC int
+xfs_attr_shortform_addname(xfs_da_args_t *args)
+{
+ int newsize, retval;
+
+ retval = xfs_attr_shortform_lookup(args);
+ if ((args->flags & ATTR_REPLACE) && (retval == ENOATTR)) {
+ return(retval);
+ } else if (retval == EEXIST) {
+ if (args->flags & ATTR_CREATE)
+ return(retval);
+ retval = xfs_attr_shortform_remove(args);
+ ASSERT(retval == 0);
+ }
+
+ newsize = XFS_ATTR_SF_TOTSIZE(args->dp);
+ newsize += XFS_ATTR_SF_ENTSIZE_BYNAME(args->namelen, args->valuelen);
+ if ((newsize <= XFS_IFORK_ASIZE(args->dp)) &&
+ (args->namelen < XFS_ATTR_SF_ENTSIZE_MAX) &&
+ (args->valuelen < XFS_ATTR_SF_ENTSIZE_MAX)) {
+ retval = xfs_attr_shortform_add(args);
+ ASSERT(retval == 0);
+ } else {
+ return(XFS_ERROR(ENOSPC));
+ }
+ return(0);
+}
+
+
+/*========================================================================
+ * External routines when attribute list is one block
+ *========================================================================*/
+
+/*
+ * Add a name to the leaf attribute list structure
+ *
+ * This leaf block cannot have a "remote" value, we only call this routine
+ * if bmap_one_block() says there is only one block (ie: no remote blks).
+ */
+int
+xfs_attr_leaf_addname(xfs_da_args_t *args)
+{
+ xfs_inode_t *dp;
+ xfs_dabuf_t *bp;
+ int retval, error, committed;
+
+ /*
+ * Read the (only) block in the attribute list in.
+ */
+ dp = args->dp;
+ args->blkno = 0;
+ error = xfs_da_read_buf(args->trans, args->dp, args->blkno, -1, &bp,
+ XFS_ATTR_FORK);
+ if (error)
+ return(error);
+ ASSERT(bp != NULL);
+
+ /*
+ * Look up the given attribute in the leaf block. Figure out if
+ * the given flags produce an error or call for an atomic rename.
+ */
+ retval = xfs_attr_leaf_lookup_int(bp, args);
+ if ((args->flags & ATTR_REPLACE) && (retval == ENOATTR)) {
+ xfs_da_brelse(args->trans, bp);
+ return(retval);
+ } else if (retval == EEXIST) {
+ if (args->flags & ATTR_CREATE) { /* pure create op */
+ xfs_da_brelse(args->trans, bp);
+ return(retval);
+ }
+ args->rename = 1; /* an atomic rename */
+ args->blkno2 = args->blkno; /* set 2nd entry info*/
+ args->index2 = args->index;
+ args->rmtblkno2 = args->rmtblkno;
+ args->rmtblkcnt2 = args->rmtblkcnt;
+ }
+
+ /*
+ * Add the attribute to the leaf block, transitioning to a Btree
+ * if required.
+ */
+ retval = xfs_attr_leaf_add(bp, args);
+ xfs_da_buf_done(bp);
+ if (retval == ENOSPC) {
+ /*
+ * Promote the attribute list to the Btree format, then
+ * Commit that transaction so that the node_addname() call
+ * can manage its own transactions.
+ */
+ XFS_BMAP_INIT(args->flist, args->firstblock);
+ error = xfs_attr_leaf_to_node(args);
+ if (!error) {
+ error = xfs_bmap_finish(&args->trans, args->flist,
+ *args->firstblock, &committed);
+ }
+ if (error) {
+ ASSERT(committed);
+ args->trans = NULL;
+ xfs_bmap_cancel(args->flist);
+ return(error);
+ }
+
+ /*
+ * bmap_finish() may have committed the last trans and started
+ * a new one. We need the inode to be in all transactions.
+ */
+ if (committed) {
+ xfs_trans_ijoin(args->trans, dp, XFS_ILOCK_EXCL);
+ xfs_trans_ihold(args->trans, dp);
+ }
+
+ /*
+ * Commit the current trans (including the inode) and start
+ * a new one.
+ */
+ if ((error = xfs_attr_rolltrans(&args->trans, dp)))
+ return (error);
+
+ /*
+ * Fob the whole rest of the problem off on the Btree code.
+ */
+ error = xfs_attr_node_addname(args);
+ return(error);
+ }
+
+ /*
+ * Commit the transaction that added the attr name so that
+ * later routines can manage their own transactions.
+ */
+ if ((error = xfs_attr_rolltrans(&args->trans, dp)))
+ return (error);
+
+ /*
+ * If there was an out-of-line value, allocate the blocks we
+ * identified for its storage and copy the value. This is done
+ * after we create the attribute so that we don't overflow the
+ * maximum size of a transaction and/or hit a deadlock.
+ */
+ if (args->rmtblkno > 0) {
+ error = xfs_attr_rmtval_set(args);
+ if (error)
+ return(error);
+ }
+
+ /*
+ * If this is an atomic rename operation, we must "flip" the
+ * incomplete flags on the "new" and "old" attribute/value pairs
+ * so that one disappears and one appears atomically. Then we
+ * must remove the "old" attribute/value pair.
+ */
+ if (args->rename) {
+ /*
+ * In a separate transaction, set the incomplete flag on the
+ * "old" attr and clear the incomplete flag on the "new" attr.
+ */
+ error = xfs_attr_leaf_flipflags(args);
+ if (error)
+ return(error);
+
+ /*
+ * Dismantle the "old" attribute/value pair by removing
+ * a "remote" value (if it exists).
+ */
+ args->index = args->index2;
+ args->blkno = args->blkno2;
+ args->rmtblkno = args->rmtblkno2;
+ args->rmtblkcnt = args->rmtblkcnt2;
+ if (args->rmtblkno) {
+ error = xfs_attr_rmtval_remove(args);
+ if (error)
+ return(error);
+ }
+
+ /*
+ * Read in the block containing the "old" attr, then
+ * remove the "old" attr from that block (neat, huh!)
+ */
+ error = xfs_da_read_buf(args->trans, args->dp, args->blkno, -1,
+ &bp, XFS_ATTR_FORK);
+ if (error)
+ return(error);
+ ASSERT(bp != NULL);
+ (void)xfs_attr_leaf_remove(bp, args);
+
+ /*
+ * If the result is small enough, shrink it all into the inode.
+ */
+ if (xfs_attr_shortform_allfit(bp, dp)) {
+ XFS_BMAP_INIT(args->flist, args->firstblock);
+ error = xfs_attr_leaf_to_shortform(bp, args);
+ /* bp is gone due to xfs_da_shrink_inode */
+ if (!error) {
+ error = xfs_bmap_finish(&args->trans,
+ args->flist,
+ *args->firstblock,
+ &committed);
+ }
+ if (error) {
+ ASSERT(committed);
+ args->trans = NULL;
+ xfs_bmap_cancel(args->flist);
+ return(error);
+ }
+
+ /*
+ * bmap_finish() may have committed the last trans
+ * and started a new one. We need the inode to be
+ * in all transactions.
+ */
+ if (committed) {
+ xfs_trans_ijoin(args->trans, dp, XFS_ILOCK_EXCL);
+ xfs_trans_ihold(args->trans, dp);
+ }
+ } else
+ xfs_da_buf_done(bp);
+
+ /*
+ * Commit the remove and start the next trans in series.
+ */
+ error = xfs_attr_rolltrans(&args->trans, dp);
+
+ } else if (args->rmtblkno > 0) {
+ /*
+ * Added a "remote" value, just clear the incomplete flag.
+ */
+ error = xfs_attr_leaf_clearflag(args);
+ }
+ return(error);
+}
+
+/*
+ * Remove a name from the leaf attribute list structure
+ *
+ * This leaf block cannot have a "remote" value, we only call this routine
+ * if bmap_one_block() says there is only one block (ie: no remote blks).
+ */
+STATIC int
+xfs_attr_leaf_removename(xfs_da_args_t *args)
+{
+ xfs_inode_t *dp;
+ xfs_dabuf_t *bp;
+ int committed;
+ int error;
+
+ /*
+ * Remove the attribute.
+ */
+ dp = args->dp;
+ args->blkno = 0;
+ error = xfs_da_read_buf(args->trans, args->dp, args->blkno, -1, &bp,
+ XFS_ATTR_FORK);
+ if (error) {
+ return(error);
+ }
+
+ ASSERT(bp != NULL);
+ error = xfs_attr_leaf_lookup_int(bp, args);
+ if (error == ENOATTR) {
+ xfs_da_brelse(args->trans, bp);
+ return(error);
+ }
+
+ (void)xfs_attr_leaf_remove(bp, args);
+
+ /*
+ * If the result is small enough, shrink it all into the inode.
+ */
+ if (xfs_attr_shortform_allfit(bp, dp)) {
+ XFS_BMAP_INIT(args->flist, args->firstblock);
+ error = xfs_attr_leaf_to_shortform(bp, args);
+ /* bp is gone due to xfs_da_shrink_inode */
+ if (!error) {
+ error = xfs_bmap_finish(&args->trans, args->flist,
+ *args->firstblock, &committed);
+ }
+ if (error) {
+ ASSERT(committed);
+ args->trans = NULL;
+ xfs_bmap_cancel(args->flist);
+ return(error);
+ }
+
+ /*
+ * bmap_finish() may have committed the last trans and started
+ * a new one. We need the inode to be in all transactions.
+ */
+ if (committed) {
+ xfs_trans_ijoin(args->trans, dp, XFS_ILOCK_EXCL);
+ xfs_trans_ihold(args->trans, dp);
+ }
+ } else
+ xfs_da_buf_done(bp);
+ return(0);
+}
+
+/*
+ * Look up a name in a leaf attribute list structure.
+ *
+ * This leaf block cannot have a "remote" value, we only call this routine
+ * if bmap_one_block() says there is only one block (ie: no remote blks).
+ */
+int
+xfs_attr_leaf_get(xfs_da_args_t *args)
+{
+ xfs_dabuf_t *bp;
+ int error;
+
+ args->blkno = 0;
+ error = xfs_da_read_buf(args->trans, args->dp, args->blkno, -1, &bp,
+ XFS_ATTR_FORK);
+ if (error)
+ return(error);
+ ASSERT(bp != NULL);
+
+ error = xfs_attr_leaf_lookup_int(bp, args);
+ if (error != EEXIST) {
+ xfs_da_brelse(args->trans, bp);
+ return(error);
+ }
+ error = xfs_attr_leaf_getvalue(bp, args);
+ xfs_da_brelse(args->trans, bp);
+ if (!error && (args->rmtblkno > 0) && !(args->flags & ATTR_KERNOVAL)) {
+ error = xfs_attr_rmtval_get(args);
+ }
+ return(error);
+}
+
+/*
+ * Copy out attribute entries for attr_list(), for leaf attribute lists.
+ */
+STATIC int
+xfs_attr_leaf_list(xfs_attr_list_context_t *context)
+{
+ xfs_attr_leafblock_t *leaf;
+ int error;
+ xfs_dabuf_t *bp;
+
+ context->cursor->blkno = 0;
+ error = xfs_da_read_buf(NULL, context->dp, 0, -1, &bp, XFS_ATTR_FORK);
+ if (error)
+ return(error);
+ ASSERT(bp != NULL);
+ leaf = bp->data;
+ if (unlikely(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT)
+ != XFS_ATTR_LEAF_MAGIC)) {
+ XFS_CORRUPTION_ERROR("xfs_attr_leaf_list", XFS_ERRLEVEL_LOW,
+ context->dp->i_mount, leaf);
+ xfs_da_brelse(NULL, bp);
+ return(XFS_ERROR(EFSCORRUPTED));
+ }
+
+ (void)xfs_attr_leaf_list_int(bp, context);
+ xfs_da_brelse(NULL, bp);
+ return(0);
+}
+
+
+/*========================================================================
+ * External routines when attribute list size > XFS_LBSIZE(mp).
+ *========================================================================*/
+
+/*
+ * Add a name to a Btree-format attribute list.
+ *
+ * This will involve walking down the Btree, and may involve splitting
+ * leaf nodes and even splitting intermediate nodes up to and including
+ * the root node (a special case of an intermediate node).
+ *
+ * "Remote" attribute values confuse the issue and atomic rename operations
+ * add a whole extra layer of confusion on top of that.
+ */
+STATIC int
+xfs_attr_node_addname(xfs_da_args_t *args)
+{
+ xfs_da_state_t *state;
+ xfs_da_state_blk_t *blk;
+ xfs_inode_t *dp;
+ xfs_mount_t *mp;
+ int committed, retval, error;
+
+ /*
+ * Fill in bucket of arguments/results/context to carry around.
+ */
+ dp = args->dp;
+ mp = dp->i_mount;
+restart:
+ state = xfs_da_state_alloc();
+ state->args = args;
+ state->mp = mp;
+ state->blocksize = state->mp->m_sb.sb_blocksize;
+ state->node_ents = state->mp->m_attr_node_ents;
+
+ /*
+ * Search to see if name already exists, and get back a pointer
+ * to where it should go.
+ */
+ error = xfs_da_node_lookup_int(state, &retval);
+ if (error)
+ goto out;
+ blk = &state->path.blk[ state->path.active-1 ];
+ ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
+ if ((args->flags & ATTR_REPLACE) && (retval == ENOATTR)) {
+ goto out;
+ } else if (retval == EEXIST) {
+ if (args->flags & ATTR_CREATE)
+ goto out;
+ args->rename = 1; /* atomic rename op */
+ args->blkno2 = args->blkno; /* set 2nd entry info*/
+ args->index2 = args->index;
+ args->rmtblkno2 = args->rmtblkno;
+ args->rmtblkcnt2 = args->rmtblkcnt;
+ args->rmtblkno = 0;
+ args->rmtblkcnt = 0;
+ }
+
+ retval = xfs_attr_leaf_add(blk->bp, state->args);
+ if (retval == ENOSPC) {
+ if (state->path.active == 1) {
+ /*
+ * Its really a single leaf node, but it had
+ * out-of-line values so it looked like it *might*
+ * have been a b-tree.
+ */
+ xfs_da_state_free(state);
+ XFS_BMAP_INIT(args->flist, args->firstblock);
+ error = xfs_attr_leaf_to_node(args);
+ if (!error) {
+ error = xfs_bmap_finish(&args->trans,
+ args->flist,
+ *args->firstblock,
+ &committed);
+ }
+ if (error) {
+ ASSERT(committed);
+ args->trans = NULL;
+ xfs_bmap_cancel(args->flist);
+ goto out;
+ }
+
+ /*
+ * bmap_finish() may have committed the last trans
+ * and started a new one. We need the inode to be
+ * in all transactions.
+ */
+ if (committed) {
+ xfs_trans_ijoin(args->trans, dp, XFS_ILOCK_EXCL);
+ xfs_trans_ihold(args->trans, dp);
+ }
+
+ /*
+ * Commit the node conversion and start the next
+ * trans in the chain.
+ */
+ if ((error = xfs_attr_rolltrans(&args->trans, dp)))
+ goto out;
+
+ goto restart;
+ }
+
+ /*
+ * Split as many Btree elements as required.
+ * This code tracks the new and old attr's location
+ * in the index/blkno/rmtblkno/rmtblkcnt fields and
+ * in the index2/blkno2/rmtblkno2/rmtblkcnt2 fields.
+ */
+ XFS_BMAP_INIT(args->flist, args->firstblock);
+ error = xfs_da_split(state);
+ if (!error) {
+ error = xfs_bmap_finish(&args->trans, args->flist,
+ *args->firstblock, &committed);
+ }
+ if (error) {
+ ASSERT(committed);
+ args->trans = NULL;
+ xfs_bmap_cancel(args->flist);
+ goto out;
+ }
+
+ /*
+ * bmap_finish() may have committed the last trans and started
+ * a new one. We need the inode to be in all transactions.
+ */
+ if (committed) {
+ xfs_trans_ijoin(args->trans, dp, XFS_ILOCK_EXCL);
+ xfs_trans_ihold(args->trans, dp);
+ }
+ } else {
+ /*
+ * Addition succeeded, update Btree hashvals.
+ */
+ xfs_da_fixhashpath(state, &state->path);
+ }
+
+ /*
+ * Kill the state structure, we're done with it and need to
+ * allow the buffers to come back later.
+ */
+ xfs_da_state_free(state);
+ state = NULL;
+
+ /*
+ * Commit the leaf addition or btree split and start the next
+ * trans in the chain.
+ */
+ if ((error = xfs_attr_rolltrans(&args->trans, dp)))
+ goto out;
+
+ /*
+ * If there was an out-of-line value, allocate the blocks we
+ * identified for its storage and copy the value. This is done
+ * after we create the attribute so that we don't overflow the
+ * maximum size of a transaction and/or hit a deadlock.
+ */
+ if (args->rmtblkno > 0) {
+ error = xfs_attr_rmtval_set(args);
+ if (error)
+ return(error);
+ }
+
+ /*
+ * If this is an atomic rename operation, we must "flip" the
+ * incomplete flags on the "new" and "old" attribute/value pairs
+ * so that one disappears and one appears atomically. Then we
+ * must remove the "old" attribute/value pair.
+ */
+ if (args->rename) {
+ /*
+ * In a separate transaction, set the incomplete flag on the
+ * "old" attr and clear the incomplete flag on the "new" attr.
+ */
+ error = xfs_attr_leaf_flipflags(args);
+ if (error)
+ goto out;
+
+ /*
+ * Dismantle the "old" attribute/value pair by removing
+ * a "remote" value (if it exists).
+ */
+ args->index = args->index2;
+ args->blkno = args->blkno2;
+ args->rmtblkno = args->rmtblkno2;
+ args->rmtblkcnt = args->rmtblkcnt2;
+ if (args->rmtblkno) {
+ error = xfs_attr_rmtval_remove(args);
+ if (error)
+ return(error);
+ }
+
+ /*
+ * Re-find the "old" attribute entry after any split ops.
+ * The INCOMPLETE flag means that we will find the "old"
+ * attr, not the "new" one.
+ */
+ args->flags |= XFS_ATTR_INCOMPLETE;
+ state = xfs_da_state_alloc();
+ state->args = args;
+ state->mp = mp;
+ state->blocksize = state->mp->m_sb.sb_blocksize;
+ state->node_ents = state->mp->m_attr_node_ents;
+ state->inleaf = 0;
+ error = xfs_da_node_lookup_int(state, &retval);
+ if (error)
+ goto out;
+
+ /*
+ * Remove the name and update the hashvals in the tree.
+ */
+ blk = &state->path.blk[ state->path.active-1 ];
+ ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
+ error = xfs_attr_leaf_remove(blk->bp, args);
+ xfs_da_fixhashpath(state, &state->path);
+
+ /*
+ * Check to see if the tree needs to be collapsed.
+ */
+ if (retval && (state->path.active > 1)) {
+ XFS_BMAP_INIT(args->flist, args->firstblock);
+ error = xfs_da_join(state);
+ if (!error) {
+ error = xfs_bmap_finish(&args->trans,
+ args->flist,
+ *args->firstblock,
+ &committed);
+ }
+ if (error) {
+ ASSERT(committed);
+ args->trans = NULL;
+ xfs_bmap_cancel(args->flist);
+ goto out;
+ }
+
+ /*
+ * bmap_finish() may have committed the last trans
+ * and started a new one. We need the inode to be
+ * in all transactions.
+ */
+ if (committed) {
+ xfs_trans_ijoin(args->trans, dp, XFS_ILOCK_EXCL);
+ xfs_trans_ihold(args->trans, dp);
+ }
+ }
+
+ /*
+ * Commit and start the next trans in the chain.
+ */
+ if ((error = xfs_attr_rolltrans(&args->trans, dp)))
+ goto out;
+
+ } else if (args->rmtblkno > 0) {
+ /*
+ * Added a "remote" value, just clear the incomplete flag.
+ */
+ error = xfs_attr_leaf_clearflag(args);
+ if (error)
+ goto out;
+ }
+ retval = error = 0;
+
+out:
+ if (state)
+ xfs_da_state_free(state);
+ if (error)
+ return(error);
+ return(retval);
+}
+
+/*
+ * Remove a name from a B-tree attribute list.
+ *
+ * This will involve walking down the Btree, and may involve joining
+ * leaf nodes and even joining intermediate nodes up to and including
+ * the root node (a special case of an intermediate node).
+ */
+STATIC int
+xfs_attr_node_removename(xfs_da_args_t *args)
+{
+ xfs_da_state_t *state;
+ xfs_da_state_blk_t *blk;
+ xfs_inode_t *dp;
+ xfs_dabuf_t *bp;
+ int retval, error, committed;
+
+ /*
+ * Tie a string around our finger to remind us where we are.
+ */
+ dp = args->dp;
+ state = xfs_da_state_alloc();
+ state->args = args;
+ state->mp = dp->i_mount;
+ state->blocksize = state->mp->m_sb.sb_blocksize;
+ state->node_ents = state->mp->m_attr_node_ents;
+
+ /*
+ * Search to see if name exists, and get back a pointer to it.
+ */
+ error = xfs_da_node_lookup_int(state, &retval);
+ if (error || (retval != EEXIST)) {
+ if (error == 0)
+ error = retval;
+ goto out;
+ }
+
+ /*
+ * If there is an out-of-line value, de-allocate the blocks.
+ * This is done before we remove the attribute so that we don't
+ * overflow the maximum size of a transaction and/or hit a deadlock.
+ */
+ blk = &state->path.blk[ state->path.active-1 ];
+ ASSERT(blk->bp != NULL);
+ ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
+ if (args->rmtblkno > 0) {
+ /*
+ * Fill in disk block numbers in the state structure
+ * so that we can get the buffers back after we commit
+ * several transactions in the following calls.
+ */
+ error = xfs_attr_fillstate(state);
+ if (error)
+ goto out;
+
+ /*
+ * Mark the attribute as INCOMPLETE, then bunmapi() the
+ * remote value.
+ */
+ error = xfs_attr_leaf_setflag(args);
+ if (error)
+ goto out;
+ error = xfs_attr_rmtval_remove(args);
+ if (error)
+ goto out;
+
+ /*
+ * Refill the state structure with buffers, the prior calls
+ * released our buffers.
+ */
+ error = xfs_attr_refillstate(state);
+ if (error)
+ goto out;
+ }
+
+ /*
+ * Remove the name and update the hashvals in the tree.
+ */
+ blk = &state->path.blk[ state->path.active-1 ];
+ ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
+ retval = xfs_attr_leaf_remove(blk->bp, args);
+ xfs_da_fixhashpath(state, &state->path);
+
+ /*
+ * Check to see if the tree needs to be collapsed.
+ */
+ if (retval && (state->path.active > 1)) {
+ XFS_BMAP_INIT(args->flist, args->firstblock);
+ error = xfs_da_join(state);
+ if (!error) {
+ error = xfs_bmap_finish(&args->trans, args->flist,
+ *args->firstblock, &committed);
+ }
+ if (error) {
+ ASSERT(committed);
+ args->trans = NULL;
+ xfs_bmap_cancel(args->flist);
+ goto out;
+ }
+
+ /*
+ * bmap_finish() may have committed the last trans and started
+ * a new one. We need the inode to be in all transactions.
+ */
+ if (committed) {
+ xfs_trans_ijoin(args->trans, dp, XFS_ILOCK_EXCL);
+ xfs_trans_ihold(args->trans, dp);
+ }
+
+ /*
+ * Commit the Btree join operation and start a new trans.
+ */
+ if ((error = xfs_attr_rolltrans(&args->trans, dp)))
+ goto out;
+ }
+
+ /*
+ * If the result is small enough, push it all into the inode.
+ */
+ if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) {
+ /*
+ * Have to get rid of the copy of this dabuf in the state.
+ */
+ ASSERT(state->path.active == 1);
+ ASSERT(state->path.blk[0].bp);
+ xfs_da_buf_done(state->path.blk[0].bp);
+ state->path.blk[0].bp = NULL;
+
+ error = xfs_da_read_buf(args->trans, args->dp, 0, -1, &bp,
+ XFS_ATTR_FORK);
+ if (error)
+ goto out;
+ ASSERT(INT_GET(((xfs_attr_leafblock_t *)
+ bp->data)->hdr.info.magic, ARCH_CONVERT)
+ == XFS_ATTR_LEAF_MAGIC);
+
+ if (xfs_attr_shortform_allfit(bp, dp)) {
+ XFS_BMAP_INIT(args->flist, args->firstblock);
+ error = xfs_attr_leaf_to_shortform(bp, args);
+ /* bp is gone due to xfs_da_shrink_inode */
+ if (!error) {
+ error = xfs_bmap_finish(&args->trans,
+ args->flist,
+ *args->firstblock,
+ &committed);
+ }
+ if (error) {
+ ASSERT(committed);
+ args->trans = NULL;
+ xfs_bmap_cancel(args->flist);
+ goto out;
+ }
+
+ /*
+ * bmap_finish() may have committed the last trans
+ * and started a new one. We need the inode to be
+ * in all transactions.
+ */
+ if (committed) {
+ xfs_trans_ijoin(args->trans, dp, XFS_ILOCK_EXCL);
+ xfs_trans_ihold(args->trans, dp);
+ }
+ } else
+ xfs_da_brelse(args->trans, bp);
+ }
+ error = 0;
+
+out:
+ xfs_da_state_free(state);
+ return(error);
+}
+
+/*
+ * Fill in the disk block numbers in the state structure for the buffers
+ * that are attached to the state structure.
+ * This is done so that we can quickly reattach ourselves to those buffers
+ * after some set of transaction commit's has released these buffers.
+ */
+STATIC int
+xfs_attr_fillstate(xfs_da_state_t *state)
+{
+ xfs_da_state_path_t *path;
+ xfs_da_state_blk_t *blk;
+ int level;
+
+ /*
+ * Roll down the "path" in the state structure, storing the on-disk
+ * block number for those buffers in the "path".
+ */
+ path = &state->path;
+ ASSERT((path->active >= 0) && (path->active < XFS_DA_NODE_MAXDEPTH));
+ for (blk = path->blk, level = 0; level < path->active; blk++, level++) {
+ if (blk->bp) {
+ blk->disk_blkno = xfs_da_blkno(blk->bp);
+ xfs_da_buf_done(blk->bp);
+ blk->bp = NULL;
+ } else {
+ blk->disk_blkno = 0;
+ }
+ }
+
+ /*
+ * Roll down the "altpath" in the state structure, storing the on-disk
+ * block number for those buffers in the "altpath".
+ */
+ path = &state->altpath;
+ ASSERT((path->active >= 0) && (path->active < XFS_DA_NODE_MAXDEPTH));
+ for (blk = path->blk, level = 0; level < path->active; blk++, level++) {
+ if (blk->bp) {
+ blk->disk_blkno = xfs_da_blkno(blk->bp);
+ xfs_da_buf_done(blk->bp);
+ blk->bp = NULL;
+ } else {
+ blk->disk_blkno = 0;
+ }
+ }
+
+ return(0);
+}
+
+/*
+ * Reattach the buffers to the state structure based on the disk block
+ * numbers stored in the state structure.
+ * This is done after some set of transaction commit's has released those
+ * buffers from our grip.
+ */
+STATIC int
+xfs_attr_refillstate(xfs_da_state_t *state)
+{
+ xfs_da_state_path_t *path;
+ xfs_da_state_blk_t *blk;
+ int level, error;
+
+ /*
+ * Roll down the "path" in the state structure, storing the on-disk
+ * block number for those buffers in the "path".
+ */
+ path = &state->path;
+ ASSERT((path->active >= 0) && (path->active < XFS_DA_NODE_MAXDEPTH));
+ for (blk = path->blk, level = 0; level < path->active; blk++, level++) {
+ if (blk->disk_blkno) {
+ error = xfs_da_read_buf(state->args->trans,
+ state->args->dp,
+ blk->blkno, blk->disk_blkno,
+ &blk->bp, XFS_ATTR_FORK);
+ if (error)
+ return(error);
+ } else {
+ blk->bp = NULL;
+ }
+ }
+
+ /*
+ * Roll down the "altpath" in the state structure, storing the on-disk
+ * block number for those buffers in the "altpath".
+ */
+ path = &state->altpath;
+ ASSERT((path->active >= 0) && (path->active < XFS_DA_NODE_MAXDEPTH));
+ for (blk = path->blk, level = 0; level < path->active; blk++, level++) {
+ if (blk->disk_blkno) {
+ error = xfs_da_read_buf(state->args->trans,
+ state->args->dp,
+ blk->blkno, blk->disk_blkno,
+ &blk->bp, XFS_ATTR_FORK);
+ if (error)
+ return(error);
+ } else {
+ blk->bp = NULL;
+ }
+ }
+
+ return(0);
+}
+
+/*
+ * Look up a filename in a node attribute list.
+ *
+ * This routine gets called for any attribute fork that has more than one
+ * block, ie: both true Btree attr lists and for single-leaf-blocks with
+ * "remote" values taking up more blocks.
+ */
+int
+xfs_attr_node_get(xfs_da_args_t *args)
+{
+ xfs_da_state_t *state;
+ xfs_da_state_blk_t *blk;
+ int error, retval;
+ int i;
+
+ state = xfs_da_state_alloc();
+ state->args = args;
+ state->mp = args->dp->i_mount;
+ state->blocksize = state->mp->m_sb.sb_blocksize;
+ state->node_ents = state->mp->m_attr_node_ents;
+
+ /*
+ * Search to see if name exists, and get back a pointer to it.
+ */
+ error = xfs_da_node_lookup_int(state, &retval);
+ if (error) {
+ retval = error;
+ } else if (retval == EEXIST) {
+ blk = &state->path.blk[ state->path.active-1 ];
+ ASSERT(blk->bp != NULL);
+ ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC);
+
+ /*
+ * Get the value, local or "remote"
+ */
+ retval = xfs_attr_leaf_getvalue(blk->bp, args);
+ if (!retval && (args->rmtblkno > 0)
+ && !(args->flags & ATTR_KERNOVAL)) {
+ retval = xfs_attr_rmtval_get(args);
+ }
+ }
+
+ /*
+ * If not in a transaction, we have to release all the buffers.
+ */
+ for (i = 0; i < state->path.active; i++) {
+ xfs_da_brelse(args->trans, state->path.blk[i].bp);
+ state->path.blk[i].bp = NULL;
+ }
+
+ xfs_da_state_free(state);
+ return(retval);
+}
+
+STATIC int /* error */
+xfs_attr_node_list(xfs_attr_list_context_t *context)
+{
+ attrlist_cursor_kern_t *cursor;
+ xfs_attr_leafblock_t *leaf;
+ xfs_da_intnode_t *node;
+ xfs_da_node_entry_t *btree;
+ int error, i;
+ xfs_dabuf_t *bp;
+
+ cursor = context->cursor;
+ cursor->initted = 1;
+
+ /*
+ * Do all sorts of validation on the passed-in cursor structure.
+ * If anything is amiss, ignore the cursor and look up the hashval
+ * starting from the btree root.
+ */
+ bp = NULL;
+ if (cursor->blkno > 0) {
+ error = xfs_da_read_buf(NULL, context->dp, cursor->blkno, -1,
+ &bp, XFS_ATTR_FORK);
+ if ((error != 0) && (error != EFSCORRUPTED))
+ return(error);
+ if (bp) {
+ node = bp->data;
+ switch (INT_GET(node->hdr.info.magic, ARCH_CONVERT)) {
+ case XFS_DA_NODE_MAGIC:
+ xfs_attr_trace_l_cn("wrong blk", context, node);
+ xfs_da_brelse(NULL, bp);
+ bp = NULL;
+ break;
+ case XFS_ATTR_LEAF_MAGIC:
+ leaf = bp->data;
+ if (cursor->hashval >
+ INT_GET(leaf->entries[
+ INT_GET(leaf->hdr.count,
+ ARCH_CONVERT)-1].hashval,
+ ARCH_CONVERT)) {
+ xfs_attr_trace_l_cl("wrong blk",
+ context, leaf);
+ xfs_da_brelse(NULL, bp);
+ bp = NULL;
+ } else if (cursor->hashval <=
+ INT_GET(leaf->entries[0].hashval,
+ ARCH_CONVERT)) {
+ xfs_attr_trace_l_cl("maybe wrong blk",
+ context, leaf);
+ xfs_da_brelse(NULL, bp);
+ bp = NULL;
+ }
+ break;
+ default:
+ xfs_attr_trace_l_c("wrong blk - ??", context);
+ xfs_da_brelse(NULL, bp);
+ bp = NULL;
+ }
+ }
+ }
+
+ /*
+ * We did not find what we expected given the cursor's contents,
+ * so we start from the top and work down based on the hash value.
+ * Note that start of node block is same as start of leaf block.
+ */
+ if (bp == NULL) {
+ cursor->blkno = 0;
+ for (;;) {
+ error = xfs_da_read_buf(NULL, context->dp,
+ cursor->blkno, -1, &bp,
+ XFS_ATTR_FORK);
+ if (error)
+ return(error);
+ if (unlikely(bp == NULL)) {
+ XFS_ERROR_REPORT("xfs_attr_node_list(2)",
+ XFS_ERRLEVEL_LOW,
+ context->dp->i_mount);
+ return(XFS_ERROR(EFSCORRUPTED));
+ }
+ node = bp->data;
+ if (INT_GET(node->hdr.info.magic, ARCH_CONVERT)
+ == XFS_ATTR_LEAF_MAGIC)
+ break;
+ if (unlikely(INT_GET(node->hdr.info.magic, ARCH_CONVERT)
+ != XFS_DA_NODE_MAGIC)) {
+ XFS_CORRUPTION_ERROR("xfs_attr_node_list(3)",
+ XFS_ERRLEVEL_LOW,
+ context->dp->i_mount,
+ node);
+ xfs_da_brelse(NULL, bp);
+ return(XFS_ERROR(EFSCORRUPTED));
+ }
+ btree = node->btree;
+ for (i = 0;
+ i < INT_GET(node->hdr.count, ARCH_CONVERT);
+ btree++, i++) {
+ if (cursor->hashval
+ <= INT_GET(btree->hashval,
+ ARCH_CONVERT)) {
+ cursor->blkno = INT_GET(btree->before, ARCH_CONVERT);
+ xfs_attr_trace_l_cb("descending",
+ context, btree);
+ break;
+ }
+ }
+ if (i == INT_GET(node->hdr.count, ARCH_CONVERT)) {
+ xfs_da_brelse(NULL, bp);
+ return(0);
+ }
+ xfs_da_brelse(NULL, bp);
+ }
+ }
+ ASSERT(bp != NULL);
+
+ /*
+ * Roll upward through the blocks, processing each leaf block in
+ * order. As long as there is space in the result buffer, keep
+ * adding the information.
+ */
+ for (;;) {
+ leaf = bp->data;
+ if (unlikely(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT)
+ != XFS_ATTR_LEAF_MAGIC)) {
+ XFS_CORRUPTION_ERROR("xfs_attr_node_list(4)",
+ XFS_ERRLEVEL_LOW,
+ context->dp->i_mount, leaf);
+ xfs_da_brelse(NULL, bp);
+ return(XFS_ERROR(EFSCORRUPTED));
+ }
+ error = xfs_attr_leaf_list_int(bp, context);
+ if (error || (INT_ISZERO(leaf->hdr.info.forw, ARCH_CONVERT)))
+ break; /* not really an error, buffer full or EOF */
+ cursor->blkno = INT_GET(leaf->hdr.info.forw, ARCH_CONVERT);
+ xfs_da_brelse(NULL, bp);
+ error = xfs_da_read_buf(NULL, context->dp, cursor->blkno, -1,
+ &bp, XFS_ATTR_FORK);
+ if (error)
+ return(error);
+ if (unlikely((bp == NULL))) {
+ XFS_ERROR_REPORT("xfs_attr_node_list(5)",
+ XFS_ERRLEVEL_LOW,
+ context->dp->i_mount);
+ return(XFS_ERROR(EFSCORRUPTED));
+ }
+ }
+ xfs_da_brelse(NULL, bp);
+ return(0);
+}
+
+
+/*========================================================================
+ * External routines for manipulating out-of-line attribute values.
+ *========================================================================*/
+
+/*
+ * Read the value associated with an attribute from the out-of-line buffer
+ * that we stored it in.
+ */
+STATIC int
+xfs_attr_rmtval_get(xfs_da_args_t *args)
+{
+ xfs_bmbt_irec_t map[ATTR_RMTVALUE_MAPSIZE];
+ xfs_mount_t *mp;
+ xfs_daddr_t dblkno;
+ xfs_caddr_t dst;
+ xfs_buf_t *bp;
+ int nmap, error, tmp, valuelen, blkcnt, i;
+ xfs_dablk_t lblkno;
+
+ ASSERT(!(args->flags & ATTR_KERNOVAL));
+
+ mp = args->dp->i_mount;
+ dst = args->value;
+ valuelen = args->valuelen;
+ lblkno = args->rmtblkno;
+ while (valuelen > 0) {
+ nmap = ATTR_RMTVALUE_MAPSIZE;
+ error = xfs_bmapi(args->trans, args->dp, (xfs_fileoff_t)lblkno,
+ args->rmtblkcnt,
+ XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA,
+ NULL, 0, map, &nmap, NULL);
+ if (error)
+ return(error);
+ ASSERT(nmap >= 1);
+
+ for (i = 0; (i < nmap) && (valuelen > 0); i++) {
+ ASSERT((map[i].br_startblock != DELAYSTARTBLOCK) &&
+ (map[i].br_startblock != HOLESTARTBLOCK));
+ dblkno = XFS_FSB_TO_DADDR(mp, map[i].br_startblock);
+ blkcnt = XFS_FSB_TO_BB(mp, map[i].br_blockcount);
+ error = xfs_read_buf(mp, mp->m_ddev_targp, dblkno,
+ blkcnt, XFS_BUF_LOCK, &bp);
+ if (error)
+ return(error);
+
+ tmp = (valuelen < XFS_BUF_SIZE(bp))
+ ? valuelen : XFS_BUF_SIZE(bp);
+ xfs_biomove(bp, 0, tmp, dst, XFS_B_READ);
+ xfs_buf_relse(bp);
+ dst += tmp;
+ valuelen -= tmp;
+
+ lblkno += map[i].br_blockcount;
+ }
+ }
+ ASSERT(valuelen == 0);
+ return(0);
+}
+
+/*
+ * Write the value associated with an attribute into the out-of-line buffer
+ * that we have defined for it.
+ */
+STATIC int
+xfs_attr_rmtval_set(xfs_da_args_t *args)
+{
+ xfs_mount_t *mp;
+ xfs_fileoff_t lfileoff;
+ xfs_inode_t *dp;
+ xfs_bmbt_irec_t map;
+ xfs_daddr_t dblkno;
+ xfs_caddr_t src;
+ xfs_buf_t *bp;
+ xfs_dablk_t lblkno;
+ int blkcnt, valuelen, nmap, error, tmp, committed;
+
+ dp = args->dp;
+ mp = dp->i_mount;
+ src = args->value;
+
+ /*
+ * Find a "hole" in the attribute address space large enough for
+ * us to drop the new attribute's value into.
+ */
+ blkcnt = XFS_B_TO_FSB(mp, args->valuelen);
+ lfileoff = 0;
+ error = xfs_bmap_first_unused(args->trans, args->dp, blkcnt, &lfileoff,
+ XFS_ATTR_FORK);
+ if (error) {
+ return(error);
+ }
+ args->rmtblkno = lblkno = (xfs_dablk_t)lfileoff;
+ args->rmtblkcnt = blkcnt;
+
+ /*
+ * Roll through the "value", allocating blocks on disk as required.
+ */
+ while (blkcnt > 0) {
+ /*
+ * Allocate a single extent, up to the size of the value.
+ */
+ XFS_BMAP_INIT(args->flist, args->firstblock);
+ nmap = 1;
+ error = xfs_bmapi(args->trans, dp, (xfs_fileoff_t)lblkno,
+ blkcnt,
+ XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA |
+ XFS_BMAPI_WRITE,
+ args->firstblock, args->total, &map, &nmap,
+ args->flist);
+ if (!error) {
+ error = xfs_bmap_finish(&args->trans, args->flist,
+ *args->firstblock, &committed);
+ }
+ if (error) {
+ ASSERT(committed);
+ args->trans = NULL;
+ xfs_bmap_cancel(args->flist);
+ return(error);
+ }
+
+ /*
+ * bmap_finish() may have committed the last trans and started
+ * a new one. We need the inode to be in all transactions.
+ */
+ if (committed) {
+ xfs_trans_ijoin(args->trans, dp, XFS_ILOCK_EXCL);
+ xfs_trans_ihold(args->trans, dp);
+ }
+
+ ASSERT(nmap == 1);
+ ASSERT((map.br_startblock != DELAYSTARTBLOCK) &&
+ (map.br_startblock != HOLESTARTBLOCK));
+ lblkno += map.br_blockcount;
+ blkcnt -= map.br_blockcount;
+
+ /*
+ * Start the next trans in the chain.
+ */
+ if ((error = xfs_attr_rolltrans(&args->trans, dp)))
+ return (error);
+ }
+
+ /*
+ * Roll through the "value", copying the attribute value to the
+ * already-allocated blocks. Blocks are written synchronously
+ * so that we can know they are all on disk before we turn off
+ * the INCOMPLETE flag.
+ */
+ lblkno = args->rmtblkno;
+ valuelen = args->valuelen;
+ while (valuelen > 0) {
+ /*
+ * Try to remember where we decided to put the value.
+ */
+ XFS_BMAP_INIT(args->flist, args->firstblock);
+ nmap = 1;
+ error = xfs_bmapi(NULL, dp, (xfs_fileoff_t)lblkno,
+ args->rmtblkcnt,
+ XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA,
+ args->firstblock, 0, &map, &nmap, NULL);
+ if (error) {
+ return(error);
+ }
+ ASSERT(nmap == 1);
+ ASSERT((map.br_startblock != DELAYSTARTBLOCK) &&
+ (map.br_startblock != HOLESTARTBLOCK));
+
+ dblkno = XFS_FSB_TO_DADDR(mp, map.br_startblock),
+ blkcnt = XFS_FSB_TO_BB(mp, map.br_blockcount);
+
+ bp = xfs_buf_get_flags(mp->m_ddev_targp, dblkno,
+ blkcnt, XFS_BUF_LOCK);
+ ASSERT(bp);
+ ASSERT(!XFS_BUF_GETERROR(bp));
+
+ tmp = (valuelen < XFS_BUF_SIZE(bp)) ? valuelen :
+ XFS_BUF_SIZE(bp);
+ xfs_biomove(bp, 0, tmp, src, XFS_B_WRITE);
+ if (tmp < XFS_BUF_SIZE(bp))
+ xfs_biozero(bp, tmp, XFS_BUF_SIZE(bp) - tmp);
+ if ((error = xfs_bwrite(mp, bp))) {/* GROT: NOTE: synchronous write */
+ return (error);
+ }
+ src += tmp;
+ valuelen -= tmp;
+
+ lblkno += map.br_blockcount;
+ }
+ ASSERT(valuelen == 0);
+ return(0);
+}
+
+/*
+ * Remove the value associated with an attribute by deleting the
+ * out-of-line buffer that it is stored on.
+ */
+STATIC int
+xfs_attr_rmtval_remove(xfs_da_args_t *args)
+{
+ xfs_mount_t *mp;
+ xfs_bmbt_irec_t map;
+ xfs_buf_t *bp;
+ xfs_daddr_t dblkno;
+ xfs_dablk_t lblkno;
+ int valuelen, blkcnt, nmap, error, done, committed;
+
+ mp = args->dp->i_mount;
+
+ /*
+ * Roll through the "value", invalidating the attribute value's
+ * blocks.
+ */
+ lblkno = args->rmtblkno;
+ valuelen = args->rmtblkcnt;
+ while (valuelen > 0) {
+ /*
+ * Try to remember where we decided to put the value.
+ */
+ XFS_BMAP_INIT(args->flist, args->firstblock);
+ nmap = 1;
+ error = xfs_bmapi(NULL, args->dp, (xfs_fileoff_t)lblkno,
+ args->rmtblkcnt,
+ XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA,
+ args->firstblock, 0, &map, &nmap,
+ args->flist);
+ if (error) {
+ return(error);
+ }
+ ASSERT(nmap == 1);
+ ASSERT((map.br_startblock != DELAYSTARTBLOCK) &&
+ (map.br_startblock != HOLESTARTBLOCK));
+
+ dblkno = XFS_FSB_TO_DADDR(mp, map.br_startblock),
+ blkcnt = XFS_FSB_TO_BB(mp, map.br_blockcount);
+
+ /*
+ * If the "remote" value is in the cache, remove it.
+ */
+ bp = xfs_incore(mp->m_ddev_targp, dblkno, blkcnt,
+ XFS_INCORE_TRYLOCK);
+ if (bp) {
+ XFS_BUF_STALE(bp);
+ XFS_BUF_UNDELAYWRITE(bp);
+ xfs_buf_relse(bp);
+ bp = NULL;
+ }
+
+ valuelen -= map.br_blockcount;
+
+ lblkno += map.br_blockcount;
+ }
+
+ /*
+ * Keep de-allocating extents until the remote-value region is gone.
+ */
+ lblkno = args->rmtblkno;
+ blkcnt = args->rmtblkcnt;
+ done = 0;
+ while (!done) {
+ XFS_BMAP_INIT(args->flist, args->firstblock);
+ error = xfs_bunmapi(args->trans, args->dp, lblkno, blkcnt,
+ XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA,
+ 1, args->firstblock, args->flist, &done);
+ if (!error) {
+ error = xfs_bmap_finish(&args->trans, args->flist,
+ *args->firstblock, &committed);
+ }
+ if (error) {
+ ASSERT(committed);
+ args->trans = NULL;
+ xfs_bmap_cancel(args->flist);
+ return(error);
+ }
+
+ /*
+ * bmap_finish() may have committed the last trans and started
+ * a new one. We need the inode to be in all transactions.
+ */
+ if (committed) {
+ xfs_trans_ijoin(args->trans, args->dp, XFS_ILOCK_EXCL);
+ xfs_trans_ihold(args->trans, args->dp);
+ }
+
+ /*
+ * Close out trans and start the next one in the chain.
+ */
+ if ((error = xfs_attr_rolltrans(&args->trans, args->dp)))
+ return (error);
+ }
+ return(0);
+}
+
+#if defined(XFS_ATTR_TRACE)
+/*
+ * Add a trace buffer entry for an attr_list context structure.
+ */
+void
+xfs_attr_trace_l_c(char *where, struct xfs_attr_list_context *context)
+{
+ xfs_attr_trace_enter(XFS_ATTR_KTRACE_L_C, where,
+ (__psunsigned_t)context->dp,
+ (__psunsigned_t)context->cursor->hashval,
+ (__psunsigned_t)context->cursor->blkno,
+ (__psunsigned_t)context->cursor->offset,
+ (__psunsigned_t)context->alist,
+ (__psunsigned_t)context->bufsize,
+ (__psunsigned_t)context->count,
+ (__psunsigned_t)context->firstu,
+ (__psunsigned_t)
+ ((context->count > 0) &&
+ !(context->flags & (ATTR_KERNAMELS|ATTR_KERNOVAL)))
+ ? (ATTR_ENTRY(context->alist,
+ context->count-1)->a_valuelen)
+ : 0,
+ (__psunsigned_t)context->dupcnt,
+ (__psunsigned_t)context->flags,
+ (__psunsigned_t)NULL,
+ (__psunsigned_t)NULL,
+ (__psunsigned_t)NULL);
+}
+
+/*
+ * Add a trace buffer entry for a context structure and a Btree node.
+ */
+void
+xfs_attr_trace_l_cn(char *where, struct xfs_attr_list_context *context,
+ struct xfs_da_intnode *node)
+{
+ xfs_attr_trace_enter(XFS_ATTR_KTRACE_L_CN, where,
+ (__psunsigned_t)context->dp,
+ (__psunsigned_t)context->cursor->hashval,
+ (__psunsigned_t)context->cursor->blkno,
+ (__psunsigned_t)context->cursor->offset,
+ (__psunsigned_t)context->alist,
+ (__psunsigned_t)context->bufsize,
+ (__psunsigned_t)context->count,
+ (__psunsigned_t)context->firstu,
+ (__psunsigned_t)
+ ((context->count > 0) &&
+ !(context->flags & (ATTR_KERNAMELS|ATTR_KERNOVAL)))
+ ? (ATTR_ENTRY(context->alist,
+ context->count-1)->a_valuelen)
+ : 0,
+ (__psunsigned_t)context->dupcnt,
+ (__psunsigned_t)context->flags,
+ (__psunsigned_t)INT_GET(node->hdr.count, ARCH_CONVERT),
+ (__psunsigned_t)INT_GET(node->btree[0].hashval, ARCH_CONVERT),
+ (__psunsigned_t)INT_GET(node->btree[INT_GET(node->hdr.count, ARCH_CONVERT)-1].hashval, ARCH_CONVERT));
+}
+
+/*
+ * Add a trace buffer entry for a context structure and a Btree element.
+ */
+void
+xfs_attr_trace_l_cb(char *where, struct xfs_attr_list_context *context,
+ struct xfs_da_node_entry *btree)
+{
+ xfs_attr_trace_enter(XFS_ATTR_KTRACE_L_CB, where,
+ (__psunsigned_t)context->dp,
+ (__psunsigned_t)context->cursor->hashval,
+ (__psunsigned_t)context->cursor->blkno,
+ (__psunsigned_t)context->cursor->offset,
+ (__psunsigned_t)context->alist,
+ (__psunsigned_t)context->bufsize,
+ (__psunsigned_t)context->count,
+ (__psunsigned_t)context->firstu,
+ (__psunsigned_t)
+ ((context->count > 0) &&
+ !(context->flags & (ATTR_KERNAMELS|ATTR_KERNOVAL)))
+ ? (ATTR_ENTRY(context->alist,
+ context->count-1)->a_valuelen)
+ : 0,
+ (__psunsigned_t)context->dupcnt,
+ (__psunsigned_t)context->flags,
+ (__psunsigned_t)INT_GET(btree->hashval, ARCH_CONVERT),
+ (__psunsigned_t)INT_GET(btree->before, ARCH_CONVERT),
+ (__psunsigned_t)NULL);
+}
+
+/*
+ * Add a trace buffer entry for a context structure and a leaf block.
+ */
+void
+xfs_attr_trace_l_cl(char *where, struct xfs_attr_list_context *context,
+ struct xfs_attr_leafblock *leaf)
+{
+ xfs_attr_trace_enter(XFS_ATTR_KTRACE_L_CL, where,
+ (__psunsigned_t)context->dp,
+ (__psunsigned_t)context->cursor->hashval,
+ (__psunsigned_t)context->cursor->blkno,
+ (__psunsigned_t)context->cursor->offset,
+ (__psunsigned_t)context->alist,
+ (__psunsigned_t)context->bufsize,
+ (__psunsigned_t)context->count,
+ (__psunsigned_t)context->firstu,
+ (__psunsigned_t)
+ ((context->count > 0) &&
+ !(context->flags & (ATTR_KERNAMELS|ATTR_KERNOVAL)))
+ ? (ATTR_ENTRY(context->alist,
+ context->count-1)->a_valuelen)
+ : 0,
+ (__psunsigned_t)context->dupcnt,
+ (__psunsigned_t)context->flags,
+ (__psunsigned_t)INT_GET(leaf->hdr.count, ARCH_CONVERT),
+ (__psunsigned_t)INT_GET(leaf->entries[0].hashval, ARCH_CONVERT),
+ (__psunsigned_t)INT_GET(leaf->entries[INT_GET(leaf->hdr.count, ARCH_CONVERT)-1].hashval, ARCH_CONVERT));
+}
+
+/*
+ * Add a trace buffer entry for the arguments given to the routine,
+ * generic form.
+ */
+void
+xfs_attr_trace_enter(int type, char *where,
+ __psunsigned_t a2, __psunsigned_t a3,
+ __psunsigned_t a4, __psunsigned_t a5,
+ __psunsigned_t a6, __psunsigned_t a7,
+ __psunsigned_t a8, __psunsigned_t a9,
+ __psunsigned_t a10, __psunsigned_t a11,
+ __psunsigned_t a12, __psunsigned_t a13,
+ __psunsigned_t a14, __psunsigned_t a15)
+{
+ ASSERT(xfs_attr_trace_buf);
+ ktrace_enter(xfs_attr_trace_buf, (void *)((__psunsigned_t)type),
+ (void *)where,
+ (void *)a2, (void *)a3, (void *)a4,
+ (void *)a5, (void *)a6, (void *)a7,
+ (void *)a8, (void *)a9, (void *)a10,
+ (void *)a11, (void *)a12, (void *)a13,
+ (void *)a14, (void *)a15);
+}
+#endif /* XFS_ATTR_TRACE */
+
+
+/*========================================================================
+ * System (pseudo) namespace attribute interface routines.
+ *========================================================================*/
+
+STATIC int
+posix_acl_access_set(
+ vnode_t *vp, char *name, void *data, size_t size, int xflags)
+{
+ return xfs_acl_vset(vp, data, size, _ACL_TYPE_ACCESS);
+}
+
+STATIC int
+posix_acl_access_remove(
+ struct vnode *vp, char *name, int xflags)
+{
+ return xfs_acl_vremove(vp, _ACL_TYPE_ACCESS);
+}
+
+STATIC int
+posix_acl_access_get(
+ vnode_t *vp, char *name, void *data, size_t size, int xflags)
+{
+ return xfs_acl_vget(vp, data, size, _ACL_TYPE_ACCESS);
+}
+
+STATIC int
+posix_acl_access_exists(
+ vnode_t *vp)
+{
+ return xfs_acl_vhasacl_access(vp);
+}
+
+STATIC int
+posix_acl_default_set(
+ vnode_t *vp, char *name, void *data, size_t size, int xflags)
+{
+ return xfs_acl_vset(vp, data, size, _ACL_TYPE_DEFAULT);
+}
+
+STATIC int
+posix_acl_default_get(
+ vnode_t *vp, char *name, void *data, size_t size, int xflags)
+{
+ return xfs_acl_vget(vp, data, size, _ACL_TYPE_DEFAULT);
+}
+
+STATIC int
+posix_acl_default_remove(
+ struct vnode *vp, char *name, int xflags)
+{
+ return xfs_acl_vremove(vp, _ACL_TYPE_DEFAULT);
+}
+
+STATIC int
+posix_acl_default_exists(
+ vnode_t *vp)
+{
+ return xfs_acl_vhasacl_default(vp);
+}
+
+struct attrnames posix_acl_access = {
+ .attr_name = "posix_acl_access",
+ .attr_namelen = sizeof("posix_acl_access") - 1,
+ .attr_get = posix_acl_access_get,
+ .attr_set = posix_acl_access_set,
+ .attr_remove = posix_acl_access_remove,
+ .attr_exists = posix_acl_access_exists,
+};
+
+struct attrnames posix_acl_default = {
+ .attr_name = "posix_acl_default",
+ .attr_namelen = sizeof("posix_acl_default") - 1,
+ .attr_get = posix_acl_default_get,
+ .attr_set = posix_acl_default_set,
+ .attr_remove = posix_acl_default_remove,
+ .attr_exists = posix_acl_default_exists,
+};
+
+struct attrnames *attr_system_names[] =
+ { &posix_acl_access, &posix_acl_default };
+
+
+/*========================================================================
+ * Namespace-prefix-style attribute name interface routines.
+ *========================================================================*/
+
+STATIC int
+attr_generic_set(
+ struct vnode *vp, char *name, void *data, size_t size, int xflags)
+{
+ int error;
+
+ VOP_ATTR_SET(vp, name, data, size, xflags, NULL, error);
+ return -error;
+}
+
+STATIC int
+attr_generic_get(
+ struct vnode *vp, char *name, void *data, size_t size, int xflags)
+{
+ int error, asize = size;
+
+ VOP_ATTR_GET(vp, name, data, &asize, xflags, NULL, error);
+ if (!error)
+ return asize;
+ return -error;
+}
+
+STATIC int
+attr_generic_remove(
+ struct vnode *vp, char *name, int xflags)
+{
+ int error;
+
+ VOP_ATTR_REMOVE(vp, name, xflags, NULL, error);
+ return -error;
+}
+
+STATIC int
+attr_generic_listadd(
+ attrnames_t *prefix,
+ attrnames_t *namesp,
+ void *data,
+ size_t size,
+ ssize_t *result)
+{
+ char *p = data + *result;
+
+ *result += prefix->attr_namelen;
+ *result += namesp->attr_namelen + 1;
+ if (!size)
+ return 0;
+ if (*result > size)
+ return -ERANGE;
+ strcpy(p, prefix->attr_name);
+ p += prefix->attr_namelen;
+ strcpy(p, namesp->attr_name);
+ p += namesp->attr_namelen + 1;
+ return 0;
+}
+
+STATIC int
+attr_system_list(
+ struct vnode *vp,
+ void *data,
+ size_t size,
+ ssize_t *result)
+{
+ attrnames_t *namesp;
+ int i, error = 0;
+
+ for (i = 0; i < ATTR_SYSCOUNT; i++) {
+ namesp = attr_system_names[i];
+ if (!namesp->attr_exists || !namesp->attr_exists(vp))
+ continue;
+ error = attr_generic_listadd(&attr_system, namesp,
+ data, size, result);
+ if (error)
+ break;
+ }
+ return error;
+}
+
+int
+attr_generic_list(
+ struct vnode *vp, void *data, size_t size, int xflags, ssize_t *result)
+{
+ attrlist_cursor_kern_t cursor = { 0 };
+ int error;
+
+ VOP_ATTR_LIST(vp, data, size, xflags, &cursor, NULL, error);
+ if (error > 0)
+ return -error;
+ *result = -error;
+ return attr_system_list(vp, data, size, result);
+}
+
+attrnames_t *
+attr_lookup_namespace(
+ char *name,
+ struct attrnames **names,
+ int nnames)
+{
+ int i;
+
+ for (i = 0; i < nnames; i++)
+ if (!strncmp(name, names[i]->attr_name, names[i]->attr_namelen))
+ return names[i];
+ return NULL;
+}
+
+/*
+ * Some checks to prevent people abusing EAs to get over quota:
+ * - Don't allow modifying user EAs on devices/symlinks;
+ * - Don't allow modifying user EAs if sticky bit set;
+ */
+STATIC int
+attr_user_capable(
+ struct vnode *vp,
+ cred_t *cred)
+{
+ struct inode *inode = LINVFS_GET_IP(vp);
+
+ if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
+ return -EPERM;
+ if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode) &&
+ !capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (S_ISDIR(inode->i_mode) && (inode->i_mode & S_ISVTX) &&
+ (current_fsuid(cred) != inode->i_uid) && !capable(CAP_FOWNER))
+ return -EPERM;
+ return 0;
+}
+
+STATIC int
+attr_trusted_capable(
+ struct vnode *vp,
+ cred_t *cred)
+{
+ struct inode *inode = LINVFS_GET_IP(vp);
+
+ if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
+ return -EPERM;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ return 0;
+}
+
+STATIC int
+attr_secure_capable(
+ struct vnode *vp,
+ cred_t *cred)
+{
+ return -ENOSECURITY;
+}
+
+STATIC int
+attr_system_set(
+ struct vnode *vp, char *name, void *data, size_t size, int xflags)
+{
+ attrnames_t *namesp;
+ int error;
+
+ if (xflags & ATTR_CREATE)
+ return -EINVAL;
+
+ namesp = attr_lookup_namespace(name, attr_system_names, ATTR_SYSCOUNT);
+ if (!namesp)
+ return -EOPNOTSUPP;
+ error = namesp->attr_set(vp, name, data, size, xflags);
+ if (!error)
+ error = vn_revalidate(vp);
+ return error;
+}
+
+STATIC int
+attr_system_get(
+ struct vnode *vp, char *name, void *data, size_t size, int xflags)
+{
+ attrnames_t *namesp;
+
+ namesp = attr_lookup_namespace(name, attr_system_names, ATTR_SYSCOUNT);
+ if (!namesp)
+ return -EOPNOTSUPP;
+ return namesp->attr_get(vp, name, data, size, xflags);
+}
+
+STATIC int
+attr_system_remove(
+ struct vnode *vp, char *name, int xflags)
+{
+ attrnames_t *namesp;
+
+ namesp = attr_lookup_namespace(name, attr_system_names, ATTR_SYSCOUNT);
+ if (!namesp)
+ return -EOPNOTSUPP;
+ return namesp->attr_remove(vp, name, xflags);
+}
+
+struct attrnames attr_system = {
+ .attr_name = "system.",
+ .attr_namelen = sizeof("system.") - 1,
+ .attr_flag = ATTR_SYSTEM,
+ .attr_get = attr_system_get,
+ .attr_set = attr_system_set,
+ .attr_remove = attr_system_remove,
+ .attr_capable = (attrcapable_t)fs_noerr,
+};
+
+struct attrnames attr_trusted = {
+ .attr_name = "trusted.",
+ .attr_namelen = sizeof("trusted.") - 1,
+ .attr_flag = ATTR_ROOT,
+ .attr_get = attr_generic_get,
+ .attr_set = attr_generic_set,
+ .attr_remove = attr_generic_remove,
+ .attr_capable = attr_trusted_capable,
+};
+
+struct attrnames attr_secure = {
+ .attr_name = "security.",
+ .attr_namelen = sizeof("security.") - 1,
+ .attr_flag = ATTR_SECURE,
+ .attr_get = attr_generic_get,
+ .attr_set = attr_generic_set,
+ .attr_remove = attr_generic_remove,
+ .attr_capable = attr_secure_capable,
+};
+
+struct attrnames attr_user = {
+ .attr_name = "user.",
+ .attr_namelen = sizeof("user.") - 1,
+ .attr_get = attr_generic_get,
+ .attr_set = attr_generic_set,
+ .attr_remove = attr_generic_remove,
+ .attr_capable = attr_user_capable,
+};
+
+struct attrnames *attr_namespaces[] =
+ { &attr_system, &attr_trusted, &attr_secure, &attr_user };
diff --git a/fs/xfs/xfs_attr.h b/fs/xfs/xfs_attr.h
new file mode 100644
index 00000000000000..67cd0f5ac1a759
--- /dev/null
+++ b/fs/xfs/xfs_attr.h
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2000, 2002-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_ATTR_H__
+#define __XFS_ATTR_H__
+
+/*
+ * xfs_attr.h
+ *
+ * Large attribute lists are structured around Btrees where all the data
+ * elements are in the leaf nodes. Attribute names are hashed into an int,
+ * then that int is used as the index into the Btree. Since the hashval
+ * of an attribute name may not be unique, we may have duplicate keys.
+ * The internal links in the Btree are logical block offsets into the file.
+ *
+ * Small attribute lists use a different format and are packed as tightly
+ * as possible so as to fit into the literal area of the inode.
+ */
+
+/*========================================================================
+ * External interfaces
+ *========================================================================*/
+
+struct cred;
+struct vnode;
+
+typedef int (*attrset_t)(struct vnode *, char *, void *, size_t, int);
+typedef int (*attrget_t)(struct vnode *, char *, void *, size_t, int);
+typedef int (*attrremove_t)(struct vnode *, char *, int);
+typedef int (*attrexists_t)(struct vnode *);
+typedef int (*attrcapable_t)(struct vnode *, struct cred *);
+
+typedef struct attrnames {
+ char * attr_name;
+ unsigned int attr_namelen;
+ unsigned int attr_flag;
+ attrget_t attr_get;
+ attrset_t attr_set;
+ attrremove_t attr_remove;
+ attrexists_t attr_exists;
+ attrcapable_t attr_capable;
+} attrnames_t;
+
+#define ATTR_NAMECOUNT 4
+extern struct attrnames attr_user;
+extern struct attrnames attr_secure;
+extern struct attrnames attr_system;
+extern struct attrnames attr_trusted;
+extern struct attrnames *attr_namespaces[ATTR_NAMECOUNT];
+
+#define ATTR_SYSCOUNT 2
+extern struct attrnames posix_acl_access;
+extern struct attrnames posix_acl_default;
+extern struct attrnames *attr_system_names[ATTR_SYSCOUNT];
+
+extern attrnames_t *attr_lookup_namespace(char *, attrnames_t **, int);
+extern int attr_generic_list(struct vnode *, void *, size_t, int, ssize_t *);
+
+#define ATTR_DONTFOLLOW 0x0001 /* -- unused, from IRIX -- */
+#define ATTR_ROOT 0x0002 /* use attrs in root (trusted) namespace */
+#define ATTR_TRUST 0x0004 /* -- unused, from IRIX -- */
+#define ATTR_SECURE 0x0008 /* use attrs in security namespace */
+#define ATTR_CREATE 0x0010 /* pure create: fail if attr already exists */
+#define ATTR_REPLACE 0x0020 /* pure set: fail if attr does not exist */
+#define ATTR_SYSTEM 0x0100 /* use attrs in system (pseudo) namespace */
+
+#define ATTR_KERNACCESS 0x0400 /* [kernel] iaccess, inode held io-locked */
+#define ATTR_KERNOTIME 0x1000 /* [kernel] don't update inode timestamps */
+#define ATTR_KERNOVAL 0x2000 /* [kernel] get attr size only, not value */
+#define ATTR_KERNAMELS 0x4000 /* [kernel] list attr names (simple list) */
+
+#define ATTR_KERNORMALS 0x0800 /* [kernel] normal attr list: user+secure */
+#define ATTR_KERNROOTLS 0x8000 /* [kernel] include root in the attr list */
+#define ATTR_KERNFULLS (ATTR_KERNORMALS|ATTR_KERNROOTLS)
+
+/*
+ * The maximum size (into the kernel or returned from the kernel) of an
+ * attribute value or the buffer used for an attr_list() call. Larger
+ * sizes will result in an ERANGE return code.
+ */
+#define ATTR_MAX_VALUELEN (64*1024) /* max length of a value */
+
+/*
+ * Define how lists of attribute names are returned to the user from
+ * the attr_list() call. A large, 32bit aligned, buffer is passed in
+ * along with its size. We put an array of offsets at the top that each
+ * reference an attrlist_ent_t and pack the attrlist_ent_t's at the bottom.
+ */
+typedef struct attrlist {
+ __s32 al_count; /* number of entries in attrlist */
+ __s32 al_more; /* T/F: more attrs (do call again) */
+ __s32 al_offset[1]; /* byte offsets of attrs [var-sized] */
+} attrlist_t;
+
+/*
+ * Show the interesting info about one attribute. This is what the
+ * al_offset[i] entry points to.
+ */
+typedef struct attrlist_ent { /* data from attr_list() */
+ __u32 a_valuelen; /* number bytes in value of attr */
+ char a_name[1]; /* attr name (NULL terminated) */
+} attrlist_ent_t;
+
+/*
+ * Given a pointer to the (char*) buffer containing the attr_list() result,
+ * and an index, return a pointer to the indicated attribute in the buffer.
+ */
+#define ATTR_ENTRY(buffer, index) \
+ ((attrlist_ent_t *) \
+ &((char *)buffer)[ ((attrlist_t *)(buffer))->al_offset[index] ])
+
+/*
+ * Multi-attribute operation vector.
+ */
+typedef struct attr_multiop {
+ int am_opcode; /* operation to perform (ATTR_OP_GET, etc.) */
+ int am_error; /* [out arg] result of this sub-op (an errno) */
+ char *am_attrname; /* attribute name to work with */
+ char *am_attrvalue; /* [in/out arg] attribute value (raw bytes) */
+ int am_length; /* [in/out arg] length of value */
+ int am_flags; /* bitwise OR of attr API flags defined above */
+} attr_multiop_t;
+
+#define ATTR_OP_GET 1 /* return the indicated attr's value */
+#define ATTR_OP_SET 2 /* set/create the indicated attr/value pair */
+#define ATTR_OP_REMOVE 3 /* remove the indicated attr */
+
+/*
+ * Kernel-internal version of the attrlist cursor.
+ */
+typedef struct attrlist_cursor_kern {
+ __u32 hashval; /* hash value of next entry to add */
+ __u32 blkno; /* block containing entry (suggestion) */
+ __u32 offset; /* offset in list of equal-hashvals */
+ __u16 pad1; /* padding to match user-level */
+ __u8 pad2; /* padding to match user-level */
+ __u8 initted; /* T/F: cursor has been initialized */
+} attrlist_cursor_kern_t;
+
+
+/*========================================================================
+ * Function prototypes for the kernel.
+ *========================================================================*/
+
+struct xfs_inode;
+struct attrlist_cursor_kern;
+struct xfs_da_args;
+
+/*
+ * Overall external interface routines.
+ */
+int xfs_attr_get(bhv_desc_t *, char *, char *, int *, int, struct cred *);
+int xfs_attr_set(bhv_desc_t *, char *, char *, int, int, struct cred *);
+int xfs_attr_remove(bhv_desc_t *, char *, int, struct cred *);
+int xfs_attr_list(bhv_desc_t *, char *, int, int,
+ struct attrlist_cursor_kern *, struct cred *);
+int xfs_attr_inactive(struct xfs_inode *dp);
+
+int xfs_attr_node_get(struct xfs_da_args *);
+int xfs_attr_leaf_get(struct xfs_da_args *);
+int xfs_attr_shortform_getvalue(struct xfs_da_args *);
+int xfs_attr_fetch(struct xfs_inode *, char *, int,
+ char *, int *, int, struct cred *);
+
+#endif /* __XFS_ATTR_H__ */
diff --git a/fs/xfs/xfs_attr_leaf.c b/fs/xfs/xfs_attr_leaf.c
new file mode 100644
index 00000000000000..444f341ed5eef9
--- /dev/null
+++ b/fs/xfs/xfs_attr_leaf.c
@@ -0,0 +1,3050 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+/*
+ * xfs_attr_leaf.c
+ *
+ * GROT: figure out how to recover gracefully when bmap returns ENOSPC.
+ */
+
+#include "xfs.h"
+
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_alloc.h"
+#include "xfs_btree.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode_item.h"
+#include "xfs_inode.h"
+#include "xfs_bmap.h"
+#include "xfs_da_btree.h"
+#include "xfs_attr.h"
+#include "xfs_attr_leaf.h"
+#include "xfs_error.h"
+#include "xfs_bit.h"
+
+/*
+ * xfs_attr_leaf.c
+ *
+ * Routines to implement leaf blocks of attributes as Btrees of hashed names.
+ */
+
+/*========================================================================
+ * Function prototypes for the kernel.
+ *========================================================================*/
+
+/*
+ * Routines used for growing the Btree.
+ */
+STATIC int xfs_attr_leaf_add_work(xfs_dabuf_t *leaf_buffer, xfs_da_args_t *args,
+ int freemap_index);
+STATIC void xfs_attr_leaf_compact(xfs_trans_t *trans, xfs_dabuf_t *leaf_buffer);
+STATIC void xfs_attr_leaf_rebalance(xfs_da_state_t *state,
+ xfs_da_state_blk_t *blk1,
+ xfs_da_state_blk_t *blk2);
+STATIC int xfs_attr_leaf_figure_balance(xfs_da_state_t *state,
+ xfs_da_state_blk_t *leaf_blk_1,
+ xfs_da_state_blk_t *leaf_blk_2,
+ int *number_entries_in_blk1,
+ int *number_usedbytes_in_blk1);
+
+/*
+ * Utility routines.
+ */
+STATIC void xfs_attr_leaf_moveents(xfs_attr_leafblock_t *src_leaf,
+ int src_start,
+ xfs_attr_leafblock_t *dst_leaf,
+ int dst_start, int move_count,
+ xfs_mount_t *mp);
+
+
+/*========================================================================
+ * External routines when dirsize < XFS_LITINO(mp).
+ *========================================================================*/
+
+/*
+ * Create the initial contents of a shortform attribute list.
+ */
+int
+xfs_attr_shortform_create(xfs_da_args_t *args)
+{
+ xfs_attr_sf_hdr_t *hdr;
+ xfs_inode_t *dp;
+ xfs_ifork_t *ifp;
+
+ dp = args->dp;
+ ASSERT(dp != NULL);
+ ifp = dp->i_afp;
+ ASSERT(ifp != NULL);
+ ASSERT(ifp->if_bytes == 0);
+ if (dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS) {
+ ifp->if_flags &= ~XFS_IFEXTENTS; /* just in case */
+ dp->i_d.di_aformat = XFS_DINODE_FMT_LOCAL;
+ ifp->if_flags |= XFS_IFINLINE;
+ } else {
+ ASSERT(ifp->if_flags & XFS_IFINLINE);
+ }
+ xfs_idata_realloc(dp, sizeof(*hdr), XFS_ATTR_FORK);
+ hdr = (xfs_attr_sf_hdr_t *)ifp->if_u1.if_data;
+ INT_ZERO(hdr->count, ARCH_CONVERT);
+ INT_SET(hdr->totsize, ARCH_CONVERT, sizeof(*hdr));
+ xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_ADATA);
+ return(0);
+}
+
+/*
+ * Add a name/value pair to the shortform attribute list.
+ * Overflow from the inode has already been checked for.
+ */
+int
+xfs_attr_shortform_add(xfs_da_args_t *args)
+{
+ xfs_attr_shortform_t *sf;
+ xfs_attr_sf_entry_t *sfe;
+ int i, offset, size;
+ xfs_inode_t *dp;
+ xfs_ifork_t *ifp;
+
+ dp = args->dp;
+ ifp = dp->i_afp;
+ ASSERT(ifp->if_flags & XFS_IFINLINE);
+ sf = (xfs_attr_shortform_t *)ifp->if_u1.if_data;
+ sfe = &sf->list[0];
+ for (i = 0; i < INT_GET(sf->hdr.count, ARCH_CONVERT);
+ sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) {
+ if (sfe->namelen != args->namelen)
+ continue;
+ if (memcmp(args->name, sfe->nameval, args->namelen) != 0)
+ continue;
+ if (((args->flags & ATTR_SECURE) != 0) !=
+ ((sfe->flags & XFS_ATTR_SECURE) != 0))
+ continue;
+ if (((args->flags & ATTR_ROOT) != 0) !=
+ ((sfe->flags & XFS_ATTR_ROOT) != 0))
+ continue;
+ return(XFS_ERROR(EEXIST));
+ }
+
+ offset = (char *)sfe - (char *)sf;
+ size = XFS_ATTR_SF_ENTSIZE_BYNAME(args->namelen, args->valuelen);
+ xfs_idata_realloc(dp, size, XFS_ATTR_FORK);
+ sf = (xfs_attr_shortform_t *)ifp->if_u1.if_data;
+ sfe = (xfs_attr_sf_entry_t *)((char *)sf + offset);
+
+ sfe->namelen = args->namelen;
+ INT_SET(sfe->valuelen, ARCH_CONVERT, args->valuelen);
+ sfe->flags = (args->flags & ATTR_SECURE) ? XFS_ATTR_SECURE :
+ ((args->flags & ATTR_ROOT) ? XFS_ATTR_ROOT : 0);
+ memcpy(sfe->nameval, args->name, args->namelen);
+ memcpy(&sfe->nameval[args->namelen], args->value, args->valuelen);
+ INT_MOD(sf->hdr.count, ARCH_CONVERT, 1);
+ INT_MOD(sf->hdr.totsize, ARCH_CONVERT, size);
+ xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_ADATA);
+
+ return(0);
+}
+
+/*
+ * Remove a name from the shortform attribute list structure.
+ */
+int
+xfs_attr_shortform_remove(xfs_da_args_t *args)
+{
+ xfs_attr_shortform_t *sf;
+ xfs_attr_sf_entry_t *sfe;
+ int base, size=0, end, totsize, i;
+ xfs_inode_t *dp;
+
+ /*
+ * Remove the attribute.
+ */
+ dp = args->dp;
+ base = sizeof(xfs_attr_sf_hdr_t);
+ sf = (xfs_attr_shortform_t *)dp->i_afp->if_u1.if_data;
+ sfe = &sf->list[0];
+ for (i = 0; i < INT_GET(sf->hdr.count, ARCH_CONVERT);
+ sfe = XFS_ATTR_SF_NEXTENTRY(sfe),
+ base += size, i++) {
+ size = XFS_ATTR_SF_ENTSIZE(sfe);
+ if (sfe->namelen != args->namelen)
+ continue;
+ if (memcmp(sfe->nameval, args->name, args->namelen) != 0)
+ continue;
+ if (((args->flags & ATTR_SECURE) != 0) !=
+ ((sfe->flags & XFS_ATTR_SECURE) != 0))
+ continue;
+ if (((args->flags & ATTR_ROOT) != 0) !=
+ ((sfe->flags & XFS_ATTR_ROOT) != 0))
+ continue;
+ break;
+ }
+ if (i == INT_GET(sf->hdr.count, ARCH_CONVERT))
+ return(XFS_ERROR(ENOATTR));
+
+ end = base + size;
+ totsize = INT_GET(sf->hdr.totsize, ARCH_CONVERT);
+ if (end != totsize) {
+ memmove(&((char *)sf)[base], &((char *)sf)[end],
+ totsize - end);
+ }
+ INT_MOD(sf->hdr.count, ARCH_CONVERT, -1);
+ INT_MOD(sf->hdr.totsize, ARCH_CONVERT, -size);
+ xfs_idata_realloc(dp, -size, XFS_ATTR_FORK);
+ xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_ADATA);
+
+ return(0);
+}
+
+/*
+ * Look up a name in a shortform attribute list structure.
+ */
+/*ARGSUSED*/
+int
+xfs_attr_shortform_lookup(xfs_da_args_t *args)
+{
+ xfs_attr_shortform_t *sf;
+ xfs_attr_sf_entry_t *sfe;
+ int i;
+ xfs_ifork_t *ifp;
+
+ ifp = args->dp->i_afp;
+ ASSERT(ifp->if_flags & XFS_IFINLINE);
+ sf = (xfs_attr_shortform_t *)ifp->if_u1.if_data;
+ sfe = &sf->list[0];
+ for (i = 0; i < INT_GET(sf->hdr.count, ARCH_CONVERT);
+ sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) {
+ if (sfe->namelen != args->namelen)
+ continue;
+ if (memcmp(args->name, sfe->nameval, args->namelen) != 0)
+ continue;
+ if (((args->flags & ATTR_SECURE) != 0) !=
+ ((sfe->flags & XFS_ATTR_SECURE) != 0))
+ continue;
+ if (((args->flags & ATTR_ROOT) != 0) !=
+ ((sfe->flags & XFS_ATTR_ROOT) != 0))
+ continue;
+ return(XFS_ERROR(EEXIST));
+ }
+ return(XFS_ERROR(ENOATTR));
+}
+
+/*
+ * Look up a name in a shortform attribute list structure.
+ */
+/*ARGSUSED*/
+int
+xfs_attr_shortform_getvalue(xfs_da_args_t *args)
+{
+ xfs_attr_shortform_t *sf;
+ xfs_attr_sf_entry_t *sfe;
+ int i;
+
+ ASSERT(args->dp->i_d.di_aformat == XFS_IFINLINE);
+ sf = (xfs_attr_shortform_t *)args->dp->i_afp->if_u1.if_data;
+ sfe = &sf->list[0];
+ for (i = 0; i < INT_GET(sf->hdr.count, ARCH_CONVERT);
+ sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) {
+ if (sfe->namelen != args->namelen)
+ continue;
+ if (memcmp(args->name, sfe->nameval, args->namelen) != 0)
+ continue;
+ if (((args->flags & ATTR_SECURE) != 0) !=
+ ((sfe->flags & XFS_ATTR_SECURE) != 0))
+ continue;
+ if (((args->flags & ATTR_ROOT) != 0) !=
+ ((sfe->flags & XFS_ATTR_ROOT) != 0))
+ continue;
+ if (args->flags & ATTR_KERNOVAL) {
+ args->valuelen = INT_GET(sfe->valuelen, ARCH_CONVERT);
+ return(XFS_ERROR(EEXIST));
+ }
+ if (args->valuelen < INT_GET(sfe->valuelen, ARCH_CONVERT)) {
+ args->valuelen = INT_GET(sfe->valuelen, ARCH_CONVERT);
+ return(XFS_ERROR(ERANGE));
+ }
+ args->valuelen = INT_GET(sfe->valuelen, ARCH_CONVERT);
+ memcpy(args->value, &sfe->nameval[args->namelen],
+ args->valuelen);
+ return(XFS_ERROR(EEXIST));
+ }
+ return(XFS_ERROR(ENOATTR));
+}
+
+/*
+ * Convert from using the shortform to the leaf.
+ */
+int
+xfs_attr_shortform_to_leaf(xfs_da_args_t *args)
+{
+ xfs_inode_t *dp;
+ xfs_attr_shortform_t *sf;
+ xfs_attr_sf_entry_t *sfe;
+ xfs_da_args_t nargs;
+ char *tmpbuffer;
+ int error, i, size;
+ xfs_dablk_t blkno;
+ xfs_dabuf_t *bp;
+ xfs_ifork_t *ifp;
+
+ dp = args->dp;
+ ifp = dp->i_afp;
+ sf = (xfs_attr_shortform_t *)ifp->if_u1.if_data;
+ size = INT_GET(sf->hdr.totsize, ARCH_CONVERT);
+ tmpbuffer = kmem_alloc(size, KM_SLEEP);
+ ASSERT(tmpbuffer != NULL);
+ memcpy(tmpbuffer, ifp->if_u1.if_data, size);
+ sf = (xfs_attr_shortform_t *)tmpbuffer;
+
+ xfs_idata_realloc(dp, -size, XFS_ATTR_FORK);
+ bp = NULL;
+ error = xfs_da_grow_inode(args, &blkno);
+ if (error) {
+ /*
+ * If we hit an IO error middle of the transaction inside
+ * grow_inode(), we may have inconsistent data. Bail out.
+ */
+ if (error == EIO)
+ goto out;
+ xfs_idata_realloc(dp, size, XFS_ATTR_FORK); /* try to put */
+ memcpy(ifp->if_u1.if_data, tmpbuffer, size); /* it back */
+ goto out;
+ }
+
+ ASSERT(blkno == 0);
+ error = xfs_attr_leaf_create(args, blkno, &bp);
+ if (error) {
+ error = xfs_da_shrink_inode(args, 0, bp);
+ bp = NULL;
+ if (error)
+ goto out;
+ xfs_idata_realloc(dp, size, XFS_ATTR_FORK); /* try to put */
+ memcpy(ifp->if_u1.if_data, tmpbuffer, size); /* it back */
+ goto out;
+ }
+
+ memset((char *)&nargs, 0, sizeof(nargs));
+ nargs.dp = dp;
+ nargs.firstblock = args->firstblock;
+ nargs.flist = args->flist;
+ nargs.total = args->total;
+ nargs.whichfork = XFS_ATTR_FORK;
+ nargs.trans = args->trans;
+ nargs.oknoent = 1;
+
+ sfe = &sf->list[0];
+ for (i = 0; i < INT_GET(sf->hdr.count, ARCH_CONVERT); i++) {
+ nargs.name = (char *)sfe->nameval;
+ nargs.namelen = sfe->namelen;
+ nargs.value = (char *)&sfe->nameval[nargs.namelen];
+ nargs.valuelen = INT_GET(sfe->valuelen, ARCH_CONVERT);
+ nargs.hashval = xfs_da_hashname((char *)sfe->nameval,
+ sfe->namelen);
+ nargs.flags = (sfe->flags & XFS_ATTR_SECURE) ? ATTR_SECURE :
+ ((sfe->flags & XFS_ATTR_ROOT) ? ATTR_ROOT : 0);
+ error = xfs_attr_leaf_lookup_int(bp, &nargs); /* set a->index */
+ ASSERT(error == ENOATTR);
+ error = xfs_attr_leaf_add(bp, &nargs);
+ ASSERT(error != ENOSPC);
+ if (error)
+ goto out;
+ sfe = XFS_ATTR_SF_NEXTENTRY(sfe);
+ }
+ error = 0;
+
+out:
+ if(bp)
+ xfs_da_buf_done(bp);
+ kmem_free(tmpbuffer, size);
+ return(error);
+}
+
+STATIC int
+xfs_attr_shortform_compare(const void *a, const void *b)
+{
+ xfs_attr_sf_sort_t *sa, *sb;
+
+ sa = (xfs_attr_sf_sort_t *)a;
+ sb = (xfs_attr_sf_sort_t *)b;
+ if (INT_GET(sa->hash, ARCH_CONVERT)
+ < INT_GET(sb->hash, ARCH_CONVERT)) {
+ return(-1);
+ } else if (INT_GET(sa->hash, ARCH_CONVERT)
+ > INT_GET(sb->hash, ARCH_CONVERT)) {
+ return(1);
+ } else {
+ return(sa->entno - sb->entno);
+ }
+}
+
+/*
+ * Copy out entries of shortform attribute lists for attr_list().
+ * Shortform atrtribute lists are not stored in hashval sorted order.
+ * If the output buffer is not large enough to hold them all, then we
+ * we have to calculate each entries' hashvalue and sort them before
+ * we can begin returning them to the user.
+ */
+/*ARGSUSED*/
+int
+xfs_attr_shortform_list(xfs_attr_list_context_t *context)
+{
+ attrlist_cursor_kern_t *cursor;
+ xfs_attr_sf_sort_t *sbuf, *sbp;
+ xfs_attr_shortform_t *sf;
+ xfs_attr_sf_entry_t *sfe;
+ xfs_inode_t *dp;
+ int sbsize, nsbuf, count, i;
+
+ ASSERT(context != NULL);
+ dp = context->dp;
+ ASSERT(dp != NULL);
+ ASSERT(dp->i_afp != NULL);
+ sf = (xfs_attr_shortform_t *)dp->i_afp->if_u1.if_data;
+ ASSERT(sf != NULL);
+ if (INT_ISZERO(sf->hdr.count, ARCH_CONVERT))
+ return(0);
+ cursor = context->cursor;
+ ASSERT(cursor != NULL);
+
+ xfs_attr_trace_l_c("sf start", context);
+
+ /*
+ * If the buffer is large enough, do not bother with sorting.
+ * Note the generous fudge factor of 16 overhead bytes per entry.
+ */
+ if ((dp->i_afp->if_bytes + INT_GET(sf->hdr.count, ARCH_CONVERT) * 16)
+ < context->bufsize) {
+ for (i = 0, sfe = &sf->list[0];
+ i < INT_GET(sf->hdr.count, ARCH_CONVERT); i++) {
+ attrnames_t *namesp;
+
+ if (((context->flags & ATTR_SECURE) != 0) !=
+ ((sfe->flags & XFS_ATTR_SECURE) != 0) &&
+ !(context->flags & ATTR_KERNORMALS)) {
+ sfe = XFS_ATTR_SF_NEXTENTRY(sfe);
+ continue;
+ }
+ if (((context->flags & ATTR_ROOT) != 0) !=
+ ((sfe->flags & XFS_ATTR_ROOT) != 0) &&
+ !(context->flags & ATTR_KERNROOTLS)) {
+ sfe = XFS_ATTR_SF_NEXTENTRY(sfe);
+ continue;
+ }
+ namesp = (sfe->flags & XFS_ATTR_SECURE) ? &attr_secure:
+ ((sfe->flags & XFS_ATTR_ROOT) ? &attr_trusted :
+ &attr_user);
+ if (context->flags & ATTR_KERNOVAL) {
+ ASSERT(context->flags & ATTR_KERNAMELS);
+ context->count += namesp->attr_namelen +
+ INT_GET(sfe->namelen, ARCH_CONVERT) + 1;
+ }
+ else {
+ if (xfs_attr_put_listent(context, namesp,
+ (char *)sfe->nameval,
+ (int)sfe->namelen,
+ (int)INT_GET(sfe->valuelen,
+ ARCH_CONVERT)))
+ break;
+ }
+ sfe = XFS_ATTR_SF_NEXTENTRY(sfe);
+ }
+ xfs_attr_trace_l_c("sf big-gulp", context);
+ return(0);
+ }
+
+ /*
+ * It didn't all fit, so we have to sort everything on hashval.
+ */
+ sbsize = INT_GET(sf->hdr.count, ARCH_CONVERT) * sizeof(*sbuf);
+ sbp = sbuf = kmem_alloc(sbsize, KM_SLEEP);
+
+ /*
+ * Scan the attribute list for the rest of the entries, storing
+ * the relevant info from only those that match into a buffer.
+ */
+ nsbuf = 0;
+ for (i = 0, sfe = &sf->list[0];
+ i < INT_GET(sf->hdr.count, ARCH_CONVERT); i++) {
+ if (unlikely(
+ ((char *)sfe < (char *)sf) ||
+ ((char *)sfe >= ((char *)sf + dp->i_afp->if_bytes)))) {
+ XFS_CORRUPTION_ERROR("xfs_attr_shortform_list",
+ XFS_ERRLEVEL_LOW,
+ context->dp->i_mount, sfe);
+ xfs_attr_trace_l_c("sf corrupted", context);
+ kmem_free(sbuf, sbsize);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ if (((context->flags & ATTR_SECURE) != 0) !=
+ ((sfe->flags & XFS_ATTR_SECURE) != 0) &&
+ !(context->flags & ATTR_KERNORMALS)) {
+ sfe = XFS_ATTR_SF_NEXTENTRY(sfe);
+ continue;
+ }
+ if (((context->flags & ATTR_ROOT) != 0) !=
+ ((sfe->flags & XFS_ATTR_ROOT) != 0) &&
+ !(context->flags & ATTR_KERNROOTLS)) {
+ sfe = XFS_ATTR_SF_NEXTENTRY(sfe);
+ continue;
+ }
+ sbp->entno = i;
+ INT_SET(sbp->hash, ARCH_CONVERT,
+ xfs_da_hashname((char *)sfe->nameval, sfe->namelen));
+ sbp->name = (char *)sfe->nameval;
+ sbp->namelen = sfe->namelen;
+ /* These are bytes, and both on-disk, don't endian-flip */
+ sbp->valuelen = sfe->valuelen;
+ sbp->flags = sfe->flags;
+ sfe = XFS_ATTR_SF_NEXTENTRY(sfe);
+ sbp++;
+ nsbuf++;
+ }
+
+ /*
+ * Sort the entries on hash then entno.
+ */
+ qsort(sbuf, nsbuf, sizeof(*sbuf), xfs_attr_shortform_compare);
+
+ /*
+ * Re-find our place IN THE SORTED LIST.
+ */
+ count = 0;
+ cursor->initted = 1;
+ cursor->blkno = 0;
+ for (sbp = sbuf, i = 0; i < nsbuf; i++, sbp++) {
+ if (INT_GET(sbp->hash, ARCH_CONVERT) == cursor->hashval) {
+ if (cursor->offset == count) {
+ break;
+ }
+ count++;
+ } else if (INT_GET(sbp->hash, ARCH_CONVERT) > cursor->hashval) {
+ break;
+ }
+ }
+ if (i == nsbuf) {
+ kmem_free(sbuf, sbsize);
+ xfs_attr_trace_l_c("blk end", context);
+ return(0);
+ }
+
+ /*
+ * Loop putting entries into the user buffer.
+ */
+ for ( ; i < nsbuf; i++, sbp++) {
+ attrnames_t *namesp;
+
+ namesp = (sbp->flags & XFS_ATTR_SECURE) ? &attr_secure :
+ ((sbp->flags & XFS_ATTR_ROOT) ? &attr_trusted :
+ &attr_user);
+
+ if (cursor->hashval != INT_GET(sbp->hash, ARCH_CONVERT)) {
+ cursor->hashval = INT_GET(sbp->hash, ARCH_CONVERT);
+ cursor->offset = 0;
+ }
+ if (context->flags & ATTR_KERNOVAL) {
+ ASSERT(context->flags & ATTR_KERNAMELS);
+ context->count += namesp->attr_namelen +
+ sbp->namelen + 1;
+ } else {
+ if (xfs_attr_put_listent(context, namesp,
+ sbp->name, sbp->namelen,
+ INT_GET(sbp->valuelen, ARCH_CONVERT)))
+ break;
+ }
+ cursor->offset++;
+ }
+
+ kmem_free(sbuf, sbsize);
+ xfs_attr_trace_l_c("sf E-O-F", context);
+ return(0);
+}
+
+/*
+ * Check a leaf attribute block to see if all the entries would fit into
+ * a shortform attribute list.
+ */
+int
+xfs_attr_shortform_allfit(xfs_dabuf_t *bp, xfs_inode_t *dp)
+{
+ xfs_attr_leafblock_t *leaf;
+ xfs_attr_leaf_entry_t *entry;
+ xfs_attr_leaf_name_local_t *name_loc;
+ int bytes, i;
+
+ leaf = bp->data;
+ ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT)
+ == XFS_ATTR_LEAF_MAGIC);
+
+ entry = &leaf->entries[0];
+ bytes = sizeof(struct xfs_attr_sf_hdr);
+ for (i = 0; i < INT_GET(leaf->hdr.count, ARCH_CONVERT); entry++, i++) {
+ if (entry->flags & XFS_ATTR_INCOMPLETE)
+ continue; /* don't copy partial entries */
+ if (!(entry->flags & XFS_ATTR_LOCAL))
+ return(0);
+ name_loc = XFS_ATTR_LEAF_NAME_LOCAL(leaf, i);
+ if (name_loc->namelen >= XFS_ATTR_SF_ENTSIZE_MAX)
+ return(0);
+ if (INT_GET(name_loc->valuelen, ARCH_CONVERT) >= XFS_ATTR_SF_ENTSIZE_MAX)
+ return(0);
+ bytes += sizeof(struct xfs_attr_sf_entry)-1
+ + name_loc->namelen
+ + INT_GET(name_loc->valuelen, ARCH_CONVERT);
+ }
+ return( bytes < XFS_IFORK_ASIZE(dp) );
+}
+
+/*
+ * Convert a leaf attribute list to shortform attribute list
+ */
+int
+xfs_attr_leaf_to_shortform(xfs_dabuf_t *bp, xfs_da_args_t *args)
+{
+ xfs_attr_leafblock_t *leaf;
+ xfs_attr_leaf_entry_t *entry;
+ xfs_attr_leaf_name_local_t *name_loc;
+ xfs_da_args_t nargs;
+ xfs_inode_t *dp;
+ char *tmpbuffer;
+ int error, i;
+
+ dp = args->dp;
+ tmpbuffer = kmem_alloc(XFS_LBSIZE(dp->i_mount), KM_SLEEP);
+ ASSERT(tmpbuffer != NULL);
+
+ ASSERT(bp != NULL);
+ memcpy(tmpbuffer, bp->data, XFS_LBSIZE(dp->i_mount));
+ leaf = (xfs_attr_leafblock_t *)tmpbuffer;
+ ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT)
+ == XFS_ATTR_LEAF_MAGIC);
+ memset(bp->data, 0, XFS_LBSIZE(dp->i_mount));
+
+ /*
+ * Clean out the prior contents of the attribute list.
+ */
+ error = xfs_da_shrink_inode(args, 0, bp);
+ if (error)
+ goto out;
+ error = xfs_attr_shortform_create(args);
+ if (error)
+ goto out;
+
+ /*
+ * Copy the attributes
+ */
+ memset((char *)&nargs, 0, sizeof(nargs));
+ nargs.dp = dp;
+ nargs.firstblock = args->firstblock;
+ nargs.flist = args->flist;
+ nargs.total = args->total;
+ nargs.whichfork = XFS_ATTR_FORK;
+ nargs.trans = args->trans;
+ nargs.oknoent = 1;
+ entry = &leaf->entries[0];
+ for (i = 0; i < INT_GET(leaf->hdr.count, ARCH_CONVERT); entry++, i++) {
+ if (entry->flags & XFS_ATTR_INCOMPLETE)
+ continue; /* don't copy partial entries */
+ if (INT_ISZERO(entry->nameidx, ARCH_CONVERT))
+ continue;
+ ASSERT(entry->flags & XFS_ATTR_LOCAL);
+ name_loc = XFS_ATTR_LEAF_NAME_LOCAL(leaf, i);
+ nargs.name = (char *)name_loc->nameval;
+ nargs.namelen = name_loc->namelen;
+ nargs.value = (char *)&name_loc->nameval[nargs.namelen];
+ nargs.valuelen = INT_GET(name_loc->valuelen, ARCH_CONVERT);
+ nargs.hashval = INT_GET(entry->hashval, ARCH_CONVERT);
+ nargs.flags = (entry->flags & XFS_ATTR_SECURE) ? ATTR_SECURE :
+ ((entry->flags & XFS_ATTR_ROOT) ? ATTR_ROOT : 0);
+ xfs_attr_shortform_add(&nargs);
+ }
+ error = 0;
+
+out:
+ kmem_free(tmpbuffer, XFS_LBSIZE(dp->i_mount));
+ return(error);
+}
+
+/*
+ * Convert from using a single leaf to a root node and a leaf.
+ */
+int
+xfs_attr_leaf_to_node(xfs_da_args_t *args)
+{
+ xfs_attr_leafblock_t *leaf;
+ xfs_da_intnode_t *node;
+ xfs_inode_t *dp;
+ xfs_dabuf_t *bp1, *bp2;
+ xfs_dablk_t blkno;
+ int error;
+
+ dp = args->dp;
+ bp1 = bp2 = NULL;
+ error = xfs_da_grow_inode(args, &blkno);
+ if (error)
+ goto out;
+ error = xfs_da_read_buf(args->trans, args->dp, 0, -1, &bp1,
+ XFS_ATTR_FORK);
+ if (error)
+ goto out;
+ ASSERT(bp1 != NULL);
+ bp2 = NULL;
+ error = xfs_da_get_buf(args->trans, args->dp, blkno, -1, &bp2,
+ XFS_ATTR_FORK);
+ if (error)
+ goto out;
+ ASSERT(bp2 != NULL);
+ memcpy(bp2->data, bp1->data, XFS_LBSIZE(dp->i_mount));
+ xfs_da_buf_done(bp1);
+ bp1 = NULL;
+ xfs_da_log_buf(args->trans, bp2, 0, XFS_LBSIZE(dp->i_mount) - 1);
+
+ /*
+ * Set up the new root node.
+ */
+ error = xfs_da_node_create(args, 0, 1, &bp1, XFS_ATTR_FORK);
+ if (error)
+ goto out;
+ node = bp1->data;
+ leaf = bp2->data;
+ ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT)
+ == XFS_ATTR_LEAF_MAGIC);
+ /* both on-disk, don't endian-flip twice */
+ node->btree[0].hashval =
+ leaf->entries[INT_GET(leaf->hdr.count, ARCH_CONVERT)-1 ].hashval;
+ INT_SET(node->btree[0].before, ARCH_CONVERT, blkno);
+ INT_SET(node->hdr.count, ARCH_CONVERT, 1);
+ xfs_da_log_buf(args->trans, bp1, 0, XFS_LBSIZE(dp->i_mount) - 1);
+ error = 0;
+out:
+ if (bp1)
+ xfs_da_buf_done(bp1);
+ if (bp2)
+ xfs_da_buf_done(bp2);
+ return(error);
+}
+
+
+/*========================================================================
+ * Routines used for growing the Btree.
+ *========================================================================*/
+
+/*
+ * Create the initial contents of a leaf attribute list
+ * or a leaf in a node attribute list.
+ */
+int
+xfs_attr_leaf_create(xfs_da_args_t *args, xfs_dablk_t blkno, xfs_dabuf_t **bpp)
+{
+ xfs_attr_leafblock_t *leaf;
+ xfs_attr_leaf_hdr_t *hdr;
+ xfs_inode_t *dp;
+ xfs_dabuf_t *bp;
+ int error;
+
+ dp = args->dp;
+ ASSERT(dp != NULL);
+ error = xfs_da_get_buf(args->trans, args->dp, blkno, -1, &bp,
+ XFS_ATTR_FORK);
+ if (error)
+ return(error);
+ ASSERT(bp != NULL);
+ leaf = bp->data;
+ memset((char *)leaf, 0, XFS_LBSIZE(dp->i_mount));
+ hdr = &leaf->hdr;
+ INT_SET(hdr->info.magic, ARCH_CONVERT, XFS_ATTR_LEAF_MAGIC);
+ INT_SET(hdr->firstused, ARCH_CONVERT, XFS_LBSIZE(dp->i_mount));
+ if (INT_ISZERO(hdr->firstused, ARCH_CONVERT)) {
+ INT_SET(hdr->firstused, ARCH_CONVERT,
+ XFS_LBSIZE(dp->i_mount) - XFS_ATTR_LEAF_NAME_ALIGN);
+ }
+
+ INT_SET(hdr->freemap[0].base, ARCH_CONVERT,
+ sizeof(xfs_attr_leaf_hdr_t));
+ INT_SET(hdr->freemap[0].size, ARCH_CONVERT,
+ INT_GET(hdr->firstused, ARCH_CONVERT)
+ - INT_GET(hdr->freemap[0].base,
+ ARCH_CONVERT));
+
+ xfs_da_log_buf(args->trans, bp, 0, XFS_LBSIZE(dp->i_mount) - 1);
+
+ *bpp = bp;
+ return(0);
+}
+
+/*
+ * Split the leaf node, rebalance, then add the new entry.
+ */
+int
+xfs_attr_leaf_split(xfs_da_state_t *state, xfs_da_state_blk_t *oldblk,
+ xfs_da_state_blk_t *newblk)
+{
+ xfs_dablk_t blkno;
+ int error;
+
+ /*
+ * Allocate space for a new leaf node.
+ */
+ ASSERT(oldblk->magic == XFS_ATTR_LEAF_MAGIC);
+ error = xfs_da_grow_inode(state->args, &blkno);
+ if (error)
+ return(error);
+ error = xfs_attr_leaf_create(state->args, blkno, &newblk->bp);
+ if (error)
+ return(error);
+ newblk->blkno = blkno;
+ newblk->magic = XFS_ATTR_LEAF_MAGIC;
+
+ /*
+ * Rebalance the entries across the two leaves.
+ * NOTE: rebalance() currently depends on the 2nd block being empty.
+ */
+ xfs_attr_leaf_rebalance(state, oldblk, newblk);
+ error = xfs_da_blk_link(state, oldblk, newblk);
+ if (error)
+ return(error);
+
+ /*
+ * Save info on "old" attribute for "atomic rename" ops, leaf_add()
+ * modifies the index/blkno/rmtblk/rmtblkcnt fields to show the
+ * "new" attrs info. Will need the "old" info to remove it later.
+ *
+ * Insert the "new" entry in the correct block.
+ */
+ if (state->inleaf)
+ error = xfs_attr_leaf_add(oldblk->bp, state->args);
+ else
+ error = xfs_attr_leaf_add(newblk->bp, state->args);
+
+ /*
+ * Update last hashval in each block since we added the name.
+ */
+ oldblk->hashval = xfs_attr_leaf_lasthash(oldblk->bp, NULL);
+ newblk->hashval = xfs_attr_leaf_lasthash(newblk->bp, NULL);
+ return(error);
+}
+
+/*
+ * Add a name to the leaf attribute list structure.
+ */
+int
+xfs_attr_leaf_add(xfs_dabuf_t *bp, xfs_da_args_t *args)
+{
+ xfs_attr_leafblock_t *leaf;
+ xfs_attr_leaf_hdr_t *hdr;
+ xfs_attr_leaf_map_t *map;
+ int tablesize, entsize, sum, tmp, i;
+
+ leaf = bp->data;
+ ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT)
+ == XFS_ATTR_LEAF_MAGIC);
+ ASSERT((args->index >= 0)
+ && (args->index <= INT_GET(leaf->hdr.count, ARCH_CONVERT)));
+ hdr = &leaf->hdr;
+ entsize = xfs_attr_leaf_newentsize(args,
+ args->trans->t_mountp->m_sb.sb_blocksize, NULL);
+
+ /*
+ * Search through freemap for first-fit on new name length.
+ * (may need to figure in size of entry struct too)
+ */
+ tablesize = (INT_GET(hdr->count, ARCH_CONVERT) + 1)
+ * sizeof(xfs_attr_leaf_entry_t)
+ + sizeof(xfs_attr_leaf_hdr_t);
+ map = &hdr->freemap[XFS_ATTR_LEAF_MAPSIZE-1];
+ for (sum = 0, i = XFS_ATTR_LEAF_MAPSIZE-1; i >= 0; map--, i--) {
+ if (tablesize > INT_GET(hdr->firstused, ARCH_CONVERT)) {
+ sum += INT_GET(map->size, ARCH_CONVERT);
+ continue;
+ }
+ if (INT_ISZERO(map->size, ARCH_CONVERT))
+ continue; /* no space in this map */
+ tmp = entsize;
+ if (INT_GET(map->base, ARCH_CONVERT)
+ < INT_GET(hdr->firstused, ARCH_CONVERT))
+ tmp += sizeof(xfs_attr_leaf_entry_t);
+ if (INT_GET(map->size, ARCH_CONVERT) >= tmp) {
+ tmp = xfs_attr_leaf_add_work(bp, args, i);
+ return(tmp);
+ }
+ sum += INT_GET(map->size, ARCH_CONVERT);
+ }
+
+ /*
+ * If there are no holes in the address space of the block,
+ * and we don't have enough freespace, then compaction will do us
+ * no good and we should just give up.
+ */
+ if (!hdr->holes && (sum < entsize))
+ return(XFS_ERROR(ENOSPC));
+
+ /*
+ * Compact the entries to coalesce free space.
+ * This may change the hdr->count via dropping INCOMPLETE entries.
+ */
+ xfs_attr_leaf_compact(args->trans, bp);
+
+ /*
+ * After compaction, the block is guaranteed to have only one
+ * free region, in freemap[0]. If it is not big enough, give up.
+ */
+ if (INT_GET(hdr->freemap[0].size, ARCH_CONVERT)
+ < (entsize + sizeof(xfs_attr_leaf_entry_t)))
+ return(XFS_ERROR(ENOSPC));
+
+ return(xfs_attr_leaf_add_work(bp, args, 0));
+}
+
+/*
+ * Add a name to a leaf attribute list structure.
+ */
+STATIC int
+xfs_attr_leaf_add_work(xfs_dabuf_t *bp, xfs_da_args_t *args, int mapindex)
+{
+ xfs_attr_leafblock_t *leaf;
+ xfs_attr_leaf_hdr_t *hdr;
+ xfs_attr_leaf_entry_t *entry;
+ xfs_attr_leaf_name_local_t *name_loc;
+ xfs_attr_leaf_name_remote_t *name_rmt;
+ xfs_attr_leaf_map_t *map;
+ xfs_mount_t *mp;
+ int tmp, i;
+
+ leaf = bp->data;
+ ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT)
+ == XFS_ATTR_LEAF_MAGIC);
+ hdr = &leaf->hdr;
+ ASSERT((mapindex >= 0) && (mapindex < XFS_ATTR_LEAF_MAPSIZE));
+ ASSERT((args->index >= 0)
+ && (args->index <= INT_GET(hdr->count, ARCH_CONVERT)));
+
+ /*
+ * Force open some space in the entry array and fill it in.
+ */
+ entry = &leaf->entries[args->index];
+ if (args->index < INT_GET(hdr->count, ARCH_CONVERT)) {
+ tmp = INT_GET(hdr->count, ARCH_CONVERT) - args->index;
+ tmp *= sizeof(xfs_attr_leaf_entry_t);
+ memmove((char *)(entry+1), (char *)entry, tmp);
+ xfs_da_log_buf(args->trans, bp,
+ XFS_DA_LOGRANGE(leaf, entry, tmp + sizeof(*entry)));
+ }
+ INT_MOD(hdr->count, ARCH_CONVERT, 1);
+
+ /*
+ * Allocate space for the new string (at the end of the run).
+ */
+ map = &hdr->freemap[mapindex];
+ mp = args->trans->t_mountp;
+ ASSERT(INT_GET(map->base, ARCH_CONVERT) < XFS_LBSIZE(mp));
+ ASSERT((INT_GET(map->base, ARCH_CONVERT) & 0x3) == 0);
+ ASSERT(INT_GET(map->size, ARCH_CONVERT)
+ >= xfs_attr_leaf_newentsize(args,
+ mp->m_sb.sb_blocksize, NULL));
+ ASSERT(INT_GET(map->size, ARCH_CONVERT) < XFS_LBSIZE(mp));
+ ASSERT((INT_GET(map->size, ARCH_CONVERT) & 0x3) == 0);
+ INT_MOD(map->size, ARCH_CONVERT,
+ -xfs_attr_leaf_newentsize(args, mp->m_sb.sb_blocksize, &tmp));
+ INT_SET(entry->nameidx, ARCH_CONVERT,
+ INT_GET(map->base, ARCH_CONVERT)
+ + INT_GET(map->size, ARCH_CONVERT));
+ INT_SET(entry->hashval, ARCH_CONVERT, args->hashval);
+ entry->flags = tmp ? XFS_ATTR_LOCAL : 0;
+ entry->flags |= (args->flags & ATTR_SECURE) ? XFS_ATTR_SECURE :
+ ((args->flags & ATTR_ROOT) ? XFS_ATTR_ROOT : 0);
+ if (args->rename) {
+ entry->flags |= XFS_ATTR_INCOMPLETE;
+ if ((args->blkno2 == args->blkno) &&
+ (args->index2 <= args->index)) {
+ args->index2++;
+ }
+ }
+ xfs_da_log_buf(args->trans, bp,
+ XFS_DA_LOGRANGE(leaf, entry, sizeof(*entry)));
+ ASSERT((args->index == 0) || (INT_GET(entry->hashval, ARCH_CONVERT)
+ >= INT_GET((entry-1)->hashval,
+ ARCH_CONVERT)));
+ ASSERT((args->index == INT_GET(hdr->count, ARCH_CONVERT)-1) ||
+ (INT_GET(entry->hashval, ARCH_CONVERT)
+ <= (INT_GET((entry+1)->hashval, ARCH_CONVERT))));
+
+ /*
+ * Copy the attribute name and value into the new space.
+ *
+ * For "remote" attribute values, simply note that we need to
+ * allocate space for the "remote" value. We can't actually
+ * allocate the extents in this transaction, and we can't decide
+ * which blocks they should be as we might allocate more blocks
+ * as part of this transaction (a split operation for example).
+ */
+ if (entry->flags & XFS_ATTR_LOCAL) {
+ name_loc = XFS_ATTR_LEAF_NAME_LOCAL(leaf, args->index);
+ name_loc->namelen = args->namelen;
+ INT_SET(name_loc->valuelen, ARCH_CONVERT, args->valuelen);
+ memcpy((char *)name_loc->nameval, args->name, args->namelen);
+ memcpy((char *)&name_loc->nameval[args->namelen], args->value,
+ INT_GET(name_loc->valuelen, ARCH_CONVERT));
+ } else {
+ name_rmt = XFS_ATTR_LEAF_NAME_REMOTE(leaf, args->index);
+ name_rmt->namelen = args->namelen;
+ memcpy((char *)name_rmt->name, args->name, args->namelen);
+ entry->flags |= XFS_ATTR_INCOMPLETE;
+ /* just in case */
+ INT_ZERO(name_rmt->valuelen, ARCH_CONVERT);
+ INT_ZERO(name_rmt->valueblk, ARCH_CONVERT);
+ args->rmtblkno = 1;
+ args->rmtblkcnt = XFS_B_TO_FSB(mp, args->valuelen);
+ }
+ xfs_da_log_buf(args->trans, bp,
+ XFS_DA_LOGRANGE(leaf, XFS_ATTR_LEAF_NAME(leaf, args->index),
+ xfs_attr_leaf_entsize(leaf, args->index)));
+
+ /*
+ * Update the control info for this leaf node
+ */
+ if (INT_GET(entry->nameidx, ARCH_CONVERT)
+ < INT_GET(hdr->firstused, ARCH_CONVERT)) {
+ /* both on-disk, don't endian-flip twice */
+ hdr->firstused = entry->nameidx;
+ }
+ ASSERT(INT_GET(hdr->firstused, ARCH_CONVERT)
+ >= ((INT_GET(hdr->count, ARCH_CONVERT)
+ * sizeof(*entry))+sizeof(*hdr)));
+ tmp = (INT_GET(hdr->count, ARCH_CONVERT)-1)
+ * sizeof(xfs_attr_leaf_entry_t)
+ + sizeof(xfs_attr_leaf_hdr_t);
+ map = &hdr->freemap[0];
+ for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; map++, i++) {
+ if (INT_GET(map->base, ARCH_CONVERT) == tmp) {
+ INT_MOD(map->base, ARCH_CONVERT,
+ sizeof(xfs_attr_leaf_entry_t));
+ INT_MOD(map->size, ARCH_CONVERT,
+ -sizeof(xfs_attr_leaf_entry_t));
+ }
+ }
+ INT_MOD(hdr->usedbytes, ARCH_CONVERT,
+ xfs_attr_leaf_entsize(leaf, args->index));
+ xfs_da_log_buf(args->trans, bp,
+ XFS_DA_LOGRANGE(leaf, hdr, sizeof(*hdr)));
+ return(0);
+}
+
+/*
+ * Garbage collect a leaf attribute list block by copying it to a new buffer.
+ */
+STATIC void
+xfs_attr_leaf_compact(xfs_trans_t *trans, xfs_dabuf_t *bp)
+{
+ xfs_attr_leafblock_t *leaf_s, *leaf_d;
+ xfs_attr_leaf_hdr_t *hdr_s, *hdr_d;
+ xfs_mount_t *mp;
+ char *tmpbuffer;
+
+ mp = trans->t_mountp;
+ tmpbuffer = kmem_alloc(XFS_LBSIZE(mp), KM_SLEEP);
+ ASSERT(tmpbuffer != NULL);
+ memcpy(tmpbuffer, bp->data, XFS_LBSIZE(mp));
+ memset(bp->data, 0, XFS_LBSIZE(mp));
+
+ /*
+ * Copy basic information
+ */
+ leaf_s = (xfs_attr_leafblock_t *)tmpbuffer;
+ leaf_d = bp->data;
+ hdr_s = &leaf_s->hdr;
+ hdr_d = &leaf_d->hdr;
+ hdr_d->info = hdr_s->info; /* struct copy */
+ INT_SET(hdr_d->firstused, ARCH_CONVERT, XFS_LBSIZE(mp));
+ /* handle truncation gracefully */
+ if (INT_ISZERO(hdr_d->firstused, ARCH_CONVERT)) {
+ INT_SET(hdr_d->firstused, ARCH_CONVERT,
+ XFS_LBSIZE(mp) - XFS_ATTR_LEAF_NAME_ALIGN);
+ }
+ INT_ZERO(hdr_d->usedbytes, ARCH_CONVERT);
+ INT_ZERO(hdr_d->count, ARCH_CONVERT);
+ hdr_d->holes = 0;
+ INT_SET(hdr_d->freemap[0].base, ARCH_CONVERT,
+ sizeof(xfs_attr_leaf_hdr_t));
+ INT_SET(hdr_d->freemap[0].size, ARCH_CONVERT,
+ INT_GET(hdr_d->firstused, ARCH_CONVERT)
+ - INT_GET(hdr_d->freemap[0].base, ARCH_CONVERT));
+
+ /*
+ * Copy all entry's in the same (sorted) order,
+ * but allocate name/value pairs packed and in sequence.
+ */
+ xfs_attr_leaf_moveents(leaf_s, 0, leaf_d, 0,
+ (int)INT_GET(hdr_s->count, ARCH_CONVERT), mp);
+
+ xfs_da_log_buf(trans, bp, 0, XFS_LBSIZE(mp) - 1);
+
+ kmem_free(tmpbuffer, XFS_LBSIZE(mp));
+}
+
+/*
+ * Redistribute the attribute list entries between two leaf nodes,
+ * taking into account the size of the new entry.
+ *
+ * NOTE: if new block is empty, then it will get the upper half of the
+ * old block. At present, all (one) callers pass in an empty second block.
+ *
+ * This code adjusts the args->index/blkno and args->index2/blkno2 fields
+ * to match what it is doing in splitting the attribute leaf block. Those
+ * values are used in "atomic rename" operations on attributes. Note that
+ * the "new" and "old" values can end up in different blocks.
+ */
+STATIC void
+xfs_attr_leaf_rebalance(xfs_da_state_t *state, xfs_da_state_blk_t *blk1,
+ xfs_da_state_blk_t *blk2)
+{
+ xfs_da_args_t *args;
+ xfs_da_state_blk_t *tmp_blk;
+ xfs_attr_leafblock_t *leaf1, *leaf2;
+ xfs_attr_leaf_hdr_t *hdr1, *hdr2;
+ int count, totallen, max, space, swap;
+
+ /*
+ * Set up environment.
+ */
+ ASSERT(blk1->magic == XFS_ATTR_LEAF_MAGIC);
+ ASSERT(blk2->magic == XFS_ATTR_LEAF_MAGIC);
+ leaf1 = blk1->bp->data;
+ leaf2 = blk2->bp->data;
+ ASSERT(INT_GET(leaf1->hdr.info.magic, ARCH_CONVERT)
+ == XFS_ATTR_LEAF_MAGIC);
+ ASSERT(INT_GET(leaf2->hdr.info.magic, ARCH_CONVERT)
+ == XFS_ATTR_LEAF_MAGIC);
+ args = state->args;
+
+ /*
+ * Check ordering of blocks, reverse if it makes things simpler.
+ *
+ * NOTE: Given that all (current) callers pass in an empty
+ * second block, this code should never set "swap".
+ */
+ swap = 0;
+ if (xfs_attr_leaf_order(blk1->bp, blk2->bp)) {
+ tmp_blk = blk1;
+ blk1 = blk2;
+ blk2 = tmp_blk;
+ leaf1 = blk1->bp->data;
+ leaf2 = blk2->bp->data;
+ swap = 1;
+ }
+ hdr1 = &leaf1->hdr;
+ hdr2 = &leaf2->hdr;
+
+ /*
+ * Examine entries until we reduce the absolute difference in
+ * byte usage between the two blocks to a minimum. Then get
+ * the direction to copy and the number of elements to move.
+ *
+ * "inleaf" is true if the new entry should be inserted into blk1.
+ * If "swap" is also true, then reverse the sense of "inleaf".
+ */
+ state->inleaf = xfs_attr_leaf_figure_balance(state, blk1, blk2,
+ &count, &totallen);
+ if (swap)
+ state->inleaf = !state->inleaf;
+
+ /*
+ * Move any entries required from leaf to leaf:
+ */
+ if (count < INT_GET(hdr1->count, ARCH_CONVERT)) {
+ /*
+ * Figure the total bytes to be added to the destination leaf.
+ */
+ /* number entries being moved */
+ count = INT_GET(hdr1->count, ARCH_CONVERT) - count;
+ space = INT_GET(hdr1->usedbytes, ARCH_CONVERT) - totallen;
+ space += count * sizeof(xfs_attr_leaf_entry_t);
+
+ /*
+ * leaf2 is the destination, compact it if it looks tight.
+ */
+ max = INT_GET(hdr2->firstused, ARCH_CONVERT)
+ - sizeof(xfs_attr_leaf_hdr_t);
+ max -= INT_GET(hdr2->count, ARCH_CONVERT)
+ * sizeof(xfs_attr_leaf_entry_t);
+ if (space > max) {
+ xfs_attr_leaf_compact(args->trans, blk2->bp);
+ }
+
+ /*
+ * Move high entries from leaf1 to low end of leaf2.
+ */
+ xfs_attr_leaf_moveents(leaf1,
+ INT_GET(hdr1->count, ARCH_CONVERT)-count,
+ leaf2, 0, count, state->mp);
+
+ xfs_da_log_buf(args->trans, blk1->bp, 0, state->blocksize-1);
+ xfs_da_log_buf(args->trans, blk2->bp, 0, state->blocksize-1);
+ } else if (count > INT_GET(hdr1->count, ARCH_CONVERT)) {
+ /*
+ * I assert that since all callers pass in an empty
+ * second buffer, this code should never execute.
+ */
+
+ /*
+ * Figure the total bytes to be added to the destination leaf.
+ */
+ /* number entries being moved */
+ count -= INT_GET(hdr1->count, ARCH_CONVERT);
+ space = totallen - INT_GET(hdr1->usedbytes, ARCH_CONVERT);
+ space += count * sizeof(xfs_attr_leaf_entry_t);
+
+ /*
+ * leaf1 is the destination, compact it if it looks tight.
+ */
+ max = INT_GET(hdr1->firstused, ARCH_CONVERT)
+ - sizeof(xfs_attr_leaf_hdr_t);
+ max -= INT_GET(hdr1->count, ARCH_CONVERT)
+ * sizeof(xfs_attr_leaf_entry_t);
+ if (space > max) {
+ xfs_attr_leaf_compact(args->trans, blk1->bp);
+ }
+
+ /*
+ * Move low entries from leaf2 to high end of leaf1.
+ */
+ xfs_attr_leaf_moveents(leaf2, 0, leaf1,
+ (int)INT_GET(hdr1->count, ARCH_CONVERT), count,
+ state->mp);
+
+ xfs_da_log_buf(args->trans, blk1->bp, 0, state->blocksize-1);
+ xfs_da_log_buf(args->trans, blk2->bp, 0, state->blocksize-1);
+ }
+
+ /*
+ * Copy out last hashval in each block for B-tree code.
+ */
+ blk1->hashval =
+ INT_GET(leaf1->entries[INT_GET(leaf1->hdr.count,
+ ARCH_CONVERT)-1].hashval, ARCH_CONVERT);
+ blk2->hashval =
+ INT_GET(leaf2->entries[INT_GET(leaf2->hdr.count,
+ ARCH_CONVERT)-1].hashval, ARCH_CONVERT);
+
+ /*
+ * Adjust the expected index for insertion.
+ * NOTE: this code depends on the (current) situation that the
+ * second block was originally empty.
+ *
+ * If the insertion point moved to the 2nd block, we must adjust
+ * the index. We must also track the entry just following the
+ * new entry for use in an "atomic rename" operation, that entry
+ * is always the "old" entry and the "new" entry is what we are
+ * inserting. The index/blkno fields refer to the "old" entry,
+ * while the index2/blkno2 fields refer to the "new" entry.
+ */
+ if (blk1->index > INT_GET(leaf1->hdr.count, ARCH_CONVERT)) {
+ ASSERT(state->inleaf == 0);
+ blk2->index = blk1->index
+ - INT_GET(leaf1->hdr.count, ARCH_CONVERT);
+ args->index = args->index2 = blk2->index;
+ args->blkno = args->blkno2 = blk2->blkno;
+ } else if (blk1->index == INT_GET(leaf1->hdr.count, ARCH_CONVERT)) {
+ if (state->inleaf) {
+ args->index = blk1->index;
+ args->blkno = blk1->blkno;
+ args->index2 = 0;
+ args->blkno2 = blk2->blkno;
+ } else {
+ blk2->index = blk1->index
+ - INT_GET(leaf1->hdr.count, ARCH_CONVERT);
+ args->index = args->index2 = blk2->index;
+ args->blkno = args->blkno2 = blk2->blkno;
+ }
+ } else {
+ ASSERT(state->inleaf == 1);
+ args->index = args->index2 = blk1->index;
+ args->blkno = args->blkno2 = blk1->blkno;
+ }
+}
+
+/*
+ * Examine entries until we reduce the absolute difference in
+ * byte usage between the two blocks to a minimum.
+ * GROT: Is this really necessary? With other than a 512 byte blocksize,
+ * GROT: there will always be enough room in either block for a new entry.
+ * GROT: Do a double-split for this case?
+ */
+STATIC int
+xfs_attr_leaf_figure_balance(xfs_da_state_t *state,
+ xfs_da_state_blk_t *blk1,
+ xfs_da_state_blk_t *blk2,
+ int *countarg, int *usedbytesarg)
+{
+ xfs_attr_leafblock_t *leaf1, *leaf2;
+ xfs_attr_leaf_hdr_t *hdr1, *hdr2;
+ xfs_attr_leaf_entry_t *entry;
+ int count, max, index, totallen, half;
+ int lastdelta, foundit, tmp;
+
+ /*
+ * Set up environment.
+ */
+ leaf1 = blk1->bp->data;
+ leaf2 = blk2->bp->data;
+ hdr1 = &leaf1->hdr;
+ hdr2 = &leaf2->hdr;
+ foundit = 0;
+ totallen = 0;
+
+ /*
+ * Examine entries until we reduce the absolute difference in
+ * byte usage between the two blocks to a minimum.
+ */
+ max = INT_GET(hdr1->count, ARCH_CONVERT)
+ + INT_GET(hdr2->count, ARCH_CONVERT);
+ half = (max+1) * sizeof(*entry);
+ half += INT_GET(hdr1->usedbytes, ARCH_CONVERT)
+ + INT_GET(hdr2->usedbytes, ARCH_CONVERT)
+ + xfs_attr_leaf_newentsize(state->args,
+ state->blocksize, NULL);
+ half /= 2;
+ lastdelta = state->blocksize;
+ entry = &leaf1->entries[0];
+ for (count = index = 0; count < max; entry++, index++, count++) {
+
+#define XFS_ATTR_ABS(A) (((A) < 0) ? -(A) : (A))
+ /*
+ * The new entry is in the first block, account for it.
+ */
+ if (count == blk1->index) {
+ tmp = totallen + sizeof(*entry) +
+ xfs_attr_leaf_newentsize(state->args,
+ state->blocksize,
+ NULL);
+ if (XFS_ATTR_ABS(half - tmp) > lastdelta)
+ break;
+ lastdelta = XFS_ATTR_ABS(half - tmp);
+ totallen = tmp;
+ foundit = 1;
+ }
+
+ /*
+ * Wrap around into the second block if necessary.
+ */
+ if (count == INT_GET(hdr1->count, ARCH_CONVERT)) {
+ leaf1 = leaf2;
+ entry = &leaf1->entries[0];
+ index = 0;
+ }
+
+ /*
+ * Figure out if next leaf entry would be too much.
+ */
+ tmp = totallen + sizeof(*entry) + xfs_attr_leaf_entsize(leaf1,
+ index);
+ if (XFS_ATTR_ABS(half - tmp) > lastdelta)
+ break;
+ lastdelta = XFS_ATTR_ABS(half - tmp);
+ totallen = tmp;
+#undef XFS_ATTR_ABS
+ }
+
+ /*
+ * Calculate the number of usedbytes that will end up in lower block.
+ * If new entry not in lower block, fix up the count.
+ */
+ totallen -= count * sizeof(*entry);
+ if (foundit) {
+ totallen -= sizeof(*entry) +
+ xfs_attr_leaf_newentsize(state->args,
+ state->blocksize,
+ NULL);
+ }
+
+ *countarg = count;
+ *usedbytesarg = totallen;
+ return(foundit);
+}
+
+/*========================================================================
+ * Routines used for shrinking the Btree.
+ *========================================================================*/
+
+/*
+ * Check a leaf block and its neighbors to see if the block should be
+ * collapsed into one or the other neighbor. Always keep the block
+ * with the smaller block number.
+ * If the current block is over 50% full, don't try to join it, return 0.
+ * If the block is empty, fill in the state structure and return 2.
+ * If it can be collapsed, fill in the state structure and return 1.
+ * If nothing can be done, return 0.
+ *
+ * GROT: allow for INCOMPLETE entries in calculation.
+ */
+int
+xfs_attr_leaf_toosmall(xfs_da_state_t *state, int *action)
+{
+ xfs_attr_leafblock_t *leaf;
+ xfs_da_state_blk_t *blk;
+ xfs_da_blkinfo_t *info;
+ int count, bytes, forward, error, retval, i;
+ xfs_dablk_t blkno;
+ xfs_dabuf_t *bp;
+
+ /*
+ * Check for the degenerate case of the block being over 50% full.
+ * If so, it's not worth even looking to see if we might be able
+ * to coalesce with a sibling.
+ */
+ blk = &state->path.blk[ state->path.active-1 ];
+ info = blk->bp->data;
+ ASSERT(INT_GET(info->magic, ARCH_CONVERT) == XFS_ATTR_LEAF_MAGIC);
+ leaf = (xfs_attr_leafblock_t *)info;
+ count = INT_GET(leaf->hdr.count, ARCH_CONVERT);
+ bytes = sizeof(xfs_attr_leaf_hdr_t) +
+ count * sizeof(xfs_attr_leaf_entry_t) +
+ INT_GET(leaf->hdr.usedbytes, ARCH_CONVERT);
+ if (bytes > (state->blocksize >> 1)) {
+ *action = 0; /* blk over 50%, don't try to join */
+ return(0);
+ }
+
+ /*
+ * Check for the degenerate case of the block being empty.
+ * If the block is empty, we'll simply delete it, no need to
+ * coalesce it with a sibling block. We choose (aribtrarily)
+ * to merge with the forward block unless it is NULL.
+ */
+ if (count == 0) {
+ /*
+ * Make altpath point to the block we want to keep and
+ * path point to the block we want to drop (this one).
+ */
+ forward = (!INT_ISZERO(info->forw, ARCH_CONVERT));
+ memcpy(&state->altpath, &state->path, sizeof(state->path));
+ error = xfs_da_path_shift(state, &state->altpath, forward,
+ 0, &retval);
+ if (error)
+ return(error);
+ if (retval) {
+ *action = 0;
+ } else {
+ *action = 2;
+ }
+ return(0);
+ }
+
+ /*
+ * Examine each sibling block to see if we can coalesce with
+ * at least 25% free space to spare. We need to figure out
+ * whether to merge with the forward or the backward block.
+ * We prefer coalescing with the lower numbered sibling so as
+ * to shrink an attribute list over time.
+ */
+ /* start with smaller blk num */
+ forward = (INT_GET(info->forw, ARCH_CONVERT)
+ < INT_GET(info->back, ARCH_CONVERT));
+ for (i = 0; i < 2; forward = !forward, i++) {
+ if (forward)
+ blkno = INT_GET(info->forw, ARCH_CONVERT);
+ else
+ blkno = INT_GET(info->back, ARCH_CONVERT);
+ if (blkno == 0)
+ continue;
+ error = xfs_da_read_buf(state->args->trans, state->args->dp,
+ blkno, -1, &bp, XFS_ATTR_FORK);
+ if (error)
+ return(error);
+ ASSERT(bp != NULL);
+
+ leaf = (xfs_attr_leafblock_t *)info;
+ count = INT_GET(leaf->hdr.count, ARCH_CONVERT);
+ bytes = state->blocksize - (state->blocksize>>2);
+ bytes -= INT_GET(leaf->hdr.usedbytes, ARCH_CONVERT);
+ leaf = bp->data;
+ ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT)
+ == XFS_ATTR_LEAF_MAGIC);
+ count += INT_GET(leaf->hdr.count, ARCH_CONVERT);
+ bytes -= INT_GET(leaf->hdr.usedbytes, ARCH_CONVERT);
+ bytes -= count * sizeof(xfs_attr_leaf_entry_t);
+ bytes -= sizeof(xfs_attr_leaf_hdr_t);
+ xfs_da_brelse(state->args->trans, bp);
+ if (bytes >= 0)
+ break; /* fits with at least 25% to spare */
+ }
+ if (i >= 2) {
+ *action = 0;
+ return(0);
+ }
+
+ /*
+ * Make altpath point to the block we want to keep (the lower
+ * numbered block) and path point to the block we want to drop.
+ */
+ memcpy(&state->altpath, &state->path, sizeof(state->path));
+ if (blkno < blk->blkno) {
+ error = xfs_da_path_shift(state, &state->altpath, forward,
+ 0, &retval);
+ } else {
+ error = xfs_da_path_shift(state, &state->path, forward,
+ 0, &retval);
+ }
+ if (error)
+ return(error);
+ if (retval) {
+ *action = 0;
+ } else {
+ *action = 1;
+ }
+ return(0);
+}
+
+/*
+ * Remove a name from the leaf attribute list structure.
+ *
+ * Return 1 if leaf is less than 37% full, 0 if >= 37% full.
+ * If two leaves are 37% full, when combined they will leave 25% free.
+ */
+int
+xfs_attr_leaf_remove(xfs_dabuf_t *bp, xfs_da_args_t *args)
+{
+ xfs_attr_leafblock_t *leaf;
+ xfs_attr_leaf_hdr_t *hdr;
+ xfs_attr_leaf_map_t *map;
+ xfs_attr_leaf_entry_t *entry;
+ int before, after, smallest, entsize;
+ int tablesize, tmp, i;
+ xfs_mount_t *mp;
+
+ leaf = bp->data;
+ ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT)
+ == XFS_ATTR_LEAF_MAGIC);
+ hdr = &leaf->hdr;
+ mp = args->trans->t_mountp;
+ ASSERT((INT_GET(hdr->count, ARCH_CONVERT) > 0)
+ && (INT_GET(hdr->count, ARCH_CONVERT) < (XFS_LBSIZE(mp)/8)));
+ ASSERT((args->index >= 0)
+ && (args->index < INT_GET(hdr->count, ARCH_CONVERT)));
+ ASSERT(INT_GET(hdr->firstused, ARCH_CONVERT)
+ >= ((INT_GET(hdr->count, ARCH_CONVERT)
+ * sizeof(*entry))+sizeof(*hdr)));
+ entry = &leaf->entries[args->index];
+ ASSERT(INT_GET(entry->nameidx, ARCH_CONVERT)
+ >= INT_GET(hdr->firstused, ARCH_CONVERT));
+ ASSERT(INT_GET(entry->nameidx, ARCH_CONVERT) < XFS_LBSIZE(mp));
+
+ /*
+ * Scan through free region table:
+ * check for adjacency of free'd entry with an existing one,
+ * find smallest free region in case we need to replace it,
+ * adjust any map that borders the entry table,
+ */
+ tablesize = INT_GET(hdr->count, ARCH_CONVERT)
+ * sizeof(xfs_attr_leaf_entry_t)
+ + sizeof(xfs_attr_leaf_hdr_t);
+ map = &hdr->freemap[0];
+ tmp = INT_GET(map->size, ARCH_CONVERT);
+ before = after = -1;
+ smallest = XFS_ATTR_LEAF_MAPSIZE - 1;
+ entsize = xfs_attr_leaf_entsize(leaf, args->index);
+ for (i = 0; i < XFS_ATTR_LEAF_MAPSIZE; map++, i++) {
+ ASSERT(INT_GET(map->base, ARCH_CONVERT) < XFS_LBSIZE(mp));
+ ASSERT(INT_GET(map->size, ARCH_CONVERT) < XFS_LBSIZE(mp));
+ if (INT_GET(map->base, ARCH_CONVERT) == tablesize) {
+ INT_MOD(map->base, ARCH_CONVERT,
+ -sizeof(xfs_attr_leaf_entry_t));
+ INT_MOD(map->size, ARCH_CONVERT,
+ sizeof(xfs_attr_leaf_entry_t));
+ }
+
+ if ((INT_GET(map->base, ARCH_CONVERT)
+ + INT_GET(map->size, ARCH_CONVERT))
+ == INT_GET(entry->nameidx, ARCH_CONVERT)) {
+ before = i;
+ } else if (INT_GET(map->base, ARCH_CONVERT)
+ == (INT_GET(entry->nameidx, ARCH_CONVERT) + entsize)) {
+ after = i;
+ } else if (INT_GET(map->size, ARCH_CONVERT) < tmp) {
+ tmp = INT_GET(map->size, ARCH_CONVERT);
+ smallest = i;
+ }
+ }
+
+ /*
+ * Coalesce adjacent freemap regions,
+ * or replace the smallest region.
+ */
+ if ((before >= 0) || (after >= 0)) {
+ if ((before >= 0) && (after >= 0)) {
+ map = &hdr->freemap[before];
+ INT_MOD(map->size, ARCH_CONVERT, entsize);
+ INT_MOD(map->size, ARCH_CONVERT,
+ INT_GET(hdr->freemap[after].size,
+ ARCH_CONVERT));
+ INT_ZERO(hdr->freemap[after].base, ARCH_CONVERT);
+ INT_ZERO(hdr->freemap[after].size, ARCH_CONVERT);
+ } else if (before >= 0) {
+ map = &hdr->freemap[before];
+ INT_MOD(map->size, ARCH_CONVERT, entsize);
+ } else {
+ map = &hdr->freemap[after];
+ /* both on-disk, don't endian flip twice */
+ map->base = entry->nameidx;
+ INT_MOD(map->size, ARCH_CONVERT, entsize);
+ }
+ } else {
+ /*
+ * Replace smallest region (if it is smaller than free'd entry)
+ */
+ map = &hdr->freemap[smallest];
+ if (INT_GET(map->size, ARCH_CONVERT) < entsize) {
+ INT_SET(map->base, ARCH_CONVERT,
+ INT_GET(entry->nameidx, ARCH_CONVERT));
+ INT_SET(map->size, ARCH_CONVERT, entsize);
+ }
+ }
+
+ /*
+ * Did we remove the first entry?
+ */
+ if (INT_GET(entry->nameidx, ARCH_CONVERT)
+ == INT_GET(hdr->firstused, ARCH_CONVERT))
+ smallest = 1;
+ else
+ smallest = 0;
+
+ /*
+ * Compress the remaining entries and zero out the removed stuff.
+ */
+ memset(XFS_ATTR_LEAF_NAME(leaf, args->index), 0, entsize);
+ INT_MOD(hdr->usedbytes, ARCH_CONVERT, -entsize);
+ xfs_da_log_buf(args->trans, bp,
+ XFS_DA_LOGRANGE(leaf, XFS_ATTR_LEAF_NAME(leaf, args->index),
+ entsize));
+
+ tmp = (INT_GET(hdr->count, ARCH_CONVERT) - args->index)
+ * sizeof(xfs_attr_leaf_entry_t);
+ memmove((char *)entry, (char *)(entry+1), tmp);
+ INT_MOD(hdr->count, ARCH_CONVERT, -1);
+ xfs_da_log_buf(args->trans, bp,
+ XFS_DA_LOGRANGE(leaf, entry, tmp + sizeof(*entry)));
+ entry = &leaf->entries[INT_GET(hdr->count, ARCH_CONVERT)];
+ memset((char *)entry, 0, sizeof(xfs_attr_leaf_entry_t));
+
+ /*
+ * If we removed the first entry, re-find the first used byte
+ * in the name area. Note that if the entry was the "firstused",
+ * then we don't have a "hole" in our block resulting from
+ * removing the name.
+ */
+ if (smallest) {
+ tmp = XFS_LBSIZE(mp);
+ entry = &leaf->entries[0];
+ for (i = INT_GET(hdr->count, ARCH_CONVERT)-1;
+ i >= 0; entry++, i--) {
+ ASSERT(INT_GET(entry->nameidx, ARCH_CONVERT)
+ >= INT_GET(hdr->firstused, ARCH_CONVERT));
+ ASSERT(INT_GET(entry->nameidx, ARCH_CONVERT)
+ < XFS_LBSIZE(mp));
+ if (INT_GET(entry->nameidx, ARCH_CONVERT) < tmp)
+ tmp = INT_GET(entry->nameidx, ARCH_CONVERT);
+ }
+ INT_SET(hdr->firstused, ARCH_CONVERT, tmp);
+ if (INT_ISZERO(hdr->firstused, ARCH_CONVERT)) {
+ INT_SET(hdr->firstused, ARCH_CONVERT,
+ tmp - XFS_ATTR_LEAF_NAME_ALIGN);
+ }
+ } else {
+ hdr->holes = 1; /* mark as needing compaction */
+ }
+ xfs_da_log_buf(args->trans, bp,
+ XFS_DA_LOGRANGE(leaf, hdr, sizeof(*hdr)));
+
+ /*
+ * Check if leaf is less than 50% full, caller may want to
+ * "join" the leaf with a sibling if so.
+ */
+ tmp = sizeof(xfs_attr_leaf_hdr_t);
+ tmp += INT_GET(leaf->hdr.count, ARCH_CONVERT)
+ * sizeof(xfs_attr_leaf_entry_t);
+ tmp += INT_GET(leaf->hdr.usedbytes, ARCH_CONVERT);
+ return(tmp < mp->m_attr_magicpct); /* leaf is < 37% full */
+}
+
+/*
+ * Move all the attribute list entries from drop_leaf into save_leaf.
+ */
+void
+xfs_attr_leaf_unbalance(xfs_da_state_t *state, xfs_da_state_blk_t *drop_blk,
+ xfs_da_state_blk_t *save_blk)
+{
+ xfs_attr_leafblock_t *drop_leaf, *save_leaf, *tmp_leaf;
+ xfs_attr_leaf_hdr_t *drop_hdr, *save_hdr, *tmp_hdr;
+ xfs_mount_t *mp;
+ char *tmpbuffer;
+
+ /*
+ * Set up environment.
+ */
+ mp = state->mp;
+ ASSERT(drop_blk->magic == XFS_ATTR_LEAF_MAGIC);
+ ASSERT(save_blk->magic == XFS_ATTR_LEAF_MAGIC);
+ drop_leaf = drop_blk->bp->data;
+ save_leaf = save_blk->bp->data;
+ ASSERT(INT_GET(drop_leaf->hdr.info.magic, ARCH_CONVERT)
+ == XFS_ATTR_LEAF_MAGIC);
+ ASSERT(INT_GET(save_leaf->hdr.info.magic, ARCH_CONVERT)
+ == XFS_ATTR_LEAF_MAGIC);
+ drop_hdr = &drop_leaf->hdr;
+ save_hdr = &save_leaf->hdr;
+
+ /*
+ * Save last hashval from dying block for later Btree fixup.
+ */
+ drop_blk->hashval =
+ INT_GET(drop_leaf->entries[INT_GET(drop_leaf->hdr.count,
+ ARCH_CONVERT)-1].hashval,
+ ARCH_CONVERT);
+
+ /*
+ * Check if we need a temp buffer, or can we do it in place.
+ * Note that we don't check "leaf" for holes because we will
+ * always be dropping it, toosmall() decided that for us already.
+ */
+ if (save_hdr->holes == 0) {
+ /*
+ * dest leaf has no holes, so we add there. May need
+ * to make some room in the entry array.
+ */
+ if (xfs_attr_leaf_order(save_blk->bp, drop_blk->bp)) {
+ xfs_attr_leaf_moveents(drop_leaf, 0, save_leaf, 0,
+ (int)INT_GET(drop_hdr->count, ARCH_CONVERT), mp);
+ } else {
+ xfs_attr_leaf_moveents(drop_leaf, 0, save_leaf,
+ INT_GET(save_hdr->count, ARCH_CONVERT),
+ (int)INT_GET(drop_hdr->count, ARCH_CONVERT),
+ mp);
+ }
+ } else {
+ /*
+ * Destination has holes, so we make a temporary copy
+ * of the leaf and add them both to that.
+ */
+ tmpbuffer = kmem_alloc(state->blocksize, KM_SLEEP);
+ ASSERT(tmpbuffer != NULL);
+ memset(tmpbuffer, 0, state->blocksize);
+ tmp_leaf = (xfs_attr_leafblock_t *)tmpbuffer;
+ tmp_hdr = &tmp_leaf->hdr;
+ tmp_hdr->info = save_hdr->info; /* struct copy */
+ INT_ZERO(tmp_hdr->count, ARCH_CONVERT);
+ INT_SET(tmp_hdr->firstused, ARCH_CONVERT, state->blocksize);
+ if (INT_ISZERO(tmp_hdr->firstused, ARCH_CONVERT)) {
+ INT_SET(tmp_hdr->firstused, ARCH_CONVERT,
+ state->blocksize - XFS_ATTR_LEAF_NAME_ALIGN);
+ }
+ INT_ZERO(tmp_hdr->usedbytes, ARCH_CONVERT);
+ if (xfs_attr_leaf_order(save_blk->bp, drop_blk->bp)) {
+ xfs_attr_leaf_moveents(drop_leaf, 0, tmp_leaf, 0,
+ (int)INT_GET(drop_hdr->count, ARCH_CONVERT),
+ mp);
+ xfs_attr_leaf_moveents(save_leaf, 0, tmp_leaf,
+ INT_GET(tmp_leaf->hdr.count, ARCH_CONVERT),
+ (int)INT_GET(save_hdr->count, ARCH_CONVERT),
+ mp);
+ } else {
+ xfs_attr_leaf_moveents(save_leaf, 0, tmp_leaf, 0,
+ (int)INT_GET(save_hdr->count, ARCH_CONVERT),
+ mp);
+ xfs_attr_leaf_moveents(drop_leaf, 0, tmp_leaf,
+ INT_GET(tmp_leaf->hdr.count, ARCH_CONVERT),
+ (int)INT_GET(drop_hdr->count, ARCH_CONVERT),
+ mp);
+ }
+ memcpy((char *)save_leaf, (char *)tmp_leaf, state->blocksize);
+ kmem_free(tmpbuffer, state->blocksize);
+ }
+
+ xfs_da_log_buf(state->args->trans, save_blk->bp, 0,
+ state->blocksize - 1);
+
+ /*
+ * Copy out last hashval in each block for B-tree code.
+ */
+ save_blk->hashval =
+ INT_GET(save_leaf->entries[INT_GET(save_leaf->hdr.count,
+ ARCH_CONVERT)-1].hashval,
+ ARCH_CONVERT);
+}
+
+/*========================================================================
+ * Routines used for finding things in the Btree.
+ *========================================================================*/
+
+/*
+ * Look up a name in a leaf attribute list structure.
+ * This is the internal routine, it uses the caller's buffer.
+ *
+ * Note that duplicate keys are allowed, but only check within the
+ * current leaf node. The Btree code must check in adjacent leaf nodes.
+ *
+ * Return in args->index the index into the entry[] array of either
+ * the found entry, or where the entry should have been (insert before
+ * that entry).
+ *
+ * Don't change the args->value unless we find the attribute.
+ */
+int
+xfs_attr_leaf_lookup_int(xfs_dabuf_t *bp, xfs_da_args_t *args)
+{
+ xfs_attr_leafblock_t *leaf;
+ xfs_attr_leaf_entry_t *entry;
+ xfs_attr_leaf_name_local_t *name_loc;
+ xfs_attr_leaf_name_remote_t *name_rmt;
+ int probe, span;
+ xfs_dahash_t hashval;
+
+ leaf = bp->data;
+ ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT)
+ == XFS_ATTR_LEAF_MAGIC);
+ ASSERT(INT_GET(leaf->hdr.count, ARCH_CONVERT)
+ < (XFS_LBSIZE(args->dp->i_mount)/8));
+
+ /*
+ * Binary search. (note: small blocks will skip this loop)
+ */
+ hashval = args->hashval;
+ probe = span = INT_GET(leaf->hdr.count, ARCH_CONVERT) / 2;
+ for (entry = &leaf->entries[probe]; span > 4;
+ entry = &leaf->entries[probe]) {
+ span /= 2;
+ if (INT_GET(entry->hashval, ARCH_CONVERT) < hashval)
+ probe += span;
+ else if (INT_GET(entry->hashval, ARCH_CONVERT) > hashval)
+ probe -= span;
+ else
+ break;
+ }
+ ASSERT((probe >= 0) && \
+ ((INT_ISZERO(leaf->hdr.count, ARCH_CONVERT))
+ || (probe < INT_GET(leaf->hdr.count, ARCH_CONVERT))));
+ ASSERT((span <= 4) || (INT_GET(entry->hashval, ARCH_CONVERT)
+ == hashval));
+
+ /*
+ * Since we may have duplicate hashval's, find the first matching
+ * hashval in the leaf.
+ */
+ while ((probe > 0) && (INT_GET(entry->hashval, ARCH_CONVERT)
+ >= hashval)) {
+ entry--;
+ probe--;
+ }
+ while ((probe < INT_GET(leaf->hdr.count, ARCH_CONVERT))
+ && (INT_GET(entry->hashval, ARCH_CONVERT) < hashval)) {
+ entry++;
+ probe++;
+ }
+ if ((probe == INT_GET(leaf->hdr.count, ARCH_CONVERT))
+ || (INT_GET(entry->hashval, ARCH_CONVERT) != hashval)) {
+ args->index = probe;
+ return(XFS_ERROR(ENOATTR));
+ }
+
+ /*
+ * Duplicate keys may be present, so search all of them for a match.
+ */
+ for ( ; (probe < INT_GET(leaf->hdr.count, ARCH_CONVERT))
+ && (INT_GET(entry->hashval, ARCH_CONVERT) == hashval);
+ entry++, probe++) {
+/*
+ * GROT: Add code to remove incomplete entries.
+ */
+ /*
+ * If we are looking for INCOMPLETE entries, show only those.
+ * If we are looking for complete entries, show only those.
+ */
+ if ((args->flags & XFS_ATTR_INCOMPLETE) !=
+ (entry->flags & XFS_ATTR_INCOMPLETE)) {
+ continue;
+ }
+ if (entry->flags & XFS_ATTR_LOCAL) {
+ name_loc = XFS_ATTR_LEAF_NAME_LOCAL(leaf, probe);
+ if (name_loc->namelen != args->namelen)
+ continue;
+ if (memcmp(args->name, (char *)name_loc->nameval,
+ args->namelen) != 0)
+ continue;
+ if (((args->flags & ATTR_SECURE) != 0) !=
+ ((entry->flags & XFS_ATTR_SECURE) != 0))
+ continue;
+ if (((args->flags & ATTR_ROOT) != 0) !=
+ ((entry->flags & XFS_ATTR_ROOT) != 0))
+ continue;
+ args->index = probe;
+ return(XFS_ERROR(EEXIST));
+ } else {
+ name_rmt = XFS_ATTR_LEAF_NAME_REMOTE(leaf, probe);
+ if (name_rmt->namelen != args->namelen)
+ continue;
+ if (memcmp(args->name, (char *)name_rmt->name,
+ args->namelen) != 0)
+ continue;
+ if (((args->flags & ATTR_SECURE) != 0) !=
+ ((entry->flags & XFS_ATTR_SECURE) != 0))
+ continue;
+ if (((args->flags & ATTR_ROOT) != 0) !=
+ ((entry->flags & XFS_ATTR_ROOT) != 0))
+ continue;
+ args->index = probe;
+ args->rmtblkno
+ = INT_GET(name_rmt->valueblk, ARCH_CONVERT);
+ args->rmtblkcnt = XFS_B_TO_FSB(args->dp->i_mount,
+ INT_GET(name_rmt->valuelen,
+ ARCH_CONVERT));
+ return(XFS_ERROR(EEXIST));
+ }
+ }
+ args->index = probe;
+ return(XFS_ERROR(ENOATTR));
+}
+
+/*
+ * Get the value associated with an attribute name from a leaf attribute
+ * list structure.
+ */
+int
+xfs_attr_leaf_getvalue(xfs_dabuf_t *bp, xfs_da_args_t *args)
+{
+ int valuelen;
+ xfs_attr_leafblock_t *leaf;
+ xfs_attr_leaf_entry_t *entry;
+ xfs_attr_leaf_name_local_t *name_loc;
+ xfs_attr_leaf_name_remote_t *name_rmt;
+
+ leaf = bp->data;
+ ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT)
+ == XFS_ATTR_LEAF_MAGIC);
+ ASSERT(INT_GET(leaf->hdr.count, ARCH_CONVERT)
+ < (XFS_LBSIZE(args->dp->i_mount)/8));
+ ASSERT(args->index < ((int)INT_GET(leaf->hdr.count, ARCH_CONVERT)));
+
+ entry = &leaf->entries[args->index];
+ if (entry->flags & XFS_ATTR_LOCAL) {
+ name_loc = XFS_ATTR_LEAF_NAME_LOCAL(leaf, args->index);
+ ASSERT(name_loc->namelen == args->namelen);
+ ASSERT(memcmp(args->name, name_loc->nameval, args->namelen) == 0);
+ valuelen = INT_GET(name_loc->valuelen, ARCH_CONVERT);
+ if (args->flags & ATTR_KERNOVAL) {
+ args->valuelen = valuelen;
+ return(0);
+ }
+ if (args->valuelen < valuelen) {
+ args->valuelen = valuelen;
+ return(XFS_ERROR(ERANGE));
+ }
+ args->valuelen = valuelen;
+ memcpy(args->value, &name_loc->nameval[args->namelen], valuelen);
+ } else {
+ name_rmt = XFS_ATTR_LEAF_NAME_REMOTE(leaf, args->index);
+ ASSERT(name_rmt->namelen == args->namelen);
+ ASSERT(memcmp(args->name, name_rmt->name, args->namelen) == 0);
+ valuelen = INT_GET(name_rmt->valuelen, ARCH_CONVERT);
+ args->rmtblkno = INT_GET(name_rmt->valueblk, ARCH_CONVERT);
+ args->rmtblkcnt = XFS_B_TO_FSB(args->dp->i_mount, valuelen);
+ if (args->flags & ATTR_KERNOVAL) {
+ args->valuelen = valuelen;
+ return(0);
+ }
+ if (args->valuelen < valuelen) {
+ args->valuelen = valuelen;
+ return(XFS_ERROR(ERANGE));
+ }
+ args->valuelen = valuelen;
+ }
+ return(0);
+}
+
+/*========================================================================
+ * Utility routines.
+ *========================================================================*/
+
+/*
+ * Move the indicated entries from one leaf to another.
+ * NOTE: this routine modifies both source and destination leaves.
+ */
+/*ARGSUSED*/
+STATIC void
+xfs_attr_leaf_moveents(xfs_attr_leafblock_t *leaf_s, int start_s,
+ xfs_attr_leafblock_t *leaf_d, int start_d,
+ int count, xfs_mount_t *mp)
+{
+ xfs_attr_leaf_hdr_t *hdr_s, *hdr_d;
+ xfs_attr_leaf_entry_t *entry_s, *entry_d;
+ int desti, tmp, i;
+
+ /*
+ * Check for nothing to do.
+ */
+ if (count == 0)
+ return;
+
+ /*
+ * Set up environment.
+ */
+ ASSERT(INT_GET(leaf_s->hdr.info.magic, ARCH_CONVERT)
+ == XFS_ATTR_LEAF_MAGIC);
+ ASSERT(INT_GET(leaf_d->hdr.info.magic, ARCH_CONVERT)
+ == XFS_ATTR_LEAF_MAGIC);
+ hdr_s = &leaf_s->hdr;
+ hdr_d = &leaf_d->hdr;
+ ASSERT((INT_GET(hdr_s->count, ARCH_CONVERT) > 0)
+ && (INT_GET(hdr_s->count, ARCH_CONVERT)
+ < (XFS_LBSIZE(mp)/8)));
+ ASSERT(INT_GET(hdr_s->firstused, ARCH_CONVERT) >=
+ ((INT_GET(hdr_s->count, ARCH_CONVERT)
+ * sizeof(*entry_s))+sizeof(*hdr_s)));
+ ASSERT(INT_GET(hdr_d->count, ARCH_CONVERT) < (XFS_LBSIZE(mp)/8));
+ ASSERT(INT_GET(hdr_d->firstused, ARCH_CONVERT) >=
+ ((INT_GET(hdr_d->count, ARCH_CONVERT)
+ * sizeof(*entry_d))+sizeof(*hdr_d)));
+
+ ASSERT(start_s < INT_GET(hdr_s->count, ARCH_CONVERT));
+ ASSERT(start_d <= INT_GET(hdr_d->count, ARCH_CONVERT));
+ ASSERT(count <= INT_GET(hdr_s->count, ARCH_CONVERT));
+
+ /*
+ * Move the entries in the destination leaf up to make a hole?
+ */
+ if (start_d < INT_GET(hdr_d->count, ARCH_CONVERT)) {
+ tmp = INT_GET(hdr_d->count, ARCH_CONVERT) - start_d;
+ tmp *= sizeof(xfs_attr_leaf_entry_t);
+ entry_s = &leaf_d->entries[start_d];
+ entry_d = &leaf_d->entries[start_d + count];
+ memmove((char *)entry_d, (char *)entry_s, tmp);
+ }
+
+ /*
+ * Copy all entry's in the same (sorted) order,
+ * but allocate attribute info packed and in sequence.
+ */
+ entry_s = &leaf_s->entries[start_s];
+ entry_d = &leaf_d->entries[start_d];
+ desti = start_d;
+ for (i = 0; i < count; entry_s++, entry_d++, desti++, i++) {
+ ASSERT(INT_GET(entry_s->nameidx, ARCH_CONVERT)
+ >= INT_GET(hdr_s->firstused, ARCH_CONVERT));
+ tmp = xfs_attr_leaf_entsize(leaf_s, start_s + i);
+#ifdef GROT
+ /*
+ * Code to drop INCOMPLETE entries. Difficult to use as we
+ * may also need to change the insertion index. Code turned
+ * off for 6.2, should be revisited later.
+ */
+ if (entry_s->flags & XFS_ATTR_INCOMPLETE) { /* skip partials? */
+ memset(XFS_ATTR_LEAF_NAME(leaf_s, start_s + i), 0, tmp);
+ INT_MOD(hdr_s->usedbytes, ARCH_CONVERT, -tmp);
+ INT_MOD(hdr_s->count, ARCH_CONVERT, -1);
+ entry_d--; /* to compensate for ++ in loop hdr */
+ desti--;
+ if ((start_s + i) < offset)
+ result++; /* insertion index adjustment */
+ } else {
+#endif /* GROT */
+ INT_MOD(hdr_d->firstused, ARCH_CONVERT, -tmp);
+ /* both on-disk, don't endian flip twice */
+ entry_d->hashval = entry_s->hashval;
+ /* both on-disk, don't endian flip twice */
+ entry_d->nameidx = hdr_d->firstused;
+ entry_d->flags = entry_s->flags;
+ ASSERT(INT_GET(entry_d->nameidx, ARCH_CONVERT) + tmp
+ <= XFS_LBSIZE(mp));
+ memmove(XFS_ATTR_LEAF_NAME(leaf_d, desti),
+ XFS_ATTR_LEAF_NAME(leaf_s, start_s + i), tmp);
+ ASSERT(INT_GET(entry_s->nameidx, ARCH_CONVERT) + tmp
+ <= XFS_LBSIZE(mp));
+ memset(XFS_ATTR_LEAF_NAME(leaf_s, start_s + i), 0, tmp);
+ INT_MOD(hdr_s->usedbytes, ARCH_CONVERT, -tmp);
+ INT_MOD(hdr_d->usedbytes, ARCH_CONVERT, tmp);
+ INT_MOD(hdr_s->count, ARCH_CONVERT, -1);
+ INT_MOD(hdr_d->count, ARCH_CONVERT, 1);
+ tmp = INT_GET(hdr_d->count, ARCH_CONVERT)
+ * sizeof(xfs_attr_leaf_entry_t)
+ + sizeof(xfs_attr_leaf_hdr_t);
+ ASSERT(INT_GET(hdr_d->firstused, ARCH_CONVERT) >= tmp);
+#ifdef GROT
+ }
+#endif /* GROT */
+ }
+
+ /*
+ * Zero out the entries we just copied.
+ */
+ if (start_s == INT_GET(hdr_s->count, ARCH_CONVERT)) {
+ tmp = count * sizeof(xfs_attr_leaf_entry_t);
+ entry_s = &leaf_s->entries[start_s];
+ ASSERT(((char *)entry_s + tmp) <=
+ ((char *)leaf_s + XFS_LBSIZE(mp)));
+ memset((char *)entry_s, 0, tmp);
+ } else {
+ /*
+ * Move the remaining entries down to fill the hole,
+ * then zero the entries at the top.
+ */
+ tmp = INT_GET(hdr_s->count, ARCH_CONVERT) - count;
+ tmp *= sizeof(xfs_attr_leaf_entry_t);
+ entry_s = &leaf_s->entries[start_s + count];
+ entry_d = &leaf_s->entries[start_s];
+ memmove((char *)entry_d, (char *)entry_s, tmp);
+
+ tmp = count * sizeof(xfs_attr_leaf_entry_t);
+ entry_s = &leaf_s->entries[INT_GET(hdr_s->count,
+ ARCH_CONVERT)];
+ ASSERT(((char *)entry_s + tmp) <=
+ ((char *)leaf_s + XFS_LBSIZE(mp)));
+ memset((char *)entry_s, 0, tmp);
+ }
+
+ /*
+ * Fill in the freemap information
+ */
+ INT_SET(hdr_d->freemap[0].base, ARCH_CONVERT,
+ sizeof(xfs_attr_leaf_hdr_t));
+ INT_MOD(hdr_d->freemap[0].base, ARCH_CONVERT,
+ INT_GET(hdr_d->count, ARCH_CONVERT)
+ * sizeof(xfs_attr_leaf_entry_t));
+ INT_SET(hdr_d->freemap[0].size, ARCH_CONVERT,
+ INT_GET(hdr_d->firstused, ARCH_CONVERT)
+ - INT_GET(hdr_d->freemap[0].base, ARCH_CONVERT));
+ INT_ZERO(hdr_d->freemap[1].base, ARCH_CONVERT);
+ INT_ZERO(hdr_d->freemap[2].base, ARCH_CONVERT);
+ INT_ZERO(hdr_d->freemap[1].size, ARCH_CONVERT);
+ INT_ZERO(hdr_d->freemap[2].size, ARCH_CONVERT);
+ hdr_s->holes = 1; /* leaf may not be compact */
+}
+
+/*
+ * Compare two leaf blocks "order".
+ * Return 0 unless leaf2 should go before leaf1.
+ */
+int
+xfs_attr_leaf_order(xfs_dabuf_t *leaf1_bp, xfs_dabuf_t *leaf2_bp)
+{
+ xfs_attr_leafblock_t *leaf1, *leaf2;
+
+ leaf1 = leaf1_bp->data;
+ leaf2 = leaf2_bp->data;
+ ASSERT((INT_GET(leaf1->hdr.info.magic, ARCH_CONVERT)
+ == XFS_ATTR_LEAF_MAGIC) &&
+ (INT_GET(leaf2->hdr.info.magic, ARCH_CONVERT)
+ == XFS_ATTR_LEAF_MAGIC));
+ if ( (INT_GET(leaf1->hdr.count, ARCH_CONVERT) > 0)
+ && (INT_GET(leaf2->hdr.count, ARCH_CONVERT) > 0)
+ && ( (INT_GET(leaf2->entries[ 0 ].hashval, ARCH_CONVERT) <
+ INT_GET(leaf1->entries[ 0 ].hashval, ARCH_CONVERT))
+ || (INT_GET(leaf2->entries[INT_GET(leaf2->hdr.count,
+ ARCH_CONVERT)-1].hashval, ARCH_CONVERT) <
+ INT_GET(leaf1->entries[INT_GET(leaf1->hdr.count,
+ ARCH_CONVERT)-1].hashval, ARCH_CONVERT))) ) {
+ return(1);
+ }
+ return(0);
+}
+
+/*
+ * Pick up the last hashvalue from a leaf block.
+ */
+xfs_dahash_t
+xfs_attr_leaf_lasthash(xfs_dabuf_t *bp, int *count)
+{
+ xfs_attr_leafblock_t *leaf;
+
+ leaf = bp->data;
+ ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT)
+ == XFS_ATTR_LEAF_MAGIC);
+ if (count)
+ *count = INT_GET(leaf->hdr.count, ARCH_CONVERT);
+ if (INT_ISZERO(leaf->hdr.count, ARCH_CONVERT))
+ return(0);
+ return(INT_GET(leaf->entries[INT_GET(leaf->hdr.count,
+ ARCH_CONVERT)-1].hashval, ARCH_CONVERT));
+}
+
+/*
+ * Calculate the number of bytes used to store the indicated attribute
+ * (whether local or remote only calculate bytes in this block).
+ */
+int
+xfs_attr_leaf_entsize(xfs_attr_leafblock_t *leaf, int index)
+{
+ xfs_attr_leaf_name_local_t *name_loc;
+ xfs_attr_leaf_name_remote_t *name_rmt;
+ int size;
+
+ ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT)
+ == XFS_ATTR_LEAF_MAGIC);
+ if (leaf->entries[index].flags & XFS_ATTR_LOCAL) {
+ name_loc = XFS_ATTR_LEAF_NAME_LOCAL(leaf, index);
+ size = XFS_ATTR_LEAF_ENTSIZE_LOCAL(name_loc->namelen,
+ INT_GET(name_loc->valuelen,
+ ARCH_CONVERT));
+ } else {
+ name_rmt = XFS_ATTR_LEAF_NAME_REMOTE(leaf, index);
+ size = XFS_ATTR_LEAF_ENTSIZE_REMOTE(name_rmt->namelen);
+ }
+ return(size);
+}
+
+/*
+ * Calculate the number of bytes that would be required to store the new
+ * attribute (whether local or remote only calculate bytes in this block).
+ * This routine decides as a side effect whether the attribute will be
+ * a "local" or a "remote" attribute.
+ */
+int
+xfs_attr_leaf_newentsize(xfs_da_args_t *args, int blocksize, int *local)
+{
+ int size;
+
+ size = XFS_ATTR_LEAF_ENTSIZE_LOCAL(args->namelen, args->valuelen);
+ if (size < XFS_ATTR_LEAF_ENTSIZE_LOCAL_MAX(blocksize)) {
+ if (local) {
+ *local = 1;
+ }
+ } else {
+ size = XFS_ATTR_LEAF_ENTSIZE_REMOTE(args->namelen);
+ if (local) {
+ *local = 0;
+ }
+ }
+ return(size);
+}
+
+/*
+ * Copy out attribute list entries for attr_list(), for leaf attribute lists.
+ */
+int
+xfs_attr_leaf_list_int(xfs_dabuf_t *bp, xfs_attr_list_context_t *context)
+{
+ attrlist_cursor_kern_t *cursor;
+ xfs_attr_leafblock_t *leaf;
+ xfs_attr_leaf_entry_t *entry;
+ xfs_attr_leaf_name_local_t *name_loc;
+ xfs_attr_leaf_name_remote_t *name_rmt;
+ int retval, i;
+
+ ASSERT(bp != NULL);
+ leaf = bp->data;
+ cursor = context->cursor;
+ cursor->initted = 1;
+
+ xfs_attr_trace_l_cl("blk start", context, leaf);
+
+ /*
+ * Re-find our place in the leaf block if this is a new syscall.
+ */
+ if (context->resynch) {
+ entry = &leaf->entries[0];
+ for (i = 0; i < INT_GET(leaf->hdr.count, ARCH_CONVERT);
+ entry++, i++) {
+ if (INT_GET(entry->hashval, ARCH_CONVERT)
+ == cursor->hashval) {
+ if (cursor->offset == context->dupcnt) {
+ context->dupcnt = 0;
+ break;
+ }
+ context->dupcnt++;
+ } else if (INT_GET(entry->hashval, ARCH_CONVERT)
+ > cursor->hashval) {
+ context->dupcnt = 0;
+ break;
+ }
+ }
+ if (i == INT_GET(leaf->hdr.count, ARCH_CONVERT)) {
+ xfs_attr_trace_l_c("not found", context);
+ return(0);
+ }
+ } else {
+ entry = &leaf->entries[0];
+ i = 0;
+ }
+ context->resynch = 0;
+
+ /*
+ * We have found our place, start copying out the new attributes.
+ */
+ retval = 0;
+ for ( ; (i < INT_GET(leaf->hdr.count, ARCH_CONVERT))
+ && (retval == 0); entry++, i++) {
+ attrnames_t *namesp;
+
+ if (INT_GET(entry->hashval, ARCH_CONVERT) != cursor->hashval) {
+ cursor->hashval = INT_GET(entry->hashval, ARCH_CONVERT);
+ cursor->offset = 0;
+ }
+
+ if (entry->flags & XFS_ATTR_INCOMPLETE)
+ continue; /* skip incomplete entries */
+ if (((context->flags & ATTR_SECURE) != 0) !=
+ ((entry->flags & XFS_ATTR_SECURE) != 0) &&
+ !(context->flags & ATTR_KERNORMALS))
+ continue; /* skip non-matching entries */
+ if (((context->flags & ATTR_ROOT) != 0) !=
+ ((entry->flags & XFS_ATTR_ROOT) != 0) &&
+ !(context->flags & ATTR_KERNROOTLS))
+ continue; /* skip non-matching entries */
+
+ namesp = (entry->flags & XFS_ATTR_SECURE) ? &attr_secure :
+ ((entry->flags & XFS_ATTR_ROOT) ? &attr_trusted :
+ &attr_user);
+
+ if (entry->flags & XFS_ATTR_LOCAL) {
+ name_loc = XFS_ATTR_LEAF_NAME_LOCAL(leaf, i);
+ if (context->flags & ATTR_KERNOVAL) {
+ ASSERT(context->flags & ATTR_KERNAMELS);
+ context->count += namesp->attr_namelen +
+ (int)name_loc->namelen + 1;
+ } else {
+ retval = xfs_attr_put_listent(context, namesp,
+ (char *)name_loc->nameval,
+ (int)name_loc->namelen,
+ (int)INT_GET(name_loc->valuelen,
+ ARCH_CONVERT));
+ }
+ } else {
+ name_rmt = XFS_ATTR_LEAF_NAME_REMOTE(leaf, i);
+ if (context->flags & ATTR_KERNOVAL) {
+ ASSERT(context->flags & ATTR_KERNAMELS);
+ context->count += namesp->attr_namelen +
+ (int)name_rmt->namelen + 1;
+ } else {
+ retval = xfs_attr_put_listent(context, namesp,
+ (char *)name_rmt->name,
+ (int)name_rmt->namelen,
+ (int)INT_GET(name_rmt->valuelen,
+ ARCH_CONVERT));
+ }
+ }
+ if (retval == 0) {
+ cursor->offset++;
+ }
+ }
+ xfs_attr_trace_l_cl("blk end", context, leaf);
+ return(retval);
+}
+
+#define ATTR_ENTBASESIZE /* minimum bytes used by an attr */ \
+ (((struct attrlist_ent *) 0)->a_name - (char *) 0)
+#define ATTR_ENTSIZE(namelen) /* actual bytes used by an attr */ \
+ ((ATTR_ENTBASESIZE + (namelen) + 1 + sizeof(u_int32_t)-1) \
+ & ~(sizeof(u_int32_t)-1))
+
+/*
+ * Format an attribute and copy it out to the user's buffer.
+ * Take care to check values and protect against them changing later,
+ * we may be reading them directly out of a user buffer.
+ */
+/*ARGSUSED*/
+int
+xfs_attr_put_listent(xfs_attr_list_context_t *context,
+ attrnames_t *namesp, char *name, int namelen, int valuelen)
+{
+ attrlist_ent_t *aep;
+ int arraytop;
+
+ ASSERT(!(context->flags & ATTR_KERNOVAL));
+ if (context->flags & ATTR_KERNAMELS) {
+ char *offset;
+
+ ASSERT(context->count >= 0);
+
+ arraytop = context->count + namesp->attr_namelen + namelen + 1;
+ if (arraytop > context->firstu) {
+ context->count = -1; /* insufficient space */
+ return(1);
+ }
+ offset = (char *)context->alist + context->count;
+ strncpy(offset, namesp->attr_name, namesp->attr_namelen);
+ offset += namesp->attr_namelen;
+ strncpy(offset, name, namelen); /* real name */
+ offset += namelen;
+ *offset = '\0';
+ context->count += namesp->attr_namelen + namelen + 1;
+ return(0);
+ }
+
+ ASSERT(context->count >= 0);
+ ASSERT(context->count < (ATTR_MAX_VALUELEN/8));
+ ASSERT(context->firstu >= sizeof(*context->alist));
+ ASSERT(context->firstu <= context->bufsize);
+
+ arraytop = sizeof(*context->alist) +
+ context->count * sizeof(context->alist->al_offset[0]);
+ context->firstu -= ATTR_ENTSIZE(namelen);
+ if (context->firstu < arraytop) {
+ xfs_attr_trace_l_c("buffer full", context);
+ context->alist->al_more = 1;
+ return(1);
+ }
+
+ aep = (attrlist_ent_t *)&(((char *)context->alist)[ context->firstu ]);
+ aep->a_valuelen = valuelen;
+ memcpy(aep->a_name, name, namelen);
+ aep->a_name[ namelen ] = 0;
+ context->alist->al_offset[ context->count++ ] = context->firstu;
+ context->alist->al_count = context->count;
+ xfs_attr_trace_l_c("add", context);
+ return(0);
+}
+
+/*========================================================================
+ * Manage the INCOMPLETE flag in a leaf entry
+ *========================================================================*/
+
+/*
+ * Clear the INCOMPLETE flag on an entry in a leaf block.
+ */
+int
+xfs_attr_leaf_clearflag(xfs_da_args_t *args)
+{
+ xfs_attr_leafblock_t *leaf;
+ xfs_attr_leaf_entry_t *entry;
+ xfs_attr_leaf_name_remote_t *name_rmt;
+ xfs_dabuf_t *bp;
+ int error;
+#ifdef DEBUG
+ xfs_attr_leaf_name_local_t *name_loc;
+ int namelen;
+ char *name;
+#endif /* DEBUG */
+
+ /*
+ * Set up the operation.
+ */
+ error = xfs_da_read_buf(args->trans, args->dp, args->blkno, -1, &bp,
+ XFS_ATTR_FORK);
+ if (error) {
+ return(error);
+ }
+ ASSERT(bp != NULL);
+
+ leaf = bp->data;
+ ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT)
+ == XFS_ATTR_LEAF_MAGIC);
+ ASSERT(args->index < INT_GET(leaf->hdr.count, ARCH_CONVERT));
+ ASSERT(args->index >= 0);
+ entry = &leaf->entries[ args->index ];
+ ASSERT(entry->flags & XFS_ATTR_INCOMPLETE);
+
+#ifdef DEBUG
+ if (entry->flags & XFS_ATTR_LOCAL) {
+ name_loc = XFS_ATTR_LEAF_NAME_LOCAL(leaf, args->index);
+ namelen = name_loc->namelen;
+ name = (char *)name_loc->nameval;
+ } else {
+ name_rmt = XFS_ATTR_LEAF_NAME_REMOTE(leaf, args->index);
+ namelen = name_rmt->namelen;
+ name = (char *)name_rmt->name;
+ }
+ ASSERT(INT_GET(entry->hashval, ARCH_CONVERT) == args->hashval);
+ ASSERT(namelen == args->namelen);
+ ASSERT(memcmp(name, args->name, namelen) == 0);
+#endif /* DEBUG */
+
+ entry->flags &= ~XFS_ATTR_INCOMPLETE;
+ xfs_da_log_buf(args->trans, bp,
+ XFS_DA_LOGRANGE(leaf, entry, sizeof(*entry)));
+
+ if (args->rmtblkno) {
+ ASSERT((entry->flags & XFS_ATTR_LOCAL) == 0);
+ name_rmt = XFS_ATTR_LEAF_NAME_REMOTE(leaf, args->index);
+ INT_SET(name_rmt->valueblk, ARCH_CONVERT, args->rmtblkno);
+ INT_SET(name_rmt->valuelen, ARCH_CONVERT, args->valuelen);
+ xfs_da_log_buf(args->trans, bp,
+ XFS_DA_LOGRANGE(leaf, name_rmt, sizeof(*name_rmt)));
+ }
+ xfs_da_buf_done(bp);
+
+ /*
+ * Commit the flag value change and start the next trans in series.
+ */
+ error = xfs_attr_rolltrans(&args->trans, args->dp);
+
+ return(error);
+}
+
+/*
+ * Set the INCOMPLETE flag on an entry in a leaf block.
+ */
+int
+xfs_attr_leaf_setflag(xfs_da_args_t *args)
+{
+ xfs_attr_leafblock_t *leaf;
+ xfs_attr_leaf_entry_t *entry;
+ xfs_attr_leaf_name_remote_t *name_rmt;
+ xfs_dabuf_t *bp;
+ int error;
+
+ /*
+ * Set up the operation.
+ */
+ error = xfs_da_read_buf(args->trans, args->dp, args->blkno, -1, &bp,
+ XFS_ATTR_FORK);
+ if (error) {
+ return(error);
+ }
+ ASSERT(bp != NULL);
+
+ leaf = bp->data;
+ ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT)
+ == XFS_ATTR_LEAF_MAGIC);
+ ASSERT(args->index < INT_GET(leaf->hdr.count, ARCH_CONVERT));
+ ASSERT(args->index >= 0);
+ entry = &leaf->entries[ args->index ];
+
+ ASSERT((entry->flags & XFS_ATTR_INCOMPLETE) == 0);
+ entry->flags |= XFS_ATTR_INCOMPLETE;
+ xfs_da_log_buf(args->trans, bp,
+ XFS_DA_LOGRANGE(leaf, entry, sizeof(*entry)));
+ if ((entry->flags & XFS_ATTR_LOCAL) == 0) {
+ name_rmt = XFS_ATTR_LEAF_NAME_REMOTE(leaf, args->index);
+ INT_ZERO(name_rmt->valueblk, ARCH_CONVERT);
+ INT_ZERO(name_rmt->valuelen, ARCH_CONVERT);
+ xfs_da_log_buf(args->trans, bp,
+ XFS_DA_LOGRANGE(leaf, name_rmt, sizeof(*name_rmt)));
+ }
+ xfs_da_buf_done(bp);
+
+ /*
+ * Commit the flag value change and start the next trans in series.
+ */
+ error = xfs_attr_rolltrans(&args->trans, args->dp);
+
+ return(error);
+}
+
+/*
+ * In a single transaction, clear the INCOMPLETE flag on the leaf entry
+ * given by args->blkno/index and set the INCOMPLETE flag on the leaf
+ * entry given by args->blkno2/index2.
+ *
+ * Note that they could be in different blocks, or in the same block.
+ */
+int
+xfs_attr_leaf_flipflags(xfs_da_args_t *args)
+{
+ xfs_attr_leafblock_t *leaf1, *leaf2;
+ xfs_attr_leaf_entry_t *entry1, *entry2;
+ xfs_attr_leaf_name_remote_t *name_rmt;
+ xfs_dabuf_t *bp1, *bp2;
+ int error;
+#ifdef DEBUG
+ xfs_attr_leaf_name_local_t *name_loc;
+ int namelen1, namelen2;
+ char *name1, *name2;
+#endif /* DEBUG */
+
+ /*
+ * Read the block containing the "old" attr
+ */
+ error = xfs_da_read_buf(args->trans, args->dp, args->blkno, -1, &bp1,
+ XFS_ATTR_FORK);
+ if (error) {
+ return(error);
+ }
+ ASSERT(bp1 != NULL);
+
+ /*
+ * Read the block containing the "new" attr, if it is different
+ */
+ if (args->blkno2 != args->blkno) {
+ error = xfs_da_read_buf(args->trans, args->dp, args->blkno2,
+ -1, &bp2, XFS_ATTR_FORK);
+ if (error) {
+ return(error);
+ }
+ ASSERT(bp2 != NULL);
+ } else {
+ bp2 = bp1;
+ }
+
+ leaf1 = bp1->data;
+ ASSERT(INT_GET(leaf1->hdr.info.magic, ARCH_CONVERT)
+ == XFS_ATTR_LEAF_MAGIC);
+ ASSERT(args->index < INT_GET(leaf1->hdr.count, ARCH_CONVERT));
+ ASSERT(args->index >= 0);
+ entry1 = &leaf1->entries[ args->index ];
+
+ leaf2 = bp2->data;
+ ASSERT(INT_GET(leaf2->hdr.info.magic, ARCH_CONVERT)
+ == XFS_ATTR_LEAF_MAGIC);
+ ASSERT(args->index2 < INT_GET(leaf2->hdr.count, ARCH_CONVERT));
+ ASSERT(args->index2 >= 0);
+ entry2 = &leaf2->entries[ args->index2 ];
+
+#ifdef DEBUG
+ if (entry1->flags & XFS_ATTR_LOCAL) {
+ name_loc = XFS_ATTR_LEAF_NAME_LOCAL(leaf1, args->index);
+ namelen1 = name_loc->namelen;
+ name1 = (char *)name_loc->nameval;
+ } else {
+ name_rmt = XFS_ATTR_LEAF_NAME_REMOTE(leaf1, args->index);
+ namelen1 = name_rmt->namelen;
+ name1 = (char *)name_rmt->name;
+ }
+ if (entry2->flags & XFS_ATTR_LOCAL) {
+ name_loc = XFS_ATTR_LEAF_NAME_LOCAL(leaf2, args->index2);
+ namelen2 = name_loc->namelen;
+ name2 = (char *)name_loc->nameval;
+ } else {
+ name_rmt = XFS_ATTR_LEAF_NAME_REMOTE(leaf2, args->index2);
+ namelen2 = name_rmt->namelen;
+ name2 = (char *)name_rmt->name;
+ }
+ ASSERT(INT_GET(entry1->hashval, ARCH_CONVERT) == INT_GET(entry2->hashval, ARCH_CONVERT));
+ ASSERT(namelen1 == namelen2);
+ ASSERT(memcmp(name1, name2, namelen1) == 0);
+#endif /* DEBUG */
+
+ ASSERT(entry1->flags & XFS_ATTR_INCOMPLETE);
+ ASSERT((entry2->flags & XFS_ATTR_INCOMPLETE) == 0);
+
+ entry1->flags &= ~XFS_ATTR_INCOMPLETE;
+ xfs_da_log_buf(args->trans, bp1,
+ XFS_DA_LOGRANGE(leaf1, entry1, sizeof(*entry1)));
+ if (args->rmtblkno) {
+ ASSERT((entry1->flags & XFS_ATTR_LOCAL) == 0);
+ name_rmt = XFS_ATTR_LEAF_NAME_REMOTE(leaf1, args->index);
+ INT_SET(name_rmt->valueblk, ARCH_CONVERT, args->rmtblkno);
+ INT_SET(name_rmt->valuelen, ARCH_CONVERT, args->valuelen);
+ xfs_da_log_buf(args->trans, bp1,
+ XFS_DA_LOGRANGE(leaf1, name_rmt, sizeof(*name_rmt)));
+ }
+
+ entry2->flags |= XFS_ATTR_INCOMPLETE;
+ xfs_da_log_buf(args->trans, bp2,
+ XFS_DA_LOGRANGE(leaf2, entry2, sizeof(*entry2)));
+ if ((entry2->flags & XFS_ATTR_LOCAL) == 0) {
+ name_rmt = XFS_ATTR_LEAF_NAME_REMOTE(leaf2, args->index2);
+ INT_ZERO(name_rmt->valueblk, ARCH_CONVERT);
+ INT_ZERO(name_rmt->valuelen, ARCH_CONVERT);
+ xfs_da_log_buf(args->trans, bp2,
+ XFS_DA_LOGRANGE(leaf2, name_rmt, sizeof(*name_rmt)));
+ }
+ xfs_da_buf_done(bp1);
+ if (bp1 != bp2)
+ xfs_da_buf_done(bp2);
+
+ /*
+ * Commit the flag value change and start the next trans in series.
+ */
+ error = xfs_attr_rolltrans(&args->trans, args->dp);
+
+ return(error);
+}
+
+/*========================================================================
+ * Indiscriminately delete the entire attribute fork
+ *========================================================================*/
+
+/*
+ * Recurse (gasp!) through the attribute nodes until we find leaves.
+ * We're doing a depth-first traversal in order to invalidate everything.
+ */
+int
+xfs_attr_root_inactive(xfs_trans_t **trans, xfs_inode_t *dp)
+{
+ xfs_da_blkinfo_t *info;
+ xfs_daddr_t blkno;
+ xfs_dabuf_t *bp;
+ int error;
+
+ /*
+ * Read block 0 to see what we have to work with.
+ * We only get here if we have extents, since we remove
+ * the extents in reverse order the extent containing
+ * block 0 must still be there.
+ */
+ error = xfs_da_read_buf(*trans, dp, 0, -1, &bp, XFS_ATTR_FORK);
+ if (error)
+ return(error);
+ blkno = xfs_da_blkno(bp);
+
+ /*
+ * Invalidate the tree, even if the "tree" is only a single leaf block.
+ * This is a depth-first traversal!
+ */
+ info = bp->data;
+ if (INT_GET(info->magic, ARCH_CONVERT) == XFS_DA_NODE_MAGIC) {
+ error = xfs_attr_node_inactive(trans, dp, bp, 1);
+ } else if (INT_GET(info->magic, ARCH_CONVERT) == XFS_ATTR_LEAF_MAGIC) {
+ error = xfs_attr_leaf_inactive(trans, dp, bp);
+ } else {
+ error = XFS_ERROR(EIO);
+ xfs_da_brelse(*trans, bp);
+ }
+ if (error)
+ return(error);
+
+ /*
+ * Invalidate the incore copy of the root block.
+ */
+ error = xfs_da_get_buf(*trans, dp, 0, blkno, &bp, XFS_ATTR_FORK);
+ if (error)
+ return(error);
+ xfs_da_binval(*trans, bp); /* remove from cache */
+ /*
+ * Commit the invalidate and start the next transaction.
+ */
+ error = xfs_attr_rolltrans(trans, dp);
+
+ return (error);
+}
+
+/*
+ * Recurse (gasp!) through the attribute nodes until we find leaves.
+ * We're doing a depth-first traversal in order to invalidate everything.
+ */
+int
+xfs_attr_node_inactive(xfs_trans_t **trans, xfs_inode_t *dp, xfs_dabuf_t *bp,
+ int level)
+{
+ xfs_da_blkinfo_t *info;
+ xfs_da_intnode_t *node;
+ xfs_dablk_t child_fsb;
+ xfs_daddr_t parent_blkno, child_blkno;
+ int error, count, i;
+ xfs_dabuf_t *child_bp;
+
+ /*
+ * Since this code is recursive (gasp!) we must protect ourselves.
+ */
+ if (level > XFS_DA_NODE_MAXDEPTH) {
+ xfs_da_brelse(*trans, bp); /* no locks for later trans */
+ return(XFS_ERROR(EIO));
+ }
+
+ node = bp->data;
+ ASSERT(INT_GET(node->hdr.info.magic, ARCH_CONVERT)
+ == XFS_DA_NODE_MAGIC);
+ parent_blkno = xfs_da_blkno(bp); /* save for re-read later */
+ count = INT_GET(node->hdr.count, ARCH_CONVERT);
+ if (!count) {
+ xfs_da_brelse(*trans, bp);
+ return(0);
+ }
+ child_fsb = INT_GET(node->btree[0].before, ARCH_CONVERT);
+ xfs_da_brelse(*trans, bp); /* no locks for later trans */
+
+ /*
+ * If this is the node level just above the leaves, simply loop
+ * over the leaves removing all of them. If this is higher up
+ * in the tree, recurse downward.
+ */
+ for (i = 0; i < count; i++) {
+ /*
+ * Read the subsidiary block to see what we have to work with.
+ * Don't do this in a transaction. This is a depth-first
+ * traversal of the tree so we may deal with many blocks
+ * before we come back to this one.
+ */
+ error = xfs_da_read_buf(*trans, dp, child_fsb, -2, &child_bp,
+ XFS_ATTR_FORK);
+ if (error)
+ return(error);
+ if (child_bp) {
+ /* save for re-read later */
+ child_blkno = xfs_da_blkno(child_bp);
+
+ /*
+ * Invalidate the subtree, however we have to.
+ */
+ info = child_bp->data;
+ if (INT_GET(info->magic, ARCH_CONVERT)
+ == XFS_DA_NODE_MAGIC) {
+ error = xfs_attr_node_inactive(trans, dp,
+ child_bp, level+1);
+ } else if (INT_GET(info->magic, ARCH_CONVERT)
+ == XFS_ATTR_LEAF_MAGIC) {
+ error = xfs_attr_leaf_inactive(trans, dp,
+ child_bp);
+ } else {
+ error = XFS_ERROR(EIO);
+ xfs_da_brelse(*trans, child_bp);
+ }
+ if (error)
+ return(error);
+
+ /*
+ * Remove the subsidiary block from the cache
+ * and from the log.
+ */
+ error = xfs_da_get_buf(*trans, dp, 0, child_blkno,
+ &child_bp, XFS_ATTR_FORK);
+ if (error)
+ return(error);
+ xfs_da_binval(*trans, child_bp);
+ }
+
+ /*
+ * If we're not done, re-read the parent to get the next
+ * child block number.
+ */
+ if ((i+1) < count) {
+ error = xfs_da_read_buf(*trans, dp, 0, parent_blkno,
+ &bp, XFS_ATTR_FORK);
+ if (error)
+ return(error);
+ child_fsb = INT_GET(node->btree[i+1].before, ARCH_CONVERT);
+ xfs_da_brelse(*trans, bp);
+ }
+ /*
+ * Atomically commit the whole invalidate stuff.
+ */
+ if ((error = xfs_attr_rolltrans(trans, dp)))
+ return (error);
+ }
+
+ return(0);
+}
+
+/*
+ * Invalidate all of the "remote" value regions pointed to by a particular
+ * leaf block.
+ * Note that we must release the lock on the buffer so that we are not
+ * caught holding something that the logging code wants to flush to disk.
+ */
+int
+xfs_attr_leaf_inactive(xfs_trans_t **trans, xfs_inode_t *dp, xfs_dabuf_t *bp)
+{
+ xfs_attr_leafblock_t *leaf;
+ xfs_attr_leaf_entry_t *entry;
+ xfs_attr_leaf_name_remote_t *name_rmt;
+ xfs_attr_inactive_list_t *list, *lp;
+ int error, count, size, tmp, i;
+
+ leaf = bp->data;
+ ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT)
+ == XFS_ATTR_LEAF_MAGIC);
+
+ /*
+ * Count the number of "remote" value extents.
+ */
+ count = 0;
+ entry = &leaf->entries[0];
+ for (i = 0; i < INT_GET(leaf->hdr.count, ARCH_CONVERT); entry++, i++) {
+ if ( INT_GET(entry->nameidx, ARCH_CONVERT)
+ && ((entry->flags & XFS_ATTR_LOCAL) == 0)) {
+ name_rmt = XFS_ATTR_LEAF_NAME_REMOTE(leaf, i);
+ if (!INT_ISZERO(name_rmt->valueblk, ARCH_CONVERT))
+ count++;
+ }
+ }
+
+ /*
+ * If there are no "remote" values, we're done.
+ */
+ if (count == 0) {
+ xfs_da_brelse(*trans, bp);
+ return(0);
+ }
+
+ /*
+ * Allocate storage for a list of all the "remote" value extents.
+ */
+ size = count * sizeof(xfs_attr_inactive_list_t);
+ list = (xfs_attr_inactive_list_t *)kmem_alloc(size, KM_SLEEP);
+
+ /*
+ * Identify each of the "remote" value extents.
+ */
+ lp = list;
+ entry = &leaf->entries[0];
+ for (i = 0; i < INT_GET(leaf->hdr.count, ARCH_CONVERT); entry++, i++) {
+ if ( INT_GET(entry->nameidx, ARCH_CONVERT)
+ && ((entry->flags & XFS_ATTR_LOCAL) == 0)) {
+ name_rmt = XFS_ATTR_LEAF_NAME_REMOTE(leaf, i);
+ if (!INT_ISZERO(name_rmt->valueblk, ARCH_CONVERT)) {
+ /* both on-disk, don't endian flip twice */
+ lp->valueblk = name_rmt->valueblk;
+ INT_SET(lp->valuelen, ARCH_CONVERT,
+ XFS_B_TO_FSB(dp->i_mount,
+ INT_GET(name_rmt->valuelen,
+ ARCH_CONVERT)));
+ lp++;
+ }
+ }
+ }
+ xfs_da_brelse(*trans, bp); /* unlock for trans. in freextent() */
+
+ /*
+ * Invalidate each of the "remote" value extents.
+ */
+ error = 0;
+ for (lp = list, i = 0; i < count; i++, lp++) {
+ tmp = xfs_attr_leaf_freextent(trans, dp,
+ INT_GET(lp->valueblk,
+ ARCH_CONVERT),
+ INT_GET(lp->valuelen,
+ ARCH_CONVERT));
+ if (error == 0)
+ error = tmp; /* save only the 1st errno */
+ }
+
+ kmem_free((xfs_caddr_t)list, size);
+ return(error);
+}
+
+/*
+ * Look at all the extents for this logical region,
+ * invalidate any buffers that are incore/in transactions.
+ */
+int
+xfs_attr_leaf_freextent(xfs_trans_t **trans, xfs_inode_t *dp,
+ xfs_dablk_t blkno, int blkcnt)
+{
+ xfs_bmbt_irec_t map;
+ xfs_dablk_t tblkno;
+ int tblkcnt, dblkcnt, nmap, error;
+ xfs_daddr_t dblkno;
+ xfs_buf_t *bp;
+
+ /*
+ * Roll through the "value", invalidating the attribute value's
+ * blocks.
+ */
+ tblkno = blkno;
+ tblkcnt = blkcnt;
+ while (tblkcnt > 0) {
+ /*
+ * Try to remember where we decided to put the value.
+ */
+ nmap = 1;
+ error = xfs_bmapi(*trans, dp, (xfs_fileoff_t)tblkno, tblkcnt,
+ XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA,
+ NULL, 0, &map, &nmap, NULL);
+ if (error) {
+ return(error);
+ }
+ ASSERT(nmap == 1);
+ ASSERT(map.br_startblock != DELAYSTARTBLOCK);
+
+ /*
+ * If it's a hole, these are already unmapped
+ * so there's nothing to invalidate.
+ */
+ if (map.br_startblock != HOLESTARTBLOCK) {
+
+ dblkno = XFS_FSB_TO_DADDR(dp->i_mount,
+ map.br_startblock);
+ dblkcnt = XFS_FSB_TO_BB(dp->i_mount,
+ map.br_blockcount);
+ bp = xfs_trans_get_buf(*trans,
+ dp->i_mount->m_ddev_targp,
+ dblkno, dblkcnt, XFS_BUF_LOCK);
+ xfs_trans_binval(*trans, bp);
+ /*
+ * Roll to next transaction.
+ */
+ if ((error = xfs_attr_rolltrans(trans, dp)))
+ return (error);
+ }
+
+ tblkno += map.br_blockcount;
+ tblkcnt -= map.br_blockcount;
+ }
+
+ return(0);
+}
+
+
+/*
+ * Roll from one trans in the sequence of PERMANENT transactions to the next.
+ */
+int
+xfs_attr_rolltrans(xfs_trans_t **transp, xfs_inode_t *dp)
+{
+ xfs_trans_t *trans;
+ unsigned int logres, count;
+ int error;
+
+ /*
+ * Ensure that the inode is always logged.
+ */
+ trans = *transp;
+ xfs_trans_log_inode(trans, dp, XFS_ILOG_CORE);
+
+ /*
+ * Copy the critical parameters from one trans to the next.
+ */
+ logres = trans->t_log_res;
+ count = trans->t_log_count;
+ *transp = xfs_trans_dup(trans);
+
+ /*
+ * Commit the current transaction.
+ * If this commit failed, then it'd just unlock those items that
+ * are not marked ihold. That also means that a filesystem shutdown
+ * is in progress. The caller takes the responsibility to cancel
+ * the duplicate transaction that gets returned.
+ */
+ if ((error = xfs_trans_commit(trans, 0, NULL)))
+ return (error);
+
+ trans = *transp;
+
+ /*
+ * Reserve space in the log for th next transaction.
+ * This also pushes items in the "AIL", the list of logged items,
+ * out to disk if they are taking up space at the tail of the log
+ * that we want to use. This requires that either nothing be locked
+ * across this call, or that anything that is locked be logged in
+ * the prior and the next transactions.
+ */
+ error = xfs_trans_reserve(trans, 0, logres, 0,
+ XFS_TRANS_PERM_LOG_RES, count);
+ /*
+ * Ensure that the inode is in the new transaction and locked.
+ */
+ if (!error) {
+ xfs_trans_ijoin(trans, dp, XFS_ILOCK_EXCL);
+ xfs_trans_ihold(trans, dp);
+ }
+ return (error);
+
+}
diff --git a/fs/xfs/xfs_attr_leaf.h b/fs/xfs/xfs_attr_leaf.h
new file mode 100644
index 00000000000000..b1480e0b334922
--- /dev/null
+++ b/fs/xfs/xfs_attr_leaf.h
@@ -0,0 +1,308 @@
+/*
+ * Copyright (c) 2000, 2002-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_ATTR_LEAF_H__
+#define __XFS_ATTR_LEAF_H__
+
+/*
+ * Attribute storage layout, internal structure, access macros, etc.
+ *
+ * Attribute lists are structured around Btrees where all the data
+ * elements are in the leaf nodes. Attribute names are hashed into an int,
+ * then that int is used as the index into the Btree. Since the hashval
+ * of an attribute name may not be unique, we may have duplicate keys. The
+ * internal links in the Btree are logical block offsets into the file.
+ */
+
+struct attrlist;
+struct attrlist_cursor_kern;
+struct attrnames;
+struct xfs_dabuf;
+struct xfs_da_args;
+struct xfs_da_state;
+struct xfs_da_state_blk;
+struct xfs_inode;
+struct xfs_trans;
+
+/*========================================================================
+ * Attribute structure when equal to XFS_LBSIZE(mp) bytes.
+ *========================================================================*/
+
+/*
+ * This is the structure of the leaf nodes in the Btree.
+ *
+ * Struct leaf_entry's are packed from the top. Name/values grow from the
+ * bottom but are not packed. The freemap contains run-length-encoded entries
+ * for the free bytes after the leaf_entry's, but only the N largest such,
+ * smaller runs are dropped. When the freemap doesn't show enough space
+ * for an allocation, we compact the name/value area and try again. If we
+ * still don't have enough space, then we have to split the block. The
+ * name/value structs (both local and remote versions) must be 32bit aligned.
+ *
+ * Since we have duplicate hash keys, for each key that matches, compare
+ * the actual name string. The root and intermediate node search always
+ * takes the first-in-the-block key match found, so we should only have
+ * to work "forw"ard. If none matches, continue with the "forw"ard leaf
+ * nodes until the hash key changes or the attribute name is found.
+ *
+ * We store the fact that an attribute is a ROOT/USER/SECURE attribute in
+ * the leaf_entry. The namespaces are independent only because we also look
+ * at the namespace bit when we are looking for a matching attribute name.
+ *
+ * We also store a "incomplete" bit in the leaf_entry. It shows that an
+ * attribute is in the middle of being created and should not be shown to
+ * the user if we crash during the time that the bit is set. We clear the
+ * bit when we have finished setting up the attribute. We do this because
+ * we cannot create some large attributes inside a single transaction, and we
+ * need some indication that we weren't finished if we crash in the middle.
+ */
+#define XFS_ATTR_LEAF_MAPSIZE 3 /* how many freespace slots */
+
+typedef struct xfs_attr_leafblock {
+ struct xfs_attr_leaf_hdr { /* constant-structure header block */
+ xfs_da_blkinfo_t info; /* block type, links, etc. */
+ __uint16_t count; /* count of active leaf_entry's */
+ __uint16_t usedbytes; /* num bytes of names/values stored */
+ __uint16_t firstused; /* first used byte in name area */
+ __uint8_t holes; /* != 0 if blk needs compaction */
+ __uint8_t pad1;
+ struct xfs_attr_leaf_map { /* RLE map of free bytes */
+ __uint16_t base; /* base of free region */
+ __uint16_t size; /* length of free region */
+ } freemap[XFS_ATTR_LEAF_MAPSIZE]; /* N largest free regions */
+ } hdr;
+ struct xfs_attr_leaf_entry { /* sorted on key, not name */
+ xfs_dahash_t hashval; /* hash value of name */
+ __uint16_t nameidx; /* index into buffer of name/value */
+ __uint8_t flags; /* LOCAL/ROOT/SECURE/INCOMPLETE flag */
+ __uint8_t pad2; /* unused pad byte */
+ } entries[1]; /* variable sized array */
+ struct xfs_attr_leaf_name_local {
+ __uint16_t valuelen; /* number of bytes in value */
+ __uint8_t namelen; /* length of name bytes */
+ __uint8_t nameval[1]; /* name/value bytes */
+ } namelist; /* grows from bottom of buf */
+ struct xfs_attr_leaf_name_remote {
+ xfs_dablk_t valueblk; /* block number of value bytes */
+ __uint32_t valuelen; /* number of bytes in value */
+ __uint8_t namelen; /* length of name bytes */
+ __uint8_t name[1]; /* name bytes */
+ } valuelist; /* grows from bottom of buf */
+} xfs_attr_leafblock_t;
+typedef struct xfs_attr_leaf_hdr xfs_attr_leaf_hdr_t;
+typedef struct xfs_attr_leaf_map xfs_attr_leaf_map_t;
+typedef struct xfs_attr_leaf_entry xfs_attr_leaf_entry_t;
+typedef struct xfs_attr_leaf_name_local xfs_attr_leaf_name_local_t;
+typedef struct xfs_attr_leaf_name_remote xfs_attr_leaf_name_remote_t;
+
+/*
+ * Flags used in the leaf_entry[i].flags field.
+ * NOTE: the INCOMPLETE bit must not collide with the flags bits specified
+ * on the system call, they are "or"ed together for various operations.
+ */
+#define XFS_ATTR_LOCAL_BIT 0 /* attr is stored locally */
+#define XFS_ATTR_ROOT_BIT 1 /* limit access to trusted attrs */
+#define XFS_ATTR_SECURE_BIT 2 /* limit access to secure attrs */
+#define XFS_ATTR_INCOMPLETE_BIT 7 /* attr in middle of create/delete */
+#define XFS_ATTR_LOCAL (1 << XFS_ATTR_LOCAL_BIT)
+#define XFS_ATTR_ROOT (1 << XFS_ATTR_ROOT_BIT)
+#define XFS_ATTR_SECURE (1 << XFS_ATTR_SECURE_BIT)
+#define XFS_ATTR_INCOMPLETE (1 << XFS_ATTR_INCOMPLETE_BIT)
+
+/*
+ * Alignment for namelist and valuelist entries (since they are mixed
+ * there can be only one alignment value)
+ */
+#define XFS_ATTR_LEAF_NAME_ALIGN ((uint)sizeof(xfs_dablk_t))
+
+/*
+ * Cast typed pointers for "local" and "remote" name/value structs.
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_ATTR_LEAF_NAME_REMOTE)
+xfs_attr_leaf_name_remote_t *
+xfs_attr_leaf_name_remote(xfs_attr_leafblock_t *leafp, int idx);
+#define XFS_ATTR_LEAF_NAME_REMOTE(leafp,idx) \
+ xfs_attr_leaf_name_remote(leafp,idx)
+#else
+#define XFS_ATTR_LEAF_NAME_REMOTE(leafp,idx) /* remote name struct ptr */ \
+ ((xfs_attr_leaf_name_remote_t *) \
+ &((char *)(leafp))[ INT_GET((leafp)->entries[idx].nameidx, ARCH_CONVERT) ])
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_ATTR_LEAF_NAME_LOCAL)
+xfs_attr_leaf_name_local_t *
+xfs_attr_leaf_name_local(xfs_attr_leafblock_t *leafp, int idx);
+#define XFS_ATTR_LEAF_NAME_LOCAL(leafp,idx) \
+ xfs_attr_leaf_name_local(leafp,idx)
+#else
+#define XFS_ATTR_LEAF_NAME_LOCAL(leafp,idx) /* local name struct ptr */ \
+ ((xfs_attr_leaf_name_local_t *) \
+ &((char *)(leafp))[ INT_GET((leafp)->entries[idx].nameidx, ARCH_CONVERT) ])
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_ATTR_LEAF_NAME)
+char *xfs_attr_leaf_name(xfs_attr_leafblock_t *leafp, int idx);
+#define XFS_ATTR_LEAF_NAME(leafp,idx) xfs_attr_leaf_name(leafp,idx)
+#else
+#define XFS_ATTR_LEAF_NAME(leafp,idx) /* generic name struct ptr */ \
+ (&((char *)(leafp))[ INT_GET((leafp)->entries[idx].nameidx, ARCH_CONVERT) ])
+#endif
+
+/*
+ * Calculate total bytes used (including trailing pad for alignment) for
+ * a "local" name/value structure, a "remote" name/value structure, and
+ * a pointer which might be either.
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_ATTR_LEAF_ENTSIZE_REMOTE)
+int xfs_attr_leaf_entsize_remote(int nlen);
+#define XFS_ATTR_LEAF_ENTSIZE_REMOTE(nlen) \
+ xfs_attr_leaf_entsize_remote(nlen)
+#else
+#define XFS_ATTR_LEAF_ENTSIZE_REMOTE(nlen) /* space for remote struct */ \
+ (((uint)sizeof(xfs_attr_leaf_name_remote_t) - 1 + (nlen) + \
+ XFS_ATTR_LEAF_NAME_ALIGN - 1) & ~(XFS_ATTR_LEAF_NAME_ALIGN - 1))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_ATTR_LEAF_ENTSIZE_LOCAL)
+int xfs_attr_leaf_entsize_local(int nlen, int vlen);
+#define XFS_ATTR_LEAF_ENTSIZE_LOCAL(nlen,vlen) \
+ xfs_attr_leaf_entsize_local(nlen,vlen)
+#else
+#define XFS_ATTR_LEAF_ENTSIZE_LOCAL(nlen,vlen) /* space for local struct */ \
+ (((uint)sizeof(xfs_attr_leaf_name_local_t) - 1 + (nlen) + (vlen) + \
+ XFS_ATTR_LEAF_NAME_ALIGN - 1) & ~(XFS_ATTR_LEAF_NAME_ALIGN - 1))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_ATTR_LEAF_ENTSIZE_LOCAL_MAX)
+int xfs_attr_leaf_entsize_local_max(int bsize);
+#define XFS_ATTR_LEAF_ENTSIZE_LOCAL_MAX(bsize) \
+ xfs_attr_leaf_entsize_local_max(bsize)
+#else
+#define XFS_ATTR_LEAF_ENTSIZE_LOCAL_MAX(bsize) /* max local struct size */ \
+ (((bsize) >> 1) + ((bsize) >> 2))
+#endif
+
+
+/*========================================================================
+ * Structure used to pass context around among the routines.
+ *========================================================================*/
+
+typedef struct xfs_attr_list_context {
+ struct xfs_inode *dp; /* inode */
+ struct attrlist_cursor_kern *cursor;/* position in list */
+ struct attrlist *alist; /* output buffer */
+ int count; /* num used entries */
+ int dupcnt; /* count dup hashvals seen */
+ int bufsize;/* total buffer size */
+ int firstu; /* first used byte in buffer */
+ int flags; /* from VOP call */
+ int resynch;/* T/F: resynch with cursor */
+} xfs_attr_list_context_t;
+
+/*
+ * Used to keep a list of "remote value" extents when unlinking an inode.
+ */
+typedef struct xfs_attr_inactive_list {
+ xfs_dablk_t valueblk; /* block number of value bytes */
+ int valuelen; /* number of bytes in value */
+} xfs_attr_inactive_list_t;
+
+
+/*========================================================================
+ * Function prototypes for the kernel.
+ *========================================================================*/
+
+/*
+ * Internal routines when dirsize < XFS_LITINO(mp).
+ */
+int xfs_attr_shortform_create(struct xfs_da_args *args);
+int xfs_attr_shortform_add(struct xfs_da_args *add);
+int xfs_attr_shortform_lookup(struct xfs_da_args *args);
+int xfs_attr_shortform_getvalue(struct xfs_da_args *args);
+int xfs_attr_shortform_to_leaf(struct xfs_da_args *args);
+int xfs_attr_shortform_remove(struct xfs_da_args *remove);
+int xfs_attr_shortform_list(struct xfs_attr_list_context *context);
+int xfs_attr_shortform_allfit(struct xfs_dabuf *bp, struct xfs_inode *dp);
+
+/*
+ * Internal routines when dirsize == XFS_LBSIZE(mp).
+ */
+int xfs_attr_leaf_to_node(struct xfs_da_args *args);
+int xfs_attr_leaf_to_shortform(struct xfs_dabuf *bp,
+ struct xfs_da_args *args);
+int xfs_attr_leaf_clearflag(struct xfs_da_args *args);
+int xfs_attr_leaf_setflag(struct xfs_da_args *args);
+int xfs_attr_leaf_flipflags(xfs_da_args_t *args);
+
+/*
+ * Routines used for growing the Btree.
+ */
+int xfs_attr_leaf_create(struct xfs_da_args *args, xfs_dablk_t which_block,
+ struct xfs_dabuf **bpp);
+int xfs_attr_leaf_split(struct xfs_da_state *state,
+ struct xfs_da_state_blk *oldblk,
+ struct xfs_da_state_blk *newblk);
+int xfs_attr_leaf_lookup_int(struct xfs_dabuf *leaf,
+ struct xfs_da_args *args);
+int xfs_attr_leaf_getvalue(struct xfs_dabuf *bp, struct xfs_da_args *args);
+int xfs_attr_leaf_add(struct xfs_dabuf *leaf_buffer,
+ struct xfs_da_args *args);
+int xfs_attr_leaf_remove(struct xfs_dabuf *leaf_buffer,
+ struct xfs_da_args *args);
+int xfs_attr_leaf_list_int(struct xfs_dabuf *bp,
+ struct xfs_attr_list_context *context);
+
+/*
+ * Routines used for shrinking the Btree.
+ */
+int xfs_attr_leaf_toosmall(struct xfs_da_state *state, int *retval);
+void xfs_attr_leaf_unbalance(struct xfs_da_state *state,
+ struct xfs_da_state_blk *drop_blk,
+ struct xfs_da_state_blk *save_blk);
+int xfs_attr_root_inactive(struct xfs_trans **trans, struct xfs_inode *dp);
+int xfs_attr_node_inactive(struct xfs_trans **trans, struct xfs_inode *dp,
+ struct xfs_dabuf *bp, int level);
+int xfs_attr_leaf_inactive(struct xfs_trans **trans, struct xfs_inode *dp,
+ struct xfs_dabuf *bp);
+int xfs_attr_leaf_freextent(struct xfs_trans **trans, struct xfs_inode *dp,
+ xfs_dablk_t blkno, int blkcnt);
+
+/*
+ * Utility routines.
+ */
+xfs_dahash_t xfs_attr_leaf_lasthash(struct xfs_dabuf *bp, int *count);
+int xfs_attr_leaf_order(struct xfs_dabuf *leaf1_bp,
+ struct xfs_dabuf *leaf2_bp);
+int xfs_attr_leaf_newentsize(struct xfs_da_args *args, int blocksize,
+ int *local);
+int xfs_attr_leaf_entsize(struct xfs_attr_leafblock *leaf, int index);
+int xfs_attr_put_listent(struct xfs_attr_list_context *context,
+ struct attrnames *, char *name, int namelen,
+ int valuelen);
+int xfs_attr_rolltrans(struct xfs_trans **transp, struct xfs_inode *dp);
+
+#endif /* __XFS_ATTR_LEAF_H__ */
diff --git a/fs/xfs/xfs_attr_sf.h b/fs/xfs/xfs_attr_sf.h
new file mode 100644
index 00000000000000..ef7d2942d30607
--- /dev/null
+++ b/fs/xfs/xfs_attr_sf.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2000, 2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_ATTR_SF_H__
+#define __XFS_ATTR_SF_H__
+
+/*
+ * Attribute storage when stored inside the inode.
+ *
+ * Small attribute lists are packed as tightly as possible so as
+ * to fit into the literal area of the inode.
+ */
+
+struct xfs_inode;
+
+/*
+ * Entries are packed toward the top as tight as possible.
+ */
+typedef struct xfs_attr_shortform {
+ struct xfs_attr_sf_hdr { /* constant-structure header block */
+ __uint16_t totsize; /* total bytes in shortform list */
+ __uint8_t count; /* count of active entries */
+ } hdr;
+ struct xfs_attr_sf_entry {
+ __uint8_t namelen; /* actual length of name (no NULL) */
+ __uint8_t valuelen; /* actual length of value (no NULL) */
+ __uint8_t flags; /* flags bits (see xfs_attr_leaf.h) */
+ __uint8_t nameval[1]; /* name & value bytes concatenated */
+ } list[1]; /* variable sized array */
+} xfs_attr_shortform_t;
+typedef struct xfs_attr_sf_hdr xfs_attr_sf_hdr_t;
+typedef struct xfs_attr_sf_entry xfs_attr_sf_entry_t;
+
+/*
+ * We generate this then sort it, attr_list() must return things in hash-order.
+ */
+typedef struct xfs_attr_sf_sort {
+ __uint8_t entno; /* entry number in original list */
+ __uint8_t namelen; /* length of name value (no null) */
+ __uint8_t valuelen; /* length of value */
+ __uint8_t flags; /* flags bits (see xfs_attr_leaf.h) */
+ xfs_dahash_t hash; /* this entry's hash value */
+ char *name; /* name value, pointer into buffer */
+} xfs_attr_sf_sort_t;
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_ATTR_SF_ENTSIZE_BYNAME)
+int xfs_attr_sf_entsize_byname(int nlen, int vlen);
+#define XFS_ATTR_SF_ENTSIZE_BYNAME(nlen,vlen) \
+ xfs_attr_sf_entsize_byname(nlen,vlen)
+#else
+#define XFS_ATTR_SF_ENTSIZE_BYNAME(nlen,vlen) /* space name/value uses */ \
+ ((int)sizeof(xfs_attr_sf_entry_t)-1 + (nlen)+(vlen))
+#endif
+#define XFS_ATTR_SF_ENTSIZE_MAX /* max space for name&value */ \
+ ((1 << (NBBY*(int)sizeof(__uint8_t))) - 1)
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_ATTR_SF_ENTSIZE)
+int xfs_attr_sf_entsize(xfs_attr_sf_entry_t *sfep);
+#define XFS_ATTR_SF_ENTSIZE(sfep) xfs_attr_sf_entsize(sfep)
+#else
+#define XFS_ATTR_SF_ENTSIZE(sfep) /* space an entry uses */ \
+ ((int)sizeof(xfs_attr_sf_entry_t)-1 + (sfep)->namelen+(sfep)->valuelen)
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_ATTR_SF_NEXTENTRY)
+xfs_attr_sf_entry_t *xfs_attr_sf_nextentry(xfs_attr_sf_entry_t *sfep);
+#define XFS_ATTR_SF_NEXTENTRY(sfep) xfs_attr_sf_nextentry(sfep)
+#else
+#define XFS_ATTR_SF_NEXTENTRY(sfep) /* next entry in struct */ \
+ ((xfs_attr_sf_entry_t *) \
+ ((char *)(sfep) + XFS_ATTR_SF_ENTSIZE(sfep)))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_ATTR_SF_TOTSIZE)
+int xfs_attr_sf_totsize(struct xfs_inode *dp);
+#define XFS_ATTR_SF_TOTSIZE(dp) xfs_attr_sf_totsize(dp)
+#else
+#define XFS_ATTR_SF_TOTSIZE(dp) /* total space in use */ \
+ (INT_GET(((xfs_attr_shortform_t *)((dp)->i_afp->if_u1.if_data))->hdr.totsize, ARCH_CONVERT))
+#endif
+
+#if defined(XFS_ATTR_TRACE)
+/*
+ * Kernel tracing support for attribute lists
+ */
+struct xfs_attr_list_context;
+struct xfs_da_intnode;
+struct xfs_da_node_entry;
+struct xfs_attr_leafblock;
+
+#define XFS_ATTR_TRACE_SIZE 4096 /* size of global trace buffer */
+extern ktrace_t *xfs_attr_trace_buf;
+
+/*
+ * Trace record types.
+ */
+#define XFS_ATTR_KTRACE_L_C 1 /* context */
+#define XFS_ATTR_KTRACE_L_CN 2 /* context, node */
+#define XFS_ATTR_KTRACE_L_CB 3 /* context, btree */
+#define XFS_ATTR_KTRACE_L_CL 4 /* context, leaf */
+
+void xfs_attr_trace_l_c(char *where, struct xfs_attr_list_context *context);
+void xfs_attr_trace_l_cn(char *where, struct xfs_attr_list_context *context,
+ struct xfs_da_intnode *node);
+void xfs_attr_trace_l_cb(char *where, struct xfs_attr_list_context *context,
+ struct xfs_da_node_entry *btree);
+void xfs_attr_trace_l_cl(char *where, struct xfs_attr_list_context *context,
+ struct xfs_attr_leafblock *leaf);
+void xfs_attr_trace_enter(int type, char *where,
+ __psunsigned_t a2, __psunsigned_t a3,
+ __psunsigned_t a4, __psunsigned_t a5,
+ __psunsigned_t a6, __psunsigned_t a7,
+ __psunsigned_t a8, __psunsigned_t a9,
+ __psunsigned_t a10, __psunsigned_t a11,
+ __psunsigned_t a12, __psunsigned_t a13,
+ __psunsigned_t a14, __psunsigned_t a15);
+#else
+#define xfs_attr_trace_l_c(w,c)
+#define xfs_attr_trace_l_cn(w,c,n)
+#define xfs_attr_trace_l_cb(w,c,b)
+#define xfs_attr_trace_l_cl(w,c,l)
+#endif /* XFS_ATTR_TRACE */
+
+#endif /* __XFS_ATTR_SF_H__ */
diff --git a/fs/xfs/xfs_behavior.c b/fs/xfs/xfs_behavior.c
new file mode 100644
index 00000000000000..16088e175ecc3d
--- /dev/null
+++ b/fs/xfs/xfs_behavior.c
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ *
+ */
+#include "xfs.h"
+
+/*
+ * Source file used to associate/disassociate behaviors with virtualized
+ * objects. See xfs_behavior.h for more information about behaviors, etc.
+ *
+ * The implementation is split between functions in this file and macros
+ * in xfs_behavior.h.
+ */
+
+/*
+ * Insert a new behavior descriptor into a behavior chain.
+ *
+ * The behavior chain is ordered based on the 'position' number which
+ * lives in the first field of the ops vector (higher numbers first).
+ *
+ * Attemps to insert duplicate ops result in an EINVAL return code.
+ * Otherwise, return 0 to indicate success.
+ */
+int
+bhv_insert(bhv_head_t *bhp, bhv_desc_t *bdp)
+{
+ bhv_desc_t *curdesc, *prev;
+ int position;
+
+ /*
+ * Validate the position value of the new behavior.
+ */
+ position = BHV_POSITION(bdp);
+ ASSERT(position >= BHV_POSITION_BASE && position <= BHV_POSITION_TOP);
+
+ /*
+ * Find location to insert behavior. Check for duplicates.
+ */
+ prev = NULL;
+ for (curdesc = bhp->bh_first;
+ curdesc != NULL;
+ curdesc = curdesc->bd_next) {
+
+ /* Check for duplication. */
+ if (curdesc->bd_ops == bdp->bd_ops) {
+ ASSERT(0);
+ return EINVAL;
+ }
+
+ /* Find correct position */
+ if (position >= BHV_POSITION(curdesc)) {
+ ASSERT(position != BHV_POSITION(curdesc));
+ break; /* found it */
+ }
+
+ prev = curdesc;
+ }
+
+ if (prev == NULL) {
+ /* insert at front of chain */
+ bdp->bd_next = bhp->bh_first;
+ bhp->bh_first = bdp;
+ } else {
+ /* insert after prev */
+ bdp->bd_next = prev->bd_next;
+ prev->bd_next = bdp;
+ }
+
+ return 0;
+}
+
+/*
+ * Remove a behavior descriptor from a position in a behavior chain;
+ * the postition is guaranteed not to be the first position.
+ * Should only be called by the bhv_remove() macro.
+ */
+void
+bhv_remove_not_first(bhv_head_t *bhp, bhv_desc_t *bdp)
+{
+ bhv_desc_t *curdesc, *prev;
+
+ ASSERT(bhp->bh_first != NULL);
+ ASSERT(bhp->bh_first->bd_next != NULL);
+
+ prev = bhp->bh_first;
+ for (curdesc = bhp->bh_first->bd_next;
+ curdesc != NULL;
+ curdesc = curdesc->bd_next) {
+
+ if (curdesc == bdp)
+ break; /* found it */
+ prev = curdesc;
+ }
+
+ ASSERT(curdesc == bdp);
+ prev->bd_next = bdp->bd_next; /* remove from after prev */
+}
+
+/*
+ * Look for a specific ops vector on the specified behavior chain.
+ * Return the associated behavior descriptor. Or NULL, if not found.
+ */
+bhv_desc_t *
+bhv_lookup(bhv_head_t *bhp, void *ops)
+{
+ bhv_desc_t *curdesc;
+
+ for (curdesc = bhp->bh_first;
+ curdesc != NULL;
+ curdesc = curdesc->bd_next) {
+
+ if (curdesc->bd_ops == ops)
+ return curdesc;
+ }
+
+ return NULL;
+}
+
+/*
+ * Looks for the first behavior within a specified range of positions.
+ * Return the associated behavior descriptor. Or NULL, if none found.
+ */
+bhv_desc_t *
+bhv_lookup_range(bhv_head_t *bhp, int low, int high)
+{
+ bhv_desc_t *curdesc;
+
+ for (curdesc = bhp->bh_first;
+ curdesc != NULL;
+ curdesc = curdesc->bd_next) {
+
+ int position = BHV_POSITION(curdesc);
+
+ if (position <= high) {
+ if (position >= low)
+ return curdesc;
+ return NULL;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Return the base behavior in the chain, or NULL if the chain
+ * is empty.
+ *
+ * The caller has not read locked the behavior chain, so acquire the
+ * lock before traversing the chain.
+ */
+bhv_desc_t *
+bhv_base(bhv_head_t *bhp)
+{
+ bhv_desc_t *curdesc;
+
+ for (curdesc = bhp->bh_first;
+ curdesc != NULL;
+ curdesc = curdesc->bd_next) {
+
+ if (curdesc->bd_next == NULL) {
+ return curdesc;
+ }
+ }
+
+ return NULL;
+}
+
+void
+bhv_head_init(
+ bhv_head_t *bhp,
+ char *name)
+{
+ bhp->bh_first = NULL;
+}
+
+void
+bhv_insert_initial(
+ bhv_head_t *bhp,
+ bhv_desc_t *bdp)
+{
+ ASSERT(bhp->bh_first == NULL);
+ (bhp)->bh_first = bdp;
+}
+
+void
+bhv_head_destroy(
+ bhv_head_t *bhp)
+{
+ ASSERT(bhp->bh_first == NULL);
+}
diff --git a/fs/xfs/xfs_behavior.h b/fs/xfs/xfs_behavior.h
new file mode 100644
index 00000000000000..d5ed5a843921c5
--- /dev/null
+++ b/fs/xfs/xfs_behavior.h
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_BEHAVIOR_H__
+#define __XFS_BEHAVIOR_H__
+
+/*
+ * Header file used to associate behaviors with virtualized objects.
+ *
+ * A virtualized object is an internal, virtualized representation of
+ * OS entities such as persistent files, processes, or sockets. Examples
+ * of virtualized objects include vnodes, vprocs, and vsockets. Often
+ * a virtualized object is referred to simply as an "object."
+ *
+ * A behavior is essentially an implementation layer associated with
+ * an object. Multiple behaviors for an object are chained together,
+ * the order of chaining determining the order of invocation. Each
+ * behavior of a given object implements the same set of interfaces
+ * (e.g., the VOP interfaces).
+ *
+ * Behaviors may be dynamically inserted into an object's behavior chain,
+ * such that the addition is transparent to consumers that already have
+ * references to the object. Typically, a given behavior will be inserted
+ * at a particular location in the behavior chain. Insertion of new
+ * behaviors is synchronized with operations-in-progress (oip's) so that
+ * the oip's always see a consistent view of the chain.
+ *
+ * The term "interpostion" is used to refer to the act of inserting
+ * a behavior such that it interposes on (i.e., is inserted in front
+ * of) a particular other behavior. A key example of this is when a
+ * system implementing distributed single system image wishes to
+ * interpose a distribution layer (providing distributed coherency)
+ * in front of an object that is otherwise only accessed locally.
+ *
+ * Note that the traditional vnode/inode combination is simply a virtualized
+ * object that has exactly one associated behavior.
+ *
+ * Behavior synchronization is logic which is necessary under certain
+ * circumstances that there is no conflict between ongoing operations
+ * traversing the behavior chain and those dunamically modifying the
+ * behavior chain. Because behavior synchronization adds extra overhead
+ * to virtual operation invocation, we want to restrict, as much as
+ * we can, the requirement for this extra code, to those situations
+ * in which it is truly necessary.
+ *
+ * Behavior synchronization is needed whenever there's at least one class
+ * of object in the system for which:
+ * 1) multiple behaviors for a given object are supported,
+ * -- AND --
+ * 2a) insertion of a new behavior can happen dynamically at any time during
+ * the life of an active object,
+ * -- AND --
+ * 3a) insertion of a new behavior needs to synchronize with existing
+ * ops-in-progress.
+ * -- OR --
+ * 3b) multiple different behaviors can be dynamically inserted at
+ * any time during the life of an active object
+ * -- OR --
+ * 3c) removal of a behavior can occur at any time during the life of
+ * an active object.
+ * -- OR --
+ * 2b) removal of a behavior can occur at any time during the life of an
+ * active object
+ *
+ */
+
+struct bhv_head_lock;
+
+/*
+ * Behavior head. Head of the chain of behaviors.
+ * Contained within each virtualized object data structure.
+ */
+typedef struct bhv_head {
+ struct bhv_desc *bh_first; /* first behavior in chain */
+ struct bhv_head_lock *bh_lockp; /* pointer to lock info struct */
+} bhv_head_t;
+
+/*
+ * Behavior descriptor. Descriptor associated with each behavior.
+ * Contained within the behavior's private data structure.
+ */
+typedef struct bhv_desc {
+ void *bd_pdata; /* private data for this behavior */
+ void *bd_vobj; /* virtual object associated with */
+ void *bd_ops; /* ops for this behavior */
+ struct bhv_desc *bd_next; /* next behavior in chain */
+} bhv_desc_t;
+
+/*
+ * Behavior identity field. A behavior's identity determines the position
+ * where it lives within a behavior chain, and it's always the first field
+ * of the behavior's ops vector. The optional id field further identifies the
+ * subsystem responsible for the behavior.
+ */
+typedef struct bhv_identity {
+ __u16 bi_id; /* owning subsystem id */
+ __u16 bi_position; /* position in chain */
+} bhv_identity_t;
+
+typedef bhv_identity_t bhv_position_t;
+
+#define BHV_IDENTITY_INIT(id,pos) {id, pos}
+#define BHV_IDENTITY_INIT_POSITION(pos) BHV_IDENTITY_INIT(0, pos)
+
+/*
+ * Define boundaries of position values.
+ */
+#define BHV_POSITION_INVALID 0 /* invalid position number */
+#define BHV_POSITION_BASE 1 /* base (last) implementation layer */
+#define BHV_POSITION_TOP 63 /* top (first) implementation layer */
+
+/*
+ * Plumbing macros.
+ */
+#define BHV_HEAD_FIRST(bhp) (ASSERT((bhp)->bh_first), (bhp)->bh_first)
+#define BHV_NEXT(bdp) (ASSERT((bdp)->bd_next), (bdp)->bd_next)
+#define BHV_NEXTNULL(bdp) ((bdp)->bd_next)
+#define BHV_VOBJ(bdp) (ASSERT((bdp)->bd_vobj), (bdp)->bd_vobj)
+#define BHV_VOBJNULL(bdp) ((bdp)->bd_vobj)
+#define BHV_PDATA(bdp) (bdp)->bd_pdata
+#define BHV_OPS(bdp) (bdp)->bd_ops
+#define BHV_IDENTITY(bdp) ((bhv_identity_t *)(bdp)->bd_ops)
+#define BHV_POSITION(bdp) (BHV_IDENTITY(bdp)->bi_position)
+
+extern void bhv_head_init(bhv_head_t *, char *);
+extern void bhv_head_destroy(bhv_head_t *);
+extern int bhv_insert(bhv_head_t *, bhv_desc_t *);
+extern void bhv_insert_initial(bhv_head_t *, bhv_desc_t *);
+
+/*
+ * Initialize a new behavior descriptor.
+ * Arguments:
+ * bdp - pointer to behavior descriptor
+ * pdata - pointer to behavior's private data
+ * vobj - pointer to associated virtual object
+ * ops - pointer to ops for this behavior
+ */
+#define bhv_desc_init(bdp, pdata, vobj, ops) \
+ { \
+ (bdp)->bd_pdata = pdata; \
+ (bdp)->bd_vobj = vobj; \
+ (bdp)->bd_ops = ops; \
+ (bdp)->bd_next = NULL; \
+ }
+
+/*
+ * Remove a behavior descriptor from a behavior chain.
+ */
+#define bhv_remove(bhp, bdp) \
+ { \
+ if ((bhp)->bh_first == (bdp)) { \
+ /* \
+ * Remove from front of chain. \
+ * Atomic wrt oip's. \
+ */ \
+ (bhp)->bh_first = (bdp)->bd_next; \
+ } else { \
+ /* remove from non-front of chain */ \
+ bhv_remove_not_first(bhp, bdp); \
+ } \
+ (bdp)->bd_vobj = NULL; \
+ }
+
+/*
+ * Behavior module prototypes.
+ */
+extern void bhv_remove_not_first(bhv_head_t *bhp, bhv_desc_t *bdp);
+extern bhv_desc_t * bhv_lookup(bhv_head_t *bhp, void *ops);
+extern bhv_desc_t * bhv_lookup_range(bhv_head_t *bhp, int low, int high);
+extern bhv_desc_t * bhv_base(bhv_head_t *bhp);
+
+/* No bhv locking on Linux */
+#define bhv_lookup_unlocked bhv_lookup
+#define bhv_base_unlocked bhv_base
+
+#endif /* __XFS_BEHAVIOR_H__ */
diff --git a/fs/xfs/xfs_bit.c b/fs/xfs/xfs_bit.c
new file mode 100644
index 00000000000000..a20a6c3dc13e75
--- /dev/null
+++ b/fs/xfs/xfs_bit.c
@@ -0,0 +1,312 @@
+/*
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+/*
+ * XFS bit manipulation routines, used in non-realtime code.
+ */
+
+#include "xfs.h"
+#include "xfs_bit.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_buf_item.h"
+
+
+#ifndef HAVE_ARCH_HIGHBIT
+/*
+ * Index of high bit number in byte, -1 for none set, 0..7 otherwise.
+ */
+const char xfs_highbit[256] = {
+ -1, 0, 1, 1, 2, 2, 2, 2, /* 00 .. 07 */
+ 3, 3, 3, 3, 3, 3, 3, 3, /* 08 .. 0f */
+ 4, 4, 4, 4, 4, 4, 4, 4, /* 10 .. 17 */
+ 4, 4, 4, 4, 4, 4, 4, 4, /* 18 .. 1f */
+ 5, 5, 5, 5, 5, 5, 5, 5, /* 20 .. 27 */
+ 5, 5, 5, 5, 5, 5, 5, 5, /* 28 .. 2f */
+ 5, 5, 5, 5, 5, 5, 5, 5, /* 30 .. 37 */
+ 5, 5, 5, 5, 5, 5, 5, 5, /* 38 .. 3f */
+ 6, 6, 6, 6, 6, 6, 6, 6, /* 40 .. 47 */
+ 6, 6, 6, 6, 6, 6, 6, 6, /* 48 .. 4f */
+ 6, 6, 6, 6, 6, 6, 6, 6, /* 50 .. 57 */
+ 6, 6, 6, 6, 6, 6, 6, 6, /* 58 .. 5f */
+ 6, 6, 6, 6, 6, 6, 6, 6, /* 60 .. 67 */
+ 6, 6, 6, 6, 6, 6, 6, 6, /* 68 .. 6f */
+ 6, 6, 6, 6, 6, 6, 6, 6, /* 70 .. 77 */
+ 6, 6, 6, 6, 6, 6, 6, 6, /* 78 .. 7f */
+ 7, 7, 7, 7, 7, 7, 7, 7, /* 80 .. 87 */
+ 7, 7, 7, 7, 7, 7, 7, 7, /* 88 .. 8f */
+ 7, 7, 7, 7, 7, 7, 7, 7, /* 90 .. 97 */
+ 7, 7, 7, 7, 7, 7, 7, 7, /* 98 .. 9f */
+ 7, 7, 7, 7, 7, 7, 7, 7, /* a0 .. a7 */
+ 7, 7, 7, 7, 7, 7, 7, 7, /* a8 .. af */
+ 7, 7, 7, 7, 7, 7, 7, 7, /* b0 .. b7 */
+ 7, 7, 7, 7, 7, 7, 7, 7, /* b8 .. bf */
+ 7, 7, 7, 7, 7, 7, 7, 7, /* c0 .. c7 */
+ 7, 7, 7, 7, 7, 7, 7, 7, /* c8 .. cf */
+ 7, 7, 7, 7, 7, 7, 7, 7, /* d0 .. d7 */
+ 7, 7, 7, 7, 7, 7, 7, 7, /* d8 .. df */
+ 7, 7, 7, 7, 7, 7, 7, 7, /* e0 .. e7 */
+ 7, 7, 7, 7, 7, 7, 7, 7, /* e8 .. ef */
+ 7, 7, 7, 7, 7, 7, 7, 7, /* f0 .. f7 */
+ 7, 7, 7, 7, 7, 7, 7, 7, /* f8 .. ff */
+};
+#endif
+
+/*
+ * Count of bits set in byte, 0..8.
+ */
+static const char xfs_countbit[256] = {
+ 0, 1, 1, 2, 1, 2, 2, 3, /* 00 .. 07 */
+ 1, 2, 2, 3, 2, 3, 3, 4, /* 08 .. 0f */
+ 1, 2, 2, 3, 2, 3, 3, 4, /* 10 .. 17 */
+ 2, 3, 3, 4, 3, 4, 4, 5, /* 18 .. 1f */
+ 1, 2, 2, 3, 2, 3, 3, 4, /* 20 .. 27 */
+ 2, 3, 3, 4, 3, 4, 4, 5, /* 28 .. 2f */
+ 2, 3, 3, 4, 3, 4, 4, 5, /* 30 .. 37 */
+ 3, 4, 4, 5, 4, 5, 5, 6, /* 38 .. 3f */
+ 1, 2, 2, 3, 2, 3, 3, 4, /* 40 .. 47 */
+ 2, 3, 3, 4, 3, 4, 4, 5, /* 48 .. 4f */
+ 2, 3, 3, 4, 3, 4, 4, 5, /* 50 .. 57 */
+ 3, 4, 4, 5, 4, 5, 5, 6, /* 58 .. 5f */
+ 2, 3, 3, 4, 3, 4, 4, 5, /* 60 .. 67 */
+ 3, 4, 4, 5, 4, 5, 5, 6, /* 68 .. 6f */
+ 3, 4, 4, 5, 4, 5, 5, 6, /* 70 .. 77 */
+ 4, 5, 5, 6, 5, 6, 6, 7, /* 78 .. 7f */
+ 1, 2, 2, 3, 2, 3, 3, 4, /* 80 .. 87 */
+ 2, 3, 3, 4, 3, 4, 4, 5, /* 88 .. 8f */
+ 2, 3, 3, 4, 3, 4, 4, 5, /* 90 .. 97 */
+ 3, 4, 4, 5, 4, 5, 5, 6, /* 98 .. 9f */
+ 2, 3, 3, 4, 3, 4, 4, 5, /* a0 .. a7 */
+ 3, 4, 4, 5, 4, 5, 5, 6, /* a8 .. af */
+ 3, 4, 4, 5, 4, 5, 5, 6, /* b0 .. b7 */
+ 4, 5, 5, 6, 5, 6, 6, 7, /* b8 .. bf */
+ 2, 3, 3, 4, 3, 4, 4, 5, /* c0 .. c7 */
+ 3, 4, 4, 5, 4, 5, 5, 6, /* c8 .. cf */
+ 3, 4, 4, 5, 4, 5, 5, 6, /* d0 .. d7 */
+ 4, 5, 5, 6, 5, 6, 6, 7, /* d8 .. df */
+ 3, 4, 4, 5, 4, 5, 5, 6, /* e0 .. e7 */
+ 4, 5, 5, 6, 5, 6, 6, 7, /* e8 .. ef */
+ 4, 5, 5, 6, 5, 6, 6, 7, /* f0 .. f7 */
+ 5, 6, 6, 7, 6, 7, 7, 8, /* f8 .. ff */
+};
+
+/*
+ * xfs_highbit32: get high bit set out of 32-bit argument, -1 if none set.
+ */
+inline int
+xfs_highbit32(
+ __uint32_t v)
+{
+#ifdef HAVE_ARCH_HIGHBIT
+ return highbit32(v);
+#else
+ int i;
+
+ if (v & 0xffff0000)
+ if (v & 0xff000000)
+ i = 24;
+ else
+ i = 16;
+ else if (v & 0x0000ffff)
+ if (v & 0x0000ff00)
+ i = 8;
+ else
+ i = 0;
+ else
+ return -1;
+ return i + xfs_highbit[(v >> i) & 0xff];
+#endif
+}
+
+/*
+ * xfs_lowbit64: get low bit set out of 64-bit argument, -1 if none set.
+ */
+int
+xfs_lowbit64(
+ __uint64_t v)
+{
+ __uint32_t w = (__uint32_t)v;
+ int n = 0;
+
+ if (w) { /* lower bits */
+ n = ffs(w);
+ } else { /* upper bits */
+ w = (__uint32_t)(v >> 32);
+ if (w && (n = ffs(w)))
+ n += 32;
+ }
+ return n - 1;
+}
+
+/*
+ * xfs_highbit64: get high bit set out of 64-bit argument, -1 if none set.
+ */
+int
+xfs_highbit64(
+ __uint64_t v)
+{
+ __uint32_t h = (__uint32_t)(v >> 32);
+
+ if (h)
+ return xfs_highbit32(h) + 32;
+ return xfs_highbit32((__uint32_t)v);
+}
+
+
+/*
+ * Count the number of bits set in the bitmap starting with bit
+ * start_bit. Size is the size of the bitmap in words.
+ *
+ * Do the counting by mapping a byte value to the number of set
+ * bits for that value using the xfs_countbit array, i.e.
+ * xfs_countbit[0] == 0, xfs_countbit[1] == 1, xfs_countbit[2] == 1,
+ * xfs_countbit[3] == 2, etc.
+ */
+int
+xfs_count_bits(uint *map, uint size, uint start_bit)
+{
+ register int bits;
+ register unsigned char *bytep;
+ register unsigned char *end_map;
+ int byte_bit;
+
+ bits = 0;
+ end_map = (char*)(map + size);
+ bytep = (char*)(map + (start_bit & ~0x7));
+ byte_bit = start_bit & 0x7;
+
+ /*
+ * If the caller fell off the end of the map, return 0.
+ */
+ if (bytep >= end_map) {
+ return (0);
+ }
+
+ /*
+ * If start_bit is not byte aligned, then process the
+ * first byte separately.
+ */
+ if (byte_bit != 0) {
+ /*
+ * Shift off the bits we don't want to look at,
+ * before indexing into xfs_countbit.
+ */
+ bits += xfs_countbit[(*bytep >> byte_bit)];
+ bytep++;
+ }
+
+ /*
+ * Count the bits in each byte until the end of the bitmap.
+ */
+ while (bytep < end_map) {
+ bits += xfs_countbit[*bytep];
+ bytep++;
+ }
+
+ return (bits);
+}
+
+/*
+ * Count the number of contiguous bits set in the bitmap starting with bit
+ * start_bit. Size is the size of the bitmap in words.
+ */
+int
+xfs_contig_bits(uint *map, uint size, uint start_bit)
+{
+ uint * p = ((unsigned int *) map) + (start_bit >> BIT_TO_WORD_SHIFT);
+ uint result = 0;
+ uint tmp;
+
+ size <<= BIT_TO_WORD_SHIFT;
+
+ ASSERT(start_bit < size);
+ size -= start_bit & ~(NBWORD - 1);
+ start_bit &= (NBWORD - 1);
+ if (start_bit) {
+ tmp = *p++;
+ /* set to one first offset bits prior to start */
+ tmp |= (~0U >> (NBWORD-start_bit));
+ if (tmp != ~0U)
+ goto found;
+ result += NBWORD;
+ size -= NBWORD;
+ }
+ while (size) {
+ if ((tmp = *p++) != ~0U)
+ goto found;
+ result += NBWORD;
+ size -= NBWORD;
+ }
+ return result - start_bit;
+found:
+ return result + ffz(tmp) - start_bit;
+}
+
+/*
+ * This takes the bit number to start looking from and
+ * returns the next set bit from there. It returns -1
+ * if there are no more bits set or the start bit is
+ * beyond the end of the bitmap.
+ *
+ * Size is the number of words, not bytes, in the bitmap.
+ */
+int xfs_next_bit(uint *map, uint size, uint start_bit)
+{
+ uint * p = ((unsigned int *) map) + (start_bit >> BIT_TO_WORD_SHIFT);
+ uint result = start_bit & ~(NBWORD - 1);
+ uint tmp;
+
+ size <<= BIT_TO_WORD_SHIFT;
+
+ if (start_bit >= size)
+ return -1;
+ size -= result;
+ start_bit &= (NBWORD - 1);
+ if (start_bit) {
+ tmp = *p++;
+ /* set to zero first offset bits prior to start */
+ tmp &= (~0U << start_bit);
+ if (tmp != 0U)
+ goto found;
+ result += NBWORD;
+ size -= NBWORD;
+ }
+ while (size) {
+ if ((tmp = *p++) != 0U)
+ goto found;
+ result += NBWORD;
+ size -= NBWORD;
+ }
+ return -1;
+found:
+ return result + ffs(tmp) - 1;
+}
diff --git a/fs/xfs/xfs_bit.h b/fs/xfs/xfs_bit.h
new file mode 100644
index 00000000000000..1e7f57ddf7a864
--- /dev/null
+++ b/fs/xfs/xfs_bit.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2000, 2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_BIT_H__
+#define __XFS_BIT_H__
+
+/*
+ * XFS bit manipulation routines.
+ */
+
+/*
+ * masks with n high/low bits set, 32-bit values & 64-bit values
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_MASK32HI)
+__uint32_t xfs_mask32hi(int n);
+#define XFS_MASK32HI(n) xfs_mask32hi(n)
+#else
+#define XFS_MASK32HI(n) ((__uint32_t)-1 << (32 - (n)))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_MASK64HI)
+__uint64_t xfs_mask64hi(int n);
+#define XFS_MASK64HI(n) xfs_mask64hi(n)
+#else
+#define XFS_MASK64HI(n) ((__uint64_t)-1 << (64 - (n)))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_MASK32LO)
+__uint32_t xfs_mask32lo(int n);
+#define XFS_MASK32LO(n) xfs_mask32lo(n)
+#else
+#define XFS_MASK32LO(n) (((__uint32_t)1 << (n)) - 1)
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_MASK64LO)
+__uint64_t xfs_mask64lo(int n);
+#define XFS_MASK64LO(n) xfs_mask64lo(n)
+#else
+#define XFS_MASK64LO(n) (((__uint64_t)1 << (n)) - 1)
+#endif
+
+/* Get high bit set out of 32-bit argument, -1 if none set */
+extern int xfs_highbit32(__uint32_t v);
+
+/* Get low bit set out of 64-bit argument, -1 if none set */
+extern int xfs_lowbit64(__uint64_t v);
+
+/* Get high bit set out of 64-bit argument, -1 if none set */
+extern int xfs_highbit64(__uint64_t);
+
+/* Count set bits in map starting with start_bit */
+extern int xfs_count_bits(uint *map, uint size, uint start_bit);
+
+/* Count continuous one bits in map starting with start_bit */
+extern int xfs_contig_bits(uint *map, uint size, uint start_bit);
+
+/* Find next set bit in map */
+extern int xfs_next_bit(uint *map, uint size, uint start_bit);
+
+#endif /* __XFS_BIT_H__ */
diff --git a/fs/xfs/xfs_bmap.c b/fs/xfs/xfs_bmap.c
new file mode 100644
index 00000000000000..4b0738238809c4
--- /dev/null
+++ b/fs/xfs/xfs_bmap.c
@@ -0,0 +1,6246 @@
+/*
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode_item.h"
+#include "xfs_inode.h"
+#include "xfs_itable.h"
+#include "xfs_extfree_item.h"
+#include "xfs_alloc.h"
+#include "xfs_bmap.h"
+#include "xfs_rtalloc.h"
+#include "xfs_error.h"
+#include "xfs_da_btree.h"
+#include "xfs_dir_leaf.h"
+#include "xfs_bit.h"
+#include "xfs_rw.h"
+#include "xfs_quota.h"
+#include "xfs_trans_space.h"
+#include "xfs_buf_item.h"
+
+
+#ifdef DEBUG
+STATIC void
+xfs_bmap_check_leaf_extents(xfs_btree_cur_t *cur, xfs_inode_t *ip, int whichfork);
+#endif
+
+kmem_zone_t *xfs_bmap_free_item_zone;
+
+/*
+ * Prototypes for internal bmap routines.
+ */
+
+
+/*
+ * Called from xfs_bmap_add_attrfork to handle extents format files.
+ */
+STATIC int /* error */
+xfs_bmap_add_attrfork_extents(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_fsblock_t *firstblock, /* first block allocated */
+ xfs_bmap_free_t *flist, /* blocks to free at commit */
+ int *flags); /* inode logging flags */
+
+/*
+ * Called from xfs_bmap_add_attrfork to handle local format files.
+ */
+STATIC int /* error */
+xfs_bmap_add_attrfork_local(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_fsblock_t *firstblock, /* first block allocated */
+ xfs_bmap_free_t *flist, /* blocks to free at commit */
+ int *flags); /* inode logging flags */
+
+/*
+ * Called by xfs_bmapi to update extent list structure and the btree
+ * after allocating space (or doing a delayed allocation).
+ */
+STATIC int /* error */
+xfs_bmap_add_extent(
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_extnum_t idx, /* extent number to update/insert */
+ xfs_btree_cur_t **curp, /* if *curp is null, not a btree */
+ xfs_bmbt_irec_t *new, /* new data to put in extent list */
+ xfs_fsblock_t *first, /* pointer to firstblock variable */
+ xfs_bmap_free_t *flist, /* list of extents to be freed */
+ int *logflagsp, /* inode logging flags */
+ int whichfork, /* data or attr fork */
+ int rsvd); /* OK to allocate reserved blocks */
+
+/*
+ * Called by xfs_bmap_add_extent to handle cases converting a delayed
+ * allocation to a real allocation.
+ */
+STATIC int /* error */
+xfs_bmap_add_extent_delay_real(
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_extnum_t idx, /* extent number to update/insert */
+ xfs_btree_cur_t **curp, /* if *curp is null, not a btree */
+ xfs_bmbt_irec_t *new, /* new data to put in extent list */
+ xfs_filblks_t *dnew, /* new delayed-alloc indirect blocks */
+ xfs_fsblock_t *first, /* pointer to firstblock variable */
+ xfs_bmap_free_t *flist, /* list of extents to be freed */
+ int *logflagsp, /* inode logging flags */
+ int rsvd); /* OK to allocate reserved blocks */
+
+/*
+ * Called by xfs_bmap_add_extent to handle cases converting a hole
+ * to a delayed allocation.
+ */
+STATIC int /* error */
+xfs_bmap_add_extent_hole_delay(
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_extnum_t idx, /* extent number to update/insert */
+ xfs_btree_cur_t *cur, /* if null, not a btree */
+ xfs_bmbt_irec_t *new, /* new data to put in extent list */
+ int *logflagsp,/* inode logging flags */
+ int rsvd); /* OK to allocate reserved blocks */
+
+/*
+ * Called by xfs_bmap_add_extent to handle cases converting a hole
+ * to a real allocation.
+ */
+STATIC int /* error */
+xfs_bmap_add_extent_hole_real(
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_extnum_t idx, /* extent number to update/insert */
+ xfs_btree_cur_t *cur, /* if null, not a btree */
+ xfs_bmbt_irec_t *new, /* new data to put in extent list */
+ int *logflagsp, /* inode logging flags */
+ int whichfork); /* data or attr fork */
+
+/*
+ * Called by xfs_bmap_add_extent to handle cases converting an unwritten
+ * allocation to a real allocation or vice versa.
+ */
+STATIC int /* error */
+xfs_bmap_add_extent_unwritten_real(
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_extnum_t idx, /* extent number to update/insert */
+ xfs_btree_cur_t **curp, /* if *curp is null, not a btree */
+ xfs_bmbt_irec_t *new, /* new data to put in extent list */
+ int *logflagsp); /* inode logging flags */
+
+/*
+ * xfs_bmap_alloc is called by xfs_bmapi to allocate an extent for a file.
+ * It figures out where to ask the underlying allocator to put the new extent.
+ */
+STATIC int /* error */
+xfs_bmap_alloc(
+ xfs_bmalloca_t *ap); /* bmap alloc argument struct */
+
+/*
+ * Transform a btree format file with only one leaf node, where the
+ * extents list will fit in the inode, into an extents format file.
+ * Since the extent list is already in-core, all we have to do is
+ * give up the space for the btree root and pitch the leaf block.
+ */
+STATIC int /* error */
+xfs_bmap_btree_to_extents(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_btree_cur_t *cur, /* btree cursor */
+ int *logflagsp, /* inode logging flags */
+ int whichfork); /* data or attr fork */
+
+#ifdef DEBUG
+/*
+ * Check that the extents list for the inode ip is in the right order.
+ */
+STATIC void
+xfs_bmap_check_extents(
+ xfs_inode_t *ip, /* incore inode pointer */
+ int whichfork); /* data or attr fork */
+#endif
+
+/*
+ * Called by xfs_bmapi to update extent list structure and the btree
+ * after removing space (or undoing a delayed allocation).
+ */
+STATIC int /* error */
+xfs_bmap_del_extent(
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_trans_t *tp, /* current trans pointer */
+ xfs_extnum_t idx, /* extent number to update/insert */
+ xfs_bmap_free_t *flist, /* list of extents to be freed */
+ xfs_btree_cur_t *cur, /* if null, not a btree */
+ xfs_bmbt_irec_t *new, /* new data to put in extent list */
+ int *logflagsp,/* inode logging flags */
+ int whichfork, /* data or attr fork */
+ int rsvd); /* OK to allocate reserved blocks */
+
+/*
+ * Remove the entry "free" from the free item list. Prev points to the
+ * previous entry, unless "free" is the head of the list.
+ */
+STATIC void
+xfs_bmap_del_free(
+ xfs_bmap_free_t *flist, /* free item list header */
+ xfs_bmap_free_item_t *prev, /* previous item on list, if any */
+ xfs_bmap_free_item_t *free); /* list item to be freed */
+
+/*
+ * Remove count entries from the extents array for inode "ip", starting
+ * at index "idx". Copies the remaining items down over the deleted ones,
+ * and gives back the excess memory.
+ */
+STATIC void
+xfs_bmap_delete_exlist(
+ xfs_inode_t *ip, /* incode inode pointer */
+ xfs_extnum_t idx, /* starting delete index */
+ xfs_extnum_t count, /* count of items to delete */
+ int whichfork); /* data or attr fork */
+
+/*
+ * Convert an extents-format file into a btree-format file.
+ * The new file will have a root block (in the inode) and a single child block.
+ */
+STATIC int /* error */
+xfs_bmap_extents_to_btree(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_fsblock_t *firstblock, /* first-block-allocated */
+ xfs_bmap_free_t *flist, /* blocks freed in xaction */
+ xfs_btree_cur_t **curp, /* cursor returned to caller */
+ int wasdel, /* converting a delayed alloc */
+ int *logflagsp, /* inode logging flags */
+ int whichfork); /* data or attr fork */
+
+/*
+ * Insert new item(s) in the extent list for inode "ip".
+ * Count new items are inserted at offset idx.
+ */
+STATIC void
+xfs_bmap_insert_exlist(
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_extnum_t idx, /* starting index of new items */
+ xfs_extnum_t count, /* number of inserted items */
+ xfs_bmbt_irec_t *new, /* items to insert */
+ int whichfork); /* data or attr fork */
+
+/*
+ * Convert a local file to an extents file.
+ * This code is sort of bogus, since the file data needs to get
+ * logged so it won't be lost. The bmap-level manipulations are ok, though.
+ */
+STATIC int /* error */
+xfs_bmap_local_to_extents(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_fsblock_t *firstblock, /* first block allocated in xaction */
+ xfs_extlen_t total, /* total blocks needed by transaction */
+ int *logflagsp, /* inode logging flags */
+ int whichfork); /* data or attr fork */
+
+/*
+ * Search the extents list for the inode, for the extent containing bno.
+ * If bno lies in a hole, point to the next entry. If bno lies past eof,
+ * *eofp will be set, and *prevp will contain the last entry (null if none).
+ * Else, *lastxp will be set to the index of the found
+ * entry; *gotp will contain the entry.
+ */
+STATIC xfs_bmbt_rec_t * /* pointer to found extent entry */
+xfs_bmap_search_extents(
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_fileoff_t bno, /* block number searched for */
+ int whichfork, /* data or attr fork */
+ int *eofp, /* out: end of file found */
+ xfs_extnum_t *lastxp, /* out: last extent index */
+ xfs_bmbt_irec_t *gotp, /* out: extent entry found */
+ xfs_bmbt_irec_t *prevp); /* out: previous extent entry found */
+
+#ifdef XFS_BMAP_TRACE
+/*
+ * Add a bmap trace buffer entry. Base routine for the others.
+ */
+STATIC void
+xfs_bmap_trace_addentry(
+ int opcode, /* operation */
+ char *fname, /* function name */
+ char *desc, /* operation description */
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_extnum_t idx, /* index of entry(ies) */
+ xfs_extnum_t cnt, /* count of entries, 1 or 2 */
+ xfs_bmbt_rec_t *r1, /* first record */
+ xfs_bmbt_rec_t *r2, /* second record or null */
+ int whichfork); /* data or attr fork */
+
+/*
+ * Add bmap trace entry prior to a call to xfs_bmap_delete_exlist.
+ */
+STATIC void
+xfs_bmap_trace_delete(
+ char *fname, /* function name */
+ char *desc, /* operation description */
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_extnum_t idx, /* index of entry(entries) deleted */
+ xfs_extnum_t cnt, /* count of entries deleted, 1 or 2 */
+ int whichfork); /* data or attr fork */
+
+/*
+ * Add bmap trace entry prior to a call to xfs_bmap_insert_exlist, or
+ * reading in the extents list from the disk (in the btree).
+ */
+STATIC void
+xfs_bmap_trace_insert(
+ char *fname, /* function name */
+ char *desc, /* operation description */
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_extnum_t idx, /* index of entry(entries) inserted */
+ xfs_extnum_t cnt, /* count of entries inserted, 1 or 2 */
+ xfs_bmbt_irec_t *r1, /* inserted record 1 */
+ xfs_bmbt_irec_t *r2, /* inserted record 2 or null */
+ int whichfork); /* data or attr fork */
+
+/*
+ * Add bmap trace entry after updating an extent list entry in place.
+ */
+STATIC void
+xfs_bmap_trace_post_update(
+ char *fname, /* function name */
+ char *desc, /* operation description */
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_extnum_t idx, /* index of entry updated */
+ int whichfork); /* data or attr fork */
+
+/*
+ * Add bmap trace entry prior to updating an extent list entry in place.
+ */
+STATIC void
+xfs_bmap_trace_pre_update(
+ char *fname, /* function name */
+ char *desc, /* operation description */
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_extnum_t idx, /* index of entry to be updated */
+ int whichfork); /* data or attr fork */
+
+#else
+#define xfs_bmap_trace_delete(f,d,ip,i,c,w)
+#define xfs_bmap_trace_insert(f,d,ip,i,c,r1,r2,w)
+#define xfs_bmap_trace_post_update(f,d,ip,i,w)
+#define xfs_bmap_trace_pre_update(f,d,ip,i,w)
+#endif /* XFS_BMAP_TRACE */
+
+/*
+ * Compute the worst-case number of indirect blocks that will be used
+ * for ip's delayed extent of length "len".
+ */
+STATIC xfs_filblks_t
+xfs_bmap_worst_indlen(
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_filblks_t len); /* delayed extent length */
+
+#ifdef DEBUG
+/*
+ * Perform various validation checks on the values being returned
+ * from xfs_bmapi().
+ */
+STATIC void
+xfs_bmap_validate_ret(
+ xfs_fileoff_t bno,
+ xfs_filblks_t len,
+ int flags,
+ xfs_bmbt_irec_t *mval,
+ int nmap,
+ int ret_nmap);
+#else
+#define xfs_bmap_validate_ret(bno,len,flags,mval,onmap,nmap)
+#endif /* DEBUG */
+
+#if defined(XFS_RW_TRACE)
+STATIC void
+xfs_bunmap_trace(
+ xfs_inode_t *ip,
+ xfs_fileoff_t bno,
+ xfs_filblks_t len,
+ int flags,
+ inst_t *ra);
+#else
+#define xfs_bunmap_trace(ip, bno, len, flags, ra)
+#endif /* XFS_RW_TRACE */
+
+STATIC int
+xfs_bmap_count_tree(
+ xfs_mount_t *mp,
+ xfs_trans_t *tp,
+ xfs_fsblock_t blockno,
+ int levelin,
+ int *count);
+
+STATIC int
+xfs_bmap_count_leaves(
+ xfs_bmbt_rec_t *frp,
+ int numrecs,
+ int *count);
+
+/*
+ * Bmap internal routines.
+ */
+
+/*
+ * Called from xfs_bmap_add_attrfork to handle btree format files.
+ */
+STATIC int /* error */
+xfs_bmap_add_attrfork_btree(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_fsblock_t *firstblock, /* first block allocated */
+ xfs_bmap_free_t *flist, /* blocks to free at commit */
+ int *flags) /* inode logging flags */
+{
+ xfs_btree_cur_t *cur; /* btree cursor */
+ int error; /* error return value */
+ xfs_mount_t *mp; /* file system mount struct */
+ int stat; /* newroot status */
+
+ mp = ip->i_mount;
+ if (ip->i_df.if_broot_bytes <= XFS_IFORK_DSIZE(ip))
+ *flags |= XFS_ILOG_DBROOT;
+ else {
+ cur = xfs_btree_init_cursor(mp, tp, NULL, 0, XFS_BTNUM_BMAP, ip,
+ XFS_DATA_FORK);
+ cur->bc_private.b.flist = flist;
+ cur->bc_private.b.firstblock = *firstblock;
+ if ((error = xfs_bmbt_lookup_ge(cur, 0, 0, 0, &stat)))
+ goto error0;
+ ASSERT(stat == 1); /* must be at least one entry */
+ if ((error = xfs_bmbt_newroot(cur, flags, &stat)))
+ goto error0;
+ if (stat == 0) {
+ xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
+ return XFS_ERROR(ENOSPC);
+ }
+ *firstblock = cur->bc_private.b.firstblock;
+ cur->bc_private.b.allocated = 0;
+ xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
+ }
+ return 0;
+error0:
+ xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
+ return error;
+}
+
+/*
+ * Called from xfs_bmap_add_attrfork to handle extents format files.
+ */
+STATIC int /* error */
+xfs_bmap_add_attrfork_extents(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_fsblock_t *firstblock, /* first block allocated */
+ xfs_bmap_free_t *flist, /* blocks to free at commit */
+ int *flags) /* inode logging flags */
+{
+ xfs_btree_cur_t *cur; /* bmap btree cursor */
+ int error; /* error return value */
+
+ if (ip->i_d.di_nextents * sizeof(xfs_bmbt_rec_t) <= XFS_IFORK_DSIZE(ip))
+ return 0;
+ cur = NULL;
+ error = xfs_bmap_extents_to_btree(tp, ip, firstblock, flist, &cur, 0,
+ flags, XFS_DATA_FORK);
+ if (cur) {
+ cur->bc_private.b.allocated = 0;
+ xfs_btree_del_cursor(cur,
+ error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
+ }
+ return error;
+}
+
+/*
+ * Called from xfs_bmap_add_attrfork to handle local format files.
+ */
+STATIC int /* error */
+xfs_bmap_add_attrfork_local(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_fsblock_t *firstblock, /* first block allocated */
+ xfs_bmap_free_t *flist, /* blocks to free at commit */
+ int *flags) /* inode logging flags */
+{
+ xfs_da_args_t dargs; /* args for dir/attr code */
+ int error; /* error return value */
+ xfs_mount_t *mp; /* mount structure pointer */
+
+ if (ip->i_df.if_bytes <= XFS_IFORK_DSIZE(ip))
+ return 0;
+ if ((ip->i_d.di_mode & S_IFMT) == S_IFDIR) {
+ mp = ip->i_mount;
+ memset(&dargs, 0, sizeof(dargs));
+ dargs.dp = ip;
+ dargs.firstblock = firstblock;
+ dargs.flist = flist;
+ dargs.total = mp->m_dirblkfsbs;
+ dargs.whichfork = XFS_DATA_FORK;
+ dargs.trans = tp;
+ error = XFS_DIR_SHORTFORM_TO_SINGLE(mp, &dargs);
+ } else
+ error = xfs_bmap_local_to_extents(tp, ip, firstblock, 1, flags,
+ XFS_DATA_FORK);
+ return error;
+}
+
+/*
+ * Called by xfs_bmapi to update extent list structure and the btree
+ * after allocating space (or doing a delayed allocation).
+ */
+STATIC int /* error */
+xfs_bmap_add_extent(
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_extnum_t idx, /* extent number to update/insert */
+ xfs_btree_cur_t **curp, /* if *curp is null, not a btree */
+ xfs_bmbt_irec_t *new, /* new data to put in extent list */
+ xfs_fsblock_t *first, /* pointer to firstblock variable */
+ xfs_bmap_free_t *flist, /* list of extents to be freed */
+ int *logflagsp, /* inode logging flags */
+ int whichfork, /* data or attr fork */
+ int rsvd) /* OK to use reserved data blocks */
+{
+ xfs_btree_cur_t *cur; /* btree cursor or null */
+ xfs_filblks_t da_new; /* new count del alloc blocks used */
+ xfs_filblks_t da_old; /* old count del alloc blocks used */
+ int error; /* error return value */
+#ifdef XFS_BMAP_TRACE
+ static char fname[] = "xfs_bmap_add_extent";
+#endif
+ xfs_ifork_t *ifp; /* inode fork ptr */
+ int logflags; /* returned value */
+ xfs_extnum_t nextents; /* number of extents in file now */
+
+ XFS_STATS_INC(xs_add_exlist);
+ cur = *curp;
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+ nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
+ ASSERT(idx <= nextents);
+ da_old = da_new = 0;
+ error = 0;
+ /*
+ * This is the first extent added to a new/empty file.
+ * Special case this one, so other routines get to assume there are
+ * already extents in the list.
+ */
+ if (nextents == 0) {
+ xfs_bmap_trace_insert(fname, "insert empty", ip, 0, 1, new,
+ NULL, whichfork);
+ xfs_bmap_insert_exlist(ip, 0, 1, new, whichfork);
+ ASSERT(cur == NULL);
+ ifp->if_lastex = 0;
+ if (!ISNULLSTARTBLOCK(new->br_startblock)) {
+ XFS_IFORK_NEXT_SET(ip, whichfork, 1);
+ logflags = XFS_ILOG_CORE | XFS_ILOG_FEXT(whichfork);
+ } else
+ logflags = 0;
+ }
+ /*
+ * Any kind of new delayed allocation goes here.
+ */
+ else if (ISNULLSTARTBLOCK(new->br_startblock)) {
+ if (cur)
+ ASSERT((cur->bc_private.b.flags &
+ XFS_BTCUR_BPRV_WASDEL) == 0);
+ if ((error = xfs_bmap_add_extent_hole_delay(ip, idx, cur, new,
+ &logflags, rsvd)))
+ goto done;
+ }
+ /*
+ * Real allocation off the end of the file.
+ */
+ else if (idx == nextents) {
+ if (cur)
+ ASSERT((cur->bc_private.b.flags &
+ XFS_BTCUR_BPRV_WASDEL) == 0);
+ if ((error = xfs_bmap_add_extent_hole_real(ip, idx, cur, new,
+ &logflags, whichfork)))
+ goto done;
+ } else {
+ xfs_bmbt_irec_t prev; /* old extent at offset idx */
+
+ /*
+ * Get the record referred to by idx.
+ */
+ xfs_bmbt_get_all(&ifp->if_u1.if_extents[idx], &prev);
+ /*
+ * If it's a real allocation record, and the new allocation ends
+ * after the start of the referred to record, then we're filling
+ * in a delayed or unwritten allocation with a real one, or
+ * converting real back to unwritten.
+ */
+ if (!ISNULLSTARTBLOCK(new->br_startblock) &&
+ new->br_startoff + new->br_blockcount > prev.br_startoff) {
+ if (prev.br_state != XFS_EXT_UNWRITTEN &&
+ ISNULLSTARTBLOCK(prev.br_startblock)) {
+ da_old = STARTBLOCKVAL(prev.br_startblock);
+ if (cur)
+ ASSERT(cur->bc_private.b.flags &
+ XFS_BTCUR_BPRV_WASDEL);
+ if ((error = xfs_bmap_add_extent_delay_real(ip,
+ idx, &cur, new, &da_new, first, flist,
+ &logflags, rsvd)))
+ goto done;
+ } else if (new->br_state == XFS_EXT_NORM) {
+ ASSERT(new->br_state == XFS_EXT_NORM);
+ if ((error = xfs_bmap_add_extent_unwritten_real(
+ ip, idx, &cur, new, &logflags)))
+ goto done;
+ } else {
+ ASSERT(new->br_state == XFS_EXT_UNWRITTEN);
+ if ((error = xfs_bmap_add_extent_unwritten_real(
+ ip, idx, &cur, new, &logflags)))
+ goto done;
+ }
+ ASSERT(*curp == cur || *curp == NULL);
+ }
+ /*
+ * Otherwise we're filling in a hole with an allocation.
+ */
+ else {
+ if (cur)
+ ASSERT((cur->bc_private.b.flags &
+ XFS_BTCUR_BPRV_WASDEL) == 0);
+ if ((error = xfs_bmap_add_extent_hole_real(ip, idx, cur,
+ new, &logflags, whichfork)))
+ goto done;
+ }
+ }
+
+ ASSERT(*curp == cur || *curp == NULL);
+ /*
+ * Convert to a btree if necessary.
+ */
+ if (XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_EXTENTS &&
+ XFS_IFORK_NEXTENTS(ip, whichfork) > ifp->if_ext_max) {
+ int tmp_logflags; /* partial log flag return val */
+
+ ASSERT(cur == NULL);
+ error = xfs_bmap_extents_to_btree(ip->i_transp, ip, first,
+ flist, &cur, da_old > 0, &tmp_logflags, whichfork);
+ logflags |= tmp_logflags;
+ if (error)
+ goto done;
+ }
+ /*
+ * Adjust for changes in reserved delayed indirect blocks.
+ * Nothing to do for disk quotas here.
+ */
+ if (da_old || da_new) {
+ xfs_filblks_t nblks;
+
+ nblks = da_new;
+ if (cur)
+ nblks += cur->bc_private.b.allocated;
+ ASSERT(nblks <= da_old);
+ if (nblks < da_old)
+ xfs_mod_incore_sb(ip->i_mount, XFS_SBS_FDBLOCKS,
+ (int)(da_old - nblks), rsvd);
+ }
+ /*
+ * Clear out the allocated field, done with it now in any case.
+ */
+ if (cur) {
+ cur->bc_private.b.allocated = 0;
+ *curp = cur;
+ }
+done:
+#ifdef DEBUG
+ if (!error)
+ xfs_bmap_check_leaf_extents(*curp, ip, whichfork);
+#endif
+ *logflagsp = logflags;
+ return error;
+}
+
+/*
+ * Called by xfs_bmap_add_extent to handle cases converting a delayed
+ * allocation to a real allocation.
+ */
+STATIC int /* error */
+xfs_bmap_add_extent_delay_real(
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_extnum_t idx, /* extent number to update/insert */
+ xfs_btree_cur_t **curp, /* if *curp is null, not a btree */
+ xfs_bmbt_irec_t *new, /* new data to put in extent list */
+ xfs_filblks_t *dnew, /* new delayed-alloc indirect blocks */
+ xfs_fsblock_t *first, /* pointer to firstblock variable */
+ xfs_bmap_free_t *flist, /* list of extents to be freed */
+ int *logflagsp, /* inode logging flags */
+ int rsvd) /* OK to use reserved data block allocation */
+{
+ xfs_bmbt_rec_t *base; /* base of extent entry list */
+ xfs_btree_cur_t *cur; /* btree cursor */
+ int diff; /* temp value */
+ xfs_bmbt_rec_t *ep; /* extent entry for idx */
+ int error; /* error return value */
+#ifdef XFS_BMAP_TRACE
+ static char fname[] = "xfs_bmap_add_extent_delay_real";
+#endif
+ int i; /* temp state */
+ xfs_fileoff_t new_endoff; /* end offset of new entry */
+ xfs_bmbt_irec_t r[3]; /* neighbor extent entries */
+ /* left is 0, right is 1, prev is 2 */
+ int rval=0; /* return value (logging flags) */
+ int state = 0;/* state bits, accessed thru macros */
+ xfs_filblks_t temp; /* value for dnew calculations */
+ xfs_filblks_t temp2; /* value for dnew calculations */
+ int tmp_rval; /* partial logging flags */
+ enum { /* bit number definitions for state */
+ LEFT_CONTIG, RIGHT_CONTIG,
+ LEFT_FILLING, RIGHT_FILLING,
+ LEFT_DELAY, RIGHT_DELAY,
+ LEFT_VALID, RIGHT_VALID
+ };
+
+#define LEFT r[0]
+#define RIGHT r[1]
+#define PREV r[2]
+#define MASK(b) (1 << (b))
+#define MASK2(a,b) (MASK(a) | MASK(b))
+#define MASK3(a,b,c) (MASK2(a,b) | MASK(c))
+#define MASK4(a,b,c,d) (MASK3(a,b,c) | MASK(d))
+#define STATE_SET(b,v) ((v) ? (state |= MASK(b)) : (state &= ~MASK(b)))
+#define STATE_TEST(b) (state & MASK(b))
+#define STATE_SET_TEST(b,v) ((v) ? ((state |= MASK(b)), 1) : \
+ ((state &= ~MASK(b)), 0))
+#define SWITCH_STATE \
+ (state & MASK4(LEFT_FILLING, RIGHT_FILLING, LEFT_CONTIG, RIGHT_CONTIG))
+
+ /*
+ * Set up a bunch of variables to make the tests simpler.
+ */
+ cur = *curp;
+ base = ip->i_df.if_u1.if_extents;
+ ep = &base[idx];
+ xfs_bmbt_get_all(ep, &PREV);
+ new_endoff = new->br_startoff + new->br_blockcount;
+ ASSERT(PREV.br_startoff <= new->br_startoff);
+ ASSERT(PREV.br_startoff + PREV.br_blockcount >= new_endoff);
+ /*
+ * Set flags determining what part of the previous delayed allocation
+ * extent is being replaced by a real allocation.
+ */
+ STATE_SET(LEFT_FILLING, PREV.br_startoff == new->br_startoff);
+ STATE_SET(RIGHT_FILLING,
+ PREV.br_startoff + PREV.br_blockcount == new_endoff);
+ /*
+ * Check and set flags if this segment has a left neighbor.
+ * Don't set contiguous if the combined extent would be too large.
+ */
+ if (STATE_SET_TEST(LEFT_VALID, idx > 0)) {
+ xfs_bmbt_get_all(ep - 1, &LEFT);
+ STATE_SET(LEFT_DELAY, ISNULLSTARTBLOCK(LEFT.br_startblock));
+ }
+ STATE_SET(LEFT_CONTIG,
+ STATE_TEST(LEFT_VALID) && !STATE_TEST(LEFT_DELAY) &&
+ LEFT.br_startoff + LEFT.br_blockcount == new->br_startoff &&
+ LEFT.br_startblock + LEFT.br_blockcount == new->br_startblock &&
+ LEFT.br_state == new->br_state &&
+ LEFT.br_blockcount + new->br_blockcount <= MAXEXTLEN);
+ /*
+ * Check and set flags if this segment has a right neighbor.
+ * Don't set contiguous if the combined extent would be too large.
+ * Also check for all-three-contiguous being too large.
+ */
+ if (STATE_SET_TEST(RIGHT_VALID,
+ idx <
+ ip->i_df.if_bytes / (uint)sizeof(xfs_bmbt_rec_t) - 1)) {
+ xfs_bmbt_get_all(ep + 1, &RIGHT);
+ STATE_SET(RIGHT_DELAY, ISNULLSTARTBLOCK(RIGHT.br_startblock));
+ }
+ STATE_SET(RIGHT_CONTIG,
+ STATE_TEST(RIGHT_VALID) && !STATE_TEST(RIGHT_DELAY) &&
+ new_endoff == RIGHT.br_startoff &&
+ new->br_startblock + new->br_blockcount ==
+ RIGHT.br_startblock &&
+ new->br_state == RIGHT.br_state &&
+ new->br_blockcount + RIGHT.br_blockcount <= MAXEXTLEN &&
+ ((state & MASK3(LEFT_CONTIG, LEFT_FILLING, RIGHT_FILLING)) !=
+ MASK3(LEFT_CONTIG, LEFT_FILLING, RIGHT_FILLING) ||
+ LEFT.br_blockcount + new->br_blockcount + RIGHT.br_blockcount
+ <= MAXEXTLEN));
+ error = 0;
+ /*
+ * Switch out based on the FILLING and CONTIG state bits.
+ */
+ switch (SWITCH_STATE) {
+
+ case MASK4(LEFT_FILLING, RIGHT_FILLING, LEFT_CONTIG, RIGHT_CONTIG):
+ /*
+ * Filling in all of a previously delayed allocation extent.
+ * The left and right neighbors are both contiguous with new.
+ */
+ xfs_bmap_trace_pre_update(fname, "LF|RF|LC|RC", ip, idx - 1,
+ XFS_DATA_FORK);
+ xfs_bmbt_set_blockcount(ep - 1,
+ LEFT.br_blockcount + PREV.br_blockcount +
+ RIGHT.br_blockcount);
+ xfs_bmap_trace_post_update(fname, "LF|RF|LC|RC", ip, idx - 1,
+ XFS_DATA_FORK);
+ xfs_bmap_trace_delete(fname, "LF|RF|LC|RC", ip, idx, 2,
+ XFS_DATA_FORK);
+ xfs_bmap_delete_exlist(ip, idx, 2, XFS_DATA_FORK);
+ ip->i_df.if_lastex = idx - 1;
+ ip->i_d.di_nextents--;
+ if (cur == NULL)
+ rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
+ else {
+ rval = XFS_ILOG_CORE;
+ if ((error = xfs_bmbt_lookup_eq(cur, RIGHT.br_startoff,
+ RIGHT.br_startblock,
+ RIGHT.br_blockcount, &i)))
+ goto done;
+ ASSERT(i == 1);
+ if ((error = xfs_bmbt_delete(cur, &i)))
+ goto done;
+ ASSERT(i == 1);
+ if ((error = xfs_bmbt_decrement(cur, 0, &i)))
+ goto done;
+ ASSERT(i == 1);
+ if ((error = xfs_bmbt_update(cur, LEFT.br_startoff,
+ LEFT.br_startblock,
+ LEFT.br_blockcount +
+ PREV.br_blockcount +
+ RIGHT.br_blockcount, LEFT.br_state)))
+ goto done;
+ }
+ *dnew = 0;
+ break;
+
+ case MASK3(LEFT_FILLING, RIGHT_FILLING, LEFT_CONTIG):
+ /*
+ * Filling in all of a previously delayed allocation extent.
+ * The left neighbor is contiguous, the right is not.
+ */
+ xfs_bmap_trace_pre_update(fname, "LF|RF|LC", ip, idx - 1,
+ XFS_DATA_FORK);
+ xfs_bmbt_set_blockcount(ep - 1,
+ LEFT.br_blockcount + PREV.br_blockcount);
+ xfs_bmap_trace_post_update(fname, "LF|RF|LC", ip, idx - 1,
+ XFS_DATA_FORK);
+ ip->i_df.if_lastex = idx - 1;
+ xfs_bmap_trace_delete(fname, "LF|RF|LC", ip, idx, 1,
+ XFS_DATA_FORK);
+ xfs_bmap_delete_exlist(ip, idx, 1, XFS_DATA_FORK);
+ if (cur == NULL)
+ rval = XFS_ILOG_DEXT;
+ else {
+ rval = 0;
+ if ((error = xfs_bmbt_lookup_eq(cur, LEFT.br_startoff,
+ LEFT.br_startblock, LEFT.br_blockcount,
+ &i)))
+ goto done;
+ ASSERT(i == 1);
+ if ((error = xfs_bmbt_update(cur, LEFT.br_startoff,
+ LEFT.br_startblock,
+ LEFT.br_blockcount +
+ PREV.br_blockcount, LEFT.br_state)))
+ goto done;
+ }
+ *dnew = 0;
+ break;
+
+ case MASK3(LEFT_FILLING, RIGHT_FILLING, RIGHT_CONTIG):
+ /*
+ * Filling in all of a previously delayed allocation extent.
+ * The right neighbor is contiguous, the left is not.
+ */
+ xfs_bmap_trace_pre_update(fname, "LF|RF|RC", ip, idx,
+ XFS_DATA_FORK);
+ xfs_bmbt_set_startblock(ep, new->br_startblock);
+ xfs_bmbt_set_blockcount(ep,
+ PREV.br_blockcount + RIGHT.br_blockcount);
+ xfs_bmap_trace_post_update(fname, "LF|RF|RC", ip, idx,
+ XFS_DATA_FORK);
+ ip->i_df.if_lastex = idx;
+ xfs_bmap_trace_delete(fname, "LF|RF|RC", ip, idx + 1, 1,
+ XFS_DATA_FORK);
+ xfs_bmap_delete_exlist(ip, idx + 1, 1, XFS_DATA_FORK);
+ if (cur == NULL)
+ rval = XFS_ILOG_DEXT;
+ else {
+ rval = 0;
+ if ((error = xfs_bmbt_lookup_eq(cur, RIGHT.br_startoff,
+ RIGHT.br_startblock,
+ RIGHT.br_blockcount, &i)))
+ goto done;
+ ASSERT(i == 1);
+ if ((error = xfs_bmbt_update(cur, PREV.br_startoff,
+ new->br_startblock,
+ PREV.br_blockcount +
+ RIGHT.br_blockcount, PREV.br_state)))
+ goto done;
+ }
+ *dnew = 0;
+ break;
+
+ case MASK2(LEFT_FILLING, RIGHT_FILLING):
+ /*
+ * Filling in all of a previously delayed allocation extent.
+ * Neither the left nor right neighbors are contiguous with
+ * the new one.
+ */
+ xfs_bmap_trace_pre_update(fname, "LF|RF", ip, idx,
+ XFS_DATA_FORK);
+ xfs_bmbt_set_startblock(ep, new->br_startblock);
+ xfs_bmap_trace_post_update(fname, "LF|RF", ip, idx,
+ XFS_DATA_FORK);
+ ip->i_df.if_lastex = idx;
+ ip->i_d.di_nextents++;
+ if (cur == NULL)
+ rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
+ else {
+ rval = XFS_ILOG_CORE;
+ if ((error = xfs_bmbt_lookup_eq(cur, new->br_startoff,
+ new->br_startblock, new->br_blockcount,
+ &i)))
+ goto done;
+ ASSERT(i == 0);
+ cur->bc_rec.b.br_state = XFS_EXT_NORM;
+ if ((error = xfs_bmbt_insert(cur, &i)))
+ goto done;
+ ASSERT(i == 1);
+ }
+ *dnew = 0;
+ break;
+
+ case MASK2(LEFT_FILLING, LEFT_CONTIG):
+ /*
+ * Filling in the first part of a previous delayed allocation.
+ * The left neighbor is contiguous.
+ */
+ xfs_bmap_trace_pre_update(fname, "LF|LC", ip, idx - 1,
+ XFS_DATA_FORK);
+ xfs_bmbt_set_blockcount(ep - 1,
+ LEFT.br_blockcount + new->br_blockcount);
+ xfs_bmbt_set_startoff(ep,
+ PREV.br_startoff + new->br_blockcount);
+ xfs_bmap_trace_post_update(fname, "LF|LC", ip, idx - 1,
+ XFS_DATA_FORK);
+ temp = PREV.br_blockcount - new->br_blockcount;
+ xfs_bmap_trace_pre_update(fname, "LF|LC", ip, idx,
+ XFS_DATA_FORK);
+ xfs_bmbt_set_blockcount(ep, temp);
+ ip->i_df.if_lastex = idx - 1;
+ if (cur == NULL)
+ rval = XFS_ILOG_DEXT;
+ else {
+ rval = 0;
+ if ((error = xfs_bmbt_lookup_eq(cur, LEFT.br_startoff,
+ LEFT.br_startblock, LEFT.br_blockcount,
+ &i)))
+ goto done;
+ ASSERT(i == 1);
+ if ((error = xfs_bmbt_update(cur, LEFT.br_startoff,
+ LEFT.br_startblock,
+ LEFT.br_blockcount +
+ new->br_blockcount,
+ LEFT.br_state)))
+ goto done;
+ }
+ temp = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
+ STARTBLOCKVAL(PREV.br_startblock));
+ xfs_bmbt_set_startblock(ep, NULLSTARTBLOCK((int)temp));
+ xfs_bmap_trace_post_update(fname, "LF|LC", ip, idx,
+ XFS_DATA_FORK);
+ *dnew = temp;
+ break;
+
+ case MASK(LEFT_FILLING):
+ /*
+ * Filling in the first part of a previous delayed allocation.
+ * The left neighbor is not contiguous.
+ */
+ xfs_bmap_trace_pre_update(fname, "LF", ip, idx, XFS_DATA_FORK);
+ xfs_bmbt_set_startoff(ep, new_endoff);
+ temp = PREV.br_blockcount - new->br_blockcount;
+ xfs_bmbt_set_blockcount(ep, temp);
+ xfs_bmap_trace_insert(fname, "LF", ip, idx, 1, new, NULL,
+ XFS_DATA_FORK);
+ xfs_bmap_insert_exlist(ip, idx, 1, new, XFS_DATA_FORK);
+ ip->i_df.if_lastex = idx;
+ ip->i_d.di_nextents++;
+ if (cur == NULL)
+ rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
+ else {
+ rval = XFS_ILOG_CORE;
+ if ((error = xfs_bmbt_lookup_eq(cur, new->br_startoff,
+ new->br_startblock, new->br_blockcount,
+ &i)))
+ goto done;
+ ASSERT(i == 0);
+ cur->bc_rec.b.br_state = XFS_EXT_NORM;
+ if ((error = xfs_bmbt_insert(cur, &i)))
+ goto done;
+ ASSERT(i == 1);
+ }
+ if (ip->i_d.di_format == XFS_DINODE_FMT_EXTENTS &&
+ ip->i_d.di_nextents > ip->i_df.if_ext_max) {
+ error = xfs_bmap_extents_to_btree(ip->i_transp, ip,
+ first, flist, &cur, 1, &tmp_rval,
+ XFS_DATA_FORK);
+ rval |= tmp_rval;
+ if (error)
+ goto done;
+ }
+ temp = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
+ STARTBLOCKVAL(PREV.br_startblock) -
+ (cur ? cur->bc_private.b.allocated : 0));
+ base = ip->i_df.if_u1.if_extents;
+ ep = &base[idx + 1];
+ xfs_bmbt_set_startblock(ep, NULLSTARTBLOCK((int)temp));
+ xfs_bmap_trace_post_update(fname, "LF", ip, idx + 1,
+ XFS_DATA_FORK);
+ *dnew = temp;
+ break;
+
+ case MASK2(RIGHT_FILLING, RIGHT_CONTIG):
+ /*
+ * Filling in the last part of a previous delayed allocation.
+ * The right neighbor is contiguous with the new allocation.
+ */
+ temp = PREV.br_blockcount - new->br_blockcount;
+ xfs_bmap_trace_pre_update(fname, "RF|RC", ip, idx,
+ XFS_DATA_FORK);
+ xfs_bmap_trace_pre_update(fname, "RF|RC", ip, idx + 1,
+ XFS_DATA_FORK);
+ xfs_bmbt_set_blockcount(ep, temp);
+ xfs_bmbt_set_allf(ep + 1, new->br_startoff, new->br_startblock,
+ new->br_blockcount + RIGHT.br_blockcount,
+ RIGHT.br_state);
+ xfs_bmap_trace_post_update(fname, "RF|RC", ip, idx + 1,
+ XFS_DATA_FORK);
+ ip->i_df.if_lastex = idx + 1;
+ if (cur == NULL)
+ rval = XFS_ILOG_DEXT;
+ else {
+ rval = 0;
+ if ((error = xfs_bmbt_lookup_eq(cur, RIGHT.br_startoff,
+ RIGHT.br_startblock,
+ RIGHT.br_blockcount, &i)))
+ goto done;
+ ASSERT(i == 1);
+ if ((error = xfs_bmbt_update(cur, new->br_startoff,
+ new->br_startblock,
+ new->br_blockcount +
+ RIGHT.br_blockcount,
+ RIGHT.br_state)))
+ goto done;
+ }
+ temp = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
+ STARTBLOCKVAL(PREV.br_startblock));
+ xfs_bmbt_set_startblock(ep, NULLSTARTBLOCK((int)temp));
+ xfs_bmap_trace_post_update(fname, "RF|RC", ip, idx,
+ XFS_DATA_FORK);
+ *dnew = temp;
+ break;
+
+ case MASK(RIGHT_FILLING):
+ /*
+ * Filling in the last part of a previous delayed allocation.
+ * The right neighbor is not contiguous.
+ */
+ temp = PREV.br_blockcount - new->br_blockcount;
+ xfs_bmap_trace_pre_update(fname, "RF", ip, idx, XFS_DATA_FORK);
+ xfs_bmbt_set_blockcount(ep, temp);
+ xfs_bmap_trace_insert(fname, "RF", ip, idx + 1, 1,
+ new, NULL, XFS_DATA_FORK);
+ xfs_bmap_insert_exlist(ip, idx + 1, 1, new, XFS_DATA_FORK);
+ ip->i_df.if_lastex = idx + 1;
+ ip->i_d.di_nextents++;
+ if (cur == NULL)
+ rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
+ else {
+ rval = XFS_ILOG_CORE;
+ if ((error = xfs_bmbt_lookup_eq(cur, new->br_startoff,
+ new->br_startblock, new->br_blockcount,
+ &i)))
+ goto done;
+ ASSERT(i == 0);
+ cur->bc_rec.b.br_state = XFS_EXT_NORM;
+ if ((error = xfs_bmbt_insert(cur, &i)))
+ goto done;
+ ASSERT(i == 1);
+ }
+ if (ip->i_d.di_format == XFS_DINODE_FMT_EXTENTS &&
+ ip->i_d.di_nextents > ip->i_df.if_ext_max) {
+ error = xfs_bmap_extents_to_btree(ip->i_transp, ip,
+ first, flist, &cur, 1, &tmp_rval,
+ XFS_DATA_FORK);
+ rval |= tmp_rval;
+ if (error)
+ goto done;
+ }
+ temp = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
+ STARTBLOCKVAL(PREV.br_startblock) -
+ (cur ? cur->bc_private.b.allocated : 0));
+ base = ip->i_df.if_u1.if_extents;
+ ep = &base[idx];
+ xfs_bmbt_set_startblock(ep, NULLSTARTBLOCK((int)temp));
+ xfs_bmap_trace_post_update(fname, "RF", ip, idx, XFS_DATA_FORK);
+ *dnew = temp;
+ break;
+
+ case 0:
+ /*
+ * Filling in the middle part of a previous delayed allocation.
+ * Contiguity is impossible here.
+ * This case is avoided almost all the time.
+ */
+ temp = new->br_startoff - PREV.br_startoff;
+ xfs_bmap_trace_pre_update(fname, "0", ip, idx, XFS_DATA_FORK);
+ xfs_bmbt_set_blockcount(ep, temp);
+ r[0] = *new;
+ r[1].br_startoff = new_endoff;
+ temp2 = PREV.br_startoff + PREV.br_blockcount - new_endoff;
+ r[1].br_blockcount = temp2;
+ xfs_bmap_trace_insert(fname, "0", ip, idx + 1, 2, &r[0], &r[1],
+ XFS_DATA_FORK);
+ xfs_bmap_insert_exlist(ip, idx + 1, 2, &r[0], XFS_DATA_FORK);
+ ip->i_df.if_lastex = idx + 1;
+ ip->i_d.di_nextents++;
+ if (cur == NULL)
+ rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
+ else {
+ rval = XFS_ILOG_CORE;
+ if ((error = xfs_bmbt_lookup_eq(cur, new->br_startoff,
+ new->br_startblock, new->br_blockcount,
+ &i)))
+ goto done;
+ ASSERT(i == 0);
+ cur->bc_rec.b.br_state = XFS_EXT_NORM;
+ if ((error = xfs_bmbt_insert(cur, &i)))
+ goto done;
+ ASSERT(i == 1);
+ }
+ if (ip->i_d.di_format == XFS_DINODE_FMT_EXTENTS &&
+ ip->i_d.di_nextents > ip->i_df.if_ext_max) {
+ error = xfs_bmap_extents_to_btree(ip->i_transp, ip,
+ first, flist, &cur, 1, &tmp_rval,
+ XFS_DATA_FORK);
+ rval |= tmp_rval;
+ if (error)
+ goto done;
+ }
+ temp = xfs_bmap_worst_indlen(ip, temp);
+ temp2 = xfs_bmap_worst_indlen(ip, temp2);
+ diff = (int)(temp + temp2 - STARTBLOCKVAL(PREV.br_startblock) -
+ (cur ? cur->bc_private.b.allocated : 0));
+ if (diff > 0 &&
+ xfs_mod_incore_sb(ip->i_mount, XFS_SBS_FDBLOCKS, -diff, rsvd)) {
+ /*
+ * Ick gross gag me with a spoon.
+ */
+ ASSERT(0); /* want to see if this ever happens! */
+ while (diff > 0) {
+ if (temp) {
+ temp--;
+ diff--;
+ if (!diff ||
+ !xfs_mod_incore_sb(ip->i_mount,
+ XFS_SBS_FDBLOCKS, -diff, rsvd))
+ break;
+ }
+ if (temp2) {
+ temp2--;
+ diff--;
+ if (!diff ||
+ !xfs_mod_incore_sb(ip->i_mount,
+ XFS_SBS_FDBLOCKS, -diff, rsvd))
+ break;
+ }
+ }
+ }
+ base = ip->i_df.if_u1.if_extents;
+ ep = &base[idx];
+ xfs_bmbt_set_startblock(ep, NULLSTARTBLOCK((int)temp));
+ xfs_bmap_trace_post_update(fname, "0", ip, idx, XFS_DATA_FORK);
+ xfs_bmap_trace_pre_update(fname, "0", ip, idx + 2,
+ XFS_DATA_FORK);
+ xfs_bmbt_set_startblock(ep + 2, NULLSTARTBLOCK((int)temp2));
+ xfs_bmap_trace_post_update(fname, "0", ip, idx + 2,
+ XFS_DATA_FORK);
+ *dnew = temp + temp2;
+ break;
+
+ case MASK3(LEFT_FILLING, LEFT_CONTIG, RIGHT_CONTIG):
+ case MASK3(RIGHT_FILLING, LEFT_CONTIG, RIGHT_CONTIG):
+ case MASK2(LEFT_FILLING, RIGHT_CONTIG):
+ case MASK2(RIGHT_FILLING, LEFT_CONTIG):
+ case MASK2(LEFT_CONTIG, RIGHT_CONTIG):
+ case MASK(LEFT_CONTIG):
+ case MASK(RIGHT_CONTIG):
+ /*
+ * These cases are all impossible.
+ */
+ ASSERT(0);
+ }
+ *curp = cur;
+done:
+ *logflagsp = rval;
+ return error;
+#undef LEFT
+#undef RIGHT
+#undef PREV
+#undef MASK
+#undef MASK2
+#undef MASK3
+#undef MASK4
+#undef STATE_SET
+#undef STATE_TEST
+#undef STATE_SET_TEST
+#undef SWITCH_STATE
+}
+
+/*
+ * Called by xfs_bmap_add_extent to handle cases converting an unwritten
+ * allocation to a real allocation or vice versa.
+ */
+STATIC int /* error */
+xfs_bmap_add_extent_unwritten_real(
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_extnum_t idx, /* extent number to update/insert */
+ xfs_btree_cur_t **curp, /* if *curp is null, not a btree */
+ xfs_bmbt_irec_t *new, /* new data to put in extent list */
+ int *logflagsp) /* inode logging flags */
+{
+ xfs_bmbt_rec_t *base; /* base of extent entry list */
+ xfs_btree_cur_t *cur; /* btree cursor */
+ xfs_bmbt_rec_t *ep; /* extent entry for idx */
+ int error; /* error return value */
+#ifdef XFS_BMAP_TRACE
+ static char fname[] = "xfs_bmap_add_extent_unwritten_real";
+#endif
+ int i; /* temp state */
+ xfs_fileoff_t new_endoff; /* end offset of new entry */
+ xfs_exntst_t newext; /* new extent state */
+ xfs_exntst_t oldext; /* old extent state */
+ xfs_bmbt_irec_t r[3]; /* neighbor extent entries */
+ /* left is 0, right is 1, prev is 2 */
+ int rval=0; /* return value (logging flags) */
+ int state = 0;/* state bits, accessed thru macros */
+ enum { /* bit number definitions for state */
+ LEFT_CONTIG, RIGHT_CONTIG,
+ LEFT_FILLING, RIGHT_FILLING,
+ LEFT_DELAY, RIGHT_DELAY,
+ LEFT_VALID, RIGHT_VALID
+ };
+
+#define LEFT r[0]
+#define RIGHT r[1]
+#define PREV r[2]
+#define MASK(b) (1 << (b))
+#define MASK2(a,b) (MASK(a) | MASK(b))
+#define MASK3(a,b,c) (MASK2(a,b) | MASK(c))
+#define MASK4(a,b,c,d) (MASK3(a,b,c) | MASK(d))
+#define STATE_SET(b,v) ((v) ? (state |= MASK(b)) : (state &= ~MASK(b)))
+#define STATE_TEST(b) (state & MASK(b))
+#define STATE_SET_TEST(b,v) ((v) ? ((state |= MASK(b)), 1) : \
+ ((state &= ~MASK(b)), 0))
+#define SWITCH_STATE \
+ (state & MASK4(LEFT_FILLING, RIGHT_FILLING, LEFT_CONTIG, RIGHT_CONTIG))
+
+ /*
+ * Set up a bunch of variables to make the tests simpler.
+ */
+ error = 0;
+ cur = *curp;
+ base = ip->i_df.if_u1.if_extents;
+ ep = &base[idx];
+ xfs_bmbt_get_all(ep, &PREV);
+ newext = new->br_state;
+ oldext = (newext == XFS_EXT_UNWRITTEN) ?
+ XFS_EXT_NORM : XFS_EXT_UNWRITTEN;
+ ASSERT(PREV.br_state == oldext);
+ new_endoff = new->br_startoff + new->br_blockcount;
+ ASSERT(PREV.br_startoff <= new->br_startoff);
+ ASSERT(PREV.br_startoff + PREV.br_blockcount >= new_endoff);
+ /*
+ * Set flags determining what part of the previous oldext allocation
+ * extent is being replaced by a newext allocation.
+ */
+ STATE_SET(LEFT_FILLING, PREV.br_startoff == new->br_startoff);
+ STATE_SET(RIGHT_FILLING,
+ PREV.br_startoff + PREV.br_blockcount == new_endoff);
+ /*
+ * Check and set flags if this segment has a left neighbor.
+ * Don't set contiguous if the combined extent would be too large.
+ */
+ if (STATE_SET_TEST(LEFT_VALID, idx > 0)) {
+ xfs_bmbt_get_all(ep - 1, &LEFT);
+ STATE_SET(LEFT_DELAY, ISNULLSTARTBLOCK(LEFT.br_startblock));
+ }
+ STATE_SET(LEFT_CONTIG,
+ STATE_TEST(LEFT_VALID) && !STATE_TEST(LEFT_DELAY) &&
+ LEFT.br_startoff + LEFT.br_blockcount == new->br_startoff &&
+ LEFT.br_startblock + LEFT.br_blockcount == new->br_startblock &&
+ LEFT.br_state == newext &&
+ LEFT.br_blockcount + new->br_blockcount <= MAXEXTLEN);
+ /*
+ * Check and set flags if this segment has a right neighbor.
+ * Don't set contiguous if the combined extent would be too large.
+ * Also check for all-three-contiguous being too large.
+ */
+ if (STATE_SET_TEST(RIGHT_VALID,
+ idx <
+ ip->i_df.if_bytes / (uint)sizeof(xfs_bmbt_rec_t) - 1)) {
+ xfs_bmbt_get_all(ep + 1, &RIGHT);
+ STATE_SET(RIGHT_DELAY, ISNULLSTARTBLOCK(RIGHT.br_startblock));
+ }
+ STATE_SET(RIGHT_CONTIG,
+ STATE_TEST(RIGHT_VALID) && !STATE_TEST(RIGHT_DELAY) &&
+ new_endoff == RIGHT.br_startoff &&
+ new->br_startblock + new->br_blockcount ==
+ RIGHT.br_startblock &&
+ newext == RIGHT.br_state &&
+ new->br_blockcount + RIGHT.br_blockcount <= MAXEXTLEN &&
+ ((state & MASK3(LEFT_CONTIG, LEFT_FILLING, RIGHT_FILLING)) !=
+ MASK3(LEFT_CONTIG, LEFT_FILLING, RIGHT_FILLING) ||
+ LEFT.br_blockcount + new->br_blockcount + RIGHT.br_blockcount
+ <= MAXEXTLEN));
+ /*
+ * Switch out based on the FILLING and CONTIG state bits.
+ */
+ switch (SWITCH_STATE) {
+
+ case MASK4(LEFT_FILLING, RIGHT_FILLING, LEFT_CONTIG, RIGHT_CONTIG):
+ /*
+ * Setting all of a previous oldext extent to newext.
+ * The left and right neighbors are both contiguous with new.
+ */
+ xfs_bmap_trace_pre_update(fname, "LF|RF|LC|RC", ip, idx - 1,
+ XFS_DATA_FORK);
+ xfs_bmbt_set_blockcount(ep - 1,
+ LEFT.br_blockcount + PREV.br_blockcount +
+ RIGHT.br_blockcount);
+ xfs_bmap_trace_post_update(fname, "LF|RF|LC|RC", ip, idx - 1,
+ XFS_DATA_FORK);
+ xfs_bmap_trace_delete(fname, "LF|RF|LC|RC", ip, idx, 2,
+ XFS_DATA_FORK);
+ xfs_bmap_delete_exlist(ip, idx, 2, XFS_DATA_FORK);
+ ip->i_df.if_lastex = idx - 1;
+ ip->i_d.di_nextents -= 2;
+ if (cur == NULL)
+ rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
+ else {
+ rval = XFS_ILOG_CORE;
+ if ((error = xfs_bmbt_lookup_eq(cur, RIGHT.br_startoff,
+ RIGHT.br_startblock,
+ RIGHT.br_blockcount, &i)))
+ goto done;
+ ASSERT(i == 1);
+ if ((error = xfs_bmbt_delete(cur, &i)))
+ goto done;
+ ASSERT(i == 1);
+ if ((error = xfs_bmbt_decrement(cur, 0, &i)))
+ goto done;
+ ASSERT(i == 1);
+ if ((error = xfs_bmbt_delete(cur, &i)))
+ goto done;
+ ASSERT(i == 1);
+ if ((error = xfs_bmbt_decrement(cur, 0, &i)))
+ goto done;
+ ASSERT(i == 1);
+ if ((error = xfs_bmbt_update(cur, LEFT.br_startoff,
+ LEFT.br_startblock,
+ LEFT.br_blockcount + PREV.br_blockcount +
+ RIGHT.br_blockcount, LEFT.br_state)))
+ goto done;
+ }
+ break;
+
+ case MASK3(LEFT_FILLING, RIGHT_FILLING, LEFT_CONTIG):
+ /*
+ * Setting all of a previous oldext extent to newext.
+ * The left neighbor is contiguous, the right is not.
+ */
+ xfs_bmap_trace_pre_update(fname, "LF|RF|LC", ip, idx - 1,
+ XFS_DATA_FORK);
+ xfs_bmbt_set_blockcount(ep - 1,
+ LEFT.br_blockcount + PREV.br_blockcount);
+ xfs_bmap_trace_post_update(fname, "LF|RF|LC", ip, idx - 1,
+ XFS_DATA_FORK);
+ ip->i_df.if_lastex = idx - 1;
+ xfs_bmap_trace_delete(fname, "LF|RF|LC", ip, idx, 1,
+ XFS_DATA_FORK);
+ xfs_bmap_delete_exlist(ip, idx, 1, XFS_DATA_FORK);
+ ip->i_d.di_nextents--;
+ if (cur == NULL)
+ rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
+ else {
+ rval = XFS_ILOG_CORE;
+ if ((error = xfs_bmbt_lookup_eq(cur, PREV.br_startoff,
+ PREV.br_startblock, PREV.br_blockcount,
+ &i)))
+ goto done;
+ ASSERT(i == 1);
+ if ((error = xfs_bmbt_delete(cur, &i)))
+ goto done;
+ ASSERT(i == 1);
+ if ((error = xfs_bmbt_decrement(cur, 0, &i)))
+ goto done;
+ ASSERT(i == 1);
+ if ((error = xfs_bmbt_update(cur, LEFT.br_startoff,
+ LEFT.br_startblock,
+ LEFT.br_blockcount + PREV.br_blockcount,
+ LEFT.br_state)))
+ goto done;
+ }
+ break;
+
+ case MASK3(LEFT_FILLING, RIGHT_FILLING, RIGHT_CONTIG):
+ /*
+ * Setting all of a previous oldext extent to newext.
+ * The right neighbor is contiguous, the left is not.
+ */
+ xfs_bmap_trace_pre_update(fname, "LF|RF|RC", ip, idx,
+ XFS_DATA_FORK);
+ xfs_bmbt_set_blockcount(ep,
+ PREV.br_blockcount + RIGHT.br_blockcount);
+ xfs_bmbt_set_state(ep, newext);
+ xfs_bmap_trace_post_update(fname, "LF|RF|RC", ip, idx,
+ XFS_DATA_FORK);
+ ip->i_df.if_lastex = idx;
+ xfs_bmap_trace_delete(fname, "LF|RF|RC", ip, idx + 1, 1,
+ XFS_DATA_FORK);
+ xfs_bmap_delete_exlist(ip, idx + 1, 1, XFS_DATA_FORK);
+ ip->i_d.di_nextents--;
+ if (cur == NULL)
+ rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
+ else {
+ rval = XFS_ILOG_CORE;
+ if ((error = xfs_bmbt_lookup_eq(cur, RIGHT.br_startoff,
+ RIGHT.br_startblock,
+ RIGHT.br_blockcount, &i)))
+ goto done;
+ ASSERT(i == 1);
+ if ((error = xfs_bmbt_delete(cur, &i)))
+ goto done;
+ ASSERT(i == 1);
+ if ((error = xfs_bmbt_decrement(cur, 0, &i)))
+ goto done;
+ ASSERT(i == 1);
+ if ((error = xfs_bmbt_update(cur, new->br_startoff,
+ new->br_startblock,
+ new->br_blockcount + RIGHT.br_blockcount,
+ newext)))
+ goto done;
+ }
+ break;
+
+ case MASK2(LEFT_FILLING, RIGHT_FILLING):
+ /*
+ * Setting all of a previous oldext extent to newext.
+ * Neither the left nor right neighbors are contiguous with
+ * the new one.
+ */
+ xfs_bmap_trace_pre_update(fname, "LF|RF", ip, idx,
+ XFS_DATA_FORK);
+ xfs_bmbt_set_state(ep, newext);
+ xfs_bmap_trace_post_update(fname, "LF|RF", ip, idx,
+ XFS_DATA_FORK);
+ ip->i_df.if_lastex = idx;
+ if (cur == NULL)
+ rval = XFS_ILOG_DEXT;
+ else {
+ rval = 0;
+ if ((error = xfs_bmbt_lookup_eq(cur, new->br_startoff,
+ new->br_startblock, new->br_blockcount,
+ &i)))
+ goto done;
+ ASSERT(i == 1);
+ if ((error = xfs_bmbt_update(cur, new->br_startoff,
+ new->br_startblock, new->br_blockcount,
+ newext)))
+ goto done;
+ }
+ break;
+
+ case MASK2(LEFT_FILLING, LEFT_CONTIG):
+ /*
+ * Setting the first part of a previous oldext extent to newext.
+ * The left neighbor is contiguous.
+ */
+ xfs_bmap_trace_pre_update(fname, "LF|LC", ip, idx - 1,
+ XFS_DATA_FORK);
+ xfs_bmbt_set_blockcount(ep - 1,
+ LEFT.br_blockcount + new->br_blockcount);
+ xfs_bmbt_set_startoff(ep,
+ PREV.br_startoff + new->br_blockcount);
+ xfs_bmap_trace_post_update(fname, "LF|LC", ip, idx - 1,
+ XFS_DATA_FORK);
+ xfs_bmap_trace_pre_update(fname, "LF|LC", ip, idx,
+ XFS_DATA_FORK);
+ xfs_bmbt_set_startblock(ep,
+ new->br_startblock + new->br_blockcount);
+ xfs_bmbt_set_blockcount(ep,
+ PREV.br_blockcount - new->br_blockcount);
+ xfs_bmap_trace_post_update(fname, "LF|LC", ip, idx,
+ XFS_DATA_FORK);
+ ip->i_df.if_lastex = idx - 1;
+ if (cur == NULL)
+ rval = XFS_ILOG_DEXT;
+ else {
+ rval = 0;
+ if ((error = xfs_bmbt_lookup_eq(cur, PREV.br_startoff,
+ PREV.br_startblock, PREV.br_blockcount,
+ &i)))
+ goto done;
+ ASSERT(i == 1);
+ if ((error = xfs_bmbt_update(cur,
+ PREV.br_startoff + new->br_blockcount,
+ PREV.br_startblock + new->br_blockcount,
+ PREV.br_blockcount - new->br_blockcount,
+ oldext)))
+ goto done;
+ if ((error = xfs_bmbt_decrement(cur, 0, &i)))
+ goto done;
+ if (xfs_bmbt_update(cur, LEFT.br_startoff,
+ LEFT.br_startblock,
+ LEFT.br_blockcount + new->br_blockcount,
+ LEFT.br_state))
+ goto done;
+ }
+ break;
+
+ case MASK(LEFT_FILLING):
+ /*
+ * Setting the first part of a previous oldext extent to newext.
+ * The left neighbor is not contiguous.
+ */
+ xfs_bmap_trace_pre_update(fname, "LF", ip, idx, XFS_DATA_FORK);
+ ASSERT(ep && xfs_bmbt_get_state(ep) == oldext);
+ xfs_bmbt_set_startoff(ep, new_endoff);
+ xfs_bmbt_set_blockcount(ep,
+ PREV.br_blockcount - new->br_blockcount);
+ xfs_bmbt_set_startblock(ep,
+ new->br_startblock + new->br_blockcount);
+ xfs_bmap_trace_post_update(fname, "LF", ip, idx, XFS_DATA_FORK);
+ xfs_bmap_trace_insert(fname, "LF", ip, idx, 1, new, NULL,
+ XFS_DATA_FORK);
+ xfs_bmap_insert_exlist(ip, idx, 1, new, XFS_DATA_FORK);
+ ip->i_df.if_lastex = idx;
+ ip->i_d.di_nextents++;
+ if (cur == NULL)
+ rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
+ else {
+ rval = XFS_ILOG_CORE;
+ if ((error = xfs_bmbt_lookup_eq(cur, PREV.br_startoff,
+ PREV.br_startblock, PREV.br_blockcount,
+ &i)))
+ goto done;
+ ASSERT(i == 1);
+ if ((error = xfs_bmbt_update(cur,
+ PREV.br_startoff + new->br_blockcount,
+ PREV.br_startblock + new->br_blockcount,
+ PREV.br_blockcount - new->br_blockcount,
+ oldext)))
+ goto done;
+ cur->bc_rec.b = *new;
+ if ((error = xfs_bmbt_insert(cur, &i)))
+ goto done;
+ ASSERT(i == 1);
+ }
+ break;
+
+ case MASK2(RIGHT_FILLING, RIGHT_CONTIG):
+ /*
+ * Setting the last part of a previous oldext extent to newext.
+ * The right neighbor is contiguous with the new allocation.
+ */
+ xfs_bmap_trace_pre_update(fname, "RF|RC", ip, idx,
+ XFS_DATA_FORK);
+ xfs_bmap_trace_pre_update(fname, "RF|RC", ip, idx + 1,
+ XFS_DATA_FORK);
+ xfs_bmbt_set_blockcount(ep,
+ PREV.br_blockcount - new->br_blockcount);
+ xfs_bmap_trace_post_update(fname, "RF|RC", ip, idx,
+ XFS_DATA_FORK);
+ xfs_bmbt_set_allf(ep + 1, new->br_startoff, new->br_startblock,
+ new->br_blockcount + RIGHT.br_blockcount, newext);
+ xfs_bmap_trace_post_update(fname, "RF|RC", ip, idx + 1,
+ XFS_DATA_FORK);
+ ip->i_df.if_lastex = idx + 1;
+ if (cur == NULL)
+ rval = XFS_ILOG_DEXT;
+ else {
+ rval = 0;
+ if ((error = xfs_bmbt_lookup_eq(cur, PREV.br_startoff,
+ PREV.br_startblock,
+ PREV.br_blockcount, &i)))
+ goto done;
+ ASSERT(i == 1);
+ if ((error = xfs_bmbt_update(cur, PREV.br_startoff,
+ PREV.br_startblock,
+ PREV.br_blockcount - new->br_blockcount,
+ oldext)))
+ goto done;
+ if ((error = xfs_bmbt_increment(cur, 0, &i)))
+ goto done;
+ if ((error = xfs_bmbt_update(cur, new->br_startoff,
+ new->br_startblock,
+ new->br_blockcount + RIGHT.br_blockcount,
+ newext)))
+ goto done;
+ }
+ break;
+
+ case MASK(RIGHT_FILLING):
+ /*
+ * Setting the last part of a previous oldext extent to newext.
+ * The right neighbor is not contiguous.
+ */
+ xfs_bmap_trace_pre_update(fname, "RF", ip, idx, XFS_DATA_FORK);
+ xfs_bmbt_set_blockcount(ep,
+ PREV.br_blockcount - new->br_blockcount);
+ xfs_bmap_trace_post_update(fname, "RF", ip, idx, XFS_DATA_FORK);
+ xfs_bmap_trace_insert(fname, "RF", ip, idx + 1, 1,
+ new, NULL, XFS_DATA_FORK);
+ xfs_bmap_insert_exlist(ip, idx + 1, 1, new, XFS_DATA_FORK);
+ ip->i_df.if_lastex = idx + 1;
+ ip->i_d.di_nextents++;
+ if (cur == NULL)
+ rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
+ else {
+ rval = XFS_ILOG_CORE;
+ if ((error = xfs_bmbt_lookup_eq(cur, PREV.br_startoff,
+ PREV.br_startblock, PREV.br_blockcount,
+ &i)))
+ goto done;
+ ASSERT(i == 1);
+ if ((error = xfs_bmbt_update(cur, PREV.br_startoff,
+ PREV.br_startblock,
+ PREV.br_blockcount - new->br_blockcount,
+ oldext)))
+ goto done;
+ if ((error = xfs_bmbt_lookup_eq(cur, new->br_startoff,
+ new->br_startblock, new->br_blockcount,
+ &i)))
+ goto done;
+ ASSERT(i == 0);
+ cur->bc_rec.b.br_state = XFS_EXT_NORM;
+ if ((error = xfs_bmbt_insert(cur, &i)))
+ goto done;
+ ASSERT(i == 1);
+ }
+ break;
+
+ case 0:
+ /*
+ * Setting the middle part of a previous oldext extent to
+ * newext. Contiguity is impossible here.
+ * One extent becomes three extents.
+ */
+ xfs_bmap_trace_pre_update(fname, "0", ip, idx, XFS_DATA_FORK);
+ xfs_bmbt_set_blockcount(ep,
+ new->br_startoff - PREV.br_startoff);
+ xfs_bmap_trace_post_update(fname, "0", ip, idx, XFS_DATA_FORK);
+ r[0] = *new;
+ r[1].br_startoff = new_endoff;
+ r[1].br_blockcount =
+ PREV.br_startoff + PREV.br_blockcount - new_endoff;
+ r[1].br_startblock = new->br_startblock + new->br_blockcount;
+ r[1].br_state = oldext;
+ xfs_bmap_trace_insert(fname, "0", ip, idx + 1, 2, &r[0], &r[1],
+ XFS_DATA_FORK);
+ xfs_bmap_insert_exlist(ip, idx + 1, 2, &r[0], XFS_DATA_FORK);
+ ip->i_df.if_lastex = idx + 1;
+ ip->i_d.di_nextents += 2;
+ if (cur == NULL)
+ rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
+ else {
+ rval = XFS_ILOG_CORE;
+ if ((error = xfs_bmbt_lookup_eq(cur, PREV.br_startoff,
+ PREV.br_startblock, PREV.br_blockcount,
+ &i)))
+ goto done;
+ ASSERT(i == 1);
+ /* new right extent - oldext */
+ if ((error = xfs_bmbt_update(cur, r[1].br_startoff,
+ r[1].br_startblock, r[1].br_blockcount,
+ r[1].br_state)))
+ goto done;
+ /* new left extent - oldext */
+ PREV.br_blockcount =
+ new->br_startoff - PREV.br_startoff;
+ cur->bc_rec.b = PREV;
+ if ((error = xfs_bmbt_insert(cur, &i)))
+ goto done;
+ ASSERT(i == 1);
+ if ((error = xfs_bmbt_increment(cur, 0, &i)))
+ goto done;
+ ASSERT(i == 1);
+ /* new middle extent - newext */
+ cur->bc_rec.b = *new;
+ if ((error = xfs_bmbt_insert(cur, &i)))
+ goto done;
+ ASSERT(i == 1);
+ }
+ break;
+
+ case MASK3(LEFT_FILLING, LEFT_CONTIG, RIGHT_CONTIG):
+ case MASK3(RIGHT_FILLING, LEFT_CONTIG, RIGHT_CONTIG):
+ case MASK2(LEFT_FILLING, RIGHT_CONTIG):
+ case MASK2(RIGHT_FILLING, LEFT_CONTIG):
+ case MASK2(LEFT_CONTIG, RIGHT_CONTIG):
+ case MASK(LEFT_CONTIG):
+ case MASK(RIGHT_CONTIG):
+ /*
+ * These cases are all impossible.
+ */
+ ASSERT(0);
+ }
+ *curp = cur;
+done:
+ *logflagsp = rval;
+ return error;
+#undef LEFT
+#undef RIGHT
+#undef PREV
+#undef MASK
+#undef MASK2
+#undef MASK3
+#undef MASK4
+#undef STATE_SET
+#undef STATE_TEST
+#undef STATE_SET_TEST
+#undef SWITCH_STATE
+}
+
+/*
+ * Called by xfs_bmap_add_extent to handle cases converting a hole
+ * to a delayed allocation.
+ */
+/*ARGSUSED*/
+STATIC int /* error */
+xfs_bmap_add_extent_hole_delay(
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_extnum_t idx, /* extent number to update/insert */
+ xfs_btree_cur_t *cur, /* if null, not a btree */
+ xfs_bmbt_irec_t *new, /* new data to put in extent list */
+ int *logflagsp, /* inode logging flags */
+ int rsvd) /* OK to allocate reserved blocks */
+{
+ xfs_bmbt_rec_t *base; /* base of extent entry list */
+ xfs_bmbt_rec_t *ep; /* extent list entry for idx */
+#ifdef XFS_BMAP_TRACE
+ static char fname[] = "xfs_bmap_add_extent_hole_delay";
+#endif
+ xfs_bmbt_irec_t left; /* left neighbor extent entry */
+ xfs_filblks_t newlen=0; /* new indirect size */
+ xfs_filblks_t oldlen=0; /* old indirect size */
+ xfs_bmbt_irec_t right; /* right neighbor extent entry */
+ int state; /* state bits, accessed thru macros */
+ xfs_filblks_t temp; /* temp for indirect calculations */
+ enum { /* bit number definitions for state */
+ LEFT_CONTIG, RIGHT_CONTIG,
+ LEFT_DELAY, RIGHT_DELAY,
+ LEFT_VALID, RIGHT_VALID
+ };
+
+#define MASK(b) (1 << (b))
+#define MASK2(a,b) (MASK(a) | MASK(b))
+#define STATE_SET(b,v) ((v) ? (state |= MASK(b)) : (state &= ~MASK(b)))
+#define STATE_TEST(b) (state & MASK(b))
+#define STATE_SET_TEST(b,v) ((v) ? ((state |= MASK(b)), 1) : \
+ ((state &= ~MASK(b)), 0))
+#define SWITCH_STATE (state & MASK2(LEFT_CONTIG, RIGHT_CONTIG))
+
+ base = ip->i_df.if_u1.if_extents;
+ ep = &base[idx];
+ state = 0;
+ ASSERT(ISNULLSTARTBLOCK(new->br_startblock));
+ /*
+ * Check and set flags if this segment has a left neighbor
+ */
+ if (STATE_SET_TEST(LEFT_VALID, idx > 0)) {
+ xfs_bmbt_get_all(ep - 1, &left);
+ STATE_SET(LEFT_DELAY, ISNULLSTARTBLOCK(left.br_startblock));
+ }
+ /*
+ * Check and set flags if the current (right) segment exists.
+ * If it doesn't exist, we're converting the hole at end-of-file.
+ */
+ if (STATE_SET_TEST(RIGHT_VALID,
+ idx <
+ ip->i_df.if_bytes / (uint)sizeof(xfs_bmbt_rec_t))) {
+ xfs_bmbt_get_all(ep, &right);
+ STATE_SET(RIGHT_DELAY, ISNULLSTARTBLOCK(right.br_startblock));
+ }
+ /*
+ * Set contiguity flags on the left and right neighbors.
+ * Don't let extents get too large, even if the pieces are contiguous.
+ */
+ STATE_SET(LEFT_CONTIG,
+ STATE_TEST(LEFT_VALID) && STATE_TEST(LEFT_DELAY) &&
+ left.br_startoff + left.br_blockcount == new->br_startoff &&
+ left.br_blockcount + new->br_blockcount <= MAXEXTLEN);
+ STATE_SET(RIGHT_CONTIG,
+ STATE_TEST(RIGHT_VALID) && STATE_TEST(RIGHT_DELAY) &&
+ new->br_startoff + new->br_blockcount == right.br_startoff &&
+ new->br_blockcount + right.br_blockcount <= MAXEXTLEN &&
+ (!STATE_TEST(LEFT_CONTIG) ||
+ (left.br_blockcount + new->br_blockcount +
+ right.br_blockcount <= MAXEXTLEN)));
+ /*
+ * Switch out based on the contiguity flags.
+ */
+ switch (SWITCH_STATE) {
+
+ case MASK2(LEFT_CONTIG, RIGHT_CONTIG):
+ /*
+ * New allocation is contiguous with delayed allocations
+ * on the left and on the right.
+ * Merge all three into a single extent list entry.
+ */
+ temp = left.br_blockcount + new->br_blockcount +
+ right.br_blockcount;
+ xfs_bmap_trace_pre_update(fname, "LC|RC", ip, idx - 1,
+ XFS_DATA_FORK);
+ xfs_bmbt_set_blockcount(ep - 1, temp);
+ oldlen = STARTBLOCKVAL(left.br_startblock) +
+ STARTBLOCKVAL(new->br_startblock) +
+ STARTBLOCKVAL(right.br_startblock);
+ newlen = xfs_bmap_worst_indlen(ip, temp);
+ xfs_bmbt_set_startblock(ep - 1, NULLSTARTBLOCK((int)newlen));
+ xfs_bmap_trace_post_update(fname, "LC|RC", ip, idx - 1,
+ XFS_DATA_FORK);
+ xfs_bmap_trace_delete(fname, "LC|RC", ip, idx, 1,
+ XFS_DATA_FORK);
+ xfs_bmap_delete_exlist(ip, idx, 1, XFS_DATA_FORK);
+ ip->i_df.if_lastex = idx - 1;
+ break;
+
+ case MASK(LEFT_CONTIG):
+ /*
+ * New allocation is contiguous with a delayed allocation
+ * on the left.
+ * Merge the new allocation with the left neighbor.
+ */
+ temp = left.br_blockcount + new->br_blockcount;
+ xfs_bmap_trace_pre_update(fname, "LC", ip, idx - 1,
+ XFS_DATA_FORK);
+ xfs_bmbt_set_blockcount(ep - 1, temp);
+ oldlen = STARTBLOCKVAL(left.br_startblock) +
+ STARTBLOCKVAL(new->br_startblock);
+ newlen = xfs_bmap_worst_indlen(ip, temp);
+ xfs_bmbt_set_startblock(ep - 1, NULLSTARTBLOCK((int)newlen));
+ xfs_bmap_trace_post_update(fname, "LC", ip, idx - 1,
+ XFS_DATA_FORK);
+ ip->i_df.if_lastex = idx - 1;
+ break;
+
+ case MASK(RIGHT_CONTIG):
+ /*
+ * New allocation is contiguous with a delayed allocation
+ * on the right.
+ * Merge the new allocation with the right neighbor.
+ */
+ xfs_bmap_trace_pre_update(fname, "RC", ip, idx, XFS_DATA_FORK);
+ temp = new->br_blockcount + right.br_blockcount;
+ oldlen = STARTBLOCKVAL(new->br_startblock) +
+ STARTBLOCKVAL(right.br_startblock);
+ newlen = xfs_bmap_worst_indlen(ip, temp);
+ xfs_bmbt_set_allf(ep, new->br_startoff,
+ NULLSTARTBLOCK((int)newlen), temp, right.br_state);
+ xfs_bmap_trace_post_update(fname, "RC", ip, idx, XFS_DATA_FORK);
+ ip->i_df.if_lastex = idx;
+ break;
+
+ case 0:
+ /*
+ * New allocation is not contiguous with another
+ * delayed allocation.
+ * Insert a new entry.
+ */
+ oldlen = newlen = 0;
+ xfs_bmap_trace_insert(fname, "0", ip, idx, 1, new, NULL,
+ XFS_DATA_FORK);
+ xfs_bmap_insert_exlist(ip, idx, 1, new, XFS_DATA_FORK);
+ ip->i_df.if_lastex = idx;
+ break;
+ }
+ if (oldlen != newlen) {
+ ASSERT(oldlen > newlen);
+ xfs_mod_incore_sb(ip->i_mount, XFS_SBS_FDBLOCKS,
+ (int)(oldlen - newlen), rsvd);
+ /*
+ * Nothing to do for disk quota accounting here.
+ */
+ }
+ *logflagsp = 0;
+ return 0;
+#undef MASK
+#undef MASK2
+#undef STATE_SET
+#undef STATE_TEST
+#undef STATE_SET_TEST
+#undef SWITCH_STATE
+}
+
+/*
+ * Called by xfs_bmap_add_extent to handle cases converting a hole
+ * to a real allocation.
+ */
+STATIC int /* error */
+xfs_bmap_add_extent_hole_real(
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_extnum_t idx, /* extent number to update/insert */
+ xfs_btree_cur_t *cur, /* if null, not a btree */
+ xfs_bmbt_irec_t *new, /* new data to put in extent list */
+ int *logflagsp, /* inode logging flags */
+ int whichfork) /* data or attr fork */
+{
+ xfs_bmbt_rec_t *ep; /* pointer to extent entry ins. point */
+ int error; /* error return value */
+#ifdef XFS_BMAP_TRACE
+ static char fname[] = "xfs_bmap_add_extent_hole_real";
+#endif
+ int i; /* temp state */
+ xfs_ifork_t *ifp; /* inode fork pointer */
+ xfs_bmbt_irec_t left; /* left neighbor extent entry */
+ xfs_bmbt_irec_t right; /* right neighbor extent entry */
+ int state; /* state bits, accessed thru macros */
+ enum { /* bit number definitions for state */
+ LEFT_CONTIG, RIGHT_CONTIG,
+ LEFT_DELAY, RIGHT_DELAY,
+ LEFT_VALID, RIGHT_VALID
+ };
+
+#define MASK(b) (1 << (b))
+#define MASK2(a,b) (MASK(a) | MASK(b))
+#define STATE_SET(b,v) ((v) ? (state |= MASK(b)) : (state &= ~MASK(b)))
+#define STATE_TEST(b) (state & MASK(b))
+#define STATE_SET_TEST(b,v) ((v) ? ((state |= MASK(b)), 1) : \
+ ((state &= ~MASK(b)), 0))
+#define SWITCH_STATE (state & MASK2(LEFT_CONTIG, RIGHT_CONTIG))
+
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+ ASSERT(idx <= ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t));
+ ep = &ifp->if_u1.if_extents[idx];
+ state = 0;
+ /*
+ * Check and set flags if this segment has a left neighbor.
+ */
+ if (STATE_SET_TEST(LEFT_VALID, idx > 0)) {
+ xfs_bmbt_get_all(ep - 1, &left);
+ STATE_SET(LEFT_DELAY, ISNULLSTARTBLOCK(left.br_startblock));
+ }
+ /*
+ * Check and set flags if this segment has a current value.
+ * Not true if we're inserting into the "hole" at eof.
+ */
+ if (STATE_SET_TEST(RIGHT_VALID,
+ idx <
+ ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t))) {
+ xfs_bmbt_get_all(ep, &right);
+ STATE_SET(RIGHT_DELAY, ISNULLSTARTBLOCK(right.br_startblock));
+ }
+ /*
+ * We're inserting a real allocation between "left" and "right".
+ * Set the contiguity flags. Don't let extents get too large.
+ */
+ STATE_SET(LEFT_CONTIG,
+ STATE_TEST(LEFT_VALID) && !STATE_TEST(LEFT_DELAY) &&
+ left.br_startoff + left.br_blockcount == new->br_startoff &&
+ left.br_startblock + left.br_blockcount == new->br_startblock &&
+ left.br_state == new->br_state &&
+ left.br_blockcount + new->br_blockcount <= MAXEXTLEN);
+ STATE_SET(RIGHT_CONTIG,
+ STATE_TEST(RIGHT_VALID) && !STATE_TEST(RIGHT_DELAY) &&
+ new->br_startoff + new->br_blockcount == right.br_startoff &&
+ new->br_startblock + new->br_blockcount ==
+ right.br_startblock &&
+ new->br_state == right.br_state &&
+ new->br_blockcount + right.br_blockcount <= MAXEXTLEN &&
+ (!STATE_TEST(LEFT_CONTIG) ||
+ left.br_blockcount + new->br_blockcount +
+ right.br_blockcount <= MAXEXTLEN));
+
+ /*
+ * Select which case we're in here, and implement it.
+ */
+ switch (SWITCH_STATE) {
+
+ case MASK2(LEFT_CONTIG, RIGHT_CONTIG):
+ /*
+ * New allocation is contiguous with real allocations on the
+ * left and on the right.
+ * Merge all three into a single extent list entry.
+ */
+ xfs_bmap_trace_pre_update(fname, "LC|RC", ip, idx - 1,
+ whichfork);
+ xfs_bmbt_set_blockcount(ep - 1,
+ left.br_blockcount + new->br_blockcount +
+ right.br_blockcount);
+ xfs_bmap_trace_post_update(fname, "LC|RC", ip, idx - 1,
+ whichfork);
+ xfs_bmap_trace_delete(fname, "LC|RC", ip,
+ idx, 1, whichfork);
+ xfs_bmap_delete_exlist(ip, idx, 1, whichfork);
+ ifp->if_lastex = idx - 1;
+ XFS_IFORK_NEXT_SET(ip, whichfork,
+ XFS_IFORK_NEXTENTS(ip, whichfork) - 1);
+ if (cur == NULL) {
+ *logflagsp = XFS_ILOG_CORE | XFS_ILOG_FEXT(whichfork);
+ return 0;
+ }
+ *logflagsp = XFS_ILOG_CORE;
+ if ((error = xfs_bmbt_lookup_eq(cur, right.br_startoff,
+ right.br_startblock, right.br_blockcount, &i)))
+ return error;
+ ASSERT(i == 1);
+ if ((error = xfs_bmbt_delete(cur, &i)))
+ return error;
+ ASSERT(i == 1);
+ if ((error = xfs_bmbt_decrement(cur, 0, &i)))
+ return error;
+ ASSERT(i == 1);
+ error = xfs_bmbt_update(cur, left.br_startoff,
+ left.br_startblock,
+ left.br_blockcount + new->br_blockcount +
+ right.br_blockcount, left.br_state);
+ return error;
+
+ case MASK(LEFT_CONTIG):
+ /*
+ * New allocation is contiguous with a real allocation
+ * on the left.
+ * Merge the new allocation with the left neighbor.
+ */
+ xfs_bmap_trace_pre_update(fname, "LC", ip, idx - 1, whichfork);
+ xfs_bmbt_set_blockcount(ep - 1,
+ left.br_blockcount + new->br_blockcount);
+ xfs_bmap_trace_post_update(fname, "LC", ip, idx - 1, whichfork);
+ ifp->if_lastex = idx - 1;
+ if (cur == NULL) {
+ *logflagsp = XFS_ILOG_FEXT(whichfork);
+ return 0;
+ }
+ *logflagsp = 0;
+ if ((error = xfs_bmbt_lookup_eq(cur, left.br_startoff,
+ left.br_startblock, left.br_blockcount, &i)))
+ return error;
+ ASSERT(i == 1);
+ error = xfs_bmbt_update(cur, left.br_startoff,
+ left.br_startblock,
+ left.br_blockcount + new->br_blockcount,
+ left.br_state);
+ return error;
+
+ case MASK(RIGHT_CONTIG):
+ /*
+ * New allocation is contiguous with a real allocation
+ * on the right.
+ * Merge the new allocation with the right neighbor.
+ */
+ xfs_bmap_trace_pre_update(fname, "RC", ip, idx, whichfork);
+ xfs_bmbt_set_allf(ep, new->br_startoff, new->br_startblock,
+ new->br_blockcount + right.br_blockcount,
+ right.br_state);
+ xfs_bmap_trace_post_update(fname, "RC", ip, idx, whichfork);
+ ifp->if_lastex = idx;
+ if (cur == NULL) {
+ *logflagsp = XFS_ILOG_FEXT(whichfork);
+ return 0;
+ }
+ *logflagsp = 0;
+ if ((error = xfs_bmbt_lookup_eq(cur, right.br_startoff,
+ right.br_startblock, right.br_blockcount, &i)))
+ return error;
+ ASSERT(i == 1);
+ error = xfs_bmbt_update(cur, new->br_startoff,
+ new->br_startblock,
+ new->br_blockcount + right.br_blockcount,
+ right.br_state);
+ return error;
+
+ case 0:
+ /*
+ * New allocation is not contiguous with another
+ * real allocation.
+ * Insert a new entry.
+ */
+ xfs_bmap_trace_insert(fname, "0", ip, idx, 1, new, NULL,
+ whichfork);
+ xfs_bmap_insert_exlist(ip, idx, 1, new, whichfork);
+ ifp->if_lastex = idx;
+ XFS_IFORK_NEXT_SET(ip, whichfork,
+ XFS_IFORK_NEXTENTS(ip, whichfork) + 1);
+ if (cur == NULL) {
+ *logflagsp = XFS_ILOG_CORE | XFS_ILOG_FEXT(whichfork);
+ return 0;
+ }
+ *logflagsp = XFS_ILOG_CORE;
+ if ((error = xfs_bmbt_lookup_eq(cur, new->br_startoff,
+ new->br_startblock, new->br_blockcount, &i)))
+ return error;
+ ASSERT(i == 0);
+ cur->bc_rec.b.br_state = new->br_state;
+ if ((error = xfs_bmbt_insert(cur, &i)))
+ return error;
+ ASSERT(i == 1);
+ return 0;
+ }
+#undef MASK
+#undef MASK2
+#undef STATE_SET
+#undef STATE_TEST
+#undef STATE_SET_TEST
+#undef SWITCH_STATE
+ /* NOTREACHED */
+ ASSERT(0);
+ return 0; /* keep gcc quite */
+}
+
+#define XFS_ALLOC_GAP_UNITS 4
+
+/*
+ * xfs_bmap_alloc is called by xfs_bmapi to allocate an extent for a file.
+ * It figures out where to ask the underlying allocator to put the new extent.
+ */
+STATIC int /* error */
+xfs_bmap_alloc(
+ xfs_bmalloca_t *ap) /* bmap alloc argument struct */
+{
+ xfs_fsblock_t adjust; /* adjustment to block numbers */
+ xfs_alloctype_t atype=0; /* type for allocation routines */
+ int error; /* error return value */
+ xfs_agnumber_t fb_agno; /* ag number of ap->firstblock */
+ xfs_mount_t *mp; /* mount point structure */
+ int nullfb; /* true if ap->firstblock isn't set */
+ int rt; /* true if inode is realtime */
+#ifdef __KERNEL__
+ xfs_extlen_t prod=0; /* product factor for allocators */
+ xfs_extlen_t ralen=0; /* realtime allocation length */
+#endif
+
+#define ISVALID(x,y) \
+ (rt ? \
+ (x) < mp->m_sb.sb_rblocks : \
+ XFS_FSB_TO_AGNO(mp, x) == XFS_FSB_TO_AGNO(mp, y) && \
+ XFS_FSB_TO_AGNO(mp, x) < mp->m_sb.sb_agcount && \
+ XFS_FSB_TO_AGBNO(mp, x) < mp->m_sb.sb_agblocks)
+
+ /*
+ * Set up variables.
+ */
+ mp = ap->ip->i_mount;
+ nullfb = ap->firstblock == NULLFSBLOCK;
+ rt = XFS_IS_REALTIME_INODE(ap->ip) && ap->userdata;
+ fb_agno = nullfb ? NULLAGNUMBER : XFS_FSB_TO_AGNO(mp, ap->firstblock);
+#ifdef __KERNEL__
+ if (rt) {
+ xfs_extlen_t extsz; /* file extent size for rt */
+ xfs_fileoff_t nexto; /* next file offset */
+ xfs_extlen_t orig_alen; /* original ap->alen */
+ xfs_fileoff_t orig_end; /* original off+len */
+ xfs_fileoff_t orig_off; /* original ap->off */
+ xfs_extlen_t mod_off; /* modulus calculations */
+ xfs_fileoff_t prevo; /* previous file offset */
+ xfs_rtblock_t rtx; /* realtime extent number */
+ xfs_extlen_t temp; /* temp for rt calculations */
+
+ /*
+ * Set prod to match the realtime extent size.
+ */
+ if (!(extsz = ap->ip->i_d.di_extsize))
+ extsz = mp->m_sb.sb_rextsize;
+ prod = extsz / mp->m_sb.sb_rextsize;
+ orig_off = ap->off;
+ orig_alen = ap->alen;
+ orig_end = orig_off + orig_alen;
+ /*
+ * If the file offset is unaligned vs. the extent size
+ * we need to align it. This will be possible unless
+ * the file was previously written with a kernel that didn't
+ * perform this alignment.
+ */
+ mod_off = do_mod(orig_off, extsz);
+ if (mod_off) {
+ ap->alen += mod_off;
+ ap->off -= mod_off;
+ }
+ /*
+ * Same adjustment for the end of the requested area.
+ */
+ if ((temp = (ap->alen % extsz)))
+ ap->alen += extsz - temp;
+ /*
+ * If the previous block overlaps with this proposed allocation
+ * then move the start forward without adjusting the length.
+ */
+ prevo =
+ ap->prevp->br_startoff == NULLFILEOFF ?
+ 0 :
+ (ap->prevp->br_startoff +
+ ap->prevp->br_blockcount);
+ if (ap->off != orig_off && ap->off < prevo)
+ ap->off = prevo;
+ /*
+ * If the next block overlaps with this proposed allocation
+ * then move the start back without adjusting the length,
+ * but not before offset 0.
+ * This may of course make the start overlap previous block,
+ * and if we hit the offset 0 limit then the next block
+ * can still overlap too.
+ */
+ nexto = (ap->eof || ap->gotp->br_startoff == NULLFILEOFF) ?
+ NULLFILEOFF : ap->gotp->br_startoff;
+ if (!ap->eof &&
+ ap->off + ap->alen != orig_end &&
+ ap->off + ap->alen > nexto)
+ ap->off = nexto > ap->alen ? nexto - ap->alen : 0;
+ /*
+ * If we're now overlapping the next or previous extent that
+ * means we can't fit an extsz piece in this hole. Just move
+ * the start forward to the first valid spot and set
+ * the length so we hit the end.
+ */
+ if ((ap->off != orig_off && ap->off < prevo) ||
+ (ap->off + ap->alen != orig_end &&
+ ap->off + ap->alen > nexto)) {
+ ap->off = prevo;
+ ap->alen = nexto - prevo;
+ }
+ /*
+ * If the result isn't a multiple of rtextents we need to
+ * remove blocks until it is.
+ */
+ if ((temp = (ap->alen % mp->m_sb.sb_rextsize))) {
+ /*
+ * We're not covering the original request, or
+ * we won't be able to once we fix the length.
+ */
+ if (orig_off < ap->off ||
+ orig_end > ap->off + ap->alen ||
+ ap->alen - temp < orig_alen)
+ return XFS_ERROR(EINVAL);
+ /*
+ * Try to fix it by moving the start up.
+ */
+ if (ap->off + temp <= orig_off) {
+ ap->alen -= temp;
+ ap->off += temp;
+ }
+ /*
+ * Try to fix it by moving the end in.
+ */
+ else if (ap->off + ap->alen - temp >= orig_end)
+ ap->alen -= temp;
+ /*
+ * Set the start to the minimum then trim the length.
+ */
+ else {
+ ap->alen -= orig_off - ap->off;
+ ap->off = orig_off;
+ ap->alen -= ap->alen % mp->m_sb.sb_rextsize;
+ }
+ /*
+ * Result doesn't cover the request, fail it.
+ */
+ if (orig_off < ap->off || orig_end > ap->off + ap->alen)
+ return XFS_ERROR(EINVAL);
+ }
+ ASSERT(ap->alen % mp->m_sb.sb_rextsize == 0);
+ /*
+ * If the offset & length are not perfectly aligned
+ * then kill prod, it will just get us in trouble.
+ */
+ if (do_mod(ap->off, extsz) || ap->alen % extsz)
+ prod = 1;
+ /*
+ * Set ralen to be the actual requested length in rtextents.
+ */
+ ralen = ap->alen / mp->m_sb.sb_rextsize;
+ /*
+ * If the old value was close enough to MAXEXTLEN that
+ * we rounded up to it, cut it back so it's valid again.
+ * Note that if it's a really large request (bigger than
+ * MAXEXTLEN), we don't hear about that number, and can't
+ * adjust the starting point to match it.
+ */
+ if (ralen * mp->m_sb.sb_rextsize >= MAXEXTLEN)
+ ralen = MAXEXTLEN / mp->m_sb.sb_rextsize;
+ /*
+ * If it's an allocation to an empty file at offset 0,
+ * pick an extent that will space things out in the rt area.
+ */
+ if (ap->eof && ap->off == 0) {
+ error = xfs_rtpick_extent(mp, ap->tp, ralen, &rtx);
+ if (error)
+ return error;
+ ap->rval = rtx * mp->m_sb.sb_rextsize;
+ } else
+ ap->rval = 0;
+ }
+#else
+ if (rt)
+ ap->rval = 0;
+#endif /* __KERNEL__ */
+ else if (nullfb)
+ ap->rval = XFS_INO_TO_FSB(mp, ap->ip->i_ino);
+ else
+ ap->rval = ap->firstblock;
+ /*
+ * If allocating at eof, and there's a previous real block,
+ * try to use it's last block as our starting point.
+ */
+ if (ap->eof && ap->prevp->br_startoff != NULLFILEOFF &&
+ !ISNULLSTARTBLOCK(ap->prevp->br_startblock) &&
+ ISVALID(ap->prevp->br_startblock + ap->prevp->br_blockcount,
+ ap->prevp->br_startblock)) {
+ ap->rval = ap->prevp->br_startblock + ap->prevp->br_blockcount;
+ /*
+ * Adjust for the gap between prevp and us.
+ */
+ adjust = ap->off -
+ (ap->prevp->br_startoff + ap->prevp->br_blockcount);
+ if (adjust &&
+ ISVALID(ap->rval + adjust, ap->prevp->br_startblock))
+ ap->rval += adjust;
+ }
+ /*
+ * If not at eof, then compare the two neighbor blocks.
+ * Figure out whether either one gives us a good starting point,
+ * and pick the better one.
+ */
+ else if (!ap->eof) {
+ xfs_fsblock_t gotbno; /* right side block number */
+ xfs_fsblock_t gotdiff=0; /* right side difference */
+ xfs_fsblock_t prevbno; /* left side block number */
+ xfs_fsblock_t prevdiff=0; /* left side difference */
+
+ /*
+ * If there's a previous (left) block, select a requested
+ * start block based on it.
+ */
+ if (ap->prevp->br_startoff != NULLFILEOFF &&
+ !ISNULLSTARTBLOCK(ap->prevp->br_startblock) &&
+ (prevbno = ap->prevp->br_startblock +
+ ap->prevp->br_blockcount) &&
+ ISVALID(prevbno, ap->prevp->br_startblock)) {
+ /*
+ * Calculate gap to end of previous block.
+ */
+ adjust = prevdiff = ap->off -
+ (ap->prevp->br_startoff +
+ ap->prevp->br_blockcount);
+ /*
+ * Figure the startblock based on the previous block's
+ * end and the gap size.
+ * Heuristic!
+ * If the gap is large relative to the piece we're
+ * allocating, or using it gives us an invalid block
+ * number, then just use the end of the previous block.
+ */
+ if (prevdiff <= XFS_ALLOC_GAP_UNITS * ap->alen &&
+ ISVALID(prevbno + prevdiff,
+ ap->prevp->br_startblock))
+ prevbno += adjust;
+ else
+ prevdiff += adjust;
+ /*
+ * If the firstblock forbids it, can't use it,
+ * must use default.
+ */
+ if (!rt && !nullfb &&
+ XFS_FSB_TO_AGNO(mp, prevbno) != fb_agno)
+ prevbno = NULLFSBLOCK;
+ }
+ /*
+ * No previous block or can't follow it, just default.
+ */
+ else
+ prevbno = NULLFSBLOCK;
+ /*
+ * If there's a following (right) block, select a requested
+ * start block based on it.
+ */
+ if (!ISNULLSTARTBLOCK(ap->gotp->br_startblock)) {
+ /*
+ * Calculate gap to start of next block.
+ */
+ adjust = gotdiff = ap->gotp->br_startoff - ap->off;
+ /*
+ * Figure the startblock based on the next block's
+ * start and the gap size.
+ */
+ gotbno = ap->gotp->br_startblock;
+ /*
+ * Heuristic!
+ * If the gap is large relative to the piece we're
+ * allocating, or using it gives us an invalid block
+ * number, then just use the start of the next block
+ * offset by our length.
+ */
+ if (gotdiff <= XFS_ALLOC_GAP_UNITS * ap->alen &&
+ ISVALID(gotbno - gotdiff, gotbno))
+ gotbno -= adjust;
+ else if (ISVALID(gotbno - ap->alen, gotbno)) {
+ gotbno -= ap->alen;
+ gotdiff += adjust - ap->alen;
+ } else
+ gotdiff += adjust;
+ /*
+ * If the firstblock forbids it, can't use it,
+ * must use default.
+ */
+ if (!rt && !nullfb &&
+ XFS_FSB_TO_AGNO(mp, gotbno) != fb_agno)
+ gotbno = NULLFSBLOCK;
+ }
+ /*
+ * No next block, just default.
+ */
+ else
+ gotbno = NULLFSBLOCK;
+ /*
+ * If both valid, pick the better one, else the only good
+ * one, else ap->rval is already set (to 0 or the inode block).
+ */
+ if (prevbno != NULLFSBLOCK && gotbno != NULLFSBLOCK)
+ ap->rval = prevdiff <= gotdiff ? prevbno : gotbno;
+ else if (prevbno != NULLFSBLOCK)
+ ap->rval = prevbno;
+ else if (gotbno != NULLFSBLOCK)
+ ap->rval = gotbno;
+ }
+ /*
+ * If allowed, use ap->rval; otherwise must use firstblock since
+ * it's in the right allocation group.
+ */
+ if (nullfb || rt || XFS_FSB_TO_AGNO(mp, ap->rval) == fb_agno)
+ ;
+ else
+ ap->rval = ap->firstblock;
+ /*
+ * Realtime allocation, done through xfs_rtallocate_extent.
+ */
+ if (rt) {
+#ifndef __KERNEL__
+ ASSERT(0);
+#else
+ xfs_rtblock_t rtb;
+
+ atype = ap->rval == 0 ?
+ XFS_ALLOCTYPE_ANY_AG : XFS_ALLOCTYPE_NEAR_BNO;
+ do_div(ap->rval, mp->m_sb.sb_rextsize);
+ rtb = ap->rval;
+ ap->alen = ralen;
+ if ((error = xfs_rtallocate_extent(ap->tp, ap->rval, 1, ap->alen,
+ &ralen, atype, ap->wasdel, prod, &rtb)))
+ return error;
+ if (rtb == NULLFSBLOCK && prod > 1 &&
+ (error = xfs_rtallocate_extent(ap->tp, ap->rval, 1,
+ ap->alen, &ralen, atype,
+ ap->wasdel, 1, &rtb)))
+ return error;
+ ap->rval = rtb;
+ if (ap->rval != NULLFSBLOCK) {
+ ap->rval *= mp->m_sb.sb_rextsize;
+ ralen *= mp->m_sb.sb_rextsize;
+ ap->alen = ralen;
+ ap->ip->i_d.di_nblocks += ralen;
+ xfs_trans_log_inode(ap->tp, ap->ip, XFS_ILOG_CORE);
+ if (ap->wasdel)
+ ap->ip->i_delayed_blks -= ralen;
+ /*
+ * Adjust the disk quota also. This was reserved
+ * earlier.
+ */
+ XFS_TRANS_MOD_DQUOT_BYINO(mp, ap->tp, ap->ip,
+ ap->wasdel ? XFS_TRANS_DQ_DELRTBCOUNT :
+ XFS_TRANS_DQ_RTBCOUNT,
+ (long) ralen);
+ } else
+ ap->alen = 0;
+#endif /* __KERNEL__ */
+ }
+ /*
+ * Normal allocation, done through xfs_alloc_vextent.
+ */
+ else {
+ xfs_agnumber_t ag;
+ xfs_alloc_arg_t args;
+ xfs_extlen_t blen;
+ xfs_extlen_t delta;
+ int isaligned;
+ xfs_extlen_t longest;
+ xfs_extlen_t need;
+ xfs_extlen_t nextminlen=0;
+ int notinit;
+ xfs_perag_t *pag;
+ xfs_agnumber_t startag;
+ int tryagain;
+
+ tryagain = isaligned = 0;
+ args.tp = ap->tp;
+ args.mp = mp;
+ args.fsbno = ap->rval;
+ args.maxlen = MIN(ap->alen, mp->m_sb.sb_agblocks);
+ blen = 0;
+ if (nullfb) {
+ args.type = XFS_ALLOCTYPE_START_BNO;
+ args.total = ap->total;
+ /*
+ * Find the longest available space.
+ * We're going to try for the whole allocation at once.
+ */
+ startag = ag = XFS_FSB_TO_AGNO(mp, args.fsbno);
+ notinit = 0;
+ down_read(&mp->m_peraglock);
+ while (blen < ap->alen) {
+ pag = &mp->m_perag[ag];
+ if (!pag->pagf_init &&
+ (error = xfs_alloc_pagf_init(mp, args.tp,
+ ag, XFS_ALLOC_FLAG_TRYLOCK))) {
+ up_read(&mp->m_peraglock);
+ return error;
+ }
+ /*
+ * See xfs_alloc_fix_freelist...
+ */
+ if (pag->pagf_init) {
+ need = XFS_MIN_FREELIST_PAG(pag, mp);
+ delta = need > pag->pagf_flcount ?
+ need - pag->pagf_flcount : 0;
+ longest = (pag->pagf_longest > delta) ?
+ (pag->pagf_longest - delta) :
+ (pag->pagf_flcount > 0 ||
+ pag->pagf_longest > 0);
+ if (blen < longest)
+ blen = longest;
+ } else
+ notinit = 1;
+ if (++ag == mp->m_sb.sb_agcount)
+ ag = 0;
+ if (ag == startag)
+ break;
+ }
+ up_read(&mp->m_peraglock);
+ /*
+ * Since the above loop did a BUF_TRYLOCK, it is
+ * possible that there is space for this request.
+ */
+ if (notinit || blen < ap->minlen)
+ args.minlen = ap->minlen;
+ /*
+ * If the best seen length is less than the request
+ * length, use the best as the minimum.
+ */
+ else if (blen < ap->alen)
+ args.minlen = blen;
+ /*
+ * Otherwise we've seen an extent as big as alen,
+ * use that as the minimum.
+ */
+ else
+ args.minlen = ap->alen;
+ } else if (ap->low) {
+ args.type = XFS_ALLOCTYPE_FIRST_AG;
+ args.total = args.minlen = ap->minlen;
+ } else {
+ args.type = XFS_ALLOCTYPE_NEAR_BNO;
+ args.total = ap->total;
+ args.minlen = ap->minlen;
+ }
+ if (ap->ip->i_d.di_extsize) {
+ args.prod = ap->ip->i_d.di_extsize;
+ if ((args.mod = (xfs_extlen_t)do_mod(ap->off, args.prod)))
+ args.mod = (xfs_extlen_t)(args.prod - args.mod);
+ } else if (mp->m_sb.sb_blocksize >= NBPP) {
+ args.prod = 1;
+ args.mod = 0;
+ } else {
+ args.prod = NBPP >> mp->m_sb.sb_blocklog;
+ if ((args.mod = (xfs_extlen_t)(do_mod(ap->off, args.prod))))
+ args.mod = (xfs_extlen_t)(args.prod - args.mod);
+ }
+ /*
+ * If we are not low on available data blocks, and the
+ * underlying logical volume manager is a stripe, and
+ * the file offset is zero then try to allocate data
+ * blocks on stripe unit boundary.
+ * NOTE: ap->aeof is only set if the allocation length
+ * is >= the stripe unit and the allocation offset is
+ * at the end of file.
+ */
+ if (!ap->low && ap->aeof) {
+ if (!ap->off) {
+ args.alignment = mp->m_dalign;
+ atype = args.type;
+ isaligned = 1;
+ /*
+ * Adjust for alignment
+ */
+ if (blen > args.alignment && blen <= ap->alen)
+ args.minlen = blen - args.alignment;
+ args.minalignslop = 0;
+ } else {
+ /*
+ * First try an exact bno allocation.
+ * If it fails then do a near or start bno
+ * allocation with alignment turned on.
+ */
+ atype = args.type;
+ tryagain = 1;
+ args.type = XFS_ALLOCTYPE_THIS_BNO;
+ args.alignment = 1;
+ /*
+ * Compute the minlen+alignment for the
+ * next case. Set slop so that the value
+ * of minlen+alignment+slop doesn't go up
+ * between the calls.
+ */
+ if (blen > mp->m_dalign && blen <= ap->alen)
+ nextminlen = blen - mp->m_dalign;
+ else
+ nextminlen = args.minlen;
+ if (nextminlen + mp->m_dalign > args.minlen + 1)
+ args.minalignslop =
+ nextminlen + mp->m_dalign -
+ args.minlen - 1;
+ else
+ args.minalignslop = 0;
+ }
+ } else {
+ args.alignment = 1;
+ args.minalignslop = 0;
+ }
+ args.minleft = ap->minleft;
+ args.wasdel = ap->wasdel;
+ args.isfl = 0;
+ args.userdata = ap->userdata;
+ if ((error = xfs_alloc_vextent(&args)))
+ return error;
+ if (tryagain && args.fsbno == NULLFSBLOCK) {
+ /*
+ * Exact allocation failed. Now try with alignment
+ * turned on.
+ */
+ args.type = atype;
+ args.fsbno = ap->rval;
+ args.alignment = mp->m_dalign;
+ args.minlen = nextminlen;
+ args.minalignslop = 0;
+ isaligned = 1;
+ if ((error = xfs_alloc_vextent(&args)))
+ return error;
+ }
+ if (isaligned && args.fsbno == NULLFSBLOCK) {
+ /*
+ * allocation failed, so turn off alignment and
+ * try again.
+ */
+ args.type = atype;
+ args.fsbno = ap->rval;
+ args.alignment = 0;
+ if ((error = xfs_alloc_vextent(&args)))
+ return error;
+ }
+ if (args.fsbno == NULLFSBLOCK && nullfb &&
+ args.minlen > ap->minlen) {
+ args.minlen = ap->minlen;
+ args.type = XFS_ALLOCTYPE_START_BNO;
+ args.fsbno = ap->rval;
+ if ((error = xfs_alloc_vextent(&args)))
+ return error;
+ }
+ if (args.fsbno == NULLFSBLOCK && nullfb) {
+ args.fsbno = 0;
+ args.type = XFS_ALLOCTYPE_FIRST_AG;
+ args.total = ap->minlen;
+ args.minleft = 0;
+ if ((error = xfs_alloc_vextent(&args)))
+ return error;
+ ap->low = 1;
+ }
+ if (args.fsbno != NULLFSBLOCK) {
+ ap->firstblock = ap->rval = args.fsbno;
+ ASSERT(nullfb || fb_agno == args.agno ||
+ (ap->low && fb_agno < args.agno));
+ ap->alen = args.len;
+ ap->ip->i_d.di_nblocks += args.len;
+ xfs_trans_log_inode(ap->tp, ap->ip, XFS_ILOG_CORE);
+ if (ap->wasdel)
+ ap->ip->i_delayed_blks -= args.len;
+ /*
+ * Adjust the disk quota also. This was reserved
+ * earlier.
+ */
+ XFS_TRANS_MOD_DQUOT_BYINO(mp, ap->tp, ap->ip,
+ ap->wasdel ? XFS_TRANS_DQ_DELBCOUNT :
+ XFS_TRANS_DQ_BCOUNT,
+ (long) args.len);
+ } else {
+ ap->rval = NULLFSBLOCK;
+ ap->alen = 0;
+ }
+ }
+ return 0;
+#undef ISVALID
+}
+
+/*
+ * Transform a btree format file with only one leaf node, where the
+ * extents list will fit in the inode, into an extents format file.
+ * Since the extent list is already in-core, all we have to do is
+ * give up the space for the btree root and pitch the leaf block.
+ */
+STATIC int /* error */
+xfs_bmap_btree_to_extents(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_btree_cur_t *cur, /* btree cursor */
+ int *logflagsp, /* inode logging flags */
+ int whichfork) /* data or attr fork */
+{
+ /* REFERENCED */
+ xfs_bmbt_block_t *cblock;/* child btree block */
+ xfs_fsblock_t cbno; /* child block number */
+ xfs_buf_t *cbp; /* child block's buffer */
+ int error; /* error return value */
+ xfs_ifork_t *ifp; /* inode fork data */
+ xfs_mount_t *mp; /* mount point structure */
+ xfs_bmbt_ptr_t *pp; /* ptr to block address */
+ xfs_bmbt_block_t *rblock;/* root btree block */
+
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+ ASSERT(ifp->if_flags & XFS_IFEXTENTS);
+ ASSERT(XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_BTREE);
+ rblock = ifp->if_broot;
+ ASSERT(INT_GET(rblock->bb_level, ARCH_CONVERT) == 1);
+ ASSERT(INT_GET(rblock->bb_numrecs, ARCH_CONVERT) == 1);
+ ASSERT(XFS_BMAP_BROOT_MAXRECS(ifp->if_broot_bytes) == 1);
+ mp = ip->i_mount;
+ pp = XFS_BMAP_BROOT_PTR_ADDR(rblock, 1, ifp->if_broot_bytes);
+ *logflagsp = 0;
+#ifdef DEBUG
+ if ((error = xfs_btree_check_lptr(cur, INT_GET(*pp, ARCH_CONVERT), 1)))
+ return error;
+#endif
+ cbno = INT_GET(*pp, ARCH_CONVERT);
+ if ((error = xfs_btree_read_bufl(mp, tp, cbno, 0, &cbp,
+ XFS_BMAP_BTREE_REF)))
+ return error;
+ cblock = XFS_BUF_TO_BMBT_BLOCK(cbp);
+ if ((error = xfs_btree_check_lblock(cur, cblock, 0, cbp)))
+ return error;
+ xfs_bmap_add_free(cbno, 1, cur->bc_private.b.flist, mp);
+ ip->i_d.di_nblocks--;
+ XFS_TRANS_MOD_DQUOT_BYINO(mp, tp, ip, XFS_TRANS_DQ_BCOUNT, -1L);
+ xfs_trans_binval(tp, cbp);
+ if (cur->bc_bufs[0] == cbp)
+ cur->bc_bufs[0] = NULL;
+ xfs_iroot_realloc(ip, -1, whichfork);
+ ASSERT(ifp->if_broot == NULL);
+ ASSERT((ifp->if_flags & XFS_IFBROOT) == 0);
+ XFS_IFORK_FMT_SET(ip, whichfork, XFS_DINODE_FMT_EXTENTS);
+ *logflagsp = XFS_ILOG_CORE | XFS_ILOG_FEXT(whichfork);
+ return 0;
+}
+
+/*
+ * Called by xfs_bmapi to update extent list structure and the btree
+ * after removing space (or undoing a delayed allocation).
+ */
+STATIC int /* error */
+xfs_bmap_del_extent(
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_trans_t *tp, /* current transaction pointer */
+ xfs_extnum_t idx, /* extent number to update/delete */
+ xfs_bmap_free_t *flist, /* list of extents to be freed */
+ xfs_btree_cur_t *cur, /* if null, not a btree */
+ xfs_bmbt_irec_t *del, /* data to remove from extent list */
+ int *logflagsp, /* inode logging flags */
+ int whichfork, /* data or attr fork */
+ int rsvd) /* OK to allocate reserved blocks */
+{
+ xfs_filblks_t da_new; /* new delay-alloc indirect blocks */
+ xfs_filblks_t da_old; /* old delay-alloc indirect blocks */
+ xfs_fsblock_t del_endblock=0; /* first block past del */
+ xfs_fileoff_t del_endoff; /* first offset past del */
+ int delay; /* current block is delayed allocated */
+ int do_fx; /* free extent at end of routine */
+ xfs_bmbt_rec_t *ep; /* current extent entry pointer */
+ int error; /* error return value */
+ int flags; /* inode logging flags */
+#ifdef XFS_BMAP_TRACE
+ static char fname[] = "xfs_bmap_del_extent";
+#endif
+ xfs_bmbt_irec_t got; /* current extent entry */
+ xfs_fileoff_t got_endoff; /* first offset past got */
+ int i; /* temp state */
+ xfs_ifork_t *ifp; /* inode fork pointer */
+ xfs_mount_t *mp; /* mount structure */
+ xfs_filblks_t nblks; /* quota/sb block count */
+ xfs_bmbt_irec_t new; /* new record to be inserted */
+ /* REFERENCED */
+ xfs_extnum_t nextents; /* number of extents in list */
+ uint qfield; /* quota field to update */
+ xfs_filblks_t temp; /* for indirect length calculations */
+ xfs_filblks_t temp2; /* for indirect length calculations */
+
+ XFS_STATS_INC(xs_del_exlist);
+ mp = ip->i_mount;
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+ nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
+ ASSERT(idx >= 0 && idx < nextents);
+ ASSERT(del->br_blockcount > 0);
+ ep = &ifp->if_u1.if_extents[idx];
+ xfs_bmbt_get_all(ep, &got);
+ ASSERT(got.br_startoff <= del->br_startoff);
+ del_endoff = del->br_startoff + del->br_blockcount;
+ got_endoff = got.br_startoff + got.br_blockcount;
+ ASSERT(got_endoff >= del_endoff);
+ delay = ISNULLSTARTBLOCK(got.br_startblock);
+ ASSERT(ISNULLSTARTBLOCK(del->br_startblock) == delay);
+ flags = 0;
+ qfield = 0;
+ error = 0;
+ /*
+ * If deleting a real allocation, must free up the disk space.
+ */
+ if (!delay) {
+ flags = XFS_ILOG_CORE;
+ /*
+ * Realtime allocation. Free it and record di_nblocks update.
+ */
+ if (whichfork == XFS_DATA_FORK &&
+ (ip->i_d.di_flags & XFS_DIFLAG_REALTIME)) {
+ xfs_fsblock_t bno;
+ xfs_filblks_t len;
+
+ ASSERT(do_mod(del->br_blockcount,
+ mp->m_sb.sb_rextsize) == 0);
+ ASSERT(do_mod(del->br_startblock,
+ mp->m_sb.sb_rextsize) == 0);
+ bno = del->br_startblock;
+ len = del->br_blockcount;
+ do_div(bno, mp->m_sb.sb_rextsize);
+ do_div(len, mp->m_sb.sb_rextsize);
+ if ((error = xfs_rtfree_extent(ip->i_transp, bno,
+ (xfs_extlen_t)len)))
+ goto done;
+ do_fx = 0;
+ nblks = len * mp->m_sb.sb_rextsize;
+ qfield = XFS_TRANS_DQ_RTBCOUNT;
+ }
+ /*
+ * Ordinary allocation.
+ */
+ else {
+ do_fx = 1;
+ nblks = del->br_blockcount;
+ qfield = XFS_TRANS_DQ_BCOUNT;
+ }
+ /*
+ * Set up del_endblock and cur for later.
+ */
+ del_endblock = del->br_startblock + del->br_blockcount;
+ if (cur) {
+ if ((error = xfs_bmbt_lookup_eq(cur, got.br_startoff,
+ got.br_startblock, got.br_blockcount,
+ &i)))
+ goto done;
+ ASSERT(i == 1);
+ }
+ da_old = da_new = 0;
+ } else {
+ da_old = STARTBLOCKVAL(got.br_startblock);
+ da_new = 0;
+ nblks = 0;
+ do_fx = 0;
+ }
+ /*
+ * Set flag value to use in switch statement.
+ * Left-contig is 2, right-contig is 1.
+ */
+ switch (((got.br_startoff == del->br_startoff) << 1) |
+ (got_endoff == del_endoff)) {
+ case 3:
+ /*
+ * Matches the whole extent. Delete the entry.
+ */
+ xfs_bmap_trace_delete(fname, "3", ip, idx, 1, whichfork);
+ xfs_bmap_delete_exlist(ip, idx, 1, whichfork);
+ ifp->if_lastex = idx;
+ if (delay)
+ break;
+ XFS_IFORK_NEXT_SET(ip, whichfork,
+ XFS_IFORK_NEXTENTS(ip, whichfork) - 1);
+ flags |= XFS_ILOG_CORE;
+ if (!cur) {
+ flags |= XFS_ILOG_FEXT(whichfork);
+ break;
+ }
+ if ((error = xfs_bmbt_delete(cur, &i)))
+ goto done;
+ ASSERT(i == 1);
+ break;
+
+ case 2:
+ /*
+ * Deleting the first part of the extent.
+ */
+ xfs_bmap_trace_pre_update(fname, "2", ip, idx, whichfork);
+ xfs_bmbt_set_startoff(ep, del_endoff);
+ temp = got.br_blockcount - del->br_blockcount;
+ xfs_bmbt_set_blockcount(ep, temp);
+ ifp->if_lastex = idx;
+ if (delay) {
+ temp = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
+ da_old);
+ xfs_bmbt_set_startblock(ep, NULLSTARTBLOCK((int)temp));
+ xfs_bmap_trace_post_update(fname, "2", ip, idx,
+ whichfork);
+ da_new = temp;
+ break;
+ }
+ xfs_bmbt_set_startblock(ep, del_endblock);
+ xfs_bmap_trace_post_update(fname, "2", ip, idx, whichfork);
+ if (!cur) {
+ flags |= XFS_ILOG_FEXT(whichfork);
+ break;
+ }
+ if ((error = xfs_bmbt_update(cur, del_endoff, del_endblock,
+ got.br_blockcount - del->br_blockcount,
+ got.br_state)))
+ goto done;
+ break;
+
+ case 1:
+ /*
+ * Deleting the last part of the extent.
+ */
+ temp = got.br_blockcount - del->br_blockcount;
+ xfs_bmap_trace_pre_update(fname, "1", ip, idx, whichfork);
+ xfs_bmbt_set_blockcount(ep, temp);
+ ifp->if_lastex = idx;
+ if (delay) {
+ temp = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
+ da_old);
+ xfs_bmbt_set_startblock(ep, NULLSTARTBLOCK((int)temp));
+ xfs_bmap_trace_post_update(fname, "1", ip, idx,
+ whichfork);
+ da_new = temp;
+ break;
+ }
+ xfs_bmap_trace_post_update(fname, "1", ip, idx, whichfork);
+ if (!cur) {
+ flags |= XFS_ILOG_FEXT(whichfork);
+ break;
+ }
+ if ((error = xfs_bmbt_update(cur, got.br_startoff,
+ got.br_startblock,
+ got.br_blockcount - del->br_blockcount,
+ got.br_state)))
+ goto done;
+ break;
+
+ case 0:
+ /*
+ * Deleting the middle of the extent.
+ */
+ temp = del->br_startoff - got.br_startoff;
+ xfs_bmap_trace_pre_update(fname, "0", ip, idx, whichfork);
+ xfs_bmbt_set_blockcount(ep, temp);
+ new.br_startoff = del_endoff;
+ temp2 = got_endoff - del_endoff;
+ new.br_blockcount = temp2;
+ new.br_state = got.br_state;
+ if (!delay) {
+ new.br_startblock = del_endblock;
+ flags |= XFS_ILOG_CORE;
+ if (cur) {
+ if ((error = xfs_bmbt_update(cur,
+ got.br_startoff,
+ got.br_startblock, temp,
+ got.br_state)))
+ goto done;
+ if ((error = xfs_bmbt_increment(cur, 0, &i)))
+ goto done;
+ cur->bc_rec.b = new;
+ error = xfs_bmbt_insert(cur, &i);
+ if (error && error != ENOSPC)
+ goto done;
+ /*
+ * If get no-space back from btree insert,
+ * it tried a split, and we have a zero
+ * block reservation.
+ * Fix up our state and return the error.
+ */
+ if (error == ENOSPC) {
+ /*
+ * Reset the cursor, don't trust
+ * it after any insert operation.
+ */
+ if ((error = xfs_bmbt_lookup_eq(cur,
+ got.br_startoff,
+ got.br_startblock,
+ temp, &i)))
+ goto done;
+ ASSERT(i == 1);
+ /*
+ * Update the btree record back
+ * to the original value.
+ */
+ if ((error = xfs_bmbt_update(cur,
+ got.br_startoff,
+ got.br_startblock,
+ got.br_blockcount,
+ got.br_state)))
+ goto done;
+ /*
+ * Reset the extent record back
+ * to the original value.
+ */
+ xfs_bmbt_set_blockcount(ep,
+ got.br_blockcount);
+ flags = 0;
+ error = XFS_ERROR(ENOSPC);
+ goto done;
+ }
+ ASSERT(i == 1);
+ } else
+ flags |= XFS_ILOG_FEXT(whichfork);
+ XFS_IFORK_NEXT_SET(ip, whichfork,
+ XFS_IFORK_NEXTENTS(ip, whichfork) + 1);
+ } else {
+ ASSERT(whichfork == XFS_DATA_FORK);
+ temp = xfs_bmap_worst_indlen(ip, temp);
+ xfs_bmbt_set_startblock(ep, NULLSTARTBLOCK((int)temp));
+ temp2 = xfs_bmap_worst_indlen(ip, temp2);
+ new.br_startblock = NULLSTARTBLOCK((int)temp2);
+ da_new = temp + temp2;
+ while (da_new > da_old) {
+ if (temp) {
+ temp--;
+ da_new--;
+ xfs_bmbt_set_startblock(ep,
+ NULLSTARTBLOCK((int)temp));
+ }
+ if (da_new == da_old)
+ break;
+ if (temp2) {
+ temp2--;
+ da_new--;
+ new.br_startblock =
+ NULLSTARTBLOCK((int)temp2);
+ }
+ }
+ }
+ xfs_bmap_trace_post_update(fname, "0", ip, idx, whichfork);
+ xfs_bmap_trace_insert(fname, "0", ip, idx + 1, 1, &new, NULL,
+ whichfork);
+ xfs_bmap_insert_exlist(ip, idx + 1, 1, &new, whichfork);
+ ifp->if_lastex = idx + 1;
+ break;
+ }
+ /*
+ * If we need to, add to list of extents to delete.
+ */
+ if (do_fx)
+ xfs_bmap_add_free(del->br_startblock, del->br_blockcount, flist,
+ mp);
+ /*
+ * Adjust inode # blocks in the file.
+ */
+ if (nblks)
+ ip->i_d.di_nblocks -= nblks;
+ /*
+ * Adjust quota data.
+ */
+ if (qfield)
+ XFS_TRANS_MOD_DQUOT_BYINO(mp, tp, ip, qfield, (long)-nblks);
+
+ /*
+ * Account for change in delayed indirect blocks.
+ * Nothing to do for disk quota accounting here.
+ */
+ ASSERT(da_old >= da_new);
+ if (da_old > da_new)
+ xfs_mod_incore_sb(mp, XFS_SBS_FDBLOCKS, (int)(da_old - da_new),
+ rsvd);
+done:
+ *logflagsp = flags;
+ return error;
+}
+
+/*
+ * Remove the entry "free" from the free item list. Prev points to the
+ * previous entry, unless "free" is the head of the list.
+ */
+STATIC void
+xfs_bmap_del_free(
+ xfs_bmap_free_t *flist, /* free item list header */
+ xfs_bmap_free_item_t *prev, /* previous item on list, if any */
+ xfs_bmap_free_item_t *free) /* list item to be freed */
+{
+ if (prev)
+ prev->xbfi_next = free->xbfi_next;
+ else
+ flist->xbf_first = free->xbfi_next;
+ flist->xbf_count--;
+ kmem_zone_free(xfs_bmap_free_item_zone, free);
+}
+
+/*
+ * Remove count entries from the extents array for inode "ip", starting
+ * at index "idx". Copies the remaining items down over the deleted ones,
+ * and gives back the excess memory.
+ */
+STATIC void
+xfs_bmap_delete_exlist(
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_extnum_t idx, /* starting delete index */
+ xfs_extnum_t count, /* count of items to delete */
+ int whichfork) /* data or attr fork */
+{
+ xfs_bmbt_rec_t *base; /* base of extent list */
+ xfs_ifork_t *ifp; /* inode fork pointer */
+ xfs_extnum_t nextents; /* number of extents in list after */
+
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+ ASSERT(ifp->if_flags & XFS_IFEXTENTS);
+ base = ifp->if_u1.if_extents;
+ nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t) - count;
+ memmove(&base[idx], &base[idx + count],
+ (nextents - idx) * sizeof(*base));
+ xfs_iext_realloc(ip, -count, whichfork);
+}
+
+/*
+ * Convert an extents-format file into a btree-format file.
+ * The new file will have a root block (in the inode) and a single child block.
+ */
+STATIC int /* error */
+xfs_bmap_extents_to_btree(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_fsblock_t *firstblock, /* first-block-allocated */
+ xfs_bmap_free_t *flist, /* blocks freed in xaction */
+ xfs_btree_cur_t **curp, /* cursor returned to caller */
+ int wasdel, /* converting a delayed alloc */
+ int *logflagsp, /* inode logging flags */
+ int whichfork) /* data or attr fork */
+{
+ xfs_bmbt_block_t *ablock; /* allocated (child) bt block */
+ xfs_buf_t *abp; /* buffer for ablock */
+ xfs_alloc_arg_t args; /* allocation arguments */
+ xfs_bmbt_rec_t *arp; /* child record pointer */
+ xfs_bmbt_block_t *block; /* btree root block */
+ xfs_btree_cur_t *cur; /* bmap btree cursor */
+ xfs_bmbt_rec_t *ep; /* extent list pointer */
+ int error; /* error return value */
+ xfs_extnum_t i, cnt; /* extent list index */
+ xfs_ifork_t *ifp; /* inode fork pointer */
+ xfs_bmbt_key_t *kp; /* root block key pointer */
+ xfs_mount_t *mp; /* mount structure */
+ xfs_extnum_t nextents; /* extent list size */
+ xfs_bmbt_ptr_t *pp; /* root block address pointer */
+
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+ ASSERT(XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_EXTENTS);
+ ASSERT(ifp->if_ext_max ==
+ XFS_IFORK_SIZE(ip, whichfork) / (uint)sizeof(xfs_bmbt_rec_t));
+ /*
+ * Make space in the inode incore.
+ */
+ xfs_iroot_realloc(ip, 1, whichfork);
+ ifp->if_flags |= XFS_IFBROOT;
+ /*
+ * Fill in the root.
+ */
+ block = ifp->if_broot;
+ INT_SET(block->bb_magic, ARCH_CONVERT, XFS_BMAP_MAGIC);
+ INT_SET(block->bb_level, ARCH_CONVERT, 1);
+ INT_SET(block->bb_numrecs, ARCH_CONVERT, 1);
+ INT_SET(block->bb_leftsib, ARCH_CONVERT, NULLDFSBNO);
+ INT_SET(block->bb_rightsib, ARCH_CONVERT, NULLDFSBNO);
+ /*
+ * Need a cursor. Can't allocate until bb_level is filled in.
+ */
+ mp = ip->i_mount;
+ cur = xfs_btree_init_cursor(mp, tp, NULL, 0, XFS_BTNUM_BMAP, ip,
+ whichfork);
+ cur->bc_private.b.firstblock = *firstblock;
+ cur->bc_private.b.flist = flist;
+ cur->bc_private.b.flags = wasdel ? XFS_BTCUR_BPRV_WASDEL : 0;
+ /*
+ * Convert to a btree with two levels, one record in root.
+ */
+ XFS_IFORK_FMT_SET(ip, whichfork, XFS_DINODE_FMT_BTREE);
+ args.tp = tp;
+ args.mp = mp;
+ if (*firstblock == NULLFSBLOCK) {
+ args.type = XFS_ALLOCTYPE_START_BNO;
+ args.fsbno = XFS_INO_TO_FSB(mp, ip->i_ino);
+ } else if (flist->xbf_low) {
+ args.type = XFS_ALLOCTYPE_START_BNO;
+ args.fsbno = *firstblock;
+ } else {
+ args.type = XFS_ALLOCTYPE_NEAR_BNO;
+ args.fsbno = *firstblock;
+ }
+ args.minlen = args.maxlen = args.prod = 1;
+ args.total = args.minleft = args.alignment = args.mod = args.isfl =
+ args.minalignslop = 0;
+ args.wasdel = wasdel;
+ *logflagsp = 0;
+ if ((error = xfs_alloc_vextent(&args))) {
+ xfs_iroot_realloc(ip, -1, whichfork);
+ xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
+ return error;
+ }
+ /*
+ * Allocation can't fail, the space was reserved.
+ */
+ ASSERT(args.fsbno != NULLFSBLOCK);
+ ASSERT(*firstblock == NULLFSBLOCK ||
+ args.agno == XFS_FSB_TO_AGNO(mp, *firstblock) ||
+ (flist->xbf_low &&
+ args.agno > XFS_FSB_TO_AGNO(mp, *firstblock)));
+ *firstblock = cur->bc_private.b.firstblock = args.fsbno;
+ cur->bc_private.b.allocated++;
+ ip->i_d.di_nblocks++;
+ XFS_TRANS_MOD_DQUOT_BYINO(mp, tp, ip, XFS_TRANS_DQ_BCOUNT, 1L);
+ abp = xfs_btree_get_bufl(mp, tp, args.fsbno, 0);
+ /*
+ * Fill in the child block.
+ */
+ ablock = XFS_BUF_TO_BMBT_BLOCK(abp);
+ INT_SET(ablock->bb_magic, ARCH_CONVERT, XFS_BMAP_MAGIC);
+ INT_ZERO(ablock->bb_level, ARCH_CONVERT);
+ INT_SET(ablock->bb_leftsib, ARCH_CONVERT, NULLDFSBNO);
+ INT_SET(ablock->bb_rightsib, ARCH_CONVERT, NULLDFSBNO);
+ arp = XFS_BMAP_REC_IADDR(ablock, 1, cur);
+ nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
+ for (ep = ifp->if_u1.if_extents, cnt = i = 0; i < nextents; i++, ep++) {
+ if (!ISNULLSTARTBLOCK(xfs_bmbt_get_startblock(ep))) {
+ arp->l0 = INT_GET(ep->l0, ARCH_CONVERT);
+ arp->l1 = INT_GET(ep->l1, ARCH_CONVERT);
+ arp++; cnt++;
+ }
+ }
+ INT_SET(ablock->bb_numrecs, ARCH_CONVERT, cnt);
+ ASSERT(INT_GET(ablock->bb_numrecs, ARCH_CONVERT) == XFS_IFORK_NEXTENTS(ip, whichfork));
+ /*
+ * Fill in the root key and pointer.
+ */
+ kp = XFS_BMAP_KEY_IADDR(block, 1, cur);
+ arp = XFS_BMAP_REC_IADDR(ablock, 1, cur);
+ INT_SET(kp->br_startoff, ARCH_CONVERT, xfs_bmbt_disk_get_startoff(arp));
+ pp = XFS_BMAP_PTR_IADDR(block, 1, cur);
+ INT_SET(*pp, ARCH_CONVERT, args.fsbno);
+ /*
+ * Do all this logging at the end so that
+ * the root is at the right level.
+ */
+ xfs_bmbt_log_block(cur, abp, XFS_BB_ALL_BITS);
+ xfs_bmbt_log_recs(cur, abp, 1, INT_GET(ablock->bb_numrecs, ARCH_CONVERT));
+ ASSERT(*curp == NULL);
+ *curp = cur;
+ *logflagsp = XFS_ILOG_CORE | XFS_ILOG_FBROOT(whichfork);
+ return 0;
+}
+
+/*
+ * Insert new item(s) in the extent list for inode "ip".
+ * Count new items are inserted at offset idx.
+ */
+STATIC void
+xfs_bmap_insert_exlist(
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_extnum_t idx, /* starting index of new items */
+ xfs_extnum_t count, /* number of inserted items */
+ xfs_bmbt_irec_t *new, /* items to insert */
+ int whichfork) /* data or attr fork */
+{
+ xfs_bmbt_rec_t *base; /* extent list base */
+ xfs_ifork_t *ifp; /* inode fork pointer */
+ xfs_extnum_t nextents; /* extent list size */
+ xfs_extnum_t to; /* extent list index */
+
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+ ASSERT(ifp->if_flags & XFS_IFEXTENTS);
+ xfs_iext_realloc(ip, count, whichfork);
+ base = ifp->if_u1.if_extents;
+ nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
+ memmove(&base[idx + count], &base[idx],
+ (nextents - (idx + count)) * sizeof(*base));
+ for (to = idx; to < idx + count; to++, new++)
+ xfs_bmbt_set_all(&base[to], new);
+}
+
+/*
+ * Convert a local file to an extents file.
+ * This code is out of bounds for data forks of regular files,
+ * since the file data needs to get logged so things will stay consistent.
+ * (The bmap-level manipulations are ok, though).
+ */
+STATIC int /* error */
+xfs_bmap_local_to_extents(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_fsblock_t *firstblock, /* first block allocated in xaction */
+ xfs_extlen_t total, /* total blocks needed by transaction */
+ int *logflagsp, /* inode logging flags */
+ int whichfork) /* data or attr fork */
+{
+ int error; /* error return value */
+ int flags; /* logging flags returned */
+#ifdef XFS_BMAP_TRACE
+ static char fname[] = "xfs_bmap_local_to_extents";
+#endif
+ xfs_ifork_t *ifp; /* inode fork pointer */
+
+ /*
+ * We don't want to deal with the case of keeping inode data inline yet.
+ * So sending the data fork of a regular inode is invalid.
+ */
+ ASSERT(!((ip->i_d.di_mode & S_IFMT) == S_IFREG &&
+ whichfork == XFS_DATA_FORK));
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+ ASSERT(XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_LOCAL);
+ flags = 0;
+ error = 0;
+ if (ifp->if_bytes) {
+ xfs_alloc_arg_t args; /* allocation arguments */
+ xfs_buf_t *bp; /* buffer for extent list block */
+ xfs_bmbt_rec_t *ep; /* extent list pointer */
+
+ args.tp = tp;
+ args.mp = ip->i_mount;
+ ASSERT(ifp->if_flags & XFS_IFINLINE);
+ /*
+ * Allocate a block. We know we need only one, since the
+ * file currently fits in an inode.
+ */
+ if (*firstblock == NULLFSBLOCK) {
+ args.fsbno = XFS_INO_TO_FSB(args.mp, ip->i_ino);
+ args.type = XFS_ALLOCTYPE_START_BNO;
+ } else {
+ args.fsbno = *firstblock;
+ args.type = XFS_ALLOCTYPE_NEAR_BNO;
+ }
+ args.total = total;
+ args.mod = args.minleft = args.alignment = args.wasdel =
+ args.isfl = args.minalignslop = 0;
+ args.minlen = args.maxlen = args.prod = 1;
+ if ((error = xfs_alloc_vextent(&args)))
+ goto done;
+ /*
+ * Can't fail, the space was reserved.
+ */
+ ASSERT(args.fsbno != NULLFSBLOCK);
+ ASSERT(args.len == 1);
+ *firstblock = args.fsbno;
+ bp = xfs_btree_get_bufl(args.mp, tp, args.fsbno, 0);
+ memcpy((char *)XFS_BUF_PTR(bp), ifp->if_u1.if_data,
+ ifp->if_bytes);
+ xfs_trans_log_buf(tp, bp, 0, ifp->if_bytes - 1);
+ xfs_idata_realloc(ip, -ifp->if_bytes, whichfork);
+ xfs_iext_realloc(ip, 1, whichfork);
+ ep = ifp->if_u1.if_extents;
+ xfs_bmbt_set_allf(ep, 0, args.fsbno, 1, XFS_EXT_NORM);
+ xfs_bmap_trace_post_update(fname, "new", ip, 0, whichfork);
+ XFS_IFORK_NEXT_SET(ip, whichfork, 1);
+ ip->i_d.di_nblocks = 1;
+ XFS_TRANS_MOD_DQUOT_BYINO(args.mp, tp, ip,
+ XFS_TRANS_DQ_BCOUNT, 1L);
+ flags |= XFS_ILOG_FEXT(whichfork);
+ } else
+ ASSERT(XFS_IFORK_NEXTENTS(ip, whichfork) == 0);
+ ifp->if_flags &= ~XFS_IFINLINE;
+ ifp->if_flags |= XFS_IFEXTENTS;
+ XFS_IFORK_FMT_SET(ip, whichfork, XFS_DINODE_FMT_EXTENTS);
+ flags |= XFS_ILOG_CORE;
+done:
+ *logflagsp = flags;
+ return error;
+}
+
+xfs_bmbt_rec_t * /* pointer to found extent entry */
+xfs_bmap_do_search_extents(
+ xfs_bmbt_rec_t *base, /* base of extent list */
+ xfs_extnum_t lastx, /* last extent index used */
+ xfs_extnum_t nextents, /* extent list size */
+ xfs_fileoff_t bno, /* block number searched for */
+ int *eofp, /* out: end of file found */
+ xfs_extnum_t *lastxp, /* out: last extent index */
+ xfs_bmbt_irec_t *gotp, /* out: extent entry found */
+ xfs_bmbt_irec_t *prevp) /* out: previous extent entry found */
+{
+ xfs_bmbt_rec_t *ep; /* extent list entry pointer */
+ xfs_bmbt_irec_t got; /* extent list entry, decoded */
+ int high; /* high index of binary search */
+ int low; /* low index of binary search */
+
+ /*
+ * Initialize the extent entry structure to catch access to
+ * uninitialized br_startblock field.
+ */
+ got.br_startoff = 0xffa5a5a5a5a5a5a5LL;
+ got.br_blockcount = 0xa55a5a5a5a5a5a5aLL;
+ got.br_state = XFS_EXT_INVALID;
+
+#if XFS_BIG_BLKNOS
+ got.br_startblock = 0xffffa5a5a5a5a5a5LL;
+#else
+ got.br_startblock = 0xffffa5a5;
+#endif
+
+ if (lastx != NULLEXTNUM && lastx < nextents)
+ ep = base + lastx;
+ else
+ ep = NULL;
+ prevp->br_startoff = NULLFILEOFF;
+ if (ep && bno >= (got.br_startoff = xfs_bmbt_get_startoff(ep)) &&
+ bno < got.br_startoff +
+ (got.br_blockcount = xfs_bmbt_get_blockcount(ep)))
+ *eofp = 0;
+ else if (ep && lastx < nextents - 1 &&
+ bno >= (got.br_startoff = xfs_bmbt_get_startoff(ep + 1)) &&
+ bno < got.br_startoff +
+ (got.br_blockcount = xfs_bmbt_get_blockcount(ep + 1))) {
+ lastx++;
+ ep++;
+ *eofp = 0;
+ } else if (nextents == 0)
+ *eofp = 1;
+ else if (bno == 0 &&
+ (got.br_startoff = xfs_bmbt_get_startoff(base)) == 0) {
+ ep = base;
+ lastx = 0;
+ got.br_blockcount = xfs_bmbt_get_blockcount(ep);
+ *eofp = 0;
+ } else {
+ /* binary search the extents array */
+ low = 0;
+ high = nextents - 1;
+ while (low <= high) {
+ XFS_STATS_INC(xs_cmp_exlist);
+ lastx = (low + high) >> 1;
+ ep = base + lastx;
+ got.br_startoff = xfs_bmbt_get_startoff(ep);
+ got.br_blockcount = xfs_bmbt_get_blockcount(ep);
+ if (bno < got.br_startoff)
+ high = lastx - 1;
+ else if (bno >= got.br_startoff + got.br_blockcount)
+ low = lastx + 1;
+ else {
+ got.br_startblock = xfs_bmbt_get_startblock(ep);
+ got.br_state = xfs_bmbt_get_state(ep);
+ *eofp = 0;
+ *lastxp = lastx;
+ *gotp = got;
+ return ep;
+ }
+ }
+ if (bno >= got.br_startoff + got.br_blockcount) {
+ lastx++;
+ if (lastx == nextents) {
+ *eofp = 1;
+ got.br_startblock = xfs_bmbt_get_startblock(ep);
+ got.br_state = xfs_bmbt_get_state(ep);
+ *prevp = got;
+ ep = NULL;
+ } else {
+ *eofp = 0;
+ xfs_bmbt_get_all(ep, prevp);
+ ep++;
+ got.br_startoff = xfs_bmbt_get_startoff(ep);
+ got.br_blockcount = xfs_bmbt_get_blockcount(ep);
+ }
+ } else {
+ *eofp = 0;
+ if (ep > base)
+ xfs_bmbt_get_all(ep - 1, prevp);
+ }
+ }
+ if (ep) {
+ got.br_startblock = xfs_bmbt_get_startblock(ep);
+ got.br_state = xfs_bmbt_get_state(ep);
+ }
+ *lastxp = lastx;
+ *gotp = got;
+ return ep;
+}
+
+/*
+ * Search the extents list for the inode, for the extent containing bno.
+ * If bno lies in a hole, point to the next entry. If bno lies past eof,
+ * *eofp will be set, and *prevp will contain the last entry (null if none).
+ * Else, *lastxp will be set to the index of the found
+ * entry; *gotp will contain the entry.
+ */
+STATIC xfs_bmbt_rec_t * /* pointer to found extent entry */
+xfs_bmap_search_extents(
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_fileoff_t bno, /* block number searched for */
+ int whichfork, /* data or attr fork */
+ int *eofp, /* out: end of file found */
+ xfs_extnum_t *lastxp, /* out: last extent index */
+ xfs_bmbt_irec_t *gotp, /* out: extent entry found */
+ xfs_bmbt_irec_t *prevp) /* out: previous extent entry found */
+{
+ xfs_ifork_t *ifp; /* inode fork pointer */
+ xfs_bmbt_rec_t *base; /* base of extent list */
+ xfs_extnum_t lastx; /* last extent index used */
+ xfs_extnum_t nextents; /* extent list size */
+ xfs_bmbt_rec_t *ep; /* extent list entry pointer */
+ int rt; /* realtime flag */
+
+ XFS_STATS_INC(xs_look_exlist);
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+ lastx = ifp->if_lastex;
+ nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
+ base = &ifp->if_u1.if_extents[0];
+
+ ep = xfs_bmap_do_search_extents(base, lastx, nextents, bno, eofp,
+ lastxp, gotp, prevp);
+ rt = ip->i_d.di_flags & XFS_DIFLAG_REALTIME;
+ if(!rt && !gotp->br_startblock && (*lastxp != NULLEXTNUM)) {
+ cmn_err(CE_PANIC,"Access to block zero: fs: <%s> inode: %lld "
+ "start_block : %llx start_off : %llx blkcnt : %llx "
+ "extent-state : %x \n",
+ (ip->i_mount)->m_fsname,(long long)ip->i_ino,
+ gotp->br_startblock, gotp->br_startoff,
+ gotp->br_blockcount,gotp->br_state);
+ }
+ return ep;
+}
+
+
+#ifdef XFS_BMAP_TRACE
+ktrace_t *xfs_bmap_trace_buf;
+
+/*
+ * Add a bmap trace buffer entry. Base routine for the others.
+ */
+STATIC void
+xfs_bmap_trace_addentry(
+ int opcode, /* operation */
+ char *fname, /* function name */
+ char *desc, /* operation description */
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_extnum_t idx, /* index of entry(ies) */
+ xfs_extnum_t cnt, /* count of entries, 1 or 2 */
+ xfs_bmbt_rec_t *r1, /* first record */
+ xfs_bmbt_rec_t *r2, /* second record or null */
+ int whichfork) /* data or attr fork */
+{
+ xfs_bmbt_rec_t tr2;
+
+ ASSERT(cnt == 1 || cnt == 2);
+ ASSERT(r1 != NULL);
+ if (cnt == 1) {
+ ASSERT(r2 == NULL);
+ r2 = &tr2;
+ memset(&tr2, 0, sizeof(tr2));
+ } else
+ ASSERT(r2 != NULL);
+ ktrace_enter(xfs_bmap_trace_buf,
+ (void *)(__psint_t)(opcode | (whichfork << 16)),
+ (void *)fname, (void *)desc, (void *)ip,
+ (void *)(__psint_t)idx,
+ (void *)(__psint_t)cnt,
+ (void *)(__psunsigned_t)(ip->i_ino >> 32),
+ (void *)(__psunsigned_t)(unsigned)ip->i_ino,
+ (void *)(__psunsigned_t)(r1->l0 >> 32),
+ (void *)(__psunsigned_t)(unsigned)(r1->l0),
+ (void *)(__psunsigned_t)(r1->l1 >> 32),
+ (void *)(__psunsigned_t)(unsigned)(r1->l1),
+ (void *)(__psunsigned_t)(r2->l0 >> 32),
+ (void *)(__psunsigned_t)(unsigned)(r2->l0),
+ (void *)(__psunsigned_t)(r2->l1 >> 32),
+ (void *)(__psunsigned_t)(unsigned)(r2->l1)
+ );
+ ASSERT(ip->i_xtrace);
+ ktrace_enter(ip->i_xtrace,
+ (void *)(__psint_t)(opcode | (whichfork << 16)),
+ (void *)fname, (void *)desc, (void *)ip,
+ (void *)(__psint_t)idx,
+ (void *)(__psint_t)cnt,
+ (void *)(__psunsigned_t)(ip->i_ino >> 32),
+ (void *)(__psunsigned_t)(unsigned)ip->i_ino,
+ (void *)(__psunsigned_t)(r1->l0 >> 32),
+ (void *)(__psunsigned_t)(unsigned)(r1->l0),
+ (void *)(__psunsigned_t)(r1->l1 >> 32),
+ (void *)(__psunsigned_t)(unsigned)(r1->l1),
+ (void *)(__psunsigned_t)(r2->l0 >> 32),
+ (void *)(__psunsigned_t)(unsigned)(r2->l0),
+ (void *)(__psunsigned_t)(r2->l1 >> 32),
+ (void *)(__psunsigned_t)(unsigned)(r2->l1)
+ );
+}
+
+/*
+ * Add bmap trace entry prior to a call to xfs_bmap_delete_exlist.
+ */
+STATIC void
+xfs_bmap_trace_delete(
+ char *fname, /* function name */
+ char *desc, /* operation description */
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_extnum_t idx, /* index of entry(entries) deleted */
+ xfs_extnum_t cnt, /* count of entries deleted, 1 or 2 */
+ int whichfork) /* data or attr fork */
+{
+ xfs_ifork_t *ifp; /* inode fork pointer */
+
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+ xfs_bmap_trace_addentry(XFS_BMAP_KTRACE_DELETE, fname, desc, ip, idx,
+ cnt, &ifp->if_u1.if_extents[idx],
+ cnt == 2 ? &ifp->if_u1.if_extents[idx + 1] : NULL,
+ whichfork);
+}
+
+/*
+ * Add bmap trace entry prior to a call to xfs_bmap_insert_exlist, or
+ * reading in the extents list from the disk (in the btree).
+ */
+STATIC void
+xfs_bmap_trace_insert(
+ char *fname, /* function name */
+ char *desc, /* operation description */
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_extnum_t idx, /* index of entry(entries) inserted */
+ xfs_extnum_t cnt, /* count of entries inserted, 1 or 2 */
+ xfs_bmbt_irec_t *r1, /* inserted record 1 */
+ xfs_bmbt_irec_t *r2, /* inserted record 2 or null */
+ int whichfork) /* data or attr fork */
+{
+ xfs_bmbt_rec_t tr1; /* compressed record 1 */
+ xfs_bmbt_rec_t tr2; /* compressed record 2 if needed */
+
+ xfs_bmbt_set_all(&tr1, r1);
+ if (cnt == 2) {
+ ASSERT(r2 != NULL);
+ xfs_bmbt_set_all(&tr2, r2);
+ } else {
+ ASSERT(cnt == 1);
+ ASSERT(r2 == NULL);
+ }
+ xfs_bmap_trace_addentry(XFS_BMAP_KTRACE_INSERT, fname, desc, ip, idx,
+ cnt, &tr1, cnt == 2 ? &tr2 : NULL, whichfork);
+}
+
+/*
+ * Add bmap trace entry after updating an extent list entry in place.
+ */
+STATIC void
+xfs_bmap_trace_post_update(
+ char *fname, /* function name */
+ char *desc, /* operation description */
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_extnum_t idx, /* index of entry updated */
+ int whichfork) /* data or attr fork */
+{
+ xfs_ifork_t *ifp; /* inode fork pointer */
+
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+ xfs_bmap_trace_addentry(XFS_BMAP_KTRACE_POST_UP, fname, desc, ip, idx,
+ 1, &ifp->if_u1.if_extents[idx], NULL, whichfork);
+}
+
+/*
+ * Add bmap trace entry prior to updating an extent list entry in place.
+ */
+STATIC void
+xfs_bmap_trace_pre_update(
+ char *fname, /* function name */
+ char *desc, /* operation description */
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_extnum_t idx, /* index of entry to be updated */
+ int whichfork) /* data or attr fork */
+{
+ xfs_ifork_t *ifp; /* inode fork pointer */
+
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+ xfs_bmap_trace_addentry(XFS_BMAP_KTRACE_PRE_UP, fname, desc, ip, idx, 1,
+ &ifp->if_u1.if_extents[idx], NULL, whichfork);
+}
+#endif /* XFS_BMAP_TRACE */
+
+/*
+ * Compute the worst-case number of indirect blocks that will be used
+ * for ip's delayed extent of length "len".
+ */
+STATIC xfs_filblks_t
+xfs_bmap_worst_indlen(
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_filblks_t len) /* delayed extent length */
+{
+ int level; /* btree level number */
+ int maxrecs; /* maximum record count at this level */
+ xfs_mount_t *mp; /* mount structure */
+ xfs_filblks_t rval; /* return value */
+
+ mp = ip->i_mount;
+ maxrecs = mp->m_bmap_dmxr[0];
+ for (level = 0, rval = 0;
+ level < XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK);
+ level++) {
+ len += maxrecs - 1;
+ do_div(len, maxrecs);
+ rval += len;
+ if (len == 1)
+ return rval + XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK) -
+ level - 1;
+ if (level == 0)
+ maxrecs = mp->m_bmap_dmxr[1];
+ }
+ return rval;
+}
+
+#if defined(XFS_RW_TRACE)
+STATIC void
+xfs_bunmap_trace(
+ xfs_inode_t *ip,
+ xfs_fileoff_t bno,
+ xfs_filblks_t len,
+ int flags,
+ inst_t *ra)
+{
+ if (ip->i_rwtrace == NULL)
+ return;
+ ktrace_enter(ip->i_rwtrace,
+ (void *)(__psint_t)XFS_BUNMAPI,
+ (void *)ip,
+ (void *)(__psint_t)((ip->i_d.di_size >> 32) & 0xffffffff),
+ (void *)(__psint_t)(ip->i_d.di_size & 0xffffffff),
+ (void *)(__psint_t)(((xfs_dfiloff_t)bno >> 32) & 0xffffffff),
+ (void *)(__psint_t)((xfs_dfiloff_t)bno & 0xffffffff),
+ (void *)(__psint_t)len,
+ (void *)(__psint_t)flags,
+ (void *)(unsigned long)current_cpu(),
+ (void *)ra,
+ (void *)0,
+ (void *)0,
+ (void *)0,
+ (void *)0,
+ (void *)0,
+ (void *)0);
+}
+#endif
+
+/*
+ * Convert inode from non-attributed to attributed.
+ * Must not be in a transaction, ip must not be locked.
+ */
+int /* error code */
+xfs_bmap_add_attrfork(
+ xfs_inode_t *ip, /* incore inode pointer */
+ int rsvd) /* OK to allocated reserved blocks in trans */
+{
+ int blks; /* space reservation */
+ int committed; /* xaction was committed */
+ int error; /* error return value */
+ xfs_fsblock_t firstblock; /* 1st block/ag allocated */
+ xfs_bmap_free_t flist; /* freed extent list */
+ int logflags; /* logging flags */
+ xfs_mount_t *mp; /* mount structure */
+ unsigned long s; /* spinlock spl value */
+ xfs_trans_t *tp; /* transaction pointer */
+
+ ASSERT(ip->i_df.if_ext_max ==
+ XFS_IFORK_DSIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t));
+ if (XFS_IFORK_Q(ip))
+ return 0;
+ mp = ip->i_mount;
+ ASSERT(!XFS_NOT_DQATTACHED(mp, ip));
+ tp = xfs_trans_alloc(mp, XFS_TRANS_ADDAFORK);
+ blks = XFS_ADDAFORK_SPACE_RES(mp);
+ if (rsvd)
+ tp->t_flags |= XFS_TRANS_RESERVE;
+ if ((error = xfs_trans_reserve(tp, blks, XFS_ADDAFORK_LOG_RES(mp), 0,
+ XFS_TRANS_PERM_LOG_RES, XFS_ADDAFORK_LOG_COUNT)))
+ goto error0;
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
+ error = XFS_TRANS_RESERVE_QUOTA_NBLKS(mp, tp, ip, blks, 0, rsvd ?
+ XFS_QMOPT_RES_REGBLKS | XFS_QMOPT_FORCE_RES :
+ XFS_QMOPT_RES_REGBLKS);
+ if (error) {
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES);
+ return error;
+ }
+ if (XFS_IFORK_Q(ip))
+ goto error1;
+ if (ip->i_d.di_aformat != XFS_DINODE_FMT_EXTENTS) {
+ /*
+ * For inodes coming from pre-6.2 filesystems.
+ */
+ ASSERT(ip->i_d.di_aformat == 0);
+ ip->i_d.di_aformat = XFS_DINODE_FMT_EXTENTS;
+ }
+ ASSERT(ip->i_d.di_anextents == 0);
+ VN_HOLD(XFS_ITOV(ip));
+ xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
+ xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+ switch (ip->i_d.di_format) {
+ case XFS_DINODE_FMT_DEV:
+ ip->i_d.di_forkoff = roundup(sizeof(xfs_dev_t), 8) >> 3;
+ break;
+ case XFS_DINODE_FMT_UUID:
+ ip->i_d.di_forkoff = roundup(sizeof(uuid_t), 8) >> 3;
+ break;
+ case XFS_DINODE_FMT_LOCAL:
+ case XFS_DINODE_FMT_EXTENTS:
+ case XFS_DINODE_FMT_BTREE:
+ ip->i_d.di_forkoff = mp->m_attroffset >> 3;
+ break;
+ default:
+ ASSERT(0);
+ error = XFS_ERROR(EINVAL);
+ goto error1;
+ }
+ ip->i_df.if_ext_max =
+ XFS_IFORK_DSIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t);
+ ASSERT(ip->i_afp == NULL);
+ ip->i_afp = kmem_zone_zalloc(xfs_ifork_zone, KM_SLEEP);
+ ip->i_afp->if_ext_max =
+ XFS_IFORK_ASIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t);
+ ip->i_afp->if_flags = XFS_IFEXTENTS;
+ logflags = 0;
+ XFS_BMAP_INIT(&flist, &firstblock);
+ switch (ip->i_d.di_format) {
+ case XFS_DINODE_FMT_LOCAL:
+ error = xfs_bmap_add_attrfork_local(tp, ip, &firstblock, &flist,
+ &logflags);
+ break;
+ case XFS_DINODE_FMT_EXTENTS:
+ error = xfs_bmap_add_attrfork_extents(tp, ip, &firstblock,
+ &flist, &logflags);
+ break;
+ case XFS_DINODE_FMT_BTREE:
+ error = xfs_bmap_add_attrfork_btree(tp, ip, &firstblock, &flist,
+ &logflags);
+ break;
+ default:
+ error = 0;
+ break;
+ }
+ if (logflags)
+ xfs_trans_log_inode(tp, ip, logflags);
+ if (error)
+ goto error2;
+ if (!XFS_SB_VERSION_HASATTR(&mp->m_sb)) {
+ s = XFS_SB_LOCK(mp);
+ if (!XFS_SB_VERSION_HASATTR(&mp->m_sb)) {
+ XFS_SB_VERSION_ADDATTR(&mp->m_sb);
+ XFS_SB_UNLOCK(mp, s);
+ xfs_mod_sb(tp, XFS_SB_VERSIONNUM);
+ } else
+ XFS_SB_UNLOCK(mp, s);
+ }
+ if ((error = xfs_bmap_finish(&tp, &flist, firstblock, &committed)))
+ goto error2;
+ error = xfs_trans_commit(tp, XFS_TRANS_PERM_LOG_RES, NULL);
+ ASSERT(ip->i_df.if_ext_max ==
+ XFS_IFORK_DSIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t));
+ return error;
+error2:
+ xfs_bmap_cancel(&flist);
+error1:
+ ASSERT(ismrlocked(&ip->i_lock,MR_UPDATE));
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+error0:
+ xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_ABORT);
+ ASSERT(ip->i_df.if_ext_max ==
+ XFS_IFORK_DSIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t));
+ return error;
+}
+
+/*
+ * Add the extent to the list of extents to be free at transaction end.
+ * The list is maintained sorted (by block number).
+ */
+/* ARGSUSED */
+void
+xfs_bmap_add_free(
+ xfs_fsblock_t bno, /* fs block number of extent */
+ xfs_filblks_t len, /* length of extent */
+ xfs_bmap_free_t *flist, /* list of extents */
+ xfs_mount_t *mp) /* mount point structure */
+{
+ xfs_bmap_free_item_t *cur; /* current (next) element */
+ xfs_bmap_free_item_t *new; /* new element */
+ xfs_bmap_free_item_t *prev; /* previous element */
+#ifdef DEBUG
+ xfs_agnumber_t agno;
+ xfs_agblock_t agbno;
+
+ ASSERT(bno != NULLFSBLOCK);
+ ASSERT(len > 0);
+ ASSERT(len <= MAXEXTLEN);
+ ASSERT(!ISNULLSTARTBLOCK(bno));
+ agno = XFS_FSB_TO_AGNO(mp, bno);
+ agbno = XFS_FSB_TO_AGBNO(mp, bno);
+ ASSERT(agno < mp->m_sb.sb_agcount);
+ ASSERT(agbno < mp->m_sb.sb_agblocks);
+ ASSERT(len < mp->m_sb.sb_agblocks);
+ ASSERT(agbno + len <= mp->m_sb.sb_agblocks);
+#endif
+ ASSERT(xfs_bmap_free_item_zone != NULL);
+ new = kmem_zone_alloc(xfs_bmap_free_item_zone, KM_SLEEP);
+ new->xbfi_startblock = bno;
+ new->xbfi_blockcount = (xfs_extlen_t)len;
+ for (prev = NULL, cur = flist->xbf_first;
+ cur != NULL;
+ prev = cur, cur = cur->xbfi_next) {
+ if (cur->xbfi_startblock >= bno)
+ break;
+ }
+ if (prev)
+ prev->xbfi_next = new;
+ else
+ flist->xbf_first = new;
+ new->xbfi_next = cur;
+ flist->xbf_count++;
+}
+
+/*
+ * Compute and fill in the value of the maximum depth of a bmap btree
+ * in this filesystem. Done once, during mount.
+ */
+void
+xfs_bmap_compute_maxlevels(
+ xfs_mount_t *mp, /* file system mount structure */
+ int whichfork) /* data or attr fork */
+{
+ int level; /* btree level */
+ uint maxblocks; /* max blocks at this level */
+ uint maxleafents; /* max leaf entries possible */
+ int maxrootrecs; /* max records in root block */
+ int minleafrecs; /* min records in leaf block */
+ int minnoderecs; /* min records in node block */
+ int sz; /* root block size */
+
+ /*
+ * The maximum number of extents in a file, hence the maximum
+ * number of leaf entries, is controlled by the type of di_nextents
+ * (a signed 32-bit number, xfs_extnum_t), or by di_anextents
+ * (a signed 16-bit number, xfs_aextnum_t).
+ */
+ maxleafents = (whichfork == XFS_DATA_FORK) ? MAXEXTNUM : MAXAEXTNUM;
+ minleafrecs = mp->m_bmap_dmnr[0];
+ minnoderecs = mp->m_bmap_dmnr[1];
+ sz = (whichfork == XFS_DATA_FORK) ?
+ mp->m_attroffset :
+ mp->m_sb.sb_inodesize - mp->m_attroffset;
+ maxrootrecs = (int)XFS_BTREE_BLOCK_MAXRECS(sz, xfs_bmdr, 0);
+ maxblocks = (maxleafents + minleafrecs - 1) / minleafrecs;
+ for (level = 1; maxblocks > 1; level++) {
+ if (maxblocks <= maxrootrecs)
+ maxblocks = 1;
+ else
+ maxblocks = (maxblocks + minnoderecs - 1) / minnoderecs;
+ }
+ mp->m_bm_maxlevels[whichfork] = level;
+}
+
+/*
+ * Routine to be called at transaction's end by xfs_bmapi, xfs_bunmapi
+ * caller. Frees all the extents that need freeing, which must be done
+ * last due to locking considerations. We never free any extents in
+ * the first transaction. This is to allow the caller to make the first
+ * transaction a synchronous one so that the pointers to the data being
+ * broken in this transaction will be permanent before the data is actually
+ * freed. This is necessary to prevent blocks from being reallocated
+ * and written to before the free and reallocation are actually permanent.
+ * We do not just make the first transaction synchronous here, because
+ * there are more efficient ways to gain the same protection in some cases
+ * (see the file truncation code).
+ *
+ * Return 1 if the given transaction was committed and a new one
+ * started, and 0 otherwise in the committed parameter.
+ */
+/*ARGSUSED*/
+int /* error */
+xfs_bmap_finish(
+ xfs_trans_t **tp, /* transaction pointer addr */
+ xfs_bmap_free_t *flist, /* i/o: list extents to free */
+ xfs_fsblock_t firstblock, /* controlled ag for allocs */
+ int *committed) /* xact committed or not */
+{
+ xfs_efd_log_item_t *efd; /* extent free data */
+ xfs_efi_log_item_t *efi; /* extent free intention */
+ int error; /* error return value */
+ xfs_bmap_free_item_t *free; /* free extent list item */
+ unsigned int logres; /* new log reservation */
+ unsigned int logcount; /* new log count */
+ xfs_mount_t *mp; /* filesystem mount structure */
+ xfs_bmap_free_item_t *next; /* next item on free list */
+ xfs_trans_t *ntp; /* new transaction pointer */
+
+ ASSERT((*tp)->t_flags & XFS_TRANS_PERM_LOG_RES);
+ if (flist->xbf_count == 0) {
+ *committed = 0;
+ return 0;
+ }
+ ntp = *tp;
+ efi = xfs_trans_get_efi(ntp, flist->xbf_count);
+ for (free = flist->xbf_first; free; free = free->xbfi_next)
+ xfs_trans_log_efi_extent(ntp, efi, free->xbfi_startblock,
+ free->xbfi_blockcount);
+ logres = ntp->t_log_res;
+ logcount = ntp->t_log_count;
+ ntp = xfs_trans_dup(*tp);
+ error = xfs_trans_commit(*tp, 0, NULL);
+ *tp = ntp;
+ *committed = 1;
+ /*
+ * We have a new transaction, so we should return committed=1,
+ * even though we're returning an error.
+ */
+ if (error) {
+ return error;
+ }
+ if ((error = xfs_trans_reserve(ntp, 0, logres, 0, XFS_TRANS_PERM_LOG_RES,
+ logcount)))
+ return error;
+ efd = xfs_trans_get_efd(ntp, efi, flist->xbf_count);
+ for (free = flist->xbf_first; free != NULL; free = next) {
+ next = free->xbfi_next;
+ if ((error = xfs_free_extent(ntp, free->xbfi_startblock,
+ free->xbfi_blockcount))) {
+ /*
+ * The bmap free list will be cleaned up at a
+ * higher level. The EFI will be canceled when
+ * this transaction is aborted.
+ * Need to force shutdown here to make sure it
+ * happens, since this transaction may not be
+ * dirty yet.
+ */
+ mp = ntp->t_mountp;
+ if (!XFS_FORCED_SHUTDOWN(mp))
+ xfs_force_shutdown(mp,
+ (error == EFSCORRUPTED) ?
+ XFS_CORRUPT_INCORE :
+ XFS_METADATA_IO_ERROR);
+ return error;
+ }
+ xfs_trans_log_efd_extent(ntp, efd, free->xbfi_startblock,
+ free->xbfi_blockcount);
+ xfs_bmap_del_free(flist, NULL, free);
+ }
+ return 0;
+}
+
+/*
+ * Free up any items left in the list.
+ */
+void
+xfs_bmap_cancel(
+ xfs_bmap_free_t *flist) /* list of bmap_free_items */
+{
+ xfs_bmap_free_item_t *free; /* free list item */
+ xfs_bmap_free_item_t *next;
+
+ if (flist->xbf_count == 0)
+ return;
+ ASSERT(flist->xbf_first != NULL);
+ for (free = flist->xbf_first; free; free = next) {
+ next = free->xbfi_next;
+ xfs_bmap_del_free(flist, NULL, free);
+ }
+ ASSERT(flist->xbf_count == 0);
+}
+
+/*
+ * Returns the file-relative block number of the first unused block(s)
+ * in the file with at least "len" logically contiguous blocks free.
+ * This is the lowest-address hole if the file has holes, else the first block
+ * past the end of file.
+ * Return 0 if the file is currently local (in-inode).
+ */
+int /* error */
+xfs_bmap_first_unused(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_inode_t *ip, /* incore inode */
+ xfs_extlen_t len, /* size of hole to find */
+ xfs_fileoff_t *first_unused, /* unused block */
+ int whichfork) /* data or attr fork */
+{
+ xfs_bmbt_rec_t *base; /* base of extent array */
+ xfs_bmbt_rec_t *ep; /* pointer to an extent entry */
+ int error; /* error return value */
+ xfs_ifork_t *ifp; /* inode fork pointer */
+ xfs_fileoff_t lastaddr; /* last block number seen */
+ xfs_fileoff_t lowest; /* lowest useful block */
+ xfs_fileoff_t max; /* starting useful block */
+ xfs_fileoff_t off; /* offset for this block */
+ xfs_extnum_t nextents; /* number of extent entries */
+
+ ASSERT(XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_BTREE ||
+ XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_EXTENTS ||
+ XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_LOCAL);
+ if (XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_LOCAL) {
+ *first_unused = 0;
+ return 0;
+ }
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+ if (!(ifp->if_flags & XFS_IFEXTENTS) &&
+ (error = xfs_iread_extents(tp, ip, whichfork)))
+ return error;
+ lowest = *first_unused;
+ nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
+ base = &ifp->if_u1.if_extents[0];
+ for (lastaddr = 0, max = lowest, ep = base;
+ ep < &base[nextents];
+ ep++) {
+ off = xfs_bmbt_get_startoff(ep);
+ /*
+ * See if the hole before this extent will work.
+ */
+ if (off >= lowest + len && off - max >= len) {
+ *first_unused = max;
+ return 0;
+ }
+ lastaddr = off + xfs_bmbt_get_blockcount(ep);
+ max = XFS_FILEOFF_MAX(lastaddr, lowest);
+ }
+ *first_unused = max;
+ return 0;
+}
+
+/*
+ * Returns the file-relative block number of the last block + 1 before
+ * last_block (input value) in the file.
+ * This is not based on i_size, it is based on the extent list.
+ * Returns 0 for local files, as they do not have an extent list.
+ */
+int /* error */
+xfs_bmap_last_before(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_inode_t *ip, /* incore inode */
+ xfs_fileoff_t *last_block, /* last block */
+ int whichfork) /* data or attr fork */
+{
+ xfs_fileoff_t bno; /* input file offset */
+ int eof; /* hit end of file */
+ xfs_bmbt_rec_t *ep; /* pointer to last extent */
+ int error; /* error return value */
+ xfs_bmbt_irec_t got; /* current extent value */
+ xfs_ifork_t *ifp; /* inode fork pointer */
+ xfs_extnum_t lastx; /* last extent used */
+ xfs_bmbt_irec_t prev; /* previous extent value */
+
+ if (XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE &&
+ XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS &&
+ XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_LOCAL)
+ return XFS_ERROR(EIO);
+ if (XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_LOCAL) {
+ *last_block = 0;
+ return 0;
+ }
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+ if (!(ifp->if_flags & XFS_IFEXTENTS) &&
+ (error = xfs_iread_extents(tp, ip, whichfork)))
+ return error;
+ bno = *last_block - 1;
+ ep = xfs_bmap_search_extents(ip, bno, whichfork, &eof, &lastx, &got,
+ &prev);
+ if (eof || xfs_bmbt_get_startoff(ep) > bno) {
+ if (prev.br_startoff == NULLFILEOFF)
+ *last_block = 0;
+ else
+ *last_block = prev.br_startoff + prev.br_blockcount;
+ }
+ /*
+ * Otherwise *last_block is already the right answer.
+ */
+ return 0;
+}
+
+/*
+ * Returns the file-relative block number of the first block past eof in
+ * the file. This is not based on i_size, it is based on the extent list.
+ * Returns 0 for local files, as they do not have an extent list.
+ */
+int /* error */
+xfs_bmap_last_offset(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_inode_t *ip, /* incore inode */
+ xfs_fileoff_t *last_block, /* last block */
+ int whichfork) /* data or attr fork */
+{
+ xfs_bmbt_rec_t *base; /* base of extent array */
+ xfs_bmbt_rec_t *ep; /* pointer to last extent */
+ int error; /* error return value */
+ xfs_ifork_t *ifp; /* inode fork pointer */
+ xfs_extnum_t nextents; /* number of extent entries */
+
+ if (XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE &&
+ XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS &&
+ XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_LOCAL)
+ return XFS_ERROR(EIO);
+ if (XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_LOCAL) {
+ *last_block = 0;
+ return 0;
+ }
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+ if (!(ifp->if_flags & XFS_IFEXTENTS) &&
+ (error = xfs_iread_extents(tp, ip, whichfork)))
+ return error;
+ nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
+ if (!nextents) {
+ *last_block = 0;
+ return 0;
+ }
+ base = &ifp->if_u1.if_extents[0];
+ ASSERT(base != NULL);
+ ep = &base[nextents - 1];
+ *last_block = xfs_bmbt_get_startoff(ep) + xfs_bmbt_get_blockcount(ep);
+ return 0;
+}
+
+/*
+ * Returns whether the selected fork of the inode has exactly one
+ * block or not. For the data fork we check this matches di_size,
+ * implying the file's range is 0..bsize-1.
+ */
+int /* 1=>1 block, 0=>otherwise */
+xfs_bmap_one_block(
+ xfs_inode_t *ip, /* incore inode */
+ int whichfork) /* data or attr fork */
+{
+ xfs_bmbt_rec_t *ep; /* ptr to fork's extent */
+ xfs_ifork_t *ifp; /* inode fork pointer */
+ int rval; /* return value */
+ xfs_bmbt_irec_t s; /* internal version of extent */
+
+#ifndef DEBUG
+ if (whichfork == XFS_DATA_FORK)
+ return ip->i_d.di_size == ip->i_mount->m_sb.sb_blocksize;
+#endif /* !DEBUG */
+ if (XFS_IFORK_NEXTENTS(ip, whichfork) != 1)
+ return 0;
+ if (XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS)
+ return 0;
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+ ASSERT(ifp->if_flags & XFS_IFEXTENTS);
+ ep = ifp->if_u1.if_extents;
+ xfs_bmbt_get_all(ep, &s);
+ rval = s.br_startoff == 0 && s.br_blockcount == 1;
+ if (rval && whichfork == XFS_DATA_FORK)
+ ASSERT(ip->i_d.di_size == ip->i_mount->m_sb.sb_blocksize);
+ return rval;
+}
+
+/*
+ * Read in the extents to if_extents.
+ * All inode fields are set up by caller, we just traverse the btree
+ * and copy the records in. If the file system cannot contain unwritten
+ * extents, the records are checked for no "state" flags.
+ */
+int /* error */
+xfs_bmap_read_extents(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_inode_t *ip, /* incore inode */
+ int whichfork) /* data or attr fork */
+{
+ xfs_bmbt_block_t *block; /* current btree block */
+ xfs_fsblock_t bno; /* block # of "block" */
+ xfs_buf_t *bp; /* buffer for "block" */
+ int error; /* error return value */
+ xfs_exntfmt_t exntf; /* XFS_EXTFMT_NOSTATE, if checking */
+#ifdef XFS_BMAP_TRACE
+ static char fname[] = "xfs_bmap_read_extents";
+#endif
+ xfs_extnum_t i, j; /* index into the extents list */
+ xfs_ifork_t *ifp; /* fork structure */
+ int level; /* btree level, for checking */
+ xfs_mount_t *mp; /* file system mount structure */
+ xfs_bmbt_ptr_t *pp; /* pointer to block address */
+ /* REFERENCED */
+ xfs_extnum_t room; /* number of entries there's room for */
+ xfs_bmbt_rec_t *trp; /* target record pointer */
+
+ bno = NULLFSBLOCK;
+ mp = ip->i_mount;
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+ exntf = (whichfork != XFS_DATA_FORK) ? XFS_EXTFMT_NOSTATE :
+ XFS_EXTFMT_INODE(ip);
+ block = ifp->if_broot;
+ /*
+ * Root level must use BMAP_BROOT_PTR_ADDR macro to get ptr out.
+ */
+ ASSERT(INT_GET(block->bb_level, ARCH_CONVERT) > 0);
+ level = INT_GET(block->bb_level, ARCH_CONVERT);
+ pp = XFS_BMAP_BROOT_PTR_ADDR(block, 1, ifp->if_broot_bytes);
+ ASSERT(INT_GET(*pp, ARCH_CONVERT) != NULLDFSBNO);
+ ASSERT(XFS_FSB_TO_AGNO(mp, INT_GET(*pp, ARCH_CONVERT)) < mp->m_sb.sb_agcount);
+ ASSERT(XFS_FSB_TO_AGBNO(mp, INT_GET(*pp, ARCH_CONVERT)) < mp->m_sb.sb_agblocks);
+ bno = INT_GET(*pp, ARCH_CONVERT);
+ /*
+ * Go down the tree until leaf level is reached, following the first
+ * pointer (leftmost) at each level.
+ */
+ while (level-- > 0) {
+ if ((error = xfs_btree_read_bufl(mp, tp, bno, 0, &bp,
+ XFS_BMAP_BTREE_REF)))
+ return error;
+ block = XFS_BUF_TO_BMBT_BLOCK(bp);
+ XFS_WANT_CORRUPTED_GOTO(
+ XFS_BMAP_SANITY_CHECK(mp, block, level),
+ error0);
+ if (level == 0)
+ break;
+ pp = XFS_BTREE_PTR_ADDR(mp->m_sb.sb_blocksize, xfs_bmbt, block,
+ 1, mp->m_bmap_dmxr[1]);
+ XFS_WANT_CORRUPTED_GOTO(
+ XFS_FSB_SANITY_CHECK(mp, INT_GET(*pp, ARCH_CONVERT)),
+ error0);
+ bno = INT_GET(*pp, ARCH_CONVERT);
+ xfs_trans_brelse(tp, bp);
+ }
+ /*
+ * Here with bp and block set to the leftmost leaf node in the tree.
+ */
+ room = ifp->if_bytes / (uint)sizeof(*trp);
+ trp = ifp->if_u1.if_extents;
+ i = 0;
+ /*
+ * Loop over all leaf nodes. Copy information to the extent list.
+ */
+ for (;;) {
+ xfs_bmbt_rec_t *frp, *temp;
+ xfs_fsblock_t nextbno;
+ xfs_extnum_t num_recs;
+
+
+ num_recs = INT_GET(block->bb_numrecs, ARCH_CONVERT);
+ if (unlikely(i + num_recs > room)) {
+ ASSERT(i + num_recs <= room);
+ xfs_fs_cmn_err(CE_WARN, ip->i_mount,
+ "corrupt dinode %Lu, (btree extents). Unmount and run xfs_repair.",
+ (unsigned long long) ip->i_ino);
+ XFS_ERROR_REPORT("xfs_bmap_read_extents(1)",
+ XFS_ERRLEVEL_LOW,
+ ip->i_mount);
+ goto error0;
+ }
+ XFS_WANT_CORRUPTED_GOTO(
+ XFS_BMAP_SANITY_CHECK(mp, block, 0),
+ error0);
+ /*
+ * Read-ahead the next leaf block, if any.
+ */
+ nextbno = INT_GET(block->bb_rightsib, ARCH_CONVERT);
+ if (nextbno != NULLFSBLOCK)
+ xfs_btree_reada_bufl(mp, nextbno, 1);
+ /*
+ * Copy records into the extent list.
+ */
+ frp = XFS_BTREE_REC_ADDR(mp->m_sb.sb_blocksize, xfs_bmbt,
+ block, 1, mp->m_bmap_dmxr[0]);
+ temp = trp;
+ for (j = 0; j < num_recs; j++, frp++, trp++) {
+ trp->l0 = INT_GET(frp->l0, ARCH_CONVERT);
+ trp->l1 = INT_GET(frp->l1, ARCH_CONVERT);
+ }
+ if (exntf == XFS_EXTFMT_NOSTATE) {
+ /*
+ * Check all attribute bmap btree records and
+ * any "older" data bmap btree records for a
+ * set bit in the "extent flag" position.
+ */
+ if (unlikely(xfs_check_nostate_extents(temp, num_recs))) {
+ XFS_ERROR_REPORT("xfs_bmap_read_extents(2)",
+ XFS_ERRLEVEL_LOW,
+ ip->i_mount);
+ goto error0;
+ }
+ }
+ i += num_recs;
+ xfs_trans_brelse(tp, bp);
+ bno = nextbno;
+ /*
+ * If we've reached the end, stop.
+ */
+ if (bno == NULLFSBLOCK)
+ break;
+ if ((error = xfs_btree_read_bufl(mp, tp, bno, 0, &bp,
+ XFS_BMAP_BTREE_REF)))
+ return error;
+ block = XFS_BUF_TO_BMBT_BLOCK(bp);
+ }
+ ASSERT(i == ifp->if_bytes / (uint)sizeof(*trp));
+ ASSERT(i == XFS_IFORK_NEXTENTS(ip, whichfork));
+ xfs_bmap_trace_exlist(fname, ip, i, whichfork);
+ return 0;
+error0:
+ xfs_trans_brelse(tp, bp);
+ return XFS_ERROR(EFSCORRUPTED);
+}
+
+#ifdef XFS_BMAP_TRACE
+/*
+ * Add bmap trace insert entries for all the contents of the extent list.
+ */
+void
+xfs_bmap_trace_exlist(
+ char *fname, /* function name */
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_extnum_t cnt, /* count of entries in the list */
+ int whichfork) /* data or attr fork */
+{
+ xfs_bmbt_rec_t *base; /* base of extent list */
+ xfs_bmbt_rec_t *ep; /* current entry in extent list */
+ xfs_extnum_t idx; /* extent list entry number */
+ xfs_ifork_t *ifp; /* inode fork pointer */
+ xfs_bmbt_irec_t s; /* extent list record */
+
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+ ASSERT(cnt == ifp->if_bytes / (uint)sizeof(*base));
+ base = ifp->if_u1.if_extents;
+ for (idx = 0, ep = base; idx < cnt; idx++, ep++) {
+ xfs_bmbt_get_all(ep, &s);
+ xfs_bmap_trace_insert(fname, "exlist", ip, idx, 1, &s, NULL,
+ whichfork);
+ }
+}
+#endif
+
+#ifdef DEBUG
+/*
+ * Validate that the bmbt_irecs being returned from bmapi are valid
+ * given the callers original parameters. Specifically check the
+ * ranges of the returned irecs to ensure that they only extent beyond
+ * the given parameters if the XFS_BMAPI_ENTIRE flag was set.
+ */
+STATIC void
+xfs_bmap_validate_ret(
+ xfs_fileoff_t bno,
+ xfs_filblks_t len,
+ int flags,
+ xfs_bmbt_irec_t *mval,
+ int nmap,
+ int ret_nmap)
+{
+ int i; /* index to map values */
+
+ ASSERT(ret_nmap <= nmap);
+
+ for (i = 0; i < ret_nmap; i++) {
+ ASSERT(mval[i].br_blockcount > 0);
+ if (!(flags & XFS_BMAPI_ENTIRE)) {
+ ASSERT(mval[i].br_startoff >= bno);
+ ASSERT(mval[i].br_blockcount <= len);
+ ASSERT(mval[i].br_startoff + mval[i].br_blockcount <=
+ bno + len);
+ } else {
+ ASSERT(mval[i].br_startoff < bno + len);
+ ASSERT(mval[i].br_startoff + mval[i].br_blockcount >
+ bno);
+ }
+ ASSERT(i == 0 ||
+ mval[i - 1].br_startoff + mval[i - 1].br_blockcount ==
+ mval[i].br_startoff);
+ if ((flags & XFS_BMAPI_WRITE) && !(flags & XFS_BMAPI_DELAY))
+ ASSERT(mval[i].br_startblock != DELAYSTARTBLOCK &&
+ mval[i].br_startblock != HOLESTARTBLOCK);
+ ASSERT(mval[i].br_state == XFS_EXT_NORM ||
+ mval[i].br_state == XFS_EXT_UNWRITTEN);
+ }
+}
+#endif /* DEBUG */
+
+
+/*
+ * Map file blocks to filesystem blocks.
+ * File range is given by the bno/len pair.
+ * Adds blocks to file if a write ("flags & XFS_BMAPI_WRITE" set)
+ * into a hole or past eof.
+ * Only allocates blocks from a single allocation group,
+ * to avoid locking problems.
+ * The returned value in "firstblock" from the first call in a transaction
+ * must be remembered and presented to subsequent calls in "firstblock".
+ * An upper bound for the number of blocks to be allocated is supplied to
+ * the first call in "total"; if no allocation group has that many free
+ * blocks then the call will fail (return NULLFSBLOCK in "firstblock").
+ */
+int /* error */
+xfs_bmapi(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_inode_t *ip, /* incore inode */
+ xfs_fileoff_t bno, /* starting file offs. mapped */
+ xfs_filblks_t len, /* length to map in file */
+ int flags, /* XFS_BMAPI_... */
+ xfs_fsblock_t *firstblock, /* first allocated block
+ controls a.g. for allocs */
+ xfs_extlen_t total, /* total blocks needed */
+ xfs_bmbt_irec_t *mval, /* output: map values */
+ int *nmap, /* i/o: mval size/count */
+ xfs_bmap_free_t *flist) /* i/o: list extents to free */
+{
+ xfs_fsblock_t abno; /* allocated block number */
+ xfs_extlen_t alen; /* allocated extent length */
+ xfs_fileoff_t aoff; /* allocated file offset */
+ xfs_bmalloca_t bma; /* args for xfs_bmap_alloc */
+ char contig; /* allocation must be one extent */
+ xfs_btree_cur_t *cur; /* bmap btree cursor */
+ char delay; /* this request is for delayed alloc */
+ xfs_fileoff_t end; /* end of mapped file region */
+ int eof; /* we've hit the end of extent list */
+ xfs_bmbt_rec_t *ep; /* extent list entry pointer */
+ int error; /* error return */
+ char exact; /* don't do all of wasdelayed extent */
+ xfs_bmbt_irec_t got; /* current extent list record */
+ xfs_ifork_t *ifp; /* inode fork pointer */
+ xfs_extlen_t indlen; /* indirect blocks length */
+ char inhole; /* current location is hole in file */
+ xfs_extnum_t lastx; /* last useful extent number */
+ int logflags; /* flags for transaction logging */
+ xfs_extlen_t minleft; /* min blocks left after allocation */
+ xfs_extlen_t minlen; /* min allocation size */
+ xfs_mount_t *mp; /* xfs mount structure */
+ int n; /* current extent index */
+ int nallocs; /* number of extents alloc\'d */
+ xfs_extnum_t nextents; /* number of extents in file */
+ xfs_fileoff_t obno; /* old block number (offset) */
+ xfs_bmbt_irec_t prev; /* previous extent list record */
+ char stateless; /* ignore state flag set */
+ int tmp_logflags; /* temp flags holder */
+ char trim; /* output trimmed to match range */
+ char userdata; /* allocating non-metadata */
+ char wasdelay; /* old extent was delayed */
+ int whichfork; /* data or attr fork */
+ char wr; /* this is a write request */
+ char rsvd; /* OK to allocate reserved blocks */
+#ifdef DEBUG
+ xfs_fileoff_t orig_bno; /* original block number value */
+ int orig_flags; /* original flags arg value */
+ xfs_filblks_t orig_len; /* original value of len arg */
+ xfs_bmbt_irec_t *orig_mval; /* original value of mval */
+ int orig_nmap; /* original value of *nmap */
+
+ orig_bno = bno;
+ orig_len = len;
+ orig_flags = flags;
+ orig_mval = mval;
+ orig_nmap = *nmap;
+#endif
+ ASSERT(*nmap >= 1);
+ ASSERT(*nmap <= XFS_BMAP_MAX_NMAP || !(flags & XFS_BMAPI_WRITE));
+ whichfork = (flags & XFS_BMAPI_ATTRFORK) ?
+ XFS_ATTR_FORK : XFS_DATA_FORK;
+ mp = ip->i_mount;
+ if (unlikely(XFS_TEST_ERROR(
+ (XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS &&
+ XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE &&
+ XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_LOCAL),
+ mp, XFS_ERRTAG_BMAPIFORMAT, XFS_RANDOM_BMAPIFORMAT))) {
+ XFS_ERROR_REPORT("xfs_bmapi", XFS_ERRLEVEL_LOW, mp);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ if (XFS_FORCED_SHUTDOWN(mp))
+ return XFS_ERROR(EIO);
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+ ASSERT(ifp->if_ext_max ==
+ XFS_IFORK_SIZE(ip, whichfork) / (uint)sizeof(xfs_bmbt_rec_t));
+ if ((wr = (flags & XFS_BMAPI_WRITE)) != 0)
+ XFS_STATS_INC(xs_blk_mapw);
+ else
+ XFS_STATS_INC(xs_blk_mapr);
+ delay = (flags & XFS_BMAPI_DELAY) != 0;
+ trim = (flags & XFS_BMAPI_ENTIRE) == 0;
+ userdata = (flags & XFS_BMAPI_METADATA) == 0;
+ exact = (flags & XFS_BMAPI_EXACT) != 0;
+ rsvd = (flags & XFS_BMAPI_RSVBLOCKS) != 0;
+ contig = (flags & XFS_BMAPI_CONTIG) != 0;
+ /*
+ * stateless is used to combine extents which
+ * differ only due to the state of the extents.
+ * This technique is used from xfs_getbmap()
+ * when the caller does not wish to see the
+ * separation (which is the default).
+ *
+ * This technique is also used when writing a
+ * buffer which has been partially written,
+ * (usually by being flushed during a chunkread),
+ * to ensure one write takes place. This also
+ * prevents a change in the xfs inode extents at
+ * this time, intentionally. This change occurs
+ * on completion of the write operation, in
+ * xfs_strat_comp(), where the xfs_bmapi() call
+ * is transactioned, and the extents combined.
+ */
+ stateless = (flags & XFS_BMAPI_IGSTATE) != 0;
+ if (stateless && wr) /* if writing unwritten space, no */
+ wr = 0; /* allocations are allowed */
+ ASSERT(wr || !delay);
+ logflags = 0;
+ nallocs = 0;
+ cur = NULL;
+ if (XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_LOCAL) {
+ ASSERT(wr && tp);
+ if ((error = xfs_bmap_local_to_extents(tp, ip,
+ firstblock, total, &logflags, whichfork)))
+ goto error0;
+ }
+ if (wr && *firstblock == NULLFSBLOCK) {
+ if (XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_BTREE)
+ minleft = INT_GET(ifp->if_broot->bb_level, ARCH_CONVERT) + 1;
+ else
+ minleft = 1;
+ } else
+ minleft = 0;
+ if (!(ifp->if_flags & XFS_IFEXTENTS) &&
+ (error = xfs_iread_extents(tp, ip, whichfork)))
+ goto error0;
+ ep = xfs_bmap_search_extents(ip, bno, whichfork, &eof, &lastx, &got,
+ &prev);
+ nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
+ n = 0;
+ end = bno + len;
+ obno = bno;
+ bma.ip = NULL;
+ while (bno < end && n < *nmap) {
+ /*
+ * Reading past eof, act as though there's a hole
+ * up to end.
+ */
+ if (eof && !wr)
+ got.br_startoff = end;
+ inhole = eof || got.br_startoff > bno;
+ wasdelay = wr && !inhole && !delay &&
+ ISNULLSTARTBLOCK(got.br_startblock);
+ /*
+ * First, deal with the hole before the allocated space
+ * that we found, if any.
+ */
+ if (wr && (inhole || wasdelay)) {
+ /*
+ * For the wasdelay case, we could also just
+ * allocate the stuff asked for in this bmap call
+ * but that wouldn't be as good.
+ */
+ if (wasdelay && !exact) {
+ alen = (xfs_extlen_t)got.br_blockcount;
+ aoff = got.br_startoff;
+ if (lastx != NULLEXTNUM && lastx) {
+ ep = &ifp->if_u1.if_extents[lastx - 1];
+ xfs_bmbt_get_all(ep, &prev);
+ }
+ } else if (wasdelay) {
+ alen = (xfs_extlen_t)
+ XFS_FILBLKS_MIN(len,
+ (got.br_startoff +
+ got.br_blockcount) - bno);
+ aoff = bno;
+ } else {
+ alen = (xfs_extlen_t)
+ XFS_FILBLKS_MIN(len, MAXEXTLEN);
+ if (!eof)
+ alen = (xfs_extlen_t)
+ XFS_FILBLKS_MIN(alen,
+ got.br_startoff - bno);
+ aoff = bno;
+ }
+ minlen = contig ? alen : 1;
+ if (delay) {
+ indlen = (xfs_extlen_t)
+ xfs_bmap_worst_indlen(ip, alen);
+ ASSERT(indlen > 0);
+ /*
+ * Make a transaction-less quota reservation for
+ * delayed allocation blocks. This number gets
+ * adjusted later.
+ * We return EDQUOT if we haven't allocated
+ * blks already inside this loop;
+ */
+ if (XFS_TRANS_RESERVE_BLKQUOTA(
+ mp, NULL, ip, (long)alen)) {
+ if (n == 0) {
+ *nmap = 0;
+ ASSERT(cur == NULL);
+ return XFS_ERROR(EDQUOT);
+ }
+ break;
+ }
+
+ /*
+ * Split changing sb for alen and indlen since
+ * they could be coming from different places.
+ */
+ if (ip->i_d.di_flags & XFS_DIFLAG_REALTIME) {
+ xfs_extlen_t extsz;
+ xfs_extlen_t ralen;
+ if (!(extsz = ip->i_d.di_extsize))
+ extsz = mp->m_sb.sb_rextsize;
+ ralen = roundup(alen, extsz);
+ ralen = ralen / mp->m_sb.sb_rextsize;
+ if (xfs_mod_incore_sb(mp,
+ XFS_SBS_FREXTENTS,
+ -(ralen), rsvd)) {
+ if (XFS_IS_QUOTA_ON(ip->i_mount))
+ XFS_TRANS_UNRESERVE_BLKQUOTA(
+ mp, NULL, ip,
+ (long)alen);
+ break;
+ }
+ } else {
+ if (xfs_mod_incore_sb(mp,
+ XFS_SBS_FDBLOCKS,
+ -(alen), rsvd)) {
+ if (XFS_IS_QUOTA_ON(ip->i_mount))
+ XFS_TRANS_UNRESERVE_BLKQUOTA(
+ mp, NULL, ip,
+ (long)alen);
+ break;
+ }
+ }
+
+ if (xfs_mod_incore_sb(mp, XFS_SBS_FDBLOCKS,
+ -(indlen), rsvd)) {
+ XFS_TRANS_UNRESERVE_BLKQUOTA(
+ mp, NULL, ip, (long)alen);
+ break;
+ }
+ ip->i_delayed_blks += alen;
+ abno = NULLSTARTBLOCK(indlen);
+ } else {
+ /*
+ * If first time, allocate and fill in
+ * once-only bma fields.
+ */
+ if (bma.ip == NULL) {
+ bma.tp = tp;
+ bma.ip = ip;
+ bma.prevp = &prev;
+ bma.gotp = &got;
+ bma.total = total;
+ bma.userdata = 0;
+ }
+ /* Indicate if this is the first user data
+ * in the file, or just any user data.
+ */
+ if (userdata) {
+ bma.userdata = (aoff == 0) ?
+ XFS_ALLOC_INITIAL_USER_DATA :
+ XFS_ALLOC_USERDATA;
+ }
+ /*
+ * Fill in changeable bma fields.
+ */
+ bma.eof = eof;
+ bma.firstblock = *firstblock;
+ bma.alen = alen;
+ bma.off = aoff;
+ bma.wasdel = wasdelay;
+ bma.minlen = minlen;
+ bma.low = flist->xbf_low;
+ bma.minleft = minleft;
+ /*
+ * Only want to do the alignment at the
+ * eof if it is userdata and allocation length
+ * is larger than a stripe unit.
+ */
+ if (mp->m_dalign && alen >= mp->m_dalign &&
+ userdata && whichfork == XFS_DATA_FORK) {
+ if ((error = xfs_bmap_isaeof(ip, aoff,
+ whichfork, &bma.aeof)))
+ goto error0;
+ } else
+ bma.aeof = 0;
+ /*
+ * Call allocator.
+ */
+ if ((error = xfs_bmap_alloc(&bma)))
+ goto error0;
+ /*
+ * Copy out result fields.
+ */
+ abno = bma.rval;
+ if ((flist->xbf_low = bma.low))
+ minleft = 0;
+ alen = bma.alen;
+ aoff = bma.off;
+ ASSERT(*firstblock == NULLFSBLOCK ||
+ XFS_FSB_TO_AGNO(mp, *firstblock) ==
+ XFS_FSB_TO_AGNO(mp, bma.firstblock) ||
+ (flist->xbf_low &&
+ XFS_FSB_TO_AGNO(mp, *firstblock) <
+ XFS_FSB_TO_AGNO(mp, bma.firstblock)));
+ *firstblock = bma.firstblock;
+ if (cur)
+ cur->bc_private.b.firstblock =
+ *firstblock;
+ if (abno == NULLFSBLOCK)
+ break;
+ if ((ifp->if_flags & XFS_IFBROOT) && !cur) {
+ cur = xfs_btree_init_cursor(mp,
+ tp, NULL, 0, XFS_BTNUM_BMAP,
+ ip, whichfork);
+ cur->bc_private.b.firstblock =
+ *firstblock;
+ cur->bc_private.b.flist = flist;
+ }
+ /*
+ * Bump the number of extents we've allocated
+ * in this call.
+ */
+ nallocs++;
+ }
+ if (cur)
+ cur->bc_private.b.flags =
+ wasdelay ? XFS_BTCUR_BPRV_WASDEL : 0;
+ got.br_startoff = aoff;
+ got.br_startblock = abno;
+ got.br_blockcount = alen;
+ got.br_state = XFS_EXT_NORM; /* assume normal */
+ /*
+ * Determine state of extent, and the filesystem.
+ * A wasdelay extent has been initialized, so
+ * shouldn't be flagged as unwritten.
+ */
+ if (wr && XFS_SB_VERSION_HASEXTFLGBIT(&mp->m_sb)) {
+ if (!wasdelay && (flags & XFS_BMAPI_PREALLOC))
+ got.br_state = XFS_EXT_UNWRITTEN;
+ }
+ error = xfs_bmap_add_extent(ip, lastx, &cur, &got,
+ firstblock, flist, &tmp_logflags, whichfork,
+ rsvd);
+ logflags |= tmp_logflags;
+ if (error)
+ goto error0;
+ lastx = ifp->if_lastex;
+ ep = &ifp->if_u1.if_extents[lastx];
+ nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
+ xfs_bmbt_get_all(ep, &got);
+ ASSERT(got.br_startoff <= aoff);
+ ASSERT(got.br_startoff + got.br_blockcount >=
+ aoff + alen);
+#ifdef DEBUG
+ if (delay) {
+ ASSERT(ISNULLSTARTBLOCK(got.br_startblock));
+ ASSERT(STARTBLOCKVAL(got.br_startblock) > 0);
+ }
+ ASSERT(got.br_state == XFS_EXT_NORM ||
+ got.br_state == XFS_EXT_UNWRITTEN);
+#endif
+ /*
+ * Fall down into the found allocated space case.
+ */
+ } else if (inhole) {
+ /*
+ * Reading in a hole.
+ */
+ mval->br_startoff = bno;
+ mval->br_startblock = HOLESTARTBLOCK;
+ mval->br_blockcount =
+ XFS_FILBLKS_MIN(len, got.br_startoff - bno);
+ mval->br_state = XFS_EXT_NORM;
+ bno += mval->br_blockcount;
+ len -= mval->br_blockcount;
+ mval++;
+ n++;
+ continue;
+ }
+ /*
+ * Then deal with the allocated space we found.
+ */
+ ASSERT(ep != NULL);
+ if (trim && (got.br_startoff + got.br_blockcount > obno)) {
+ if (obno > bno)
+ bno = obno;
+ ASSERT((bno >= obno) || (n == 0));
+ ASSERT(bno < end);
+ mval->br_startoff = bno;
+ if (ISNULLSTARTBLOCK(got.br_startblock)) {
+ ASSERT(!wr || delay);
+ mval->br_startblock = DELAYSTARTBLOCK;
+ } else
+ mval->br_startblock =
+ got.br_startblock +
+ (bno - got.br_startoff);
+ /*
+ * Return the minimum of what we got and what we
+ * asked for for the length. We can use the len
+ * variable here because it is modified below
+ * and we could have been there before coming
+ * here if the first part of the allocation
+ * didn't overlap what was asked for.
+ */
+ mval->br_blockcount =
+ XFS_FILBLKS_MIN(end - bno, got.br_blockcount -
+ (bno - got.br_startoff));
+ mval->br_state = got.br_state;
+ ASSERT(mval->br_blockcount <= len);
+ } else {
+ *mval = got;
+ if (ISNULLSTARTBLOCK(mval->br_startblock)) {
+ ASSERT(!wr || delay);
+ mval->br_startblock = DELAYSTARTBLOCK;
+ }
+ }
+
+ /*
+ * Check if writing previously allocated but
+ * unwritten extents.
+ */
+ if (wr && mval->br_state == XFS_EXT_UNWRITTEN &&
+ ((flags & (XFS_BMAPI_PREALLOC|XFS_BMAPI_DELAY)) == 0)) {
+ /*
+ * Modify (by adding) the state flag, if writing.
+ */
+ ASSERT(mval->br_blockcount <= len);
+ if ((ifp->if_flags & XFS_IFBROOT) && !cur) {
+ cur = xfs_btree_init_cursor(mp,
+ tp, NULL, 0, XFS_BTNUM_BMAP,
+ ip, whichfork);
+ cur->bc_private.b.firstblock =
+ *firstblock;
+ cur->bc_private.b.flist = flist;
+ }
+ mval->br_state = XFS_EXT_NORM;
+ error = xfs_bmap_add_extent(ip, lastx, &cur, mval,
+ firstblock, flist, &tmp_logflags, whichfork,
+ rsvd);
+ logflags |= tmp_logflags;
+ if (error)
+ goto error0;
+ lastx = ifp->if_lastex;
+ ep = &ifp->if_u1.if_extents[lastx];
+ nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
+ xfs_bmbt_get_all(ep, &got);
+ /*
+ * We may have combined previously unwritten
+ * space with written space, so generate
+ * another request.
+ */
+ if (mval->br_blockcount < len)
+ continue;
+ }
+
+ ASSERT(!trim ||
+ ((mval->br_startoff + mval->br_blockcount) <= end));
+ ASSERT(!trim || (mval->br_blockcount <= len) ||
+ (mval->br_startoff < obno));
+ bno = mval->br_startoff + mval->br_blockcount;
+ len = end - bno;
+ if (n > 0 && mval->br_startoff == mval[-1].br_startoff) {
+ ASSERT(mval->br_startblock == mval[-1].br_startblock);
+ ASSERT(mval->br_blockcount > mval[-1].br_blockcount);
+ ASSERT(mval->br_state == mval[-1].br_state);
+ mval[-1].br_blockcount = mval->br_blockcount;
+ mval[-1].br_state = mval->br_state;
+ } else if (n > 0 && mval->br_startblock != DELAYSTARTBLOCK &&
+ mval[-1].br_startblock != DELAYSTARTBLOCK &&
+ mval[-1].br_startblock != HOLESTARTBLOCK &&
+ mval->br_startblock ==
+ mval[-1].br_startblock + mval[-1].br_blockcount &&
+ (stateless || mval[-1].br_state == mval->br_state)) {
+ ASSERT(mval->br_startoff ==
+ mval[-1].br_startoff + mval[-1].br_blockcount);
+ mval[-1].br_blockcount += mval->br_blockcount;
+ } else if (n > 0 &&
+ mval->br_startblock == DELAYSTARTBLOCK &&
+ mval[-1].br_startblock == DELAYSTARTBLOCK &&
+ mval->br_startoff ==
+ mval[-1].br_startoff + mval[-1].br_blockcount) {
+ mval[-1].br_blockcount += mval->br_blockcount;
+ mval[-1].br_state = mval->br_state;
+ } else if (!((n == 0) &&
+ ((mval->br_startoff + mval->br_blockcount) <=
+ obno))) {
+ mval++;
+ n++;
+ }
+ /*
+ * If we're done, stop now. Stop when we've allocated
+ * XFS_BMAP_MAX_NMAP extents no matter what. Otherwise
+ * the transaction may get too big.
+ */
+ if (bno >= end || n >= *nmap || nallocs >= *nmap)
+ break;
+ /*
+ * Else go on to the next record.
+ */
+ ep++;
+ lastx++;
+ if (lastx >= nextents) {
+ eof = 1;
+ prev = got;
+ } else
+ xfs_bmbt_get_all(ep, &got);
+ }
+ ifp->if_lastex = lastx;
+ *nmap = n;
+ /*
+ * Transform from btree to extents, give it cur.
+ */
+ if (tp && XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_BTREE &&
+ XFS_IFORK_NEXTENTS(ip, whichfork) <= ifp->if_ext_max) {
+ ASSERT(wr && cur);
+ error = xfs_bmap_btree_to_extents(tp, ip, cur,
+ &tmp_logflags, whichfork);
+ logflags |= tmp_logflags;
+ if (error)
+ goto error0;
+ }
+ ASSERT(ifp->if_ext_max ==
+ XFS_IFORK_SIZE(ip, whichfork) / (uint)sizeof(xfs_bmbt_rec_t));
+ ASSERT(XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE ||
+ XFS_IFORK_NEXTENTS(ip, whichfork) > ifp->if_ext_max);
+ error = 0;
+
+error0:
+ /*
+ * Log everything. Do this after conversion, there's no point in
+ * logging the extent list if we've converted to btree format.
+ */
+ if ((logflags & XFS_ILOG_FEXT(whichfork)) &&
+ XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS)
+ logflags &= ~XFS_ILOG_FEXT(whichfork);
+ else if ((logflags & XFS_ILOG_FBROOT(whichfork)) &&
+ XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE)
+ logflags &= ~XFS_ILOG_FBROOT(whichfork);
+ /*
+ * Log whatever the flags say, even if error. Otherwise we might miss
+ * detecting a case where the data is changed, there's an error,
+ * and it's not logged so we don't shutdown when we should.
+ */
+ if (logflags) {
+ ASSERT(tp && wr);
+ xfs_trans_log_inode(tp, ip, logflags);
+ }
+ if (cur) {
+ if (!error) {
+ ASSERT(*firstblock == NULLFSBLOCK ||
+ XFS_FSB_TO_AGNO(mp, *firstblock) ==
+ XFS_FSB_TO_AGNO(mp,
+ cur->bc_private.b.firstblock) ||
+ (flist->xbf_low &&
+ XFS_FSB_TO_AGNO(mp, *firstblock) <
+ XFS_FSB_TO_AGNO(mp,
+ cur->bc_private.b.firstblock)));
+ *firstblock = cur->bc_private.b.firstblock;
+ }
+ xfs_btree_del_cursor(cur,
+ error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
+ }
+ if (!error)
+ xfs_bmap_validate_ret(orig_bno, orig_len, orig_flags, orig_mval,
+ orig_nmap, *nmap);
+ return error;
+}
+
+/*
+ * Map file blocks to filesystem blocks, simple version.
+ * One block (extent) only, read-only.
+ * For flags, only the XFS_BMAPI_ATTRFORK flag is examined.
+ * For the other flag values, the effect is as if XFS_BMAPI_METADATA
+ * was set and all the others were clear.
+ */
+int /* error */
+xfs_bmapi_single(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_inode_t *ip, /* incore inode */
+ int whichfork, /* data or attr fork */
+ xfs_fsblock_t *fsb, /* output: mapped block */
+ xfs_fileoff_t bno) /* starting file offs. mapped */
+{
+ int eof; /* we've hit the end of extent list */
+ int error; /* error return */
+ xfs_bmbt_irec_t got; /* current extent list record */
+ xfs_ifork_t *ifp; /* inode fork pointer */
+ xfs_extnum_t lastx; /* last useful extent number */
+ xfs_bmbt_irec_t prev; /* previous extent list record */
+
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+ if (unlikely(
+ XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE &&
+ XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS)) {
+ XFS_ERROR_REPORT("xfs_bmapi_single", XFS_ERRLEVEL_LOW,
+ ip->i_mount);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ if (XFS_FORCED_SHUTDOWN(ip->i_mount))
+ return XFS_ERROR(EIO);
+ XFS_STATS_INC(xs_blk_mapr);
+ if (!(ifp->if_flags & XFS_IFEXTENTS) &&
+ (error = xfs_iread_extents(tp, ip, whichfork)))
+ return error;
+ (void)xfs_bmap_search_extents(ip, bno, whichfork, &eof, &lastx, &got,
+ &prev);
+ /*
+ * Reading past eof, act as though there's a hole
+ * up to end.
+ */
+ if (eof || got.br_startoff > bno) {
+ *fsb = NULLFSBLOCK;
+ return 0;
+ }
+ ASSERT(!ISNULLSTARTBLOCK(got.br_startblock));
+ ASSERT(bno < got.br_startoff + got.br_blockcount);
+ *fsb = got.br_startblock + (bno - got.br_startoff);
+ ifp->if_lastex = lastx;
+ return 0;
+}
+
+/*
+ * Unmap (remove) blocks from a file.
+ * If nexts is nonzero then the number of extents to remove is limited to
+ * that value. If not all extents in the block range can be removed then
+ * *done is set.
+ */
+int /* error */
+xfs_bunmapi(
+ xfs_trans_t *tp, /* transaction pointer */
+ struct xfs_inode *ip, /* incore inode */
+ xfs_fileoff_t bno, /* starting offset to unmap */
+ xfs_filblks_t len, /* length to unmap in file */
+ int flags, /* misc flags */
+ xfs_extnum_t nexts, /* number of extents max */
+ xfs_fsblock_t *firstblock, /* first allocated block
+ controls a.g. for allocs */
+ xfs_bmap_free_t *flist, /* i/o: list extents to free */
+ int *done) /* set if not done yet */
+{
+ xfs_btree_cur_t *cur; /* bmap btree cursor */
+ xfs_bmbt_irec_t del; /* extent being deleted */
+ int eof; /* is deleting at eof */
+ xfs_bmbt_rec_t *ep; /* extent list entry pointer */
+ int error; /* error return value */
+ xfs_extnum_t extno; /* extent number in list */
+ xfs_bmbt_irec_t got; /* current extent list entry */
+ xfs_ifork_t *ifp; /* inode fork pointer */
+ int isrt; /* freeing in rt area */
+ xfs_extnum_t lastx; /* last extent index used */
+ int logflags; /* transaction logging flags */
+ xfs_extlen_t mod; /* rt extent offset */
+ xfs_mount_t *mp; /* mount structure */
+ xfs_extnum_t nextents; /* size of extent list */
+ xfs_bmbt_irec_t prev; /* previous extent list entry */
+ xfs_fileoff_t start; /* first file offset deleted */
+ int tmp_logflags; /* partial logging flags */
+ int wasdel; /* was a delayed alloc extent */
+ int whichfork; /* data or attribute fork */
+ int rsvd; /* OK to allocate reserved blocks */
+ xfs_fsblock_t sum;
+
+ xfs_bunmap_trace(ip, bno, len, flags, (inst_t *)__return_address);
+ whichfork = (flags & XFS_BMAPI_ATTRFORK) ?
+ XFS_ATTR_FORK : XFS_DATA_FORK;
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+ if (unlikely(
+ XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS &&
+ XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE)) {
+ XFS_ERROR_REPORT("xfs_bunmapi", XFS_ERRLEVEL_LOW,
+ ip->i_mount);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ mp = ip->i_mount;
+ if (XFS_FORCED_SHUTDOWN(mp))
+ return XFS_ERROR(EIO);
+ rsvd = (flags & XFS_BMAPI_RSVBLOCKS) != 0;
+ ASSERT(len > 0);
+ ASSERT(nexts >= 0);
+ ASSERT(ifp->if_ext_max ==
+ XFS_IFORK_SIZE(ip, whichfork) / (uint)sizeof(xfs_bmbt_rec_t));
+ if (!(ifp->if_flags & XFS_IFEXTENTS) &&
+ (error = xfs_iread_extents(tp, ip, whichfork)))
+ return error;
+ nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
+ if (nextents == 0) {
+ *done = 1;
+ return 0;
+ }
+ XFS_STATS_INC(xs_blk_unmap);
+ isrt = (whichfork == XFS_DATA_FORK) &&
+ (ip->i_d.di_flags & XFS_DIFLAG_REALTIME);
+ start = bno;
+ bno = start + len - 1;
+ ep = xfs_bmap_search_extents(ip, bno, whichfork, &eof, &lastx, &got,
+ &prev);
+ /*
+ * Check to see if the given block number is past the end of the
+ * file, back up to the last block if so...
+ */
+ if (eof) {
+ ep = &ifp->if_u1.if_extents[--lastx];
+ xfs_bmbt_get_all(ep, &got);
+ bno = got.br_startoff + got.br_blockcount - 1;
+ }
+ logflags = 0;
+ if (ifp->if_flags & XFS_IFBROOT) {
+ ASSERT(XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_BTREE);
+ cur = xfs_btree_init_cursor(mp, tp, NULL, 0, XFS_BTNUM_BMAP, ip,
+ whichfork);
+ cur->bc_private.b.firstblock = *firstblock;
+ cur->bc_private.b.flist = flist;
+ cur->bc_private.b.flags = 0;
+ } else
+ cur = NULL;
+ extno = 0;
+ while (bno != (xfs_fileoff_t)-1 && bno >= start && lastx >= 0 &&
+ (nexts == 0 || extno < nexts)) {
+ /*
+ * Is the found extent after a hole in which bno lives?
+ * Just back up to the previous extent, if so.
+ */
+ if (got.br_startoff > bno) {
+ if (--lastx < 0)
+ break;
+ ep--;
+ xfs_bmbt_get_all(ep, &got);
+ }
+ /*
+ * Is the last block of this extent before the range
+ * we're supposed to delete? If so, we're done.
+ */
+ bno = XFS_FILEOFF_MIN(bno,
+ got.br_startoff + got.br_blockcount - 1);
+ if (bno < start)
+ break;
+ /*
+ * Then deal with the (possibly delayed) allocated space
+ * we found.
+ */
+ ASSERT(ep != NULL);
+ del = got;
+ wasdel = ISNULLSTARTBLOCK(del.br_startblock);
+ if (got.br_startoff < start) {
+ del.br_startoff = start;
+ del.br_blockcount -= start - got.br_startoff;
+ if (!wasdel)
+ del.br_startblock += start - got.br_startoff;
+ }
+ if (del.br_startoff + del.br_blockcount > bno + 1)
+ del.br_blockcount = bno + 1 - del.br_startoff;
+ sum = del.br_startblock + del.br_blockcount;
+ if (isrt &&
+ (mod = do_mod(sum, mp->m_sb.sb_rextsize))) {
+ /*
+ * Realtime extent not lined up at the end.
+ * The extent could have been split into written
+ * and unwritten pieces, or we could just be
+ * unmapping part of it. But we can't really
+ * get rid of part of a realtime extent.
+ */
+ if (del.br_state == XFS_EXT_UNWRITTEN ||
+ !XFS_SB_VERSION_HASEXTFLGBIT(&mp->m_sb)) {
+ /*
+ * This piece is unwritten, or we're not
+ * using unwritten extents. Skip over it.
+ */
+ ASSERT(bno >= mod);
+ bno -= mod > del.br_blockcount ?
+ del.br_blockcount : mod;
+ if (bno < got.br_startoff) {
+ if (--lastx >= 0)
+ xfs_bmbt_get_all(--ep, &got);
+ }
+ continue;
+ }
+ /*
+ * It's written, turn it unwritten.
+ * This is better than zeroing it.
+ */
+ ASSERT(del.br_state == XFS_EXT_NORM);
+ ASSERT(xfs_trans_get_block_res(tp) > 0);
+ /*
+ * If this spans a realtime extent boundary,
+ * chop it back to the start of the one we end at.
+ */
+ if (del.br_blockcount > mod) {
+ del.br_startoff += del.br_blockcount - mod;
+ del.br_startblock += del.br_blockcount - mod;
+ del.br_blockcount = mod;
+ }
+ del.br_state = XFS_EXT_UNWRITTEN;
+ error = xfs_bmap_add_extent(ip, lastx, &cur, &del,
+ firstblock, flist, &logflags, XFS_DATA_FORK, 0);
+ if (error)
+ goto error0;
+ goto nodelete;
+ }
+ if (isrt && (mod = do_mod(del.br_startblock, mp->m_sb.sb_rextsize))) {
+ /*
+ * Realtime extent is lined up at the end but not
+ * at the front. We'll get rid of full extents if
+ * we can.
+ */
+ mod = mp->m_sb.sb_rextsize - mod;
+ if (del.br_blockcount > mod) {
+ del.br_blockcount -= mod;
+ del.br_startoff += mod;
+ del.br_startblock += mod;
+ } else if ((del.br_startoff == start &&
+ (del.br_state == XFS_EXT_UNWRITTEN ||
+ xfs_trans_get_block_res(tp) == 0)) ||
+ !XFS_SB_VERSION_HASEXTFLGBIT(&mp->m_sb)) {
+ /*
+ * Can't make it unwritten. There isn't
+ * a full extent here so just skip it.
+ */
+ ASSERT(bno >= del.br_blockcount);
+ bno -= del.br_blockcount;
+ if (bno < got.br_startoff) {
+ if (--lastx >= 0)
+ xfs_bmbt_get_all(--ep, &got);
+ }
+ continue;
+ } else if (del.br_state == XFS_EXT_UNWRITTEN) {
+ /*
+ * This one is already unwritten.
+ * It must have a written left neighbor.
+ * Unwrite the killed part of that one and
+ * try again.
+ */
+ ASSERT(lastx > 0);
+ xfs_bmbt_get_all(ep - 1, &prev);
+ ASSERT(prev.br_state == XFS_EXT_NORM);
+ ASSERT(!ISNULLSTARTBLOCK(prev.br_startblock));
+ ASSERT(del.br_startblock ==
+ prev.br_startblock + prev.br_blockcount);
+ if (prev.br_startoff < start) {
+ mod = start - prev.br_startoff;
+ prev.br_blockcount -= mod;
+ prev.br_startblock += mod;
+ prev.br_startoff = start;
+ }
+ prev.br_state = XFS_EXT_UNWRITTEN;
+ error = xfs_bmap_add_extent(ip, lastx - 1, &cur,
+ &prev, firstblock, flist, &logflags,
+ XFS_DATA_FORK, 0);
+ if (error)
+ goto error0;
+ goto nodelete;
+ } else {
+ ASSERT(del.br_state == XFS_EXT_NORM);
+ del.br_state = XFS_EXT_UNWRITTEN;
+ error = xfs_bmap_add_extent(ip, lastx, &cur,
+ &del, firstblock, flist, &logflags,
+ XFS_DATA_FORK, 0);
+ if (error)
+ goto error0;
+ goto nodelete;
+ }
+ }
+ if (wasdel) {
+ ASSERT(STARTBLOCKVAL(del.br_startblock) > 0);
+ xfs_mod_incore_sb(mp, XFS_SBS_FDBLOCKS,
+ (int)del.br_blockcount, rsvd);
+ /* Unreserve our quota space */
+ XFS_TRANS_RESERVE_QUOTA_NBLKS(
+ mp, NULL, ip, -((long)del.br_blockcount), 0,
+ isrt ? XFS_QMOPT_RES_RTBLKS :
+ XFS_QMOPT_RES_REGBLKS);
+ ip->i_delayed_blks -= del.br_blockcount;
+ if (cur)
+ cur->bc_private.b.flags |=
+ XFS_BTCUR_BPRV_WASDEL;
+ } else if (cur)
+ cur->bc_private.b.flags &= ~XFS_BTCUR_BPRV_WASDEL;
+ /*
+ * If it's the case where the directory code is running
+ * with no block reservation, and the deleted block is in
+ * the middle of its extent, and the resulting insert
+ * of an extent would cause transformation to btree format,
+ * then reject it. The calling code will then swap
+ * blocks around instead.
+ * We have to do this now, rather than waiting for the
+ * conversion to btree format, since the transaction
+ * will be dirty.
+ */
+ if (!wasdel && xfs_trans_get_block_res(tp) == 0 &&
+ XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_EXTENTS &&
+ XFS_IFORK_NEXTENTS(ip, whichfork) >= ifp->if_ext_max &&
+ del.br_startoff > got.br_startoff &&
+ del.br_startoff + del.br_blockcount <
+ got.br_startoff + got.br_blockcount) {
+ error = XFS_ERROR(ENOSPC);
+ goto error0;
+ }
+ error = xfs_bmap_del_extent(ip, tp, lastx, flist, cur, &del,
+ &tmp_logflags, whichfork, rsvd);
+ logflags |= tmp_logflags;
+ if (error)
+ goto error0;
+ bno = del.br_startoff - 1;
+nodelete:
+ lastx = ifp->if_lastex;
+ /*
+ * If not done go on to the next (previous) record.
+ * Reset ep in case the extents array was re-alloced.
+ */
+ ep = &ifp->if_u1.if_extents[lastx];
+ if (bno != (xfs_fileoff_t)-1 && bno >= start) {
+ if (lastx >= XFS_IFORK_NEXTENTS(ip, whichfork) ||
+ xfs_bmbt_get_startoff(ep) > bno) {
+ lastx--;
+ ep--;
+ }
+ if (lastx >= 0)
+ xfs_bmbt_get_all(ep, &got);
+ extno++;
+ }
+ }
+ ifp->if_lastex = lastx;
+ *done = bno == (xfs_fileoff_t)-1 || bno < start || lastx < 0;
+ ASSERT(ifp->if_ext_max ==
+ XFS_IFORK_SIZE(ip, whichfork) / (uint)sizeof(xfs_bmbt_rec_t));
+ /*
+ * Convert to a btree if necessary.
+ */
+ if (XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_EXTENTS &&
+ XFS_IFORK_NEXTENTS(ip, whichfork) > ifp->if_ext_max) {
+ ASSERT(cur == NULL);
+ error = xfs_bmap_extents_to_btree(tp, ip, firstblock, flist,
+ &cur, 0, &tmp_logflags, whichfork);
+ logflags |= tmp_logflags;
+ if (error)
+ goto error0;
+ }
+ /*
+ * transform from btree to extents, give it cur
+ */
+ else if (XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_BTREE &&
+ XFS_IFORK_NEXTENTS(ip, whichfork) <= ifp->if_ext_max) {
+ ASSERT(cur != NULL);
+ error = xfs_bmap_btree_to_extents(tp, ip, cur, &tmp_logflags,
+ whichfork);
+ logflags |= tmp_logflags;
+ if (error)
+ goto error0;
+ }
+ /*
+ * transform from extents to local?
+ */
+ ASSERT(ifp->if_ext_max ==
+ XFS_IFORK_SIZE(ip, whichfork) / (uint)sizeof(xfs_bmbt_rec_t));
+ error = 0;
+error0:
+ /*
+ * Log everything. Do this after conversion, there's no point in
+ * logging the extent list if we've converted to btree format.
+ */
+ if ((logflags & XFS_ILOG_FEXT(whichfork)) &&
+ XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS)
+ logflags &= ~XFS_ILOG_FEXT(whichfork);
+ else if ((logflags & XFS_ILOG_FBROOT(whichfork)) &&
+ XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE)
+ logflags &= ~XFS_ILOG_FBROOT(whichfork);
+ /*
+ * Log inode even in the error case, if the transaction
+ * is dirty we'll need to shut down the filesystem.
+ */
+ if (logflags)
+ xfs_trans_log_inode(tp, ip, logflags);
+ if (cur) {
+ if (!error) {
+ *firstblock = cur->bc_private.b.firstblock;
+ cur->bc_private.b.allocated = 0;
+ }
+ xfs_btree_del_cursor(cur,
+ error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
+ }
+ return error;
+}
+
+/*
+ * Fcntl interface to xfs_bmapi.
+ */
+int /* error code */
+xfs_getbmap(
+ bhv_desc_t *bdp, /* XFS behavior descriptor*/
+ struct getbmap *bmv, /* user bmap structure */
+ void __user *ap, /* pointer to user's array */
+ int interface) /* interface flags */
+{
+ __int64_t bmvend; /* last block requested */
+ int error; /* return value */
+ __int64_t fixlen; /* length for -1 case */
+ int i; /* extent number */
+ xfs_inode_t *ip; /* xfs incore inode pointer */
+ vnode_t *vp; /* corresponding vnode */
+ int lock; /* lock state */
+ xfs_bmbt_irec_t *map; /* buffer for user's data */
+ xfs_mount_t *mp; /* file system mount point */
+ int nex; /* # of user extents can do */
+ int nexleft; /* # of user extents left */
+ int subnex; /* # of bmapi's can do */
+ int nmap; /* number of map entries */
+ struct getbmap out; /* output structure */
+ int whichfork; /* data or attr fork */
+ int prealloced; /* this is a file with
+ * preallocated data space */
+ int sh_unwritten; /* true, if unwritten */
+ /* extents listed separately */
+ int bmapi_flags; /* flags for xfs_bmapi */
+ __int32_t oflags; /* getbmapx bmv_oflags field */
+
+ vp = BHV_TO_VNODE(bdp);
+ ip = XFS_BHVTOI(bdp);
+ mp = ip->i_mount;
+
+ whichfork = interface & BMV_IF_ATTRFORK ? XFS_ATTR_FORK : XFS_DATA_FORK;
+ sh_unwritten = (interface & BMV_IF_PREALLOC) != 0;
+
+ /* If the BMV_IF_NO_DMAPI_READ interface bit specified, do not
+ * generate a DMAPI read event. Otherwise, if the DM_EVENT_READ
+ * bit is set for the file, generate a read event in order
+ * that the DMAPI application may do its thing before we return
+ * the extents. Usually this means restoring user file data to
+ * regions of the file that look like holes.
+ *
+ * The "old behavior" (from XFS_IOC_GETBMAP) is to not specify
+ * BMV_IF_NO_DMAPI_READ so that read events are generated.
+ * If this were not true, callers of ioctl( XFS_IOC_GETBMAP )
+ * could misinterpret holes in a DMAPI file as true holes,
+ * when in fact they may represent offline user data.
+ */
+ if ( (interface & BMV_IF_NO_DMAPI_READ) == 0
+ && DM_EVENT_ENABLED(vp->v_vfsp, ip, DM_EVENT_READ)
+ && whichfork == XFS_DATA_FORK) {
+
+ error = XFS_SEND_DATA(mp, DM_EVENT_READ, vp, 0, 0, 0, NULL);
+ if (error)
+ return XFS_ERROR(error);
+ }
+
+ if (whichfork == XFS_ATTR_FORK) {
+ if (XFS_IFORK_Q(ip)) {
+ if (ip->i_d.di_aformat != XFS_DINODE_FMT_EXTENTS &&
+ ip->i_d.di_aformat != XFS_DINODE_FMT_BTREE &&
+ ip->i_d.di_aformat != XFS_DINODE_FMT_LOCAL)
+ return XFS_ERROR(EINVAL);
+ } else if (unlikely(
+ ip->i_d.di_aformat != 0 &&
+ ip->i_d.di_aformat != XFS_DINODE_FMT_EXTENTS)) {
+ XFS_ERROR_REPORT("xfs_getbmap", XFS_ERRLEVEL_LOW,
+ ip->i_mount);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ } else if (ip->i_d.di_format != XFS_DINODE_FMT_EXTENTS &&
+ ip->i_d.di_format != XFS_DINODE_FMT_BTREE &&
+ ip->i_d.di_format != XFS_DINODE_FMT_LOCAL)
+ return XFS_ERROR(EINVAL);
+ if (whichfork == XFS_DATA_FORK) {
+ if (ip->i_d.di_flags & XFS_DIFLAG_PREALLOC) {
+ prealloced = 1;
+ fixlen = XFS_MAXIOFFSET(mp);
+ } else {
+ prealloced = 0;
+ fixlen = ip->i_d.di_size;
+ }
+ } else {
+ prealloced = 0;
+ fixlen = 1LL << 32;
+ }
+
+ if (bmv->bmv_length == -1) {
+ fixlen = XFS_FSB_TO_BB(mp, XFS_B_TO_FSB(mp, fixlen));
+ bmv->bmv_length = MAX( (__int64_t)(fixlen - bmv->bmv_offset),
+ (__int64_t)0);
+ } else if (bmv->bmv_length < 0)
+ return XFS_ERROR(EINVAL);
+ if (bmv->bmv_length == 0) {
+ bmv->bmv_entries = 0;
+ return 0;
+ }
+ nex = bmv->bmv_count - 1;
+ if (nex <= 0)
+ return XFS_ERROR(EINVAL);
+ bmvend = bmv->bmv_offset + bmv->bmv_length;
+
+ xfs_ilock(ip, XFS_IOLOCK_SHARED);
+
+ if (whichfork == XFS_DATA_FORK && ip->i_delayed_blks) {
+ /* xfs_fsize_t last_byte = xfs_file_last_byte(ip); */
+ VOP_FLUSH_PAGES(vp, (xfs_off_t)0, -1, 0, FI_REMAPF, error);
+ }
+
+ ASSERT(whichfork == XFS_ATTR_FORK || ip->i_delayed_blks == 0);
+
+ lock = xfs_ilock_map_shared(ip);
+
+ /*
+ * Don't let nex be bigger than the number of extents
+ * we can have assuming alternating holes and real extents.
+ */
+ if (nex > XFS_IFORK_NEXTENTS(ip, whichfork) * 2 + 1)
+ nex = XFS_IFORK_NEXTENTS(ip, whichfork) * 2 + 1;
+
+ bmapi_flags = XFS_BMAPI_AFLAG(whichfork) |
+ ((sh_unwritten) ? 0 : XFS_BMAPI_IGSTATE);
+
+ /*
+ * Allocate enough space to handle "subnex" maps at a time.
+ */
+ subnex = 16;
+ map = kmem_alloc(subnex * sizeof(*map), KM_SLEEP);
+
+ bmv->bmv_entries = 0;
+
+ if (XFS_IFORK_NEXTENTS(ip, whichfork) == 0) {
+ error = 0;
+ goto unlock_and_return;
+ }
+
+ nexleft = nex;
+
+ do {
+ nmap = (nexleft > subnex) ? subnex : nexleft;
+ error = xfs_bmapi(NULL, ip, XFS_BB_TO_FSBT(mp, bmv->bmv_offset),
+ XFS_BB_TO_FSB(mp, bmv->bmv_length),
+ bmapi_flags, NULL, 0, map, &nmap, NULL);
+ if (error)
+ goto unlock_and_return;
+ ASSERT(nmap <= subnex);
+
+ for (i = 0; i < nmap && nexleft && bmv->bmv_length; i++) {
+ nexleft--;
+ oflags = (map[i].br_state == XFS_EXT_UNWRITTEN) ?
+ BMV_OF_PREALLOC : 0;
+ out.bmv_offset = XFS_FSB_TO_BB(mp, map[i].br_startoff);
+ out.bmv_length = XFS_FSB_TO_BB(mp, map[i].br_blockcount);
+ ASSERT(map[i].br_startblock != DELAYSTARTBLOCK);
+ if (prealloced &&
+ map[i].br_startblock == HOLESTARTBLOCK &&
+ out.bmv_offset + out.bmv_length == bmvend) {
+ /*
+ * came to hole at end of file
+ */
+ goto unlock_and_return;
+ } else {
+ out.bmv_block =
+ (map[i].br_startblock == HOLESTARTBLOCK) ?
+ -1 :
+ XFS_FSB_TO_DB(ip, map[i].br_startblock);
+
+ /* return either getbmap/getbmapx structure. */
+ if (interface & BMV_IF_EXTENDED) {
+ struct getbmapx outx;
+
+ GETBMAP_CONVERT(out,outx);
+ outx.bmv_oflags = oflags;
+ outx.bmv_unused1 = outx.bmv_unused2 = 0;
+ if (copy_to_user(ap, &outx,
+ sizeof(outx))) {
+ error = XFS_ERROR(EFAULT);
+ goto unlock_and_return;
+ }
+ } else {
+ if (copy_to_user(ap, &out,
+ sizeof(out))) {
+ error = XFS_ERROR(EFAULT);
+ goto unlock_and_return;
+ }
+ }
+ bmv->bmv_offset =
+ out.bmv_offset + out.bmv_length;
+ bmv->bmv_length = MAX((__int64_t)0,
+ (__int64_t)(bmvend - bmv->bmv_offset));
+ bmv->bmv_entries++;
+ ap = (interface & BMV_IF_EXTENDED) ?
+ (void __user *)
+ ((struct getbmapx __user *)ap + 1) :
+ (void __user *)
+ ((struct getbmap __user *)ap + 1);
+ }
+ }
+ } while (nmap && nexleft && bmv->bmv_length);
+
+unlock_and_return:
+ xfs_iunlock_map_shared(ip, lock);
+ xfs_iunlock(ip, XFS_IOLOCK_SHARED);
+
+ kmem_free(map, subnex * sizeof(*map));
+
+ return error;
+}
+
+/*
+ * Check the last inode extent to determine whether this allocation will result
+ * in blocks being allocated at the end of the file. When we allocate new data
+ * blocks at the end of the file which do not start at the previous data block,
+ * we will try to align the new blocks at stripe unit boundaries.
+ */
+int /* error */
+xfs_bmap_isaeof(
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_fileoff_t off, /* file offset in fsblocks */
+ int whichfork, /* data or attribute fork */
+ char *aeof) /* return value */
+{
+ int error; /* error return value */
+ xfs_ifork_t *ifp; /* inode fork pointer */
+ xfs_bmbt_rec_t *lastrec; /* extent list entry pointer */
+ xfs_extnum_t nextents; /* size of extent list */
+ xfs_bmbt_irec_t s; /* expanded extent list entry */
+
+ ASSERT(whichfork == XFS_DATA_FORK);
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+ if (!(ifp->if_flags & XFS_IFEXTENTS) &&
+ (error = xfs_iread_extents(NULL, ip, whichfork)))
+ return error;
+ nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
+ if (nextents == 0) {
+ *aeof = 1;
+ return 0;
+ }
+ /*
+ * Go to the last extent
+ */
+ lastrec = &ifp->if_u1.if_extents[nextents - 1];
+ xfs_bmbt_get_all(lastrec, &s);
+ /*
+ * Check we are allocating in the last extent (for delayed allocations)
+ * or past the last extent for non-delayed allocations.
+ */
+ *aeof = (off >= s.br_startoff &&
+ off < s.br_startoff + s.br_blockcount &&
+ ISNULLSTARTBLOCK(s.br_startblock)) ||
+ off >= s.br_startoff + s.br_blockcount;
+ return 0;
+}
+
+/*
+ * Check if the endoff is outside the last extent. If so the caller will grow
+ * the allocation to a stripe unit boundary.
+ */
+int /* error */
+xfs_bmap_eof(
+ xfs_inode_t *ip, /* incore inode pointer */
+ xfs_fileoff_t endoff, /* file offset in fsblocks */
+ int whichfork, /* data or attribute fork */
+ int *eof) /* result value */
+{
+ xfs_fsblock_t blockcount; /* extent block count */
+ int error; /* error return value */
+ xfs_ifork_t *ifp; /* inode fork pointer */
+ xfs_bmbt_rec_t *lastrec; /* extent list entry pointer */
+ xfs_extnum_t nextents; /* size of extent list */
+ xfs_fileoff_t startoff; /* extent starting file offset */
+
+ ASSERT(whichfork == XFS_DATA_FORK);
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+ if (!(ifp->if_flags & XFS_IFEXTENTS) &&
+ (error = xfs_iread_extents(NULL, ip, whichfork)))
+ return error;
+ nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
+ if (nextents == 0) {
+ *eof = 1;
+ return 0;
+ }
+ /*
+ * Go to the last extent
+ */
+ lastrec = &ifp->if_u1.if_extents[nextents - 1];
+ startoff = xfs_bmbt_get_startoff(lastrec);
+ blockcount = xfs_bmbt_get_blockcount(lastrec);
+ *eof = endoff >= startoff + blockcount;
+ return 0;
+}
+
+#ifdef DEBUG
+/*
+ * Check that the extents list for the inode ip is in the right order.
+ */
+STATIC void
+xfs_bmap_check_extents(
+ xfs_inode_t *ip, /* incore inode pointer */
+ int whichfork) /* data or attr fork */
+{
+ xfs_bmbt_rec_t *base; /* base of extents list */
+ xfs_bmbt_rec_t *ep; /* current extent entry */
+ xfs_ifork_t *ifp; /* inode fork pointer */
+ xfs_extnum_t nextents; /* number of extents in list */
+
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+ ASSERT(ifp->if_flags & XFS_IFEXTENTS);
+ base = ifp->if_u1.if_extents;
+ nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
+ for (ep = base; ep < &base[nextents - 1]; ep++) {
+ xfs_btree_check_rec(XFS_BTNUM_BMAP, (void *)ep,
+ (void *)(ep + 1));
+ }
+}
+
+STATIC
+xfs_buf_t *
+xfs_bmap_get_bp(
+ xfs_btree_cur_t *cur,
+ xfs_fsblock_t bno)
+{
+ int i;
+ xfs_buf_t *bp;
+
+ if (!cur)
+ return(NULL);
+
+ bp = NULL;
+ for(i = 0; i < XFS_BTREE_MAXLEVELS; i++) {
+ bp = cur->bc_bufs[i];
+ if (!bp) break;
+ if (XFS_BUF_ADDR(bp) == bno)
+ break; /* Found it */
+ }
+ if (i == XFS_BTREE_MAXLEVELS)
+ bp = NULL;
+
+ if (!bp) { /* Chase down all the log items to see if the bp is there */
+ xfs_log_item_chunk_t *licp;
+ xfs_trans_t *tp;
+
+ tp = cur->bc_tp;
+ licp = &tp->t_items;
+ while (!bp && licp != NULL) {
+ if (XFS_LIC_ARE_ALL_FREE(licp)) {
+ licp = licp->lic_next;
+ continue;
+ }
+ for (i = 0; i < licp->lic_unused; i++) {
+ xfs_log_item_desc_t *lidp;
+ xfs_log_item_t *lip;
+ xfs_buf_log_item_t *bip;
+ xfs_buf_t *lbp;
+
+ if (XFS_LIC_ISFREE(licp, i)) {
+ continue;
+ }
+
+ lidp = XFS_LIC_SLOT(licp, i);
+ lip = lidp->lid_item;
+ if (lip->li_type != XFS_LI_BUF)
+ continue;
+
+ bip = (xfs_buf_log_item_t *)lip;
+ lbp = bip->bli_buf;
+
+ if (XFS_BUF_ADDR(lbp) == bno) {
+ bp = lbp;
+ break; /* Found it */
+ }
+ }
+ licp = licp->lic_next;
+ }
+ }
+ return(bp);
+}
+
+void
+xfs_check_block(
+ xfs_bmbt_block_t *block,
+ xfs_mount_t *mp,
+ int root,
+ short sz)
+{
+ int i, j, dmxr;
+ xfs_bmbt_ptr_t *pp, *thispa; /* pointer to block address */
+ xfs_bmbt_key_t *prevp, *keyp;
+
+ ASSERT(INT_GET(block->bb_level, ARCH_CONVERT) > 0);
+
+ prevp = NULL;
+ for( i = 1; i <= INT_GET(block->bb_numrecs, ARCH_CONVERT);i++) {
+ dmxr = mp->m_bmap_dmxr[0];
+
+ if (root) {
+ keyp = XFS_BMAP_BROOT_KEY_ADDR(block, i, sz);
+ } else {
+ keyp = XFS_BTREE_KEY_ADDR(mp->m_sb.sb_blocksize,
+ xfs_bmbt, block, i, dmxr);
+ }
+
+ if (prevp) {
+ xfs_btree_check_key(XFS_BTNUM_BMAP, prevp, keyp);
+ }
+ prevp = keyp;
+
+ /*
+ * Compare the block numbers to see if there are dups.
+ */
+
+ if (root) {
+ pp = XFS_BMAP_BROOT_PTR_ADDR(block, i, sz);
+ } else {
+ pp = XFS_BTREE_PTR_ADDR(mp->m_sb.sb_blocksize,
+ xfs_bmbt, block, i, dmxr);
+ }
+ for (j = i+1; j <= INT_GET(block->bb_numrecs, ARCH_CONVERT); j++) {
+ if (root) {
+ thispa = XFS_BMAP_BROOT_PTR_ADDR(block, j, sz);
+ } else {
+ thispa = XFS_BTREE_PTR_ADDR(mp->m_sb.sb_blocksize,
+ xfs_bmbt, block, j, dmxr);
+ }
+ if (INT_GET(*thispa, ARCH_CONVERT) ==
+ INT_GET(*pp, ARCH_CONVERT)) {
+ cmn_err(CE_WARN, "%s: thispa(%d) == pp(%d) %Ld",
+ __FUNCTION__, j, i,
+ INT_GET(*thispa, ARCH_CONVERT));
+ panic("%s: ptrs are equal in node\n",
+ __FUNCTION__);
+ }
+ }
+ }
+}
+
+/*
+ * Check that the extents for the inode ip are in the right order in all
+ * btree leaves.
+ */
+
+STATIC void
+xfs_bmap_check_leaf_extents(
+ xfs_btree_cur_t *cur, /* btree cursor or null */
+ xfs_inode_t *ip, /* incore inode pointer */
+ int whichfork) /* data or attr fork */
+{
+ xfs_bmbt_block_t *block; /* current btree block */
+ xfs_fsblock_t bno; /* block # of "block" */
+ xfs_buf_t *bp; /* buffer for "block" */
+ int error; /* error return value */
+ xfs_extnum_t i=0; /* index into the extents list */
+ xfs_ifork_t *ifp; /* fork structure */
+ int level; /* btree level, for checking */
+ xfs_mount_t *mp; /* file system mount structure */
+ xfs_bmbt_ptr_t *pp; /* pointer to block address */
+ xfs_bmbt_rec_t *ep, *lastp; /* extent pointers in block entry */
+ int bp_release = 0;
+
+ if (XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE) {
+ return;
+ }
+
+ bno = NULLFSBLOCK;
+ mp = ip->i_mount;
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+ block = ifp->if_broot;
+ /*
+ * Root level must use BMAP_BROOT_PTR_ADDR macro to get ptr out.
+ */
+ ASSERT(INT_GET(block->bb_level, ARCH_CONVERT) > 0);
+ level = INT_GET(block->bb_level, ARCH_CONVERT);
+ xfs_check_block(block, mp, 1, ifp->if_broot_bytes);
+ pp = XFS_BMAP_BROOT_PTR_ADDR(block, 1, ifp->if_broot_bytes);
+ ASSERT(INT_GET(*pp, ARCH_CONVERT) != NULLDFSBNO);
+ ASSERT(XFS_FSB_TO_AGNO(mp, INT_GET(*pp, ARCH_CONVERT)) < mp->m_sb.sb_agcount);
+ ASSERT(XFS_FSB_TO_AGBNO(mp, INT_GET(*pp, ARCH_CONVERT)) < mp->m_sb.sb_agblocks);
+ bno = INT_GET(*pp, ARCH_CONVERT);
+ /*
+ * Go down the tree until leaf level is reached, following the first
+ * pointer (leftmost) at each level.
+ */
+ while (level-- > 0) {
+ /* See if buf is in cur first */
+ bp = xfs_bmap_get_bp(cur, XFS_FSB_TO_DADDR(mp, bno));
+ if (bp) {
+ bp_release = 0;
+ } else {
+ bp_release = 1;
+ }
+ if (!bp && (error = xfs_btree_read_bufl(mp, NULL, bno, 0, &bp,
+ XFS_BMAP_BTREE_REF)))
+ goto error_norelse;
+ block = XFS_BUF_TO_BMBT_BLOCK(bp);
+ XFS_WANT_CORRUPTED_GOTO(
+ XFS_BMAP_SANITY_CHECK(mp, block, level),
+ error0);
+ if (level == 0)
+ break;
+
+ /*
+ * Check this block for basic sanity (increasing keys and
+ * no duplicate blocks).
+ */
+
+ xfs_check_block(block, mp, 0, 0);
+ pp = XFS_BTREE_PTR_ADDR(mp->m_sb.sb_blocksize, xfs_bmbt, block,
+ 1, mp->m_bmap_dmxr[1]);
+ XFS_WANT_CORRUPTED_GOTO(XFS_FSB_SANITY_CHECK(mp, INT_GET(*pp, ARCH_CONVERT)), error0);
+ bno = INT_GET(*pp, ARCH_CONVERT);
+ if (bp_release) {
+ bp_release = 0;
+ xfs_trans_brelse(NULL, bp);
+ }
+ }
+
+ /*
+ * Here with bp and block set to the leftmost leaf node in the tree.
+ */
+ i = 0;
+
+ /*
+ * Loop over all leaf nodes checking that all extents are in the right order.
+ */
+ lastp = NULL;
+ for (;;) {
+ xfs_bmbt_rec_t *frp;
+ xfs_fsblock_t nextbno;
+ xfs_extnum_t num_recs;
+
+
+ num_recs = INT_GET(block->bb_numrecs, ARCH_CONVERT);
+
+ /*
+ * Read-ahead the next leaf block, if any.
+ */
+
+ nextbno = INT_GET(block->bb_rightsib, ARCH_CONVERT);
+
+ /*
+ * Check all the extents to make sure they are OK.
+ * If we had a previous block, the last entry should
+ * conform with the first entry in this one.
+ */
+
+ frp = XFS_BTREE_REC_ADDR(mp->m_sb.sb_blocksize, xfs_bmbt,
+ block, 1, mp->m_bmap_dmxr[0]);
+
+ for (ep = frp;ep < frp + (num_recs - 1); ep++) {
+ if (lastp) {
+ xfs_btree_check_rec(XFS_BTNUM_BMAP,
+ (void *)lastp, (void *)ep);
+ }
+ xfs_btree_check_rec(XFS_BTNUM_BMAP, (void *)ep,
+ (void *)(ep + 1));
+ }
+ lastp = frp + num_recs - 1; /* For the next iteration */
+
+ i += num_recs;
+ if (bp_release) {
+ bp_release = 0;
+ xfs_trans_brelse(NULL, bp);
+ }
+ bno = nextbno;
+ /*
+ * If we've reached the end, stop.
+ */
+ if (bno == NULLFSBLOCK)
+ break;
+
+ bp = xfs_bmap_get_bp(cur, XFS_FSB_TO_DADDR(mp, bno));
+ if (bp) {
+ bp_release = 0;
+ } else {
+ bp_release = 1;
+ }
+ if (!bp && (error = xfs_btree_read_bufl(mp, NULL, bno, 0, &bp,
+ XFS_BMAP_BTREE_REF)))
+ goto error_norelse;
+ block = XFS_BUF_TO_BMBT_BLOCK(bp);
+ }
+ if (bp_release) {
+ bp_release = 0;
+ xfs_trans_brelse(NULL, bp);
+ }
+ return;
+
+error0:
+ cmn_err(CE_WARN, "%s: at error0", __FUNCTION__);
+ if (bp_release)
+ xfs_trans_brelse(NULL, bp);
+error_norelse:
+ cmn_err(CE_WARN, "%s: BAD after btree leaves for %d extents",
+ i, __FUNCTION__);
+ panic("%s: CORRUPTED BTREE OR SOMETHING", __FUNCTION__);
+ return;
+}
+#endif
+
+/*
+ * Count fsblocks of the given fork.
+ */
+int /* error */
+xfs_bmap_count_blocks(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_inode_t *ip, /* incore inode */
+ int whichfork, /* data or attr fork */
+ int *count) /* out: count of blocks */
+{
+ xfs_bmbt_block_t *block; /* current btree block */
+ xfs_fsblock_t bno; /* block # of "block" */
+ xfs_ifork_t *ifp; /* fork structure */
+ int level; /* btree level, for checking */
+ xfs_mount_t *mp; /* file system mount structure */
+ xfs_bmbt_ptr_t *pp; /* pointer to block address */
+
+ bno = NULLFSBLOCK;
+ mp = ip->i_mount;
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+ if ( XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_EXTENTS ) {
+ if (unlikely(xfs_bmap_count_leaves(ifp->if_u1.if_extents,
+ ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t),
+ count) < 0)) {
+ XFS_ERROR_REPORT("xfs_bmap_count_blocks(1)",
+ XFS_ERRLEVEL_LOW, mp);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ return 0;
+ }
+
+ /*
+ * Root level must use BMAP_BROOT_PTR_ADDR macro to get ptr out.
+ */
+ block = ifp->if_broot;
+ ASSERT(INT_GET(block->bb_level, ARCH_CONVERT) > 0);
+ level = INT_GET(block->bb_level, ARCH_CONVERT);
+ pp = XFS_BMAP_BROOT_PTR_ADDR(block, 1, ifp->if_broot_bytes);
+ ASSERT(INT_GET(*pp, ARCH_CONVERT) != NULLDFSBNO);
+ ASSERT(XFS_FSB_TO_AGNO(mp, INT_GET(*pp, ARCH_CONVERT)) < mp->m_sb.sb_agcount);
+ ASSERT(XFS_FSB_TO_AGBNO(mp, INT_GET(*pp, ARCH_CONVERT)) < mp->m_sb.sb_agblocks);
+ bno = INT_GET(*pp, ARCH_CONVERT);
+
+ if (unlikely(xfs_bmap_count_tree(mp, tp, bno, level, count) < 0)) {
+ XFS_ERROR_REPORT("xfs_bmap_count_blocks(2)", XFS_ERRLEVEL_LOW,
+ mp);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+
+ return 0;
+}
+
+/*
+ * Recursively walks each level of a btree
+ * to count total fsblocks is use.
+ */
+int /* error */
+xfs_bmap_count_tree(
+ xfs_mount_t *mp, /* file system mount point */
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_fsblock_t blockno, /* file system block number */
+ int levelin, /* level in btree */
+ int *count) /* Count of blocks */
+{
+ int error;
+ xfs_buf_t *bp, *nbp;
+ int level = levelin;
+ xfs_bmbt_ptr_t *pp;
+ xfs_fsblock_t bno = blockno;
+ xfs_fsblock_t nextbno;
+ xfs_bmbt_block_t *block, *nextblock;
+ int numrecs;
+ xfs_bmbt_rec_t *frp;
+
+ if ((error = xfs_btree_read_bufl(mp, tp, bno, 0, &bp, XFS_BMAP_BTREE_REF)))
+ return error;
+ *count += 1;
+ block = XFS_BUF_TO_BMBT_BLOCK(bp);
+
+ if (--level) {
+ /* Not at node above leafs, count this level of nodes */
+ nextbno = INT_GET(block->bb_rightsib, ARCH_CONVERT);
+ while (nextbno != NULLFSBLOCK) {
+ if ((error = xfs_btree_read_bufl(mp, tp, nextbno,
+ 0, &nbp, XFS_BMAP_BTREE_REF)))
+ return error;
+ *count += 1;
+ nextblock = XFS_BUF_TO_BMBT_BLOCK(nbp);
+ nextbno = INT_GET(nextblock->bb_rightsib, ARCH_CONVERT);
+ xfs_trans_brelse(tp, nbp);
+ }
+
+ /* Dive to the next level */
+ pp = XFS_BTREE_PTR_ADDR(mp->m_sb.sb_blocksize,
+ xfs_bmbt, block, 1, mp->m_bmap_dmxr[1]);
+ bno = INT_GET(*pp, ARCH_CONVERT);
+ if (unlikely((error =
+ xfs_bmap_count_tree(mp, tp, bno, level, count)) < 0)) {
+ xfs_trans_brelse(tp, bp);
+ XFS_ERROR_REPORT("xfs_bmap_count_tree(1)",
+ XFS_ERRLEVEL_LOW, mp);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ xfs_trans_brelse(tp, bp);
+ } else {
+ /* count all level 1 nodes and their leaves */
+ for (;;) {
+ nextbno = INT_GET(block->bb_rightsib, ARCH_CONVERT);
+ numrecs = INT_GET(block->bb_numrecs, ARCH_CONVERT);
+ frp = XFS_BTREE_REC_ADDR(mp->m_sb.sb_blocksize,
+ xfs_bmbt, block, 1, mp->m_bmap_dmxr[0]);
+ if (unlikely(xfs_bmap_count_leaves(frp, numrecs, count) < 0)) {
+ xfs_trans_brelse(tp, bp);
+ XFS_ERROR_REPORT("xfs_bmap_count_tree(2)",
+ XFS_ERRLEVEL_LOW, mp);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ xfs_trans_brelse(tp, bp);
+ if (nextbno == NULLFSBLOCK)
+ break;
+ bno = nextbno;
+ if ((error = xfs_btree_read_bufl(mp, tp, bno, 0, &bp,
+ XFS_BMAP_BTREE_REF)))
+ return error;
+ *count += 1;
+ block = XFS_BUF_TO_BMBT_BLOCK(bp);
+ }
+ }
+ return 0;
+}
+
+/*
+ * Count leaf blocks given a pointer to an extent list.
+ */
+int
+xfs_bmap_count_leaves(
+ xfs_bmbt_rec_t *frp,
+ int numrecs,
+ int *count)
+{
+ int b;
+
+ for ( b = 1; b <= numrecs; b++, frp++)
+ *count += xfs_bmbt_disk_get_blockcount(frp);
+ return 0;
+}
diff --git a/fs/xfs/xfs_bmap.h b/fs/xfs/xfs_bmap.h
new file mode 100644
index 00000000000000..f1bc22fb26aec4
--- /dev/null
+++ b/fs/xfs/xfs_bmap.h
@@ -0,0 +1,379 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_BMAP_H__
+#define __XFS_BMAP_H__
+
+struct getbmap;
+struct xfs_bmbt_irec;
+struct xfs_inode;
+struct xfs_mount;
+struct xfs_trans;
+
+/*
+ * List of extents to be free "later".
+ * The list is kept sorted on xbf_startblock.
+ */
+typedef struct xfs_bmap_free_item
+{
+ xfs_fsblock_t xbfi_startblock;/* starting fs block number */
+ xfs_extlen_t xbfi_blockcount;/* number of blocks in extent */
+ struct xfs_bmap_free_item *xbfi_next; /* link to next entry */
+} xfs_bmap_free_item_t;
+
+/*
+ * Header for free extent list.
+ */
+typedef struct xfs_bmap_free
+{
+ xfs_bmap_free_item_t *xbf_first; /* list of to-be-free extents */
+ int xbf_count; /* count of items on list */
+ int xbf_low; /* kludge: alloc in low mode */
+} xfs_bmap_free_t;
+
+#define XFS_BMAP_MAX_NMAP 4
+
+/*
+ * Flags for xfs_bmapi
+ */
+#define XFS_BMAPI_WRITE 0x001 /* write operation: allocate space */
+#define XFS_BMAPI_DELAY 0x002 /* delayed write operation */
+#define XFS_BMAPI_ENTIRE 0x004 /* return entire extent, not trimmed */
+#define XFS_BMAPI_METADATA 0x008 /* mapping metadata not user data */
+#define XFS_BMAPI_EXACT 0x010 /* allocate only to spec'd bounds */
+#define XFS_BMAPI_ATTRFORK 0x020 /* use attribute fork not data */
+#define XFS_BMAPI_ASYNC 0x040 /* bunmapi xactions can be async */
+#define XFS_BMAPI_RSVBLOCKS 0x080 /* OK to alloc. reserved data blocks */
+#define XFS_BMAPI_PREALLOC 0x100 /* preallocation op: unwritten space */
+#define XFS_BMAPI_IGSTATE 0x200 /* Ignore state - */
+ /* combine contig. space */
+#define XFS_BMAPI_CONTIG 0x400 /* must allocate only one extent */
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BMAPI_AFLAG)
+int xfs_bmapi_aflag(int w);
+#define XFS_BMAPI_AFLAG(w) xfs_bmapi_aflag(w)
+#else
+#define XFS_BMAPI_AFLAG(w) ((w) == XFS_ATTR_FORK ? XFS_BMAPI_ATTRFORK : 0)
+#endif
+
+/*
+ * Special values for xfs_bmbt_irec_t br_startblock field.
+ */
+#define DELAYSTARTBLOCK ((xfs_fsblock_t)-1LL)
+#define HOLESTARTBLOCK ((xfs_fsblock_t)-2LL)
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BMAP_INIT)
+void xfs_bmap_init(xfs_bmap_free_t *flp, xfs_fsblock_t *fbp);
+#define XFS_BMAP_INIT(flp,fbp) xfs_bmap_init(flp,fbp)
+#else
+#define XFS_BMAP_INIT(flp,fbp) \
+ ((flp)->xbf_first = NULL, (flp)->xbf_count = 0, \
+ (flp)->xbf_low = 0, *(fbp) = NULLFSBLOCK)
+#endif
+
+/*
+ * Argument structure for xfs_bmap_alloc.
+ */
+typedef struct xfs_bmalloca {
+ xfs_fsblock_t firstblock; /* i/o first block allocated */
+ xfs_fsblock_t rval; /* starting block of new extent */
+ xfs_fileoff_t off; /* offset in file filling in */
+ struct xfs_trans *tp; /* transaction pointer */
+ struct xfs_inode *ip; /* incore inode pointer */
+ struct xfs_bmbt_irec *prevp; /* extent before the new one */
+ struct xfs_bmbt_irec *gotp; /* extent after, or delayed */
+ xfs_extlen_t alen; /* i/o length asked/allocated */
+ xfs_extlen_t total; /* total blocks needed for xaction */
+ xfs_extlen_t minlen; /* mininum allocation size (blocks) */
+ xfs_extlen_t minleft; /* amount must be left after alloc */
+ char eof; /* set if allocating past last extent */
+ char wasdel; /* replacing a delayed allocation */
+ char userdata;/* set if is user data */
+ char low; /* low on space, using seq'l ags */
+ char aeof; /* allocated space at eof */
+} xfs_bmalloca_t;
+
+#ifdef __KERNEL__
+
+#if defined(XFS_BMAP_TRACE)
+/*
+ * Trace operations for bmap extent tracing
+ */
+#define XFS_BMAP_KTRACE_DELETE 1
+#define XFS_BMAP_KTRACE_INSERT 2
+#define XFS_BMAP_KTRACE_PRE_UP 3
+#define XFS_BMAP_KTRACE_POST_UP 4
+
+#define XFS_BMAP_TRACE_SIZE 4096 /* size of global trace buffer */
+#define XFS_BMAP_KTRACE_SIZE 32 /* size of per-inode trace buffer */
+extern ktrace_t *xfs_bmap_trace_buf;
+
+/*
+ * Add bmap trace insert entries for all the contents of the extent list.
+ */
+void
+xfs_bmap_trace_exlist(
+ char *fname, /* function name */
+ struct xfs_inode *ip, /* incore inode pointer */
+ xfs_extnum_t cnt, /* count of entries in list */
+ int whichfork); /* data or attr fork */
+#else
+#define xfs_bmap_trace_exlist(f,ip,c,w)
+#endif
+
+/*
+ * Convert inode from non-attributed to attributed.
+ * Must not be in a transaction, ip must not be locked.
+ */
+int /* error code */
+xfs_bmap_add_attrfork(
+ struct xfs_inode *ip, /* incore inode pointer */
+ int rsvd); /* flag for reserved block allocation */
+
+/*
+ * Add the extent to the list of extents to be free at transaction end.
+ * The list is maintained sorted (by block number).
+ */
+void
+xfs_bmap_add_free(
+ xfs_fsblock_t bno, /* fs block number of extent */
+ xfs_filblks_t len, /* length of extent */
+ xfs_bmap_free_t *flist, /* list of extents */
+ struct xfs_mount *mp); /* mount point structure */
+
+/*
+ * Routine to clean up the free list data structure when
+ * an error occurs during a transaction.
+ */
+void
+xfs_bmap_cancel(
+ xfs_bmap_free_t *flist); /* free list to clean up */
+
+/*
+ * Compute and fill in the value of the maximum depth of a bmap btree
+ * in this filesystem. Done once, during mount.
+ */
+void
+xfs_bmap_compute_maxlevels(
+ struct xfs_mount *mp, /* file system mount structure */
+ int whichfork); /* data or attr fork */
+
+/*
+ * Routine to be called at transaction's end by xfs_bmapi, xfs_bunmapi
+ * caller. Frees all the extents that need freeing, which must be done
+ * last due to locking considerations.
+ *
+ * Return 1 if the given transaction was committed and a new one allocated,
+ * and 0 otherwise.
+ */
+int /* error */
+xfs_bmap_finish(
+ struct xfs_trans **tp, /* transaction pointer addr */
+ xfs_bmap_free_t *flist, /* i/o: list extents to free */
+ xfs_fsblock_t firstblock, /* controlled a.g. for allocs */
+ int *committed); /* xact committed or not */
+
+/*
+ * Returns the file-relative block number of the first unused block in the file.
+ * This is the lowest-address hole if the file has holes, else the first block
+ * past the end of file.
+ */
+int /* error */
+xfs_bmap_first_unused(
+ struct xfs_trans *tp, /* transaction pointer */
+ struct xfs_inode *ip, /* incore inode */
+ xfs_extlen_t len, /* size of hole to find */
+ xfs_fileoff_t *unused, /* unused block num */
+ int whichfork); /* data or attr fork */
+
+/*
+ * Returns the file-relative block number of the last block + 1 before
+ * last_block (input value) in the file.
+ * This is not based on i_size, it is based on the extent list.
+ * Returns 0 for local files, as they do not have an extent list.
+ */
+int /* error */
+xfs_bmap_last_before(
+ struct xfs_trans *tp, /* transaction pointer */
+ struct xfs_inode *ip, /* incore inode */
+ xfs_fileoff_t *last_block, /* last block */
+ int whichfork); /* data or attr fork */
+
+/*
+ * Returns the file-relative block number of the first block past eof in
+ * the file. This is not based on i_size, it is based on the extent list.
+ * Returns 0 for local files, as they do not have an extent list.
+ */
+int /* error */
+xfs_bmap_last_offset(
+ struct xfs_trans *tp, /* transaction pointer */
+ struct xfs_inode *ip, /* incore inode */
+ xfs_fileoff_t *unused, /* last block num */
+ int whichfork); /* data or attr fork */
+
+/*
+ * Returns whether the selected fork of the inode has exactly one
+ * block or not. For the data fork we check this matches di_size,
+ * implying the file's range is 0..bsize-1.
+ */
+int
+xfs_bmap_one_block(
+ struct xfs_inode *ip, /* incore inode */
+ int whichfork); /* data or attr fork */
+
+/*
+ * Read in the extents to iu_extents.
+ * All inode fields are set up by caller, we just traverse the btree
+ * and copy the records in.
+ */
+int /* error */
+xfs_bmap_read_extents(
+ struct xfs_trans *tp, /* transaction pointer */
+ struct xfs_inode *ip, /* incore inode */
+ int whichfork); /* data or attr fork */
+
+/*
+ * Map file blocks to filesystem blocks.
+ * File range is given by the bno/len pair.
+ * Adds blocks to file if a write ("flags & XFS_BMAPI_WRITE" set)
+ * into a hole or past eof.
+ * Only allocates blocks from a single allocation group,
+ * to avoid locking problems.
+ * The returned value in "firstblock" from the first call in a transaction
+ * must be remembered and presented to subsequent calls in "firstblock".
+ * An upper bound for the number of blocks to be allocated is supplied to
+ * the first call in "total"; if no allocation group has that many free
+ * blocks then the call will fail (return NULLFSBLOCK in "firstblock").
+ */
+int /* error */
+xfs_bmapi(
+ struct xfs_trans *tp, /* transaction pointer */
+ struct xfs_inode *ip, /* incore inode */
+ xfs_fileoff_t bno, /* starting file offs. mapped */
+ xfs_filblks_t len, /* length to map in file */
+ int flags, /* XFS_BMAPI_... */
+ xfs_fsblock_t *firstblock, /* first allocated block
+ controls a.g. for allocs */
+ xfs_extlen_t total, /* total blocks needed */
+ struct xfs_bmbt_irec *mval, /* output: map values */
+ int *nmap, /* i/o: mval size/count */
+ xfs_bmap_free_t *flist); /* i/o: list extents to free */
+
+/*
+ * Map file blocks to filesystem blocks, simple version.
+ * One block only, read-only.
+ * For flags, only the XFS_BMAPI_ATTRFORK flag is examined.
+ * For the other flag values, the effect is as if XFS_BMAPI_METADATA
+ * was set and all the others were clear.
+ */
+int /* error */
+xfs_bmapi_single(
+ struct xfs_trans *tp, /* transaction pointer */
+ struct xfs_inode *ip, /* incore inode */
+ int whichfork, /* data or attr fork */
+ xfs_fsblock_t *fsb, /* output: mapped block */
+ xfs_fileoff_t bno); /* starting file offs. mapped */
+
+/*
+ * Unmap (remove) blocks from a file.
+ * If nexts is nonzero then the number of extents to remove is limited to
+ * that value. If not all extents in the block range can be removed then
+ * *done is set.
+ */
+int /* error */
+xfs_bunmapi(
+ struct xfs_trans *tp, /* transaction pointer */
+ struct xfs_inode *ip, /* incore inode */
+ xfs_fileoff_t bno, /* starting offset to unmap */
+ xfs_filblks_t len, /* length to unmap in file */
+ int flags, /* XFS_BMAPI_... */
+ xfs_extnum_t nexts, /* number of extents max */
+ xfs_fsblock_t *firstblock, /* first allocated block
+ controls a.g. for allocs */
+ xfs_bmap_free_t *flist, /* i/o: list extents to free */
+ int *done); /* set if not done yet */
+
+/*
+ * Fcntl interface to xfs_bmapi.
+ */
+int /* error code */
+xfs_getbmap(
+ bhv_desc_t *bdp, /* XFS behavior descriptor*/
+ struct getbmap *bmv, /* user bmap structure */
+ void __user *ap, /* pointer to user's array */
+ int iflags); /* interface flags */
+
+/*
+ * Check the last inode extent to determine whether this allocation will result
+ * in blocks being allocated at the end of the file. When we allocate new data
+ * blocks at the end of the file which do not start at the previous data block,
+ * we will try to align the new blocks at stripe unit boundaries.
+ */
+int
+xfs_bmap_isaeof(
+ struct xfs_inode *ip,
+ xfs_fileoff_t off,
+ int whichfork,
+ char *aeof);
+
+/*
+ * Check if the endoff is outside the last extent. If so the caller will grow
+ * the allocation to a stripe unit boundary
+ */
+int
+xfs_bmap_eof(
+ struct xfs_inode *ip,
+ xfs_fileoff_t endoff,
+ int whichfork,
+ int *eof);
+
+/*
+ * Count fsblocks of the given fork.
+ */
+int
+xfs_bmap_count_blocks(
+ xfs_trans_t *tp,
+ struct xfs_inode *ip,
+ int whichfork,
+ int *count);
+
+/*
+ * Check an extent list, which has just been read, for
+ * any bit in the extent flag field.
+ */
+int
+xfs_check_nostate_extents(
+ xfs_bmbt_rec_t *ep,
+ xfs_extnum_t num);
+
+#endif /* __KERNEL__ */
+
+#endif /* __XFS_BMAP_H__ */
diff --git a/fs/xfs/xfs_bmap_btree.c b/fs/xfs/xfs_bmap_btree.c
new file mode 100644
index 00000000000000..0ed189102257d0
--- /dev/null
+++ b/fs/xfs/xfs_bmap_btree.c
@@ -0,0 +1,2807 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_itable.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode_item.h"
+#include "xfs_inode.h"
+#include "xfs_alloc.h"
+#include "xfs_bit.h"
+#include "xfs_bmap.h"
+#include "xfs_error.h"
+#include "xfs_quota.h"
+
+#if defined(XFS_BMBT_TRACE)
+ktrace_t *xfs_bmbt_trace_buf;
+#endif
+
+/*
+ * Prototypes for internal btree functions.
+ */
+
+
+STATIC int xfs_bmbt_killroot(xfs_btree_cur_t *);
+STATIC void xfs_bmbt_log_keys(xfs_btree_cur_t *, xfs_buf_t *, int, int);
+STATIC void xfs_bmbt_log_ptrs(xfs_btree_cur_t *, xfs_buf_t *, int, int);
+STATIC int xfs_bmbt_lshift(xfs_btree_cur_t *, int, int *);
+STATIC int xfs_bmbt_rshift(xfs_btree_cur_t *, int, int *);
+STATIC int xfs_bmbt_split(xfs_btree_cur_t *, int, xfs_fsblock_t *,
+ xfs_bmbt_key_t *, xfs_btree_cur_t **, int *);
+STATIC int xfs_bmbt_updkey(xfs_btree_cur_t *, xfs_bmbt_key_t *, int);
+
+
+#if defined(XFS_BMBT_TRACE)
+
+static char ARGS[] = "args";
+static char ENTRY[] = "entry";
+static char ERROR[] = "error";
+#undef EXIT
+static char EXIT[] = "exit";
+
+/*
+ * Add a trace buffer entry for the arguments given to the routine,
+ * generic form.
+ */
+STATIC void
+xfs_bmbt_trace_enter(
+ char *func,
+ xfs_btree_cur_t *cur,
+ char *s,
+ int type,
+ int line,
+ __psunsigned_t a0,
+ __psunsigned_t a1,
+ __psunsigned_t a2,
+ __psunsigned_t a3,
+ __psunsigned_t a4,
+ __psunsigned_t a5,
+ __psunsigned_t a6,
+ __psunsigned_t a7,
+ __psunsigned_t a8,
+ __psunsigned_t a9,
+ __psunsigned_t a10)
+{
+ xfs_inode_t *ip;
+ int whichfork;
+
+ ip = cur->bc_private.b.ip;
+ whichfork = cur->bc_private.b.whichfork;
+ ktrace_enter(xfs_bmbt_trace_buf,
+ (void *)((__psint_t)type | (whichfork << 8) | (line << 16)),
+ (void *)func, (void *)s, (void *)ip, (void *)cur,
+ (void *)a0, (void *)a1, (void *)a2, (void *)a3,
+ (void *)a4, (void *)a5, (void *)a6, (void *)a7,
+ (void *)a8, (void *)a9, (void *)a10);
+ ASSERT(ip->i_btrace);
+ ktrace_enter(ip->i_btrace,
+ (void *)((__psint_t)type | (whichfork << 8) | (line << 16)),
+ (void *)func, (void *)s, (void *)ip, (void *)cur,
+ (void *)a0, (void *)a1, (void *)a2, (void *)a3,
+ (void *)a4, (void *)a5, (void *)a6, (void *)a7,
+ (void *)a8, (void *)a9, (void *)a10);
+}
+/*
+ * Add a trace buffer entry for arguments, for a buffer & 1 integer arg.
+ */
+STATIC void
+xfs_bmbt_trace_argbi(
+ char *func,
+ xfs_btree_cur_t *cur,
+ xfs_buf_t *b,
+ int i,
+ int line)
+{
+ xfs_bmbt_trace_enter(func, cur, ARGS, XFS_BMBT_KTRACE_ARGBI, line,
+ (__psunsigned_t)b, i, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0);
+}
+
+/*
+ * Add a trace buffer entry for arguments, for a buffer & 2 integer args.
+ */
+STATIC void
+xfs_bmbt_trace_argbii(
+ char *func,
+ xfs_btree_cur_t *cur,
+ xfs_buf_t *b,
+ int i0,
+ int i1,
+ int line)
+{
+ xfs_bmbt_trace_enter(func, cur, ARGS, XFS_BMBT_KTRACE_ARGBII, line,
+ (__psunsigned_t)b, i0, i1, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0);
+}
+
+/*
+ * Add a trace buffer entry for arguments, for 3 block-length args
+ * and an integer arg.
+ */
+STATIC void
+xfs_bmbt_trace_argfffi(
+ char *func,
+ xfs_btree_cur_t *cur,
+ xfs_dfiloff_t o,
+ xfs_dfsbno_t b,
+ xfs_dfilblks_t i,
+ int j,
+ int line)
+{
+ xfs_bmbt_trace_enter(func, cur, ARGS, XFS_BMBT_KTRACE_ARGFFFI, line,
+ o >> 32, (int)o, b >> 32, (int)b,
+ i >> 32, (int)i, (int)j, 0,
+ 0, 0, 0);
+}
+
+/*
+ * Add a trace buffer entry for arguments, for one integer arg.
+ */
+STATIC void
+xfs_bmbt_trace_argi(
+ char *func,
+ xfs_btree_cur_t *cur,
+ int i,
+ int line)
+{
+ xfs_bmbt_trace_enter(func, cur, ARGS, XFS_BMBT_KTRACE_ARGI, line,
+ i, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0);
+}
+
+/*
+ * Add a trace buffer entry for arguments, for int, fsblock, key.
+ */
+STATIC void
+xfs_bmbt_trace_argifk(
+ char *func,
+ xfs_btree_cur_t *cur,
+ int i,
+ xfs_fsblock_t f,
+ xfs_bmbt_key_t *k,
+ int line)
+{
+ xfs_dfsbno_t d;
+ xfs_dfiloff_t o;
+
+ d = (xfs_dfsbno_t)f;
+ o = INT_GET(k->br_startoff, ARCH_CONVERT);
+ xfs_bmbt_trace_enter(func, cur, ARGS, XFS_BMBT_KTRACE_ARGIFK, line,
+ i, d >> 32, (int)d, o >> 32,
+ (int)o, 0, 0, 0,
+ 0, 0, 0);
+}
+
+/*
+ * Add a trace buffer entry for arguments, for int, fsblock, rec.
+ */
+STATIC void
+xfs_bmbt_trace_argifr(
+ char *func,
+ xfs_btree_cur_t *cur,
+ int i,
+ xfs_fsblock_t f,
+ xfs_bmbt_rec_t *r,
+ int line)
+{
+ xfs_dfsbno_t b;
+ xfs_dfilblks_t c;
+ xfs_dfsbno_t d;
+ xfs_dfiloff_t o;
+ xfs_bmbt_irec_t s;
+
+ d = (xfs_dfsbno_t)f;
+ xfs_bmbt_disk_get_all(r, &s);
+ o = (xfs_dfiloff_t)s.br_startoff;
+ b = (xfs_dfsbno_t)s.br_startblock;
+ c = s.br_blockcount;
+ xfs_bmbt_trace_enter(func, cur, ARGS, XFS_BMBT_KTRACE_ARGIFR, line,
+ i, d >> 32, (int)d, o >> 32,
+ (int)o, b >> 32, (int)b, c >> 32,
+ (int)c, 0, 0);
+}
+
+/*
+ * Add a trace buffer entry for arguments, for int, key.
+ */
+STATIC void
+xfs_bmbt_trace_argik(
+ char *func,
+ xfs_btree_cur_t *cur,
+ int i,
+ xfs_bmbt_key_t *k,
+ int line)
+{
+ xfs_dfiloff_t o;
+
+ o = INT_GET(k->br_startoff, ARCH_CONVERT);
+ xfs_bmbt_trace_enter(func, cur, ARGS, XFS_BMBT_KTRACE_ARGIFK, line,
+ i, o >> 32, (int)o, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0);
+}
+
+/*
+ * Add a trace buffer entry for the cursor/operation.
+ */
+STATIC void
+xfs_bmbt_trace_cursor(
+ char *func,
+ xfs_btree_cur_t *cur,
+ char *s,
+ int line)
+{
+ xfs_bmbt_rec_t r;
+
+ xfs_bmbt_set_all(&r, &cur->bc_rec.b);
+ xfs_bmbt_trace_enter(func, cur, s, XFS_BMBT_KTRACE_CUR, line,
+ (cur->bc_nlevels << 24) | (cur->bc_private.b.flags << 16) |
+ cur->bc_private.b.allocated,
+ INT_GET(r.l0, ARCH_CONVERT) >> 32, (int)INT_GET(r.l0, ARCH_CONVERT), INT_GET(r.l1, ARCH_CONVERT) >> 32, (int)INT_GET(r.l1, ARCH_CONVERT),
+ (unsigned long)cur->bc_bufs[0], (unsigned long)cur->bc_bufs[1],
+ (unsigned long)cur->bc_bufs[2], (unsigned long)cur->bc_bufs[3],
+ (cur->bc_ptrs[0] << 16) | cur->bc_ptrs[1],
+ (cur->bc_ptrs[2] << 16) | cur->bc_ptrs[3]);
+}
+
+#define XFS_BMBT_TRACE_ARGBI(c,b,i) \
+ xfs_bmbt_trace_argbi(fname, c, b, i, __LINE__)
+#define XFS_BMBT_TRACE_ARGBII(c,b,i,j) \
+ xfs_bmbt_trace_argbii(fname, c, b, i, j, __LINE__)
+#define XFS_BMBT_TRACE_ARGFFFI(c,o,b,i,j) \
+ xfs_bmbt_trace_argfffi(fname, c, o, b, i, j, __LINE__)
+#define XFS_BMBT_TRACE_ARGI(c,i) \
+ xfs_bmbt_trace_argi(fname, c, i, __LINE__)
+#define XFS_BMBT_TRACE_ARGIFK(c,i,f,k) \
+ xfs_bmbt_trace_argifk(fname, c, i, f, k, __LINE__)
+#define XFS_BMBT_TRACE_ARGIFR(c,i,f,r) \
+ xfs_bmbt_trace_argifr(fname, c, i, f, r, __LINE__)
+#define XFS_BMBT_TRACE_ARGIK(c,i,k) \
+ xfs_bmbt_trace_argik(fname, c, i, k, __LINE__)
+#define XFS_BMBT_TRACE_CURSOR(c,s) \
+ xfs_bmbt_trace_cursor(fname, c, s, __LINE__)
+#else
+#define XFS_BMBT_TRACE_ARGBI(c,b,i)
+#define XFS_BMBT_TRACE_ARGBII(c,b,i,j)
+#define XFS_BMBT_TRACE_ARGFFFI(c,o,b,i,j)
+#define XFS_BMBT_TRACE_ARGI(c,i)
+#define XFS_BMBT_TRACE_ARGIFK(c,i,f,k)
+#define XFS_BMBT_TRACE_ARGIFR(c,i,f,r)
+#define XFS_BMBT_TRACE_ARGIK(c,i,k)
+#define XFS_BMBT_TRACE_CURSOR(c,s)
+#endif /* XFS_BMBT_TRACE */
+
+
+/*
+ * Internal functions.
+ */
+
+/*
+ * Delete record pointed to by cur/level.
+ */
+STATIC int /* error */
+xfs_bmbt_delrec(
+ xfs_btree_cur_t *cur,
+ int level,
+ int *stat) /* success/failure */
+{
+ xfs_bmbt_block_t *block; /* bmap btree block */
+ xfs_fsblock_t bno; /* fs-relative block number */
+ xfs_buf_t *bp; /* buffer for block */
+ int error; /* error return value */
+#ifdef XFS_BMBT_TRACE
+ static char fname[] = "xfs_bmbt_delrec";
+#endif
+ int i; /* loop counter */
+ int j; /* temp state */
+ xfs_bmbt_key_t key; /* bmap btree key */
+ xfs_bmbt_key_t *kp=NULL; /* pointer to bmap btree key */
+ xfs_fsblock_t lbno; /* left sibling block number */
+ xfs_buf_t *lbp; /* left buffer pointer */
+ xfs_bmbt_block_t *left; /* left btree block */
+ xfs_bmbt_key_t *lkp; /* left btree key */
+ xfs_bmbt_ptr_t *lpp; /* left address pointer */
+ int lrecs=0; /* left record count */
+ xfs_bmbt_rec_t *lrp; /* left record pointer */
+ xfs_mount_t *mp; /* file system mount point */
+ xfs_bmbt_ptr_t *pp; /* pointer to bmap block addr */
+ int ptr; /* key/record index */
+ xfs_fsblock_t rbno; /* right sibling block number */
+ xfs_buf_t *rbp; /* right buffer pointer */
+ xfs_bmbt_block_t *right; /* right btree block */
+ xfs_bmbt_key_t *rkp; /* right btree key */
+ xfs_bmbt_rec_t *rp; /* pointer to bmap btree rec */
+ xfs_bmbt_ptr_t *rpp; /* right address pointer */
+ xfs_bmbt_block_t *rrblock; /* right-right btree block */
+ xfs_buf_t *rrbp; /* right-right buffer pointer */
+ int rrecs=0; /* right record count */
+ xfs_bmbt_rec_t *rrp; /* right record pointer */
+ xfs_btree_cur_t *tcur; /* temporary btree cursor */
+ int numrecs; /* temporary numrec count */
+ int numlrecs, numrrecs;
+
+ XFS_BMBT_TRACE_CURSOR(cur, ENTRY);
+ XFS_BMBT_TRACE_ARGI(cur, level);
+ ptr = cur->bc_ptrs[level];
+ tcur = (xfs_btree_cur_t *)0;
+ if (ptr == 0) {
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *stat = 0;
+ return 0;
+ }
+ block = xfs_bmbt_get_block(cur, level, &bp);
+ numrecs = INT_GET(block->bb_numrecs, ARCH_CONVERT);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_lblock(cur, block, level, bp))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ goto error0;
+ }
+#endif
+ if (ptr > numrecs) {
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *stat = 0;
+ return 0;
+ }
+ XFS_STATS_INC(xs_bmbt_delrec);
+ if (level > 0) {
+ kp = XFS_BMAP_KEY_IADDR(block, 1, cur);
+ pp = XFS_BMAP_PTR_IADDR(block, 1, cur);
+#ifdef DEBUG
+ for (i = ptr; i < numrecs; i++) {
+ if ((error = xfs_btree_check_lptr(cur, INT_GET(pp[i], ARCH_CONVERT), level))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ goto error0;
+ }
+ }
+#endif
+ if (ptr < numrecs) {
+ memmove(&kp[ptr - 1], &kp[ptr],
+ (numrecs - ptr) * sizeof(*kp));
+ memmove(&pp[ptr - 1], &pp[ptr], /* INT_: direct copy */
+ (numrecs - ptr) * sizeof(*pp));
+ xfs_bmbt_log_ptrs(cur, bp, ptr, numrecs - 1);
+ xfs_bmbt_log_keys(cur, bp, ptr, numrecs - 1);
+ }
+ } else {
+ rp = XFS_BMAP_REC_IADDR(block, 1, cur);
+ if (ptr < numrecs) {
+ memmove(&rp[ptr - 1], &rp[ptr],
+ (numrecs - ptr) * sizeof(*rp));
+ xfs_bmbt_log_recs(cur, bp, ptr, numrecs - 1);
+ }
+ if (ptr == 1) {
+ INT_SET(key.br_startoff, ARCH_CONVERT, xfs_bmbt_disk_get_startoff(rp));
+ kp = &key;
+ }
+ }
+ numrecs--;
+ INT_SET(block->bb_numrecs, ARCH_CONVERT, numrecs);
+ xfs_bmbt_log_block(cur, bp, XFS_BB_NUMRECS);
+ /*
+ * We're at the root level.
+ * First, shrink the root block in-memory.
+ * Try to get rid of the next level down.
+ * If we can't then there's nothing left to do.
+ */
+ if (level == cur->bc_nlevels - 1) {
+ xfs_iroot_realloc(cur->bc_private.b.ip, -1,
+ cur->bc_private.b.whichfork);
+ if ((error = xfs_bmbt_killroot(cur))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ goto error0;
+ }
+ if (level > 0 && (error = xfs_bmbt_decrement(cur, level, &j))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ goto error0;
+ }
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *stat = 1;
+ return 0;
+ }
+ if (ptr == 1 && (error = xfs_bmbt_updkey(cur, kp, level + 1))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ goto error0;
+ }
+ if (numrecs >= XFS_BMAP_BLOCK_IMINRECS(level, cur)) {
+ if (level > 0 && (error = xfs_bmbt_decrement(cur, level, &j))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ goto error0;
+ }
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *stat = 1;
+ return 0;
+ }
+ rbno = INT_GET(block->bb_rightsib, ARCH_CONVERT);
+ lbno = INT_GET(block->bb_leftsib, ARCH_CONVERT);
+ /*
+ * One child of root, need to get a chance to copy its contents
+ * into the root and delete it. Can't go up to next level,
+ * there's nothing to delete there.
+ */
+ if (lbno == NULLFSBLOCK && rbno == NULLFSBLOCK &&
+ level == cur->bc_nlevels - 2) {
+ if ((error = xfs_bmbt_killroot(cur))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ goto error0;
+ }
+ if (level > 0 && (error = xfs_bmbt_decrement(cur, level, &i))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ goto error0;
+ }
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *stat = 1;
+ return 0;
+ }
+ ASSERT(rbno != NULLFSBLOCK || lbno != NULLFSBLOCK);
+ if ((error = xfs_btree_dup_cursor(cur, &tcur))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ goto error0;
+ }
+ bno = NULLFSBLOCK;
+ if (rbno != NULLFSBLOCK) {
+ i = xfs_btree_lastrec(tcur, level);
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ if ((error = xfs_bmbt_increment(tcur, level, &i))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ goto error0;
+ }
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ i = xfs_btree_lastrec(tcur, level);
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ rbp = tcur->bc_bufs[level];
+ right = XFS_BUF_TO_BMBT_BLOCK(rbp);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_lblock(cur, right, level, rbp))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ goto error0;
+ }
+#endif
+ bno = INT_GET(right->bb_leftsib, ARCH_CONVERT);
+ if (INT_GET(right->bb_numrecs, ARCH_CONVERT) - 1 >=
+ XFS_BMAP_BLOCK_IMINRECS(level, cur)) {
+ if ((error = xfs_bmbt_lshift(tcur, level, &i))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ goto error0;
+ }
+ if (i) {
+ ASSERT(INT_GET(block->bb_numrecs, ARCH_CONVERT) >=
+ XFS_BMAP_BLOCK_IMINRECS(level, tcur));
+ xfs_btree_del_cursor(tcur, XFS_BTREE_NOERROR);
+ tcur = NULL;
+ if (level > 0) {
+ if ((error = xfs_bmbt_decrement(cur,
+ level, &i))) {
+ XFS_BMBT_TRACE_CURSOR(cur,
+ ERROR);
+ goto error0;
+ }
+ }
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *stat = 1;
+ return 0;
+ }
+ }
+ rrecs = INT_GET(right->bb_numrecs, ARCH_CONVERT);
+ if (lbno != NULLFSBLOCK) {
+ i = xfs_btree_firstrec(tcur, level);
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ if ((error = xfs_bmbt_decrement(tcur, level, &i))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ goto error0;
+ }
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ }
+ }
+ if (lbno != NULLFSBLOCK) {
+ i = xfs_btree_firstrec(tcur, level);
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ /*
+ * decrement to last in block
+ */
+ if ((error = xfs_bmbt_decrement(tcur, level, &i))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ goto error0;
+ }
+ i = xfs_btree_firstrec(tcur, level);
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ lbp = tcur->bc_bufs[level];
+ left = XFS_BUF_TO_BMBT_BLOCK(lbp);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_lblock(cur, left, level, lbp))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ goto error0;
+ }
+#endif
+ bno = INT_GET(left->bb_rightsib, ARCH_CONVERT);
+ if (INT_GET(left->bb_numrecs, ARCH_CONVERT) - 1 >=
+ XFS_BMAP_BLOCK_IMINRECS(level, cur)) {
+ if ((error = xfs_bmbt_rshift(tcur, level, &i))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ goto error0;
+ }
+ if (i) {
+ ASSERT(INT_GET(block->bb_numrecs, ARCH_CONVERT) >=
+ XFS_BMAP_BLOCK_IMINRECS(level, tcur));
+ xfs_btree_del_cursor(tcur, XFS_BTREE_NOERROR);
+ tcur = NULL;
+ if (level == 0)
+ cur->bc_ptrs[0]++;
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *stat = 1;
+ return 0;
+ }
+ }
+ lrecs = INT_GET(left->bb_numrecs, ARCH_CONVERT);
+ }
+ xfs_btree_del_cursor(tcur, XFS_BTREE_NOERROR);
+ tcur = NULL;
+ mp = cur->bc_mp;
+ ASSERT(bno != NULLFSBLOCK);
+ if (lbno != NULLFSBLOCK &&
+ lrecs + INT_GET(block->bb_numrecs, ARCH_CONVERT) <= XFS_BMAP_BLOCK_IMAXRECS(level, cur)) {
+ rbno = bno;
+ right = block;
+ rbp = bp;
+ if ((error = xfs_btree_read_bufl(mp, cur->bc_tp, lbno, 0, &lbp,
+ XFS_BMAP_BTREE_REF))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ goto error0;
+ }
+ left = XFS_BUF_TO_BMBT_BLOCK(lbp);
+ if ((error = xfs_btree_check_lblock(cur, left, level, lbp))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ goto error0;
+ }
+ } else if (rbno != NULLFSBLOCK &&
+ rrecs + INT_GET(block->bb_numrecs, ARCH_CONVERT) <=
+ XFS_BMAP_BLOCK_IMAXRECS(level, cur)) {
+ lbno = bno;
+ left = block;
+ lbp = bp;
+ if ((error = xfs_btree_read_bufl(mp, cur->bc_tp, rbno, 0, &rbp,
+ XFS_BMAP_BTREE_REF))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ goto error0;
+ }
+ right = XFS_BUF_TO_BMBT_BLOCK(rbp);
+ if ((error = xfs_btree_check_lblock(cur, right, level, rbp))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ goto error0;
+ }
+ lrecs = INT_GET(left->bb_numrecs, ARCH_CONVERT);
+ } else {
+ if (level > 0 && (error = xfs_bmbt_decrement(cur, level, &i))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ goto error0;
+ }
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *stat = 1;
+ return 0;
+ }
+ numlrecs = INT_GET(left->bb_numrecs, ARCH_CONVERT);
+ numrrecs = INT_GET(right->bb_numrecs, ARCH_CONVERT);
+ if (level > 0) {
+ lkp = XFS_BMAP_KEY_IADDR(left, numlrecs + 1, cur);
+ lpp = XFS_BMAP_PTR_IADDR(left, numlrecs + 1, cur);
+ rkp = XFS_BMAP_KEY_IADDR(right, 1, cur);
+ rpp = XFS_BMAP_PTR_IADDR(right, 1, cur);
+#ifdef DEBUG
+ for (i = 0; i < numrrecs; i++) {
+ if ((error = xfs_btree_check_lptr(cur, INT_GET(rpp[i], ARCH_CONVERT), level))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ goto error0;
+ }
+ }
+#endif
+ memcpy(lkp, rkp, numrrecs * sizeof(*lkp));
+ memcpy(lpp, rpp, numrrecs * sizeof(*lpp));
+ xfs_bmbt_log_keys(cur, lbp, numlrecs + 1, numlrecs + numrrecs);
+ xfs_bmbt_log_ptrs(cur, lbp, numlrecs + 1, numlrecs + numrrecs);
+ } else {
+ lrp = XFS_BMAP_REC_IADDR(left, numlrecs + 1, cur);
+ rrp = XFS_BMAP_REC_IADDR(right, 1, cur);
+ memcpy(lrp, rrp, numrrecs * sizeof(*lrp));
+ xfs_bmbt_log_recs(cur, lbp, numlrecs + 1, numlrecs + numrrecs);
+ }
+ INT_MOD(left->bb_numrecs, ARCH_CONVERT, numrrecs);
+ left->bb_rightsib = right->bb_rightsib; /* INT_: direct copy */
+ xfs_bmbt_log_block(cur, lbp, XFS_BB_RIGHTSIB | XFS_BB_NUMRECS);
+ if (INT_GET(left->bb_rightsib, ARCH_CONVERT) != NULLDFSBNO) {
+ if ((error = xfs_btree_read_bufl(mp, cur->bc_tp,
+ INT_GET(left->bb_rightsib, ARCH_CONVERT),
+ 0, &rrbp, XFS_BMAP_BTREE_REF))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ goto error0;
+ }
+ rrblock = XFS_BUF_TO_BMBT_BLOCK(rrbp);
+ if ((error = xfs_btree_check_lblock(cur, rrblock, level, rrbp))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ goto error0;
+ }
+ INT_SET(rrblock->bb_leftsib, ARCH_CONVERT, lbno);
+ xfs_bmbt_log_block(cur, rrbp, XFS_BB_LEFTSIB);
+ }
+ xfs_bmap_add_free(XFS_DADDR_TO_FSB(mp, XFS_BUF_ADDR(rbp)), 1,
+ cur->bc_private.b.flist, mp);
+ cur->bc_private.b.ip->i_d.di_nblocks--;
+ xfs_trans_log_inode(cur->bc_tp, cur->bc_private.b.ip, XFS_ILOG_CORE);
+ XFS_TRANS_MOD_DQUOT_BYINO(mp, cur->bc_tp, cur->bc_private.b.ip,
+ XFS_TRANS_DQ_BCOUNT, -1L);
+ xfs_trans_binval(cur->bc_tp, rbp);
+ if (bp != lbp) {
+ cur->bc_bufs[level] = lbp;
+ cur->bc_ptrs[level] += lrecs;
+ cur->bc_ra[level] = 0;
+ } else if ((error = xfs_bmbt_increment(cur, level + 1, &i))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ goto error0;
+ }
+ if (level > 0)
+ cur->bc_ptrs[level]--;
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *stat = 2;
+ return 0;
+
+error0:
+ if (tcur)
+ xfs_btree_del_cursor(tcur, XFS_BTREE_ERROR);
+ return error;
+}
+
+#ifdef DEBUG
+/*
+ * Get the data from the pointed-to record.
+ */
+int
+xfs_bmbt_get_rec(
+ xfs_btree_cur_t *cur,
+ xfs_fileoff_t *off,
+ xfs_fsblock_t *bno,
+ xfs_filblks_t *len,
+ xfs_exntst_t *state,
+ int *stat)
+{
+ xfs_bmbt_block_t *block;
+ xfs_buf_t *bp;
+#ifdef DEBUG
+ int error;
+#endif
+ int ptr;
+ xfs_bmbt_rec_t *rp;
+
+ block = xfs_bmbt_get_block(cur, 0, &bp);
+ ptr = cur->bc_ptrs[0];
+#ifdef DEBUG
+ if ((error = xfs_btree_check_lblock(cur, block, 0, bp)))
+ return error;
+#endif
+ if (ptr > INT_GET(block->bb_numrecs, ARCH_CONVERT) || ptr <= 0) {
+ *stat = 0;
+ return 0;
+ }
+ rp = XFS_BMAP_REC_IADDR(block, ptr, cur);
+ *off = xfs_bmbt_disk_get_startoff(rp);
+ *bno = xfs_bmbt_disk_get_startblock(rp);
+ *len = xfs_bmbt_disk_get_blockcount(rp);
+ *state = xfs_bmbt_disk_get_state(rp);
+ *stat = 1;
+ return 0;
+}
+#endif
+
+/*
+ * Insert one record/level. Return information to the caller
+ * allowing the next level up to proceed if necessary.
+ */
+STATIC int /* error */
+xfs_bmbt_insrec(
+ xfs_btree_cur_t *cur,
+ int level,
+ xfs_fsblock_t *bnop,
+ xfs_bmbt_rec_t *recp,
+ xfs_btree_cur_t **curp,
+ int *stat) /* no-go/done/continue */
+{
+ xfs_bmbt_block_t *block; /* bmap btree block */
+ xfs_buf_t *bp; /* buffer for block */
+ int error; /* error return value */
+#ifdef XFS_BMBT_TRACE
+ static char fname[] = "xfs_bmbt_insrec";
+#endif
+ int i; /* loop index */
+ xfs_bmbt_key_t key; /* bmap btree key */
+ xfs_bmbt_key_t *kp=NULL; /* pointer to bmap btree key */
+ int logflags; /* inode logging flags */
+ xfs_fsblock_t nbno; /* new block number */
+ struct xfs_btree_cur *ncur; /* new btree cursor */
+ xfs_bmbt_key_t nkey; /* new btree key value */
+ xfs_bmbt_rec_t nrec; /* new record count */
+ int optr; /* old key/record index */
+ xfs_bmbt_ptr_t *pp; /* pointer to bmap block addr */
+ int ptr; /* key/record index */
+ xfs_bmbt_rec_t *rp=NULL; /* pointer to bmap btree rec */
+ int numrecs;
+
+ ASSERT(level < cur->bc_nlevels);
+ XFS_BMBT_TRACE_CURSOR(cur, ENTRY);
+ XFS_BMBT_TRACE_ARGIFR(cur, level, *bnop, recp);
+ ncur = (xfs_btree_cur_t *)0;
+ INT_SET(key.br_startoff, ARCH_CONVERT,
+ xfs_bmbt_disk_get_startoff(recp));
+ optr = ptr = cur->bc_ptrs[level];
+ if (ptr == 0) {
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *stat = 0;
+ return 0;
+ }
+ XFS_STATS_INC(xs_bmbt_insrec);
+ block = xfs_bmbt_get_block(cur, level, &bp);
+ numrecs = INT_GET(block->bb_numrecs, ARCH_CONVERT);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_lblock(cur, block, level, bp))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+ if (ptr <= numrecs) {
+ if (level == 0) {
+ rp = XFS_BMAP_REC_IADDR(block, ptr, cur);
+ xfs_btree_check_rec(XFS_BTNUM_BMAP, recp, rp);
+ } else {
+ kp = XFS_BMAP_KEY_IADDR(block, ptr, cur);
+ xfs_btree_check_key(XFS_BTNUM_BMAP, &key, kp);
+ }
+ }
+#endif
+ nbno = NULLFSBLOCK;
+ if (numrecs == XFS_BMAP_BLOCK_IMAXRECS(level, cur)) {
+ if (numrecs < XFS_BMAP_BLOCK_DMAXRECS(level, cur)) {
+ /*
+ * A root block, that can be made bigger.
+ */
+ xfs_iroot_realloc(cur->bc_private.b.ip, 1,
+ cur->bc_private.b.whichfork);
+ block = xfs_bmbt_get_block(cur, level, &bp);
+ } else if (level == cur->bc_nlevels - 1) {
+ if ((error = xfs_bmbt_newroot(cur, &logflags, stat)) ||
+ *stat == 0) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+ xfs_trans_log_inode(cur->bc_tp, cur->bc_private.b.ip,
+ logflags);
+ block = xfs_bmbt_get_block(cur, level, &bp);
+ } else {
+ if ((error = xfs_bmbt_rshift(cur, level, &i))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+ if (i) {
+ /* nothing */
+ } else {
+ if ((error = xfs_bmbt_lshift(cur, level, &i))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+ if (i) {
+ optr = ptr = cur->bc_ptrs[level];
+ } else {
+ if ((error = xfs_bmbt_split(cur, level,
+ &nbno, &nkey, &ncur,
+ &i))) {
+ XFS_BMBT_TRACE_CURSOR(cur,
+ ERROR);
+ return error;
+ }
+ if (i) {
+ block = xfs_bmbt_get_block(
+ cur, level, &bp);
+#ifdef DEBUG
+ if ((error =
+ xfs_btree_check_lblock(cur,
+ block, level, bp))) {
+ XFS_BMBT_TRACE_CURSOR(
+ cur, ERROR);
+ return error;
+ }
+#endif
+ ptr = cur->bc_ptrs[level];
+ xfs_bmbt_disk_set_allf(&nrec,
+ nkey.br_startoff, 0, 0,
+ XFS_EXT_NORM);
+ } else {
+ XFS_BMBT_TRACE_CURSOR(cur,
+ EXIT);
+ *stat = 0;
+ return 0;
+ }
+ }
+ }
+ }
+ }
+ numrecs = INT_GET(block->bb_numrecs, ARCH_CONVERT);
+ if (level > 0) {
+ kp = XFS_BMAP_KEY_IADDR(block, 1, cur);
+ pp = XFS_BMAP_PTR_IADDR(block, 1, cur);
+#ifdef DEBUG
+ for (i = numrecs; i >= ptr; i--) {
+ if ((error = xfs_btree_check_lptr(cur, INT_GET(pp[i - 1], ARCH_CONVERT),
+ level))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+ }
+#endif
+ memmove(&kp[ptr], &kp[ptr - 1],
+ (numrecs - ptr + 1) * sizeof(*kp));
+ memmove(&pp[ptr], &pp[ptr - 1], /* INT_: direct copy */
+ (numrecs - ptr + 1) * sizeof(*pp));
+#ifdef DEBUG
+ if ((error = xfs_btree_check_lptr(cur, (xfs_bmbt_ptr_t)*bnop,
+ level))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+#endif
+ kp[ptr - 1] = key;
+ INT_SET(pp[ptr - 1], ARCH_CONVERT, *bnop);
+ numrecs++;
+ INT_SET(block->bb_numrecs, ARCH_CONVERT, numrecs);
+ xfs_bmbt_log_keys(cur, bp, ptr, numrecs);
+ xfs_bmbt_log_ptrs(cur, bp, ptr, numrecs);
+ } else {
+ rp = XFS_BMAP_REC_IADDR(block, 1, cur);
+ memmove(&rp[ptr], &rp[ptr - 1],
+ (numrecs - ptr + 1) * sizeof(*rp));
+ rp[ptr - 1] = *recp;
+ numrecs++;
+ INT_SET(block->bb_numrecs, ARCH_CONVERT, numrecs);
+ xfs_bmbt_log_recs(cur, bp, ptr, numrecs);
+ }
+ xfs_bmbt_log_block(cur, bp, XFS_BB_NUMRECS);
+#ifdef DEBUG
+ if (ptr < numrecs) {
+ if (level == 0)
+ xfs_btree_check_rec(XFS_BTNUM_BMAP, rp + ptr - 1,
+ rp + ptr);
+ else
+ xfs_btree_check_key(XFS_BTNUM_BMAP, kp + ptr - 1,
+ kp + ptr);
+ }
+#endif
+ if (optr == 1 && (error = xfs_bmbt_updkey(cur, &key, level + 1))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+ *bnop = nbno;
+ if (nbno != NULLFSBLOCK) {
+ *recp = nrec;
+ *curp = ncur;
+ }
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *stat = 1;
+ return 0;
+}
+
+STATIC int
+xfs_bmbt_killroot(
+ xfs_btree_cur_t *cur)
+{
+ xfs_bmbt_block_t *block;
+ xfs_bmbt_block_t *cblock;
+ xfs_buf_t *cbp;
+ xfs_bmbt_key_t *ckp;
+ xfs_bmbt_ptr_t *cpp;
+#ifdef DEBUG
+ int error;
+#endif
+#ifdef XFS_BMBT_TRACE
+ static char fname[] = "xfs_bmbt_killroot";
+#endif
+ int i;
+ xfs_bmbt_key_t *kp;
+ xfs_inode_t *ip;
+ xfs_ifork_t *ifp;
+ int level;
+ xfs_bmbt_ptr_t *pp;
+
+ XFS_BMBT_TRACE_CURSOR(cur, ENTRY);
+ level = cur->bc_nlevels - 1;
+ ASSERT(level >= 1);
+ /*
+ * Don't deal with the root block needs to be a leaf case.
+ * We're just going to turn the thing back into extents anyway.
+ */
+ if (level == 1) {
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ return 0;
+ }
+ block = xfs_bmbt_get_block(cur, level, &cbp);
+ /*
+ * Give up if the root has multiple children.
+ */
+ if (INT_GET(block->bb_numrecs, ARCH_CONVERT) != 1) {
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ return 0;
+ }
+ /*
+ * Only do this if the next level will fit.
+ * Then the data must be copied up to the inode,
+ * instead of freeing the root you free the next level.
+ */
+ cbp = cur->bc_bufs[level - 1];
+ cblock = XFS_BUF_TO_BMBT_BLOCK(cbp);
+ if (INT_GET(cblock->bb_numrecs, ARCH_CONVERT) > XFS_BMAP_BLOCK_DMAXRECS(level, cur)) {
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ return 0;
+ }
+ ASSERT(INT_GET(cblock->bb_leftsib, ARCH_CONVERT) == NULLDFSBNO);
+ ASSERT(INT_GET(cblock->bb_rightsib, ARCH_CONVERT) == NULLDFSBNO);
+ ip = cur->bc_private.b.ip;
+ ifp = XFS_IFORK_PTR(ip, cur->bc_private.b.whichfork);
+ ASSERT(XFS_BMAP_BLOCK_IMAXRECS(level, cur) ==
+ XFS_BMAP_BROOT_MAXRECS(ifp->if_broot_bytes));
+ i = (int)(INT_GET(cblock->bb_numrecs, ARCH_CONVERT) - XFS_BMAP_BLOCK_IMAXRECS(level, cur));
+ if (i) {
+ xfs_iroot_realloc(ip, i, cur->bc_private.b.whichfork);
+ block = ifp->if_broot;
+ }
+ INT_MOD(block->bb_numrecs, ARCH_CONVERT, i);
+ ASSERT(INT_GET(block->bb_numrecs, ARCH_CONVERT) == INT_GET(cblock->bb_numrecs, ARCH_CONVERT));
+ kp = XFS_BMAP_KEY_IADDR(block, 1, cur);
+ ckp = XFS_BMAP_KEY_IADDR(cblock, 1, cur);
+ memcpy(kp, ckp, INT_GET(block->bb_numrecs, ARCH_CONVERT) * sizeof(*kp));
+ pp = XFS_BMAP_PTR_IADDR(block, 1, cur);
+ cpp = XFS_BMAP_PTR_IADDR(cblock, 1, cur);
+#ifdef DEBUG
+ for (i = 0; i < INT_GET(cblock->bb_numrecs, ARCH_CONVERT); i++) {
+ if ((error = xfs_btree_check_lptr(cur, INT_GET(cpp[i], ARCH_CONVERT), level - 1))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+ }
+#endif
+ memcpy(pp, cpp, INT_GET(block->bb_numrecs, ARCH_CONVERT) * sizeof(*pp));
+ xfs_bmap_add_free(XFS_DADDR_TO_FSB(cur->bc_mp, XFS_BUF_ADDR(cbp)), 1,
+ cur->bc_private.b.flist, cur->bc_mp);
+ ip->i_d.di_nblocks--;
+ XFS_TRANS_MOD_DQUOT_BYINO(cur->bc_mp, cur->bc_tp, ip,
+ XFS_TRANS_DQ_BCOUNT, -1L);
+ xfs_trans_binval(cur->bc_tp, cbp);
+ cur->bc_bufs[level - 1] = NULL;
+ INT_MOD(block->bb_level, ARCH_CONVERT, -1);
+ xfs_trans_log_inode(cur->bc_tp, ip,
+ XFS_ILOG_CORE | XFS_ILOG_FBROOT(cur->bc_private.b.whichfork));
+ cur->bc_nlevels--;
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ return 0;
+}
+
+/*
+ * Log key values from the btree block.
+ */
+STATIC void
+xfs_bmbt_log_keys(
+ xfs_btree_cur_t *cur,
+ xfs_buf_t *bp,
+ int kfirst,
+ int klast)
+{
+#ifdef XFS_BMBT_TRACE
+ static char fname[] = "xfs_bmbt_log_keys";
+#endif
+ xfs_trans_t *tp;
+
+ XFS_BMBT_TRACE_CURSOR(cur, ENTRY);
+ XFS_BMBT_TRACE_ARGBII(cur, bp, kfirst, klast);
+ tp = cur->bc_tp;
+ if (bp) {
+ xfs_bmbt_block_t *block;
+ int first;
+ xfs_bmbt_key_t *kp;
+ int last;
+
+ block = XFS_BUF_TO_BMBT_BLOCK(bp);
+ kp = XFS_BMAP_KEY_DADDR(block, 1, cur);
+ first = (int)((xfs_caddr_t)&kp[kfirst - 1] - (xfs_caddr_t)block);
+ last = (int)(((xfs_caddr_t)&kp[klast] - 1) - (xfs_caddr_t)block);
+ xfs_trans_log_buf(tp, bp, first, last);
+ } else {
+ xfs_inode_t *ip;
+
+ ip = cur->bc_private.b.ip;
+ xfs_trans_log_inode(tp, ip,
+ XFS_ILOG_FBROOT(cur->bc_private.b.whichfork));
+ }
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+}
+
+/*
+ * Log pointer values from the btree block.
+ */
+STATIC void
+xfs_bmbt_log_ptrs(
+ xfs_btree_cur_t *cur,
+ xfs_buf_t *bp,
+ int pfirst,
+ int plast)
+{
+#ifdef XFS_BMBT_TRACE
+ static char fname[] = "xfs_bmbt_log_ptrs";
+#endif
+ xfs_trans_t *tp;
+
+ XFS_BMBT_TRACE_CURSOR(cur, ENTRY);
+ XFS_BMBT_TRACE_ARGBII(cur, bp, pfirst, plast);
+ tp = cur->bc_tp;
+ if (bp) {
+ xfs_bmbt_block_t *block;
+ int first;
+ int last;
+ xfs_bmbt_ptr_t *pp;
+
+ block = XFS_BUF_TO_BMBT_BLOCK(bp);
+ pp = XFS_BMAP_PTR_DADDR(block, 1, cur);
+ first = (int)((xfs_caddr_t)&pp[pfirst - 1] - (xfs_caddr_t)block);
+ last = (int)(((xfs_caddr_t)&pp[plast] - 1) - (xfs_caddr_t)block);
+ xfs_trans_log_buf(tp, bp, first, last);
+ } else {
+ xfs_inode_t *ip;
+
+ ip = cur->bc_private.b.ip;
+ xfs_trans_log_inode(tp, ip,
+ XFS_ILOG_FBROOT(cur->bc_private.b.whichfork));
+ }
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+}
+
+/*
+ * Lookup the record. The cursor is made to point to it, based on dir.
+ */
+STATIC int /* error */
+xfs_bmbt_lookup(
+ xfs_btree_cur_t *cur,
+ xfs_lookup_t dir,
+ int *stat) /* success/failure */
+{
+ xfs_bmbt_block_t *block=NULL;
+ xfs_buf_t *bp;
+ xfs_daddr_t d;
+ xfs_sfiloff_t diff;
+ int error; /* error return value */
+#ifdef XFS_BMBT_TRACE
+ static char fname[] = "xfs_bmbt_lookup";
+#endif
+ xfs_fsblock_t fsbno=0;
+ int high;
+ int i;
+ int keyno=0;
+ xfs_bmbt_key_t *kkbase=NULL;
+ xfs_bmbt_key_t *kkp;
+ xfs_bmbt_rec_t *krbase=NULL;
+ xfs_bmbt_rec_t *krp;
+ int level;
+ int low;
+ xfs_mount_t *mp;
+ xfs_bmbt_ptr_t *pp;
+ xfs_bmbt_irec_t *rp;
+ xfs_fileoff_t startoff;
+ xfs_trans_t *tp;
+
+ XFS_STATS_INC(xs_bmbt_lookup);
+ XFS_BMBT_TRACE_CURSOR(cur, ENTRY);
+ XFS_BMBT_TRACE_ARGI(cur, (int)dir);
+ tp = cur->bc_tp;
+ mp = cur->bc_mp;
+ rp = &cur->bc_rec.b;
+ for (level = cur->bc_nlevels - 1, diff = 1; level >= 0; level--) {
+ if (level < cur->bc_nlevels - 1) {
+ d = XFS_FSB_TO_DADDR(mp, fsbno);
+ bp = cur->bc_bufs[level];
+ if (bp && XFS_BUF_ADDR(bp) != d)
+ bp = (xfs_buf_t *)0;
+ if (!bp) {
+ if ((error = xfs_btree_read_bufl(mp, tp, fsbno,
+ 0, &bp, XFS_BMAP_BTREE_REF))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+ xfs_btree_setbuf(cur, level, bp);
+ block = XFS_BUF_TO_BMBT_BLOCK(bp);
+ if ((error = xfs_btree_check_lblock(cur, block,
+ level, bp))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+ } else
+ block = XFS_BUF_TO_BMBT_BLOCK(bp);
+ } else
+ block = xfs_bmbt_get_block(cur, level, &bp);
+ if (diff == 0)
+ keyno = 1;
+ else {
+ if (level > 0)
+ kkbase = XFS_BMAP_KEY_IADDR(block, 1, cur);
+ else
+ krbase = XFS_BMAP_REC_IADDR(block, 1, cur);
+ low = 1;
+ if (!(high = INT_GET(block->bb_numrecs, ARCH_CONVERT))) {
+ ASSERT(level == 0);
+ cur->bc_ptrs[0] = dir != XFS_LOOKUP_LE;
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *stat = 0;
+ return 0;
+ }
+ while (low <= high) {
+ XFS_STATS_INC(xs_bmbt_compare);
+ keyno = (low + high) >> 1;
+ if (level > 0) {
+ kkp = kkbase + keyno - 1;
+ startoff = INT_GET(kkp->br_startoff, ARCH_CONVERT);
+ } else {
+ krp = krbase + keyno - 1;
+ startoff = xfs_bmbt_disk_get_startoff(krp);
+ }
+ diff = (xfs_sfiloff_t)
+ (startoff - rp->br_startoff);
+ if (diff < 0)
+ low = keyno + 1;
+ else if (diff > 0)
+ high = keyno - 1;
+ else
+ break;
+ }
+ }
+ if (level > 0) {
+ if (diff > 0 && --keyno < 1)
+ keyno = 1;
+ pp = XFS_BMAP_PTR_IADDR(block, keyno, cur);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_lptr(cur, INT_GET(*pp, ARCH_CONVERT), level))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+#endif
+ fsbno = INT_GET(*pp, ARCH_CONVERT);
+ cur->bc_ptrs[level] = keyno;
+ }
+ }
+ if (dir != XFS_LOOKUP_LE && diff < 0) {
+ keyno++;
+ /*
+ * If ge search and we went off the end of the block, but it's
+ * not the last block, we're in the wrong block.
+ */
+ if (dir == XFS_LOOKUP_GE && keyno > INT_GET(block->bb_numrecs, ARCH_CONVERT) &&
+ INT_GET(block->bb_rightsib, ARCH_CONVERT) != NULLDFSBNO) {
+ cur->bc_ptrs[0] = keyno;
+ if ((error = xfs_bmbt_increment(cur, 0, &i))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+ XFS_WANT_CORRUPTED_RETURN(i == 1);
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *stat = 1;
+ return 0;
+ }
+ }
+ else if (dir == XFS_LOOKUP_LE && diff > 0)
+ keyno--;
+ cur->bc_ptrs[0] = keyno;
+ if (keyno == 0 || keyno > INT_GET(block->bb_numrecs, ARCH_CONVERT)) {
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *stat = 0;
+ } else {
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *stat = ((dir != XFS_LOOKUP_EQ) || (diff == 0));
+ }
+ return 0;
+}
+
+/*
+ * Move 1 record left from cur/level if possible.
+ * Update cur to reflect the new path.
+ */
+STATIC int /* error */
+xfs_bmbt_lshift(
+ xfs_btree_cur_t *cur,
+ int level,
+ int *stat) /* success/failure */
+{
+ int error; /* error return value */
+#ifdef XFS_BMBT_TRACE
+ static char fname[] = "xfs_bmbt_lshift";
+#endif
+#ifdef DEBUG
+ int i; /* loop counter */
+#endif
+ xfs_bmbt_key_t key; /* bmap btree key */
+ xfs_buf_t *lbp; /* left buffer pointer */
+ xfs_bmbt_block_t *left; /* left btree block */
+ xfs_bmbt_key_t *lkp=NULL; /* left btree key */
+ xfs_bmbt_ptr_t *lpp; /* left address pointer */
+ int lrecs; /* left record count */
+ xfs_bmbt_rec_t *lrp=NULL; /* left record pointer */
+ xfs_mount_t *mp; /* file system mount point */
+ xfs_buf_t *rbp; /* right buffer pointer */
+ xfs_bmbt_block_t *right; /* right btree block */
+ xfs_bmbt_key_t *rkp=NULL; /* right btree key */
+ xfs_bmbt_ptr_t *rpp=NULL; /* right address pointer */
+ xfs_bmbt_rec_t *rrp=NULL; /* right record pointer */
+ int rrecs; /* right record count */
+
+ XFS_BMBT_TRACE_CURSOR(cur, ENTRY);
+ XFS_BMBT_TRACE_ARGI(cur, level);
+ if (level == cur->bc_nlevels - 1) {
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *stat = 0;
+ return 0;
+ }
+ rbp = cur->bc_bufs[level];
+ right = XFS_BUF_TO_BMBT_BLOCK(rbp);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_lblock(cur, right, level, rbp))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+#endif
+ if (INT_GET(right->bb_leftsib, ARCH_CONVERT) == NULLDFSBNO) {
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *stat = 0;
+ return 0;
+ }
+ if (cur->bc_ptrs[level] <= 1) {
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *stat = 0;
+ return 0;
+ }
+ mp = cur->bc_mp;
+ if ((error = xfs_btree_read_bufl(mp, cur->bc_tp, INT_GET(right->bb_leftsib, ARCH_CONVERT), 0,
+ &lbp, XFS_BMAP_BTREE_REF))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+ left = XFS_BUF_TO_BMBT_BLOCK(lbp);
+ if ((error = xfs_btree_check_lblock(cur, left, level, lbp))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+ if (INT_GET(left->bb_numrecs, ARCH_CONVERT) == XFS_BMAP_BLOCK_IMAXRECS(level, cur)) {
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *stat = 0;
+ return 0;
+ }
+ lrecs = INT_GET(left->bb_numrecs, ARCH_CONVERT) + 1;
+ if (level > 0) {
+ lkp = XFS_BMAP_KEY_IADDR(left, lrecs, cur);
+ rkp = XFS_BMAP_KEY_IADDR(right, 1, cur);
+ *lkp = *rkp;
+ xfs_bmbt_log_keys(cur, lbp, lrecs, lrecs);
+ lpp = XFS_BMAP_PTR_IADDR(left, lrecs, cur);
+ rpp = XFS_BMAP_PTR_IADDR(right, 1, cur);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_lptr(cur, INT_GET(*rpp, ARCH_CONVERT), level))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+#endif
+ *lpp = *rpp; /* INT_: direct copy */
+ xfs_bmbt_log_ptrs(cur, lbp, lrecs, lrecs);
+ } else {
+ lrp = XFS_BMAP_REC_IADDR(left, lrecs, cur);
+ rrp = XFS_BMAP_REC_IADDR(right, 1, cur);
+ *lrp = *rrp;
+ xfs_bmbt_log_recs(cur, lbp, lrecs, lrecs);
+ }
+ INT_SET(left->bb_numrecs, ARCH_CONVERT, lrecs);
+ xfs_bmbt_log_block(cur, lbp, XFS_BB_NUMRECS);
+#ifdef DEBUG
+ if (level > 0)
+ xfs_btree_check_key(XFS_BTNUM_BMAP, lkp - 1, lkp);
+ else
+ xfs_btree_check_rec(XFS_BTNUM_BMAP, lrp - 1, lrp);
+#endif
+ rrecs = INT_GET(right->bb_numrecs, ARCH_CONVERT) - 1;
+ INT_SET(right->bb_numrecs, ARCH_CONVERT, rrecs);
+ xfs_bmbt_log_block(cur, rbp, XFS_BB_NUMRECS);
+ if (level > 0) {
+#ifdef DEBUG
+ for (i = 0; i < rrecs; i++) {
+ if ((error = xfs_btree_check_lptr(cur, INT_GET(rpp[i + 1], ARCH_CONVERT),
+ level))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+ }
+#endif
+ memmove(rkp, rkp + 1, rrecs * sizeof(*rkp));
+ memmove(rpp, rpp + 1, rrecs * sizeof(*rpp));
+ xfs_bmbt_log_keys(cur, rbp, 1, rrecs);
+ xfs_bmbt_log_ptrs(cur, rbp, 1, rrecs);
+ } else {
+ memmove(rrp, rrp + 1, rrecs * sizeof(*rrp));
+ xfs_bmbt_log_recs(cur, rbp, 1, rrecs);
+ INT_SET(key.br_startoff, ARCH_CONVERT,
+ xfs_bmbt_disk_get_startoff(rrp));
+ rkp = &key;
+ }
+ if ((error = xfs_bmbt_updkey(cur, rkp, level + 1))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+ cur->bc_ptrs[level]--;
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *stat = 1;
+ return 0;
+}
+
+/*
+ * Move 1 record right from cur/level if possible.
+ * Update cur to reflect the new path.
+ */
+STATIC int /* error */
+xfs_bmbt_rshift(
+ xfs_btree_cur_t *cur,
+ int level,
+ int *stat) /* success/failure */
+{
+ int error; /* error return value */
+#ifdef XFS_BMBT_TRACE
+ static char fname[] = "xfs_bmbt_rshift";
+#endif
+ int i; /* loop counter */
+ xfs_bmbt_key_t key; /* bmap btree key */
+ xfs_buf_t *lbp; /* left buffer pointer */
+ xfs_bmbt_block_t *left; /* left btree block */
+ xfs_bmbt_key_t *lkp; /* left btree key */
+ xfs_bmbt_ptr_t *lpp; /* left address pointer */
+ xfs_bmbt_rec_t *lrp; /* left record pointer */
+ xfs_mount_t *mp; /* file system mount point */
+ xfs_buf_t *rbp; /* right buffer pointer */
+ xfs_bmbt_block_t *right; /* right btree block */
+ xfs_bmbt_key_t *rkp; /* right btree key */
+ xfs_bmbt_ptr_t *rpp; /* right address pointer */
+ xfs_bmbt_rec_t *rrp=NULL; /* right record pointer */
+ struct xfs_btree_cur *tcur; /* temporary btree cursor */
+
+ XFS_BMBT_TRACE_CURSOR(cur, ENTRY);
+ XFS_BMBT_TRACE_ARGI(cur, level);
+ if (level == cur->bc_nlevels - 1) {
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *stat = 0;
+ return 0;
+ }
+ lbp = cur->bc_bufs[level];
+ left = XFS_BUF_TO_BMBT_BLOCK(lbp);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_lblock(cur, left, level, lbp))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+#endif
+ if (INT_GET(left->bb_rightsib, ARCH_CONVERT) == NULLDFSBNO) {
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *stat = 0;
+ return 0;
+ }
+ if (cur->bc_ptrs[level] >= INT_GET(left->bb_numrecs, ARCH_CONVERT)) {
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *stat = 0;
+ return 0;
+ }
+ mp = cur->bc_mp;
+ if ((error = xfs_btree_read_bufl(mp, cur->bc_tp, INT_GET(left->bb_rightsib, ARCH_CONVERT), 0,
+ &rbp, XFS_BMAP_BTREE_REF))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+ right = XFS_BUF_TO_BMBT_BLOCK(rbp);
+ if ((error = xfs_btree_check_lblock(cur, right, level, rbp))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+ if (INT_GET(right->bb_numrecs, ARCH_CONVERT) == XFS_BMAP_BLOCK_IMAXRECS(level, cur)) {
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *stat = 0;
+ return 0;
+ }
+ if (level > 0) {
+ lkp = XFS_BMAP_KEY_IADDR(left, INT_GET(left->bb_numrecs, ARCH_CONVERT), cur);
+ lpp = XFS_BMAP_PTR_IADDR(left, INT_GET(left->bb_numrecs, ARCH_CONVERT), cur);
+ rkp = XFS_BMAP_KEY_IADDR(right, 1, cur);
+ rpp = XFS_BMAP_PTR_IADDR(right, 1, cur);
+#ifdef DEBUG
+ for (i = INT_GET(right->bb_numrecs, ARCH_CONVERT) - 1; i >= 0; i--) {
+ if ((error = xfs_btree_check_lptr(cur, INT_GET(rpp[i], ARCH_CONVERT), level))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+ }
+#endif
+ memmove(rkp + 1, rkp, INT_GET(right->bb_numrecs, ARCH_CONVERT) * sizeof(*rkp));
+ memmove(rpp + 1, rpp, INT_GET(right->bb_numrecs, ARCH_CONVERT) * sizeof(*rpp));
+#ifdef DEBUG
+ if ((error = xfs_btree_check_lptr(cur, INT_GET(*lpp, ARCH_CONVERT), level))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+#endif
+ *rkp = *lkp;
+ *rpp = *lpp; /* INT_: direct copy */
+ xfs_bmbt_log_keys(cur, rbp, 1, INT_GET(right->bb_numrecs, ARCH_CONVERT) + 1);
+ xfs_bmbt_log_ptrs(cur, rbp, 1, INT_GET(right->bb_numrecs, ARCH_CONVERT) + 1);
+ } else {
+ lrp = XFS_BMAP_REC_IADDR(left, INT_GET(left->bb_numrecs, ARCH_CONVERT), cur);
+ rrp = XFS_BMAP_REC_IADDR(right, 1, cur);
+ memmove(rrp + 1, rrp, INT_GET(right->bb_numrecs, ARCH_CONVERT) * sizeof(*rrp));
+ *rrp = *lrp;
+ xfs_bmbt_log_recs(cur, rbp, 1, INT_GET(right->bb_numrecs, ARCH_CONVERT) + 1);
+ INT_SET(key.br_startoff, ARCH_CONVERT,
+ xfs_bmbt_disk_get_startoff(rrp));
+ rkp = &key;
+ }
+ INT_MOD(left->bb_numrecs, ARCH_CONVERT, -1);
+ xfs_bmbt_log_block(cur, lbp, XFS_BB_NUMRECS);
+ INT_MOD(right->bb_numrecs, ARCH_CONVERT, +1);
+#ifdef DEBUG
+ if (level > 0)
+ xfs_btree_check_key(XFS_BTNUM_BMAP, rkp, rkp + 1);
+ else
+ xfs_btree_check_rec(XFS_BTNUM_BMAP, rrp, rrp + 1);
+#endif
+ xfs_bmbt_log_block(cur, rbp, XFS_BB_NUMRECS);
+ if ((error = xfs_btree_dup_cursor(cur, &tcur))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+ i = xfs_btree_lastrec(tcur, level);
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ if ((error = xfs_bmbt_increment(tcur, level, &i))) {
+ XFS_BMBT_TRACE_CURSOR(tcur, ERROR);
+ goto error1;
+ }
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ if ((error = xfs_bmbt_updkey(tcur, rkp, level + 1))) {
+ XFS_BMBT_TRACE_CURSOR(tcur, ERROR);
+ goto error1;
+ }
+ xfs_btree_del_cursor(tcur, XFS_BTREE_NOERROR);
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *stat = 1;
+ return 0;
+error0:
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+error1:
+ xfs_btree_del_cursor(tcur, XFS_BTREE_ERROR);
+ return error;
+}
+
+/*
+ * Determine the extent state.
+ */
+/* ARGSUSED */
+STATIC xfs_exntst_t
+xfs_extent_state(
+ xfs_filblks_t blks,
+ int extent_flag)
+{
+ if (extent_flag) {
+ ASSERT(blks != 0); /* saved for DMIG */
+ return XFS_EXT_UNWRITTEN;
+ }
+ return XFS_EXT_NORM;
+}
+
+
+/*
+ * Split cur/level block in half.
+ * Return new block number and its first record (to be inserted into parent).
+ */
+STATIC int /* error */
+xfs_bmbt_split(
+ xfs_btree_cur_t *cur,
+ int level,
+ xfs_fsblock_t *bnop,
+ xfs_bmbt_key_t *keyp,
+ xfs_btree_cur_t **curp,
+ int *stat) /* success/failure */
+{
+ xfs_alloc_arg_t args; /* block allocation args */
+ int error; /* error return value */
+#ifdef XFS_BMBT_TRACE
+ static char fname[] = "xfs_bmbt_split";
+#endif
+ int i; /* loop counter */
+ xfs_fsblock_t lbno; /* left sibling block number */
+ xfs_buf_t *lbp; /* left buffer pointer */
+ xfs_bmbt_block_t *left; /* left btree block */
+ xfs_bmbt_key_t *lkp; /* left btree key */
+ xfs_bmbt_ptr_t *lpp; /* left address pointer */
+ xfs_bmbt_rec_t *lrp; /* left record pointer */
+ xfs_buf_t *rbp; /* right buffer pointer */
+ xfs_bmbt_block_t *right; /* right btree block */
+ xfs_bmbt_key_t *rkp; /* right btree key */
+ xfs_bmbt_ptr_t *rpp; /* right address pointer */
+ xfs_bmbt_block_t *rrblock; /* right-right btree block */
+ xfs_buf_t *rrbp; /* right-right buffer pointer */
+ xfs_bmbt_rec_t *rrp; /* right record pointer */
+
+ XFS_BMBT_TRACE_CURSOR(cur, ENTRY);
+ XFS_BMBT_TRACE_ARGIFK(cur, level, *bnop, keyp);
+ args.tp = cur->bc_tp;
+ args.mp = cur->bc_mp;
+ lbp = cur->bc_bufs[level];
+ lbno = XFS_DADDR_TO_FSB(args.mp, XFS_BUF_ADDR(lbp));
+ left = XFS_BUF_TO_BMBT_BLOCK(lbp);
+ args.fsbno = cur->bc_private.b.firstblock;
+ if (args.fsbno == NULLFSBLOCK) {
+ args.fsbno = lbno;
+ args.type = XFS_ALLOCTYPE_START_BNO;
+ } else if (cur->bc_private.b.flist->xbf_low)
+ args.type = XFS_ALLOCTYPE_FIRST_AG;
+ else
+ args.type = XFS_ALLOCTYPE_NEAR_BNO;
+ args.mod = args.minleft = args.alignment = args.total = args.isfl =
+ args.userdata = args.minalignslop = 0;
+ args.minlen = args.maxlen = args.prod = 1;
+ args.wasdel = cur->bc_private.b.flags & XFS_BTCUR_BPRV_WASDEL;
+ if (!args.wasdel && xfs_trans_get_block_res(args.tp) == 0) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return XFS_ERROR(ENOSPC);
+ }
+ if ((error = xfs_alloc_vextent(&args))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+ if (args.fsbno == NULLFSBLOCK) {
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *stat = 0;
+ return 0;
+ }
+ ASSERT(args.len == 1);
+ cur->bc_private.b.firstblock = args.fsbno;
+ cur->bc_private.b.allocated++;
+ cur->bc_private.b.ip->i_d.di_nblocks++;
+ xfs_trans_log_inode(args.tp, cur->bc_private.b.ip, XFS_ILOG_CORE);
+ XFS_TRANS_MOD_DQUOT_BYINO(args.mp, args.tp, cur->bc_private.b.ip,
+ XFS_TRANS_DQ_BCOUNT, 1L);
+ rbp = xfs_btree_get_bufl(args.mp, args.tp, args.fsbno, 0);
+ right = XFS_BUF_TO_BMBT_BLOCK(rbp);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_lblock(cur, left, level, rbp))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+#endif
+ INT_SET(right->bb_magic, ARCH_CONVERT, XFS_BMAP_MAGIC);
+ right->bb_level = left->bb_level; /* INT_: direct copy */
+ INT_SET(right->bb_numrecs, ARCH_CONVERT, (__uint16_t)(INT_GET(left->bb_numrecs, ARCH_CONVERT) / 2));
+ if ((INT_GET(left->bb_numrecs, ARCH_CONVERT) & 1) &&
+ cur->bc_ptrs[level] <= INT_GET(right->bb_numrecs, ARCH_CONVERT) + 1)
+ INT_MOD(right->bb_numrecs, ARCH_CONVERT, +1);
+ i = INT_GET(left->bb_numrecs, ARCH_CONVERT) - INT_GET(right->bb_numrecs, ARCH_CONVERT) + 1;
+ if (level > 0) {
+ lkp = XFS_BMAP_KEY_IADDR(left, i, cur);
+ lpp = XFS_BMAP_PTR_IADDR(left, i, cur);
+ rkp = XFS_BMAP_KEY_IADDR(right, 1, cur);
+ rpp = XFS_BMAP_PTR_IADDR(right, 1, cur);
+#ifdef DEBUG
+ for (i = 0; i < INT_GET(right->bb_numrecs, ARCH_CONVERT); i++) {
+ if ((error = xfs_btree_check_lptr(cur, INT_GET(lpp[i], ARCH_CONVERT), level))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+ }
+#endif
+ memcpy(rkp, lkp, INT_GET(right->bb_numrecs, ARCH_CONVERT) * sizeof(*rkp));
+ memcpy(rpp, lpp, INT_GET(right->bb_numrecs, ARCH_CONVERT) * sizeof(*rpp));
+ xfs_bmbt_log_keys(cur, rbp, 1, INT_GET(right->bb_numrecs, ARCH_CONVERT));
+ xfs_bmbt_log_ptrs(cur, rbp, 1, INT_GET(right->bb_numrecs, ARCH_CONVERT));
+ keyp->br_startoff = INT_GET(rkp->br_startoff, ARCH_CONVERT);
+ } else {
+ lrp = XFS_BMAP_REC_IADDR(left, i, cur);
+ rrp = XFS_BMAP_REC_IADDR(right, 1, cur);
+ memcpy(rrp, lrp, INT_GET(right->bb_numrecs, ARCH_CONVERT) * sizeof(*rrp));
+ xfs_bmbt_log_recs(cur, rbp, 1, INT_GET(right->bb_numrecs, ARCH_CONVERT));
+ keyp->br_startoff = xfs_bmbt_disk_get_startoff(rrp);
+ }
+ INT_MOD(left->bb_numrecs, ARCH_CONVERT, -(INT_GET(right->bb_numrecs, ARCH_CONVERT)));
+ right->bb_rightsib = left->bb_rightsib; /* INT_: direct copy */
+ INT_SET(left->bb_rightsib, ARCH_CONVERT, args.fsbno);
+ INT_SET(right->bb_leftsib, ARCH_CONVERT, lbno);
+ xfs_bmbt_log_block(cur, rbp, XFS_BB_ALL_BITS);
+ xfs_bmbt_log_block(cur, lbp, XFS_BB_NUMRECS | XFS_BB_RIGHTSIB);
+ if (INT_GET(right->bb_rightsib, ARCH_CONVERT) != NULLDFSBNO) {
+ if ((error = xfs_btree_read_bufl(args.mp, args.tp,
+ INT_GET(right->bb_rightsib, ARCH_CONVERT), 0, &rrbp,
+ XFS_BMAP_BTREE_REF))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+ rrblock = XFS_BUF_TO_BMBT_BLOCK(rrbp);
+ if ((error = xfs_btree_check_lblock(cur, rrblock, level, rrbp))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+ INT_SET(rrblock->bb_leftsib, ARCH_CONVERT, args.fsbno);
+ xfs_bmbt_log_block(cur, rrbp, XFS_BB_LEFTSIB);
+ }
+ if (cur->bc_ptrs[level] > INT_GET(left->bb_numrecs, ARCH_CONVERT) + 1) {
+ xfs_btree_setbuf(cur, level, rbp);
+ cur->bc_ptrs[level] -= INT_GET(left->bb_numrecs, ARCH_CONVERT);
+ }
+ if (level + 1 < cur->bc_nlevels) {
+ if ((error = xfs_btree_dup_cursor(cur, curp))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+ (*curp)->bc_ptrs[level + 1]++;
+ }
+ *bnop = args.fsbno;
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *stat = 1;
+ return 0;
+}
+
+
+/*
+ * Update keys for the record.
+ */
+STATIC int
+xfs_bmbt_updkey(
+ xfs_btree_cur_t *cur,
+ xfs_bmbt_key_t *keyp, /* on-disk format */
+ int level)
+{
+ xfs_bmbt_block_t *block;
+ xfs_buf_t *bp;
+#ifdef DEBUG
+ int error;
+#endif
+#ifdef XFS_BMBT_TRACE
+ static char fname[] = "xfs_bmbt_updkey";
+#endif
+ xfs_bmbt_key_t *kp;
+ int ptr;
+
+ ASSERT(level >= 1);
+ XFS_BMBT_TRACE_CURSOR(cur, ENTRY);
+ XFS_BMBT_TRACE_ARGIK(cur, level, keyp);
+ for (ptr = 1; ptr == 1 && level < cur->bc_nlevels; level++) {
+ block = xfs_bmbt_get_block(cur, level, &bp);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_lblock(cur, block, level, bp))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+#endif
+ ptr = cur->bc_ptrs[level];
+ kp = XFS_BMAP_KEY_IADDR(block, ptr, cur);
+ *kp = *keyp;
+ xfs_bmbt_log_keys(cur, bp, ptr, ptr);
+ }
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ return 0;
+}
+
+/*
+ * Convert on-disk form of btree root to in-memory form.
+ */
+void
+xfs_bmdr_to_bmbt(
+ xfs_bmdr_block_t *dblock,
+ int dblocklen,
+ xfs_bmbt_block_t *rblock,
+ int rblocklen)
+{
+ int dmxr;
+ xfs_bmbt_key_t *fkp;
+ xfs_bmbt_ptr_t *fpp;
+ xfs_bmbt_key_t *tkp;
+ xfs_bmbt_ptr_t *tpp;
+
+ INT_SET(rblock->bb_magic, ARCH_CONVERT, XFS_BMAP_MAGIC);
+ rblock->bb_level = dblock->bb_level; /* both in on-disk format */
+ ASSERT(INT_GET(rblock->bb_level, ARCH_CONVERT) > 0);
+ rblock->bb_numrecs = dblock->bb_numrecs;/* both in on-disk format */
+ INT_SET(rblock->bb_leftsib, ARCH_CONVERT, NULLDFSBNO);
+ INT_SET(rblock->bb_rightsib, ARCH_CONVERT, NULLDFSBNO);
+ dmxr = (int)XFS_BTREE_BLOCK_MAXRECS(dblocklen, xfs_bmdr, 0);
+ fkp = XFS_BTREE_KEY_ADDR(dblocklen, xfs_bmdr, dblock, 1, dmxr);
+ tkp = XFS_BMAP_BROOT_KEY_ADDR(rblock, 1, rblocklen);
+ fpp = XFS_BTREE_PTR_ADDR(dblocklen, xfs_bmdr, dblock, 1, dmxr);
+ tpp = XFS_BMAP_BROOT_PTR_ADDR(rblock, 1, rblocklen);
+ dmxr = INT_GET(dblock->bb_numrecs, ARCH_CONVERT);
+ memcpy(tkp, fkp, sizeof(*fkp) * dmxr);
+ memcpy(tpp, fpp, sizeof(*fpp) * dmxr); /* INT_: direct copy */
+}
+
+/*
+ * Decrement cursor by one record at the level.
+ * For nonzero levels the leaf-ward information is untouched.
+ */
+int /* error */
+xfs_bmbt_decrement(
+ xfs_btree_cur_t *cur,
+ int level,
+ int *stat) /* success/failure */
+{
+ xfs_bmbt_block_t *block;
+ xfs_buf_t *bp;
+ int error; /* error return value */
+#ifdef XFS_BMBT_TRACE
+ static char fname[] = "xfs_bmbt_decrement";
+#endif
+ xfs_fsblock_t fsbno;
+ int lev;
+ xfs_mount_t *mp;
+ xfs_trans_t *tp;
+
+ XFS_BMBT_TRACE_CURSOR(cur, ENTRY);
+ XFS_BMBT_TRACE_ARGI(cur, level);
+ ASSERT(level < cur->bc_nlevels);
+ if (level < cur->bc_nlevels - 1)
+ xfs_btree_readahead(cur, level, XFS_BTCUR_LEFTRA);
+ if (--cur->bc_ptrs[level] > 0) {
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *stat = 1;
+ return 0;
+ }
+ block = xfs_bmbt_get_block(cur, level, &bp);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_lblock(cur, block, level, bp))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+#endif
+ if (INT_GET(block->bb_leftsib, ARCH_CONVERT) == NULLDFSBNO) {
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *stat = 0;
+ return 0;
+ }
+ for (lev = level + 1; lev < cur->bc_nlevels; lev++) {
+ if (--cur->bc_ptrs[lev] > 0)
+ break;
+ if (lev < cur->bc_nlevels - 1)
+ xfs_btree_readahead(cur, lev, XFS_BTCUR_LEFTRA);
+ }
+ if (lev == cur->bc_nlevels) {
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *stat = 0;
+ return 0;
+ }
+ tp = cur->bc_tp;
+ mp = cur->bc_mp;
+ for (block = xfs_bmbt_get_block(cur, lev, &bp); lev > level; ) {
+ fsbno = INT_GET(*XFS_BMAP_PTR_IADDR(block, cur->bc_ptrs[lev], cur), ARCH_CONVERT);
+ if ((error = xfs_btree_read_bufl(mp, tp, fsbno, 0, &bp,
+ XFS_BMAP_BTREE_REF))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+ lev--;
+ xfs_btree_setbuf(cur, lev, bp);
+ block = XFS_BUF_TO_BMBT_BLOCK(bp);
+ if ((error = xfs_btree_check_lblock(cur, block, lev, bp))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+ cur->bc_ptrs[lev] = INT_GET(block->bb_numrecs, ARCH_CONVERT);
+ }
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *stat = 1;
+ return 0;
+}
+
+/*
+ * Delete the record pointed to by cur.
+ */
+int /* error */
+xfs_bmbt_delete(
+ xfs_btree_cur_t *cur,
+ int *stat) /* success/failure */
+{
+ int error; /* error return value */
+#ifdef XFS_BMBT_TRACE
+ static char fname[] = "xfs_bmbt_delete";
+#endif
+ int i;
+ int level;
+
+ XFS_BMBT_TRACE_CURSOR(cur, ENTRY);
+ for (level = 0, i = 2; i == 2; level++) {
+ if ((error = xfs_bmbt_delrec(cur, level, &i))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+ }
+ if (i == 0) {
+ for (level = 1; level < cur->bc_nlevels; level++) {
+ if (cur->bc_ptrs[level] == 0) {
+ if ((error = xfs_bmbt_decrement(cur, level,
+ &i))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+ break;
+ }
+ }
+ }
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *stat = i;
+ return 0;
+}
+
+/*
+ * Convert a compressed bmap extent record to an uncompressed form.
+ * This code must be in sync with the routines xfs_bmbt_get_startoff,
+ * xfs_bmbt_get_startblock, xfs_bmbt_get_blockcount and xfs_bmbt_get_state.
+ */
+
+STATIC __inline__ void
+__xfs_bmbt_get_all(
+ __uint64_t l0,
+ __uint64_t l1,
+ xfs_bmbt_irec_t *s)
+{
+ int ext_flag;
+ xfs_exntst_t st;
+
+ ext_flag = (int)(l0 >> (64 - BMBT_EXNTFLAG_BITLEN));
+ s->br_startoff = ((xfs_fileoff_t)l0 &
+ XFS_MASK64LO(64 - BMBT_EXNTFLAG_BITLEN)) >> 9;
+#if XFS_BIG_BLKNOS
+ s->br_startblock = (((xfs_fsblock_t)l0 & XFS_MASK64LO(9)) << 43) |
+ (((xfs_fsblock_t)l1) >> 21);
+#else
+#ifdef DEBUG
+ {
+ xfs_dfsbno_t b;
+
+ b = (((xfs_dfsbno_t)l0 & XFS_MASK64LO(9)) << 43) |
+ (((xfs_dfsbno_t)l1) >> 21);
+ ASSERT((b >> 32) == 0 || ISNULLDSTARTBLOCK(b));
+ s->br_startblock = (xfs_fsblock_t)b;
+ }
+#else /* !DEBUG */
+ s->br_startblock = (xfs_fsblock_t)(((xfs_dfsbno_t)l1) >> 21);
+#endif /* DEBUG */
+#endif /* XFS_BIG_BLKNOS */
+ s->br_blockcount = (xfs_filblks_t)(l1 & XFS_MASK64LO(21));
+ /* This is xfs_extent_state() in-line */
+ if (ext_flag) {
+ ASSERT(s->br_blockcount != 0); /* saved for DMIG */
+ st = XFS_EXT_UNWRITTEN;
+ } else
+ st = XFS_EXT_NORM;
+ s->br_state = st;
+}
+
+void
+xfs_bmbt_get_all(
+ xfs_bmbt_rec_t *r,
+ xfs_bmbt_irec_t *s)
+{
+ __xfs_bmbt_get_all(r->l0, r->l1, s);
+}
+
+/*
+ * Get the block pointer for the given level of the cursor.
+ * Fill in the buffer pointer, if applicable.
+ */
+xfs_bmbt_block_t *
+xfs_bmbt_get_block(
+ xfs_btree_cur_t *cur,
+ int level,
+ xfs_buf_t **bpp)
+{
+ xfs_ifork_t *ifp;
+ xfs_bmbt_block_t *rval;
+
+ if (level < cur->bc_nlevels - 1) {
+ *bpp = cur->bc_bufs[level];
+ rval = XFS_BUF_TO_BMBT_BLOCK(*bpp);
+ } else {
+ *bpp = NULL;
+ ifp = XFS_IFORK_PTR(cur->bc_private.b.ip,
+ cur->bc_private.b.whichfork);
+ rval = ifp->if_broot;
+ }
+ return rval;
+}
+
+/*
+ * Extract the blockcount field from an in memory bmap extent record.
+ */
+xfs_filblks_t
+xfs_bmbt_get_blockcount(
+ xfs_bmbt_rec_t *r)
+{
+ return (xfs_filblks_t)(r->l1 & XFS_MASK64LO(21));
+}
+
+/*
+ * Extract the startblock field from an in memory bmap extent record.
+ */
+xfs_fsblock_t
+xfs_bmbt_get_startblock(
+ xfs_bmbt_rec_t *r)
+{
+#if XFS_BIG_BLKNOS
+ return (((xfs_fsblock_t)r->l0 & XFS_MASK64LO(9)) << 43) |
+ (((xfs_fsblock_t)r->l1) >> 21);
+#else
+#ifdef DEBUG
+ xfs_dfsbno_t b;
+
+ b = (((xfs_dfsbno_t)r->l0 & XFS_MASK64LO(9)) << 43) |
+ (((xfs_dfsbno_t)r->l1) >> 21);
+ ASSERT((b >> 32) == 0 || ISNULLDSTARTBLOCK(b));
+ return (xfs_fsblock_t)b;
+#else /* !DEBUG */
+ return (xfs_fsblock_t)(((xfs_dfsbno_t)r->l1) >> 21);
+#endif /* DEBUG */
+#endif /* XFS_BIG_BLKNOS */
+}
+
+/*
+ * Extract the startoff field from an in memory bmap extent record.
+ */
+xfs_fileoff_t
+xfs_bmbt_get_startoff(
+ xfs_bmbt_rec_t *r)
+{
+ return ((xfs_fileoff_t)r->l0 &
+ XFS_MASK64LO(64 - BMBT_EXNTFLAG_BITLEN)) >> 9;
+}
+
+xfs_exntst_t
+xfs_bmbt_get_state(
+ xfs_bmbt_rec_t *r)
+{
+ int ext_flag;
+
+ ext_flag = (int)((r->l0) >> (64 - BMBT_EXNTFLAG_BITLEN));
+ return xfs_extent_state(xfs_bmbt_get_blockcount(r),
+ ext_flag);
+}
+
+#if ARCH_CONVERT != ARCH_NOCONVERT
+/* Endian flipping versions of the bmbt extraction functions */
+void
+xfs_bmbt_disk_get_all(
+ xfs_bmbt_rec_t *r,
+ xfs_bmbt_irec_t *s)
+{
+ __uint64_t l0, l1;
+
+ l0 = INT_GET(r->l0, ARCH_CONVERT);
+ l1 = INT_GET(r->l1, ARCH_CONVERT);
+
+ __xfs_bmbt_get_all(l0, l1, s);
+}
+
+/*
+ * Extract the blockcount field from an on disk bmap extent record.
+ */
+xfs_filblks_t
+xfs_bmbt_disk_get_blockcount(
+ xfs_bmbt_rec_t *r)
+{
+ return (xfs_filblks_t)(INT_GET(r->l1, ARCH_CONVERT) & XFS_MASK64LO(21));
+}
+
+/*
+ * Extract the startblock field from an on disk bmap extent record.
+ */
+xfs_fsblock_t
+xfs_bmbt_disk_get_startblock(
+ xfs_bmbt_rec_t *r)
+{
+#if XFS_BIG_BLKNOS
+ return (((xfs_fsblock_t)INT_GET(r->l0, ARCH_CONVERT) & XFS_MASK64LO(9)) << 43) |
+ (((xfs_fsblock_t)INT_GET(r->l1, ARCH_CONVERT)) >> 21);
+#else
+#ifdef DEBUG
+ xfs_dfsbno_t b;
+
+ b = (((xfs_dfsbno_t)INT_GET(r->l0, ARCH_CONVERT) & XFS_MASK64LO(9)) << 43) |
+ (((xfs_dfsbno_t)INT_GET(r->l1, ARCH_CONVERT)) >> 21);
+ ASSERT((b >> 32) == 0 || ISNULLDSTARTBLOCK(b));
+ return (xfs_fsblock_t)b;
+#else /* !DEBUG */
+ return (xfs_fsblock_t)(((xfs_dfsbno_t)INT_GET(r->l1, ARCH_CONVERT)) >> 21);
+#endif /* DEBUG */
+#endif /* XFS_BIG_BLKNOS */
+}
+
+/*
+ * Extract the startoff field from a disk format bmap extent record.
+ */
+xfs_fileoff_t
+xfs_bmbt_disk_get_startoff(
+ xfs_bmbt_rec_t *r)
+{
+ return ((xfs_fileoff_t)INT_GET(r->l0, ARCH_CONVERT) &
+ XFS_MASK64LO(64 - BMBT_EXNTFLAG_BITLEN)) >> 9;
+}
+
+xfs_exntst_t
+xfs_bmbt_disk_get_state(
+ xfs_bmbt_rec_t *r)
+{
+ int ext_flag;
+
+ ext_flag = (int)((INT_GET(r->l0, ARCH_CONVERT)) >> (64 - BMBT_EXNTFLAG_BITLEN));
+ return xfs_extent_state(xfs_bmbt_disk_get_blockcount(r),
+ ext_flag);
+}
+#endif
+
+
+/*
+ * Increment cursor by one record at the level.
+ * For nonzero levels the leaf-ward information is untouched.
+ */
+int /* error */
+xfs_bmbt_increment(
+ xfs_btree_cur_t *cur,
+ int level,
+ int *stat) /* success/failure */
+{
+ xfs_bmbt_block_t *block;
+ xfs_buf_t *bp;
+ int error; /* error return value */
+#ifdef XFS_BMBT_TRACE
+ static char fname[] = "xfs_bmbt_increment";
+#endif
+ xfs_fsblock_t fsbno;
+ int lev;
+ xfs_mount_t *mp;
+ xfs_trans_t *tp;
+
+ XFS_BMBT_TRACE_CURSOR(cur, ENTRY);
+ XFS_BMBT_TRACE_ARGI(cur, level);
+ ASSERT(level < cur->bc_nlevels);
+ if (level < cur->bc_nlevels - 1)
+ xfs_btree_readahead(cur, level, XFS_BTCUR_RIGHTRA);
+ block = xfs_bmbt_get_block(cur, level, &bp);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_lblock(cur, block, level, bp))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+#endif
+ if (++cur->bc_ptrs[level] <= INT_GET(block->bb_numrecs, ARCH_CONVERT)) {
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *stat = 1;
+ return 0;
+ }
+ if (INT_GET(block->bb_rightsib, ARCH_CONVERT) == NULLDFSBNO) {
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *stat = 0;
+ return 0;
+ }
+ for (lev = level + 1; lev < cur->bc_nlevels; lev++) {
+ block = xfs_bmbt_get_block(cur, lev, &bp);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_lblock(cur, block, lev, bp))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+#endif
+ if (++cur->bc_ptrs[lev] <= INT_GET(block->bb_numrecs, ARCH_CONVERT))
+ break;
+ if (lev < cur->bc_nlevels - 1)
+ xfs_btree_readahead(cur, lev, XFS_BTCUR_RIGHTRA);
+ }
+ if (lev == cur->bc_nlevels) {
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *stat = 0;
+ return 0;
+ }
+ tp = cur->bc_tp;
+ mp = cur->bc_mp;
+ for (block = xfs_bmbt_get_block(cur, lev, &bp); lev > level; ) {
+ fsbno = INT_GET(*XFS_BMAP_PTR_IADDR(block, cur->bc_ptrs[lev], cur), ARCH_CONVERT);
+ if ((error = xfs_btree_read_bufl(mp, tp, fsbno, 0, &bp,
+ XFS_BMAP_BTREE_REF))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+ lev--;
+ xfs_btree_setbuf(cur, lev, bp);
+ block = XFS_BUF_TO_BMBT_BLOCK(bp);
+ if ((error = xfs_btree_check_lblock(cur, block, lev, bp))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+ cur->bc_ptrs[lev] = 1;
+ }
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *stat = 1;
+ return 0;
+}
+
+/*
+ * Insert the current record at the point referenced by cur.
+ */
+int /* error */
+xfs_bmbt_insert(
+ xfs_btree_cur_t *cur,
+ int *stat) /* success/failure */
+{
+ int error; /* error return value */
+#ifdef XFS_BMBT_TRACE
+ static char fname[] = "xfs_bmbt_insert";
+#endif
+ int i;
+ int level;
+ xfs_fsblock_t nbno;
+ xfs_btree_cur_t *ncur;
+ xfs_bmbt_rec_t nrec;
+ xfs_btree_cur_t *pcur;
+
+ XFS_BMBT_TRACE_CURSOR(cur, ENTRY);
+ level = 0;
+ nbno = NULLFSBLOCK;
+ xfs_bmbt_disk_set_all(&nrec, &cur->bc_rec.b);
+ ncur = (xfs_btree_cur_t *)0;
+ pcur = cur;
+ do {
+ if ((error = xfs_bmbt_insrec(pcur, level++, &nbno, &nrec, &ncur,
+ &i))) {
+ if (pcur != cur)
+ xfs_btree_del_cursor(pcur, XFS_BTREE_ERROR);
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ if (pcur != cur && (ncur || nbno == NULLFSBLOCK)) {
+ cur->bc_nlevels = pcur->bc_nlevels;
+ cur->bc_private.b.allocated +=
+ pcur->bc_private.b.allocated;
+ pcur->bc_private.b.allocated = 0;
+ ASSERT((cur->bc_private.b.firstblock != NULLFSBLOCK) ||
+ (cur->bc_private.b.ip->i_d.di_flags &
+ XFS_DIFLAG_REALTIME));
+ cur->bc_private.b.firstblock =
+ pcur->bc_private.b.firstblock;
+ ASSERT(cur->bc_private.b.flist ==
+ pcur->bc_private.b.flist);
+ xfs_btree_del_cursor(pcur, XFS_BTREE_NOERROR);
+ }
+ if (ncur) {
+ pcur = ncur;
+ ncur = (xfs_btree_cur_t *)0;
+ }
+ } while (nbno != NULLFSBLOCK);
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *stat = i;
+ return 0;
+error0:
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+}
+
+/*
+ * Log fields from the btree block header.
+ */
+void
+xfs_bmbt_log_block(
+ xfs_btree_cur_t *cur,
+ xfs_buf_t *bp,
+ int fields)
+{
+ int first;
+#ifdef XFS_BMBT_TRACE
+ static char fname[] = "xfs_bmbt_log_block";
+#endif
+ int last;
+ xfs_trans_t *tp;
+ static const short offsets[] = {
+ offsetof(xfs_bmbt_block_t, bb_magic),
+ offsetof(xfs_bmbt_block_t, bb_level),
+ offsetof(xfs_bmbt_block_t, bb_numrecs),
+ offsetof(xfs_bmbt_block_t, bb_leftsib),
+ offsetof(xfs_bmbt_block_t, bb_rightsib),
+ sizeof(xfs_bmbt_block_t)
+ };
+
+ XFS_BMBT_TRACE_CURSOR(cur, ENTRY);
+ XFS_BMBT_TRACE_ARGBI(cur, bp, fields);
+ tp = cur->bc_tp;
+ if (bp) {
+ xfs_btree_offsets(fields, offsets, XFS_BB_NUM_BITS, &first,
+ &last);
+ xfs_trans_log_buf(tp, bp, first, last);
+ } else
+ xfs_trans_log_inode(tp, cur->bc_private.b.ip,
+ XFS_ILOG_FBROOT(cur->bc_private.b.whichfork));
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+}
+
+/*
+ * Log record values from the btree block.
+ */
+void
+xfs_bmbt_log_recs(
+ xfs_btree_cur_t *cur,
+ xfs_buf_t *bp,
+ int rfirst,
+ int rlast)
+{
+ xfs_bmbt_block_t *block;
+ int first;
+#ifdef XFS_BMBT_TRACE
+ static char fname[] = "xfs_bmbt_log_recs";
+#endif
+ int last;
+ xfs_bmbt_rec_t *rp;
+ xfs_trans_t *tp;
+
+ XFS_BMBT_TRACE_CURSOR(cur, ENTRY);
+ XFS_BMBT_TRACE_ARGBII(cur, bp, rfirst, rlast);
+ ASSERT(bp);
+ tp = cur->bc_tp;
+ block = XFS_BUF_TO_BMBT_BLOCK(bp);
+ rp = XFS_BMAP_REC_DADDR(block, 1, cur);
+ first = (int)((xfs_caddr_t)&rp[rfirst - 1] - (xfs_caddr_t)block);
+ last = (int)(((xfs_caddr_t)&rp[rlast] - 1) - (xfs_caddr_t)block);
+ xfs_trans_log_buf(tp, bp, first, last);
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+}
+
+int /* error */
+xfs_bmbt_lookup_eq(
+ xfs_btree_cur_t *cur,
+ xfs_fileoff_t off,
+ xfs_fsblock_t bno,
+ xfs_filblks_t len,
+ int *stat) /* success/failure */
+{
+ cur->bc_rec.b.br_startoff = off;
+ cur->bc_rec.b.br_startblock = bno;
+ cur->bc_rec.b.br_blockcount = len;
+ return xfs_bmbt_lookup(cur, XFS_LOOKUP_EQ, stat);
+}
+
+int /* error */
+xfs_bmbt_lookup_ge(
+ xfs_btree_cur_t *cur,
+ xfs_fileoff_t off,
+ xfs_fsblock_t bno,
+ xfs_filblks_t len,
+ int *stat) /* success/failure */
+{
+ cur->bc_rec.b.br_startoff = off;
+ cur->bc_rec.b.br_startblock = bno;
+ cur->bc_rec.b.br_blockcount = len;
+ return xfs_bmbt_lookup(cur, XFS_LOOKUP_GE, stat);
+}
+
+int /* error */
+xfs_bmbt_lookup_le(
+ xfs_btree_cur_t *cur,
+ xfs_fileoff_t off,
+ xfs_fsblock_t bno,
+ xfs_filblks_t len,
+ int *stat) /* success/failure */
+{
+ cur->bc_rec.b.br_startoff = off;
+ cur->bc_rec.b.br_startblock = bno;
+ cur->bc_rec.b.br_blockcount = len;
+ return xfs_bmbt_lookup(cur, XFS_LOOKUP_LE, stat);
+}
+
+/*
+ * Give the bmap btree a new root block. Copy the old broot contents
+ * down into a real block and make the broot point to it.
+ */
+int /* error */
+xfs_bmbt_newroot(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ int *logflags, /* logging flags for inode */
+ int *stat) /* return status - 0 fail */
+{
+ xfs_alloc_arg_t args; /* allocation arguments */
+ xfs_bmbt_block_t *block; /* bmap btree block */
+ xfs_buf_t *bp; /* buffer for block */
+ xfs_bmbt_block_t *cblock; /* child btree block */
+ xfs_bmbt_key_t *ckp; /* child key pointer */
+ xfs_bmbt_ptr_t *cpp; /* child ptr pointer */
+ int error; /* error return code */
+#ifdef XFS_BMBT_TRACE
+ static char fname[] = "xfs_bmbt_newroot";
+#endif
+#ifdef DEBUG
+ int i; /* loop counter */
+#endif
+ xfs_bmbt_key_t *kp; /* pointer to bmap btree key */
+ int level; /* btree level */
+ xfs_bmbt_ptr_t *pp; /* pointer to bmap block addr */
+
+ XFS_BMBT_TRACE_CURSOR(cur, ENTRY);
+ level = cur->bc_nlevels - 1;
+ block = xfs_bmbt_get_block(cur, level, &bp);
+ /*
+ * Copy the root into a real block.
+ */
+ args.mp = cur->bc_mp;
+ pp = XFS_BMAP_PTR_IADDR(block, 1, cur);
+ args.tp = cur->bc_tp;
+ args.fsbno = cur->bc_private.b.firstblock;
+ args.mod = args.minleft = args.alignment = args.total = args.isfl =
+ args.userdata = args.minalignslop = 0;
+ args.minlen = args.maxlen = args.prod = 1;
+ args.wasdel = cur->bc_private.b.flags & XFS_BTCUR_BPRV_WASDEL;
+ if (args.fsbno == NULLFSBLOCK) {
+#ifdef DEBUG
+ if ((error = xfs_btree_check_lptr(cur, INT_GET(*pp, ARCH_CONVERT), level))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+#endif
+ args.fsbno = INT_GET(*pp, ARCH_CONVERT);
+ args.type = XFS_ALLOCTYPE_START_BNO;
+ } else if (args.wasdel)
+ args.type = XFS_ALLOCTYPE_FIRST_AG;
+ else
+ args.type = XFS_ALLOCTYPE_NEAR_BNO;
+ if ((error = xfs_alloc_vextent(&args))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+ if (args.fsbno == NULLFSBLOCK) {
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *stat = 0;
+ return 0;
+ }
+ ASSERT(args.len == 1);
+ cur->bc_private.b.firstblock = args.fsbno;
+ cur->bc_private.b.allocated++;
+ cur->bc_private.b.ip->i_d.di_nblocks++;
+ XFS_TRANS_MOD_DQUOT_BYINO(args.mp, args.tp, cur->bc_private.b.ip,
+ XFS_TRANS_DQ_BCOUNT, 1L);
+ bp = xfs_btree_get_bufl(args.mp, cur->bc_tp, args.fsbno, 0);
+ cblock = XFS_BUF_TO_BMBT_BLOCK(bp);
+ *cblock = *block;
+ INT_MOD(block->bb_level, ARCH_CONVERT, +1);
+ INT_SET(block->bb_numrecs, ARCH_CONVERT, 1);
+ cur->bc_nlevels++;
+ cur->bc_ptrs[level + 1] = 1;
+ kp = XFS_BMAP_KEY_IADDR(block, 1, cur);
+ ckp = XFS_BMAP_KEY_IADDR(cblock, 1, cur);
+ memcpy(ckp, kp, INT_GET(cblock->bb_numrecs, ARCH_CONVERT) * sizeof(*kp));
+ cpp = XFS_BMAP_PTR_IADDR(cblock, 1, cur);
+#ifdef DEBUG
+ for (i = 0; i < INT_GET(cblock->bb_numrecs, ARCH_CONVERT); i++) {
+ if ((error = xfs_btree_check_lptr(cur, INT_GET(pp[i], ARCH_CONVERT), level))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+ }
+#endif
+ memcpy(cpp, pp, INT_GET(cblock->bb_numrecs, ARCH_CONVERT) * sizeof(*pp));
+#ifdef DEBUG
+ if ((error = xfs_btree_check_lptr(cur, (xfs_bmbt_ptr_t)args.fsbno,
+ level))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+#endif
+ INT_SET(*pp, ARCH_CONVERT, args.fsbno);
+ xfs_iroot_realloc(cur->bc_private.b.ip, 1 - INT_GET(cblock->bb_numrecs, ARCH_CONVERT),
+ cur->bc_private.b.whichfork);
+ xfs_btree_setbuf(cur, level, bp);
+ /*
+ * Do all this logging at the end so that
+ * the root is at the right level.
+ */
+ xfs_bmbt_log_block(cur, bp, XFS_BB_ALL_BITS);
+ xfs_bmbt_log_keys(cur, bp, 1, INT_GET(cblock->bb_numrecs, ARCH_CONVERT));
+ xfs_bmbt_log_ptrs(cur, bp, 1, INT_GET(cblock->bb_numrecs, ARCH_CONVERT));
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ *logflags |=
+ XFS_ILOG_CORE | XFS_ILOG_FBROOT(cur->bc_private.b.whichfork);
+ *stat = 1;
+ return 0;
+}
+
+/*
+ * Set all the fields in a bmap extent record from the uncompressed form.
+ */
+void
+xfs_bmbt_set_all(
+ xfs_bmbt_rec_t *r,
+ xfs_bmbt_irec_t *s)
+{
+ int extent_flag;
+
+ ASSERT((s->br_state == XFS_EXT_NORM) ||
+ (s->br_state == XFS_EXT_UNWRITTEN));
+ extent_flag = (s->br_state == XFS_EXT_NORM) ? 0 : 1;
+ ASSERT((s->br_startoff & XFS_MASK64HI(9)) == 0);
+ ASSERT((s->br_blockcount & XFS_MASK64HI(43)) == 0);
+#if XFS_BIG_BLKNOS
+ ASSERT((s->br_startblock & XFS_MASK64HI(12)) == 0);
+ r->l0 = ((xfs_bmbt_rec_base_t)extent_flag << 63) |
+ ((xfs_bmbt_rec_base_t)s->br_startoff << 9) |
+ ((xfs_bmbt_rec_base_t)s->br_startblock >> 43);
+ r->l1 = ((xfs_bmbt_rec_base_t)s->br_startblock << 21) |
+ ((xfs_bmbt_rec_base_t)s->br_blockcount &
+ (xfs_bmbt_rec_base_t)XFS_MASK64LO(21));
+#else /* !XFS_BIG_BLKNOS */
+ if (ISNULLSTARTBLOCK(s->br_startblock)) {
+ r->l0 = ((xfs_bmbt_rec_base_t)extent_flag << 63) |
+ ((xfs_bmbt_rec_base_t)s->br_startoff << 9) |
+ (xfs_bmbt_rec_base_t)XFS_MASK64LO(9);
+ r->l1 = XFS_MASK64HI(11) |
+ ((xfs_bmbt_rec_base_t)s->br_startblock << 21) |
+ ((xfs_bmbt_rec_base_t)s->br_blockcount &
+ (xfs_bmbt_rec_base_t)XFS_MASK64LO(21));
+ } else {
+ r->l0 = ((xfs_bmbt_rec_base_t)extent_flag << 63) |
+ ((xfs_bmbt_rec_base_t)s->br_startoff << 9);
+ r->l1 = ((xfs_bmbt_rec_base_t)s->br_startblock << 21) |
+ ((xfs_bmbt_rec_base_t)s->br_blockcount &
+ (xfs_bmbt_rec_base_t)XFS_MASK64LO(21));
+ }
+#endif /* XFS_BIG_BLKNOS */
+}
+
+/*
+ * Set all the fields in a bmap extent record from the arguments.
+ */
+void
+xfs_bmbt_set_allf(
+ xfs_bmbt_rec_t *r,
+ xfs_fileoff_t o,
+ xfs_fsblock_t b,
+ xfs_filblks_t c,
+ xfs_exntst_t v)
+{
+ int extent_flag;
+
+ ASSERT((v == XFS_EXT_NORM) || (v == XFS_EXT_UNWRITTEN));
+ extent_flag = (v == XFS_EXT_NORM) ? 0 : 1;
+ ASSERT((o & XFS_MASK64HI(64-BMBT_STARTOFF_BITLEN)) == 0);
+ ASSERT((c & XFS_MASK64HI(64-BMBT_BLOCKCOUNT_BITLEN)) == 0);
+#if XFS_BIG_BLKNOS
+ ASSERT((b & XFS_MASK64HI(64-BMBT_STARTBLOCK_BITLEN)) == 0);
+ r->l0 = ((xfs_bmbt_rec_base_t)extent_flag << 63) |
+ ((xfs_bmbt_rec_base_t)o << 9) |
+ ((xfs_bmbt_rec_base_t)b >> 43);
+ r->l1 = ((xfs_bmbt_rec_base_t)b << 21) |
+ ((xfs_bmbt_rec_base_t)c &
+ (xfs_bmbt_rec_base_t)XFS_MASK64LO(21));
+#else /* !XFS_BIG_BLKNOS */
+ if (ISNULLSTARTBLOCK(b)) {
+ r->l0 = ((xfs_bmbt_rec_base_t)extent_flag << 63) |
+ ((xfs_bmbt_rec_base_t)o << 9) |
+ (xfs_bmbt_rec_base_t)XFS_MASK64LO(9);
+ r->l1 = XFS_MASK64HI(11) |
+ ((xfs_bmbt_rec_base_t)b << 21) |
+ ((xfs_bmbt_rec_base_t)c &
+ (xfs_bmbt_rec_base_t)XFS_MASK64LO(21));
+ } else {
+ r->l0 = ((xfs_bmbt_rec_base_t)extent_flag << 63) |
+ ((xfs_bmbt_rec_base_t)o << 9);
+ r->l1 = ((xfs_bmbt_rec_base_t)b << 21) |
+ ((xfs_bmbt_rec_base_t)c &
+ (xfs_bmbt_rec_base_t)XFS_MASK64LO(21));
+ }
+#endif /* XFS_BIG_BLKNOS */
+}
+
+#if ARCH_CONVERT != ARCH_NOCONVERT
+/*
+ * Set all the fields in a bmap extent record from the uncompressed form.
+ */
+void
+xfs_bmbt_disk_set_all(
+ xfs_bmbt_rec_t *r,
+ xfs_bmbt_irec_t *s)
+{
+ int extent_flag;
+
+ ASSERT((s->br_state == XFS_EXT_NORM) ||
+ (s->br_state == XFS_EXT_UNWRITTEN));
+ extent_flag = (s->br_state == XFS_EXT_NORM) ? 0 : 1;
+ ASSERT((s->br_startoff & XFS_MASK64HI(9)) == 0);
+ ASSERT((s->br_blockcount & XFS_MASK64HI(43)) == 0);
+#if XFS_BIG_BLKNOS
+ ASSERT((s->br_startblock & XFS_MASK64HI(12)) == 0);
+ INT_SET(r->l0, ARCH_CONVERT, ((xfs_bmbt_rec_base_t)extent_flag << 63) |
+ ((xfs_bmbt_rec_base_t)s->br_startoff << 9) |
+ ((xfs_bmbt_rec_base_t)s->br_startblock >> 43));
+ INT_SET(r->l1, ARCH_CONVERT, ((xfs_bmbt_rec_base_t)s->br_startblock << 21) |
+ ((xfs_bmbt_rec_base_t)s->br_blockcount &
+ (xfs_bmbt_rec_base_t)XFS_MASK64LO(21)));
+#else /* !XFS_BIG_BLKNOS */
+ if (ISNULLSTARTBLOCK(s->br_startblock)) {
+ INT_SET(r->l0, ARCH_CONVERT, ((xfs_bmbt_rec_base_t)extent_flag << 63) |
+ ((xfs_bmbt_rec_base_t)s->br_startoff << 9) |
+ (xfs_bmbt_rec_base_t)XFS_MASK64LO(9));
+ INT_SET(r->l1, ARCH_CONVERT, XFS_MASK64HI(11) |
+ ((xfs_bmbt_rec_base_t)s->br_startblock << 21) |
+ ((xfs_bmbt_rec_base_t)s->br_blockcount &
+ (xfs_bmbt_rec_base_t)XFS_MASK64LO(21)));
+ } else {
+ INT_SET(r->l0, ARCH_CONVERT, ((xfs_bmbt_rec_base_t)extent_flag << 63) |
+ ((xfs_bmbt_rec_base_t)s->br_startoff << 9));
+ INT_SET(r->l1, ARCH_CONVERT, ((xfs_bmbt_rec_base_t)s->br_startblock << 21) |
+ ((xfs_bmbt_rec_base_t)s->br_blockcount &
+ (xfs_bmbt_rec_base_t)XFS_MASK64LO(21)));
+ }
+#endif /* XFS_BIG_BLKNOS */
+}
+
+/*
+ * Set all the fields in a disk format bmap extent record from the arguments.
+ */
+void
+xfs_bmbt_disk_set_allf(
+ xfs_bmbt_rec_t *r,
+ xfs_fileoff_t o,
+ xfs_fsblock_t b,
+ xfs_filblks_t c,
+ xfs_exntst_t v)
+{
+ int extent_flag;
+
+ ASSERT((v == XFS_EXT_NORM) || (v == XFS_EXT_UNWRITTEN));
+ extent_flag = (v == XFS_EXT_NORM) ? 0 : 1;
+ ASSERT((o & XFS_MASK64HI(64-BMBT_STARTOFF_BITLEN)) == 0);
+ ASSERT((c & XFS_MASK64HI(64-BMBT_BLOCKCOUNT_BITLEN)) == 0);
+#if XFS_BIG_BLKNOS
+ ASSERT((b & XFS_MASK64HI(64-BMBT_STARTBLOCK_BITLEN)) == 0);
+ INT_SET(r->l0, ARCH_CONVERT, ((xfs_bmbt_rec_base_t)extent_flag << 63) |
+ ((xfs_bmbt_rec_base_t)o << 9) |
+ ((xfs_bmbt_rec_base_t)b >> 43));
+ INT_SET(r->l1, ARCH_CONVERT, ((xfs_bmbt_rec_base_t)b << 21) |
+ ((xfs_bmbt_rec_base_t)c &
+ (xfs_bmbt_rec_base_t)XFS_MASK64LO(21)));
+#else /* !XFS_BIG_BLKNOS */
+ if (ISNULLSTARTBLOCK(b)) {
+ INT_SET(r->l0, ARCH_CONVERT, ((xfs_bmbt_rec_base_t)extent_flag << 63) |
+ ((xfs_bmbt_rec_base_t)o << 9) |
+ (xfs_bmbt_rec_base_t)XFS_MASK64LO(9));
+ INT_SET(r->l1, ARCH_CONVERT, XFS_MASK64HI(11) |
+ ((xfs_bmbt_rec_base_t)b << 21) |
+ ((xfs_bmbt_rec_base_t)c &
+ (xfs_bmbt_rec_base_t)XFS_MASK64LO(21)));
+ } else {
+ INT_SET(r->l0, ARCH_CONVERT, ((xfs_bmbt_rec_base_t)extent_flag << 63) |
+ ((xfs_bmbt_rec_base_t)o << 9));
+ INT_SET(r->l1, ARCH_CONVERT, ((xfs_bmbt_rec_base_t)b << 21) |
+ ((xfs_bmbt_rec_base_t)c &
+ (xfs_bmbt_rec_base_t)XFS_MASK64LO(21)));
+ }
+#endif /* XFS_BIG_BLKNOS */
+}
+#endif
+
+/*
+ * Set the blockcount field in a bmap extent record.
+ */
+void
+xfs_bmbt_set_blockcount(
+ xfs_bmbt_rec_t *r,
+ xfs_filblks_t v)
+{
+ ASSERT((v & XFS_MASK64HI(43)) == 0);
+ r->l1 = (r->l1 & (xfs_bmbt_rec_base_t)XFS_MASK64HI(43)) |
+ (xfs_bmbt_rec_base_t)(v & XFS_MASK64LO(21));
+}
+
+/*
+ * Set the startblock field in a bmap extent record.
+ */
+void
+xfs_bmbt_set_startblock(
+ xfs_bmbt_rec_t *r,
+ xfs_fsblock_t v)
+{
+#if XFS_BIG_BLKNOS
+ ASSERT((v & XFS_MASK64HI(12)) == 0);
+ r->l0 = (r->l0 & (xfs_bmbt_rec_base_t)XFS_MASK64HI(55)) |
+ (xfs_bmbt_rec_base_t)(v >> 43);
+ r->l1 = (r->l1 & (xfs_bmbt_rec_base_t)XFS_MASK64LO(21)) |
+ (xfs_bmbt_rec_base_t)(v << 21);
+#else /* !XFS_BIG_BLKNOS */
+ if (ISNULLSTARTBLOCK(v)) {
+ r->l0 |= (xfs_bmbt_rec_base_t)XFS_MASK64LO(9);
+ r->l1 = (xfs_bmbt_rec_base_t)XFS_MASK64HI(11) |
+ ((xfs_bmbt_rec_base_t)v << 21) |
+ (r->l1 & (xfs_bmbt_rec_base_t)XFS_MASK64LO(21));
+ } else {
+ r->l0 &= ~(xfs_bmbt_rec_base_t)XFS_MASK64LO(9);
+ r->l1 = ((xfs_bmbt_rec_base_t)v << 21) |
+ (r->l1 & (xfs_bmbt_rec_base_t)XFS_MASK64LO(21));
+ }
+#endif /* XFS_BIG_BLKNOS */
+}
+
+/*
+ * Set the startoff field in a bmap extent record.
+ */
+void
+xfs_bmbt_set_startoff(
+ xfs_bmbt_rec_t *r,
+ xfs_fileoff_t v)
+{
+ ASSERT((v & XFS_MASK64HI(9)) == 0);
+ r->l0 = (r->l0 & (xfs_bmbt_rec_base_t) XFS_MASK64HI(1)) |
+ ((xfs_bmbt_rec_base_t)v << 9) |
+ (r->l0 & (xfs_bmbt_rec_base_t)XFS_MASK64LO(9));
+}
+
+/*
+ * Set the extent state field in a bmap extent record.
+ */
+void
+xfs_bmbt_set_state(
+ xfs_bmbt_rec_t *r,
+ xfs_exntst_t v)
+{
+ ASSERT(v == XFS_EXT_NORM || v == XFS_EXT_UNWRITTEN);
+ if (v == XFS_EXT_NORM)
+ r->l0 &= XFS_MASK64LO(64 - BMBT_EXNTFLAG_BITLEN);
+ else
+ r->l0 |= XFS_MASK64HI(BMBT_EXNTFLAG_BITLEN);
+}
+
+/*
+ * Convert in-memory form of btree root to on-disk form.
+ */
+void
+xfs_bmbt_to_bmdr(
+ xfs_bmbt_block_t *rblock,
+ int rblocklen,
+ xfs_bmdr_block_t *dblock,
+ int dblocklen)
+{
+ int dmxr;
+ xfs_bmbt_key_t *fkp;
+ xfs_bmbt_ptr_t *fpp;
+ xfs_bmbt_key_t *tkp;
+ xfs_bmbt_ptr_t *tpp;
+
+ ASSERT(INT_GET(rblock->bb_magic, ARCH_CONVERT) == XFS_BMAP_MAGIC);
+ ASSERT(INT_GET(rblock->bb_leftsib, ARCH_CONVERT) == NULLDFSBNO);
+ ASSERT(INT_GET(rblock->bb_rightsib, ARCH_CONVERT) == NULLDFSBNO);
+ ASSERT(INT_GET(rblock->bb_level, ARCH_CONVERT) > 0);
+ dblock->bb_level = rblock->bb_level; /* both in on-disk format */
+ dblock->bb_numrecs = rblock->bb_numrecs;/* both in on-disk format */
+ dmxr = (int)XFS_BTREE_BLOCK_MAXRECS(dblocklen, xfs_bmdr, 0);
+ fkp = XFS_BMAP_BROOT_KEY_ADDR(rblock, 1, rblocklen);
+ tkp = XFS_BTREE_KEY_ADDR(dblocklen, xfs_bmdr, dblock, 1, dmxr);
+ fpp = XFS_BMAP_BROOT_PTR_ADDR(rblock, 1, rblocklen);
+ tpp = XFS_BTREE_PTR_ADDR(dblocklen, xfs_bmdr, dblock, 1, dmxr);
+ dmxr = INT_GET(dblock->bb_numrecs, ARCH_CONVERT);
+ memcpy(tkp, fkp, sizeof(*fkp) * dmxr);
+ memcpy(tpp, fpp, sizeof(*fpp) * dmxr); /* INT_: direct copy */
+}
+
+/*
+ * Update the record to the passed values.
+ */
+int
+xfs_bmbt_update(
+ xfs_btree_cur_t *cur,
+ xfs_fileoff_t off,
+ xfs_fsblock_t bno,
+ xfs_filblks_t len,
+ xfs_exntst_t state)
+{
+ xfs_bmbt_block_t *block;
+ xfs_buf_t *bp;
+ int error;
+#ifdef XFS_BMBT_TRACE
+ static char fname[] = "xfs_bmbt_update";
+#endif
+ xfs_bmbt_key_t key;
+ int ptr;
+ xfs_bmbt_rec_t *rp;
+
+ XFS_BMBT_TRACE_CURSOR(cur, ENTRY);
+ XFS_BMBT_TRACE_ARGFFFI(cur, (xfs_dfiloff_t)off, (xfs_dfsbno_t)bno,
+ (xfs_dfilblks_t)len, (int)state);
+ block = xfs_bmbt_get_block(cur, 0, &bp);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_lblock(cur, block, 0, bp))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+#endif
+ ptr = cur->bc_ptrs[0];
+ rp = XFS_BMAP_REC_IADDR(block, ptr, cur);
+ xfs_bmbt_disk_set_allf(rp, off, bno, len, state);
+ xfs_bmbt_log_recs(cur, bp, ptr, ptr);
+ if (ptr > 1) {
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ return 0;
+ }
+ INT_SET(key.br_startoff, ARCH_CONVERT, off);
+ if ((error = xfs_bmbt_updkey(cur, &key, 1))) {
+ XFS_BMBT_TRACE_CURSOR(cur, ERROR);
+ return error;
+ }
+ XFS_BMBT_TRACE_CURSOR(cur, EXIT);
+ return 0;
+}
+
+/*
+ * Check an extent list, which has just been read, for
+ * any bit in the extent flag field. ASSERT on debug
+ * kernels, as this condition should not occur.
+ * Return an error condition (1) if any flags found,
+ * otherwise return 0.
+ */
+
+int
+xfs_check_nostate_extents(
+ xfs_bmbt_rec_t *ep,
+ xfs_extnum_t num)
+{
+ for (; num > 0; num--, ep++) {
+ if ((ep->l0 >>
+ (64 - BMBT_EXNTFLAG_BITLEN)) != 0) {
+ ASSERT(0);
+ return 1;
+ }
+ }
+ return 0;
+}
diff --git a/fs/xfs/xfs_bmap_btree.h b/fs/xfs/xfs_bmap_btree.h
new file mode 100644
index 00000000000000..1c0904d0857bf1
--- /dev/null
+++ b/fs/xfs/xfs_bmap_btree.h
@@ -0,0 +1,701 @@
+/*
+ * Copyright (c) 2000,2002-2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_BMAP_BTREE_H__
+#define __XFS_BMAP_BTREE_H__
+
+#define XFS_BMAP_MAGIC 0x424d4150 /* 'BMAP' */
+
+struct xfs_btree_cur;
+struct xfs_btree_lblock;
+struct xfs_mount;
+struct xfs_inode;
+
+/*
+ * Bmap root header, on-disk form only.
+ */
+typedef struct xfs_bmdr_block
+{
+ __uint16_t bb_level; /* 0 is a leaf */
+ __uint16_t bb_numrecs; /* current # of data records */
+} xfs_bmdr_block_t;
+
+/*
+ * Bmap btree record and extent descriptor.
+ * For 32-bit kernels,
+ * l0:31 is an extent flag (value 1 indicates non-normal).
+ * l0:0-30 and l1:9-31 are startoff.
+ * l1:0-8, l2:0-31, and l3:21-31 are startblock.
+ * l3:0-20 are blockcount.
+ * For 64-bit kernels,
+ * l0:63 is an extent flag (value 1 indicates non-normal).
+ * l0:9-62 are startoff.
+ * l0:0-8 and l1:21-63 are startblock.
+ * l1:0-20 are blockcount.
+ */
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+#define BMBT_TOTAL_BITLEN 128 /* 128 bits, 16 bytes */
+#define BMBT_EXNTFLAG_BITOFF 0
+#define BMBT_EXNTFLAG_BITLEN 1
+#define BMBT_STARTOFF_BITOFF (BMBT_EXNTFLAG_BITOFF + BMBT_EXNTFLAG_BITLEN)
+#define BMBT_STARTOFF_BITLEN 54
+#define BMBT_STARTBLOCK_BITOFF (BMBT_STARTOFF_BITOFF + BMBT_STARTOFF_BITLEN)
+#define BMBT_STARTBLOCK_BITLEN 52
+#define BMBT_BLOCKCOUNT_BITOFF \
+ (BMBT_STARTBLOCK_BITOFF + BMBT_STARTBLOCK_BITLEN)
+#define BMBT_BLOCKCOUNT_BITLEN (BMBT_TOTAL_BITLEN - BMBT_BLOCKCOUNT_BITOFF)
+
+#else
+
+#define BMBT_TOTAL_BITLEN 128 /* 128 bits, 16 bytes */
+#define BMBT_EXNTFLAG_BITOFF 63
+#define BMBT_EXNTFLAG_BITLEN 1
+#define BMBT_STARTOFF_BITOFF (BMBT_EXNTFLAG_BITOFF - BMBT_STARTOFF_BITLEN)
+#define BMBT_STARTOFF_BITLEN 54
+#define BMBT_STARTBLOCK_BITOFF 85 /* 128 - 43 (other 9 is in first word) */
+#define BMBT_STARTBLOCK_BITLEN 52
+#define BMBT_BLOCKCOUNT_BITOFF 64 /* Start of second 64 bit container */
+#define BMBT_BLOCKCOUNT_BITLEN 21
+
+#endif
+
+
+#define BMBT_USE_64 1
+
+typedef struct xfs_bmbt_rec_32
+{
+ __uint32_t l0, l1, l2, l3;
+} xfs_bmbt_rec_32_t;
+typedef struct xfs_bmbt_rec_64
+{
+ __uint64_t l0, l1;
+} xfs_bmbt_rec_64_t;
+
+typedef __uint64_t xfs_bmbt_rec_base_t; /* use this for casts */
+typedef xfs_bmbt_rec_64_t xfs_bmbt_rec_t, xfs_bmdr_rec_t;
+
+/*
+ * Values and macros for delayed-allocation startblock fields.
+ */
+#define STARTBLOCKVALBITS 17
+#define STARTBLOCKMASKBITS (15 + XFS_BIG_BLKNOS * 20)
+#define DSTARTBLOCKMASKBITS (15 + 20)
+#define STARTBLOCKMASK \
+ (((((xfs_fsblock_t)1) << STARTBLOCKMASKBITS) - 1) << STARTBLOCKVALBITS)
+#define DSTARTBLOCKMASK \
+ (((((xfs_dfsbno_t)1) << DSTARTBLOCKMASKBITS) - 1) << STARTBLOCKVALBITS)
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_ISNULLSTARTBLOCK)
+int isnullstartblock(xfs_fsblock_t x);
+#define ISNULLSTARTBLOCK(x) isnullstartblock(x)
+#else
+#define ISNULLSTARTBLOCK(x) (((x) & STARTBLOCKMASK) == STARTBLOCKMASK)
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_ISNULLDSTARTBLOCK)
+int isnulldstartblock(xfs_dfsbno_t x);
+#define ISNULLDSTARTBLOCK(x) isnulldstartblock(x)
+#else
+#define ISNULLDSTARTBLOCK(x) (((x) & DSTARTBLOCKMASK) == DSTARTBLOCKMASK)
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_NULLSTARTBLOCK)
+xfs_fsblock_t nullstartblock(int k);
+#define NULLSTARTBLOCK(k) nullstartblock(k)
+#else
+#define NULLSTARTBLOCK(k) \
+ ((ASSERT(k < (1 << STARTBLOCKVALBITS))), (STARTBLOCKMASK | (k)))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_STARTBLOCKVAL)
+xfs_filblks_t startblockval(xfs_fsblock_t x);
+#define STARTBLOCKVAL(x) startblockval(x)
+#else
+#define STARTBLOCKVAL(x) ((xfs_filblks_t)((x) & ~STARTBLOCKMASK))
+#endif
+
+/*
+ * Possible extent formats.
+ */
+typedef enum {
+ XFS_EXTFMT_NOSTATE = 0,
+ XFS_EXTFMT_HASSTATE
+} xfs_exntfmt_t;
+
+/*
+ * Possible extent states.
+ */
+typedef enum {
+ XFS_EXT_NORM, XFS_EXT_UNWRITTEN,
+ XFS_EXT_DMAPI_OFFLINE, XFS_EXT_INVALID
+} xfs_exntst_t;
+
+/*
+ * Extent state and extent format macros.
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_EXTFMT_INODE )
+xfs_exntfmt_t xfs_extfmt_inode(struct xfs_inode *ip);
+#define XFS_EXTFMT_INODE(x) xfs_extfmt_inode(x)
+#else
+#define XFS_EXTFMT_INODE(x) \
+ (XFS_SB_VERSION_HASEXTFLGBIT(&((x)->i_mount->m_sb)) ? \
+ XFS_EXTFMT_HASSTATE : XFS_EXTFMT_NOSTATE)
+#endif
+#define ISUNWRITTEN(x) ((x)->br_state == XFS_EXT_UNWRITTEN)
+
+/*
+ * Incore version of above.
+ */
+typedef struct xfs_bmbt_irec
+{
+ xfs_fileoff_t br_startoff; /* starting file offset */
+ xfs_fsblock_t br_startblock; /* starting block number */
+ xfs_filblks_t br_blockcount; /* number of blocks */
+ xfs_exntst_t br_state; /* extent state */
+} xfs_bmbt_irec_t;
+
+/*
+ * Key structure for non-leaf levels of the tree.
+ */
+typedef struct xfs_bmbt_key
+{
+ xfs_dfiloff_t br_startoff; /* starting file offset */
+} xfs_bmbt_key_t, xfs_bmdr_key_t;
+
+typedef xfs_dfsbno_t xfs_bmbt_ptr_t, xfs_bmdr_ptr_t; /* btree pointer type */
+ /* btree block header type */
+typedef struct xfs_btree_lblock xfs_bmbt_block_t;
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BUF_TO_BMBT_BLOCK)
+xfs_bmbt_block_t *xfs_buf_to_bmbt_block(struct xfs_buf *bp);
+#define XFS_BUF_TO_BMBT_BLOCK(bp) xfs_buf_to_bmbt_block(bp)
+#else
+#define XFS_BUF_TO_BMBT_BLOCK(bp) ((xfs_bmbt_block_t *)(XFS_BUF_PTR(bp)))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BMAP_RBLOCK_DSIZE)
+int xfs_bmap_rblock_dsize(int lev, struct xfs_btree_cur *cur);
+#define XFS_BMAP_RBLOCK_DSIZE(lev,cur) xfs_bmap_rblock_dsize(lev,cur)
+#else
+#define XFS_BMAP_RBLOCK_DSIZE(lev,cur) ((cur)->bc_private.b.forksize)
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BMAP_RBLOCK_ISIZE)
+int xfs_bmap_rblock_isize(int lev, struct xfs_btree_cur *cur);
+#define XFS_BMAP_RBLOCK_ISIZE(lev,cur) xfs_bmap_rblock_isize(lev,cur)
+#else
+#define XFS_BMAP_RBLOCK_ISIZE(lev,cur) \
+ ((int)XFS_IFORK_PTR((cur)->bc_private.b.ip, \
+ (cur)->bc_private.b.whichfork)->if_broot_bytes)
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BMAP_IBLOCK_SIZE)
+int xfs_bmap_iblock_size(int lev, struct xfs_btree_cur *cur);
+#define XFS_BMAP_IBLOCK_SIZE(lev,cur) xfs_bmap_iblock_size(lev,cur)
+#else
+#define XFS_BMAP_IBLOCK_SIZE(lev,cur) (1 << (cur)->bc_blocklog)
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BMAP_BLOCK_DSIZE)
+int xfs_bmap_block_dsize(int lev, struct xfs_btree_cur *cur);
+#define XFS_BMAP_BLOCK_DSIZE(lev,cur) xfs_bmap_block_dsize(lev,cur)
+#else
+#define XFS_BMAP_BLOCK_DSIZE(lev,cur) \
+ ((lev) == (cur)->bc_nlevels - 1 ? \
+ XFS_BMAP_RBLOCK_DSIZE(lev,cur) : \
+ XFS_BMAP_IBLOCK_SIZE(lev,cur))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BMAP_BLOCK_ISIZE)
+int xfs_bmap_block_isize(int lev, struct xfs_btree_cur *cur);
+#define XFS_BMAP_BLOCK_ISIZE(lev,cur) xfs_bmap_block_isize(lev,cur)
+#else
+#define XFS_BMAP_BLOCK_ISIZE(lev,cur) \
+ ((lev) == (cur)->bc_nlevels - 1 ? \
+ XFS_BMAP_RBLOCK_ISIZE(lev,cur) : \
+ XFS_BMAP_IBLOCK_SIZE(lev,cur))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BMAP_BLOCK_DMAXRECS)
+int xfs_bmap_block_dmaxrecs(int lev, struct xfs_btree_cur *cur);
+#define XFS_BMAP_BLOCK_DMAXRECS(lev,cur) xfs_bmap_block_dmaxrecs(lev,cur)
+#else
+#define XFS_BMAP_BLOCK_DMAXRECS(lev,cur) \
+ ((lev) == (cur)->bc_nlevels - 1 ? \
+ XFS_BTREE_BLOCK_MAXRECS(XFS_BMAP_RBLOCK_DSIZE(lev,cur), \
+ xfs_bmdr, (lev) == 0) : \
+ ((cur)->bc_mp->m_bmap_dmxr[(lev) != 0]))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BMAP_BLOCK_IMAXRECS)
+int xfs_bmap_block_imaxrecs(int lev, struct xfs_btree_cur *cur);
+#define XFS_BMAP_BLOCK_IMAXRECS(lev,cur) xfs_bmap_block_imaxrecs(lev,cur)
+#else
+#define XFS_BMAP_BLOCK_IMAXRECS(lev,cur) \
+ ((lev) == (cur)->bc_nlevels - 1 ? \
+ XFS_BTREE_BLOCK_MAXRECS(XFS_BMAP_RBLOCK_ISIZE(lev,cur), \
+ xfs_bmbt, (lev) == 0) : \
+ ((cur)->bc_mp->m_bmap_dmxr[(lev) != 0]))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BMAP_BLOCK_DMINRECS)
+int xfs_bmap_block_dminrecs(int lev, struct xfs_btree_cur *cur);
+#define XFS_BMAP_BLOCK_DMINRECS(lev,cur) xfs_bmap_block_dminrecs(lev,cur)
+#else
+#define XFS_BMAP_BLOCK_DMINRECS(lev,cur) \
+ ((lev) == (cur)->bc_nlevels - 1 ? \
+ XFS_BTREE_BLOCK_MINRECS(XFS_BMAP_RBLOCK_DSIZE(lev,cur), \
+ xfs_bmdr, (lev) == 0) : \
+ ((cur)->bc_mp->m_bmap_dmnr[(lev) != 0]))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BMAP_BLOCK_IMINRECS)
+int xfs_bmap_block_iminrecs(int lev, struct xfs_btree_cur *cur);
+#define XFS_BMAP_BLOCK_IMINRECS(lev,cur) xfs_bmap_block_iminrecs(lev,cur)
+#else
+#define XFS_BMAP_BLOCK_IMINRECS(lev,cur) \
+ ((lev) == (cur)->bc_nlevels - 1 ? \
+ XFS_BTREE_BLOCK_MINRECS(XFS_BMAP_RBLOCK_ISIZE(lev,cur), \
+ xfs_bmbt, (lev) == 0) : \
+ ((cur)->bc_mp->m_bmap_dmnr[(lev) != 0]))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BMAP_REC_DADDR)
+xfs_bmbt_rec_t *
+xfs_bmap_rec_daddr(xfs_bmbt_block_t *bb, int i, struct xfs_btree_cur *cur);
+#define XFS_BMAP_REC_DADDR(bb,i,cur) xfs_bmap_rec_daddr(bb,i,cur)
+#else
+#define XFS_BMAP_REC_DADDR(bb,i,cur) \
+ XFS_BTREE_REC_ADDR(XFS_BMAP_BLOCK_DSIZE( \
+ INT_GET((bb)->bb_level, ARCH_CONVERT), cur), \
+ xfs_bmbt, bb, i, XFS_BMAP_BLOCK_DMAXRECS( \
+ INT_GET((bb)->bb_level, ARCH_CONVERT), cur))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BMAP_REC_IADDR)
+xfs_bmbt_rec_t *
+xfs_bmap_rec_iaddr(xfs_bmbt_block_t *bb, int i, struct xfs_btree_cur *cur);
+#define XFS_BMAP_REC_IADDR(bb,i,cur) xfs_bmap_rec_iaddr(bb,i,cur)
+#else
+#define XFS_BMAP_REC_IADDR(bb,i,cur) \
+ XFS_BTREE_REC_ADDR(XFS_BMAP_BLOCK_ISIZE( \
+ INT_GET((bb)->bb_level, ARCH_CONVERT), cur), \
+ xfs_bmbt, bb, i, XFS_BMAP_BLOCK_IMAXRECS( \
+ INT_GET((bb)->bb_level, ARCH_CONVERT), cur))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BMAP_KEY_DADDR)
+xfs_bmbt_key_t *
+xfs_bmap_key_daddr(xfs_bmbt_block_t *bb, int i, struct xfs_btree_cur *cur);
+#define XFS_BMAP_KEY_DADDR(bb,i,cur) xfs_bmap_key_daddr(bb,i,cur)
+#else
+#define XFS_BMAP_KEY_DADDR(bb,i,cur) \
+ XFS_BTREE_KEY_ADDR(XFS_BMAP_BLOCK_DSIZE( \
+ INT_GET((bb)->bb_level, ARCH_CONVERT), cur), \
+ xfs_bmbt, bb, i, XFS_BMAP_BLOCK_DMAXRECS( \
+ INT_GET((bb)->bb_level, ARCH_CONVERT), cur))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BMAP_KEY_IADDR)
+xfs_bmbt_key_t *
+xfs_bmap_key_iaddr(xfs_bmbt_block_t *bb, int i, struct xfs_btree_cur *cur);
+#define XFS_BMAP_KEY_IADDR(bb,i,cur) xfs_bmap_key_iaddr(bb,i,cur)
+#else
+#define XFS_BMAP_KEY_IADDR(bb,i,cur) \
+ XFS_BTREE_KEY_ADDR(XFS_BMAP_BLOCK_ISIZE( \
+ INT_GET((bb)->bb_level, ARCH_CONVERT), cur), \
+ xfs_bmbt, bb, i, XFS_BMAP_BLOCK_IMAXRECS( \
+ INT_GET((bb)->bb_level, ARCH_CONVERT), cur))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BMAP_PTR_DADDR)
+xfs_bmbt_ptr_t *
+xfs_bmap_ptr_daddr(xfs_bmbt_block_t *bb, int i, struct xfs_btree_cur *cur);
+#define XFS_BMAP_PTR_DADDR(bb,i,cur) xfs_bmap_ptr_daddr(bb,i,cur)
+#else
+#define XFS_BMAP_PTR_DADDR(bb,i,cur) \
+ XFS_BTREE_PTR_ADDR(XFS_BMAP_BLOCK_DSIZE( \
+ INT_GET((bb)->bb_level, ARCH_CONVERT), cur), \
+ xfs_bmbt, bb, i, XFS_BMAP_BLOCK_DMAXRECS( \
+ INT_GET((bb)->bb_level, ARCH_CONVERT), cur))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BMAP_PTR_IADDR)
+xfs_bmbt_ptr_t *
+xfs_bmap_ptr_iaddr(xfs_bmbt_block_t *bb, int i, struct xfs_btree_cur *cur);
+#define XFS_BMAP_PTR_IADDR(bb,i,cur) xfs_bmap_ptr_iaddr(bb,i,cur)
+#else
+#define XFS_BMAP_PTR_IADDR(bb,i,cur) \
+ XFS_BTREE_PTR_ADDR(XFS_BMAP_BLOCK_ISIZE( \
+ INT_GET((bb)->bb_level, ARCH_CONVERT), cur), \
+ xfs_bmbt, bb, i, XFS_BMAP_BLOCK_IMAXRECS( \
+ INT_GET((bb)->bb_level, ARCH_CONVERT), cur))
+#endif
+
+/*
+ * These are to be used when we know the size of the block and
+ * we don't have a cursor.
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BMAP_BROOT_REC_ADDR)
+xfs_bmbt_rec_t *xfs_bmap_broot_rec_addr(xfs_bmbt_block_t *bb, int i, int sz);
+#define XFS_BMAP_BROOT_REC_ADDR(bb,i,sz) xfs_bmap_broot_rec_addr(bb,i,sz)
+#else
+#define XFS_BMAP_BROOT_REC_ADDR(bb,i,sz) \
+ XFS_BTREE_REC_ADDR(sz,xfs_bmbt,bb,i,XFS_BMAP_BROOT_MAXRECS(sz))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BMAP_BROOT_KEY_ADDR)
+xfs_bmbt_key_t *xfs_bmap_broot_key_addr(xfs_bmbt_block_t *bb, int i, int sz);
+#define XFS_BMAP_BROOT_KEY_ADDR(bb,i,sz) xfs_bmap_broot_key_addr(bb,i,sz)
+#else
+#define XFS_BMAP_BROOT_KEY_ADDR(bb,i,sz) \
+ XFS_BTREE_KEY_ADDR(sz,xfs_bmbt,bb,i,XFS_BMAP_BROOT_MAXRECS(sz))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BMAP_BROOT_PTR_ADDR)
+xfs_bmbt_ptr_t *xfs_bmap_broot_ptr_addr(xfs_bmbt_block_t *bb, int i, int sz);
+#define XFS_BMAP_BROOT_PTR_ADDR(bb,i,sz) xfs_bmap_broot_ptr_addr(bb,i,sz)
+#else
+#define XFS_BMAP_BROOT_PTR_ADDR(bb,i,sz) \
+ XFS_BTREE_PTR_ADDR(sz,xfs_bmbt,bb,i,XFS_BMAP_BROOT_MAXRECS(sz))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BMAP_BROOT_NUMRECS)
+int xfs_bmap_broot_numrecs(xfs_bmdr_block_t *bb);
+#define XFS_BMAP_BROOT_NUMRECS(bb) xfs_bmap_broot_numrecs(bb)
+#else
+#define XFS_BMAP_BROOT_NUMRECS(bb) (INT_GET((bb)->bb_numrecs, ARCH_CONVERT))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BMAP_BROOT_MAXRECS)
+int xfs_bmap_broot_maxrecs(int sz);
+#define XFS_BMAP_BROOT_MAXRECS(sz) xfs_bmap_broot_maxrecs(sz)
+#else
+#define XFS_BMAP_BROOT_MAXRECS(sz) XFS_BTREE_BLOCK_MAXRECS(sz,xfs_bmbt,0)
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BMAP_BROOT_SPACE_CALC)
+int xfs_bmap_broot_space_calc(int nrecs);
+#define XFS_BMAP_BROOT_SPACE_CALC(nrecs) xfs_bmap_broot_space_calc(nrecs)
+#else
+#define XFS_BMAP_BROOT_SPACE_CALC(nrecs) \
+ ((int)(sizeof(xfs_bmbt_block_t) + \
+ ((nrecs) * (sizeof(xfs_bmbt_key_t) + sizeof(xfs_bmbt_ptr_t)))))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BMAP_BROOT_SPACE)
+int xfs_bmap_broot_space(xfs_bmdr_block_t *bb);
+#define XFS_BMAP_BROOT_SPACE(bb) xfs_bmap_broot_space(bb)
+#else
+#define XFS_BMAP_BROOT_SPACE(bb) \
+ XFS_BMAP_BROOT_SPACE_CALC(INT_GET((bb)->bb_numrecs, ARCH_CONVERT))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BMDR_SPACE_CALC)
+int xfs_bmdr_space_calc(int nrecs);
+#define XFS_BMDR_SPACE_CALC(nrecs) xfs_bmdr_space_calc(nrecs)
+#else
+#define XFS_BMDR_SPACE_CALC(nrecs) \
+ ((int)(sizeof(xfs_bmdr_block_t) + \
+ ((nrecs) * (sizeof(xfs_bmbt_key_t) + sizeof(xfs_bmbt_ptr_t)))))
+#endif
+
+/*
+ * Maximum number of bmap btree levels.
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BM_MAXLEVELS)
+int xfs_bm_maxlevels(struct xfs_mount *mp, int w);
+#define XFS_BM_MAXLEVELS(mp,w) xfs_bm_maxlevels(mp,w)
+#else
+#define XFS_BM_MAXLEVELS(mp,w) ((mp)->m_bm_maxlevels[w])
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BMAP_SANITY_CHECK)
+int xfs_bmap_sanity_check(struct xfs_mount *mp, xfs_bmbt_block_t *bb,
+ int level);
+#define XFS_BMAP_SANITY_CHECK(mp,bb,level) \
+ xfs_bmap_sanity_check(mp,bb,level)
+#else
+#define XFS_BMAP_SANITY_CHECK(mp,bb,level) \
+ (INT_GET((bb)->bb_magic, ARCH_CONVERT) == XFS_BMAP_MAGIC && \
+ INT_GET((bb)->bb_level, ARCH_CONVERT) == level && \
+ INT_GET((bb)->bb_numrecs, ARCH_CONVERT) > 0 && \
+ INT_GET((bb)->bb_numrecs, ARCH_CONVERT) <= (mp)->m_bmap_dmxr[(level) != 0])
+#endif
+
+
+#ifdef __KERNEL__
+
+#if defined(XFS_BMBT_TRACE)
+/*
+ * Trace buffer entry types.
+ */
+#define XFS_BMBT_KTRACE_ARGBI 1
+#define XFS_BMBT_KTRACE_ARGBII 2
+#define XFS_BMBT_KTRACE_ARGFFFI 3
+#define XFS_BMBT_KTRACE_ARGI 4
+#define XFS_BMBT_KTRACE_ARGIFK 5
+#define XFS_BMBT_KTRACE_ARGIFR 6
+#define XFS_BMBT_KTRACE_ARGIK 7
+#define XFS_BMBT_KTRACE_CUR 8
+
+#define XFS_BMBT_TRACE_SIZE 4096 /* size of global trace buffer */
+#define XFS_BMBT_KTRACE_SIZE 32 /* size of per-inode trace buffer */
+extern ktrace_t *xfs_bmbt_trace_buf;
+#endif
+
+/*
+ * Prototypes for xfs_bmap.c to call.
+ */
+
+void
+xfs_bmdr_to_bmbt(
+ xfs_bmdr_block_t *,
+ int,
+ xfs_bmbt_block_t *,
+ int);
+
+int
+xfs_bmbt_decrement(
+ struct xfs_btree_cur *,
+ int,
+ int *);
+
+int
+xfs_bmbt_delete(
+ struct xfs_btree_cur *,
+ int *);
+
+void
+xfs_bmbt_get_all(
+ xfs_bmbt_rec_t *r,
+ xfs_bmbt_irec_t *s);
+
+xfs_bmbt_block_t *
+xfs_bmbt_get_block(
+ struct xfs_btree_cur *cur,
+ int level,
+ struct xfs_buf **bpp);
+
+xfs_filblks_t
+xfs_bmbt_get_blockcount(
+ xfs_bmbt_rec_t *r);
+
+xfs_fsblock_t
+xfs_bmbt_get_startblock(
+ xfs_bmbt_rec_t *r);
+
+xfs_fileoff_t
+xfs_bmbt_get_startoff(
+ xfs_bmbt_rec_t *r);
+
+xfs_exntst_t
+xfs_bmbt_get_state(
+ xfs_bmbt_rec_t *r);
+
+#if ARCH_CONVERT != ARCH_NOCONVERT
+void
+xfs_bmbt_disk_get_all(
+ xfs_bmbt_rec_t *r,
+ xfs_bmbt_irec_t *s);
+
+xfs_exntst_t
+xfs_bmbt_disk_get_state(
+ xfs_bmbt_rec_t *r);
+
+xfs_filblks_t
+xfs_bmbt_disk_get_blockcount(
+ xfs_bmbt_rec_t *r);
+
+xfs_fsblock_t
+xfs_bmbt_disk_get_startblock(
+ xfs_bmbt_rec_t *r);
+
+xfs_fileoff_t
+xfs_bmbt_disk_get_startoff(
+ xfs_bmbt_rec_t *r);
+
+#else
+#define xfs_bmbt_disk_get_all(r, s) \
+ xfs_bmbt_get_all(r, s)
+#define xfs_bmbt_disk_get_state(r) \
+ xfs_bmbt_get_state(r)
+#define xfs_bmbt_disk_get_blockcount(r) \
+ xfs_bmbt_get_blockcount(r)
+#define xfs_bmbt_disk_get_startblock(r) \
+ xfs_bmbt_get_blockcount(r)
+#define xfs_bmbt_disk_get_startoff(r) \
+ xfs_bmbt_get_startoff(r)
+#endif
+
+int
+xfs_bmbt_increment(
+ struct xfs_btree_cur *,
+ int,
+ int *);
+
+int
+xfs_bmbt_insert(
+ struct xfs_btree_cur *,
+ int *);
+
+void
+xfs_bmbt_log_block(
+ struct xfs_btree_cur *,
+ struct xfs_buf *,
+ int);
+
+void
+xfs_bmbt_log_recs(
+ struct xfs_btree_cur *,
+ struct xfs_buf *,
+ int,
+ int);
+
+int
+xfs_bmbt_lookup_eq(
+ struct xfs_btree_cur *,
+ xfs_fileoff_t,
+ xfs_fsblock_t,
+ xfs_filblks_t,
+ int *);
+
+int
+xfs_bmbt_lookup_ge(
+ struct xfs_btree_cur *,
+ xfs_fileoff_t,
+ xfs_fsblock_t,
+ xfs_filblks_t,
+ int *);
+
+int
+xfs_bmbt_lookup_le(
+ struct xfs_btree_cur *,
+ xfs_fileoff_t,
+ xfs_fsblock_t,
+ xfs_filblks_t,
+ int *);
+
+/*
+ * Give the bmap btree a new root block. Copy the old broot contents
+ * down into a real block and make the broot point to it.
+ */
+int /* error */
+xfs_bmbt_newroot(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ int *logflags, /* logging flags for inode */
+ int *stat); /* return status - 0 fail */
+
+void
+xfs_bmbt_set_all(
+ xfs_bmbt_rec_t *r,
+ xfs_bmbt_irec_t *s);
+
+void
+xfs_bmbt_set_allf(
+ xfs_bmbt_rec_t *r,
+ xfs_fileoff_t o,
+ xfs_fsblock_t b,
+ xfs_filblks_t c,
+ xfs_exntst_t v);
+
+void
+xfs_bmbt_set_blockcount(
+ xfs_bmbt_rec_t *r,
+ xfs_filblks_t v);
+
+void
+xfs_bmbt_set_startblock(
+ xfs_bmbt_rec_t *r,
+ xfs_fsblock_t v);
+
+void
+xfs_bmbt_set_startoff(
+ xfs_bmbt_rec_t *r,
+ xfs_fileoff_t v);
+
+void
+xfs_bmbt_set_state(
+ xfs_bmbt_rec_t *r,
+ xfs_exntst_t v);
+
+#if ARCH_CONVERT != ARCH_NOCONVERT
+void
+xfs_bmbt_disk_set_all(
+ xfs_bmbt_rec_t *r,
+ xfs_bmbt_irec_t *s);
+
+void
+xfs_bmbt_disk_set_allf(
+ xfs_bmbt_rec_t *r,
+ xfs_fileoff_t o,
+ xfs_fsblock_t b,
+ xfs_filblks_t c,
+ xfs_exntst_t v);
+#else
+#define xfs_bmbt_disk_set_all(r, s) \
+ xfs_bmbt_set_all(r, s)
+#define xfs_bmbt_disk_set_allf(r, o, b, c, v) \
+ xfs_bmbt_set_allf(r, o, b, c, v)
+#endif
+
+void
+xfs_bmbt_to_bmdr(
+ xfs_bmbt_block_t *,
+ int,
+ xfs_bmdr_block_t *,
+ int);
+
+int
+xfs_bmbt_update(
+ struct xfs_btree_cur *,
+ xfs_fileoff_t,
+ xfs_fsblock_t,
+ xfs_filblks_t,
+ xfs_exntst_t);
+
+#ifdef DEBUG
+/*
+ * Get the data from the pointed-to record.
+ */
+int
+xfs_bmbt_get_rec(
+ struct xfs_btree_cur *,
+ xfs_fileoff_t *,
+ xfs_fsblock_t *,
+ xfs_filblks_t *,
+ xfs_exntst_t *,
+ int *);
+#endif
+
+
+/*
+ * Search an extent list for the extent which includes block
+ * bno.
+ */
+xfs_bmbt_rec_t *
+xfs_bmap_do_search_extents(
+ xfs_bmbt_rec_t *,
+ xfs_extnum_t,
+ xfs_extnum_t,
+ xfs_fileoff_t,
+ int *,
+ xfs_extnum_t *,
+ xfs_bmbt_irec_t *,
+ xfs_bmbt_irec_t *);
+
+#endif /* __KERNEL__ */
+
+#endif /* __XFS_BMAP_BTREE_H__ */
diff --git a/fs/xfs/xfs_btree.c b/fs/xfs/xfs_btree.c
new file mode 100644
index 00000000000000..099e26aae2a1f3
--- /dev/null
+++ b/fs/xfs/xfs_btree.c
@@ -0,0 +1,949 @@
+/*
+ * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+/*
+ * This file contains common code for the space manager's btree implementations.
+ */
+
+#include "xfs.h"
+
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode.h"
+#include "xfs_bit.h"
+#include "xfs_error.h"
+
+/*
+ * Cursor allocation zone.
+ */
+kmem_zone_t *xfs_btree_cur_zone;
+
+/*
+ * Btree magic numbers.
+ */
+const __uint32_t xfs_magics[XFS_BTNUM_MAX] =
+{
+ XFS_ABTB_MAGIC, XFS_ABTC_MAGIC, XFS_BMAP_MAGIC, XFS_IBT_MAGIC
+};
+
+/*
+ * Prototypes for internal routines.
+ */
+
+/*
+ * Checking routine: return maxrecs for the block.
+ */
+STATIC int /* number of records fitting in block */
+xfs_btree_maxrecs(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ xfs_btree_block_t *block);/* generic btree block pointer */
+
+/*
+ * Internal routines.
+ */
+
+/*
+ * Checking routine: return maxrecs for the block.
+ */
+STATIC int /* number of records fitting in block */
+xfs_btree_maxrecs(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ xfs_btree_block_t *block) /* generic btree block pointer */
+{
+ switch (cur->bc_btnum) {
+ case XFS_BTNUM_BNO:
+ case XFS_BTNUM_CNT:
+ return (int)XFS_ALLOC_BLOCK_MAXRECS(INT_GET(block->bb_h.bb_level, ARCH_CONVERT), cur);
+ case XFS_BTNUM_BMAP:
+ return (int)XFS_BMAP_BLOCK_IMAXRECS(INT_GET(block->bb_h.bb_level, ARCH_CONVERT), cur);
+ case XFS_BTNUM_INO:
+ return (int)XFS_INOBT_BLOCK_MAXRECS(INT_GET(block->bb_h.bb_level, ARCH_CONVERT), cur);
+ default:
+ ASSERT(0);
+ return 0;
+ }
+}
+
+/*
+ * External routines.
+ */
+
+#ifdef DEBUG
+/*
+ * Debug routine: check that block header is ok.
+ */
+void
+xfs_btree_check_block(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ xfs_btree_block_t *block, /* generic btree block pointer */
+ int level, /* level of the btree block */
+ xfs_buf_t *bp) /* buffer containing block, if any */
+{
+ if (XFS_BTREE_LONG_PTRS(cur->bc_btnum))
+ xfs_btree_check_lblock(cur, (xfs_btree_lblock_t *)block, level,
+ bp);
+ else
+ xfs_btree_check_sblock(cur, (xfs_btree_sblock_t *)block, level,
+ bp);
+}
+
+/*
+ * Debug routine: check that keys are in the right order.
+ */
+void
+xfs_btree_check_key(
+ xfs_btnum_t btnum, /* btree identifier */
+ void *ak1, /* pointer to left (lower) key */
+ void *ak2) /* pointer to right (higher) key */
+{
+ switch (btnum) {
+ case XFS_BTNUM_BNO: {
+ xfs_alloc_key_t *k1;
+ xfs_alloc_key_t *k2;
+
+ k1 = ak1;
+ k2 = ak2;
+ ASSERT(INT_GET(k1->ar_startblock, ARCH_CONVERT) < INT_GET(k2->ar_startblock, ARCH_CONVERT));
+ break;
+ }
+ case XFS_BTNUM_CNT: {
+ xfs_alloc_key_t *k1;
+ xfs_alloc_key_t *k2;
+
+ k1 = ak1;
+ k2 = ak2;
+ ASSERT(INT_GET(k1->ar_blockcount, ARCH_CONVERT) < INT_GET(k2->ar_blockcount, ARCH_CONVERT) ||
+ (INT_GET(k1->ar_blockcount, ARCH_CONVERT) == INT_GET(k2->ar_blockcount, ARCH_CONVERT) &&
+ INT_GET(k1->ar_startblock, ARCH_CONVERT) < INT_GET(k2->ar_startblock, ARCH_CONVERT)));
+ break;
+ }
+ case XFS_BTNUM_BMAP: {
+ xfs_bmbt_key_t *k1;
+ xfs_bmbt_key_t *k2;
+
+ k1 = ak1;
+ k2 = ak2;
+ ASSERT(INT_GET(k1->br_startoff, ARCH_CONVERT) < INT_GET(k2->br_startoff, ARCH_CONVERT));
+ break;
+ }
+ case XFS_BTNUM_INO: {
+ xfs_inobt_key_t *k1;
+ xfs_inobt_key_t *k2;
+
+ k1 = ak1;
+ k2 = ak2;
+ ASSERT(INT_GET(k1->ir_startino, ARCH_CONVERT) < INT_GET(k2->ir_startino, ARCH_CONVERT));
+ break;
+ }
+ default:
+ ASSERT(0);
+ }
+}
+#endif /* DEBUG */
+
+/*
+ * Checking routine: check that long form block header is ok.
+ */
+/* ARGSUSED */
+int /* error (0 or EFSCORRUPTED) */
+xfs_btree_check_lblock(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ xfs_btree_lblock_t *block, /* btree long form block pointer */
+ int level, /* level of the btree block */
+ xfs_buf_t *bp) /* buffer for block, if any */
+{
+ int lblock_ok; /* block passes checks */
+ xfs_mount_t *mp; /* file system mount point */
+
+ mp = cur->bc_mp;
+ lblock_ok =
+ INT_GET(block->bb_magic, ARCH_CONVERT) == xfs_magics[cur->bc_btnum] &&
+ INT_GET(block->bb_level, ARCH_CONVERT) == level &&
+ INT_GET(block->bb_numrecs, ARCH_CONVERT) <=
+ xfs_btree_maxrecs(cur, (xfs_btree_block_t *)block) &&
+ !INT_ISZERO(block->bb_leftsib, ARCH_CONVERT) &&
+ (INT_GET(block->bb_leftsib, ARCH_CONVERT) == NULLDFSBNO ||
+ XFS_FSB_SANITY_CHECK(mp, INT_GET(block->bb_leftsib, ARCH_CONVERT))) &&
+ !INT_ISZERO(block->bb_rightsib, ARCH_CONVERT) &&
+ (INT_GET(block->bb_rightsib, ARCH_CONVERT) == NULLDFSBNO ||
+ XFS_FSB_SANITY_CHECK(mp, INT_GET(block->bb_rightsib, ARCH_CONVERT)));
+ if (unlikely(XFS_TEST_ERROR(!lblock_ok, mp, XFS_ERRTAG_BTREE_CHECK_LBLOCK,
+ XFS_RANDOM_BTREE_CHECK_LBLOCK))) {
+ if (bp)
+ xfs_buftrace("LBTREE ERROR", bp);
+ XFS_ERROR_REPORT("xfs_btree_check_lblock", XFS_ERRLEVEL_LOW,
+ mp);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ return 0;
+}
+
+/*
+ * Checking routine: check that (long) pointer is ok.
+ */
+int /* error (0 or EFSCORRUPTED) */
+xfs_btree_check_lptr(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ xfs_dfsbno_t ptr, /* btree block disk address */
+ int level) /* btree block level */
+{
+ xfs_mount_t *mp; /* file system mount point */
+
+ mp = cur->bc_mp;
+ XFS_WANT_CORRUPTED_RETURN(
+ level > 0 &&
+ ptr != NULLDFSBNO &&
+ XFS_FSB_SANITY_CHECK(mp, ptr));
+ return 0;
+}
+
+#ifdef DEBUG
+/*
+ * Debug routine: check that records are in the right order.
+ */
+void
+xfs_btree_check_rec(
+ xfs_btnum_t btnum, /* btree identifier */
+ void *ar1, /* pointer to left (lower) record */
+ void *ar2) /* pointer to right (higher) record */
+{
+ switch (btnum) {
+ case XFS_BTNUM_BNO: {
+ xfs_alloc_rec_t *r1;
+ xfs_alloc_rec_t *r2;
+
+ r1 = ar1;
+ r2 = ar2;
+ ASSERT(INT_GET(r1->ar_startblock, ARCH_CONVERT) + INT_GET(r1->ar_blockcount, ARCH_CONVERT) <=
+ INT_GET(r2->ar_startblock, ARCH_CONVERT));
+ break;
+ }
+ case XFS_BTNUM_CNT: {
+ xfs_alloc_rec_t *r1;
+ xfs_alloc_rec_t *r2;
+
+ r1 = ar1;
+ r2 = ar2;
+ ASSERT(INT_GET(r1->ar_blockcount, ARCH_CONVERT) < INT_GET(r2->ar_blockcount, ARCH_CONVERT) ||
+ (INT_GET(r1->ar_blockcount, ARCH_CONVERT) == INT_GET(r2->ar_blockcount, ARCH_CONVERT) &&
+ INT_GET(r1->ar_startblock, ARCH_CONVERT) < INT_GET(r2->ar_startblock, ARCH_CONVERT)));
+ break;
+ }
+ case XFS_BTNUM_BMAP: {
+ xfs_bmbt_rec_t *r1;
+ xfs_bmbt_rec_t *r2;
+
+ r1 = ar1;
+ r2 = ar2;
+ ASSERT(xfs_bmbt_disk_get_startoff(r1) +
+ xfs_bmbt_disk_get_blockcount(r1) <=
+ xfs_bmbt_disk_get_startoff(r2));
+ break;
+ }
+ case XFS_BTNUM_INO: {
+ xfs_inobt_rec_t *r1;
+ xfs_inobt_rec_t *r2;
+
+ r1 = ar1;
+ r2 = ar2;
+ ASSERT(INT_GET(r1->ir_startino, ARCH_CONVERT) + XFS_INODES_PER_CHUNK <=
+ INT_GET(r2->ir_startino, ARCH_CONVERT));
+ break;
+ }
+ default:
+ ASSERT(0);
+ }
+}
+#endif /* DEBUG */
+
+/*
+ * Checking routine: check that block header is ok.
+ */
+/* ARGSUSED */
+int /* error (0 or EFSCORRUPTED) */
+xfs_btree_check_sblock(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ xfs_btree_sblock_t *block, /* btree short form block pointer */
+ int level, /* level of the btree block */
+ xfs_buf_t *bp) /* buffer containing block */
+{
+ xfs_buf_t *agbp; /* buffer for ag. freespace struct */
+ xfs_agf_t *agf; /* ag. freespace structure */
+ xfs_agblock_t agflen; /* native ag. freespace length */
+ int sblock_ok; /* block passes checks */
+
+ agbp = cur->bc_private.a.agbp;
+ agf = XFS_BUF_TO_AGF(agbp);
+ agflen = INT_GET(agf->agf_length, ARCH_CONVERT);
+ sblock_ok =
+ INT_GET(block->bb_magic, ARCH_CONVERT) == xfs_magics[cur->bc_btnum] &&
+ INT_GET(block->bb_level, ARCH_CONVERT) == level &&
+ INT_GET(block->bb_numrecs, ARCH_CONVERT) <=
+ xfs_btree_maxrecs(cur, (xfs_btree_block_t *)block) &&
+ (INT_GET(block->bb_leftsib, ARCH_CONVERT) == NULLAGBLOCK ||
+ INT_GET(block->bb_leftsib, ARCH_CONVERT) < agflen) &&
+ !INT_ISZERO(block->bb_leftsib, ARCH_CONVERT) &&
+ (INT_GET(block->bb_rightsib, ARCH_CONVERT) == NULLAGBLOCK ||
+ INT_GET(block->bb_rightsib, ARCH_CONVERT) < agflen) &&
+ !INT_ISZERO(block->bb_rightsib, ARCH_CONVERT);
+ if (unlikely(XFS_TEST_ERROR(!sblock_ok, cur->bc_mp,
+ XFS_ERRTAG_BTREE_CHECK_SBLOCK,
+ XFS_RANDOM_BTREE_CHECK_SBLOCK))) {
+ if (bp)
+ xfs_buftrace("SBTREE ERROR", bp);
+ XFS_ERROR_REPORT("xfs_btree_check_sblock", XFS_ERRLEVEL_LOW,
+ cur->bc_mp);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ return 0;
+}
+
+/*
+ * Checking routine: check that (short) pointer is ok.
+ */
+int /* error (0 or EFSCORRUPTED) */
+xfs_btree_check_sptr(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ xfs_agblock_t ptr, /* btree block disk address */
+ int level) /* btree block level */
+{
+ xfs_buf_t *agbp; /* buffer for ag. freespace struct */
+ xfs_agf_t *agf; /* ag. freespace structure */
+
+ agbp = cur->bc_private.a.agbp;
+ agf = XFS_BUF_TO_AGF(agbp);
+ XFS_WANT_CORRUPTED_RETURN(
+ level > 0 &&
+ ptr != NULLAGBLOCK && ptr != 0 &&
+ ptr < INT_GET(agf->agf_length, ARCH_CONVERT));
+ return 0;
+}
+
+/*
+ * Delete the btree cursor.
+ */
+void
+xfs_btree_del_cursor(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ int error) /* del because of error */
+{
+ int i; /* btree level */
+
+ /*
+ * Clear the buffer pointers, and release the buffers.
+ * If we're doing this in the face of an error, we
+ * need to make sure to inspect all of the entries
+ * in the bc_bufs array for buffers to be unlocked.
+ * This is because some of the btree code works from
+ * level n down to 0, and if we get an error along
+ * the way we won't have initialized all the entries
+ * down to 0.
+ */
+ for (i = 0; i < cur->bc_nlevels; i++) {
+ if (cur->bc_bufs[i])
+ xfs_btree_setbuf(cur, i, NULL);
+ else if (!error)
+ break;
+ }
+ /*
+ * Can't free a bmap cursor without having dealt with the
+ * allocated indirect blocks' accounting.
+ */
+ ASSERT(cur->bc_btnum != XFS_BTNUM_BMAP ||
+ cur->bc_private.b.allocated == 0);
+ /*
+ * Free the cursor.
+ */
+ kmem_zone_free(xfs_btree_cur_zone, cur);
+}
+
+/*
+ * Duplicate the btree cursor.
+ * Allocate a new one, copy the record, re-get the buffers.
+ */
+int /* error */
+xfs_btree_dup_cursor(
+ xfs_btree_cur_t *cur, /* input cursor */
+ xfs_btree_cur_t **ncur) /* output cursor */
+{
+ xfs_buf_t *bp; /* btree block's buffer pointer */
+ int error; /* error return value */
+ int i; /* level number of btree block */
+ xfs_mount_t *mp; /* mount structure for filesystem */
+ xfs_btree_cur_t *new; /* new cursor value */
+ xfs_trans_t *tp; /* transaction pointer, can be NULL */
+
+ tp = cur->bc_tp;
+ mp = cur->bc_mp;
+ /*
+ * Allocate a new cursor like the old one.
+ */
+ new = xfs_btree_init_cursor(mp, tp, cur->bc_private.a.agbp,
+ cur->bc_private.a.agno, cur->bc_btnum, cur->bc_private.b.ip,
+ cur->bc_private.b.whichfork);
+ /*
+ * Copy the record currently in the cursor.
+ */
+ new->bc_rec = cur->bc_rec;
+ /*
+ * For each level current, re-get the buffer and copy the ptr value.
+ */
+ for (i = 0; i < new->bc_nlevels; i++) {
+ new->bc_ptrs[i] = cur->bc_ptrs[i];
+ new->bc_ra[i] = cur->bc_ra[i];
+ if ((bp = cur->bc_bufs[i])) {
+ if ((error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp,
+ XFS_BUF_ADDR(bp), mp->m_bsize, 0, &bp))) {
+ xfs_btree_del_cursor(new, error);
+ *ncur = NULL;
+ return error;
+ }
+ new->bc_bufs[i] = bp;
+ ASSERT(bp);
+ ASSERT(!XFS_BUF_GETERROR(bp));
+ } else
+ new->bc_bufs[i] = NULL;
+ }
+ /*
+ * For bmap btrees, copy the firstblock, flist, and flags values,
+ * since init cursor doesn't get them.
+ */
+ if (new->bc_btnum == XFS_BTNUM_BMAP) {
+ new->bc_private.b.firstblock = cur->bc_private.b.firstblock;
+ new->bc_private.b.flist = cur->bc_private.b.flist;
+ new->bc_private.b.flags = cur->bc_private.b.flags;
+ }
+ *ncur = new;
+ return 0;
+}
+
+/*
+ * Change the cursor to point to the first record at the given level.
+ * Other levels are unaffected.
+ */
+int /* success=1, failure=0 */
+xfs_btree_firstrec(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ int level) /* level to change */
+{
+ xfs_btree_block_t *block; /* generic btree block pointer */
+ xfs_buf_t *bp; /* buffer containing block */
+
+ /*
+ * Get the block pointer for this level.
+ */
+ block = xfs_btree_get_block(cur, level, &bp);
+ xfs_btree_check_block(cur, block, level, bp);
+ /*
+ * It's empty, there is no such record.
+ */
+ if (INT_ISZERO(block->bb_h.bb_numrecs, ARCH_CONVERT))
+ return 0;
+ /*
+ * Set the ptr value to 1, that's the first record/key.
+ */
+ cur->bc_ptrs[level] = 1;
+ return 1;
+}
+
+/*
+ * Retrieve the block pointer from the cursor at the given level.
+ * This may be a bmap btree root or from a buffer.
+ */
+xfs_btree_block_t * /* generic btree block pointer */
+xfs_btree_get_block(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ int level, /* level in btree */
+ xfs_buf_t **bpp) /* buffer containing the block */
+{
+ xfs_btree_block_t *block; /* return value */
+ xfs_buf_t *bp; /* return buffer */
+ xfs_ifork_t *ifp; /* inode fork pointer */
+ int whichfork; /* data or attr fork */
+
+ if (cur->bc_btnum == XFS_BTNUM_BMAP && level == cur->bc_nlevels - 1) {
+ whichfork = cur->bc_private.b.whichfork;
+ ifp = XFS_IFORK_PTR(cur->bc_private.b.ip, whichfork);
+ block = (xfs_btree_block_t *)ifp->if_broot;
+ bp = NULL;
+ } else {
+ bp = cur->bc_bufs[level];
+ block = XFS_BUF_TO_BLOCK(bp);
+ }
+ ASSERT(block != NULL);
+ *bpp = bp;
+ return block;
+}
+
+/*
+ * Get a buffer for the block, return it with no data read.
+ * Long-form addressing.
+ */
+xfs_buf_t * /* buffer for fsbno */
+xfs_btree_get_bufl(
+ xfs_mount_t *mp, /* file system mount point */
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_fsblock_t fsbno, /* file system block number */
+ uint lock) /* lock flags for get_buf */
+{
+ xfs_buf_t *bp; /* buffer pointer (return value) */
+ xfs_daddr_t d; /* real disk block address */
+
+ ASSERT(fsbno != NULLFSBLOCK);
+ d = XFS_FSB_TO_DADDR(mp, fsbno);
+ bp = xfs_trans_get_buf(tp, mp->m_ddev_targp, d, mp->m_bsize, lock);
+ ASSERT(bp);
+ ASSERT(!XFS_BUF_GETERROR(bp));
+ return bp;
+}
+
+/*
+ * Get a buffer for the block, return it with no data read.
+ * Short-form addressing.
+ */
+xfs_buf_t * /* buffer for agno/agbno */
+xfs_btree_get_bufs(
+ xfs_mount_t *mp, /* file system mount point */
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_agnumber_t agno, /* allocation group number */
+ xfs_agblock_t agbno, /* allocation group block number */
+ uint lock) /* lock flags for get_buf */
+{
+ xfs_buf_t *bp; /* buffer pointer (return value) */
+ xfs_daddr_t d; /* real disk block address */
+
+ ASSERT(agno != NULLAGNUMBER);
+ ASSERT(agbno != NULLAGBLOCK);
+ d = XFS_AGB_TO_DADDR(mp, agno, agbno);
+ bp = xfs_trans_get_buf(tp, mp->m_ddev_targp, d, mp->m_bsize, lock);
+ ASSERT(bp);
+ ASSERT(!XFS_BUF_GETERROR(bp));
+ return bp;
+}
+
+/*
+ * Allocate a new btree cursor.
+ * The cursor is either for allocation (A) or bmap (B) or inodes (I).
+ */
+xfs_btree_cur_t * /* new btree cursor */
+xfs_btree_init_cursor(
+ xfs_mount_t *mp, /* file system mount point */
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_buf_t *agbp, /* (A only) buffer for agf structure */
+ /* (I only) buffer for agi structure */
+ xfs_agnumber_t agno, /* (AI only) allocation group number */
+ xfs_btnum_t btnum, /* btree identifier */
+ xfs_inode_t *ip, /* (B only) inode owning the btree */
+ int whichfork) /* (B only) data or attr fork */
+{
+ xfs_agf_t *agf; /* (A) allocation group freespace */
+ xfs_agi_t *agi; /* (I) allocation group inodespace */
+ xfs_btree_cur_t *cur; /* return value */
+ xfs_ifork_t *ifp; /* (I) inode fork pointer */
+ int nlevels=0; /* number of levels in the btree */
+
+ ASSERT(xfs_btree_cur_zone != NULL);
+ /*
+ * Allocate a new cursor.
+ */
+ cur = kmem_zone_zalloc(xfs_btree_cur_zone, KM_SLEEP);
+ /*
+ * Deduce the number of btree levels from the arguments.
+ */
+ switch (btnum) {
+ case XFS_BTNUM_BNO:
+ case XFS_BTNUM_CNT:
+ agf = XFS_BUF_TO_AGF(agbp);
+ nlevels = INT_GET(agf->agf_levels[btnum], ARCH_CONVERT);
+ break;
+ case XFS_BTNUM_BMAP:
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+ nlevels = INT_GET(ifp->if_broot->bb_level, ARCH_CONVERT) + 1;
+ break;
+ case XFS_BTNUM_INO:
+ agi = XFS_BUF_TO_AGI(agbp);
+ nlevels = INT_GET(agi->agi_level, ARCH_CONVERT);
+ break;
+ default:
+ ASSERT(0);
+ }
+ /*
+ * Fill in the common fields.
+ */
+ cur->bc_tp = tp;
+ cur->bc_mp = mp;
+ cur->bc_nlevels = nlevels;
+ cur->bc_btnum = btnum;
+ cur->bc_blocklog = mp->m_sb.sb_blocklog;
+ /*
+ * Fill in private fields.
+ */
+ switch (btnum) {
+ case XFS_BTNUM_BNO:
+ case XFS_BTNUM_CNT:
+ /*
+ * Allocation btree fields.
+ */
+ cur->bc_private.a.agbp = agbp;
+ cur->bc_private.a.agno = agno;
+ break;
+ case XFS_BTNUM_BMAP:
+ /*
+ * Bmap btree fields.
+ */
+ cur->bc_private.b.forksize = XFS_IFORK_SIZE(ip, whichfork);
+ cur->bc_private.b.ip = ip;
+ cur->bc_private.b.firstblock = NULLFSBLOCK;
+ cur->bc_private.b.flist = NULL;
+ cur->bc_private.b.allocated = 0;
+ cur->bc_private.b.flags = 0;
+ cur->bc_private.b.whichfork = whichfork;
+ break;
+ case XFS_BTNUM_INO:
+ /*
+ * Inode allocation btree fields.
+ */
+ cur->bc_private.i.agbp = agbp;
+ cur->bc_private.i.agno = agno;
+ break;
+ default:
+ ASSERT(0);
+ }
+ return cur;
+}
+
+/*
+ * Check for the cursor referring to the last block at the given level.
+ */
+int /* 1=is last block, 0=not last block */
+xfs_btree_islastblock(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ int level) /* level to check */
+{
+ xfs_btree_block_t *block; /* generic btree block pointer */
+ xfs_buf_t *bp; /* buffer containing block */
+
+ block = xfs_btree_get_block(cur, level, &bp);
+ xfs_btree_check_block(cur, block, level, bp);
+ if (XFS_BTREE_LONG_PTRS(cur->bc_btnum))
+ return INT_GET(block->bb_u.l.bb_rightsib, ARCH_CONVERT) == NULLDFSBNO;
+ else
+ return INT_GET(block->bb_u.s.bb_rightsib, ARCH_CONVERT) == NULLAGBLOCK;
+}
+
+/*
+ * Change the cursor to point to the last record in the current block
+ * at the given level. Other levels are unaffected.
+ */
+int /* success=1, failure=0 */
+xfs_btree_lastrec(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ int level) /* level to change */
+{
+ xfs_btree_block_t *block; /* generic btree block pointer */
+ xfs_buf_t *bp; /* buffer containing block */
+
+ /*
+ * Get the block pointer for this level.
+ */
+ block = xfs_btree_get_block(cur, level, &bp);
+ xfs_btree_check_block(cur, block, level, bp);
+ /*
+ * It's empty, there is no such record.
+ */
+ if (INT_ISZERO(block->bb_h.bb_numrecs, ARCH_CONVERT))
+ return 0;
+ /*
+ * Set the ptr value to numrecs, that's the last record/key.
+ */
+ cur->bc_ptrs[level] = INT_GET(block->bb_h.bb_numrecs, ARCH_CONVERT);
+ return 1;
+}
+
+/*
+ * Compute first and last byte offsets for the fields given.
+ * Interprets the offsets table, which contains struct field offsets.
+ */
+void
+xfs_btree_offsets(
+ __int64_t fields, /* bitmask of fields */
+ const short *offsets, /* table of field offsets */
+ int nbits, /* number of bits to inspect */
+ int *first, /* output: first byte offset */
+ int *last) /* output: last byte offset */
+{
+ int i; /* current bit number */
+ __int64_t imask; /* mask for current bit number */
+
+ ASSERT(fields != 0);
+ /*
+ * Find the lowest bit, so the first byte offset.
+ */
+ for (i = 0, imask = 1LL; ; i++, imask <<= 1) {
+ if (imask & fields) {
+ *first = offsets[i];
+ break;
+ }
+ }
+ /*
+ * Find the highest bit, so the last byte offset.
+ */
+ for (i = nbits - 1, imask = 1LL << i; ; i--, imask >>= 1) {
+ if (imask & fields) {
+ *last = offsets[i + 1] - 1;
+ break;
+ }
+ }
+}
+
+/*
+ * Get a buffer for the block, return it read in.
+ * Long-form addressing.
+ */
+int /* error */
+xfs_btree_read_bufl(
+ xfs_mount_t *mp, /* file system mount point */
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_fsblock_t fsbno, /* file system block number */
+ uint lock, /* lock flags for read_buf */
+ xfs_buf_t **bpp, /* buffer for fsbno */
+ int refval) /* ref count value for buffer */
+{
+ xfs_buf_t *bp; /* return value */
+ xfs_daddr_t d; /* real disk block address */
+ int error;
+
+ ASSERT(fsbno != NULLFSBLOCK);
+ d = XFS_FSB_TO_DADDR(mp, fsbno);
+ if ((error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp, d,
+ mp->m_bsize, lock, &bp))) {
+ return error;
+ }
+ ASSERT(!bp || !XFS_BUF_GETERROR(bp));
+ if (bp != NULL) {
+ XFS_BUF_SET_VTYPE_REF(bp, B_FS_MAP, refval);
+ }
+ *bpp = bp;
+ return 0;
+}
+
+/*
+ * Get a buffer for the block, return it read in.
+ * Short-form addressing.
+ */
+int /* error */
+xfs_btree_read_bufs(
+ xfs_mount_t *mp, /* file system mount point */
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_agnumber_t agno, /* allocation group number */
+ xfs_agblock_t agbno, /* allocation group block number */
+ uint lock, /* lock flags for read_buf */
+ xfs_buf_t **bpp, /* buffer for agno/agbno */
+ int refval) /* ref count value for buffer */
+{
+ xfs_buf_t *bp; /* return value */
+ xfs_daddr_t d; /* real disk block address */
+ int error;
+
+ ASSERT(agno != NULLAGNUMBER);
+ ASSERT(agbno != NULLAGBLOCK);
+ d = XFS_AGB_TO_DADDR(mp, agno, agbno);
+ if ((error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp, d,
+ mp->m_bsize, lock, &bp))) {
+ return error;
+ }
+ ASSERT(!bp || !XFS_BUF_GETERROR(bp));
+ if (bp != NULL) {
+ switch (refval) {
+ case XFS_ALLOC_BTREE_REF:
+ XFS_BUF_SET_VTYPE_REF(bp, B_FS_MAP, refval);
+ break;
+ case XFS_INO_BTREE_REF:
+ XFS_BUF_SET_VTYPE_REF(bp, B_FS_INOMAP, refval);
+ break;
+ }
+ }
+ *bpp = bp;
+ return 0;
+}
+
+/*
+ * Read-ahead the block, don't wait for it, don't return a buffer.
+ * Long-form addressing.
+ */
+/* ARGSUSED */
+void
+xfs_btree_reada_bufl(
+ xfs_mount_t *mp, /* file system mount point */
+ xfs_fsblock_t fsbno, /* file system block number */
+ xfs_extlen_t count) /* count of filesystem blocks */
+{
+ xfs_daddr_t d;
+
+ ASSERT(fsbno != NULLFSBLOCK);
+ d = XFS_FSB_TO_DADDR(mp, fsbno);
+ xfs_baread(mp->m_ddev_targp, d, mp->m_bsize * count);
+}
+
+/*
+ * Read-ahead the block, don't wait for it, don't return a buffer.
+ * Short-form addressing.
+ */
+/* ARGSUSED */
+void
+xfs_btree_reada_bufs(
+ xfs_mount_t *mp, /* file system mount point */
+ xfs_agnumber_t agno, /* allocation group number */
+ xfs_agblock_t agbno, /* allocation group block number */
+ xfs_extlen_t count) /* count of filesystem blocks */
+{
+ xfs_daddr_t d;
+
+ ASSERT(agno != NULLAGNUMBER);
+ ASSERT(agbno != NULLAGBLOCK);
+ d = XFS_AGB_TO_DADDR(mp, agno, agbno);
+ xfs_baread(mp->m_ddev_targp, d, mp->m_bsize * count);
+}
+
+/*
+ * Read-ahead btree blocks, at the given level.
+ * Bits in lr are set from XFS_BTCUR_{LEFT,RIGHT}RA.
+ */
+int
+xfs_btree_readahead_core(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ int lev, /* level in btree */
+ int lr) /* left/right bits */
+{
+ xfs_alloc_block_t *a;
+ xfs_bmbt_block_t *b;
+ xfs_inobt_block_t *i;
+ int rval = 0;
+
+ ASSERT(cur->bc_bufs[lev] != NULL);
+ cur->bc_ra[lev] |= lr;
+ switch (cur->bc_btnum) {
+ case XFS_BTNUM_BNO:
+ case XFS_BTNUM_CNT:
+ a = XFS_BUF_TO_ALLOC_BLOCK(cur->bc_bufs[lev]);
+ if ((lr & XFS_BTCUR_LEFTRA) && INT_GET(a->bb_leftsib, ARCH_CONVERT) != NULLAGBLOCK) {
+ xfs_btree_reada_bufs(cur->bc_mp, cur->bc_private.a.agno,
+ INT_GET(a->bb_leftsib, ARCH_CONVERT), 1);
+ rval++;
+ }
+ if ((lr & XFS_BTCUR_RIGHTRA) && INT_GET(a->bb_rightsib, ARCH_CONVERT) != NULLAGBLOCK) {
+ xfs_btree_reada_bufs(cur->bc_mp, cur->bc_private.a.agno,
+ INT_GET(a->bb_rightsib, ARCH_CONVERT), 1);
+ rval++;
+ }
+ break;
+ case XFS_BTNUM_BMAP:
+ b = XFS_BUF_TO_BMBT_BLOCK(cur->bc_bufs[lev]);
+ if ((lr & XFS_BTCUR_LEFTRA) && INT_GET(b->bb_leftsib, ARCH_CONVERT) != NULLDFSBNO) {
+ xfs_btree_reada_bufl(cur->bc_mp, INT_GET(b->bb_leftsib, ARCH_CONVERT), 1);
+ rval++;
+ }
+ if ((lr & XFS_BTCUR_RIGHTRA) && INT_GET(b->bb_rightsib, ARCH_CONVERT) != NULLDFSBNO) {
+ xfs_btree_reada_bufl(cur->bc_mp, INT_GET(b->bb_rightsib, ARCH_CONVERT), 1);
+ rval++;
+ }
+ break;
+ case XFS_BTNUM_INO:
+ i = XFS_BUF_TO_INOBT_BLOCK(cur->bc_bufs[lev]);
+ if ((lr & XFS_BTCUR_LEFTRA) && INT_GET(i->bb_leftsib, ARCH_CONVERT) != NULLAGBLOCK) {
+ xfs_btree_reada_bufs(cur->bc_mp, cur->bc_private.i.agno,
+ INT_GET(i->bb_leftsib, ARCH_CONVERT), 1);
+ rval++;
+ }
+ if ((lr & XFS_BTCUR_RIGHTRA) && INT_GET(i->bb_rightsib, ARCH_CONVERT) != NULLAGBLOCK) {
+ xfs_btree_reada_bufs(cur->bc_mp, cur->bc_private.i.agno,
+ INT_GET(i->bb_rightsib, ARCH_CONVERT), 1);
+ rval++;
+ }
+ break;
+ default:
+ ASSERT(0);
+ }
+ return rval;
+}
+
+/*
+ * Set the buffer for level "lev" in the cursor to bp, releasing
+ * any previous buffer.
+ */
+void
+xfs_btree_setbuf(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ int lev, /* level in btree */
+ xfs_buf_t *bp) /* new buffer to set */
+{
+ xfs_btree_block_t *b; /* btree block */
+ xfs_buf_t *obp; /* old buffer pointer */
+
+ obp = cur->bc_bufs[lev];
+ if (obp)
+ xfs_trans_brelse(cur->bc_tp, obp);
+ cur->bc_bufs[lev] = bp;
+ cur->bc_ra[lev] = 0;
+ if (!bp)
+ return;
+ b = XFS_BUF_TO_BLOCK(bp);
+ if (XFS_BTREE_LONG_PTRS(cur->bc_btnum)) {
+ if (INT_GET(b->bb_u.l.bb_leftsib, ARCH_CONVERT) == NULLDFSBNO)
+ cur->bc_ra[lev] |= XFS_BTCUR_LEFTRA;
+ if (INT_GET(b->bb_u.l.bb_rightsib, ARCH_CONVERT) == NULLDFSBNO)
+ cur->bc_ra[lev] |= XFS_BTCUR_RIGHTRA;
+ } else {
+ if (INT_GET(b->bb_u.s.bb_leftsib, ARCH_CONVERT) == NULLAGBLOCK)
+ cur->bc_ra[lev] |= XFS_BTCUR_LEFTRA;
+ if (INT_GET(b->bb_u.s.bb_rightsib, ARCH_CONVERT) == NULLAGBLOCK)
+ cur->bc_ra[lev] |= XFS_BTCUR_RIGHTRA;
+ }
+}
diff --git a/fs/xfs/xfs_btree.h b/fs/xfs/xfs_btree.h
new file mode 100644
index 00000000000000..93872bba41f50e
--- /dev/null
+++ b/fs/xfs/xfs_btree.h
@@ -0,0 +1,592 @@
+/*
+ * Copyright (c) 2000-2001 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_BTREE_H__
+#define __XFS_BTREE_H__
+
+struct xfs_buf;
+struct xfs_bmap_free;
+struct xfs_inode;
+struct xfs_mount;
+struct xfs_trans;
+
+/*
+ * This nonsense is to make -wlint happy.
+ */
+#define XFS_LOOKUP_EQ ((xfs_lookup_t)XFS_LOOKUP_EQi)
+#define XFS_LOOKUP_LE ((xfs_lookup_t)XFS_LOOKUP_LEi)
+#define XFS_LOOKUP_GE ((xfs_lookup_t)XFS_LOOKUP_GEi)
+
+#define XFS_BTNUM_BNO ((xfs_btnum_t)XFS_BTNUM_BNOi)
+#define XFS_BTNUM_CNT ((xfs_btnum_t)XFS_BTNUM_CNTi)
+#define XFS_BTNUM_BMAP ((xfs_btnum_t)XFS_BTNUM_BMAPi)
+#define XFS_BTNUM_INO ((xfs_btnum_t)XFS_BTNUM_INOi)
+
+/*
+ * Short form header: space allocation btrees.
+ */
+typedef struct xfs_btree_sblock
+{
+ __uint32_t bb_magic; /* magic number for block type */
+ __uint16_t bb_level; /* 0 is a leaf */
+ __uint16_t bb_numrecs; /* current # of data records */
+ xfs_agblock_t bb_leftsib; /* left sibling block or NULLAGBLOCK */
+ xfs_agblock_t bb_rightsib; /* right sibling block or NULLAGBLOCK */
+} xfs_btree_sblock_t;
+
+/*
+ * Long form header: bmap btrees.
+ */
+typedef struct xfs_btree_lblock
+{
+ __uint32_t bb_magic; /* magic number for block type */
+ __uint16_t bb_level; /* 0 is a leaf */
+ __uint16_t bb_numrecs; /* current # of data records */
+ xfs_dfsbno_t bb_leftsib; /* left sibling block or NULLDFSBNO */
+ xfs_dfsbno_t bb_rightsib; /* right sibling block or NULLDFSBNO */
+} xfs_btree_lblock_t;
+
+/*
+ * Combined header and structure, used by common code.
+ */
+typedef struct xfs_btree_hdr
+{
+ __uint32_t bb_magic; /* magic number for block type */
+ __uint16_t bb_level; /* 0 is a leaf */
+ __uint16_t bb_numrecs; /* current # of data records */
+} xfs_btree_hdr_t;
+
+typedef struct xfs_btree_block
+{
+ xfs_btree_hdr_t bb_h; /* header */
+ union {
+ struct {
+ xfs_agblock_t bb_leftsib;
+ xfs_agblock_t bb_rightsib;
+ } s; /* short form pointers */
+ struct {
+ xfs_dfsbno_t bb_leftsib;
+ xfs_dfsbno_t bb_rightsib;
+ } l; /* long form pointers */
+ } bb_u; /* rest */
+} xfs_btree_block_t;
+
+/*
+ * For logging record fields.
+ */
+#define XFS_BB_MAGIC 0x01
+#define XFS_BB_LEVEL 0x02
+#define XFS_BB_NUMRECS 0x04
+#define XFS_BB_LEFTSIB 0x08
+#define XFS_BB_RIGHTSIB 0x10
+#define XFS_BB_NUM_BITS 5
+#define XFS_BB_ALL_BITS ((1 << XFS_BB_NUM_BITS) - 1)
+
+/*
+ * Boolean to select which form of xfs_btree_block_t.bb_u to use.
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BTREE_LONG_PTRS)
+int xfs_btree_long_ptrs(xfs_btnum_t btnum);
+#define XFS_BTREE_LONG_PTRS(btnum) ((btnum) == XFS_BTNUM_BMAP)
+#else
+#define XFS_BTREE_LONG_PTRS(btnum) ((btnum) == XFS_BTNUM_BMAP)
+#endif
+
+/*
+ * Magic numbers for btree blocks.
+ */
+extern const __uint32_t xfs_magics[];
+
+/*
+ * Maximum and minimum records in a btree block.
+ * Given block size, type prefix, and leaf flag (0 or 1).
+ * The divisor below is equivalent to lf ? (e1) : (e2) but that produces
+ * compiler warnings.
+ */
+#define XFS_BTREE_BLOCK_MAXRECS(bsz,t,lf) \
+ ((int)(((bsz) - (uint)sizeof(t ## _block_t)) / \
+ (((lf) * (uint)sizeof(t ## _rec_t)) + \
+ ((1 - (lf)) * \
+ ((uint)sizeof(t ## _key_t) + (uint)sizeof(t ## _ptr_t))))))
+#define XFS_BTREE_BLOCK_MINRECS(bsz,t,lf) \
+ (XFS_BTREE_BLOCK_MAXRECS(bsz,t,lf) / 2)
+
+/*
+ * Record, key, and pointer address calculation macros.
+ * Given block size, type prefix, block pointer, and index of requested entry
+ * (first entry numbered 1).
+ */
+#define XFS_BTREE_REC_ADDR(bsz,t,bb,i,mxr) \
+ ((t ## _rec_t *)((char *)(bb) + sizeof(t ## _block_t) + \
+ ((i) - 1) * sizeof(t ## _rec_t)))
+#define XFS_BTREE_KEY_ADDR(bsz,t,bb,i,mxr) \
+ ((t ## _key_t *)((char *)(bb) + sizeof(t ## _block_t) + \
+ ((i) - 1) * sizeof(t ## _key_t)))
+#define XFS_BTREE_PTR_ADDR(bsz,t,bb,i,mxr) \
+ ((t ## _ptr_t *)((char *)(bb) + sizeof(t ## _block_t) + \
+ (mxr) * sizeof(t ## _key_t) + ((i) - 1) * sizeof(t ## _ptr_t)))
+
+#define XFS_BTREE_MAXLEVELS 8 /* max of all btrees */
+
+/*
+ * Btree cursor structure.
+ * This collects all information needed by the btree code in one place.
+ */
+typedef struct xfs_btree_cur
+{
+ struct xfs_trans *bc_tp; /* transaction we're in, if any */
+ struct xfs_mount *bc_mp; /* file system mount struct */
+ union {
+ xfs_alloc_rec_t a;
+ xfs_bmbt_irec_t b;
+ xfs_inobt_rec_t i;
+ } bc_rec; /* current insert/search record value */
+ struct xfs_buf *bc_bufs[XFS_BTREE_MAXLEVELS]; /* buf ptr per level */
+ int bc_ptrs[XFS_BTREE_MAXLEVELS]; /* key/record # */
+ __uint8_t bc_ra[XFS_BTREE_MAXLEVELS]; /* readahead bits */
+#define XFS_BTCUR_LEFTRA 1 /* left sibling has been read-ahead */
+#define XFS_BTCUR_RIGHTRA 2 /* right sibling has been read-ahead */
+ __uint8_t bc_nlevels; /* number of levels in the tree */
+ __uint8_t bc_blocklog; /* log2(blocksize) of btree blocks */
+ xfs_btnum_t bc_btnum; /* identifies which btree type */
+ union {
+ struct { /* needed for BNO, CNT */
+ struct xfs_buf *agbp; /* agf buffer pointer */
+ xfs_agnumber_t agno; /* ag number */
+ } a;
+ struct { /* needed for BMAP */
+ struct xfs_inode *ip; /* pointer to our inode */
+ struct xfs_bmap_free *flist; /* list to free after */
+ xfs_fsblock_t firstblock; /* 1st blk allocated */
+ int allocated; /* count of alloced */
+ short forksize; /* fork's inode space */
+ char whichfork; /* data or attr fork */
+ char flags; /* flags */
+#define XFS_BTCUR_BPRV_WASDEL 1 /* was delayed */
+ } b;
+ struct { /* needed for INO */
+ struct xfs_buf *agbp; /* agi buffer pointer */
+ xfs_agnumber_t agno; /* ag number */
+ } i;
+ } bc_private; /* per-btree type data */
+} xfs_btree_cur_t;
+
+#define XFS_BTREE_NOERROR 0
+#define XFS_BTREE_ERROR 1
+
+/*
+ * Convert from buffer to btree block header.
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BUF_TO_BLOCK)
+xfs_btree_block_t *xfs_buf_to_block(struct xfs_buf *bp);
+#define XFS_BUF_TO_BLOCK(bp) xfs_buf_to_block(bp)
+#else
+#define XFS_BUF_TO_BLOCK(bp) ((xfs_btree_block_t *)(XFS_BUF_PTR(bp)))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BUF_TO_LBLOCK)
+xfs_btree_lblock_t *xfs_buf_to_lblock(struct xfs_buf *bp);
+#define XFS_BUF_TO_LBLOCK(bp) xfs_buf_to_lblock(bp)
+#else
+#define XFS_BUF_TO_LBLOCK(bp) ((xfs_btree_lblock_t *)(XFS_BUF_PTR(bp)))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BUF_TO_SBLOCK)
+xfs_btree_sblock_t *xfs_buf_to_sblock(struct xfs_buf *bp);
+#define XFS_BUF_TO_SBLOCK(bp) xfs_buf_to_sblock(bp)
+#else
+#define XFS_BUF_TO_SBLOCK(bp) ((xfs_btree_sblock_t *)(XFS_BUF_PTR(bp)))
+#endif
+
+#ifdef __KERNEL__
+
+#ifdef DEBUG
+/*
+ * Debug routine: check that block header is ok.
+ */
+void
+xfs_btree_check_block(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ xfs_btree_block_t *block, /* generic btree block pointer */
+ int level, /* level of the btree block */
+ struct xfs_buf *bp); /* buffer containing block, if any */
+
+/*
+ * Debug routine: check that keys are in the right order.
+ */
+void
+xfs_btree_check_key(
+ xfs_btnum_t btnum, /* btree identifier */
+ void *ak1, /* pointer to left (lower) key */
+ void *ak2); /* pointer to right (higher) key */
+
+/*
+ * Debug routine: check that records are in the right order.
+ */
+void
+xfs_btree_check_rec(
+ xfs_btnum_t btnum, /* btree identifier */
+ void *ar1, /* pointer to left (lower) record */
+ void *ar2); /* pointer to right (higher) record */
+#else
+#define xfs_btree_check_block(a,b,c,d)
+#define xfs_btree_check_key(a,b,c)
+#define xfs_btree_check_rec(a,b,c)
+#endif /* DEBUG */
+
+/*
+ * Checking routine: check that long form block header is ok.
+ */
+int /* error (0 or EFSCORRUPTED) */
+xfs_btree_check_lblock(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ xfs_btree_lblock_t *block, /* btree long form block pointer */
+ int level, /* level of the btree block */
+ struct xfs_buf *bp); /* buffer containing block, if any */
+
+/*
+ * Checking routine: check that (long) pointer is ok.
+ */
+int /* error (0 or EFSCORRUPTED) */
+xfs_btree_check_lptr(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ xfs_dfsbno_t ptr, /* btree block disk address */
+ int level); /* btree block level */
+
+/*
+ * Checking routine: check that short form block header is ok.
+ */
+int /* error (0 or EFSCORRUPTED) */
+xfs_btree_check_sblock(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ xfs_btree_sblock_t *block, /* btree short form block pointer */
+ int level, /* level of the btree block */
+ struct xfs_buf *bp); /* buffer containing block */
+
+/*
+ * Checking routine: check that (short) pointer is ok.
+ */
+int /* error (0 or EFSCORRUPTED) */
+xfs_btree_check_sptr(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ xfs_agblock_t ptr, /* btree block disk address */
+ int level); /* btree block level */
+
+/*
+ * Delete the btree cursor.
+ */
+void
+xfs_btree_del_cursor(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ int error); /* del because of error */
+
+/*
+ * Duplicate the btree cursor.
+ * Allocate a new one, copy the record, re-get the buffers.
+ */
+int /* error */
+xfs_btree_dup_cursor(
+ xfs_btree_cur_t *cur, /* input cursor */
+ xfs_btree_cur_t **ncur);/* output cursor */
+
+/*
+ * Change the cursor to point to the first record in the current block
+ * at the given level. Other levels are unaffected.
+ */
+int /* success=1, failure=0 */
+xfs_btree_firstrec(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ int level); /* level to change */
+
+/*
+ * Retrieve the block pointer from the cursor at the given level.
+ * This may be a bmap btree root or from a buffer.
+ */
+xfs_btree_block_t * /* generic btree block pointer */
+xfs_btree_get_block(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ int level, /* level in btree */
+ struct xfs_buf **bpp); /* buffer containing the block */
+
+/*
+ * Get a buffer for the block, return it with no data read.
+ * Long-form addressing.
+ */
+struct xfs_buf * /* buffer for fsbno */
+xfs_btree_get_bufl(
+ struct xfs_mount *mp, /* file system mount point */
+ struct xfs_trans *tp, /* transaction pointer */
+ xfs_fsblock_t fsbno, /* file system block number */
+ uint lock); /* lock flags for get_buf */
+
+/*
+ * Get a buffer for the block, return it with no data read.
+ * Short-form addressing.
+ */
+struct xfs_buf * /* buffer for agno/agbno */
+xfs_btree_get_bufs(
+ struct xfs_mount *mp, /* file system mount point */
+ struct xfs_trans *tp, /* transaction pointer */
+ xfs_agnumber_t agno, /* allocation group number */
+ xfs_agblock_t agbno, /* allocation group block number */
+ uint lock); /* lock flags for get_buf */
+
+/*
+ * Allocate a new btree cursor.
+ * The cursor is either for allocation (A) or bmap (B).
+ */
+xfs_btree_cur_t * /* new btree cursor */
+xfs_btree_init_cursor(
+ struct xfs_mount *mp, /* file system mount point */
+ struct xfs_trans *tp, /* transaction pointer */
+ struct xfs_buf *agbp, /* (A only) buffer for agf structure */
+ xfs_agnumber_t agno, /* (A only) allocation group number */
+ xfs_btnum_t btnum, /* btree identifier */
+ struct xfs_inode *ip, /* (B only) inode owning the btree */
+ int whichfork); /* (B only) data/attr fork */
+
+/*
+ * Check for the cursor referring to the last block at the given level.
+ */
+int /* 1=is last block, 0=not last block */
+xfs_btree_islastblock(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ int level); /* level to check */
+
+/*
+ * Change the cursor to point to the last record in the current block
+ * at the given level. Other levels are unaffected.
+ */
+int /* success=1, failure=0 */
+xfs_btree_lastrec(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ int level); /* level to change */
+
+/*
+ * Compute first and last byte offsets for the fields given.
+ * Interprets the offsets table, which contains struct field offsets.
+ */
+void
+xfs_btree_offsets(
+ __int64_t fields, /* bitmask of fields */
+ const short *offsets,/* table of field offsets */
+ int nbits, /* number of bits to inspect */
+ int *first, /* output: first byte offset */
+ int *last); /* output: last byte offset */
+
+/*
+ * Get a buffer for the block, return it read in.
+ * Long-form addressing.
+ */
+int /* error */
+xfs_btree_read_bufl(
+ struct xfs_mount *mp, /* file system mount point */
+ struct xfs_trans *tp, /* transaction pointer */
+ xfs_fsblock_t fsbno, /* file system block number */
+ uint lock, /* lock flags for read_buf */
+ struct xfs_buf **bpp, /* buffer for fsbno */
+ int refval);/* ref count value for buffer */
+
+/*
+ * Get a buffer for the block, return it read in.
+ * Short-form addressing.
+ */
+int /* error */
+xfs_btree_read_bufs(
+ struct xfs_mount *mp, /* file system mount point */
+ struct xfs_trans *tp, /* transaction pointer */
+ xfs_agnumber_t agno, /* allocation group number */
+ xfs_agblock_t agbno, /* allocation group block number */
+ uint lock, /* lock flags for read_buf */
+ struct xfs_buf **bpp, /* buffer for agno/agbno */
+ int refval);/* ref count value for buffer */
+
+/*
+ * Read-ahead the block, don't wait for it, don't return a buffer.
+ * Long-form addressing.
+ */
+void /* error */
+xfs_btree_reada_bufl(
+ struct xfs_mount *mp, /* file system mount point */
+ xfs_fsblock_t fsbno, /* file system block number */
+ xfs_extlen_t count); /* count of filesystem blocks */
+
+/*
+ * Read-ahead the block, don't wait for it, don't return a buffer.
+ * Short-form addressing.
+ */
+void /* error */
+xfs_btree_reada_bufs(
+ struct xfs_mount *mp, /* file system mount point */
+ xfs_agnumber_t agno, /* allocation group number */
+ xfs_agblock_t agbno, /* allocation group block number */
+ xfs_extlen_t count); /* count of filesystem blocks */
+
+/*
+ * Read-ahead btree blocks, at the given level.
+ * Bits in lr are set from XFS_BTCUR_{LEFT,RIGHT}RA.
+ */
+int /* readahead block count */
+xfs_btree_readahead_core(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ int lev, /* level in btree */
+ int lr); /* left/right bits */
+
+static inline int /* readahead block count */
+xfs_btree_readahead(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ int lev, /* level in btree */
+ int lr) /* left/right bits */
+{
+ if ((cur->bc_ra[lev] | lr) == cur->bc_ra[lev])
+ return 0;
+
+ return xfs_btree_readahead_core(cur, lev, lr);
+}
+
+
+/*
+ * Set the buffer for level "lev" in the cursor to bp, releasing
+ * any previous buffer.
+ */
+void
+xfs_btree_setbuf(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ int lev, /* level in btree */
+ struct xfs_buf *bp); /* new buffer to set */
+
+#endif /* __KERNEL__ */
+
+
+/*
+ * Min and max functions for extlen, agblock, fileoff, and filblks types.
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_EXTLEN_MIN)
+xfs_extlen_t xfs_extlen_min(xfs_extlen_t a, xfs_extlen_t b);
+#define XFS_EXTLEN_MIN(a,b) xfs_extlen_min(a,b)
+#else
+#define XFS_EXTLEN_MIN(a,b) \
+ ((xfs_extlen_t)(a) < (xfs_extlen_t)(b) ? \
+ (xfs_extlen_t)(a) : (xfs_extlen_t)(b))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_EXTLEN_MAX)
+xfs_extlen_t xfs_extlen_max(xfs_extlen_t a, xfs_extlen_t b);
+#define XFS_EXTLEN_MAX(a,b) xfs_extlen_max(a,b)
+#else
+#define XFS_EXTLEN_MAX(a,b) \
+ ((xfs_extlen_t)(a) > (xfs_extlen_t)(b) ? \
+ (xfs_extlen_t)(a) : (xfs_extlen_t)(b))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_AGBLOCK_MIN)
+xfs_agblock_t xfs_agblock_min(xfs_agblock_t a, xfs_agblock_t b);
+#define XFS_AGBLOCK_MIN(a,b) xfs_agblock_min(a,b)
+#else
+#define XFS_AGBLOCK_MIN(a,b) \
+ ((xfs_agblock_t)(a) < (xfs_agblock_t)(b) ? \
+ (xfs_agblock_t)(a) : (xfs_agblock_t)(b))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_AGBLOCK_MAX)
+xfs_agblock_t xfs_agblock_max(xfs_agblock_t a, xfs_agblock_t b);
+#define XFS_AGBLOCK_MAX(a,b) xfs_agblock_max(a,b)
+#else
+#define XFS_AGBLOCK_MAX(a,b) \
+ ((xfs_agblock_t)(a) > (xfs_agblock_t)(b) ? \
+ (xfs_agblock_t)(a) : (xfs_agblock_t)(b))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_FILEOFF_MIN)
+xfs_fileoff_t xfs_fileoff_min(xfs_fileoff_t a, xfs_fileoff_t b);
+#define XFS_FILEOFF_MIN(a,b) xfs_fileoff_min(a,b)
+#else
+#define XFS_FILEOFF_MIN(a,b) \
+ ((xfs_fileoff_t)(a) < (xfs_fileoff_t)(b) ? \
+ (xfs_fileoff_t)(a) : (xfs_fileoff_t)(b))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_FILEOFF_MAX)
+xfs_fileoff_t xfs_fileoff_max(xfs_fileoff_t a, xfs_fileoff_t b);
+#define XFS_FILEOFF_MAX(a,b) xfs_fileoff_max(a,b)
+#else
+#define XFS_FILEOFF_MAX(a,b) \
+ ((xfs_fileoff_t)(a) > (xfs_fileoff_t)(b) ? \
+ (xfs_fileoff_t)(a) : (xfs_fileoff_t)(b))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_FILBLKS_MIN)
+xfs_filblks_t xfs_filblks_min(xfs_filblks_t a, xfs_filblks_t b);
+#define XFS_FILBLKS_MIN(a,b) xfs_filblks_min(a,b)
+#else
+#define XFS_FILBLKS_MIN(a,b) \
+ ((xfs_filblks_t)(a) < (xfs_filblks_t)(b) ? \
+ (xfs_filblks_t)(a) : (xfs_filblks_t)(b))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_FILBLKS_MAX)
+xfs_filblks_t xfs_filblks_max(xfs_filblks_t a, xfs_filblks_t b);
+#define XFS_FILBLKS_MAX(a,b) xfs_filblks_max(a,b)
+#else
+#define XFS_FILBLKS_MAX(a,b) \
+ ((xfs_filblks_t)(a) > (xfs_filblks_t)(b) ? \
+ (xfs_filblks_t)(a) : (xfs_filblks_t)(b))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_FSB_SANITY_CHECK)
+int xfs_fsb_sanity_check(struct xfs_mount *mp, xfs_fsblock_t fsb);
+#define XFS_FSB_SANITY_CHECK(mp,fsb) xfs_fsb_sanity_check(mp,fsb)
+#else
+#define XFS_FSB_SANITY_CHECK(mp,fsb) \
+ (XFS_FSB_TO_AGNO(mp, fsb) < mp->m_sb.sb_agcount && \
+ XFS_FSB_TO_AGBNO(mp, fsb) < mp->m_sb.sb_agblocks)
+#endif
+
+/*
+ * Macros to set EFSCORRUPTED & return/branch.
+ */
+#define XFS_WANT_CORRUPTED_GOTO(x,l) \
+ { \
+ int fs_is_ok = (x); \
+ ASSERT(fs_is_ok); \
+ if (unlikely(!fs_is_ok)) { \
+ XFS_ERROR_REPORT("XFS_WANT_CORRUPTED_GOTO", \
+ XFS_ERRLEVEL_LOW, NULL); \
+ error = XFS_ERROR(EFSCORRUPTED); \
+ goto l; \
+ } \
+ }
+
+#define XFS_WANT_CORRUPTED_RETURN(x) \
+ { \
+ int fs_is_ok = (x); \
+ ASSERT(fs_is_ok); \
+ if (unlikely(!fs_is_ok)) { \
+ XFS_ERROR_REPORT("XFS_WANT_CORRUPTED_RETURN", \
+ XFS_ERRLEVEL_LOW, NULL); \
+ return XFS_ERROR(EFSCORRUPTED); \
+ } \
+ }
+
+#endif /* __XFS_BTREE_H__ */
diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c
new file mode 100644
index 00000000000000..9ab0039f07df21
--- /dev/null
+++ b/fs/xfs/xfs_buf_item.c
@@ -0,0 +1,1221 @@
+/*
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+/*
+ * This file contains the implementation of the xfs_buf_log_item.
+ * It contains the item operations used to manipulate the buf log
+ * items as well as utility routines used by the buffer specific
+ * transaction routines.
+ */
+
+#include "xfs.h"
+
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_buf_item.h"
+#include "xfs_sb.h"
+#include "xfs_dir.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_trans_priv.h"
+#include "xfs_rw.h"
+#include "xfs_bit.h"
+#include "xfs_error.h"
+
+
+kmem_zone_t *xfs_buf_item_zone;
+
+#ifdef XFS_TRANS_DEBUG
+/*
+ * This function uses an alternate strategy for tracking the bytes
+ * that the user requests to be logged. This can then be used
+ * in conjunction with the bli_orig array in the buf log item to
+ * catch bugs in our callers' code.
+ *
+ * We also double check the bits set in xfs_buf_item_log using a
+ * simple algorithm to check that every byte is accounted for.
+ */
+STATIC void
+xfs_buf_item_log_debug(
+ xfs_buf_log_item_t *bip,
+ uint first,
+ uint last)
+{
+ uint x;
+ uint byte;
+ uint nbytes;
+ uint chunk_num;
+ uint word_num;
+ uint bit_num;
+ uint bit_set;
+ uint *wordp;
+
+ ASSERT(bip->bli_logged != NULL);
+ byte = first;
+ nbytes = last - first + 1;
+ bfset(bip->bli_logged, first, nbytes);
+ for (x = 0; x < nbytes; x++) {
+ chunk_num = byte >> XFS_BLI_SHIFT;
+ word_num = chunk_num >> BIT_TO_WORD_SHIFT;
+ bit_num = chunk_num & (NBWORD - 1);
+ wordp = &(bip->bli_format.blf_data_map[word_num]);
+ bit_set = *wordp & (1 << bit_num);
+ ASSERT(bit_set);
+ byte++;
+ }
+}
+
+/*
+ * This function is called when we flush something into a buffer without
+ * logging it. This happens for things like inodes which are logged
+ * separately from the buffer.
+ */
+void
+xfs_buf_item_flush_log_debug(
+ xfs_buf_t *bp,
+ uint first,
+ uint last)
+{
+ xfs_buf_log_item_t *bip;
+ uint nbytes;
+
+ bip = XFS_BUF_FSPRIVATE(bp, xfs_buf_log_item_t*);
+ if ((bip == NULL) || (bip->bli_item.li_type != XFS_LI_BUF)) {
+ return;
+ }
+
+ ASSERT(bip->bli_logged != NULL);
+ nbytes = last - first + 1;
+ bfset(bip->bli_logged, first, nbytes);
+}
+
+/*
+ * This function is called to verify that our caller's have logged
+ * all the bytes that they changed.
+ *
+ * It does this by comparing the original copy of the buffer stored in
+ * the buf log item's bli_orig array to the current copy of the buffer
+ * and ensuring that all bytes which miscompare are set in the bli_logged
+ * array of the buf log item.
+ */
+STATIC void
+xfs_buf_item_log_check(
+ xfs_buf_log_item_t *bip)
+{
+ char *orig;
+ char *buffer;
+ int x;
+ xfs_buf_t *bp;
+
+ ASSERT(bip->bli_orig != NULL);
+ ASSERT(bip->bli_logged != NULL);
+
+ bp = bip->bli_buf;
+ ASSERT(XFS_BUF_COUNT(bp) > 0);
+ ASSERT(XFS_BUF_PTR(bp) != NULL);
+ orig = bip->bli_orig;
+ buffer = XFS_BUF_PTR(bp);
+ for (x = 0; x < XFS_BUF_COUNT(bp); x++) {
+ if (orig[x] != buffer[x] && !btst(bip->bli_logged, x))
+ cmn_err(CE_PANIC,
+ "xfs_buf_item_log_check bip %x buffer %x orig %x index %d",
+ bip, bp, orig, x);
+ }
+}
+#else
+#define xfs_buf_item_log_debug(x,y,z)
+#define xfs_buf_item_log_check(x)
+#endif
+
+STATIC void xfs_buf_error_relse(xfs_buf_t *bp);
+STATIC void xfs_buf_do_callbacks(xfs_buf_t *bp, xfs_log_item_t *lip);
+
+/*
+ * This returns the number of log iovecs needed to log the
+ * given buf log item.
+ *
+ * It calculates this as 1 iovec for the buf log format structure
+ * and 1 for each stretch of non-contiguous chunks to be logged.
+ * Contiguous chunks are logged in a single iovec.
+ *
+ * If the XFS_BLI_STALE flag has been set, then log nothing.
+ */
+uint
+xfs_buf_item_size(
+ xfs_buf_log_item_t *bip)
+{
+ uint nvecs;
+ int next_bit;
+ int last_bit;
+ xfs_buf_t *bp;
+
+ ASSERT(atomic_read(&bip->bli_refcount) > 0);
+ if (bip->bli_flags & XFS_BLI_STALE) {
+ /*
+ * The buffer is stale, so all we need to log
+ * is the buf log format structure with the
+ * cancel flag in it.
+ */
+ xfs_buf_item_trace("SIZE STALE", bip);
+ ASSERT(bip->bli_format.blf_flags & XFS_BLI_CANCEL);
+ return 1;
+ }
+
+ bp = bip->bli_buf;
+ ASSERT(bip->bli_flags & XFS_BLI_LOGGED);
+ nvecs = 1;
+ last_bit = xfs_next_bit(bip->bli_format.blf_data_map,
+ bip->bli_format.blf_map_size, 0);
+ ASSERT(last_bit != -1);
+ nvecs++;
+ while (last_bit != -1) {
+ /*
+ * This takes the bit number to start looking from and
+ * returns the next set bit from there. It returns -1
+ * if there are no more bits set or the start bit is
+ * beyond the end of the bitmap.
+ */
+ next_bit = xfs_next_bit(bip->bli_format.blf_data_map,
+ bip->bli_format.blf_map_size,
+ last_bit + 1);
+ /*
+ * If we run out of bits, leave the loop,
+ * else if we find a new set of bits bump the number of vecs,
+ * else keep scanning the current set of bits.
+ */
+ if (next_bit == -1) {
+ last_bit = -1;
+ } else if (next_bit != last_bit + 1) {
+ last_bit = next_bit;
+ nvecs++;
+ } else if (xfs_buf_offset(bp, next_bit * XFS_BLI_CHUNK) !=
+ (xfs_buf_offset(bp, last_bit * XFS_BLI_CHUNK) +
+ XFS_BLI_CHUNK)) {
+ last_bit = next_bit;
+ nvecs++;
+ } else {
+ last_bit++;
+ }
+ }
+
+ xfs_buf_item_trace("SIZE NORM", bip);
+ return nvecs;
+}
+
+/*
+ * This is called to fill in the vector of log iovecs for the
+ * given log buf item. It fills the first entry with a buf log
+ * format structure, and the rest point to contiguous chunks
+ * within the buffer.
+ */
+void
+xfs_buf_item_format(
+ xfs_buf_log_item_t *bip,
+ xfs_log_iovec_t *log_vector)
+{
+ uint base_size;
+ uint nvecs;
+ xfs_log_iovec_t *vecp;
+ xfs_buf_t *bp;
+ int first_bit;
+ int last_bit;
+ int next_bit;
+ uint nbits;
+ uint buffer_offset;
+
+ ASSERT(atomic_read(&bip->bli_refcount) > 0);
+ ASSERT((bip->bli_flags & XFS_BLI_LOGGED) ||
+ (bip->bli_flags & XFS_BLI_STALE));
+ bp = bip->bli_buf;
+ ASSERT(XFS_BUF_BP_ISMAPPED(bp));
+ vecp = log_vector;
+
+ /*
+ * The size of the base structure is the size of the
+ * declared structure plus the space for the extra words
+ * of the bitmap. We subtract one from the map size, because
+ * the first element of the bitmap is accounted for in the
+ * size of the base structure.
+ */
+ base_size =
+ (uint)(sizeof(xfs_buf_log_format_t) +
+ ((bip->bli_format.blf_map_size - 1) * sizeof(uint)));
+ vecp->i_addr = (xfs_caddr_t)&bip->bli_format;
+ vecp->i_len = base_size;
+ vecp++;
+ nvecs = 1;
+
+ if (bip->bli_flags & XFS_BLI_STALE) {
+ /*
+ * The buffer is stale, so all we need to log
+ * is the buf log format structure with the
+ * cancel flag in it.
+ */
+ xfs_buf_item_trace("FORMAT STALE", bip);
+ ASSERT(bip->bli_format.blf_flags & XFS_BLI_CANCEL);
+ bip->bli_format.blf_size = nvecs;
+ return;
+ }
+
+ /*
+ * Fill in an iovec for each set of contiguous chunks.
+ */
+ first_bit = xfs_next_bit(bip->bli_format.blf_data_map,
+ bip->bli_format.blf_map_size, 0);
+ ASSERT(first_bit != -1);
+ last_bit = first_bit;
+ nbits = 1;
+ for (;;) {
+ /*
+ * This takes the bit number to start looking from and
+ * returns the next set bit from there. It returns -1
+ * if there are no more bits set or the start bit is
+ * beyond the end of the bitmap.
+ */
+ next_bit = xfs_next_bit(bip->bli_format.blf_data_map,
+ bip->bli_format.blf_map_size,
+ (uint)last_bit + 1);
+ /*
+ * If we run out of bits fill in the last iovec and get
+ * out of the loop.
+ * Else if we start a new set of bits then fill in the
+ * iovec for the series we were looking at and start
+ * counting the bits in the new one.
+ * Else we're still in the same set of bits so just
+ * keep counting and scanning.
+ */
+ if (next_bit == -1) {
+ buffer_offset = first_bit * XFS_BLI_CHUNK;
+ vecp->i_addr = xfs_buf_offset(bp, buffer_offset);
+ vecp->i_len = nbits * XFS_BLI_CHUNK;
+ nvecs++;
+ break;
+ } else if (next_bit != last_bit + 1) {
+ buffer_offset = first_bit * XFS_BLI_CHUNK;
+ vecp->i_addr = xfs_buf_offset(bp, buffer_offset);
+ vecp->i_len = nbits * XFS_BLI_CHUNK;
+ nvecs++;
+ vecp++;
+ first_bit = next_bit;
+ last_bit = next_bit;
+ nbits = 1;
+ } else if (xfs_buf_offset(bp, next_bit << XFS_BLI_SHIFT) !=
+ (xfs_buf_offset(bp, last_bit << XFS_BLI_SHIFT) +
+ XFS_BLI_CHUNK)) {
+ buffer_offset = first_bit * XFS_BLI_CHUNK;
+ vecp->i_addr = xfs_buf_offset(bp, buffer_offset);
+ vecp->i_len = nbits * XFS_BLI_CHUNK;
+/* You would think we need to bump the nvecs here too, but we do not
+ * this number is used by recovery, and it gets confused by the boundary
+ * split here
+ * nvecs++;
+ */
+ vecp++;
+ first_bit = next_bit;
+ last_bit = next_bit;
+ nbits = 1;
+ } else {
+ last_bit++;
+ nbits++;
+ }
+ }
+ bip->bli_format.blf_size = nvecs;
+
+ /*
+ * Check to make sure everything is consistent.
+ */
+ xfs_buf_item_trace("FORMAT NORM", bip);
+ xfs_buf_item_log_check(bip);
+}
+
+/*
+ * This is called to pin the buffer associated with the buf log
+ * item in memory so it cannot be written out. Simply call bpin()
+ * on the buffer to do this.
+ */
+void
+xfs_buf_item_pin(
+ xfs_buf_log_item_t *bip)
+{
+ xfs_buf_t *bp;
+
+ bp = bip->bli_buf;
+ ASSERT(XFS_BUF_ISBUSY(bp));
+ ASSERT(atomic_read(&bip->bli_refcount) > 0);
+ ASSERT((bip->bli_flags & XFS_BLI_LOGGED) ||
+ (bip->bli_flags & XFS_BLI_STALE));
+ xfs_buf_item_trace("PIN", bip);
+ xfs_buftrace("XFS_PIN", bp);
+ xfs_bpin(bp);
+}
+
+
+/*
+ * This is called to unpin the buffer associated with the buf log
+ * item which was previously pinned with a call to xfs_buf_item_pin().
+ * Just call bunpin() on the buffer to do this.
+ *
+ * Also drop the reference to the buf item for the current transaction.
+ * If the XFS_BLI_STALE flag is set and we are the last reference,
+ * then free up the buf log item and unlock the buffer.
+ */
+void
+xfs_buf_item_unpin(
+ xfs_buf_log_item_t *bip,
+ int stale)
+{
+ xfs_mount_t *mp;
+ xfs_buf_t *bp;
+ int freed;
+ SPLDECL(s);
+
+ bp = bip->bli_buf;
+ ASSERT(bp != NULL);
+ ASSERT(XFS_BUF_FSPRIVATE(bp, xfs_buf_log_item_t *) == bip);
+ ASSERT(atomic_read(&bip->bli_refcount) > 0);
+ xfs_buf_item_trace("UNPIN", bip);
+ xfs_buftrace("XFS_UNPIN", bp);
+
+ freed = atomic_dec_and_test(&bip->bli_refcount);
+ mp = bip->bli_item.li_mountp;
+ xfs_bunpin(bp);
+ if (freed && stale) {
+ ASSERT(bip->bli_flags & XFS_BLI_STALE);
+ ASSERT(XFS_BUF_VALUSEMA(bp) <= 0);
+ ASSERT(!(XFS_BUF_ISDELAYWRITE(bp)));
+ ASSERT(XFS_BUF_ISSTALE(bp));
+ ASSERT(bip->bli_format.blf_flags & XFS_BLI_CANCEL);
+ xfs_buf_item_trace("UNPIN STALE", bip);
+ xfs_buftrace("XFS_UNPIN STALE", bp);
+ /*
+ * If we get called here because of an IO error, we may
+ * or may not have the item on the AIL. xfs_trans_delete_ail()
+ * will take care of that situation.
+ * xfs_trans_delete_ail() drops the AIL lock.
+ */
+ if (bip->bli_flags & XFS_BLI_STALE_INODE) {
+ xfs_buf_do_callbacks(bp, (xfs_log_item_t *)bip);
+ XFS_BUF_SET_FSPRIVATE(bp, NULL);
+ XFS_BUF_CLR_IODONE_FUNC(bp);
+ } else {
+ AIL_LOCK(mp,s);
+ xfs_trans_delete_ail(mp, (xfs_log_item_t *)bip, s);
+ xfs_buf_item_relse(bp);
+ ASSERT(XFS_BUF_FSPRIVATE(bp, void *) == NULL);
+ }
+ xfs_buf_relse(bp);
+ }
+}
+
+/*
+ * this is called from uncommit in the forced-shutdown path.
+ * we need to check to see if the reference count on the log item
+ * is going to drop to zero. If so, unpin will free the log item
+ * so we need to free the item's descriptor (that points to the item)
+ * in the transaction.
+ */
+void
+xfs_buf_item_unpin_remove(
+ xfs_buf_log_item_t *bip,
+ xfs_trans_t *tp)
+{
+ xfs_buf_t *bp;
+ xfs_log_item_desc_t *lidp;
+ int stale = 0;
+
+ bp = bip->bli_buf;
+ /*
+ * will xfs_buf_item_unpin() call xfs_buf_item_relse()?
+ */
+ if ((atomic_read(&bip->bli_refcount) == 1) &&
+ (bip->bli_flags & XFS_BLI_STALE)) {
+ ASSERT(XFS_BUF_VALUSEMA(bip->bli_buf) <= 0);
+ xfs_buf_item_trace("UNPIN REMOVE", bip);
+ xfs_buftrace("XFS_UNPIN_REMOVE", bp);
+ /*
+ * yes -- clear the xaction descriptor in-use flag
+ * and free the chunk if required. We can safely
+ * do some work here and then call buf_item_unpin
+ * to do the rest because if the if is true, then
+ * we are holding the buffer locked so no one else
+ * will be able to bump up the refcount.
+ */
+ lidp = xfs_trans_find_item(tp, (xfs_log_item_t *) bip);
+ stale = lidp->lid_flags & XFS_LID_BUF_STALE;
+ xfs_trans_free_item(tp, lidp);
+ /*
+ * Since the transaction no longer refers to the buffer,
+ * the buffer should no longer refer to the transaction.
+ */
+ XFS_BUF_SET_FSPRIVATE2(bp, NULL);
+ }
+
+ xfs_buf_item_unpin(bip, stale);
+
+ return;
+}
+
+/*
+ * This is called to attempt to lock the buffer associated with this
+ * buf log item. Don't sleep on the buffer lock. If we can't get
+ * the lock right away, return 0. If we can get the lock, pull the
+ * buffer from the free list, mark it busy, and return 1.
+ */
+uint
+xfs_buf_item_trylock(
+ xfs_buf_log_item_t *bip)
+{
+ xfs_buf_t *bp;
+
+ bp = bip->bli_buf;
+
+ if (XFS_BUF_ISPINNED(bp)) {
+ return XFS_ITEM_PINNED;
+ }
+
+ if (!XFS_BUF_CPSEMA(bp)) {
+ return XFS_ITEM_LOCKED;
+ }
+
+ /*
+ * Remove the buffer from the free list. Only do this
+ * if it's on the free list. Private buffers like the
+ * superblock buffer are not.
+ */
+ XFS_BUF_HOLD(bp);
+
+ ASSERT(!(bip->bli_flags & XFS_BLI_STALE));
+ xfs_buf_item_trace("TRYLOCK SUCCESS", bip);
+ return XFS_ITEM_SUCCESS;
+}
+
+/*
+ * Release the buffer associated with the buf log item.
+ * If there is no dirty logged data associated with the
+ * buffer recorded in the buf log item, then free the
+ * buf log item and remove the reference to it in the
+ * buffer.
+ *
+ * This call ignores the recursion count. It is only called
+ * when the buffer should REALLY be unlocked, regardless
+ * of the recursion count.
+ *
+ * If the XFS_BLI_HOLD flag is set in the buf log item, then
+ * free the log item if necessary but do not unlock the buffer.
+ * This is for support of xfs_trans_bhold(). Make sure the
+ * XFS_BLI_HOLD field is cleared if we don't free the item.
+ */
+void
+xfs_buf_item_unlock(
+ xfs_buf_log_item_t *bip)
+{
+ int aborted;
+ xfs_buf_t *bp;
+ uint hold;
+
+ bp = bip->bli_buf;
+ xfs_buftrace("XFS_UNLOCK", bp);
+
+ /*
+ * Clear the buffer's association with this transaction.
+ */
+ XFS_BUF_SET_FSPRIVATE2(bp, NULL);
+
+ /*
+ * If this is a transaction abort, don't return early.
+ * Instead, allow the brelse to happen.
+ * Normally it would be done for stale (cancelled) buffers
+ * at unpin time, but we'll never go through the pin/unpin
+ * cycle if we abort inside commit.
+ */
+ aborted = (bip->bli_item.li_flags & XFS_LI_ABORTED) != 0;
+
+ /*
+ * If the buf item is marked stale, then don't do anything.
+ * We'll unlock the buffer and free the buf item when the
+ * buffer is unpinned for the last time.
+ */
+ if (bip->bli_flags & XFS_BLI_STALE) {
+ bip->bli_flags &= ~XFS_BLI_LOGGED;
+ xfs_buf_item_trace("UNLOCK STALE", bip);
+ ASSERT(bip->bli_format.blf_flags & XFS_BLI_CANCEL);
+ if (!aborted)
+ return;
+ }
+
+ /*
+ * Drop the transaction's reference to the log item if
+ * it was not logged as part of the transaction. Otherwise
+ * we'll drop the reference in xfs_buf_item_unpin() when
+ * the transaction is really through with the buffer.
+ */
+ if (!(bip->bli_flags & XFS_BLI_LOGGED)) {
+ atomic_dec(&bip->bli_refcount);
+ } else {
+ /*
+ * Clear the logged flag since this is per
+ * transaction state.
+ */
+ bip->bli_flags &= ~XFS_BLI_LOGGED;
+ }
+
+ /*
+ * Before possibly freeing the buf item, determine if we should
+ * release the buffer at the end of this routine.
+ */
+ hold = bip->bli_flags & XFS_BLI_HOLD;
+ xfs_buf_item_trace("UNLOCK", bip);
+
+ /*
+ * If the buf item isn't tracking any data, free it.
+ * Otherwise, if XFS_BLI_HOLD is set clear it.
+ */
+ if (xfs_count_bits(bip->bli_format.blf_data_map,
+ bip->bli_format.blf_map_size, 0) == 0) {
+ xfs_buf_item_relse(bp);
+ } else if (hold) {
+ bip->bli_flags &= ~XFS_BLI_HOLD;
+ }
+
+ /*
+ * Release the buffer if XFS_BLI_HOLD was not set.
+ */
+ if (!hold) {
+ xfs_buf_relse(bp);
+ }
+}
+
+/*
+ * This is called to find out where the oldest active copy of the
+ * buf log item in the on disk log resides now that the last log
+ * write of it completed at the given lsn.
+ * We always re-log all the dirty data in a buffer, so usually the
+ * latest copy in the on disk log is the only one that matters. For
+ * those cases we simply return the given lsn.
+ *
+ * The one exception to this is for buffers full of newly allocated
+ * inodes. These buffers are only relogged with the XFS_BLI_INODE_BUF
+ * flag set, indicating that only the di_next_unlinked fields from the
+ * inodes in the buffers will be replayed during recovery. If the
+ * original newly allocated inode images have not yet been flushed
+ * when the buffer is so relogged, then we need to make sure that we
+ * keep the old images in the 'active' portion of the log. We do this
+ * by returning the original lsn of that transaction here rather than
+ * the current one.
+ */
+xfs_lsn_t
+xfs_buf_item_committed(
+ xfs_buf_log_item_t *bip,
+ xfs_lsn_t lsn)
+{
+ xfs_buf_item_trace("COMMITTED", bip);
+ if ((bip->bli_flags & XFS_BLI_INODE_ALLOC_BUF) &&
+ (bip->bli_item.li_lsn != 0)) {
+ return bip->bli_item.li_lsn;
+ }
+ return (lsn);
+}
+
+/*
+ * This is called when the transaction holding the buffer is aborted.
+ * Just behave as if the transaction had been cancelled. If we're shutting down
+ * and have aborted this transaction, we'll trap this buffer when it tries to
+ * get written out.
+ */
+void
+xfs_buf_item_abort(
+ xfs_buf_log_item_t *bip)
+{
+ xfs_buf_t *bp;
+
+ bp = bip->bli_buf;
+ xfs_buftrace("XFS_ABORT", bp);
+ XFS_BUF_SUPER_STALE(bp);
+ xfs_buf_item_unlock(bip);
+ return;
+}
+
+/*
+ * This is called to asynchronously write the buffer associated with this
+ * buf log item out to disk. The buffer will already have been locked by
+ * a successful call to xfs_buf_item_trylock(). If the buffer still has
+ * B_DELWRI set, then get it going out to disk with a call to bawrite().
+ * If not, then just release the buffer.
+ */
+void
+xfs_buf_item_push(
+ xfs_buf_log_item_t *bip)
+{
+ xfs_buf_t *bp;
+
+ ASSERT(!(bip->bli_flags & XFS_BLI_STALE));
+ xfs_buf_item_trace("PUSH", bip);
+
+ bp = bip->bli_buf;
+
+ if (XFS_BUF_ISDELAYWRITE(bp)) {
+ xfs_bawrite(bip->bli_item.li_mountp, bp);
+ } else {
+ xfs_buf_relse(bp);
+ }
+}
+
+/* ARGSUSED */
+void
+xfs_buf_item_committing(xfs_buf_log_item_t *bip, xfs_lsn_t commit_lsn)
+{
+}
+
+/*
+ * This is the ops vector shared by all buf log items.
+ */
+struct xfs_item_ops xfs_buf_item_ops = {
+ .iop_size = (uint(*)(xfs_log_item_t*))xfs_buf_item_size,
+ .iop_format = (void(*)(xfs_log_item_t*, xfs_log_iovec_t*))
+ xfs_buf_item_format,
+ .iop_pin = (void(*)(xfs_log_item_t*))xfs_buf_item_pin,
+ .iop_unpin = (void(*)(xfs_log_item_t*, int))xfs_buf_item_unpin,
+ .iop_unpin_remove = (void(*)(xfs_log_item_t*, xfs_trans_t *))
+ xfs_buf_item_unpin_remove,
+ .iop_trylock = (uint(*)(xfs_log_item_t*))xfs_buf_item_trylock,
+ .iop_unlock = (void(*)(xfs_log_item_t*))xfs_buf_item_unlock,
+ .iop_committed = (xfs_lsn_t(*)(xfs_log_item_t*, xfs_lsn_t))
+ xfs_buf_item_committed,
+ .iop_push = (void(*)(xfs_log_item_t*))xfs_buf_item_push,
+ .iop_abort = (void(*)(xfs_log_item_t*))xfs_buf_item_abort,
+ .iop_pushbuf = NULL,
+ .iop_committing = (void(*)(xfs_log_item_t*, xfs_lsn_t))
+ xfs_buf_item_committing
+};
+
+
+/*
+ * Allocate a new buf log item to go with the given buffer.
+ * Set the buffer's b_fsprivate field to point to the new
+ * buf log item. If there are other item's attached to the
+ * buffer (see xfs_buf_attach_iodone() below), then put the
+ * buf log item at the front.
+ */
+void
+xfs_buf_item_init(
+ xfs_buf_t *bp,
+ xfs_mount_t *mp)
+{
+ xfs_log_item_t *lip;
+ xfs_buf_log_item_t *bip;
+ int chunks;
+ int map_size;
+
+ /*
+ * Check to see if there is already a buf log item for
+ * this buffer. If there is, it is guaranteed to be
+ * the first. If we do already have one, there is
+ * nothing to do here so return.
+ */
+ if (XFS_BUF_FSPRIVATE3(bp, xfs_mount_t *) != mp)
+ XFS_BUF_SET_FSPRIVATE3(bp, mp);
+ XFS_BUF_SET_BDSTRAT_FUNC(bp, xfs_bdstrat_cb);
+ if (XFS_BUF_FSPRIVATE(bp, void *) != NULL) {
+ lip = XFS_BUF_FSPRIVATE(bp, xfs_log_item_t *);
+ if (lip->li_type == XFS_LI_BUF) {
+ return;
+ }
+ }
+
+ /*
+ * chunks is the number of XFS_BLI_CHUNK size pieces
+ * the buffer can be divided into. Make sure not to
+ * truncate any pieces. map_size is the size of the
+ * bitmap needed to describe the chunks of the buffer.
+ */
+ chunks = (int)((XFS_BUF_COUNT(bp) + (XFS_BLI_CHUNK - 1)) >> XFS_BLI_SHIFT);
+ map_size = (int)((chunks + NBWORD) >> BIT_TO_WORD_SHIFT);
+
+ bip = (xfs_buf_log_item_t*)kmem_zone_zalloc(xfs_buf_item_zone,
+ KM_SLEEP);
+ bip->bli_item.li_type = XFS_LI_BUF;
+ bip->bli_item.li_ops = &xfs_buf_item_ops;
+ bip->bli_item.li_mountp = mp;
+ bip->bli_buf = bp;
+ bip->bli_format.blf_type = XFS_LI_BUF;
+ bip->bli_format.blf_blkno = (__int64_t)XFS_BUF_ADDR(bp);
+ bip->bli_format.blf_len = (ushort)BTOBB(XFS_BUF_COUNT(bp));
+ bip->bli_format.blf_map_size = map_size;
+#ifdef XFS_BLI_TRACE
+ bip->bli_trace = ktrace_alloc(XFS_BLI_TRACE_SIZE, KM_SLEEP);
+#endif
+
+#ifdef XFS_TRANS_DEBUG
+ /*
+ * Allocate the arrays for tracking what needs to be logged
+ * and what our callers request to be logged. bli_orig
+ * holds a copy of the original, clean buffer for comparison
+ * against, and bli_logged keeps a 1 bit flag per byte in
+ * the buffer to indicate which bytes the callers have asked
+ * to have logged.
+ */
+ bip->bli_orig = (char *)kmem_alloc(XFS_BUF_COUNT(bp), KM_SLEEP);
+ memcpy(bip->bli_orig, XFS_BUF_PTR(bp), XFS_BUF_COUNT(bp));
+ bip->bli_logged = (char *)kmem_zalloc(XFS_BUF_COUNT(bp) / NBBY, KM_SLEEP);
+#endif
+
+ /*
+ * Put the buf item into the list of items attached to the
+ * buffer at the front.
+ */
+ if (XFS_BUF_FSPRIVATE(bp, void *) != NULL) {
+ bip->bli_item.li_bio_list =
+ XFS_BUF_FSPRIVATE(bp, xfs_log_item_t *);
+ }
+ XFS_BUF_SET_FSPRIVATE(bp, bip);
+}
+
+
+/*
+ * Mark bytes first through last inclusive as dirty in the buf
+ * item's bitmap.
+ */
+void
+xfs_buf_item_log(
+ xfs_buf_log_item_t *bip,
+ uint first,
+ uint last)
+{
+ uint first_bit;
+ uint last_bit;
+ uint bits_to_set;
+ uint bits_set;
+ uint word_num;
+ uint *wordp;
+ uint bit;
+ uint end_bit;
+ uint mask;
+
+ /*
+ * Mark the item as having some dirty data for
+ * quick reference in xfs_buf_item_dirty.
+ */
+ bip->bli_flags |= XFS_BLI_DIRTY;
+
+ /*
+ * Convert byte offsets to bit numbers.
+ */
+ first_bit = first >> XFS_BLI_SHIFT;
+ last_bit = last >> XFS_BLI_SHIFT;
+
+ /*
+ * Calculate the total number of bits to be set.
+ */
+ bits_to_set = last_bit - first_bit + 1;
+
+ /*
+ * Get a pointer to the first word in the bitmap
+ * to set a bit in.
+ */
+ word_num = first_bit >> BIT_TO_WORD_SHIFT;
+ wordp = &(bip->bli_format.blf_data_map[word_num]);
+
+ /*
+ * Calculate the starting bit in the first word.
+ */
+ bit = first_bit & (uint)(NBWORD - 1);
+
+ /*
+ * First set any bits in the first word of our range.
+ * If it starts at bit 0 of the word, it will be
+ * set below rather than here. That is what the variable
+ * bit tells us. The variable bits_set tracks the number
+ * of bits that have been set so far. End_bit is the number
+ * of the last bit to be set in this word plus one.
+ */
+ if (bit) {
+ end_bit = MIN(bit + bits_to_set, (uint)NBWORD);
+ mask = ((1 << (end_bit - bit)) - 1) << bit;
+ *wordp |= mask;
+ wordp++;
+ bits_set = end_bit - bit;
+ } else {
+ bits_set = 0;
+ }
+
+ /*
+ * Now set bits a whole word at a time that are between
+ * first_bit and last_bit.
+ */
+ while ((bits_to_set - bits_set) >= NBWORD) {
+ *wordp |= 0xffffffff;
+ bits_set += NBWORD;
+ wordp++;
+ }
+
+ /*
+ * Finally, set any bits left to be set in one last partial word.
+ */
+ end_bit = bits_to_set - bits_set;
+ if (end_bit) {
+ mask = (1 << end_bit) - 1;
+ *wordp |= mask;
+ }
+
+ xfs_buf_item_log_debug(bip, first, last);
+}
+
+
+/*
+ * Return 1 if the buffer has some data that has been logged (at any
+ * point, not just the current transaction) and 0 if not.
+ */
+uint
+xfs_buf_item_dirty(
+ xfs_buf_log_item_t *bip)
+{
+ return (bip->bli_flags & XFS_BLI_DIRTY);
+}
+
+/*
+ * This is called when the buf log item is no longer needed. It should
+ * free the buf log item associated with the given buffer and clear
+ * the buffer's pointer to the buf log item. If there are no more
+ * items in the list, clear the b_iodone field of the buffer (see
+ * xfs_buf_attach_iodone() below).
+ */
+void
+xfs_buf_item_relse(
+ xfs_buf_t *bp)
+{
+ xfs_buf_log_item_t *bip;
+
+ xfs_buftrace("XFS_RELSE", bp);
+ bip = XFS_BUF_FSPRIVATE(bp, xfs_buf_log_item_t*);
+ XFS_BUF_SET_FSPRIVATE(bp, bip->bli_item.li_bio_list);
+ if ((XFS_BUF_FSPRIVATE(bp, void *) == NULL) &&
+ (XFS_BUF_IODONE_FUNC(bp) != NULL)) {
+ ASSERT((XFS_BUF_ISUNINITIAL(bp)) == 0);
+ XFS_BUF_CLR_IODONE_FUNC(bp);
+ }
+
+#ifdef XFS_TRANS_DEBUG
+ kmem_free(bip->bli_orig, XFS_BUF_COUNT(bp));
+ bip->bli_orig = NULL;
+ kmem_free(bip->bli_logged, XFS_BUF_COUNT(bp) / NBBY);
+ bip->bli_logged = NULL;
+#endif /* XFS_TRANS_DEBUG */
+
+#ifdef XFS_BLI_TRACE
+ ktrace_free(bip->bli_trace);
+#endif
+ kmem_zone_free(xfs_buf_item_zone, bip);
+}
+
+
+/*
+ * Add the given log item with its callback to the list of callbacks
+ * to be called when the buffer's I/O completes. If it is not set
+ * already, set the buffer's b_iodone() routine to be
+ * xfs_buf_iodone_callbacks() and link the log item into the list of
+ * items rooted at b_fsprivate. Items are always added as the second
+ * entry in the list if there is a first, because the buf item code
+ * assumes that the buf log item is first.
+ */
+void
+xfs_buf_attach_iodone(
+ xfs_buf_t *bp,
+ void (*cb)(xfs_buf_t *, xfs_log_item_t *),
+ xfs_log_item_t *lip)
+{
+ xfs_log_item_t *head_lip;
+
+ ASSERT(XFS_BUF_ISBUSY(bp));
+ ASSERT(XFS_BUF_VALUSEMA(bp) <= 0);
+
+ lip->li_cb = cb;
+ if (XFS_BUF_FSPRIVATE(bp, void *) != NULL) {
+ head_lip = XFS_BUF_FSPRIVATE(bp, xfs_log_item_t *);
+ lip->li_bio_list = head_lip->li_bio_list;
+ head_lip->li_bio_list = lip;
+ } else {
+ XFS_BUF_SET_FSPRIVATE(bp, lip);
+ }
+
+ ASSERT((XFS_BUF_IODONE_FUNC(bp) == xfs_buf_iodone_callbacks) ||
+ (XFS_BUF_IODONE_FUNC(bp) == NULL));
+ XFS_BUF_SET_IODONE_FUNC(bp, xfs_buf_iodone_callbacks);
+}
+
+STATIC void
+xfs_buf_do_callbacks(
+ xfs_buf_t *bp,
+ xfs_log_item_t *lip)
+{
+ xfs_log_item_t *nlip;
+
+ while (lip != NULL) {
+ nlip = lip->li_bio_list;
+ ASSERT(lip->li_cb != NULL);
+ /*
+ * Clear the next pointer so we don't have any
+ * confusion if the item is added to another buf.
+ * Don't touch the log item after calling its
+ * callback, because it could have freed itself.
+ */
+ lip->li_bio_list = NULL;
+ lip->li_cb(bp, lip);
+ lip = nlip;
+ }
+}
+
+/*
+ * This is the iodone() function for buffers which have had callbacks
+ * attached to them by xfs_buf_attach_iodone(). It should remove each
+ * log item from the buffer's list and call the callback of each in turn.
+ * When done, the buffer's fsprivate field is set to NULL and the buffer
+ * is unlocked with a call to iodone().
+ */
+void
+xfs_buf_iodone_callbacks(
+ xfs_buf_t *bp)
+{
+ xfs_log_item_t *lip;
+ static ulong lasttime;
+ static xfs_buftarg_t *lasttarg;
+ xfs_mount_t *mp;
+
+ ASSERT(XFS_BUF_FSPRIVATE(bp, void *) != NULL);
+ lip = XFS_BUF_FSPRIVATE(bp, xfs_log_item_t *);
+
+ if (XFS_BUF_GETERROR(bp) != 0) {
+ /*
+ * If we've already decided to shutdown the filesystem
+ * because of IO errors, there's no point in giving this
+ * a retry.
+ */
+ mp = lip->li_mountp;
+ if (XFS_FORCED_SHUTDOWN(mp)) {
+ ASSERT(XFS_BUF_TARGET(bp) == mp->m_ddev_targp);
+ XFS_BUF_SUPER_STALE(bp);
+ xfs_buftrace("BUF_IODONE_CB", bp);
+ xfs_buf_do_callbacks(bp, lip);
+ XFS_BUF_SET_FSPRIVATE(bp, NULL);
+ XFS_BUF_CLR_IODONE_FUNC(bp);
+
+ /*
+ * XFS_SHUT flag gets set when we go thru the
+ * entire buffer cache and deliberately start
+ * throwing away delayed write buffers.
+ * Since there's no biowait done on those,
+ * we should just brelse them.
+ */
+ if (XFS_BUF_ISSHUT(bp)) {
+ XFS_BUF_UNSHUT(bp);
+ xfs_buf_relse(bp);
+ } else {
+ xfs_biodone(bp);
+ }
+
+ return;
+ }
+
+ if ((XFS_BUF_TARGET(bp) != lasttarg) ||
+ (time_after(jiffies, (lasttime + 5*HZ)))) {
+ lasttime = jiffies;
+ prdev("XFS write error in file system meta-data "
+ "block 0x%llx in %s",
+ XFS_BUF_TARGET(bp),
+ (__uint64_t)XFS_BUF_ADDR(bp), mp->m_fsname);
+ }
+ lasttarg = XFS_BUF_TARGET(bp);
+
+ if (XFS_BUF_ISASYNC(bp)) {
+ /*
+ * If the write was asynchronous then noone will be
+ * looking for the error. Clear the error state
+ * and write the buffer out again delayed write.
+ *
+ * XXXsup This is OK, so long as we catch these
+ * before we start the umount; we don't want these
+ * DELWRI metadata bufs to be hanging around.
+ */
+ XFS_BUF_ERROR(bp,0); /* errno of 0 unsets the flag */
+
+ if (!(XFS_BUF_ISSTALE(bp))) {
+ XFS_BUF_DELAYWRITE(bp);
+ XFS_BUF_DONE(bp);
+ XFS_BUF_SET_START(bp);
+ }
+ ASSERT(XFS_BUF_IODONE_FUNC(bp));
+ xfs_buftrace("BUF_IODONE ASYNC", bp);
+ xfs_buf_relse(bp);
+ } else {
+ /*
+ * If the write of the buffer was not asynchronous,
+ * then we want to make sure to return the error
+ * to the caller of bwrite(). Because of this we
+ * cannot clear the B_ERROR state at this point.
+ * Instead we install a callback function that
+ * will be called when the buffer is released, and
+ * that routine will clear the error state and
+ * set the buffer to be written out again after
+ * some delay.
+ */
+ /* We actually overwrite the existing b-relse
+ function at times, but we're gonna be shutting down
+ anyway. */
+ XFS_BUF_SET_BRELSE_FUNC(bp,xfs_buf_error_relse);
+ XFS_BUF_DONE(bp);
+ XFS_BUF_V_IODONESEMA(bp);
+ }
+ return;
+ }
+#ifdef XFSERRORDEBUG
+ xfs_buftrace("XFS BUFCB NOERR", bp);
+#endif
+ xfs_buf_do_callbacks(bp, lip);
+ XFS_BUF_SET_FSPRIVATE(bp, NULL);
+ XFS_BUF_CLR_IODONE_FUNC(bp);
+ xfs_biodone(bp);
+}
+
+/*
+ * This is a callback routine attached to a buffer which gets an error
+ * when being written out synchronously.
+ */
+STATIC void
+xfs_buf_error_relse(
+ xfs_buf_t *bp)
+{
+ xfs_log_item_t *lip;
+ xfs_mount_t *mp;
+
+ lip = XFS_BUF_FSPRIVATE(bp, xfs_log_item_t *);
+ mp = (xfs_mount_t *)lip->li_mountp;
+ ASSERT(XFS_BUF_TARGET(bp) == mp->m_ddev_targp);
+
+ XFS_BUF_STALE(bp);
+ XFS_BUF_DONE(bp);
+ XFS_BUF_UNDELAYWRITE(bp);
+ XFS_BUF_ERROR(bp,0);
+ xfs_buftrace("BUF_ERROR_RELSE", bp);
+ if (! XFS_FORCED_SHUTDOWN(mp))
+ xfs_force_shutdown(mp, XFS_METADATA_IO_ERROR);
+ /*
+ * We have to unpin the pinned buffers so do the
+ * callbacks.
+ */
+ xfs_buf_do_callbacks(bp, lip);
+ XFS_BUF_SET_FSPRIVATE(bp, NULL);
+ XFS_BUF_CLR_IODONE_FUNC(bp);
+ XFS_BUF_SET_BRELSE_FUNC(bp,NULL);
+ xfs_buf_relse(bp);
+}
+
+
+/*
+ * This is the iodone() function for buffers which have been
+ * logged. It is called when they are eventually flushed out.
+ * It should remove the buf item from the AIL, and free the buf item.
+ * It is called by xfs_buf_iodone_callbacks() above which will take
+ * care of cleaning up the buffer itself.
+ */
+/* ARGSUSED */
+void
+xfs_buf_iodone(
+ xfs_buf_t *bp,
+ xfs_buf_log_item_t *bip)
+{
+ struct xfs_mount *mp;
+ SPLDECL(s);
+
+ ASSERT(bip->bli_buf == bp);
+
+ mp = bip->bli_item.li_mountp;
+
+ /*
+ * If we are forcibly shutting down, this may well be
+ * off the AIL already. That's because we simulate the
+ * log-committed callbacks to unpin these buffers. Or we may never
+ * have put this item on AIL because of the transaction was
+ * aborted forcibly. xfs_trans_delete_ail() takes care of these.
+ *
+ * Either way, AIL is useless if we're forcing a shutdown.
+ */
+ AIL_LOCK(mp,s);
+ /*
+ * xfs_trans_delete_ail() drops the AIL lock.
+ */
+ xfs_trans_delete_ail(mp, (xfs_log_item_t *)bip, s);
+
+#ifdef XFS_TRANS_DEBUG
+ kmem_free(bip->bli_orig, XFS_BUF_COUNT(bp));
+ bip->bli_orig = NULL;
+ kmem_free(bip->bli_logged, XFS_BUF_COUNT(bp) / NBBY);
+ bip->bli_logged = NULL;
+#endif /* XFS_TRANS_DEBUG */
+
+#ifdef XFS_BLI_TRACE
+ ktrace_free(bip->bli_trace);
+#endif
+ kmem_zone_free(xfs_buf_item_zone, bip);
+}
+
+#if defined(XFS_BLI_TRACE)
+void
+xfs_buf_item_trace(
+ char *id,
+ xfs_buf_log_item_t *bip)
+{
+ xfs_buf_t *bp;
+ ASSERT(bip->bli_trace != NULL);
+
+ bp = bip->bli_buf;
+ ktrace_enter(bip->bli_trace,
+ (void *)id,
+ (void *)bip->bli_buf,
+ (void *)((unsigned long)bip->bli_flags),
+ (void *)((unsigned long)bip->bli_recur),
+ (void *)((unsigned long)atomic_read(&bip->bli_refcount)),
+ (void *)((unsigned long)
+ (0xFFFFFFFF & XFS_BUF_ADDR(bp) >> 32)),
+ (void *)((unsigned long)(0xFFFFFFFF & XFS_BUF_ADDR(bp))),
+ (void *)((unsigned long)XFS_BUF_COUNT(bp)),
+ (void *)((unsigned long)XFS_BUF_BFLAGS(bp)),
+ XFS_BUF_FSPRIVATE(bp, void *),
+ XFS_BUF_FSPRIVATE2(bp, void *),
+ (void *)(unsigned long)XFS_BUF_ISPINNED(bp),
+ (void *)XFS_BUF_IODONE_FUNC(bp),
+ (void *)((unsigned long)(XFS_BUF_VALUSEMA(bp))),
+ (void *)bip->bli_item.li_desc,
+ (void *)((unsigned long)bip->bli_item.li_flags));
+}
+#endif /* XFS_BLI_TRACE */
diff --git a/fs/xfs/xfs_buf_item.h b/fs/xfs/xfs_buf_item.h
new file mode 100644
index 00000000000000..5f1b0c9308f672
--- /dev/null
+++ b/fs/xfs/xfs_buf_item.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2000-2001 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_BUF_ITEM_H__
+#define __XFS_BUF_ITEM_H__
+
+/*
+ * This is the structure used to lay out a buf log item in the
+ * log. The data map describes which 128 byte chunks of the buffer
+ * have been logged. This structure works only on buffers that
+ * reside up to the first TB in the filesystem. These buffers are
+ * generated only by pre-6.2 systems and are known as XFS_LI_6_1_BUF.
+ */
+typedef struct xfs_buf_log_format_v1 {
+ unsigned short blf_type; /* buf log item type indicator */
+ unsigned short blf_size; /* size of this item */
+ __int32_t blf_blkno; /* starting blkno of this buf */
+ ushort blf_flags; /* misc state */
+ ushort blf_len; /* number of blocks in this buf */
+ unsigned int blf_map_size; /* size of data bitmap in words */
+ unsigned int blf_data_map[1];/* variable size bitmap of */
+ /* regions of buffer in this item */
+} xfs_buf_log_format_v1_t;
+
+/*
+ * This is a form of the above structure with a 64 bit blkno field.
+ * For 6.2 and beyond, this is XFS_LI_BUF. We use this to log everything.
+ */
+typedef struct xfs_buf_log_format_t {
+ unsigned short blf_type; /* buf log item type indicator */
+ unsigned short blf_size; /* size of this item */
+ ushort blf_flags; /* misc state */
+ ushort blf_len; /* number of blocks in this buf */
+ __int64_t blf_blkno; /* starting blkno of this buf */
+ unsigned int blf_map_size; /* size of data bitmap in words */
+ unsigned int blf_data_map[1];/* variable size bitmap of */
+ /* regions of buffer in this item */
+} xfs_buf_log_format_t;
+
+/*
+ * This flag indicates that the buffer contains on disk inodes
+ * and requires special recovery handling.
+ */
+#define XFS_BLI_INODE_BUF 0x1
+/*
+ * This flag indicates that the buffer should not be replayed
+ * during recovery because its blocks are being freed.
+ */
+#define XFS_BLI_CANCEL 0x2
+/*
+ * This flag indicates that the buffer contains on disk
+ * user or group dquots and may require special recovery handling.
+ */
+#define XFS_BLI_UDQUOT_BUF 0x4
+/* #define XFS_BLI_PDQUOT_BUF 0x8 */
+#define XFS_BLI_GDQUOT_BUF 0x10
+
+#define XFS_BLI_CHUNK 128
+#define XFS_BLI_SHIFT 7
+#define BIT_TO_WORD_SHIFT 5
+#define NBWORD (NBBY * sizeof(unsigned int))
+
+/*
+ * buf log item flags
+ */
+#define XFS_BLI_HOLD 0x01
+#define XFS_BLI_DIRTY 0x02
+#define XFS_BLI_STALE 0x04
+#define XFS_BLI_LOGGED 0x08
+#define XFS_BLI_INODE_ALLOC_BUF 0x10
+#define XFS_BLI_STALE_INODE 0x20
+
+
+#ifdef __KERNEL__
+
+struct xfs_buf;
+struct ktrace;
+struct xfs_mount;
+struct xfs_buf_log_item;
+
+#if defined(XFS_BLI_TRACE)
+#define XFS_BLI_TRACE_SIZE 32
+
+void xfs_buf_item_trace(char *, struct xfs_buf_log_item *);
+#else
+#define xfs_buf_item_trace(id, bip)
+#endif
+
+/*
+ * This is the in core log item structure used to track information
+ * needed to log buffers. It tracks how many times the lock has been
+ * locked, and which 128 byte chunks of the buffer are dirty.
+ */
+typedef struct xfs_buf_log_item {
+ xfs_log_item_t bli_item; /* common item structure */
+ struct xfs_buf *bli_buf; /* real buffer pointer */
+ unsigned int bli_flags; /* misc flags */
+ unsigned int bli_recur; /* lock recursion count */
+ atomic_t bli_refcount; /* cnt of tp refs */
+#ifdef XFS_BLI_TRACE
+ struct ktrace *bli_trace; /* event trace buf */
+#endif
+#ifdef XFS_TRANS_DEBUG
+ char *bli_orig; /* original buffer copy */
+ char *bli_logged; /* bytes logged (bitmap) */
+#endif
+ xfs_buf_log_format_t bli_format; /* in-log header */
+} xfs_buf_log_item_t;
+
+/*
+ * This structure is used during recovery to record the buf log
+ * items which have been canceled and should not be replayed.
+ */
+typedef struct xfs_buf_cancel {
+ xfs_daddr_t bc_blkno;
+ uint bc_len;
+ int bc_refcount;
+ struct xfs_buf_cancel *bc_next;
+} xfs_buf_cancel_t;
+
+void xfs_buf_item_init(struct xfs_buf *, struct xfs_mount *);
+void xfs_buf_item_relse(struct xfs_buf *);
+void xfs_buf_item_log(xfs_buf_log_item_t *, uint, uint);
+uint xfs_buf_item_dirty(xfs_buf_log_item_t *);
+void xfs_buf_attach_iodone(struct xfs_buf *,
+ void(*)(struct xfs_buf *, xfs_log_item_t *),
+ xfs_log_item_t *);
+void xfs_buf_iodone_callbacks(struct xfs_buf *);
+void xfs_buf_iodone(struct xfs_buf *, xfs_buf_log_item_t *);
+
+#ifdef XFS_TRANS_DEBUG
+void
+xfs_buf_item_flush_log_debug(
+ struct xfs_buf *bp,
+ uint first,
+ uint last);
+#else
+#define xfs_buf_item_flush_log_debug(bp, first, last)
+#endif
+
+#endif /* __KERNEL__ */
+
+#endif /* __XFS_BUF_ITEM_H__ */
diff --git a/fs/xfs/xfs_cap.h b/fs/xfs/xfs_cap.h
new file mode 100644
index 00000000000000..2deac730375844
--- /dev/null
+++ b/fs/xfs/xfs_cap.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_CAP_H__
+#define __XFS_CAP_H__
+
+/*
+ * Capabilities
+ */
+typedef __uint64_t xfs_cap_value_t;
+
+typedef struct xfs_cap_set {
+ xfs_cap_value_t cap_effective; /* use in capability checks */
+ xfs_cap_value_t cap_permitted; /* combined with file attrs */
+ xfs_cap_value_t cap_inheritable;/* pass through exec */
+} xfs_cap_set_t;
+
+/* On-disk XFS extended attribute names */
+#define SGI_CAP_FILE "SGI_CAP_FILE"
+#define SGI_CAP_FILE_SIZE (sizeof(SGI_CAP_FILE)-1)
+#define SGI_CAP_LINUX "SGI_CAP_LINUX"
+#define SGI_CAP_LINUX_SIZE (sizeof(SGI_CAP_LINUX)-1)
+
+/*
+ * For Linux, we take the bitfields directly from capability.h
+ * and no longer attempt to keep this attribute ondisk compatible
+ * with IRIX. Since this attribute is only set on exectuables,
+ * it just doesn't make much sense to try. We do use a different
+ * named attribute though, to avoid confusion.
+ */
+
+#ifdef __KERNEL__
+
+#ifdef CONFIG_FS_POSIX_CAP
+
+#include <linux/posix_cap_xattr.h>
+
+struct vnode;
+
+extern int xfs_cap_vhascap(struct vnode *);
+extern int xfs_cap_vset(struct vnode *, void *, size_t);
+extern int xfs_cap_vget(struct vnode *, void *, size_t);
+extern int xfs_cap_vremove(struct vnode *vp);
+
+#define _CAP_EXISTS xfs_cap_vhascap
+
+#else
+#define xfs_cap_vset(v,p,sz) (-EOPNOTSUPP)
+#define xfs_cap_vget(v,p,sz) (-EOPNOTSUPP)
+#define xfs_cap_vremove(v) (-EOPNOTSUPP)
+#define _CAP_EXISTS (NULL)
+#endif
+
+#endif /* __KERNEL__ */
+
+#endif /* __XFS_CAP_H__ */
diff --git a/fs/xfs/xfs_clnt.h b/fs/xfs/xfs_clnt.h
new file mode 100644
index 00000000000000..4a49cde9b857d7
--- /dev/null
+++ b/fs/xfs/xfs_clnt.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_CLNT_H__
+#define __XFS_CLNT_H__
+
+/*
+ * XFS arguments structure, constructed from the arguments we
+ * are passed via the mount system call.
+ *
+ * NOTE: The mount system call is handled differently between
+ * Linux and IRIX. In IRIX we worked work with a binary data
+ * structure coming in across the syscall interface from user
+ * space (the mount userspace knows about each filesystem type
+ * and the set of valid options for it, and converts the users
+ * argument string into a binary structure _before_ making the
+ * system call), and the ABI issues that this implies.
+ *
+ * In Linux, we are passed a comma separated set of options;
+ * ie. a NULL terminated string of characters. Userspace mount
+ * code does not have any knowledge of mount options expected by
+ * each filesystem type and so each filesystem parses its mount
+ * options in kernel space.
+ *
+ * For the Linux port, we kept this structure pretty much intact
+ * and use it internally (because the existing code groks it).
+ */
+struct xfs_mount_args {
+ int flags; /* flags -> see XFSMNT_... macros below */
+ int logbufs; /* Number of log buffers, -1 to default */
+ int logbufsize; /* Size of log buffers, -1 to default */
+ char fsname[MAXNAMELEN+1]; /* data device name */
+ char rtname[MAXNAMELEN+1]; /* realtime device filename */
+ char logname[MAXNAMELEN+1]; /* journal device filename */
+ char mtpt[MAXNAMELEN+1]; /* filesystem mount point */
+ int sunit; /* stripe unit (BBs) */
+ int swidth; /* stripe width (BBs), multiple of sunit */
+ uchar_t iosizelog; /* log2 of the preferred I/O size */
+};
+
+/*
+ * XFS mount option flags
+ */
+#define XFSMNT_CHKLOG 0x00000001 /* check log */
+#define XFSMNT_WSYNC 0x00000002 /* safe mode nfs mount
+ * compatible */
+#define XFSMNT_INO64 0x00000004 /* move inode numbers up
+ * past 2^32 */
+#define XFSMNT_UQUOTA 0x00000008 /* user quota accounting */
+#define XFSMNT_PQUOTA 0x00000010 /* IRIX prj quota accounting */
+#define XFSMNT_UQUOTAENF 0x00000020 /* user quota limit
+ * enforcement */
+#define XFSMNT_PQUOTAENF 0x00000040 /* IRIX project quota limit
+ * enforcement */
+#define XFSMNT_NOATIME 0x00000100 /* don't modify access
+ * times on reads */
+#define XFSMNT_NOALIGN 0x00000200 /* don't allocate at
+ * stripe boundaries*/
+#define XFSMNT_RETERR 0x00000400 /* return error to user */
+#define XFSMNT_NORECOVERY 0x00000800 /* no recovery, implies
+ * read-only mount */
+#define XFSMNT_SHARED 0x00001000 /* shared XFS mount */
+#define XFSMNT_IOSIZE 0x00002000 /* optimize for I/O size */
+#define XFSMNT_OSYNCISOSYNC 0x00004000 /* o_sync is REALLY o_sync */
+ /* (osyncisdsync is now default) */
+#define XFSMNT_32BITINODES 0x00200000 /* restrict inodes to 32
+ * bits of address space */
+#define XFSMNT_GQUOTA 0x00400000 /* group quota accounting */
+#define XFSMNT_GQUOTAENF 0x00800000 /* group quota limit
+ * enforcement */
+#define XFSMNT_NOUUID 0x01000000 /* Ignore fs uuid */
+#define XFSMNT_DMAPI 0x02000000 /* enable dmapi/xdsm */
+#define XFSMNT_NOLOGFLUSH 0x04000000 /* Don't flush for log blocks */
+#define XFSMNT_IDELETE 0x08000000 /* inode cluster delete */
+#define XFSMNT_SWALLOC 0x10000000 /* turn on stripe width
+ * allocation */
+
+#endif /* __XFS_CLNT_H__ */
diff --git a/fs/xfs/xfs_da_btree.c b/fs/xfs/xfs_da_btree.c
new file mode 100644
index 00000000000000..bbe00dc550d656
--- /dev/null
+++ b/fs/xfs/xfs_da_btree.c
@@ -0,0 +1,2650 @@
+/*
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_alloc.h"
+#include "xfs_btree.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode_item.h"
+#include "xfs_inode.h"
+#include "xfs_bmap.h"
+#include "xfs_da_btree.h"
+#include "xfs_attr.h"
+#include "xfs_attr_leaf.h"
+#include "xfs_dir_leaf.h"
+#include "xfs_dir2_data.h"
+#include "xfs_dir2_leaf.h"
+#include "xfs_dir2_block.h"
+#include "xfs_dir2_node.h"
+#include "xfs_error.h"
+#include "xfs_bit.h"
+
+/*
+ * xfs_da_btree.c
+ *
+ * Routines to implement directories as Btrees of hashed names.
+ */
+
+/*========================================================================
+ * Function prototypes for the kernel.
+ *========================================================================*/
+
+/*
+ * Routines used for growing the Btree.
+ */
+STATIC int xfs_da_root_split(xfs_da_state_t *state,
+ xfs_da_state_blk_t *existing_root,
+ xfs_da_state_blk_t *new_child);
+STATIC int xfs_da_node_split(xfs_da_state_t *state,
+ xfs_da_state_blk_t *existing_blk,
+ xfs_da_state_blk_t *split_blk,
+ xfs_da_state_blk_t *blk_to_add,
+ int treelevel,
+ int *result);
+STATIC void xfs_da_node_rebalance(xfs_da_state_t *state,
+ xfs_da_state_blk_t *node_blk_1,
+ xfs_da_state_blk_t *node_blk_2);
+STATIC void xfs_da_node_add(xfs_da_state_t *state,
+ xfs_da_state_blk_t *old_node_blk,
+ xfs_da_state_blk_t *new_node_blk);
+
+/*
+ * Routines used for shrinking the Btree.
+ */
+STATIC int xfs_da_root_join(xfs_da_state_t *state,
+ xfs_da_state_blk_t *root_blk);
+STATIC int xfs_da_node_toosmall(xfs_da_state_t *state, int *retval);
+STATIC void xfs_da_node_remove(xfs_da_state_t *state,
+ xfs_da_state_blk_t *drop_blk);
+STATIC void xfs_da_node_unbalance(xfs_da_state_t *state,
+ xfs_da_state_blk_t *src_node_blk,
+ xfs_da_state_blk_t *dst_node_blk);
+
+/*
+ * Utility routines.
+ */
+STATIC uint xfs_da_node_lasthash(xfs_dabuf_t *bp, int *count);
+STATIC int xfs_da_node_order(xfs_dabuf_t *node1_bp, xfs_dabuf_t *node2_bp);
+STATIC xfs_dabuf_t *xfs_da_buf_make(int nbuf, xfs_buf_t **bps, inst_t *ra);
+
+
+/*========================================================================
+ * Routines used for growing the Btree.
+ *========================================================================*/
+
+/*
+ * Create the initial contents of an intermediate node.
+ */
+int
+xfs_da_node_create(xfs_da_args_t *args, xfs_dablk_t blkno, int level,
+ xfs_dabuf_t **bpp, int whichfork)
+{
+ xfs_da_intnode_t *node;
+ xfs_dabuf_t *bp;
+ int error;
+ xfs_trans_t *tp;
+
+ tp = args->trans;
+ error = xfs_da_get_buf(tp, args->dp, blkno, -1, &bp, whichfork);
+ if (error)
+ return(error);
+ ASSERT(bp != NULL);
+ node = bp->data;
+ INT_ZERO(node->hdr.info.forw, ARCH_CONVERT);
+ INT_ZERO(node->hdr.info.back, ARCH_CONVERT);
+ INT_SET(node->hdr.info.magic, ARCH_CONVERT, XFS_DA_NODE_MAGIC);
+ INT_ZERO(node->hdr.info.pad, ARCH_CONVERT);
+ INT_ZERO(node->hdr.count, ARCH_CONVERT);
+ INT_SET(node->hdr.level, ARCH_CONVERT, level);
+
+ xfs_da_log_buf(tp, bp,
+ XFS_DA_LOGRANGE(node, &node->hdr, sizeof(node->hdr)));
+
+ *bpp = bp;
+ return(0);
+}
+
+/*
+ * Split a leaf node, rebalance, then possibly split
+ * intermediate nodes, rebalance, etc.
+ */
+int /* error */
+xfs_da_split(xfs_da_state_t *state)
+{
+ xfs_da_state_blk_t *oldblk, *newblk, *addblk;
+ xfs_da_intnode_t *node;
+ xfs_dabuf_t *bp;
+ int max, action, error, i;
+
+ /*
+ * Walk back up the tree splitting/inserting/adjusting as necessary.
+ * If we need to insert and there isn't room, split the node, then
+ * decide which fragment to insert the new block from below into.
+ * Note that we may split the root this way, but we need more fixup.
+ */
+ max = state->path.active - 1;
+ ASSERT((max >= 0) && (max < XFS_DA_NODE_MAXDEPTH));
+ ASSERT(state->path.blk[max].magic == XFS_ATTR_LEAF_MAGIC ||
+ state->path.blk[max].magic == XFS_DIRX_LEAF_MAGIC(state->mp));
+
+ addblk = &state->path.blk[max]; /* initial dummy value */
+ for (i = max; (i >= 0) && addblk; state->path.active--, i--) {
+ oldblk = &state->path.blk[i];
+ newblk = &state->altpath.blk[i];
+
+ /*
+ * If a leaf node then
+ * Allocate a new leaf node, then rebalance across them.
+ * else if an intermediate node then
+ * We split on the last layer, must we split the node?
+ */
+ switch (oldblk->magic) {
+ case XFS_ATTR_LEAF_MAGIC:
+#ifndef __KERNEL__
+ return(ENOTTY);
+#else
+ error = xfs_attr_leaf_split(state, oldblk, newblk);
+ if ((error != 0) && (error != ENOSPC)) {
+ return(error); /* GROT: attr is inconsistent */
+ }
+ if (!error) {
+ addblk = newblk;
+ break;
+ }
+ /*
+ * Entry wouldn't fit, split the leaf again.
+ */
+ state->extravalid = 1;
+ if (state->inleaf) {
+ state->extraafter = 0; /* before newblk */
+ error = xfs_attr_leaf_split(state, oldblk,
+ &state->extrablk);
+ } else {
+ state->extraafter = 1; /* after newblk */
+ error = xfs_attr_leaf_split(state, newblk,
+ &state->extrablk);
+ }
+ if (error)
+ return(error); /* GROT: attr inconsistent */
+ addblk = newblk;
+ break;
+#endif
+ case XFS_DIR_LEAF_MAGIC:
+ ASSERT(XFS_DIR_IS_V1(state->mp));
+ error = xfs_dir_leaf_split(state, oldblk, newblk);
+ if ((error != 0) && (error != ENOSPC)) {
+ return(error); /* GROT: dir is inconsistent */
+ }
+ if (!error) {
+ addblk = newblk;
+ break;
+ }
+ /*
+ * Entry wouldn't fit, split the leaf again.
+ */
+ state->extravalid = 1;
+ if (state->inleaf) {
+ state->extraafter = 0; /* before newblk */
+ error = xfs_dir_leaf_split(state, oldblk,
+ &state->extrablk);
+ if (error)
+ return(error); /* GROT: dir incon. */
+ addblk = newblk;
+ } else {
+ state->extraafter = 1; /* after newblk */
+ error = xfs_dir_leaf_split(state, newblk,
+ &state->extrablk);
+ if (error)
+ return(error); /* GROT: dir incon. */
+ addblk = newblk;
+ }
+ break;
+ case XFS_DIR2_LEAFN_MAGIC:
+ ASSERT(XFS_DIR_IS_V2(state->mp));
+ error = xfs_dir2_leafn_split(state, oldblk, newblk);
+ if (error)
+ return error;
+ addblk = newblk;
+ break;
+ case XFS_DA_NODE_MAGIC:
+ error = xfs_da_node_split(state, oldblk, newblk, addblk,
+ max - i, &action);
+ xfs_da_buf_done(addblk->bp);
+ addblk->bp = NULL;
+ if (error)
+ return(error); /* GROT: dir is inconsistent */
+ /*
+ * Record the newly split block for the next time thru?
+ */
+ if (action)
+ addblk = newblk;
+ else
+ addblk = NULL;
+ break;
+ }
+
+ /*
+ * Update the btree to show the new hashval for this child.
+ */
+ xfs_da_fixhashpath(state, &state->path);
+ /*
+ * If we won't need this block again, it's getting dropped
+ * from the active path by the loop control, so we need
+ * to mark it done now.
+ */
+ if (i > 0 || !addblk)
+ xfs_da_buf_done(oldblk->bp);
+ }
+ if (!addblk)
+ return(0);
+
+ /*
+ * Split the root node.
+ */
+ ASSERT(state->path.active == 0);
+ oldblk = &state->path.blk[0];
+ error = xfs_da_root_split(state, oldblk, addblk);
+ if (error) {
+ xfs_da_buf_done(oldblk->bp);
+ xfs_da_buf_done(addblk->bp);
+ addblk->bp = NULL;
+ return(error); /* GROT: dir is inconsistent */
+ }
+
+ /*
+ * Update pointers to the node which used to be block 0 and
+ * just got bumped because of the addition of a new root node.
+ * There might be three blocks involved if a double split occurred,
+ * and the original block 0 could be at any position in the list.
+ */
+
+ node = oldblk->bp->data;
+ if (!INT_ISZERO(node->hdr.info.forw, ARCH_CONVERT)) {
+ if (INT_GET(node->hdr.info.forw, ARCH_CONVERT) == addblk->blkno) {
+ bp = addblk->bp;
+ } else {
+ ASSERT(state->extravalid);
+ bp = state->extrablk.bp;
+ }
+ node = bp->data;
+ INT_SET(node->hdr.info.back, ARCH_CONVERT, oldblk->blkno);
+ xfs_da_log_buf(state->args->trans, bp,
+ XFS_DA_LOGRANGE(node, &node->hdr.info,
+ sizeof(node->hdr.info)));
+ }
+ node = oldblk->bp->data;
+ if (INT_GET(node->hdr.info.back, ARCH_CONVERT)) {
+ if (INT_GET(node->hdr.info.back, ARCH_CONVERT) == addblk->blkno) {
+ bp = addblk->bp;
+ } else {
+ ASSERT(state->extravalid);
+ bp = state->extrablk.bp;
+ }
+ node = bp->data;
+ INT_SET(node->hdr.info.forw, ARCH_CONVERT, oldblk->blkno);
+ xfs_da_log_buf(state->args->trans, bp,
+ XFS_DA_LOGRANGE(node, &node->hdr.info,
+ sizeof(node->hdr.info)));
+ }
+ xfs_da_buf_done(oldblk->bp);
+ xfs_da_buf_done(addblk->bp);
+ addblk->bp = NULL;
+ return(0);
+}
+
+/*
+ * Split the root. We have to create a new root and point to the two
+ * parts (the split old root) that we just created. Copy block zero to
+ * the EOF, extending the inode in process.
+ */
+STATIC int /* error */
+xfs_da_root_split(xfs_da_state_t *state, xfs_da_state_blk_t *blk1,
+ xfs_da_state_blk_t *blk2)
+{
+ xfs_da_intnode_t *node, *oldroot;
+ xfs_da_args_t *args;
+ xfs_dablk_t blkno;
+ xfs_dabuf_t *bp;
+ int error, size;
+ xfs_inode_t *dp;
+ xfs_trans_t *tp;
+ xfs_mount_t *mp;
+ xfs_dir2_leaf_t *leaf;
+
+ /*
+ * Copy the existing (incorrect) block from the root node position
+ * to a free space somewhere.
+ */
+ args = state->args;
+ ASSERT(args != NULL);
+ error = xfs_da_grow_inode(args, &blkno);
+ if (error)
+ return(error);
+ dp = args->dp;
+ tp = args->trans;
+ mp = state->mp;
+ error = xfs_da_get_buf(tp, dp, blkno, -1, &bp, args->whichfork);
+ if (error)
+ return(error);
+ ASSERT(bp != NULL);
+ node = bp->data;
+ oldroot = blk1->bp->data;
+ if (INT_GET(oldroot->hdr.info.magic, ARCH_CONVERT) == XFS_DA_NODE_MAGIC) {
+ size = (int)((char *)&oldroot->btree[INT_GET(oldroot->hdr.count, ARCH_CONVERT)] -
+ (char *)oldroot);
+ } else {
+ ASSERT(XFS_DIR_IS_V2(mp));
+ ASSERT(INT_GET(oldroot->hdr.info.magic, ARCH_CONVERT) == XFS_DIR2_LEAFN_MAGIC);
+ leaf = (xfs_dir2_leaf_t *)oldroot;
+ size = (int)((char *)&leaf->ents[INT_GET(leaf->hdr.count, ARCH_CONVERT)] -
+ (char *)leaf);
+ }
+ memcpy(node, oldroot, size);
+ xfs_da_log_buf(tp, bp, 0, size - 1);
+ xfs_da_buf_done(blk1->bp);
+ blk1->bp = bp;
+ blk1->blkno = blkno;
+
+ /*
+ * Set up the new root node.
+ */
+ error = xfs_da_node_create(args,
+ args->whichfork == XFS_DATA_FORK &&
+ XFS_DIR_IS_V2(mp) ? mp->m_dirleafblk : 0,
+ INT_GET(node->hdr.level, ARCH_CONVERT) + 1, &bp, args->whichfork);
+ if (error)
+ return(error);
+ node = bp->data;
+ INT_SET(node->btree[0].hashval, ARCH_CONVERT, blk1->hashval);
+ INT_SET(node->btree[0].before, ARCH_CONVERT, blk1->blkno);
+ INT_SET(node->btree[1].hashval, ARCH_CONVERT, blk2->hashval);
+ INT_SET(node->btree[1].before, ARCH_CONVERT, blk2->blkno);
+ INT_SET(node->hdr.count, ARCH_CONVERT, 2);
+
+#ifdef DEBUG
+ if (INT_GET(oldroot->hdr.info.magic, ARCH_CONVERT) == XFS_DIR2_LEAFN_MAGIC) {
+ ASSERT(blk1->blkno >= mp->m_dirleafblk &&
+ blk1->blkno < mp->m_dirfreeblk);
+ ASSERT(blk2->blkno >= mp->m_dirleafblk &&
+ blk2->blkno < mp->m_dirfreeblk);
+ }
+#endif
+
+ /* Header is already logged by xfs_da_node_create */
+ xfs_da_log_buf(tp, bp,
+ XFS_DA_LOGRANGE(node, node->btree,
+ sizeof(xfs_da_node_entry_t) * 2));
+ xfs_da_buf_done(bp);
+
+ return(0);
+}
+
+/*
+ * Split the node, rebalance, then add the new entry.
+ */
+STATIC int /* error */
+xfs_da_node_split(xfs_da_state_t *state, xfs_da_state_blk_t *oldblk,
+ xfs_da_state_blk_t *newblk,
+ xfs_da_state_blk_t *addblk,
+ int treelevel, int *result)
+{
+ xfs_da_intnode_t *node;
+ xfs_dablk_t blkno;
+ int newcount, error;
+ int useextra;
+
+ node = oldblk->bp->data;
+ ASSERT(INT_GET(node->hdr.info.magic, ARCH_CONVERT) == XFS_DA_NODE_MAGIC);
+
+ /*
+ * With V2 the extra block is data or freespace.
+ */
+ useextra = state->extravalid && XFS_DIR_IS_V1(state->mp);
+ newcount = 1 + useextra;
+ /*
+ * Do we have to split the node?
+ */
+ if ((INT_GET(node->hdr.count, ARCH_CONVERT) + newcount) > state->node_ents) {
+ /*
+ * Allocate a new node, add to the doubly linked chain of
+ * nodes, then move some of our excess entries into it.
+ */
+ error = xfs_da_grow_inode(state->args, &blkno);
+ if (error)
+ return(error); /* GROT: dir is inconsistent */
+
+ error = xfs_da_node_create(state->args, blkno, treelevel,
+ &newblk->bp, state->args->whichfork);
+ if (error)
+ return(error); /* GROT: dir is inconsistent */
+ newblk->blkno = blkno;
+ newblk->magic = XFS_DA_NODE_MAGIC;
+ xfs_da_node_rebalance(state, oldblk, newblk);
+ error = xfs_da_blk_link(state, oldblk, newblk);
+ if (error)
+ return(error);
+ *result = 1;
+ } else {
+ *result = 0;
+ }
+
+ /*
+ * Insert the new entry(s) into the correct block
+ * (updating last hashval in the process).
+ *
+ * xfs_da_node_add() inserts BEFORE the given index,
+ * and as a result of using node_lookup_int() we always
+ * point to a valid entry (not after one), but a split
+ * operation always results in a new block whose hashvals
+ * FOLLOW the current block.
+ *
+ * If we had double-split op below us, then add the extra block too.
+ */
+ node = oldblk->bp->data;
+ if (oldblk->index <= INT_GET(node->hdr.count, ARCH_CONVERT)) {
+ oldblk->index++;
+ xfs_da_node_add(state, oldblk, addblk);
+ if (useextra) {
+ if (state->extraafter)
+ oldblk->index++;
+ xfs_da_node_add(state, oldblk, &state->extrablk);
+ state->extravalid = 0;
+ }
+ } else {
+ newblk->index++;
+ xfs_da_node_add(state, newblk, addblk);
+ if (useextra) {
+ if (state->extraafter)
+ newblk->index++;
+ xfs_da_node_add(state, newblk, &state->extrablk);
+ state->extravalid = 0;
+ }
+ }
+
+ return(0);
+}
+
+/*
+ * Balance the btree elements between two intermediate nodes,
+ * usually one full and one empty.
+ *
+ * NOTE: if blk2 is empty, then it will get the upper half of blk1.
+ */
+STATIC void
+xfs_da_node_rebalance(xfs_da_state_t *state, xfs_da_state_blk_t *blk1,
+ xfs_da_state_blk_t *blk2)
+{
+ xfs_da_intnode_t *node1, *node2, *tmpnode;
+ xfs_da_node_entry_t *btree_s, *btree_d;
+ int count, tmp;
+ xfs_trans_t *tp;
+
+ node1 = blk1->bp->data;
+ node2 = blk2->bp->data;
+ /*
+ * Figure out how many entries need to move, and in which direction.
+ * Swap the nodes around if that makes it simpler.
+ */
+ if ((INT_GET(node1->hdr.count, ARCH_CONVERT) > 0) && (INT_GET(node2->hdr.count, ARCH_CONVERT) > 0) &&
+ ((INT_GET(node2->btree[ 0 ].hashval, ARCH_CONVERT) < INT_GET(node1->btree[ 0 ].hashval, ARCH_CONVERT)) ||
+ (INT_GET(node2->btree[ INT_GET(node2->hdr.count, ARCH_CONVERT)-1 ].hashval, ARCH_CONVERT) <
+ INT_GET(node1->btree[ INT_GET(node1->hdr.count, ARCH_CONVERT)-1 ].hashval, ARCH_CONVERT)))) {
+ tmpnode = node1;
+ node1 = node2;
+ node2 = tmpnode;
+ }
+ ASSERT(INT_GET(node1->hdr.info.magic, ARCH_CONVERT) == XFS_DA_NODE_MAGIC);
+ ASSERT(INT_GET(node2->hdr.info.magic, ARCH_CONVERT) == XFS_DA_NODE_MAGIC);
+ count = (INT_GET(node1->hdr.count, ARCH_CONVERT) - INT_GET(node2->hdr.count, ARCH_CONVERT)) / 2;
+ if (count == 0)
+ return;
+ tp = state->args->trans;
+ /*
+ * Two cases: high-to-low and low-to-high.
+ */
+ if (count > 0) {
+ /*
+ * Move elements in node2 up to make a hole.
+ */
+ if ((tmp = INT_GET(node2->hdr.count, ARCH_CONVERT)) > 0) {
+ tmp *= (uint)sizeof(xfs_da_node_entry_t);
+ btree_s = &node2->btree[0];
+ btree_d = &node2->btree[count];
+ memmove(btree_d, btree_s, tmp);
+ }
+
+ /*
+ * Move the req'd B-tree elements from high in node1 to
+ * low in node2.
+ */
+ INT_MOD(node2->hdr.count, ARCH_CONVERT, count);
+ tmp = count * (uint)sizeof(xfs_da_node_entry_t);
+ btree_s = &node1->btree[INT_GET(node1->hdr.count, ARCH_CONVERT) - count];
+ btree_d = &node2->btree[0];
+ memcpy(btree_d, btree_s, tmp);
+ INT_MOD(node1->hdr.count, ARCH_CONVERT, -(count));
+
+ } else {
+ /*
+ * Move the req'd B-tree elements from low in node2 to
+ * high in node1.
+ */
+ count = -count;
+ tmp = count * (uint)sizeof(xfs_da_node_entry_t);
+ btree_s = &node2->btree[0];
+ btree_d = &node1->btree[INT_GET(node1->hdr.count, ARCH_CONVERT)];
+ memcpy(btree_d, btree_s, tmp);
+ INT_MOD(node1->hdr.count, ARCH_CONVERT, count);
+ xfs_da_log_buf(tp, blk1->bp,
+ XFS_DA_LOGRANGE(node1, btree_d, tmp));
+
+ /*
+ * Move elements in node2 down to fill the hole.
+ */
+ tmp = INT_GET(node2->hdr.count, ARCH_CONVERT) - count;
+ tmp *= (uint)sizeof(xfs_da_node_entry_t);
+ btree_s = &node2->btree[count];
+ btree_d = &node2->btree[0];
+ memmove(btree_d, btree_s, tmp);
+ INT_MOD(node2->hdr.count, ARCH_CONVERT, -(count));
+ }
+
+ /*
+ * Log header of node 1 and all current bits of node 2.
+ */
+ xfs_da_log_buf(tp, blk1->bp,
+ XFS_DA_LOGRANGE(node1, &node1->hdr, sizeof(node1->hdr)));
+ xfs_da_log_buf(tp, blk2->bp,
+ XFS_DA_LOGRANGE(node2, &node2->hdr,
+ sizeof(node2->hdr) +
+ sizeof(node2->btree[0]) * INT_GET(node2->hdr.count, ARCH_CONVERT)));
+
+ /*
+ * Record the last hashval from each block for upward propagation.
+ * (note: don't use the swapped node pointers)
+ */
+ node1 = blk1->bp->data;
+ node2 = blk2->bp->data;
+ blk1->hashval = INT_GET(node1->btree[ INT_GET(node1->hdr.count, ARCH_CONVERT)-1 ].hashval, ARCH_CONVERT);
+ blk2->hashval = INT_GET(node2->btree[ INT_GET(node2->hdr.count, ARCH_CONVERT)-1 ].hashval, ARCH_CONVERT);
+
+ /*
+ * Adjust the expected index for insertion.
+ */
+ if (blk1->index >= INT_GET(node1->hdr.count, ARCH_CONVERT)) {
+ blk2->index = blk1->index - INT_GET(node1->hdr.count, ARCH_CONVERT);
+ blk1->index = INT_GET(node1->hdr.count, ARCH_CONVERT) + 1; /* make it invalid */
+ }
+}
+
+/*
+ * Add a new entry to an intermediate node.
+ */
+STATIC void
+xfs_da_node_add(xfs_da_state_t *state, xfs_da_state_blk_t *oldblk,
+ xfs_da_state_blk_t *newblk)
+{
+ xfs_da_intnode_t *node;
+ xfs_da_node_entry_t *btree;
+ int tmp;
+ xfs_mount_t *mp;
+
+ node = oldblk->bp->data;
+ mp = state->mp;
+ ASSERT(INT_GET(node->hdr.info.magic, ARCH_CONVERT) == XFS_DA_NODE_MAGIC);
+ ASSERT((oldblk->index >= 0) && (oldblk->index <= INT_GET(node->hdr.count, ARCH_CONVERT)));
+ ASSERT(newblk->blkno != 0);
+ if (state->args->whichfork == XFS_DATA_FORK && XFS_DIR_IS_V2(mp))
+ ASSERT(newblk->blkno >= mp->m_dirleafblk &&
+ newblk->blkno < mp->m_dirfreeblk);
+
+ /*
+ * We may need to make some room before we insert the new node.
+ */
+ tmp = 0;
+ btree = &node->btree[ oldblk->index ];
+ if (oldblk->index < INT_GET(node->hdr.count, ARCH_CONVERT)) {
+ tmp = (INT_GET(node->hdr.count, ARCH_CONVERT) - oldblk->index) * (uint)sizeof(*btree);
+ memmove(btree + 1, btree, tmp);
+ }
+ INT_SET(btree->hashval, ARCH_CONVERT, newblk->hashval);
+ INT_SET(btree->before, ARCH_CONVERT, newblk->blkno);
+ xfs_da_log_buf(state->args->trans, oldblk->bp,
+ XFS_DA_LOGRANGE(node, btree, tmp + sizeof(*btree)));
+ INT_MOD(node->hdr.count, ARCH_CONVERT, +1);
+ xfs_da_log_buf(state->args->trans, oldblk->bp,
+ XFS_DA_LOGRANGE(node, &node->hdr, sizeof(node->hdr)));
+
+ /*
+ * Copy the last hash value from the oldblk to propagate upwards.
+ */
+ oldblk->hashval = INT_GET(node->btree[ INT_GET(node->hdr.count, ARCH_CONVERT)-1 ].hashval, ARCH_CONVERT);
+}
+
+/*========================================================================
+ * Routines used for shrinking the Btree.
+ *========================================================================*/
+
+/*
+ * Deallocate an empty leaf node, remove it from its parent,
+ * possibly deallocating that block, etc...
+ */
+int
+xfs_da_join(xfs_da_state_t *state)
+{
+ xfs_da_state_blk_t *drop_blk, *save_blk;
+ int action, error;
+
+ action = 0;
+ drop_blk = &state->path.blk[ state->path.active-1 ];
+ save_blk = &state->altpath.blk[ state->path.active-1 ];
+ ASSERT(state->path.blk[0].magic == XFS_DA_NODE_MAGIC);
+ ASSERT(drop_blk->magic == XFS_ATTR_LEAF_MAGIC ||
+ drop_blk->magic == XFS_DIRX_LEAF_MAGIC(state->mp));
+
+ /*
+ * Walk back up the tree joining/deallocating as necessary.
+ * When we stop dropping blocks, break out.
+ */
+ for ( ; state->path.active >= 2; drop_blk--, save_blk--,
+ state->path.active--) {
+ /*
+ * See if we can combine the block with a neighbor.
+ * (action == 0) => no options, just leave
+ * (action == 1) => coalesce, then unlink
+ * (action == 2) => block empty, unlink it
+ */
+ switch (drop_blk->magic) {
+ case XFS_ATTR_LEAF_MAGIC:
+#ifndef __KERNEL__
+ error = ENOTTY;
+#else
+ error = xfs_attr_leaf_toosmall(state, &action);
+#endif
+ if (error)
+ return(error);
+ if (action == 0)
+ return(0);
+#ifdef __KERNEL__
+ xfs_attr_leaf_unbalance(state, drop_blk, save_blk);
+#endif
+ break;
+ case XFS_DIR_LEAF_MAGIC:
+ ASSERT(XFS_DIR_IS_V1(state->mp));
+ error = xfs_dir_leaf_toosmall(state, &action);
+ if (error)
+ return(error);
+ if (action == 0)
+ return(0);
+ xfs_dir_leaf_unbalance(state, drop_blk, save_blk);
+ break;
+ case XFS_DIR2_LEAFN_MAGIC:
+ ASSERT(XFS_DIR_IS_V2(state->mp));
+ error = xfs_dir2_leafn_toosmall(state, &action);
+ if (error)
+ return error;
+ if (action == 0)
+ return 0;
+ xfs_dir2_leafn_unbalance(state, drop_blk, save_blk);
+ break;
+ case XFS_DA_NODE_MAGIC:
+ /*
+ * Remove the offending node, fixup hashvals,
+ * check for a toosmall neighbor.
+ */
+ xfs_da_node_remove(state, drop_blk);
+ xfs_da_fixhashpath(state, &state->path);
+ error = xfs_da_node_toosmall(state, &action);
+ if (error)
+ return(error);
+ if (action == 0)
+ return 0;
+ xfs_da_node_unbalance(state, drop_blk, save_blk);
+ break;
+ }
+ xfs_da_fixhashpath(state, &state->altpath);
+ error = xfs_da_blk_unlink(state, drop_blk, save_blk);
+ xfs_da_state_kill_altpath(state);
+ if (error)
+ return(error);
+ error = xfs_da_shrink_inode(state->args, drop_blk->blkno,
+ drop_blk->bp);
+ drop_blk->bp = NULL;
+ if (error)
+ return(error);
+ }
+ /*
+ * We joined all the way to the top. If it turns out that
+ * we only have one entry in the root, make the child block
+ * the new root.
+ */
+ xfs_da_node_remove(state, drop_blk);
+ xfs_da_fixhashpath(state, &state->path);
+ error = xfs_da_root_join(state, &state->path.blk[0]);
+ return(error);
+}
+
+/*
+ * We have only one entry in the root. Copy the only remaining child of
+ * the old root to block 0 as the new root node.
+ */
+STATIC int
+xfs_da_root_join(xfs_da_state_t *state, xfs_da_state_blk_t *root_blk)
+{
+ xfs_da_intnode_t *oldroot;
+ /* REFERENCED */
+ xfs_da_blkinfo_t *blkinfo;
+ xfs_da_args_t *args;
+ xfs_dablk_t child;
+ xfs_dabuf_t *bp;
+ int error;
+
+ args = state->args;
+ ASSERT(args != NULL);
+ ASSERT(root_blk->magic == XFS_DA_NODE_MAGIC);
+ oldroot = root_blk->bp->data;
+ ASSERT(INT_GET(oldroot->hdr.info.magic, ARCH_CONVERT) == XFS_DA_NODE_MAGIC);
+ ASSERT(INT_ISZERO(oldroot->hdr.info.forw, ARCH_CONVERT));
+ ASSERT(INT_ISZERO(oldroot->hdr.info.back, ARCH_CONVERT));
+
+ /*
+ * If the root has more than one child, then don't do anything.
+ */
+ if (INT_GET(oldroot->hdr.count, ARCH_CONVERT) > 1)
+ return(0);
+
+ /*
+ * Read in the (only) child block, then copy those bytes into
+ * the root block's buffer and free the original child block.
+ */
+ child = INT_GET(oldroot->btree[ 0 ].before, ARCH_CONVERT);
+ ASSERT(child != 0);
+ error = xfs_da_read_buf(args->trans, args->dp, child, -1, &bp,
+ args->whichfork);
+ if (error)
+ return(error);
+ ASSERT(bp != NULL);
+ blkinfo = bp->data;
+ if (INT_GET(oldroot->hdr.level, ARCH_CONVERT) == 1) {
+ ASSERT(INT_GET(blkinfo->magic, ARCH_CONVERT) == XFS_DIRX_LEAF_MAGIC(state->mp) ||
+ INT_GET(blkinfo->magic, ARCH_CONVERT) == XFS_ATTR_LEAF_MAGIC);
+ } else {
+ ASSERT(INT_GET(blkinfo->magic, ARCH_CONVERT) == XFS_DA_NODE_MAGIC);
+ }
+ ASSERT(INT_ISZERO(blkinfo->forw, ARCH_CONVERT));
+ ASSERT(INT_ISZERO(blkinfo->back, ARCH_CONVERT));
+ memcpy(root_blk->bp->data, bp->data, state->blocksize);
+ xfs_da_log_buf(args->trans, root_blk->bp, 0, state->blocksize - 1);
+ error = xfs_da_shrink_inode(args, child, bp);
+ return(error);
+}
+
+/*
+ * Check a node block and its neighbors to see if the block should be
+ * collapsed into one or the other neighbor. Always keep the block
+ * with the smaller block number.
+ * If the current block is over 50% full, don't try to join it, return 0.
+ * If the block is empty, fill in the state structure and return 2.
+ * If it can be collapsed, fill in the state structure and return 1.
+ * If nothing can be done, return 0.
+ */
+STATIC int
+xfs_da_node_toosmall(xfs_da_state_t *state, int *action)
+{
+ xfs_da_intnode_t *node;
+ xfs_da_state_blk_t *blk;
+ xfs_da_blkinfo_t *info;
+ int count, forward, error, retval, i;
+ xfs_dablk_t blkno;
+ xfs_dabuf_t *bp;
+
+ /*
+ * Check for the degenerate case of the block being over 50% full.
+ * If so, it's not worth even looking to see if we might be able
+ * to coalesce with a sibling.
+ */
+ blk = &state->path.blk[ state->path.active-1 ];
+ info = blk->bp->data;
+ ASSERT(INT_GET(info->magic, ARCH_CONVERT) == XFS_DA_NODE_MAGIC);
+ node = (xfs_da_intnode_t *)info;
+ count = INT_GET(node->hdr.count, ARCH_CONVERT);
+ if (count > (state->node_ents >> 1)) {
+ *action = 0; /* blk over 50%, don't try to join */
+ return(0); /* blk over 50%, don't try to join */
+ }
+
+ /*
+ * Check for the degenerate case of the block being empty.
+ * If the block is empty, we'll simply delete it, no need to
+ * coalesce it with a sibling block. We choose (aribtrarily)
+ * to merge with the forward block unless it is NULL.
+ */
+ if (count == 0) {
+ /*
+ * Make altpath point to the block we want to keep and
+ * path point to the block we want to drop (this one).
+ */
+ forward = (!INT_ISZERO(info->forw, ARCH_CONVERT));
+ memcpy(&state->altpath, &state->path, sizeof(state->path));
+ error = xfs_da_path_shift(state, &state->altpath, forward,
+ 0, &retval);
+ if (error)
+ return(error);
+ if (retval) {
+ *action = 0;
+ } else {
+ *action = 2;
+ }
+ return(0);
+ }
+
+ /*
+ * Examine each sibling block to see if we can coalesce with
+ * at least 25% free space to spare. We need to figure out
+ * whether to merge with the forward or the backward block.
+ * We prefer coalescing with the lower numbered sibling so as
+ * to shrink a directory over time.
+ */
+ /* start with smaller blk num */
+ forward = (INT_GET(info->forw, ARCH_CONVERT)
+ < INT_GET(info->back, ARCH_CONVERT));
+ for (i = 0; i < 2; forward = !forward, i++) {
+ if (forward)
+ blkno = INT_GET(info->forw, ARCH_CONVERT);
+ else
+ blkno = INT_GET(info->back, ARCH_CONVERT);
+ if (blkno == 0)
+ continue;
+ error = xfs_da_read_buf(state->args->trans, state->args->dp,
+ blkno, -1, &bp, state->args->whichfork);
+ if (error)
+ return(error);
+ ASSERT(bp != NULL);
+
+ node = (xfs_da_intnode_t *)info;
+ count = state->node_ents;
+ count -= state->node_ents >> 2;
+ count -= INT_GET(node->hdr.count, ARCH_CONVERT);
+ node = bp->data;
+ ASSERT(INT_GET(node->hdr.info.magic, ARCH_CONVERT) == XFS_DA_NODE_MAGIC);
+ count -= INT_GET(node->hdr.count, ARCH_CONVERT);
+ xfs_da_brelse(state->args->trans, bp);
+ if (count >= 0)
+ break; /* fits with at least 25% to spare */
+ }
+ if (i >= 2) {
+ *action = 0;
+ return(0);
+ }
+
+ /*
+ * Make altpath point to the block we want to keep (the lower
+ * numbered block) and path point to the block we want to drop.
+ */
+ memcpy(&state->altpath, &state->path, sizeof(state->path));
+ if (blkno < blk->blkno) {
+ error = xfs_da_path_shift(state, &state->altpath, forward,
+ 0, &retval);
+ if (error) {
+ return(error);
+ }
+ if (retval) {
+ *action = 0;
+ return(0);
+ }
+ } else {
+ error = xfs_da_path_shift(state, &state->path, forward,
+ 0, &retval);
+ if (error) {
+ return(error);
+ }
+ if (retval) {
+ *action = 0;
+ return(0);
+ }
+ }
+ *action = 1;
+ return(0);
+}
+
+/*
+ * Walk back up the tree adjusting hash values as necessary,
+ * when we stop making changes, return.
+ */
+void
+xfs_da_fixhashpath(xfs_da_state_t *state, xfs_da_state_path_t *path)
+{
+ xfs_da_state_blk_t *blk;
+ xfs_da_intnode_t *node;
+ xfs_da_node_entry_t *btree;
+ xfs_dahash_t lasthash=0;
+ int level, count;
+
+ level = path->active-1;
+ blk = &path->blk[ level ];
+ switch (blk->magic) {
+#ifdef __KERNEL__
+ case XFS_ATTR_LEAF_MAGIC:
+ lasthash = xfs_attr_leaf_lasthash(blk->bp, &count);
+ if (count == 0)
+ return;
+ break;
+#endif
+ case XFS_DIR_LEAF_MAGIC:
+ ASSERT(XFS_DIR_IS_V1(state->mp));
+ lasthash = xfs_dir_leaf_lasthash(blk->bp, &count);
+ if (count == 0)
+ return;
+ break;
+ case XFS_DIR2_LEAFN_MAGIC:
+ ASSERT(XFS_DIR_IS_V2(state->mp));
+ lasthash = xfs_dir2_leafn_lasthash(blk->bp, &count);
+ if (count == 0)
+ return;
+ break;
+ case XFS_DA_NODE_MAGIC:
+ lasthash = xfs_da_node_lasthash(blk->bp, &count);
+ if (count == 0)
+ return;
+ break;
+ }
+ for (blk--, level--; level >= 0; blk--, level--) {
+ node = blk->bp->data;
+ ASSERT(INT_GET(node->hdr.info.magic, ARCH_CONVERT) == XFS_DA_NODE_MAGIC);
+ btree = &node->btree[ blk->index ];
+ if (INT_GET(btree->hashval, ARCH_CONVERT) == lasthash)
+ break;
+ blk->hashval = lasthash;
+ INT_SET(btree->hashval, ARCH_CONVERT, lasthash);
+ xfs_da_log_buf(state->args->trans, blk->bp,
+ XFS_DA_LOGRANGE(node, btree, sizeof(*btree)));
+
+ lasthash = INT_GET(node->btree[ INT_GET(node->hdr.count, ARCH_CONVERT)-1 ].hashval, ARCH_CONVERT);
+ }
+}
+
+/*
+ * Remove an entry from an intermediate node.
+ */
+STATIC void
+xfs_da_node_remove(xfs_da_state_t *state, xfs_da_state_blk_t *drop_blk)
+{
+ xfs_da_intnode_t *node;
+ xfs_da_node_entry_t *btree;
+ int tmp;
+
+ node = drop_blk->bp->data;
+ ASSERT(drop_blk->index < INT_GET(node->hdr.count, ARCH_CONVERT));
+ ASSERT(drop_blk->index >= 0);
+
+ /*
+ * Copy over the offending entry, or just zero it out.
+ */
+ btree = &node->btree[drop_blk->index];
+ if (drop_blk->index < (INT_GET(node->hdr.count, ARCH_CONVERT)-1)) {
+ tmp = INT_GET(node->hdr.count, ARCH_CONVERT) - drop_blk->index - 1;
+ tmp *= (uint)sizeof(xfs_da_node_entry_t);
+ memmove(btree, btree + 1, tmp);
+ xfs_da_log_buf(state->args->trans, drop_blk->bp,
+ XFS_DA_LOGRANGE(node, btree, tmp));
+ btree = &node->btree[ INT_GET(node->hdr.count, ARCH_CONVERT)-1 ];
+ }
+ memset((char *)btree, 0, sizeof(xfs_da_node_entry_t));
+ xfs_da_log_buf(state->args->trans, drop_blk->bp,
+ XFS_DA_LOGRANGE(node, btree, sizeof(*btree)));
+ INT_MOD(node->hdr.count, ARCH_CONVERT, -1);
+ xfs_da_log_buf(state->args->trans, drop_blk->bp,
+ XFS_DA_LOGRANGE(node, &node->hdr, sizeof(node->hdr)));
+
+ /*
+ * Copy the last hash value from the block to propagate upwards.
+ */
+ btree--;
+ drop_blk->hashval = INT_GET(btree->hashval, ARCH_CONVERT);
+}
+
+/*
+ * Unbalance the btree elements between two intermediate nodes,
+ * move all Btree elements from one node into another.
+ */
+STATIC void
+xfs_da_node_unbalance(xfs_da_state_t *state, xfs_da_state_blk_t *drop_blk,
+ xfs_da_state_blk_t *save_blk)
+{
+ xfs_da_intnode_t *drop_node, *save_node;
+ xfs_da_node_entry_t *btree;
+ int tmp;
+ xfs_trans_t *tp;
+
+ drop_node = drop_blk->bp->data;
+ save_node = save_blk->bp->data;
+ ASSERT(INT_GET(drop_node->hdr.info.magic, ARCH_CONVERT) == XFS_DA_NODE_MAGIC);
+ ASSERT(INT_GET(save_node->hdr.info.magic, ARCH_CONVERT) == XFS_DA_NODE_MAGIC);
+ tp = state->args->trans;
+
+ /*
+ * If the dying block has lower hashvals, then move all the
+ * elements in the remaining block up to make a hole.
+ */
+ if ((INT_GET(drop_node->btree[ 0 ].hashval, ARCH_CONVERT) < INT_GET(save_node->btree[ 0 ].hashval, ARCH_CONVERT)) ||
+ (INT_GET(drop_node->btree[ INT_GET(drop_node->hdr.count, ARCH_CONVERT)-1 ].hashval, ARCH_CONVERT) <
+ INT_GET(save_node->btree[ INT_GET(save_node->hdr.count, ARCH_CONVERT)-1 ].hashval, ARCH_CONVERT)))
+ {
+ btree = &save_node->btree[ INT_GET(drop_node->hdr.count, ARCH_CONVERT) ];
+ tmp = INT_GET(save_node->hdr.count, ARCH_CONVERT) * (uint)sizeof(xfs_da_node_entry_t);
+ memmove(btree, &save_node->btree[0], tmp);
+ btree = &save_node->btree[0];
+ xfs_da_log_buf(tp, save_blk->bp,
+ XFS_DA_LOGRANGE(save_node, btree,
+ (INT_GET(save_node->hdr.count, ARCH_CONVERT) + INT_GET(drop_node->hdr.count, ARCH_CONVERT)) *
+ sizeof(xfs_da_node_entry_t)));
+ } else {
+ btree = &save_node->btree[ INT_GET(save_node->hdr.count, ARCH_CONVERT) ];
+ xfs_da_log_buf(tp, save_blk->bp,
+ XFS_DA_LOGRANGE(save_node, btree,
+ INT_GET(drop_node->hdr.count, ARCH_CONVERT) *
+ sizeof(xfs_da_node_entry_t)));
+ }
+
+ /*
+ * Move all the B-tree elements from drop_blk to save_blk.
+ */
+ tmp = INT_GET(drop_node->hdr.count, ARCH_CONVERT) * (uint)sizeof(xfs_da_node_entry_t);
+ memcpy(btree, &drop_node->btree[0], tmp);
+ INT_MOD(save_node->hdr.count, ARCH_CONVERT, INT_GET(drop_node->hdr.count, ARCH_CONVERT));
+
+ xfs_da_log_buf(tp, save_blk->bp,
+ XFS_DA_LOGRANGE(save_node, &save_node->hdr,
+ sizeof(save_node->hdr)));
+
+ /*
+ * Save the last hashval in the remaining block for upward propagation.
+ */
+ save_blk->hashval = INT_GET(save_node->btree[ INT_GET(save_node->hdr.count, ARCH_CONVERT)-1 ].hashval, ARCH_CONVERT);
+}
+
+/*========================================================================
+ * Routines used for finding things in the Btree.
+ *========================================================================*/
+
+/*
+ * Walk down the Btree looking for a particular filename, filling
+ * in the state structure as we go.
+ *
+ * We will set the state structure to point to each of the elements
+ * in each of the nodes where either the hashval is or should be.
+ *
+ * We support duplicate hashval's so for each entry in the current
+ * node that could contain the desired hashval, descend. This is a
+ * pruned depth-first tree search.
+ */
+int /* error */
+xfs_da_node_lookup_int(xfs_da_state_t *state, int *result)
+{
+ xfs_da_state_blk_t *blk;
+ xfs_da_blkinfo_t *curr;
+ xfs_da_intnode_t *node;
+ xfs_da_node_entry_t *btree;
+ xfs_dablk_t blkno;
+ int probe, span, max, error, retval;
+ xfs_dahash_t hashval;
+ xfs_da_args_t *args;
+
+ args = state->args;
+
+ /*
+ * Descend thru the B-tree searching each level for the right
+ * node to use, until the right hashval is found.
+ */
+ if (args->whichfork == XFS_DATA_FORK && XFS_DIR_IS_V2(state->mp))
+ blkno = state->mp->m_dirleafblk;
+ else
+ blkno = 0;
+ for (blk = &state->path.blk[0], state->path.active = 1;
+ state->path.active <= XFS_DA_NODE_MAXDEPTH;
+ blk++, state->path.active++) {
+ /*
+ * Read the next node down in the tree.
+ */
+ blk->blkno = blkno;
+ error = xfs_da_read_buf(args->trans, args->dp, blkno,
+ -1, &blk->bp, args->whichfork);
+ if (error) {
+ blk->blkno = 0;
+ state->path.active--;
+ return(error);
+ }
+ curr = blk->bp->data;
+ ASSERT(INT_GET(curr->magic, ARCH_CONVERT) == XFS_DA_NODE_MAGIC ||
+ INT_GET(curr->magic, ARCH_CONVERT) == XFS_DIRX_LEAF_MAGIC(state->mp) ||
+ INT_GET(curr->magic, ARCH_CONVERT) == XFS_ATTR_LEAF_MAGIC);
+
+ /*
+ * Search an intermediate node for a match.
+ */
+ blk->magic = INT_GET(curr->magic, ARCH_CONVERT);
+ if (INT_GET(curr->magic, ARCH_CONVERT) == XFS_DA_NODE_MAGIC) {
+ node = blk->bp->data;
+ blk->hashval = INT_GET(node->btree[ INT_GET(node->hdr.count, ARCH_CONVERT)-1 ].hashval, ARCH_CONVERT);
+
+ /*
+ * Binary search. (note: small blocks will skip loop)
+ */
+ max = INT_GET(node->hdr.count, ARCH_CONVERT);
+ probe = span = max / 2;
+ hashval = args->hashval;
+ for (btree = &node->btree[probe]; span > 4;
+ btree = &node->btree[probe]) {
+ span /= 2;
+ if (INT_GET(btree->hashval, ARCH_CONVERT) < hashval)
+ probe += span;
+ else if (INT_GET(btree->hashval, ARCH_CONVERT) > hashval)
+ probe -= span;
+ else
+ break;
+ }
+ ASSERT((probe >= 0) && (probe < max));
+ ASSERT((span <= 4) || (INT_GET(btree->hashval, ARCH_CONVERT) == hashval));
+
+ /*
+ * Since we may have duplicate hashval's, find the first
+ * matching hashval in the node.
+ */
+ while ((probe > 0) && (INT_GET(btree->hashval, ARCH_CONVERT) >= hashval)) {
+ btree--;
+ probe--;
+ }
+ while ((probe < max) && (INT_GET(btree->hashval, ARCH_CONVERT) < hashval)) {
+ btree++;
+ probe++;
+ }
+
+ /*
+ * Pick the right block to descend on.
+ */
+ if (probe == max) {
+ blk->index = max-1;
+ blkno = INT_GET(node->btree[ max-1 ].before, ARCH_CONVERT);
+ } else {
+ blk->index = probe;
+ blkno = INT_GET(btree->before, ARCH_CONVERT);
+ }
+ }
+#ifdef __KERNEL__
+ else if (INT_GET(curr->magic, ARCH_CONVERT) == XFS_ATTR_LEAF_MAGIC) {
+ blk->hashval = xfs_attr_leaf_lasthash(blk->bp, NULL);
+ break;
+ }
+#endif
+ else if (INT_GET(curr->magic, ARCH_CONVERT) == XFS_DIR_LEAF_MAGIC) {
+ blk->hashval = xfs_dir_leaf_lasthash(blk->bp, NULL);
+ break;
+ }
+ else if (INT_GET(curr->magic, ARCH_CONVERT) == XFS_DIR2_LEAFN_MAGIC) {
+ blk->hashval = xfs_dir2_leafn_lasthash(blk->bp, NULL);
+ break;
+ }
+ }
+
+ /*
+ * A leaf block that ends in the hashval that we are interested in
+ * (final hashval == search hashval) means that the next block may
+ * contain more entries with the same hashval, shift upward to the
+ * next leaf and keep searching.
+ */
+ for (;;) {
+ if (blk->magic == XFS_DIR_LEAF_MAGIC) {
+ ASSERT(XFS_DIR_IS_V1(state->mp));
+ retval = xfs_dir_leaf_lookup_int(blk->bp, args,
+ &blk->index);
+ } else if (blk->magic == XFS_DIR2_LEAFN_MAGIC) {
+ ASSERT(XFS_DIR_IS_V2(state->mp));
+ retval = xfs_dir2_leafn_lookup_int(blk->bp, args,
+ &blk->index, state);
+ }
+#ifdef __KERNEL__
+ else if (blk->magic == XFS_ATTR_LEAF_MAGIC) {
+ retval = xfs_attr_leaf_lookup_int(blk->bp, args);
+ blk->index = args->index;
+ args->blkno = blk->blkno;
+ }
+#endif
+ if (((retval == ENOENT) || (retval == ENOATTR)) &&
+ (blk->hashval == args->hashval)) {
+ error = xfs_da_path_shift(state, &state->path, 1, 1,
+ &retval);
+ if (error)
+ return(error);
+ if (retval == 0) {
+ continue;
+ }
+#ifdef __KERNEL__
+ else if (blk->magic == XFS_ATTR_LEAF_MAGIC) {
+ /* path_shift() gives ENOENT */
+ retval = XFS_ERROR(ENOATTR);
+ }
+#endif
+ }
+ break;
+ }
+ *result = retval;
+ return(0);
+}
+
+/*========================================================================
+ * Utility routines.
+ *========================================================================*/
+
+/*
+ * Link a new block into a doubly linked list of blocks (of whatever type).
+ */
+int /* error */
+xfs_da_blk_link(xfs_da_state_t *state, xfs_da_state_blk_t *old_blk,
+ xfs_da_state_blk_t *new_blk)
+{
+ xfs_da_blkinfo_t *old_info, *new_info, *tmp_info;
+ xfs_da_args_t *args;
+ int before=0, error;
+ xfs_dabuf_t *bp;
+
+ /*
+ * Set up environment.
+ */
+ args = state->args;
+ ASSERT(args != NULL);
+ old_info = old_blk->bp->data;
+ new_info = new_blk->bp->data;
+ ASSERT(old_blk->magic == XFS_DA_NODE_MAGIC ||
+ old_blk->magic == XFS_DIRX_LEAF_MAGIC(state->mp) ||
+ old_blk->magic == XFS_ATTR_LEAF_MAGIC);
+ ASSERT(old_blk->magic == INT_GET(old_info->magic, ARCH_CONVERT));
+ ASSERT(new_blk->magic == INT_GET(new_info->magic, ARCH_CONVERT));
+ ASSERT(old_blk->magic == new_blk->magic);
+
+ switch (old_blk->magic) {
+#ifdef __KERNEL__
+ case XFS_ATTR_LEAF_MAGIC:
+ before = xfs_attr_leaf_order(old_blk->bp, new_blk->bp);
+ break;
+#endif
+ case XFS_DIR_LEAF_MAGIC:
+ ASSERT(XFS_DIR_IS_V1(state->mp));
+ before = xfs_dir_leaf_order(old_blk->bp, new_blk->bp);
+ break;
+ case XFS_DIR2_LEAFN_MAGIC:
+ ASSERT(XFS_DIR_IS_V2(state->mp));
+ before = xfs_dir2_leafn_order(old_blk->bp, new_blk->bp);
+ break;
+ case XFS_DA_NODE_MAGIC:
+ before = xfs_da_node_order(old_blk->bp, new_blk->bp);
+ break;
+ }
+
+ /*
+ * Link blocks in appropriate order.
+ */
+ if (before) {
+ /*
+ * Link new block in before existing block.
+ */
+ INT_SET(new_info->forw, ARCH_CONVERT, old_blk->blkno);
+ new_info->back = old_info->back; /* INT_: direct copy */
+ if (INT_GET(old_info->back, ARCH_CONVERT)) {
+ error = xfs_da_read_buf(args->trans, args->dp,
+ INT_GET(old_info->back,
+ ARCH_CONVERT), -1, &bp,
+ args->whichfork);
+ if (error)
+ return(error);
+ ASSERT(bp != NULL);
+ tmp_info = bp->data;
+ ASSERT(INT_GET(tmp_info->magic, ARCH_CONVERT) == INT_GET(old_info->magic, ARCH_CONVERT));
+ ASSERT(INT_GET(tmp_info->forw, ARCH_CONVERT) == old_blk->blkno);
+ INT_SET(tmp_info->forw, ARCH_CONVERT, new_blk->blkno);
+ xfs_da_log_buf(args->trans, bp, 0, sizeof(*tmp_info)-1);
+ xfs_da_buf_done(bp);
+ }
+ INT_SET(old_info->back, ARCH_CONVERT, new_blk->blkno);
+ } else {
+ /*
+ * Link new block in after existing block.
+ */
+ new_info->forw = old_info->forw; /* INT_: direct copy */
+ INT_SET(new_info->back, ARCH_CONVERT, old_blk->blkno);
+ if (INT_GET(old_info->forw, ARCH_CONVERT)) {
+ error = xfs_da_read_buf(args->trans, args->dp,
+ INT_GET(old_info->forw, ARCH_CONVERT), -1, &bp,
+ args->whichfork);
+ if (error)
+ return(error);
+ ASSERT(bp != NULL);
+ tmp_info = bp->data;
+ ASSERT(INT_GET(tmp_info->magic, ARCH_CONVERT)
+ == INT_GET(old_info->magic, ARCH_CONVERT));
+ ASSERT(INT_GET(tmp_info->back, ARCH_CONVERT)
+ == old_blk->blkno);
+ INT_SET(tmp_info->back, ARCH_CONVERT, new_blk->blkno);
+ xfs_da_log_buf(args->trans, bp, 0, sizeof(*tmp_info)-1);
+ xfs_da_buf_done(bp);
+ }
+ INT_SET(old_info->forw, ARCH_CONVERT, new_blk->blkno);
+ }
+
+ xfs_da_log_buf(args->trans, old_blk->bp, 0, sizeof(*tmp_info) - 1);
+ xfs_da_log_buf(args->trans, new_blk->bp, 0, sizeof(*tmp_info) - 1);
+ return(0);
+}
+
+/*
+ * Compare two intermediate nodes for "order".
+ */
+STATIC int
+xfs_da_node_order(xfs_dabuf_t *node1_bp, xfs_dabuf_t *node2_bp)
+{
+ xfs_da_intnode_t *node1, *node2;
+
+ node1 = node1_bp->data;
+ node2 = node2_bp->data;
+ ASSERT((INT_GET(node1->hdr.info.magic, ARCH_CONVERT) == XFS_DA_NODE_MAGIC) &&
+ (INT_GET(node2->hdr.info.magic, ARCH_CONVERT) == XFS_DA_NODE_MAGIC));
+ if ((INT_GET(node1->hdr.count, ARCH_CONVERT) > 0) && (INT_GET(node2->hdr.count, ARCH_CONVERT) > 0) &&
+ ((INT_GET(node2->btree[ 0 ].hashval, ARCH_CONVERT) <
+ INT_GET(node1->btree[ 0 ].hashval, ARCH_CONVERT)) ||
+ (INT_GET(node2->btree[ INT_GET(node2->hdr.count, ARCH_CONVERT)-1 ].hashval, ARCH_CONVERT) <
+ INT_GET(node1->btree[ INT_GET(node1->hdr.count, ARCH_CONVERT)-1 ].hashval, ARCH_CONVERT)))) {
+ return(1);
+ }
+ return(0);
+}
+
+/*
+ * Pick up the last hashvalue from an intermediate node.
+ */
+STATIC uint
+xfs_da_node_lasthash(xfs_dabuf_t *bp, int *count)
+{
+ xfs_da_intnode_t *node;
+
+ node = bp->data;
+ ASSERT(INT_GET(node->hdr.info.magic, ARCH_CONVERT) == XFS_DA_NODE_MAGIC);
+ if (count)
+ *count = INT_GET(node->hdr.count, ARCH_CONVERT);
+ if (INT_ISZERO(node->hdr.count, ARCH_CONVERT))
+ return(0);
+ return(INT_GET(node->btree[ INT_GET(node->hdr.count, ARCH_CONVERT)-1 ].hashval, ARCH_CONVERT));
+}
+
+/*
+ * Unlink a block from a doubly linked list of blocks.
+ */
+int /* error */
+xfs_da_blk_unlink(xfs_da_state_t *state, xfs_da_state_blk_t *drop_blk,
+ xfs_da_state_blk_t *save_blk)
+{
+ xfs_da_blkinfo_t *drop_info, *save_info, *tmp_info;
+ xfs_da_args_t *args;
+ xfs_dabuf_t *bp;
+ int error;
+
+ /*
+ * Set up environment.
+ */
+ args = state->args;
+ ASSERT(args != NULL);
+ save_info = save_blk->bp->data;
+ drop_info = drop_blk->bp->data;
+ ASSERT(save_blk->magic == XFS_DA_NODE_MAGIC ||
+ save_blk->magic == XFS_DIRX_LEAF_MAGIC(state->mp) ||
+ save_blk->magic == XFS_ATTR_LEAF_MAGIC);
+ ASSERT(save_blk->magic == INT_GET(save_info->magic, ARCH_CONVERT));
+ ASSERT(drop_blk->magic == INT_GET(drop_info->magic, ARCH_CONVERT));
+ ASSERT(save_blk->magic == drop_blk->magic);
+ ASSERT((INT_GET(save_info->forw, ARCH_CONVERT) == drop_blk->blkno) ||
+ (INT_GET(save_info->back, ARCH_CONVERT) == drop_blk->blkno));
+ ASSERT((INT_GET(drop_info->forw, ARCH_CONVERT) == save_blk->blkno) ||
+ (INT_GET(drop_info->back, ARCH_CONVERT) == save_blk->blkno));
+
+ /*
+ * Unlink the leaf block from the doubly linked chain of leaves.
+ */
+ if (INT_GET(save_info->back, ARCH_CONVERT) == drop_blk->blkno) {
+ save_info->back = drop_info->back; /* INT_: direct copy */
+ if (INT_GET(drop_info->back, ARCH_CONVERT)) {
+ error = xfs_da_read_buf(args->trans, args->dp,
+ INT_GET(drop_info->back,
+ ARCH_CONVERT), -1, &bp,
+ args->whichfork);
+ if (error)
+ return(error);
+ ASSERT(bp != NULL);
+ tmp_info = bp->data;
+ ASSERT(INT_GET(tmp_info->magic, ARCH_CONVERT) == INT_GET(save_info->magic, ARCH_CONVERT));
+ ASSERT(INT_GET(tmp_info->forw, ARCH_CONVERT) == drop_blk->blkno);
+ INT_SET(tmp_info->forw, ARCH_CONVERT, save_blk->blkno);
+ xfs_da_log_buf(args->trans, bp, 0,
+ sizeof(*tmp_info) - 1);
+ xfs_da_buf_done(bp);
+ }
+ } else {
+ save_info->forw = drop_info->forw; /* INT_: direct copy */
+ if (INT_GET(drop_info->forw, ARCH_CONVERT)) {
+ error = xfs_da_read_buf(args->trans, args->dp,
+ INT_GET(drop_info->forw, ARCH_CONVERT), -1, &bp,
+ args->whichfork);
+ if (error)
+ return(error);
+ ASSERT(bp != NULL);
+ tmp_info = bp->data;
+ ASSERT(INT_GET(tmp_info->magic, ARCH_CONVERT)
+ == INT_GET(save_info->magic, ARCH_CONVERT));
+ ASSERT(INT_GET(tmp_info->back, ARCH_CONVERT)
+ == drop_blk->blkno);
+ INT_SET(tmp_info->back, ARCH_CONVERT, save_blk->blkno);
+ xfs_da_log_buf(args->trans, bp, 0,
+ sizeof(*tmp_info) - 1);
+ xfs_da_buf_done(bp);
+ }
+ }
+
+ xfs_da_log_buf(args->trans, save_blk->bp, 0, sizeof(*save_info) - 1);
+ return(0);
+}
+
+/*
+ * Move a path "forward" or "!forward" one block at the current level.
+ *
+ * This routine will adjust a "path" to point to the next block
+ * "forward" (higher hashvalues) or "!forward" (lower hashvals) in the
+ * Btree, including updating pointers to the intermediate nodes between
+ * the new bottom and the root.
+ */
+int /* error */
+xfs_da_path_shift(xfs_da_state_t *state, xfs_da_state_path_t *path,
+ int forward, int release, int *result)
+{
+ xfs_da_state_blk_t *blk;
+ xfs_da_blkinfo_t *info;
+ xfs_da_intnode_t *node;
+ xfs_da_args_t *args;
+ xfs_dablk_t blkno=0;
+ int level, error;
+
+ /*
+ * Roll up the Btree looking for the first block where our
+ * current index is not at the edge of the block. Note that
+ * we skip the bottom layer because we want the sibling block.
+ */
+ args = state->args;
+ ASSERT(args != NULL);
+ ASSERT(path != NULL);
+ ASSERT((path->active > 0) && (path->active < XFS_DA_NODE_MAXDEPTH));
+ level = (path->active-1) - 1; /* skip bottom layer in path */
+ for (blk = &path->blk[level]; level >= 0; blk--, level--) {
+ ASSERT(blk->bp != NULL);
+ node = blk->bp->data;
+ ASSERT(INT_GET(node->hdr.info.magic, ARCH_CONVERT) == XFS_DA_NODE_MAGIC);
+ if (forward && (blk->index < INT_GET(node->hdr.count, ARCH_CONVERT)-1)) {
+ blk->index++;
+ blkno = INT_GET(node->btree[ blk->index ].before, ARCH_CONVERT);
+ break;
+ } else if (!forward && (blk->index > 0)) {
+ blk->index--;
+ blkno = INT_GET(node->btree[ blk->index ].before, ARCH_CONVERT);
+ break;
+ }
+ }
+ if (level < 0) {
+ *result = XFS_ERROR(ENOENT); /* we're out of our tree */
+ ASSERT(args->oknoent);
+ return(0);
+ }
+
+ /*
+ * Roll down the edge of the subtree until we reach the
+ * same depth we were at originally.
+ */
+ for (blk++, level++; level < path->active; blk++, level++) {
+ /*
+ * Release the old block.
+ * (if it's dirty, trans won't actually let go)
+ */
+ if (release)
+ xfs_da_brelse(args->trans, blk->bp);
+
+ /*
+ * Read the next child block.
+ */
+ blk->blkno = blkno;
+ error = xfs_da_read_buf(args->trans, args->dp, blkno, -1,
+ &blk->bp, args->whichfork);
+ if (error)
+ return(error);
+ ASSERT(blk->bp != NULL);
+ info = blk->bp->data;
+ ASSERT(INT_GET(info->magic, ARCH_CONVERT) == XFS_DA_NODE_MAGIC ||
+ INT_GET(info->magic, ARCH_CONVERT) == XFS_DIRX_LEAF_MAGIC(state->mp) ||
+ INT_GET(info->magic, ARCH_CONVERT) == XFS_ATTR_LEAF_MAGIC);
+ blk->magic = INT_GET(info->magic, ARCH_CONVERT);
+ if (INT_GET(info->magic, ARCH_CONVERT) == XFS_DA_NODE_MAGIC) {
+ node = (xfs_da_intnode_t *)info;
+ blk->hashval = INT_GET(node->btree[ INT_GET(node->hdr.count, ARCH_CONVERT)-1 ].hashval, ARCH_CONVERT);
+ if (forward)
+ blk->index = 0;
+ else
+ blk->index = INT_GET(node->hdr.count, ARCH_CONVERT)-1;
+ blkno = INT_GET(node->btree[ blk->index ].before, ARCH_CONVERT);
+ } else {
+ ASSERT(level == path->active-1);
+ blk->index = 0;
+ switch(blk->magic) {
+#ifdef __KERNEL__
+ case XFS_ATTR_LEAF_MAGIC:
+ blk->hashval = xfs_attr_leaf_lasthash(blk->bp,
+ NULL);
+ break;
+#endif
+ case XFS_DIR_LEAF_MAGIC:
+ ASSERT(XFS_DIR_IS_V1(state->mp));
+ blk->hashval = xfs_dir_leaf_lasthash(blk->bp,
+ NULL);
+ break;
+ case XFS_DIR2_LEAFN_MAGIC:
+ ASSERT(XFS_DIR_IS_V2(state->mp));
+ blk->hashval = xfs_dir2_leafn_lasthash(blk->bp,
+ NULL);
+ break;
+ default:
+ ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC ||
+ blk->magic ==
+ XFS_DIRX_LEAF_MAGIC(state->mp));
+ break;
+ }
+ }
+ }
+ *result = 0;
+ return(0);
+}
+
+
+/*========================================================================
+ * Utility routines.
+ *========================================================================*/
+
+/*
+ * Implement a simple hash on a character string.
+ * Rotate the hash value by 7 bits, then XOR each character in.
+ * This is implemented with some source-level loop unrolling.
+ */
+xfs_dahash_t
+xfs_da_hashname(uchar_t *name, int namelen)
+{
+ xfs_dahash_t hash;
+
+#define ROTL(x,y) (((x) << (y)) | ((x) >> (32 - (y))))
+#ifdef SLOWVERSION
+ /*
+ * This is the old one-byte-at-a-time version.
+ */
+ for (hash = 0; namelen > 0; namelen--) {
+ hash = *name++ ^ ROTL(hash, 7);
+ }
+ return(hash);
+#else
+ /*
+ * Do four characters at a time as long as we can.
+ */
+ for (hash = 0; namelen >= 4; namelen -= 4, name += 4) {
+ hash = (name[0] << 21) ^ (name[1] << 14) ^ (name[2] << 7) ^
+ (name[3] << 0) ^ ROTL(hash, 7 * 4);
+ }
+ /*
+ * Now do the rest of the characters.
+ */
+ switch (namelen) {
+ case 3:
+ return (name[0] << 14) ^ (name[1] << 7) ^ (name[2] << 0) ^
+ ROTL(hash, 7 * 3);
+ case 2:
+ return (name[0] << 7) ^ (name[1] << 0) ^ ROTL(hash, 7 * 2);
+ case 1:
+ return (name[0] << 0) ^ ROTL(hash, 7 * 1);
+ case 0:
+ return hash;
+ }
+ /* NOTREACHED */
+#endif
+#undef ROTL
+ return 0; /* keep gcc happy */
+}
+
+/*
+ * Add a block to the btree ahead of the file.
+ * Return the new block number to the caller.
+ */
+int
+xfs_da_grow_inode(xfs_da_args_t *args, xfs_dablk_t *new_blkno)
+{
+ xfs_fileoff_t bno, b;
+ xfs_bmbt_irec_t map;
+ xfs_bmbt_irec_t *mapp;
+ xfs_inode_t *dp;
+ int nmap, error, w, count, c, got, i, mapi;
+ xfs_fsize_t size;
+ xfs_trans_t *tp;
+ xfs_mount_t *mp;
+
+ dp = args->dp;
+ mp = dp->i_mount;
+ w = args->whichfork;
+ tp = args->trans;
+ /*
+ * For new directories adjust the file offset and block count.
+ */
+ if (w == XFS_DATA_FORK && XFS_DIR_IS_V2(mp)) {
+ bno = mp->m_dirleafblk;
+ count = mp->m_dirblkfsbs;
+ } else {
+ bno = 0;
+ count = 1;
+ }
+ /*
+ * Find a spot in the file space to put the new block.
+ */
+ if ((error = xfs_bmap_first_unused(tp, dp, count, &bno, w))) {
+ return error;
+ }
+ if (w == XFS_DATA_FORK && XFS_DIR_IS_V2(mp))
+ ASSERT(bno >= mp->m_dirleafblk && bno < mp->m_dirfreeblk);
+ /*
+ * Try mapping it in one filesystem block.
+ */
+ nmap = 1;
+ ASSERT(args->firstblock != NULL);
+ if ((error = xfs_bmapi(tp, dp, bno, count,
+ XFS_BMAPI_AFLAG(w)|XFS_BMAPI_WRITE|XFS_BMAPI_METADATA|
+ XFS_BMAPI_CONTIG,
+ args->firstblock, args->total, &map, &nmap,
+ args->flist))) {
+ return error;
+ }
+ ASSERT(nmap <= 1);
+ if (nmap == 1) {
+ mapp = &map;
+ mapi = 1;
+ }
+ /*
+ * If we didn't get it and the block might work if fragmented,
+ * try without the CONTIG flag. Loop until we get it all.
+ */
+ else if (nmap == 0 && count > 1) {
+ mapp = kmem_alloc(sizeof(*mapp) * count, KM_SLEEP);
+ for (b = bno, mapi = 0; b < bno + count; ) {
+ nmap = MIN(XFS_BMAP_MAX_NMAP, count);
+ c = (int)(bno + count - b);
+ if ((error = xfs_bmapi(tp, dp, b, c,
+ XFS_BMAPI_AFLAG(w)|XFS_BMAPI_WRITE|
+ XFS_BMAPI_METADATA,
+ args->firstblock, args->total,
+ &mapp[mapi], &nmap, args->flist))) {
+ kmem_free(mapp, sizeof(*mapp) * count);
+ return error;
+ }
+ if (nmap < 1)
+ break;
+ mapi += nmap;
+ b = mapp[mapi - 1].br_startoff +
+ mapp[mapi - 1].br_blockcount;
+ }
+ } else {
+ mapi = 0;
+ mapp = NULL;
+ }
+ /*
+ * Count the blocks we got, make sure it matches the total.
+ */
+ for (i = 0, got = 0; i < mapi; i++)
+ got += mapp[i].br_blockcount;
+ if (got != count || mapp[0].br_startoff != bno ||
+ mapp[mapi - 1].br_startoff + mapp[mapi - 1].br_blockcount !=
+ bno + count) {
+ if (mapp != &map)
+ kmem_free(mapp, sizeof(*mapp) * count);
+ return XFS_ERROR(ENOSPC);
+ }
+ if (mapp != &map)
+ kmem_free(mapp, sizeof(*mapp) * count);
+ *new_blkno = (xfs_dablk_t)bno;
+ /*
+ * For version 1 directories, adjust the file size if it changed.
+ */
+ if (w == XFS_DATA_FORK && XFS_DIR_IS_V1(mp)) {
+ ASSERT(mapi == 1);
+ if ((error = xfs_bmap_last_offset(tp, dp, &bno, w)))
+ return error;
+ size = XFS_FSB_TO_B(mp, bno);
+ if (size != dp->i_d.di_size) {
+ dp->i_d.di_size = size;
+ xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE);
+ }
+ }
+ return 0;
+}
+
+/*
+ * Ick. We need to always be able to remove a btree block, even
+ * if there's no space reservation because the filesystem is full.
+ * This is called if xfs_bunmapi on a btree block fails due to ENOSPC.
+ * It swaps the target block with the last block in the file. The
+ * last block in the file can always be removed since it can't cause
+ * a bmap btree split to do that.
+ */
+STATIC int
+xfs_da_swap_lastblock(xfs_da_args_t *args, xfs_dablk_t *dead_blknop,
+ xfs_dabuf_t **dead_bufp)
+{
+ xfs_dablk_t dead_blkno, last_blkno, sib_blkno, par_blkno;
+ xfs_dabuf_t *dead_buf, *last_buf, *sib_buf, *par_buf;
+ xfs_fileoff_t lastoff;
+ xfs_inode_t *ip;
+ xfs_trans_t *tp;
+ xfs_mount_t *mp;
+ int error, w, entno, level, dead_level;
+ xfs_da_blkinfo_t *dead_info, *sib_info;
+ xfs_da_intnode_t *par_node, *dead_node;
+ xfs_dir_leafblock_t *dead_leaf;
+ xfs_dir2_leaf_t *dead_leaf2;
+ xfs_dahash_t dead_hash;
+
+ dead_buf = *dead_bufp;
+ dead_blkno = *dead_blknop;
+ tp = args->trans;
+ ip = args->dp;
+ w = args->whichfork;
+ ASSERT(w == XFS_DATA_FORK);
+ mp = ip->i_mount;
+ if (XFS_DIR_IS_V2(mp)) {
+ lastoff = mp->m_dirfreeblk;
+ error = xfs_bmap_last_before(tp, ip, &lastoff, w);
+ } else
+ error = xfs_bmap_last_offset(tp, ip, &lastoff, w);
+ if (error)
+ return error;
+ if (unlikely(lastoff == 0)) {
+ XFS_ERROR_REPORT("xfs_da_swap_lastblock(1)", XFS_ERRLEVEL_LOW,
+ mp);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ /*
+ * Read the last block in the btree space.
+ */
+ last_blkno = (xfs_dablk_t)lastoff - mp->m_dirblkfsbs;
+ if ((error = xfs_da_read_buf(tp, ip, last_blkno, -1, &last_buf, w)))
+ return error;
+ /*
+ * Copy the last block into the dead buffer and log it.
+ */
+ memcpy(dead_buf->data, last_buf->data, mp->m_dirblksize);
+ xfs_da_log_buf(tp, dead_buf, 0, mp->m_dirblksize - 1);
+ dead_info = dead_buf->data;
+ /*
+ * Get values from the moved block.
+ */
+ if (INT_GET(dead_info->magic, ARCH_CONVERT) == XFS_DIR_LEAF_MAGIC) {
+ ASSERT(XFS_DIR_IS_V1(mp));
+ dead_leaf = (xfs_dir_leafblock_t *)dead_info;
+ dead_level = 0;
+ dead_hash =
+ INT_GET(dead_leaf->entries[INT_GET(dead_leaf->hdr.count, ARCH_CONVERT) - 1].hashval, ARCH_CONVERT);
+ } else if (INT_GET(dead_info->magic, ARCH_CONVERT) == XFS_DIR2_LEAFN_MAGIC) {
+ ASSERT(XFS_DIR_IS_V2(mp));
+ dead_leaf2 = (xfs_dir2_leaf_t *)dead_info;
+ dead_level = 0;
+ dead_hash = INT_GET(dead_leaf2->ents[INT_GET(dead_leaf2->hdr.count, ARCH_CONVERT) - 1].hashval, ARCH_CONVERT);
+ } else {
+ ASSERT(INT_GET(dead_info->magic, ARCH_CONVERT) == XFS_DA_NODE_MAGIC);
+ dead_node = (xfs_da_intnode_t *)dead_info;
+ dead_level = INT_GET(dead_node->hdr.level, ARCH_CONVERT);
+ dead_hash = INT_GET(dead_node->btree[INT_GET(dead_node->hdr.count, ARCH_CONVERT) - 1].hashval, ARCH_CONVERT);
+ }
+ sib_buf = par_buf = NULL;
+ /*
+ * If the moved block has a left sibling, fix up the pointers.
+ */
+ if ((sib_blkno = INT_GET(dead_info->back, ARCH_CONVERT))) {
+ if ((error = xfs_da_read_buf(tp, ip, sib_blkno, -1, &sib_buf, w)))
+ goto done;
+ sib_info = sib_buf->data;
+ if (unlikely(
+ INT_GET(sib_info->forw, ARCH_CONVERT) != last_blkno ||
+ INT_GET(sib_info->magic, ARCH_CONVERT) != INT_GET(dead_info->magic, ARCH_CONVERT))) {
+ XFS_ERROR_REPORT("xfs_da_swap_lastblock(2)",
+ XFS_ERRLEVEL_LOW, mp);
+ error = XFS_ERROR(EFSCORRUPTED);
+ goto done;
+ }
+ INT_SET(sib_info->forw, ARCH_CONVERT, dead_blkno);
+ xfs_da_log_buf(tp, sib_buf,
+ XFS_DA_LOGRANGE(sib_info, &sib_info->forw,
+ sizeof(sib_info->forw)));
+ xfs_da_buf_done(sib_buf);
+ sib_buf = NULL;
+ }
+ /*
+ * If the moved block has a right sibling, fix up the pointers.
+ */
+ if ((sib_blkno = INT_GET(dead_info->forw, ARCH_CONVERT))) {
+ if ((error = xfs_da_read_buf(tp, ip, sib_blkno, -1, &sib_buf, w)))
+ goto done;
+ sib_info = sib_buf->data;
+ if (unlikely(
+ INT_GET(sib_info->back, ARCH_CONVERT) != last_blkno
+ || INT_GET(sib_info->magic, ARCH_CONVERT)
+ != INT_GET(dead_info->magic, ARCH_CONVERT))) {
+ XFS_ERROR_REPORT("xfs_da_swap_lastblock(3)",
+ XFS_ERRLEVEL_LOW, mp);
+ error = XFS_ERROR(EFSCORRUPTED);
+ goto done;
+ }
+ INT_SET(sib_info->back, ARCH_CONVERT, dead_blkno);
+ xfs_da_log_buf(tp, sib_buf,
+ XFS_DA_LOGRANGE(sib_info, &sib_info->back,
+ sizeof(sib_info->back)));
+ xfs_da_buf_done(sib_buf);
+ sib_buf = NULL;
+ }
+ par_blkno = XFS_DIR_IS_V1(mp) ? 0 : mp->m_dirleafblk;
+ level = -1;
+ /*
+ * Walk down the tree looking for the parent of the moved block.
+ */
+ for (;;) {
+ if ((error = xfs_da_read_buf(tp, ip, par_blkno, -1, &par_buf, w)))
+ goto done;
+ par_node = par_buf->data;
+ if (unlikely(
+ INT_GET(par_node->hdr.info.magic, ARCH_CONVERT) != XFS_DA_NODE_MAGIC ||
+ (level >= 0 && level != INT_GET(par_node->hdr.level, ARCH_CONVERT) + 1))) {
+ XFS_ERROR_REPORT("xfs_da_swap_lastblock(4)",
+ XFS_ERRLEVEL_LOW, mp);
+ error = XFS_ERROR(EFSCORRUPTED);
+ goto done;
+ }
+ level = INT_GET(par_node->hdr.level, ARCH_CONVERT);
+ for (entno = 0;
+ entno < INT_GET(par_node->hdr.count, ARCH_CONVERT) &&
+ INT_GET(par_node->btree[entno].hashval, ARCH_CONVERT) < dead_hash;
+ entno++)
+ continue;
+ if (unlikely(entno == INT_GET(par_node->hdr.count, ARCH_CONVERT))) {
+ XFS_ERROR_REPORT("xfs_da_swap_lastblock(5)",
+ XFS_ERRLEVEL_LOW, mp);
+ error = XFS_ERROR(EFSCORRUPTED);
+ goto done;
+ }
+ par_blkno = INT_GET(par_node->btree[entno].before, ARCH_CONVERT);
+ if (level == dead_level + 1)
+ break;
+ xfs_da_brelse(tp, par_buf);
+ par_buf = NULL;
+ }
+ /*
+ * We're in the right parent block.
+ * Look for the right entry.
+ */
+ for (;;) {
+ for (;
+ entno < INT_GET(par_node->hdr.count, ARCH_CONVERT) &&
+ INT_GET(par_node->btree[entno].before, ARCH_CONVERT) != last_blkno;
+ entno++)
+ continue;
+ if (entno < INT_GET(par_node->hdr.count, ARCH_CONVERT))
+ break;
+ par_blkno = INT_GET(par_node->hdr.info.forw, ARCH_CONVERT);
+ xfs_da_brelse(tp, par_buf);
+ par_buf = NULL;
+ if (unlikely(par_blkno == 0)) {
+ XFS_ERROR_REPORT("xfs_da_swap_lastblock(6)",
+ XFS_ERRLEVEL_LOW, mp);
+ error = XFS_ERROR(EFSCORRUPTED);
+ goto done;
+ }
+ if ((error = xfs_da_read_buf(tp, ip, par_blkno, -1, &par_buf, w)))
+ goto done;
+ par_node = par_buf->data;
+ if (unlikely(
+ INT_GET(par_node->hdr.level, ARCH_CONVERT) != level ||
+ INT_GET(par_node->hdr.info.magic, ARCH_CONVERT) != XFS_DA_NODE_MAGIC)) {
+ XFS_ERROR_REPORT("xfs_da_swap_lastblock(7)",
+ XFS_ERRLEVEL_LOW, mp);
+ error = XFS_ERROR(EFSCORRUPTED);
+ goto done;
+ }
+ entno = 0;
+ }
+ /*
+ * Update the parent entry pointing to the moved block.
+ */
+ INT_SET(par_node->btree[entno].before, ARCH_CONVERT, dead_blkno);
+ xfs_da_log_buf(tp, par_buf,
+ XFS_DA_LOGRANGE(par_node, &par_node->btree[entno].before,
+ sizeof(par_node->btree[entno].before)));
+ xfs_da_buf_done(par_buf);
+ xfs_da_buf_done(dead_buf);
+ *dead_blknop = last_blkno;
+ *dead_bufp = last_buf;
+ return 0;
+done:
+ if (par_buf)
+ xfs_da_brelse(tp, par_buf);
+ if (sib_buf)
+ xfs_da_brelse(tp, sib_buf);
+ xfs_da_brelse(tp, last_buf);
+ return error;
+}
+
+/*
+ * Remove a btree block from a directory or attribute.
+ */
+int
+xfs_da_shrink_inode(xfs_da_args_t *args, xfs_dablk_t dead_blkno,
+ xfs_dabuf_t *dead_buf)
+{
+ xfs_inode_t *dp;
+ int done, error, w, count;
+ xfs_fileoff_t bno;
+ xfs_fsize_t size;
+ xfs_trans_t *tp;
+ xfs_mount_t *mp;
+
+ dp = args->dp;
+ w = args->whichfork;
+ tp = args->trans;
+ mp = dp->i_mount;
+ if (w == XFS_DATA_FORK && XFS_DIR_IS_V2(mp))
+ count = mp->m_dirblkfsbs;
+ else
+ count = 1;
+ for (;;) {
+ /*
+ * Remove extents. If we get ENOSPC for a dir we have to move
+ * the last block to the place we want to kill.
+ */
+ if ((error = xfs_bunmapi(tp, dp, dead_blkno, count,
+ XFS_BMAPI_AFLAG(w)|XFS_BMAPI_METADATA,
+ 0, args->firstblock, args->flist,
+ &done)) == ENOSPC) {
+ if (w != XFS_DATA_FORK)
+ goto done;
+ if ((error = xfs_da_swap_lastblock(args, &dead_blkno,
+ &dead_buf)))
+ goto done;
+ } else if (error)
+ goto done;
+ else
+ break;
+ }
+ ASSERT(done);
+ xfs_da_binval(tp, dead_buf);
+ /*
+ * Adjust the directory size for version 1.
+ */
+ if (w == XFS_DATA_FORK && XFS_DIR_IS_V1(mp)) {
+ if ((error = xfs_bmap_last_offset(tp, dp, &bno, w)))
+ return error;
+ size = XFS_FSB_TO_B(dp->i_mount, bno);
+ if (size != dp->i_d.di_size) {
+ dp->i_d.di_size = size;
+ xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE);
+ }
+ }
+ return 0;
+done:
+ xfs_da_binval(tp, dead_buf);
+ return error;
+}
+
+/*
+ * See if the mapping(s) for this btree block are valid, i.e.
+ * don't contain holes, are logically contiguous, and cover the whole range.
+ */
+STATIC int
+xfs_da_map_covers_blocks(
+ int nmap,
+ xfs_bmbt_irec_t *mapp,
+ xfs_dablk_t bno,
+ int count)
+{
+ int i;
+ xfs_fileoff_t off;
+
+ for (i = 0, off = bno; i < nmap; i++) {
+ if (mapp[i].br_startblock == HOLESTARTBLOCK ||
+ mapp[i].br_startblock == DELAYSTARTBLOCK) {
+ return 0;
+ }
+ if (off != mapp[i].br_startoff) {
+ return 0;
+ }
+ off += mapp[i].br_blockcount;
+ }
+ return off == bno + count;
+}
+
+/*
+ * Make a dabuf.
+ * Used for get_buf, read_buf, read_bufr, and reada_buf.
+ */
+STATIC int
+xfs_da_do_buf(
+ xfs_trans_t *trans,
+ xfs_inode_t *dp,
+ xfs_dablk_t bno,
+ xfs_daddr_t *mappedbnop,
+ xfs_dabuf_t **bpp,
+ int whichfork,
+ int caller,
+ inst_t *ra)
+{
+ xfs_buf_t *bp = NULL;
+ xfs_buf_t **bplist;
+ int error=0;
+ int i;
+ xfs_bmbt_irec_t map;
+ xfs_bmbt_irec_t *mapp;
+ xfs_daddr_t mappedbno;
+ xfs_mount_t *mp;
+ int nbplist=0;
+ int nfsb;
+ int nmap;
+ xfs_dabuf_t *rbp;
+
+ mp = dp->i_mount;
+ if (whichfork == XFS_DATA_FORK && XFS_DIR_IS_V2(mp))
+ nfsb = mp->m_dirblkfsbs;
+ else
+ nfsb = 1;
+ mappedbno = *mappedbnop;
+ /*
+ * Caller doesn't have a mapping. -2 means don't complain
+ * if we land in a hole.
+ */
+ if (mappedbno == -1 || mappedbno == -2) {
+ /*
+ * Optimize the one-block case.
+ */
+ if (nfsb == 1) {
+ xfs_fsblock_t fsb;
+
+ if ((error =
+ xfs_bmapi_single(trans, dp, whichfork, &fsb,
+ (xfs_fileoff_t)bno))) {
+ return error;
+ }
+ mapp = &map;
+ if (fsb == NULLFSBLOCK) {
+ nmap = 0;
+ } else {
+ map.br_startblock = fsb;
+ map.br_startoff = (xfs_fileoff_t)bno;
+ map.br_blockcount = 1;
+ nmap = 1;
+ }
+ } else {
+ mapp = kmem_alloc(sizeof(*mapp) * nfsb, KM_SLEEP);
+ nmap = nfsb;
+ if ((error = xfs_bmapi(trans, dp, (xfs_fileoff_t)bno,
+ nfsb,
+ XFS_BMAPI_METADATA |
+ XFS_BMAPI_AFLAG(whichfork),
+ NULL, 0, mapp, &nmap, NULL)))
+ goto exit0;
+ }
+ } else {
+ map.br_startblock = XFS_DADDR_TO_FSB(mp, mappedbno);
+ map.br_startoff = (xfs_fileoff_t)bno;
+ map.br_blockcount = nfsb;
+ mapp = &map;
+ nmap = 1;
+ }
+ if (!xfs_da_map_covers_blocks(nmap, mapp, bno, nfsb)) {
+ error = mappedbno == -2 ? 0 : XFS_ERROR(EFSCORRUPTED);
+ if (unlikely(error == EFSCORRUPTED)) {
+ if (xfs_error_level >= XFS_ERRLEVEL_LOW) {
+ int i;
+ cmn_err(CE_ALERT, "xfs_da_do_buf: bno %lld\n",
+ (long long)bno);
+ cmn_err(CE_ALERT, "dir: inode %lld\n",
+ (long long)dp->i_ino);
+ for (i = 0; i < nmap; i++) {
+ cmn_err(CE_ALERT,
+ "[%02d] br_startoff %lld br_startblock %lld br_blockcount %lld br_state %d\n",
+ i,
+ (long long)mapp[i].br_startoff,
+ (long long)mapp[i].br_startblock,
+ (long long)mapp[i].br_blockcount,
+ mapp[i].br_state);
+ }
+ }
+ XFS_ERROR_REPORT("xfs_da_do_buf(1)",
+ XFS_ERRLEVEL_LOW, mp);
+ }
+ goto exit0;
+ }
+ if (caller != 3 && nmap > 1) {
+ bplist = kmem_alloc(sizeof(*bplist) * nmap, KM_SLEEP);
+ nbplist = 0;
+ } else
+ bplist = NULL;
+ /*
+ * Turn the mapping(s) into buffer(s).
+ */
+ for (i = 0; i < nmap; i++) {
+ int nmapped;
+
+ mappedbno = XFS_FSB_TO_DADDR(mp, mapp[i].br_startblock);
+ if (i == 0)
+ *mappedbnop = mappedbno;
+ nmapped = (int)XFS_FSB_TO_BB(mp, mapp[i].br_blockcount);
+ switch (caller) {
+ case 0:
+ bp = xfs_trans_get_buf(trans, mp->m_ddev_targp,
+ mappedbno, nmapped, 0);
+ error = bp ? XFS_BUF_GETERROR(bp) : XFS_ERROR(EIO);
+ break;
+ case 1:
+#ifndef __KERNEL__
+ case 2:
+#endif
+ bp = NULL;
+ error = xfs_trans_read_buf(mp, trans, mp->m_ddev_targp,
+ mappedbno, nmapped, 0, &bp);
+ break;
+#ifdef __KERNEL__
+ case 3:
+ xfs_baread(mp->m_ddev_targp, mappedbno, nmapped);
+ error = 0;
+ bp = NULL;
+ break;
+#endif
+ }
+ if (error) {
+ if (bp)
+ xfs_trans_brelse(trans, bp);
+ goto exit1;
+ }
+ if (!bp)
+ continue;
+ if (caller == 1) {
+ if (whichfork == XFS_ATTR_FORK) {
+ XFS_BUF_SET_VTYPE_REF(bp, B_FS_ATTR_BTREE,
+ XFS_ATTR_BTREE_REF);
+ } else {
+ XFS_BUF_SET_VTYPE_REF(bp, B_FS_DIR_BTREE,
+ XFS_DIR_BTREE_REF);
+ }
+ }
+ if (bplist) {
+ bplist[nbplist++] = bp;
+ }
+ }
+ /*
+ * Build a dabuf structure.
+ */
+ if (bplist) {
+ rbp = xfs_da_buf_make(nbplist, bplist, ra);
+ } else if (bp)
+ rbp = xfs_da_buf_make(1, &bp, ra);
+ else
+ rbp = NULL;
+ /*
+ * For read_buf, check the magic number.
+ */
+ if (caller == 1) {
+ xfs_dir2_data_t *data;
+ xfs_dir2_free_t *free;
+ xfs_da_blkinfo_t *info;
+ uint magic, magic1;
+
+ info = rbp->data;
+ data = rbp->data;
+ free = rbp->data;
+ magic = INT_GET(info->magic, ARCH_CONVERT);
+ magic1 = INT_GET(data->hdr.magic, ARCH_CONVERT);
+ if (unlikely(
+ XFS_TEST_ERROR((magic != XFS_DA_NODE_MAGIC) &&
+ (magic != XFS_DIR_LEAF_MAGIC) &&
+ (magic != XFS_ATTR_LEAF_MAGIC) &&
+ (magic != XFS_DIR2_LEAF1_MAGIC) &&
+ (magic != XFS_DIR2_LEAFN_MAGIC) &&
+ (magic1 != XFS_DIR2_BLOCK_MAGIC) &&
+ (magic1 != XFS_DIR2_DATA_MAGIC) &&
+ (INT_GET(free->hdr.magic, ARCH_CONVERT) != XFS_DIR2_FREE_MAGIC),
+ mp, XFS_ERRTAG_DA_READ_BUF,
+ XFS_RANDOM_DA_READ_BUF))) {
+ xfs_buftrace("DA READ ERROR", rbp->bps[0]);
+ XFS_CORRUPTION_ERROR("xfs_da_do_buf(2)",
+ XFS_ERRLEVEL_LOW, mp, info);
+ error = XFS_ERROR(EFSCORRUPTED);
+ xfs_da_brelse(trans, rbp);
+ nbplist = 0;
+ goto exit1;
+ }
+ }
+ if (bplist) {
+ kmem_free(bplist, sizeof(*bplist) * nmap);
+ }
+ if (mapp != &map) {
+ kmem_free(mapp, sizeof(*mapp) * nfsb);
+ }
+ if (bpp)
+ *bpp = rbp;
+ return 0;
+exit1:
+ if (bplist) {
+ for (i = 0; i < nbplist; i++)
+ xfs_trans_brelse(trans, bplist[i]);
+ kmem_free(bplist, sizeof(*bplist) * nmap);
+ }
+exit0:
+ if (mapp != &map)
+ kmem_free(mapp, sizeof(*mapp) * nfsb);
+ if (bpp)
+ *bpp = NULL;
+ return error;
+}
+
+/*
+ * Get a buffer for the dir/attr block.
+ */
+int
+xfs_da_get_buf(
+ xfs_trans_t *trans,
+ xfs_inode_t *dp,
+ xfs_dablk_t bno,
+ xfs_daddr_t mappedbno,
+ xfs_dabuf_t **bpp,
+ int whichfork)
+{
+ return xfs_da_do_buf(trans, dp, bno, &mappedbno, bpp, whichfork, 0,
+ (inst_t *)__return_address);
+}
+
+/*
+ * Get a buffer for the dir/attr block, fill in the contents.
+ */
+int
+xfs_da_read_buf(
+ xfs_trans_t *trans,
+ xfs_inode_t *dp,
+ xfs_dablk_t bno,
+ xfs_daddr_t mappedbno,
+ xfs_dabuf_t **bpp,
+ int whichfork)
+{
+ return xfs_da_do_buf(trans, dp, bno, &mappedbno, bpp, whichfork, 1,
+ (inst_t *)__return_address);
+}
+
+/*
+ * Readahead the dir/attr block.
+ */
+xfs_daddr_t
+xfs_da_reada_buf(
+ xfs_trans_t *trans,
+ xfs_inode_t *dp,
+ xfs_dablk_t bno,
+ int whichfork)
+{
+ xfs_daddr_t rval;
+
+ rval = -1;
+ if (xfs_da_do_buf(trans, dp, bno, &rval, NULL, whichfork, 3,
+ (inst_t *)__return_address))
+ return -1;
+ else
+ return rval;
+}
+
+/*
+ * Calculate the number of bits needed to hold i different values.
+ */
+uint
+xfs_da_log2_roundup(uint i)
+{
+ uint rval;
+
+ for (rval = 0; rval < NBBY * sizeof(i); rval++) {
+ if ((1 << rval) >= i)
+ break;
+ }
+ return(rval);
+}
+
+kmem_zone_t *xfs_da_state_zone; /* anchor for state struct zone */
+kmem_zone_t *xfs_dabuf_zone; /* dabuf zone */
+
+/*
+ * Allocate a dir-state structure.
+ * We don't put them on the stack since they're large.
+ */
+xfs_da_state_t *
+xfs_da_state_alloc(void)
+{
+ return kmem_zone_zalloc(xfs_da_state_zone, KM_SLEEP);
+}
+
+/*
+ * Kill the altpath contents of a da-state structure.
+ */
+void
+xfs_da_state_kill_altpath(xfs_da_state_t *state)
+{
+ int i;
+
+ for (i = 0; i < state->altpath.active; i++) {
+ if (state->altpath.blk[i].bp) {
+ if (state->altpath.blk[i].bp != state->path.blk[i].bp)
+ xfs_da_buf_done(state->altpath.blk[i].bp);
+ state->altpath.blk[i].bp = NULL;
+ }
+ }
+ state->altpath.active = 0;
+}
+
+/*
+ * Free a da-state structure.
+ */
+void
+xfs_da_state_free(xfs_da_state_t *state)
+{
+ int i;
+
+ xfs_da_state_kill_altpath(state);
+ for (i = 0; i < state->path.active; i++) {
+ if (state->path.blk[i].bp)
+ xfs_da_buf_done(state->path.blk[i].bp);
+ }
+ if (state->extravalid && state->extrablk.bp)
+ xfs_da_buf_done(state->extrablk.bp);
+#ifdef DEBUG
+ memset((char *)state, 0, sizeof(*state));
+#endif /* DEBUG */
+ kmem_zone_free(xfs_da_state_zone, state);
+}
+
+#ifdef XFS_DABUF_DEBUG
+xfs_dabuf_t *xfs_dabuf_global_list;
+lock_t xfs_dabuf_global_lock;
+#endif
+
+/*
+ * Create a dabuf.
+ */
+/* ARGSUSED */
+STATIC xfs_dabuf_t *
+xfs_da_buf_make(int nbuf, xfs_buf_t **bps, inst_t *ra)
+{
+ xfs_buf_t *bp;
+ xfs_dabuf_t *dabuf;
+ int i;
+ int off;
+
+ if (nbuf == 1)
+ dabuf = kmem_zone_alloc(xfs_dabuf_zone, KM_SLEEP);
+ else
+ dabuf = kmem_alloc(XFS_DA_BUF_SIZE(nbuf), KM_SLEEP);
+ dabuf->dirty = 0;
+#ifdef XFS_DABUF_DEBUG
+ dabuf->ra = ra;
+ dabuf->target = XFS_BUF_TARGET(bps[0]);
+ dabuf->blkno = XFS_BUF_ADDR(bps[0]);
+#endif
+ if (nbuf == 1) {
+ dabuf->nbuf = 1;
+ bp = bps[0];
+ dabuf->bbcount = (short)BTOBB(XFS_BUF_COUNT(bp));
+ dabuf->data = XFS_BUF_PTR(bp);
+ dabuf->bps[0] = bp;
+ } else {
+ dabuf->nbuf = nbuf;
+ for (i = 0, dabuf->bbcount = 0; i < nbuf; i++) {
+ dabuf->bps[i] = bp = bps[i];
+ dabuf->bbcount += BTOBB(XFS_BUF_COUNT(bp));
+ }
+ dabuf->data = kmem_alloc(BBTOB(dabuf->bbcount), KM_SLEEP);
+ for (i = off = 0; i < nbuf; i++, off += XFS_BUF_COUNT(bp)) {
+ bp = bps[i];
+ memcpy((char *)dabuf->data + off, XFS_BUF_PTR(bp),
+ XFS_BUF_COUNT(bp));
+ }
+ }
+#ifdef XFS_DABUF_DEBUG
+ {
+ SPLDECL(s);
+ xfs_dabuf_t *p;
+
+ s = mutex_spinlock(&xfs_dabuf_global_lock);
+ for (p = xfs_dabuf_global_list; p; p = p->next) {
+ ASSERT(p->blkno != dabuf->blkno ||
+ p->target != dabuf->target);
+ }
+ dabuf->prev = NULL;
+ if (xfs_dabuf_global_list)
+ xfs_dabuf_global_list->prev = dabuf;
+ dabuf->next = xfs_dabuf_global_list;
+ xfs_dabuf_global_list = dabuf;
+ mutex_spinunlock(&xfs_dabuf_global_lock, s);
+ }
+#endif
+ return dabuf;
+}
+
+/*
+ * Un-dirty a dabuf.
+ */
+STATIC void
+xfs_da_buf_clean(xfs_dabuf_t *dabuf)
+{
+ xfs_buf_t *bp;
+ int i;
+ int off;
+
+ if (dabuf->dirty) {
+ ASSERT(dabuf->nbuf > 1);
+ dabuf->dirty = 0;
+ for (i = off = 0; i < dabuf->nbuf;
+ i++, off += XFS_BUF_COUNT(bp)) {
+ bp = dabuf->bps[i];
+ memcpy(XFS_BUF_PTR(bp), (char *)dabuf->data + off,
+ XFS_BUF_COUNT(bp));
+ }
+ }
+}
+
+/*
+ * Release a dabuf.
+ */
+void
+xfs_da_buf_done(xfs_dabuf_t *dabuf)
+{
+ ASSERT(dabuf);
+ ASSERT(dabuf->nbuf && dabuf->data && dabuf->bbcount && dabuf->bps[0]);
+ if (dabuf->dirty)
+ xfs_da_buf_clean(dabuf);
+ if (dabuf->nbuf > 1)
+ kmem_free(dabuf->data, BBTOB(dabuf->bbcount));
+#ifdef XFS_DABUF_DEBUG
+ {
+ SPLDECL(s);
+
+ s = mutex_spinlock(&xfs_dabuf_global_lock);
+ if (dabuf->prev)
+ dabuf->prev->next = dabuf->next;
+ else
+ xfs_dabuf_global_list = dabuf->next;
+ if (dabuf->next)
+ dabuf->next->prev = dabuf->prev;
+ mutex_spinunlock(&xfs_dabuf_global_lock, s);
+ }
+ memset(dabuf, 0, XFS_DA_BUF_SIZE(dabuf->nbuf));
+#endif
+ if (dabuf->nbuf == 1)
+ kmem_zone_free(xfs_dabuf_zone, dabuf);
+ else
+ kmem_free(dabuf, XFS_DA_BUF_SIZE(dabuf->nbuf));
+}
+
+/*
+ * Log transaction from a dabuf.
+ */
+void
+xfs_da_log_buf(xfs_trans_t *tp, xfs_dabuf_t *dabuf, uint first, uint last)
+{
+ xfs_buf_t *bp;
+ uint f;
+ int i;
+ uint l;
+ int off;
+
+ ASSERT(dabuf->nbuf && dabuf->data && dabuf->bbcount && dabuf->bps[0]);
+ if (dabuf->nbuf == 1) {
+ ASSERT(dabuf->data == (void *)XFS_BUF_PTR(dabuf->bps[0]));
+ xfs_trans_log_buf(tp, dabuf->bps[0], first, last);
+ return;
+ }
+ dabuf->dirty = 1;
+ ASSERT(first <= last);
+ for (i = off = 0; i < dabuf->nbuf; i++, off += XFS_BUF_COUNT(bp)) {
+ bp = dabuf->bps[i];
+ f = off;
+ l = f + XFS_BUF_COUNT(bp) - 1;
+ if (f < first)
+ f = first;
+ if (l > last)
+ l = last;
+ if (f <= l)
+ xfs_trans_log_buf(tp, bp, f - off, l - off);
+ /*
+ * B_DONE is set by xfs_trans_log buf.
+ * If we don't set it on a new buffer (get not read)
+ * then if we don't put anything in the buffer it won't
+ * be set, and at commit it it released into the cache,
+ * and then a read will fail.
+ */
+ else if (!(XFS_BUF_ISDONE(bp)))
+ XFS_BUF_DONE(bp);
+ }
+ ASSERT(last < off);
+}
+
+/*
+ * Release dabuf from a transaction.
+ * Have to free up the dabuf before the buffers are released,
+ * since the synchronization on the dabuf is really the lock on the buffer.
+ */
+void
+xfs_da_brelse(xfs_trans_t *tp, xfs_dabuf_t *dabuf)
+{
+ xfs_buf_t *bp;
+ xfs_buf_t **bplist;
+ int i;
+ int nbuf;
+
+ ASSERT(dabuf->nbuf && dabuf->data && dabuf->bbcount && dabuf->bps[0]);
+ if ((nbuf = dabuf->nbuf) == 1) {
+ bplist = &bp;
+ bp = dabuf->bps[0];
+ } else {
+ bplist = kmem_alloc(nbuf * sizeof(*bplist), KM_SLEEP);
+ memcpy(bplist, dabuf->bps, nbuf * sizeof(*bplist));
+ }
+ xfs_da_buf_done(dabuf);
+ for (i = 0; i < nbuf; i++)
+ xfs_trans_brelse(tp, bplist[i]);
+ if (bplist != &bp)
+ kmem_free(bplist, nbuf * sizeof(*bplist));
+}
+
+/*
+ * Invalidate dabuf from a transaction.
+ */
+void
+xfs_da_binval(xfs_trans_t *tp, xfs_dabuf_t *dabuf)
+{
+ xfs_buf_t *bp;
+ xfs_buf_t **bplist;
+ int i;
+ int nbuf;
+
+ ASSERT(dabuf->nbuf && dabuf->data && dabuf->bbcount && dabuf->bps[0]);
+ if ((nbuf = dabuf->nbuf) == 1) {
+ bplist = &bp;
+ bp = dabuf->bps[0];
+ } else {
+ bplist = kmem_alloc(nbuf * sizeof(*bplist), KM_SLEEP);
+ memcpy(bplist, dabuf->bps, nbuf * sizeof(*bplist));
+ }
+ xfs_da_buf_done(dabuf);
+ for (i = 0; i < nbuf; i++)
+ xfs_trans_binval(tp, bplist[i]);
+ if (bplist != &bp)
+ kmem_free(bplist, nbuf * sizeof(*bplist));
+}
+
+/*
+ * Get the first daddr from a dabuf.
+ */
+xfs_daddr_t
+xfs_da_blkno(xfs_dabuf_t *dabuf)
+{
+ ASSERT(dabuf->nbuf);
+ ASSERT(dabuf->data);
+ return XFS_BUF_ADDR(dabuf->bps[0]);
+}
diff --git a/fs/xfs/xfs_da_btree.h b/fs/xfs/xfs_da_btree.h
new file mode 100644
index 00000000000000..9fc699d9699575
--- /dev/null
+++ b/fs/xfs/xfs_da_btree.h
@@ -0,0 +1,335 @@
+/*
+ * Copyright (c) 2000, 2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_DA_BTREE_H__
+#define __XFS_DA_BTREE_H__
+
+struct xfs_buf;
+struct xfs_bmap_free;
+struct xfs_inode;
+struct xfs_mount;
+struct xfs_trans;
+struct zone;
+
+/*========================================================================
+ * Directory Structure when greater than XFS_LBSIZE(mp) bytes.
+ *========================================================================*/
+
+/*
+ * This structure is common to both leaf nodes and non-leaf nodes in the Btree.
+ *
+ * Is is used to manage a doubly linked list of all blocks at the same
+ * level in the Btree, and to identify which type of block this is.
+ */
+#define XFS_DA_NODE_MAGIC 0xfebe /* magic number: non-leaf blocks */
+#define XFS_DIR_LEAF_MAGIC 0xfeeb /* magic number: directory leaf blks */
+#define XFS_ATTR_LEAF_MAGIC 0xfbee /* magic number: attribute leaf blks */
+#define XFS_DIR2_LEAF1_MAGIC 0xd2f1 /* magic number: v2 dirlf single blks */
+#define XFS_DIR2_LEAFN_MAGIC 0xd2ff /* magic number: v2 dirlf multi blks */
+
+#define XFS_DIRX_LEAF_MAGIC(mp) \
+ (XFS_DIR_IS_V1(mp) ? XFS_DIR_LEAF_MAGIC : XFS_DIR2_LEAFN_MAGIC)
+
+typedef struct xfs_da_blkinfo {
+ xfs_dablk_t forw; /* previous block in list */
+ xfs_dablk_t back; /* following block in list */
+ __uint16_t magic; /* validity check on block */
+ __uint16_t pad; /* unused */
+} xfs_da_blkinfo_t;
+
+/*
+ * This is the structure of the root and intermediate nodes in the Btree.
+ * The leaf nodes are defined above.
+ *
+ * Entries are not packed.
+ *
+ * Since we have duplicate keys, use a binary search but always follow
+ * all match in the block, not just the first match found.
+ */
+#define XFS_DA_NODE_MAXDEPTH 5 /* max depth of Btree */
+
+typedef struct xfs_da_intnode {
+ struct xfs_da_node_hdr { /* constant-structure header block */
+ xfs_da_blkinfo_t info; /* block type, links, etc. */
+ __uint16_t count; /* count of active entries */
+ __uint16_t level; /* level above leaves (leaf == 0) */
+ } hdr;
+ struct xfs_da_node_entry {
+ xfs_dahash_t hashval; /* hash value for this descendant */
+ xfs_dablk_t before; /* Btree block before this key */
+ } btree[1]; /* variable sized array of keys */
+} xfs_da_intnode_t;
+typedef struct xfs_da_node_hdr xfs_da_node_hdr_t;
+typedef struct xfs_da_node_entry xfs_da_node_entry_t;
+
+#define XFS_DA_MAXHASH ((xfs_dahash_t)-1) /* largest valid hash value */
+
+/*
+ * Macros used by directory code to interface to the filesystem.
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_LBSIZE)
+int xfs_lbsize(struct xfs_mount *mp);
+#define XFS_LBSIZE(mp) xfs_lbsize(mp)
+#else
+#define XFS_LBSIZE(mp) ((mp)->m_sb.sb_blocksize)
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_LBLOG)
+int xfs_lblog(struct xfs_mount *mp);
+#define XFS_LBLOG(mp) xfs_lblog(mp)
+#else
+#define XFS_LBLOG(mp) ((mp)->m_sb.sb_blocklog)
+#endif
+
+/*
+ * Macros used by directory code to interface to the kernel
+ */
+
+/*
+ * Macros used to manipulate directory off_t's
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DA_MAKE_BNOENTRY)
+__uint32_t xfs_da_make_bnoentry(struct xfs_mount *mp, xfs_dablk_t bno,
+ int entry);
+#define XFS_DA_MAKE_BNOENTRY(mp,bno,entry) \
+ xfs_da_make_bnoentry(mp,bno,entry)
+#else
+#define XFS_DA_MAKE_BNOENTRY(mp,bno,entry) \
+ (((bno) << (mp)->m_dircook_elog) | (entry))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DA_MAKE_COOKIE)
+xfs_off_t xfs_da_make_cookie(struct xfs_mount *mp, xfs_dablk_t bno, int entry,
+ xfs_dahash_t hash);
+#define XFS_DA_MAKE_COOKIE(mp,bno,entry,hash) \
+ xfs_da_make_cookie(mp,bno,entry,hash)
+#else
+#define XFS_DA_MAKE_COOKIE(mp,bno,entry,hash) \
+ (((xfs_off_t)XFS_DA_MAKE_BNOENTRY(mp, bno, entry) << 32) | (hash))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DA_COOKIE_HASH)
+xfs_dahash_t xfs_da_cookie_hash(struct xfs_mount *mp, xfs_off_t cookie);
+#define XFS_DA_COOKIE_HASH(mp,cookie) xfs_da_cookie_hash(mp,cookie)
+#else
+#define XFS_DA_COOKIE_HASH(mp,cookie) ((xfs_dahash_t)(cookie))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DA_COOKIE_BNO)
+xfs_dablk_t xfs_da_cookie_bno(struct xfs_mount *mp, xfs_off_t cookie);
+#define XFS_DA_COOKIE_BNO(mp,cookie) xfs_da_cookie_bno(mp,cookie)
+#else
+#define XFS_DA_COOKIE_BNO(mp,cookie) \
+ (((xfs_off_t)(cookie) >> 31) == -1LL ? \
+ (xfs_dablk_t)0 : \
+ (xfs_dablk_t)((xfs_off_t)(cookie) >> ((mp)->m_dircook_elog + 32)))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DA_COOKIE_ENTRY)
+int xfs_da_cookie_entry(struct xfs_mount *mp, xfs_off_t cookie);
+#define XFS_DA_COOKIE_ENTRY(mp,cookie) xfs_da_cookie_entry(mp,cookie)
+#else
+#define XFS_DA_COOKIE_ENTRY(mp,cookie) \
+ (((xfs_off_t)(cookie) >> 31) == -1LL ? \
+ (xfs_dablk_t)0 : \
+ (xfs_dablk_t)(((xfs_off_t)(cookie) >> 32) & \
+ ((1 << (mp)->m_dircook_elog) - 1)))
+#endif
+
+
+/*========================================================================
+ * Btree searching and modification structure definitions.
+ *========================================================================*/
+
+/*
+ * Structure to ease passing around component names.
+ */
+typedef struct xfs_da_args {
+ uchar_t *name; /* string (maybe not NULL terminated) */
+ int namelen; /* length of string (maybe no NULL) */
+ uchar_t *value; /* set of bytes (maybe contain NULLs) */
+ int valuelen; /* length of value */
+ int flags; /* argument flags (eg: ATTR_NOCREATE) */
+ xfs_dahash_t hashval; /* hash value of name */
+ xfs_ino_t inumber; /* input/output inode number */
+ struct xfs_inode *dp; /* directory inode to manipulate */
+ xfs_fsblock_t *firstblock; /* ptr to firstblock for bmap calls */
+ struct xfs_bmap_free *flist; /* ptr to freelist for bmap_finish */
+ struct xfs_trans *trans; /* current trans (changes over time) */
+ xfs_extlen_t total; /* total blocks needed, for 1st bmap */
+ int whichfork; /* data or attribute fork */
+ xfs_dablk_t blkno; /* blkno of attr leaf of interest */
+ int index; /* index of attr of interest in blk */
+ xfs_dablk_t rmtblkno; /* remote attr value starting blkno */
+ int rmtblkcnt; /* remote attr value block count */
+ xfs_dablk_t blkno2; /* blkno of 2nd attr leaf of interest */
+ int index2; /* index of 2nd attr in blk */
+ xfs_dablk_t rmtblkno2; /* remote attr value starting blkno */
+ int rmtblkcnt2; /* remote attr value block count */
+ unsigned char justcheck; /* T/F: check for ok with no space */
+ unsigned char rename; /* T/F: this is an atomic rename op */
+ unsigned char addname; /* T/F: this is an add operation */
+ unsigned char oknoent; /* T/F: ok to return ENOENT, else die */
+} xfs_da_args_t;
+
+/*
+ * Structure to describe buffer(s) for a block.
+ * This is needed in the directory version 2 format case, when
+ * multiple non-contiguous fsblocks might be needed to cover one
+ * logical directory block.
+ * If the buffer count is 1 then the data pointer points to the
+ * same place as the b_addr field for the buffer, else to kmem_alloced memory.
+ */
+typedef struct xfs_dabuf {
+ int nbuf; /* number of buffer pointers present */
+ short dirty; /* data needs to be copied back */
+ short bbcount; /* how large is data in bbs */
+ void *data; /* pointer for buffers' data */
+#ifdef XFS_DABUF_DEBUG
+ inst_t *ra; /* return address of caller to make */
+ struct xfs_dabuf *next; /* next in global chain */
+ struct xfs_dabuf *prev; /* previous in global chain */
+ struct xfs_buftarg *target; /* device for buffer */
+ xfs_daddr_t blkno; /* daddr first in bps[0] */
+#endif
+ struct xfs_buf *bps[1]; /* actually nbuf of these */
+} xfs_dabuf_t;
+#define XFS_DA_BUF_SIZE(n) \
+ (sizeof(xfs_dabuf_t) + sizeof(struct xfs_buf *) * ((n) - 1))
+
+#ifdef XFS_DABUF_DEBUG
+extern xfs_dabuf_t *xfs_dabuf_global_list;
+#endif
+
+/*
+ * Storage for holding state during Btree searches and split/join ops.
+ *
+ * Only need space for 5 intermediate nodes. With a minimum of 62-way
+ * fanout to the Btree, we can support over 900 million directory blocks,
+ * which is slightly more than enough.
+ */
+typedef struct xfs_da_state_blk {
+ xfs_dabuf_t *bp; /* buffer containing block */
+ xfs_dablk_t blkno; /* filesystem blkno of buffer */
+ xfs_daddr_t disk_blkno; /* on-disk blkno (in BBs) of buffer */
+ int index; /* relevant index into block */
+ xfs_dahash_t hashval; /* last hash value in block */
+ int magic; /* blk's magic number, ie: blk type */
+} xfs_da_state_blk_t;
+
+typedef struct xfs_da_state_path {
+ int active; /* number of active levels */
+ xfs_da_state_blk_t blk[XFS_DA_NODE_MAXDEPTH];
+} xfs_da_state_path_t;
+
+typedef struct xfs_da_state {
+ xfs_da_args_t *args; /* filename arguments */
+ struct xfs_mount *mp; /* filesystem mount point */
+ unsigned int blocksize; /* logical block size */
+ unsigned int node_ents; /* how many entries in danode */
+ xfs_da_state_path_t path; /* search/split paths */
+ xfs_da_state_path_t altpath; /* alternate path for join */
+ unsigned char inleaf; /* insert into 1->lf, 0->splf */
+ unsigned char extravalid; /* T/F: extrablk is in use */
+ unsigned char extraafter; /* T/F: extrablk is after new */
+ xfs_da_state_blk_t extrablk; /* for double-splits on leafs */
+ /* for dirv2 extrablk is data */
+} xfs_da_state_t;
+
+/*
+ * Utility macros to aid in logging changed structure fields.
+ */
+#define XFS_DA_LOGOFF(BASE, ADDR) ((char *)(ADDR) - (char *)(BASE))
+#define XFS_DA_LOGRANGE(BASE, ADDR, SIZE) \
+ (uint)(XFS_DA_LOGOFF(BASE, ADDR)), \
+ (uint)(XFS_DA_LOGOFF(BASE, ADDR)+(SIZE)-1)
+
+
+#ifdef __KERNEL__
+/*========================================================================
+ * Function prototypes for the kernel.
+ *========================================================================*/
+
+/*
+ * Routines used for growing the Btree.
+ */
+int xfs_da_node_create(xfs_da_args_t *args, xfs_dablk_t blkno, int level,
+ xfs_dabuf_t **bpp, int whichfork);
+int xfs_da_split(xfs_da_state_t *state);
+
+/*
+ * Routines used for shrinking the Btree.
+ */
+int xfs_da_join(xfs_da_state_t *state);
+void xfs_da_fixhashpath(xfs_da_state_t *state,
+ xfs_da_state_path_t *path_to_to_fix);
+
+/*
+ * Routines used for finding things in the Btree.
+ */
+int xfs_da_node_lookup_int(xfs_da_state_t *state, int *result);
+int xfs_da_path_shift(xfs_da_state_t *state, xfs_da_state_path_t *path,
+ int forward, int release, int *result);
+/*
+ * Utility routines.
+ */
+int xfs_da_blk_unlink(xfs_da_state_t *state, xfs_da_state_blk_t *drop_blk,
+ xfs_da_state_blk_t *save_blk);
+int xfs_da_blk_link(xfs_da_state_t *state, xfs_da_state_blk_t *old_blk,
+ xfs_da_state_blk_t *new_blk);
+
+/*
+ * Utility routines.
+ */
+int xfs_da_grow_inode(xfs_da_args_t *args, xfs_dablk_t *new_blkno);
+int xfs_da_get_buf(struct xfs_trans *trans, struct xfs_inode *dp,
+ xfs_dablk_t bno, xfs_daddr_t mappedbno,
+ xfs_dabuf_t **bp, int whichfork);
+int xfs_da_read_buf(struct xfs_trans *trans, struct xfs_inode *dp,
+ xfs_dablk_t bno, xfs_daddr_t mappedbno,
+ xfs_dabuf_t **bpp, int whichfork);
+xfs_daddr_t xfs_da_reada_buf(struct xfs_trans *trans, struct xfs_inode *dp,
+ xfs_dablk_t bno, int whichfork);
+int xfs_da_shrink_inode(xfs_da_args_t *args, xfs_dablk_t dead_blkno,
+ xfs_dabuf_t *dead_buf);
+
+uint xfs_da_hashname(uchar_t *name_string, int name_length);
+uint xfs_da_log2_roundup(uint i);
+xfs_da_state_t *xfs_da_state_alloc(void);
+void xfs_da_state_free(xfs_da_state_t *state);
+void xfs_da_state_kill_altpath(xfs_da_state_t *state);
+
+void xfs_da_buf_done(xfs_dabuf_t *dabuf);
+void xfs_da_log_buf(struct xfs_trans *tp, xfs_dabuf_t *dabuf, uint first,
+ uint last);
+void xfs_da_brelse(struct xfs_trans *tp, xfs_dabuf_t *dabuf);
+void xfs_da_binval(struct xfs_trans *tp, xfs_dabuf_t *dabuf);
+xfs_daddr_t xfs_da_blkno(xfs_dabuf_t *dabuf);
+
+extern struct kmem_zone *xfs_da_state_zone;
+#endif /* __KERNEL__ */
+
+#endif /* __XFS_DA_BTREE_H__ */
diff --git a/fs/xfs/xfs_dfrag.c b/fs/xfs/xfs_dfrag.c
new file mode 100644
index 00000000000000..08d551a173478d
--- /dev/null
+++ b/fs/xfs/xfs_dfrag.c
@@ -0,0 +1,387 @@
+/*
+ * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_ag.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_btree.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode_item.h"
+#include "xfs_inode.h"
+#include "xfs_bmap.h"
+#include "xfs_ialloc.h"
+#include "xfs_itable.h"
+#include "xfs_dfrag.h"
+#include "xfs_error.h"
+#include "xfs_mac.h"
+#include "xfs_rw.h"
+
+/*
+ * Syssgi interface for swapext
+ */
+int
+xfs_swapext(
+ xfs_swapext_t __user *sxp)
+{
+ xfs_swapext_t sx;
+ xfs_inode_t *ip=NULL, *tip=NULL, *ips[2];
+ xfs_trans_t *tp;
+ xfs_mount_t *mp;
+ xfs_bstat_t *sbp;
+ struct file *fp = NULL, *tfp = NULL;
+ vnode_t *vp, *tvp;
+ bhv_desc_t *bdp, *tbdp;
+ vn_bhv_head_t *bhp, *tbhp;
+ uint lock_flags=0;
+ int ilf_fields, tilf_fields;
+ int error = 0;
+ xfs_ifork_t tempif, *ifp, *tifp;
+ __uint64_t tmp;
+ int aforkblks = 0;
+ int taforkblks = 0;
+ int locked = 0;
+
+ if (copy_from_user(&sx, sxp, sizeof(sx)))
+ return XFS_ERROR(EFAULT);
+
+ /* Pull information for the target fd */
+ if (((fp = fget((int)sx.sx_fdtarget)) == NULL) ||
+ ((vp = LINVFS_GET_VP(fp->f_dentry->d_inode)) == NULL)) {
+ error = XFS_ERROR(EINVAL);
+ goto error0;
+ }
+
+ bhp = VN_BHV_HEAD(vp);
+ bdp = vn_bhv_lookup(bhp, &xfs_vnodeops);
+ if (bdp == NULL) {
+ error = XFS_ERROR(EBADF);
+ goto error0;
+ } else {
+ ip = XFS_BHVTOI(bdp);
+ }
+
+ if (((tfp = fget((int)sx.sx_fdtmp)) == NULL) ||
+ ((tvp = LINVFS_GET_VP(tfp->f_dentry->d_inode)) == NULL)) {
+ error = XFS_ERROR(EINVAL);
+ goto error0;
+ }
+
+ tbhp = VN_BHV_HEAD(tvp);
+ tbdp = vn_bhv_lookup(tbhp, &xfs_vnodeops);
+ if (tbdp == NULL) {
+ error = XFS_ERROR(EBADF);
+ goto error0;
+ } else {
+ tip = XFS_BHVTOI(tbdp);
+ }
+
+ if (ip->i_mount != tip->i_mount) {
+ error = XFS_ERROR(EINVAL);
+ goto error0;
+ }
+
+ if (ip->i_ino == tip->i_ino) {
+ error = XFS_ERROR(EINVAL);
+ goto error0;
+ }
+
+ mp = ip->i_mount;
+
+ sbp = &sx.sx_stat;
+
+ if (XFS_FORCED_SHUTDOWN(mp)) {
+ error = XFS_ERROR(EIO);
+ goto error0;
+ }
+
+ locked = 1;
+
+ /* Lock in i_ino order */
+ if (ip->i_ino < tip->i_ino) {
+ ips[0] = ip;
+ ips[1] = tip;
+ } else {
+ ips[0] = tip;
+ ips[1] = ip;
+ }
+ lock_flags = XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL;
+ xfs_lock_inodes(ips, 2, 0, lock_flags);
+
+ /* Check permissions */
+ error = xfs_iaccess(ip, S_IWUSR, NULL);
+ if (error)
+ goto error0;
+
+ error = xfs_iaccess(tip, S_IWUSR, NULL);
+ if (error)
+ goto error0;
+
+ /* Verify that both files have the same format */
+ if ((ip->i_d.di_mode & S_IFMT) != (tip->i_d.di_mode & S_IFMT)) {
+ error = XFS_ERROR(EINVAL);
+ goto error0;
+ }
+
+ /* Verify both files are either real-time or non-realtime */
+ if ((ip->i_d.di_flags & XFS_DIFLAG_REALTIME) !=
+ (tip->i_d.di_flags & XFS_DIFLAG_REALTIME)) {
+ error = XFS_ERROR(EINVAL);
+ goto error0;
+ }
+
+ /* Should never get a local format */
+ if (ip->i_d.di_format == XFS_DINODE_FMT_LOCAL ||
+ tip->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
+ error = XFS_ERROR(EINVAL);
+ goto error0;
+ }
+
+ if (VN_CACHED(tvp) != 0)
+ xfs_inval_cached_pages(XFS_ITOV(tip), &(tip->i_iocore),
+ (loff_t)0, 0, 0);
+
+ /* Verify O_DIRECT for ftmp */
+ if (VN_CACHED(tvp) != 0) {
+ error = XFS_ERROR(EINVAL);
+ goto error0;
+ }
+
+ /* Verify all data are being swapped */
+ if (sx.sx_offset != 0 ||
+ sx.sx_length != ip->i_d.di_size ||
+ sx.sx_length != tip->i_d.di_size) {
+ error = XFS_ERROR(EFAULT);
+ goto error0;
+ }
+
+ /*
+ * If the target has extended attributes, the tmp file
+ * must also in order to ensure the correct data fork
+ * format.
+ */
+ if ( XFS_IFORK_Q(ip) != XFS_IFORK_Q(tip) ) {
+ error = XFS_ERROR(EINVAL);
+ goto error0;
+ }
+
+ /*
+ * Compare the current change & modify times with that
+ * passed in. If they differ, we abort this swap.
+ * This is the mechanism used to ensure the calling
+ * process that the file was not changed out from
+ * under it.
+ */
+ if ((sbp->bs_ctime.tv_sec != ip->i_d.di_ctime.t_sec) ||
+ (sbp->bs_ctime.tv_nsec != ip->i_d.di_ctime.t_nsec) ||
+ (sbp->bs_mtime.tv_sec != ip->i_d.di_mtime.t_sec) ||
+ (sbp->bs_mtime.tv_nsec != ip->i_d.di_mtime.t_nsec)) {
+ error = XFS_ERROR(EBUSY);
+ goto error0;
+ }
+
+ /* We need to fail if the file is memory mapped. Once we have tossed
+ * all existing pages, the page fault will have no option
+ * but to go to the filesystem for pages. By making the page fault call
+ * VOP_READ (or write in the case of autogrow) they block on the iolock
+ * until we have switched the extents.
+ */
+ if (VN_MAPPED(vp)) {
+ error = XFS_ERROR(EBUSY);
+ goto error0;
+ }
+
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ xfs_iunlock(tip, XFS_ILOCK_EXCL);
+
+ /*
+ * There is a race condition here since we gave up the
+ * ilock. However, the data fork will not change since
+ * we have the iolock (locked for truncation too) so we
+ * are safe. We don't really care if non-io related
+ * fields change.
+ */
+
+ VOP_TOSS_PAGES(vp, 0, -1, FI_REMAPF);
+
+ tp = xfs_trans_alloc(mp, XFS_TRANS_SWAPEXT);
+ if ((error = xfs_trans_reserve(tp, 0,
+ XFS_ICHANGE_LOG_RES(mp), 0,
+ 0, 0))) {
+ xfs_iunlock(ip, XFS_IOLOCK_EXCL);
+ xfs_iunlock(tip, XFS_IOLOCK_EXCL);
+ xfs_trans_cancel(tp, 0);
+ return error;
+ }
+ xfs_lock_inodes(ips, 2, 0, XFS_ILOCK_EXCL);
+
+ /*
+ * Count the number of extended attribute blocks
+ */
+ if ( ((XFS_IFORK_Q(ip) != 0) && (ip->i_d.di_anextents > 0)) &&
+ (ip->i_d.di_aformat != XFS_DINODE_FMT_LOCAL)) {
+ error = xfs_bmap_count_blocks(tp, ip, XFS_ATTR_FORK, &aforkblks);
+ if (error) {
+ xfs_iunlock(ip, lock_flags);
+ xfs_iunlock(tip, lock_flags);
+ xfs_trans_cancel(tp, 0);
+ return error;
+ }
+ }
+ if ( ((XFS_IFORK_Q(tip) != 0) && (tip->i_d.di_anextents > 0)) &&
+ (tip->i_d.di_aformat != XFS_DINODE_FMT_LOCAL)) {
+ error = xfs_bmap_count_blocks(tp, tip, XFS_ATTR_FORK,
+ &taforkblks);
+ if (error) {
+ xfs_iunlock(ip, lock_flags);
+ xfs_iunlock(tip, lock_flags);
+ xfs_trans_cancel(tp, 0);
+ return error;
+ }
+ }
+
+ /*
+ * Swap the data forks of the inodes
+ */
+ ifp = &ip->i_df;
+ tifp = &tip->i_df;
+ tempif = *ifp; /* struct copy */
+ *ifp = *tifp; /* struct copy */
+ *tifp = tempif; /* struct copy */
+
+ /*
+ * Fix the on-disk inode values
+ */
+ tmp = (__uint64_t)ip->i_d.di_nblocks;
+ ip->i_d.di_nblocks = tip->i_d.di_nblocks - taforkblks + aforkblks;
+ tip->i_d.di_nblocks = tmp + taforkblks - aforkblks;
+
+ tmp = (__uint64_t) ip->i_d.di_nextents;
+ ip->i_d.di_nextents = tip->i_d.di_nextents;
+ tip->i_d.di_nextents = tmp;
+
+ tmp = (__uint64_t) ip->i_d.di_format;
+ ip->i_d.di_format = tip->i_d.di_format;
+ tip->i_d.di_format = tmp;
+
+ ilf_fields = XFS_ILOG_CORE;
+
+ switch(ip->i_d.di_format) {
+ case XFS_DINODE_FMT_EXTENTS:
+ /* If the extents fit in the inode, fix the
+ * pointer. Otherwise it's already NULL or
+ * pointing to the extent.
+ */
+ if (ip->i_d.di_nextents <= XFS_INLINE_EXTS) {
+ ifp->if_u1.if_extents =
+ ifp->if_u2.if_inline_ext;
+ }
+ ilf_fields |= XFS_ILOG_DEXT;
+ break;
+ case XFS_DINODE_FMT_BTREE:
+ ilf_fields |= XFS_ILOG_DBROOT;
+ break;
+ }
+
+ tilf_fields = XFS_ILOG_CORE;
+
+ switch(tip->i_d.di_format) {
+ case XFS_DINODE_FMT_EXTENTS:
+ /* If the extents fit in the inode, fix the
+ * pointer. Otherwise it's already NULL or
+ * pointing to the extent.
+ */
+ if (tip->i_d.di_nextents <= XFS_INLINE_EXTS) {
+ tifp->if_u1.if_extents =
+ tifp->if_u2.if_inline_ext;
+ }
+ tilf_fields |= XFS_ILOG_DEXT;
+ break;
+ case XFS_DINODE_FMT_BTREE:
+ tilf_fields |= XFS_ILOG_DBROOT;
+ break;
+ }
+
+ /*
+ * Increment vnode ref counts since xfs_trans_commit &
+ * xfs_trans_cancel will both unlock the inodes and
+ * decrement the associated ref counts.
+ */
+ VN_HOLD(vp);
+ VN_HOLD(tvp);
+
+ xfs_trans_ijoin(tp, ip, lock_flags);
+ xfs_trans_ijoin(tp, tip, lock_flags);
+
+ xfs_trans_log_inode(tp, ip, ilf_fields);
+ xfs_trans_log_inode(tp, tip, tilf_fields);
+
+ /*
+ * If this is a synchronous mount, make sure that the
+ * transaction goes to disk before returning to the user.
+ */
+ if (mp->m_flags & XFS_MOUNT_WSYNC) {
+ xfs_trans_set_sync(tp);
+ }
+
+ error = xfs_trans_commit(tp, XFS_TRANS_SWAPEXT, NULL);
+
+ fput(fp);
+ fput(tfp);
+
+ return error;
+
+ error0:
+ if (locked) {
+ xfs_iunlock(ip, lock_flags);
+ xfs_iunlock(tip, lock_flags);
+ }
+
+ if (fp != NULL) fput(fp);
+ if (tfp != NULL) fput(tfp);
+
+ return error;
+}
diff --git a/fs/xfs/xfs_dfrag.h b/fs/xfs/xfs_dfrag.h
new file mode 100644
index 00000000000000..904860594b8f1f
--- /dev/null
+++ b/fs/xfs/xfs_dfrag.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_DFRAG_H__
+#define __XFS_DFRAG_H__
+
+/*
+ * Structure passed to xfs_swapext
+ */
+
+typedef struct xfs_swapext
+{
+ __int64_t sx_version; /* version */
+ __int64_t sx_fdtarget; /* fd of target file */
+ __int64_t sx_fdtmp; /* fd of tmp file */
+ xfs_off_t sx_offset; /* offset into file */
+ xfs_off_t sx_length; /* leng from offset */
+ char sx_pad[16]; /* pad space, unused */
+ xfs_bstat_t sx_stat; /* stat of target b4 copy */
+} xfs_swapext_t;
+
+/*
+ * Version flag
+ */
+#define XFS_SX_VERSION 0
+
+#ifdef __KERNEL__
+/*
+ * Prototypes for visible xfs_dfrag.c routines.
+ */
+
+/*
+ * Syscall interface for xfs_swapext
+ */
+int xfs_swapext(struct xfs_swapext __user *sx);
+
+#endif /* __KERNEL__ */
+
+#endif /* __XFS_DFRAG_H__ */
diff --git a/fs/xfs/xfs_dinode.h b/fs/xfs/xfs_dinode.h
new file mode 100644
index 00000000000000..d395f783833a16
--- /dev/null
+++ b/fs/xfs/xfs_dinode.h
@@ -0,0 +1,480 @@
+/*
+ * Copyright (c) 2000, 2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_DINODE_H__
+#define __XFS_DINODE_H__
+
+struct xfs_buf;
+struct xfs_mount;
+
+#define XFS_DINODE_VERSION_1 1
+#define XFS_DINODE_VERSION_2 2
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DINODE_GOOD_VERSION)
+int xfs_dinode_good_version(int v);
+#define XFS_DINODE_GOOD_VERSION(v) xfs_dinode_good_version(v)
+#else
+#define XFS_DINODE_GOOD_VERSION(v) (((v) == XFS_DINODE_VERSION_1) || \
+ ((v) == XFS_DINODE_VERSION_2))
+#endif
+#define XFS_DINODE_MAGIC 0x494e /* 'IN' */
+
+/*
+ * Disk inode structure.
+ * This is just the header; the inode is expanded to fill a variable size
+ * with the last field expanding. It is split into the core and "other"
+ * because we only need the core part in the in-core inode.
+ */
+typedef struct xfs_timestamp {
+ __int32_t t_sec; /* timestamp seconds */
+ __int32_t t_nsec; /* timestamp nanoseconds */
+} xfs_timestamp_t;
+
+/*
+ * Note: Coordinate changes to this structure with the XFS_DI_* #defines
+ * below and the offsets table in xfs_ialloc_log_di().
+ */
+typedef struct xfs_dinode_core
+{
+ __uint16_t di_magic; /* inode magic # = XFS_DINODE_MAGIC */
+ __uint16_t di_mode; /* mode and type of file */
+ __int8_t di_version; /* inode version */
+ __int8_t di_format; /* format of di_c data */
+ __uint16_t di_onlink; /* old number of links to file */
+ __uint32_t di_uid; /* owner's user id */
+ __uint32_t di_gid; /* owner's group id */
+ __uint32_t di_nlink; /* number of links to file */
+ __uint16_t di_projid; /* owner's project id */
+ __uint8_t di_pad[8]; /* unused, zeroed space */
+ __uint16_t di_flushiter; /* incremented on flush */
+ xfs_timestamp_t di_atime; /* time last accessed */
+ xfs_timestamp_t di_mtime; /* time last modified */
+ xfs_timestamp_t di_ctime; /* time created/inode modified */
+ xfs_fsize_t di_size; /* number of bytes in file */
+ xfs_drfsbno_t di_nblocks; /* # of direct & btree blocks used */
+ xfs_extlen_t di_extsize; /* basic/minimum extent size for file */
+ xfs_extnum_t di_nextents; /* number of extents in data fork */
+ xfs_aextnum_t di_anextents; /* number of extents in attribute fork*/
+ __uint8_t di_forkoff; /* attr fork offs, <<3 for 64b align */
+ __int8_t di_aformat; /* format of attr fork's data */
+ __uint32_t di_dmevmask; /* DMIG event mask */
+ __uint16_t di_dmstate; /* DMIG state info */
+ __uint16_t di_flags; /* random flags, XFS_DIFLAG_... */
+ __uint32_t di_gen; /* generation number */
+} xfs_dinode_core_t;
+
+#define DI_MAX_FLUSH 0xffff
+
+typedef struct xfs_dinode
+{
+ xfs_dinode_core_t di_core;
+ /*
+ * In adding anything between the core and the union, be
+ * sure to update the macros like XFS_LITINO below and
+ * XFS_BMAP_RBLOCK_DSIZE in xfs_bmap_btree.h.
+ */
+ xfs_agino_t di_next_unlinked;/* agi unlinked list ptr */
+ union {
+ xfs_bmdr_block_t di_bmbt; /* btree root block */
+ xfs_bmbt_rec_32_t di_bmx[1]; /* extent list */
+ xfs_dir_shortform_t di_dirsf; /* shortform directory */
+ xfs_dir2_sf_t di_dir2sf; /* shortform directory v2 */
+ char di_c[1]; /* local contents */
+ xfs_dev_t di_dev; /* device for S_IFCHR/S_IFBLK */
+ uuid_t di_muuid; /* mount point value */
+ char di_symlink[1]; /* local symbolic link */
+ } di_u;
+ union {
+ xfs_bmdr_block_t di_abmbt; /* btree root block */
+ xfs_bmbt_rec_32_t di_abmx[1]; /* extent list */
+ xfs_attr_shortform_t di_attrsf; /* shortform attribute list */
+ } di_a;
+} xfs_dinode_t;
+
+/*
+ * The 32 bit link count in the inode theoretically maxes out at UINT_MAX.
+ * Since the pathconf interface is signed, we use 2^31 - 1 instead.
+ * The old inode format had a 16 bit link count, so its maximum is USHRT_MAX.
+ */
+#define XFS_MAXLINK ((1U << 31) - 1U)
+#define XFS_MAXLINK_1 65535U
+
+/*
+ * Bit names for logging disk inodes only
+ */
+#define XFS_DI_MAGIC 0x0000001
+#define XFS_DI_MODE 0x0000002
+#define XFS_DI_VERSION 0x0000004
+#define XFS_DI_FORMAT 0x0000008
+#define XFS_DI_ONLINK 0x0000010
+#define XFS_DI_UID 0x0000020
+#define XFS_DI_GID 0x0000040
+#define XFS_DI_NLINK 0x0000080
+#define XFS_DI_PROJID 0x0000100
+#define XFS_DI_PAD 0x0000200
+#define XFS_DI_ATIME 0x0000400
+#define XFS_DI_MTIME 0x0000800
+#define XFS_DI_CTIME 0x0001000
+#define XFS_DI_SIZE 0x0002000
+#define XFS_DI_NBLOCKS 0x0004000
+#define XFS_DI_EXTSIZE 0x0008000
+#define XFS_DI_NEXTENTS 0x0010000
+#define XFS_DI_NAEXTENTS 0x0020000
+#define XFS_DI_FORKOFF 0x0040000
+#define XFS_DI_AFORMAT 0x0080000
+#define XFS_DI_DMEVMASK 0x0100000
+#define XFS_DI_DMSTATE 0x0200000
+#define XFS_DI_FLAGS 0x0400000
+#define XFS_DI_GEN 0x0800000
+#define XFS_DI_NEXT_UNLINKED 0x1000000
+#define XFS_DI_U 0x2000000
+#define XFS_DI_A 0x4000000
+#define XFS_DI_NUM_BITS 27
+#define XFS_DI_ALL_BITS ((1 << XFS_DI_NUM_BITS) - 1)
+#define XFS_DI_CORE_BITS (XFS_DI_ALL_BITS & ~(XFS_DI_U|XFS_DI_A))
+
+/*
+ * Values for di_format
+ */
+typedef enum xfs_dinode_fmt
+{
+ XFS_DINODE_FMT_DEV, /* CHR, BLK: di_dev */
+ XFS_DINODE_FMT_LOCAL, /* DIR, REG: di_c */
+ /* LNK: di_symlink */
+ XFS_DINODE_FMT_EXTENTS, /* DIR, REG, LNK: di_bmx */
+ XFS_DINODE_FMT_BTREE, /* DIR, REG, LNK: di_bmbt */
+ XFS_DINODE_FMT_UUID /* MNT: di_uuid */
+} xfs_dinode_fmt_t;
+
+/*
+ * Inode minimum and maximum sizes.
+ */
+#define XFS_DINODE_MIN_LOG 8
+#define XFS_DINODE_MAX_LOG 11
+#define XFS_DINODE_MIN_SIZE (1 << XFS_DINODE_MIN_LOG)
+#define XFS_DINODE_MAX_SIZE (1 << XFS_DINODE_MAX_LOG)
+
+/*
+ * Inode size for given fs.
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_LITINO)
+int xfs_litino(struct xfs_mount *mp);
+#define XFS_LITINO(mp) xfs_litino(mp)
+#else
+#define XFS_LITINO(mp) ((mp)->m_litino)
+#endif
+#define XFS_BROOT_SIZE_ADJ \
+ (sizeof(xfs_bmbt_block_t) - sizeof(xfs_bmdr_block_t))
+
+/*
+ * Fork identifiers. Here so utilities can use them without including
+ * xfs_inode.h.
+ */
+#define XFS_DATA_FORK 0
+#define XFS_ATTR_FORK 1
+
+/*
+ * Inode data & attribute fork sizes, per inode.
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_CFORK_Q)
+int xfs_cfork_q_arch(xfs_dinode_core_t *dcp, xfs_arch_t arch);
+int xfs_cfork_q(xfs_dinode_core_t *dcp);
+#define XFS_CFORK_Q_ARCH(dcp,arch) xfs_cfork_q_arch(dcp,arch)
+#define XFS_CFORK_Q(dcp) xfs_cfork_q(dcp)
+#else
+#define XFS_CFORK_Q_ARCH(dcp,arch) (!INT_ISZERO((dcp)->di_forkoff, arch))
+#define XFS_CFORK_Q(dcp) ((dcp)->di_forkoff != 0)
+
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_CFORK_BOFF)
+int xfs_cfork_boff_arch(xfs_dinode_core_t *dcp, xfs_arch_t arch);
+int xfs_cfork_boff(xfs_dinode_core_t *dcp);
+#define XFS_CFORK_BOFF_ARCH(dcp,arch) xfs_cfork_boff_arch(dcp,arch)
+#define XFS_CFORK_BOFF(dcp) xfs_cfork_boff(dcp)
+#else
+#define XFS_CFORK_BOFF_ARCH(dcp,arch) ((int)(INT_GET((dcp)->di_forkoff, arch) << 3))
+#define XFS_CFORK_BOFF(dcp) ((int)((dcp)->di_forkoff << 3))
+
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_CFORK_DSIZE)
+int xfs_cfork_dsize_arch(xfs_dinode_core_t *dcp, struct xfs_mount *mp, xfs_arch_t arch);
+int xfs_cfork_dsize(xfs_dinode_core_t *dcp, struct xfs_mount *mp);
+#define XFS_CFORK_DSIZE_ARCH(dcp,mp,arch) xfs_cfork_dsize_arch(dcp,mp,arch)
+#define XFS_CFORK_DSIZE(dcp,mp) xfs_cfork_dsize(dcp,mp)
+#else
+#define XFS_CFORK_DSIZE_ARCH(dcp,mp,arch) \
+ (XFS_CFORK_Q_ARCH(dcp, arch) ? XFS_CFORK_BOFF_ARCH(dcp, arch) : XFS_LITINO(mp))
+#define XFS_CFORK_DSIZE(dcp,mp) \
+ (XFS_CFORK_Q(dcp) ? XFS_CFORK_BOFF(dcp) : XFS_LITINO(mp))
+
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_CFORK_ASIZE)
+int xfs_cfork_asize_arch(xfs_dinode_core_t *dcp, struct xfs_mount *mp, xfs_arch_t arch);
+int xfs_cfork_asize(xfs_dinode_core_t *dcp, struct xfs_mount *mp);
+#define XFS_CFORK_ASIZE_ARCH(dcp,mp,arch) xfs_cfork_asize_arch(dcp,mp,arch)
+#define XFS_CFORK_ASIZE(dcp,mp) xfs_cfork_asize(dcp,mp)
+#else
+#define XFS_CFORK_ASIZE_ARCH(dcp,mp,arch) \
+ (XFS_CFORK_Q_ARCH(dcp, arch) ? XFS_LITINO(mp) - XFS_CFORK_BOFF_ARCH(dcp, arch) : 0)
+#define XFS_CFORK_ASIZE(dcp,mp) \
+ (XFS_CFORK_Q(dcp) ? XFS_LITINO(mp) - XFS_CFORK_BOFF(dcp) : 0)
+
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_CFORK_SIZE)
+int xfs_cfork_size_arch(xfs_dinode_core_t *dcp, struct xfs_mount *mp, int w, xfs_arch_t arch);
+int xfs_cfork_size(xfs_dinode_core_t *dcp, struct xfs_mount *mp, int w);
+#define XFS_CFORK_SIZE_ARCH(dcp,mp,w,arch) xfs_cfork_size_arch(dcp,mp,w,arch)
+#define XFS_CFORK_SIZE(dcp,mp,w) xfs_cfork_size(dcp,mp,w)
+#else
+#define XFS_CFORK_SIZE_ARCH(dcp,mp,w,arch) \
+ ((w) == XFS_DATA_FORK ? \
+ XFS_CFORK_DSIZE_ARCH(dcp, mp, arch) : XFS_CFORK_ASIZE_ARCH(dcp, mp, arch))
+#define XFS_CFORK_SIZE(dcp,mp,w) \
+ ((w) == XFS_DATA_FORK ? \
+ XFS_CFORK_DSIZE(dcp, mp) : XFS_CFORK_ASIZE(dcp, mp))
+
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DFORK_DSIZE)
+int xfs_dfork_dsize_arch(xfs_dinode_t *dip, struct xfs_mount *mp, xfs_arch_t arch);
+int xfs_dfork_dsize(xfs_dinode_t *dip, struct xfs_mount *mp);
+#define XFS_DFORK_DSIZE_ARCH(dip,mp,arch) xfs_dfork_dsize_arch(dip,mp,arch)
+#define XFS_DFORK_DSIZE(dip,mp) xfs_dfork_dsize(dip,mp)
+#else
+#define XFS_DFORK_DSIZE_ARCH(dip,mp,arch) XFS_CFORK_DSIZE_ARCH(&(dip)->di_core, mp, arch)
+#define XFS_DFORK_DSIZE(dip,mp) XFS_DFORK_DSIZE_ARCH(dip,mp,ARCH_NOCONVERT)
+
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DFORK_ASIZE)
+int xfs_dfork_asize_arch(xfs_dinode_t *dip, struct xfs_mount *mp, xfs_arch_t arch);
+int xfs_dfork_asize(xfs_dinode_t *dip, struct xfs_mount *mp);
+#define XFS_DFORK_ASIZE_ARCH(dip,mp,arch) xfs_dfork_asize_arch(dip,mp,arch)
+#define XFS_DFORK_ASIZE(dip,mp) xfs_dfork_asize(dip,mp)
+#else
+#define XFS_DFORK_ASIZE_ARCH(dip,mp,arch) XFS_CFORK_ASIZE_ARCH(&(dip)->di_core, mp, arch)
+#define XFS_DFORK_ASIZE(dip,mp) XFS_DFORK_ASIZE_ARCH(dip,mp,ARCH_NOCONVERT)
+
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DFORK_SIZE)
+int xfs_dfork_size_arch(xfs_dinode_t *dip, struct xfs_mount *mp, int w, xfs_arch_t arch);
+int xfs_dfork_size(xfs_dinode_t *dip, struct xfs_mount *mp, int w);
+#define XFS_DFORK_SIZE_ARCH(dip,mp,w,arch) xfs_dfork_size_arch(dip,mp,w,arch)
+#define XFS_DFORK_SIZE(dip,mp,w) xfs_dfork_size(dip,mp,w)
+#else
+#define XFS_DFORK_SIZE_ARCH(dip,mp,w,arch) XFS_CFORK_SIZE_ARCH(&(dip)->di_core, mp, w, arch)
+#define XFS_DFORK_SIZE(dip,mp,w) XFS_DFORK_SIZE_ARCH(dip,mp,w,ARCH_NOCONVERT)
+
+#endif
+
+/*
+ * Macros for accessing per-fork disk inode information.
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DFORK_Q)
+int xfs_dfork_q_arch(xfs_dinode_t *dip, xfs_arch_t arch);
+int xfs_dfork_q(xfs_dinode_t *dip);
+#define XFS_DFORK_Q_ARCH(dip,arch) xfs_dfork_q_arch(dip,arch)
+#define XFS_DFORK_Q(dip) xfs_dfork_q(dip)
+#else
+#define XFS_DFORK_Q_ARCH(dip,arch) XFS_CFORK_Q_ARCH(&(dip)->di_core, arch)
+#define XFS_DFORK_Q(dip) XFS_DFORK_Q_ARCH(dip,ARCH_NOCONVERT)
+
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DFORK_BOFF)
+int xfs_dfork_boff_arch(xfs_dinode_t *dip, xfs_arch_t arch);
+int xfs_dfork_boff(xfs_dinode_t *dip);
+#define XFS_DFORK_BOFF_ARCH(dip,arch) xfs_dfork_boff_arch(dip,arch)
+#define XFS_DFORK_BOFF(dip) xfs_dfork_boff(dip)
+#else
+#define XFS_DFORK_BOFF_ARCH(dip,arch) XFS_CFORK_BOFF_ARCH(&(dip)->di_core, arch)
+#define XFS_DFORK_BOFF(dip) XFS_DFORK_BOFF_ARCH(dip,ARCH_NOCONVERT)
+
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DFORK_DPTR)
+char *xfs_dfork_dptr_arch(xfs_dinode_t *dip, xfs_arch_t arch);
+char *xfs_dfork_dptr(xfs_dinode_t *dip);
+#define XFS_DFORK_DPTR_ARCH(dip,arch) xfs_dfork_dptr_arch(dip,arch)
+#define XFS_DFORK_DPTR(dip) xfs_dfork_dptr(dip)
+#else
+#define XFS_DFORK_DPTR_ARCH(dip,arch) ((dip)->di_u.di_c)
+#define XFS_DFORK_DPTR(dip) XFS_DFORK_DPTR_ARCH(dip,ARCH_NOCONVERT)
+
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DFORK_APTR)
+char *xfs_dfork_aptr_arch(xfs_dinode_t *dip, xfs_arch_t arch);
+char *xfs_dfork_aptr(xfs_dinode_t *dip);
+#define XFS_DFORK_APTR_ARCH(dip,arch) xfs_dfork_aptr_arch(dip,arch)
+#define XFS_DFORK_APTR(dip) xfs_dfork_aptr(dip)
+#else
+#define XFS_DFORK_APTR_ARCH(dip,arch) ((dip)->di_u.di_c + XFS_DFORK_BOFF_ARCH(dip, arch))
+#define XFS_DFORK_APTR(dip) XFS_DFORK_APTR_ARCH(dip,ARCH_NOCONVERT)
+
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DFORK_PTR)
+char *xfs_dfork_ptr_arch(xfs_dinode_t *dip, int w, xfs_arch_t arch);
+char *xfs_dfork_ptr(xfs_dinode_t *dip, int w);
+#define XFS_DFORK_PTR_ARCH(dip,w,arch) xfs_dfork_ptr_arch(dip,w,arch)
+#define XFS_DFORK_PTR(dip,w) xfs_dfork_ptr(dip,w)
+#else
+#define XFS_DFORK_PTR_ARCH(dip,w,arch) \
+ ((w) == XFS_DATA_FORK ? XFS_DFORK_DPTR_ARCH(dip, arch) : XFS_DFORK_APTR_ARCH(dip, arch))
+#define XFS_DFORK_PTR(dip,w) XFS_DFORK_PTR_ARCH(dip,w,ARCH_NOCONVERT)
+
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_CFORK_FORMAT)
+int xfs_cfork_format_arch(xfs_dinode_core_t *dcp, int w, xfs_arch_t arch);
+int xfs_cfork_format(xfs_dinode_core_t *dcp, int w);
+#define XFS_CFORK_FORMAT_ARCH(dcp,w,arch) xfs_cfork_format_arch(dcp,w,arch)
+#define XFS_CFORK_FORMAT(dcp,w) xfs_cfork_format(dcp,w)
+#else
+#define XFS_CFORK_FORMAT_ARCH(dcp,w,arch) \
+ ((w) == XFS_DATA_FORK ? INT_GET((dcp)->di_format, arch) : INT_GET((dcp)->di_aformat, arch))
+#define XFS_CFORK_FORMAT(dcp,w) XFS_CFORK_FORMAT_ARCH(dcp,w,ARCH_NOCONVERT)
+
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_CFORK_FMT_SET)
+void xfs_cfork_fmt_set_arch(xfs_dinode_core_t *dcp, int w, int n, xfs_arch_t arch);
+void xfs_cfork_fmt_set(xfs_dinode_core_t *dcp, int w, int n);
+#define XFS_CFORK_FMT_SET_ARCH(dcp,w,n,arch) xfs_cfork_fmt_set_arch(dcp,w,n,arch)
+#define XFS_CFORK_FMT_SET(dcp,w,n) xfs_cfork_fmt_set(dcp,w,n)
+#else
+#define XFS_CFORK_FMT_SET_ARCH(dcp,w,n,arch) \
+ ((w) == XFS_DATA_FORK ? \
+ (INT_SET((dcp)->di_format, arch, (n))) : \
+ (INT_SET((dcp)->di_aformat, arch, (n))))
+#define XFS_CFORK_FMT_SET(dcp,w,n) XFS_CFORK_FMT_SET_ARCH(dcp,w,n,ARCH_NOCONVERT)
+
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_CFORK_NEXTENTS)
+int xfs_cfork_nextents_arch(xfs_dinode_core_t *dcp, int w, xfs_arch_t arch);
+int xfs_cfork_nextents(xfs_dinode_core_t *dcp, int w);
+#define XFS_CFORK_NEXTENTS_ARCH(dcp,w,arch) xfs_cfork_nextents_arch(dcp,w,arch)
+#define XFS_CFORK_NEXTENTS(dcp,w) xfs_cfork_nextents(dcp,w)
+#else
+#define XFS_CFORK_NEXTENTS_ARCH(dcp,w,arch) \
+ ((w) == XFS_DATA_FORK ? INT_GET((dcp)->di_nextents, arch) : INT_GET((dcp)->di_anextents, arch))
+#define XFS_CFORK_NEXTENTS(dcp,w) XFS_CFORK_NEXTENTS_ARCH(dcp,w,ARCH_NOCONVERT)
+
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_CFORK_NEXT_SET)
+void xfs_cfork_next_set_arch(xfs_dinode_core_t *dcp, int w, int n, xfs_arch_t arch);
+void xfs_cfork_next_set(xfs_dinode_core_t *dcp, int w, int n);
+#define XFS_CFORK_NEXT_SET_ARCH(dcp,w,n,arch) xfs_cfork_next_set_arch(dcp,w,n,arch)
+#define XFS_CFORK_NEXT_SET(dcp,w,n) xfs_cfork_next_set(dcp,w,n)
+#else
+#define XFS_CFORK_NEXT_SET_ARCH(dcp,w,n,arch) \
+ ((w) == XFS_DATA_FORK ? \
+ (INT_SET((dcp)->di_nextents, arch, (n))) : \
+ (INT_SET((dcp)->di_anextents, arch, (n))))
+#define XFS_CFORK_NEXT_SET(dcp,w,n) XFS_CFORK_NEXT_SET_ARCH(dcp,w,n,ARCH_NOCONVERT)
+
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DFORK_FORMAT)
+int xfs_dfork_format_arch(xfs_dinode_t *dip, int w, xfs_arch_t arch);
+int xfs_dfork_format(xfs_dinode_t *dip, int w);
+#define XFS_DFORK_FORMAT_ARCH(dip,w,arch) xfs_dfork_format_arch(dip,w,arch)
+#define XFS_DFORK_FORMAT(dip,w) xfs_dfork_format(dip,w)
+#else
+#define XFS_DFORK_FORMAT_ARCH(dip,w,arch) XFS_CFORK_FORMAT_ARCH(&(dip)->di_core, w, arch)
+#define XFS_DFORK_FORMAT(dip,w) XFS_DFORK_FORMAT_ARCH(dip,w,ARCH_NOCONVERT)
+
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DFORK_FMT_SET)
+void xfs_dfork_fmt_set_arch(xfs_dinode_t *dip, int w, int n, xfs_arch_t arch);
+void xfs_dfork_fmt_set(xfs_dinode_t *dip, int w, int n);
+#define XFS_DFORK_FMT_SET_ARCH(dip,w,n,arch) xfs_dfork_fmt_set_arch(dip,w,n,arch)
+#define XFS_DFORK_FMT_SET(dip,w,n) xfs_dfork_fmt_set(dip,w,n)
+#else
+#define XFS_DFORK_FMT_SET_ARCH(dip,w,n,arch) XFS_CFORK_FMT_SET_ARCH(&(dip)->di_core, w, n, arch)
+#define XFS_DFORK_FMT_SET(dip,w,n) XFS_DFORK_FMT_SET_ARCH(dip,w,n,ARCH_NOCONVERT)
+
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DFORK_NEXTENTS)
+int xfs_dfork_nextents_arch(xfs_dinode_t *dip, int w, xfs_arch_t arch);
+int xfs_dfork_nextents(xfs_dinode_t *dip, int w);
+#define XFS_DFORK_NEXTENTS_ARCH(dip,w,arch) xfs_dfork_nextents_arch(dip,w,arch)
+#define XFS_DFORK_NEXTENTS(dip,w) xfs_dfork_nextents(dip,w)
+#else
+#define XFS_DFORK_NEXTENTS_ARCH(dip,w,arch) XFS_CFORK_NEXTENTS_ARCH(&(dip)->di_core, w, arch)
+#define XFS_DFORK_NEXTENTS(dip,w) XFS_DFORK_NEXTENTS_ARCH(dip,w,ARCH_NOCONVERT)
+
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DFORK_NEXT_SET)
+void xfs_dfork_next_set_arch(xfs_dinode_t *dip, int w, int n, xfs_arch_t arch);
+void xfs_dfork_next_set(xfs_dinode_t *dip, int w, int n);
+#define XFS_DFORK_NEXT_SET_ARCH(dip,w,n,arch) xfs_dfork_next_set_arch(dip,w,n,arch)
+#define XFS_DFORK_NEXT_SET(dip,w,n) xfs_dfork_next_set(dip,w,n)
+#else
+#define XFS_DFORK_NEXT_SET_ARCH(dip,w,n,arch) XFS_CFORK_NEXT_SET_ARCH(&(dip)->di_core, w, n, arch)
+#define XFS_DFORK_NEXT_SET(dip,w,n) XFS_DFORK_NEXT_SET_ARCH(dip,w,n,ARCH_NOCONVERT)
+
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BUF_TO_DINODE)
+xfs_dinode_t *xfs_buf_to_dinode(struct xfs_buf *bp);
+#define XFS_BUF_TO_DINODE(bp) xfs_buf_to_dinode(bp)
+#else
+#define XFS_BUF_TO_DINODE(bp) ((xfs_dinode_t *)(XFS_BUF_PTR(bp)))
+#endif
+
+/*
+ * Values for di_flags
+ * There should be a one-to-one correspondence between these flags and the
+ * XFS_XFLAG_s.
+ */
+#define XFS_DIFLAG_REALTIME_BIT 0 /* file's blocks come from rt area */
+#define XFS_DIFLAG_PREALLOC_BIT 1 /* file space has been preallocated */
+#define XFS_DIFLAG_NEWRTBM_BIT 2 /* for rtbitmap inode, new format */
+#define XFS_DIFLAG_IMMUTABLE_BIT 3 /* inode is immutable */
+#define XFS_DIFLAG_APPEND_BIT 4 /* inode is append-only */
+#define XFS_DIFLAG_SYNC_BIT 5 /* inode is written synchronously */
+#define XFS_DIFLAG_NOATIME_BIT 6 /* do not update atime */
+#define XFS_DIFLAG_NODUMP_BIT 7 /* do not dump */
+#define XFS_DIFLAG_RTINHERIT_BIT 8 /* create with realtime bit set */
+#define XFS_DIFLAG_PROJINHERIT_BIT 9 /* create with parents projid */
+#define XFS_DIFLAG_NOSYMLINKS_BIT 10 /* disallow symlink creation */
+#define XFS_DIFLAG_REALTIME (1 << XFS_DIFLAG_REALTIME_BIT)
+#define XFS_DIFLAG_PREALLOC (1 << XFS_DIFLAG_PREALLOC_BIT)
+#define XFS_DIFLAG_NEWRTBM (1 << XFS_DIFLAG_NEWRTBM_BIT)
+#define XFS_DIFLAG_IMMUTABLE (1 << XFS_DIFLAG_IMMUTABLE_BIT)
+#define XFS_DIFLAG_APPEND (1 << XFS_DIFLAG_APPEND_BIT)
+#define XFS_DIFLAG_SYNC (1 << XFS_DIFLAG_SYNC_BIT)
+#define XFS_DIFLAG_NOATIME (1 << XFS_DIFLAG_NOATIME_BIT)
+#define XFS_DIFLAG_NODUMP (1 << XFS_DIFLAG_NODUMP_BIT)
+#define XFS_DIFLAG_RTINHERIT (1 << XFS_DIFLAG_RTINHERIT_BIT)
+#define XFS_DIFLAG_PROJINHERIT (1 << XFS_DIFLAG_PROJINHERIT_BIT)
+#define XFS_DIFLAG_NOSYMLINKS (1 << XFS_DIFLAG_NOSYMLINKS_BIT)
+
+#define XFS_DIFLAG_ANY \
+ (XFS_DIFLAG_REALTIME | XFS_DIFLAG_PREALLOC | XFS_DIFLAG_NEWRTBM | \
+ XFS_DIFLAG_IMMUTABLE | XFS_DIFLAG_APPEND | XFS_DIFLAG_SYNC | \
+ XFS_DIFLAG_NOATIME | XFS_DIFLAG_NODUMP | XFS_DIFLAG_RTINHERIT | \
+ XFS_DIFLAG_PROJINHERIT | XFS_DIFLAG_NOSYMLINKS)
+
+#endif /* __XFS_DINODE_H__ */
diff --git a/fs/xfs/xfs_dir.c b/fs/xfs/xfs_dir.c
new file mode 100644
index 00000000000000..3ea04f9ac2310b
--- /dev/null
+++ b/fs/xfs/xfs_dir.c
@@ -0,0 +1,1223 @@
+/*
+ * Copyright (c) 2000-2001 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_alloc.h"
+#include "xfs_btree.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode.h"
+#include "xfs_bmap.h"
+#include "xfs_da_btree.h"
+#include "xfs_dir_leaf.h"
+#include "xfs_error.h"
+
+/*
+ * xfs_dir.c
+ *
+ * Provide the external interfaces to manage directories.
+ */
+
+/*========================================================================
+ * Function prototypes for the kernel.
+ *========================================================================*/
+
+/*
+ * Functions for the dirops interfaces.
+ */
+static void xfs_dir_mount(struct xfs_mount *mp);
+
+static int xfs_dir_isempty(struct xfs_inode *dp);
+
+static int xfs_dir_init(struct xfs_trans *trans,
+ struct xfs_inode *dir,
+ struct xfs_inode *parent_dir);
+
+static int xfs_dir_createname(struct xfs_trans *trans,
+ struct xfs_inode *dp,
+ char *name_string,
+ int name_len,
+ xfs_ino_t inode_number,
+ xfs_fsblock_t *firstblock,
+ xfs_bmap_free_t *flist,
+ xfs_extlen_t total);
+
+static int xfs_dir_lookup(struct xfs_trans *tp,
+ struct xfs_inode *dp,
+ char *name_string,
+ int name_length,
+ xfs_ino_t *inode_number);
+
+static int xfs_dir_removename(struct xfs_trans *trans,
+ struct xfs_inode *dp,
+ char *name_string,
+ int name_length,
+ xfs_ino_t ino,
+ xfs_fsblock_t *firstblock,
+ xfs_bmap_free_t *flist,
+ xfs_extlen_t total);
+
+static int xfs_dir_getdents(struct xfs_trans *tp,
+ struct xfs_inode *dp,
+ struct uio *uiop,
+ int *eofp);
+
+static int xfs_dir_replace(struct xfs_trans *tp,
+ struct xfs_inode *dp,
+ char *name_string,
+ int name_length,
+ xfs_ino_t inode_number,
+ xfs_fsblock_t *firstblock,
+ xfs_bmap_free_t *flist,
+ xfs_extlen_t total);
+
+static int xfs_dir_canenter(struct xfs_trans *tp,
+ struct xfs_inode *dp,
+ char *name_string,
+ int name_length);
+
+static int xfs_dir_shortform_validate_ondisk(xfs_mount_t *mp,
+ xfs_dinode_t *dip);
+
+xfs_dirops_t xfsv1_dirops = {
+ .xd_mount = xfs_dir_mount,
+ .xd_isempty = xfs_dir_isempty,
+ .xd_init = xfs_dir_init,
+ .xd_createname = xfs_dir_createname,
+ .xd_lookup = xfs_dir_lookup,
+ .xd_removename = xfs_dir_removename,
+ .xd_getdents = xfs_dir_getdents,
+ .xd_replace = xfs_dir_replace,
+ .xd_canenter = xfs_dir_canenter,
+ .xd_shortform_validate_ondisk = xfs_dir_shortform_validate_ondisk,
+ .xd_shortform_to_single = xfs_dir_shortform_to_leaf,
+};
+
+/*
+ * Internal routines when dirsize == XFS_LBSIZE(mp).
+ */
+STATIC int xfs_dir_leaf_lookup(xfs_da_args_t *args);
+STATIC int xfs_dir_leaf_removename(xfs_da_args_t *args, int *number_entries,
+ int *total_namebytes);
+STATIC int xfs_dir_leaf_getdents(xfs_trans_t *trans, xfs_inode_t *dp,
+ uio_t *uio, int *eofp,
+ xfs_dirent_t *dbp,
+ xfs_dir_put_t put);
+STATIC int xfs_dir_leaf_replace(xfs_da_args_t *args);
+
+/*
+ * Internal routines when dirsize > XFS_LBSIZE(mp).
+ */
+STATIC int xfs_dir_node_addname(xfs_da_args_t *args);
+STATIC int xfs_dir_node_lookup(xfs_da_args_t *args);
+STATIC int xfs_dir_node_removename(xfs_da_args_t *args);
+STATIC int xfs_dir_node_getdents(xfs_trans_t *trans, xfs_inode_t *dp,
+ uio_t *uio, int *eofp,
+ xfs_dirent_t *dbp,
+ xfs_dir_put_t put);
+STATIC int xfs_dir_node_replace(xfs_da_args_t *args);
+
+#if defined(XFS_DIR_TRACE)
+ktrace_t *xfs_dir_trace_buf;
+#endif
+
+
+/*========================================================================
+ * Overall external interface routines.
+ *========================================================================*/
+
+xfs_dahash_t xfs_dir_hash_dot, xfs_dir_hash_dotdot;
+
+/*
+ * One-time startup routine called from xfs_init().
+ */
+void
+xfs_dir_startup(void)
+{
+ xfs_dir_hash_dot = xfs_da_hashname(".", 1);
+ xfs_dir_hash_dotdot = xfs_da_hashname("..", 2);
+}
+
+/*
+ * Initialize directory-related fields in the mount structure.
+ */
+static void
+xfs_dir_mount(xfs_mount_t *mp)
+{
+ uint shortcount, leafcount, count;
+
+ mp->m_dirversion = 1;
+ shortcount = (mp->m_attroffset - (uint)sizeof(xfs_dir_sf_hdr_t)) /
+ (uint)sizeof(xfs_dir_sf_entry_t);
+ leafcount = (XFS_LBSIZE(mp) - (uint)sizeof(xfs_dir_leaf_hdr_t)) /
+ ((uint)sizeof(xfs_dir_leaf_entry_t) +
+ (uint)sizeof(xfs_dir_leaf_name_t));
+ count = shortcount > leafcount ? shortcount : leafcount;
+ mp->m_dircook_elog = xfs_da_log2_roundup(count + 1);
+ ASSERT(mp->m_dircook_elog <= mp->m_sb.sb_blocklog);
+ mp->m_dir_node_ents = mp->m_attr_node_ents =
+ (XFS_LBSIZE(mp) - (uint)sizeof(xfs_da_node_hdr_t)) /
+ (uint)sizeof(xfs_da_node_entry_t);
+ mp->m_dir_magicpct = (XFS_LBSIZE(mp) * 37) / 100;
+ mp->m_dirblksize = mp->m_sb.sb_blocksize;
+ mp->m_dirblkfsbs = 1;
+}
+
+/*
+ * Return 1 if directory contains only "." and "..".
+ */
+static int
+xfs_dir_isempty(xfs_inode_t *dp)
+{
+ xfs_dir_sf_hdr_t *hdr;
+
+ ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
+ if (dp->i_d.di_size == 0)
+ return(1);
+ if (dp->i_d.di_size > XFS_IFORK_DSIZE(dp))
+ return(0);
+ hdr = (xfs_dir_sf_hdr_t *)dp->i_df.if_u1.if_data;
+ return(hdr->count == 0);
+}
+
+/*
+ * Initialize a directory with its "." and ".." entries.
+ */
+static int
+xfs_dir_init(xfs_trans_t *trans, xfs_inode_t *dir, xfs_inode_t *parent_dir)
+{
+ xfs_da_args_t args;
+ int error;
+
+ memset((char *)&args, 0, sizeof(args));
+ args.dp = dir;
+ args.trans = trans;
+
+ ASSERT((dir->i_d.di_mode & S_IFMT) == S_IFDIR);
+ if ((error = xfs_dir_ino_validate(trans->t_mountp, parent_dir->i_ino)))
+ return error;
+
+ return(xfs_dir_shortform_create(&args, parent_dir->i_ino));
+}
+
+/*
+ * Generic handler routine to add a name to a directory.
+ * Transitions directory from shortform to Btree as necessary.
+ */
+static int /* error */
+xfs_dir_createname(xfs_trans_t *trans, xfs_inode_t *dp, char *name,
+ int namelen, xfs_ino_t inum, xfs_fsblock_t *firstblock,
+ xfs_bmap_free_t *flist, xfs_extlen_t total)
+{
+ xfs_da_args_t args;
+ int retval, newsize, done;
+
+ ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
+
+ if ((retval = xfs_dir_ino_validate(trans->t_mountp, inum)))
+ return (retval);
+
+ XFS_STATS_INC(xs_dir_create);
+ /*
+ * Fill in the arg structure for this request.
+ */
+ args.name = name;
+ args.namelen = namelen;
+ args.hashval = xfs_da_hashname(name, namelen);
+ args.inumber = inum;
+ args.dp = dp;
+ args.firstblock = firstblock;
+ args.flist = flist;
+ args.total = total;
+ args.whichfork = XFS_DATA_FORK;
+ args.trans = trans;
+ args.justcheck = 0;
+ args.addname = args.oknoent = 1;
+
+ /*
+ * Decide on what work routines to call based on the inode size.
+ */
+ done = 0;
+ if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
+ newsize = XFS_DIR_SF_ENTSIZE_BYNAME(args.namelen);
+ if ((dp->i_d.di_size + newsize) <= XFS_IFORK_DSIZE(dp)) {
+ retval = xfs_dir_shortform_addname(&args);
+ done = 1;
+ } else {
+ if (total == 0)
+ return XFS_ERROR(ENOSPC);
+ retval = xfs_dir_shortform_to_leaf(&args);
+ done = retval != 0;
+ }
+ }
+ if (!done && xfs_bmap_one_block(dp, XFS_DATA_FORK)) {
+ retval = xfs_dir_leaf_addname(&args);
+ done = retval != ENOSPC;
+ if (!done) {
+ if (total == 0)
+ return XFS_ERROR(ENOSPC);
+ retval = xfs_dir_leaf_to_node(&args);
+ done = retval != 0;
+ }
+ }
+ if (!done) {
+ retval = xfs_dir_node_addname(&args);
+ }
+ return(retval);
+}
+
+/*
+ * Generic handler routine to check if a name can be added to a directory,
+ * without adding any blocks to the directory.
+ */
+static int /* error */
+xfs_dir_canenter(xfs_trans_t *trans, xfs_inode_t *dp, char *name, int namelen)
+{
+ xfs_da_args_t args;
+ int retval, newsize;
+
+ ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
+ /*
+ * Fill in the arg structure for this request.
+ */
+ args.name = name;
+ args.namelen = namelen;
+ args.hashval = xfs_da_hashname(name, namelen);
+ args.inumber = 0;
+ args.dp = dp;
+ args.firstblock = NULL;
+ args.flist = NULL;
+ args.total = 0;
+ args.whichfork = XFS_DATA_FORK;
+ args.trans = trans;
+ args.justcheck = args.addname = args.oknoent = 1;
+
+ /*
+ * Decide on what work routines to call based on the inode size.
+ */
+ if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
+ newsize = XFS_DIR_SF_ENTSIZE_BYNAME(args.namelen);
+ if ((dp->i_d.di_size + newsize) <= XFS_IFORK_DSIZE(dp))
+ retval = 0;
+ else
+ retval = XFS_ERROR(ENOSPC);
+ } else if (xfs_bmap_one_block(dp, XFS_DATA_FORK)) {
+ retval = xfs_dir_leaf_addname(&args);
+ } else {
+ retval = xfs_dir_node_addname(&args);
+ }
+ return(retval);
+}
+
+/*
+ * Generic handler routine to remove a name from a directory.
+ * Transitions directory from Btree to shortform as necessary.
+ */
+static int /* error */
+xfs_dir_removename(xfs_trans_t *trans, xfs_inode_t *dp, char *name,
+ int namelen, xfs_ino_t ino, xfs_fsblock_t *firstblock,
+ xfs_bmap_free_t *flist, xfs_extlen_t total)
+{
+ xfs_da_args_t args;
+ int count, totallen, newsize, retval;
+
+ ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
+ XFS_STATS_INC(xs_dir_remove);
+ /*
+ * Fill in the arg structure for this request.
+ */
+ args.name = name;
+ args.namelen = namelen;
+ args.hashval = xfs_da_hashname(name, namelen);
+ args.inumber = ino;
+ args.dp = dp;
+ args.firstblock = firstblock;
+ args.flist = flist;
+ args.total = total;
+ args.whichfork = XFS_DATA_FORK;
+ args.trans = trans;
+ args.justcheck = args.addname = args.oknoent = 0;
+
+ /*
+ * Decide on what work routines to call based on the inode size.
+ */
+ if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
+ retval = xfs_dir_shortform_removename(&args);
+ } else if (xfs_bmap_one_block(dp, XFS_DATA_FORK)) {
+ retval = xfs_dir_leaf_removename(&args, &count, &totallen);
+ if (retval == 0) {
+ newsize = XFS_DIR_SF_ALLFIT(count, totallen);
+ if (newsize <= XFS_IFORK_DSIZE(dp)) {
+ retval = xfs_dir_leaf_to_shortform(&args);
+ }
+ }
+ } else {
+ retval = xfs_dir_node_removename(&args);
+ }
+ return(retval);
+}
+
+static int /* error */
+xfs_dir_lookup(xfs_trans_t *trans, xfs_inode_t *dp, char *name, int namelen,
+ xfs_ino_t *inum)
+{
+ xfs_da_args_t args;
+ int retval;
+
+ ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
+
+ XFS_STATS_INC(xs_dir_lookup);
+ /*
+ * Fill in the arg structure for this request.
+ */
+ args.name = name;
+ args.namelen = namelen;
+ args.hashval = xfs_da_hashname(name, namelen);
+ args.inumber = 0;
+ args.dp = dp;
+ args.firstblock = NULL;
+ args.flist = NULL;
+ args.total = 0;
+ args.whichfork = XFS_DATA_FORK;
+ args.trans = trans;
+ args.justcheck = args.addname = 0;
+ args.oknoent = 1;
+
+ /*
+ * Decide on what work routines to call based on the inode size.
+ */
+ if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
+ retval = xfs_dir_shortform_lookup(&args);
+ } else if (xfs_bmap_one_block(dp, XFS_DATA_FORK)) {
+ retval = xfs_dir_leaf_lookup(&args);
+ } else {
+ retval = xfs_dir_node_lookup(&args);
+ }
+ if (retval == EEXIST)
+ retval = 0;
+ *inum = args.inumber;
+ return(retval);
+}
+
+/*
+ * Implement readdir.
+ */
+static int /* error */
+xfs_dir_getdents(xfs_trans_t *trans, xfs_inode_t *dp, uio_t *uio, int *eofp)
+{
+ xfs_dirent_t *dbp;
+ int alignment, retval;
+ xfs_dir_put_t put;
+
+ XFS_STATS_INC(xs_dir_getdents);
+ ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
+
+ /*
+ * If our caller has given us a single contiguous memory buffer,
+ * just work directly within that buffer. If it's in user memory,
+ * lock it down first.
+ */
+ alignment = sizeof(xfs_off_t) - 1;
+ if ((uio->uio_iovcnt == 1) &&
+ (((__psint_t)uio->uio_iov[0].iov_base & alignment) == 0) &&
+ ((uio->uio_iov[0].iov_len & alignment) == 0)) {
+ dbp = NULL;
+ put = xfs_dir_put_dirent64_direct;
+ } else {
+ dbp = kmem_alloc(sizeof(*dbp) + MAXNAMELEN, KM_SLEEP);
+ put = xfs_dir_put_dirent64_uio;
+ }
+
+ /*
+ * Decide on what work routines to call based on the inode size.
+ */
+ *eofp = 0;
+
+ if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
+ retval = xfs_dir_shortform_getdents(dp, uio, eofp, dbp, put);
+ } else if (xfs_bmap_one_block(dp, XFS_DATA_FORK)) {
+ retval = xfs_dir_leaf_getdents(trans, dp, uio, eofp, dbp, put);
+ } else {
+ retval = xfs_dir_node_getdents(trans, dp, uio, eofp, dbp, put);
+ }
+ if (dbp != NULL)
+ kmem_free(dbp, sizeof(*dbp) + MAXNAMELEN);
+
+ return(retval);
+}
+
+static int /* error */
+xfs_dir_replace(xfs_trans_t *trans, xfs_inode_t *dp, char *name, int namelen,
+ xfs_ino_t inum, xfs_fsblock_t *firstblock,
+ xfs_bmap_free_t *flist, xfs_extlen_t total)
+{
+ xfs_da_args_t args;
+ int retval;
+
+ ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
+
+ if ((retval = xfs_dir_ino_validate(trans->t_mountp, inum)))
+ return retval;
+
+ /*
+ * Fill in the arg structure for this request.
+ */
+ args.name = name;
+ args.namelen = namelen;
+ args.hashval = xfs_da_hashname(name, namelen);
+ args.inumber = inum;
+ args.dp = dp;
+ args.firstblock = firstblock;
+ args.flist = flist;
+ args.total = total;
+ args.whichfork = XFS_DATA_FORK;
+ args.trans = trans;
+ args.justcheck = args.addname = args.oknoent = 0;
+
+ /*
+ * Decide on what work routines to call based on the inode size.
+ */
+ if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
+ retval = xfs_dir_shortform_replace(&args);
+ } else if (xfs_bmap_one_block(dp, XFS_DATA_FORK)) {
+ retval = xfs_dir_leaf_replace(&args);
+ } else {
+ retval = xfs_dir_node_replace(&args);
+ }
+
+ return(retval);
+}
+
+static int
+xfs_dir_shortform_validate_ondisk(xfs_mount_t *mp, xfs_dinode_t *dp)
+{
+ xfs_ino_t ino;
+ int namelen_sum;
+ int count;
+ xfs_dir_shortform_t *sf;
+ xfs_dir_sf_entry_t *sfe;
+ int i;
+
+
+
+ if ((INT_GET(dp->di_core.di_mode, ARCH_CONVERT) & S_IFMT) != S_IFDIR) {
+ return 0;
+ }
+ if (INT_GET(dp->di_core.di_format, ARCH_CONVERT) != XFS_DINODE_FMT_LOCAL) {
+ return 0;
+ }
+ if (INT_GET(dp->di_core.di_size, ARCH_CONVERT) < sizeof(sf->hdr)) {
+ xfs_fs_cmn_err(CE_WARN, mp, "Invalid shortform size: dp 0x%p",
+ dp);
+ return 1;
+ }
+ sf = (xfs_dir_shortform_t *)(&dp->di_u.di_dirsf);
+ ino = XFS_GET_DIR_INO_ARCH(mp, sf->hdr.parent, ARCH_CONVERT);
+ if (xfs_dir_ino_validate(mp, ino))
+ return 1;
+
+ count = sf->hdr.count;
+ if ((count < 0) || ((count * 10) > XFS_LITINO(mp))) {
+ xfs_fs_cmn_err(CE_WARN, mp,
+ "Invalid shortform count: dp 0x%p", dp);
+ return(1);
+ }
+
+ if (count == 0) {
+ return 0;
+ }
+
+ namelen_sum = 0;
+ sfe = &sf->list[0];
+ for (i = sf->hdr.count - 1; i >= 0; i--) {
+ ino = XFS_GET_DIR_INO_ARCH(mp, sfe->inumber, ARCH_CONVERT);
+ xfs_dir_ino_validate(mp, ino);
+ if (sfe->namelen >= XFS_LITINO(mp)) {
+ xfs_fs_cmn_err(CE_WARN, mp,
+ "Invalid shortform namelen: dp 0x%p", dp);
+ return 1;
+ }
+ namelen_sum += sfe->namelen;
+ sfe = XFS_DIR_SF_NEXTENTRY(sfe);
+ }
+ if (namelen_sum >= XFS_LITINO(mp)) {
+ xfs_fs_cmn_err(CE_WARN, mp,
+ "Invalid shortform namelen: dp 0x%p", dp);
+ return 1;
+ }
+
+ return 0;
+}
+
+/*========================================================================
+ * External routines when dirsize == XFS_LBSIZE(dp->i_mount).
+ *========================================================================*/
+
+/*
+ * Add a name to the leaf directory structure
+ * This is the external routine.
+ */
+int
+xfs_dir_leaf_addname(xfs_da_args_t *args)
+{
+ int index, retval;
+ xfs_dabuf_t *bp;
+
+ retval = xfs_da_read_buf(args->trans, args->dp, 0, -1, &bp,
+ XFS_DATA_FORK);
+ if (retval)
+ return(retval);
+ ASSERT(bp != NULL);
+
+ retval = xfs_dir_leaf_lookup_int(bp, args, &index);
+ if (retval == ENOENT)
+ retval = xfs_dir_leaf_add(bp, args, index);
+ xfs_da_buf_done(bp);
+ return(retval);
+}
+
+/*
+ * Remove a name from the leaf directory structure
+ * This is the external routine.
+ */
+STATIC int
+xfs_dir_leaf_removename(xfs_da_args_t *args, int *count, int *totallen)
+{
+ xfs_dir_leafblock_t *leaf;
+ int index, retval;
+ xfs_dabuf_t *bp;
+
+ retval = xfs_da_read_buf(args->trans, args->dp, 0, -1, &bp,
+ XFS_DATA_FORK);
+ if (retval)
+ return(retval);
+ ASSERT(bp != NULL);
+ leaf = bp->data;
+ ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) == XFS_DIR_LEAF_MAGIC);
+ retval = xfs_dir_leaf_lookup_int(bp, args, &index);
+ if (retval == EEXIST) {
+ (void)xfs_dir_leaf_remove(args->trans, bp, index);
+ *count = INT_GET(leaf->hdr.count, ARCH_CONVERT);
+ *totallen = INT_GET(leaf->hdr.namebytes, ARCH_CONVERT);
+ retval = 0;
+ }
+ xfs_da_buf_done(bp);
+ return(retval);
+}
+
+/*
+ * Look up a name in a leaf directory structure.
+ * This is the external routine.
+ */
+STATIC int
+xfs_dir_leaf_lookup(xfs_da_args_t *args)
+{
+ int index, retval;
+ xfs_dabuf_t *bp;
+
+ retval = xfs_da_read_buf(args->trans, args->dp, 0, -1, &bp,
+ XFS_DATA_FORK);
+ if (retval)
+ return(retval);
+ ASSERT(bp != NULL);
+ retval = xfs_dir_leaf_lookup_int(bp, args, &index);
+ xfs_da_brelse(args->trans, bp);
+ return(retval);
+}
+
+/*
+ * Copy out directory entries for getdents(), for leaf directories.
+ */
+STATIC int
+xfs_dir_leaf_getdents(xfs_trans_t *trans, xfs_inode_t *dp, uio_t *uio,
+ int *eofp, xfs_dirent_t *dbp, xfs_dir_put_t put)
+{
+ xfs_dabuf_t *bp;
+ int retval, eob;
+
+ retval = xfs_da_read_buf(dp->i_transp, dp, 0, -1, &bp, XFS_DATA_FORK);
+ if (retval)
+ return(retval);
+ ASSERT(bp != NULL);
+ retval = xfs_dir_leaf_getdents_int(bp, dp, 0, uio, &eob, dbp, put, -1);
+ xfs_da_brelse(trans, bp);
+ *eofp = (eob == 0);
+ return(retval);
+}
+
+/*
+ * Look up a name in a leaf directory structure, replace the inode number.
+ * This is the external routine.
+ */
+STATIC int
+xfs_dir_leaf_replace(xfs_da_args_t *args)
+{
+ int index, retval;
+ xfs_dabuf_t *bp;
+ xfs_ino_t inum;
+ xfs_dir_leafblock_t *leaf;
+ xfs_dir_leaf_entry_t *entry;
+ xfs_dir_leaf_name_t *namest;
+
+ inum = args->inumber;
+ retval = xfs_da_read_buf(args->trans, args->dp, 0, -1, &bp,
+ XFS_DATA_FORK);
+ if (retval)
+ return(retval);
+ ASSERT(bp != NULL);
+ retval = xfs_dir_leaf_lookup_int(bp, args, &index);
+ if (retval == EEXIST) {
+ leaf = bp->data;
+ entry = &leaf->entries[index];
+ namest = XFS_DIR_LEAF_NAMESTRUCT(leaf, INT_GET(entry->nameidx, ARCH_CONVERT));
+ /* XXX - replace assert? */
+ XFS_DIR_SF_PUT_DIRINO_ARCH(&inum, &namest->inumber, ARCH_CONVERT);
+ xfs_da_log_buf(args->trans, bp,
+ XFS_DA_LOGRANGE(leaf, namest, sizeof(namest->inumber)));
+ xfs_da_buf_done(bp);
+ retval = 0;
+ } else
+ xfs_da_brelse(args->trans, bp);
+ return(retval);
+}
+
+
+/*========================================================================
+ * External routines when dirsize > XFS_LBSIZE(mp).
+ *========================================================================*/
+
+/*
+ * Add a name to a Btree-format directory.
+ *
+ * This will involve walking down the Btree, and may involve splitting
+ * leaf nodes and even splitting intermediate nodes up to and including
+ * the root node (a special case of an intermediate node).
+ */
+STATIC int
+xfs_dir_node_addname(xfs_da_args_t *args)
+{
+ xfs_da_state_t *state;
+ xfs_da_state_blk_t *blk;
+ int retval, error;
+
+ /*
+ * Fill in bucket of arguments/results/context to carry around.
+ */
+ state = xfs_da_state_alloc();
+ state->args = args;
+ state->mp = args->dp->i_mount;
+ state->blocksize = state->mp->m_sb.sb_blocksize;
+ state->node_ents = state->mp->m_dir_node_ents;
+
+ /*
+ * Search to see if name already exists, and get back a pointer
+ * to where it should go.
+ */
+ error = xfs_da_node_lookup_int(state, &retval);
+ if (error)
+ retval = error;
+ if (retval != ENOENT)
+ goto error;
+ blk = &state->path.blk[ state->path.active-1 ];
+ ASSERT(blk->magic == XFS_DIR_LEAF_MAGIC);
+ retval = xfs_dir_leaf_add(blk->bp, args, blk->index);
+ if (retval == 0) {
+ /*
+ * Addition succeeded, update Btree hashvals.
+ */
+ if (!args->justcheck)
+ xfs_da_fixhashpath(state, &state->path);
+ } else {
+ /*
+ * Addition failed, split as many Btree elements as required.
+ */
+ if (args->total == 0) {
+ ASSERT(retval == ENOSPC);
+ goto error;
+ }
+ retval = xfs_da_split(state);
+ }
+error:
+ xfs_da_state_free(state);
+
+ return(retval);
+}
+
+/*
+ * Remove a name from a B-tree directory.
+ *
+ * This will involve walking down the Btree, and may involve joining
+ * leaf nodes and even joining intermediate nodes up to and including
+ * the root node (a special case of an intermediate node).
+ */
+STATIC int
+xfs_dir_node_removename(xfs_da_args_t *args)
+{
+ xfs_da_state_t *state;
+ xfs_da_state_blk_t *blk;
+ int retval, error;
+
+ state = xfs_da_state_alloc();
+ state->args = args;
+ state->mp = args->dp->i_mount;
+ state->blocksize = state->mp->m_sb.sb_blocksize;
+ state->node_ents = state->mp->m_dir_node_ents;
+
+ /*
+ * Search to see if name exists, and get back a pointer to it.
+ */
+ error = xfs_da_node_lookup_int(state, &retval);
+ if (error)
+ retval = error;
+ if (retval != EEXIST) {
+ xfs_da_state_free(state);
+ return(retval);
+ }
+
+ /*
+ * Remove the name and update the hashvals in the tree.
+ */
+ blk = &state->path.blk[ state->path.active-1 ];
+ ASSERT(blk->magic == XFS_DIR_LEAF_MAGIC);
+ retval = xfs_dir_leaf_remove(args->trans, blk->bp, blk->index);
+ xfs_da_fixhashpath(state, &state->path);
+
+ /*
+ * Check to see if the tree needs to be collapsed.
+ */
+ error = 0;
+ if (retval) {
+ error = xfs_da_join(state);
+ }
+
+ xfs_da_state_free(state);
+ if (error)
+ return(error);
+ return(0);
+}
+
+/*
+ * Look up a filename in a int directory.
+ * Use an internal routine to actually do all the work.
+ */
+STATIC int
+xfs_dir_node_lookup(xfs_da_args_t *args)
+{
+ xfs_da_state_t *state;
+ int retval, error, i;
+
+ state = xfs_da_state_alloc();
+ state->args = args;
+ state->mp = args->dp->i_mount;
+ state->blocksize = state->mp->m_sb.sb_blocksize;
+ state->node_ents = state->mp->m_dir_node_ents;
+
+ /*
+ * Search to see if name exists,
+ * and get back a pointer to it.
+ */
+ error = xfs_da_node_lookup_int(state, &retval);
+ if (error) {
+ retval = error;
+ }
+
+ /*
+ * If not in a transaction, we have to release all the buffers.
+ */
+ for (i = 0; i < state->path.active; i++) {
+ xfs_da_brelse(args->trans, state->path.blk[i].bp);
+ state->path.blk[i].bp = NULL;
+ }
+
+ xfs_da_state_free(state);
+ return(retval);
+}
+
+STATIC int
+xfs_dir_node_getdents(xfs_trans_t *trans, xfs_inode_t *dp, uio_t *uio,
+ int *eofp, xfs_dirent_t *dbp, xfs_dir_put_t put)
+{
+ xfs_da_intnode_t *node;
+ xfs_da_node_entry_t *btree;
+ xfs_dir_leafblock_t *leaf = NULL;
+ xfs_dablk_t bno, nextbno;
+ xfs_dahash_t cookhash;
+ xfs_mount_t *mp;
+ int error, eob, i;
+ xfs_dabuf_t *bp;
+ xfs_daddr_t nextda;
+
+ /*
+ * Pick up our context.
+ */
+ mp = dp->i_mount;
+ bp = NULL;
+ bno = XFS_DA_COOKIE_BNO(mp, uio->uio_offset);
+ cookhash = XFS_DA_COOKIE_HASH(mp, uio->uio_offset);
+
+ xfs_dir_trace_g_du("node: start", dp, uio);
+
+ /*
+ * Re-find our place, even if we're confused about what our place is.
+ *
+ * First we check the block number from the magic cookie, it is a
+ * cache of where we ended last time. If we find a leaf block, and
+ * the starting hashval in that block is less than our desired
+ * hashval, then we run with it.
+ */
+ if (bno > 0) {
+ error = xfs_da_read_buf(trans, dp, bno, -2, &bp, XFS_DATA_FORK);
+ if ((error != 0) && (error != EFSCORRUPTED))
+ return(error);
+ if (bp)
+ leaf = bp->data;
+ if (bp && INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) != XFS_DIR_LEAF_MAGIC) {
+ xfs_dir_trace_g_dub("node: block not a leaf",
+ dp, uio, bno);
+ xfs_da_brelse(trans, bp);
+ bp = NULL;
+ }
+ if (bp && INT_GET(leaf->entries[0].hashval, ARCH_CONVERT) > cookhash) {
+ xfs_dir_trace_g_dub("node: leaf hash too large",
+ dp, uio, bno);
+ xfs_da_brelse(trans, bp);
+ bp = NULL;
+ }
+ if (bp &&
+ cookhash > INT_GET(leaf->entries[INT_GET(leaf->hdr.count, ARCH_CONVERT) - 1].hashval, ARCH_CONVERT)) {
+ xfs_dir_trace_g_dub("node: leaf hash too small",
+ dp, uio, bno);
+ xfs_da_brelse(trans, bp);
+ bp = NULL;
+ }
+ }
+
+ /*
+ * If we did not find a leaf block from the blockno in the cookie,
+ * or we there was no blockno in the cookie (eg: first time thru),
+ * the we start at the top of the Btree and re-find our hashval.
+ */
+ if (bp == NULL) {
+ xfs_dir_trace_g_du("node: start at root" , dp, uio);
+ bno = 0;
+ for (;;) {
+ error = xfs_da_read_buf(trans, dp, bno, -1, &bp,
+ XFS_DATA_FORK);
+ if (error)
+ return(error);
+ if (bp == NULL)
+ return(XFS_ERROR(EFSCORRUPTED));
+ node = bp->data;
+ if (INT_GET(node->hdr.info.magic, ARCH_CONVERT) != XFS_DA_NODE_MAGIC)
+ break;
+ btree = &node->btree[0];
+ xfs_dir_trace_g_dun("node: node detail", dp, uio, node);
+ for (i = 0; i < INT_GET(node->hdr.count, ARCH_CONVERT); btree++, i++) {
+ if (INT_GET(btree->hashval, ARCH_CONVERT) >= cookhash) {
+ bno = INT_GET(btree->before, ARCH_CONVERT);
+ break;
+ }
+ }
+ if (i == INT_GET(node->hdr.count, ARCH_CONVERT)) {
+ xfs_da_brelse(trans, bp);
+ xfs_dir_trace_g_du("node: hash beyond EOF",
+ dp, uio);
+ uio->uio_offset = XFS_DA_MAKE_COOKIE(mp, 0, 0,
+ XFS_DA_MAXHASH);
+ *eofp = 1;
+ return(0);
+ }
+ xfs_dir_trace_g_dub("node: going to block",
+ dp, uio, bno);
+ xfs_da_brelse(trans, bp);
+ }
+ }
+ ASSERT(cookhash != XFS_DA_MAXHASH);
+
+ /*
+ * We've dropped down to the (first) leaf block that contains the
+ * hashval we are interested in. Continue rolling upward thru the
+ * leaf blocks until we fill up our buffer.
+ */
+ for (;;) {
+ leaf = bp->data;
+ if (unlikely(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) != XFS_DIR_LEAF_MAGIC)) {
+ xfs_dir_trace_g_dul("node: not a leaf", dp, uio, leaf);
+ xfs_da_brelse(trans, bp);
+ XFS_CORRUPTION_ERROR("xfs_dir_node_getdents(1)",
+ XFS_ERRLEVEL_LOW, mp, leaf);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ xfs_dir_trace_g_dul("node: leaf detail", dp, uio, leaf);
+ if ((nextbno = INT_GET(leaf->hdr.info.forw, ARCH_CONVERT))) {
+ nextda = xfs_da_reada_buf(trans, dp, nextbno,
+ XFS_DATA_FORK);
+ } else
+ nextda = -1;
+ error = xfs_dir_leaf_getdents_int(bp, dp, bno, uio, &eob, dbp,
+ put, nextda);
+ xfs_da_brelse(trans, bp);
+ bno = nextbno;
+ if (eob) {
+ xfs_dir_trace_g_dub("node: E-O-B", dp, uio, bno);
+ *eofp = 0;
+ return(error);
+ }
+ if (bno == 0)
+ break;
+ error = xfs_da_read_buf(trans, dp, bno, nextda, &bp,
+ XFS_DATA_FORK);
+ if (error)
+ return(error);
+ if (unlikely(bp == NULL)) {
+ XFS_ERROR_REPORT("xfs_dir_node_getdents(2)",
+ XFS_ERRLEVEL_LOW, mp);
+ return(XFS_ERROR(EFSCORRUPTED));
+ }
+ }
+ *eofp = 1;
+ xfs_dir_trace_g_du("node: E-O-F", dp, uio);
+ return(0);
+}
+
+/*
+ * Look up a filename in an int directory, replace the inode number.
+ * Use an internal routine to actually do the lookup.
+ */
+STATIC int
+xfs_dir_node_replace(xfs_da_args_t *args)
+{
+ xfs_da_state_t *state;
+ xfs_da_state_blk_t *blk;
+ xfs_dir_leafblock_t *leaf;
+ xfs_dir_leaf_entry_t *entry;
+ xfs_dir_leaf_name_t *namest;
+ xfs_ino_t inum;
+ int retval, error, i;
+ xfs_dabuf_t *bp;
+
+ state = xfs_da_state_alloc();
+ state->args = args;
+ state->mp = args->dp->i_mount;
+ state->blocksize = state->mp->m_sb.sb_blocksize;
+ state->node_ents = state->mp->m_dir_node_ents;
+ inum = args->inumber;
+
+ /*
+ * Search to see if name exists,
+ * and get back a pointer to it.
+ */
+ error = xfs_da_node_lookup_int(state, &retval);
+ if (error) {
+ retval = error;
+ }
+
+ if (retval == EEXIST) {
+ blk = &state->path.blk[state->path.active - 1];
+ ASSERT(blk->magic == XFS_DIR_LEAF_MAGIC);
+ bp = blk->bp;
+ leaf = bp->data;
+ entry = &leaf->entries[blk->index];
+ namest = XFS_DIR_LEAF_NAMESTRUCT(leaf, INT_GET(entry->nameidx, ARCH_CONVERT));
+ /* XXX - replace assert ? */
+ XFS_DIR_SF_PUT_DIRINO_ARCH(&inum, &namest->inumber, ARCH_CONVERT);
+ xfs_da_log_buf(args->trans, bp,
+ XFS_DA_LOGRANGE(leaf, namest, sizeof(namest->inumber)));
+ xfs_da_buf_done(bp);
+ blk->bp = NULL;
+ retval = 0;
+ } else {
+ i = state->path.active - 1;
+ xfs_da_brelse(args->trans, state->path.blk[i].bp);
+ state->path.blk[i].bp = NULL;
+ }
+ for (i = 0; i < state->path.active - 1; i++) {
+ xfs_da_brelse(args->trans, state->path.blk[i].bp);
+ state->path.blk[i].bp = NULL;
+ }
+
+ xfs_da_state_free(state);
+ return(retval);
+}
+
+#if defined(XFS_DIR_TRACE)
+/*
+ * Add a trace buffer entry for an inode and a uio.
+ */
+void
+xfs_dir_trace_g_du(char *where, xfs_inode_t *dp, uio_t *uio)
+{
+ xfs_dir_trace_enter(XFS_DIR_KTRACE_G_DU, where,
+ (void *)dp, (void *)dp->i_mount,
+ (void *)((unsigned long)(uio->uio_offset >> 32)),
+ (void *)((unsigned long)(uio->uio_offset & 0xFFFFFFFF)),
+ (void *)(unsigned long)uio->uio_resid,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+}
+
+/*
+ * Add a trace buffer entry for an inode and a uio.
+ */
+void
+xfs_dir_trace_g_dub(char *where, xfs_inode_t *dp, uio_t *uio, xfs_dablk_t bno)
+{
+ xfs_dir_trace_enter(XFS_DIR_KTRACE_G_DUB, where,
+ (void *)dp, (void *)dp->i_mount,
+ (void *)((unsigned long)(uio->uio_offset >> 32)),
+ (void *)((unsigned long)(uio->uio_offset & 0xFFFFFFFF)),
+ (void *)(unsigned long)uio->uio_resid,
+ (void *)(unsigned long)bno,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+}
+
+/*
+ * Add a trace buffer entry for an inode and a uio.
+ */
+void
+xfs_dir_trace_g_dun(char *where, xfs_inode_t *dp, uio_t *uio,
+ xfs_da_intnode_t *node)
+{
+ int last = INT_GET(node->hdr.count, ARCH_CONVERT) - 1;
+
+ xfs_dir_trace_enter(XFS_DIR_KTRACE_G_DUN, where,
+ (void *)dp, (void *)dp->i_mount,
+ (void *)((unsigned long)(uio->uio_offset >> 32)),
+ (void *)((unsigned long)(uio->uio_offset & 0xFFFFFFFF)),
+ (void *)(unsigned long)uio->uio_resid,
+ (void *)(unsigned long)
+ INT_GET(node->hdr.info.forw, ARCH_CONVERT),
+ (void *)(unsigned long)
+ INT_GET(node->hdr.count, ARCH_CONVERT),
+ (void *)(unsigned long)
+ INT_GET(node->btree[0].hashval, ARCH_CONVERT),
+ (void *)(unsigned long)
+ INT_GET(node->btree[last].hashval, ARCH_CONVERT),
+ NULL, NULL, NULL);
+}
+
+/*
+ * Add a trace buffer entry for an inode and a uio.
+ */
+void
+xfs_dir_trace_g_dul(char *where, xfs_inode_t *dp, uio_t *uio,
+ xfs_dir_leafblock_t *leaf)
+{
+ int last = INT_GET(leaf->hdr.count, ARCH_CONVERT) - 1;
+
+ xfs_dir_trace_enter(XFS_DIR_KTRACE_G_DUL, where,
+ (void *)dp, (void *)dp->i_mount,
+ (void *)((unsigned long)(uio->uio_offset >> 32)),
+ (void *)((unsigned long)(uio->uio_offset & 0xFFFFFFFF)),
+ (void *)(unsigned long)uio->uio_resid,
+ (void *)(unsigned long)
+ INT_GET(leaf->hdr.info.forw, ARCH_CONVERT),
+ (void *)(unsigned long)
+ INT_GET(leaf->hdr.count, ARCH_CONVERT),
+ (void *)(unsigned long)
+ INT_GET(leaf->entries[0].hashval, ARCH_CONVERT),
+ (void *)(unsigned long)
+ INT_GET(leaf->entries[last].hashval, ARCH_CONVERT),
+ NULL, NULL, NULL);
+}
+
+/*
+ * Add a trace buffer entry for an inode and a uio.
+ */
+void
+xfs_dir_trace_g_due(char *where, xfs_inode_t *dp, uio_t *uio,
+ xfs_dir_leaf_entry_t *entry)
+{
+ xfs_dir_trace_enter(XFS_DIR_KTRACE_G_DUE, where,
+ (void *)dp, (void *)dp->i_mount,
+ (void *)((unsigned long)(uio->uio_offset >> 32)),
+ (void *)((unsigned long)(uio->uio_offset & 0xFFFFFFFF)),
+ (void *)(unsigned long)uio->uio_resid,
+ (void *)(unsigned long)
+ INT_GET(entry->hashval, ARCH_CONVERT),
+ NULL, NULL, NULL, NULL, NULL, NULL);
+}
+
+/*
+ * Add a trace buffer entry for an inode and a uio.
+ */
+void
+xfs_dir_trace_g_duc(char *where, xfs_inode_t *dp, uio_t *uio, xfs_off_t cookie)
+{
+ xfs_dir_trace_enter(XFS_DIR_KTRACE_G_DUC, where,
+ (void *)dp, (void *)dp->i_mount,
+ (void *)((unsigned long)(uio->uio_offset >> 32)),
+ (void *)((unsigned long)(uio->uio_offset & 0xFFFFFFFF)),
+ (void *)(unsigned long)uio->uio_resid,
+ (void *)((unsigned long)(cookie >> 32)),
+ (void *)((unsigned long)(cookie & 0xFFFFFFFF)),
+ NULL, NULL, NULL, NULL, NULL);
+}
+
+/*
+ * Add a trace buffer entry for the arguments given to the routine,
+ * generic form.
+ */
+void
+xfs_dir_trace_enter(int type, char *where,
+ void * a0, void * a1,
+ void * a2, void * a3,
+ void * a4, void * a5,
+ void * a6, void * a7,
+ void * a8, void * a9,
+ void * a10, void * a11)
+{
+ ASSERT(xfs_dir_trace_buf);
+ ktrace_enter(xfs_dir_trace_buf, (void *)(unsigned long)type,
+ (void *)where,
+ (void *)a0, (void *)a1, (void *)a2,
+ (void *)a3, (void *)a4, (void *)a5,
+ (void *)a6, (void *)a7, (void *)a8,
+ (void *)a9, (void *)a10, (void *)a11,
+ NULL, NULL);
+}
+#endif /* XFS_DIR_TRACE */
diff --git a/fs/xfs/xfs_dir.h b/fs/xfs/xfs_dir.h
new file mode 100644
index 00000000000000..4dbc9f54cca5b1
--- /dev/null
+++ b/fs/xfs/xfs_dir.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_DIR_H__
+#define __XFS_DIR_H__
+
+/*
+ * Large directories are structured around Btrees where all the data
+ * elements are in the leaf nodes. Filenames are hashed into an int,
+ * then that int is used as the index into the Btree. Since the hashval
+ * of a filename may not be unique, we may have duplicate keys. The
+ * internal links in the Btree are logical block offsets into the file.
+ *
+ * Small directories use a different format and are packed as tightly
+ * as possible so as to fit into the literal area of the inode.
+ */
+
+/*========================================================================
+ * Function prototypes for the kernel.
+ *========================================================================*/
+
+struct uio;
+struct xfs_bmap_free;
+struct xfs_da_args;
+struct xfs_dinode;
+struct xfs_inode;
+struct xfs_mount;
+struct xfs_trans;
+
+/*
+ * Directory function types.
+ * Put in structures (xfs_dirops_t) for v1 and v2 directories.
+ */
+typedef void (*xfs_dir_mount_t)(struct xfs_mount *mp);
+typedef int (*xfs_dir_isempty_t)(struct xfs_inode *dp);
+typedef int (*xfs_dir_init_t)(struct xfs_trans *tp,
+ struct xfs_inode *dp,
+ struct xfs_inode *pdp);
+typedef int (*xfs_dir_createname_t)(struct xfs_trans *tp,
+ struct xfs_inode *dp,
+ char *name,
+ int namelen,
+ xfs_ino_t inum,
+ xfs_fsblock_t *first,
+ struct xfs_bmap_free *flist,
+ xfs_extlen_t total);
+typedef int (*xfs_dir_lookup_t)(struct xfs_trans *tp,
+ struct xfs_inode *dp,
+ char *name,
+ int namelen,
+ xfs_ino_t *inum);
+typedef int (*xfs_dir_removename_t)(struct xfs_trans *tp,
+ struct xfs_inode *dp,
+ char *name,
+ int namelen,
+ xfs_ino_t ino,
+ xfs_fsblock_t *first,
+ struct xfs_bmap_free *flist,
+ xfs_extlen_t total);
+typedef int (*xfs_dir_getdents_t)(struct xfs_trans *tp,
+ struct xfs_inode *dp,
+ struct uio *uio,
+ int *eofp);
+typedef int (*xfs_dir_replace_t)(struct xfs_trans *tp,
+ struct xfs_inode *dp,
+ char *name,
+ int namelen,
+ xfs_ino_t inum,
+ xfs_fsblock_t *first,
+ struct xfs_bmap_free *flist,
+ xfs_extlen_t total);
+typedef int (*xfs_dir_canenter_t)(struct xfs_trans *tp,
+ struct xfs_inode *dp,
+ char *name,
+ int namelen);
+typedef int (*xfs_dir_shortform_validate_ondisk_t)(struct xfs_mount *mp,
+ struct xfs_dinode *dip);
+typedef int (*xfs_dir_shortform_to_single_t)(struct xfs_da_args *args);
+
+typedef struct xfs_dirops {
+ xfs_dir_mount_t xd_mount;
+ xfs_dir_isempty_t xd_isempty;
+ xfs_dir_init_t xd_init;
+ xfs_dir_createname_t xd_createname;
+ xfs_dir_lookup_t xd_lookup;
+ xfs_dir_removename_t xd_removename;
+ xfs_dir_getdents_t xd_getdents;
+ xfs_dir_replace_t xd_replace;
+ xfs_dir_canenter_t xd_canenter;
+ xfs_dir_shortform_validate_ondisk_t xd_shortform_validate_ondisk;
+ xfs_dir_shortform_to_single_t xd_shortform_to_single;
+} xfs_dirops_t;
+
+/*
+ * Overall external interface routines.
+ */
+void xfs_dir_startup(void); /* called exactly once */
+
+#define XFS_DIR_MOUNT(mp) \
+ ((mp)->m_dirops.xd_mount(mp))
+#define XFS_DIR_ISEMPTY(mp,dp) \
+ ((mp)->m_dirops.xd_isempty(dp))
+#define XFS_DIR_INIT(mp,tp,dp,pdp) \
+ ((mp)->m_dirops.xd_init(tp,dp,pdp))
+#define XFS_DIR_CREATENAME(mp,tp,dp,name,namelen,inum,first,flist,total) \
+ ((mp)->m_dirops.xd_createname(tp,dp,name,namelen,inum,first,flist,\
+ total))
+#define XFS_DIR_LOOKUP(mp,tp,dp,name,namelen,inum) \
+ ((mp)->m_dirops.xd_lookup(tp,dp,name,namelen,inum))
+#define XFS_DIR_REMOVENAME(mp,tp,dp,name,namelen,ino,first,flist,total) \
+ ((mp)->m_dirops.xd_removename(tp,dp,name,namelen,ino,first,flist,total))
+#define XFS_DIR_GETDENTS(mp,tp,dp,uio,eofp) \
+ ((mp)->m_dirops.xd_getdents(tp,dp,uio,eofp))
+#define XFS_DIR_REPLACE(mp,tp,dp,name,namelen,inum,first,flist,total) \
+ ((mp)->m_dirops.xd_replace(tp,dp,name,namelen,inum,first,flist,total))
+#define XFS_DIR_CANENTER(mp,tp,dp,name,namelen) \
+ ((mp)->m_dirops.xd_canenter(tp,dp,name,namelen))
+#define XFS_DIR_SHORTFORM_VALIDATE_ONDISK(mp,dip) \
+ ((mp)->m_dirops.xd_shortform_validate_ondisk(mp,dip))
+#define XFS_DIR_SHORTFORM_TO_SINGLE(mp,args) \
+ ((mp)->m_dirops.xd_shortform_to_single(args))
+
+#define XFS_DIR_IS_V1(mp) ((mp)->m_dirversion == 1)
+extern xfs_dirops_t xfsv1_dirops;
+
+#endif /* __XFS_DIR_H__ */
diff --git a/fs/xfs/xfs_dir2.c b/fs/xfs/xfs_dir2.c
new file mode 100644
index 00000000000000..bd062886d58411
--- /dev/null
+++ b/fs/xfs/xfs_dir2.c
@@ -0,0 +1,859 @@
+/*
+ * Copyright (c) 2000-2001 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+/*
+ * XFS v2 directory implmentation.
+ * Top-level and utility routines.
+ */
+
+#include "xfs.h"
+
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode_item.h"
+#include "xfs_inode.h"
+#include "xfs_bmap.h"
+#include "xfs_da_btree.h"
+#include "xfs_dir_leaf.h"
+#include "xfs_dir2_data.h"
+#include "xfs_dir2_leaf.h"
+#include "xfs_dir2_block.h"
+#include "xfs_dir2_node.h"
+#include "xfs_dir2_trace.h"
+#include "xfs_error.h"
+#include "xfs_bit.h"
+
+/*
+ * Declarations for interface routines.
+ */
+static void xfs_dir2_mount(xfs_mount_t *mp);
+static int xfs_dir2_isempty(xfs_inode_t *dp);
+static int xfs_dir2_init(xfs_trans_t *tp, xfs_inode_t *dp,
+ xfs_inode_t *pdp);
+static int xfs_dir2_createname(xfs_trans_t *tp, xfs_inode_t *dp,
+ char *name, int namelen, xfs_ino_t inum,
+ xfs_fsblock_t *first,
+ xfs_bmap_free_t *flist, xfs_extlen_t total);
+static int xfs_dir2_lookup(xfs_trans_t *tp, xfs_inode_t *dp, char *name,
+ int namelen, xfs_ino_t *inum);
+static int xfs_dir2_removename(xfs_trans_t *tp, xfs_inode_t *dp,
+ char *name, int namelen, xfs_ino_t ino,
+ xfs_fsblock_t *first,
+ xfs_bmap_free_t *flist, xfs_extlen_t total);
+static int xfs_dir2_getdents(xfs_trans_t *tp, xfs_inode_t *dp, uio_t *uio,
+ int *eofp);
+static int xfs_dir2_replace(xfs_trans_t *tp, xfs_inode_t *dp, char *name,
+ int namelen, xfs_ino_t inum,
+ xfs_fsblock_t *first, xfs_bmap_free_t *flist,
+ xfs_extlen_t total);
+static int xfs_dir2_canenter(xfs_trans_t *tp, xfs_inode_t *dp, char *name,
+ int namelen);
+static int xfs_dir2_shortform_validate_ondisk(xfs_mount_t *mp,
+ xfs_dinode_t *dip);
+
+/*
+ * Utility routine declarations.
+ */
+static int xfs_dir2_put_dirent64_direct(xfs_dir2_put_args_t *pa);
+static int xfs_dir2_put_dirent64_uio(xfs_dir2_put_args_t *pa);
+
+/*
+ * Directory operations vector.
+ */
+xfs_dirops_t xfsv2_dirops = {
+ .xd_mount = xfs_dir2_mount,
+ .xd_isempty = xfs_dir2_isempty,
+ .xd_init = xfs_dir2_init,
+ .xd_createname = xfs_dir2_createname,
+ .xd_lookup = xfs_dir2_lookup,
+ .xd_removename = xfs_dir2_removename,
+ .xd_getdents = xfs_dir2_getdents,
+ .xd_replace = xfs_dir2_replace,
+ .xd_canenter = xfs_dir2_canenter,
+ .xd_shortform_validate_ondisk = xfs_dir2_shortform_validate_ondisk,
+ .xd_shortform_to_single = xfs_dir2_sf_to_block,
+};
+
+/*
+ * Interface routines.
+ */
+
+/*
+ * Initialize directory-related fields in the mount structure.
+ */
+static void
+xfs_dir2_mount(
+ xfs_mount_t *mp) /* filesystem mount point */
+{
+ mp->m_dirversion = 2;
+ ASSERT((1 << (mp->m_sb.sb_blocklog + mp->m_sb.sb_dirblklog)) <=
+ XFS_MAX_BLOCKSIZE);
+ mp->m_dirblksize = 1 << (mp->m_sb.sb_blocklog + mp->m_sb.sb_dirblklog);
+ mp->m_dirblkfsbs = 1 << mp->m_sb.sb_dirblklog;
+ mp->m_dirdatablk = XFS_DIR2_DB_TO_DA(mp, XFS_DIR2_DATA_FIRSTDB(mp));
+ mp->m_dirleafblk = XFS_DIR2_DB_TO_DA(mp, XFS_DIR2_LEAF_FIRSTDB(mp));
+ mp->m_dirfreeblk = XFS_DIR2_DB_TO_DA(mp, XFS_DIR2_FREE_FIRSTDB(mp));
+ mp->m_attr_node_ents =
+ (mp->m_sb.sb_blocksize - (uint)sizeof(xfs_da_node_hdr_t)) /
+ (uint)sizeof(xfs_da_node_entry_t);
+ mp->m_dir_node_ents =
+ (mp->m_dirblksize - (uint)sizeof(xfs_da_node_hdr_t)) /
+ (uint)sizeof(xfs_da_node_entry_t);
+ mp->m_dir_magicpct = (mp->m_dirblksize * 37) / 100;
+}
+
+/*
+ * Return 1 if directory contains only "." and "..".
+ */
+static int /* return code */
+xfs_dir2_isempty(
+ xfs_inode_t *dp) /* incore inode structure */
+{
+ xfs_dir2_sf_t *sfp; /* shortform directory structure */
+
+ ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
+ /*
+ * Might happen during shutdown.
+ */
+ if (dp->i_d.di_size == 0) {
+ return 1;
+ }
+ if (dp->i_d.di_size > XFS_IFORK_DSIZE(dp))
+ return 0;
+ sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data;
+ return INT_ISZERO(sfp->hdr.count, ARCH_CONVERT);
+}
+
+/*
+ * Initialize a directory with its "." and ".." entries.
+ */
+static int /* error */
+xfs_dir2_init(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_inode_t *dp, /* incore directory inode */
+ xfs_inode_t *pdp) /* incore parent directory inode */
+{
+ xfs_da_args_t args; /* operation arguments */
+ int error; /* error return value */
+
+ memset((char *)&args, 0, sizeof(args));
+ args.dp = dp;
+ args.trans = tp;
+ ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
+ if ((error = xfs_dir_ino_validate(tp->t_mountp, pdp->i_ino))) {
+ return error;
+ }
+ return xfs_dir2_sf_create(&args, pdp->i_ino);
+}
+
+/*
+ Enter a name in a directory.
+ */
+static int /* error */
+xfs_dir2_createname(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_inode_t *dp, /* incore directory inode */
+ char *name, /* new entry name */
+ int namelen, /* new entry name length */
+ xfs_ino_t inum, /* new entry inode number */
+ xfs_fsblock_t *first, /* bmap's firstblock */
+ xfs_bmap_free_t *flist, /* bmap's freeblock list */
+ xfs_extlen_t total) /* bmap's total block count */
+{
+ xfs_da_args_t args; /* operation arguments */
+ int rval; /* return value */
+ int v; /* type-checking value */
+
+ ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
+ if ((rval = xfs_dir_ino_validate(tp->t_mountp, inum))) {
+ return rval;
+ }
+ XFS_STATS_INC(xs_dir_create);
+ /*
+ * Fill in the arg structure for this request.
+ */
+ args.name = name;
+ args.namelen = namelen;
+ args.hashval = xfs_da_hashname(name, namelen);
+ args.inumber = inum;
+ args.dp = dp;
+ args.firstblock = first;
+ args.flist = flist;
+ args.total = total;
+ args.whichfork = XFS_DATA_FORK;
+ args.trans = tp;
+ args.justcheck = 0;
+ args.addname = args.oknoent = 1;
+ /*
+ * Decide on what work routines to call based on the inode size.
+ */
+ if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
+ rval = xfs_dir2_sf_addname(&args);
+ else if ((rval = xfs_dir2_isblock(tp, dp, &v))) {
+ return rval;
+ } else if (v)
+ rval = xfs_dir2_block_addname(&args);
+ else if ((rval = xfs_dir2_isleaf(tp, dp, &v))) {
+ return rval;
+ } else if (v)
+ rval = xfs_dir2_leaf_addname(&args);
+ else
+ rval = xfs_dir2_node_addname(&args);
+ return rval;
+}
+
+/*
+ * Lookup a name in a directory, give back the inode number.
+ */
+static int /* error */
+xfs_dir2_lookup(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_inode_t *dp, /* incore directory inode */
+ char *name, /* lookup name */
+ int namelen, /* lookup name length */
+ xfs_ino_t *inum) /* out: inode number */
+{
+ xfs_da_args_t args; /* operation arguments */
+ int rval; /* return value */
+ int v; /* type-checking value */
+
+ ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
+ XFS_STATS_INC(xs_dir_lookup);
+
+ /*
+ * Fill in the arg structure for this request.
+ */
+ args.name = name;
+ args.namelen = namelen;
+ args.hashval = xfs_da_hashname(name, namelen);
+ args.inumber = 0;
+ args.dp = dp;
+ args.firstblock = NULL;
+ args.flist = NULL;
+ args.total = 0;
+ args.whichfork = XFS_DATA_FORK;
+ args.trans = tp;
+ args.justcheck = args.addname = 0;
+ args.oknoent = 1;
+ /*
+ * Decide on what work routines to call based on the inode size.
+ */
+ if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
+ rval = xfs_dir2_sf_lookup(&args);
+ else if ((rval = xfs_dir2_isblock(tp, dp, &v))) {
+ return rval;
+ } else if (v)
+ rval = xfs_dir2_block_lookup(&args);
+ else if ((rval = xfs_dir2_isleaf(tp, dp, &v))) {
+ return rval;
+ } else if (v)
+ rval = xfs_dir2_leaf_lookup(&args);
+ else
+ rval = xfs_dir2_node_lookup(&args);
+ if (rval == EEXIST)
+ rval = 0;
+ if (rval == 0)
+ *inum = args.inumber;
+ return rval;
+}
+
+/*
+ * Remove an entry from a directory.
+ */
+static int /* error */
+xfs_dir2_removename(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_inode_t *dp, /* incore directory inode */
+ char *name, /* name of entry to remove */
+ int namelen, /* name length of entry to remove */
+ xfs_ino_t ino, /* inode number of entry to remove */
+ xfs_fsblock_t *first, /* bmap's firstblock */
+ xfs_bmap_free_t *flist, /* bmap's freeblock list */
+ xfs_extlen_t total) /* bmap's total block count */
+{
+ xfs_da_args_t args; /* operation arguments */
+ int rval; /* return value */
+ int v; /* type-checking value */
+
+ ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
+ XFS_STATS_INC(xs_dir_remove);
+ /*
+ * Fill in the arg structure for this request.
+ */
+ args.name = name;
+ args.namelen = namelen;
+ args.hashval = xfs_da_hashname(name, namelen);
+ args.inumber = ino;
+ args.dp = dp;
+ args.firstblock = first;
+ args.flist = flist;
+ args.total = total;
+ args.whichfork = XFS_DATA_FORK;
+ args.trans = tp;
+ args.justcheck = args.addname = args.oknoent = 0;
+ /*
+ * Decide on what work routines to call based on the inode size.
+ */
+ if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
+ rval = xfs_dir2_sf_removename(&args);
+ else if ((rval = xfs_dir2_isblock(tp, dp, &v))) {
+ return rval;
+ } else if (v)
+ rval = xfs_dir2_block_removename(&args);
+ else if ((rval = xfs_dir2_isleaf(tp, dp, &v))) {
+ return rval;
+ } else if (v)
+ rval = xfs_dir2_leaf_removename(&args);
+ else
+ rval = xfs_dir2_node_removename(&args);
+ return rval;
+}
+
+/*
+ * Read a directory.
+ */
+static int /* error */
+xfs_dir2_getdents(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_inode_t *dp, /* incore directory inode */
+ uio_t *uio, /* caller's buffer control */
+ int *eofp) /* out: eof reached */
+{
+ int alignment; /* alignment required for ABI */
+ xfs_dirent_t *dbp; /* malloc'ed buffer */
+ xfs_dir2_put_t put; /* entry formatting routine */
+ int rval; /* return value */
+ int v; /* type-checking value */
+
+ ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
+ XFS_STATS_INC(xs_dir_getdents);
+ /*
+ * If our caller has given us a single contiguous aligned memory buffer,
+ * just work directly within that buffer. If it's in user memory,
+ * lock it down first.
+ */
+ alignment = sizeof(xfs_off_t) - 1;
+ if ((uio->uio_iovcnt == 1) &&
+ (((__psint_t)uio->uio_iov[0].iov_base & alignment) == 0) &&
+ ((uio->uio_iov[0].iov_len & alignment) == 0)) {
+ dbp = NULL;
+ put = xfs_dir2_put_dirent64_direct;
+ } else {
+ dbp = kmem_alloc(sizeof(*dbp) + MAXNAMELEN, KM_SLEEP);
+ put = xfs_dir2_put_dirent64_uio;
+ }
+
+ *eofp = 0;
+ /*
+ * Decide on what work routines to call based on the inode size.
+ */
+ if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
+ rval = xfs_dir2_sf_getdents(dp, uio, eofp, dbp, put);
+ else if ((rval = xfs_dir2_isblock(tp, dp, &v))) {
+ ;
+ } else if (v)
+ rval = xfs_dir2_block_getdents(tp, dp, uio, eofp, dbp, put);
+ else
+ rval = xfs_dir2_leaf_getdents(tp, dp, uio, eofp, dbp, put);
+ if (dbp != NULL)
+ kmem_free(dbp, sizeof(*dbp) + MAXNAMELEN);
+ return rval;
+}
+
+/*
+ * Replace the inode number of a directory entry.
+ */
+static int /* error */
+xfs_dir2_replace(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_inode_t *dp, /* incore directory inode */
+ char *name, /* name of entry to replace */
+ int namelen, /* name length of entry to replace */
+ xfs_ino_t inum, /* new inode number */
+ xfs_fsblock_t *first, /* bmap's firstblock */
+ xfs_bmap_free_t *flist, /* bmap's freeblock list */
+ xfs_extlen_t total) /* bmap's total block count */
+{
+ xfs_da_args_t args; /* operation arguments */
+ int rval; /* return value */
+ int v; /* type-checking value */
+
+ ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
+
+ if ((rval = xfs_dir_ino_validate(tp->t_mountp, inum))) {
+ return rval;
+ }
+ /*
+ * Fill in the arg structure for this request.
+ */
+ args.name = name;
+ args.namelen = namelen;
+ args.hashval = xfs_da_hashname(name, namelen);
+ args.inumber = inum;
+ args.dp = dp;
+ args.firstblock = first;
+ args.flist = flist;
+ args.total = total;
+ args.whichfork = XFS_DATA_FORK;
+ args.trans = tp;
+ args.justcheck = args.addname = args.oknoent = 0;
+ /*
+ * Decide on what work routines to call based on the inode size.
+ */
+ if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
+ rval = xfs_dir2_sf_replace(&args);
+ else if ((rval = xfs_dir2_isblock(tp, dp, &v))) {
+ return rval;
+ } else if (v)
+ rval = xfs_dir2_block_replace(&args);
+ else if ((rval = xfs_dir2_isleaf(tp, dp, &v))) {
+ return rval;
+ } else if (v)
+ rval = xfs_dir2_leaf_replace(&args);
+ else
+ rval = xfs_dir2_node_replace(&args);
+ return rval;
+}
+
+/*
+ * See if this entry can be added to the directory without allocating space.
+ */
+static int /* error */
+xfs_dir2_canenter(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_inode_t *dp, /* incore directory inode */
+ char *name, /* name of entry to add */
+ int namelen) /* name length of entry to add */
+{
+ xfs_da_args_t args; /* operation arguments */
+ int rval; /* return value */
+ int v; /* type-checking value */
+
+ ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
+ /*
+ * Fill in the arg structure for this request.
+ */
+ args.name = name;
+ args.namelen = namelen;
+ args.hashval = xfs_da_hashname(name, namelen);
+ args.inumber = 0;
+ args.dp = dp;
+ args.firstblock = NULL;
+ args.flist = NULL;
+ args.total = 0;
+ args.whichfork = XFS_DATA_FORK;
+ args.trans = tp;
+ args.justcheck = args.addname = args.oknoent = 1;
+ /*
+ * Decide on what work routines to call based on the inode size.
+ */
+ if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
+ rval = xfs_dir2_sf_addname(&args);
+ else if ((rval = xfs_dir2_isblock(tp, dp, &v))) {
+ return rval;
+ } else if (v)
+ rval = xfs_dir2_block_addname(&args);
+ else if ((rval = xfs_dir2_isleaf(tp, dp, &v))) {
+ return rval;
+ } else if (v)
+ rval = xfs_dir2_leaf_addname(&args);
+ else
+ rval = xfs_dir2_node_addname(&args);
+ return rval;
+}
+
+/*
+ * Dummy routine for shortform inode validation.
+ * Can't really do this.
+ */
+/* ARGSUSED */
+static int /* error */
+xfs_dir2_shortform_validate_ondisk(
+ xfs_mount_t *mp, /* filesystem mount point */
+ xfs_dinode_t *dip) /* ondisk inode */
+{
+ return 0;
+}
+
+/*
+ * Utility routines.
+ */
+
+/*
+ * Add a block to the directory.
+ * This routine is for data and free blocks, not leaf/node blocks
+ * which are handled by xfs_da_grow_inode.
+ */
+int /* error */
+xfs_dir2_grow_inode(
+ xfs_da_args_t *args, /* operation arguments */
+ int space, /* v2 dir's space XFS_DIR2_xxx_SPACE */
+ xfs_dir2_db_t *dbp) /* out: block number added */
+{
+ xfs_fileoff_t bno; /* directory offset of new block */
+ int count; /* count of filesystem blocks */
+ xfs_inode_t *dp; /* incore directory inode */
+ int error; /* error return value */
+ int got; /* blocks actually mapped */
+ int i; /* temp mapping index */
+ xfs_bmbt_irec_t map; /* single structure for bmap */
+ int mapi; /* mapping index */
+ xfs_bmbt_irec_t *mapp; /* bmap mapping structure(s) */
+ xfs_mount_t *mp; /* filesystem mount point */
+ int nmap; /* number of bmap entries */
+ xfs_trans_t *tp; /* transaction pointer */
+
+ xfs_dir2_trace_args_s("grow_inode", args, space);
+ dp = args->dp;
+ tp = args->trans;
+ mp = dp->i_mount;
+ /*
+ * Set lowest possible block in the space requested.
+ */
+ bno = XFS_B_TO_FSBT(mp, space * XFS_DIR2_SPACE_SIZE);
+ count = mp->m_dirblkfsbs;
+ /*
+ * Find the first hole for our block.
+ */
+ if ((error = xfs_bmap_first_unused(tp, dp, count, &bno, XFS_DATA_FORK))) {
+ return error;
+ }
+ nmap = 1;
+ ASSERT(args->firstblock != NULL);
+ /*
+ * Try mapping the new block contiguously (one extent).
+ */
+ if ((error = xfs_bmapi(tp, dp, bno, count,
+ XFS_BMAPI_WRITE|XFS_BMAPI_METADATA|XFS_BMAPI_CONTIG,
+ args->firstblock, args->total, &map, &nmap,
+ args->flist))) {
+ return error;
+ }
+ ASSERT(nmap <= 1);
+ /*
+ * Got it in 1.
+ */
+ if (nmap == 1) {
+ mapp = &map;
+ mapi = 1;
+ }
+ /*
+ * Didn't work and this is a multiple-fsb directory block.
+ * Try again with contiguous flag turned on.
+ */
+ else if (nmap == 0 && count > 1) {
+ xfs_fileoff_t b; /* current file offset */
+
+ /*
+ * Space for maximum number of mappings.
+ */
+ mapp = kmem_alloc(sizeof(*mapp) * count, KM_SLEEP);
+ /*
+ * Iterate until we get to the end of our block.
+ */
+ for (b = bno, mapi = 0; b < bno + count; ) {
+ int c; /* current fsb count */
+
+ /*
+ * Can't map more than MAX_NMAP at once.
+ */
+ nmap = MIN(XFS_BMAP_MAX_NMAP, count);
+ c = (int)(bno + count - b);
+ if ((error = xfs_bmapi(tp, dp, b, c,
+ XFS_BMAPI_WRITE|XFS_BMAPI_METADATA,
+ args->firstblock, args->total,
+ &mapp[mapi], &nmap, args->flist))) {
+ kmem_free(mapp, sizeof(*mapp) * count);
+ return error;
+ }
+ if (nmap < 1)
+ break;
+ /*
+ * Add this bunch into our table, go to the next offset.
+ */
+ mapi += nmap;
+ b = mapp[mapi - 1].br_startoff +
+ mapp[mapi - 1].br_blockcount;
+ }
+ }
+ /*
+ * Didn't work.
+ */
+ else {
+ mapi = 0;
+ mapp = NULL;
+ }
+ /*
+ * See how many fsb's we got.
+ */
+ for (i = 0, got = 0; i < mapi; i++)
+ got += mapp[i].br_blockcount;
+ /*
+ * Didn't get enough fsb's, or the first/last block's are wrong.
+ */
+ if (got != count || mapp[0].br_startoff != bno ||
+ mapp[mapi - 1].br_startoff + mapp[mapi - 1].br_blockcount !=
+ bno + count) {
+ if (mapp != &map)
+ kmem_free(mapp, sizeof(*mapp) * count);
+ return XFS_ERROR(ENOSPC);
+ }
+ /*
+ * Done with the temporary mapping table.
+ */
+ if (mapp != &map)
+ kmem_free(mapp, sizeof(*mapp) * count);
+ *dbp = XFS_DIR2_DA_TO_DB(mp, (xfs_dablk_t)bno);
+ /*
+ * Update file's size if this is the data space and it grew.
+ */
+ if (space == XFS_DIR2_DATA_SPACE) {
+ xfs_fsize_t size; /* directory file (data) size */
+
+ size = XFS_FSB_TO_B(mp, bno + count);
+ if (size > dp->i_d.di_size) {
+ dp->i_d.di_size = size;
+ xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE);
+ }
+ }
+ return 0;
+}
+
+/*
+ * See if the directory is a single-block form directory.
+ */
+int /* error */
+xfs_dir2_isblock(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_inode_t *dp, /* incore directory inode */
+ int *vp) /* out: 1 is block, 0 is not block */
+{
+ xfs_fileoff_t last; /* last file offset */
+ xfs_mount_t *mp; /* filesystem mount point */
+ int rval; /* return value */
+
+ mp = dp->i_mount;
+ if ((rval = xfs_bmap_last_offset(tp, dp, &last, XFS_DATA_FORK))) {
+ return rval;
+ }
+ rval = XFS_FSB_TO_B(mp, last) == mp->m_dirblksize;
+ ASSERT(rval == 0 || dp->i_d.di_size == mp->m_dirblksize);
+ *vp = rval;
+ return 0;
+}
+
+/*
+ * See if the directory is a single-leaf form directory.
+ */
+int /* error */
+xfs_dir2_isleaf(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_inode_t *dp, /* incore directory inode */
+ int *vp) /* out: 1 is leaf, 0 is not leaf */
+{
+ xfs_fileoff_t last; /* last file offset */
+ xfs_mount_t *mp; /* filesystem mount point */
+ int rval; /* return value */
+
+ mp = dp->i_mount;
+ if ((rval = xfs_bmap_last_offset(tp, dp, &last, XFS_DATA_FORK))) {
+ return rval;
+ }
+ *vp = last == mp->m_dirleafblk + (1 << mp->m_sb.sb_dirblklog);
+ return 0;
+}
+
+/*
+ * Getdents put routine for 64-bit ABI, direct form.
+ */
+static int /* error */
+xfs_dir2_put_dirent64_direct(
+ xfs_dir2_put_args_t *pa) /* argument bundle */
+{
+ xfs_dirent_t *idbp; /* dirent pointer */
+ iovec_t *iovp; /* io vector */
+ int namelen; /* entry name length */
+ int reclen; /* entry total length */
+ uio_t *uio; /* I/O control */
+
+ namelen = pa->namelen;
+ reclen = DIRENTSIZE(namelen);
+ uio = pa->uio;
+ /*
+ * Won't fit in the remaining space.
+ */
+ if (reclen > uio->uio_resid) {
+ pa->done = 0;
+ return 0;
+ }
+ iovp = uio->uio_iov;
+ idbp = (xfs_dirent_t *)iovp->iov_base;
+ iovp->iov_base = (char *)idbp + reclen;
+ iovp->iov_len -= reclen;
+ uio->uio_resid -= reclen;
+ idbp->d_reclen = reclen;
+ idbp->d_ino = pa->ino;
+ idbp->d_off = pa->cook;
+ idbp->d_name[namelen] = '\0';
+ pa->done = 1;
+ memcpy(idbp->d_name, pa->name, namelen);
+ return 0;
+}
+
+/*
+ * Getdents put routine for 64-bit ABI, uio form.
+ */
+static int /* error */
+xfs_dir2_put_dirent64_uio(
+ xfs_dir2_put_args_t *pa) /* argument bundle */
+{
+ xfs_dirent_t *idbp; /* dirent pointer */
+ int namelen; /* entry name length */
+ int reclen; /* entry total length */
+ int rval; /* return value */
+ uio_t *uio; /* I/O control */
+
+ namelen = pa->namelen;
+ reclen = DIRENTSIZE(namelen);
+ uio = pa->uio;
+ /*
+ * Won't fit in the remaining space.
+ */
+ if (reclen > uio->uio_resid) {
+ pa->done = 0;
+ return 0;
+ }
+ idbp = pa->dbp;
+ idbp->d_reclen = reclen;
+ idbp->d_ino = pa->ino;
+ idbp->d_off = pa->cook;
+ idbp->d_name[namelen] = '\0';
+ memcpy(idbp->d_name, pa->name, namelen);
+ rval = uio_read((caddr_t)idbp, reclen, uio);
+ pa->done = (rval == 0);
+ return rval;
+}
+
+/*
+ * Remove the given block from the directory.
+ * This routine is used for data and free blocks, leaf/node are done
+ * by xfs_da_shrink_inode.
+ */
+int
+xfs_dir2_shrink_inode(
+ xfs_da_args_t *args, /* operation arguments */
+ xfs_dir2_db_t db, /* directory block number */
+ xfs_dabuf_t *bp) /* block's buffer */
+{
+ xfs_fileoff_t bno; /* directory file offset */
+ xfs_dablk_t da; /* directory file offset */
+ int done; /* bunmap is finished */
+ xfs_inode_t *dp; /* incore directory inode */
+ int error; /* error return value */
+ xfs_mount_t *mp; /* filesystem mount point */
+ xfs_trans_t *tp; /* transaction pointer */
+
+ xfs_dir2_trace_args_db("shrink_inode", args, db, bp);
+ dp = args->dp;
+ mp = dp->i_mount;
+ tp = args->trans;
+ da = XFS_DIR2_DB_TO_DA(mp, db);
+ /*
+ * Unmap the fsblock(s).
+ */
+ if ((error = xfs_bunmapi(tp, dp, da, mp->m_dirblkfsbs,
+ XFS_BMAPI_METADATA, 0, args->firstblock, args->flist,
+ &done))) {
+ /*
+ * ENOSPC actually can happen if we're in a removename with
+ * no space reservation, and the resulting block removal
+ * would cause a bmap btree split or conversion from extents
+ * to btree. This can only happen for un-fragmented
+ * directory blocks, since you need to be punching out
+ * the middle of an extent.
+ * In this case we need to leave the block in the file,
+ * and not binval it.
+ * So the block has to be in a consistent empty state
+ * and appropriately logged.
+ * We don't free up the buffer, the caller can tell it
+ * hasn't happened since it got an error back.
+ */
+ return error;
+ }
+ ASSERT(done);
+ /*
+ * Invalidate the buffer from the transaction.
+ */
+ xfs_da_binval(tp, bp);
+ /*
+ * If it's not a data block, we're done.
+ */
+ if (db >= XFS_DIR2_LEAF_FIRSTDB(mp))
+ return 0;
+ /*
+ * If the block isn't the last one in the directory, we're done.
+ */
+ if (dp->i_d.di_size > XFS_DIR2_DB_OFF_TO_BYTE(mp, db + 1, 0))
+ return 0;
+ bno = da;
+ if ((error = xfs_bmap_last_before(tp, dp, &bno, XFS_DATA_FORK))) {
+ /*
+ * This can't really happen unless there's kernel corruption.
+ */
+ return error;
+ }
+ if (db == mp->m_dirdatablk)
+ ASSERT(bno == 0);
+ else
+ ASSERT(bno > 0);
+ /*
+ * Set the size to the new last block.
+ */
+ dp->i_d.di_size = XFS_FSB_TO_B(mp, bno);
+ xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE);
+ return 0;
+}
diff --git a/fs/xfs/xfs_dir2.h b/fs/xfs/xfs_dir2.h
new file mode 100644
index 00000000000000..8f4fc7f23bcd89
--- /dev/null
+++ b/fs/xfs/xfs_dir2.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2000-2001 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_DIR2_H__
+#define __XFS_DIR2_H__
+
+struct uio;
+struct xfs_dabuf;
+struct xfs_da_args;
+struct xfs_dir2_put_args;
+struct xfs_inode;
+struct xfs_trans;
+
+/*
+ * Directory version 2.
+ * There are 4 possible formats:
+ * shortform
+ * single block - data with embedded leaf at the end
+ * multiple data blocks, single leaf+freeindex block
+ * data blocks, node&leaf blocks (btree), freeindex blocks
+ *
+ * The shortform format is in xfs_dir2_sf.h.
+ * The single block format is in xfs_dir2_block.h.
+ * The data block format is in xfs_dir2_data.h.
+ * The leaf and freeindex block formats are in xfs_dir2_leaf.h.
+ * Node blocks are the same as the other version, in xfs_da_btree.h.
+ */
+
+/*
+ * Byte offset in data block and shortform entry.
+ */
+typedef __uint16_t xfs_dir2_data_off_t;
+#define NULLDATAOFF 0xffffU
+typedef uint xfs_dir2_data_aoff_t; /* argument form */
+
+/*
+ * Directory block number (logical dirblk in file)
+ */
+typedef __uint32_t xfs_dir2_db_t;
+
+/*
+ * Byte offset in a directory.
+ */
+typedef xfs_off_t xfs_dir2_off_t;
+
+/*
+ * For getdents, argument struct for put routines.
+ */
+typedef int (*xfs_dir2_put_t)(struct xfs_dir2_put_args *pa);
+typedef struct xfs_dir2_put_args {
+ xfs_off_t cook; /* cookie of (next) entry */
+ xfs_intino_t ino; /* inode number */
+ struct xfs_dirent *dbp; /* buffer pointer */
+ char *name; /* directory entry name */
+ int namelen; /* length of name */
+ int done; /* output: set if value was stored */
+ xfs_dir2_put_t put; /* put function ptr (i/o) */
+ struct uio *uio; /* uio control structure */
+} xfs_dir2_put_args_t;
+
+#define XFS_DIR_IS_V2(mp) ((mp)->m_dirversion == 2)
+extern xfs_dirops_t xfsv2_dirops;
+
+/*
+ * Other interfaces used by the rest of the dir v2 code.
+ */
+extern int
+ xfs_dir2_grow_inode(struct xfs_da_args *args, int space,
+ xfs_dir2_db_t *dbp);
+
+extern int
+ xfs_dir2_isblock(struct xfs_trans *tp, struct xfs_inode *dp, int *vp);
+
+extern int
+ xfs_dir2_isleaf(struct xfs_trans *tp, struct xfs_inode *dp, int *vp);
+
+extern int
+ xfs_dir2_shrink_inode(struct xfs_da_args *args, xfs_dir2_db_t db,
+ struct xfs_dabuf *bp);
+
+#endif /* __XFS_DIR2_H__ */
diff --git a/fs/xfs/xfs_dir2_block.c b/fs/xfs/xfs_dir2_block.c
new file mode 100644
index 00000000000000..696f77da944596
--- /dev/null
+++ b/fs/xfs/xfs_dir2_block.c
@@ -0,0 +1,1248 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+/*
+ * xfs_dir2_block.c
+ * XFS V2 directory implementation, single-block form.
+ * See xfs_dir2_block.h for the format.
+ */
+
+#include "xfs.h"
+
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode_item.h"
+#include "xfs_inode.h"
+#include "xfs_da_btree.h"
+#include "xfs_dir_leaf.h"
+#include "xfs_dir2_data.h"
+#include "xfs_dir2_leaf.h"
+#include "xfs_dir2_block.h"
+#include "xfs_dir2_trace.h"
+#include "xfs_error.h"
+
+/*
+ * Local function prototypes.
+ */
+static void xfs_dir2_block_log_leaf(xfs_trans_t *tp, xfs_dabuf_t *bp, int first,
+ int last);
+static void xfs_dir2_block_log_tail(xfs_trans_t *tp, xfs_dabuf_t *bp);
+static int xfs_dir2_block_lookup_int(xfs_da_args_t *args, xfs_dabuf_t **bpp,
+ int *entno);
+static int xfs_dir2_block_sort(const void *a, const void *b);
+
+/*
+ * Add an entry to a block directory.
+ */
+int /* error */
+xfs_dir2_block_addname(
+ xfs_da_args_t *args) /* directory op arguments */
+{
+ xfs_dir2_data_free_t *bf; /* bestfree table in block */
+ xfs_dir2_block_t *block; /* directory block structure */
+ xfs_dir2_leaf_entry_t *blp; /* block leaf entries */
+ xfs_dabuf_t *bp; /* buffer for block */
+ xfs_dir2_block_tail_t *btp; /* block tail */
+ int compact; /* need to compact leaf ents */
+ xfs_dir2_data_entry_t *dep; /* block data entry */
+ xfs_inode_t *dp; /* directory inode */
+ xfs_dir2_data_unused_t *dup; /* block unused entry */
+ int error; /* error return value */
+ xfs_dir2_data_unused_t *enddup=NULL; /* unused at end of data */
+ xfs_dahash_t hash; /* hash value of found entry */
+ int high; /* high index for binary srch */
+ int highstale; /* high stale index */
+ int lfloghigh=0; /* last final leaf to log */
+ int lfloglow=0; /* first final leaf to log */
+ int len; /* length of the new entry */
+ int low; /* low index for binary srch */
+ int lowstale; /* low stale index */
+ int mid=0; /* midpoint for binary srch */
+ xfs_mount_t *mp; /* filesystem mount point */
+ int needlog; /* need to log header */
+ int needscan; /* need to rescan freespace */
+ xfs_dir2_data_off_t *tagp; /* pointer to tag value */
+ xfs_trans_t *tp; /* transaction structure */
+
+ xfs_dir2_trace_args("block_addname", args);
+ dp = args->dp;
+ tp = args->trans;
+ mp = dp->i_mount;
+ /*
+ * Read the (one and only) directory block into dabuf bp.
+ */
+ if ((error =
+ xfs_da_read_buf(tp, dp, mp->m_dirdatablk, -1, &bp, XFS_DATA_FORK))) {
+ return error;
+ }
+ ASSERT(bp != NULL);
+ block = bp->data;
+ /*
+ * Check the magic number, corrupted if wrong.
+ */
+ if (unlikely(INT_GET(block->hdr.magic, ARCH_CONVERT)
+ != XFS_DIR2_BLOCK_MAGIC)) {
+ XFS_CORRUPTION_ERROR("xfs_dir2_block_addname",
+ XFS_ERRLEVEL_LOW, mp, block);
+ xfs_da_brelse(tp, bp);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ len = XFS_DIR2_DATA_ENTSIZE(args->namelen);
+ /*
+ * Set up pointers to parts of the block.
+ */
+ bf = block->hdr.bestfree;
+ btp = XFS_DIR2_BLOCK_TAIL_P(mp, block);
+ blp = XFS_DIR2_BLOCK_LEAF_P_ARCH(btp, ARCH_CONVERT);
+ /*
+ * No stale entries? Need space for entry and new leaf.
+ */
+ if (INT_ISZERO(btp->stale, ARCH_CONVERT)) {
+ /*
+ * Tag just before the first leaf entry.
+ */
+ tagp = (xfs_dir2_data_off_t *)blp - 1;
+ /*
+ * Data object just before the first leaf entry.
+ */
+ enddup = (xfs_dir2_data_unused_t *)((char *)block + INT_GET(*tagp, ARCH_CONVERT));
+ /*
+ * If it's not free then can't do this add without cleaning up:
+ * the space before the first leaf entry needs to be free so it
+ * can be expanded to hold the pointer to the new entry.
+ */
+ if (INT_GET(enddup->freetag, ARCH_CONVERT) != XFS_DIR2_DATA_FREE_TAG)
+ dup = enddup = NULL;
+ /*
+ * Check out the biggest freespace and see if it's the same one.
+ */
+ else {
+ dup = (xfs_dir2_data_unused_t *)
+ ((char *)block + INT_GET(bf[0].offset, ARCH_CONVERT));
+ if (dup == enddup) {
+ /*
+ * It is the biggest freespace, is it too small
+ * to hold the new leaf too?
+ */
+ if (INT_GET(dup->length, ARCH_CONVERT) < len + (uint)sizeof(*blp)) {
+ /*
+ * Yes, we use the second-largest
+ * entry instead if it works.
+ */
+ if (INT_GET(bf[1].length, ARCH_CONVERT) >= len)
+ dup = (xfs_dir2_data_unused_t *)
+ ((char *)block +
+ INT_GET(bf[1].offset, ARCH_CONVERT));
+ else
+ dup = NULL;
+ }
+ } else {
+ /*
+ * Not the same free entry,
+ * just check its length.
+ */
+ if (INT_GET(dup->length, ARCH_CONVERT) < len) {
+ dup = NULL;
+ }
+ }
+ }
+ compact = 0;
+ }
+ /*
+ * If there are stale entries we'll use one for the leaf.
+ * Is the biggest entry enough to avoid compaction?
+ */
+ else if (INT_GET(bf[0].length, ARCH_CONVERT) >= len) {
+ dup = (xfs_dir2_data_unused_t *)
+ ((char *)block + INT_GET(bf[0].offset, ARCH_CONVERT));
+ compact = 0;
+ }
+ /*
+ * Will need to compact to make this work.
+ */
+ else {
+ /*
+ * Tag just before the first leaf entry.
+ */
+ tagp = (xfs_dir2_data_off_t *)blp - 1;
+ /*
+ * Data object just before the first leaf entry.
+ */
+ dup = (xfs_dir2_data_unused_t *)((char *)block + INT_GET(*tagp, ARCH_CONVERT));
+ /*
+ * If it's not free then the data will go where the
+ * leaf data starts now, if it works at all.
+ */
+ if (INT_GET(dup->freetag, ARCH_CONVERT) == XFS_DIR2_DATA_FREE_TAG) {
+ if (INT_GET(dup->length, ARCH_CONVERT) + (INT_GET(btp->stale, ARCH_CONVERT) - 1) *
+ (uint)sizeof(*blp) < len)
+ dup = NULL;
+ } else if ((INT_GET(btp->stale, ARCH_CONVERT) - 1) * (uint)sizeof(*blp) < len)
+ dup = NULL;
+ else
+ dup = (xfs_dir2_data_unused_t *)blp;
+ compact = 1;
+ }
+ /*
+ * If this isn't a real add, we're done with the buffer.
+ */
+ if (args->justcheck)
+ xfs_da_brelse(tp, bp);
+ /*
+ * If we don't have space for the new entry & leaf ...
+ */
+ if (!dup) {
+ /*
+ * Not trying to actually do anything, or don't have
+ * a space reservation: return no-space.
+ */
+ if (args->justcheck || args->total == 0)
+ return XFS_ERROR(ENOSPC);
+ /*
+ * Convert to the next larger format.
+ * Then add the new entry in that format.
+ */
+ error = xfs_dir2_block_to_leaf(args, bp);
+ xfs_da_buf_done(bp);
+ if (error)
+ return error;
+ return xfs_dir2_leaf_addname(args);
+ }
+ /*
+ * Just checking, and it would work, so say so.
+ */
+ if (args->justcheck)
+ return 0;
+ needlog = needscan = 0;
+ /*
+ * If need to compact the leaf entries, do it now.
+ * Leave the highest-numbered stale entry stale.
+ * XXX should be the one closest to mid but mid is not yet computed.
+ */
+ if (compact) {
+ int fromidx; /* source leaf index */
+ int toidx; /* target leaf index */
+
+ for (fromidx = toidx = INT_GET(btp->count, ARCH_CONVERT) - 1,
+ highstale = lfloghigh = -1;
+ fromidx >= 0;
+ fromidx--) {
+ if (INT_GET(blp[fromidx].address, ARCH_CONVERT) == XFS_DIR2_NULL_DATAPTR) {
+ if (highstale == -1)
+ highstale = toidx;
+ else {
+ if (lfloghigh == -1)
+ lfloghigh = toidx;
+ continue;
+ }
+ }
+ if (fromidx < toidx)
+ blp[toidx] = blp[fromidx];
+ toidx--;
+ }
+ lfloglow = toidx + 1 - (INT_GET(btp->stale, ARCH_CONVERT) - 1);
+ lfloghigh -= INT_GET(btp->stale, ARCH_CONVERT) - 1;
+ INT_MOD(btp->count, ARCH_CONVERT, -(INT_GET(btp->stale, ARCH_CONVERT) - 1));
+ xfs_dir2_data_make_free(tp, bp,
+ (xfs_dir2_data_aoff_t)((char *)blp - (char *)block),
+ (xfs_dir2_data_aoff_t)((INT_GET(btp->stale, ARCH_CONVERT) - 1) * sizeof(*blp)),
+ &needlog, &needscan);
+ blp += INT_GET(btp->stale, ARCH_CONVERT) - 1;
+ INT_SET(btp->stale, ARCH_CONVERT, 1);
+ /*
+ * If we now need to rebuild the bestfree map, do so.
+ * This needs to happen before the next call to use_free.
+ */
+ if (needscan) {
+ xfs_dir2_data_freescan(mp, (xfs_dir2_data_t *)block,
+ &needlog, NULL);
+ needscan = 0;
+ }
+ }
+ /*
+ * Set leaf logging boundaries to impossible state.
+ * For the no-stale case they're set explicitly.
+ */
+ else if (INT_GET(btp->stale, ARCH_CONVERT)) {
+ lfloglow = INT_GET(btp->count, ARCH_CONVERT);
+ lfloghigh = -1;
+ }
+ /*
+ * Find the slot that's first lower than our hash value, -1 if none.
+ */
+ for (low = 0, high = INT_GET(btp->count, ARCH_CONVERT) - 1; low <= high; ) {
+ mid = (low + high) >> 1;
+ if ((hash = INT_GET(blp[mid].hashval, ARCH_CONVERT)) == args->hashval)
+ break;
+ if (hash < args->hashval)
+ low = mid + 1;
+ else
+ high = mid - 1;
+ }
+ while (mid >= 0 && INT_GET(blp[mid].hashval, ARCH_CONVERT) >= args->hashval) {
+ mid--;
+ }
+ /*
+ * No stale entries, will use enddup space to hold new leaf.
+ */
+ if (INT_ISZERO(btp->stale, ARCH_CONVERT)) {
+ /*
+ * Mark the space needed for the new leaf entry, now in use.
+ */
+ xfs_dir2_data_use_free(tp, bp, enddup,
+ (xfs_dir2_data_aoff_t)
+ ((char *)enddup - (char *)block + INT_GET(enddup->length, ARCH_CONVERT) -
+ sizeof(*blp)),
+ (xfs_dir2_data_aoff_t)sizeof(*blp),
+ &needlog, &needscan);
+ /*
+ * Update the tail (entry count).
+ */
+ INT_MOD(btp->count, ARCH_CONVERT, +1);
+ /*
+ * If we now need to rebuild the bestfree map, do so.
+ * This needs to happen before the next call to use_free.
+ */
+ if (needscan) {
+ xfs_dir2_data_freescan(mp, (xfs_dir2_data_t *)block,
+ &needlog, NULL);
+ needscan = 0;
+ }
+ /*
+ * Adjust pointer to the first leaf entry, we're about to move
+ * the table up one to open up space for the new leaf entry.
+ * Then adjust our index to match.
+ */
+ blp--;
+ mid++;
+ if (mid)
+ memmove(blp, &blp[1], mid * sizeof(*blp));
+ lfloglow = 0;
+ lfloghigh = mid;
+ }
+ /*
+ * Use a stale leaf for our new entry.
+ */
+ else {
+ for (lowstale = mid;
+ lowstale >= 0 &&
+ INT_GET(blp[lowstale].address, ARCH_CONVERT) != XFS_DIR2_NULL_DATAPTR;
+ lowstale--)
+ continue;
+ for (highstale = mid + 1;
+ highstale < INT_GET(btp->count, ARCH_CONVERT) &&
+ INT_GET(blp[highstale].address, ARCH_CONVERT) != XFS_DIR2_NULL_DATAPTR &&
+ (lowstale < 0 || mid - lowstale > highstale - mid);
+ highstale++)
+ continue;
+ /*
+ * Move entries toward the low-numbered stale entry.
+ */
+ if (lowstale >= 0 &&
+ (highstale == INT_GET(btp->count, ARCH_CONVERT) ||
+ mid - lowstale <= highstale - mid)) {
+ if (mid - lowstale)
+ memmove(&blp[lowstale], &blp[lowstale + 1],
+ (mid - lowstale) * sizeof(*blp));
+ lfloglow = MIN(lowstale, lfloglow);
+ lfloghigh = MAX(mid, lfloghigh);
+ }
+ /*
+ * Move entries toward the high-numbered stale entry.
+ */
+ else {
+ ASSERT(highstale < INT_GET(btp->count, ARCH_CONVERT));
+ mid++;
+ if (highstale - mid)
+ memmove(&blp[mid + 1], &blp[mid],
+ (highstale - mid) * sizeof(*blp));
+ lfloglow = MIN(mid, lfloglow);
+ lfloghigh = MAX(highstale, lfloghigh);
+ }
+ INT_MOD(btp->stale, ARCH_CONVERT, -1);
+ }
+ /*
+ * Point to the new data entry.
+ */
+ dep = (xfs_dir2_data_entry_t *)dup;
+ /*
+ * Fill in the leaf entry.
+ */
+ INT_SET(blp[mid].hashval, ARCH_CONVERT, args->hashval);
+ INT_SET(blp[mid].address, ARCH_CONVERT, XFS_DIR2_BYTE_TO_DATAPTR(mp, (char *)dep - (char *)block));
+ xfs_dir2_block_log_leaf(tp, bp, lfloglow, lfloghigh);
+ /*
+ * Mark space for the data entry used.
+ */
+ xfs_dir2_data_use_free(tp, bp, dup,
+ (xfs_dir2_data_aoff_t)((char *)dup - (char *)block),
+ (xfs_dir2_data_aoff_t)len, &needlog, &needscan);
+ /*
+ * Create the new data entry.
+ */
+ INT_SET(dep->inumber, ARCH_CONVERT, args->inumber);
+ dep->namelen = args->namelen;
+ memcpy(dep->name, args->name, args->namelen);
+ tagp = XFS_DIR2_DATA_ENTRY_TAG_P(dep);
+ INT_SET(*tagp, ARCH_CONVERT, (xfs_dir2_data_off_t)((char *)dep - (char *)block));
+ /*
+ * Clean up the bestfree array and log the header, tail, and entry.
+ */
+ if (needscan)
+ xfs_dir2_data_freescan(mp, (xfs_dir2_data_t *)block, &needlog,
+ NULL);
+ if (needlog)
+ xfs_dir2_data_log_header(tp, bp);
+ xfs_dir2_block_log_tail(tp, bp);
+ xfs_dir2_data_log_entry(tp, bp, dep);
+ xfs_dir2_data_check(dp, bp);
+ xfs_da_buf_done(bp);
+ return 0;
+}
+
+/*
+ * Readdir for block directories.
+ */
+int /* error */
+xfs_dir2_block_getdents(
+ xfs_trans_t *tp, /* transaction (NULL) */
+ xfs_inode_t *dp, /* incore inode */
+ uio_t *uio, /* caller's buffer control */
+ int *eofp, /* eof reached? (out) */
+ xfs_dirent_t *dbp, /* caller's buffer */
+ xfs_dir2_put_t put) /* abi's formatting function */
+{
+ xfs_dir2_block_t *block; /* directory block structure */
+ xfs_dabuf_t *bp; /* buffer for block */
+ xfs_dir2_block_tail_t *btp; /* block tail */
+ xfs_dir2_data_entry_t *dep; /* block data entry */
+ xfs_dir2_data_unused_t *dup; /* block unused entry */
+ char *endptr; /* end of the data entries */
+ int error; /* error return value */
+ xfs_mount_t *mp; /* filesystem mount point */
+ xfs_dir2_put_args_t p; /* arg package for put rtn */
+ char *ptr; /* current data entry */
+ int wantoff; /* starting block offset */
+
+ mp = dp->i_mount;
+ /*
+ * If the block number in the offset is out of range, we're done.
+ */
+ if (XFS_DIR2_DATAPTR_TO_DB(mp, uio->uio_offset) > mp->m_dirdatablk) {
+ *eofp = 1;
+ return 0;
+ }
+ /*
+ * Can't read the block, give up, else get dabuf in bp.
+ */
+ if ((error =
+ xfs_da_read_buf(tp, dp, mp->m_dirdatablk, -1, &bp, XFS_DATA_FORK))) {
+ return error;
+ }
+ ASSERT(bp != NULL);
+ /*
+ * Extract the byte offset we start at from the seek pointer.
+ * We'll skip entries before this.
+ */
+ wantoff = XFS_DIR2_DATAPTR_TO_OFF(mp, uio->uio_offset);
+ block = bp->data;
+ xfs_dir2_data_check(dp, bp);
+ /*
+ * Set up values for the loop.
+ */
+ btp = XFS_DIR2_BLOCK_TAIL_P(mp, block);
+ ptr = (char *)block->u;
+ endptr = (char *)XFS_DIR2_BLOCK_LEAF_P_ARCH(btp, ARCH_CONVERT);
+ p.dbp = dbp;
+ p.put = put;
+ p.uio = uio;
+ /*
+ * Loop over the data portion of the block.
+ * Each object is a real entry (dep) or an unused one (dup).
+ */
+ while (ptr < endptr) {
+ dup = (xfs_dir2_data_unused_t *)ptr;
+ /*
+ * Unused, skip it.
+ */
+ if (INT_GET(dup->freetag, ARCH_CONVERT) == XFS_DIR2_DATA_FREE_TAG) {
+ ptr += INT_GET(dup->length, ARCH_CONVERT);
+ continue;
+ }
+
+ dep = (xfs_dir2_data_entry_t *)ptr;
+
+ /*
+ * Bump pointer for the next iteration.
+ */
+ ptr += XFS_DIR2_DATA_ENTSIZE(dep->namelen);
+ /*
+ * The entry is before the desired starting point, skip it.
+ */
+ if ((char *)dep - (char *)block < wantoff)
+ continue;
+ /*
+ * Set up argument structure for put routine.
+ */
+ p.namelen = dep->namelen;
+
+ p.cook = XFS_DIR2_DB_OFF_TO_DATAPTR(mp, mp->m_dirdatablk,
+ ptr - (char *)block);
+ p.ino = INT_GET(dep->inumber, ARCH_CONVERT);
+#if XFS_BIG_INUMS
+ p.ino += mp->m_inoadd;
+#endif
+ p.name = (char *)dep->name;
+
+ /*
+ * Put the entry in the caller's buffer.
+ */
+ error = p.put(&p);
+
+ /*
+ * If it didn't fit, set the final offset to here & return.
+ */
+ if (!p.done) {
+ uio->uio_offset =
+ XFS_DIR2_DB_OFF_TO_DATAPTR(mp, mp->m_dirdatablk,
+ (char *)dep - (char *)block);
+ xfs_da_brelse(tp, bp);
+ return error;
+ }
+ }
+
+ /*
+ * Reached the end of the block.
+ * Set the offset to a nonexistent block 1 and return.
+ */
+ *eofp = 1;
+
+ uio->uio_offset =
+ XFS_DIR2_DB_OFF_TO_DATAPTR(mp, mp->m_dirdatablk + 1, 0);
+
+ xfs_da_brelse(tp, bp);
+
+ return 0;
+}
+
+/*
+ * Log leaf entries from the block.
+ */
+static void
+xfs_dir2_block_log_leaf(
+ xfs_trans_t *tp, /* transaction structure */
+ xfs_dabuf_t *bp, /* block buffer */
+ int first, /* index of first logged leaf */
+ int last) /* index of last logged leaf */
+{
+ xfs_dir2_block_t *block; /* directory block structure */
+ xfs_dir2_leaf_entry_t *blp; /* block leaf entries */
+ xfs_dir2_block_tail_t *btp; /* block tail */
+ xfs_mount_t *mp; /* filesystem mount point */
+
+ mp = tp->t_mountp;
+ block = bp->data;
+ btp = XFS_DIR2_BLOCK_TAIL_P(mp, block);
+ blp = XFS_DIR2_BLOCK_LEAF_P_ARCH(btp, ARCH_CONVERT);
+ xfs_da_log_buf(tp, bp, (uint)((char *)&blp[first] - (char *)block),
+ (uint)((char *)&blp[last + 1] - (char *)block - 1));
+}
+
+/*
+ * Log the block tail.
+ */
+static void
+xfs_dir2_block_log_tail(
+ xfs_trans_t *tp, /* transaction structure */
+ xfs_dabuf_t *bp) /* block buffer */
+{
+ xfs_dir2_block_t *block; /* directory block structure */
+ xfs_dir2_block_tail_t *btp; /* block tail */
+ xfs_mount_t *mp; /* filesystem mount point */
+
+ mp = tp->t_mountp;
+ block = bp->data;
+ btp = XFS_DIR2_BLOCK_TAIL_P(mp, block);
+ xfs_da_log_buf(tp, bp, (uint)((char *)btp - (char *)block),
+ (uint)((char *)(btp + 1) - (char *)block - 1));
+}
+
+/*
+ * Look up an entry in the block. This is the external routine,
+ * xfs_dir2_block_lookup_int does the real work.
+ */
+int /* error */
+xfs_dir2_block_lookup(
+ xfs_da_args_t *args) /* dir lookup arguments */
+{
+ xfs_dir2_block_t *block; /* block structure */
+ xfs_dir2_leaf_entry_t *blp; /* block leaf entries */
+ xfs_dabuf_t *bp; /* block buffer */
+ xfs_dir2_block_tail_t *btp; /* block tail */
+ xfs_dir2_data_entry_t *dep; /* block data entry */
+ xfs_inode_t *dp; /* incore inode */
+ int ent; /* entry index */
+ int error; /* error return value */
+ xfs_mount_t *mp; /* filesystem mount point */
+
+ xfs_dir2_trace_args("block_lookup", args);
+ /*
+ * Get the buffer, look up the entry.
+ * If not found (ENOENT) then return, have no buffer.
+ */
+ if ((error = xfs_dir2_block_lookup_int(args, &bp, &ent)))
+ return error;
+ dp = args->dp;
+ mp = dp->i_mount;
+ block = bp->data;
+ xfs_dir2_data_check(dp, bp);
+ btp = XFS_DIR2_BLOCK_TAIL_P(mp, block);
+ blp = XFS_DIR2_BLOCK_LEAF_P_ARCH(btp, ARCH_CONVERT);
+ /*
+ * Get the offset from the leaf entry, to point to the data.
+ */
+ dep = (xfs_dir2_data_entry_t *)
+ ((char *)block + XFS_DIR2_DATAPTR_TO_OFF(mp, INT_GET(blp[ent].address, ARCH_CONVERT)));
+ /*
+ * Fill in inode number, release the block.
+ */
+ args->inumber = INT_GET(dep->inumber, ARCH_CONVERT);
+ xfs_da_brelse(args->trans, bp);
+ return XFS_ERROR(EEXIST);
+}
+
+/*
+ * Internal block lookup routine.
+ */
+static int /* error */
+xfs_dir2_block_lookup_int(
+ xfs_da_args_t *args, /* dir lookup arguments */
+ xfs_dabuf_t **bpp, /* returned block buffer */
+ int *entno) /* returned entry number */
+{
+ xfs_dir2_dataptr_t addr; /* data entry address */
+ xfs_dir2_block_t *block; /* block structure */
+ xfs_dir2_leaf_entry_t *blp; /* block leaf entries */
+ xfs_dabuf_t *bp; /* block buffer */
+ xfs_dir2_block_tail_t *btp; /* block tail */
+ xfs_dir2_data_entry_t *dep; /* block data entry */
+ xfs_inode_t *dp; /* incore inode */
+ int error; /* error return value */
+ xfs_dahash_t hash; /* found hash value */
+ int high; /* binary search high index */
+ int low; /* binary search low index */
+ int mid; /* binary search current idx */
+ xfs_mount_t *mp; /* filesystem mount point */
+ xfs_trans_t *tp; /* transaction pointer */
+
+ dp = args->dp;
+ tp = args->trans;
+ mp = dp->i_mount;
+ /*
+ * Read the buffer, return error if we can't get it.
+ */
+ if ((error =
+ xfs_da_read_buf(tp, dp, mp->m_dirdatablk, -1, &bp, XFS_DATA_FORK))) {
+ return error;
+ }
+ ASSERT(bp != NULL);
+ block = bp->data;
+ xfs_dir2_data_check(dp, bp);
+ btp = XFS_DIR2_BLOCK_TAIL_P(mp, block);
+ blp = XFS_DIR2_BLOCK_LEAF_P_ARCH(btp, ARCH_CONVERT);
+ /*
+ * Loop doing a binary search for our hash value.
+ * Find our entry, ENOENT if it's not there.
+ */
+ for (low = 0, high = INT_GET(btp->count, ARCH_CONVERT) - 1; ; ) {
+ ASSERT(low <= high);
+ mid = (low + high) >> 1;
+ if ((hash = INT_GET(blp[mid].hashval, ARCH_CONVERT)) == args->hashval)
+ break;
+ if (hash < args->hashval)
+ low = mid + 1;
+ else
+ high = mid - 1;
+ if (low > high) {
+ ASSERT(args->oknoent);
+ xfs_da_brelse(tp, bp);
+ return XFS_ERROR(ENOENT);
+ }
+ }
+ /*
+ * Back up to the first one with the right hash value.
+ */
+ while (mid > 0 && INT_GET(blp[mid - 1].hashval, ARCH_CONVERT) == args->hashval) {
+ mid--;
+ }
+ /*
+ * Now loop forward through all the entries with the
+ * right hash value looking for our name.
+ */
+ do {
+ if ((addr = INT_GET(blp[mid].address, ARCH_CONVERT)) == XFS_DIR2_NULL_DATAPTR)
+ continue;
+ /*
+ * Get pointer to the entry from the leaf.
+ */
+ dep = (xfs_dir2_data_entry_t *)
+ ((char *)block + XFS_DIR2_DATAPTR_TO_OFF(mp, addr));
+ /*
+ * Compare, if it's right give back buffer & entry number.
+ */
+ if (dep->namelen == args->namelen &&
+ dep->name[0] == args->name[0] &&
+ memcmp(dep->name, args->name, args->namelen) == 0) {
+ *bpp = bp;
+ *entno = mid;
+ return 0;
+ }
+ } while (++mid < INT_GET(btp->count, ARCH_CONVERT) && INT_GET(blp[mid].hashval, ARCH_CONVERT) == hash);
+ /*
+ * No match, release the buffer and return ENOENT.
+ */
+ ASSERT(args->oknoent);
+ xfs_da_brelse(tp, bp);
+ return XFS_ERROR(ENOENT);
+}
+
+/*
+ * Remove an entry from a block format directory.
+ * If that makes the block small enough to fit in shortform, transform it.
+ */
+int /* error */
+xfs_dir2_block_removename(
+ xfs_da_args_t *args) /* directory operation args */
+{
+ xfs_dir2_block_t *block; /* block structure */
+ xfs_dir2_leaf_entry_t *blp; /* block leaf pointer */
+ xfs_dabuf_t *bp; /* block buffer */
+ xfs_dir2_block_tail_t *btp; /* block tail */
+ xfs_dir2_data_entry_t *dep; /* block data entry */
+ xfs_inode_t *dp; /* incore inode */
+ int ent; /* block leaf entry index */
+ int error; /* error return value */
+ xfs_mount_t *mp; /* filesystem mount point */
+ int needlog; /* need to log block header */
+ int needscan; /* need to fixup bestfree */
+ xfs_dir2_sf_hdr_t sfh; /* shortform header */
+ int size; /* shortform size */
+ xfs_trans_t *tp; /* transaction pointer */
+
+ xfs_dir2_trace_args("block_removename", args);
+ /*
+ * Look up the entry in the block. Gets the buffer and entry index.
+ * It will always be there, the vnodeops level does a lookup first.
+ */
+ if ((error = xfs_dir2_block_lookup_int(args, &bp, &ent))) {
+ return error;
+ }
+ dp = args->dp;
+ tp = args->trans;
+ mp = dp->i_mount;
+ block = bp->data;
+ btp = XFS_DIR2_BLOCK_TAIL_P(mp, block);
+ blp = XFS_DIR2_BLOCK_LEAF_P_ARCH(btp, ARCH_CONVERT);
+ /*
+ * Point to the data entry using the leaf entry.
+ */
+ dep = (xfs_dir2_data_entry_t *)
+ ((char *)block + XFS_DIR2_DATAPTR_TO_OFF(mp, INT_GET(blp[ent].address, ARCH_CONVERT)));
+ /*
+ * Mark the data entry's space free.
+ */
+ needlog = needscan = 0;
+ xfs_dir2_data_make_free(tp, bp,
+ (xfs_dir2_data_aoff_t)((char *)dep - (char *)block),
+ XFS_DIR2_DATA_ENTSIZE(dep->namelen), &needlog, &needscan);
+ /*
+ * Fix up the block tail.
+ */
+ INT_MOD(btp->stale, ARCH_CONVERT, +1);
+ xfs_dir2_block_log_tail(tp, bp);
+ /*
+ * Remove the leaf entry by marking it stale.
+ */
+ INT_SET(blp[ent].address, ARCH_CONVERT, XFS_DIR2_NULL_DATAPTR);
+ xfs_dir2_block_log_leaf(tp, bp, ent, ent);
+ /*
+ * Fix up bestfree, log the header if necessary.
+ */
+ if (needscan)
+ xfs_dir2_data_freescan(mp, (xfs_dir2_data_t *)block, &needlog,
+ NULL);
+ if (needlog)
+ xfs_dir2_data_log_header(tp, bp);
+ xfs_dir2_data_check(dp, bp);
+ /*
+ * See if the size as a shortform is good enough.
+ */
+ if ((size = xfs_dir2_block_sfsize(dp, block, &sfh)) >
+ XFS_IFORK_DSIZE(dp)) {
+ xfs_da_buf_done(bp);
+ return 0;
+ }
+ /*
+ * If it works, do the conversion.
+ */
+ return xfs_dir2_block_to_sf(args, bp, size, &sfh);
+}
+
+/*
+ * Replace an entry in a V2 block directory.
+ * Change the inode number to the new value.
+ */
+int /* error */
+xfs_dir2_block_replace(
+ xfs_da_args_t *args) /* directory operation args */
+{
+ xfs_dir2_block_t *block; /* block structure */
+ xfs_dir2_leaf_entry_t *blp; /* block leaf entries */
+ xfs_dabuf_t *bp; /* block buffer */
+ xfs_dir2_block_tail_t *btp; /* block tail */
+ xfs_dir2_data_entry_t *dep; /* block data entry */
+ xfs_inode_t *dp; /* incore inode */
+ int ent; /* leaf entry index */
+ int error; /* error return value */
+ xfs_mount_t *mp; /* filesystem mount point */
+
+ xfs_dir2_trace_args("block_replace", args);
+ /*
+ * Lookup the entry in the directory. Get buffer and entry index.
+ * This will always succeed since the caller has already done a lookup.
+ */
+ if ((error = xfs_dir2_block_lookup_int(args, &bp, &ent))) {
+ return error;
+ }
+ dp = args->dp;
+ mp = dp->i_mount;
+ block = bp->data;
+ btp = XFS_DIR2_BLOCK_TAIL_P(mp, block);
+ blp = XFS_DIR2_BLOCK_LEAF_P_ARCH(btp, ARCH_CONVERT);
+ /*
+ * Point to the data entry we need to change.
+ */
+ dep = (xfs_dir2_data_entry_t *)
+ ((char *)block + XFS_DIR2_DATAPTR_TO_OFF(mp, INT_GET(blp[ent].address, ARCH_CONVERT)));
+ ASSERT(INT_GET(dep->inumber, ARCH_CONVERT) != args->inumber);
+ /*
+ * Change the inode number to the new value.
+ */
+ INT_SET(dep->inumber, ARCH_CONVERT, args->inumber);
+ xfs_dir2_data_log_entry(args->trans, bp, dep);
+ xfs_dir2_data_check(dp, bp);
+ xfs_da_buf_done(bp);
+ return 0;
+}
+
+/*
+ * Qsort comparison routine for the block leaf entries.
+ */
+static int /* sort order */
+xfs_dir2_block_sort(
+ const void *a, /* first leaf entry */
+ const void *b) /* second leaf entry */
+{
+ const xfs_dir2_leaf_entry_t *la; /* first leaf entry */
+ const xfs_dir2_leaf_entry_t *lb; /* second leaf entry */
+
+ la = a;
+ lb = b;
+ return INT_GET(la->hashval, ARCH_CONVERT) < INT_GET(lb->hashval, ARCH_CONVERT) ? -1 :
+ (INT_GET(la->hashval, ARCH_CONVERT) > INT_GET(lb->hashval, ARCH_CONVERT) ? 1 : 0);
+}
+
+/*
+ * Convert a V2 leaf directory to a V2 block directory if possible.
+ */
+int /* error */
+xfs_dir2_leaf_to_block(
+ xfs_da_args_t *args, /* operation arguments */
+ xfs_dabuf_t *lbp, /* leaf buffer */
+ xfs_dabuf_t *dbp) /* data buffer */
+{
+ xfs_dir2_data_off_t *bestsp; /* leaf bests table */
+ xfs_dir2_block_t *block; /* block structure */
+ xfs_dir2_block_tail_t *btp; /* block tail */
+ xfs_inode_t *dp; /* incore directory inode */
+ xfs_dir2_data_unused_t *dup; /* unused data entry */
+ int error; /* error return value */
+ int from; /* leaf from index */
+ xfs_dir2_leaf_t *leaf; /* leaf structure */
+ xfs_dir2_leaf_entry_t *lep; /* leaf entry */
+ xfs_dir2_leaf_tail_t *ltp; /* leaf tail structure */
+ xfs_mount_t *mp; /* file system mount point */
+ int needlog; /* need to log data header */
+ int needscan; /* need to scan for bestfree */
+ xfs_dir2_sf_hdr_t sfh; /* shortform header */
+ int size; /* bytes used */
+ xfs_dir2_data_off_t *tagp; /* end of entry (tag) */
+ int to; /* block/leaf to index */
+ xfs_trans_t *tp; /* transaction pointer */
+
+ xfs_dir2_trace_args_bb("leaf_to_block", args, lbp, dbp);
+ dp = args->dp;
+ tp = args->trans;
+ mp = dp->i_mount;
+ leaf = lbp->data;
+ ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) == XFS_DIR2_LEAF1_MAGIC);
+ ltp = XFS_DIR2_LEAF_TAIL_P(mp, leaf);
+ /*
+ * If there are data blocks other than the first one, take this
+ * opportunity to remove trailing empty data blocks that may have
+ * been left behind during no-space-reservation operations.
+ * These will show up in the leaf bests table.
+ */
+ while (dp->i_d.di_size > mp->m_dirblksize) {
+ bestsp = XFS_DIR2_LEAF_BESTS_P_ARCH(ltp, ARCH_CONVERT);
+ if (INT_GET(bestsp[INT_GET(ltp->bestcount, ARCH_CONVERT) - 1], ARCH_CONVERT) ==
+ mp->m_dirblksize - (uint)sizeof(block->hdr)) {
+ if ((error =
+ xfs_dir2_leaf_trim_data(args, lbp,
+ (xfs_dir2_db_t)(INT_GET(ltp->bestcount, ARCH_CONVERT) - 1))))
+ goto out;
+ } else {
+ error = 0;
+ goto out;
+ }
+ }
+ /*
+ * Read the data block if we don't already have it, give up if it fails.
+ */
+ if (dbp == NULL &&
+ (error = xfs_da_read_buf(tp, dp, mp->m_dirdatablk, -1, &dbp,
+ XFS_DATA_FORK))) {
+ goto out;
+ }
+ block = dbp->data;
+ ASSERT(INT_GET(block->hdr.magic, ARCH_CONVERT) == XFS_DIR2_DATA_MAGIC);
+ /*
+ * Size of the "leaf" area in the block.
+ */
+ size = (uint)sizeof(block->tail) +
+ (uint)sizeof(*lep) * (INT_GET(leaf->hdr.count, ARCH_CONVERT) - INT_GET(leaf->hdr.stale, ARCH_CONVERT));
+ /*
+ * Look at the last data entry.
+ */
+ tagp = (xfs_dir2_data_off_t *)((char *)block + mp->m_dirblksize) - 1;
+ dup = (xfs_dir2_data_unused_t *)((char *)block + INT_GET(*tagp, ARCH_CONVERT));
+ /*
+ * If it's not free or is too short we can't do it.
+ */
+ if (INT_GET(dup->freetag, ARCH_CONVERT) != XFS_DIR2_DATA_FREE_TAG || INT_GET(dup->length, ARCH_CONVERT) < size) {
+ error = 0;
+ goto out;
+ }
+ /*
+ * Start converting it to block form.
+ */
+ INT_SET(block->hdr.magic, ARCH_CONVERT, XFS_DIR2_BLOCK_MAGIC);
+ needlog = 1;
+ needscan = 0;
+ /*
+ * Use up the space at the end of the block (blp/btp).
+ */
+ xfs_dir2_data_use_free(tp, dbp, dup, mp->m_dirblksize - size, size,
+ &needlog, &needscan);
+ /*
+ * Initialize the block tail.
+ */
+ btp = XFS_DIR2_BLOCK_TAIL_P(mp, block);
+ INT_SET(btp->count, ARCH_CONVERT, INT_GET(leaf->hdr.count, ARCH_CONVERT) - INT_GET(leaf->hdr.stale, ARCH_CONVERT));
+ INT_ZERO(btp->stale, ARCH_CONVERT);
+ xfs_dir2_block_log_tail(tp, dbp);
+ /*
+ * Initialize the block leaf area. We compact out stale entries.
+ */
+ lep = XFS_DIR2_BLOCK_LEAF_P_ARCH(btp, ARCH_CONVERT);
+ for (from = to = 0; from < INT_GET(leaf->hdr.count, ARCH_CONVERT); from++) {
+ if (INT_GET(leaf->ents[from].address, ARCH_CONVERT) == XFS_DIR2_NULL_DATAPTR)
+ continue;
+ lep[to++] = leaf->ents[from];
+ }
+ ASSERT(to == INT_GET(btp->count, ARCH_CONVERT));
+ xfs_dir2_block_log_leaf(tp, dbp, 0, INT_GET(btp->count, ARCH_CONVERT) - 1);
+ /*
+ * Scan the bestfree if we need it and log the data block header.
+ */
+ if (needscan)
+ xfs_dir2_data_freescan(mp, (xfs_dir2_data_t *)block, &needlog,
+ NULL);
+ if (needlog)
+ xfs_dir2_data_log_header(tp, dbp);
+ /*
+ * Pitch the old leaf block.
+ */
+ error = xfs_da_shrink_inode(args, mp->m_dirleafblk, lbp);
+ lbp = NULL;
+ if (error) {
+ goto out;
+ }
+ /*
+ * Now see if the resulting block can be shrunken to shortform.
+ */
+ if ((size = xfs_dir2_block_sfsize(dp, block, &sfh)) >
+ XFS_IFORK_DSIZE(dp)) {
+ error = 0;
+ goto out;
+ }
+ return xfs_dir2_block_to_sf(args, dbp, size, &sfh);
+out:
+ if (lbp)
+ xfs_da_buf_done(lbp);
+ if (dbp)
+ xfs_da_buf_done(dbp);
+ return error;
+}
+
+/*
+ * Convert the shortform directory to block form.
+ */
+int /* error */
+xfs_dir2_sf_to_block(
+ xfs_da_args_t *args) /* operation arguments */
+{
+ xfs_dir2_db_t blkno; /* dir-relative block # (0) */
+ xfs_dir2_block_t *block; /* block structure */
+ xfs_dir2_leaf_entry_t *blp; /* block leaf entries */
+ xfs_dabuf_t *bp; /* block buffer */
+ xfs_dir2_block_tail_t *btp; /* block tail pointer */
+ char *buf; /* sf buffer */
+ int buf_len;
+ xfs_dir2_data_entry_t *dep; /* data entry pointer */
+ xfs_inode_t *dp; /* incore directory inode */
+ int dummy; /* trash */
+ xfs_dir2_data_unused_t *dup; /* unused entry pointer */
+ int endoffset; /* end of data objects */
+ int error; /* error return value */
+ int i; /* index */
+ xfs_mount_t *mp; /* filesystem mount point */
+ int needlog; /* need to log block header */
+ int needscan; /* need to scan block freespc */
+ int newoffset; /* offset from current entry */
+ int offset; /* target block offset */
+ xfs_dir2_sf_entry_t *sfep; /* sf entry pointer */
+ xfs_dir2_sf_t *sfp; /* shortform structure */
+ xfs_dir2_data_off_t *tagp; /* end of data entry */
+ xfs_trans_t *tp; /* transaction pointer */
+
+ xfs_dir2_trace_args("sf_to_block", args);
+ dp = args->dp;
+ tp = args->trans;
+ mp = dp->i_mount;
+ ASSERT(dp->i_df.if_flags & XFS_IFINLINE);
+ /*
+ * Bomb out if the shortform directory is way too short.
+ */
+ if (dp->i_d.di_size < offsetof(xfs_dir2_sf_hdr_t, parent)) {
+ ASSERT(XFS_FORCED_SHUTDOWN(mp));
+ return XFS_ERROR(EIO);
+ }
+ ASSERT(dp->i_df.if_bytes == dp->i_d.di_size);
+ ASSERT(dp->i_df.if_u1.if_data != NULL);
+ sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data;
+ ASSERT(dp->i_d.di_size >= XFS_DIR2_SF_HDR_SIZE(sfp->hdr.i8count));
+ /*
+ * Copy the directory into the stack buffer.
+ * Then pitch the incore inode data so we can make extents.
+ */
+
+ buf_len = dp->i_df.if_bytes;
+ buf = kmem_alloc(dp->i_df.if_bytes, KM_SLEEP);
+
+ memcpy(buf, sfp, dp->i_df.if_bytes);
+ xfs_idata_realloc(dp, -dp->i_df.if_bytes, XFS_DATA_FORK);
+ dp->i_d.di_size = 0;
+ xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE);
+ /*
+ * Reset pointer - old sfp is gone.
+ */
+ sfp = (xfs_dir2_sf_t *)buf;
+ /*
+ * Add block 0 to the inode.
+ */
+ error = xfs_dir2_grow_inode(args, XFS_DIR2_DATA_SPACE, &blkno);
+ if (error) {
+ kmem_free(buf, buf_len);
+ return error;
+ }
+ /*
+ * Initialize the data block.
+ */
+ error = xfs_dir2_data_init(args, blkno, &bp);
+ if (error) {
+ kmem_free(buf, buf_len);
+ return error;
+ }
+ block = bp->data;
+ INT_SET(block->hdr.magic, ARCH_CONVERT, XFS_DIR2_BLOCK_MAGIC);
+ /*
+ * Compute size of block "tail" area.
+ */
+ i = (uint)sizeof(*btp) +
+ (INT_GET(sfp->hdr.count, ARCH_CONVERT) + 2) * (uint)sizeof(xfs_dir2_leaf_entry_t);
+ /*
+ * The whole thing is initialized to free by the init routine.
+ * Say we're using the leaf and tail area.
+ */
+ dup = (xfs_dir2_data_unused_t *)block->u;
+ needlog = needscan = 0;
+ xfs_dir2_data_use_free(tp, bp, dup, mp->m_dirblksize - i, i, &needlog,
+ &needscan);
+ ASSERT(needscan == 0);
+ /*
+ * Fill in the tail.
+ */
+ btp = XFS_DIR2_BLOCK_TAIL_P(mp, block);
+ INT_SET(btp->count, ARCH_CONVERT, INT_GET(sfp->hdr.count, ARCH_CONVERT) + 2); /* ., .. */
+ INT_ZERO(btp->stale, ARCH_CONVERT);
+ blp = XFS_DIR2_BLOCK_LEAF_P_ARCH(btp, ARCH_CONVERT);
+ endoffset = (uint)((char *)blp - (char *)block);
+ /*
+ * Remove the freespace, we'll manage it.
+ */
+ xfs_dir2_data_use_free(tp, bp, dup,
+ (xfs_dir2_data_aoff_t)((char *)dup - (char *)block),
+ INT_GET(dup->length, ARCH_CONVERT), &needlog, &needscan);
+ /*
+ * Create entry for .
+ */
+ dep = (xfs_dir2_data_entry_t *)
+ ((char *)block + XFS_DIR2_DATA_DOT_OFFSET);
+ INT_SET(dep->inumber, ARCH_CONVERT, dp->i_ino);
+ dep->namelen = 1;
+ dep->name[0] = '.';
+ tagp = XFS_DIR2_DATA_ENTRY_TAG_P(dep);
+ INT_SET(*tagp, ARCH_CONVERT, (xfs_dir2_data_off_t)((char *)dep - (char *)block));
+ xfs_dir2_data_log_entry(tp, bp, dep);
+ INT_SET(blp[0].hashval, ARCH_CONVERT, xfs_dir_hash_dot);
+ INT_SET(blp[0].address, ARCH_CONVERT, XFS_DIR2_BYTE_TO_DATAPTR(mp, (char *)dep - (char *)block));
+ /*
+ * Create entry for ..
+ */
+ dep = (xfs_dir2_data_entry_t *)
+ ((char *)block + XFS_DIR2_DATA_DOTDOT_OFFSET);
+ INT_SET(dep->inumber, ARCH_CONVERT, XFS_DIR2_SF_GET_INUMBER_ARCH(sfp, &sfp->hdr.parent, ARCH_CONVERT));
+ dep->namelen = 2;
+ dep->name[0] = dep->name[1] = '.';
+ tagp = XFS_DIR2_DATA_ENTRY_TAG_P(dep);
+ INT_SET(*tagp, ARCH_CONVERT, (xfs_dir2_data_off_t)((char *)dep - (char *)block));
+ xfs_dir2_data_log_entry(tp, bp, dep);
+ INT_SET(blp[1].hashval, ARCH_CONVERT, xfs_dir_hash_dotdot);
+ INT_SET(blp[1].address, ARCH_CONVERT, XFS_DIR2_BYTE_TO_DATAPTR(mp, (char *)dep - (char *)block));
+ offset = XFS_DIR2_DATA_FIRST_OFFSET;
+ /*
+ * Loop over existing entries, stuff them in.
+ */
+ if ((i = 0) == INT_GET(sfp->hdr.count, ARCH_CONVERT))
+ sfep = NULL;
+ else
+ sfep = XFS_DIR2_SF_FIRSTENTRY(sfp);
+ /*
+ * Need to preserve the existing offset values in the sf directory.
+ * Insert holes (unused entries) where necessary.
+ */
+ while (offset < endoffset) {
+ /*
+ * sfep is null when we reach the end of the list.
+ */
+ if (sfep == NULL)
+ newoffset = endoffset;
+ else
+ newoffset = XFS_DIR2_SF_GET_OFFSET_ARCH(sfep, ARCH_CONVERT);
+ /*
+ * There should be a hole here, make one.
+ */
+ if (offset < newoffset) {
+ dup = (xfs_dir2_data_unused_t *)
+ ((char *)block + offset);
+ INT_SET(dup->freetag, ARCH_CONVERT, XFS_DIR2_DATA_FREE_TAG);
+ INT_SET(dup->length, ARCH_CONVERT, newoffset - offset);
+ INT_SET(*XFS_DIR2_DATA_UNUSED_TAG_P_ARCH(dup, ARCH_CONVERT), ARCH_CONVERT,
+ (xfs_dir2_data_off_t)
+ ((char *)dup - (char *)block));
+ xfs_dir2_data_log_unused(tp, bp, dup);
+ (void)xfs_dir2_data_freeinsert((xfs_dir2_data_t *)block,
+ dup, &dummy);
+ offset += INT_GET(dup->length, ARCH_CONVERT);
+ continue;
+ }
+ /*
+ * Copy a real entry.
+ */
+ dep = (xfs_dir2_data_entry_t *)((char *)block + newoffset);
+ INT_SET(dep->inumber, ARCH_CONVERT, XFS_DIR2_SF_GET_INUMBER_ARCH(sfp,
+ XFS_DIR2_SF_INUMBERP(sfep), ARCH_CONVERT));
+ dep->namelen = sfep->namelen;
+ memcpy(dep->name, sfep->name, dep->namelen);
+ tagp = XFS_DIR2_DATA_ENTRY_TAG_P(dep);
+ INT_SET(*tagp, ARCH_CONVERT, (xfs_dir2_data_off_t)((char *)dep - (char *)block));
+ xfs_dir2_data_log_entry(tp, bp, dep);
+ INT_SET(blp[2 + i].hashval, ARCH_CONVERT, xfs_da_hashname((char *)sfep->name, sfep->namelen));
+ INT_SET(blp[2 + i].address, ARCH_CONVERT, XFS_DIR2_BYTE_TO_DATAPTR(mp,
+ (char *)dep - (char *)block));
+ offset = (int)((char *)(tagp + 1) - (char *)block);
+ if (++i == INT_GET(sfp->hdr.count, ARCH_CONVERT))
+ sfep = NULL;
+ else
+ sfep = XFS_DIR2_SF_NEXTENTRY(sfp, sfep);
+ }
+ /* Done with the temporary buffer */
+ kmem_free(buf, buf_len);
+ /*
+ * Sort the leaf entries by hash value.
+ */
+ qsort(blp, INT_GET(btp->count, ARCH_CONVERT), sizeof(*blp), xfs_dir2_block_sort);
+ /*
+ * Log the leaf entry area and tail.
+ * Already logged the header in data_init, ignore needlog.
+ */
+ ASSERT(needscan == 0);
+ xfs_dir2_block_log_leaf(tp, bp, 0, INT_GET(btp->count, ARCH_CONVERT) - 1);
+ xfs_dir2_block_log_tail(tp, bp);
+ xfs_dir2_data_check(dp, bp);
+ xfs_da_buf_done(bp);
+ return 0;
+}
diff --git a/fs/xfs/xfs_dir2_block.h b/fs/xfs/xfs_dir2_block.h
new file mode 100644
index 00000000000000..3c4f70c915eff0
--- /dev/null
+++ b/fs/xfs/xfs_dir2_block.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2000-2001 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_DIR2_BLOCK_H__
+#define __XFS_DIR2_BLOCK_H__
+
+/*
+ * xfs_dir2_block.h
+ * Directory version 2, single block format structures
+ */
+
+struct uio;
+struct xfs_dabuf;
+struct xfs_da_args;
+struct xfs_dir2_data_hdr;
+struct xfs_dir2_leaf_entry;
+struct xfs_inode;
+struct xfs_mount;
+struct xfs_trans;
+
+/*
+ * The single block format is as follows:
+ * xfs_dir2_data_hdr_t structure
+ * xfs_dir2_data_entry_t and xfs_dir2_data_unused_t structures
+ * xfs_dir2_leaf_entry_t structures
+ * xfs_dir2_block_tail_t structure
+ */
+
+#define XFS_DIR2_BLOCK_MAGIC 0x58443242 /* XD2B: for one block dirs */
+
+typedef struct xfs_dir2_block_tail {
+ __uint32_t count; /* count of leaf entries */
+ __uint32_t stale; /* count of stale lf entries */
+} xfs_dir2_block_tail_t;
+
+/*
+ * Generic single-block structure, for xfs_db.
+ */
+typedef struct xfs_dir2_block {
+ xfs_dir2_data_hdr_t hdr; /* magic XFS_DIR2_BLOCK_MAGIC */
+ xfs_dir2_data_union_t u[1];
+ xfs_dir2_leaf_entry_t leaf[1];
+ xfs_dir2_block_tail_t tail;
+} xfs_dir2_block_t;
+
+/*
+ * Pointer to the leaf header embedded in a data block (1-block format)
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR2_BLOCK_TAIL_P)
+xfs_dir2_block_tail_t *
+xfs_dir2_block_tail_p(struct xfs_mount *mp, xfs_dir2_block_t *block);
+#define XFS_DIR2_BLOCK_TAIL_P(mp,block) xfs_dir2_block_tail_p(mp,block)
+#else
+#define XFS_DIR2_BLOCK_TAIL_P(mp,block) \
+ (((xfs_dir2_block_tail_t *)((char *)(block) + (mp)->m_dirblksize)) - 1)
+#endif
+
+/*
+ * Pointer to the leaf entries embedded in a data block (1-block format)
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR2_BLOCK_LEAF_P)
+struct xfs_dir2_leaf_entry *xfs_dir2_block_leaf_p_arch(
+ xfs_dir2_block_tail_t *btp, xfs_arch_t arch);
+#define XFS_DIR2_BLOCK_LEAF_P_ARCH(btp,arch) \
+ xfs_dir2_block_leaf_p_arch(btp,arch)
+#else
+#define XFS_DIR2_BLOCK_LEAF_P_ARCH(btp,arch) \
+ (((struct xfs_dir2_leaf_entry *)(btp)) - INT_GET((btp)->count, arch))
+#endif
+
+/*
+ * Function declarations.
+ */
+
+extern int
+ xfs_dir2_block_addname(struct xfs_da_args *args);
+
+extern int
+ xfs_dir2_block_getdents(struct xfs_trans *tp, struct xfs_inode *dp,
+ struct uio *uio, int *eofp, struct xfs_dirent *dbp,
+ xfs_dir2_put_t put);
+
+extern int
+ xfs_dir2_block_lookup(struct xfs_da_args *args);
+
+extern int
+ xfs_dir2_block_removename(struct xfs_da_args *args);
+
+extern int
+ xfs_dir2_block_replace(struct xfs_da_args *args);
+
+extern int
+ xfs_dir2_leaf_to_block(struct xfs_da_args *args, struct xfs_dabuf *lbp,
+ struct xfs_dabuf *dbp);
+
+extern int
+ xfs_dir2_sf_to_block(struct xfs_da_args *args);
+
+#endif /* __XFS_DIR2_BLOCK_H__ */
diff --git a/fs/xfs/xfs_dir2_data.c b/fs/xfs/xfs_dir2_data.c
new file mode 100644
index 00000000000000..c1b8ea010a91d4
--- /dev/null
+++ b/fs/xfs/xfs_dir2_data.c
@@ -0,0 +1,855 @@
+/*
+ * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+/*
+ * xfs_dir2_data.c
+ * Core data block handling routines for XFS V2 directories.
+ * See xfs_dir2_data.h for data structures.
+ */
+
+#include "xfs.h"
+
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode.h"
+#include "xfs_da_btree.h"
+#include "xfs_dir_leaf.h"
+#include "xfs_dir2_data.h"
+#include "xfs_dir2_leaf.h"
+#include "xfs_dir2_block.h"
+#include "xfs_error.h"
+
+#ifdef DEBUG
+/*
+ * Check the consistency of the data block.
+ * The input can also be a block-format directory.
+ * Pop an assert if we find anything bad.
+ */
+void
+xfs_dir2_data_check(
+ xfs_inode_t *dp, /* incore inode pointer */
+ xfs_dabuf_t *bp) /* data block's buffer */
+{
+ xfs_dir2_dataptr_t addr; /* addr for leaf lookup */
+ xfs_dir2_data_free_t *bf; /* bestfree table */
+ xfs_dir2_block_tail_t *btp=NULL; /* block tail */
+ int count; /* count of entries found */
+ xfs_dir2_data_t *d; /* data block pointer */
+ xfs_dir2_data_entry_t *dep; /* data entry */
+ xfs_dir2_data_free_t *dfp; /* bestfree entry */
+ xfs_dir2_data_unused_t *dup; /* unused entry */
+ char *endp; /* end of useful data */
+ int freeseen; /* mask of bestfrees seen */
+ xfs_dahash_t hash; /* hash of current name */
+ int i; /* leaf index */
+ int lastfree; /* last entry was unused */
+ xfs_dir2_leaf_entry_t *lep=NULL; /* block leaf entries */
+ xfs_mount_t *mp; /* filesystem mount point */
+ char *p; /* current data position */
+ int stale; /* count of stale leaves */
+
+ mp = dp->i_mount;
+ d = bp->data;
+ ASSERT(INT_GET(d->hdr.magic, ARCH_CONVERT) == XFS_DIR2_DATA_MAGIC ||
+ INT_GET(d->hdr.magic, ARCH_CONVERT) == XFS_DIR2_BLOCK_MAGIC);
+ bf = d->hdr.bestfree;
+ p = (char *)d->u;
+ if (INT_GET(d->hdr.magic, ARCH_CONVERT) == XFS_DIR2_BLOCK_MAGIC) {
+ btp = XFS_DIR2_BLOCK_TAIL_P(mp, (xfs_dir2_block_t *)d);
+ lep = XFS_DIR2_BLOCK_LEAF_P_ARCH(btp, ARCH_CONVERT);
+ endp = (char *)lep;
+ } else
+ endp = (char *)d + mp->m_dirblksize;
+ count = lastfree = freeseen = 0;
+ /*
+ * Account for zero bestfree entries.
+ */
+ if (INT_ISZERO(bf[0].length, ARCH_CONVERT)) {
+ ASSERT(INT_ISZERO(bf[0].offset, ARCH_CONVERT));
+ freeseen |= 1 << 0;
+ }
+ if (INT_ISZERO(bf[1].length, ARCH_CONVERT)) {
+ ASSERT(INT_ISZERO(bf[1].offset, ARCH_CONVERT));
+ freeseen |= 1 << 1;
+ }
+ if (INT_ISZERO(bf[2].length, ARCH_CONVERT)) {
+ ASSERT(INT_ISZERO(bf[2].offset, ARCH_CONVERT));
+ freeseen |= 1 << 2;
+ }
+ ASSERT(INT_GET(bf[0].length, ARCH_CONVERT) >= INT_GET(bf[1].length, ARCH_CONVERT));
+ ASSERT(INT_GET(bf[1].length, ARCH_CONVERT) >= INT_GET(bf[2].length, ARCH_CONVERT));
+ /*
+ * Loop over the data/unused entries.
+ */
+ while (p < endp) {
+ dup = (xfs_dir2_data_unused_t *)p;
+ /*
+ * If it's unused, look for the space in the bestfree table.
+ * If we find it, account for that, else make sure it
+ * doesn't need to be there.
+ */
+ if (INT_GET(dup->freetag, ARCH_CONVERT) == XFS_DIR2_DATA_FREE_TAG) {
+ ASSERT(lastfree == 0);
+ ASSERT(INT_GET(*XFS_DIR2_DATA_UNUSED_TAG_P_ARCH(dup, ARCH_CONVERT), ARCH_CONVERT) ==
+ (char *)dup - (char *)d);
+ dfp = xfs_dir2_data_freefind(d, dup);
+ if (dfp) {
+ i = (int)(dfp - bf);
+ ASSERT((freeseen & (1 << i)) == 0);
+ freeseen |= 1 << i;
+ } else
+ ASSERT(INT_GET(dup->length, ARCH_CONVERT) <= INT_GET(bf[2].length, ARCH_CONVERT));
+ p += INT_GET(dup->length, ARCH_CONVERT);
+ lastfree = 1;
+ continue;
+ }
+ /*
+ * It's a real entry. Validate the fields.
+ * If this is a block directory then make sure it's
+ * in the leaf section of the block.
+ * The linear search is crude but this is DEBUG code.
+ */
+ dep = (xfs_dir2_data_entry_t *)p;
+ ASSERT(dep->namelen != 0);
+ ASSERT(xfs_dir_ino_validate(mp, INT_GET(dep->inumber, ARCH_CONVERT)) == 0);
+ ASSERT(INT_GET(*XFS_DIR2_DATA_ENTRY_TAG_P(dep), ARCH_CONVERT) ==
+ (char *)dep - (char *)d);
+ count++;
+ lastfree = 0;
+ if (INT_GET(d->hdr.magic, ARCH_CONVERT) == XFS_DIR2_BLOCK_MAGIC) {
+ addr = XFS_DIR2_DB_OFF_TO_DATAPTR(mp, mp->m_dirdatablk,
+ (xfs_dir2_data_aoff_t)
+ ((char *)dep - (char *)d));
+ hash = xfs_da_hashname((char *)dep->name, dep->namelen);
+ for (i = 0; i < INT_GET(btp->count, ARCH_CONVERT); i++) {
+ if (INT_GET(lep[i].address, ARCH_CONVERT) == addr &&
+ INT_GET(lep[i].hashval, ARCH_CONVERT) == hash)
+ break;
+ }
+ ASSERT(i < INT_GET(btp->count, ARCH_CONVERT));
+ }
+ p += XFS_DIR2_DATA_ENTSIZE(dep->namelen);
+ }
+ /*
+ * Need to have seen all the entries and all the bestfree slots.
+ */
+ ASSERT(freeseen == 7);
+ if (INT_GET(d->hdr.magic, ARCH_CONVERT) == XFS_DIR2_BLOCK_MAGIC) {
+ for (i = stale = 0; i < INT_GET(btp->count, ARCH_CONVERT); i++) {
+ if (INT_GET(lep[i].address, ARCH_CONVERT) == XFS_DIR2_NULL_DATAPTR)
+ stale++;
+ if (i > 0)
+ ASSERT(INT_GET(lep[i].hashval, ARCH_CONVERT) >= INT_GET(lep[i - 1].hashval, ARCH_CONVERT));
+ }
+ ASSERT(count == INT_GET(btp->count, ARCH_CONVERT) - INT_GET(btp->stale, ARCH_CONVERT));
+ ASSERT(stale == INT_GET(btp->stale, ARCH_CONVERT));
+ }
+}
+#endif
+
+/*
+ * Given a data block and an unused entry from that block,
+ * return the bestfree entry if any that corresponds to it.
+ */
+xfs_dir2_data_free_t *
+xfs_dir2_data_freefind(
+ xfs_dir2_data_t *d, /* data block */
+ xfs_dir2_data_unused_t *dup) /* data unused entry */
+{
+ xfs_dir2_data_free_t *dfp; /* bestfree entry */
+ xfs_dir2_data_aoff_t off; /* offset value needed */
+#if defined(DEBUG) && defined(__KERNEL__)
+ int matched; /* matched the value */
+ int seenzero; /* saw a 0 bestfree entry */
+#endif
+
+ off = (xfs_dir2_data_aoff_t)((char *)dup - (char *)d);
+#if defined(DEBUG) && defined(__KERNEL__)
+ /*
+ * Validate some consistency in the bestfree table.
+ * Check order, non-overlapping entries, and if we find the
+ * one we're looking for it has to be exact.
+ */
+ ASSERT(INT_GET(d->hdr.magic, ARCH_CONVERT) == XFS_DIR2_DATA_MAGIC ||
+ INT_GET(d->hdr.magic, ARCH_CONVERT) == XFS_DIR2_BLOCK_MAGIC);
+ for (dfp = &d->hdr.bestfree[0], seenzero = matched = 0;
+ dfp < &d->hdr.bestfree[XFS_DIR2_DATA_FD_COUNT];
+ dfp++) {
+ if (INT_ISZERO(dfp->offset, ARCH_CONVERT)) {
+ ASSERT(INT_ISZERO(dfp->length, ARCH_CONVERT));
+ seenzero = 1;
+ continue;
+ }
+ ASSERT(seenzero == 0);
+ if (INT_GET(dfp->offset, ARCH_CONVERT) == off) {
+ matched = 1;
+ ASSERT(INT_GET(dfp->length, ARCH_CONVERT) == INT_GET(dup->length, ARCH_CONVERT));
+ } else if (off < INT_GET(dfp->offset, ARCH_CONVERT))
+ ASSERT(off + INT_GET(dup->length, ARCH_CONVERT) <= INT_GET(dfp->offset, ARCH_CONVERT));
+ else
+ ASSERT(INT_GET(dfp->offset, ARCH_CONVERT) + INT_GET(dfp->length, ARCH_CONVERT) <= off);
+ ASSERT(matched || INT_GET(dfp->length, ARCH_CONVERT) >= INT_GET(dup->length, ARCH_CONVERT));
+ if (dfp > &d->hdr.bestfree[0])
+ ASSERT(INT_GET(dfp[-1].length, ARCH_CONVERT) >= INT_GET(dfp[0].length, ARCH_CONVERT));
+ }
+#endif
+ /*
+ * If this is smaller than the smallest bestfree entry,
+ * it can't be there since they're sorted.
+ */
+ if (INT_GET(dup->length, ARCH_CONVERT) < INT_GET(d->hdr.bestfree[XFS_DIR2_DATA_FD_COUNT - 1].length, ARCH_CONVERT))
+ return NULL;
+ /*
+ * Look at the three bestfree entries for our guy.
+ */
+ for (dfp = &d->hdr.bestfree[0];
+ dfp < &d->hdr.bestfree[XFS_DIR2_DATA_FD_COUNT];
+ dfp++) {
+ if (INT_ISZERO(dfp->offset, ARCH_CONVERT))
+ return NULL;
+ if (INT_GET(dfp->offset, ARCH_CONVERT) == off)
+ return dfp;
+ }
+ /*
+ * Didn't find it. This only happens if there are duplicate lengths.
+ */
+ return NULL;
+}
+
+/*
+ * Insert an unused-space entry into the bestfree table.
+ */
+xfs_dir2_data_free_t * /* entry inserted */
+xfs_dir2_data_freeinsert(
+ xfs_dir2_data_t *d, /* data block pointer */
+ xfs_dir2_data_unused_t *dup, /* unused space */
+ int *loghead) /* log the data header (out) */
+{
+ xfs_dir2_data_free_t *dfp; /* bestfree table pointer */
+ xfs_dir2_data_free_t new; /* new bestfree entry */
+
+#ifdef __KERNEL__
+ ASSERT(INT_GET(d->hdr.magic, ARCH_CONVERT) == XFS_DIR2_DATA_MAGIC ||
+ INT_GET(d->hdr.magic, ARCH_CONVERT) == XFS_DIR2_BLOCK_MAGIC);
+#endif
+ dfp = d->hdr.bestfree;
+ INT_COPY(new.length, dup->length, ARCH_CONVERT);
+ INT_SET(new.offset, ARCH_CONVERT, (xfs_dir2_data_off_t)((char *)dup - (char *)d));
+ /*
+ * Insert at position 0, 1, or 2; or not at all.
+ */
+ if (INT_GET(new.length, ARCH_CONVERT) > INT_GET(dfp[0].length, ARCH_CONVERT)) {
+ dfp[2] = dfp[1];
+ dfp[1] = dfp[0];
+ dfp[0] = new;
+ *loghead = 1;
+ return &dfp[0];
+ }
+ if (INT_GET(new.length, ARCH_CONVERT) > INT_GET(dfp[1].length, ARCH_CONVERT)) {
+ dfp[2] = dfp[1];
+ dfp[1] = new;
+ *loghead = 1;
+ return &dfp[1];
+ }
+ if (INT_GET(new.length, ARCH_CONVERT) > INT_GET(dfp[2].length, ARCH_CONVERT)) {
+ dfp[2] = new;
+ *loghead = 1;
+ return &dfp[2];
+ }
+ return NULL;
+}
+
+/*
+ * Remove a bestfree entry from the table.
+ */
+void
+xfs_dir2_data_freeremove(
+ xfs_dir2_data_t *d, /* data block pointer */
+ xfs_dir2_data_free_t *dfp, /* bestfree entry pointer */
+ int *loghead) /* out: log data header */
+{
+#ifdef __KERNEL__
+ ASSERT(INT_GET(d->hdr.magic, ARCH_CONVERT) == XFS_DIR2_DATA_MAGIC ||
+ INT_GET(d->hdr.magic, ARCH_CONVERT) == XFS_DIR2_BLOCK_MAGIC);
+#endif
+ /*
+ * It's the first entry, slide the next 2 up.
+ */
+ if (dfp == &d->hdr.bestfree[0]) {
+ d->hdr.bestfree[0] = d->hdr.bestfree[1];
+ d->hdr.bestfree[1] = d->hdr.bestfree[2];
+ }
+ /*
+ * It's the second entry, slide the 3rd entry up.
+ */
+ else if (dfp == &d->hdr.bestfree[1])
+ d->hdr.bestfree[1] = d->hdr.bestfree[2];
+ /*
+ * Must be the last entry.
+ */
+ else
+ ASSERT(dfp == &d->hdr.bestfree[2]);
+ /*
+ * Clear the 3rd entry, must be zero now.
+ */
+ INT_ZERO(d->hdr.bestfree[2].length, ARCH_CONVERT);
+ INT_ZERO(d->hdr.bestfree[2].offset, ARCH_CONVERT);
+ *loghead = 1;
+}
+
+/*
+ * Given a data block, reconstruct its bestfree map.
+ */
+void
+xfs_dir2_data_freescan(
+ xfs_mount_t *mp, /* filesystem mount point */
+ xfs_dir2_data_t *d, /* data block pointer */
+ int *loghead, /* out: log data header */
+ char *aendp) /* in: caller's endp */
+{
+ xfs_dir2_block_tail_t *btp; /* block tail */
+ xfs_dir2_data_entry_t *dep; /* active data entry */
+ xfs_dir2_data_unused_t *dup; /* unused data entry */
+ char *endp; /* end of block's data */
+ char *p; /* current entry pointer */
+
+#ifdef __KERNEL__
+ ASSERT(INT_GET(d->hdr.magic, ARCH_CONVERT) == XFS_DIR2_DATA_MAGIC ||
+ INT_GET(d->hdr.magic, ARCH_CONVERT) == XFS_DIR2_BLOCK_MAGIC);
+#endif
+ /*
+ * Start by clearing the table.
+ */
+ memset(d->hdr.bestfree, 0, sizeof(d->hdr.bestfree));
+ *loghead = 1;
+ /*
+ * Set up pointers.
+ */
+ p = (char *)d->u;
+ if (aendp)
+ endp = aendp;
+ else if (INT_GET(d->hdr.magic, ARCH_CONVERT) == XFS_DIR2_BLOCK_MAGIC) {
+ btp = XFS_DIR2_BLOCK_TAIL_P(mp, (xfs_dir2_block_t *)d);
+ endp = (char *)XFS_DIR2_BLOCK_LEAF_P_ARCH(btp, ARCH_CONVERT);
+ } else
+ endp = (char *)d + mp->m_dirblksize;
+ /*
+ * Loop over the block's entries.
+ */
+ while (p < endp) {
+ dup = (xfs_dir2_data_unused_t *)p;
+ /*
+ * If it's a free entry, insert it.
+ */
+ if (INT_GET(dup->freetag, ARCH_CONVERT) == XFS_DIR2_DATA_FREE_TAG) {
+ ASSERT((char *)dup - (char *)d ==
+ INT_GET(*XFS_DIR2_DATA_UNUSED_TAG_P_ARCH(dup, ARCH_CONVERT), ARCH_CONVERT));
+ xfs_dir2_data_freeinsert(d, dup, loghead);
+ p += INT_GET(dup->length, ARCH_CONVERT);
+ }
+ /*
+ * For active entries, check their tags and skip them.
+ */
+ else {
+ dep = (xfs_dir2_data_entry_t *)p;
+ ASSERT((char *)dep - (char *)d ==
+ INT_GET(*XFS_DIR2_DATA_ENTRY_TAG_P(dep), ARCH_CONVERT));
+ p += XFS_DIR2_DATA_ENTSIZE(dep->namelen);
+ }
+ }
+}
+
+/*
+ * Initialize a data block at the given block number in the directory.
+ * Give back the buffer for the created block.
+ */
+int /* error */
+xfs_dir2_data_init(
+ xfs_da_args_t *args, /* directory operation args */
+ xfs_dir2_db_t blkno, /* logical dir block number */
+ xfs_dabuf_t **bpp) /* output block buffer */
+{
+ xfs_dabuf_t *bp; /* block buffer */
+ xfs_dir2_data_t *d; /* pointer to block */
+ xfs_inode_t *dp; /* incore directory inode */
+ xfs_dir2_data_unused_t *dup; /* unused entry pointer */
+ int error; /* error return value */
+ int i; /* bestfree index */
+ xfs_mount_t *mp; /* filesystem mount point */
+ xfs_trans_t *tp; /* transaction pointer */
+ int t; /* temp */
+
+ dp = args->dp;
+ mp = dp->i_mount;
+ tp = args->trans;
+ /*
+ * Get the buffer set up for the block.
+ */
+ error = xfs_da_get_buf(tp, dp, XFS_DIR2_DB_TO_DA(mp, blkno), -1, &bp,
+ XFS_DATA_FORK);
+ if (error) {
+ return error;
+ }
+ ASSERT(bp != NULL);
+ /*
+ * Initialize the header.
+ */
+ d = bp->data;
+ INT_SET(d->hdr.magic, ARCH_CONVERT, XFS_DIR2_DATA_MAGIC);
+ INT_SET(d->hdr.bestfree[0].offset, ARCH_CONVERT, (xfs_dir2_data_off_t)sizeof(d->hdr));
+ for (i = 1; i < XFS_DIR2_DATA_FD_COUNT; i++) {
+ INT_ZERO(d->hdr.bestfree[i].length, ARCH_CONVERT);
+ INT_ZERO(d->hdr.bestfree[i].offset, ARCH_CONVERT);
+ }
+ /*
+ * Set up an unused entry for the block's body.
+ */
+ dup = &d->u[0].unused;
+ INT_SET(dup->freetag, ARCH_CONVERT, XFS_DIR2_DATA_FREE_TAG);
+
+ t=mp->m_dirblksize - (uint)sizeof(d->hdr);
+ INT_SET(d->hdr.bestfree[0].length, ARCH_CONVERT, t);
+ INT_SET(dup->length, ARCH_CONVERT, t);
+ INT_SET(*XFS_DIR2_DATA_UNUSED_TAG_P_ARCH(dup, ARCH_CONVERT), ARCH_CONVERT,
+ (xfs_dir2_data_off_t)((char *)dup - (char *)d));
+ /*
+ * Log it and return it.
+ */
+ xfs_dir2_data_log_header(tp, bp);
+ xfs_dir2_data_log_unused(tp, bp, dup);
+ *bpp = bp;
+ return 0;
+}
+
+/*
+ * Log an active data entry from the block.
+ */
+void
+xfs_dir2_data_log_entry(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_dabuf_t *bp, /* block buffer */
+ xfs_dir2_data_entry_t *dep) /* data entry pointer */
+{
+ xfs_dir2_data_t *d; /* data block pointer */
+
+ d = bp->data;
+ ASSERT(INT_GET(d->hdr.magic, ARCH_CONVERT) == XFS_DIR2_DATA_MAGIC ||
+ INT_GET(d->hdr.magic, ARCH_CONVERT) == XFS_DIR2_BLOCK_MAGIC);
+ xfs_da_log_buf(tp, bp, (uint)((char *)dep - (char *)d),
+ (uint)((char *)(XFS_DIR2_DATA_ENTRY_TAG_P(dep) + 1) -
+ (char *)d - 1));
+}
+
+/*
+ * Log a data block header.
+ */
+void
+xfs_dir2_data_log_header(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_dabuf_t *bp) /* block buffer */
+{
+ xfs_dir2_data_t *d; /* data block pointer */
+
+ d = bp->data;
+ ASSERT(INT_GET(d->hdr.magic, ARCH_CONVERT) == XFS_DIR2_DATA_MAGIC ||
+ INT_GET(d->hdr.magic, ARCH_CONVERT) == XFS_DIR2_BLOCK_MAGIC);
+ xfs_da_log_buf(tp, bp, (uint)((char *)&d->hdr - (char *)d),
+ (uint)(sizeof(d->hdr) - 1));
+}
+
+/*
+ * Log a data unused entry.
+ */
+void
+xfs_dir2_data_log_unused(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_dabuf_t *bp, /* block buffer */
+ xfs_dir2_data_unused_t *dup) /* data unused pointer */
+{
+ xfs_dir2_data_t *d; /* data block pointer */
+
+ d = bp->data;
+ ASSERT(INT_GET(d->hdr.magic, ARCH_CONVERT) == XFS_DIR2_DATA_MAGIC ||
+ INT_GET(d->hdr.magic, ARCH_CONVERT) == XFS_DIR2_BLOCK_MAGIC);
+ /*
+ * Log the first part of the unused entry.
+ */
+ xfs_da_log_buf(tp, bp, (uint)((char *)dup - (char *)d),
+ (uint)((char *)&dup->length + sizeof(dup->length) -
+ 1 - (char *)d));
+ /*
+ * Log the end (tag) of the unused entry.
+ */
+ xfs_da_log_buf(tp, bp,
+ (uint)((char *)XFS_DIR2_DATA_UNUSED_TAG_P_ARCH(dup, ARCH_CONVERT) - (char *)d),
+ (uint)((char *)XFS_DIR2_DATA_UNUSED_TAG_P_ARCH(dup, ARCH_CONVERT) - (char *)d +
+ sizeof(xfs_dir2_data_off_t) - 1));
+}
+
+/*
+ * Make a byte range in the data block unused.
+ * Its current contents are unimportant.
+ */
+void
+xfs_dir2_data_make_free(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_dabuf_t *bp, /* block buffer */
+ xfs_dir2_data_aoff_t offset, /* starting byte offset */
+ xfs_dir2_data_aoff_t len, /* length in bytes */
+ int *needlogp, /* out: log header */
+ int *needscanp) /* out: regen bestfree */
+{
+ xfs_dir2_data_t *d; /* data block pointer */
+ xfs_dir2_data_free_t *dfp; /* bestfree pointer */
+ char *endptr; /* end of data area */
+ xfs_mount_t *mp; /* filesystem mount point */
+ int needscan; /* need to regen bestfree */
+ xfs_dir2_data_unused_t *newdup; /* new unused entry */
+ xfs_dir2_data_unused_t *postdup; /* unused entry after us */
+ xfs_dir2_data_unused_t *prevdup; /* unused entry before us */
+
+ mp = tp->t_mountp;
+ d = bp->data;
+ /*
+ * Figure out where the end of the data area is.
+ */
+ if (INT_GET(d->hdr.magic, ARCH_CONVERT) == XFS_DIR2_DATA_MAGIC)
+ endptr = (char *)d + mp->m_dirblksize;
+ else {
+ xfs_dir2_block_tail_t *btp; /* block tail */
+
+ ASSERT(INT_GET(d->hdr.magic, ARCH_CONVERT) == XFS_DIR2_BLOCK_MAGIC);
+ btp = XFS_DIR2_BLOCK_TAIL_P(mp, (xfs_dir2_block_t *)d);
+ endptr = (char *)XFS_DIR2_BLOCK_LEAF_P_ARCH(btp, ARCH_CONVERT);
+ }
+ /*
+ * If this isn't the start of the block, then back up to
+ * the previous entry and see if it's free.
+ */
+ if (offset > sizeof(d->hdr)) {
+ xfs_dir2_data_off_t *tagp; /* tag just before us */
+
+ tagp = (xfs_dir2_data_off_t *)((char *)d + offset) - 1;
+ prevdup = (xfs_dir2_data_unused_t *)((char *)d + INT_GET(*tagp, ARCH_CONVERT));
+ if (INT_GET(prevdup->freetag, ARCH_CONVERT) != XFS_DIR2_DATA_FREE_TAG)
+ prevdup = NULL;
+ } else
+ prevdup = NULL;
+ /*
+ * If this isn't the end of the block, see if the entry after
+ * us is free.
+ */
+ if ((char *)d + offset + len < endptr) {
+ postdup =
+ (xfs_dir2_data_unused_t *)((char *)d + offset + len);
+ if (INT_GET(postdup->freetag, ARCH_CONVERT) != XFS_DIR2_DATA_FREE_TAG)
+ postdup = NULL;
+ } else
+ postdup = NULL;
+ ASSERT(*needscanp == 0);
+ needscan = 0;
+ /*
+ * Previous and following entries are both free,
+ * merge everything into a single free entry.
+ */
+ if (prevdup && postdup) {
+ xfs_dir2_data_free_t *dfp2; /* another bestfree pointer */
+
+ /*
+ * See if prevdup and/or postdup are in bestfree table.
+ */
+ dfp = xfs_dir2_data_freefind(d, prevdup);
+ dfp2 = xfs_dir2_data_freefind(d, postdup);
+ /*
+ * We need a rescan unless there are exactly 2 free entries
+ * namely our two. Then we know what's happening, otherwise
+ * since the third bestfree is there, there might be more
+ * entries.
+ */
+ needscan = !INT_ISZERO(d->hdr.bestfree[2].length, ARCH_CONVERT);
+ /*
+ * Fix up the new big freespace.
+ */
+ INT_MOD(prevdup->length, ARCH_CONVERT, len + INT_GET(postdup->length, ARCH_CONVERT));
+ INT_SET(*XFS_DIR2_DATA_UNUSED_TAG_P_ARCH(prevdup, ARCH_CONVERT), ARCH_CONVERT,
+ (xfs_dir2_data_off_t)((char *)prevdup - (char *)d));
+ xfs_dir2_data_log_unused(tp, bp, prevdup);
+ if (!needscan) {
+ /*
+ * Has to be the case that entries 0 and 1 are
+ * dfp and dfp2 (don't know which is which), and
+ * entry 2 is empty.
+ * Remove entry 1 first then entry 0.
+ */
+ ASSERT(dfp && dfp2);
+ if (dfp == &d->hdr.bestfree[1]) {
+ dfp = &d->hdr.bestfree[0];
+ ASSERT(dfp2 == dfp);
+ dfp2 = &d->hdr.bestfree[1];
+ }
+ xfs_dir2_data_freeremove(d, dfp2, needlogp);
+ xfs_dir2_data_freeremove(d, dfp, needlogp);
+ /*
+ * Now insert the new entry.
+ */
+ dfp = xfs_dir2_data_freeinsert(d, prevdup, needlogp);
+ ASSERT(dfp == &d->hdr.bestfree[0]);
+ ASSERT(INT_GET(dfp->length, ARCH_CONVERT) == INT_GET(prevdup->length, ARCH_CONVERT));
+ ASSERT(INT_ISZERO(dfp[1].length, ARCH_CONVERT));
+ ASSERT(INT_ISZERO(dfp[2].length, ARCH_CONVERT));
+ }
+ }
+ /*
+ * The entry before us is free, merge with it.
+ */
+ else if (prevdup) {
+ dfp = xfs_dir2_data_freefind(d, prevdup);
+ INT_MOD(prevdup->length, ARCH_CONVERT, len);
+ INT_SET(*XFS_DIR2_DATA_UNUSED_TAG_P_ARCH(prevdup, ARCH_CONVERT), ARCH_CONVERT,
+ (xfs_dir2_data_off_t)((char *)prevdup - (char *)d));
+ xfs_dir2_data_log_unused(tp, bp, prevdup);
+ /*
+ * If the previous entry was in the table, the new entry
+ * is longer, so it will be in the table too. Remove
+ * the old one and add the new one.
+ */
+ if (dfp) {
+ xfs_dir2_data_freeremove(d, dfp, needlogp);
+ (void)xfs_dir2_data_freeinsert(d, prevdup, needlogp);
+ }
+ /*
+ * Otherwise we need a scan if the new entry is big enough.
+ */
+ else
+ needscan = INT_GET(prevdup->length, ARCH_CONVERT) > INT_GET(d->hdr.bestfree[2].length, ARCH_CONVERT);
+ }
+ /*
+ * The following entry is free, merge with it.
+ */
+ else if (postdup) {
+ dfp = xfs_dir2_data_freefind(d, postdup);
+ newdup = (xfs_dir2_data_unused_t *)((char *)d + offset);
+ INT_SET(newdup->freetag, ARCH_CONVERT, XFS_DIR2_DATA_FREE_TAG);
+ INT_SET(newdup->length, ARCH_CONVERT, len + INT_GET(postdup->length, ARCH_CONVERT));
+ INT_SET(*XFS_DIR2_DATA_UNUSED_TAG_P_ARCH(newdup, ARCH_CONVERT), ARCH_CONVERT,
+ (xfs_dir2_data_off_t)((char *)newdup - (char *)d));
+ xfs_dir2_data_log_unused(tp, bp, newdup);
+ /*
+ * If the following entry was in the table, the new entry
+ * is longer, so it will be in the table too. Remove
+ * the old one and add the new one.
+ */
+ if (dfp) {
+ xfs_dir2_data_freeremove(d, dfp, needlogp);
+ (void)xfs_dir2_data_freeinsert(d, newdup, needlogp);
+ }
+ /*
+ * Otherwise we need a scan if the new entry is big enough.
+ */
+ else
+ needscan = INT_GET(newdup->length, ARCH_CONVERT) > INT_GET(d->hdr.bestfree[2].length, ARCH_CONVERT);
+ }
+ /*
+ * Neither neighbor is free. Make a new entry.
+ */
+ else {
+ newdup = (xfs_dir2_data_unused_t *)((char *)d + offset);
+ INT_SET(newdup->freetag, ARCH_CONVERT, XFS_DIR2_DATA_FREE_TAG);
+ INT_SET(newdup->length, ARCH_CONVERT, len);
+ INT_SET(*XFS_DIR2_DATA_UNUSED_TAG_P_ARCH(newdup, ARCH_CONVERT), ARCH_CONVERT,
+ (xfs_dir2_data_off_t)((char *)newdup - (char *)d));
+ xfs_dir2_data_log_unused(tp, bp, newdup);
+ (void)xfs_dir2_data_freeinsert(d, newdup, needlogp);
+ }
+ *needscanp = needscan;
+}
+
+/*
+ * Take a byte range out of an existing unused space and make it un-free.
+ */
+void
+xfs_dir2_data_use_free(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_dabuf_t *bp, /* data block buffer */
+ xfs_dir2_data_unused_t *dup, /* unused entry */
+ xfs_dir2_data_aoff_t offset, /* starting offset to use */
+ xfs_dir2_data_aoff_t len, /* length to use */
+ int *needlogp, /* out: need to log header */
+ int *needscanp) /* out: need regen bestfree */
+{
+ xfs_dir2_data_t *d; /* data block */
+ xfs_dir2_data_free_t *dfp; /* bestfree pointer */
+ int matchback; /* matches end of freespace */
+ int matchfront; /* matches start of freespace */
+ int needscan; /* need to regen bestfree */
+ xfs_dir2_data_unused_t *newdup; /* new unused entry */
+ xfs_dir2_data_unused_t *newdup2; /* another new unused entry */
+ int oldlen; /* old unused entry's length */
+
+ d = bp->data;
+ ASSERT(INT_GET(d->hdr.magic, ARCH_CONVERT) == XFS_DIR2_DATA_MAGIC ||
+ INT_GET(d->hdr.magic, ARCH_CONVERT) == XFS_DIR2_BLOCK_MAGIC);
+ ASSERT(INT_GET(dup->freetag, ARCH_CONVERT) == XFS_DIR2_DATA_FREE_TAG);
+ ASSERT(offset >= (char *)dup - (char *)d);
+ ASSERT(offset + len <= (char *)dup + INT_GET(dup->length, ARCH_CONVERT) - (char *)d);
+ ASSERT((char *)dup - (char *)d == INT_GET(*XFS_DIR2_DATA_UNUSED_TAG_P_ARCH(dup, ARCH_CONVERT), ARCH_CONVERT));
+ /*
+ * Look up the entry in the bestfree table.
+ */
+ dfp = xfs_dir2_data_freefind(d, dup);
+ oldlen = INT_GET(dup->length, ARCH_CONVERT);
+ ASSERT(dfp || oldlen <= INT_GET(d->hdr.bestfree[2].length, ARCH_CONVERT));
+ /*
+ * Check for alignment with front and back of the entry.
+ */
+ matchfront = (char *)dup - (char *)d == offset;
+ matchback = (char *)dup + oldlen - (char *)d == offset + len;
+ ASSERT(*needscanp == 0);
+ needscan = 0;
+ /*
+ * If we matched it exactly we just need to get rid of it from
+ * the bestfree table.
+ */
+ if (matchfront && matchback) {
+ if (dfp) {
+ needscan = !INT_ISZERO(d->hdr.bestfree[2].offset, ARCH_CONVERT);
+ if (!needscan)
+ xfs_dir2_data_freeremove(d, dfp, needlogp);
+ }
+ }
+ /*
+ * We match the first part of the entry.
+ * Make a new entry with the remaining freespace.
+ */
+ else if (matchfront) {
+ newdup = (xfs_dir2_data_unused_t *)((char *)d + offset + len);
+ INT_SET(newdup->freetag, ARCH_CONVERT, XFS_DIR2_DATA_FREE_TAG);
+ INT_SET(newdup->length, ARCH_CONVERT, oldlen - len);
+ INT_SET(*XFS_DIR2_DATA_UNUSED_TAG_P_ARCH(newdup, ARCH_CONVERT), ARCH_CONVERT,
+ (xfs_dir2_data_off_t)((char *)newdup - (char *)d));
+ xfs_dir2_data_log_unused(tp, bp, newdup);
+ /*
+ * If it was in the table, remove it and add the new one.
+ */
+ if (dfp) {
+ xfs_dir2_data_freeremove(d, dfp, needlogp);
+ dfp = xfs_dir2_data_freeinsert(d, newdup, needlogp);
+ ASSERT(dfp != NULL);
+ ASSERT(INT_GET(dfp->length, ARCH_CONVERT) == INT_GET(newdup->length, ARCH_CONVERT));
+ ASSERT(INT_GET(dfp->offset, ARCH_CONVERT) == (char *)newdup - (char *)d);
+ /*
+ * If we got inserted at the last slot,
+ * that means we don't know if there was a better
+ * choice for the last slot, or not. Rescan.
+ */
+ needscan = dfp == &d->hdr.bestfree[2];
+ }
+ }
+ /*
+ * We match the last part of the entry.
+ * Trim the allocated space off the tail of the entry.
+ */
+ else if (matchback) {
+ newdup = dup;
+ INT_SET(newdup->length, ARCH_CONVERT, (xfs_dir2_data_off_t)
+ (((char *)d + offset) - (char *)newdup));
+ INT_SET(*XFS_DIR2_DATA_UNUSED_TAG_P_ARCH(newdup, ARCH_CONVERT), ARCH_CONVERT,
+ (xfs_dir2_data_off_t)((char *)newdup - (char *)d));
+ xfs_dir2_data_log_unused(tp, bp, newdup);
+ /*
+ * If it was in the table, remove it and add the new one.
+ */
+ if (dfp) {
+ xfs_dir2_data_freeremove(d, dfp, needlogp);
+ dfp = xfs_dir2_data_freeinsert(d, newdup, needlogp);
+ ASSERT(dfp != NULL);
+ ASSERT(INT_GET(dfp->length, ARCH_CONVERT) == INT_GET(newdup->length, ARCH_CONVERT));
+ ASSERT(INT_GET(dfp->offset, ARCH_CONVERT) == (char *)newdup - (char *)d);
+ /*
+ * If we got inserted at the last slot,
+ * that means we don't know if there was a better
+ * choice for the last slot, or not. Rescan.
+ */
+ needscan = dfp == &d->hdr.bestfree[2];
+ }
+ }
+ /*
+ * Poking out the middle of an entry.
+ * Make two new entries.
+ */
+ else {
+ newdup = dup;
+ INT_SET(newdup->length, ARCH_CONVERT, (xfs_dir2_data_off_t)
+ (((char *)d + offset) - (char *)newdup));
+ INT_SET(*XFS_DIR2_DATA_UNUSED_TAG_P_ARCH(newdup, ARCH_CONVERT), ARCH_CONVERT,
+ (xfs_dir2_data_off_t)((char *)newdup - (char *)d));
+ xfs_dir2_data_log_unused(tp, bp, newdup);
+ newdup2 = (xfs_dir2_data_unused_t *)((char *)d + offset + len);
+ INT_SET(newdup2->freetag, ARCH_CONVERT, XFS_DIR2_DATA_FREE_TAG);
+ INT_SET(newdup2->length, ARCH_CONVERT, oldlen - len - INT_GET(newdup->length, ARCH_CONVERT));
+ INT_SET(*XFS_DIR2_DATA_UNUSED_TAG_P_ARCH(newdup2, ARCH_CONVERT), ARCH_CONVERT,
+ (xfs_dir2_data_off_t)((char *)newdup2 - (char *)d));
+ xfs_dir2_data_log_unused(tp, bp, newdup2);
+ /*
+ * If the old entry was in the table, we need to scan
+ * if the 3rd entry was valid, since these entries
+ * are smaller than the old one.
+ * If we don't need to scan that means there were 1 or 2
+ * entries in the table, and removing the old and adding
+ * the 2 new will work.
+ */
+ if (dfp) {
+ needscan = !INT_ISZERO(d->hdr.bestfree[2].length, ARCH_CONVERT);
+ if (!needscan) {
+ xfs_dir2_data_freeremove(d, dfp, needlogp);
+ (void)xfs_dir2_data_freeinsert(d, newdup,
+ needlogp);
+ (void)xfs_dir2_data_freeinsert(d, newdup2,
+ needlogp);
+ }
+ }
+ }
+ *needscanp = needscan;
+}
diff --git a/fs/xfs/xfs_dir2_data.h b/fs/xfs/xfs_dir2_data.h
new file mode 100644
index 00000000000000..b9e16ab5f6223b
--- /dev/null
+++ b/fs/xfs/xfs_dir2_data.h
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_DIR2_DATA_H__
+#define __XFS_DIR2_DATA_H__
+
+/*
+ * Directory format 2, data block structures.
+ */
+
+struct xfs_dabuf;
+struct xfs_da_args;
+struct xfs_inode;
+struct xfs_trans;
+
+/*
+ * Constants.
+ */
+#define XFS_DIR2_DATA_MAGIC 0x58443244 /* XD2D: for multiblock dirs */
+#define XFS_DIR2_DATA_ALIGN_LOG 3 /* i.e., 8 bytes */
+#define XFS_DIR2_DATA_ALIGN (1 << XFS_DIR2_DATA_ALIGN_LOG)
+#define XFS_DIR2_DATA_FREE_TAG 0xffff
+#define XFS_DIR2_DATA_FD_COUNT 3
+
+/*
+ * Directory address space divided into sections,
+ * spaces separated by 32gb.
+ */
+#define XFS_DIR2_SPACE_SIZE (1ULL << (32 + XFS_DIR2_DATA_ALIGN_LOG))
+#define XFS_DIR2_DATA_SPACE 0
+#define XFS_DIR2_DATA_OFFSET (XFS_DIR2_DATA_SPACE * XFS_DIR2_SPACE_SIZE)
+#define XFS_DIR2_DATA_FIRSTDB(mp) \
+ XFS_DIR2_BYTE_TO_DB(mp, XFS_DIR2_DATA_OFFSET)
+
+/*
+ * Offsets of . and .. in data space (always block 0)
+ */
+#define XFS_DIR2_DATA_DOT_OFFSET \
+ ((xfs_dir2_data_aoff_t)sizeof(xfs_dir2_data_hdr_t))
+#define XFS_DIR2_DATA_DOTDOT_OFFSET \
+ (XFS_DIR2_DATA_DOT_OFFSET + XFS_DIR2_DATA_ENTSIZE(1))
+#define XFS_DIR2_DATA_FIRST_OFFSET \
+ (XFS_DIR2_DATA_DOTDOT_OFFSET + XFS_DIR2_DATA_ENTSIZE(2))
+
+/*
+ * Structures.
+ */
+
+/*
+ * Describe a free area in the data block.
+ * The freespace will be formatted as a xfs_dir2_data_unused_t.
+ */
+typedef struct xfs_dir2_data_free {
+ xfs_dir2_data_off_t offset; /* start of freespace */
+ xfs_dir2_data_off_t length; /* length of freespace */
+} xfs_dir2_data_free_t;
+
+/*
+ * Header for the data blocks.
+ * Always at the beginning of a directory-sized block.
+ * The code knows that XFS_DIR2_DATA_FD_COUNT is 3.
+ */
+typedef struct xfs_dir2_data_hdr {
+ __uint32_t magic; /* XFS_DIR2_DATA_MAGIC */
+ /* or XFS_DIR2_BLOCK_MAGIC */
+ xfs_dir2_data_free_t bestfree[XFS_DIR2_DATA_FD_COUNT];
+} xfs_dir2_data_hdr_t;
+
+/*
+ * Active entry in a data block. Aligned to 8 bytes.
+ * Tag appears as the last 2 bytes.
+ */
+typedef struct xfs_dir2_data_entry {
+ xfs_ino_t inumber; /* inode number */
+ __uint8_t namelen; /* name length */
+ __uint8_t name[1]; /* name bytes, no null */
+ /* variable offset */
+ xfs_dir2_data_off_t tag; /* starting offset of us */
+} xfs_dir2_data_entry_t;
+
+/*
+ * Unused entry in a data block. Aligned to 8 bytes.
+ * Tag appears as the last 2 bytes.
+ */
+typedef struct xfs_dir2_data_unused {
+ __uint16_t freetag; /* XFS_DIR2_DATA_FREE_TAG */
+ xfs_dir2_data_off_t length; /* total free length */
+ /* variable offset */
+ xfs_dir2_data_off_t tag; /* starting offset of us */
+} xfs_dir2_data_unused_t;
+
+typedef union {
+ xfs_dir2_data_entry_t entry;
+ xfs_dir2_data_unused_t unused;
+} xfs_dir2_data_union_t;
+
+/*
+ * Generic data block structure, for xfs_db.
+ */
+typedef struct xfs_dir2_data {
+ xfs_dir2_data_hdr_t hdr; /* magic XFS_DIR2_DATA_MAGIC */
+ xfs_dir2_data_union_t u[1];
+} xfs_dir2_data_t;
+
+/*
+ * Macros.
+ */
+
+/*
+ * Size of a data entry.
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR2_DATA_ENTSIZE)
+int xfs_dir2_data_entsize(int n);
+#define XFS_DIR2_DATA_ENTSIZE(n) xfs_dir2_data_entsize(n)
+#else
+#define XFS_DIR2_DATA_ENTSIZE(n) \
+ ((int)(roundup(offsetof(xfs_dir2_data_entry_t, name[0]) + (n) + \
+ (uint)sizeof(xfs_dir2_data_off_t), XFS_DIR2_DATA_ALIGN)))
+#endif
+
+/*
+ * Pointer to an entry's tag word.
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR2_DATA_ENTRY_TAG_P)
+xfs_dir2_data_off_t *xfs_dir2_data_entry_tag_p(xfs_dir2_data_entry_t *dep);
+#define XFS_DIR2_DATA_ENTRY_TAG_P(dep) xfs_dir2_data_entry_tag_p(dep)
+#else
+#define XFS_DIR2_DATA_ENTRY_TAG_P(dep) \
+ ((xfs_dir2_data_off_t *)\
+ ((char *)(dep) + XFS_DIR2_DATA_ENTSIZE((dep)->namelen) - \
+ (uint)sizeof(xfs_dir2_data_off_t)))
+#endif
+
+/*
+ * Pointer to a freespace's tag word.
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR2_DATA_UNUSED_TAG_P)
+xfs_dir2_data_off_t *xfs_dir2_data_unused_tag_p_arch(
+ xfs_dir2_data_unused_t *dup, xfs_arch_t arch);
+#define XFS_DIR2_DATA_UNUSED_TAG_P_ARCH(dup,arch) \
+ xfs_dir2_data_unused_tag_p_arch(dup,arch)
+#else
+#define XFS_DIR2_DATA_UNUSED_TAG_P_ARCH(dup,arch) \
+ ((xfs_dir2_data_off_t *)\
+ ((char *)(dup) + INT_GET((dup)->length, arch) \
+ - (uint)sizeof(xfs_dir2_data_off_t)))
+#endif
+
+/*
+ * Function declarations.
+ */
+
+#ifdef DEBUG
+extern void
+ xfs_dir2_data_check(struct xfs_inode *dp, struct xfs_dabuf *bp);
+#else
+#define xfs_dir2_data_check(dp,bp)
+#endif
+
+extern xfs_dir2_data_free_t *
+ xfs_dir2_data_freefind(xfs_dir2_data_t *d,
+ xfs_dir2_data_unused_t *dup);
+
+extern xfs_dir2_data_free_t *
+ xfs_dir2_data_freeinsert(xfs_dir2_data_t *d,
+ xfs_dir2_data_unused_t *dup, int *loghead);
+
+extern void
+ xfs_dir2_data_freeremove(xfs_dir2_data_t *d,
+ xfs_dir2_data_free_t *dfp, int *loghead);
+
+extern void
+ xfs_dir2_data_freescan(struct xfs_mount *mp, xfs_dir2_data_t *d,
+ int *loghead, char *aendp);
+
+extern int
+ xfs_dir2_data_init(struct xfs_da_args *args, xfs_dir2_db_t blkno,
+ struct xfs_dabuf **bpp);
+
+extern void
+ xfs_dir2_data_log_entry(struct xfs_trans *tp, struct xfs_dabuf *bp,
+ xfs_dir2_data_entry_t *dep);
+
+extern void
+ xfs_dir2_data_log_header(struct xfs_trans *tp, struct xfs_dabuf *bp);
+
+extern void
+ xfs_dir2_data_log_unused(struct xfs_trans *tp, struct xfs_dabuf *bp,
+ xfs_dir2_data_unused_t *dup);
+
+extern void
+ xfs_dir2_data_make_free(struct xfs_trans *tp, struct xfs_dabuf *bp,
+ xfs_dir2_data_aoff_t offset,
+ xfs_dir2_data_aoff_t len, int *needlogp,
+ int *needscanp);
+
+extern void
+ xfs_dir2_data_use_free(struct xfs_trans *tp, struct xfs_dabuf *bp,
+ xfs_dir2_data_unused_t *dup,
+ xfs_dir2_data_aoff_t offset,
+ xfs_dir2_data_aoff_t len, int *needlogp,
+ int *needscanp);
+
+#endif /* __XFS_DIR2_DATA_H__ */
diff --git a/fs/xfs/xfs_dir2_leaf.c b/fs/xfs/xfs_dir2_leaf.c
new file mode 100644
index 00000000000000..c75d54f3fda7c7
--- /dev/null
+++ b/fs/xfs/xfs_dir2_leaf.c
@@ -0,0 +1,1896 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+/*
+ * xfs_dir2_leaf.c
+ * XFS directory version 2 implementation - single leaf form
+ * see xfs_dir2_leaf.h for data structures.
+ * These directories have multiple XFS_DIR2_DATA blocks and one
+ * XFS_DIR2_LEAF1 block containing the hash table and freespace map.
+ */
+
+#include "xfs.h"
+
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode.h"
+#include "xfs_bmap.h"
+#include "xfs_da_btree.h"
+#include "xfs_dir2_data.h"
+#include "xfs_dir2_leaf.h"
+#include "xfs_dir2_block.h"
+#include "xfs_dir2_node.h"
+#include "xfs_dir2_trace.h"
+#include "xfs_error.h"
+#include "xfs_bit.h"
+
+/*
+ * Local function declarations.
+ */
+#ifdef DEBUG
+static void xfs_dir2_leaf_check(xfs_inode_t *dp, xfs_dabuf_t *bp);
+#else
+#define xfs_dir2_leaf_check(dp, bp)
+#endif
+static int xfs_dir2_leaf_lookup_int(xfs_da_args_t *args, xfs_dabuf_t **lbpp,
+ int *indexp, xfs_dabuf_t **dbpp);
+
+/*
+ * Convert a block form directory to a leaf form directory.
+ */
+int /* error */
+xfs_dir2_block_to_leaf(
+ xfs_da_args_t *args, /* operation arguments */
+ xfs_dabuf_t *dbp) /* input block's buffer */
+{
+ xfs_dir2_data_off_t *bestsp; /* leaf's bestsp entries */
+ xfs_dablk_t blkno; /* leaf block's bno */
+ xfs_dir2_block_t *block; /* block structure */
+ xfs_dir2_leaf_entry_t *blp; /* block's leaf entries */
+ xfs_dir2_block_tail_t *btp; /* block's tail */
+ xfs_inode_t *dp; /* incore directory inode */
+ int error; /* error return code */
+ xfs_dabuf_t *lbp; /* leaf block's buffer */
+ xfs_dir2_db_t ldb; /* leaf block's bno */
+ xfs_dir2_leaf_t *leaf; /* leaf structure */
+ xfs_dir2_leaf_tail_t *ltp; /* leaf's tail */
+ xfs_mount_t *mp; /* filesystem mount point */
+ int needlog; /* need to log block header */
+ int needscan; /* need to rescan bestfree */
+ xfs_trans_t *tp; /* transaction pointer */
+
+ xfs_dir2_trace_args_b("block_to_leaf", args, dbp);
+ dp = args->dp;
+ mp = dp->i_mount;
+ tp = args->trans;
+ /*
+ * Add the leaf block to the inode.
+ * This interface will only put blocks in the leaf/node range.
+ * Since that's empty now, we'll get the root (block 0 in range).
+ */
+ if ((error = xfs_da_grow_inode(args, &blkno))) {
+ return error;
+ }
+ ldb = XFS_DIR2_DA_TO_DB(mp, blkno);
+ ASSERT(ldb == XFS_DIR2_LEAF_FIRSTDB(mp));
+ /*
+ * Initialize the leaf block, get a buffer for it.
+ */
+ if ((error = xfs_dir2_leaf_init(args, ldb, &lbp, XFS_DIR2_LEAF1_MAGIC))) {
+ return error;
+ }
+ ASSERT(lbp != NULL);
+ leaf = lbp->data;
+ block = dbp->data;
+ xfs_dir2_data_check(dp, dbp);
+ btp = XFS_DIR2_BLOCK_TAIL_P(mp, block);
+ blp = XFS_DIR2_BLOCK_LEAF_P_ARCH(btp, ARCH_CONVERT);
+ /*
+ * Set the counts in the leaf header.
+ */
+ INT_COPY(leaf->hdr.count, btp->count, ARCH_CONVERT); /* INT_: type change */
+ INT_COPY(leaf->hdr.stale, btp->stale, ARCH_CONVERT); /* INT_: type change */
+ /*
+ * Could compact these but I think we always do the conversion
+ * after squeezing out stale entries.
+ */
+ memcpy(leaf->ents, blp, INT_GET(btp->count, ARCH_CONVERT) * sizeof(xfs_dir2_leaf_entry_t));
+ xfs_dir2_leaf_log_ents(tp, lbp, 0, INT_GET(leaf->hdr.count, ARCH_CONVERT) - 1);
+ needscan = 0;
+ needlog = 1;
+ /*
+ * Make the space formerly occupied by the leaf entries and block
+ * tail be free.
+ */
+ xfs_dir2_data_make_free(tp, dbp,
+ (xfs_dir2_data_aoff_t)((char *)blp - (char *)block),
+ (xfs_dir2_data_aoff_t)((char *)block + mp->m_dirblksize -
+ (char *)blp),
+ &needlog, &needscan);
+ /*
+ * Fix up the block header, make it a data block.
+ */
+ INT_SET(block->hdr.magic, ARCH_CONVERT, XFS_DIR2_DATA_MAGIC);
+ if (needscan)
+ xfs_dir2_data_freescan(mp, (xfs_dir2_data_t *)block, &needlog,
+ NULL);
+ /*
+ * Set up leaf tail and bests table.
+ */
+ ltp = XFS_DIR2_LEAF_TAIL_P(mp, leaf);
+ INT_SET(ltp->bestcount, ARCH_CONVERT, 1);
+ bestsp = XFS_DIR2_LEAF_BESTS_P_ARCH(ltp, ARCH_CONVERT);
+ INT_COPY(bestsp[0], block->hdr.bestfree[0].length, ARCH_CONVERT);
+ /*
+ * Log the data header and leaf bests table.
+ */
+ if (needlog)
+ xfs_dir2_data_log_header(tp, dbp);
+ xfs_dir2_leaf_check(dp, lbp);
+ xfs_dir2_data_check(dp, dbp);
+ xfs_dir2_leaf_log_bests(tp, lbp, 0, 0);
+ xfs_da_buf_done(lbp);
+ return 0;
+}
+
+/*
+ * Add an entry to a leaf form directory.
+ */
+int /* error */
+xfs_dir2_leaf_addname(
+ xfs_da_args_t *args) /* operation arguments */
+{
+ xfs_dir2_data_off_t *bestsp; /* freespace table in leaf */
+ int compact; /* need to compact leaves */
+ xfs_dir2_data_t *data; /* data block structure */
+ xfs_dabuf_t *dbp; /* data block buffer */
+ xfs_dir2_data_entry_t *dep; /* data block entry */
+ xfs_inode_t *dp; /* incore directory inode */
+ xfs_dir2_data_unused_t *dup; /* data unused entry */
+ int error; /* error return value */
+ int grown; /* allocated new data block */
+ int highstale; /* index of next stale leaf */
+ int i; /* temporary, index */
+ int index; /* leaf table position */
+ xfs_dabuf_t *lbp; /* leaf's buffer */
+ xfs_dir2_leaf_t *leaf; /* leaf structure */
+ int length; /* length of new entry */
+ xfs_dir2_leaf_entry_t *lep; /* leaf entry table pointer */
+ int lfloglow; /* low leaf logging index */
+ int lfloghigh; /* high leaf logging index */
+ int lowstale; /* index of prev stale leaf */
+ xfs_dir2_leaf_tail_t *ltp; /* leaf tail pointer */
+ xfs_mount_t *mp; /* filesystem mount point */
+ int needbytes; /* leaf block bytes needed */
+ int needlog; /* need to log data header */
+ int needscan; /* need to rescan data free */
+ xfs_dir2_data_off_t *tagp; /* end of data entry */
+ xfs_trans_t *tp; /* transaction pointer */
+ xfs_dir2_db_t use_block; /* data block number */
+
+ xfs_dir2_trace_args("leaf_addname", args);
+ dp = args->dp;
+ tp = args->trans;
+ mp = dp->i_mount;
+ /*
+ * Read the leaf block.
+ */
+ error = xfs_da_read_buf(tp, dp, mp->m_dirleafblk, -1, &lbp,
+ XFS_DATA_FORK);
+ if (error) {
+ return error;
+ }
+ ASSERT(lbp != NULL);
+ /*
+ * Look up the entry by hash value and name.
+ * We know it's not there, our caller has already done a lookup.
+ * So the index is of the entry to insert in front of.
+ * But if there are dup hash values the index is of the first of those.
+ */
+ index = xfs_dir2_leaf_search_hash(args, lbp);
+ leaf = lbp->data;
+ ltp = XFS_DIR2_LEAF_TAIL_P(mp, leaf);
+ bestsp = XFS_DIR2_LEAF_BESTS_P_ARCH(ltp, ARCH_CONVERT);
+ length = XFS_DIR2_DATA_ENTSIZE(args->namelen);
+ /*
+ * See if there are any entries with the same hash value
+ * and space in their block for the new entry.
+ * This is good because it puts multiple same-hash value entries
+ * in a data block, improving the lookup of those entries.
+ */
+ for (use_block = -1, lep = &leaf->ents[index];
+ index < INT_GET(leaf->hdr.count, ARCH_CONVERT) && INT_GET(lep->hashval, ARCH_CONVERT) == args->hashval;
+ index++, lep++) {
+ if (INT_GET(lep->address, ARCH_CONVERT) == XFS_DIR2_NULL_DATAPTR)
+ continue;
+ i = XFS_DIR2_DATAPTR_TO_DB(mp, INT_GET(lep->address, ARCH_CONVERT));
+ ASSERT(i < INT_GET(ltp->bestcount, ARCH_CONVERT));
+ ASSERT(INT_GET(bestsp[i], ARCH_CONVERT) != NULLDATAOFF);
+ if (INT_GET(bestsp[i], ARCH_CONVERT) >= length) {
+ use_block = i;
+ break;
+ }
+ }
+ /*
+ * Didn't find a block yet, linear search all the data blocks.
+ */
+ if (use_block == -1) {
+ for (i = 0; i < INT_GET(ltp->bestcount, ARCH_CONVERT); i++) {
+ /*
+ * Remember a block we see that's missing.
+ */
+ if (INT_GET(bestsp[i], ARCH_CONVERT) == NULLDATAOFF && use_block == -1)
+ use_block = i;
+ else if (INT_GET(bestsp[i], ARCH_CONVERT) >= length) {
+ use_block = i;
+ break;
+ }
+ }
+ }
+ /*
+ * How many bytes do we need in the leaf block?
+ */
+ needbytes =
+ (!INT_ISZERO(leaf->hdr.stale, ARCH_CONVERT) ? 0 : (uint)sizeof(leaf->ents[0])) +
+ (use_block != -1 ? 0 : (uint)sizeof(leaf->bests[0]));
+ /*
+ * Now kill use_block if it refers to a missing block, so we
+ * can use it as an indication of allocation needed.
+ */
+ if (use_block != -1 && INT_GET(bestsp[use_block], ARCH_CONVERT) == NULLDATAOFF)
+ use_block = -1;
+ /*
+ * If we don't have enough free bytes but we can make enough
+ * by compacting out stale entries, we'll do that.
+ */
+ if ((char *)bestsp - (char *)&leaf->ents[INT_GET(leaf->hdr.count, ARCH_CONVERT)] < needbytes &&
+ INT_GET(leaf->hdr.stale, ARCH_CONVERT) > 1) {
+ compact = 1;
+ }
+ /*
+ * Otherwise if we don't have enough free bytes we need to
+ * convert to node form.
+ */
+ else if ((char *)bestsp - (char *)&leaf->ents[INT_GET(leaf->hdr.count, ARCH_CONVERT)] <
+ needbytes) {
+ /*
+ * Just checking or no space reservation, give up.
+ */
+ if (args->justcheck || args->total == 0) {
+ xfs_da_brelse(tp, lbp);
+ return XFS_ERROR(ENOSPC);
+ }
+ /*
+ * Convert to node form.
+ */
+ error = xfs_dir2_leaf_to_node(args, lbp);
+ xfs_da_buf_done(lbp);
+ if (error)
+ return error;
+ /*
+ * Then add the new entry.
+ */
+ return xfs_dir2_node_addname(args);
+ }
+ /*
+ * Otherwise it will fit without compaction.
+ */
+ else
+ compact = 0;
+ /*
+ * If just checking, then it will fit unless we needed to allocate
+ * a new data block.
+ */
+ if (args->justcheck) {
+ xfs_da_brelse(tp, lbp);
+ return use_block == -1 ? XFS_ERROR(ENOSPC) : 0;
+ }
+ /*
+ * If no allocations are allowed, return now before we've
+ * changed anything.
+ */
+ if (args->total == 0 && use_block == -1) {
+ xfs_da_brelse(tp, lbp);
+ return XFS_ERROR(ENOSPC);
+ }
+ /*
+ * Need to compact the leaf entries, removing stale ones.
+ * Leave one stale entry behind - the one closest to our
+ * insertion index - and we'll shift that one to our insertion
+ * point later.
+ */
+ if (compact) {
+ xfs_dir2_leaf_compact_x1(lbp, &index, &lowstale, &highstale,
+ &lfloglow, &lfloghigh);
+ }
+ /*
+ * There are stale entries, so we'll need log-low and log-high
+ * impossibly bad values later.
+ */
+ else if (INT_GET(leaf->hdr.stale, ARCH_CONVERT)) {
+ lfloglow = INT_GET(leaf->hdr.count, ARCH_CONVERT);
+ lfloghigh = -1;
+ }
+ /*
+ * If there was no data block space found, we need to allocate
+ * a new one.
+ */
+ if (use_block == -1) {
+ /*
+ * Add the new data block.
+ */
+ if ((error = xfs_dir2_grow_inode(args, XFS_DIR2_DATA_SPACE,
+ &use_block))) {
+ xfs_da_brelse(tp, lbp);
+ return error;
+ }
+ /*
+ * Initialize the block.
+ */
+ if ((error = xfs_dir2_data_init(args, use_block, &dbp))) {
+ xfs_da_brelse(tp, lbp);
+ return error;
+ }
+ /*
+ * If we're adding a new data block on the end we need to
+ * extend the bests table. Copy it up one entry.
+ */
+ if (use_block >= INT_GET(ltp->bestcount, ARCH_CONVERT)) {
+ bestsp--;
+ memmove(&bestsp[0], &bestsp[1],
+ INT_GET(ltp->bestcount, ARCH_CONVERT) * sizeof(bestsp[0]));
+ INT_MOD(ltp->bestcount, ARCH_CONVERT, +1);
+ xfs_dir2_leaf_log_tail(tp, lbp);
+ xfs_dir2_leaf_log_bests(tp, lbp, 0, INT_GET(ltp->bestcount, ARCH_CONVERT) - 1);
+ }
+ /*
+ * If we're filling in a previously empty block just log it.
+ */
+ else
+ xfs_dir2_leaf_log_bests(tp, lbp, use_block, use_block);
+ data = dbp->data;
+ INT_COPY(bestsp[use_block], data->hdr.bestfree[0].length, ARCH_CONVERT);
+ grown = 1;
+ }
+ /*
+ * Already had space in some data block.
+ * Just read that one in.
+ */
+ else {
+ if ((error =
+ xfs_da_read_buf(tp, dp, XFS_DIR2_DB_TO_DA(mp, use_block),
+ -1, &dbp, XFS_DATA_FORK))) {
+ xfs_da_brelse(tp, lbp);
+ return error;
+ }
+ data = dbp->data;
+ grown = 0;
+ }
+ xfs_dir2_data_check(dp, dbp);
+ /*
+ * Point to the biggest freespace in our data block.
+ */
+ dup = (xfs_dir2_data_unused_t *)
+ ((char *)data + INT_GET(data->hdr.bestfree[0].offset, ARCH_CONVERT));
+ ASSERT(INT_GET(dup->length, ARCH_CONVERT) >= length);
+ needscan = needlog = 0;
+ /*
+ * Mark the initial part of our freespace in use for the new entry.
+ */
+ xfs_dir2_data_use_free(tp, dbp, dup,
+ (xfs_dir2_data_aoff_t)((char *)dup - (char *)data), length,
+ &needlog, &needscan);
+ /*
+ * Initialize our new entry (at last).
+ */
+ dep = (xfs_dir2_data_entry_t *)dup;
+ INT_SET(dep->inumber, ARCH_CONVERT, args->inumber);
+ dep->namelen = args->namelen;
+ memcpy(dep->name, args->name, dep->namelen);
+ tagp = XFS_DIR2_DATA_ENTRY_TAG_P(dep);
+ INT_SET(*tagp, ARCH_CONVERT, (xfs_dir2_data_off_t)((char *)dep - (char *)data));
+ /*
+ * Need to scan fix up the bestfree table.
+ */
+ if (needscan)
+ xfs_dir2_data_freescan(mp, data, &needlog, NULL);
+ /*
+ * Need to log the data block's header.
+ */
+ if (needlog)
+ xfs_dir2_data_log_header(tp, dbp);
+ xfs_dir2_data_log_entry(tp, dbp, dep);
+ /*
+ * If the bests table needs to be changed, do it.
+ * Log the change unless we've already done that.
+ */
+ if (INT_GET(bestsp[use_block], ARCH_CONVERT) != INT_GET(data->hdr.bestfree[0].length, ARCH_CONVERT)) {
+ INT_COPY(bestsp[use_block], data->hdr.bestfree[0].length, ARCH_CONVERT);
+ if (!grown)
+ xfs_dir2_leaf_log_bests(tp, lbp, use_block, use_block);
+ }
+ /*
+ * Now we need to make room to insert the leaf entry.
+ * If there are no stale entries, we just insert a hole at index.
+ */
+ if (INT_ISZERO(leaf->hdr.stale, ARCH_CONVERT)) {
+ /*
+ * lep is still good as the index leaf entry.
+ */
+ if (index < INT_GET(leaf->hdr.count, ARCH_CONVERT))
+ memmove(lep + 1, lep,
+ (INT_GET(leaf->hdr.count, ARCH_CONVERT) - index) * sizeof(*lep));
+ /*
+ * Record low and high logging indices for the leaf.
+ */
+ lfloglow = index;
+ lfloghigh = INT_GET(leaf->hdr.count, ARCH_CONVERT);
+ INT_MOD(leaf->hdr.count, ARCH_CONVERT, +1);
+ }
+ /*
+ * There are stale entries.
+ * We will use one of them for the new entry.
+ * It's probably not at the right location, so we'll have to
+ * shift some up or down first.
+ */
+ else {
+ /*
+ * If we didn't compact before, we need to find the nearest
+ * stale entries before and after our insertion point.
+ */
+ if (compact == 0) {
+ /*
+ * Find the first stale entry before the insertion
+ * point, if any.
+ */
+ for (lowstale = index - 1;
+ lowstale >= 0 &&
+ INT_GET(leaf->ents[lowstale].address, ARCH_CONVERT) !=
+ XFS_DIR2_NULL_DATAPTR;
+ lowstale--)
+ continue;
+ /*
+ * Find the next stale entry at or after the insertion
+ * point, if any. Stop if we go so far that the
+ * lowstale entry would be better.
+ */
+ for (highstale = index;
+ highstale < INT_GET(leaf->hdr.count, ARCH_CONVERT) &&
+ INT_GET(leaf->ents[highstale].address, ARCH_CONVERT) !=
+ XFS_DIR2_NULL_DATAPTR &&
+ (lowstale < 0 ||
+ index - lowstale - 1 >= highstale - index);
+ highstale++)
+ continue;
+ }
+ /*
+ * If the low one is better, use it.
+ */
+ if (lowstale >= 0 &&
+ (highstale == INT_GET(leaf->hdr.count, ARCH_CONVERT) ||
+ index - lowstale - 1 < highstale - index)) {
+ ASSERT(index - lowstale - 1 >= 0);
+ ASSERT(INT_GET(leaf->ents[lowstale].address, ARCH_CONVERT) ==
+ XFS_DIR2_NULL_DATAPTR);
+ /*
+ * Copy entries up to cover the stale entry
+ * and make room for the new entry.
+ */
+ if (index - lowstale - 1 > 0)
+ memmove(&leaf->ents[lowstale],
+ &leaf->ents[lowstale + 1],
+ (index - lowstale - 1) * sizeof(*lep));
+ lep = &leaf->ents[index - 1];
+ lfloglow = MIN(lowstale, lfloglow);
+ lfloghigh = MAX(index - 1, lfloghigh);
+ }
+ /*
+ * The high one is better, so use that one.
+ */
+ else {
+ ASSERT(highstale - index >= 0);
+ ASSERT(INT_GET(leaf->ents[highstale].address, ARCH_CONVERT) ==
+ XFS_DIR2_NULL_DATAPTR);
+ /*
+ * Copy entries down to copver the stale entry
+ * and make room for the new entry.
+ */
+ if (highstale - index > 0)
+ memmove(&leaf->ents[index + 1],
+ &leaf->ents[index],
+ (highstale - index) * sizeof(*lep));
+ lep = &leaf->ents[index];
+ lfloglow = MIN(index, lfloglow);
+ lfloghigh = MAX(highstale, lfloghigh);
+ }
+ INT_MOD(leaf->hdr.stale, ARCH_CONVERT, -1);
+ }
+ /*
+ * Fill in the new leaf entry.
+ */
+ INT_SET(lep->hashval, ARCH_CONVERT, args->hashval);
+ INT_SET(lep->address, ARCH_CONVERT, XFS_DIR2_DB_OFF_TO_DATAPTR(mp, use_block, INT_GET(*tagp, ARCH_CONVERT)));
+ /*
+ * Log the leaf fields and give up the buffers.
+ */
+ xfs_dir2_leaf_log_header(tp, lbp);
+ xfs_dir2_leaf_log_ents(tp, lbp, lfloglow, lfloghigh);
+ xfs_dir2_leaf_check(dp, lbp);
+ xfs_da_buf_done(lbp);
+ xfs_dir2_data_check(dp, dbp);
+ xfs_da_buf_done(dbp);
+ return 0;
+}
+
+#ifdef DEBUG
+/*
+ * Check the internal consistency of a leaf1 block.
+ * Pop an assert if something is wrong.
+ */
+void
+xfs_dir2_leaf_check(
+ xfs_inode_t *dp, /* incore directory inode */
+ xfs_dabuf_t *bp) /* leaf's buffer */
+{
+ int i; /* leaf index */
+ xfs_dir2_leaf_t *leaf; /* leaf structure */
+ xfs_dir2_leaf_tail_t *ltp; /* leaf tail pointer */
+ xfs_mount_t *mp; /* filesystem mount point */
+ int stale; /* count of stale leaves */
+
+ leaf = bp->data;
+ mp = dp->i_mount;
+ ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) == XFS_DIR2_LEAF1_MAGIC);
+ /*
+ * This value is not restrictive enough.
+ * Should factor in the size of the bests table as well.
+ * We can deduce a value for that from di_size.
+ */
+ ASSERT(INT_GET(leaf->hdr.count, ARCH_CONVERT) <= XFS_DIR2_MAX_LEAF_ENTS(mp));
+ ltp = XFS_DIR2_LEAF_TAIL_P(mp, leaf);
+ /*
+ * Leaves and bests don't overlap.
+ */
+ ASSERT((char *)&leaf->ents[INT_GET(leaf->hdr.count, ARCH_CONVERT)] <=
+ (char *)XFS_DIR2_LEAF_BESTS_P_ARCH(ltp, ARCH_CONVERT));
+ /*
+ * Check hash value order, count stale entries.
+ */
+ for (i = stale = 0; i < INT_GET(leaf->hdr.count, ARCH_CONVERT); i++) {
+ if (i + 1 < INT_GET(leaf->hdr.count, ARCH_CONVERT))
+ ASSERT(INT_GET(leaf->ents[i].hashval, ARCH_CONVERT) <=
+ INT_GET(leaf->ents[i + 1].hashval, ARCH_CONVERT));
+ if (INT_GET(leaf->ents[i].address, ARCH_CONVERT) == XFS_DIR2_NULL_DATAPTR)
+ stale++;
+ }
+ ASSERT(INT_GET(leaf->hdr.stale, ARCH_CONVERT) == stale);
+}
+#endif /* DEBUG */
+
+/*
+ * Compact out any stale entries in the leaf.
+ * Log the header and changed leaf entries, if any.
+ */
+void
+xfs_dir2_leaf_compact(
+ xfs_da_args_t *args, /* operation arguments */
+ xfs_dabuf_t *bp) /* leaf buffer */
+{
+ int from; /* source leaf index */
+ xfs_dir2_leaf_t *leaf; /* leaf structure */
+ int loglow; /* first leaf entry to log */
+ int to; /* target leaf index */
+
+ leaf = bp->data;
+ if (INT_ISZERO(leaf->hdr.stale, ARCH_CONVERT)) {
+ return;
+ }
+ /*
+ * Compress out the stale entries in place.
+ */
+ for (from = to = 0, loglow = -1; from < INT_GET(leaf->hdr.count, ARCH_CONVERT); from++) {
+ if (INT_GET(leaf->ents[from].address, ARCH_CONVERT) == XFS_DIR2_NULL_DATAPTR)
+ continue;
+ /*
+ * Only actually copy the entries that are different.
+ */
+ if (from > to) {
+ if (loglow == -1)
+ loglow = to;
+ leaf->ents[to] = leaf->ents[from];
+ }
+ to++;
+ }
+ /*
+ * Update and log the header, log the leaf entries.
+ */
+ ASSERT(INT_GET(leaf->hdr.stale, ARCH_CONVERT) == from - to);
+ INT_MOD(leaf->hdr.count, ARCH_CONVERT, -(INT_GET(leaf->hdr.stale, ARCH_CONVERT)));
+ INT_ZERO(leaf->hdr.stale, ARCH_CONVERT);
+ xfs_dir2_leaf_log_header(args->trans, bp);
+ if (loglow != -1)
+ xfs_dir2_leaf_log_ents(args->trans, bp, loglow, to - 1);
+}
+
+/*
+ * Compact the leaf entries, removing stale ones.
+ * Leave one stale entry behind - the one closest to our
+ * insertion index - and the caller will shift that one to our insertion
+ * point later.
+ * Return new insertion index, where the remaining stale entry is,
+ * and leaf logging indices.
+ */
+void
+xfs_dir2_leaf_compact_x1(
+ xfs_dabuf_t *bp, /* leaf buffer */
+ int *indexp, /* insertion index */
+ int *lowstalep, /* out: stale entry before us */
+ int *highstalep, /* out: stale entry after us */
+ int *lowlogp, /* out: low log index */
+ int *highlogp) /* out: high log index */
+{
+ int from; /* source copy index */
+ int highstale; /* stale entry at/after index */
+ int index; /* insertion index */
+ int keepstale; /* source index of kept stale */
+ xfs_dir2_leaf_t *leaf; /* leaf structure */
+ int lowstale; /* stale entry before index */
+ int newindex=0; /* new insertion index */
+ int to; /* destination copy index */
+
+ leaf = bp->data;
+ ASSERT(INT_GET(leaf->hdr.stale, ARCH_CONVERT) > 1);
+ index = *indexp;
+ /*
+ * Find the first stale entry before our index, if any.
+ */
+ for (lowstale = index - 1;
+ lowstale >= 0 &&
+ INT_GET(leaf->ents[lowstale].address, ARCH_CONVERT) != XFS_DIR2_NULL_DATAPTR;
+ lowstale--)
+ continue;
+ /*
+ * Find the first stale entry at or after our index, if any.
+ * Stop if the answer would be worse than lowstale.
+ */
+ for (highstale = index;
+ highstale < INT_GET(leaf->hdr.count, ARCH_CONVERT) &&
+ INT_GET(leaf->ents[highstale].address, ARCH_CONVERT) != XFS_DIR2_NULL_DATAPTR &&
+ (lowstale < 0 || index - lowstale > highstale - index);
+ highstale++)
+ continue;
+ /*
+ * Pick the better of lowstale and highstale.
+ */
+ if (lowstale >= 0 &&
+ (highstale == INT_GET(leaf->hdr.count, ARCH_CONVERT) ||
+ index - lowstale <= highstale - index))
+ keepstale = lowstale;
+ else
+ keepstale = highstale;
+ /*
+ * Copy the entries in place, removing all the stale entries
+ * except keepstale.
+ */
+ for (from = to = 0; from < INT_GET(leaf->hdr.count, ARCH_CONVERT); from++) {
+ /*
+ * Notice the new value of index.
+ */
+ if (index == from)
+ newindex = to;
+ if (from != keepstale &&
+ INT_GET(leaf->ents[from].address, ARCH_CONVERT) == XFS_DIR2_NULL_DATAPTR) {
+ if (from == to)
+ *lowlogp = to;
+ continue;
+ }
+ /*
+ * Record the new keepstale value for the insertion.
+ */
+ if (from == keepstale)
+ lowstale = highstale = to;
+ /*
+ * Copy only the entries that have moved.
+ */
+ if (from > to)
+ leaf->ents[to] = leaf->ents[from];
+ to++;
+ }
+ ASSERT(from > to);
+ /*
+ * If the insertion point was past the last entry,
+ * set the new insertion point accordingly.
+ */
+ if (index == from)
+ newindex = to;
+ *indexp = newindex;
+ /*
+ * Adjust the leaf header values.
+ */
+ INT_MOD(leaf->hdr.count, ARCH_CONVERT, -(from - to));
+ INT_SET(leaf->hdr.stale, ARCH_CONVERT, 1);
+ /*
+ * Remember the low/high stale value only in the "right"
+ * direction.
+ */
+ if (lowstale >= newindex)
+ lowstale = -1;
+ else
+ highstale = INT_GET(leaf->hdr.count, ARCH_CONVERT);
+ *highlogp = INT_GET(leaf->hdr.count, ARCH_CONVERT) - 1;
+ *lowstalep = lowstale;
+ *highstalep = highstale;
+}
+
+/*
+ * Getdents (readdir) for leaf and node directories.
+ * This reads the data blocks only, so is the same for both forms.
+ */
+int /* error */
+xfs_dir2_leaf_getdents(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_inode_t *dp, /* incore directory inode */
+ uio_t *uio, /* I/O control & vectors */
+ int *eofp, /* out: reached end of dir */
+ xfs_dirent_t *dbp, /* caller's buffer */
+ xfs_dir2_put_t put) /* ABI formatting routine */
+{
+ xfs_dabuf_t *bp; /* data block buffer */
+ int byteoff; /* offset in current block */
+ xfs_dir2_db_t curdb; /* db for current block */
+ xfs_dir2_off_t curoff; /* current overall offset */
+ xfs_dir2_data_t *data; /* data block structure */
+ xfs_dir2_data_entry_t *dep; /* data entry */
+ xfs_dir2_data_unused_t *dup; /* unused entry */
+ int eof; /* reached end of directory */
+ int error=0; /* error return value */
+ int i; /* temporary loop index */
+ int j; /* temporary loop index */
+ int length; /* temporary length value */
+ xfs_bmbt_irec_t *map; /* map vector for blocks */
+ xfs_extlen_t map_blocks; /* number of fsbs in map */
+ xfs_dablk_t map_off; /* last mapped file offset */
+ int map_size; /* total entries in *map */
+ int map_valid; /* valid entries in *map */
+ xfs_mount_t *mp; /* filesystem mount point */
+ xfs_dir2_off_t newoff; /* new curoff after new blk */
+ int nmap; /* mappings to ask xfs_bmapi */
+ xfs_dir2_put_args_t p; /* formatting arg bundle */
+ char *ptr=NULL; /* pointer to current data */
+ int ra_current; /* number of read-ahead blks */
+ int ra_index; /* *map index for read-ahead */
+ int ra_offset; /* map entry offset for ra */
+ int ra_want; /* readahead count wanted */
+
+ /*
+ * If the offset is at or past the largest allowed value,
+ * give up right away, return eof.
+ */
+ if (uio->uio_offset >= XFS_DIR2_MAX_DATAPTR) {
+ *eofp = 1;
+ return 0;
+ }
+ mp = dp->i_mount;
+ /*
+ * Setup formatting arguments.
+ */
+ p.dbp = dbp;
+ p.put = put;
+ p.uio = uio;
+ /*
+ * Set up to bmap a number of blocks based on the caller's
+ * buffer size, the directory block size, and the filesystem
+ * block size.
+ */
+ map_size =
+ howmany(uio->uio_resid + mp->m_dirblksize,
+ mp->m_sb.sb_blocksize);
+ map = kmem_alloc(map_size * sizeof(*map), KM_SLEEP);
+ map_valid = ra_index = ra_offset = ra_current = map_blocks = 0;
+ bp = NULL;
+ eof = 1;
+ /*
+ * Inside the loop we keep the main offset value as a byte offset
+ * in the directory file.
+ */
+ curoff = XFS_DIR2_DATAPTR_TO_BYTE(mp, uio->uio_offset);
+ /*
+ * Force this conversion through db so we truncate the offset
+ * down to get the start of the data block.
+ */
+ map_off = XFS_DIR2_DB_TO_DA(mp, XFS_DIR2_BYTE_TO_DB(mp, curoff));
+ /*
+ * Loop over directory entries until we reach the end offset.
+ * Get more blocks and readahead as necessary.
+ */
+ while (curoff < XFS_DIR2_LEAF_OFFSET) {
+ /*
+ * If we have no buffer, or we're off the end of the
+ * current buffer, need to get another one.
+ */
+ if (!bp || ptr >= (char *)bp->data + mp->m_dirblksize) {
+ /*
+ * If we have a buffer, we need to release it and
+ * take it out of the mapping.
+ */
+ if (bp) {
+ xfs_da_brelse(tp, bp);
+ bp = NULL;
+ map_blocks -= mp->m_dirblkfsbs;
+ /*
+ * Loop to get rid of the extents for the
+ * directory block.
+ */
+ for (i = mp->m_dirblkfsbs; i > 0; ) {
+ j = MIN((int)map->br_blockcount, i);
+ map->br_blockcount -= j;
+ map->br_startblock += j;
+ map->br_startoff += j;
+ /*
+ * If mapping is done, pitch it from
+ * the table.
+ */
+ if (!map->br_blockcount && --map_valid)
+ memmove(&map[0], &map[1],
+ sizeof(map[0]) *
+ map_valid);
+ i -= j;
+ }
+ }
+ /*
+ * Recalculate the readahead blocks wanted.
+ */
+ ra_want = howmany(uio->uio_resid + mp->m_dirblksize,
+ mp->m_sb.sb_blocksize) - 1;
+ /*
+ * If we don't have as many as we want, and we haven't
+ * run out of data blocks, get some more mappings.
+ */
+ if (1 + ra_want > map_blocks &&
+ map_off <
+ XFS_DIR2_BYTE_TO_DA(mp, XFS_DIR2_LEAF_OFFSET)) {
+ /*
+ * Get more bmaps, fill in after the ones
+ * we already have in the table.
+ */
+ nmap = map_size - map_valid;
+ error = xfs_bmapi(tp, dp,
+ map_off,
+ XFS_DIR2_BYTE_TO_DA(mp,
+ XFS_DIR2_LEAF_OFFSET) - map_off,
+ XFS_BMAPI_METADATA, NULL, 0,
+ &map[map_valid], &nmap, NULL);
+ /*
+ * Don't know if we should ignore this or
+ * try to return an error.
+ * The trouble with returning errors
+ * is that readdir will just stop without
+ * actually passing the error through.
+ */
+ if (error)
+ break; /* XXX */
+ /*
+ * If we got all the mappings we asked for,
+ * set the final map offset based on the
+ * last bmap value received.
+ * Otherwise, we've reached the end.
+ */
+ if (nmap == map_size - map_valid)
+ map_off =
+ map[map_valid + nmap - 1].br_startoff +
+ map[map_valid + nmap - 1].br_blockcount;
+ else
+ map_off =
+ XFS_DIR2_BYTE_TO_DA(mp,
+ XFS_DIR2_LEAF_OFFSET);
+ /*
+ * Look for holes in the mapping, and
+ * eliminate them. Count up the valid blocks.
+ */
+ for (i = map_valid; i < map_valid + nmap; ) {
+ if (map[i].br_startblock ==
+ HOLESTARTBLOCK) {
+ nmap--;
+ length = map_valid + nmap - i;
+ if (length)
+ memmove(&map[i],
+ &map[i + 1],
+ sizeof(map[i]) *
+ length);
+ } else {
+ map_blocks +=
+ map[i].br_blockcount;
+ i++;
+ }
+ }
+ map_valid += nmap;
+ }
+ /*
+ * No valid mappings, so no more data blocks.
+ */
+ if (!map_valid) {
+ curoff = XFS_DIR2_DA_TO_BYTE(mp, map_off);
+ break;
+ }
+ /*
+ * Read the directory block starting at the first
+ * mapping.
+ */
+ curdb = XFS_DIR2_DA_TO_DB(mp, map->br_startoff);
+ error = xfs_da_read_buf(tp, dp, map->br_startoff,
+ map->br_blockcount >= mp->m_dirblkfsbs ?
+ XFS_FSB_TO_DADDR(mp, map->br_startblock) :
+ -1,
+ &bp, XFS_DATA_FORK);
+ /*
+ * Should just skip over the data block instead
+ * of giving up.
+ */
+ if (error)
+ break; /* XXX */
+ /*
+ * Adjust the current amount of read-ahead: we just
+ * read a block that was previously ra.
+ */
+ if (ra_current)
+ ra_current -= mp->m_dirblkfsbs;
+ /*
+ * Do we need more readahead?
+ */
+ for (ra_index = ra_offset = i = 0;
+ ra_want > ra_current && i < map_blocks;
+ i += mp->m_dirblkfsbs) {
+ ASSERT(ra_index < map_valid);
+ /*
+ * Read-ahead a contiguous directory block.
+ */
+ if (i > ra_current &&
+ map[ra_index].br_blockcount >=
+ mp->m_dirblkfsbs) {
+ xfs_baread(mp->m_ddev_targp,
+ XFS_FSB_TO_DADDR(mp,
+ map[ra_index].br_startblock +
+ ra_offset),
+ (int)BTOBB(mp->m_dirblksize));
+ ra_current = i;
+ }
+ /*
+ * Read-ahead a non-contiguous directory block.
+ * This doesn't use our mapping, but this
+ * is a very rare case.
+ */
+ else if (i > ra_current) {
+ (void)xfs_da_reada_buf(tp, dp,
+ map[ra_index].br_startoff +
+ ra_offset, XFS_DATA_FORK);
+ ra_current = i;
+ }
+ /*
+ * Advance offset through the mapping table.
+ */
+ for (j = 0; j < mp->m_dirblkfsbs; j++) {
+ /*
+ * The rest of this extent but not
+ * more than a dir block.
+ */
+ length = MIN(mp->m_dirblkfsbs,
+ (int)(map[ra_index].br_blockcount -
+ ra_offset));
+ j += length;
+ ra_offset += length;
+ /*
+ * Advance to the next mapping if
+ * this one is used up.
+ */
+ if (ra_offset ==
+ map[ra_index].br_blockcount) {
+ ra_offset = 0;
+ ra_index++;
+ }
+ }
+ }
+ /*
+ * Having done a read, we need to set a new offset.
+ */
+ newoff = XFS_DIR2_DB_OFF_TO_BYTE(mp, curdb, 0);
+ /*
+ * Start of the current block.
+ */
+ if (curoff < newoff)
+ curoff = newoff;
+ /*
+ * Make sure we're in the right block.
+ */
+ else if (curoff > newoff)
+ ASSERT(XFS_DIR2_BYTE_TO_DB(mp, curoff) ==
+ curdb);
+ data = bp->data;
+ xfs_dir2_data_check(dp, bp);
+ /*
+ * Find our position in the block.
+ */
+ ptr = (char *)&data->u;
+ byteoff = XFS_DIR2_BYTE_TO_OFF(mp, curoff);
+ /*
+ * Skip past the header.
+ */
+ if (byteoff == 0)
+ curoff += (uint)sizeof(data->hdr);
+ /*
+ * Skip past entries until we reach our offset.
+ */
+ else {
+ while ((char *)ptr - (char *)data < byteoff) {
+ dup = (xfs_dir2_data_unused_t *)ptr;
+
+ if (INT_GET(dup->freetag, ARCH_CONVERT)
+ == XFS_DIR2_DATA_FREE_TAG) {
+
+ length = INT_GET(dup->length,
+ ARCH_CONVERT);
+ ptr += length;
+ continue;
+ }
+ dep = (xfs_dir2_data_entry_t *)ptr;
+ length =
+ XFS_DIR2_DATA_ENTSIZE(dep->namelen);
+ ptr += length;
+ }
+ /*
+ * Now set our real offset.
+ */
+ curoff =
+ XFS_DIR2_DB_OFF_TO_BYTE(mp,
+ XFS_DIR2_BYTE_TO_DB(mp, curoff),
+ (char *)ptr - (char *)data);
+ if (ptr >= (char *)data + mp->m_dirblksize) {
+ continue;
+ }
+ }
+ }
+ /*
+ * We have a pointer to an entry.
+ * Is it a live one?
+ */
+ dup = (xfs_dir2_data_unused_t *)ptr;
+ /*
+ * No, it's unused, skip over it.
+ */
+ if (INT_GET(dup->freetag, ARCH_CONVERT)
+ == XFS_DIR2_DATA_FREE_TAG) {
+ length = INT_GET(dup->length, ARCH_CONVERT);
+ ptr += length;
+ curoff += length;
+ continue;
+ }
+
+ /*
+ * Copy the entry into the putargs, and try formatting it.
+ */
+ dep = (xfs_dir2_data_entry_t *)ptr;
+
+ p.namelen = dep->namelen;
+
+ length = XFS_DIR2_DATA_ENTSIZE(p.namelen);
+
+ p.cook = XFS_DIR2_BYTE_TO_DATAPTR(mp, curoff + length);
+
+ p.ino = INT_GET(dep->inumber, ARCH_CONVERT);
+#if XFS_BIG_INUMS
+ p.ino += mp->m_inoadd;
+#endif
+ p.name = (char *)dep->name;
+
+ error = p.put(&p);
+
+ /*
+ * Won't fit. Return to caller.
+ */
+ if (!p.done) {
+ eof = 0;
+ break;
+ }
+ /*
+ * Advance to next entry in the block.
+ */
+ ptr += length;
+ curoff += length;
+ }
+
+ /*
+ * All done. Set output offset value to current offset.
+ */
+ *eofp = eof;
+ if (curoff > XFS_DIR2_DATAPTR_TO_BYTE(mp, XFS_DIR2_MAX_DATAPTR))
+ uio->uio_offset = XFS_DIR2_MAX_DATAPTR;
+ else
+ uio->uio_offset = XFS_DIR2_BYTE_TO_DATAPTR(mp, curoff);
+ kmem_free(map, map_size * sizeof(*map));
+ if (bp)
+ xfs_da_brelse(tp, bp);
+ return error;
+}
+
+/*
+ * Initialize a new leaf block, leaf1 or leafn magic accepted.
+ */
+int
+xfs_dir2_leaf_init(
+ xfs_da_args_t *args, /* operation arguments */
+ xfs_dir2_db_t bno, /* directory block number */
+ xfs_dabuf_t **bpp, /* out: leaf buffer */
+ int magic) /* magic number for block */
+{
+ xfs_dabuf_t *bp; /* leaf buffer */
+ xfs_inode_t *dp; /* incore directory inode */
+ int error; /* error return code */
+ xfs_dir2_leaf_t *leaf; /* leaf structure */
+ xfs_dir2_leaf_tail_t *ltp; /* leaf tail structure */
+ xfs_mount_t *mp; /* filesystem mount point */
+ xfs_trans_t *tp; /* transaction pointer */
+
+ dp = args->dp;
+ ASSERT(dp != NULL);
+ tp = args->trans;
+ mp = dp->i_mount;
+ ASSERT(bno >= XFS_DIR2_LEAF_FIRSTDB(mp) &&
+ bno < XFS_DIR2_FREE_FIRSTDB(mp));
+ /*
+ * Get the buffer for the block.
+ */
+ error = xfs_da_get_buf(tp, dp, XFS_DIR2_DB_TO_DA(mp, bno), -1, &bp,
+ XFS_DATA_FORK);
+ if (error) {
+ return error;
+ }
+ ASSERT(bp != NULL);
+ leaf = bp->data;
+ /*
+ * Initialize the header.
+ */
+ INT_SET(leaf->hdr.info.magic, ARCH_CONVERT, magic);
+ INT_ZERO(leaf->hdr.info.forw, ARCH_CONVERT);
+ INT_ZERO(leaf->hdr.info.back, ARCH_CONVERT);
+ INT_ZERO(leaf->hdr.count, ARCH_CONVERT);
+ INT_ZERO(leaf->hdr.stale, ARCH_CONVERT);
+ xfs_dir2_leaf_log_header(tp, bp);
+ /*
+ * If it's a leaf-format directory initialize the tail.
+ * In this case our caller has the real bests table to copy into
+ * the block.
+ */
+ if (magic == XFS_DIR2_LEAF1_MAGIC) {
+ ltp = XFS_DIR2_LEAF_TAIL_P(mp, leaf);
+ INT_ZERO(ltp->bestcount, ARCH_CONVERT);
+ xfs_dir2_leaf_log_tail(tp, bp);
+ }
+ *bpp = bp;
+ return 0;
+}
+
+/*
+ * Log the bests entries indicated from a leaf1 block.
+ */
+void
+xfs_dir2_leaf_log_bests(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_dabuf_t *bp, /* leaf buffer */
+ int first, /* first entry to log */
+ int last) /* last entry to log */
+{
+ xfs_dir2_data_off_t *firstb; /* pointer to first entry */
+ xfs_dir2_data_off_t *lastb; /* pointer to last entry */
+ xfs_dir2_leaf_t *leaf; /* leaf structure */
+ xfs_dir2_leaf_tail_t *ltp; /* leaf tail structure */
+
+ leaf = bp->data;
+ ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) == XFS_DIR2_LEAF1_MAGIC);
+ ltp = XFS_DIR2_LEAF_TAIL_P(tp->t_mountp, leaf);
+ firstb = XFS_DIR2_LEAF_BESTS_P_ARCH(ltp, ARCH_CONVERT) + first;
+ lastb = XFS_DIR2_LEAF_BESTS_P_ARCH(ltp, ARCH_CONVERT) + last;
+ xfs_da_log_buf(tp, bp, (uint)((char *)firstb - (char *)leaf),
+ (uint)((char *)lastb - (char *)leaf + sizeof(*lastb) - 1));
+}
+
+/*
+ * Log the leaf entries indicated from a leaf1 or leafn block.
+ */
+void
+xfs_dir2_leaf_log_ents(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_dabuf_t *bp, /* leaf buffer */
+ int first, /* first entry to log */
+ int last) /* last entry to log */
+{
+ xfs_dir2_leaf_entry_t *firstlep; /* pointer to first entry */
+ xfs_dir2_leaf_entry_t *lastlep; /* pointer to last entry */
+ xfs_dir2_leaf_t *leaf; /* leaf structure */
+
+ leaf = bp->data;
+ ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) == XFS_DIR2_LEAF1_MAGIC ||
+ INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) == XFS_DIR2_LEAFN_MAGIC);
+ firstlep = &leaf->ents[first];
+ lastlep = &leaf->ents[last];
+ xfs_da_log_buf(tp, bp, (uint)((char *)firstlep - (char *)leaf),
+ (uint)((char *)lastlep - (char *)leaf + sizeof(*lastlep) - 1));
+}
+
+/*
+ * Log the header of the leaf1 or leafn block.
+ */
+void
+xfs_dir2_leaf_log_header(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_dabuf_t *bp) /* leaf buffer */
+{
+ xfs_dir2_leaf_t *leaf; /* leaf structure */
+
+ leaf = bp->data;
+ ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) == XFS_DIR2_LEAF1_MAGIC ||
+ INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) == XFS_DIR2_LEAFN_MAGIC);
+ xfs_da_log_buf(tp, bp, (uint)((char *)&leaf->hdr - (char *)leaf),
+ (uint)(sizeof(leaf->hdr) - 1));
+}
+
+/*
+ * Log the tail of the leaf1 block.
+ */
+void
+xfs_dir2_leaf_log_tail(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_dabuf_t *bp) /* leaf buffer */
+{
+ xfs_dir2_leaf_t *leaf; /* leaf structure */
+ xfs_dir2_leaf_tail_t *ltp; /* leaf tail structure */
+ xfs_mount_t *mp; /* filesystem mount point */
+
+ mp = tp->t_mountp;
+ leaf = bp->data;
+ ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) == XFS_DIR2_LEAF1_MAGIC);
+ ltp = XFS_DIR2_LEAF_TAIL_P(mp, leaf);
+ xfs_da_log_buf(tp, bp, (uint)((char *)ltp - (char *)leaf),
+ (uint)(mp->m_dirblksize - 1));
+}
+
+/*
+ * Look up the entry referred to by args in the leaf format directory.
+ * Most of the work is done by the xfs_dir2_leaf_lookup_int routine which
+ * is also used by the node-format code.
+ */
+int
+xfs_dir2_leaf_lookup(
+ xfs_da_args_t *args) /* operation arguments */
+{
+ xfs_dabuf_t *dbp; /* data block buffer */
+ xfs_dir2_data_entry_t *dep; /* data block entry */
+ xfs_inode_t *dp; /* incore directory inode */
+ int error; /* error return code */
+ int index; /* found entry index */
+ xfs_dabuf_t *lbp; /* leaf buffer */
+ xfs_dir2_leaf_t *leaf; /* leaf structure */
+ xfs_dir2_leaf_entry_t *lep; /* leaf entry */
+ xfs_trans_t *tp; /* transaction pointer */
+
+ xfs_dir2_trace_args("leaf_lookup", args);
+ /*
+ * Look up name in the leaf block, returning both buffers and index.
+ */
+ if ((error = xfs_dir2_leaf_lookup_int(args, &lbp, &index, &dbp))) {
+ return error;
+ }
+ tp = args->trans;
+ dp = args->dp;
+ xfs_dir2_leaf_check(dp, lbp);
+ leaf = lbp->data;
+ /*
+ * Get to the leaf entry and contained data entry address.
+ */
+ lep = &leaf->ents[index];
+ /*
+ * Point to the data entry.
+ */
+ dep = (xfs_dir2_data_entry_t *)
+ ((char *)dbp->data +
+ XFS_DIR2_DATAPTR_TO_OFF(dp->i_mount, INT_GET(lep->address, ARCH_CONVERT)));
+ /*
+ * Return the found inode number.
+ */
+ args->inumber = INT_GET(dep->inumber, ARCH_CONVERT);
+ xfs_da_brelse(tp, dbp);
+ xfs_da_brelse(tp, lbp);
+ return XFS_ERROR(EEXIST);
+}
+
+/*
+ * Look up name/hash in the leaf block.
+ * Fill in indexp with the found index, and dbpp with the data buffer.
+ * If not found dbpp will be NULL, and ENOENT comes back.
+ * lbpp will always be filled in with the leaf buffer unless there's an error.
+ */
+static int /* error */
+xfs_dir2_leaf_lookup_int(
+ xfs_da_args_t *args, /* operation arguments */
+ xfs_dabuf_t **lbpp, /* out: leaf buffer */
+ int *indexp, /* out: index in leaf block */
+ xfs_dabuf_t **dbpp) /* out: data buffer */
+{
+ xfs_dir2_db_t curdb; /* current data block number */
+ xfs_dabuf_t *dbp; /* data buffer */
+ xfs_dir2_data_entry_t *dep; /* data entry */
+ xfs_inode_t *dp; /* incore directory inode */
+ int error; /* error return code */
+ int index; /* index in leaf block */
+ xfs_dabuf_t *lbp; /* leaf buffer */
+ xfs_dir2_leaf_entry_t *lep; /* leaf entry */
+ xfs_dir2_leaf_t *leaf; /* leaf structure */
+ xfs_mount_t *mp; /* filesystem mount point */
+ xfs_dir2_db_t newdb; /* new data block number */
+ xfs_trans_t *tp; /* transaction pointer */
+
+ dp = args->dp;
+ tp = args->trans;
+ mp = dp->i_mount;
+ /*
+ * Read the leaf block into the buffer.
+ */
+ if ((error =
+ xfs_da_read_buf(tp, dp, mp->m_dirleafblk, -1, &lbp,
+ XFS_DATA_FORK))) {
+ return error;
+ }
+ *lbpp = lbp;
+ leaf = lbp->data;
+ xfs_dir2_leaf_check(dp, lbp);
+ /*
+ * Look for the first leaf entry with our hash value.
+ */
+ index = xfs_dir2_leaf_search_hash(args, lbp);
+ /*
+ * Loop over all the entries with the right hash value
+ * looking to match the name.
+ */
+ for (lep = &leaf->ents[index], dbp = NULL, curdb = -1;
+ index < INT_GET(leaf->hdr.count, ARCH_CONVERT) && INT_GET(lep->hashval, ARCH_CONVERT) == args->hashval;
+ lep++, index++) {
+ /*
+ * Skip over stale leaf entries.
+ */
+ if (INT_GET(lep->address, ARCH_CONVERT) == XFS_DIR2_NULL_DATAPTR)
+ continue;
+ /*
+ * Get the new data block number.
+ */
+ newdb = XFS_DIR2_DATAPTR_TO_DB(mp, INT_GET(lep->address, ARCH_CONVERT));
+ /*
+ * If it's not the same as the old data block number,
+ * need to pitch the old one and read the new one.
+ */
+ if (newdb != curdb) {
+ if (dbp)
+ xfs_da_brelse(tp, dbp);
+ if ((error =
+ xfs_da_read_buf(tp, dp,
+ XFS_DIR2_DB_TO_DA(mp, newdb), -1, &dbp,
+ XFS_DATA_FORK))) {
+ xfs_da_brelse(tp, lbp);
+ return error;
+ }
+ xfs_dir2_data_check(dp, dbp);
+ curdb = newdb;
+ }
+ /*
+ * Point to the data entry.
+ */
+ dep = (xfs_dir2_data_entry_t *)
+ ((char *)dbp->data +
+ XFS_DIR2_DATAPTR_TO_OFF(mp, INT_GET(lep->address, ARCH_CONVERT)));
+ /*
+ * If it matches then return it.
+ */
+ if (dep->namelen == args->namelen &&
+ dep->name[0] == args->name[0] &&
+ memcmp(dep->name, args->name, args->namelen) == 0) {
+ *dbpp = dbp;
+ *indexp = index;
+ return 0;
+ }
+ }
+ /*
+ * No match found, return ENOENT.
+ */
+ ASSERT(args->oknoent);
+ if (dbp)
+ xfs_da_brelse(tp, dbp);
+ xfs_da_brelse(tp, lbp);
+ return XFS_ERROR(ENOENT);
+}
+
+/*
+ * Remove an entry from a leaf format directory.
+ */
+int /* error */
+xfs_dir2_leaf_removename(
+ xfs_da_args_t *args) /* operation arguments */
+{
+ xfs_dir2_data_off_t *bestsp; /* leaf block best freespace */
+ xfs_dir2_data_t *data; /* data block structure */
+ xfs_dir2_db_t db; /* data block number */
+ xfs_dabuf_t *dbp; /* data block buffer */
+ xfs_dir2_data_entry_t *dep; /* data entry structure */
+ xfs_inode_t *dp; /* incore directory inode */
+ int error; /* error return code */
+ xfs_dir2_db_t i; /* temporary data block # */
+ int index; /* index into leaf entries */
+ xfs_dabuf_t *lbp; /* leaf buffer */
+ xfs_dir2_leaf_t *leaf; /* leaf structure */
+ xfs_dir2_leaf_entry_t *lep; /* leaf entry */
+ xfs_dir2_leaf_tail_t *ltp; /* leaf tail structure */
+ xfs_mount_t *mp; /* filesystem mount point */
+ int needlog; /* need to log data header */
+ int needscan; /* need to rescan data frees */
+ xfs_dir2_data_off_t oldbest; /* old value of best free */
+ xfs_trans_t *tp; /* transaction pointer */
+
+ xfs_dir2_trace_args("leaf_removename", args);
+ /*
+ * Lookup the leaf entry, get the leaf and data blocks read in.
+ */
+ if ((error = xfs_dir2_leaf_lookup_int(args, &lbp, &index, &dbp))) {
+ return error;
+ }
+ dp = args->dp;
+ tp = args->trans;
+ mp = dp->i_mount;
+ leaf = lbp->data;
+ data = dbp->data;
+ xfs_dir2_data_check(dp, dbp);
+ /*
+ * Point to the leaf entry, use that to point to the data entry.
+ */
+ lep = &leaf->ents[index];
+ db = XFS_DIR2_DATAPTR_TO_DB(mp, INT_GET(lep->address, ARCH_CONVERT));
+ dep = (xfs_dir2_data_entry_t *)
+ ((char *)data + XFS_DIR2_DATAPTR_TO_OFF(mp, INT_GET(lep->address, ARCH_CONVERT)));
+ needscan = needlog = 0;
+ oldbest = INT_GET(data->hdr.bestfree[0].length, ARCH_CONVERT);
+ ltp = XFS_DIR2_LEAF_TAIL_P(mp, leaf);
+ bestsp = XFS_DIR2_LEAF_BESTS_P_ARCH(ltp, ARCH_CONVERT);
+ ASSERT(INT_GET(bestsp[db], ARCH_CONVERT) == oldbest);
+ /*
+ * Mark the former data entry unused.
+ */
+ xfs_dir2_data_make_free(tp, dbp,
+ (xfs_dir2_data_aoff_t)((char *)dep - (char *)data),
+ XFS_DIR2_DATA_ENTSIZE(dep->namelen), &needlog, &needscan);
+ /*
+ * We just mark the leaf entry stale by putting a null in it.
+ */
+ INT_MOD(leaf->hdr.stale, ARCH_CONVERT, +1);
+ xfs_dir2_leaf_log_header(tp, lbp);
+ INT_SET(lep->address, ARCH_CONVERT, XFS_DIR2_NULL_DATAPTR);
+ xfs_dir2_leaf_log_ents(tp, lbp, index, index);
+ /*
+ * Scan the freespace in the data block again if necessary,
+ * log the data block header if necessary.
+ */
+ if (needscan)
+ xfs_dir2_data_freescan(mp, data, &needlog, NULL);
+ if (needlog)
+ xfs_dir2_data_log_header(tp, dbp);
+ /*
+ * If the longest freespace in the data block has changed,
+ * put the new value in the bests table and log that.
+ */
+ if (INT_GET(data->hdr.bestfree[0].length, ARCH_CONVERT) != oldbest) {
+ INT_COPY(bestsp[db], data->hdr.bestfree[0].length, ARCH_CONVERT);
+ xfs_dir2_leaf_log_bests(tp, lbp, db, db);
+ }
+ xfs_dir2_data_check(dp, dbp);
+ /*
+ * If the data block is now empty then get rid of the data block.
+ */
+ if (INT_GET(data->hdr.bestfree[0].length, ARCH_CONVERT) ==
+ mp->m_dirblksize - (uint)sizeof(data->hdr)) {
+ ASSERT(db != mp->m_dirdatablk);
+ if ((error = xfs_dir2_shrink_inode(args, db, dbp))) {
+ /*
+ * Nope, can't get rid of it because it caused
+ * allocation of a bmap btree block to do so.
+ * Just go on, returning success, leaving the
+ * empty block in place.
+ */
+ if (error == ENOSPC && args->total == 0) {
+ xfs_da_buf_done(dbp);
+ error = 0;
+ }
+ xfs_dir2_leaf_check(dp, lbp);
+ xfs_da_buf_done(lbp);
+ return error;
+ }
+ dbp = NULL;
+ /*
+ * If this is the last data block then compact the
+ * bests table by getting rid of entries.
+ */
+ if (db == INT_GET(ltp->bestcount, ARCH_CONVERT) - 1) {
+ /*
+ * Look for the last active entry (i).
+ */
+ for (i = db - 1; i > 0; i--) {
+ if (INT_GET(bestsp[i], ARCH_CONVERT) != NULLDATAOFF)
+ break;
+ }
+ /*
+ * Copy the table down so inactive entries at the
+ * end are removed.
+ */
+ memmove(&bestsp[db - i], bestsp,
+ (INT_GET(ltp->bestcount, ARCH_CONVERT) - (db - i)) * sizeof(*bestsp));
+ INT_MOD(ltp->bestcount, ARCH_CONVERT, -(db - i));
+ xfs_dir2_leaf_log_tail(tp, lbp);
+ xfs_dir2_leaf_log_bests(tp, lbp, 0, INT_GET(ltp->bestcount, ARCH_CONVERT) - 1);
+ } else
+ INT_SET(bestsp[db], ARCH_CONVERT, NULLDATAOFF);
+ }
+ /*
+ * If the data block was not the first one, drop it.
+ */
+ else if (db != mp->m_dirdatablk && dbp != NULL) {
+ xfs_da_buf_done(dbp);
+ dbp = NULL;
+ }
+ xfs_dir2_leaf_check(dp, lbp);
+ /*
+ * See if we can convert to block form.
+ */
+ return xfs_dir2_leaf_to_block(args, lbp, dbp);
+}
+
+/*
+ * Replace the inode number in a leaf format directory entry.
+ */
+int /* error */
+xfs_dir2_leaf_replace(
+ xfs_da_args_t *args) /* operation arguments */
+{
+ xfs_dabuf_t *dbp; /* data block buffer */
+ xfs_dir2_data_entry_t *dep; /* data block entry */
+ xfs_inode_t *dp; /* incore directory inode */
+ int error; /* error return code */
+ int index; /* index of leaf entry */
+ xfs_dabuf_t *lbp; /* leaf buffer */
+ xfs_dir2_leaf_t *leaf; /* leaf structure */
+ xfs_dir2_leaf_entry_t *lep; /* leaf entry */
+ xfs_trans_t *tp; /* transaction pointer */
+
+ xfs_dir2_trace_args("leaf_replace", args);
+ /*
+ * Look up the entry.
+ */
+ if ((error = xfs_dir2_leaf_lookup_int(args, &lbp, &index, &dbp))) {
+ return error;
+ }
+ dp = args->dp;
+ leaf = lbp->data;
+ /*
+ * Point to the leaf entry, get data address from it.
+ */
+ lep = &leaf->ents[index];
+ /*
+ * Point to the data entry.
+ */
+ dep = (xfs_dir2_data_entry_t *)
+ ((char *)dbp->data +
+ XFS_DIR2_DATAPTR_TO_OFF(dp->i_mount, INT_GET(lep->address, ARCH_CONVERT)));
+ ASSERT(args->inumber != INT_GET(dep->inumber, ARCH_CONVERT));
+ /*
+ * Put the new inode number in, log it.
+ */
+ INT_SET(dep->inumber, ARCH_CONVERT, args->inumber);
+ tp = args->trans;
+ xfs_dir2_data_log_entry(tp, dbp, dep);
+ xfs_da_buf_done(dbp);
+ xfs_dir2_leaf_check(dp, lbp);
+ xfs_da_brelse(tp, lbp);
+ return 0;
+}
+
+/*
+ * Return index in the leaf block (lbp) which is either the first
+ * one with this hash value, or if there are none, the insert point
+ * for that hash value.
+ */
+int /* index value */
+xfs_dir2_leaf_search_hash(
+ xfs_da_args_t *args, /* operation arguments */
+ xfs_dabuf_t *lbp) /* leaf buffer */
+{
+ xfs_dahash_t hash=0; /* hash from this entry */
+ xfs_dahash_t hashwant; /* hash value looking for */
+ int high; /* high leaf index */
+ int low; /* low leaf index */
+ xfs_dir2_leaf_t *leaf; /* leaf structure */
+ xfs_dir2_leaf_entry_t *lep; /* leaf entry */
+ int mid=0; /* current leaf index */
+
+ leaf = lbp->data;
+#ifndef __KERNEL__
+ if (INT_ISZERO(leaf->hdr.count, ARCH_CONVERT))
+ return 0;
+#endif
+ /*
+ * Note, the table cannot be empty, so we have to go through the loop.
+ * Binary search the leaf entries looking for our hash value.
+ */
+ for (lep = leaf->ents, low = 0, high = INT_GET(leaf->hdr.count, ARCH_CONVERT) - 1,
+ hashwant = args->hashval;
+ low <= high; ) {
+ mid = (low + high) >> 1;
+ if ((hash = INT_GET(lep[mid].hashval, ARCH_CONVERT)) == hashwant)
+ break;
+ if (hash < hashwant)
+ low = mid + 1;
+ else
+ high = mid - 1;
+ }
+ /*
+ * Found one, back up through all the equal hash values.
+ */
+ if (hash == hashwant) {
+ while (mid > 0 && INT_GET(lep[mid - 1].hashval, ARCH_CONVERT) == hashwant) {
+ mid--;
+ }
+ }
+ /*
+ * Need to point to an entry higher than ours.
+ */
+ else if (hash < hashwant)
+ mid++;
+ return mid;
+}
+
+/*
+ * Trim off a trailing data block. We know it's empty since the leaf
+ * freespace table says so.
+ */
+int /* error */
+xfs_dir2_leaf_trim_data(
+ xfs_da_args_t *args, /* operation arguments */
+ xfs_dabuf_t *lbp, /* leaf buffer */
+ xfs_dir2_db_t db) /* data block number */
+{
+ xfs_dir2_data_off_t *bestsp; /* leaf bests table */
+#ifdef DEBUG
+ xfs_dir2_data_t *data; /* data block structure */
+#endif
+ xfs_dabuf_t *dbp; /* data block buffer */
+ xfs_inode_t *dp; /* incore directory inode */
+ int error; /* error return value */
+ xfs_dir2_leaf_t *leaf; /* leaf structure */
+ xfs_dir2_leaf_tail_t *ltp; /* leaf tail structure */
+ xfs_mount_t *mp; /* filesystem mount point */
+ xfs_trans_t *tp; /* transaction pointer */
+
+ dp = args->dp;
+ mp = dp->i_mount;
+ tp = args->trans;
+ /*
+ * Read the offending data block. We need its buffer.
+ */
+ if ((error = xfs_da_read_buf(tp, dp, XFS_DIR2_DB_TO_DA(mp, db), -1, &dbp,
+ XFS_DATA_FORK))) {
+ return error;
+ }
+#ifdef DEBUG
+ data = dbp->data;
+ ASSERT(INT_GET(data->hdr.magic, ARCH_CONVERT) == XFS_DIR2_DATA_MAGIC);
+#endif
+ /* this seems to be an error
+ * data is only valid if DEBUG is defined?
+ * RMC 09/08/1999
+ */
+
+ leaf = lbp->data;
+ ltp = XFS_DIR2_LEAF_TAIL_P(mp, leaf);
+ ASSERT(INT_GET(data->hdr.bestfree[0].length, ARCH_CONVERT) ==
+ mp->m_dirblksize - (uint)sizeof(data->hdr));
+ ASSERT(db == INT_GET(ltp->bestcount, ARCH_CONVERT) - 1);
+ /*
+ * Get rid of the data block.
+ */
+ if ((error = xfs_dir2_shrink_inode(args, db, dbp))) {
+ ASSERT(error != ENOSPC);
+ xfs_da_brelse(tp, dbp);
+ return error;
+ }
+ /*
+ * Eliminate the last bests entry from the table.
+ */
+ bestsp = XFS_DIR2_LEAF_BESTS_P_ARCH(ltp, ARCH_CONVERT);
+ INT_MOD(ltp->bestcount, ARCH_CONVERT, -1);
+ memmove(&bestsp[1], &bestsp[0], INT_GET(ltp->bestcount, ARCH_CONVERT) * sizeof(*bestsp));
+ xfs_dir2_leaf_log_tail(tp, lbp);
+ xfs_dir2_leaf_log_bests(tp, lbp, 0, INT_GET(ltp->bestcount, ARCH_CONVERT) - 1);
+ return 0;
+}
+
+/*
+ * Convert node form directory to leaf form directory.
+ * The root of the node form dir needs to already be a LEAFN block.
+ * Just return if we can't do anything.
+ */
+int /* error */
+xfs_dir2_node_to_leaf(
+ xfs_da_state_t *state) /* directory operation state */
+{
+ xfs_da_args_t *args; /* operation arguments */
+ xfs_inode_t *dp; /* incore directory inode */
+ int error; /* error return code */
+ xfs_dabuf_t *fbp; /* buffer for freespace block */
+ xfs_fileoff_t fo; /* freespace file offset */
+ xfs_dir2_free_t *free; /* freespace structure */
+ xfs_dabuf_t *lbp; /* buffer for leaf block */
+ xfs_dir2_leaf_tail_t *ltp; /* tail of leaf structure */
+ xfs_dir2_leaf_t *leaf; /* leaf structure */
+ xfs_mount_t *mp; /* filesystem mount point */
+ int rval; /* successful free trim? */
+ xfs_trans_t *tp; /* transaction pointer */
+
+ /*
+ * There's more than a leaf level in the btree, so there must
+ * be multiple leafn blocks. Give up.
+ */
+ if (state->path.active > 1)
+ return 0;
+ args = state->args;
+ xfs_dir2_trace_args("node_to_leaf", args);
+ mp = state->mp;
+ dp = args->dp;
+ tp = args->trans;
+ /*
+ * Get the last offset in the file.
+ */
+ if ((error = xfs_bmap_last_offset(tp, dp, &fo, XFS_DATA_FORK))) {
+ return error;
+ }
+ fo -= mp->m_dirblkfsbs;
+ /*
+ * If there are freespace blocks other than the first one,
+ * take this opportunity to remove trailing empty freespace blocks
+ * that may have been left behind during no-space-reservation
+ * operations.
+ */
+ while (fo > mp->m_dirfreeblk) {
+ if ((error = xfs_dir2_node_trim_free(args, fo, &rval))) {
+ return error;
+ }
+ if (rval)
+ fo -= mp->m_dirblkfsbs;
+ else
+ return 0;
+ }
+ /*
+ * Now find the block just before the freespace block.
+ */
+ if ((error = xfs_bmap_last_before(tp, dp, &fo, XFS_DATA_FORK))) {
+ return error;
+ }
+ /*
+ * If it's not the single leaf block, give up.
+ */
+ if (XFS_FSB_TO_B(mp, fo) > XFS_DIR2_LEAF_OFFSET + mp->m_dirblksize)
+ return 0;
+ lbp = state->path.blk[0].bp;
+ leaf = lbp->data;
+ ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) == XFS_DIR2_LEAFN_MAGIC);
+ /*
+ * Read the freespace block.
+ */
+ if ((error = xfs_da_read_buf(tp, dp, mp->m_dirfreeblk, -1, &fbp,
+ XFS_DATA_FORK))) {
+ return error;
+ }
+ free = fbp->data;
+ ASSERT(INT_GET(free->hdr.magic, ARCH_CONVERT) == XFS_DIR2_FREE_MAGIC);
+ ASSERT(INT_ISZERO(free->hdr.firstdb, ARCH_CONVERT));
+ /*
+ * Now see if the leafn and free data will fit in a leaf1.
+ * If not, release the buffer and give up.
+ */
+ if ((uint)sizeof(leaf->hdr) +
+ (INT_GET(leaf->hdr.count, ARCH_CONVERT) - INT_GET(leaf->hdr.stale, ARCH_CONVERT)) * (uint)sizeof(leaf->ents[0]) +
+ INT_GET(free->hdr.nvalid, ARCH_CONVERT) * (uint)sizeof(leaf->bests[0]) +
+ (uint)sizeof(leaf->tail) >
+ mp->m_dirblksize) {
+ xfs_da_brelse(tp, fbp);
+ return 0;
+ }
+ /*
+ * If the leaf has any stale entries in it, compress them out.
+ * The compact routine will log the header.
+ */
+ if (INT_GET(leaf->hdr.stale, ARCH_CONVERT))
+ xfs_dir2_leaf_compact(args, lbp);
+ else
+ xfs_dir2_leaf_log_header(tp, lbp);
+ INT_SET(leaf->hdr.info.magic, ARCH_CONVERT, XFS_DIR2_LEAF1_MAGIC);
+ /*
+ * Set up the leaf tail from the freespace block.
+ */
+ ltp = XFS_DIR2_LEAF_TAIL_P(mp, leaf);
+ INT_COPY(ltp->bestcount, free->hdr.nvalid, ARCH_CONVERT);
+ /*
+ * Set up the leaf bests table.
+ */
+ memcpy(XFS_DIR2_LEAF_BESTS_P_ARCH(ltp, ARCH_CONVERT), free->bests,
+ INT_GET(ltp->bestcount, ARCH_CONVERT) * sizeof(leaf->bests[0]));
+ xfs_dir2_leaf_log_bests(tp, lbp, 0, INT_GET(ltp->bestcount, ARCH_CONVERT) - 1);
+ xfs_dir2_leaf_log_tail(tp, lbp);
+ xfs_dir2_leaf_check(dp, lbp);
+ /*
+ * Get rid of the freespace block.
+ */
+ error = xfs_dir2_shrink_inode(args, XFS_DIR2_FREE_FIRSTDB(mp), fbp);
+ if (error) {
+ /*
+ * This can't fail here because it can only happen when
+ * punching out the middle of an extent, and this is an
+ * isolated block.
+ */
+ ASSERT(error != ENOSPC);
+ return error;
+ }
+ fbp = NULL;
+ /*
+ * Now see if we can convert the single-leaf directory
+ * down to a block form directory.
+ * This routine always kills the dabuf for the leaf, so
+ * eliminate it from the path.
+ */
+ error = xfs_dir2_leaf_to_block(args, lbp, NULL);
+ state->path.blk[0].bp = NULL;
+ return error;
+}
diff --git a/fs/xfs/xfs_dir2_leaf.h b/fs/xfs/xfs_dir2_leaf.h
new file mode 100644
index 00000000000000..d3b5145a9b4f17
--- /dev/null
+++ b/fs/xfs/xfs_dir2_leaf.h
@@ -0,0 +1,360 @@
+/*
+ * Copyright (c) 2000-2001 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_DIR2_LEAF_H__
+#define __XFS_DIR2_LEAF_H__
+
+/*
+ * Directory version 2, leaf block structures.
+ */
+
+struct uio;
+struct xfs_dabuf;
+struct xfs_da_args;
+struct xfs_inode;
+struct xfs_mount;
+struct xfs_trans;
+
+/*
+ * Constants.
+ */
+
+/*
+ * Offset of the leaf/node space. First block in this space
+ * is the btree root.
+ */
+#define XFS_DIR2_LEAF_SPACE 1
+#define XFS_DIR2_LEAF_OFFSET (XFS_DIR2_LEAF_SPACE * XFS_DIR2_SPACE_SIZE)
+#define XFS_DIR2_LEAF_FIRSTDB(mp) \
+ XFS_DIR2_BYTE_TO_DB(mp, XFS_DIR2_LEAF_OFFSET)
+
+/*
+ * Types.
+ */
+
+/*
+ * Offset in data space of a data entry.
+ */
+typedef __uint32_t xfs_dir2_dataptr_t;
+#define XFS_DIR2_MAX_DATAPTR ((xfs_dir2_dataptr_t)0xffffffff)
+#define XFS_DIR2_NULL_DATAPTR ((xfs_dir2_dataptr_t)0)
+
+/*
+ * Structures.
+ */
+
+/*
+ * Leaf block header.
+ */
+typedef struct xfs_dir2_leaf_hdr {
+ xfs_da_blkinfo_t info; /* header for da routines */
+ __uint16_t count; /* count of entries */
+ __uint16_t stale; /* count of stale entries */
+} xfs_dir2_leaf_hdr_t;
+
+/*
+ * Leaf block entry.
+ */
+typedef struct xfs_dir2_leaf_entry {
+ xfs_dahash_t hashval; /* hash value of name */
+ xfs_dir2_dataptr_t address; /* address of data entry */
+} xfs_dir2_leaf_entry_t;
+
+/*
+ * Leaf block tail.
+ */
+typedef struct xfs_dir2_leaf_tail {
+ __uint32_t bestcount;
+} xfs_dir2_leaf_tail_t;
+
+/*
+ * Leaf block.
+ * bests and tail are at the end of the block for single-leaf only
+ * (magic = XFS_DIR2_LEAF1_MAGIC not XFS_DIR2_LEAFN_MAGIC).
+ */
+typedef struct xfs_dir2_leaf {
+ xfs_dir2_leaf_hdr_t hdr; /* leaf header */
+ xfs_dir2_leaf_entry_t ents[1]; /* entries */
+ /* ... */
+ xfs_dir2_data_off_t bests[1]; /* best free counts */
+ xfs_dir2_leaf_tail_t tail; /* leaf tail */
+} xfs_dir2_leaf_t;
+
+/*
+ * Macros.
+ * The DB blocks are logical directory block numbers, not filesystem blocks.
+ */
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR2_MAX_LEAF_ENTS)
+int
+xfs_dir2_max_leaf_ents(struct xfs_mount *mp);
+#define XFS_DIR2_MAX_LEAF_ENTS(mp) \
+ xfs_dir2_max_leaf_ents(mp)
+#else
+#define XFS_DIR2_MAX_LEAF_ENTS(mp) \
+ ((int)(((mp)->m_dirblksize - (uint)sizeof(xfs_dir2_leaf_hdr_t)) / \
+ (uint)sizeof(xfs_dir2_leaf_entry_t)))
+#endif
+
+/*
+ * Get address of the bestcount field in the single-leaf block.
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR2_LEAF_TAIL_P)
+xfs_dir2_leaf_tail_t *
+xfs_dir2_leaf_tail_p(struct xfs_mount *mp, xfs_dir2_leaf_t *lp);
+#define XFS_DIR2_LEAF_TAIL_P(mp,lp) \
+ xfs_dir2_leaf_tail_p(mp, lp)
+#else
+#define XFS_DIR2_LEAF_TAIL_P(mp,lp) \
+ ((xfs_dir2_leaf_tail_t *)\
+ ((char *)(lp) + (mp)->m_dirblksize - \
+ (uint)sizeof(xfs_dir2_leaf_tail_t)))
+#endif
+
+/*
+ * Get address of the bests array in the single-leaf block.
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR2_LEAF_BESTS_P)
+xfs_dir2_data_off_t *
+xfs_dir2_leaf_bests_p_arch(xfs_dir2_leaf_tail_t *ltp, xfs_arch_t arch);
+#define XFS_DIR2_LEAF_BESTS_P_ARCH(ltp,arch) xfs_dir2_leaf_bests_p_arch(ltp,arch)
+#else
+#define XFS_DIR2_LEAF_BESTS_P_ARCH(ltp,arch) \
+ ((xfs_dir2_data_off_t *)(ltp) - INT_GET((ltp)->bestcount, arch))
+#endif
+
+/*
+ * Convert dataptr to byte in file space
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR2_DATAPTR_TO_BYTE)
+xfs_dir2_off_t
+xfs_dir2_dataptr_to_byte(struct xfs_mount *mp, xfs_dir2_dataptr_t dp);
+#define XFS_DIR2_DATAPTR_TO_BYTE(mp,dp) xfs_dir2_dataptr_to_byte(mp, dp)
+#else
+#define XFS_DIR2_DATAPTR_TO_BYTE(mp,dp) \
+ ((xfs_dir2_off_t)(dp) << XFS_DIR2_DATA_ALIGN_LOG)
+#endif
+
+/*
+ * Convert byte in file space to dataptr. It had better be aligned.
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR2_BYTE_TO_DATAPTR)
+xfs_dir2_dataptr_t
+xfs_dir2_byte_to_dataptr(struct xfs_mount *mp, xfs_dir2_off_t by);
+#define XFS_DIR2_BYTE_TO_DATAPTR(mp,by) xfs_dir2_byte_to_dataptr(mp,by)
+#else
+#define XFS_DIR2_BYTE_TO_DATAPTR(mp,by) \
+ ((xfs_dir2_dataptr_t)((by) >> XFS_DIR2_DATA_ALIGN_LOG))
+#endif
+
+/*
+ * Convert dataptr to a block number
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR2_DATAPTR_TO_DB)
+xfs_dir2_db_t
+xfs_dir2_dataptr_to_db(struct xfs_mount *mp, xfs_dir2_dataptr_t dp);
+#define XFS_DIR2_DATAPTR_TO_DB(mp,dp) xfs_dir2_dataptr_to_db(mp, dp)
+#else
+#define XFS_DIR2_DATAPTR_TO_DB(mp,dp) \
+ XFS_DIR2_BYTE_TO_DB(mp, XFS_DIR2_DATAPTR_TO_BYTE(mp, dp))
+#endif
+
+/*
+ * Convert dataptr to a byte offset in a block
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR2_DATAPTR_TO_OFF)
+xfs_dir2_data_aoff_t
+xfs_dir2_dataptr_to_off(struct xfs_mount *mp, xfs_dir2_dataptr_t dp);
+#define XFS_DIR2_DATAPTR_TO_OFF(mp,dp) xfs_dir2_dataptr_to_off(mp, dp)
+#else
+#define XFS_DIR2_DATAPTR_TO_OFF(mp,dp) \
+ XFS_DIR2_BYTE_TO_OFF(mp, XFS_DIR2_DATAPTR_TO_BYTE(mp, dp))
+#endif
+
+/*
+ * Convert block and offset to byte in space
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR2_DB_OFF_TO_BYTE)
+xfs_dir2_off_t
+xfs_dir2_db_off_to_byte(struct xfs_mount *mp, xfs_dir2_db_t db,
+ xfs_dir2_data_aoff_t o);
+#define XFS_DIR2_DB_OFF_TO_BYTE(mp,db,o) \
+ xfs_dir2_db_off_to_byte(mp, db, o)
+#else
+#define XFS_DIR2_DB_OFF_TO_BYTE(mp,db,o) \
+ (((xfs_dir2_off_t)(db) << \
+ ((mp)->m_sb.sb_blocklog + (mp)->m_sb.sb_dirblklog)) + (o))
+#endif
+
+/*
+ * Convert byte in space to (DB) block
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR2_BYTE_TO_DB)
+xfs_dir2_db_t xfs_dir2_byte_to_db(struct xfs_mount *mp, xfs_dir2_off_t by);
+#define XFS_DIR2_BYTE_TO_DB(mp,by) xfs_dir2_byte_to_db(mp, by)
+#else
+#define XFS_DIR2_BYTE_TO_DB(mp,by) \
+ ((xfs_dir2_db_t)((by) >> \
+ ((mp)->m_sb.sb_blocklog + (mp)->m_sb.sb_dirblklog)))
+#endif
+
+/*
+ * Convert byte in space to (DA) block
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR2_BYTE_TO_DA)
+xfs_dablk_t xfs_dir2_byte_to_da(struct xfs_mount *mp, xfs_dir2_off_t by);
+#define XFS_DIR2_BYTE_TO_DA(mp,by) xfs_dir2_byte_to_da(mp, by)
+#else
+#define XFS_DIR2_BYTE_TO_DA(mp,by) \
+ XFS_DIR2_DB_TO_DA(mp, XFS_DIR2_BYTE_TO_DB(mp, by))
+#endif
+
+/*
+ * Convert byte in space to offset in a block
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR2_BYTE_TO_OFF)
+xfs_dir2_data_aoff_t
+xfs_dir2_byte_to_off(struct xfs_mount *mp, xfs_dir2_off_t by);
+#define XFS_DIR2_BYTE_TO_OFF(mp,by) xfs_dir2_byte_to_off(mp, by)
+#else
+#define XFS_DIR2_BYTE_TO_OFF(mp,by) \
+ ((xfs_dir2_data_aoff_t)((by) & \
+ ((1 << ((mp)->m_sb.sb_blocklog + \
+ (mp)->m_sb.sb_dirblklog)) - 1)))
+#endif
+
+/*
+ * Convert block and offset to dataptr
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR2_DB_OFF_TO_DATAPTR)
+xfs_dir2_dataptr_t
+xfs_dir2_db_off_to_dataptr(struct xfs_mount *mp, xfs_dir2_db_t db,
+ xfs_dir2_data_aoff_t o);
+#define XFS_DIR2_DB_OFF_TO_DATAPTR(mp,db,o) \
+ xfs_dir2_db_off_to_dataptr(mp, db, o)
+#else
+#define XFS_DIR2_DB_OFF_TO_DATAPTR(mp,db,o) \
+ XFS_DIR2_BYTE_TO_DATAPTR(mp, XFS_DIR2_DB_OFF_TO_BYTE(mp, db, o))
+#endif
+
+/*
+ * Convert block (DB) to block (dablk)
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR2_DB_TO_DA)
+xfs_dablk_t xfs_dir2_db_to_da(struct xfs_mount *mp, xfs_dir2_db_t db);
+#define XFS_DIR2_DB_TO_DA(mp,db) xfs_dir2_db_to_da(mp, db)
+#else
+#define XFS_DIR2_DB_TO_DA(mp,db) \
+ ((xfs_dablk_t)((db) << (mp)->m_sb.sb_dirblklog))
+#endif
+
+/*
+ * Convert block (dablk) to block (DB)
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR2_DA_TO_DB)
+xfs_dir2_db_t xfs_dir2_da_to_db(struct xfs_mount *mp, xfs_dablk_t da);
+#define XFS_DIR2_DA_TO_DB(mp,da) xfs_dir2_da_to_db(mp, da)
+#else
+#define XFS_DIR2_DA_TO_DB(mp,da) \
+ ((xfs_dir2_db_t)((da) >> (mp)->m_sb.sb_dirblklog))
+#endif
+
+/*
+ * Convert block (dablk) to byte offset in space
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR2_DA_TO_BYTE)
+xfs_dir2_off_t xfs_dir2_da_to_byte(struct xfs_mount *mp, xfs_dablk_t da);
+#define XFS_DIR2_DA_TO_BYTE(mp,da) xfs_dir2_da_to_byte(mp, da)
+#else
+#define XFS_DIR2_DA_TO_BYTE(mp,da) \
+ XFS_DIR2_DB_OFF_TO_BYTE(mp, XFS_DIR2_DA_TO_DB(mp, da), 0)
+#endif
+
+/*
+ * Function declarations.
+ */
+
+extern int
+ xfs_dir2_block_to_leaf(struct xfs_da_args *args, struct xfs_dabuf *dbp);
+
+extern int
+ xfs_dir2_leaf_addname(struct xfs_da_args *args);
+
+extern void
+ xfs_dir2_leaf_compact(struct xfs_da_args *args, struct xfs_dabuf *bp);
+
+extern void
+ xfs_dir2_leaf_compact_x1(struct xfs_dabuf *bp, int *indexp,
+ int *lowstalep, int *highstalep, int *lowlogp,
+ int *highlogp);
+
+extern int
+ xfs_dir2_leaf_getdents(struct xfs_trans *tp, struct xfs_inode *dp,
+ struct uio *uio, int *eofp, struct xfs_dirent *dbp,
+ xfs_dir2_put_t put);
+
+extern int
+ xfs_dir2_leaf_init(struct xfs_da_args *args, xfs_dir2_db_t bno,
+ struct xfs_dabuf **bpp, int magic);
+
+extern void
+ xfs_dir2_leaf_log_ents(struct xfs_trans *tp, struct xfs_dabuf *bp,
+ int first, int last);
+
+extern void
+ xfs_dir2_leaf_log_bests(struct xfs_trans *tp, struct xfs_dabuf *bp,
+ int first, int last);
+
+extern void
+ xfs_dir2_leaf_log_header(struct xfs_trans *tp, struct xfs_dabuf *bp);
+
+extern void
+ xfs_dir2_leaf_log_tail(struct xfs_trans *tp, struct xfs_dabuf *bp);
+
+extern int
+ xfs_dir2_leaf_lookup(struct xfs_da_args *args);
+
+extern int
+ xfs_dir2_leaf_removename(struct xfs_da_args *args);
+
+extern int
+ xfs_dir2_leaf_replace(struct xfs_da_args *args);
+
+extern int
+ xfs_dir2_leaf_search_hash(struct xfs_da_args *args,
+ struct xfs_dabuf *lbp);
+extern int
+ xfs_dir2_leaf_trim_data(struct xfs_da_args *args, struct xfs_dabuf *lbp, xfs_dir2_db_t db);
+
+extern int
+ xfs_dir2_node_to_leaf(struct xfs_da_state *state);
+
+#endif /* __XFS_DIR2_LEAF_H__ */
diff --git a/fs/xfs/xfs_dir2_node.c b/fs/xfs/xfs_dir2_node.c
new file mode 100644
index 00000000000000..ec42931d049877
--- /dev/null
+++ b/fs/xfs/xfs_dir2_node.c
@@ -0,0 +1,2020 @@
+/*
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+/*
+ * xfs_dir2_node.c
+ * XFS directory implementation, version 2, node form files
+ * See data structures in xfs_dir2_node.h and xfs_da_btree.h.
+ */
+
+#include "xfs.h"
+
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode.h"
+#include "xfs_bmap.h"
+#include "xfs_da_btree.h"
+#include "xfs_dir2_data.h"
+#include "xfs_dir2_leaf.h"
+#include "xfs_dir2_block.h"
+#include "xfs_dir2_node.h"
+#include "xfs_dir2_trace.h"
+#include "xfs_error.h"
+
+/*
+ * Function declarations.
+ */
+static void xfs_dir2_free_log_header(xfs_trans_t *tp, xfs_dabuf_t *bp);
+static int xfs_dir2_leafn_add(xfs_dabuf_t *bp, xfs_da_args_t *args, int index);
+#ifdef DEBUG
+static void xfs_dir2_leafn_check(xfs_inode_t *dp, xfs_dabuf_t *bp);
+#else
+#define xfs_dir2_leafn_check(dp, bp)
+#endif
+static void xfs_dir2_leafn_moveents(xfs_da_args_t *args, xfs_dabuf_t *bp_s,
+ int start_s, xfs_dabuf_t *bp_d, int start_d,
+ int count);
+static void xfs_dir2_leafn_rebalance(xfs_da_state_t *state,
+ xfs_da_state_blk_t *blk1,
+ xfs_da_state_blk_t *blk2);
+static int xfs_dir2_leafn_remove(xfs_da_args_t *args, xfs_dabuf_t *bp,
+ int index, xfs_da_state_blk_t *dblk,
+ int *rval);
+static int xfs_dir2_node_addname_int(xfs_da_args_t *args,
+ xfs_da_state_blk_t *fblk);
+
+/*
+ * Log entries from a freespace block.
+ */
+void
+xfs_dir2_free_log_bests(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_dabuf_t *bp, /* freespace buffer */
+ int first, /* first entry to log */
+ int last) /* last entry to log */
+{
+ xfs_dir2_free_t *free; /* freespace structure */
+
+ free = bp->data;
+ ASSERT(INT_GET(free->hdr.magic, ARCH_CONVERT) == XFS_DIR2_FREE_MAGIC);
+ xfs_da_log_buf(tp, bp,
+ (uint)((char *)&free->bests[first] - (char *)free),
+ (uint)((char *)&free->bests[last] - (char *)free +
+ sizeof(free->bests[0]) - 1));
+}
+
+/*
+ * Log header from a freespace block.
+ */
+static void
+xfs_dir2_free_log_header(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_dabuf_t *bp) /* freespace buffer */
+{
+ xfs_dir2_free_t *free; /* freespace structure */
+
+ free = bp->data;
+ ASSERT(INT_GET(free->hdr.magic, ARCH_CONVERT) == XFS_DIR2_FREE_MAGIC);
+ xfs_da_log_buf(tp, bp, (uint)((char *)&free->hdr - (char *)free),
+ (uint)(sizeof(xfs_dir2_free_hdr_t) - 1));
+}
+
+/*
+ * Convert a leaf-format directory to a node-format directory.
+ * We need to change the magic number of the leaf block, and copy
+ * the freespace table out of the leaf block into its own block.
+ */
+int /* error */
+xfs_dir2_leaf_to_node(
+ xfs_da_args_t *args, /* operation arguments */
+ xfs_dabuf_t *lbp) /* leaf buffer */
+{
+ xfs_inode_t *dp; /* incore directory inode */
+ int error; /* error return value */
+ xfs_dabuf_t *fbp; /* freespace buffer */
+ xfs_dir2_db_t fdb; /* freespace block number */
+ xfs_dir2_free_t *free; /* freespace structure */
+ xfs_dir2_data_off_t *from; /* pointer to freespace entry */
+ int i; /* leaf freespace index */
+ xfs_dir2_leaf_t *leaf; /* leaf structure */
+ xfs_dir2_leaf_tail_t *ltp; /* leaf tail structure */
+ xfs_mount_t *mp; /* filesystem mount point */
+ int n; /* count of live freespc ents */
+ xfs_dir2_data_off_t off; /* freespace entry value */
+ xfs_dir2_data_off_t *to; /* pointer to freespace entry */
+ xfs_trans_t *tp; /* transaction pointer */
+
+ xfs_dir2_trace_args_b("leaf_to_node", args, lbp);
+ dp = args->dp;
+ mp = dp->i_mount;
+ tp = args->trans;
+ /*
+ * Add a freespace block to the directory.
+ */
+ if ((error = xfs_dir2_grow_inode(args, XFS_DIR2_FREE_SPACE, &fdb))) {
+ return error;
+ }
+ ASSERT(fdb == XFS_DIR2_FREE_FIRSTDB(mp));
+ /*
+ * Get the buffer for the new freespace block.
+ */
+ if ((error = xfs_da_get_buf(tp, dp, XFS_DIR2_DB_TO_DA(mp, fdb), -1, &fbp,
+ XFS_DATA_FORK))) {
+ return error;
+ }
+ ASSERT(fbp != NULL);
+ free = fbp->data;
+ leaf = lbp->data;
+ ltp = XFS_DIR2_LEAF_TAIL_P(mp, leaf);
+ /*
+ * Initialize the freespace block header.
+ */
+ INT_SET(free->hdr.magic, ARCH_CONVERT, XFS_DIR2_FREE_MAGIC);
+ INT_ZERO(free->hdr.firstdb, ARCH_CONVERT);
+ ASSERT(INT_GET(ltp->bestcount, ARCH_CONVERT) <= (uint)dp->i_d.di_size / mp->m_dirblksize);
+ INT_COPY(free->hdr.nvalid, ltp->bestcount, ARCH_CONVERT);
+ /*
+ * Copy freespace entries from the leaf block to the new block.
+ * Count active entries.
+ */
+ for (i = n = 0, from = XFS_DIR2_LEAF_BESTS_P_ARCH(ltp, ARCH_CONVERT), to = free->bests;
+ i < INT_GET(ltp->bestcount, ARCH_CONVERT); i++, from++, to++) {
+ if ((off = INT_GET(*from, ARCH_CONVERT)) != NULLDATAOFF)
+ n++;
+ INT_SET(*to, ARCH_CONVERT, off);
+ }
+ INT_SET(free->hdr.nused, ARCH_CONVERT, n);
+ INT_SET(leaf->hdr.info.magic, ARCH_CONVERT, XFS_DIR2_LEAFN_MAGIC);
+ /*
+ * Log everything.
+ */
+ xfs_dir2_leaf_log_header(tp, lbp);
+ xfs_dir2_free_log_header(tp, fbp);
+ xfs_dir2_free_log_bests(tp, fbp, 0, INT_GET(free->hdr.nvalid, ARCH_CONVERT) - 1);
+ xfs_da_buf_done(fbp);
+ xfs_dir2_leafn_check(dp, lbp);
+ return 0;
+}
+
+/*
+ * Add a leaf entry to a leaf block in a node-form directory.
+ * The other work necessary is done from the caller.
+ */
+static int /* error */
+xfs_dir2_leafn_add(
+ xfs_dabuf_t *bp, /* leaf buffer */
+ xfs_da_args_t *args, /* operation arguments */
+ int index) /* insertion pt for new entry */
+{
+ int compact; /* compacting stale leaves */
+ xfs_inode_t *dp; /* incore directory inode */
+ int highstale; /* next stale entry */
+ xfs_dir2_leaf_t *leaf; /* leaf structure */
+ xfs_dir2_leaf_entry_t *lep; /* leaf entry */
+ int lfloghigh; /* high leaf entry logging */
+ int lfloglow; /* low leaf entry logging */
+ int lowstale; /* previous stale entry */
+ xfs_mount_t *mp; /* filesystem mount point */
+ xfs_trans_t *tp; /* transaction pointer */
+
+ xfs_dir2_trace_args_sb("leafn_add", args, index, bp);
+ dp = args->dp;
+ mp = dp->i_mount;
+ tp = args->trans;
+ leaf = bp->data;
+
+ /*
+ * Quick check just to make sure we are not going to index
+ * into other peoples memory
+ */
+ if (index < 0)
+ return XFS_ERROR(EFSCORRUPTED);
+
+ /*
+ * If there are already the maximum number of leaf entries in
+ * the block, if there are no stale entries it won't fit.
+ * Caller will do a split. If there are stale entries we'll do
+ * a compact.
+ */
+
+ if (INT_GET(leaf->hdr.count, ARCH_CONVERT) == XFS_DIR2_MAX_LEAF_ENTS(mp)) {
+ if (INT_ISZERO(leaf->hdr.stale, ARCH_CONVERT))
+ return XFS_ERROR(ENOSPC);
+ compact = INT_GET(leaf->hdr.stale, ARCH_CONVERT) > 1;
+ } else
+ compact = 0;
+ ASSERT(index == 0 || INT_GET(leaf->ents[index - 1].hashval, ARCH_CONVERT) <= args->hashval);
+ ASSERT(index == INT_GET(leaf->hdr.count, ARCH_CONVERT) ||
+ INT_GET(leaf->ents[index].hashval, ARCH_CONVERT) >= args->hashval);
+
+ if (args->justcheck)
+ return 0;
+
+ /*
+ * Compact out all but one stale leaf entry. Leaves behind
+ * the entry closest to index.
+ */
+ if (compact) {
+ xfs_dir2_leaf_compact_x1(bp, &index, &lowstale, &highstale,
+ &lfloglow, &lfloghigh);
+ }
+ /*
+ * Set impossible logging indices for this case.
+ */
+ else if (!INT_ISZERO(leaf->hdr.stale, ARCH_CONVERT)) {
+ lfloglow = INT_GET(leaf->hdr.count, ARCH_CONVERT);
+ lfloghigh = -1;
+ }
+ /*
+ * No stale entries, just insert a space for the new entry.
+ */
+ if (INT_ISZERO(leaf->hdr.stale, ARCH_CONVERT)) {
+ lep = &leaf->ents[index];
+ if (index < INT_GET(leaf->hdr.count, ARCH_CONVERT))
+ memmove(lep + 1, lep,
+ (INT_GET(leaf->hdr.count, ARCH_CONVERT) - index) * sizeof(*lep));
+ lfloglow = index;
+ lfloghigh = INT_GET(leaf->hdr.count, ARCH_CONVERT);
+ INT_MOD(leaf->hdr.count, ARCH_CONVERT, +1);
+ }
+ /*
+ * There are stale entries. We'll use one for the new entry.
+ */
+ else {
+ /*
+ * If we didn't do a compact then we need to figure out
+ * which stale entry will be used.
+ */
+ if (compact == 0) {
+ /*
+ * Find first stale entry before our insertion point.
+ */
+ for (lowstale = index - 1;
+ lowstale >= 0 &&
+ INT_GET(leaf->ents[lowstale].address, ARCH_CONVERT) !=
+ XFS_DIR2_NULL_DATAPTR;
+ lowstale--)
+ continue;
+ /*
+ * Find next stale entry after insertion point.
+ * Stop looking if the answer would be worse than
+ * lowstale already found.
+ */
+ for (highstale = index;
+ highstale < INT_GET(leaf->hdr.count, ARCH_CONVERT) &&
+ INT_GET(leaf->ents[highstale].address, ARCH_CONVERT) !=
+ XFS_DIR2_NULL_DATAPTR &&
+ (lowstale < 0 ||
+ index - lowstale - 1 >= highstale - index);
+ highstale++)
+ continue;
+ }
+ /*
+ * Using the low stale entry.
+ * Shift entries up toward the stale slot.
+ */
+ if (lowstale >= 0 &&
+ (highstale == INT_GET(leaf->hdr.count, ARCH_CONVERT) ||
+ index - lowstale - 1 < highstale - index)) {
+ ASSERT(INT_GET(leaf->ents[lowstale].address, ARCH_CONVERT) ==
+ XFS_DIR2_NULL_DATAPTR);
+ ASSERT(index - lowstale - 1 >= 0);
+ if (index - lowstale - 1 > 0)
+ memmove(&leaf->ents[lowstale],
+ &leaf->ents[lowstale + 1],
+ (index - lowstale - 1) * sizeof(*lep));
+ lep = &leaf->ents[index - 1];
+ lfloglow = MIN(lowstale, lfloglow);
+ lfloghigh = MAX(index - 1, lfloghigh);
+ }
+ /*
+ * Using the high stale entry.
+ * Shift entries down toward the stale slot.
+ */
+ else {
+ ASSERT(INT_GET(leaf->ents[highstale].address, ARCH_CONVERT) ==
+ XFS_DIR2_NULL_DATAPTR);
+ ASSERT(highstale - index >= 0);
+ if (highstale - index > 0)
+ memmove(&leaf->ents[index + 1],
+ &leaf->ents[index],
+ (highstale - index) * sizeof(*lep));
+ lep = &leaf->ents[index];
+ lfloglow = MIN(index, lfloglow);
+ lfloghigh = MAX(highstale, lfloghigh);
+ }
+ INT_MOD(leaf->hdr.stale, ARCH_CONVERT, -1);
+ }
+ /*
+ * Insert the new entry, log everything.
+ */
+ INT_SET(lep->hashval, ARCH_CONVERT, args->hashval);
+ INT_SET(lep->address, ARCH_CONVERT, XFS_DIR2_DB_OFF_TO_DATAPTR(mp, args->blkno, args->index));
+ xfs_dir2_leaf_log_header(tp, bp);
+ xfs_dir2_leaf_log_ents(tp, bp, lfloglow, lfloghigh);
+ xfs_dir2_leafn_check(dp, bp);
+ return 0;
+}
+
+#ifdef DEBUG
+/*
+ * Check internal consistency of a leafn block.
+ */
+void
+xfs_dir2_leafn_check(
+ xfs_inode_t *dp, /* incore directory inode */
+ xfs_dabuf_t *bp) /* leaf buffer */
+{
+ int i; /* leaf index */
+ xfs_dir2_leaf_t *leaf; /* leaf structure */
+ xfs_mount_t *mp; /* filesystem mount point */
+ int stale; /* count of stale leaves */
+
+ leaf = bp->data;
+ mp = dp->i_mount;
+ ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) == XFS_DIR2_LEAFN_MAGIC);
+ ASSERT(INT_GET(leaf->hdr.count, ARCH_CONVERT) <= XFS_DIR2_MAX_LEAF_ENTS(mp));
+ for (i = stale = 0; i < INT_GET(leaf->hdr.count, ARCH_CONVERT); i++) {
+ if (i + 1 < INT_GET(leaf->hdr.count, ARCH_CONVERT)) {
+ ASSERT(INT_GET(leaf->ents[i].hashval, ARCH_CONVERT) <=
+ INT_GET(leaf->ents[i + 1].hashval, ARCH_CONVERT));
+ }
+ if (INT_GET(leaf->ents[i].address, ARCH_CONVERT) == XFS_DIR2_NULL_DATAPTR)
+ stale++;
+ }
+ ASSERT(INT_GET(leaf->hdr.stale, ARCH_CONVERT) == stale);
+}
+#endif /* DEBUG */
+
+/*
+ * Return the last hash value in the leaf.
+ * Stale entries are ok.
+ */
+xfs_dahash_t /* hash value */
+xfs_dir2_leafn_lasthash(
+ xfs_dabuf_t *bp, /* leaf buffer */
+ int *count) /* count of entries in leaf */
+{
+ xfs_dir2_leaf_t *leaf; /* leaf structure */
+
+ leaf = bp->data;
+ ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) == XFS_DIR2_LEAFN_MAGIC);
+ if (count)
+ *count = INT_GET(leaf->hdr.count, ARCH_CONVERT);
+ if (INT_ISZERO(leaf->hdr.count, ARCH_CONVERT))
+ return 0;
+ return INT_GET(leaf->ents[INT_GET(leaf->hdr.count, ARCH_CONVERT) - 1].hashval, ARCH_CONVERT);
+}
+
+/*
+ * Look up a leaf entry in a node-format leaf block.
+ * If this is an addname then the extrablk in state is a freespace block,
+ * otherwise it's a data block.
+ */
+int
+xfs_dir2_leafn_lookup_int(
+ xfs_dabuf_t *bp, /* leaf buffer */
+ xfs_da_args_t *args, /* operation arguments */
+ int *indexp, /* out: leaf entry index */
+ xfs_da_state_t *state) /* state to fill in */
+{
+ xfs_dabuf_t *curbp; /* current data/free buffer */
+ xfs_dir2_db_t curdb; /* current data block number */
+ xfs_dir2_db_t curfdb; /* current free block number */
+ xfs_dir2_data_entry_t *dep; /* data block entry */
+ xfs_inode_t *dp; /* incore directory inode */
+ int error; /* error return value */
+ int fi; /* free entry index */
+ xfs_dir2_free_t *free=NULL; /* free block structure */
+ int index; /* leaf entry index */
+ xfs_dir2_leaf_t *leaf; /* leaf structure */
+ int length=0; /* length of new data entry */
+ xfs_dir2_leaf_entry_t *lep; /* leaf entry */
+ xfs_mount_t *mp; /* filesystem mount point */
+ xfs_dir2_db_t newdb; /* new data block number */
+ xfs_dir2_db_t newfdb; /* new free block number */
+ xfs_trans_t *tp; /* transaction pointer */
+
+ dp = args->dp;
+ tp = args->trans;
+ mp = dp->i_mount;
+ leaf = bp->data;
+ ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) == XFS_DIR2_LEAFN_MAGIC);
+#ifdef __KERNEL__
+ ASSERT(INT_GET(leaf->hdr.count, ARCH_CONVERT) > 0);
+#endif
+ xfs_dir2_leafn_check(dp, bp);
+ /*
+ * Look up the hash value in the leaf entries.
+ */
+ index = xfs_dir2_leaf_search_hash(args, bp);
+ /*
+ * Do we have a buffer coming in?
+ */
+ if (state->extravalid)
+ curbp = state->extrablk.bp;
+ else
+ curbp = NULL;
+ /*
+ * For addname, it's a free block buffer, get the block number.
+ */
+ if (args->addname) {
+ curfdb = curbp ? state->extrablk.blkno : -1;
+ curdb = -1;
+ length = XFS_DIR2_DATA_ENTSIZE(args->namelen);
+ if ((free = (curbp ? curbp->data : NULL)))
+ ASSERT(INT_GET(free->hdr.magic, ARCH_CONVERT) == XFS_DIR2_FREE_MAGIC);
+ }
+ /*
+ * For others, it's a data block buffer, get the block number.
+ */
+ else {
+ curfdb = -1;
+ curdb = curbp ? state->extrablk.blkno : -1;
+ }
+ /*
+ * Loop over leaf entries with the right hash value.
+ */
+ for (lep = &leaf->ents[index];
+ index < INT_GET(leaf->hdr.count, ARCH_CONVERT) && INT_GET(lep->hashval, ARCH_CONVERT) == args->hashval;
+ lep++, index++) {
+ /*
+ * Skip stale leaf entries.
+ */
+ if (INT_GET(lep->address, ARCH_CONVERT) == XFS_DIR2_NULL_DATAPTR)
+ continue;
+ /*
+ * Pull the data block number from the entry.
+ */
+ newdb = XFS_DIR2_DATAPTR_TO_DB(mp, INT_GET(lep->address, ARCH_CONVERT));
+ /*
+ * For addname, we're looking for a place to put the new entry.
+ * We want to use a data block with an entry of equal
+ * hash value to ours if there is one with room.
+ */
+ if (args->addname) {
+ /*
+ * If this block isn't the data block we already have
+ * in hand, take a look at it.
+ */
+ if (newdb != curdb) {
+ curdb = newdb;
+ /*
+ * Convert the data block to the free block
+ * holding its freespace information.
+ */
+ newfdb = XFS_DIR2_DB_TO_FDB(mp, newdb);
+ /*
+ * If it's not the one we have in hand,
+ * read it in.
+ */
+ if (newfdb != curfdb) {
+ /*
+ * If we had one before, drop it.
+ */
+ if (curbp)
+ xfs_da_brelse(tp, curbp);
+ /*
+ * Read the free block.
+ */
+ if ((error = xfs_da_read_buf(tp, dp,
+ XFS_DIR2_DB_TO_DA(mp,
+ newfdb),
+ -1, &curbp,
+ XFS_DATA_FORK))) {
+ return error;
+ }
+ curfdb = newfdb;
+ free = curbp->data;
+ ASSERT(INT_GET(free->hdr.magic, ARCH_CONVERT) ==
+ XFS_DIR2_FREE_MAGIC);
+ ASSERT((INT_GET(free->hdr.firstdb, ARCH_CONVERT) %
+ XFS_DIR2_MAX_FREE_BESTS(mp)) ==
+ 0);
+ ASSERT(INT_GET(free->hdr.firstdb, ARCH_CONVERT) <= curdb);
+ ASSERT(curdb <
+ INT_GET(free->hdr.firstdb, ARCH_CONVERT) +
+ INT_GET(free->hdr.nvalid, ARCH_CONVERT));
+ }
+ /*
+ * Get the index for our entry.
+ */
+ fi = XFS_DIR2_DB_TO_FDINDEX(mp, curdb);
+ /*
+ * If it has room, return it.
+ */
+ if (unlikely(INT_GET(free->bests[fi], ARCH_CONVERT) == NULLDATAOFF)) {
+ XFS_ERROR_REPORT("xfs_dir2_leafn_lookup_int",
+ XFS_ERRLEVEL_LOW, mp);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ if (INT_GET(free->bests[fi], ARCH_CONVERT) >= length) {
+ *indexp = index;
+ state->extravalid = 1;
+ state->extrablk.bp = curbp;
+ state->extrablk.blkno = curfdb;
+ state->extrablk.index = fi;
+ state->extrablk.magic =
+ XFS_DIR2_FREE_MAGIC;
+ ASSERT(args->oknoent);
+ return XFS_ERROR(ENOENT);
+ }
+ }
+ }
+ /*
+ * Not adding a new entry, so we really want to find
+ * the name given to us.
+ */
+ else {
+ /*
+ * If it's a different data block, go get it.
+ */
+ if (newdb != curdb) {
+ /*
+ * If we had a block before, drop it.
+ */
+ if (curbp)
+ xfs_da_brelse(tp, curbp);
+ /*
+ * Read the data block.
+ */
+ if ((error =
+ xfs_da_read_buf(tp, dp,
+ XFS_DIR2_DB_TO_DA(mp, newdb), -1,
+ &curbp, XFS_DATA_FORK))) {
+ return error;
+ }
+ xfs_dir2_data_check(dp, curbp);
+ curdb = newdb;
+ }
+ /*
+ * Point to the data entry.
+ */
+ dep = (xfs_dir2_data_entry_t *)
+ ((char *)curbp->data +
+ XFS_DIR2_DATAPTR_TO_OFF(mp, INT_GET(lep->address, ARCH_CONVERT)));
+ /*
+ * Compare the entry, return it if it matches.
+ */
+ if (dep->namelen == args->namelen &&
+ dep->name[0] == args->name[0] &&
+ memcmp(dep->name, args->name, args->namelen) == 0) {
+ args->inumber = INT_GET(dep->inumber, ARCH_CONVERT);
+ *indexp = index;
+ state->extravalid = 1;
+ state->extrablk.bp = curbp;
+ state->extrablk.blkno = curdb;
+ state->extrablk.index =
+ (int)((char *)dep -
+ (char *)curbp->data);
+ state->extrablk.magic = XFS_DIR2_DATA_MAGIC;
+ return XFS_ERROR(EEXIST);
+ }
+ }
+ }
+ /*
+ * Didn't find a match.
+ * If we are holding a buffer, give it back in case our caller
+ * finds it useful.
+ */
+ if ((state->extravalid = (curbp != NULL))) {
+ state->extrablk.bp = curbp;
+ state->extrablk.index = -1;
+ /*
+ * For addname, giving back a free block.
+ */
+ if (args->addname) {
+ state->extrablk.blkno = curfdb;
+ state->extrablk.magic = XFS_DIR2_FREE_MAGIC;
+ }
+ /*
+ * For other callers, giving back a data block.
+ */
+ else {
+ state->extrablk.blkno = curdb;
+ state->extrablk.magic = XFS_DIR2_DATA_MAGIC;
+ }
+ }
+ /*
+ * Return the final index, that will be the insertion point.
+ */
+ *indexp = index;
+ ASSERT(index == INT_GET(leaf->hdr.count, ARCH_CONVERT) || args->oknoent);
+ return XFS_ERROR(ENOENT);
+}
+
+/*
+ * Move count leaf entries from source to destination leaf.
+ * Log entries and headers. Stale entries are preserved.
+ */
+static void
+xfs_dir2_leafn_moveents(
+ xfs_da_args_t *args, /* operation arguments */
+ xfs_dabuf_t *bp_s, /* source leaf buffer */
+ int start_s, /* source leaf index */
+ xfs_dabuf_t *bp_d, /* destination leaf buffer */
+ int start_d, /* destination leaf index */
+ int count) /* count of leaves to copy */
+{
+ xfs_dir2_leaf_t *leaf_d; /* destination leaf structure */
+ xfs_dir2_leaf_t *leaf_s; /* source leaf structure */
+ int stale; /* count stale leaves copied */
+ xfs_trans_t *tp; /* transaction pointer */
+
+ xfs_dir2_trace_args_bibii("leafn_moveents", args, bp_s, start_s, bp_d,
+ start_d, count);
+ /*
+ * Silently return if nothing to do.
+ */
+ if (count == 0) {
+ return;
+ }
+ tp = args->trans;
+ leaf_s = bp_s->data;
+ leaf_d = bp_d->data;
+ /*
+ * If the destination index is not the end of the current
+ * destination leaf entries, open up a hole in the destination
+ * to hold the new entries.
+ */
+ if (start_d < INT_GET(leaf_d->hdr.count, ARCH_CONVERT)) {
+ memmove(&leaf_d->ents[start_d + count], &leaf_d->ents[start_d],
+ (INT_GET(leaf_d->hdr.count, ARCH_CONVERT) - start_d) *
+ sizeof(xfs_dir2_leaf_entry_t));
+ xfs_dir2_leaf_log_ents(tp, bp_d, start_d + count,
+ count + INT_GET(leaf_d->hdr.count, ARCH_CONVERT) - 1);
+ }
+ /*
+ * If the source has stale leaves, count the ones in the copy range
+ * so we can update the header correctly.
+ */
+ if (!INT_ISZERO(leaf_s->hdr.stale, ARCH_CONVERT)) {
+ int i; /* temp leaf index */
+
+ for (i = start_s, stale = 0; i < start_s + count; i++) {
+ if (INT_GET(leaf_s->ents[i].address, ARCH_CONVERT) == XFS_DIR2_NULL_DATAPTR)
+ stale++;
+ }
+ } else
+ stale = 0;
+ /*
+ * Copy the leaf entries from source to destination.
+ */
+ memcpy(&leaf_d->ents[start_d], &leaf_s->ents[start_s],
+ count * sizeof(xfs_dir2_leaf_entry_t));
+ xfs_dir2_leaf_log_ents(tp, bp_d, start_d, start_d + count - 1);
+ /*
+ * If there are source entries after the ones we copied,
+ * delete the ones we copied by sliding the next ones down.
+ */
+ if (start_s + count < INT_GET(leaf_s->hdr.count, ARCH_CONVERT)) {
+ memmove(&leaf_s->ents[start_s], &leaf_s->ents[start_s + count],
+ count * sizeof(xfs_dir2_leaf_entry_t));
+ xfs_dir2_leaf_log_ents(tp, bp_s, start_s, start_s + count - 1);
+ }
+ /*
+ * Update the headers and log them.
+ */
+ INT_MOD(leaf_s->hdr.count, ARCH_CONVERT, -(count));
+ INT_MOD(leaf_s->hdr.stale, ARCH_CONVERT, -(stale));
+ INT_MOD(leaf_d->hdr.count, ARCH_CONVERT, count);
+ INT_MOD(leaf_d->hdr.stale, ARCH_CONVERT, stale);
+ xfs_dir2_leaf_log_header(tp, bp_s);
+ xfs_dir2_leaf_log_header(tp, bp_d);
+ xfs_dir2_leafn_check(args->dp, bp_s);
+ xfs_dir2_leafn_check(args->dp, bp_d);
+}
+
+/*
+ * Determine the sort order of two leaf blocks.
+ * Returns 1 if both are valid and leaf2 should be before leaf1, else 0.
+ */
+int /* sort order */
+xfs_dir2_leafn_order(
+ xfs_dabuf_t *leaf1_bp, /* leaf1 buffer */
+ xfs_dabuf_t *leaf2_bp) /* leaf2 buffer */
+{
+ xfs_dir2_leaf_t *leaf1; /* leaf1 structure */
+ xfs_dir2_leaf_t *leaf2; /* leaf2 structure */
+
+ leaf1 = leaf1_bp->data;
+ leaf2 = leaf2_bp->data;
+ ASSERT(INT_GET(leaf1->hdr.info.magic, ARCH_CONVERT) == XFS_DIR2_LEAFN_MAGIC);
+ ASSERT(INT_GET(leaf2->hdr.info.magic, ARCH_CONVERT) == XFS_DIR2_LEAFN_MAGIC);
+ if (INT_GET(leaf1->hdr.count, ARCH_CONVERT) > 0 &&
+ INT_GET(leaf2->hdr.count, ARCH_CONVERT) > 0 &&
+ (INT_GET(leaf2->ents[0].hashval, ARCH_CONVERT) < INT_GET(leaf1->ents[0].hashval, ARCH_CONVERT) ||
+ INT_GET(leaf2->ents[INT_GET(leaf2->hdr.count, ARCH_CONVERT) - 1].hashval, ARCH_CONVERT) <
+ INT_GET(leaf1->ents[INT_GET(leaf1->hdr.count, ARCH_CONVERT) - 1].hashval, ARCH_CONVERT)))
+ return 1;
+ return 0;
+}
+
+/*
+ * Rebalance leaf entries between two leaf blocks.
+ * This is actually only called when the second block is new,
+ * though the code deals with the general case.
+ * A new entry will be inserted in one of the blocks, and that
+ * entry is taken into account when balancing.
+ */
+static void
+xfs_dir2_leafn_rebalance(
+ xfs_da_state_t *state, /* btree cursor */
+ xfs_da_state_blk_t *blk1, /* first btree block */
+ xfs_da_state_blk_t *blk2) /* second btree block */
+{
+ xfs_da_args_t *args; /* operation arguments */
+ int count; /* count (& direction) leaves */
+ int isleft; /* new goes in left leaf */
+ xfs_dir2_leaf_t *leaf1; /* first leaf structure */
+ xfs_dir2_leaf_t *leaf2; /* second leaf structure */
+ int mid; /* midpoint leaf index */
+#ifdef DEBUG
+ int oldstale; /* old count of stale leaves */
+#endif
+ int oldsum; /* old total leaf count */
+ int swap; /* swapped leaf blocks */
+
+ args = state->args;
+ /*
+ * If the block order is wrong, swap the arguments.
+ */
+ if ((swap = xfs_dir2_leafn_order(blk1->bp, blk2->bp))) {
+ xfs_da_state_blk_t *tmp; /* temp for block swap */
+
+ tmp = blk1;
+ blk1 = blk2;
+ blk2 = tmp;
+ }
+ leaf1 = blk1->bp->data;
+ leaf2 = blk2->bp->data;
+ oldsum = INT_GET(leaf1->hdr.count, ARCH_CONVERT) + INT_GET(leaf2->hdr.count, ARCH_CONVERT);
+#ifdef DEBUG
+ oldstale = INT_GET(leaf1->hdr.stale, ARCH_CONVERT) + INT_GET(leaf2->hdr.stale, ARCH_CONVERT);
+#endif
+ mid = oldsum >> 1;
+ /*
+ * If the old leaf count was odd then the new one will be even,
+ * so we need to divide the new count evenly.
+ */
+ if (oldsum & 1) {
+ xfs_dahash_t midhash; /* middle entry hash value */
+
+ if (mid >= INT_GET(leaf1->hdr.count, ARCH_CONVERT))
+ midhash = INT_GET(leaf2->ents[mid - INT_GET(leaf1->hdr.count, ARCH_CONVERT)].hashval, ARCH_CONVERT);
+ else
+ midhash = INT_GET(leaf1->ents[mid].hashval, ARCH_CONVERT);
+ isleft = args->hashval <= midhash;
+ }
+ /*
+ * If the old count is even then the new count is odd, so there's
+ * no preferred side for the new entry.
+ * Pick the left one.
+ */
+ else
+ isleft = 1;
+ /*
+ * Calculate moved entry count. Positive means left-to-right,
+ * negative means right-to-left. Then move the entries.
+ */
+ count = INT_GET(leaf1->hdr.count, ARCH_CONVERT) - mid + (isleft == 0);
+ if (count > 0)
+ xfs_dir2_leafn_moveents(args, blk1->bp,
+ INT_GET(leaf1->hdr.count, ARCH_CONVERT) - count, blk2->bp, 0, count);
+ else if (count < 0)
+ xfs_dir2_leafn_moveents(args, blk2->bp, 0, blk1->bp,
+ INT_GET(leaf1->hdr.count, ARCH_CONVERT), count);
+ ASSERT(INT_GET(leaf1->hdr.count, ARCH_CONVERT) + INT_GET(leaf2->hdr.count, ARCH_CONVERT) == oldsum);
+ ASSERT(INT_GET(leaf1->hdr.stale, ARCH_CONVERT) + INT_GET(leaf2->hdr.stale, ARCH_CONVERT) == oldstale);
+ /*
+ * Mark whether we're inserting into the old or new leaf.
+ */
+ if (INT_GET(leaf1->hdr.count, ARCH_CONVERT) < INT_GET(leaf2->hdr.count, ARCH_CONVERT))
+ state->inleaf = swap;
+ else if (INT_GET(leaf1->hdr.count, ARCH_CONVERT) > INT_GET(leaf2->hdr.count, ARCH_CONVERT))
+ state->inleaf = !swap;
+ else
+ state->inleaf =
+ swap ^ (blk1->index <= INT_GET(leaf1->hdr.count, ARCH_CONVERT));
+ /*
+ * Adjust the expected index for insertion.
+ */
+ if (!state->inleaf)
+ blk2->index = blk1->index - INT_GET(leaf1->hdr.count, ARCH_CONVERT);
+
+ /*
+ * Finally sanity check just to make sure we are not returning a negative index
+ */
+ if(blk2->index < 0) {
+ state->inleaf = 1;
+ blk2->index = 0;
+ cmn_err(CE_ALERT,
+ "xfs_dir2_leafn_rebalance: picked the wrong leaf? reverting orignal leaf: "
+ "blk1->index %d\n",
+ blk1->index);
+ }
+}
+
+/*
+ * Remove an entry from a node directory.
+ * This removes the leaf entry and the data entry,
+ * and updates the free block if necessary.
+ */
+static int /* error */
+xfs_dir2_leafn_remove(
+ xfs_da_args_t *args, /* operation arguments */
+ xfs_dabuf_t *bp, /* leaf buffer */
+ int index, /* leaf entry index */
+ xfs_da_state_blk_t *dblk, /* data block */
+ int *rval) /* resulting block needs join */
+{
+ xfs_dir2_data_t *data; /* data block structure */
+ xfs_dir2_db_t db; /* data block number */
+ xfs_dabuf_t *dbp; /* data block buffer */
+ xfs_dir2_data_entry_t *dep; /* data block entry */
+ xfs_inode_t *dp; /* incore directory inode */
+ xfs_dir2_leaf_t *leaf; /* leaf structure */
+ xfs_dir2_leaf_entry_t *lep; /* leaf entry */
+ int longest; /* longest data free entry */
+ int off; /* data block entry offset */
+ xfs_mount_t *mp; /* filesystem mount point */
+ int needlog; /* need to log data header */
+ int needscan; /* need to rescan data frees */
+ xfs_trans_t *tp; /* transaction pointer */
+
+ xfs_dir2_trace_args_sb("leafn_remove", args, index, bp);
+ dp = args->dp;
+ tp = args->trans;
+ mp = dp->i_mount;
+ leaf = bp->data;
+ ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) == XFS_DIR2_LEAFN_MAGIC);
+ /*
+ * Point to the entry we're removing.
+ */
+ lep = &leaf->ents[index];
+ /*
+ * Extract the data block and offset from the entry.
+ */
+ db = XFS_DIR2_DATAPTR_TO_DB(mp, INT_GET(lep->address, ARCH_CONVERT));
+ ASSERT(dblk->blkno == db);
+ off = XFS_DIR2_DATAPTR_TO_OFF(mp, INT_GET(lep->address, ARCH_CONVERT));
+ ASSERT(dblk->index == off);
+ /*
+ * Kill the leaf entry by marking it stale.
+ * Log the leaf block changes.
+ */
+ INT_MOD(leaf->hdr.stale, ARCH_CONVERT, +1);
+ xfs_dir2_leaf_log_header(tp, bp);
+ INT_SET(lep->address, ARCH_CONVERT, XFS_DIR2_NULL_DATAPTR);
+ xfs_dir2_leaf_log_ents(tp, bp, index, index);
+ /*
+ * Make the data entry free. Keep track of the longest freespace
+ * in the data block in case it changes.
+ */
+ dbp = dblk->bp;
+ data = dbp->data;
+ dep = (xfs_dir2_data_entry_t *)((char *)data + off);
+ longest = INT_GET(data->hdr.bestfree[0].length, ARCH_CONVERT);
+ needlog = needscan = 0;
+ xfs_dir2_data_make_free(tp, dbp, off,
+ XFS_DIR2_DATA_ENTSIZE(dep->namelen), &needlog, &needscan);
+ /*
+ * Rescan the data block freespaces for bestfree.
+ * Log the data block header if needed.
+ */
+ if (needscan)
+ xfs_dir2_data_freescan(mp, data, &needlog, NULL);
+ if (needlog)
+ xfs_dir2_data_log_header(tp, dbp);
+ xfs_dir2_data_check(dp, dbp);
+ /*
+ * If the longest data block freespace changes, need to update
+ * the corresponding freeblock entry.
+ */
+ if (longest < INT_GET(data->hdr.bestfree[0].length, ARCH_CONVERT)) {
+ int error; /* error return value */
+ xfs_dabuf_t *fbp; /* freeblock buffer */
+ xfs_dir2_db_t fdb; /* freeblock block number */
+ int findex; /* index in freeblock entries */
+ xfs_dir2_free_t *free; /* freeblock structure */
+ int logfree; /* need to log free entry */
+
+ /*
+ * Convert the data block number to a free block,
+ * read in the free block.
+ */
+ fdb = XFS_DIR2_DB_TO_FDB(mp, db);
+ if ((error = xfs_da_read_buf(tp, dp, XFS_DIR2_DB_TO_DA(mp, fdb),
+ -1, &fbp, XFS_DATA_FORK))) {
+ return error;
+ }
+ free = fbp->data;
+ ASSERT(INT_GET(free->hdr.magic, ARCH_CONVERT) == XFS_DIR2_FREE_MAGIC);
+ ASSERT(INT_GET(free->hdr.firstdb, ARCH_CONVERT) ==
+ XFS_DIR2_MAX_FREE_BESTS(mp) *
+ (fdb - XFS_DIR2_FREE_FIRSTDB(mp)));
+ /*
+ * Calculate which entry we need to fix.
+ */
+ findex = XFS_DIR2_DB_TO_FDINDEX(mp, db);
+ longest = INT_GET(data->hdr.bestfree[0].length, ARCH_CONVERT);
+ /*
+ * If the data block is now empty we can get rid of it
+ * (usually).
+ */
+ if (longest == mp->m_dirblksize - (uint)sizeof(data->hdr)) {
+ /*
+ * Try to punch out the data block.
+ */
+ error = xfs_dir2_shrink_inode(args, db, dbp);
+ if (error == 0) {
+ dblk->bp = NULL;
+ data = NULL;
+ }
+ /*
+ * We can get ENOSPC if there's no space reservation.
+ * In this case just drop the buffer and some one else
+ * will eventually get rid of the empty block.
+ */
+ else if (error == ENOSPC && args->total == 0)
+ xfs_da_buf_done(dbp);
+ else
+ return error;
+ }
+ /*
+ * If we got rid of the data block, we can eliminate that entry
+ * in the free block.
+ */
+ if (data == NULL) {
+ /*
+ * One less used entry in the free table.
+ */
+ INT_MOD(free->hdr.nused, ARCH_CONVERT, -1);
+ xfs_dir2_free_log_header(tp, fbp);
+ /*
+ * If this was the last entry in the table, we can
+ * trim the table size back. There might be other
+ * entries at the end referring to non-existent
+ * data blocks, get those too.
+ */
+ if (findex == INT_GET(free->hdr.nvalid, ARCH_CONVERT) - 1) {
+ int i; /* free entry index */
+
+ for (i = findex - 1;
+ i >= 0 && INT_GET(free->bests[i], ARCH_CONVERT) == NULLDATAOFF;
+ i--)
+ continue;
+ INT_SET(free->hdr.nvalid, ARCH_CONVERT, i + 1);
+ logfree = 0;
+ }
+ /*
+ * Not the last entry, just punch it out.
+ */
+ else {
+ INT_SET(free->bests[findex], ARCH_CONVERT, NULLDATAOFF);
+ logfree = 1;
+ }
+ /*
+ * If there are no useful entries left in the block,
+ * get rid of the block if we can.
+ */
+ if (INT_ISZERO(free->hdr.nused, ARCH_CONVERT)) {
+ error = xfs_dir2_shrink_inode(args, fdb, fbp);
+ if (error == 0) {
+ fbp = NULL;
+ logfree = 0;
+ } else if (error != ENOSPC || args->total != 0)
+ return error;
+ /*
+ * It's possible to get ENOSPC if there is no
+ * space reservation. In this case some one
+ * else will eventually get rid of this block.
+ */
+ }
+ }
+ /*
+ * Data block is not empty, just set the free entry to
+ * the new value.
+ */
+ else {
+ INT_SET(free->bests[findex], ARCH_CONVERT, longest);
+ logfree = 1;
+ }
+ /*
+ * Log the free entry that changed, unless we got rid of it.
+ */
+ if (logfree)
+ xfs_dir2_free_log_bests(tp, fbp, findex, findex);
+ /*
+ * Drop the buffer if we still have it.
+ */
+ if (fbp)
+ xfs_da_buf_done(fbp);
+ }
+ xfs_dir2_leafn_check(dp, bp);
+ /*
+ * Return indication of whether this leaf block is emtpy enough
+ * to justify trying to join it with a neighbor.
+ */
+ *rval =
+ ((uint)sizeof(leaf->hdr) +
+ (uint)sizeof(leaf->ents[0]) *
+ (INT_GET(leaf->hdr.count, ARCH_CONVERT) - INT_GET(leaf->hdr.stale, ARCH_CONVERT))) <
+ mp->m_dir_magicpct;
+ return 0;
+}
+
+/*
+ * Split the leaf entries in the old block into old and new blocks.
+ */
+int /* error */
+xfs_dir2_leafn_split(
+ xfs_da_state_t *state, /* btree cursor */
+ xfs_da_state_blk_t *oldblk, /* original block */
+ xfs_da_state_blk_t *newblk) /* newly created block */
+{
+ xfs_da_args_t *args; /* operation arguments */
+ xfs_dablk_t blkno; /* new leaf block number */
+ int error; /* error return value */
+ xfs_mount_t *mp; /* filesystem mount point */
+
+ /*
+ * Allocate space for a new leaf node.
+ */
+ args = state->args;
+ mp = args->dp->i_mount;
+ ASSERT(args != NULL);
+ ASSERT(oldblk->magic == XFS_DIR2_LEAFN_MAGIC);
+ error = xfs_da_grow_inode(args, &blkno);
+ if (error) {
+ return error;
+ }
+ /*
+ * Initialize the new leaf block.
+ */
+ error = xfs_dir2_leaf_init(args, XFS_DIR2_DA_TO_DB(mp, blkno),
+ &newblk->bp, XFS_DIR2_LEAFN_MAGIC);
+ if (error) {
+ return error;
+ }
+ newblk->blkno = blkno;
+ newblk->magic = XFS_DIR2_LEAFN_MAGIC;
+ /*
+ * Rebalance the entries across the two leaves, link the new
+ * block into the leaves.
+ */
+ xfs_dir2_leafn_rebalance(state, oldblk, newblk);
+ error = xfs_da_blk_link(state, oldblk, newblk);
+ if (error) {
+ return error;
+ }
+ /*
+ * Insert the new entry in the correct block.
+ */
+ if (state->inleaf)
+ error = xfs_dir2_leafn_add(oldblk->bp, args, oldblk->index);
+ else
+ error = xfs_dir2_leafn_add(newblk->bp, args, newblk->index);
+ /*
+ * Update last hashval in each block since we added the name.
+ */
+ oldblk->hashval = xfs_dir2_leafn_lasthash(oldblk->bp, NULL);
+ newblk->hashval = xfs_dir2_leafn_lasthash(newblk->bp, NULL);
+ xfs_dir2_leafn_check(args->dp, oldblk->bp);
+ xfs_dir2_leafn_check(args->dp, newblk->bp);
+ return error;
+}
+
+/*
+ * Check a leaf block and its neighbors to see if the block should be
+ * collapsed into one or the other neighbor. Always keep the block
+ * with the smaller block number.
+ * If the current block is over 50% full, don't try to join it, return 0.
+ * If the block is empty, fill in the state structure and return 2.
+ * If it can be collapsed, fill in the state structure and return 1.
+ * If nothing can be done, return 0.
+ */
+int /* error */
+xfs_dir2_leafn_toosmall(
+ xfs_da_state_t *state, /* btree cursor */
+ int *action) /* resulting action to take */
+{
+ xfs_da_state_blk_t *blk; /* leaf block */
+ xfs_dablk_t blkno; /* leaf block number */
+ xfs_dabuf_t *bp; /* leaf buffer */
+ int bytes; /* bytes in use */
+ int count; /* leaf live entry count */
+ int error; /* error return value */
+ int forward; /* sibling block direction */
+ int i; /* sibling counter */
+ xfs_da_blkinfo_t *info; /* leaf block header */
+ xfs_dir2_leaf_t *leaf; /* leaf structure */
+ int rval; /* result from path_shift */
+
+ /*
+ * Check for the degenerate case of the block being over 50% full.
+ * If so, it's not worth even looking to see if we might be able
+ * to coalesce with a sibling.
+ */
+ blk = &state->path.blk[state->path.active - 1];
+ info = blk->bp->data;
+ ASSERT(INT_GET(info->magic, ARCH_CONVERT) == XFS_DIR2_LEAFN_MAGIC);
+ leaf = (xfs_dir2_leaf_t *)info;
+ count = INT_GET(leaf->hdr.count, ARCH_CONVERT) - INT_GET(leaf->hdr.stale, ARCH_CONVERT);
+ bytes = (uint)sizeof(leaf->hdr) + count * (uint)sizeof(leaf->ents[0]);
+ if (bytes > (state->blocksize >> 1)) {
+ /*
+ * Blk over 50%, don't try to join.
+ */
+ *action = 0;
+ return 0;
+ }
+ /*
+ * Check for the degenerate case of the block being empty.
+ * If the block is empty, we'll simply delete it, no need to
+ * coalesce it with a sibling block. We choose (arbitrarily)
+ * to merge with the forward block unless it is NULL.
+ */
+ if (count == 0) {
+ /*
+ * Make altpath point to the block we want to keep and
+ * path point to the block we want to drop (this one).
+ */
+ forward = !INT_ISZERO(info->forw, ARCH_CONVERT);
+ memcpy(&state->altpath, &state->path, sizeof(state->path));
+ error = xfs_da_path_shift(state, &state->altpath, forward, 0,
+ &rval);
+ if (error)
+ return error;
+ *action = rval ? 2 : 0;
+ return 0;
+ }
+ /*
+ * Examine each sibling block to see if we can coalesce with
+ * at least 25% free space to spare. We need to figure out
+ * whether to merge with the forward or the backward block.
+ * We prefer coalescing with the lower numbered sibling so as
+ * to shrink a directory over time.
+ */
+ forward = INT_GET(info->forw, ARCH_CONVERT) < INT_GET(info->back, ARCH_CONVERT);
+ for (i = 0, bp = NULL; i < 2; forward = !forward, i++) {
+ blkno = forward ?INT_GET( info->forw, ARCH_CONVERT) : INT_GET(info->back, ARCH_CONVERT);
+ if (blkno == 0)
+ continue;
+ /*
+ * Read the sibling leaf block.
+ */
+ if ((error =
+ xfs_da_read_buf(state->args->trans, state->args->dp, blkno,
+ -1, &bp, XFS_DATA_FORK))) {
+ return error;
+ }
+ ASSERT(bp != NULL);
+ /*
+ * Count bytes in the two blocks combined.
+ */
+ leaf = (xfs_dir2_leaf_t *)info;
+ count = INT_GET(leaf->hdr.count, ARCH_CONVERT) - INT_GET(leaf->hdr.stale, ARCH_CONVERT);
+ bytes = state->blocksize - (state->blocksize >> 2);
+ leaf = bp->data;
+ ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) == XFS_DIR2_LEAFN_MAGIC);
+ count += INT_GET(leaf->hdr.count, ARCH_CONVERT) - INT_GET(leaf->hdr.stale, ARCH_CONVERT);
+ bytes -= count * (uint)sizeof(leaf->ents[0]);
+ /*
+ * Fits with at least 25% to spare.
+ */
+ if (bytes >= 0)
+ break;
+ xfs_da_brelse(state->args->trans, bp);
+ }
+ /*
+ * Didn't like either block, give up.
+ */
+ if (i >= 2) {
+ *action = 0;
+ return 0;
+ }
+ /*
+ * Done with the sibling leaf block here, drop the dabuf
+ * so path_shift can get it.
+ */
+ xfs_da_buf_done(bp);
+ /*
+ * Make altpath point to the block we want to keep (the lower
+ * numbered block) and path point to the block we want to drop.
+ */
+ memcpy(&state->altpath, &state->path, sizeof(state->path));
+ if (blkno < blk->blkno)
+ error = xfs_da_path_shift(state, &state->altpath, forward, 0,
+ &rval);
+ else
+ error = xfs_da_path_shift(state, &state->path, forward, 0,
+ &rval);
+ if (error) {
+ return error;
+ }
+ *action = rval ? 0 : 1;
+ return 0;
+}
+
+/*
+ * Move all the leaf entries from drop_blk to save_blk.
+ * This is done as part of a join operation.
+ */
+void
+xfs_dir2_leafn_unbalance(
+ xfs_da_state_t *state, /* cursor */
+ xfs_da_state_blk_t *drop_blk, /* dead block */
+ xfs_da_state_blk_t *save_blk) /* surviving block */
+{
+ xfs_da_args_t *args; /* operation arguments */
+ xfs_dir2_leaf_t *drop_leaf; /* dead leaf structure */
+ xfs_dir2_leaf_t *save_leaf; /* surviving leaf structure */
+
+ args = state->args;
+ ASSERT(drop_blk->magic == XFS_DIR2_LEAFN_MAGIC);
+ ASSERT(save_blk->magic == XFS_DIR2_LEAFN_MAGIC);
+ drop_leaf = drop_blk->bp->data;
+ save_leaf = save_blk->bp->data;
+ ASSERT(INT_GET(drop_leaf->hdr.info.magic, ARCH_CONVERT) == XFS_DIR2_LEAFN_MAGIC);
+ ASSERT(INT_GET(save_leaf->hdr.info.magic, ARCH_CONVERT) == XFS_DIR2_LEAFN_MAGIC);
+ /*
+ * If there are any stale leaf entries, take this opportunity
+ * to purge them.
+ */
+ if (INT_GET(drop_leaf->hdr.stale, ARCH_CONVERT))
+ xfs_dir2_leaf_compact(args, drop_blk->bp);
+ if (INT_GET(save_leaf->hdr.stale, ARCH_CONVERT))
+ xfs_dir2_leaf_compact(args, save_blk->bp);
+ /*
+ * Move the entries from drop to the appropriate end of save.
+ */
+ drop_blk->hashval = INT_GET(drop_leaf->ents[INT_GET(drop_leaf->hdr.count, ARCH_CONVERT) - 1].hashval, ARCH_CONVERT);
+ if (xfs_dir2_leafn_order(save_blk->bp, drop_blk->bp))
+ xfs_dir2_leafn_moveents(args, drop_blk->bp, 0, save_blk->bp, 0,
+ INT_GET(drop_leaf->hdr.count, ARCH_CONVERT));
+ else
+ xfs_dir2_leafn_moveents(args, drop_blk->bp, 0, save_blk->bp,
+ INT_GET(save_leaf->hdr.count, ARCH_CONVERT), INT_GET(drop_leaf->hdr.count, ARCH_CONVERT));
+ save_blk->hashval = INT_GET(save_leaf->ents[INT_GET(save_leaf->hdr.count, ARCH_CONVERT) - 1].hashval, ARCH_CONVERT);
+ xfs_dir2_leafn_check(args->dp, save_blk->bp);
+}
+
+/*
+ * Top-level node form directory addname routine.
+ */
+int /* error */
+xfs_dir2_node_addname(
+ xfs_da_args_t *args) /* operation arguments */
+{
+ xfs_da_state_blk_t *blk; /* leaf block for insert */
+ int error; /* error return value */
+ int rval; /* sub-return value */
+ xfs_da_state_t *state; /* btree cursor */
+
+ xfs_dir2_trace_args("node_addname", args);
+ /*
+ * Allocate and initialize the state (btree cursor).
+ */
+ state = xfs_da_state_alloc();
+ state->args = args;
+ state->mp = args->dp->i_mount;
+ state->blocksize = state->mp->m_dirblksize;
+ state->node_ents = state->mp->m_dir_node_ents;
+ /*
+ * Look up the name. We're not supposed to find it, but
+ * this gives us the insertion point.
+ */
+ error = xfs_da_node_lookup_int(state, &rval);
+ if (error)
+ rval = error;
+ if (rval != ENOENT) {
+ goto done;
+ }
+ /*
+ * Add the data entry to a data block.
+ * Extravalid is set to a freeblock found by lookup.
+ */
+ rval = xfs_dir2_node_addname_int(args,
+ state->extravalid ? &state->extrablk : NULL);
+ if (rval) {
+ goto done;
+ }
+ blk = &state->path.blk[state->path.active - 1];
+ ASSERT(blk->magic == XFS_DIR2_LEAFN_MAGIC);
+ /*
+ * Add the new leaf entry.
+ */
+ rval = xfs_dir2_leafn_add(blk->bp, args, blk->index);
+ if (rval == 0) {
+ /*
+ * It worked, fix the hash values up the btree.
+ */
+ if (!args->justcheck)
+ xfs_da_fixhashpath(state, &state->path);
+ } else {
+ /*
+ * It didn't work, we need to split the leaf block.
+ */
+ if (args->total == 0) {
+ ASSERT(rval == ENOSPC);
+ goto done;
+ }
+ /*
+ * Split the leaf block and insert the new entry.
+ */
+ rval = xfs_da_split(state);
+ }
+done:
+ xfs_da_state_free(state);
+ return rval;
+}
+
+/*
+ * Add the data entry for a node-format directory name addition.
+ * The leaf entry is added in xfs_dir2_leafn_add.
+ * We may enter with a freespace block that the lookup found.
+ */
+static int /* error */
+xfs_dir2_node_addname_int(
+ xfs_da_args_t *args, /* operation arguments */
+ xfs_da_state_blk_t *fblk) /* optional freespace block */
+{
+ xfs_dir2_data_t *data; /* data block structure */
+ xfs_dir2_db_t dbno; /* data block number */
+ xfs_dabuf_t *dbp; /* data block buffer */
+ xfs_dir2_data_entry_t *dep; /* data entry pointer */
+ xfs_inode_t *dp; /* incore directory inode */
+ xfs_dir2_data_unused_t *dup; /* data unused entry pointer */
+ int error; /* error return value */
+ xfs_dir2_db_t fbno; /* freespace block number */
+ xfs_dabuf_t *fbp; /* freespace buffer */
+ int findex; /* freespace entry index */
+ xfs_dir2_free_t *free=NULL; /* freespace block structure */
+ xfs_dir2_db_t ifbno; /* initial freespace block no */
+ xfs_dir2_db_t lastfbno=0; /* highest freespace block no */
+ int length; /* length of the new entry */
+ int logfree; /* need to log free entry */
+ xfs_mount_t *mp; /* filesystem mount point */
+ int needlog; /* need to log data header */
+ int needscan; /* need to rescan data frees */
+ xfs_dir2_data_off_t *tagp; /* data entry tag pointer */
+ xfs_trans_t *tp; /* transaction pointer */
+
+ dp = args->dp;
+ mp = dp->i_mount;
+ tp = args->trans;
+ length = XFS_DIR2_DATA_ENTSIZE(args->namelen);
+ /*
+ * If we came in with a freespace block that means that lookup
+ * found an entry with our hash value. This is the freespace
+ * block for that data entry.
+ */
+ if (fblk) {
+ fbp = fblk->bp;
+ /*
+ * Remember initial freespace block number.
+ */
+ ifbno = fblk->blkno;
+ free = fbp->data;
+ ASSERT(INT_GET(free->hdr.magic, ARCH_CONVERT) == XFS_DIR2_FREE_MAGIC);
+ findex = fblk->index;
+ /*
+ * This means the free entry showed that the data block had
+ * space for our entry, so we remembered it.
+ * Use that data block.
+ */
+ if (findex >= 0) {
+ ASSERT(findex < INT_GET(free->hdr.nvalid, ARCH_CONVERT));
+ ASSERT(INT_GET(free->bests[findex], ARCH_CONVERT) != NULLDATAOFF);
+ ASSERT(INT_GET(free->bests[findex], ARCH_CONVERT) >= length);
+ dbno = INT_GET(free->hdr.firstdb, ARCH_CONVERT) + findex;
+ }
+ /*
+ * The data block looked at didn't have enough room.
+ * We'll start at the beginning of the freespace entries.
+ */
+ else {
+ dbno = -1;
+ findex = 0;
+ }
+ }
+ /*
+ * Didn't come in with a freespace block, so don't have a data block.
+ */
+ else {
+ ifbno = dbno = -1;
+ fbp = NULL;
+ findex = 0;
+ }
+ /*
+ * If we don't have a data block yet, we're going to scan the
+ * freespace blocks looking for one. Figure out what the
+ * highest freespace block number is.
+ */
+ if (dbno == -1) {
+ xfs_fileoff_t fo; /* freespace block number */
+
+ if ((error = xfs_bmap_last_offset(tp, dp, &fo, XFS_DATA_FORK)))
+ return error;
+ lastfbno = XFS_DIR2_DA_TO_DB(mp, (xfs_dablk_t)fo);
+ fbno = ifbno;
+ }
+ /*
+ * While we haven't identified a data block, search the freeblock
+ * data for a good data block. If we find a null freeblock entry,
+ * indicating a hole in the data blocks, remember that.
+ */
+ while (dbno == -1) {
+ /*
+ * If we don't have a freeblock in hand, get the next one.
+ */
+ if (fbp == NULL) {
+ /*
+ * Happens the first time through unless lookup gave
+ * us a freespace block to start with.
+ */
+ if (++fbno == 0)
+ fbno = XFS_DIR2_FREE_FIRSTDB(mp);
+ /*
+ * If it's ifbno we already looked at it.
+ */
+ if (fbno == ifbno)
+ fbno++;
+ /*
+ * If it's off the end we're done.
+ */
+ if (fbno >= lastfbno)
+ break;
+ /*
+ * Read the block. There can be holes in the
+ * freespace blocks, so this might not succeed.
+ * This should be really rare, so there's no reason
+ * to avoid it.
+ */
+ if ((error = xfs_da_read_buf(tp, dp,
+ XFS_DIR2_DB_TO_DA(mp, fbno), -2, &fbp,
+ XFS_DATA_FORK))) {
+ return error;
+ }
+ if (unlikely(fbp == NULL)) {
+ continue;
+ }
+ free = fbp->data;
+ ASSERT(INT_GET(free->hdr.magic, ARCH_CONVERT) == XFS_DIR2_FREE_MAGIC);
+ findex = 0;
+ }
+ /*
+ * Look at the current free entry. Is it good enough?
+ */
+ if (INT_GET(free->bests[findex], ARCH_CONVERT) != NULLDATAOFF &&
+ INT_GET(free->bests[findex], ARCH_CONVERT) >= length)
+ dbno = INT_GET(free->hdr.firstdb, ARCH_CONVERT) + findex;
+ else {
+ /*
+ * Are we done with the freeblock?
+ */
+ if (++findex == INT_GET(free->hdr.nvalid, ARCH_CONVERT)) {
+ /*
+ * Drop the block.
+ */
+ xfs_da_brelse(tp, fbp);
+ fbp = NULL;
+ if (fblk && fblk->bp)
+ fblk->bp = NULL;
+ }
+ }
+ }
+ /*
+ * If we don't have a data block, we need to allocate one and make
+ * the freespace entries refer to it.
+ */
+ if (unlikely(dbno == -1)) {
+ /*
+ * Not allowed to allocate, return failure.
+ */
+ if (args->justcheck || args->total == 0) {
+ /*
+ * Drop the freespace buffer unless it came from our
+ * caller.
+ */
+ if ((fblk == NULL || fblk->bp == NULL) && fbp != NULL)
+ xfs_da_buf_done(fbp);
+ return XFS_ERROR(ENOSPC);
+ }
+ /*
+ * Allocate and initialize the new data block.
+ */
+ if (unlikely((error = xfs_dir2_grow_inode(args,
+ XFS_DIR2_DATA_SPACE,
+ &dbno)) ||
+ (error = xfs_dir2_data_init(args, dbno, &dbp)))) {
+ /*
+ * Drop the freespace buffer unless it came from our
+ * caller.
+ */
+ if ((fblk == NULL || fblk->bp == NULL) && fbp != NULL)
+ xfs_da_buf_done(fbp);
+ return error;
+ }
+ /*
+ * If (somehow) we have a freespace block, get rid of it.
+ */
+ if (fbp)
+ xfs_da_brelse(tp, fbp);
+ if (fblk && fblk->bp)
+ fblk->bp = NULL;
+
+ /*
+ * Get the freespace block corresponding to the data block
+ * that was just allocated.
+ */
+ fbno = XFS_DIR2_DB_TO_FDB(mp, dbno);
+ if (unlikely(error = xfs_da_read_buf(tp, dp,
+ XFS_DIR2_DB_TO_DA(mp, fbno), -2, &fbp,
+ XFS_DATA_FORK))) {
+ xfs_da_buf_done(dbp);
+ return error;
+ }
+ /*
+ * If there wasn't a freespace block, the read will
+ * return a NULL fbp. Allocate and initialize a new one.
+ */
+ if( fbp == NULL ) {
+ if ((error = xfs_dir2_grow_inode(args, XFS_DIR2_FREE_SPACE,
+ &fbno))) {
+ return error;
+ }
+
+ if (unlikely(XFS_DIR2_DB_TO_FDB(mp, dbno) != fbno)) {
+ cmn_err(CE_ALERT,
+ "xfs_dir2_node_addname_int: dir ino "
+ "%llu needed freesp block %lld for\n"
+ " data block %lld, got %lld\n"
+ " ifbno %llu lastfbno %d\n",
+ (unsigned long long)dp->i_ino,
+ (long long)XFS_DIR2_DB_TO_FDB(mp, dbno),
+ (long long)dbno, (long long)fbno,
+ (unsigned long long)ifbno, lastfbno);
+ if (fblk) {
+ cmn_err(CE_ALERT,
+ " fblk 0x%p blkno %llu "
+ "index %d magic 0x%x\n",
+ fblk,
+ (unsigned long long)fblk->blkno,
+ fblk->index,
+ fblk->magic);
+ } else {
+ cmn_err(CE_ALERT,
+ " ... fblk is NULL\n");
+ }
+ XFS_ERROR_REPORT("xfs_dir2_node_addname_int",
+ XFS_ERRLEVEL_LOW, mp);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+
+ /*
+ * Get a buffer for the new block.
+ */
+ if ((error = xfs_da_get_buf(tp, dp,
+ XFS_DIR2_DB_TO_DA(mp, fbno),
+ -1, &fbp, XFS_DATA_FORK))) {
+ return error;
+ }
+ ASSERT(fbp != NULL);
+
+ /*
+ * Initialize the new block to be empty, and remember
+ * its first slot as our empty slot.
+ */
+ free = fbp->data;
+ INT_SET(free->hdr.magic, ARCH_CONVERT, XFS_DIR2_FREE_MAGIC);
+ INT_SET(free->hdr.firstdb, ARCH_CONVERT,
+ (fbno - XFS_DIR2_FREE_FIRSTDB(mp)) *
+ XFS_DIR2_MAX_FREE_BESTS(mp));
+ INT_ZERO(free->hdr.nvalid, ARCH_CONVERT);
+ INT_ZERO(free->hdr.nused, ARCH_CONVERT);
+ } else {
+ free = fbp->data;
+ ASSERT(INT_GET(free->hdr.magic, ARCH_CONVERT) == XFS_DIR2_FREE_MAGIC);
+ }
+
+ /*
+ * Set the freespace block index from the data block number.
+ */
+ findex = XFS_DIR2_DB_TO_FDINDEX(mp, dbno);
+ /*
+ * If it's after the end of the current entries in the
+ * freespace block, extend that table.
+ */
+ if (findex >= INT_GET(free->hdr.nvalid, ARCH_CONVERT)) {
+ ASSERT(findex < XFS_DIR2_MAX_FREE_BESTS(mp));
+ INT_SET(free->hdr.nvalid, ARCH_CONVERT, findex + 1);
+ /*
+ * Tag new entry so nused will go up.
+ */
+ INT_SET(free->bests[findex], ARCH_CONVERT, NULLDATAOFF);
+ }
+ /*
+ * If this entry was for an empty data block
+ * (this should always be true) then update the header.
+ */
+ if (INT_GET(free->bests[findex], ARCH_CONVERT) == NULLDATAOFF) {
+ INT_MOD(free->hdr.nused, ARCH_CONVERT, +1);
+ xfs_dir2_free_log_header(tp, fbp);
+ }
+ /*
+ * Update the real value in the table.
+ * We haven't allocated the data entry yet so this will
+ * change again.
+ */
+ data = dbp->data;
+ INT_COPY(free->bests[findex], data->hdr.bestfree[0].length, ARCH_CONVERT);
+ logfree = 1;
+ }
+ /*
+ * We had a data block so we don't have to make a new one.
+ */
+ else {
+ /*
+ * If just checking, we succeeded.
+ */
+ if (args->justcheck) {
+ if ((fblk == NULL || fblk->bp == NULL) && fbp != NULL)
+ xfs_da_buf_done(fbp);
+ return 0;
+ }
+ /*
+ * Read the data block in.
+ */
+ if (unlikely(
+ error = xfs_da_read_buf(tp, dp, XFS_DIR2_DB_TO_DA(mp, dbno),
+ -1, &dbp, XFS_DATA_FORK))) {
+ if ((fblk == NULL || fblk->bp == NULL) && fbp != NULL)
+ xfs_da_buf_done(fbp);
+ return error;
+ }
+ data = dbp->data;
+ logfree = 0;
+ }
+ ASSERT(INT_GET(data->hdr.bestfree[0].length, ARCH_CONVERT) >= length);
+ /*
+ * Point to the existing unused space.
+ */
+ dup = (xfs_dir2_data_unused_t *)
+ ((char *)data + INT_GET(data->hdr.bestfree[0].offset, ARCH_CONVERT));
+ needscan = needlog = 0;
+ /*
+ * Mark the first part of the unused space, inuse for us.
+ */
+ xfs_dir2_data_use_free(tp, dbp, dup,
+ (xfs_dir2_data_aoff_t)((char *)dup - (char *)data), length,
+ &needlog, &needscan);
+ /*
+ * Fill in the new entry and log it.
+ */
+ dep = (xfs_dir2_data_entry_t *)dup;
+ INT_SET(dep->inumber, ARCH_CONVERT, args->inumber);
+ dep->namelen = args->namelen;
+ memcpy(dep->name, args->name, dep->namelen);
+ tagp = XFS_DIR2_DATA_ENTRY_TAG_P(dep);
+ INT_SET(*tagp, ARCH_CONVERT, (xfs_dir2_data_off_t)((char *)dep - (char *)data));
+ xfs_dir2_data_log_entry(tp, dbp, dep);
+ /*
+ * Rescan the block for bestfree if needed.
+ */
+ if (needscan)
+ xfs_dir2_data_freescan(mp, data, &needlog, NULL);
+ /*
+ * Log the data block header if needed.
+ */
+ if (needlog)
+ xfs_dir2_data_log_header(tp, dbp);
+ /*
+ * If the freespace entry is now wrong, update it.
+ */
+ if (INT_GET(free->bests[findex], ARCH_CONVERT) != INT_GET(data->hdr.bestfree[0].length, ARCH_CONVERT)) {
+ INT_COPY(free->bests[findex], data->hdr.bestfree[0].length, ARCH_CONVERT);
+ logfree = 1;
+ }
+ /*
+ * Log the freespace entry if needed.
+ */
+ if (logfree)
+ xfs_dir2_free_log_bests(tp, fbp, findex, findex);
+ /*
+ * If the caller didn't hand us the freespace block, drop it.
+ */
+ if ((fblk == NULL || fblk->bp == NULL) && fbp != NULL)
+ xfs_da_buf_done(fbp);
+ /*
+ * Return the data block and offset in args, then drop the data block.
+ */
+ args->blkno = (xfs_dablk_t)dbno;
+ args->index = INT_GET(*tagp, ARCH_CONVERT);
+ xfs_da_buf_done(dbp);
+ return 0;
+}
+
+/*
+ * Lookup an entry in a node-format directory.
+ * All the real work happens in xfs_da_node_lookup_int.
+ * The only real output is the inode number of the entry.
+ */
+int /* error */
+xfs_dir2_node_lookup(
+ xfs_da_args_t *args) /* operation arguments */
+{
+ int error; /* error return value */
+ int i; /* btree level */
+ int rval; /* operation return value */
+ xfs_da_state_t *state; /* btree cursor */
+
+ xfs_dir2_trace_args("node_lookup", args);
+ /*
+ * Allocate and initialize the btree cursor.
+ */
+ state = xfs_da_state_alloc();
+ state->args = args;
+ state->mp = args->dp->i_mount;
+ state->blocksize = state->mp->m_dirblksize;
+ state->node_ents = state->mp->m_dir_node_ents;
+ /*
+ * Fill in the path to the entry in the cursor.
+ */
+ error = xfs_da_node_lookup_int(state, &rval);
+ if (error)
+ rval = error;
+ /*
+ * Release the btree blocks and leaf block.
+ */
+ for (i = 0; i < state->path.active; i++) {
+ xfs_da_brelse(args->trans, state->path.blk[i].bp);
+ state->path.blk[i].bp = NULL;
+ }
+ /*
+ * Release the data block if we have it.
+ */
+ if (state->extravalid && state->extrablk.bp) {
+ xfs_da_brelse(args->trans, state->extrablk.bp);
+ state->extrablk.bp = NULL;
+ }
+ xfs_da_state_free(state);
+ return rval;
+}
+
+/*
+ * Remove an entry from a node-format directory.
+ */
+int /* error */
+xfs_dir2_node_removename(
+ xfs_da_args_t *args) /* operation arguments */
+{
+ xfs_da_state_blk_t *blk; /* leaf block */
+ int error; /* error return value */
+ int rval; /* operation return value */
+ xfs_da_state_t *state; /* btree cursor */
+
+ xfs_dir2_trace_args("node_removename", args);
+ /*
+ * Allocate and initialize the btree cursor.
+ */
+ state = xfs_da_state_alloc();
+ state->args = args;
+ state->mp = args->dp->i_mount;
+ state->blocksize = state->mp->m_dirblksize;
+ state->node_ents = state->mp->m_dir_node_ents;
+ /*
+ * Look up the entry we're deleting, set up the cursor.
+ */
+ error = xfs_da_node_lookup_int(state, &rval);
+ if (error) {
+ rval = error;
+ }
+ /*
+ * Didn't find it, upper layer screwed up.
+ */
+ if (rval != EEXIST) {
+ xfs_da_state_free(state);
+ return rval;
+ }
+ blk = &state->path.blk[state->path.active - 1];
+ ASSERT(blk->magic == XFS_DIR2_LEAFN_MAGIC);
+ ASSERT(state->extravalid);
+ /*
+ * Remove the leaf and data entries.
+ * Extrablk refers to the data block.
+ */
+ error = xfs_dir2_leafn_remove(args, blk->bp, blk->index,
+ &state->extrablk, &rval);
+ if (error) {
+ return error;
+ }
+ /*
+ * Fix the hash values up the btree.
+ */
+ xfs_da_fixhashpath(state, &state->path);
+ /*
+ * If we need to join leaf blocks, do it.
+ */
+ if (rval && state->path.active > 1)
+ error = xfs_da_join(state);
+ /*
+ * If no errors so far, try conversion to leaf format.
+ */
+ if (!error)
+ error = xfs_dir2_node_to_leaf(state);
+ xfs_da_state_free(state);
+ return error;
+}
+
+/*
+ * Replace an entry's inode number in a node-format directory.
+ */
+int /* error */
+xfs_dir2_node_replace(
+ xfs_da_args_t *args) /* operation arguments */
+{
+ xfs_da_state_blk_t *blk; /* leaf block */
+ xfs_dir2_data_t *data; /* data block structure */
+ xfs_dir2_data_entry_t *dep; /* data entry changed */
+ int error; /* error return value */
+ int i; /* btree level */
+ xfs_ino_t inum; /* new inode number */
+ xfs_dir2_leaf_t *leaf; /* leaf structure */
+ xfs_dir2_leaf_entry_t *lep; /* leaf entry being changed */
+ int rval; /* internal return value */
+ xfs_da_state_t *state; /* btree cursor */
+
+ xfs_dir2_trace_args("node_replace", args);
+ /*
+ * Allocate and initialize the btree cursor.
+ */
+ state = xfs_da_state_alloc();
+ state->args = args;
+ state->mp = args->dp->i_mount;
+ state->blocksize = state->mp->m_dirblksize;
+ state->node_ents = state->mp->m_dir_node_ents;
+ inum = args->inumber;
+ /*
+ * Lookup the entry to change in the btree.
+ */
+ error = xfs_da_node_lookup_int(state, &rval);
+ if (error) {
+ rval = error;
+ }
+ /*
+ * It should be found, since the vnodeops layer has looked it up
+ * and locked it. But paranoia is good.
+ */
+ if (rval == EEXIST) {
+ /*
+ * Find the leaf entry.
+ */
+ blk = &state->path.blk[state->path.active - 1];
+ ASSERT(blk->magic == XFS_DIR2_LEAFN_MAGIC);
+ leaf = blk->bp->data;
+ lep = &leaf->ents[blk->index];
+ ASSERT(state->extravalid);
+ /*
+ * Point to the data entry.
+ */
+ data = state->extrablk.bp->data;
+ ASSERT(INT_GET(data->hdr.magic, ARCH_CONVERT) == XFS_DIR2_DATA_MAGIC);
+ dep = (xfs_dir2_data_entry_t *)
+ ((char *)data +
+ XFS_DIR2_DATAPTR_TO_OFF(state->mp, INT_GET(lep->address, ARCH_CONVERT)));
+ ASSERT(inum != INT_GET(dep->inumber, ARCH_CONVERT));
+ /*
+ * Fill in the new inode number and log the entry.
+ */
+ INT_SET(dep->inumber, ARCH_CONVERT, inum);
+ xfs_dir2_data_log_entry(args->trans, state->extrablk.bp, dep);
+ rval = 0;
+ }
+ /*
+ * Didn't find it, and we're holding a data block. Drop it.
+ */
+ else if (state->extravalid) {
+ xfs_da_brelse(args->trans, state->extrablk.bp);
+ state->extrablk.bp = NULL;
+ }
+ /*
+ * Release all the buffers in the cursor.
+ */
+ for (i = 0; i < state->path.active; i++) {
+ xfs_da_brelse(args->trans, state->path.blk[i].bp);
+ state->path.blk[i].bp = NULL;
+ }
+ xfs_da_state_free(state);
+ return rval;
+}
+
+/*
+ * Trim off a trailing empty freespace block.
+ * Return (in rvalp) 1 if we did it, 0 if not.
+ */
+int /* error */
+xfs_dir2_node_trim_free(
+ xfs_da_args_t *args, /* operation arguments */
+ xfs_fileoff_t fo, /* free block number */
+ int *rvalp) /* out: did something */
+{
+ xfs_dabuf_t *bp; /* freespace buffer */
+ xfs_inode_t *dp; /* incore directory inode */
+ int error; /* error return code */
+ xfs_dir2_free_t *free; /* freespace structure */
+ xfs_mount_t *mp; /* filesystem mount point */
+ xfs_trans_t *tp; /* transaction pointer */
+
+ dp = args->dp;
+ mp = dp->i_mount;
+ tp = args->trans;
+ /*
+ * Read the freespace block.
+ */
+ if (unlikely(error = xfs_da_read_buf(tp, dp, (xfs_dablk_t)fo, -2, &bp,
+ XFS_DATA_FORK))) {
+ return error;
+ }
+
+ /*
+ * There can be holes in freespace. If fo is a hole, there's
+ * nothing to do.
+ */
+ if (bp == NULL) {
+ return 0;
+ }
+ free = bp->data;
+ ASSERT(INT_GET(free->hdr.magic, ARCH_CONVERT) == XFS_DIR2_FREE_MAGIC);
+ /*
+ * If there are used entries, there's nothing to do.
+ */
+ if (INT_GET(free->hdr.nused, ARCH_CONVERT) > 0) {
+ xfs_da_brelse(tp, bp);
+ *rvalp = 0;
+ return 0;
+ }
+ /*
+ * Blow the block away.
+ */
+ if ((error =
+ xfs_dir2_shrink_inode(args, XFS_DIR2_DA_TO_DB(mp, (xfs_dablk_t)fo),
+ bp))) {
+ /*
+ * Can't fail with ENOSPC since that only happens with no
+ * space reservation, when breaking up an extent into two
+ * pieces. This is the last block of an extent.
+ */
+ ASSERT(error != ENOSPC);
+ xfs_da_brelse(tp, bp);
+ return error;
+ }
+ /*
+ * Return that we succeeded.
+ */
+ *rvalp = 1;
+ return 0;
+}
diff --git a/fs/xfs/xfs_dir2_node.h b/fs/xfs/xfs_dir2_node.h
new file mode 100644
index 00000000000000..96db420c7c5cda
--- /dev/null
+++ b/fs/xfs/xfs_dir2_node.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_DIR2_NODE_H__
+#define __XFS_DIR2_NODE_H__
+
+/*
+ * Directory version 2, btree node format structures
+ */
+
+struct uio;
+struct xfs_dabuf;
+struct xfs_da_args;
+struct xfs_da_state;
+struct xfs_da_state_blk;
+struct xfs_inode;
+struct xfs_trans;
+
+/*
+ * Constants.
+ */
+
+/*
+ * Offset of the freespace index.
+ */
+#define XFS_DIR2_FREE_SPACE 2
+#define XFS_DIR2_FREE_OFFSET (XFS_DIR2_FREE_SPACE * XFS_DIR2_SPACE_SIZE)
+#define XFS_DIR2_FREE_FIRSTDB(mp) \
+ XFS_DIR2_BYTE_TO_DB(mp, XFS_DIR2_FREE_OFFSET)
+
+#define XFS_DIR2_FREE_MAGIC 0x58443246 /* XD2F */
+
+/*
+ * Structures.
+ */
+typedef struct xfs_dir2_free_hdr {
+ __uint32_t magic; /* XFS_DIR2_FREE_MAGIC */
+ __int32_t firstdb; /* db of first entry */
+ __int32_t nvalid; /* count of valid entries */
+ __int32_t nused; /* count of used entries */
+} xfs_dir2_free_hdr_t;
+
+typedef struct xfs_dir2_free {
+ xfs_dir2_free_hdr_t hdr; /* block header */
+ xfs_dir2_data_off_t bests[1]; /* best free counts */
+ /* unused entries are -1 */
+} xfs_dir2_free_t;
+#define XFS_DIR2_MAX_FREE_BESTS(mp) \
+ (((mp)->m_dirblksize - (uint)sizeof(xfs_dir2_free_hdr_t)) / \
+ (uint)sizeof(xfs_dir2_data_off_t))
+
+/*
+ * Macros.
+ */
+
+/*
+ * Convert data space db to the corresponding free db.
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR2_DB_TO_FDB)
+xfs_dir2_db_t
+xfs_dir2_db_to_fdb(struct xfs_mount *mp, xfs_dir2_db_t db);
+#define XFS_DIR2_DB_TO_FDB(mp,db) xfs_dir2_db_to_fdb(mp, db)
+#else
+#define XFS_DIR2_DB_TO_FDB(mp,db) \
+ (XFS_DIR2_FREE_FIRSTDB(mp) + (db) / XFS_DIR2_MAX_FREE_BESTS(mp))
+#endif
+
+/*
+ * Convert data space db to the corresponding index in a free db.
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR2_DB_TO_FDINDEX)
+int
+xfs_dir2_db_to_fdindex(struct xfs_mount *mp, xfs_dir2_db_t db);
+#define XFS_DIR2_DB_TO_FDINDEX(mp,db) xfs_dir2_db_to_fdindex(mp, db)
+#else
+#define XFS_DIR2_DB_TO_FDINDEX(mp,db) ((db) % XFS_DIR2_MAX_FREE_BESTS(mp))
+#endif
+
+/*
+ * Functions.
+ */
+
+extern void
+ xfs_dir2_free_log_bests(struct xfs_trans *tp, struct xfs_dabuf *bp,
+ int first, int last);
+
+extern int
+ xfs_dir2_leaf_to_node(struct xfs_da_args *args, struct xfs_dabuf *lbp);
+
+extern xfs_dahash_t
+ xfs_dir2_leafn_lasthash(struct xfs_dabuf *bp, int *count);
+
+extern int
+ xfs_dir2_leafn_lookup_int(struct xfs_dabuf *bp,
+ struct xfs_da_args *args, int *indexp,
+ struct xfs_da_state *state);
+
+extern int
+ xfs_dir2_leafn_order(struct xfs_dabuf *leaf1_bp,
+ struct xfs_dabuf *leaf2_bp);
+
+extern int
+ xfs_dir2_leafn_split(struct xfs_da_state *state,
+ struct xfs_da_state_blk *oldblk,
+ struct xfs_da_state_blk *newblk);
+
+extern int
+ xfs_dir2_leafn_toosmall(struct xfs_da_state *state, int *action);
+
+extern void
+ xfs_dir2_leafn_unbalance(struct xfs_da_state *state,
+ struct xfs_da_state_blk *drop_blk,
+ struct xfs_da_state_blk *save_blk);
+
+extern int
+ xfs_dir2_node_addname(struct xfs_da_args *args);
+
+extern int
+ xfs_dir2_node_lookup(struct xfs_da_args *args);
+
+extern int
+ xfs_dir2_node_removename(struct xfs_da_args *args);
+
+extern int
+ xfs_dir2_node_replace(struct xfs_da_args *args);
+
+extern int
+ xfs_dir2_node_trim_free(struct xfs_da_args *args, xfs_fileoff_t fo,
+ int *rvalp);
+
+#endif /* __XFS_DIR2_NODE_H__ */
diff --git a/fs/xfs/xfs_dir2_sf.c b/fs/xfs/xfs_dir2_sf.c
new file mode 100644
index 00000000000000..9e9b3836eb27ff
--- /dev/null
+++ b/fs/xfs/xfs_dir2_sf.c
@@ -0,0 +1,1319 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+/*
+ * xfs_dir2_sf.c
+ * Shortform directory implementation for v2 directories.
+ */
+
+#include "xfs.h"
+
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode_item.h"
+#include "xfs_inode.h"
+#include "xfs_da_btree.h"
+#include "xfs_dir_leaf.h"
+#include "xfs_error.h"
+#include "xfs_dir2_data.h"
+#include "xfs_dir2_leaf.h"
+#include "xfs_dir2_block.h"
+#include "xfs_dir2_trace.h"
+
+/*
+ * Prototypes for internal functions.
+ */
+static void xfs_dir2_sf_addname_easy(xfs_da_args_t *args,
+ xfs_dir2_sf_entry_t *sfep,
+ xfs_dir2_data_aoff_t offset,
+ int new_isize);
+static void xfs_dir2_sf_addname_hard(xfs_da_args_t *args, int objchange,
+ int new_isize);
+static int xfs_dir2_sf_addname_pick(xfs_da_args_t *args, int objchange,
+ xfs_dir2_sf_entry_t **sfepp,
+ xfs_dir2_data_aoff_t *offsetp);
+#ifdef DEBUG
+static void xfs_dir2_sf_check(xfs_da_args_t *args);
+#else
+#define xfs_dir2_sf_check(args)
+#endif /* DEBUG */
+#if XFS_BIG_INUMS
+static void xfs_dir2_sf_toino4(xfs_da_args_t *args);
+static void xfs_dir2_sf_toino8(xfs_da_args_t *args);
+#endif /* XFS_BIG_INUMS */
+
+/*
+ * Given a block directory (dp/block), calculate its size as a shortform (sf)
+ * directory and a header for the sf directory, if it will fit it the
+ * space currently present in the inode. If it won't fit, the output
+ * size is too big (but not accurate).
+ */
+int /* size for sf form */
+xfs_dir2_block_sfsize(
+ xfs_inode_t *dp, /* incore inode pointer */
+ xfs_dir2_block_t *block, /* block directory data */
+ xfs_dir2_sf_hdr_t *sfhp) /* output: header for sf form */
+{
+ xfs_dir2_dataptr_t addr; /* data entry address */
+ xfs_dir2_leaf_entry_t *blp; /* leaf area of the block */
+ xfs_dir2_block_tail_t *btp; /* tail area of the block */
+ int count; /* shortform entry count */
+ xfs_dir2_data_entry_t *dep; /* data entry in the block */
+ int i; /* block entry index */
+ int i8count; /* count of big-inode entries */
+ int isdot; /* entry is "." */
+ int isdotdot; /* entry is ".." */
+ xfs_mount_t *mp; /* mount structure pointer */
+ int namelen; /* total name bytes */
+ xfs_ino_t parent; /* parent inode number */
+ int size=0; /* total computed size */
+
+ mp = dp->i_mount;
+
+ count = i8count = namelen = 0;
+ btp = XFS_DIR2_BLOCK_TAIL_P(mp, block);
+ blp = XFS_DIR2_BLOCK_LEAF_P_ARCH(btp, ARCH_CONVERT);
+
+ /*
+ * Iterate over the block's data entries by using the leaf pointers.
+ */
+ for (i = 0; i < INT_GET(btp->count, ARCH_CONVERT); i++) {
+ if ((addr = INT_GET(blp[i].address, ARCH_CONVERT)) == XFS_DIR2_NULL_DATAPTR)
+ continue;
+ /*
+ * Calculate the pointer to the entry at hand.
+ */
+ dep = (xfs_dir2_data_entry_t *)
+ ((char *)block + XFS_DIR2_DATAPTR_TO_OFF(mp, addr));
+ /*
+ * Detect . and .., so we can special-case them.
+ * . is not included in sf directories.
+ * .. is included by just the parent inode number.
+ */
+ isdot = dep->namelen == 1 && dep->name[0] == '.';
+ isdotdot =
+ dep->namelen == 2 &&
+ dep->name[0] == '.' && dep->name[1] == '.';
+#if XFS_BIG_INUMS
+ if (!isdot)
+ i8count += INT_GET(dep->inumber, ARCH_CONVERT) > XFS_DIR2_MAX_SHORT_INUM;
+#endif
+ if (!isdot && !isdotdot) {
+ count++;
+ namelen += dep->namelen;
+ } else if (isdotdot)
+ parent = INT_GET(dep->inumber, ARCH_CONVERT);
+ /*
+ * Calculate the new size, see if we should give up yet.
+ */
+ size = XFS_DIR2_SF_HDR_SIZE(i8count) + /* header */
+ count + /* namelen */
+ count * (uint)sizeof(xfs_dir2_sf_off_t) + /* offset */
+ namelen + /* name */
+ (i8count ? /* inumber */
+ (uint)sizeof(xfs_dir2_ino8_t) * count :
+ (uint)sizeof(xfs_dir2_ino4_t) * count);
+ if (size > XFS_IFORK_DSIZE(dp))
+ return size; /* size value is a failure */
+ }
+ /*
+ * Create the output header, if it worked.
+ */
+ sfhp->count = count;
+ sfhp->i8count = i8count;
+ XFS_DIR2_SF_PUT_INUMBER_ARCH((xfs_dir2_sf_t *)sfhp, &parent, &sfhp->parent, ARCH_CONVERT);
+ return size;
+}
+
+/*
+ * Convert a block format directory to shortform.
+ * Caller has already checked that it will fit, and built us a header.
+ */
+int /* error */
+xfs_dir2_block_to_sf(
+ xfs_da_args_t *args, /* operation arguments */
+ xfs_dabuf_t *bp, /* block buffer */
+ int size, /* shortform directory size */
+ xfs_dir2_sf_hdr_t *sfhp) /* shortform directory hdr */
+{
+ xfs_dir2_block_t *block; /* block structure */
+ xfs_dir2_block_tail_t *btp; /* block tail pointer */
+ xfs_dir2_data_entry_t *dep; /* data entry pointer */
+ xfs_inode_t *dp; /* incore directory inode */
+ xfs_dir2_data_unused_t *dup; /* unused data pointer */
+ char *endptr; /* end of data entries */
+ int error; /* error return value */
+ int logflags; /* inode logging flags */
+ xfs_mount_t *mp; /* filesystem mount point */
+ char *ptr; /* current data pointer */
+ xfs_dir2_sf_entry_t *sfep; /* shortform entry */
+ xfs_dir2_sf_t *sfp; /* shortform structure */
+ xfs_ino_t temp;
+
+ xfs_dir2_trace_args_sb("block_to_sf", args, size, bp);
+ dp = args->dp;
+ mp = dp->i_mount;
+
+ /*
+ * Make a copy of the block data, so we can shrink the inode
+ * and add local data.
+ */
+ block = kmem_alloc(mp->m_dirblksize, KM_SLEEP);
+ memcpy(block, bp->data, mp->m_dirblksize);
+ logflags = XFS_ILOG_CORE;
+ if ((error = xfs_dir2_shrink_inode(args, mp->m_dirdatablk, bp))) {
+ ASSERT(error != ENOSPC);
+ goto out;
+ }
+ /*
+ * The buffer is now unconditionally gone, whether
+ * xfs_dir2_shrink_inode worked or not.
+ *
+ * Convert the inode to local format.
+ */
+ dp->i_df.if_flags &= ~XFS_IFEXTENTS;
+ dp->i_df.if_flags |= XFS_IFINLINE;
+ dp->i_d.di_format = XFS_DINODE_FMT_LOCAL;
+ ASSERT(dp->i_df.if_bytes == 0);
+ xfs_idata_realloc(dp, size, XFS_DATA_FORK);
+ logflags |= XFS_ILOG_DDATA;
+ /*
+ * Copy the header into the newly allocate local space.
+ */
+ sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data;
+ memcpy(sfp, sfhp, XFS_DIR2_SF_HDR_SIZE(sfhp->i8count));
+ dp->i_d.di_size = size;
+ /*
+ * Set up to loop over the block's entries.
+ */
+ btp = XFS_DIR2_BLOCK_TAIL_P(mp, block);
+ ptr = (char *)block->u;
+ endptr = (char *)XFS_DIR2_BLOCK_LEAF_P_ARCH(btp, ARCH_CONVERT);
+ sfep = XFS_DIR2_SF_FIRSTENTRY(sfp);
+ /*
+ * Loop over the active and unused entries.
+ * Stop when we reach the leaf/tail portion of the block.
+ */
+ while (ptr < endptr) {
+ /*
+ * If it's unused, just skip over it.
+ */
+ dup = (xfs_dir2_data_unused_t *)ptr;
+ if (INT_GET(dup->freetag, ARCH_CONVERT) == XFS_DIR2_DATA_FREE_TAG) {
+ ptr += INT_GET(dup->length, ARCH_CONVERT);
+ continue;
+ }
+ dep = (xfs_dir2_data_entry_t *)ptr;
+ /*
+ * Skip .
+ */
+ if (dep->namelen == 1 && dep->name[0] == '.')
+ ASSERT(INT_GET(dep->inumber, ARCH_CONVERT) == dp->i_ino);
+ /*
+ * Skip .., but make sure the inode number is right.
+ */
+ else if (dep->namelen == 2 &&
+ dep->name[0] == '.' && dep->name[1] == '.')
+ ASSERT(INT_GET(dep->inumber, ARCH_CONVERT) ==
+ XFS_DIR2_SF_GET_INUMBER_ARCH(sfp, &sfp->hdr.parent, ARCH_CONVERT));
+ /*
+ * Normal entry, copy it into shortform.
+ */
+ else {
+ sfep->namelen = dep->namelen;
+ XFS_DIR2_SF_PUT_OFFSET_ARCH(sfep,
+ (xfs_dir2_data_aoff_t)
+ ((char *)dep - (char *)block), ARCH_CONVERT);
+ memcpy(sfep->name, dep->name, dep->namelen);
+ temp=INT_GET(dep->inumber, ARCH_CONVERT);
+ XFS_DIR2_SF_PUT_INUMBER_ARCH(sfp, &temp,
+ XFS_DIR2_SF_INUMBERP(sfep), ARCH_CONVERT);
+ sfep = XFS_DIR2_SF_NEXTENTRY(sfp, sfep);
+ }
+ ptr += XFS_DIR2_DATA_ENTSIZE(dep->namelen);
+ }
+ ASSERT((char *)sfep - (char *)sfp == size);
+ xfs_dir2_sf_check(args);
+out:
+ xfs_trans_log_inode(args->trans, dp, logflags);
+ kmem_free(block, mp->m_dirblksize);
+ return error;
+}
+
+/*
+ * Add a name to a shortform directory.
+ * There are two algorithms, "easy" and "hard" which we decide on
+ * before changing anything.
+ * Convert to block form if necessary, if the new entry won't fit.
+ */
+int /* error */
+xfs_dir2_sf_addname(
+ xfs_da_args_t *args) /* operation arguments */
+{
+ int add_entsize; /* size of the new entry */
+ xfs_inode_t *dp; /* incore directory inode */
+ int error; /* error return value */
+ int incr_isize; /* total change in size */
+ int new_isize; /* di_size after adding name */
+ int objchange; /* changing to 8-byte inodes */
+ xfs_dir2_data_aoff_t offset; /* offset for new entry */
+ int old_isize; /* di_size before adding name */
+ int pick; /* which algorithm to use */
+ xfs_dir2_sf_t *sfp; /* shortform structure */
+ xfs_dir2_sf_entry_t *sfep; /* shortform entry */
+
+ xfs_dir2_trace_args("sf_addname", args);
+ ASSERT(xfs_dir2_sf_lookup(args) == ENOENT);
+ dp = args->dp;
+ ASSERT(dp->i_df.if_flags & XFS_IFINLINE);
+ /*
+ * Make sure the shortform value has some of its header.
+ */
+ if (dp->i_d.di_size < offsetof(xfs_dir2_sf_hdr_t, parent)) {
+ ASSERT(XFS_FORCED_SHUTDOWN(dp->i_mount));
+ return XFS_ERROR(EIO);
+ }
+ ASSERT(dp->i_df.if_bytes == dp->i_d.di_size);
+ ASSERT(dp->i_df.if_u1.if_data != NULL);
+ sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data;
+ ASSERT(dp->i_d.di_size >= XFS_DIR2_SF_HDR_SIZE(sfp->hdr.i8count));
+ /*
+ * Compute entry (and change in) size.
+ */
+ add_entsize = XFS_DIR2_SF_ENTSIZE_BYNAME(sfp, args->namelen);
+ incr_isize = add_entsize;
+ objchange = 0;
+#if XFS_BIG_INUMS
+ /*
+ * Do we have to change to 8 byte inodes?
+ */
+ if (args->inumber > XFS_DIR2_MAX_SHORT_INUM && sfp->hdr.i8count == 0) {
+ /*
+ * Yes, adjust the entry size and the total size.
+ */
+ add_entsize +=
+ (uint)sizeof(xfs_dir2_ino8_t) -
+ (uint)sizeof(xfs_dir2_ino4_t);
+ incr_isize +=
+ (sfp->hdr.count + 2) *
+ ((uint)sizeof(xfs_dir2_ino8_t) -
+ (uint)sizeof(xfs_dir2_ino4_t));
+ objchange = 1;
+ }
+#endif
+ old_isize = (int)dp->i_d.di_size;
+ new_isize = old_isize + incr_isize;
+ /*
+ * Won't fit as shortform any more (due to size),
+ * or the pick routine says it won't (due to offset values).
+ */
+ if (new_isize > XFS_IFORK_DSIZE(dp) ||
+ (pick =
+ xfs_dir2_sf_addname_pick(args, objchange, &sfep, &offset)) == 0) {
+ /*
+ * Just checking or no space reservation, it doesn't fit.
+ */
+ if (args->justcheck || args->total == 0)
+ return XFS_ERROR(ENOSPC);
+ /*
+ * Convert to block form then add the name.
+ */
+ error = xfs_dir2_sf_to_block(args);
+ if (error)
+ return error;
+ return xfs_dir2_block_addname(args);
+ }
+ /*
+ * Just checking, it fits.
+ */
+ if (args->justcheck)
+ return 0;
+ /*
+ * Do it the easy way - just add it at the end.
+ */
+ if (pick == 1)
+ xfs_dir2_sf_addname_easy(args, sfep, offset, new_isize);
+ /*
+ * Do it the hard way - look for a place to insert the new entry.
+ * Convert to 8 byte inode numbers first if necessary.
+ */
+ else {
+ ASSERT(pick == 2);
+#if XFS_BIG_INUMS
+ if (objchange)
+ xfs_dir2_sf_toino8(args);
+#endif
+ xfs_dir2_sf_addname_hard(args, objchange, new_isize);
+ }
+ xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA);
+ return 0;
+}
+
+/*
+ * Add the new entry the "easy" way.
+ * This is copying the old directory and adding the new entry at the end.
+ * Since it's sorted by "offset" we need room after the last offset
+ * that's already there, and then room to convert to a block directory.
+ * This is already checked by the pick routine.
+ */
+static void
+xfs_dir2_sf_addname_easy(
+ xfs_da_args_t *args, /* operation arguments */
+ xfs_dir2_sf_entry_t *sfep, /* pointer to new entry */
+ xfs_dir2_data_aoff_t offset, /* offset to use for new ent */
+ int new_isize) /* new directory size */
+{
+ int byteoff; /* byte offset in sf dir */
+ xfs_inode_t *dp; /* incore directory inode */
+ xfs_dir2_sf_t *sfp; /* shortform structure */
+
+ dp = args->dp;
+
+ sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data;
+ byteoff = (int)((char *)sfep - (char *)sfp);
+ /*
+ * Grow the in-inode space.
+ */
+ xfs_idata_realloc(dp, XFS_DIR2_SF_ENTSIZE_BYNAME(sfp, args->namelen),
+ XFS_DATA_FORK);
+ /*
+ * Need to set up again due to realloc of the inode data.
+ */
+ sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data;
+ sfep = (xfs_dir2_sf_entry_t *)((char *)sfp + byteoff);
+ /*
+ * Fill in the new entry.
+ */
+ sfep->namelen = args->namelen;
+ XFS_DIR2_SF_PUT_OFFSET_ARCH(sfep, offset, ARCH_CONVERT);
+ memcpy(sfep->name, args->name, sfep->namelen);
+ XFS_DIR2_SF_PUT_INUMBER_ARCH(sfp, &args->inumber,
+ XFS_DIR2_SF_INUMBERP(sfep), ARCH_CONVERT);
+ /*
+ * Update the header and inode.
+ */
+ sfp->hdr.count++;
+#if XFS_BIG_INUMS
+ if (args->inumber > XFS_DIR2_MAX_SHORT_INUM)
+ sfp->hdr.i8count++;
+#endif
+ dp->i_d.di_size = new_isize;
+ xfs_dir2_sf_check(args);
+}
+
+/*
+ * Add the new entry the "hard" way.
+ * The caller has already converted to 8 byte inode numbers if necessary,
+ * in which case we need to leave the i8count at 1.
+ * Find a hole that the new entry will fit into, and copy
+ * the first part of the entries, the new entry, and the last part of
+ * the entries.
+ */
+/* ARGSUSED */
+static void
+xfs_dir2_sf_addname_hard(
+ xfs_da_args_t *args, /* operation arguments */
+ int objchange, /* changing inode number size */
+ int new_isize) /* new directory size */
+{
+ int add_datasize; /* data size need for new ent */
+ char *buf; /* buffer for old */
+ xfs_inode_t *dp; /* incore directory inode */
+ int eof; /* reached end of old dir */
+ int nbytes; /* temp for byte copies */
+ xfs_dir2_data_aoff_t new_offset; /* next offset value */
+ xfs_dir2_data_aoff_t offset; /* current offset value */
+ int old_isize; /* previous di_size */
+ xfs_dir2_sf_entry_t *oldsfep; /* entry in original dir */
+ xfs_dir2_sf_t *oldsfp; /* original shortform dir */
+ xfs_dir2_sf_entry_t *sfep; /* entry in new dir */
+ xfs_dir2_sf_t *sfp; /* new shortform dir */
+
+ /*
+ * Copy the old directory to the stack buffer.
+ */
+ dp = args->dp;
+
+ sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data;
+ old_isize = (int)dp->i_d.di_size;
+ buf = kmem_alloc(old_isize, KM_SLEEP);
+ oldsfp = (xfs_dir2_sf_t *)buf;
+ memcpy(oldsfp, sfp, old_isize);
+ /*
+ * Loop over the old directory finding the place we're going
+ * to insert the new entry.
+ * If it's going to end up at the end then oldsfep will point there.
+ */
+ for (offset = XFS_DIR2_DATA_FIRST_OFFSET,
+ oldsfep = XFS_DIR2_SF_FIRSTENTRY(oldsfp),
+ add_datasize = XFS_DIR2_DATA_ENTSIZE(args->namelen),
+ eof = (char *)oldsfep == &buf[old_isize];
+ !eof;
+ offset = new_offset + XFS_DIR2_DATA_ENTSIZE(oldsfep->namelen),
+ oldsfep = XFS_DIR2_SF_NEXTENTRY(oldsfp, oldsfep),
+ eof = (char *)oldsfep == &buf[old_isize]) {
+ new_offset = XFS_DIR2_SF_GET_OFFSET_ARCH(oldsfep, ARCH_CONVERT);
+ if (offset + add_datasize <= new_offset)
+ break;
+ }
+ /*
+ * Get rid of the old directory, then allocate space for
+ * the new one. We do this so xfs_idata_realloc won't copy
+ * the data.
+ */
+ xfs_idata_realloc(dp, -old_isize, XFS_DATA_FORK);
+ xfs_idata_realloc(dp, new_isize, XFS_DATA_FORK);
+ /*
+ * Reset the pointer since the buffer was reallocated.
+ */
+ sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data;
+ /*
+ * Copy the first part of the directory, including the header.
+ */
+ nbytes = (int)((char *)oldsfep - (char *)oldsfp);
+ memcpy(sfp, oldsfp, nbytes);
+ sfep = (xfs_dir2_sf_entry_t *)((char *)sfp + nbytes);
+ /*
+ * Fill in the new entry, and update the header counts.
+ */
+ sfep->namelen = args->namelen;
+ XFS_DIR2_SF_PUT_OFFSET_ARCH(sfep, offset, ARCH_CONVERT);
+ memcpy(sfep->name, args->name, sfep->namelen);
+ XFS_DIR2_SF_PUT_INUMBER_ARCH(sfp, &args->inumber,
+ XFS_DIR2_SF_INUMBERP(sfep), ARCH_CONVERT);
+ sfp->hdr.count++;
+#if XFS_BIG_INUMS
+ if (args->inumber > XFS_DIR2_MAX_SHORT_INUM && !objchange)
+ sfp->hdr.i8count++;
+#endif
+ /*
+ * If there's more left to copy, do that.
+ */
+ if (!eof) {
+ sfep = XFS_DIR2_SF_NEXTENTRY(sfp, sfep);
+ memcpy(sfep, oldsfep, old_isize - nbytes);
+ }
+ kmem_free(buf, old_isize);
+ dp->i_d.di_size = new_isize;
+ xfs_dir2_sf_check(args);
+}
+
+/*
+ * Decide if the new entry will fit at all.
+ * If it will fit, pick between adding the new entry to the end (easy)
+ * or somewhere else (hard).
+ * Return 0 (won't fit), 1 (easy), 2 (hard).
+ */
+/*ARGSUSED*/
+static int /* pick result */
+xfs_dir2_sf_addname_pick(
+ xfs_da_args_t *args, /* operation arguments */
+ int objchange, /* inode # size changes */
+ xfs_dir2_sf_entry_t **sfepp, /* out(1): new entry ptr */
+ xfs_dir2_data_aoff_t *offsetp) /* out(1): new offset */
+{
+ xfs_inode_t *dp; /* incore directory inode */
+ int holefit; /* found hole it will fit in */
+ int i; /* entry number */
+ xfs_mount_t *mp; /* filesystem mount point */
+ xfs_dir2_data_aoff_t offset; /* data block offset */
+ xfs_dir2_sf_entry_t *sfep; /* shortform entry */
+ xfs_dir2_sf_t *sfp; /* shortform structure */
+ int size; /* entry's data size */
+ int used; /* data bytes used */
+
+ dp = args->dp;
+ mp = dp->i_mount;
+
+ sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data;
+ size = XFS_DIR2_DATA_ENTSIZE(args->namelen);
+ offset = XFS_DIR2_DATA_FIRST_OFFSET;
+ sfep = XFS_DIR2_SF_FIRSTENTRY(sfp);
+ holefit = 0;
+ /*
+ * Loop over sf entries.
+ * Keep track of data offset and whether we've seen a place
+ * to insert the new entry.
+ */
+ for (i = 0; i < sfp->hdr.count; i++) {
+ if (!holefit)
+ holefit = offset + size <= XFS_DIR2_SF_GET_OFFSET_ARCH(sfep, ARCH_CONVERT);
+ offset = XFS_DIR2_SF_GET_OFFSET_ARCH(sfep, ARCH_CONVERT) +
+ XFS_DIR2_DATA_ENTSIZE(sfep->namelen);
+ sfep = XFS_DIR2_SF_NEXTENTRY(sfp, sfep);
+ }
+ /*
+ * Calculate data bytes used excluding the new entry, if this
+ * was a data block (block form directory).
+ */
+ used = offset +
+ (sfp->hdr.count + 3) * (uint)sizeof(xfs_dir2_leaf_entry_t) +
+ (uint)sizeof(xfs_dir2_block_tail_t);
+ /*
+ * If it won't fit in a block form then we can't insert it,
+ * we'll go back, convert to block, then try the insert and convert
+ * to leaf.
+ */
+ if (used + (holefit ? 0 : size) > mp->m_dirblksize)
+ return 0;
+ /*
+ * If changing the inode number size, do it the hard way.
+ */
+#if XFS_BIG_INUMS
+ if (objchange) {
+ return 2;
+ }
+#else
+ ASSERT(objchange == 0);
+#endif
+ /*
+ * If it won't fit at the end then do it the hard way (use the hole).
+ */
+ if (used + size > mp->m_dirblksize)
+ return 2;
+ /*
+ * Do it the easy way.
+ */
+ *sfepp = sfep;
+ *offsetp = offset;
+ return 1;
+}
+
+#ifdef DEBUG
+/*
+ * Check consistency of shortform directory, assert if bad.
+ */
+static void
+xfs_dir2_sf_check(
+ xfs_da_args_t *args) /* operation arguments */
+{
+ xfs_inode_t *dp; /* incore directory inode */
+ int i; /* entry number */
+ int i8count; /* number of big inode#s */
+ xfs_ino_t ino; /* entry inode number */
+ int offset; /* data offset */
+ xfs_dir2_sf_entry_t *sfep; /* shortform dir entry */
+ xfs_dir2_sf_t *sfp; /* shortform structure */
+
+ dp = args->dp;
+
+ sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data;
+ offset = XFS_DIR2_DATA_FIRST_OFFSET;
+ ino = XFS_DIR2_SF_GET_INUMBER_ARCH(sfp, &sfp->hdr.parent, ARCH_CONVERT);
+ i8count = ino > XFS_DIR2_MAX_SHORT_INUM;
+
+ for (i = 0, sfep = XFS_DIR2_SF_FIRSTENTRY(sfp);
+ i < sfp->hdr.count;
+ i++, sfep = XFS_DIR2_SF_NEXTENTRY(sfp, sfep)) {
+ ASSERT(XFS_DIR2_SF_GET_OFFSET_ARCH(sfep, ARCH_CONVERT) >= offset);
+ ino = XFS_DIR2_SF_GET_INUMBER_ARCH(sfp, XFS_DIR2_SF_INUMBERP(sfep), ARCH_CONVERT);
+ i8count += ino > XFS_DIR2_MAX_SHORT_INUM;
+ offset =
+ XFS_DIR2_SF_GET_OFFSET_ARCH(sfep, ARCH_CONVERT) +
+ XFS_DIR2_DATA_ENTSIZE(sfep->namelen);
+ }
+ ASSERT(i8count == sfp->hdr.i8count);
+ ASSERT(XFS_BIG_INUMS || i8count == 0);
+ ASSERT((char *)sfep - (char *)sfp == dp->i_d.di_size);
+ ASSERT(offset +
+ (sfp->hdr.count + 2) * (uint)sizeof(xfs_dir2_leaf_entry_t) +
+ (uint)sizeof(xfs_dir2_block_tail_t) <=
+ dp->i_mount->m_dirblksize);
+}
+#endif /* DEBUG */
+
+/*
+ * Create a new (shortform) directory.
+ */
+int /* error, always 0 */
+xfs_dir2_sf_create(
+ xfs_da_args_t *args, /* operation arguments */
+ xfs_ino_t pino) /* parent inode number */
+{
+ xfs_inode_t *dp; /* incore directory inode */
+ int i8count; /* parent inode is an 8-byte number */
+ xfs_dir2_sf_t *sfp; /* shortform structure */
+ int size; /* directory size */
+
+ xfs_dir2_trace_args_i("sf_create", args, pino);
+ dp = args->dp;
+
+ ASSERT(dp != NULL);
+ ASSERT(dp->i_d.di_size == 0);
+ /*
+ * If it's currently a zero-length extent file,
+ * convert it to local format.
+ */
+ if (dp->i_d.di_format == XFS_DINODE_FMT_EXTENTS) {
+ dp->i_df.if_flags &= ~XFS_IFEXTENTS; /* just in case */
+ dp->i_d.di_format = XFS_DINODE_FMT_LOCAL;
+ xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE);
+ dp->i_df.if_flags |= XFS_IFINLINE;
+ }
+ ASSERT(dp->i_df.if_flags & XFS_IFINLINE);
+ ASSERT(dp->i_df.if_bytes == 0);
+ i8count = pino > XFS_DIR2_MAX_SHORT_INUM;
+ size = XFS_DIR2_SF_HDR_SIZE(i8count);
+ /*
+ * Make a buffer for the data.
+ */
+ xfs_idata_realloc(dp, size, XFS_DATA_FORK);
+ /*
+ * Fill in the header,
+ */
+ sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data;
+ sfp->hdr.i8count = i8count;
+ /*
+ * Now can put in the inode number, since i8count is set.
+ */
+ XFS_DIR2_SF_PUT_INUMBER_ARCH(sfp, &pino, &sfp->hdr.parent, ARCH_CONVERT);
+ sfp->hdr.count = 0;
+ dp->i_d.di_size = size;
+ xfs_dir2_sf_check(args);
+ xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA);
+ return 0;
+}
+
+int /* error */
+xfs_dir2_sf_getdents(
+ xfs_inode_t *dp, /* incore directory inode */
+ uio_t *uio, /* caller's buffer control */
+ int *eofp, /* eof reached? (out) */
+ xfs_dirent_t *dbp, /* caller's buffer */
+ xfs_dir2_put_t put) /* abi's formatting function */
+{
+ int error; /* error return value */
+ int i; /* shortform entry number */
+ xfs_mount_t *mp; /* filesystem mount point */
+ xfs_dir2_dataptr_t off; /* current entry's offset */
+ xfs_dir2_put_args_t p; /* arg package for put rtn */
+ xfs_dir2_sf_entry_t *sfep; /* shortform directory entry */
+ xfs_dir2_sf_t *sfp; /* shortform structure */
+ xfs_off_t dir_offset;
+
+ mp = dp->i_mount;
+
+ ASSERT(dp->i_df.if_flags & XFS_IFINLINE);
+ /*
+ * Give up if the directory is way too short.
+ */
+ if (dp->i_d.di_size < offsetof(xfs_dir2_sf_hdr_t, parent)) {
+ ASSERT(XFS_FORCED_SHUTDOWN(mp));
+ return XFS_ERROR(EIO);
+ }
+
+ dir_offset = uio->uio_offset;
+
+ ASSERT(dp->i_df.if_bytes == dp->i_d.di_size);
+ ASSERT(dp->i_df.if_u1.if_data != NULL);
+
+ sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data;
+
+ ASSERT(dp->i_d.di_size >= XFS_DIR2_SF_HDR_SIZE(sfp->hdr.i8count));
+
+ /*
+ * If the block number in the offset is out of range, we're done.
+ */
+ if (XFS_DIR2_DATAPTR_TO_DB(mp, dir_offset) > mp->m_dirdatablk) {
+ *eofp = 1;
+ return 0;
+ }
+
+ /*
+ * Set up putargs structure.
+ */
+ p.dbp = dbp;
+ p.put = put;
+ p.uio = uio;
+ /*
+ * Put . entry unless we're starting past it.
+ */
+ if (dir_offset <=
+ XFS_DIR2_DB_OFF_TO_DATAPTR(mp, mp->m_dirdatablk,
+ XFS_DIR2_DATA_DOT_OFFSET)) {
+ p.cook = XFS_DIR2_DB_OFF_TO_DATAPTR(mp, 0,
+ XFS_DIR2_DATA_DOTDOT_OFFSET);
+ p.ino = dp->i_ino;
+#if XFS_BIG_INUMS
+ p.ino += mp->m_inoadd;
+#endif
+ p.name = ".";
+ p.namelen = 1;
+
+ error = p.put(&p);
+
+ if (!p.done) {
+ uio->uio_offset =
+ XFS_DIR2_DB_OFF_TO_DATAPTR(mp, mp->m_dirdatablk,
+ XFS_DIR2_DATA_DOT_OFFSET);
+ return error;
+ }
+ }
+
+ /*
+ * Put .. entry unless we're starting past it.
+ */
+ if (dir_offset <=
+ XFS_DIR2_DB_OFF_TO_DATAPTR(mp, mp->m_dirdatablk,
+ XFS_DIR2_DATA_DOTDOT_OFFSET)) {
+ p.cook = XFS_DIR2_DB_OFF_TO_DATAPTR(mp, mp->m_dirdatablk,
+ XFS_DIR2_DATA_FIRST_OFFSET);
+ p.ino = XFS_DIR2_SF_GET_INUMBER_ARCH(sfp, &sfp->hdr.parent,
+ ARCH_CONVERT);
+#if XFS_BIG_INUMS
+ p.ino += mp->m_inoadd;
+#endif
+ p.name = "..";
+ p.namelen = 2;
+
+ error = p.put(&p);
+
+ if (!p.done) {
+ uio->uio_offset =
+ XFS_DIR2_DB_OFF_TO_DATAPTR(mp, mp->m_dirdatablk,
+ XFS_DIR2_DATA_DOTDOT_OFFSET);
+ return error;
+ }
+ }
+
+ /*
+ * Loop while there are more entries and put'ing works.
+ */
+ for (i = 0, sfep = XFS_DIR2_SF_FIRSTENTRY(sfp);
+ i < sfp->hdr.count;
+ i++, sfep = XFS_DIR2_SF_NEXTENTRY(sfp, sfep)) {
+
+ off = XFS_DIR2_DB_OFF_TO_DATAPTR(mp, mp->m_dirdatablk,
+ XFS_DIR2_SF_GET_OFFSET_ARCH(sfep, ARCH_CONVERT));
+
+ if (dir_offset > off)
+ continue;
+
+ p.namelen = sfep->namelen;
+
+ p.cook = XFS_DIR2_DB_OFF_TO_DATAPTR(mp, mp->m_dirdatablk,
+ XFS_DIR2_SF_GET_OFFSET_ARCH(sfep, ARCH_CONVERT) +
+ XFS_DIR2_DATA_ENTSIZE(p.namelen));
+
+ p.ino = XFS_DIR2_SF_GET_INUMBER_ARCH(sfp,
+ XFS_DIR2_SF_INUMBERP(sfep), ARCH_CONVERT);
+#if XFS_BIG_INUMS
+ p.ino += mp->m_inoadd;
+#endif
+ p.name = (char *)sfep->name;
+
+ error = p.put(&p);
+
+ if (!p.done) {
+ uio->uio_offset = off;
+ return error;
+ }
+ }
+
+ /*
+ * They all fit.
+ */
+ *eofp = 1;
+
+ uio->uio_offset =
+ XFS_DIR2_DB_OFF_TO_DATAPTR(mp, mp->m_dirdatablk + 1, 0);
+
+ return 0;
+}
+
+/*
+ * Lookup an entry in a shortform directory.
+ * Returns EEXIST if found, ENOENT if not found.
+ */
+int /* error */
+xfs_dir2_sf_lookup(
+ xfs_da_args_t *args) /* operation arguments */
+{
+ xfs_inode_t *dp; /* incore directory inode */
+ int i; /* entry index */
+ xfs_dir2_sf_entry_t *sfep; /* shortform directory entry */
+ xfs_dir2_sf_t *sfp; /* shortform structure */
+
+ xfs_dir2_trace_args("sf_lookup", args);
+ xfs_dir2_sf_check(args);
+ dp = args->dp;
+
+ ASSERT(dp->i_df.if_flags & XFS_IFINLINE);
+ /*
+ * Bail out if the directory is way too short.
+ */
+ if (dp->i_d.di_size < offsetof(xfs_dir2_sf_hdr_t, parent)) {
+ ASSERT(XFS_FORCED_SHUTDOWN(dp->i_mount));
+ return XFS_ERROR(EIO);
+ }
+ ASSERT(dp->i_df.if_bytes == dp->i_d.di_size);
+ ASSERT(dp->i_df.if_u1.if_data != NULL);
+ sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data;
+ ASSERT(dp->i_d.di_size >= XFS_DIR2_SF_HDR_SIZE(sfp->hdr.i8count));
+ /*
+ * Special case for .
+ */
+ if (args->namelen == 1 && args->name[0] == '.') {
+ args->inumber = dp->i_ino;
+ return XFS_ERROR(EEXIST);
+ }
+ /*
+ * Special case for ..
+ */
+ if (args->namelen == 2 &&
+ args->name[0] == '.' && args->name[1] == '.') {
+ args->inumber = XFS_DIR2_SF_GET_INUMBER_ARCH(sfp, &sfp->hdr.parent, ARCH_CONVERT);
+ return XFS_ERROR(EEXIST);
+ }
+ /*
+ * Loop over all the entries trying to match ours.
+ */
+ for (i = 0, sfep = XFS_DIR2_SF_FIRSTENTRY(sfp);
+ i < sfp->hdr.count;
+ i++, sfep = XFS_DIR2_SF_NEXTENTRY(sfp, sfep)) {
+ if (sfep->namelen == args->namelen &&
+ sfep->name[0] == args->name[0] &&
+ memcmp(args->name, sfep->name, args->namelen) == 0) {
+ args->inumber =
+ XFS_DIR2_SF_GET_INUMBER_ARCH(sfp,
+ XFS_DIR2_SF_INUMBERP(sfep), ARCH_CONVERT);
+ return XFS_ERROR(EEXIST);
+ }
+ }
+ /*
+ * Didn't find it.
+ */
+ ASSERT(args->oknoent);
+ return XFS_ERROR(ENOENT);
+}
+
+/*
+ * Remove an entry from a shortform directory.
+ */
+int /* error */
+xfs_dir2_sf_removename(
+ xfs_da_args_t *args)
+{
+ int byteoff; /* offset of removed entry */
+ xfs_inode_t *dp; /* incore directory inode */
+ int entsize; /* this entry's size */
+ int i; /* shortform entry index */
+ int newsize; /* new inode size */
+ int oldsize; /* old inode size */
+ xfs_dir2_sf_entry_t *sfep; /* shortform directory entry */
+ xfs_dir2_sf_t *sfp; /* shortform structure */
+
+ xfs_dir2_trace_args("sf_removename", args);
+ dp = args->dp;
+
+ ASSERT(dp->i_df.if_flags & XFS_IFINLINE);
+ oldsize = (int)dp->i_d.di_size;
+ /*
+ * Bail out if the directory is way too short.
+ */
+ if (oldsize < offsetof(xfs_dir2_sf_hdr_t, parent)) {
+ ASSERT(XFS_FORCED_SHUTDOWN(dp->i_mount));
+ return XFS_ERROR(EIO);
+ }
+ ASSERT(dp->i_df.if_bytes == oldsize);
+ ASSERT(dp->i_df.if_u1.if_data != NULL);
+ sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data;
+ ASSERT(oldsize >= XFS_DIR2_SF_HDR_SIZE(sfp->hdr.i8count));
+ /*
+ * Loop over the old directory entries.
+ * Find the one we're deleting.
+ */
+ for (i = 0, sfep = XFS_DIR2_SF_FIRSTENTRY(sfp);
+ i < sfp->hdr.count;
+ i++, sfep = XFS_DIR2_SF_NEXTENTRY(sfp, sfep)) {
+ if (sfep->namelen == args->namelen &&
+ sfep->name[0] == args->name[0] &&
+ memcmp(sfep->name, args->name, args->namelen) == 0) {
+ ASSERT(XFS_DIR2_SF_GET_INUMBER_ARCH(sfp,
+ XFS_DIR2_SF_INUMBERP(sfep), ARCH_CONVERT) ==
+ args->inumber);
+ break;
+ }
+ }
+ /*
+ * Didn't find it.
+ */
+ if (i == sfp->hdr.count) {
+ return XFS_ERROR(ENOENT);
+ }
+ /*
+ * Calculate sizes.
+ */
+ byteoff = (int)((char *)sfep - (char *)sfp);
+ entsize = XFS_DIR2_SF_ENTSIZE_BYNAME(sfp, args->namelen);
+ newsize = oldsize - entsize;
+ /*
+ * Copy the part if any after the removed entry, sliding it down.
+ */
+ if (byteoff + entsize < oldsize)
+ memmove((char *)sfp + byteoff, (char *)sfp + byteoff + entsize,
+ oldsize - (byteoff + entsize));
+ /*
+ * Fix up the header and file size.
+ */
+ sfp->hdr.count--;
+ dp->i_d.di_size = newsize;
+ /*
+ * Reallocate, making it smaller.
+ */
+ xfs_idata_realloc(dp, newsize - oldsize, XFS_DATA_FORK);
+ sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data;
+#if XFS_BIG_INUMS
+ /*
+ * Are we changing inode number size?
+ */
+ if (args->inumber > XFS_DIR2_MAX_SHORT_INUM) {
+ if (sfp->hdr.i8count == 1)
+ xfs_dir2_sf_toino4(args);
+ else
+ sfp->hdr.i8count--;
+ }
+#endif
+ xfs_dir2_sf_check(args);
+ xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA);
+ return 0;
+}
+
+/*
+ * Replace the inode number of an entry in a shortform directory.
+ */
+int /* error */
+xfs_dir2_sf_replace(
+ xfs_da_args_t *args) /* operation arguments */
+{
+ xfs_inode_t *dp; /* incore directory inode */
+ int i; /* entry index */
+#if XFS_BIG_INUMS || defined(DEBUG)
+ xfs_ino_t ino=0; /* entry old inode number */
+#endif
+#if XFS_BIG_INUMS
+ int i8elevated; /* sf_toino8 set i8count=1 */
+#endif
+ xfs_dir2_sf_entry_t *sfep; /* shortform directory entry */
+ xfs_dir2_sf_t *sfp; /* shortform structure */
+
+ xfs_dir2_trace_args("sf_replace", args);
+ dp = args->dp;
+
+ ASSERT(dp->i_df.if_flags & XFS_IFINLINE);
+ /*
+ * Bail out if the shortform directory is way too small.
+ */
+ if (dp->i_d.di_size < offsetof(xfs_dir2_sf_hdr_t, parent)) {
+ ASSERT(XFS_FORCED_SHUTDOWN(dp->i_mount));
+ return XFS_ERROR(EIO);
+ }
+ ASSERT(dp->i_df.if_bytes == dp->i_d.di_size);
+ ASSERT(dp->i_df.if_u1.if_data != NULL);
+ sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data;
+ ASSERT(dp->i_d.di_size >= XFS_DIR2_SF_HDR_SIZE(sfp->hdr.i8count));
+#if XFS_BIG_INUMS
+ /*
+ * New inode number is large, and need to convert to 8-byte inodes.
+ */
+ if (args->inumber > XFS_DIR2_MAX_SHORT_INUM && sfp->hdr.i8count == 0) {
+ int error; /* error return value */
+ int newsize; /* new inode size */
+
+ newsize =
+ dp->i_df.if_bytes +
+ (sfp->hdr.count + 1) *
+ ((uint)sizeof(xfs_dir2_ino8_t) -
+ (uint)sizeof(xfs_dir2_ino4_t));
+ /*
+ * Won't fit as shortform, convert to block then do replace.
+ */
+ if (newsize > XFS_IFORK_DSIZE(dp)) {
+ error = xfs_dir2_sf_to_block(args);
+ if (error) {
+ return error;
+ }
+ return xfs_dir2_block_replace(args);
+ }
+ /*
+ * Still fits, convert to 8-byte now.
+ */
+ xfs_dir2_sf_toino8(args);
+ i8elevated = 1;
+ sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data;
+ } else
+ i8elevated = 0;
+#endif
+ ASSERT(args->namelen != 1 || args->name[0] != '.');
+ /*
+ * Replace ..'s entry.
+ */
+ if (args->namelen == 2 &&
+ args->name[0] == '.' && args->name[1] == '.') {
+#if XFS_BIG_INUMS || defined(DEBUG)
+ ino = XFS_DIR2_SF_GET_INUMBER_ARCH(sfp, &sfp->hdr.parent, ARCH_CONVERT);
+ ASSERT(args->inumber != ino);
+#endif
+ XFS_DIR2_SF_PUT_INUMBER_ARCH(sfp, &args->inumber, &sfp->hdr.parent, ARCH_CONVERT);
+ }
+ /*
+ * Normal entry, look for the name.
+ */
+ else {
+ for (i = 0, sfep = XFS_DIR2_SF_FIRSTENTRY(sfp);
+ i < sfp->hdr.count;
+ i++, sfep = XFS_DIR2_SF_NEXTENTRY(sfp, sfep)) {
+ if (sfep->namelen == args->namelen &&
+ sfep->name[0] == args->name[0] &&
+ memcmp(args->name, sfep->name, args->namelen) == 0) {
+#if XFS_BIG_INUMS || defined(DEBUG)
+ ino = XFS_DIR2_SF_GET_INUMBER_ARCH(sfp,
+ XFS_DIR2_SF_INUMBERP(sfep), ARCH_CONVERT);
+ ASSERT(args->inumber != ino);
+#endif
+ XFS_DIR2_SF_PUT_INUMBER_ARCH(sfp, &args->inumber,
+ XFS_DIR2_SF_INUMBERP(sfep), ARCH_CONVERT);
+ break;
+ }
+ }
+ /*
+ * Didn't find it.
+ */
+ if (i == sfp->hdr.count) {
+ ASSERT(args->oknoent);
+#if XFS_BIG_INUMS
+ if (i8elevated)
+ xfs_dir2_sf_toino4(args);
+#endif
+ return XFS_ERROR(ENOENT);
+ }
+ }
+#if XFS_BIG_INUMS
+ /*
+ * See if the old number was large, the new number is small.
+ */
+ if (ino > XFS_DIR2_MAX_SHORT_INUM &&
+ args->inumber <= XFS_DIR2_MAX_SHORT_INUM) {
+ /*
+ * And the old count was one, so need to convert to small.
+ */
+ if (sfp->hdr.i8count == 1)
+ xfs_dir2_sf_toino4(args);
+ else
+ sfp->hdr.i8count--;
+ }
+ /*
+ * See if the old number was small, the new number is large.
+ */
+ if (ino <= XFS_DIR2_MAX_SHORT_INUM &&
+ args->inumber > XFS_DIR2_MAX_SHORT_INUM) {
+ /*
+ * add to the i8count unless we just converted to 8-byte
+ * inodes (which does an implied i8count = 1)
+ */
+ ASSERT(sfp->hdr.i8count != 0);
+ if (!i8elevated)
+ sfp->hdr.i8count++;
+ }
+#endif
+ xfs_dir2_sf_check(args);
+ xfs_trans_log_inode(args->trans, dp, XFS_ILOG_DDATA);
+ return 0;
+}
+
+#if XFS_BIG_INUMS
+/*
+ * Convert from 8-byte inode numbers to 4-byte inode numbers.
+ * The last 8-byte inode number is gone, but the count is still 1.
+ */
+static void
+xfs_dir2_sf_toino4(
+ xfs_da_args_t *args) /* operation arguments */
+{
+ char *buf; /* old dir's buffer */
+ xfs_inode_t *dp; /* incore directory inode */
+ int i; /* entry index */
+ xfs_ino_t ino; /* entry inode number */
+ int newsize; /* new inode size */
+ xfs_dir2_sf_entry_t *oldsfep; /* old sf entry */
+ xfs_dir2_sf_t *oldsfp; /* old sf directory */
+ int oldsize; /* old inode size */
+ xfs_dir2_sf_entry_t *sfep; /* new sf entry */
+ xfs_dir2_sf_t *sfp; /* new sf directory */
+
+ xfs_dir2_trace_args("sf_toino4", args);
+ dp = args->dp;
+
+ /*
+ * Copy the old directory to the buffer.
+ * Then nuke it from the inode, and add the new buffer to the inode.
+ * Don't want xfs_idata_realloc copying the data here.
+ */
+ oldsize = dp->i_df.if_bytes;
+ buf = kmem_alloc(oldsize, KM_SLEEP);
+ oldsfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data;
+ ASSERT(oldsfp->hdr.i8count == 1);
+ memcpy(buf, oldsfp, oldsize);
+ /*
+ * Compute the new inode size.
+ */
+ newsize =
+ oldsize -
+ (oldsfp->hdr.count + 1) *
+ ((uint)sizeof(xfs_dir2_ino8_t) - (uint)sizeof(xfs_dir2_ino4_t));
+ xfs_idata_realloc(dp, -oldsize, XFS_DATA_FORK);
+ xfs_idata_realloc(dp, newsize, XFS_DATA_FORK);
+ /*
+ * Reset our pointers, the data has moved.
+ */
+ oldsfp = (xfs_dir2_sf_t *)buf;
+ sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data;
+ /*
+ * Fill in the new header.
+ */
+ sfp->hdr.count = oldsfp->hdr.count;
+ sfp->hdr.i8count = 0;
+ ino = XFS_DIR2_SF_GET_INUMBER_ARCH(oldsfp, &oldsfp->hdr.parent, ARCH_CONVERT);
+ XFS_DIR2_SF_PUT_INUMBER_ARCH(sfp, &ino, &sfp->hdr.parent, ARCH_CONVERT);
+ /*
+ * Copy the entries field by field.
+ */
+ for (i = 0, sfep = XFS_DIR2_SF_FIRSTENTRY(sfp),
+ oldsfep = XFS_DIR2_SF_FIRSTENTRY(oldsfp);
+ i < sfp->hdr.count;
+ i++, sfep = XFS_DIR2_SF_NEXTENTRY(sfp, sfep),
+ oldsfep = XFS_DIR2_SF_NEXTENTRY(oldsfp, oldsfep)) {
+ sfep->namelen = oldsfep->namelen;
+ sfep->offset = oldsfep->offset;
+ memcpy(sfep->name, oldsfep->name, sfep->namelen);
+ ino = XFS_DIR2_SF_GET_INUMBER_ARCH(oldsfp,
+ XFS_DIR2_SF_INUMBERP(oldsfep), ARCH_CONVERT);
+ XFS_DIR2_SF_PUT_INUMBER_ARCH(sfp, &ino, XFS_DIR2_SF_INUMBERP(sfep), ARCH_CONVERT);
+ }
+ /*
+ * Clean up the inode.
+ */
+ kmem_free(buf, oldsize);
+ dp->i_d.di_size = newsize;
+ xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA);
+}
+
+/*
+ * Convert from 4-byte inode numbers to 8-byte inode numbers.
+ * The new 8-byte inode number is not there yet, we leave with the
+ * count 1 but no corresponding entry.
+ */
+static void
+xfs_dir2_sf_toino8(
+ xfs_da_args_t *args) /* operation arguments */
+{
+ char *buf; /* old dir's buffer */
+ xfs_inode_t *dp; /* incore directory inode */
+ int i; /* entry index */
+ xfs_ino_t ino; /* entry inode number */
+ int newsize; /* new inode size */
+ xfs_dir2_sf_entry_t *oldsfep; /* old sf entry */
+ xfs_dir2_sf_t *oldsfp; /* old sf directory */
+ int oldsize; /* old inode size */
+ xfs_dir2_sf_entry_t *sfep; /* new sf entry */
+ xfs_dir2_sf_t *sfp; /* new sf directory */
+
+ xfs_dir2_trace_args("sf_toino8", args);
+ dp = args->dp;
+
+ /*
+ * Copy the old directory to the buffer.
+ * Then nuke it from the inode, and add the new buffer to the inode.
+ * Don't want xfs_idata_realloc copying the data here.
+ */
+ oldsize = dp->i_df.if_bytes;
+ buf = kmem_alloc(oldsize, KM_SLEEP);
+ oldsfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data;
+ ASSERT(oldsfp->hdr.i8count == 0);
+ memcpy(buf, oldsfp, oldsize);
+ /*
+ * Compute the new inode size.
+ */
+ newsize =
+ oldsize +
+ (oldsfp->hdr.count + 1) *
+ ((uint)sizeof(xfs_dir2_ino8_t) - (uint)sizeof(xfs_dir2_ino4_t));
+ xfs_idata_realloc(dp, -oldsize, XFS_DATA_FORK);
+ xfs_idata_realloc(dp, newsize, XFS_DATA_FORK);
+ /*
+ * Reset our pointers, the data has moved.
+ */
+ oldsfp = (xfs_dir2_sf_t *)buf;
+ sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data;
+ /*
+ * Fill in the new header.
+ */
+ sfp->hdr.count = oldsfp->hdr.count;
+ sfp->hdr.i8count = 1;
+ ino = XFS_DIR2_SF_GET_INUMBER_ARCH(oldsfp, &oldsfp->hdr.parent, ARCH_CONVERT);
+ XFS_DIR2_SF_PUT_INUMBER_ARCH(sfp, &ino, &sfp->hdr.parent, ARCH_CONVERT);
+ /*
+ * Copy the entries field by field.
+ */
+ for (i = 0, sfep = XFS_DIR2_SF_FIRSTENTRY(sfp),
+ oldsfep = XFS_DIR2_SF_FIRSTENTRY(oldsfp);
+ i < sfp->hdr.count;
+ i++, sfep = XFS_DIR2_SF_NEXTENTRY(sfp, sfep),
+ oldsfep = XFS_DIR2_SF_NEXTENTRY(oldsfp, oldsfep)) {
+ sfep->namelen = oldsfep->namelen;
+ sfep->offset = oldsfep->offset;
+ memcpy(sfep->name, oldsfep->name, sfep->namelen);
+ ino = XFS_DIR2_SF_GET_INUMBER_ARCH(oldsfp,
+ XFS_DIR2_SF_INUMBERP(oldsfep), ARCH_CONVERT);
+ XFS_DIR2_SF_PUT_INUMBER_ARCH(sfp, &ino, XFS_DIR2_SF_INUMBERP(sfep), ARCH_CONVERT);
+ }
+ /*
+ * Clean up the inode.
+ */
+ kmem_free(buf, oldsize);
+ dp->i_d.di_size = newsize;
+ xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA);
+}
+#endif /* XFS_BIG_INUMS */
diff --git a/fs/xfs/xfs_dir2_sf.h b/fs/xfs/xfs_dir2_sf.h
new file mode 100644
index 00000000000000..3fea83ecd59cba
--- /dev/null
+++ b/fs/xfs/xfs_dir2_sf.h
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2000-2001 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_DIR2_SF_H__
+#define __XFS_DIR2_SF_H__
+
+/*
+ * Directory layout when stored internal to an inode.
+ *
+ * Small directories are packed as tightly as possible so as to
+ * fit into the literal area of the inode.
+ */
+
+struct uio;
+struct xfs_dabuf;
+struct xfs_da_args;
+struct xfs_dir2_block;
+struct xfs_inode;
+struct xfs_mount;
+struct xfs_trans;
+
+/*
+ * Maximum size of a shortform directory.
+ */
+#define XFS_DIR2_SF_MAX_SIZE \
+ (XFS_DINODE_MAX_SIZE - (uint)sizeof(xfs_dinode_core_t) - \
+ (uint)sizeof(xfs_agino_t))
+
+/*
+ * Inode number stored as 8 8-bit values.
+ */
+typedef struct { __uint8_t i[8]; } xfs_dir2_ino8_t;
+
+#define XFS_DIR2_SF_GET_INO8_ARCH(di,arch) \
+ (xfs_ino_t)(DIRINO_GET_ARCH(&di,arch))
+#define XFS_DIR2_SF_GET_INO8(di) \
+ XFS_DIR2_SF_GET_INO8_ARCH(di,ARCH_NOCONVERT)
+
+/*
+ * Inode number stored as 4 8-bit values.
+ * Works a lot of the time, when all the inode numbers in a directory
+ * fit in 32 bits.
+ */
+typedef struct { __uint8_t i[4]; } xfs_dir2_ino4_t;
+#define XFS_DIR2_SF_GET_INO4_ARCH(di,arch) \
+ (xfs_ino_t)(DIRINO4_GET_ARCH(&di,arch))
+#define XFS_DIR2_SF_GET_INO4(di) \
+ XFS_DIR2_SF_GET_INO4_ARCH(di,ARCH_NOCONVERT)
+
+typedef union {
+ xfs_dir2_ino8_t i8;
+ xfs_dir2_ino4_t i4;
+} xfs_dir2_inou_t;
+#define XFS_DIR2_MAX_SHORT_INUM ((xfs_ino_t)0xffffffffULL)
+
+/*
+ * Normalized offset (in a data block) of the entry, really xfs_dir2_data_off_t.
+ * Only need 16 bits, this is the byte offset into the single block form.
+ */
+typedef struct { __uint8_t i[2]; } xfs_dir2_sf_off_t;
+
+/*
+ * The parent directory has a dedicated field, and the self-pointer must
+ * be calculated on the fly.
+ *
+ * Entries are packed toward the top as tightly as possible. The header
+ * and the elements must be memcpy'd out into a work area to get correct
+ * alignment for the inode number fields.
+ */
+typedef struct xfs_dir2_sf_hdr {
+ __uint8_t count; /* count of entries */
+ __uint8_t i8count; /* count of 8-byte inode #s */
+ xfs_dir2_inou_t parent; /* parent dir inode number */
+} xfs_dir2_sf_hdr_t;
+
+typedef struct xfs_dir2_sf_entry {
+ __uint8_t namelen; /* actual name length */
+ xfs_dir2_sf_off_t offset; /* saved offset */
+ __uint8_t name[1]; /* name, variable size */
+ xfs_dir2_inou_t inumber; /* inode number, var. offset */
+} xfs_dir2_sf_entry_t;
+
+typedef struct xfs_dir2_sf {
+ xfs_dir2_sf_hdr_t hdr; /* shortform header */
+ xfs_dir2_sf_entry_t list[1]; /* shortform entries */
+} xfs_dir2_sf_t;
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR2_SF_HDR_SIZE)
+int xfs_dir2_sf_hdr_size(int i8count);
+#define XFS_DIR2_SF_HDR_SIZE(i8count) xfs_dir2_sf_hdr_size(i8count)
+#else
+#define XFS_DIR2_SF_HDR_SIZE(i8count) \
+ ((uint)sizeof(xfs_dir2_sf_hdr_t) - \
+ ((i8count) == 0) * \
+ ((uint)sizeof(xfs_dir2_ino8_t) - (uint)sizeof(xfs_dir2_ino4_t)))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR2_SF_INUMBERP)
+xfs_dir2_inou_t *xfs_dir2_sf_inumberp(xfs_dir2_sf_entry_t *sfep);
+#define XFS_DIR2_SF_INUMBERP(sfep) xfs_dir2_sf_inumberp(sfep)
+#else
+#define XFS_DIR2_SF_INUMBERP(sfep) \
+ ((xfs_dir2_inou_t *)&(sfep)->name[(sfep)->namelen])
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR2_SF_GET_INUMBER)
+xfs_intino_t xfs_dir2_sf_get_inumber_arch(xfs_dir2_sf_t *sfp, xfs_dir2_inou_t *from,
+ xfs_arch_t arch);
+#define XFS_DIR2_SF_GET_INUMBER_ARCH(sfp, from, arch) \
+ xfs_dir2_sf_get_inumber_arch(sfp, from, arch)
+
+#else
+#define XFS_DIR2_SF_GET_INUMBER_ARCH(sfp, from, arch) \
+ ((sfp)->hdr.i8count == 0 ? \
+ (xfs_intino_t)XFS_DIR2_SF_GET_INO4_ARCH(*(from), arch) : \
+ (xfs_intino_t)XFS_DIR2_SF_GET_INO8_ARCH(*(from), arch))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR2_SF_PUT_INUMBER)
+void xfs_dir2_sf_put_inumber_arch(xfs_dir2_sf_t *sfp, xfs_ino_t *from,
+ xfs_dir2_inou_t *to, xfs_arch_t arch);
+#define XFS_DIR2_SF_PUT_INUMBER_ARCH(sfp,from,to,arch) \
+ xfs_dir2_sf_put_inumber_arch(sfp,from,to,arch)
+#else
+#define XFS_DIR2_SF_PUT_INUMBER_ARCH(sfp,from,to,arch) \
+ if ((sfp)->hdr.i8count == 0) { \
+ DIRINO4_COPY_ARCH(from,to,arch); \
+ } else { \
+ DIRINO_COPY_ARCH(from,to,arch); \
+ }
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR2_SF_GET_OFFSET)
+xfs_dir2_data_aoff_t xfs_dir2_sf_get_offset_arch(xfs_dir2_sf_entry_t *sfep,
+ xfs_arch_t arch);
+xfs_dir2_data_aoff_t xfs_dir2_sf_get_offset(xfs_dir2_sf_entry_t *sfep);
+#define XFS_DIR2_SF_GET_OFFSET_ARCH(sfep,arch) \
+ xfs_dir2_sf_get_offset_arch(sfep,arch)
+#else
+#define XFS_DIR2_SF_GET_OFFSET_ARCH(sfep,arch) \
+ INT_GET_UNALIGNED_16_ARCH(&(sfep)->offset.i,arch)
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR2_SF_PUT_OFFSET)
+void xfs_dir2_sf_put_offset_arch(xfs_dir2_sf_entry_t *sfep,
+ xfs_dir2_data_aoff_t off, xfs_arch_t arch);
+#define XFS_DIR2_SF_PUT_OFFSET_ARCH(sfep,off,arch) \
+ xfs_dir2_sf_put_offset_arch(sfep,off,arch)
+#else
+#define XFS_DIR2_SF_PUT_OFFSET_ARCH(sfep,off,arch) \
+ INT_SET_UNALIGNED_16_ARCH(&(sfep)->offset.i,off,arch)
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR2_SF_ENTSIZE_BYNAME)
+int xfs_dir2_sf_entsize_byname(xfs_dir2_sf_t *sfp, int len);
+#define XFS_DIR2_SF_ENTSIZE_BYNAME(sfp,len) \
+ xfs_dir2_sf_entsize_byname(sfp,len)
+#else
+#define XFS_DIR2_SF_ENTSIZE_BYNAME(sfp,len) /* space a name uses */ \
+ ((uint)sizeof(xfs_dir2_sf_entry_t) - 1 + (len) - \
+ ((sfp)->hdr.i8count == 0) * \
+ ((uint)sizeof(xfs_dir2_ino8_t) - (uint)sizeof(xfs_dir2_ino4_t)))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR2_SF_ENTSIZE_BYENTRY)
+int xfs_dir2_sf_entsize_byentry(xfs_dir2_sf_t *sfp, xfs_dir2_sf_entry_t *sfep);
+#define XFS_DIR2_SF_ENTSIZE_BYENTRY(sfp,sfep) \
+ xfs_dir2_sf_entsize_byentry(sfp,sfep)
+#else
+#define XFS_DIR2_SF_ENTSIZE_BYENTRY(sfp,sfep) /* space an entry uses */ \
+ ((uint)sizeof(xfs_dir2_sf_entry_t) - 1 + (sfep)->namelen - \
+ ((sfp)->hdr.i8count == 0) * \
+ ((uint)sizeof(xfs_dir2_ino8_t) - (uint)sizeof(xfs_dir2_ino4_t)))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR2_SF_FIRSTENTRY)
+xfs_dir2_sf_entry_t *xfs_dir2_sf_firstentry(xfs_dir2_sf_t *sfp);
+#define XFS_DIR2_SF_FIRSTENTRY(sfp) xfs_dir2_sf_firstentry(sfp)
+#else
+#define XFS_DIR2_SF_FIRSTENTRY(sfp) /* first entry in struct */ \
+ ((xfs_dir2_sf_entry_t *) \
+ ((char *)(sfp) + XFS_DIR2_SF_HDR_SIZE(sfp->hdr.i8count)))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR2_SF_NEXTENTRY)
+xfs_dir2_sf_entry_t *xfs_dir2_sf_nextentry(xfs_dir2_sf_t *sfp,
+ xfs_dir2_sf_entry_t *sfep);
+#define XFS_DIR2_SF_NEXTENTRY(sfp,sfep) xfs_dir2_sf_nextentry(sfp,sfep)
+#else
+#define XFS_DIR2_SF_NEXTENTRY(sfp,sfep) /* next entry in struct */ \
+ ((xfs_dir2_sf_entry_t *) \
+ ((char *)(sfep) + XFS_DIR2_SF_ENTSIZE_BYENTRY(sfp,sfep)))
+#endif
+
+/*
+ * Functions.
+ */
+
+extern int
+ xfs_dir2_block_sfsize(struct xfs_inode *dp,
+ struct xfs_dir2_block *block,
+ xfs_dir2_sf_hdr_t *sfhp);
+
+extern int
+ xfs_dir2_block_to_sf(struct xfs_da_args *args, struct xfs_dabuf *bp,
+ int size, xfs_dir2_sf_hdr_t *sfhp);
+
+extern int
+ xfs_dir2_sf_addname(struct xfs_da_args *args);
+
+extern int
+ xfs_dir2_sf_create(struct xfs_da_args *args, xfs_ino_t pino);
+
+extern int
+ xfs_dir2_sf_getdents(struct xfs_inode *dp, struct uio *uio, int *eofp,
+ struct xfs_dirent *dbp, xfs_dir2_put_t put);
+
+extern int
+ xfs_dir2_sf_lookup(struct xfs_da_args *args);
+
+extern int
+ xfs_dir2_sf_removename(struct xfs_da_args *args);
+
+extern int
+ xfs_dir2_sf_replace(struct xfs_da_args *args);
+
+#endif /* __XFS_DIR2_SF_H__ */
diff --git a/fs/xfs/xfs_dir2_trace.c b/fs/xfs/xfs_dir2_trace.c
new file mode 100644
index 00000000000000..9d64173931408d
--- /dev/null
+++ b/fs/xfs/xfs_dir2_trace.c
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+/*
+ * xfs_dir2_trace.c
+ * Tracing for xfs v2 directories.
+ */
+#include "xfs.h"
+
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode.h"
+#include "xfs_da_btree.h"
+#include "xfs_dir2_trace.h"
+
+#ifdef XFS_DIR2_TRACE
+ktrace_t *xfs_dir2_trace_buf;
+
+/*
+ * Enter something in the trace buffers.
+ */
+static void
+xfs_dir2_trace_enter(
+ xfs_inode_t *dp,
+ int type,
+ char *where,
+ char *name,
+ int namelen,
+ void *a0,
+ void *a1,
+ void *a2,
+ void *a3,
+ void *a4,
+ void *a5,
+ void *a6,
+ void *a7)
+{
+ void *n[5];
+
+ ASSERT(xfs_dir2_trace_buf);
+ ASSERT(dp->i_dir_trace);
+ if (name)
+ memcpy(n, name, min((int)sizeof(n), namelen));
+ else
+ memset((char *)n, 0, sizeof(n));
+ ktrace_enter(xfs_dir2_trace_buf,
+ (void *)(long)type, (void *)where,
+ (void *)a0, (void *)a1, (void *)a2, (void *)a3,
+ (void *)a4, (void *)a5, (void *)a6, (void *)a7,
+ (void *)(long)namelen,
+ (void *)n[0], (void *)n[1], (void *)n[2],
+ (void *)n[3], (void *)n[4]);
+ ktrace_enter(dp->i_dir_trace,
+ (void *)(long)type, (void *)where,
+ (void *)a0, (void *)a1, (void *)a2, (void *)a3,
+ (void *)a4, (void *)a5, (void *)a6, (void *)a7,
+ (void *)(long)namelen,
+ (void *)n[0], (void *)n[1], (void *)n[2],
+ (void *)n[3], (void *)n[4]);
+}
+
+void
+xfs_dir2_trace_args(
+ char *where,
+ xfs_da_args_t *args)
+{
+ xfs_dir2_trace_enter(args->dp, XFS_DIR2_KTRACE_ARGS, where,
+ (char *)args->name, (int)args->namelen,
+ (void *)(unsigned long)args->hashval,
+ (void *)((unsigned long)(args->inumber >> 32)),
+ (void *)((unsigned long)(args->inumber & 0xFFFFFFFF)),
+ (void *)args->dp, (void *)args->trans,
+ (void *)(unsigned long)args->justcheck, NULL, NULL);
+}
+
+void
+xfs_dir2_trace_args_b(
+ char *where,
+ xfs_da_args_t *args,
+ xfs_dabuf_t *bp)
+{
+ xfs_dir2_trace_enter(args->dp, XFS_DIR2_KTRACE_ARGS_B, where,
+ (char *)args->name, (int)args->namelen,
+ (void *)(unsigned long)args->hashval,
+ (void *)((unsigned long)(args->inumber >> 32)),
+ (void *)((unsigned long)(args->inumber & 0xFFFFFFFF)),
+ (void *)args->dp, (void *)args->trans,
+ (void *)(unsigned long)args->justcheck,
+ (void *)(bp ? bp->bps[0] : NULL), NULL);
+}
+
+void
+xfs_dir2_trace_args_bb(
+ char *where,
+ xfs_da_args_t *args,
+ xfs_dabuf_t *lbp,
+ xfs_dabuf_t *dbp)
+{
+ xfs_dir2_trace_enter(args->dp, XFS_DIR2_KTRACE_ARGS_BB, where,
+ (char *)args->name, (int)args->namelen,
+ (void *)(unsigned long)args->hashval,
+ (void *)((unsigned long)(args->inumber >> 32)),
+ (void *)((unsigned long)(args->inumber & 0xFFFFFFFF)),
+ (void *)args->dp, (void *)args->trans,
+ (void *)(unsigned long)args->justcheck,
+ (void *)(lbp ? lbp->bps[0] : NULL),
+ (void *)(dbp ? dbp->bps[0] : NULL));
+}
+
+void
+xfs_dir2_trace_args_bibii(
+ char *where,
+ xfs_da_args_t *args,
+ xfs_dabuf_t *bs,
+ int ss,
+ xfs_dabuf_t *bd,
+ int sd,
+ int c)
+{
+ xfs_buf_t *bpbs = bs ? bs->bps[0] : NULL;
+ xfs_buf_t *bpbd = bd ? bd->bps[0] : NULL;
+
+ xfs_dir2_trace_enter(args->dp, XFS_DIR2_KTRACE_ARGS_BIBII, where,
+ (char *)args->name, (int)args->namelen,
+ (void *)args->dp, (void *)args->trans,
+ (void *)bpbs, (void *)(long)ss, (void *)bpbd, (void *)(long)sd,
+ (void *)(long)c, NULL);
+}
+
+void
+xfs_dir2_trace_args_db(
+ char *where,
+ xfs_da_args_t *args,
+ xfs_dir2_db_t db,
+ xfs_dabuf_t *bp)
+{
+ xfs_buf_t *dbp = bp ? bp->bps[0] : NULL;
+
+ xfs_dir2_trace_enter(args->dp, XFS_DIR2_KTRACE_ARGS_DB, where,
+ (char *)args->name, (int)args->namelen,
+ (void *)(unsigned long)args->hashval,
+ (void *)((unsigned long)(args->inumber >> 32)),
+ (void *)((unsigned long)(args->inumber & 0xFFFFFFFF)),
+ (void *)args->dp, (void *)args->trans,
+ (void *)(unsigned long)args->justcheck, (void *)(long)db,
+ (void *)dbp);
+}
+
+void
+xfs_dir2_trace_args_i(
+ char *where,
+ xfs_da_args_t *args,
+ xfs_ino_t i)
+{
+ xfs_dir2_trace_enter(args->dp, XFS_DIR2_KTRACE_ARGS_I, where,
+ (char *)args->name, (int)args->namelen,
+ (void *)(unsigned long)args->hashval,
+ (void *)((unsigned long)(args->inumber >> 32)),
+ (void *)((unsigned long)(args->inumber & 0xFFFFFFFF)),
+ (void *)args->dp, (void *)args->trans,
+ (void *)(unsigned long)args->justcheck,
+ (void *)((unsigned long)(i >> 32)),
+ (void *)((unsigned long)(i & 0xFFFFFFFF)));
+}
+
+void
+xfs_dir2_trace_args_s(
+ char *where,
+ xfs_da_args_t *args,
+ int s)
+{
+ xfs_dir2_trace_enter(args->dp, XFS_DIR2_KTRACE_ARGS_S, where,
+ (char *)args->name, (int)args->namelen,
+ (void *)(unsigned long)args->hashval,
+ (void *)((unsigned long)(args->inumber >> 32)),
+ (void *)((unsigned long)(args->inumber & 0xFFFFFFFF)),
+ (void *)args->dp, (void *)args->trans,
+ (void *)(unsigned long)args->justcheck, (void *)(long)s, NULL);
+}
+
+void
+xfs_dir2_trace_args_sb(
+ char *where,
+ xfs_da_args_t *args,
+ int s,
+ xfs_dabuf_t *bp)
+{
+ xfs_buf_t *dbp = bp ? bp->bps[0] : NULL;
+
+ xfs_dir2_trace_enter(args->dp, XFS_DIR2_KTRACE_ARGS_SB, where,
+ (char *)args->name, (int)args->namelen,
+ (void *)(unsigned long)args->hashval,
+ (void *)((unsigned long)(args->inumber >> 32)),
+ (void *)((unsigned long)(args->inumber & 0xFFFFFFFF)),
+ (void *)args->dp, (void *)args->trans,
+ (void *)(unsigned long)args->justcheck, (void *)(long)s,
+ (void *)dbp);
+}
+#endif /* XFS_DIR2_TRACE */
diff --git a/fs/xfs/xfs_dir2_trace.h b/fs/xfs/xfs_dir2_trace.h
new file mode 100644
index 00000000000000..0a178bffa806ee
--- /dev/null
+++ b/fs/xfs/xfs_dir2_trace.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_DIR2_TRACE_H__
+#define __XFS_DIR2_TRACE_H__
+
+/*
+ * Tracing for xfs v2 directories.
+ */
+
+#if defined(XFS_DIR2_TRACE)
+
+struct ktrace;
+struct xfs_dabuf;
+struct xfs_da_args;
+
+#define XFS_DIR2_GTRACE_SIZE 4096 /* global buffer */
+#define XFS_DIR2_KTRACE_SIZE 32 /* per-inode buffer */
+extern struct ktrace *xfs_dir2_trace_buf;
+
+#define XFS_DIR2_KTRACE_ARGS 1 /* args only */
+#define XFS_DIR2_KTRACE_ARGS_B 2 /* args + buffer */
+#define XFS_DIR2_KTRACE_ARGS_BB 3 /* args + 2 buffers */
+#define XFS_DIR2_KTRACE_ARGS_DB 4 /* args, db, buffer */
+#define XFS_DIR2_KTRACE_ARGS_I 5 /* args, inum */
+#define XFS_DIR2_KTRACE_ARGS_S 6 /* args, int */
+#define XFS_DIR2_KTRACE_ARGS_SB 7 /* args, int, buffer */
+#define XFS_DIR2_KTRACE_ARGS_BIBII 8 /* args, buf/int/buf/int/int */
+
+void xfs_dir2_trace_args(char *where, struct xfs_da_args *args);
+void xfs_dir2_trace_args_b(char *where, struct xfs_da_args *args,
+ struct xfs_dabuf *bp);
+void xfs_dir2_trace_args_bb(char *where, struct xfs_da_args *args,
+ struct xfs_dabuf *lbp, struct xfs_dabuf *dbp);
+void xfs_dir2_trace_args_bibii(char *where, struct xfs_da_args *args,
+ struct xfs_dabuf *bs, int ss,
+ struct xfs_dabuf *bd, int sd, int c);
+void xfs_dir2_trace_args_db(char *where, struct xfs_da_args *args,
+ xfs_dir2_db_t db, struct xfs_dabuf *bp);
+void xfs_dir2_trace_args_i(char *where, struct xfs_da_args *args, xfs_ino_t i);
+void xfs_dir2_trace_args_s(char *where, struct xfs_da_args *args, int s);
+void xfs_dir2_trace_args_sb(char *where, struct xfs_da_args *args, int s,
+ struct xfs_dabuf *bp);
+
+#else /* XFS_DIR2_TRACE */
+
+#define xfs_dir2_trace_args(where, args)
+#define xfs_dir2_trace_args_b(where, args, bp)
+#define xfs_dir2_trace_args_bb(where, args, lbp, dbp)
+#define xfs_dir2_trace_args_bibii(where, args, bs, ss, bd, sd, c)
+#define xfs_dir2_trace_args_db(where, args, db, bp)
+#define xfs_dir2_trace_args_i(where, args, i)
+#define xfs_dir2_trace_args_s(where, args, s)
+#define xfs_dir2_trace_args_sb(where, args, s, bp)
+
+#endif /* XFS_DIR2_TRACE */
+
+#endif /* __XFS_DIR2_TRACE_H__ */
diff --git a/fs/xfs/xfs_dir_leaf.c b/fs/xfs/xfs_dir_leaf.c
new file mode 100644
index 00000000000000..2f889ce88f4ccf
--- /dev/null
+++ b/fs/xfs/xfs_dir_leaf.c
@@ -0,0 +1,2231 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+/*
+ * xfs_dir_leaf.c
+ *
+ * GROT: figure out how to recover gracefully when bmap returns ENOSPC.
+ */
+
+#include "xfs.h"
+
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_alloc.h"
+#include "xfs_btree.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode_item.h"
+#include "xfs_inode.h"
+#include "xfs_bmap.h"
+#include "xfs_da_btree.h"
+#include "xfs_dir_leaf.h"
+#include "xfs_error.h"
+
+/*
+ * xfs_dir_leaf.c
+ *
+ * Routines to implement leaf blocks of directories as Btrees of hashed names.
+ */
+
+/*========================================================================
+ * Function prototypes for the kernel.
+ *========================================================================*/
+
+/*
+ * Routines used for growing the Btree.
+ */
+STATIC void xfs_dir_leaf_add_work(xfs_dabuf_t *leaf_buffer, xfs_da_args_t *args,
+ int insertion_index,
+ int freemap_index);
+STATIC int xfs_dir_leaf_compact(xfs_trans_t *trans, xfs_dabuf_t *leaf_buffer,
+ int musthave, int justcheck);
+STATIC void xfs_dir_leaf_rebalance(xfs_da_state_t *state,
+ xfs_da_state_blk_t *blk1,
+ xfs_da_state_blk_t *blk2);
+STATIC int xfs_dir_leaf_figure_balance(xfs_da_state_t *state,
+ xfs_da_state_blk_t *leaf_blk_1,
+ xfs_da_state_blk_t *leaf_blk_2,
+ int *number_entries_in_blk1,
+ int *number_namebytes_in_blk1);
+
+/*
+ * Utility routines.
+ */
+STATIC void xfs_dir_leaf_moveents(xfs_dir_leafblock_t *src_leaf,
+ int src_start,
+ xfs_dir_leafblock_t *dst_leaf,
+ int dst_start, int move_count,
+ xfs_mount_t *mp);
+
+
+/*========================================================================
+ * External routines when dirsize < XFS_IFORK_DSIZE(dp).
+ *========================================================================*/
+
+
+/*
+ * Validate a given inode number.
+ */
+int
+xfs_dir_ino_validate(xfs_mount_t *mp, xfs_ino_t ino)
+{
+ xfs_agblock_t agblkno;
+ xfs_agino_t agino;
+ xfs_agnumber_t agno;
+ int ino_ok;
+ int ioff;
+
+ agno = XFS_INO_TO_AGNO(mp, ino);
+ agblkno = XFS_INO_TO_AGBNO(mp, ino);
+ ioff = XFS_INO_TO_OFFSET(mp, ino);
+ agino = XFS_OFFBNO_TO_AGINO(mp, agblkno, ioff);
+ ino_ok =
+ agno < mp->m_sb.sb_agcount &&
+ agblkno < mp->m_sb.sb_agblocks &&
+ agblkno != 0 &&
+ ioff < (1 << mp->m_sb.sb_inopblog) &&
+ XFS_AGINO_TO_INO(mp, agno, agino) == ino;
+ if (unlikely(XFS_TEST_ERROR(!ino_ok, mp, XFS_ERRTAG_DIR_INO_VALIDATE,
+ XFS_RANDOM_DIR_INO_VALIDATE))) {
+ xfs_fs_cmn_err(CE_WARN, mp, "Invalid inode number 0x%Lx",
+ (unsigned long long) ino);
+ XFS_ERROR_REPORT("xfs_dir_ino_validate", XFS_ERRLEVEL_LOW, mp);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ return 0;
+}
+
+/*
+ * Create the initial contents of a shortform directory.
+ */
+int
+xfs_dir_shortform_create(xfs_da_args_t *args, xfs_ino_t parent)
+{
+ xfs_dir_sf_hdr_t *hdr;
+ xfs_inode_t *dp;
+
+ dp = args->dp;
+ ASSERT(dp != NULL);
+ ASSERT(dp->i_d.di_size == 0);
+ if (dp->i_d.di_format == XFS_DINODE_FMT_EXTENTS) {
+ dp->i_df.if_flags &= ~XFS_IFEXTENTS; /* just in case */
+ dp->i_d.di_format = XFS_DINODE_FMT_LOCAL;
+ xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE);
+ dp->i_df.if_flags |= XFS_IFINLINE;
+ }
+ ASSERT(dp->i_df.if_flags & XFS_IFINLINE);
+ ASSERT(dp->i_df.if_bytes == 0);
+ xfs_idata_realloc(dp, sizeof(*hdr), XFS_DATA_FORK);
+ hdr = (xfs_dir_sf_hdr_t *)dp->i_df.if_u1.if_data;
+ XFS_DIR_SF_PUT_DIRINO_ARCH(&parent, &hdr->parent, ARCH_CONVERT);
+
+ INT_ZERO(hdr->count, ARCH_CONVERT);
+ dp->i_d.di_size = sizeof(*hdr);
+ xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA);
+ return(0);
+}
+
+/*
+ * Add a name to the shortform directory structure.
+ * Overflow from the inode has already been checked for.
+ */
+int
+xfs_dir_shortform_addname(xfs_da_args_t *args)
+{
+ xfs_dir_shortform_t *sf;
+ xfs_dir_sf_entry_t *sfe;
+ int i, offset, size;
+ xfs_inode_t *dp;
+
+ dp = args->dp;
+ ASSERT(dp->i_df.if_flags & XFS_IFINLINE);
+ /*
+ * Catch the case where the conversion from shortform to leaf
+ * failed part way through.
+ */
+ if (dp->i_d.di_size < sizeof(xfs_dir_sf_hdr_t)) {
+ ASSERT(XFS_FORCED_SHUTDOWN(dp->i_mount));
+ return XFS_ERROR(EIO);
+ }
+ ASSERT(dp->i_df.if_bytes == dp->i_d.di_size);
+ ASSERT(dp->i_df.if_u1.if_data != NULL);
+ sf = (xfs_dir_shortform_t *)dp->i_df.if_u1.if_data;
+ sfe = &sf->list[0];
+ for (i = INT_GET(sf->hdr.count, ARCH_CONVERT)-1; i >= 0; i--) {
+ if (sfe->namelen == args->namelen &&
+ args->name[0] == sfe->name[0] &&
+ memcmp(args->name, sfe->name, args->namelen) == 0)
+ return(XFS_ERROR(EEXIST));
+ sfe = XFS_DIR_SF_NEXTENTRY(sfe);
+ }
+
+ offset = (int)((char *)sfe - (char *)sf);
+ size = XFS_DIR_SF_ENTSIZE_BYNAME(args->namelen);
+ xfs_idata_realloc(dp, size, XFS_DATA_FORK);
+ sf = (xfs_dir_shortform_t *)dp->i_df.if_u1.if_data;
+ sfe = (xfs_dir_sf_entry_t *)((char *)sf + offset);
+
+ XFS_DIR_SF_PUT_DIRINO_ARCH(&args->inumber, &sfe->inumber, ARCH_CONVERT);
+ sfe->namelen = args->namelen;
+ memcpy(sfe->name, args->name, sfe->namelen);
+ INT_MOD(sf->hdr.count, ARCH_CONVERT, +1);
+
+ dp->i_d.di_size += size;
+ xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA);
+
+ return(0);
+}
+
+/*
+ * Remove a name from the shortform directory structure.
+ */
+int
+xfs_dir_shortform_removename(xfs_da_args_t *args)
+{
+ xfs_dir_shortform_t *sf;
+ xfs_dir_sf_entry_t *sfe;
+ int base, size = 0, i;
+ xfs_inode_t *dp;
+
+ dp = args->dp;
+ ASSERT(dp->i_df.if_flags & XFS_IFINLINE);
+ /*
+ * Catch the case where the conversion from shortform to leaf
+ * failed part way through.
+ */
+ if (dp->i_d.di_size < sizeof(xfs_dir_sf_hdr_t)) {
+ ASSERT(XFS_FORCED_SHUTDOWN(dp->i_mount));
+ return XFS_ERROR(EIO);
+ }
+ ASSERT(dp->i_df.if_bytes == dp->i_d.di_size);
+ ASSERT(dp->i_df.if_u1.if_data != NULL);
+ base = sizeof(xfs_dir_sf_hdr_t);
+ sf = (xfs_dir_shortform_t *)dp->i_df.if_u1.if_data;
+ sfe = &sf->list[0];
+ for (i = INT_GET(sf->hdr.count, ARCH_CONVERT)-1; i >= 0; i--) {
+ size = XFS_DIR_SF_ENTSIZE_BYENTRY(sfe);
+ if (sfe->namelen == args->namelen &&
+ sfe->name[0] == args->name[0] &&
+ memcmp(sfe->name, args->name, args->namelen) == 0)
+ break;
+ base += size;
+ sfe = XFS_DIR_SF_NEXTENTRY(sfe);
+ }
+ if (i < 0) {
+ ASSERT(args->oknoent);
+ return(XFS_ERROR(ENOENT));
+ }
+
+ if ((base + size) != dp->i_d.di_size) {
+ memmove(&((char *)sf)[base], &((char *)sf)[base+size],
+ dp->i_d.di_size - (base+size));
+ }
+ INT_MOD(sf->hdr.count, ARCH_CONVERT, -1);
+
+ xfs_idata_realloc(dp, -size, XFS_DATA_FORK);
+ dp->i_d.di_size -= size;
+ xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA);
+
+ return(0);
+}
+
+/*
+ * Look up a name in a shortform directory structure.
+ */
+int
+xfs_dir_shortform_lookup(xfs_da_args_t *args)
+{
+ xfs_dir_shortform_t *sf;
+ xfs_dir_sf_entry_t *sfe;
+ int i;
+ xfs_inode_t *dp;
+
+ dp = args->dp;
+ ASSERT(dp->i_df.if_flags & XFS_IFINLINE);
+ /*
+ * Catch the case where the conversion from shortform to leaf
+ * failed part way through.
+ */
+ if (dp->i_d.di_size < sizeof(xfs_dir_sf_hdr_t)) {
+ ASSERT(XFS_FORCED_SHUTDOWN(dp->i_mount));
+ return XFS_ERROR(EIO);
+ }
+ ASSERT(dp->i_df.if_bytes == dp->i_d.di_size);
+ ASSERT(dp->i_df.if_u1.if_data != NULL);
+ sf = (xfs_dir_shortform_t *)dp->i_df.if_u1.if_data;
+ if (args->namelen == 2 &&
+ args->name[0] == '.' && args->name[1] == '.') {
+ XFS_DIR_SF_GET_DIRINO_ARCH(&sf->hdr.parent, &args->inumber, ARCH_CONVERT);
+ return(XFS_ERROR(EEXIST));
+ }
+ if (args->namelen == 1 && args->name[0] == '.') {
+ args->inumber = dp->i_ino;
+ return(XFS_ERROR(EEXIST));
+ }
+ sfe = &sf->list[0];
+ for (i = INT_GET(sf->hdr.count, ARCH_CONVERT)-1; i >= 0; i--) {
+ if (sfe->namelen == args->namelen &&
+ sfe->name[0] == args->name[0] &&
+ memcmp(args->name, sfe->name, args->namelen) == 0) {
+ XFS_DIR_SF_GET_DIRINO_ARCH(&sfe->inumber, &args->inumber, ARCH_CONVERT);
+ return(XFS_ERROR(EEXIST));
+ }
+ sfe = XFS_DIR_SF_NEXTENTRY(sfe);
+ }
+ ASSERT(args->oknoent);
+ return(XFS_ERROR(ENOENT));
+}
+
+/*
+ * Convert from using the shortform to the leaf.
+ */
+int
+xfs_dir_shortform_to_leaf(xfs_da_args_t *iargs)
+{
+ xfs_inode_t *dp;
+ xfs_dir_shortform_t *sf;
+ xfs_dir_sf_entry_t *sfe;
+ xfs_da_args_t args;
+ xfs_ino_t inumber;
+ char *tmpbuffer;
+ int retval, i, size;
+ xfs_dablk_t blkno;
+ xfs_dabuf_t *bp;
+
+ dp = iargs->dp;
+ /*
+ * Catch the case where the conversion from shortform to leaf
+ * failed part way through.
+ */
+ if (dp->i_d.di_size < sizeof(xfs_dir_sf_hdr_t)) {
+ ASSERT(XFS_FORCED_SHUTDOWN(dp->i_mount));
+ return XFS_ERROR(EIO);
+ }
+ ASSERT(dp->i_df.if_bytes == dp->i_d.di_size);
+ ASSERT(dp->i_df.if_u1.if_data != NULL);
+ size = dp->i_df.if_bytes;
+ tmpbuffer = kmem_alloc(size, KM_SLEEP);
+ ASSERT(tmpbuffer != NULL);
+
+ memcpy(tmpbuffer, dp->i_df.if_u1.if_data, size);
+
+ sf = (xfs_dir_shortform_t *)tmpbuffer;
+ XFS_DIR_SF_GET_DIRINO_ARCH(&sf->hdr.parent, &inumber, ARCH_CONVERT);
+
+ xfs_idata_realloc(dp, -size, XFS_DATA_FORK);
+ dp->i_d.di_size = 0;
+ xfs_trans_log_inode(iargs->trans, dp, XFS_ILOG_CORE);
+ retval = xfs_da_grow_inode(iargs, &blkno);
+ if (retval)
+ goto out;
+
+ ASSERT(blkno == 0);
+ retval = xfs_dir_leaf_create(iargs, blkno, &bp);
+ if (retval)
+ goto out;
+ xfs_da_buf_done(bp);
+
+ args.name = ".";
+ args.namelen = 1;
+ args.hashval = xfs_dir_hash_dot;
+ args.inumber = dp->i_ino;
+ args.dp = dp;
+ args.firstblock = iargs->firstblock;
+ args.flist = iargs->flist;
+ args.total = iargs->total;
+ args.whichfork = XFS_DATA_FORK;
+ args.trans = iargs->trans;
+ args.justcheck = 0;
+ args.addname = args.oknoent = 1;
+ retval = xfs_dir_leaf_addname(&args);
+ if (retval)
+ goto out;
+
+ args.name = "..";
+ args.namelen = 2;
+ args.hashval = xfs_dir_hash_dotdot;
+ args.inumber = inumber;
+ retval = xfs_dir_leaf_addname(&args);
+ if (retval)
+ goto out;
+
+ sfe = &sf->list[0];
+ for (i = 0; i < INT_GET(sf->hdr.count, ARCH_CONVERT); i++) {
+ args.name = (char *)(sfe->name);
+ args.namelen = sfe->namelen;
+ args.hashval = xfs_da_hashname((char *)(sfe->name),
+ sfe->namelen);
+ XFS_DIR_SF_GET_DIRINO_ARCH(&sfe->inumber, &args.inumber, ARCH_CONVERT);
+ retval = xfs_dir_leaf_addname(&args);
+ if (retval)
+ goto out;
+ sfe = XFS_DIR_SF_NEXTENTRY(sfe);
+ }
+ retval = 0;
+
+out:
+ kmem_free(tmpbuffer, size);
+ return(retval);
+}
+
+STATIC int
+xfs_dir_shortform_compare(const void *a, const void *b)
+{
+ xfs_dir_sf_sort_t *sa, *sb;
+
+ sa = (xfs_dir_sf_sort_t *)a;
+ sb = (xfs_dir_sf_sort_t *)b;
+ if (sa->hash < sb->hash)
+ return -1;
+ else if (sa->hash > sb->hash)
+ return 1;
+ else
+ return sa->entno - sb->entno;
+}
+
+/*
+ * Copy out directory entries for getdents(), for shortform directories.
+ */
+/*ARGSUSED*/
+int
+xfs_dir_shortform_getdents(xfs_inode_t *dp, uio_t *uio, int *eofp,
+ xfs_dirent_t *dbp, xfs_dir_put_t put)
+{
+ xfs_dir_shortform_t *sf;
+ xfs_dir_sf_entry_t *sfe;
+ int retval, i, sbsize, nsbuf, lastresid=0, want_entno;
+ xfs_mount_t *mp;
+ xfs_dahash_t cookhash, hash;
+ xfs_dir_put_args_t p;
+ xfs_dir_sf_sort_t *sbuf, *sbp;
+
+ mp = dp->i_mount;
+ sf = (xfs_dir_shortform_t *)dp->i_df.if_u1.if_data;
+ cookhash = XFS_DA_COOKIE_HASH(mp, uio->uio_offset);
+ want_entno = XFS_DA_COOKIE_ENTRY(mp, uio->uio_offset);
+ nsbuf = INT_GET(sf->hdr.count, ARCH_CONVERT) + 2;
+ sbsize = (nsbuf + 1) * sizeof(*sbuf);
+ sbp = sbuf = kmem_alloc(sbsize, KM_SLEEP);
+
+ xfs_dir_trace_g_du("sf: start", dp, uio);
+
+ /*
+ * Collect all the entries into the buffer.
+ * Entry 0 is .
+ */
+ sbp->entno = 0;
+ sbp->seqno = 0;
+ sbp->hash = xfs_dir_hash_dot;
+ sbp->ino = dp->i_ino;
+ sbp->name = ".";
+ sbp->namelen = 1;
+ sbp++;
+
+ /*
+ * Entry 1 is ..
+ */
+ sbp->entno = 1;
+ sbp->seqno = 0;
+ sbp->hash = xfs_dir_hash_dotdot;
+ sbp->ino = XFS_GET_DIR_INO_ARCH(mp, sf->hdr.parent, ARCH_CONVERT);
+ sbp->name = "..";
+ sbp->namelen = 2;
+ sbp++;
+
+ /*
+ * Scan the directory data for the rest of the entries.
+ */
+ for (i = 0, sfe = &sf->list[0];
+ i < INT_GET(sf->hdr.count, ARCH_CONVERT); i++) {
+
+ if (unlikely(
+ ((char *)sfe < (char *)sf) ||
+ ((char *)sfe >= ((char *)sf + dp->i_df.if_bytes)))) {
+ xfs_dir_trace_g_du("sf: corrupted", dp, uio);
+ XFS_CORRUPTION_ERROR("xfs_dir_shortform_getdents",
+ XFS_ERRLEVEL_LOW, mp, sfe);
+ kmem_free(sbuf, sbsize);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+
+ sbp->entno = i + 2;
+ sbp->seqno = 0;
+ sbp->hash = xfs_da_hashname((char *)sfe->name, sfe->namelen);
+ sbp->ino = XFS_GET_DIR_INO_ARCH(mp, sfe->inumber, ARCH_CONVERT);
+ sbp->name = (char *)sfe->name;
+ sbp->namelen = sfe->namelen;
+ sfe = XFS_DIR_SF_NEXTENTRY(sfe);
+ sbp++;
+ }
+
+ /*
+ * Sort the entries on hash then entno.
+ */
+ qsort(sbuf, nsbuf, sizeof(*sbuf), xfs_dir_shortform_compare);
+ /*
+ * Stuff in last entry.
+ */
+ sbp->entno = nsbuf;
+ sbp->hash = XFS_DA_MAXHASH;
+ sbp->seqno = 0;
+ /*
+ * Figure out the sequence numbers in case there's a hash duplicate.
+ */
+ for (hash = sbuf->hash, sbp = sbuf + 1;
+ sbp < &sbuf[nsbuf + 1]; sbp++) {
+ if (sbp->hash == hash)
+ sbp->seqno = sbp[-1].seqno + 1;
+ else
+ hash = sbp->hash;
+ }
+
+ /*
+ * Set up put routine.
+ */
+ p.dbp = dbp;
+ p.put = put;
+ p.uio = uio;
+
+ /*
+ * Find our place.
+ */
+ for (sbp = sbuf; sbp < &sbuf[nsbuf + 1]; sbp++) {
+ if (sbp->hash > cookhash ||
+ (sbp->hash == cookhash && sbp->seqno >= want_entno))
+ break;
+ }
+
+ /*
+ * Did we fail to find anything? We stop at the last entry,
+ * the one we put maxhash into.
+ */
+ if (sbp == &sbuf[nsbuf]) {
+ kmem_free(sbuf, sbsize);
+ xfs_dir_trace_g_du("sf: hash beyond end", dp, uio);
+ uio->uio_offset = XFS_DA_MAKE_COOKIE(mp, 0, 0, XFS_DA_MAXHASH);
+ *eofp = 1;
+ return 0;
+ }
+
+ /*
+ * Loop putting entries into the user buffer.
+ */
+ while (sbp < &sbuf[nsbuf]) {
+ /*
+ * Save the first resid in a run of equal-hashval entries
+ * so that we can back them out if they don't all fit.
+ */
+ if (sbp->seqno == 0 || sbp == sbuf)
+ lastresid = uio->uio_resid;
+ XFS_PUT_COOKIE(p.cook, mp, 0, sbp[1].seqno, sbp[1].hash);
+ p.ino = sbp->ino;
+#if XFS_BIG_INUMS
+ p.ino += mp->m_inoadd;
+#endif
+ p.name = sbp->name;
+ p.namelen = sbp->namelen;
+ retval = p.put(&p);
+ if (!p.done) {
+ uio->uio_offset =
+ XFS_DA_MAKE_COOKIE(mp, 0, 0, sbp->hash);
+ kmem_free(sbuf, sbsize);
+ uio->uio_resid = lastresid;
+ xfs_dir_trace_g_du("sf: E-O-B", dp, uio);
+ return retval;
+ }
+ sbp++;
+ }
+ kmem_free(sbuf, sbsize);
+ uio->uio_offset = p.cook.o;
+ *eofp = 1;
+ xfs_dir_trace_g_du("sf: E-O-F", dp, uio);
+ return 0;
+}
+
+/*
+ * Look up a name in a shortform directory structure, replace the inode number.
+ */
+int
+xfs_dir_shortform_replace(xfs_da_args_t *args)
+{
+ xfs_dir_shortform_t *sf;
+ xfs_dir_sf_entry_t *sfe;
+ xfs_inode_t *dp;
+ int i;
+
+ dp = args->dp;
+ ASSERT(dp->i_df.if_flags & XFS_IFINLINE);
+ /*
+ * Catch the case where the conversion from shortform to leaf
+ * failed part way through.
+ */
+ if (dp->i_d.di_size < sizeof(xfs_dir_sf_hdr_t)) {
+ ASSERT(XFS_FORCED_SHUTDOWN(dp->i_mount));
+ return XFS_ERROR(EIO);
+ }
+ ASSERT(dp->i_df.if_bytes == dp->i_d.di_size);
+ ASSERT(dp->i_df.if_u1.if_data != NULL);
+ sf = (xfs_dir_shortform_t *)dp->i_df.if_u1.if_data;
+ if (args->namelen == 2 &&
+ args->name[0] == '.' && args->name[1] == '.') {
+ /* XXX - replace assert? */
+ XFS_DIR_SF_PUT_DIRINO_ARCH(&args->inumber, &sf->hdr.parent, ARCH_CONVERT);
+ xfs_trans_log_inode(args->trans, dp, XFS_ILOG_DDATA);
+ return(0);
+ }
+ ASSERT(args->namelen != 1 || args->name[0] != '.');
+ sfe = &sf->list[0];
+ for (i = INT_GET(sf->hdr.count, ARCH_CONVERT)-1; i >= 0; i--) {
+ if (sfe->namelen == args->namelen &&
+ sfe->name[0] == args->name[0] &&
+ memcmp(args->name, sfe->name, args->namelen) == 0) {
+ ASSERT(memcmp((char *)&args->inumber,
+ (char *)&sfe->inumber, sizeof(xfs_ino_t)));
+ XFS_DIR_SF_PUT_DIRINO_ARCH(&args->inumber, &sfe->inumber, ARCH_CONVERT);
+ xfs_trans_log_inode(args->trans, dp, XFS_ILOG_DDATA);
+ return(0);
+ }
+ sfe = XFS_DIR_SF_NEXTENTRY(sfe);
+ }
+ ASSERT(args->oknoent);
+ return(XFS_ERROR(ENOENT));
+}
+
+/*
+ * Convert a leaf directory to shortform structure
+ */
+int
+xfs_dir_leaf_to_shortform(xfs_da_args_t *iargs)
+{
+ xfs_dir_leafblock_t *leaf;
+ xfs_dir_leaf_hdr_t *hdr;
+ xfs_dir_leaf_entry_t *entry;
+ xfs_dir_leaf_name_t *namest;
+ xfs_da_args_t args;
+ xfs_inode_t *dp;
+ xfs_ino_t parent;
+ char *tmpbuffer;
+ int retval, i;
+ xfs_dabuf_t *bp;
+
+ dp = iargs->dp;
+ tmpbuffer = kmem_alloc(XFS_LBSIZE(dp->i_mount), KM_SLEEP);
+ ASSERT(tmpbuffer != NULL);
+
+ retval = xfs_da_read_buf(iargs->trans, iargs->dp, 0, -1, &bp,
+ XFS_DATA_FORK);
+ if (retval)
+ goto out;
+ ASSERT(bp != NULL);
+ memcpy(tmpbuffer, bp->data, XFS_LBSIZE(dp->i_mount));
+ leaf = (xfs_dir_leafblock_t *)tmpbuffer;
+ ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) == XFS_DIR_LEAF_MAGIC);
+ memset(bp->data, 0, XFS_LBSIZE(dp->i_mount));
+
+ /*
+ * Find and special case the parent inode number
+ */
+ hdr = &leaf->hdr;
+ entry = &leaf->entries[0];
+ for (i = INT_GET(hdr->count, ARCH_CONVERT)-1; i >= 0; entry++, i--) {
+ namest = XFS_DIR_LEAF_NAMESTRUCT(leaf, INT_GET(entry->nameidx, ARCH_CONVERT));
+ if ((entry->namelen == 2) &&
+ (namest->name[0] == '.') &&
+ (namest->name[1] == '.')) {
+ XFS_DIR_SF_GET_DIRINO_ARCH(&namest->inumber, &parent, ARCH_CONVERT);
+ INT_ZERO(entry->nameidx, ARCH_CONVERT);
+ } else if ((entry->namelen == 1) && (namest->name[0] == '.')) {
+ INT_ZERO(entry->nameidx, ARCH_CONVERT);
+ }
+ }
+ retval = xfs_da_shrink_inode(iargs, 0, bp);
+ if (retval)
+ goto out;
+ retval = xfs_dir_shortform_create(iargs, parent);
+ if (retval)
+ goto out;
+
+ /*
+ * Copy the rest of the filenames
+ */
+ entry = &leaf->entries[0];
+ args.dp = dp;
+ args.firstblock = iargs->firstblock;
+ args.flist = iargs->flist;
+ args.total = iargs->total;
+ args.whichfork = XFS_DATA_FORK;
+ args.trans = iargs->trans;
+ args.justcheck = 0;
+ args.addname = args.oknoent = 1;
+ for (i = 0; i < INT_GET(hdr->count, ARCH_CONVERT); entry++, i++) {
+ if (INT_ISZERO(entry->nameidx, ARCH_CONVERT))
+ continue;
+ namest = XFS_DIR_LEAF_NAMESTRUCT(leaf, INT_GET(entry->nameidx, ARCH_CONVERT));
+ args.name = (char *)(namest->name);
+ args.namelen = entry->namelen;
+ args.hashval = INT_GET(entry->hashval, ARCH_CONVERT);
+ XFS_DIR_SF_GET_DIRINO_ARCH(&namest->inumber, &args.inumber, ARCH_CONVERT);
+ xfs_dir_shortform_addname(&args);
+ }
+
+out:
+ kmem_free(tmpbuffer, XFS_LBSIZE(dp->i_mount));
+ return(retval);
+}
+
+/*
+ * Convert from using a single leaf to a root node and a leaf.
+ */
+int
+xfs_dir_leaf_to_node(xfs_da_args_t *args)
+{
+ xfs_dir_leafblock_t *leaf;
+ xfs_da_intnode_t *node;
+ xfs_inode_t *dp;
+ xfs_dabuf_t *bp1, *bp2;
+ xfs_dablk_t blkno;
+ int retval;
+
+ dp = args->dp;
+ retval = xfs_da_grow_inode(args, &blkno);
+ ASSERT(blkno == 1);
+ if (retval)
+ return(retval);
+ retval = xfs_da_read_buf(args->trans, args->dp, 0, -1, &bp1,
+ XFS_DATA_FORK);
+ if (retval)
+ return(retval);
+ ASSERT(bp1 != NULL);
+ retval = xfs_da_get_buf(args->trans, args->dp, 1, -1, &bp2,
+ XFS_DATA_FORK);
+ if (retval) {
+ xfs_da_buf_done(bp1);
+ return(retval);
+ }
+ ASSERT(bp2 != NULL);
+ memcpy(bp2->data, bp1->data, XFS_LBSIZE(dp->i_mount));
+ xfs_da_buf_done(bp1);
+ xfs_da_log_buf(args->trans, bp2, 0, XFS_LBSIZE(dp->i_mount) - 1);
+
+ /*
+ * Set up the new root node.
+ */
+ retval = xfs_da_node_create(args, 0, 1, &bp1, XFS_DATA_FORK);
+ if (retval) {
+ xfs_da_buf_done(bp2);
+ return(retval);
+ }
+ node = bp1->data;
+ leaf = bp2->data;
+ ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) == XFS_DIR_LEAF_MAGIC);
+ INT_SET(node->btree[0].hashval, ARCH_CONVERT, INT_GET(leaf->entries[ INT_GET(leaf->hdr.count, ARCH_CONVERT)-1 ].hashval, ARCH_CONVERT));
+ xfs_da_buf_done(bp2);
+ INT_SET(node->btree[0].before, ARCH_CONVERT, blkno);
+ INT_SET(node->hdr.count, ARCH_CONVERT, 1);
+ xfs_da_log_buf(args->trans, bp1,
+ XFS_DA_LOGRANGE(node, &node->btree[0], sizeof(node->btree[0])));
+ xfs_da_buf_done(bp1);
+
+ return(retval);
+}
+
+
+/*========================================================================
+ * Routines used for growing the Btree.
+ *========================================================================*/
+
+/*
+ * Create the initial contents of a leaf directory
+ * or a leaf in a node directory.
+ */
+int
+xfs_dir_leaf_create(xfs_da_args_t *args, xfs_dablk_t blkno, xfs_dabuf_t **bpp)
+{
+ xfs_dir_leafblock_t *leaf;
+ xfs_dir_leaf_hdr_t *hdr;
+ xfs_inode_t *dp;
+ xfs_dabuf_t *bp;
+ int retval;
+
+ dp = args->dp;
+ ASSERT(dp != NULL);
+ retval = xfs_da_get_buf(args->trans, dp, blkno, -1, &bp, XFS_DATA_FORK);
+ if (retval)
+ return(retval);
+ ASSERT(bp != NULL);
+ leaf = bp->data;
+ memset((char *)leaf, 0, XFS_LBSIZE(dp->i_mount));
+ hdr = &leaf->hdr;
+ INT_SET(hdr->info.magic, ARCH_CONVERT, XFS_DIR_LEAF_MAGIC);
+ INT_SET(hdr->firstused, ARCH_CONVERT, XFS_LBSIZE(dp->i_mount));
+ if (INT_ISZERO(hdr->firstused, ARCH_CONVERT))
+ INT_SET(hdr->firstused, ARCH_CONVERT, XFS_LBSIZE(dp->i_mount) - 1);
+ INT_SET(hdr->freemap[0].base, ARCH_CONVERT, sizeof(xfs_dir_leaf_hdr_t));
+ INT_SET(hdr->freemap[0].size, ARCH_CONVERT, INT_GET(hdr->firstused, ARCH_CONVERT) - INT_GET(hdr->freemap[0].base, ARCH_CONVERT));
+
+ xfs_da_log_buf(args->trans, bp, 0, XFS_LBSIZE(dp->i_mount) - 1);
+
+ *bpp = bp;
+ return(0);
+}
+
+/*
+ * Split the leaf node, rebalance, then add the new entry.
+ */
+int
+xfs_dir_leaf_split(xfs_da_state_t *state, xfs_da_state_blk_t *oldblk,
+ xfs_da_state_blk_t *newblk)
+{
+ xfs_dablk_t blkno;
+ xfs_da_args_t *args;
+ int error;
+
+ /*
+ * Allocate space for a new leaf node.
+ */
+ args = state->args;
+ ASSERT(args != NULL);
+ ASSERT(oldblk->magic == XFS_DIR_LEAF_MAGIC);
+ error = xfs_da_grow_inode(args, &blkno);
+ if (error)
+ return(error);
+ error = xfs_dir_leaf_create(args, blkno, &newblk->bp);
+ if (error)
+ return(error);
+ newblk->blkno = blkno;
+ newblk->magic = XFS_DIR_LEAF_MAGIC;
+
+ /*
+ * Rebalance the entries across the two leaves.
+ */
+ xfs_dir_leaf_rebalance(state, oldblk, newblk);
+ error = xfs_da_blk_link(state, oldblk, newblk);
+ if (error)
+ return(error);
+
+ /*
+ * Insert the new entry in the correct block.
+ */
+ if (state->inleaf) {
+ error = xfs_dir_leaf_add(oldblk->bp, args, oldblk->index);
+ } else {
+ error = xfs_dir_leaf_add(newblk->bp, args, newblk->index);
+ }
+
+ /*
+ * Update last hashval in each block since we added the name.
+ */
+ oldblk->hashval = xfs_dir_leaf_lasthash(oldblk->bp, NULL);
+ newblk->hashval = xfs_dir_leaf_lasthash(newblk->bp, NULL);
+ return(error);
+}
+
+/*
+ * Add a name to the leaf directory structure.
+ *
+ * Must take into account fragmented leaves and leaves where spacemap has
+ * lost some freespace information (ie: holes).
+ */
+int
+xfs_dir_leaf_add(xfs_dabuf_t *bp, xfs_da_args_t *args, int index)
+{
+ xfs_dir_leafblock_t *leaf;
+ xfs_dir_leaf_hdr_t *hdr;
+ xfs_dir_leaf_map_t *map;
+ int tablesize, entsize, sum, i, tmp, error;
+
+ leaf = bp->data;
+ ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) == XFS_DIR_LEAF_MAGIC);
+ ASSERT((index >= 0) && (index <= INT_GET(leaf->hdr.count, ARCH_CONVERT)));
+ hdr = &leaf->hdr;
+ entsize = XFS_DIR_LEAF_ENTSIZE_BYNAME(args->namelen);
+
+ /*
+ * Search through freemap for first-fit on new name length.
+ * (may need to figure in size of entry struct too)
+ */
+ tablesize = (INT_GET(hdr->count, ARCH_CONVERT) + 1) * (uint)sizeof(xfs_dir_leaf_entry_t)
+ + (uint)sizeof(xfs_dir_leaf_hdr_t);
+ map = &hdr->freemap[XFS_DIR_LEAF_MAPSIZE-1];
+ for (sum = 0, i = XFS_DIR_LEAF_MAPSIZE-1; i >= 0; map--, i--) {
+ if (tablesize > INT_GET(hdr->firstused, ARCH_CONVERT)) {
+ sum += INT_GET(map->size, ARCH_CONVERT);
+ continue;
+ }
+ if (INT_ISZERO(map->size, ARCH_CONVERT))
+ continue; /* no space in this map */
+ tmp = entsize;
+ if (INT_GET(map->base, ARCH_CONVERT) < INT_GET(hdr->firstused, ARCH_CONVERT))
+ tmp += (uint)sizeof(xfs_dir_leaf_entry_t);
+ if (INT_GET(map->size, ARCH_CONVERT) >= tmp) {
+ if (!args->justcheck)
+ xfs_dir_leaf_add_work(bp, args, index, i);
+ return(0);
+ }
+ sum += INT_GET(map->size, ARCH_CONVERT);
+ }
+
+ /*
+ * If there are no holes in the address space of the block,
+ * and we don't have enough freespace, then compaction will do us
+ * no good and we should just give up.
+ */
+ if (!hdr->holes && (sum < entsize))
+ return(XFS_ERROR(ENOSPC));
+
+ /*
+ * Compact the entries to coalesce free space.
+ * Pass the justcheck flag so the checking pass can return
+ * an error, without changing anything, if it won't fit.
+ */
+ error = xfs_dir_leaf_compact(args->trans, bp,
+ args->total == 0 ?
+ entsize +
+ (uint)sizeof(xfs_dir_leaf_entry_t) : 0,
+ args->justcheck);
+ if (error)
+ return(error);
+ /*
+ * After compaction, the block is guaranteed to have only one
+ * free region, in freemap[0]. If it is not big enough, give up.
+ */
+ if (INT_GET(hdr->freemap[0].size, ARCH_CONVERT) <
+ (entsize + (uint)sizeof(xfs_dir_leaf_entry_t)))
+ return(XFS_ERROR(ENOSPC));
+
+ if (!args->justcheck)
+ xfs_dir_leaf_add_work(bp, args, index, 0);
+ return(0);
+}
+
+/*
+ * Add a name to a leaf directory structure.
+ */
+STATIC void
+xfs_dir_leaf_add_work(xfs_dabuf_t *bp, xfs_da_args_t *args, int index,
+ int mapindex)
+{
+ xfs_dir_leafblock_t *leaf;
+ xfs_dir_leaf_hdr_t *hdr;
+ xfs_dir_leaf_entry_t *entry;
+ xfs_dir_leaf_name_t *namest;
+ xfs_dir_leaf_map_t *map;
+ /* REFERENCED */
+ xfs_mount_t *mp;
+ int tmp, i;
+
+ leaf = bp->data;
+ ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) == XFS_DIR_LEAF_MAGIC);
+ hdr = &leaf->hdr;
+ ASSERT((mapindex >= 0) && (mapindex < XFS_DIR_LEAF_MAPSIZE));
+ ASSERT((index >= 0) && (index <= INT_GET(hdr->count, ARCH_CONVERT)));
+
+ /*
+ * Force open some space in the entry array and fill it in.
+ */
+ entry = &leaf->entries[index];
+ if (index < INT_GET(hdr->count, ARCH_CONVERT)) {
+ tmp = INT_GET(hdr->count, ARCH_CONVERT) - index;
+ tmp *= (uint)sizeof(xfs_dir_leaf_entry_t);
+ memmove(entry + 1, entry, tmp);
+ xfs_da_log_buf(args->trans, bp,
+ XFS_DA_LOGRANGE(leaf, entry, tmp + (uint)sizeof(*entry)));
+ }
+ INT_MOD(hdr->count, ARCH_CONVERT, +1);
+
+ /*
+ * Allocate space for the new string (at the end of the run).
+ */
+ map = &hdr->freemap[mapindex];
+ mp = args->trans->t_mountp;
+ ASSERT(INT_GET(map->base, ARCH_CONVERT) < XFS_LBSIZE(mp));
+ ASSERT(INT_GET(map->size, ARCH_CONVERT) >= XFS_DIR_LEAF_ENTSIZE_BYNAME(args->namelen));
+ ASSERT(INT_GET(map->size, ARCH_CONVERT) < XFS_LBSIZE(mp));
+ INT_MOD(map->size, ARCH_CONVERT, -(XFS_DIR_LEAF_ENTSIZE_BYNAME(args->namelen)));
+ INT_SET(entry->nameidx, ARCH_CONVERT, INT_GET(map->base, ARCH_CONVERT) + INT_GET(map->size, ARCH_CONVERT));
+ INT_SET(entry->hashval, ARCH_CONVERT, args->hashval);
+ entry->namelen = args->namelen;
+ xfs_da_log_buf(args->trans, bp,
+ XFS_DA_LOGRANGE(leaf, entry, sizeof(*entry)));
+
+ /*
+ * Copy the string and inode number into the new space.
+ */
+ namest = XFS_DIR_LEAF_NAMESTRUCT(leaf, INT_GET(entry->nameidx, ARCH_CONVERT));
+ XFS_DIR_SF_PUT_DIRINO_ARCH(&args->inumber, &namest->inumber, ARCH_CONVERT);
+ memcpy(namest->name, args->name, args->namelen);
+ xfs_da_log_buf(args->trans, bp,
+ XFS_DA_LOGRANGE(leaf, namest, XFS_DIR_LEAF_ENTSIZE_BYENTRY(entry)));
+
+ /*
+ * Update the control info for this leaf node
+ */
+ if (INT_GET(entry->nameidx, ARCH_CONVERT) < INT_GET(hdr->firstused, ARCH_CONVERT))
+ INT_COPY(hdr->firstused, entry->nameidx, ARCH_CONVERT);
+ ASSERT(INT_GET(hdr->firstused, ARCH_CONVERT) >= ((INT_GET(hdr->count, ARCH_CONVERT)*sizeof(*entry))+sizeof(*hdr)));
+ tmp = (INT_GET(hdr->count, ARCH_CONVERT)-1) * (uint)sizeof(xfs_dir_leaf_entry_t)
+ + (uint)sizeof(xfs_dir_leaf_hdr_t);
+ map = &hdr->freemap[0];
+ for (i = 0; i < XFS_DIR_LEAF_MAPSIZE; map++, i++) {
+ if (INT_GET(map->base, ARCH_CONVERT) == tmp) {
+ INT_MOD(map->base, ARCH_CONVERT, (uint)sizeof(xfs_dir_leaf_entry_t));
+ INT_MOD(map->size, ARCH_CONVERT, -((uint)sizeof(xfs_dir_leaf_entry_t)));
+ }
+ }
+ INT_MOD(hdr->namebytes, ARCH_CONVERT, args->namelen);
+ xfs_da_log_buf(args->trans, bp,
+ XFS_DA_LOGRANGE(leaf, hdr, sizeof(*hdr)));
+}
+
+/*
+ * Garbage collect a leaf directory block by copying it to a new buffer.
+ */
+STATIC int
+xfs_dir_leaf_compact(xfs_trans_t *trans, xfs_dabuf_t *bp, int musthave,
+ int justcheck)
+{
+ xfs_dir_leafblock_t *leaf_s, *leaf_d;
+ xfs_dir_leaf_hdr_t *hdr_s, *hdr_d;
+ xfs_mount_t *mp;
+ char *tmpbuffer;
+ char *tmpbuffer2=NULL;
+ int rval;
+ int lbsize;
+
+ mp = trans->t_mountp;
+ lbsize = XFS_LBSIZE(mp);
+ tmpbuffer = kmem_alloc(lbsize, KM_SLEEP);
+ ASSERT(tmpbuffer != NULL);
+ memcpy(tmpbuffer, bp->data, lbsize);
+
+ /*
+ * Make a second copy in case xfs_dir_leaf_moveents()
+ * below destroys the original.
+ */
+ if (musthave || justcheck) {
+ tmpbuffer2 = kmem_alloc(lbsize, KM_SLEEP);
+ memcpy(tmpbuffer2, bp->data, lbsize);
+ }
+ memset(bp->data, 0, lbsize);
+
+ /*
+ * Copy basic information
+ */
+ leaf_s = (xfs_dir_leafblock_t *)tmpbuffer;
+ leaf_d = bp->data;
+ hdr_s = &leaf_s->hdr;
+ hdr_d = &leaf_d->hdr;
+ hdr_d->info = hdr_s->info; /* struct copy */
+ INT_SET(hdr_d->firstused, ARCH_CONVERT, lbsize);
+ if (INT_ISZERO(hdr_d->firstused, ARCH_CONVERT))
+ INT_SET(hdr_d->firstused, ARCH_CONVERT, lbsize - 1);
+ INT_ZERO(hdr_d->namebytes, ARCH_CONVERT);
+ INT_ZERO(hdr_d->count, ARCH_CONVERT);
+ hdr_d->holes = 0;
+ INT_SET(hdr_d->freemap[0].base, ARCH_CONVERT, sizeof(xfs_dir_leaf_hdr_t));
+ INT_SET(hdr_d->freemap[0].size, ARCH_CONVERT, INT_GET(hdr_d->firstused, ARCH_CONVERT) - INT_GET(hdr_d->freemap[0].base, ARCH_CONVERT));
+
+ /*
+ * Copy all entry's in the same (sorted) order,
+ * but allocate filenames packed and in sequence.
+ * This changes the source (leaf_s) as well.
+ */
+ xfs_dir_leaf_moveents(leaf_s, 0, leaf_d, 0, (int)INT_GET(hdr_s->count, ARCH_CONVERT), mp);
+
+ if (musthave && INT_GET(hdr_d->freemap[0].size, ARCH_CONVERT) < musthave)
+ rval = XFS_ERROR(ENOSPC);
+ else
+ rval = 0;
+
+ if (justcheck || rval == ENOSPC) {
+ ASSERT(tmpbuffer2);
+ memcpy(bp->data, tmpbuffer2, lbsize);
+ } else {
+ xfs_da_log_buf(trans, bp, 0, lbsize - 1);
+ }
+
+ kmem_free(tmpbuffer, lbsize);
+ if (musthave || justcheck)
+ kmem_free(tmpbuffer2, lbsize);
+ return(rval);
+}
+
+/*
+ * Redistribute the directory entries between two leaf nodes,
+ * taking into account the size of the new entry.
+ *
+ * NOTE: if new block is empty, then it will get the upper half of old block.
+ */
+STATIC void
+xfs_dir_leaf_rebalance(xfs_da_state_t *state, xfs_da_state_blk_t *blk1,
+ xfs_da_state_blk_t *blk2)
+{
+ xfs_da_state_blk_t *tmp_blk;
+ xfs_dir_leafblock_t *leaf1, *leaf2;
+ xfs_dir_leaf_hdr_t *hdr1, *hdr2;
+ int count, totallen, max, space, swap;
+
+ /*
+ * Set up environment.
+ */
+ ASSERT(blk1->magic == XFS_DIR_LEAF_MAGIC);
+ ASSERT(blk2->magic == XFS_DIR_LEAF_MAGIC);
+ leaf1 = blk1->bp->data;
+ leaf2 = blk2->bp->data;
+ ASSERT(INT_GET(leaf1->hdr.info.magic, ARCH_CONVERT) == XFS_DIR_LEAF_MAGIC);
+ ASSERT(INT_GET(leaf2->hdr.info.magic, ARCH_CONVERT) == XFS_DIR_LEAF_MAGIC);
+
+ /*
+ * Check ordering of blocks, reverse if it makes things simpler.
+ */
+ swap = 0;
+ if (xfs_dir_leaf_order(blk1->bp, blk2->bp)) {
+ tmp_blk = blk1;
+ blk1 = blk2;
+ blk2 = tmp_blk;
+ leaf1 = blk1->bp->data;
+ leaf2 = blk2->bp->data;
+ swap = 1;
+ }
+ hdr1 = &leaf1->hdr;
+ hdr2 = &leaf2->hdr;
+
+ /*
+ * Examine entries until we reduce the absolute difference in
+ * byte usage between the two blocks to a minimum. Then get
+ * the direction to copy and the number of elements to move.
+ */
+ state->inleaf = xfs_dir_leaf_figure_balance(state, blk1, blk2,
+ &count, &totallen);
+ if (swap)
+ state->inleaf = !state->inleaf;
+
+ /*
+ * Move any entries required from leaf to leaf:
+ */
+ if (count < INT_GET(hdr1->count, ARCH_CONVERT)) {
+ /*
+ * Figure the total bytes to be added to the destination leaf.
+ */
+ count = INT_GET(hdr1->count, ARCH_CONVERT) - count; /* number entries being moved */
+ space = INT_GET(hdr1->namebytes, ARCH_CONVERT) - totallen;
+ space += count * ((uint)sizeof(xfs_dir_leaf_name_t)-1);
+ space += count * (uint)sizeof(xfs_dir_leaf_entry_t);
+
+ /*
+ * leaf2 is the destination, compact it if it looks tight.
+ */
+ max = INT_GET(hdr2->firstused, ARCH_CONVERT) - (uint)sizeof(xfs_dir_leaf_hdr_t);
+ max -= INT_GET(hdr2->count, ARCH_CONVERT) * (uint)sizeof(xfs_dir_leaf_entry_t);
+ if (space > max) {
+ xfs_dir_leaf_compact(state->args->trans, blk2->bp,
+ 0, 0);
+ }
+
+ /*
+ * Move high entries from leaf1 to low end of leaf2.
+ */
+ xfs_dir_leaf_moveents(leaf1, INT_GET(hdr1->count, ARCH_CONVERT) - count,
+ leaf2, 0, count, state->mp);
+
+ xfs_da_log_buf(state->args->trans, blk1->bp, 0,
+ state->blocksize-1);
+ xfs_da_log_buf(state->args->trans, blk2->bp, 0,
+ state->blocksize-1);
+
+ } else if (count > INT_GET(hdr1->count, ARCH_CONVERT)) {
+ /*
+ * Figure the total bytes to be added to the destination leaf.
+ */
+ count -= INT_GET(hdr1->count, ARCH_CONVERT); /* number entries being moved */
+ space = totallen - INT_GET(hdr1->namebytes, ARCH_CONVERT);
+ space += count * ((uint)sizeof(xfs_dir_leaf_name_t)-1);
+ space += count * (uint)sizeof(xfs_dir_leaf_entry_t);
+
+ /*
+ * leaf1 is the destination, compact it if it looks tight.
+ */
+ max = INT_GET(hdr1->firstused, ARCH_CONVERT) - (uint)sizeof(xfs_dir_leaf_hdr_t);
+ max -= INT_GET(hdr1->count, ARCH_CONVERT) * (uint)sizeof(xfs_dir_leaf_entry_t);
+ if (space > max) {
+ xfs_dir_leaf_compact(state->args->trans, blk1->bp,
+ 0, 0);
+ }
+
+ /*
+ * Move low entries from leaf2 to high end of leaf1.
+ */
+ xfs_dir_leaf_moveents(leaf2, 0, leaf1, (int)INT_GET(hdr1->count, ARCH_CONVERT),
+ count, state->mp);
+
+ xfs_da_log_buf(state->args->trans, blk1->bp, 0,
+ state->blocksize-1);
+ xfs_da_log_buf(state->args->trans, blk2->bp, 0,
+ state->blocksize-1);
+ }
+
+ /*
+ * Copy out last hashval in each block for B-tree code.
+ */
+ blk1->hashval = INT_GET(leaf1->entries[ INT_GET(leaf1->hdr.count, ARCH_CONVERT)-1 ].hashval, ARCH_CONVERT);
+ blk2->hashval = INT_GET(leaf2->entries[ INT_GET(leaf2->hdr.count, ARCH_CONVERT)-1 ].hashval, ARCH_CONVERT);
+
+ /*
+ * Adjust the expected index for insertion.
+ * GROT: this doesn't work unless blk2 was originally empty.
+ */
+ if (!state->inleaf) {
+ blk2->index = blk1->index - INT_GET(leaf1->hdr.count, ARCH_CONVERT);
+ }
+}
+
+/*
+ * Examine entries until we reduce the absolute difference in
+ * byte usage between the two blocks to a minimum.
+ * GROT: Is this really necessary? With other than a 512 byte blocksize,
+ * GROT: there will always be enough room in either block for a new entry.
+ * GROT: Do a double-split for this case?
+ */
+STATIC int
+xfs_dir_leaf_figure_balance(xfs_da_state_t *state,
+ xfs_da_state_blk_t *blk1,
+ xfs_da_state_blk_t *blk2,
+ int *countarg, int *namebytesarg)
+{
+ xfs_dir_leafblock_t *leaf1, *leaf2;
+ xfs_dir_leaf_hdr_t *hdr1, *hdr2;
+ xfs_dir_leaf_entry_t *entry;
+ int count, max, totallen, half;
+ int lastdelta, foundit, tmp;
+
+ /*
+ * Set up environment.
+ */
+ leaf1 = blk1->bp->data;
+ leaf2 = blk2->bp->data;
+ hdr1 = &leaf1->hdr;
+ hdr2 = &leaf2->hdr;
+ foundit = 0;
+ totallen = 0;
+
+ /*
+ * Examine entries until we reduce the absolute difference in
+ * byte usage between the two blocks to a minimum.
+ */
+ max = INT_GET(hdr1->count, ARCH_CONVERT) + INT_GET(hdr2->count, ARCH_CONVERT);
+ half = (max+1) * (uint)(sizeof(*entry)+sizeof(xfs_dir_leaf_entry_t)-1);
+ half += INT_GET(hdr1->namebytes, ARCH_CONVERT) + INT_GET(hdr2->namebytes, ARCH_CONVERT) + state->args->namelen;
+ half /= 2;
+ lastdelta = state->blocksize;
+ entry = &leaf1->entries[0];
+ for (count = 0; count < max; entry++, count++) {
+
+#define XFS_DIR_ABS(A) (((A) < 0) ? -(A) : (A))
+ /*
+ * The new entry is in the first block, account for it.
+ */
+ if (count == blk1->index) {
+ tmp = totallen + (uint)sizeof(*entry)
+ + XFS_DIR_LEAF_ENTSIZE_BYNAME(state->args->namelen);
+ if (XFS_DIR_ABS(half - tmp) > lastdelta)
+ break;
+ lastdelta = XFS_DIR_ABS(half - tmp);
+ totallen = tmp;
+ foundit = 1;
+ }
+
+ /*
+ * Wrap around into the second block if necessary.
+ */
+ if (count == INT_GET(hdr1->count, ARCH_CONVERT)) {
+ leaf1 = leaf2;
+ entry = &leaf1->entries[0];
+ }
+
+ /*
+ * Figure out if next leaf entry would be too much.
+ */
+ tmp = totallen + (uint)sizeof(*entry)
+ + XFS_DIR_LEAF_ENTSIZE_BYENTRY(entry);
+ if (XFS_DIR_ABS(half - tmp) > lastdelta)
+ break;
+ lastdelta = XFS_DIR_ABS(half - tmp);
+ totallen = tmp;
+#undef XFS_DIR_ABS
+ }
+
+ /*
+ * Calculate the number of namebytes that will end up in lower block.
+ * If new entry not in lower block, fix up the count.
+ */
+ totallen -=
+ count * (uint)(sizeof(*entry)+sizeof(xfs_dir_leaf_entry_t)-1);
+ if (foundit) {
+ totallen -= (sizeof(*entry)+sizeof(xfs_dir_leaf_entry_t)-1) +
+ state->args->namelen;
+ }
+
+ *countarg = count;
+ *namebytesarg = totallen;
+ return(foundit);
+}
+
+/*========================================================================
+ * Routines used for shrinking the Btree.
+ *========================================================================*/
+
+/*
+ * Check a leaf block and its neighbors to see if the block should be
+ * collapsed into one or the other neighbor. Always keep the block
+ * with the smaller block number.
+ * If the current block is over 50% full, don't try to join it, return 0.
+ * If the block is empty, fill in the state structure and return 2.
+ * If it can be collapsed, fill in the state structure and return 1.
+ * If nothing can be done, return 0.
+ */
+int
+xfs_dir_leaf_toosmall(xfs_da_state_t *state, int *action)
+{
+ xfs_dir_leafblock_t *leaf;
+ xfs_da_state_blk_t *blk;
+ xfs_da_blkinfo_t *info;
+ int count, bytes, forward, error, retval, i;
+ xfs_dablk_t blkno;
+ xfs_dabuf_t *bp;
+
+ /*
+ * Check for the degenerate case of the block being over 50% full.
+ * If so, it's not worth even looking to see if we might be able
+ * to coalesce with a sibling.
+ */
+ blk = &state->path.blk[ state->path.active-1 ];
+ info = blk->bp->data;
+ ASSERT(INT_GET(info->magic, ARCH_CONVERT) == XFS_DIR_LEAF_MAGIC);
+ leaf = (xfs_dir_leafblock_t *)info;
+ count = INT_GET(leaf->hdr.count, ARCH_CONVERT);
+ bytes = (uint)sizeof(xfs_dir_leaf_hdr_t) +
+ count * (uint)sizeof(xfs_dir_leaf_entry_t) +
+ count * ((uint)sizeof(xfs_dir_leaf_name_t)-1) +
+ INT_GET(leaf->hdr.namebytes, ARCH_CONVERT);
+ if (bytes > (state->blocksize >> 1)) {
+ *action = 0; /* blk over 50%, don't try to join */
+ return(0);
+ }
+
+ /*
+ * Check for the degenerate case of the block being empty.
+ * If the block is empty, we'll simply delete it, no need to
+ * coalesce it with a sibling block. We choose (aribtrarily)
+ * to merge with the forward block unless it is NULL.
+ */
+ if (count == 0) {
+ /*
+ * Make altpath point to the block we want to keep and
+ * path point to the block we want to drop (this one).
+ */
+ forward = !INT_ISZERO(info->forw, ARCH_CONVERT);
+ memcpy(&state->altpath, &state->path, sizeof(state->path));
+ error = xfs_da_path_shift(state, &state->altpath, forward,
+ 0, &retval);
+ if (error)
+ return(error);
+ if (retval) {
+ *action = 0;
+ } else {
+ *action = 2;
+ }
+ return(0);
+ }
+
+ /*
+ * Examine each sibling block to see if we can coalesce with
+ * at least 25% free space to spare. We need to figure out
+ * whether to merge with the forward or the backward block.
+ * We prefer coalescing with the lower numbered sibling so as
+ * to shrink a directory over time.
+ */
+ forward = (INT_GET(info->forw, ARCH_CONVERT) < INT_GET(info->back, ARCH_CONVERT)); /* start with smaller blk num */
+ for (i = 0; i < 2; forward = !forward, i++) {
+ if (forward)
+ blkno = INT_GET(info->forw, ARCH_CONVERT);
+ else
+ blkno = INT_GET(info->back, ARCH_CONVERT);
+ if (blkno == 0)
+ continue;
+ error = xfs_da_read_buf(state->args->trans, state->args->dp,
+ blkno, -1, &bp,
+ XFS_DATA_FORK);
+ if (error)
+ return(error);
+ ASSERT(bp != NULL);
+
+ leaf = (xfs_dir_leafblock_t *)info;
+ count = INT_GET(leaf->hdr.count, ARCH_CONVERT);
+ bytes = state->blocksize - (state->blocksize>>2);
+ bytes -= INT_GET(leaf->hdr.namebytes, ARCH_CONVERT);
+ leaf = bp->data;
+ ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) == XFS_DIR_LEAF_MAGIC);
+ count += INT_GET(leaf->hdr.count, ARCH_CONVERT);
+ bytes -= INT_GET(leaf->hdr.namebytes, ARCH_CONVERT);
+ bytes -= count * ((uint)sizeof(xfs_dir_leaf_name_t) - 1);
+ bytes -= count * (uint)sizeof(xfs_dir_leaf_entry_t);
+ bytes -= (uint)sizeof(xfs_dir_leaf_hdr_t);
+ if (bytes >= 0)
+ break; /* fits with at least 25% to spare */
+
+ xfs_da_brelse(state->args->trans, bp);
+ }
+ if (i >= 2) {
+ *action = 0;
+ return(0);
+ }
+ xfs_da_buf_done(bp);
+
+ /*
+ * Make altpath point to the block we want to keep (the lower
+ * numbered block) and path point to the block we want to drop.
+ */
+ memcpy(&state->altpath, &state->path, sizeof(state->path));
+ if (blkno < blk->blkno) {
+ error = xfs_da_path_shift(state, &state->altpath, forward,
+ 0, &retval);
+ } else {
+ error = xfs_da_path_shift(state, &state->path, forward,
+ 0, &retval);
+ }
+ if (error)
+ return(error);
+ if (retval) {
+ *action = 0;
+ } else {
+ *action = 1;
+ }
+ return(0);
+}
+
+/*
+ * Remove a name from the leaf directory structure.
+ *
+ * Return 1 if leaf is less than 37% full, 0 if >= 37% full.
+ * If two leaves are 37% full, when combined they will leave 25% free.
+ */
+int
+xfs_dir_leaf_remove(xfs_trans_t *trans, xfs_dabuf_t *bp, int index)
+{
+ xfs_dir_leafblock_t *leaf;
+ xfs_dir_leaf_hdr_t *hdr;
+ xfs_dir_leaf_map_t *map;
+ xfs_dir_leaf_entry_t *entry;
+ xfs_dir_leaf_name_t *namest;
+ int before, after, smallest, entsize;
+ int tablesize, tmp, i;
+ xfs_mount_t *mp;
+
+ leaf = bp->data;
+ ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) == XFS_DIR_LEAF_MAGIC);
+ hdr = &leaf->hdr;
+ mp = trans->t_mountp;
+ ASSERT((INT_GET(hdr->count, ARCH_CONVERT) > 0) && (INT_GET(hdr->count, ARCH_CONVERT) < (XFS_LBSIZE(mp)/8)));
+ ASSERT((index >= 0) && (index < INT_GET(hdr->count, ARCH_CONVERT)));
+ ASSERT(INT_GET(hdr->firstused, ARCH_CONVERT) >= ((INT_GET(hdr->count, ARCH_CONVERT)*sizeof(*entry))+sizeof(*hdr)));
+ entry = &leaf->entries[index];
+ ASSERT(INT_GET(entry->nameidx, ARCH_CONVERT) >= INT_GET(hdr->firstused, ARCH_CONVERT));
+ ASSERT(INT_GET(entry->nameidx, ARCH_CONVERT) < XFS_LBSIZE(mp));
+
+ /*
+ * Scan through free region table:
+ * check for adjacency of free'd entry with an existing one,
+ * find smallest free region in case we need to replace it,
+ * adjust any map that borders the entry table,
+ */
+ tablesize = INT_GET(hdr->count, ARCH_CONVERT) * (uint)sizeof(xfs_dir_leaf_entry_t)
+ + (uint)sizeof(xfs_dir_leaf_hdr_t);
+ map = &hdr->freemap[0];
+ tmp = INT_GET(map->size, ARCH_CONVERT);
+ before = after = -1;
+ smallest = XFS_DIR_LEAF_MAPSIZE - 1;
+ entsize = XFS_DIR_LEAF_ENTSIZE_BYENTRY(entry);
+ for (i = 0; i < XFS_DIR_LEAF_MAPSIZE; map++, i++) {
+ ASSERT(INT_GET(map->base, ARCH_CONVERT) < XFS_LBSIZE(mp));
+ ASSERT(INT_GET(map->size, ARCH_CONVERT) < XFS_LBSIZE(mp));
+ if (INT_GET(map->base, ARCH_CONVERT) == tablesize) {
+ INT_MOD(map->base, ARCH_CONVERT, -((uint)sizeof(xfs_dir_leaf_entry_t)));
+ INT_MOD(map->size, ARCH_CONVERT, (uint)sizeof(xfs_dir_leaf_entry_t));
+ }
+
+ if ((INT_GET(map->base, ARCH_CONVERT) + INT_GET(map->size, ARCH_CONVERT)) == INT_GET(entry->nameidx, ARCH_CONVERT)) {
+ before = i;
+ } else if (INT_GET(map->base, ARCH_CONVERT) == (INT_GET(entry->nameidx, ARCH_CONVERT) + entsize)) {
+ after = i;
+ } else if (INT_GET(map->size, ARCH_CONVERT) < tmp) {
+ tmp = INT_GET(map->size, ARCH_CONVERT);
+ smallest = i;
+ }
+ }
+
+ /*
+ * Coalesce adjacent freemap regions,
+ * or replace the smallest region.
+ */
+ if ((before >= 0) || (after >= 0)) {
+ if ((before >= 0) && (after >= 0)) {
+ map = &hdr->freemap[before];
+ INT_MOD(map->size, ARCH_CONVERT, entsize);
+ INT_MOD(map->size, ARCH_CONVERT, INT_GET(hdr->freemap[after].size, ARCH_CONVERT));
+ INT_ZERO(hdr->freemap[after].base, ARCH_CONVERT);
+ INT_ZERO(hdr->freemap[after].size, ARCH_CONVERT);
+ } else if (before >= 0) {
+ map = &hdr->freemap[before];
+ INT_MOD(map->size, ARCH_CONVERT, entsize);
+ } else {
+ map = &hdr->freemap[after];
+ INT_COPY(map->base, entry->nameidx, ARCH_CONVERT);
+ INT_MOD(map->size, ARCH_CONVERT, entsize);
+ }
+ } else {
+ /*
+ * Replace smallest region (if it is smaller than free'd entry)
+ */
+ map = &hdr->freemap[smallest];
+ if (INT_GET(map->size, ARCH_CONVERT) < entsize) {
+ INT_COPY(map->base, entry->nameidx, ARCH_CONVERT);
+ INT_SET(map->size, ARCH_CONVERT, entsize);
+ }
+ }
+
+ /*
+ * Did we remove the first entry?
+ */
+ if (INT_GET(entry->nameidx, ARCH_CONVERT) == INT_GET(hdr->firstused, ARCH_CONVERT))
+ smallest = 1;
+ else
+ smallest = 0;
+
+ /*
+ * Compress the remaining entries and zero out the removed stuff.
+ */
+ namest = XFS_DIR_LEAF_NAMESTRUCT(leaf, INT_GET(entry->nameidx, ARCH_CONVERT));
+ memset((char *)namest, 0, entsize);
+ xfs_da_log_buf(trans, bp, XFS_DA_LOGRANGE(leaf, namest, entsize));
+
+ INT_MOD(hdr->namebytes, ARCH_CONVERT, -(entry->namelen));
+ tmp = (INT_GET(hdr->count, ARCH_CONVERT) - index) * (uint)sizeof(xfs_dir_leaf_entry_t);
+ memmove(entry, entry + 1, tmp);
+ INT_MOD(hdr->count, ARCH_CONVERT, -1);
+ xfs_da_log_buf(trans, bp,
+ XFS_DA_LOGRANGE(leaf, entry, tmp + (uint)sizeof(*entry)));
+ entry = &leaf->entries[INT_GET(hdr->count, ARCH_CONVERT)];
+ memset((char *)entry, 0, sizeof(xfs_dir_leaf_entry_t));
+
+ /*
+ * If we removed the first entry, re-find the first used byte
+ * in the name area. Note that if the entry was the "firstused",
+ * then we don't have a "hole" in our block resulting from
+ * removing the name.
+ */
+ if (smallest) {
+ tmp = XFS_LBSIZE(mp);
+ entry = &leaf->entries[0];
+ for (i = INT_GET(hdr->count, ARCH_CONVERT)-1; i >= 0; entry++, i--) {
+ ASSERT(INT_GET(entry->nameidx, ARCH_CONVERT) >= INT_GET(hdr->firstused, ARCH_CONVERT));
+ ASSERT(INT_GET(entry->nameidx, ARCH_CONVERT) < XFS_LBSIZE(mp));
+ if (INT_GET(entry->nameidx, ARCH_CONVERT) < tmp)
+ tmp = INT_GET(entry->nameidx, ARCH_CONVERT);
+ }
+ INT_SET(hdr->firstused, ARCH_CONVERT, tmp);
+ if (INT_ISZERO(hdr->firstused, ARCH_CONVERT))
+ INT_SET(hdr->firstused, ARCH_CONVERT, tmp - 1);
+ } else {
+ hdr->holes = 1; /* mark as needing compaction */
+ }
+
+ xfs_da_log_buf(trans, bp, XFS_DA_LOGRANGE(leaf, hdr, sizeof(*hdr)));
+
+ /*
+ * Check if leaf is less than 50% full, caller may want to
+ * "join" the leaf with a sibling if so.
+ */
+ tmp = (uint)sizeof(xfs_dir_leaf_hdr_t);
+ tmp += INT_GET(leaf->hdr.count, ARCH_CONVERT) * (uint)sizeof(xfs_dir_leaf_entry_t);
+ tmp += INT_GET(leaf->hdr.count, ARCH_CONVERT) * ((uint)sizeof(xfs_dir_leaf_name_t) - 1);
+ tmp += INT_GET(leaf->hdr.namebytes, ARCH_CONVERT);
+ if (tmp < mp->m_dir_magicpct)
+ return(1); /* leaf is < 37% full */
+ return(0);
+}
+
+/*
+ * Move all the directory entries from drop_leaf into save_leaf.
+ */
+void
+xfs_dir_leaf_unbalance(xfs_da_state_t *state, xfs_da_state_blk_t *drop_blk,
+ xfs_da_state_blk_t *save_blk)
+{
+ xfs_dir_leafblock_t *drop_leaf, *save_leaf, *tmp_leaf;
+ xfs_dir_leaf_hdr_t *drop_hdr, *save_hdr, *tmp_hdr;
+ xfs_mount_t *mp;
+ char *tmpbuffer;
+
+ /*
+ * Set up environment.
+ */
+ mp = state->mp;
+ ASSERT(drop_blk->magic == XFS_DIR_LEAF_MAGIC);
+ ASSERT(save_blk->magic == XFS_DIR_LEAF_MAGIC);
+ drop_leaf = drop_blk->bp->data;
+ save_leaf = save_blk->bp->data;
+ ASSERT(INT_GET(drop_leaf->hdr.info.magic, ARCH_CONVERT) == XFS_DIR_LEAF_MAGIC);
+ ASSERT(INT_GET(save_leaf->hdr.info.magic, ARCH_CONVERT) == XFS_DIR_LEAF_MAGIC);
+ drop_hdr = &drop_leaf->hdr;
+ save_hdr = &save_leaf->hdr;
+
+ /*
+ * Save last hashval from dying block for later Btree fixup.
+ */
+ drop_blk->hashval = INT_GET(drop_leaf->entries[ drop_leaf->hdr.count-1 ].hashval, ARCH_CONVERT);
+
+ /*
+ * Check if we need a temp buffer, or can we do it in place.
+ * Note that we don't check "leaf" for holes because we will
+ * always be dropping it, toosmall() decided that for us already.
+ */
+ if (save_hdr->holes == 0) {
+ /*
+ * dest leaf has no holes, so we add there. May need
+ * to make some room in the entry array.
+ */
+ if (xfs_dir_leaf_order(save_blk->bp, drop_blk->bp)) {
+ xfs_dir_leaf_moveents(drop_leaf, 0, save_leaf, 0,
+ (int)INT_GET(drop_hdr->count, ARCH_CONVERT), mp);
+ } else {
+ xfs_dir_leaf_moveents(drop_leaf, 0,
+ save_leaf, INT_GET(save_hdr->count, ARCH_CONVERT),
+ (int)INT_GET(drop_hdr->count, ARCH_CONVERT), mp);
+ }
+ } else {
+ /*
+ * Destination has holes, so we make a temporary copy
+ * of the leaf and add them both to that.
+ */
+ tmpbuffer = kmem_alloc(state->blocksize, KM_SLEEP);
+ ASSERT(tmpbuffer != NULL);
+ memset(tmpbuffer, 0, state->blocksize);
+ tmp_leaf = (xfs_dir_leafblock_t *)tmpbuffer;
+ tmp_hdr = &tmp_leaf->hdr;
+ tmp_hdr->info = save_hdr->info; /* struct copy */
+ INT_ZERO(tmp_hdr->count, ARCH_CONVERT);
+ INT_SET(tmp_hdr->firstused, ARCH_CONVERT, state->blocksize);
+ if (INT_ISZERO(tmp_hdr->firstused, ARCH_CONVERT))
+ INT_SET(tmp_hdr->firstused, ARCH_CONVERT, state->blocksize - 1);
+ INT_ZERO(tmp_hdr->namebytes, ARCH_CONVERT);
+ if (xfs_dir_leaf_order(save_blk->bp, drop_blk->bp)) {
+ xfs_dir_leaf_moveents(drop_leaf, 0, tmp_leaf, 0,
+ (int)INT_GET(drop_hdr->count, ARCH_CONVERT), mp);
+ xfs_dir_leaf_moveents(save_leaf, 0,
+ tmp_leaf, INT_GET(tmp_leaf->hdr.count, ARCH_CONVERT),
+ (int)INT_GET(save_hdr->count, ARCH_CONVERT), mp);
+ } else {
+ xfs_dir_leaf_moveents(save_leaf, 0, tmp_leaf, 0,
+ (int)INT_GET(save_hdr->count, ARCH_CONVERT), mp);
+ xfs_dir_leaf_moveents(drop_leaf, 0,
+ tmp_leaf, INT_GET(tmp_leaf->hdr.count, ARCH_CONVERT),
+ (int)INT_GET(drop_hdr->count, ARCH_CONVERT), mp);
+ }
+ memcpy(save_leaf, tmp_leaf, state->blocksize);
+ kmem_free(tmpbuffer, state->blocksize);
+ }
+
+ xfs_da_log_buf(state->args->trans, save_blk->bp, 0,
+ state->blocksize - 1);
+
+ /*
+ * Copy out last hashval in each block for B-tree code.
+ */
+ save_blk->hashval = INT_GET(save_leaf->entries[ INT_GET(save_leaf->hdr.count, ARCH_CONVERT)-1 ].hashval, ARCH_CONVERT);
+}
+
+/*========================================================================
+ * Routines used for finding things in the Btree.
+ *========================================================================*/
+
+/*
+ * Look up a name in a leaf directory structure.
+ * This is the internal routine, it uses the caller's buffer.
+ *
+ * Note that duplicate keys are allowed, but only check within the
+ * current leaf node. The Btree code must check in adjacent leaf nodes.
+ *
+ * Return in *index the index into the entry[] array of either the found
+ * entry, or where the entry should have been (insert before that entry).
+ *
+ * Don't change the args->inumber unless we find the filename.
+ */
+int
+xfs_dir_leaf_lookup_int(xfs_dabuf_t *bp, xfs_da_args_t *args, int *index)
+{
+ xfs_dir_leafblock_t *leaf;
+ xfs_dir_leaf_entry_t *entry;
+ xfs_dir_leaf_name_t *namest;
+ int probe, span;
+ xfs_dahash_t hashval;
+
+ leaf = bp->data;
+ ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) == XFS_DIR_LEAF_MAGIC);
+ ASSERT(INT_GET(leaf->hdr.count, ARCH_CONVERT) < (XFS_LBSIZE(args->dp->i_mount)/8));
+
+ /*
+ * Binary search. (note: small blocks will skip this loop)
+ */
+ hashval = args->hashval;
+ probe = span = INT_GET(leaf->hdr.count, ARCH_CONVERT) / 2;
+ for (entry = &leaf->entries[probe]; span > 4;
+ entry = &leaf->entries[probe]) {
+ span /= 2;
+ if (INT_GET(entry->hashval, ARCH_CONVERT) < hashval)
+ probe += span;
+ else if (INT_GET(entry->hashval, ARCH_CONVERT) > hashval)
+ probe -= span;
+ else
+ break;
+ }
+ ASSERT((probe >= 0) && \
+ ((INT_ISZERO(leaf->hdr.count, ARCH_CONVERT)) || (probe < INT_GET(leaf->hdr.count, ARCH_CONVERT))));
+ ASSERT((span <= 4) || (INT_GET(entry->hashval, ARCH_CONVERT) == hashval));
+
+ /*
+ * Since we may have duplicate hashval's, find the first matching
+ * hashval in the leaf.
+ */
+ while ((probe > 0) && (INT_GET(entry->hashval, ARCH_CONVERT) >= hashval)) {
+ entry--;
+ probe--;
+ }
+ while ((probe < INT_GET(leaf->hdr.count, ARCH_CONVERT)) && (INT_GET(entry->hashval, ARCH_CONVERT) < hashval)) {
+ entry++;
+ probe++;
+ }
+ if ((probe == INT_GET(leaf->hdr.count, ARCH_CONVERT)) || (INT_GET(entry->hashval, ARCH_CONVERT) != hashval)) {
+ *index = probe;
+ ASSERT(args->oknoent);
+ return(XFS_ERROR(ENOENT));
+ }
+
+ /*
+ * Duplicate keys may be present, so search all of them for a match.
+ */
+ while ((probe < INT_GET(leaf->hdr.count, ARCH_CONVERT)) && (INT_GET(entry->hashval, ARCH_CONVERT) == hashval)) {
+ namest = XFS_DIR_LEAF_NAMESTRUCT(leaf, INT_GET(entry->nameidx, ARCH_CONVERT));
+ if (entry->namelen == args->namelen &&
+ namest->name[0] == args->name[0] &&
+ memcmp(args->name, namest->name, args->namelen) == 0) {
+ XFS_DIR_SF_GET_DIRINO_ARCH(&namest->inumber, &args->inumber, ARCH_CONVERT);
+ *index = probe;
+ return(XFS_ERROR(EEXIST));
+ }
+ entry++;
+ probe++;
+ }
+ *index = probe;
+ ASSERT(probe == INT_GET(leaf->hdr.count, ARCH_CONVERT) || args->oknoent);
+ return(XFS_ERROR(ENOENT));
+}
+
+/*========================================================================
+ * Utility routines.
+ *========================================================================*/
+
+/*
+ * Move the indicated entries from one leaf to another.
+ * NOTE: this routine modifies both source and destination leaves.
+ */
+/* ARGSUSED */
+STATIC void
+xfs_dir_leaf_moveents(xfs_dir_leafblock_t *leaf_s, int start_s,
+ xfs_dir_leafblock_t *leaf_d, int start_d,
+ int count, xfs_mount_t *mp)
+{
+ xfs_dir_leaf_hdr_t *hdr_s, *hdr_d;
+ xfs_dir_leaf_entry_t *entry_s, *entry_d;
+ int tmp, i;
+
+ /*
+ * Check for nothing to do.
+ */
+ if (count == 0)
+ return;
+
+ /*
+ * Set up environment.
+ */
+ ASSERT(INT_GET(leaf_s->hdr.info.magic, ARCH_CONVERT) == XFS_DIR_LEAF_MAGIC);
+ ASSERT(INT_GET(leaf_d->hdr.info.magic, ARCH_CONVERT) == XFS_DIR_LEAF_MAGIC);
+ hdr_s = &leaf_s->hdr;
+ hdr_d = &leaf_d->hdr;
+ ASSERT((INT_GET(hdr_s->count, ARCH_CONVERT) > 0) && (INT_GET(hdr_s->count, ARCH_CONVERT) < (XFS_LBSIZE(mp)/8)));
+ ASSERT(INT_GET(hdr_s->firstused, ARCH_CONVERT) >=
+ ((INT_GET(hdr_s->count, ARCH_CONVERT)*sizeof(*entry_s))+sizeof(*hdr_s)));
+ ASSERT(INT_GET(hdr_d->count, ARCH_CONVERT) < (XFS_LBSIZE(mp)/8));
+ ASSERT(INT_GET(hdr_d->firstused, ARCH_CONVERT) >=
+ ((INT_GET(hdr_d->count, ARCH_CONVERT)*sizeof(*entry_d))+sizeof(*hdr_d)));
+
+ ASSERT(start_s < INT_GET(hdr_s->count, ARCH_CONVERT));
+ ASSERT(start_d <= INT_GET(hdr_d->count, ARCH_CONVERT));
+ ASSERT(count <= INT_GET(hdr_s->count, ARCH_CONVERT));
+
+ /*
+ * Move the entries in the destination leaf up to make a hole?
+ */
+ if (start_d < INT_GET(hdr_d->count, ARCH_CONVERT)) {
+ tmp = INT_GET(hdr_d->count, ARCH_CONVERT) - start_d;
+ tmp *= (uint)sizeof(xfs_dir_leaf_entry_t);
+ entry_s = &leaf_d->entries[start_d];
+ entry_d = &leaf_d->entries[start_d + count];
+ memcpy(entry_d, entry_s, tmp);
+ }
+
+ /*
+ * Copy all entry's in the same (sorted) order,
+ * but allocate filenames packed and in sequence.
+ */
+ entry_s = &leaf_s->entries[start_s];
+ entry_d = &leaf_d->entries[start_d];
+ for (i = 0; i < count; entry_s++, entry_d++, i++) {
+ ASSERT(INT_GET(entry_s->nameidx, ARCH_CONVERT) >= INT_GET(hdr_s->firstused, ARCH_CONVERT));
+ tmp = XFS_DIR_LEAF_ENTSIZE_BYENTRY(entry_s);
+ INT_MOD(hdr_d->firstused, ARCH_CONVERT, -(tmp));
+ entry_d->hashval = entry_s->hashval; /* INT_: direct copy */
+ INT_COPY(entry_d->nameidx, hdr_d->firstused, ARCH_CONVERT);
+ entry_d->namelen = entry_s->namelen;
+ ASSERT(INT_GET(entry_d->nameidx, ARCH_CONVERT) + tmp <= XFS_LBSIZE(mp));
+ memcpy(XFS_DIR_LEAF_NAMESTRUCT(leaf_d, INT_GET(entry_d->nameidx, ARCH_CONVERT)),
+ XFS_DIR_LEAF_NAMESTRUCT(leaf_s, INT_GET(entry_s->nameidx, ARCH_CONVERT)), tmp);
+ ASSERT(INT_GET(entry_s->nameidx, ARCH_CONVERT) + tmp <= XFS_LBSIZE(mp));
+ memset((char *)XFS_DIR_LEAF_NAMESTRUCT(leaf_s, INT_GET(entry_s->nameidx, ARCH_CONVERT)),
+ 0, tmp);
+ INT_MOD(hdr_s->namebytes, ARCH_CONVERT, -(entry_d->namelen));
+ INT_MOD(hdr_d->namebytes, ARCH_CONVERT, entry_d->namelen);
+ INT_MOD(hdr_s->count, ARCH_CONVERT, -1);
+ INT_MOD(hdr_d->count, ARCH_CONVERT, +1);
+ tmp = INT_GET(hdr_d->count, ARCH_CONVERT) * (uint)sizeof(xfs_dir_leaf_entry_t)
+ + (uint)sizeof(xfs_dir_leaf_hdr_t);
+ ASSERT(INT_GET(hdr_d->firstused, ARCH_CONVERT) >= tmp);
+
+ }
+
+ /*
+ * Zero out the entries we just copied.
+ */
+ if (start_s == INT_GET(hdr_s->count, ARCH_CONVERT)) {
+ tmp = count * (uint)sizeof(xfs_dir_leaf_entry_t);
+ entry_s = &leaf_s->entries[start_s];
+ ASSERT((char *)entry_s + tmp <= (char *)leaf_s + XFS_LBSIZE(mp));
+ memset((char *)entry_s, 0, tmp);
+ } else {
+ /*
+ * Move the remaining entries down to fill the hole,
+ * then zero the entries at the top.
+ */
+ tmp = INT_GET(hdr_s->count, ARCH_CONVERT) - count;
+ tmp *= (uint)sizeof(xfs_dir_leaf_entry_t);
+ entry_s = &leaf_s->entries[start_s + count];
+ entry_d = &leaf_s->entries[start_s];
+ memcpy(entry_d, entry_s, tmp);
+
+ tmp = count * (uint)sizeof(xfs_dir_leaf_entry_t);
+ entry_s = &leaf_s->entries[INT_GET(hdr_s->count, ARCH_CONVERT)];
+ ASSERT((char *)entry_s + tmp <= (char *)leaf_s + XFS_LBSIZE(mp));
+ memset((char *)entry_s, 0, tmp);
+ }
+
+ /*
+ * Fill in the freemap information
+ */
+ INT_SET(hdr_d->freemap[0].base, ARCH_CONVERT, (uint)sizeof(xfs_dir_leaf_hdr_t));
+ INT_MOD(hdr_d->freemap[0].base, ARCH_CONVERT, INT_GET(hdr_d->count, ARCH_CONVERT) * (uint)sizeof(xfs_dir_leaf_entry_t));
+ INT_SET(hdr_d->freemap[0].size, ARCH_CONVERT, INT_GET(hdr_d->firstused, ARCH_CONVERT) - INT_GET(hdr_d->freemap[0].base, ARCH_CONVERT));
+ INT_SET(hdr_d->freemap[1].base, ARCH_CONVERT, INT_ZERO(hdr_d->freemap[2].base, ARCH_CONVERT));
+ INT_SET(hdr_d->freemap[1].size, ARCH_CONVERT, INT_ZERO(hdr_d->freemap[2].size, ARCH_CONVERT));
+ hdr_s->holes = 1; /* leaf may not be compact */
+}
+
+/*
+ * Compare two leaf blocks "order".
+ */
+int
+xfs_dir_leaf_order(xfs_dabuf_t *leaf1_bp, xfs_dabuf_t *leaf2_bp)
+{
+ xfs_dir_leafblock_t *leaf1, *leaf2;
+
+ leaf1 = leaf1_bp->data;
+ leaf2 = leaf2_bp->data;
+ ASSERT((INT_GET(leaf1->hdr.info.magic, ARCH_CONVERT) == XFS_DIR_LEAF_MAGIC) &&
+ (INT_GET(leaf2->hdr.info.magic, ARCH_CONVERT) == XFS_DIR_LEAF_MAGIC));
+ if ((INT_GET(leaf1->hdr.count, ARCH_CONVERT) > 0) && (INT_GET(leaf2->hdr.count, ARCH_CONVERT) > 0) &&
+ ((INT_GET(leaf2->entries[ 0 ].hashval, ARCH_CONVERT) <
+ INT_GET(leaf1->entries[ 0 ].hashval, ARCH_CONVERT)) ||
+ (INT_GET(leaf2->entries[ INT_GET(leaf2->hdr.count, ARCH_CONVERT)-1 ].hashval, ARCH_CONVERT) <
+ INT_GET(leaf1->entries[ INT_GET(leaf1->hdr.count, ARCH_CONVERT)-1 ].hashval, ARCH_CONVERT)))) {
+ return(1);
+ }
+ return(0);
+}
+
+/*
+ * Pick up the last hashvalue from a leaf block.
+ */
+xfs_dahash_t
+xfs_dir_leaf_lasthash(xfs_dabuf_t *bp, int *count)
+{
+ xfs_dir_leafblock_t *leaf;
+
+ leaf = bp->data;
+ ASSERT(INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) == XFS_DIR_LEAF_MAGIC);
+ if (count)
+ *count = INT_GET(leaf->hdr.count, ARCH_CONVERT);
+ if (INT_ISZERO(leaf->hdr.count, ARCH_CONVERT))
+ return(0);
+ return(INT_GET(leaf->entries[ INT_GET(leaf->hdr.count, ARCH_CONVERT)-1 ].hashval, ARCH_CONVERT));
+}
+
+/*
+ * Copy out directory entries for getdents(), for leaf directories.
+ */
+int
+xfs_dir_leaf_getdents_int(
+ xfs_dabuf_t *bp,
+ xfs_inode_t *dp,
+ xfs_dablk_t bno,
+ uio_t *uio,
+ int *eobp,
+ xfs_dirent_t *dbp,
+ xfs_dir_put_t put,
+ xfs_daddr_t nextda)
+{
+ xfs_dir_leafblock_t *leaf;
+ xfs_dir_leaf_entry_t *entry;
+ xfs_dir_leaf_name_t *namest;
+ int entno, want_entno, i, nextentno;
+ xfs_mount_t *mp;
+ xfs_dahash_t cookhash;
+ xfs_dahash_t nexthash = 0;
+#if (BITS_PER_LONG == 32)
+ xfs_dahash_t lasthash = XFS_DA_MAXHASH;
+#endif
+ xfs_dir_put_args_t p;
+
+ mp = dp->i_mount;
+ leaf = bp->data;
+ if (INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) != XFS_DIR_LEAF_MAGIC) {
+ *eobp = 1;
+ return(XFS_ERROR(ENOENT)); /* XXX wrong code */
+ }
+
+ want_entno = XFS_DA_COOKIE_ENTRY(mp, uio->uio_offset);
+
+ cookhash = XFS_DA_COOKIE_HASH(mp, uio->uio_offset);
+
+ xfs_dir_trace_g_dul("leaf: start", dp, uio, leaf);
+
+ /*
+ * Re-find our place.
+ */
+ for (i = entno = 0, entry = &leaf->entries[0];
+ i < INT_GET(leaf->hdr.count, ARCH_CONVERT);
+ entry++, i++) {
+
+ namest = XFS_DIR_LEAF_NAMESTRUCT(leaf,
+ INT_GET(entry->nameidx, ARCH_CONVERT));
+
+ if (unlikely(
+ ((char *)namest < (char *)leaf) ||
+ ((char *)namest >= (char *)leaf + XFS_LBSIZE(mp)))) {
+ XFS_CORRUPTION_ERROR("xfs_dir_leaf_getdents_int(1)",
+ XFS_ERRLEVEL_LOW, mp, leaf);
+ xfs_dir_trace_g_du("leaf: corrupted", dp, uio);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ if (INT_GET(entry->hashval, ARCH_CONVERT) >= cookhash) {
+ if ( entno < want_entno
+ && INT_GET(entry->hashval, ARCH_CONVERT)
+ == cookhash) {
+ /*
+ * Trying to get to a particular offset in a
+ * run of equal-hashval entries.
+ */
+ entno++;
+ } else if ( want_entno > 0
+ && entno == want_entno
+ && INT_GET(entry->hashval, ARCH_CONVERT)
+ == cookhash) {
+ break;
+ } else {
+ entno = 0;
+ break;
+ }
+ }
+ }
+
+ if (i == INT_GET(leaf->hdr.count, ARCH_CONVERT)) {
+ xfs_dir_trace_g_du("leaf: hash not found", dp, uio);
+ if (!INT_GET(leaf->hdr.info.forw, ARCH_CONVERT))
+ uio->uio_offset =
+ XFS_DA_MAKE_COOKIE(mp, 0, 0, XFS_DA_MAXHASH);
+ /*
+ * Don't set uio_offset if there's another block:
+ * the node code will be setting uio_offset anyway.
+ */
+ *eobp = 0;
+ return(0);
+ }
+ xfs_dir_trace_g_due("leaf: hash found", dp, uio, entry);
+
+ p.dbp = dbp;
+ p.put = put;
+ p.uio = uio;
+
+ /*
+ * We're synchronized, start copying entries out to the user.
+ */
+ for (; entno >= 0 && i < INT_GET(leaf->hdr.count, ARCH_CONVERT);
+ entry++, i++, (entno = nextentno)) {
+ int lastresid=0, retval;
+ xfs_dircook_t lastoffset;
+ xfs_dahash_t thishash;
+
+ /*
+ * Check for a damaged directory leaf block and pick up
+ * the inode number from this entry.
+ */
+ namest = XFS_DIR_LEAF_NAMESTRUCT(leaf,
+ INT_GET(entry->nameidx, ARCH_CONVERT));
+
+ if (unlikely(
+ ((char *)namest < (char *)leaf) ||
+ ((char *)namest >= (char *)leaf + XFS_LBSIZE(mp)))) {
+ XFS_CORRUPTION_ERROR("xfs_dir_leaf_getdents_int(2)",
+ XFS_ERRLEVEL_LOW, mp, leaf);
+ xfs_dir_trace_g_du("leaf: corrupted", dp, uio);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+
+ xfs_dir_trace_g_duc("leaf: middle cookie ",
+ dp, uio, p.cook.o);
+
+ if (i < (INT_GET(leaf->hdr.count, ARCH_CONVERT) - 1)) {
+ nexthash = INT_GET(entry[1].hashval, ARCH_CONVERT);
+
+ if (nexthash == INT_GET(entry->hashval, ARCH_CONVERT))
+ nextentno = entno + 1;
+ else
+ nextentno = 0;
+ XFS_PUT_COOKIE(p.cook, mp, bno, nextentno, nexthash);
+ xfs_dir_trace_g_duc("leaf: middle cookie ",
+ dp, uio, p.cook.o);
+
+ } else if ((thishash = INT_GET(leaf->hdr.info.forw,
+ ARCH_CONVERT))) {
+ xfs_dabuf_t *bp2;
+ xfs_dir_leafblock_t *leaf2;
+
+ ASSERT(nextda != -1);
+
+ retval = xfs_da_read_buf(dp->i_transp, dp, thishash,
+ nextda, &bp2, XFS_DATA_FORK);
+ if (retval)
+ return(retval);
+
+ ASSERT(bp2 != NULL);
+
+ leaf2 = bp2->data;
+
+ if (unlikely(
+ (INT_GET(leaf2->hdr.info.magic, ARCH_CONVERT)
+ != XFS_DIR_LEAF_MAGIC)
+ || (INT_GET(leaf2->hdr.info.back, ARCH_CONVERT)
+ != bno))) { /* GROT */
+ XFS_CORRUPTION_ERROR("xfs_dir_leaf_getdents_int(3)",
+ XFS_ERRLEVEL_LOW, mp,
+ leaf2);
+ xfs_da_brelse(dp->i_transp, bp2);
+
+ return(XFS_ERROR(EFSCORRUPTED));
+ }
+
+ nexthash = INT_GET(leaf2->entries[0].hashval,
+ ARCH_CONVERT);
+ nextentno = -1;
+ XFS_PUT_COOKIE(p.cook, mp, thishash, 0, nexthash);
+ xfs_da_brelse(dp->i_transp, bp2);
+ xfs_dir_trace_g_duc("leaf: next blk cookie",
+ dp, uio, p.cook.o);
+ } else {
+ nextentno = -1;
+ XFS_PUT_COOKIE(p.cook, mp, 0, 0, XFS_DA_MAXHASH);
+ }
+
+ /*
+ * Save off the cookie so we can fall back should the
+ * 'put' into the outgoing buffer fails. To handle a run
+ * of equal-hashvals, the off_t structure on 64bit
+ * builds has entno built into the cookie to ID the
+ * entry. On 32bit builds, we only have space for the
+ * hashval so we can't ID specific entries within a group
+ * of same hashval entries. For this, lastoffset is set
+ * to the first in the run of equal hashvals so we don't
+ * include any entries unless we can include all entries
+ * that share the same hashval. Hopefully the buffer
+ * provided is big enough to handle it (see pv763517).
+ */
+#if (BITS_PER_LONG == 32)
+ if ((thishash = INT_GET(entry->hashval, ARCH_CONVERT))
+ != lasthash) {
+ XFS_PUT_COOKIE(lastoffset, mp, bno, entno, thishash);
+ lastresid = uio->uio_resid;
+ lasthash = thishash;
+ } else {
+ xfs_dir_trace_g_duc("leaf: DUP COOKIES, skipped",
+ dp, uio, p.cook.o);
+ }
+#else
+ thishash = INT_GET(entry->hashval, ARCH_CONVERT);
+ XFS_PUT_COOKIE(lastoffset, mp, bno, entno, thishash);
+ lastresid = uio->uio_resid;
+#endif /* BITS_PER_LONG == 32 */
+
+ /*
+ * Put the current entry into the outgoing buffer. If we fail
+ * then restore the UIO to the first entry in the current
+ * run of equal-hashval entries (probably one 1 entry long).
+ */
+ p.ino = XFS_GET_DIR_INO_ARCH(mp, namest->inumber, ARCH_CONVERT);
+#if XFS_BIG_INUMS
+ p.ino += mp->m_inoadd;
+#endif
+ p.name = (char *)namest->name;
+ p.namelen = entry->namelen;
+
+ retval = p.put(&p);
+
+ if (!p.done) {
+ uio->uio_offset = lastoffset.o;
+ uio->uio_resid = lastresid;
+
+ *eobp = 1;
+
+ xfs_dir_trace_g_du("leaf: E-O-B", dp, uio);
+
+ return(retval);
+ }
+ }
+
+ uio->uio_offset = p.cook.o;
+
+ *eobp = 0;
+
+ xfs_dir_trace_g_du("leaf: E-O-F", dp, uio);
+
+ return(0);
+}
+
+/*
+ * Format a dirent64 structure and copy it out the the user's buffer.
+ */
+int
+xfs_dir_put_dirent64_direct(xfs_dir_put_args_t *pa)
+{
+ iovec_t *iovp;
+ int reclen, namelen;
+ xfs_dirent_t *idbp;
+ uio_t *uio;
+
+ namelen = pa->namelen;
+ reclen = DIRENTSIZE(namelen);
+ uio = pa->uio;
+ if (reclen > uio->uio_resid) {
+ pa->done = 0;
+ return 0;
+ }
+ iovp = uio->uio_iov;
+ idbp = (xfs_dirent_t *)iovp->iov_base;
+ iovp->iov_base = (char *)idbp + reclen;
+ iovp->iov_len -= reclen;
+ uio->uio_resid -= reclen;
+ idbp->d_reclen = reclen;
+ idbp->d_ino = pa->ino;
+ idbp->d_off = pa->cook.o;
+ idbp->d_name[namelen] = '\0';
+ pa->done = 1;
+ memcpy(idbp->d_name, pa->name, namelen);
+ return 0;
+}
+
+/*
+ * Format a dirent64 structure and copy it out the the user's buffer.
+ */
+int
+xfs_dir_put_dirent64_uio(xfs_dir_put_args_t *pa)
+{
+ int retval, reclen, namelen;
+ xfs_dirent_t *idbp;
+ uio_t *uio;
+
+ namelen = pa->namelen;
+ reclen = DIRENTSIZE(namelen);
+ uio = pa->uio;
+ if (reclen > uio->uio_resid) {
+ pa->done = 0;
+ return 0;
+ }
+ idbp = pa->dbp;
+ idbp->d_reclen = reclen;
+ idbp->d_ino = pa->ino;
+ idbp->d_off = pa->cook.o;
+ idbp->d_name[namelen] = '\0';
+ memcpy(idbp->d_name, pa->name, namelen);
+ retval = uio_read((caddr_t)idbp, reclen, uio);
+ pa->done = (retval == 0);
+ return retval;
+}
diff --git a/fs/xfs/xfs_dir_leaf.h b/fs/xfs/xfs_dir_leaf.h
new file mode 100644
index 00000000000000..ad74774011d21d
--- /dev/null
+++ b/fs/xfs/xfs_dir_leaf.h
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 2000-2001 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_DIR_LEAF_H__
+#define __XFS_DIR_LEAF_H__
+
+/*
+ * Directory layout, internal structure, access macros, etc.
+ *
+ * Large directories are structured around Btrees where all the data
+ * elements are in the leaf nodes. Filenames are hashed into an int,
+ * then that int is used as the index into the Btree. Since the hashval
+ * of a filename may not be unique, we may have duplicate keys. The
+ * internal links in the Btree are logical block offsets into the file.
+ */
+
+struct uio;
+struct xfs_bmap_free;
+struct xfs_dabuf;
+struct xfs_da_args;
+struct xfs_da_state;
+struct xfs_da_state_blk;
+struct xfs_dir_put_args;
+struct xfs_inode;
+struct xfs_mount;
+struct xfs_trans;
+
+/*========================================================================
+ * Directory Structure when equal to XFS_LBSIZE(mp) bytes.
+ *========================================================================*/
+
+/*
+ * This is the structure of the leaf nodes in the Btree.
+ *
+ * Struct leaf_entry's are packed from the top. Names grow from the bottom
+ * but are not packed. The freemap contains run-length-encoded entries
+ * for the free bytes after the leaf_entry's, but only the N largest such,
+ * smaller runs are dropped. When the freemap doesn't show enough space
+ * for an allocation, we compact the namelist area and try again. If we
+ * still don't have enough space, then we have to split the block.
+ *
+ * Since we have duplicate hash keys, for each key that matches, compare
+ * the actual string. The root and intermediate node search always takes
+ * the first-in-the-block key match found, so we should only have to work
+ * "forw"ard. If none matches, continue with the "forw"ard leaf nodes
+ * until the hash key changes or the filename is found.
+ *
+ * The parent directory and the self-pointer are explicitly represented
+ * (ie: there are entries for "." and "..").
+ *
+ * Note that the count being a __uint16_t limits us to something like a
+ * blocksize of 1.3MB in the face of worst case (short) filenames.
+ */
+#define XFS_DIR_LEAF_MAPSIZE 3 /* how many freespace slots */
+
+typedef struct xfs_dir_leafblock {
+ struct xfs_dir_leaf_hdr { /* constant-structure header block */
+ xfs_da_blkinfo_t info; /* block type, links, etc. */
+ __uint16_t count; /* count of active leaf_entry's */
+ __uint16_t namebytes; /* num bytes of name strings stored */
+ __uint16_t firstused; /* first used byte in name area */
+ __uint8_t holes; /* != 0 if blk needs compaction */
+ __uint8_t pad1;
+ struct xfs_dir_leaf_map {/* RLE map of free bytes */
+ __uint16_t base; /* base of free region */
+ __uint16_t size; /* run length of free region */
+ } freemap[XFS_DIR_LEAF_MAPSIZE]; /* N largest free regions */
+ } hdr;
+ struct xfs_dir_leaf_entry { /* sorted on key, not name */
+ xfs_dahash_t hashval; /* hash value of name */
+ __uint16_t nameidx; /* index into buffer of name */
+ __uint8_t namelen; /* length of name string */
+ __uint8_t pad2;
+ } entries[1]; /* var sized array */
+ struct xfs_dir_leaf_name {
+ xfs_dir_ino_t inumber; /* inode number for this key */
+ __uint8_t name[1]; /* name string itself */
+ } namelist[1]; /* grows from bottom of buf */
+} xfs_dir_leafblock_t;
+typedef struct xfs_dir_leaf_hdr xfs_dir_leaf_hdr_t;
+typedef struct xfs_dir_leaf_map xfs_dir_leaf_map_t;
+typedef struct xfs_dir_leaf_entry xfs_dir_leaf_entry_t;
+typedef struct xfs_dir_leaf_name xfs_dir_leaf_name_t;
+
+/*
+ * Length of name for which a 512-byte block filesystem
+ * can get a double split.
+ */
+#define XFS_DIR_LEAF_CAN_DOUBLE_SPLIT_LEN \
+ (512 - (uint)sizeof(xfs_dir_leaf_hdr_t) - \
+ (uint)sizeof(xfs_dir_leaf_entry_t) * 2 - \
+ (uint)sizeof(xfs_dir_leaf_name_t) * 2 - (MAXNAMELEN - 2) + 1 + 1)
+
+typedef int (*xfs_dir_put_t)(struct xfs_dir_put_args *pa);
+
+typedef union {
+ xfs_off_t o; /* offset (cookie) */
+ /*
+ * Watch the order here (endian-ness dependent).
+ */
+ struct {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ xfs_dahash_t h; /* hash value */
+ __uint32_t be; /* block and entry */
+#else /* __BYTE_ORDER == __BIG_ENDIAN */
+ __uint32_t be; /* block and entry */
+ xfs_dahash_t h; /* hash value */
+#endif /* __BYTE_ORDER == __BIG_ENDIAN */
+ } s;
+} xfs_dircook_t;
+
+#define XFS_PUT_COOKIE(c,mp,bno,entry,hash) \
+ ((c).s.be = XFS_DA_MAKE_BNOENTRY(mp, bno, entry), (c).s.h = (hash))
+
+#define XFS_GET_DIR_INO_ARCH(mp,di,arch) \
+ DIRINO_GET_ARCH(&(di),arch)
+#define XFS_GET_DIR_INO(mp,di) \
+ XFS_GET_DIR_INO_ARCH(mp,di,ARCH_NOCONVERT)
+
+typedef struct xfs_dir_put_args
+{
+ xfs_dircook_t cook; /* cookie of (next) entry */
+ xfs_intino_t ino; /* inode number */
+ struct xfs_dirent *dbp; /* buffer pointer */
+ char *name; /* directory entry name */
+ int namelen; /* length of name */
+ int done; /* output: set if value was stored */
+ xfs_dir_put_t put; /* put function ptr (i/o) */
+ struct uio *uio; /* uio control structure */
+} xfs_dir_put_args_t;
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR_LEAF_ENTSIZE_BYNAME)
+int xfs_dir_leaf_entsize_byname(int len);
+#define XFS_DIR_LEAF_ENTSIZE_BYNAME(len) xfs_dir_leaf_entsize_byname(len)
+#else
+#define XFS_DIR_LEAF_ENTSIZE_BYNAME(len) /* space a name will use */ \
+ ((uint)sizeof(xfs_dir_leaf_name_t)-1 + len)
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR_LEAF_ENTSIZE_BYENTRY)
+int xfs_dir_leaf_entsize_byentry(xfs_dir_leaf_entry_t *entry);
+#define XFS_DIR_LEAF_ENTSIZE_BYENTRY(entry) \
+ xfs_dir_leaf_entsize_byentry(entry)
+#else
+#define XFS_DIR_LEAF_ENTSIZE_BYENTRY(entry) /* space an entry will use */ \
+ ((uint)sizeof(xfs_dir_leaf_name_t)-1 + (entry)->namelen)
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR_LEAF_NAMESTRUCT)
+xfs_dir_leaf_name_t *
+xfs_dir_leaf_namestruct(xfs_dir_leafblock_t *leafp, int offset);
+#define XFS_DIR_LEAF_NAMESTRUCT(leafp,offset) \
+ xfs_dir_leaf_namestruct(leafp,offset)
+#else
+#define XFS_DIR_LEAF_NAMESTRUCT(leafp,offset) /* point to name struct */ \
+ ((xfs_dir_leaf_name_t *)&((char *)(leafp))[offset])
+#endif
+
+/*========================================================================
+ * Function prototypes for the kernel.
+ *========================================================================*/
+
+/*
+ * Internal routines when dirsize < XFS_LITINO(mp).
+ */
+int xfs_dir_shortform_create(struct xfs_da_args *args, xfs_ino_t parent);
+int xfs_dir_shortform_addname(struct xfs_da_args *args);
+int xfs_dir_shortform_lookup(struct xfs_da_args *args);
+int xfs_dir_shortform_to_leaf(struct xfs_da_args *args);
+int xfs_dir_shortform_removename(struct xfs_da_args *args);
+int xfs_dir_shortform_getdents(struct xfs_inode *dp, struct uio *uio, int *eofp,
+ struct xfs_dirent *dbp, xfs_dir_put_t put);
+int xfs_dir_shortform_replace(struct xfs_da_args *args);
+
+/*
+ * Internal routines when dirsize == XFS_LBSIZE(mp).
+ */
+int xfs_dir_leaf_to_node(struct xfs_da_args *args);
+int xfs_dir_leaf_to_shortform(struct xfs_da_args *args);
+
+/*
+ * Routines used for growing the Btree.
+ */
+int xfs_dir_leaf_create(struct xfs_da_args *args, xfs_dablk_t which_block,
+ struct xfs_dabuf **bpp);
+int xfs_dir_leaf_split(struct xfs_da_state *state,
+ struct xfs_da_state_blk *oldblk,
+ struct xfs_da_state_blk *newblk);
+int xfs_dir_leaf_add(struct xfs_dabuf *leaf_buffer,
+ struct xfs_da_args *args, int insertion_index);
+int xfs_dir_leaf_addname(struct xfs_da_args *args);
+int xfs_dir_leaf_lookup_int(struct xfs_dabuf *leaf_buffer,
+ struct xfs_da_args *args,
+ int *index_found_at);
+int xfs_dir_leaf_remove(struct xfs_trans *trans,
+ struct xfs_dabuf *leaf_buffer,
+ int index_to_remove);
+int xfs_dir_leaf_getdents_int(struct xfs_dabuf *bp, struct xfs_inode *dp,
+ xfs_dablk_t bno, struct uio *uio,
+ int *eobp, struct xfs_dirent *dbp,
+ xfs_dir_put_t put, xfs_daddr_t nextda);
+
+/*
+ * Routines used for shrinking the Btree.
+ */
+int xfs_dir_leaf_toosmall(struct xfs_da_state *state, int *retval);
+void xfs_dir_leaf_unbalance(struct xfs_da_state *state,
+ struct xfs_da_state_blk *drop_blk,
+ struct xfs_da_state_blk *save_blk);
+
+/*
+ * Utility routines.
+ */
+uint xfs_dir_leaf_lasthash(struct xfs_dabuf *bp, int *count);
+int xfs_dir_leaf_order(struct xfs_dabuf *leaf1_bp,
+ struct xfs_dabuf *leaf2_bp);
+int xfs_dir_put_dirent64_direct(xfs_dir_put_args_t *pa);
+int xfs_dir_put_dirent64_uio(xfs_dir_put_args_t *pa);
+int xfs_dir_ino_validate(struct xfs_mount *mp, xfs_ino_t ino);
+
+
+/*
+ * Global data.
+ */
+extern xfs_dahash_t xfs_dir_hash_dot, xfs_dir_hash_dotdot;
+
+#endif /* __XFS_DIR_LEAF_H__ */
diff --git a/fs/xfs/xfs_dir_sf.h b/fs/xfs/xfs_dir_sf.h
new file mode 100644
index 00000000000000..a363d1a01356cc
--- /dev/null
+++ b/fs/xfs/xfs_dir_sf.h
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_DIR_SF_H__
+#define __XFS_DIR_SF_H__
+
+/*
+ * Directory layout when stored internal to an inode.
+ *
+ * Small directories are packed as tightly as possible so as to
+ * fit into the literal area of the inode.
+ */
+
+typedef struct { __uint8_t i[sizeof(xfs_ino_t)]; } xfs_dir_ino_t;
+
+/*
+ * The parent directory has a dedicated field, and the self-pointer must
+ * be calculated on the fly.
+ *
+ * Entries are packed toward the top as tight as possible. The header
+ * and the elements much be memcpy'd out into a work area to get correct
+ * alignment for the inode number fields.
+ */
+typedef struct xfs_dir_shortform {
+ struct xfs_dir_sf_hdr { /* constant-structure header block */
+ xfs_dir_ino_t parent; /* parent dir inode number */
+ __uint8_t count; /* count of active entries */
+ } hdr;
+ struct xfs_dir_sf_entry {
+ xfs_dir_ino_t inumber; /* referenced inode number */
+ __uint8_t namelen; /* actual length of name (no NULL) */
+ __uint8_t name[1]; /* name */
+ } list[1]; /* variable sized array */
+} xfs_dir_shortform_t;
+typedef struct xfs_dir_sf_hdr xfs_dir_sf_hdr_t;
+typedef struct xfs_dir_sf_entry xfs_dir_sf_entry_t;
+
+/*
+ * We generate this then sort it, so that readdirs are returned in
+ * hash-order. Else seekdir won't work.
+ */
+typedef struct xfs_dir_sf_sort {
+ __uint8_t entno; /* .=0, ..=1, else entry# + 2 */
+ __uint8_t seqno; /* sequence # with same hash value */
+ __uint8_t namelen; /* length of name value (no null) */
+ xfs_dahash_t hash; /* this entry's hash value */
+ xfs_intino_t ino; /* this entry's inode number */
+ char *name; /* name value, pointer into buffer */
+} xfs_dir_sf_sort_t;
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR_SF_GET_DIRINO)
+void xfs_dir_sf_get_dirino_arch(xfs_dir_ino_t *from, xfs_ino_t *to, xfs_arch_t arch);
+void xfs_dir_sf_get_dirino(xfs_dir_ino_t *from, xfs_ino_t *to);
+#define XFS_DIR_SF_GET_DIRINO_ARCH(from,to,arch) xfs_dir_sf_get_dirino_arch(from, to, arch)
+#define XFS_DIR_SF_GET_DIRINO(from,to) xfs_dir_sf_get_dirino(from, to)
+#else
+#define XFS_DIR_SF_GET_DIRINO_ARCH(from,to,arch) DIRINO_COPY_ARCH(from,to,arch)
+#define XFS_DIR_SF_GET_DIRINO(from,to) DIRINO_COPY_ARCH(from,to,ARCH_NOCONVERT)
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR_SF_PUT_DIRINO)
+void xfs_dir_sf_put_dirino_arch(xfs_ino_t *from, xfs_dir_ino_t *to, xfs_arch_t arch);
+void xfs_dir_sf_put_dirino(xfs_ino_t *from, xfs_dir_ino_t *to);
+#define XFS_DIR_SF_PUT_DIRINO_ARCH(from,to,arch) xfs_dir_sf_put_dirino_arch(from, to, arch)
+#define XFS_DIR_SF_PUT_DIRINO(from,to) xfs_dir_sf_put_dirino(from, to)
+#else
+#define XFS_DIR_SF_PUT_DIRINO_ARCH(from,to,arch) DIRINO_COPY_ARCH(from,to,arch)
+#define XFS_DIR_SF_PUT_DIRINO(from,to) DIRINO_COPY_ARCH(from,to,ARCH_NOCONVERT)
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR_SF_ENTSIZE_BYNAME)
+int xfs_dir_sf_entsize_byname(int len);
+#define XFS_DIR_SF_ENTSIZE_BYNAME(len) xfs_dir_sf_entsize_byname(len)
+#else
+#define XFS_DIR_SF_ENTSIZE_BYNAME(len) /* space a name uses */ \
+ ((uint)sizeof(xfs_dir_sf_entry_t)-1 + (len))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR_SF_ENTSIZE_BYENTRY)
+int xfs_dir_sf_entsize_byentry(xfs_dir_sf_entry_t *sfep);
+#define XFS_DIR_SF_ENTSIZE_BYENTRY(sfep) xfs_dir_sf_entsize_byentry(sfep)
+#else
+#define XFS_DIR_SF_ENTSIZE_BYENTRY(sfep) /* space an entry uses */ \
+ ((uint)sizeof(xfs_dir_sf_entry_t)-1 + (sfep)->namelen)
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR_SF_NEXTENTRY)
+xfs_dir_sf_entry_t *xfs_dir_sf_nextentry(xfs_dir_sf_entry_t *sfep);
+#define XFS_DIR_SF_NEXTENTRY(sfep) xfs_dir_sf_nextentry(sfep)
+#else
+#define XFS_DIR_SF_NEXTENTRY(sfep) /* next entry in struct */ \
+ ((xfs_dir_sf_entry_t *) \
+ ((char *)(sfep) + XFS_DIR_SF_ENTSIZE_BYENTRY(sfep)))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DIR_SF_ALLFIT)
+int xfs_dir_sf_allfit(int count, int totallen);
+#define XFS_DIR_SF_ALLFIT(count,totallen) \
+ xfs_dir_sf_allfit(count,totallen)
+#else
+#define XFS_DIR_SF_ALLFIT(count,totallen) /* will all entries fit? */ \
+ ((uint)sizeof(xfs_dir_sf_hdr_t) + \
+ ((uint)sizeof(xfs_dir_sf_entry_t)-1)*(count) + (totallen))
+#endif
+
+#if defined(XFS_DIR_TRACE)
+
+/*
+ * Kernel tracing support for directories.
+ */
+struct uio;
+struct xfs_inode;
+struct xfs_da_intnode;
+struct xfs_dinode;
+struct xfs_dir_leafblock;
+struct xfs_dir_leaf_entry;
+
+#define XFS_DIR_TRACE_SIZE 4096 /* size of global trace buffer */
+extern ktrace_t *xfs_dir_trace_buf;
+
+/*
+ * Trace record types.
+ */
+#define XFS_DIR_KTRACE_G_DU 1 /* dp, uio */
+#define XFS_DIR_KTRACE_G_DUB 2 /* dp, uio, bno */
+#define XFS_DIR_KTRACE_G_DUN 3 /* dp, uio, node */
+#define XFS_DIR_KTRACE_G_DUL 4 /* dp, uio, leaf */
+#define XFS_DIR_KTRACE_G_DUE 5 /* dp, uio, leaf entry */
+#define XFS_DIR_KTRACE_G_DUC 6 /* dp, uio, cookie */
+
+void xfs_dir_trace_g_du(char *where, struct xfs_inode *dp, struct uio *uio);
+void xfs_dir_trace_g_dub(char *where, struct xfs_inode *dp, struct uio *uio,
+ xfs_dablk_t bno);
+void xfs_dir_trace_g_dun(char *where, struct xfs_inode *dp, struct uio *uio,
+ struct xfs_da_intnode *node);
+void xfs_dir_trace_g_dul(char *where, struct xfs_inode *dp, struct uio *uio,
+ struct xfs_dir_leafblock *leaf);
+void xfs_dir_trace_g_due(char *where, struct xfs_inode *dp, struct uio *uio,
+ struct xfs_dir_leaf_entry *entry);
+void xfs_dir_trace_g_duc(char *where, struct xfs_inode *dp, struct uio *uio,
+ xfs_off_t cookie);
+void xfs_dir_trace_enter(int type, char *where,
+ void *a0, void *a1, void *a2, void *a3,
+ void *a4, void *a5, void *a6, void *a7,
+ void *a8, void *a9, void *a10, void *a11);
+#else
+#define xfs_dir_trace_g_du(w,d,u)
+#define xfs_dir_trace_g_dub(w,d,u,b)
+#define xfs_dir_trace_g_dun(w,d,u,n)
+#define xfs_dir_trace_g_dul(w,d,u,l)
+#define xfs_dir_trace_g_due(w,d,u,e)
+#define xfs_dir_trace_g_duc(w,d,u,c)
+#endif /* DEBUG */
+
+#endif /* __XFS_DIR_SF_H__ */
diff --git a/fs/xfs/xfs_dmapi.h b/fs/xfs/xfs_dmapi.h
new file mode 100644
index 00000000000000..55ae3e67d2450b
--- /dev/null
+++ b/fs/xfs/xfs_dmapi.h
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_DMAPI_H__
+#define __XFS_DMAPI_H__
+
+/* Values used to define the on-disk version of dm_attrname_t. All
+ * on-disk attribute names start with the 8-byte string "SGI_DMI_".
+ *
+ * In the on-disk inode, DMAPI attribute names consist of the user-provided
+ * name with the DMATTR_PREFIXSTRING pre-pended. This string must NEVER be
+ * changed.
+ */
+
+#define DMATTR_PREFIXLEN 8
+#define DMATTR_PREFIXSTRING "SGI_DMI_"
+
+typedef enum {
+ DM_EVENT_INVALID = -1,
+ DM_EVENT_CANCEL = 0, /* not supported */
+ DM_EVENT_MOUNT = 1,
+ DM_EVENT_PREUNMOUNT = 2,
+ DM_EVENT_UNMOUNT = 3,
+ DM_EVENT_DEBUT = 4, /* not supported */
+ DM_EVENT_CREATE = 5,
+ DM_EVENT_CLOSE = 6, /* not supported */
+ DM_EVENT_POSTCREATE = 7,
+ DM_EVENT_REMOVE = 8,
+ DM_EVENT_POSTREMOVE = 9,
+ DM_EVENT_RENAME = 10,
+ DM_EVENT_POSTRENAME = 11,
+ DM_EVENT_LINK = 12,
+ DM_EVENT_POSTLINK = 13,
+ DM_EVENT_SYMLINK = 14,
+ DM_EVENT_POSTSYMLINK = 15,
+ DM_EVENT_READ = 16,
+ DM_EVENT_WRITE = 17,
+ DM_EVENT_TRUNCATE = 18,
+ DM_EVENT_ATTRIBUTE = 19,
+ DM_EVENT_DESTROY = 20,
+ DM_EVENT_NOSPACE = 21,
+ DM_EVENT_USER = 22,
+ DM_EVENT_MAX = 23
+} dm_eventtype_t;
+#define HAVE_DM_EVENTTYPE_T
+
+typedef enum {
+ DM_RIGHT_NULL,
+ DM_RIGHT_SHARED,
+ DM_RIGHT_EXCL
+} dm_right_t;
+#define HAVE_DM_RIGHT_T
+
+/* Defines for determining if an event message should be sent. */
+#define DM_EVENT_ENABLED(vfsp, ip, event) ( \
+ unlikely ((vfsp)->vfs_flag & VFS_DMI) && \
+ ( ((ip)->i_d.di_dmevmask & (1 << event)) || \
+ ((ip)->i_mount->m_dmevmask & (1 << event)) ) \
+ )
+
+#define DM_EVENT_ENABLED_IO(vfsp, io, event) ( \
+ unlikely ((vfsp)->vfs_flag & VFS_DMI) && \
+ ( ((io)->io_dmevmask & (1 << event)) || \
+ ((io)->io_mount->m_dmevmask & (1 << event)) ) \
+ )
+
+#define DM_XFS_VALID_FS_EVENTS ( \
+ (1 << DM_EVENT_PREUNMOUNT) | \
+ (1 << DM_EVENT_UNMOUNT) | \
+ (1 << DM_EVENT_NOSPACE) | \
+ (1 << DM_EVENT_DEBUT) | \
+ (1 << DM_EVENT_CREATE) | \
+ (1 << DM_EVENT_POSTCREATE) | \
+ (1 << DM_EVENT_REMOVE) | \
+ (1 << DM_EVENT_POSTREMOVE) | \
+ (1 << DM_EVENT_RENAME) | \
+ (1 << DM_EVENT_POSTRENAME) | \
+ (1 << DM_EVENT_LINK) | \
+ (1 << DM_EVENT_POSTLINK) | \
+ (1 << DM_EVENT_SYMLINK) | \
+ (1 << DM_EVENT_POSTSYMLINK) | \
+ (1 << DM_EVENT_ATTRIBUTE) | \
+ (1 << DM_EVENT_DESTROY) )
+
+/* Events valid in dm_set_eventlist() when called with a file handle for
+ a regular file or a symlink. These events are persistent.
+*/
+
+#define DM_XFS_VALID_FILE_EVENTS ( \
+ (1 << DM_EVENT_ATTRIBUTE) | \
+ (1 << DM_EVENT_DESTROY) )
+
+/* Events valid in dm_set_eventlist() when called with a file handle for
+ a directory. These events are persistent.
+*/
+
+#define DM_XFS_VALID_DIRECTORY_EVENTS ( \
+ (1 << DM_EVENT_CREATE) | \
+ (1 << DM_EVENT_POSTCREATE) | \
+ (1 << DM_EVENT_REMOVE) | \
+ (1 << DM_EVENT_POSTREMOVE) | \
+ (1 << DM_EVENT_RENAME) | \
+ (1 << DM_EVENT_POSTRENAME) | \
+ (1 << DM_EVENT_LINK) | \
+ (1 << DM_EVENT_POSTLINK) | \
+ (1 << DM_EVENT_SYMLINK) | \
+ (1 << DM_EVENT_POSTSYMLINK) | \
+ (1 << DM_EVENT_ATTRIBUTE) | \
+ (1 << DM_EVENT_DESTROY) )
+
+/* Events supported by the XFS filesystem. */
+#define DM_XFS_SUPPORTED_EVENTS ( \
+ (1 << DM_EVENT_MOUNT) | \
+ (1 << DM_EVENT_PREUNMOUNT) | \
+ (1 << DM_EVENT_UNMOUNT) | \
+ (1 << DM_EVENT_NOSPACE) | \
+ (1 << DM_EVENT_CREATE) | \
+ (1 << DM_EVENT_POSTCREATE) | \
+ (1 << DM_EVENT_REMOVE) | \
+ (1 << DM_EVENT_POSTREMOVE) | \
+ (1 << DM_EVENT_RENAME) | \
+ (1 << DM_EVENT_POSTRENAME) | \
+ (1 << DM_EVENT_LINK) | \
+ (1 << DM_EVENT_POSTLINK) | \
+ (1 << DM_EVENT_SYMLINK) | \
+ (1 << DM_EVENT_POSTSYMLINK) | \
+ (1 << DM_EVENT_READ) | \
+ (1 << DM_EVENT_WRITE) | \
+ (1 << DM_EVENT_TRUNCATE) | \
+ (1 << DM_EVENT_ATTRIBUTE) | \
+ (1 << DM_EVENT_DESTROY) )
+
+
+/*
+ * Definitions used for the flags field on dm_send_*_event().
+ */
+
+#define DM_FLAGS_NDELAY 0x001 /* return EAGAIN after dm_pending() */
+#define DM_FLAGS_UNWANTED 0x002 /* event not in fsys dm_eventset_t */
+#define DM_FLAGS_ISEM 0x004 /* thread holds i_sem */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,21)
+/* i_alloc_sem was added in 2.4.22-pre1 */
+#define DM_FLAGS_IALLOCSEM_RD 0x010 /* thread holds i_alloc_sem rd */
+#define DM_FLAGS_IALLOCSEM_WR 0x020 /* thread holds i_alloc_sem wr */
+#endif
+#endif
+
+/*
+ * Based on IO_ISDIRECT, decide which i_ flag is set.
+ */
+#ifdef DM_FLAGS_IALLOCSEM_RD
+#define DM_SEM_FLAG_RD(ioflags) (((ioflags) & IO_ISDIRECT) ? \
+ DM_FLAGS_IALLOCSEM_RD : DM_FLAGS_ISEM)
+#define DM_SEM_FLAG_WR (DM_FLAGS_IALLOCSEM_WR | DM_FLAGS_ISEM)
+#else
+#define DM_SEM_FLAG_RD(ioflags) (((ioflags) & IO_ISDIRECT) ? \
+ 0 : DM_FLAGS_ISEM)
+#define DM_SEM_FLAG_WR (DM_FLAGS_ISEM)
+#endif
+
+/*
+ * Macros to turn caller specified delay/block flags into
+ * dm_send_xxxx_event flag DM_FLAGS_NDELAY.
+ */
+
+#define FILP_DELAY_FLAG(filp) ((filp->f_flags&(O_NDELAY|O_NONBLOCK)) ? \
+ DM_FLAGS_NDELAY : 0)
+#define AT_DELAY_FLAG(f) ((f&ATTR_NONBLOCK) ? DM_FLAGS_NDELAY : 0)
+
+
+extern struct bhv_vfsops xfs_dmops;
+
+#ifdef CONFIG_XFS_DMAPI
+void xfs_dm_init(struct file_system_type *);
+void xfs_dm_exit(struct file_system_type *);
+#define XFS_DM_INIT(fstype) xfs_dm_init(fstype)
+#define XFS_DM_EXIT(fstype) xfs_dm_exit(fstype)
+#else
+#define XFS_DM_INIT(fstype)
+#define XFS_DM_EXIT(fstype)
+#endif
+
+#endif /* __XFS_DMAPI_H__ */
diff --git a/fs/xfs/xfs_dmops.c b/fs/xfs/xfs_dmops.c
new file mode 100644
index 00000000000000..cec54ba800eb64
--- /dev/null
+++ b/fs/xfs/xfs_dmops.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#include "xfs.h"
+
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+
+xfs_dmops_t xfs_dmcore_stub = {
+ .xfs_send_data = (xfs_send_data_t)fs_nosys,
+ .xfs_send_mmap = (xfs_send_mmap_t)fs_noerr,
+ .xfs_send_destroy = (xfs_send_destroy_t)fs_nosys,
+ .xfs_send_namesp = (xfs_send_namesp_t)fs_nosys,
+ .xfs_send_unmount = (xfs_send_unmount_t)fs_noval,
+};
diff --git a/fs/xfs/xfs_error.c b/fs/xfs/xfs_error.c
new file mode 100644
index 00000000000000..bbe1dea11c0879
--- /dev/null
+++ b/fs/xfs/xfs_error.c
@@ -0,0 +1,327 @@
+/*
+ * Copyright (c) 2000-2001 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_sb.h"
+#include "xfs_trans.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode.h"
+#include "xfs_utils.h"
+#include "xfs_error.h"
+
+#ifdef DEBUG
+
+int xfs_etrap[XFS_ERROR_NTRAP] = {
+ 0,
+};
+
+int
+xfs_error_trap(int e)
+{
+ int i;
+
+ if (!e)
+ return 0;
+ for (i = 0; i < XFS_ERROR_NTRAP; i++) {
+ if (xfs_etrap[i] == 0)
+ break;
+ if (e != xfs_etrap[i])
+ continue;
+ cmn_err(CE_NOTE, "xfs_error_trap: error %d", e);
+ debug_stop_all_cpus((void *)-1LL);
+ BUG();
+ break;
+ }
+ return e;
+}
+#endif
+
+#if (defined(DEBUG) || defined(INDUCE_IO_ERROR))
+
+int xfs_etest[XFS_NUM_INJECT_ERROR];
+int64_t xfs_etest_fsid[XFS_NUM_INJECT_ERROR];
+char * xfs_etest_fsname[XFS_NUM_INJECT_ERROR];
+
+void
+xfs_error_test_init(void)
+{
+ memset(xfs_etest, 0, sizeof(xfs_etest));
+ memset(xfs_etest_fsid, 0, sizeof(xfs_etest_fsid));
+ memset(xfs_etest_fsname, 0, sizeof(xfs_etest_fsname));
+}
+
+int
+xfs_error_test(int error_tag, int *fsidp, char *expression,
+ int line, char *file, unsigned long randfactor)
+{
+ int i;
+ int64_t fsid;
+
+ if (random() % randfactor)
+ return 0;
+
+ memcpy(&fsid, fsidp, sizeof(xfs_fsid_t));
+
+ for (i = 0; i < XFS_NUM_INJECT_ERROR; i++) {
+ if (xfs_etest[i] == error_tag && xfs_etest_fsid[i] == fsid) {
+ cmn_err(CE_WARN,
+ "Injecting error (%s) at file %s, line %d, on filesystem \"%s\"",
+ expression, file, line, xfs_etest_fsname[i]);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+int
+xfs_errortag_add(int error_tag, xfs_mount_t *mp)
+{
+ int i;
+ int len;
+ int64_t fsid;
+
+ memcpy(&fsid, mp->m_fixedfsid, sizeof(xfs_fsid_t));
+
+ for (i = 0; i < XFS_NUM_INJECT_ERROR; i++) {
+ if (xfs_etest_fsid[i] == fsid && xfs_etest[i] == error_tag) {
+ cmn_err(CE_WARN, "XFS error tag #%d on", error_tag);
+ return 0;
+ }
+ }
+
+ for (i = 0; i < XFS_NUM_INJECT_ERROR; i++) {
+ if (xfs_etest[i] == 0) {
+ cmn_err(CE_WARN, "Turned on XFS error tag #%d",
+ error_tag);
+ xfs_etest[i] = error_tag;
+ xfs_etest_fsid[i] = fsid;
+ len = strlen(mp->m_fsname);
+ xfs_etest_fsname[i] = kmem_alloc(len + 1, KM_SLEEP);
+ strcpy(xfs_etest_fsname[i], mp->m_fsname);
+ return 0;
+ }
+ }
+
+ cmn_err(CE_WARN, "error tag overflow, too many turned on");
+
+ return 1;
+}
+
+int
+xfs_errortag_clear(int error_tag, xfs_mount_t *mp)
+{
+ int i;
+ int64_t fsid;
+
+ memcpy(&fsid, mp->m_fixedfsid, sizeof(xfs_fsid_t));
+
+ for (i = 0; i < XFS_NUM_INJECT_ERROR; i++) {
+ if (xfs_etest_fsid[i] == fsid && xfs_etest[i] == error_tag) {
+ xfs_etest[i] = 0;
+ xfs_etest_fsid[i] = 0LL;
+ kmem_free(xfs_etest_fsname[i],
+ strlen(xfs_etest_fsname[i]) + 1);
+ xfs_etest_fsname[i] = NULL;
+ cmn_err(CE_WARN, "Cleared XFS error tag #%d",
+ error_tag);
+ return 0;
+ }
+ }
+
+ cmn_err(CE_WARN, "XFS error tag %d not on", error_tag);
+
+ return 1;
+}
+
+int
+xfs_errortag_clearall_umount(int64_t fsid, char *fsname, int loud)
+{
+ int i;
+ int cleared = 0;
+
+ for (i = 0; i < XFS_NUM_INJECT_ERROR; i++) {
+ if ((fsid == 0LL || xfs_etest_fsid[i] == fsid) &&
+ xfs_etest[i] != 0) {
+ cleared = 1;
+ cmn_err(CE_WARN, "Clearing XFS error tag #%d",
+ xfs_etest[i]);
+ xfs_etest[i] = 0;
+ xfs_etest_fsid[i] = 0LL;
+ kmem_free(xfs_etest_fsname[i],
+ strlen(xfs_etest_fsname[i]) + 1);
+ xfs_etest_fsname[i] = NULL;
+ }
+ }
+
+ if (loud || cleared)
+ cmn_err(CE_WARN,
+ "Cleared all XFS error tags for filesystem \"%s\"",
+ fsname);
+
+ return 0;
+}
+
+int
+xfs_errortag_clearall(xfs_mount_t *mp)
+{
+ int64_t fsid;
+
+ memcpy(&fsid, mp->m_fixedfsid, sizeof(xfs_fsid_t));
+
+ return xfs_errortag_clearall_umount(fsid, mp->m_fsname, 1);
+}
+#endif /* DEBUG || INDUCE_IO_ERROR */
+
+static void
+xfs_fs_vcmn_err(int level, xfs_mount_t *mp, char *fmt, va_list ap)
+{
+ if (mp != NULL) {
+ char *newfmt;
+ int len = 16 + mp->m_fsname_len + strlen(fmt);
+
+ newfmt = kmem_alloc(len, KM_SLEEP);
+ sprintf(newfmt, "Filesystem \"%s\": %s", mp->m_fsname, fmt);
+ icmn_err(level, newfmt, ap);
+ kmem_free(newfmt, len);
+ } else {
+ icmn_err(level, fmt, ap);
+ }
+}
+
+void
+xfs_fs_cmn_err(int level, xfs_mount_t *mp, char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ xfs_fs_vcmn_err(level, mp, fmt, ap);
+ va_end(ap);
+}
+
+void
+xfs_cmn_err(int panic_tag, int level, xfs_mount_t *mp, char *fmt, ...)
+{
+ va_list ap;
+
+#ifdef DEBUG
+ xfs_panic_mask |= XFS_PTAG_SHUTDOWN_CORRUPT;
+#endif
+
+ if (xfs_panic_mask && (xfs_panic_mask & panic_tag)
+ && (level & CE_ALERT)) {
+ level &= ~CE_ALERT;
+ level |= CE_PANIC;
+ cmn_err(CE_ALERT, "XFS: Transforming an alert into a BUG.");
+ }
+ va_start(ap, fmt);
+ xfs_fs_vcmn_err(level, mp, fmt, ap);
+ va_end(ap);
+}
+
+void
+xfs_error_report(
+ char *tag,
+ int level,
+ xfs_mount_t *mp,
+ char *fname,
+ int linenum,
+ inst_t *ra)
+{
+ if (level <= xfs_error_level) {
+ xfs_cmn_err(XFS_PTAG_ERROR_REPORT,
+ CE_ALERT, mp,
+ "XFS internal error %s at line %d of file %s. Caller 0x%p\n",
+ tag, linenum, fname, ra);
+
+ xfs_stack_trace();
+ }
+}
+
+void
+xfs_hex_dump(void *p, int length)
+{
+ __uint8_t *uip = (__uint8_t*)p;
+ int i;
+ char sbuf[128], *s;
+
+ s = sbuf;
+ *s = '\0';
+ for (i=0; i<length; i++, uip++) {
+ if ((i % 16) == 0) {
+ if (*s != '\0')
+ cmn_err(CE_ALERT, "%s\n", sbuf);
+ s = sbuf;
+ sprintf(s, "0x%x: ", i);
+ while( *s != '\0')
+ s++;
+ }
+ sprintf(s, "%02x ", *uip);
+
+ /*
+ * the kernel sprintf is a void; user sprintf returns
+ * the sprintf'ed string's length. Find the new end-
+ * of-string
+ */
+ while( *s != '\0')
+ s++;
+ }
+ cmn_err(CE_ALERT, "%s\n", sbuf);
+}
+
+void
+xfs_corruption_error(
+ char *tag,
+ int level,
+ xfs_mount_t *mp,
+ void *p,
+ char *fname,
+ int linenum,
+ inst_t *ra)
+{
+ if (level <= xfs_error_level)
+ xfs_hex_dump(p, 16);
+ xfs_error_report(tag, level, mp, fname, linenum, ra);
+}
diff --git a/fs/xfs/xfs_error.h b/fs/xfs/xfs_error.h
new file mode 100644
index 00000000000000..6bc0535c0a65bf
--- /dev/null
+++ b/fs/xfs/xfs_error.h
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_ERROR_H__
+#define __XFS_ERROR_H__
+
+#define prdev(fmt,targ,args...) \
+ printk("XFS: device %s- " fmt "\n", XFS_BUFTARG_NAME(targ), ## args)
+
+#define XFS_ERECOVER 1 /* Failure to recover log */
+#define XFS_ELOGSTAT 2 /* Failure to stat log in user space */
+#define XFS_ENOLOGSPACE 3 /* Reservation too large */
+#define XFS_ENOTSUP 4 /* Operation not supported */
+#define XFS_ENOLSN 5 /* Can't find the lsn you asked for */
+#define XFS_ENOTFOUND 6
+#define XFS_ENOTXFS 7 /* Not XFS filesystem */
+
+#ifdef DEBUG
+#define XFS_ERROR_NTRAP 10
+extern int xfs_etrap[XFS_ERROR_NTRAP];
+extern int xfs_error_trap(int);
+#define XFS_ERROR(e) xfs_error_trap(e)
+#else
+#define XFS_ERROR(e) (e)
+#endif
+
+struct xfs_mount;
+
+extern void
+xfs_error_report(
+ char *tag,
+ int level,
+ struct xfs_mount *mp,
+ char *fname,
+ int linenum,
+ inst_t *ra);
+
+extern void
+xfs_corruption_error(
+ char *tag,
+ int level,
+ struct xfs_mount *mp,
+ void *p,
+ char *fname,
+ int linenum,
+ inst_t *ra);
+
+extern void
+xfs_hex_dump(void *p, int length);
+
+#define XFS_ERROR_REPORT(e, lvl, mp) \
+ xfs_error_report(e, lvl, mp, __FILE__, __LINE__, __return_address)
+#define XFS_CORRUPTION_ERROR(e, lvl, mp, mem) \
+ xfs_corruption_error(e, lvl, mp, mem, \
+ __FILE__, __LINE__, __return_address)
+
+#define XFS_ERRLEVEL_OFF 0
+#define XFS_ERRLEVEL_LOW 1
+#define XFS_ERRLEVEL_HIGH 5
+
+/*
+ * error injection tags - the labels can be anything you want
+ * but each tag should have its own unique number
+ */
+
+#define XFS_ERRTAG_NOERROR 0
+#define XFS_ERRTAG_IFLUSH_1 1
+#define XFS_ERRTAG_IFLUSH_2 2
+#define XFS_ERRTAG_IFLUSH_3 3
+#define XFS_ERRTAG_IFLUSH_4 4
+#define XFS_ERRTAG_IFLUSH_5 5
+#define XFS_ERRTAG_IFLUSH_6 6
+#define XFS_ERRTAG_DA_READ_BUF 7
+#define XFS_ERRTAG_BTREE_CHECK_LBLOCK 8
+#define XFS_ERRTAG_BTREE_CHECK_SBLOCK 9
+#define XFS_ERRTAG_ALLOC_READ_AGF 10
+#define XFS_ERRTAG_IALLOC_READ_AGI 11
+#define XFS_ERRTAG_ITOBP_INOTOBP 12
+#define XFS_ERRTAG_IUNLINK 13
+#define XFS_ERRTAG_IUNLINK_REMOVE 14
+#define XFS_ERRTAG_DIR_INO_VALIDATE 15
+#define XFS_ERRTAG_BULKSTAT_READ_CHUNK 16
+#define XFS_ERRTAG_IODONE_IOERR 17
+#define XFS_ERRTAG_STRATREAD_IOERR 18
+#define XFS_ERRTAG_STRATCMPL_IOERR 19
+#define XFS_ERRTAG_DIOWRITE_IOERR 20
+#define XFS_ERRTAG_BMAPIFORMAT 21
+#define XFS_ERRTAG_MAX 22
+
+/*
+ * Random factors for above tags, 1 means always, 2 means 1/2 time, etc.
+ */
+#define XFS_RANDOM_DEFAULT 100
+#define XFS_RANDOM_IFLUSH_1 XFS_RANDOM_DEFAULT
+#define XFS_RANDOM_IFLUSH_2 XFS_RANDOM_DEFAULT
+#define XFS_RANDOM_IFLUSH_3 XFS_RANDOM_DEFAULT
+#define XFS_RANDOM_IFLUSH_4 XFS_RANDOM_DEFAULT
+#define XFS_RANDOM_IFLUSH_5 XFS_RANDOM_DEFAULT
+#define XFS_RANDOM_IFLUSH_6 XFS_RANDOM_DEFAULT
+#define XFS_RANDOM_DA_READ_BUF XFS_RANDOM_DEFAULT
+#define XFS_RANDOM_BTREE_CHECK_LBLOCK (XFS_RANDOM_DEFAULT/4)
+#define XFS_RANDOM_BTREE_CHECK_SBLOCK XFS_RANDOM_DEFAULT
+#define XFS_RANDOM_ALLOC_READ_AGF XFS_RANDOM_DEFAULT
+#define XFS_RANDOM_IALLOC_READ_AGI XFS_RANDOM_DEFAULT
+#define XFS_RANDOM_ITOBP_INOTOBP XFS_RANDOM_DEFAULT
+#define XFS_RANDOM_IUNLINK XFS_RANDOM_DEFAULT
+#define XFS_RANDOM_IUNLINK_REMOVE XFS_RANDOM_DEFAULT
+#define XFS_RANDOM_DIR_INO_VALIDATE XFS_RANDOM_DEFAULT
+#define XFS_RANDOM_BULKSTAT_READ_CHUNK XFS_RANDOM_DEFAULT
+#define XFS_RANDOM_IODONE_IOERR (XFS_RANDOM_DEFAULT/10)
+#define XFS_RANDOM_STRATREAD_IOERR (XFS_RANDOM_DEFAULT/10)
+#define XFS_RANDOM_STRATCMPL_IOERR (XFS_RANDOM_DEFAULT/10)
+#define XFS_RANDOM_DIOWRITE_IOERR (XFS_RANDOM_DEFAULT/10)
+#define XFS_RANDOM_BMAPIFORMAT XFS_RANDOM_DEFAULT
+
+#if (defined(DEBUG) || defined(INDUCE_IO_ERROR))
+extern int xfs_error_test(int, int *, char *, int, char *, unsigned long);
+void xfs_error_test_init(void);
+
+#define XFS_NUM_INJECT_ERROR 10
+
+#ifdef __ANSI_CPP__
+#define XFS_TEST_ERROR(expr, mp, tag, rf) \
+ ((expr) || \
+ xfs_error_test((tag), (mp)->m_fixedfsid, #expr, __LINE__, __FILE__, \
+ (rf)))
+#else
+#define XFS_TEST_ERROR(expr, mp, tag, rf) \
+ ((expr) || \
+ xfs_error_test((tag), (mp)->m_fixedfsid, "expr", __LINE__, __FILE__, \
+ (rf)))
+#endif /* __ANSI_CPP__ */
+
+int xfs_errortag_add(int error_tag, xfs_mount_t *mp);
+int xfs_errortag_clear(int error_tag, xfs_mount_t *mp);
+
+int xfs_errortag_clearall(xfs_mount_t *mp);
+int xfs_errortag_clearall_umount(int64_t fsid, char *fsname,
+ int loud);
+#else
+#define XFS_TEST_ERROR(expr, mp, tag, rf) (expr)
+#define xfs_errortag_add(tag, mp) (ENOSYS)
+#define xfs_errortag_clearall(mp) (ENOSYS)
+#endif /* (DEBUG || INDUCE_IO_ERROR) */
+
+/*
+ * XFS panic tags -- allow a call to xfs_cmn_err() be turned into
+ * a panic by setting xfs_panic_mask in a
+ * sysctl. update xfs_max[XFS_PARAM] if
+ * more are added.
+ */
+#define XFS_NO_PTAG 0
+#define XFS_PTAG_IFLUSH 0x00000001
+#define XFS_PTAG_LOGRES 0x00000002
+#define XFS_PTAG_AILDELETE 0x00000004
+#define XFS_PTAG_ERROR_REPORT 0x00000008
+#define XFS_PTAG_SHUTDOWN_CORRUPT 0x00000010
+#define XFS_PTAG_SHUTDOWN_IOERROR 0x00000020
+#define XFS_PTAG_SHUTDOWN_LOGERROR 0x00000040
+
+struct xfs_mount;
+/* PRINTFLIKE4 */
+void xfs_cmn_err(int panic_tag, int level, struct xfs_mount *mp,
+ char *fmt, ...);
+/* PRINTFLIKE3 */
+void xfs_fs_cmn_err(int level, struct xfs_mount *mp, char *fmt, ...);
+
+#endif /* __XFS_ERROR_H__ */
diff --git a/fs/xfs/xfs_extfree_item.c b/fs/xfs/xfs_extfree_item.c
new file mode 100644
index 00000000000000..5eafd5b6321183
--- /dev/null
+++ b/fs/xfs/xfs_extfree_item.c
@@ -0,0 +1,668 @@
+/*
+ * Copyright (c) 2000-2001 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+/*
+ * This file contains the implementation of the xfs_efi_log_item
+ * and xfs_efd_log_item items.
+ */
+
+#include "xfs.h"
+
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_buf_item.h"
+#include "xfs_sb.h"
+#include "xfs_dir.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_trans_priv.h"
+#include "xfs_extfree_item.h"
+
+
+kmem_zone_t *xfs_efi_zone;
+kmem_zone_t *xfs_efd_zone;
+
+STATIC void xfs_efi_item_unlock(xfs_efi_log_item_t *);
+STATIC void xfs_efi_item_abort(xfs_efi_log_item_t *);
+STATIC void xfs_efd_item_abort(xfs_efd_log_item_t *);
+
+
+
+/*
+ * This returns the number of iovecs needed to log the given efi item.
+ * We only need 1 iovec for an efi item. It just logs the efi_log_format
+ * structure.
+ */
+/*ARGSUSED*/
+STATIC uint
+xfs_efi_item_size(xfs_efi_log_item_t *efip)
+{
+ return 1;
+}
+
+/*
+ * This is called to fill in the vector of log iovecs for the
+ * given efi log item. We use only 1 iovec, and we point that
+ * at the efi_log_format structure embedded in the efi item.
+ * It is at this point that we assert that all of the extent
+ * slots in the efi item have been filled.
+ */
+STATIC void
+xfs_efi_item_format(xfs_efi_log_item_t *efip,
+ xfs_log_iovec_t *log_vector)
+{
+ uint size;
+
+ ASSERT(efip->efi_next_extent == efip->efi_format.efi_nextents);
+
+ efip->efi_format.efi_type = XFS_LI_EFI;
+
+ size = sizeof(xfs_efi_log_format_t);
+ size += (efip->efi_format.efi_nextents - 1) * sizeof(xfs_extent_t);
+ efip->efi_format.efi_size = 1;
+
+ log_vector->i_addr = (xfs_caddr_t)&(efip->efi_format);
+ log_vector->i_len = size;
+ ASSERT(size >= sizeof(xfs_efi_log_format_t));
+}
+
+
+/*
+ * Pinning has no meaning for an efi item, so just return.
+ */
+/*ARGSUSED*/
+STATIC void
+xfs_efi_item_pin(xfs_efi_log_item_t *efip)
+{
+ return;
+}
+
+
+/*
+ * While EFIs cannot really be pinned, the unpin operation is the
+ * last place at which the EFI is manipulated during a transaction.
+ * Here we coordinate with xfs_efi_cancel() to determine who gets to
+ * free the EFI.
+ */
+/*ARGSUSED*/
+STATIC void
+xfs_efi_item_unpin(xfs_efi_log_item_t *efip, int stale)
+{
+ int nexts;
+ int size;
+ xfs_mount_t *mp;
+ SPLDECL(s);
+
+ mp = efip->efi_item.li_mountp;
+ AIL_LOCK(mp, s);
+ if (efip->efi_flags & XFS_EFI_CANCELED) {
+ /*
+ * xfs_trans_delete_ail() drops the AIL lock.
+ */
+ xfs_trans_delete_ail(mp, (xfs_log_item_t *)efip, s);
+
+ nexts = efip->efi_format.efi_nextents;
+ if (nexts > XFS_EFI_MAX_FAST_EXTENTS) {
+ size = sizeof(xfs_efi_log_item_t);
+ size += (nexts - 1) * sizeof(xfs_extent_t);
+ kmem_free(efip, size);
+ } else {
+ kmem_zone_free(xfs_efi_zone, efip);
+ }
+ } else {
+ efip->efi_flags |= XFS_EFI_COMMITTED;
+ AIL_UNLOCK(mp, s);
+ }
+
+ return;
+}
+
+/*
+ * like unpin only we have to also clear the xaction descriptor
+ * pointing the log item if we free the item. This routine duplicates
+ * unpin because efi_flags is protected by the AIL lock. Freeing
+ * the descriptor and then calling unpin would force us to drop the AIL
+ * lock which would open up a race condition.
+ */
+STATIC void
+xfs_efi_item_unpin_remove(xfs_efi_log_item_t *efip, xfs_trans_t *tp)
+{
+ int nexts;
+ int size;
+ xfs_mount_t *mp;
+ xfs_log_item_desc_t *lidp;
+ SPLDECL(s);
+
+ mp = efip->efi_item.li_mountp;
+ AIL_LOCK(mp, s);
+ if (efip->efi_flags & XFS_EFI_CANCELED) {
+ /*
+ * free the xaction descriptor pointing to this item
+ */
+ lidp = xfs_trans_find_item(tp, (xfs_log_item_t *) efip);
+ xfs_trans_free_item(tp, lidp);
+ /*
+ * pull the item off the AIL.
+ * xfs_trans_delete_ail() drops the AIL lock.
+ */
+ xfs_trans_delete_ail(mp, (xfs_log_item_t *)efip, s);
+ /*
+ * now free the item itself
+ */
+ nexts = efip->efi_format.efi_nextents;
+ if (nexts > XFS_EFI_MAX_FAST_EXTENTS) {
+ size = sizeof(xfs_efi_log_item_t);
+ size += (nexts - 1) * sizeof(xfs_extent_t);
+ kmem_free(efip, size);
+ } else {
+ kmem_zone_free(xfs_efi_zone, efip);
+ }
+ } else {
+ efip->efi_flags |= XFS_EFI_COMMITTED;
+ AIL_UNLOCK(mp, s);
+ }
+
+ return;
+}
+
+/*
+ * Efi items have no locking or pushing. However, since EFIs are
+ * pulled from the AIL when their corresponding EFDs are committed
+ * to disk, their situation is very similar to being pinned. Return
+ * XFS_ITEM_PINNED so that the caller will eventually flush the log.
+ * This should help in getting the EFI out of the AIL.
+ */
+/*ARGSUSED*/
+STATIC uint
+xfs_efi_item_trylock(xfs_efi_log_item_t *efip)
+{
+ return XFS_ITEM_PINNED;
+}
+
+/*
+ * Efi items have no locking, so just return.
+ */
+/*ARGSUSED*/
+STATIC void
+xfs_efi_item_unlock(xfs_efi_log_item_t *efip)
+{
+ if (efip->efi_item.li_flags & XFS_LI_ABORTED)
+ xfs_efi_item_abort(efip);
+ return;
+}
+
+/*
+ * The EFI is logged only once and cannot be moved in the log, so
+ * simply return the lsn at which it's been logged. The canceled
+ * flag is not paid any attention here. Checking for that is delayed
+ * until the EFI is unpinned.
+ */
+/*ARGSUSED*/
+STATIC xfs_lsn_t
+xfs_efi_item_committed(xfs_efi_log_item_t *efip, xfs_lsn_t lsn)
+{
+ return lsn;
+}
+
+/*
+ * This is called when the transaction logging the EFI is aborted.
+ * Free up the EFI and return. No need to clean up the slot for
+ * the item in the transaction. That was done by the unpin code
+ * which is called prior to this routine in the abort/fs-shutdown path.
+ */
+STATIC void
+xfs_efi_item_abort(xfs_efi_log_item_t *efip)
+{
+ int nexts;
+ int size;
+
+ nexts = efip->efi_format.efi_nextents;
+ if (nexts > XFS_EFI_MAX_FAST_EXTENTS) {
+ size = sizeof(xfs_efi_log_item_t);
+ size += (nexts - 1) * sizeof(xfs_extent_t);
+ kmem_free(efip, size);
+ } else {
+ kmem_zone_free(xfs_efi_zone, efip);
+ }
+ return;
+}
+
+/*
+ * There isn't much you can do to push on an efi item. It is simply
+ * stuck waiting for all of its corresponding efd items to be
+ * committed to disk.
+ */
+/*ARGSUSED*/
+STATIC void
+xfs_efi_item_push(xfs_efi_log_item_t *efip)
+{
+ return;
+}
+
+/*
+ * The EFI dependency tracking op doesn't do squat. It can't because
+ * it doesn't know where the free extent is coming from. The dependency
+ * tracking has to be handled by the "enclosing" metadata object. For
+ * example, for inodes, the inode is locked throughout the extent freeing
+ * so the dependency should be recorded there.
+ */
+/*ARGSUSED*/
+STATIC void
+xfs_efi_item_committing(xfs_efi_log_item_t *efip, xfs_lsn_t lsn)
+{
+ return;
+}
+
+/*
+ * This is the ops vector shared by all efi log items.
+ */
+struct xfs_item_ops xfs_efi_item_ops = {
+ .iop_size = (uint(*)(xfs_log_item_t*))xfs_efi_item_size,
+ .iop_format = (void(*)(xfs_log_item_t*, xfs_log_iovec_t*))
+ xfs_efi_item_format,
+ .iop_pin = (void(*)(xfs_log_item_t*))xfs_efi_item_pin,
+ .iop_unpin = (void(*)(xfs_log_item_t*, int))xfs_efi_item_unpin,
+ .iop_unpin_remove = (void(*)(xfs_log_item_t*, xfs_trans_t *))
+ xfs_efi_item_unpin_remove,
+ .iop_trylock = (uint(*)(xfs_log_item_t*))xfs_efi_item_trylock,
+ .iop_unlock = (void(*)(xfs_log_item_t*))xfs_efi_item_unlock,
+ .iop_committed = (xfs_lsn_t(*)(xfs_log_item_t*, xfs_lsn_t))
+ xfs_efi_item_committed,
+ .iop_push = (void(*)(xfs_log_item_t*))xfs_efi_item_push,
+ .iop_abort = (void(*)(xfs_log_item_t*))xfs_efi_item_abort,
+ .iop_pushbuf = NULL,
+ .iop_committing = (void(*)(xfs_log_item_t*, xfs_lsn_t))
+ xfs_efi_item_committing
+};
+
+
+/*
+ * Allocate and initialize an efi item with the given number of extents.
+ */
+xfs_efi_log_item_t *
+xfs_efi_init(xfs_mount_t *mp,
+ uint nextents)
+
+{
+ xfs_efi_log_item_t *efip;
+ uint size;
+
+ ASSERT(nextents > 0);
+ if (nextents > XFS_EFI_MAX_FAST_EXTENTS) {
+ size = (uint)(sizeof(xfs_efi_log_item_t) +
+ ((nextents - 1) * sizeof(xfs_extent_t)));
+ efip = (xfs_efi_log_item_t*)kmem_zalloc(size, KM_SLEEP);
+ } else {
+ efip = (xfs_efi_log_item_t*)kmem_zone_zalloc(xfs_efi_zone,
+ KM_SLEEP);
+ }
+
+ efip->efi_item.li_type = XFS_LI_EFI;
+ efip->efi_item.li_ops = &xfs_efi_item_ops;
+ efip->efi_item.li_mountp = mp;
+ efip->efi_format.efi_nextents = nextents;
+ efip->efi_format.efi_id = (__psint_t)(void*)efip;
+
+ return (efip);
+}
+
+/*
+ * This is called by the efd item code below to release references to
+ * the given efi item. Each efd calls this with the number of
+ * extents that it has logged, and when the sum of these reaches
+ * the total number of extents logged by this efi item we can free
+ * the efi item.
+ *
+ * Freeing the efi item requires that we remove it from the AIL.
+ * We'll use the AIL lock to protect our counters as well as
+ * the removal from the AIL.
+ */
+void
+xfs_efi_release(xfs_efi_log_item_t *efip,
+ uint nextents)
+{
+ xfs_mount_t *mp;
+ int extents_left;
+ uint size;
+ int nexts;
+ SPLDECL(s);
+
+ mp = efip->efi_item.li_mountp;
+ ASSERT(efip->efi_next_extent > 0);
+ ASSERT(efip->efi_flags & XFS_EFI_COMMITTED);
+
+ AIL_LOCK(mp, s);
+ ASSERT(efip->efi_next_extent >= nextents);
+ efip->efi_next_extent -= nextents;
+ extents_left = efip->efi_next_extent;
+ if (extents_left == 0) {
+ /*
+ * xfs_trans_delete_ail() drops the AIL lock.
+ */
+ xfs_trans_delete_ail(mp, (xfs_log_item_t *)efip, s);
+ } else {
+ AIL_UNLOCK(mp, s);
+ }
+
+ if (extents_left == 0) {
+ nexts = efip->efi_format.efi_nextents;
+ if (nexts > XFS_EFI_MAX_FAST_EXTENTS) {
+ size = sizeof(xfs_efi_log_item_t);
+ size += (nexts - 1) * sizeof(xfs_extent_t);
+ kmem_free(efip, size);
+ } else {
+ kmem_zone_free(xfs_efi_zone, efip);
+ }
+ }
+}
+
+/*
+ * This is called when the transaction that should be committing the
+ * EFD corresponding to the given EFI is aborted. The committed and
+ * canceled flags are used to coordinate the freeing of the EFI and
+ * the references by the transaction that committed it.
+ */
+STATIC void
+xfs_efi_cancel(
+ xfs_efi_log_item_t *efip)
+{
+ int nexts;
+ int size;
+ xfs_mount_t *mp;
+ SPLDECL(s);
+
+ mp = efip->efi_item.li_mountp;
+ AIL_LOCK(mp, s);
+ if (efip->efi_flags & XFS_EFI_COMMITTED) {
+ /*
+ * xfs_trans_delete_ail() drops the AIL lock.
+ */
+ xfs_trans_delete_ail(mp, (xfs_log_item_t *)efip, s);
+
+ nexts = efip->efi_format.efi_nextents;
+ if (nexts > XFS_EFI_MAX_FAST_EXTENTS) {
+ size = sizeof(xfs_efi_log_item_t);
+ size += (nexts - 1) * sizeof(xfs_extent_t);
+ kmem_free(efip, size);
+ } else {
+ kmem_zone_free(xfs_efi_zone, efip);
+ }
+ } else {
+ efip->efi_flags |= XFS_EFI_CANCELED;
+ AIL_UNLOCK(mp, s);
+ }
+
+ return;
+}
+
+
+
+
+
+/*
+ * This returns the number of iovecs needed to log the given efd item.
+ * We only need 1 iovec for an efd item. It just logs the efd_log_format
+ * structure.
+ */
+/*ARGSUSED*/
+STATIC uint
+xfs_efd_item_size(xfs_efd_log_item_t *efdp)
+{
+ return 1;
+}
+
+/*
+ * This is called to fill in the vector of log iovecs for the
+ * given efd log item. We use only 1 iovec, and we point that
+ * at the efd_log_format structure embedded in the efd item.
+ * It is at this point that we assert that all of the extent
+ * slots in the efd item have been filled.
+ */
+STATIC void
+xfs_efd_item_format(xfs_efd_log_item_t *efdp,
+ xfs_log_iovec_t *log_vector)
+{
+ uint size;
+
+ ASSERT(efdp->efd_next_extent == efdp->efd_format.efd_nextents);
+
+ efdp->efd_format.efd_type = XFS_LI_EFD;
+
+ size = sizeof(xfs_efd_log_format_t);
+ size += (efdp->efd_format.efd_nextents - 1) * sizeof(xfs_extent_t);
+ efdp->efd_format.efd_size = 1;
+
+ log_vector->i_addr = (xfs_caddr_t)&(efdp->efd_format);
+ log_vector->i_len = size;
+ ASSERT(size >= sizeof(xfs_efd_log_format_t));
+}
+
+
+/*
+ * Pinning has no meaning for an efd item, so just return.
+ */
+/*ARGSUSED*/
+STATIC void
+xfs_efd_item_pin(xfs_efd_log_item_t *efdp)
+{
+ return;
+}
+
+
+/*
+ * Since pinning has no meaning for an efd item, unpinning does
+ * not either.
+ */
+/*ARGSUSED*/
+STATIC void
+xfs_efd_item_unpin(xfs_efd_log_item_t *efdp, int stale)
+{
+ return;
+}
+
+/*ARGSUSED*/
+STATIC void
+xfs_efd_item_unpin_remove(xfs_efd_log_item_t *efdp, xfs_trans_t *tp)
+{
+ return;
+}
+
+/*
+ * Efd items have no locking, so just return success.
+ */
+/*ARGSUSED*/
+STATIC uint
+xfs_efd_item_trylock(xfs_efd_log_item_t *efdp)
+{
+ return XFS_ITEM_LOCKED;
+}
+
+/*
+ * Efd items have no locking or pushing, so return failure
+ * so that the caller doesn't bother with us.
+ */
+/*ARGSUSED*/
+STATIC void
+xfs_efd_item_unlock(xfs_efd_log_item_t *efdp)
+{
+ if (efdp->efd_item.li_flags & XFS_LI_ABORTED)
+ xfs_efd_item_abort(efdp);
+ return;
+}
+
+/*
+ * When the efd item is committed to disk, all we need to do
+ * is delete our reference to our partner efi item and then
+ * free ourselves. Since we're freeing ourselves we must
+ * return -1 to keep the transaction code from further referencing
+ * this item.
+ */
+/*ARGSUSED*/
+STATIC xfs_lsn_t
+xfs_efd_item_committed(xfs_efd_log_item_t *efdp, xfs_lsn_t lsn)
+{
+ uint size;
+ int nexts;
+
+ /*
+ * If we got a log I/O error, it's always the case that the LR with the
+ * EFI got unpinned and freed before the EFD got aborted.
+ */
+ if ((efdp->efd_item.li_flags & XFS_LI_ABORTED) == 0)
+ xfs_efi_release(efdp->efd_efip, efdp->efd_format.efd_nextents);
+
+ nexts = efdp->efd_format.efd_nextents;
+ if (nexts > XFS_EFD_MAX_FAST_EXTENTS) {
+ size = sizeof(xfs_efd_log_item_t);
+ size += (nexts - 1) * sizeof(xfs_extent_t);
+ kmem_free(efdp, size);
+ } else {
+ kmem_zone_free(xfs_efd_zone, efdp);
+ }
+
+ return (xfs_lsn_t)-1;
+}
+
+/*
+ * The transaction of which this EFD is a part has been aborted.
+ * Inform its companion EFI of this fact and then clean up after
+ * ourselves. No need to clean up the slot for the item in the
+ * transaction. That was done by the unpin code which is called
+ * prior to this routine in the abort/fs-shutdown path.
+ */
+STATIC void
+xfs_efd_item_abort(xfs_efd_log_item_t *efdp)
+{
+ int nexts;
+ int size;
+
+ /*
+ * If we got a log I/O error, it's always the case that the LR with the
+ * EFI got unpinned and freed before the EFD got aborted. So don't
+ * reference the EFI at all in that case.
+ */
+ if ((efdp->efd_item.li_flags & XFS_LI_ABORTED) == 0)
+ xfs_efi_cancel(efdp->efd_efip);
+
+ nexts = efdp->efd_format.efd_nextents;
+ if (nexts > XFS_EFD_MAX_FAST_EXTENTS) {
+ size = sizeof(xfs_efd_log_item_t);
+ size += (nexts - 1) * sizeof(xfs_extent_t);
+ kmem_free(efdp, size);
+ } else {
+ kmem_zone_free(xfs_efd_zone, efdp);
+ }
+ return;
+}
+
+/*
+ * There isn't much you can do to push on an efd item. It is simply
+ * stuck waiting for the log to be flushed to disk.
+ */
+/*ARGSUSED*/
+STATIC void
+xfs_efd_item_push(xfs_efd_log_item_t *efdp)
+{
+ return;
+}
+
+/*
+ * The EFD dependency tracking op doesn't do squat. It can't because
+ * it doesn't know where the free extent is coming from. The dependency
+ * tracking has to be handled by the "enclosing" metadata object. For
+ * example, for inodes, the inode is locked throughout the extent freeing
+ * so the dependency should be recorded there.
+ */
+/*ARGSUSED*/
+STATIC void
+xfs_efd_item_committing(xfs_efd_log_item_t *efip, xfs_lsn_t lsn)
+{
+ return;
+}
+
+/*
+ * This is the ops vector shared by all efd log items.
+ */
+struct xfs_item_ops xfs_efd_item_ops = {
+ .iop_size = (uint(*)(xfs_log_item_t*))xfs_efd_item_size,
+ .iop_format = (void(*)(xfs_log_item_t*, xfs_log_iovec_t*))
+ xfs_efd_item_format,
+ .iop_pin = (void(*)(xfs_log_item_t*))xfs_efd_item_pin,
+ .iop_unpin = (void(*)(xfs_log_item_t*, int))xfs_efd_item_unpin,
+ .iop_unpin_remove = (void(*)(xfs_log_item_t*, xfs_trans_t*))
+ xfs_efd_item_unpin_remove,
+ .iop_trylock = (uint(*)(xfs_log_item_t*))xfs_efd_item_trylock,
+ .iop_unlock = (void(*)(xfs_log_item_t*))xfs_efd_item_unlock,
+ .iop_committed = (xfs_lsn_t(*)(xfs_log_item_t*, xfs_lsn_t))
+ xfs_efd_item_committed,
+ .iop_push = (void(*)(xfs_log_item_t*))xfs_efd_item_push,
+ .iop_abort = (void(*)(xfs_log_item_t*))xfs_efd_item_abort,
+ .iop_pushbuf = NULL,
+ .iop_committing = (void(*)(xfs_log_item_t*, xfs_lsn_t))
+ xfs_efd_item_committing
+};
+
+
+/*
+ * Allocate and initialize an efd item with the given number of extents.
+ */
+xfs_efd_log_item_t *
+xfs_efd_init(xfs_mount_t *mp,
+ xfs_efi_log_item_t *efip,
+ uint nextents)
+
+{
+ xfs_efd_log_item_t *efdp;
+ uint size;
+
+ ASSERT(nextents > 0);
+ if (nextents > XFS_EFD_MAX_FAST_EXTENTS) {
+ size = (uint)(sizeof(xfs_efd_log_item_t) +
+ ((nextents - 1) * sizeof(xfs_extent_t)));
+ efdp = (xfs_efd_log_item_t*)kmem_zalloc(size, KM_SLEEP);
+ } else {
+ efdp = (xfs_efd_log_item_t*)kmem_zone_zalloc(xfs_efd_zone,
+ KM_SLEEP);
+ }
+
+ efdp->efd_item.li_type = XFS_LI_EFD;
+ efdp->efd_item.li_ops = &xfs_efd_item_ops;
+ efdp->efd_item.li_mountp = mp;
+ efdp->efd_efip = efip;
+ efdp->efd_format.efd_nextents = nextents;
+ efdp->efd_format.efd_efi_id = efip->efi_format.efi_id;
+
+ return (efdp);
+}
diff --git a/fs/xfs/xfs_extfree_item.h b/fs/xfs/xfs_extfree_item.h
new file mode 100644
index 00000000000000..7122d6101d1580
--- /dev/null
+++ b/fs/xfs/xfs_extfree_item.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_EXTFREE_ITEM_H__
+#define __XFS_EXTFREE_ITEM_H__
+
+struct xfs_mount;
+struct kmem_zone;
+
+typedef struct xfs_extent {
+ xfs_dfsbno_t ext_start;
+ xfs_extlen_t ext_len;
+} xfs_extent_t;
+
+/*
+ * This is the structure used to lay out an efi log item in the
+ * log. The efi_extents field is a variable size array whose
+ * size is given by efi_nextents.
+ */
+typedef struct xfs_efi_log_format {
+ unsigned short efi_type; /* efi log item type */
+ unsigned short efi_size; /* size of this item */
+ uint efi_nextents; /* # extents to free */
+ __uint64_t efi_id; /* efi identifier */
+ xfs_extent_t efi_extents[1]; /* array of extents to free */
+} xfs_efi_log_format_t;
+
+/*
+ * This is the structure used to lay out an efd log item in the
+ * log. The efd_extents array is a variable size array whose
+ * size is given by efd_nextents;
+ */
+typedef struct xfs_efd_log_format {
+ unsigned short efd_type; /* efd log item type */
+ unsigned short efd_size; /* size of this item */
+ uint efd_nextents; /* # of extents freed */
+ __uint64_t efd_efi_id; /* id of corresponding efi */
+ xfs_extent_t efd_extents[1]; /* array of extents freed */
+} xfs_efd_log_format_t;
+
+
+#ifdef __KERNEL__
+
+/*
+ * Max number of extents in fast allocation path.
+ */
+#define XFS_EFI_MAX_FAST_EXTENTS 16
+
+/*
+ * Define EFI flags.
+ */
+#define XFS_EFI_RECOVERED 0x1
+#define XFS_EFI_COMMITTED 0x2
+#define XFS_EFI_CANCELED 0x4
+
+/*
+ * This is the "extent free intention" log item. It is used
+ * to log the fact that some extents need to be free. It is
+ * used in conjunction with the "extent free done" log item
+ * described below.
+ */
+typedef struct xfs_efi_log_item {
+ xfs_log_item_t efi_item;
+ uint efi_flags; /* misc flags */
+ uint efi_next_extent;
+ xfs_efi_log_format_t efi_format;
+} xfs_efi_log_item_t;
+
+/*
+ * This is the "extent free done" log item. It is used to log
+ * the fact that some extents earlier mentioned in an efi item
+ * have been freed.
+ */
+typedef struct xfs_efd_log_item {
+ xfs_log_item_t efd_item;
+ xfs_efi_log_item_t *efd_efip;
+ uint efd_next_extent;
+ xfs_efd_log_format_t efd_format;
+} xfs_efd_log_item_t;
+
+/*
+ * Max number of extents in fast allocation path.
+ */
+#define XFS_EFD_MAX_FAST_EXTENTS 16
+
+extern struct kmem_zone *xfs_efi_zone;
+extern struct kmem_zone *xfs_efd_zone;
+
+xfs_efi_log_item_t *xfs_efi_init(struct xfs_mount *, uint);
+xfs_efd_log_item_t *xfs_efd_init(struct xfs_mount *, xfs_efi_log_item_t *,
+ uint);
+
+#endif /* __KERNEL__ */
+
+#endif /* __XFS_EXTFREE_ITEM_H__ */
diff --git a/fs/xfs/xfs_fs.h b/fs/xfs/xfs_fs.h
new file mode 100644
index 00000000000000..6ee8443bf9d33d
--- /dev/null
+++ b/fs/xfs/xfs_fs.h
@@ -0,0 +1,527 @@
+/*
+ * Copyright (c) 1995-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2.1 of the GNU Lesser General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307,
+ * USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_FS_H__
+#define __XFS_FS_H__
+
+/*
+ * SGI's XFS filesystem's major stuff (constants, structures)
+ */
+
+#define XFS_NAME "xfs"
+
+/*
+ * Direct I/O attribute record used with XFS_IOC_DIOINFO
+ * d_miniosz is the min xfer size, xfer size multiple and file seek offset
+ * alignment.
+ */
+#ifndef HAVE_DIOATTR
+struct dioattr {
+ __u32 d_mem; /* data buffer memory alignment */
+ __u32 d_miniosz; /* min xfer size */
+ __u32 d_maxiosz; /* max xfer size */
+};
+#endif
+
+/*
+ * Structure for XFS_IOC_FSGETXATTR[A] and XFS_IOC_FSSETXATTR.
+ */
+#ifndef HAVE_FSXATTR
+struct fsxattr {
+ __u32 fsx_xflags; /* xflags field value (get/set) */
+ __u32 fsx_extsize; /* extsize field value (get/set)*/
+ __u32 fsx_nextents; /* nextents field value (get) */
+ unsigned char fsx_pad[16];
+};
+#endif
+
+/*
+ * Flags for the bs_xflags/fsx_xflags field
+ * There should be a one-to-one correspondence between these flags and the
+ * XFS_DIFLAG_s.
+ */
+#define XFS_XFLAG_REALTIME 0x00000001 /* data in realtime volume */
+#define XFS_XFLAG_PREALLOC 0x00000002 /* preallocated file extents */
+#define XFS_XFLAG_IMMUTABLE 0x00000008 /* file cannot be modified */
+#define XFS_XFLAG_APPEND 0x00000010 /* all writes append */
+#define XFS_XFLAG_SYNC 0x00000020 /* all writes synchronous */
+#define XFS_XFLAG_NOATIME 0x00000040 /* do not update access time */
+#define XFS_XFLAG_NODUMP 0x00000080 /* do not include in backups */
+#define XFS_XFLAG_RTINHERIT 0x00000100 /* create with rt bit set */
+#define XFS_XFLAG_PROJINHERIT 0x00000200 /* create with parents projid */
+#define XFS_XFLAG_NOSYMLINKS 0x00000400 /* disallow symlink creation */
+#define XFS_XFLAG_HASATTR 0x80000000 /* no DIFLAG for this */
+
+/*
+ * Structure for XFS_IOC_GETBMAP.
+ * On input, fill in bmv_offset and bmv_length of the first structure
+ * to indicate the area of interest in the file, and bmv_entry with the
+ * number of array elements given. The first structure is updated on
+ * return to give the offset and length for the next call.
+ */
+#ifndef HAVE_GETBMAP
+struct getbmap {
+ __s64 bmv_offset; /* file offset of segment in blocks */
+ __s64 bmv_block; /* starting block (64-bit daddr_t) */
+ __s64 bmv_length; /* length of segment, blocks */
+ __s32 bmv_count; /* # of entries in array incl. 1st */
+ __s32 bmv_entries; /* # of entries filled in (output) */
+};
+#endif
+
+/*
+ * Structure for XFS_IOC_GETBMAPX. Fields bmv_offset through bmv_entries
+ * are used exactly as in the getbmap structure. The getbmapx structure
+ * has additional bmv_iflags and bmv_oflags fields. The bmv_iflags field
+ * is only used for the first structure. It contains input flags
+ * specifying XFS_IOC_GETBMAPX actions. The bmv_oflags field is filled
+ * in by the XFS_IOC_GETBMAPX command for each returned structure after
+ * the first.
+ */
+#ifndef HAVE_GETBMAPX
+struct getbmapx {
+ __s64 bmv_offset; /* file offset of segment in blocks */
+ __s64 bmv_block; /* starting block (64-bit daddr_t) */
+ __s64 bmv_length; /* length of segment, blocks */
+ __s32 bmv_count; /* # of entries in array incl. 1st */
+ __s32 bmv_entries; /* # of entries filled in (output). */
+ __s32 bmv_iflags; /* input flags (1st structure) */
+ __s32 bmv_oflags; /* output flags (after 1st structure)*/
+ __s32 bmv_unused1; /* future use */
+ __s32 bmv_unused2; /* future use */
+};
+#endif
+
+/* bmv_iflags values - set by XFS_IOC_GETBMAPX caller. */
+#define BMV_IF_ATTRFORK 0x1 /* return attr fork rather than data */
+#define BMV_IF_NO_DMAPI_READ 0x2 /* Do not generate DMAPI read event */
+#define BMV_IF_PREALLOC 0x4 /* rtn status BMV_OF_PREALLOC if req */
+#define BMV_IF_VALID (BMV_IF_ATTRFORK|BMV_IF_NO_DMAPI_READ|BMV_IF_PREALLOC)
+#ifdef __KERNEL__
+#define BMV_IF_EXTENDED 0x40000000 /* getpmapx if set */
+#endif
+
+/* bmv_oflags values - returned for for each non-header segment */
+#define BMV_OF_PREALLOC 0x1 /* segment = unwritten pre-allocation */
+
+/* Convert getbmap <-> getbmapx - move fields from p1 to p2. */
+#define GETBMAP_CONVERT(p1,p2) { \
+ p2.bmv_offset = p1.bmv_offset; \
+ p2.bmv_block = p1.bmv_block; \
+ p2.bmv_length = p1.bmv_length; \
+ p2.bmv_count = p1.bmv_count; \
+ p2.bmv_entries = p1.bmv_entries; }
+
+
+/*
+ * Structure for XFS_IOC_FSSETDM.
+ * For use by backup and restore programs to set the XFS on-disk inode
+ * fields di_dmevmask and di_dmstate. These must be set to exactly and
+ * only values previously obtained via xfs_bulkstat! (Specifically the
+ * xfs_bstat_t fields bs_dmevmask and bs_dmstate.)
+ */
+#ifndef HAVE_FSDMIDATA
+struct fsdmidata {
+ __u32 fsd_dmevmask; /* corresponds to di_dmevmask */
+ __u16 fsd_padding;
+ __u16 fsd_dmstate; /* corresponds to di_dmstate */
+};
+#endif
+
+/*
+ * File segment locking set data type for 64 bit access.
+ * Also used for all the RESV/FREE interfaces.
+ */
+typedef struct xfs_flock64 {
+ __s16 l_type;
+ __s16 l_whence;
+ __s64 l_start;
+ __s64 l_len; /* len == 0 means until end of file */
+ __s32 l_sysid;
+ __u32 l_pid;
+ __s32 l_pad[4]; /* reserve area */
+} xfs_flock64_t;
+
+/*
+ * Output for XFS_IOC_FSGEOMETRY_V1
+ */
+typedef struct xfs_fsop_geom_v1 {
+ __u32 blocksize; /* filesystem (data) block size */
+ __u32 rtextsize; /* realtime extent size */
+ __u32 agblocks; /* fsblocks in an AG */
+ __u32 agcount; /* number of allocation groups */
+ __u32 logblocks; /* fsblocks in the log */
+ __u32 sectsize; /* (data) sector size, bytes */
+ __u32 inodesize; /* inode size in bytes */
+ __u32 imaxpct; /* max allowed inode space(%) */
+ __u64 datablocks; /* fsblocks in data subvolume */
+ __u64 rtblocks; /* fsblocks in realtime subvol */
+ __u64 rtextents; /* rt extents in realtime subvol*/
+ __u64 logstart; /* starting fsblock of the log */
+ unsigned char uuid[16]; /* unique id of the filesystem */
+ __u32 sunit; /* stripe unit, fsblocks */
+ __u32 swidth; /* stripe width, fsblocks */
+ __s32 version; /* structure version */
+ __u32 flags; /* superblock version flags */
+ __u32 logsectsize; /* log sector size, bytes */
+ __u32 rtsectsize; /* realtime sector size, bytes */
+ __u32 dirblocksize; /* directory block size, bytes */
+} xfs_fsop_geom_v1_t;
+
+/*
+ * Output for XFS_IOC_FSGEOMETRY
+ */
+typedef struct xfs_fsop_geom {
+ __u32 blocksize; /* filesystem (data) block size */
+ __u32 rtextsize; /* realtime extent size */
+ __u32 agblocks; /* fsblocks in an AG */
+ __u32 agcount; /* number of allocation groups */
+ __u32 logblocks; /* fsblocks in the log */
+ __u32 sectsize; /* (data) sector size, bytes */
+ __u32 inodesize; /* inode size in bytes */
+ __u32 imaxpct; /* max allowed inode space(%) */
+ __u64 datablocks; /* fsblocks in data subvolume */
+ __u64 rtblocks; /* fsblocks in realtime subvol */
+ __u64 rtextents; /* rt extents in realtime subvol*/
+ __u64 logstart; /* starting fsblock of the log */
+ unsigned char uuid[16]; /* unique id of the filesystem */
+ __u32 sunit; /* stripe unit, fsblocks */
+ __u32 swidth; /* stripe width, fsblocks */
+ __s32 version; /* structure version */
+ __u32 flags; /* superblock version flags */
+ __u32 logsectsize; /* log sector size, bytes */
+ __u32 rtsectsize; /* realtime sector size, bytes */
+ __u32 dirblocksize; /* directory block size, bytes */
+ __u32 logsunit; /* log stripe unit, bytes */
+} xfs_fsop_geom_t;
+
+/* Output for XFS_FS_COUNTS */
+typedef struct xfs_fsop_counts {
+ __u64 freedata; /* free data section blocks */
+ __u64 freertx; /* free rt extents */
+ __u64 freeino; /* free inodes */
+ __u64 allocino; /* total allocated inodes */
+} xfs_fsop_counts_t;
+
+/* Input/Output for XFS_GET_RESBLKS and XFS_SET_RESBLKS */
+typedef struct xfs_fsop_resblks {
+ __u64 resblks;
+ __u64 resblks_avail;
+} xfs_fsop_resblks_t;
+
+#define XFS_FSOP_GEOM_VERSION 0
+
+#define XFS_FSOP_GEOM_FLAGS_ATTR 0x0001 /* attributes in use */
+#define XFS_FSOP_GEOM_FLAGS_NLINK 0x0002 /* 32-bit nlink values */
+#define XFS_FSOP_GEOM_FLAGS_QUOTA 0x0004 /* quotas enabled */
+#define XFS_FSOP_GEOM_FLAGS_IALIGN 0x0008 /* inode alignment */
+#define XFS_FSOP_GEOM_FLAGS_DALIGN 0x0010 /* large data alignment */
+#define XFS_FSOP_GEOM_FLAGS_SHARED 0x0020 /* read-only shared */
+#define XFS_FSOP_GEOM_FLAGS_EXTFLG 0x0040 /* special extent flag */
+#define XFS_FSOP_GEOM_FLAGS_DIRV2 0x0080 /* directory version 2 */
+#define XFS_FSOP_GEOM_FLAGS_LOGV2 0x0100 /* log format version 2 */
+#define XFS_FSOP_GEOM_FLAGS_SECTOR 0x0200 /* sector sizes >1BB */
+
+
+/*
+ * Minimum and maximum sizes need for growth checks
+ */
+#define XFS_MIN_AG_BLOCKS 64
+#define XFS_MIN_LOG_BLOCKS 512
+#define XFS_MAX_LOG_BLOCKS (64 * 1024)
+#define XFS_MIN_LOG_BYTES (256 * 1024)
+#define XFS_MAX_LOG_BYTES (128 * 1024 * 1024)
+
+/*
+ * Structures for XFS_IOC_FSGROWFSDATA, XFS_IOC_FSGROWFSLOG & XFS_IOC_FSGROWFSRT
+ */
+typedef struct xfs_growfs_data {
+ __u64 newblocks; /* new data subvol size, fsblocks */
+ __u32 imaxpct; /* new inode space percentage limit */
+} xfs_growfs_data_t;
+
+typedef struct xfs_growfs_log {
+ __u32 newblocks; /* new log size, fsblocks */
+ __u32 isint; /* 1 if new log is internal */
+} xfs_growfs_log_t;
+
+typedef struct xfs_growfs_rt {
+ __u64 newblocks; /* new realtime size, fsblocks */
+ __u32 extsize; /* new realtime extent size, fsblocks */
+} xfs_growfs_rt_t;
+
+
+/*
+ * Structures returned from ioctl XFS_IOC_FSBULKSTAT & XFS_IOC_FSBULKSTAT_SINGLE
+ */
+typedef struct xfs_bstime {
+ time_t tv_sec; /* seconds */
+ __s32 tv_nsec; /* and nanoseconds */
+} xfs_bstime_t;
+
+typedef struct xfs_bstat {
+ __u64 bs_ino; /* inode number */
+ __u16 bs_mode; /* type and mode */
+ __u16 bs_nlink; /* number of links */
+ __u32 bs_uid; /* user id */
+ __u32 bs_gid; /* group id */
+ __u32 bs_rdev; /* device value */
+ __s32 bs_blksize; /* block size */
+ __s64 bs_size; /* file size */
+ xfs_bstime_t bs_atime; /* access time */
+ xfs_bstime_t bs_mtime; /* modify time */
+ xfs_bstime_t bs_ctime; /* inode change time */
+ int64_t bs_blocks; /* number of blocks */
+ __u32 bs_xflags; /* extended flags */
+ __s32 bs_extsize; /* extent size */
+ __s32 bs_extents; /* number of extents */
+ __u32 bs_gen; /* generation count */
+ __u16 bs_projid; /* project id */
+ unsigned char bs_pad[14]; /* pad space, unused */
+ __u32 bs_dmevmask; /* DMIG event mask */
+ __u16 bs_dmstate; /* DMIG state info */
+ __u16 bs_aextents; /* attribute number of extents */
+} xfs_bstat_t;
+
+/*
+ * The user-level BulkStat Request interface structure.
+ */
+typedef struct xfs_fsop_bulkreq {
+ __u64 __user *lastip; /* last inode # pointer */
+ __s32 icount; /* count of entries in buffer */
+ void __user *ubuffer;/* user buffer for inode desc. */
+ __s32 __user *ocount; /* output count pointer */
+} xfs_fsop_bulkreq_t;
+
+
+/*
+ * Structures returned from xfs_inumbers routine (XFS_IOC_FSINUMBERS).
+ */
+typedef struct xfs_inogrp {
+ __u64 xi_startino; /* starting inode number */
+ __s32 xi_alloccount; /* # bits set in allocmask */
+ __u64 xi_allocmask; /* mask of allocated inodes */
+} xfs_inogrp_t;
+
+
+/*
+ * Error injection.
+ */
+typedef struct xfs_error_injection {
+ __s32 fd;
+ __s32 errtag;
+} xfs_error_injection_t;
+
+
+/*
+ * The user-level Handle Request interface structure.
+ */
+typedef struct xfs_fsop_handlereq {
+ __u32 fd; /* fd for FD_TO_HANDLE */
+ void __user *path; /* user pathname */
+ __u32 oflags; /* open flags */
+ void __user *ihandle;/* user supplied handle */
+ __u32 ihandlen; /* user supplied length */
+ void __user *ohandle;/* user buffer for handle */
+ __u32 __user *ohandlen;/* user buffer length */
+} xfs_fsop_handlereq_t;
+
+/*
+ * Compound structures for passing args through Handle Request interfaces
+ * xfs_fssetdm_by_handle, xfs_attrlist_by_handle, xfs_attrmulti_by_handle
+ * - ioctls: XFS_IOC_FSSETDM_BY_HANDLE, XFS_IOC_ATTRLIST_BY_HANDLE, and
+ * XFS_IOC_ATTRMULTI_BY_HANDLE
+ */
+
+typedef struct xfs_fsop_setdm_handlereq {
+ struct xfs_fsop_handlereq hreq; /* handle information */
+ struct fsdmidata __user *data; /* DMAPI data */
+} xfs_fsop_setdm_handlereq_t;
+
+typedef struct xfs_attrlist_cursor {
+ __u32 opaque[4];
+} xfs_attrlist_cursor_t;
+
+typedef struct xfs_fsop_attrlist_handlereq {
+ struct xfs_fsop_handlereq hreq; /* handle interface structure */
+ struct xfs_attrlist_cursor pos; /* opaque cookie, list offset */
+ __u32 flags; /* which namespace to use */
+ __u32 buflen; /* length of buffer supplied */
+ void __user *buffer; /* returned names */
+} xfs_fsop_attrlist_handlereq_t;
+
+typedef struct xfs_attr_multiop {
+ __u32 am_opcode;
+ __s32 am_error;
+ void __user *am_attrname;
+ void __user *am_attrvalue;
+ __u32 am_length;
+ __u32 am_flags;
+} xfs_attr_multiop_t;
+
+typedef struct xfs_fsop_attrmulti_handlereq {
+ struct xfs_fsop_handlereq hreq; /* handle interface structure */
+ __u32 opcount;/* count of following multiop */
+ struct xfs_attr_multiop __user *ops; /* attr_multi data */
+} xfs_fsop_attrmulti_handlereq_t;
+
+/*
+ * per machine unique filesystem identifier types.
+ */
+typedef struct { __u32 val[2]; } xfs_fsid_t; /* file system id type */
+
+
+#ifndef HAVE_FID
+#define MAXFIDSZ 46
+
+typedef struct fid {
+ __u16 fid_len; /* length of data in bytes */
+ unsigned char fid_data[MAXFIDSZ]; /* data (fid_len worth) */
+} fid_t;
+#endif
+
+typedef struct xfs_fid {
+ __u16 xfs_fid_len; /* length of remainder */
+ __u16 xfs_fid_pad;
+ __u32 xfs_fid_gen; /* generation number */
+ __u64 xfs_fid_ino; /* 64 bits inode number */
+} xfs_fid_t;
+
+typedef struct xfs_fid2 {
+ __u16 fid_len; /* length of remainder */
+ __u16 fid_pad; /* padding, must be zero */
+ __u32 fid_gen; /* generation number */
+ __u64 fid_ino; /* inode number */
+} xfs_fid2_t;
+
+typedef struct xfs_handle {
+ union {
+ __s64 align; /* force alignment of ha_fid */
+ xfs_fsid_t _ha_fsid; /* unique file system identifier */
+ } ha_u;
+ xfs_fid_t ha_fid; /* file system specific file ID */
+} xfs_handle_t;
+#define ha_fsid ha_u._ha_fsid
+
+#define XFS_HSIZE(handle) (((char *) &(handle).ha_fid.xfs_fid_pad \
+ - (char *) &(handle)) \
+ + (handle).ha_fid.xfs_fid_len)
+
+#define XFS_HANDLE_CMP(h1, h2) memcmp(h1, h2, sizeof(xfs_handle_t))
+
+#define FSHSIZE sizeof(fsid_t)
+
+/*
+ * Flags for going down operation
+ */
+#define XFS_FSOP_GOING_FLAGS_DEFAULT 0x0 /* going down */
+#define XFS_FSOP_GOING_FLAGS_LOGFLUSH 0x1 /* flush log but not data */
+#define XFS_FSOP_GOING_FLAGS_NOLOGFLUSH 0x2 /* don't flush log nor data */
+
+/*
+ * ioctl commands that are used by Linux filesystems
+ */
+#define XFS_IOC_GETXFLAGS _IOR('f', 1, long)
+#define XFS_IOC_SETXFLAGS _IOW('f', 2, long)
+#define XFS_IOC_GETVERSION _IOR('v', 1, long)
+
+/*
+ * ioctl commands that replace IRIX fcntl()'s
+ * For 'documentation' purposed more than anything else,
+ * the "cmd #" field reflects the IRIX fcntl number.
+ */
+#define XFS_IOC_ALLOCSP _IOW ('X', 10, struct xfs_flock64)
+#define XFS_IOC_FREESP _IOW ('X', 11, struct xfs_flock64)
+#define XFS_IOC_DIOINFO _IOR ('X', 30, struct dioattr)
+#define XFS_IOC_FSGETXATTR _IOR ('X', 31, struct fsxattr)
+#define XFS_IOC_FSSETXATTR _IOW ('X', 32, struct fsxattr)
+#define XFS_IOC_ALLOCSP64 _IOW ('X', 36, struct xfs_flock64)
+#define XFS_IOC_FREESP64 _IOW ('X', 37, struct xfs_flock64)
+#define XFS_IOC_GETBMAP _IOWR('X', 38, struct getbmap)
+#define XFS_IOC_FSSETDM _IOW ('X', 39, struct fsdmidata)
+#define XFS_IOC_RESVSP _IOW ('X', 40, struct xfs_flock64)
+#define XFS_IOC_UNRESVSP _IOW ('X', 41, struct xfs_flock64)
+#define XFS_IOC_RESVSP64 _IOW ('X', 42, struct xfs_flock64)
+#define XFS_IOC_UNRESVSP64 _IOW ('X', 43, struct xfs_flock64)
+#define XFS_IOC_GETBMAPA _IOWR('X', 44, struct getbmap)
+#define XFS_IOC_FSGETXATTRA _IOR ('X', 45, struct fsxattr)
+/* XFS_IOC_SETBIOSIZE ---- deprecated 46 */
+/* XFS_IOC_GETBIOSIZE ---- deprecated 47 */
+#define XFS_IOC_GETBMAPX _IOWR('X', 56, struct getbmap)
+
+/*
+ * ioctl commands that replace IRIX syssgi()'s
+ */
+#define XFS_IOC_FSGEOMETRY_V1 _IOR ('X', 100, struct xfs_fsop_geom_v1)
+#define XFS_IOC_FSBULKSTAT _IOWR('X', 101, struct xfs_fsop_bulkreq)
+#define XFS_IOC_FSBULKSTAT_SINGLE _IOWR('X', 102, struct xfs_fsop_bulkreq)
+#define XFS_IOC_FSINUMBERS _IOWR('X', 103, struct xfs_fsop_bulkreq)
+#define XFS_IOC_PATH_TO_FSHANDLE _IOWR('X', 104, struct xfs_fsop_handlereq)
+#define XFS_IOC_PATH_TO_HANDLE _IOWR('X', 105, struct xfs_fsop_handlereq)
+#define XFS_IOC_FD_TO_HANDLE _IOWR('X', 106, struct xfs_fsop_handlereq)
+#define XFS_IOC_OPEN_BY_HANDLE _IOWR('X', 107, struct xfs_fsop_handlereq)
+#define XFS_IOC_READLINK_BY_HANDLE _IOWR('X', 108, struct xfs_fsop_handlereq)
+#define XFS_IOC_SWAPEXT _IOWR('X', 109, struct xfs_swapext)
+#define XFS_IOC_FSGROWFSDATA _IOW ('X', 110, struct xfs_growfs_data)
+#define XFS_IOC_FSGROWFSLOG _IOW ('X', 111, struct xfs_growfs_log)
+#define XFS_IOC_FSGROWFSRT _IOW ('X', 112, struct xfs_growfs_rt)
+#define XFS_IOC_FSCOUNTS _IOR ('X', 113, struct xfs_fsop_counts)
+#define XFS_IOC_SET_RESBLKS _IOWR('X', 114, struct xfs_fsop_resblks)
+#define XFS_IOC_GET_RESBLKS _IOR ('X', 115, struct xfs_fsop_resblks)
+#define XFS_IOC_ERROR_INJECTION _IOW ('X', 116, struct xfs_error_injection)
+#define XFS_IOC_ERROR_CLEARALL _IOW ('X', 117, struct xfs_error_injection)
+/* XFS_IOC_ATTRCTL_BY_HANDLE -- deprecated 118 */
+#define XFS_IOC_FREEZE _IOWR('X', 119, int)
+#define XFS_IOC_THAW _IOWR('X', 120, int)
+#define XFS_IOC_FSSETDM_BY_HANDLE _IOW ('X', 121, struct xfs_fsop_setdm_handlereq)
+#define XFS_IOC_ATTRLIST_BY_HANDLE _IOW ('X', 122, struct xfs_fsop_attrlist_handlereq)
+#define XFS_IOC_ATTRMULTI_BY_HANDLE _IOW ('X', 123, struct xfs_fsop_attrmulti_handlereq)
+#define XFS_IOC_FSGEOMETRY _IOR ('X', 124, struct xfs_fsop_geom)
+#define XFS_IOC_GOINGDOWN _IOR ('X', 125, __uint32_t)
+/* XFS_IOC_GETFSUUID ---------- deprecated 140 */
+
+
+#ifndef HAVE_BBMACROS
+/*
+ * Block I/O parameterization. A basic block (BB) is the lowest size of
+ * filesystem allocation, and must equal 512. Length units given to bio
+ * routines are in BB's.
+ */
+#define BBSHIFT 9
+#define BBSIZE (1<<BBSHIFT)
+#define BBMASK (BBSIZE-1)
+#define BTOBB(bytes) (((__u64)(bytes) + BBSIZE - 1) >> BBSHIFT)
+#define BTOBBT(bytes) ((__u64)(bytes) >> BBSHIFT)
+#define BBTOB(bbs) ((bbs) << BBSHIFT)
+#endif
+
+#endif /* __XFS_FS_H__ */
diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
new file mode 100644
index 00000000000000..ebc2f27ce9e1b0
--- /dev/null
+++ b/fs/xfs/xfs_fsops.c
@@ -0,0 +1,616 @@
+/*
+ * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_dir.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_ag.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_btree.h"
+#include "xfs_error.h"
+#include "xfs_alloc.h"
+#include "xfs_ialloc.h"
+#include "xfs_fsops.h"
+#include "xfs_itable.h"
+#include "xfs_rw.h"
+#include "xfs_refcache.h"
+#include "xfs_trans_space.h"
+#include "xfs_rtalloc.h"
+#include "xfs_dir2.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode.h"
+#include "xfs_inode_item.h"
+
+/*
+ * File system operations
+ */
+
+int
+xfs_fs_geometry(
+ xfs_mount_t *mp,
+ xfs_fsop_geom_t *geo,
+ int new_version)
+{
+ geo->blocksize = mp->m_sb.sb_blocksize;
+ geo->rtextsize = mp->m_sb.sb_rextsize;
+ geo->agblocks = mp->m_sb.sb_agblocks;
+ geo->agcount = mp->m_sb.sb_agcount;
+ geo->logblocks = mp->m_sb.sb_logblocks;
+ geo->sectsize = mp->m_sb.sb_sectsize;
+ geo->inodesize = mp->m_sb.sb_inodesize;
+ geo->imaxpct = mp->m_sb.sb_imax_pct;
+ geo->datablocks = mp->m_sb.sb_dblocks;
+ geo->rtblocks = mp->m_sb.sb_rblocks;
+ geo->rtextents = mp->m_sb.sb_rextents;
+ geo->logstart = mp->m_sb.sb_logstart;
+ ASSERT(sizeof(geo->uuid)==sizeof(mp->m_sb.sb_uuid));
+ memcpy(geo->uuid, &mp->m_sb.sb_uuid, sizeof(mp->m_sb.sb_uuid));
+ if (new_version >= 2) {
+ geo->sunit = mp->m_sb.sb_unit;
+ geo->swidth = mp->m_sb.sb_width;
+ }
+ if (new_version >= 3) {
+ geo->version = XFS_FSOP_GEOM_VERSION;
+ geo->flags =
+ (XFS_SB_VERSION_HASATTR(&mp->m_sb) ?
+ XFS_FSOP_GEOM_FLAGS_ATTR : 0) |
+ (XFS_SB_VERSION_HASNLINK(&mp->m_sb) ?
+ XFS_FSOP_GEOM_FLAGS_NLINK : 0) |
+ (XFS_SB_VERSION_HASQUOTA(&mp->m_sb) ?
+ XFS_FSOP_GEOM_FLAGS_QUOTA : 0) |
+ (XFS_SB_VERSION_HASALIGN(&mp->m_sb) ?
+ XFS_FSOP_GEOM_FLAGS_IALIGN : 0) |
+ (XFS_SB_VERSION_HASDALIGN(&mp->m_sb) ?
+ XFS_FSOP_GEOM_FLAGS_DALIGN : 0) |
+ (XFS_SB_VERSION_HASSHARED(&mp->m_sb) ?
+ XFS_FSOP_GEOM_FLAGS_SHARED : 0) |
+ (XFS_SB_VERSION_HASEXTFLGBIT(&mp->m_sb) ?
+ XFS_FSOP_GEOM_FLAGS_EXTFLG : 0) |
+ (XFS_SB_VERSION_HASDIRV2(&mp->m_sb) ?
+ XFS_FSOP_GEOM_FLAGS_DIRV2 : 0) |
+ (XFS_SB_VERSION_HASSECTOR(&mp->m_sb) ?
+ XFS_FSOP_GEOM_FLAGS_SECTOR : 0);
+ geo->logsectsize = XFS_SB_VERSION_HASSECTOR(&mp->m_sb) ?
+ mp->m_sb.sb_logsectsize : BBSIZE;
+ geo->rtsectsize = mp->m_sb.sb_blocksize;
+ geo->dirblocksize = mp->m_dirblksize;
+ }
+ if (new_version >= 4) {
+ geo->flags |=
+ (XFS_SB_VERSION_HASLOGV2(&mp->m_sb) ?
+ XFS_FSOP_GEOM_FLAGS_LOGV2 : 0);
+ geo->logsunit = mp->m_sb.sb_logsunit;
+ }
+ return 0;
+}
+
+static int
+xfs_growfs_data_private(
+ xfs_mount_t *mp, /* mount point for filesystem */
+ xfs_growfs_data_t *in) /* growfs data input struct */
+{
+ xfs_agf_t *agf;
+ xfs_agi_t *agi;
+ xfs_agnumber_t agno;
+ xfs_extlen_t agsize;
+ xfs_extlen_t tmpsize;
+ xfs_alloc_rec_t *arec;
+ xfs_btree_sblock_t *block;
+ xfs_buf_t *bp;
+ int bucket;
+ int dpct;
+ int error;
+ xfs_agnumber_t nagcount;
+ xfs_agnumber_t nagimax = 0;
+ xfs_rfsblock_t nb, nb_mod;
+ xfs_rfsblock_t new;
+ xfs_rfsblock_t nfree;
+ xfs_agnumber_t oagcount;
+ int pct;
+ xfs_sb_t *sbp;
+ xfs_trans_t *tp;
+
+ nb = in->newblocks;
+ pct = in->imaxpct;
+ if (nb < mp->m_sb.sb_dblocks || pct < 0 || pct > 100)
+ return XFS_ERROR(EINVAL);
+ dpct = pct - mp->m_sb.sb_imax_pct;
+ error = xfs_read_buf(mp, mp->m_ddev_targp,
+ XFS_FSB_TO_BB(mp, nb) - XFS_FSS_TO_BB(mp, 1),
+ XFS_FSS_TO_BB(mp, 1), 0, &bp);
+ if (error)
+ return error;
+ ASSERT(bp);
+ xfs_buf_relse(bp);
+
+ new = nb; /* use new as a temporary here */
+ nb_mod = do_div(new, mp->m_sb.sb_agblocks);
+ nagcount = new + (nb_mod != 0);
+ if (nb_mod && nb_mod < XFS_MIN_AG_BLOCKS) {
+ nagcount--;
+ nb = nagcount * mp->m_sb.sb_agblocks;
+ if (nb < mp->m_sb.sb_dblocks)
+ return XFS_ERROR(EINVAL);
+ }
+ new = nb - mp->m_sb.sb_dblocks;
+ oagcount = mp->m_sb.sb_agcount;
+ if (nagcount > oagcount) {
+ down_write(&mp->m_peraglock);
+ mp->m_perag = kmem_realloc(mp->m_perag,
+ sizeof(xfs_perag_t) * nagcount,
+ sizeof(xfs_perag_t) * oagcount,
+ KM_SLEEP);
+ memset(&mp->m_perag[oagcount], 0,
+ (nagcount - oagcount) * sizeof(xfs_perag_t));
+ mp->m_flags |= XFS_MOUNT_32BITINODES;
+ nagimax = xfs_initialize_perag(mp, nagcount);
+ up_write(&mp->m_peraglock);
+ }
+ tp = xfs_trans_alloc(mp, XFS_TRANS_GROWFS);
+ if ((error = xfs_trans_reserve(tp, XFS_GROWFS_SPACE_RES(mp),
+ XFS_GROWDATA_LOG_RES(mp), 0, 0, 0))) {
+ xfs_trans_cancel(tp, 0);
+ return error;
+ }
+
+ nfree = 0;
+ for (agno = nagcount - 1; agno >= oagcount; agno--, new -= agsize) {
+ /*
+ * AG freelist header block
+ */
+ bp = xfs_buf_get(mp->m_ddev_targp,
+ XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)),
+ XFS_FSS_TO_BB(mp, 1), 0);
+ agf = XFS_BUF_TO_AGF(bp);
+ memset(agf, 0, mp->m_sb.sb_sectsize);
+ INT_SET(agf->agf_magicnum, ARCH_CONVERT, XFS_AGF_MAGIC);
+ INT_SET(agf->agf_versionnum, ARCH_CONVERT, XFS_AGF_VERSION);
+ INT_SET(agf->agf_seqno, ARCH_CONVERT, agno);
+ if (agno == nagcount - 1)
+ agsize =
+ nb -
+ (agno * (xfs_rfsblock_t)mp->m_sb.sb_agblocks);
+ else
+ agsize = mp->m_sb.sb_agblocks;
+ INT_SET(agf->agf_length, ARCH_CONVERT, agsize);
+ INT_SET(agf->agf_roots[XFS_BTNUM_BNOi], ARCH_CONVERT,
+ XFS_BNO_BLOCK(mp));
+ INT_SET(agf->agf_roots[XFS_BTNUM_CNTi], ARCH_CONVERT,
+ XFS_CNT_BLOCK(mp));
+ INT_SET(agf->agf_levels[XFS_BTNUM_BNOi], ARCH_CONVERT, 1);
+ INT_SET(agf->agf_levels[XFS_BTNUM_CNTi], ARCH_CONVERT, 1);
+ INT_ZERO(agf->agf_flfirst, ARCH_CONVERT);
+ INT_SET(agf->agf_fllast, ARCH_CONVERT, XFS_AGFL_SIZE(mp) - 1);
+ INT_ZERO(agf->agf_flcount, ARCH_CONVERT);
+ tmpsize = agsize - XFS_PREALLOC_BLOCKS(mp);
+ INT_SET(agf->agf_freeblks, ARCH_CONVERT, tmpsize);
+ INT_SET(agf->agf_longest, ARCH_CONVERT, tmpsize);
+ error = xfs_bwrite(mp, bp);
+ if (error) {
+ goto error0;
+ }
+ /*
+ * AG inode header block
+ */
+ bp = xfs_buf_get(mp->m_ddev_targp,
+ XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)),
+ XFS_FSS_TO_BB(mp, 1), 0);
+ agi = XFS_BUF_TO_AGI(bp);
+ memset(agi, 0, mp->m_sb.sb_sectsize);
+ INT_SET(agi->agi_magicnum, ARCH_CONVERT, XFS_AGI_MAGIC);
+ INT_SET(agi->agi_versionnum, ARCH_CONVERT, XFS_AGI_VERSION);
+ INT_SET(agi->agi_seqno, ARCH_CONVERT, agno);
+ INT_SET(agi->agi_length, ARCH_CONVERT, agsize);
+ INT_ZERO(agi->agi_count, ARCH_CONVERT);
+ INT_SET(agi->agi_root, ARCH_CONVERT, XFS_IBT_BLOCK(mp));
+ INT_SET(agi->agi_level, ARCH_CONVERT, 1);
+ INT_ZERO(agi->agi_freecount, ARCH_CONVERT);
+ INT_SET(agi->agi_newino, ARCH_CONVERT, NULLAGINO);
+ INT_SET(agi->agi_dirino, ARCH_CONVERT, NULLAGINO);
+ for (bucket = 0; bucket < XFS_AGI_UNLINKED_BUCKETS; bucket++)
+ INT_SET(agi->agi_unlinked[bucket], ARCH_CONVERT,
+ NULLAGINO);
+ error = xfs_bwrite(mp, bp);
+ if (error) {
+ goto error0;
+ }
+ /*
+ * BNO btree root block
+ */
+ bp = xfs_buf_get(mp->m_ddev_targp,
+ XFS_AGB_TO_DADDR(mp, agno, XFS_BNO_BLOCK(mp)),
+ BTOBB(mp->m_sb.sb_blocksize), 0);
+ block = XFS_BUF_TO_SBLOCK(bp);
+ memset(block, 0, mp->m_sb.sb_blocksize);
+ INT_SET(block->bb_magic, ARCH_CONVERT, XFS_ABTB_MAGIC);
+ INT_ZERO(block->bb_level, ARCH_CONVERT);
+ INT_SET(block->bb_numrecs, ARCH_CONVERT, 1);
+ INT_SET(block->bb_leftsib, ARCH_CONVERT, NULLAGBLOCK);
+ INT_SET(block->bb_rightsib, ARCH_CONVERT, NULLAGBLOCK);
+ arec = XFS_BTREE_REC_ADDR(mp->m_sb.sb_blocksize, xfs_alloc,
+ block, 1, mp->m_alloc_mxr[0]);
+ INT_SET(arec->ar_startblock, ARCH_CONVERT,
+ XFS_PREALLOC_BLOCKS(mp));
+ INT_SET(arec->ar_blockcount, ARCH_CONVERT,
+ agsize - INT_GET(arec->ar_startblock, ARCH_CONVERT));
+ error = xfs_bwrite(mp, bp);
+ if (error) {
+ goto error0;
+ }
+ /*
+ * CNT btree root block
+ */
+ bp = xfs_buf_get(mp->m_ddev_targp,
+ XFS_AGB_TO_DADDR(mp, agno, XFS_CNT_BLOCK(mp)),
+ BTOBB(mp->m_sb.sb_blocksize), 0);
+ block = XFS_BUF_TO_SBLOCK(bp);
+ memset(block, 0, mp->m_sb.sb_blocksize);
+ INT_SET(block->bb_magic, ARCH_CONVERT, XFS_ABTC_MAGIC);
+ INT_ZERO(block->bb_level, ARCH_CONVERT);
+ INT_SET(block->bb_numrecs, ARCH_CONVERT, 1);
+ INT_SET(block->bb_leftsib, ARCH_CONVERT, NULLAGBLOCK);
+ INT_SET(block->bb_rightsib, ARCH_CONVERT, NULLAGBLOCK);
+ arec = XFS_BTREE_REC_ADDR(mp->m_sb.sb_blocksize, xfs_alloc,
+ block, 1, mp->m_alloc_mxr[0]);
+ INT_SET(arec->ar_startblock, ARCH_CONVERT,
+ XFS_PREALLOC_BLOCKS(mp));
+ INT_SET(arec->ar_blockcount, ARCH_CONVERT,
+ agsize - INT_GET(arec->ar_startblock, ARCH_CONVERT));
+ nfree += INT_GET(arec->ar_blockcount, ARCH_CONVERT);
+ error = xfs_bwrite(mp, bp);
+ if (error) {
+ goto error0;
+ }
+ /*
+ * INO btree root block
+ */
+ bp = xfs_buf_get(mp->m_ddev_targp,
+ XFS_AGB_TO_DADDR(mp, agno, XFS_IBT_BLOCK(mp)),
+ BTOBB(mp->m_sb.sb_blocksize), 0);
+ block = XFS_BUF_TO_SBLOCK(bp);
+ memset(block, 0, mp->m_sb.sb_blocksize);
+ INT_SET(block->bb_magic, ARCH_CONVERT, XFS_IBT_MAGIC);
+ INT_ZERO(block->bb_level, ARCH_CONVERT);
+ INT_ZERO(block->bb_numrecs, ARCH_CONVERT);
+ INT_SET(block->bb_leftsib, ARCH_CONVERT, NULLAGBLOCK);
+ INT_SET(block->bb_rightsib, ARCH_CONVERT, NULLAGBLOCK);
+ error = xfs_bwrite(mp, bp);
+ if (error) {
+ goto error0;
+ }
+ }
+ xfs_trans_agblocks_delta(tp, nfree);
+ /*
+ * There are new blocks in the old last a.g.
+ */
+ if (new) {
+ /*
+ * Change the agi length.
+ */
+ error = xfs_ialloc_read_agi(mp, tp, agno, &bp);
+ if (error) {
+ goto error0;
+ }
+ ASSERT(bp);
+ agi = XFS_BUF_TO_AGI(bp);
+ INT_MOD(agi->agi_length, ARCH_CONVERT, new);
+ ASSERT(nagcount == oagcount ||
+ INT_GET(agi->agi_length, ARCH_CONVERT) ==
+ mp->m_sb.sb_agblocks);
+ xfs_ialloc_log_agi(tp, bp, XFS_AGI_LENGTH);
+ /*
+ * Change agf length.
+ */
+ error = xfs_alloc_read_agf(mp, tp, agno, 0, &bp);
+ if (error) {
+ goto error0;
+ }
+ ASSERT(bp);
+ agf = XFS_BUF_TO_AGF(bp);
+ INT_MOD(agf->agf_length, ARCH_CONVERT, new);
+ ASSERT(INT_GET(agf->agf_length, ARCH_CONVERT) ==
+ INT_GET(agi->agi_length, ARCH_CONVERT));
+ /*
+ * Free the new space.
+ */
+ error = xfs_free_extent(tp, XFS_AGB_TO_FSB(mp, agno,
+ INT_GET(agf->agf_length, ARCH_CONVERT) - new), new);
+ if (error) {
+ goto error0;
+ }
+ }
+ if (nagcount > oagcount)
+ xfs_trans_mod_sb(tp, XFS_TRANS_SB_AGCOUNT, nagcount - oagcount);
+ if (nb > mp->m_sb.sb_dblocks)
+ xfs_trans_mod_sb(tp, XFS_TRANS_SB_DBLOCKS,
+ nb - mp->m_sb.sb_dblocks);
+ if (nfree)
+ xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, nfree);
+ if (dpct)
+ xfs_trans_mod_sb(tp, XFS_TRANS_SB_IMAXPCT, dpct);
+ error = xfs_trans_commit(tp, 0, NULL);
+ if (error) {
+ return error;
+ }
+ /* New allocation groups fully initialized, so update mount struct */
+ if (nagimax)
+ mp->m_maxagi = nagimax;
+ if (mp->m_sb.sb_imax_pct) {
+ __uint64_t icount = mp->m_sb.sb_dblocks * mp->m_sb.sb_imax_pct;
+ do_div(icount, 100);
+ mp->m_maxicount = icount << mp->m_sb.sb_inopblog;
+ } else
+ mp->m_maxicount = 0;
+ for (agno = 1; agno < nagcount; agno++) {
+ error = xfs_read_buf(mp, mp->m_ddev_targp,
+ XFS_AGB_TO_DADDR(mp, agno, XFS_SB_BLOCK(mp)),
+ XFS_FSS_TO_BB(mp, 1), 0, &bp);
+ if (error) {
+ xfs_fs_cmn_err(CE_WARN, mp,
+ "error %d reading secondary superblock for ag %d",
+ error, agno);
+ break;
+ }
+ sbp = XFS_BUF_TO_SBP(bp);
+ xfs_xlatesb(sbp, &mp->m_sb, -1, ARCH_CONVERT, XFS_SB_ALL_BITS);
+ /*
+ * If we get an error writing out the alternate superblocks,
+ * just issue a warning and continue. The real work is
+ * already done and committed.
+ */
+ if (!(error = xfs_bwrite(mp, bp))) {
+ continue;
+ } else {
+ xfs_fs_cmn_err(CE_WARN, mp,
+ "write error %d updating secondary superblock for ag %d",
+ error, agno);
+ break; /* no point in continuing */
+ }
+ }
+ return 0;
+
+ error0:
+ xfs_trans_cancel(tp, XFS_TRANS_ABORT);
+ return error;
+}
+
+static int
+xfs_growfs_log_private(
+ xfs_mount_t *mp, /* mount point for filesystem */
+ xfs_growfs_log_t *in) /* growfs log input struct */
+{
+ xfs_extlen_t nb;
+
+ nb = in->newblocks;
+ if (nb < XFS_MIN_LOG_BLOCKS || nb < XFS_B_TO_FSB(mp, XFS_MIN_LOG_BYTES))
+ return XFS_ERROR(EINVAL);
+ if (nb == mp->m_sb.sb_logblocks &&
+ in->isint == (mp->m_sb.sb_logstart != 0))
+ return XFS_ERROR(EINVAL);
+ /*
+ * Moving the log is hard, need new interfaces to sync
+ * the log first, hold off all activity while moving it.
+ * Can have shorter or longer log in the same space,
+ * or transform internal to external log or vice versa.
+ */
+ return XFS_ERROR(ENOSYS);
+}
+
+/*
+ * protected versions of growfs function acquire and release locks on the mount
+ * point - exported through ioctls: XFS_IOC_FSGROWFSDATA, XFS_IOC_FSGROWFSLOG,
+ * XFS_IOC_FSGROWFSRT
+ */
+
+
+int
+xfs_growfs_data(
+ xfs_mount_t *mp,
+ xfs_growfs_data_t *in)
+{
+ int error;
+ if (!cpsema(&mp->m_growlock))
+ return XFS_ERROR(EWOULDBLOCK);
+ error = xfs_growfs_data_private(mp, in);
+ vsema(&mp->m_growlock);
+ return error;
+}
+
+int
+xfs_growfs_log(
+ xfs_mount_t *mp,
+ xfs_growfs_log_t *in)
+{
+ int error;
+ if (!cpsema(&mp->m_growlock))
+ return XFS_ERROR(EWOULDBLOCK);
+ error = xfs_growfs_log_private(mp, in);
+ vsema(&mp->m_growlock);
+ return error;
+}
+
+/*
+ * exported through ioctl XFS_IOC_FSCOUNTS
+ */
+
+int
+xfs_fs_counts(
+ xfs_mount_t *mp,
+ xfs_fsop_counts_t *cnt)
+{
+ unsigned long s;
+
+ s = XFS_SB_LOCK(mp);
+ cnt->freedata = mp->m_sb.sb_fdblocks;
+ cnt->freertx = mp->m_sb.sb_frextents;
+ cnt->freeino = mp->m_sb.sb_ifree;
+ cnt->allocino = mp->m_sb.sb_icount;
+ XFS_SB_UNLOCK(mp, s);
+ return 0;
+}
+
+/*
+ * exported through ioctl XFS_IOC_SET_RESBLKS & XFS_IOC_GET_RESBLKS
+ *
+ * xfs_reserve_blocks is called to set m_resblks
+ * in the in-core mount table. The number of unused reserved blocks
+ * is kept in m_resbls_avail.
+ *
+ * Reserve the requested number of blocks if available. Otherwise return
+ * as many as possible to satisfy the request. The actual number
+ * reserved are returned in outval
+ *
+ * A null inval pointer indicates that only the current reserved blocks
+ * available should be returned no settings are changed.
+ */
+
+int
+xfs_reserve_blocks(
+ xfs_mount_t *mp,
+ __uint64_t *inval,
+ xfs_fsop_resblks_t *outval)
+{
+ __int64_t lcounter, delta;
+ __uint64_t request;
+ unsigned long s;
+
+ /* If inval is null, report current values and return */
+
+ if (inval == (__uint64_t *)NULL) {
+ outval->resblks = mp->m_resblks;
+ outval->resblks_avail = mp->m_resblks_avail;
+ return(0);
+ }
+
+ request = *inval;
+ s = XFS_SB_LOCK(mp);
+
+ /*
+ * If our previous reservation was larger than the current value,
+ * then move any unused blocks back to the free pool.
+ */
+
+ if (mp->m_resblks > request) {
+ lcounter = mp->m_resblks_avail - request;
+ if (lcounter > 0) { /* release unused blocks */
+ mp->m_sb.sb_fdblocks += lcounter;
+ mp->m_resblks_avail -= lcounter;
+ }
+ mp->m_resblks = request;
+ } else {
+ delta = request - mp->m_resblks;
+ lcounter = mp->m_sb.sb_fdblocks - delta;
+ if (lcounter < 0) {
+ /* We can't satisfy the request, just get what we can */
+ mp->m_resblks += mp->m_sb.sb_fdblocks;
+ mp->m_resblks_avail += mp->m_sb.sb_fdblocks;
+ mp->m_sb.sb_fdblocks = 0;
+ } else {
+ mp->m_sb.sb_fdblocks = lcounter;
+ mp->m_resblks = request;
+ mp->m_resblks_avail += delta;
+ }
+ }
+
+ outval->resblks = mp->m_resblks;
+ outval->resblks_avail = mp->m_resblks_avail;
+ XFS_SB_UNLOCK(mp, s);
+ return(0);
+}
+
+void
+xfs_fs_log_dummy(xfs_mount_t *mp)
+{
+ xfs_trans_t *tp;
+ xfs_inode_t *ip;
+
+
+ tp = _xfs_trans_alloc(mp, XFS_TRANS_DUMMY1);
+ atomic_inc(&mp->m_active_trans);
+ if (xfs_trans_reserve(tp, 0, XFS_ICHANGE_LOG_RES(mp), 0, 0, 0)) {
+ xfs_trans_cancel(tp, 0);
+ return;
+ }
+
+ ip = mp->m_rootip;
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
+
+ xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
+ xfs_trans_ihold(tp, ip);
+ xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+ xfs_trans_set_sync(tp);
+ xfs_trans_commit(tp, 0, NULL);
+
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+}
+
+int
+xfs_fs_goingdown(
+ xfs_mount_t *mp,
+ __uint32_t inflags)
+{
+ switch (inflags) {
+ case XFS_FSOP_GOING_FLAGS_DEFAULT: {
+ struct vfs *vfsp = XFS_MTOVFS(mp);
+ struct super_block *sb = freeze_bdev(vfsp->vfs_super->s_bdev);
+
+ if (sb) {
+ xfs_force_shutdown(mp, XFS_FORCE_UMOUNT);
+ thaw_bdev(sb->s_bdev, sb);
+ }
+
+ break;
+ }
+ case XFS_FSOP_GOING_FLAGS_LOGFLUSH:
+ xfs_force_shutdown(mp, XFS_FORCE_UMOUNT);
+ break;
+ case XFS_FSOP_GOING_FLAGS_NOLOGFLUSH:
+ xfs_force_shutdown(mp, XFS_FORCE_UMOUNT|XFS_LOG_IO_ERROR);
+ break;
+ default:
+ return XFS_ERROR(EINVAL);
+ }
+
+ return 0;
+}
diff --git a/fs/xfs/xfs_fsops.h b/fs/xfs/xfs_fsops.h
new file mode 100644
index 00000000000000..b61486173a610e
--- /dev/null
+++ b/fs/xfs/xfs_fsops.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2000-2001 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_FSOPS_H__
+#define __XFS_FSOPS_H__
+
+int
+xfs_fs_geometry(
+ xfs_mount_t *mp,
+ xfs_fsop_geom_t *geo,
+ int new_version);
+
+int
+xfs_growfs_data(
+ xfs_mount_t *mp,
+ xfs_growfs_data_t *in);
+
+int
+xfs_growfs_log(
+ xfs_mount_t *mp,
+ xfs_growfs_log_t *in);
+
+int
+xfs_fs_counts(
+ xfs_mount_t *mp,
+ xfs_fsop_counts_t *cnt);
+
+int
+xfs_reserve_blocks(
+ xfs_mount_t *mp,
+ __uint64_t *inval,
+ xfs_fsop_resblks_t *outval);
+
+int
+xfs_fs_goingdown(
+ xfs_mount_t *mp,
+ __uint32_t inflags);
+
+#endif /* __XFS_FSOPS_H__ */
diff --git a/fs/xfs/xfs_ialloc.c b/fs/xfs/xfs_ialloc.c
new file mode 100644
index 00000000000000..76ffe4a8b4fe18
--- /dev/null
+++ b/fs/xfs/xfs_ialloc.c
@@ -0,0 +1,1402 @@
+/*
+ * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode.h"
+#include "xfs_alloc.h"
+#include "xfs_bit.h"
+#include "xfs_rtalloc.h"
+#include "xfs_error.h"
+#include "xfs_bmap.h"
+
+/*
+ * Log specified fields for the inode given by bp and off.
+ */
+STATIC void
+xfs_ialloc_log_di(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_buf_t *bp, /* inode buffer */
+ int off, /* index of inode in buffer */
+ int fields) /* bitmask of fields to log */
+{
+ int first; /* first byte number */
+ int ioffset; /* off in bytes */
+ int last; /* last byte number */
+ xfs_mount_t *mp; /* mount point structure */
+ static const short offsets[] = { /* field offsets */
+ /* keep in sync with bits */
+ offsetof(xfs_dinode_core_t, di_magic),
+ offsetof(xfs_dinode_core_t, di_mode),
+ offsetof(xfs_dinode_core_t, di_version),
+ offsetof(xfs_dinode_core_t, di_format),
+ offsetof(xfs_dinode_core_t, di_onlink),
+ offsetof(xfs_dinode_core_t, di_uid),
+ offsetof(xfs_dinode_core_t, di_gid),
+ offsetof(xfs_dinode_core_t, di_nlink),
+ offsetof(xfs_dinode_core_t, di_projid),
+ offsetof(xfs_dinode_core_t, di_pad),
+ offsetof(xfs_dinode_core_t, di_atime),
+ offsetof(xfs_dinode_core_t, di_mtime),
+ offsetof(xfs_dinode_core_t, di_ctime),
+ offsetof(xfs_dinode_core_t, di_size),
+ offsetof(xfs_dinode_core_t, di_nblocks),
+ offsetof(xfs_dinode_core_t, di_extsize),
+ offsetof(xfs_dinode_core_t, di_nextents),
+ offsetof(xfs_dinode_core_t, di_anextents),
+ offsetof(xfs_dinode_core_t, di_forkoff),
+ offsetof(xfs_dinode_core_t, di_aformat),
+ offsetof(xfs_dinode_core_t, di_dmevmask),
+ offsetof(xfs_dinode_core_t, di_dmstate),
+ offsetof(xfs_dinode_core_t, di_flags),
+ offsetof(xfs_dinode_core_t, di_gen),
+ offsetof(xfs_dinode_t, di_next_unlinked),
+ offsetof(xfs_dinode_t, di_u),
+ offsetof(xfs_dinode_t, di_a),
+ sizeof(xfs_dinode_t)
+ };
+
+
+ ASSERT(offsetof(xfs_dinode_t, di_core) == 0);
+ ASSERT((fields & (XFS_DI_U|XFS_DI_A)) == 0);
+ mp = tp->t_mountp;
+ /*
+ * Get the inode-relative first and last bytes for these fields
+ */
+ xfs_btree_offsets(fields, offsets, XFS_DI_NUM_BITS, &first, &last);
+ /*
+ * Convert to buffer offsets and log it.
+ */
+ ioffset = off << mp->m_sb.sb_inodelog;
+ first += ioffset;
+ last += ioffset;
+ xfs_trans_log_buf(tp, bp, first, last);
+}
+
+/*
+ * Allocation group level functions.
+ */
+
+/*
+ * Allocate new inodes in the allocation group specified by agbp.
+ * Return 0 for success, else error code.
+ */
+STATIC int /* error code or 0 */
+xfs_ialloc_ag_alloc(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_buf_t *agbp, /* alloc group buffer */
+ int *alloc)
+{
+ xfs_agi_t *agi; /* allocation group header */
+ xfs_alloc_arg_t args; /* allocation argument structure */
+ int blks_per_cluster; /* fs blocks per inode cluster */
+ xfs_btree_cur_t *cur; /* inode btree cursor */
+ xfs_daddr_t d; /* disk addr of buffer */
+ int error;
+ xfs_buf_t *fbuf; /* new free inodes' buffer */
+ xfs_dinode_t *free; /* new free inode structure */
+ int i; /* inode counter */
+ int j; /* block counter */
+ int nbufs; /* num bufs of new inodes */
+ xfs_agino_t newino; /* new first inode's number */
+ xfs_agino_t newlen; /* new number of inodes */
+ int ninodes; /* num inodes per buf */
+ xfs_agino_t thisino; /* current inode number, for loop */
+ int version; /* inode version number to use */
+ int isaligned; /* inode allocation at stripe unit */
+ /* boundary */
+ xfs_dinode_core_t dic; /* a dinode_core to copy to new */
+ /* inodes */
+
+ args.tp = tp;
+ args.mp = tp->t_mountp;
+
+ /*
+ * Locking will ensure that we don't have two callers in here
+ * at one time.
+ */
+ newlen = XFS_IALLOC_INODES(args.mp);
+ if (args.mp->m_maxicount &&
+ args.mp->m_sb.sb_icount + newlen > args.mp->m_maxicount)
+ return XFS_ERROR(ENOSPC);
+ args.minlen = args.maxlen = XFS_IALLOC_BLOCKS(args.mp);
+ /*
+ * Set the alignment for the allocation.
+ * If stripe alignment is turned on then align at stripe unit
+ * boundary.
+ * If the cluster size is smaller than a filesystem block
+ * then we're doing I/O for inodes in filesystem block size pieces,
+ * so don't need alignment anyway.
+ */
+ isaligned = 0;
+ if (args.mp->m_sinoalign) {
+ ASSERT(!(args.mp->m_flags & XFS_MOUNT_NOALIGN));
+ args.alignment = args.mp->m_dalign;
+ isaligned = 1;
+ } else if (XFS_SB_VERSION_HASALIGN(&args.mp->m_sb) &&
+ args.mp->m_sb.sb_inoalignmt >=
+ XFS_B_TO_FSBT(args.mp, XFS_INODE_CLUSTER_SIZE(args.mp)))
+ args.alignment = args.mp->m_sb.sb_inoalignmt;
+ else
+ args.alignment = 1;
+ agi = XFS_BUF_TO_AGI(agbp);
+ /*
+ * Need to figure out where to allocate the inode blocks.
+ * Ideally they should be spaced out through the a.g.
+ * For now, just allocate blocks up front.
+ */
+ args.agbno = INT_GET(agi->agi_root, ARCH_CONVERT);
+ args.fsbno = XFS_AGB_TO_FSB(args.mp, INT_GET(agi->agi_seqno, ARCH_CONVERT),
+ args.agbno);
+ /*
+ * Allocate a fixed-size extent of inodes.
+ */
+ args.type = XFS_ALLOCTYPE_NEAR_BNO;
+ args.mod = args.total = args.wasdel = args.isfl = args.userdata =
+ args.minalignslop = 0;
+ args.prod = 1;
+ /*
+ * Allow space for the inode btree to split.
+ */
+ args.minleft = XFS_IN_MAXLEVELS(args.mp) - 1;
+ if ((error = xfs_alloc_vextent(&args)))
+ return error;
+
+ /*
+ * If stripe alignment is turned on, then try again with cluster
+ * alignment.
+ */
+ if (isaligned && args.fsbno == NULLFSBLOCK) {
+ args.type = XFS_ALLOCTYPE_NEAR_BNO;
+ args.agbno = INT_GET(agi->agi_root, ARCH_CONVERT);
+ args.fsbno = XFS_AGB_TO_FSB(args.mp,
+ INT_GET(agi->agi_seqno, ARCH_CONVERT), args.agbno);
+ if (XFS_SB_VERSION_HASALIGN(&args.mp->m_sb) &&
+ args.mp->m_sb.sb_inoalignmt >=
+ XFS_B_TO_FSBT(args.mp, XFS_INODE_CLUSTER_SIZE(args.mp)))
+ args.alignment = args.mp->m_sb.sb_inoalignmt;
+ else
+ args.alignment = 1;
+ if ((error = xfs_alloc_vextent(&args)))
+ return error;
+ }
+
+ if (args.fsbno == NULLFSBLOCK) {
+ *alloc = 0;
+ return 0;
+ }
+ ASSERT(args.len == args.minlen);
+ /*
+ * Convert the results.
+ */
+ newino = XFS_OFFBNO_TO_AGINO(args.mp, args.agbno, 0);
+ /*
+ * Loop over the new block(s), filling in the inodes.
+ * For small block sizes, manipulate the inodes in buffers
+ * which are multiples of the blocks size.
+ */
+ if (args.mp->m_sb.sb_blocksize >= XFS_INODE_CLUSTER_SIZE(args.mp)) {
+ blks_per_cluster = 1;
+ nbufs = (int)args.len;
+ ninodes = args.mp->m_sb.sb_inopblock;
+ } else {
+ blks_per_cluster = XFS_INODE_CLUSTER_SIZE(args.mp) /
+ args.mp->m_sb.sb_blocksize;
+ nbufs = (int)args.len / blks_per_cluster;
+ ninodes = blks_per_cluster * args.mp->m_sb.sb_inopblock;
+ }
+ /*
+ * Figure out what version number to use in the inodes we create.
+ * If the superblock version has caught up to the one that supports
+ * the new inode format, then use the new inode version. Otherwise
+ * use the old version so that old kernels will continue to be
+ * able to use the file system.
+ */
+ if (XFS_SB_VERSION_HASNLINK(&args.mp->m_sb))
+ version = XFS_DINODE_VERSION_2;
+ else
+ version = XFS_DINODE_VERSION_1;
+
+ memset(&dic, 0, sizeof(xfs_dinode_core_t));
+ INT_SET(dic.di_magic, ARCH_CONVERT, XFS_DINODE_MAGIC);
+ INT_SET(dic.di_version, ARCH_CONVERT, version);
+
+ for (j = 0; j < nbufs; j++) {
+ /*
+ * Get the block.
+ */
+ d = XFS_AGB_TO_DADDR(args.mp, INT_GET(agi->agi_seqno, ARCH_CONVERT),
+ args.agbno + (j * blks_per_cluster));
+ fbuf = xfs_trans_get_buf(tp, args.mp->m_ddev_targp, d,
+ args.mp->m_bsize * blks_per_cluster,
+ XFS_BUF_LOCK);
+ ASSERT(fbuf);
+ ASSERT(!XFS_BUF_GETERROR(fbuf));
+ /*
+ * Loop over the inodes in this buffer.
+ */
+
+ for (i = 0; i < ninodes; i++) {
+ free = XFS_MAKE_IPTR(args.mp, fbuf, i);
+ memcpy(&(free->di_core), &dic, sizeof(xfs_dinode_core_t));
+ INT_SET(free->di_next_unlinked, ARCH_CONVERT, NULLAGINO);
+ xfs_ialloc_log_di(tp, fbuf, i,
+ XFS_DI_CORE_BITS | XFS_DI_NEXT_UNLINKED);
+ }
+ xfs_trans_inode_alloc_buf(tp, fbuf);
+ }
+ INT_MOD(agi->agi_count, ARCH_CONVERT, newlen);
+ INT_MOD(agi->agi_freecount, ARCH_CONVERT, newlen);
+ down_read(&args.mp->m_peraglock);
+ args.mp->m_perag[INT_GET(agi->agi_seqno, ARCH_CONVERT)].pagi_freecount += newlen;
+ up_read(&args.mp->m_peraglock);
+ INT_SET(agi->agi_newino, ARCH_CONVERT, newino);
+ /*
+ * Insert records describing the new inode chunk into the btree.
+ */
+ cur = xfs_btree_init_cursor(args.mp, tp, agbp,
+ INT_GET(agi->agi_seqno, ARCH_CONVERT),
+ XFS_BTNUM_INO, (xfs_inode_t *)0, 0);
+ for (thisino = newino;
+ thisino < newino + newlen;
+ thisino += XFS_INODES_PER_CHUNK) {
+ if ((error = xfs_inobt_lookup_eq(cur, thisino,
+ XFS_INODES_PER_CHUNK, XFS_INOBT_ALL_FREE, &i))) {
+ xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
+ return error;
+ }
+ ASSERT(i == 0);
+ if ((error = xfs_inobt_insert(cur, &i))) {
+ xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
+ return error;
+ }
+ ASSERT(i == 1);
+ }
+ xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
+ /*
+ * Log allocation group header fields
+ */
+ xfs_ialloc_log_agi(tp, agbp,
+ XFS_AGI_COUNT | XFS_AGI_FREECOUNT | XFS_AGI_NEWINO);
+ /*
+ * Modify/log superblock values for inode count and inode free count.
+ */
+ xfs_trans_mod_sb(tp, XFS_TRANS_SB_ICOUNT, (long)newlen);
+ xfs_trans_mod_sb(tp, XFS_TRANS_SB_IFREE, (long)newlen);
+ *alloc = 1;
+ return 0;
+}
+
+STATIC __inline xfs_agnumber_t
+xfs_ialloc_next_ag(
+ xfs_mount_t *mp)
+{
+ xfs_agnumber_t agno;
+
+ spin_lock(&mp->m_agirotor_lock);
+ agno = mp->m_agirotor;
+ if (++mp->m_agirotor == mp->m_maxagi)
+ mp->m_agirotor = 0;
+ spin_unlock(&mp->m_agirotor_lock);
+
+ return agno;
+}
+
+/*
+ * Select an allocation group to look for a free inode in, based on the parent
+ * inode and then mode. Return the allocation group buffer.
+ */
+STATIC xfs_buf_t * /* allocation group buffer */
+xfs_ialloc_ag_select(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_ino_t parent, /* parent directory inode number */
+ mode_t mode, /* bits set to indicate file type */
+ int okalloc) /* ok to allocate more space */
+{
+ xfs_buf_t *agbp; /* allocation group header buffer */
+ xfs_agnumber_t agcount; /* number of ag's in the filesystem */
+ xfs_agnumber_t agno; /* current ag number */
+ int flags; /* alloc buffer locking flags */
+ xfs_extlen_t ineed; /* blocks needed for inode allocation */
+ xfs_extlen_t longest = 0; /* longest extent available */
+ xfs_mount_t *mp; /* mount point structure */
+ int needspace; /* file mode implies space allocated */
+ xfs_perag_t *pag; /* per allocation group data */
+ xfs_agnumber_t pagno; /* parent (starting) ag number */
+
+ /*
+ * Files of these types need at least one block if length > 0
+ * (and they won't fit in the inode, but that's hard to figure out).
+ */
+ needspace = S_ISDIR(mode) || S_ISREG(mode) || S_ISLNK(mode);
+ mp = tp->t_mountp;
+ agcount = mp->m_maxagi;
+ if (S_ISDIR(mode))
+ pagno = xfs_ialloc_next_ag(mp);
+ else {
+ pagno = XFS_INO_TO_AGNO(mp, parent);
+ if (pagno >= agcount)
+ pagno = 0;
+ }
+ ASSERT(pagno < agcount);
+ /*
+ * Loop through allocation groups, looking for one with a little
+ * free space in it. Note we don't look for free inodes, exactly.
+ * Instead, we include whether there is a need to allocate inodes
+ * to mean that blocks must be allocated for them,
+ * if none are currently free.
+ */
+ agno = pagno;
+ flags = XFS_ALLOC_FLAG_TRYLOCK;
+ down_read(&mp->m_peraglock);
+ for (;;) {
+ pag = &mp->m_perag[agno];
+ if (!pag->pagi_init) {
+ if (xfs_ialloc_read_agi(mp, tp, agno, &agbp)) {
+ agbp = NULL;
+ goto nextag;
+ }
+ } else
+ agbp = NULL;
+
+ if (!pag->pagi_inodeok) {
+ xfs_ialloc_next_ag(mp);
+ goto unlock_nextag;
+ }
+
+ /*
+ * Is there enough free space for the file plus a block
+ * of inodes (if we need to allocate some)?
+ */
+ ineed = pag->pagi_freecount ? 0 : XFS_IALLOC_BLOCKS(mp);
+ if (ineed && !pag->pagf_init) {
+ if (agbp == NULL &&
+ xfs_ialloc_read_agi(mp, tp, agno, &agbp)) {
+ agbp = NULL;
+ goto nextag;
+ }
+ (void)xfs_alloc_pagf_init(mp, tp, agno, flags);
+ }
+ if (!ineed || pag->pagf_init) {
+ if (ineed && !(longest = pag->pagf_longest))
+ longest = pag->pagf_flcount > 0;
+ if (!ineed ||
+ (pag->pagf_freeblks >= needspace + ineed &&
+ longest >= ineed &&
+ okalloc)) {
+ if (agbp == NULL &&
+ xfs_ialloc_read_agi(mp, tp, agno, &agbp)) {
+ agbp = NULL;
+ goto nextag;
+ }
+ up_read(&mp->m_peraglock);
+ return agbp;
+ }
+ }
+unlock_nextag:
+ if (agbp)
+ xfs_trans_brelse(tp, agbp);
+nextag:
+ /*
+ * No point in iterating over the rest, if we're shutting
+ * down.
+ */
+ if (XFS_FORCED_SHUTDOWN(mp)) {
+ up_read(&mp->m_peraglock);
+ return (xfs_buf_t *)0;
+ }
+ agno++;
+ if (agno >= agcount)
+ agno = 0;
+ if (agno == pagno) {
+ if (flags == 0) {
+ up_read(&mp->m_peraglock);
+ return (xfs_buf_t *)0;
+ }
+ flags = 0;
+ }
+ }
+}
+
+/*
+ * Visible inode allocation functions.
+ */
+
+/*
+ * Allocate an inode on disk.
+ * Mode is used to tell whether the new inode will need space, and whether
+ * it is a directory.
+ *
+ * The arguments IO_agbp and alloc_done are defined to work within
+ * the constraint of one allocation per transaction.
+ * xfs_dialloc() is designed to be called twice if it has to do an
+ * allocation to make more free inodes. On the first call,
+ * IO_agbp should be set to NULL. If an inode is available,
+ * i.e., xfs_dialloc() did not need to do an allocation, an inode
+ * number is returned. In this case, IO_agbp would be set to the
+ * current ag_buf and alloc_done set to false.
+ * If an allocation needed to be done, xfs_dialloc would return
+ * the current ag_buf in IO_agbp and set alloc_done to true.
+ * The caller should then commit the current transaction, allocate a new
+ * transaction, and call xfs_dialloc() again, passing in the previous
+ * value of IO_agbp. IO_agbp should be held across the transactions.
+ * Since the agbp is locked across the two calls, the second call is
+ * guaranteed to have a free inode available.
+ *
+ * Once we successfully pick an inode its number is returned and the
+ * on-disk data structures are updated. The inode itself is not read
+ * in, since doing so would break ordering constraints with xfs_reclaim.
+ */
+int
+xfs_dialloc(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_ino_t parent, /* parent inode (directory) */
+ mode_t mode, /* mode bits for new inode */
+ int okalloc, /* ok to allocate more space */
+ xfs_buf_t **IO_agbp, /* in/out ag header's buffer */
+ boolean_t *alloc_done, /* true if we needed to replenish
+ inode freelist */
+ xfs_ino_t *inop) /* inode number allocated */
+{
+ xfs_agnumber_t agcount; /* number of allocation groups */
+ xfs_buf_t *agbp; /* allocation group header's buffer */
+ xfs_agnumber_t agno; /* allocation group number */
+ xfs_agi_t *agi; /* allocation group header structure */
+ xfs_btree_cur_t *cur; /* inode allocation btree cursor */
+ int error; /* error return value */
+ int i; /* result code */
+ int ialloced; /* inode allocation status */
+ int noroom = 0; /* no space for inode blk allocation */
+ xfs_ino_t ino; /* fs-relative inode to be returned */
+ /* REFERENCED */
+ int j; /* result code */
+ xfs_mount_t *mp; /* file system mount structure */
+ int offset; /* index of inode in chunk */
+ xfs_agino_t pagino; /* parent's a.g. relative inode # */
+ xfs_agnumber_t pagno; /* parent's allocation group number */
+ xfs_inobt_rec_t rec; /* inode allocation record */
+ xfs_agnumber_t tagno; /* testing allocation group number */
+ xfs_btree_cur_t *tcur; /* temp cursor */
+ xfs_inobt_rec_t trec; /* temp inode allocation record */
+
+
+ if (*IO_agbp == NULL) {
+ /*
+ * We do not have an agbp, so select an initial allocation
+ * group for inode allocation.
+ */
+ agbp = xfs_ialloc_ag_select(tp, parent, mode, okalloc);
+ /*
+ * Couldn't find an allocation group satisfying the
+ * criteria, give up.
+ */
+ if (!agbp) {
+ *inop = NULLFSINO;
+ return 0;
+ }
+ agi = XFS_BUF_TO_AGI(agbp);
+ ASSERT(INT_GET(agi->agi_magicnum, ARCH_CONVERT) == XFS_AGI_MAGIC);
+ } else {
+ /*
+ * Continue where we left off before. In this case, we
+ * know that the allocation group has free inodes.
+ */
+ agbp = *IO_agbp;
+ agi = XFS_BUF_TO_AGI(agbp);
+ ASSERT(INT_GET(agi->agi_magicnum, ARCH_CONVERT) == XFS_AGI_MAGIC);
+ ASSERT(INT_GET(agi->agi_freecount, ARCH_CONVERT) > 0);
+ }
+ mp = tp->t_mountp;
+ agcount = mp->m_sb.sb_agcount;
+ agno = INT_GET(agi->agi_seqno, ARCH_CONVERT);
+ tagno = agno;
+ pagno = XFS_INO_TO_AGNO(mp, parent);
+ pagino = XFS_INO_TO_AGINO(mp, parent);
+
+ /*
+ * If we have already hit the ceiling of inode blocks then clear
+ * okalloc so we scan all available agi structures for a free
+ * inode.
+ */
+
+ if (mp->m_maxicount &&
+ mp->m_sb.sb_icount + XFS_IALLOC_INODES(mp) > mp->m_maxicount) {
+ noroom = 1;
+ okalloc = 0;
+ }
+
+ /*
+ * Loop until we find an allocation group that either has free inodes
+ * or in which we can allocate some inodes. Iterate through the
+ * allocation groups upward, wrapping at the end.
+ */
+ *alloc_done = B_FALSE;
+ while (INT_ISZERO(agi->agi_freecount, ARCH_CONVERT)) {
+ /*
+ * Don't do anything if we're not supposed to allocate
+ * any blocks, just go on to the next ag.
+ */
+ if (okalloc) {
+ /*
+ * Try to allocate some new inodes in the allocation
+ * group.
+ */
+ if ((error = xfs_ialloc_ag_alloc(tp, agbp, &ialloced))) {
+ xfs_trans_brelse(tp, agbp);
+ if (error == ENOSPC) {
+ *inop = NULLFSINO;
+ return 0;
+ } else
+ return error;
+ }
+ if (ialloced) {
+ /*
+ * We successfully allocated some inodes, return
+ * the current context to the caller so that it
+ * can commit the current transaction and call
+ * us again where we left off.
+ */
+ ASSERT(INT_GET(agi->agi_freecount, ARCH_CONVERT) > 0);
+ *alloc_done = B_TRUE;
+ *IO_agbp = agbp;
+ *inop = NULLFSINO;
+ return 0;
+ }
+ }
+ /*
+ * If it failed, give up on this ag.
+ */
+ xfs_trans_brelse(tp, agbp);
+ /*
+ * Go on to the next ag: get its ag header.
+ */
+nextag:
+ if (++tagno == agcount)
+ tagno = 0;
+ if (tagno == agno) {
+ *inop = NULLFSINO;
+ return noroom ? ENOSPC : 0;
+ }
+ down_read(&mp->m_peraglock);
+ if (mp->m_perag[tagno].pagi_inodeok == 0) {
+ up_read(&mp->m_peraglock);
+ goto nextag;
+ }
+ error = xfs_ialloc_read_agi(mp, tp, tagno, &agbp);
+ up_read(&mp->m_peraglock);
+ if (error)
+ goto nextag;
+ agi = XFS_BUF_TO_AGI(agbp);
+ ASSERT(INT_GET(agi->agi_magicnum, ARCH_CONVERT) == XFS_AGI_MAGIC);
+ }
+ /*
+ * Here with an allocation group that has a free inode.
+ * Reset agno since we may have chosen a new ag in the
+ * loop above.
+ */
+ agno = tagno;
+ *IO_agbp = NULL;
+ cur = xfs_btree_init_cursor(mp, tp, agbp, INT_GET(agi->agi_seqno, ARCH_CONVERT),
+ XFS_BTNUM_INO, (xfs_inode_t *)0, 0);
+ /*
+ * If pagino is 0 (this is the root inode allocation) use newino.
+ * This must work because we've just allocated some.
+ */
+ if (!pagino)
+ pagino = INT_GET(agi->agi_newino, ARCH_CONVERT);
+#ifdef DEBUG
+ if (cur->bc_nlevels == 1) {
+ int freecount = 0;
+
+ if ((error = xfs_inobt_lookup_ge(cur, 0, 0, 0, &i)))
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ do {
+ if ((error = xfs_inobt_get_rec(cur, &rec.ir_startino,
+ &rec.ir_freecount, &rec.ir_free, &i, ARCH_NOCONVERT)))
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ freecount += rec.ir_freecount;
+ if ((error = xfs_inobt_increment(cur, 0, &i)))
+ goto error0;
+ } while (i == 1);
+
+ ASSERT(freecount == INT_GET(agi->agi_freecount, ARCH_CONVERT) ||
+ XFS_FORCED_SHUTDOWN(mp));
+ }
+#endif
+ /*
+ * If in the same a.g. as the parent, try to get near the parent.
+ */
+ if (pagno == agno) {
+ if ((error = xfs_inobt_lookup_le(cur, pagino, 0, 0, &i)))
+ goto error0;
+ if (i != 0 &&
+ (error = xfs_inobt_get_rec(cur, &rec.ir_startino,
+ &rec.ir_freecount, &rec.ir_free, &j, ARCH_NOCONVERT)) == 0 &&
+ j == 1 &&
+ rec.ir_freecount > 0) {
+ /*
+ * Found a free inode in the same chunk
+ * as parent, done.
+ */
+ }
+ /*
+ * In the same a.g. as parent, but parent's chunk is full.
+ */
+ else {
+ int doneleft; /* done, to the left */
+ int doneright; /* done, to the right */
+
+ if (error)
+ goto error0;
+ ASSERT(i == 1);
+ ASSERT(j == 1);
+ /*
+ * Duplicate the cursor, search left & right
+ * simultaneously.
+ */
+ if ((error = xfs_btree_dup_cursor(cur, &tcur)))
+ goto error0;
+ /*
+ * Search left with tcur, back up 1 record.
+ */
+ if ((error = xfs_inobt_decrement(tcur, 0, &i)))
+ goto error1;
+ doneleft = !i;
+ if (!doneleft) {
+ if ((error = xfs_inobt_get_rec(tcur,
+ &trec.ir_startino,
+ &trec.ir_freecount,
+ &trec.ir_free, &i, ARCH_NOCONVERT)))
+ goto error1;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error1);
+ }
+ /*
+ * Search right with cur, go forward 1 record.
+ */
+ if ((error = xfs_inobt_increment(cur, 0, &i)))
+ goto error1;
+ doneright = !i;
+ if (!doneright) {
+ if ((error = xfs_inobt_get_rec(cur,
+ &rec.ir_startino,
+ &rec.ir_freecount,
+ &rec.ir_free, &i, ARCH_NOCONVERT)))
+ goto error1;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error1);
+ }
+ /*
+ * Loop until we find the closest inode chunk
+ * with a free one.
+ */
+ while (!doneleft || !doneright) {
+ int useleft; /* using left inode
+ chunk this time */
+
+ /*
+ * Figure out which block is closer,
+ * if both are valid.
+ */
+ if (!doneleft && !doneright)
+ useleft =
+ pagino -
+ (trec.ir_startino +
+ XFS_INODES_PER_CHUNK - 1) <
+ rec.ir_startino - pagino;
+ else
+ useleft = !doneleft;
+ /*
+ * If checking the left, does it have
+ * free inodes?
+ */
+ if (useleft && trec.ir_freecount) {
+ /*
+ * Yes, set it up as the chunk to use.
+ */
+ rec = trec;
+ xfs_btree_del_cursor(cur,
+ XFS_BTREE_NOERROR);
+ cur = tcur;
+ break;
+ }
+ /*
+ * If checking the right, does it have
+ * free inodes?
+ */
+ if (!useleft && rec.ir_freecount) {
+ /*
+ * Yes, it's already set up.
+ */
+ xfs_btree_del_cursor(tcur,
+ XFS_BTREE_NOERROR);
+ break;
+ }
+ /*
+ * If used the left, get another one
+ * further left.
+ */
+ if (useleft) {
+ if ((error = xfs_inobt_decrement(tcur, 0,
+ &i)))
+ goto error1;
+ doneleft = !i;
+ if (!doneleft) {
+ if ((error = xfs_inobt_get_rec(
+ tcur,
+ &trec.ir_startino,
+ &trec.ir_freecount,
+ &trec.ir_free, &i, ARCH_NOCONVERT)))
+ goto error1;
+ XFS_WANT_CORRUPTED_GOTO(i == 1,
+ error1);
+ }
+ }
+ /*
+ * If used the right, get another one
+ * further right.
+ */
+ else {
+ if ((error = xfs_inobt_increment(cur, 0,
+ &i)))
+ goto error1;
+ doneright = !i;
+ if (!doneright) {
+ if ((error = xfs_inobt_get_rec(
+ cur,
+ &rec.ir_startino,
+ &rec.ir_freecount,
+ &rec.ir_free, &i, ARCH_NOCONVERT)))
+ goto error1;
+ XFS_WANT_CORRUPTED_GOTO(i == 1,
+ error1);
+ }
+ }
+ }
+ ASSERT(!doneleft || !doneright);
+ }
+ }
+ /*
+ * In a different a.g. from the parent.
+ * See if the most recently allocated block has any free.
+ */
+ else if (INT_GET(agi->agi_newino, ARCH_CONVERT) != NULLAGINO) {
+ if ((error = xfs_inobt_lookup_eq(cur,
+ INT_GET(agi->agi_newino, ARCH_CONVERT), 0, 0, &i)))
+ goto error0;
+ if (i == 1 &&
+ (error = xfs_inobt_get_rec(cur, &rec.ir_startino,
+ &rec.ir_freecount, &rec.ir_free, &j, ARCH_NOCONVERT)) == 0 &&
+ j == 1 &&
+ rec.ir_freecount > 0) {
+ /*
+ * The last chunk allocated in the group still has
+ * a free inode.
+ */
+ }
+ /*
+ * None left in the last group, search the whole a.g.
+ */
+ else {
+ if (error)
+ goto error0;
+ if ((error = xfs_inobt_lookup_ge(cur, 0, 0, 0, &i)))
+ goto error0;
+ ASSERT(i == 1);
+ for (;;) {
+ if ((error = xfs_inobt_get_rec(cur,
+ &rec.ir_startino,
+ &rec.ir_freecount, &rec.ir_free,
+ &i, ARCH_NOCONVERT)))
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ if (rec.ir_freecount > 0)
+ break;
+ if ((error = xfs_inobt_increment(cur, 0, &i)))
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ }
+ }
+ }
+ offset = XFS_IALLOC_FIND_FREE(&rec.ir_free);
+ ASSERT(offset >= 0);
+ ASSERT(offset < XFS_INODES_PER_CHUNK);
+ ASSERT((XFS_AGINO_TO_OFFSET(mp, rec.ir_startino) %
+ XFS_INODES_PER_CHUNK) == 0);
+ ino = XFS_AGINO_TO_INO(mp, agno, rec.ir_startino + offset);
+ XFS_INOBT_CLR_FREE(&rec, offset, ARCH_NOCONVERT);
+ rec.ir_freecount--;
+ if ((error = xfs_inobt_update(cur, rec.ir_startino, rec.ir_freecount,
+ rec.ir_free)))
+ goto error0;
+ INT_MOD(agi->agi_freecount, ARCH_CONVERT, -1);
+ xfs_ialloc_log_agi(tp, agbp, XFS_AGI_FREECOUNT);
+ down_read(&mp->m_peraglock);
+ mp->m_perag[tagno].pagi_freecount--;
+ up_read(&mp->m_peraglock);
+#ifdef DEBUG
+ if (cur->bc_nlevels == 1) {
+ int freecount = 0;
+
+ if ((error = xfs_inobt_lookup_ge(cur, 0, 0, 0, &i)))
+ goto error0;
+ do {
+ if ((error = xfs_inobt_get_rec(cur, &rec.ir_startino,
+ &rec.ir_freecount, &rec.ir_free, &i, ARCH_NOCONVERT)))
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ freecount += rec.ir_freecount;
+ if ((error = xfs_inobt_increment(cur, 0, &i)))
+ goto error0;
+ } while (i == 1);
+ ASSERT(freecount == INT_GET(agi->agi_freecount, ARCH_CONVERT) ||
+ XFS_FORCED_SHUTDOWN(mp));
+ }
+#endif
+ xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
+ xfs_trans_mod_sb(tp, XFS_TRANS_SB_IFREE, -1);
+ *inop = ino;
+ return 0;
+error1:
+ xfs_btree_del_cursor(tcur, XFS_BTREE_ERROR);
+error0:
+ xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
+ return error;
+}
+
+/*
+ * Free disk inode. Carefully avoids touching the incore inode, all
+ * manipulations incore are the caller's responsibility.
+ * The on-disk inode is not changed by this operation, only the
+ * btree (free inode mask) is changed.
+ */
+int
+xfs_difree(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_ino_t inode, /* inode to be freed */
+ xfs_bmap_free_t *flist, /* extents to free */
+ int *delete, /* set if inode cluster was deleted */
+ xfs_ino_t *first_ino) /* first inode in deleted cluster */
+{
+ /* REFERENCED */
+ xfs_agblock_t agbno; /* block number containing inode */
+ xfs_buf_t *agbp; /* buffer containing allocation group header */
+ xfs_agino_t agino; /* inode number relative to allocation group */
+ xfs_agnumber_t agno; /* allocation group number */
+ xfs_agi_t *agi; /* allocation group header */
+ xfs_btree_cur_t *cur; /* inode btree cursor */
+ int error; /* error return value */
+ int i; /* result code */
+ int ilen; /* inodes in an inode cluster */
+ xfs_mount_t *mp; /* mount structure for filesystem */
+ int off; /* offset of inode in inode chunk */
+ xfs_inobt_rec_t rec; /* btree record */
+
+ mp = tp->t_mountp;
+
+ /*
+ * Break up inode number into its components.
+ */
+ agno = XFS_INO_TO_AGNO(mp, inode);
+ if (agno >= mp->m_sb.sb_agcount) {
+ cmn_err(CE_WARN,
+ "xfs_difree: agno >= mp->m_sb.sb_agcount (%d >= %d) on %s. Returning EINVAL.",
+ agno, mp->m_sb.sb_agcount, mp->m_fsname);
+ ASSERT(0);
+ return XFS_ERROR(EINVAL);
+ }
+ agino = XFS_INO_TO_AGINO(mp, inode);
+ if (inode != XFS_AGINO_TO_INO(mp, agno, agino)) {
+ cmn_err(CE_WARN,
+ "xfs_difree: inode != XFS_AGINO_TO_INO() (%d != %d) on %s. Returning EINVAL.",
+ inode, XFS_AGINO_TO_INO(mp, agno, agino), mp->m_fsname);
+ ASSERT(0);
+ return XFS_ERROR(EINVAL);
+ }
+ agbno = XFS_AGINO_TO_AGBNO(mp, agino);
+ if (agbno >= mp->m_sb.sb_agblocks) {
+ cmn_err(CE_WARN,
+ "xfs_difree: agbno >= mp->m_sb.sb_agblocks (%d >= %d) on %s. Returning EINVAL.",
+ agbno, mp->m_sb.sb_agblocks, mp->m_fsname);
+ ASSERT(0);
+ return XFS_ERROR(EINVAL);
+ }
+ /*
+ * Get the allocation group header.
+ */
+ down_read(&mp->m_peraglock);
+ error = xfs_ialloc_read_agi(mp, tp, agno, &agbp);
+ up_read(&mp->m_peraglock);
+ if (error) {
+ cmn_err(CE_WARN,
+ "xfs_difree: xfs_ialloc_read_agi() returned an error %d on %s. Returning error.",
+ error, mp->m_fsname);
+ return error;
+ }
+ agi = XFS_BUF_TO_AGI(agbp);
+ ASSERT(INT_GET(agi->agi_magicnum, ARCH_CONVERT) == XFS_AGI_MAGIC);
+ ASSERT(agbno < INT_GET(agi->agi_length, ARCH_CONVERT));
+ /*
+ * Initialize the cursor.
+ */
+ cur = xfs_btree_init_cursor(mp, tp, agbp, agno, XFS_BTNUM_INO,
+ (xfs_inode_t *)0, 0);
+#ifdef DEBUG
+ if (cur->bc_nlevels == 1) {
+ int freecount = 0;
+
+ if ((error = xfs_inobt_lookup_ge(cur, 0, 0, 0, &i)))
+ goto error0;
+ do {
+ if ((error = xfs_inobt_get_rec(cur, &rec.ir_startino,
+ &rec.ir_freecount, &rec.ir_free, &i, ARCH_NOCONVERT)))
+ goto error0;
+ if (i) {
+ freecount += rec.ir_freecount;
+ if ((error = xfs_inobt_increment(cur, 0, &i)))
+ goto error0;
+ }
+ } while (i == 1);
+ ASSERT(freecount == INT_GET(agi->agi_freecount, ARCH_CONVERT) ||
+ XFS_FORCED_SHUTDOWN(mp));
+ }
+#endif
+ /*
+ * Look for the entry describing this inode.
+ */
+ if ((error = xfs_inobt_lookup_le(cur, agino, 0, 0, &i))) {
+ cmn_err(CE_WARN,
+ "xfs_difree: xfs_inobt_lookup_le returned() an error %d on %s. Returning error.",
+ error, mp->m_fsname);
+ goto error0;
+ }
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ if ((error = xfs_inobt_get_rec(cur, &rec.ir_startino, &rec.ir_freecount,
+ &rec.ir_free, &i, ARCH_NOCONVERT))) {
+ cmn_err(CE_WARN,
+ "xfs_difree: xfs_inobt_get_rec() returned an error %d on %s. Returning error.",
+ error, mp->m_fsname);
+ goto error0;
+ }
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ /*
+ * Get the offset in the inode chunk.
+ */
+ off = agino - rec.ir_startino;
+ ASSERT(off >= 0 && off < XFS_INODES_PER_CHUNK);
+ ASSERT(!XFS_INOBT_IS_FREE(&rec, off, ARCH_NOCONVERT));
+ /*
+ * Mark the inode free & increment the count.
+ */
+ XFS_INOBT_SET_FREE(&rec, off, ARCH_NOCONVERT);
+ rec.ir_freecount++;
+
+ /*
+ * When an inode cluster is free, it becomes elgible for removal
+ */
+ if ((mp->m_flags & XFS_MOUNT_IDELETE) &&
+ (rec.ir_freecount == XFS_IALLOC_INODES(mp))) {
+
+ *delete = 1;
+ *first_ino = XFS_AGINO_TO_INO(mp, agno, rec.ir_startino);
+
+ /*
+ * Remove the inode cluster from the AGI B+Tree, adjust the
+ * AGI and Superblock inode counts, and mark the disk space
+ * to be freed when the transaction is committed.
+ */
+ ilen = XFS_IALLOC_INODES(mp);
+ INT_MOD(agi->agi_count, ARCH_CONVERT, -ilen);
+ INT_MOD(agi->agi_freecount, ARCH_CONVERT, -(ilen - 1));
+ xfs_ialloc_log_agi(tp, agbp, XFS_AGI_COUNT | XFS_AGI_FREECOUNT);
+ down_read(&mp->m_peraglock);
+ mp->m_perag[agno].pagi_freecount -= ilen - 1;
+ up_read(&mp->m_peraglock);
+ xfs_trans_mod_sb(tp, XFS_TRANS_SB_ICOUNT, -ilen);
+ xfs_trans_mod_sb(tp, XFS_TRANS_SB_IFREE, -(ilen - 1));
+
+ if ((error = xfs_inobt_delete(cur, &i))) {
+ cmn_err(CE_WARN, "xfs_difree: xfs_inobt_delete returned an error %d on %s.\n",
+ error, mp->m_fsname);
+ goto error0;
+ }
+
+ xfs_bmap_add_free(XFS_AGB_TO_FSB(mp,
+ agno, XFS_INO_TO_AGBNO(mp,rec.ir_startino)),
+ XFS_IALLOC_BLOCKS(mp), flist, mp);
+ } else {
+ *delete = 0;
+
+ if ((error = xfs_inobt_update(cur, rec.ir_startino, rec.ir_freecount, rec.ir_free))) {
+ cmn_err(CE_WARN,
+ "xfs_difree: xfs_inobt_update() returned an error %d on %s. Returning error.",
+ error, mp->m_fsname);
+ goto error0;
+ }
+ /*
+ * Change the inode free counts and log the ag/sb changes.
+ */
+ INT_MOD(agi->agi_freecount, ARCH_CONVERT, 1);
+ xfs_ialloc_log_agi(tp, agbp, XFS_AGI_FREECOUNT);
+ down_read(&mp->m_peraglock);
+ mp->m_perag[agno].pagi_freecount++;
+ up_read(&mp->m_peraglock);
+ xfs_trans_mod_sb(tp, XFS_TRANS_SB_IFREE, 1);
+ }
+
+#ifdef DEBUG
+ if (cur->bc_nlevels == 1) {
+ int freecount = 0;
+
+ if ((error = xfs_inobt_lookup_ge(cur, 0, 0, 0, &i)))
+ goto error0;
+ do {
+ if ((error = xfs_inobt_get_rec(cur,
+ &rec.ir_startino,
+ &rec.ir_freecount,
+ &rec.ir_free, &i,
+ ARCH_NOCONVERT)))
+ goto error0;
+ if (i) {
+ freecount += rec.ir_freecount;
+ if ((error = xfs_inobt_increment(cur, 0, &i)))
+ goto error0;
+ }
+ } while (i == 1);
+ ASSERT(freecount == INT_GET(agi->agi_freecount, ARCH_CONVERT) ||
+ XFS_FORCED_SHUTDOWN(mp));
+ }
+#endif
+ xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
+ return 0;
+
+error0:
+ xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
+ return error;
+}
+
+/*
+ * Return the location of the inode in bno/off, for mapping it into a buffer.
+ */
+/*ARGSUSED*/
+int
+xfs_dilocate(
+ xfs_mount_t *mp, /* file system mount structure */
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_ino_t ino, /* inode to locate */
+ xfs_fsblock_t *bno, /* output: block containing inode */
+ int *len, /* output: num blocks in inode cluster */
+ int *off, /* output: index in block of inode */
+ uint flags) /* flags concerning inode lookup */
+{
+ xfs_agblock_t agbno; /* block number of inode in the alloc group */
+ xfs_buf_t *agbp; /* agi buffer */
+ xfs_agino_t agino; /* inode number within alloc group */
+ xfs_agnumber_t agno; /* allocation group number */
+ int blks_per_cluster; /* num blocks per inode cluster */
+ xfs_agblock_t chunk_agbno; /* first block in inode chunk */
+ xfs_agino_t chunk_agino; /* first agino in inode chunk */
+ __int32_t chunk_cnt; /* count of free inodes in chunk */
+ xfs_inofree_t chunk_free; /* mask of free inodes in chunk */
+ xfs_agblock_t cluster_agbno; /* first block in inode cluster */
+ xfs_btree_cur_t *cur; /* inode btree cursor */
+ int error; /* error code */
+ int i; /* temp state */
+ int offset; /* index of inode in its buffer */
+ int offset_agbno; /* blks from chunk start to inode */
+
+ ASSERT(ino != NULLFSINO);
+ /*
+ * Split up the inode number into its parts.
+ */
+ agno = XFS_INO_TO_AGNO(mp, ino);
+ agino = XFS_INO_TO_AGINO(mp, ino);
+ agbno = XFS_AGINO_TO_AGBNO(mp, agino);
+ if (agno >= mp->m_sb.sb_agcount || agbno >= mp->m_sb.sb_agblocks ||
+ ino != XFS_AGINO_TO_INO(mp, agno, agino)) {
+#ifdef DEBUG
+ if (agno >= mp->m_sb.sb_agcount) {
+ xfs_fs_cmn_err(CE_ALERT, mp,
+ "xfs_dilocate: agno (%d) >= "
+ "mp->m_sb.sb_agcount (%d)",
+ agno, mp->m_sb.sb_agcount);
+ }
+ if (agbno >= mp->m_sb.sb_agblocks) {
+ xfs_fs_cmn_err(CE_ALERT, mp,
+ "xfs_dilocate: agbno (0x%llx) >= "
+ "mp->m_sb.sb_agblocks (0x%lx)",
+ (unsigned long long) agbno,
+ (unsigned long) mp->m_sb.sb_agblocks);
+ }
+ if (ino != XFS_AGINO_TO_INO(mp, agno, agino)) {
+ xfs_fs_cmn_err(CE_ALERT, mp,
+ "xfs_dilocate: ino (0x%llx) != "
+ "XFS_AGINO_TO_INO(mp, agno, agino) "
+ "(0x%llx)",
+ ino, XFS_AGINO_TO_INO(mp, agno, agino));
+ }
+#endif /* DEBUG */
+ return XFS_ERROR(EINVAL);
+ }
+ if ((mp->m_sb.sb_blocksize >= XFS_INODE_CLUSTER_SIZE(mp)) ||
+ !(flags & XFS_IMAP_LOOKUP)) {
+ offset = XFS_INO_TO_OFFSET(mp, ino);
+ ASSERT(offset < mp->m_sb.sb_inopblock);
+ *bno = XFS_AGB_TO_FSB(mp, agno, agbno);
+ *off = offset;
+ *len = 1;
+ return 0;
+ }
+ blks_per_cluster = XFS_INODE_CLUSTER_SIZE(mp) >> mp->m_sb.sb_blocklog;
+ if (*bno != NULLFSBLOCK) {
+ offset = XFS_INO_TO_OFFSET(mp, ino);
+ ASSERT(offset < mp->m_sb.sb_inopblock);
+ cluster_agbno = XFS_FSB_TO_AGBNO(mp, *bno);
+ *off = ((agbno - cluster_agbno) * mp->m_sb.sb_inopblock) +
+ offset;
+ *len = blks_per_cluster;
+ return 0;
+ }
+ if (mp->m_inoalign_mask) {
+ offset_agbno = agbno & mp->m_inoalign_mask;
+ chunk_agbno = agbno - offset_agbno;
+ } else {
+ down_read(&mp->m_peraglock);
+ error = xfs_ialloc_read_agi(mp, tp, agno, &agbp);
+ up_read(&mp->m_peraglock);
+ if (error) {
+#ifdef DEBUG
+ xfs_fs_cmn_err(CE_ALERT, mp, "xfs_dilocate: "
+ "xfs_ialloc_read_agi() returned "
+ "error %d, agno %d",
+ error, agno);
+#endif /* DEBUG */
+ return error;
+ }
+ cur = xfs_btree_init_cursor(mp, tp, agbp, agno, XFS_BTNUM_INO,
+ (xfs_inode_t *)0, 0);
+ if ((error = xfs_inobt_lookup_le(cur, agino, 0, 0, &i))) {
+#ifdef DEBUG
+ xfs_fs_cmn_err(CE_ALERT, mp, "xfs_dilocate: "
+ "xfs_inobt_lookup_le() failed");
+#endif /* DEBUG */
+ goto error0;
+ }
+ if ((error = xfs_inobt_get_rec(cur, &chunk_agino, &chunk_cnt,
+ &chunk_free, &i, ARCH_NOCONVERT))) {
+#ifdef DEBUG
+ xfs_fs_cmn_err(CE_ALERT, mp, "xfs_dilocate: "
+ "xfs_inobt_get_rec() failed");
+#endif /* DEBUG */
+ goto error0;
+ }
+ if (i == 0) {
+#ifdef DEBUG
+ xfs_fs_cmn_err(CE_ALERT, mp, "xfs_dilocate: "
+ "xfs_inobt_get_rec() failed");
+#endif /* DEBUG */
+ error = XFS_ERROR(EINVAL);
+ }
+ xfs_trans_brelse(tp, agbp);
+ xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
+ if (error)
+ return error;
+ chunk_agbno = XFS_AGINO_TO_AGBNO(mp, chunk_agino);
+ offset_agbno = agbno - chunk_agbno;
+ }
+ ASSERT(agbno >= chunk_agbno);
+ cluster_agbno = chunk_agbno +
+ ((offset_agbno / blks_per_cluster) * blks_per_cluster);
+ offset = ((agbno - cluster_agbno) * mp->m_sb.sb_inopblock) +
+ XFS_INO_TO_OFFSET(mp, ino);
+ *bno = XFS_AGB_TO_FSB(mp, agno, cluster_agbno);
+ *off = offset;
+ *len = blks_per_cluster;
+ return 0;
+error0:
+ xfs_trans_brelse(tp, agbp);
+ xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
+ return error;
+}
+
+/*
+ * Compute and fill in value of m_in_maxlevels.
+ */
+void
+xfs_ialloc_compute_maxlevels(
+ xfs_mount_t *mp) /* file system mount structure */
+{
+ int level;
+ uint maxblocks;
+ uint maxleafents;
+ int minleafrecs;
+ int minnoderecs;
+
+ maxleafents = (1LL << XFS_INO_AGINO_BITS(mp)) >>
+ XFS_INODES_PER_CHUNK_LOG;
+ minleafrecs = mp->m_alloc_mnr[0];
+ minnoderecs = mp->m_alloc_mnr[1];
+ maxblocks = (maxleafents + minleafrecs - 1) / minleafrecs;
+ for (level = 1; maxblocks > 1; level++)
+ maxblocks = (maxblocks + minnoderecs - 1) / minnoderecs;
+ mp->m_in_maxlevels = level;
+}
+
+/*
+ * Log specified fields for the ag hdr (inode section)
+ */
+void
+xfs_ialloc_log_agi(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_buf_t *bp, /* allocation group header buffer */
+ int fields) /* bitmask of fields to log */
+{
+ int first; /* first byte number */
+ int last; /* last byte number */
+ static const short offsets[] = { /* field starting offsets */
+ /* keep in sync with bit definitions */
+ offsetof(xfs_agi_t, agi_magicnum),
+ offsetof(xfs_agi_t, agi_versionnum),
+ offsetof(xfs_agi_t, agi_seqno),
+ offsetof(xfs_agi_t, agi_length),
+ offsetof(xfs_agi_t, agi_count),
+ offsetof(xfs_agi_t, agi_root),
+ offsetof(xfs_agi_t, agi_level),
+ offsetof(xfs_agi_t, agi_freecount),
+ offsetof(xfs_agi_t, agi_newino),
+ offsetof(xfs_agi_t, agi_dirino),
+ offsetof(xfs_agi_t, agi_unlinked),
+ sizeof(xfs_agi_t)
+ };
+#ifdef DEBUG
+ xfs_agi_t *agi; /* allocation group header */
+
+ agi = XFS_BUF_TO_AGI(bp);
+ ASSERT(INT_GET(agi->agi_magicnum, ARCH_CONVERT) == XFS_AGI_MAGIC);
+#endif
+ /*
+ * Compute byte offsets for the first and last fields.
+ */
+ xfs_btree_offsets(fields, offsets, XFS_AGI_NUM_BITS, &first, &last);
+ /*
+ * Log the allocation group inode header buffer.
+ */
+ xfs_trans_log_buf(tp, bp, first, last);
+}
+
+/*
+ * Read in the allocation group header (inode allocation section)
+ */
+int
+xfs_ialloc_read_agi(
+ xfs_mount_t *mp, /* file system mount structure */
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_agnumber_t agno, /* allocation group number */
+ xfs_buf_t **bpp) /* allocation group hdr buf */
+{
+ xfs_agi_t *agi; /* allocation group header */
+ int agi_ok; /* agi is consistent */
+ xfs_buf_t *bp; /* allocation group hdr buf */
+ xfs_perag_t *pag; /* per allocation group data */
+ int error;
+
+ ASSERT(agno != NULLAGNUMBER);
+ error = xfs_trans_read_buf(
+ mp, tp, mp->m_ddev_targp,
+ XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)),
+ XFS_FSS_TO_BB(mp, 1), 0, &bp);
+ if (error)
+ return error;
+ ASSERT(bp && !XFS_BUF_GETERROR(bp));
+
+ /*
+ * Validate the magic number of the agi block.
+ */
+ agi = XFS_BUF_TO_AGI(bp);
+ agi_ok =
+ INT_GET(agi->agi_magicnum, ARCH_CONVERT) == XFS_AGI_MAGIC &&
+ XFS_AGI_GOOD_VERSION(
+ INT_GET(agi->agi_versionnum, ARCH_CONVERT));
+ if (unlikely(XFS_TEST_ERROR(!agi_ok, mp, XFS_ERRTAG_IALLOC_READ_AGI,
+ XFS_RANDOM_IALLOC_READ_AGI))) {
+ XFS_CORRUPTION_ERROR("xfs_ialloc_read_agi", XFS_ERRLEVEL_LOW,
+ mp, agi);
+ xfs_trans_brelse(tp, bp);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ pag = &mp->m_perag[agno];
+ if (!pag->pagi_init) {
+ pag->pagi_freecount = INT_GET(agi->agi_freecount, ARCH_CONVERT);
+ pag->pagi_init = 1;
+ } else {
+ /*
+ * It's possible for these to be out of sync if
+ * we are in the middle of a forced shutdown.
+ */
+ ASSERT(pag->pagi_freecount ==
+ INT_GET(agi->agi_freecount, ARCH_CONVERT)
+ || XFS_FORCED_SHUTDOWN(mp));
+ }
+
+#ifdef DEBUG
+ {
+ int i;
+
+ for (i = 0; i < XFS_AGI_UNLINKED_BUCKETS; i++)
+ ASSERT(!INT_ISZERO(agi->agi_unlinked[i], ARCH_CONVERT));
+ }
+#endif
+
+ XFS_BUF_SET_VTYPE_REF(bp, B_FS_AGI, XFS_AGI_REF);
+ *bpp = bp;
+ return 0;
+}
diff --git a/fs/xfs/xfs_ialloc.h b/fs/xfs/xfs_ialloc.h
new file mode 100644
index 00000000000000..db6d0015cecfa9
--- /dev/null
+++ b/fs/xfs/xfs_ialloc.h
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_IALLOC_H__
+#define __XFS_IALLOC_H__
+
+struct xfs_buf;
+struct xfs_dinode;
+struct xfs_mount;
+struct xfs_trans;
+
+/*
+ * Allocation parameters for inode allocation.
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_IALLOC_INODES)
+int xfs_ialloc_inodes(struct xfs_mount *mp);
+#define XFS_IALLOC_INODES(mp) xfs_ialloc_inodes(mp)
+#else
+#define XFS_IALLOC_INODES(mp) ((mp)->m_ialloc_inos)
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_IALLOC_BLOCKS)
+xfs_extlen_t xfs_ialloc_blocks(struct xfs_mount *mp);
+#define XFS_IALLOC_BLOCKS(mp) xfs_ialloc_blocks(mp)
+#else
+#define XFS_IALLOC_BLOCKS(mp) ((mp)->m_ialloc_blks)
+#endif
+
+/*
+ * For small block file systems, move inodes in clusters of this size.
+ * When we don't have a lot of memory, however, we go a bit smaller
+ * to reduce the number of AGI and ialloc btree blocks we need to keep
+ * around for xfs_dilocate(). We choose which one to use in
+ * xfs_mount_int().
+ */
+#define XFS_INODE_BIG_CLUSTER_SIZE 8192
+#define XFS_INODE_SMALL_CLUSTER_SIZE 4096
+#define XFS_INODE_CLUSTER_SIZE(mp) (mp)->m_inode_cluster_size
+
+/*
+ * Make an inode pointer out of the buffer/offset.
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_MAKE_IPTR)
+struct xfs_dinode *xfs_make_iptr(struct xfs_mount *mp, struct xfs_buf *b, int o);
+#define XFS_MAKE_IPTR(mp,b,o) xfs_make_iptr(mp,b,o)
+#else
+#define XFS_MAKE_IPTR(mp,b,o) \
+ ((xfs_dinode_t *)(xfs_buf_offset(b, (o) << (mp)->m_sb.sb_inodelog)))
+#endif
+
+/*
+ * Find a free (set) bit in the inode bitmask.
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_IALLOC_FIND_FREE)
+int xfs_ialloc_find_free(xfs_inofree_t *fp);
+#define XFS_IALLOC_FIND_FREE(fp) xfs_ialloc_find_free(fp)
+#else
+#define XFS_IALLOC_FIND_FREE(fp) xfs_lowbit64(*(fp))
+#endif
+
+
+#ifdef __KERNEL__
+
+/*
+ * Prototypes for visible xfs_ialloc.c routines.
+ */
+
+/*
+ * Allocate an inode on disk.
+ * Mode is used to tell whether the new inode will need space, and whether
+ * it is a directory.
+ *
+ * To work within the constraint of one allocation per transaction,
+ * xfs_dialloc() is designed to be called twice if it has to do an
+ * allocation to make more free inodes. If an inode is
+ * available without an allocation, agbp would be set to the current
+ * agbp and alloc_done set to false.
+ * If an allocation needed to be done, agbp would be set to the
+ * inode header of the allocation group and alloc_done set to true.
+ * The caller should then commit the current transaction and allocate a new
+ * transaction. xfs_dialloc() should then be called again with
+ * the agbp value returned from the previous call.
+ *
+ * Once we successfully pick an inode its number is returned and the
+ * on-disk data structures are updated. The inode itself is not read
+ * in, since doing so would break ordering constraints with xfs_reclaim.
+ *
+ * *agbp should be set to NULL on the first call, *alloc_done set to FALSE.
+ */
+int /* error */
+xfs_dialloc(
+ struct xfs_trans *tp, /* transaction pointer */
+ xfs_ino_t parent, /* parent inode (directory) */
+ mode_t mode, /* mode bits for new inode */
+ int okalloc, /* ok to allocate more space */
+ struct xfs_buf **agbp, /* buf for a.g. inode header */
+ boolean_t *alloc_done, /* an allocation was done to replenish
+ the free inodes */
+ xfs_ino_t *inop); /* inode number allocated */
+
+/*
+ * Free disk inode. Carefully avoids touching the incore inode, all
+ * manipulations incore are the caller's responsibility.
+ * The on-disk inode is not changed by this operation, only the
+ * btree (free inode mask) is changed.
+ */
+int /* error */
+xfs_difree(
+ struct xfs_trans *tp, /* transaction pointer */
+ xfs_ino_t inode, /* inode to be freed */
+ struct xfs_bmap_free *flist, /* extents to free */
+ int *delete, /* set if inode cluster was deleted */
+ xfs_ino_t *first_ino); /* first inode in deleted cluster */
+
+/*
+ * Return the location of the inode in bno/len/off,
+ * for mapping it into a buffer.
+ */
+int
+xfs_dilocate(
+ struct xfs_mount *mp, /* file system mount structure */
+ struct xfs_trans *tp, /* transaction pointer */
+ xfs_ino_t ino, /* inode to locate */
+ xfs_fsblock_t *bno, /* output: block containing inode */
+ int *len, /* output: num blocks in cluster*/
+ int *off, /* output: index in block of inode */
+ uint flags); /* flags for inode btree lookup */
+
+/*
+ * Compute and fill in value of m_in_maxlevels.
+ */
+void
+xfs_ialloc_compute_maxlevels(
+ struct xfs_mount *mp); /* file system mount structure */
+
+/*
+ * Log specified fields for the ag hdr (inode section)
+ */
+void
+xfs_ialloc_log_agi(
+ struct xfs_trans *tp, /* transaction pointer */
+ struct xfs_buf *bp, /* allocation group header buffer */
+ int fields); /* bitmask of fields to log */
+
+/*
+ * Read in the allocation group header (inode allocation section)
+ */
+int /* error */
+xfs_ialloc_read_agi(
+ struct xfs_mount *mp, /* file system mount structure */
+ struct xfs_trans *tp, /* transaction pointer */
+ xfs_agnumber_t agno, /* allocation group number */
+ struct xfs_buf **bpp); /* allocation group hdr buf */
+
+#endif /* __KERNEL__ */
+
+#endif /* __XFS_IALLOC_H__ */
diff --git a/fs/xfs/xfs_ialloc_btree.c b/fs/xfs/xfs_ialloc_btree.c
new file mode 100644
index 00000000000000..804fc4bc3711fc
--- /dev/null
+++ b/fs/xfs/xfs_ialloc_btree.c
@@ -0,0 +1,2102 @@
+/*
+ * Copyright (c) 2000-2001 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_alloc.h"
+#include "xfs_error.h"
+
+/*
+ * Inode allocation management for XFS.
+ */
+
+/*
+ * Prototypes for internal functions.
+ */
+
+STATIC void xfs_inobt_log_block(xfs_trans_t *, xfs_buf_t *, int);
+STATIC void xfs_inobt_log_keys(xfs_btree_cur_t *, xfs_buf_t *, int, int);
+STATIC void xfs_inobt_log_ptrs(xfs_btree_cur_t *, xfs_buf_t *, int, int);
+STATIC void xfs_inobt_log_recs(xfs_btree_cur_t *, xfs_buf_t *, int, int);
+STATIC int xfs_inobt_lshift(xfs_btree_cur_t *, int, int *);
+STATIC int xfs_inobt_newroot(xfs_btree_cur_t *, int *);
+STATIC int xfs_inobt_rshift(xfs_btree_cur_t *, int, int *);
+STATIC int xfs_inobt_split(xfs_btree_cur_t *, int, xfs_agblock_t *,
+ xfs_inobt_key_t *, xfs_btree_cur_t **, int *);
+STATIC int xfs_inobt_updkey(xfs_btree_cur_t *, xfs_inobt_key_t *, int);
+
+/*
+ * Internal functions.
+ */
+
+/*
+ * Single level of the xfs_inobt_delete record deletion routine.
+ * Delete record pointed to by cur/level.
+ * Remove the record from its block then rebalance the tree.
+ * Return 0 for error, 1 for done, 2 to go on to the next level.
+ */
+STATIC int /* error */
+xfs_inobt_delrec(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ int level, /* level removing record from */
+ int *stat) /* fail/done/go-on */
+{
+ xfs_buf_t *agbp; /* buffer for a.g. inode header */
+ xfs_mount_t *mp; /* mount structure */
+ xfs_agi_t *agi; /* allocation group inode header */
+ xfs_inobt_block_t *block; /* btree block record/key lives in */
+ xfs_agblock_t bno; /* btree block number */
+ xfs_buf_t *bp; /* buffer for block */
+ int error; /* error return value */
+ int i; /* loop index */
+ xfs_inobt_key_t key; /* kp points here if block is level 0 */
+ xfs_inobt_key_t *kp = NULL; /* pointer to btree keys */
+ xfs_agblock_t lbno; /* left block's block number */
+ xfs_buf_t *lbp; /* left block's buffer pointer */
+ xfs_inobt_block_t *left; /* left btree block */
+ xfs_inobt_key_t *lkp; /* left block key pointer */
+ xfs_inobt_ptr_t *lpp; /* left block address pointer */
+ int lrecs = 0; /* number of records in left block */
+ xfs_inobt_rec_t *lrp; /* left block record pointer */
+ xfs_inobt_ptr_t *pp = NULL; /* pointer to btree addresses */
+ int ptr; /* index in btree block for this rec */
+ xfs_agblock_t rbno; /* right block's block number */
+ xfs_buf_t *rbp; /* right block's buffer pointer */
+ xfs_inobt_block_t *right; /* right btree block */
+ xfs_inobt_key_t *rkp; /* right block key pointer */
+ xfs_inobt_rec_t *rp; /* pointer to btree records */
+ xfs_inobt_ptr_t *rpp; /* right block address pointer */
+ int rrecs = 0; /* number of records in right block */
+ int numrecs;
+ xfs_inobt_rec_t *rrp; /* right block record pointer */
+ xfs_btree_cur_t *tcur; /* temporary btree cursor */
+
+ mp = cur->bc_mp;
+
+ /*
+ * Get the index of the entry being deleted, check for nothing there.
+ */
+ ptr = cur->bc_ptrs[level];
+ if (ptr == 0) {
+ *stat = 0;
+ return 0;
+ }
+
+ /*
+ * Get the buffer & block containing the record or key/ptr.
+ */
+ bp = cur->bc_bufs[level];
+ block = XFS_BUF_TO_INOBT_BLOCK(bp);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_sblock(cur, block, level, bp)))
+ return error;
+#endif
+ /*
+ * Fail if we're off the end of the block.
+ */
+
+ numrecs = INT_GET(block->bb_numrecs, ARCH_CONVERT);
+ if (ptr > numrecs) {
+ *stat = 0;
+ return 0;
+ }
+ /*
+ * It's a nonleaf. Excise the key and ptr being deleted, by
+ * sliding the entries past them down one.
+ * Log the changed areas of the block.
+ */
+ if (level > 0) {
+ kp = XFS_INOBT_KEY_ADDR(block, 1, cur);
+ pp = XFS_INOBT_PTR_ADDR(block, 1, cur);
+#ifdef DEBUG
+ for (i = ptr; i < numrecs; i++) {
+ if ((error = xfs_btree_check_sptr(cur, INT_GET(pp[i], ARCH_CONVERT), level)))
+ return error;
+ }
+#endif
+ if (ptr < numrecs) {
+ memmove(&kp[ptr - 1], &kp[ptr],
+ (numrecs - ptr) * sizeof(*kp));
+ memmove(&pp[ptr - 1], &pp[ptr],
+ (numrecs - ptr) * sizeof(*kp));
+ xfs_inobt_log_keys(cur, bp, ptr, numrecs - 1);
+ xfs_inobt_log_ptrs(cur, bp, ptr, numrecs - 1);
+ }
+ }
+ /*
+ * It's a leaf. Excise the record being deleted, by sliding the
+ * entries past it down one. Log the changed areas of the block.
+ */
+ else {
+ rp = XFS_INOBT_REC_ADDR(block, 1, cur);
+ if (ptr < numrecs) {
+ memmove(&rp[ptr - 1], &rp[ptr],
+ (numrecs - ptr) * sizeof(*rp));
+ xfs_inobt_log_recs(cur, bp, ptr, numrecs - 1);
+ }
+ /*
+ * If it's the first record in the block, we'll need a key
+ * structure to pass up to the next level (updkey).
+ */
+ if (ptr == 1) {
+ key.ir_startino = rp->ir_startino;
+ kp = &key;
+ }
+ }
+ /*
+ * Decrement and log the number of entries in the block.
+ */
+ numrecs--;
+ INT_SET(block->bb_numrecs, ARCH_CONVERT, numrecs);
+ xfs_inobt_log_block(cur->bc_tp, bp, XFS_BB_NUMRECS);
+ /*
+ * Is this the root level? If so, we're almost done.
+ */
+ if (level == cur->bc_nlevels - 1) {
+ /*
+ * If this is the root level,
+ * and there's only one entry left,
+ * and it's NOT the leaf level,
+ * then we can get rid of this level.
+ */
+ if (numrecs == 1 && level > 0) {
+ agbp = cur->bc_private.i.agbp;
+ agi = XFS_BUF_TO_AGI(agbp);
+ /*
+ * pp is still set to the first pointer in the block.
+ * Make it the new root of the btree.
+ */
+ bno = INT_GET(agi->agi_root, ARCH_CONVERT);
+ agi->agi_root = *pp;
+ INT_MOD(agi->agi_level, ARCH_CONVERT, -1);
+ /*
+ * Free the block.
+ */
+ if ((error = xfs_free_extent(cur->bc_tp,
+ XFS_AGB_TO_FSB(mp, cur->bc_private.i.agno, bno), 1)))
+ return error;
+ xfs_trans_binval(cur->bc_tp, bp);
+ xfs_ialloc_log_agi(cur->bc_tp, agbp,
+ XFS_AGI_ROOT | XFS_AGI_LEVEL);
+ /*
+ * Update the cursor so there's one fewer level.
+ */
+ cur->bc_bufs[level] = NULL;
+ cur->bc_nlevels--;
+ } else if (level > 0 &&
+ (error = xfs_inobt_decrement(cur, level, &i)))
+ return error;
+ *stat = 1;
+ return 0;
+ }
+ /*
+ * If we deleted the leftmost entry in the block, update the
+ * key values above us in the tree.
+ */
+ if (ptr == 1 && (error = xfs_inobt_updkey(cur, kp, level + 1)))
+ return error;
+ /*
+ * If the number of records remaining in the block is at least
+ * the minimum, we're done.
+ */
+ if (numrecs >= XFS_INOBT_BLOCK_MINRECS(level, cur)) {
+ if (level > 0 &&
+ (error = xfs_inobt_decrement(cur, level, &i)))
+ return error;
+ *stat = 1;
+ return 0;
+ }
+ /*
+ * Otherwise, we have to move some records around to keep the
+ * tree balanced. Look at the left and right sibling blocks to
+ * see if we can re-balance by moving only one record.
+ */
+ rbno = INT_GET(block->bb_rightsib, ARCH_CONVERT);
+ lbno = INT_GET(block->bb_leftsib, ARCH_CONVERT);
+ bno = NULLAGBLOCK;
+ ASSERT(rbno != NULLAGBLOCK || lbno != NULLAGBLOCK);
+ /*
+ * Duplicate the cursor so our btree manipulations here won't
+ * disrupt the next level up.
+ */
+ if ((error = xfs_btree_dup_cursor(cur, &tcur)))
+ return error;
+ /*
+ * If there's a right sibling, see if it's ok to shift an entry
+ * out of it.
+ */
+ if (rbno != NULLAGBLOCK) {
+ /*
+ * Move the temp cursor to the last entry in the next block.
+ * Actually any entry but the first would suffice.
+ */
+ i = xfs_btree_lastrec(tcur, level);
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ if ((error = xfs_inobt_increment(tcur, level, &i)))
+ goto error0;
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ i = xfs_btree_lastrec(tcur, level);
+ XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
+ /*
+ * Grab a pointer to the block.
+ */
+ rbp = tcur->bc_bufs[level];
+ right = XFS_BUF_TO_INOBT_BLOCK(rbp);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_sblock(cur, right, level, rbp)))
+ goto error0;
+#endif
+ /*
+ * Grab the current block number, for future use.
+ */
+ bno = INT_GET(right->bb_leftsib, ARCH_CONVERT);
+ /*
+ * If right block is full enough so that removing one entry
+ * won't make it too empty, and left-shifting an entry out
+ * of right to us works, we're done.
+ */
+ if (INT_GET(right->bb_numrecs, ARCH_CONVERT) - 1 >=
+ XFS_INOBT_BLOCK_MINRECS(level, cur)) {
+ if ((error = xfs_inobt_lshift(tcur, level, &i)))
+ goto error0;
+ if (i) {
+ ASSERT(INT_GET(block->bb_numrecs, ARCH_CONVERT) >=
+ XFS_INOBT_BLOCK_MINRECS(level, cur));
+ xfs_btree_del_cursor(tcur,
+ XFS_BTREE_NOERROR);
+ if (level > 0 &&
+ (error = xfs_inobt_decrement(cur, level,
+ &i)))
+ return error;
+ *stat = 1;
+ return 0;
+ }
+ }
+ /*
+ * Otherwise, grab the number of records in right for
+ * future reference, and fix up the temp cursor to point
+ * to our block again (last record).
+ */
+ rrecs = INT_GET(right->bb_numrecs, ARCH_CONVERT);
+ if (lbno != NULLAGBLOCK) {
+ xfs_btree_firstrec(tcur, level);
+ if ((error = xfs_inobt_decrement(tcur, level, &i)))
+ goto error0;
+ }
+ }
+ /*
+ * If there's a left sibling, see if it's ok to shift an entry
+ * out of it.
+ */
+ if (lbno != NULLAGBLOCK) {
+ /*
+ * Move the temp cursor to the first entry in the
+ * previous block.
+ */
+ xfs_btree_firstrec(tcur, level);
+ if ((error = xfs_inobt_decrement(tcur, level, &i)))
+ goto error0;
+ xfs_btree_firstrec(tcur, level);
+ /*
+ * Grab a pointer to the block.
+ */
+ lbp = tcur->bc_bufs[level];
+ left = XFS_BUF_TO_INOBT_BLOCK(lbp);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_sblock(cur, left, level, lbp)))
+ goto error0;
+#endif
+ /*
+ * Grab the current block number, for future use.
+ */
+ bno = INT_GET(left->bb_rightsib, ARCH_CONVERT);
+ /*
+ * If left block is full enough so that removing one entry
+ * won't make it too empty, and right-shifting an entry out
+ * of left to us works, we're done.
+ */
+ if (INT_GET(left->bb_numrecs, ARCH_CONVERT) - 1 >=
+ XFS_INOBT_BLOCK_MINRECS(level, cur)) {
+ if ((error = xfs_inobt_rshift(tcur, level, &i)))
+ goto error0;
+ if (i) {
+ ASSERT(INT_GET(block->bb_numrecs, ARCH_CONVERT) >=
+ XFS_INOBT_BLOCK_MINRECS(level, cur));
+ xfs_btree_del_cursor(tcur,
+ XFS_BTREE_NOERROR);
+ if (level == 0)
+ cur->bc_ptrs[0]++;
+ *stat = 1;
+ return 0;
+ }
+ }
+ /*
+ * Otherwise, grab the number of records in right for
+ * future reference.
+ */
+ lrecs = INT_GET(left->bb_numrecs, ARCH_CONVERT);
+ }
+ /*
+ * Delete the temp cursor, we're done with it.
+ */
+ xfs_btree_del_cursor(tcur, XFS_BTREE_NOERROR);
+ /*
+ * If here, we need to do a join to keep the tree balanced.
+ */
+ ASSERT(bno != NULLAGBLOCK);
+ /*
+ * See if we can join with the left neighbor block.
+ */
+ if (lbno != NULLAGBLOCK &&
+ lrecs + numrecs <= XFS_INOBT_BLOCK_MAXRECS(level, cur)) {
+ /*
+ * Set "right" to be the starting block,
+ * "left" to be the left neighbor.
+ */
+ rbno = bno;
+ right = block;
+ rrecs = INT_GET(right->bb_numrecs, ARCH_CONVERT);
+ rbp = bp;
+ if ((error = xfs_btree_read_bufs(mp, cur->bc_tp,
+ cur->bc_private.i.agno, lbno, 0, &lbp,
+ XFS_INO_BTREE_REF)))
+ return error;
+ left = XFS_BUF_TO_INOBT_BLOCK(lbp);
+ lrecs = INT_GET(left->bb_numrecs, ARCH_CONVERT);
+ if ((error = xfs_btree_check_sblock(cur, left, level, lbp)))
+ return error;
+ }
+ /*
+ * If that won't work, see if we can join with the right neighbor block.
+ */
+ else if (rbno != NULLAGBLOCK &&
+ rrecs + numrecs <= XFS_INOBT_BLOCK_MAXRECS(level, cur)) {
+ /*
+ * Set "left" to be the starting block,
+ * "right" to be the right neighbor.
+ */
+ lbno = bno;
+ left = block;
+ lrecs = INT_GET(left->bb_numrecs, ARCH_CONVERT);
+ lbp = bp;
+ if ((error = xfs_btree_read_bufs(mp, cur->bc_tp,
+ cur->bc_private.i.agno, rbno, 0, &rbp,
+ XFS_INO_BTREE_REF)))
+ return error;
+ right = XFS_BUF_TO_INOBT_BLOCK(rbp);
+ rrecs = INT_GET(right->bb_numrecs, ARCH_CONVERT);
+ if ((error = xfs_btree_check_sblock(cur, right, level, rbp)))
+ return error;
+ }
+ /*
+ * Otherwise, we can't fix the imbalance.
+ * Just return. This is probably a logic error, but it's not fatal.
+ */
+ else {
+ if (level > 0 && (error = xfs_inobt_decrement(cur, level, &i)))
+ return error;
+ *stat = 1;
+ return 0;
+ }
+ /*
+ * We're now going to join "left" and "right" by moving all the stuff
+ * in "right" to "left" and deleting "right".
+ */
+ if (level > 0) {
+ /*
+ * It's a non-leaf. Move keys and pointers.
+ */
+ lkp = XFS_INOBT_KEY_ADDR(left, lrecs + 1, cur);
+ lpp = XFS_INOBT_PTR_ADDR(left, lrecs + 1, cur);
+ rkp = XFS_INOBT_KEY_ADDR(right, 1, cur);
+ rpp = XFS_INOBT_PTR_ADDR(right, 1, cur);
+#ifdef DEBUG
+ for (i = 0; i < rrecs; i++) {
+ if ((error = xfs_btree_check_sptr(cur, INT_GET(rpp[i], ARCH_CONVERT), level)))
+ return error;
+ }
+#endif
+ memcpy(lkp, rkp, rrecs * sizeof(*lkp));
+ memcpy(lpp, rpp, rrecs * sizeof(*lpp));
+ xfs_inobt_log_keys(cur, lbp, lrecs + 1, lrecs + rrecs);
+ xfs_inobt_log_ptrs(cur, lbp, lrecs + 1, lrecs + rrecs);
+ } else {
+ /*
+ * It's a leaf. Move records.
+ */
+ lrp = XFS_INOBT_REC_ADDR(left, lrecs + 1, cur);
+ rrp = XFS_INOBT_REC_ADDR(right, 1, cur);
+ memcpy(lrp, rrp, rrecs * sizeof(*lrp));
+ xfs_inobt_log_recs(cur, lbp, lrecs + 1, lrecs + rrecs);
+ }
+ /*
+ * If we joined with the left neighbor, set the buffer in the
+ * cursor to the left block, and fix up the index.
+ */
+ if (bp != lbp) {
+ xfs_btree_setbuf(cur, level, lbp);
+ cur->bc_ptrs[level] += lrecs;
+ }
+ /*
+ * If we joined with the right neighbor and there's a level above
+ * us, increment the cursor at that level.
+ */
+ else if (level + 1 < cur->bc_nlevels &&
+ (error = xfs_alloc_increment(cur, level + 1, &i)))
+ return error;
+ /*
+ * Fix up the number of records in the surviving block.
+ */
+ lrecs += rrecs;
+ INT_SET(left->bb_numrecs, ARCH_CONVERT, lrecs);
+ /*
+ * Fix up the right block pointer in the surviving block, and log it.
+ */
+ left->bb_rightsib = right->bb_rightsib;
+ xfs_inobt_log_block(cur->bc_tp, lbp, XFS_BB_NUMRECS | XFS_BB_RIGHTSIB);
+ /*
+ * If there is a right sibling now, make it point to the
+ * remaining block.
+ */
+ if (INT_GET(left->bb_rightsib, ARCH_CONVERT) != NULLAGBLOCK) {
+ xfs_inobt_block_t *rrblock;
+ xfs_buf_t *rrbp;
+
+ if ((error = xfs_btree_read_bufs(mp, cur->bc_tp,
+ cur->bc_private.i.agno, INT_GET(left->bb_rightsib, ARCH_CONVERT), 0,
+ &rrbp, XFS_INO_BTREE_REF)))
+ return error;
+ rrblock = XFS_BUF_TO_INOBT_BLOCK(rrbp);
+ if ((error = xfs_btree_check_sblock(cur, rrblock, level, rrbp)))
+ return error;
+ INT_SET(rrblock->bb_leftsib, ARCH_CONVERT, lbno);
+ xfs_inobt_log_block(cur->bc_tp, rrbp, XFS_BB_LEFTSIB);
+ }
+ /*
+ * Free the deleting block.
+ */
+ if ((error = xfs_free_extent(cur->bc_tp, XFS_AGB_TO_FSB(mp,
+ cur->bc_private.i.agno, rbno), 1)))
+ return error;
+ xfs_trans_binval(cur->bc_tp, rbp);
+ /*
+ * Readjust the ptr at this level if it's not a leaf, since it's
+ * still pointing at the deletion point, which makes the cursor
+ * inconsistent. If this makes the ptr 0, the caller fixes it up.
+ * We can't use decrement because it would change the next level up.
+ */
+ if (level > 0)
+ cur->bc_ptrs[level]--;
+ /*
+ * Return value means the next level up has something to do.
+ */
+ *stat = 2;
+ return 0;
+
+error0:
+ xfs_btree_del_cursor(tcur, XFS_BTREE_ERROR);
+ return error;
+}
+
+/*
+ * Insert one record/level. Return information to the caller
+ * allowing the next level up to proceed if necessary.
+ */
+STATIC int /* error */
+xfs_inobt_insrec(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ int level, /* level to insert record at */
+ xfs_agblock_t *bnop, /* i/o: block number inserted */
+ xfs_inobt_rec_t *recp, /* i/o: record data inserted */
+ xfs_btree_cur_t **curp, /* output: new cursor replacing cur */
+ int *stat) /* success/failure */
+{
+ xfs_inobt_block_t *block; /* btree block record/key lives in */
+ xfs_buf_t *bp; /* buffer for block */
+ int error; /* error return value */
+ int i; /* loop index */
+ xfs_inobt_key_t key; /* key value being inserted */
+ xfs_inobt_key_t *kp=NULL; /* pointer to btree keys */
+ xfs_agblock_t nbno; /* block number of allocated block */
+ xfs_btree_cur_t *ncur; /* new cursor to be used at next lvl */
+ xfs_inobt_key_t nkey; /* new key value, from split */
+ xfs_inobt_rec_t nrec; /* new record value, for caller */
+ int numrecs;
+ int optr; /* old ptr value */
+ xfs_inobt_ptr_t *pp; /* pointer to btree addresses */
+ int ptr; /* index in btree block for this rec */
+ xfs_inobt_rec_t *rp=NULL; /* pointer to btree records */
+
+ /*
+ * If we made it to the root level, allocate a new root block
+ * and we're done.
+ */
+ if (level >= cur->bc_nlevels) {
+ error = xfs_inobt_newroot(cur, &i);
+ *bnop = NULLAGBLOCK;
+ *stat = i;
+ return error;
+ }
+ /*
+ * Make a key out of the record data to be inserted, and save it.
+ */
+ key.ir_startino = recp->ir_startino; /* INT_: direct copy */
+ optr = ptr = cur->bc_ptrs[level];
+ /*
+ * If we're off the left edge, return failure.
+ */
+ if (ptr == 0) {
+ *stat = 0;
+ return 0;
+ }
+ /*
+ * Get pointers to the btree buffer and block.
+ */
+ bp = cur->bc_bufs[level];
+ block = XFS_BUF_TO_INOBT_BLOCK(bp);
+ numrecs = INT_GET(block->bb_numrecs, ARCH_CONVERT);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_sblock(cur, block, level, bp)))
+ return error;
+ /*
+ * Check that the new entry is being inserted in the right place.
+ */
+ if (ptr <= numrecs) {
+ if (level == 0) {
+ rp = XFS_INOBT_REC_ADDR(block, ptr, cur);
+ xfs_btree_check_rec(cur->bc_btnum, recp, rp);
+ } else {
+ kp = XFS_INOBT_KEY_ADDR(block, ptr, cur);
+ xfs_btree_check_key(cur->bc_btnum, &key, kp);
+ }
+ }
+#endif
+ nbno = NULLAGBLOCK;
+ ncur = (xfs_btree_cur_t *)0;
+ /*
+ * If the block is full, we can't insert the new entry until we
+ * make the block un-full.
+ */
+ if (numrecs == XFS_INOBT_BLOCK_MAXRECS(level, cur)) {
+ /*
+ * First, try shifting an entry to the right neighbor.
+ */
+ if ((error = xfs_inobt_rshift(cur, level, &i)))
+ return error;
+ if (i) {
+ /* nothing */
+ }
+ /*
+ * Next, try shifting an entry to the left neighbor.
+ */
+ else {
+ if ((error = xfs_inobt_lshift(cur, level, &i)))
+ return error;
+ if (i) {
+ optr = ptr = cur->bc_ptrs[level];
+ } else {
+ /*
+ * Next, try splitting the current block
+ * in half. If this works we have to
+ * re-set our variables because
+ * we could be in a different block now.
+ */
+ if ((error = xfs_inobt_split(cur, level, &nbno,
+ &nkey, &ncur, &i)))
+ return error;
+ if (i) {
+ bp = cur->bc_bufs[level];
+ block = XFS_BUF_TO_INOBT_BLOCK(bp);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_sblock(cur,
+ block, level, bp)))
+ return error;
+#endif
+ ptr = cur->bc_ptrs[level];
+ nrec.ir_startino = nkey.ir_startino; /* INT_: direct copy */
+ } else {
+ /*
+ * Otherwise the insert fails.
+ */
+ *stat = 0;
+ return 0;
+ }
+ }
+ }
+ }
+ /*
+ * At this point we know there's room for our new entry in the block
+ * we're pointing at.
+ */
+ numrecs = INT_GET(block->bb_numrecs, ARCH_CONVERT);
+ if (level > 0) {
+ /*
+ * It's a non-leaf entry. Make a hole for the new data
+ * in the key and ptr regions of the block.
+ */
+ kp = XFS_INOBT_KEY_ADDR(block, 1, cur);
+ pp = XFS_INOBT_PTR_ADDR(block, 1, cur);
+#ifdef DEBUG
+ for (i = numrecs; i >= ptr; i--) {
+ if ((error = xfs_btree_check_sptr(cur, INT_GET(pp[i - 1], ARCH_CONVERT), level)))
+ return error;
+ }
+#endif
+ memmove(&kp[ptr], &kp[ptr - 1],
+ (numrecs - ptr + 1) * sizeof(*kp));
+ memmove(&pp[ptr], &pp[ptr - 1],
+ (numrecs - ptr + 1) * sizeof(*pp));
+ /*
+ * Now stuff the new data in, bump numrecs and log the new data.
+ */
+#ifdef DEBUG
+ if ((error = xfs_btree_check_sptr(cur, *bnop, level)))
+ return error;
+#endif
+ kp[ptr - 1] = key; /* INT_: struct copy */
+ INT_SET(pp[ptr - 1], ARCH_CONVERT, *bnop);
+ numrecs++;
+ INT_SET(block->bb_numrecs, ARCH_CONVERT, numrecs);
+ xfs_inobt_log_keys(cur, bp, ptr, numrecs);
+ xfs_inobt_log_ptrs(cur, bp, ptr, numrecs);
+ } else {
+ /*
+ * It's a leaf entry. Make a hole for the new record.
+ */
+ rp = XFS_INOBT_REC_ADDR(block, 1, cur);
+ memmove(&rp[ptr], &rp[ptr - 1],
+ (numrecs - ptr + 1) * sizeof(*rp));
+ /*
+ * Now stuff the new record in, bump numrecs
+ * and log the new data.
+ */
+ rp[ptr - 1] = *recp; /* INT_: struct copy */
+ numrecs++;
+ INT_SET(block->bb_numrecs, ARCH_CONVERT, numrecs);
+ xfs_inobt_log_recs(cur, bp, ptr, numrecs);
+ }
+ /*
+ * Log the new number of records in the btree header.
+ */
+ xfs_inobt_log_block(cur->bc_tp, bp, XFS_BB_NUMRECS);
+#ifdef DEBUG
+ /*
+ * Check that the key/record is in the right place, now.
+ */
+ if (ptr < numrecs) {
+ if (level == 0)
+ xfs_btree_check_rec(cur->bc_btnum, rp + ptr - 1,
+ rp + ptr);
+ else
+ xfs_btree_check_key(cur->bc_btnum, kp + ptr - 1,
+ kp + ptr);
+ }
+#endif
+ /*
+ * If we inserted at the start of a block, update the parents' keys.
+ */
+ if (optr == 1 && (error = xfs_inobt_updkey(cur, &key, level + 1)))
+ return error;
+ /*
+ * Return the new block number, if any.
+ * If there is one, give back a record value and a cursor too.
+ */
+ *bnop = nbno;
+ if (nbno != NULLAGBLOCK) {
+ *recp = nrec; /* INT_: struct copy */
+ *curp = ncur;
+ }
+ *stat = 1;
+ return 0;
+}
+
+/*
+ * Log header fields from a btree block.
+ */
+STATIC void
+xfs_inobt_log_block(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_buf_t *bp, /* buffer containing btree block */
+ int fields) /* mask of fields: XFS_BB_... */
+{
+ int first; /* first byte offset logged */
+ int last; /* last byte offset logged */
+ static const short offsets[] = { /* table of offsets */
+ offsetof(xfs_inobt_block_t, bb_magic),
+ offsetof(xfs_inobt_block_t, bb_level),
+ offsetof(xfs_inobt_block_t, bb_numrecs),
+ offsetof(xfs_inobt_block_t, bb_leftsib),
+ offsetof(xfs_inobt_block_t, bb_rightsib),
+ sizeof(xfs_inobt_block_t)
+ };
+
+ xfs_btree_offsets(fields, offsets, XFS_BB_NUM_BITS, &first, &last);
+ xfs_trans_log_buf(tp, bp, first, last);
+}
+
+/*
+ * Log keys from a btree block (nonleaf).
+ */
+STATIC void
+xfs_inobt_log_keys(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ xfs_buf_t *bp, /* buffer containing btree block */
+ int kfirst, /* index of first key to log */
+ int klast) /* index of last key to log */
+{
+ xfs_inobt_block_t *block; /* btree block to log from */
+ int first; /* first byte offset logged */
+ xfs_inobt_key_t *kp; /* key pointer in btree block */
+ int last; /* last byte offset logged */
+
+ block = XFS_BUF_TO_INOBT_BLOCK(bp);
+ kp = XFS_INOBT_KEY_ADDR(block, 1, cur);
+ first = (int)((xfs_caddr_t)&kp[kfirst - 1] - (xfs_caddr_t)block);
+ last = (int)(((xfs_caddr_t)&kp[klast] - 1) - (xfs_caddr_t)block);
+ xfs_trans_log_buf(cur->bc_tp, bp, first, last);
+}
+
+/*
+ * Log block pointer fields from a btree block (nonleaf).
+ */
+STATIC void
+xfs_inobt_log_ptrs(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ xfs_buf_t *bp, /* buffer containing btree block */
+ int pfirst, /* index of first pointer to log */
+ int plast) /* index of last pointer to log */
+{
+ xfs_inobt_block_t *block; /* btree block to log from */
+ int first; /* first byte offset logged */
+ int last; /* last byte offset logged */
+ xfs_inobt_ptr_t *pp; /* block-pointer pointer in btree blk */
+
+ block = XFS_BUF_TO_INOBT_BLOCK(bp);
+ pp = XFS_INOBT_PTR_ADDR(block, 1, cur);
+ first = (int)((xfs_caddr_t)&pp[pfirst - 1] - (xfs_caddr_t)block);
+ last = (int)(((xfs_caddr_t)&pp[plast] - 1) - (xfs_caddr_t)block);
+ xfs_trans_log_buf(cur->bc_tp, bp, first, last);
+}
+
+/*
+ * Log records from a btree block (leaf).
+ */
+STATIC void
+xfs_inobt_log_recs(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ xfs_buf_t *bp, /* buffer containing btree block */
+ int rfirst, /* index of first record to log */
+ int rlast) /* index of last record to log */
+{
+ xfs_inobt_block_t *block; /* btree block to log from */
+ int first; /* first byte offset logged */
+ int last; /* last byte offset logged */
+ xfs_inobt_rec_t *rp; /* record pointer for btree block */
+
+ block = XFS_BUF_TO_INOBT_BLOCK(bp);
+ rp = XFS_INOBT_REC_ADDR(block, 1, cur);
+ first = (int)((xfs_caddr_t)&rp[rfirst - 1] - (xfs_caddr_t)block);
+ last = (int)(((xfs_caddr_t)&rp[rlast] - 1) - (xfs_caddr_t)block);
+ xfs_trans_log_buf(cur->bc_tp, bp, first, last);
+}
+
+/*
+ * Lookup the record. The cursor is made to point to it, based on dir.
+ * Return 0 if can't find any such record, 1 for success.
+ */
+STATIC int /* error */
+xfs_inobt_lookup(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ xfs_lookup_t dir, /* <=, ==, or >= */
+ int *stat) /* success/failure */
+{
+ xfs_agblock_t agbno; /* a.g. relative btree block number */
+ xfs_agnumber_t agno; /* allocation group number */
+ xfs_inobt_block_t *block=NULL; /* current btree block */
+ __int64_t diff; /* difference for the current key */
+ int error; /* error return value */
+ int keyno=0; /* current key number */
+ int level; /* level in the btree */
+ xfs_mount_t *mp; /* file system mount point */
+
+ /*
+ * Get the allocation group header, and the root block number.
+ */
+ mp = cur->bc_mp;
+ {
+ xfs_agi_t *agi; /* a.g. inode header */
+
+ agi = XFS_BUF_TO_AGI(cur->bc_private.i.agbp);
+ agno = INT_GET(agi->agi_seqno, ARCH_CONVERT);
+ agbno = INT_GET(agi->agi_root, ARCH_CONVERT);
+ }
+ /*
+ * Iterate over each level in the btree, starting at the root.
+ * For each level above the leaves, find the key we need, based
+ * on the lookup record, then follow the corresponding block
+ * pointer down to the next level.
+ */
+ for (level = cur->bc_nlevels - 1, diff = 1; level >= 0; level--) {
+ xfs_buf_t *bp; /* buffer pointer for btree block */
+ xfs_daddr_t d; /* disk address of btree block */
+
+ /*
+ * Get the disk address we're looking for.
+ */
+ d = XFS_AGB_TO_DADDR(mp, agno, agbno);
+ /*
+ * If the old buffer at this level is for a different block,
+ * throw it away, otherwise just use it.
+ */
+ bp = cur->bc_bufs[level];
+ if (bp && XFS_BUF_ADDR(bp) != d)
+ bp = (xfs_buf_t *)0;
+ if (!bp) {
+ /*
+ * Need to get a new buffer. Read it, then
+ * set it in the cursor, releasing the old one.
+ */
+ if ((error = xfs_btree_read_bufs(mp, cur->bc_tp,
+ agno, agbno, 0, &bp, XFS_INO_BTREE_REF)))
+ return error;
+ xfs_btree_setbuf(cur, level, bp);
+ /*
+ * Point to the btree block, now that we have the buffer
+ */
+ block = XFS_BUF_TO_INOBT_BLOCK(bp);
+ if ((error = xfs_btree_check_sblock(cur, block, level,
+ bp)))
+ return error;
+ } else
+ block = XFS_BUF_TO_INOBT_BLOCK(bp);
+ /*
+ * If we already had a key match at a higher level, we know
+ * we need to use the first entry in this block.
+ */
+ if (diff == 0)
+ keyno = 1;
+ /*
+ * Otherwise we need to search this block. Do a binary search.
+ */
+ else {
+ int high; /* high entry number */
+ xfs_inobt_key_t *kkbase=NULL;/* base of keys in block */
+ xfs_inobt_rec_t *krbase=NULL;/* base of records in block */
+ int low; /* low entry number */
+
+ /*
+ * Get a pointer to keys or records.
+ */
+ if (level > 0)
+ kkbase = XFS_INOBT_KEY_ADDR(block, 1, cur);
+ else
+ krbase = XFS_INOBT_REC_ADDR(block, 1, cur);
+ /*
+ * Set low and high entry numbers, 1-based.
+ */
+ low = 1;
+ if (!(high = INT_GET(block->bb_numrecs, ARCH_CONVERT))) {
+ /*
+ * If the block is empty, the tree must
+ * be an empty leaf.
+ */
+ ASSERT(level == 0 && cur->bc_nlevels == 1);
+ cur->bc_ptrs[0] = dir != XFS_LOOKUP_LE;
+ *stat = 0;
+ return 0;
+ }
+ /*
+ * Binary search the block.
+ */
+ while (low <= high) {
+ xfs_agino_t startino; /* key value */
+
+ /*
+ * keyno is average of low and high.
+ */
+ keyno = (low + high) >> 1;
+ /*
+ * Get startino.
+ */
+ if (level > 0) {
+ xfs_inobt_key_t *kkp;
+
+ kkp = kkbase + keyno - 1;
+ startino = INT_GET(kkp->ir_startino, ARCH_CONVERT);
+ } else {
+ xfs_inobt_rec_t *krp;
+
+ krp = krbase + keyno - 1;
+ startino = INT_GET(krp->ir_startino, ARCH_CONVERT);
+ }
+ /*
+ * Compute difference to get next direction.
+ */
+ diff = (__int64_t)
+ startino - cur->bc_rec.i.ir_startino;
+ /*
+ * Less than, move right.
+ */
+ if (diff < 0)
+ low = keyno + 1;
+ /*
+ * Greater than, move left.
+ */
+ else if (diff > 0)
+ high = keyno - 1;
+ /*
+ * Equal, we're done.
+ */
+ else
+ break;
+ }
+ }
+ /*
+ * If there are more levels, set up for the next level
+ * by getting the block number and filling in the cursor.
+ */
+ if (level > 0) {
+ /*
+ * If we moved left, need the previous key number,
+ * unless there isn't one.
+ */
+ if (diff > 0 && --keyno < 1)
+ keyno = 1;
+ agbno = INT_GET(*XFS_INOBT_PTR_ADDR(block, keyno, cur), ARCH_CONVERT);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_sptr(cur, agbno, level)))
+ return error;
+#endif
+ cur->bc_ptrs[level] = keyno;
+ }
+ }
+ /*
+ * Done with the search.
+ * See if we need to adjust the results.
+ */
+ if (dir != XFS_LOOKUP_LE && diff < 0) {
+ keyno++;
+ /*
+ * If ge search and we went off the end of the block, but it's
+ * not the last block, we're in the wrong block.
+ */
+ if (dir == XFS_LOOKUP_GE &&
+ keyno > INT_GET(block->bb_numrecs, ARCH_CONVERT) &&
+ INT_GET(block->bb_rightsib, ARCH_CONVERT) != NULLAGBLOCK) {
+ int i;
+
+ cur->bc_ptrs[0] = keyno;
+ if ((error = xfs_inobt_increment(cur, 0, &i)))
+ return error;
+ ASSERT(i == 1);
+ *stat = 1;
+ return 0;
+ }
+ }
+ else if (dir == XFS_LOOKUP_LE && diff > 0)
+ keyno--;
+ cur->bc_ptrs[0] = keyno;
+ /*
+ * Return if we succeeded or not.
+ */
+ if (keyno == 0 || keyno > INT_GET(block->bb_numrecs, ARCH_CONVERT))
+ *stat = 0;
+ else
+ *stat = ((dir != XFS_LOOKUP_EQ) || (diff == 0));
+ return 0;
+}
+
+/*
+ * Move 1 record left from cur/level if possible.
+ * Update cur to reflect the new path.
+ */
+STATIC int /* error */
+xfs_inobt_lshift(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ int level, /* level to shift record on */
+ int *stat) /* success/failure */
+{
+ int error; /* error return value */
+#ifdef DEBUG
+ int i; /* loop index */
+#endif
+ xfs_inobt_key_t key; /* key value for leaf level upward */
+ xfs_buf_t *lbp; /* buffer for left neighbor block */
+ xfs_inobt_block_t *left; /* left neighbor btree block */
+ xfs_inobt_key_t *lkp=NULL; /* key pointer for left block */
+ xfs_inobt_ptr_t *lpp; /* address pointer for left block */
+ xfs_inobt_rec_t *lrp=NULL; /* record pointer for left block */
+ int nrec; /* new number of left block entries */
+ xfs_buf_t *rbp; /* buffer for right (current) block */
+ xfs_inobt_block_t *right; /* right (current) btree block */
+ xfs_inobt_key_t *rkp=NULL; /* key pointer for right block */
+ xfs_inobt_ptr_t *rpp=NULL; /* address pointer for right block */
+ xfs_inobt_rec_t *rrp=NULL; /* record pointer for right block */
+
+ /*
+ * Set up variables for this block as "right".
+ */
+ rbp = cur->bc_bufs[level];
+ right = XFS_BUF_TO_INOBT_BLOCK(rbp);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_sblock(cur, right, level, rbp)))
+ return error;
+#endif
+ /*
+ * If we've got no left sibling then we can't shift an entry left.
+ */
+ if (INT_GET(right->bb_leftsib, ARCH_CONVERT) == NULLAGBLOCK) {
+ *stat = 0;
+ return 0;
+ }
+ /*
+ * If the cursor entry is the one that would be moved, don't
+ * do it... it's too complicated.
+ */
+ if (cur->bc_ptrs[level] <= 1) {
+ *stat = 0;
+ return 0;
+ }
+ /*
+ * Set up the left neighbor as "left".
+ */
+ if ((error = xfs_btree_read_bufs(cur->bc_mp, cur->bc_tp,
+ cur->bc_private.i.agno, INT_GET(right->bb_leftsib, ARCH_CONVERT), 0, &lbp,
+ XFS_INO_BTREE_REF)))
+ return error;
+ left = XFS_BUF_TO_INOBT_BLOCK(lbp);
+ if ((error = xfs_btree_check_sblock(cur, left, level, lbp)))
+ return error;
+ /*
+ * If it's full, it can't take another entry.
+ */
+ if (INT_GET(left->bb_numrecs, ARCH_CONVERT) == XFS_INOBT_BLOCK_MAXRECS(level, cur)) {
+ *stat = 0;
+ return 0;
+ }
+ nrec = INT_GET(left->bb_numrecs, ARCH_CONVERT) + 1;
+ /*
+ * If non-leaf, copy a key and a ptr to the left block.
+ */
+ if (level > 0) {
+ lkp = XFS_INOBT_KEY_ADDR(left, nrec, cur);
+ rkp = XFS_INOBT_KEY_ADDR(right, 1, cur);
+ *lkp = *rkp;
+ xfs_inobt_log_keys(cur, lbp, nrec, nrec);
+ lpp = XFS_INOBT_PTR_ADDR(left, nrec, cur);
+ rpp = XFS_INOBT_PTR_ADDR(right, 1, cur);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_sptr(cur, INT_GET(*rpp, ARCH_CONVERT), level)))
+ return error;
+#endif
+ *lpp = *rpp; /* INT_: no-change copy */
+ xfs_inobt_log_ptrs(cur, lbp, nrec, nrec);
+ }
+ /*
+ * If leaf, copy a record to the left block.
+ */
+ else {
+ lrp = XFS_INOBT_REC_ADDR(left, nrec, cur);
+ rrp = XFS_INOBT_REC_ADDR(right, 1, cur);
+ *lrp = *rrp;
+ xfs_inobt_log_recs(cur, lbp, nrec, nrec);
+ }
+ /*
+ * Bump and log left's numrecs, decrement and log right's numrecs.
+ */
+ INT_MOD(left->bb_numrecs, ARCH_CONVERT, +1);
+ xfs_inobt_log_block(cur->bc_tp, lbp, XFS_BB_NUMRECS);
+#ifdef DEBUG
+ if (level > 0)
+ xfs_btree_check_key(cur->bc_btnum, lkp - 1, lkp);
+ else
+ xfs_btree_check_rec(cur->bc_btnum, lrp - 1, lrp);
+#endif
+ INT_MOD(right->bb_numrecs, ARCH_CONVERT, -1);
+ xfs_inobt_log_block(cur->bc_tp, rbp, XFS_BB_NUMRECS);
+ /*
+ * Slide the contents of right down one entry.
+ */
+ if (level > 0) {
+#ifdef DEBUG
+ for (i = 0; i < INT_GET(right->bb_numrecs, ARCH_CONVERT); i++) {
+ if ((error = xfs_btree_check_sptr(cur, INT_GET(rpp[i + 1], ARCH_CONVERT),
+ level)))
+ return error;
+ }
+#endif
+ memmove(rkp, rkp + 1, INT_GET(right->bb_numrecs, ARCH_CONVERT) * sizeof(*rkp));
+ memmove(rpp, rpp + 1, INT_GET(right->bb_numrecs, ARCH_CONVERT) * sizeof(*rpp));
+ xfs_inobt_log_keys(cur, rbp, 1, INT_GET(right->bb_numrecs, ARCH_CONVERT));
+ xfs_inobt_log_ptrs(cur, rbp, 1, INT_GET(right->bb_numrecs, ARCH_CONVERT));
+ } else {
+ memmove(rrp, rrp + 1, INT_GET(right->bb_numrecs, ARCH_CONVERT) * sizeof(*rrp));
+ xfs_inobt_log_recs(cur, rbp, 1, INT_GET(right->bb_numrecs, ARCH_CONVERT));
+ key.ir_startino = rrp->ir_startino; /* INT_: direct copy */
+ rkp = &key;
+ }
+ /*
+ * Update the parent key values of right.
+ */
+ if ((error = xfs_inobt_updkey(cur, rkp, level + 1)))
+ return error;
+ /*
+ * Slide the cursor value left one.
+ */
+ cur->bc_ptrs[level]--;
+ *stat = 1;
+ return 0;
+}
+
+/*
+ * Allocate a new root block, fill it in.
+ */
+STATIC int /* error */
+xfs_inobt_newroot(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ int *stat) /* success/failure */
+{
+ xfs_agi_t *agi; /* a.g. inode header */
+ xfs_alloc_arg_t args; /* allocation argument structure */
+ xfs_inobt_block_t *block; /* one half of the old root block */
+ xfs_buf_t *bp; /* buffer containing block */
+ int error; /* error return value */
+ xfs_inobt_key_t *kp; /* btree key pointer */
+ xfs_agblock_t lbno; /* left block number */
+ xfs_buf_t *lbp; /* left buffer pointer */
+ xfs_inobt_block_t *left; /* left btree block */
+ xfs_buf_t *nbp; /* new (root) buffer */
+ xfs_inobt_block_t *new; /* new (root) btree block */
+ int nptr; /* new value for key index, 1 or 2 */
+ xfs_inobt_ptr_t *pp; /* btree address pointer */
+ xfs_agblock_t rbno; /* right block number */
+ xfs_buf_t *rbp; /* right buffer pointer */
+ xfs_inobt_block_t *right; /* right btree block */
+ xfs_inobt_rec_t *rp; /* btree record pointer */
+
+ ASSERT(cur->bc_nlevels < XFS_IN_MAXLEVELS(cur->bc_mp));
+
+ /*
+ * Get a block & a buffer.
+ */
+ agi = XFS_BUF_TO_AGI(cur->bc_private.i.agbp);
+ args.tp = cur->bc_tp;
+ args.mp = cur->bc_mp;
+ args.fsbno = XFS_AGB_TO_FSB(args.mp, cur->bc_private.i.agno,
+ INT_GET(agi->agi_root, ARCH_CONVERT));
+ args.mod = args.minleft = args.alignment = args.total = args.wasdel =
+ args.isfl = args.userdata = args.minalignslop = 0;
+ args.minlen = args.maxlen = args.prod = 1;
+ args.type = XFS_ALLOCTYPE_NEAR_BNO;
+ if ((error = xfs_alloc_vextent(&args)))
+ return error;
+ /*
+ * None available, we fail.
+ */
+ if (args.fsbno == NULLFSBLOCK) {
+ *stat = 0;
+ return 0;
+ }
+ ASSERT(args.len == 1);
+ nbp = xfs_btree_get_bufs(args.mp, args.tp, args.agno, args.agbno, 0);
+ new = XFS_BUF_TO_INOBT_BLOCK(nbp);
+ /*
+ * Set the root data in the a.g. inode structure.
+ */
+ INT_SET(agi->agi_root, ARCH_CONVERT, args.agbno);
+ INT_MOD(agi->agi_level, ARCH_CONVERT, 1);
+ xfs_ialloc_log_agi(args.tp, cur->bc_private.i.agbp,
+ XFS_AGI_ROOT | XFS_AGI_LEVEL);
+ /*
+ * At the previous root level there are now two blocks: the old
+ * root, and the new block generated when it was split.
+ * We don't know which one the cursor is pointing at, so we
+ * set up variables "left" and "right" for each case.
+ */
+ bp = cur->bc_bufs[cur->bc_nlevels - 1];
+ block = XFS_BUF_TO_INOBT_BLOCK(bp);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_sblock(cur, block, cur->bc_nlevels - 1, bp)))
+ return error;
+#endif
+ if (INT_GET(block->bb_rightsib, ARCH_CONVERT) != NULLAGBLOCK) {
+ /*
+ * Our block is left, pick up the right block.
+ */
+ lbp = bp;
+ lbno = XFS_DADDR_TO_AGBNO(args.mp, XFS_BUF_ADDR(lbp));
+ left = block;
+ rbno = INT_GET(left->bb_rightsib, ARCH_CONVERT);
+ if ((error = xfs_btree_read_bufs(args.mp, args.tp, args.agno,
+ rbno, 0, &rbp, XFS_INO_BTREE_REF)))
+ return error;
+ bp = rbp;
+ right = XFS_BUF_TO_INOBT_BLOCK(rbp);
+ if ((error = xfs_btree_check_sblock(cur, right,
+ cur->bc_nlevels - 1, rbp)))
+ return error;
+ nptr = 1;
+ } else {
+ /*
+ * Our block is right, pick up the left block.
+ */
+ rbp = bp;
+ rbno = XFS_DADDR_TO_AGBNO(args.mp, XFS_BUF_ADDR(rbp));
+ right = block;
+ lbno = INT_GET(right->bb_leftsib, ARCH_CONVERT);
+ if ((error = xfs_btree_read_bufs(args.mp, args.tp, args.agno,
+ lbno, 0, &lbp, XFS_INO_BTREE_REF)))
+ return error;
+ bp = lbp;
+ left = XFS_BUF_TO_INOBT_BLOCK(lbp);
+ if ((error = xfs_btree_check_sblock(cur, left,
+ cur->bc_nlevels - 1, lbp)))
+ return error;
+ nptr = 2;
+ }
+ /*
+ * Fill in the new block's btree header and log it.
+ */
+ INT_SET(new->bb_magic, ARCH_CONVERT, xfs_magics[cur->bc_btnum]);
+ INT_SET(new->bb_level, ARCH_CONVERT, (__uint16_t)cur->bc_nlevels);
+ INT_SET(new->bb_numrecs, ARCH_CONVERT, 2);
+ INT_SET(new->bb_leftsib, ARCH_CONVERT, NULLAGBLOCK);
+ INT_SET(new->bb_rightsib, ARCH_CONVERT, NULLAGBLOCK);
+ xfs_inobt_log_block(args.tp, nbp, XFS_BB_ALL_BITS);
+ ASSERT(lbno != NULLAGBLOCK && rbno != NULLAGBLOCK);
+ /*
+ * Fill in the key data in the new root.
+ */
+ kp = XFS_INOBT_KEY_ADDR(new, 1, cur);
+ if (INT_GET(left->bb_level, ARCH_CONVERT) > 0) {
+ kp[0] = *XFS_INOBT_KEY_ADDR(left, 1, cur); /* INT_: struct copy */
+ kp[1] = *XFS_INOBT_KEY_ADDR(right, 1, cur); /* INT_: struct copy */
+ } else {
+ rp = XFS_INOBT_REC_ADDR(left, 1, cur);
+ INT_COPY(kp[0].ir_startino, rp->ir_startino, ARCH_CONVERT);
+ rp = XFS_INOBT_REC_ADDR(right, 1, cur);
+ INT_COPY(kp[1].ir_startino, rp->ir_startino, ARCH_CONVERT);
+ }
+ xfs_inobt_log_keys(cur, nbp, 1, 2);
+ /*
+ * Fill in the pointer data in the new root.
+ */
+ pp = XFS_INOBT_PTR_ADDR(new, 1, cur);
+ INT_SET(pp[0], ARCH_CONVERT, lbno);
+ INT_SET(pp[1], ARCH_CONVERT, rbno);
+ xfs_inobt_log_ptrs(cur, nbp, 1, 2);
+ /*
+ * Fix up the cursor.
+ */
+ xfs_btree_setbuf(cur, cur->bc_nlevels, nbp);
+ cur->bc_ptrs[cur->bc_nlevels] = nptr;
+ cur->bc_nlevels++;
+ *stat = 1;
+ return 0;
+}
+
+/*
+ * Move 1 record right from cur/level if possible.
+ * Update cur to reflect the new path.
+ */
+STATIC int /* error */
+xfs_inobt_rshift(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ int level, /* level to shift record on */
+ int *stat) /* success/failure */
+{
+ int error; /* error return value */
+ int i; /* loop index */
+ xfs_inobt_key_t key; /* key value for leaf level upward */
+ xfs_buf_t *lbp; /* buffer for left (current) block */
+ xfs_inobt_block_t *left; /* left (current) btree block */
+ xfs_inobt_key_t *lkp; /* key pointer for left block */
+ xfs_inobt_ptr_t *lpp; /* address pointer for left block */
+ xfs_inobt_rec_t *lrp; /* record pointer for left block */
+ xfs_buf_t *rbp; /* buffer for right neighbor block */
+ xfs_inobt_block_t *right; /* right neighbor btree block */
+ xfs_inobt_key_t *rkp; /* key pointer for right block */
+ xfs_inobt_ptr_t *rpp; /* address pointer for right block */
+ xfs_inobt_rec_t *rrp=NULL; /* record pointer for right block */
+ xfs_btree_cur_t *tcur; /* temporary cursor */
+
+ /*
+ * Set up variables for this block as "left".
+ */
+ lbp = cur->bc_bufs[level];
+ left = XFS_BUF_TO_INOBT_BLOCK(lbp);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_sblock(cur, left, level, lbp)))
+ return error;
+#endif
+ /*
+ * If we've got no right sibling then we can't shift an entry right.
+ */
+ if (INT_GET(left->bb_rightsib, ARCH_CONVERT) == NULLAGBLOCK) {
+ *stat = 0;
+ return 0;
+ }
+ /*
+ * If the cursor entry is the one that would be moved, don't
+ * do it... it's too complicated.
+ */
+ if (cur->bc_ptrs[level] >= INT_GET(left->bb_numrecs, ARCH_CONVERT)) {
+ *stat = 0;
+ return 0;
+ }
+ /*
+ * Set up the right neighbor as "right".
+ */
+ if ((error = xfs_btree_read_bufs(cur->bc_mp, cur->bc_tp,
+ cur->bc_private.i.agno, INT_GET(left->bb_rightsib, ARCH_CONVERT), 0, &rbp,
+ XFS_INO_BTREE_REF)))
+ return error;
+ right = XFS_BUF_TO_INOBT_BLOCK(rbp);
+ if ((error = xfs_btree_check_sblock(cur, right, level, rbp)))
+ return error;
+ /*
+ * If it's full, it can't take another entry.
+ */
+ if (INT_GET(right->bb_numrecs, ARCH_CONVERT) == XFS_INOBT_BLOCK_MAXRECS(level, cur)) {
+ *stat = 0;
+ return 0;
+ }
+ /*
+ * Make a hole at the start of the right neighbor block, then
+ * copy the last left block entry to the hole.
+ */
+ if (level > 0) {
+ lkp = XFS_INOBT_KEY_ADDR(left, INT_GET(left->bb_numrecs, ARCH_CONVERT), cur);
+ lpp = XFS_INOBT_PTR_ADDR(left, INT_GET(left->bb_numrecs, ARCH_CONVERT), cur);
+ rkp = XFS_INOBT_KEY_ADDR(right, 1, cur);
+ rpp = XFS_INOBT_PTR_ADDR(right, 1, cur);
+#ifdef DEBUG
+ for (i = INT_GET(right->bb_numrecs, ARCH_CONVERT) - 1; i >= 0; i--) {
+ if ((error = xfs_btree_check_sptr(cur, INT_GET(rpp[i], ARCH_CONVERT), level)))
+ return error;
+ }
+#endif
+ memmove(rkp + 1, rkp, INT_GET(right->bb_numrecs, ARCH_CONVERT) * sizeof(*rkp));
+ memmove(rpp + 1, rpp, INT_GET(right->bb_numrecs, ARCH_CONVERT) * sizeof(*rpp));
+#ifdef DEBUG
+ if ((error = xfs_btree_check_sptr(cur, INT_GET(*lpp, ARCH_CONVERT), level)))
+ return error;
+#endif
+ *rkp = *lkp; /* INT_: no change copy */
+ *rpp = *lpp; /* INT_: no change copy */
+ xfs_inobt_log_keys(cur, rbp, 1, INT_GET(right->bb_numrecs, ARCH_CONVERT) + 1);
+ xfs_inobt_log_ptrs(cur, rbp, 1, INT_GET(right->bb_numrecs, ARCH_CONVERT) + 1);
+ } else {
+ lrp = XFS_INOBT_REC_ADDR(left, INT_GET(left->bb_numrecs, ARCH_CONVERT), cur);
+ rrp = XFS_INOBT_REC_ADDR(right, 1, cur);
+ memmove(rrp + 1, rrp, INT_GET(right->bb_numrecs, ARCH_CONVERT) * sizeof(*rrp));
+ *rrp = *lrp;
+ xfs_inobt_log_recs(cur, rbp, 1, INT_GET(right->bb_numrecs, ARCH_CONVERT) + 1);
+ key.ir_startino = rrp->ir_startino; /* INT_: direct copy */
+ rkp = &key;
+ }
+ /*
+ * Decrement and log left's numrecs, bump and log right's numrecs.
+ */
+ INT_MOD(left->bb_numrecs, ARCH_CONVERT, -1);
+ xfs_inobt_log_block(cur->bc_tp, lbp, XFS_BB_NUMRECS);
+ INT_MOD(right->bb_numrecs, ARCH_CONVERT, +1);
+#ifdef DEBUG
+ if (level > 0)
+ xfs_btree_check_key(cur->bc_btnum, rkp, rkp + 1);
+ else
+ xfs_btree_check_rec(cur->bc_btnum, rrp, rrp + 1);
+#endif
+ xfs_inobt_log_block(cur->bc_tp, rbp, XFS_BB_NUMRECS);
+ /*
+ * Using a temporary cursor, update the parent key values of the
+ * block on the right.
+ */
+ if ((error = xfs_btree_dup_cursor(cur, &tcur)))
+ return error;
+ xfs_btree_lastrec(tcur, level);
+ if ((error = xfs_inobt_increment(tcur, level, &i)) ||
+ (error = xfs_inobt_updkey(tcur, rkp, level + 1))) {
+ xfs_btree_del_cursor(tcur, XFS_BTREE_ERROR);
+ return error;
+ }
+ xfs_btree_del_cursor(tcur, XFS_BTREE_NOERROR);
+ *stat = 1;
+ return 0;
+}
+
+/*
+ * Split cur/level block in half.
+ * Return new block number and its first record (to be inserted into parent).
+ */
+STATIC int /* error */
+xfs_inobt_split(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ int level, /* level to split */
+ xfs_agblock_t *bnop, /* output: block number allocated */
+ xfs_inobt_key_t *keyp, /* output: first key of new block */
+ xfs_btree_cur_t **curp, /* output: new cursor */
+ int *stat) /* success/failure */
+{
+ xfs_alloc_arg_t args; /* allocation argument structure */
+ int error; /* error return value */
+ int i; /* loop index/record number */
+ xfs_agblock_t lbno; /* left (current) block number */
+ xfs_buf_t *lbp; /* buffer for left block */
+ xfs_inobt_block_t *left; /* left (current) btree block */
+ xfs_inobt_key_t *lkp; /* left btree key pointer */
+ xfs_inobt_ptr_t *lpp; /* left btree address pointer */
+ xfs_inobt_rec_t *lrp; /* left btree record pointer */
+ xfs_buf_t *rbp; /* buffer for right block */
+ xfs_inobt_block_t *right; /* right (new) btree block */
+ xfs_inobt_key_t *rkp; /* right btree key pointer */
+ xfs_inobt_ptr_t *rpp; /* right btree address pointer */
+ xfs_inobt_rec_t *rrp; /* right btree record pointer */
+
+ /*
+ * Set up left block (current one).
+ */
+ lbp = cur->bc_bufs[level];
+ args.tp = cur->bc_tp;
+ args.mp = cur->bc_mp;
+ lbno = XFS_DADDR_TO_AGBNO(args.mp, XFS_BUF_ADDR(lbp));
+ /*
+ * Allocate the new block.
+ * If we can't do it, we're toast. Give up.
+ */
+ args.fsbno = XFS_AGB_TO_FSB(args.mp, cur->bc_private.i.agno, lbno);
+ args.mod = args.minleft = args.alignment = args.total = args.wasdel =
+ args.isfl = args.userdata = args.minalignslop = 0;
+ args.minlen = args.maxlen = args.prod = 1;
+ args.type = XFS_ALLOCTYPE_NEAR_BNO;
+ if ((error = xfs_alloc_vextent(&args)))
+ return error;
+ if (args.fsbno == NULLFSBLOCK) {
+ *stat = 0;
+ return 0;
+ }
+ ASSERT(args.len == 1);
+ rbp = xfs_btree_get_bufs(args.mp, args.tp, args.agno, args.agbno, 0);
+ /*
+ * Set up the new block as "right".
+ */
+ right = XFS_BUF_TO_INOBT_BLOCK(rbp);
+ /*
+ * "Left" is the current (according to the cursor) block.
+ */
+ left = XFS_BUF_TO_INOBT_BLOCK(lbp);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_sblock(cur, left, level, lbp)))
+ return error;
+#endif
+ /*
+ * Fill in the btree header for the new block.
+ */
+ INT_SET(right->bb_magic, ARCH_CONVERT, xfs_magics[cur->bc_btnum]);
+ right->bb_level = left->bb_level; /* INT_: direct copy */
+ INT_SET(right->bb_numrecs, ARCH_CONVERT, (__uint16_t)(INT_GET(left->bb_numrecs, ARCH_CONVERT) / 2));
+ /*
+ * Make sure that if there's an odd number of entries now, that
+ * each new block will have the same number of entries.
+ */
+ if ((INT_GET(left->bb_numrecs, ARCH_CONVERT) & 1) &&
+ cur->bc_ptrs[level] <= INT_GET(right->bb_numrecs, ARCH_CONVERT) + 1)
+ INT_MOD(right->bb_numrecs, ARCH_CONVERT, +1);
+ i = INT_GET(left->bb_numrecs, ARCH_CONVERT) - INT_GET(right->bb_numrecs, ARCH_CONVERT) + 1;
+ /*
+ * For non-leaf blocks, copy keys and addresses over to the new block.
+ */
+ if (level > 0) {
+ lkp = XFS_INOBT_KEY_ADDR(left, i, cur);
+ lpp = XFS_INOBT_PTR_ADDR(left, i, cur);
+ rkp = XFS_INOBT_KEY_ADDR(right, 1, cur);
+ rpp = XFS_INOBT_PTR_ADDR(right, 1, cur);
+#ifdef DEBUG
+ for (i = 0; i < INT_GET(right->bb_numrecs, ARCH_CONVERT); i++) {
+ if ((error = xfs_btree_check_sptr(cur, INT_GET(lpp[i], ARCH_CONVERT), level)))
+ return error;
+ }
+#endif
+ memcpy(rkp, lkp, INT_GET(right->bb_numrecs, ARCH_CONVERT) * sizeof(*rkp));
+ memcpy(rpp, lpp, INT_GET(right->bb_numrecs, ARCH_CONVERT) * sizeof(*rpp));
+ xfs_inobt_log_keys(cur, rbp, 1, INT_GET(right->bb_numrecs, ARCH_CONVERT));
+ xfs_inobt_log_ptrs(cur, rbp, 1, INT_GET(right->bb_numrecs, ARCH_CONVERT));
+ *keyp = *rkp;
+ }
+ /*
+ * For leaf blocks, copy records over to the new block.
+ */
+ else {
+ lrp = XFS_INOBT_REC_ADDR(left, i, cur);
+ rrp = XFS_INOBT_REC_ADDR(right, 1, cur);
+ memcpy(rrp, lrp, INT_GET(right->bb_numrecs, ARCH_CONVERT) * sizeof(*rrp));
+ xfs_inobt_log_recs(cur, rbp, 1, INT_GET(right->bb_numrecs, ARCH_CONVERT));
+ keyp->ir_startino = rrp->ir_startino; /* INT_: direct copy */
+ }
+ /*
+ * Find the left block number by looking in the buffer.
+ * Adjust numrecs, sibling pointers.
+ */
+ INT_MOD(left->bb_numrecs, ARCH_CONVERT, -(INT_GET(right->bb_numrecs, ARCH_CONVERT)));
+ right->bb_rightsib = left->bb_rightsib; /* INT_: direct copy */
+ INT_SET(left->bb_rightsib, ARCH_CONVERT, args.agbno);
+ INT_SET(right->bb_leftsib, ARCH_CONVERT, lbno);
+ xfs_inobt_log_block(args.tp, rbp, XFS_BB_ALL_BITS);
+ xfs_inobt_log_block(args.tp, lbp, XFS_BB_NUMRECS | XFS_BB_RIGHTSIB);
+ /*
+ * If there's a block to the new block's right, make that block
+ * point back to right instead of to left.
+ */
+ if (INT_GET(right->bb_rightsib, ARCH_CONVERT) != NULLAGBLOCK) {
+ xfs_inobt_block_t *rrblock; /* rr btree block */
+ xfs_buf_t *rrbp; /* buffer for rrblock */
+
+ if ((error = xfs_btree_read_bufs(args.mp, args.tp, args.agno,
+ INT_GET(right->bb_rightsib, ARCH_CONVERT), 0, &rrbp,
+ XFS_INO_BTREE_REF)))
+ return error;
+ rrblock = XFS_BUF_TO_INOBT_BLOCK(rrbp);
+ if ((error = xfs_btree_check_sblock(cur, rrblock, level, rrbp)))
+ return error;
+ INT_SET(rrblock->bb_leftsib, ARCH_CONVERT, args.agbno);
+ xfs_inobt_log_block(args.tp, rrbp, XFS_BB_LEFTSIB);
+ }
+ /*
+ * If the cursor is really in the right block, move it there.
+ * If it's just pointing past the last entry in left, then we'll
+ * insert there, so don't change anything in that case.
+ */
+ if (cur->bc_ptrs[level] > INT_GET(left->bb_numrecs, ARCH_CONVERT) + 1) {
+ xfs_btree_setbuf(cur, level, rbp);
+ cur->bc_ptrs[level] -= INT_GET(left->bb_numrecs, ARCH_CONVERT);
+ }
+ /*
+ * If there are more levels, we'll need another cursor which refers
+ * the right block, no matter where this cursor was.
+ */
+ if (level + 1 < cur->bc_nlevels) {
+ if ((error = xfs_btree_dup_cursor(cur, curp)))
+ return error;
+ (*curp)->bc_ptrs[level + 1]++;
+ }
+ *bnop = args.agbno;
+ *stat = 1;
+ return 0;
+}
+
+/*
+ * Update keys at all levels from here to the root along the cursor's path.
+ */
+STATIC int /* error */
+xfs_inobt_updkey(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ xfs_inobt_key_t *keyp, /* new key value to update to */
+ int level) /* starting level for update */
+{
+ int ptr; /* index of key in block */
+
+ /*
+ * Go up the tree from this level toward the root.
+ * At each level, update the key value to the value input.
+ * Stop when we reach a level where the cursor isn't pointing
+ * at the first entry in the block.
+ */
+ for (ptr = 1; ptr == 1 && level < cur->bc_nlevels; level++) {
+ xfs_buf_t *bp; /* buffer for block */
+ xfs_inobt_block_t *block; /* btree block */
+#ifdef DEBUG
+ int error; /* error return value */
+#endif
+ xfs_inobt_key_t *kp; /* ptr to btree block keys */
+
+ bp = cur->bc_bufs[level];
+ block = XFS_BUF_TO_INOBT_BLOCK(bp);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_sblock(cur, block, level, bp)))
+ return error;
+#endif
+ ptr = cur->bc_ptrs[level];
+ kp = XFS_INOBT_KEY_ADDR(block, ptr, cur);
+ *kp = *keyp;
+ xfs_inobt_log_keys(cur, bp, ptr, ptr);
+ }
+ return 0;
+}
+
+/*
+ * Externally visible routines.
+ */
+
+/*
+ * Decrement cursor by one record at the level.
+ * For nonzero levels the leaf-ward information is untouched.
+ */
+int /* error */
+xfs_inobt_decrement(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ int level, /* level in btree, 0 is leaf */
+ int *stat) /* success/failure */
+{
+ xfs_inobt_block_t *block; /* btree block */
+ int error;
+ int lev; /* btree level */
+
+ ASSERT(level < cur->bc_nlevels);
+ /*
+ * Read-ahead to the left at this level.
+ */
+ xfs_btree_readahead(cur, level, XFS_BTCUR_LEFTRA);
+ /*
+ * Decrement the ptr at this level. If we're still in the block
+ * then we're done.
+ */
+ if (--cur->bc_ptrs[level] > 0) {
+ *stat = 1;
+ return 0;
+ }
+ /*
+ * Get a pointer to the btree block.
+ */
+ block = XFS_BUF_TO_INOBT_BLOCK(cur->bc_bufs[level]);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_sblock(cur, block, level,
+ cur->bc_bufs[level])))
+ return error;
+#endif
+ /*
+ * If we just went off the left edge of the tree, return failure.
+ */
+ if (INT_GET(block->bb_leftsib, ARCH_CONVERT) == NULLAGBLOCK) {
+ *stat = 0;
+ return 0;
+ }
+ /*
+ * March up the tree decrementing pointers.
+ * Stop when we don't go off the left edge of a block.
+ */
+ for (lev = level + 1; lev < cur->bc_nlevels; lev++) {
+ if (--cur->bc_ptrs[lev] > 0)
+ break;
+ /*
+ * Read-ahead the left block, we're going to read it
+ * in the next loop.
+ */
+ xfs_btree_readahead(cur, lev, XFS_BTCUR_LEFTRA);
+ }
+ /*
+ * If we went off the root then we are seriously confused.
+ */
+ ASSERT(lev < cur->bc_nlevels);
+ /*
+ * Now walk back down the tree, fixing up the cursor's buffer
+ * pointers and key numbers.
+ */
+ for (block = XFS_BUF_TO_INOBT_BLOCK(cur->bc_bufs[lev]); lev > level; ) {
+ xfs_agblock_t agbno; /* block number of btree block */
+ xfs_buf_t *bp; /* buffer containing btree block */
+
+ agbno = INT_GET(*XFS_INOBT_PTR_ADDR(block, cur->bc_ptrs[lev], cur), ARCH_CONVERT);
+ if ((error = xfs_btree_read_bufs(cur->bc_mp, cur->bc_tp,
+ cur->bc_private.i.agno, agbno, 0, &bp,
+ XFS_INO_BTREE_REF)))
+ return error;
+ lev--;
+ xfs_btree_setbuf(cur, lev, bp);
+ block = XFS_BUF_TO_INOBT_BLOCK(bp);
+ if ((error = xfs_btree_check_sblock(cur, block, lev, bp)))
+ return error;
+ cur->bc_ptrs[lev] = INT_GET(block->bb_numrecs, ARCH_CONVERT);
+ }
+ *stat = 1;
+ return 0;
+}
+
+/*
+ * Delete the record pointed to by cur.
+ * The cursor refers to the place where the record was (could be inserted)
+ * when the operation returns.
+ */
+int /* error */
+xfs_inobt_delete(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ int *stat) /* success/failure */
+{
+ int error;
+ int i; /* result code */
+ int level; /* btree level */
+
+ /*
+ * Go up the tree, starting at leaf level.
+ * If 2 is returned then a join was done; go to the next level.
+ * Otherwise we are done.
+ */
+ for (level = 0, i = 2; i == 2; level++) {
+ if ((error = xfs_inobt_delrec(cur, level, &i)))
+ return error;
+ }
+ if (i == 0) {
+ for (level = 1; level < cur->bc_nlevels; level++) {
+ if (cur->bc_ptrs[level] == 0) {
+ if ((error = xfs_inobt_decrement(cur, level, &i)))
+ return error;
+ break;
+ }
+ }
+ }
+ *stat = i;
+ return 0;
+}
+
+
+/*
+ * Get the data from the pointed-to record.
+ */
+int /* error */
+xfs_inobt_get_rec(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ xfs_agino_t *ino, /* output: starting inode of chunk */
+ __int32_t *fcnt, /* output: number of free inodes */
+ xfs_inofree_t *free, /* output: free inode mask */
+ int *stat, /* output: success/failure */
+ xfs_arch_t arch) /* input: architecture */
+{
+ xfs_inobt_block_t *block; /* btree block */
+ xfs_buf_t *bp; /* buffer containing btree block */
+#ifdef DEBUG
+ int error; /* error return value */
+#endif
+ int ptr; /* record number */
+ xfs_inobt_rec_t *rec; /* record data */
+
+ bp = cur->bc_bufs[0];
+ ptr = cur->bc_ptrs[0];
+ block = XFS_BUF_TO_INOBT_BLOCK(bp);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_sblock(cur, block, 0, bp)))
+ return error;
+#endif
+ /*
+ * Off the right end or left end, return failure.
+ */
+ if (ptr > INT_GET(block->bb_numrecs, ARCH_CONVERT) || ptr <= 0) {
+ *stat = 0;
+ return 0;
+ }
+ /*
+ * Point to the record and extract its data.
+ */
+ rec = XFS_INOBT_REC_ADDR(block, ptr, cur);
+ ASSERT(arch == ARCH_NOCONVERT || arch == ARCH_CONVERT);
+ if (arch == ARCH_NOCONVERT) {
+ *ino = INT_GET(rec->ir_startino, ARCH_CONVERT);
+ *fcnt = INT_GET(rec->ir_freecount, ARCH_CONVERT);
+ *free = INT_GET(rec->ir_free, ARCH_CONVERT);
+ } else {
+ INT_COPY(*ino, rec->ir_startino, ARCH_CONVERT);
+ INT_COPY(*fcnt, rec->ir_freecount, ARCH_CONVERT);
+ INT_COPY(*free, rec->ir_free, ARCH_CONVERT);
+ }
+ *stat = 1;
+ return 0;
+}
+
+/*
+ * Increment cursor by one record at the level.
+ * For nonzero levels the leaf-ward information is untouched.
+ */
+int /* error */
+xfs_inobt_increment(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ int level, /* level in btree, 0 is leaf */
+ int *stat) /* success/failure */
+{
+ xfs_inobt_block_t *block; /* btree block */
+ xfs_buf_t *bp; /* buffer containing btree block */
+ int error; /* error return value */
+ int lev; /* btree level */
+
+ ASSERT(level < cur->bc_nlevels);
+ /*
+ * Read-ahead to the right at this level.
+ */
+ xfs_btree_readahead(cur, level, XFS_BTCUR_RIGHTRA);
+ /*
+ * Get a pointer to the btree block.
+ */
+ bp = cur->bc_bufs[level];
+ block = XFS_BUF_TO_INOBT_BLOCK(bp);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_sblock(cur, block, level, bp)))
+ return error;
+#endif
+ /*
+ * Increment the ptr at this level. If we're still in the block
+ * then we're done.
+ */
+ if (++cur->bc_ptrs[level] <= INT_GET(block->bb_numrecs, ARCH_CONVERT)) {
+ *stat = 1;
+ return 0;
+ }
+ /*
+ * If we just went off the right edge of the tree, return failure.
+ */
+ if (INT_GET(block->bb_rightsib, ARCH_CONVERT) == NULLAGBLOCK) {
+ *stat = 0;
+ return 0;
+ }
+ /*
+ * March up the tree incrementing pointers.
+ * Stop when we don't go off the right edge of a block.
+ */
+ for (lev = level + 1; lev < cur->bc_nlevels; lev++) {
+ bp = cur->bc_bufs[lev];
+ block = XFS_BUF_TO_INOBT_BLOCK(bp);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_sblock(cur, block, lev, bp)))
+ return error;
+#endif
+ if (++cur->bc_ptrs[lev] <= INT_GET(block->bb_numrecs, ARCH_CONVERT))
+ break;
+ /*
+ * Read-ahead the right block, we're going to read it
+ * in the next loop.
+ */
+ xfs_btree_readahead(cur, lev, XFS_BTCUR_RIGHTRA);
+ }
+ /*
+ * If we went off the root then we are seriously confused.
+ */
+ ASSERT(lev < cur->bc_nlevels);
+ /*
+ * Now walk back down the tree, fixing up the cursor's buffer
+ * pointers and key numbers.
+ */
+ for (bp = cur->bc_bufs[lev], block = XFS_BUF_TO_INOBT_BLOCK(bp);
+ lev > level; ) {
+ xfs_agblock_t agbno; /* block number of btree block */
+
+ agbno = INT_GET(*XFS_INOBT_PTR_ADDR(block, cur->bc_ptrs[lev], cur), ARCH_CONVERT);
+ if ((error = xfs_btree_read_bufs(cur->bc_mp, cur->bc_tp,
+ cur->bc_private.i.agno, agbno, 0, &bp,
+ XFS_INO_BTREE_REF)))
+ return error;
+ lev--;
+ xfs_btree_setbuf(cur, lev, bp);
+ block = XFS_BUF_TO_INOBT_BLOCK(bp);
+ if ((error = xfs_btree_check_sblock(cur, block, lev, bp)))
+ return error;
+ cur->bc_ptrs[lev] = 1;
+ }
+ *stat = 1;
+ return 0;
+}
+
+/*
+ * Insert the current record at the point referenced by cur.
+ * The cursor may be inconsistent on return if splits have been done.
+ */
+int /* error */
+xfs_inobt_insert(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ int *stat) /* success/failure */
+{
+ int error; /* error return value */
+ int i; /* result value, 0 for failure */
+ int level; /* current level number in btree */
+ xfs_agblock_t nbno; /* new block number (split result) */
+ xfs_btree_cur_t *ncur; /* new cursor (split result) */
+ xfs_inobt_rec_t nrec; /* record being inserted this level */
+ xfs_btree_cur_t *pcur; /* previous level's cursor */
+
+ level = 0;
+ nbno = NULLAGBLOCK;
+ INT_SET(nrec.ir_startino, ARCH_CONVERT, cur->bc_rec.i.ir_startino);
+ INT_SET(nrec.ir_freecount, ARCH_CONVERT, cur->bc_rec.i.ir_freecount);
+ INT_SET(nrec.ir_free, ARCH_CONVERT, cur->bc_rec.i.ir_free);
+ ncur = (xfs_btree_cur_t *)0;
+ pcur = cur;
+ /*
+ * Loop going up the tree, starting at the leaf level.
+ * Stop when we don't get a split block, that must mean that
+ * the insert is finished with this level.
+ */
+ do {
+ /*
+ * Insert nrec/nbno into this level of the tree.
+ * Note if we fail, nbno will be null.
+ */
+ if ((error = xfs_inobt_insrec(pcur, level++, &nbno, &nrec, &ncur,
+ &i))) {
+ if (pcur != cur)
+ xfs_btree_del_cursor(pcur, XFS_BTREE_ERROR);
+ return error;
+ }
+ /*
+ * See if the cursor we just used is trash.
+ * Can't trash the caller's cursor, but otherwise we should
+ * if ncur is a new cursor or we're about to be done.
+ */
+ if (pcur != cur && (ncur || nbno == NULLAGBLOCK)) {
+ cur->bc_nlevels = pcur->bc_nlevels;
+ xfs_btree_del_cursor(pcur, XFS_BTREE_NOERROR);
+ }
+ /*
+ * If we got a new cursor, switch to it.
+ */
+ if (ncur) {
+ pcur = ncur;
+ ncur = (xfs_btree_cur_t *)0;
+ }
+ } while (nbno != NULLAGBLOCK);
+ *stat = i;
+ return 0;
+}
+
+/*
+ * Lookup the record equal to ino in the btree given by cur.
+ */
+int /* error */
+xfs_inobt_lookup_eq(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ xfs_agino_t ino, /* starting inode of chunk */
+ __int32_t fcnt, /* free inode count */
+ xfs_inofree_t free, /* free inode mask */
+ int *stat) /* success/failure */
+{
+ cur->bc_rec.i.ir_startino = ino;
+ cur->bc_rec.i.ir_freecount = fcnt;
+ cur->bc_rec.i.ir_free = free;
+ return xfs_inobt_lookup(cur, XFS_LOOKUP_EQ, stat);
+}
+
+/*
+ * Lookup the first record greater than or equal to ino
+ * in the btree given by cur.
+ */
+int /* error */
+xfs_inobt_lookup_ge(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ xfs_agino_t ino, /* starting inode of chunk */
+ __int32_t fcnt, /* free inode count */
+ xfs_inofree_t free, /* free inode mask */
+ int *stat) /* success/failure */
+{
+ cur->bc_rec.i.ir_startino = ino;
+ cur->bc_rec.i.ir_freecount = fcnt;
+ cur->bc_rec.i.ir_free = free;
+ return xfs_inobt_lookup(cur, XFS_LOOKUP_GE, stat);
+}
+
+/*
+ * Lookup the first record less than or equal to ino
+ * in the btree given by cur.
+ */
+int /* error */
+xfs_inobt_lookup_le(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ xfs_agino_t ino, /* starting inode of chunk */
+ __int32_t fcnt, /* free inode count */
+ xfs_inofree_t free, /* free inode mask */
+ int *stat) /* success/failure */
+{
+ cur->bc_rec.i.ir_startino = ino;
+ cur->bc_rec.i.ir_freecount = fcnt;
+ cur->bc_rec.i.ir_free = free;
+ return xfs_inobt_lookup(cur, XFS_LOOKUP_LE, stat);
+}
+
+/*
+ * Update the record referred to by cur, to the value given
+ * by [ino, fcnt, free].
+ * This either works (return 0) or gets an EFSCORRUPTED error.
+ */
+int /* error */
+xfs_inobt_update(
+ xfs_btree_cur_t *cur, /* btree cursor */
+ xfs_agino_t ino, /* starting inode of chunk */
+ __int32_t fcnt, /* free inode count */
+ xfs_inofree_t free) /* free inode mask */
+{
+ xfs_inobt_block_t *block; /* btree block to update */
+ xfs_buf_t *bp; /* buffer containing btree block */
+ int error; /* error return value */
+ int ptr; /* current record number (updating) */
+ xfs_inobt_rec_t *rp; /* pointer to updated record */
+
+ /*
+ * Pick up the current block.
+ */
+ bp = cur->bc_bufs[0];
+ block = XFS_BUF_TO_INOBT_BLOCK(bp);
+#ifdef DEBUG
+ if ((error = xfs_btree_check_sblock(cur, block, 0, bp)))
+ return error;
+#endif
+ /*
+ * Get the address of the rec to be updated.
+ */
+ ptr = cur->bc_ptrs[0];
+ rp = XFS_INOBT_REC_ADDR(block, ptr, cur);
+ /*
+ * Fill in the new contents and log them.
+ */
+ INT_SET(rp->ir_startino, ARCH_CONVERT, ino);
+ INT_SET(rp->ir_freecount, ARCH_CONVERT, fcnt);
+ INT_SET(rp->ir_free, ARCH_CONVERT, free);
+ xfs_inobt_log_recs(cur, bp, ptr, ptr);
+ /*
+ * Updating first record in leaf. Pass new key value up to our parent.
+ */
+ if (ptr == 1) {
+ xfs_inobt_key_t key; /* key containing [ino] */
+
+ INT_SET(key.ir_startino, ARCH_CONVERT, ino);
+ if ((error = xfs_inobt_updkey(cur, &key, 1)))
+ return error;
+ }
+ return 0;
+}
diff --git a/fs/xfs/xfs_ialloc_btree.h b/fs/xfs/xfs_ialloc_btree.h
new file mode 100644
index 00000000000000..dbda2ed8128e34
--- /dev/null
+++ b/fs/xfs/xfs_ialloc_btree.h
@@ -0,0 +1,316 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_IALLOC_BTREE_H__
+#define __XFS_IALLOC_BTREE_H__
+
+/*
+ * Inode map on-disk structures
+ */
+
+struct xfs_buf;
+struct xfs_btree_cur;
+struct xfs_btree_sblock;
+struct xfs_mount;
+
+/*
+ * There is a btree for the inode map per allocation group.
+ */
+#define XFS_IBT_MAGIC 0x49414254 /* 'IABT' */
+
+typedef __uint64_t xfs_inofree_t;
+#define XFS_INODES_PER_CHUNK (NBBY * sizeof(xfs_inofree_t))
+#define XFS_INODES_PER_CHUNK_LOG (XFS_NBBYLOG + 3)
+#define XFS_INOBT_ALL_FREE ((xfs_inofree_t)-1)
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_INOBT_MASKN)
+xfs_inofree_t xfs_inobt_maskn(int i, int n);
+#define XFS_INOBT_MASKN(i,n) xfs_inobt_maskn(i,n)
+#else
+#define XFS_INOBT_MASKN(i,n) \
+ ((((n) >= XFS_INODES_PER_CHUNK ? \
+ (xfs_inofree_t)0 : ((xfs_inofree_t)1 << (n))) - 1) << (i))
+#endif
+
+/*
+ * Data record structure
+ */
+typedef struct xfs_inobt_rec
+{
+ xfs_agino_t ir_startino; /* starting inode number */
+ __int32_t ir_freecount; /* count of free inodes (set bits) */
+ xfs_inofree_t ir_free; /* free inode mask */
+} xfs_inobt_rec_t;
+
+/*
+ * Key structure
+ */
+typedef struct xfs_inobt_key
+{
+ xfs_agino_t ir_startino; /* starting inode number */
+} xfs_inobt_key_t;
+
+typedef xfs_agblock_t xfs_inobt_ptr_t; /* btree pointer type */
+ /* btree block header type */
+typedef struct xfs_btree_sblock xfs_inobt_block_t;
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BUF_TO_INOBT_BLOCK)
+xfs_inobt_block_t *xfs_buf_to_inobt_block(struct xfs_buf *bp);
+#define XFS_BUF_TO_INOBT_BLOCK(bp) xfs_buf_to_inobt_block(bp)
+#else
+#define XFS_BUF_TO_INOBT_BLOCK(bp) ((xfs_inobt_block_t *)(XFS_BUF_PTR(bp)))
+#endif
+
+/*
+ * Bit manipulations for ir_free.
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_INOBT_MASK)
+xfs_inofree_t xfs_inobt_mask(int i);
+#define XFS_INOBT_MASK(i) xfs_inobt_mask(i)
+#else
+#define XFS_INOBT_MASK(i) ((xfs_inofree_t)1 << (i))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_INOBT_IS_FREE)
+int xfs_inobt_is_free(xfs_inobt_rec_t *rp, int i, xfs_arch_t arch);
+#define XFS_INOBT_IS_FREE(rp,i,arch) xfs_inobt_is_free(rp,i,arch)
+#else
+#define XFS_INOBT_IS_FREE(rp,i,arch) ((INT_GET((rp)->ir_free, arch) \
+ & XFS_INOBT_MASK(i)) != 0)
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_INOBT_SET_FREE)
+void xfs_inobt_set_free(xfs_inobt_rec_t *rp, int i, xfs_arch_t arch);
+#define XFS_INOBT_SET_FREE(rp,i,arch) xfs_inobt_set_free(rp,i,arch)
+#else
+#define XFS_INOBT_SET_FREE(rp,i,arch) (INT_MOD_EXPR((rp)->ir_free, arch, |= XFS_INOBT_MASK(i)))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_INOBT_CLR_FREE)
+void xfs_inobt_clr_free(xfs_inobt_rec_t *rp, int i, xfs_arch_t arch);
+#define XFS_INOBT_CLR_FREE(rp,i,arch) xfs_inobt_clr_free(rp,i,arch)
+#else
+#define XFS_INOBT_CLR_FREE(rp,i,arch) (INT_MOD_EXPR((rp)->ir_free, arch, &= ~XFS_INOBT_MASK(i)))
+#endif
+
+/*
+ * Real block structures have a size equal to the disk block size.
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_INOBT_BLOCK_SIZE)
+int xfs_inobt_block_size(int lev, struct xfs_btree_cur *cur);
+#define XFS_INOBT_BLOCK_SIZE(lev,cur) xfs_inobt_block_size(lev,cur)
+#else
+#define XFS_INOBT_BLOCK_SIZE(lev,cur) (1 << (cur)->bc_blocklog)
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_INOBT_BLOCK_MAXRECS)
+int xfs_inobt_block_maxrecs(int lev, struct xfs_btree_cur *cur);
+#define XFS_INOBT_BLOCK_MAXRECS(lev,cur) xfs_inobt_block_maxrecs(lev,cur)
+#else
+#define XFS_INOBT_BLOCK_MAXRECS(lev,cur) \
+ ((cur)->bc_mp->m_inobt_mxr[lev != 0])
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_INOBT_BLOCK_MINRECS)
+int xfs_inobt_block_minrecs(int lev, struct xfs_btree_cur *cur);
+#define XFS_INOBT_BLOCK_MINRECS(lev,cur) xfs_inobt_block_minrecs(lev,cur)
+#else
+#define XFS_INOBT_BLOCK_MINRECS(lev,cur) \
+ ((cur)->bc_mp->m_inobt_mnr[lev != 0])
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_INOBT_IS_LAST_REC)
+int xfs_inobt_is_last_rec(struct xfs_btree_cur *cur);
+#define XFS_INOBT_IS_LAST_REC(cur) xfs_inobt_is_last_rec(cur)
+#else
+#define XFS_INOBT_IS_LAST_REC(cur) \
+ ((cur)->bc_ptrs[0] == \
+ INT_GET(XFS_BUF_TO_INOBT_BLOCK((cur)->bc_bufs[0])->bb_numrecs, ARCH_CONVERT))
+#endif
+
+/*
+ * Maximum number of inode btree levels.
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_IN_MAXLEVELS)
+int xfs_in_maxlevels(struct xfs_mount *mp);
+#define XFS_IN_MAXLEVELS(mp) xfs_in_maxlevels(mp)
+#else
+#define XFS_IN_MAXLEVELS(mp) ((mp)->m_in_maxlevels)
+#endif
+
+/*
+ * block numbers in the AG.
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_IBT_BLOCK)
+xfs_agblock_t xfs_ibt_block(struct xfs_mount *mp);
+#define XFS_IBT_BLOCK(mp) xfs_ibt_block(mp)
+#else
+#define XFS_IBT_BLOCK(mp) ((xfs_agblock_t)(XFS_CNT_BLOCK(mp) + 1))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_PREALLOC_BLOCKS)
+xfs_agblock_t xfs_prealloc_blocks(struct xfs_mount *mp);
+#define XFS_PREALLOC_BLOCKS(mp) xfs_prealloc_blocks(mp)
+#else
+#define XFS_PREALLOC_BLOCKS(mp) ((xfs_agblock_t)(XFS_IBT_BLOCK(mp) + 1))
+#endif
+
+/*
+ * Record, key, and pointer address macros for btree blocks.
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_INOBT_REC_ADDR)
+xfs_inobt_rec_t *
+xfs_inobt_rec_addr(xfs_inobt_block_t *bb, int i, struct xfs_btree_cur *cur);
+#define XFS_INOBT_REC_ADDR(bb,i,cur) xfs_inobt_rec_addr(bb,i,cur)
+#else
+#define XFS_INOBT_REC_ADDR(bb,i,cur) \
+ XFS_BTREE_REC_ADDR(XFS_INOBT_BLOCK_SIZE(0,cur), xfs_inobt, bb, i, \
+ XFS_INOBT_BLOCK_MAXRECS(0, cur))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_INOBT_KEY_ADDR)
+xfs_inobt_key_t *
+xfs_inobt_key_addr(xfs_inobt_block_t *bb, int i, struct xfs_btree_cur *cur);
+#define XFS_INOBT_KEY_ADDR(bb,i,cur) xfs_inobt_key_addr(bb,i,cur)
+#else
+#define XFS_INOBT_KEY_ADDR(bb,i,cur) \
+ XFS_BTREE_KEY_ADDR(XFS_INOBT_BLOCK_SIZE(1,cur), xfs_inobt, bb, i, \
+ XFS_INOBT_BLOCK_MAXRECS(1, cur))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_INOBT_PTR_ADDR)
+xfs_inobt_ptr_t *
+xfs_inobt_ptr_addr(xfs_inobt_block_t *bb, int i, struct xfs_btree_cur *cur);
+#define XFS_INOBT_PTR_ADDR(bb,i,cur) xfs_inobt_ptr_addr(bb,i,cur)
+#else
+#define XFS_INOBT_PTR_ADDR(bb,i,cur) \
+ XFS_BTREE_PTR_ADDR(XFS_INOBT_BLOCK_SIZE(1,cur), xfs_inobt, bb, i, \
+ XFS_INOBT_BLOCK_MAXRECS(1, cur))
+#endif
+
+/*
+ * Prototypes for externally visible routines.
+ */
+
+/*
+ * Decrement cursor by one record at the level.
+ * For nonzero levels the leaf-ward information is untouched.
+ */
+int /* error */
+xfs_inobt_decrement(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ int level, /* level in btree, 0 is leaf */
+ int *stat); /* success/failure */
+
+/*
+ * Delete the record pointed to by cur.
+ * The cursor refers to the place where the record was (could be inserted)
+ * when the operation returns.
+ */
+int /* error */
+xfs_inobt_delete(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ int *stat); /* success/failure */
+
+/*
+ * Get the data from the pointed-to record.
+ */
+int /* error */
+xfs_inobt_get_rec(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ xfs_agino_t *ino, /* output: starting inode of chunk */
+ __int32_t *fcnt, /* output: number of free inodes */
+ xfs_inofree_t *free, /* output: free inode mask */
+ int *stat, /* output: success/failure */
+ xfs_arch_t arch); /* output: architecture */
+
+/*
+ * Increment cursor by one record at the level.
+ * For nonzero levels the leaf-ward information is untouched.
+ */
+int /* error */
+xfs_inobt_increment(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ int level, /* level in btree, 0 is leaf */
+ int *stat); /* success/failure */
+
+/*
+ * Insert the current record at the point referenced by cur.
+ * The cursor may be inconsistent on return if splits have been done.
+ */
+int /* error */
+xfs_inobt_insert(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ int *stat); /* success/failure */
+
+/*
+ * Lookup the record equal to ino in the btree given by cur.
+ */
+int /* error */
+xfs_inobt_lookup_eq(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ xfs_agino_t ino, /* starting inode of chunk */
+ __int32_t fcnt, /* free inode count */
+ xfs_inofree_t free, /* free inode mask */
+ int *stat); /* success/failure */
+
+/*
+ * Lookup the first record greater than or equal to ino
+ * in the btree given by cur.
+ */
+int /* error */
+xfs_inobt_lookup_ge(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ xfs_agino_t ino, /* starting inode of chunk */
+ __int32_t fcnt, /* free inode count */
+ xfs_inofree_t free, /* free inode mask */
+ int *stat); /* success/failure */
+
+/*
+ * Lookup the first record less than or equal to ino
+ * in the btree given by cur.
+ */
+int /* error */
+xfs_inobt_lookup_le(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ xfs_agino_t ino, /* starting inode of chunk */
+ __int32_t fcnt, /* free inode count */
+ xfs_inofree_t free, /* free inode mask */
+ int *stat); /* success/failure */
+
+/*
+ * Update the record referred to by cur, to the value given
+ * by [ino, fcnt, free].
+ * This either works (return 0) or gets an EFSCORRUPTED error.
+ */
+int /* error */
+xfs_inobt_update(
+ struct xfs_btree_cur *cur, /* btree cursor */
+ xfs_agino_t ino, /* starting inode of chunk */
+ __int32_t fcnt, /* free inode count */
+ xfs_inofree_t free); /* free inode mask */
+
+#endif /* __XFS_IALLOC_BTREE_H__ */
diff --git a/fs/xfs/xfs_iget.c b/fs/xfs/xfs_iget.c
new file mode 100644
index 00000000000000..c231e9e34559f8
--- /dev/null
+++ b/fs/xfs/xfs_iget.c
@@ -0,0 +1,1019 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode.h"
+#include "xfs_quota.h"
+#include "xfs_utils.h"
+
+/*
+ * Initialize the inode hash table for the newly mounted file system.
+ *
+ * mp -- this is the mount point structure for the file system being
+ * initialized
+ */
+void
+xfs_ihash_init(xfs_mount_t *mp)
+{
+ int i;
+
+ mp->m_ihsize = XFS_BUCKETS(mp);
+ mp->m_ihash = (xfs_ihash_t *)kmem_zalloc(mp->m_ihsize
+ * sizeof(xfs_ihash_t), KM_SLEEP);
+ ASSERT(mp->m_ihash != NULL);
+ for (i = 0; i < mp->m_ihsize; i++) {
+ rwlock_init(&(mp->m_ihash[i].ih_lock));
+ }
+}
+
+/*
+ * Free up structures allocated by xfs_ihash_init, at unmount time.
+ */
+void
+xfs_ihash_free(xfs_mount_t *mp)
+{
+ kmem_free(mp->m_ihash, mp->m_ihsize*sizeof(xfs_ihash_t));
+ mp->m_ihash = NULL;
+}
+
+/*
+ * Initialize the inode cluster hash table for the newly mounted file system.
+ *
+ * mp -- this is the mount point structure for the file system being
+ * initialized
+ */
+void
+xfs_chash_init(xfs_mount_t *mp)
+{
+ int i;
+
+ /*
+ * m_chash size is based on m_ihash
+ * with a minimum of 37 entries
+ */
+ mp->m_chsize = (XFS_BUCKETS(mp)) /
+ (XFS_INODE_CLUSTER_SIZE(mp) >> mp->m_sb.sb_inodelog);
+ if (mp->m_chsize < 37) {
+ mp->m_chsize = 37;
+ }
+ mp->m_chash = (xfs_chash_t *)kmem_zalloc(mp->m_chsize
+ * sizeof(xfs_chash_t),
+ KM_SLEEP);
+ ASSERT(mp->m_chash != NULL);
+
+ for (i = 0; i < mp->m_chsize; i++) {
+ spinlock_init(&mp->m_chash[i].ch_lock,"xfshash");
+ }
+}
+
+/*
+ * Free up structures allocated by xfs_chash_init, at unmount time.
+ */
+void
+xfs_chash_free(xfs_mount_t *mp)
+{
+ int i;
+
+ for (i = 0; i < mp->m_chsize; i++) {
+ spinlock_destroy(&mp->m_chash[i].ch_lock);
+ }
+
+ kmem_free(mp->m_chash, mp->m_chsize*sizeof(xfs_chash_t));
+ mp->m_chash = NULL;
+}
+
+/*
+ * Look up an inode by number in the given file system.
+ * The inode is looked up in the hash table for the file system
+ * represented by the mount point parameter mp. Each bucket of
+ * the hash table is guarded by an individual semaphore.
+ *
+ * If the inode is found in the hash table, its corresponding vnode
+ * is obtained with a call to vn_get(). This call takes care of
+ * coordination with the reclamation of the inode and vnode. Note
+ * that the vmap structure is filled in while holding the hash lock.
+ * This gives us the state of the inode/vnode when we found it and
+ * is used for coordination in vn_get().
+ *
+ * If it is not in core, read it in from the file system's device and
+ * add the inode into the hash table.
+ *
+ * The inode is locked according to the value of the lock_flags parameter.
+ * This flag parameter indicates how and if the inode's IO lock and inode lock
+ * should be taken.
+ *
+ * mp -- the mount point structure for the current file system. It points
+ * to the inode hash table.
+ * tp -- a pointer to the current transaction if there is one. This is
+ * simply passed through to the xfs_iread() call.
+ * ino -- the number of the inode desired. This is the unique identifier
+ * within the file system for the inode being requested.
+ * lock_flags -- flags indicating how to lock the inode. See the comment
+ * for xfs_ilock() for a list of valid values.
+ * bno -- the block number starting the buffer containing the inode,
+ * if known (as by bulkstat), else 0.
+ */
+STATIC int
+xfs_iget_core(
+ vnode_t *vp,
+ xfs_mount_t *mp,
+ xfs_trans_t *tp,
+ xfs_ino_t ino,
+ uint flags,
+ uint lock_flags,
+ xfs_inode_t **ipp,
+ xfs_daddr_t bno)
+{
+ xfs_ihash_t *ih;
+ xfs_inode_t *ip;
+ xfs_inode_t *iq;
+ vnode_t *inode_vp;
+ ulong version;
+ int error;
+ /* REFERENCED */
+ xfs_chash_t *ch;
+ xfs_chashlist_t *chl, *chlnew;
+ SPLDECL(s);
+
+
+ ih = XFS_IHASH(mp, ino);
+
+again:
+ read_lock(&ih->ih_lock);
+
+ for (ip = ih->ih_next; ip != NULL; ip = ip->i_next) {
+ if (ip->i_ino == ino) {
+ /*
+ * If INEW is set this inode is being set up
+ * we need to pause and try again.
+ */
+ if (ip->i_flags & XFS_INEW) {
+ read_unlock(&ih->ih_lock);
+ delay(1);
+ XFS_STATS_INC(xs_ig_frecycle);
+
+ goto again;
+ }
+
+ inode_vp = XFS_ITOV_NULL(ip);
+ if (inode_vp == NULL) {
+ /*
+ * If IRECLAIM is set this inode is
+ * on its way out of the system,
+ * we need to pause and try again.
+ */
+ if (ip->i_flags & XFS_IRECLAIM) {
+ read_unlock(&ih->ih_lock);
+ delay(1);
+ XFS_STATS_INC(xs_ig_frecycle);
+
+ goto again;
+ }
+
+ vn_trace_exit(vp, "xfs_iget.alloc",
+ (inst_t *)__return_address);
+
+ XFS_STATS_INC(xs_ig_found);
+
+ ip->i_flags &= ~XFS_IRECLAIMABLE;
+ read_unlock(&ih->ih_lock);
+
+ XFS_MOUNT_ILOCK(mp);
+ list_del_init(&ip->i_reclaim);
+ XFS_MOUNT_IUNLOCK(mp);
+
+ goto finish_inode;
+
+ } else if (vp != inode_vp) {
+ struct inode *inode = LINVFS_GET_IP(inode_vp);
+
+ /* The inode is being torn down, pause and
+ * try again.
+ */
+ if (inode->i_state & (I_FREEING | I_CLEAR)) {
+ read_unlock(&ih->ih_lock);
+ delay(1);
+ XFS_STATS_INC(xs_ig_frecycle);
+
+ goto again;
+ }
+/* Chances are the other vnode (the one in the inode) is being torn
+ * down right now, and we landed on top of it. Question is, what do
+ * we do? Unhook the old inode and hook up the new one?
+ */
+ cmn_err(CE_PANIC,
+ "xfs_iget_core: ambiguous vns: vp/0x%p, invp/0x%p",
+ inode_vp, vp);
+ }
+
+ read_unlock(&ih->ih_lock);
+
+ XFS_STATS_INC(xs_ig_found);
+
+finish_inode:
+ if (ip->i_d.di_mode == 0) {
+ if (!(flags & IGET_CREATE))
+ return ENOENT;
+ xfs_iocore_inode_reinit(ip);
+ }
+
+ if (lock_flags != 0)
+ xfs_ilock(ip, lock_flags);
+
+ ip->i_flags &= ~XFS_ISTALE;
+
+ vn_trace_exit(vp, "xfs_iget.found",
+ (inst_t *)__return_address);
+ goto return_ip;
+ }
+ }
+
+ /*
+ * Inode cache miss: save the hash chain version stamp and unlock
+ * the chain, so we don't deadlock in vn_alloc.
+ */
+ XFS_STATS_INC(xs_ig_missed);
+
+ version = ih->ih_version;
+
+ read_unlock(&ih->ih_lock);
+
+ /*
+ * Read the disk inode attributes into a new inode structure and get
+ * a new vnode for it. This should also initialize i_ino and i_mount.
+ */
+ error = xfs_iread(mp, tp, ino, &ip, bno);
+ if (error) {
+ return error;
+ }
+
+ vn_trace_exit(vp, "xfs_iget.alloc", (inst_t *)__return_address);
+
+ xfs_inode_lock_init(ip, vp);
+ xfs_iocore_inode_init(ip);
+
+ if (lock_flags != 0) {
+ xfs_ilock(ip, lock_flags);
+ }
+
+ if ((ip->i_d.di_mode == 0) && !(flags & IGET_CREATE)) {
+ xfs_idestroy(ip);
+ return ENOENT;
+ }
+
+ /*
+ * Put ip on its hash chain, unless someone else hashed a duplicate
+ * after we released the hash lock.
+ */
+ write_lock(&ih->ih_lock);
+
+ if (ih->ih_version != version) {
+ for (iq = ih->ih_next; iq != NULL; iq = iq->i_next) {
+ if (iq->i_ino == ino) {
+ write_unlock(&ih->ih_lock);
+ xfs_idestroy(ip);
+
+ XFS_STATS_INC(xs_ig_dup);
+ goto again;
+ }
+ }
+ }
+
+ /*
+ * These values _must_ be set before releasing ihlock!
+ */
+ ip->i_hash = ih;
+ if ((iq = ih->ih_next)) {
+ iq->i_prevp = &ip->i_next;
+ }
+ ip->i_next = iq;
+ ip->i_prevp = &ih->ih_next;
+ ih->ih_next = ip;
+ ip->i_udquot = ip->i_gdquot = NULL;
+ ih->ih_version++;
+ ip->i_flags |= XFS_INEW;
+
+ write_unlock(&ih->ih_lock);
+
+ /*
+ * put ip on its cluster's hash chain
+ */
+ ASSERT(ip->i_chash == NULL && ip->i_cprev == NULL &&
+ ip->i_cnext == NULL);
+
+ chlnew = NULL;
+ ch = XFS_CHASH(mp, ip->i_blkno);
+ chlredo:
+ s = mutex_spinlock(&ch->ch_lock);
+ for (chl = ch->ch_list; chl != NULL; chl = chl->chl_next) {
+ if (chl->chl_blkno == ip->i_blkno) {
+
+ /* insert this inode into the doubly-linked list
+ * where chl points */
+ if ((iq = chl->chl_ip)) {
+ ip->i_cprev = iq->i_cprev;
+ iq->i_cprev->i_cnext = ip;
+ iq->i_cprev = ip;
+ ip->i_cnext = iq;
+ } else {
+ ip->i_cnext = ip;
+ ip->i_cprev = ip;
+ }
+ chl->chl_ip = ip;
+ ip->i_chash = chl;
+ break;
+ }
+ }
+
+ /* no hash list found for this block; add a new hash list */
+ if (chl == NULL) {
+ if (chlnew == NULL) {
+ mutex_spinunlock(&ch->ch_lock, s);
+ ASSERT(xfs_chashlist_zone != NULL);
+ chlnew = (xfs_chashlist_t *)
+ kmem_zone_alloc(xfs_chashlist_zone,
+ KM_SLEEP);
+ ASSERT(chlnew != NULL);
+ goto chlredo;
+ } else {
+ ip->i_cnext = ip;
+ ip->i_cprev = ip;
+ ip->i_chash = chlnew;
+ chlnew->chl_ip = ip;
+ chlnew->chl_blkno = ip->i_blkno;
+ chlnew->chl_next = ch->ch_list;
+ ch->ch_list = chlnew;
+ chlnew = NULL;
+ }
+ } else {
+ if (chlnew != NULL) {
+ kmem_zone_free(xfs_chashlist_zone, chlnew);
+ }
+ }
+
+ mutex_spinunlock(&ch->ch_lock, s);
+
+
+ /*
+ * Link ip to its mount and thread it on the mount's inode list.
+ */
+ XFS_MOUNT_ILOCK(mp);
+ if ((iq = mp->m_inodes)) {
+ ASSERT(iq->i_mprev->i_mnext == iq);
+ ip->i_mprev = iq->i_mprev;
+ iq->i_mprev->i_mnext = ip;
+ iq->i_mprev = ip;
+ ip->i_mnext = iq;
+ } else {
+ ip->i_mnext = ip;
+ ip->i_mprev = ip;
+ }
+ mp->m_inodes = ip;
+
+ XFS_MOUNT_IUNLOCK(mp);
+
+ return_ip:
+ ASSERT(ip->i_df.if_ext_max ==
+ XFS_IFORK_DSIZE(ip) / sizeof(xfs_bmbt_rec_t));
+
+ ASSERT(((ip->i_d.di_flags & XFS_DIFLAG_REALTIME) != 0) ==
+ ((ip->i_iocore.io_flags & XFS_IOCORE_RT) != 0));
+
+ *ipp = ip;
+
+ /*
+ * If we have a real type for an on-disk inode, we can set ops(&unlock)
+ * now. If it's a new inode being created, xfs_ialloc will handle it.
+ */
+ VFS_INIT_VNODE(XFS_MTOVFS(mp), vp, XFS_ITOBHV(ip), 1);
+
+ return 0;
+}
+
+
+/*
+ * The 'normal' internal xfs_iget, if needed it will
+ * 'allocate', or 'get', the vnode.
+ */
+int
+xfs_iget(
+ xfs_mount_t *mp,
+ xfs_trans_t *tp,
+ xfs_ino_t ino,
+ uint flags,
+ uint lock_flags,
+ xfs_inode_t **ipp,
+ xfs_daddr_t bno)
+{
+ struct inode *inode;
+ vnode_t *vp = NULL;
+ int error;
+
+retry:
+ XFS_STATS_INC(xs_ig_attempts);
+
+ if ((inode = VFS_GET_INODE(XFS_MTOVFS(mp), ino, 0))) {
+ bhv_desc_t *bdp;
+ xfs_inode_t *ip;
+ int newnode;
+
+ vp = LINVFS_GET_VP(inode);
+ if (inode->i_state & I_NEW) {
+inode_allocate:
+ vn_initialize(inode);
+ error = xfs_iget_core(vp, mp, tp, ino, flags,
+ lock_flags, ipp, bno);
+ if (error) {
+ vn_mark_bad(vp);
+ if (inode->i_state & I_NEW)
+ unlock_new_inode(inode);
+ iput(inode);
+ }
+ } else {
+ /* These are true if the inode is in inactive or
+ * reclaim. The linux inode is about to go away,
+ * wait for that path to finish, and try again.
+ */
+ if (vp->v_flag & (VINACT | VRECLM)) {
+ vn_wait(vp);
+ iput(inode);
+ goto retry;
+ }
+
+ if (is_bad_inode(inode)) {
+ iput(inode);
+ return EIO;
+ }
+
+ bdp = vn_bhv_lookup(VN_BHV_HEAD(vp), &xfs_vnodeops);
+ if (bdp == NULL) {
+ XFS_STATS_INC(xs_ig_dup);
+ goto inode_allocate;
+ }
+ ip = XFS_BHVTOI(bdp);
+ if (lock_flags != 0)
+ xfs_ilock(ip, lock_flags);
+ newnode = (ip->i_d.di_mode == 0);
+ if (newnode)
+ xfs_iocore_inode_reinit(ip);
+ XFS_STATS_INC(xs_ig_found);
+ *ipp = ip;
+ error = 0;
+ }
+ } else
+ error = ENOMEM; /* If we got no inode we are out of memory */
+
+ return error;
+}
+
+/*
+ * Do the setup for the various locks within the incore inode.
+ */
+void
+xfs_inode_lock_init(
+ xfs_inode_t *ip,
+ vnode_t *vp)
+{
+ mrlock_init(&ip->i_lock, MRLOCK_ALLOW_EQUAL_PRI|MRLOCK_BARRIER,
+ "xfsino", (long)vp->v_number);
+ mrlock_init(&ip->i_iolock, MRLOCK_BARRIER, "xfsio", vp->v_number);
+ init_waitqueue_head(&ip->i_ipin_wait);
+ atomic_set(&ip->i_pincount, 0);
+ init_sema(&ip->i_flock, 1, "xfsfino", vp->v_number);
+}
+
+/*
+ * Look for the inode corresponding to the given ino in the hash table.
+ * If it is there and its i_transp pointer matches tp, return it.
+ * Otherwise, return NULL.
+ */
+xfs_inode_t *
+xfs_inode_incore(xfs_mount_t *mp,
+ xfs_ino_t ino,
+ xfs_trans_t *tp)
+{
+ xfs_ihash_t *ih;
+ xfs_inode_t *ip;
+
+ ih = XFS_IHASH(mp, ino);
+ read_lock(&ih->ih_lock);
+ for (ip = ih->ih_next; ip != NULL; ip = ip->i_next) {
+ if (ip->i_ino == ino) {
+ /*
+ * If we find it and tp matches, return it.
+ * Otherwise break from the loop and return
+ * NULL.
+ */
+ if (ip->i_transp == tp) {
+ read_unlock(&ih->ih_lock);
+ return (ip);
+ }
+ break;
+ }
+ }
+ read_unlock(&ih->ih_lock);
+ return (NULL);
+}
+
+/*
+ * Decrement reference count of an inode structure and unlock it.
+ *
+ * ip -- the inode being released
+ * lock_flags -- this parameter indicates the inode's locks to be
+ * to be released. See the comment on xfs_iunlock() for a list
+ * of valid values.
+ */
+void
+xfs_iput(xfs_inode_t *ip,
+ uint lock_flags)
+{
+ vnode_t *vp = XFS_ITOV(ip);
+
+ vn_trace_entry(vp, "xfs_iput", (inst_t *)__return_address);
+
+ xfs_iunlock(ip, lock_flags);
+
+ VN_RELE(vp);
+}
+
+/*
+ * Special iput for brand-new inodes that are still locked
+ */
+void
+xfs_iput_new(xfs_inode_t *ip,
+ uint lock_flags)
+{
+ vnode_t *vp = XFS_ITOV(ip);
+ struct inode *inode = LINVFS_GET_IP(vp);
+
+ vn_trace_entry(vp, "xfs_iput_new", (inst_t *)__return_address);
+
+ if ((ip->i_d.di_mode == 0)) {
+ ASSERT(!(ip->i_flags & XFS_IRECLAIMABLE));
+ vn_mark_bad(vp);
+ }
+ if (inode->i_state & I_NEW)
+ unlock_new_inode(inode);
+ if (lock_flags)
+ xfs_iunlock(ip, lock_flags);
+ VN_RELE(vp);
+}
+
+
+/*
+ * This routine embodies the part of the reclaim code that pulls
+ * the inode from the inode hash table and the mount structure's
+ * inode list.
+ * This should only be called from xfs_reclaim().
+ */
+void
+xfs_ireclaim(xfs_inode_t *ip)
+{
+ vnode_t *vp;
+
+ /*
+ * Remove from old hash list and mount list.
+ */
+ XFS_STATS_INC(xs_ig_reclaims);
+
+ xfs_iextract(ip);
+
+ /*
+ * Here we do a spurious inode lock in order to coordinate with
+ * xfs_sync(). This is because xfs_sync() references the inodes
+ * in the mount list without taking references on the corresponding
+ * vnodes. We make that OK here by ensuring that we wait until
+ * the inode is unlocked in xfs_sync() before we go ahead and
+ * free it. We get both the regular lock and the io lock because
+ * the xfs_sync() code may need to drop the regular one but will
+ * still hold the io lock.
+ */
+ xfs_ilock(ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL);
+
+ /*
+ * Release dquots (and their references) if any. An inode may escape
+ * xfs_inactive and get here via vn_alloc->vn_reclaim path.
+ */
+ XFS_QM_DQDETACH(ip->i_mount, ip);
+
+ /*
+ * Pull our behavior descriptor from the vnode chain.
+ */
+ vp = XFS_ITOV_NULL(ip);
+ if (vp) {
+ vn_bhv_remove(VN_BHV_HEAD(vp), XFS_ITOBHV(ip));
+ }
+
+ /*
+ * Free all memory associated with the inode.
+ */
+ xfs_idestroy(ip);
+}
+
+/*
+ * This routine removes an about-to-be-destroyed inode from
+ * all of the lists in which it is located with the exception
+ * of the behavior chain.
+ */
+void
+xfs_iextract(
+ xfs_inode_t *ip)
+{
+ xfs_ihash_t *ih;
+ xfs_inode_t *iq;
+ xfs_mount_t *mp;
+ xfs_chash_t *ch;
+ xfs_chashlist_t *chl, *chm;
+ SPLDECL(s);
+
+ ih = ip->i_hash;
+ write_lock(&ih->ih_lock);
+ if ((iq = ip->i_next)) {
+ iq->i_prevp = ip->i_prevp;
+ }
+ *ip->i_prevp = iq;
+ write_unlock(&ih->ih_lock);
+
+ /*
+ * Remove from cluster hash list
+ * 1) delete the chashlist if this is the last inode on the chashlist
+ * 2) unchain from list of inodes
+ * 3) point chashlist->chl_ip to 'chl_next' if to this inode.
+ */
+ mp = ip->i_mount;
+ ch = XFS_CHASH(mp, ip->i_blkno);
+ s = mutex_spinlock(&ch->ch_lock);
+
+ if (ip->i_cnext == ip) {
+ /* Last inode on chashlist */
+ ASSERT(ip->i_cnext == ip && ip->i_cprev == ip);
+ ASSERT(ip->i_chash != NULL);
+ chm=NULL;
+ for (chl = ch->ch_list; chl != NULL; chl = chl->chl_next) {
+ if (chl->chl_blkno == ip->i_blkno) {
+ if (chm == NULL) {
+ /* first item on the list */
+ ch->ch_list = chl->chl_next;
+ } else {
+ chm->chl_next = chl->chl_next;
+ }
+ kmem_zone_free(xfs_chashlist_zone, chl);
+ break;
+ } else {
+ ASSERT(chl->chl_ip != ip);
+ chm = chl;
+ }
+ }
+ ASSERT_ALWAYS(chl != NULL);
+ } else {
+ /* delete one inode from a non-empty list */
+ iq = ip->i_cnext;
+ iq->i_cprev = ip->i_cprev;
+ ip->i_cprev->i_cnext = iq;
+ if (ip->i_chash->chl_ip == ip) {
+ ip->i_chash->chl_ip = iq;
+ }
+ ip->i_chash = __return_address;
+ ip->i_cprev = __return_address;
+ ip->i_cnext = __return_address;
+ }
+ mutex_spinunlock(&ch->ch_lock, s);
+
+ /*
+ * Remove from mount's inode list.
+ */
+ XFS_MOUNT_ILOCK(mp);
+ ASSERT((ip->i_mnext != NULL) && (ip->i_mprev != NULL));
+ iq = ip->i_mnext;
+ iq->i_mprev = ip->i_mprev;
+ ip->i_mprev->i_mnext = iq;
+
+ /*
+ * Fix up the head pointer if it points to the inode being deleted.
+ */
+ if (mp->m_inodes == ip) {
+ if (ip == iq) {
+ mp->m_inodes = NULL;
+ } else {
+ mp->m_inodes = iq;
+ }
+ }
+
+ /* Deal with the deleted inodes list */
+ list_del_init(&ip->i_reclaim);
+
+ mp->m_ireclaims++;
+ XFS_MOUNT_IUNLOCK(mp);
+}
+
+/*
+ * This is a wrapper routine around the xfs_ilock() routine
+ * used to centralize some grungy code. It is used in places
+ * that wish to lock the inode solely for reading the extents.
+ * The reason these places can't just call xfs_ilock(SHARED)
+ * is that the inode lock also guards to bringing in of the
+ * extents from disk for a file in b-tree format. If the inode
+ * is in b-tree format, then we need to lock the inode exclusively
+ * until the extents are read in. Locking it exclusively all
+ * the time would limit our parallelism unnecessarily, though.
+ * What we do instead is check to see if the extents have been
+ * read in yet, and only lock the inode exclusively if they
+ * have not.
+ *
+ * The function returns a value which should be given to the
+ * corresponding xfs_iunlock_map_shared(). This value is
+ * the mode in which the lock was actually taken.
+ */
+uint
+xfs_ilock_map_shared(
+ xfs_inode_t *ip)
+{
+ uint lock_mode;
+
+ if ((ip->i_d.di_format == XFS_DINODE_FMT_BTREE) &&
+ ((ip->i_df.if_flags & XFS_IFEXTENTS) == 0)) {
+ lock_mode = XFS_ILOCK_EXCL;
+ } else {
+ lock_mode = XFS_ILOCK_SHARED;
+ }
+
+ xfs_ilock(ip, lock_mode);
+
+ return lock_mode;
+}
+
+/*
+ * This is simply the unlock routine to go with xfs_ilock_map_shared().
+ * All it does is call xfs_iunlock() with the given lock_mode.
+ */
+void
+xfs_iunlock_map_shared(
+ xfs_inode_t *ip,
+ unsigned int lock_mode)
+{
+ xfs_iunlock(ip, lock_mode);
+}
+
+/*
+ * The xfs inode contains 2 locks: a multi-reader lock called the
+ * i_iolock and a multi-reader lock called the i_lock. This routine
+ * allows either or both of the locks to be obtained.
+ *
+ * The 2 locks should always be ordered so that the IO lock is
+ * obtained first in order to prevent deadlock.
+ *
+ * ip -- the inode being locked
+ * lock_flags -- this parameter indicates the inode's locks
+ * to be locked. It can be:
+ * XFS_IOLOCK_SHARED,
+ * XFS_IOLOCK_EXCL,
+ * XFS_ILOCK_SHARED,
+ * XFS_ILOCK_EXCL,
+ * XFS_IOLOCK_SHARED | XFS_ILOCK_SHARED,
+ * XFS_IOLOCK_SHARED | XFS_ILOCK_EXCL,
+ * XFS_IOLOCK_EXCL | XFS_ILOCK_SHARED,
+ * XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL
+ */
+void
+xfs_ilock(xfs_inode_t *ip,
+ uint lock_flags)
+{
+ /*
+ * You can't set both SHARED and EXCL for the same lock,
+ * and only XFS_IOLOCK_SHARED, XFS_IOLOCK_EXCL, XFS_ILOCK_SHARED,
+ * and XFS_ILOCK_EXCL are valid values to set in lock_flags.
+ */
+ ASSERT((lock_flags & (XFS_IOLOCK_SHARED | XFS_IOLOCK_EXCL)) !=
+ (XFS_IOLOCK_SHARED | XFS_IOLOCK_EXCL));
+ ASSERT((lock_flags & (XFS_ILOCK_SHARED | XFS_ILOCK_EXCL)) !=
+ (XFS_ILOCK_SHARED | XFS_ILOCK_EXCL));
+ ASSERT((lock_flags & ~XFS_LOCK_MASK) == 0);
+
+ if (lock_flags & XFS_IOLOCK_EXCL) {
+ mrupdate(&ip->i_iolock);
+ } else if (lock_flags & XFS_IOLOCK_SHARED) {
+ mraccess(&ip->i_iolock);
+ }
+ if (lock_flags & XFS_ILOCK_EXCL) {
+ mrupdate(&ip->i_lock);
+ } else if (lock_flags & XFS_ILOCK_SHARED) {
+ mraccess(&ip->i_lock);
+ }
+ xfs_ilock_trace(ip, 1, lock_flags, (inst_t *)__return_address);
+}
+
+/*
+ * This is just like xfs_ilock(), except that the caller
+ * is guaranteed not to sleep. It returns 1 if it gets
+ * the requested locks and 0 otherwise. If the IO lock is
+ * obtained but the inode lock cannot be, then the IO lock
+ * is dropped before returning.
+ *
+ * ip -- the inode being locked
+ * lock_flags -- this parameter indicates the inode's locks to be
+ * to be locked. See the comment for xfs_ilock() for a list
+ * of valid values.
+ *
+ */
+int
+xfs_ilock_nowait(xfs_inode_t *ip,
+ uint lock_flags)
+{
+ int iolocked;
+ int ilocked;
+
+ /*
+ * You can't set both SHARED and EXCL for the same lock,
+ * and only XFS_IOLOCK_SHARED, XFS_IOLOCK_EXCL, XFS_ILOCK_SHARED,
+ * and XFS_ILOCK_EXCL are valid values to set in lock_flags.
+ */
+ ASSERT((lock_flags & (XFS_IOLOCK_SHARED | XFS_IOLOCK_EXCL)) !=
+ (XFS_IOLOCK_SHARED | XFS_IOLOCK_EXCL));
+ ASSERT((lock_flags & (XFS_ILOCK_SHARED | XFS_ILOCK_EXCL)) !=
+ (XFS_ILOCK_SHARED | XFS_ILOCK_EXCL));
+ ASSERT((lock_flags & ~XFS_LOCK_MASK) == 0);
+
+ iolocked = 0;
+ if (lock_flags & XFS_IOLOCK_EXCL) {
+ iolocked = mrtryupdate(&ip->i_iolock);
+ if (!iolocked) {
+ return 0;
+ }
+ } else if (lock_flags & XFS_IOLOCK_SHARED) {
+ iolocked = mrtryaccess(&ip->i_iolock);
+ if (!iolocked) {
+ return 0;
+ }
+ }
+ if (lock_flags & XFS_ILOCK_EXCL) {
+ ilocked = mrtryupdate(&ip->i_lock);
+ if (!ilocked) {
+ if (iolocked) {
+ mrunlock(&ip->i_iolock);
+ }
+ return 0;
+ }
+ } else if (lock_flags & XFS_ILOCK_SHARED) {
+ ilocked = mrtryaccess(&ip->i_lock);
+ if (!ilocked) {
+ if (iolocked) {
+ mrunlock(&ip->i_iolock);
+ }
+ return 0;
+ }
+ }
+ xfs_ilock_trace(ip, 2, lock_flags, (inst_t *)__return_address);
+ return 1;
+}
+
+/*
+ * xfs_iunlock() is used to drop the inode locks acquired with
+ * xfs_ilock() and xfs_ilock_nowait(). The caller must pass
+ * in the flags given to xfs_ilock() or xfs_ilock_nowait() so
+ * that we know which locks to drop.
+ *
+ * ip -- the inode being unlocked
+ * lock_flags -- this parameter indicates the inode's locks to be
+ * to be unlocked. See the comment for xfs_ilock() for a list
+ * of valid values for this parameter.
+ *
+ */
+void
+xfs_iunlock(xfs_inode_t *ip,
+ uint lock_flags)
+{
+ /*
+ * You can't set both SHARED and EXCL for the same lock,
+ * and only XFS_IOLOCK_SHARED, XFS_IOLOCK_EXCL, XFS_ILOCK_SHARED,
+ * and XFS_ILOCK_EXCL are valid values to set in lock_flags.
+ */
+ ASSERT((lock_flags & (XFS_IOLOCK_SHARED | XFS_IOLOCK_EXCL)) !=
+ (XFS_IOLOCK_SHARED | XFS_IOLOCK_EXCL));
+ ASSERT((lock_flags & (XFS_ILOCK_SHARED | XFS_ILOCK_EXCL)) !=
+ (XFS_ILOCK_SHARED | XFS_ILOCK_EXCL));
+ ASSERT((lock_flags & ~(XFS_LOCK_MASK | XFS_IUNLOCK_NONOTIFY)) == 0);
+ ASSERT(lock_flags != 0);
+
+ if (lock_flags & (XFS_IOLOCK_SHARED | XFS_IOLOCK_EXCL)) {
+ ASSERT(!(lock_flags & XFS_IOLOCK_SHARED) ||
+ (ismrlocked(&ip->i_iolock, MR_ACCESS)));
+ ASSERT(!(lock_flags & XFS_IOLOCK_EXCL) ||
+ (ismrlocked(&ip->i_iolock, MR_UPDATE)));
+ mrunlock(&ip->i_iolock);
+ }
+
+ if (lock_flags & (XFS_ILOCK_SHARED | XFS_ILOCK_EXCL)) {
+ ASSERT(!(lock_flags & XFS_ILOCK_SHARED) ||
+ (ismrlocked(&ip->i_lock, MR_ACCESS)));
+ ASSERT(!(lock_flags & XFS_ILOCK_EXCL) ||
+ (ismrlocked(&ip->i_lock, MR_UPDATE)));
+ mrunlock(&ip->i_lock);
+
+ /*
+ * Let the AIL know that this item has been unlocked in case
+ * it is in the AIL and anyone is waiting on it. Don't do
+ * this if the caller has asked us not to.
+ */
+ if (!(lock_flags & XFS_IUNLOCK_NONOTIFY) &&
+ ip->i_itemp != NULL) {
+ xfs_trans_unlocked_item(ip->i_mount,
+ (xfs_log_item_t*)(ip->i_itemp));
+ }
+ }
+ xfs_ilock_trace(ip, 3, lock_flags, (inst_t *)__return_address);
+}
+
+/*
+ * give up write locks. the i/o lock cannot be held nested
+ * if it is being demoted.
+ */
+void
+xfs_ilock_demote(xfs_inode_t *ip,
+ uint lock_flags)
+{
+ ASSERT(lock_flags & (XFS_IOLOCK_EXCL|XFS_ILOCK_EXCL));
+ ASSERT((lock_flags & ~(XFS_IOLOCK_EXCL|XFS_ILOCK_EXCL)) == 0);
+
+ if (lock_flags & XFS_ILOCK_EXCL) {
+ ASSERT(ismrlocked(&ip->i_lock, MR_UPDATE));
+ mrdemote(&ip->i_lock);
+ }
+ if (lock_flags & XFS_IOLOCK_EXCL) {
+ ASSERT(ismrlocked(&ip->i_iolock, MR_UPDATE));
+ mrdemote(&ip->i_iolock);
+ }
+}
+
+/*
+ * The following three routines simply manage the i_flock
+ * semaphore embedded in the inode. This semaphore synchronizes
+ * processes attempting to flush the in-core inode back to disk.
+ */
+void
+xfs_iflock(xfs_inode_t *ip)
+{
+ psema(&(ip->i_flock), PINOD|PLTWAIT);
+}
+
+int
+xfs_iflock_nowait(xfs_inode_t *ip)
+{
+ return (cpsema(&(ip->i_flock)));
+}
+
+void
+xfs_ifunlock(xfs_inode_t *ip)
+{
+ ASSERT(valusema(&(ip->i_flock)) <= 0);
+ vsema(&(ip->i_flock));
+}
diff --git a/fs/xfs/xfs_imap.h b/fs/xfs/xfs_imap.h
new file mode 100644
index 00000000000000..e385064a066a5f
--- /dev/null
+++ b/fs/xfs/xfs_imap.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_IMAP_H__
+#define __XFS_IMAP_H__
+
+/*
+ * This is the structure passed to xfs_imap() to map
+ * an inode number to its on disk location.
+ */
+typedef struct xfs_imap {
+ xfs_daddr_t im_blkno; /* starting BB of inode chunk */
+ uint im_len; /* length in BBs of inode chunk */
+ xfs_agblock_t im_agblkno; /* logical block of inode chunk in ag */
+ ushort im_ioffset; /* inode offset in block in "inodes" */
+ ushort im_boffset; /* inode offset in block in bytes */
+} xfs_imap_t;
+
+#ifdef __KERNEL__
+struct xfs_mount;
+struct xfs_trans;
+int xfs_imap(struct xfs_mount *, struct xfs_trans *, xfs_ino_t,
+ xfs_imap_t *, uint);
+#endif
+
+#endif /* __XFS_IMAP_H__ */
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
new file mode 100644
index 00000000000000..5573702347ed6b
--- /dev/null
+++ b/fs/xfs/xfs_inode.c
@@ -0,0 +1,3875 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_trans_priv.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_btree.h"
+#include "xfs_imap.h"
+#include "xfs_alloc.h"
+#include "xfs_ialloc.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode_item.h"
+#include "xfs_inode.h"
+#include "xfs_bmap.h"
+#include "xfs_buf_item.h"
+#include "xfs_rw.h"
+#include "xfs_error.h"
+#include "xfs_bit.h"
+#include "xfs_utils.h"
+#include "xfs_dir2_trace.h"
+#include "xfs_quota.h"
+#include "xfs_mac.h"
+#include "xfs_acl.h"
+
+
+kmem_zone_t *xfs_ifork_zone;
+kmem_zone_t *xfs_inode_zone;
+kmem_zone_t *xfs_chashlist_zone;
+
+/*
+ * Used in xfs_itruncate(). This is the maximum number of extents
+ * freed from a file in a single transaction.
+ */
+#define XFS_ITRUNC_MAX_EXTENTS 2
+
+STATIC int xfs_iflush_int(xfs_inode_t *, xfs_buf_t *);
+STATIC int xfs_iformat_local(xfs_inode_t *, xfs_dinode_t *, int, int);
+STATIC int xfs_iformat_extents(xfs_inode_t *, xfs_dinode_t *, int);
+STATIC int xfs_iformat_btree(xfs_inode_t *, xfs_dinode_t *, int);
+
+
+#ifdef DEBUG
+/*
+ * Make sure that the extents in the given memory buffer
+ * are valid.
+ */
+STATIC void
+xfs_validate_extents(
+ xfs_bmbt_rec_t *ep,
+ int nrecs,
+ int disk,
+ xfs_exntfmt_t fmt)
+{
+ xfs_bmbt_irec_t irec;
+ xfs_bmbt_rec_t rec;
+ int i;
+
+ for (i = 0; i < nrecs; i++) {
+ rec.l0 = get_unaligned((__uint64_t*)&ep->l0);
+ rec.l1 = get_unaligned((__uint64_t*)&ep->l1);
+ if (disk)
+ xfs_bmbt_disk_get_all(&rec, &irec);
+ else
+ xfs_bmbt_get_all(&rec, &irec);
+ if (fmt == XFS_EXTFMT_NOSTATE)
+ ASSERT(irec.br_state == XFS_EXT_NORM);
+ ep++;
+ }
+}
+#else /* DEBUG */
+#define xfs_validate_extents(ep, nrecs, disk, fmt)
+#endif /* DEBUG */
+
+/*
+ * Check that none of the inode's in the buffer have a next
+ * unlinked field of 0.
+ */
+#if defined(DEBUG)
+void
+xfs_inobp_check(
+ xfs_mount_t *mp,
+ xfs_buf_t *bp)
+{
+ int i;
+ int j;
+ xfs_dinode_t *dip;
+
+ j = mp->m_inode_cluster_size >> mp->m_sb.sb_inodelog;
+
+ for (i = 0; i < j; i++) {
+ dip = (xfs_dinode_t *)xfs_buf_offset(bp,
+ i * mp->m_sb.sb_inodesize);
+ if (INT_ISZERO(dip->di_next_unlinked, ARCH_CONVERT)) {
+ xfs_fs_cmn_err(CE_ALERT, mp,
+ "Detected a bogus zero next_unlinked field in incore inode buffer 0x%p. About to pop an ASSERT.",
+ bp);
+ ASSERT(!INT_ISZERO(dip->di_next_unlinked, ARCH_CONVERT));
+ }
+ }
+}
+#endif
+
+/*
+ * called from bwrite on xfs inode buffers
+ */
+void
+xfs_inobp_bwcheck(xfs_buf_t *bp)
+{
+ xfs_mount_t *mp;
+ int i;
+ int j;
+ xfs_dinode_t *dip;
+
+ ASSERT(XFS_BUF_FSPRIVATE3(bp, void *) != NULL);
+
+ mp = XFS_BUF_FSPRIVATE3(bp, xfs_mount_t *);
+
+
+ j = mp->m_inode_cluster_size >> mp->m_sb.sb_inodelog;
+
+ for (i = 0; i < j; i++) {
+ dip = (xfs_dinode_t *) xfs_buf_offset(bp,
+ i * mp->m_sb.sb_inodesize);
+ if (INT_GET(dip->di_core.di_magic, ARCH_CONVERT) != XFS_DINODE_MAGIC) {
+ cmn_err(CE_WARN,
+"Bad magic # 0x%x in XFS inode buffer 0x%Lx, starting blockno %Ld, offset 0x%x",
+ INT_GET(dip->di_core.di_magic, ARCH_CONVERT),
+ (__uint64_t)(__psunsigned_t) bp,
+ (__int64_t) XFS_BUF_ADDR(bp),
+ xfs_buf_offset(bp, i * mp->m_sb.sb_inodesize));
+ xfs_fs_cmn_err(CE_WARN, mp,
+ "corrupt, unmount and run xfs_repair");
+ }
+ if (INT_ISZERO(dip->di_next_unlinked, ARCH_CONVERT)) {
+ cmn_err(CE_WARN,
+"Bad next_unlinked field (0) in XFS inode buffer 0x%p, starting blockno %Ld, offset 0x%x",
+ (__uint64_t)(__psunsigned_t) bp,
+ (__int64_t) XFS_BUF_ADDR(bp),
+ xfs_buf_offset(bp, i * mp->m_sb.sb_inodesize));
+ xfs_fs_cmn_err(CE_WARN, mp,
+ "corrupt, unmount and run xfs_repair");
+ }
+ }
+
+ return;
+}
+
+/*
+ * This routine is called to map an inode number within a file
+ * system to the buffer containing the on-disk version of the
+ * inode. It returns a pointer to the buffer containing the
+ * on-disk inode in the bpp parameter, and in the dip parameter
+ * it returns a pointer to the on-disk inode within that buffer.
+ *
+ * If a non-zero error is returned, then the contents of bpp and
+ * dipp are undefined.
+ *
+ * Use xfs_imap() to determine the size and location of the
+ * buffer to read from disk.
+ */
+int
+xfs_inotobp(
+ xfs_mount_t *mp,
+ xfs_trans_t *tp,
+ xfs_ino_t ino,
+ xfs_dinode_t **dipp,
+ xfs_buf_t **bpp,
+ int *offset)
+{
+ int di_ok;
+ xfs_imap_t imap;
+ xfs_buf_t *bp;
+ int error;
+ xfs_dinode_t *dip;
+
+ /*
+ * Call the space managment code to find the location of the
+ * inode on disk.
+ */
+ imap.im_blkno = 0;
+ error = xfs_imap(mp, tp, ino, &imap, XFS_IMAP_LOOKUP);
+ if (error != 0) {
+ cmn_err(CE_WARN,
+ "xfs_inotobp: xfs_imap() returned an "
+ "error %d on %s. Returning error.", error, mp->m_fsname);
+ return error;
+ }
+
+ /*
+ * If the inode number maps to a block outside the bounds of the
+ * file system then return NULL rather than calling read_buf
+ * and panicing when we get an error from the driver.
+ */
+ if ((imap.im_blkno + imap.im_len) >
+ XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks)) {
+ cmn_err(CE_WARN,
+ "xfs_inotobp: inode number (%d + %d) maps to a block outside the bounds "
+ "of the file system %s. Returning EINVAL.",
+ imap.im_blkno, imap.im_len,mp->m_fsname);
+ return XFS_ERROR(EINVAL);
+ }
+
+ /*
+ * Read in the buffer. If tp is NULL, xfs_trans_read_buf() will
+ * default to just a read_buf() call.
+ */
+ error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp, imap.im_blkno,
+ (int)imap.im_len, XFS_BUF_LOCK, &bp);
+
+ if (error) {
+ cmn_err(CE_WARN,
+ "xfs_inotobp: xfs_trans_read_buf() returned an "
+ "error %d on %s. Returning error.", error, mp->m_fsname);
+ return error;
+ }
+ dip = (xfs_dinode_t *)xfs_buf_offset(bp, 0);
+ di_ok =
+ INT_GET(dip->di_core.di_magic, ARCH_CONVERT) == XFS_DINODE_MAGIC &&
+ XFS_DINODE_GOOD_VERSION(INT_GET(dip->di_core.di_version, ARCH_CONVERT));
+ if (unlikely(XFS_TEST_ERROR(!di_ok, mp, XFS_ERRTAG_ITOBP_INOTOBP,
+ XFS_RANDOM_ITOBP_INOTOBP))) {
+ XFS_CORRUPTION_ERROR("xfs_inotobp", XFS_ERRLEVEL_LOW, mp, dip);
+ xfs_trans_brelse(tp, bp);
+ cmn_err(CE_WARN,
+ "xfs_inotobp: XFS_TEST_ERROR() returned an "
+ "error on %s. Returning EFSCORRUPTED.", mp->m_fsname);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+
+ xfs_inobp_check(mp, bp);
+
+ /*
+ * Set *dipp to point to the on-disk inode in the buffer.
+ */
+ *dipp = (xfs_dinode_t *)xfs_buf_offset(bp, imap.im_boffset);
+ *bpp = bp;
+ *offset = imap.im_boffset;
+ return 0;
+}
+
+
+/*
+ * This routine is called to map an inode to the buffer containing
+ * the on-disk version of the inode. It returns a pointer to the
+ * buffer containing the on-disk inode in the bpp parameter, and in
+ * the dip parameter it returns a pointer to the on-disk inode within
+ * that buffer.
+ *
+ * If a non-zero error is returned, then the contents of bpp and
+ * dipp are undefined.
+ *
+ * If the inode is new and has not yet been initialized, use xfs_imap()
+ * to determine the size and location of the buffer to read from disk.
+ * If the inode has already been mapped to its buffer and read in once,
+ * then use the mapping information stored in the inode rather than
+ * calling xfs_imap(). This allows us to avoid the overhead of looking
+ * at the inode btree for small block file systems (see xfs_dilocate()).
+ * We can tell whether the inode has been mapped in before by comparing
+ * its disk block address to 0. Only uninitialized inodes will have
+ * 0 for the disk block address.
+ */
+int
+xfs_itobp(
+ xfs_mount_t *mp,
+ xfs_trans_t *tp,
+ xfs_inode_t *ip,
+ xfs_dinode_t **dipp,
+ xfs_buf_t **bpp,
+ xfs_daddr_t bno)
+{
+ xfs_buf_t *bp;
+ int error;
+ xfs_imap_t imap;
+#ifdef __KERNEL__
+ int i;
+ int ni;
+#endif
+
+ if (ip->i_blkno == (xfs_daddr_t)0) {
+ /*
+ * Call the space management code to find the location of the
+ * inode on disk.
+ */
+ imap.im_blkno = bno;
+ error = xfs_imap(mp, tp, ip->i_ino, &imap, XFS_IMAP_LOOKUP);
+ if (error != 0) {
+ return error;
+ }
+
+ /*
+ * If the inode number maps to a block outside the bounds
+ * of the file system then return NULL rather than calling
+ * read_buf and panicing when we get an error from the
+ * driver.
+ */
+ if ((imap.im_blkno + imap.im_len) >
+ XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks)) {
+#ifdef DEBUG
+ xfs_fs_cmn_err(CE_ALERT, mp, "xfs_itobp: "
+ "(imap.im_blkno (0x%llx) "
+ "+ imap.im_len (0x%llx)) > "
+ " XFS_FSB_TO_BB(mp, "
+ "mp->m_sb.sb_dblocks) (0x%llx)",
+ (unsigned long long) imap.im_blkno,
+ (unsigned long long) imap.im_len,
+ XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks));
+#endif /* DEBUG */
+ return XFS_ERROR(EINVAL);
+ }
+
+ /*
+ * Fill in the fields in the inode that will be used to
+ * map the inode to its buffer from now on.
+ */
+ ip->i_blkno = imap.im_blkno;
+ ip->i_len = imap.im_len;
+ ip->i_boffset = imap.im_boffset;
+ } else {
+ /*
+ * We've already mapped the inode once, so just use the
+ * mapping that we saved the first time.
+ */
+ imap.im_blkno = ip->i_blkno;
+ imap.im_len = ip->i_len;
+ imap.im_boffset = ip->i_boffset;
+ }
+ ASSERT(bno == 0 || bno == imap.im_blkno);
+
+ /*
+ * Read in the buffer. If tp is NULL, xfs_trans_read_buf() will
+ * default to just a read_buf() call.
+ */
+ error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp, imap.im_blkno,
+ (int)imap.im_len, XFS_BUF_LOCK, &bp);
+
+ if (error) {
+#ifdef DEBUG
+ xfs_fs_cmn_err(CE_ALERT, mp, "xfs_itobp: "
+ "xfs_trans_read_buf() returned error %d, "
+ "imap.im_blkno 0x%llx, imap.im_len 0x%llx",
+ error, (unsigned long long) imap.im_blkno,
+ (unsigned long long) imap.im_len);
+#endif /* DEBUG */
+ return error;
+ }
+#ifdef __KERNEL__
+ /*
+ * Validate the magic number and version of every inode in the buffer
+ * (if DEBUG kernel) or the first inode in the buffer, otherwise.
+ */
+#ifdef DEBUG
+ ni = BBTOB(imap.im_len) >> mp->m_sb.sb_inodelog;
+#else
+ ni = 1;
+#endif
+ for (i = 0; i < ni; i++) {
+ int di_ok;
+ xfs_dinode_t *dip;
+
+ dip = (xfs_dinode_t *)xfs_buf_offset(bp,
+ (i << mp->m_sb.sb_inodelog));
+ di_ok = INT_GET(dip->di_core.di_magic, ARCH_CONVERT) == XFS_DINODE_MAGIC &&
+ XFS_DINODE_GOOD_VERSION(INT_GET(dip->di_core.di_version, ARCH_CONVERT));
+ if (unlikely(XFS_TEST_ERROR(!di_ok, mp, XFS_ERRTAG_ITOBP_INOTOBP,
+ XFS_RANDOM_ITOBP_INOTOBP))) {
+#ifdef DEBUG
+ prdev("bad inode magic/vsn daddr %lld #%d (magic=%x)",
+ mp->m_ddev_targp,
+ (unsigned long long)imap.im_blkno, i,
+ INT_GET(dip->di_core.di_magic, ARCH_CONVERT));
+#endif
+ XFS_CORRUPTION_ERROR("xfs_itobp", XFS_ERRLEVEL_HIGH,
+ mp, dip);
+ xfs_trans_brelse(tp, bp);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ }
+#endif /* __KERNEL__ */
+
+ xfs_inobp_check(mp, bp);
+
+ /*
+ * Mark the buffer as an inode buffer now that it looks good
+ */
+ XFS_BUF_SET_VTYPE(bp, B_FS_INO);
+
+ /*
+ * Set *dipp to point to the on-disk inode in the buffer.
+ */
+ *dipp = (xfs_dinode_t *)xfs_buf_offset(bp, imap.im_boffset);
+ *bpp = bp;
+ return 0;
+}
+
+/*
+ * Move inode type and inode format specific information from the
+ * on-disk inode to the in-core inode. For fifos, devs, and sockets
+ * this means set if_rdev to the proper value. For files, directories,
+ * and symlinks this means to bring in the in-line data or extent
+ * pointers. For a file in B-tree format, only the root is immediately
+ * brought in-core. The rest will be in-lined in if_extents when it
+ * is first referenced (see xfs_iread_extents()).
+ */
+STATIC int
+xfs_iformat(
+ xfs_inode_t *ip,
+ xfs_dinode_t *dip)
+{
+ xfs_attr_shortform_t *atp;
+ int size;
+ int error;
+ xfs_fsize_t di_size;
+ ip->i_df.if_ext_max =
+ XFS_IFORK_DSIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t);
+ error = 0;
+
+ if (unlikely(
+ INT_GET(dip->di_core.di_nextents, ARCH_CONVERT) +
+ INT_GET(dip->di_core.di_anextents, ARCH_CONVERT) >
+ INT_GET(dip->di_core.di_nblocks, ARCH_CONVERT))) {
+ xfs_fs_cmn_err(CE_WARN, ip->i_mount,
+ "corrupt dinode %Lu, extent total = %d, nblocks = %Lu."
+ " Unmount and run xfs_repair.",
+ (unsigned long long)ip->i_ino,
+ (int)(INT_GET(dip->di_core.di_nextents, ARCH_CONVERT)
+ + INT_GET(dip->di_core.di_anextents, ARCH_CONVERT)),
+ (unsigned long long)
+ INT_GET(dip->di_core.di_nblocks, ARCH_CONVERT));
+ XFS_CORRUPTION_ERROR("xfs_iformat(1)", XFS_ERRLEVEL_LOW,
+ ip->i_mount, dip);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+
+ if (unlikely(INT_GET(dip->di_core.di_forkoff, ARCH_CONVERT) > ip->i_mount->m_sb.sb_inodesize)) {
+ xfs_fs_cmn_err(CE_WARN, ip->i_mount,
+ "corrupt dinode %Lu, forkoff = 0x%x."
+ " Unmount and run xfs_repair.",
+ (unsigned long long)ip->i_ino,
+ (int)(INT_GET(dip->di_core.di_forkoff, ARCH_CONVERT)));
+ XFS_CORRUPTION_ERROR("xfs_iformat(2)", XFS_ERRLEVEL_LOW,
+ ip->i_mount, dip);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+
+ switch (ip->i_d.di_mode & S_IFMT) {
+ case S_IFIFO:
+ case S_IFCHR:
+ case S_IFBLK:
+ case S_IFSOCK:
+ if (unlikely(INT_GET(dip->di_core.di_format, ARCH_CONVERT) != XFS_DINODE_FMT_DEV)) {
+ XFS_CORRUPTION_ERROR("xfs_iformat(3)", XFS_ERRLEVEL_LOW,
+ ip->i_mount, dip);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ ip->i_d.di_size = 0;
+ ip->i_df.if_u2.if_rdev = INT_GET(dip->di_u.di_dev, ARCH_CONVERT);
+ break;
+
+ case S_IFREG:
+ case S_IFLNK:
+ case S_IFDIR:
+ switch (INT_GET(dip->di_core.di_format, ARCH_CONVERT)) {
+ case XFS_DINODE_FMT_LOCAL:
+ /*
+ * no local regular files yet
+ */
+ if (unlikely((INT_GET(dip->di_core.di_mode, ARCH_CONVERT) & S_IFMT) == S_IFREG)) {
+ xfs_fs_cmn_err(CE_WARN, ip->i_mount,
+ "corrupt inode (local format for regular file) %Lu. Unmount and run xfs_repair.",
+ (unsigned long long) ip->i_ino);
+ XFS_CORRUPTION_ERROR("xfs_iformat(4)",
+ XFS_ERRLEVEL_LOW,
+ ip->i_mount, dip);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+
+ di_size = INT_GET(dip->di_core.di_size, ARCH_CONVERT);
+ if (unlikely(di_size >
+ XFS_DFORK_DSIZE_ARCH(dip, ip->i_mount, ARCH_CONVERT))) {
+ xfs_fs_cmn_err(CE_WARN, ip->i_mount,
+ "corrupt inode %Lu (bad size %Ld for local inode). Unmount and run xfs_repair.",
+ (unsigned long long) ip->i_ino,
+ (long long) di_size);
+ XFS_CORRUPTION_ERROR("xfs_iformat(5)",
+ XFS_ERRLEVEL_LOW,
+ ip->i_mount, dip);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+
+ size = (int)di_size;
+ error = xfs_iformat_local(ip, dip, XFS_DATA_FORK, size);
+ break;
+ case XFS_DINODE_FMT_EXTENTS:
+ error = xfs_iformat_extents(ip, dip, XFS_DATA_FORK);
+ break;
+ case XFS_DINODE_FMT_BTREE:
+ error = xfs_iformat_btree(ip, dip, XFS_DATA_FORK);
+ break;
+ default:
+ XFS_ERROR_REPORT("xfs_iformat(6)", XFS_ERRLEVEL_LOW,
+ ip->i_mount);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ break;
+
+ default:
+ XFS_ERROR_REPORT("xfs_iformat(7)", XFS_ERRLEVEL_LOW, ip->i_mount);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ if (error) {
+ return error;
+ }
+ if (!XFS_DFORK_Q_ARCH(dip, ARCH_CONVERT))
+ return 0;
+ ASSERT(ip->i_afp == NULL);
+ ip->i_afp = kmem_zone_zalloc(xfs_ifork_zone, KM_SLEEP);
+ ip->i_afp->if_ext_max =
+ XFS_IFORK_ASIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t);
+ switch (INT_GET(dip->di_core.di_aformat, ARCH_CONVERT)) {
+ case XFS_DINODE_FMT_LOCAL:
+ atp = (xfs_attr_shortform_t *)XFS_DFORK_APTR_ARCH(dip, ARCH_CONVERT);
+ size = (int)INT_GET(atp->hdr.totsize, ARCH_CONVERT);
+ error = xfs_iformat_local(ip, dip, XFS_ATTR_FORK, size);
+ break;
+ case XFS_DINODE_FMT_EXTENTS:
+ error = xfs_iformat_extents(ip, dip, XFS_ATTR_FORK);
+ break;
+ case XFS_DINODE_FMT_BTREE:
+ error = xfs_iformat_btree(ip, dip, XFS_ATTR_FORK);
+ break;
+ default:
+ error = XFS_ERROR(EFSCORRUPTED);
+ break;
+ }
+ if (error) {
+ kmem_zone_free(xfs_ifork_zone, ip->i_afp);
+ ip->i_afp = NULL;
+ xfs_idestroy_fork(ip, XFS_DATA_FORK);
+ }
+ return error;
+}
+
+/*
+ * The file is in-lined in the on-disk inode.
+ * If it fits into if_inline_data, then copy
+ * it there, otherwise allocate a buffer for it
+ * and copy the data there. Either way, set
+ * if_data to point at the data.
+ * If we allocate a buffer for the data, make
+ * sure that its size is a multiple of 4 and
+ * record the real size in i_real_bytes.
+ */
+STATIC int
+xfs_iformat_local(
+ xfs_inode_t *ip,
+ xfs_dinode_t *dip,
+ int whichfork,
+ int size)
+{
+ xfs_ifork_t *ifp;
+ int real_size;
+
+ /*
+ * If the size is unreasonable, then something
+ * is wrong and we just bail out rather than crash in
+ * kmem_alloc() or memcpy() below.
+ */
+ if (unlikely(size > XFS_DFORK_SIZE_ARCH(dip, ip->i_mount, whichfork, ARCH_CONVERT))) {
+ xfs_fs_cmn_err(CE_WARN, ip->i_mount,
+ "corrupt inode %Lu (bad size %d for local fork, size = %d). Unmount and run xfs_repair.",
+ (unsigned long long) ip->i_ino, size,
+ XFS_DFORK_SIZE_ARCH(dip, ip->i_mount, whichfork, ARCH_CONVERT));
+ XFS_CORRUPTION_ERROR("xfs_iformat_local", XFS_ERRLEVEL_LOW,
+ ip->i_mount, dip);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+ real_size = 0;
+ if (size == 0)
+ ifp->if_u1.if_data = NULL;
+ else if (size <= sizeof(ifp->if_u2.if_inline_data))
+ ifp->if_u1.if_data = ifp->if_u2.if_inline_data;
+ else {
+ real_size = roundup(size, 4);
+ ifp->if_u1.if_data = kmem_alloc(real_size, KM_SLEEP);
+ }
+ ifp->if_bytes = size;
+ ifp->if_real_bytes = real_size;
+ if (size)
+ memcpy(ifp->if_u1.if_data,
+ XFS_DFORK_PTR_ARCH(dip, whichfork, ARCH_CONVERT), size);
+ ifp->if_flags &= ~XFS_IFEXTENTS;
+ ifp->if_flags |= XFS_IFINLINE;
+ return 0;
+}
+
+/*
+ * The file consists of a set of extents all
+ * of which fit into the on-disk inode.
+ * If there are few enough extents to fit into
+ * the if_inline_ext, then copy them there.
+ * Otherwise allocate a buffer for them and copy
+ * them into it. Either way, set if_extents
+ * to point at the extents.
+ */
+STATIC int
+xfs_iformat_extents(
+ xfs_inode_t *ip,
+ xfs_dinode_t *dip,
+ int whichfork)
+{
+ xfs_bmbt_rec_t *ep, *dp;
+ xfs_ifork_t *ifp;
+ int nex;
+ int real_size;
+ int size;
+ int i;
+
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+ nex = XFS_DFORK_NEXTENTS_ARCH(dip, whichfork, ARCH_CONVERT);
+ size = nex * (uint)sizeof(xfs_bmbt_rec_t);
+
+ /*
+ * If the number of extents is unreasonable, then something
+ * is wrong and we just bail out rather than crash in
+ * kmem_alloc() or memcpy() below.
+ */
+ if (unlikely(size < 0 || size > XFS_DFORK_SIZE_ARCH(dip, ip->i_mount, whichfork, ARCH_CONVERT))) {
+ xfs_fs_cmn_err(CE_WARN, ip->i_mount,
+ "corrupt inode %Lu ((a)extents = %d). Unmount and run xfs_repair.",
+ (unsigned long long) ip->i_ino, nex);
+ XFS_CORRUPTION_ERROR("xfs_iformat_extents(1)", XFS_ERRLEVEL_LOW,
+ ip->i_mount, dip);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+
+ real_size = 0;
+ if (nex == 0)
+ ifp->if_u1.if_extents = NULL;
+ else if (nex <= XFS_INLINE_EXTS)
+ ifp->if_u1.if_extents = ifp->if_u2.if_inline_ext;
+ else {
+ ifp->if_u1.if_extents = kmem_alloc(size, KM_SLEEP);
+ ASSERT(ifp->if_u1.if_extents != NULL);
+ real_size = size;
+ }
+ ifp->if_bytes = size;
+ ifp->if_real_bytes = real_size;
+ if (size) {
+ dp = (xfs_bmbt_rec_t *)
+ XFS_DFORK_PTR_ARCH(dip, whichfork, ARCH_CONVERT);
+ xfs_validate_extents(dp, nex, 1, XFS_EXTFMT_INODE(ip));
+ ep = ifp->if_u1.if_extents;
+ for (i = 0; i < nex; i++, ep++, dp++) {
+ ep->l0 = INT_GET(get_unaligned((__uint64_t*)&dp->l0),
+ ARCH_CONVERT);
+ ep->l1 = INT_GET(get_unaligned((__uint64_t*)&dp->l1),
+ ARCH_CONVERT);
+ }
+ xfs_bmap_trace_exlist("xfs_iformat_extents", ip, nex,
+ whichfork);
+ if (whichfork != XFS_DATA_FORK ||
+ XFS_EXTFMT_INODE(ip) == XFS_EXTFMT_NOSTATE)
+ if (unlikely(xfs_check_nostate_extents(
+ ifp->if_u1.if_extents, nex))) {
+ XFS_ERROR_REPORT("xfs_iformat_extents(2)",
+ XFS_ERRLEVEL_LOW,
+ ip->i_mount);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ }
+ ifp->if_flags |= XFS_IFEXTENTS;
+ return 0;
+}
+
+/*
+ * The file has too many extents to fit into
+ * the inode, so they are in B-tree format.
+ * Allocate a buffer for the root of the B-tree
+ * and copy the root into it. The i_extents
+ * field will remain NULL until all of the
+ * extents are read in (when they are needed).
+ */
+STATIC int
+xfs_iformat_btree(
+ xfs_inode_t *ip,
+ xfs_dinode_t *dip,
+ int whichfork)
+{
+ xfs_bmdr_block_t *dfp;
+ xfs_ifork_t *ifp;
+ /* REFERENCED */
+ int nrecs;
+ int size;
+
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+ dfp = (xfs_bmdr_block_t *)XFS_DFORK_PTR_ARCH(dip, whichfork, ARCH_CONVERT);
+ size = XFS_BMAP_BROOT_SPACE(dfp);
+ nrecs = XFS_BMAP_BROOT_NUMRECS(dfp);
+
+ /*
+ * blow out if -- fork has less extents than can fit in
+ * fork (fork shouldn't be a btree format), root btree
+ * block has more records than can fit into the fork,
+ * or the number of extents is greater than the number of
+ * blocks.
+ */
+ if (unlikely(XFS_IFORK_NEXTENTS(ip, whichfork) <= ifp->if_ext_max
+ || XFS_BMDR_SPACE_CALC(nrecs) >
+ XFS_DFORK_SIZE_ARCH(dip, ip->i_mount, whichfork, ARCH_CONVERT)
+ || XFS_IFORK_NEXTENTS(ip, whichfork) > ip->i_d.di_nblocks)) {
+ xfs_fs_cmn_err(CE_WARN, ip->i_mount,
+ "corrupt inode %Lu (btree). Unmount and run xfs_repair.",
+ (unsigned long long) ip->i_ino);
+ XFS_ERROR_REPORT("xfs_iformat_btree", XFS_ERRLEVEL_LOW,
+ ip->i_mount);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+
+ ifp->if_broot_bytes = size;
+ ifp->if_broot = kmem_alloc(size, KM_SLEEP);
+ ASSERT(ifp->if_broot != NULL);
+ /*
+ * Copy and convert from the on-disk structure
+ * to the in-memory structure.
+ */
+ xfs_bmdr_to_bmbt(dfp, XFS_DFORK_SIZE_ARCH(dip, ip->i_mount, whichfork, ARCH_CONVERT),
+ ifp->if_broot, size);
+ ifp->if_flags &= ~XFS_IFEXTENTS;
+ ifp->if_flags |= XFS_IFBROOT;
+
+ return 0;
+}
+
+/*
+ * xfs_xlate_dinode_core - translate an xfs_inode_core_t between ondisk
+ * and native format
+ *
+ * buf = on-disk representation
+ * dip = native representation
+ * dir = direction - +ve -> disk to native
+ * -ve -> native to disk
+ * arch = on-disk architecture
+ */
+void
+xfs_xlate_dinode_core(
+ xfs_caddr_t buf,
+ xfs_dinode_core_t *dip,
+ int dir,
+ xfs_arch_t arch)
+{
+ xfs_dinode_core_t *buf_core = (xfs_dinode_core_t *)buf;
+ xfs_dinode_core_t *mem_core = (xfs_dinode_core_t *)dip;
+
+ ASSERT(dir);
+ if (arch == ARCH_NOCONVERT) {
+ if (dir > 0) {
+ memcpy((xfs_caddr_t)mem_core, (xfs_caddr_t)buf_core,
+ sizeof(xfs_dinode_core_t));
+ } else {
+ memcpy((xfs_caddr_t)buf_core, (xfs_caddr_t)mem_core,
+ sizeof(xfs_dinode_core_t));
+ }
+ return;
+ }
+
+ INT_XLATE(buf_core->di_magic, mem_core->di_magic, dir, arch);
+ INT_XLATE(buf_core->di_mode, mem_core->di_mode, dir, arch);
+ INT_XLATE(buf_core->di_version, mem_core->di_version, dir, arch);
+ INT_XLATE(buf_core->di_format, mem_core->di_format, dir, arch);
+ INT_XLATE(buf_core->di_onlink, mem_core->di_onlink, dir, arch);
+ INT_XLATE(buf_core->di_uid, mem_core->di_uid, dir, arch);
+ INT_XLATE(buf_core->di_gid, mem_core->di_gid, dir, arch);
+ INT_XLATE(buf_core->di_nlink, mem_core->di_nlink, dir, arch);
+ INT_XLATE(buf_core->di_projid, mem_core->di_projid, dir, arch);
+
+ if (dir > 0) {
+ memcpy(mem_core->di_pad, buf_core->di_pad,
+ sizeof(buf_core->di_pad));
+ } else {
+ memcpy(buf_core->di_pad, mem_core->di_pad,
+ sizeof(buf_core->di_pad));
+ }
+
+ INT_XLATE(buf_core->di_flushiter, mem_core->di_flushiter, dir, arch);
+
+ INT_XLATE(buf_core->di_atime.t_sec, mem_core->di_atime.t_sec,
+ dir, arch);
+ INT_XLATE(buf_core->di_atime.t_nsec, mem_core->di_atime.t_nsec,
+ dir, arch);
+ INT_XLATE(buf_core->di_mtime.t_sec, mem_core->di_mtime.t_sec,
+ dir, arch);
+ INT_XLATE(buf_core->di_mtime.t_nsec, mem_core->di_mtime.t_nsec,
+ dir, arch);
+ INT_XLATE(buf_core->di_ctime.t_sec, mem_core->di_ctime.t_sec,
+ dir, arch);
+ INT_XLATE(buf_core->di_ctime.t_nsec, mem_core->di_ctime.t_nsec,
+ dir, arch);
+ INT_XLATE(buf_core->di_size, mem_core->di_size, dir, arch);
+ INT_XLATE(buf_core->di_nblocks, mem_core->di_nblocks, dir, arch);
+ INT_XLATE(buf_core->di_extsize, mem_core->di_extsize, dir, arch);
+ INT_XLATE(buf_core->di_nextents, mem_core->di_nextents, dir, arch);
+ INT_XLATE(buf_core->di_anextents, mem_core->di_anextents, dir, arch);
+ INT_XLATE(buf_core->di_forkoff, mem_core->di_forkoff, dir, arch);
+ INT_XLATE(buf_core->di_aformat, mem_core->di_aformat, dir, arch);
+ INT_XLATE(buf_core->di_dmevmask, mem_core->di_dmevmask, dir, arch);
+ INT_XLATE(buf_core->di_dmstate, mem_core->di_dmstate, dir, arch);
+ INT_XLATE(buf_core->di_flags, mem_core->di_flags, dir, arch);
+ INT_XLATE(buf_core->di_gen, mem_core->di_gen, dir, arch);
+}
+
+uint
+xfs_dic2xflags(
+ xfs_dinode_core_t *dic,
+ xfs_arch_t arch)
+{
+ __uint16_t di_flags;
+ uint flags;
+
+ di_flags = INT_GET(dic->di_flags, arch);
+ flags = XFS_CFORK_Q_ARCH(dic, arch) ? XFS_XFLAG_HASATTR : 0;
+ if (di_flags & XFS_DIFLAG_ANY) {
+ if (di_flags & XFS_DIFLAG_REALTIME)
+ flags |= XFS_XFLAG_REALTIME;
+ if (di_flags & XFS_DIFLAG_PREALLOC)
+ flags |= XFS_XFLAG_PREALLOC;
+ if (di_flags & XFS_DIFLAG_IMMUTABLE)
+ flags |= XFS_XFLAG_IMMUTABLE;
+ if (di_flags & XFS_DIFLAG_APPEND)
+ flags |= XFS_XFLAG_APPEND;
+ if (di_flags & XFS_DIFLAG_SYNC)
+ flags |= XFS_XFLAG_SYNC;
+ if (di_flags & XFS_DIFLAG_NOATIME)
+ flags |= XFS_XFLAG_NOATIME;
+ if (di_flags & XFS_DIFLAG_NODUMP)
+ flags |= XFS_XFLAG_NODUMP;
+ if (di_flags & XFS_DIFLAG_RTINHERIT)
+ flags |= XFS_XFLAG_RTINHERIT;
+ if (di_flags & XFS_DIFLAG_PROJINHERIT)
+ flags |= XFS_XFLAG_PROJINHERIT;
+ if (di_flags & XFS_DIFLAG_NOSYMLINKS)
+ flags |= XFS_XFLAG_NOSYMLINKS;
+ }
+ return flags;
+}
+
+/*
+ * Given a mount structure and an inode number, return a pointer
+ * to a newly allocated in-core inode coresponding to the given
+ * inode number.
+ *
+ * Initialize the inode's attributes and extent pointers if it
+ * already has them (it will not if the inode has no links).
+ */
+int
+xfs_iread(
+ xfs_mount_t *mp,
+ xfs_trans_t *tp,
+ xfs_ino_t ino,
+ xfs_inode_t **ipp,
+ xfs_daddr_t bno)
+{
+ xfs_buf_t *bp;
+ xfs_dinode_t *dip;
+ xfs_inode_t *ip;
+ int error;
+
+ ASSERT(xfs_inode_zone != NULL);
+
+ ip = kmem_zone_zalloc(xfs_inode_zone, KM_SLEEP);
+ ip->i_ino = ino;
+ ip->i_mount = mp;
+
+ /*
+ * Get pointer's to the on-disk inode and the buffer containing it.
+ * If the inode number refers to a block outside the file system
+ * then xfs_itobp() will return NULL. In this case we should
+ * return NULL as well. Set i_blkno to 0 so that xfs_itobp() will
+ * know that this is a new incore inode.
+ */
+ error = xfs_itobp(mp, tp, ip, &dip, &bp, bno);
+
+ if (error != 0) {
+ kmem_zone_free(xfs_inode_zone, ip);
+ return error;
+ }
+
+ /*
+ * Initialize inode's trace buffers.
+ * Do this before xfs_iformat in case it adds entries.
+ */
+#ifdef XFS_BMAP_TRACE
+ ip->i_xtrace = ktrace_alloc(XFS_BMAP_KTRACE_SIZE, KM_SLEEP);
+#endif
+#ifdef XFS_BMBT_TRACE
+ ip->i_btrace = ktrace_alloc(XFS_BMBT_KTRACE_SIZE, KM_SLEEP);
+#endif
+#ifdef XFS_RW_TRACE
+ ip->i_rwtrace = ktrace_alloc(XFS_RW_KTRACE_SIZE, KM_SLEEP);
+#endif
+#ifdef XFS_ILOCK_TRACE
+ ip->i_lock_trace = ktrace_alloc(XFS_ILOCK_KTRACE_SIZE, KM_SLEEP);
+#endif
+#ifdef XFS_DIR2_TRACE
+ ip->i_dir_trace = ktrace_alloc(XFS_DIR2_KTRACE_SIZE, KM_SLEEP);
+#endif
+
+ /*
+ * If we got something that isn't an inode it means someone
+ * (nfs or dmi) has a stale handle.
+ */
+ if (INT_GET(dip->di_core.di_magic, ARCH_CONVERT) != XFS_DINODE_MAGIC) {
+ kmem_zone_free(xfs_inode_zone, ip);
+ xfs_trans_brelse(tp, bp);
+#ifdef DEBUG
+ xfs_fs_cmn_err(CE_ALERT, mp, "xfs_iread: "
+ "dip->di_core.di_magic (0x%x) != "
+ "XFS_DINODE_MAGIC (0x%x)",
+ INT_GET(dip->di_core.di_magic, ARCH_CONVERT),
+ XFS_DINODE_MAGIC);
+#endif /* DEBUG */
+ return XFS_ERROR(EINVAL);
+ }
+
+ /*
+ * If the on-disk inode is already linked to a directory
+ * entry, copy all of the inode into the in-core inode.
+ * xfs_iformat() handles copying in the inode format
+ * specific information.
+ * Otherwise, just get the truly permanent information.
+ */
+ if (!INT_ISZERO(dip->di_core.di_mode, ARCH_CONVERT)) {
+ xfs_xlate_dinode_core((xfs_caddr_t)&dip->di_core,
+ &(ip->i_d), 1, ARCH_CONVERT);
+ error = xfs_iformat(ip, dip);
+ if (error) {
+ kmem_zone_free(xfs_inode_zone, ip);
+ xfs_trans_brelse(tp, bp);
+#ifdef DEBUG
+ xfs_fs_cmn_err(CE_ALERT, mp, "xfs_iread: "
+ "xfs_iformat() returned error %d",
+ error);
+#endif /* DEBUG */
+ return error;
+ }
+ } else {
+ ip->i_d.di_magic = INT_GET(dip->di_core.di_magic, ARCH_CONVERT);
+ ip->i_d.di_version = INT_GET(dip->di_core.di_version, ARCH_CONVERT);
+ ip->i_d.di_gen = INT_GET(dip->di_core.di_gen, ARCH_CONVERT);
+ ip->i_d.di_flushiter = INT_GET(dip->di_core.di_flushiter, ARCH_CONVERT);
+ /*
+ * Make sure to pull in the mode here as well in
+ * case the inode is released without being used.
+ * This ensures that xfs_inactive() will see that
+ * the inode is already free and not try to mess
+ * with the uninitialized part of it.
+ */
+ ip->i_d.di_mode = 0;
+ /*
+ * Initialize the per-fork minima and maxima for a new
+ * inode here. xfs_iformat will do it for old inodes.
+ */
+ ip->i_df.if_ext_max =
+ XFS_IFORK_DSIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t);
+ }
+
+ INIT_LIST_HEAD(&ip->i_reclaim);
+
+ /*
+ * The inode format changed when we moved the link count and
+ * made it 32 bits long. If this is an old format inode,
+ * convert it in memory to look like a new one. If it gets
+ * flushed to disk we will convert back before flushing or
+ * logging it. We zero out the new projid field and the old link
+ * count field. We'll handle clearing the pad field (the remains
+ * of the old uuid field) when we actually convert the inode to
+ * the new format. We don't change the version number so that we
+ * can distinguish this from a real new format inode.
+ */
+ if (ip->i_d.di_version == XFS_DINODE_VERSION_1) {
+ ip->i_d.di_nlink = ip->i_d.di_onlink;
+ ip->i_d.di_onlink = 0;
+ ip->i_d.di_projid = 0;
+ }
+
+ ip->i_delayed_blks = 0;
+
+ /*
+ * Mark the buffer containing the inode as something to keep
+ * around for a while. This helps to keep recently accessed
+ * meta-data in-core longer.
+ */
+ XFS_BUF_SET_REF(bp, XFS_INO_REF);
+
+ /*
+ * Use xfs_trans_brelse() to release the buffer containing the
+ * on-disk inode, because it was acquired with xfs_trans_read_buf()
+ * in xfs_itobp() above. If tp is NULL, this is just a normal
+ * brelse(). If we're within a transaction, then xfs_trans_brelse()
+ * will only release the buffer if it is not dirty within the
+ * transaction. It will be OK to release the buffer in this case,
+ * because inodes on disk are never destroyed and we will be
+ * locking the new in-core inode before putting it in the hash
+ * table where other processes can find it. Thus we don't have
+ * to worry about the inode being changed just because we released
+ * the buffer.
+ */
+ xfs_trans_brelse(tp, bp);
+ *ipp = ip;
+ return 0;
+}
+
+/*
+ * Read in extents from a btree-format inode.
+ * Allocate and fill in if_extents. Real work is done in xfs_bmap.c.
+ */
+int
+xfs_iread_extents(
+ xfs_trans_t *tp,
+ xfs_inode_t *ip,
+ int whichfork)
+{
+ int error;
+ xfs_ifork_t *ifp;
+ size_t size;
+
+ if (unlikely(XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE)) {
+ XFS_ERROR_REPORT("xfs_iread_extents", XFS_ERRLEVEL_LOW,
+ ip->i_mount);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ size = XFS_IFORK_NEXTENTS(ip, whichfork) * (uint)sizeof(xfs_bmbt_rec_t);
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+ /*
+ * We know that the size is valid (it's checked in iformat_btree)
+ */
+ ifp->if_u1.if_extents = kmem_alloc(size, KM_SLEEP);
+ ASSERT(ifp->if_u1.if_extents != NULL);
+ ifp->if_lastex = NULLEXTNUM;
+ ifp->if_bytes = ifp->if_real_bytes = (int)size;
+ ifp->if_flags |= XFS_IFEXTENTS;
+ error = xfs_bmap_read_extents(tp, ip, whichfork);
+ if (error) {
+ kmem_free(ifp->if_u1.if_extents, size);
+ ifp->if_u1.if_extents = NULL;
+ ifp->if_bytes = ifp->if_real_bytes = 0;
+ ifp->if_flags &= ~XFS_IFEXTENTS;
+ return error;
+ }
+ xfs_validate_extents((xfs_bmbt_rec_t *)ifp->if_u1.if_extents,
+ XFS_IFORK_NEXTENTS(ip, whichfork), 0, XFS_EXTFMT_INODE(ip));
+ return 0;
+}
+
+/*
+ * Allocate an inode on disk and return a copy of its in-core version.
+ * The in-core inode is locked exclusively. Set mode, nlink, and rdev
+ * appropriately within the inode. The uid and gid for the inode are
+ * set according to the contents of the given cred structure.
+ *
+ * Use xfs_dialloc() to allocate the on-disk inode. If xfs_dialloc()
+ * has a free inode available, call xfs_iget()
+ * to obtain the in-core version of the allocated inode. Finally,
+ * fill in the inode and log its initial contents. In this case,
+ * ialloc_context would be set to NULL and call_again set to false.
+ *
+ * If xfs_dialloc() does not have an available inode,
+ * it will replenish its supply by doing an allocation. Since we can
+ * only do one allocation within a transaction without deadlocks, we
+ * must commit the current transaction before returning the inode itself.
+ * In this case, therefore, we will set call_again to true and return.
+ * The caller should then commit the current transaction, start a new
+ * transaction, and call xfs_ialloc() again to actually get the inode.
+ *
+ * To ensure that some other process does not grab the inode that
+ * was allocated during the first call to xfs_ialloc(), this routine
+ * also returns the [locked] bp pointing to the head of the freelist
+ * as ialloc_context. The caller should hold this buffer across
+ * the commit and pass it back into this routine on the second call.
+ */
+int
+xfs_ialloc(
+ xfs_trans_t *tp,
+ xfs_inode_t *pip,
+ mode_t mode,
+ nlink_t nlink,
+ xfs_dev_t rdev,
+ cred_t *cr,
+ xfs_prid_t prid,
+ int okalloc,
+ xfs_buf_t **ialloc_context,
+ boolean_t *call_again,
+ xfs_inode_t **ipp)
+{
+ xfs_ino_t ino;
+ xfs_inode_t *ip;
+ vnode_t *vp;
+ uint flags;
+ int error;
+
+ /*
+ * Call the space management code to pick
+ * the on-disk inode to be allocated.
+ */
+ error = xfs_dialloc(tp, pip->i_ino, mode, okalloc,
+ ialloc_context, call_again, &ino);
+ if (error != 0) {
+ return error;
+ }
+ if (*call_again || ino == NULLFSINO) {
+ *ipp = NULL;
+ return 0;
+ }
+ ASSERT(*ialloc_context == NULL);
+
+ /*
+ * Get the in-core inode with the lock held exclusively.
+ * This is because we're setting fields here we need
+ * to prevent others from looking at until we're done.
+ */
+ error = xfs_trans_iget(tp->t_mountp, tp, ino,
+ IGET_CREATE, XFS_ILOCK_EXCL, &ip);
+ if (error != 0) {
+ return error;
+ }
+ ASSERT(ip != NULL);
+
+ vp = XFS_ITOV(ip);
+ vp->v_type = IFTOVT(mode);
+ ip->i_d.di_mode = (__uint16_t)mode;
+ ip->i_d.di_onlink = 0;
+ ip->i_d.di_nlink = nlink;
+ ASSERT(ip->i_d.di_nlink == nlink);
+ ip->i_d.di_uid = current_fsuid(cr);
+ ip->i_d.di_gid = current_fsgid(cr);
+ ip->i_d.di_projid = prid;
+ memset(&(ip->i_d.di_pad[0]), 0, sizeof(ip->i_d.di_pad));
+
+ /*
+ * If the superblock version is up to where we support new format
+ * inodes and this is currently an old format inode, then change
+ * the inode version number now. This way we only do the conversion
+ * here rather than here and in the flush/logging code.
+ */
+ if (XFS_SB_VERSION_HASNLINK(&tp->t_mountp->m_sb) &&
+ ip->i_d.di_version == XFS_DINODE_VERSION_1) {
+ ip->i_d.di_version = XFS_DINODE_VERSION_2;
+ /*
+ * We've already zeroed the old link count, the projid field,
+ * and the pad field.
+ */
+ }
+
+ /*
+ * Project ids won't be stored on disk if we are using a version 1 inode.
+ */
+ if ( (prid != 0) && (ip->i_d.di_version == XFS_DINODE_VERSION_1))
+ xfs_bump_ino_vers2(tp, ip);
+
+ if (XFS_INHERIT_GID(pip, vp->v_vfsp)) {
+ ip->i_d.di_gid = pip->i_d.di_gid;
+ if ((pip->i_d.di_mode & S_ISGID) && (mode & S_IFMT) == S_IFDIR) {
+ ip->i_d.di_mode |= S_ISGID;
+ }
+ }
+
+ /*
+ * If the group ID of the new file does not match the effective group
+ * ID or one of the supplementary group IDs, the S_ISGID bit is cleared
+ * (and only if the irix_sgid_inherit compatibility variable is set).
+ */
+ if ((irix_sgid_inherit) &&
+ (ip->i_d.di_mode & S_ISGID) &&
+ (!in_group_p((gid_t)ip->i_d.di_gid))) {
+ ip->i_d.di_mode &= ~S_ISGID;
+ }
+
+ ip->i_d.di_size = 0;
+ ip->i_d.di_nextents = 0;
+ ASSERT(ip->i_d.di_nblocks == 0);
+ xfs_ichgtime(ip, XFS_ICHGTIME_CHG|XFS_ICHGTIME_ACC|XFS_ICHGTIME_MOD);
+ /*
+ * di_gen will have been taken care of in xfs_iread.
+ */
+ ip->i_d.di_extsize = 0;
+ ip->i_d.di_dmevmask = 0;
+ ip->i_d.di_dmstate = 0;
+ ip->i_d.di_flags = 0;
+ flags = XFS_ILOG_CORE;
+ switch (mode & S_IFMT) {
+ case S_IFIFO:
+ case S_IFCHR:
+ case S_IFBLK:
+ case S_IFSOCK:
+ ip->i_d.di_format = XFS_DINODE_FMT_DEV;
+ ip->i_df.if_u2.if_rdev = rdev;
+ ip->i_df.if_flags = 0;
+ flags |= XFS_ILOG_DEV;
+ break;
+ case S_IFREG:
+ case S_IFDIR:
+ if (unlikely(pip->i_d.di_flags & XFS_DIFLAG_ANY)) {
+ if (pip->i_d.di_flags & XFS_DIFLAG_RTINHERIT) {
+ if ((mode & S_IFMT) == S_IFDIR) {
+ ip->i_d.di_flags |= XFS_DIFLAG_RTINHERIT;
+ } else {
+ ip->i_d.di_flags |= XFS_DIFLAG_REALTIME;
+ ip->i_iocore.io_flags |= XFS_IOCORE_RT;
+ }
+ }
+ if ((pip->i_d.di_flags & XFS_DIFLAG_NOATIME) &&
+ xfs_inherit_noatime)
+ ip->i_d.di_flags |= XFS_DIFLAG_NOATIME;
+ if ((pip->i_d.di_flags & XFS_DIFLAG_NODUMP) &&
+ xfs_inherit_nodump)
+ ip->i_d.di_flags |= XFS_DIFLAG_NODUMP;
+ if ((pip->i_d.di_flags & XFS_DIFLAG_SYNC) &&
+ xfs_inherit_sync)
+ ip->i_d.di_flags |= XFS_DIFLAG_SYNC;
+ if ((pip->i_d.di_flags & XFS_DIFLAG_NOSYMLINKS) &&
+ xfs_inherit_nosymlinks)
+ ip->i_d.di_flags |= XFS_DIFLAG_NOSYMLINKS;
+ }
+ /* FALLTHROUGH */
+ case S_IFLNK:
+ ip->i_d.di_format = XFS_DINODE_FMT_EXTENTS;
+ ip->i_df.if_flags = XFS_IFEXTENTS;
+ ip->i_df.if_bytes = ip->i_df.if_real_bytes = 0;
+ ip->i_df.if_u1.if_extents = NULL;
+ break;
+ default:
+ ASSERT(0);
+ }
+ /*
+ * Attribute fork settings for new inode.
+ */
+ ip->i_d.di_aformat = XFS_DINODE_FMT_EXTENTS;
+ ip->i_d.di_anextents = 0;
+
+ /*
+ * Log the new values stuffed into the inode.
+ */
+ xfs_trans_log_inode(tp, ip, flags);
+
+ /* now that we have a v_type we can set Linux inode ops (& unlock) */
+ VFS_INIT_VNODE(XFS_MTOVFS(tp->t_mountp), vp, XFS_ITOBHV(ip), 1);
+
+ *ipp = ip;
+ return 0;
+}
+
+/*
+ * Check to make sure that there are no blocks allocated to the
+ * file beyond the size of the file. We don't check this for
+ * files with fixed size extents or real time extents, but we
+ * at least do it for regular files.
+ */
+#ifdef DEBUG
+void
+xfs_isize_check(
+ xfs_mount_t *mp,
+ xfs_inode_t *ip,
+ xfs_fsize_t isize)
+{
+ xfs_fileoff_t map_first;
+ int nimaps;
+ xfs_bmbt_irec_t imaps[2];
+
+ if ((ip->i_d.di_mode & S_IFMT) != S_IFREG)
+ return;
+
+ if ( ip->i_d.di_flags & XFS_DIFLAG_REALTIME )
+ return;
+
+ nimaps = 2;
+ map_first = XFS_B_TO_FSB(mp, (xfs_ufsize_t)isize);
+ /*
+ * The filesystem could be shutting down, so bmapi may return
+ * an error.
+ */
+ if (xfs_bmapi(NULL, ip, map_first,
+ (XFS_B_TO_FSB(mp,
+ (xfs_ufsize_t)XFS_MAXIOFFSET(mp)) -
+ map_first),
+ XFS_BMAPI_ENTIRE, NULL, 0, imaps, &nimaps,
+ NULL))
+ return;
+ ASSERT(nimaps == 1);
+ ASSERT(imaps[0].br_startblock == HOLESTARTBLOCK);
+}
+#endif /* DEBUG */
+
+/*
+ * Calculate the last possible buffered byte in a file. This must
+ * include data that was buffered beyond the EOF by the write code.
+ * This also needs to deal with overflowing the xfs_fsize_t type
+ * which can happen for sizes near the limit.
+ *
+ * We also need to take into account any blocks beyond the EOF. It
+ * may be the case that they were buffered by a write which failed.
+ * In that case the pages will still be in memory, but the inode size
+ * will never have been updated.
+ */
+xfs_fsize_t
+xfs_file_last_byte(
+ xfs_inode_t *ip)
+{
+ xfs_mount_t *mp;
+ xfs_fsize_t last_byte;
+ xfs_fileoff_t last_block;
+ xfs_fileoff_t size_last_block;
+ int error;
+
+ ASSERT(ismrlocked(&(ip->i_iolock), MR_UPDATE | MR_ACCESS));
+
+ mp = ip->i_mount;
+ /*
+ * Only check for blocks beyond the EOF if the extents have
+ * been read in. This eliminates the need for the inode lock,
+ * and it also saves us from looking when it really isn't
+ * necessary.
+ */
+ if (ip->i_df.if_flags & XFS_IFEXTENTS) {
+ error = xfs_bmap_last_offset(NULL, ip, &last_block,
+ XFS_DATA_FORK);
+ if (error) {
+ last_block = 0;
+ }
+ } else {
+ last_block = 0;
+ }
+ size_last_block = XFS_B_TO_FSB(mp, (xfs_ufsize_t)ip->i_d.di_size);
+ last_block = XFS_FILEOFF_MAX(last_block, size_last_block);
+
+ last_byte = XFS_FSB_TO_B(mp, last_block);
+ if (last_byte < 0) {
+ return XFS_MAXIOFFSET(mp);
+ }
+ last_byte += (1 << mp->m_writeio_log);
+ if (last_byte < 0) {
+ return XFS_MAXIOFFSET(mp);
+ }
+ return last_byte;
+}
+
+#if defined(XFS_RW_TRACE)
+STATIC void
+xfs_itrunc_trace(
+ int tag,
+ xfs_inode_t *ip,
+ int flag,
+ xfs_fsize_t new_size,
+ xfs_off_t toss_start,
+ xfs_off_t toss_finish)
+{
+ if (ip->i_rwtrace == NULL) {
+ return;
+ }
+
+ ktrace_enter(ip->i_rwtrace,
+ (void*)((long)tag),
+ (void*)ip,
+ (void*)(unsigned long)((ip->i_d.di_size >> 32) & 0xffffffff),
+ (void*)(unsigned long)(ip->i_d.di_size & 0xffffffff),
+ (void*)((long)flag),
+ (void*)(unsigned long)((new_size >> 32) & 0xffffffff),
+ (void*)(unsigned long)(new_size & 0xffffffff),
+ (void*)(unsigned long)((toss_start >> 32) & 0xffffffff),
+ (void*)(unsigned long)(toss_start & 0xffffffff),
+ (void*)(unsigned long)((toss_finish >> 32) & 0xffffffff),
+ (void*)(unsigned long)(toss_finish & 0xffffffff),
+ (void*)(unsigned long)current_cpu(),
+ (void*)0,
+ (void*)0,
+ (void*)0,
+ (void*)0);
+}
+#else
+#define xfs_itrunc_trace(tag, ip, flag, new_size, toss_start, toss_finish)
+#endif
+
+/*
+ * Start the truncation of the file to new_size. The new size
+ * must be smaller than the current size. This routine will
+ * clear the buffer and page caches of file data in the removed
+ * range, and xfs_itruncate_finish() will remove the underlying
+ * disk blocks.
+ *
+ * The inode must have its I/O lock locked EXCLUSIVELY, and it
+ * must NOT have the inode lock held at all. This is because we're
+ * calling into the buffer/page cache code and we can't hold the
+ * inode lock when we do so.
+ *
+ * The flags parameter can have either the value XFS_ITRUNC_DEFINITE
+ * or XFS_ITRUNC_MAYBE. The XFS_ITRUNC_MAYBE value should be used
+ * in the case that the caller is locking things out of order and
+ * may not be able to call xfs_itruncate_finish() with the inode lock
+ * held without dropping the I/O lock. If the caller must drop the
+ * I/O lock before calling xfs_itruncate_finish(), then xfs_itruncate_start()
+ * must be called again with all the same restrictions as the initial
+ * call.
+ */
+void
+xfs_itruncate_start(
+ xfs_inode_t *ip,
+ uint flags,
+ xfs_fsize_t new_size)
+{
+ xfs_fsize_t last_byte;
+ xfs_off_t toss_start;
+ xfs_mount_t *mp;
+ vnode_t *vp;
+
+ ASSERT(ismrlocked(&ip->i_iolock, MR_UPDATE) != 0);
+ ASSERT((new_size == 0) || (new_size <= ip->i_d.di_size));
+ ASSERT((flags == XFS_ITRUNC_DEFINITE) ||
+ (flags == XFS_ITRUNC_MAYBE));
+
+ mp = ip->i_mount;
+ vp = XFS_ITOV(ip);
+ /*
+ * Call VOP_TOSS_PAGES() or VOP_FLUSHINVAL_PAGES() to get rid of pages and buffers
+ * overlapping the region being removed. We have to use
+ * the less efficient VOP_FLUSHINVAL_PAGES() in the case that the
+ * caller may not be able to finish the truncate without
+ * dropping the inode's I/O lock. Make sure
+ * to catch any pages brought in by buffers overlapping
+ * the EOF by searching out beyond the isize by our
+ * block size. We round new_size up to a block boundary
+ * so that we don't toss things on the same block as
+ * new_size but before it.
+ *
+ * Before calling VOP_TOSS_PAGES() or VOP_FLUSHINVAL_PAGES(), make sure to
+ * call remapf() over the same region if the file is mapped.
+ * This frees up mapped file references to the pages in the
+ * given range and for the VOP_FLUSHINVAL_PAGES() case it ensures
+ * that we get the latest mapped changes flushed out.
+ */
+ toss_start = XFS_B_TO_FSB(mp, (xfs_ufsize_t)new_size);
+ toss_start = XFS_FSB_TO_B(mp, toss_start);
+ if (toss_start < 0) {
+ /*
+ * The place to start tossing is beyond our maximum
+ * file size, so there is no way that the data extended
+ * out there.
+ */
+ return;
+ }
+ last_byte = xfs_file_last_byte(ip);
+ xfs_itrunc_trace(XFS_ITRUNC_START, ip, flags, new_size, toss_start,
+ last_byte);
+ if (last_byte > toss_start) {
+ if (flags & XFS_ITRUNC_DEFINITE) {
+ VOP_TOSS_PAGES(vp, toss_start, -1, FI_REMAPF_LOCKED);
+ } else {
+ VOP_FLUSHINVAL_PAGES(vp, toss_start, -1, FI_REMAPF_LOCKED);
+ }
+ }
+
+#ifdef DEBUG
+ if (new_size == 0) {
+ ASSERT(VN_CACHED(vp) == 0);
+ }
+#endif
+}
+
+/*
+ * Shrink the file to the given new_size. The new
+ * size must be smaller than the current size.
+ * This will free up the underlying blocks
+ * in the removed range after a call to xfs_itruncate_start()
+ * or xfs_atruncate_start().
+ *
+ * The transaction passed to this routine must have made
+ * a permanent log reservation of at least XFS_ITRUNCATE_LOG_RES.
+ * This routine may commit the given transaction and
+ * start new ones, so make sure everything involved in
+ * the transaction is tidy before calling here.
+ * Some transaction will be returned to the caller to be
+ * committed. The incoming transaction must already include
+ * the inode, and both inode locks must be held exclusively.
+ * The inode must also be "held" within the transaction. On
+ * return the inode will be "held" within the returned transaction.
+ * This routine does NOT require any disk space to be reserved
+ * for it within the transaction.
+ *
+ * The fork parameter must be either xfs_attr_fork or xfs_data_fork,
+ * and it indicates the fork which is to be truncated. For the
+ * attribute fork we only support truncation to size 0.
+ *
+ * We use the sync parameter to indicate whether or not the first
+ * transaction we perform might have to be synchronous. For the attr fork,
+ * it needs to be so if the unlink of the inode is not yet known to be
+ * permanent in the log. This keeps us from freeing and reusing the
+ * blocks of the attribute fork before the unlink of the inode becomes
+ * permanent.
+ *
+ * For the data fork, we normally have to run synchronously if we're
+ * being called out of the inactive path or we're being called
+ * out of the create path where we're truncating an existing file.
+ * Either way, the truncate needs to be sync so blocks don't reappear
+ * in the file with altered data in case of a crash. wsync filesystems
+ * can run the first case async because anything that shrinks the inode
+ * has to run sync so by the time we're called here from inactive, the
+ * inode size is permanently set to 0.
+ *
+ * Calls from the truncate path always need to be sync unless we're
+ * in a wsync filesystem and the file has already been unlinked.
+ *
+ * The caller is responsible for correctly setting the sync parameter.
+ * It gets too hard for us to guess here which path we're being called
+ * out of just based on inode state.
+ */
+int
+xfs_itruncate_finish(
+ xfs_trans_t **tp,
+ xfs_inode_t *ip,
+ xfs_fsize_t new_size,
+ int fork,
+ int sync)
+{
+ xfs_fsblock_t first_block;
+ xfs_fileoff_t first_unmap_block;
+ xfs_fileoff_t last_block;
+ xfs_filblks_t unmap_len=0;
+ xfs_mount_t *mp;
+ xfs_trans_t *ntp;
+ int done;
+ int committed;
+ xfs_bmap_free_t free_list;
+ int error;
+
+ ASSERT(ismrlocked(&ip->i_iolock, MR_UPDATE) != 0);
+ ASSERT(ismrlocked(&ip->i_lock, MR_UPDATE) != 0);
+ ASSERT((new_size == 0) || (new_size <= ip->i_d.di_size));
+ ASSERT(*tp != NULL);
+ ASSERT((*tp)->t_flags & XFS_TRANS_PERM_LOG_RES);
+ ASSERT(ip->i_transp == *tp);
+ ASSERT(ip->i_itemp != NULL);
+ ASSERT(ip->i_itemp->ili_flags & XFS_ILI_HOLD);
+
+
+ ntp = *tp;
+ mp = (ntp)->t_mountp;
+ ASSERT(! XFS_NOT_DQATTACHED(mp, ip));
+
+ /*
+ * We only support truncating the entire attribute fork.
+ */
+ if (fork == XFS_ATTR_FORK) {
+ new_size = 0LL;
+ }
+ first_unmap_block = XFS_B_TO_FSB(mp, (xfs_ufsize_t)new_size);
+ xfs_itrunc_trace(XFS_ITRUNC_FINISH1, ip, 0, new_size, 0, 0);
+ /*
+ * The first thing we do is set the size to new_size permanently
+ * on disk. This way we don't have to worry about anyone ever
+ * being able to look at the data being freed even in the face
+ * of a crash. What we're getting around here is the case where
+ * we free a block, it is allocated to another file, it is written
+ * to, and then we crash. If the new data gets written to the
+ * file but the log buffers containing the free and reallocation
+ * don't, then we'd end up with garbage in the blocks being freed.
+ * As long as we make the new_size permanent before actually
+ * freeing any blocks it doesn't matter if they get writtten to.
+ *
+ * The callers must signal into us whether or not the size
+ * setting here must be synchronous. There are a few cases
+ * where it doesn't have to be synchronous. Those cases
+ * occur if the file is unlinked and we know the unlink is
+ * permanent or if the blocks being truncated are guaranteed
+ * to be beyond the inode eof (regardless of the link count)
+ * and the eof value is permanent. Both of these cases occur
+ * only on wsync-mounted filesystems. In those cases, we're
+ * guaranteed that no user will ever see the data in the blocks
+ * that are being truncated so the truncate can run async.
+ * In the free beyond eof case, the file may wind up with
+ * more blocks allocated to it than it needs if we crash
+ * and that won't get fixed until the next time the file
+ * is re-opened and closed but that's ok as that shouldn't
+ * be too many blocks.
+ *
+ * However, we can't just make all wsync xactions run async
+ * because there's one call out of the create path that needs
+ * to run sync where it's truncating an existing file to size
+ * 0 whose size is > 0.
+ *
+ * It's probably possible to come up with a test in this
+ * routine that would correctly distinguish all the above
+ * cases from the values of the function parameters and the
+ * inode state but for sanity's sake, I've decided to let the
+ * layers above just tell us. It's simpler to correctly figure
+ * out in the layer above exactly under what conditions we
+ * can run async and I think it's easier for others read and
+ * follow the logic in case something has to be changed.
+ * cscope is your friend -- rcc.
+ *
+ * The attribute fork is much simpler.
+ *
+ * For the attribute fork we allow the caller to tell us whether
+ * the unlink of the inode that led to this call is yet permanent
+ * in the on disk log. If it is not and we will be freeing extents
+ * in this inode then we make the first transaction synchronous
+ * to make sure that the unlink is permanent by the time we free
+ * the blocks.
+ */
+ if (fork == XFS_DATA_FORK) {
+ if (ip->i_d.di_nextents > 0) {
+ ip->i_d.di_size = new_size;
+ xfs_trans_log_inode(ntp, ip, XFS_ILOG_CORE);
+ }
+ } else if (sync) {
+ ASSERT(!(mp->m_flags & XFS_MOUNT_WSYNC));
+ if (ip->i_d.di_anextents > 0)
+ xfs_trans_set_sync(ntp);
+ }
+ ASSERT(fork == XFS_DATA_FORK ||
+ (fork == XFS_ATTR_FORK &&
+ ((sync && !(mp->m_flags & XFS_MOUNT_WSYNC)) ||
+ (sync == 0 && (mp->m_flags & XFS_MOUNT_WSYNC)))));
+
+ /*
+ * Since it is possible for space to become allocated beyond
+ * the end of the file (in a crash where the space is allocated
+ * but the inode size is not yet updated), simply remove any
+ * blocks which show up between the new EOF and the maximum
+ * possible file size. If the first block to be removed is
+ * beyond the maximum file size (ie it is the same as last_block),
+ * then there is nothing to do.
+ */
+ last_block = XFS_B_TO_FSB(mp, (xfs_ufsize_t)XFS_MAXIOFFSET(mp));
+ ASSERT(first_unmap_block <= last_block);
+ done = 0;
+ if (last_block == first_unmap_block) {
+ done = 1;
+ } else {
+ unmap_len = last_block - first_unmap_block + 1;
+ }
+ while (!done) {
+ /*
+ * Free up up to XFS_ITRUNC_MAX_EXTENTS. xfs_bunmapi()
+ * will tell us whether it freed the entire range or
+ * not. If this is a synchronous mount (wsync),
+ * then we can tell bunmapi to keep all the
+ * transactions asynchronous since the unlink
+ * transaction that made this inode inactive has
+ * already hit the disk. There's no danger of
+ * the freed blocks being reused, there being a
+ * crash, and the reused blocks suddenly reappearing
+ * in this file with garbage in them once recovery
+ * runs.
+ */
+ XFS_BMAP_INIT(&free_list, &first_block);
+ error = xfs_bunmapi(ntp, ip, first_unmap_block,
+ unmap_len,
+ XFS_BMAPI_AFLAG(fork) |
+ (sync ? 0 : XFS_BMAPI_ASYNC),
+ XFS_ITRUNC_MAX_EXTENTS,
+ &first_block, &free_list, &done);
+ if (error) {
+ /*
+ * If the bunmapi call encounters an error,
+ * return to the caller where the transaction
+ * can be properly aborted. We just need to
+ * make sure we're not holding any resources
+ * that we were not when we came in.
+ */
+ xfs_bmap_cancel(&free_list);
+ return error;
+ }
+
+ /*
+ * Duplicate the transaction that has the permanent
+ * reservation and commit the old transaction.
+ */
+ error = xfs_bmap_finish(tp, &free_list, first_block,
+ &committed);
+ ntp = *tp;
+ if (error) {
+ /*
+ * If the bmap finish call encounters an error,
+ * return to the caller where the transaction
+ * can be properly aborted. We just need to
+ * make sure we're not holding any resources
+ * that we were not when we came in.
+ *
+ * Aborting from this point might lose some
+ * blocks in the file system, but oh well.
+ */
+ xfs_bmap_cancel(&free_list);
+ if (committed) {
+ /*
+ * If the passed in transaction committed
+ * in xfs_bmap_finish(), then we want to
+ * add the inode to this one before returning.
+ * This keeps things simple for the higher
+ * level code, because it always knows that
+ * the inode is locked and held in the
+ * transaction that returns to it whether
+ * errors occur or not. We don't mark the
+ * inode dirty so that this transaction can
+ * be easily aborted if possible.
+ */
+ xfs_trans_ijoin(ntp, ip,
+ XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL);
+ xfs_trans_ihold(ntp, ip);
+ }
+ return error;
+ }
+
+ if (committed) {
+ /*
+ * The first xact was committed,
+ * so add the inode to the new one.
+ * Mark it dirty so it will be logged
+ * and moved forward in the log as
+ * part of every commit.
+ */
+ xfs_trans_ijoin(ntp, ip,
+ XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL);
+ xfs_trans_ihold(ntp, ip);
+ xfs_trans_log_inode(ntp, ip, XFS_ILOG_CORE);
+ }
+ ntp = xfs_trans_dup(ntp);
+ (void) xfs_trans_commit(*tp, 0, NULL);
+ *tp = ntp;
+ error = xfs_trans_reserve(ntp, 0, XFS_ITRUNCATE_LOG_RES(mp), 0,
+ XFS_TRANS_PERM_LOG_RES,
+ XFS_ITRUNCATE_LOG_COUNT);
+ /*
+ * Add the inode being truncated to the next chained
+ * transaction.
+ */
+ xfs_trans_ijoin(ntp, ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL);
+ xfs_trans_ihold(ntp, ip);
+ if (error)
+ return (error);
+ }
+ /*
+ * Only update the size in the case of the data fork, but
+ * always re-log the inode so that our permanent transaction
+ * can keep on rolling it forward in the log.
+ */
+ if (fork == XFS_DATA_FORK) {
+ xfs_isize_check(mp, ip, new_size);
+ ip->i_d.di_size = new_size;
+ }
+ xfs_trans_log_inode(ntp, ip, XFS_ILOG_CORE);
+ ASSERT((new_size != 0) ||
+ (fork == XFS_ATTR_FORK) ||
+ (ip->i_delayed_blks == 0));
+ ASSERT((new_size != 0) ||
+ (fork == XFS_ATTR_FORK) ||
+ (ip->i_d.di_nextents == 0));
+ xfs_itrunc_trace(XFS_ITRUNC_FINISH2, ip, 0, new_size, 0, 0);
+ return 0;
+}
+
+
+/*
+ * xfs_igrow_start
+ *
+ * Do the first part of growing a file: zero any data in the last
+ * block that is beyond the old EOF. We need to do this before
+ * the inode is joined to the transaction to modify the i_size.
+ * That way we can drop the inode lock and call into the buffer
+ * cache to get the buffer mapping the EOF.
+ */
+int
+xfs_igrow_start(
+ xfs_inode_t *ip,
+ xfs_fsize_t new_size,
+ cred_t *credp)
+{
+ xfs_fsize_t isize;
+ int error;
+
+ ASSERT(ismrlocked(&(ip->i_lock), MR_UPDATE) != 0);
+ ASSERT(ismrlocked(&(ip->i_iolock), MR_UPDATE) != 0);
+ ASSERT(new_size > ip->i_d.di_size);
+
+ error = 0;
+ isize = ip->i_d.di_size;
+ /*
+ * Zero any pages that may have been created by
+ * xfs_write_file() beyond the end of the file
+ * and any blocks between the old and new file sizes.
+ */
+ error = xfs_zero_eof(XFS_ITOV(ip), &ip->i_iocore, new_size, isize,
+ new_size);
+ return error;
+}
+
+/*
+ * xfs_igrow_finish
+ *
+ * This routine is called to extend the size of a file.
+ * The inode must have both the iolock and the ilock locked
+ * for update and it must be a part of the current transaction.
+ * The xfs_igrow_start() function must have been called previously.
+ * If the change_flag is not zero, the inode change timestamp will
+ * be updated.
+ */
+void
+xfs_igrow_finish(
+ xfs_trans_t *tp,
+ xfs_inode_t *ip,
+ xfs_fsize_t new_size,
+ int change_flag)
+{
+ ASSERT(ismrlocked(&(ip->i_lock), MR_UPDATE) != 0);
+ ASSERT(ismrlocked(&(ip->i_iolock), MR_UPDATE) != 0);
+ ASSERT(ip->i_transp == tp);
+ ASSERT(new_size > ip->i_d.di_size);
+
+ /*
+ * Update the file size. Update the inode change timestamp
+ * if change_flag set.
+ */
+ ip->i_d.di_size = new_size;
+ if (change_flag)
+ xfs_ichgtime(ip, XFS_ICHGTIME_CHG);
+ xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+
+}
+
+
+/*
+ * This is called when the inode's link count goes to 0.
+ * We place the on-disk inode on a list in the AGI. It
+ * will be pulled from this list when the inode is freed.
+ */
+int
+xfs_iunlink(
+ xfs_trans_t *tp,
+ xfs_inode_t *ip)
+{
+ xfs_mount_t *mp;
+ xfs_agi_t *agi;
+ xfs_dinode_t *dip;
+ xfs_buf_t *agibp;
+ xfs_buf_t *ibp;
+ xfs_agnumber_t agno;
+ xfs_daddr_t agdaddr;
+ xfs_agino_t agino;
+ short bucket_index;
+ int offset;
+ int error;
+ int agi_ok;
+
+ ASSERT(ip->i_d.di_nlink == 0);
+ ASSERT(ip->i_d.di_mode != 0);
+ ASSERT(ip->i_transp == tp);
+
+ mp = tp->t_mountp;
+
+ agno = XFS_INO_TO_AGNO(mp, ip->i_ino);
+ agdaddr = XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp));
+
+ /*
+ * Get the agi buffer first. It ensures lock ordering
+ * on the list.
+ */
+ error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp, agdaddr,
+ XFS_FSS_TO_BB(mp, 1), 0, &agibp);
+ if (error) {
+ return error;
+ }
+ /*
+ * Validate the magic number of the agi block.
+ */
+ agi = XFS_BUF_TO_AGI(agibp);
+ agi_ok =
+ INT_GET(agi->agi_magicnum, ARCH_CONVERT) == XFS_AGI_MAGIC &&
+ XFS_AGI_GOOD_VERSION(INT_GET(agi->agi_versionnum, ARCH_CONVERT));
+ if (unlikely(XFS_TEST_ERROR(!agi_ok, mp, XFS_ERRTAG_IUNLINK,
+ XFS_RANDOM_IUNLINK))) {
+ XFS_CORRUPTION_ERROR("xfs_iunlink", XFS_ERRLEVEL_LOW, mp, agi);
+ xfs_trans_brelse(tp, agibp);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ /*
+ * Get the index into the agi hash table for the
+ * list this inode will go on.
+ */
+ agino = XFS_INO_TO_AGINO(mp, ip->i_ino);
+ ASSERT(agino != 0);
+ bucket_index = agino % XFS_AGI_UNLINKED_BUCKETS;
+ ASSERT(!INT_ISZERO(agi->agi_unlinked[bucket_index], ARCH_CONVERT));
+ ASSERT(INT_GET(agi->agi_unlinked[bucket_index], ARCH_CONVERT) != agino);
+
+ if (INT_GET(agi->agi_unlinked[bucket_index], ARCH_CONVERT) != NULLAGINO) {
+ /*
+ * There is already another inode in the bucket we need
+ * to add ourselves to. Add us at the front of the list.
+ * Here we put the head pointer into our next pointer,
+ * and then we fall through to point the head at us.
+ */
+ error = xfs_itobp(mp, tp, ip, &dip, &ibp, 0);
+ if (error) {
+ return error;
+ }
+ ASSERT(INT_GET(dip->di_next_unlinked, ARCH_CONVERT) == NULLAGINO);
+ ASSERT(!INT_ISZERO(dip->di_next_unlinked, ARCH_CONVERT));
+ /* both on-disk, don't endian flip twice */
+ dip->di_next_unlinked = agi->agi_unlinked[bucket_index];
+ offset = ip->i_boffset +
+ offsetof(xfs_dinode_t, di_next_unlinked);
+ xfs_trans_inode_buf(tp, ibp);
+ xfs_trans_log_buf(tp, ibp, offset,
+ (offset + sizeof(xfs_agino_t) - 1));
+ xfs_inobp_check(mp, ibp);
+ }
+
+ /*
+ * Point the bucket head pointer at the inode being inserted.
+ */
+ ASSERT(agino != 0);
+ INT_SET(agi->agi_unlinked[bucket_index], ARCH_CONVERT, agino);
+ offset = offsetof(xfs_agi_t, agi_unlinked) +
+ (sizeof(xfs_agino_t) * bucket_index);
+ xfs_trans_log_buf(tp, agibp, offset,
+ (offset + sizeof(xfs_agino_t) - 1));
+ return 0;
+}
+
+/*
+ * Pull the on-disk inode from the AGI unlinked list.
+ */
+STATIC int
+xfs_iunlink_remove(
+ xfs_trans_t *tp,
+ xfs_inode_t *ip)
+{
+ xfs_ino_t next_ino;
+ xfs_mount_t *mp;
+ xfs_agi_t *agi;
+ xfs_dinode_t *dip;
+ xfs_buf_t *agibp;
+ xfs_buf_t *ibp;
+ xfs_agnumber_t agno;
+ xfs_daddr_t agdaddr;
+ xfs_agino_t agino;
+ xfs_agino_t next_agino;
+ xfs_buf_t *last_ibp;
+ xfs_dinode_t *last_dip;
+ short bucket_index;
+ int offset, last_offset;
+ int error;
+ int agi_ok;
+
+ /*
+ * First pull the on-disk inode from the AGI unlinked list.
+ */
+ mp = tp->t_mountp;
+
+ agno = XFS_INO_TO_AGNO(mp, ip->i_ino);
+ agdaddr = XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp));
+
+ /*
+ * Get the agi buffer first. It ensures lock ordering
+ * on the list.
+ */
+ error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp, agdaddr,
+ XFS_FSS_TO_BB(mp, 1), 0, &agibp);
+ if (error) {
+ cmn_err(CE_WARN,
+ "xfs_iunlink_remove: xfs_trans_read_buf() returned an error %d on %s. Returning error.",
+ error, mp->m_fsname);
+ return error;
+ }
+ /*
+ * Validate the magic number of the agi block.
+ */
+ agi = XFS_BUF_TO_AGI(agibp);
+ agi_ok =
+ INT_GET(agi->agi_magicnum, ARCH_CONVERT) == XFS_AGI_MAGIC &&
+ XFS_AGI_GOOD_VERSION(INT_GET(agi->agi_versionnum, ARCH_CONVERT));
+ if (unlikely(XFS_TEST_ERROR(!agi_ok, mp, XFS_ERRTAG_IUNLINK_REMOVE,
+ XFS_RANDOM_IUNLINK_REMOVE))) {
+ XFS_CORRUPTION_ERROR("xfs_iunlink_remove", XFS_ERRLEVEL_LOW,
+ mp, agi);
+ xfs_trans_brelse(tp, agibp);
+ cmn_err(CE_WARN,
+ "xfs_iunlink_remove: XFS_TEST_ERROR() returned an error on %s. Returning EFSCORRUPTED.",
+ mp->m_fsname);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ /*
+ * Get the index into the agi hash table for the
+ * list this inode will go on.
+ */
+ agino = XFS_INO_TO_AGINO(mp, ip->i_ino);
+ ASSERT(agino != 0);
+ bucket_index = agino % XFS_AGI_UNLINKED_BUCKETS;
+ ASSERT(INT_GET(agi->agi_unlinked[bucket_index], ARCH_CONVERT) != NULLAGINO);
+ ASSERT(!INT_ISZERO(agi->agi_unlinked[bucket_index], ARCH_CONVERT));
+
+ if (INT_GET(agi->agi_unlinked[bucket_index], ARCH_CONVERT) == agino) {
+ /*
+ * We're at the head of the list. Get the inode's
+ * on-disk buffer to see if there is anyone after us
+ * on the list. Only modify our next pointer if it
+ * is not already NULLAGINO. This saves us the overhead
+ * of dealing with the buffer when there is no need to
+ * change it.
+ */
+ error = xfs_itobp(mp, tp, ip, &dip, &ibp, 0);
+ if (error) {
+ cmn_err(CE_WARN,
+ "xfs_iunlink_remove: xfs_itobp() returned an error %d on %s. Returning error.",
+ error, mp->m_fsname);
+ return error;
+ }
+ next_agino = INT_GET(dip->di_next_unlinked, ARCH_CONVERT);
+ ASSERT(next_agino != 0);
+ if (next_agino != NULLAGINO) {
+ INT_SET(dip->di_next_unlinked, ARCH_CONVERT, NULLAGINO);
+ offset = ip->i_boffset +
+ offsetof(xfs_dinode_t, di_next_unlinked);
+ xfs_trans_inode_buf(tp, ibp);
+ xfs_trans_log_buf(tp, ibp, offset,
+ (offset + sizeof(xfs_agino_t) - 1));
+ xfs_inobp_check(mp, ibp);
+ } else {
+ xfs_trans_brelse(tp, ibp);
+ }
+ /*
+ * Point the bucket head pointer at the next inode.
+ */
+ ASSERT(next_agino != 0);
+ ASSERT(next_agino != agino);
+ INT_SET(agi->agi_unlinked[bucket_index], ARCH_CONVERT, next_agino);
+ offset = offsetof(xfs_agi_t, agi_unlinked) +
+ (sizeof(xfs_agino_t) * bucket_index);
+ xfs_trans_log_buf(tp, agibp, offset,
+ (offset + sizeof(xfs_agino_t) - 1));
+ } else {
+ /*
+ * We need to search the list for the inode being freed.
+ */
+ next_agino = INT_GET(agi->agi_unlinked[bucket_index], ARCH_CONVERT);
+ last_ibp = NULL;
+ while (next_agino != agino) {
+ /*
+ * If the last inode wasn't the one pointing to
+ * us, then release its buffer since we're not
+ * going to do anything with it.
+ */
+ if (last_ibp != NULL) {
+ xfs_trans_brelse(tp, last_ibp);
+ }
+ next_ino = XFS_AGINO_TO_INO(mp, agno, next_agino);
+ error = xfs_inotobp(mp, tp, next_ino, &last_dip,
+ &last_ibp, &last_offset);
+ if (error) {
+ cmn_err(CE_WARN,
+ "xfs_iunlink_remove: xfs_inotobp() returned an error %d on %s. Returning error.",
+ error, mp->m_fsname);
+ return error;
+ }
+ next_agino = INT_GET(last_dip->di_next_unlinked, ARCH_CONVERT);
+ ASSERT(next_agino != NULLAGINO);
+ ASSERT(next_agino != 0);
+ }
+ /*
+ * Now last_ibp points to the buffer previous to us on
+ * the unlinked list. Pull us from the list.
+ */
+ error = xfs_itobp(mp, tp, ip, &dip, &ibp, 0);
+ if (error) {
+ cmn_err(CE_WARN,
+ "xfs_iunlink_remove: xfs_itobp() returned an error %d on %s. Returning error.",
+ error, mp->m_fsname);
+ return error;
+ }
+ next_agino = INT_GET(dip->di_next_unlinked, ARCH_CONVERT);
+ ASSERT(next_agino != 0);
+ ASSERT(next_agino != agino);
+ if (next_agino != NULLAGINO) {
+ INT_SET(dip->di_next_unlinked, ARCH_CONVERT, NULLAGINO);
+ offset = ip->i_boffset +
+ offsetof(xfs_dinode_t, di_next_unlinked);
+ xfs_trans_inode_buf(tp, ibp);
+ xfs_trans_log_buf(tp, ibp, offset,
+ (offset + sizeof(xfs_agino_t) - 1));
+ xfs_inobp_check(mp, ibp);
+ } else {
+ xfs_trans_brelse(tp, ibp);
+ }
+ /*
+ * Point the previous inode on the list to the next inode.
+ */
+ INT_SET(last_dip->di_next_unlinked, ARCH_CONVERT, next_agino);
+ ASSERT(next_agino != 0);
+ offset = last_offset + offsetof(xfs_dinode_t, di_next_unlinked);
+ xfs_trans_inode_buf(tp, last_ibp);
+ xfs_trans_log_buf(tp, last_ibp, offset,
+ (offset + sizeof(xfs_agino_t) - 1));
+ xfs_inobp_check(mp, last_ibp);
+ }
+ return 0;
+}
+
+static __inline__ int xfs_inode_clean(xfs_inode_t *ip)
+{
+ return (((ip->i_itemp == NULL) ||
+ !(ip->i_itemp->ili_format.ilf_fields & XFS_ILOG_ALL)) &&
+ (ip->i_update_core == 0));
+}
+
+void
+xfs_ifree_cluster(
+ xfs_inode_t *free_ip,
+ xfs_trans_t *tp,
+ xfs_ino_t inum)
+{
+ xfs_mount_t *mp = free_ip->i_mount;
+ int blks_per_cluster;
+ int nbufs;
+ int ninodes;
+ int i, j, found, pre_flushed;
+ xfs_daddr_t blkno;
+ xfs_buf_t *bp;
+ xfs_ihash_t *ih;
+ xfs_inode_t *ip, **ip_found;
+ xfs_inode_log_item_t *iip;
+ xfs_log_item_t *lip;
+ SPLDECL(s);
+
+ if (mp->m_sb.sb_blocksize >= XFS_INODE_CLUSTER_SIZE(mp)) {
+ blks_per_cluster = 1;
+ ninodes = mp->m_sb.sb_inopblock;
+ nbufs = XFS_IALLOC_BLOCKS(mp);
+ } else {
+ blks_per_cluster = XFS_INODE_CLUSTER_SIZE(mp) /
+ mp->m_sb.sb_blocksize;
+ ninodes = blks_per_cluster * mp->m_sb.sb_inopblock;
+ nbufs = XFS_IALLOC_BLOCKS(mp) / blks_per_cluster;
+ }
+
+ ip_found = kmem_alloc(ninodes * sizeof(xfs_inode_t *), KM_NOFS);
+
+ for (j = 0; j < nbufs; j++, inum += ninodes) {
+ blkno = XFS_AGB_TO_DADDR(mp, XFS_INO_TO_AGNO(mp, inum),
+ XFS_INO_TO_AGBNO(mp, inum));
+
+
+ /*
+ * Look for each inode in memory and attempt to lock it,
+ * we can be racing with flush and tail pushing here.
+ * any inode we get the locks on, add to an array of
+ * inode items to process later.
+ *
+ * The get the buffer lock, we could beat a flush
+ * or tail pushing thread to the lock here, in which
+ * case they will go looking for the inode buffer
+ * and fail, we need some other form of interlock
+ * here.
+ */
+ found = 0;
+ for (i = 0; i < ninodes; i++) {
+ ih = XFS_IHASH(mp, inum + i);
+ read_lock(&ih->ih_lock);
+ for (ip = ih->ih_next; ip != NULL; ip = ip->i_next) {
+ if (ip->i_ino == inum + i)
+ break;
+ }
+
+ /* Inode not in memory or we found it already,
+ * nothing to do
+ */
+ if (!ip || (ip->i_flags & XFS_ISTALE)) {
+ read_unlock(&ih->ih_lock);
+ continue;
+ }
+
+ if (xfs_inode_clean(ip)) {
+ read_unlock(&ih->ih_lock);
+ continue;
+ }
+
+ /* If we can get the locks then add it to the
+ * list, otherwise by the time we get the bp lock
+ * below it will already be attached to the
+ * inode buffer.
+ */
+
+ /* This inode will already be locked - by us, lets
+ * keep it that way.
+ */
+
+ if (ip == free_ip) {
+ if (xfs_iflock_nowait(ip)) {
+ ip->i_flags |= XFS_ISTALE;
+
+ if (xfs_inode_clean(ip)) {
+ xfs_ifunlock(ip);
+ } else {
+ ip_found[found++] = ip;
+ }
+ }
+ read_unlock(&ih->ih_lock);
+ continue;
+ }
+
+ if (xfs_ilock_nowait(ip, XFS_ILOCK_EXCL)) {
+ if (xfs_iflock_nowait(ip)) {
+ ip->i_flags |= XFS_ISTALE;
+
+ if (xfs_inode_clean(ip)) {
+ xfs_ifunlock(ip);
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ } else {
+ ip_found[found++] = ip;
+ }
+ } else {
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ }
+ }
+
+ read_unlock(&ih->ih_lock);
+ }
+
+ bp = xfs_trans_get_buf(tp, mp->m_ddev_targp, blkno,
+ mp->m_bsize * blks_per_cluster,
+ XFS_BUF_LOCK);
+
+ pre_flushed = 0;
+ lip = XFS_BUF_FSPRIVATE(bp, xfs_log_item_t *);
+ while (lip) {
+ if (lip->li_type == XFS_LI_INODE) {
+ iip = (xfs_inode_log_item_t *)lip;
+ ASSERT(iip->ili_logged == 1);
+ lip->li_cb = (void(*)(xfs_buf_t*,xfs_log_item_t*)) xfs_istale_done;
+ AIL_LOCK(mp,s);
+ iip->ili_flush_lsn = iip->ili_item.li_lsn;
+ AIL_UNLOCK(mp, s);
+ iip->ili_inode->i_flags |= XFS_ISTALE;
+ pre_flushed++;
+ }
+ lip = lip->li_bio_list;
+ }
+
+ for (i = 0; i < found; i++) {
+ ip = ip_found[i];
+ iip = ip->i_itemp;
+
+ if (!iip) {
+ ip->i_update_core = 0;
+ xfs_ifunlock(ip);
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ continue;
+ }
+
+ iip->ili_last_fields = iip->ili_format.ilf_fields;
+ iip->ili_format.ilf_fields = 0;
+ iip->ili_logged = 1;
+ AIL_LOCK(mp,s);
+ iip->ili_flush_lsn = iip->ili_item.li_lsn;
+ AIL_UNLOCK(mp, s);
+
+ xfs_buf_attach_iodone(bp,
+ (void(*)(xfs_buf_t*,xfs_log_item_t*))
+ xfs_istale_done, (xfs_log_item_t *)iip);
+ if (ip != free_ip) {
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ }
+ }
+
+ if (found || pre_flushed)
+ xfs_trans_stale_inode_buf(tp, bp);
+ xfs_trans_binval(tp, bp);
+ }
+
+ kmem_free(ip_found, ninodes * sizeof(xfs_inode_t *));
+}
+
+/*
+ * This is called to return an inode to the inode free list.
+ * The inode should already be truncated to 0 length and have
+ * no pages associated with it. This routine also assumes that
+ * the inode is already a part of the transaction.
+ *
+ * The on-disk copy of the inode will have been added to the list
+ * of unlinked inodes in the AGI. We need to remove the inode from
+ * that list atomically with respect to freeing it here.
+ */
+int
+xfs_ifree(
+ xfs_trans_t *tp,
+ xfs_inode_t *ip,
+ xfs_bmap_free_t *flist)
+{
+ int error;
+ int delete;
+ xfs_ino_t first_ino;
+
+ ASSERT(ismrlocked(&ip->i_lock, MR_UPDATE));
+ ASSERT(ip->i_transp == tp);
+ ASSERT(ip->i_d.di_nlink == 0);
+ ASSERT(ip->i_d.di_nextents == 0);
+ ASSERT(ip->i_d.di_anextents == 0);
+ ASSERT((ip->i_d.di_size == 0) ||
+ ((ip->i_d.di_mode & S_IFMT) != S_IFREG));
+ ASSERT(ip->i_d.di_nblocks == 0);
+
+ /*
+ * Pull the on-disk inode from the AGI unlinked list.
+ */
+ error = xfs_iunlink_remove(tp, ip);
+ if (error != 0) {
+ return error;
+ }
+
+ error = xfs_difree(tp, ip->i_ino, flist, &delete, &first_ino);
+ if (error != 0) {
+ return error;
+ }
+ ip->i_d.di_mode = 0; /* mark incore inode as free */
+ ip->i_d.di_flags = 0;
+ ip->i_d.di_dmevmask = 0;
+ ip->i_d.di_forkoff = 0; /* mark the attr fork not in use */
+ ip->i_df.if_ext_max =
+ XFS_IFORK_DSIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t);
+ ip->i_d.di_format = XFS_DINODE_FMT_EXTENTS;
+ ip->i_d.di_aformat = XFS_DINODE_FMT_EXTENTS;
+ /*
+ * Bump the generation count so no one will be confused
+ * by reincarnations of this inode.
+ */
+ ip->i_d.di_gen++;
+ xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+
+ if (delete) {
+ xfs_ifree_cluster(ip, tp, first_ino);
+ }
+
+ return 0;
+}
+
+/*
+ * Reallocate the space for if_broot based on the number of records
+ * being added or deleted as indicated in rec_diff. Move the records
+ * and pointers in if_broot to fit the new size. When shrinking this
+ * will eliminate holes between the records and pointers created by
+ * the caller. When growing this will create holes to be filled in
+ * by the caller.
+ *
+ * The caller must not request to add more records than would fit in
+ * the on-disk inode root. If the if_broot is currently NULL, then
+ * if we adding records one will be allocated. The caller must also
+ * not request that the number of records go below zero, although
+ * it can go to zero.
+ *
+ * ip -- the inode whose if_broot area is changing
+ * ext_diff -- the change in the number of records, positive or negative,
+ * requested for the if_broot array.
+ */
+void
+xfs_iroot_realloc(
+ xfs_inode_t *ip,
+ int rec_diff,
+ int whichfork)
+{
+ int cur_max;
+ xfs_ifork_t *ifp;
+ xfs_bmbt_block_t *new_broot;
+ int new_max;
+ size_t new_size;
+ char *np;
+ char *op;
+
+ /*
+ * Handle the degenerate case quietly.
+ */
+ if (rec_diff == 0) {
+ return;
+ }
+
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+ if (rec_diff > 0) {
+ /*
+ * If there wasn't any memory allocated before, just
+ * allocate it now and get out.
+ */
+ if (ifp->if_broot_bytes == 0) {
+ new_size = (size_t)XFS_BMAP_BROOT_SPACE_CALC(rec_diff);
+ ifp->if_broot = (xfs_bmbt_block_t*)kmem_alloc(new_size,
+ KM_SLEEP);
+ ifp->if_broot_bytes = (int)new_size;
+ return;
+ }
+
+ /*
+ * If there is already an existing if_broot, then we need
+ * to realloc() it and shift the pointers to their new
+ * location. The records don't change location because
+ * they are kept butted up against the btree block header.
+ */
+ cur_max = XFS_BMAP_BROOT_MAXRECS(ifp->if_broot_bytes);
+ new_max = cur_max + rec_diff;
+ new_size = (size_t)XFS_BMAP_BROOT_SPACE_CALC(new_max);
+ ifp->if_broot = (xfs_bmbt_block_t *)
+ kmem_realloc(ifp->if_broot,
+ new_size,
+ (size_t)XFS_BMAP_BROOT_SPACE_CALC(cur_max), /* old size */
+ KM_SLEEP);
+ op = (char *)XFS_BMAP_BROOT_PTR_ADDR(ifp->if_broot, 1,
+ ifp->if_broot_bytes);
+ np = (char *)XFS_BMAP_BROOT_PTR_ADDR(ifp->if_broot, 1,
+ (int)new_size);
+ ifp->if_broot_bytes = (int)new_size;
+ ASSERT(ifp->if_broot_bytes <=
+ XFS_IFORK_SIZE(ip, whichfork) + XFS_BROOT_SIZE_ADJ);
+ memmove(np, op, cur_max * (uint)sizeof(xfs_dfsbno_t));
+ return;
+ }
+
+ /*
+ * rec_diff is less than 0. In this case, we are shrinking the
+ * if_broot buffer. It must already exist. If we go to zero
+ * records, just get rid of the root and clear the status bit.
+ */
+ ASSERT((ifp->if_broot != NULL) && (ifp->if_broot_bytes > 0));
+ cur_max = XFS_BMAP_BROOT_MAXRECS(ifp->if_broot_bytes);
+ new_max = cur_max + rec_diff;
+ ASSERT(new_max >= 0);
+ if (new_max > 0)
+ new_size = (size_t)XFS_BMAP_BROOT_SPACE_CALC(new_max);
+ else
+ new_size = 0;
+ if (new_size > 0) {
+ new_broot = (xfs_bmbt_block_t *)kmem_alloc(new_size, KM_SLEEP);
+ /*
+ * First copy over the btree block header.
+ */
+ memcpy(new_broot, ifp->if_broot, sizeof(xfs_bmbt_block_t));
+ } else {
+ new_broot = NULL;
+ ifp->if_flags &= ~XFS_IFBROOT;
+ }
+
+ /*
+ * Only copy the records and pointers if there are any.
+ */
+ if (new_max > 0) {
+ /*
+ * First copy the records.
+ */
+ op = (char *)XFS_BMAP_BROOT_REC_ADDR(ifp->if_broot, 1,
+ ifp->if_broot_bytes);
+ np = (char *)XFS_BMAP_BROOT_REC_ADDR(new_broot, 1,
+ (int)new_size);
+ memcpy(np, op, new_max * (uint)sizeof(xfs_bmbt_rec_t));
+
+ /*
+ * Then copy the pointers.
+ */
+ op = (char *)XFS_BMAP_BROOT_PTR_ADDR(ifp->if_broot, 1,
+ ifp->if_broot_bytes);
+ np = (char *)XFS_BMAP_BROOT_PTR_ADDR(new_broot, 1,
+ (int)new_size);
+ memcpy(np, op, new_max * (uint)sizeof(xfs_dfsbno_t));
+ }
+ kmem_free(ifp->if_broot, ifp->if_broot_bytes);
+ ifp->if_broot = new_broot;
+ ifp->if_broot_bytes = (int)new_size;
+ ASSERT(ifp->if_broot_bytes <=
+ XFS_IFORK_SIZE(ip, whichfork) + XFS_BROOT_SIZE_ADJ);
+ return;
+}
+
+
+/*
+ * This is called when the amount of space needed for if_extents
+ * is increased or decreased. The change in size is indicated by
+ * the number of extents that need to be added or deleted in the
+ * ext_diff parameter.
+ *
+ * If the amount of space needed has decreased below the size of the
+ * inline buffer, then switch to using the inline buffer. Otherwise,
+ * use kmem_realloc() or kmem_alloc() to adjust the size of the buffer
+ * to what is needed.
+ *
+ * ip -- the inode whose if_extents area is changing
+ * ext_diff -- the change in the number of extents, positive or negative,
+ * requested for the if_extents array.
+ */
+void
+xfs_iext_realloc(
+ xfs_inode_t *ip,
+ int ext_diff,
+ int whichfork)
+{
+ int byte_diff;
+ xfs_ifork_t *ifp;
+ int new_size;
+ uint rnew_size;
+
+ if (ext_diff == 0) {
+ return;
+ }
+
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+ byte_diff = ext_diff * (uint)sizeof(xfs_bmbt_rec_t);
+ new_size = (int)ifp->if_bytes + byte_diff;
+ ASSERT(new_size >= 0);
+
+ if (new_size == 0) {
+ if (ifp->if_u1.if_extents != ifp->if_u2.if_inline_ext) {
+ ASSERT(ifp->if_real_bytes != 0);
+ kmem_free(ifp->if_u1.if_extents, ifp->if_real_bytes);
+ }
+ ifp->if_u1.if_extents = NULL;
+ rnew_size = 0;
+ } else if (new_size <= sizeof(ifp->if_u2.if_inline_ext)) {
+ /*
+ * If the valid extents can fit in if_inline_ext,
+ * copy them from the malloc'd vector and free it.
+ */
+ if (ifp->if_u1.if_extents != ifp->if_u2.if_inline_ext) {
+ /*
+ * For now, empty files are format EXTENTS,
+ * so the if_extents pointer is null.
+ */
+ if (ifp->if_u1.if_extents) {
+ memcpy(ifp->if_u2.if_inline_ext,
+ ifp->if_u1.if_extents, new_size);
+ kmem_free(ifp->if_u1.if_extents,
+ ifp->if_real_bytes);
+ }
+ ifp->if_u1.if_extents = ifp->if_u2.if_inline_ext;
+ }
+ rnew_size = 0;
+ } else {
+ rnew_size = new_size;
+ if ((rnew_size & (rnew_size - 1)) != 0)
+ rnew_size = xfs_iroundup(rnew_size);
+ /*
+ * Stuck with malloc/realloc.
+ */
+ if (ifp->if_u1.if_extents == ifp->if_u2.if_inline_ext) {
+ ifp->if_u1.if_extents = (xfs_bmbt_rec_t *)
+ kmem_alloc(rnew_size, KM_SLEEP);
+ memcpy(ifp->if_u1.if_extents, ifp->if_u2.if_inline_ext,
+ sizeof(ifp->if_u2.if_inline_ext));
+ } else if (rnew_size != ifp->if_real_bytes) {
+ ifp->if_u1.if_extents = (xfs_bmbt_rec_t *)
+ kmem_realloc(ifp->if_u1.if_extents,
+ rnew_size,
+ ifp->if_real_bytes,
+ KM_NOFS);
+ }
+ }
+ ifp->if_real_bytes = rnew_size;
+ ifp->if_bytes = new_size;
+}
+
+
+/*
+ * This is called when the amount of space needed for if_data
+ * is increased or decreased. The change in size is indicated by
+ * the number of bytes that need to be added or deleted in the
+ * byte_diff parameter.
+ *
+ * If the amount of space needed has decreased below the size of the
+ * inline buffer, then switch to using the inline buffer. Otherwise,
+ * use kmem_realloc() or kmem_alloc() to adjust the size of the buffer
+ * to what is needed.
+ *
+ * ip -- the inode whose if_data area is changing
+ * byte_diff -- the change in the number of bytes, positive or negative,
+ * requested for the if_data array.
+ */
+void
+xfs_idata_realloc(
+ xfs_inode_t *ip,
+ int byte_diff,
+ int whichfork)
+{
+ xfs_ifork_t *ifp;
+ int new_size;
+ int real_size;
+
+ if (byte_diff == 0) {
+ return;
+ }
+
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+ new_size = (int)ifp->if_bytes + byte_diff;
+ ASSERT(new_size >= 0);
+
+ if (new_size == 0) {
+ if (ifp->if_u1.if_data != ifp->if_u2.if_inline_data) {
+ kmem_free(ifp->if_u1.if_data, ifp->if_real_bytes);
+ }
+ ifp->if_u1.if_data = NULL;
+ real_size = 0;
+ } else if (new_size <= sizeof(ifp->if_u2.if_inline_data)) {
+ /*
+ * If the valid extents/data can fit in if_inline_ext/data,
+ * copy them from the malloc'd vector and free it.
+ */
+ if (ifp->if_u1.if_data == NULL) {
+ ifp->if_u1.if_data = ifp->if_u2.if_inline_data;
+ } else if (ifp->if_u1.if_data != ifp->if_u2.if_inline_data) {
+ ASSERT(ifp->if_real_bytes != 0);
+ memcpy(ifp->if_u2.if_inline_data, ifp->if_u1.if_data,
+ new_size);
+ kmem_free(ifp->if_u1.if_data, ifp->if_real_bytes);
+ ifp->if_u1.if_data = ifp->if_u2.if_inline_data;
+ }
+ real_size = 0;
+ } else {
+ /*
+ * Stuck with malloc/realloc.
+ * For inline data, the underlying buffer must be
+ * a multiple of 4 bytes in size so that it can be
+ * logged and stay on word boundaries. We enforce
+ * that here.
+ */
+ real_size = roundup(new_size, 4);
+ if (ifp->if_u1.if_data == NULL) {
+ ASSERT(ifp->if_real_bytes == 0);
+ ifp->if_u1.if_data = kmem_alloc(real_size, KM_SLEEP);
+ } else if (ifp->if_u1.if_data != ifp->if_u2.if_inline_data) {
+ /*
+ * Only do the realloc if the underlying size
+ * is really changing.
+ */
+ if (ifp->if_real_bytes != real_size) {
+ ifp->if_u1.if_data =
+ kmem_realloc(ifp->if_u1.if_data,
+ real_size,
+ ifp->if_real_bytes,
+ KM_SLEEP);
+ }
+ } else {
+ ASSERT(ifp->if_real_bytes == 0);
+ ifp->if_u1.if_data = kmem_alloc(real_size, KM_SLEEP);
+ memcpy(ifp->if_u1.if_data, ifp->if_u2.if_inline_data,
+ ifp->if_bytes);
+ }
+ }
+ ifp->if_real_bytes = real_size;
+ ifp->if_bytes = new_size;
+ ASSERT(ifp->if_bytes <= XFS_IFORK_SIZE(ip, whichfork));
+}
+
+
+
+
+/*
+ * Map inode to disk block and offset.
+ *
+ * mp -- the mount point structure for the current file system
+ * tp -- the current transaction
+ * ino -- the inode number of the inode to be located
+ * imap -- this structure is filled in with the information necessary
+ * to retrieve the given inode from disk
+ * flags -- flags to pass to xfs_dilocate indicating whether or not
+ * lookups in the inode btree were OK or not
+ */
+int
+xfs_imap(
+ xfs_mount_t *mp,
+ xfs_trans_t *tp,
+ xfs_ino_t ino,
+ xfs_imap_t *imap,
+ uint flags)
+{
+ xfs_fsblock_t fsbno;
+ int len;
+ int off;
+ int error;
+
+ fsbno = imap->im_blkno ?
+ XFS_DADDR_TO_FSB(mp, imap->im_blkno) : NULLFSBLOCK;
+ error = xfs_dilocate(mp, tp, ino, &fsbno, &len, &off, flags);
+ if (error != 0) {
+ return error;
+ }
+ imap->im_blkno = XFS_FSB_TO_DADDR(mp, fsbno);
+ imap->im_len = XFS_FSB_TO_BB(mp, len);
+ imap->im_agblkno = XFS_FSB_TO_AGBNO(mp, fsbno);
+ imap->im_ioffset = (ushort)off;
+ imap->im_boffset = (ushort)(off << mp->m_sb.sb_inodelog);
+ return 0;
+}
+
+void
+xfs_idestroy_fork(
+ xfs_inode_t *ip,
+ int whichfork)
+{
+ xfs_ifork_t *ifp;
+
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+ if (ifp->if_broot != NULL) {
+ kmem_free(ifp->if_broot, ifp->if_broot_bytes);
+ ifp->if_broot = NULL;
+ }
+
+ /*
+ * If the format is local, then we can't have an extents
+ * array so just look for an inline data array. If we're
+ * not local then we may or may not have an extents list,
+ * so check and free it up if we do.
+ */
+ if (XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_LOCAL) {
+ if ((ifp->if_u1.if_data != ifp->if_u2.if_inline_data) &&
+ (ifp->if_u1.if_data != NULL)) {
+ ASSERT(ifp->if_real_bytes != 0);
+ kmem_free(ifp->if_u1.if_data, ifp->if_real_bytes);
+ ifp->if_u1.if_data = NULL;
+ ifp->if_real_bytes = 0;
+ }
+ } else if ((ifp->if_flags & XFS_IFEXTENTS) &&
+ (ifp->if_u1.if_extents != NULL) &&
+ (ifp->if_u1.if_extents != ifp->if_u2.if_inline_ext)) {
+ ASSERT(ifp->if_real_bytes != 0);
+ kmem_free(ifp->if_u1.if_extents, ifp->if_real_bytes);
+ ifp->if_u1.if_extents = NULL;
+ ifp->if_real_bytes = 0;
+ }
+ ASSERT(ifp->if_u1.if_extents == NULL ||
+ ifp->if_u1.if_extents == ifp->if_u2.if_inline_ext);
+ ASSERT(ifp->if_real_bytes == 0);
+ if (whichfork == XFS_ATTR_FORK) {
+ kmem_zone_free(xfs_ifork_zone, ip->i_afp);
+ ip->i_afp = NULL;
+ }
+}
+
+/*
+ * This is called free all the memory associated with an inode.
+ * It must free the inode itself and any buffers allocated for
+ * if_extents/if_data and if_broot. It must also free the lock
+ * associated with the inode.
+ */
+void
+xfs_idestroy(
+ xfs_inode_t *ip)
+{
+
+ switch (ip->i_d.di_mode & S_IFMT) {
+ case S_IFREG:
+ case S_IFDIR:
+ case S_IFLNK:
+ xfs_idestroy_fork(ip, XFS_DATA_FORK);
+ break;
+ }
+ if (ip->i_afp)
+ xfs_idestroy_fork(ip, XFS_ATTR_FORK);
+ mrfree(&ip->i_lock);
+ mrfree(&ip->i_iolock);
+ freesema(&ip->i_flock);
+#ifdef XFS_BMAP_TRACE
+ ktrace_free(ip->i_xtrace);
+#endif
+#ifdef XFS_BMBT_TRACE
+ ktrace_free(ip->i_btrace);
+#endif
+#ifdef XFS_RW_TRACE
+ ktrace_free(ip->i_rwtrace);
+#endif
+#ifdef XFS_ILOCK_TRACE
+ ktrace_free(ip->i_lock_trace);
+#endif
+#ifdef XFS_DIR2_TRACE
+ ktrace_free(ip->i_dir_trace);
+#endif
+ if (ip->i_itemp) {
+ /* XXXdpd should be able to assert this but shutdown
+ * is leaving the AIL behind. */
+ ASSERT(((ip->i_itemp->ili_item.li_flags & XFS_LI_IN_AIL) == 0) ||
+ XFS_FORCED_SHUTDOWN(ip->i_mount));
+ xfs_inode_item_destroy(ip);
+ }
+ kmem_zone_free(xfs_inode_zone, ip);
+}
+
+
+/*
+ * Increment the pin count of the given buffer.
+ * This value is protected by ipinlock spinlock in the mount structure.
+ */
+void
+xfs_ipin(
+ xfs_inode_t *ip)
+{
+ ASSERT(ismrlocked(&ip->i_lock, MR_UPDATE));
+
+ atomic_inc(&ip->i_pincount);
+}
+
+/*
+ * Decrement the pin count of the given inode, and wake up
+ * anyone in xfs_iwait_unpin() if the count goes to 0. The
+ * inode must have been previoulsy pinned with a call to xfs_ipin().
+ */
+void
+xfs_iunpin(
+ xfs_inode_t *ip)
+{
+ ASSERT(atomic_read(&ip->i_pincount) > 0);
+
+ if (atomic_dec_and_test(&ip->i_pincount)) {
+ vnode_t *vp = XFS_ITOV_NULL(ip);
+
+ /* make sync come back and flush this inode */
+ if (vp) {
+ struct inode *inode = LINVFS_GET_IP(vp);
+
+ if (!(inode->i_state & I_NEW))
+ mark_inode_dirty_sync(inode);
+ }
+
+ wake_up(&ip->i_ipin_wait);
+ }
+}
+
+/*
+ * This is called to wait for the given inode to be unpinned.
+ * It will sleep until this happens. The caller must have the
+ * inode locked in at least shared mode so that the buffer cannot
+ * be subsequently pinned once someone is waiting for it to be
+ * unpinned.
+ */
+void
+xfs_iunpin_wait(
+ xfs_inode_t *ip)
+{
+ xfs_inode_log_item_t *iip;
+ xfs_lsn_t lsn;
+
+ ASSERT(ismrlocked(&ip->i_lock, MR_UPDATE | MR_ACCESS));
+
+ if (atomic_read(&ip->i_pincount) == 0) {
+ return;
+ }
+
+ iip = ip->i_itemp;
+ if (iip && iip->ili_last_lsn) {
+ lsn = iip->ili_last_lsn;
+ } else {
+ lsn = (xfs_lsn_t)0;
+ }
+
+ /*
+ * Give the log a push so we don't wait here too long.
+ */
+ xfs_log_force(ip->i_mount, lsn, XFS_LOG_FORCE);
+
+ wait_event(ip->i_ipin_wait, (atomic_read(&ip->i_pincount) == 0));
+}
+
+
+/*
+ * xfs_iextents_copy()
+ *
+ * This is called to copy the REAL extents (as opposed to the delayed
+ * allocation extents) from the inode into the given buffer. It
+ * returns the number of bytes copied into the buffer.
+ *
+ * If there are no delayed allocation extents, then we can just
+ * memcpy() the extents into the buffer. Otherwise, we need to
+ * examine each extent in turn and skip those which are delayed.
+ */
+int
+xfs_iextents_copy(
+ xfs_inode_t *ip,
+ xfs_bmbt_rec_t *buffer,
+ int whichfork)
+{
+ int copied;
+ xfs_bmbt_rec_t *dest_ep;
+ xfs_bmbt_rec_t *ep;
+#ifdef XFS_BMAP_TRACE
+ static char fname[] = "xfs_iextents_copy";
+#endif
+ int i;
+ xfs_ifork_t *ifp;
+ int nrecs;
+ xfs_fsblock_t start_block;
+
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+ ASSERT(ismrlocked(&ip->i_lock, MR_UPDATE|MR_ACCESS));
+ ASSERT(ifp->if_bytes > 0);
+
+ nrecs = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
+ xfs_bmap_trace_exlist(fname, ip, nrecs, whichfork);
+ ASSERT(nrecs > 0);
+
+ /*
+ * There are some delayed allocation extents in the
+ * inode, so copy the extents one at a time and skip
+ * the delayed ones. There must be at least one
+ * non-delayed extent.
+ */
+ ep = ifp->if_u1.if_extents;
+ dest_ep = buffer;
+ copied = 0;
+ for (i = 0; i < nrecs; i++) {
+ start_block = xfs_bmbt_get_startblock(ep);
+ if (ISNULLSTARTBLOCK(start_block)) {
+ /*
+ * It's a delayed allocation extent, so skip it.
+ */
+ ep++;
+ continue;
+ }
+
+ /* Translate to on disk format */
+ put_unaligned(INT_GET(ep->l0, ARCH_CONVERT),
+ (__uint64_t*)&dest_ep->l0);
+ put_unaligned(INT_GET(ep->l1, ARCH_CONVERT),
+ (__uint64_t*)&dest_ep->l1);
+ dest_ep++;
+ ep++;
+ copied++;
+ }
+ ASSERT(copied != 0);
+ xfs_validate_extents(buffer, copied, 1, XFS_EXTFMT_INODE(ip));
+
+ return (copied * (uint)sizeof(xfs_bmbt_rec_t));
+}
+
+/*
+ * Each of the following cases stores data into the same region
+ * of the on-disk inode, so only one of them can be valid at
+ * any given time. While it is possible to have conflicting formats
+ * and log flags, e.g. having XFS_ILOG_?DATA set when the fork is
+ * in EXTENTS format, this can only happen when the fork has
+ * changed formats after being modified but before being flushed.
+ * In these cases, the format always takes precedence, because the
+ * format indicates the current state of the fork.
+ */
+/*ARGSUSED*/
+STATIC int
+xfs_iflush_fork(
+ xfs_inode_t *ip,
+ xfs_dinode_t *dip,
+ xfs_inode_log_item_t *iip,
+ int whichfork,
+ xfs_buf_t *bp)
+{
+ char *cp;
+ xfs_ifork_t *ifp;
+ xfs_mount_t *mp;
+#ifdef XFS_TRANS_DEBUG
+ int first;
+#endif
+ static const short brootflag[2] =
+ { XFS_ILOG_DBROOT, XFS_ILOG_ABROOT };
+ static const short dataflag[2] =
+ { XFS_ILOG_DDATA, XFS_ILOG_ADATA };
+ static const short extflag[2] =
+ { XFS_ILOG_DEXT, XFS_ILOG_AEXT };
+
+ if (iip == NULL)
+ return 0;
+ ifp = XFS_IFORK_PTR(ip, whichfork);
+ /*
+ * This can happen if we gave up in iformat in an error path,
+ * for the attribute fork.
+ */
+ if (ifp == NULL) {
+ ASSERT(whichfork == XFS_ATTR_FORK);
+ return 0;
+ }
+ cp = XFS_DFORK_PTR_ARCH(dip, whichfork, ARCH_CONVERT);
+ mp = ip->i_mount;
+ switch (XFS_IFORK_FORMAT(ip, whichfork)) {
+ case XFS_DINODE_FMT_LOCAL:
+ if ((iip->ili_format.ilf_fields & dataflag[whichfork]) &&
+ (ifp->if_bytes > 0)) {
+ ASSERT(ifp->if_u1.if_data != NULL);
+ ASSERT(ifp->if_bytes <= XFS_IFORK_SIZE(ip, whichfork));
+ memcpy(cp, ifp->if_u1.if_data, ifp->if_bytes);
+ }
+ if (whichfork == XFS_DATA_FORK) {
+ if (unlikely(XFS_DIR_SHORTFORM_VALIDATE_ONDISK(mp, dip))) {
+ XFS_ERROR_REPORT("xfs_iflush_fork",
+ XFS_ERRLEVEL_LOW, mp);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ }
+ break;
+
+ case XFS_DINODE_FMT_EXTENTS:
+ ASSERT((ifp->if_flags & XFS_IFEXTENTS) ||
+ !(iip->ili_format.ilf_fields & extflag[whichfork]));
+ ASSERT((ifp->if_u1.if_extents != NULL) || (ifp->if_bytes == 0));
+ ASSERT((ifp->if_u1.if_extents == NULL) || (ifp->if_bytes > 0));
+ if ((iip->ili_format.ilf_fields & extflag[whichfork]) &&
+ (ifp->if_bytes > 0)) {
+ ASSERT(XFS_IFORK_NEXTENTS(ip, whichfork) > 0);
+ (void)xfs_iextents_copy(ip, (xfs_bmbt_rec_t *)cp,
+ whichfork);
+ }
+ break;
+
+ case XFS_DINODE_FMT_BTREE:
+ if ((iip->ili_format.ilf_fields & brootflag[whichfork]) &&
+ (ifp->if_broot_bytes > 0)) {
+ ASSERT(ifp->if_broot != NULL);
+ ASSERT(ifp->if_broot_bytes <=
+ (XFS_IFORK_SIZE(ip, whichfork) +
+ XFS_BROOT_SIZE_ADJ));
+ xfs_bmbt_to_bmdr(ifp->if_broot, ifp->if_broot_bytes,
+ (xfs_bmdr_block_t *)cp,
+ XFS_DFORK_SIZE_ARCH(dip, mp, whichfork, ARCH_CONVERT));
+ }
+ break;
+
+ case XFS_DINODE_FMT_DEV:
+ if (iip->ili_format.ilf_fields & XFS_ILOG_DEV) {
+ ASSERT(whichfork == XFS_DATA_FORK);
+ INT_SET(dip->di_u.di_dev, ARCH_CONVERT, ip->i_df.if_u2.if_rdev);
+ }
+ break;
+
+ case XFS_DINODE_FMT_UUID:
+ if (iip->ili_format.ilf_fields & XFS_ILOG_UUID) {
+ ASSERT(whichfork == XFS_DATA_FORK);
+ memcpy(&dip->di_u.di_muuid, &ip->i_df.if_u2.if_uuid,
+ sizeof(uuid_t));
+ }
+ break;
+
+ default:
+ ASSERT(0);
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * xfs_iflush() will write a modified inode's changes out to the
+ * inode's on disk home. The caller must have the inode lock held
+ * in at least shared mode and the inode flush semaphore must be
+ * held as well. The inode lock will still be held upon return from
+ * the call and the caller is free to unlock it.
+ * The inode flush lock will be unlocked when the inode reaches the disk.
+ * The flags indicate how the inode's buffer should be written out.
+ */
+int
+xfs_iflush(
+ xfs_inode_t *ip,
+ uint flags)
+{
+ xfs_inode_log_item_t *iip;
+ xfs_buf_t *bp;
+ xfs_dinode_t *dip;
+ xfs_mount_t *mp;
+ int error;
+ /* REFERENCED */
+ xfs_chash_t *ch;
+ xfs_inode_t *iq;
+ int clcount; /* count of inodes clustered */
+ int bufwasdelwri;
+ enum { INT_DELWRI = (1 << 0), INT_ASYNC = (1 << 1) };
+ SPLDECL(s);
+
+ XFS_STATS_INC(xs_iflush_count);
+
+ ASSERT(ismrlocked(&ip->i_lock, MR_UPDATE|MR_ACCESS));
+ ASSERT(valusema(&ip->i_flock) <= 0);
+ ASSERT(ip->i_d.di_format != XFS_DINODE_FMT_BTREE ||
+ ip->i_d.di_nextents > ip->i_df.if_ext_max);
+
+ iip = ip->i_itemp;
+ mp = ip->i_mount;
+
+ /*
+ * If the inode isn't dirty, then just release the inode
+ * flush lock and do nothing.
+ */
+ if ((ip->i_update_core == 0) &&
+ ((iip == NULL) || !(iip->ili_format.ilf_fields & XFS_ILOG_ALL))) {
+ ASSERT((iip != NULL) ?
+ !(iip->ili_item.li_flags & XFS_LI_IN_AIL) : 1);
+ xfs_ifunlock(ip);
+ return 0;
+ }
+
+ /*
+ * We can't flush the inode until it is unpinned, so
+ * wait for it. We know noone new can pin it, because
+ * we are holding the inode lock shared and you need
+ * to hold it exclusively to pin the inode.
+ */
+ xfs_iunpin_wait(ip);
+
+ /*
+ * This may have been unpinned because the filesystem is shutting
+ * down forcibly. If that's the case we must not write this inode
+ * to disk, because the log record didn't make it to disk!
+ */
+ if (XFS_FORCED_SHUTDOWN(mp)) {
+ ip->i_update_core = 0;
+ if (iip)
+ iip->ili_format.ilf_fields = 0;
+ xfs_ifunlock(ip);
+ return XFS_ERROR(EIO);
+ }
+
+ /*
+ * Get the buffer containing the on-disk inode.
+ */
+ error = xfs_itobp(mp, NULL, ip, &dip, &bp, 0);
+ if (error != 0) {
+ xfs_ifunlock(ip);
+ return error;
+ }
+
+ /*
+ * Decide how buffer will be flushed out. This is done before
+ * the call to xfs_iflush_int because this field is zeroed by it.
+ */
+ if (iip != NULL && iip->ili_format.ilf_fields != 0) {
+ /*
+ * Flush out the inode buffer according to the directions
+ * of the caller. In the cases where the caller has given
+ * us a choice choose the non-delwri case. This is because
+ * the inode is in the AIL and we need to get it out soon.
+ */
+ switch (flags) {
+ case XFS_IFLUSH_SYNC:
+ case XFS_IFLUSH_DELWRI_ELSE_SYNC:
+ flags = 0;
+ break;
+ case XFS_IFLUSH_ASYNC:
+ case XFS_IFLUSH_DELWRI_ELSE_ASYNC:
+ flags = INT_ASYNC;
+ break;
+ case XFS_IFLUSH_DELWRI:
+ flags = INT_DELWRI;
+ break;
+ default:
+ ASSERT(0);
+ flags = 0;
+ break;
+ }
+ } else {
+ switch (flags) {
+ case XFS_IFLUSH_DELWRI_ELSE_SYNC:
+ case XFS_IFLUSH_DELWRI_ELSE_ASYNC:
+ case XFS_IFLUSH_DELWRI:
+ flags = INT_DELWRI;
+ break;
+ case XFS_IFLUSH_ASYNC:
+ flags = INT_ASYNC;
+ break;
+ case XFS_IFLUSH_SYNC:
+ flags = 0;
+ break;
+ default:
+ ASSERT(0);
+ flags = 0;
+ break;
+ }
+ }
+
+ /*
+ * First flush out the inode that xfs_iflush was called with.
+ */
+ error = xfs_iflush_int(ip, bp);
+ if (error) {
+ goto corrupt_out;
+ }
+
+ /*
+ * inode clustering:
+ * see if other inodes can be gathered into this write
+ */
+
+ ip->i_chash->chl_buf = bp;
+
+ ch = XFS_CHASH(mp, ip->i_blkno);
+ s = mutex_spinlock(&ch->ch_lock);
+
+ clcount = 0;
+ for (iq = ip->i_cnext; iq != ip; iq = iq->i_cnext) {
+ /*
+ * Do an un-protected check to see if the inode is dirty and
+ * is a candidate for flushing. These checks will be repeated
+ * later after the appropriate locks are acquired.
+ */
+ iip = iq->i_itemp;
+ if ((iq->i_update_core == 0) &&
+ ((iip == NULL) ||
+ !(iip->ili_format.ilf_fields & XFS_ILOG_ALL)) &&
+ xfs_ipincount(iq) == 0) {
+ continue;
+ }
+
+ /*
+ * Try to get locks. If any are unavailable,
+ * then this inode cannot be flushed and is skipped.
+ */
+
+ /* get inode locks (just i_lock) */
+ if (xfs_ilock_nowait(iq, XFS_ILOCK_SHARED)) {
+ /* get inode flush lock */
+ if (xfs_iflock_nowait(iq)) {
+ /* check if pinned */
+ if (xfs_ipincount(iq) == 0) {
+ /* arriving here means that
+ * this inode can be flushed.
+ * first re-check that it's
+ * dirty
+ */
+ iip = iq->i_itemp;
+ if ((iq->i_update_core != 0)||
+ ((iip != NULL) &&
+ (iip->ili_format.ilf_fields & XFS_ILOG_ALL))) {
+ clcount++;
+ error = xfs_iflush_int(iq, bp);
+ if (error) {
+ xfs_iunlock(iq,
+ XFS_ILOCK_SHARED);
+ goto cluster_corrupt_out;
+ }
+ } else {
+ xfs_ifunlock(iq);
+ }
+ } else {
+ xfs_ifunlock(iq);
+ }
+ }
+ xfs_iunlock(iq, XFS_ILOCK_SHARED);
+ }
+ }
+ mutex_spinunlock(&ch->ch_lock, s);
+
+ if (clcount) {
+ XFS_STATS_INC(xs_icluster_flushcnt);
+ XFS_STATS_ADD(xs_icluster_flushinode, clcount);
+ }
+
+ /*
+ * If the buffer is pinned then push on the log so we won't
+ * get stuck waiting in the write for too long.
+ */
+ if (XFS_BUF_ISPINNED(bp)){
+ xfs_log_force(mp, (xfs_lsn_t)0, XFS_LOG_FORCE);
+ }
+
+ if (flags & INT_DELWRI) {
+ xfs_bdwrite(mp, bp);
+ } else if (flags & INT_ASYNC) {
+ xfs_bawrite(mp, bp);
+ } else {
+ error = xfs_bwrite(mp, bp);
+ }
+ return error;
+
+corrupt_out:
+ xfs_buf_relse(bp);
+ xfs_force_shutdown(mp, XFS_CORRUPT_INCORE);
+ xfs_iflush_abort(ip);
+ /*
+ * Unlocks the flush lock
+ */
+ return XFS_ERROR(EFSCORRUPTED);
+
+cluster_corrupt_out:
+ /* Corruption detected in the clustering loop. Invalidate the
+ * inode buffer and shut down the filesystem.
+ */
+ mutex_spinunlock(&ch->ch_lock, s);
+
+ /*
+ * Clean up the buffer. If it was B_DELWRI, just release it --
+ * brelse can handle it with no problems. If not, shut down the
+ * filesystem before releasing the buffer.
+ */
+ if ((bufwasdelwri= XFS_BUF_ISDELAYWRITE(bp))) {
+ xfs_buf_relse(bp);
+ }
+
+ xfs_force_shutdown(mp, XFS_CORRUPT_INCORE);
+
+ if(!bufwasdelwri) {
+ /*
+ * Just like incore_relse: if we have b_iodone functions,
+ * mark the buffer as an error and call them. Otherwise
+ * mark it as stale and brelse.
+ */
+ if (XFS_BUF_IODONE_FUNC(bp)) {
+ XFS_BUF_CLR_BDSTRAT_FUNC(bp);
+ XFS_BUF_UNDONE(bp);
+ XFS_BUF_STALE(bp);
+ XFS_BUF_SHUT(bp);
+ XFS_BUF_ERROR(bp,EIO);
+ xfs_biodone(bp);
+ } else {
+ XFS_BUF_STALE(bp);
+ xfs_buf_relse(bp);
+ }
+ }
+
+ xfs_iflush_abort(iq);
+ /*
+ * Unlocks the flush lock
+ */
+ return XFS_ERROR(EFSCORRUPTED);
+}
+
+
+STATIC int
+xfs_iflush_int(
+ xfs_inode_t *ip,
+ xfs_buf_t *bp)
+{
+ xfs_inode_log_item_t *iip;
+ xfs_dinode_t *dip;
+ xfs_mount_t *mp;
+#ifdef XFS_TRANS_DEBUG
+ int first;
+#endif
+ SPLDECL(s);
+
+ ASSERT(ismrlocked(&ip->i_lock, MR_UPDATE|MR_ACCESS));
+ ASSERT(valusema(&ip->i_flock) <= 0);
+ ASSERT(ip->i_d.di_format != XFS_DINODE_FMT_BTREE ||
+ ip->i_d.di_nextents > ip->i_df.if_ext_max);
+
+ iip = ip->i_itemp;
+ mp = ip->i_mount;
+
+
+ /*
+ * If the inode isn't dirty, then just release the inode
+ * flush lock and do nothing.
+ */
+ if ((ip->i_update_core == 0) &&
+ ((iip == NULL) || !(iip->ili_format.ilf_fields & XFS_ILOG_ALL))) {
+ xfs_ifunlock(ip);
+ return 0;
+ }
+
+ /* set *dip = inode's place in the buffer */
+ dip = (xfs_dinode_t *)xfs_buf_offset(bp, ip->i_boffset);
+
+ /*
+ * Clear i_update_core before copying out the data.
+ * This is for coordination with our timestamp updates
+ * that don't hold the inode lock. They will always
+ * update the timestamps BEFORE setting i_update_core,
+ * so if we clear i_update_core after they set it we
+ * are guaranteed to see their updates to the timestamps.
+ * I believe that this depends on strongly ordered memory
+ * semantics, but we have that. We use the SYNCHRONIZE
+ * macro to make sure that the compiler does not reorder
+ * the i_update_core access below the data copy below.
+ */
+ ip->i_update_core = 0;
+ SYNCHRONIZE();
+
+ if (XFS_TEST_ERROR(INT_GET(dip->di_core.di_magic,ARCH_CONVERT) != XFS_DINODE_MAGIC,
+ mp, XFS_ERRTAG_IFLUSH_1, XFS_RANDOM_IFLUSH_1)) {
+ xfs_cmn_err(XFS_PTAG_IFLUSH, CE_ALERT, mp,
+ "xfs_iflush: Bad inode %Lu magic number 0x%x, ptr 0x%p",
+ ip->i_ino, (int) INT_GET(dip->di_core.di_magic, ARCH_CONVERT), dip);
+ goto corrupt_out;
+ }
+ if (XFS_TEST_ERROR(ip->i_d.di_magic != XFS_DINODE_MAGIC,
+ mp, XFS_ERRTAG_IFLUSH_2, XFS_RANDOM_IFLUSH_2)) {
+ xfs_cmn_err(XFS_PTAG_IFLUSH, CE_ALERT, mp,
+ "xfs_iflush: Bad inode %Lu, ptr 0x%p, magic number 0x%x",
+ ip->i_ino, ip, ip->i_d.di_magic);
+ goto corrupt_out;
+ }
+ if ((ip->i_d.di_mode & S_IFMT) == S_IFREG) {
+ if (XFS_TEST_ERROR(
+ (ip->i_d.di_format != XFS_DINODE_FMT_EXTENTS) &&
+ (ip->i_d.di_format != XFS_DINODE_FMT_BTREE),
+ mp, XFS_ERRTAG_IFLUSH_3, XFS_RANDOM_IFLUSH_3)) {
+ xfs_cmn_err(XFS_PTAG_IFLUSH, CE_ALERT, mp,
+ "xfs_iflush: Bad regular inode %Lu, ptr 0x%p",
+ ip->i_ino, ip);
+ goto corrupt_out;
+ }
+ } else if ((ip->i_d.di_mode & S_IFMT) == S_IFDIR) {
+ if (XFS_TEST_ERROR(
+ (ip->i_d.di_format != XFS_DINODE_FMT_EXTENTS) &&
+ (ip->i_d.di_format != XFS_DINODE_FMT_BTREE) &&
+ (ip->i_d.di_format != XFS_DINODE_FMT_LOCAL),
+ mp, XFS_ERRTAG_IFLUSH_4, XFS_RANDOM_IFLUSH_4)) {
+ xfs_cmn_err(XFS_PTAG_IFLUSH, CE_ALERT, mp,
+ "xfs_iflush: Bad directory inode %Lu, ptr 0x%p",
+ ip->i_ino, ip);
+ goto corrupt_out;
+ }
+ }
+ if (XFS_TEST_ERROR(ip->i_d.di_nextents + ip->i_d.di_anextents >
+ ip->i_d.di_nblocks, mp, XFS_ERRTAG_IFLUSH_5,
+ XFS_RANDOM_IFLUSH_5)) {
+ xfs_cmn_err(XFS_PTAG_IFLUSH, CE_ALERT, mp,
+ "xfs_iflush: detected corrupt incore inode %Lu, total extents = %d, nblocks = %Ld, ptr 0x%p",
+ ip->i_ino,
+ ip->i_d.di_nextents + ip->i_d.di_anextents,
+ ip->i_d.di_nblocks,
+ ip);
+ goto corrupt_out;
+ }
+ if (XFS_TEST_ERROR(ip->i_d.di_forkoff > mp->m_sb.sb_inodesize,
+ mp, XFS_ERRTAG_IFLUSH_6, XFS_RANDOM_IFLUSH_6)) {
+ xfs_cmn_err(XFS_PTAG_IFLUSH, CE_ALERT, mp,
+ "xfs_iflush: bad inode %Lu, forkoff 0x%x, ptr 0x%p",
+ ip->i_ino, ip->i_d.di_forkoff, ip);
+ goto corrupt_out;
+ }
+ /*
+ * bump the flush iteration count, used to detect flushes which
+ * postdate a log record during recovery.
+ */
+
+ ip->i_d.di_flushiter++;
+
+ /*
+ * Copy the dirty parts of the inode into the on-disk
+ * inode. We always copy out the core of the inode,
+ * because if the inode is dirty at all the core must
+ * be.
+ */
+ xfs_xlate_dinode_core((xfs_caddr_t)&(dip->di_core), &(ip->i_d),
+ -1, ARCH_CONVERT);
+
+ /* Wrap, we never let the log put out DI_MAX_FLUSH */
+ if (ip->i_d.di_flushiter == DI_MAX_FLUSH)
+ ip->i_d.di_flushiter = 0;
+
+ /*
+ * If this is really an old format inode and the superblock version
+ * has not been updated to support only new format inodes, then
+ * convert back to the old inode format. If the superblock version
+ * has been updated, then make the conversion permanent.
+ */
+ ASSERT(ip->i_d.di_version == XFS_DINODE_VERSION_1 ||
+ XFS_SB_VERSION_HASNLINK(&mp->m_sb));
+ if (ip->i_d.di_version == XFS_DINODE_VERSION_1) {
+ if (!XFS_SB_VERSION_HASNLINK(&mp->m_sb)) {
+ /*
+ * Convert it back.
+ */
+ ASSERT(ip->i_d.di_nlink <= XFS_MAXLINK_1);
+ INT_SET(dip->di_core.di_onlink, ARCH_CONVERT, ip->i_d.di_nlink);
+ } else {
+ /*
+ * The superblock version has already been bumped,
+ * so just make the conversion to the new inode
+ * format permanent.
+ */
+ ip->i_d.di_version = XFS_DINODE_VERSION_2;
+ INT_SET(dip->di_core.di_version, ARCH_CONVERT, XFS_DINODE_VERSION_2);
+ ip->i_d.di_onlink = 0;
+ INT_ZERO(dip->di_core.di_onlink, ARCH_CONVERT);
+ memset(&(ip->i_d.di_pad[0]), 0, sizeof(ip->i_d.di_pad));
+ memset(&(dip->di_core.di_pad[0]), 0,
+ sizeof(dip->di_core.di_pad));
+ ASSERT(ip->i_d.di_projid == 0);
+ }
+ }
+
+ if (xfs_iflush_fork(ip, dip, iip, XFS_DATA_FORK, bp) == EFSCORRUPTED) {
+ goto corrupt_out;
+ }
+
+ if (XFS_IFORK_Q(ip)) {
+ /*
+ * The only error from xfs_iflush_fork is on the data fork.
+ */
+ (void) xfs_iflush_fork(ip, dip, iip, XFS_ATTR_FORK, bp);
+ }
+ xfs_inobp_check(mp, bp);
+
+ /*
+ * We've recorded everything logged in the inode, so we'd
+ * like to clear the ilf_fields bits so we don't log and
+ * flush things unnecessarily. However, we can't stop
+ * logging all this information until the data we've copied
+ * into the disk buffer is written to disk. If we did we might
+ * overwrite the copy of the inode in the log with all the
+ * data after re-logging only part of it, and in the face of
+ * a crash we wouldn't have all the data we need to recover.
+ *
+ * What we do is move the bits to the ili_last_fields field.
+ * When logging the inode, these bits are moved back to the
+ * ilf_fields field. In the xfs_iflush_done() routine we
+ * clear ili_last_fields, since we know that the information
+ * those bits represent is permanently on disk. As long as
+ * the flush completes before the inode is logged again, then
+ * both ilf_fields and ili_last_fields will be cleared.
+ *
+ * We can play with the ilf_fields bits here, because the inode
+ * lock must be held exclusively in order to set bits there
+ * and the flush lock protects the ili_last_fields bits.
+ * Set ili_logged so the flush done
+ * routine can tell whether or not to look in the AIL.
+ * Also, store the current LSN of the inode so that we can tell
+ * whether the item has moved in the AIL from xfs_iflush_done().
+ * In order to read the lsn we need the AIL lock, because
+ * it is a 64 bit value that cannot be read atomically.
+ */
+ if (iip != NULL && iip->ili_format.ilf_fields != 0) {
+ iip->ili_last_fields = iip->ili_format.ilf_fields;
+ iip->ili_format.ilf_fields = 0;
+ iip->ili_logged = 1;
+
+ ASSERT(sizeof(xfs_lsn_t) == 8); /* don't lock if it shrinks */
+ AIL_LOCK(mp,s);
+ iip->ili_flush_lsn = iip->ili_item.li_lsn;
+ AIL_UNLOCK(mp, s);
+
+ /*
+ * Attach the function xfs_iflush_done to the inode's
+ * buffer. This will remove the inode from the AIL
+ * and unlock the inode's flush lock when the inode is
+ * completely written to disk.
+ */
+ xfs_buf_attach_iodone(bp, (void(*)(xfs_buf_t*,xfs_log_item_t*))
+ xfs_iflush_done, (xfs_log_item_t *)iip);
+
+ ASSERT(XFS_BUF_FSPRIVATE(bp, void *) != NULL);
+ ASSERT(XFS_BUF_IODONE_FUNC(bp) != NULL);
+ } else {
+ /*
+ * We're flushing an inode which is not in the AIL and has
+ * not been logged but has i_update_core set. For this
+ * case we can use a B_DELWRI flush and immediately drop
+ * the inode flush lock because we can avoid the whole
+ * AIL state thing. It's OK to drop the flush lock now,
+ * because we've already locked the buffer and to do anything
+ * you really need both.
+ */
+ if (iip != NULL) {
+ ASSERT(iip->ili_logged == 0);
+ ASSERT(iip->ili_last_fields == 0);
+ ASSERT((iip->ili_item.li_flags & XFS_LI_IN_AIL) == 0);
+ }
+ xfs_ifunlock(ip);
+ }
+
+ return 0;
+
+corrupt_out:
+ return XFS_ERROR(EFSCORRUPTED);
+}
+
+
+/*
+ * Flush all inactive inodes in mp. Return true if no user references
+ * were found, false otherwise.
+ */
+int
+xfs_iflush_all(
+ xfs_mount_t *mp,
+ int flag)
+{
+ int busy;
+ int done;
+ int purged;
+ xfs_inode_t *ip;
+ vmap_t vmap;
+ vnode_t *vp;
+
+ busy = done = 0;
+ while (!done) {
+ purged = 0;
+ XFS_MOUNT_ILOCK(mp);
+ ip = mp->m_inodes;
+ if (ip == NULL) {
+ break;
+ }
+ do {
+ /* Make sure we skip markers inserted by sync */
+ if (ip->i_mount == NULL) {
+ ip = ip->i_mnext;
+ continue;
+ }
+
+ /*
+ * It's up to our caller to purge the root
+ * and quota vnodes later.
+ */
+ vp = XFS_ITOV_NULL(ip);
+
+ if (!vp) {
+ XFS_MOUNT_IUNLOCK(mp);
+ xfs_finish_reclaim(ip, 0, XFS_IFLUSH_ASYNC);
+ purged = 1;
+ break;
+ }
+
+ if (vn_count(vp) != 0) {
+ if (vn_count(vp) == 1 &&
+ (ip == mp->m_rootip ||
+ (mp->m_quotainfo &&
+ (ip->i_ino == mp->m_sb.sb_uquotino ||
+ ip->i_ino == mp->m_sb.sb_gquotino)))) {
+
+ ip = ip->i_mnext;
+ continue;
+ }
+ if (!(flag & XFS_FLUSH_ALL)) {
+ busy = 1;
+ done = 1;
+ break;
+ }
+ /*
+ * Ignore busy inodes but continue flushing
+ * others.
+ */
+ ip = ip->i_mnext;
+ continue;
+ }
+ /*
+ * Sample vp mapping while holding mp locked on MP
+ * systems, so we don't purge a reclaimed or
+ * nonexistent vnode. We break from the loop
+ * since we know that we modify
+ * it by pulling ourselves from it in xfs_reclaim()
+ * called via vn_purge() below. Set ip to the next
+ * entry in the list anyway so we'll know below
+ * whether we reached the end or not.
+ */
+ VMAP(vp, vmap);
+ XFS_MOUNT_IUNLOCK(mp);
+
+ vn_purge(vp, &vmap);
+
+ purged = 1;
+ break;
+ } while (ip != mp->m_inodes);
+ /*
+ * We need to distinguish between when we exit the loop
+ * after a purge and when we simply hit the end of the
+ * list. We can't use the (ip == mp->m_inodes) test,
+ * because when we purge an inode at the start of the list
+ * the next inode on the list becomes mp->m_inodes. That
+ * would cause such a test to bail out early. The purged
+ * variable tells us how we got out of the loop.
+ */
+ if (!purged) {
+ done = 1;
+ }
+ }
+ XFS_MOUNT_IUNLOCK(mp);
+ return !busy;
+}
+
+
+/*
+ * xfs_iaccess: check accessibility of inode for mode.
+ */
+int
+xfs_iaccess(
+ xfs_inode_t *ip,
+ mode_t mode,
+ cred_t *cr)
+{
+ int error;
+ mode_t orgmode = mode;
+ struct inode *inode = LINVFS_GET_IP(XFS_ITOV(ip));
+
+ if (mode & S_IWUSR) {
+ umode_t imode = inode->i_mode;
+
+ if (IS_RDONLY(inode) &&
+ (S_ISREG(imode) || S_ISDIR(imode) || S_ISLNK(imode)))
+ return XFS_ERROR(EROFS);
+
+ if (IS_IMMUTABLE(inode))
+ return XFS_ERROR(EACCES);
+ }
+
+ /*
+ * If there's an Access Control List it's used instead of
+ * the mode bits.
+ */
+ if ((error = _ACL_XFS_IACCESS(ip, mode, cr)) != -1)
+ return error ? XFS_ERROR(error) : 0;
+
+ if (current_fsuid(cr) != ip->i_d.di_uid) {
+ mode >>= 3;
+ if (!in_group_p((gid_t)ip->i_d.di_gid))
+ mode >>= 3;
+ }
+
+ /*
+ * If the DACs are ok we don't need any capability check.
+ */
+ if ((ip->i_d.di_mode & mode) == mode)
+ return 0;
+ /*
+ * Read/write DACs are always overridable.
+ * Executable DACs are overridable if at least one exec bit is set.
+ */
+ if (!(orgmode & S_IXUSR) ||
+ (inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode))
+ if (capable_cred(cr, CAP_DAC_OVERRIDE))
+ return 0;
+
+ if ((orgmode == S_IRUSR) ||
+ (S_ISDIR(inode->i_mode) && (!(orgmode & S_IWUSR)))) {
+ if (capable_cred(cr, CAP_DAC_READ_SEARCH))
+ return 0;
+#ifdef NOISE
+ cmn_err(CE_NOTE, "Ick: mode=%o, orgmode=%o", mode, orgmode);
+#endif /* NOISE */
+ return XFS_ERROR(EACCES);
+ }
+ return XFS_ERROR(EACCES);
+}
+
+/*
+ * xfs_iroundup: round up argument to next power of two
+ */
+uint
+xfs_iroundup(
+ uint v)
+{
+ int i;
+ uint m;
+
+ if ((v & (v - 1)) == 0)
+ return v;
+ ASSERT((v & 0x80000000) == 0);
+ if ((v & (v + 1)) == 0)
+ return v + 1;
+ for (i = 0, m = 1; i < 31; i++, m <<= 1) {
+ if (v & m)
+ continue;
+ v |= m;
+ if ((v & (v + 1)) == 0)
+ return v + 1;
+ }
+ ASSERT(0);
+ return( 0 );
+}
+
+/*
+ * Change the requested timestamp in the given inode.
+ * We don't lock across timestamp updates, and we don't log them but
+ * we do record the fact that there is dirty information in core.
+ *
+ * NOTE -- callers MUST combine XFS_ICHGTIME_MOD or XFS_ICHGTIME_CHG
+ * with XFS_ICHGTIME_ACC to be sure that access time
+ * update will take. Calling first with XFS_ICHGTIME_ACC
+ * and then XFS_ICHGTIME_MOD may fail to modify the access
+ * timestamp if the filesystem is mounted noacctm.
+ */
+void
+xfs_ichgtime(xfs_inode_t *ip,
+ int flags)
+{
+ timespec_t tv;
+ vnode_t *vp = XFS_ITOV(ip);
+ struct inode *inode = LINVFS_GET_IP(vp);
+
+ /*
+ * We're not supposed to change timestamps in readonly-mounted
+ * filesystems. Throw it away if anyone asks us.
+ */
+ if (unlikely(vp->v_vfsp->vfs_flag & VFS_RDONLY))
+ return;
+
+ /*
+ * Don't update access timestamps on reads if mounted "noatime"
+ * Throw it away if anyone asks us.
+ */
+ if ((ip->i_mount->m_flags & XFS_MOUNT_NOATIME || IS_NOATIME(inode)) &&
+ ((flags & (XFS_ICHGTIME_ACC|XFS_ICHGTIME_MOD|XFS_ICHGTIME_CHG))
+ == XFS_ICHGTIME_ACC))
+ return;
+
+ nanotime(&tv);
+ if (flags & XFS_ICHGTIME_MOD) {
+ VN_MTIMESET(vp, &tv);
+ ip->i_d.di_mtime.t_sec = (__int32_t)tv.tv_sec;
+ ip->i_d.di_mtime.t_nsec = (__int32_t)tv.tv_nsec;
+ }
+ if (flags & XFS_ICHGTIME_ACC) {
+ VN_ATIMESET(vp, &tv);
+ ip->i_d.di_atime.t_sec = (__int32_t)tv.tv_sec;
+ ip->i_d.di_atime.t_nsec = (__int32_t)tv.tv_nsec;
+ }
+ if (flags & XFS_ICHGTIME_CHG) {
+ VN_CTIMESET(vp, &tv);
+ ip->i_d.di_ctime.t_sec = (__int32_t)tv.tv_sec;
+ ip->i_d.di_ctime.t_nsec = (__int32_t)tv.tv_nsec;
+ }
+
+ /*
+ * We update the i_update_core field _after_ changing
+ * the timestamps in order to coordinate properly with
+ * xfs_iflush() so that we don't lose timestamp updates.
+ * This keeps us from having to hold the inode lock
+ * while doing this. We use the SYNCHRONIZE macro to
+ * ensure that the compiler does not reorder the update
+ * of i_update_core above the timestamp updates above.
+ */
+ SYNCHRONIZE();
+ ip->i_update_core = 1;
+ if (!(inode->i_state & I_LOCK))
+ mark_inode_dirty_sync(inode);
+}
+
+#ifdef XFS_ILOCK_TRACE
+ktrace_t *xfs_ilock_trace_buf;
+
+void
+xfs_ilock_trace(xfs_inode_t *ip, int lock, unsigned int lockflags, inst_t *ra)
+{
+ ktrace_enter(ip->i_lock_trace,
+ (void *)ip,
+ (void *)(unsigned long)lock, /* 1 = LOCK, 3=UNLOCK, etc */
+ (void *)(unsigned long)lockflags, /* XFS_ILOCK_EXCL etc */
+ (void *)ra, /* caller of ilock */
+ (void *)(unsigned long)current_cpu(),
+ (void *)(unsigned long)current_pid(),
+ NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
+}
+#endif
diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h
new file mode 100644
index 00000000000000..97bd24ba4198f2
--- /dev/null
+++ b/fs/xfs/xfs_inode.h
@@ -0,0 +1,562 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_INODE_H__
+#define __XFS_INODE_H__
+
+/*
+ * File incore extent information, present for each of data & attr forks.
+ */
+#define XFS_INLINE_EXTS 2
+#define XFS_INLINE_DATA 32
+typedef struct xfs_ifork {
+ int if_bytes; /* bytes in if_u1 */
+ int if_real_bytes; /* bytes allocated in if_u1 */
+ xfs_bmbt_block_t *if_broot; /* file's incore btree root */
+ short if_broot_bytes; /* bytes allocated for root */
+ unsigned char if_flags; /* per-fork flags */
+ unsigned char if_ext_max; /* max # of extent records */
+ xfs_extnum_t if_lastex; /* last if_extents used */
+ union {
+ xfs_bmbt_rec_t *if_extents; /* linear map file exts */
+ char *if_data; /* inline file data */
+ } if_u1;
+ union {
+ xfs_bmbt_rec_t if_inline_ext[XFS_INLINE_EXTS];
+ /* very small file extents */
+ char if_inline_data[XFS_INLINE_DATA];
+ /* very small file data */
+ xfs_dev_t if_rdev; /* dev number if special */
+ uuid_t if_uuid; /* mount point value */
+ } if_u2;
+} xfs_ifork_t;
+
+/*
+ * Flags for xfs_ichgtime().
+ */
+#define XFS_ICHGTIME_MOD 0x1 /* data fork modification timestamp */
+#define XFS_ICHGTIME_ACC 0x2 /* data fork access timestamp */
+#define XFS_ICHGTIME_CHG 0x4 /* inode field change timestamp */
+
+/*
+ * Per-fork incore inode flags.
+ */
+#define XFS_IFINLINE 0x0001 /* Inline data is read in */
+#define XFS_IFEXTENTS 0x0002 /* All extent pointers are read in */
+#define XFS_IFBROOT 0x0004 /* i_broot points to the bmap b-tree root */
+
+/*
+ * Flags for xfs_imap() and xfs_dilocate().
+ */
+#define XFS_IMAP_LOOKUP 0x1
+
+/*
+ * Maximum number of extent pointers in if_u1.if_extents.
+ */
+#define XFS_MAX_INCORE_EXTENTS 32768
+
+
+#ifdef __KERNEL__
+struct bhv_desc;
+struct cred;
+struct ktrace;
+struct vnode;
+struct xfs_buf;
+struct xfs_bmap_free;
+struct xfs_bmbt_irec;
+struct xfs_bmbt_block;
+struct xfs_inode;
+struct xfs_inode_log_item;
+struct xfs_mount;
+struct xfs_trans;
+struct xfs_dquot;
+
+#if defined(XFS_ILOCK_TRACE)
+#define XFS_ILOCK_KTRACE_SIZE 32
+extern ktrace_t *xfs_ilock_trace_buf;
+extern void xfs_ilock_trace(struct xfs_inode *, int, unsigned int, inst_t *);
+#else
+#define xfs_ilock_trace(i,n,f,ra)
+#endif
+
+/*
+ * This structure is used to communicate which extents of a file
+ * were holes when a write started from xfs_write_file() to
+ * xfs_strat_read(). This is necessary so that we can know which
+ * blocks need to be zeroed when they are read in in xfs_strat_read()
+ * if they weren\'t allocated when the buffer given to xfs_strat_read()
+ * was mapped.
+ *
+ * We keep a list of these attached to the inode. The list is
+ * protected by the inode lock and the fact that the io lock is
+ * held exclusively by writers.
+ */
+typedef struct xfs_gap {
+ struct xfs_gap *xg_next;
+ xfs_fileoff_t xg_offset_fsb;
+ xfs_extlen_t xg_count_fsb;
+} xfs_gap_t;
+
+typedef struct dm_attrs_s {
+ __uint32_t da_dmevmask; /* DMIG event mask */
+ __uint16_t da_dmstate; /* DMIG state info */
+ __uint16_t da_pad; /* DMIG extra padding */
+} dm_attrs_t;
+
+typedef struct xfs_iocore {
+ void *io_obj; /* pointer to container
+ * inode or dcxvn structure */
+ struct xfs_mount *io_mount; /* fs mount struct ptr */
+#ifdef DEBUG
+ mrlock_t *io_lock; /* inode IO lock */
+ mrlock_t *io_iolock; /* inode IO lock */
+#endif
+
+ /* I/O state */
+ xfs_fsize_t io_new_size; /* sz when write completes */
+
+ /* Miscellaneous state. */
+ unsigned int io_flags; /* IO related flags */
+
+ /* DMAPI state */
+ dm_attrs_t io_dmattrs;
+
+} xfs_iocore_t;
+
+#define io_dmevmask io_dmattrs.da_dmevmask
+#define io_dmstate io_dmattrs.da_dmstate
+
+#define XFS_IO_INODE(io) ((xfs_inode_t *) ((io)->io_obj))
+#define XFS_IO_DCXVN(io) ((dcxvn_t *) ((io)->io_obj))
+
+/*
+ * Flags in the flags field
+ */
+
+#define XFS_IOCORE_RT 0x1
+
+/*
+ * xfs_iocore prototypes
+ */
+
+extern void xfs_iocore_inode_init(struct xfs_inode *);
+extern void xfs_iocore_inode_reinit(struct xfs_inode *);
+
+
+/*
+ * This is the type used in the xfs inode hash table.
+ * An array of these is allocated for each mounted
+ * file system to hash the inodes for that file system.
+ */
+typedef struct xfs_ihash {
+ struct xfs_inode *ih_next;
+ rwlock_t ih_lock;
+ uint ih_version;
+} xfs_ihash_t;
+
+/*
+ * Inode hashing and hash bucket locking.
+ */
+#define XFS_BUCKETS(mp) (37*(mp)->m_sb.sb_agcount-1)
+#define XFS_IHASH(mp,ino) ((mp)->m_ihash + (((uint)(ino)) % (mp)->m_ihsize))
+
+/*
+ * This is the xfs inode cluster hash. This hash is used by xfs_iflush to
+ * find inodes that share a cluster and can be flushed to disk at the same
+ * time.
+ */
+
+typedef struct xfs_chashlist {
+ struct xfs_chashlist *chl_next;
+ struct xfs_inode *chl_ip;
+ xfs_daddr_t chl_blkno; /* starting block number of
+ * the cluster */
+ struct xfs_buf *chl_buf; /* the inode buffer */
+} xfs_chashlist_t;
+
+typedef struct xfs_chash {
+ xfs_chashlist_t *ch_list;
+ lock_t ch_lock;
+} xfs_chash_t;
+
+
+/*
+ * This is the xfs in-core inode structure.
+ * Most of the on-disk inode is embedded in the i_d field.
+ *
+ * The extent pointers/inline file space, however, are managed
+ * separately. The memory for this information is pointed to by
+ * the if_u1 unions depending on the type of the data.
+ * This is used to linearize the array of extents for fast in-core
+ * access. This is used until the file's number of extents
+ * surpasses XFS_MAX_INCORE_EXTENTS, at which point all extent pointers
+ * are accessed through the buffer cache.
+ *
+ * Other state kept in the in-core inode is used for identification,
+ * locking, transactional updating, etc of the inode.
+ *
+ * Generally, we do not want to hold the i_rlock while holding the
+ * i_ilock. Hierarchy is i_iolock followed by i_rlock.
+ *
+ * xfs_iptr_t contains all the inode fields upto and including the
+ * i_mnext and i_mprev fields, it is used as a marker in the inode
+ * chain off the mount structure by xfs_sync calls.
+ */
+
+typedef struct {
+ struct xfs_ihash *ip_hash; /* pointer to hash header */
+ struct xfs_inode *ip_next; /* inode hash link forw */
+ struct xfs_inode *ip_mnext; /* next inode in mount list */
+ struct xfs_inode *ip_mprev; /* ptr to prev inode */
+ struct xfs_inode **ip_prevp; /* ptr to prev i_next */
+ struct xfs_mount *ip_mount; /* fs mount struct ptr */
+} xfs_iptr_t;
+
+typedef struct xfs_inode {
+ /* Inode linking and identification information. */
+ struct xfs_ihash *i_hash; /* pointer to hash header */
+ struct xfs_inode *i_next; /* inode hash link forw */
+ struct xfs_inode *i_mnext; /* next inode in mount list */
+ struct xfs_inode *i_mprev; /* ptr to prev inode */
+ struct xfs_inode **i_prevp; /* ptr to prev i_next */
+ struct xfs_mount *i_mount; /* fs mount struct ptr */
+ struct list_head i_reclaim; /* reclaim list */
+ struct bhv_desc i_bhv_desc; /* inode behavior descriptor*/
+ struct xfs_dquot *i_udquot; /* user dquot */
+ struct xfs_dquot *i_gdquot; /* group dquot */
+
+ /* Inode location stuff */
+ xfs_ino_t i_ino; /* inode number (agno/agino)*/
+ xfs_daddr_t i_blkno; /* blkno of inode buffer */
+ ushort i_len; /* len of inode buffer */
+ ushort i_boffset; /* off of inode in buffer */
+
+ /* Extent information. */
+ xfs_ifork_t *i_afp; /* attribute fork pointer */
+ xfs_ifork_t i_df; /* data fork */
+
+ /* Transaction and locking information. */
+ struct xfs_trans *i_transp; /* ptr to owning transaction*/
+ struct xfs_inode_log_item *i_itemp; /* logging information */
+ mrlock_t i_lock; /* inode lock */
+ mrlock_t i_iolock; /* inode IO lock */
+ sema_t i_flock; /* inode flush lock */
+ atomic_t i_pincount; /* inode pin count */
+ wait_queue_head_t i_ipin_wait; /* inode pinning wait queue */
+#ifdef HAVE_REFCACHE
+ struct xfs_inode **i_refcache; /* ptr to entry in ref cache */
+ struct xfs_inode *i_release; /* inode to unref */
+#endif
+ /* I/O state */
+ xfs_iocore_t i_iocore; /* I/O core */
+
+ /* Miscellaneous state. */
+ unsigned short i_flags; /* see defined flags below */
+ unsigned char i_update_core; /* timestamps/size is dirty */
+ unsigned char i_update_size; /* di_size field is dirty */
+ unsigned int i_gen; /* generation count */
+ unsigned int i_delayed_blks; /* count of delay alloc blks */
+
+ xfs_dinode_core_t i_d; /* most of ondisk inode */
+ xfs_chashlist_t *i_chash; /* cluster hash list header */
+ struct xfs_inode *i_cnext; /* cluster hash link forward */
+ struct xfs_inode *i_cprev; /* cluster hash link backward */
+
+ /* Trace buffers per inode. */
+#ifdef XFS_BMAP_TRACE
+ struct ktrace *i_xtrace; /* inode extent list trace */
+#endif
+#ifdef XFS_BMBT_TRACE
+ struct ktrace *i_btrace; /* inode bmap btree trace */
+#endif
+#ifdef XFS_RW_TRACE
+ struct ktrace *i_rwtrace; /* inode read/write trace */
+#endif
+#ifdef XFS_ILOCK_TRACE
+ struct ktrace *i_lock_trace; /* inode lock/unlock trace */
+#endif
+#ifdef XFS_DIR2_TRACE
+ struct ktrace *i_dir_trace; /* inode directory trace */
+#endif
+} xfs_inode_t;
+
+#endif /* __KERNEL__ */
+
+
+/*
+ * Fork handling.
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_IFORK_PTR)
+xfs_ifork_t *xfs_ifork_ptr(xfs_inode_t *ip, int w);
+#define XFS_IFORK_PTR(ip,w) xfs_ifork_ptr(ip,w)
+#else
+#define XFS_IFORK_PTR(ip,w) ((w) == XFS_DATA_FORK ? &(ip)->i_df : (ip)->i_afp)
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_IFORK_Q)
+int xfs_ifork_q(xfs_inode_t *ip);
+#define XFS_IFORK_Q(ip) xfs_ifork_q(ip)
+#else
+#define XFS_IFORK_Q(ip) XFS_CFORK_Q(&(ip)->i_d)
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_IFORK_DSIZE)
+int xfs_ifork_dsize(xfs_inode_t *ip);
+#define XFS_IFORK_DSIZE(ip) xfs_ifork_dsize(ip)
+#else
+#define XFS_IFORK_DSIZE(ip) XFS_CFORK_DSIZE(&ip->i_d, ip->i_mount)
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_IFORK_ASIZE)
+int xfs_ifork_asize(xfs_inode_t *ip);
+#define XFS_IFORK_ASIZE(ip) xfs_ifork_asize(ip)
+#else
+#define XFS_IFORK_ASIZE(ip) XFS_CFORK_ASIZE(&ip->i_d, ip->i_mount)
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_IFORK_SIZE)
+int xfs_ifork_size(xfs_inode_t *ip, int w);
+#define XFS_IFORK_SIZE(ip,w) xfs_ifork_size(ip,w)
+#else
+#define XFS_IFORK_SIZE(ip,w) XFS_CFORK_SIZE(&ip->i_d, ip->i_mount, w)
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_IFORK_FORMAT)
+int xfs_ifork_format(xfs_inode_t *ip, int w);
+#define XFS_IFORK_FORMAT(ip,w) xfs_ifork_format(ip,w)
+#else
+#define XFS_IFORK_FORMAT(ip,w) XFS_CFORK_FORMAT(&ip->i_d, w)
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_IFORK_FMT_SET)
+void xfs_ifork_fmt_set(xfs_inode_t *ip, int w, int n);
+#define XFS_IFORK_FMT_SET(ip,w,n) xfs_ifork_fmt_set(ip,w,n)
+#else
+#define XFS_IFORK_FMT_SET(ip,w,n) XFS_CFORK_FMT_SET(&ip->i_d, w, n)
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_IFORK_NEXTENTS)
+int xfs_ifork_nextents(xfs_inode_t *ip, int w);
+#define XFS_IFORK_NEXTENTS(ip,w) xfs_ifork_nextents(ip,w)
+#else
+#define XFS_IFORK_NEXTENTS(ip,w) XFS_CFORK_NEXTENTS(&ip->i_d, w)
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_IFORK_NEXT_SET)
+void xfs_ifork_next_set(xfs_inode_t *ip, int w, int n);
+#define XFS_IFORK_NEXT_SET(ip,w,n) xfs_ifork_next_set(ip,w,n)
+#else
+#define XFS_IFORK_NEXT_SET(ip,w,n) XFS_CFORK_NEXT_SET(&ip->i_d, w, n)
+#endif
+
+
+#ifdef __KERNEL__
+
+/*
+ * In-core inode flags.
+ */
+#define XFS_IGRIO 0x0001 /* inode used for guaranteed rate i/o */
+#define XFS_IUIOSZ 0x0002 /* inode i/o sizes have been explicitly set */
+#define XFS_IQUIESCE 0x0004 /* we have started quiescing for this inode */
+#define XFS_IRECLAIM 0x0008 /* we have started reclaiming this inode */
+#define XFS_ISTALE 0x0010 /* inode has been staled */
+#define XFS_IRECLAIMABLE 0x0020 /* inode can be reclaimed */
+#define XFS_INEW 0x0040
+
+/*
+ * Flags for inode locking.
+ */
+#define XFS_IOLOCK_EXCL 0x001
+#define XFS_IOLOCK_SHARED 0x002
+#define XFS_ILOCK_EXCL 0x004
+#define XFS_ILOCK_SHARED 0x008
+#define XFS_IUNLOCK_NONOTIFY 0x010
+#define XFS_EXTENT_TOKEN_RD 0x040
+#define XFS_SIZE_TOKEN_RD 0x080
+#define XFS_EXTSIZE_RD (XFS_EXTENT_TOKEN_RD|XFS_SIZE_TOKEN_RD)
+#define XFS_WILLLEND 0x100 /* Always acquire tokens for lending */
+#define XFS_EXTENT_TOKEN_WR (XFS_EXTENT_TOKEN_RD | XFS_WILLLEND)
+#define XFS_SIZE_TOKEN_WR (XFS_SIZE_TOKEN_RD | XFS_WILLLEND)
+#define XFS_EXTSIZE_WR (XFS_EXTSIZE_RD | XFS_WILLLEND)
+
+
+#define XFS_LOCK_MASK \
+ (XFS_IOLOCK_EXCL | XFS_IOLOCK_SHARED | XFS_ILOCK_EXCL | \
+ XFS_ILOCK_SHARED | XFS_EXTENT_TOKEN_RD | XFS_SIZE_TOKEN_RD | \
+ XFS_WILLLEND)
+
+/*
+ * Flags for xfs_iflush()
+ */
+#define XFS_IFLUSH_DELWRI_ELSE_SYNC 1
+#define XFS_IFLUSH_DELWRI_ELSE_ASYNC 2
+#define XFS_IFLUSH_SYNC 3
+#define XFS_IFLUSH_ASYNC 4
+#define XFS_IFLUSH_DELWRI 5
+
+/*
+ * Flags for xfs_iflush_all.
+ */
+#define XFS_FLUSH_ALL 0x1
+
+/*
+ * Flags for xfs_itruncate_start().
+ */
+#define XFS_ITRUNC_DEFINITE 0x1
+#define XFS_ITRUNC_MAYBE 0x2
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_ITOV)
+struct vnode *xfs_itov(xfs_inode_t *ip);
+#define XFS_ITOV(ip) xfs_itov(ip)
+#else
+#define XFS_ITOV(ip) BHV_TO_VNODE(XFS_ITOBHV(ip))
+#endif
+#define XFS_ITOV_NULL(ip) BHV_TO_VNODE_NULL(XFS_ITOBHV(ip))
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_ITOBHV)
+struct bhv_desc *xfs_itobhv(xfs_inode_t *ip);
+#define XFS_ITOBHV(ip) xfs_itobhv(ip)
+#else
+#define XFS_ITOBHV(ip) ((struct bhv_desc *)(&((ip)->i_bhv_desc)))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BHVTOI)
+xfs_inode_t *xfs_bhvtoi(struct bhv_desc *bhvp);
+#define XFS_BHVTOI(bhvp) xfs_bhvtoi(bhvp)
+#else
+#define XFS_BHVTOI(bhvp) \
+ ((xfs_inode_t *)((char *)(bhvp) - \
+ (char *)&(((xfs_inode_t *)0)->i_bhv_desc)))
+#endif
+
+#define BHV_IS_XFS(bdp) (BHV_OPS(bdp) == &xfs_vnodeops)
+
+/*
+ * Pick the inode cluster hash bucket
+ * (m_chash is the same size as m_ihash)
+ */
+#define XFS_CHASH(mp,blk) ((mp)->m_chash + (((uint)blk) % (mp)->m_chsize))
+
+/*
+ * For multiple groups support: if S_ISGID bit is set in the parent
+ * directory, group of new file is set to that of the parent, and
+ * new subdirectory gets S_ISGID bit from parent.
+ */
+#define XFS_INHERIT_GID(pip, vfsp) \
+ (((vfsp)->vfs_flag & VFS_GRPID) || ((pip)->i_d.di_mode & S_ISGID))
+
+/*
+ * xfs_iget.c prototypes.
+ */
+
+#define IGET_CREATE 1
+
+void xfs_ihash_init(struct xfs_mount *);
+void xfs_ihash_free(struct xfs_mount *);
+void xfs_chash_init(struct xfs_mount *);
+void xfs_chash_free(struct xfs_mount *);
+xfs_inode_t *xfs_inode_incore(struct xfs_mount *, xfs_ino_t,
+ struct xfs_trans *);
+void xfs_inode_lock_init(xfs_inode_t *, struct vnode *);
+int xfs_iget(struct xfs_mount *, struct xfs_trans *, xfs_ino_t,
+ uint, uint, xfs_inode_t **, xfs_daddr_t);
+void xfs_iput(xfs_inode_t *, uint);
+void xfs_iput_new(xfs_inode_t *, uint);
+void xfs_ilock(xfs_inode_t *, uint);
+int xfs_ilock_nowait(xfs_inode_t *, uint);
+void xfs_iunlock(xfs_inode_t *, uint);
+void xfs_ilock_demote(xfs_inode_t *, uint);
+void xfs_iflock(xfs_inode_t *);
+int xfs_iflock_nowait(xfs_inode_t *);
+uint xfs_ilock_map_shared(xfs_inode_t *);
+void xfs_iunlock_map_shared(xfs_inode_t *, uint);
+void xfs_ifunlock(xfs_inode_t *);
+void xfs_ireclaim(xfs_inode_t *);
+int xfs_finish_reclaim(xfs_inode_t *, int, int);
+int xfs_finish_reclaim_all(struct xfs_mount *, int);
+
+/*
+ * xfs_inode.c prototypes.
+ */
+int xfs_inotobp(struct xfs_mount *, struct xfs_trans *, xfs_ino_t,
+ xfs_dinode_t **, struct xfs_buf **, int *);
+int xfs_itobp(struct xfs_mount *, struct xfs_trans *,
+ xfs_inode_t *, xfs_dinode_t **, struct xfs_buf **,
+ xfs_daddr_t);
+int xfs_iread(struct xfs_mount *, struct xfs_trans *, xfs_ino_t,
+ xfs_inode_t **, xfs_daddr_t);
+int xfs_iread_extents(struct xfs_trans *, xfs_inode_t *, int);
+int xfs_ialloc(struct xfs_trans *, xfs_inode_t *, mode_t, nlink_t,
+ xfs_dev_t, struct cred *, xfs_prid_t, int,
+ struct xfs_buf **, boolean_t *, xfs_inode_t **);
+void xfs_xlate_dinode_core(xfs_caddr_t, struct xfs_dinode_core *,
+ int, xfs_arch_t);
+uint xfs_dic2xflags(struct xfs_dinode_core *, xfs_arch_t);
+int xfs_ifree(struct xfs_trans *, xfs_inode_t *,
+ struct xfs_bmap_free *);
+void xfs_itruncate_start(xfs_inode_t *, uint, xfs_fsize_t);
+int xfs_itruncate_finish(struct xfs_trans **, xfs_inode_t *,
+ xfs_fsize_t, int, int);
+int xfs_iunlink(struct xfs_trans *, xfs_inode_t *);
+int xfs_igrow_start(xfs_inode_t *, xfs_fsize_t, struct cred *);
+void xfs_igrow_finish(struct xfs_trans *, xfs_inode_t *,
+ xfs_fsize_t, int);
+
+void xfs_idestroy_fork(xfs_inode_t *, int);
+void xfs_idestroy(xfs_inode_t *);
+void xfs_idata_realloc(xfs_inode_t *, int, int);
+void xfs_iextract(xfs_inode_t *);
+void xfs_iext_realloc(xfs_inode_t *, int, int);
+void xfs_iroot_realloc(xfs_inode_t *, int, int);
+void xfs_ipin(xfs_inode_t *);
+void xfs_iunpin(xfs_inode_t *);
+int xfs_iextents_copy(xfs_inode_t *, xfs_bmbt_rec_t *, int);
+int xfs_iflush(xfs_inode_t *, uint);
+int xfs_iflush_all(struct xfs_mount *, int);
+int xfs_iaccess(xfs_inode_t *, mode_t, cred_t *);
+uint xfs_iroundup(uint);
+void xfs_ichgtime(xfs_inode_t *, int);
+xfs_fsize_t xfs_file_last_byte(xfs_inode_t *);
+void xfs_lock_inodes(xfs_inode_t **, int, int, uint);
+
+#define xfs_ipincount(ip) ((unsigned int) atomic_read(&ip->i_pincount))
+
+#ifdef DEBUG
+void xfs_isize_check(struct xfs_mount *, xfs_inode_t *, xfs_fsize_t);
+#else /* DEBUG */
+#define xfs_isize_check(mp, ip, isize)
+#endif /* DEBUG */
+
+#if defined(DEBUG)
+void xfs_inobp_check(struct xfs_mount *, struct xfs_buf *);
+#else
+#define xfs_inobp_check(mp, bp)
+#endif /* DEBUG */
+
+extern struct kmem_zone *xfs_chashlist_zone;
+extern struct kmem_zone *xfs_ifork_zone;
+extern struct kmem_zone *xfs_inode_zone;
+extern struct kmem_zone *xfs_ili_zone;
+extern struct vnodeops xfs_vnodeops;
+
+#endif /* __KERNEL__ */
+
+#endif /* __XFS_INODE_H__ */
diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c
new file mode 100644
index 00000000000000..2900528c62da29
--- /dev/null
+++ b/fs/xfs/xfs_inode_item.c
@@ -0,0 +1,1092 @@
+/*
+ * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+/*
+ * This file contains the implementation of the xfs_inode_log_item.
+ * It contains the item operations used to manipulate the inode log
+ * items as well as utility routines used by the inode specific
+ * transaction routines.
+ */
+#include "xfs.h"
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_buf_item.h"
+#include "xfs_sb.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_trans_priv.h"
+#include "xfs_ag.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode_item.h"
+#include "xfs_inode.h"
+#include "xfs_rw.h"
+
+
+kmem_zone_t *xfs_ili_zone; /* inode log item zone */
+
+/*
+ * This returns the number of iovecs needed to log the given inode item.
+ *
+ * We need one iovec for the inode log format structure, one for the
+ * inode core, and possibly one for the inode data/extents/b-tree root
+ * and one for the inode attribute data/extents/b-tree root.
+ */
+STATIC uint
+xfs_inode_item_size(
+ xfs_inode_log_item_t *iip)
+{
+ uint nvecs;
+ xfs_inode_t *ip;
+
+ ip = iip->ili_inode;
+ nvecs = 2;
+
+ /*
+ * Only log the data/extents/b-tree root if there is something
+ * left to log.
+ */
+ iip->ili_format.ilf_fields |= XFS_ILOG_CORE;
+
+ switch (ip->i_d.di_format) {
+ case XFS_DINODE_FMT_EXTENTS:
+ iip->ili_format.ilf_fields &=
+ ~(XFS_ILOG_DDATA | XFS_ILOG_DBROOT |
+ XFS_ILOG_DEV | XFS_ILOG_UUID);
+ if ((iip->ili_format.ilf_fields & XFS_ILOG_DEXT) &&
+ (ip->i_d.di_nextents > 0) &&
+ (ip->i_df.if_bytes > 0)) {
+ ASSERT(ip->i_df.if_u1.if_extents != NULL);
+ nvecs++;
+ } else {
+ iip->ili_format.ilf_fields &= ~XFS_ILOG_DEXT;
+ }
+ break;
+
+ case XFS_DINODE_FMT_BTREE:
+ ASSERT(ip->i_df.if_ext_max ==
+ XFS_IFORK_DSIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t));
+ iip->ili_format.ilf_fields &=
+ ~(XFS_ILOG_DDATA | XFS_ILOG_DEXT |
+ XFS_ILOG_DEV | XFS_ILOG_UUID);
+ if ((iip->ili_format.ilf_fields & XFS_ILOG_DBROOT) &&
+ (ip->i_df.if_broot_bytes > 0)) {
+ ASSERT(ip->i_df.if_broot != NULL);
+ nvecs++;
+ } else {
+ ASSERT(!(iip->ili_format.ilf_fields &
+ XFS_ILOG_DBROOT));
+#ifdef XFS_TRANS_DEBUG
+ if (iip->ili_root_size > 0) {
+ ASSERT(iip->ili_root_size ==
+ ip->i_df.if_broot_bytes);
+ ASSERT(memcmp(iip->ili_orig_root,
+ ip->i_df.if_broot,
+ iip->ili_root_size) == 0);
+ } else {
+ ASSERT(ip->i_df.if_broot_bytes == 0);
+ }
+#endif
+ iip->ili_format.ilf_fields &= ~XFS_ILOG_DBROOT;
+ }
+ break;
+
+ case XFS_DINODE_FMT_LOCAL:
+ iip->ili_format.ilf_fields &=
+ ~(XFS_ILOG_DEXT | XFS_ILOG_DBROOT |
+ XFS_ILOG_DEV | XFS_ILOG_UUID);
+ if ((iip->ili_format.ilf_fields & XFS_ILOG_DDATA) &&
+ (ip->i_df.if_bytes > 0)) {
+ ASSERT(ip->i_df.if_u1.if_data != NULL);
+ ASSERT(ip->i_d.di_size > 0);
+ nvecs++;
+ } else {
+ iip->ili_format.ilf_fields &= ~XFS_ILOG_DDATA;
+ }
+ break;
+
+ case XFS_DINODE_FMT_DEV:
+ iip->ili_format.ilf_fields &=
+ ~(XFS_ILOG_DDATA | XFS_ILOG_DBROOT |
+ XFS_ILOG_DEXT | XFS_ILOG_UUID);
+ break;
+
+ case XFS_DINODE_FMT_UUID:
+ iip->ili_format.ilf_fields &=
+ ~(XFS_ILOG_DDATA | XFS_ILOG_DBROOT |
+ XFS_ILOG_DEXT | XFS_ILOG_DEV);
+ break;
+
+ default:
+ ASSERT(0);
+ break;
+ }
+
+ /*
+ * If there are no attributes associated with this file,
+ * then there cannot be anything more to log.
+ * Clear all attribute-related log flags.
+ */
+ if (!XFS_IFORK_Q(ip)) {
+ iip->ili_format.ilf_fields &=
+ ~(XFS_ILOG_ADATA | XFS_ILOG_ABROOT | XFS_ILOG_AEXT);
+ return nvecs;
+ }
+
+ /*
+ * Log any necessary attribute data.
+ */
+ switch (ip->i_d.di_aformat) {
+ case XFS_DINODE_FMT_EXTENTS:
+ iip->ili_format.ilf_fields &=
+ ~(XFS_ILOG_ADATA | XFS_ILOG_ABROOT);
+ if ((iip->ili_format.ilf_fields & XFS_ILOG_AEXT) &&
+ (ip->i_d.di_anextents > 0) &&
+ (ip->i_afp->if_bytes > 0)) {
+ ASSERT(ip->i_afp->if_u1.if_extents != NULL);
+ nvecs++;
+ } else {
+ iip->ili_format.ilf_fields &= ~XFS_ILOG_AEXT;
+ }
+ break;
+
+ case XFS_DINODE_FMT_BTREE:
+ iip->ili_format.ilf_fields &=
+ ~(XFS_ILOG_ADATA | XFS_ILOG_AEXT);
+ if ((iip->ili_format.ilf_fields & XFS_ILOG_ABROOT) &&
+ (ip->i_afp->if_broot_bytes > 0)) {
+ ASSERT(ip->i_afp->if_broot != NULL);
+ nvecs++;
+ } else {
+ iip->ili_format.ilf_fields &= ~XFS_ILOG_ABROOT;
+ }
+ break;
+
+ case XFS_DINODE_FMT_LOCAL:
+ iip->ili_format.ilf_fields &=
+ ~(XFS_ILOG_AEXT | XFS_ILOG_ABROOT);
+ if ((iip->ili_format.ilf_fields & XFS_ILOG_ADATA) &&
+ (ip->i_afp->if_bytes > 0)) {
+ ASSERT(ip->i_afp->if_u1.if_data != NULL);
+ nvecs++;
+ } else {
+ iip->ili_format.ilf_fields &= ~XFS_ILOG_ADATA;
+ }
+ break;
+
+ default:
+ ASSERT(0);
+ break;
+ }
+
+ return nvecs;
+}
+
+/*
+ * This is called to fill in the vector of log iovecs for the
+ * given inode log item. It fills the first item with an inode
+ * log format structure, the second with the on-disk inode structure,
+ * and a possible third and/or fourth with the inode data/extents/b-tree
+ * root and inode attributes data/extents/b-tree root.
+ */
+STATIC void
+xfs_inode_item_format(
+ xfs_inode_log_item_t *iip,
+ xfs_log_iovec_t *log_vector)
+{
+ uint nvecs;
+ xfs_log_iovec_t *vecp;
+ xfs_inode_t *ip;
+ size_t data_bytes;
+ xfs_bmbt_rec_t *ext_buffer;
+ int nrecs;
+ xfs_mount_t *mp;
+
+ ip = iip->ili_inode;
+ vecp = log_vector;
+
+ vecp->i_addr = (xfs_caddr_t)&iip->ili_format;
+ vecp->i_len = sizeof(xfs_inode_log_format_t);
+ vecp++;
+ nvecs = 1;
+
+ /*
+ * Clear i_update_core if the timestamps (or any other
+ * non-transactional modification) need flushing/logging
+ * and we're about to log them with the rest of the core.
+ *
+ * This is the same logic as xfs_iflush() but this code can't
+ * run at the same time as xfs_iflush because we're in commit
+ * processing here and so we have the inode lock held in
+ * exclusive mode. Although it doesn't really matter
+ * for the timestamps if both routines were to grab the
+ * timestamps or not. That would be ok.
+ *
+ * We clear i_update_core before copying out the data.
+ * This is for coordination with our timestamp updates
+ * that don't hold the inode lock. They will always
+ * update the timestamps BEFORE setting i_update_core,
+ * so if we clear i_update_core after they set it we
+ * are guaranteed to see their updates to the timestamps
+ * either here. Likewise, if they set it after we clear it
+ * here, we'll see it either on the next commit of this
+ * inode or the next time the inode gets flushed via
+ * xfs_iflush(). This depends on strongly ordered memory
+ * semantics, but we have that. We use the SYNCHRONIZE
+ * macro to make sure that the compiler does not reorder
+ * the i_update_core access below the data copy below.
+ */
+ if (ip->i_update_core) {
+ ip->i_update_core = 0;
+ SYNCHRONIZE();
+ }
+
+ /*
+ * We don't have to worry about re-ordering here because
+ * the update_size field is protected by the inode lock
+ * and we have that held in exclusive mode.
+ */
+ if (ip->i_update_size)
+ ip->i_update_size = 0;
+
+ vecp->i_addr = (xfs_caddr_t)&ip->i_d;
+ vecp->i_len = sizeof(xfs_dinode_core_t);
+ vecp++;
+ nvecs++;
+ iip->ili_format.ilf_fields |= XFS_ILOG_CORE;
+
+ /*
+ * If this is really an old format inode, then we need to
+ * log it as such. This means that we have to copy the link
+ * count from the new field to the old. We don't have to worry
+ * about the new fields, because nothing trusts them as long as
+ * the old inode version number is there. If the superblock already
+ * has a new version number, then we don't bother converting back.
+ */
+ mp = ip->i_mount;
+ ASSERT(ip->i_d.di_version == XFS_DINODE_VERSION_1 ||
+ XFS_SB_VERSION_HASNLINK(&mp->m_sb));
+ if (ip->i_d.di_version == XFS_DINODE_VERSION_1) {
+ if (!XFS_SB_VERSION_HASNLINK(&mp->m_sb)) {
+ /*
+ * Convert it back.
+ */
+ ASSERT(ip->i_d.di_nlink <= XFS_MAXLINK_1);
+ ip->i_d.di_onlink = ip->i_d.di_nlink;
+ } else {
+ /*
+ * The superblock version has already been bumped,
+ * so just make the conversion to the new inode
+ * format permanent.
+ */
+ ip->i_d.di_version = XFS_DINODE_VERSION_2;
+ ip->i_d.di_onlink = 0;
+ memset(&(ip->i_d.di_pad[0]), 0, sizeof(ip->i_d.di_pad));
+ }
+ }
+
+ switch (ip->i_d.di_format) {
+ case XFS_DINODE_FMT_EXTENTS:
+ ASSERT(!(iip->ili_format.ilf_fields &
+ (XFS_ILOG_DDATA | XFS_ILOG_DBROOT |
+ XFS_ILOG_DEV | XFS_ILOG_UUID)));
+ if (iip->ili_format.ilf_fields & XFS_ILOG_DEXT) {
+ ASSERT(ip->i_df.if_bytes > 0);
+ ASSERT(ip->i_df.if_u1.if_extents != NULL);
+ ASSERT(ip->i_d.di_nextents > 0);
+ ASSERT(iip->ili_extents_buf == NULL);
+ nrecs = ip->i_df.if_bytes /
+ (uint)sizeof(xfs_bmbt_rec_t);
+ ASSERT(nrecs > 0);
+#if ARCH_CONVERT == ARCH_NOCONVERT
+ if (nrecs == ip->i_d.di_nextents) {
+ /*
+ * There are no delayed allocation
+ * extents, so just point to the
+ * real extents array.
+ */
+ vecp->i_addr =
+ (char *)(ip->i_df.if_u1.if_extents);
+ vecp->i_len = ip->i_df.if_bytes;
+ } else
+#endif
+ {
+ /*
+ * There are delayed allocation extents
+ * in the inode, or we need to convert
+ * the extents to on disk format.
+ * Use xfs_iextents_copy()
+ * to copy only the real extents into
+ * a separate buffer. We'll free the
+ * buffer in the unlock routine.
+ */
+ ext_buffer = kmem_alloc(ip->i_df.if_bytes,
+ KM_SLEEP);
+ iip->ili_extents_buf = ext_buffer;
+ vecp->i_addr = (xfs_caddr_t)ext_buffer;
+ vecp->i_len = xfs_iextents_copy(ip, ext_buffer,
+ XFS_DATA_FORK);
+ }
+ ASSERT(vecp->i_len <= ip->i_df.if_bytes);
+ iip->ili_format.ilf_dsize = vecp->i_len;
+ vecp++;
+ nvecs++;
+ }
+ break;
+
+ case XFS_DINODE_FMT_BTREE:
+ ASSERT(!(iip->ili_format.ilf_fields &
+ (XFS_ILOG_DDATA | XFS_ILOG_DEXT |
+ XFS_ILOG_DEV | XFS_ILOG_UUID)));
+ if (iip->ili_format.ilf_fields & XFS_ILOG_DBROOT) {
+ ASSERT(ip->i_df.if_broot_bytes > 0);
+ ASSERT(ip->i_df.if_broot != NULL);
+ vecp->i_addr = (xfs_caddr_t)ip->i_df.if_broot;
+ vecp->i_len = ip->i_df.if_broot_bytes;
+ vecp++;
+ nvecs++;
+ iip->ili_format.ilf_dsize = ip->i_df.if_broot_bytes;
+ }
+ break;
+
+ case XFS_DINODE_FMT_LOCAL:
+ ASSERT(!(iip->ili_format.ilf_fields &
+ (XFS_ILOG_DBROOT | XFS_ILOG_DEXT |
+ XFS_ILOG_DEV | XFS_ILOG_UUID)));
+ if (iip->ili_format.ilf_fields & XFS_ILOG_DDATA) {
+ ASSERT(ip->i_df.if_bytes > 0);
+ ASSERT(ip->i_df.if_u1.if_data != NULL);
+ ASSERT(ip->i_d.di_size > 0);
+
+ vecp->i_addr = (xfs_caddr_t)ip->i_df.if_u1.if_data;
+ /*
+ * Round i_bytes up to a word boundary.
+ * The underlying memory is guaranteed to
+ * to be there by xfs_idata_realloc().
+ */
+ data_bytes = roundup(ip->i_df.if_bytes, 4);
+ ASSERT((ip->i_df.if_real_bytes == 0) ||
+ (ip->i_df.if_real_bytes == data_bytes));
+ vecp->i_len = (int)data_bytes;
+ vecp++;
+ nvecs++;
+ iip->ili_format.ilf_dsize = (unsigned)data_bytes;
+ }
+ break;
+
+ case XFS_DINODE_FMT_DEV:
+ ASSERT(!(iip->ili_format.ilf_fields &
+ (XFS_ILOG_DBROOT | XFS_ILOG_DEXT |
+ XFS_ILOG_DDATA | XFS_ILOG_UUID)));
+ if (iip->ili_format.ilf_fields & XFS_ILOG_DEV) {
+ iip->ili_format.ilf_u.ilfu_rdev =
+ ip->i_df.if_u2.if_rdev;
+ }
+ break;
+
+ case XFS_DINODE_FMT_UUID:
+ ASSERT(!(iip->ili_format.ilf_fields &
+ (XFS_ILOG_DBROOT | XFS_ILOG_DEXT |
+ XFS_ILOG_DDATA | XFS_ILOG_DEV)));
+ if (iip->ili_format.ilf_fields & XFS_ILOG_UUID) {
+ iip->ili_format.ilf_u.ilfu_uuid =
+ ip->i_df.if_u2.if_uuid;
+ }
+ break;
+
+ default:
+ ASSERT(0);
+ break;
+ }
+
+ /*
+ * If there are no attributes associated with the file,
+ * then we're done.
+ * Assert that no attribute-related log flags are set.
+ */
+ if (!XFS_IFORK_Q(ip)) {
+ ASSERT(nvecs == iip->ili_item.li_desc->lid_size);
+ iip->ili_format.ilf_size = nvecs;
+ ASSERT(!(iip->ili_format.ilf_fields &
+ (XFS_ILOG_ADATA | XFS_ILOG_ABROOT | XFS_ILOG_AEXT)));
+ return;
+ }
+
+ switch (ip->i_d.di_aformat) {
+ case XFS_DINODE_FMT_EXTENTS:
+ ASSERT(!(iip->ili_format.ilf_fields &
+ (XFS_ILOG_ADATA | XFS_ILOG_ABROOT)));
+ if (iip->ili_format.ilf_fields & XFS_ILOG_AEXT) {
+ ASSERT(ip->i_afp->if_bytes > 0);
+ ASSERT(ip->i_afp->if_u1.if_extents != NULL);
+ ASSERT(ip->i_d.di_anextents > 0);
+#ifdef DEBUG
+ nrecs = ip->i_afp->if_bytes /
+ (uint)sizeof(xfs_bmbt_rec_t);
+#endif
+ ASSERT(nrecs > 0);
+ ASSERT(nrecs == ip->i_d.di_anextents);
+#if ARCH_CONVERT == ARCH_NOCONVERT
+ /*
+ * There are not delayed allocation extents
+ * for attributes, so just point at the array.
+ */
+ vecp->i_addr = (char *)(ip->i_afp->if_u1.if_extents);
+ vecp->i_len = ip->i_afp->if_bytes;
+#else
+ ASSERT(iip->ili_aextents_buf == NULL);
+ /*
+ * Need to endian flip before logging
+ */
+ ext_buffer = kmem_alloc(ip->i_afp->if_bytes,
+ KM_SLEEP);
+ iip->ili_aextents_buf = ext_buffer;
+ vecp->i_addr = (xfs_caddr_t)ext_buffer;
+ vecp->i_len = xfs_iextents_copy(ip, ext_buffer,
+ XFS_ATTR_FORK);
+#endif
+ iip->ili_format.ilf_asize = vecp->i_len;
+ vecp++;
+ nvecs++;
+ }
+ break;
+
+ case XFS_DINODE_FMT_BTREE:
+ ASSERT(!(iip->ili_format.ilf_fields &
+ (XFS_ILOG_ADATA | XFS_ILOG_AEXT)));
+ if (iip->ili_format.ilf_fields & XFS_ILOG_ABROOT) {
+ ASSERT(ip->i_afp->if_broot_bytes > 0);
+ ASSERT(ip->i_afp->if_broot != NULL);
+ vecp->i_addr = (xfs_caddr_t)ip->i_afp->if_broot;
+ vecp->i_len = ip->i_afp->if_broot_bytes;
+ vecp++;
+ nvecs++;
+ iip->ili_format.ilf_asize = ip->i_afp->if_broot_bytes;
+ }
+ break;
+
+ case XFS_DINODE_FMT_LOCAL:
+ ASSERT(!(iip->ili_format.ilf_fields &
+ (XFS_ILOG_ABROOT | XFS_ILOG_AEXT)));
+ if (iip->ili_format.ilf_fields & XFS_ILOG_ADATA) {
+ ASSERT(ip->i_afp->if_bytes > 0);
+ ASSERT(ip->i_afp->if_u1.if_data != NULL);
+
+ vecp->i_addr = (xfs_caddr_t)ip->i_afp->if_u1.if_data;
+ /*
+ * Round i_bytes up to a word boundary.
+ * The underlying memory is guaranteed to
+ * to be there by xfs_idata_realloc().
+ */
+ data_bytes = roundup(ip->i_afp->if_bytes, 4);
+ ASSERT((ip->i_afp->if_real_bytes == 0) ||
+ (ip->i_afp->if_real_bytes == data_bytes));
+ vecp->i_len = (int)data_bytes;
+ vecp++;
+ nvecs++;
+ iip->ili_format.ilf_asize = (unsigned)data_bytes;
+ }
+ break;
+
+ default:
+ ASSERT(0);
+ break;
+ }
+
+ ASSERT(nvecs == iip->ili_item.li_desc->lid_size);
+ iip->ili_format.ilf_size = nvecs;
+}
+
+
+/*
+ * This is called to pin the inode associated with the inode log
+ * item in memory so it cannot be written out. Do this by calling
+ * xfs_ipin() to bump the pin count in the inode while holding the
+ * inode pin lock.
+ */
+STATIC void
+xfs_inode_item_pin(
+ xfs_inode_log_item_t *iip)
+{
+ ASSERT(ismrlocked(&(iip->ili_inode->i_lock), MR_UPDATE));
+ xfs_ipin(iip->ili_inode);
+}
+
+
+/*
+ * This is called to unpin the inode associated with the inode log
+ * item which was previously pinned with a call to xfs_inode_item_pin().
+ * Just call xfs_iunpin() on the inode to do this.
+ */
+/* ARGSUSED */
+STATIC void
+xfs_inode_item_unpin(
+ xfs_inode_log_item_t *iip,
+ int stale)
+{
+ xfs_iunpin(iip->ili_inode);
+}
+
+/* ARGSUSED */
+STATIC void
+xfs_inode_item_unpin_remove(
+ xfs_inode_log_item_t *iip,
+ xfs_trans_t *tp)
+{
+ xfs_iunpin(iip->ili_inode);
+}
+
+/*
+ * This is called to attempt to lock the inode associated with this
+ * inode log item, in preparation for the push routine which does the actual
+ * iflush. Don't sleep on the inode lock or the flush lock.
+ *
+ * If the flush lock is already held, indicating that the inode has
+ * been or is in the process of being flushed, then (ideally) we'd like to
+ * see if the inode's buffer is still incore, and if so give it a nudge.
+ * We delay doing so until the pushbuf routine, though, to avoid holding
+ * the AIL lock across a call to the blackhole which is the buffercache.
+ * Also we don't want to sleep in any device strategy routines, which can happen
+ * if we do the subsequent bawrite in here.
+ */
+STATIC uint
+xfs_inode_item_trylock(
+ xfs_inode_log_item_t *iip)
+{
+ register xfs_inode_t *ip;
+
+ ip = iip->ili_inode;
+
+ if (xfs_ipincount(ip) > 0) {
+ return XFS_ITEM_PINNED;
+ }
+
+ if (!xfs_ilock_nowait(ip, XFS_ILOCK_SHARED)) {
+ return XFS_ITEM_LOCKED;
+ }
+
+ if (!xfs_iflock_nowait(ip)) {
+ /*
+ * If someone else isn't already trying to push the inode
+ * buffer, we get to do it.
+ */
+ if (iip->ili_pushbuf_flag == 0) {
+ iip->ili_pushbuf_flag = 1;
+#ifdef DEBUG
+ iip->ili_push_owner = get_thread_id();
+#endif
+ /*
+ * Inode is left locked in shared mode.
+ * Pushbuf routine gets to unlock it.
+ */
+ return XFS_ITEM_PUSHBUF;
+ } else {
+ /*
+ * We hold the AIL_LOCK, so we must specify the
+ * NONOTIFY flag so that we won't double trip.
+ */
+ xfs_iunlock(ip, XFS_ILOCK_SHARED|XFS_IUNLOCK_NONOTIFY);
+ return XFS_ITEM_FLUSHING;
+ }
+ /* NOTREACHED */
+ }
+
+ /* Stale items should force out the iclog */
+ if (ip->i_flags & XFS_ISTALE) {
+ xfs_ifunlock(ip);
+ xfs_iunlock(ip, XFS_ILOCK_SHARED|XFS_IUNLOCK_NONOTIFY);
+ return XFS_ITEM_PINNED;
+ }
+
+#ifdef DEBUG
+ if (!XFS_FORCED_SHUTDOWN(ip->i_mount)) {
+ ASSERT(iip->ili_format.ilf_fields != 0);
+ ASSERT(iip->ili_logged == 0);
+ ASSERT(iip->ili_item.li_flags & XFS_LI_IN_AIL);
+ }
+#endif
+ return XFS_ITEM_SUCCESS;
+}
+
+/*
+ * Unlock the inode associated with the inode log item.
+ * Clear the fields of the inode and inode log item that
+ * are specific to the current transaction. If the
+ * hold flags is set, do not unlock the inode.
+ */
+STATIC void
+xfs_inode_item_unlock(
+ xfs_inode_log_item_t *iip)
+{
+ uint hold;
+ uint iolocked;
+ uint lock_flags;
+ xfs_inode_t *ip;
+
+ ASSERT(iip != NULL);
+ ASSERT(iip->ili_inode->i_itemp != NULL);
+ ASSERT(ismrlocked(&(iip->ili_inode->i_lock), MR_UPDATE));
+ ASSERT((!(iip->ili_inode->i_itemp->ili_flags &
+ XFS_ILI_IOLOCKED_EXCL)) ||
+ ismrlocked(&(iip->ili_inode->i_iolock), MR_UPDATE));
+ ASSERT((!(iip->ili_inode->i_itemp->ili_flags &
+ XFS_ILI_IOLOCKED_SHARED)) ||
+ ismrlocked(&(iip->ili_inode->i_iolock), MR_ACCESS));
+ /*
+ * Clear the transaction pointer in the inode.
+ */
+ ip = iip->ili_inode;
+ ip->i_transp = NULL;
+
+ /*
+ * If the inode needed a separate buffer with which to log
+ * its extents, then free it now.
+ */
+ if (iip->ili_extents_buf != NULL) {
+ ASSERT(ip->i_d.di_format == XFS_DINODE_FMT_EXTENTS);
+ ASSERT(ip->i_d.di_nextents > 0);
+ ASSERT(iip->ili_format.ilf_fields & XFS_ILOG_DEXT);
+ ASSERT(ip->i_df.if_bytes > 0);
+ kmem_free(iip->ili_extents_buf, ip->i_df.if_bytes);
+ iip->ili_extents_buf = NULL;
+ }
+ if (iip->ili_aextents_buf != NULL) {
+ ASSERT(ip->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS);
+ ASSERT(ip->i_d.di_anextents > 0);
+ ASSERT(iip->ili_format.ilf_fields & XFS_ILOG_AEXT);
+ ASSERT(ip->i_afp->if_bytes > 0);
+ kmem_free(iip->ili_aextents_buf, ip->i_afp->if_bytes);
+ iip->ili_aextents_buf = NULL;
+ }
+
+ /*
+ * Figure out if we should unlock the inode or not.
+ */
+ hold = iip->ili_flags & XFS_ILI_HOLD;
+
+ /*
+ * Before clearing out the flags, remember whether we
+ * are holding the inode's IO lock.
+ */
+ iolocked = iip->ili_flags & XFS_ILI_IOLOCKED_ANY;
+
+ /*
+ * Clear out the fields of the inode log item particular
+ * to the current transaction.
+ */
+ iip->ili_ilock_recur = 0;
+ iip->ili_iolock_recur = 0;
+ iip->ili_flags = 0;
+
+ /*
+ * Unlock the inode if XFS_ILI_HOLD was not set.
+ */
+ if (!hold) {
+ lock_flags = XFS_ILOCK_EXCL;
+ if (iolocked & XFS_ILI_IOLOCKED_EXCL) {
+ lock_flags |= XFS_IOLOCK_EXCL;
+ } else if (iolocked & XFS_ILI_IOLOCKED_SHARED) {
+ lock_flags |= XFS_IOLOCK_SHARED;
+ }
+ xfs_iput(iip->ili_inode, lock_flags);
+ }
+}
+
+/*
+ * This is called to find out where the oldest active copy of the
+ * inode log item in the on disk log resides now that the last log
+ * write of it completed at the given lsn. Since we always re-log
+ * all dirty data in an inode, the latest copy in the on disk log
+ * is the only one that matters. Therefore, simply return the
+ * given lsn.
+ */
+/*ARGSUSED*/
+STATIC xfs_lsn_t
+xfs_inode_item_committed(
+ xfs_inode_log_item_t *iip,
+ xfs_lsn_t lsn)
+{
+ return (lsn);
+}
+
+/*
+ * The transaction with the inode locked has aborted. The inode
+ * must not be dirty within the transaction (unless we're forcibly
+ * shutting down). We simply unlock just as if the transaction
+ * had been cancelled.
+ */
+STATIC void
+xfs_inode_item_abort(
+ xfs_inode_log_item_t *iip)
+{
+ xfs_inode_item_unlock(iip);
+ return;
+}
+
+
+/*
+ * This gets called by xfs_trans_push_ail(), when IOP_TRYLOCK
+ * failed to get the inode flush lock but did get the inode locked SHARED.
+ * Here we're trying to see if the inode buffer is incore, and if so whether it's
+ * marked delayed write. If that's the case, we'll initiate a bawrite on that
+ * buffer to expedite the process.
+ *
+ * We aren't holding the AIL_LOCK (or the flush lock) when this gets called,
+ * so it is inherently race-y.
+ */
+STATIC void
+xfs_inode_item_pushbuf(
+ xfs_inode_log_item_t *iip)
+{
+ xfs_inode_t *ip;
+ xfs_mount_t *mp;
+ xfs_buf_t *bp;
+ uint dopush;
+
+ ip = iip->ili_inode;
+
+ ASSERT(ismrlocked(&(ip->i_lock), MR_ACCESS));
+
+ /*
+ * The ili_pushbuf_flag keeps others from
+ * trying to duplicate our effort.
+ */
+ ASSERT(iip->ili_pushbuf_flag != 0);
+ ASSERT(iip->ili_push_owner == get_thread_id());
+
+ /*
+ * If flushlock isn't locked anymore, chances are that the
+ * inode flush completed and the inode was taken off the AIL.
+ * So, just get out.
+ */
+ if ((valusema(&(ip->i_flock)) > 0) ||
+ ((iip->ili_item.li_flags & XFS_LI_IN_AIL) == 0)) {
+ iip->ili_pushbuf_flag = 0;
+ xfs_iunlock(ip, XFS_ILOCK_SHARED);
+ return;
+ }
+
+ mp = ip->i_mount;
+ bp = xfs_incore(mp->m_ddev_targp, iip->ili_format.ilf_blkno,
+ iip->ili_format.ilf_len, XFS_INCORE_TRYLOCK);
+
+ if (bp != NULL) {
+ if (XFS_BUF_ISDELAYWRITE(bp)) {
+ /*
+ * We were racing with iflush because we don't hold
+ * the AIL_LOCK or the flush lock. However, at this point,
+ * we have the buffer, and we know that it's dirty.
+ * So, it's possible that iflush raced with us, and
+ * this item is already taken off the AIL.
+ * If not, we can flush it async.
+ */
+ dopush = ((iip->ili_item.li_flags & XFS_LI_IN_AIL) &&
+ (valusema(&(ip->i_flock)) <= 0));
+ iip->ili_pushbuf_flag = 0;
+ xfs_iunlock(ip, XFS_ILOCK_SHARED);
+ xfs_buftrace("INODE ITEM PUSH", bp);
+ if (XFS_BUF_ISPINNED(bp)) {
+ xfs_log_force(mp, (xfs_lsn_t)0,
+ XFS_LOG_FORCE);
+ }
+ if (dopush) {
+ xfs_bawrite(mp, bp);
+ } else {
+ xfs_buf_relse(bp);
+ }
+ } else {
+ iip->ili_pushbuf_flag = 0;
+ xfs_iunlock(ip, XFS_ILOCK_SHARED);
+ xfs_buf_relse(bp);
+ }
+ return;
+ }
+ /*
+ * We have to be careful about resetting pushbuf flag too early (above).
+ * Even though in theory we can do it as soon as we have the buflock,
+ * we don't want others to be doing work needlessly. They'll come to
+ * this function thinking that pushing the buffer is their
+ * responsibility only to find that the buffer is still locked by
+ * another doing the same thing
+ */
+ iip->ili_pushbuf_flag = 0;
+ xfs_iunlock(ip, XFS_ILOCK_SHARED);
+ return;
+}
+
+
+/*
+ * This is called to asynchronously write the inode associated with this
+ * inode log item out to disk. The inode will already have been locked by
+ * a successful call to xfs_inode_item_trylock().
+ */
+STATIC void
+xfs_inode_item_push(
+ xfs_inode_log_item_t *iip)
+{
+ xfs_inode_t *ip;
+
+ ip = iip->ili_inode;
+
+ ASSERT(ismrlocked(&(ip->i_lock), MR_ACCESS));
+ ASSERT(valusema(&(ip->i_flock)) <= 0);
+ /*
+ * Since we were able to lock the inode's flush lock and
+ * we found it on the AIL, the inode must be dirty. This
+ * is because the inode is removed from the AIL while still
+ * holding the flush lock in xfs_iflush_done(). Thus, if
+ * we found it in the AIL and were able to obtain the flush
+ * lock without sleeping, then there must not have been
+ * anyone in the process of flushing the inode.
+ */
+ ASSERT(XFS_FORCED_SHUTDOWN(ip->i_mount) ||
+ iip->ili_format.ilf_fields != 0);
+
+ /*
+ * Write out the inode. The completion routine ('iflush_done') will
+ * pull it from the AIL, mark it clean, unlock the flush lock.
+ */
+ (void) xfs_iflush(ip, XFS_IFLUSH_ASYNC);
+ xfs_iunlock(ip, XFS_ILOCK_SHARED);
+
+ return;
+}
+
+/*
+ * XXX rcc - this one really has to do something. Probably needs
+ * to stamp in a new field in the incore inode.
+ */
+/* ARGSUSED */
+STATIC void
+xfs_inode_item_committing(
+ xfs_inode_log_item_t *iip,
+ xfs_lsn_t lsn)
+{
+ iip->ili_last_lsn = lsn;
+ return;
+}
+
+/*
+ * This is the ops vector shared by all buf log items.
+ */
+struct xfs_item_ops xfs_inode_item_ops = {
+ .iop_size = (uint(*)(xfs_log_item_t*))xfs_inode_item_size,
+ .iop_format = (void(*)(xfs_log_item_t*, xfs_log_iovec_t*))
+ xfs_inode_item_format,
+ .iop_pin = (void(*)(xfs_log_item_t*))xfs_inode_item_pin,
+ .iop_unpin = (void(*)(xfs_log_item_t*, int))xfs_inode_item_unpin,
+ .iop_unpin_remove = (void(*)(xfs_log_item_t*, xfs_trans_t*))
+ xfs_inode_item_unpin_remove,
+ .iop_trylock = (uint(*)(xfs_log_item_t*))xfs_inode_item_trylock,
+ .iop_unlock = (void(*)(xfs_log_item_t*))xfs_inode_item_unlock,
+ .iop_committed = (xfs_lsn_t(*)(xfs_log_item_t*, xfs_lsn_t))
+ xfs_inode_item_committed,
+ .iop_push = (void(*)(xfs_log_item_t*))xfs_inode_item_push,
+ .iop_abort = (void(*)(xfs_log_item_t*))xfs_inode_item_abort,
+ .iop_pushbuf = (void(*)(xfs_log_item_t*))xfs_inode_item_pushbuf,
+ .iop_committing = (void(*)(xfs_log_item_t*, xfs_lsn_t))
+ xfs_inode_item_committing
+};
+
+
+/*
+ * Initialize the inode log item for a newly allocated (in-core) inode.
+ */
+void
+xfs_inode_item_init(
+ xfs_inode_t *ip,
+ xfs_mount_t *mp)
+{
+ xfs_inode_log_item_t *iip;
+
+ ASSERT(ip->i_itemp == NULL);
+ iip = ip->i_itemp = kmem_zone_zalloc(xfs_ili_zone, KM_SLEEP);
+
+ iip->ili_item.li_type = XFS_LI_INODE;
+ iip->ili_item.li_ops = &xfs_inode_item_ops;
+ iip->ili_item.li_mountp = mp;
+ iip->ili_inode = ip;
+
+ /*
+ We have zeroed memory. No need ...
+ iip->ili_extents_buf = NULL;
+ iip->ili_pushbuf_flag = 0;
+ */
+
+ iip->ili_format.ilf_type = XFS_LI_INODE;
+ iip->ili_format.ilf_ino = ip->i_ino;
+ iip->ili_format.ilf_blkno = ip->i_blkno;
+ iip->ili_format.ilf_len = ip->i_len;
+ iip->ili_format.ilf_boffset = ip->i_boffset;
+}
+
+/*
+ * Free the inode log item and any memory hanging off of it.
+ */
+void
+xfs_inode_item_destroy(
+ xfs_inode_t *ip)
+{
+#ifdef XFS_TRANS_DEBUG
+ if (ip->i_itemp->ili_root_size != 0) {
+ kmem_free(ip->i_itemp->ili_orig_root,
+ ip->i_itemp->ili_root_size);
+ }
+#endif
+ kmem_zone_free(xfs_ili_zone, ip->i_itemp);
+}
+
+
+/*
+ * This is the inode flushing I/O completion routine. It is called
+ * from interrupt level when the buffer containing the inode is
+ * flushed to disk. It is responsible for removing the inode item
+ * from the AIL if it has not been re-logged, and unlocking the inode's
+ * flush lock.
+ */
+/*ARGSUSED*/
+void
+xfs_iflush_done(
+ xfs_buf_t *bp,
+ xfs_inode_log_item_t *iip)
+{
+ xfs_inode_t *ip;
+ SPLDECL(s);
+
+ ip = iip->ili_inode;
+
+ /*
+ * We only want to pull the item from the AIL if it is
+ * actually there and its location in the log has not
+ * changed since we started the flush. Thus, we only bother
+ * if the ili_logged flag is set and the inode's lsn has not
+ * changed. First we check the lsn outside
+ * the lock since it's cheaper, and then we recheck while
+ * holding the lock before removing the inode from the AIL.
+ */
+ if (iip->ili_logged &&
+ (iip->ili_item.li_lsn == iip->ili_flush_lsn)) {
+ AIL_LOCK(ip->i_mount, s);
+ if (iip->ili_item.li_lsn == iip->ili_flush_lsn) {
+ /*
+ * xfs_trans_delete_ail() drops the AIL lock.
+ */
+ xfs_trans_delete_ail(ip->i_mount,
+ (xfs_log_item_t*)iip, s);
+ } else {
+ AIL_UNLOCK(ip->i_mount, s);
+ }
+ }
+
+ iip->ili_logged = 0;
+
+ /*
+ * Clear the ili_last_fields bits now that we know that the
+ * data corresponding to them is safely on disk.
+ */
+ iip->ili_last_fields = 0;
+
+ /*
+ * Release the inode's flush lock since we're done with it.
+ */
+ xfs_ifunlock(ip);
+
+ return;
+}
+
+/*
+ * This is the inode flushing abort routine. It is called
+ * from xfs_iflush when the filesystem is shutting down to clean
+ * up the inode state.
+ * It is responsible for removing the inode item
+ * from the AIL if it has not been re-logged, and unlocking the inode's
+ * flush lock.
+ */
+void
+xfs_iflush_abort(
+ xfs_inode_t *ip)
+{
+ xfs_inode_log_item_t *iip;
+ xfs_mount_t *mp;
+ SPLDECL(s);
+
+ iip = ip->i_itemp;
+ mp = ip->i_mount;
+ if (iip) {
+ if (iip->ili_item.li_flags & XFS_LI_IN_AIL) {
+ AIL_LOCK(mp, s);
+ if (iip->ili_item.li_flags & XFS_LI_IN_AIL) {
+ /*
+ * xfs_trans_delete_ail() drops the AIL lock.
+ */
+ xfs_trans_delete_ail(mp, (xfs_log_item_t *)iip,
+ s);
+ } else
+ AIL_UNLOCK(mp, s);
+ }
+ iip->ili_logged = 0;
+ /*
+ * Clear the ili_last_fields bits now that we know that the
+ * data corresponding to them is safely on disk.
+ */
+ iip->ili_last_fields = 0;
+ /*
+ * Clear the inode logging fields so no more flushes are
+ * attempted.
+ */
+ iip->ili_format.ilf_fields = 0;
+ }
+ /*
+ * Release the inode's flush lock since we're done with it.
+ */
+ xfs_ifunlock(ip);
+}
+
+void
+xfs_istale_done(
+ xfs_buf_t *bp,
+ xfs_inode_log_item_t *iip)
+{
+ xfs_iflush_abort(iip->ili_inode);
+}
diff --git a/fs/xfs/xfs_inode_item.h b/fs/xfs/xfs_inode_item.h
new file mode 100644
index 00000000000000..d8775e0d62914e
--- /dev/null
+++ b/fs/xfs/xfs_inode_item.h
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_INODE_ITEM_H__
+#define __XFS_INODE_ITEM_H__
+
+/*
+ * This is the structure used to lay out an inode log item in the
+ * log. The size of the inline data/extents/b-tree root to be logged
+ * (if any) is indicated in the ilf_dsize field. Changes to this structure
+ * must be added on to the end.
+ *
+ * Convention for naming inode log item versions : The current version
+ * is always named XFS_LI_INODE. When an inode log item gets superseded,
+ * add the latest version of IRIX that will generate logs with that item
+ * to the version name.
+ *
+ * -Version 1 of this structure (XFS_LI_5_3_INODE) included up to the first
+ * union (ilf_u) field. This was released with IRIX 5.3-XFS.
+ * -Version 2 of this structure (XFS_LI_6_1_INODE) is currently the entire
+ * structure. This was released with IRIX 6.0.1-XFS and IRIX 6.1.
+ * -Version 3 of this structure (XFS_LI_INODE) is the same as version 2
+ * so a new structure definition wasn't necessary. However, we had
+ * to add a new type because the inode cluster size changed from 4K
+ * to 8K and the version number had to be rev'ved to keep older kernels
+ * from trying to recover logs with the 8K buffers in them. The logging
+ * code can handle recovery on different-sized clusters now so hopefully
+ * this'll be the last time we need to change the inode log item just
+ * for a change in the inode cluster size. This new version was
+ * released with IRIX 6.2.
+ */
+typedef struct xfs_inode_log_format {
+ unsigned short ilf_type; /* inode log item type */
+ unsigned short ilf_size; /* size of this item */
+ uint ilf_fields; /* flags for fields logged */
+ ushort ilf_asize; /* size of attr d/ext/root */
+ ushort ilf_dsize; /* size of data/ext/root */
+ xfs_ino_t ilf_ino; /* inode number */
+ union {
+ xfs_dev_t ilfu_rdev; /* rdev value for dev inode*/
+ uuid_t ilfu_uuid; /* mount point value */
+ } ilf_u;
+ __int64_t ilf_blkno; /* blkno of inode buffer */
+ int ilf_len; /* len of inode buffer */
+ int ilf_boffset; /* off of inode in buffer */
+} xfs_inode_log_format_t;
+
+/* Initial version shipped with IRIX 5.3-XFS */
+typedef struct xfs_inode_log_format_v1 {
+ unsigned short ilf_type; /* inode log item type */
+ unsigned short ilf_size; /* size of this item */
+ uint ilf_fields; /* flags for fields logged */
+ uint ilf_dsize; /* size of data/ext/root */
+ xfs_ino_t ilf_ino; /* inode number */
+ union {
+ xfs_dev_t ilfu_rdev; /* rdev value for dev inode*/
+ uuid_t ilfu_uuid; /* mount point value */
+ } ilf_u;
+} xfs_inode_log_format_t_v1;
+
+/*
+ * Flags for xfs_trans_log_inode flags field.
+ */
+#define XFS_ILOG_CORE 0x001 /* log standard inode fields */
+#define XFS_ILOG_DDATA 0x002 /* log i_df.if_data */
+#define XFS_ILOG_DEXT 0x004 /* log i_df.if_extents */
+#define XFS_ILOG_DBROOT 0x008 /* log i_df.i_broot */
+#define XFS_ILOG_DEV 0x010 /* log the dev field */
+#define XFS_ILOG_UUID 0x020 /* log the uuid field */
+#define XFS_ILOG_ADATA 0x040 /* log i_af.if_data */
+#define XFS_ILOG_AEXT 0x080 /* log i_af.if_extents */
+#define XFS_ILOG_ABROOT 0x100 /* log i_af.i_broot */
+
+#define XFS_ILOG_NONCORE (XFS_ILOG_DDATA | XFS_ILOG_DEXT | \
+ XFS_ILOG_DBROOT | XFS_ILOG_DEV | \
+ XFS_ILOG_UUID | XFS_ILOG_ADATA | \
+ XFS_ILOG_AEXT | XFS_ILOG_ABROOT)
+
+#define XFS_ILOG_DFORK (XFS_ILOG_DDATA | XFS_ILOG_DEXT | \
+ XFS_ILOG_DBROOT)
+
+#define XFS_ILOG_AFORK (XFS_ILOG_ADATA | XFS_ILOG_AEXT | \
+ XFS_ILOG_ABROOT)
+
+#define XFS_ILOG_ALL (XFS_ILOG_CORE | XFS_ILOG_DDATA | \
+ XFS_ILOG_DEXT | XFS_ILOG_DBROOT | \
+ XFS_ILOG_DEV | XFS_ILOG_UUID | \
+ XFS_ILOG_ADATA | XFS_ILOG_AEXT | \
+ XFS_ILOG_ABROOT)
+
+#define XFS_ILI_HOLD 0x1
+#define XFS_ILI_IOLOCKED_EXCL 0x2
+#define XFS_ILI_IOLOCKED_SHARED 0x4
+
+#define XFS_ILI_IOLOCKED_ANY (XFS_ILI_IOLOCKED_EXCL | XFS_ILI_IOLOCKED_SHARED)
+
+
+#ifdef __KERNEL__
+
+struct xfs_buf;
+struct xfs_bmbt_rec_64;
+struct xfs_inode;
+struct xfs_mount;
+
+
+typedef struct xfs_inode_log_item {
+ xfs_log_item_t ili_item; /* common portion */
+ struct xfs_inode *ili_inode; /* inode ptr */
+ xfs_lsn_t ili_flush_lsn; /* lsn at last flush */
+ xfs_lsn_t ili_last_lsn; /* lsn at last transaction */
+ unsigned short ili_ilock_recur; /* lock recursion count */
+ unsigned short ili_iolock_recur; /* lock recursion count */
+ unsigned short ili_flags; /* misc flags */
+ unsigned short ili_logged; /* flushed logged data */
+ unsigned int ili_last_fields; /* fields when flushed */
+ struct xfs_bmbt_rec_64 *ili_extents_buf; /* array of logged
+ data exts */
+ struct xfs_bmbt_rec_64 *ili_aextents_buf; /* array of logged
+ attr exts */
+ unsigned int ili_pushbuf_flag; /* one bit used in push_ail */
+
+#ifdef DEBUG
+ uint64_t ili_push_owner; /* one who sets pushbuf_flag
+ above gets to push the buf */
+#endif
+#ifdef XFS_TRANS_DEBUG
+ int ili_root_size;
+ char *ili_orig_root;
+#endif
+ xfs_inode_log_format_t ili_format; /* logged structure */
+} xfs_inode_log_item_t;
+
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_ILOG_FDATA)
+int xfs_ilog_fdata(int w);
+#define XFS_ILOG_FDATA(w) xfs_ilog_fdata(w)
+#else
+#define XFS_ILOG_FDATA(w) \
+ ((w) == XFS_DATA_FORK ? XFS_ILOG_DDATA : XFS_ILOG_ADATA)
+#endif
+
+#endif /* __KERNEL__ */
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_ILOG_FBROOT)
+int xfs_ilog_fbroot(int w);
+#define XFS_ILOG_FBROOT(w) xfs_ilog_fbroot(w)
+#else
+#define XFS_ILOG_FBROOT(w) \
+ ((w) == XFS_DATA_FORK ? XFS_ILOG_DBROOT : XFS_ILOG_ABROOT)
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_ILOG_FEXT)
+int xfs_ilog_fext(int w);
+#define XFS_ILOG_FEXT(w) xfs_ilog_fext(w)
+#else
+#define XFS_ILOG_FEXT(w) \
+ ((w) == XFS_DATA_FORK ? XFS_ILOG_DEXT : XFS_ILOG_AEXT)
+#endif
+
+#ifdef __KERNEL__
+
+void xfs_inode_item_init(struct xfs_inode *, struct xfs_mount *);
+void xfs_inode_item_destroy(struct xfs_inode *);
+void xfs_iflush_done(struct xfs_buf *, xfs_inode_log_item_t *);
+void xfs_istale_done(struct xfs_buf *, xfs_inode_log_item_t *);
+void xfs_iflush_abort(struct xfs_inode *);
+
+#endif /* __KERNEL__ */
+
+#endif /* __XFS_INODE_ITEM_H__ */
diff --git a/fs/xfs/xfs_inum.h b/fs/xfs/xfs_inum.h
new file mode 100644
index 00000000000000..a3af2d5a6eb7a5
--- /dev/null
+++ b/fs/xfs/xfs_inum.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_INUM_H__
+#define __XFS_INUM_H__
+
+/*
+ * Inode number format:
+ * low inopblog bits - offset in block
+ * next agblklog bits - block number in ag
+ * next agno_log bits - ag number
+ * high agno_log-agblklog-inopblog bits - 0
+ */
+
+typedef __uint32_t xfs_agino_t; /* within allocation grp inode number */
+
+/*
+ * Useful inode bits for this kernel.
+ * Used in some places where having 64-bits in the 32-bit kernels
+ * costs too much.
+ */
+#if XFS_BIG_INUMS
+typedef xfs_ino_t xfs_intino_t;
+#else
+typedef __uint32_t xfs_intino_t;
+#endif
+
+#define NULLFSINO ((xfs_ino_t)-1)
+#define NULLAGINO ((xfs_agino_t)-1)
+
+struct xfs_mount;
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_INO_MASK)
+__uint32_t xfs_ino_mask(int k);
+#define XFS_INO_MASK(k) xfs_ino_mask(k)
+#else
+#define XFS_INO_MASK(k) ((__uint32_t)((1ULL << (k)) - 1))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_INO_OFFSET_BITS)
+int xfs_ino_offset_bits(struct xfs_mount *mp);
+#define XFS_INO_OFFSET_BITS(mp) xfs_ino_offset_bits(mp)
+#else
+#define XFS_INO_OFFSET_BITS(mp) ((mp)->m_sb.sb_inopblog)
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_INO_AGBNO_BITS)
+int xfs_ino_agbno_bits(struct xfs_mount *mp);
+#define XFS_INO_AGBNO_BITS(mp) xfs_ino_agbno_bits(mp)
+#else
+#define XFS_INO_AGBNO_BITS(mp) ((mp)->m_sb.sb_agblklog)
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_INO_AGINO_BITS)
+int xfs_ino_agino_bits(struct xfs_mount *mp);
+#define XFS_INO_AGINO_BITS(mp) xfs_ino_agino_bits(mp)
+#else
+#define XFS_INO_AGINO_BITS(mp) ((mp)->m_agino_log)
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_INO_AGNO_BITS)
+int xfs_ino_agno_bits(struct xfs_mount *mp);
+#define XFS_INO_AGNO_BITS(mp) xfs_ino_agno_bits(mp)
+#else
+#define XFS_INO_AGNO_BITS(mp) ((mp)->m_agno_log)
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_INO_BITS)
+int xfs_ino_bits(struct xfs_mount *mp);
+#define XFS_INO_BITS(mp) xfs_ino_bits(mp)
+#else
+#define XFS_INO_BITS(mp) (XFS_INO_AGNO_BITS(mp) + XFS_INO_AGINO_BITS(mp))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_INO_TO_AGNO)
+xfs_agnumber_t xfs_ino_to_agno(struct xfs_mount *mp, xfs_ino_t i);
+#define XFS_INO_TO_AGNO(mp,i) xfs_ino_to_agno(mp,i)
+#else
+#define XFS_INO_TO_AGNO(mp,i) \
+ ((xfs_agnumber_t)((i) >> XFS_INO_AGINO_BITS(mp)))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_INO_TO_AGINO)
+xfs_agino_t xfs_ino_to_agino(struct xfs_mount *mp, xfs_ino_t i);
+#define XFS_INO_TO_AGINO(mp,i) xfs_ino_to_agino(mp,i)
+#else
+#define XFS_INO_TO_AGINO(mp,i) \
+ ((xfs_agino_t)(i) & XFS_INO_MASK(XFS_INO_AGINO_BITS(mp)))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_INO_TO_AGBNO)
+xfs_agblock_t xfs_ino_to_agbno(struct xfs_mount *mp, xfs_ino_t i);
+#define XFS_INO_TO_AGBNO(mp,i) xfs_ino_to_agbno(mp,i)
+#else
+#define XFS_INO_TO_AGBNO(mp,i) \
+ (((xfs_agblock_t)(i) >> XFS_INO_OFFSET_BITS(mp)) & \
+ XFS_INO_MASK(XFS_INO_AGBNO_BITS(mp)))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_INO_TO_OFFSET)
+int xfs_ino_to_offset(struct xfs_mount *mp, xfs_ino_t i);
+#define XFS_INO_TO_OFFSET(mp,i) xfs_ino_to_offset(mp,i)
+#else
+#define XFS_INO_TO_OFFSET(mp,i) \
+ ((int)(i) & XFS_INO_MASK(XFS_INO_OFFSET_BITS(mp)))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_INO_TO_FSB)
+xfs_fsblock_t xfs_ino_to_fsb(struct xfs_mount *mp, xfs_ino_t i);
+#define XFS_INO_TO_FSB(mp,i) xfs_ino_to_fsb(mp,i)
+#else
+#define XFS_INO_TO_FSB(mp,i) \
+ XFS_AGB_TO_FSB(mp, XFS_INO_TO_AGNO(mp,i), XFS_INO_TO_AGBNO(mp,i))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_AGINO_TO_INO)
+xfs_ino_t
+xfs_agino_to_ino(struct xfs_mount *mp, xfs_agnumber_t a, xfs_agino_t i);
+#define XFS_AGINO_TO_INO(mp,a,i) xfs_agino_to_ino(mp,a,i)
+#else
+#define XFS_AGINO_TO_INO(mp,a,i) \
+ (((xfs_ino_t)(a) << XFS_INO_AGINO_BITS(mp)) | (i))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_AGINO_TO_AGBNO)
+xfs_agblock_t xfs_agino_to_agbno(struct xfs_mount *mp, xfs_agino_t i);
+#define XFS_AGINO_TO_AGBNO(mp,i) xfs_agino_to_agbno(mp,i)
+#else
+#define XFS_AGINO_TO_AGBNO(mp,i) ((i) >> XFS_INO_OFFSET_BITS(mp))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_AGINO_TO_OFFSET)
+int xfs_agino_to_offset(struct xfs_mount *mp, xfs_agino_t i);
+#define XFS_AGINO_TO_OFFSET(mp,i) xfs_agino_to_offset(mp,i)
+#else
+#define XFS_AGINO_TO_OFFSET(mp,i) \
+ ((i) & XFS_INO_MASK(XFS_INO_OFFSET_BITS(mp)))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_OFFBNO_TO_AGINO)
+xfs_agino_t xfs_offbno_to_agino(struct xfs_mount *mp, xfs_agblock_t b, int o);
+#define XFS_OFFBNO_TO_AGINO(mp,b,o) xfs_offbno_to_agino(mp,b,o)
+#else
+#define XFS_OFFBNO_TO_AGINO(mp,b,o) \
+ ((xfs_agino_t)(((b) << XFS_INO_OFFSET_BITS(mp)) | (o)))
+#endif
+
+#if XFS_BIG_INUMS
+#define XFS_MAXINUMBER ((xfs_ino_t)((1ULL << 56) - 1ULL))
+#define XFS_INO64_OFFSET ((xfs_ino_t)(1ULL << 32))
+#else
+#define XFS_MAXINUMBER ((xfs_ino_t)((1ULL << 32) - 1ULL))
+#endif
+#define XFS_MAXINUMBER_32 ((xfs_ino_t)((1ULL << 32) - 1ULL))
+
+#endif /* __XFS_INUM_H__ */
diff --git a/fs/xfs/xfs_iocore.c b/fs/xfs/xfs_iocore.c
new file mode 100644
index 00000000000000..414ec496845f17
--- /dev/null
+++ b/fs/xfs/xfs_iocore.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_itable.h"
+#include "xfs_btree.h"
+#include "xfs_alloc.h"
+#include "xfs_ialloc.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode_item.h"
+#include "xfs_inode.h"
+#include "xfs_bmap.h"
+#include "xfs_error.h"
+#include "xfs_bit.h"
+#include "xfs_rw.h"
+#include "xfs_quota.h"
+#include "xfs_trans_space.h"
+#include "xfs_iomap.h"
+
+
+STATIC xfs_fsize_t
+xfs_size_fn(
+ xfs_inode_t *ip)
+{
+ return (ip->i_d.di_size);
+}
+
+STATIC int
+xfs_ioinit(
+ struct vfs *vfsp,
+ struct xfs_mount_args *mntargs,
+ int flags)
+{
+ return xfs_mountfs(vfsp, XFS_VFSTOM(vfsp), flags);
+}
+
+xfs_ioops_t xfs_iocore_xfs = {
+ .xfs_ioinit = (xfs_ioinit_t) xfs_ioinit,
+ .xfs_bmapi_func = (xfs_bmapi_t) xfs_bmapi,
+ .xfs_bmap_eof_func = (xfs_bmap_eof_t) xfs_bmap_eof,
+ .xfs_iomap_write_direct =
+ (xfs_iomap_write_direct_t) xfs_iomap_write_direct,
+ .xfs_iomap_write_delay =
+ (xfs_iomap_write_delay_t) xfs_iomap_write_delay,
+ .xfs_iomap_write_allocate =
+ (xfs_iomap_write_allocate_t) xfs_iomap_write_allocate,
+ .xfs_iomap_write_unwritten =
+ (xfs_iomap_write_unwritten_t) xfs_iomap_write_unwritten,
+ .xfs_ilock = (xfs_lock_t) xfs_ilock,
+ .xfs_lck_map_shared = (xfs_lck_map_shared_t) xfs_ilock_map_shared,
+ .xfs_ilock_demote = (xfs_lock_demote_t) xfs_ilock_demote,
+ .xfs_ilock_nowait = (xfs_lock_nowait_t) xfs_ilock_nowait,
+ .xfs_unlock = (xfs_unlk_t) xfs_iunlock,
+ .xfs_size_func = (xfs_size_t) xfs_size_fn,
+ .xfs_iodone = (xfs_iodone_t) fs_noerr,
+};
+
+void
+xfs_iocore_inode_reinit(
+ xfs_inode_t *ip)
+{
+ xfs_iocore_t *io = &ip->i_iocore;
+
+ io->io_flags = 0;
+ if (ip->i_d.di_flags & XFS_DIFLAG_REALTIME)
+ io->io_flags |= XFS_IOCORE_RT;
+ io->io_dmevmask = ip->i_d.di_dmevmask;
+ io->io_dmstate = ip->i_d.di_dmstate;
+}
+
+void
+xfs_iocore_inode_init(
+ xfs_inode_t *ip)
+{
+ xfs_iocore_t *io = &ip->i_iocore;
+ xfs_mount_t *mp = ip->i_mount;
+
+ io->io_mount = mp;
+#ifdef DEBUG
+ io->io_lock = &ip->i_lock;
+ io->io_iolock = &ip->i_iolock;
+#endif
+
+ io->io_obj = (void *)ip;
+
+ xfs_iocore_inode_reinit(ip);
+}
diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
new file mode 100644
index 00000000000000..3826e8f0e28a6d
--- /dev/null
+++ b/fs/xfs/xfs_iomap.c
@@ -0,0 +1,1000 @@
+/*
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+
+#include "xfs_fs.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_alloc.h"
+#include "xfs_dmapi.h"
+#include "xfs_quota.h"
+#include "xfs_mount.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode.h"
+#include "xfs_bmap.h"
+#include "xfs_bit.h"
+#include "xfs_rtalloc.h"
+#include "xfs_error.h"
+#include "xfs_itable.h"
+#include "xfs_rw.h"
+#include "xfs_acl.h"
+#include "xfs_cap.h"
+#include "xfs_mac.h"
+#include "xfs_attr.h"
+#include "xfs_buf_item.h"
+#include "xfs_trans_space.h"
+#include "xfs_utils.h"
+#include "xfs_iomap.h"
+
+#if defined(XFS_RW_TRACE)
+void
+xfs_iomap_enter_trace(
+ int tag,
+ xfs_iocore_t *io,
+ xfs_off_t offset,
+ ssize_t count)
+{
+ xfs_inode_t *ip = XFS_IO_INODE(io);
+
+ if (!ip->i_rwtrace)
+ return;
+
+ ktrace_enter(ip->i_rwtrace,
+ (void *)((unsigned long)tag),
+ (void *)ip,
+ (void *)((unsigned long)((ip->i_d.di_size >> 32) & 0xffffffff)),
+ (void *)((unsigned long)(ip->i_d.di_size & 0xffffffff)),
+ (void *)((unsigned long)((offset >> 32) & 0xffffffff)),
+ (void *)((unsigned long)(offset & 0xffffffff)),
+ (void *)((unsigned long)count),
+ (void *)((unsigned long)((io->io_new_size >> 32) & 0xffffffff)),
+ (void *)((unsigned long)(io->io_new_size & 0xffffffff)),
+ (void *)NULL,
+ (void *)NULL,
+ (void *)NULL,
+ (void *)NULL,
+ (void *)NULL,
+ (void *)NULL,
+ (void *)NULL);
+}
+
+void
+xfs_iomap_map_trace(
+ int tag,
+ xfs_iocore_t *io,
+ xfs_off_t offset,
+ ssize_t count,
+ xfs_iomap_t *iomapp,
+ xfs_bmbt_irec_t *imapp,
+ int flags)
+{
+ xfs_inode_t *ip = XFS_IO_INODE(io);
+
+ if (!ip->i_rwtrace)
+ return;
+
+ ktrace_enter(ip->i_rwtrace,
+ (void *)((unsigned long)tag),
+ (void *)ip,
+ (void *)((unsigned long)((ip->i_d.di_size >> 32) & 0xffffffff)),
+ (void *)((unsigned long)(ip->i_d.di_size & 0xffffffff)),
+ (void *)((unsigned long)((offset >> 32) & 0xffffffff)),
+ (void *)((unsigned long)(offset & 0xffffffff)),
+ (void *)((unsigned long)count),
+ (void *)((unsigned long)flags),
+ (void *)((unsigned long)((iomapp->iomap_offset >> 32) & 0xffffffff)),
+ (void *)((unsigned long)(iomapp->iomap_offset & 0xffffffff)),
+ (void *)((unsigned long)(iomapp->iomap_delta)),
+ (void *)((unsigned long)(iomapp->iomap_bsize)),
+ (void *)((unsigned long)(iomapp->iomap_bn)),
+ (void *)(__psint_t)(imapp->br_startoff),
+ (void *)((unsigned long)(imapp->br_blockcount)),
+ (void *)(__psint_t)(imapp->br_startblock));
+}
+#else
+#define xfs_iomap_enter_trace(tag, io, offset, count)
+#define xfs_iomap_map_trace(tag, io, offset, count, iomapp, imapp, flags)
+#endif
+
+#define XFS_WRITEIO_ALIGN(mp,off) (((off) >> mp->m_writeio_log) \
+ << mp->m_writeio_log)
+#define XFS_STRAT_WRITE_IMAPS 2
+#define XFS_WRITE_IMAPS XFS_BMAP_MAX_NMAP
+
+STATIC int
+xfs_imap_to_bmap(
+ xfs_iocore_t *io,
+ xfs_off_t offset,
+ xfs_bmbt_irec_t *imap,
+ xfs_iomap_t *iomapp,
+ int imaps, /* Number of imap entries */
+ int iomaps, /* Number of iomap entries */
+ int flags)
+{
+ xfs_mount_t *mp;
+ xfs_fsize_t nisize;
+ int pbm;
+ xfs_fsblock_t start_block;
+
+ mp = io->io_mount;
+ nisize = XFS_SIZE(mp, io);
+ if (io->io_new_size > nisize)
+ nisize = io->io_new_size;
+
+ for (pbm = 0; imaps && pbm < iomaps; imaps--, iomapp++, imap++, pbm++) {
+ iomapp->iomap_offset = XFS_FSB_TO_B(mp, imap->br_startoff);
+ iomapp->iomap_delta = offset - iomapp->iomap_offset;
+ iomapp->iomap_bsize = XFS_FSB_TO_B(mp, imap->br_blockcount);
+ iomapp->iomap_flags = flags;
+
+ if (io->io_flags & XFS_IOCORE_RT) {
+ iomapp->iomap_flags |= IOMAP_REALTIME;
+ iomapp->iomap_target = mp->m_rtdev_targp;
+ } else {
+ iomapp->iomap_target = mp->m_ddev_targp;
+ }
+ start_block = imap->br_startblock;
+ if (start_block == HOLESTARTBLOCK) {
+ iomapp->iomap_bn = IOMAP_DADDR_NULL;
+ iomapp->iomap_flags |= IOMAP_HOLE;
+ } else if (start_block == DELAYSTARTBLOCK) {
+ iomapp->iomap_bn = IOMAP_DADDR_NULL;
+ iomapp->iomap_flags |= IOMAP_DELAY;
+ } else {
+ iomapp->iomap_bn = XFS_FSB_TO_DB_IO(io, start_block);
+ if (ISUNWRITTEN(imap))
+ iomapp->iomap_flags |= IOMAP_UNWRITTEN;
+ }
+
+ if ((iomapp->iomap_offset + iomapp->iomap_bsize) >= nisize) {
+ iomapp->iomap_flags |= IOMAP_EOF;
+ }
+
+ offset += iomapp->iomap_bsize - iomapp->iomap_delta;
+ }
+ return pbm; /* Return the number filled */
+}
+
+int
+xfs_iomap(
+ xfs_iocore_t *io,
+ xfs_off_t offset,
+ ssize_t count,
+ int flags,
+ xfs_iomap_t *iomapp,
+ int *niomaps)
+{
+ xfs_mount_t *mp = io->io_mount;
+ xfs_fileoff_t offset_fsb, end_fsb;
+ int error = 0;
+ int lockmode = 0;
+ xfs_bmbt_irec_t imap;
+ int nimaps = 1;
+ int bmapi_flags = 0;
+ int iomap_flags = 0;
+
+ if (XFS_FORCED_SHUTDOWN(mp))
+ return XFS_ERROR(EIO);
+
+ switch (flags &
+ (BMAPI_READ | BMAPI_WRITE | BMAPI_ALLOCATE |
+ BMAPI_UNWRITTEN | BMAPI_DEVICE)) {
+ case BMAPI_READ:
+ xfs_iomap_enter_trace(XFS_IOMAP_READ_ENTER, io, offset, count);
+ lockmode = XFS_LCK_MAP_SHARED(mp, io);
+ bmapi_flags = XFS_BMAPI_ENTIRE;
+ if (flags & BMAPI_IGNSTATE)
+ bmapi_flags |= XFS_BMAPI_IGSTATE;
+ break;
+ case BMAPI_WRITE:
+ xfs_iomap_enter_trace(XFS_IOMAP_WRITE_ENTER, io, offset, count);
+ lockmode = XFS_ILOCK_EXCL|XFS_EXTSIZE_WR;
+ bmapi_flags = 0;
+ XFS_ILOCK(mp, io, lockmode);
+ break;
+ case BMAPI_ALLOCATE:
+ xfs_iomap_enter_trace(XFS_IOMAP_ALLOC_ENTER, io, offset, count);
+ lockmode = XFS_ILOCK_SHARED|XFS_EXTSIZE_RD;
+ bmapi_flags = XFS_BMAPI_ENTIRE;
+ /* Attempt non-blocking lock */
+ if (flags & BMAPI_TRYLOCK) {
+ if (!XFS_ILOCK_NOWAIT(mp, io, lockmode))
+ return XFS_ERROR(EAGAIN);
+ } else {
+ XFS_ILOCK(mp, io, lockmode);
+ }
+ break;
+ case BMAPI_UNWRITTEN:
+ goto phase2;
+ case BMAPI_DEVICE:
+ lockmode = XFS_LCK_MAP_SHARED(mp, io);
+ iomapp->iomap_target = io->io_flags & XFS_IOCORE_RT ?
+ mp->m_rtdev_targp : mp->m_ddev_targp;
+ error = 0;
+ *niomaps = 1;
+ goto out;
+ default:
+ BUG();
+ }
+
+ ASSERT(offset <= mp->m_maxioffset);
+ if ((xfs_fsize_t)offset + count > mp->m_maxioffset)
+ count = mp->m_maxioffset - offset;
+ end_fsb = XFS_B_TO_FSB(mp, (xfs_ufsize_t)offset + count);
+ offset_fsb = XFS_B_TO_FSBT(mp, offset);
+
+ error = XFS_BMAPI(mp, NULL, io, offset_fsb,
+ (xfs_filblks_t)(end_fsb - offset_fsb),
+ bmapi_flags, NULL, 0, &imap,
+ &nimaps, NULL);
+
+ if (error)
+ goto out;
+
+phase2:
+ switch (flags & (BMAPI_WRITE|BMAPI_ALLOCATE|BMAPI_UNWRITTEN)) {
+ case BMAPI_WRITE:
+ /* If we found an extent, return it */
+ if (nimaps && (imap.br_startblock != HOLESTARTBLOCK)) {
+ xfs_iomap_map_trace(XFS_IOMAP_WRITE_MAP, io,
+ offset, count, iomapp, &imap, flags);
+ break;
+ }
+
+ if (flags & (BMAPI_DIRECT|BMAPI_MMAP)) {
+ error = XFS_IOMAP_WRITE_DIRECT(mp, io, offset,
+ count, flags, &imap, &nimaps, nimaps);
+ } else {
+ error = XFS_IOMAP_WRITE_DELAY(mp, io, offset, count,
+ flags, &imap, &nimaps);
+ }
+ if (!error) {
+ xfs_iomap_map_trace(XFS_IOMAP_ALLOC_MAP, io,
+ offset, count, iomapp, &imap, flags);
+ }
+ iomap_flags = IOMAP_NEW;
+ break;
+ case BMAPI_ALLOCATE:
+ /* If we found an extent, return it */
+ XFS_IUNLOCK(mp, io, lockmode);
+ lockmode = 0;
+
+ if (nimaps && !ISNULLSTARTBLOCK(imap.br_startblock)) {
+ xfs_iomap_map_trace(XFS_IOMAP_WRITE_MAP, io,
+ offset, count, iomapp, &imap, flags);
+ break;
+ }
+
+ error = XFS_IOMAP_WRITE_ALLOCATE(mp, io, &imap, &nimaps);
+ break;
+ case BMAPI_UNWRITTEN:
+ lockmode = 0;
+ error = XFS_IOMAP_WRITE_UNWRITTEN(mp, io, offset, count);
+ nimaps = 0;
+ break;
+ }
+
+ if (nimaps) {
+ *niomaps = xfs_imap_to_bmap(io, offset, &imap,
+ iomapp, nimaps, *niomaps, iomap_flags);
+ } else if (niomaps) {
+ *niomaps = 0;
+ }
+
+out:
+ if (lockmode)
+ XFS_IUNLOCK(mp, io, lockmode);
+ return XFS_ERROR(error);
+}
+
+STATIC int
+xfs_flush_space(
+ xfs_inode_t *ip,
+ int *fsynced,
+ int *ioflags)
+{
+ switch (*fsynced) {
+ case 0:
+ if (ip->i_delayed_blks) {
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ xfs_flush_inode(ip);
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
+ *fsynced = 1;
+ } else {
+ *ioflags |= BMAPI_SYNC;
+ *fsynced = 2;
+ }
+ return 0;
+ case 1:
+ *fsynced = 2;
+ *ioflags |= BMAPI_SYNC;
+ return 0;
+ case 2:
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ xfs_flush_device(ip);
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
+ *fsynced = 3;
+ return 0;
+ }
+ return 1;
+}
+
+int
+xfs_iomap_write_direct(
+ xfs_inode_t *ip,
+ loff_t offset,
+ size_t count,
+ int flags,
+ xfs_bmbt_irec_t *ret_imap,
+ int *nmaps,
+ int found)
+{
+ xfs_mount_t *mp = ip->i_mount;
+ xfs_iocore_t *io = &ip->i_iocore;
+ xfs_fileoff_t offset_fsb;
+ xfs_fileoff_t last_fsb;
+ xfs_filblks_t count_fsb;
+ xfs_fsize_t isize;
+ xfs_fsblock_t firstfsb;
+ int nimaps, maps;
+ int error;
+ int bmapi_flag;
+ int rt;
+ xfs_trans_t *tp;
+ xfs_bmbt_irec_t imap[XFS_WRITE_IMAPS], *imapp;
+ xfs_bmap_free_t free_list;
+ int aeof;
+ xfs_filblks_t datablocks;
+ int committed;
+ int numrtextents;
+ uint resblks;
+
+ /*
+ * Make sure that the dquots are there. This doesn't hold
+ * the ilock across a disk read.
+ */
+ error = XFS_QM_DQATTACH(ip->i_mount, ip, XFS_QMOPT_ILOCKED);
+ if (error)
+ return XFS_ERROR(error);
+
+ maps = min(XFS_WRITE_IMAPS, *nmaps);
+ nimaps = maps;
+
+ isize = ip->i_d.di_size;
+ aeof = (offset + count) > isize;
+
+ if (io->io_new_size > isize)
+ isize = io->io_new_size;
+
+ offset_fsb = XFS_B_TO_FSBT(mp, offset);
+ last_fsb = XFS_B_TO_FSB(mp, ((xfs_ufsize_t)(offset + count)));
+ count_fsb = last_fsb - offset_fsb;
+ if (found && (ret_imap->br_startblock == HOLESTARTBLOCK)) {
+ xfs_fileoff_t map_last_fsb;
+
+ map_last_fsb = ret_imap->br_blockcount + ret_imap->br_startoff;
+
+ if (map_last_fsb < last_fsb) {
+ last_fsb = map_last_fsb;
+ count_fsb = last_fsb - offset_fsb;
+ }
+ ASSERT(count_fsb > 0);
+ }
+
+ /*
+ * determine if reserving space on
+ * the data or realtime partition.
+ */
+ if ((rt = XFS_IS_REALTIME_INODE(ip))) {
+ int sbrtextsize, iprtextsize;
+
+ sbrtextsize = mp->m_sb.sb_rextsize;
+ iprtextsize =
+ ip->i_d.di_extsize ? ip->i_d.di_extsize : sbrtextsize;
+ numrtextents = (count_fsb + iprtextsize - 1);
+ do_div(numrtextents, sbrtextsize);
+ datablocks = 0;
+ } else {
+ datablocks = count_fsb;
+ numrtextents = 0;
+ }
+
+ /*
+ * allocate and setup the transaction
+ */
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ tp = xfs_trans_alloc(mp, XFS_TRANS_DIOSTRAT);
+
+ resblks = XFS_DIOSTRAT_SPACE_RES(mp, datablocks);
+
+ error = xfs_trans_reserve(tp, resblks,
+ XFS_WRITE_LOG_RES(mp), numrtextents,
+ XFS_TRANS_PERM_LOG_RES,
+ XFS_WRITE_LOG_COUNT);
+
+ /*
+ * check for running out of space
+ */
+ if (error)
+ /*
+ * Free the transaction structure.
+ */
+ xfs_trans_cancel(tp, 0);
+
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
+
+ if (error)
+ goto error_out; /* Don't return in above if .. trans ..,
+ need lock to return */
+
+ if (XFS_TRANS_RESERVE_BLKQUOTA(mp, tp, ip, resblks)) {
+ error = (EDQUOT);
+ goto error1;
+ }
+ nimaps = 1;
+
+ bmapi_flag = XFS_BMAPI_WRITE;
+ xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
+ xfs_trans_ihold(tp, ip);
+
+ if (!(flags & BMAPI_MMAP) && (offset < ip->i_d.di_size || rt))
+ bmapi_flag |= XFS_BMAPI_PREALLOC;
+
+ /*
+ * issue the bmapi() call to allocate the blocks
+ */
+ XFS_BMAP_INIT(&free_list, &firstfsb);
+ imapp = &imap[0];
+ error = xfs_bmapi(tp, ip, offset_fsb, count_fsb,
+ bmapi_flag, &firstfsb, 0, imapp, &nimaps, &free_list);
+ if (error) {
+ goto error0;
+ }
+
+ /*
+ * complete the transaction
+ */
+
+ error = xfs_bmap_finish(&tp, &free_list, firstfsb, &committed);
+ if (error) {
+ goto error0;
+ }
+
+ error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES, NULL);
+ if (error) {
+ goto error_out;
+ }
+
+ /* copy any maps to caller's array and return any error. */
+ if (nimaps == 0) {
+ error = (ENOSPC);
+ goto error_out;
+ }
+
+ *ret_imap = imap[0];
+ *nmaps = 1;
+ if ( !(io->io_flags & XFS_IOCORE_RT) && !ret_imap->br_startblock) {
+ cmn_err(CE_PANIC,"Access to block zero: fs <%s> inode: %lld "
+ "start_block : %llx start_off : %llx blkcnt : %llx "
+ "extent-state : %x \n",
+ (ip->i_mount)->m_fsname,
+ (long long)ip->i_ino,
+ ret_imap->br_startblock, ret_imap->br_startoff,
+ ret_imap->br_blockcount,ret_imap->br_state);
+ }
+ return 0;
+
+ error0: /* Cancel bmap, unlock inode, and cancel trans */
+ xfs_bmap_cancel(&free_list);
+
+ error1: /* Just cancel transaction */
+ xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT);
+ *nmaps = 0; /* nothing set-up here */
+
+error_out:
+ return XFS_ERROR(error);
+}
+
+int
+xfs_iomap_write_delay(
+ xfs_inode_t *ip,
+ loff_t offset,
+ size_t count,
+ int ioflag,
+ xfs_bmbt_irec_t *ret_imap,
+ int *nmaps)
+{
+ xfs_mount_t *mp = ip->i_mount;
+ xfs_iocore_t *io = &ip->i_iocore;
+ xfs_fileoff_t offset_fsb;
+ xfs_fileoff_t last_fsb;
+ xfs_fsize_t isize;
+ xfs_fsblock_t firstblock;
+ int nimaps;
+ int error;
+ xfs_bmbt_irec_t imap[XFS_WRITE_IMAPS];
+ int aeof;
+ int fsynced = 0;
+
+ ASSERT(ismrlocked(&ip->i_lock, MR_UPDATE) != 0);
+
+ /*
+ * Make sure that the dquots are there. This doesn't hold
+ * the ilock across a disk read.
+ */
+
+ error = XFS_QM_DQATTACH(mp, ip, XFS_QMOPT_ILOCKED);
+ if (error)
+ return XFS_ERROR(error);
+
+retry:
+ isize = ip->i_d.di_size;
+ if (io->io_new_size > isize) {
+ isize = io->io_new_size;
+ }
+
+ aeof = 0;
+ offset_fsb = XFS_B_TO_FSBT(mp, offset);
+ last_fsb = XFS_B_TO_FSB(mp, ((xfs_ufsize_t)(offset + count)));
+ /*
+ * If the caller is doing a write at the end of the file,
+ * then extend the allocation (and the buffer used for the write)
+ * out to the file system's write iosize. We clean up any extra
+ * space left over when the file is closed in xfs_inactive().
+ *
+ * For sync writes, we are flushing delayed allocate space to
+ * try to make additional space available for allocation near
+ * the filesystem full boundary - preallocation hurts in that
+ * situation, of course.
+ */
+ if (!(ioflag & BMAPI_SYNC) && ((offset + count) > ip->i_d.di_size)) {
+ xfs_off_t aligned_offset;
+ xfs_filblks_t count_fsb;
+ unsigned int iosize;
+ xfs_fileoff_t ioalign;
+ int n;
+ xfs_fileoff_t start_fsb;
+
+ /*
+ * If there are any real blocks past eof, then don't
+ * do any speculative allocation.
+ */
+ start_fsb = XFS_B_TO_FSBT(mp,
+ ((xfs_ufsize_t)(offset + count - 1)));
+ count_fsb = XFS_B_TO_FSB(mp, (xfs_ufsize_t)XFS_MAXIOFFSET(mp));
+ while (count_fsb > 0) {
+ nimaps = XFS_WRITE_IMAPS;
+ error = XFS_BMAPI(mp, NULL, io, start_fsb, count_fsb,
+ 0, &firstblock, 0, imap, &nimaps, NULL);
+ if (error) {
+ return error;
+ }
+ for (n = 0; n < nimaps; n++) {
+ if ( !(io->io_flags & XFS_IOCORE_RT) &&
+ !imap[n].br_startblock) {
+ cmn_err(CE_PANIC,"Access to block "
+ "zero: fs <%s> inode: %lld "
+ "start_block : %llx start_off "
+ ": %llx blkcnt : %llx "
+ "extent-state : %x \n",
+ (ip->i_mount)->m_fsname,
+ (long long)ip->i_ino,
+ imap[n].br_startblock,
+ imap[n].br_startoff,
+ imap[n].br_blockcount,
+ imap[n].br_state);
+ }
+ if ((imap[n].br_startblock != HOLESTARTBLOCK) &&
+ (imap[n].br_startblock != DELAYSTARTBLOCK)) {
+ goto write_map;
+ }
+ start_fsb += imap[n].br_blockcount;
+ count_fsb -= imap[n].br_blockcount;
+ }
+ }
+ iosize = mp->m_writeio_blocks;
+ aligned_offset = XFS_WRITEIO_ALIGN(mp, (offset + count - 1));
+ ioalign = XFS_B_TO_FSBT(mp, aligned_offset);
+ last_fsb = ioalign + iosize;
+ aeof = 1;
+ }
+write_map:
+ nimaps = XFS_WRITE_IMAPS;
+ firstblock = NULLFSBLOCK;
+
+ /*
+ * If mounted with the "-o swalloc" option, roundup the allocation
+ * request to a stripe width boundary if the file size is >=
+ * stripe width and we are allocating past the allocation eof.
+ */
+ if (!(io->io_flags & XFS_IOCORE_RT) && mp->m_swidth
+ && (mp->m_flags & XFS_MOUNT_SWALLOC)
+ && (isize >= XFS_FSB_TO_B(mp, mp->m_swidth)) && aeof) {
+ int eof;
+ xfs_fileoff_t new_last_fsb;
+
+ new_last_fsb = roundup_64(last_fsb, mp->m_swidth);
+ error = xfs_bmap_eof(ip, new_last_fsb, XFS_DATA_FORK, &eof);
+ if (error) {
+ return error;
+ }
+ if (eof) {
+ last_fsb = new_last_fsb;
+ }
+ /*
+ * Roundup the allocation request to a stripe unit (m_dalign) boundary
+ * if the file size is >= stripe unit size, and we are allocating past
+ * the allocation eof.
+ */
+ } else if (!(io->io_flags & XFS_IOCORE_RT) && mp->m_dalign &&
+ (isize >= XFS_FSB_TO_B(mp, mp->m_dalign)) && aeof) {
+ int eof;
+ xfs_fileoff_t new_last_fsb;
+ new_last_fsb = roundup_64(last_fsb, mp->m_dalign);
+ error = xfs_bmap_eof(ip, new_last_fsb, XFS_DATA_FORK, &eof);
+ if (error) {
+ return error;
+ }
+ if (eof) {
+ last_fsb = new_last_fsb;
+ }
+ /*
+ * Round up the allocation request to a real-time extent boundary
+ * if the file is on the real-time subvolume.
+ */
+ } else if (io->io_flags & XFS_IOCORE_RT && aeof) {
+ int eof;
+ xfs_fileoff_t new_last_fsb;
+
+ new_last_fsb = roundup_64(last_fsb, mp->m_sb.sb_rextsize);
+ error = XFS_BMAP_EOF(mp, io, new_last_fsb, XFS_DATA_FORK, &eof);
+ if (error) {
+ return error;
+ }
+ if (eof)
+ last_fsb = new_last_fsb;
+ }
+ error = xfs_bmapi(NULL, ip, offset_fsb,
+ (xfs_filblks_t)(last_fsb - offset_fsb),
+ XFS_BMAPI_DELAY | XFS_BMAPI_WRITE |
+ XFS_BMAPI_ENTIRE, &firstblock, 1, imap,
+ &nimaps, NULL);
+ /*
+ * This can be EDQUOT, if nimaps == 0
+ */
+ if (error && (error != ENOSPC)) {
+ return XFS_ERROR(error);
+ }
+ /*
+ * If bmapi returned us nothing, and if we didn't get back EDQUOT,
+ * then we must have run out of space.
+ */
+ if (nimaps == 0) {
+ xfs_iomap_enter_trace(XFS_IOMAP_WRITE_NOSPACE,
+ io, offset, count);
+ if (xfs_flush_space(ip, &fsynced, &ioflag))
+ return XFS_ERROR(ENOSPC);
+
+ error = 0;
+ goto retry;
+ }
+
+ *ret_imap = imap[0];
+ *nmaps = 1;
+ if ( !(io->io_flags & XFS_IOCORE_RT) && !ret_imap->br_startblock) {
+ cmn_err(CE_PANIC,"Access to block zero: fs <%s> inode: %lld "
+ "start_block : %llx start_off : %llx blkcnt : %llx "
+ "extent-state : %x \n",
+ (ip->i_mount)->m_fsname,
+ (long long)ip->i_ino,
+ ret_imap->br_startblock, ret_imap->br_startoff,
+ ret_imap->br_blockcount,ret_imap->br_state);
+ }
+ return 0;
+}
+
+/*
+ * Pass in a delayed allocate extent, convert it to real extents;
+ * return to the caller the extent we create which maps on top of
+ * the originating callers request.
+ *
+ * Called without a lock on the inode.
+ */
+int
+xfs_iomap_write_allocate(
+ xfs_inode_t *ip,
+ xfs_bmbt_irec_t *map,
+ int *retmap)
+{
+ xfs_mount_t *mp = ip->i_mount;
+ xfs_iocore_t *io = &ip->i_iocore;
+ xfs_fileoff_t offset_fsb, last_block;
+ xfs_fileoff_t end_fsb, map_start_fsb;
+ xfs_fsblock_t first_block;
+ xfs_bmap_free_t free_list;
+ xfs_filblks_t count_fsb;
+ xfs_bmbt_irec_t imap[XFS_STRAT_WRITE_IMAPS];
+ xfs_trans_t *tp;
+ int i, nimaps, committed;
+ int error = 0;
+ int nres;
+
+ *retmap = 0;
+
+ /*
+ * Make sure that the dquots are there.
+ */
+ if ((error = XFS_QM_DQATTACH(mp, ip, 0)))
+ return XFS_ERROR(error);
+
+ offset_fsb = map->br_startoff;
+ count_fsb = map->br_blockcount;
+ map_start_fsb = offset_fsb;
+
+ XFS_STATS_ADD(xs_xstrat_bytes, XFS_FSB_TO_B(mp, count_fsb));
+
+ while (count_fsb != 0) {
+ /*
+ * Set up a transaction with which to allocate the
+ * backing store for the file. Do allocations in a
+ * loop until we get some space in the range we are
+ * interested in. The other space that might be allocated
+ * is in the delayed allocation extent on which we sit
+ * but before our buffer starts.
+ */
+
+ nimaps = 0;
+ while (nimaps == 0) {
+ tp = xfs_trans_alloc(mp, XFS_TRANS_STRAT_WRITE);
+ nres = XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK);
+ error = xfs_trans_reserve(tp, nres,
+ XFS_WRITE_LOG_RES(mp),
+ 0, XFS_TRANS_PERM_LOG_RES,
+ XFS_WRITE_LOG_COUNT);
+ if (error == ENOSPC) {
+ error = xfs_trans_reserve(tp, 0,
+ XFS_WRITE_LOG_RES(mp),
+ 0,
+ XFS_TRANS_PERM_LOG_RES,
+ XFS_WRITE_LOG_COUNT);
+ }
+ if (error) {
+ xfs_trans_cancel(tp, 0);
+ return XFS_ERROR(error);
+ }
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
+ xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
+ xfs_trans_ihold(tp, ip);
+
+ XFS_BMAP_INIT(&free_list, &first_block);
+
+ nimaps = XFS_STRAT_WRITE_IMAPS;
+ /*
+ * Ensure we don't go beyond eof - it is possible
+ * the extents changed since we did the read call,
+ * we dropped the ilock in the interim.
+ */
+
+ end_fsb = XFS_B_TO_FSB(mp, ip->i_d.di_size);
+ xfs_bmap_last_offset(NULL, ip, &last_block,
+ XFS_DATA_FORK);
+ last_block = XFS_FILEOFF_MAX(last_block, end_fsb);
+ if ((map_start_fsb + count_fsb) > last_block) {
+ count_fsb = last_block - map_start_fsb;
+ if (count_fsb == 0) {
+ error = EAGAIN;
+ goto trans_cancel;
+ }
+ }
+
+ /* Go get the actual blocks */
+ error = xfs_bmapi(tp, ip, map_start_fsb, count_fsb,
+ XFS_BMAPI_WRITE, &first_block, 1,
+ imap, &nimaps, &free_list);
+ if (error)
+ goto trans_cancel;
+
+ error = xfs_bmap_finish(&tp, &free_list,
+ first_block, &committed);
+ if (error)
+ goto trans_cancel;
+
+ error = xfs_trans_commit(tp,
+ XFS_TRANS_RELEASE_LOG_RES, NULL);
+ if (error)
+ goto error0;
+
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ }
+
+ /*
+ * See if we were able to allocate an extent that
+ * covers at least part of the callers request
+ */
+
+ for (i = 0; i < nimaps; i++) {
+ if ( !(io->io_flags & XFS_IOCORE_RT) &&
+ !imap[i].br_startblock) {
+ cmn_err(CE_PANIC,"Access to block zero: "
+ "fs <%s> inode: %lld "
+ "start_block : %llx start_off : %llx "
+ "blkcnt : %llx extent-state : %x \n",
+ (ip->i_mount)->m_fsname,
+ (long long)ip->i_ino,
+ imap[i].br_startblock,
+ imap[i].br_startoff,
+ imap[i].br_blockcount,imap[i].br_state);
+ }
+ if ((map->br_startoff >= imap[i].br_startoff) &&
+ (map->br_startoff < (imap[i].br_startoff +
+ imap[i].br_blockcount))) {
+ *map = imap[i];
+ *retmap = 1;
+ XFS_STATS_INC(xs_xstrat_quick);
+ return 0;
+ }
+ count_fsb -= imap[i].br_blockcount;
+ }
+
+ /* So far we have not mapped the requested part of the
+ * file, just surrounding data, try again.
+ */
+ nimaps--;
+ offset_fsb = imap[nimaps].br_startoff +
+ imap[nimaps].br_blockcount;
+ map_start_fsb = offset_fsb;
+ }
+
+trans_cancel:
+ xfs_bmap_cancel(&free_list);
+ xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT);
+error0:
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ return XFS_ERROR(error);
+}
+
+int
+xfs_iomap_write_unwritten(
+ xfs_inode_t *ip,
+ loff_t offset,
+ size_t count)
+{
+ xfs_mount_t *mp = ip->i_mount;
+ xfs_iocore_t *io = &ip->i_iocore;
+ xfs_trans_t *tp;
+ xfs_fileoff_t offset_fsb;
+ xfs_filblks_t count_fsb;
+ xfs_filblks_t numblks_fsb;
+ xfs_bmbt_irec_t imap;
+ int committed;
+ int error;
+ int nres;
+ int nimaps;
+ xfs_fsblock_t firstfsb;
+ xfs_bmap_free_t free_list;
+
+ xfs_iomap_enter_trace(XFS_IOMAP_UNWRITTEN,
+ &ip->i_iocore, offset, count);
+
+ offset_fsb = XFS_B_TO_FSBT(mp, offset);
+ count_fsb = XFS_B_TO_FSB(mp, (xfs_ufsize_t)offset + count);
+ count_fsb = (xfs_filblks_t)(count_fsb - offset_fsb);
+
+ do {
+ nres = XFS_DIOSTRAT_SPACE_RES(mp, 0);
+
+ /*
+ * set up a transaction to convert the range of extents
+ * from unwritten to real. Do allocations in a loop until
+ * we have covered the range passed in.
+ */
+
+ tp = xfs_trans_alloc(mp, XFS_TRANS_STRAT_WRITE);
+ error = xfs_trans_reserve(tp, nres,
+ XFS_WRITE_LOG_RES(mp), 0,
+ XFS_TRANS_PERM_LOG_RES,
+ XFS_WRITE_LOG_COUNT);
+ if (error) {
+ xfs_trans_cancel(tp, 0);
+ goto error0;
+ }
+
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
+ xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
+ xfs_trans_ihold(tp, ip);
+
+ /*
+ * Modify the unwritten extent state of the buffer.
+ */
+ XFS_BMAP_INIT(&free_list, &firstfsb);
+ nimaps = 1;
+ error = xfs_bmapi(tp, ip, offset_fsb, count_fsb,
+ XFS_BMAPI_WRITE, &firstfsb,
+ 1, &imap, &nimaps, &free_list);
+ if (error)
+ goto error_on_bmapi_transaction;
+
+ error = xfs_bmap_finish(&(tp), &(free_list),
+ firstfsb, &committed);
+ if (error)
+ goto error_on_bmapi_transaction;
+
+ error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES, NULL);
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ if (error)
+ goto error0;
+
+ if ( !(io->io_flags & XFS_IOCORE_RT) && !imap.br_startblock) {
+ cmn_err(CE_PANIC,"Access to block zero: fs <%s> "
+ "inode: %lld start_block : %llx start_off : "
+ "%llx blkcnt : %llx extent-state : %x \n",
+ (ip->i_mount)->m_fsname,
+ (long long)ip->i_ino,
+ imap.br_startblock,imap.br_startoff,
+ imap.br_blockcount,imap.br_state);
+ }
+
+ if ((numblks_fsb = imap.br_blockcount) == 0) {
+ /*
+ * The numblks_fsb value should always get
+ * smaller, otherwise the loop is stuck.
+ */
+ ASSERT(imap.br_blockcount);
+ break;
+ }
+ offset_fsb += numblks_fsb;
+ count_fsb -= numblks_fsb;
+ } while (count_fsb > 0);
+
+ return 0;
+
+error_on_bmapi_transaction:
+ xfs_bmap_cancel(&free_list);
+ xfs_trans_cancel(tp, (XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT));
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+error0:
+ return XFS_ERROR(error);
+}
diff --git a/fs/xfs/xfs_iomap.h b/fs/xfs/xfs_iomap.h
new file mode 100644
index 00000000000000..31c91087cb3343
--- /dev/null
+++ b/fs/xfs/xfs_iomap.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2003,2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+
+
+#ifndef __XFS_IOMAP_H__
+#define __XFS_IOMAP_H__
+
+#define IOMAP_DADDR_NULL ((xfs_daddr_t) (-1LL))
+
+
+typedef enum { /* iomap_flags values */
+ IOMAP_EOF = 0x01, /* mapping contains EOF */
+ IOMAP_HOLE = 0x02, /* mapping covers a hole */
+ IOMAP_DELAY = 0x04, /* mapping covers delalloc region */
+ IOMAP_REALTIME = 0x10, /* mapping on the realtime device */
+ IOMAP_UNWRITTEN = 0x20, /* mapping covers allocated */
+ /* but uninitialized file data */
+ IOMAP_NEW = 0x40 /* just allocate */
+} iomap_flags_t;
+
+typedef enum {
+ /* base extent manipulation calls */
+ BMAPI_READ = (1 << 0), /* read extents */
+ BMAPI_WRITE = (1 << 1), /* create extents */
+ BMAPI_ALLOCATE = (1 << 2), /* delayed allocate to real extents */
+ BMAPI_UNWRITTEN = (1 << 3), /* unwritten extents to real extents */
+ /* modifiers */
+ BMAPI_IGNSTATE = (1 << 4), /* ignore unwritten state on read */
+ BMAPI_DIRECT = (1 << 5), /* direct instead of buffered write */
+ BMAPI_MMAP = (1 << 6), /* allocate for mmap write */
+ BMAPI_SYNC = (1 << 7), /* sync write to flush delalloc space */
+ BMAPI_TRYLOCK = (1 << 8), /* non-blocking request */
+ BMAPI_DEVICE = (1 << 9), /* we only want to know the device */
+} bmapi_flags_t;
+
+
+/*
+ * xfs_iomap_t: File system I/O map
+ *
+ * The iomap_bn field is expressed in 512-byte blocks, and is where the
+ * mapping starts on disk.
+ *
+ * The iomap_offset, iomap_bsize and iomap_delta fields are in bytes.
+ * iomap_offset is the offset of the mapping in the file itself.
+ * iomap_bsize is the size of the mapping, iomap_delta is the
+ * desired data's offset into the mapping, given the offset supplied
+ * to the file I/O map routine.
+ *
+ * When a request is made to read beyond the logical end of the object,
+ * iomap_size may be set to 0, but iomap_offset and iomap_length should be set
+ * to the actual amount of underlying storage that has been allocated, if any.
+ */
+
+typedef struct xfs_iomap {
+ xfs_daddr_t iomap_bn; /* first 512b blk of mapping */
+ xfs_buftarg_t *iomap_target;
+ loff_t iomap_offset; /* offset of mapping, bytes */
+ loff_t iomap_bsize; /* size of mapping, bytes */
+ size_t iomap_delta; /* offset into mapping, bytes */
+ iomap_flags_t iomap_flags;
+} xfs_iomap_t;
+
+struct xfs_iocore;
+struct xfs_inode;
+struct xfs_bmbt_irec;
+
+extern int xfs_iomap(struct xfs_iocore *, xfs_off_t, ssize_t, int,
+ struct xfs_iomap *, int *);
+extern int xfs_iomap_write_direct(struct xfs_inode *, loff_t, size_t,
+ int, struct xfs_bmbt_irec *, int *, int);
+extern int xfs_iomap_write_delay(struct xfs_inode *, loff_t, size_t, int,
+ struct xfs_bmbt_irec *, int *);
+extern int xfs_iomap_write_allocate(struct xfs_inode *,
+ struct xfs_bmbt_irec *, int *);
+extern int xfs_iomap_write_unwritten(struct xfs_inode *, loff_t, size_t);
+
+#endif /* __XFS_IOMAP_H__*/
diff --git a/fs/xfs/xfs_itable.c b/fs/xfs/xfs_itable.c
new file mode 100644
index 00000000000000..0e86be199cd105
--- /dev/null
+++ b/fs/xfs/xfs_itable.c
@@ -0,0 +1,806 @@
+/*
+ * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_ag.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_btree.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode.h"
+#include "xfs_ialloc.h"
+#include "xfs_itable.h"
+#include "xfs_error.h"
+
+#ifndef HAVE_USERACC
+#define useracc(ubuffer, size, flags, foo) (0)
+#define unuseracc(ubuffer, size, flags)
+#endif
+
+/*
+ * Return stat information for one inode.
+ * Return 0 if ok, else errno.
+ */
+int /* error status */
+xfs_bulkstat_one(
+ xfs_mount_t *mp, /* mount point for filesystem */
+ xfs_ino_t ino, /* inode number to get data for */
+ void __user *buffer, /* buffer to place output in */
+ int ubsize, /* size of buffer */
+ void *private_data, /* my private data */
+ xfs_daddr_t bno, /* starting bno of inode cluster */
+ int *ubused, /* bytes used by me */
+ void *dibuff, /* on-disk inode buffer */
+ int *stat) /* BULKSTAT_RV_... */
+{
+ xfs_bstat_t *buf; /* return buffer */
+ int error; /* error value */
+ xfs_dinode_t *dip; /* dinode inode pointer */
+ xfs_dinode_core_t *dic; /* dinode core info pointer */
+ xfs_inode_t *ip = NULL; /* incore inode pointer */
+ xfs_arch_t arch; /* these are set according to */
+
+ dip = (xfs_dinode_t *)dibuff;
+
+ if (!buffer || ino == mp->m_sb.sb_rbmino || ino == mp->m_sb.sb_rsumino ||
+ (XFS_SB_VERSION_HASQUOTA(&mp->m_sb) &&
+ (ino == mp->m_sb.sb_uquotino || ino == mp->m_sb.sb_gquotino))) {
+ *stat = BULKSTAT_RV_NOTHING;
+ return XFS_ERROR(EINVAL);
+ }
+ if (ubsize < sizeof(*buf)) {
+ *stat = BULKSTAT_RV_NOTHING;
+ return XFS_ERROR(ENOMEM);
+ }
+
+ buf = kmem_alloc(sizeof(*buf), KM_SLEEP);
+
+ if (dip == NULL) {
+ /* We're not being passed a pointer to a dinode. This happens
+ * if BULKSTAT_FG_IGET is selected. Do the iget.
+ */
+ error = xfs_iget(mp, NULL, ino, 0, XFS_ILOCK_SHARED, &ip, bno);
+ if (error) {
+ *stat = BULKSTAT_RV_NOTHING;
+ return error;
+ }
+ ASSERT(ip != NULL);
+ ASSERT(ip->i_blkno != (xfs_daddr_t)0);
+ if (ip->i_d.di_mode == 0) {
+ xfs_iput_new(ip, XFS_ILOCK_SHARED);
+ *stat = BULKSTAT_RV_NOTHING;
+ kmem_free(buf, sizeof(*buf));
+ return XFS_ERROR(ENOENT);
+ }
+ dic = &ip->i_d;
+ arch = ARCH_NOCONVERT; /* in-core! */
+ ASSERT(dic != NULL);
+
+ /* xfs_iget returns the following without needing
+ * further change.
+ */
+ buf->bs_nlink = dic->di_nlink;
+ buf->bs_projid = dic->di_projid;
+
+ } else {
+ dic = &dip->di_core;
+ ASSERT(dic != NULL);
+
+ /* buffer dinode_core is in on-disk arch */
+ arch = ARCH_CONVERT;
+
+ /*
+ * The inode format changed when we moved the link count and
+ * made it 32 bits long. If this is an old format inode,
+ * convert it in memory to look like a new one. If it gets
+ * flushed to disk we will convert back before flushing or
+ * logging it. We zero out the new projid field and the old link
+ * count field. We'll handle clearing the pad field (the remains
+ * of the old uuid field) when we actually convert the inode to
+ * the new format. We don't change the version number so that we
+ * can distinguish this from a real new format inode.
+ */
+ if (INT_GET(dic->di_version, arch) == XFS_DINODE_VERSION_1) {
+ buf->bs_nlink = INT_GET(dic->di_onlink, arch);
+ buf->bs_projid = 0;
+ }
+ else {
+ buf->bs_nlink = INT_GET(dic->di_nlink, arch);
+ buf->bs_projid = INT_GET(dic->di_projid, arch);
+ }
+
+ }
+
+ buf->bs_ino = ino;
+ buf->bs_mode = INT_GET(dic->di_mode, arch);
+ buf->bs_uid = INT_GET(dic->di_uid, arch);
+ buf->bs_gid = INT_GET(dic->di_gid, arch);
+ buf->bs_size = INT_GET(dic->di_size, arch);
+ buf->bs_atime.tv_sec = INT_GET(dic->di_atime.t_sec, arch);
+ buf->bs_atime.tv_nsec = INT_GET(dic->di_atime.t_nsec, arch);
+ buf->bs_mtime.tv_sec = INT_GET(dic->di_mtime.t_sec, arch);
+ buf->bs_mtime.tv_nsec = INT_GET(dic->di_mtime.t_nsec, arch);
+ buf->bs_ctime.tv_sec = INT_GET(dic->di_ctime.t_sec, arch);
+ buf->bs_ctime.tv_nsec = INT_GET(dic->di_ctime.t_nsec, arch);
+ buf->bs_xflags = xfs_dic2xflags(dic, arch);
+ buf->bs_extsize = INT_GET(dic->di_extsize, arch) << mp->m_sb.sb_blocklog;
+ buf->bs_extents = INT_GET(dic->di_nextents, arch);
+ buf->bs_gen = INT_GET(dic->di_gen, arch);
+ memset(buf->bs_pad, 0, sizeof(buf->bs_pad));
+ buf->bs_dmevmask = INT_GET(dic->di_dmevmask, arch);
+ buf->bs_dmstate = INT_GET(dic->di_dmstate, arch);
+ buf->bs_aextents = INT_GET(dic->di_anextents, arch);
+
+ switch (INT_GET(dic->di_format, arch)) {
+ case XFS_DINODE_FMT_DEV:
+ if ( ip ) {
+ buf->bs_rdev = ip->i_df.if_u2.if_rdev;
+ } else {
+ buf->bs_rdev = INT_GET(dip->di_u.di_dev, arch);
+ }
+
+ buf->bs_blksize = BLKDEV_IOSIZE;
+ buf->bs_blocks = 0;
+ break;
+ case XFS_DINODE_FMT_LOCAL:
+ case XFS_DINODE_FMT_UUID:
+ buf->bs_rdev = 0;
+ buf->bs_blksize = mp->m_sb.sb_blocksize;
+ buf->bs_blocks = 0;
+ break;
+ case XFS_DINODE_FMT_EXTENTS:
+ case XFS_DINODE_FMT_BTREE:
+ buf->bs_rdev = 0;
+ buf->bs_blksize = mp->m_sb.sb_blocksize;
+ if ( ip ) {
+ buf->bs_blocks = INT_GET(dic->di_nblocks, arch) + ip->i_delayed_blks;
+ } else {
+ buf->bs_blocks = INT_GET(dic->di_nblocks, arch);
+ }
+ break;
+ }
+
+ if (ip) {
+ xfs_iput(ip, XFS_ILOCK_SHARED);
+ }
+
+ if (copy_to_user(buffer, buf, sizeof(*buf))) {
+ kmem_free(buf, sizeof(*buf));
+ *stat = BULKSTAT_RV_NOTHING;
+ return EFAULT;
+ }
+
+ kmem_free(buf, sizeof(*buf));
+ *stat = BULKSTAT_RV_DIDONE;
+ if (ubused)
+ *ubused = sizeof(*buf);
+ return 0;
+}
+
+/*
+ * Return stat information in bulk (by-inode) for the filesystem.
+ */
+int /* error status */
+xfs_bulkstat(
+ xfs_mount_t *mp, /* mount point for filesystem */
+ xfs_ino_t *lastinop, /* last inode returned */
+ int *ubcountp, /* size of buffer/count returned */
+ bulkstat_one_pf formatter, /* func that'd fill a single buf */
+ void *private_data,/* private data for formatter */
+ size_t statstruct_size, /* sizeof struct filling */
+ char __user *ubuffer, /* buffer with inode stats */
+ int flags, /* defined in xfs_itable.h */
+ int *done) /* 1 if there're more stats to get */
+{
+ xfs_agblock_t agbno=0;/* allocation group block number */
+ xfs_buf_t *agbp; /* agi header buffer */
+ xfs_agi_t *agi; /* agi header data */
+ xfs_agino_t agino; /* inode # in allocation group */
+ xfs_agnumber_t agno; /* allocation group number */
+ xfs_daddr_t bno; /* inode cluster start daddr */
+ int chunkidx; /* current index into inode chunk */
+ int clustidx; /* current index into inode cluster */
+ xfs_btree_cur_t *cur; /* btree cursor for ialloc btree */
+ int end_of_ag; /* set if we've seen the ag end */
+ int error; /* error code */
+ int fmterror;/* bulkstat formatter result */
+ __int32_t gcnt; /* current btree rec's count */
+ xfs_inofree_t gfree; /* current btree rec's free mask */
+ xfs_agino_t gino; /* current btree rec's start inode */
+ int i; /* loop index */
+ int icount; /* count of inodes good in irbuf */
+ xfs_ino_t ino; /* inode number (filesystem) */
+ xfs_inobt_rec_t *irbp; /* current irec buffer pointer */
+ xfs_inobt_rec_t *irbuf; /* start of irec buffer */
+ xfs_inobt_rec_t *irbufend; /* end of good irec buffer entries */
+ xfs_ino_t lastino=0; /* last inode number returned */
+ int nbcluster; /* # of blocks in a cluster */
+ int nicluster; /* # of inodes in a cluster */
+ int nimask; /* mask for inode clusters */
+ int nirbuf; /* size of irbuf */
+ int rval; /* return value error code */
+ int tmp; /* result value from btree calls */
+ int ubcount; /* size of user's buffer */
+ int ubleft; /* bytes left in user's buffer */
+ char __user *ubufp; /* pointer into user's buffer */
+ int ubelem; /* spaces used in user's buffer */
+ int ubused; /* bytes used by formatter */
+ xfs_buf_t *bp; /* ptr to on-disk inode cluster buf */
+ xfs_dinode_t *dip; /* ptr into bp for specific inode */
+ xfs_inode_t *ip; /* ptr to in-core inode struct */
+
+ /*
+ * Get the last inode value, see if there's nothing to do.
+ */
+ ino = (xfs_ino_t)*lastinop;
+ dip = NULL;
+ agno = XFS_INO_TO_AGNO(mp, ino);
+ agino = XFS_INO_TO_AGINO(mp, ino);
+ if (agno >= mp->m_sb.sb_agcount ||
+ ino != XFS_AGINO_TO_INO(mp, agno, agino)) {
+ *done = 1;
+ *ubcountp = 0;
+ return 0;
+ }
+ ubcount = *ubcountp; /* statstruct's */
+ ubleft = ubcount * statstruct_size; /* bytes */
+ *ubcountp = ubelem = 0;
+ *done = 0;
+ fmterror = 0;
+ ubufp = ubuffer;
+ nicluster = mp->m_sb.sb_blocksize >= XFS_INODE_CLUSTER_SIZE(mp) ?
+ mp->m_sb.sb_inopblock :
+ (XFS_INODE_CLUSTER_SIZE(mp) >> mp->m_sb.sb_inodelog);
+ nimask = ~(nicluster - 1);
+ nbcluster = nicluster >> mp->m_sb.sb_inopblog;
+ /*
+ * Lock down the user's buffer. If a buffer was not sent, as in the case
+ * disk quota code calls here, we skip this.
+ */
+ if (ubuffer &&
+ (error = useracc(ubuffer, ubcount * statstruct_size,
+ (B_READ|B_PHYS), NULL))) {
+ return error;
+ }
+ /*
+ * Allocate a page-sized buffer for inode btree records.
+ * We could try allocating something smaller, but for normal
+ * calls we'll always (potentially) need the whole page.
+ */
+ irbuf = kmem_alloc(NBPC, KM_SLEEP);
+ nirbuf = NBPC / sizeof(*irbuf);
+ /*
+ * Loop over the allocation groups, starting from the last
+ * inode returned; 0 means start of the allocation group.
+ */
+ rval = 0;
+ while (ubleft >= statstruct_size && agno < mp->m_sb.sb_agcount) {
+ bp = NULL;
+ down_read(&mp->m_peraglock);
+ error = xfs_ialloc_read_agi(mp, NULL, agno, &agbp);
+ up_read(&mp->m_peraglock);
+ if (error) {
+ /*
+ * Skip this allocation group and go to the next one.
+ */
+ agno++;
+ agino = 0;
+ continue;
+ }
+ agi = XFS_BUF_TO_AGI(agbp);
+ /*
+ * Allocate and initialize a btree cursor for ialloc btree.
+ */
+ cur = xfs_btree_init_cursor(mp, NULL, agbp, agno, XFS_BTNUM_INO,
+ (xfs_inode_t *)0, 0);
+ irbp = irbuf;
+ irbufend = irbuf + nirbuf;
+ end_of_ag = 0;
+ /*
+ * If we're returning in the middle of an allocation group,
+ * we need to get the remainder of the chunk we're in.
+ */
+ if (agino > 0) {
+ /*
+ * Lookup the inode chunk that this inode lives in.
+ */
+ error = xfs_inobt_lookup_le(cur, agino, 0, 0, &tmp);
+ if (!error && /* no I/O error */
+ tmp && /* lookup succeeded */
+ /* got the record, should always work */
+ !(error = xfs_inobt_get_rec(cur, &gino, &gcnt,
+ &gfree, &i, ARCH_NOCONVERT)) &&
+ i == 1 &&
+ /* this is the right chunk */
+ agino < gino + XFS_INODES_PER_CHUNK &&
+ /* lastino was not last in chunk */
+ (chunkidx = agino - gino + 1) <
+ XFS_INODES_PER_CHUNK &&
+ /* there are some left allocated */
+ XFS_INOBT_MASKN(chunkidx,
+ XFS_INODES_PER_CHUNK - chunkidx) & ~gfree) {
+ /*
+ * Grab the chunk record. Mark all the
+ * uninteresting inodes (because they're
+ * before our start point) free.
+ */
+ for (i = 0; i < chunkidx; i++) {
+ if (XFS_INOBT_MASK(i) & ~gfree)
+ gcnt++;
+ }
+ gfree |= XFS_INOBT_MASKN(0, chunkidx);
+ INT_SET(irbp->ir_startino, ARCH_CONVERT, gino);
+ INT_SET(irbp->ir_freecount, ARCH_CONVERT, gcnt);
+ INT_SET(irbp->ir_free, ARCH_CONVERT, gfree);
+ irbp++;
+ agino = gino + XFS_INODES_PER_CHUNK;
+ icount = XFS_INODES_PER_CHUNK - gcnt;
+ } else {
+ /*
+ * If any of those tests failed, bump the
+ * inode number (just in case).
+ */
+ agino++;
+ icount = 0;
+ }
+ /*
+ * In any case, increment to the next record.
+ */
+ if (!error)
+ error = xfs_inobt_increment(cur, 0, &tmp);
+ } else {
+ /*
+ * Start of ag. Lookup the first inode chunk.
+ */
+ error = xfs_inobt_lookup_ge(cur, 0, 0, 0, &tmp);
+ icount = 0;
+ }
+ /*
+ * Loop through inode btree records in this ag,
+ * until we run out of inodes or space in the buffer.
+ */
+ while (irbp < irbufend && icount < ubcount) {
+ /*
+ * Loop as long as we're unable to read the
+ * inode btree.
+ */
+ while (error) {
+ agino += XFS_INODES_PER_CHUNK;
+ if (XFS_AGINO_TO_AGBNO(mp, agino) >=
+ INT_GET(agi->agi_length, ARCH_CONVERT))
+ break;
+ error = xfs_inobt_lookup_ge(cur, agino, 0, 0,
+ &tmp);
+ }
+ /*
+ * If ran off the end of the ag either with an error,
+ * or the normal way, set end and stop collecting.
+ */
+ if (error ||
+ (error = xfs_inobt_get_rec(cur, &gino, &gcnt,
+ &gfree, &i, ARCH_NOCONVERT)) ||
+ i == 0) {
+ end_of_ag = 1;
+ break;
+ }
+ /*
+ * If this chunk has any allocated inodes, save it.
+ */
+ if (gcnt < XFS_INODES_PER_CHUNK) {
+ INT_SET(irbp->ir_startino, ARCH_CONVERT, gino);
+ INT_SET(irbp->ir_freecount, ARCH_CONVERT, gcnt);
+ INT_SET(irbp->ir_free, ARCH_CONVERT, gfree);
+ irbp++;
+ icount += XFS_INODES_PER_CHUNK - gcnt;
+ }
+ /*
+ * Set agino to after this chunk and bump the cursor.
+ */
+ agino = gino + XFS_INODES_PER_CHUNK;
+ error = xfs_inobt_increment(cur, 0, &tmp);
+ }
+ /*
+ * Drop the btree buffers and the agi buffer.
+ * We can't hold any of the locks these represent
+ * when calling iget.
+ */
+ xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
+ xfs_buf_relse(agbp);
+ /*
+ * Now format all the good inodes into the user's buffer.
+ */
+ irbufend = irbp;
+ for (irbp = irbuf;
+ irbp < irbufend && ubleft >= statstruct_size; irbp++) {
+ /*
+ * Read-ahead the next chunk's worth of inodes.
+ */
+ if (&irbp[1] < irbufend) {
+ /*
+ * Loop over all clusters in the next chunk.
+ * Do a readahead if there are any allocated
+ * inodes in that cluster.
+ */
+ for (agbno = XFS_AGINO_TO_AGBNO(mp,
+ INT_GET(irbp[1].ir_startino, ARCH_CONVERT)),
+ chunkidx = 0;
+ chunkidx < XFS_INODES_PER_CHUNK;
+ chunkidx += nicluster,
+ agbno += nbcluster) {
+ if (XFS_INOBT_MASKN(chunkidx,
+ nicluster) &
+ ~(INT_GET(irbp[1].ir_free, ARCH_CONVERT)))
+ xfs_btree_reada_bufs(mp, agno,
+ agbno, nbcluster);
+ }
+ }
+ /*
+ * Now process this chunk of inodes.
+ */
+ for (agino = INT_GET(irbp->ir_startino, ARCH_CONVERT), chunkidx = 0, clustidx = 0;
+ ubleft > 0 &&
+ INT_GET(irbp->ir_freecount, ARCH_CONVERT) < XFS_INODES_PER_CHUNK;
+ chunkidx++, clustidx++, agino++) {
+ ASSERT(chunkidx < XFS_INODES_PER_CHUNK);
+ /*
+ * Recompute agbno if this is the
+ * first inode of the cluster.
+ *
+ * Careful with clustidx. There can be
+ * multple clusters per chunk, a single
+ * cluster per chunk or a cluster that has
+ * inodes represented from several different
+ * chunks (if blocksize is large).
+ *
+ * Because of this, the starting clustidx is
+ * initialized to zero in this loop but must
+ * later be reset after reading in the cluster
+ * buffer.
+ */
+ if ((chunkidx & (nicluster - 1)) == 0) {
+ agbno = XFS_AGINO_TO_AGBNO(mp,
+ INT_GET(irbp->ir_startino, ARCH_CONVERT)) +
+ ((chunkidx & nimask) >>
+ mp->m_sb.sb_inopblog);
+
+ if (flags & BULKSTAT_FG_QUICK) {
+ ino = XFS_AGINO_TO_INO(mp, agno,
+ agino);
+ bno = XFS_AGB_TO_DADDR(mp, agno,
+ agbno);
+
+ /*
+ * Get the inode cluster buffer
+ */
+ ASSERT(xfs_inode_zone != NULL);
+ ip = kmem_zone_zalloc(xfs_inode_zone,
+ KM_SLEEP);
+ ip->i_ino = ino;
+ ip->i_mount = mp;
+ if (bp)
+ xfs_buf_relse(bp);
+ error = xfs_itobp(mp, NULL, ip,
+ &dip, &bp, bno);
+ if (!error)
+ clustidx = ip->i_boffset / mp->m_sb.sb_inodesize;
+ kmem_zone_free(xfs_inode_zone, ip);
+ if (XFS_TEST_ERROR(error != 0,
+ mp, XFS_ERRTAG_BULKSTAT_READ_CHUNK,
+ XFS_RANDOM_BULKSTAT_READ_CHUNK)) {
+ bp = NULL;
+ break;
+ }
+ }
+ }
+ /*
+ * Skip if this inode is free.
+ */
+ if (XFS_INOBT_MASK(chunkidx) & INT_GET(irbp->ir_free, ARCH_CONVERT))
+ continue;
+ /*
+ * Count used inodes as free so we can tell
+ * when the chunk is used up.
+ */
+ INT_MOD(irbp->ir_freecount, ARCH_CONVERT, +1);
+ ino = XFS_AGINO_TO_INO(mp, agno, agino);
+ bno = XFS_AGB_TO_DADDR(mp, agno, agbno);
+ if (flags & BULKSTAT_FG_QUICK) {
+ dip = (xfs_dinode_t *)xfs_buf_offset(bp,
+ (clustidx << mp->m_sb.sb_inodelog));
+
+ if (INT_GET(dip->di_core.di_magic, ARCH_CONVERT)
+ != XFS_DINODE_MAGIC
+ || !XFS_DINODE_GOOD_VERSION(
+ INT_GET(dip->di_core.di_version, ARCH_CONVERT)))
+ continue;
+ }
+
+ /*
+ * Get the inode and fill in a single buffer.
+ * BULKSTAT_FG_QUICK uses dip to fill it in.
+ * BULKSTAT_FG_IGET uses igets.
+ * See: xfs_bulkstat_one & xfs_dm_bulkstat_one.
+ * This is also used to count inodes/blks, etc
+ * in xfs_qm_quotacheck.
+ */
+ ubused = statstruct_size;
+ error = formatter(mp, ino, ubufp,
+ ubleft, private_data,
+ bno, &ubused, dip, &fmterror);
+ if (fmterror == BULKSTAT_RV_NOTHING) {
+ if (error == ENOMEM)
+ ubleft = 0;
+ continue;
+ }
+ if (fmterror == BULKSTAT_RV_GIVEUP) {
+ ubleft = 0;
+ ASSERT(error);
+ rval = error;
+ break;
+ }
+ if (ubufp)
+ ubufp += ubused;
+ ubleft -= ubused;
+ ubelem++;
+ lastino = ino;
+ }
+ }
+
+ if (bp)
+ xfs_buf_relse(bp);
+
+ /*
+ * Set up for the next loop iteration.
+ */
+ if (ubleft > 0) {
+ if (end_of_ag) {
+ agno++;
+ agino = 0;
+ } else
+ agino = XFS_INO_TO_AGINO(mp, lastino);
+ } else
+ break;
+ }
+ /*
+ * Done, we're either out of filesystem or space to put the data.
+ */
+ kmem_free(irbuf, NBPC);
+ if (ubuffer)
+ unuseracc(ubuffer, ubcount * statstruct_size, (B_READ|B_PHYS));
+ *ubcountp = ubelem;
+ if (agno >= mp->m_sb.sb_agcount) {
+ /*
+ * If we ran out of filesystem, mark lastino as off
+ * the end of the filesystem, so the next call
+ * will return immediately.
+ */
+ *lastinop = (xfs_ino_t)XFS_AGINO_TO_INO(mp, agno, 0);
+ *done = 1;
+ } else
+ *lastinop = (xfs_ino_t)lastino;
+
+ return rval;
+}
+
+/*
+ * Return stat information in bulk (by-inode) for the filesystem.
+ * Special case for non-sequential one inode bulkstat.
+ */
+int /* error status */
+xfs_bulkstat_single(
+ xfs_mount_t *mp, /* mount point for filesystem */
+ xfs_ino_t *lastinop, /* inode to return */
+ char __user *buffer, /* buffer with inode stats */
+ int *done) /* 1 if there're more stats to get */
+{
+ int count; /* count value for bulkstat call */
+ int error; /* return value */
+ xfs_ino_t ino; /* filesystem inode number */
+ int res; /* result from bs1 */
+
+ /*
+ * note that requesting valid inode numbers which are not allocated
+ * to inodes will most likely cause xfs_itobp to generate warning
+ * messages about bad magic numbers. This is ok. The fact that
+ * the inode isn't actually an inode is handled by the
+ * error check below. Done this way to make the usual case faster
+ * at the expense of the error case.
+ */
+
+ ino = (xfs_ino_t)*lastinop;
+ error = xfs_bulkstat_one(mp, ino, buffer, sizeof(xfs_bstat_t),
+ NULL, 0, NULL, NULL, &res);
+ if (error) {
+ /*
+ * Special case way failed, do it the "long" way
+ * to see if that works.
+ */
+ (*lastinop)--;
+ count = 1;
+ if (xfs_bulkstat(mp, lastinop, &count, xfs_bulkstat_one,
+ NULL, sizeof(xfs_bstat_t), buffer,
+ BULKSTAT_FG_IGET, done))
+ return error;
+ if (count == 0 || (xfs_ino_t)*lastinop != ino)
+ return error == EFSCORRUPTED ?
+ XFS_ERROR(EINVAL) : error;
+ else
+ return 0;
+ }
+ *done = 0;
+ return 0;
+}
+
+/*
+ * Return inode number table for the filesystem.
+ */
+int /* error status */
+xfs_inumbers(
+ xfs_mount_t *mp, /* mount point for filesystem */
+ xfs_ino_t *lastino, /* last inode returned */
+ int *count, /* size of buffer/count returned */
+ xfs_inogrp_t __user *ubuffer)/* buffer with inode descriptions */
+{
+ xfs_buf_t *agbp;
+ xfs_agino_t agino;
+ xfs_agnumber_t agno;
+ int bcount;
+ xfs_inogrp_t *buffer;
+ int bufidx;
+ xfs_btree_cur_t *cur;
+ int error;
+ __int32_t gcnt;
+ xfs_inofree_t gfree;
+ xfs_agino_t gino;
+ int i;
+ xfs_ino_t ino;
+ int left;
+ int tmp;
+
+ ino = (xfs_ino_t)*lastino;
+ agno = XFS_INO_TO_AGNO(mp, ino);
+ agino = XFS_INO_TO_AGINO(mp, ino);
+ left = *count;
+ *count = 0;
+ bcount = MIN(left, (int)(NBPP / sizeof(*buffer)));
+ buffer = kmem_alloc(bcount * sizeof(*buffer), KM_SLEEP);
+ error = bufidx = 0;
+ cur = NULL;
+ agbp = NULL;
+ while (left > 0 && agno < mp->m_sb.sb_agcount) {
+ if (agbp == NULL) {
+ down_read(&mp->m_peraglock);
+ error = xfs_ialloc_read_agi(mp, NULL, agno, &agbp);
+ up_read(&mp->m_peraglock);
+ if (error) {
+ /*
+ * If we can't read the AGI of this ag,
+ * then just skip to the next one.
+ */
+ ASSERT(cur == NULL);
+ agbp = NULL;
+ agno++;
+ agino = 0;
+ continue;
+ }
+ cur = xfs_btree_init_cursor(mp, NULL, agbp, agno,
+ XFS_BTNUM_INO, (xfs_inode_t *)0, 0);
+ error = xfs_inobt_lookup_ge(cur, agino, 0, 0, &tmp);
+ if (error) {
+ xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
+ cur = NULL;
+ xfs_buf_relse(agbp);
+ agbp = NULL;
+ /*
+ * Move up the the last inode in the current
+ * chunk. The lookup_ge will always get
+ * us the first inode in the next chunk.
+ */
+ agino += XFS_INODES_PER_CHUNK - 1;
+ continue;
+ }
+ }
+ if ((error = xfs_inobt_get_rec(cur, &gino, &gcnt, &gfree,
+ &i, ARCH_NOCONVERT)) ||
+ i == 0) {
+ xfs_buf_relse(agbp);
+ agbp = NULL;
+ xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
+ cur = NULL;
+ agno++;
+ agino = 0;
+ continue;
+ }
+ agino = gino + XFS_INODES_PER_CHUNK - 1;
+ buffer[bufidx].xi_startino = XFS_AGINO_TO_INO(mp, agno, gino);
+ buffer[bufidx].xi_alloccount = XFS_INODES_PER_CHUNK - gcnt;
+ buffer[bufidx].xi_allocmask = ~gfree;
+ bufidx++;
+ left--;
+ if (bufidx == bcount) {
+ if (copy_to_user(ubuffer, buffer,
+ bufidx * sizeof(*buffer))) {
+ error = XFS_ERROR(EFAULT);
+ break;
+ }
+ ubuffer += bufidx;
+ *count += bufidx;
+ bufidx = 0;
+ }
+ if (left) {
+ error = xfs_inobt_increment(cur, 0, &tmp);
+ if (error) {
+ xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
+ cur = NULL;
+ xfs_buf_relse(agbp);
+ agbp = NULL;
+ /*
+ * The agino value has already been bumped.
+ * Just try to skip up to it.
+ */
+ agino += XFS_INODES_PER_CHUNK;
+ continue;
+ }
+ }
+ }
+ if (!error) {
+ if (bufidx) {
+ if (copy_to_user(ubuffer, buffer,
+ bufidx * sizeof(*buffer)))
+ error = XFS_ERROR(EFAULT);
+ else
+ *count += bufidx;
+ }
+ *lastino = XFS_AGINO_TO_INO(mp, agno, agino);
+ }
+ kmem_free(buffer, bcount * sizeof(*buffer));
+ if (cur)
+ xfs_btree_del_cursor(cur, (error ? XFS_BTREE_ERROR :
+ XFS_BTREE_NOERROR));
+ if (agbp)
+ xfs_buf_relse(agbp);
+ return error;
+}
diff --git a/fs/xfs/xfs_itable.h b/fs/xfs/xfs_itable.h
new file mode 100644
index 00000000000000..2be9d1805ab237
--- /dev/null
+++ b/fs/xfs/xfs_itable.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2000-2001 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_ITABLE_H__
+#define __XFS_ITABLE_H__
+
+/*
+ * xfs_bulkstat() is used to fill in xfs_bstat structures as well as dm_stat
+ * structures (by the dmi library). This is a pointer to a formatter function
+ * that will iget the inode and fill in the appropriate structure.
+ * see xfs_bulkstat_one() and xfs_dm_bulkstat_one() in dmapi_xfs.c
+ */
+typedef int (*bulkstat_one_pf)(struct xfs_mount *mp,
+ xfs_ino_t ino,
+ void __user *buffer,
+ int ubsize,
+ void *private_data,
+ xfs_daddr_t bno,
+ int *ubused,
+ void *dip,
+ int *stat);
+
+/*
+ * Values for stat return value.
+ */
+#define BULKSTAT_RV_NOTHING 0
+#define BULKSTAT_RV_DIDONE 1
+#define BULKSTAT_RV_GIVEUP 2
+
+/*
+ * Values for bulkstat flag argument.
+ */
+#define BULKSTAT_FG_IGET 0x1 /* Go through the buffer cache */
+#define BULKSTAT_FG_QUICK 0x2 /* No iget, walk the dinode cluster */
+#define BULKSTAT_FG_VFSLOCKED 0x4 /* Already have vfs lock */
+
+/*
+ * Return stat information in bulk (by-inode) for the filesystem.
+ */
+int /* error status */
+xfs_bulkstat(
+ xfs_mount_t *mp, /* mount point for filesystem */
+ xfs_ino_t *lastino, /* last inode returned */
+ int *count, /* size of buffer/count returned */
+ bulkstat_one_pf formatter, /* func that'd fill a single buf */
+ void *private_data, /* private data for formatter */
+ size_t statstruct_size,/* sizeof struct that we're filling */
+ char __user *ubuffer,/* buffer with inode stats */
+ int flags, /* flag to control access method */
+ int *done); /* 1 if there're more stats to get */
+
+int
+xfs_bulkstat_single(
+ xfs_mount_t *mp,
+ xfs_ino_t *lastinop,
+ char __user *buffer,
+ int *done);
+
+int
+xfs_bulkstat_one(
+ xfs_mount_t *mp,
+ xfs_ino_t ino,
+ void __user *buffer,
+ int ubsize,
+ void *private_data,
+ xfs_daddr_t bno,
+ int *ubused,
+ void *dibuff,
+ int *stat);
+
+int /* error status */
+xfs_inumbers(
+ xfs_mount_t *mp, /* mount point for filesystem */
+ xfs_ino_t *last, /* last inode returned */
+ int *count, /* size of buffer/count returned */
+ xfs_inogrp_t __user *buffer);/* buffer with inode info */
+
+#endif /* __XFS_ITABLE_H__ */
diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c
new file mode 100644
index 00000000000000..4ee4da88ce47ba
--- /dev/null
+++ b/fs/xfs/xfs_log.c
@@ -0,0 +1,3562 @@
+/*
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+/*
+ * High level interface routines for log manager
+ */
+
+#include "xfs.h"
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_ag.h"
+#include "xfs_sb.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_dir.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_error.h"
+#include "xfs_log_priv.h"
+#include "xfs_buf_item.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_log_recover.h"
+#include "xfs_bit.h"
+#include "xfs_rw.h"
+#include "xfs_trans_priv.h"
+
+
+#define xlog_write_adv_cnt(ptr, len, off, bytes) \
+ { (ptr) += (bytes); \
+ (len) -= (bytes); \
+ (off) += (bytes);}
+
+/* Local miscellaneous function prototypes */
+STATIC int xlog_bdstrat_cb(struct xfs_buf *);
+STATIC int xlog_commit_record(xfs_mount_t *mp, xlog_ticket_t *ticket,
+ xlog_in_core_t **, xfs_lsn_t *);
+STATIC xlog_t * xlog_alloc_log(xfs_mount_t *mp,
+ xfs_buftarg_t *log_target,
+ xfs_daddr_t blk_offset,
+ int num_bblks);
+STATIC int xlog_space_left(xlog_t *log, int cycle, int bytes);
+STATIC int xlog_sync(xlog_t *log, xlog_in_core_t *iclog);
+STATIC void xlog_unalloc_log(xlog_t *log);
+STATIC int xlog_write(xfs_mount_t *mp, xfs_log_iovec_t region[],
+ int nentries, xfs_log_ticket_t tic,
+ xfs_lsn_t *start_lsn,
+ xlog_in_core_t **commit_iclog,
+ uint flags);
+
+/* local state machine functions */
+STATIC void xlog_state_done_syncing(xlog_in_core_t *iclog, int);
+STATIC void xlog_state_do_callback(xlog_t *log,int aborted, xlog_in_core_t *iclog);
+STATIC int xlog_state_get_iclog_space(xlog_t *log,
+ int len,
+ xlog_in_core_t **iclog,
+ xlog_ticket_t *ticket,
+ int *continued_write,
+ int *logoffsetp);
+STATIC void xlog_state_put_ticket(xlog_t *log,
+ xlog_ticket_t *tic);
+STATIC int xlog_state_release_iclog(xlog_t *log,
+ xlog_in_core_t *iclog);
+STATIC void xlog_state_switch_iclogs(xlog_t *log,
+ xlog_in_core_t *iclog,
+ int eventual_size);
+STATIC int xlog_state_sync(xlog_t *log, xfs_lsn_t lsn, uint flags);
+STATIC int xlog_state_sync_all(xlog_t *log, uint flags);
+STATIC void xlog_state_want_sync(xlog_t *log, xlog_in_core_t *iclog);
+
+/* local functions to manipulate grant head */
+STATIC int xlog_grant_log_space(xlog_t *log,
+ xlog_ticket_t *xtic);
+STATIC void xlog_grant_push_ail(xfs_mount_t *mp,
+ int need_bytes);
+STATIC void xlog_regrant_reserve_log_space(xlog_t *log,
+ xlog_ticket_t *ticket);
+STATIC int xlog_regrant_write_log_space(xlog_t *log,
+ xlog_ticket_t *ticket);
+STATIC void xlog_ungrant_log_space(xlog_t *log,
+ xlog_ticket_t *ticket);
+
+
+/* local ticket functions */
+STATIC void xlog_state_ticket_alloc(xlog_t *log);
+STATIC xlog_ticket_t *xlog_ticket_get(xlog_t *log,
+ int unit_bytes,
+ int count,
+ char clientid,
+ uint flags);
+STATIC void xlog_ticket_put(xlog_t *log, xlog_ticket_t *ticket);
+
+/* local debug functions */
+#if defined(DEBUG) && !defined(XLOG_NOLOG)
+STATIC void xlog_verify_dest_ptr(xlog_t *log, __psint_t ptr);
+STATIC void xlog_verify_grant_head(xlog_t *log, int equals);
+STATIC void xlog_verify_iclog(xlog_t *log, xlog_in_core_t *iclog,
+ int count, boolean_t syncing);
+STATIC void xlog_verify_tail_lsn(xlog_t *log, xlog_in_core_t *iclog,
+ xfs_lsn_t tail_lsn);
+#else
+#define xlog_verify_dest_ptr(a,b)
+#define xlog_verify_grant_head(a,b)
+#define xlog_verify_iclog(a,b,c,d)
+#define xlog_verify_tail_lsn(a,b,c)
+#endif
+
+int xlog_iclogs_empty(xlog_t *log);
+
+#ifdef DEBUG
+int xlog_do_error = 0;
+int xlog_req_num = 0;
+int xlog_error_mod = 33;
+#endif
+
+#define XLOG_FORCED_SHUTDOWN(log) (log->l_flags & XLOG_IO_ERROR)
+
+/*
+ * 0 => disable log manager
+ * 1 => enable log manager
+ * 2 => enable log manager and log debugging
+ */
+#if defined(XLOG_NOLOG) || defined(DEBUG)
+int xlog_debug = 1;
+xfs_buftarg_t *xlog_target;
+#endif
+
+#if defined(XFS_LOG_TRACE)
+
+void
+xlog_trace_loggrant(xlog_t *log, xlog_ticket_t *tic, xfs_caddr_t string)
+{
+ if (! log->l_grant_trace) {
+ log->l_grant_trace = ktrace_alloc(1024, KM_NOSLEEP);
+ if (! log->l_grant_trace)
+ return;
+ }
+
+ ktrace_enter(log->l_grant_trace,
+ (void *)tic,
+ (void *)log->l_reserve_headq,
+ (void *)log->l_write_headq,
+ (void *)((unsigned long)log->l_grant_reserve_cycle),
+ (void *)((unsigned long)log->l_grant_reserve_bytes),
+ (void *)((unsigned long)log->l_grant_write_cycle),
+ (void *)((unsigned long)log->l_grant_write_bytes),
+ (void *)((unsigned long)log->l_curr_cycle),
+ (void *)((unsigned long)log->l_curr_block),
+ (void *)((unsigned long)CYCLE_LSN(log->l_tail_lsn, ARCH_NOCONVERT)),
+ (void *)((unsigned long)BLOCK_LSN(log->l_tail_lsn, ARCH_NOCONVERT)),
+ (void *)string,
+ (void *)((unsigned long)13),
+ (void *)((unsigned long)14),
+ (void *)((unsigned long)15),
+ (void *)((unsigned long)16));
+}
+
+void
+xlog_trace_iclog(xlog_in_core_t *iclog, uint state)
+{
+ pid_t pid;
+
+ pid = current_pid();
+
+ if (!iclog->ic_trace)
+ iclog->ic_trace = ktrace_alloc(256, KM_SLEEP);
+ ktrace_enter(iclog->ic_trace,
+ (void *)((unsigned long)state),
+ (void *)((unsigned long)pid),
+ (void *)0,
+ (void *)0,
+ (void *)0,
+ (void *)0,
+ (void *)0,
+ (void *)0,
+ (void *)0,
+ (void *)0,
+ (void *)0,
+ (void *)0,
+ (void *)0,
+ (void *)0,
+ (void *)0,
+ (void *)0);
+}
+
+#else
+#define xlog_trace_loggrant(log,tic,string)
+#define xlog_trace_iclog(iclog,state)
+#endif /* XFS_LOG_TRACE */
+
+/*
+ * NOTES:
+ *
+ * 1. currblock field gets updated at startup and after in-core logs
+ * marked as with WANT_SYNC.
+ */
+
+/*
+ * This routine is called when a user of a log manager ticket is done with
+ * the reservation. If the ticket was ever used, then a commit record for
+ * the associated transaction is written out as a log operation header with
+ * no data. The flag XLOG_TIC_INITED is set when the first write occurs with
+ * a given ticket. If the ticket was one with a permanent reservation, then
+ * a few operations are done differently. Permanent reservation tickets by
+ * default don't release the reservation. They just commit the current
+ * transaction with the belief that the reservation is still needed. A flag
+ * must be passed in before permanent reservations are actually released.
+ * When these type of tickets are not released, they need to be set into
+ * the inited state again. By doing this, a start record will be written
+ * out when the next write occurs.
+ */
+xfs_lsn_t
+xfs_log_done(xfs_mount_t *mp,
+ xfs_log_ticket_t xtic,
+ void **iclog,
+ uint flags)
+{
+ xlog_t *log = mp->m_log;
+ xlog_ticket_t *ticket = (xfs_log_ticket_t) xtic;
+ xfs_lsn_t lsn = 0;
+
+#if defined(DEBUG) || defined(XLOG_NOLOG)
+ if (!xlog_debug && xlog_target == log->l_targ)
+ return 0;
+#endif
+
+ if (XLOG_FORCED_SHUTDOWN(log) ||
+ /*
+ * If nothing was ever written, don't write out commit record.
+ * If we get an error, just continue and give back the log ticket.
+ */
+ (((ticket->t_flags & XLOG_TIC_INITED) == 0) &&
+ (xlog_commit_record(mp, ticket,
+ (xlog_in_core_t **)iclog, &lsn)))) {
+ lsn = (xfs_lsn_t) -1;
+ if (ticket->t_flags & XLOG_TIC_PERM_RESERV) {
+ flags |= XFS_LOG_REL_PERM_RESERV;
+ }
+ }
+
+
+ if ((ticket->t_flags & XLOG_TIC_PERM_RESERV) == 0 ||
+ (flags & XFS_LOG_REL_PERM_RESERV)) {
+ /*
+ * Release ticket if not permanent reservation or a specifc
+ * request has been made to release a permanent reservation.
+ */
+ xlog_ungrant_log_space(log, ticket);
+ xlog_state_put_ticket(log, ticket);
+ } else {
+ xlog_regrant_reserve_log_space(log, ticket);
+ }
+
+ /* If this ticket was a permanent reservation and we aren't
+ * trying to release it, reset the inited flags; so next time
+ * we write, a start record will be written out.
+ */
+ if ((ticket->t_flags & XLOG_TIC_PERM_RESERV) &&
+ (flags & XFS_LOG_REL_PERM_RESERV) == 0)
+ ticket->t_flags |= XLOG_TIC_INITED;
+
+ return lsn;
+} /* xfs_log_done */
+
+
+/*
+ * Force the in-core log to disk. If flags == XFS_LOG_SYNC,
+ * the force is done synchronously.
+ *
+ * Asynchronous forces are implemented by setting the WANT_SYNC
+ * bit in the appropriate in-core log and then returning.
+ *
+ * Synchronous forces are implemented with a semaphore. All callers
+ * to force a given lsn to disk will wait on a semaphore attached to the
+ * specific in-core log. When given in-core log finally completes its
+ * write to disk, that thread will wake up all threads waiting on the
+ * semaphore.
+ */
+int
+xfs_log_force(xfs_mount_t *mp,
+ xfs_lsn_t lsn,
+ uint flags)
+{
+ int rval;
+ xlog_t *log = mp->m_log;
+
+#if defined(DEBUG) || defined(XLOG_NOLOG)
+ if (!xlog_debug && xlog_target == log->l_targ)
+ return 0;
+#endif
+
+ ASSERT(flags & XFS_LOG_FORCE);
+
+ XFS_STATS_INC(xs_log_force);
+
+ if ((log->l_flags & XLOG_IO_ERROR) == 0) {
+ if (lsn == 0)
+ rval = xlog_state_sync_all(log, flags);
+ else
+ rval = xlog_state_sync(log, lsn, flags);
+ } else {
+ rval = XFS_ERROR(EIO);
+ }
+
+ return rval;
+
+} /* xfs_log_force */
+
+/*
+ * Attaches a new iclog I/O completion callback routine during
+ * transaction commit. If the log is in error state, a non-zero
+ * return code is handed back and the caller is responsible for
+ * executing the callback at an appropriate time.
+ */
+int
+xfs_log_notify(xfs_mount_t *mp, /* mount of partition */
+ void *iclog_hndl, /* iclog to hang callback off */
+ xfs_log_callback_t *cb)
+{
+ xlog_t *log = mp->m_log;
+ xlog_in_core_t *iclog = (xlog_in_core_t *)iclog_hndl;
+ int abortflg, spl;
+
+#if defined(DEBUG) || defined(XLOG_NOLOG)
+ if (!xlog_debug && xlog_target == log->l_targ)
+ return 0;
+#endif
+ cb->cb_next = NULL;
+ spl = LOG_LOCK(log);
+ abortflg = (iclog->ic_state & XLOG_STATE_IOERROR);
+ if (!abortflg) {
+ ASSERT_ALWAYS((iclog->ic_state == XLOG_STATE_ACTIVE) ||
+ (iclog->ic_state == XLOG_STATE_WANT_SYNC));
+ cb->cb_next = NULL;
+ *(iclog->ic_callback_tail) = cb;
+ iclog->ic_callback_tail = &(cb->cb_next);
+ }
+ LOG_UNLOCK(log, spl);
+ return abortflg;
+} /* xfs_log_notify */
+
+int
+xfs_log_release_iclog(xfs_mount_t *mp,
+ void *iclog_hndl)
+{
+ xlog_t *log = mp->m_log;
+ xlog_in_core_t *iclog = (xlog_in_core_t *)iclog_hndl;
+
+ if (xlog_state_release_iclog(log, iclog)) {
+ xfs_force_shutdown(mp, XFS_LOG_IO_ERROR);
+ return(EIO);
+ }
+
+ return 0;
+}
+
+/*
+ * 1. Reserve an amount of on-disk log space and return a ticket corresponding
+ * to the reservation.
+ * 2. Potentially, push buffers at tail of log to disk.
+ *
+ * Each reservation is going to reserve extra space for a log record header.
+ * When writes happen to the on-disk log, we don't subtract the length of the
+ * log record header from any reservation. By wasting space in each
+ * reservation, we prevent over allocation problems.
+ */
+int
+xfs_log_reserve(xfs_mount_t *mp,
+ int unit_bytes,
+ int cnt,
+ xfs_log_ticket_t *ticket,
+ __uint8_t client,
+ uint flags)
+{
+ xlog_t *log = mp->m_log;
+ xlog_ticket_t *internal_ticket;
+ int retval;
+
+#if defined(DEBUG) || defined(XLOG_NOLOG)
+ if (!xlog_debug && xlog_target == log->l_targ)
+ return 0;
+#endif
+ retval = 0;
+ ASSERT(client == XFS_TRANSACTION || client == XFS_LOG);
+ ASSERT((flags & XFS_LOG_NOSLEEP) == 0);
+
+ if (XLOG_FORCED_SHUTDOWN(log))
+ return XFS_ERROR(EIO);
+
+ XFS_STATS_INC(xs_try_logspace);
+
+ if (*ticket != NULL) {
+ ASSERT(flags & XFS_LOG_PERM_RESERV);
+ internal_ticket = (xlog_ticket_t *)*ticket;
+ xlog_grant_push_ail(mp, internal_ticket->t_unit_res);
+ retval = xlog_regrant_write_log_space(log, internal_ticket);
+ } else {
+ /* may sleep if need to allocate more tickets */
+ internal_ticket = xlog_ticket_get(log, unit_bytes, cnt,
+ client, flags);
+ *ticket = internal_ticket;
+ xlog_grant_push_ail(mp,
+ (internal_ticket->t_unit_res *
+ internal_ticket->t_cnt));
+ retval = xlog_grant_log_space(log, internal_ticket);
+ }
+
+ return retval;
+} /* xfs_log_reserve */
+
+
+/*
+ * Mount a log filesystem
+ *
+ * mp - ubiquitous xfs mount point structure
+ * log_target - buftarg of on-disk log device
+ * blk_offset - Start block # where block size is 512 bytes (BBSIZE)
+ * num_bblocks - Number of BBSIZE blocks in on-disk log
+ *
+ * Return error or zero.
+ */
+int
+xfs_log_mount(xfs_mount_t *mp,
+ xfs_buftarg_t *log_target,
+ xfs_daddr_t blk_offset,
+ int num_bblks)
+{
+ if (!(mp->m_flags & XFS_MOUNT_NORECOVERY))
+ cmn_err(CE_NOTE, "XFS mounting filesystem %s", mp->m_fsname);
+ else {
+ cmn_err(CE_NOTE,
+ "!Mounting filesystem \"%s\" in no-recovery mode. Filesystem will be inconsistent.",
+ mp->m_fsname);
+ ASSERT(XFS_MTOVFS(mp)->vfs_flag & VFS_RDONLY);
+ }
+
+ mp->m_log = xlog_alloc_log(mp, log_target, blk_offset, num_bblks);
+
+#if defined(DEBUG) || defined(XLOG_NOLOG)
+ if (!xlog_debug) {
+ cmn_err(CE_NOTE, "log dev: %s", XFS_BUFTARG_NAME(log_target));
+ return 0;
+ }
+#endif
+ /*
+ * skip log recovery on a norecovery mount. pretend it all
+ * just worked.
+ */
+ if (!(mp->m_flags & XFS_MOUNT_NORECOVERY)) {
+ int error;
+ vfs_t *vfsp = XFS_MTOVFS(mp);
+ int readonly = (vfsp->vfs_flag & VFS_RDONLY);
+
+ if (readonly)
+ vfsp->vfs_flag &= ~VFS_RDONLY;
+
+ error = xlog_recover(mp->m_log, readonly);
+
+ if (readonly)
+ vfsp->vfs_flag |= VFS_RDONLY;
+ if (error) {
+ cmn_err(CE_WARN, "XFS: log mount/recovery failed: error %d", error);
+ xlog_unalloc_log(mp->m_log);
+ return error;
+ }
+ }
+
+ /* Normal transactions can now occur */
+ mp->m_log->l_flags &= ~XLOG_ACTIVE_RECOVERY;
+
+ /* End mounting message in xfs_log_mount_finish */
+ return 0;
+} /* xfs_log_mount */
+
+/*
+ * Finish the recovery of the file system. This is separate from
+ * the xfs_log_mount() call, because it depends on the code in
+ * xfs_mountfs() to read in the root and real-time bitmap inodes
+ * between calling xfs_log_mount() and here.
+ *
+ * mp - ubiquitous xfs mount point structure
+ */
+int
+xfs_log_mount_finish(xfs_mount_t *mp, int mfsi_flags)
+{
+ int error;
+
+ if (!(mp->m_flags & XFS_MOUNT_NORECOVERY))
+ error = xlog_recover_finish(mp->m_log, mfsi_flags);
+ else {
+ error = 0;
+ ASSERT(XFS_MTOVFS(mp)->vfs_flag & VFS_RDONLY);
+ }
+
+ return error;
+}
+
+/*
+ * Unmount processing for the log.
+ */
+int
+xfs_log_unmount(xfs_mount_t *mp)
+{
+ int error;
+
+ error = xfs_log_unmount_write(mp);
+ xfs_log_unmount_dealloc(mp);
+ return (error);
+}
+
+/*
+ * Final log writes as part of unmount.
+ *
+ * Mark the filesystem clean as unmount happens. Note that during relocation
+ * this routine needs to be executed as part of source-bag while the
+ * deallocation must not be done until source-end.
+ */
+
+/*
+ * Unmount record used to have a string "Unmount filesystem--" in the
+ * data section where the "Un" was really a magic number (XLOG_UNMOUNT_TYPE).
+ * We just write the magic number now since that particular field isn't
+ * currently architecture converted and "nUmount" is a bit foo.
+ * As far as I know, there weren't any dependencies on the old behaviour.
+ */
+
+int
+xfs_log_unmount_write(xfs_mount_t *mp)
+{
+ xlog_t *log = mp->m_log;
+ xlog_in_core_t *iclog;
+#ifdef DEBUG
+ xlog_in_core_t *first_iclog;
+#endif
+ xfs_log_iovec_t reg[1];
+ xfs_log_ticket_t tic = NULL;
+ xfs_lsn_t lsn;
+ int error;
+ SPLDECL(s);
+
+ /* the data section must be 32 bit size aligned */
+ struct {
+ __uint16_t magic;
+ __uint16_t pad1;
+ __uint32_t pad2; /* may as well make it 64 bits */
+ } magic = { XLOG_UNMOUNT_TYPE, 0, 0 };
+
+#if defined(DEBUG) || defined(XLOG_NOLOG)
+ if (!xlog_debug && xlog_target == log->l_targ)
+ return 0;
+#endif
+
+ /*
+ * Don't write out unmount record on read-only mounts.
+ * Or, if we are doing a forced umount (typically because of IO errors).
+ */
+ if (XFS_MTOVFS(mp)->vfs_flag & VFS_RDONLY)
+ return 0;
+
+ xfs_log_force(mp, 0, XFS_LOG_FORCE|XFS_LOG_SYNC);
+
+#ifdef DEBUG
+ first_iclog = iclog = log->l_iclog;
+ do {
+ if (!(iclog->ic_state & XLOG_STATE_IOERROR)) {
+ ASSERT(iclog->ic_state & XLOG_STATE_ACTIVE);
+ ASSERT(iclog->ic_offset == 0);
+ }
+ iclog = iclog->ic_next;
+ } while (iclog != first_iclog);
+#endif
+ if (! (XLOG_FORCED_SHUTDOWN(log))) {
+ reg[0].i_addr = (void*)&magic;
+ reg[0].i_len = sizeof(magic);
+
+ error = xfs_log_reserve(mp, 600, 1, &tic, XFS_LOG, 0);
+ if (!error) {
+ /* remove inited flag */
+ ((xlog_ticket_t *)tic)->t_flags = 0;
+ error = xlog_write(mp, reg, 1, tic, &lsn,
+ NULL, XLOG_UNMOUNT_TRANS);
+ /*
+ * At this point, we're umounting anyway,
+ * so there's no point in transitioning log state
+ * to IOERROR. Just continue...
+ */
+ }
+
+ if (error) {
+ xfs_fs_cmn_err(CE_ALERT, mp,
+ "xfs_log_unmount: unmount record failed");
+ }
+
+
+ s = LOG_LOCK(log);
+ iclog = log->l_iclog;
+ iclog->ic_refcnt++;
+ LOG_UNLOCK(log, s);
+ xlog_state_want_sync(log, iclog);
+ (void) xlog_state_release_iclog(log, iclog);
+
+ s = LOG_LOCK(log);
+ if (!(iclog->ic_state == XLOG_STATE_ACTIVE ||
+ iclog->ic_state == XLOG_STATE_DIRTY)) {
+ if (!XLOG_FORCED_SHUTDOWN(log)) {
+ sv_wait(&iclog->ic_forcesema, PMEM,
+ &log->l_icloglock, s);
+ } else {
+ LOG_UNLOCK(log, s);
+ }
+ } else {
+ LOG_UNLOCK(log, s);
+ }
+ if (tic)
+ xlog_state_put_ticket(log, tic);
+ } else {
+ /*
+ * We're already in forced_shutdown mode, couldn't
+ * even attempt to write out the unmount transaction.
+ *
+ * Go through the motions of sync'ing and releasing
+ * the iclog, even though no I/O will actually happen,
+ * we need to wait for other log I/O's that may already
+ * be in progress. Do this as a separate section of
+ * code so we'll know if we ever get stuck here that
+ * we're in this odd situation of trying to unmount
+ * a file system that went into forced_shutdown as
+ * the result of an unmount..
+ */
+ s = LOG_LOCK(log);
+ iclog = log->l_iclog;
+ iclog->ic_refcnt++;
+ LOG_UNLOCK(log, s);
+
+ xlog_state_want_sync(log, iclog);
+ (void) xlog_state_release_iclog(log, iclog);
+
+ s = LOG_LOCK(log);
+
+ if ( ! ( iclog->ic_state == XLOG_STATE_ACTIVE
+ || iclog->ic_state == XLOG_STATE_DIRTY
+ || iclog->ic_state == XLOG_STATE_IOERROR) ) {
+
+ sv_wait(&iclog->ic_forcesema, PMEM,
+ &log->l_icloglock, s);
+ } else {
+ LOG_UNLOCK(log, s);
+ }
+ }
+
+ return 0;
+} /* xfs_log_unmount_write */
+
+/*
+ * Deallocate log structures for unmount/relocation.
+ */
+void
+xfs_log_unmount_dealloc(xfs_mount_t *mp)
+{
+ xlog_unalloc_log(mp->m_log);
+}
+
+/*
+ * Write region vectors to log. The write happens using the space reservation
+ * of the ticket (tic). It is not a requirement that all writes for a given
+ * transaction occur with one call to xfs_log_write().
+ */
+int
+xfs_log_write(xfs_mount_t * mp,
+ xfs_log_iovec_t reg[],
+ int nentries,
+ xfs_log_ticket_t tic,
+ xfs_lsn_t *start_lsn)
+{
+ int error;
+ xlog_t *log = mp->m_log;
+
+#if defined(DEBUG) || defined(XLOG_NOLOG)
+ if (!xlog_debug && xlog_target == log->l_targ) {
+ *start_lsn = 0;
+ return 0;
+ }
+#endif
+ if (XLOG_FORCED_SHUTDOWN(log))
+ return XFS_ERROR(EIO);
+
+ if ((error = xlog_write(mp, reg, nentries, tic, start_lsn, NULL, 0))) {
+ xfs_force_shutdown(mp, XFS_LOG_IO_ERROR);
+ }
+ return (error);
+} /* xfs_log_write */
+
+
+void
+xfs_log_move_tail(xfs_mount_t *mp,
+ xfs_lsn_t tail_lsn)
+{
+ xlog_ticket_t *tic;
+ xlog_t *log = mp->m_log;
+ int need_bytes, free_bytes, cycle, bytes;
+ SPLDECL(s);
+
+#if defined(DEBUG) || defined(XLOG_NOLOG)
+ if (!xlog_debug && xlog_target == log->l_targ)
+ return;
+#endif
+ /* XXXsup tmp */
+ if (XLOG_FORCED_SHUTDOWN(log))
+ return;
+ ASSERT(!XFS_FORCED_SHUTDOWN(mp));
+
+ if (tail_lsn == 0) {
+ /* needed since sync_lsn is 64 bits */
+ s = LOG_LOCK(log);
+ tail_lsn = log->l_last_sync_lsn;
+ LOG_UNLOCK(log, s);
+ }
+
+ s = GRANT_LOCK(log);
+
+ /* Also an invalid lsn. 1 implies that we aren't passing in a valid
+ * tail_lsn.
+ */
+ if (tail_lsn != 1) {
+ log->l_tail_lsn = tail_lsn;
+ }
+
+ if ((tic = log->l_write_headq)) {
+#ifdef DEBUG
+ if (log->l_flags & XLOG_ACTIVE_RECOVERY)
+ panic("Recovery problem");
+#endif
+ cycle = log->l_grant_write_cycle;
+ bytes = log->l_grant_write_bytes;
+ free_bytes = xlog_space_left(log, cycle, bytes);
+ do {
+ ASSERT(tic->t_flags & XLOG_TIC_PERM_RESERV);
+
+ if (free_bytes < tic->t_unit_res && tail_lsn != 1)
+ break;
+ tail_lsn = 0;
+ free_bytes -= tic->t_unit_res;
+ sv_signal(&tic->t_sema);
+ tic = tic->t_next;
+ } while (tic != log->l_write_headq);
+ }
+ if ((tic = log->l_reserve_headq)) {
+#ifdef DEBUG
+ if (log->l_flags & XLOG_ACTIVE_RECOVERY)
+ panic("Recovery problem");
+#endif
+ cycle = log->l_grant_reserve_cycle;
+ bytes = log->l_grant_reserve_bytes;
+ free_bytes = xlog_space_left(log, cycle, bytes);
+ do {
+ if (tic->t_flags & XLOG_TIC_PERM_RESERV)
+ need_bytes = tic->t_unit_res*tic->t_cnt;
+ else
+ need_bytes = tic->t_unit_res;
+ if (free_bytes < need_bytes && tail_lsn != 1)
+ break;
+ tail_lsn = 0;
+ free_bytes -= need_bytes;
+ sv_signal(&tic->t_sema);
+ tic = tic->t_next;
+ } while (tic != log->l_reserve_headq);
+ }
+ GRANT_UNLOCK(log, s);
+} /* xfs_log_move_tail */
+
+/*
+ * Determine if we have a transaction that has gone to disk
+ * that needs to be covered. Log activity needs to be idle (no AIL and
+ * nothing in the iclogs). And, we need to be in the right state indicating
+ * something has gone out.
+ */
+int
+xfs_log_need_covered(xfs_mount_t *mp)
+{
+ SPLDECL(s);
+ int needed = 0, gen;
+ xlog_t *log = mp->m_log;
+ vfs_t *vfsp = XFS_MTOVFS(mp);
+
+ if (fs_frozen(vfsp) || XFS_FORCED_SHUTDOWN(mp) ||
+ (vfsp->vfs_flag & VFS_RDONLY))
+ return 0;
+
+ s = LOG_LOCK(log);
+ if (((log->l_covered_state == XLOG_STATE_COVER_NEED) ||
+ (log->l_covered_state == XLOG_STATE_COVER_NEED2))
+ && !xfs_trans_first_ail(mp, &gen)
+ && xlog_iclogs_empty(log)) {
+ if (log->l_covered_state == XLOG_STATE_COVER_NEED)
+ log->l_covered_state = XLOG_STATE_COVER_DONE;
+ else {
+ ASSERT(log->l_covered_state == XLOG_STATE_COVER_NEED2);
+ log->l_covered_state = XLOG_STATE_COVER_DONE2;
+ }
+ needed = 1;
+ }
+ LOG_UNLOCK(log, s);
+ return(needed);
+}
+
+/******************************************************************************
+ *
+ * local routines
+ *
+ ******************************************************************************
+ */
+
+/* xfs_trans_tail_ail returns 0 when there is nothing in the list.
+ * The log manager must keep track of the last LR which was committed
+ * to disk. The lsn of this LR will become the new tail_lsn whenever
+ * xfs_trans_tail_ail returns 0. If we don't do this, we run into
+ * the situation where stuff could be written into the log but nothing
+ * was ever in the AIL when asked. Eventually, we panic since the
+ * tail hits the head.
+ *
+ * We may be holding the log iclog lock upon entering this routine.
+ */
+xfs_lsn_t
+xlog_assign_tail_lsn(xfs_mount_t *mp)
+{
+ xfs_lsn_t tail_lsn;
+ SPLDECL(s);
+ xlog_t *log = mp->m_log;
+
+ tail_lsn = xfs_trans_tail_ail(mp);
+ s = GRANT_LOCK(log);
+ if (tail_lsn != 0) {
+ log->l_tail_lsn = tail_lsn;
+ } else {
+ tail_lsn = log->l_tail_lsn = log->l_last_sync_lsn;
+ }
+ GRANT_UNLOCK(log, s);
+
+ return tail_lsn;
+} /* xlog_assign_tail_lsn */
+
+
+/*
+ * Return the space in the log between the tail and the head. The head
+ * is passed in the cycle/bytes formal parms. In the special case where
+ * the reserve head has wrapped passed the tail, this calculation is no
+ * longer valid. In this case, just return 0 which means there is no space
+ * in the log. This works for all places where this function is called
+ * with the reserve head. Of course, if the write head were to ever
+ * wrap the tail, we should blow up. Rather than catch this case here,
+ * we depend on other ASSERTions in other parts of the code. XXXmiken
+ *
+ * This code also handles the case where the reservation head is behind
+ * the tail. The details of this case are described below, but the end
+ * result is that we return the size of the log as the amount of space left.
+ */
+int
+xlog_space_left(xlog_t *log, int cycle, int bytes)
+{
+ int free_bytes;
+ int tail_bytes;
+ int tail_cycle;
+
+ tail_bytes = BBTOB(BLOCK_LSN(log->l_tail_lsn, ARCH_NOCONVERT));
+ tail_cycle = CYCLE_LSN(log->l_tail_lsn, ARCH_NOCONVERT);
+ if ((tail_cycle == cycle) && (bytes >= tail_bytes)) {
+ free_bytes = log->l_logsize - (bytes - tail_bytes);
+ } else if ((tail_cycle + 1) < cycle) {
+ return 0;
+ } else if (tail_cycle < cycle) {
+ ASSERT(tail_cycle == (cycle - 1));
+ free_bytes = tail_bytes - bytes;
+ } else {
+ /*
+ * The reservation head is behind the tail.
+ * In this case we just want to return the size of the
+ * log as the amount of space left.
+ */
+ xfs_fs_cmn_err(CE_ALERT, log->l_mp,
+ "xlog_space_left: head behind tail\n"
+ " tail_cycle = %d, tail_bytes = %d\n"
+ " GH cycle = %d, GH bytes = %d",
+ tail_cycle, tail_bytes, cycle, bytes);
+ ASSERT(0);
+ free_bytes = log->l_logsize;
+ }
+ return free_bytes;
+} /* xlog_space_left */
+
+
+/*
+ * Log function which is called when an io completes.
+ *
+ * The log manager needs its own routine, in order to control what
+ * happens with the buffer after the write completes.
+ */
+void
+xlog_iodone(xfs_buf_t *bp)
+{
+ xlog_in_core_t *iclog;
+ xlog_t *l;
+ int aborted;
+
+ iclog = XFS_BUF_FSPRIVATE(bp, xlog_in_core_t *);
+ ASSERT(XFS_BUF_FSPRIVATE2(bp, unsigned long) == (unsigned long) 2);
+ XFS_BUF_SET_FSPRIVATE2(bp, (unsigned long)1);
+ aborted = 0;
+
+ /*
+ * Some versions of cpp barf on the recursive definition of
+ * ic_log -> hic_fields.ic_log and expand ic_log twice when
+ * it is passed through two macros. Workaround broken cpp.
+ */
+ l = iclog->ic_log;
+
+ /*
+ * Race to shutdown the filesystem if we see an error.
+ */
+ if (XFS_TEST_ERROR((XFS_BUF_GETERROR(bp)), l->l_mp,
+ XFS_ERRTAG_IODONE_IOERR, XFS_RANDOM_IODONE_IOERR)) {
+ xfs_ioerror_alert("xlog_iodone", l->l_mp, bp, XFS_BUF_ADDR(bp));
+ XFS_BUF_STALE(bp);
+ xfs_force_shutdown(l->l_mp, XFS_LOG_IO_ERROR);
+ /*
+ * This flag will be propagated to the trans-committed
+ * callback routines to let them know that the log-commit
+ * didn't succeed.
+ */
+ aborted = XFS_LI_ABORTED;
+ } else if (iclog->ic_state & XLOG_STATE_IOERROR) {
+ aborted = XFS_LI_ABORTED;
+ }
+ xlog_state_done_syncing(iclog, aborted);
+ if (!(XFS_BUF_ISASYNC(bp))) {
+ /*
+ * Corresponding psema() will be done in bwrite(). If we don't
+ * vsema() here, panic.
+ */
+ XFS_BUF_V_IODONESEMA(bp);
+ }
+} /* xlog_iodone */
+
+/*
+ * The bdstrat callback function for log bufs. This gives us a central
+ * place to trap bufs in case we get hit by a log I/O error and need to
+ * shutdown. Actually, in practice, even when we didn't get a log error,
+ * we transition the iclogs to IOERROR state *after* flushing all existing
+ * iclogs to disk. This is because we don't want anymore new transactions to be
+ * started or completed afterwards.
+ */
+STATIC int
+xlog_bdstrat_cb(struct xfs_buf *bp)
+{
+ xlog_in_core_t *iclog;
+
+ iclog = XFS_BUF_FSPRIVATE(bp, xlog_in_core_t *);
+
+ if ((iclog->ic_state & XLOG_STATE_IOERROR) == 0) {
+ /* note for irix bstrat will need struct bdevsw passed
+ * Fix the following macro if the code ever is merged
+ */
+ XFS_bdstrat(bp);
+ return 0;
+ }
+
+ xfs_buftrace("XLOG__BDSTRAT IOERROR", bp);
+ XFS_BUF_ERROR(bp, EIO);
+ XFS_BUF_STALE(bp);
+ xfs_biodone(bp);
+ return (XFS_ERROR(EIO));
+
+
+}
+
+/*
+ * Return size of each in-core log record buffer.
+ *
+ * Low memory machines only get 2 16KB buffers. We don't want to waste
+ * memory here. However, all other machines get at least 2 32KB buffers.
+ * The number is hard coded because we don't care about the minimum
+ * memory size, just 32MB systems.
+ *
+ * If the filesystem blocksize is too large, we may need to choose a
+ * larger size since the directory code currently logs entire blocks.
+ */
+
+STATIC void
+xlog_get_iclog_buffer_size(xfs_mount_t *mp,
+ xlog_t *log)
+{
+ int size;
+ int xhdrs;
+
+#if defined(DEBUG) || defined(XLOG_NOLOG)
+ /*
+ * When logbufs == 0, someone has disabled the log from the FSTAB
+ * file. This is not a documented feature. We need to set xlog_debug
+ * to zero (this deactivates the log) and set xlog_target to the
+ * appropriate device. Only one filesystem may be affected as such
+ * since this is just a performance hack to test what we might be able
+ * to get if the log were not present.
+ */
+ if (mp->m_logbufs == 0) {
+ xlog_debug = 0;
+ xlog_target = log->l_targ;
+ log->l_iclog_bufs = XLOG_MIN_ICLOGS;
+ } else
+#endif
+ {
+ /*
+ * This is the normal path. If m_logbufs == -1, then the
+ * admin has chosen to use the system defaults for logbuffers.
+ */
+ if (mp->m_logbufs == -1) {
+ if (xfs_physmem <= btoc(128*1024*1024)) {
+ log->l_iclog_bufs = XLOG_MIN_ICLOGS;
+ } else if (xfs_physmem <= btoc(400*1024*1024)) {
+ log->l_iclog_bufs = XLOG_MED_ICLOGS;
+ } else {
+ /* 256K with 32K bufs */
+ log->l_iclog_bufs = XLOG_MAX_ICLOGS;
+ }
+ } else
+ log->l_iclog_bufs = mp->m_logbufs;
+
+#if defined(DEBUG) || defined(XLOG_NOLOG)
+ /* We are reactivating a filesystem after it was inactive */
+ if (log->l_targ == xlog_target) {
+ xlog_target = NULL;
+ xlog_debug = 1;
+ }
+#endif
+ }
+
+ /*
+ * Buffer size passed in from mount system call.
+ */
+ if (mp->m_logbsize != -1) {
+ size = log->l_iclog_size = mp->m_logbsize;
+ log->l_iclog_size_log = 0;
+ while (size != 1) {
+ log->l_iclog_size_log++;
+ size >>= 1;
+ }
+
+ if (XFS_SB_VERSION_HASLOGV2(&mp->m_sb)) {
+ /* # headers = size / 32K
+ * one header holds cycles from 32K of data
+ */
+
+ xhdrs = mp->m_logbsize / XLOG_HEADER_CYCLE_SIZE;
+ if (mp->m_logbsize % XLOG_HEADER_CYCLE_SIZE)
+ xhdrs++;
+ log->l_iclog_hsize = xhdrs << BBSHIFT;
+ log->l_iclog_heads = xhdrs;
+ } else {
+ ASSERT(mp->m_logbsize <= XLOG_BIG_RECORD_BSIZE);
+ log->l_iclog_hsize = BBSIZE;
+ log->l_iclog_heads = 1;
+ }
+ return;
+ }
+
+ /*
+ * Special case machines that have less than 32MB of memory.
+ * All machines with more memory use 32KB buffers.
+ */
+ if (xfs_physmem <= btoc(32*1024*1024)) {
+ /* Don't change; min configuration */
+ log->l_iclog_size = XLOG_RECORD_BSIZE; /* 16k */
+ log->l_iclog_size_log = XLOG_RECORD_BSHIFT;
+ } else {
+ log->l_iclog_size = XLOG_BIG_RECORD_BSIZE; /* 32k */
+ log->l_iclog_size_log = XLOG_BIG_RECORD_BSHIFT;
+ }
+
+ /* the default log size is 16k or 32k which is one header sector */
+ log->l_iclog_hsize = BBSIZE;
+ log->l_iclog_heads = 1;
+
+ /*
+ * For 16KB, we use 3 32KB buffers. For 32KB block sizes, we use
+ * 4 32KB buffers. For 64KB block sizes, we use 8 32KB buffers.
+ */
+ if (mp->m_sb.sb_blocksize >= 16*1024) {
+ log->l_iclog_size = XLOG_BIG_RECORD_BSIZE;
+ log->l_iclog_size_log = XLOG_BIG_RECORD_BSHIFT;
+ if (mp->m_logbufs == -1) {
+ switch (mp->m_sb.sb_blocksize) {
+ case 16*1024: /* 16 KB */
+ log->l_iclog_bufs = 3;
+ break;
+ case 32*1024: /* 32 KB */
+ log->l_iclog_bufs = 4;
+ break;
+ case 64*1024: /* 64 KB */
+ log->l_iclog_bufs = 8;
+ break;
+ default:
+ xlog_panic("XFS: Invalid blocksize");
+ break;
+ }
+ }
+ }
+} /* xlog_get_iclog_buffer_size */
+
+
+/*
+ * This routine initializes some of the log structure for a given mount point.
+ * Its primary purpose is to fill in enough, so recovery can occur. However,
+ * some other stuff may be filled in too.
+ */
+STATIC xlog_t *
+xlog_alloc_log(xfs_mount_t *mp,
+ xfs_buftarg_t *log_target,
+ xfs_daddr_t blk_offset,
+ int num_bblks)
+{
+ xlog_t *log;
+ xlog_rec_header_t *head;
+ xlog_in_core_t **iclogp;
+ xlog_in_core_t *iclog, *prev_iclog=NULL;
+ xfs_buf_t *bp;
+ int i;
+ int iclogsize;
+
+ log = (xlog_t *)kmem_zalloc(sizeof(xlog_t), KM_SLEEP);
+
+ log->l_mp = mp;
+ log->l_targ = log_target;
+ log->l_logsize = BBTOB(num_bblks);
+ log->l_logBBstart = blk_offset;
+ log->l_logBBsize = num_bblks;
+ log->l_covered_state = XLOG_STATE_COVER_IDLE;
+ log->l_flags |= XLOG_ACTIVE_RECOVERY;
+
+ log->l_prev_block = -1;
+ ASSIGN_ANY_LSN(log->l_tail_lsn, 1, 0, ARCH_NOCONVERT);
+ /* log->l_tail_lsn = 0x100000000LL; cycle = 1; current block = 0 */
+ log->l_last_sync_lsn = log->l_tail_lsn;
+ log->l_curr_cycle = 1; /* 0 is bad since this is initial value */
+ log->l_grant_reserve_cycle = 1;
+ log->l_grant_write_cycle = 1;
+
+ if (XFS_SB_VERSION_HASSECTOR(&mp->m_sb)) {
+ log->l_sectbb_log = mp->m_sb.sb_logsectlog - BBSHIFT;
+ ASSERT(log->l_sectbb_log <= mp->m_sectbb_log);
+ /* for larger sector sizes, must have v2 or external log */
+ ASSERT(log->l_sectbb_log == 0 ||
+ log->l_logBBstart == 0 ||
+ XFS_SB_VERSION_HASLOGV2(&mp->m_sb));
+ ASSERT(mp->m_sb.sb_logsectlog >= BBSHIFT);
+ }
+ log->l_sectbb_mask = (1 << log->l_sectbb_log) - 1;
+
+ xlog_get_iclog_buffer_size(mp, log);
+
+ bp = xfs_buf_get_empty(log->l_iclog_size, mp->m_logdev_targp);
+ XFS_BUF_SET_IODONE_FUNC(bp, xlog_iodone);
+ XFS_BUF_SET_BDSTRAT_FUNC(bp, xlog_bdstrat_cb);
+ XFS_BUF_SET_FSPRIVATE2(bp, (unsigned long)1);
+ ASSERT(XFS_BUF_ISBUSY(bp));
+ ASSERT(XFS_BUF_VALUSEMA(bp) <= 0);
+ log->l_xbuf = bp;
+
+ spinlock_init(&log->l_icloglock, "iclog");
+ spinlock_init(&log->l_grant_lock, "grhead_iclog");
+ initnsema(&log->l_flushsema, 0, "ic-flush");
+ xlog_state_ticket_alloc(log); /* wait until after icloglock inited */
+
+ /* log record size must be multiple of BBSIZE; see xlog_rec_header_t */
+ ASSERT((XFS_BUF_SIZE(bp) & BBMASK) == 0);
+
+ iclogp = &log->l_iclog;
+ /*
+ * The amount of memory to allocate for the iclog structure is
+ * rather funky due to the way the structure is defined. It is
+ * done this way so that we can use different sizes for machines
+ * with different amounts of memory. See the definition of
+ * xlog_in_core_t in xfs_log_priv.h for details.
+ */
+ iclogsize = log->l_iclog_size;
+ ASSERT(log->l_iclog_size >= 4096);
+ for (i=0; i < log->l_iclog_bufs; i++) {
+ *iclogp = (xlog_in_core_t *)
+ kmem_zalloc(sizeof(xlog_in_core_t), KM_SLEEP);
+ iclog = *iclogp;
+ iclog->hic_data = (xlog_in_core_2_t *)
+ kmem_zalloc(iclogsize, KM_SLEEP);
+
+ iclog->ic_prev = prev_iclog;
+ prev_iclog = iclog;
+ log->l_iclog_bak[i] = (xfs_caddr_t)&(iclog->ic_header);
+
+ head = &iclog->ic_header;
+ memset(head, 0, sizeof(xlog_rec_header_t));
+ INT_SET(head->h_magicno, ARCH_CONVERT, XLOG_HEADER_MAGIC_NUM);
+ INT_SET(head->h_version, ARCH_CONVERT,
+ XFS_SB_VERSION_HASLOGV2(&log->l_mp->m_sb) ? 2 : 1);
+ INT_SET(head->h_size, ARCH_CONVERT, log->l_iclog_size);
+ /* new fields */
+ INT_SET(head->h_fmt, ARCH_CONVERT, XLOG_FMT);
+ memcpy(&head->h_fs_uuid, &mp->m_sb.sb_uuid, sizeof(uuid_t));
+
+ bp = xfs_buf_get_empty(log->l_iclog_size, mp->m_logdev_targp);
+ XFS_BUF_SET_IODONE_FUNC(bp, xlog_iodone);
+ XFS_BUF_SET_BDSTRAT_FUNC(bp, xlog_bdstrat_cb);
+ XFS_BUF_SET_FSPRIVATE2(bp, (unsigned long)1);
+ iclog->ic_bp = bp;
+
+ iclog->ic_size = XFS_BUF_SIZE(bp) - log->l_iclog_hsize;
+ iclog->ic_state = XLOG_STATE_ACTIVE;
+ iclog->ic_log = log;
+ iclog->ic_callback_tail = &(iclog->ic_callback);
+ iclog->ic_datap = (char *)iclog->hic_data + log->l_iclog_hsize;
+
+ ASSERT(XFS_BUF_ISBUSY(iclog->ic_bp));
+ ASSERT(XFS_BUF_VALUSEMA(iclog->ic_bp) <= 0);
+ sv_init(&iclog->ic_forcesema, SV_DEFAULT, "iclog-force");
+ sv_init(&iclog->ic_writesema, SV_DEFAULT, "iclog-write");
+
+ iclogp = &iclog->ic_next;
+ }
+ *iclogp = log->l_iclog; /* complete ring */
+ log->l_iclog->ic_prev = prev_iclog; /* re-write 1st prev ptr */
+
+ return log;
+} /* xlog_alloc_log */
+
+
+/*
+ * Write out the commit record of a transaction associated with the given
+ * ticket. Return the lsn of the commit record.
+ */
+STATIC int
+xlog_commit_record(xfs_mount_t *mp,
+ xlog_ticket_t *ticket,
+ xlog_in_core_t **iclog,
+ xfs_lsn_t *commitlsnp)
+{
+ int error;
+ xfs_log_iovec_t reg[1];
+
+ reg[0].i_addr = NULL;
+ reg[0].i_len = 0;
+
+ ASSERT_ALWAYS(iclog);
+ if ((error = xlog_write(mp, reg, 1, ticket, commitlsnp,
+ iclog, XLOG_COMMIT_TRANS))) {
+ xfs_force_shutdown(mp, XFS_LOG_IO_ERROR);
+ }
+ return (error);
+} /* xlog_commit_record */
+
+
+/*
+ * Push on the buffer cache code if we ever use more than 75% of the on-disk
+ * log space. This code pushes on the lsn which would supposedly free up
+ * the 25% which we want to leave free. We may need to adopt a policy which
+ * pushes on an lsn which is further along in the log once we reach the high
+ * water mark. In this manner, we would be creating a low water mark.
+ */
+void
+xlog_grant_push_ail(xfs_mount_t *mp,
+ int need_bytes)
+{
+ xlog_t *log = mp->m_log; /* pointer to the log */
+ xfs_lsn_t tail_lsn; /* lsn of the log tail */
+ xfs_lsn_t threshold_lsn = 0; /* lsn we'd like to be at */
+ int free_blocks; /* free blocks left to write to */
+ int free_bytes; /* free bytes left to write to */
+ int threshold_block; /* block in lsn we'd like to be at */
+ int threshold_cycle; /* lsn cycle we'd like to be at */
+ int free_threshold;
+ SPLDECL(s);
+
+ ASSERT(BTOBB(need_bytes) < log->l_logBBsize);
+
+ s = GRANT_LOCK(log);
+ free_bytes = xlog_space_left(log,
+ log->l_grant_reserve_cycle,
+ log->l_grant_reserve_bytes);
+ tail_lsn = log->l_tail_lsn;
+ free_blocks = BTOBBT(free_bytes);
+
+ /*
+ * Set the threshold for the minimum number of free blocks in the
+ * log to the maximum of what the caller needs, one quarter of the
+ * log, and 256 blocks.
+ */
+ free_threshold = BTOBB(need_bytes);
+ free_threshold = MAX(free_threshold, (log->l_logBBsize >> 2));
+ free_threshold = MAX(free_threshold, 256);
+ if (free_blocks < free_threshold) {
+ threshold_block = BLOCK_LSN(tail_lsn, ARCH_NOCONVERT) + free_threshold;
+ threshold_cycle = CYCLE_LSN(tail_lsn, ARCH_NOCONVERT);
+ if (threshold_block >= log->l_logBBsize) {
+ threshold_block -= log->l_logBBsize;
+ threshold_cycle += 1;
+ }
+ ASSIGN_ANY_LSN(threshold_lsn, threshold_cycle,
+ threshold_block, ARCH_NOCONVERT);
+
+ /* Don't pass in an lsn greater than the lsn of the last
+ * log record known to be on disk.
+ */
+ if (XFS_LSN_CMP_ARCH(threshold_lsn, log->l_last_sync_lsn, ARCH_NOCONVERT) > 0)
+ threshold_lsn = log->l_last_sync_lsn;
+ }
+ GRANT_UNLOCK(log, s);
+
+ /*
+ * Get the transaction layer to kick the dirty buffers out to
+ * disk asynchronously. No point in trying to do this if
+ * the filesystem is shutting down.
+ */
+ if (threshold_lsn &&
+ !XLOG_FORCED_SHUTDOWN(log))
+ xfs_trans_push_ail(mp, threshold_lsn);
+} /* xlog_grant_push_ail */
+
+
+/*
+ * Flush out the in-core log (iclog) to the on-disk log in an asynchronous
+ * fashion. Previously, we should have moved the current iclog
+ * ptr in the log to point to the next available iclog. This allows further
+ * write to continue while this code syncs out an iclog ready to go.
+ * Before an in-core log can be written out, the data section must be scanned
+ * to save away the 1st word of each BBSIZE block into the header. We replace
+ * it with the current cycle count. Each BBSIZE block is tagged with the
+ * cycle count because there in an implicit assumption that drives will
+ * guarantee that entire 512 byte blocks get written at once. In other words,
+ * we can't have part of a 512 byte block written and part not written. By
+ * tagging each block, we will know which blocks are valid when recovering
+ * after an unclean shutdown.
+ *
+ * This routine is single threaded on the iclog. No other thread can be in
+ * this routine with the same iclog. Changing contents of iclog can there-
+ * fore be done without grabbing the state machine lock. Updating the global
+ * log will require grabbing the lock though.
+ *
+ * The entire log manager uses a logical block numbering scheme. Only
+ * log_sync (and then only bwrite()) know about the fact that the log may
+ * not start with block zero on a given device. The log block start offset
+ * is added immediately before calling bwrite().
+ */
+
+int
+xlog_sync(xlog_t *log,
+ xlog_in_core_t *iclog)
+{
+ xfs_caddr_t dptr; /* pointer to byte sized element */
+ xfs_buf_t *bp;
+ int i, ops;
+ uint count; /* byte count of bwrite */
+ uint count_init; /* initial count before roundup */
+ int roundoff; /* roundoff to BB or stripe */
+ int split = 0; /* split write into two regions */
+ int error;
+ SPLDECL(s);
+ int v2 = XFS_SB_VERSION_HASLOGV2(&log->l_mp->m_sb);
+
+ XFS_STATS_INC(xs_log_writes);
+ ASSERT(iclog->ic_refcnt == 0);
+
+ /* Add for LR header */
+ count_init = log->l_iclog_hsize + iclog->ic_offset;
+
+ /* Round out the log write size */
+ if (v2 && log->l_mp->m_sb.sb_logsunit > 1) {
+ /* we have a v2 stripe unit to use */
+ count = XLOG_LSUNITTOB(log, XLOG_BTOLSUNIT(log, count_init));
+ } else {
+ count = BBTOB(BTOBB(count_init));
+ }
+ roundoff = count - count_init;
+ ASSERT(roundoff >= 0);
+ ASSERT((v2 && log->l_mp->m_sb.sb_logsunit > 1 &&
+ roundoff < log->l_mp->m_sb.sb_logsunit)
+ ||
+ (log->l_mp->m_sb.sb_logsunit <= 1 &&
+ roundoff < BBTOB(1)));
+
+ /* move grant heads by roundoff in sync */
+ s = GRANT_LOCK(log);
+ XLOG_GRANT_ADD_SPACE(log, roundoff, 'w');
+ XLOG_GRANT_ADD_SPACE(log, roundoff, 'r');
+ GRANT_UNLOCK(log, s);
+
+ /* put cycle number in every block */
+ xlog_pack_data(log, iclog, roundoff);
+
+ /* real byte length */
+ if (v2) {
+ INT_SET(iclog->ic_header.h_len,
+ ARCH_CONVERT,
+ iclog->ic_offset + roundoff);
+ } else {
+ INT_SET(iclog->ic_header.h_len, ARCH_CONVERT, iclog->ic_offset);
+ }
+
+ /* put ops count in correct order */
+ ops = iclog->ic_header.h_num_logops;
+ INT_SET(iclog->ic_header.h_num_logops, ARCH_CONVERT, ops);
+
+ bp = iclog->ic_bp;
+ ASSERT(XFS_BUF_FSPRIVATE2(bp, unsigned long) == (unsigned long)1);
+ XFS_BUF_SET_FSPRIVATE2(bp, (unsigned long)2);
+ XFS_BUF_SET_ADDR(bp, BLOCK_LSN(iclog->ic_header.h_lsn, ARCH_CONVERT));
+
+ XFS_STATS_ADD(xs_log_blocks, BTOBB(count));
+
+ /* Do we need to split this write into 2 parts? */
+ if (XFS_BUF_ADDR(bp) + BTOBB(count) > log->l_logBBsize) {
+ split = count - (BBTOB(log->l_logBBsize - XFS_BUF_ADDR(bp)));
+ count = BBTOB(log->l_logBBsize - XFS_BUF_ADDR(bp));
+ iclog->ic_bwritecnt = 2; /* split into 2 writes */
+ } else {
+ iclog->ic_bwritecnt = 1;
+ }
+ XFS_BUF_SET_PTR(bp, (xfs_caddr_t) &(iclog->ic_header), count);
+ XFS_BUF_SET_FSPRIVATE(bp, iclog); /* save for later */
+ XFS_BUF_BUSY(bp);
+ XFS_BUF_ASYNC(bp);
+ /*
+ * Do a disk write cache flush for the log block.
+ * This is a bit of a sledgehammer, it would be better
+ * to use a tag barrier here that just prevents reordering.
+ * It may not be needed to flush the first split block in the log wrap
+ * case, but do it anyways to be safe -AK
+ */
+ if (!(log->l_mp->m_flags & XFS_MOUNT_NOLOGFLUSH))
+ XFS_BUF_FLUSH(bp);
+
+ ASSERT(XFS_BUF_ADDR(bp) <= log->l_logBBsize-1);
+ ASSERT(XFS_BUF_ADDR(bp) + BTOBB(count) <= log->l_logBBsize);
+
+ xlog_verify_iclog(log, iclog, count, B_TRUE);
+
+ /* account for log which doesn't start at block #0 */
+ XFS_BUF_SET_ADDR(bp, XFS_BUF_ADDR(bp) + log->l_logBBstart);
+ /*
+ * Don't call xfs_bwrite here. We do log-syncs even when the filesystem
+ * is shutting down.
+ */
+ XFS_BUF_WRITE(bp);
+
+ if ((error = XFS_bwrite(bp))) {
+ xfs_ioerror_alert("xlog_sync", log->l_mp, bp,
+ XFS_BUF_ADDR(bp));
+ return (error);
+ }
+ if (split) {
+ bp = iclog->ic_log->l_xbuf;
+ ASSERT(XFS_BUF_FSPRIVATE2(bp, unsigned long) ==
+ (unsigned long)1);
+ XFS_BUF_SET_FSPRIVATE2(bp, (unsigned long)2);
+ XFS_BUF_SET_ADDR(bp, 0); /* logical 0 */
+ XFS_BUF_SET_PTR(bp, (xfs_caddr_t)((__psint_t)&(iclog->ic_header)+
+ (__psint_t)count), split);
+ XFS_BUF_SET_FSPRIVATE(bp, iclog);
+ XFS_BUF_BUSY(bp);
+ XFS_BUF_ASYNC(bp);
+ if (!(log->l_mp->m_flags & XFS_MOUNT_NOLOGFLUSH))
+ XFS_BUF_FLUSH(bp);
+ dptr = XFS_BUF_PTR(bp);
+ /*
+ * Bump the cycle numbers at the start of each block
+ * since this part of the buffer is at the start of
+ * a new cycle. Watch out for the header magic number
+ * case, though.
+ */
+ for (i=0; i<split; i += BBSIZE) {
+ INT_MOD(*(uint *)dptr, ARCH_CONVERT, +1);
+ if (INT_GET(*(uint *)dptr, ARCH_CONVERT) == XLOG_HEADER_MAGIC_NUM)
+ INT_MOD(*(uint *)dptr, ARCH_CONVERT, +1);
+ dptr += BBSIZE;
+ }
+
+ ASSERT(XFS_BUF_ADDR(bp) <= log->l_logBBsize-1);
+ ASSERT(XFS_BUF_ADDR(bp) + BTOBB(count) <= log->l_logBBsize);
+
+ /* account for internal log which does't start at block #0 */
+ XFS_BUF_SET_ADDR(bp, XFS_BUF_ADDR(bp) + log->l_logBBstart);
+ XFS_BUF_WRITE(bp);
+ if ((error = XFS_bwrite(bp))) {
+ xfs_ioerror_alert("xlog_sync (split)", log->l_mp,
+ bp, XFS_BUF_ADDR(bp));
+ return (error);
+ }
+ }
+ return (0);
+} /* xlog_sync */
+
+
+/*
+ * Unallocate a log structure
+ */
+void
+xlog_unalloc_log(xlog_t *log)
+{
+ xlog_in_core_t *iclog, *next_iclog;
+ xlog_ticket_t *tic, *next_tic;
+ int i;
+
+
+ iclog = log->l_iclog;
+ for (i=0; i<log->l_iclog_bufs; i++) {
+ sv_destroy(&iclog->ic_forcesema);
+ sv_destroy(&iclog->ic_writesema);
+ xfs_buf_free(iclog->ic_bp);
+#ifdef XFS_LOG_TRACE
+ if (iclog->ic_trace != NULL) {
+ ktrace_free(iclog->ic_trace);
+ }
+#endif
+ next_iclog = iclog->ic_next;
+ kmem_free(iclog->hic_data, log->l_iclog_size);
+ kmem_free(iclog, sizeof(xlog_in_core_t));
+ iclog = next_iclog;
+ }
+ freesema(&log->l_flushsema);
+ spinlock_destroy(&log->l_icloglock);
+ spinlock_destroy(&log->l_grant_lock);
+
+ /* XXXsup take a look at this again. */
+ if ((log->l_ticket_cnt != log->l_ticket_tcnt) &&
+ !XLOG_FORCED_SHUTDOWN(log)) {
+ xfs_fs_cmn_err(CE_WARN, log->l_mp,
+ "xlog_unalloc_log: (cnt: %d, total: %d)",
+ log->l_ticket_cnt, log->l_ticket_tcnt);
+ /* ASSERT(log->l_ticket_cnt == log->l_ticket_tcnt); */
+
+ } else {
+ tic = log->l_unmount_free;
+ while (tic) {
+ next_tic = tic->t_next;
+ kmem_free(tic, NBPP);
+ tic = next_tic;
+ }
+ }
+ xfs_buf_free(log->l_xbuf);
+#ifdef XFS_LOG_TRACE
+ if (log->l_trace != NULL) {
+ ktrace_free(log->l_trace);
+ }
+ if (log->l_grant_trace != NULL) {
+ ktrace_free(log->l_grant_trace);
+ }
+#endif
+ log->l_mp->m_log = NULL;
+ kmem_free(log, sizeof(xlog_t));
+} /* xlog_unalloc_log */
+
+/*
+ * Update counters atomically now that memcpy is done.
+ */
+/* ARGSUSED */
+static inline void
+xlog_state_finish_copy(xlog_t *log,
+ xlog_in_core_t *iclog,
+ int record_cnt,
+ int copy_bytes)
+{
+ SPLDECL(s);
+
+ s = LOG_LOCK(log);
+
+ iclog->ic_header.h_num_logops += record_cnt;
+ iclog->ic_offset += copy_bytes;
+
+ LOG_UNLOCK(log, s);
+} /* xlog_state_finish_copy */
+
+
+
+
+/*
+ * Write some region out to in-core log
+ *
+ * This will be called when writing externally provided regions or when
+ * writing out a commit record for a given transaction.
+ *
+ * General algorithm:
+ * 1. Find total length of this write. This may include adding to the
+ * lengths passed in.
+ * 2. Check whether we violate the tickets reservation.
+ * 3. While writing to this iclog
+ * A. Reserve as much space in this iclog as can get
+ * B. If this is first write, save away start lsn
+ * C. While writing this region:
+ * 1. If first write of transaction, write start record
+ * 2. Write log operation header (header per region)
+ * 3. Find out if we can fit entire region into this iclog
+ * 4. Potentially, verify destination memcpy ptr
+ * 5. Memcpy (partial) region
+ * 6. If partial copy, release iclog; otherwise, continue
+ * copying more regions into current iclog
+ * 4. Mark want sync bit (in simulation mode)
+ * 5. Release iclog for potential flush to on-disk log.
+ *
+ * ERRORS:
+ * 1. Panic if reservation is overrun. This should never happen since
+ * reservation amounts are generated internal to the filesystem.
+ * NOTES:
+ * 1. Tickets are single threaded data structures.
+ * 2. The XLOG_END_TRANS & XLOG_CONTINUE_TRANS flags are passed down to the
+ * syncing routine. When a single log_write region needs to span
+ * multiple in-core logs, the XLOG_CONTINUE_TRANS bit should be set
+ * on all log operation writes which don't contain the end of the
+ * region. The XLOG_END_TRANS bit is used for the in-core log
+ * operation which contains the end of the continued log_write region.
+ * 3. When xlog_state_get_iclog_space() grabs the rest of the current iclog,
+ * we don't really know exactly how much space will be used. As a result,
+ * we don't update ic_offset until the end when we know exactly how many
+ * bytes have been written out.
+ */
+int
+xlog_write(xfs_mount_t * mp,
+ xfs_log_iovec_t reg[],
+ int nentries,
+ xfs_log_ticket_t tic,
+ xfs_lsn_t *start_lsn,
+ xlog_in_core_t **commit_iclog,
+ uint flags)
+{
+ xlog_t *log = mp->m_log;
+ xlog_ticket_t *ticket = (xlog_ticket_t *)tic;
+ xlog_op_header_t *logop_head; /* ptr to log operation header */
+ xlog_in_core_t *iclog; /* ptr to current in-core log */
+ __psint_t ptr; /* copy address into data region */
+ int len; /* # xlog_write() bytes 2 still copy */
+ int index; /* region index currently copying */
+ int log_offset; /* offset (from 0) into data region */
+ int start_rec_copy; /* # bytes to copy for start record */
+ int partial_copy; /* did we split a region? */
+ int partial_copy_len;/* # bytes copied if split region */
+ int need_copy; /* # bytes need to memcpy this region */
+ int copy_len; /* # bytes actually memcpy'ing */
+ int copy_off; /* # bytes from entry start */
+ int contwr; /* continued write of in-core log? */
+ int error;
+ int record_cnt = 0, data_cnt = 0;
+
+ partial_copy_len = partial_copy = 0;
+
+ /* Calculate potential maximum space. Each region gets its own
+ * xlog_op_header_t and may need to be double word aligned.
+ */
+ len = 0;
+ if (ticket->t_flags & XLOG_TIC_INITED) /* acct for start rec of xact */
+ len += sizeof(xlog_op_header_t);
+
+ for (index = 0; index < nentries; index++) {
+ len += sizeof(xlog_op_header_t); /* each region gets >= 1 */
+ len += reg[index].i_len;
+ }
+ contwr = *start_lsn = 0;
+
+ if (ticket->t_curr_res < len) {
+#ifdef DEBUG
+ xlog_panic(
+ "xfs_log_write: reservation ran out. Need to up reservation");
+#else
+ /* Customer configurable panic */
+ xfs_cmn_err(XFS_PTAG_LOGRES, CE_ALERT, mp,
+ "xfs_log_write: reservation ran out. Need to up reservation");
+ /* If we did not panic, shutdown the filesystem */
+ xfs_force_shutdown(mp, XFS_CORRUPT_INCORE);
+#endif
+ } else
+ ticket->t_curr_res -= len;
+
+ for (index = 0; index < nentries; ) {
+ if ((error = xlog_state_get_iclog_space(log, len, &iclog, ticket,
+ &contwr, &log_offset)))
+ return (error);
+
+ ASSERT(log_offset <= iclog->ic_size - 1);
+ ptr = (__psint_t) ((char *)iclog->ic_datap+log_offset);
+
+ /* start_lsn is the first lsn written to. That's all we need. */
+ if (! *start_lsn)
+ *start_lsn = INT_GET(iclog->ic_header.h_lsn, ARCH_CONVERT);
+
+ /* This loop writes out as many regions as can fit in the amount
+ * of space which was allocated by xlog_state_get_iclog_space().
+ */
+ while (index < nentries) {
+ ASSERT(reg[index].i_len % sizeof(__int32_t) == 0);
+ ASSERT((__psint_t)ptr % sizeof(__int32_t) == 0);
+ start_rec_copy = 0;
+
+ /* If first write for transaction, insert start record.
+ * We can't be trying to commit if we are inited. We can't
+ * have any "partial_copy" if we are inited.
+ */
+ if (ticket->t_flags & XLOG_TIC_INITED) {
+ logop_head = (xlog_op_header_t *)ptr;
+ INT_SET(logop_head->oh_tid, ARCH_CONVERT, ticket->t_tid);
+ logop_head->oh_clientid = ticket->t_clientid;
+ INT_ZERO(logop_head->oh_len, ARCH_CONVERT);
+ logop_head->oh_flags = XLOG_START_TRANS;
+ INT_ZERO(logop_head->oh_res2, ARCH_CONVERT);
+ ticket->t_flags &= ~XLOG_TIC_INITED; /* clear bit */
+ record_cnt++;
+
+ start_rec_copy = sizeof(xlog_op_header_t);
+ xlog_write_adv_cnt(ptr, len, log_offset, start_rec_copy);
+ }
+
+ /* Copy log operation header directly into data section */
+ logop_head = (xlog_op_header_t *)ptr;
+ INT_SET(logop_head->oh_tid, ARCH_CONVERT, ticket->t_tid);
+ logop_head->oh_clientid = ticket->t_clientid;
+ INT_ZERO(logop_head->oh_res2, ARCH_CONVERT);
+
+ /* header copied directly */
+ xlog_write_adv_cnt(ptr, len, log_offset, sizeof(xlog_op_header_t));
+
+ /* are we copying a commit or unmount record? */
+ logop_head->oh_flags = flags;
+
+ /*
+ * We've seen logs corrupted with bad transaction client
+ * ids. This makes sure that XFS doesn't generate them on.
+ * Turn this into an EIO and shut down the filesystem.
+ */
+ switch (logop_head->oh_clientid) {
+ case XFS_TRANSACTION:
+ case XFS_VOLUME:
+ case XFS_LOG:
+ break;
+ default:
+ xfs_fs_cmn_err(CE_WARN, mp,
+ "Bad XFS transaction clientid 0x%x in ticket 0x%p",
+ logop_head->oh_clientid, tic);
+ return XFS_ERROR(EIO);
+ }
+
+ /* Partial write last time? => (partial_copy != 0)
+ * need_copy is the amount we'd like to copy if everything could
+ * fit in the current memcpy.
+ */
+ need_copy = reg[index].i_len - partial_copy_len;
+
+ copy_off = partial_copy_len;
+ if (need_copy <= iclog->ic_size - log_offset) { /*complete write */
+ INT_SET(logop_head->oh_len, ARCH_CONVERT, copy_len = need_copy);
+ if (partial_copy)
+ logop_head->oh_flags|= (XLOG_END_TRANS|XLOG_WAS_CONT_TRANS);
+ partial_copy_len = partial_copy = 0;
+ } else { /* partial write */
+ copy_len = iclog->ic_size - log_offset;
+ INT_SET(logop_head->oh_len, ARCH_CONVERT, copy_len);
+ logop_head->oh_flags |= XLOG_CONTINUE_TRANS;
+ if (partial_copy)
+ logop_head->oh_flags |= XLOG_WAS_CONT_TRANS;
+ partial_copy_len += copy_len;
+ partial_copy++;
+ len += sizeof(xlog_op_header_t); /* from splitting of region */
+ /* account for new log op header */
+ ticket->t_curr_res -= sizeof(xlog_op_header_t);
+ }
+ xlog_verify_dest_ptr(log, ptr);
+
+ /* copy region */
+ ASSERT(copy_len >= 0);
+ memcpy((xfs_caddr_t)ptr, reg[index].i_addr + copy_off, copy_len);
+ xlog_write_adv_cnt(ptr, len, log_offset, copy_len);
+
+ /* make copy_len total bytes copied, including headers */
+ copy_len += start_rec_copy + sizeof(xlog_op_header_t);
+ record_cnt++;
+ data_cnt += contwr ? copy_len : 0;
+ if (partial_copy) { /* copied partial region */
+ /* already marked WANT_SYNC by xlog_state_get_iclog_space */
+ xlog_state_finish_copy(log, iclog, record_cnt, data_cnt);
+ record_cnt = data_cnt = 0;
+ if ((error = xlog_state_release_iclog(log, iclog)))
+ return (error);
+ break; /* don't increment index */
+ } else { /* copied entire region */
+ index++;
+ partial_copy_len = partial_copy = 0;
+
+ if (iclog->ic_size - log_offset <= sizeof(xlog_op_header_t)) {
+ xlog_state_finish_copy(log, iclog, record_cnt, data_cnt);
+ record_cnt = data_cnt = 0;
+ xlog_state_want_sync(log, iclog);
+ if (commit_iclog) {
+ ASSERT(flags & XLOG_COMMIT_TRANS);
+ *commit_iclog = iclog;
+ } else if ((error = xlog_state_release_iclog(log, iclog)))
+ return (error);
+ if (index == nentries)
+ return 0; /* we are done */
+ else
+ break;
+ }
+ } /* if (partial_copy) */
+ } /* while (index < nentries) */
+ } /* for (index = 0; index < nentries; ) */
+ ASSERT(len == 0);
+
+ xlog_state_finish_copy(log, iclog, record_cnt, data_cnt);
+ if (commit_iclog) {
+ ASSERT(flags & XLOG_COMMIT_TRANS);
+ *commit_iclog = iclog;
+ return 0;
+ }
+ return (xlog_state_release_iclog(log, iclog));
+} /* xlog_write */
+
+
+/*****************************************************************************
+ *
+ * State Machine functions
+ *
+ *****************************************************************************
+ */
+
+/* Clean iclogs starting from the head. This ordering must be
+ * maintained, so an iclog doesn't become ACTIVE beyond one that
+ * is SYNCING. This is also required to maintain the notion that we use
+ * a counting semaphore to hold off would be writers to the log when every
+ * iclog is trying to sync to disk.
+ *
+ * State Change: DIRTY -> ACTIVE
+ */
+void
+xlog_state_clean_log(xlog_t *log)
+{
+ xlog_in_core_t *iclog;
+ int changed = 0;
+
+ iclog = log->l_iclog;
+ do {
+ if (iclog->ic_state == XLOG_STATE_DIRTY) {
+ iclog->ic_state = XLOG_STATE_ACTIVE;
+ iclog->ic_offset = 0;
+ iclog->ic_callback = NULL; /* don't need to free */
+ /*
+ * If the number of ops in this iclog indicate it just
+ * contains the dummy transaction, we can
+ * change state into IDLE (the second time around).
+ * Otherwise we should change the state into
+ * NEED a dummy.
+ * We don't need to cover the dummy.
+ */
+ if (!changed &&
+ (INT_GET(iclog->ic_header.h_num_logops, ARCH_CONVERT) == XLOG_COVER_OPS)) {
+ changed = 1;
+ } else {
+ /*
+ * We have two dirty iclogs so start over
+ * This could also be num of ops indicates
+ * this is not the dummy going out.
+ */
+ changed = 2;
+ }
+ INT_ZERO(iclog->ic_header.h_num_logops, ARCH_CONVERT);
+ memset(iclog->ic_header.h_cycle_data, 0,
+ sizeof(iclog->ic_header.h_cycle_data));
+ INT_ZERO(iclog->ic_header.h_lsn, ARCH_CONVERT);
+ } else if (iclog->ic_state == XLOG_STATE_ACTIVE)
+ /* do nothing */;
+ else
+ break; /* stop cleaning */
+ iclog = iclog->ic_next;
+ } while (iclog != log->l_iclog);
+
+ /* log is locked when we are called */
+ /*
+ * Change state for the dummy log recording.
+ * We usually go to NEED. But we go to NEED2 if the changed indicates
+ * we are done writing the dummy record.
+ * If we are done with the second dummy recored (DONE2), then
+ * we go to IDLE.
+ */
+ if (changed) {
+ switch (log->l_covered_state) {
+ case XLOG_STATE_COVER_IDLE:
+ case XLOG_STATE_COVER_NEED:
+ case XLOG_STATE_COVER_NEED2:
+ log->l_covered_state = XLOG_STATE_COVER_NEED;
+ break;
+
+ case XLOG_STATE_COVER_DONE:
+ if (changed == 1)
+ log->l_covered_state = XLOG_STATE_COVER_NEED2;
+ else
+ log->l_covered_state = XLOG_STATE_COVER_NEED;
+ break;
+
+ case XLOG_STATE_COVER_DONE2:
+ if (changed == 1)
+ log->l_covered_state = XLOG_STATE_COVER_IDLE;
+ else
+ log->l_covered_state = XLOG_STATE_COVER_NEED;
+ break;
+
+ default:
+ ASSERT(0);
+ }
+ }
+} /* xlog_state_clean_log */
+
+STATIC xfs_lsn_t
+xlog_get_lowest_lsn(
+ xlog_t *log)
+{
+ xlog_in_core_t *lsn_log;
+ xfs_lsn_t lowest_lsn, lsn;
+
+ lsn_log = log->l_iclog;
+ lowest_lsn = 0;
+ do {
+ if (!(lsn_log->ic_state & (XLOG_STATE_ACTIVE|XLOG_STATE_DIRTY))) {
+ lsn = INT_GET(lsn_log->ic_header.h_lsn, ARCH_CONVERT);
+ if ((lsn && !lowest_lsn) ||
+ (XFS_LSN_CMP_ARCH(lsn, lowest_lsn, ARCH_NOCONVERT) < 0)) {
+ lowest_lsn = lsn;
+ }
+ }
+ lsn_log = lsn_log->ic_next;
+ } while (lsn_log != log->l_iclog);
+ return(lowest_lsn);
+}
+
+
+STATIC void
+xlog_state_do_callback(
+ xlog_t *log,
+ int aborted,
+ xlog_in_core_t *ciclog)
+{
+ xlog_in_core_t *iclog;
+ xlog_in_core_t *first_iclog; /* used to know when we've
+ * processed all iclogs once */
+ xfs_log_callback_t *cb, *cb_next;
+ int flushcnt = 0;
+ xfs_lsn_t lowest_lsn;
+ int ioerrors; /* counter: iclogs with errors */
+ int loopdidcallbacks; /* flag: inner loop did callbacks*/
+ int funcdidcallbacks; /* flag: function did callbacks */
+ int repeats; /* for issuing console warnings if
+ * looping too many times */
+ SPLDECL(s);
+
+ s = LOG_LOCK(log);
+ first_iclog = iclog = log->l_iclog;
+ ioerrors = 0;
+ funcdidcallbacks = 0;
+ repeats = 0;
+
+ do {
+ /*
+ * Scan all iclogs starting with the one pointed to by the
+ * log. Reset this starting point each time the log is
+ * unlocked (during callbacks).
+ *
+ * Keep looping through iclogs until one full pass is made
+ * without running any callbacks.
+ */
+ first_iclog = log->l_iclog;
+ iclog = log->l_iclog;
+ loopdidcallbacks = 0;
+ repeats++;
+
+ do {
+
+ /* skip all iclogs in the ACTIVE & DIRTY states */
+ if (iclog->ic_state &
+ (XLOG_STATE_ACTIVE|XLOG_STATE_DIRTY)) {
+ iclog = iclog->ic_next;
+ continue;
+ }
+
+ /*
+ * Between marking a filesystem SHUTDOWN and stopping
+ * the log, we do flush all iclogs to disk (if there
+ * wasn't a log I/O error). So, we do want things to
+ * go smoothly in case of just a SHUTDOWN w/o a
+ * LOG_IO_ERROR.
+ */
+ if (!(iclog->ic_state & XLOG_STATE_IOERROR)) {
+ /*
+ * Can only perform callbacks in order. Since
+ * this iclog is not in the DONE_SYNC/
+ * DO_CALLBACK state, we skip the rest and
+ * just try to clean up. If we set our iclog
+ * to DO_CALLBACK, we will not process it when
+ * we retry since a previous iclog is in the
+ * CALLBACK and the state cannot change since
+ * we are holding the LOG_LOCK.
+ */
+ if (!(iclog->ic_state &
+ (XLOG_STATE_DONE_SYNC |
+ XLOG_STATE_DO_CALLBACK))) {
+ if (ciclog && (ciclog->ic_state ==
+ XLOG_STATE_DONE_SYNC)) {
+ ciclog->ic_state = XLOG_STATE_DO_CALLBACK;
+ }
+ break;
+ }
+ /*
+ * We now have an iclog that is in either the
+ * DO_CALLBACK or DONE_SYNC states. The other
+ * states (WANT_SYNC, SYNCING, or CALLBACK were
+ * caught by the above if and are going to
+ * clean (i.e. we aren't doing their callbacks)
+ * see the above if.
+ */
+
+ /*
+ * We will do one more check here to see if we
+ * have chased our tail around.
+ */
+
+ lowest_lsn = xlog_get_lowest_lsn(log);
+ if (lowest_lsn && (
+ XFS_LSN_CMP_ARCH(
+ lowest_lsn,
+ INT_GET(iclog->ic_header.h_lsn, ARCH_CONVERT),
+ ARCH_NOCONVERT
+ )<0)) {
+ iclog = iclog->ic_next;
+ continue; /* Leave this iclog for
+ * another thread */
+ }
+
+ iclog->ic_state = XLOG_STATE_CALLBACK;
+
+ LOG_UNLOCK(log, s);
+
+ /* l_last_sync_lsn field protected by
+ * GRANT_LOCK. Don't worry about iclog's lsn.
+ * No one else can be here except us.
+ */
+ s = GRANT_LOCK(log);
+ ASSERT(XFS_LSN_CMP_ARCH(
+ log->l_last_sync_lsn,
+ INT_GET(iclog->ic_header.h_lsn, ARCH_CONVERT),
+ ARCH_NOCONVERT
+ )<=0);
+ log->l_last_sync_lsn = INT_GET(iclog->ic_header.h_lsn, ARCH_CONVERT);
+ GRANT_UNLOCK(log, s);
+
+ /*
+ * Keep processing entries in the callback list
+ * until we come around and it is empty. We
+ * need to atomically see that the list is
+ * empty and change the state to DIRTY so that
+ * we don't miss any more callbacks being added.
+ */
+ s = LOG_LOCK(log);
+ } else {
+ ioerrors++;
+ }
+ cb = iclog->ic_callback;
+
+ while (cb != 0) {
+ iclog->ic_callback_tail = &(iclog->ic_callback);
+ iclog->ic_callback = NULL;
+ LOG_UNLOCK(log, s);
+
+ /* perform callbacks in the order given */
+ for (; cb != 0; cb = cb_next) {
+ cb_next = cb->cb_next;
+ cb->cb_func(cb->cb_arg, aborted);
+ }
+ s = LOG_LOCK(log);
+ cb = iclog->ic_callback;
+ }
+
+ loopdidcallbacks++;
+ funcdidcallbacks++;
+
+ ASSERT(iclog->ic_callback == 0);
+ if (!(iclog->ic_state & XLOG_STATE_IOERROR))
+ iclog->ic_state = XLOG_STATE_DIRTY;
+
+ /*
+ * Transition from DIRTY to ACTIVE if applicable.
+ * NOP if STATE_IOERROR.
+ */
+ xlog_state_clean_log(log);
+
+ /* wake up threads waiting in xfs_log_force() */
+ sv_broadcast(&iclog->ic_forcesema);
+
+ iclog = iclog->ic_next;
+ } while (first_iclog != iclog);
+ if (repeats && (repeats % 10) == 0) {
+ xfs_fs_cmn_err(CE_WARN, log->l_mp,
+ "xlog_state_do_callback: looping %d", repeats);
+ }
+ } while (!ioerrors && loopdidcallbacks);
+
+ /*
+ * make one last gasp attempt to see if iclogs are being left in
+ * limbo..
+ */
+#ifdef DEBUG
+ if (funcdidcallbacks) {
+ first_iclog = iclog = log->l_iclog;
+ do {
+ ASSERT(iclog->ic_state != XLOG_STATE_DO_CALLBACK);
+ /*
+ * Terminate the loop if iclogs are found in states
+ * which will cause other threads to clean up iclogs.
+ *
+ * SYNCING - i/o completion will go through logs
+ * DONE_SYNC - interrupt thread should be waiting for
+ * LOG_LOCK
+ * IOERROR - give up hope all ye who enter here
+ */
+ if (iclog->ic_state == XLOG_STATE_WANT_SYNC ||
+ iclog->ic_state == XLOG_STATE_SYNCING ||
+ iclog->ic_state == XLOG_STATE_DONE_SYNC ||
+ iclog->ic_state == XLOG_STATE_IOERROR )
+ break;
+ iclog = iclog->ic_next;
+ } while (first_iclog != iclog);
+ }
+#endif
+
+ if (log->l_iclog->ic_state & (XLOG_STATE_ACTIVE|XLOG_STATE_IOERROR)) {
+ flushcnt = log->l_flushcnt;
+ log->l_flushcnt = 0;
+ }
+ LOG_UNLOCK(log, s);
+ while (flushcnt--)
+ vsema(&log->l_flushsema);
+} /* xlog_state_do_callback */
+
+
+/*
+ * Finish transitioning this iclog to the dirty state.
+ *
+ * Make sure that we completely execute this routine only when this is
+ * the last call to the iclog. There is a good chance that iclog flushes,
+ * when we reach the end of the physical log, get turned into 2 separate
+ * calls to bwrite. Hence, one iclog flush could generate two calls to this
+ * routine. By using the reference count bwritecnt, we guarantee that only
+ * the second completion goes through.
+ *
+ * Callbacks could take time, so they are done outside the scope of the
+ * global state machine log lock. Assume that the calls to cvsema won't
+ * take a long time. At least we know it won't sleep.
+ */
+void
+xlog_state_done_syncing(
+ xlog_in_core_t *iclog,
+ int aborted)
+{
+ xlog_t *log = iclog->ic_log;
+ SPLDECL(s);
+
+ s = LOG_LOCK(log);
+
+ ASSERT(iclog->ic_state == XLOG_STATE_SYNCING ||
+ iclog->ic_state == XLOG_STATE_IOERROR);
+ ASSERT(iclog->ic_refcnt == 0);
+ ASSERT(iclog->ic_bwritecnt == 1 || iclog->ic_bwritecnt == 2);
+
+
+ /*
+ * If we got an error, either on the first buffer, or in the case of
+ * split log writes, on the second, we mark ALL iclogs STATE_IOERROR,
+ * and none should ever be attempted to be written to disk
+ * again.
+ */
+ if (iclog->ic_state != XLOG_STATE_IOERROR) {
+ if (--iclog->ic_bwritecnt == 1) {
+ LOG_UNLOCK(log, s);
+ return;
+ }
+ iclog->ic_state = XLOG_STATE_DONE_SYNC;
+ }
+
+ /*
+ * Someone could be sleeping prior to writing out the next
+ * iclog buffer, we wake them all, one will get to do the
+ * I/O, the others get to wait for the result.
+ */
+ sv_broadcast(&iclog->ic_writesema);
+ LOG_UNLOCK(log, s);
+ xlog_state_do_callback(log, aborted, iclog); /* also cleans log */
+} /* xlog_state_done_syncing */
+
+
+/*
+ * If the head of the in-core log ring is not (ACTIVE or DIRTY), then we must
+ * sleep. The flush semaphore is set to the number of in-core buffers and
+ * decremented around disk syncing. Therefore, if all buffers are syncing,
+ * this semaphore will cause new writes to sleep until a sync completes.
+ * Otherwise, this code just does p() followed by v(). This approximates
+ * a sleep/wakeup except we can't race.
+ *
+ * The in-core logs are used in a circular fashion. They are not used
+ * out-of-order even when an iclog past the head is free.
+ *
+ * return:
+ * * log_offset where xlog_write() can start writing into the in-core
+ * log's data space.
+ * * in-core log pointer to which xlog_write() should write.
+ * * boolean indicating this is a continued write to an in-core log.
+ * If this is the last write, then the in-core log's offset field
+ * needs to be incremented, depending on the amount of data which
+ * is copied.
+ */
+int
+xlog_state_get_iclog_space(xlog_t *log,
+ int len,
+ xlog_in_core_t **iclogp,
+ xlog_ticket_t *ticket,
+ int *continued_write,
+ int *logoffsetp)
+{
+ SPLDECL(s);
+ int log_offset;
+ xlog_rec_header_t *head;
+ xlog_in_core_t *iclog;
+ int error;
+
+restart:
+ s = LOG_LOCK(log);
+ if (XLOG_FORCED_SHUTDOWN(log)) {
+ LOG_UNLOCK(log, s);
+ return XFS_ERROR(EIO);
+ }
+
+ iclog = log->l_iclog;
+ if (! (iclog->ic_state == XLOG_STATE_ACTIVE)) {
+ log->l_flushcnt++;
+ LOG_UNLOCK(log, s);
+ xlog_trace_iclog(iclog, XLOG_TRACE_SLEEP_FLUSH);
+ XFS_STATS_INC(xs_log_noiclogs);
+ /* Ensure that log writes happen */
+ psema(&log->l_flushsema, PINOD);
+ goto restart;
+ }
+ ASSERT(iclog->ic_state == XLOG_STATE_ACTIVE);
+ head = &iclog->ic_header;
+
+ iclog->ic_refcnt++; /* prevents sync */
+ log_offset = iclog->ic_offset;
+
+ /* On the 1st write to an iclog, figure out lsn. This works
+ * if iclogs marked XLOG_STATE_WANT_SYNC always write out what they are
+ * committing to. If the offset is set, that's how many blocks
+ * must be written.
+ */
+ if (log_offset == 0) {
+ ticket->t_curr_res -= log->l_iclog_hsize;
+ INT_SET(head->h_cycle, ARCH_CONVERT, log->l_curr_cycle);
+ ASSIGN_LSN(head->h_lsn, log, ARCH_CONVERT);
+ ASSERT(log->l_curr_block >= 0);
+ }
+
+ /* If there is enough room to write everything, then do it. Otherwise,
+ * claim the rest of the region and make sure the XLOG_STATE_WANT_SYNC
+ * bit is on, so this will get flushed out. Don't update ic_offset
+ * until you know exactly how many bytes get copied. Therefore, wait
+ * until later to update ic_offset.
+ *
+ * xlog_write() algorithm assumes that at least 2 xlog_op_header_t's
+ * can fit into remaining data section.
+ */
+ if (iclog->ic_size - iclog->ic_offset < 2*sizeof(xlog_op_header_t)) {
+ xlog_state_switch_iclogs(log, iclog, iclog->ic_size);
+
+ /* If I'm the only one writing to this iclog, sync it to disk */
+ if (iclog->ic_refcnt == 1) {
+ LOG_UNLOCK(log, s);
+ if ((error = xlog_state_release_iclog(log, iclog)))
+ return (error);
+ } else {
+ iclog->ic_refcnt--;
+ LOG_UNLOCK(log, s);
+ }
+ goto restart;
+ }
+
+ /* Do we have enough room to write the full amount in the remainder
+ * of this iclog? Or must we continue a write on the next iclog and
+ * mark this iclog as completely taken? In the case where we switch
+ * iclogs (to mark it taken), this particular iclog will release/sync
+ * to disk in xlog_write().
+ */
+ if (len <= iclog->ic_size - iclog->ic_offset) {
+ *continued_write = 0;
+ iclog->ic_offset += len;
+ } else {
+ *continued_write = 1;
+ xlog_state_switch_iclogs(log, iclog, iclog->ic_size);
+ }
+ *iclogp = iclog;
+
+ ASSERT(iclog->ic_offset <= iclog->ic_size);
+ LOG_UNLOCK(log, s);
+
+ *logoffsetp = log_offset;
+ return 0;
+} /* xlog_state_get_iclog_space */
+
+/*
+ * Atomically get the log space required for a log ticket.
+ *
+ * Once a ticket gets put onto the reserveq, it will only return after
+ * the needed reservation is satisfied.
+ */
+STATIC int
+xlog_grant_log_space(xlog_t *log,
+ xlog_ticket_t *tic)
+{
+ int free_bytes;
+ int need_bytes;
+ SPLDECL(s);
+#ifdef DEBUG
+ xfs_lsn_t tail_lsn;
+#endif
+
+
+#ifdef DEBUG
+ if (log->l_flags & XLOG_ACTIVE_RECOVERY)
+ panic("grant Recovery problem");
+#endif
+
+ /* Is there space or do we need to sleep? */
+ s = GRANT_LOCK(log);
+ xlog_trace_loggrant(log, tic, "xlog_grant_log_space: enter");
+
+ /* something is already sleeping; insert new transaction at end */
+ if (log->l_reserve_headq) {
+ XLOG_INS_TICKETQ(log->l_reserve_headq, tic);
+ xlog_trace_loggrant(log, tic,
+ "xlog_grant_log_space: sleep 1");
+ /*
+ * Gotta check this before going to sleep, while we're
+ * holding the grant lock.
+ */
+ if (XLOG_FORCED_SHUTDOWN(log))
+ goto error_return;
+
+ XFS_STATS_INC(xs_sleep_logspace);
+ sv_wait(&tic->t_sema, PINOD|PLTWAIT, &log->l_grant_lock, s);
+ /*
+ * If we got an error, and the filesystem is shutting down,
+ * we'll catch it down below. So just continue...
+ */
+ xlog_trace_loggrant(log, tic,
+ "xlog_grant_log_space: wake 1");
+ s = GRANT_LOCK(log);
+ }
+ if (tic->t_flags & XFS_LOG_PERM_RESERV)
+ need_bytes = tic->t_unit_res*tic->t_ocnt;
+ else
+ need_bytes = tic->t_unit_res;
+
+redo:
+ if (XLOG_FORCED_SHUTDOWN(log))
+ goto error_return;
+
+ free_bytes = xlog_space_left(log, log->l_grant_reserve_cycle,
+ log->l_grant_reserve_bytes);
+ if (free_bytes < need_bytes) {
+ if ((tic->t_flags & XLOG_TIC_IN_Q) == 0)
+ XLOG_INS_TICKETQ(log->l_reserve_headq, tic);
+ xlog_trace_loggrant(log, tic,
+ "xlog_grant_log_space: sleep 2");
+ XFS_STATS_INC(xs_sleep_logspace);
+ sv_wait(&tic->t_sema, PINOD|PLTWAIT, &log->l_grant_lock, s);
+
+ if (XLOG_FORCED_SHUTDOWN(log)) {
+ s = GRANT_LOCK(log);
+ goto error_return;
+ }
+
+ xlog_trace_loggrant(log, tic,
+ "xlog_grant_log_space: wake 2");
+ xlog_grant_push_ail(log->l_mp, need_bytes);
+ s = GRANT_LOCK(log);
+ goto redo;
+ } else if (tic->t_flags & XLOG_TIC_IN_Q)
+ XLOG_DEL_TICKETQ(log->l_reserve_headq, tic);
+
+ /* we've got enough space */
+ XLOG_GRANT_ADD_SPACE(log, need_bytes, 'w');
+ XLOG_GRANT_ADD_SPACE(log, need_bytes, 'r');
+#ifdef DEBUG
+ tail_lsn = log->l_tail_lsn;
+ /*
+ * Check to make sure the grant write head didn't just over lap the
+ * tail. If the cycles are the same, we can't be overlapping.
+ * Otherwise, make sure that the cycles differ by exactly one and
+ * check the byte count.
+ */
+ if (CYCLE_LSN(tail_lsn, ARCH_NOCONVERT) != log->l_grant_write_cycle) {
+ ASSERT(log->l_grant_write_cycle-1 == CYCLE_LSN(tail_lsn, ARCH_NOCONVERT));
+ ASSERT(log->l_grant_write_bytes <= BBTOB(BLOCK_LSN(tail_lsn, ARCH_NOCONVERT)));
+ }
+#endif
+ xlog_trace_loggrant(log, tic, "xlog_grant_log_space: exit");
+ xlog_verify_grant_head(log, 1);
+ GRANT_UNLOCK(log, s);
+ return 0;
+
+ error_return:
+ if (tic->t_flags & XLOG_TIC_IN_Q)
+ XLOG_DEL_TICKETQ(log->l_reserve_headq, tic);
+ xlog_trace_loggrant(log, tic, "xlog_grant_log_space: err_ret");
+ /*
+ * If we are failing, make sure the ticket doesn't have any
+ * current reservations. We don't want to add this back when
+ * the ticket/transaction gets cancelled.
+ */
+ tic->t_curr_res = 0;
+ tic->t_cnt = 0; /* ungrant will give back unit_res * t_cnt. */
+ GRANT_UNLOCK(log, s);
+ return XFS_ERROR(EIO);
+} /* xlog_grant_log_space */
+
+
+/*
+ * Replenish the byte reservation required by moving the grant write head.
+ *
+ *
+ */
+STATIC int
+xlog_regrant_write_log_space(xlog_t *log,
+ xlog_ticket_t *tic)
+{
+ SPLDECL(s);
+ int free_bytes, need_bytes;
+ xlog_ticket_t *ntic;
+#ifdef DEBUG
+ xfs_lsn_t tail_lsn;
+#endif
+
+ tic->t_curr_res = tic->t_unit_res;
+
+ if (tic->t_cnt > 0)
+ return (0);
+
+#ifdef DEBUG
+ if (log->l_flags & XLOG_ACTIVE_RECOVERY)
+ panic("regrant Recovery problem");
+#endif
+
+ s = GRANT_LOCK(log);
+ xlog_trace_loggrant(log, tic, "xlog_regrant_write_log_space: enter");
+
+ if (XLOG_FORCED_SHUTDOWN(log))
+ goto error_return;
+
+ /* If there are other waiters on the queue then give them a
+ * chance at logspace before us. Wake up the first waiters,
+ * if we do not wake up all the waiters then go to sleep waiting
+ * for more free space, otherwise try to get some space for
+ * this transaction.
+ */
+
+ if ((ntic = log->l_write_headq)) {
+ free_bytes = xlog_space_left(log, log->l_grant_write_cycle,
+ log->l_grant_write_bytes);
+ do {
+ ASSERT(ntic->t_flags & XLOG_TIC_PERM_RESERV);
+
+ if (free_bytes < ntic->t_unit_res)
+ break;
+ free_bytes -= ntic->t_unit_res;
+ sv_signal(&ntic->t_sema);
+ ntic = ntic->t_next;
+ } while (ntic != log->l_write_headq);
+
+ if (ntic != log->l_write_headq) {
+ if ((tic->t_flags & XLOG_TIC_IN_Q) == 0)
+ XLOG_INS_TICKETQ(log->l_write_headq, tic);
+
+ xlog_trace_loggrant(log, tic,
+ "xlog_regrant_write_log_space: sleep 1");
+ XFS_STATS_INC(xs_sleep_logspace);
+ sv_wait(&tic->t_sema, PINOD|PLTWAIT,
+ &log->l_grant_lock, s);
+
+ /* If we're shutting down, this tic is already
+ * off the queue */
+ if (XLOG_FORCED_SHUTDOWN(log)) {
+ s = GRANT_LOCK(log);
+ goto error_return;
+ }
+
+ xlog_trace_loggrant(log, tic,
+ "xlog_regrant_write_log_space: wake 1");
+ xlog_grant_push_ail(log->l_mp, tic->t_unit_res);
+ s = GRANT_LOCK(log);
+ }
+ }
+
+ need_bytes = tic->t_unit_res;
+
+redo:
+ if (XLOG_FORCED_SHUTDOWN(log))
+ goto error_return;
+
+ free_bytes = xlog_space_left(log, log->l_grant_write_cycle,
+ log->l_grant_write_bytes);
+ if (free_bytes < need_bytes) {
+ if ((tic->t_flags & XLOG_TIC_IN_Q) == 0)
+ XLOG_INS_TICKETQ(log->l_write_headq, tic);
+ XFS_STATS_INC(xs_sleep_logspace);
+ sv_wait(&tic->t_sema, PINOD|PLTWAIT, &log->l_grant_lock, s);
+
+ /* If we're shutting down, this tic is already off the queue */
+ if (XLOG_FORCED_SHUTDOWN(log)) {
+ s = GRANT_LOCK(log);
+ goto error_return;
+ }
+
+ xlog_trace_loggrant(log, tic,
+ "xlog_regrant_write_log_space: wake 2");
+ xlog_grant_push_ail(log->l_mp, need_bytes);
+ s = GRANT_LOCK(log);
+ goto redo;
+ } else if (tic->t_flags & XLOG_TIC_IN_Q)
+ XLOG_DEL_TICKETQ(log->l_write_headq, tic);
+
+ XLOG_GRANT_ADD_SPACE(log, need_bytes, 'w'); /* we've got enough space */
+#ifdef DEBUG
+ tail_lsn = log->l_tail_lsn;
+ if (CYCLE_LSN(tail_lsn, ARCH_NOCONVERT) != log->l_grant_write_cycle) {
+ ASSERT(log->l_grant_write_cycle-1 == CYCLE_LSN(tail_lsn, ARCH_NOCONVERT));
+ ASSERT(log->l_grant_write_bytes <= BBTOB(BLOCK_LSN(tail_lsn, ARCH_NOCONVERT)));
+ }
+#endif
+
+ xlog_trace_loggrant(log, tic, "xlog_regrant_write_log_space: exit");
+ xlog_verify_grant_head(log, 1);
+ GRANT_UNLOCK(log, s);
+ return (0);
+
+
+ error_return:
+ if (tic->t_flags & XLOG_TIC_IN_Q)
+ XLOG_DEL_TICKETQ(log->l_reserve_headq, tic);
+ xlog_trace_loggrant(log, tic, "xlog_regrant_write_log_space: err_ret");
+ /*
+ * If we are failing, make sure the ticket doesn't have any
+ * current reservations. We don't want to add this back when
+ * the ticket/transaction gets cancelled.
+ */
+ tic->t_curr_res = 0;
+ tic->t_cnt = 0; /* ungrant will give back unit_res * t_cnt. */
+ GRANT_UNLOCK(log, s);
+ return XFS_ERROR(EIO);
+} /* xlog_regrant_write_log_space */
+
+
+/* The first cnt-1 times through here we don't need to
+ * move the grant write head because the permanent
+ * reservation has reserved cnt times the unit amount.
+ * Release part of current permanent unit reservation and
+ * reset current reservation to be one units worth. Also
+ * move grant reservation head forward.
+ */
+STATIC void
+xlog_regrant_reserve_log_space(xlog_t *log,
+ xlog_ticket_t *ticket)
+{
+ SPLDECL(s);
+
+ xlog_trace_loggrant(log, ticket,
+ "xlog_regrant_reserve_log_space: enter");
+ if (ticket->t_cnt > 0)
+ ticket->t_cnt--;
+
+ s = GRANT_LOCK(log);
+ XLOG_GRANT_SUB_SPACE(log, ticket->t_curr_res, 'w');
+ XLOG_GRANT_SUB_SPACE(log, ticket->t_curr_res, 'r');
+ ticket->t_curr_res = ticket->t_unit_res;
+ xlog_trace_loggrant(log, ticket,
+ "xlog_regrant_reserve_log_space: sub current res");
+ xlog_verify_grant_head(log, 1);
+
+ /* just return if we still have some of the pre-reserved space */
+ if (ticket->t_cnt > 0) {
+ GRANT_UNLOCK(log, s);
+ return;
+ }
+
+ XLOG_GRANT_ADD_SPACE(log, ticket->t_unit_res, 'r');
+ xlog_trace_loggrant(log, ticket,
+ "xlog_regrant_reserve_log_space: exit");
+ xlog_verify_grant_head(log, 0);
+ GRANT_UNLOCK(log, s);
+ ticket->t_curr_res = ticket->t_unit_res;
+} /* xlog_regrant_reserve_log_space */
+
+
+/*
+ * Give back the space left from a reservation.
+ *
+ * All the information we need to make a correct determination of space left
+ * is present. For non-permanent reservations, things are quite easy. The
+ * count should have been decremented to zero. We only need to deal with the
+ * space remaining in the current reservation part of the ticket. If the
+ * ticket contains a permanent reservation, there may be left over space which
+ * needs to be released. A count of N means that N-1 refills of the current
+ * reservation can be done before we need to ask for more space. The first
+ * one goes to fill up the first current reservation. Once we run out of
+ * space, the count will stay at zero and the only space remaining will be
+ * in the current reservation field.
+ */
+STATIC void
+xlog_ungrant_log_space(xlog_t *log,
+ xlog_ticket_t *ticket)
+{
+ SPLDECL(s);
+
+ if (ticket->t_cnt > 0)
+ ticket->t_cnt--;
+
+ s = GRANT_LOCK(log);
+ xlog_trace_loggrant(log, ticket, "xlog_ungrant_log_space: enter");
+
+ XLOG_GRANT_SUB_SPACE(log, ticket->t_curr_res, 'w');
+ XLOG_GRANT_SUB_SPACE(log, ticket->t_curr_res, 'r');
+
+ xlog_trace_loggrant(log, ticket, "xlog_ungrant_log_space: sub current");
+
+ /* If this is a permanent reservation ticket, we may be able to free
+ * up more space based on the remaining count.
+ */
+ if (ticket->t_cnt > 0) {
+ ASSERT(ticket->t_flags & XLOG_TIC_PERM_RESERV);
+ XLOG_GRANT_SUB_SPACE(log, ticket->t_unit_res*ticket->t_cnt,'w');
+ XLOG_GRANT_SUB_SPACE(log, ticket->t_unit_res*ticket->t_cnt,'r');
+ }
+
+ xlog_trace_loggrant(log, ticket, "xlog_ungrant_log_space: exit");
+ xlog_verify_grant_head(log, 1);
+ GRANT_UNLOCK(log, s);
+ xfs_log_move_tail(log->l_mp, 1);
+} /* xlog_ungrant_log_space */
+
+
+/*
+ * Atomically put back used ticket.
+ */
+void
+xlog_state_put_ticket(xlog_t *log,
+ xlog_ticket_t *tic)
+{
+ unsigned long s;
+
+ s = LOG_LOCK(log);
+ xlog_ticket_put(log, tic);
+ LOG_UNLOCK(log, s);
+} /* xlog_state_put_ticket */
+
+/*
+ * Flush iclog to disk if this is the last reference to the given iclog and
+ * the WANT_SYNC bit is set.
+ *
+ * When this function is entered, the iclog is not necessarily in the
+ * WANT_SYNC state. It may be sitting around waiting to get filled.
+ *
+ *
+ */
+int
+xlog_state_release_iclog(xlog_t *log,
+ xlog_in_core_t *iclog)
+{
+ SPLDECL(s);
+ int sync = 0; /* do we sync? */
+
+ xlog_assign_tail_lsn(log->l_mp);
+
+ s = LOG_LOCK(log);
+
+ if (iclog->ic_state & XLOG_STATE_IOERROR) {
+ LOG_UNLOCK(log, s);
+ return XFS_ERROR(EIO);
+ }
+
+ ASSERT(iclog->ic_refcnt > 0);
+ ASSERT(iclog->ic_state == XLOG_STATE_ACTIVE ||
+ iclog->ic_state == XLOG_STATE_WANT_SYNC);
+
+ if (--iclog->ic_refcnt == 0 &&
+ iclog->ic_state == XLOG_STATE_WANT_SYNC) {
+ sync++;
+ iclog->ic_state = XLOG_STATE_SYNCING;
+ INT_SET(iclog->ic_header.h_tail_lsn, ARCH_CONVERT, log->l_tail_lsn);
+ xlog_verify_tail_lsn(log, iclog, log->l_tail_lsn);
+ /* cycle incremented when incrementing curr_block */
+ }
+
+ LOG_UNLOCK(log, s);
+
+ /*
+ * We let the log lock go, so it's possible that we hit a log I/O
+ * error or someother SHUTDOWN condition that marks the iclog
+ * as XLOG_STATE_IOERROR before the bwrite. However, we know that
+ * this iclog has consistent data, so we ignore IOERROR
+ * flags after this point.
+ */
+ if (sync) {
+ return xlog_sync(log, iclog);
+ }
+ return (0);
+
+} /* xlog_state_release_iclog */
+
+
+/*
+ * This routine will mark the current iclog in the ring as WANT_SYNC
+ * and move the current iclog pointer to the next iclog in the ring.
+ * When this routine is called from xlog_state_get_iclog_space(), the
+ * exact size of the iclog has not yet been determined. All we know is
+ * that every data block. We have run out of space in this log record.
+ */
+STATIC void
+xlog_state_switch_iclogs(xlog_t *log,
+ xlog_in_core_t *iclog,
+ int eventual_size)
+{
+ ASSERT(iclog->ic_state == XLOG_STATE_ACTIVE);
+ if (!eventual_size)
+ eventual_size = iclog->ic_offset;
+ iclog->ic_state = XLOG_STATE_WANT_SYNC;
+ INT_SET(iclog->ic_header.h_prev_block, ARCH_CONVERT, log->l_prev_block);
+ log->l_prev_block = log->l_curr_block;
+ log->l_prev_cycle = log->l_curr_cycle;
+
+ /* roll log?: ic_offset changed later */
+ log->l_curr_block += BTOBB(eventual_size)+BTOBB(log->l_iclog_hsize);
+
+ /* Round up to next log-sunit */
+ if (XFS_SB_VERSION_HASLOGV2(&log->l_mp->m_sb) &&
+ log->l_mp->m_sb.sb_logsunit > 1) {
+ __uint32_t sunit_bb = BTOBB(log->l_mp->m_sb.sb_logsunit);
+ log->l_curr_block = roundup(log->l_curr_block, sunit_bb);
+ }
+
+ if (log->l_curr_block >= log->l_logBBsize) {
+ log->l_curr_cycle++;
+ if (log->l_curr_cycle == XLOG_HEADER_MAGIC_NUM)
+ log->l_curr_cycle++;
+ log->l_curr_block -= log->l_logBBsize;
+ ASSERT(log->l_curr_block >= 0);
+ }
+ ASSERT(iclog == log->l_iclog);
+ log->l_iclog = iclog->ic_next;
+} /* xlog_state_switch_iclogs */
+
+
+/*
+ * Write out all data in the in-core log as of this exact moment in time.
+ *
+ * Data may be written to the in-core log during this call. However,
+ * we don't guarantee this data will be written out. A change from past
+ * implementation means this routine will *not* write out zero length LRs.
+ *
+ * Basically, we try and perform an intelligent scan of the in-core logs.
+ * If we determine there is no flushable data, we just return. There is no
+ * flushable data if:
+ *
+ * 1. the current iclog is active and has no data; the previous iclog
+ * is in the active or dirty state.
+ * 2. the current iclog is drity, and the previous iclog is in the
+ * active or dirty state.
+ *
+ * We may sleep (call psema) if:
+ *
+ * 1. the current iclog is not in the active nor dirty state.
+ * 2. the current iclog dirty, and the previous iclog is not in the
+ * active nor dirty state.
+ * 3. the current iclog is active, and there is another thread writing
+ * to this particular iclog.
+ * 4. a) the current iclog is active and has no other writers
+ * b) when we return from flushing out this iclog, it is still
+ * not in the active nor dirty state.
+ */
+STATIC int
+xlog_state_sync_all(xlog_t *log, uint flags)
+{
+ xlog_in_core_t *iclog;
+ xfs_lsn_t lsn;
+ SPLDECL(s);
+
+ s = LOG_LOCK(log);
+
+ iclog = log->l_iclog;
+ if (iclog->ic_state & XLOG_STATE_IOERROR) {
+ LOG_UNLOCK(log, s);
+ return XFS_ERROR(EIO);
+ }
+
+ /* If the head iclog is not active nor dirty, we just attach
+ * ourselves to the head and go to sleep.
+ */
+ if (iclog->ic_state == XLOG_STATE_ACTIVE ||
+ iclog->ic_state == XLOG_STATE_DIRTY) {
+ /*
+ * If the head is dirty or (active and empty), then
+ * we need to look at the previous iclog. If the previous
+ * iclog is active or dirty we are done. There is nothing
+ * to sync out. Otherwise, we attach ourselves to the
+ * previous iclog and go to sleep.
+ */
+ if (iclog->ic_state == XLOG_STATE_DIRTY ||
+ (iclog->ic_refcnt == 0 && iclog->ic_offset == 0)) {
+ iclog = iclog->ic_prev;
+ if (iclog->ic_state == XLOG_STATE_ACTIVE ||
+ iclog->ic_state == XLOG_STATE_DIRTY)
+ goto no_sleep;
+ else
+ goto maybe_sleep;
+ } else {
+ if (iclog->ic_refcnt == 0) {
+ /* We are the only one with access to this
+ * iclog. Flush it out now. There should
+ * be a roundoff of zero to show that someone
+ * has already taken care of the roundoff from
+ * the previous sync.
+ */
+ iclog->ic_refcnt++;
+ lsn = INT_GET(iclog->ic_header.h_lsn, ARCH_CONVERT);
+ xlog_state_switch_iclogs(log, iclog, 0);
+ LOG_UNLOCK(log, s);
+
+ if (xlog_state_release_iclog(log, iclog))
+ return XFS_ERROR(EIO);
+ s = LOG_LOCK(log);
+ if (INT_GET(iclog->ic_header.h_lsn, ARCH_CONVERT) == lsn &&
+ iclog->ic_state != XLOG_STATE_DIRTY)
+ goto maybe_sleep;
+ else
+ goto no_sleep;
+ } else {
+ /* Someone else is writing to this iclog.
+ * Use its call to flush out the data. However,
+ * the other thread may not force out this LR,
+ * so we mark it WANT_SYNC.
+ */
+ xlog_state_switch_iclogs(log, iclog, 0);
+ goto maybe_sleep;
+ }
+ }
+ }
+
+ /* By the time we come around again, the iclog could've been filled
+ * which would give it another lsn. If we have a new lsn, just
+ * return because the relevant data has been flushed.
+ */
+maybe_sleep:
+ if (flags & XFS_LOG_SYNC) {
+ /*
+ * We must check if we're shutting down here, before
+ * we wait, while we're holding the LOG_LOCK.
+ * Then we check again after waking up, in case our
+ * sleep was disturbed by a bad news.
+ */
+ if (iclog->ic_state & XLOG_STATE_IOERROR) {
+ LOG_UNLOCK(log, s);
+ return XFS_ERROR(EIO);
+ }
+ XFS_STATS_INC(xs_log_force_sleep);
+ sv_wait(&iclog->ic_forcesema, PINOD, &log->l_icloglock, s);
+ /*
+ * No need to grab the log lock here since we're
+ * only deciding whether or not to return EIO
+ * and the memory read should be atomic.
+ */
+ if (iclog->ic_state & XLOG_STATE_IOERROR)
+ return XFS_ERROR(EIO);
+
+ } else {
+
+no_sleep:
+ LOG_UNLOCK(log, s);
+ }
+ return 0;
+} /* xlog_state_sync_all */
+
+
+/*
+ * Used by code which implements synchronous log forces.
+ *
+ * Find in-core log with lsn.
+ * If it is in the DIRTY state, just return.
+ * If it is in the ACTIVE state, move the in-core log into the WANT_SYNC
+ * state and go to sleep or return.
+ * If it is in any other state, go to sleep or return.
+ *
+ * If filesystem activity goes to zero, the iclog will get flushed only by
+ * bdflush().
+ */
+int
+xlog_state_sync(xlog_t *log,
+ xfs_lsn_t lsn,
+ uint flags)
+{
+ xlog_in_core_t *iclog;
+ int already_slept = 0;
+ SPLDECL(s);
+
+
+try_again:
+ s = LOG_LOCK(log);
+ iclog = log->l_iclog;
+
+ if (iclog->ic_state & XLOG_STATE_IOERROR) {
+ LOG_UNLOCK(log, s);
+ return XFS_ERROR(EIO);
+ }
+
+ do {
+ if (INT_GET(iclog->ic_header.h_lsn, ARCH_CONVERT) != lsn) {
+ iclog = iclog->ic_next;
+ continue;
+ }
+
+ if (iclog->ic_state == XLOG_STATE_DIRTY) {
+ LOG_UNLOCK(log, s);
+ return 0;
+ }
+
+ if (iclog->ic_state == XLOG_STATE_ACTIVE) {
+ /*
+ * We sleep here if we haven't already slept (e.g.
+ * this is the first time we've looked at the correct
+ * iclog buf) and the buffer before us is going to
+ * be sync'ed. The reason for this is that if we
+ * are doing sync transactions here, by waiting for
+ * the previous I/O to complete, we can allow a few
+ * more transactions into this iclog before we close
+ * it down.
+ *
+ * Otherwise, we mark the buffer WANT_SYNC, and bump
+ * up the refcnt so we can release the log (which drops
+ * the ref count). The state switch keeps new transaction
+ * commits from using this buffer. When the current commits
+ * finish writing into the buffer, the refcount will drop to
+ * zero and the buffer will go out then.
+ */
+ if (!already_slept &&
+ (iclog->ic_prev->ic_state & (XLOG_STATE_WANT_SYNC |
+ XLOG_STATE_SYNCING))) {
+ ASSERT(!(iclog->ic_state & XLOG_STATE_IOERROR));
+ XFS_STATS_INC(xs_log_force_sleep);
+ sv_wait(&iclog->ic_prev->ic_writesema, PSWP,
+ &log->l_icloglock, s);
+ already_slept = 1;
+ goto try_again;
+ } else {
+ iclog->ic_refcnt++;
+ xlog_state_switch_iclogs(log, iclog, 0);
+ LOG_UNLOCK(log, s);
+ if (xlog_state_release_iclog(log, iclog))
+ return XFS_ERROR(EIO);
+ s = LOG_LOCK(log);
+ }
+ }
+
+ if ((flags & XFS_LOG_SYNC) && /* sleep */
+ !(iclog->ic_state & (XLOG_STATE_ACTIVE | XLOG_STATE_DIRTY))) {
+
+ /*
+ * Don't wait on the forcesema if we know that we've
+ * gotten a log write error.
+ */
+ if (iclog->ic_state & XLOG_STATE_IOERROR) {
+ LOG_UNLOCK(log, s);
+ return XFS_ERROR(EIO);
+ }
+ XFS_STATS_INC(xs_log_force_sleep);
+ sv_wait(&iclog->ic_forcesema, PSWP, &log->l_icloglock, s);
+ /*
+ * No need to grab the log lock here since we're
+ * only deciding whether or not to return EIO
+ * and the memory read should be atomic.
+ */
+ if (iclog->ic_state & XLOG_STATE_IOERROR)
+ return XFS_ERROR(EIO);
+ } else { /* just return */
+ LOG_UNLOCK(log, s);
+ }
+ return 0;
+
+ } while (iclog != log->l_iclog);
+
+ LOG_UNLOCK(log, s);
+ return (0);
+} /* xlog_state_sync */
+
+
+/*
+ * Called when we want to mark the current iclog as being ready to sync to
+ * disk.
+ */
+void
+xlog_state_want_sync(xlog_t *log, xlog_in_core_t *iclog)
+{
+ SPLDECL(s);
+
+ s = LOG_LOCK(log);
+
+ if (iclog->ic_state == XLOG_STATE_ACTIVE) {
+ xlog_state_switch_iclogs(log, iclog, 0);
+ } else {
+ ASSERT(iclog->ic_state &
+ (XLOG_STATE_WANT_SYNC|XLOG_STATE_IOERROR));
+ }
+
+ LOG_UNLOCK(log, s);
+} /* xlog_state_want_sync */
+
+
+
+/*****************************************************************************
+ *
+ * TICKET functions
+ *
+ *****************************************************************************
+ */
+
+/*
+ * Algorithm doesn't take into account page size. ;-(
+ */
+STATIC void
+xlog_state_ticket_alloc(xlog_t *log)
+{
+ xlog_ticket_t *t_list;
+ xlog_ticket_t *next;
+ xfs_caddr_t buf;
+ uint i = (NBPP / sizeof(xlog_ticket_t)) - 2;
+ SPLDECL(s);
+
+ /*
+ * The kmem_zalloc may sleep, so we shouldn't be holding the
+ * global lock. XXXmiken: may want to use zone allocator.
+ */
+ buf = (xfs_caddr_t) kmem_zalloc(NBPP, KM_SLEEP);
+
+ s = LOG_LOCK(log);
+
+ /* Attach 1st ticket to Q, so we can keep track of allocated memory */
+ t_list = (xlog_ticket_t *)buf;
+ t_list->t_next = log->l_unmount_free;
+ log->l_unmount_free = t_list++;
+ log->l_ticket_cnt++;
+ log->l_ticket_tcnt++;
+
+ /* Next ticket becomes first ticket attached to ticket free list */
+ if (log->l_freelist != NULL) {
+ ASSERT(log->l_tail != NULL);
+ log->l_tail->t_next = t_list;
+ } else {
+ log->l_freelist = t_list;
+ }
+ log->l_ticket_cnt++;
+ log->l_ticket_tcnt++;
+
+ /* Cycle through rest of alloc'ed memory, building up free Q */
+ for ( ; i > 0; i--) {
+ next = t_list + 1;
+ t_list->t_next = next;
+ t_list = next;
+ log->l_ticket_cnt++;
+ log->l_ticket_tcnt++;
+ }
+ t_list->t_next = NULL;
+ log->l_tail = t_list;
+ LOG_UNLOCK(log, s);
+} /* xlog_state_ticket_alloc */
+
+
+/*
+ * Put ticket into free list
+ *
+ * Assumption: log lock is held around this call.
+ */
+STATIC void
+xlog_ticket_put(xlog_t *log,
+ xlog_ticket_t *ticket)
+{
+ sv_destroy(&ticket->t_sema);
+
+ /*
+ * Don't think caching will make that much difference. It's
+ * more important to make debug easier.
+ */
+#if 0
+ /* real code will want to use LIFO for caching */
+ ticket->t_next = log->l_freelist;
+ log->l_freelist = ticket;
+ /* no need to clear fields */
+#else
+ /* When we debug, it is easier if tickets are cycled */
+ ticket->t_next = NULL;
+ if (log->l_tail != 0) {
+ log->l_tail->t_next = ticket;
+ } else {
+ ASSERT(log->l_freelist == 0);
+ log->l_freelist = ticket;
+ }
+ log->l_tail = ticket;
+#endif /* DEBUG */
+ log->l_ticket_cnt++;
+} /* xlog_ticket_put */
+
+
+/*
+ * Grab ticket off freelist or allocation some more
+ */
+xlog_ticket_t *
+xlog_ticket_get(xlog_t *log,
+ int unit_bytes,
+ int cnt,
+ char client,
+ uint xflags)
+{
+ xlog_ticket_t *tic;
+ uint num_headers;
+ SPLDECL(s);
+
+ alloc:
+ if (log->l_freelist == NULL)
+ xlog_state_ticket_alloc(log); /* potentially sleep */
+
+ s = LOG_LOCK(log);
+ if (log->l_freelist == NULL) {
+ LOG_UNLOCK(log, s);
+ goto alloc;
+ }
+ tic = log->l_freelist;
+ log->l_freelist = tic->t_next;
+ if (log->l_freelist == NULL)
+ log->l_tail = NULL;
+ log->l_ticket_cnt--;
+ LOG_UNLOCK(log, s);
+
+ /*
+ * Permanent reservations have up to 'cnt'-1 active log operations
+ * in the log. A unit in this case is the amount of space for one
+ * of these log operations. Normal reservations have a cnt of 1
+ * and their unit amount is the total amount of space required.
+ *
+ * The following lines of code account for non-transaction data
+ * which occupy space in the on-disk log.
+ */
+
+ /* for start-rec */
+ unit_bytes += sizeof(xlog_op_header_t);
+
+ /* for padding */
+ if (XFS_SB_VERSION_HASLOGV2(&log->l_mp->m_sb) &&
+ log->l_mp->m_sb.sb_logsunit > 1) {
+ /* log su roundoff */
+ unit_bytes += log->l_mp->m_sb.sb_logsunit;
+ } else {
+ /* BB roundoff */
+ unit_bytes += BBSIZE;
+ }
+
+ /* for commit-rec */
+ unit_bytes += sizeof(xlog_op_header_t);
+
+ /* for LR headers */
+ num_headers = ((unit_bytes + log->l_iclog_size-1) >> log->l_iclog_size_log);
+ unit_bytes += log->l_iclog_hsize * num_headers;
+
+ tic->t_unit_res = unit_bytes;
+ tic->t_curr_res = unit_bytes;
+ tic->t_cnt = cnt;
+ tic->t_ocnt = cnt;
+ tic->t_tid = (xlog_tid_t)((__psint_t)tic & 0xffffffff);
+ tic->t_clientid = client;
+ tic->t_flags = XLOG_TIC_INITED;
+ if (xflags & XFS_LOG_PERM_RESERV)
+ tic->t_flags |= XLOG_TIC_PERM_RESERV;
+ sv_init(&(tic->t_sema), SV_DEFAULT, "logtick");
+
+ return tic;
+} /* xlog_ticket_get */
+
+
+/******************************************************************************
+ *
+ * Log debug routines
+ *
+ ******************************************************************************
+ */
+#if defined(DEBUG) && !defined(XLOG_NOLOG)
+/*
+ * Make sure that the destination ptr is within the valid data region of
+ * one of the iclogs. This uses backup pointers stored in a different
+ * part of the log in case we trash the log structure.
+ */
+void
+xlog_verify_dest_ptr(xlog_t *log,
+ __psint_t ptr)
+{
+ int i;
+ int good_ptr = 0;
+
+ for (i=0; i < log->l_iclog_bufs; i++) {
+ if (ptr >= (__psint_t)log->l_iclog_bak[i] &&
+ ptr <= (__psint_t)log->l_iclog_bak[i]+log->l_iclog_size)
+ good_ptr++;
+ }
+ if (! good_ptr)
+ xlog_panic("xlog_verify_dest_ptr: invalid ptr");
+} /* xlog_verify_dest_ptr */
+
+STATIC void
+xlog_verify_grant_head(xlog_t *log, int equals)
+{
+ if (log->l_grant_reserve_cycle == log->l_grant_write_cycle) {
+ if (equals)
+ ASSERT(log->l_grant_reserve_bytes >= log->l_grant_write_bytes);
+ else
+ ASSERT(log->l_grant_reserve_bytes > log->l_grant_write_bytes);
+ } else {
+ ASSERT(log->l_grant_reserve_cycle-1 == log->l_grant_write_cycle);
+ ASSERT(log->l_grant_write_bytes >= log->l_grant_reserve_bytes);
+ }
+} /* xlog_verify_grant_head */
+
+/* check if it will fit */
+STATIC void
+xlog_verify_tail_lsn(xlog_t *log,
+ xlog_in_core_t *iclog,
+ xfs_lsn_t tail_lsn)
+{
+ int blocks;
+
+ if (CYCLE_LSN(tail_lsn, ARCH_NOCONVERT) == log->l_prev_cycle) {
+ blocks =
+ log->l_logBBsize - (log->l_prev_block - BLOCK_LSN(tail_lsn, ARCH_NOCONVERT));
+ if (blocks < BTOBB(iclog->ic_offset)+BTOBB(log->l_iclog_hsize))
+ xlog_panic("xlog_verify_tail_lsn: ran out of log space");
+ } else {
+ ASSERT(CYCLE_LSN(tail_lsn, ARCH_NOCONVERT)+1 == log->l_prev_cycle);
+
+ if (BLOCK_LSN(tail_lsn, ARCH_NOCONVERT) == log->l_prev_block)
+ xlog_panic("xlog_verify_tail_lsn: tail wrapped");
+
+ blocks = BLOCK_LSN(tail_lsn, ARCH_NOCONVERT) - log->l_prev_block;
+ if (blocks < BTOBB(iclog->ic_offset) + 1)
+ xlog_panic("xlog_verify_tail_lsn: ran out of log space");
+ }
+} /* xlog_verify_tail_lsn */
+
+/*
+ * Perform a number of checks on the iclog before writing to disk.
+ *
+ * 1. Make sure the iclogs are still circular
+ * 2. Make sure we have a good magic number
+ * 3. Make sure we don't have magic numbers in the data
+ * 4. Check fields of each log operation header for:
+ * A. Valid client identifier
+ * B. tid ptr value falls in valid ptr space (user space code)
+ * C. Length in log record header is correct according to the
+ * individual operation headers within record.
+ * 5. When a bwrite will occur within 5 blocks of the front of the physical
+ * log, check the preceding blocks of the physical log to make sure all
+ * the cycle numbers agree with the current cycle number.
+ */
+STATIC void
+xlog_verify_iclog(xlog_t *log,
+ xlog_in_core_t *iclog,
+ int count,
+ boolean_t syncing)
+{
+ xlog_op_header_t *ophead;
+ xlog_in_core_t *icptr;
+ xlog_in_core_2_t *xhdr;
+ xfs_caddr_t ptr;
+ xfs_caddr_t base_ptr;
+ __psint_t field_offset;
+ __uint8_t clientid;
+ int len, i, j, k, op_len;
+ int idx;
+ SPLDECL(s);
+
+ /* check validity of iclog pointers */
+ s = LOG_LOCK(log);
+ icptr = log->l_iclog;
+ for (i=0; i < log->l_iclog_bufs; i++) {
+ if (icptr == 0)
+ xlog_panic("xlog_verify_iclog: invalid ptr");
+ icptr = icptr->ic_next;
+ }
+ if (icptr != log->l_iclog)
+ xlog_panic("xlog_verify_iclog: corrupt iclog ring");
+ LOG_UNLOCK(log, s);
+
+ /* check log magic numbers */
+ ptr = (xfs_caddr_t) &(iclog->ic_header);
+ if (INT_GET(*(uint *)ptr, ARCH_CONVERT) != XLOG_HEADER_MAGIC_NUM)
+ xlog_panic("xlog_verify_iclog: invalid magic num");
+
+ for (ptr += BBSIZE; ptr < ((xfs_caddr_t)&(iclog->ic_header))+count;
+ ptr += BBSIZE) {
+ if (INT_GET(*(uint *)ptr, ARCH_CONVERT) == XLOG_HEADER_MAGIC_NUM)
+ xlog_panic("xlog_verify_iclog: unexpected magic num");
+ }
+
+ /* check fields */
+ len = INT_GET(iclog->ic_header.h_num_logops, ARCH_CONVERT);
+ ptr = iclog->ic_datap;
+ base_ptr = ptr;
+ ophead = (xlog_op_header_t *)ptr;
+ xhdr = (xlog_in_core_2_t *)&iclog->ic_header;
+ for (i = 0; i < len; i++) {
+ ophead = (xlog_op_header_t *)ptr;
+
+ /* clientid is only 1 byte */
+ field_offset = (__psint_t)
+ ((xfs_caddr_t)&(ophead->oh_clientid) - base_ptr);
+ if (syncing == B_FALSE || (field_offset & 0x1ff)) {
+ clientid = ophead->oh_clientid;
+ } else {
+ idx = BTOBBT((xfs_caddr_t)&(ophead->oh_clientid) - iclog->ic_datap);
+ if (idx >= (XLOG_HEADER_CYCLE_SIZE / BBSIZE)) {
+ j = idx / (XLOG_HEADER_CYCLE_SIZE / BBSIZE);
+ k = idx % (XLOG_HEADER_CYCLE_SIZE / BBSIZE);
+ clientid = GET_CLIENT_ID(xhdr[j].hic_xheader.xh_cycle_data[k], ARCH_CONVERT);
+ } else {
+ clientid = GET_CLIENT_ID(iclog->ic_header.h_cycle_data[idx], ARCH_CONVERT);
+ }
+ }
+ if (clientid != XFS_TRANSACTION && clientid != XFS_LOG)
+ cmn_err(CE_WARN, "xlog_verify_iclog: invalid clientid %d op 0x%p offset 0x%x", clientid, ophead, field_offset);
+
+ /* check length */
+ field_offset = (__psint_t)
+ ((xfs_caddr_t)&(ophead->oh_len) - base_ptr);
+ if (syncing == B_FALSE || (field_offset & 0x1ff)) {
+ op_len = INT_GET(ophead->oh_len, ARCH_CONVERT);
+ } else {
+ idx = BTOBBT((__psint_t)&ophead->oh_len -
+ (__psint_t)iclog->ic_datap);
+ if (idx >= (XLOG_HEADER_CYCLE_SIZE / BBSIZE)) {
+ j = idx / (XLOG_HEADER_CYCLE_SIZE / BBSIZE);
+ k = idx % (XLOG_HEADER_CYCLE_SIZE / BBSIZE);
+ op_len = INT_GET(xhdr[j].hic_xheader.xh_cycle_data[k], ARCH_CONVERT);
+ } else {
+ op_len = INT_GET(iclog->ic_header.h_cycle_data[idx], ARCH_CONVERT);
+ }
+ }
+ ptr += sizeof(xlog_op_header_t) + op_len;
+ }
+} /* xlog_verify_iclog */
+#endif /* DEBUG && !XLOG_NOLOG */
+
+/*
+ * Mark all iclogs IOERROR. LOG_LOCK is held by the caller.
+ */
+STATIC int
+xlog_state_ioerror(
+ xlog_t *log)
+{
+ xlog_in_core_t *iclog, *ic;
+
+ iclog = log->l_iclog;
+ if (! (iclog->ic_state & XLOG_STATE_IOERROR)) {
+ /*
+ * Mark all the incore logs IOERROR.
+ * From now on, no log flushes will result.
+ */
+ ic = iclog;
+ do {
+ ic->ic_state = XLOG_STATE_IOERROR;
+ ic = ic->ic_next;
+ } while (ic != iclog);
+ return (0);
+ }
+ /*
+ * Return non-zero, if state transition has already happened.
+ */
+ return (1);
+}
+
+/*
+ * This is called from xfs_force_shutdown, when we're forcibly
+ * shutting down the filesystem, typically because of an IO error.
+ * Our main objectives here are to make sure that:
+ * a. the filesystem gets marked 'SHUTDOWN' for all interested
+ * parties to find out, 'atomically'.
+ * b. those who're sleeping on log reservations, pinned objects and
+ * other resources get woken up, and be told the bad news.
+ * c. nothing new gets queued up after (a) and (b) are done.
+ * d. if !logerror, flush the iclogs to disk, then seal them off
+ * for business.
+ */
+int
+xfs_log_force_umount(
+ struct xfs_mount *mp,
+ int logerror)
+{
+ xlog_ticket_t *tic;
+ xlog_t *log;
+ int retval;
+ SPLDECL(s);
+ SPLDECL(s2);
+
+ log = mp->m_log;
+
+ /*
+ * If this happens during log recovery, don't worry about
+ * locking; the log isn't open for business yet.
+ */
+ if (!log ||
+ log->l_flags & XLOG_ACTIVE_RECOVERY) {
+ mp->m_flags |= XFS_MOUNT_FS_SHUTDOWN;
+ XFS_BUF_DONE(mp->m_sb_bp);
+ return (0);
+ }
+
+ /*
+ * Somebody could've already done the hard work for us.
+ * No need to get locks for this.
+ */
+ if (logerror && log->l_iclog->ic_state & XLOG_STATE_IOERROR) {
+ ASSERT(XLOG_FORCED_SHUTDOWN(log));
+ return (1);
+ }
+ retval = 0;
+ /*
+ * We must hold both the GRANT lock and the LOG lock,
+ * before we mark the filesystem SHUTDOWN and wake
+ * everybody up to tell the bad news.
+ */
+ s = GRANT_LOCK(log);
+ s2 = LOG_LOCK(log);
+ mp->m_flags |= XFS_MOUNT_FS_SHUTDOWN;
+ XFS_BUF_DONE(mp->m_sb_bp);
+ /*
+ * This flag is sort of redundant because of the mount flag, but
+ * it's good to maintain the separation between the log and the rest
+ * of XFS.
+ */
+ log->l_flags |= XLOG_IO_ERROR;
+
+ /*
+ * If we hit a log error, we want to mark all the iclogs IOERROR
+ * while we're still holding the loglock.
+ */
+ if (logerror)
+ retval = xlog_state_ioerror(log);
+ LOG_UNLOCK(log, s2);
+
+ /*
+ * We don't want anybody waiting for log reservations
+ * after this. That means we have to wake up everybody
+ * queued up on reserve_headq as well as write_headq.
+ * In addition, we make sure in xlog_{re}grant_log_space
+ * that we don't enqueue anything once the SHUTDOWN flag
+ * is set, and this action is protected by the GRANTLOCK.
+ */
+ if ((tic = log->l_reserve_headq)) {
+ do {
+ sv_signal(&tic->t_sema);
+ tic = tic->t_next;
+ } while (tic != log->l_reserve_headq);
+ }
+
+ if ((tic = log->l_write_headq)) {
+ do {
+ sv_signal(&tic->t_sema);
+ tic = tic->t_next;
+ } while (tic != log->l_write_headq);
+ }
+ GRANT_UNLOCK(log, s);
+
+ if (! (log->l_iclog->ic_state & XLOG_STATE_IOERROR)) {
+ ASSERT(!logerror);
+ /*
+ * Force the incore logs to disk before shutting the
+ * log down completely.
+ */
+ xlog_state_sync_all(log, XFS_LOG_FORCE|XFS_LOG_SYNC);
+ s2 = LOG_LOCK(log);
+ retval = xlog_state_ioerror(log);
+ LOG_UNLOCK(log, s2);
+ }
+ /*
+ * Wake up everybody waiting on xfs_log_force.
+ * Callback all log item committed functions as if the
+ * log writes were completed.
+ */
+ xlog_state_do_callback(log, XFS_LI_ABORTED, NULL);
+
+#ifdef XFSERRORDEBUG
+ {
+ xlog_in_core_t *iclog;
+
+ s = LOG_LOCK(log);
+ iclog = log->l_iclog;
+ do {
+ ASSERT(iclog->ic_callback == 0);
+ iclog = iclog->ic_next;
+ } while (iclog != log->l_iclog);
+ LOG_UNLOCK(log, s);
+ }
+#endif
+ /* return non-zero if log IOERROR transition had already happened */
+ return (retval);
+}
+
+int
+xlog_iclogs_empty(xlog_t *log)
+{
+ xlog_in_core_t *iclog;
+
+ iclog = log->l_iclog;
+ do {
+ /* endianness does not matter here, zero is zero in
+ * any language.
+ */
+ if (iclog->ic_header.h_num_logops)
+ return(0);
+ iclog = iclog->ic_next;
+ } while (iclog != log->l_iclog);
+ return(1);
+}
diff --git a/fs/xfs/xfs_log.h b/fs/xfs/xfs_log.h
new file mode 100644
index 00000000000000..3b1f395b67bbeb
--- /dev/null
+++ b/fs/xfs/xfs_log.h
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_LOG_H__
+#define __XFS_LOG_H__
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define LSN_FIELD_CYCLE(arch) (((arch)==ARCH_NOCONVERT)?1:0)
+#define LSN_FIELD_BLOCK(arch) (((arch)==ARCH_NOCONVERT)?0:1)
+#else
+#define LSN_FIELD_CYCLE(arch) (0)
+#define LSN_FIELD_BLOCK(arch) (1)
+#endif
+
+/* get lsn fields */
+
+#define CYCLE_LSN(lsn,arch) (INT_GET(((uint *)&(lsn))[LSN_FIELD_CYCLE(arch)], arch))
+#define BLOCK_LSN(lsn,arch) (INT_GET(((uint *)&(lsn))[LSN_FIELD_BLOCK(arch)], arch))
+/* this is used in a spot where we might otherwise double-endian-flip */
+#define CYCLE_LSN_NOCONV(lsn,arch) (((uint *)&(lsn))[LSN_FIELD_CYCLE(arch)])
+
+#ifdef __KERNEL__
+/*
+ * By comparing each compnent, we don't have to worry about extra
+ * endian issues in treating two 32 bit numbers as one 64 bit number
+ */
+static
+#if defined(__GNUC__) && (__GNUC__ == 2) && ( (__GNUC_MINOR__ == 95) || (__GNUC_MINOR__ == 96))
+__attribute__((unused)) /* gcc 2.95, 2.96 miscompile this when inlined */
+#else
+__inline__
+#endif
+xfs_lsn_t _lsn_cmp(xfs_lsn_t lsn1, xfs_lsn_t lsn2, xfs_arch_t arch)
+{
+ if (CYCLE_LSN(lsn1, arch) != CYCLE_LSN(lsn2, arch))
+ return (CYCLE_LSN(lsn1, arch)<CYCLE_LSN(lsn2, arch))? -999 : 999;
+
+ if (BLOCK_LSN(lsn1, arch) != BLOCK_LSN(lsn2, arch))
+ return (BLOCK_LSN(lsn1, arch)<BLOCK_LSN(lsn2, arch))? -999 : 999;
+
+ return 0;
+}
+
+#define XFS_LSN_CMP_ARCH(x,y,arch) _lsn_cmp(x, y, arch)
+#define XFS_LSN_CMP(x,y) XFS_LSN_CMP_ARCH(x,y,ARCH_NOCONVERT)
+#define XFS_LSN_DIFF_ARCH(x,y,arch) _lsn_cmp(x, y, arch)
+#define XFS_LSN_DIFF(x,y) XFS_LSN_DIFF_ARCH(x,y,ARCH_NOCONVERT)
+
+/*
+ * Macros, structures, prototypes for interface to the log manager.
+ */
+
+/*
+ * Flags to xfs_log_mount
+ */
+#define XFS_LOG_RECOVER 0x1
+
+/*
+ * Flags to xfs_log_done()
+ */
+#define XFS_LOG_REL_PERM_RESERV 0x1
+
+
+/*
+ * Flags to xfs_log_reserve()
+ *
+ * XFS_LOG_SLEEP: If space is not available, sleep (default)
+ * XFS_LOG_NOSLEEP: If space is not available, return error
+ * XFS_LOG_PERM_RESERV: Permanent reservation. When writes are
+ * performed against this type of reservation, the reservation
+ * is not decreased. Long running transactions should use this.
+ */
+#define XFS_LOG_SLEEP 0x0
+#define XFS_LOG_NOSLEEP 0x1
+#define XFS_LOG_PERM_RESERV 0x2
+#define XFS_LOG_RESV_ALL (XFS_LOG_NOSLEEP|XFS_LOG_PERM_RESERV)
+
+
+/*
+ * Flags to xfs_log_force()
+ *
+ * XFS_LOG_SYNC: Synchronous force in-core log to disk
+ * XFS_LOG_FORCE: Start in-core log write now.
+ * XFS_LOG_URGE: Start write within some window of time.
+ *
+ * Note: Either XFS_LOG_FORCE or XFS_LOG_URGE must be set.
+ */
+#define XFS_LOG_SYNC 0x1
+#define XFS_LOG_FORCE 0x2
+#define XFS_LOG_URGE 0x4
+
+#endif /* __KERNEL__ */
+
+
+/* Log Clients */
+#define XFS_TRANSACTION 0x69
+#define XFS_VOLUME 0x2
+#define XFS_LOG 0xaa
+
+typedef struct xfs_log_iovec {
+ xfs_caddr_t i_addr; /* beginning address of region */
+ int i_len; /* length in bytes of region */
+} xfs_log_iovec_t;
+
+typedef void* xfs_log_ticket_t;
+
+/*
+ * Structure used to pass callback function and the function's argument
+ * to the log manager.
+ */
+typedef struct xfs_log_callback {
+ struct xfs_log_callback *cb_next;
+ void (*cb_func)(void *, int);
+ void *cb_arg;
+} xfs_log_callback_t;
+
+
+#ifdef __KERNEL__
+/* Log manager interfaces */
+struct xfs_mount;
+xfs_lsn_t xfs_log_done(struct xfs_mount *mp,
+ xfs_log_ticket_t ticket,
+ void **iclog,
+ uint flags);
+int xfs_log_force(struct xfs_mount *mp,
+ xfs_lsn_t lsn,
+ uint flags);
+int xfs_log_mount(struct xfs_mount *mp,
+ struct xfs_buftarg *log_target,
+ xfs_daddr_t start_block,
+ int num_bblocks);
+int xfs_log_mount_finish(struct xfs_mount *mp, int);
+void xfs_log_move_tail(struct xfs_mount *mp,
+ xfs_lsn_t tail_lsn);
+int xfs_log_notify(struct xfs_mount *mp,
+ void *iclog,
+ xfs_log_callback_t *callback_entry);
+int xfs_log_release_iclog(struct xfs_mount *mp,
+ void *iclog_hndl);
+int xfs_log_reserve(struct xfs_mount *mp,
+ int length,
+ int count,
+ xfs_log_ticket_t *ticket,
+ __uint8_t clientid,
+ uint flags);
+int xfs_log_write(struct xfs_mount *mp,
+ xfs_log_iovec_t region[],
+ int nentries,
+ xfs_log_ticket_t ticket,
+ xfs_lsn_t *start_lsn);
+int xfs_log_unmount(struct xfs_mount *mp);
+int xfs_log_unmount_write(struct xfs_mount *mp);
+void xfs_log_unmount_dealloc(struct xfs_mount *mp);
+int xfs_log_force_umount(struct xfs_mount *mp, int logerror);
+int xfs_log_need_covered(struct xfs_mount *mp);
+
+void xlog_iodone(struct xfs_buf *);
+
+#endif
+
+
+extern int xlog_debug; /* set to 1 to enable real log */
+
+
+#endif /* __XFS_LOG_H__ */
diff --git a/fs/xfs/xfs_log_priv.h b/fs/xfs/xfs_log_priv.h
new file mode 100644
index 00000000000000..0505a3069e0253
--- /dev/null
+++ b/fs/xfs/xfs_log_priv.h
@@ -0,0 +1,561 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_LOG_PRIV_H__
+#define __XFS_LOG_PRIV_H__
+
+struct xfs_buf;
+struct ktrace;
+struct log;
+struct xfs_buf_cancel;
+struct xfs_mount;
+
+/*
+ * Macros, structures, prototypes for internal log manager use.
+ */
+
+#define XLOG_MIN_ICLOGS 2
+#define XLOG_MED_ICLOGS 4
+#define XLOG_MAX_ICLOGS 8
+#define XLOG_CALLBACK_SIZE 10
+#define XLOG_HEADER_MAGIC_NUM 0xFEEDbabe /* Invalid cycle number */
+#define XLOG_VERSION_1 1
+#define XLOG_VERSION_2 2 /* Large IClogs, Log sunit */
+#define XLOG_VERSION_OKBITS (XLOG_VERSION_1 | XLOG_VERSION_2)
+#define XLOG_RECORD_BSIZE (16*1024) /* eventually 32k */
+#define XLOG_BIG_RECORD_BSIZE (32*1024) /* 32k buffers */
+#define XLOG_MAX_RECORD_BSIZE (256*1024)
+#define XLOG_HEADER_CYCLE_SIZE (32*1024) /* cycle data in header */
+#define XLOG_RECORD_BSHIFT 14 /* 16384 == 1 << 14 */
+#define XLOG_BIG_RECORD_BSHIFT 15 /* 32k == 1 << 15 */
+#define XLOG_MAX_RECORD_BSHIFT 18 /* 256k == 1 << 18 */
+#define XLOG_BTOLSUNIT(log, b) (((b)+(log)->l_mp->m_sb.sb_logsunit-1) / \
+ (log)->l_mp->m_sb.sb_logsunit)
+#define XLOG_LSUNITTOB(log, su) ((su) * (log)->l_mp->m_sb.sb_logsunit)
+
+#define XLOG_HEADER_SIZE 512
+
+#define XLOG_REC_SHIFT(log) \
+ BTOBB(1 << (XFS_SB_VERSION_HASLOGV2(&log->l_mp->m_sb) ? \
+ XLOG_MAX_RECORD_BSHIFT : XLOG_BIG_RECORD_BSHIFT))
+#define XLOG_TOTAL_REC_SHIFT(log) \
+ BTOBB(XLOG_MAX_ICLOGS << (XFS_SB_VERSION_HASLOGV2(&log->l_mp->m_sb) ? \
+ XLOG_MAX_RECORD_BSHIFT : XLOG_BIG_RECORD_BSHIFT))
+
+/*
+ * set lsns
+ */
+
+#define ASSIGN_LSN_CYCLE(lsn,cycle,arch) \
+ INT_SET(((uint *)&(lsn))[LSN_FIELD_CYCLE(arch)], arch, (cycle));
+#define ASSIGN_LSN_BLOCK(lsn,block,arch) \
+ INT_SET(((uint *)&(lsn))[LSN_FIELD_BLOCK(arch)], arch, (block));
+#define ASSIGN_ANY_LSN(lsn,cycle,block,arch) \
+ { \
+ ASSIGN_LSN_CYCLE(lsn,cycle,arch); \
+ ASSIGN_LSN_BLOCK(lsn,block,arch); \
+ }
+#define ASSIGN_LSN(lsn,log,arch) \
+ ASSIGN_ANY_LSN(lsn,(log)->l_curr_cycle,(log)->l_curr_block,arch);
+
+#define XLOG_SET(f,b) (((f) & (b)) == (b))
+
+#define GET_CYCLE(ptr, arch) \
+ (INT_GET(*(uint *)(ptr), arch) == XLOG_HEADER_MAGIC_NUM ? \
+ INT_GET(*((uint *)(ptr)+1), arch) : \
+ INT_GET(*(uint *)(ptr), arch) \
+ )
+
+#define BLK_AVG(blk1, blk2) ((blk1+blk2) >> 1)
+
+
+#ifdef __KERNEL__
+
+/*
+ * get client id from packed copy.
+ *
+ * this hack is here because the xlog_pack code copies four bytes
+ * of xlog_op_header containing the fields oh_clientid, oh_flags
+ * and oh_res2 into the packed copy.
+ *
+ * later on this four byte chunk is treated as an int and the
+ * client id is pulled out.
+ *
+ * this has endian issues, of course.
+ */
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define GET_CLIENT_ID(i,arch) \
+ ((i) & 0xff)
+#else
+#define GET_CLIENT_ID(i,arch) \
+ ((i) >> 24)
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XLOG_GRANT_SUB_SPACE)
+void xlog_grant_sub_space(struct log *log, int bytes, int type);
+#define XLOG_GRANT_SUB_SPACE(log,bytes,type) \
+ xlog_grant_sub_space(log,bytes,type)
+#else
+#define XLOG_GRANT_SUB_SPACE(log,bytes,type) \
+ { \
+ if (type == 'w') { \
+ (log)->l_grant_write_bytes -= (bytes); \
+ if ((log)->l_grant_write_bytes < 0) { \
+ (log)->l_grant_write_bytes += (log)->l_logsize; \
+ (log)->l_grant_write_cycle--; \
+ } \
+ } else { \
+ (log)->l_grant_reserve_bytes -= (bytes); \
+ if ((log)->l_grant_reserve_bytes < 0) { \
+ (log)->l_grant_reserve_bytes += (log)->l_logsize;\
+ (log)->l_grant_reserve_cycle--; \
+ } \
+ } \
+ }
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XLOG_GRANT_ADD_SPACE)
+void xlog_grant_add_space(struct log *log, int bytes, int type);
+#define XLOG_GRANT_ADD_SPACE(log,bytes,type) \
+ xlog_grant_add_space(log,bytes,type)
+#else
+#define XLOG_GRANT_ADD_SPACE(log,bytes,type) \
+ { \
+ if (type == 'w') { \
+ (log)->l_grant_write_bytes += (bytes); \
+ if ((log)->l_grant_write_bytes > (log)->l_logsize) { \
+ (log)->l_grant_write_bytes -= (log)->l_logsize; \
+ (log)->l_grant_write_cycle++; \
+ } \
+ } else { \
+ (log)->l_grant_reserve_bytes += (bytes); \
+ if ((log)->l_grant_reserve_bytes > (log)->l_logsize) { \
+ (log)->l_grant_reserve_bytes -= (log)->l_logsize;\
+ (log)->l_grant_reserve_cycle++; \
+ } \
+ } \
+ }
+#endif
+#define XLOG_INS_TICKETQ(q,tic) \
+ { \
+ if (q) { \
+ (tic)->t_next = (q); \
+ (tic)->t_prev = (q)->t_prev; \
+ (q)->t_prev->t_next = (tic); \
+ (q)->t_prev = (tic); \
+ } else { \
+ (tic)->t_prev = (tic)->t_next = (tic); \
+ (q) = (tic); \
+ } \
+ (tic)->t_flags |= XLOG_TIC_IN_Q; \
+ }
+#define XLOG_DEL_TICKETQ(q,tic) \
+ { \
+ if ((tic) == (tic)->t_next) { \
+ (q) = NULL; \
+ } else { \
+ (q) = (tic)->t_next; \
+ (tic)->t_next->t_prev = (tic)->t_prev; \
+ (tic)->t_prev->t_next = (tic)->t_next; \
+ } \
+ (tic)->t_next = (tic)->t_prev = NULL; \
+ (tic)->t_flags &= ~XLOG_TIC_IN_Q; \
+ }
+
+
+#define GRANT_LOCK(log) mutex_spinlock(&(log)->l_grant_lock)
+#define GRANT_UNLOCK(log, s) mutex_spinunlock(&(log)->l_grant_lock, s)
+#define LOG_LOCK(log) mutex_spinlock(&(log)->l_icloglock)
+#define LOG_UNLOCK(log, s) mutex_spinunlock(&(log)->l_icloglock, s)
+
+#define xlog_panic(args...) cmn_err(CE_PANIC, ## args)
+#define xlog_exit(args...) cmn_err(CE_PANIC, ## args)
+#define xlog_warn(args...) cmn_err(CE_WARN, ## args)
+
+/*
+ * In core log state
+ */
+#define XLOG_STATE_ACTIVE 0x0001 /* Current IC log being written to */
+#define XLOG_STATE_WANT_SYNC 0x0002 /* Want to sync this iclog; no more writes */
+#define XLOG_STATE_SYNCING 0x0004 /* This IC log is syncing */
+#define XLOG_STATE_DONE_SYNC 0x0008 /* Done syncing to disk */
+#define XLOG_STATE_DO_CALLBACK \
+ 0x0010 /* Process callback functions */
+#define XLOG_STATE_CALLBACK 0x0020 /* Callback functions now */
+#define XLOG_STATE_DIRTY 0x0040 /* Dirty IC log, not ready for ACTIVE status*/
+#define XLOG_STATE_IOERROR 0x0080 /* IO error happened in sync'ing log */
+#define XLOG_STATE_ALL 0x7FFF /* All possible valid flags */
+#define XLOG_STATE_NOTUSED 0x8000 /* This IC log not being used */
+#endif /* __KERNEL__ */
+
+/*
+ * Flags to log operation header
+ *
+ * The first write of a new transaction will be preceded with a start
+ * record, XLOG_START_TRANS. Once a transaction is committed, a commit
+ * record is written, XLOG_COMMIT_TRANS. If a single region can not fit into
+ * the remainder of the current active in-core log, it is split up into
+ * multiple regions. Each partial region will be marked with a
+ * XLOG_CONTINUE_TRANS until the last one, which gets marked with XLOG_END_TRANS.
+ *
+ */
+#define XLOG_START_TRANS 0x01 /* Start a new transaction */
+#define XLOG_COMMIT_TRANS 0x02 /* Commit this transaction */
+#define XLOG_CONTINUE_TRANS 0x04 /* Cont this trans into new region */
+#define XLOG_WAS_CONT_TRANS 0x08 /* Cont this trans into new region */
+#define XLOG_END_TRANS 0x10 /* End a continued transaction */
+#define XLOG_UNMOUNT_TRANS 0x20 /* Unmount a filesystem transaction */
+#define XLOG_SKIP_TRANS (XLOG_COMMIT_TRANS | XLOG_CONTINUE_TRANS | \
+ XLOG_WAS_CONT_TRANS | XLOG_END_TRANS | \
+ XLOG_UNMOUNT_TRANS)
+
+#ifdef __KERNEL__
+/*
+ * Flags to log ticket
+ */
+#define XLOG_TIC_INITED 0x1 /* has been initialized */
+#define XLOG_TIC_PERM_RESERV 0x2 /* permanent reservation */
+#define XLOG_TIC_IN_Q 0x4
+#endif /* __KERNEL__ */
+
+#define XLOG_UNMOUNT_TYPE 0x556e /* Un for Unmount */
+
+/*
+ * Flags for log structure
+ */
+#define XLOG_CHKSUM_MISMATCH 0x1 /* used only during recovery */
+#define XLOG_ACTIVE_RECOVERY 0x2 /* in the middle of recovery */
+#define XLOG_RECOVERY_NEEDED 0x4 /* log was recovered */
+#define XLOG_IO_ERROR 0x8 /* log hit an I/O error, and being
+ shutdown */
+typedef __uint32_t xlog_tid_t;
+
+
+#ifdef __KERNEL__
+/*
+ * Below are states for covering allocation transactions.
+ * By covering, we mean changing the h_tail_lsn in the last on-disk
+ * log write such that no allocation transactions will be re-done during
+ * recovery after a system crash. Recovery starts at the last on-disk
+ * log write.
+ *
+ * These states are used to insert dummy log entries to cover
+ * space allocation transactions which can undo non-transactional changes
+ * after a crash. Writes to a file with space
+ * already allocated do not result in any transactions. Allocations
+ * might include space beyond the EOF. So if we just push the EOF a
+ * little, the last transaction for the file could contain the wrong
+ * size. If there is no file system activity, after an allocation
+ * transaction, and the system crashes, the allocation transaction
+ * will get replayed and the file will be truncated. This could
+ * be hours/days/... after the allocation occurred.
+ *
+ * The fix for this is to do two dummy transactions when the
+ * system is idle. We need two dummy transaction because the h_tail_lsn
+ * in the log record header needs to point beyond the last possible
+ * non-dummy transaction. The first dummy changes the h_tail_lsn to
+ * the first transaction before the dummy. The second dummy causes
+ * h_tail_lsn to point to the first dummy. Recovery starts at h_tail_lsn.
+ *
+ * These dummy transactions get committed when everything
+ * is idle (after there has been some activity).
+ *
+ * There are 5 states used to control this.
+ *
+ * IDLE -- no logging has been done on the file system or
+ * we are done covering previous transactions.
+ * NEED -- logging has occurred and we need a dummy transaction
+ * when the log becomes idle.
+ * DONE -- we were in the NEED state and have committed a dummy
+ * transaction.
+ * NEED2 -- we detected that a dummy transaction has gone to the
+ * on disk log with no other transactions.
+ * DONE2 -- we committed a dummy transaction when in the NEED2 state.
+ *
+ * There are two places where we switch states:
+ *
+ * 1.) In xfs_sync, when we detect an idle log and are in NEED or NEED2.
+ * We commit the dummy transaction and switch to DONE or DONE2,
+ * respectively. In all other states, we don't do anything.
+ *
+ * 2.) When we finish writing the on-disk log (xlog_state_clean_log).
+ *
+ * No matter what state we are in, if this isn't the dummy
+ * transaction going out, the next state is NEED.
+ * So, if we aren't in the DONE or DONE2 states, the next state
+ * is NEED. We can't be finishing a write of the dummy record
+ * unless it was committed and the state switched to DONE or DONE2.
+ *
+ * If we are in the DONE state and this was a write of the
+ * dummy transaction, we move to NEED2.
+ *
+ * If we are in the DONE2 state and this was a write of the
+ * dummy transaction, we move to IDLE.
+ *
+ *
+ * Writing only one dummy transaction can get appended to
+ * one file space allocation. When this happens, the log recovery
+ * code replays the space allocation and a file could be truncated.
+ * This is why we have the NEED2 and DONE2 states before going idle.
+ */
+
+#define XLOG_STATE_COVER_IDLE 0
+#define XLOG_STATE_COVER_NEED 1
+#define XLOG_STATE_COVER_DONE 2
+#define XLOG_STATE_COVER_NEED2 3
+#define XLOG_STATE_COVER_DONE2 4
+
+#define XLOG_COVER_OPS 5
+
+typedef struct xlog_ticket {
+ sv_t t_sema; /* sleep on this semaphore :20 */
+ struct xlog_ticket *t_next; /* : 4 */
+ struct xlog_ticket *t_prev; /* : 4 */
+ xlog_tid_t t_tid; /* transaction identifier : 4 */
+ int t_curr_res; /* current reservation in bytes : 4 */
+ int t_unit_res; /* unit reservation in bytes : 4 */
+ __uint8_t t_ocnt; /* original count : 1 */
+ __uint8_t t_cnt; /* current count : 1 */
+ __uint8_t t_clientid; /* who does this belong to; : 1 */
+ __uint8_t t_flags; /* properties of reservation : 1 */
+} xlog_ticket_t;
+#endif
+
+
+typedef struct xlog_op_header {
+ xlog_tid_t oh_tid; /* transaction id of operation : 4 b */
+ int oh_len; /* bytes in data region : 4 b */
+ __uint8_t oh_clientid; /* who sent me this : 1 b */
+ __uint8_t oh_flags; /* : 1 b */
+ ushort oh_res2; /* 32 bit align : 2 b */
+} xlog_op_header_t;
+
+
+/* valid values for h_fmt */
+#define XLOG_FMT_UNKNOWN 0
+#define XLOG_FMT_LINUX_LE 1
+#define XLOG_FMT_LINUX_BE 2
+#define XLOG_FMT_IRIX_BE 3
+
+/* our fmt */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define XLOG_FMT XLOG_FMT_LINUX_LE
+#else
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define XLOG_FMT XLOG_FMT_LINUX_BE
+#else
+#error unknown byte order
+#endif
+#endif
+
+typedef struct xlog_rec_header {
+ uint h_magicno; /* log record (LR) identifier : 4 */
+ uint h_cycle; /* write cycle of log : 4 */
+ int h_version; /* LR version : 4 */
+ int h_len; /* len in bytes; should be 64-bit aligned: 4 */
+ xfs_lsn_t h_lsn; /* lsn of this LR : 8 */
+ xfs_lsn_t h_tail_lsn; /* lsn of 1st LR w/ buffers not committed: 8 */
+ uint h_chksum; /* may not be used; non-zero if used : 4 */
+ int h_prev_block; /* block number to previous LR : 4 */
+ int h_num_logops; /* number of log operations in this LR : 4 */
+ uint h_cycle_data[XLOG_HEADER_CYCLE_SIZE / BBSIZE];
+ /* new fields */
+ int h_fmt; /* format of log record : 4 */
+ uuid_t h_fs_uuid; /* uuid of FS : 16 */
+ int h_size; /* iclog size : 4 */
+} xlog_rec_header_t;
+
+typedef struct xlog_rec_ext_header {
+ uint xh_cycle; /* write cycle of log : 4 */
+ uint xh_cycle_data[XLOG_HEADER_CYCLE_SIZE / BBSIZE]; /* : 256 */
+} xlog_rec_ext_header_t;
+
+#ifdef __KERNEL__
+/*
+ * - A log record header is 512 bytes. There is plenty of room to grow the
+ * xlog_rec_header_t into the reserved space.
+ * - ic_data follows, so a write to disk can start at the beginning of
+ * the iclog.
+ * - ic_forcesema is used to implement synchronous forcing of the iclog to disk.
+ * - ic_next is the pointer to the next iclog in the ring.
+ * - ic_bp is a pointer to the buffer used to write this incore log to disk.
+ * - ic_log is a pointer back to the global log structure.
+ * - ic_callback is a linked list of callback function/argument pairs to be
+ * called after an iclog finishes writing.
+ * - ic_size is the full size of the header plus data.
+ * - ic_offset is the current number of bytes written to in this iclog.
+ * - ic_refcnt is bumped when someone is writing to the log.
+ * - ic_state is the state of the iclog.
+ */
+typedef struct xlog_iclog_fields {
+ sv_t ic_forcesema;
+ sv_t ic_writesema;
+ struct xlog_in_core *ic_next;
+ struct xlog_in_core *ic_prev;
+ struct xfs_buf *ic_bp;
+ struct log *ic_log;
+ xfs_log_callback_t *ic_callback;
+ xfs_log_callback_t **ic_callback_tail;
+#ifdef XFS_LOG_TRACE
+ struct ktrace *ic_trace;
+#endif
+ int ic_size;
+ int ic_offset;
+ int ic_refcnt;
+ int ic_bwritecnt;
+ ushort_t ic_state;
+ char *ic_datap; /* pointer to iclog data */
+} xlog_iclog_fields_t;
+
+typedef union xlog_in_core2 {
+ xlog_rec_header_t hic_header;
+ xlog_rec_ext_header_t hic_xheader;
+ char hic_sector[XLOG_HEADER_SIZE];
+} xlog_in_core_2_t;
+
+typedef struct xlog_in_core {
+ xlog_iclog_fields_t hic_fields;
+ xlog_in_core_2_t *hic_data;
+} xlog_in_core_t;
+
+/*
+ * Defines to save our code from this glop.
+ */
+#define ic_forcesema hic_fields.ic_forcesema
+#define ic_writesema hic_fields.ic_writesema
+#define ic_next hic_fields.ic_next
+#define ic_prev hic_fields.ic_prev
+#define ic_bp hic_fields.ic_bp
+#define ic_log hic_fields.ic_log
+#define ic_callback hic_fields.ic_callback
+#define ic_callback_tail hic_fields.ic_callback_tail
+#define ic_trace hic_fields.ic_trace
+#define ic_size hic_fields.ic_size
+#define ic_offset hic_fields.ic_offset
+#define ic_refcnt hic_fields.ic_refcnt
+#define ic_bwritecnt hic_fields.ic_bwritecnt
+#define ic_state hic_fields.ic_state
+#define ic_datap hic_fields.ic_datap
+#define ic_header hic_data->hic_header
+
+/*
+ * The reservation head lsn is not made up of a cycle number and block number.
+ * Instead, it uses a cycle number and byte number. Logs don't expect to
+ * overflow 31 bits worth of byte offset, so using a byte number will mean
+ * that round off problems won't occur when releasing partial reservations.
+ */
+typedef struct log {
+ /* The following block of fields are changed while holding icloglock */
+ sema_t l_flushsema; /* iclog flushing semaphore */
+ int l_flushcnt; /* # of procs waiting on this
+ * sema */
+ int l_ticket_cnt; /* free ticket count */
+ int l_ticket_tcnt; /* total ticket count */
+ int l_covered_state;/* state of "covering disk
+ * log entries" */
+ xlog_ticket_t *l_freelist; /* free list of tickets */
+ xlog_ticket_t *l_unmount_free;/* kmem_free these addresses */
+ xlog_ticket_t *l_tail; /* free list of tickets */
+ xlog_in_core_t *l_iclog; /* head log queue */
+ lock_t l_icloglock; /* grab to change iclog state */
+ xfs_lsn_t l_tail_lsn; /* lsn of 1st LR with unflushed
+ * buffers */
+ xfs_lsn_t l_last_sync_lsn;/* lsn of last LR on disk */
+ struct xfs_mount *l_mp; /* mount point */
+ struct xfs_buf *l_xbuf; /* extra buffer for log
+ * wrapping */
+ struct xfs_buftarg *l_targ; /* buftarg of log */
+ xfs_daddr_t l_logBBstart; /* start block of log */
+ int l_logsize; /* size of log in bytes */
+ int l_logBBsize; /* size of log in BB chunks */
+ int l_curr_cycle; /* Cycle number of log writes */
+ int l_prev_cycle; /* Cycle number before last
+ * block increment */
+ int l_curr_block; /* current logical log block */
+ int l_prev_block; /* previous logical log block */
+ int l_iclog_size; /* size of log in bytes */
+ int l_iclog_size_log; /* log power size of log */
+ int l_iclog_bufs; /* number of iclog buffers */
+
+ /* The following field are used for debugging; need to hold icloglock */
+ char *l_iclog_bak[XLOG_MAX_ICLOGS];
+
+ /* The following block of fields are changed while holding grant_lock */
+ lock_t l_grant_lock;
+ xlog_ticket_t *l_reserve_headq;
+ xlog_ticket_t *l_write_headq;
+ int l_grant_reserve_cycle;
+ int l_grant_reserve_bytes;
+ int l_grant_write_cycle;
+ int l_grant_write_bytes;
+
+ /* The following fields don't need locking */
+#ifdef XFS_LOG_TRACE
+ struct ktrace *l_trace;
+ struct ktrace *l_grant_trace;
+#endif
+ uint l_flags;
+ uint l_quotaoffs_flag; /* XFS_DQ_*, for QUOTAOFFs */
+ struct xfs_buf_cancel **l_buf_cancel_table;
+ int l_iclog_hsize; /* size of iclog header */
+ int l_iclog_heads; /* # of iclog header sectors */
+ uint l_sectbb_log; /* log2 of sector size in BBs */
+ uint l_sectbb_mask; /* sector size (in BBs)
+ * alignment mask */
+} xlog_t;
+
+
+/* common routines */
+extern xfs_lsn_t xlog_assign_tail_lsn(struct xfs_mount *mp);
+extern int xlog_find_head(xlog_t *log, xfs_daddr_t *head_blk);
+extern int xlog_find_tail(xlog_t *log,
+ xfs_daddr_t *head_blk,
+ xfs_daddr_t *tail_blk,
+ int readonly);
+extern int xlog_recover(xlog_t *log, int readonly);
+extern int xlog_recover_finish(xlog_t *log, int mfsi_flags);
+extern void xlog_pack_data(xlog_t *log, xlog_in_core_t *iclog, int);
+extern void xlog_recover_process_iunlinks(xlog_t *log);
+
+extern struct xfs_buf *xlog_get_bp(xlog_t *, int);
+extern void xlog_put_bp(struct xfs_buf *);
+extern int xlog_bread(xlog_t *, xfs_daddr_t, int, struct xfs_buf *);
+extern xfs_caddr_t xlog_align(xlog_t *, xfs_daddr_t, int, struct xfs_buf *);
+
+/* iclog tracing */
+#define XLOG_TRACE_GRAB_FLUSH 1
+#define XLOG_TRACE_REL_FLUSH 2
+#define XLOG_TRACE_SLEEP_FLUSH 3
+#define XLOG_TRACE_WAKE_FLUSH 4
+
+#endif /* __KERNEL__ */
+
+#endif /* __XFS_LOG_PRIV_H__ */
diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
new file mode 100644
index 00000000000000..6079aad901569c
--- /dev/null
+++ b/fs/xfs/xfs_log_recover.c
@@ -0,0 +1,4098 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_ag.h"
+#include "xfs_sb.h"
+#include "xfs_trans.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_error.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_alloc.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_imap.h"
+#include "xfs_inode_item.h"
+#include "xfs_inode.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_log_priv.h"
+#include "xfs_buf_item.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_log_recover.h"
+#include "xfs_extfree_item.h"
+#include "xfs_trans_priv.h"
+#include "xfs_bit.h"
+#include "xfs_quota.h"
+#include "xfs_rw.h"
+
+STATIC int xlog_find_zeroed(xlog_t *, xfs_daddr_t *);
+STATIC int xlog_clear_stale_blocks(xlog_t *, xfs_lsn_t);
+STATIC void xlog_recover_insert_item_backq(xlog_recover_item_t **q,
+ xlog_recover_item_t *item);
+#if defined(DEBUG)
+STATIC void xlog_recover_check_summary(xlog_t *);
+STATIC void xlog_recover_check_ail(xfs_mount_t *, xfs_log_item_t *, int);
+#else
+#define xlog_recover_check_summary(log)
+#define xlog_recover_check_ail(mp, lip, gen)
+#endif
+
+
+/*
+ * Sector aligned buffer routines for buffer create/read/write/access
+ */
+
+#define XLOG_SECTOR_ROUNDUP_BBCOUNT(log, bbs) \
+ ( ((log)->l_sectbb_mask && (bbs & (log)->l_sectbb_mask)) ? \
+ ((bbs + (log)->l_sectbb_mask + 1) & ~(log)->l_sectbb_mask) : (bbs) )
+#define XLOG_SECTOR_ROUNDDOWN_BLKNO(log, bno) ((bno) & ~(log)->l_sectbb_mask)
+
+xfs_buf_t *
+xlog_get_bp(
+ xlog_t *log,
+ int num_bblks)
+{
+ ASSERT(num_bblks > 0);
+
+ if (log->l_sectbb_log) {
+ if (num_bblks > 1)
+ num_bblks += XLOG_SECTOR_ROUNDUP_BBCOUNT(log, 1);
+ num_bblks = XLOG_SECTOR_ROUNDUP_BBCOUNT(log, num_bblks);
+ }
+ return xfs_buf_get_noaddr(BBTOB(num_bblks), log->l_mp->m_logdev_targp);
+}
+
+void
+xlog_put_bp(
+ xfs_buf_t *bp)
+{
+ xfs_buf_free(bp);
+}
+
+
+/*
+ * nbblks should be uint, but oh well. Just want to catch that 32-bit length.
+ */
+int
+xlog_bread(
+ xlog_t *log,
+ xfs_daddr_t blk_no,
+ int nbblks,
+ xfs_buf_t *bp)
+{
+ int error;
+
+ if (log->l_sectbb_log) {
+ blk_no = XLOG_SECTOR_ROUNDDOWN_BLKNO(log, blk_no);
+ nbblks = XLOG_SECTOR_ROUNDUP_BBCOUNT(log, nbblks);
+ }
+
+ ASSERT(nbblks > 0);
+ ASSERT(BBTOB(nbblks) <= XFS_BUF_SIZE(bp));
+ ASSERT(bp);
+
+ XFS_BUF_SET_ADDR(bp, log->l_logBBstart + blk_no);
+ XFS_BUF_READ(bp);
+ XFS_BUF_BUSY(bp);
+ XFS_BUF_SET_COUNT(bp, BBTOB(nbblks));
+ XFS_BUF_SET_TARGET(bp, log->l_mp->m_logdev_targp);
+
+ xfsbdstrat(log->l_mp, bp);
+ if ((error = xfs_iowait(bp)))
+ xfs_ioerror_alert("xlog_bread", log->l_mp,
+ bp, XFS_BUF_ADDR(bp));
+ return error;
+}
+
+/*
+ * Write out the buffer at the given block for the given number of blocks.
+ * The buffer is kept locked across the write and is returned locked.
+ * This can only be used for synchronous log writes.
+ */
+int
+xlog_bwrite(
+ xlog_t *log,
+ xfs_daddr_t blk_no,
+ int nbblks,
+ xfs_buf_t *bp)
+{
+ int error;
+
+ if (log->l_sectbb_log) {
+ blk_no = XLOG_SECTOR_ROUNDDOWN_BLKNO(log, blk_no);
+ nbblks = XLOG_SECTOR_ROUNDUP_BBCOUNT(log, nbblks);
+ }
+
+ ASSERT(nbblks > 0);
+ ASSERT(BBTOB(nbblks) <= XFS_BUF_SIZE(bp));
+
+ XFS_BUF_SET_ADDR(bp, log->l_logBBstart + blk_no);
+ XFS_BUF_ZEROFLAGS(bp);
+ XFS_BUF_BUSY(bp);
+ XFS_BUF_HOLD(bp);
+ XFS_BUF_PSEMA(bp, PRIBIO);
+ XFS_BUF_SET_COUNT(bp, BBTOB(nbblks));
+ XFS_BUF_SET_TARGET(bp, log->l_mp->m_logdev_targp);
+
+ if ((error = xfs_bwrite(log->l_mp, bp)))
+ xfs_ioerror_alert("xlog_bwrite", log->l_mp,
+ bp, XFS_BUF_ADDR(bp));
+ return error;
+}
+
+xfs_caddr_t
+xlog_align(
+ xlog_t *log,
+ xfs_daddr_t blk_no,
+ int nbblks,
+ xfs_buf_t *bp)
+{
+ xfs_caddr_t ptr;
+
+ if (!log->l_sectbb_log)
+ return XFS_BUF_PTR(bp);
+
+ ptr = XFS_BUF_PTR(bp) + BBTOB((int)blk_no & log->l_sectbb_mask);
+ ASSERT(XFS_BUF_SIZE(bp) >=
+ BBTOB(nbblks + (blk_no & log->l_sectbb_mask)));
+ return ptr;
+}
+
+#ifdef DEBUG
+/*
+ * dump debug superblock and log record information
+ */
+STATIC void
+xlog_header_check_dump(
+ xfs_mount_t *mp,
+ xlog_rec_header_t *head)
+{
+ int b;
+
+ printk("%s: SB : uuid = ", __FUNCTION__);
+ for (b = 0; b < 16; b++)
+ printk("%02x",((unsigned char *)&mp->m_sb.sb_uuid)[b]);
+ printk(", fmt = %d\n", XLOG_FMT);
+ printk(" log : uuid = ");
+ for (b = 0; b < 16; b++)
+ printk("%02x",((unsigned char *)&head->h_fs_uuid)[b]);
+ printk(", fmt = %d\n", INT_GET(head->h_fmt, ARCH_CONVERT));
+}
+#else
+#define xlog_header_check_dump(mp, head)
+#endif
+
+/*
+ * check log record header for recovery
+ */
+STATIC int
+xlog_header_check_recover(
+ xfs_mount_t *mp,
+ xlog_rec_header_t *head)
+{
+ ASSERT(INT_GET(head->h_magicno, ARCH_CONVERT) == XLOG_HEADER_MAGIC_NUM);
+
+ /*
+ * IRIX doesn't write the h_fmt field and leaves it zeroed
+ * (XLOG_FMT_UNKNOWN). This stops us from trying to recover
+ * a dirty log created in IRIX.
+ */
+ if (unlikely(INT_GET(head->h_fmt, ARCH_CONVERT) != XLOG_FMT)) {
+ xlog_warn(
+ "XFS: dirty log written in incompatible format - can't recover");
+ xlog_header_check_dump(mp, head);
+ XFS_ERROR_REPORT("xlog_header_check_recover(1)",
+ XFS_ERRLEVEL_HIGH, mp);
+ return XFS_ERROR(EFSCORRUPTED);
+ } else if (unlikely(!uuid_equal(&mp->m_sb.sb_uuid, &head->h_fs_uuid))) {
+ xlog_warn(
+ "XFS: dirty log entry has mismatched uuid - can't recover");
+ xlog_header_check_dump(mp, head);
+ XFS_ERROR_REPORT("xlog_header_check_recover(2)",
+ XFS_ERRLEVEL_HIGH, mp);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ return 0;
+}
+
+/*
+ * read the head block of the log and check the header
+ */
+STATIC int
+xlog_header_check_mount(
+ xfs_mount_t *mp,
+ xlog_rec_header_t *head)
+{
+ ASSERT(INT_GET(head->h_magicno, ARCH_CONVERT) == XLOG_HEADER_MAGIC_NUM);
+
+ if (uuid_is_nil(&head->h_fs_uuid)) {
+ /*
+ * IRIX doesn't write the h_fs_uuid or h_fmt fields. If
+ * h_fs_uuid is nil, we assume this log was last mounted
+ * by IRIX and continue.
+ */
+ xlog_warn("XFS: nil uuid in log - IRIX style log");
+ } else if (unlikely(!uuid_equal(&mp->m_sb.sb_uuid, &head->h_fs_uuid))) {
+ xlog_warn("XFS: log has mismatched uuid - can't recover");
+ xlog_header_check_dump(mp, head);
+ XFS_ERROR_REPORT("xlog_header_check_mount",
+ XFS_ERRLEVEL_HIGH, mp);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ return 0;
+}
+
+STATIC void
+xlog_recover_iodone(
+ struct xfs_buf *bp)
+{
+ xfs_mount_t *mp;
+
+ ASSERT(XFS_BUF_FSPRIVATE(bp, void *));
+
+ if (XFS_BUF_GETERROR(bp)) {
+ /*
+ * We're not going to bother about retrying
+ * this during recovery. One strike!
+ */
+ mp = XFS_BUF_FSPRIVATE(bp, xfs_mount_t *);
+ xfs_ioerror_alert("xlog_recover_iodone",
+ mp, bp, XFS_BUF_ADDR(bp));
+ xfs_force_shutdown(mp, XFS_METADATA_IO_ERROR);
+ }
+ XFS_BUF_SET_FSPRIVATE(bp, NULL);
+ XFS_BUF_CLR_IODONE_FUNC(bp);
+ xfs_biodone(bp);
+}
+
+/*
+ * This routine finds (to an approximation) the first block in the physical
+ * log which contains the given cycle. It uses a binary search algorithm.
+ * Note that the algorithm can not be perfect because the disk will not
+ * necessarily be perfect.
+ */
+int
+xlog_find_cycle_start(
+ xlog_t *log,
+ xfs_buf_t *bp,
+ xfs_daddr_t first_blk,
+ xfs_daddr_t *last_blk,
+ uint cycle)
+{
+ xfs_caddr_t offset;
+ xfs_daddr_t mid_blk;
+ uint mid_cycle;
+ int error;
+
+ mid_blk = BLK_AVG(first_blk, *last_blk);
+ while (mid_blk != first_blk && mid_blk != *last_blk) {
+ if ((error = xlog_bread(log, mid_blk, 1, bp)))
+ return error;
+ offset = xlog_align(log, mid_blk, 1, bp);
+ mid_cycle = GET_CYCLE(offset, ARCH_CONVERT);
+ if (mid_cycle == cycle) {
+ *last_blk = mid_blk;
+ /* last_half_cycle == mid_cycle */
+ } else {
+ first_blk = mid_blk;
+ /* first_half_cycle == mid_cycle */
+ }
+ mid_blk = BLK_AVG(first_blk, *last_blk);
+ }
+ ASSERT((mid_blk == first_blk && mid_blk+1 == *last_blk) ||
+ (mid_blk == *last_blk && mid_blk-1 == first_blk));
+
+ return 0;
+}
+
+/*
+ * Check that the range of blocks does not contain the cycle number
+ * given. The scan needs to occur from front to back and the ptr into the
+ * region must be updated since a later routine will need to perform another
+ * test. If the region is completely good, we end up returning the same
+ * last block number.
+ *
+ * Set blkno to -1 if we encounter no errors. This is an invalid block number
+ * since we don't ever expect logs to get this large.
+ */
+STATIC int
+xlog_find_verify_cycle(
+ xlog_t *log,
+ xfs_daddr_t start_blk,
+ int nbblks,
+ uint stop_on_cycle_no,
+ xfs_daddr_t *new_blk)
+{
+ xfs_daddr_t i, j;
+ uint cycle;
+ xfs_buf_t *bp;
+ xfs_daddr_t bufblks;
+ xfs_caddr_t buf = NULL;
+ int error = 0;
+
+ bufblks = 1 << ffs(nbblks);
+
+ while (!(bp = xlog_get_bp(log, bufblks))) {
+ /* can't get enough memory to do everything in one big buffer */
+ bufblks >>= 1;
+ if (bufblks <= log->l_sectbb_log)
+ return ENOMEM;
+ }
+
+ for (i = start_blk; i < start_blk + nbblks; i += bufblks) {
+ int bcount;
+
+ bcount = min(bufblks, (start_blk + nbblks - i));
+
+ if ((error = xlog_bread(log, i, bcount, bp)))
+ goto out;
+
+ buf = xlog_align(log, i, bcount, bp);
+ for (j = 0; j < bcount; j++) {
+ cycle = GET_CYCLE(buf, ARCH_CONVERT);
+ if (cycle == stop_on_cycle_no) {
+ *new_blk = i+j;
+ goto out;
+ }
+
+ buf += BBSIZE;
+ }
+ }
+
+ *new_blk = -1;
+
+out:
+ xlog_put_bp(bp);
+ return error;
+}
+
+/*
+ * Potentially backup over partial log record write.
+ *
+ * In the typical case, last_blk is the number of the block directly after
+ * a good log record. Therefore, we subtract one to get the block number
+ * of the last block in the given buffer. extra_bblks contains the number
+ * of blocks we would have read on a previous read. This happens when the
+ * last log record is split over the end of the physical log.
+ *
+ * extra_bblks is the number of blocks potentially verified on a previous
+ * call to this routine.
+ */
+STATIC int
+xlog_find_verify_log_record(
+ xlog_t *log,
+ xfs_daddr_t start_blk,
+ xfs_daddr_t *last_blk,
+ int extra_bblks)
+{
+ xfs_daddr_t i;
+ xfs_buf_t *bp;
+ xfs_caddr_t offset = NULL;
+ xlog_rec_header_t *head = NULL;
+ int error = 0;
+ int smallmem = 0;
+ int num_blks = *last_blk - start_blk;
+ int xhdrs;
+
+ ASSERT(start_blk != 0 || *last_blk != start_blk);
+
+ if (!(bp = xlog_get_bp(log, num_blks))) {
+ if (!(bp = xlog_get_bp(log, 1)))
+ return ENOMEM;
+ smallmem = 1;
+ } else {
+ if ((error = xlog_bread(log, start_blk, num_blks, bp)))
+ goto out;
+ offset = xlog_align(log, start_blk, num_blks, bp);
+ offset += ((num_blks - 1) << BBSHIFT);
+ }
+
+ for (i = (*last_blk) - 1; i >= 0; i--) {
+ if (i < start_blk) {
+ /* valid log record not found */
+ xlog_warn(
+ "XFS: Log inconsistent (didn't find previous header)");
+ ASSERT(0);
+ error = XFS_ERROR(EIO);
+ goto out;
+ }
+
+ if (smallmem) {
+ if ((error = xlog_bread(log, i, 1, bp)))
+ goto out;
+ offset = xlog_align(log, i, 1, bp);
+ }
+
+ head = (xlog_rec_header_t *)offset;
+
+ if (XLOG_HEADER_MAGIC_NUM ==
+ INT_GET(head->h_magicno, ARCH_CONVERT))
+ break;
+
+ if (!smallmem)
+ offset -= BBSIZE;
+ }
+
+ /*
+ * We hit the beginning of the physical log & still no header. Return
+ * to caller. If caller can handle a return of -1, then this routine
+ * will be called again for the end of the physical log.
+ */
+ if (i == -1) {
+ error = -1;
+ goto out;
+ }
+
+ /*
+ * We have the final block of the good log (the first block
+ * of the log record _before_ the head. So we check the uuid.
+ */
+ if ((error = xlog_header_check_mount(log->l_mp, head)))
+ goto out;
+
+ /*
+ * We may have found a log record header before we expected one.
+ * last_blk will be the 1st block # with a given cycle #. We may end
+ * up reading an entire log record. In this case, we don't want to
+ * reset last_blk. Only when last_blk points in the middle of a log
+ * record do we update last_blk.
+ */
+ if (XFS_SB_VERSION_HASLOGV2(&log->l_mp->m_sb)) {
+ uint h_size = INT_GET(head->h_size, ARCH_CONVERT);
+
+ xhdrs = h_size / XLOG_HEADER_CYCLE_SIZE;
+ if (h_size % XLOG_HEADER_CYCLE_SIZE)
+ xhdrs++;
+ } else {
+ xhdrs = 1;
+ }
+
+ if (*last_blk - i + extra_bblks
+ != BTOBB(INT_GET(head->h_len, ARCH_CONVERT)) + xhdrs)
+ *last_blk = i;
+
+out:
+ xlog_put_bp(bp);
+ return error;
+}
+
+/*
+ * Head is defined to be the point of the log where the next log write
+ * write could go. This means that incomplete LR writes at the end are
+ * eliminated when calculating the head. We aren't guaranteed that previous
+ * LR have complete transactions. We only know that a cycle number of
+ * current cycle number -1 won't be present in the log if we start writing
+ * from our current block number.
+ *
+ * last_blk contains the block number of the first block with a given
+ * cycle number.
+ *
+ * Return: zero if normal, non-zero if error.
+ */
+int
+xlog_find_head(
+ xlog_t *log,
+ xfs_daddr_t *return_head_blk)
+{
+ xfs_buf_t *bp;
+ xfs_caddr_t offset;
+ xfs_daddr_t new_blk, first_blk, start_blk, last_blk, head_blk;
+ int num_scan_bblks;
+ uint first_half_cycle, last_half_cycle;
+ uint stop_on_cycle;
+ int error, log_bbnum = log->l_logBBsize;
+
+ /* Is the end of the log device zeroed? */
+ if ((error = xlog_find_zeroed(log, &first_blk)) == -1) {
+ *return_head_blk = first_blk;
+
+ /* Is the whole lot zeroed? */
+ if (!first_blk) {
+ /* Linux XFS shouldn't generate totally zeroed logs -
+ * mkfs etc write a dummy unmount record to a fresh
+ * log so we can store the uuid in there
+ */
+ xlog_warn("XFS: totally zeroed log");
+ }
+
+ return 0;
+ } else if (error) {
+ xlog_warn("XFS: empty log check failed");
+ return error;
+ }
+
+ first_blk = 0; /* get cycle # of 1st block */
+ bp = xlog_get_bp(log, 1);
+ if (!bp)
+ return ENOMEM;
+ if ((error = xlog_bread(log, 0, 1, bp)))
+ goto bp_err;
+ offset = xlog_align(log, 0, 1, bp);
+ first_half_cycle = GET_CYCLE(offset, ARCH_CONVERT);
+
+ last_blk = head_blk = log_bbnum - 1; /* get cycle # of last block */
+ if ((error = xlog_bread(log, last_blk, 1, bp)))
+ goto bp_err;
+ offset = xlog_align(log, last_blk, 1, bp);
+ last_half_cycle = GET_CYCLE(offset, ARCH_CONVERT);
+ ASSERT(last_half_cycle != 0);
+
+ /*
+ * If the 1st half cycle number is equal to the last half cycle number,
+ * then the entire log is stamped with the same cycle number. In this
+ * case, head_blk can't be set to zero (which makes sense). The below
+ * math doesn't work out properly with head_blk equal to zero. Instead,
+ * we set it to log_bbnum which is an invalid block number, but this
+ * value makes the math correct. If head_blk doesn't changed through
+ * all the tests below, *head_blk is set to zero at the very end rather
+ * than log_bbnum. In a sense, log_bbnum and zero are the same block
+ * in a circular file.
+ */
+ if (first_half_cycle == last_half_cycle) {
+ /*
+ * In this case we believe that the entire log should have
+ * cycle number last_half_cycle. We need to scan backwards
+ * from the end verifying that there are no holes still
+ * containing last_half_cycle - 1. If we find such a hole,
+ * then the start of that hole will be the new head. The
+ * simple case looks like
+ * x | x ... | x - 1 | x
+ * Another case that fits this picture would be
+ * x | x + 1 | x ... | x
+ * In this case the head really is somwhere at the end of the
+ * log, as one of the latest writes at the beginning was
+ * incomplete.
+ * One more case is
+ * x | x + 1 | x ... | x - 1 | x
+ * This is really the combination of the above two cases, and
+ * the head has to end up at the start of the x-1 hole at the
+ * end of the log.
+ *
+ * In the 256k log case, we will read from the beginning to the
+ * end of the log and search for cycle numbers equal to x-1.
+ * We don't worry about the x+1 blocks that we encounter,
+ * because we know that they cannot be the head since the log
+ * started with x.
+ */
+ head_blk = log_bbnum;
+ stop_on_cycle = last_half_cycle - 1;
+ } else {
+ /*
+ * In this case we want to find the first block with cycle
+ * number matching last_half_cycle. We expect the log to be
+ * some variation on
+ * x + 1 ... | x ...
+ * The first block with cycle number x (last_half_cycle) will
+ * be where the new head belongs. First we do a binary search
+ * for the first occurrence of last_half_cycle. The binary
+ * search may not be totally accurate, so then we scan back
+ * from there looking for occurrences of last_half_cycle before
+ * us. If that backwards scan wraps around the beginning of
+ * the log, then we look for occurrences of last_half_cycle - 1
+ * at the end of the log. The cases we're looking for look
+ * like
+ * x + 1 ... | x | x + 1 | x ...
+ * ^ binary search stopped here
+ * or
+ * x + 1 ... | x ... | x - 1 | x
+ * <---------> less than scan distance
+ */
+ stop_on_cycle = last_half_cycle;
+ if ((error = xlog_find_cycle_start(log, bp, first_blk,
+ &head_blk, last_half_cycle)))
+ goto bp_err;
+ }
+
+ /*
+ * Now validate the answer. Scan back some number of maximum possible
+ * blocks and make sure each one has the expected cycle number. The
+ * maximum is determined by the total possible amount of buffering
+ * in the in-core log. The following number can be made tighter if
+ * we actually look at the block size of the filesystem.
+ */
+ num_scan_bblks = XLOG_TOTAL_REC_SHIFT(log);
+ if (head_blk >= num_scan_bblks) {
+ /*
+ * We are guaranteed that the entire check can be performed
+ * in one buffer.
+ */
+ start_blk = head_blk - num_scan_bblks;
+ if ((error = xlog_find_verify_cycle(log,
+ start_blk, num_scan_bblks,
+ stop_on_cycle, &new_blk)))
+ goto bp_err;
+ if (new_blk != -1)
+ head_blk = new_blk;
+ } else { /* need to read 2 parts of log */
+ /*
+ * We are going to scan backwards in the log in two parts.
+ * First we scan the physical end of the log. In this part
+ * of the log, we are looking for blocks with cycle number
+ * last_half_cycle - 1.
+ * If we find one, then we know that the log starts there, as
+ * we've found a hole that didn't get written in going around
+ * the end of the physical log. The simple case for this is
+ * x + 1 ... | x ... | x - 1 | x
+ * <---------> less than scan distance
+ * If all of the blocks at the end of the log have cycle number
+ * last_half_cycle, then we check the blocks at the start of
+ * the log looking for occurrences of last_half_cycle. If we
+ * find one, then our current estimate for the location of the
+ * first occurrence of last_half_cycle is wrong and we move
+ * back to the hole we've found. This case looks like
+ * x + 1 ... | x | x + 1 | x ...
+ * ^ binary search stopped here
+ * Another case we need to handle that only occurs in 256k
+ * logs is
+ * x + 1 ... | x ... | x+1 | x ...
+ * ^ binary search stops here
+ * In a 256k log, the scan at the end of the log will see the
+ * x + 1 blocks. We need to skip past those since that is
+ * certainly not the head of the log. By searching for
+ * last_half_cycle-1 we accomplish that.
+ */
+ start_blk = log_bbnum - num_scan_bblks + head_blk;
+ ASSERT(head_blk <= INT_MAX &&
+ (xfs_daddr_t) num_scan_bblks - head_blk >= 0);
+ if ((error = xlog_find_verify_cycle(log, start_blk,
+ num_scan_bblks - (int)head_blk,
+ (stop_on_cycle - 1), &new_blk)))
+ goto bp_err;
+ if (new_blk != -1) {
+ head_blk = new_blk;
+ goto bad_blk;
+ }
+
+ /*
+ * Scan beginning of log now. The last part of the physical
+ * log is good. This scan needs to verify that it doesn't find
+ * the last_half_cycle.
+ */
+ start_blk = 0;
+ ASSERT(head_blk <= INT_MAX);
+ if ((error = xlog_find_verify_cycle(log,
+ start_blk, (int)head_blk,
+ stop_on_cycle, &new_blk)))
+ goto bp_err;
+ if (new_blk != -1)
+ head_blk = new_blk;
+ }
+
+ bad_blk:
+ /*
+ * Now we need to make sure head_blk is not pointing to a block in
+ * the middle of a log record.
+ */
+ num_scan_bblks = XLOG_REC_SHIFT(log);
+ if (head_blk >= num_scan_bblks) {
+ start_blk = head_blk - num_scan_bblks; /* don't read head_blk */
+
+ /* start ptr at last block ptr before head_blk */
+ if ((error = xlog_find_verify_log_record(log, start_blk,
+ &head_blk, 0)) == -1) {
+ error = XFS_ERROR(EIO);
+ goto bp_err;
+ } else if (error)
+ goto bp_err;
+ } else {
+ start_blk = 0;
+ ASSERT(head_blk <= INT_MAX);
+ if ((error = xlog_find_verify_log_record(log, start_blk,
+ &head_blk, 0)) == -1) {
+ /* We hit the beginning of the log during our search */
+ start_blk = log_bbnum - num_scan_bblks + head_blk;
+ new_blk = log_bbnum;
+ ASSERT(start_blk <= INT_MAX &&
+ (xfs_daddr_t) log_bbnum-start_blk >= 0);
+ ASSERT(head_blk <= INT_MAX);
+ if ((error = xlog_find_verify_log_record(log,
+ start_blk, &new_blk,
+ (int)head_blk)) == -1) {
+ error = XFS_ERROR(EIO);
+ goto bp_err;
+ } else if (error)
+ goto bp_err;
+ if (new_blk != log_bbnum)
+ head_blk = new_blk;
+ } else if (error)
+ goto bp_err;
+ }
+
+ xlog_put_bp(bp);
+ if (head_blk == log_bbnum)
+ *return_head_blk = 0;
+ else
+ *return_head_blk = head_blk;
+ /*
+ * When returning here, we have a good block number. Bad block
+ * means that during a previous crash, we didn't have a clean break
+ * from cycle number N to cycle number N-1. In this case, we need
+ * to find the first block with cycle number N-1.
+ */
+ return 0;
+
+ bp_err:
+ xlog_put_bp(bp);
+
+ if (error)
+ xlog_warn("XFS: failed to find log head");
+ return error;
+}
+
+/*
+ * Find the sync block number or the tail of the log.
+ *
+ * This will be the block number of the last record to have its
+ * associated buffers synced to disk. Every log record header has
+ * a sync lsn embedded in it. LSNs hold block numbers, so it is easy
+ * to get a sync block number. The only concern is to figure out which
+ * log record header to believe.
+ *
+ * The following algorithm uses the log record header with the largest
+ * lsn. The entire log record does not need to be valid. We only care
+ * that the header is valid.
+ *
+ * We could speed up search by using current head_blk buffer, but it is not
+ * available.
+ */
+int
+xlog_find_tail(
+ xlog_t *log,
+ xfs_daddr_t *head_blk,
+ xfs_daddr_t *tail_blk,
+ int readonly)
+{
+ xlog_rec_header_t *rhead;
+ xlog_op_header_t *op_head;
+ xfs_caddr_t offset = NULL;
+ xfs_buf_t *bp;
+ int error, i, found;
+ xfs_daddr_t umount_data_blk;
+ xfs_daddr_t after_umount_blk;
+ xfs_lsn_t tail_lsn;
+ int hblks;
+
+ found = 0;
+
+ /*
+ * Find previous log record
+ */
+ if ((error = xlog_find_head(log, head_blk)))
+ return error;
+
+ bp = xlog_get_bp(log, 1);
+ if (!bp)
+ return ENOMEM;
+ if (*head_blk == 0) { /* special case */
+ if ((error = xlog_bread(log, 0, 1, bp)))
+ goto bread_err;
+ offset = xlog_align(log, 0, 1, bp);
+ if (GET_CYCLE(offset, ARCH_CONVERT) == 0) {
+ *tail_blk = 0;
+ /* leave all other log inited values alone */
+ goto exit;
+ }
+ }
+
+ /*
+ * Search backwards looking for log record header block
+ */
+ ASSERT(*head_blk < INT_MAX);
+ for (i = (int)(*head_blk) - 1; i >= 0; i--) {
+ if ((error = xlog_bread(log, i, 1, bp)))
+ goto bread_err;
+ offset = xlog_align(log, i, 1, bp);
+ if (XLOG_HEADER_MAGIC_NUM ==
+ INT_GET(*(uint *)offset, ARCH_CONVERT)) {
+ found = 1;
+ break;
+ }
+ }
+ /*
+ * If we haven't found the log record header block, start looking
+ * again from the end of the physical log. XXXmiken: There should be
+ * a check here to make sure we didn't search more than N blocks in
+ * the previous code.
+ */
+ if (!found) {
+ for (i = log->l_logBBsize - 1; i >= (int)(*head_blk); i--) {
+ if ((error = xlog_bread(log, i, 1, bp)))
+ goto bread_err;
+ offset = xlog_align(log, i, 1, bp);
+ if (XLOG_HEADER_MAGIC_NUM ==
+ INT_GET(*(uint*)offset, ARCH_CONVERT)) {
+ found = 2;
+ break;
+ }
+ }
+ }
+ if (!found) {
+ xlog_warn("XFS: xlog_find_tail: couldn't find sync record");
+ ASSERT(0);
+ return XFS_ERROR(EIO);
+ }
+
+ /* find blk_no of tail of log */
+ rhead = (xlog_rec_header_t *)offset;
+ *tail_blk = BLOCK_LSN(rhead->h_tail_lsn, ARCH_CONVERT);
+
+ /*
+ * Reset log values according to the state of the log when we
+ * crashed. In the case where head_blk == 0, we bump curr_cycle
+ * one because the next write starts a new cycle rather than
+ * continuing the cycle of the last good log record. At this
+ * point we have guaranteed that all partial log records have been
+ * accounted for. Therefore, we know that the last good log record
+ * written was complete and ended exactly on the end boundary
+ * of the physical log.
+ */
+ log->l_prev_block = i;
+ log->l_curr_block = (int)*head_blk;
+ log->l_curr_cycle = INT_GET(rhead->h_cycle, ARCH_CONVERT);
+ if (found == 2)
+ log->l_curr_cycle++;
+ log->l_tail_lsn = INT_GET(rhead->h_tail_lsn, ARCH_CONVERT);
+ log->l_last_sync_lsn = INT_GET(rhead->h_lsn, ARCH_CONVERT);
+ log->l_grant_reserve_cycle = log->l_curr_cycle;
+ log->l_grant_reserve_bytes = BBTOB(log->l_curr_block);
+ log->l_grant_write_cycle = log->l_curr_cycle;
+ log->l_grant_write_bytes = BBTOB(log->l_curr_block);
+
+ /*
+ * Look for unmount record. If we find it, then we know there
+ * was a clean unmount. Since 'i' could be the last block in
+ * the physical log, we convert to a log block before comparing
+ * to the head_blk.
+ *
+ * Save the current tail lsn to use to pass to
+ * xlog_clear_stale_blocks() below. We won't want to clear the
+ * unmount record if there is one, so we pass the lsn of the
+ * unmount record rather than the block after it.
+ */
+ if (XFS_SB_VERSION_HASLOGV2(&log->l_mp->m_sb)) {
+ int h_size = INT_GET(rhead->h_size, ARCH_CONVERT);
+ int h_version = INT_GET(rhead->h_version, ARCH_CONVERT);
+
+ if ((h_version & XLOG_VERSION_2) &&
+ (h_size > XLOG_HEADER_CYCLE_SIZE)) {
+ hblks = h_size / XLOG_HEADER_CYCLE_SIZE;
+ if (h_size % XLOG_HEADER_CYCLE_SIZE)
+ hblks++;
+ } else {
+ hblks = 1;
+ }
+ } else {
+ hblks = 1;
+ }
+ after_umount_blk = (i + hblks + (int)
+ BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT))) % log->l_logBBsize;
+ tail_lsn = log->l_tail_lsn;
+ if (*head_blk == after_umount_blk &&
+ INT_GET(rhead->h_num_logops, ARCH_CONVERT) == 1) {
+ umount_data_blk = (i + hblks) % log->l_logBBsize;
+ if ((error = xlog_bread(log, umount_data_blk, 1, bp))) {
+ goto bread_err;
+ }
+ offset = xlog_align(log, umount_data_blk, 1, bp);
+ op_head = (xlog_op_header_t *)offset;
+ if (op_head->oh_flags & XLOG_UNMOUNT_TRANS) {
+ /*
+ * Set tail and last sync so that newly written
+ * log records will point recovery to after the
+ * current unmount record.
+ */
+ ASSIGN_ANY_LSN(log->l_tail_lsn, log->l_curr_cycle,
+ after_umount_blk, ARCH_NOCONVERT);
+ ASSIGN_ANY_LSN(log->l_last_sync_lsn, log->l_curr_cycle,
+ after_umount_blk, ARCH_NOCONVERT);
+ *tail_blk = after_umount_blk;
+ }
+ }
+
+ /*
+ * Make sure that there are no blocks in front of the head
+ * with the same cycle number as the head. This can happen
+ * because we allow multiple outstanding log writes concurrently,
+ * and the later writes might make it out before earlier ones.
+ *
+ * We use the lsn from before modifying it so that we'll never
+ * overwrite the unmount record after a clean unmount.
+ *
+ * Do this only if we are going to recover the filesystem
+ *
+ * NOTE: This used to say "if (!readonly)"
+ * However on Linux, we can & do recover a read-only filesystem.
+ * We only skip recovery if NORECOVERY is specified on mount,
+ * in which case we would not be here.
+ *
+ * But... if the -device- itself is readonly, just skip this.
+ * We can't recover this device anyway, so it won't matter.
+ */
+ if (!xfs_readonly_buftarg(log->l_mp->m_logdev_targp)) {
+ error = xlog_clear_stale_blocks(log, tail_lsn);
+ }
+
+bread_err:
+exit:
+ xlog_put_bp(bp);
+
+ if (error)
+ xlog_warn("XFS: failed to locate log tail");
+ return error;
+}
+
+/*
+ * Is the log zeroed at all?
+ *
+ * The last binary search should be changed to perform an X block read
+ * once X becomes small enough. You can then search linearly through
+ * the X blocks. This will cut down on the number of reads we need to do.
+ *
+ * If the log is partially zeroed, this routine will pass back the blkno
+ * of the first block with cycle number 0. It won't have a complete LR
+ * preceding it.
+ *
+ * Return:
+ * 0 => the log is completely written to
+ * -1 => use *blk_no as the first block of the log
+ * >0 => error has occurred
+ */
+int
+xlog_find_zeroed(
+ xlog_t *log,
+ xfs_daddr_t *blk_no)
+{
+ xfs_buf_t *bp;
+ xfs_caddr_t offset;
+ uint first_cycle, last_cycle;
+ xfs_daddr_t new_blk, last_blk, start_blk;
+ xfs_daddr_t num_scan_bblks;
+ int error, log_bbnum = log->l_logBBsize;
+
+ /* check totally zeroed log */
+ bp = xlog_get_bp(log, 1);
+ if (!bp)
+ return ENOMEM;
+ if ((error = xlog_bread(log, 0, 1, bp)))
+ goto bp_err;
+ offset = xlog_align(log, 0, 1, bp);
+ first_cycle = GET_CYCLE(offset, ARCH_CONVERT);
+ if (first_cycle == 0) { /* completely zeroed log */
+ *blk_no = 0;
+ xlog_put_bp(bp);
+ return -1;
+ }
+
+ /* check partially zeroed log */
+ if ((error = xlog_bread(log, log_bbnum-1, 1, bp)))
+ goto bp_err;
+ offset = xlog_align(log, log_bbnum-1, 1, bp);
+ last_cycle = GET_CYCLE(offset, ARCH_CONVERT);
+ if (last_cycle != 0) { /* log completely written to */
+ xlog_put_bp(bp);
+ return 0;
+ } else if (first_cycle != 1) {
+ /*
+ * If the cycle of the last block is zero, the cycle of
+ * the first block must be 1. If it's not, maybe we're
+ * not looking at a log... Bail out.
+ */
+ xlog_warn("XFS: Log inconsistent or not a log (last==0, first!=1)");
+ return XFS_ERROR(EINVAL);
+ }
+
+ /* we have a partially zeroed log */
+ last_blk = log_bbnum-1;
+ if ((error = xlog_find_cycle_start(log, bp, 0, &last_blk, 0)))
+ goto bp_err;
+
+ /*
+ * Validate the answer. Because there is no way to guarantee that
+ * the entire log is made up of log records which are the same size,
+ * we scan over the defined maximum blocks. At this point, the maximum
+ * is not chosen to mean anything special. XXXmiken
+ */
+ num_scan_bblks = XLOG_TOTAL_REC_SHIFT(log);
+ ASSERT(num_scan_bblks <= INT_MAX);
+
+ if (last_blk < num_scan_bblks)
+ num_scan_bblks = last_blk;
+ start_blk = last_blk - num_scan_bblks;
+
+ /*
+ * We search for any instances of cycle number 0 that occur before
+ * our current estimate of the head. What we're trying to detect is
+ * 1 ... | 0 | 1 | 0...
+ * ^ binary search ends here
+ */
+ if ((error = xlog_find_verify_cycle(log, start_blk,
+ (int)num_scan_bblks, 0, &new_blk)))
+ goto bp_err;
+ if (new_blk != -1)
+ last_blk = new_blk;
+
+ /*
+ * Potentially backup over partial log record write. We don't need
+ * to search the end of the log because we know it is zero.
+ */
+ if ((error = xlog_find_verify_log_record(log, start_blk,
+ &last_blk, 0)) == -1) {
+ error = XFS_ERROR(EIO);
+ goto bp_err;
+ } else if (error)
+ goto bp_err;
+
+ *blk_no = last_blk;
+bp_err:
+ xlog_put_bp(bp);
+ if (error)
+ return error;
+ return -1;
+}
+
+/*
+ * These are simple subroutines used by xlog_clear_stale_blocks() below
+ * to initialize a buffer full of empty log record headers and write
+ * them into the log.
+ */
+STATIC void
+xlog_add_record(
+ xlog_t *log,
+ xfs_caddr_t buf,
+ int cycle,
+ int block,
+ int tail_cycle,
+ int tail_block)
+{
+ xlog_rec_header_t *recp = (xlog_rec_header_t *)buf;
+
+ memset(buf, 0, BBSIZE);
+ INT_SET(recp->h_magicno, ARCH_CONVERT, XLOG_HEADER_MAGIC_NUM);
+ INT_SET(recp->h_cycle, ARCH_CONVERT, cycle);
+ INT_SET(recp->h_version, ARCH_CONVERT,
+ XFS_SB_VERSION_HASLOGV2(&log->l_mp->m_sb) ? 2 : 1);
+ ASSIGN_ANY_LSN(recp->h_lsn, cycle, block, ARCH_CONVERT);
+ ASSIGN_ANY_LSN(recp->h_tail_lsn, tail_cycle, tail_block, ARCH_CONVERT);
+ INT_SET(recp->h_fmt, ARCH_CONVERT, XLOG_FMT);
+ memcpy(&recp->h_fs_uuid, &log->l_mp->m_sb.sb_uuid, sizeof(uuid_t));
+}
+
+STATIC int
+xlog_write_log_records(
+ xlog_t *log,
+ int cycle,
+ int start_block,
+ int blocks,
+ int tail_cycle,
+ int tail_block)
+{
+ xfs_caddr_t offset;
+ xfs_buf_t *bp;
+ int balign, ealign;
+ int sectbb = XLOG_SECTOR_ROUNDUP_BBCOUNT(log, 1);
+ int end_block = start_block + blocks;
+ int bufblks;
+ int error = 0;
+ int i, j = 0;
+
+ bufblks = 1 << ffs(blocks);
+ while (!(bp = xlog_get_bp(log, bufblks))) {
+ bufblks >>= 1;
+ if (bufblks <= log->l_sectbb_log)
+ return ENOMEM;
+ }
+
+ /* We may need to do a read at the start to fill in part of
+ * the buffer in the starting sector not covered by the first
+ * write below.
+ */
+ balign = XLOG_SECTOR_ROUNDDOWN_BLKNO(log, start_block);
+ if (balign != start_block) {
+ if ((error = xlog_bread(log, start_block, 1, bp))) {
+ xlog_put_bp(bp);
+ return error;
+ }
+ j = start_block - balign;
+ }
+
+ for (i = start_block; i < end_block; i += bufblks) {
+ int bcount, endcount;
+
+ bcount = min(bufblks, end_block - start_block);
+ endcount = bcount - j;
+
+ /* We may need to do a read at the end to fill in part of
+ * the buffer in the final sector not covered by the write.
+ * If this is the same sector as the above read, skip it.
+ */
+ ealign = XLOG_SECTOR_ROUNDDOWN_BLKNO(log, end_block);
+ if (j == 0 && (start_block + endcount > ealign)) {
+ offset = XFS_BUF_PTR(bp);
+ balign = BBTOB(ealign - start_block);
+ XFS_BUF_SET_PTR(bp, offset + balign, BBTOB(sectbb));
+ if ((error = xlog_bread(log, ealign, sectbb, bp)))
+ break;
+ XFS_BUF_SET_PTR(bp, offset, bufblks);
+ }
+
+ offset = xlog_align(log, start_block, endcount, bp);
+ for (; j < endcount; j++) {
+ xlog_add_record(log, offset, cycle, i+j,
+ tail_cycle, tail_block);
+ offset += BBSIZE;
+ }
+ error = xlog_bwrite(log, start_block, endcount, bp);
+ if (error)
+ break;
+ start_block += endcount;
+ j = 0;
+ }
+ xlog_put_bp(bp);
+ return error;
+}
+
+/*
+ * This routine is called to blow away any incomplete log writes out
+ * in front of the log head. We do this so that we won't become confused
+ * if we come up, write only a little bit more, and then crash again.
+ * If we leave the partial log records out there, this situation could
+ * cause us to think those partial writes are valid blocks since they
+ * have the current cycle number. We get rid of them by overwriting them
+ * with empty log records with the old cycle number rather than the
+ * current one.
+ *
+ * The tail lsn is passed in rather than taken from
+ * the log so that we will not write over the unmount record after a
+ * clean unmount in a 512 block log. Doing so would leave the log without
+ * any valid log records in it until a new one was written. If we crashed
+ * during that time we would not be able to recover.
+ */
+STATIC int
+xlog_clear_stale_blocks(
+ xlog_t *log,
+ xfs_lsn_t tail_lsn)
+{
+ int tail_cycle, head_cycle;
+ int tail_block, head_block;
+ int tail_distance, max_distance;
+ int distance;
+ int error;
+
+ tail_cycle = CYCLE_LSN(tail_lsn, ARCH_NOCONVERT);
+ tail_block = BLOCK_LSN(tail_lsn, ARCH_NOCONVERT);
+ head_cycle = log->l_curr_cycle;
+ head_block = log->l_curr_block;
+
+ /*
+ * Figure out the distance between the new head of the log
+ * and the tail. We want to write over any blocks beyond the
+ * head that we may have written just before the crash, but
+ * we don't want to overwrite the tail of the log.
+ */
+ if (head_cycle == tail_cycle) {
+ /*
+ * The tail is behind the head in the physical log,
+ * so the distance from the head to the tail is the
+ * distance from the head to the end of the log plus
+ * the distance from the beginning of the log to the
+ * tail.
+ */
+ if (unlikely(head_block < tail_block || head_block >= log->l_logBBsize)) {
+ XFS_ERROR_REPORT("xlog_clear_stale_blocks(1)",
+ XFS_ERRLEVEL_LOW, log->l_mp);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ tail_distance = tail_block + (log->l_logBBsize - head_block);
+ } else {
+ /*
+ * The head is behind the tail in the physical log,
+ * so the distance from the head to the tail is just
+ * the tail block minus the head block.
+ */
+ if (unlikely(head_block >= tail_block || head_cycle != (tail_cycle + 1))){
+ XFS_ERROR_REPORT("xlog_clear_stale_blocks(2)",
+ XFS_ERRLEVEL_LOW, log->l_mp);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ tail_distance = tail_block - head_block;
+ }
+
+ /*
+ * If the head is right up against the tail, we can't clear
+ * anything.
+ */
+ if (tail_distance <= 0) {
+ ASSERT(tail_distance == 0);
+ return 0;
+ }
+
+ max_distance = XLOG_TOTAL_REC_SHIFT(log);
+ /*
+ * Take the smaller of the maximum amount of outstanding I/O
+ * we could have and the distance to the tail to clear out.
+ * We take the smaller so that we don't overwrite the tail and
+ * we don't waste all day writing from the head to the tail
+ * for no reason.
+ */
+ max_distance = MIN(max_distance, tail_distance);
+
+ if ((head_block + max_distance) <= log->l_logBBsize) {
+ /*
+ * We can stomp all the blocks we need to without
+ * wrapping around the end of the log. Just do it
+ * in a single write. Use the cycle number of the
+ * current cycle minus one so that the log will look like:
+ * n ... | n - 1 ...
+ */
+ error = xlog_write_log_records(log, (head_cycle - 1),
+ head_block, max_distance, tail_cycle,
+ tail_block);
+ if (error)
+ return error;
+ } else {
+ /*
+ * We need to wrap around the end of the physical log in
+ * order to clear all the blocks. Do it in two separate
+ * I/Os. The first write should be from the head to the
+ * end of the physical log, and it should use the current
+ * cycle number minus one just like above.
+ */
+ distance = log->l_logBBsize - head_block;
+ error = xlog_write_log_records(log, (head_cycle - 1),
+ head_block, distance, tail_cycle,
+ tail_block);
+
+ if (error)
+ return error;
+
+ /*
+ * Now write the blocks at the start of the physical log.
+ * This writes the remainder of the blocks we want to clear.
+ * It uses the current cycle number since we're now on the
+ * same cycle as the head so that we get:
+ * n ... n ... | n - 1 ...
+ * ^^^^^ blocks we're writing
+ */
+ distance = max_distance - (log->l_logBBsize - head_block);
+ error = xlog_write_log_records(log, head_cycle, 0, distance,
+ tail_cycle, tail_block);
+ if (error)
+ return error;
+ }
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * Log recover routines
+ *
+ ******************************************************************************
+ */
+
+STATIC xlog_recover_t *
+xlog_recover_find_tid(
+ xlog_recover_t *q,
+ xlog_tid_t tid)
+{
+ xlog_recover_t *p = q;
+
+ while (p != NULL) {
+ if (p->r_log_tid == tid)
+ break;
+ p = p->r_next;
+ }
+ return p;
+}
+
+STATIC void
+xlog_recover_put_hashq(
+ xlog_recover_t **q,
+ xlog_recover_t *trans)
+{
+ trans->r_next = *q;
+ *q = trans;
+}
+
+STATIC void
+xlog_recover_add_item(
+ xlog_recover_item_t **itemq)
+{
+ xlog_recover_item_t *item;
+
+ item = kmem_zalloc(sizeof(xlog_recover_item_t), KM_SLEEP);
+ xlog_recover_insert_item_backq(itemq, item);
+}
+
+STATIC int
+xlog_recover_add_to_cont_trans(
+ xlog_recover_t *trans,
+ xfs_caddr_t dp,
+ int len)
+{
+ xlog_recover_item_t *item;
+ xfs_caddr_t ptr, old_ptr;
+ int old_len;
+
+ item = trans->r_itemq;
+ if (item == 0) {
+ /* finish copying rest of trans header */
+ xlog_recover_add_item(&trans->r_itemq);
+ ptr = (xfs_caddr_t) &trans->r_theader +
+ sizeof(xfs_trans_header_t) - len;
+ memcpy(ptr, dp, len); /* d, s, l */
+ return 0;
+ }
+ item = item->ri_prev;
+
+ old_ptr = item->ri_buf[item->ri_cnt-1].i_addr;
+ old_len = item->ri_buf[item->ri_cnt-1].i_len;
+
+ ptr = kmem_realloc(old_ptr, len+old_len, old_len, 0);
+ memcpy(&ptr[old_len], dp, len); /* d, s, l */
+ item->ri_buf[item->ri_cnt-1].i_len += len;
+ item->ri_buf[item->ri_cnt-1].i_addr = ptr;
+ return 0;
+}
+
+/*
+ * The next region to add is the start of a new region. It could be
+ * a whole region or it could be the first part of a new region. Because
+ * of this, the assumption here is that the type and size fields of all
+ * format structures fit into the first 32 bits of the structure.
+ *
+ * This works because all regions must be 32 bit aligned. Therefore, we
+ * either have both fields or we have neither field. In the case we have
+ * neither field, the data part of the region is zero length. We only have
+ * a log_op_header and can throw away the header since a new one will appear
+ * later. If we have at least 4 bytes, then we can determine how many regions
+ * will appear in the current log item.
+ */
+STATIC int
+xlog_recover_add_to_trans(
+ xlog_recover_t *trans,
+ xfs_caddr_t dp,
+ int len)
+{
+ xfs_inode_log_format_t *in_f; /* any will do */
+ xlog_recover_item_t *item;
+ xfs_caddr_t ptr;
+
+ if (!len)
+ return 0;
+ item = trans->r_itemq;
+ if (item == 0) {
+ ASSERT(*(uint *)dp == XFS_TRANS_HEADER_MAGIC);
+ if (len == sizeof(xfs_trans_header_t))
+ xlog_recover_add_item(&trans->r_itemq);
+ memcpy(&trans->r_theader, dp, len); /* d, s, l */
+ return 0;
+ }
+
+ ptr = kmem_alloc(len, KM_SLEEP);
+ memcpy(ptr, dp, len);
+ in_f = (xfs_inode_log_format_t *)ptr;
+
+ if (item->ri_prev->ri_total != 0 &&
+ item->ri_prev->ri_total == item->ri_prev->ri_cnt) {
+ xlog_recover_add_item(&trans->r_itemq);
+ }
+ item = trans->r_itemq;
+ item = item->ri_prev;
+
+ if (item->ri_total == 0) { /* first region to be added */
+ item->ri_total = in_f->ilf_size;
+ ASSERT(item->ri_total <= XLOG_MAX_REGIONS_IN_ITEM);
+ item->ri_buf = kmem_zalloc((item->ri_total *
+ sizeof(xfs_log_iovec_t)), KM_SLEEP);
+ }
+ ASSERT(item->ri_total > item->ri_cnt);
+ /* Description region is ri_buf[0] */
+ item->ri_buf[item->ri_cnt].i_addr = ptr;
+ item->ri_buf[item->ri_cnt].i_len = len;
+ item->ri_cnt++;
+ return 0;
+}
+
+STATIC void
+xlog_recover_new_tid(
+ xlog_recover_t **q,
+ xlog_tid_t tid,
+ xfs_lsn_t lsn)
+{
+ xlog_recover_t *trans;
+
+ trans = kmem_zalloc(sizeof(xlog_recover_t), KM_SLEEP);
+ trans->r_log_tid = tid;
+ trans->r_lsn = lsn;
+ xlog_recover_put_hashq(q, trans);
+}
+
+STATIC int
+xlog_recover_unlink_tid(
+ xlog_recover_t **q,
+ xlog_recover_t *trans)
+{
+ xlog_recover_t *tp;
+ int found = 0;
+
+ ASSERT(trans != 0);
+ if (trans == *q) {
+ *q = (*q)->r_next;
+ } else {
+ tp = *q;
+ while (tp != 0) {
+ if (tp->r_next == trans) {
+ found = 1;
+ break;
+ }
+ tp = tp->r_next;
+ }
+ if (!found) {
+ xlog_warn(
+ "XFS: xlog_recover_unlink_tid: trans not found");
+ ASSERT(0);
+ return XFS_ERROR(EIO);
+ }
+ tp->r_next = tp->r_next->r_next;
+ }
+ return 0;
+}
+
+STATIC void
+xlog_recover_insert_item_backq(
+ xlog_recover_item_t **q,
+ xlog_recover_item_t *item)
+{
+ if (*q == 0) {
+ item->ri_prev = item->ri_next = item;
+ *q = item;
+ } else {
+ item->ri_next = *q;
+ item->ri_prev = (*q)->ri_prev;
+ (*q)->ri_prev = item;
+ item->ri_prev->ri_next = item;
+ }
+}
+
+STATIC void
+xlog_recover_insert_item_frontq(
+ xlog_recover_item_t **q,
+ xlog_recover_item_t *item)
+{
+ xlog_recover_insert_item_backq(q, item);
+ *q = item;
+}
+
+STATIC int
+xlog_recover_reorder_trans(
+ xlog_t *log,
+ xlog_recover_t *trans)
+{
+ xlog_recover_item_t *first_item, *itemq, *itemq_next;
+ xfs_buf_log_format_t *buf_f;
+ xfs_buf_log_format_v1_t *obuf_f;
+ ushort flags = 0;
+
+ first_item = itemq = trans->r_itemq;
+ trans->r_itemq = NULL;
+ do {
+ itemq_next = itemq->ri_next;
+ buf_f = (xfs_buf_log_format_t *)itemq->ri_buf[0].i_addr;
+ switch (ITEM_TYPE(itemq)) {
+ case XFS_LI_BUF:
+ flags = buf_f->blf_flags;
+ break;
+ case XFS_LI_6_1_BUF:
+ case XFS_LI_5_3_BUF:
+ obuf_f = (xfs_buf_log_format_v1_t*)buf_f;
+ flags = obuf_f->blf_flags;
+ break;
+ }
+
+ switch (ITEM_TYPE(itemq)) {
+ case XFS_LI_BUF:
+ case XFS_LI_6_1_BUF:
+ case XFS_LI_5_3_BUF:
+ if (!(flags & XFS_BLI_CANCEL)) {
+ xlog_recover_insert_item_frontq(&trans->r_itemq,
+ itemq);
+ break;
+ }
+ case XFS_LI_INODE:
+ case XFS_LI_6_1_INODE:
+ case XFS_LI_5_3_INODE:
+ case XFS_LI_DQUOT:
+ case XFS_LI_QUOTAOFF:
+ case XFS_LI_EFD:
+ case XFS_LI_EFI:
+ xlog_recover_insert_item_backq(&trans->r_itemq, itemq);
+ break;
+ default:
+ xlog_warn(
+ "XFS: xlog_recover_reorder_trans: unrecognized type of log operation");
+ ASSERT(0);
+ return XFS_ERROR(EIO);
+ }
+ itemq = itemq_next;
+ } while (first_item != itemq);
+ return 0;
+}
+
+/*
+ * Build up the table of buf cancel records so that we don't replay
+ * cancelled data in the second pass. For buffer records that are
+ * not cancel records, there is nothing to do here so we just return.
+ *
+ * If we get a cancel record which is already in the table, this indicates
+ * that the buffer was cancelled multiple times. In order to ensure
+ * that during pass 2 we keep the record in the table until we reach its
+ * last occurrence in the log, we keep a reference count in the cancel
+ * record in the table to tell us how many times we expect to see this
+ * record during the second pass.
+ */
+STATIC void
+xlog_recover_do_buffer_pass1(
+ xlog_t *log,
+ xfs_buf_log_format_t *buf_f)
+{
+ xfs_buf_cancel_t *bcp;
+ xfs_buf_cancel_t *nextp;
+ xfs_buf_cancel_t *prevp;
+ xfs_buf_cancel_t **bucket;
+ xfs_buf_log_format_v1_t *obuf_f;
+ xfs_daddr_t blkno = 0;
+ uint len = 0;
+ ushort flags = 0;
+
+ switch (buf_f->blf_type) {
+ case XFS_LI_BUF:
+ blkno = buf_f->blf_blkno;
+ len = buf_f->blf_len;
+ flags = buf_f->blf_flags;
+ break;
+ case XFS_LI_6_1_BUF:
+ case XFS_LI_5_3_BUF:
+ obuf_f = (xfs_buf_log_format_v1_t*)buf_f;
+ blkno = (xfs_daddr_t) obuf_f->blf_blkno;
+ len = obuf_f->blf_len;
+ flags = obuf_f->blf_flags;
+ break;
+ }
+
+ /*
+ * If this isn't a cancel buffer item, then just return.
+ */
+ if (!(flags & XFS_BLI_CANCEL))
+ return;
+
+ /*
+ * Insert an xfs_buf_cancel record into the hash table of
+ * them. If there is already an identical record, bump
+ * its reference count.
+ */
+ bucket = &log->l_buf_cancel_table[(__uint64_t)blkno %
+ XLOG_BC_TABLE_SIZE];
+ /*
+ * If the hash bucket is empty then just insert a new record into
+ * the bucket.
+ */
+ if (*bucket == NULL) {
+ bcp = (xfs_buf_cancel_t *)kmem_alloc(sizeof(xfs_buf_cancel_t),
+ KM_SLEEP);
+ bcp->bc_blkno = blkno;
+ bcp->bc_len = len;
+ bcp->bc_refcount = 1;
+ bcp->bc_next = NULL;
+ *bucket = bcp;
+ return;
+ }
+
+ /*
+ * The hash bucket is not empty, so search for duplicates of our
+ * record. If we find one them just bump its refcount. If not
+ * then add us at the end of the list.
+ */
+ prevp = NULL;
+ nextp = *bucket;
+ while (nextp != NULL) {
+ if (nextp->bc_blkno == blkno && nextp->bc_len == len) {
+ nextp->bc_refcount++;
+ return;
+ }
+ prevp = nextp;
+ nextp = nextp->bc_next;
+ }
+ ASSERT(prevp != NULL);
+ bcp = (xfs_buf_cancel_t *)kmem_alloc(sizeof(xfs_buf_cancel_t),
+ KM_SLEEP);
+ bcp->bc_blkno = blkno;
+ bcp->bc_len = len;
+ bcp->bc_refcount = 1;
+ bcp->bc_next = NULL;
+ prevp->bc_next = bcp;
+}
+
+/*
+ * Check to see whether the buffer being recovered has a corresponding
+ * entry in the buffer cancel record table. If it does then return 1
+ * so that it will be cancelled, otherwise return 0. If the buffer is
+ * actually a buffer cancel item (XFS_BLI_CANCEL is set), then decrement
+ * the refcount on the entry in the table and remove it from the table
+ * if this is the last reference.
+ *
+ * We remove the cancel record from the table when we encounter its
+ * last occurrence in the log so that if the same buffer is re-used
+ * again after its last cancellation we actually replay the changes
+ * made at that point.
+ */
+STATIC int
+xlog_check_buffer_cancelled(
+ xlog_t *log,
+ xfs_daddr_t blkno,
+ uint len,
+ ushort flags)
+{
+ xfs_buf_cancel_t *bcp;
+ xfs_buf_cancel_t *prevp;
+ xfs_buf_cancel_t **bucket;
+
+ if (log->l_buf_cancel_table == NULL) {
+ /*
+ * There is nothing in the table built in pass one,
+ * so this buffer must not be cancelled.
+ */
+ ASSERT(!(flags & XFS_BLI_CANCEL));
+ return 0;
+ }
+
+ bucket = &log->l_buf_cancel_table[(__uint64_t)blkno %
+ XLOG_BC_TABLE_SIZE];
+ bcp = *bucket;
+ if (bcp == NULL) {
+ /*
+ * There is no corresponding entry in the table built
+ * in pass one, so this buffer has not been cancelled.
+ */
+ ASSERT(!(flags & XFS_BLI_CANCEL));
+ return 0;
+ }
+
+ /*
+ * Search for an entry in the buffer cancel table that
+ * matches our buffer.
+ */
+ prevp = NULL;
+ while (bcp != NULL) {
+ if (bcp->bc_blkno == blkno && bcp->bc_len == len) {
+ /*
+ * We've go a match, so return 1 so that the
+ * recovery of this buffer is cancelled.
+ * If this buffer is actually a buffer cancel
+ * log item, then decrement the refcount on the
+ * one in the table and remove it if this is the
+ * last reference.
+ */
+ if (flags & XFS_BLI_CANCEL) {
+ bcp->bc_refcount--;
+ if (bcp->bc_refcount == 0) {
+ if (prevp == NULL) {
+ *bucket = bcp->bc_next;
+ } else {
+ prevp->bc_next = bcp->bc_next;
+ }
+ kmem_free(bcp,
+ sizeof(xfs_buf_cancel_t));
+ }
+ }
+ return 1;
+ }
+ prevp = bcp;
+ bcp = bcp->bc_next;
+ }
+ /*
+ * We didn't find a corresponding entry in the table, so
+ * return 0 so that the buffer is NOT cancelled.
+ */
+ ASSERT(!(flags & XFS_BLI_CANCEL));
+ return 0;
+}
+
+STATIC int
+xlog_recover_do_buffer_pass2(
+ xlog_t *log,
+ xfs_buf_log_format_t *buf_f)
+{
+ xfs_buf_log_format_v1_t *obuf_f;
+ xfs_daddr_t blkno = 0;
+ ushort flags = 0;
+ uint len = 0;
+
+ switch (buf_f->blf_type) {
+ case XFS_LI_BUF:
+ blkno = buf_f->blf_blkno;
+ flags = buf_f->blf_flags;
+ len = buf_f->blf_len;
+ break;
+ case XFS_LI_6_1_BUF:
+ case XFS_LI_5_3_BUF:
+ obuf_f = (xfs_buf_log_format_v1_t*)buf_f;
+ blkno = (xfs_daddr_t) obuf_f->blf_blkno;
+ flags = obuf_f->blf_flags;
+ len = (xfs_daddr_t) obuf_f->blf_len;
+ break;
+ }
+
+ return xlog_check_buffer_cancelled(log, blkno, len, flags);
+}
+
+/*
+ * Perform recovery for a buffer full of inodes. In these buffers,
+ * the only data which should be recovered is that which corresponds
+ * to the di_next_unlinked pointers in the on disk inode structures.
+ * The rest of the data for the inodes is always logged through the
+ * inodes themselves rather than the inode buffer and is recovered
+ * in xlog_recover_do_inode_trans().
+ *
+ * The only time when buffers full of inodes are fully recovered is
+ * when the buffer is full of newly allocated inodes. In this case
+ * the buffer will not be marked as an inode buffer and so will be
+ * sent to xlog_recover_do_reg_buffer() below during recovery.
+ */
+STATIC int
+xlog_recover_do_inode_buffer(
+ xfs_mount_t *mp,
+ xlog_recover_item_t *item,
+ xfs_buf_t *bp,
+ xfs_buf_log_format_t *buf_f)
+{
+ int i;
+ int item_index;
+ int bit;
+ int nbits;
+ int reg_buf_offset;
+ int reg_buf_bytes;
+ int next_unlinked_offset;
+ int inodes_per_buf;
+ xfs_agino_t *logged_nextp;
+ xfs_agino_t *buffer_nextp;
+ xfs_buf_log_format_v1_t *obuf_f;
+ unsigned int *data_map = NULL;
+ unsigned int map_size = 0;
+
+ switch (buf_f->blf_type) {
+ case XFS_LI_BUF:
+ data_map = buf_f->blf_data_map;
+ map_size = buf_f->blf_map_size;
+ break;
+ case XFS_LI_6_1_BUF:
+ case XFS_LI_5_3_BUF:
+ obuf_f = (xfs_buf_log_format_v1_t*)buf_f;
+ data_map = obuf_f->blf_data_map;
+ map_size = obuf_f->blf_map_size;
+ break;
+ }
+ /*
+ * Set the variables corresponding to the current region to
+ * 0 so that we'll initialize them on the first pass through
+ * the loop.
+ */
+ reg_buf_offset = 0;
+ reg_buf_bytes = 0;
+ bit = 0;
+ nbits = 0;
+ item_index = 0;
+ inodes_per_buf = XFS_BUF_COUNT(bp) >> mp->m_sb.sb_inodelog;
+ for (i = 0; i < inodes_per_buf; i++) {
+ next_unlinked_offset = (i * mp->m_sb.sb_inodesize) +
+ offsetof(xfs_dinode_t, di_next_unlinked);
+
+ while (next_unlinked_offset >=
+ (reg_buf_offset + reg_buf_bytes)) {
+ /*
+ * The next di_next_unlinked field is beyond
+ * the current logged region. Find the next
+ * logged region that contains or is beyond
+ * the current di_next_unlinked field.
+ */
+ bit += nbits;
+ bit = xfs_next_bit(data_map, map_size, bit);
+
+ /*
+ * If there are no more logged regions in the
+ * buffer, then we're done.
+ */
+ if (bit == -1) {
+ return 0;
+ }
+
+ nbits = xfs_contig_bits(data_map, map_size,
+ bit);
+ ASSERT(nbits > 0);
+ reg_buf_offset = bit << XFS_BLI_SHIFT;
+ reg_buf_bytes = nbits << XFS_BLI_SHIFT;
+ item_index++;
+ }
+
+ /*
+ * If the current logged region starts after the current
+ * di_next_unlinked field, then move on to the next
+ * di_next_unlinked field.
+ */
+ if (next_unlinked_offset < reg_buf_offset) {
+ continue;
+ }
+
+ ASSERT(item->ri_buf[item_index].i_addr != NULL);
+ ASSERT((item->ri_buf[item_index].i_len % XFS_BLI_CHUNK) == 0);
+ ASSERT((reg_buf_offset + reg_buf_bytes) <= XFS_BUF_COUNT(bp));
+
+ /*
+ * The current logged region contains a copy of the
+ * current di_next_unlinked field. Extract its value
+ * and copy it to the buffer copy.
+ */
+ logged_nextp = (xfs_agino_t *)
+ ((char *)(item->ri_buf[item_index].i_addr) +
+ (next_unlinked_offset - reg_buf_offset));
+ if (unlikely(*logged_nextp == 0)) {
+ xfs_fs_cmn_err(CE_ALERT, mp,
+ "bad inode buffer log record (ptr = 0x%p, bp = 0x%p). XFS trying to replay bad (0) inode di_next_unlinked field",
+ item, bp);
+ XFS_ERROR_REPORT("xlog_recover_do_inode_buf",
+ XFS_ERRLEVEL_LOW, mp);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+
+ buffer_nextp = (xfs_agino_t *)xfs_buf_offset(bp,
+ next_unlinked_offset);
+ INT_SET(*buffer_nextp, ARCH_CONVERT, *logged_nextp);
+ }
+
+ return 0;
+}
+
+/*
+ * Perform a 'normal' buffer recovery. Each logged region of the
+ * buffer should be copied over the corresponding region in the
+ * given buffer. The bitmap in the buf log format structure indicates
+ * where to place the logged data.
+ */
+/*ARGSUSED*/
+STATIC void
+xlog_recover_do_reg_buffer(
+ xfs_mount_t *mp,
+ xlog_recover_item_t *item,
+ xfs_buf_t *bp,
+ xfs_buf_log_format_t *buf_f)
+{
+ int i;
+ int bit;
+ int nbits;
+ xfs_buf_log_format_v1_t *obuf_f;
+ unsigned int *data_map = NULL;
+ unsigned int map_size = 0;
+ int error;
+
+ switch (buf_f->blf_type) {
+ case XFS_LI_BUF:
+ data_map = buf_f->blf_data_map;
+ map_size = buf_f->blf_map_size;
+ break;
+ case XFS_LI_6_1_BUF:
+ case XFS_LI_5_3_BUF:
+ obuf_f = (xfs_buf_log_format_v1_t*)buf_f;
+ data_map = obuf_f->blf_data_map;
+ map_size = obuf_f->blf_map_size;
+ break;
+ }
+ bit = 0;
+ i = 1; /* 0 is the buf format structure */
+ while (1) {
+ bit = xfs_next_bit(data_map, map_size, bit);
+ if (bit == -1)
+ break;
+ nbits = xfs_contig_bits(data_map, map_size, bit);
+ ASSERT(nbits > 0);
+ ASSERT(item->ri_buf[i].i_addr != 0);
+ ASSERT(item->ri_buf[i].i_len % XFS_BLI_CHUNK == 0);
+ ASSERT(XFS_BUF_COUNT(bp) >=
+ ((uint)bit << XFS_BLI_SHIFT)+(nbits<<XFS_BLI_SHIFT));
+
+ /*
+ * Do a sanity check if this is a dquot buffer. Just checking
+ * the first dquot in the buffer should do. XXXThis is
+ * probably a good thing to do for other buf types also.
+ */
+ error = 0;
+ if (buf_f->blf_flags & (XFS_BLI_UDQUOT_BUF|XFS_BLI_GDQUOT_BUF)) {
+ error = xfs_qm_dqcheck((xfs_disk_dquot_t *)
+ item->ri_buf[i].i_addr,
+ -1, 0, XFS_QMOPT_DOWARN,
+ "dquot_buf_recover");
+ }
+ if (!error)
+ memcpy(xfs_buf_offset(bp,
+ (uint)bit << XFS_BLI_SHIFT), /* dest */
+ item->ri_buf[i].i_addr, /* source */
+ nbits<<XFS_BLI_SHIFT); /* length */
+ i++;
+ bit += nbits;
+ }
+
+ /* Shouldn't be any more regions */
+ ASSERT(i == item->ri_total);
+}
+
+/*
+ * Do some primitive error checking on ondisk dquot data structures.
+ */
+int
+xfs_qm_dqcheck(
+ xfs_disk_dquot_t *ddq,
+ xfs_dqid_t id,
+ uint type, /* used only when IO_dorepair is true */
+ uint flags,
+ char *str)
+{
+ xfs_dqblk_t *d = (xfs_dqblk_t *)ddq;
+ int errs = 0;
+
+ /*
+ * We can encounter an uninitialized dquot buffer for 2 reasons:
+ * 1. If we crash while deleting the quotainode(s), and those blks got
+ * used for user data. This is because we take the path of regular
+ * file deletion; however, the size field of quotainodes is never
+ * updated, so all the tricks that we play in itruncate_finish
+ * don't quite matter.
+ *
+ * 2. We don't play the quota buffers when there's a quotaoff logitem.
+ * But the allocation will be replayed so we'll end up with an
+ * uninitialized quota block.
+ *
+ * This is all fine; things are still consistent, and we haven't lost
+ * any quota information. Just don't complain about bad dquot blks.
+ */
+ if (INT_GET(ddq->d_magic, ARCH_CONVERT) != XFS_DQUOT_MAGIC) {
+ if (flags & XFS_QMOPT_DOWARN)
+ cmn_err(CE_ALERT,
+ "%s : XFS dquot ID 0x%x, magic 0x%x != 0x%x",
+ str, id,
+ INT_GET(ddq->d_magic, ARCH_CONVERT), XFS_DQUOT_MAGIC);
+ errs++;
+ }
+ if (INT_GET(ddq->d_version, ARCH_CONVERT) != XFS_DQUOT_VERSION) {
+ if (flags & XFS_QMOPT_DOWARN)
+ cmn_err(CE_ALERT,
+ "%s : XFS dquot ID 0x%x, version 0x%x != 0x%x",
+ str, id,
+ INT_GET(ddq->d_magic, ARCH_CONVERT), XFS_DQUOT_VERSION);
+ errs++;
+ }
+
+ if (INT_GET(ddq->d_flags, ARCH_CONVERT) != XFS_DQ_USER &&
+ INT_GET(ddq->d_flags, ARCH_CONVERT) != XFS_DQ_GROUP) {
+ if (flags & XFS_QMOPT_DOWARN)
+ cmn_err(CE_ALERT,
+ "%s : XFS dquot ID 0x%x, unknown flags 0x%x",
+ str, id, INT_GET(ddq->d_flags, ARCH_CONVERT));
+ errs++;
+ }
+
+ if (id != -1 && id != INT_GET(ddq->d_id, ARCH_CONVERT)) {
+ if (flags & XFS_QMOPT_DOWARN)
+ cmn_err(CE_ALERT,
+ "%s : ondisk-dquot 0x%p, ID mismatch: "
+ "0x%x expected, found id 0x%x",
+ str, ddq, id, INT_GET(ddq->d_id, ARCH_CONVERT));
+ errs++;
+ }
+
+ if (! errs && !INT_ISZERO(ddq->d_id, ARCH_CONVERT)) {
+ if (INT_GET(ddq->d_blk_softlimit, ARCH_CONVERT) &&
+ INT_GET(ddq->d_bcount, ARCH_CONVERT) >=
+ INT_GET(ddq->d_blk_softlimit, ARCH_CONVERT)) {
+ if (INT_ISZERO(ddq->d_btimer, ARCH_CONVERT)) {
+ if (flags & XFS_QMOPT_DOWARN)
+ cmn_err(CE_ALERT,
+ "%s : Dquot ID 0x%x (0x%p) "
+ "BLK TIMER NOT STARTED",
+ str, (int)
+ INT_GET(ddq->d_id, ARCH_CONVERT), ddq);
+ errs++;
+ }
+ }
+ if (INT_GET(ddq->d_ino_softlimit, ARCH_CONVERT) &&
+ INT_GET(ddq->d_icount, ARCH_CONVERT) >=
+ INT_GET(ddq->d_ino_softlimit, ARCH_CONVERT)) {
+ if (INT_ISZERO(ddq->d_itimer, ARCH_CONVERT)) {
+ if (flags & XFS_QMOPT_DOWARN)
+ cmn_err(CE_ALERT,
+ "%s : Dquot ID 0x%x (0x%p) "
+ "INODE TIMER NOT STARTED",
+ str, (int)
+ INT_GET(ddq->d_id, ARCH_CONVERT), ddq);
+ errs++;
+ }
+ }
+ if (INT_GET(ddq->d_rtb_softlimit, ARCH_CONVERT) &&
+ INT_GET(ddq->d_rtbcount, ARCH_CONVERT) >=
+ INT_GET(ddq->d_rtb_softlimit, ARCH_CONVERT)) {
+ if (INT_ISZERO(ddq->d_rtbtimer, ARCH_CONVERT)) {
+ if (flags & XFS_QMOPT_DOWARN)
+ cmn_err(CE_ALERT,
+ "%s : Dquot ID 0x%x (0x%p) "
+ "RTBLK TIMER NOT STARTED",
+ str, (int)
+ INT_GET(ddq->d_id, ARCH_CONVERT), ddq);
+ errs++;
+ }
+ }
+ }
+
+ if (!errs || !(flags & XFS_QMOPT_DQREPAIR))
+ return errs;
+
+ if (flags & XFS_QMOPT_DOWARN)
+ cmn_err(CE_NOTE, "Re-initializing dquot ID 0x%x", id);
+
+ /*
+ * Typically, a repair is only requested by quotacheck.
+ */
+ ASSERT(id != -1);
+ ASSERT(flags & XFS_QMOPT_DQREPAIR);
+ memset(d, 0, sizeof(xfs_dqblk_t));
+ INT_SET(d->dd_diskdq.d_magic, ARCH_CONVERT, XFS_DQUOT_MAGIC);
+ INT_SET(d->dd_diskdq.d_version, ARCH_CONVERT, XFS_DQUOT_VERSION);
+ INT_SET(d->dd_diskdq.d_id, ARCH_CONVERT, id);
+ INT_SET(d->dd_diskdq.d_flags, ARCH_CONVERT, type);
+
+ return errs;
+}
+
+/*
+ * Perform a dquot buffer recovery.
+ * Simple algorithm: if we have found a QUOTAOFF logitem of the same type
+ * (ie. USR or GRP), then just toss this buffer away; don't recover it.
+ * Else, treat it as a regular buffer and do recovery.
+ */
+STATIC void
+xlog_recover_do_dquot_buffer(
+ xfs_mount_t *mp,
+ xlog_t *log,
+ xlog_recover_item_t *item,
+ xfs_buf_t *bp,
+ xfs_buf_log_format_t *buf_f)
+{
+ uint type;
+
+ /*
+ * Filesystems are required to send in quota flags at mount time.
+ */
+ if (mp->m_qflags == 0) {
+ return;
+ }
+
+ type = 0;
+ if (buf_f->blf_flags & XFS_BLI_UDQUOT_BUF)
+ type |= XFS_DQ_USER;
+ if (buf_f->blf_flags & XFS_BLI_GDQUOT_BUF)
+ type |= XFS_DQ_GROUP;
+ /*
+ * This type of quotas was turned off, so ignore this buffer
+ */
+ if (log->l_quotaoffs_flag & type)
+ return;
+
+ xlog_recover_do_reg_buffer(mp, item, bp, buf_f);
+}
+
+/*
+ * This routine replays a modification made to a buffer at runtime.
+ * There are actually two types of buffer, regular and inode, which
+ * are handled differently. Inode buffers are handled differently
+ * in that we only recover a specific set of data from them, namely
+ * the inode di_next_unlinked fields. This is because all other inode
+ * data is actually logged via inode records and any data we replay
+ * here which overlaps that may be stale.
+ *
+ * When meta-data buffers are freed at run time we log a buffer item
+ * with the XFS_BLI_CANCEL bit set to indicate that previous copies
+ * of the buffer in the log should not be replayed at recovery time.
+ * This is so that if the blocks covered by the buffer are reused for
+ * file data before we crash we don't end up replaying old, freed
+ * meta-data into a user's file.
+ *
+ * To handle the cancellation of buffer log items, we make two passes
+ * over the log during recovery. During the first we build a table of
+ * those buffers which have been cancelled, and during the second we
+ * only replay those buffers which do not have corresponding cancel
+ * records in the table. See xlog_recover_do_buffer_pass[1,2] above
+ * for more details on the implementation of the table of cancel records.
+ */
+STATIC int
+xlog_recover_do_buffer_trans(
+ xlog_t *log,
+ xlog_recover_item_t *item,
+ int pass)
+{
+ xfs_buf_log_format_t *buf_f;
+ xfs_buf_log_format_v1_t *obuf_f;
+ xfs_mount_t *mp;
+ xfs_buf_t *bp;
+ int error;
+ int cancel;
+ xfs_daddr_t blkno;
+ int len;
+ ushort flags;
+
+ buf_f = (xfs_buf_log_format_t *)item->ri_buf[0].i_addr;
+
+ if (pass == XLOG_RECOVER_PASS1) {
+ /*
+ * In this pass we're only looking for buf items
+ * with the XFS_BLI_CANCEL bit set.
+ */
+ xlog_recover_do_buffer_pass1(log, buf_f);
+ return 0;
+ } else {
+ /*
+ * In this pass we want to recover all the buffers
+ * which have not been cancelled and are not
+ * cancellation buffers themselves. The routine
+ * we call here will tell us whether or not to
+ * continue with the replay of this buffer.
+ */
+ cancel = xlog_recover_do_buffer_pass2(log, buf_f);
+ if (cancel) {
+ return 0;
+ }
+ }
+ switch (buf_f->blf_type) {
+ case XFS_LI_BUF:
+ blkno = buf_f->blf_blkno;
+ len = buf_f->blf_len;
+ flags = buf_f->blf_flags;
+ break;
+ case XFS_LI_6_1_BUF:
+ case XFS_LI_5_3_BUF:
+ obuf_f = (xfs_buf_log_format_v1_t*)buf_f;
+ blkno = obuf_f->blf_blkno;
+ len = obuf_f->blf_len;
+ flags = obuf_f->blf_flags;
+ break;
+ default:
+ xfs_fs_cmn_err(CE_ALERT, log->l_mp,
+ "xfs_log_recover: unknown buffer type 0x%x, dev %s",
+ buf_f->blf_type, XFS_BUFTARG_NAME(log->l_targ));
+ XFS_ERROR_REPORT("xlog_recover_do_buffer_trans",
+ XFS_ERRLEVEL_LOW, log->l_mp);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+
+ mp = log->l_mp;
+ if (flags & XFS_BLI_INODE_BUF) {
+ bp = xfs_buf_read_flags(mp->m_ddev_targp, blkno, len,
+ XFS_BUF_LOCK);
+ } else {
+ bp = xfs_buf_read(mp->m_ddev_targp, blkno, len, 0);
+ }
+ if (XFS_BUF_ISERROR(bp)) {
+ xfs_ioerror_alert("xlog_recover_do..(read#1)", log->l_mp,
+ bp, blkno);
+ error = XFS_BUF_GETERROR(bp);
+ xfs_buf_relse(bp);
+ return error;
+ }
+
+ error = 0;
+ if (flags & XFS_BLI_INODE_BUF) {
+ error = xlog_recover_do_inode_buffer(mp, item, bp, buf_f);
+ } else if (flags & (XFS_BLI_UDQUOT_BUF | XFS_BLI_GDQUOT_BUF)) {
+ xlog_recover_do_dquot_buffer(mp, log, item, bp, buf_f);
+ } else {
+ xlog_recover_do_reg_buffer(mp, item, bp, buf_f);
+ }
+ if (error)
+ return XFS_ERROR(error);
+
+ /*
+ * Perform delayed write on the buffer. Asynchronous writes will be
+ * slower when taking into account all the buffers to be flushed.
+ *
+ * Also make sure that only inode buffers with good sizes stay in
+ * the buffer cache. The kernel moves inodes in buffers of 1 block
+ * or XFS_INODE_CLUSTER_SIZE bytes, whichever is bigger. The inode
+ * buffers in the log can be a different size if the log was generated
+ * by an older kernel using unclustered inode buffers or a newer kernel
+ * running with a different inode cluster size. Regardless, if the
+ * the inode buffer size isn't MAX(blocksize, XFS_INODE_CLUSTER_SIZE)
+ * for *our* value of XFS_INODE_CLUSTER_SIZE, then we need to keep
+ * the buffer out of the buffer cache so that the buffer won't
+ * overlap with future reads of those inodes.
+ */
+ if (XFS_DINODE_MAGIC ==
+ INT_GET(*((__uint16_t *)(xfs_buf_offset(bp, 0))), ARCH_CONVERT) &&
+ (XFS_BUF_COUNT(bp) != MAX(log->l_mp->m_sb.sb_blocksize,
+ (__uint32_t)XFS_INODE_CLUSTER_SIZE(log->l_mp)))) {
+ XFS_BUF_STALE(bp);
+ error = xfs_bwrite(mp, bp);
+ } else {
+ ASSERT(XFS_BUF_FSPRIVATE(bp, void *) == NULL ||
+ XFS_BUF_FSPRIVATE(bp, xfs_mount_t *) == mp);
+ XFS_BUF_SET_FSPRIVATE(bp, mp);
+ XFS_BUF_SET_IODONE_FUNC(bp, xlog_recover_iodone);
+ xfs_bdwrite(mp, bp);
+ }
+
+ return (error);
+}
+
+STATIC int
+xlog_recover_do_inode_trans(
+ xlog_t *log,
+ xlog_recover_item_t *item,
+ int pass)
+{
+ xfs_inode_log_format_t *in_f;
+ xfs_mount_t *mp;
+ xfs_buf_t *bp;
+ xfs_imap_t imap;
+ xfs_dinode_t *dip;
+ xfs_ino_t ino;
+ int len;
+ xfs_caddr_t src;
+ xfs_caddr_t dest;
+ int error;
+ int attr_index;
+ uint fields;
+ xfs_dinode_core_t *dicp;
+
+ if (pass == XLOG_RECOVER_PASS1) {
+ return 0;
+ }
+
+ in_f = (xfs_inode_log_format_t *)item->ri_buf[0].i_addr;
+ ino = in_f->ilf_ino;
+ mp = log->l_mp;
+ if (ITEM_TYPE(item) == XFS_LI_INODE) {
+ imap.im_blkno = (xfs_daddr_t)in_f->ilf_blkno;
+ imap.im_len = in_f->ilf_len;
+ imap.im_boffset = in_f->ilf_boffset;
+ } else {
+ /*
+ * It's an old inode format record. We don't know where
+ * its cluster is located on disk, and we can't allow
+ * xfs_imap() to figure it out because the inode btrees
+ * are not ready to be used. Therefore do not pass the
+ * XFS_IMAP_LOOKUP flag to xfs_imap(). This will give
+ * us only the single block in which the inode lives
+ * rather than its cluster, so we must make sure to
+ * invalidate the buffer when we write it out below.
+ */
+ imap.im_blkno = 0;
+ xfs_imap(log->l_mp, NULL, ino, &imap, 0);
+ }
+
+ /*
+ * Inode buffers can be freed, look out for it,
+ * and do not replay the inode.
+ */
+ if (xlog_check_buffer_cancelled(log, imap.im_blkno, imap.im_len, 0))
+ return 0;
+
+ bp = xfs_buf_read_flags(mp->m_ddev_targp, imap.im_blkno, imap.im_len,
+ XFS_BUF_LOCK);
+ if (XFS_BUF_ISERROR(bp)) {
+ xfs_ioerror_alert("xlog_recover_do..(read#2)", mp,
+ bp, imap.im_blkno);
+ error = XFS_BUF_GETERROR(bp);
+ xfs_buf_relse(bp);
+ return error;
+ }
+ error = 0;
+ ASSERT(in_f->ilf_fields & XFS_ILOG_CORE);
+ dip = (xfs_dinode_t *)xfs_buf_offset(bp, imap.im_boffset);
+
+ /*
+ * Make sure the place we're flushing out to really looks
+ * like an inode!
+ */
+ if (unlikely(INT_GET(dip->di_core.di_magic, ARCH_CONVERT) != XFS_DINODE_MAGIC)) {
+ xfs_buf_relse(bp);
+ xfs_fs_cmn_err(CE_ALERT, mp,
+ "xfs_inode_recover: Bad inode magic number, dino ptr = 0x%p, dino bp = 0x%p, ino = %Ld",
+ dip, bp, ino);
+ XFS_ERROR_REPORT("xlog_recover_do_inode_trans(1)",
+ XFS_ERRLEVEL_LOW, mp);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ dicp = (xfs_dinode_core_t*)(item->ri_buf[1].i_addr);
+ if (unlikely(dicp->di_magic != XFS_DINODE_MAGIC)) {
+ xfs_buf_relse(bp);
+ xfs_fs_cmn_err(CE_ALERT, mp,
+ "xfs_inode_recover: Bad inode log record, rec ptr 0x%p, ino %Ld",
+ item, ino);
+ XFS_ERROR_REPORT("xlog_recover_do_inode_trans(2)",
+ XFS_ERRLEVEL_LOW, mp);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+
+ /* Skip replay when the on disk inode is newer than the log one */
+ if (dicp->di_flushiter <
+ INT_GET(dip->di_core.di_flushiter, ARCH_CONVERT)) {
+ /*
+ * Deal with the wrap case, DI_MAX_FLUSH is less
+ * than smaller numbers
+ */
+ if ((INT_GET(dip->di_core.di_flushiter, ARCH_CONVERT)
+ == DI_MAX_FLUSH) &&
+ (dicp->di_flushiter < (DI_MAX_FLUSH>>1))) {
+ /* do nothing */
+ } else {
+ xfs_buf_relse(bp);
+ return 0;
+ }
+ }
+ /* Take the opportunity to reset the flush iteration count */
+ dicp->di_flushiter = 0;
+
+ if (unlikely((dicp->di_mode & S_IFMT) == S_IFREG)) {
+ if ((dicp->di_format != XFS_DINODE_FMT_EXTENTS) &&
+ (dicp->di_format != XFS_DINODE_FMT_BTREE)) {
+ XFS_CORRUPTION_ERROR("xlog_recover_do_inode_trans(3)",
+ XFS_ERRLEVEL_LOW, mp, dicp);
+ xfs_buf_relse(bp);
+ xfs_fs_cmn_err(CE_ALERT, mp,
+ "xfs_inode_recover: Bad regular inode log record, rec ptr 0x%p, ino ptr = 0x%p, ino bp = 0x%p, ino %Ld",
+ item, dip, bp, ino);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ } else if (unlikely((dicp->di_mode & S_IFMT) == S_IFDIR)) {
+ if ((dicp->di_format != XFS_DINODE_FMT_EXTENTS) &&
+ (dicp->di_format != XFS_DINODE_FMT_BTREE) &&
+ (dicp->di_format != XFS_DINODE_FMT_LOCAL)) {
+ XFS_CORRUPTION_ERROR("xlog_recover_do_inode_trans(4)",
+ XFS_ERRLEVEL_LOW, mp, dicp);
+ xfs_buf_relse(bp);
+ xfs_fs_cmn_err(CE_ALERT, mp,
+ "xfs_inode_recover: Bad dir inode log record, rec ptr 0x%p, ino ptr = 0x%p, ino bp = 0x%p, ino %Ld",
+ item, dip, bp, ino);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ }
+ if (unlikely(dicp->di_nextents + dicp->di_anextents > dicp->di_nblocks)){
+ XFS_CORRUPTION_ERROR("xlog_recover_do_inode_trans(5)",
+ XFS_ERRLEVEL_LOW, mp, dicp);
+ xfs_buf_relse(bp);
+ xfs_fs_cmn_err(CE_ALERT, mp,
+ "xfs_inode_recover: Bad inode log record, rec ptr 0x%p, dino ptr 0x%p, dino bp 0x%p, ino %Ld, total extents = %d, nblocks = %Ld",
+ item, dip, bp, ino,
+ dicp->di_nextents + dicp->di_anextents,
+ dicp->di_nblocks);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ if (unlikely(dicp->di_forkoff > mp->m_sb.sb_inodesize)) {
+ XFS_CORRUPTION_ERROR("xlog_recover_do_inode_trans(6)",
+ XFS_ERRLEVEL_LOW, mp, dicp);
+ xfs_buf_relse(bp);
+ xfs_fs_cmn_err(CE_ALERT, mp,
+ "xfs_inode_recover: Bad inode log rec ptr 0x%p, dino ptr 0x%p, dino bp 0x%p, ino %Ld, forkoff 0x%x",
+ item, dip, bp, ino, dicp->di_forkoff);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ if (unlikely(item->ri_buf[1].i_len > sizeof(xfs_dinode_core_t))) {
+ XFS_CORRUPTION_ERROR("xlog_recover_do_inode_trans(7)",
+ XFS_ERRLEVEL_LOW, mp, dicp);
+ xfs_buf_relse(bp);
+ xfs_fs_cmn_err(CE_ALERT, mp,
+ "xfs_inode_recover: Bad inode log record length %d, rec ptr 0x%p",
+ item->ri_buf[1].i_len, item);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+
+ /* The core is in in-core format */
+ xfs_xlate_dinode_core((xfs_caddr_t)&dip->di_core,
+ (xfs_dinode_core_t*)item->ri_buf[1].i_addr,
+ -1, ARCH_CONVERT);
+ /* the rest is in on-disk format */
+ if (item->ri_buf[1].i_len > sizeof(xfs_dinode_core_t)) {
+ memcpy((xfs_caddr_t) dip + sizeof(xfs_dinode_core_t),
+ item->ri_buf[1].i_addr + sizeof(xfs_dinode_core_t),
+ item->ri_buf[1].i_len - sizeof(xfs_dinode_core_t));
+ }
+
+ fields = in_f->ilf_fields;
+ switch (fields & (XFS_ILOG_DEV | XFS_ILOG_UUID)) {
+ case XFS_ILOG_DEV:
+ INT_SET(dip->di_u.di_dev, ARCH_CONVERT, in_f->ilf_u.ilfu_rdev);
+
+ break;
+ case XFS_ILOG_UUID:
+ dip->di_u.di_muuid = in_f->ilf_u.ilfu_uuid;
+ break;
+ }
+
+ if (in_f->ilf_size == 2)
+ goto write_inode_buffer;
+ len = item->ri_buf[2].i_len;
+ src = item->ri_buf[2].i_addr;
+ ASSERT(in_f->ilf_size <= 4);
+ ASSERT((in_f->ilf_size == 3) || (fields & XFS_ILOG_AFORK));
+ ASSERT(!(fields & XFS_ILOG_DFORK) ||
+ (len == in_f->ilf_dsize));
+
+ switch (fields & XFS_ILOG_DFORK) {
+ case XFS_ILOG_DDATA:
+ case XFS_ILOG_DEXT:
+ memcpy(&dip->di_u, src, len);
+ break;
+
+ case XFS_ILOG_DBROOT:
+ xfs_bmbt_to_bmdr((xfs_bmbt_block_t *)src, len,
+ &(dip->di_u.di_bmbt),
+ XFS_DFORK_DSIZE(dip, mp));
+ break;
+
+ default:
+ /*
+ * There are no data fork flags set.
+ */
+ ASSERT((fields & XFS_ILOG_DFORK) == 0);
+ break;
+ }
+
+ /*
+ * If we logged any attribute data, recover it. There may or
+ * may not have been any other non-core data logged in this
+ * transaction.
+ */
+ if (in_f->ilf_fields & XFS_ILOG_AFORK) {
+ if (in_f->ilf_fields & XFS_ILOG_DFORK) {
+ attr_index = 3;
+ } else {
+ attr_index = 2;
+ }
+ len = item->ri_buf[attr_index].i_len;
+ src = item->ri_buf[attr_index].i_addr;
+ ASSERT(len == in_f->ilf_asize);
+
+ switch (in_f->ilf_fields & XFS_ILOG_AFORK) {
+ case XFS_ILOG_ADATA:
+ case XFS_ILOG_AEXT:
+ dest = XFS_DFORK_APTR(dip);
+ ASSERT(len <= XFS_DFORK_ASIZE(dip, mp));
+ memcpy(dest, src, len);
+ break;
+
+ case XFS_ILOG_ABROOT:
+ dest = XFS_DFORK_APTR(dip);
+ xfs_bmbt_to_bmdr((xfs_bmbt_block_t *)src, len,
+ (xfs_bmdr_block_t*)dest,
+ XFS_DFORK_ASIZE(dip, mp));
+ break;
+
+ default:
+ xlog_warn("XFS: xlog_recover_do_inode_trans: Invalid flag");
+ ASSERT(0);
+ xfs_buf_relse(bp);
+ return XFS_ERROR(EIO);
+ }
+ }
+
+write_inode_buffer:
+ if (ITEM_TYPE(item) == XFS_LI_INODE) {
+ ASSERT(XFS_BUF_FSPRIVATE(bp, void *) == NULL ||
+ XFS_BUF_FSPRIVATE(bp, xfs_mount_t *) == mp);
+ XFS_BUF_SET_FSPRIVATE(bp, mp);
+ XFS_BUF_SET_IODONE_FUNC(bp, xlog_recover_iodone);
+ xfs_bdwrite(mp, bp);
+ } else {
+ XFS_BUF_STALE(bp);
+ error = xfs_bwrite(mp, bp);
+ }
+
+ return (error);
+}
+
+/*
+ * Recover QUOTAOFF records. We simply make a note of it in the xlog_t
+ * structure, so that we know not to do any dquot item or dquot buffer recovery,
+ * of that type.
+ */
+STATIC int
+xlog_recover_do_quotaoff_trans(
+ xlog_t *log,
+ xlog_recover_item_t *item,
+ int pass)
+{
+ xfs_qoff_logformat_t *qoff_f;
+
+ if (pass == XLOG_RECOVER_PASS2) {
+ return (0);
+ }
+
+ qoff_f = (xfs_qoff_logformat_t *)item->ri_buf[0].i_addr;
+ ASSERT(qoff_f);
+
+ /*
+ * The logitem format's flag tells us if this was user quotaoff,
+ * group quotaoff or both.
+ */
+ if (qoff_f->qf_flags & XFS_UQUOTA_ACCT)
+ log->l_quotaoffs_flag |= XFS_DQ_USER;
+ if (qoff_f->qf_flags & XFS_GQUOTA_ACCT)
+ log->l_quotaoffs_flag |= XFS_DQ_GROUP;
+
+ return (0);
+}
+
+/*
+ * Recover a dquot record
+ */
+STATIC int
+xlog_recover_do_dquot_trans(
+ xlog_t *log,
+ xlog_recover_item_t *item,
+ int pass)
+{
+ xfs_mount_t *mp;
+ xfs_buf_t *bp;
+ struct xfs_disk_dquot *ddq, *recddq;
+ int error;
+ xfs_dq_logformat_t *dq_f;
+ uint type;
+
+ if (pass == XLOG_RECOVER_PASS1) {
+ return 0;
+ }
+ mp = log->l_mp;
+
+ /*
+ * Filesystems are required to send in quota flags at mount time.
+ */
+ if (mp->m_qflags == 0)
+ return (0);
+
+ recddq = (xfs_disk_dquot_t *)item->ri_buf[1].i_addr;
+ ASSERT(recddq);
+ /*
+ * This type of quotas was turned off, so ignore this record.
+ */
+ type = INT_GET(recddq->d_flags, ARCH_CONVERT) &
+ (XFS_DQ_USER | XFS_DQ_GROUP);
+ ASSERT(type);
+ if (log->l_quotaoffs_flag & type)
+ return (0);
+
+ /*
+ * At this point we know that quota was _not_ turned off.
+ * Since the mount flags are not indicating to us otherwise, this
+ * must mean that quota is on, and the dquot needs to be replayed.
+ * Remember that we may not have fully recovered the superblock yet,
+ * so we can't do the usual trick of looking at the SB quota bits.
+ *
+ * The other possibility, of course, is that the quota subsystem was
+ * removed since the last mount - ENOSYS.
+ */
+ dq_f = (xfs_dq_logformat_t *)item->ri_buf[0].i_addr;
+ ASSERT(dq_f);
+ if ((error = xfs_qm_dqcheck(recddq,
+ dq_f->qlf_id,
+ 0, XFS_QMOPT_DOWARN,
+ "xlog_recover_do_dquot_trans (log copy)"))) {
+ return XFS_ERROR(EIO);
+ }
+ ASSERT(dq_f->qlf_len == 1);
+
+ error = xfs_read_buf(mp, mp->m_ddev_targp,
+ dq_f->qlf_blkno,
+ XFS_FSB_TO_BB(mp, dq_f->qlf_len),
+ 0, &bp);
+ if (error) {
+ xfs_ioerror_alert("xlog_recover_do..(read#3)", mp,
+ bp, dq_f->qlf_blkno);
+ return error;
+ }
+ ASSERT(bp);
+ ddq = (xfs_disk_dquot_t *)xfs_buf_offset(bp, dq_f->qlf_boffset);
+
+ /*
+ * At least the magic num portion should be on disk because this
+ * was among a chunk of dquots created earlier, and we did some
+ * minimal initialization then.
+ */
+ if (xfs_qm_dqcheck(ddq, dq_f->qlf_id, 0, XFS_QMOPT_DOWARN,
+ "xlog_recover_do_dquot_trans")) {
+ xfs_buf_relse(bp);
+ return XFS_ERROR(EIO);
+ }
+
+ memcpy(ddq, recddq, item->ri_buf[1].i_len);
+
+ ASSERT(dq_f->qlf_size == 2);
+ ASSERT(XFS_BUF_FSPRIVATE(bp, void *) == NULL ||
+ XFS_BUF_FSPRIVATE(bp, xfs_mount_t *) == mp);
+ XFS_BUF_SET_FSPRIVATE(bp, mp);
+ XFS_BUF_SET_IODONE_FUNC(bp, xlog_recover_iodone);
+ xfs_bdwrite(mp, bp);
+
+ return (0);
+}
+
+/*
+ * This routine is called to create an in-core extent free intent
+ * item from the efi format structure which was logged on disk.
+ * It allocates an in-core efi, copies the extents from the format
+ * structure into it, and adds the efi to the AIL with the given
+ * LSN.
+ */
+STATIC void
+xlog_recover_do_efi_trans(
+ xlog_t *log,
+ xlog_recover_item_t *item,
+ xfs_lsn_t lsn,
+ int pass)
+{
+ xfs_mount_t *mp;
+ xfs_efi_log_item_t *efip;
+ xfs_efi_log_format_t *efi_formatp;
+ SPLDECL(s);
+
+ if (pass == XLOG_RECOVER_PASS1) {
+ return;
+ }
+
+ efi_formatp = (xfs_efi_log_format_t *)item->ri_buf[0].i_addr;
+ ASSERT(item->ri_buf[0].i_len ==
+ (sizeof(xfs_efi_log_format_t) +
+ ((efi_formatp->efi_nextents - 1) * sizeof(xfs_extent_t))));
+
+ mp = log->l_mp;
+ efip = xfs_efi_init(mp, efi_formatp->efi_nextents);
+ memcpy((char *)&(efip->efi_format), (char *)efi_formatp,
+ sizeof(xfs_efi_log_format_t) +
+ ((efi_formatp->efi_nextents - 1) * sizeof(xfs_extent_t)));
+ efip->efi_next_extent = efi_formatp->efi_nextents;
+ efip->efi_flags |= XFS_EFI_COMMITTED;
+
+ AIL_LOCK(mp,s);
+ /*
+ * xfs_trans_update_ail() drops the AIL lock.
+ */
+ xfs_trans_update_ail(mp, (xfs_log_item_t *)efip, lsn, s);
+}
+
+
+/*
+ * This routine is called when an efd format structure is found in
+ * a committed transaction in the log. It's purpose is to cancel
+ * the corresponding efi if it was still in the log. To do this
+ * it searches the AIL for the efi with an id equal to that in the
+ * efd format structure. If we find it, we remove the efi from the
+ * AIL and free it.
+ */
+STATIC void
+xlog_recover_do_efd_trans(
+ xlog_t *log,
+ xlog_recover_item_t *item,
+ int pass)
+{
+ xfs_mount_t *mp;
+ xfs_efd_log_format_t *efd_formatp;
+ xfs_efi_log_item_t *efip = NULL;
+ xfs_log_item_t *lip;
+ int gen;
+ int nexts;
+ __uint64_t efi_id;
+ SPLDECL(s);
+
+ if (pass == XLOG_RECOVER_PASS1) {
+ return;
+ }
+
+ efd_formatp = (xfs_efd_log_format_t *)item->ri_buf[0].i_addr;
+ ASSERT(item->ri_buf[0].i_len ==
+ (sizeof(xfs_efd_log_format_t) +
+ ((efd_formatp->efd_nextents - 1) * sizeof(xfs_extent_t))));
+ efi_id = efd_formatp->efd_efi_id;
+
+ /*
+ * Search for the efi with the id in the efd format structure
+ * in the AIL.
+ */
+ mp = log->l_mp;
+ AIL_LOCK(mp,s);
+ lip = xfs_trans_first_ail(mp, &gen);
+ while (lip != NULL) {
+ if (lip->li_type == XFS_LI_EFI) {
+ efip = (xfs_efi_log_item_t *)lip;
+ if (efip->efi_format.efi_id == efi_id) {
+ /*
+ * xfs_trans_delete_ail() drops the
+ * AIL lock.
+ */
+ xfs_trans_delete_ail(mp, lip, s);
+ break;
+ }
+ }
+ lip = xfs_trans_next_ail(mp, lip, &gen, NULL);
+ }
+ if (lip == NULL) {
+ AIL_UNLOCK(mp, s);
+ }
+
+ /*
+ * If we found it, then free it up. If it wasn't there, it
+ * must have been overwritten in the log. Oh well.
+ */
+ if (lip != NULL) {
+ nexts = efip->efi_format.efi_nextents;
+ if (nexts > XFS_EFI_MAX_FAST_EXTENTS) {
+ kmem_free(lip, sizeof(xfs_efi_log_item_t) +
+ ((nexts - 1) * sizeof(xfs_extent_t)));
+ } else {
+ kmem_zone_free(xfs_efi_zone, efip);
+ }
+ }
+}
+
+/*
+ * Perform the transaction
+ *
+ * If the transaction modifies a buffer or inode, do it now. Otherwise,
+ * EFIs and EFDs get queued up by adding entries into the AIL for them.
+ */
+STATIC int
+xlog_recover_do_trans(
+ xlog_t *log,
+ xlog_recover_t *trans,
+ int pass)
+{
+ int error = 0;
+ xlog_recover_item_t *item, *first_item;
+
+ if ((error = xlog_recover_reorder_trans(log, trans)))
+ return error;
+ first_item = item = trans->r_itemq;
+ do {
+ /*
+ * we don't need to worry about the block number being
+ * truncated in > 1 TB buffers because in user-land,
+ * we're now n32 or 64-bit so xfs_daddr_t is 64-bits so
+ * the blkno's will get through the user-mode buffer
+ * cache properly. The only bad case is o32 kernels
+ * where xfs_daddr_t is 32-bits but mount will warn us
+ * off a > 1 TB filesystem before we get here.
+ */
+ if ((ITEM_TYPE(item) == XFS_LI_BUF) ||
+ (ITEM_TYPE(item) == XFS_LI_6_1_BUF) ||
+ (ITEM_TYPE(item) == XFS_LI_5_3_BUF)) {
+ if ((error = xlog_recover_do_buffer_trans(log, item,
+ pass)))
+ break;
+ } else if ((ITEM_TYPE(item) == XFS_LI_INODE) ||
+ (ITEM_TYPE(item) == XFS_LI_6_1_INODE) ||
+ (ITEM_TYPE(item) == XFS_LI_5_3_INODE)) {
+ if ((error = xlog_recover_do_inode_trans(log, item,
+ pass)))
+ break;
+ } else if (ITEM_TYPE(item) == XFS_LI_EFI) {
+ xlog_recover_do_efi_trans(log, item, trans->r_lsn,
+ pass);
+ } else if (ITEM_TYPE(item) == XFS_LI_EFD) {
+ xlog_recover_do_efd_trans(log, item, pass);
+ } else if (ITEM_TYPE(item) == XFS_LI_DQUOT) {
+ if ((error = xlog_recover_do_dquot_trans(log, item,
+ pass)))
+ break;
+ } else if ((ITEM_TYPE(item) == XFS_LI_QUOTAOFF)) {
+ if ((error = xlog_recover_do_quotaoff_trans(log, item,
+ pass)))
+ break;
+ } else {
+ xlog_warn("XFS: xlog_recover_do_trans");
+ ASSERT(0);
+ error = XFS_ERROR(EIO);
+ break;
+ }
+ item = item->ri_next;
+ } while (first_item != item);
+
+ return error;
+}
+
+/*
+ * Free up any resources allocated by the transaction
+ *
+ * Remember that EFIs, EFDs, and IUNLINKs are handled later.
+ */
+STATIC void
+xlog_recover_free_trans(
+ xlog_recover_t *trans)
+{
+ xlog_recover_item_t *first_item, *item, *free_item;
+ int i;
+
+ item = first_item = trans->r_itemq;
+ do {
+ free_item = item;
+ item = item->ri_next;
+ /* Free the regions in the item. */
+ for (i = 0; i < free_item->ri_cnt; i++) {
+ kmem_free(free_item->ri_buf[i].i_addr,
+ free_item->ri_buf[i].i_len);
+ }
+ /* Free the item itself */
+ kmem_free(free_item->ri_buf,
+ (free_item->ri_total * sizeof(xfs_log_iovec_t)));
+ kmem_free(free_item, sizeof(xlog_recover_item_t));
+ } while (first_item != item);
+ /* Free the transaction recover structure */
+ kmem_free(trans, sizeof(xlog_recover_t));
+}
+
+STATIC int
+xlog_recover_commit_trans(
+ xlog_t *log,
+ xlog_recover_t **q,
+ xlog_recover_t *trans,
+ int pass)
+{
+ int error;
+
+ if ((error = xlog_recover_unlink_tid(q, trans)))
+ return error;
+ if ((error = xlog_recover_do_trans(log, trans, pass)))
+ return error;
+ xlog_recover_free_trans(trans); /* no error */
+ return 0;
+}
+
+STATIC int
+xlog_recover_unmount_trans(
+ xlog_recover_t *trans)
+{
+ /* Do nothing now */
+ xlog_warn("XFS: xlog_recover_unmount_trans: Unmount LR");
+ return 0;
+}
+
+/*
+ * There are two valid states of the r_state field. 0 indicates that the
+ * transaction structure is in a normal state. We have either seen the
+ * start of the transaction or the last operation we added was not a partial
+ * operation. If the last operation we added to the transaction was a
+ * partial operation, we need to mark r_state with XLOG_WAS_CONT_TRANS.
+ *
+ * NOTE: skip LRs with 0 data length.
+ */
+STATIC int
+xlog_recover_process_data(
+ xlog_t *log,
+ xlog_recover_t *rhash[],
+ xlog_rec_header_t *rhead,
+ xfs_caddr_t dp,
+ int pass)
+{
+ xfs_caddr_t lp;
+ int num_logops;
+ xlog_op_header_t *ohead;
+ xlog_recover_t *trans;
+ xlog_tid_t tid;
+ int error;
+ unsigned long hash;
+ uint flags;
+
+ lp = dp + INT_GET(rhead->h_len, ARCH_CONVERT);
+ num_logops = INT_GET(rhead->h_num_logops, ARCH_CONVERT);
+
+ /* check the log format matches our own - else we can't recover */
+ if (xlog_header_check_recover(log->l_mp, rhead))
+ return (XFS_ERROR(EIO));
+
+ while ((dp < lp) && num_logops) {
+ ASSERT(dp + sizeof(xlog_op_header_t) <= lp);
+ ohead = (xlog_op_header_t *)dp;
+ dp += sizeof(xlog_op_header_t);
+ if (ohead->oh_clientid != XFS_TRANSACTION &&
+ ohead->oh_clientid != XFS_LOG) {
+ xlog_warn(
+ "XFS: xlog_recover_process_data: bad clientid");
+ ASSERT(0);
+ return (XFS_ERROR(EIO));
+ }
+ tid = INT_GET(ohead->oh_tid, ARCH_CONVERT);
+ hash = XLOG_RHASH(tid);
+ trans = xlog_recover_find_tid(rhash[hash], tid);
+ if (trans == NULL) { /* not found; add new tid */
+ if (ohead->oh_flags & XLOG_START_TRANS)
+ xlog_recover_new_tid(&rhash[hash], tid,
+ INT_GET(rhead->h_lsn, ARCH_CONVERT));
+ } else {
+ ASSERT(dp+INT_GET(ohead->oh_len, ARCH_CONVERT) <= lp);
+ flags = ohead->oh_flags & ~XLOG_END_TRANS;
+ if (flags & XLOG_WAS_CONT_TRANS)
+ flags &= ~XLOG_CONTINUE_TRANS;
+ switch (flags) {
+ case XLOG_COMMIT_TRANS:
+ error = xlog_recover_commit_trans(log,
+ &rhash[hash], trans, pass);
+ break;
+ case XLOG_UNMOUNT_TRANS:
+ error = xlog_recover_unmount_trans(trans);
+ break;
+ case XLOG_WAS_CONT_TRANS:
+ error = xlog_recover_add_to_cont_trans(trans,
+ dp, INT_GET(ohead->oh_len,
+ ARCH_CONVERT));
+ break;
+ case XLOG_START_TRANS:
+ xlog_warn(
+ "XFS: xlog_recover_process_data: bad transaction");
+ ASSERT(0);
+ error = XFS_ERROR(EIO);
+ break;
+ case 0:
+ case XLOG_CONTINUE_TRANS:
+ error = xlog_recover_add_to_trans(trans,
+ dp, INT_GET(ohead->oh_len,
+ ARCH_CONVERT));
+ break;
+ default:
+ xlog_warn(
+ "XFS: xlog_recover_process_data: bad flag");
+ ASSERT(0);
+ error = XFS_ERROR(EIO);
+ break;
+ }
+ if (error)
+ return error;
+ }
+ dp += INT_GET(ohead->oh_len, ARCH_CONVERT);
+ num_logops--;
+ }
+ return 0;
+}
+
+/*
+ * Process an extent free intent item that was recovered from
+ * the log. We need to free the extents that it describes.
+ */
+STATIC void
+xlog_recover_process_efi(
+ xfs_mount_t *mp,
+ xfs_efi_log_item_t *efip)
+{
+ xfs_efd_log_item_t *efdp;
+ xfs_trans_t *tp;
+ int i;
+ xfs_extent_t *extp;
+ xfs_fsblock_t startblock_fsb;
+
+ ASSERT(!(efip->efi_flags & XFS_EFI_RECOVERED));
+
+ /*
+ * First check the validity of the extents described by the
+ * EFI. If any are bad, then assume that all are bad and
+ * just toss the EFI.
+ */
+ for (i = 0; i < efip->efi_format.efi_nextents; i++) {
+ extp = &(efip->efi_format.efi_extents[i]);
+ startblock_fsb = XFS_BB_TO_FSB(mp,
+ XFS_FSB_TO_DADDR(mp, extp->ext_start));
+ if ((startblock_fsb == 0) ||
+ (extp->ext_len == 0) ||
+ (startblock_fsb >= mp->m_sb.sb_dblocks) ||
+ (extp->ext_len >= mp->m_sb.sb_agblocks)) {
+ /*
+ * This will pull the EFI from the AIL and
+ * free the memory associated with it.
+ */
+ xfs_efi_release(efip, efip->efi_format.efi_nextents);
+ return;
+ }
+ }
+
+ tp = xfs_trans_alloc(mp, 0);
+ xfs_trans_reserve(tp, 0, XFS_ITRUNCATE_LOG_RES(mp), 0, 0, 0);
+ efdp = xfs_trans_get_efd(tp, efip, efip->efi_format.efi_nextents);
+
+ for (i = 0; i < efip->efi_format.efi_nextents; i++) {
+ extp = &(efip->efi_format.efi_extents[i]);
+ xfs_free_extent(tp, extp->ext_start, extp->ext_len);
+ xfs_trans_log_efd_extent(tp, efdp, extp->ext_start,
+ extp->ext_len);
+ }
+
+ efip->efi_flags |= XFS_EFI_RECOVERED;
+ xfs_trans_commit(tp, 0, NULL);
+}
+
+/*
+ * Verify that once we've encountered something other than an EFI
+ * in the AIL that there are no more EFIs in the AIL.
+ */
+#if defined(DEBUG)
+STATIC void
+xlog_recover_check_ail(
+ xfs_mount_t *mp,
+ xfs_log_item_t *lip,
+ int gen)
+{
+ int orig_gen = gen;
+
+ do {
+ ASSERT(lip->li_type != XFS_LI_EFI);
+ lip = xfs_trans_next_ail(mp, lip, &gen, NULL);
+ /*
+ * The check will be bogus if we restart from the
+ * beginning of the AIL, so ASSERT that we don't.
+ * We never should since we're holding the AIL lock
+ * the entire time.
+ */
+ ASSERT(gen == orig_gen);
+ } while (lip != NULL);
+}
+#endif /* DEBUG */
+
+/*
+ * When this is called, all of the EFIs which did not have
+ * corresponding EFDs should be in the AIL. What we do now
+ * is free the extents associated with each one.
+ *
+ * Since we process the EFIs in normal transactions, they
+ * will be removed at some point after the commit. This prevents
+ * us from just walking down the list processing each one.
+ * We'll use a flag in the EFI to skip those that we've already
+ * processed and use the AIL iteration mechanism's generation
+ * count to try to speed this up at least a bit.
+ *
+ * When we start, we know that the EFIs are the only things in
+ * the AIL. As we process them, however, other items are added
+ * to the AIL. Since everything added to the AIL must come after
+ * everything already in the AIL, we stop processing as soon as
+ * we see something other than an EFI in the AIL.
+ */
+STATIC void
+xlog_recover_process_efis(
+ xlog_t *log)
+{
+ xfs_log_item_t *lip;
+ xfs_efi_log_item_t *efip;
+ int gen;
+ xfs_mount_t *mp;
+ SPLDECL(s);
+
+ mp = log->l_mp;
+ AIL_LOCK(mp,s);
+
+ lip = xfs_trans_first_ail(mp, &gen);
+ while (lip != NULL) {
+ /*
+ * We're done when we see something other than an EFI.
+ */
+ if (lip->li_type != XFS_LI_EFI) {
+ xlog_recover_check_ail(mp, lip, gen);
+ break;
+ }
+
+ /*
+ * Skip EFIs that we've already processed.
+ */
+ efip = (xfs_efi_log_item_t *)lip;
+ if (efip->efi_flags & XFS_EFI_RECOVERED) {
+ lip = xfs_trans_next_ail(mp, lip, &gen, NULL);
+ continue;
+ }
+
+ AIL_UNLOCK(mp, s);
+ xlog_recover_process_efi(mp, efip);
+ AIL_LOCK(mp,s);
+ lip = xfs_trans_next_ail(mp, lip, &gen, NULL);
+ }
+ AIL_UNLOCK(mp, s);
+}
+
+/*
+ * This routine performs a transaction to null out a bad inode pointer
+ * in an agi unlinked inode hash bucket.
+ */
+STATIC void
+xlog_recover_clear_agi_bucket(
+ xfs_mount_t *mp,
+ xfs_agnumber_t agno,
+ int bucket)
+{
+ xfs_trans_t *tp;
+ xfs_agi_t *agi;
+ xfs_buf_t *agibp;
+ int offset;
+ int error;
+
+ tp = xfs_trans_alloc(mp, XFS_TRANS_CLEAR_AGI_BUCKET);
+ xfs_trans_reserve(tp, 0, XFS_CLEAR_AGI_BUCKET_LOG_RES(mp), 0, 0, 0);
+
+ error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp,
+ XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)),
+ XFS_FSS_TO_BB(mp, 1), 0, &agibp);
+ if (error) {
+ xfs_trans_cancel(tp, XFS_TRANS_ABORT);
+ return;
+ }
+
+ agi = XFS_BUF_TO_AGI(agibp);
+ if (INT_GET(agi->agi_magicnum, ARCH_CONVERT) != XFS_AGI_MAGIC) {
+ xfs_trans_cancel(tp, XFS_TRANS_ABORT);
+ return;
+ }
+ ASSERT(INT_GET(agi->agi_magicnum, ARCH_CONVERT) == XFS_AGI_MAGIC);
+
+ INT_SET(agi->agi_unlinked[bucket], ARCH_CONVERT, NULLAGINO);
+ offset = offsetof(xfs_agi_t, agi_unlinked) +
+ (sizeof(xfs_agino_t) * bucket);
+ xfs_trans_log_buf(tp, agibp, offset,
+ (offset + sizeof(xfs_agino_t) - 1));
+
+ (void) xfs_trans_commit(tp, 0, NULL);
+}
+
+/*
+ * xlog_iunlink_recover
+ *
+ * This is called during recovery to process any inodes which
+ * we unlinked but not freed when the system crashed. These
+ * inodes will be on the lists in the AGI blocks. What we do
+ * here is scan all the AGIs and fully truncate and free any
+ * inodes found on the lists. Each inode is removed from the
+ * lists when it has been fully truncated and is freed. The
+ * freeing of the inode and its removal from the list must be
+ * atomic.
+ */
+void
+xlog_recover_process_iunlinks(
+ xlog_t *log)
+{
+ xfs_mount_t *mp;
+ xfs_agnumber_t agno;
+ xfs_agi_t *agi;
+ xfs_buf_t *agibp;
+ xfs_buf_t *ibp;
+ xfs_dinode_t *dip;
+ xfs_inode_t *ip;
+ xfs_agino_t agino;
+ xfs_ino_t ino;
+ int bucket;
+ int error;
+ uint mp_dmevmask;
+
+ mp = log->l_mp;
+
+ /*
+ * Prevent any DMAPI event from being sent while in this function.
+ */
+ mp_dmevmask = mp->m_dmevmask;
+ mp->m_dmevmask = 0;
+
+ for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) {
+ /*
+ * Find the agi for this ag.
+ */
+ agibp = xfs_buf_read(mp->m_ddev_targp,
+ XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)),
+ XFS_FSS_TO_BB(mp, 1), 0);
+ if (XFS_BUF_ISERROR(agibp)) {
+ xfs_ioerror_alert("xlog_recover_process_iunlinks(#1)",
+ log->l_mp, agibp,
+ XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)));
+ }
+ agi = XFS_BUF_TO_AGI(agibp);
+ ASSERT(XFS_AGI_MAGIC ==
+ INT_GET(agi->agi_magicnum, ARCH_CONVERT));
+
+ for (bucket = 0; bucket < XFS_AGI_UNLINKED_BUCKETS; bucket++) {
+
+ agino = INT_GET(agi->agi_unlinked[bucket], ARCH_CONVERT);
+ while (agino != NULLAGINO) {
+
+ /*
+ * Release the agi buffer so that it can
+ * be acquired in the normal course of the
+ * transaction to truncate and free the inode.
+ */
+ xfs_buf_relse(agibp);
+
+ ino = XFS_AGINO_TO_INO(mp, agno, agino);
+ error = xfs_iget(mp, NULL, ino, 0, 0, &ip, 0);
+ ASSERT(error || (ip != NULL));
+
+ if (!error) {
+ /*
+ * Get the on disk inode to find the
+ * next inode in the bucket.
+ */
+ error = xfs_itobp(mp, NULL, ip, &dip,
+ &ibp, 0);
+ ASSERT(error || (dip != NULL));
+ }
+
+ if (!error) {
+ ASSERT(ip->i_d.di_nlink == 0);
+
+ /* setup for the next pass */
+ agino = INT_GET(dip->di_next_unlinked,
+ ARCH_CONVERT);
+ xfs_buf_relse(ibp);
+ /*
+ * Prevent any DMAPI event from
+ * being sent when the
+ * reference on the inode is
+ * dropped.
+ */
+ ip->i_d.di_dmevmask = 0;
+
+ /*
+ * If this is a new inode, handle
+ * it specially. Otherwise,
+ * just drop our reference to the
+ * inode. If there are no
+ * other references, this will
+ * send the inode to
+ * xfs_inactive() which will
+ * truncate the file and free
+ * the inode.
+ */
+ if (ip->i_d.di_mode == 0)
+ xfs_iput_new(ip, 0);
+ else
+ VN_RELE(XFS_ITOV(ip));
+ } else {
+ /*
+ * We can't read in the inode
+ * this bucket points to, or
+ * this inode is messed up. Just
+ * ditch this bucket of inodes. We
+ * will lose some inodes and space,
+ * but at least we won't hang. Call
+ * xlog_recover_clear_agi_bucket()
+ * to perform a transaction to clear
+ * the inode pointer in the bucket.
+ */
+ xlog_recover_clear_agi_bucket(mp, agno,
+ bucket);
+
+ agino = NULLAGINO;
+ }
+
+ /*
+ * Reacquire the agibuffer and continue around
+ * the loop.
+ */
+ agibp = xfs_buf_read(mp->m_ddev_targp,
+ XFS_AG_DADDR(mp, agno,
+ XFS_AGI_DADDR(mp)),
+ XFS_FSS_TO_BB(mp, 1), 0);
+ if (XFS_BUF_ISERROR(agibp)) {
+ xfs_ioerror_alert(
+ "xlog_recover_process_iunlinks(#2)",
+ log->l_mp, agibp,
+ XFS_AG_DADDR(mp, agno,
+ XFS_AGI_DADDR(mp)));
+ }
+ agi = XFS_BUF_TO_AGI(agibp);
+ ASSERT(XFS_AGI_MAGIC == INT_GET(
+ agi->agi_magicnum, ARCH_CONVERT));
+ }
+ }
+
+ /*
+ * Release the buffer for the current agi so we can
+ * go on to the next one.
+ */
+ xfs_buf_relse(agibp);
+ }
+
+ mp->m_dmevmask = mp_dmevmask;
+}
+
+
+#ifdef DEBUG
+STATIC void
+xlog_pack_data_checksum(
+ xlog_t *log,
+ xlog_in_core_t *iclog,
+ int size)
+{
+ int i;
+ uint *up;
+ uint chksum = 0;
+
+ up = (uint *)iclog->ic_datap;
+ /* divide length by 4 to get # words */
+ for (i = 0; i < (size >> 2); i++) {
+ chksum ^= INT_GET(*up, ARCH_CONVERT);
+ up++;
+ }
+ INT_SET(iclog->ic_header.h_chksum, ARCH_CONVERT, chksum);
+}
+#else
+#define xlog_pack_data_checksum(log, iclog, size)
+#endif
+
+/*
+ * Stamp cycle number in every block
+ */
+void
+xlog_pack_data(
+ xlog_t *log,
+ xlog_in_core_t *iclog,
+ int roundoff)
+{
+ int i, j, k;
+ int size = iclog->ic_offset + roundoff;
+ uint cycle_lsn;
+ xfs_caddr_t dp;
+ xlog_in_core_2_t *xhdr;
+
+ xlog_pack_data_checksum(log, iclog, size);
+
+ cycle_lsn = CYCLE_LSN_NOCONV(iclog->ic_header.h_lsn, ARCH_CONVERT);
+
+ dp = iclog->ic_datap;
+ for (i = 0; i < BTOBB(size) &&
+ i < (XLOG_HEADER_CYCLE_SIZE / BBSIZE); i++) {
+ iclog->ic_header.h_cycle_data[i] = *(uint *)dp;
+ *(uint *)dp = cycle_lsn;
+ dp += BBSIZE;
+ }
+
+ if (XFS_SB_VERSION_HASLOGV2(&log->l_mp->m_sb)) {
+ xhdr = (xlog_in_core_2_t *)&iclog->ic_header;
+ for ( ; i < BTOBB(size); i++) {
+ j = i / (XLOG_HEADER_CYCLE_SIZE / BBSIZE);
+ k = i % (XLOG_HEADER_CYCLE_SIZE / BBSIZE);
+ xhdr[j].hic_xheader.xh_cycle_data[k] = *(uint *)dp;
+ *(uint *)dp = cycle_lsn;
+ dp += BBSIZE;
+ }
+
+ for (i = 1; i < log->l_iclog_heads; i++) {
+ xhdr[i].hic_xheader.xh_cycle = cycle_lsn;
+ }
+ }
+}
+
+#if defined(DEBUG) && defined(XFS_LOUD_RECOVERY)
+STATIC void
+xlog_unpack_data_checksum(
+ xlog_rec_header_t *rhead,
+ xfs_caddr_t dp,
+ xlog_t *log)
+{
+ uint *up = (uint *)dp;
+ uint chksum = 0;
+ int i;
+
+ /* divide length by 4 to get # words */
+ for (i=0; i < INT_GET(rhead->h_len, ARCH_CONVERT) >> 2; i++) {
+ chksum ^= INT_GET(*up, ARCH_CONVERT);
+ up++;
+ }
+ if (chksum != INT_GET(rhead->h_chksum, ARCH_CONVERT)) {
+ if (!INT_ISZERO(rhead->h_chksum, ARCH_CONVERT) ||
+ ((log->l_flags & XLOG_CHKSUM_MISMATCH) == 0)) {
+ cmn_err(CE_DEBUG,
+ "XFS: LogR chksum mismatch: was (0x%x) is (0x%x)",
+ INT_GET(rhead->h_chksum, ARCH_CONVERT), chksum);
+ cmn_err(CE_DEBUG,
+"XFS: Disregard message if filesystem was created with non-DEBUG kernel");
+ if (XFS_SB_VERSION_HASLOGV2(&log->l_mp->m_sb)) {
+ cmn_err(CE_DEBUG,
+ "XFS: LogR this is a LogV2 filesystem");
+ }
+ log->l_flags |= XLOG_CHKSUM_MISMATCH;
+ }
+ }
+}
+#else
+#define xlog_unpack_data_checksum(rhead, dp, log)
+#endif
+
+STATIC void
+xlog_unpack_data(
+ xlog_rec_header_t *rhead,
+ xfs_caddr_t dp,
+ xlog_t *log)
+{
+ int i, j, k;
+ xlog_in_core_2_t *xhdr;
+
+ for (i = 0; i < BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT)) &&
+ i < (XLOG_HEADER_CYCLE_SIZE / BBSIZE); i++) {
+ *(uint *)dp = *(uint *)&rhead->h_cycle_data[i];
+ dp += BBSIZE;
+ }
+
+ if (XFS_SB_VERSION_HASLOGV2(&log->l_mp->m_sb)) {
+ xhdr = (xlog_in_core_2_t *)rhead;
+ for ( ; i < BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT)); i++) {
+ j = i / (XLOG_HEADER_CYCLE_SIZE / BBSIZE);
+ k = i % (XLOG_HEADER_CYCLE_SIZE / BBSIZE);
+ *(uint *)dp = xhdr[j].hic_xheader.xh_cycle_data[k];
+ dp += BBSIZE;
+ }
+ }
+
+ xlog_unpack_data_checksum(rhead, dp, log);
+}
+
+STATIC int
+xlog_valid_rec_header(
+ xlog_t *log,
+ xlog_rec_header_t *rhead,
+ xfs_daddr_t blkno)
+{
+ int hlen;
+
+ if (unlikely(
+ (INT_GET(rhead->h_magicno, ARCH_CONVERT) !=
+ XLOG_HEADER_MAGIC_NUM))) {
+ XFS_ERROR_REPORT("xlog_valid_rec_header(1)",
+ XFS_ERRLEVEL_LOW, log->l_mp);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ if (unlikely(
+ (INT_ISZERO(rhead->h_version, ARCH_CONVERT) ||
+ (INT_GET(rhead->h_version, ARCH_CONVERT) &
+ (~XLOG_VERSION_OKBITS)) != 0))) {
+ xlog_warn("XFS: %s: unrecognised log version (%d).",
+ __FUNCTION__, INT_GET(rhead->h_version, ARCH_CONVERT));
+ return XFS_ERROR(EIO);
+ }
+
+ /* LR body must have data or it wouldn't have been written */
+ hlen = INT_GET(rhead->h_len, ARCH_CONVERT);
+ if (unlikely( hlen <= 0 || hlen > INT_MAX )) {
+ XFS_ERROR_REPORT("xlog_valid_rec_header(2)",
+ XFS_ERRLEVEL_LOW, log->l_mp);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ if (unlikely( blkno > log->l_logBBsize || blkno > INT_MAX )) {
+ XFS_ERROR_REPORT("xlog_valid_rec_header(3)",
+ XFS_ERRLEVEL_LOW, log->l_mp);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+ return 0;
+}
+
+/*
+ * Read the log from tail to head and process the log records found.
+ * Handle the two cases where the tail and head are in the same cycle
+ * and where the active portion of the log wraps around the end of
+ * the physical log separately. The pass parameter is passed through
+ * to the routines called to process the data and is not looked at
+ * here.
+ */
+STATIC int
+xlog_do_recovery_pass(
+ xlog_t *log,
+ xfs_daddr_t head_blk,
+ xfs_daddr_t tail_blk,
+ int pass)
+{
+ xlog_rec_header_t *rhead;
+ xfs_daddr_t blk_no;
+ xfs_caddr_t bufaddr, offset;
+ xfs_buf_t *hbp, *dbp;
+ int error = 0, h_size;
+ int bblks, split_bblks;
+ int hblks, split_hblks, wrapped_hblks;
+ xlog_recover_t *rhash[XLOG_RHASH_SIZE];
+
+ ASSERT(head_blk != tail_blk);
+
+ /*
+ * Read the header of the tail block and get the iclog buffer size from
+ * h_size. Use this to tell how many sectors make up the log header.
+ */
+ if (XFS_SB_VERSION_HASLOGV2(&log->l_mp->m_sb)) {
+ /*
+ * When using variable length iclogs, read first sector of
+ * iclog header and extract the header size from it. Get a
+ * new hbp that is the correct size.
+ */
+ hbp = xlog_get_bp(log, 1);
+ if (!hbp)
+ return ENOMEM;
+ if ((error = xlog_bread(log, tail_blk, 1, hbp)))
+ goto bread_err1;
+ offset = xlog_align(log, tail_blk, 1, hbp);
+ rhead = (xlog_rec_header_t *)offset;
+ error = xlog_valid_rec_header(log, rhead, tail_blk);
+ if (error)
+ goto bread_err1;
+ h_size = INT_GET(rhead->h_size, ARCH_CONVERT);
+ if ((INT_GET(rhead->h_version, ARCH_CONVERT)
+ & XLOG_VERSION_2) &&
+ (h_size > XLOG_HEADER_CYCLE_SIZE)) {
+ hblks = h_size / XLOG_HEADER_CYCLE_SIZE;
+ if (h_size % XLOG_HEADER_CYCLE_SIZE)
+ hblks++;
+ xlog_put_bp(hbp);
+ hbp = xlog_get_bp(log, hblks);
+ } else {
+ hblks = 1;
+ }
+ } else {
+ ASSERT(log->l_sectbb_log == 0);
+ hblks = 1;
+ hbp = xlog_get_bp(log, 1);
+ h_size = XLOG_BIG_RECORD_BSIZE;
+ }
+
+ if (!hbp)
+ return ENOMEM;
+ dbp = xlog_get_bp(log, BTOBB(h_size));
+ if (!dbp) {
+ xlog_put_bp(hbp);
+ return ENOMEM;
+ }
+
+ memset(rhash, 0, sizeof(rhash));
+ if (tail_blk <= head_blk) {
+ for (blk_no = tail_blk; blk_no < head_blk; ) {
+ if ((error = xlog_bread(log, blk_no, hblks, hbp)))
+ goto bread_err2;
+ offset = xlog_align(log, blk_no, hblks, hbp);
+ rhead = (xlog_rec_header_t *)offset;
+ error = xlog_valid_rec_header(log, rhead, blk_no);
+ if (error)
+ goto bread_err2;
+
+ /* blocks in data section */
+ bblks = (int)BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT));
+ error = xlog_bread(log, blk_no + hblks, bblks, dbp);
+ if (error)
+ goto bread_err2;
+ offset = xlog_align(log, blk_no + hblks, bblks, dbp);
+ xlog_unpack_data(rhead, offset, log);
+ if ((error = xlog_recover_process_data(log,
+ rhash, rhead, offset, pass)))
+ goto bread_err2;
+ blk_no += bblks + hblks;
+ }
+ } else {
+ /*
+ * Perform recovery around the end of the physical log.
+ * When the head is not on the same cycle number as the tail,
+ * we can't do a sequential recovery as above.
+ */
+ blk_no = tail_blk;
+ while (blk_no < log->l_logBBsize) {
+ /*
+ * Check for header wrapping around physical end-of-log
+ */
+ offset = NULL;
+ split_hblks = 0;
+ wrapped_hblks = 0;
+ if (blk_no + hblks <= log->l_logBBsize) {
+ /* Read header in one read */
+ error = xlog_bread(log, blk_no, hblks, hbp);
+ if (error)
+ goto bread_err2;
+ offset = xlog_align(log, blk_no, hblks, hbp);
+ } else {
+ /* This LR is split across physical log end */
+ if (blk_no != log->l_logBBsize) {
+ /* some data before physical log end */
+ ASSERT(blk_no <= INT_MAX);
+ split_hblks = log->l_logBBsize - (int)blk_no;
+ ASSERT(split_hblks > 0);
+ if ((error = xlog_bread(log, blk_no,
+ split_hblks, hbp)))
+ goto bread_err2;
+ offset = xlog_align(log, blk_no,
+ split_hblks, hbp);
+ }
+ /*
+ * Note: this black magic still works with
+ * large sector sizes (non-512) only because:
+ * - we increased the buffer size originally
+ * by 1 sector giving us enough extra space
+ * for the second read;
+ * - the log start is guaranteed to be sector
+ * aligned;
+ * - we read the log end (LR header start)
+ * _first_, then the log start (LR header end)
+ * - order is important.
+ */
+ bufaddr = XFS_BUF_PTR(hbp);
+ XFS_BUF_SET_PTR(hbp,
+ bufaddr + BBTOB(split_hblks),
+ BBTOB(hblks - split_hblks));
+ wrapped_hblks = hblks - split_hblks;
+ error = xlog_bread(log, 0, wrapped_hblks, hbp);
+ if (error)
+ goto bread_err2;
+ XFS_BUF_SET_PTR(hbp, bufaddr, BBTOB(hblks));
+ if (!offset)
+ offset = xlog_align(log, 0,
+ wrapped_hblks, hbp);
+ }
+ rhead = (xlog_rec_header_t *)offset;
+ error = xlog_valid_rec_header(log, rhead,
+ split_hblks ? blk_no : 0);
+ if (error)
+ goto bread_err2;
+
+ bblks = (int)BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT));
+ blk_no += hblks;
+
+ /* Read in data for log record */
+ if (blk_no + bblks <= log->l_logBBsize) {
+ error = xlog_bread(log, blk_no, bblks, dbp);
+ if (error)
+ goto bread_err2;
+ offset = xlog_align(log, blk_no, bblks, dbp);
+ } else {
+ /* This log record is split across the
+ * physical end of log */
+ offset = NULL;
+ split_bblks = 0;
+ if (blk_no != log->l_logBBsize) {
+ /* some data is before the physical
+ * end of log */
+ ASSERT(!wrapped_hblks);
+ ASSERT(blk_no <= INT_MAX);
+ split_bblks =
+ log->l_logBBsize - (int)blk_no;
+ ASSERT(split_bblks > 0);
+ if ((error = xlog_bread(log, blk_no,
+ split_bblks, dbp)))
+ goto bread_err2;
+ offset = xlog_align(log, blk_no,
+ split_bblks, dbp);
+ }
+ /*
+ * Note: this black magic still works with
+ * large sector sizes (non-512) only because:
+ * - we increased the buffer size originally
+ * by 1 sector giving us enough extra space
+ * for the second read;
+ * - the log start is guaranteed to be sector
+ * aligned;
+ * - we read the log end (LR header start)
+ * _first_, then the log start (LR header end)
+ * - order is important.
+ */
+ bufaddr = XFS_BUF_PTR(dbp);
+ XFS_BUF_SET_PTR(dbp,
+ bufaddr + BBTOB(split_bblks),
+ BBTOB(bblks - split_bblks));
+ if ((error = xlog_bread(log, wrapped_hblks,
+ bblks - split_bblks, dbp)))
+ goto bread_err2;
+ XFS_BUF_SET_PTR(dbp, bufaddr, h_size);
+ if (!offset)
+ offset = xlog_align(log, wrapped_hblks,
+ bblks - split_bblks, dbp);
+ }
+ xlog_unpack_data(rhead, offset, log);
+ if ((error = xlog_recover_process_data(log, rhash,
+ rhead, offset, pass)))
+ goto bread_err2;
+ blk_no += bblks;
+ }
+
+ ASSERT(blk_no >= log->l_logBBsize);
+ blk_no -= log->l_logBBsize;
+
+ /* read first part of physical log */
+ while (blk_no < head_blk) {
+ if ((error = xlog_bread(log, blk_no, hblks, hbp)))
+ goto bread_err2;
+ offset = xlog_align(log, blk_no, hblks, hbp);
+ rhead = (xlog_rec_header_t *)offset;
+ error = xlog_valid_rec_header(log, rhead, blk_no);
+ if (error)
+ goto bread_err2;
+ bblks = (int)BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT));
+ if ((error = xlog_bread(log, blk_no+hblks, bblks, dbp)))
+ goto bread_err2;
+ offset = xlog_align(log, blk_no+hblks, bblks, dbp);
+ xlog_unpack_data(rhead, offset, log);
+ if ((error = xlog_recover_process_data(log, rhash,
+ rhead, offset, pass)))
+ goto bread_err2;
+ blk_no += bblks + hblks;
+ }
+ }
+
+ bread_err2:
+ xlog_put_bp(dbp);
+ bread_err1:
+ xlog_put_bp(hbp);
+ return error;
+}
+
+/*
+ * Do the recovery of the log. We actually do this in two phases.
+ * The two passes are necessary in order to implement the function
+ * of cancelling a record written into the log. The first pass
+ * determines those things which have been cancelled, and the
+ * second pass replays log items normally except for those which
+ * have been cancelled. The handling of the replay and cancellations
+ * takes place in the log item type specific routines.
+ *
+ * The table of items which have cancel records in the log is allocated
+ * and freed at this level, since only here do we know when all of
+ * the log recovery has been completed.
+ */
+STATIC int
+xlog_do_log_recovery(
+ xlog_t *log,
+ xfs_daddr_t head_blk,
+ xfs_daddr_t tail_blk)
+{
+ int error;
+
+ ASSERT(head_blk != tail_blk);
+
+ /*
+ * First do a pass to find all of the cancelled buf log items.
+ * Store them in the buf_cancel_table for use in the second pass.
+ */
+ log->l_buf_cancel_table =
+ (xfs_buf_cancel_t **)kmem_zalloc(XLOG_BC_TABLE_SIZE *
+ sizeof(xfs_buf_cancel_t*),
+ KM_SLEEP);
+ error = xlog_do_recovery_pass(log, head_blk, tail_blk,
+ XLOG_RECOVER_PASS1);
+ if (error != 0) {
+ kmem_free(log->l_buf_cancel_table,
+ XLOG_BC_TABLE_SIZE * sizeof(xfs_buf_cancel_t*));
+ log->l_buf_cancel_table = NULL;
+ return error;
+ }
+ /*
+ * Then do a second pass to actually recover the items in the log.
+ * When it is complete free the table of buf cancel items.
+ */
+ error = xlog_do_recovery_pass(log, head_blk, tail_blk,
+ XLOG_RECOVER_PASS2);
+#ifdef DEBUG
+ {
+ int i;
+
+ for (i = 0; i < XLOG_BC_TABLE_SIZE; i++)
+ ASSERT(log->l_buf_cancel_table[i] == NULL);
+ }
+#endif /* DEBUG */
+
+ kmem_free(log->l_buf_cancel_table,
+ XLOG_BC_TABLE_SIZE * sizeof(xfs_buf_cancel_t*));
+ log->l_buf_cancel_table = NULL;
+
+ return error;
+}
+
+/*
+ * Do the actual recovery
+ */
+STATIC int
+xlog_do_recover(
+ xlog_t *log,
+ xfs_daddr_t head_blk,
+ xfs_daddr_t tail_blk)
+{
+ int error;
+ xfs_buf_t *bp;
+ xfs_sb_t *sbp;
+
+ /*
+ * First replay the images in the log.
+ */
+ error = xlog_do_log_recovery(log, head_blk, tail_blk);
+ if (error) {
+ return error;
+ }
+
+ XFS_bflush(log->l_mp->m_ddev_targp);
+
+ /*
+ * If IO errors happened during recovery, bail out.
+ */
+ if (XFS_FORCED_SHUTDOWN(log->l_mp)) {
+ return (EIO);
+ }
+
+ /*
+ * We now update the tail_lsn since much of the recovery has completed
+ * and there may be space available to use. If there were no extent
+ * or iunlinks, we can free up the entire log and set the tail_lsn to
+ * be the last_sync_lsn. This was set in xlog_find_tail to be the
+ * lsn of the last known good LR on disk. If there are extent frees
+ * or iunlinks they will have some entries in the AIL; so we look at
+ * the AIL to determine how to set the tail_lsn.
+ */
+ xlog_assign_tail_lsn(log->l_mp);
+
+ /*
+ * Now that we've finished replaying all buffer and inode
+ * updates, re-read in the superblock.
+ */
+ bp = xfs_getsb(log->l_mp, 0);
+ XFS_BUF_UNDONE(bp);
+ XFS_BUF_READ(bp);
+ xfsbdstrat(log->l_mp, bp);
+ if ((error = xfs_iowait(bp))) {
+ xfs_ioerror_alert("xlog_do_recover",
+ log->l_mp, bp, XFS_BUF_ADDR(bp));
+ ASSERT(0);
+ xfs_buf_relse(bp);
+ return error;
+ }
+
+ /* Convert superblock from on-disk format */
+ sbp = &log->l_mp->m_sb;
+ xfs_xlatesb(XFS_BUF_TO_SBP(bp), sbp, 1, ARCH_CONVERT, XFS_SB_ALL_BITS);
+ ASSERT(sbp->sb_magicnum == XFS_SB_MAGIC);
+ ASSERT(XFS_SB_GOOD_VERSION(sbp));
+ xfs_buf_relse(bp);
+
+ xlog_recover_check_summary(log);
+
+ /* Normal transactions can now occur */
+ log->l_flags &= ~XLOG_ACTIVE_RECOVERY;
+ return 0;
+}
+
+/*
+ * Perform recovery and re-initialize some log variables in xlog_find_tail.
+ *
+ * Return error or zero.
+ */
+int
+xlog_recover(
+ xlog_t *log,
+ int readonly)
+{
+ xfs_daddr_t head_blk, tail_blk;
+ int error;
+
+ /* find the tail of the log */
+ if ((error = xlog_find_tail(log, &head_blk, &tail_blk, readonly)))
+ return error;
+
+ if (tail_blk != head_blk) {
+ /* There used to be a comment here:
+ *
+ * disallow recovery on read-only mounts. note -- mount
+ * checks for ENOSPC and turns it into an intelligent
+ * error message.
+ * ...but this is no longer true. Now, unless you specify
+ * NORECOVERY (in which case this function would never be
+ * called), we just go ahead and recover. We do this all
+ * under the vfs layer, so we can get away with it unless
+ * the device itself is read-only, in which case we fail.
+ */
+ if ((error = xfs_dev_is_read_only(log->l_mp,
+ "recovery required"))) {
+ return error;
+ }
+
+ cmn_err(CE_NOTE,
+ "Starting XFS recovery on filesystem: %s (dev: %s)",
+ log->l_mp->m_fsname, XFS_BUFTARG_NAME(log->l_targ));
+
+ error = xlog_do_recover(log, head_blk, tail_blk);
+ log->l_flags |= XLOG_RECOVERY_NEEDED;
+ }
+ return error;
+}
+
+/*
+ * In the first part of recovery we replay inodes and buffers and build
+ * up the list of extent free items which need to be processed. Here
+ * we process the extent free items and clean up the on disk unlinked
+ * inode lists. This is separated from the first part of recovery so
+ * that the root and real-time bitmap inodes can be read in from disk in
+ * between the two stages. This is necessary so that we can free space
+ * in the real-time portion of the file system.
+ */
+int
+xlog_recover_finish(
+ xlog_t *log,
+ int mfsi_flags)
+{
+ /*
+ * Now we're ready to do the transactions needed for the
+ * rest of recovery. Start with completing all the extent
+ * free intent records and then process the unlinked inode
+ * lists. At this point, we essentially run in normal mode
+ * except that we're still performing recovery actions
+ * rather than accepting new requests.
+ */
+ if (log->l_flags & XLOG_RECOVERY_NEEDED) {
+ xlog_recover_process_efis(log);
+ /*
+ * Sync the log to get all the EFIs out of the AIL.
+ * This isn't absolutely necessary, but it helps in
+ * case the unlink transactions would have problems
+ * pushing the EFIs out of the way.
+ */
+ xfs_log_force(log->l_mp, (xfs_lsn_t)0,
+ (XFS_LOG_FORCE | XFS_LOG_SYNC));
+
+ if ( (mfsi_flags & XFS_MFSI_NOUNLINK) == 0 ) {
+ xlog_recover_process_iunlinks(log);
+ }
+
+ xlog_recover_check_summary(log);
+
+ cmn_err(CE_NOTE,
+ "Ending XFS recovery on filesystem: %s (dev: %s)",
+ log->l_mp->m_fsname, XFS_BUFTARG_NAME(log->l_targ));
+ log->l_flags &= ~XLOG_RECOVERY_NEEDED;
+ } else {
+ cmn_err(CE_DEBUG,
+ "!Ending clean XFS mount for filesystem: %s",
+ log->l_mp->m_fsname);
+ }
+ return 0;
+}
+
+
+#if defined(DEBUG)
+/*
+ * Read all of the agf and agi counters and check that they
+ * are consistent with the superblock counters.
+ */
+void
+xlog_recover_check_summary(
+ xlog_t *log)
+{
+ xfs_mount_t *mp;
+ xfs_agf_t *agfp;
+ xfs_agi_t *agip;
+ xfs_buf_t *agfbp;
+ xfs_buf_t *agibp;
+ xfs_daddr_t agfdaddr;
+ xfs_daddr_t agidaddr;
+ xfs_buf_t *sbbp;
+#ifdef XFS_LOUD_RECOVERY
+ xfs_sb_t *sbp;
+#endif
+ xfs_agnumber_t agno;
+ __uint64_t freeblks;
+ __uint64_t itotal;
+ __uint64_t ifree;
+
+ mp = log->l_mp;
+
+ freeblks = 0LL;
+ itotal = 0LL;
+ ifree = 0LL;
+ for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) {
+ agfdaddr = XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp));
+ agfbp = xfs_buf_read(mp->m_ddev_targp, agfdaddr,
+ XFS_FSS_TO_BB(mp, 1), 0);
+ if (XFS_BUF_ISERROR(agfbp)) {
+ xfs_ioerror_alert("xlog_recover_check_summary(agf)",
+ mp, agfbp, agfdaddr);
+ }
+ agfp = XFS_BUF_TO_AGF(agfbp);
+ ASSERT(XFS_AGF_MAGIC ==
+ INT_GET(agfp->agf_magicnum, ARCH_CONVERT));
+ ASSERT(XFS_AGF_GOOD_VERSION(
+ INT_GET(agfp->agf_versionnum, ARCH_CONVERT)));
+ ASSERT(INT_GET(agfp->agf_seqno, ARCH_CONVERT) == agno);
+
+ freeblks += INT_GET(agfp->agf_freeblks, ARCH_CONVERT) +
+ INT_GET(agfp->agf_flcount, ARCH_CONVERT);
+ xfs_buf_relse(agfbp);
+
+ agidaddr = XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp));
+ agibp = xfs_buf_read(mp->m_ddev_targp, agidaddr,
+ XFS_FSS_TO_BB(mp, 1), 0);
+ if (XFS_BUF_ISERROR(agibp)) {
+ xfs_ioerror_alert("xlog_recover_check_summary(agi)",
+ mp, agibp, agidaddr);
+ }
+ agip = XFS_BUF_TO_AGI(agibp);
+ ASSERT(XFS_AGI_MAGIC ==
+ INT_GET(agip->agi_magicnum, ARCH_CONVERT));
+ ASSERT(XFS_AGI_GOOD_VERSION(
+ INT_GET(agip->agi_versionnum, ARCH_CONVERT)));
+ ASSERT(INT_GET(agip->agi_seqno, ARCH_CONVERT) == agno);
+
+ itotal += INT_GET(agip->agi_count, ARCH_CONVERT);
+ ifree += INT_GET(agip->agi_freecount, ARCH_CONVERT);
+ xfs_buf_relse(agibp);
+ }
+
+ sbbp = xfs_getsb(mp, 0);
+#ifdef XFS_LOUD_RECOVERY
+ sbp = &mp->m_sb;
+ xfs_xlatesb(XFS_BUF_TO_SBP(sbbp), sbp, 1, ARCH_CONVERT, XFS_SB_ALL_BITS);
+ cmn_err(CE_NOTE,
+ "xlog_recover_check_summary: sb_icount %Lu itotal %Lu",
+ sbp->sb_icount, itotal);
+ cmn_err(CE_NOTE,
+ "xlog_recover_check_summary: sb_ifree %Lu itotal %Lu",
+ sbp->sb_ifree, ifree);
+ cmn_err(CE_NOTE,
+ "xlog_recover_check_summary: sb_fdblocks %Lu freeblks %Lu",
+ sbp->sb_fdblocks, freeblks);
+#if 0
+ /*
+ * This is turned off until I account for the allocation
+ * btree blocks which live in free space.
+ */
+ ASSERT(sbp->sb_icount == itotal);
+ ASSERT(sbp->sb_ifree == ifree);
+ ASSERT(sbp->sb_fdblocks == freeblks);
+#endif
+#endif
+ xfs_buf_relse(sbbp);
+}
+#endif /* DEBUG */
diff --git a/fs/xfs/xfs_log_recover.h b/fs/xfs/xfs_log_recover.h
new file mode 100644
index 00000000000000..42158b442b5506
--- /dev/null
+++ b/fs/xfs/xfs_log_recover.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_LOG_RECOVER_H__
+#define __XFS_LOG_RECOVER_H__
+
+/*
+ * Macros, structures, prototypes for internal log manager use.
+ */
+
+#define XLOG_RHASH_BITS 4
+#define XLOG_RHASH_SIZE 16
+#define XLOG_RHASH_SHIFT 2
+#define XLOG_RHASH(tid) \
+ ((((__uint32_t)tid)>>XLOG_RHASH_SHIFT) & (XLOG_RHASH_SIZE-1))
+
+#define XLOG_MAX_REGIONS_IN_ITEM (XFS_MAX_BLOCKSIZE / XFS_BLI_CHUNK / 2 + 1)
+
+
+/*
+ * item headers are in ri_buf[0]. Additional buffers follow.
+ */
+typedef struct xlog_recover_item {
+ struct xlog_recover_item *ri_next;
+ struct xlog_recover_item *ri_prev;
+ int ri_type;
+ int ri_cnt; /* count of regions found */
+ int ri_total; /* total regions */
+ xfs_log_iovec_t *ri_buf; /* ptr to regions buffer */
+} xlog_recover_item_t;
+
+struct xlog_tid;
+typedef struct xlog_recover {
+ struct xlog_recover *r_next;
+ xlog_tid_t r_log_tid; /* log's transaction id */
+ xfs_trans_header_t r_theader; /* trans header for partial */
+ int r_state; /* not needed */
+ xfs_lsn_t r_lsn; /* xact lsn */
+ xlog_recover_item_t *r_itemq; /* q for items */
+} xlog_recover_t;
+
+#define ITEM_TYPE(i) (*(ushort *)(i)->ri_buf[0].i_addr)
+
+/*
+ * This is the number of entries in the l_buf_cancel_table used during
+ * recovery.
+ */
+#define XLOG_BC_TABLE_SIZE 64
+
+#define XLOG_RECOVER_PASS1 1
+#define XLOG_RECOVER_PASS2 2
+
+#endif /* __XFS_LOG_RECOVER_H__ */
diff --git a/fs/xfs/xfs_mac.h b/fs/xfs/xfs_mac.h
new file mode 100644
index 00000000000000..8d59aaffeb8ec5
--- /dev/null
+++ b/fs/xfs/xfs_mac.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2001-2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_MAC_H__
+#define __XFS_MAC_H__
+
+/*
+ * Mandatory Access Control
+ *
+ * Layout of a composite MAC label:
+ * ml_list contains the list of categories (MSEN) followed by the list of
+ * divisions (MINT). This is actually a header for the data structure which
+ * will have an ml_list with more than one element.
+ *
+ * -------------------------------
+ * | ml_msen_type | ml_mint_type |
+ * -------------------------------
+ * | ml_level | ml_grade |
+ * -------------------------------
+ * | ml_catcount |
+ * -------------------------------
+ * | ml_divcount |
+ * -------------------------------
+ * | category 1 |
+ * | . . . |
+ * | category N | (where N = ml_catcount)
+ * -------------------------------
+ * | division 1 |
+ * | . . . |
+ * | division M | (where M = ml_divcount)
+ * -------------------------------
+ */
+#define XFS_MAC_MAX_SETS 250
+typedef struct xfs_mac_label {
+ __uint8_t ml_msen_type; /* MSEN label type */
+ __uint8_t ml_mint_type; /* MINT label type */
+ __uint8_t ml_level; /* Hierarchical level */
+ __uint8_t ml_grade; /* Hierarchical grade */
+ __uint16_t ml_catcount; /* Category count */
+ __uint16_t ml_divcount; /* Division count */
+ /* Category set, then Division set */
+ __uint16_t ml_list[XFS_MAC_MAX_SETS];
+} xfs_mac_label_t;
+
+/* MSEN label type names. Choose an upper case ASCII character. */
+#define XFS_MSEN_ADMIN_LABEL 'A' /* Admin: low<admin != tcsec<high */
+#define XFS_MSEN_EQUAL_LABEL 'E' /* Wildcard - always equal */
+#define XFS_MSEN_HIGH_LABEL 'H' /* System High - always dominates */
+#define XFS_MSEN_MLD_HIGH_LABEL 'I' /* System High, multi-level dir */
+#define XFS_MSEN_LOW_LABEL 'L' /* System Low - always dominated */
+#define XFS_MSEN_MLD_LABEL 'M' /* TCSEC label on a multi-level dir */
+#define XFS_MSEN_MLD_LOW_LABEL 'N' /* System Low, multi-level dir */
+#define XFS_MSEN_TCSEC_LABEL 'T' /* TCSEC label */
+#define XFS_MSEN_UNKNOWN_LABEL 'U' /* unknown label */
+
+/* MINT label type names. Choose a lower case ASCII character. */
+#define XFS_MINT_BIBA_LABEL 'b' /* Dual of a TCSEC label */
+#define XFS_MINT_EQUAL_LABEL 'e' /* Wildcard - always equal */
+#define XFS_MINT_HIGH_LABEL 'h' /* High Grade - always dominates */
+#define XFS_MINT_LOW_LABEL 'l' /* Low Grade - always dominated */
+
+/* On-disk XFS extended attribute names */
+#define SGI_MAC_FILE "SGI_MAC_FILE"
+#define SGI_MAC_FILE_SIZE (sizeof(SGI_MAC_FILE)-1)
+
+
+#ifdef __KERNEL__
+
+#ifdef CONFIG_FS_POSIX_MAC
+
+/* NOT YET IMPLEMENTED */
+
+#define MACEXEC 00100
+#define MACWRITE 00200
+#define MACREAD 00400
+
+struct xfs_inode;
+extern int xfs_mac_iaccess(struct xfs_inode *, mode_t, cred_t *);
+
+#define _MAC_XFS_IACCESS(i,m,c) (xfs_mac_iaccess(i,m,c))
+#define _MAC_VACCESS(v,c,m) (xfs_mac_vaccess(v,c,m))
+#define _MAC_EXISTS xfs_mac_vhaslabel
+
+#else
+#define _MAC_XFS_IACCESS(i,m,c) (0)
+#define _MAC_VACCESS(v,c,m) (0)
+#define _MAC_EXISTS (NULL)
+#endif
+
+#endif /* __KERNEL__ */
+
+#endif /* __XFS_MAC_H__ */
diff --git a/fs/xfs/xfs_macros.c b/fs/xfs/xfs_macros.c
new file mode 100644
index 00000000000000..626be21c1e282d
--- /dev/null
+++ b/fs/xfs/xfs_macros.c
@@ -0,0 +1,2245 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#define XFS_MACRO_C
+
+#include "xfs.h"
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_btree.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_ialloc.h"
+#include "xfs_inode_item.h"
+#include "xfs_inode.h"
+#include "xfs_bmap.h"
+#include "xfs_rw.h"
+#include "xfs_log_priv.h"
+#include "xfs_da_btree.h"
+#include "xfs_attr_leaf.h"
+#include "xfs_dir_leaf.h"
+#include "xfs_dir2_data.h"
+#include "xfs_dir2_leaf.h"
+#include "xfs_dir2_block.h"
+#include "xfs_dir2_node.h"
+#include "xfs_bit.h"
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_ISNULLDSTARTBLOCK)
+int
+isnulldstartblock(xfs_dfsbno_t x)
+{
+ return ISNULLDSTARTBLOCK(x);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_ISNULLSTARTBLOCK)
+int
+isnullstartblock(xfs_fsblock_t x)
+{
+ return ISNULLSTARTBLOCK(x);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_NULLSTARTBLOCK)
+xfs_fsblock_t
+nullstartblock(int k)
+{
+ return NULLSTARTBLOCK(k);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_STARTBLOCKVAL)
+xfs_filblks_t
+startblockval(xfs_fsblock_t x)
+{
+ return STARTBLOCKVAL(x);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_AG_CHECK_DADDR)
+void
+xfs_ag_check_daddr(xfs_mount_t *mp, xfs_daddr_t d, xfs_extlen_t len)
+{
+ XFS_AG_CHECK_DADDR(mp, d, len);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_AG_DADDR)
+xfs_daddr_t
+xfs_ag_daddr(xfs_mount_t *mp, xfs_agnumber_t agno, xfs_daddr_t d)
+{
+ return XFS_AG_DADDR(mp, agno, d);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_AG_MAXLEVELS)
+int
+xfs_ag_maxlevels(xfs_mount_t *mp)
+{
+ return XFS_AG_MAXLEVELS(mp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_AGB_TO_DADDR)
+xfs_daddr_t
+xfs_agb_to_daddr(xfs_mount_t *mp, xfs_agnumber_t agno, xfs_agblock_t agbno)
+{
+ return XFS_AGB_TO_DADDR(mp, agno, agbno);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_AGB_TO_FSB)
+xfs_fsblock_t
+xfs_agb_to_fsb(xfs_mount_t *mp, xfs_agnumber_t agno, xfs_agblock_t agbno)
+{
+ return XFS_AGB_TO_FSB(mp, agno, agbno);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_AGBLOCK_MAX)
+xfs_agblock_t
+xfs_agblock_max(xfs_agblock_t a, xfs_agblock_t b)
+{
+ return XFS_AGBLOCK_MAX(a, b);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_AGBLOCK_MIN)
+xfs_agblock_t
+xfs_agblock_min(xfs_agblock_t a, xfs_agblock_t b)
+{
+ return XFS_AGBLOCK_MIN(a, b);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_AGF_BLOCK)
+xfs_agblock_t
+xfs_agf_block(xfs_mount_t *mp)
+{
+ return XFS_AGF_BLOCK(mp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_AGF_GOOD_VERSION)
+int
+xfs_agf_good_version(unsigned v)
+{
+ return XFS_AGF_GOOD_VERSION(v);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_AGFL_BLOCK)
+xfs_agblock_t
+xfs_agfl_block(xfs_mount_t *mp)
+{
+ return XFS_AGFL_BLOCK(mp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_AGI_BLOCK)
+xfs_agblock_t
+xfs_agi_block(xfs_mount_t *mp)
+{
+ return XFS_AGI_BLOCK(mp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_AGI_GOOD_VERSION)
+int
+xfs_agi_good_version(unsigned v)
+{
+ return XFS_AGI_GOOD_VERSION(v);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_AGINO_TO_AGBNO)
+xfs_agblock_t
+xfs_agino_to_agbno(xfs_mount_t *mp, xfs_agino_t i)
+{
+ return XFS_AGINO_TO_AGBNO(mp, i);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_AGINO_TO_INO)
+xfs_ino_t
+xfs_agino_to_ino(xfs_mount_t *mp, xfs_agnumber_t a, xfs_agino_t i)
+{
+ return XFS_AGINO_TO_INO(mp, a, i);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_AGINO_TO_OFFSET)
+int
+xfs_agino_to_offset(xfs_mount_t *mp, xfs_agino_t i)
+{
+ return XFS_AGINO_TO_OFFSET(mp, i);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_ALLOC_BLOCK_MAXRECS)
+int
+xfs_alloc_block_maxrecs(int lev, xfs_btree_cur_t *cur)
+{
+ return XFS_ALLOC_BLOCK_MAXRECS(lev, cur);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_ALLOC_BLOCK_MINRECS)
+int
+xfs_alloc_block_minrecs(int lev, xfs_btree_cur_t *cur)
+{
+ return XFS_ALLOC_BLOCK_MINRECS(lev, cur);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_ALLOC_BLOCK_SIZE)
+/*ARGSUSED1*/
+int
+xfs_alloc_block_size(int lev, xfs_btree_cur_t *cur)
+{
+ return XFS_ALLOC_BLOCK_SIZE(lev, cur);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_ALLOC_KEY_ADDR)
+/*ARGSUSED3*/
+xfs_alloc_key_t *
+xfs_alloc_key_addr(xfs_alloc_block_t *bb, int i, xfs_btree_cur_t *cur)
+{
+ return XFS_ALLOC_KEY_ADDR(bb, i, cur);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_ALLOC_PTR_ADDR)
+xfs_alloc_ptr_t *
+xfs_alloc_ptr_addr(xfs_alloc_block_t *bb, int i, xfs_btree_cur_t *cur)
+{
+ return XFS_ALLOC_PTR_ADDR(bb, i, cur);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_ALLOC_REC_ADDR)
+/*ARGSUSED3*/
+xfs_alloc_rec_t *
+xfs_alloc_rec_addr(xfs_alloc_block_t *bb, int i, xfs_btree_cur_t *cur)
+{
+ return XFS_ALLOC_REC_ADDR(bb, i, cur);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_ATTR_LEAF_ENTSIZE_LOCAL)
+int
+xfs_attr_leaf_entsize_local(int nlen, int vlen)
+{
+ return XFS_ATTR_LEAF_ENTSIZE_LOCAL(nlen, vlen);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_ATTR_LEAF_ENTSIZE_LOCAL_MAX)
+int
+xfs_attr_leaf_entsize_local_max(int bsize)
+{
+ return XFS_ATTR_LEAF_ENTSIZE_LOCAL_MAX(bsize);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_ATTR_LEAF_ENTSIZE_REMOTE)
+int
+xfs_attr_leaf_entsize_remote(int nlen)
+{
+ return XFS_ATTR_LEAF_ENTSIZE_REMOTE(nlen);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_ATTR_LEAF_NAME)
+char *
+xfs_attr_leaf_name(xfs_attr_leafblock_t *leafp, int idx)
+{
+ return XFS_ATTR_LEAF_NAME(leafp, idx);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_ATTR_LEAF_NAME_LOCAL)
+xfs_attr_leaf_name_local_t *
+xfs_attr_leaf_name_local(xfs_attr_leafblock_t *leafp, int idx)
+{
+ return XFS_ATTR_LEAF_NAME_LOCAL(leafp, idx);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_ATTR_LEAF_NAME_REMOTE)
+xfs_attr_leaf_name_remote_t *
+xfs_attr_leaf_name_remote(xfs_attr_leafblock_t *leafp, int idx)
+{
+ return XFS_ATTR_LEAF_NAME_REMOTE(leafp, idx);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_ATTR_SF_ENTSIZE)
+int
+xfs_attr_sf_entsize(xfs_attr_sf_entry_t *sfep)
+{
+ return XFS_ATTR_SF_ENTSIZE(sfep);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_ATTR_SF_ENTSIZE_BYNAME)
+int
+xfs_attr_sf_entsize_byname(int nlen, int vlen)
+{
+ return XFS_ATTR_SF_ENTSIZE_BYNAME(nlen, vlen);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_ATTR_SF_NEXTENTRY)
+xfs_attr_sf_entry_t *
+xfs_attr_sf_nextentry(xfs_attr_sf_entry_t *sfep)
+{
+ return XFS_ATTR_SF_NEXTENTRY(sfep);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_ATTR_SF_TOTSIZE)
+int
+xfs_attr_sf_totsize(xfs_inode_t *dp)
+{
+ return XFS_ATTR_SF_TOTSIZE(dp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BHVTOI)
+xfs_inode_t *
+xfs_bhvtoi(bhv_desc_t *bhvp)
+{
+ return XFS_BHVTOI(bhvp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BHVTOM)
+xfs_mount_t *
+xfs_bhvtom(bhv_desc_t *bdp)
+{
+ return XFS_BHVTOM(bdp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_VFSTOM)
+xfs_mount_t *
+xfs_vfstom(vfs_t *vfs)
+{
+ return XFS_VFSTOM(vfs);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BM_MAXLEVELS)
+int
+xfs_bm_maxlevels(xfs_mount_t *mp, int w)
+{
+ return XFS_BM_MAXLEVELS(mp, w);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BMAP_BLOCK_DMAXRECS)
+int
+xfs_bmap_block_dmaxrecs(int lev, xfs_btree_cur_t *cur)
+{
+ return XFS_BMAP_BLOCK_DMAXRECS(lev, cur);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BMAP_BLOCK_DMINRECS)
+int
+xfs_bmap_block_dminrecs(int lev, xfs_btree_cur_t *cur)
+{
+ return XFS_BMAP_BLOCK_DMINRECS(lev, cur);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BMAP_BLOCK_DSIZE)
+int
+xfs_bmap_block_dsize(int lev, xfs_btree_cur_t *cur)
+{
+ return XFS_BMAP_BLOCK_DSIZE(lev, cur);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BMAP_BLOCK_IMAXRECS)
+int
+xfs_bmap_block_imaxrecs(int lev, xfs_btree_cur_t *cur)
+{
+ return XFS_BMAP_BLOCK_IMAXRECS(lev, cur);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BMAP_BLOCK_IMINRECS)
+int
+xfs_bmap_block_iminrecs(int lev, xfs_btree_cur_t *cur)
+{
+ return XFS_BMAP_BLOCK_IMINRECS(lev, cur);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BMAP_BLOCK_ISIZE)
+int
+xfs_bmap_block_isize(int lev, xfs_btree_cur_t *cur)
+{
+ return XFS_BMAP_BLOCK_ISIZE(lev, cur);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BMAP_BROOT_KEY_ADDR)
+/*ARGSUSED3*/
+xfs_bmbt_key_t *
+xfs_bmap_broot_key_addr(xfs_bmbt_block_t *bb, int i, int sz)
+{
+ return XFS_BMAP_BROOT_KEY_ADDR(bb, i, sz);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BMAP_BROOT_MAXRECS)
+int
+xfs_bmap_broot_maxrecs(int sz)
+{
+ return XFS_BMAP_BROOT_MAXRECS(sz);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BMAP_BROOT_NUMRECS)
+int
+xfs_bmap_broot_numrecs(xfs_bmdr_block_t *bb)
+{
+ return XFS_BMAP_BROOT_NUMRECS(bb);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BMAP_BROOT_PTR_ADDR)
+xfs_bmbt_ptr_t *
+xfs_bmap_broot_ptr_addr(xfs_bmbt_block_t *bb, int i, int sz)
+{
+ return XFS_BMAP_BROOT_PTR_ADDR(bb, i, sz);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BMAP_BROOT_REC_ADDR)
+/*ARGSUSED3*/
+xfs_bmbt_rec_t *
+xfs_bmap_broot_rec_addr(xfs_bmbt_block_t *bb, int i, int sz)
+{
+ return XFS_BMAP_BROOT_REC_ADDR(bb, i, sz);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BMAP_BROOT_SPACE)
+int
+xfs_bmap_broot_space(xfs_bmdr_block_t *bb)
+{
+ return XFS_BMAP_BROOT_SPACE(bb);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BMAP_BROOT_SPACE_CALC)
+int
+xfs_bmap_broot_space_calc(int nrecs)
+{
+ return XFS_BMAP_BROOT_SPACE_CALC(nrecs);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BMAP_IBLOCK_SIZE)
+/*ARGSUSED1*/
+int
+xfs_bmap_iblock_size(int lev, xfs_btree_cur_t *cur)
+{
+ return XFS_BMAP_IBLOCK_SIZE(lev, cur);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BMAP_INIT)
+void
+xfs_bmap_init(xfs_bmap_free_t *flp, xfs_fsblock_t *fbp)
+{
+ XFS_BMAP_INIT(flp, fbp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BMAP_KEY_DADDR)
+/*ARGSUSED3*/
+xfs_bmbt_key_t *
+xfs_bmap_key_daddr(xfs_bmbt_block_t *bb, int i, xfs_btree_cur_t *cur)
+{
+ return XFS_BMAP_KEY_DADDR(bb, i, cur);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BMAP_KEY_IADDR)
+/*ARGSUSED3*/
+xfs_bmbt_key_t *
+xfs_bmap_key_iaddr(xfs_bmbt_block_t *bb, int i, xfs_btree_cur_t *cur)
+{
+ return XFS_BMAP_KEY_IADDR(bb, i, cur);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BMAP_PTR_DADDR)
+xfs_bmbt_ptr_t *
+xfs_bmap_ptr_daddr(xfs_bmbt_block_t *bb, int i, xfs_btree_cur_t *cur)
+{
+ return XFS_BMAP_PTR_DADDR(bb, i, cur);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BMAP_PTR_IADDR)
+xfs_bmbt_ptr_t *
+xfs_bmap_ptr_iaddr(xfs_bmbt_block_t *bb, int i, xfs_btree_cur_t *cur)
+{
+ return XFS_BMAP_PTR_IADDR(bb, i, cur);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BMAP_RBLOCK_DSIZE)
+/*ARGSUSED1*/
+int
+xfs_bmap_rblock_dsize(int lev, xfs_btree_cur_t *cur)
+{
+ return XFS_BMAP_RBLOCK_DSIZE(lev, cur);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BMAP_RBLOCK_ISIZE)
+/*ARGSUSED1*/
+int
+xfs_bmap_rblock_isize(int lev, xfs_btree_cur_t *cur)
+{
+ return XFS_BMAP_RBLOCK_ISIZE(lev, cur);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BMAP_REC_DADDR)
+/*ARGSUSED3*/
+xfs_bmbt_rec_t *
+xfs_bmap_rec_daddr(xfs_bmbt_block_t *bb, int i, xfs_btree_cur_t *cur)
+{
+ return XFS_BMAP_REC_DADDR(bb, i, cur);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BMAP_REC_IADDR)
+/*ARGSUSED3*/
+xfs_bmbt_rec_t *
+xfs_bmap_rec_iaddr(xfs_bmbt_block_t *bb, int i, xfs_btree_cur_t *cur)
+{
+ return XFS_BMAP_REC_IADDR(bb, i, cur);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BMAP_SANITY_CHECK)
+int
+xfs_bmap_sanity_check(xfs_mount_t *mp, xfs_bmbt_block_t *bb, int level)
+{
+ return XFS_BMAP_SANITY_CHECK(mp, bb, level);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BMAPI_AFLAG)
+int
+xfs_bmapi_aflag(int w)
+{
+ return XFS_BMAPI_AFLAG(w);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BMDR_SPACE_CALC)
+int
+xfs_bmdr_space_calc(int nrecs)
+{
+ return XFS_BMDR_SPACE_CALC(nrecs);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BNO_BLOCK)
+xfs_agblock_t
+xfs_bno_block(xfs_mount_t *mp)
+{
+ return XFS_BNO_BLOCK(mp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BTREE_LONG_PTRS)
+int
+xfs_btree_long_ptrs(xfs_btnum_t btnum)
+{
+ return XFS_BTREE_LONG_PTRS(btnum);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BUF_TO_AGF)
+xfs_agf_t *
+xfs_buf_to_agf(xfs_buf_t *bp)
+{
+ return XFS_BUF_TO_AGF(bp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BUF_TO_AGFL)
+xfs_agfl_t *
+xfs_buf_to_agfl(xfs_buf_t *bp)
+{
+ return XFS_BUF_TO_AGFL(bp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BUF_TO_AGI)
+xfs_agi_t *
+xfs_buf_to_agi(xfs_buf_t *bp)
+{
+ return XFS_BUF_TO_AGI(bp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BUF_TO_ALLOC_BLOCK)
+xfs_alloc_block_t *
+xfs_buf_to_alloc_block(xfs_buf_t *bp)
+{
+ return XFS_BUF_TO_ALLOC_BLOCK(bp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BUF_TO_BLOCK)
+xfs_btree_block_t *
+xfs_buf_to_block(xfs_buf_t *bp)
+{
+ return XFS_BUF_TO_BLOCK(bp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BUF_TO_BMBT_BLOCK)
+xfs_bmbt_block_t *
+xfs_buf_to_bmbt_block(xfs_buf_t *bp)
+{
+ return XFS_BUF_TO_BMBT_BLOCK(bp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BUF_TO_DINODE)
+xfs_dinode_t *
+xfs_buf_to_dinode(xfs_buf_t *bp)
+{
+ return XFS_BUF_TO_DINODE(bp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BUF_TO_INOBT_BLOCK)
+xfs_inobt_block_t *
+xfs_buf_to_inobt_block(xfs_buf_t *bp)
+{
+ return XFS_BUF_TO_INOBT_BLOCK(bp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BUF_TO_LBLOCK)
+xfs_btree_lblock_t *
+xfs_buf_to_lblock(xfs_buf_t *bp)
+{
+ return XFS_BUF_TO_LBLOCK(bp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BUF_TO_SBLOCK)
+xfs_btree_sblock_t *
+xfs_buf_to_sblock(xfs_buf_t *bp)
+{
+ return XFS_BUF_TO_SBLOCK(bp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_BUF_TO_SBP)
+xfs_sb_t *
+xfs_buf_to_sbp(xfs_buf_t *bp)
+{
+ return XFS_BUF_TO_SBP(bp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_CFORK_ASIZE)
+int
+xfs_cfork_asize_arch(xfs_dinode_core_t *dcp, xfs_mount_t *mp, xfs_arch_t arch)
+{
+ return XFS_CFORK_ASIZE_ARCH(dcp, mp, arch);
+}
+int
+xfs_cfork_asize(xfs_dinode_core_t *dcp, xfs_mount_t *mp)
+{
+ return XFS_CFORK_ASIZE(dcp, mp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_CFORK_BOFF)
+int
+xfs_cfork_boff_arch(xfs_dinode_core_t *dcp, xfs_arch_t arch)
+{
+ return XFS_CFORK_BOFF_ARCH(dcp, arch);
+}
+int
+xfs_cfork_boff(xfs_dinode_core_t *dcp)
+{
+ return XFS_CFORK_BOFF(dcp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_CFORK_DSIZE)
+int
+xfs_cfork_dsize_arch(xfs_dinode_core_t *dcp, xfs_mount_t *mp, xfs_arch_t arch)
+{
+ return XFS_CFORK_DSIZE_ARCH(dcp, mp, arch);
+}
+int
+xfs_cfork_dsize(xfs_dinode_core_t *dcp, xfs_mount_t *mp)
+{
+ return XFS_CFORK_DSIZE(dcp, mp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_CFORK_FMT_SET)
+void
+xfs_cfork_fmt_set_arch(xfs_dinode_core_t *dcp, int w, int n, xfs_arch_t arch)
+{
+ XFS_CFORK_FMT_SET_ARCH(dcp, w, n, arch);
+}
+void
+xfs_cfork_fmt_set(xfs_dinode_core_t *dcp, int w, int n)
+{
+ XFS_CFORK_FMT_SET(dcp, w, n);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_CFORK_FORMAT)
+int
+xfs_cfork_format_arch(xfs_dinode_core_t *dcp, int w, xfs_arch_t arch)
+{
+ return XFS_CFORK_FORMAT_ARCH(dcp, w, arch);
+}
+int
+xfs_cfork_format(xfs_dinode_core_t *dcp, int w)
+{
+ return XFS_CFORK_FORMAT(dcp, w);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_CFORK_NEXT_SET)
+void
+xfs_cfork_next_set_arch(xfs_dinode_core_t *dcp, int w, int n, xfs_arch_t arch)
+{
+ XFS_CFORK_NEXT_SET_ARCH(dcp, w, n, arch);
+}
+void
+xfs_cfork_next_set(xfs_dinode_core_t *dcp, int w, int n)
+{
+ XFS_CFORK_NEXT_SET(dcp, w, n);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_CFORK_NEXTENTS)
+int
+xfs_cfork_nextents_arch(xfs_dinode_core_t *dcp, int w, xfs_arch_t arch)
+{
+ return XFS_CFORK_NEXTENTS_ARCH(dcp, w, arch);
+}
+int
+xfs_cfork_nextents(xfs_dinode_core_t *dcp, int w)
+{
+ return XFS_CFORK_NEXTENTS(dcp, w);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_CFORK_Q)
+int
+xfs_cfork_q_arch(xfs_dinode_core_t *dcp, xfs_arch_t arch)
+{
+ return XFS_CFORK_Q_ARCH(dcp, arch);
+}
+int
+xfs_cfork_q(xfs_dinode_core_t *dcp)
+{
+ return XFS_CFORK_Q(dcp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_CFORK_SIZE)
+int
+xfs_cfork_size_arch(xfs_dinode_core_t *dcp, xfs_mount_t *mp, int w, xfs_arch_t arch)
+{
+ return XFS_CFORK_SIZE_ARCH(dcp, mp, w, arch);
+}
+int
+xfs_cfork_size(xfs_dinode_core_t *dcp, xfs_mount_t *mp, int w)
+{
+ return XFS_CFORK_SIZE(dcp, mp, w);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_CNT_BLOCK)
+xfs_agblock_t
+xfs_cnt_block(xfs_mount_t *mp)
+{
+ return XFS_CNT_BLOCK(mp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DA_COOKIE_BNO)
+xfs_dablk_t
+xfs_da_cookie_bno(xfs_mount_t *mp, xfs_off_t cookie)
+{
+ return XFS_DA_COOKIE_BNO(mp, cookie);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DA_COOKIE_ENTRY)
+int
+xfs_da_cookie_entry(xfs_mount_t *mp, xfs_off_t cookie)
+{
+ return XFS_DA_COOKIE_ENTRY(mp, cookie);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DA_COOKIE_HASH)
+/*ARGSUSED1*/
+xfs_dahash_t
+xfs_da_cookie_hash(xfs_mount_t *mp, xfs_off_t cookie)
+{
+ return XFS_DA_COOKIE_HASH(mp, cookie);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DA_MAKE_BNOENTRY)
+__uint32_t
+xfs_da_make_bnoentry(xfs_mount_t *mp, xfs_dablk_t bno, int entry)
+{
+ return XFS_DA_MAKE_BNOENTRY(mp, bno, entry);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DA_MAKE_COOKIE)
+xfs_off_t
+xfs_da_make_cookie(xfs_mount_t *mp, xfs_dablk_t bno, int entry,
+ xfs_dahash_t hash)
+{
+ return XFS_DA_MAKE_COOKIE(mp, bno, entry, hash);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DADDR_TO_AGBNO)
+xfs_agblock_t
+xfs_daddr_to_agbno(xfs_mount_t *mp, xfs_daddr_t d)
+{
+ return XFS_DADDR_TO_AGBNO(mp, d);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DADDR_TO_AGNO)
+xfs_agnumber_t
+xfs_daddr_to_agno(xfs_mount_t *mp, xfs_daddr_t d)
+{
+ return XFS_DADDR_TO_AGNO(mp, d);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DADDR_TO_FSB)
+xfs_fsblock_t
+xfs_daddr_to_fsb(xfs_mount_t *mp, xfs_daddr_t d)
+{
+ return XFS_DADDR_TO_FSB(mp, d);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DFORK_APTR)
+char *
+xfs_dfork_aptr_arch(xfs_dinode_t *dip, xfs_arch_t arch)
+{
+ return XFS_DFORK_APTR_ARCH(dip, arch);
+}
+char *
+xfs_dfork_aptr(xfs_dinode_t *dip)
+{
+ return XFS_DFORK_APTR(dip);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DFORK_ASIZE)
+int
+xfs_dfork_asize_arch(xfs_dinode_t *dip, xfs_mount_t *mp, xfs_arch_t arch)
+{
+ return XFS_DFORK_ASIZE_ARCH(dip, mp, arch);
+}
+int
+xfs_dfork_asize(xfs_dinode_t *dip, xfs_mount_t *mp)
+{
+ return XFS_DFORK_ASIZE(dip, mp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DFORK_BOFF)
+int
+xfs_dfork_boff_arch(xfs_dinode_t *dip, xfs_arch_t arch)
+{
+ return XFS_DFORK_BOFF_ARCH(dip, arch);
+}
+int
+xfs_dfork_boff(xfs_dinode_t *dip)
+{
+ return XFS_DFORK_BOFF(dip);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DFORK_DPTR)
+char *
+xfs_dfork_dptr_arch(xfs_dinode_t *dip, xfs_arch_t arch)
+{
+ return XFS_DFORK_DPTR_ARCH(dip, arch);
+}
+char *
+xfs_dfork_dptr(xfs_dinode_t *dip)
+{
+ return XFS_DFORK_DPTR(dip);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DFORK_DSIZE)
+int
+xfs_dfork_dsize_arch(xfs_dinode_t *dip, xfs_mount_t *mp, xfs_arch_t arch)
+{
+ return XFS_DFORK_DSIZE_ARCH(dip, mp, arch);
+}
+int
+xfs_dfork_dsize(xfs_dinode_t *dip, xfs_mount_t *mp)
+{
+ return XFS_DFORK_DSIZE(dip, mp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DFORK_FMT_SET)
+void
+xfs_dfork_fmt_set_arch(xfs_dinode_t *dip, int w, int n, xfs_arch_t arch)
+{
+ XFS_DFORK_FMT_SET_ARCH(dip, w, n, arch);
+}
+void
+xfs_dfork_fmt_set(xfs_dinode_t *dip, int w, int n)
+{
+ XFS_DFORK_FMT_SET(dip, w, n);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DFORK_FORMAT)
+int
+xfs_dfork_format_arch(xfs_dinode_t *dip, int w, xfs_arch_t arch)
+{
+ return XFS_DFORK_FORMAT_ARCH(dip, w, arch);
+}
+int
+xfs_dfork_format(xfs_dinode_t *dip, int w)
+{
+ return XFS_DFORK_FORMAT(dip, w);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DFORK_NEXT_SET)
+void
+xfs_dfork_next_set_arch(xfs_dinode_t *dip, int w, int n, xfs_arch_t arch)
+{
+ XFS_DFORK_NEXT_SET_ARCH(dip, w, n, arch);
+}
+void
+xfs_dfork_next_set(xfs_dinode_t *dip, int w, int n)
+{
+ XFS_DFORK_NEXT_SET(dip, w, n);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DFORK_NEXTENTS)
+int
+xfs_dfork_nextents_arch(xfs_dinode_t *dip, int w, xfs_arch_t arch)
+{
+ return XFS_DFORK_NEXTENTS_ARCH(dip, w, arch);
+}
+int
+xfs_dfork_nextents(xfs_dinode_t *dip, int w)
+{
+ return XFS_DFORK_NEXTENTS(dip, w);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DFORK_PTR)
+char *
+xfs_dfork_ptr_arch(xfs_dinode_t *dip, int w, xfs_arch_t arch)
+{
+ return XFS_DFORK_PTR_ARCH(dip, w, arch);
+}
+char *
+xfs_dfork_ptr(xfs_dinode_t *dip, int w)
+{
+ return XFS_DFORK_PTR(dip, w);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DFORK_Q)
+int
+xfs_dfork_q_arch(xfs_dinode_t *dip, xfs_arch_t arch)
+{
+ return XFS_DFORK_Q_ARCH(dip, arch);
+}
+int
+xfs_dfork_q(xfs_dinode_t *dip)
+{
+ return XFS_DFORK_Q(dip);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DFORK_SIZE)
+int
+xfs_dfork_size_arch(xfs_dinode_t *dip, xfs_mount_t *mp, int w, xfs_arch_t arch)
+{
+ return XFS_DFORK_SIZE_ARCH(dip, mp, w, arch);
+}
+int
+xfs_dfork_size(xfs_dinode_t *dip, xfs_mount_t *mp, int w)
+{
+ return XFS_DFORK_SIZE(dip, mp, w);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DINODE_GOOD_VERSION)
+int
+xfs_dinode_good_version(int v)
+{
+ return XFS_DINODE_GOOD_VERSION(v);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR_LEAF_ENTSIZE_BYENTRY)
+int
+xfs_dir_leaf_entsize_byentry(xfs_dir_leaf_entry_t *entry)
+{
+ return XFS_DIR_LEAF_ENTSIZE_BYENTRY(entry);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR_LEAF_ENTSIZE_BYNAME)
+int
+xfs_dir_leaf_entsize_byname(int len)
+{
+ return XFS_DIR_LEAF_ENTSIZE_BYNAME(len);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR_LEAF_NAMESTRUCT)
+xfs_dir_leaf_name_t *
+xfs_dir_leaf_namestruct(xfs_dir_leafblock_t *leafp, int offset)
+{
+ return XFS_DIR_LEAF_NAMESTRUCT(leafp, offset);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR_SF_ALLFIT)
+int
+xfs_dir_sf_allfit(int count, int totallen)
+{
+ return XFS_DIR_SF_ALLFIT(count, totallen);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR_SF_ENTSIZE_BYENTRY)
+int
+xfs_dir_sf_entsize_byentry(xfs_dir_sf_entry_t *sfep)
+{
+ return XFS_DIR_SF_ENTSIZE_BYENTRY(sfep);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR_SF_ENTSIZE_BYNAME)
+int
+xfs_dir_sf_entsize_byname(int len)
+{
+ return XFS_DIR_SF_ENTSIZE_BYNAME(len);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR_SF_GET_DIRINO)
+void
+xfs_dir_sf_get_dirino_arch(xfs_dir_ino_t *from, xfs_ino_t *to, xfs_arch_t arch)
+{
+ XFS_DIR_SF_GET_DIRINO_ARCH(from, to, arch);
+}
+void
+xfs_dir_sf_get_dirino(xfs_dir_ino_t *from, xfs_ino_t *to)
+{
+ XFS_DIR_SF_GET_DIRINO(from, to);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR_SF_NEXTENTRY)
+xfs_dir_sf_entry_t *
+xfs_dir_sf_nextentry(xfs_dir_sf_entry_t *sfep)
+{
+ return XFS_DIR_SF_NEXTENTRY(sfep);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR_SF_PUT_DIRINO)
+void
+xfs_dir_sf_put_dirino_arch(xfs_ino_t *from, xfs_dir_ino_t *to, xfs_arch_t arch)
+{
+ XFS_DIR_SF_PUT_DIRINO_ARCH(from, to, arch);
+}
+void
+xfs_dir_sf_put_dirino(xfs_ino_t *from, xfs_dir_ino_t *to)
+{
+ XFS_DIR_SF_PUT_DIRINO(from, to);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR2_BLOCK_LEAF_P)
+xfs_dir2_leaf_entry_t *
+xfs_dir2_block_leaf_p_arch(xfs_dir2_block_tail_t *btp, xfs_arch_t arch)
+{
+ return XFS_DIR2_BLOCK_LEAF_P_ARCH(btp,arch);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR2_BLOCK_TAIL_P)
+xfs_dir2_block_tail_t *
+xfs_dir2_block_tail_p(xfs_mount_t *mp, xfs_dir2_block_t *block)
+{
+ return XFS_DIR2_BLOCK_TAIL_P(mp, block);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR2_BYTE_TO_DA)
+xfs_dablk_t
+xfs_dir2_byte_to_da(xfs_mount_t *mp, xfs_dir2_off_t by)
+{
+ return XFS_DIR2_BYTE_TO_DA(mp, by);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR2_BYTE_TO_DATAPTR)
+/* ARGSUSED */
+xfs_dir2_dataptr_t
+xfs_dir2_byte_to_dataptr(xfs_mount_t *mp, xfs_dir2_off_t by)
+{
+ return XFS_DIR2_BYTE_TO_DATAPTR(mp, by);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR2_BYTE_TO_DB)
+xfs_dir2_db_t
+xfs_dir2_byte_to_db(xfs_mount_t *mp, xfs_dir2_off_t by)
+{
+ return XFS_DIR2_BYTE_TO_DB(mp, by);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR2_BYTE_TO_OFF)
+xfs_dir2_data_aoff_t
+xfs_dir2_byte_to_off(xfs_mount_t *mp, xfs_dir2_off_t by)
+{
+ return XFS_DIR2_BYTE_TO_OFF(mp, by);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR2_DA_TO_BYTE)
+xfs_dir2_off_t
+xfs_dir2_da_to_byte(xfs_mount_t *mp, xfs_dablk_t da)
+{
+ return XFS_DIR2_DA_TO_BYTE(mp, da);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR2_DA_TO_DB)
+xfs_dir2_db_t
+xfs_dir2_da_to_db(xfs_mount_t *mp, xfs_dablk_t da)
+{
+ return XFS_DIR2_DA_TO_DB(mp, da);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR2_DATA_ENTRY_TAG_P)
+xfs_dir2_data_off_t *
+xfs_dir2_data_entry_tag_p(xfs_dir2_data_entry_t *dep)
+{
+ return XFS_DIR2_DATA_ENTRY_TAG_P(dep);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR2_DATA_ENTSIZE)
+int
+xfs_dir2_data_entsize(int n)
+{
+ return XFS_DIR2_DATA_ENTSIZE(n);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR2_DATA_UNUSED_TAG_P)
+xfs_dir2_data_off_t *
+xfs_dir2_data_unused_tag_p_arch(xfs_dir2_data_unused_t *dup, xfs_arch_t arch)
+{
+ return XFS_DIR2_DATA_UNUSED_TAG_P_ARCH(dup,arch);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR2_DATAPTR_TO_BYTE)
+/* ARGSUSED */
+xfs_dir2_off_t
+xfs_dir2_dataptr_to_byte(xfs_mount_t *mp, xfs_dir2_dataptr_t dp)
+{
+ return XFS_DIR2_DATAPTR_TO_BYTE(mp, dp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR2_DATAPTR_TO_DB)
+xfs_dir2_db_t
+xfs_dir2_dataptr_to_db(xfs_mount_t *mp, xfs_dir2_dataptr_t dp)
+{
+ return XFS_DIR2_DATAPTR_TO_DB(mp, dp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR2_DATAPTR_TO_OFF)
+xfs_dir2_data_aoff_t
+xfs_dir2_dataptr_to_off(xfs_mount_t *mp, xfs_dir2_dataptr_t dp)
+{
+ return XFS_DIR2_DATAPTR_TO_OFF(mp, dp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR2_DB_OFF_TO_BYTE)
+xfs_dir2_off_t
+xfs_dir2_db_off_to_byte(xfs_mount_t *mp, xfs_dir2_db_t db,
+ xfs_dir2_data_aoff_t o)
+{
+ return XFS_DIR2_DB_OFF_TO_BYTE(mp, db, o);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR2_DB_OFF_TO_DATAPTR)
+xfs_dir2_dataptr_t
+xfs_dir2_db_off_to_dataptr(xfs_mount_t *mp, xfs_dir2_db_t db,
+ xfs_dir2_data_aoff_t o)
+{
+ return XFS_DIR2_DB_OFF_TO_DATAPTR(mp, db, o);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR2_DB_TO_DA)
+xfs_dablk_t
+xfs_dir2_db_to_da(xfs_mount_t *mp, xfs_dir2_db_t db)
+{
+ return XFS_DIR2_DB_TO_DA(mp, db);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR2_DB_TO_FDB)
+xfs_dir2_db_t
+xfs_dir2_db_to_fdb(xfs_mount_t *mp, xfs_dir2_db_t db)
+{
+ return XFS_DIR2_DB_TO_FDB(mp, db);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR2_DB_TO_FDINDEX)
+int
+xfs_dir2_db_to_fdindex(xfs_mount_t *mp, xfs_dir2_db_t db)
+{
+ return XFS_DIR2_DB_TO_FDINDEX(mp, db);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR2_LEAF_BESTS_P)
+xfs_dir2_data_off_t *
+xfs_dir2_leaf_bests_p_arch(xfs_dir2_leaf_tail_t *ltp, xfs_arch_t arch)
+{
+ return XFS_DIR2_LEAF_BESTS_P_ARCH(ltp, arch);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR2_LEAF_TAIL_P)
+xfs_dir2_leaf_tail_t *
+xfs_dir2_leaf_tail_p(xfs_mount_t *mp, xfs_dir2_leaf_t *lp)
+{
+ return XFS_DIR2_LEAF_TAIL_P(mp, lp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR2_MAX_LEAF_ENTS)
+int
+xfs_dir2_max_leaf_ents(xfs_mount_t *mp)
+{
+ return XFS_DIR2_MAX_LEAF_ENTS(mp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR2_SF_ENTSIZE_BYENTRY)
+int
+xfs_dir2_sf_entsize_byentry(xfs_dir2_sf_t *sfp, xfs_dir2_sf_entry_t *sfep)
+{
+ return XFS_DIR2_SF_ENTSIZE_BYENTRY(sfp, sfep);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR2_SF_FIRSTENTRY)
+xfs_dir2_sf_entry_t *
+xfs_dir2_sf_firstentry(xfs_dir2_sf_t *sfp)
+{
+ return XFS_DIR2_SF_FIRSTENTRY(sfp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR2_SF_ENTSIZE_BYNAME)
+int
+xfs_dir2_sf_entsize_byname(xfs_dir2_sf_t *sfp, int len)
+{
+ return XFS_DIR2_SF_ENTSIZE_BYNAME(sfp, len);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR2_SF_GET_INUMBER)
+xfs_intino_t
+xfs_dir2_sf_get_inumber_arch(xfs_dir2_sf_t *sfp, xfs_dir2_inou_t *from, xfs_arch_t arch)
+{
+ return XFS_DIR2_SF_GET_INUMBER_ARCH(sfp, from, arch);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR2_SF_GET_OFFSET)
+xfs_dir2_data_aoff_t
+xfs_dir2_sf_get_offset_arch(xfs_dir2_sf_entry_t *sfep, xfs_arch_t arch)
+{
+ return XFS_DIR2_SF_GET_OFFSET_ARCH(sfep, arch);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR2_SF_HDR_SIZE)
+int
+xfs_dir2_sf_hdr_size(int i8count)
+{
+ return XFS_DIR2_SF_HDR_SIZE(i8count);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR2_SF_INUMBERP)
+xfs_dir2_inou_t *
+xfs_dir2_sf_inumberp(xfs_dir2_sf_entry_t *sfep)
+{
+ return XFS_DIR2_SF_INUMBERP(sfep);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR2_SF_NEXTENTRY)
+xfs_dir2_sf_entry_t *
+xfs_dir2_sf_nextentry(xfs_dir2_sf_t *sfp, xfs_dir2_sf_entry_t *sfep)
+{
+ return XFS_DIR2_SF_NEXTENTRY(sfp, sfep);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR2_SF_PUT_INUMBER)
+void
+xfs_dir2_sf_put_inumber_arch(xfs_dir2_sf_t *sfp, xfs_ino_t *from, xfs_dir2_inou_t *to, xfs_arch_t arch)
+{
+ XFS_DIR2_SF_PUT_INUMBER_ARCH(sfp, from, to, arch);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_DIR2_SF_PUT_OFFSET)
+void
+xfs_dir2_sf_put_offset_arch(xfs_dir2_sf_entry_t *sfep, xfs_dir2_data_aoff_t off, xfs_arch_t arch)
+{
+ XFS_DIR2_SF_PUT_OFFSET_ARCH(sfep, off, arch);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_EXTFMT_INODE )
+xfs_exntfmt_t
+xfs_extfmt_inode(struct xfs_inode *ip)
+{
+ return XFS_EXTFMT_INODE(ip);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_EXTLEN_MAX)
+xfs_extlen_t
+xfs_extlen_max(xfs_extlen_t a, xfs_extlen_t b)
+{
+ return XFS_EXTLEN_MAX(a, b);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_EXTLEN_MIN)
+xfs_extlen_t
+xfs_extlen_min(xfs_extlen_t a, xfs_extlen_t b)
+{
+ return XFS_EXTLEN_MIN(a, b);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_FILBLKS_MAX)
+xfs_filblks_t
+xfs_filblks_max(xfs_filblks_t a, xfs_filblks_t b)
+{
+ return XFS_FILBLKS_MAX(a, b);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_FILBLKS_MIN)
+xfs_filblks_t
+xfs_filblks_min(xfs_filblks_t a, xfs_filblks_t b)
+{
+ return XFS_FILBLKS_MIN(a, b);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_FILEOFF_MAX)
+xfs_fileoff_t
+xfs_fileoff_max(xfs_fileoff_t a, xfs_fileoff_t b)
+{
+ return XFS_FILEOFF_MAX(a, b);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_FILEOFF_MIN)
+xfs_fileoff_t
+xfs_fileoff_min(xfs_fileoff_t a, xfs_fileoff_t b)
+{
+ return XFS_FILEOFF_MIN(a, b);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_FSB_SANITY_CHECK)
+int
+xfs_fsb_sanity_check(xfs_mount_t *mp, xfs_fsblock_t fsbno)
+{
+ return XFS_FSB_SANITY_CHECK(mp, fsbno);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_FSB_TO_AGBNO)
+xfs_agblock_t
+xfs_fsb_to_agbno(xfs_mount_t *mp, xfs_fsblock_t fsbno)
+{
+ return XFS_FSB_TO_AGBNO(mp, fsbno);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_FSB_TO_AGNO)
+xfs_agnumber_t
+xfs_fsb_to_agno(xfs_mount_t *mp, xfs_fsblock_t fsbno)
+{
+ return XFS_FSB_TO_AGNO(mp, fsbno);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_FSB_TO_DADDR)
+xfs_daddr_t
+xfs_fsb_to_daddr(xfs_mount_t *mp, xfs_fsblock_t fsbno)
+{
+ return XFS_FSB_TO_DADDR(mp, fsbno);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_FSB_TO_DB)
+xfs_daddr_t
+xfs_fsb_to_db(xfs_inode_t *ip, xfs_fsblock_t fsb)
+{
+ return XFS_FSB_TO_DB(ip, fsb);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_HDR_BLOCK)
+xfs_agblock_t
+xfs_hdr_block(xfs_mount_t *mp, xfs_daddr_t d)
+{
+ return XFS_HDR_BLOCK(mp, d);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_IALLOC_BLOCKS)
+xfs_extlen_t
+xfs_ialloc_blocks(xfs_mount_t *mp)
+{
+ return XFS_IALLOC_BLOCKS(mp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_IALLOC_FIND_FREE)
+int
+xfs_ialloc_find_free(xfs_inofree_t *fp)
+{
+ return XFS_IALLOC_FIND_FREE(fp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_IALLOC_INODES)
+int
+xfs_ialloc_inodes(xfs_mount_t *mp)
+{
+ return XFS_IALLOC_INODES(mp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_IBT_BLOCK)
+xfs_agblock_t
+xfs_ibt_block(xfs_mount_t *mp)
+{
+ return XFS_IBT_BLOCK(mp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_IFORK_ASIZE)
+int
+xfs_ifork_asize(xfs_inode_t *ip)
+{
+ return XFS_IFORK_ASIZE(ip);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_IFORK_DSIZE)
+int
+xfs_ifork_dsize(xfs_inode_t *ip)
+{
+ return XFS_IFORK_DSIZE(ip);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_IFORK_FMT_SET)
+void
+xfs_ifork_fmt_set(xfs_inode_t *ip, int w, int n)
+{
+ XFS_IFORK_FMT_SET(ip, w, n);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_IFORK_FORMAT)
+int
+xfs_ifork_format(xfs_inode_t *ip, int w)
+{
+ return XFS_IFORK_FORMAT(ip, w);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_IFORK_NEXT_SET)
+void
+xfs_ifork_next_set(xfs_inode_t *ip, int w, int n)
+{
+ XFS_IFORK_NEXT_SET(ip, w, n);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_IFORK_NEXTENTS)
+int
+xfs_ifork_nextents(xfs_inode_t *ip, int w)
+{
+ return XFS_IFORK_NEXTENTS(ip, w);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_IFORK_PTR)
+xfs_ifork_t *
+xfs_ifork_ptr(xfs_inode_t *ip, int w)
+{
+ return XFS_IFORK_PTR(ip, w);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_IFORK_Q)
+int
+xfs_ifork_q(xfs_inode_t *ip)
+{
+ return XFS_IFORK_Q(ip);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_IFORK_SIZE)
+int
+xfs_ifork_size(xfs_inode_t *ip, int w)
+{
+ return XFS_IFORK_SIZE(ip, w);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_ILOG_FBROOT)
+int
+xfs_ilog_fbroot(int w)
+{
+ return XFS_ILOG_FBROOT(w);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_ILOG_FDATA)
+int
+xfs_ilog_fdata(int w)
+{
+ return XFS_ILOG_FDATA(w);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_ILOG_FEXT)
+int
+xfs_ilog_fext(int w)
+{
+ return XFS_ILOG_FEXT(w);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_IN_MAXLEVELS)
+int
+xfs_in_maxlevels(xfs_mount_t *mp)
+{
+ return XFS_IN_MAXLEVELS(mp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_INO_AGBNO_BITS)
+int
+xfs_ino_agbno_bits(xfs_mount_t *mp)
+{
+ return XFS_INO_AGBNO_BITS(mp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_INO_AGINO_BITS)
+int
+xfs_ino_agino_bits(xfs_mount_t *mp)
+{
+ return XFS_INO_AGINO_BITS(mp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_INO_AGNO_BITS)
+int
+xfs_ino_agno_bits(xfs_mount_t *mp)
+{
+ return XFS_INO_AGNO_BITS(mp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_INO_BITS)
+int
+xfs_ino_bits(xfs_mount_t *mp)
+{
+ return XFS_INO_BITS(mp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_INO_MASK)
+__uint32_t
+xfs_ino_mask(int k)
+{
+ return XFS_INO_MASK(k);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_INO_OFFSET_BITS)
+int
+xfs_ino_offset_bits(xfs_mount_t *mp)
+{
+ return XFS_INO_OFFSET_BITS(mp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_INO_TO_AGBNO)
+xfs_agblock_t
+xfs_ino_to_agbno(xfs_mount_t *mp, xfs_ino_t i)
+{
+ return XFS_INO_TO_AGBNO(mp, i);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_INO_TO_AGINO)
+xfs_agino_t
+xfs_ino_to_agino(xfs_mount_t *mp, xfs_ino_t i)
+{
+ return XFS_INO_TO_AGINO(mp, i);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_INO_TO_AGNO)
+xfs_agnumber_t
+xfs_ino_to_agno(xfs_mount_t *mp, xfs_ino_t i)
+{
+ return XFS_INO_TO_AGNO(mp, i);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_INO_TO_FSB)
+xfs_fsblock_t
+xfs_ino_to_fsb(xfs_mount_t *mp, xfs_ino_t i)
+{
+ return XFS_INO_TO_FSB(mp, i);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_INO_TO_OFFSET)
+int
+xfs_ino_to_offset(xfs_mount_t *mp, xfs_ino_t i)
+{
+ return XFS_INO_TO_OFFSET(mp, i);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_INOBT_BLOCK_MAXRECS)
+int
+xfs_inobt_block_maxrecs(int lev, xfs_btree_cur_t *cur)
+{
+ return XFS_INOBT_BLOCK_MAXRECS(lev, cur);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_INOBT_BLOCK_MINRECS)
+int
+xfs_inobt_block_minrecs(int lev, xfs_btree_cur_t *cur)
+{
+ return XFS_INOBT_BLOCK_MINRECS(lev, cur);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_INOBT_BLOCK_SIZE)
+/*ARGSUSED1*/
+int
+xfs_inobt_block_size(int lev, xfs_btree_cur_t *cur)
+{
+ return XFS_INOBT_BLOCK_SIZE(lev, cur);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_INOBT_CLR_FREE)
+void
+xfs_inobt_clr_free(xfs_inobt_rec_t *rp, int i, xfs_arch_t arch)
+{
+ XFS_INOBT_CLR_FREE(rp, i, arch);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_INOBT_IS_FREE)
+int
+xfs_inobt_is_free(xfs_inobt_rec_t *rp, int i, xfs_arch_t arch)
+{
+ return XFS_INOBT_IS_FREE(rp, i, arch);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_INOBT_IS_LAST_REC)
+int
+xfs_inobt_is_last_rec(xfs_btree_cur_t *cur)
+{
+ return XFS_INOBT_IS_LAST_REC(cur);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_INOBT_KEY_ADDR)
+/*ARGSUSED3*/
+xfs_inobt_key_t *
+xfs_inobt_key_addr(xfs_inobt_block_t *bb, int i, xfs_btree_cur_t *cur)
+{
+ return XFS_INOBT_KEY_ADDR(bb, i, cur);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_INOBT_MASK)
+xfs_inofree_t
+xfs_inobt_mask(int i)
+{
+ return XFS_INOBT_MASK(i);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_INOBT_MASKN)
+xfs_inofree_t
+xfs_inobt_maskn(int i, int n)
+{
+ return XFS_INOBT_MASKN(i, n);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_INOBT_PTR_ADDR)
+xfs_inobt_ptr_t *
+xfs_inobt_ptr_addr(xfs_inobt_block_t *bb, int i, xfs_btree_cur_t *cur)
+{
+ return XFS_INOBT_PTR_ADDR(bb, i, cur);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_INOBT_REC_ADDR)
+/*ARGSUSED3*/
+xfs_inobt_rec_t *
+xfs_inobt_rec_addr(xfs_inobt_block_t *bb, int i, xfs_btree_cur_t *cur)
+{
+ return XFS_INOBT_REC_ADDR(bb, i, cur);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_INOBT_SET_FREE)
+void
+xfs_inobt_set_free(xfs_inobt_rec_t *rp, int i, xfs_arch_t arch)
+{
+ XFS_INOBT_SET_FREE(rp, i, arch);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_ITOBHV)
+bhv_desc_t *
+xfs_itobhv(xfs_inode_t *ip)
+{
+ return XFS_ITOBHV(ip);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_ITOV)
+vnode_t *
+xfs_itov(xfs_inode_t *ip)
+{
+ return XFS_ITOV(ip);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_LBLOG)
+int
+xfs_lblog(xfs_mount_t *mp)
+{
+ return XFS_LBLOG(mp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_LBSIZE)
+int
+xfs_lbsize(xfs_mount_t *mp)
+{
+ return XFS_LBSIZE(mp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_LIC_ALL_FREE)
+void
+xfs_lic_all_free(xfs_log_item_chunk_t *cp)
+{
+ XFS_LIC_ALL_FREE(cp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_LIC_ARE_ALL_FREE)
+int
+xfs_lic_are_all_free(xfs_log_item_chunk_t *cp)
+{
+ return XFS_LIC_ARE_ALL_FREE(cp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_LIC_CLAIM)
+void
+xfs_lic_claim(xfs_log_item_chunk_t *cp, int slot)
+{
+ XFS_LIC_CLAIM(cp, slot);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_LIC_DESC_TO_CHUNK)
+xfs_log_item_chunk_t *
+xfs_lic_desc_to_chunk(xfs_log_item_desc_t *dp)
+{
+ return XFS_LIC_DESC_TO_CHUNK(dp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_LIC_DESC_TO_SLOT)
+int
+xfs_lic_desc_to_slot(xfs_log_item_desc_t *dp)
+{
+ return XFS_LIC_DESC_TO_SLOT(dp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_LIC_INIT)
+void
+xfs_lic_init(xfs_log_item_chunk_t *cp)
+{
+ XFS_LIC_INIT(cp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_LIC_INIT_SLOT)
+void
+xfs_lic_init_slot(xfs_log_item_chunk_t *cp, int slot)
+{
+ XFS_LIC_INIT_SLOT(cp, slot);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_LIC_ISFREE)
+int
+xfs_lic_isfree(xfs_log_item_chunk_t *cp, int slot)
+{
+ return XFS_LIC_ISFREE(cp, slot);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_LIC_RELSE)
+void
+xfs_lic_relse(xfs_log_item_chunk_t *cp, int slot)
+{
+ XFS_LIC_RELSE(cp, slot);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_LIC_SLOT)
+xfs_log_item_desc_t *
+xfs_lic_slot(xfs_log_item_chunk_t *cp, int slot)
+{
+ return XFS_LIC_SLOT(cp, slot);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_LIC_VACANCY)
+int
+xfs_lic_vacancy(xfs_log_item_chunk_t *cp)
+{
+ return XFS_LIC_VACANCY(cp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_LITINO)
+int
+xfs_litino(xfs_mount_t *mp)
+{
+ return XFS_LITINO(mp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_MAKE_IPTR)
+xfs_dinode_t *
+xfs_make_iptr(xfs_mount_t *mp, xfs_buf_t *b, int o)
+{
+ return XFS_MAKE_IPTR(mp, b, o);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_MASK32HI)
+__uint32_t
+xfs_mask32hi(int n)
+{
+ return XFS_MASK32HI(n);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_MASK32LO)
+__uint32_t
+xfs_mask32lo(int n)
+{
+ return XFS_MASK32LO(n);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_MASK64HI)
+__uint64_t
+xfs_mask64hi(int n)
+{
+ return XFS_MASK64HI(n);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_MASK64LO)
+__uint64_t
+xfs_mask64lo(int n)
+{
+ return XFS_MASK64LO(n);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_MIN_FREELIST)
+int
+xfs_min_freelist(xfs_agf_t *a, xfs_mount_t *mp)
+{
+ return XFS_MIN_FREELIST(a, mp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_MIN_FREELIST_PAG)
+int
+xfs_min_freelist_pag(xfs_perag_t *pag, xfs_mount_t *mp)
+{
+ return XFS_MIN_FREELIST_PAG(pag, mp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_MIN_FREELIST_RAW)
+int
+xfs_min_freelist_raw(uint bl, uint cl, xfs_mount_t *mp)
+{
+ return XFS_MIN_FREELIST_RAW(bl, cl, mp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_MTOVFS)
+vfs_t *
+xfs_mtovfs(xfs_mount_t *mp)
+{
+ return XFS_MTOVFS(mp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_OFFBNO_TO_AGINO)
+xfs_agino_t
+xfs_offbno_to_agino(xfs_mount_t *mp, xfs_agblock_t b, int o)
+{
+ return XFS_OFFBNO_TO_AGINO(mp, b, o);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_PREALLOC_BLOCKS)
+xfs_agblock_t
+xfs_prealloc_blocks(xfs_mount_t *mp)
+{
+ return XFS_PREALLOC_BLOCKS(mp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_SB_BLOCK)
+xfs_agblock_t
+xfs_sb_block(xfs_mount_t *mp)
+{
+ return XFS_SB_BLOCK(mp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_SB_GOOD_VERSION)
+int
+xfs_sb_good_version(xfs_sb_t *sbp)
+{
+ return XFS_SB_GOOD_VERSION(sbp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_SB_VERSION_ADDATTR)
+void
+xfs_sb_version_addattr(xfs_sb_t *sbp)
+{
+ XFS_SB_VERSION_ADDATTR(sbp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_SB_VERSION_ADDDALIGN)
+void
+xfs_sb_version_adddalign(xfs_sb_t *sbp)
+{
+ XFS_SB_VERSION_ADDDALIGN(sbp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_SB_VERSION_ADDNLINK)
+void
+xfs_sb_version_addnlink(xfs_sb_t *sbp)
+{
+ XFS_SB_VERSION_ADDNLINK(sbp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_SB_VERSION_ADDQUOTA)
+void
+xfs_sb_version_addquota(xfs_sb_t *sbp)
+{
+ XFS_SB_VERSION_ADDQUOTA(sbp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_SB_VERSION_ADDSHARED)
+void
+xfs_sb_version_addshared(xfs_sb_t *sbp)
+{
+ XFS_SB_VERSION_ADDSHARED(sbp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_SB_VERSION_HASALIGN)
+int
+xfs_sb_version_hasalign(xfs_sb_t *sbp)
+{
+ return XFS_SB_VERSION_HASALIGN(sbp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_SB_VERSION_HASATTR)
+int
+xfs_sb_version_hasattr(xfs_sb_t *sbp)
+{
+ return XFS_SB_VERSION_HASATTR(sbp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_SB_VERSION_HASDALIGN)
+int
+xfs_sb_version_hasdalign(xfs_sb_t *sbp)
+{
+ return XFS_SB_VERSION_HASDALIGN(sbp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_SB_VERSION_HASDIRV2)
+int
+xfs_sb_version_hasdirv2(xfs_sb_t *sbp)
+{
+ return XFS_SB_VERSION_HASDIRV2(sbp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_SB_VERSION_HASEXTFLGBIT)
+int
+xfs_sb_version_hasextflgbit(xfs_sb_t *sbp)
+{
+ return XFS_SB_VERSION_HASEXTFLGBIT(sbp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_SB_VERSION_HASNLINK)
+int
+xfs_sb_version_hasnlink(xfs_sb_t *sbp)
+{
+ return XFS_SB_VERSION_HASNLINK(sbp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_SB_VERSION_HASQUOTA)
+int
+xfs_sb_version_hasquota(xfs_sb_t *sbp)
+{
+ return XFS_SB_VERSION_HASQUOTA(sbp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_SB_VERSION_HASSHARED)
+int
+xfs_sb_version_hasshared(xfs_sb_t *sbp)
+{
+ return XFS_SB_VERSION_HASSHARED(sbp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_SB_VERSION_NUM)
+int
+xfs_sb_version_num(xfs_sb_t *sbp)
+{
+ return XFS_SB_VERSION_NUM(sbp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_SB_VERSION_SUBALIGN)
+void
+xfs_sb_version_subalign(xfs_sb_t *sbp)
+{
+ XFS_SB_VERSION_SUBALIGN(sbp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_SB_VERSION_SUBSHARED)
+void
+xfs_sb_version_subshared(xfs_sb_t *sbp)
+{
+ XFS_SB_VERSION_SUBSHARED(sbp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_SB_VERSION_HASLOGV2)
+int
+xfs_sb_version_haslogv2(xfs_sb_t *sbp)
+{
+ return XFS_SB_VERSION_HASLOGV2(sbp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_SB_VERSION_HASSECTOR)
+int
+xfs_sb_version_hassector(xfs_sb_t *sbp)
+{
+ return XFS_SB_VERSION_HASSECTOR(sbp);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_SB_VERSION_TONEW)
+unsigned
+xfs_sb_version_tonew(unsigned v)
+{
+ return XFS_SB_VERSION_TONEW(v);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_SB_VERSION_TOOLD)
+unsigned
+xfs_sb_version_toold(unsigned v)
+{
+ return XFS_SB_VERSION_TOOLD(v);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XLOG_GRANT_ADD_SPACE)
+void
+xlog_grant_add_space(xlog_t *log, int bytes, int type)
+{
+ XLOG_GRANT_ADD_SPACE(log, bytes, type);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XLOG_GRANT_SUB_SPACE)
+void
+xlog_grant_sub_space(xlog_t *log, int bytes, int type)
+{
+ XLOG_GRANT_SUB_SPACE(log, bytes, type);
+}
+#endif
+
+#if XFS_WANT_FUNCS_C || (XFS_WANT_SPACE_C && XFSSO_XFS_SB_VERSION_HASMOREBITS)
+int
+xfs_sb_version_hasmorebits(xfs_sb_t *sbp)
+{
+ return XFS_SB_VERSION_HASMOREBITS(sbp);
+}
+#endif
+
diff --git a/fs/xfs/xfs_macros.h b/fs/xfs/xfs_macros.h
new file mode 100644
index 00000000000000..0a9307514a4887
--- /dev/null
+++ b/fs/xfs/xfs_macros.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_MACROS_H__
+#define __XFS_MACROS_H__
+
+/*
+ * Set for debug kernels and simulation
+ * These replacements save space.
+ * Used in xfs_macros.c.
+ */
+#define XFS_WANT_SPACE_C \
+ (!defined(_STANDALONE) && defined(DEBUG))
+
+/*
+ * Set for debug simulation and kernel builds, but not for standalone.
+ * These replacements do not save space.
+ * Used in xfs_macros.c.
+ */
+#define XFS_WANT_FUNCS_C \
+ (!defined(_STANDALONE) && defined(DEBUG))
+
+/*
+ * Corresponding names used in .h files.
+ */
+#define XFS_WANT_SPACE (XFS_WANT_SPACE_C && !defined(XFS_MACRO_C))
+#define XFS_WANT_FUNCS (XFS_WANT_FUNCS_C && !defined(XFS_MACRO_C))
+
+/*
+ * These are the macros that get turned into functions to save space.
+ */
+#define XFSSO_NULLSTARTBLOCK 1
+#define XFSSO_XFS_AGB_TO_DADDR 1
+#define XFSSO_XFS_AGB_TO_FSB 1
+#define XFSSO_XFS_AGINO_TO_INO 1
+#define XFSSO_XFS_ALLOC_BLOCK_MINRECS 1
+#define XFSSO_XFS_ATTR_SF_NEXTENTRY 1
+#define XFSSO_XFS_BMAP_BLOCK_DMAXRECS 1
+#define XFSSO_XFS_BMAP_BLOCK_IMAXRECS 1
+#define XFSSO_XFS_BMAP_BLOCK_IMINRECS 1
+#define XFSSO_XFS_BMAP_INIT 1
+#define XFSSO_XFS_BMAP_PTR_IADDR 1
+#define XFSSO_XFS_BMAP_SANITY_CHECK 1
+#define XFSSO_XFS_BMAPI_AFLAG 1
+#define XFSSO_XFS_CFORK_SIZE 1
+#define XFSSO_XFS_DA_COOKIE_BNO 1
+#define XFSSO_XFS_DA_COOKIE_ENTRY 1
+#define XFSSO_XFS_DADDR_TO_AGBNO 1
+#define XFSSO_XFS_DADDR_TO_FSB 1
+#define XFSSO_XFS_DFORK_PTR 1
+#define XFSSO_XFS_DIR_SF_GET_DIRINO 1
+#define XFSSO_XFS_DIR_SF_NEXTENTRY 1
+#define XFSSO_XFS_DIR_SF_PUT_DIRINO 1
+#define XFSSO_XFS_FILBLKS_MIN 1
+#define XFSSO_XFS_FSB_SANITY_CHECK 1
+#define XFSSO_XFS_FSB_TO_DADDR 1
+#define XFSSO_XFS_FSB_TO_DB 1
+#define XFSSO_XFS_IALLOC_INODES 1
+#define XFSSO_XFS_IFORK_ASIZE 1
+#define XFSSO_XFS_IFORK_DSIZE 1
+#define XFSSO_XFS_IFORK_FORMAT 1
+#define XFSSO_XFS_IFORK_NEXT_SET 1
+#define XFSSO_XFS_IFORK_NEXTENTS 1
+#define XFSSO_XFS_IFORK_PTR 1
+#define XFSSO_XFS_ILOG_FBROOT 1
+#define XFSSO_XFS_ILOG_FEXT 1
+#define XFSSO_XFS_INO_MASK 1
+#define XFSSO_XFS_INO_TO_FSB 1
+#define XFSSO_XFS_INODE_CLEAR_READ_AHEAD 1
+#define XFSSO_XFS_MIN_FREELIST 1
+#define XFSSO_XFS_SB_GOOD_VERSION 1
+#define XFSSO_XFS_SB_VERSION_HASNLINK 1
+#define XFSSO_XLOG_GRANT_ADD_SPACE 1
+#define XFSSO_XLOG_GRANT_SUB_SPACE 1
+
+#endif /* __XFS_MACROS_H__ */
diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c
new file mode 100644
index 00000000000000..96e94778940aa5
--- /dev/null
+++ b/fs/xfs/xfs_mount.c
@@ -0,0 +1,1593 @@
+/*
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode.h"
+#include "xfs_alloc.h"
+#include "xfs_rtalloc.h"
+#include "xfs_bmap.h"
+#include "xfs_error.h"
+#include "xfs_bit.h"
+#include "xfs_rw.h"
+#include "xfs_quota.h"
+#include "xfs_fsops.h"
+
+STATIC void xfs_mount_log_sbunit(xfs_mount_t *, __int64_t);
+STATIC int xfs_uuid_mount(xfs_mount_t *);
+STATIC void xfs_uuid_unmount(xfs_mount_t *mp);
+
+void xfs_xlatesb(void *, xfs_sb_t *, int, xfs_arch_t, __int64_t);
+
+static struct {
+ short offset;
+ short type; /* 0 = integer
+ * 1 = binary / string (no translation)
+ */
+} xfs_sb_info[] = {
+ { offsetof(xfs_sb_t, sb_magicnum), 0 },
+ { offsetof(xfs_sb_t, sb_blocksize), 0 },
+ { offsetof(xfs_sb_t, sb_dblocks), 0 },
+ { offsetof(xfs_sb_t, sb_rblocks), 0 },
+ { offsetof(xfs_sb_t, sb_rextents), 0 },
+ { offsetof(xfs_sb_t, sb_uuid), 1 },
+ { offsetof(xfs_sb_t, sb_logstart), 0 },
+ { offsetof(xfs_sb_t, sb_rootino), 0 },
+ { offsetof(xfs_sb_t, sb_rbmino), 0 },
+ { offsetof(xfs_sb_t, sb_rsumino), 0 },
+ { offsetof(xfs_sb_t, sb_rextsize), 0 },
+ { offsetof(xfs_sb_t, sb_agblocks), 0 },
+ { offsetof(xfs_sb_t, sb_agcount), 0 },
+ { offsetof(xfs_sb_t, sb_rbmblocks), 0 },
+ { offsetof(xfs_sb_t, sb_logblocks), 0 },
+ { offsetof(xfs_sb_t, sb_versionnum), 0 },
+ { offsetof(xfs_sb_t, sb_sectsize), 0 },
+ { offsetof(xfs_sb_t, sb_inodesize), 0 },
+ { offsetof(xfs_sb_t, sb_inopblock), 0 },
+ { offsetof(xfs_sb_t, sb_fname[0]), 1 },
+ { offsetof(xfs_sb_t, sb_blocklog), 0 },
+ { offsetof(xfs_sb_t, sb_sectlog), 0 },
+ { offsetof(xfs_sb_t, sb_inodelog), 0 },
+ { offsetof(xfs_sb_t, sb_inopblog), 0 },
+ { offsetof(xfs_sb_t, sb_agblklog), 0 },
+ { offsetof(xfs_sb_t, sb_rextslog), 0 },
+ { offsetof(xfs_sb_t, sb_inprogress), 0 },
+ { offsetof(xfs_sb_t, sb_imax_pct), 0 },
+ { offsetof(xfs_sb_t, sb_icount), 0 },
+ { offsetof(xfs_sb_t, sb_ifree), 0 },
+ { offsetof(xfs_sb_t, sb_fdblocks), 0 },
+ { offsetof(xfs_sb_t, sb_frextents), 0 },
+ { offsetof(xfs_sb_t, sb_uquotino), 0 },
+ { offsetof(xfs_sb_t, sb_gquotino), 0 },
+ { offsetof(xfs_sb_t, sb_qflags), 0 },
+ { offsetof(xfs_sb_t, sb_flags), 0 },
+ { offsetof(xfs_sb_t, sb_shared_vn), 0 },
+ { offsetof(xfs_sb_t, sb_inoalignmt), 0 },
+ { offsetof(xfs_sb_t, sb_unit), 0 },
+ { offsetof(xfs_sb_t, sb_width), 0 },
+ { offsetof(xfs_sb_t, sb_dirblklog), 0 },
+ { offsetof(xfs_sb_t, sb_logsectlog), 0 },
+ { offsetof(xfs_sb_t, sb_logsectsize),0 },
+ { offsetof(xfs_sb_t, sb_logsunit), 0 },
+ { offsetof(xfs_sb_t, sb_features2), 0 },
+ { sizeof(xfs_sb_t), 0 }
+};
+
+/*
+ * Return a pointer to an initialized xfs_mount structure.
+ */
+xfs_mount_t *
+xfs_mount_init(void)
+{
+ xfs_mount_t *mp;
+
+ mp = kmem_zalloc(sizeof(*mp), KM_SLEEP);
+
+ AIL_LOCKINIT(&mp->m_ail_lock, "xfs_ail");
+ spinlock_init(&mp->m_sb_lock, "xfs_sb");
+ mutex_init(&mp->m_ilock, MUTEX_DEFAULT, "xfs_ilock");
+ initnsema(&mp->m_growlock, 1, "xfs_grow");
+ /*
+ * Initialize the AIL.
+ */
+ xfs_trans_ail_init(mp);
+
+ atomic_set(&mp->m_active_trans, 0);
+
+ return mp;
+}
+
+/*
+ * Free up the resources associated with a mount structure. Assume that
+ * the structure was initially zeroed, so we can tell which fields got
+ * initialized.
+ */
+void
+xfs_mount_free(
+ xfs_mount_t *mp,
+ int remove_bhv)
+{
+ if (mp->m_ihash)
+ xfs_ihash_free(mp);
+ if (mp->m_chash)
+ xfs_chash_free(mp);
+
+ if (mp->m_perag) {
+ int agno;
+
+ for (agno = 0; agno < mp->m_maxagi; agno++)
+ if (mp->m_perag[agno].pagb_list)
+ kmem_free(mp->m_perag[agno].pagb_list,
+ sizeof(xfs_perag_busy_t) *
+ XFS_PAGB_NUM_SLOTS);
+ kmem_free(mp->m_perag,
+ sizeof(xfs_perag_t) * mp->m_sb.sb_agcount);
+ }
+
+ AIL_LOCK_DESTROY(&mp->m_ail_lock);
+ spinlock_destroy(&mp->m_sb_lock);
+ mutex_destroy(&mp->m_ilock);
+ freesema(&mp->m_growlock);
+ if (mp->m_quotainfo)
+ XFS_QM_DONE(mp);
+
+ if (mp->m_fsname != NULL)
+ kmem_free(mp->m_fsname, mp->m_fsname_len);
+
+ if (remove_bhv) {
+ struct vfs *vfsp = XFS_MTOVFS(mp);
+
+ bhv_remove_all_vfsops(vfsp, 0);
+ VFS_REMOVEBHV(vfsp, &mp->m_bhv);
+ }
+
+ kmem_free(mp, sizeof(xfs_mount_t));
+}
+
+
+/*
+ * Check the validity of the SB found.
+ */
+STATIC int
+xfs_mount_validate_sb(
+ xfs_mount_t *mp,
+ xfs_sb_t *sbp)
+{
+ /*
+ * If the log device and data device have the
+ * same device number, the log is internal.
+ * Consequently, the sb_logstart should be non-zero. If
+ * we have a zero sb_logstart in this case, we may be trying to mount
+ * a volume filesystem in a non-volume manner.
+ */
+ if (sbp->sb_magicnum != XFS_SB_MAGIC) {
+ cmn_err(CE_WARN, "XFS: bad magic number");
+ return XFS_ERROR(EWRONGFS);
+ }
+
+ if (!XFS_SB_GOOD_VERSION(sbp)) {
+ cmn_err(CE_WARN, "XFS: bad version");
+ return XFS_ERROR(EWRONGFS);
+ }
+
+ if (unlikely(
+ sbp->sb_logstart == 0 && mp->m_logdev_targp == mp->m_ddev_targp)) {
+ cmn_err(CE_WARN,
+ "XFS: filesystem is marked as having an external log; "
+ "specify logdev on the\nmount command line.");
+ XFS_CORRUPTION_ERROR("xfs_mount_validate_sb(1)",
+ XFS_ERRLEVEL_HIGH, mp, sbp);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+
+ if (unlikely(
+ sbp->sb_logstart != 0 && mp->m_logdev_targp != mp->m_ddev_targp)) {
+ cmn_err(CE_WARN,
+ "XFS: filesystem is marked as having an internal log; "
+ "don't specify logdev on\nthe mount command line.");
+ XFS_CORRUPTION_ERROR("xfs_mount_validate_sb(2)",
+ XFS_ERRLEVEL_HIGH, mp, sbp);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+
+ /*
+ * More sanity checking. These were stolen directly from
+ * xfs_repair.
+ */
+ if (unlikely(
+ sbp->sb_agcount <= 0 ||
+ sbp->sb_sectsize < XFS_MIN_SECTORSIZE ||
+ sbp->sb_sectsize > XFS_MAX_SECTORSIZE ||
+ sbp->sb_sectlog < XFS_MIN_SECTORSIZE_LOG ||
+ sbp->sb_sectlog > XFS_MAX_SECTORSIZE_LOG ||
+ sbp->sb_blocksize < XFS_MIN_BLOCKSIZE ||
+ sbp->sb_blocksize > XFS_MAX_BLOCKSIZE ||
+ sbp->sb_blocklog < XFS_MIN_BLOCKSIZE_LOG ||
+ sbp->sb_blocklog > XFS_MAX_BLOCKSIZE_LOG ||
+ sbp->sb_inodesize < XFS_DINODE_MIN_SIZE ||
+ sbp->sb_inodesize > XFS_DINODE_MAX_SIZE ||
+ (sbp->sb_rextsize * sbp->sb_blocksize > XFS_MAX_RTEXTSIZE) ||
+ (sbp->sb_rextsize * sbp->sb_blocksize < XFS_MIN_RTEXTSIZE) ||
+ sbp->sb_imax_pct > 100)) {
+ cmn_err(CE_WARN, "XFS: SB sanity check 1 failed");
+ XFS_CORRUPTION_ERROR("xfs_mount_validate_sb(3)",
+ XFS_ERRLEVEL_LOW, mp, sbp);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+
+ /*
+ * Sanity check AG count, size fields against data size field
+ */
+ if (unlikely(
+ sbp->sb_dblocks == 0 ||
+ sbp->sb_dblocks >
+ (xfs_drfsbno_t)sbp->sb_agcount * sbp->sb_agblocks ||
+ sbp->sb_dblocks < (xfs_drfsbno_t)(sbp->sb_agcount - 1) *
+ sbp->sb_agblocks + XFS_MIN_AG_BLOCKS)) {
+ cmn_err(CE_WARN, "XFS: SB sanity check 2 failed");
+ XFS_ERROR_REPORT("xfs_mount_validate_sb(4)",
+ XFS_ERRLEVEL_LOW, mp);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+
+ ASSERT(PAGE_SHIFT >= sbp->sb_blocklog);
+ ASSERT(sbp->sb_blocklog >= BBSHIFT);
+
+#if XFS_BIG_BLKNOS /* Limited by ULONG_MAX of page cache index */
+ if (unlikely(
+ (sbp->sb_dblocks >> (PAGE_SHIFT - sbp->sb_blocklog)) > ULONG_MAX ||
+ (sbp->sb_rblocks >> (PAGE_SHIFT - sbp->sb_blocklog)) > ULONG_MAX)) {
+#else /* Limited by UINT_MAX of sectors */
+ if (unlikely(
+ (sbp->sb_dblocks << (sbp->sb_blocklog - BBSHIFT)) > UINT_MAX ||
+ (sbp->sb_rblocks << (sbp->sb_blocklog - BBSHIFT)) > UINT_MAX)) {
+#endif
+ cmn_err(CE_WARN,
+ "XFS: File system is too large to be mounted on this system.");
+ return XFS_ERROR(E2BIG);
+ }
+
+ if (unlikely(sbp->sb_inprogress)) {
+ cmn_err(CE_WARN, "XFS: file system busy");
+ XFS_ERROR_REPORT("xfs_mount_validate_sb(5)",
+ XFS_ERRLEVEL_LOW, mp);
+ return XFS_ERROR(EFSCORRUPTED);
+ }
+
+ /*
+ * Until this is fixed only page-sized or smaller data blocks work.
+ */
+ if (unlikely(sbp->sb_blocksize > PAGE_SIZE)) {
+ cmn_err(CE_WARN,
+ "XFS: Attempted to mount file system with blocksize %d bytes",
+ sbp->sb_blocksize);
+ cmn_err(CE_WARN,
+ "XFS: Only page-sized (%d) or less blocksizes currently work.",
+ PAGE_SIZE);
+ return XFS_ERROR(ENOSYS);
+ }
+
+ return 0;
+}
+
+xfs_agnumber_t
+xfs_initialize_perag(xfs_mount_t *mp, xfs_agnumber_t agcount)
+{
+ xfs_agnumber_t index, max_metadata;
+ xfs_perag_t *pag;
+ xfs_agino_t agino;
+ xfs_ino_t ino;
+ xfs_sb_t *sbp = &mp->m_sb;
+ xfs_ino_t max_inum = XFS_MAXINUMBER_32;
+
+ /* Check to see if the filesystem can overflow 32 bit inodes */
+ agino = XFS_OFFBNO_TO_AGINO(mp, sbp->sb_agblocks - 1, 0);
+ ino = XFS_AGINO_TO_INO(mp, agcount - 1, agino);
+
+ /* Clear the mount flag if no inode can overflow 32 bits
+ * on this filesystem, or if specifically requested..
+ */
+ if ((mp->m_flags & XFS_MOUNT_32BITINOOPT) && ino > max_inum) {
+ mp->m_flags |= XFS_MOUNT_32BITINODES;
+ } else {
+ mp->m_flags &= ~XFS_MOUNT_32BITINODES;
+ }
+
+ /* If we can overflow then setup the ag headers accordingly */
+ if (mp->m_flags & XFS_MOUNT_32BITINODES) {
+ /* Calculate how much should be reserved for inodes to
+ * meet the max inode percentage.
+ */
+ if (mp->m_maxicount) {
+ __uint64_t icount;
+
+ icount = sbp->sb_dblocks * sbp->sb_imax_pct;
+ do_div(icount, 100);
+ icount += sbp->sb_agblocks - 1;
+ do_div(icount, mp->m_ialloc_blks);
+ max_metadata = icount;
+ } else {
+ max_metadata = agcount;
+ }
+ for (index = 0; index < agcount; index++) {
+ ino = XFS_AGINO_TO_INO(mp, index, agino);
+ if (ino > max_inum) {
+ index++;
+ break;
+ }
+
+ /* This ag is prefered for inodes */
+ pag = &mp->m_perag[index];
+ pag->pagi_inodeok = 1;
+ if (index < max_metadata)
+ pag->pagf_metadata = 1;
+ }
+ } else {
+ /* Setup default behavior for smaller filesystems */
+ for (index = 0; index < agcount; index++) {
+ pag = &mp->m_perag[index];
+ pag->pagi_inodeok = 1;
+ }
+ }
+ return index;
+}
+
+/*
+ * xfs_xlatesb
+ *
+ * data - on disk version of sb
+ * sb - a superblock
+ * dir - conversion direction: <0 - convert sb to buf
+ * >0 - convert buf to sb
+ * arch - architecture to read/write from/to buf
+ * fields - which fields to copy (bitmask)
+ */
+void
+xfs_xlatesb(
+ void *data,
+ xfs_sb_t *sb,
+ int dir,
+ xfs_arch_t arch,
+ __int64_t fields)
+{
+ xfs_caddr_t buf_ptr;
+ xfs_caddr_t mem_ptr;
+ xfs_sb_field_t f;
+ int first;
+ int size;
+
+ ASSERT(dir);
+ ASSERT(fields);
+
+ if (!fields)
+ return;
+
+ buf_ptr = (xfs_caddr_t)data;
+ mem_ptr = (xfs_caddr_t)sb;
+
+ while (fields) {
+ f = (xfs_sb_field_t)xfs_lowbit64((__uint64_t)fields);
+ first = xfs_sb_info[f].offset;
+ size = xfs_sb_info[f + 1].offset - first;
+
+ ASSERT(xfs_sb_info[f].type == 0 || xfs_sb_info[f].type == 1);
+
+ if (arch == ARCH_NOCONVERT ||
+ size == 1 ||
+ xfs_sb_info[f].type == 1) {
+ if (dir > 0) {
+ memcpy(mem_ptr + first, buf_ptr + first, size);
+ } else {
+ memcpy(buf_ptr + first, mem_ptr + first, size);
+ }
+ } else {
+ switch (size) {
+ case 2:
+ INT_XLATE(*(__uint16_t*)(buf_ptr+first),
+ *(__uint16_t*)(mem_ptr+first),
+ dir, arch);
+ break;
+ case 4:
+ INT_XLATE(*(__uint32_t*)(buf_ptr+first),
+ *(__uint32_t*)(mem_ptr+first),
+ dir, arch);
+ break;
+ case 8:
+ INT_XLATE(*(__uint64_t*)(buf_ptr+first),
+ *(__uint64_t*)(mem_ptr+first), dir, arch);
+ break;
+ default:
+ ASSERT(0);
+ }
+ }
+
+ fields &= ~(1LL << f);
+ }
+}
+
+/*
+ * xfs_readsb
+ *
+ * Does the initial read of the superblock.
+ */
+int
+xfs_readsb(xfs_mount_t *mp)
+{
+ unsigned int sector_size;
+ unsigned int extra_flags;
+ xfs_buf_t *bp;
+ xfs_sb_t *sbp;
+ int error;
+
+ ASSERT(mp->m_sb_bp == NULL);
+ ASSERT(mp->m_ddev_targp != NULL);
+
+ /*
+ * Allocate a (locked) buffer to hold the superblock.
+ * This will be kept around at all times to optimize
+ * access to the superblock.
+ */
+ sector_size = xfs_getsize_buftarg(mp->m_ddev_targp);
+ extra_flags = XFS_BUF_LOCK | XFS_BUF_MANAGE | XFS_BUF_MAPPED;
+
+ bp = xfs_buf_read_flags(mp->m_ddev_targp, XFS_SB_DADDR,
+ BTOBB(sector_size), extra_flags);
+ if (!bp || XFS_BUF_ISERROR(bp)) {
+ cmn_err(CE_WARN, "XFS: SB read failed");
+ error = bp ? XFS_BUF_GETERROR(bp) : ENOMEM;
+ goto fail;
+ }
+ ASSERT(XFS_BUF_ISBUSY(bp));
+ ASSERT(XFS_BUF_VALUSEMA(bp) <= 0);
+
+ /*
+ * Initialize the mount structure from the superblock.
+ * But first do some basic consistency checking.
+ */
+ sbp = XFS_BUF_TO_SBP(bp);
+ xfs_xlatesb(XFS_BUF_PTR(bp), &(mp->m_sb), 1,
+ ARCH_CONVERT, XFS_SB_ALL_BITS);
+
+ error = xfs_mount_validate_sb(mp, &(mp->m_sb));
+ if (error) {
+ cmn_err(CE_WARN, "XFS: SB validate failed");
+ goto fail;
+ }
+
+ /*
+ * We must be able to do sector-sized and sector-aligned IO.
+ */
+ if (sector_size > mp->m_sb.sb_sectsize) {
+ cmn_err(CE_WARN,
+ "XFS: device supports only %u byte sectors (not %u)",
+ sector_size, mp->m_sb.sb_sectsize);
+ error = ENOSYS;
+ goto fail;
+ }
+
+ /*
+ * If device sector size is smaller than the superblock size,
+ * re-read the superblock so the buffer is correctly sized.
+ */
+ if (sector_size < mp->m_sb.sb_sectsize) {
+ XFS_BUF_UNMANAGE(bp);
+ xfs_buf_relse(bp);
+ sector_size = mp->m_sb.sb_sectsize;
+ bp = xfs_buf_read_flags(mp->m_ddev_targp, XFS_SB_DADDR,
+ BTOBB(sector_size), extra_flags);
+ if (!bp || XFS_BUF_ISERROR(bp)) {
+ cmn_err(CE_WARN, "XFS: SB re-read failed");
+ error = bp ? XFS_BUF_GETERROR(bp) : ENOMEM;
+ goto fail;
+ }
+ ASSERT(XFS_BUF_ISBUSY(bp));
+ ASSERT(XFS_BUF_VALUSEMA(bp) <= 0);
+ }
+
+ mp->m_sb_bp = bp;
+ xfs_buf_relse(bp);
+ ASSERT(XFS_BUF_VALUSEMA(bp) > 0);
+ return 0;
+
+ fail:
+ if (bp) {
+ XFS_BUF_UNMANAGE(bp);
+ xfs_buf_relse(bp);
+ }
+ return error;
+}
+
+
+/*
+ * xfs_mount_common
+ *
+ * Mount initialization code establishing various mount
+ * fields from the superblock associated with the given
+ * mount structure
+ */
+void
+xfs_mount_common(xfs_mount_t *mp, xfs_sb_t *sbp)
+{
+ int i;
+
+ mp->m_agfrotor = mp->m_agirotor = 0;
+ spinlock_init(&mp->m_agirotor_lock, "m_agirotor_lock");
+ mp->m_maxagi = mp->m_sb.sb_agcount;
+ mp->m_blkbit_log = sbp->sb_blocklog + XFS_NBBYLOG;
+ mp->m_blkbb_log = sbp->sb_blocklog - BBSHIFT;
+ mp->m_sectbb_log = sbp->sb_sectlog - BBSHIFT;
+ mp->m_agno_log = xfs_highbit32(sbp->sb_agcount - 1) + 1;
+ mp->m_agino_log = sbp->sb_inopblog + sbp->sb_agblklog;
+ mp->m_litino = sbp->sb_inodesize -
+ ((uint)sizeof(xfs_dinode_core_t) + (uint)sizeof(xfs_agino_t));
+ mp->m_blockmask = sbp->sb_blocksize - 1;
+ mp->m_blockwsize = sbp->sb_blocksize >> XFS_WORDLOG;
+ mp->m_blockwmask = mp->m_blockwsize - 1;
+ INIT_LIST_HEAD(&mp->m_del_inodes);
+
+ /*
+ * Setup for attributes, in case they get created.
+ * This value is for inodes getting attributes for the first time,
+ * the per-inode value is for old attribute values.
+ */
+ ASSERT(sbp->sb_inodesize >= 256 && sbp->sb_inodesize <= 2048);
+ switch (sbp->sb_inodesize) {
+ case 256:
+ mp->m_attroffset = XFS_LITINO(mp) - XFS_BMDR_SPACE_CALC(2);
+ break;
+ case 512:
+ case 1024:
+ case 2048:
+ mp->m_attroffset = XFS_BMDR_SPACE_CALC(12);
+ break;
+ default:
+ ASSERT(0);
+ }
+ ASSERT(mp->m_attroffset < XFS_LITINO(mp));
+
+ for (i = 0; i < 2; i++) {
+ mp->m_alloc_mxr[i] = XFS_BTREE_BLOCK_MAXRECS(sbp->sb_blocksize,
+ xfs_alloc, i == 0);
+ mp->m_alloc_mnr[i] = XFS_BTREE_BLOCK_MINRECS(sbp->sb_blocksize,
+ xfs_alloc, i == 0);
+ }
+ for (i = 0; i < 2; i++) {
+ mp->m_bmap_dmxr[i] = XFS_BTREE_BLOCK_MAXRECS(sbp->sb_blocksize,
+ xfs_bmbt, i == 0);
+ mp->m_bmap_dmnr[i] = XFS_BTREE_BLOCK_MINRECS(sbp->sb_blocksize,
+ xfs_bmbt, i == 0);
+ }
+ for (i = 0; i < 2; i++) {
+ mp->m_inobt_mxr[i] = XFS_BTREE_BLOCK_MAXRECS(sbp->sb_blocksize,
+ xfs_inobt, i == 0);
+ mp->m_inobt_mnr[i] = XFS_BTREE_BLOCK_MINRECS(sbp->sb_blocksize,
+ xfs_inobt, i == 0);
+ }
+
+ mp->m_bsize = XFS_FSB_TO_BB(mp, 1);
+ mp->m_ialloc_inos = (int)MAX((__uint16_t)XFS_INODES_PER_CHUNK,
+ sbp->sb_inopblock);
+ mp->m_ialloc_blks = mp->m_ialloc_inos >> sbp->sb_inopblog;
+}
+/*
+ * xfs_mountfs
+ *
+ * This function does the following on an initial mount of a file system:
+ * - reads the superblock from disk and init the mount struct
+ * - if we're a 32-bit kernel, do a size check on the superblock
+ * so we don't mount terabyte filesystems
+ * - init mount struct realtime fields
+ * - allocate inode hash table for fs
+ * - init directory manager
+ * - perform recovery and init the log manager
+ */
+int
+xfs_mountfs(
+ vfs_t *vfsp,
+ xfs_mount_t *mp,
+ int mfsi_flags)
+{
+ xfs_buf_t *bp;
+ xfs_sb_t *sbp = &(mp->m_sb);
+ xfs_inode_t *rip;
+ vnode_t *rvp = NULL;
+ int readio_log, writeio_log;
+ xfs_daddr_t d;
+ __uint64_t ret64;
+ __int64_t update_flags;
+ uint quotamount, quotaflags;
+ int agno;
+ int uuid_mounted = 0;
+ int error = 0;
+
+ if (mp->m_sb_bp == NULL) {
+ if ((error = xfs_readsb(mp))) {
+ return (error);
+ }
+ }
+ xfs_mount_common(mp, sbp);
+
+ /*
+ * Check if sb_agblocks is aligned at stripe boundary
+ * If sb_agblocks is NOT aligned turn off m_dalign since
+ * allocator alignment is within an ag, therefore ag has
+ * to be aligned at stripe boundary.
+ */
+ update_flags = 0LL;
+ if (mp->m_dalign && !(mfsi_flags & XFS_MFSI_SECOND)) {
+ /*
+ * If stripe unit and stripe width are not multiples
+ * of the fs blocksize turn off alignment.
+ */
+ if ((BBTOB(mp->m_dalign) & mp->m_blockmask) ||
+ (BBTOB(mp->m_swidth) & mp->m_blockmask)) {
+ if (mp->m_flags & XFS_MOUNT_RETERR) {
+ cmn_err(CE_WARN,
+ "XFS: alignment check 1 failed");
+ error = XFS_ERROR(EINVAL);
+ goto error1;
+ }
+ mp->m_dalign = mp->m_swidth = 0;
+ } else {
+ /*
+ * Convert the stripe unit and width to FSBs.
+ */
+ mp->m_dalign = XFS_BB_TO_FSBT(mp, mp->m_dalign);
+ if (mp->m_dalign && (sbp->sb_agblocks % mp->m_dalign)) {
+ if (mp->m_flags & XFS_MOUNT_RETERR) {
+ error = XFS_ERROR(EINVAL);
+ goto error1;
+ }
+ xfs_fs_cmn_err(CE_WARN, mp,
+"stripe alignment turned off: sunit(%d)/swidth(%d) incompatible with agsize(%d)",
+ mp->m_dalign, mp->m_swidth,
+ sbp->sb_agblocks);
+
+ mp->m_dalign = 0;
+ mp->m_swidth = 0;
+ } else if (mp->m_dalign) {
+ mp->m_swidth = XFS_BB_TO_FSBT(mp, mp->m_swidth);
+ } else {
+ if (mp->m_flags & XFS_MOUNT_RETERR) {
+ xfs_fs_cmn_err(CE_WARN, mp,
+"stripe alignment turned off: sunit(%d) less than bsize(%d)",
+ mp->m_dalign,
+ mp->m_blockmask +1);
+ error = XFS_ERROR(EINVAL);
+ goto error1;
+ }
+ mp->m_swidth = 0;
+ }
+ }
+
+ /*
+ * Update superblock with new values
+ * and log changes
+ */
+ if (XFS_SB_VERSION_HASDALIGN(sbp)) {
+ if (sbp->sb_unit != mp->m_dalign) {
+ sbp->sb_unit = mp->m_dalign;
+ update_flags |= XFS_SB_UNIT;
+ }
+ if (sbp->sb_width != mp->m_swidth) {
+ sbp->sb_width = mp->m_swidth;
+ update_flags |= XFS_SB_WIDTH;
+ }
+ }
+ } else if ((mp->m_flags & XFS_MOUNT_NOALIGN) != XFS_MOUNT_NOALIGN &&
+ XFS_SB_VERSION_HASDALIGN(&mp->m_sb)) {
+ mp->m_dalign = sbp->sb_unit;
+ mp->m_swidth = sbp->sb_width;
+ }
+
+ xfs_alloc_compute_maxlevels(mp);
+ xfs_bmap_compute_maxlevels(mp, XFS_DATA_FORK);
+ xfs_bmap_compute_maxlevels(mp, XFS_ATTR_FORK);
+ xfs_ialloc_compute_maxlevels(mp);
+
+ if (sbp->sb_imax_pct) {
+ __uint64_t icount;
+
+ /* Make sure the maximum inode count is a multiple of the
+ * units we allocate inodes in.
+ */
+
+ icount = sbp->sb_dblocks * sbp->sb_imax_pct;
+ do_div(icount, 100);
+ do_div(icount, mp->m_ialloc_blks);
+ mp->m_maxicount = (icount * mp->m_ialloc_blks) <<
+ sbp->sb_inopblog;
+ } else
+ mp->m_maxicount = 0;
+
+ mp->m_maxioffset = xfs_max_file_offset(sbp->sb_blocklog);
+
+ /*
+ * XFS uses the uuid from the superblock as the unique
+ * identifier for fsid. We can not use the uuid from the volume
+ * since a single partition filesystem is identical to a single
+ * partition volume/filesystem.
+ */
+ if ((mfsi_flags & XFS_MFSI_SECOND) == 0 &&
+ (mp->m_flags & XFS_MOUNT_NOUUID) == 0) {
+ if (xfs_uuid_mount(mp)) {
+ error = XFS_ERROR(EINVAL);
+ goto error1;
+ }
+ uuid_mounted=1;
+ ret64 = uuid_hash64(&sbp->sb_uuid);
+ memcpy(&vfsp->vfs_fsid, &ret64, sizeof(ret64));
+ }
+
+ /*
+ * Set the default minimum read and write sizes unless
+ * already specified in a mount option.
+ * We use smaller I/O sizes when the file system
+ * is being used for NFS service (wsync mount option).
+ */
+ if (!(mp->m_flags & XFS_MOUNT_DFLT_IOSIZE)) {
+ if (mp->m_flags & XFS_MOUNT_WSYNC) {
+ readio_log = XFS_WSYNC_READIO_LOG;
+ writeio_log = XFS_WSYNC_WRITEIO_LOG;
+ } else {
+ readio_log = XFS_READIO_LOG_LARGE;
+ writeio_log = XFS_WRITEIO_LOG_LARGE;
+ }
+ } else {
+ readio_log = mp->m_readio_log;
+ writeio_log = mp->m_writeio_log;
+ }
+
+ /*
+ * Set the number of readahead buffers to use based on
+ * physical memory size.
+ */
+ if (xfs_physmem <= 4096) /* <= 16MB */
+ mp->m_nreadaheads = XFS_RW_NREADAHEAD_16MB;
+ else if (xfs_physmem <= 8192) /* <= 32MB */
+ mp->m_nreadaheads = XFS_RW_NREADAHEAD_32MB;
+ else
+ mp->m_nreadaheads = XFS_RW_NREADAHEAD_K32;
+ if (sbp->sb_blocklog > readio_log) {
+ mp->m_readio_log = sbp->sb_blocklog;
+ } else {
+ mp->m_readio_log = readio_log;
+ }
+ mp->m_readio_blocks = 1 << (mp->m_readio_log - sbp->sb_blocklog);
+ if (sbp->sb_blocklog > writeio_log) {
+ mp->m_writeio_log = sbp->sb_blocklog;
+ } else {
+ mp->m_writeio_log = writeio_log;
+ }
+ mp->m_writeio_blocks = 1 << (mp->m_writeio_log - sbp->sb_blocklog);
+
+ /*
+ * Set the inode cluster size based on the physical memory
+ * size. This may still be overridden by the file system
+ * block size if it is larger than the chosen cluster size.
+ */
+ if (xfs_physmem <= btoc(32 * 1024 * 1024)) { /* <= 32 MB */
+ mp->m_inode_cluster_size = XFS_INODE_SMALL_CLUSTER_SIZE;
+ } else {
+ mp->m_inode_cluster_size = XFS_INODE_BIG_CLUSTER_SIZE;
+ }
+ /*
+ * Set whether we're using inode alignment.
+ */
+ if (XFS_SB_VERSION_HASALIGN(&mp->m_sb) &&
+ mp->m_sb.sb_inoalignmt >=
+ XFS_B_TO_FSBT(mp, mp->m_inode_cluster_size))
+ mp->m_inoalign_mask = mp->m_sb.sb_inoalignmt - 1;
+ else
+ mp->m_inoalign_mask = 0;
+ /*
+ * If we are using stripe alignment, check whether
+ * the stripe unit is a multiple of the inode alignment
+ */
+ if (mp->m_dalign && mp->m_inoalign_mask &&
+ !(mp->m_dalign & mp->m_inoalign_mask))
+ mp->m_sinoalign = mp->m_dalign;
+ else
+ mp->m_sinoalign = 0;
+ /*
+ * Check that the data (and log if separate) are an ok size.
+ */
+ d = (xfs_daddr_t)XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks);
+ if (XFS_BB_TO_FSB(mp, d) != mp->m_sb.sb_dblocks) {
+ cmn_err(CE_WARN, "XFS: size check 1 failed");
+ error = XFS_ERROR(E2BIG);
+ goto error1;
+ }
+ error = xfs_read_buf(mp, mp->m_ddev_targp,
+ d - XFS_FSS_TO_BB(mp, 1),
+ XFS_FSS_TO_BB(mp, 1), 0, &bp);
+ if (!error) {
+ xfs_buf_relse(bp);
+ } else {
+ cmn_err(CE_WARN, "XFS: size check 2 failed");
+ if (error == ENOSPC) {
+ error = XFS_ERROR(E2BIG);
+ }
+ goto error1;
+ }
+
+ if (((mfsi_flags & XFS_MFSI_CLIENT) == 0) &&
+ mp->m_logdev_targp != mp->m_ddev_targp) {
+ d = (xfs_daddr_t)XFS_FSB_TO_BB(mp, mp->m_sb.sb_logblocks);
+ if (XFS_BB_TO_FSB(mp, d) != mp->m_sb.sb_logblocks) {
+ cmn_err(CE_WARN, "XFS: size check 3 failed");
+ error = XFS_ERROR(E2BIG);
+ goto error1;
+ }
+ error = xfs_read_buf(mp, mp->m_logdev_targp,
+ d - XFS_FSB_TO_BB(mp, 1),
+ XFS_FSB_TO_BB(mp, 1), 0, &bp);
+ if (!error) {
+ xfs_buf_relse(bp);
+ } else {
+ cmn_err(CE_WARN, "XFS: size check 3 failed");
+ if (error == ENOSPC) {
+ error = XFS_ERROR(E2BIG);
+ }
+ goto error1;
+ }
+ }
+
+ /*
+ * Initialize realtime fields in the mount structure
+ */
+ if ((error = xfs_rtmount_init(mp))) {
+ cmn_err(CE_WARN, "XFS: RT mount failed");
+ goto error1;
+ }
+
+ /*
+ * For client case we are done now
+ */
+ if (mfsi_flags & XFS_MFSI_CLIENT) {
+ return(0);
+ }
+
+ /*
+ * Copies the low order bits of the timestamp and the randomly
+ * set "sequence" number out of a UUID.
+ */
+ uuid_getnodeuniq(&sbp->sb_uuid, mp->m_fixedfsid);
+
+ /*
+ * The vfs structure needs to have a file system independent
+ * way of checking for the invariant file system ID. Since it
+ * can't look at mount structures it has a pointer to the data
+ * in the mount structure.
+ *
+ * File systems that don't support user level file handles (i.e.
+ * all of them except for XFS) will leave vfs_altfsid as NULL.
+ */
+ vfsp->vfs_altfsid = (xfs_fsid_t *)mp->m_fixedfsid;
+ mp->m_dmevmask = 0; /* not persistent; set after each mount */
+
+ /*
+ * Select the right directory manager.
+ */
+ mp->m_dirops =
+ XFS_SB_VERSION_HASDIRV2(&mp->m_sb) ?
+ xfsv2_dirops :
+ xfsv1_dirops;
+
+ /*
+ * Initialize directory manager's entries.
+ */
+ XFS_DIR_MOUNT(mp);
+
+ /*
+ * Initialize the attribute manager's entries.
+ */
+ mp->m_attr_magicpct = (mp->m_sb.sb_blocksize * 37) / 100;
+
+ /*
+ * Initialize the precomputed transaction reservations values.
+ */
+ xfs_trans_init(mp);
+
+ /*
+ * Allocate and initialize the inode hash table for this
+ * file system.
+ */
+ xfs_ihash_init(mp);
+ xfs_chash_init(mp);
+
+ /*
+ * Allocate and initialize the per-ag data.
+ */
+ init_rwsem(&mp->m_peraglock);
+ mp->m_perag =
+ kmem_zalloc(sbp->sb_agcount * sizeof(xfs_perag_t), KM_SLEEP);
+
+ mp->m_maxagi = xfs_initialize_perag(mp, sbp->sb_agcount);
+
+ /*
+ * log's mount-time initialization. Perform 1st part recovery if needed
+ */
+ if (likely(sbp->sb_logblocks > 0)) { /* check for volume case */
+ error = xfs_log_mount(mp, mp->m_logdev_targp,
+ XFS_FSB_TO_DADDR(mp, sbp->sb_logstart),
+ XFS_FSB_TO_BB(mp, sbp->sb_logblocks));
+ if (error) {
+ cmn_err(CE_WARN, "XFS: log mount failed");
+ goto error2;
+ }
+ } else { /* No log has been defined */
+ cmn_err(CE_WARN, "XFS: no log defined");
+ XFS_ERROR_REPORT("xfs_mountfs_int(1)", XFS_ERRLEVEL_LOW, mp);
+ error = XFS_ERROR(EFSCORRUPTED);
+ goto error2;
+ }
+
+ /*
+ * Get and sanity-check the root inode.
+ * Save the pointer to it in the mount structure.
+ */
+ error = xfs_iget(mp, NULL, sbp->sb_rootino, 0, XFS_ILOCK_EXCL, &rip, 0);
+ if (error) {
+ cmn_err(CE_WARN, "XFS: failed to read root inode");
+ goto error3;
+ }
+
+ ASSERT(rip != NULL);
+ rvp = XFS_ITOV(rip);
+
+ if (unlikely((rip->i_d.di_mode & S_IFMT) != S_IFDIR)) {
+ cmn_err(CE_WARN, "XFS: corrupted root inode");
+ prdev("Root inode %llu is not a directory",
+ mp->m_ddev_targp, (unsigned long long)rip->i_ino);
+ xfs_iunlock(rip, XFS_ILOCK_EXCL);
+ XFS_ERROR_REPORT("xfs_mountfs_int(2)", XFS_ERRLEVEL_LOW,
+ mp);
+ error = XFS_ERROR(EFSCORRUPTED);
+ goto error4;
+ }
+ mp->m_rootip = rip; /* save it */
+
+ xfs_iunlock(rip, XFS_ILOCK_EXCL);
+
+ /*
+ * Initialize realtime inode pointers in the mount structure
+ */
+ if ((error = xfs_rtmount_inodes(mp))) {
+ /*
+ * Free up the root inode.
+ */
+ cmn_err(CE_WARN, "XFS: failed to read RT inodes");
+ goto error4;
+ }
+
+ /*
+ * If fs is not mounted readonly, then update the superblock
+ * unit and width changes.
+ */
+ if (update_flags && !(vfsp->vfs_flag & VFS_RDONLY))
+ xfs_mount_log_sbunit(mp, update_flags);
+
+ /*
+ * Initialise the XFS quota management subsystem for this mount
+ */
+ if ((error = XFS_QM_INIT(mp, &quotamount, &quotaflags)))
+ goto error4;
+
+ /*
+ * Finish recovering the file system. This part needed to be
+ * delayed until after the root and real-time bitmap inodes
+ * were consistently read in.
+ */
+ error = xfs_log_mount_finish(mp, mfsi_flags);
+ if (error) {
+ cmn_err(CE_WARN, "XFS: log mount finish failed");
+ goto error4;
+ }
+
+ /*
+ * Complete the quota initialisation, post-log-replay component.
+ */
+ if ((error = XFS_QM_MOUNT(mp, quotamount, quotaflags, mfsi_flags)))
+ goto error4;
+
+ return 0;
+
+ error4:
+ /*
+ * Free up the root inode.
+ */
+ VN_RELE(rvp);
+ error3:
+ xfs_log_unmount_dealloc(mp);
+ error2:
+ xfs_ihash_free(mp);
+ xfs_chash_free(mp);
+ for (agno = 0; agno < sbp->sb_agcount; agno++)
+ if (mp->m_perag[agno].pagb_list)
+ kmem_free(mp->m_perag[agno].pagb_list,
+ sizeof(xfs_perag_busy_t) * XFS_PAGB_NUM_SLOTS);
+ kmem_free(mp->m_perag, sbp->sb_agcount * sizeof(xfs_perag_t));
+ mp->m_perag = NULL;
+ /* FALLTHROUGH */
+ error1:
+ if (uuid_mounted)
+ xfs_uuid_unmount(mp);
+ xfs_freesb(mp);
+ return error;
+}
+
+/*
+ * xfs_unmountfs
+ *
+ * This flushes out the inodes,dquots and the superblock, unmounts the
+ * log and makes sure that incore structures are freed.
+ */
+int
+xfs_unmountfs(xfs_mount_t *mp, struct cred *cr)
+{
+ struct vfs *vfsp = XFS_MTOVFS(mp);
+#if defined(DEBUG) || defined(INDUCE_IO_ERROR)
+ int64_t fsid;
+#endif
+
+ xfs_iflush_all(mp, XFS_FLUSH_ALL);
+
+ XFS_QM_DQPURGEALL(mp,
+ XFS_QMOPT_UQUOTA | XFS_QMOPT_GQUOTA | XFS_QMOPT_UMOUNTING);
+
+ /*
+ * Flush out the log synchronously so that we know for sure
+ * that nothing is pinned. This is important because bflush()
+ * will skip pinned buffers.
+ */
+ xfs_log_force(mp, (xfs_lsn_t)0, XFS_LOG_FORCE | XFS_LOG_SYNC);
+
+ xfs_binval(mp->m_ddev_targp);
+ if (mp->m_rtdev_targp) {
+ xfs_binval(mp->m_rtdev_targp);
+ }
+
+ xfs_unmountfs_writesb(mp);
+
+ xfs_unmountfs_wait(mp); /* wait for async bufs */
+
+ xfs_log_unmount(mp); /* Done! No more fs ops. */
+
+ xfs_freesb(mp);
+
+ /*
+ * All inodes from this mount point should be freed.
+ */
+ ASSERT(mp->m_inodes == NULL);
+
+ /*
+ * We may have bufs that are in the process of getting written still.
+ * We must wait for the I/O completion of those. The sync flag here
+ * does a two pass iteration thru the bufcache.
+ */
+ if (XFS_FORCED_SHUTDOWN(mp)) {
+ xfs_incore_relse(mp->m_ddev_targp, 0, 1); /* synchronous */
+ }
+
+ xfs_unmountfs_close(mp, cr);
+ if ((mp->m_flags & XFS_MOUNT_NOUUID) == 0)
+ xfs_uuid_unmount(mp);
+
+#if defined(DEBUG) || defined(INDUCE_IO_ERROR)
+ /*
+ * clear all error tags on this filesystem
+ */
+ memcpy(&fsid, &vfsp->vfs_fsid, sizeof(int64_t));
+ xfs_errortag_clearall_umount(fsid, mp->m_fsname, 0);
+#endif
+ XFS_IODONE(vfsp);
+ xfs_mount_free(mp, 1);
+ return 0;
+}
+
+void
+xfs_unmountfs_close(xfs_mount_t *mp, struct cred *cr)
+{
+ if (mp->m_logdev_targp != mp->m_ddev_targp)
+ xfs_free_buftarg(mp->m_logdev_targp, 1);
+ if (mp->m_rtdev_targp)
+ xfs_free_buftarg(mp->m_rtdev_targp, 1);
+ xfs_free_buftarg(mp->m_ddev_targp, 0);
+}
+
+void
+xfs_unmountfs_wait(xfs_mount_t *mp)
+{
+ if (mp->m_logdev_targp != mp->m_ddev_targp)
+ xfs_wait_buftarg(mp->m_logdev_targp);
+ if (mp->m_rtdev_targp)
+ xfs_wait_buftarg(mp->m_rtdev_targp);
+ xfs_wait_buftarg(mp->m_ddev_targp);
+}
+
+int
+xfs_unmountfs_writesb(xfs_mount_t *mp)
+{
+ xfs_buf_t *sbp;
+ xfs_sb_t *sb;
+ int error = 0;
+
+ /*
+ * skip superblock write if fs is read-only, or
+ * if we are doing a forced umount.
+ */
+ sbp = xfs_getsb(mp, 0);
+ if (!(XFS_MTOVFS(mp)->vfs_flag & VFS_RDONLY ||
+ XFS_FORCED_SHUTDOWN(mp))) {
+ /*
+ * mark shared-readonly if desired
+ */
+ sb = XFS_BUF_TO_SBP(sbp);
+ if (mp->m_mk_sharedro) {
+ if (!(sb->sb_flags & XFS_SBF_READONLY))
+ sb->sb_flags |= XFS_SBF_READONLY;
+ if (!XFS_SB_VERSION_HASSHARED(sb))
+ XFS_SB_VERSION_ADDSHARED(sb);
+ xfs_fs_cmn_err(CE_NOTE, mp,
+ "Unmounting, marking shared read-only");
+ }
+ XFS_BUF_UNDONE(sbp);
+ XFS_BUF_UNREAD(sbp);
+ XFS_BUF_UNDELAYWRITE(sbp);
+ XFS_BUF_WRITE(sbp);
+ XFS_BUF_UNASYNC(sbp);
+ ASSERT(XFS_BUF_TARGET(sbp) == mp->m_ddev_targp);
+ xfsbdstrat(mp, sbp);
+ /* Nevermind errors we might get here. */
+ error = xfs_iowait(sbp);
+ if (error)
+ xfs_ioerror_alert("xfs_unmountfs_writesb",
+ mp, sbp, XFS_BUF_ADDR(sbp));
+ if (error && mp->m_mk_sharedro)
+ xfs_fs_cmn_err(CE_ALERT, mp, "Superblock write error detected while unmounting. Filesystem may not be marked shared readonly");
+ }
+ xfs_buf_relse(sbp);
+ return (error);
+}
+
+/*
+ * xfs_mod_sb() can be used to copy arbitrary changes to the
+ * in-core superblock into the superblock buffer to be logged.
+ * It does not provide the higher level of locking that is
+ * needed to protect the in-core superblock from concurrent
+ * access.
+ */
+void
+xfs_mod_sb(xfs_trans_t *tp, __int64_t fields)
+{
+ xfs_buf_t *bp;
+ int first;
+ int last;
+ xfs_mount_t *mp;
+ xfs_sb_t *sbp;
+ xfs_sb_field_t f;
+
+ ASSERT(fields);
+ if (!fields)
+ return;
+ mp = tp->t_mountp;
+ bp = xfs_trans_getsb(tp, mp, 0);
+ sbp = XFS_BUF_TO_SBP(bp);
+ first = sizeof(xfs_sb_t);
+ last = 0;
+
+ /* translate/copy */
+
+ xfs_xlatesb(XFS_BUF_PTR(bp), &(mp->m_sb), -1, ARCH_CONVERT, fields);
+
+ /* find modified range */
+
+ f = (xfs_sb_field_t)xfs_lowbit64((__uint64_t)fields);
+ ASSERT((1LL << f) & XFS_SB_MOD_BITS);
+ first = xfs_sb_info[f].offset;
+
+ f = (xfs_sb_field_t)xfs_highbit64((__uint64_t)fields);
+ ASSERT((1LL << f) & XFS_SB_MOD_BITS);
+ last = xfs_sb_info[f + 1].offset - 1;
+
+ xfs_trans_log_buf(tp, bp, first, last);
+}
+
+/*
+ * xfs_mod_incore_sb_unlocked() is a utility routine common used to apply
+ * a delta to a specified field in the in-core superblock. Simply
+ * switch on the field indicated and apply the delta to that field.
+ * Fields are not allowed to dip below zero, so if the delta would
+ * do this do not apply it and return EINVAL.
+ *
+ * The SB_LOCK must be held when this routine is called.
+ */
+STATIC int
+xfs_mod_incore_sb_unlocked(xfs_mount_t *mp, xfs_sb_field_t field,
+ int delta, int rsvd)
+{
+ int scounter; /* short counter for 32 bit fields */
+ long long lcounter; /* long counter for 64 bit fields */
+ long long res_used, rem;
+
+ /*
+ * With the in-core superblock spin lock held, switch
+ * on the indicated field. Apply the delta to the
+ * proper field. If the fields value would dip below
+ * 0, then do not apply the delta and return EINVAL.
+ */
+ switch (field) {
+ case XFS_SBS_ICOUNT:
+ lcounter = (long long)mp->m_sb.sb_icount;
+ lcounter += delta;
+ if (lcounter < 0) {
+ ASSERT(0);
+ return (XFS_ERROR(EINVAL));
+ }
+ mp->m_sb.sb_icount = lcounter;
+ return (0);
+ case XFS_SBS_IFREE:
+ lcounter = (long long)mp->m_sb.sb_ifree;
+ lcounter += delta;
+ if (lcounter < 0) {
+ ASSERT(0);
+ return (XFS_ERROR(EINVAL));
+ }
+ mp->m_sb.sb_ifree = lcounter;
+ return (0);
+ case XFS_SBS_FDBLOCKS:
+
+ lcounter = (long long)mp->m_sb.sb_fdblocks;
+ res_used = (long long)(mp->m_resblks - mp->m_resblks_avail);
+
+ if (delta > 0) { /* Putting blocks back */
+ if (res_used > delta) {
+ mp->m_resblks_avail += delta;
+ } else {
+ rem = delta - res_used;
+ mp->m_resblks_avail = mp->m_resblks;
+ lcounter += rem;
+ }
+ } else { /* Taking blocks away */
+
+ lcounter += delta;
+
+ /*
+ * If were out of blocks, use any available reserved blocks if
+ * were allowed to.
+ */
+
+ if (lcounter < 0) {
+ if (rsvd) {
+ lcounter = (long long)mp->m_resblks_avail + delta;
+ if (lcounter < 0) {
+ return (XFS_ERROR(ENOSPC));
+ }
+ mp->m_resblks_avail = lcounter;
+ return (0);
+ } else { /* not reserved */
+ return (XFS_ERROR(ENOSPC));
+ }
+ }
+ }
+
+ mp->m_sb.sb_fdblocks = lcounter;
+ return (0);
+ case XFS_SBS_FREXTENTS:
+ lcounter = (long long)mp->m_sb.sb_frextents;
+ lcounter += delta;
+ if (lcounter < 0) {
+ return (XFS_ERROR(ENOSPC));
+ }
+ mp->m_sb.sb_frextents = lcounter;
+ return (0);
+ case XFS_SBS_DBLOCKS:
+ lcounter = (long long)mp->m_sb.sb_dblocks;
+ lcounter += delta;
+ if (lcounter < 0) {
+ ASSERT(0);
+ return (XFS_ERROR(EINVAL));
+ }
+ mp->m_sb.sb_dblocks = lcounter;
+ return (0);
+ case XFS_SBS_AGCOUNT:
+ scounter = mp->m_sb.sb_agcount;
+ scounter += delta;
+ if (scounter < 0) {
+ ASSERT(0);
+ return (XFS_ERROR(EINVAL));
+ }
+ mp->m_sb.sb_agcount = scounter;
+ return (0);
+ case XFS_SBS_IMAX_PCT:
+ scounter = mp->m_sb.sb_imax_pct;
+ scounter += delta;
+ if (scounter < 0) {
+ ASSERT(0);
+ return (XFS_ERROR(EINVAL));
+ }
+ mp->m_sb.sb_imax_pct = scounter;
+ return (0);
+ case XFS_SBS_REXTSIZE:
+ scounter = mp->m_sb.sb_rextsize;
+ scounter += delta;
+ if (scounter < 0) {
+ ASSERT(0);
+ return (XFS_ERROR(EINVAL));
+ }
+ mp->m_sb.sb_rextsize = scounter;
+ return (0);
+ case XFS_SBS_RBMBLOCKS:
+ scounter = mp->m_sb.sb_rbmblocks;
+ scounter += delta;
+ if (scounter < 0) {
+ ASSERT(0);
+ return (XFS_ERROR(EINVAL));
+ }
+ mp->m_sb.sb_rbmblocks = scounter;
+ return (0);
+ case XFS_SBS_RBLOCKS:
+ lcounter = (long long)mp->m_sb.sb_rblocks;
+ lcounter += delta;
+ if (lcounter < 0) {
+ ASSERT(0);
+ return (XFS_ERROR(EINVAL));
+ }
+ mp->m_sb.sb_rblocks = lcounter;
+ return (0);
+ case XFS_SBS_REXTENTS:
+ lcounter = (long long)mp->m_sb.sb_rextents;
+ lcounter += delta;
+ if (lcounter < 0) {
+ ASSERT(0);
+ return (XFS_ERROR(EINVAL));
+ }
+ mp->m_sb.sb_rextents = lcounter;
+ return (0);
+ case XFS_SBS_REXTSLOG:
+ scounter = mp->m_sb.sb_rextslog;
+ scounter += delta;
+ if (scounter < 0) {
+ ASSERT(0);
+ return (XFS_ERROR(EINVAL));
+ }
+ mp->m_sb.sb_rextslog = scounter;
+ return (0);
+ default:
+ ASSERT(0);
+ return (XFS_ERROR(EINVAL));
+ }
+}
+
+/*
+ * xfs_mod_incore_sb() is used to change a field in the in-core
+ * superblock structure by the specified delta. This modification
+ * is protected by the SB_LOCK. Just use the xfs_mod_incore_sb_unlocked()
+ * routine to do the work.
+ */
+int
+xfs_mod_incore_sb(xfs_mount_t *mp, xfs_sb_field_t field, int delta, int rsvd)
+{
+ unsigned long s;
+ int status;
+
+ s = XFS_SB_LOCK(mp);
+ status = xfs_mod_incore_sb_unlocked(mp, field, delta, rsvd);
+ XFS_SB_UNLOCK(mp, s);
+ return (status);
+}
+
+/*
+ * xfs_mod_incore_sb_batch() is used to change more than one field
+ * in the in-core superblock structure at a time. This modification
+ * is protected by a lock internal to this module. The fields and
+ * changes to those fields are specified in the array of xfs_mod_sb
+ * structures passed in.
+ *
+ * Either all of the specified deltas will be applied or none of
+ * them will. If any modified field dips below 0, then all modifications
+ * will be backed out and EINVAL will be returned.
+ */
+int
+xfs_mod_incore_sb_batch(xfs_mount_t *mp, xfs_mod_sb_t *msb, uint nmsb, int rsvd)
+{
+ unsigned long s;
+ int status=0;
+ xfs_mod_sb_t *msbp;
+
+ /*
+ * Loop through the array of mod structures and apply each
+ * individually. If any fail, then back out all those
+ * which have already been applied. Do all of this within
+ * the scope of the SB_LOCK so that all of the changes will
+ * be atomic.
+ */
+ s = XFS_SB_LOCK(mp);
+ msbp = &msb[0];
+ for (msbp = &msbp[0]; msbp < (msb + nmsb); msbp++) {
+ /*
+ * Apply the delta at index n. If it fails, break
+ * from the loop so we'll fall into the undo loop
+ * below.
+ */
+ status = xfs_mod_incore_sb_unlocked(mp, msbp->msb_field,
+ msbp->msb_delta, rsvd);
+ if (status != 0) {
+ break;
+ }
+ }
+
+ /*
+ * If we didn't complete the loop above, then back out
+ * any changes made to the superblock. If you add code
+ * between the loop above and here, make sure that you
+ * preserve the value of status. Loop back until
+ * we step below the beginning of the array. Make sure
+ * we don't touch anything back there.
+ */
+ if (status != 0) {
+ msbp--;
+ while (msbp >= msb) {
+ status = xfs_mod_incore_sb_unlocked(mp,
+ msbp->msb_field, -(msbp->msb_delta), rsvd);
+ ASSERT(status == 0);
+ msbp--;
+ }
+ }
+ XFS_SB_UNLOCK(mp, s);
+ return (status);
+}
+
+/*
+ * xfs_getsb() is called to obtain the buffer for the superblock.
+ * The buffer is returned locked and read in from disk.
+ * The buffer should be released with a call to xfs_brelse().
+ *
+ * If the flags parameter is BUF_TRYLOCK, then we'll only return
+ * the superblock buffer if it can be locked without sleeping.
+ * If it can't then we'll return NULL.
+ */
+xfs_buf_t *
+xfs_getsb(
+ xfs_mount_t *mp,
+ int flags)
+{
+ xfs_buf_t *bp;
+
+ ASSERT(mp->m_sb_bp != NULL);
+ bp = mp->m_sb_bp;
+ if (flags & XFS_BUF_TRYLOCK) {
+ if (!XFS_BUF_CPSEMA(bp)) {
+ return NULL;
+ }
+ } else {
+ XFS_BUF_PSEMA(bp, PRIBIO);
+ }
+ XFS_BUF_HOLD(bp);
+ ASSERT(XFS_BUF_ISDONE(bp));
+ return (bp);
+}
+
+/*
+ * Used to free the superblock along various error paths.
+ */
+void
+xfs_freesb(
+ xfs_mount_t *mp)
+{
+ xfs_buf_t *bp;
+
+ /*
+ * Use xfs_getsb() so that the buffer will be locked
+ * when we call xfs_buf_relse().
+ */
+ bp = xfs_getsb(mp, 0);
+ XFS_BUF_UNMANAGE(bp);
+ xfs_buf_relse(bp);
+ mp->m_sb_bp = NULL;
+}
+
+/*
+ * See if the UUID is unique among mounted XFS filesystems.
+ * Mount fails if UUID is nil or a FS with the same UUID is already mounted.
+ */
+STATIC int
+xfs_uuid_mount(
+ xfs_mount_t *mp)
+{
+ if (uuid_is_nil(&mp->m_sb.sb_uuid)) {
+ cmn_err(CE_WARN,
+ "XFS: Filesystem %s has nil UUID - can't mount",
+ mp->m_fsname);
+ return -1;
+ }
+ if (!uuid_table_insert(&mp->m_sb.sb_uuid)) {
+ cmn_err(CE_WARN,
+ "XFS: Filesystem %s has duplicate UUID - can't mount",
+ mp->m_fsname);
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Remove filesystem from the UUID table.
+ */
+STATIC void
+xfs_uuid_unmount(
+ xfs_mount_t *mp)
+{
+ uuid_table_remove(&mp->m_sb.sb_uuid);
+}
+
+/*
+ * Used to log changes to the superblock unit and width fields which could
+ * be altered by the mount options. Only the first superblock is updated.
+ */
+STATIC void
+xfs_mount_log_sbunit(
+ xfs_mount_t *mp,
+ __int64_t fields)
+{
+ xfs_trans_t *tp;
+
+ ASSERT(fields & (XFS_SB_UNIT|XFS_SB_WIDTH|XFS_SB_UUID));
+
+ tp = xfs_trans_alloc(mp, XFS_TRANS_SB_UNIT);
+ if (xfs_trans_reserve(tp, 0, mp->m_sb.sb_sectsize + 128, 0, 0,
+ XFS_DEFAULT_LOG_COUNT)) {
+ xfs_trans_cancel(tp, 0);
+ return;
+ }
+ xfs_mod_sb(tp, fields);
+ xfs_trans_commit(tp, 0, NULL);
+}
diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h
new file mode 100644
index 00000000000000..a37420eb5fc6b8
--- /dev/null
+++ b/fs/xfs/xfs_mount.h
@@ -0,0 +1,572 @@
+/*
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_MOUNT_H__
+#define __XFS_MOUNT_H__
+
+
+typedef struct xfs_trans_reservations {
+ uint tr_write; /* extent alloc trans */
+ uint tr_itruncate; /* truncate trans */
+ uint tr_rename; /* rename trans */
+ uint tr_link; /* link trans */
+ uint tr_remove; /* unlink trans */
+ uint tr_symlink; /* symlink trans */
+ uint tr_create; /* create trans */
+ uint tr_mkdir; /* mkdir trans */
+ uint tr_ifree; /* inode free trans */
+ uint tr_ichange; /* inode update trans */
+ uint tr_growdata; /* fs data section grow trans */
+ uint tr_swrite; /* sync write inode trans */
+ uint tr_addafork; /* cvt inode to attributed trans */
+ uint tr_writeid; /* write setuid/setgid file */
+ uint tr_attrinval; /* attr fork buffer invalidation */
+ uint tr_attrset; /* set/create an attribute */
+ uint tr_attrrm; /* remove an attribute */
+ uint tr_clearagi; /* clear bad agi unlinked ino bucket */
+ uint tr_growrtalloc; /* grow realtime allocations */
+ uint tr_growrtzero; /* grow realtime zeroing */
+ uint tr_growrtfree; /* grow realtime freeing */
+} xfs_trans_reservations_t;
+
+
+#ifndef __KERNEL__
+/*
+ * Moved here from xfs_ag.h to avoid reordering header files
+ */
+#define XFS_DADDR_TO_AGNO(mp,d) \
+ ((xfs_agnumber_t)(XFS_BB_TO_FSBT(mp, d) / (mp)->m_sb.sb_agblocks))
+#define XFS_DADDR_TO_AGBNO(mp,d) \
+ ((xfs_agblock_t)(XFS_BB_TO_FSBT(mp, d) % (mp)->m_sb.sb_agblocks))
+#else
+struct cred;
+struct log;
+struct vfs;
+struct vnode;
+struct xfs_mount_args;
+struct xfs_ihash;
+struct xfs_chash;
+struct xfs_inode;
+struct xfs_perag;
+struct xfs_iocore;
+struct xfs_bmbt_irec;
+struct xfs_bmap_free;
+
+#define AIL_LOCK_T lock_t
+#define AIL_LOCKINIT(x,y) spinlock_init(x,y)
+#define AIL_LOCK_DESTROY(x) spinlock_destroy(x)
+#define AIL_LOCK(mp,s) s=mutex_spinlock(&(mp)->m_ail_lock)
+#define AIL_UNLOCK(mp,s) mutex_spinunlock(&(mp)->m_ail_lock, s)
+
+
+/*
+ * Prototypes and functions for the Data Migration subsystem.
+ */
+
+typedef int (*xfs_send_data_t)(int, struct vnode *,
+ xfs_off_t, size_t, int, vrwlock_t *);
+typedef int (*xfs_send_mmap_t)(struct vm_area_struct *, uint);
+typedef int (*xfs_send_destroy_t)(struct vnode *, dm_right_t);
+typedef int (*xfs_send_namesp_t)(dm_eventtype_t, struct vfs *,
+ struct vnode *,
+ dm_right_t, struct vnode *, dm_right_t,
+ char *, char *, mode_t, int, int);
+typedef void (*xfs_send_unmount_t)(struct vfs *, struct vnode *,
+ dm_right_t, mode_t, int, int);
+
+typedef struct xfs_dmops {
+ xfs_send_data_t xfs_send_data;
+ xfs_send_mmap_t xfs_send_mmap;
+ xfs_send_destroy_t xfs_send_destroy;
+ xfs_send_namesp_t xfs_send_namesp;
+ xfs_send_unmount_t xfs_send_unmount;
+} xfs_dmops_t;
+
+#define XFS_SEND_DATA(mp, ev,vp,off,len,fl,lock) \
+ (*(mp)->m_dm_ops.xfs_send_data)(ev,vp,off,len,fl,lock)
+#define XFS_SEND_MMAP(mp, vma,fl) \
+ (*(mp)->m_dm_ops.xfs_send_mmap)(vma,fl)
+#define XFS_SEND_DESTROY(mp, vp,right) \
+ (*(mp)->m_dm_ops.xfs_send_destroy)(vp,right)
+#define XFS_SEND_NAMESP(mp, ev,b1,r1,b2,r2,n1,n2,mode,rval,fl) \
+ (*(mp)->m_dm_ops.xfs_send_namesp)(ev,NULL,b1,r1,b2,r2,n1,n2,mode,rval,fl)
+#define XFS_SEND_PREUNMOUNT(mp, vfs,b1,r1,b2,r2,n1,n2,mode,rval,fl) \
+ (*(mp)->m_dm_ops.xfs_send_namesp)(DM_EVENT_PREUNMOUNT,vfs,b1,r1,b2,r2,n1,n2,mode,rval,fl)
+#define XFS_SEND_UNMOUNT(mp, vfsp,vp,right,mode,rval,fl) \
+ (*(mp)->m_dm_ops.xfs_send_unmount)(vfsp,vp,right,mode,rval,fl)
+
+
+/*
+ * Prototypes and functions for the Quota Management subsystem.
+ */
+
+struct xfs_dquot;
+struct xfs_dqtrxops;
+struct xfs_quotainfo;
+
+typedef int (*xfs_qminit_t)(struct xfs_mount *, uint *, uint *);
+typedef int (*xfs_qmmount_t)(struct xfs_mount *, uint, uint, int);
+typedef int (*xfs_qmunmount_t)(struct xfs_mount *);
+typedef void (*xfs_qmdone_t)(struct xfs_mount *);
+typedef void (*xfs_dqrele_t)(struct xfs_dquot *);
+typedef int (*xfs_dqattach_t)(struct xfs_inode *, uint);
+typedef void (*xfs_dqdetach_t)(struct xfs_inode *);
+typedef int (*xfs_dqpurgeall_t)(struct xfs_mount *, uint);
+typedef int (*xfs_dqvopalloc_t)(struct xfs_mount *,
+ struct xfs_inode *, uid_t, gid_t, uint,
+ struct xfs_dquot **, struct xfs_dquot **);
+typedef void (*xfs_dqvopcreate_t)(struct xfs_trans *, struct xfs_inode *,
+ struct xfs_dquot *, struct xfs_dquot *);
+typedef int (*xfs_dqvoprename_t)(struct xfs_inode **);
+typedef struct xfs_dquot * (*xfs_dqvopchown_t)(
+ struct xfs_trans *, struct xfs_inode *,
+ struct xfs_dquot **, struct xfs_dquot *);
+typedef int (*xfs_dqvopchownresv_t)(struct xfs_trans *, struct xfs_inode *,
+ struct xfs_dquot *, struct xfs_dquot *, uint);
+
+typedef struct xfs_qmops {
+ xfs_qminit_t xfs_qminit;
+ xfs_qmdone_t xfs_qmdone;
+ xfs_qmmount_t xfs_qmmount;
+ xfs_qmunmount_t xfs_qmunmount;
+ xfs_dqrele_t xfs_dqrele;
+ xfs_dqattach_t xfs_dqattach;
+ xfs_dqdetach_t xfs_dqdetach;
+ xfs_dqpurgeall_t xfs_dqpurgeall;
+ xfs_dqvopalloc_t xfs_dqvopalloc;
+ xfs_dqvopcreate_t xfs_dqvopcreate;
+ xfs_dqvoprename_t xfs_dqvoprename;
+ xfs_dqvopchown_t xfs_dqvopchown;
+ xfs_dqvopchownresv_t xfs_dqvopchownresv;
+ struct xfs_dqtrxops *xfs_dqtrxops;
+} xfs_qmops_t;
+
+#define XFS_QM_INIT(mp, mnt, fl) \
+ (*(mp)->m_qm_ops.xfs_qminit)(mp, mnt, fl)
+#define XFS_QM_MOUNT(mp, mnt, fl, mfsi_flags) \
+ (*(mp)->m_qm_ops.xfs_qmmount)(mp, mnt, fl, mfsi_flags)
+#define XFS_QM_UNMOUNT(mp) \
+ (*(mp)->m_qm_ops.xfs_qmunmount)(mp)
+#define XFS_QM_DONE(mp) \
+ (*(mp)->m_qm_ops.xfs_qmdone)(mp)
+#define XFS_QM_DQRELE(mp, dq) \
+ (*(mp)->m_qm_ops.xfs_dqrele)(dq)
+#define XFS_QM_DQATTACH(mp, ip, fl) \
+ (*(mp)->m_qm_ops.xfs_dqattach)(ip, fl)
+#define XFS_QM_DQDETACH(mp, ip) \
+ (*(mp)->m_qm_ops.xfs_dqdetach)(ip)
+#define XFS_QM_DQPURGEALL(mp, fl) \
+ (*(mp)->m_qm_ops.xfs_dqpurgeall)(mp, fl)
+#define XFS_QM_DQVOPALLOC(mp, ip, uid, gid, fl, dq1, dq2) \
+ (*(mp)->m_qm_ops.xfs_dqvopalloc)(mp, ip, uid, gid, fl, dq1, dq2)
+#define XFS_QM_DQVOPCREATE(mp, tp, ip, dq1, dq2) \
+ (*(mp)->m_qm_ops.xfs_dqvopcreate)(tp, ip, dq1, dq2)
+#define XFS_QM_DQVOPRENAME(mp, ip) \
+ (*(mp)->m_qm_ops.xfs_dqvoprename)(ip)
+#define XFS_QM_DQVOPCHOWN(mp, tp, ip, dqp, dq) \
+ (*(mp)->m_qm_ops.xfs_dqvopchown)(tp, ip, dqp, dq)
+#define XFS_QM_DQVOPCHOWNRESV(mp, tp, ip, dq1, dq2, fl) \
+ (*(mp)->m_qm_ops.xfs_dqvopchownresv)(tp, ip, dq1, dq2, fl)
+
+
+/*
+ * Prototypes and functions for I/O core modularization.
+ */
+
+typedef int (*xfs_ioinit_t)(struct vfs *,
+ struct xfs_mount_args *, int);
+typedef int (*xfs_bmapi_t)(struct xfs_trans *, void *,
+ xfs_fileoff_t, xfs_filblks_t, int,
+ xfs_fsblock_t *, xfs_extlen_t,
+ struct xfs_bmbt_irec *, int *,
+ struct xfs_bmap_free *);
+typedef int (*xfs_bmap_eof_t)(void *, xfs_fileoff_t, int, int *);
+typedef int (*xfs_iomap_write_direct_t)(
+ void *, loff_t, size_t, int,
+ struct xfs_bmbt_irec *, int *, int);
+typedef int (*xfs_iomap_write_delay_t)(
+ void *, loff_t, size_t, int,
+ struct xfs_bmbt_irec *, int *);
+typedef int (*xfs_iomap_write_allocate_t)(
+ void *, struct xfs_bmbt_irec *, int *);
+typedef int (*xfs_iomap_write_unwritten_t)(
+ void *, loff_t, size_t);
+typedef uint (*xfs_lck_map_shared_t)(void *);
+typedef void (*xfs_lock_t)(void *, uint);
+typedef void (*xfs_lock_demote_t)(void *, uint);
+typedef int (*xfs_lock_nowait_t)(void *, uint);
+typedef void (*xfs_unlk_t)(void *, unsigned int);
+typedef xfs_fsize_t (*xfs_size_t)(void *);
+typedef xfs_fsize_t (*xfs_iodone_t)(struct vfs *);
+
+typedef struct xfs_ioops {
+ xfs_ioinit_t xfs_ioinit;
+ xfs_bmapi_t xfs_bmapi_func;
+ xfs_bmap_eof_t xfs_bmap_eof_func;
+ xfs_iomap_write_direct_t xfs_iomap_write_direct;
+ xfs_iomap_write_delay_t xfs_iomap_write_delay;
+ xfs_iomap_write_allocate_t xfs_iomap_write_allocate;
+ xfs_iomap_write_unwritten_t xfs_iomap_write_unwritten;
+ xfs_lock_t xfs_ilock;
+ xfs_lck_map_shared_t xfs_lck_map_shared;
+ xfs_lock_demote_t xfs_ilock_demote;
+ xfs_lock_nowait_t xfs_ilock_nowait;
+ xfs_unlk_t xfs_unlock;
+ xfs_size_t xfs_size_func;
+ xfs_iodone_t xfs_iodone;
+} xfs_ioops_t;
+
+#define XFS_IOINIT(vfsp, args, flags) \
+ (*(mp)->m_io_ops.xfs_ioinit)(vfsp, args, flags)
+#define XFS_BMAPI(mp, trans,io,bno,len,f,first,tot,mval,nmap,flist) \
+ (*(mp)->m_io_ops.xfs_bmapi_func) \
+ (trans,(io)->io_obj,bno,len,f,first,tot,mval,nmap,flist)
+#define XFS_BMAP_EOF(mp, io, endoff, whichfork, eof) \
+ (*(mp)->m_io_ops.xfs_bmap_eof_func) \
+ ((io)->io_obj, endoff, whichfork, eof)
+#define XFS_IOMAP_WRITE_DIRECT(mp, io, offset, count, flags, mval, nmap, found)\
+ (*(mp)->m_io_ops.xfs_iomap_write_direct) \
+ ((io)->io_obj, offset, count, flags, mval, nmap, found)
+#define XFS_IOMAP_WRITE_DELAY(mp, io, offset, count, flags, mval, nmap) \
+ (*(mp)->m_io_ops.xfs_iomap_write_delay) \
+ ((io)->io_obj, offset, count, flags, mval, nmap)
+#define XFS_IOMAP_WRITE_ALLOCATE(mp, io, mval, nmap) \
+ (*(mp)->m_io_ops.xfs_iomap_write_allocate) \
+ ((io)->io_obj, mval, nmap)
+#define XFS_IOMAP_WRITE_UNWRITTEN(mp, io, offset, count) \
+ (*(mp)->m_io_ops.xfs_iomap_write_unwritten) \
+ ((io)->io_obj, offset, count)
+#define XFS_LCK_MAP_SHARED(mp, io) \
+ (*(mp)->m_io_ops.xfs_lck_map_shared)((io)->io_obj)
+#define XFS_ILOCK(mp, io, mode) \
+ (*(mp)->m_io_ops.xfs_ilock)((io)->io_obj, mode)
+#define XFS_ILOCK_NOWAIT(mp, io, mode) \
+ (*(mp)->m_io_ops.xfs_ilock_nowait)((io)->io_obj, mode)
+#define XFS_IUNLOCK(mp, io, mode) \
+ (*(mp)->m_io_ops.xfs_unlock)((io)->io_obj, mode)
+#define XFS_ILOCK_DEMOTE(mp, io, mode) \
+ (*(mp)->m_io_ops.xfs_ilock_demote)((io)->io_obj, mode)
+#define XFS_SIZE(mp, io) \
+ (*(mp)->m_io_ops.xfs_size_func)((io)->io_obj)
+#define XFS_IODONE(vfsp) \
+ (*(mp)->m_io_ops.xfs_iodone)(vfsp)
+
+
+typedef struct xfs_mount {
+ bhv_desc_t m_bhv; /* vfs xfs behavior */
+ xfs_tid_t m_tid; /* next unused tid for fs */
+ AIL_LOCK_T m_ail_lock; /* fs AIL mutex */
+ xfs_ail_entry_t m_ail; /* fs active log item list */
+ uint m_ail_gen; /* fs AIL generation count */
+ xfs_sb_t m_sb; /* copy of fs superblock */
+ lock_t m_sb_lock; /* sb counter mutex */
+ struct xfs_buf *m_sb_bp; /* buffer for superblock */
+ char *m_fsname; /* filesystem name */
+ int m_fsname_len; /* strlen of fs name */
+ int m_bsize; /* fs logical block size */
+ xfs_agnumber_t m_agfrotor; /* last ag where space found */
+ xfs_agnumber_t m_agirotor; /* last ag dir inode alloced */
+ lock_t m_agirotor_lock;/* .. and lock protecting it */
+ xfs_agnumber_t m_maxagi; /* highest inode alloc group */
+ int m_ihsize; /* size of next field */
+ struct xfs_ihash *m_ihash; /* fs private inode hash table*/
+ struct xfs_inode *m_inodes; /* active inode list */
+ struct list_head m_del_inodes; /* inodes to reclaim */
+ mutex_t m_ilock; /* inode list mutex */
+ uint m_ireclaims; /* count of calls to reclaim*/
+ uint m_readio_log; /* min read size log bytes */
+ uint m_readio_blocks; /* min read size blocks */
+ uint m_writeio_log; /* min write size log bytes */
+ uint m_writeio_blocks; /* min write size blocks */
+ struct log *m_log; /* log specific stuff */
+ int m_logbufs; /* number of log buffers */
+ int m_logbsize; /* size of each log buffer */
+ uint m_rsumlevels; /* rt summary levels */
+ uint m_rsumsize; /* size of rt summary, bytes */
+ struct xfs_inode *m_rbmip; /* pointer to bitmap inode */
+ struct xfs_inode *m_rsumip; /* pointer to summary inode */
+ struct xfs_inode *m_rootip; /* pointer to root directory */
+ struct xfs_quotainfo *m_quotainfo; /* disk quota information */
+ xfs_buftarg_t *m_ddev_targp; /* saves taking the address */
+ xfs_buftarg_t *m_logdev_targp;/* ptr to log device */
+ xfs_buftarg_t *m_rtdev_targp; /* ptr to rt device */
+#define m_dev m_ddev_targp->pbr_dev
+ __uint8_t m_dircook_elog; /* log d-cookie entry bits */
+ __uint8_t m_blkbit_log; /* blocklog + NBBY */
+ __uint8_t m_blkbb_log; /* blocklog - BBSHIFT */
+ __uint8_t m_agno_log; /* log #ag's */
+ __uint8_t m_agino_log; /* #bits for agino in inum */
+ __uint8_t m_nreadaheads; /* #readahead buffers */
+ __uint16_t m_inode_cluster_size;/* min inode buf size */
+ uint m_blockmask; /* sb_blocksize-1 */
+ uint m_blockwsize; /* sb_blocksize in words */
+ uint m_blockwmask; /* blockwsize-1 */
+ uint m_alloc_mxr[2]; /* XFS_ALLOC_BLOCK_MAXRECS */
+ uint m_alloc_mnr[2]; /* XFS_ALLOC_BLOCK_MINRECS */
+ uint m_bmap_dmxr[2]; /* XFS_BMAP_BLOCK_DMAXRECS */
+ uint m_bmap_dmnr[2]; /* XFS_BMAP_BLOCK_DMINRECS */
+ uint m_inobt_mxr[2]; /* XFS_INOBT_BLOCK_MAXRECS */
+ uint m_inobt_mnr[2]; /* XFS_INOBT_BLOCK_MINRECS */
+ uint m_ag_maxlevels; /* XFS_AG_MAXLEVELS */
+ uint m_bm_maxlevels[2]; /* XFS_BM_MAXLEVELS */
+ uint m_in_maxlevels; /* XFS_IN_MAXLEVELS */
+ struct xfs_perag *m_perag; /* per-ag accounting info */
+ struct rw_semaphore m_peraglock; /* lock for m_perag (pointer) */
+ sema_t m_growlock; /* growfs mutex */
+ int m_fixedfsid[2]; /* unchanged for life of FS */
+ uint m_dmevmask; /* DMI events for this FS */
+ uint m_flags; /* global mount flags */
+ uint m_attroffset; /* inode attribute offset */
+ uint m_dir_node_ents; /* #entries in a dir danode */
+ uint m_attr_node_ents; /* #entries in attr danode */
+ int m_ialloc_inos; /* inodes in inode allocation */
+ int m_ialloc_blks; /* blocks in inode allocation */
+ int m_litino; /* size of inode union area */
+ int m_inoalign_mask;/* mask sb_inoalignmt if used */
+ uint m_qflags; /* quota status flags */
+ xfs_trans_reservations_t m_reservations;/* precomputed res values */
+ __uint64_t m_maxicount; /* maximum inode count */
+ __uint64_t m_maxioffset; /* maximum inode offset */
+ __uint64_t m_resblks; /* total reserved blocks */
+ __uint64_t m_resblks_avail;/* available reserved blocks */
+#if XFS_BIG_INUMS
+ xfs_ino_t m_inoadd; /* add value for ino64_offset */
+#endif
+ int m_dalign; /* stripe unit */
+ int m_swidth; /* stripe width */
+ int m_sinoalign; /* stripe unit inode alignmnt */
+ int m_attr_magicpct;/* 37% of the blocksize */
+ int m_dir_magicpct; /* 37% of the dir blocksize */
+ __uint8_t m_mk_sharedro; /* mark shared ro on unmount */
+ __uint8_t m_inode_quiesce;/* call quiesce on new inodes.
+ field governed by m_ilock */
+ __uint8_t m_sectbb_log; /* sectlog - BBSHIFT */
+ __uint8_t m_dirversion; /* 1 or 2 */
+ xfs_dirops_t m_dirops; /* table of dir funcs */
+ int m_dirblksize; /* directory block sz--bytes */
+ int m_dirblkfsbs; /* directory block sz--fsbs */
+ xfs_dablk_t m_dirdatablk; /* blockno of dir data v2 */
+ xfs_dablk_t m_dirleafblk; /* blockno of dir non-data v2 */
+ xfs_dablk_t m_dirfreeblk; /* blockno of dirfreeindex v2 */
+ int m_chsize; /* size of next field */
+ struct xfs_chash *m_chash; /* fs private inode per-cluster
+ * hash table */
+ struct xfs_dmops m_dm_ops; /* vector of DMI ops */
+ struct xfs_qmops m_qm_ops; /* vector of XQM ops */
+ struct xfs_ioops m_io_ops; /* vector of I/O ops */
+ atomic_t m_active_trans; /* number trans frozen */
+} xfs_mount_t;
+
+/*
+ * Flags for m_flags.
+ */
+#define XFS_MOUNT_WSYNC 0x00000001 /* for nfs - all metadata ops
+ must be synchronous except
+ for space allocations */
+#define XFS_MOUNT_INO64 0x00000002
+ /* 0x00000004 -- currently unused */
+ /* 0x00000008 -- currently unused */
+#define XFS_MOUNT_FS_SHUTDOWN 0x00000010 /* atomic stop of all filesystem
+ operations, typically for
+ disk errors in metadata */
+#define XFS_MOUNT_NOATIME 0x00000020 /* don't modify inode access
+ times on reads */
+#define XFS_MOUNT_RETERR 0x00000040 /* return alignment errors to
+ user */
+#define XFS_MOUNT_NOALIGN 0x00000080 /* turn off stripe alignment
+ allocations */
+ /* 0x00000100 -- currently unused */
+ /* 0x00000200 -- currently unused */
+#define XFS_MOUNT_NORECOVERY 0x00000400 /* no recovery - dirty fs */
+#define XFS_MOUNT_SHARED 0x00000800 /* shared mount */
+#define XFS_MOUNT_DFLT_IOSIZE 0x00001000 /* set default i/o size */
+#define XFS_MOUNT_OSYNCISOSYNC 0x00002000 /* o_sync is REALLY o_sync */
+ /* osyncisdsync is now default*/
+#define XFS_MOUNT_32BITINODES 0x00004000 /* do not create inodes above
+ * 32 bits in size */
+#define XFS_MOUNT_32BITINOOPT 0x00008000 /* saved mount option state */
+#define XFS_MOUNT_NOUUID 0x00010000 /* ignore uuid during mount */
+#define XFS_MOUNT_NOLOGFLUSH 0x00020000
+#define XFS_MOUNT_IDELETE 0x00040000 /* delete empty inode clusters*/
+#define XFS_MOUNT_SWALLOC 0x00080000 /* turn on stripe width
+ * allocation */
+
+/*
+ * Default minimum read and write sizes.
+ */
+#define XFS_READIO_LOG_LARGE 16
+#define XFS_WRITEIO_LOG_LARGE 16
+
+/*
+ * Max and min values for UIO and mount-option defined I/O sizes;
+ * min value can't be less than a page. Currently unused.
+ */
+#define XFS_MAX_IO_LOG 16 /* 64K */
+#define XFS_MIN_IO_LOG PAGE_SHIFT
+
+/*
+ * Synchronous read and write sizes. This should be
+ * better for NFSv2 wsync filesystems.
+ */
+#define XFS_WSYNC_READIO_LOG 15 /* 32K */
+#define XFS_WSYNC_WRITEIO_LOG 14 /* 16K */
+
+#define XFS_MAXIOFFSET(mp) ((mp)->m_maxioffset)
+
+#define XFS_FORCED_SHUTDOWN(mp) ((mp)->m_flags & XFS_MOUNT_FS_SHUTDOWN)
+#define xfs_force_shutdown(m,f) \
+ VFS_FORCE_SHUTDOWN((XFS_MTOVFS(m)), f, __FILE__, __LINE__)
+
+/*
+ * Flags sent to xfs_force_shutdown.
+ */
+#define XFS_METADATA_IO_ERROR 0x1
+#define XFS_LOG_IO_ERROR 0x2
+#define XFS_FORCE_UMOUNT 0x4
+#define XFS_CORRUPT_INCORE 0x8 /* Corrupt in-memory data structures */
+#define XFS_SHUTDOWN_REMOTE_REQ 0x10 /* Shutdown came from remote cell */
+
+/*
+ * xflags for xfs_syncsub
+ */
+#define XFS_XSYNC_RELOC 0x01
+
+/*
+ * Flags for xfs_mountfs
+ */
+#define XFS_MFSI_SECOND 0x01 /* Secondary mount -- skip stuff */
+#define XFS_MFSI_CLIENT 0x02 /* Is a client -- skip lots of stuff */
+#define XFS_MFSI_NOUNLINK 0x08 /* Skip unlinked inode processing in */
+ /* log recovery */
+#define XFS_MFSI_NO_QUOTACHECK 0x10 /* Skip quotacheck processing */
+
+/*
+ * Macros for getting from mount to vfs and back.
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_MTOVFS)
+struct vfs *xfs_mtovfs(xfs_mount_t *mp);
+#define XFS_MTOVFS(mp) xfs_mtovfs(mp)
+#else
+#define XFS_MTOVFS(mp) (bhvtovfs(&(mp)->m_bhv))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BHVTOM)
+xfs_mount_t *xfs_bhvtom(bhv_desc_t *bdp);
+#define XFS_BHVTOM(bdp) xfs_bhvtom(bdp)
+#else
+#define XFS_BHVTOM(bdp) ((xfs_mount_t *)BHV_PDATA(bdp))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_VFSTOM)
+xfs_mount_t *xfs_vfstom(vfs_t *vfs);
+#define XFS_VFSTOM(vfs) xfs_vfstom(vfs)
+#else
+#define XFS_VFSTOM(vfs) \
+ (XFS_BHVTOM(bhv_lookup(VFS_BHVHEAD(vfs), &xfs_vfsops)))
+#endif
+
+
+/*
+ * Moved here from xfs_ag.h to avoid reordering header files
+ */
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DADDR_TO_AGNO)
+xfs_agnumber_t xfs_daddr_to_agno(struct xfs_mount *mp, xfs_daddr_t d);
+#define XFS_DADDR_TO_AGNO(mp,d) xfs_daddr_to_agno(mp,d)
+#else
+
+static inline xfs_agnumber_t XFS_DADDR_TO_AGNO(xfs_mount_t *mp, xfs_daddr_t d)
+{
+ d = XFS_BB_TO_FSBT(mp, d);
+ do_div(d, mp->m_sb.sb_agblocks);
+ return (xfs_agnumber_t) d;
+}
+
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DADDR_TO_AGBNO)
+xfs_agblock_t xfs_daddr_to_agbno(struct xfs_mount *mp, xfs_daddr_t d);
+#define XFS_DADDR_TO_AGBNO(mp,d) xfs_daddr_to_agbno(mp,d)
+#else
+
+static inline xfs_agblock_t XFS_DADDR_TO_AGBNO(xfs_mount_t *mp, xfs_daddr_t d)
+{
+ d = XFS_BB_TO_FSBT(mp, d);
+ return (xfs_agblock_t) do_div(d, mp->m_sb.sb_agblocks);
+}
+
+#endif
+
+/*
+ * This structure is for use by the xfs_mod_incore_sb_batch() routine.
+ */
+typedef struct xfs_mod_sb {
+ xfs_sb_field_t msb_field; /* Field to modify, see below */
+ int msb_delta; /* Change to make to specified field */
+} xfs_mod_sb_t;
+
+#define XFS_MOUNT_ILOCK(mp) mutex_lock(&((mp)->m_ilock), PINOD)
+#define XFS_MOUNT_IUNLOCK(mp) mutex_unlock(&((mp)->m_ilock))
+#define XFS_SB_LOCK(mp) mutex_spinlock(&(mp)->m_sb_lock)
+#define XFS_SB_UNLOCK(mp,s) mutex_spinunlock(&(mp)->m_sb_lock,(s))
+
+extern xfs_mount_t *xfs_mount_init(void);
+extern void xfs_mod_sb(xfs_trans_t *, __int64_t);
+extern void xfs_mount_free(xfs_mount_t *mp, int remove_bhv);
+extern int xfs_mountfs(struct vfs *, xfs_mount_t *mp, int);
+
+extern int xfs_unmountfs(xfs_mount_t *, struct cred *);
+extern void xfs_unmountfs_wait(xfs_mount_t *);
+extern void xfs_unmountfs_close(xfs_mount_t *, struct cred *);
+extern int xfs_unmountfs_writesb(xfs_mount_t *);
+extern int xfs_unmount_flush(xfs_mount_t *, int);
+extern int xfs_mod_incore_sb(xfs_mount_t *, xfs_sb_field_t, int, int);
+extern int xfs_mod_incore_sb_batch(xfs_mount_t *, xfs_mod_sb_t *,
+ uint, int);
+extern struct xfs_buf *xfs_getsb(xfs_mount_t *, int);
+extern int xfs_readsb(xfs_mount_t *mp);
+extern void xfs_freesb(xfs_mount_t *);
+extern void xfs_do_force_shutdown(bhv_desc_t *, int, char *, int);
+extern int xfs_syncsub(xfs_mount_t *, int, int, int *);
+extern xfs_agnumber_t xfs_initialize_perag(xfs_mount_t *, xfs_agnumber_t);
+extern void xfs_xlatesb(void *, struct xfs_sb *, int, xfs_arch_t,
+ __int64_t);
+
+extern struct vfsops xfs_vfsops;
+extern struct vnodeops xfs_vnodeops;
+
+extern struct xfs_dmops xfs_dmcore_stub;
+extern struct xfs_qmops xfs_qmcore_stub;
+extern struct xfs_ioops xfs_iocore_xfs;
+
+extern int xfs_init(void);
+extern void xfs_cleanup(void);
+
+#endif /* __KERNEL__ */
+
+#endif /* __XFS_MOUNT_H__ */
diff --git a/fs/xfs/xfs_qmops.c b/fs/xfs/xfs_qmops.c
new file mode 100644
index 00000000000000..4f40c92863d51f
--- /dev/null
+++ b/fs/xfs/xfs_qmops.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#include "xfs.h"
+
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+
+
+STATIC struct xfs_dquot *
+xfs_dqvopchown_default(
+ struct xfs_trans *tp,
+ struct xfs_inode *ip,
+ struct xfs_dquot **dqp,
+ struct xfs_dquot *dq)
+{
+ return NULL;
+}
+
+xfs_qmops_t xfs_qmcore_stub = {
+ .xfs_qminit = (xfs_qminit_t) fs_noerr,
+ .xfs_qmdone = (xfs_qmdone_t) fs_noerr,
+ .xfs_qmmount = (xfs_qmmount_t) fs_noerr,
+ .xfs_qmunmount = (xfs_qmunmount_t) fs_noerr,
+ .xfs_dqrele = (xfs_dqrele_t) fs_noerr,
+ .xfs_dqattach = (xfs_dqattach_t) fs_noerr,
+ .xfs_dqdetach = (xfs_dqdetach_t) fs_noerr,
+ .xfs_dqpurgeall = (xfs_dqpurgeall_t) fs_noerr,
+ .xfs_dqvopalloc = (xfs_dqvopalloc_t) fs_noerr,
+ .xfs_dqvopcreate = (xfs_dqvopcreate_t) fs_noerr,
+ .xfs_dqvoprename = (xfs_dqvoprename_t) fs_noerr,
+ .xfs_dqvopchown = xfs_dqvopchown_default,
+ .xfs_dqvopchownresv = (xfs_dqvopchownresv_t) fs_noerr,
+};
diff --git a/fs/xfs/xfs_quota.h b/fs/xfs/xfs_quota.h
new file mode 100644
index 00000000000000..703ec4efcb4115
--- /dev/null
+++ b/fs/xfs/xfs_quota.h
@@ -0,0 +1,356 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_QUOTA_H__
+#define __XFS_QUOTA_H__
+
+/*
+ * The ondisk form of a dquot structure.
+ */
+#define XFS_DQUOT_MAGIC 0x4451 /* 'DQ' */
+#define XFS_DQUOT_VERSION (u_int8_t)0x01 /* latest version number */
+
+/*
+ * uid_t and gid_t are hard-coded to 32 bits in the inode.
+ * Hence, an 'id' in a dquot is 32 bits..
+ */
+typedef __int32_t xfs_dqid_t;
+
+/*
+ * Eventhough users may not have quota limits occupying all 64-bits,
+ * they may need 64-bit accounting. Hence, 64-bit quota-counters,
+ * and quota-limits. This is a waste in the common case, but hey ...
+ */
+typedef __uint64_t xfs_qcnt_t;
+typedef __uint16_t xfs_qwarncnt_t;
+
+/*
+ * This is the main portion of the on-disk representation of quota
+ * information for a user. This is the q_core of the xfs_dquot_t that
+ * is kept in kernel memory. We pad this with some more expansion room
+ * to construct the on disk structure.
+ */
+typedef struct xfs_disk_dquot {
+/*16*/ u_int16_t d_magic; /* dquot magic = XFS_DQUOT_MAGIC */
+/*8 */ u_int8_t d_version; /* dquot version */
+/*8 */ u_int8_t d_flags; /* XFS_DQ_USER/PROJ/GROUP */
+/*32*/ xfs_dqid_t d_id; /* user,project,group id */
+/*64*/ xfs_qcnt_t d_blk_hardlimit;/* absolute limit on disk blks */
+/*64*/ xfs_qcnt_t d_blk_softlimit;/* preferred limit on disk blks */
+/*64*/ xfs_qcnt_t d_ino_hardlimit;/* maximum # allocated inodes */
+/*64*/ xfs_qcnt_t d_ino_softlimit;/* preferred inode limit */
+/*64*/ xfs_qcnt_t d_bcount; /* disk blocks owned by the user */
+/*64*/ xfs_qcnt_t d_icount; /* inodes owned by the user */
+/*32*/ __int32_t d_itimer; /* zero if within inode limits if not,
+ this is when we refuse service */
+/*32*/ __int32_t d_btimer; /* similar to above; for disk blocks */
+/*16*/ xfs_qwarncnt_t d_iwarns; /* warnings issued wrt num inodes */
+/*16*/ xfs_qwarncnt_t d_bwarns; /* warnings issued wrt disk blocks */
+/*32*/ __int32_t d_pad0; /* 64 bit align */
+/*64*/ xfs_qcnt_t d_rtb_hardlimit;/* absolute limit on realtime blks */
+/*64*/ xfs_qcnt_t d_rtb_softlimit;/* preferred limit on RT disk blks */
+/*64*/ xfs_qcnt_t d_rtbcount; /* realtime blocks owned */
+/*32*/ __int32_t d_rtbtimer; /* similar to above; for RT disk blocks */
+/*16*/ xfs_qwarncnt_t d_rtbwarns; /* warnings issued wrt RT disk blocks */
+/*16*/ __uint16_t d_pad;
+} xfs_disk_dquot_t;
+
+/*
+ * This is what goes on disk. This is separated from the xfs_disk_dquot because
+ * carrying the unnecessary padding would be a waste of memory.
+ */
+typedef struct xfs_dqblk {
+ xfs_disk_dquot_t dd_diskdq; /* portion that lives incore as well */
+ char dd_fill[32]; /* filling for posterity */
+} xfs_dqblk_t;
+
+/*
+ * flags for q_flags field in the dquot.
+ */
+#define XFS_DQ_USER 0x0001 /* a user quota */
+/* #define XFS_DQ_PROJ 0x0002 -- project quota (IRIX) */
+#define XFS_DQ_GROUP 0x0004 /* a group quota */
+#define XFS_DQ_FLOCKED 0x0008 /* flush lock taken */
+#define XFS_DQ_DIRTY 0x0010 /* dquot is dirty */
+#define XFS_DQ_WANT 0x0020 /* for lookup/reclaim race */
+#define XFS_DQ_INACTIVE 0x0040 /* dq off mplist & hashlist */
+#define XFS_DQ_MARKER 0x0080 /* sentinel */
+
+/*
+ * In the worst case, when both user and group quotas are on,
+ * we can have a max of three dquots changing in a single transaction.
+ */
+#define XFS_DQUOT_LOGRES(mp) (sizeof(xfs_disk_dquot_t) * 3)
+
+
+/*
+ * These are the structures used to lay out dquots and quotaoff
+ * records on the log. Quite similar to those of inodes.
+ */
+
+/*
+ * log format struct for dquots.
+ * The first two fields must be the type and size fitting into
+ * 32 bits : log_recovery code assumes that.
+ */
+typedef struct xfs_dq_logformat {
+ __uint16_t qlf_type; /* dquot log item type */
+ __uint16_t qlf_size; /* size of this item */
+ xfs_dqid_t qlf_id; /* usr/grp id number : 32 bits */
+ __int64_t qlf_blkno; /* blkno of dquot buffer */
+ __int32_t qlf_len; /* len of dquot buffer */
+ __uint32_t qlf_boffset; /* off of dquot in buffer */
+} xfs_dq_logformat_t;
+
+/*
+ * log format struct for QUOTAOFF records.
+ * The first two fields must be the type and size fitting into
+ * 32 bits : log_recovery code assumes that.
+ * We write two LI_QUOTAOFF logitems per quotaoff, the last one keeps a pointer
+ * to the first and ensures that the first logitem is taken out of the AIL
+ * only when the last one is securely committed.
+ */
+typedef struct xfs_qoff_logformat {
+ unsigned short qf_type; /* quotaoff log item type */
+ unsigned short qf_size; /* size of this item */
+ unsigned int qf_flags; /* USR and/or GRP */
+ char qf_pad[12]; /* padding for future */
+} xfs_qoff_logformat_t;
+
+
+/*
+ * Disk quotas status in m_qflags, and also sb_qflags. 16 bits.
+ */
+#define XFS_UQUOTA_ACCT 0x0001 /* user quota accounting ON */
+#define XFS_UQUOTA_ENFD 0x0002 /* user quota limits enforced */
+#define XFS_UQUOTA_CHKD 0x0004 /* quotacheck run on usr quotas */
+#define XFS_PQUOTA_ACCT 0x0008 /* (IRIX) project quota accounting ON */
+#define XFS_GQUOTA_ENFD 0x0010 /* group quota limits enforced */
+#define XFS_GQUOTA_CHKD 0x0020 /* quotacheck run on grp quotas */
+#define XFS_GQUOTA_ACCT 0x0040 /* group quota accounting ON */
+
+/*
+ * Incore only flags for quotaoff - these bits get cleared when quota(s)
+ * are in the process of getting turned off. These flags are in m_qflags but
+ * never in sb_qflags.
+ */
+#define XFS_UQUOTA_ACTIVE 0x0080 /* uquotas are being turned off */
+#define XFS_GQUOTA_ACTIVE 0x0100 /* gquotas are being turned off */
+
+/*
+ * Checking XFS_IS_*QUOTA_ON() while holding any inode lock guarantees
+ * quota will be not be switched off as long as that inode lock is held.
+ */
+#define XFS_IS_QUOTA_ON(mp) ((mp)->m_qflags & (XFS_UQUOTA_ACTIVE | \
+ XFS_GQUOTA_ACTIVE))
+#define XFS_IS_UQUOTA_ON(mp) ((mp)->m_qflags & XFS_UQUOTA_ACTIVE)
+#define XFS_IS_GQUOTA_ON(mp) ((mp)->m_qflags & XFS_GQUOTA_ACTIVE)
+
+/*
+ * Flags to tell various functions what to do. Not all of these are meaningful
+ * to a single function. None of these XFS_QMOPT_* flags are meant to have
+ * persistent values (ie. their values can and will change between versions)
+ */
+#define XFS_QMOPT_DQLOCK 0x0000001 /* dqlock */
+#define XFS_QMOPT_DQALLOC 0x0000002 /* alloc dquot ondisk if needed */
+#define XFS_QMOPT_UQUOTA 0x0000004 /* user dquot requested */
+#define XFS_QMOPT_GQUOTA 0x0000008 /* group dquot requested */
+#define XFS_QMOPT_FORCE_RES 0x0000010 /* ignore quota limits */
+#define XFS_QMOPT_DQSUSER 0x0000020 /* don't cache super users dquot */
+#define XFS_QMOPT_SBVERSION 0x0000040 /* change superblock version num */
+#define XFS_QMOPT_QUOTAOFF 0x0000080 /* quotas are being turned off */
+#define XFS_QMOPT_UMOUNTING 0x0000100 /* filesys is being unmounted */
+#define XFS_QMOPT_DOLOG 0x0000200 /* log buf changes (in quotacheck) */
+#define XFS_QMOPT_DOWARN 0x0000400 /* increase warning cnt if necessary */
+#define XFS_QMOPT_ILOCKED 0x0000800 /* inode is already locked (excl) */
+#define XFS_QMOPT_DQREPAIR 0x0001000 /* repair dquot, if damaged. */
+
+/*
+ * flags to xfs_trans_mod_dquot to indicate which field needs to be
+ * modified.
+ */
+#define XFS_QMOPT_RES_REGBLKS 0x0010000
+#define XFS_QMOPT_RES_RTBLKS 0x0020000
+#define XFS_QMOPT_BCOUNT 0x0040000
+#define XFS_QMOPT_ICOUNT 0x0080000
+#define XFS_QMOPT_RTBCOUNT 0x0100000
+#define XFS_QMOPT_DELBCOUNT 0x0200000
+#define XFS_QMOPT_DELRTBCOUNT 0x0400000
+#define XFS_QMOPT_RES_INOS 0x0800000
+
+/*
+ * flags for dqflush and dqflush_all.
+ */
+#define XFS_QMOPT_SYNC 0x1000000
+#define XFS_QMOPT_ASYNC 0x2000000
+#define XFS_QMOPT_DELWRI 0x4000000
+
+/*
+ * flags for dqalloc.
+ */
+#define XFS_QMOPT_INHERIT 0x8000000
+
+/*
+ * flags to xfs_trans_mod_dquot.
+ */
+#define XFS_TRANS_DQ_RES_BLKS XFS_QMOPT_RES_REGBLKS
+#define XFS_TRANS_DQ_RES_RTBLKS XFS_QMOPT_RES_RTBLKS
+#define XFS_TRANS_DQ_RES_INOS XFS_QMOPT_RES_INOS
+#define XFS_TRANS_DQ_BCOUNT XFS_QMOPT_BCOUNT
+#define XFS_TRANS_DQ_DELBCOUNT XFS_QMOPT_DELBCOUNT
+#define XFS_TRANS_DQ_ICOUNT XFS_QMOPT_ICOUNT
+#define XFS_TRANS_DQ_RTBCOUNT XFS_QMOPT_RTBCOUNT
+#define XFS_TRANS_DQ_DELRTBCOUNT XFS_QMOPT_DELRTBCOUNT
+
+
+#define XFS_QMOPT_QUOTALL (XFS_QMOPT_UQUOTA|XFS_QMOPT_GQUOTA)
+#define XFS_QMOPT_RESBLK_MASK (XFS_QMOPT_RES_REGBLKS | XFS_QMOPT_RES_RTBLKS)
+
+#ifdef __KERNEL__
+/*
+ * This check is done typically without holding the inode lock;
+ * that may seem racey, but it is harmless in the context that it is used.
+ * The inode cannot go inactive as long a reference is kept, and
+ * therefore if dquot(s) were attached, they'll stay consistent.
+ * If, for example, the ownership of the inode changes while
+ * we didn't have the inode locked, the appropriate dquot(s) will be
+ * attached atomically.
+ */
+#define XFS_NOT_DQATTACHED(mp, ip) ((XFS_IS_UQUOTA_ON(mp) &&\
+ (ip)->i_udquot == NULL) || \
+ (XFS_IS_GQUOTA_ON(mp) && \
+ (ip)->i_gdquot == NULL))
+
+#define XFS_QM_NEED_QUOTACHECK(mp) ((XFS_IS_UQUOTA_ON(mp) && \
+ (mp->m_sb.sb_qflags & \
+ XFS_UQUOTA_CHKD) == 0) || \
+ (XFS_IS_GQUOTA_ON(mp) && \
+ (mp->m_sb.sb_qflags & \
+ XFS_GQUOTA_CHKD) == 0))
+
+#define XFS_MOUNT_QUOTA_ALL (XFS_UQUOTA_ACCT|XFS_UQUOTA_ENFD|\
+ XFS_UQUOTA_CHKD|XFS_GQUOTA_ACCT|\
+ XFS_GQUOTA_ENFD|XFS_GQUOTA_CHKD)
+#define XFS_MOUNT_QUOTA_MASK (XFS_MOUNT_QUOTA_ALL | XFS_UQUOTA_ACTIVE | \
+ XFS_GQUOTA_ACTIVE)
+
+
+/*
+ * The structure kept inside the xfs_trans_t keep track of dquot changes
+ * within a transaction and apply them later.
+ */
+typedef struct xfs_dqtrx {
+ struct xfs_dquot *qt_dquot; /* the dquot this refers to */
+ ulong qt_blk_res; /* blks reserved on a dquot */
+ ulong qt_blk_res_used; /* blks used from the reservation */
+ ulong qt_ino_res; /* inode reserved on a dquot */
+ ulong qt_ino_res_used; /* inodes used from the reservation */
+ long qt_bcount_delta; /* dquot blk count changes */
+ long qt_delbcnt_delta; /* delayed dquot blk count changes */
+ long qt_icount_delta; /* dquot inode count changes */
+ ulong qt_rtblk_res; /* # blks reserved on a dquot */
+ ulong qt_rtblk_res_used;/* # blks used from reservation */
+ long qt_rtbcount_delta;/* dquot realtime blk changes */
+ long qt_delrtb_delta; /* delayed RT blk count changes */
+} xfs_dqtrx_t;
+
+/*
+ * Dquot transaction functions, used if quota is enabled.
+ */
+typedef void (*qo_dup_dqinfo_t)(struct xfs_trans *, struct xfs_trans *);
+typedef void (*qo_mod_dquot_byino_t)(struct xfs_trans *,
+ struct xfs_inode *, uint, long);
+typedef void (*qo_free_dqinfo_t)(struct xfs_trans *);
+typedef void (*qo_apply_dquot_deltas_t)(struct xfs_trans *);
+typedef void (*qo_unreserve_and_mod_dquots_t)(struct xfs_trans *);
+typedef int (*qo_reserve_quota_nblks_t)(
+ struct xfs_trans *, struct xfs_mount *,
+ struct xfs_inode *, long, long, uint);
+typedef int (*qo_reserve_quota_bydquots_t)(
+ struct xfs_trans *, struct xfs_mount *,
+ struct xfs_dquot *, struct xfs_dquot *,
+ long, long, uint);
+typedef struct xfs_dqtrxops {
+ qo_dup_dqinfo_t qo_dup_dqinfo;
+ qo_free_dqinfo_t qo_free_dqinfo;
+ qo_mod_dquot_byino_t qo_mod_dquot_byino;
+ qo_apply_dquot_deltas_t qo_apply_dquot_deltas;
+ qo_reserve_quota_nblks_t qo_reserve_quota_nblks;
+ qo_reserve_quota_bydquots_t qo_reserve_quota_bydquots;
+ qo_unreserve_and_mod_dquots_t qo_unreserve_and_mod_dquots;
+} xfs_dqtrxops_t;
+
+#define XFS_DQTRXOP(mp, tp, op, args...) \
+ ((mp)->m_qm_ops.xfs_dqtrxops ? \
+ ((mp)->m_qm_ops.xfs_dqtrxops->op)(tp, ## args) : 0)
+
+#define XFS_DQTRXOP_VOID(mp, tp, op, args...) \
+ ((mp)->m_qm_ops.xfs_dqtrxops ? \
+ ((mp)->m_qm_ops.xfs_dqtrxops->op)(tp, ## args) : (void)0)
+
+#define XFS_TRANS_DUP_DQINFO(mp, otp, ntp) \
+ XFS_DQTRXOP_VOID(mp, otp, qo_dup_dqinfo, ntp)
+#define XFS_TRANS_FREE_DQINFO(mp, tp) \
+ XFS_DQTRXOP_VOID(mp, tp, qo_free_dqinfo)
+#define XFS_TRANS_MOD_DQUOT_BYINO(mp, tp, ip, field, delta) \
+ XFS_DQTRXOP_VOID(mp, tp, qo_mod_dquot_byino, ip, field, delta)
+#define XFS_TRANS_APPLY_DQUOT_DELTAS(mp, tp) \
+ XFS_DQTRXOP_VOID(mp, tp, qo_apply_dquot_deltas)
+#define XFS_TRANS_RESERVE_QUOTA_NBLKS(mp, tp, ip, nblks, ninos, fl) \
+ XFS_DQTRXOP(mp, tp, qo_reserve_quota_nblks, mp, ip, nblks, ninos, fl)
+#define XFS_TRANS_RESERVE_QUOTA_BYDQUOTS(mp, tp, ud, gd, nb, ni, fl) \
+ XFS_DQTRXOP(mp, tp, qo_reserve_quota_bydquots, mp, ud, gd, nb, ni, fl)
+#define XFS_TRANS_UNRESERVE_AND_MOD_DQUOTS(mp, tp) \
+ XFS_DQTRXOP_VOID(mp, tp, qo_unreserve_and_mod_dquots)
+
+#define XFS_TRANS_RESERVE_BLKQUOTA(mp, tp, ip, nblks) \
+ XFS_TRANS_RESERVE_QUOTA_NBLKS(mp, tp, ip, nblks, 0, \
+ XFS_QMOPT_RES_REGBLKS)
+#define XFS_TRANS_RESERVE_BLKQUOTA_FORCE(mp, tp, ip, nblks) \
+ XFS_TRANS_RESERVE_QUOTA_NBLKS(mp, tp, ip, nblks, 0, \
+ XFS_QMOPT_RES_REGBLKS | XFS_QMOPT_FORCE_RES)
+#define XFS_TRANS_UNRESERVE_BLKQUOTA(mp, tp, ip, nblks) \
+ XFS_TRANS_RESERVE_QUOTA_NBLKS(mp, tp, ip, -(nblks), 0, \
+ XFS_QMOPT_RES_REGBLKS)
+#define XFS_TRANS_RESERVE_QUOTA(mp, tp, ud, gd, nb, ni, f) \
+ XFS_TRANS_RESERVE_QUOTA_BYDQUOTS(mp, tp, ud, gd, nb, ni, \
+ f | XFS_QMOPT_RES_REGBLKS)
+#define XFS_TRANS_UNRESERVE_QUOTA(mp, tp, ud, gd, nb, ni, f) \
+ XFS_TRANS_RESERVE_QUOTA_BYDQUOTS(mp, tp, ud, gd, -(nb), -(ni), \
+ f | XFS_QMOPT_RES_REGBLKS)
+
+extern int xfs_qm_dqcheck(xfs_disk_dquot_t *, xfs_dqid_t, uint, uint, char *);
+
+extern struct bhv_vfsops xfs_qmops;
+
+#endif /* __KERNEL__ */
+
+#endif /* __XFS_QUOTA_H__ */
diff --git a/fs/xfs/xfs_refcache.c b/fs/xfs/xfs_refcache.c
new file mode 100644
index 00000000000000..e68febadaf2da6
--- /dev/null
+++ b/fs/xfs/xfs_refcache.c
@@ -0,0 +1,439 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_itable.h"
+#include "xfs_btree.h"
+#include "xfs_alloc.h"
+#include "xfs_ialloc.h"
+#include "xfs_attr.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode_item.h"
+#include "xfs_inode.h"
+#include "xfs_bmap.h"
+#include "xfs_error.h"
+#include "xfs_buf_item.h"
+#include "xfs_refcache.h"
+
+STATIC spinlock_t xfs_refcache_lock = SPIN_LOCK_UNLOCKED;
+STATIC xfs_inode_t **xfs_refcache;
+STATIC int xfs_refcache_index;
+STATIC int xfs_refcache_busy;
+STATIC int xfs_refcache_count;
+
+/*
+ * Insert the given inode into the reference cache.
+ */
+void
+xfs_refcache_insert(
+ xfs_inode_t *ip)
+{
+ vnode_t *vp;
+ xfs_inode_t *release_ip;
+ xfs_inode_t **refcache;
+
+ ASSERT(ismrlocked(&(ip->i_iolock), MR_UPDATE));
+
+ /*
+ * If an unmount is busy blowing entries out of the cache,
+ * then don't bother.
+ */
+ if (xfs_refcache_busy) {
+ return;
+ }
+
+ /*
+ * If we tuned the refcache down to zero, don't do anything.
+ */
+ if (!xfs_refcache_size) {
+ return;
+ }
+
+ /*
+ * The inode is already in the refcache, so don't bother
+ * with it.
+ */
+ if (ip->i_refcache != NULL) {
+ return;
+ }
+
+ vp = XFS_ITOV(ip);
+ /* ASSERT(vp->v_count > 0); */
+ VN_HOLD(vp);
+
+ /*
+ * We allocate the reference cache on use so that we don't
+ * waste the memory on systems not being used as NFS servers.
+ */
+ if (xfs_refcache == NULL) {
+ refcache = (xfs_inode_t **)kmem_zalloc(XFS_REFCACHE_SIZE_MAX *
+ sizeof(xfs_inode_t *),
+ KM_SLEEP);
+ } else {
+ refcache = NULL;
+ }
+
+ spin_lock(&xfs_refcache_lock);
+
+ /*
+ * If we allocated memory for the refcache above and it still
+ * needs it, then use the memory we allocated. Otherwise we'll
+ * free the memory below.
+ */
+ if (refcache != NULL) {
+ if (xfs_refcache == NULL) {
+ xfs_refcache = refcache;
+ refcache = NULL;
+ }
+ }
+
+ /*
+ * If an unmount is busy clearing out the cache, don't add new
+ * entries to it.
+ */
+ if (xfs_refcache_busy) {
+ spin_unlock(&xfs_refcache_lock);
+ VN_RELE(vp);
+ /*
+ * If we allocated memory for the refcache above but someone
+ * else beat us to using it, then free the memory now.
+ */
+ if (refcache != NULL) {
+ kmem_free(refcache,
+ XFS_REFCACHE_SIZE_MAX * sizeof(xfs_inode_t *));
+ }
+ return;
+ }
+ release_ip = xfs_refcache[xfs_refcache_index];
+ if (release_ip != NULL) {
+ release_ip->i_refcache = NULL;
+ xfs_refcache_count--;
+ ASSERT(xfs_refcache_count >= 0);
+ }
+ xfs_refcache[xfs_refcache_index] = ip;
+ ASSERT(ip->i_refcache == NULL);
+ ip->i_refcache = &(xfs_refcache[xfs_refcache_index]);
+ xfs_refcache_count++;
+ ASSERT(xfs_refcache_count <= xfs_refcache_size);
+ xfs_refcache_index++;
+ if (xfs_refcache_index == xfs_refcache_size) {
+ xfs_refcache_index = 0;
+ }
+ spin_unlock(&xfs_refcache_lock);
+
+ /*
+ * Save the pointer to the inode to be released so that we can
+ * VN_RELE it once we've dropped our inode locks in xfs_rwunlock().
+ * The pointer may be NULL, but that's OK.
+ */
+ ip->i_release = release_ip;
+
+ /*
+ * If we allocated memory for the refcache above but someone
+ * else beat us to using it, then free the memory now.
+ */
+ if (refcache != NULL) {
+ kmem_free(refcache,
+ XFS_REFCACHE_SIZE_MAX * sizeof(xfs_inode_t *));
+ }
+}
+
+
+/*
+ * If the given inode is in the reference cache, purge its entry and
+ * release the reference on the vnode.
+ */
+void
+xfs_refcache_purge_ip(
+ xfs_inode_t *ip)
+{
+ vnode_t *vp;
+ int error;
+
+ /*
+ * If we're not pointing to our entry in the cache, then
+ * we must not be in the cache.
+ */
+ if (ip->i_refcache == NULL) {
+ return;
+ }
+
+ spin_lock(&xfs_refcache_lock);
+ if (ip->i_refcache == NULL) {
+ spin_unlock(&xfs_refcache_lock);
+ return;
+ }
+
+ /*
+ * Clear both our pointer to the cache entry and its pointer
+ * back to us.
+ */
+ ASSERT(*(ip->i_refcache) == ip);
+ *(ip->i_refcache) = NULL;
+ ip->i_refcache = NULL;
+ xfs_refcache_count--;
+ ASSERT(xfs_refcache_count >= 0);
+ spin_unlock(&xfs_refcache_lock);
+
+ vp = XFS_ITOV(ip);
+ /* ASSERT(vp->v_count > 1); */
+ VOP_RELEASE(vp, error);
+ VN_RELE(vp);
+}
+
+
+/*
+ * This is called from the XFS unmount code to purge all entries for the
+ * given mount from the cache. It uses the refcache busy counter to
+ * make sure that new entries are not added to the cache as we purge them.
+ */
+void
+xfs_refcache_purge_mp(
+ xfs_mount_t *mp)
+{
+ vnode_t *vp;
+ int error, i;
+ xfs_inode_t *ip;
+
+ if (xfs_refcache == NULL) {
+ return;
+ }
+
+ spin_lock(&xfs_refcache_lock);
+ /*
+ * Bumping the busy counter keeps new entries from being added
+ * to the cache. We use a counter since multiple unmounts could
+ * be in here simultaneously.
+ */
+ xfs_refcache_busy++;
+
+ for (i = 0; i < xfs_refcache_size; i++) {
+ ip = xfs_refcache[i];
+ if ((ip != NULL) && (ip->i_mount == mp)) {
+ xfs_refcache[i] = NULL;
+ ip->i_refcache = NULL;
+ xfs_refcache_count--;
+ ASSERT(xfs_refcache_count >= 0);
+ spin_unlock(&xfs_refcache_lock);
+ vp = XFS_ITOV(ip);
+ VOP_RELEASE(vp, error);
+ VN_RELE(vp);
+ spin_lock(&xfs_refcache_lock);
+ }
+ }
+
+ xfs_refcache_busy--;
+ ASSERT(xfs_refcache_busy >= 0);
+ spin_unlock(&xfs_refcache_lock);
+}
+
+
+/*
+ * This is called from the XFS sync code to ensure that the refcache
+ * is emptied out over time. We purge a small number of entries with
+ * each call.
+ */
+void
+xfs_refcache_purge_some(xfs_mount_t *mp)
+{
+ int error, i;
+ xfs_inode_t *ip;
+ int iplist_index;
+ xfs_inode_t **iplist;
+
+ if ((xfs_refcache == NULL) || (xfs_refcache_count == 0)) {
+ return;
+ }
+
+ iplist_index = 0;
+ iplist = (xfs_inode_t **)kmem_zalloc(xfs_refcache_purge_count *
+ sizeof(xfs_inode_t *), KM_SLEEP);
+
+ spin_lock(&xfs_refcache_lock);
+
+ /*
+ * Store any inodes we find in the next several entries
+ * into the iplist array to be released after dropping
+ * the spinlock. We always start looking from the currently
+ * oldest place in the cache. We move the refcache index
+ * forward as we go so that we are sure to eventually clear
+ * out the entire cache when the system goes idle.
+ */
+ for (i = 0; i < xfs_refcache_purge_count; i++) {
+ ip = xfs_refcache[xfs_refcache_index];
+ if (ip != NULL) {
+ xfs_refcache[xfs_refcache_index] = NULL;
+ ip->i_refcache = NULL;
+ xfs_refcache_count--;
+ ASSERT(xfs_refcache_count >= 0);
+ iplist[iplist_index] = ip;
+ iplist_index++;
+ }
+ xfs_refcache_index++;
+ if (xfs_refcache_index == xfs_refcache_size) {
+ xfs_refcache_index = 0;
+ }
+ }
+
+ spin_unlock(&xfs_refcache_lock);
+
+ /*
+ * Now drop the inodes we collected.
+ */
+ for (i = 0; i < iplist_index; i++) {
+ VOP_RELEASE(XFS_ITOV(iplist[i]), error);
+ VN_RELE(XFS_ITOV(iplist[i]));
+ }
+
+ kmem_free(iplist, xfs_refcache_purge_count *
+ sizeof(xfs_inode_t *));
+}
+
+/*
+ * This is called when the refcache is dynamically resized
+ * via a sysctl.
+ *
+ * If the new size is smaller than the old size, purge all
+ * entries in slots greater than the new size, and move
+ * the index if necessary.
+ *
+ * If the refcache hasn't even been allocated yet, or the
+ * new size is larger than the old size, just set the value
+ * of xfs_refcache_size.
+ */
+
+void
+xfs_refcache_resize(int xfs_refcache_new_size)
+{
+ int i;
+ xfs_inode_t *ip;
+ int iplist_index = 0;
+ xfs_inode_t **iplist;
+ int error;
+
+ /*
+ * If the new size is smaller than the current size,
+ * purge entries to create smaller cache, and
+ * reposition index if necessary.
+ * Don't bother if no refcache yet.
+ */
+ if (xfs_refcache && (xfs_refcache_new_size < xfs_refcache_size)) {
+
+ iplist = (xfs_inode_t **)kmem_zalloc(XFS_REFCACHE_SIZE_MAX *
+ sizeof(xfs_inode_t *), KM_SLEEP);
+
+ spin_lock(&xfs_refcache_lock);
+
+ for (i = xfs_refcache_new_size; i < xfs_refcache_size; i++) {
+ ip = xfs_refcache[i];
+ if (ip != NULL) {
+ xfs_refcache[i] = NULL;
+ ip->i_refcache = NULL;
+ xfs_refcache_count--;
+ ASSERT(xfs_refcache_count >= 0);
+ iplist[iplist_index] = ip;
+ iplist_index++;
+ }
+ }
+
+ xfs_refcache_size = xfs_refcache_new_size;
+
+ /*
+ * Move index to beginning of cache if it's now past the end
+ */
+ if (xfs_refcache_index >= xfs_refcache_new_size)
+ xfs_refcache_index = 0;
+
+ spin_unlock(&xfs_refcache_lock);
+
+ /*
+ * Now drop the inodes we collected.
+ */
+ for (i = 0; i < iplist_index; i++) {
+ VOP_RELEASE(XFS_ITOV(iplist[i]), error);
+ VN_RELE(XFS_ITOV(iplist[i]));
+ }
+
+ kmem_free(iplist, XFS_REFCACHE_SIZE_MAX *
+ sizeof(xfs_inode_t *));
+ } else {
+ spin_lock(&xfs_refcache_lock);
+ xfs_refcache_size = xfs_refcache_new_size;
+ spin_unlock(&xfs_refcache_lock);
+ }
+}
+
+void
+xfs_refcache_iunlock(
+ xfs_inode_t *ip,
+ uint lock_flags)
+{
+ xfs_inode_t *release_ip;
+ int error;
+
+ release_ip = ip->i_release;
+ ip->i_release = NULL;
+
+ xfs_iunlock(ip, lock_flags);
+
+ if (release_ip != NULL) {
+ VOP_RELEASE(XFS_ITOV(release_ip), error);
+ VN_RELE(XFS_ITOV(release_ip));
+ }
+}
+
+void
+xfs_refcache_destroy(void)
+{
+ if (xfs_refcache) {
+ kmem_free(xfs_refcache,
+ XFS_REFCACHE_SIZE_MAX * sizeof(xfs_inode_t *));
+ xfs_refcache = NULL;
+ }
+}
diff --git a/fs/xfs/xfs_refcache.h b/fs/xfs/xfs_refcache.h
new file mode 100644
index 00000000000000..cd8ddfd35d6943
--- /dev/null
+++ b/fs/xfs/xfs_refcache.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_REFCACHE_H__
+#define __XFS_REFCACHE_H__
+
+#ifdef HAVE_REFCACHE
+/*
+ * Maximum size (in inodes) for the NFS reference cache
+ */
+#define XFS_REFCACHE_SIZE_MAX 512
+
+struct xfs_inode;
+struct xfs_mount;
+
+extern void xfs_refcache_insert(struct xfs_inode *);
+extern void xfs_refcache_purge_ip(struct xfs_inode *);
+extern void xfs_refcache_purge_mp(struct xfs_mount *);
+extern void xfs_refcache_purge_some(struct xfs_mount *);
+extern void xfs_refcache_resize(int);
+extern void xfs_refcache_destroy(void);
+
+extern void xfs_refcache_iunlock(struct xfs_inode *, uint);
+
+#else
+
+#define xfs_refcache_insert(ip) do { } while (0)
+#define xfs_refcache_purge_ip(ip) do { } while (0)
+#define xfs_refcache_purge_mp(mp) do { } while (0)
+#define xfs_refcache_purge_some(mp) do { } while (0)
+#define xfs_refcache_resize(size) do { } while (0)
+#define xfs_refcache_destroy() do { } while (0)
+
+#define xfs_refcache_iunlock(ip, flags) xfs_iunlock(ip, flags)
+
+#endif
+
+#endif /* __XFS_REFCACHE_H__ */
diff --git a/fs/xfs/xfs_rename.c b/fs/xfs/xfs_rename.c
new file mode 100644
index 00000000000000..876f6908212e6e
--- /dev/null
+++ b/fs/xfs/xfs_rename.c
@@ -0,0 +1,673 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode_item.h"
+#include "xfs_inode.h"
+#include "xfs_bmap.h"
+#include "xfs_error.h"
+#include "xfs_quota.h"
+#include "xfs_refcache.h"
+#include "xfs_utils.h"
+#include "xfs_trans_space.h"
+#include "xfs_da_btree.h"
+#include "xfs_dir_leaf.h"
+
+
+/*
+ * Given an array of up to 4 inode pointers, unlock the pointed to inodes.
+ * If there are fewer than 4 entries in the array, the empty entries will
+ * be at the end and will have NULL pointers in them.
+ */
+STATIC void
+xfs_rename_unlock4(
+ xfs_inode_t **i_tab,
+ uint lock_mode)
+{
+ int i;
+
+ xfs_iunlock(i_tab[0], lock_mode);
+ for (i = 1; i < 4; i++) {
+ if (i_tab[i] == NULL) {
+ break;
+ }
+ /*
+ * Watch out for duplicate entries in the table.
+ */
+ if (i_tab[i] != i_tab[i-1]) {
+ xfs_iunlock(i_tab[i], lock_mode);
+ }
+ }
+}
+
+#ifdef DEBUG
+int xfs_rename_skip, xfs_rename_nskip;
+#endif
+
+/*
+ * The following routine will acquire the locks required for a rename
+ * operation. The code understands the semantics of renames and will
+ * validate that name1 exists under dp1 & that name2 may or may not
+ * exist under dp2.
+ *
+ * We are renaming dp1/name1 to dp2/name2.
+ *
+ * Return ENOENT if dp1 does not exist, other lookup errors, or 0 for success.
+ */
+STATIC int
+xfs_lock_for_rename(
+ xfs_inode_t *dp1, /* old (source) directory inode */
+ xfs_inode_t *dp2, /* new (target) directory inode */
+ vname_t *vname1,/* old entry name */
+ vname_t *vname2,/* new entry name */
+ xfs_inode_t **ipp1, /* inode of old entry */
+ xfs_inode_t **ipp2, /* inode of new entry, if it
+ already exists, NULL otherwise. */
+ xfs_inode_t **i_tab,/* array of inode returned, sorted */
+ int *num_inodes) /* number of inodes in array */
+{
+ xfs_inode_t *ip1, *ip2, *temp;
+ xfs_ino_t inum1, inum2;
+ int error;
+ int i, j;
+ uint lock_mode;
+ int diff_dirs = (dp1 != dp2);
+
+ ip2 = NULL;
+
+ /*
+ * First, find out the current inums of the entries so that we
+ * can determine the initial locking order. We'll have to
+ * sanity check stuff after all the locks have been acquired
+ * to see if we still have the right inodes, directories, etc.
+ */
+ lock_mode = xfs_ilock_map_shared(dp1);
+ error = xfs_get_dir_entry(vname1, &ip1);
+ if (error) {
+ xfs_iunlock_map_shared(dp1, lock_mode);
+ return error;
+ }
+
+ inum1 = ip1->i_ino;
+
+ ASSERT(ip1);
+ ITRACE(ip1);
+
+ /*
+ * Unlock dp1 and lock dp2 if they are different.
+ */
+
+ if (diff_dirs) {
+ xfs_iunlock_map_shared(dp1, lock_mode);
+ lock_mode = xfs_ilock_map_shared(dp2);
+ }
+
+ error = xfs_dir_lookup_int(XFS_ITOBHV(dp2), lock_mode,
+ vname2, &inum2, &ip2);
+ if (error == ENOENT) { /* target does not need to exist. */
+ inum2 = 0;
+ } else if (error) {
+ /*
+ * If dp2 and dp1 are the same, the next line unlocks dp1.
+ * Got it?
+ */
+ xfs_iunlock_map_shared(dp2, lock_mode);
+ IRELE (ip1);
+ return error;
+ } else {
+ ITRACE(ip2);
+ }
+
+ /*
+ * i_tab contains a list of pointers to inodes. We initialize
+ * the table here & we'll sort it. We will then use it to
+ * order the acquisition of the inode locks.
+ *
+ * Note that the table may contain duplicates. e.g., dp1 == dp2.
+ */
+ i_tab[0] = dp1;
+ i_tab[1] = dp2;
+ i_tab[2] = ip1;
+ if (inum2 == 0) {
+ *num_inodes = 3;
+ i_tab[3] = NULL;
+ } else {
+ *num_inodes = 4;
+ i_tab[3] = ip2;
+ }
+
+ /*
+ * Sort the elements via bubble sort. (Remember, there are at
+ * most 4 elements to sort, so this is adequate.)
+ */
+ for (i=0; i < *num_inodes; i++) {
+ for (j=1; j < *num_inodes; j++) {
+ if (i_tab[j]->i_ino < i_tab[j-1]->i_ino) {
+ temp = i_tab[j];
+ i_tab[j] = i_tab[j-1];
+ i_tab[j-1] = temp;
+ }
+ }
+ }
+
+ /*
+ * We have dp2 locked. If it isn't first, unlock it.
+ * If it is first, tell xfs_lock_inodes so it can skip it
+ * when locking. if dp1 == dp2, xfs_lock_inodes will skip both
+ * since they are equal. xfs_lock_inodes needs all these inodes
+ * so that it can unlock and retry if there might be a dead-lock
+ * potential with the log.
+ */
+
+ if (i_tab[0] == dp2 && lock_mode == XFS_ILOCK_SHARED) {
+#ifdef DEBUG
+ xfs_rename_skip++;
+#endif
+ xfs_lock_inodes(i_tab, *num_inodes, 1, XFS_ILOCK_SHARED);
+ } else {
+#ifdef DEBUG
+ xfs_rename_nskip++;
+#endif
+ xfs_iunlock_map_shared(dp2, lock_mode);
+ xfs_lock_inodes(i_tab, *num_inodes, 0, XFS_ILOCK_SHARED);
+ }
+
+ /*
+ * Set the return value. Null out any unused entries in i_tab.
+ */
+ *ipp1 = *ipp2 = NULL;
+ for (i=0; i < *num_inodes; i++) {
+ if (i_tab[i]->i_ino == inum1) {
+ *ipp1 = i_tab[i];
+ }
+ if (i_tab[i]->i_ino == inum2) {
+ *ipp2 = i_tab[i];
+ }
+ }
+ for (;i < 4; i++) {
+ i_tab[i] = NULL;
+ }
+ return 0;
+}
+
+
+int rename_which_error_return = 0;
+
+/*
+ * xfs_rename
+ */
+int
+xfs_rename(
+ bhv_desc_t *src_dir_bdp,
+ vname_t *src_vname,
+ vnode_t *target_dir_vp,
+ vname_t *target_vname,
+ cred_t *credp)
+{
+ xfs_trans_t *tp;
+ xfs_inode_t *src_dp, *target_dp, *src_ip, *target_ip;
+ xfs_mount_t *mp;
+ int new_parent; /* moving to a new dir */
+ int src_is_directory; /* src_name is a directory */
+ int error;
+ xfs_bmap_free_t free_list;
+ xfs_fsblock_t first_block;
+ int cancel_flags;
+ int committed;
+ xfs_inode_t *inodes[4];
+ int target_ip_dropped = 0; /* dropped target_ip link? */
+ vnode_t *src_dir_vp;
+ bhv_desc_t *target_dir_bdp;
+ int spaceres;
+ int target_link_zero = 0;
+ int num_inodes;
+ char *src_name = VNAME(src_vname);
+ char *target_name = VNAME(target_vname);
+ int src_namelen = VNAMELEN(src_vname);
+ int target_namelen = VNAMELEN(target_vname);
+
+ src_dir_vp = BHV_TO_VNODE(src_dir_bdp);
+ vn_trace_entry(src_dir_vp, "xfs_rename", (inst_t *)__return_address);
+ vn_trace_entry(target_dir_vp, "xfs_rename", (inst_t *)__return_address);
+
+ /*
+ * Find the XFS behavior descriptor for the target directory
+ * vnode since it was not handed to us.
+ */
+ target_dir_bdp = vn_bhv_lookup_unlocked(VN_BHV_HEAD(target_dir_vp),
+ &xfs_vnodeops);
+ if (target_dir_bdp == NULL) {
+ return XFS_ERROR(EXDEV);
+ }
+
+ src_dp = XFS_BHVTOI(src_dir_bdp);
+ target_dp = XFS_BHVTOI(target_dir_bdp);
+ mp = src_dp->i_mount;
+
+ if (DM_EVENT_ENABLED(src_dir_vp->v_vfsp, src_dp, DM_EVENT_RENAME) ||
+ DM_EVENT_ENABLED(target_dir_vp->v_vfsp,
+ target_dp, DM_EVENT_RENAME)) {
+ error = XFS_SEND_NAMESP(mp, DM_EVENT_RENAME,
+ src_dir_vp, DM_RIGHT_NULL,
+ target_dir_vp, DM_RIGHT_NULL,
+ src_name, target_name,
+ 0, 0, 0);
+ if (error) {
+ return error;
+ }
+ }
+ /* Return through std_return after this point. */
+
+ /*
+ * Lock all the participating inodes. Depending upon whether
+ * the target_name exists in the target directory, and
+ * whether the target directory is the same as the source
+ * directory, we can lock from 2 to 4 inodes.
+ * xfs_lock_for_rename() will return ENOENT if src_name
+ * does not exist in the source directory.
+ */
+ tp = NULL;
+ error = xfs_lock_for_rename(src_dp, target_dp, src_vname,
+ target_vname, &src_ip, &target_ip, inodes,
+ &num_inodes);
+
+ if (error) {
+ rename_which_error_return = __LINE__;
+ /*
+ * We have nothing locked, no inode references, and
+ * no transaction, so just get out.
+ */
+ goto std_return;
+ }
+
+ ASSERT(src_ip != NULL);
+
+ if ((src_ip->i_d.di_mode & S_IFMT) == S_IFDIR) {
+ /*
+ * Check for link count overflow on target_dp
+ */
+ if (target_ip == NULL && (src_dp != target_dp) &&
+ target_dp->i_d.di_nlink >= XFS_MAXLINK) {
+ rename_which_error_return = __LINE__;
+ error = XFS_ERROR(EMLINK);
+ xfs_rename_unlock4(inodes, XFS_ILOCK_SHARED);
+ goto rele_return;
+ }
+ }
+
+ new_parent = (src_dp != target_dp);
+ src_is_directory = ((src_ip->i_d.di_mode & S_IFMT) == S_IFDIR);
+
+ /*
+ * Drop the locks on our inodes so that we can start the transaction.
+ */
+ xfs_rename_unlock4(inodes, XFS_ILOCK_SHARED);
+
+ XFS_BMAP_INIT(&free_list, &first_block);
+ tp = xfs_trans_alloc(mp, XFS_TRANS_RENAME);
+ cancel_flags = XFS_TRANS_RELEASE_LOG_RES;
+ spaceres = XFS_RENAME_SPACE_RES(mp, target_namelen);
+ error = xfs_trans_reserve(tp, spaceres, XFS_RENAME_LOG_RES(mp), 0,
+ XFS_TRANS_PERM_LOG_RES, XFS_RENAME_LOG_COUNT);
+ if (error == ENOSPC) {
+ spaceres = 0;
+ error = xfs_trans_reserve(tp, 0, XFS_RENAME_LOG_RES(mp), 0,
+ XFS_TRANS_PERM_LOG_RES, XFS_RENAME_LOG_COUNT);
+ }
+ if (error) {
+ rename_which_error_return = __LINE__;
+ xfs_trans_cancel(tp, 0);
+ goto rele_return;
+ }
+
+ /*
+ * Attach the dquots to the inodes
+ */
+ if ((error = XFS_QM_DQVOPRENAME(mp, inodes))) {
+ xfs_trans_cancel(tp, cancel_flags);
+ rename_which_error_return = __LINE__;
+ goto rele_return;
+ }
+
+ /*
+ * Reacquire the inode locks we dropped above.
+ */
+ xfs_lock_inodes(inodes, num_inodes, 0, XFS_ILOCK_EXCL);
+
+ /*
+ * Join all the inodes to the transaction. From this point on,
+ * we can rely on either trans_commit or trans_cancel to unlock
+ * them. Note that we need to add a vnode reference to the
+ * directories since trans_commit & trans_cancel will decrement
+ * them when they unlock the inodes. Also, we need to be careful
+ * not to add an inode to the transaction more than once.
+ */
+ VN_HOLD(src_dir_vp);
+ xfs_trans_ijoin(tp, src_dp, XFS_ILOCK_EXCL);
+ if (new_parent) {
+ VN_HOLD(target_dir_vp);
+ xfs_trans_ijoin(tp, target_dp, XFS_ILOCK_EXCL);
+ }
+ if ((src_ip != src_dp) && (src_ip != target_dp)) {
+ xfs_trans_ijoin(tp, src_ip, XFS_ILOCK_EXCL);
+ }
+ if ((target_ip != NULL) &&
+ (target_ip != src_ip) &&
+ (target_ip != src_dp) &&
+ (target_ip != target_dp)) {
+ xfs_trans_ijoin(tp, target_ip, XFS_ILOCK_EXCL);
+ }
+
+ /*
+ * Set up the target.
+ */
+ if (target_ip == NULL) {
+ /*
+ * If there's no space reservation, check the entry will
+ * fit before actually inserting it.
+ */
+ if (spaceres == 0 &&
+ (error = XFS_DIR_CANENTER(mp, tp, target_dp, target_name,
+ target_namelen))) {
+ rename_which_error_return = __LINE__;
+ goto error_return;
+ }
+ /*
+ * If target does not exist and the rename crosses
+ * directories, adjust the target directory link count
+ * to account for the ".." reference from the new entry.
+ */
+ error = XFS_DIR_CREATENAME(mp, tp, target_dp, target_name,
+ target_namelen, src_ip->i_ino,
+ &first_block, &free_list, spaceres);
+ if (error == ENOSPC) {
+ rename_which_error_return = __LINE__;
+ goto error_return;
+ }
+ if (error) {
+ rename_which_error_return = __LINE__;
+ goto abort_return;
+ }
+ xfs_ichgtime(target_dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
+
+ if (new_parent && src_is_directory) {
+ error = xfs_bumplink(tp, target_dp);
+ if (error) {
+ rename_which_error_return = __LINE__;
+ goto abort_return;
+ }
+ }
+ } else { /* target_ip != NULL */
+
+ /*
+ * If target exists and it's a directory, check that both
+ * target and source are directories and that target can be
+ * destroyed, or that neither is a directory.
+ */
+ if ((target_ip->i_d.di_mode & S_IFMT) == S_IFDIR) {
+ /*
+ * Make sure target dir is empty.
+ */
+ if (!(XFS_DIR_ISEMPTY(target_ip->i_mount, target_ip)) ||
+ (target_ip->i_d.di_nlink > 2)) {
+ error = XFS_ERROR(EEXIST);
+ rename_which_error_return = __LINE__;
+ goto error_return;
+ }
+ }
+
+ /*
+ * Link the source inode under the target name.
+ * If the source inode is a directory and we are moving
+ * it across directories, its ".." entry will be
+ * inconsistent until we replace that down below.
+ *
+ * In case there is already an entry with the same
+ * name at the destination directory, remove it first.
+ */
+ error = XFS_DIR_REPLACE(mp, tp, target_dp, target_name,
+ target_namelen, src_ip->i_ino, &first_block,
+ &free_list, spaceres);
+ if (error) {
+ rename_which_error_return = __LINE__;
+ goto abort_return;
+ }
+ xfs_ichgtime(target_dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
+
+ /*
+ * Decrement the link count on the target since the target
+ * dir no longer points to it.
+ */
+ error = xfs_droplink(tp, target_ip);
+ if (error) {
+ rename_which_error_return = __LINE__;
+ goto abort_return;
+ }
+ target_ip_dropped = 1;
+
+ if (src_is_directory) {
+ /*
+ * Drop the link from the old "." entry.
+ */
+ error = xfs_droplink(tp, target_ip);
+ if (error) {
+ rename_which_error_return = __LINE__;
+ goto abort_return;
+ }
+ }
+
+ /* Do this test while we still hold the locks */
+ target_link_zero = (target_ip)->i_d.di_nlink==0;
+
+ } /* target_ip != NULL */
+
+ /*
+ * Remove the source.
+ */
+ if (new_parent && src_is_directory) {
+
+ /*
+ * Rewrite the ".." entry to point to the new
+ * directory.
+ */
+ error = XFS_DIR_REPLACE(mp, tp, src_ip, "..", 2,
+ target_dp->i_ino, &first_block,
+ &free_list, spaceres);
+ ASSERT(error != EEXIST);
+ if (error) {
+ rename_which_error_return = __LINE__;
+ goto abort_return;
+ }
+ xfs_ichgtime(src_ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
+
+ } else {
+ /*
+ * We always want to hit the ctime on the source inode.
+ * We do it in the if clause above for the 'new_parent &&
+ * src_is_directory' case, and here we get all the other
+ * cases. This isn't strictly required by the standards
+ * since the source inode isn't really being changed,
+ * but old unix file systems did it and some incremental
+ * backup programs won't work without it.
+ */
+ xfs_ichgtime(src_ip, XFS_ICHGTIME_CHG);
+ }
+
+ /*
+ * Adjust the link count on src_dp. This is necessary when
+ * renaming a directory, either within one parent when
+ * the target existed, or across two parent directories.
+ */
+ if (src_is_directory && (new_parent || target_ip != NULL)) {
+
+ /*
+ * Decrement link count on src_directory since the
+ * entry that's moved no longer points to it.
+ */
+ error = xfs_droplink(tp, src_dp);
+ if (error) {
+ rename_which_error_return = __LINE__;
+ goto abort_return;
+ }
+ }
+
+ error = XFS_DIR_REMOVENAME(mp, tp, src_dp, src_name, src_namelen,
+ src_ip->i_ino, &first_block, &free_list, spaceres);
+ if (error) {
+ rename_which_error_return = __LINE__;
+ goto abort_return;
+ }
+ xfs_ichgtime(src_dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
+
+ /*
+ * Update the generation counts on all the directory inodes
+ * that we're modifying.
+ */
+ src_dp->i_gen++;
+ xfs_trans_log_inode(tp, src_dp, XFS_ILOG_CORE);
+
+ if (new_parent) {
+ target_dp->i_gen++;
+ xfs_trans_log_inode(tp, target_dp, XFS_ILOG_CORE);
+ }
+
+ /*
+ * If there was a target inode, take an extra reference on
+ * it here so that it doesn't go to xfs_inactive() from
+ * within the commit.
+ */
+ if (target_ip != NULL) {
+ IHOLD(target_ip);
+ }
+
+ /*
+ * If this is a synchronous mount, make sure that the
+ * rename transaction goes to disk before returning to
+ * the user.
+ */
+ if (mp->m_flags & XFS_MOUNT_WSYNC) {
+ xfs_trans_set_sync(tp);
+ }
+
+ /*
+ * Take refs. for vop_link_removed calls below. No need to worry
+ * about directory refs. because the caller holds them.
+ *
+ * Do holds before the xfs_bmap_finish since it might rele them down
+ * to zero.
+ */
+
+ if (target_ip_dropped)
+ IHOLD(target_ip);
+ IHOLD(src_ip);
+
+ error = xfs_bmap_finish(&tp, &free_list, first_block, &committed);
+ if (error) {
+ xfs_bmap_cancel(&free_list);
+ xfs_trans_cancel(tp, (XFS_TRANS_RELEASE_LOG_RES |
+ XFS_TRANS_ABORT));
+ if (target_ip != NULL) {
+ IRELE(target_ip);
+ }
+ if (target_ip_dropped) {
+ IRELE(target_ip);
+ }
+ IRELE(src_ip);
+ goto std_return;
+ }
+
+ /*
+ * trans_commit will unlock src_ip, target_ip & decrement
+ * the vnode references.
+ */
+ error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES, NULL);
+ if (target_ip != NULL) {
+ xfs_refcache_purge_ip(target_ip);
+ IRELE(target_ip);
+ }
+ /*
+ * Let interposed file systems know about removed links.
+ */
+ if (target_ip_dropped) {
+ VOP_LINK_REMOVED(XFS_ITOV(target_ip), target_dir_vp,
+ target_link_zero);
+ IRELE(target_ip);
+ }
+
+ FSC_NOTIFY_NAME_CHANGED(XFS_ITOV(src_ip));
+
+ IRELE(src_ip);
+
+ /* Fall through to std_return with error = 0 or errno from
+ * xfs_trans_commit */
+std_return:
+ if (DM_EVENT_ENABLED(src_dir_vp->v_vfsp, src_dp, DM_EVENT_POSTRENAME) ||
+ DM_EVENT_ENABLED(target_dir_vp->v_vfsp,
+ target_dp, DM_EVENT_POSTRENAME)) {
+ (void) XFS_SEND_NAMESP (mp, DM_EVENT_POSTRENAME,
+ src_dir_vp, DM_RIGHT_NULL,
+ target_dir_vp, DM_RIGHT_NULL,
+ src_name, target_name,
+ 0, error, 0);
+ }
+ return error;
+
+ abort_return:
+ cancel_flags |= XFS_TRANS_ABORT;
+ /* FALLTHROUGH */
+ error_return:
+ xfs_bmap_cancel(&free_list);
+ xfs_trans_cancel(tp, cancel_flags);
+ goto std_return;
+
+ rele_return:
+ IRELE(src_ip);
+ if (target_ip != NULL) {
+ IRELE(target_ip);
+ }
+ goto std_return;
+}
diff --git a/fs/xfs/xfs_rtalloc.c b/fs/xfs/xfs_rtalloc.c
new file mode 100644
index 00000000000000..2c37822d1012e6
--- /dev/null
+++ b/fs/xfs/xfs_rtalloc.c
@@ -0,0 +1,2469 @@
+/*
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+/*
+ * Free realtime space allocation for XFS.
+ */
+
+#include "xfs.h"
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode.h"
+#include "xfs_alloc.h"
+#include "xfs_bmap.h"
+#include "xfs_bit.h"
+#include "xfs_rtalloc.h"
+#include "xfs_fsops.h"
+#include "xfs_error.h"
+#include "xfs_rw.h"
+#include "xfs_inode_item.h"
+#include "xfs_trans_space.h"
+
+
+/*
+ * Prototypes for internal functions.
+ */
+
+
+STATIC int xfs_rtallocate_range(xfs_mount_t *, xfs_trans_t *, xfs_rtblock_t,
+ xfs_extlen_t, xfs_buf_t **, xfs_fsblock_t *);
+STATIC int xfs_rtany_summary(xfs_mount_t *, xfs_trans_t *, int, int,
+ xfs_rtblock_t, xfs_buf_t **, xfs_fsblock_t *, int *);
+STATIC int xfs_rtcheck_range(xfs_mount_t *, xfs_trans_t *, xfs_rtblock_t,
+ xfs_extlen_t, int, xfs_rtblock_t *, int *);
+STATIC int xfs_rtfind_back(xfs_mount_t *, xfs_trans_t *, xfs_rtblock_t,
+ xfs_rtblock_t, xfs_rtblock_t *);
+STATIC int xfs_rtfind_forw(xfs_mount_t *, xfs_trans_t *, xfs_rtblock_t,
+ xfs_rtblock_t, xfs_rtblock_t *);
+STATIC int xfs_rtget_summary( xfs_mount_t *, xfs_trans_t *, int,
+ xfs_rtblock_t, xfs_buf_t **, xfs_fsblock_t *, xfs_suminfo_t *);
+STATIC int xfs_rtmodify_range(xfs_mount_t *, xfs_trans_t *, xfs_rtblock_t,
+ xfs_extlen_t, int);
+STATIC int xfs_rtmodify_summary(xfs_mount_t *, xfs_trans_t *, int,
+ xfs_rtblock_t, int, xfs_buf_t **, xfs_fsblock_t *);
+
+/*
+ * Internal functions.
+ */
+
+/*
+ * xfs_lowbit32: get low bit set out of 32-bit argument, -1 if none set.
+ */
+STATIC int
+xfs_lowbit32(
+ __uint32_t v)
+{
+ if (v)
+ return ffs(v) - 1;
+ return -1;
+}
+
+/*
+ * Allocate space to the bitmap or summary file, and zero it, for growfs.
+ */
+STATIC int /* error */
+xfs_growfs_rt_alloc(
+ xfs_mount_t *mp, /* file system mount point */
+ xfs_extlen_t oblocks, /* old count of blocks */
+ xfs_extlen_t nblocks, /* new count of blocks */
+ xfs_ino_t ino) /* inode number (bitmap/summary) */
+{
+ xfs_fileoff_t bno; /* block number in file */
+ xfs_buf_t *bp; /* temporary buffer for zeroing */
+ int cancelflags; /* flags for xfs_trans_cancel */
+ int committed; /* transaction committed flag */
+ xfs_daddr_t d; /* disk block address */
+ int error; /* error return value */
+ xfs_fsblock_t firstblock; /* first block allocated in xaction */
+ xfs_bmap_free_t flist; /* list of freed blocks */
+ xfs_fsblock_t fsbno; /* filesystem block for bno */
+ xfs_inode_t *ip; /* pointer to incore inode */
+ xfs_bmbt_irec_t map; /* block map output */
+ int nmap; /* number of block maps */
+ int resblks; /* space reservation */
+ xfs_trans_t *tp; /* transaction pointer */
+
+ /*
+ * Allocate space to the file, as necessary.
+ */
+ while (oblocks < nblocks) {
+ tp = xfs_trans_alloc(mp, XFS_TRANS_GROWFSRT_ALLOC);
+ resblks = XFS_GROWFSRT_SPACE_RES(mp, nblocks - oblocks);
+ cancelflags = 0;
+ /*
+ * Reserve space & log for one extent added to the file.
+ */
+ if ((error = xfs_trans_reserve(tp, resblks,
+ XFS_GROWRTALLOC_LOG_RES(mp), 0,
+ XFS_TRANS_PERM_LOG_RES,
+ XFS_DEFAULT_PERM_LOG_COUNT)))
+ goto error_exit;
+ cancelflags = XFS_TRANS_RELEASE_LOG_RES;
+ /*
+ * Lock the inode.
+ */
+ if ((error = xfs_trans_iget(mp, tp, ino, 0, XFS_ILOCK_EXCL, &ip)))
+ goto error_exit;
+ XFS_BMAP_INIT(&flist, &firstblock);
+ /*
+ * Allocate blocks to the bitmap file.
+ */
+ nmap = 1;
+ cancelflags |= XFS_TRANS_ABORT;
+ error = xfs_bmapi(tp, ip, oblocks, nblocks - oblocks,
+ XFS_BMAPI_WRITE | XFS_BMAPI_METADATA, &firstblock,
+ resblks, &map, &nmap, &flist);
+ if (!error && nmap < 1)
+ error = XFS_ERROR(ENOSPC);
+ if (error)
+ goto error_exit;
+ /*
+ * Free any blocks freed up in the transaction, then commit.
+ */
+ error = xfs_bmap_finish(&tp, &flist, firstblock, &committed);
+ if (error)
+ goto error_exit;
+ xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES, NULL);
+ /*
+ * Now we need to clear the allocated blocks.
+ * Do this one block per transaction, to keep it simple.
+ */
+ cancelflags = 0;
+ for (bno = map.br_startoff, fsbno = map.br_startblock;
+ bno < map.br_startoff + map.br_blockcount;
+ bno++, fsbno++) {
+ tp = xfs_trans_alloc(mp, XFS_TRANS_GROWFSRT_ZERO);
+ /*
+ * Reserve log for one block zeroing.
+ */
+ if ((error = xfs_trans_reserve(tp, 0,
+ XFS_GROWRTZERO_LOG_RES(mp), 0, 0, 0)))
+ goto error_exit;
+ /*
+ * Lock the bitmap inode.
+ */
+ if ((error = xfs_trans_iget(mp, tp, ino, 0, XFS_ILOCK_EXCL,
+ &ip)))
+ goto error_exit;
+ /*
+ * Get a buffer for the block.
+ */
+ d = XFS_FSB_TO_DADDR(mp, fsbno);
+ bp = xfs_trans_get_buf(tp, mp->m_ddev_targp, d,
+ mp->m_bsize, 0);
+ if (bp == NULL) {
+ error = XFS_ERROR(EIO);
+ goto error_exit;
+ }
+ memset(XFS_BUF_PTR(bp), 0, mp->m_sb.sb_blocksize);
+ xfs_trans_log_buf(tp, bp, 0, mp->m_sb.sb_blocksize - 1);
+ /*
+ * Commit the transaction.
+ */
+ xfs_trans_commit(tp, 0, NULL);
+ }
+ /*
+ * Go on to the next extent, if any.
+ */
+ oblocks = map.br_startoff + map.br_blockcount;
+ }
+ return 0;
+error_exit:
+ xfs_trans_cancel(tp, cancelflags);
+ return error;
+}
+
+/*
+ * Attempt to allocate an extent minlen<=len<=maxlen starting from
+ * bitmap block bbno. If we don't get maxlen then use prod to trim
+ * the length, if given. Returns error; returns starting block in *rtblock.
+ * The lengths are all in rtextents.
+ */
+STATIC int /* error */
+xfs_rtallocate_extent_block(
+ xfs_mount_t *mp, /* file system mount point */
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_rtblock_t bbno, /* bitmap block number */
+ xfs_extlen_t minlen, /* minimum length to allocate */
+ xfs_extlen_t maxlen, /* maximum length to allocate */
+ xfs_extlen_t *len, /* out: actual length allocated */
+ xfs_rtblock_t *nextp, /* out: next block to try */
+ xfs_buf_t **rbpp, /* in/out: summary block buffer */
+ xfs_fsblock_t *rsb, /* in/out: summary block number */
+ xfs_extlen_t prod, /* extent product factor */
+ xfs_rtblock_t *rtblock) /* out: start block allocated */
+{
+ xfs_rtblock_t besti; /* best rtblock found so far */
+ xfs_rtblock_t bestlen; /* best length found so far */
+ xfs_rtblock_t end; /* last rtblock in chunk */
+ int error; /* error value */
+ xfs_rtblock_t i; /* current rtblock trying */
+ xfs_rtblock_t next; /* next rtblock to try */
+ int stat; /* status from internal calls */
+
+ /*
+ * Loop over all the extents starting in this bitmap block,
+ * looking for one that's long enough.
+ */
+ for (i = XFS_BLOCKTOBIT(mp, bbno), besti = -1, bestlen = 0,
+ end = XFS_BLOCKTOBIT(mp, bbno + 1) - 1;
+ i <= end;
+ i++) {
+ /*
+ * See if there's a free extent of maxlen starting at i.
+ * If it's not so then next will contain the first non-free.
+ */
+ error = xfs_rtcheck_range(mp, tp, i, maxlen, 1, &next, &stat);
+ if (error) {
+ return error;
+ }
+ if (stat) {
+ /*
+ * i for maxlen is all free, allocate and return that.
+ */
+ error = xfs_rtallocate_range(mp, tp, i, maxlen, rbpp,
+ rsb);
+ if (error) {
+ return error;
+ }
+ *len = maxlen;
+ *rtblock = i;
+ return 0;
+ }
+ /*
+ * In the case where we have a variable-sized allocation
+ * request, figure out how big this free piece is,
+ * and if it's big enough for the minimum, and the best
+ * so far, remember it.
+ */
+ if (minlen < maxlen) {
+ xfs_rtblock_t thislen; /* this extent size */
+
+ thislen = next - i;
+ if (thislen >= minlen && thislen > bestlen) {
+ besti = i;
+ bestlen = thislen;
+ }
+ }
+ /*
+ * If not done yet, find the start of the next free space.
+ */
+ if (next < end) {
+ error = xfs_rtfind_forw(mp, tp, next, end, &i);
+ if (error) {
+ return error;
+ }
+ } else
+ break;
+ }
+ /*
+ * Searched the whole thing & didn't find a maxlen free extent.
+ */
+ if (minlen < maxlen && besti != -1) {
+ xfs_extlen_t p; /* amount to trim length by */
+
+ /*
+ * If size should be a multiple of prod, make that so.
+ */
+ if (prod > 1 && (p = do_mod(bestlen, prod)))
+ bestlen -= p;
+ /*
+ * Allocate besti for bestlen & return that.
+ */
+ error = xfs_rtallocate_range(mp, tp, besti, bestlen, rbpp, rsb);
+ if (error) {
+ return error;
+ }
+ *len = bestlen;
+ *rtblock = besti;
+ return 0;
+ }
+ /*
+ * Allocation failed. Set *nextp to the next block to try.
+ */
+ *nextp = next;
+ *rtblock = NULLRTBLOCK;
+ return 0;
+}
+
+/*
+ * Allocate an extent of length minlen<=len<=maxlen, starting at block
+ * bno. If we don't get maxlen then use prod to trim the length, if given.
+ * Returns error; returns starting block in *rtblock.
+ * The lengths are all in rtextents.
+ */
+STATIC int /* error */
+xfs_rtallocate_extent_exact(
+ xfs_mount_t *mp, /* file system mount point */
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_rtblock_t bno, /* starting block number to allocate */
+ xfs_extlen_t minlen, /* minimum length to allocate */
+ xfs_extlen_t maxlen, /* maximum length to allocate */
+ xfs_extlen_t *len, /* out: actual length allocated */
+ xfs_buf_t **rbpp, /* in/out: summary block buffer */
+ xfs_fsblock_t *rsb, /* in/out: summary block number */
+ xfs_extlen_t prod, /* extent product factor */
+ xfs_rtblock_t *rtblock) /* out: start block allocated */
+{
+ int error; /* error value */
+ xfs_extlen_t i; /* extent length trimmed due to prod */
+ int isfree; /* extent is free */
+ xfs_rtblock_t next; /* next block to try (dummy) */
+
+ ASSERT(minlen % prod == 0 && maxlen % prod == 0);
+ /*
+ * Check if the range in question (for maxlen) is free.
+ */
+ error = xfs_rtcheck_range(mp, tp, bno, maxlen, 1, &next, &isfree);
+ if (error) {
+ return error;
+ }
+ if (isfree) {
+ /*
+ * If it is, allocate it and return success.
+ */
+ error = xfs_rtallocate_range(mp, tp, bno, maxlen, rbpp, rsb);
+ if (error) {
+ return error;
+ }
+ *len = maxlen;
+ *rtblock = bno;
+ return 0;
+ }
+ /*
+ * If not, allocate what there is, if it's at least minlen.
+ */
+ maxlen = next - bno;
+ if (maxlen < minlen) {
+ /*
+ * Failed, return failure status.
+ */
+ *rtblock = NULLRTBLOCK;
+ return 0;
+ }
+ /*
+ * Trim off tail of extent, if prod is specified.
+ */
+ if (prod > 1 && (i = maxlen % prod)) {
+ maxlen -= i;
+ if (maxlen < minlen) {
+ /*
+ * Now we can't do it, return failure status.
+ */
+ *rtblock = NULLRTBLOCK;
+ return 0;
+ }
+ }
+ /*
+ * Allocate what we can and return it.
+ */
+ error = xfs_rtallocate_range(mp, tp, bno, maxlen, rbpp, rsb);
+ if (error) {
+ return error;
+ }
+ *len = maxlen;
+ *rtblock = bno;
+ return 0;
+}
+
+/*
+ * Allocate an extent of length minlen<=len<=maxlen, starting as near
+ * to bno as possible. If we don't get maxlen then use prod to trim
+ * the length, if given. The lengths are all in rtextents.
+ */
+STATIC int /* error */
+xfs_rtallocate_extent_near(
+ xfs_mount_t *mp, /* file system mount point */
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_rtblock_t bno, /* starting block number to allocate */
+ xfs_extlen_t minlen, /* minimum length to allocate */
+ xfs_extlen_t maxlen, /* maximum length to allocate */
+ xfs_extlen_t *len, /* out: actual length allocated */
+ xfs_buf_t **rbpp, /* in/out: summary block buffer */
+ xfs_fsblock_t *rsb, /* in/out: summary block number */
+ xfs_extlen_t prod, /* extent product factor */
+ xfs_rtblock_t *rtblock) /* out: start block allocated */
+{
+ int any; /* any useful extents from summary */
+ xfs_rtblock_t bbno; /* bitmap block number */
+ int error; /* error value */
+ int i; /* bitmap block offset (loop control) */
+ int j; /* secondary loop control */
+ int log2len; /* log2 of minlen */
+ xfs_rtblock_t n; /* next block to try */
+ xfs_rtblock_t r; /* result block */
+
+ ASSERT(minlen % prod == 0 && maxlen % prod == 0);
+ /*
+ * If the block number given is off the end, silently set it to
+ * the last block.
+ */
+ if (bno >= mp->m_sb.sb_rextents)
+ bno = mp->m_sb.sb_rextents - 1;
+ /*
+ * Try the exact allocation first.
+ */
+ error = xfs_rtallocate_extent_exact(mp, tp, bno, minlen, maxlen, len,
+ rbpp, rsb, prod, &r);
+ if (error) {
+ return error;
+ }
+ /*
+ * If the exact allocation worked, return that.
+ */
+ if (r != NULLRTBLOCK) {
+ *rtblock = r;
+ return 0;
+ }
+ bbno = XFS_BITTOBLOCK(mp, bno);
+ i = 0;
+ log2len = xfs_highbit32(minlen);
+ /*
+ * Loop over all bitmap blocks (bbno + i is current block).
+ */
+ for (;;) {
+ /*
+ * Get summary information of extents of all useful levels
+ * starting in this bitmap block.
+ */
+ error = xfs_rtany_summary(mp, tp, log2len, mp->m_rsumlevels - 1,
+ bbno + i, rbpp, rsb, &any);
+ if (error) {
+ return error;
+ }
+ /*
+ * If there are any useful extents starting here, try
+ * allocating one.
+ */
+ if (any) {
+ /*
+ * On the positive side of the starting location.
+ */
+ if (i >= 0) {
+ /*
+ * Try to allocate an extent starting in
+ * this block.
+ */
+ error = xfs_rtallocate_extent_block(mp, tp,
+ bbno + i, minlen, maxlen, len, &n, rbpp,
+ rsb, prod, &r);
+ if (error) {
+ return error;
+ }
+ /*
+ * If it worked, return it.
+ */
+ if (r != NULLRTBLOCK) {
+ *rtblock = r;
+ return 0;
+ }
+ }
+ /*
+ * On the negative side of the starting location.
+ */
+ else { /* i < 0 */
+ /*
+ * Loop backwards through the bitmap blocks from
+ * the starting point-1 up to where we are now.
+ * There should be an extent which ends in this
+ * bitmap block and is long enough.
+ */
+ for (j = -1; j > i; j--) {
+ /*
+ * Grab the summary information for
+ * this bitmap block.
+ */
+ error = xfs_rtany_summary(mp, tp,
+ log2len, mp->m_rsumlevels - 1,
+ bbno + j, rbpp, rsb, &any);
+ if (error) {
+ return error;
+ }
+ /*
+ * If there's no extent given in the
+ * summary that means the extent we
+ * found must carry over from an
+ * earlier block. If there is an
+ * extent given, we've already tried
+ * that allocation, don't do it again.
+ */
+ if (any)
+ continue;
+ error = xfs_rtallocate_extent_block(mp,
+ tp, bbno + j, minlen, maxlen,
+ len, &n, rbpp, rsb, prod, &r);
+ if (error) {
+ return error;
+ }
+ /*
+ * If it works, return the extent.
+ */
+ if (r != NULLRTBLOCK) {
+ *rtblock = r;
+ return 0;
+ }
+ }
+ /*
+ * There weren't intervening bitmap blocks
+ * with a long enough extent, or the
+ * allocation didn't work for some reason
+ * (i.e. it's a little * too short).
+ * Try to allocate from the summary block
+ * that we found.
+ */
+ error = xfs_rtallocate_extent_block(mp, tp,
+ bbno + i, minlen, maxlen, len, &n, rbpp,
+ rsb, prod, &r);
+ if (error) {
+ return error;
+ }
+ /*
+ * If it works, return the extent.
+ */
+ if (r != NULLRTBLOCK) {
+ *rtblock = r;
+ return 0;
+ }
+ }
+ }
+ /*
+ * Loop control. If we were on the positive side, and there's
+ * still more blocks on the negative side, go there.
+ */
+ if (i > 0 && (int)bbno - i >= 0)
+ i = -i;
+ /*
+ * If positive, and no more negative, but there are more
+ * positive, go there.
+ */
+ else if (i > 0 && (int)bbno + i < mp->m_sb.sb_rbmblocks - 1)
+ i++;
+ /*
+ * If negative or 0 (just started), and there are positive
+ * blocks to go, go there. The 0 case moves to block 1.
+ */
+ else if (i <= 0 && (int)bbno - i < mp->m_sb.sb_rbmblocks - 1)
+ i = 1 - i;
+ /*
+ * If negative or 0 and there are more negative blocks,
+ * go there.
+ */
+ else if (i <= 0 && (int)bbno + i > 0)
+ i--;
+ /*
+ * Must be done. Return failure.
+ */
+ else
+ break;
+ }
+ *rtblock = NULLRTBLOCK;
+ return 0;
+}
+
+/*
+ * Allocate an extent of length minlen<=len<=maxlen, with no position
+ * specified. If we don't get maxlen then use prod to trim
+ * the length, if given. The lengths are all in rtextents.
+ */
+STATIC int /* error */
+xfs_rtallocate_extent_size(
+ xfs_mount_t *mp, /* file system mount point */
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_extlen_t minlen, /* minimum length to allocate */
+ xfs_extlen_t maxlen, /* maximum length to allocate */
+ xfs_extlen_t *len, /* out: actual length allocated */
+ xfs_buf_t **rbpp, /* in/out: summary block buffer */
+ xfs_fsblock_t *rsb, /* in/out: summary block number */
+ xfs_extlen_t prod, /* extent product factor */
+ xfs_rtblock_t *rtblock) /* out: start block allocated */
+{
+ int error; /* error value */
+ int i; /* bitmap block number */
+ int l; /* level number (loop control) */
+ xfs_rtblock_t n; /* next block to be tried */
+ xfs_rtblock_t r; /* result block number */
+ xfs_suminfo_t sum; /* summary information for extents */
+
+ ASSERT(minlen % prod == 0 && maxlen % prod == 0);
+ /*
+ * Loop over all the levels starting with maxlen.
+ * At each level, look at all the bitmap blocks, to see if there
+ * are extents starting there that are long enough (>= maxlen).
+ * Note, only on the initial level can the allocation fail if
+ * the summary says there's an extent.
+ */
+ for (l = xfs_highbit32(maxlen); l < mp->m_rsumlevels; l++) {
+ /*
+ * Loop over all the bitmap blocks.
+ */
+ for (i = 0; i < mp->m_sb.sb_rbmblocks; i++) {
+ /*
+ * Get the summary for this level/block.
+ */
+ error = xfs_rtget_summary(mp, tp, l, i, rbpp, rsb,
+ &sum);
+ if (error) {
+ return error;
+ }
+ /*
+ * Nothing there, on to the next block.
+ */
+ if (!sum)
+ continue;
+ /*
+ * Try allocating the extent.
+ */
+ error = xfs_rtallocate_extent_block(mp, tp, i, maxlen,
+ maxlen, len, &n, rbpp, rsb, prod, &r);
+ if (error) {
+ return error;
+ }
+ /*
+ * If it worked, return that.
+ */
+ if (r != NULLRTBLOCK) {
+ *rtblock = r;
+ return 0;
+ }
+ /*
+ * If the "next block to try" returned from the
+ * allocator is beyond the next bitmap block,
+ * skip to that bitmap block.
+ */
+ if (XFS_BITTOBLOCK(mp, n) > i + 1)
+ i = XFS_BITTOBLOCK(mp, n) - 1;
+ }
+ }
+ /*
+ * Didn't find any maxlen blocks. Try smaller ones, unless
+ * we're asking for a fixed size extent.
+ */
+ if (minlen > --maxlen) {
+ *rtblock = NULLRTBLOCK;
+ return 0;
+ }
+ /*
+ * Loop over sizes, from maxlen down to minlen.
+ * This time, when we do the allocations, allow smaller ones
+ * to succeed.
+ */
+ for (l = xfs_highbit32(maxlen); l >= xfs_highbit32(minlen); l--) {
+ /*
+ * Loop over all the bitmap blocks, try an allocation
+ * starting in that block.
+ */
+ for (i = 0; i < mp->m_sb.sb_rbmblocks; i++) {
+ /*
+ * Get the summary information for this level/block.
+ */
+ error = xfs_rtget_summary(mp, tp, l, i, rbpp, rsb,
+ &sum);
+ if (error) {
+ return error;
+ }
+ /*
+ * If nothing there, go on to next.
+ */
+ if (!sum)
+ continue;
+ /*
+ * Try the allocation. Make sure the specified
+ * minlen/maxlen are in the possible range for
+ * this summary level.
+ */
+ error = xfs_rtallocate_extent_block(mp, tp, i,
+ XFS_RTMAX(minlen, 1 << l),
+ XFS_RTMIN(maxlen, (1 << (l + 1)) - 1),
+ len, &n, rbpp, rsb, prod, &r);
+ if (error) {
+ return error;
+ }
+ /*
+ * If it worked, return that extent.
+ */
+ if (r != NULLRTBLOCK) {
+ *rtblock = r;
+ return 0;
+ }
+ /*
+ * If the "next block to try" returned from the
+ * allocator is beyond the next bitmap block,
+ * skip to that bitmap block.
+ */
+ if (XFS_BITTOBLOCK(mp, n) > i + 1)
+ i = XFS_BITTOBLOCK(mp, n) - 1;
+ }
+ }
+ /*
+ * Got nothing, return failure.
+ */
+ *rtblock = NULLRTBLOCK;
+ return 0;
+}
+
+/*
+ * Mark an extent specified by start and len allocated.
+ * Updates all the summary information as well as the bitmap.
+ */
+STATIC int /* error */
+xfs_rtallocate_range(
+ xfs_mount_t *mp, /* file system mount point */
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_rtblock_t start, /* start block to allocate */
+ xfs_extlen_t len, /* length to allocate */
+ xfs_buf_t **rbpp, /* in/out: summary block buffer */
+ xfs_fsblock_t *rsb) /* in/out: summary block number */
+{
+ xfs_rtblock_t end; /* end of the allocated extent */
+ int error; /* error value */
+ xfs_rtblock_t postblock; /* first block allocated > end */
+ xfs_rtblock_t preblock; /* first block allocated < start */
+
+ end = start + len - 1;
+ /*
+ * Assume we're allocating out of the middle of a free extent.
+ * We need to find the beginning and end of the extent so we can
+ * properly update the summary.
+ */
+ error = xfs_rtfind_back(mp, tp, start, 0, &preblock);
+ if (error) {
+ return error;
+ }
+ /*
+ * Find the next allocated block (end of free extent).
+ */
+ error = xfs_rtfind_forw(mp, tp, end, mp->m_sb.sb_rextents - 1,
+ &postblock);
+ if (error) {
+ return error;
+ }
+ /*
+ * Decrement the summary information corresponding to the entire
+ * (old) free extent.
+ */
+ error = xfs_rtmodify_summary(mp, tp,
+ XFS_RTBLOCKLOG(postblock + 1 - preblock),
+ XFS_BITTOBLOCK(mp, preblock), -1, rbpp, rsb);
+ if (error) {
+ return error;
+ }
+ /*
+ * If there are blocks not being allocated at the front of the
+ * old extent, add summary data for them to be free.
+ */
+ if (preblock < start) {
+ error = xfs_rtmodify_summary(mp, tp,
+ XFS_RTBLOCKLOG(start - preblock),
+ XFS_BITTOBLOCK(mp, preblock), 1, rbpp, rsb);
+ if (error) {
+ return error;
+ }
+ }
+ /*
+ * If there are blocks not being allocated at the end of the
+ * old extent, add summary data for them to be free.
+ */
+ if (postblock > end) {
+ error = xfs_rtmodify_summary(mp, tp,
+ XFS_RTBLOCKLOG(postblock - end),
+ XFS_BITTOBLOCK(mp, end + 1), 1, rbpp, rsb);
+ if (error) {
+ return error;
+ }
+ }
+ /*
+ * Modify the bitmap to mark this extent allocated.
+ */
+ error = xfs_rtmodify_range(mp, tp, start, len, 0);
+ return error;
+}
+
+/*
+ * Return whether there are any free extents in the size range given
+ * by low and high, for the bitmap block bbno.
+ */
+STATIC int /* error */
+xfs_rtany_summary(
+ xfs_mount_t *mp, /* file system mount structure */
+ xfs_trans_t *tp, /* transaction pointer */
+ int low, /* low log2 extent size */
+ int high, /* high log2 extent size */
+ xfs_rtblock_t bbno, /* bitmap block number */
+ xfs_buf_t **rbpp, /* in/out: summary block buffer */
+ xfs_fsblock_t *rsb, /* in/out: summary block number */
+ int *stat) /* out: any good extents here? */
+{
+ int error; /* error value */
+ int log; /* loop counter, log2 of ext. size */
+ xfs_suminfo_t sum; /* summary data */
+
+ /*
+ * Loop over logs of extent sizes. Order is irrelevant.
+ */
+ for (log = low; log <= high; log++) {
+ /*
+ * Get one summary datum.
+ */
+ error = xfs_rtget_summary(mp, tp, log, bbno, rbpp, rsb, &sum);
+ if (error) {
+ return error;
+ }
+ /*
+ * If there are any, return success.
+ */
+ if (sum) {
+ *stat = 1;
+ return 0;
+ }
+ }
+ /*
+ * Found nothing, return failure.
+ */
+ *stat = 0;
+ return 0;
+}
+
+/*
+ * Get a buffer for the bitmap or summary file block specified.
+ * The buffer is returned read and locked.
+ */
+STATIC int /* error */
+xfs_rtbuf_get(
+ xfs_mount_t *mp, /* file system mount structure */
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_rtblock_t block, /* block number in bitmap or summary */
+ int issum, /* is summary not bitmap */
+ xfs_buf_t **bpp) /* output: buffer for the block */
+{
+ xfs_buf_t *bp; /* block buffer, result */
+ xfs_daddr_t d; /* disk addr of block */
+ int error; /* error value */
+ xfs_fsblock_t fsb; /* fs block number for block */
+ xfs_inode_t *ip; /* bitmap or summary inode */
+
+ ip = issum ? mp->m_rsumip : mp->m_rbmip;
+ /*
+ * Map from the file offset (block) and inode number to the
+ * file system block.
+ */
+ error = xfs_bmapi_single(tp, ip, XFS_DATA_FORK, &fsb, block);
+ if (error) {
+ return error;
+ }
+ ASSERT(fsb != NULLFSBLOCK);
+ /*
+ * Convert to disk address for buffer cache.
+ */
+ d = XFS_FSB_TO_DADDR(mp, fsb);
+ /*
+ * Read the buffer.
+ */
+ error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp, d,
+ mp->m_bsize, 0, &bp);
+ if (error) {
+ return error;
+ }
+ ASSERT(bp && !XFS_BUF_GETERROR(bp));
+ *bpp = bp;
+ return 0;
+}
+
+#ifdef DEBUG
+/*
+ * Check that the given extent (block range) is allocated already.
+ */
+STATIC int /* error */
+xfs_rtcheck_alloc_range(
+ xfs_mount_t *mp, /* file system mount point */
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_rtblock_t bno, /* starting block number of extent */
+ xfs_extlen_t len, /* length of extent */
+ int *stat) /* out: 1 for allocated, 0 for not */
+{
+ xfs_rtblock_t new; /* dummy for xfs_rtcheck_range */
+
+ return xfs_rtcheck_range(mp, tp, bno, len, 0, &new, stat);
+}
+#endif
+
+#ifdef DEBUG
+/*
+ * Check whether the given block in the bitmap has the given value.
+ */
+STATIC int /* 1 for matches, 0 for not */
+xfs_rtcheck_bit(
+ xfs_mount_t *mp, /* file system mount structure */
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_rtblock_t start, /* bit (block) to check */
+ int val) /* 1 for free, 0 for allocated */
+{
+ int bit; /* bit number in the word */
+ xfs_rtblock_t block; /* bitmap block number */
+ xfs_buf_t *bp; /* buf for the block */
+ xfs_rtword_t *bufp; /* pointer into the buffer */
+ /* REFERENCED */
+ int error; /* error value */
+ xfs_rtword_t wdiff; /* difference between bit & expected */
+ int word; /* word number in the buffer */
+ xfs_rtword_t wval; /* word value from buffer */
+
+ block = XFS_BITTOBLOCK(mp, start);
+ error = xfs_rtbuf_get(mp, tp, block, 0, &bp);
+ bufp = (xfs_rtword_t *)XFS_BUF_PTR(bp);
+ word = XFS_BITTOWORD(mp, start);
+ bit = (int)(start & (XFS_NBWORD - 1));
+ wval = bufp[word];
+ xfs_trans_brelse(tp, bp);
+ wdiff = (wval ^ -val) & ((xfs_rtword_t)1 << bit);
+ return !wdiff;
+}
+#endif /* DEBUG */
+
+#if 0
+/*
+ * Check that the given extent (block range) is free already.
+ */
+STATIC int /* error */
+xfs_rtcheck_free_range(
+ xfs_mount_t *mp, /* file system mount point */
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_rtblock_t bno, /* starting block number of extent */
+ xfs_extlen_t len, /* length of extent */
+ int *stat) /* out: 1 for free, 0 for not */
+{
+ xfs_rtblock_t new; /* dummy for xfs_rtcheck_range */
+
+ return xfs_rtcheck_range(mp, tp, bno, len, 1, &new, stat);
+}
+#endif
+
+/*
+ * Check that the given range is either all allocated (val = 0) or
+ * all free (val = 1).
+ */
+STATIC int /* error */
+xfs_rtcheck_range(
+ xfs_mount_t *mp, /* file system mount point */
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_rtblock_t start, /* starting block number of extent */
+ xfs_extlen_t len, /* length of extent */
+ int val, /* 1 for free, 0 for allocated */
+ xfs_rtblock_t *new, /* out: first block not matching */
+ int *stat) /* out: 1 for matches, 0 for not */
+{
+ xfs_rtword_t *b; /* current word in buffer */
+ int bit; /* bit number in the word */
+ xfs_rtblock_t block; /* bitmap block number */
+ xfs_buf_t *bp; /* buf for the block */
+ xfs_rtword_t *bufp; /* starting word in buffer */
+ int error; /* error value */
+ xfs_rtblock_t i; /* current bit number rel. to start */
+ xfs_rtblock_t lastbit; /* last useful bit in word */
+ xfs_rtword_t mask; /* mask of relevant bits for value */
+ xfs_rtword_t wdiff; /* difference from wanted value */
+ int word; /* word number in the buffer */
+
+ /*
+ * Compute starting bitmap block number
+ */
+ block = XFS_BITTOBLOCK(mp, start);
+ /*
+ * Read the bitmap block.
+ */
+ error = xfs_rtbuf_get(mp, tp, block, 0, &bp);
+ if (error) {
+ return error;
+ }
+ bufp = (xfs_rtword_t *)XFS_BUF_PTR(bp);
+ /*
+ * Compute the starting word's address, and starting bit.
+ */
+ word = XFS_BITTOWORD(mp, start);
+ b = &bufp[word];
+ bit = (int)(start & (XFS_NBWORD - 1));
+ /*
+ * 0 (allocated) => all zero's; 1 (free) => all one's.
+ */
+ val = -val;
+ /*
+ * If not starting on a word boundary, deal with the first
+ * (partial) word.
+ */
+ if (bit) {
+ /*
+ * Compute first bit not examined.
+ */
+ lastbit = XFS_RTMIN(bit + len, XFS_NBWORD);
+ /*
+ * Mask of relevant bits.
+ */
+ mask = (((xfs_rtword_t)1 << (lastbit - bit)) - 1) << bit;
+ /*
+ * Compute difference between actual and desired value.
+ */
+ if ((wdiff = (*b ^ val) & mask)) {
+ /*
+ * Different, compute first wrong bit and return.
+ */
+ xfs_trans_brelse(tp, bp);
+ i = XFS_RTLOBIT(wdiff) - bit;
+ *new = start + i;
+ *stat = 0;
+ return 0;
+ }
+ i = lastbit - bit;
+ /*
+ * Go on to next block if that's where the next word is
+ * and we need the next word.
+ */
+ if (++word == XFS_BLOCKWSIZE(mp) && i < len) {
+ /*
+ * If done with this block, get the next one.
+ */
+ xfs_trans_brelse(tp, bp);
+ error = xfs_rtbuf_get(mp, tp, ++block, 0, &bp);
+ if (error) {
+ return error;
+ }
+ b = bufp = (xfs_rtword_t *)XFS_BUF_PTR(bp);
+ word = 0;
+ } else {
+ /*
+ * Go on to the next word in the buffer.
+ */
+ b++;
+ }
+ } else {
+ /*
+ * Starting on a word boundary, no partial word.
+ */
+ i = 0;
+ }
+ /*
+ * Loop over whole words in buffers. When we use up one buffer
+ * we move on to the next one.
+ */
+ while (len - i >= XFS_NBWORD) {
+ /*
+ * Compute difference between actual and desired value.
+ */
+ if ((wdiff = *b ^ val)) {
+ /*
+ * Different, compute first wrong bit and return.
+ */
+ xfs_trans_brelse(tp, bp);
+ i += XFS_RTLOBIT(wdiff);
+ *new = start + i;
+ *stat = 0;
+ return 0;
+ }
+ i += XFS_NBWORD;
+ /*
+ * Go on to next block if that's where the next word is
+ * and we need the next word.
+ */
+ if (++word == XFS_BLOCKWSIZE(mp) && i < len) {
+ /*
+ * If done with this block, get the next one.
+ */
+ xfs_trans_brelse(tp, bp);
+ error = xfs_rtbuf_get(mp, tp, ++block, 0, &bp);
+ if (error) {
+ return error;
+ }
+ b = bufp = (xfs_rtword_t *)XFS_BUF_PTR(bp);
+ word = 0;
+ } else {
+ /*
+ * Go on to the next word in the buffer.
+ */
+ b++;
+ }
+ }
+ /*
+ * If not ending on a word boundary, deal with the last
+ * (partial) word.
+ */
+ if ((lastbit = len - i)) {
+ /*
+ * Mask of relevant bits.
+ */
+ mask = ((xfs_rtword_t)1 << lastbit) - 1;
+ /*
+ * Compute difference between actual and desired value.
+ */
+ if ((wdiff = (*b ^ val) & mask)) {
+ /*
+ * Different, compute first wrong bit and return.
+ */
+ xfs_trans_brelse(tp, bp);
+ i += XFS_RTLOBIT(wdiff);
+ *new = start + i;
+ *stat = 0;
+ return 0;
+ } else
+ i = len;
+ }
+ /*
+ * Successful, return.
+ */
+ xfs_trans_brelse(tp, bp);
+ *new = start + i;
+ *stat = 1;
+ return 0;
+}
+
+/*
+ * Copy and transform the summary file, given the old and new
+ * parameters in the mount structures.
+ */
+STATIC int /* error */
+xfs_rtcopy_summary(
+ xfs_mount_t *omp, /* old file system mount point */
+ xfs_mount_t *nmp, /* new file system mount point */
+ xfs_trans_t *tp) /* transaction pointer */
+{
+ xfs_rtblock_t bbno; /* bitmap block number */
+ xfs_buf_t *bp; /* summary buffer */
+ int error; /* error return value */
+ int log; /* summary level number (log length) */
+ xfs_suminfo_t sum; /* summary data */
+ xfs_fsblock_t sumbno; /* summary block number */
+
+ bp = NULL;
+ for (log = omp->m_rsumlevels - 1; log >= 0; log--) {
+ for (bbno = omp->m_sb.sb_rbmblocks - 1;
+ (xfs_srtblock_t)bbno >= 0;
+ bbno--) {
+ error = xfs_rtget_summary(omp, tp, log, bbno, &bp,
+ &sumbno, &sum);
+ if (error)
+ return error;
+ if (sum == 0)
+ continue;
+ error = xfs_rtmodify_summary(omp, tp, log, bbno, -sum,
+ &bp, &sumbno);
+ if (error)
+ return error;
+ error = xfs_rtmodify_summary(nmp, tp, log, bbno, sum,
+ &bp, &sumbno);
+ if (error)
+ return error;
+ ASSERT(sum > 0);
+ }
+ }
+ return 0;
+}
+
+/*
+ * Searching backward from start to limit, find the first block whose
+ * allocated/free state is different from start's.
+ */
+STATIC int /* error */
+xfs_rtfind_back(
+ xfs_mount_t *mp, /* file system mount point */
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_rtblock_t start, /* starting block to look at */
+ xfs_rtblock_t limit, /* last block to look at */
+ xfs_rtblock_t *rtblock) /* out: start block found */
+{
+ xfs_rtword_t *b; /* current word in buffer */
+ int bit; /* bit number in the word */
+ xfs_rtblock_t block; /* bitmap block number */
+ xfs_buf_t *bp; /* buf for the block */
+ xfs_rtword_t *bufp; /* starting word in buffer */
+ int error; /* error value */
+ xfs_rtblock_t firstbit; /* first useful bit in the word */
+ xfs_rtblock_t i; /* current bit number rel. to start */
+ xfs_rtblock_t len; /* length of inspected area */
+ xfs_rtword_t mask; /* mask of relevant bits for value */
+ xfs_rtword_t want; /* mask for "good" values */
+ xfs_rtword_t wdiff; /* difference from wanted value */
+ int word; /* word number in the buffer */
+
+ /*
+ * Compute and read in starting bitmap block for starting block.
+ */
+ block = XFS_BITTOBLOCK(mp, start);
+ error = xfs_rtbuf_get(mp, tp, block, 0, &bp);
+ if (error) {
+ return error;
+ }
+ bufp = (xfs_rtword_t *)XFS_BUF_PTR(bp);
+ /*
+ * Get the first word's index & point to it.
+ */
+ word = XFS_BITTOWORD(mp, start);
+ b = &bufp[word];
+ bit = (int)(start & (XFS_NBWORD - 1));
+ len = start - limit + 1;
+ /*
+ * Compute match value, based on the bit at start: if 1 (free)
+ * then all-ones, else all-zeroes.
+ */
+ want = (*b & ((xfs_rtword_t)1 << bit)) ? -1 : 0;
+ /*
+ * If the starting position is not word-aligned, deal with the
+ * partial word.
+ */
+ if (bit < XFS_NBWORD - 1) {
+ /*
+ * Calculate first (leftmost) bit number to look at,
+ * and mask for all the relevant bits in this word.
+ */
+ firstbit = XFS_RTMAX((xfs_srtblock_t)(bit - len + 1), 0);
+ mask = (((xfs_rtword_t)1 << (bit - firstbit + 1)) - 1) <<
+ firstbit;
+ /*
+ * Calculate the difference between the value there
+ * and what we're looking for.
+ */
+ if ((wdiff = (*b ^ want) & mask)) {
+ /*
+ * Different. Mark where we are and return.
+ */
+ xfs_trans_brelse(tp, bp);
+ i = bit - XFS_RTHIBIT(wdiff);
+ *rtblock = start - i + 1;
+ return 0;
+ }
+ i = bit - firstbit + 1;
+ /*
+ * Go on to previous block if that's where the previous word is
+ * and we need the previous word.
+ */
+ if (--word == -1 && i < len) {
+ /*
+ * If done with this block, get the previous one.
+ */
+ xfs_trans_brelse(tp, bp);
+ error = xfs_rtbuf_get(mp, tp, --block, 0, &bp);
+ if (error) {
+ return error;
+ }
+ bufp = (xfs_rtword_t *)XFS_BUF_PTR(bp);
+ word = XFS_BLOCKWMASK(mp);
+ b = &bufp[word];
+ } else {
+ /*
+ * Go on to the previous word in the buffer.
+ */
+ b--;
+ }
+ } else {
+ /*
+ * Starting on a word boundary, no partial word.
+ */
+ i = 0;
+ }
+ /*
+ * Loop over whole words in buffers. When we use up one buffer
+ * we move on to the previous one.
+ */
+ while (len - i >= XFS_NBWORD) {
+ /*
+ * Compute difference between actual and desired value.
+ */
+ if ((wdiff = *b ^ want)) {
+ /*
+ * Different, mark where we are and return.
+ */
+ xfs_trans_brelse(tp, bp);
+ i += XFS_NBWORD - 1 - XFS_RTHIBIT(wdiff);
+ *rtblock = start - i + 1;
+ return 0;
+ }
+ i += XFS_NBWORD;
+ /*
+ * Go on to previous block if that's where the previous word is
+ * and we need the previous word.
+ */
+ if (--word == -1 && i < len) {
+ /*
+ * If done with this block, get the previous one.
+ */
+ xfs_trans_brelse(tp, bp);
+ error = xfs_rtbuf_get(mp, tp, --block, 0, &bp);
+ if (error) {
+ return error;
+ }
+ bufp = (xfs_rtword_t *)XFS_BUF_PTR(bp);
+ word = XFS_BLOCKWMASK(mp);
+ b = &bufp[word];
+ } else {
+ /*
+ * Go on to the previous word in the buffer.
+ */
+ b--;
+ }
+ }
+ /*
+ * If not ending on a word boundary, deal with the last
+ * (partial) word.
+ */
+ if (len - i) {
+ /*
+ * Calculate first (leftmost) bit number to look at,
+ * and mask for all the relevant bits in this word.
+ */
+ firstbit = XFS_NBWORD - (len - i);
+ mask = (((xfs_rtword_t)1 << (len - i)) - 1) << firstbit;
+ /*
+ * Compute difference between actual and desired value.
+ */
+ if ((wdiff = (*b ^ want) & mask)) {
+ /*
+ * Different, mark where we are and return.
+ */
+ xfs_trans_brelse(tp, bp);
+ i += XFS_NBWORD - 1 - XFS_RTHIBIT(wdiff);
+ *rtblock = start - i + 1;
+ return 0;
+ } else
+ i = len;
+ }
+ /*
+ * No match, return that we scanned the whole area.
+ */
+ xfs_trans_brelse(tp, bp);
+ *rtblock = start - i + 1;
+ return 0;
+}
+
+/*
+ * Searching forward from start to limit, find the first block whose
+ * allocated/free state is different from start's.
+ */
+STATIC int /* error */
+xfs_rtfind_forw(
+ xfs_mount_t *mp, /* file system mount point */
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_rtblock_t start, /* starting block to look at */
+ xfs_rtblock_t limit, /* last block to look at */
+ xfs_rtblock_t *rtblock) /* out: start block found */
+{
+ xfs_rtword_t *b; /* current word in buffer */
+ int bit; /* bit number in the word */
+ xfs_rtblock_t block; /* bitmap block number */
+ xfs_buf_t *bp; /* buf for the block */
+ xfs_rtword_t *bufp; /* starting word in buffer */
+ int error; /* error value */
+ xfs_rtblock_t i; /* current bit number rel. to start */
+ xfs_rtblock_t lastbit; /* last useful bit in the word */
+ xfs_rtblock_t len; /* length of inspected area */
+ xfs_rtword_t mask; /* mask of relevant bits for value */
+ xfs_rtword_t want; /* mask for "good" values */
+ xfs_rtword_t wdiff; /* difference from wanted value */
+ int word; /* word number in the buffer */
+
+ /*
+ * Compute and read in starting bitmap block for starting block.
+ */
+ block = XFS_BITTOBLOCK(mp, start);
+ error = xfs_rtbuf_get(mp, tp, block, 0, &bp);
+ if (error) {
+ return error;
+ }
+ bufp = (xfs_rtword_t *)XFS_BUF_PTR(bp);
+ /*
+ * Get the first word's index & point to it.
+ */
+ word = XFS_BITTOWORD(mp, start);
+ b = &bufp[word];
+ bit = (int)(start & (XFS_NBWORD - 1));
+ len = limit - start + 1;
+ /*
+ * Compute match value, based on the bit at start: if 1 (free)
+ * then all-ones, else all-zeroes.
+ */
+ want = (*b & ((xfs_rtword_t)1 << bit)) ? -1 : 0;
+ /*
+ * If the starting position is not word-aligned, deal with the
+ * partial word.
+ */
+ if (bit) {
+ /*
+ * Calculate last (rightmost) bit number to look at,
+ * and mask for all the relevant bits in this word.
+ */
+ lastbit = XFS_RTMIN(bit + len, XFS_NBWORD);
+ mask = (((xfs_rtword_t)1 << (lastbit - bit)) - 1) << bit;
+ /*
+ * Calculate the difference between the value there
+ * and what we're looking for.
+ */
+ if ((wdiff = (*b ^ want) & mask)) {
+ /*
+ * Different. Mark where we are and return.
+ */
+ xfs_trans_brelse(tp, bp);
+ i = XFS_RTLOBIT(wdiff) - bit;
+ *rtblock = start + i - 1;
+ return 0;
+ }
+ i = lastbit - bit;
+ /*
+ * Go on to next block if that's where the next word is
+ * and we need the next word.
+ */
+ if (++word == XFS_BLOCKWSIZE(mp) && i < len) {
+ /*
+ * If done with this block, get the previous one.
+ */
+ xfs_trans_brelse(tp, bp);
+ error = xfs_rtbuf_get(mp, tp, ++block, 0, &bp);
+ if (error) {
+ return error;
+ }
+ b = bufp = (xfs_rtword_t *)XFS_BUF_PTR(bp);
+ word = 0;
+ } else {
+ /*
+ * Go on to the previous word in the buffer.
+ */
+ b++;
+ }
+ } else {
+ /*
+ * Starting on a word boundary, no partial word.
+ */
+ i = 0;
+ }
+ /*
+ * Loop over whole words in buffers. When we use up one buffer
+ * we move on to the next one.
+ */
+ while (len - i >= XFS_NBWORD) {
+ /*
+ * Compute difference between actual and desired value.
+ */
+ if ((wdiff = *b ^ want)) {
+ /*
+ * Different, mark where we are and return.
+ */
+ xfs_trans_brelse(tp, bp);
+ i += XFS_RTLOBIT(wdiff);
+ *rtblock = start + i - 1;
+ return 0;
+ }
+ i += XFS_NBWORD;
+ /*
+ * Go on to next block if that's where the next word is
+ * and we need the next word.
+ */
+ if (++word == XFS_BLOCKWSIZE(mp) && i < len) {
+ /*
+ * If done with this block, get the next one.
+ */
+ xfs_trans_brelse(tp, bp);
+ error = xfs_rtbuf_get(mp, tp, ++block, 0, &bp);
+ if (error) {
+ return error;
+ }
+ b = bufp = (xfs_rtword_t *)XFS_BUF_PTR(bp);
+ word = 0;
+ } else {
+ /*
+ * Go on to the next word in the buffer.
+ */
+ b++;
+ }
+ }
+ /*
+ * If not ending on a word boundary, deal with the last
+ * (partial) word.
+ */
+ if ((lastbit = len - i)) {
+ /*
+ * Calculate mask for all the relevant bits in this word.
+ */
+ mask = ((xfs_rtword_t)1 << lastbit) - 1;
+ /*
+ * Compute difference between actual and desired value.
+ */
+ if ((wdiff = (*b ^ want) & mask)) {
+ /*
+ * Different, mark where we are and return.
+ */
+ xfs_trans_brelse(tp, bp);
+ i += XFS_RTLOBIT(wdiff);
+ *rtblock = start + i - 1;
+ return 0;
+ } else
+ i = len;
+ }
+ /*
+ * No match, return that we scanned the whole area.
+ */
+ xfs_trans_brelse(tp, bp);
+ *rtblock = start + i - 1;
+ return 0;
+}
+
+/*
+ * Mark an extent specified by start and len freed.
+ * Updates all the summary information as well as the bitmap.
+ */
+STATIC int /* error */
+xfs_rtfree_range(
+ xfs_mount_t *mp, /* file system mount point */
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_rtblock_t start, /* starting block to free */
+ xfs_extlen_t len, /* length to free */
+ xfs_buf_t **rbpp, /* in/out: summary block buffer */
+ xfs_fsblock_t *rsb) /* in/out: summary block number */
+{
+ xfs_rtblock_t end; /* end of the freed extent */
+ int error; /* error value */
+ xfs_rtblock_t postblock; /* first block freed > end */
+ xfs_rtblock_t preblock; /* first block freed < start */
+
+ end = start + len - 1;
+ /*
+ * Modify the bitmap to mark this extent freed.
+ */
+ error = xfs_rtmodify_range(mp, tp, start, len, 1);
+ if (error) {
+ return error;
+ }
+ /*
+ * Assume we're freeing out of the middle of an allocated extent.
+ * We need to find the beginning and end of the extent so we can
+ * properly update the summary.
+ */
+ error = xfs_rtfind_back(mp, tp, start, 0, &preblock);
+ if (error) {
+ return error;
+ }
+ /*
+ * Find the next allocated block (end of allocated extent).
+ */
+ error = xfs_rtfind_forw(mp, tp, end, mp->m_sb.sb_rextents - 1,
+ &postblock);
+ /*
+ * If there are blocks not being freed at the front of the
+ * old extent, add summary data for them to be allocated.
+ */
+ if (preblock < start) {
+ error = xfs_rtmodify_summary(mp, tp,
+ XFS_RTBLOCKLOG(start - preblock),
+ XFS_BITTOBLOCK(mp, preblock), -1, rbpp, rsb);
+ if (error) {
+ return error;
+ }
+ }
+ /*
+ * If there are blocks not being freed at the end of the
+ * old extent, add summary data for them to be allocated.
+ */
+ if (postblock > end) {
+ error = xfs_rtmodify_summary(mp, tp,
+ XFS_RTBLOCKLOG(postblock - end),
+ XFS_BITTOBLOCK(mp, end + 1), -1, rbpp, rsb);
+ if (error) {
+ return error;
+ }
+ }
+ /*
+ * Increment the summary information corresponding to the entire
+ * (new) free extent.
+ */
+ error = xfs_rtmodify_summary(mp, tp,
+ XFS_RTBLOCKLOG(postblock + 1 - preblock),
+ XFS_BITTOBLOCK(mp, preblock), 1, rbpp, rsb);
+ return error;
+}
+
+/*
+ * Read and return the summary information for a given extent size,
+ * bitmap block combination.
+ * Keeps track of a current summary block, so we don't keep reading
+ * it from the buffer cache.
+ */
+STATIC int /* error */
+xfs_rtget_summary(
+ xfs_mount_t *mp, /* file system mount structure */
+ xfs_trans_t *tp, /* transaction pointer */
+ int log, /* log2 of extent size */
+ xfs_rtblock_t bbno, /* bitmap block number */
+ xfs_buf_t **rbpp, /* in/out: summary block buffer */
+ xfs_fsblock_t *rsb, /* in/out: summary block number */
+ xfs_suminfo_t *sum) /* out: summary info for this block */
+{
+ xfs_buf_t *bp; /* buffer for summary block */
+ int error; /* error value */
+ xfs_fsblock_t sb; /* summary fsblock */
+ int so; /* index into the summary file */
+ xfs_suminfo_t *sp; /* pointer to returned data */
+
+ /*
+ * Compute entry number in the summary file.
+ */
+ so = XFS_SUMOFFS(mp, log, bbno);
+ /*
+ * Compute the block number in the summary file.
+ */
+ sb = XFS_SUMOFFSTOBLOCK(mp, so);
+ /*
+ * If we have an old buffer, and the block number matches, use that.
+ */
+ if (rbpp && *rbpp && *rsb == sb)
+ bp = *rbpp;
+ /*
+ * Otherwise we have to get the buffer.
+ */
+ else {
+ /*
+ * If there was an old one, get rid of it first.
+ */
+ if (rbpp && *rbpp)
+ xfs_trans_brelse(tp, *rbpp);
+ error = xfs_rtbuf_get(mp, tp, sb, 1, &bp);
+ if (error) {
+ return error;
+ }
+ /*
+ * Remember this buffer and block for the next call.
+ */
+ if (rbpp) {
+ *rbpp = bp;
+ *rsb = sb;
+ }
+ }
+ /*
+ * Point to the summary information & copy it out.
+ */
+ sp = XFS_SUMPTR(mp, bp, so);
+ *sum = *sp;
+ /*
+ * Drop the buffer if we're not asked to remember it.
+ */
+ if (!rbpp)
+ xfs_trans_brelse(tp, bp);
+ return 0;
+}
+
+/*
+ * Set the given range of bitmap bits to the given value.
+ * Do whatever I/O and logging is required.
+ */
+STATIC int /* error */
+xfs_rtmodify_range(
+ xfs_mount_t *mp, /* file system mount point */
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_rtblock_t start, /* starting block to modify */
+ xfs_extlen_t len, /* length of extent to modify */
+ int val) /* 1 for free, 0 for allocated */
+{
+ xfs_rtword_t *b; /* current word in buffer */
+ int bit; /* bit number in the word */
+ xfs_rtblock_t block; /* bitmap block number */
+ xfs_buf_t *bp; /* buf for the block */
+ xfs_rtword_t *bufp; /* starting word in buffer */
+ int error; /* error value */
+ xfs_rtword_t *first; /* first used word in the buffer */
+ int i; /* current bit number rel. to start */
+ int lastbit; /* last useful bit in word */
+ xfs_rtword_t mask; /* mask o frelevant bits for value */
+ int word; /* word number in the buffer */
+
+ /*
+ * Compute starting bitmap block number.
+ */
+ block = XFS_BITTOBLOCK(mp, start);
+ /*
+ * Read the bitmap block, and point to its data.
+ */
+ error = xfs_rtbuf_get(mp, tp, block, 0, &bp);
+ if (error) {
+ return error;
+ }
+ bufp = (xfs_rtword_t *)XFS_BUF_PTR(bp);
+ /*
+ * Compute the starting word's address, and starting bit.
+ */
+ word = XFS_BITTOWORD(mp, start);
+ first = b = &bufp[word];
+ bit = (int)(start & (XFS_NBWORD - 1));
+ /*
+ * 0 (allocated) => all zeroes; 1 (free) => all ones.
+ */
+ val = -val;
+ /*
+ * If not starting on a word boundary, deal with the first
+ * (partial) word.
+ */
+ if (bit) {
+ /*
+ * Compute first bit not changed and mask of relevant bits.
+ */
+ lastbit = XFS_RTMIN(bit + len, XFS_NBWORD);
+ mask = (((xfs_rtword_t)1 << (lastbit - bit)) - 1) << bit;
+ /*
+ * Set/clear the active bits.
+ */
+ if (val)
+ *b |= mask;
+ else
+ *b &= ~mask;
+ i = lastbit - bit;
+ /*
+ * Go on to the next block if that's where the next word is
+ * and we need the next word.
+ */
+ if (++word == XFS_BLOCKWSIZE(mp) && i < len) {
+ /*
+ * Log the changed part of this block.
+ * Get the next one.
+ */
+ xfs_trans_log_buf(tp, bp,
+ (uint)((char *)first - (char *)bufp),
+ (uint)((char *)b - (char *)bufp));
+ error = xfs_rtbuf_get(mp, tp, ++block, 0, &bp);
+ if (error) {
+ return error;
+ }
+ first = b = bufp = (xfs_rtword_t *)XFS_BUF_PTR(bp);
+ word = 0;
+ } else {
+ /*
+ * Go on to the next word in the buffer
+ */
+ b++;
+ }
+ } else {
+ /*
+ * Starting on a word boundary, no partial word.
+ */
+ i = 0;
+ }
+ /*
+ * Loop over whole words in buffers. When we use up one buffer
+ * we move on to the next one.
+ */
+ while (len - i >= XFS_NBWORD) {
+ /*
+ * Set the word value correctly.
+ */
+ *b = val;
+ i += XFS_NBWORD;
+ /*
+ * Go on to the next block if that's where the next word is
+ * and we need the next word.
+ */
+ if (++word == XFS_BLOCKWSIZE(mp) && i < len) {
+ /*
+ * Log the changed part of this block.
+ * Get the next one.
+ */
+ xfs_trans_log_buf(tp, bp,
+ (uint)((char *)first - (char *)bufp),
+ (uint)((char *)b - (char *)bufp));
+ error = xfs_rtbuf_get(mp, tp, ++block, 0, &bp);
+ if (error) {
+ return error;
+ }
+ first = b = bufp = (xfs_rtword_t *)XFS_BUF_PTR(bp);
+ word = 0;
+ } else {
+ /*
+ * Go on to the next word in the buffer
+ */
+ b++;
+ }
+ }
+ /*
+ * If not ending on a word boundary, deal with the last
+ * (partial) word.
+ */
+ if ((lastbit = len - i)) {
+ /*
+ * Compute a mask of relevant bits.
+ */
+ bit = 0;
+ mask = ((xfs_rtword_t)1 << lastbit) - 1;
+ /*
+ * Set/clear the active bits.
+ */
+ if (val)
+ *b |= mask;
+ else
+ *b &= ~mask;
+ b++;
+ }
+ /*
+ * Log any remaining changed bytes.
+ */
+ if (b > first)
+ xfs_trans_log_buf(tp, bp, (uint)((char *)first - (char *)bufp),
+ (uint)((char *)b - (char *)bufp - 1));
+ return 0;
+}
+
+/*
+ * Read and modify the summary information for a given extent size,
+ * bitmap block combination.
+ * Keeps track of a current summary block, so we don't keep reading
+ * it from the buffer cache.
+ */
+STATIC int /* error */
+xfs_rtmodify_summary(
+ xfs_mount_t *mp, /* file system mount point */
+ xfs_trans_t *tp, /* transaction pointer */
+ int log, /* log2 of extent size */
+ xfs_rtblock_t bbno, /* bitmap block number */
+ int delta, /* change to make to summary info */
+ xfs_buf_t **rbpp, /* in/out: summary block buffer */
+ xfs_fsblock_t *rsb) /* in/out: summary block number */
+{
+ xfs_buf_t *bp; /* buffer for the summary block */
+ int error; /* error value */
+ xfs_fsblock_t sb; /* summary fsblock */
+ int so; /* index into the summary file */
+ xfs_suminfo_t *sp; /* pointer to returned data */
+
+ /*
+ * Compute entry number in the summary file.
+ */
+ so = XFS_SUMOFFS(mp, log, bbno);
+ /*
+ * Compute the block number in the summary file.
+ */
+ sb = XFS_SUMOFFSTOBLOCK(mp, so);
+ /*
+ * If we have an old buffer, and the block number matches, use that.
+ */
+ if (rbpp && *rbpp && *rsb == sb)
+ bp = *rbpp;
+ /*
+ * Otherwise we have to get the buffer.
+ */
+ else {
+ /*
+ * If there was an old one, get rid of it first.
+ */
+ if (rbpp && *rbpp)
+ xfs_trans_brelse(tp, *rbpp);
+ error = xfs_rtbuf_get(mp, tp, sb, 1, &bp);
+ if (error) {
+ return error;
+ }
+ /*
+ * Remember this buffer and block for the next call.
+ */
+ if (rbpp) {
+ *rbpp = bp;
+ *rsb = sb;
+ }
+ }
+ /*
+ * Point to the summary information, modify and log it.
+ */
+ sp = XFS_SUMPTR(mp, bp, so);
+ *sp += delta;
+ xfs_trans_log_buf(tp, bp, (uint)((char *)sp - (char *)XFS_BUF_PTR(bp)),
+ (uint)((char *)sp - (char *)XFS_BUF_PTR(bp) + sizeof(*sp) - 1));
+ return 0;
+}
+
+/*
+ * Visible (exported) functions.
+ */
+
+/*
+ * Grow the realtime area of the filesystem.
+ */
+int
+xfs_growfs_rt(
+ xfs_mount_t *mp, /* mount point for filesystem */
+ xfs_growfs_rt_t *in) /* growfs rt input struct */
+{
+ xfs_rtblock_t bmbno; /* bitmap block number */
+ xfs_buf_t *bp; /* temporary buffer */
+ int cancelflags; /* flags for xfs_trans_cancel */
+ int error; /* error return value */
+ xfs_inode_t *ip; /* bitmap inode, used as lock */
+ xfs_mount_t *nmp; /* new (fake) mount structure */
+ xfs_drfsbno_t nrblocks; /* new number of realtime blocks */
+ xfs_extlen_t nrbmblocks; /* new number of rt bitmap blocks */
+ xfs_drtbno_t nrextents; /* new number of realtime extents */
+ uint8_t nrextslog; /* new log2 of sb_rextents */
+ xfs_extlen_t nrsumblocks; /* new number of summary blocks */
+ uint nrsumlevels; /* new rt summary levels */
+ uint nrsumsize; /* new size of rt summary, bytes */
+ xfs_sb_t *nsbp; /* new superblock */
+ xfs_extlen_t rbmblocks; /* current number of rt bitmap blocks */
+ xfs_extlen_t rsumblocks; /* current number of rt summary blks */
+ xfs_sb_t *sbp; /* old superblock */
+ xfs_fsblock_t sumbno; /* summary block number */
+ xfs_trans_t *tp; /* transaction pointer */
+
+ sbp = &mp->m_sb;
+ /*
+ * Initial error checking.
+ */
+ if (mp->m_rtdev_targp || mp->m_rbmip == NULL ||
+ (nrblocks = in->newblocks) <= sbp->sb_rblocks ||
+ (sbp->sb_rblocks && (in->extsize != sbp->sb_rextsize)))
+ return XFS_ERROR(EINVAL);
+ /*
+ * Read in the last block of the device, make sure it exists.
+ */
+ error = xfs_read_buf(mp, mp->m_rtdev_targp,
+ XFS_FSB_TO_BB(mp, in->newblocks - 1),
+ XFS_FSB_TO_BB(mp, 1), 0, &bp);
+ if (error)
+ return error;
+ ASSERT(bp);
+ xfs_buf_relse(bp);
+ /*
+ * Calculate new parameters. These are the final values to be reached.
+ */
+ nrextents = nrblocks;
+ do_div(nrextents, in->extsize);
+ nrbmblocks = roundup_64(nrextents, NBBY * sbp->sb_blocksize);
+ nrextslog = xfs_highbit32(nrextents);
+ nrsumlevels = nrextslog + 1;
+ nrsumsize = (uint)sizeof(xfs_suminfo_t) * nrsumlevels * nrbmblocks;
+ nrsumblocks = XFS_B_TO_FSB(mp, nrsumsize);
+ nrsumsize = XFS_FSB_TO_B(mp, nrsumblocks);
+ /*
+ * New summary size can't be more than half the size of
+ * the log. This prevents us from getting a log overflow,
+ * since we'll log basically the whole summary file at once.
+ */
+ if (nrsumblocks > (mp->m_sb.sb_logblocks >> 1))
+ return XFS_ERROR(EINVAL);
+ /*
+ * Get the old block counts for bitmap and summary inodes.
+ * These can't change since other growfs callers are locked out.
+ */
+ rbmblocks = XFS_B_TO_FSB(mp, mp->m_rbmip->i_d.di_size);
+ rsumblocks = XFS_B_TO_FSB(mp, mp->m_rsumip->i_d.di_size);
+ /*
+ * Allocate space to the bitmap and summary files, as necessary.
+ */
+ if ((error = xfs_growfs_rt_alloc(mp, rbmblocks, nrbmblocks,
+ mp->m_sb.sb_rbmino)))
+ return error;
+ if ((error = xfs_growfs_rt_alloc(mp, rsumblocks, nrsumblocks,
+ mp->m_sb.sb_rsumino)))
+ return error;
+ nmp = NULL;
+ /*
+ * Loop over the bitmap blocks.
+ * We will do everything one bitmap block at a time.
+ * Skip the current block if it is exactly full.
+ * This also deals with the case where there were no rtextents before.
+ */
+ for (bmbno = sbp->sb_rbmblocks -
+ ((sbp->sb_rextents & ((1 << mp->m_blkbit_log) - 1)) != 0);
+ bmbno < nrbmblocks;
+ bmbno++) {
+ /*
+ * Allocate a new (fake) mount/sb.
+ */
+ nmp = kmem_alloc(sizeof(*nmp), KM_SLEEP);
+ *nmp = *mp;
+ nsbp = &nmp->m_sb;
+ /*
+ * Calculate new sb and mount fields for this round.
+ */
+ nsbp->sb_rextsize = in->extsize;
+ nsbp->sb_rbmblocks = bmbno + 1;
+ nsbp->sb_rblocks =
+ XFS_RTMIN(nrblocks,
+ nsbp->sb_rbmblocks * NBBY *
+ nsbp->sb_blocksize * nsbp->sb_rextsize);
+ nsbp->sb_rextents = nsbp->sb_rblocks;
+ do_div(nsbp->sb_rextents, nsbp->sb_rextsize);
+ nsbp->sb_rextslog = xfs_highbit32(nsbp->sb_rextents);
+ nrsumlevels = nmp->m_rsumlevels = nsbp->sb_rextslog + 1;
+ nrsumsize =
+ (uint)sizeof(xfs_suminfo_t) * nrsumlevels *
+ nsbp->sb_rbmblocks;
+ nrsumblocks = XFS_B_TO_FSB(mp, nrsumsize);
+ nmp->m_rsumsize = nrsumsize = XFS_FSB_TO_B(mp, nrsumblocks);
+ /*
+ * Start a transaction, get the log reservation.
+ */
+ tp = xfs_trans_alloc(mp, XFS_TRANS_GROWFSRT_FREE);
+ cancelflags = 0;
+ if ((error = xfs_trans_reserve(tp, 0,
+ XFS_GROWRTFREE_LOG_RES(nmp), 0, 0, 0)))
+ goto error_exit;
+ /*
+ * Lock out other callers by grabbing the bitmap inode lock.
+ */
+ if ((error = xfs_trans_iget(mp, tp, 0, mp->m_sb.sb_rbmino,
+ XFS_ILOCK_EXCL, &ip)))
+ goto error_exit;
+ ASSERT(ip == mp->m_rbmip);
+ /*
+ * Update the bitmap inode's size.
+ */
+ mp->m_rbmip->i_d.di_size =
+ nsbp->sb_rbmblocks * nsbp->sb_blocksize;
+ xfs_trans_log_inode(tp, mp->m_rbmip, XFS_ILOG_CORE);
+ cancelflags |= XFS_TRANS_ABORT;
+ /*
+ * Get the summary inode into the transaction.
+ */
+ if ((error = xfs_trans_iget(mp, tp, mp->m_sb.sb_rsumino,
+ 0, XFS_ILOCK_EXCL, &ip)))
+ goto error_exit;
+ ASSERT(ip == mp->m_rsumip);
+ /*
+ * Update the summary inode's size.
+ */
+ mp->m_rsumip->i_d.di_size = nmp->m_rsumsize;
+ xfs_trans_log_inode(tp, mp->m_rsumip, XFS_ILOG_CORE);
+ /*
+ * Copy summary data from old to new sizes.
+ * Do this when the real size (not block-aligned) changes.
+ */
+ if (sbp->sb_rbmblocks != nsbp->sb_rbmblocks ||
+ mp->m_rsumlevels != nmp->m_rsumlevels) {
+ error = xfs_rtcopy_summary(mp, nmp, tp);
+ if (error)
+ goto error_exit;
+ }
+ /*
+ * Update superblock fields.
+ */
+ if (nsbp->sb_rextsize != sbp->sb_rextsize)
+ xfs_trans_mod_sb(tp, XFS_TRANS_SB_REXTSIZE,
+ nsbp->sb_rextsize - sbp->sb_rextsize);
+ if (nsbp->sb_rbmblocks != sbp->sb_rbmblocks)
+ xfs_trans_mod_sb(tp, XFS_TRANS_SB_RBMBLOCKS,
+ nsbp->sb_rbmblocks - sbp->sb_rbmblocks);
+ if (nsbp->sb_rblocks != sbp->sb_rblocks)
+ xfs_trans_mod_sb(tp, XFS_TRANS_SB_RBLOCKS,
+ nsbp->sb_rblocks - sbp->sb_rblocks);
+ if (nsbp->sb_rextents != sbp->sb_rextents)
+ xfs_trans_mod_sb(tp, XFS_TRANS_SB_REXTENTS,
+ nsbp->sb_rextents - sbp->sb_rextents);
+ if (nsbp->sb_rextslog != sbp->sb_rextslog)
+ xfs_trans_mod_sb(tp, XFS_TRANS_SB_REXTSLOG,
+ nsbp->sb_rextslog - sbp->sb_rextslog);
+ /*
+ * Free new extent.
+ */
+ bp = NULL;
+ error = xfs_rtfree_range(nmp, tp, sbp->sb_rextents,
+ nsbp->sb_rextents - sbp->sb_rextents, &bp, &sumbno);
+ if (error)
+ goto error_exit;
+ /*
+ * Mark more blocks free in the superblock.
+ */
+ xfs_trans_mod_sb(tp, XFS_TRANS_SB_FREXTENTS,
+ nsbp->sb_rextents - sbp->sb_rextents);
+ /*
+ * Free the fake mp structure.
+ */
+ kmem_free(nmp, sizeof(*nmp));
+ nmp = NULL;
+ /*
+ * Update mp values into the real mp structure.
+ */
+ mp->m_rsumlevels = nrsumlevels;
+ mp->m_rsumsize = nrsumsize;
+ /*
+ * Commit the transaction.
+ */
+ xfs_trans_commit(tp, 0, NULL);
+ }
+ return 0;
+
+ /*
+ * Error paths come here.
+ */
+error_exit:
+ if (nmp)
+ kmem_free(nmp, sizeof(*nmp));
+ xfs_trans_cancel(tp, cancelflags);
+ return error;
+}
+
+/*
+ * Allocate an extent in the realtime subvolume, with the usual allocation
+ * parameters. The length units are all in realtime extents, as is the
+ * result block number.
+ */
+int /* error */
+xfs_rtallocate_extent(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_rtblock_t bno, /* starting block number to allocate */
+ xfs_extlen_t minlen, /* minimum length to allocate */
+ xfs_extlen_t maxlen, /* maximum length to allocate */
+ xfs_extlen_t *len, /* out: actual length allocated */
+ xfs_alloctype_t type, /* allocation type XFS_ALLOCTYPE... */
+ int wasdel, /* was a delayed allocation extent */
+ xfs_extlen_t prod, /* extent product factor */
+ xfs_rtblock_t *rtblock) /* out: start block allocated */
+{
+ int error; /* error value */
+ xfs_inode_t *ip; /* inode for bitmap file */
+ xfs_mount_t *mp; /* file system mount structure */
+ xfs_rtblock_t r; /* result allocated block */
+ xfs_fsblock_t sb; /* summary file block number */
+ xfs_buf_t *sumbp; /* summary file block buffer */
+
+ ASSERT(minlen > 0 && minlen <= maxlen);
+ mp = tp->t_mountp;
+ /*
+ * If prod is set then figure out what to do to minlen and maxlen.
+ */
+ if (prod > 1) {
+ xfs_extlen_t i;
+
+ if ((i = maxlen % prod))
+ maxlen -= i;
+ if ((i = minlen % prod))
+ minlen += prod - i;
+ if (maxlen < minlen) {
+ *rtblock = NULLRTBLOCK;
+ return 0;
+ }
+ }
+ /*
+ * Lock out other callers by grabbing the bitmap inode lock.
+ */
+ error = xfs_trans_iget(mp, tp, mp->m_sb.sb_rbmino, 0, XFS_ILOCK_EXCL, &ip);
+ if (error) {
+ return error;
+ }
+ sumbp = NULL;
+ /*
+ * Allocate by size, or near another block, or exactly at some block.
+ */
+ switch (type) {
+ case XFS_ALLOCTYPE_ANY_AG:
+ error = xfs_rtallocate_extent_size(mp, tp, minlen, maxlen, len,
+ &sumbp, &sb, prod, &r);
+ break;
+ case XFS_ALLOCTYPE_NEAR_BNO:
+ error = xfs_rtallocate_extent_near(mp, tp, bno, minlen, maxlen,
+ len, &sumbp, &sb, prod, &r);
+ break;
+ case XFS_ALLOCTYPE_THIS_BNO:
+ error = xfs_rtallocate_extent_exact(mp, tp, bno, minlen, maxlen,
+ len, &sumbp, &sb, prod, &r);
+ break;
+ default:
+ ASSERT(0);
+ }
+ if (error) {
+ return error;
+ }
+ /*
+ * If it worked, update the superblock.
+ */
+ if (r != NULLRTBLOCK) {
+ long slen = (long)*len;
+
+ ASSERT(*len >= minlen && *len <= maxlen);
+ if (wasdel)
+ xfs_trans_mod_sb(tp, XFS_TRANS_SB_RES_FREXTENTS, -slen);
+ else
+ xfs_trans_mod_sb(tp, XFS_TRANS_SB_FREXTENTS, -slen);
+ }
+ *rtblock = r;
+ return 0;
+}
+
+/*
+ * Free an extent in the realtime subvolume. Length is expressed in
+ * realtime extents, as is the block number.
+ */
+int /* error */
+xfs_rtfree_extent(
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_rtblock_t bno, /* starting block number to free */
+ xfs_extlen_t len) /* length of extent freed */
+{
+ int error; /* error value */
+ xfs_inode_t *ip; /* bitmap file inode */
+ xfs_mount_t *mp; /* file system mount structure */
+ xfs_fsblock_t sb; /* summary file block number */
+ xfs_buf_t *sumbp; /* summary file block buffer */
+
+ mp = tp->t_mountp;
+ /*
+ * Synchronize by locking the bitmap inode.
+ */
+ error = xfs_trans_iget(mp, tp, mp->m_sb.sb_rbmino, 0, XFS_ILOCK_EXCL, &ip);
+ if (error) {
+ return error;
+ }
+#if defined(__KERNEL__) && defined(DEBUG)
+ /*
+ * Check to see that this whole range is currently allocated.
+ */
+ {
+ int stat; /* result from checking range */
+
+ error = xfs_rtcheck_alloc_range(mp, tp, bno, len, &stat);
+ if (error) {
+ return error;
+ }
+ ASSERT(stat);
+ }
+#endif
+ sumbp = NULL;
+ /*
+ * Free the range of realtime blocks.
+ */
+ error = xfs_rtfree_range(mp, tp, bno, len, &sumbp, &sb);
+ if (error) {
+ return error;
+ }
+ /*
+ * Mark more blocks free in the superblock.
+ */
+ xfs_trans_mod_sb(tp, XFS_TRANS_SB_FREXTENTS, (long)len);
+ /*
+ * If we've now freed all the blocks, reset the file sequence
+ * number to 0.
+ */
+ if (tp->t_frextents_delta + mp->m_sb.sb_frextents ==
+ mp->m_sb.sb_rextents) {
+ if (!(ip->i_d.di_flags & XFS_DIFLAG_NEWRTBM))
+ ip->i_d.di_flags |= XFS_DIFLAG_NEWRTBM;
+ *(__uint64_t *)&ip->i_d.di_atime = 0;
+ xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+ }
+ return 0;
+}
+
+/*
+ * Initialize realtime fields in the mount structure.
+ */
+int /* error */
+xfs_rtmount_init(
+ xfs_mount_t *mp) /* file system mount structure */
+{
+ xfs_buf_t *bp; /* buffer for last block of subvolume */
+ xfs_daddr_t d; /* address of last block of subvolume */
+ int error; /* error return value */
+ xfs_sb_t *sbp; /* filesystem superblock copy in mount */
+
+ sbp = &mp->m_sb;
+ if (sbp->sb_rblocks == 0)
+ return 0;
+ if (mp->m_rtdev_targp == NULL) {
+ cmn_err(CE_WARN,
+ "XFS: This filesystem has a realtime volume, use rtdev=device option");
+ return XFS_ERROR(ENODEV);
+ }
+ mp->m_rsumlevels = sbp->sb_rextslog + 1;
+ mp->m_rsumsize =
+ (uint)sizeof(xfs_suminfo_t) * mp->m_rsumlevels *
+ sbp->sb_rbmblocks;
+ mp->m_rsumsize = roundup(mp->m_rsumsize, sbp->sb_blocksize);
+ mp->m_rbmip = mp->m_rsumip = NULL;
+ /*
+ * Check that the realtime section is an ok size.
+ */
+ d = (xfs_daddr_t)XFS_FSB_TO_BB(mp, mp->m_sb.sb_rblocks);
+ if (XFS_BB_TO_FSB(mp, d) != mp->m_sb.sb_rblocks) {
+ cmn_err(CE_WARN, "XFS: realtime mount -- %llu != %llu",
+ (unsigned long long) XFS_BB_TO_FSB(mp, d),
+ (unsigned long long) mp->m_sb.sb_rblocks);
+ return XFS_ERROR(E2BIG);
+ }
+ error = xfs_read_buf(mp, mp->m_rtdev_targp,
+ d - XFS_FSB_TO_BB(mp, 1),
+ XFS_FSB_TO_BB(mp, 1), 0, &bp);
+ if (error) {
+ cmn_err(CE_WARN,
+ "XFS: realtime mount -- xfs_read_buf failed, returned %d", error);
+ if (error == ENOSPC)
+ return XFS_ERROR(E2BIG);
+ return error;
+ }
+ xfs_buf_relse(bp);
+ return 0;
+}
+
+/*
+ * Get the bitmap and summary inodes into the mount structure
+ * at mount time.
+ */
+int /* error */
+xfs_rtmount_inodes(
+ xfs_mount_t *mp) /* file system mount structure */
+{
+ int error; /* error return value */
+ xfs_sb_t *sbp;
+
+ sbp = &mp->m_sb;
+ if (sbp->sb_rbmino == NULLFSINO)
+ return 0;
+ error = xfs_iget(mp, NULL, sbp->sb_rbmino, 0, 0, &mp->m_rbmip, 0);
+ if (error)
+ return error;
+ ASSERT(mp->m_rbmip != NULL);
+ ASSERT(sbp->sb_rsumino != NULLFSINO);
+ error = xfs_iget(mp, NULL, sbp->sb_rsumino, 0, 0, &mp->m_rsumip, 0);
+ if (error) {
+ VN_RELE(XFS_ITOV(mp->m_rbmip));
+ return error;
+ }
+ ASSERT(mp->m_rsumip != NULL);
+ return 0;
+}
+
+/*
+ * Pick an extent for allocation at the start of a new realtime file.
+ * Use the sequence number stored in the atime field of the bitmap inode.
+ * Translate this to a fraction of the rtextents, and return the product
+ * of rtextents and the fraction.
+ * The fraction sequence is 0, 1/2, 1/4, 3/4, 1/8, ..., 7/8, 1/16, ...
+ */
+int /* error */
+xfs_rtpick_extent(
+ xfs_mount_t *mp, /* file system mount point */
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_extlen_t len, /* allocation length (rtextents) */
+ xfs_rtblock_t *pick) /* result rt extent */
+{
+ xfs_rtblock_t b; /* result block */
+ int error; /* error return value */
+ xfs_inode_t *ip; /* bitmap incore inode */
+ int log2; /* log of sequence number */
+ __uint64_t resid; /* residual after log removed */
+ __uint64_t seq; /* sequence number of file creation */
+ __uint64_t *seqp; /* pointer to seqno in inode */
+
+ error = xfs_trans_iget(mp, tp, mp->m_sb.sb_rbmino, 0, XFS_ILOCK_EXCL, &ip);
+ if (error)
+ return error;
+ ASSERT(ip == mp->m_rbmip);
+ seqp = (__uint64_t *)&ip->i_d.di_atime;
+ if (!(ip->i_d.di_flags & XFS_DIFLAG_NEWRTBM)) {
+ ip->i_d.di_flags |= XFS_DIFLAG_NEWRTBM;
+ *seqp = 0;
+ }
+ seq = *seqp;
+ if ((log2 = xfs_highbit64(seq)) == -1)
+ b = 0;
+ else {
+ resid = seq - (1ULL << log2);
+ b = (mp->m_sb.sb_rextents * ((resid << 1) + 1ULL)) >>
+ (log2 + 1);
+ if (b >= mp->m_sb.sb_rextents)
+ b = do_mod(b, mp->m_sb.sb_rextents);
+ if (b + len > mp->m_sb.sb_rextents)
+ b = mp->m_sb.sb_rextents - len;
+ }
+ *seqp = seq + 1;
+ xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+ *pick = b;
+ return 0;
+}
+
+#ifdef DEBUG
+/*
+ * Debug code: print out the value of a range in the bitmap.
+ */
+void
+xfs_rtprint_range(
+ xfs_mount_t *mp, /* file system mount structure */
+ xfs_trans_t *tp, /* transaction pointer */
+ xfs_rtblock_t start, /* starting block to print */
+ xfs_extlen_t len) /* length to print */
+{
+ xfs_extlen_t i; /* block number in the extent */
+
+ printk("%Ld: ", (long long)start);
+ for (i = 0; i < len; i++)
+ printk("%d", xfs_rtcheck_bit(mp, tp, start + i, 1));
+ printk("\n");
+}
+
+/*
+ * Debug code: print the summary file.
+ */
+void
+xfs_rtprint_summary(
+ xfs_mount_t *mp, /* file system mount structure */
+ xfs_trans_t *tp) /* transaction pointer */
+{
+ xfs_suminfo_t c; /* summary data */
+ xfs_rtblock_t i; /* bitmap block number */
+ int l; /* summary information level */
+ int p; /* flag for printed anything */
+ xfs_fsblock_t sb; /* summary block number */
+ xfs_buf_t *sumbp; /* summary block buffer */
+
+ sumbp = NULL;
+ for (l = 0; l < mp->m_rsumlevels; l++) {
+ for (p = 0, i = 0; i < mp->m_sb.sb_rbmblocks; i++) {
+ (void)xfs_rtget_summary(mp, tp, l, i, &sumbp, &sb, &c);
+ if (c) {
+ if (!p) {
+ printk("%Ld-%Ld:", 1LL << l,
+ XFS_RTMIN((1LL << l) +
+ ((1LL << l) - 1LL),
+ mp->m_sb.sb_rextents));
+ p = 1;
+ }
+ printk(" %Ld:%d", (long long)i, c);
+ }
+ }
+ if (p)
+ printk("\n");
+ }
+ if (sumbp)
+ xfs_trans_brelse(tp, sumbp);
+}
+#endif /* DEBUG */
diff --git a/fs/xfs/xfs_rtalloc.h b/fs/xfs/xfs_rtalloc.h
new file mode 100644
index 00000000000000..e2710264c0544a
--- /dev/null
+++ b/fs/xfs/xfs_rtalloc.h
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_RTALLOC_H__
+#define __XFS_RTALLOC_H__
+
+struct xfs_mount;
+struct xfs_trans;
+
+#define XFS_IS_REALTIME_INODE(ip) ((ip)->i_d.di_flags & XFS_DIFLAG_REALTIME)
+
+/* Min and max rt extent sizes, specified in bytes */
+#define XFS_MAX_RTEXTSIZE (1024 * 1024 * 1024) /* 1GB */
+#define XFS_DFL_RTEXTSIZE (64 * 1024) /* 64KB */
+#define XFS_MIN_RTEXTSIZE (4 * 1024) /* 4KB */
+
+/*
+ * Constants for bit manipulations.
+ */
+#define XFS_NBBYLOG 3 /* log2(NBBY) */
+#define XFS_WORDLOG 2 /* log2(sizeof(xfs_rtword_t)) */
+#define XFS_NBWORDLOG (XFS_NBBYLOG + XFS_WORDLOG)
+#define XFS_NBWORD (1 << XFS_NBWORDLOG)
+#define XFS_WORDMASK ((1 << XFS_WORDLOG) - 1)
+
+#define XFS_BLOCKSIZE(mp) ((mp)->m_sb.sb_blocksize)
+#define XFS_BLOCKMASK(mp) ((mp)->m_blockmask)
+#define XFS_BLOCKWSIZE(mp) ((mp)->m_blockwsize)
+#define XFS_BLOCKWMASK(mp) ((mp)->m_blockwmask)
+
+/*
+ * Summary and bit manipulation macros.
+ */
+#define XFS_SUMOFFS(mp,ls,bb) ((int)((ls) * (mp)->m_sb.sb_rbmblocks + (bb)))
+#define XFS_SUMOFFSTOBLOCK(mp,s) \
+ (((s) * (uint)sizeof(xfs_suminfo_t)) >> (mp)->m_sb.sb_blocklog)
+#define XFS_SUMPTR(mp,bp,so) \
+ ((xfs_suminfo_t *)((char *)XFS_BUF_PTR(bp) + \
+ (((so) * (uint)sizeof(xfs_suminfo_t)) & XFS_BLOCKMASK(mp))))
+
+#define XFS_BITTOBLOCK(mp,bi) ((bi) >> (mp)->m_blkbit_log)
+#define XFS_BLOCKTOBIT(mp,bb) ((bb) << (mp)->m_blkbit_log)
+#define XFS_BITTOWORD(mp,bi) \
+ ((int)(((bi) >> XFS_NBWORDLOG) & XFS_BLOCKWMASK(mp)))
+
+#define XFS_RTMIN(a,b) ((a) < (b) ? (a) : (b))
+#define XFS_RTMAX(a,b) ((a) > (b) ? (a) : (b))
+
+#define XFS_RTLOBIT(w) xfs_lowbit32(w)
+#define XFS_RTHIBIT(w) xfs_highbit32(w)
+
+#if XFS_BIG_BLKNOS
+#define XFS_RTBLOCKLOG(b) xfs_highbit64(b)
+#else
+#define XFS_RTBLOCKLOG(b) xfs_highbit32(b)
+#endif
+
+
+#ifdef __KERNEL__
+
+#ifdef CONFIG_XFS_RT
+/*
+ * Function prototypes for exported functions.
+ */
+
+/*
+ * Allocate an extent in the realtime subvolume, with the usual allocation
+ * parameters. The length units are all in realtime extents, as is the
+ * result block number.
+ */
+int /* error */
+xfs_rtallocate_extent(
+ struct xfs_trans *tp, /* transaction pointer */
+ xfs_rtblock_t bno, /* starting block number to allocate */
+ xfs_extlen_t minlen, /* minimum length to allocate */
+ xfs_extlen_t maxlen, /* maximum length to allocate */
+ xfs_extlen_t *len, /* out: actual length allocated */
+ xfs_alloctype_t type, /* allocation type XFS_ALLOCTYPE... */
+ int wasdel, /* was a delayed allocation extent */
+ xfs_extlen_t prod, /* extent product factor */
+ xfs_rtblock_t *rtblock); /* out: start block allocated */
+
+/*
+ * Free an extent in the realtime subvolume. Length is expressed in
+ * realtime extents, as is the block number.
+ */
+int /* error */
+xfs_rtfree_extent(
+ struct xfs_trans *tp, /* transaction pointer */
+ xfs_rtblock_t bno, /* starting block number to free */
+ xfs_extlen_t len); /* length of extent freed */
+
+/*
+ * Initialize realtime fields in the mount structure.
+ */
+int /* error */
+xfs_rtmount_init(
+ struct xfs_mount *mp); /* file system mount structure */
+
+/*
+ * Get the bitmap and summary inodes into the mount structure
+ * at mount time.
+ */
+int /* error */
+xfs_rtmount_inodes(
+ struct xfs_mount *mp); /* file system mount structure */
+
+/*
+ * Pick an extent for allocation at the start of a new realtime file.
+ * Use the sequence number stored in the atime field of the bitmap inode.
+ * Translate this to a fraction of the rtextents, and return the product
+ * of rtextents and the fraction.
+ * The fraction sequence is 0, 1/2, 1/4, 3/4, 1/8, ..., 7/8, 1/16, ...
+ */
+int /* error */
+xfs_rtpick_extent(
+ struct xfs_mount *mp, /* file system mount point */
+ struct xfs_trans *tp, /* transaction pointer */
+ xfs_extlen_t len, /* allocation length (rtextents) */
+ xfs_rtblock_t *pick); /* result rt extent */
+
+/*
+ * Debug code: print out the value of a range in the bitmap.
+ */
+void
+xfs_rtprint_range(
+ struct xfs_mount *mp, /* file system mount structure */
+ struct xfs_trans *tp, /* transaction pointer */
+ xfs_rtblock_t start, /* starting block to print */
+ xfs_extlen_t len); /* length to print */
+
+/*
+ * Debug code: print the summary file.
+ */
+void
+xfs_rtprint_summary(
+ struct xfs_mount *mp, /* file system mount structure */
+ struct xfs_trans *tp); /* transaction pointer */
+
+/*
+ * Grow the realtime area of the filesystem.
+ */
+int
+xfs_growfs_rt(
+ struct xfs_mount *mp, /* file system mount structure */
+ xfs_growfs_rt_t *in); /* user supplied growfs struct */
+
+#else
+# define xfs_rtallocate_extent(t,b,min,max,l,a,f,p,rb) (ENOSYS)
+# define xfs_rtfree_extent(t,b,l) (ENOSYS)
+# define xfs_rtpick_extent(m,t,l,rb) (ENOSYS)
+# define xfs_growfs_rt(mp,in) (ENOSYS)
+# define xfs_rtmount_init(m) (((mp)->m_sb.sb_rblocks == 0)? 0 : (ENOSYS))
+# define xfs_rtmount_inodes(m) (((mp)->m_sb.sb_rblocks == 0)? 0 : (ENOSYS))
+#endif /* CONFIG_XFS_RT */
+
+#endif /* __KERNEL__ */
+
+#endif /* __XFS_RTALLOC_H__ */
diff --git a/fs/xfs/xfs_rw.c b/fs/xfs/xfs_rw.c
new file mode 100644
index 00000000000000..d3ff7aef33ba1f
--- /dev/null
+++ b/fs/xfs/xfs_rw.c
@@ -0,0 +1,356 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_itable.h"
+#include "xfs_btree.h"
+#include "xfs_alloc.h"
+#include "xfs_ialloc.h"
+#include "xfs_attr.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode_item.h"
+#include "xfs_inode.h"
+#include "xfs_bmap.h"
+#include "xfs_acl.h"
+#include "xfs_mac.h"
+#include "xfs_error.h"
+#include "xfs_buf_item.h"
+#include "xfs_rw.h"
+
+/*
+ * This is a subroutine for xfs_write() and other writers (xfs_ioctl)
+ * which clears the setuid and setgid bits when a file is written.
+ */
+int
+xfs_write_clear_setuid(
+ xfs_inode_t *ip)
+{
+ xfs_mount_t *mp;
+ xfs_trans_t *tp;
+ int error;
+
+ mp = ip->i_mount;
+ tp = xfs_trans_alloc(mp, XFS_TRANS_WRITEID);
+ if ((error = xfs_trans_reserve(tp, 0,
+ XFS_WRITEID_LOG_RES(mp),
+ 0, 0, 0))) {
+ xfs_trans_cancel(tp, 0);
+ return error;
+ }
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
+ xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
+ xfs_trans_ihold(tp, ip);
+ ip->i_d.di_mode &= ~S_ISUID;
+
+ /*
+ * Note that we don't have to worry about mandatory
+ * file locking being disabled here because we only
+ * clear the S_ISGID bit if the Group execute bit is
+ * on, but if it was on then mandatory locking wouldn't
+ * have been enabled.
+ */
+ if (ip->i_d.di_mode & S_IXGRP) {
+ ip->i_d.di_mode &= ~S_ISGID;
+ }
+ xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+ xfs_trans_set_sync(tp);
+ error = xfs_trans_commit(tp, 0, NULL);
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ return 0;
+}
+
+/*
+ * Force a shutdown of the filesystem instantly while keeping
+ * the filesystem consistent. We don't do an unmount here; just shutdown
+ * the shop, make sure that absolutely nothing persistent happens to
+ * this filesystem after this point.
+ */
+
+void
+xfs_do_force_shutdown(
+ bhv_desc_t *bdp,
+ int flags,
+ char *fname,
+ int lnnum)
+{
+ int logerror;
+ xfs_mount_t *mp;
+
+ mp = XFS_BHVTOM(bdp);
+ logerror = flags & XFS_LOG_IO_ERROR;
+
+ if (!(flags & XFS_FORCE_UMOUNT)) {
+ cmn_err(CE_NOTE,
+ "xfs_force_shutdown(%s,0x%x) called from line %d of file %s. Return address = 0x%p",
+ mp->m_fsname,flags,lnnum,fname,__return_address);
+ }
+ /*
+ * No need to duplicate efforts.
+ */
+ if (XFS_FORCED_SHUTDOWN(mp) && !logerror)
+ return;
+
+ /*
+ * This flags XFS_MOUNT_FS_SHUTDOWN, makes sure that we don't
+ * queue up anybody new on the log reservations, and wakes up
+ * everybody who's sleeping on log reservations and tells
+ * them the bad news.
+ */
+ if (xfs_log_force_umount(mp, logerror))
+ return;
+
+ if (flags & XFS_CORRUPT_INCORE) {
+ xfs_cmn_err(XFS_PTAG_SHUTDOWN_CORRUPT, CE_ALERT, mp,
+ "Corruption of in-memory data detected. Shutting down filesystem: %s",
+ mp->m_fsname);
+ if (XFS_ERRLEVEL_HIGH <= xfs_error_level) {
+ xfs_stack_trace();
+ }
+ } else if (!(flags & XFS_FORCE_UMOUNT)) {
+ if (logerror) {
+ xfs_cmn_err(XFS_PTAG_SHUTDOWN_LOGERROR, CE_ALERT, mp,
+ "Log I/O Error Detected. Shutting down filesystem: %s",
+ mp->m_fsname);
+ } else if (!(flags & XFS_SHUTDOWN_REMOTE_REQ)) {
+ xfs_cmn_err(XFS_PTAG_SHUTDOWN_IOERROR, CE_ALERT, mp,
+ "I/O Error Detected. Shutting down filesystem: %s",
+ mp->m_fsname);
+ }
+ }
+ if (!(flags & XFS_FORCE_UMOUNT)) {
+ cmn_err(CE_ALERT,
+ "Please umount the filesystem, and rectify the problem(s)");
+ }
+}
+
+
+/*
+ * Called when we want to stop a buffer from getting written or read.
+ * We attach the EIO error, muck with its flags, and call biodone
+ * so that the proper iodone callbacks get called.
+ */
+int
+xfs_bioerror(
+ xfs_buf_t *bp)
+{
+
+#ifdef XFSERRORDEBUG
+ ASSERT(XFS_BUF_ISREAD(bp) || bp->b_iodone);
+#endif
+
+ /*
+ * No need to wait until the buffer is unpinned.
+ * We aren't flushing it.
+ */
+ xfs_buftrace("XFS IOERROR", bp);
+ XFS_BUF_ERROR(bp, EIO);
+ /*
+ * We're calling biodone, so delete B_DONE flag. Either way
+ * we have to call the iodone callback, and calling biodone
+ * probably is the best way since it takes care of
+ * GRIO as well.
+ */
+ XFS_BUF_UNREAD(bp);
+ XFS_BUF_UNDELAYWRITE(bp);
+ XFS_BUF_UNDONE(bp);
+ XFS_BUF_STALE(bp);
+
+ XFS_BUF_CLR_BDSTRAT_FUNC(bp);
+ xfs_biodone(bp);
+
+ return (EIO);
+}
+
+/*
+ * Same as xfs_bioerror, except that we are releasing the buffer
+ * here ourselves, and avoiding the biodone call.
+ * This is meant for userdata errors; metadata bufs come with
+ * iodone functions attached, so that we can track down errors.
+ */
+int
+xfs_bioerror_relse(
+ xfs_buf_t *bp)
+{
+ int64_t fl;
+
+ ASSERT(XFS_BUF_IODONE_FUNC(bp) != xfs_buf_iodone_callbacks);
+ ASSERT(XFS_BUF_IODONE_FUNC(bp) != xlog_iodone);
+
+ xfs_buftrace("XFS IOERRELSE", bp);
+ fl = XFS_BUF_BFLAGS(bp);
+ /*
+ * No need to wait until the buffer is unpinned.
+ * We aren't flushing it.
+ *
+ * chunkhold expects B_DONE to be set, whether
+ * we actually finish the I/O or not. We don't want to
+ * change that interface.
+ */
+ XFS_BUF_UNREAD(bp);
+ XFS_BUF_UNDELAYWRITE(bp);
+ XFS_BUF_DONE(bp);
+ XFS_BUF_STALE(bp);
+ XFS_BUF_CLR_IODONE_FUNC(bp);
+ XFS_BUF_CLR_BDSTRAT_FUNC(bp);
+ if (!(fl & XFS_B_ASYNC)) {
+ /*
+ * Mark b_error and B_ERROR _both_.
+ * Lot's of chunkcache code assumes that.
+ * There's no reason to mark error for
+ * ASYNC buffers.
+ */
+ XFS_BUF_ERROR(bp, EIO);
+ XFS_BUF_V_IODONESEMA(bp);
+ } else {
+ xfs_buf_relse(bp);
+ }
+ return (EIO);
+}
+/*
+ * Prints out an ALERT message about I/O error.
+ */
+void
+xfs_ioerror_alert(
+ char *func,
+ struct xfs_mount *mp,
+ xfs_buf_t *bp,
+ xfs_daddr_t blkno)
+{
+ cmn_err(CE_ALERT,
+ "I/O error in filesystem (\"%s\") meta-data dev %s block 0x%llx"
+ " (\"%s\") error %d buf count %u",
+ (!mp || !mp->m_fsname) ? "(fs name not set)" : mp->m_fsname,
+ XFS_BUFTARG_NAME(bp->pb_target),
+ (__uint64_t)blkno,
+ func,
+ XFS_BUF_GETERROR(bp),
+ XFS_BUF_COUNT(bp));
+}
+
+/*
+ * This isn't an absolute requirement, but it is
+ * just a good idea to call xfs_read_buf instead of
+ * directly doing a read_buf call. For one, we shouldn't
+ * be doing this disk read if we are in SHUTDOWN state anyway,
+ * so this stops that from happening. Secondly, this does all
+ * the error checking stuff and the brelse if appropriate for
+ * the caller, so the code can be a little leaner.
+ */
+
+int
+xfs_read_buf(
+ struct xfs_mount *mp,
+ xfs_buftarg_t *target,
+ xfs_daddr_t blkno,
+ int len,
+ uint flags,
+ xfs_buf_t **bpp)
+{
+ xfs_buf_t *bp;
+ int error;
+
+ if (flags)
+ bp = xfs_buf_read_flags(target, blkno, len, flags);
+ else
+ bp = xfs_buf_read(target, blkno, len, flags);
+ if (!bp)
+ return XFS_ERROR(EIO);
+ error = XFS_BUF_GETERROR(bp);
+ if (bp && !error && !XFS_FORCED_SHUTDOWN(mp)) {
+ *bpp = bp;
+ } else {
+ *bpp = NULL;
+ if (error) {
+ xfs_ioerror_alert("xfs_read_buf", mp, bp, XFS_BUF_ADDR(bp));
+ } else {
+ error = XFS_ERROR(EIO);
+ }
+ if (bp) {
+ XFS_BUF_UNDONE(bp);
+ XFS_BUF_UNDELAYWRITE(bp);
+ XFS_BUF_STALE(bp);
+ /*
+ * brelse clears B_ERROR and b_error
+ */
+ xfs_buf_relse(bp);
+ }
+ }
+ return (error);
+}
+
+/*
+ * Wrapper around bwrite() so that we can trap
+ * write errors, and act accordingly.
+ */
+int
+xfs_bwrite(
+ struct xfs_mount *mp,
+ struct xfs_buf *bp)
+{
+ int error;
+
+ /*
+ * XXXsup how does this work for quotas.
+ */
+ XFS_BUF_SET_BDSTRAT_FUNC(bp, xfs_bdstrat_cb);
+ XFS_BUF_SET_FSPRIVATE3(bp, mp);
+ XFS_BUF_WRITE(bp);
+
+ if ((error = XFS_bwrite(bp))) {
+ ASSERT(mp);
+ /*
+ * Cannot put a buftrace here since if the buffer is not
+ * B_HOLD then we will brelse() the buffer before returning
+ * from bwrite and we could be tracing a buffer that has
+ * been reused.
+ */
+ xfs_force_shutdown(mp, XFS_METADATA_IO_ERROR);
+ }
+ return (error);
+}
diff --git a/fs/xfs/xfs_rw.h b/fs/xfs/xfs_rw.h
new file mode 100644
index 00000000000000..c8b10bf8f5307a
--- /dev/null
+++ b/fs/xfs/xfs_rw.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_RW_H__
+#define __XFS_RW_H__
+
+struct xfs_buf;
+struct xfs_inode;
+struct xfs_mount;
+
+/*
+ * Maximum count of bmaps used by read and write paths.
+ */
+#define XFS_MAX_RW_NBMAPS 4
+
+/*
+ * Counts of readahead buffers to use based on physical memory size.
+ * None of these should be more than XFS_MAX_RW_NBMAPS.
+ */
+#define XFS_RW_NREADAHEAD_16MB 2
+#define XFS_RW_NREADAHEAD_32MB 3
+#define XFS_RW_NREADAHEAD_K32 4
+#define XFS_RW_NREADAHEAD_K64 4
+
+/*
+ * Maximum size of a buffer that we\'ll map. Making this
+ * too big will degrade performance due to the number of
+ * pages which need to be gathered. Making it too small
+ * will prevent us from doing large I/O\'s to hardware that
+ * needs it.
+ *
+ * This is currently set to 512 KB.
+ */
+#define XFS_MAX_BMAP_LEN_BB 1024
+#define XFS_MAX_BMAP_LEN_BYTES 524288
+
+/*
+ * Convert the given file system block to a disk block.
+ * We have to treat it differently based on whether the
+ * file is a real time file or not, because the bmap code
+ * does.
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_FSB_TO_DB)
+xfs_daddr_t xfs_fsb_to_db(struct xfs_inode *ip, xfs_fsblock_t fsb);
+#define XFS_FSB_TO_DB(ip,fsb) xfs_fsb_to_db(ip,fsb)
+#else
+#define XFS_FSB_TO_DB(ip,fsb) \
+ (((ip)->i_d.di_flags & XFS_DIFLAG_REALTIME) ? \
+ (xfs_daddr_t)XFS_FSB_TO_BB((ip)->i_mount, (fsb)) : \
+ XFS_FSB_TO_DADDR((ip)->i_mount, (fsb)))
+#endif
+
+#define XFS_FSB_TO_DB_IO(io,fsb) \
+ (((io)->io_flags & XFS_IOCORE_RT) ? \
+ XFS_FSB_TO_BB((io)->io_mount, (fsb)) : \
+ XFS_FSB_TO_DADDR((io)->io_mount, (fsb)))
+
+/*
+ * Prototypes for functions in xfs_rw.c.
+ */
+
+int
+xfs_write_clear_setuid(
+ struct xfs_inode *ip);
+
+int
+xfs_bwrite(
+ struct xfs_mount *mp,
+ struct xfs_buf *bp);
+
+int
+xfs_bioerror(
+ struct xfs_buf *b);
+
+int
+xfs_bioerror_relse(
+ struct xfs_buf *b);
+
+int
+xfs_read_buf(
+ struct xfs_mount *mp,
+ xfs_buftarg_t *target,
+ xfs_daddr_t blkno,
+ int len,
+ uint flags,
+ struct xfs_buf **bpp);
+
+void
+xfs_ioerror_alert(
+ char *func,
+ struct xfs_mount *mp,
+ xfs_buf_t *bp,
+ xfs_daddr_t blkno);
+
+
+/*
+ * Prototypes for functions in xfs_vnodeops.c.
+ */
+
+int
+xfs_rwlock(
+ bhv_desc_t *bdp,
+ vrwlock_t write_lock);
+
+void
+xfs_rwunlock(
+ bhv_desc_t *bdp,
+ vrwlock_t write_lock);
+
+int
+xfs_change_file_space(
+ bhv_desc_t *bdp,
+ int cmd,
+ xfs_flock64_t *bf,
+ xfs_off_t offset,
+ cred_t *credp,
+ int flags);
+
+int
+xfs_set_dmattrs(
+ bhv_desc_t *bdp,
+ u_int evmask,
+ u_int16_t state,
+ cred_t *credp);
+
+#endif /* __XFS_RW_H__ */
diff --git a/fs/xfs/xfs_sb.h b/fs/xfs/xfs_sb.h
new file mode 100644
index 00000000000000..ad090a834ced69
--- /dev/null
+++ b/fs/xfs/xfs_sb.h
@@ -0,0 +1,583 @@
+/*
+ * Copyright (c) 2000-2001 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_SB_H__
+#define __XFS_SB_H__
+
+/*
+ * Super block
+ * Fits into a sector-sized buffer at address 0 of each allocation group.
+ * Only the first of these is ever updated except during growfs.
+ */
+
+struct xfs_buf;
+struct xfs_mount;
+
+#define XFS_SB_MAGIC 0x58465342 /* 'XFSB' */
+#define XFS_SB_VERSION_1 1 /* 5.3, 6.0.1, 6.1 */
+#define XFS_SB_VERSION_2 2 /* 6.2 - attributes */
+#define XFS_SB_VERSION_3 3 /* 6.2 - new inode version */
+#define XFS_SB_VERSION_4 4 /* 6.2+ - bitmask version */
+#define XFS_SB_VERSION_NUMBITS 0x000f
+#define XFS_SB_VERSION_ALLFBITS 0xfff0
+#define XFS_SB_VERSION_SASHFBITS 0xf000
+#define XFS_SB_VERSION_REALFBITS 0x0ff0
+#define XFS_SB_VERSION_ATTRBIT 0x0010
+#define XFS_SB_VERSION_NLINKBIT 0x0020
+#define XFS_SB_VERSION_QUOTABIT 0x0040
+#define XFS_SB_VERSION_ALIGNBIT 0x0080
+#define XFS_SB_VERSION_DALIGNBIT 0x0100
+#define XFS_SB_VERSION_SHAREDBIT 0x0200
+#define XFS_SB_VERSION_LOGV2BIT 0x0400
+#define XFS_SB_VERSION_SECTORBIT 0x0800
+#define XFS_SB_VERSION_EXTFLGBIT 0x1000
+#define XFS_SB_VERSION_DIRV2BIT 0x2000
+#define XFS_SB_VERSION_MOREBITSBIT 0x8000
+#define XFS_SB_VERSION_OKSASHFBITS \
+ (XFS_SB_VERSION_EXTFLGBIT | \
+ XFS_SB_VERSION_DIRV2BIT)
+#define XFS_SB_VERSION_OKREALFBITS \
+ (XFS_SB_VERSION_ATTRBIT | \
+ XFS_SB_VERSION_NLINKBIT | \
+ XFS_SB_VERSION_QUOTABIT | \
+ XFS_SB_VERSION_ALIGNBIT | \
+ XFS_SB_VERSION_DALIGNBIT | \
+ XFS_SB_VERSION_SHAREDBIT | \
+ XFS_SB_VERSION_LOGV2BIT | \
+ XFS_SB_VERSION_SECTORBIT)
+#define XFS_SB_VERSION_OKSASHBITS \
+ (XFS_SB_VERSION_NUMBITS | \
+ XFS_SB_VERSION_REALFBITS | \
+ XFS_SB_VERSION_OKSASHFBITS)
+#define XFS_SB_VERSION_OKREALBITS \
+ (XFS_SB_VERSION_NUMBITS | \
+ XFS_SB_VERSION_OKREALFBITS | \
+ XFS_SB_VERSION_OKSASHFBITS)
+#define XFS_SB_VERSION_MKFS(ia,dia,extflag,dirv2,na,sflag,morebits) \
+ (((ia) || (dia) || (extflag) || (dirv2) || (na) || (sflag) || \
+ (morebits)) ? \
+ (XFS_SB_VERSION_4 | \
+ ((ia) ? XFS_SB_VERSION_ALIGNBIT : 0) | \
+ ((dia) ? XFS_SB_VERSION_DALIGNBIT : 0) | \
+ ((extflag) ? XFS_SB_VERSION_EXTFLGBIT : 0) | \
+ ((dirv2) ? XFS_SB_VERSION_DIRV2BIT : 0) | \
+ ((na) ? XFS_SB_VERSION_LOGV2BIT : 0) | \
+ ((sflag) ? XFS_SB_VERSION_SECTORBIT : 0) | \
+ ((morebits) ? XFS_SB_VERSION_MOREBITSBIT : 0)) : \
+ XFS_SB_VERSION_1)
+
+/*
+ * There are two words to hold XFS "feature" bits: the original
+ * word, sb_versionnum, and sb_features2. Whenever a bit is set in
+ * sb_features2, the feature bit XFS_SB_VERSION_MOREBITSBIT must be set.
+ *
+ * These defines represent bits in sb_features2.
+ */
+#define XFS_SB_VERSION2_REALFBITS 0x00ffffff /* Mask: features */
+#define XFS_SB_VERSION2_RESERVED1BIT 0x00000001
+#define XFS_SB_VERSION2_SASHFBITS 0xff000000 /* Mask: features that
+ require changing
+ PROM and SASH */
+
+#define XFS_SB_VERSION2_OKREALFBITS \
+ (0)
+#define XFS_SB_VERSION2_OKSASHFBITS \
+ (0)
+#define XFS_SB_VERSION2_OKREALBITS \
+ (XFS_SB_VERSION2_OKREALFBITS | \
+ XFS_SB_VERSION2_OKSASHFBITS )
+
+/*
+ * mkfs macro to set up sb_features2 word
+ */
+#define XFS_SB_VERSION2_MKFS(xyz) \
+ ((xyz) ? 0 : 0)
+
+typedef struct xfs_sb
+{
+ __uint32_t sb_magicnum; /* magic number == XFS_SB_MAGIC */
+ __uint32_t sb_blocksize; /* logical block size, bytes */
+ xfs_drfsbno_t sb_dblocks; /* number of data blocks */
+ xfs_drfsbno_t sb_rblocks; /* number of realtime blocks */
+ xfs_drtbno_t sb_rextents; /* number of realtime extents */
+ uuid_t sb_uuid; /* file system unique id */
+ xfs_dfsbno_t sb_logstart; /* starting block of log if internal */
+ xfs_ino_t sb_rootino; /* root inode number */
+ xfs_ino_t sb_rbmino; /* bitmap inode for realtime extents */
+ xfs_ino_t sb_rsumino; /* summary inode for rt bitmap */
+ xfs_agblock_t sb_rextsize; /* realtime extent size, blocks */
+ xfs_agblock_t sb_agblocks; /* size of an allocation group */
+ xfs_agnumber_t sb_agcount; /* number of allocation groups */
+ xfs_extlen_t sb_rbmblocks; /* number of rt bitmap blocks */
+ xfs_extlen_t sb_logblocks; /* number of log blocks */
+ __uint16_t sb_versionnum; /* header version == XFS_SB_VERSION */
+ __uint16_t sb_sectsize; /* volume sector size, bytes */
+ __uint16_t sb_inodesize; /* inode size, bytes */
+ __uint16_t sb_inopblock; /* inodes per block */
+ char sb_fname[12]; /* file system name */
+ __uint8_t sb_blocklog; /* log2 of sb_blocksize */
+ __uint8_t sb_sectlog; /* log2 of sb_sectsize */
+ __uint8_t sb_inodelog; /* log2 of sb_inodesize */
+ __uint8_t sb_inopblog; /* log2 of sb_inopblock */
+ __uint8_t sb_agblklog; /* log2 of sb_agblocks (rounded up) */
+ __uint8_t sb_rextslog; /* log2 of sb_rextents */
+ __uint8_t sb_inprogress; /* mkfs is in progress, don't mount */
+ __uint8_t sb_imax_pct; /* max % of fs for inode space */
+ /* statistics */
+ /*
+ * These fields must remain contiguous. If you really
+ * want to change their layout, make sure you fix the
+ * code in xfs_trans_apply_sb_deltas().
+ */
+ __uint64_t sb_icount; /* allocated inodes */
+ __uint64_t sb_ifree; /* free inodes */
+ __uint64_t sb_fdblocks; /* free data blocks */
+ __uint64_t sb_frextents; /* free realtime extents */
+ /*
+ * End contiguous fields.
+ */
+ xfs_ino_t sb_uquotino; /* user quota inode */
+ xfs_ino_t sb_gquotino; /* group quota inode */
+ __uint16_t sb_qflags; /* quota flags */
+ __uint8_t sb_flags; /* misc. flags */
+ __uint8_t sb_shared_vn; /* shared version number */
+ xfs_extlen_t sb_inoalignmt; /* inode chunk alignment, fsblocks */
+ __uint32_t sb_unit; /* stripe or raid unit */
+ __uint32_t sb_width; /* stripe or raid width */
+ __uint8_t sb_dirblklog; /* log2 of dir block size (fsbs) */
+ __uint8_t sb_logsectlog; /* log2 of the log sector size */
+ __uint16_t sb_logsectsize; /* sector size for the log, bytes */
+ __uint32_t sb_logsunit; /* stripe unit size for the log */
+ __uint32_t sb_features2; /* additonal feature bits */
+} xfs_sb_t;
+
+/*
+ * Sequence number values for the fields.
+ */
+typedef enum {
+ XFS_SBS_MAGICNUM, XFS_SBS_BLOCKSIZE, XFS_SBS_DBLOCKS, XFS_SBS_RBLOCKS,
+ XFS_SBS_REXTENTS, XFS_SBS_UUID, XFS_SBS_LOGSTART, XFS_SBS_ROOTINO,
+ XFS_SBS_RBMINO, XFS_SBS_RSUMINO, XFS_SBS_REXTSIZE, XFS_SBS_AGBLOCKS,
+ XFS_SBS_AGCOUNT, XFS_SBS_RBMBLOCKS, XFS_SBS_LOGBLOCKS,
+ XFS_SBS_VERSIONNUM, XFS_SBS_SECTSIZE, XFS_SBS_INODESIZE,
+ XFS_SBS_INOPBLOCK, XFS_SBS_FNAME, XFS_SBS_BLOCKLOG,
+ XFS_SBS_SECTLOG, XFS_SBS_INODELOG, XFS_SBS_INOPBLOG, XFS_SBS_AGBLKLOG,
+ XFS_SBS_REXTSLOG, XFS_SBS_INPROGRESS, XFS_SBS_IMAX_PCT, XFS_SBS_ICOUNT,
+ XFS_SBS_IFREE, XFS_SBS_FDBLOCKS, XFS_SBS_FREXTENTS, XFS_SBS_UQUOTINO,
+ XFS_SBS_GQUOTINO, XFS_SBS_QFLAGS, XFS_SBS_FLAGS, XFS_SBS_SHARED_VN,
+ XFS_SBS_INOALIGNMT, XFS_SBS_UNIT, XFS_SBS_WIDTH, XFS_SBS_DIRBLKLOG,
+ XFS_SBS_LOGSECTLOG, XFS_SBS_LOGSECTSIZE, XFS_SBS_LOGSUNIT,
+ XFS_SBS_FEATURES2,
+ XFS_SBS_FIELDCOUNT
+} xfs_sb_field_t;
+
+/*
+ * Mask values, defined based on the xfs_sb_field_t values.
+ * Only define the ones we're using.
+ */
+#define XFS_SB_MVAL(x) (1LL << XFS_SBS_ ## x)
+#define XFS_SB_UUID XFS_SB_MVAL(UUID)
+#define XFS_SB_FNAME XFS_SB_MVAL(FNAME)
+#define XFS_SB_ROOTINO XFS_SB_MVAL(ROOTINO)
+#define XFS_SB_RBMINO XFS_SB_MVAL(RBMINO)
+#define XFS_SB_RSUMINO XFS_SB_MVAL(RSUMINO)
+#define XFS_SB_VERSIONNUM XFS_SB_MVAL(VERSIONNUM)
+#define XFS_SB_UQUOTINO XFS_SB_MVAL(UQUOTINO)
+#define XFS_SB_GQUOTINO XFS_SB_MVAL(GQUOTINO)
+#define XFS_SB_QFLAGS XFS_SB_MVAL(QFLAGS)
+#define XFS_SB_SHARED_VN XFS_SB_MVAL(SHARED_VN)
+#define XFS_SB_UNIT XFS_SB_MVAL(UNIT)
+#define XFS_SB_WIDTH XFS_SB_MVAL(WIDTH)
+#define XFS_SB_NUM_BITS ((int)XFS_SBS_FIELDCOUNT)
+#define XFS_SB_ALL_BITS ((1LL << XFS_SB_NUM_BITS) - 1)
+#define XFS_SB_MOD_BITS \
+ (XFS_SB_UUID | XFS_SB_ROOTINO | XFS_SB_RBMINO | XFS_SB_RSUMINO | \
+ XFS_SB_VERSIONNUM | XFS_SB_UQUOTINO | XFS_SB_GQUOTINO | \
+ XFS_SB_QFLAGS | XFS_SB_SHARED_VN | XFS_SB_UNIT | XFS_SB_WIDTH)
+
+/*
+ * Misc. Flags - warning - these will be cleared by xfs_repair unless
+ * a feature bit is set when the flag is used.
+ */
+#define XFS_SBF_NOFLAGS 0x00 /* no flags set */
+#define XFS_SBF_READONLY 0x01 /* only read-only mounts allowed */
+
+/*
+ * define max. shared version we can interoperate with
+ */
+#define XFS_SB_MAX_SHARED_VN 0
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_NUM)
+int xfs_sb_version_num(xfs_sb_t *sbp);
+#define XFS_SB_VERSION_NUM(sbp) xfs_sb_version_num(sbp)
+#else
+#define XFS_SB_VERSION_NUM(sbp) ((sbp)->sb_versionnum & XFS_SB_VERSION_NUMBITS)
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_GOOD_VERSION)
+int xfs_sb_good_version(xfs_sb_t *sbp);
+#define XFS_SB_GOOD_VERSION(sbp) xfs_sb_good_version(sbp)
+#else
+#define XFS_SB_GOOD_VERSION_INT(sbp) \
+ ((((sbp)->sb_versionnum >= XFS_SB_VERSION_1) && \
+ ((sbp)->sb_versionnum <= XFS_SB_VERSION_3)) || \
+ ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \
+ !(((sbp)->sb_versionnum & ~XFS_SB_VERSION_OKREALBITS) || \
+ (((sbp)->sb_versionnum & XFS_SB_VERSION_MOREBITSBIT) && \
+ ((sbp)->sb_features2 & ~XFS_SB_VERSION2_OKREALBITS)))
+
+#ifdef __KERNEL__
+#define XFS_SB_GOOD_VERSION(sbp) \
+ (XFS_SB_GOOD_VERSION_INT(sbp) && \
+ (sbp)->sb_shared_vn <= XFS_SB_MAX_SHARED_VN) ))
+#else
+/*
+ * extra 2 paren's here (( to unconfuse paren-matching editors
+ * like vi because XFS_SB_GOOD_VERSION_INT is a partial expression
+ * and the two XFS_SB_GOOD_VERSION's each 2 more close paren's to
+ * complete the expression.
+ */
+#define XFS_SB_GOOD_VERSION(sbp) \
+ (XFS_SB_GOOD_VERSION_INT(sbp) && \
+ (!((sbp)->sb_versionnum & XFS_SB_VERSION_SHAREDBIT) || \
+ (sbp)->sb_shared_vn <= XFS_SB_MAX_SHARED_VN)) ))
+#endif /* __KERNEL__ */
+#endif
+
+#define XFS_SB_GOOD_SASH_VERSION(sbp) \
+ ((((sbp)->sb_versionnum >= XFS_SB_VERSION_1) && \
+ ((sbp)->sb_versionnum <= XFS_SB_VERSION_3)) || \
+ ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \
+ !((sbp)->sb_versionnum & ~XFS_SB_VERSION_OKSASHBITS)))
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_TONEW)
+unsigned xfs_sb_version_tonew(unsigned v);
+#define XFS_SB_VERSION_TONEW(v) xfs_sb_version_tonew(v)
+#else
+#define XFS_SB_VERSION_TONEW(v) \
+ ((((v) == XFS_SB_VERSION_1) ? \
+ 0 : \
+ (((v) == XFS_SB_VERSION_2) ? \
+ XFS_SB_VERSION_ATTRBIT : \
+ (XFS_SB_VERSION_ATTRBIT | XFS_SB_VERSION_NLINKBIT))) | \
+ XFS_SB_VERSION_4)
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_TOOLD)
+unsigned xfs_sb_version_toold(unsigned v);
+#define XFS_SB_VERSION_TOOLD(v) xfs_sb_version_toold(v)
+#else
+#define XFS_SB_VERSION_TOOLD(v) \
+ (((v) & (XFS_SB_VERSION_QUOTABIT | XFS_SB_VERSION_ALIGNBIT)) ? \
+ 0 : \
+ (((v) & XFS_SB_VERSION_NLINKBIT) ? \
+ XFS_SB_VERSION_3 : \
+ (((v) & XFS_SB_VERSION_ATTRBIT) ? \
+ XFS_SB_VERSION_2 : \
+ XFS_SB_VERSION_1)))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_HASATTR)
+int xfs_sb_version_hasattr(xfs_sb_t *sbp);
+#define XFS_SB_VERSION_HASATTR(sbp) xfs_sb_version_hasattr(sbp)
+#else
+#define XFS_SB_VERSION_HASATTR(sbp) \
+ (((sbp)->sb_versionnum == XFS_SB_VERSION_2) || \
+ ((sbp)->sb_versionnum == XFS_SB_VERSION_3) || \
+ ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \
+ ((sbp)->sb_versionnum & XFS_SB_VERSION_ATTRBIT)))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_ADDATTR)
+void xfs_sb_version_addattr(xfs_sb_t *sbp);
+#define XFS_SB_VERSION_ADDATTR(sbp) xfs_sb_version_addattr(sbp)
+#else
+#define XFS_SB_VERSION_ADDATTR(sbp) \
+ ((sbp)->sb_versionnum = \
+ (((sbp)->sb_versionnum == XFS_SB_VERSION_1) ? \
+ XFS_SB_VERSION_2 : \
+ ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) ? \
+ ((sbp)->sb_versionnum | XFS_SB_VERSION_ATTRBIT) : \
+ (XFS_SB_VERSION_4 | XFS_SB_VERSION_ATTRBIT))))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_HASNLINK)
+int xfs_sb_version_hasnlink(xfs_sb_t *sbp);
+#define XFS_SB_VERSION_HASNLINK(sbp) xfs_sb_version_hasnlink(sbp)
+#else
+#define XFS_SB_VERSION_HASNLINK(sbp) \
+ (((sbp)->sb_versionnum == XFS_SB_VERSION_3) || \
+ ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \
+ ((sbp)->sb_versionnum & XFS_SB_VERSION_NLINKBIT)))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_ADDNLINK)
+void xfs_sb_version_addnlink(xfs_sb_t *sbp);
+#define XFS_SB_VERSION_ADDNLINK(sbp) xfs_sb_version_addnlink(sbp)
+#else
+#define XFS_SB_VERSION_ADDNLINK(sbp) \
+ ((sbp)->sb_versionnum = \
+ ((sbp)->sb_versionnum <= XFS_SB_VERSION_2 ? \
+ XFS_SB_VERSION_3 : \
+ ((sbp)->sb_versionnum | XFS_SB_VERSION_NLINKBIT)))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_HASQUOTA)
+int xfs_sb_version_hasquota(xfs_sb_t *sbp);
+#define XFS_SB_VERSION_HASQUOTA(sbp) xfs_sb_version_hasquota(sbp)
+#else
+#define XFS_SB_VERSION_HASQUOTA(sbp) \
+ ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \
+ ((sbp)->sb_versionnum & XFS_SB_VERSION_QUOTABIT))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_ADDQUOTA)
+void xfs_sb_version_addquota(xfs_sb_t *sbp);
+#define XFS_SB_VERSION_ADDQUOTA(sbp) xfs_sb_version_addquota(sbp)
+#else
+#define XFS_SB_VERSION_ADDQUOTA(sbp) \
+ ((sbp)->sb_versionnum = \
+ (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4 ? \
+ ((sbp)->sb_versionnum | XFS_SB_VERSION_QUOTABIT) : \
+ (XFS_SB_VERSION_TONEW((sbp)->sb_versionnum) | \
+ XFS_SB_VERSION_QUOTABIT)))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_HASALIGN)
+int xfs_sb_version_hasalign(xfs_sb_t *sbp);
+#define XFS_SB_VERSION_HASALIGN(sbp) xfs_sb_version_hasalign(sbp)
+#else
+#define XFS_SB_VERSION_HASALIGN(sbp) \
+ ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \
+ ((sbp)->sb_versionnum & XFS_SB_VERSION_ALIGNBIT))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_SUBALIGN)
+void xfs_sb_version_subalign(xfs_sb_t *sbp);
+#define XFS_SB_VERSION_SUBALIGN(sbp) xfs_sb_version_subalign(sbp)
+#else
+#define XFS_SB_VERSION_SUBALIGN(sbp) \
+ ((sbp)->sb_versionnum = \
+ XFS_SB_VERSION_TOOLD((sbp)->sb_versionnum & ~XFS_SB_VERSION_ALIGNBIT))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_HASDALIGN)
+int xfs_sb_version_hasdalign(xfs_sb_t *sbp);
+#define XFS_SB_VERSION_HASDALIGN(sbp) xfs_sb_version_hasdalign(sbp)
+#else
+#define XFS_SB_VERSION_HASDALIGN(sbp) \
+ ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \
+ ((sbp)->sb_versionnum & XFS_SB_VERSION_DALIGNBIT))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_ADDDALIGN)
+int xfs_sb_version_adddalign(xfs_sb_t *sbp);
+#define XFS_SB_VERSION_ADDDALIGN(sbp) xfs_sb_version_adddalign(sbp)
+#else
+#define XFS_SB_VERSION_ADDDALIGN(sbp) \
+ ((sbp)->sb_versionnum = \
+ ((sbp)->sb_versionnum | XFS_SB_VERSION_DALIGNBIT))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_HASSHARED)
+int xfs_sb_version_hasshared(xfs_sb_t *sbp);
+#define XFS_SB_VERSION_HASSHARED(sbp) xfs_sb_version_hasshared(sbp)
+#else
+#define XFS_SB_VERSION_HASSHARED(sbp) \
+ ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \
+ ((sbp)->sb_versionnum & XFS_SB_VERSION_SHAREDBIT))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_ADDSHARED)
+int xfs_sb_version_addshared(xfs_sb_t *sbp);
+#define XFS_SB_VERSION_ADDSHARED(sbp) xfs_sb_version_addshared(sbp)
+#else
+#define XFS_SB_VERSION_ADDSHARED(sbp) \
+ ((sbp)->sb_versionnum = \
+ ((sbp)->sb_versionnum | XFS_SB_VERSION_SHAREDBIT))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_SUBSHARED)
+int xfs_sb_version_subshared(xfs_sb_t *sbp);
+#define XFS_SB_VERSION_SUBSHARED(sbp) xfs_sb_version_subshared(sbp)
+#else
+#define XFS_SB_VERSION_SUBSHARED(sbp) \
+ ((sbp)->sb_versionnum = \
+ ((sbp)->sb_versionnum & ~XFS_SB_VERSION_SHAREDBIT))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_HASDIRV2)
+int xfs_sb_version_hasdirv2(xfs_sb_t *sbp);
+#define XFS_SB_VERSION_HASDIRV2(sbp) xfs_sb_version_hasdirv2(sbp)
+#else
+#define XFS_SB_VERSION_HASDIRV2(sbp) \
+ ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \
+ ((sbp)->sb_versionnum & XFS_SB_VERSION_DIRV2BIT))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_HASLOGV2)
+int xfs_sb_version_haslogv2(xfs_sb_t *sbp);
+#define XFS_SB_VERSION_HASLOGV2(sbp) xfs_sb_version_haslogv2(sbp)
+#else
+#define XFS_SB_VERSION_HASLOGV2(sbp) \
+ ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \
+ ((sbp)->sb_versionnum & XFS_SB_VERSION_LOGV2BIT))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_HASEXTFLGBIT)
+int xfs_sb_version_hasextflgbit(xfs_sb_t *sbp);
+#define XFS_SB_VERSION_HASEXTFLGBIT(sbp) xfs_sb_version_hasextflgbit(sbp)
+#else
+#define XFS_SB_VERSION_HASEXTFLGBIT(sbp) \
+ ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \
+ ((sbp)->sb_versionnum & XFS_SB_VERSION_EXTFLGBIT))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_ADDEXTFLGBIT)
+int xfs_sb_version_addextflgbit(xfs_sb_t *sbp);
+#define XFS_SB_VERSION_ADDEXTFLGBIT(sbp) xfs_sb_version_addextflgbit(sbp)
+#else
+#define XFS_SB_VERSION_ADDEXTFLGBIT(sbp) \
+ ((sbp)->sb_versionnum = \
+ ((sbp)->sb_versionnum | XFS_SB_VERSION_EXTFLGBIT))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_SUBEXTFLGBIT)
+int xfs_sb_version_subextflgbit(xfs_sb_t *sbp);
+#define XFS_SB_VERSION_SUBEXTFLGBIT(sbp) xfs_sb_version_subextflgbit(sbp)
+#else
+#define XFS_SB_VERSION_SUBEXTFLGBIT(sbp) \
+ ((sbp)->sb_versionnum = \
+ ((sbp)->sb_versionnum & ~XFS_SB_VERSION_EXTFLGBIT))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_HASSECTOR)
+int xfs_sb_version_hassector(xfs_sb_t *sbp);
+#define XFS_SB_VERSION_HASSECTOR(sbp) xfs_sb_version_hassector(sbp)
+#else
+#define XFS_SB_VERSION_HASSECTOR(sbp) \
+ ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \
+ ((sbp)->sb_versionnum & XFS_SB_VERSION_SECTORBIT))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_HASMOREBITSBIT)
+int xfs_sb_version_hasmorebits(xfs_sb_t *sbp);
+#define XFS_SB_VERSION_HASMOREBITS(sbp) xfs_sb_version_hasmorebits(sbp)
+#else
+#define XFS_SB_VERSION_HASMOREBITS(sbp) \
+ ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \
+ ((sbp)->sb_versionnum & XFS_SB_VERSION_MOREBITSBIT))
+#endif
+
+/*
+ * sb_features2 bit version macros.
+ *
+ * For example, for a bit defined as XFS_SB_VERSION2_YBIT, has a macro:
+ *
+ * SB_VERSION_HASYBIT(xfs_sb_t *sbp)
+ * ((XFS_SB_VERSION_HASMOREBITS(sbp) &&
+ * ((sbp)->sb_versionnum & XFS_SB_VERSION2_YBIT)
+ */
+
+/*
+ * end of superblock version macros
+ */
+
+#define XFS_SB_DADDR ((xfs_daddr_t)0) /* daddr in filesystem/ag */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_BLOCK)
+xfs_agblock_t xfs_sb_block(struct xfs_mount *mp);
+#define XFS_SB_BLOCK(mp) xfs_sb_block(mp)
+#else
+#define XFS_SB_BLOCK(mp) XFS_HDR_BLOCK(mp, XFS_SB_DADDR)
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_HDR_BLOCK)
+xfs_agblock_t xfs_hdr_block(struct xfs_mount *mp, xfs_daddr_t d);
+#define XFS_HDR_BLOCK(mp,d) xfs_hdr_block(mp,d)
+#else
+#define XFS_HDR_BLOCK(mp,d) ((xfs_agblock_t)(XFS_BB_TO_FSBT(mp,d)))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DADDR_TO_FSB)
+xfs_fsblock_t xfs_daddr_to_fsb(struct xfs_mount *mp, xfs_daddr_t d);
+#define XFS_DADDR_TO_FSB(mp,d) xfs_daddr_to_fsb(mp,d)
+#else
+#define XFS_DADDR_TO_FSB(mp,d) \
+ XFS_AGB_TO_FSB(mp, XFS_DADDR_TO_AGNO(mp,d), XFS_DADDR_TO_AGBNO(mp,d))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_FSB_TO_DADDR)
+xfs_daddr_t xfs_fsb_to_daddr(struct xfs_mount *mp, xfs_fsblock_t fsbno);
+#define XFS_FSB_TO_DADDR(mp,fsbno) xfs_fsb_to_daddr(mp,fsbno)
+#else
+#define XFS_FSB_TO_DADDR(mp,fsbno) \
+ XFS_AGB_TO_DADDR(mp, XFS_FSB_TO_AGNO(mp,fsbno), \
+ XFS_FSB_TO_AGBNO(mp,fsbno))
+#endif
+
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BUF_TO_SBP)
+xfs_sb_t *xfs_buf_to_sbp(struct xfs_buf *bp);
+#define XFS_BUF_TO_SBP(bp) xfs_buf_to_sbp(bp)
+#else
+#define XFS_BUF_TO_SBP(bp) ((xfs_sb_t *)XFS_BUF_PTR(bp))
+#endif
+
+/*
+ * File system sector to basic block conversions.
+ */
+#define XFS_FSS_TO_BB(mp,sec) ((sec) << (mp)->m_sectbb_log)
+#define XFS_BB_TO_FSS(mp,bb) \
+ (((bb) + (XFS_FSS_TO_BB(mp,1) - 1)) >> (mp)->m_sectbb_log)
+#define XFS_BB_TO_FSST(mp,bb) ((bb) >> (mp)->m_sectbb_log)
+
+/*
+ * File system sector to byte conversions.
+ */
+#define XFS_FSS_TO_B(mp,sectno) ((xfs_fsize_t)(sectno) << (mp)->m_sb.sb_sectlog)
+#define XFS_B_TO_FSST(mp,b) (((__uint64_t)(b)) >> (mp)->m_sb.sb_sectlog)
+
+/*
+ * File system block to basic block conversions.
+ */
+#define XFS_FSB_TO_BB(mp,fsbno) ((fsbno) << (mp)->m_blkbb_log)
+#define XFS_BB_TO_FSB(mp,bb) \
+ (((bb) + (XFS_FSB_TO_BB(mp,1) - 1)) >> (mp)->m_blkbb_log)
+#define XFS_BB_TO_FSBT(mp,bb) ((bb) >> (mp)->m_blkbb_log)
+#define XFS_BB_FSB_OFFSET(mp,bb) ((bb) & ((mp)->m_bsize - 1))
+
+/*
+ * File system block to byte conversions.
+ */
+#define XFS_FSB_TO_B(mp,fsbno) ((xfs_fsize_t)(fsbno) << (mp)->m_sb.sb_blocklog)
+#define XFS_B_TO_FSB(mp,b) \
+ ((((__uint64_t)(b)) + (mp)->m_blockmask) >> (mp)->m_sb.sb_blocklog)
+#define XFS_B_TO_FSBT(mp,b) (((__uint64_t)(b)) >> (mp)->m_sb.sb_blocklog)
+#define XFS_B_FSB_OFFSET(mp,b) ((b) & (mp)->m_blockmask)
+
+#endif /* __XFS_SB_H__ */
diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c
new file mode 100644
index 00000000000000..3db0e22007759c
--- /dev/null
+++ b/fs/xfs/xfs_trans.c
@@ -0,0 +1,1315 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_error.h"
+#include "xfs_trans_priv.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_alloc.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode.h"
+#include "xfs_bmap.h"
+#include "xfs_da_btree.h"
+#include "xfs_quota.h"
+#include "xfs_trans_space.h"
+
+
+STATIC void xfs_trans_apply_sb_deltas(xfs_trans_t *);
+STATIC uint xfs_trans_count_vecs(xfs_trans_t *);
+STATIC void xfs_trans_fill_vecs(xfs_trans_t *, xfs_log_iovec_t *);
+STATIC void xfs_trans_uncommit(xfs_trans_t *, uint);
+STATIC void xfs_trans_committed(xfs_trans_t *, int);
+STATIC void xfs_trans_chunk_committed(xfs_log_item_chunk_t *, xfs_lsn_t, int);
+STATIC void xfs_trans_free(xfs_trans_t *);
+
+kmem_zone_t *xfs_trans_zone;
+
+
+/*
+ * Initialize the precomputed transaction reservation values
+ * in the mount structure.
+ */
+void
+xfs_trans_init(
+ xfs_mount_t *mp)
+{
+ xfs_trans_reservations_t *resp;
+
+ resp = &(mp->m_reservations);
+ resp->tr_write =
+ (uint)(XFS_CALC_WRITE_LOG_RES(mp) + XFS_DQUOT_LOGRES(mp));
+ resp->tr_itruncate =
+ (uint)(XFS_CALC_ITRUNCATE_LOG_RES(mp) + XFS_DQUOT_LOGRES(mp));
+ resp->tr_rename =
+ (uint)(XFS_CALC_RENAME_LOG_RES(mp) + XFS_DQUOT_LOGRES(mp));
+ resp->tr_link = (uint)XFS_CALC_LINK_LOG_RES(mp);
+ resp->tr_remove =
+ (uint)(XFS_CALC_REMOVE_LOG_RES(mp) + XFS_DQUOT_LOGRES(mp));
+ resp->tr_symlink =
+ (uint)(XFS_CALC_SYMLINK_LOG_RES(mp) + XFS_DQUOT_LOGRES(mp));
+ resp->tr_create =
+ (uint)(XFS_CALC_CREATE_LOG_RES(mp) + XFS_DQUOT_LOGRES(mp));
+ resp->tr_mkdir =
+ (uint)(XFS_CALC_MKDIR_LOG_RES(mp) + XFS_DQUOT_LOGRES(mp));
+ resp->tr_ifree =
+ (uint)(XFS_CALC_IFREE_LOG_RES(mp) + XFS_DQUOT_LOGRES(mp));
+ resp->tr_ichange =
+ (uint)(XFS_CALC_ICHANGE_LOG_RES(mp) + XFS_DQUOT_LOGRES(mp));
+ resp->tr_growdata = (uint)XFS_CALC_GROWDATA_LOG_RES(mp);
+ resp->tr_swrite = (uint)XFS_CALC_SWRITE_LOG_RES(mp);
+ resp->tr_writeid = (uint)XFS_CALC_WRITEID_LOG_RES(mp);
+ resp->tr_addafork =
+ (uint)(XFS_CALC_ADDAFORK_LOG_RES(mp) + XFS_DQUOT_LOGRES(mp));
+ resp->tr_attrinval = (uint)XFS_CALC_ATTRINVAL_LOG_RES(mp);
+ resp->tr_attrset =
+ (uint)(XFS_CALC_ATTRSET_LOG_RES(mp) + XFS_DQUOT_LOGRES(mp));
+ resp->tr_attrrm =
+ (uint)(XFS_CALC_ATTRRM_LOG_RES(mp) + XFS_DQUOT_LOGRES(mp));
+ resp->tr_clearagi = (uint)XFS_CALC_CLEAR_AGI_BUCKET_LOG_RES(mp);
+ resp->tr_growrtalloc = (uint)XFS_CALC_GROWRTALLOC_LOG_RES(mp);
+ resp->tr_growrtzero = (uint)XFS_CALC_GROWRTZERO_LOG_RES(mp);
+ resp->tr_growrtfree = (uint)XFS_CALC_GROWRTFREE_LOG_RES(mp);
+}
+
+/*
+ * This routine is called to allocate a transaction structure.
+ * The type parameter indicates the type of the transaction. These
+ * are enumerated in xfs_trans.h.
+ *
+ * Dynamically allocate the transaction structure from the transaction
+ * zone, initialize it, and return it to the caller.
+ */
+xfs_trans_t *
+xfs_trans_alloc(
+ xfs_mount_t *mp,
+ uint type)
+{
+ fs_check_frozen(XFS_MTOVFS(mp), SB_FREEZE_TRANS);
+ atomic_inc(&mp->m_active_trans);
+
+ return (_xfs_trans_alloc(mp, type));
+
+}
+
+xfs_trans_t *
+_xfs_trans_alloc(
+ xfs_mount_t *mp,
+ uint type)
+{
+ xfs_trans_t *tp;
+
+ ASSERT(xfs_trans_zone != NULL);
+ tp = kmem_zone_zalloc(xfs_trans_zone, KM_SLEEP);
+
+ /*
+ * Initialize the transaction structure.
+ */
+ tp->t_magic = XFS_TRANS_MAGIC;
+ tp->t_type = type;
+ tp->t_mountp = mp;
+ tp->t_items_free = XFS_LIC_NUM_SLOTS;
+ tp->t_busy_free = XFS_LBC_NUM_SLOTS;
+ XFS_LIC_INIT(&(tp->t_items));
+ XFS_LBC_INIT(&(tp->t_busy));
+
+ return (tp);
+}
+
+/*
+ * This is called to create a new transaction which will share the
+ * permanent log reservation of the given transaction. The remaining
+ * unused block and rt extent reservations are also inherited. This
+ * implies that the original transaction is no longer allowed to allocate
+ * blocks. Locks and log items, however, are no inherited. They must
+ * be added to the new transaction explicitly.
+ */
+xfs_trans_t *
+xfs_trans_dup(
+ xfs_trans_t *tp)
+{
+ xfs_trans_t *ntp;
+
+ ntp = kmem_zone_zalloc(xfs_trans_zone, KM_SLEEP);
+
+ /*
+ * Initialize the new transaction structure.
+ */
+ ntp->t_magic = XFS_TRANS_MAGIC;
+ ntp->t_type = tp->t_type;
+ ntp->t_mountp = tp->t_mountp;
+ ntp->t_items_free = XFS_LIC_NUM_SLOTS;
+ ntp->t_busy_free = XFS_LBC_NUM_SLOTS;
+ XFS_LIC_INIT(&(ntp->t_items));
+ XFS_LBC_INIT(&(ntp->t_busy));
+
+ ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES);
+
+#if defined(XLOG_NOLOG) || defined(DEBUG)
+ ASSERT(!xlog_debug || tp->t_ticket != NULL);
+#else
+ ASSERT(tp->t_ticket != NULL);
+#endif
+ ntp->t_flags = XFS_TRANS_PERM_LOG_RES | (tp->t_flags & XFS_TRANS_RESERVE);
+ ntp->t_ticket = tp->t_ticket;
+ ntp->t_blk_res = tp->t_blk_res - tp->t_blk_res_used;
+ tp->t_blk_res = tp->t_blk_res_used;
+ ntp->t_rtx_res = tp->t_rtx_res - tp->t_rtx_res_used;
+ tp->t_rtx_res = tp->t_rtx_res_used;
+ PFLAGS_DUP(&tp->t_pflags, &ntp->t_pflags);
+
+ XFS_TRANS_DUP_DQINFO(tp->t_mountp, tp, ntp);
+
+ atomic_inc(&tp->t_mountp->m_active_trans);
+ return ntp;
+}
+
+/*
+ * This is called to reserve free disk blocks and log space for the
+ * given transaction. This must be done before allocating any resources
+ * within the transaction.
+ *
+ * This will return ENOSPC if there are not enough blocks available.
+ * It will sleep waiting for available log space.
+ * The only valid value for the flags parameter is XFS_RES_LOG_PERM, which
+ * is used by long running transactions. If any one of the reservations
+ * fails then they will all be backed out.
+ *
+ * This does not do quota reservations. That typically is done by the
+ * caller afterwards.
+ */
+int
+xfs_trans_reserve(
+ xfs_trans_t *tp,
+ uint blocks,
+ uint logspace,
+ uint rtextents,
+ uint flags,
+ uint logcount)
+{
+ int log_flags;
+ int error;
+ int rsvd;
+
+ error = 0;
+ rsvd = (tp->t_flags & XFS_TRANS_RESERVE) != 0;
+
+ /* Mark this thread as being in a transaction */
+ PFLAGS_SET_FSTRANS(&tp->t_pflags);
+
+ /*
+ * Attempt to reserve the needed disk blocks by decrementing
+ * the number needed from the number available. This will
+ * fail if the count would go below zero.
+ */
+ if (blocks > 0) {
+ error = xfs_mod_incore_sb(tp->t_mountp, XFS_SBS_FDBLOCKS,
+ -blocks, rsvd);
+ if (error != 0) {
+ PFLAGS_RESTORE_FSTRANS(&tp->t_pflags);
+ return (XFS_ERROR(ENOSPC));
+ }
+ tp->t_blk_res += blocks;
+ }
+
+ /*
+ * Reserve the log space needed for this transaction.
+ */
+ if (logspace > 0) {
+ ASSERT((tp->t_log_res == 0) || (tp->t_log_res == logspace));
+ ASSERT((tp->t_log_count == 0) ||
+ (tp->t_log_count == logcount));
+ if (flags & XFS_TRANS_PERM_LOG_RES) {
+ log_flags = XFS_LOG_PERM_RESERV;
+ tp->t_flags |= XFS_TRANS_PERM_LOG_RES;
+ } else {
+ ASSERT(tp->t_ticket == NULL);
+ ASSERT(!(tp->t_flags & XFS_TRANS_PERM_LOG_RES));
+ log_flags = 0;
+ }
+
+ error = xfs_log_reserve(tp->t_mountp, logspace, logcount,
+ &tp->t_ticket,
+ XFS_TRANSACTION, log_flags);
+ if (error) {
+ goto undo_blocks;
+ }
+ tp->t_log_res = logspace;
+ tp->t_log_count = logcount;
+ }
+
+ /*
+ * Attempt to reserve the needed realtime extents by decrementing
+ * the number needed from the number available. This will
+ * fail if the count would go below zero.
+ */
+ if (rtextents > 0) {
+ error = xfs_mod_incore_sb(tp->t_mountp, XFS_SBS_FREXTENTS,
+ -rtextents, rsvd);
+ if (error) {
+ error = XFS_ERROR(ENOSPC);
+ goto undo_log;
+ }
+ tp->t_rtx_res += rtextents;
+ }
+
+ return 0;
+
+ /*
+ * Error cases jump to one of these labels to undo any
+ * reservations which have already been performed.
+ */
+undo_log:
+ if (logspace > 0) {
+ if (flags & XFS_TRANS_PERM_LOG_RES) {
+ log_flags = XFS_LOG_REL_PERM_RESERV;
+ } else {
+ log_flags = 0;
+ }
+ xfs_log_done(tp->t_mountp, tp->t_ticket, NULL, log_flags);
+ tp->t_ticket = NULL;
+ tp->t_log_res = 0;
+ tp->t_flags &= ~XFS_TRANS_PERM_LOG_RES;
+ }
+
+undo_blocks:
+ if (blocks > 0) {
+ (void) xfs_mod_incore_sb(tp->t_mountp, XFS_SBS_FDBLOCKS,
+ blocks, rsvd);
+ tp->t_blk_res = 0;
+ }
+
+ PFLAGS_RESTORE_FSTRANS(&tp->t_pflags);
+
+ return (error);
+}
+
+
+/*
+ * This is called to set the a callback to be called when the given
+ * transaction is committed to disk. The transaction pointer and the
+ * argument pointer will be passed to the callback routine.
+ *
+ * Only one callback can be associated with any single transaction.
+ */
+void
+xfs_trans_callback(
+ xfs_trans_t *tp,
+ xfs_trans_callback_t callback,
+ void *arg)
+{
+ ASSERT(tp->t_callback == NULL);
+ tp->t_callback = callback;
+ tp->t_callarg = arg;
+}
+
+
+/*
+ * Record the indicated change to the given field for application
+ * to the file system's superblock when the transaction commits.
+ * For now, just store the change in the transaction structure.
+ *
+ * Mark the transaction structure to indicate that the superblock
+ * needs to be updated before committing.
+ */
+void
+xfs_trans_mod_sb(
+ xfs_trans_t *tp,
+ uint field,
+ long delta)
+{
+
+ switch (field) {
+ case XFS_TRANS_SB_ICOUNT:
+ tp->t_icount_delta += delta;
+ break;
+ case XFS_TRANS_SB_IFREE:
+ tp->t_ifree_delta += delta;
+ break;
+ case XFS_TRANS_SB_FDBLOCKS:
+ /*
+ * Track the number of blocks allocated in the
+ * transaction. Make sure it does not exceed the
+ * number reserved.
+ */
+ if (delta < 0) {
+ tp->t_blk_res_used += (uint)-delta;
+ ASSERT(tp->t_blk_res_used <= tp->t_blk_res);
+ }
+ tp->t_fdblocks_delta += delta;
+ break;
+ case XFS_TRANS_SB_RES_FDBLOCKS:
+ /*
+ * The allocation has already been applied to the
+ * in-core superblock's counter. This should only
+ * be applied to the on-disk superblock.
+ */
+ ASSERT(delta < 0);
+ tp->t_res_fdblocks_delta += delta;
+ break;
+ case XFS_TRANS_SB_FREXTENTS:
+ /*
+ * Track the number of blocks allocated in the
+ * transaction. Make sure it does not exceed the
+ * number reserved.
+ */
+ if (delta < 0) {
+ tp->t_rtx_res_used += (uint)-delta;
+ ASSERT(tp->t_rtx_res_used <= tp->t_rtx_res);
+ }
+ tp->t_frextents_delta += delta;
+ break;
+ case XFS_TRANS_SB_RES_FREXTENTS:
+ /*
+ * The allocation has already been applied to the
+ * in-core superblocks's counter. This should only
+ * be applied to the on-disk superblock.
+ */
+ ASSERT(delta < 0);
+ tp->t_res_frextents_delta += delta;
+ break;
+ case XFS_TRANS_SB_DBLOCKS:
+ ASSERT(delta > 0);
+ tp->t_dblocks_delta += delta;
+ break;
+ case XFS_TRANS_SB_AGCOUNT:
+ ASSERT(delta > 0);
+ tp->t_agcount_delta += delta;
+ break;
+ case XFS_TRANS_SB_IMAXPCT:
+ tp->t_imaxpct_delta += delta;
+ break;
+ case XFS_TRANS_SB_REXTSIZE:
+ tp->t_rextsize_delta += delta;
+ break;
+ case XFS_TRANS_SB_RBMBLOCKS:
+ tp->t_rbmblocks_delta += delta;
+ break;
+ case XFS_TRANS_SB_RBLOCKS:
+ tp->t_rblocks_delta += delta;
+ break;
+ case XFS_TRANS_SB_REXTENTS:
+ tp->t_rextents_delta += delta;
+ break;
+ case XFS_TRANS_SB_REXTSLOG:
+ tp->t_rextslog_delta += delta;
+ break;
+ default:
+ ASSERT(0);
+ return;
+ }
+
+ tp->t_flags |= (XFS_TRANS_SB_DIRTY | XFS_TRANS_DIRTY);
+}
+
+/*
+ * xfs_trans_apply_sb_deltas() is called from the commit code
+ * to bring the superblock buffer into the current transaction
+ * and modify it as requested by earlier calls to xfs_trans_mod_sb().
+ *
+ * For now we just look at each field allowed to change and change
+ * it if necessary.
+ */
+STATIC void
+xfs_trans_apply_sb_deltas(
+ xfs_trans_t *tp)
+{
+ xfs_sb_t *sbp;
+ xfs_buf_t *bp;
+ int whole = 0;
+
+ bp = xfs_trans_getsb(tp, tp->t_mountp, 0);
+ sbp = XFS_BUF_TO_SBP(bp);
+
+ /*
+ * Check that superblock mods match the mods made to AGF counters.
+ */
+ ASSERT((tp->t_fdblocks_delta + tp->t_res_fdblocks_delta) ==
+ (tp->t_ag_freeblks_delta + tp->t_ag_flist_delta +
+ tp->t_ag_btree_delta));
+
+ if (tp->t_icount_delta != 0) {
+ INT_MOD(sbp->sb_icount, ARCH_CONVERT, tp->t_icount_delta);
+ }
+ if (tp->t_ifree_delta != 0) {
+ INT_MOD(sbp->sb_ifree, ARCH_CONVERT, tp->t_ifree_delta);
+ }
+
+ if (tp->t_fdblocks_delta != 0) {
+ INT_MOD(sbp->sb_fdblocks, ARCH_CONVERT, tp->t_fdblocks_delta);
+ }
+ if (tp->t_res_fdblocks_delta != 0) {
+ INT_MOD(sbp->sb_fdblocks, ARCH_CONVERT, tp->t_res_fdblocks_delta);
+ }
+
+ if (tp->t_frextents_delta != 0) {
+ INT_MOD(sbp->sb_frextents, ARCH_CONVERT, tp->t_frextents_delta);
+ }
+ if (tp->t_res_frextents_delta != 0) {
+ INT_MOD(sbp->sb_frextents, ARCH_CONVERT, tp->t_res_frextents_delta);
+ }
+ if (tp->t_dblocks_delta != 0) {
+ INT_MOD(sbp->sb_dblocks, ARCH_CONVERT, tp->t_dblocks_delta);
+ whole = 1;
+ }
+ if (tp->t_agcount_delta != 0) {
+ INT_MOD(sbp->sb_agcount, ARCH_CONVERT, tp->t_agcount_delta);
+ whole = 1;
+ }
+ if (tp->t_imaxpct_delta != 0) {
+ INT_MOD(sbp->sb_imax_pct, ARCH_CONVERT, tp->t_imaxpct_delta);
+ whole = 1;
+ }
+ if (tp->t_rextsize_delta != 0) {
+ INT_MOD(sbp->sb_rextsize, ARCH_CONVERT, tp->t_rextsize_delta);
+ whole = 1;
+ }
+ if (tp->t_rbmblocks_delta != 0) {
+ INT_MOD(sbp->sb_rbmblocks, ARCH_CONVERT, tp->t_rbmblocks_delta);
+ whole = 1;
+ }
+ if (tp->t_rblocks_delta != 0) {
+ INT_MOD(sbp->sb_rblocks, ARCH_CONVERT, tp->t_rblocks_delta);
+ whole = 1;
+ }
+ if (tp->t_rextents_delta != 0) {
+ INT_MOD(sbp->sb_rextents, ARCH_CONVERT, tp->t_rextents_delta);
+ whole = 1;
+ }
+ if (tp->t_rextslog_delta != 0) {
+ INT_MOD(sbp->sb_rextslog, ARCH_CONVERT, tp->t_rextslog_delta);
+ whole = 1;
+ }
+
+ if (whole)
+ /*
+ * Log the whole thing, the fields are discontiguous.
+ */
+ xfs_trans_log_buf(tp, bp, 0, sizeof(xfs_sb_t) - 1);
+ else
+ /*
+ * Since all the modifiable fields are contiguous, we
+ * can get away with this.
+ */
+ xfs_trans_log_buf(tp, bp, offsetof(xfs_sb_t, sb_icount),
+ offsetof(xfs_sb_t, sb_frextents) +
+ sizeof(sbp->sb_frextents) - 1);
+
+ XFS_MTOVFS(tp->t_mountp)->vfs_super->s_dirt = 1;
+}
+
+/*
+ * xfs_trans_unreserve_and_mod_sb() is called to release unused
+ * reservations and apply superblock counter changes to the in-core
+ * superblock.
+ *
+ * This is done efficiently with a single call to xfs_mod_incore_sb_batch().
+ */
+void
+xfs_trans_unreserve_and_mod_sb(
+ xfs_trans_t *tp)
+{
+ xfs_mod_sb_t msb[14]; /* If you add cases, add entries */
+ xfs_mod_sb_t *msbp;
+ /* REFERENCED */
+ int error;
+ int rsvd;
+
+ msbp = msb;
+ rsvd = (tp->t_flags & XFS_TRANS_RESERVE) != 0;
+
+ /*
+ * Release any reserved blocks. Any that were allocated
+ * will be taken back again by fdblocks_delta below.
+ */
+ if (tp->t_blk_res > 0) {
+ msbp->msb_field = XFS_SBS_FDBLOCKS;
+ msbp->msb_delta = tp->t_blk_res;
+ msbp++;
+ }
+
+ /*
+ * Release any reserved real time extents . Any that were
+ * allocated will be taken back again by frextents_delta below.
+ */
+ if (tp->t_rtx_res > 0) {
+ msbp->msb_field = XFS_SBS_FREXTENTS;
+ msbp->msb_delta = tp->t_rtx_res;
+ msbp++;
+ }
+
+ /*
+ * Apply any superblock modifications to the in-core version.
+ * The t_res_fdblocks_delta and t_res_frextents_delta fields are
+ * explicity NOT applied to the in-core superblock.
+ * The idea is that that has already been done.
+ */
+ if (tp->t_flags & XFS_TRANS_SB_DIRTY) {
+ if (tp->t_icount_delta != 0) {
+ msbp->msb_field = XFS_SBS_ICOUNT;
+ msbp->msb_delta = (int)tp->t_icount_delta;
+ msbp++;
+ }
+ if (tp->t_ifree_delta != 0) {
+ msbp->msb_field = XFS_SBS_IFREE;
+ msbp->msb_delta = (int)tp->t_ifree_delta;
+ msbp++;
+ }
+ if (tp->t_fdblocks_delta != 0) {
+ msbp->msb_field = XFS_SBS_FDBLOCKS;
+ msbp->msb_delta = (int)tp->t_fdblocks_delta;
+ msbp++;
+ }
+ if (tp->t_frextents_delta != 0) {
+ msbp->msb_field = XFS_SBS_FREXTENTS;
+ msbp->msb_delta = (int)tp->t_frextents_delta;
+ msbp++;
+ }
+ if (tp->t_dblocks_delta != 0) {
+ msbp->msb_field = XFS_SBS_DBLOCKS;
+ msbp->msb_delta = (int)tp->t_dblocks_delta;
+ msbp++;
+ }
+ if (tp->t_agcount_delta != 0) {
+ msbp->msb_field = XFS_SBS_AGCOUNT;
+ msbp->msb_delta = (int)tp->t_agcount_delta;
+ msbp++;
+ }
+ if (tp->t_imaxpct_delta != 0) {
+ msbp->msb_field = XFS_SBS_IMAX_PCT;
+ msbp->msb_delta = (int)tp->t_imaxpct_delta;
+ msbp++;
+ }
+ if (tp->t_rextsize_delta != 0) {
+ msbp->msb_field = XFS_SBS_REXTSIZE;
+ msbp->msb_delta = (int)tp->t_rextsize_delta;
+ msbp++;
+ }
+ if (tp->t_rbmblocks_delta != 0) {
+ msbp->msb_field = XFS_SBS_RBMBLOCKS;
+ msbp->msb_delta = (int)tp->t_rbmblocks_delta;
+ msbp++;
+ }
+ if (tp->t_rblocks_delta != 0) {
+ msbp->msb_field = XFS_SBS_RBLOCKS;
+ msbp->msb_delta = (int)tp->t_rblocks_delta;
+ msbp++;
+ }
+ if (tp->t_rextents_delta != 0) {
+ msbp->msb_field = XFS_SBS_REXTENTS;
+ msbp->msb_delta = (int)tp->t_rextents_delta;
+ msbp++;
+ }
+ if (tp->t_rextslog_delta != 0) {
+ msbp->msb_field = XFS_SBS_REXTSLOG;
+ msbp->msb_delta = (int)tp->t_rextslog_delta;
+ msbp++;
+ }
+ }
+
+ /*
+ * If we need to change anything, do it.
+ */
+ if (msbp > msb) {
+ error = xfs_mod_incore_sb_batch(tp->t_mountp, msb,
+ (uint)(msbp - msb), rsvd);
+ ASSERT(error == 0);
+ }
+}
+
+
+/*
+ * xfs_trans_commit
+ *
+ * Commit the given transaction to the log a/synchronously.
+ *
+ * XFS disk error handling mechanism is not based on a typical
+ * transaction abort mechanism. Logically after the filesystem
+ * gets marked 'SHUTDOWN', we can't let any new transactions
+ * be durable - ie. committed to disk - because some metadata might
+ * be inconsistent. In such cases, this returns an error, and the
+ * caller may assume that all locked objects joined to the transaction
+ * have already been unlocked as if the commit had succeeded.
+ * Do not reference the transaction structure after this call.
+ */
+ /*ARGSUSED*/
+int
+xfs_trans_commit(
+ xfs_trans_t *tp,
+ uint flags,
+ xfs_lsn_t *commit_lsn_p)
+{
+ xfs_log_iovec_t *log_vector;
+ int nvec;
+ xfs_mount_t *mp;
+ xfs_lsn_t commit_lsn;
+ /* REFERENCED */
+ int error;
+ int log_flags;
+ int sync;
+#define XFS_TRANS_LOGVEC_COUNT 16
+ xfs_log_iovec_t log_vector_fast[XFS_TRANS_LOGVEC_COUNT];
+#if defined(XLOG_NOLOG) || defined(DEBUG)
+ static xfs_lsn_t trans_lsn = 1;
+#endif
+ void *commit_iclog;
+ int shutdown;
+
+ commit_lsn = -1;
+
+ /*
+ * Determine whether this commit is releasing a permanent
+ * log reservation or not.
+ */
+ if (flags & XFS_TRANS_RELEASE_LOG_RES) {
+ ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES);
+ log_flags = XFS_LOG_REL_PERM_RESERV;
+ } else {
+ log_flags = 0;
+ }
+ mp = tp->t_mountp;
+
+ /*
+ * If there is nothing to be logged by the transaction,
+ * then unlock all of the items associated with the
+ * transaction and free the transaction structure.
+ * Also make sure to return any reserved blocks to
+ * the free pool.
+ */
+shut_us_down:
+ shutdown = XFS_FORCED_SHUTDOWN(mp) ? EIO : 0;
+ if (!(tp->t_flags & XFS_TRANS_DIRTY) || shutdown) {
+ xfs_trans_unreserve_and_mod_sb(tp);
+ /*
+ * It is indeed possible for the transaction to be
+ * not dirty but the dqinfo portion to be. All that
+ * means is that we have some (non-persistent) quota
+ * reservations that need to be unreserved.
+ */
+ XFS_TRANS_UNRESERVE_AND_MOD_DQUOTS(mp, tp);
+ if (tp->t_ticket) {
+ commit_lsn = xfs_log_done(mp, tp->t_ticket,
+ NULL, log_flags);
+ if (commit_lsn == -1 && !shutdown)
+ shutdown = XFS_ERROR(EIO);
+ }
+ PFLAGS_RESTORE_FSTRANS(&tp->t_pflags);
+ xfs_trans_free_items(tp, shutdown? XFS_TRANS_ABORT : 0);
+ xfs_trans_free_busy(tp);
+ xfs_trans_free(tp);
+ XFS_STATS_INC(xs_trans_empty);
+ if (commit_lsn_p)
+ *commit_lsn_p = commit_lsn;
+ return (shutdown);
+ }
+#if defined(XLOG_NOLOG) || defined(DEBUG)
+ ASSERT(!xlog_debug || tp->t_ticket != NULL);
+#else
+ ASSERT(tp->t_ticket != NULL);
+#endif
+
+ /*
+ * If we need to update the superblock, then do it now.
+ */
+ if (tp->t_flags & XFS_TRANS_SB_DIRTY) {
+ xfs_trans_apply_sb_deltas(tp);
+ }
+ XFS_TRANS_APPLY_DQUOT_DELTAS(mp, tp);
+
+ /*
+ * Ask each log item how many log_vector entries it will
+ * need so we can figure out how many to allocate.
+ * Try to avoid the kmem_alloc() call in the common case
+ * by using a vector from the stack when it fits.
+ */
+ nvec = xfs_trans_count_vecs(tp);
+
+ if (nvec == 0) {
+ xfs_force_shutdown(mp, XFS_LOG_IO_ERROR);
+ goto shut_us_down;
+ }
+
+
+ if (nvec <= XFS_TRANS_LOGVEC_COUNT) {
+ log_vector = log_vector_fast;
+ } else {
+ log_vector = (xfs_log_iovec_t *)kmem_alloc(nvec *
+ sizeof(xfs_log_iovec_t),
+ KM_SLEEP);
+ }
+
+ /*
+ * Fill in the log_vector and pin the logged items, and
+ * then write the transaction to the log.
+ */
+ xfs_trans_fill_vecs(tp, log_vector);
+
+ /*
+ * Ignore errors here. xfs_log_done would do the right thing.
+ * We need to put the ticket, etc. away.
+ */
+ error = xfs_log_write(mp, log_vector, nvec, tp->t_ticket,
+ &(tp->t_lsn));
+
+#if defined(XLOG_NOLOG) || defined(DEBUG)
+ if (xlog_debug) {
+ commit_lsn = xfs_log_done(mp, tp->t_ticket,
+ &commit_iclog, log_flags);
+ } else {
+ commit_lsn = 0;
+ tp->t_lsn = trans_lsn++;
+ }
+#else
+ /*
+ * This is the regular case. At this point (after the call finishes),
+ * the transaction is committed incore and could go out to disk at
+ * any time. However, all the items associated with the transaction
+ * are still locked and pinned in memory.
+ */
+ commit_lsn = xfs_log_done(mp, tp->t_ticket, &commit_iclog, log_flags);
+#endif
+
+ tp->t_commit_lsn = commit_lsn;
+ if (nvec > XFS_TRANS_LOGVEC_COUNT) {
+ kmem_free(log_vector, nvec * sizeof(xfs_log_iovec_t));
+ }
+
+ if (commit_lsn_p)
+ *commit_lsn_p = commit_lsn;
+
+ /*
+ * If we got a log write error. Unpin the logitems that we
+ * had pinned, clean up, free trans structure, and return error.
+ */
+ if (error || commit_lsn == -1) {
+ PFLAGS_RESTORE_FSTRANS(&tp->t_pflags);
+ xfs_trans_uncommit(tp, flags|XFS_TRANS_ABORT);
+ return XFS_ERROR(EIO);
+ }
+
+ /*
+ * Once the transaction has committed, unused
+ * reservations need to be released and changes to
+ * the superblock need to be reflected in the in-core
+ * version. Do that now.
+ */
+ xfs_trans_unreserve_and_mod_sb(tp);
+
+ sync = tp->t_flags & XFS_TRANS_SYNC;
+
+ /*
+ * Tell the LM to call the transaction completion routine
+ * when the log write with LSN commit_lsn completes (e.g.
+ * when the transaction commit really hits the on-disk log).
+ * After this call we cannot reference tp, because the call
+ * can happen at any time and the call will free the transaction
+ * structure pointed to by tp. The only case where we call
+ * the completion routine (xfs_trans_committed) directly is
+ * if the log is turned off on a debug kernel or we're
+ * running in simulation mode (the log is explicitly turned
+ * off).
+ */
+ tp->t_logcb.cb_func = (void(*)(void*, int))xfs_trans_committed;
+ tp->t_logcb.cb_arg = tp;
+
+ /*
+ * We need to pass the iclog buffer which was used for the
+ * transaction commit record into this function, and attach
+ * the callback to it. The callback must be attached before
+ * the items are unlocked to avoid racing with other threads
+ * waiting for an item to unlock.
+ */
+ shutdown = xfs_log_notify(mp, commit_iclog, &(tp->t_logcb));
+
+ /*
+ * Mark this thread as no longer being in a transaction
+ */
+ PFLAGS_RESTORE_FSTRANS(&tp->t_pflags);
+
+ /*
+ * Once all the items of the transaction have been copied
+ * to the in core log and the callback is attached, the
+ * items can be unlocked.
+ *
+ * This will free descriptors pointing to items which were
+ * not logged since there is nothing more to do with them.
+ * For items which were logged, we will keep pointers to them
+ * so they can be unpinned after the transaction commits to disk.
+ * This will also stamp each modified meta-data item with
+ * the commit lsn of this transaction for dependency tracking
+ * purposes.
+ */
+ xfs_trans_unlock_items(tp, commit_lsn);
+
+ /*
+ * If we detected a log error earlier, finish committing
+ * the transaction now (unpin log items, etc).
+ *
+ * Order is critical here, to avoid using the transaction
+ * pointer after its been freed (by xfs_trans_committed
+ * either here now, or as a callback). We cannot do this
+ * step inside xfs_log_notify as was done earlier because
+ * of this issue.
+ */
+ if (shutdown)
+ xfs_trans_committed(tp, XFS_LI_ABORTED);
+
+ /*
+ * Now that the xfs_trans_committed callback has been attached,
+ * and the items are released we can finally allow the iclog to
+ * go to disk.
+ */
+ error = xfs_log_release_iclog(mp, commit_iclog);
+
+ /*
+ * If the transaction needs to be synchronous, then force the
+ * log out now and wait for it.
+ */
+ if (sync) {
+ if (!error)
+ error = xfs_log_force(mp, commit_lsn,
+ XFS_LOG_FORCE | XFS_LOG_SYNC);
+ XFS_STATS_INC(xs_trans_sync);
+ } else {
+ XFS_STATS_INC(xs_trans_async);
+ }
+
+ return (error);
+}
+
+
+/*
+ * Total up the number of log iovecs needed to commit this
+ * transaction. The transaction itself needs one for the
+ * transaction header. Ask each dirty item in turn how many
+ * it needs to get the total.
+ */
+STATIC uint
+xfs_trans_count_vecs(
+ xfs_trans_t *tp)
+{
+ int nvecs;
+ xfs_log_item_desc_t *lidp;
+
+ nvecs = 1;
+ lidp = xfs_trans_first_item(tp);
+ ASSERT(lidp != NULL);
+
+ /* In the non-debug case we need to start bailing out if we
+ * didn't find a log_item here, return zero and let trans_commit
+ * deal with it.
+ */
+ if (lidp == NULL)
+ return 0;
+
+ while (lidp != NULL) {
+ /*
+ * Skip items which aren't dirty in this transaction.
+ */
+ if (!(lidp->lid_flags & XFS_LID_DIRTY)) {
+ lidp = xfs_trans_next_item(tp, lidp);
+ continue;
+ }
+ lidp->lid_size = IOP_SIZE(lidp->lid_item);
+ nvecs += lidp->lid_size;
+ lidp = xfs_trans_next_item(tp, lidp);
+ }
+
+ return nvecs;
+}
+
+/*
+ * Called from the trans_commit code when we notice that
+ * the filesystem is in the middle of a forced shutdown.
+ */
+STATIC void
+xfs_trans_uncommit(
+ xfs_trans_t *tp,
+ uint flags)
+{
+ xfs_log_item_desc_t *lidp;
+
+ for (lidp = xfs_trans_first_item(tp);
+ lidp != NULL;
+ lidp = xfs_trans_next_item(tp, lidp)) {
+ /*
+ * Unpin all but those that aren't dirty.
+ */
+ if (lidp->lid_flags & XFS_LID_DIRTY)
+ IOP_UNPIN_REMOVE(lidp->lid_item, tp);
+ }
+
+ xfs_trans_unreserve_and_mod_sb(tp);
+ XFS_TRANS_UNRESERVE_AND_MOD_DQUOTS(tp->t_mountp, tp);
+
+ xfs_trans_free_items(tp, flags);
+ xfs_trans_free_busy(tp);
+ xfs_trans_free(tp);
+}
+
+/*
+ * Fill in the vector with pointers to data to be logged
+ * by this transaction. The transaction header takes
+ * the first vector, and then each dirty item takes the
+ * number of vectors it indicated it needed in xfs_trans_count_vecs().
+ *
+ * As each item fills in the entries it needs, also pin the item
+ * so that it cannot be flushed out until the log write completes.
+ */
+STATIC void
+xfs_trans_fill_vecs(
+ xfs_trans_t *tp,
+ xfs_log_iovec_t *log_vector)
+{
+ xfs_log_item_desc_t *lidp;
+ xfs_log_iovec_t *vecp;
+ uint nitems;
+
+ /*
+ * Skip over the entry for the transaction header, we'll
+ * fill that in at the end.
+ */
+ vecp = log_vector + 1; /* pointer arithmetic */
+
+ nitems = 0;
+ lidp = xfs_trans_first_item(tp);
+ ASSERT(lidp != NULL);
+ while (lidp != NULL) {
+ /*
+ * Skip items which aren't dirty in this transaction.
+ */
+ if (!(lidp->lid_flags & XFS_LID_DIRTY)) {
+ lidp = xfs_trans_next_item(tp, lidp);
+ continue;
+ }
+ /*
+ * The item may be marked dirty but not log anything.
+ * This can be used to get called when a transaction
+ * is committed.
+ */
+ if (lidp->lid_size) {
+ nitems++;
+ }
+ IOP_FORMAT(lidp->lid_item, vecp);
+ vecp += lidp->lid_size; /* pointer arithmetic */
+ IOP_PIN(lidp->lid_item);
+ lidp = xfs_trans_next_item(tp, lidp);
+ }
+
+ /*
+ * Now that we've counted the number of items in this
+ * transaction, fill in the transaction header.
+ */
+ tp->t_header.th_magic = XFS_TRANS_HEADER_MAGIC;
+ tp->t_header.th_type = tp->t_type;
+ tp->t_header.th_num_items = nitems;
+ log_vector->i_addr = (xfs_caddr_t)&tp->t_header;
+ log_vector->i_len = sizeof(xfs_trans_header_t);
+}
+
+
+/*
+ * Unlock all of the transaction's items and free the transaction.
+ * The transaction must not have modified any of its items, because
+ * there is no way to restore them to their previous state.
+ *
+ * If the transaction has made a log reservation, make sure to release
+ * it as well.
+ */
+void
+xfs_trans_cancel(
+ xfs_trans_t *tp,
+ int flags)
+{
+ int log_flags;
+#ifdef DEBUG
+ xfs_log_item_chunk_t *licp;
+ xfs_log_item_desc_t *lidp;
+ xfs_log_item_t *lip;
+ int i;
+#endif
+
+ /*
+ * See if the caller is being too lazy to figure out if
+ * the transaction really needs an abort.
+ */
+ if ((flags & XFS_TRANS_ABORT) && !(tp->t_flags & XFS_TRANS_DIRTY))
+ flags &= ~XFS_TRANS_ABORT;
+ /*
+ * See if the caller is relying on us to shut down the
+ * filesystem. This happens in paths where we detect
+ * corruption and decide to give up.
+ */
+ if ((tp->t_flags & XFS_TRANS_DIRTY) &&
+ !XFS_FORCED_SHUTDOWN(tp->t_mountp))
+ xfs_force_shutdown(tp->t_mountp, XFS_CORRUPT_INCORE);
+#ifdef DEBUG
+ if (!(flags & XFS_TRANS_ABORT)) {
+ licp = &(tp->t_items);
+ while (licp != NULL) {
+ lidp = licp->lic_descs;
+ for (i = 0; i < licp->lic_unused; i++, lidp++) {
+ if (XFS_LIC_ISFREE(licp, i)) {
+ continue;
+ }
+
+ lip = lidp->lid_item;
+ if (!XFS_FORCED_SHUTDOWN(tp->t_mountp))
+ ASSERT(!(lip->li_type == XFS_LI_EFD));
+ }
+ licp = licp->lic_next;
+ }
+ }
+#endif
+ xfs_trans_unreserve_and_mod_sb(tp);
+ XFS_TRANS_UNRESERVE_AND_MOD_DQUOTS(tp->t_mountp, tp);
+
+ if (tp->t_ticket) {
+ if (flags & XFS_TRANS_RELEASE_LOG_RES) {
+ ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES);
+ log_flags = XFS_LOG_REL_PERM_RESERV;
+ } else {
+ log_flags = 0;
+ }
+ xfs_log_done(tp->t_mountp, tp->t_ticket, NULL, log_flags);
+ }
+
+ /* mark this thread as no longer being in a transaction */
+ PFLAGS_RESTORE_FSTRANS(&tp->t_pflags);
+
+ xfs_trans_free_items(tp, flags);
+ xfs_trans_free_busy(tp);
+ xfs_trans_free(tp);
+}
+
+
+/*
+ * Free the transaction structure. If there is more clean up
+ * to do when the structure is freed, add it here.
+ */
+STATIC void
+xfs_trans_free(
+ xfs_trans_t *tp)
+{
+ atomic_dec(&tp->t_mountp->m_active_trans);
+ XFS_TRANS_FREE_DQINFO(tp->t_mountp, tp);
+ kmem_zone_free(xfs_trans_zone, tp);
+}
+
+
+/*
+ * THIS SHOULD BE REWRITTEN TO USE xfs_trans_next_item().
+ *
+ * This is typically called by the LM when a transaction has been fully
+ * committed to disk. It needs to unpin the items which have
+ * been logged by the transaction and update their positions
+ * in the AIL if necessary.
+ * This also gets called when the transactions didn't get written out
+ * because of an I/O error. Abortflag & XFS_LI_ABORTED is set then.
+ *
+ * Call xfs_trans_chunk_committed() to process the items in
+ * each chunk.
+ */
+STATIC void
+xfs_trans_committed(
+ xfs_trans_t *tp,
+ int abortflag)
+{
+ xfs_log_item_chunk_t *licp;
+ xfs_log_item_chunk_t *next_licp;
+ xfs_log_busy_chunk_t *lbcp;
+ xfs_log_busy_slot_t *lbsp;
+ int i;
+
+ /*
+ * Call the transaction's completion callback if there
+ * is one.
+ */
+ if (tp->t_callback != NULL) {
+ tp->t_callback(tp, tp->t_callarg);
+ }
+
+ /*
+ * Special case the chunk embedded in the transaction.
+ */
+ licp = &(tp->t_items);
+ if (!(XFS_LIC_ARE_ALL_FREE(licp))) {
+ xfs_trans_chunk_committed(licp, tp->t_lsn, abortflag);
+ }
+
+ /*
+ * Process the items in each chunk in turn.
+ */
+ licp = licp->lic_next;
+ while (licp != NULL) {
+ ASSERT(!XFS_LIC_ARE_ALL_FREE(licp));
+ xfs_trans_chunk_committed(licp, tp->t_lsn, abortflag);
+ next_licp = licp->lic_next;
+ kmem_free(licp, sizeof(xfs_log_item_chunk_t));
+ licp = next_licp;
+ }
+
+ /*
+ * Clear all the per-AG busy list items listed in this transaction
+ */
+ lbcp = &tp->t_busy;
+ while (lbcp != NULL) {
+ for (i = 0, lbsp = lbcp->lbc_busy; i < lbcp->lbc_unused; i++, lbsp++) {
+ if (!XFS_LBC_ISFREE(lbcp, i)) {
+ xfs_alloc_clear_busy(tp, lbsp->lbc_ag,
+ lbsp->lbc_idx);
+ }
+ }
+ lbcp = lbcp->lbc_next;
+ }
+ xfs_trans_free_busy(tp);
+
+ /*
+ * That's it for the transaction structure. Free it.
+ */
+ xfs_trans_free(tp);
+}
+
+/*
+ * This is called to perform the commit processing for each
+ * item described by the given chunk.
+ *
+ * The commit processing consists of unlocking items which were
+ * held locked with the SYNC_UNLOCK attribute, calling the committed
+ * routine of each logged item, updating the item's position in the AIL
+ * if necessary, and unpinning each item. If the committed routine
+ * returns -1, then do nothing further with the item because it
+ * may have been freed.
+ *
+ * Since items are unlocked when they are copied to the incore
+ * log, it is possible for two transactions to be completing
+ * and manipulating the same item simultaneously. The AIL lock
+ * will protect the lsn field of each item. The value of this
+ * field can never go backwards.
+ *
+ * We unpin the items after repositioning them in the AIL, because
+ * otherwise they could be immediately flushed and we'd have to race
+ * with the flusher trying to pull the item from the AIL as we add it.
+ */
+STATIC void
+xfs_trans_chunk_committed(
+ xfs_log_item_chunk_t *licp,
+ xfs_lsn_t lsn,
+ int aborted)
+{
+ xfs_log_item_desc_t *lidp;
+ xfs_log_item_t *lip;
+ xfs_lsn_t item_lsn;
+ struct xfs_mount *mp;
+ int i;
+ SPLDECL(s);
+
+ lidp = licp->lic_descs;
+ for (i = 0; i < licp->lic_unused; i++, lidp++) {
+ if (XFS_LIC_ISFREE(licp, i)) {
+ continue;
+ }
+
+ lip = lidp->lid_item;
+ if (aborted)
+ lip->li_flags |= XFS_LI_ABORTED;
+
+ /*
+ * Send in the ABORTED flag to the COMMITTED routine
+ * so that it knows whether the transaction was aborted
+ * or not.
+ */
+ item_lsn = IOP_COMMITTED(lip, lsn);
+
+ /*
+ * If the committed routine returns -1, make
+ * no more references to the item.
+ */
+ if (XFS_LSN_CMP(item_lsn, (xfs_lsn_t)-1) == 0) {
+ continue;
+ }
+
+ /*
+ * If the returned lsn is greater than what it
+ * contained before, update the location of the
+ * item in the AIL. If it is not, then do nothing.
+ * Items can never move backwards in the AIL.
+ *
+ * While the new lsn should usually be greater, it
+ * is possible that a later transaction completing
+ * simultaneously with an earlier one using the
+ * same item could complete first with a higher lsn.
+ * This would cause the earlier transaction to fail
+ * the test below.
+ */
+ mp = lip->li_mountp;
+ AIL_LOCK(mp,s);
+ if (XFS_LSN_CMP(item_lsn, lip->li_lsn) > 0) {
+ /*
+ * This will set the item's lsn to item_lsn
+ * and update the position of the item in
+ * the AIL.
+ *
+ * xfs_trans_update_ail() drops the AIL lock.
+ */
+ xfs_trans_update_ail(mp, lip, item_lsn, s);
+ } else {
+ AIL_UNLOCK(mp, s);
+ }
+
+ /*
+ * Now that we've repositioned the item in the AIL,
+ * unpin it so it can be flushed. Pass information
+ * about buffer stale state down from the log item
+ * flags, if anyone else stales the buffer we do not
+ * want to pay any attention to it.
+ */
+ IOP_UNPIN(lip, lidp->lid_flags & XFS_LID_BUF_STALE);
+ }
+}
diff --git a/fs/xfs/xfs_trans.h b/fs/xfs/xfs_trans.h
new file mode 100644
index 00000000000000..bd37ccb85e76d3
--- /dev/null
+++ b/fs/xfs/xfs_trans.h
@@ -0,0 +1,1042 @@
+/*
+ * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_TRANS_H__
+#define __XFS_TRANS_H__
+
+/*
+ * This is the structure written in the log at the head of
+ * every transaction. It identifies the type and id of the
+ * transaction, and contains the number of items logged by
+ * the transaction so we know how many to expect during recovery.
+ *
+ * Do not change the below structure without redoing the code in
+ * xlog_recover_add_to_trans() and xlog_recover_add_to_cont_trans().
+ */
+typedef struct xfs_trans_header {
+ uint th_magic; /* magic number */
+ uint th_type; /* transaction type */
+ __int32_t th_tid; /* transaction id (unused) */
+ uint th_num_items; /* num items logged by trans */
+} xfs_trans_header_t;
+
+#define XFS_TRANS_HEADER_MAGIC 0x5452414e /* TRAN */
+
+/*
+ * Log item types.
+ */
+#define XFS_LI_5_3_BUF 0x1234 /* v1 bufs, 1-block inode buffers */
+#define XFS_LI_5_3_INODE 0x1235 /* 1-block inode buffers */
+#define XFS_LI_EFI 0x1236
+#define XFS_LI_EFD 0x1237
+#define XFS_LI_IUNLINK 0x1238
+#define XFS_LI_6_1_INODE 0x1239 /* 4K non-aligned inode bufs */
+#define XFS_LI_6_1_BUF 0x123a /* v1, 4K inode buffers */
+#define XFS_LI_INODE 0x123b /* aligned ino chunks, var-size ibufs */
+#define XFS_LI_BUF 0x123c /* v2 bufs, variable sized inode bufs */
+#define XFS_LI_DQUOT 0x123d
+#define XFS_LI_QUOTAOFF 0x123e
+
+/*
+ * Transaction types. Used to distinguish types of buffers.
+ */
+#define XFS_TRANS_SETATTR_NOT_SIZE 1
+#define XFS_TRANS_SETATTR_SIZE 2
+#define XFS_TRANS_INACTIVE 3
+#define XFS_TRANS_CREATE 4
+#define XFS_TRANS_CREATE_TRUNC 5
+#define XFS_TRANS_TRUNCATE_FILE 6
+#define XFS_TRANS_REMOVE 7
+#define XFS_TRANS_LINK 8
+#define XFS_TRANS_RENAME 9
+#define XFS_TRANS_MKDIR 10
+#define XFS_TRANS_RMDIR 11
+#define XFS_TRANS_SYMLINK 12
+#define XFS_TRANS_SET_DMATTRS 13
+#define XFS_TRANS_GROWFS 14
+#define XFS_TRANS_STRAT_WRITE 15
+#define XFS_TRANS_DIOSTRAT 16
+#define XFS_TRANS_WRITE_SYNC 17
+#define XFS_TRANS_WRITEID 18
+#define XFS_TRANS_ADDAFORK 19
+#define XFS_TRANS_ATTRINVAL 20
+#define XFS_TRANS_ATRUNCATE 21
+#define XFS_TRANS_ATTR_SET 22
+#define XFS_TRANS_ATTR_RM 23
+#define XFS_TRANS_ATTR_FLAG 24
+#define XFS_TRANS_CLEAR_AGI_BUCKET 25
+#define XFS_TRANS_QM_SBCHANGE 26
+/*
+ * Dummy entries since we use the transaction type to index into the
+ * trans_type[] in xlog_recover_print_trans_head()
+ */
+#define XFS_TRANS_DUMMY1 27
+#define XFS_TRANS_DUMMY2 28
+#define XFS_TRANS_QM_QUOTAOFF 29
+#define XFS_TRANS_QM_DQALLOC 30
+#define XFS_TRANS_QM_SETQLIM 31
+#define XFS_TRANS_QM_DQCLUSTER 32
+#define XFS_TRANS_QM_QINOCREATE 33
+#define XFS_TRANS_QM_QUOTAOFF_END 34
+#define XFS_TRANS_SB_UNIT 35
+#define XFS_TRANS_FSYNC_TS 36
+#define XFS_TRANS_GROWFSRT_ALLOC 37
+#define XFS_TRANS_GROWFSRT_ZERO 38
+#define XFS_TRANS_GROWFSRT_FREE 39
+#define XFS_TRANS_SWAPEXT 40
+/* new transaction types need to be reflected in xfs_logprint(8) */
+
+
+#ifdef __KERNEL__
+struct xfs_buf;
+struct xfs_buftarg;
+struct xfs_efd_log_item;
+struct xfs_efi_log_item;
+struct xfs_inode;
+struct xfs_item_ops;
+struct xfs_log_iovec;
+struct xfs_log_item;
+struct xfs_log_item_desc;
+struct xfs_mount;
+struct xfs_trans;
+struct xfs_dquot_acct;
+
+typedef struct xfs_ail_entry {
+ struct xfs_log_item *ail_forw; /* AIL forw pointer */
+ struct xfs_log_item *ail_back; /* AIL back pointer */
+} xfs_ail_entry_t;
+
+/*
+ * This structure is passed as a parameter to xfs_trans_push_ail()
+ * and is used to track the what LSN the waiting processes are
+ * waiting to become unused.
+ */
+typedef struct xfs_ail_ticket {
+ xfs_lsn_t at_lsn; /* lsn waitin for */
+ struct xfs_ail_ticket *at_forw; /* wait list ptr */
+ struct xfs_ail_ticket *at_back; /* wait list ptr */
+ sv_t at_sema; /* wait sema */
+} xfs_ail_ticket_t;
+
+
+typedef struct xfs_log_item {
+ xfs_ail_entry_t li_ail; /* AIL pointers */
+ xfs_lsn_t li_lsn; /* last on-disk lsn */
+ struct xfs_log_item_desc *li_desc; /* ptr to current desc*/
+ struct xfs_mount *li_mountp; /* ptr to fs mount */
+ uint li_type; /* item type */
+ uint li_flags; /* misc flags */
+ struct xfs_log_item *li_bio_list; /* buffer item list */
+ void (*li_cb)(struct xfs_buf *,
+ struct xfs_log_item *);
+ /* buffer item iodone */
+ /* callback func */
+ struct xfs_item_ops *li_ops; /* function list */
+} xfs_log_item_t;
+
+#define XFS_LI_IN_AIL 0x1
+#define XFS_LI_ABORTED 0x2
+
+typedef struct xfs_item_ops {
+ uint (*iop_size)(xfs_log_item_t *);
+ void (*iop_format)(xfs_log_item_t *, struct xfs_log_iovec *);
+ void (*iop_pin)(xfs_log_item_t *);
+ void (*iop_unpin)(xfs_log_item_t *, int);
+ void (*iop_unpin_remove)(xfs_log_item_t *, struct xfs_trans *);
+ uint (*iop_trylock)(xfs_log_item_t *);
+ void (*iop_unlock)(xfs_log_item_t *);
+ xfs_lsn_t (*iop_committed)(xfs_log_item_t *, xfs_lsn_t);
+ void (*iop_push)(xfs_log_item_t *);
+ void (*iop_abort)(xfs_log_item_t *);
+ void (*iop_pushbuf)(xfs_log_item_t *);
+ void (*iop_committing)(xfs_log_item_t *, xfs_lsn_t);
+} xfs_item_ops_t;
+
+#define IOP_SIZE(ip) (*(ip)->li_ops->iop_size)(ip)
+#define IOP_FORMAT(ip,vp) (*(ip)->li_ops->iop_format)(ip, vp)
+#define IOP_PIN(ip) (*(ip)->li_ops->iop_pin)(ip)
+#define IOP_UNPIN(ip, flags) (*(ip)->li_ops->iop_unpin)(ip, flags)
+#define IOP_UNPIN_REMOVE(ip,tp) (*(ip)->li_ops->iop_unpin_remove)(ip, tp)
+#define IOP_TRYLOCK(ip) (*(ip)->li_ops->iop_trylock)(ip)
+#define IOP_UNLOCK(ip) (*(ip)->li_ops->iop_unlock)(ip)
+#define IOP_COMMITTED(ip, lsn) (*(ip)->li_ops->iop_committed)(ip, lsn)
+#define IOP_PUSH(ip) (*(ip)->li_ops->iop_push)(ip)
+#define IOP_ABORT(ip) (*(ip)->li_ops->iop_abort)(ip)
+#define IOP_PUSHBUF(ip) (*(ip)->li_ops->iop_pushbuf)(ip)
+#define IOP_COMMITTING(ip, lsn) (*(ip)->li_ops->iop_committing)(ip, lsn)
+
+/*
+ * Return values for the IOP_TRYLOCK() routines.
+ */
+#define XFS_ITEM_SUCCESS 0
+#define XFS_ITEM_PINNED 1
+#define XFS_ITEM_LOCKED 2
+#define XFS_ITEM_FLUSHING 3
+#define XFS_ITEM_PUSHBUF 4
+
+#endif /* __KERNEL__ */
+
+/*
+ * This structure is used to track log items associated with
+ * a transaction. It points to the log item and keeps some
+ * flags to track the state of the log item. It also tracks
+ * the amount of space needed to log the item it describes
+ * once we get to commit processing (see xfs_trans_commit()).
+ */
+typedef struct xfs_log_item_desc {
+ xfs_log_item_t *lid_item;
+ ushort lid_size;
+ unsigned char lid_flags;
+ unsigned char lid_index;
+} xfs_log_item_desc_t;
+
+#define XFS_LID_DIRTY 0x1
+#define XFS_LID_PINNED 0x2
+#define XFS_LID_BUF_STALE 0x8
+
+/*
+ * This structure is used to maintain a chunk list of log_item_desc
+ * structures. The free field is a bitmask indicating which descriptors
+ * in this chunk's array are free. The unused field is the first value
+ * not used since this chunk was allocated.
+ */
+#define XFS_LIC_NUM_SLOTS 15
+typedef struct xfs_log_item_chunk {
+ struct xfs_log_item_chunk *lic_next;
+ ushort lic_free;
+ ushort lic_unused;
+ xfs_log_item_desc_t lic_descs[XFS_LIC_NUM_SLOTS];
+} xfs_log_item_chunk_t;
+
+#define XFS_LIC_MAX_SLOT (XFS_LIC_NUM_SLOTS - 1)
+#define XFS_LIC_FREEMASK ((1 << XFS_LIC_NUM_SLOTS) - 1)
+
+
+/*
+ * Initialize the given chunk. Set the chunk's free descriptor mask
+ * to indicate that all descriptors are free. The caller gets to set
+ * lic_unused to the right value (0 matches all free). The
+ * lic_descs.lid_index values are set up as each desc is allocated.
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_LIC_INIT)
+void xfs_lic_init(xfs_log_item_chunk_t *cp);
+#define XFS_LIC_INIT(cp) xfs_lic_init(cp)
+#else
+#define XFS_LIC_INIT(cp) ((cp)->lic_free = XFS_LIC_FREEMASK)
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_LIC_INIT_SLOT)
+void xfs_lic_init_slot(xfs_log_item_chunk_t *cp, int slot);
+#define XFS_LIC_INIT_SLOT(cp,slot) xfs_lic_init_slot(cp, slot)
+#else
+#define XFS_LIC_INIT_SLOT(cp,slot) \
+ ((cp)->lic_descs[slot].lid_index = (unsigned char)(slot))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_LIC_VACANCY)
+int xfs_lic_vacancy(xfs_log_item_chunk_t *cp);
+#define XFS_LIC_VACANCY(cp) xfs_lic_vacancy(cp)
+#else
+#define XFS_LIC_VACANCY(cp) (((cp)->lic_free) & XFS_LIC_FREEMASK)
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_LIC_ALL_FREE)
+void xfs_lic_all_free(xfs_log_item_chunk_t *cp);
+#define XFS_LIC_ALL_FREE(cp) xfs_lic_all_free(cp)
+#else
+#define XFS_LIC_ALL_FREE(cp) ((cp)->lic_free = XFS_LIC_FREEMASK)
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_LIC_ARE_ALL_FREE)
+int xfs_lic_are_all_free(xfs_log_item_chunk_t *cp);
+#define XFS_LIC_ARE_ALL_FREE(cp) xfs_lic_are_all_free(cp)
+#else
+#define XFS_LIC_ARE_ALL_FREE(cp) (((cp)->lic_free & XFS_LIC_FREEMASK) ==\
+ XFS_LIC_FREEMASK)
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_LIC_ISFREE)
+int xfs_lic_isfree(xfs_log_item_chunk_t *cp, int slot);
+#define XFS_LIC_ISFREE(cp,slot) xfs_lic_isfree(cp,slot)
+#else
+#define XFS_LIC_ISFREE(cp,slot) ((cp)->lic_free & (1 << (slot)))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_LIC_CLAIM)
+void xfs_lic_claim(xfs_log_item_chunk_t *cp, int slot);
+#define XFS_LIC_CLAIM(cp,slot) xfs_lic_claim(cp,slot)
+#else
+#define XFS_LIC_CLAIM(cp,slot) ((cp)->lic_free &= ~(1 << (slot)))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_LIC_RELSE)
+void xfs_lic_relse(xfs_log_item_chunk_t *cp, int slot);
+#define XFS_LIC_RELSE(cp,slot) xfs_lic_relse(cp,slot)
+#else
+#define XFS_LIC_RELSE(cp,slot) ((cp)->lic_free |= 1 << (slot))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_LIC_SLOT)
+xfs_log_item_desc_t *xfs_lic_slot(xfs_log_item_chunk_t *cp, int slot);
+#define XFS_LIC_SLOT(cp,slot) xfs_lic_slot(cp,slot)
+#else
+#define XFS_LIC_SLOT(cp,slot) (&((cp)->lic_descs[slot]))
+#endif
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_LIC_DESC_TO_SLOT)
+int xfs_lic_desc_to_slot(xfs_log_item_desc_t *dp);
+#define XFS_LIC_DESC_TO_SLOT(dp) xfs_lic_desc_to_slot(dp)
+#else
+#define XFS_LIC_DESC_TO_SLOT(dp) ((uint)((dp)->lid_index))
+#endif
+/*
+ * Calculate the address of a chunk given a descriptor pointer:
+ * dp - dp->lid_index give the address of the start of the lic_descs array.
+ * From this we subtract the offset of the lic_descs field in a chunk.
+ * All of this yields the address of the chunk, which is
+ * cast to a chunk pointer.
+ */
+#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_LIC_DESC_TO_CHUNK)
+xfs_log_item_chunk_t *xfs_lic_desc_to_chunk(xfs_log_item_desc_t *dp);
+#define XFS_LIC_DESC_TO_CHUNK(dp) xfs_lic_desc_to_chunk(dp)
+#else
+#define XFS_LIC_DESC_TO_CHUNK(dp) ((xfs_log_item_chunk_t*) \
+ (((xfs_caddr_t)((dp) - (dp)->lid_index)) -\
+ (xfs_caddr_t)(((xfs_log_item_chunk_t*) \
+ 0)->lic_descs)))
+#endif
+
+#ifdef __KERNEL__
+/*
+ * This structure is used to maintain a list of block ranges that have been
+ * freed in the transaction. The ranges are listed in the perag[] busy list
+ * between when they're freed and the transaction is committed to disk.
+ */
+
+typedef struct xfs_log_busy_slot {
+ xfs_agnumber_t lbc_ag;
+ ushort lbc_idx; /* index in perag.busy[] */
+} xfs_log_busy_slot_t;
+
+#define XFS_LBC_NUM_SLOTS 31
+typedef struct xfs_log_busy_chunk {
+ struct xfs_log_busy_chunk *lbc_next;
+ uint lbc_free; /* bitmask of free slots */
+ ushort lbc_unused; /* first unused */
+ xfs_log_busy_slot_t lbc_busy[XFS_LBC_NUM_SLOTS];
+} xfs_log_busy_chunk_t;
+
+#define XFS_LBC_MAX_SLOT (XFS_LBC_NUM_SLOTS - 1)
+#define XFS_LBC_FREEMASK ((1U << XFS_LBC_NUM_SLOTS) - 1)
+
+#define XFS_LBC_INIT(cp) ((cp)->lbc_free = XFS_LBC_FREEMASK)
+#define XFS_LBC_CLAIM(cp, slot) ((cp)->lbc_free &= ~(1 << (slot)))
+#define XFS_LBC_SLOT(cp, slot) (&((cp)->lbc_busy[(slot)]))
+#define XFS_LBC_VACANCY(cp) (((cp)->lbc_free) & XFS_LBC_FREEMASK)
+#define XFS_LBC_ISFREE(cp, slot) ((cp)->lbc_free & (1 << (slot)))
+
+/*
+ * This is the type of function which can be given to xfs_trans_callback()
+ * to be called upon the transaction's commit to disk.
+ */
+typedef void (*xfs_trans_callback_t)(struct xfs_trans *, void *);
+
+/*
+ * This is the structure maintained for every active transaction.
+ */
+typedef struct xfs_trans {
+ unsigned int t_magic; /* magic number */
+ xfs_log_callback_t t_logcb; /* log callback struct */
+ struct xfs_trans *t_forw; /* async list pointers */
+ struct xfs_trans *t_back; /* async list pointers */
+ unsigned int t_type; /* transaction type */
+ unsigned int t_log_res; /* amt of log space resvd */
+ unsigned int t_log_count; /* count for perm log res */
+ unsigned int t_blk_res; /* # of blocks resvd */
+ unsigned int t_blk_res_used; /* # of resvd blocks used */
+ unsigned int t_rtx_res; /* # of rt extents resvd */
+ unsigned int t_rtx_res_used; /* # of resvd rt extents used */
+ xfs_log_ticket_t t_ticket; /* log mgr ticket */
+ sema_t t_sema; /* sema for commit completion */
+ xfs_lsn_t t_lsn; /* log seq num of start of
+ * transaction. */
+ xfs_lsn_t t_commit_lsn; /* log seq num of end of
+ * transaction. */
+ struct xfs_mount *t_mountp; /* ptr to fs mount struct */
+ struct xfs_dquot_acct *t_dqinfo; /* accting info for dquots */
+ xfs_trans_callback_t t_callback; /* transaction callback */
+ void *t_callarg; /* callback arg */
+ unsigned int t_flags; /* misc flags */
+ long t_icount_delta; /* superblock icount change */
+ long t_ifree_delta; /* superblock ifree change */
+ long t_fdblocks_delta; /* superblock fdblocks chg */
+ long t_res_fdblocks_delta; /* on-disk only chg */
+ long t_frextents_delta;/* superblock freextents chg*/
+ long t_res_frextents_delta; /* on-disk only chg */
+ long t_ag_freeblks_delta; /* debugging counter */
+ long t_ag_flist_delta; /* debugging counter */
+ long t_ag_btree_delta; /* debugging counter */
+ long t_dblocks_delta;/* superblock dblocks change */
+ long t_agcount_delta;/* superblock agcount change */
+ long t_imaxpct_delta;/* superblock imaxpct change */
+ long t_rextsize_delta;/* superblock rextsize chg */
+ long t_rbmblocks_delta;/* superblock rbmblocks chg */
+ long t_rblocks_delta;/* superblock rblocks change */
+ long t_rextents_delta;/* superblocks rextents chg */
+ long t_rextslog_delta;/* superblocks rextslog chg */
+ unsigned int t_items_free; /* log item descs free */
+ xfs_log_item_chunk_t t_items; /* first log item desc chunk */
+ xfs_trans_header_t t_header; /* header for in-log trans */
+ unsigned int t_busy_free; /* busy descs free */
+ xfs_log_busy_chunk_t t_busy; /* busy/async free blocks */
+ xfs_pflags_t t_pflags; /* saved pflags state */
+} xfs_trans_t;
+
+#endif /* __KERNEL__ */
+
+
+#define XFS_TRANS_MAGIC 0x5452414E /* 'TRAN' */
+/*
+ * Values for t_flags.
+ */
+#define XFS_TRANS_DIRTY 0x01 /* something needs to be logged */
+#define XFS_TRANS_SB_DIRTY 0x02 /* superblock is modified */
+#define XFS_TRANS_PERM_LOG_RES 0x04 /* xact took a permanent log res */
+#define XFS_TRANS_SYNC 0x08 /* make commit synchronous */
+#define XFS_TRANS_DQ_DIRTY 0x10 /* at least one dquot in trx dirty */
+#define XFS_TRANS_RESERVE 0x20 /* OK to use reserved data blocks */
+
+/*
+ * Values for call flags parameter.
+ */
+#define XFS_TRANS_NOSLEEP 0x1
+#define XFS_TRANS_WAIT 0x2
+#define XFS_TRANS_RELEASE_LOG_RES 0x4
+#define XFS_TRANS_ABORT 0x8
+
+/*
+ * Field values for xfs_trans_mod_sb.
+ */
+#define XFS_TRANS_SB_ICOUNT 0x00000001
+#define XFS_TRANS_SB_IFREE 0x00000002
+#define XFS_TRANS_SB_FDBLOCKS 0x00000004
+#define XFS_TRANS_SB_RES_FDBLOCKS 0x00000008
+#define XFS_TRANS_SB_FREXTENTS 0x00000010
+#define XFS_TRANS_SB_RES_FREXTENTS 0x00000020
+#define XFS_TRANS_SB_DBLOCKS 0x00000040
+#define XFS_TRANS_SB_AGCOUNT 0x00000080
+#define XFS_TRANS_SB_IMAXPCT 0x00000100
+#define XFS_TRANS_SB_REXTSIZE 0x00000200
+#define XFS_TRANS_SB_RBMBLOCKS 0x00000400
+#define XFS_TRANS_SB_RBLOCKS 0x00000800
+#define XFS_TRANS_SB_REXTENTS 0x00001000
+#define XFS_TRANS_SB_REXTSLOG 0x00002000
+
+
+/*
+ * Various log reservation values.
+ * These are based on the size of the file system block
+ * because that is what most transactions manipulate.
+ * Each adds in an additional 128 bytes per item logged to
+ * try to account for the overhead of the transaction mechanism.
+ *
+ * Note:
+ * Most of the reservations underestimate the number of allocation
+ * groups into which they could free extents in the xfs_bmap_finish()
+ * call. This is because the number in the worst case is quite high
+ * and quite unusual. In order to fix this we need to change
+ * xfs_bmap_finish() to free extents in only a single AG at a time.
+ * This will require changes to the EFI code as well, however, so that
+ * the EFI for the extents not freed is logged again in each transaction.
+ * See bug 261917.
+ */
+
+/*
+ * Per-extent log reservation for the allocation btree changes
+ * involved in freeing or allocating an extent.
+ * 2 trees * (2 blocks/level * max depth - 1) * block size
+ */
+#define XFS_ALLOCFREE_LOG_RES(mp,nx) \
+ ((nx) * (2 * XFS_FSB_TO_B((mp), 2 * XFS_AG_MAXLEVELS(mp) - 1)))
+#define XFS_ALLOCFREE_LOG_COUNT(mp,nx) \
+ ((nx) * (2 * (2 * XFS_AG_MAXLEVELS(mp) - 1)))
+
+/*
+ * Per-directory log reservation for any directory change.
+ * dir blocks: (1 btree block per level + data block + free block) * dblock size
+ * bmap btree: (levels + 2) * max depth * block size
+ * v2 directory blocks can be fragmented below the dirblksize down to the fsb
+ * size, so account for that in the DAENTER macros.
+ */
+#define XFS_DIROP_LOG_RES(mp) \
+ (XFS_FSB_TO_B(mp, XFS_DAENTER_BLOCKS(mp, XFS_DATA_FORK)) + \
+ (XFS_FSB_TO_B(mp, XFS_DAENTER_BMAPS(mp, XFS_DATA_FORK) + 1)))
+#define XFS_DIROP_LOG_COUNT(mp) \
+ (XFS_DAENTER_BLOCKS(mp, XFS_DATA_FORK) + \
+ XFS_DAENTER_BMAPS(mp, XFS_DATA_FORK) + 1)
+
+/*
+ * In a write transaction we can allocate a maximum of 2
+ * extents. This gives:
+ * the inode getting the new extents: inode size
+ * the inode\'s bmap btree: max depth * block size
+ * the agfs of the ags from which the extents are allocated: 2 * sector
+ * the superblock free block counter: sector size
+ * the allocation btrees: 2 exts * 2 trees * (2 * max depth - 1) * block size
+ * And the bmap_finish transaction can free bmap blocks in a join:
+ * the agfs of the ags containing the blocks: 2 * sector size
+ * the agfls of the ags containing the blocks: 2 * sector size
+ * the super block free block counter: sector size
+ * the allocation btrees: 2 exts * 2 trees * (2 * max depth - 1) * block size
+ */
+#define XFS_CALC_WRITE_LOG_RES(mp) \
+ (MAX( \
+ ((mp)->m_sb.sb_inodesize + \
+ XFS_FSB_TO_B((mp), XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK)) + \
+ (2 * (mp)->m_sb.sb_sectsize) + \
+ (mp)->m_sb.sb_sectsize + \
+ XFS_ALLOCFREE_LOG_RES(mp, 2) + \
+ (128 * (4 + XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK) + XFS_ALLOCFREE_LOG_COUNT(mp, 2)))),\
+ ((2 * (mp)->m_sb.sb_sectsize) + \
+ (2 * (mp)->m_sb.sb_sectsize) + \
+ (mp)->m_sb.sb_sectsize + \
+ XFS_ALLOCFREE_LOG_RES(mp, 2) + \
+ (128 * (5 + XFS_ALLOCFREE_LOG_COUNT(mp, 2))))))
+
+#define XFS_WRITE_LOG_RES(mp) ((mp)->m_reservations.tr_write)
+
+/*
+ * In truncating a file we free up to two extents at once. We can modify:
+ * the inode being truncated: inode size
+ * the inode\'s bmap btree: (max depth + 1) * block size
+ * And the bmap_finish transaction can free the blocks and bmap blocks:
+ * the agf for each of the ags: 4 * sector size
+ * the agfl for each of the ags: 4 * sector size
+ * the super block to reflect the freed blocks: sector size
+ * worst case split in allocation btrees per extent assuming 4 extents:
+ * 4 exts * 2 trees * (2 * max depth - 1) * block size
+ * the inode btree: max depth * blocksize
+ * the allocation btrees: 2 trees * (max depth - 1) * block size
+ */
+#define XFS_CALC_ITRUNCATE_LOG_RES(mp) \
+ (MAX( \
+ ((mp)->m_sb.sb_inodesize + \
+ XFS_FSB_TO_B((mp), XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK) + 1) + \
+ (128 * (2 + XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK)))), \
+ ((4 * (mp)->m_sb.sb_sectsize) + \
+ (4 * (mp)->m_sb.sb_sectsize) + \
+ (mp)->m_sb.sb_sectsize + \
+ XFS_ALLOCFREE_LOG_RES(mp, 4) + \
+ (128 * (9 + XFS_ALLOCFREE_LOG_COUNT(mp, 4))) + \
+ (128 * 5) + \
+ XFS_ALLOCFREE_LOG_RES(mp, 1) + \
+ (128 * (2 + XFS_IALLOC_BLOCKS(mp) + XFS_IN_MAXLEVELS(mp) + \
+ XFS_ALLOCFREE_LOG_COUNT(mp, 1))))))
+
+#define XFS_ITRUNCATE_LOG_RES(mp) ((mp)->m_reservations.tr_itruncate)
+
+/*
+ * In renaming a files we can modify:
+ * the four inodes involved: 4 * inode size
+ * the two directory btrees: 2 * (max depth + v2) * dir block size
+ * the two directory bmap btrees: 2 * max depth * block size
+ * And the bmap_finish transaction can free dir and bmap blocks (two sets
+ * of bmap blocks) giving:
+ * the agf for the ags in which the blocks live: 3 * sector size
+ * the agfl for the ags in which the blocks live: 3 * sector size
+ * the superblock for the free block count: sector size
+ * the allocation btrees: 3 exts * 2 trees * (2 * max depth - 1) * block size
+ */
+#define XFS_CALC_RENAME_LOG_RES(mp) \
+ (MAX( \
+ ((4 * (mp)->m_sb.sb_inodesize) + \
+ (2 * XFS_DIROP_LOG_RES(mp)) + \
+ (128 * (4 + 2 * XFS_DIROP_LOG_COUNT(mp)))), \
+ ((3 * (mp)->m_sb.sb_sectsize) + \
+ (3 * (mp)->m_sb.sb_sectsize) + \
+ (mp)->m_sb.sb_sectsize + \
+ XFS_ALLOCFREE_LOG_RES(mp, 3) + \
+ (128 * (7 + XFS_ALLOCFREE_LOG_COUNT(mp, 3))))))
+
+#define XFS_RENAME_LOG_RES(mp) ((mp)->m_reservations.tr_rename)
+
+/*
+ * For creating a link to an inode:
+ * the parent directory inode: inode size
+ * the linked inode: inode size
+ * the directory btree could split: (max depth + v2) * dir block size
+ * the directory bmap btree could join or split: (max depth + v2) * blocksize
+ * And the bmap_finish transaction can free some bmap blocks giving:
+ * the agf for the ag in which the blocks live: sector size
+ * the agfl for the ag in which the blocks live: sector size
+ * the superblock for the free block count: sector size
+ * the allocation btrees: 2 trees * (2 * max depth - 1) * block size
+ */
+#define XFS_CALC_LINK_LOG_RES(mp) \
+ (MAX( \
+ ((mp)->m_sb.sb_inodesize + \
+ (mp)->m_sb.sb_inodesize + \
+ XFS_DIROP_LOG_RES(mp) + \
+ (128 * (2 + XFS_DIROP_LOG_COUNT(mp)))), \
+ ((mp)->m_sb.sb_sectsize + \
+ (mp)->m_sb.sb_sectsize + \
+ (mp)->m_sb.sb_sectsize + \
+ XFS_ALLOCFREE_LOG_RES(mp, 1) + \
+ (128 * (3 + XFS_ALLOCFREE_LOG_COUNT(mp, 1))))))
+
+#define XFS_LINK_LOG_RES(mp) ((mp)->m_reservations.tr_link)
+
+/*
+ * For removing a directory entry we can modify:
+ * the parent directory inode: inode size
+ * the removed inode: inode size
+ * the directory btree could join: (max depth + v2) * dir block size
+ * the directory bmap btree could join or split: (max depth + v2) * blocksize
+ * And the bmap_finish transaction can free the dir and bmap blocks giving:
+ * the agf for the ag in which the blocks live: 2 * sector size
+ * the agfl for the ag in which the blocks live: 2 * sector size
+ * the superblock for the free block count: sector size
+ * the allocation btrees: 2 exts * 2 trees * (2 * max depth - 1) * block size
+ */
+#define XFS_CALC_REMOVE_LOG_RES(mp) \
+ (MAX( \
+ ((mp)->m_sb.sb_inodesize + \
+ (mp)->m_sb.sb_inodesize + \
+ XFS_DIROP_LOG_RES(mp) + \
+ (128 * (2 + XFS_DIROP_LOG_COUNT(mp)))), \
+ ((2 * (mp)->m_sb.sb_sectsize) + \
+ (2 * (mp)->m_sb.sb_sectsize) + \
+ (mp)->m_sb.sb_sectsize + \
+ XFS_ALLOCFREE_LOG_RES(mp, 2) + \
+ (128 * (5 + XFS_ALLOCFREE_LOG_COUNT(mp, 2))))))
+
+#define XFS_REMOVE_LOG_RES(mp) ((mp)->m_reservations.tr_remove)
+
+/*
+ * For symlink we can modify:
+ * the parent directory inode: inode size
+ * the new inode: inode size
+ * the inode btree entry: 1 block
+ * the directory btree: (max depth + v2) * dir block size
+ * the directory inode\'s bmap btree: (max depth + v2) * block size
+ * the blocks for the symlink: 1 KB
+ * Or in the first xact we allocate some inodes giving:
+ * the agi and agf of the ag getting the new inodes: 2 * sectorsize
+ * the inode blocks allocated: XFS_IALLOC_BLOCKS * blocksize
+ * the inode btree: max depth * blocksize
+ * the allocation btrees: 2 trees * (2 * max depth - 1) * block size
+ */
+#define XFS_CALC_SYMLINK_LOG_RES(mp) \
+ (MAX( \
+ ((mp)->m_sb.sb_inodesize + \
+ (mp)->m_sb.sb_inodesize + \
+ XFS_FSB_TO_B(mp, 1) + \
+ XFS_DIROP_LOG_RES(mp) + \
+ 1024 + \
+ (128 * (4 + XFS_DIROP_LOG_COUNT(mp)))), \
+ (2 * (mp)->m_sb.sb_sectsize + \
+ XFS_FSB_TO_B((mp), XFS_IALLOC_BLOCKS((mp))) + \
+ XFS_FSB_TO_B((mp), XFS_IN_MAXLEVELS(mp)) + \
+ XFS_ALLOCFREE_LOG_RES(mp, 1) + \
+ (128 * (2 + XFS_IALLOC_BLOCKS(mp) + XFS_IN_MAXLEVELS(mp) + \
+ XFS_ALLOCFREE_LOG_COUNT(mp, 1))))))
+
+#define XFS_SYMLINK_LOG_RES(mp) ((mp)->m_reservations.tr_symlink)
+
+/*
+ * For create we can modify:
+ * the parent directory inode: inode size
+ * the new inode: inode size
+ * the inode btree entry: block size
+ * the superblock for the nlink flag: sector size
+ * the directory btree: (max depth + v2) * dir block size
+ * the directory inode\'s bmap btree: (max depth + v2) * block size
+ * Or in the first xact we allocate some inodes giving:
+ * the agi and agf of the ag getting the new inodes: 2 * sectorsize
+ * the superblock for the nlink flag: sector size
+ * the inode blocks allocated: XFS_IALLOC_BLOCKS * blocksize
+ * the inode btree: max depth * blocksize
+ * the allocation btrees: 2 trees * (max depth - 1) * block size
+ */
+#define XFS_CALC_CREATE_LOG_RES(mp) \
+ (MAX( \
+ ((mp)->m_sb.sb_inodesize + \
+ (mp)->m_sb.sb_inodesize + \
+ (mp)->m_sb.sb_sectsize + \
+ XFS_FSB_TO_B(mp, 1) + \
+ XFS_DIROP_LOG_RES(mp) + \
+ (128 * (3 + XFS_DIROP_LOG_COUNT(mp)))), \
+ (3 * (mp)->m_sb.sb_sectsize + \
+ XFS_FSB_TO_B((mp), XFS_IALLOC_BLOCKS((mp))) + \
+ XFS_FSB_TO_B((mp), XFS_IN_MAXLEVELS(mp)) + \
+ XFS_ALLOCFREE_LOG_RES(mp, 1) + \
+ (128 * (2 + XFS_IALLOC_BLOCKS(mp) + XFS_IN_MAXLEVELS(mp) + \
+ XFS_ALLOCFREE_LOG_COUNT(mp, 1))))))
+
+#define XFS_CREATE_LOG_RES(mp) ((mp)->m_reservations.tr_create)
+
+/*
+ * Making a new directory is the same as creating a new file.
+ */
+#define XFS_CALC_MKDIR_LOG_RES(mp) XFS_CALC_CREATE_LOG_RES(mp)
+
+#define XFS_MKDIR_LOG_RES(mp) ((mp)->m_reservations.tr_mkdir)
+
+/*
+ * In freeing an inode we can modify:
+ * the inode being freed: inode size
+ * the super block free inode counter: sector size
+ * the agi hash list and counters: sector size
+ * the inode btree entry: block size
+ * the on disk inode before ours in the agi hash list: inode cluster size
+ * the inode btree: max depth * blocksize
+ * the allocation btrees: 2 trees * (max depth - 1) * block size
+ */
+#define XFS_CALC_IFREE_LOG_RES(mp) \
+ ((mp)->m_sb.sb_inodesize + \
+ (mp)->m_sb.sb_sectsize + \
+ (mp)->m_sb.sb_sectsize + \
+ XFS_FSB_TO_B((mp), 1) + \
+ MAX((__uint16_t)XFS_FSB_TO_B((mp), 1), XFS_INODE_CLUSTER_SIZE(mp)) + \
+ (128 * 5) + \
+ XFS_ALLOCFREE_LOG_RES(mp, 1) + \
+ (128 * (2 + XFS_IALLOC_BLOCKS(mp) + XFS_IN_MAXLEVELS(mp) + \
+ XFS_ALLOCFREE_LOG_COUNT(mp, 1))))
+
+
+#define XFS_IFREE_LOG_RES(mp) ((mp)->m_reservations.tr_ifree)
+
+/*
+ * When only changing the inode we log the inode and possibly the superblock
+ * We also add a bit of slop for the transaction stuff.
+ */
+#define XFS_CALC_ICHANGE_LOG_RES(mp) ((mp)->m_sb.sb_inodesize + \
+ (mp)->m_sb.sb_sectsize + 512)
+
+#define XFS_ICHANGE_LOG_RES(mp) ((mp)->m_reservations.tr_ichange)
+
+/*
+ * Growing the data section of the filesystem.
+ * superblock
+ * agi and agf
+ * allocation btrees
+ */
+#define XFS_CALC_GROWDATA_LOG_RES(mp) \
+ ((mp)->m_sb.sb_sectsize * 3 + \
+ XFS_ALLOCFREE_LOG_RES(mp, 1) + \
+ (128 * (3 + XFS_ALLOCFREE_LOG_COUNT(mp, 1))))
+
+#define XFS_GROWDATA_LOG_RES(mp) ((mp)->m_reservations.tr_growdata)
+
+/*
+ * Growing the rt section of the filesystem.
+ * In the first set of transactions (ALLOC) we allocate space to the
+ * bitmap or summary files.
+ * superblock: sector size
+ * agf of the ag from which the extent is allocated: sector size
+ * bmap btree for bitmap/summary inode: max depth * blocksize
+ * bitmap/summary inode: inode size
+ * allocation btrees for 1 block alloc: 2 * (2 * maxdepth - 1) * blocksize
+ */
+#define XFS_CALC_GROWRTALLOC_LOG_RES(mp) \
+ (2 * (mp)->m_sb.sb_sectsize + \
+ XFS_FSB_TO_B((mp), XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK)) + \
+ (mp)->m_sb.sb_inodesize + \
+ XFS_ALLOCFREE_LOG_RES(mp, 1) + \
+ (128 * \
+ (3 + XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK) + \
+ XFS_ALLOCFREE_LOG_COUNT(mp, 1))))
+
+#define XFS_GROWRTALLOC_LOG_RES(mp) ((mp)->m_reservations.tr_growrtalloc)
+
+/*
+ * Growing the rt section of the filesystem.
+ * In the second set of transactions (ZERO) we zero the new metadata blocks.
+ * one bitmap/summary block: blocksize
+ */
+#define XFS_CALC_GROWRTZERO_LOG_RES(mp) \
+ ((mp)->m_sb.sb_blocksize + 128)
+
+#define XFS_GROWRTZERO_LOG_RES(mp) ((mp)->m_reservations.tr_growrtzero)
+
+/*
+ * Growing the rt section of the filesystem.
+ * In the third set of transactions (FREE) we update metadata without
+ * allocating any new blocks.
+ * superblock: sector size
+ * bitmap inode: inode size
+ * summary inode: inode size
+ * one bitmap block: blocksize
+ * summary blocks: new summary size
+ */
+#define XFS_CALC_GROWRTFREE_LOG_RES(mp) \
+ ((mp)->m_sb.sb_sectsize + \
+ 2 * (mp)->m_sb.sb_inodesize + \
+ (mp)->m_sb.sb_blocksize + \
+ (mp)->m_rsumsize + \
+ (128 * 5))
+
+#define XFS_GROWRTFREE_LOG_RES(mp) ((mp)->m_reservations.tr_growrtfree)
+
+/*
+ * Logging the inode modification timestamp on a synchronous write.
+ * inode
+ */
+#define XFS_CALC_SWRITE_LOG_RES(mp) \
+ ((mp)->m_sb.sb_inodesize + 128)
+
+#define XFS_SWRITE_LOG_RES(mp) ((mp)->m_reservations.tr_swrite)
+
+/*
+ * Logging the inode timestamps on an fsync -- same as SWRITE
+ * as long as SWRITE logs the entire inode core
+ */
+#define XFS_FSYNC_TS_LOG_RES(mp) ((mp)->m_reservations.tr_swrite)
+
+/*
+ * Logging the inode mode bits when writing a setuid/setgid file
+ * inode
+ */
+#define XFS_CALC_WRITEID_LOG_RES(mp) \
+ ((mp)->m_sb.sb_inodesize + 128)
+
+#define XFS_WRITEID_LOG_RES(mp) ((mp)->m_reservations.tr_swrite)
+
+/*
+ * Converting the inode from non-attributed to attributed.
+ * the inode being converted: inode size
+ * agf block and superblock (for block allocation)
+ * the new block (directory sized)
+ * bmap blocks for the new directory block
+ * allocation btrees
+ */
+#define XFS_CALC_ADDAFORK_LOG_RES(mp) \
+ ((mp)->m_sb.sb_inodesize + \
+ (mp)->m_sb.sb_sectsize * 2 + \
+ (mp)->m_dirblksize + \
+ (XFS_DIR_IS_V1(mp) ? 0 : \
+ XFS_FSB_TO_B(mp, (XFS_DAENTER_BMAP1B(mp, XFS_DATA_FORK) + 1))) + \
+ XFS_ALLOCFREE_LOG_RES(mp, 1) + \
+ (128 * (4 + \
+ (XFS_DIR_IS_V1(mp) ? 0 : \
+ XFS_DAENTER_BMAP1B(mp, XFS_DATA_FORK) + 1) + \
+ XFS_ALLOCFREE_LOG_COUNT(mp, 1))))
+
+#define XFS_ADDAFORK_LOG_RES(mp) ((mp)->m_reservations.tr_addafork)
+
+/*
+ * Removing the attribute fork of a file
+ * the inode being truncated: inode size
+ * the inode\'s bmap btree: max depth * block size
+ * And the bmap_finish transaction can free the blocks and bmap blocks:
+ * the agf for each of the ags: 4 * sector size
+ * the agfl for each of the ags: 4 * sector size
+ * the super block to reflect the freed blocks: sector size
+ * worst case split in allocation btrees per extent assuming 4 extents:
+ * 4 exts * 2 trees * (2 * max depth - 1) * block size
+ */
+#define XFS_CALC_ATTRINVAL_LOG_RES(mp) \
+ (MAX( \
+ ((mp)->m_sb.sb_inodesize + \
+ XFS_FSB_TO_B((mp), XFS_BM_MAXLEVELS(mp, XFS_ATTR_FORK)) + \
+ (128 * (1 + XFS_BM_MAXLEVELS(mp, XFS_ATTR_FORK)))), \
+ ((4 * (mp)->m_sb.sb_sectsize) + \
+ (4 * (mp)->m_sb.sb_sectsize) + \
+ (mp)->m_sb.sb_sectsize + \
+ XFS_ALLOCFREE_LOG_RES(mp, 4) + \
+ (128 * (9 + XFS_ALLOCFREE_LOG_COUNT(mp, 4))))))
+
+#define XFS_ATTRINVAL_LOG_RES(mp) ((mp)->m_reservations.tr_attrinval)
+
+/*
+ * Setting an attribute.
+ * the inode getting the attribute
+ * the superblock for allocations
+ * the agfs extents are allocated from
+ * the attribute btree * max depth
+ * the inode allocation btree
+ * Since attribute transaction space is dependent on the size of the attribute,
+ * the calculation is done partially at mount time and partially at runtime.
+ */
+#define XFS_CALC_ATTRSET_LOG_RES(mp) \
+ ((mp)->m_sb.sb_inodesize + \
+ (mp)->m_sb.sb_sectsize + \
+ XFS_FSB_TO_B((mp), XFS_DA_NODE_MAXDEPTH) + \
+ (128 * (2 + XFS_DA_NODE_MAXDEPTH)))
+
+#define XFS_ATTRSET_LOG_RES(mp, ext) \
+ ((mp)->m_reservations.tr_attrset + \
+ (ext * (mp)->m_sb.sb_sectsize) + \
+ (ext * XFS_FSB_TO_B((mp), XFS_BM_MAXLEVELS(mp, XFS_ATTR_FORK))) + \
+ (128 * (ext + (ext * XFS_BM_MAXLEVELS(mp, XFS_ATTR_FORK)))))
+
+/*
+ * Removing an attribute.
+ * the inode: inode size
+ * the attribute btree could join: max depth * block size
+ * the inode bmap btree could join or split: max depth * block size
+ * And the bmap_finish transaction can free the attr blocks freed giving:
+ * the agf for the ag in which the blocks live: 2 * sector size
+ * the agfl for the ag in which the blocks live: 2 * sector size
+ * the superblock for the free block count: sector size
+ * the allocation btrees: 2 exts * 2 trees * (2 * max depth - 1) * block size
+ */
+#define XFS_CALC_ATTRRM_LOG_RES(mp) \
+ (MAX( \
+ ((mp)->m_sb.sb_inodesize + \
+ XFS_FSB_TO_B((mp), XFS_DA_NODE_MAXDEPTH) + \
+ XFS_FSB_TO_B((mp), XFS_BM_MAXLEVELS(mp, XFS_ATTR_FORK)) + \
+ (128 * (1 + XFS_DA_NODE_MAXDEPTH + XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK)))), \
+ ((2 * (mp)->m_sb.sb_sectsize) + \
+ (2 * (mp)->m_sb.sb_sectsize) + \
+ (mp)->m_sb.sb_sectsize + \
+ XFS_ALLOCFREE_LOG_RES(mp, 2) + \
+ (128 * (5 + XFS_ALLOCFREE_LOG_COUNT(mp, 2))))))
+
+#define XFS_ATTRRM_LOG_RES(mp) ((mp)->m_reservations.tr_attrrm)
+
+/*
+ * Clearing a bad agino number in an agi hash bucket.
+ */
+#define XFS_CALC_CLEAR_AGI_BUCKET_LOG_RES(mp) \
+ ((mp)->m_sb.sb_sectsize + 128)
+
+#define XFS_CLEAR_AGI_BUCKET_LOG_RES(mp) ((mp)->m_reservations.tr_clearagi)
+
+
+/*
+ * Various log count values.
+ */
+#define XFS_DEFAULT_LOG_COUNT 1
+#define XFS_DEFAULT_PERM_LOG_COUNT 2
+#define XFS_ITRUNCATE_LOG_COUNT 2
+#define XFS_INACTIVE_LOG_COUNT 2
+#define XFS_CREATE_LOG_COUNT 2
+#define XFS_MKDIR_LOG_COUNT 3
+#define XFS_SYMLINK_LOG_COUNT 3
+#define XFS_REMOVE_LOG_COUNT 2
+#define XFS_LINK_LOG_COUNT 2
+#define XFS_RENAME_LOG_COUNT 2
+#define XFS_WRITE_LOG_COUNT 2
+#define XFS_ADDAFORK_LOG_COUNT 2
+#define XFS_ATTRINVAL_LOG_COUNT 1
+#define XFS_ATTRSET_LOG_COUNT 3
+#define XFS_ATTRRM_LOG_COUNT 3
+
+/*
+ * Here we centralize the specification of XFS meta-data buffer
+ * reference count values. This determine how hard the buffer
+ * cache tries to hold onto the buffer.
+ */
+#define XFS_AGF_REF 4
+#define XFS_AGI_REF 4
+#define XFS_AGFL_REF 3
+#define XFS_INO_BTREE_REF 3
+#define XFS_ALLOC_BTREE_REF 2
+#define XFS_BMAP_BTREE_REF 2
+#define XFS_DIR_BTREE_REF 2
+#define XFS_ATTR_BTREE_REF 1
+#define XFS_INO_REF 1
+#define XFS_DQUOT_REF 1
+
+#ifdef __KERNEL__
+/*
+ * XFS transaction mechanism exported interfaces that are
+ * actually macros.
+ */
+#define xfs_trans_get_log_res(tp) ((tp)->t_log_res)
+#define xfs_trans_get_log_count(tp) ((tp)->t_log_count)
+#define xfs_trans_get_block_res(tp) ((tp)->t_blk_res)
+#define xfs_trans_set_sync(tp) ((tp)->t_flags |= XFS_TRANS_SYNC)
+
+#ifdef DEBUG
+#define xfs_trans_agblocks_delta(tp, d) ((tp)->t_ag_freeblks_delta += (long)d)
+#define xfs_trans_agflist_delta(tp, d) ((tp)->t_ag_flist_delta += (long)d)
+#define xfs_trans_agbtree_delta(tp, d) ((tp)->t_ag_btree_delta += (long)d)
+#else
+#define xfs_trans_agblocks_delta(tp, d)
+#define xfs_trans_agflist_delta(tp, d)
+#define xfs_trans_agbtree_delta(tp, d)
+#endif
+
+/*
+ * XFS transaction mechanism exported interfaces.
+ */
+void xfs_trans_init(struct xfs_mount *);
+xfs_trans_t *xfs_trans_alloc(struct xfs_mount *, uint);
+xfs_trans_t *_xfs_trans_alloc(struct xfs_mount *, uint);
+xfs_trans_t *xfs_trans_dup(xfs_trans_t *);
+int xfs_trans_reserve(xfs_trans_t *, uint, uint, uint,
+ uint, uint);
+void xfs_trans_callback(xfs_trans_t *,
+ void (*)(xfs_trans_t *, void *), void *);
+void xfs_trans_mod_sb(xfs_trans_t *, uint, long);
+struct xfs_buf *xfs_trans_get_buf(xfs_trans_t *, struct xfs_buftarg *, xfs_daddr_t,
+ int, uint);
+int xfs_trans_read_buf(struct xfs_mount *, xfs_trans_t *,
+ struct xfs_buftarg *, xfs_daddr_t, int, uint,
+ struct xfs_buf **);
+struct xfs_buf *xfs_trans_getsb(xfs_trans_t *, struct xfs_mount *, int);
+
+void xfs_trans_brelse(xfs_trans_t *, struct xfs_buf *);
+void xfs_trans_bjoin(xfs_trans_t *, struct xfs_buf *);
+void xfs_trans_bhold(xfs_trans_t *, struct xfs_buf *);
+void xfs_trans_binval(xfs_trans_t *, struct xfs_buf *);
+void xfs_trans_inode_buf(xfs_trans_t *, struct xfs_buf *);
+void xfs_trans_inode_buf(xfs_trans_t *, struct xfs_buf *);
+void xfs_trans_stale_inode_buf(xfs_trans_t *, struct xfs_buf *);
+void xfs_trans_dquot_buf(xfs_trans_t *, struct xfs_buf *, uint);
+void xfs_trans_inode_alloc_buf(xfs_trans_t *, struct xfs_buf *);
+int xfs_trans_iget(struct xfs_mount *, xfs_trans_t *,
+ xfs_ino_t , uint, uint, struct xfs_inode **);
+void xfs_trans_ijoin(xfs_trans_t *, struct xfs_inode *, uint);
+void xfs_trans_ihold(xfs_trans_t *, struct xfs_inode *);
+void xfs_trans_ihold_release(xfs_trans_t *, struct xfs_inode *);
+void xfs_trans_log_buf(xfs_trans_t *, struct xfs_buf *, uint, uint);
+void xfs_trans_log_inode(xfs_trans_t *, struct xfs_inode *, uint);
+struct xfs_efi_log_item *xfs_trans_get_efi(xfs_trans_t *, uint);
+void xfs_efi_release(struct xfs_efi_log_item *, uint);
+void xfs_trans_log_efi_extent(xfs_trans_t *,
+ struct xfs_efi_log_item *,
+ xfs_fsblock_t,
+ xfs_extlen_t);
+struct xfs_efd_log_item *xfs_trans_get_efd(xfs_trans_t *,
+ struct xfs_efi_log_item *,
+ uint);
+void xfs_trans_log_efd_extent(xfs_trans_t *,
+ struct xfs_efd_log_item *,
+ xfs_fsblock_t,
+ xfs_extlen_t);
+int xfs_trans_commit(xfs_trans_t *, uint flags, xfs_lsn_t *);
+void xfs_trans_cancel(xfs_trans_t *, int);
+void xfs_trans_ail_init(struct xfs_mount *);
+xfs_lsn_t xfs_trans_push_ail(struct xfs_mount *, xfs_lsn_t);
+xfs_lsn_t xfs_trans_tail_ail(struct xfs_mount *);
+void xfs_trans_unlocked_item(struct xfs_mount *,
+ xfs_log_item_t *);
+xfs_log_busy_slot_t *xfs_trans_add_busy(xfs_trans_t *tp,
+ xfs_agnumber_t ag,
+ xfs_extlen_t idx);
+
+#endif /* __KERNEL__ */
+
+#endif /* __XFS_TRANS_H__ */
diff --git a/fs/xfs/xfs_trans_ail.c b/fs/xfs/xfs_trans_ail.c
new file mode 100644
index 00000000000000..7bc5eab4c2c19c
--- /dev/null
+++ b/fs/xfs/xfs_trans_ail.c
@@ -0,0 +1,596 @@
+/*
+ * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_dir.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_trans_priv.h"
+#include "xfs_error.h"
+
+STATIC void xfs_ail_insert(xfs_ail_entry_t *, xfs_log_item_t *);
+STATIC xfs_log_item_t * xfs_ail_delete(xfs_ail_entry_t *, xfs_log_item_t *);
+STATIC xfs_log_item_t * xfs_ail_min(xfs_ail_entry_t *);
+STATIC xfs_log_item_t * xfs_ail_next(xfs_ail_entry_t *, xfs_log_item_t *);
+
+#ifdef DEBUG
+STATIC void xfs_ail_check(xfs_ail_entry_t *);
+#else
+#define xfs_ail_check(a)
+#endif /* DEBUG */
+
+
+/*
+ * This is called by the log manager code to determine the LSN
+ * of the tail of the log. This is exactly the LSN of the first
+ * item in the AIL. If the AIL is empty, then this function
+ * returns 0.
+ *
+ * We need the AIL lock in order to get a coherent read of the
+ * lsn of the last item in the AIL.
+ */
+xfs_lsn_t
+xfs_trans_tail_ail(
+ xfs_mount_t *mp)
+{
+ xfs_lsn_t lsn;
+ xfs_log_item_t *lip;
+ SPLDECL(s);
+
+ AIL_LOCK(mp,s);
+ lip = xfs_ail_min(&(mp->m_ail));
+ if (lip == NULL) {
+ lsn = (xfs_lsn_t)0;
+ } else {
+ lsn = lip->li_lsn;
+ }
+ AIL_UNLOCK(mp, s);
+
+ return lsn;
+}
+
+/*
+ * xfs_trans_push_ail
+ *
+ * This routine is called to move the tail of the AIL
+ * forward. It does this by trying to flush items in the AIL
+ * whose lsns are below the given threshold_lsn.
+ *
+ * The routine returns the lsn of the tail of the log.
+ */
+xfs_lsn_t
+xfs_trans_push_ail(
+ xfs_mount_t *mp,
+ xfs_lsn_t threshold_lsn)
+{
+ xfs_lsn_t lsn;
+ xfs_log_item_t *lip;
+ int gen;
+ int restarts;
+ int lock_result;
+ int flush_log;
+ SPLDECL(s);
+
+#define XFS_TRANS_PUSH_AIL_RESTARTS 10
+
+ AIL_LOCK(mp,s);
+ lip = xfs_trans_first_ail(mp, &gen);
+ if (lip == NULL || XFS_FORCED_SHUTDOWN(mp)) {
+ /*
+ * Just return if the AIL is empty.
+ */
+ AIL_UNLOCK(mp, s);
+ return (xfs_lsn_t)0;
+ }
+
+ XFS_STATS_INC(xs_push_ail);
+
+ /*
+ * While the item we are looking at is below the given threshold
+ * try to flush it out. Make sure to limit the number of times
+ * we allow xfs_trans_next_ail() to restart scanning from the
+ * beginning of the list. We'd like not to stop until we've at least
+ * tried to push on everything in the AIL with an LSN less than
+ * the given threshold. However, we may give up before that if
+ * we realize that we've been holding the AIL_LOCK for 'too long',
+ * blocking interrupts. Currently, too long is < 500us roughly.
+ */
+ flush_log = 0;
+ restarts = 0;
+ while (((restarts < XFS_TRANS_PUSH_AIL_RESTARTS) &&
+ (XFS_LSN_CMP(lip->li_lsn, threshold_lsn) < 0))) {
+ /*
+ * If we can lock the item without sleeping, unlock
+ * the AIL lock and flush the item. Then re-grab the
+ * AIL lock so we can look for the next item on the
+ * AIL. Since we unlock the AIL while we flush the
+ * item, the next routine may start over again at the
+ * the beginning of the list if anything has changed.
+ * That is what the generation count is for.
+ *
+ * If we can't lock the item, either its holder will flush
+ * it or it is already being flushed or it is being relogged.
+ * In any of these case it is being taken care of and we
+ * can just skip to the next item in the list.
+ */
+ lock_result = IOP_TRYLOCK(lip);
+ switch (lock_result) {
+ case XFS_ITEM_SUCCESS:
+ AIL_UNLOCK(mp, s);
+ XFS_STATS_INC(xs_push_ail_success);
+ IOP_PUSH(lip);
+ AIL_LOCK(mp,s);
+ break;
+
+ case XFS_ITEM_PUSHBUF:
+ AIL_UNLOCK(mp, s);
+ XFS_STATS_INC(xs_push_ail_pushbuf);
+#ifdef XFSRACEDEBUG
+ delay_for_intr();
+ delay(300);
+#endif
+ ASSERT(lip->li_ops->iop_pushbuf);
+ ASSERT(lip);
+ IOP_PUSHBUF(lip);
+ AIL_LOCK(mp,s);
+ break;
+
+ case XFS_ITEM_PINNED:
+ XFS_STATS_INC(xs_push_ail_pinned);
+ flush_log = 1;
+ break;
+
+ case XFS_ITEM_LOCKED:
+ XFS_STATS_INC(xs_push_ail_locked);
+ break;
+
+ case XFS_ITEM_FLUSHING:
+ XFS_STATS_INC(xs_push_ail_flushing);
+ break;
+
+ default:
+ ASSERT(0);
+ break;
+ }
+
+ lip = xfs_trans_next_ail(mp, lip, &gen, &restarts);
+ if (lip == NULL) {
+ break;
+ }
+ if (XFS_FORCED_SHUTDOWN(mp)) {
+ /*
+ * Just return if we shut down during the last try.
+ */
+ AIL_UNLOCK(mp, s);
+ return (xfs_lsn_t)0;
+ }
+
+ }
+
+ if (flush_log) {
+ /*
+ * If something we need to push out was pinned, then
+ * push out the log so it will become unpinned and
+ * move forward in the AIL.
+ */
+ AIL_UNLOCK(mp, s);
+ XFS_STATS_INC(xs_push_ail_flush);
+ xfs_log_force(mp, (xfs_lsn_t)0, XFS_LOG_FORCE);
+ AIL_LOCK(mp, s);
+ }
+
+ lip = xfs_ail_min(&(mp->m_ail));
+ if (lip == NULL) {
+ lsn = (xfs_lsn_t)0;
+ } else {
+ lsn = lip->li_lsn;
+ }
+
+ AIL_UNLOCK(mp, s);
+ return lsn;
+} /* xfs_trans_push_ail */
+
+
+/*
+ * This is to be called when an item is unlocked that may have
+ * been in the AIL. It will wake up the first member of the AIL
+ * wait list if this item's unlocking might allow it to progress.
+ * If the item is in the AIL, then we need to get the AIL lock
+ * while doing our checking so we don't race with someone going
+ * to sleep waiting for this event in xfs_trans_push_ail().
+ */
+void
+xfs_trans_unlocked_item(
+ xfs_mount_t *mp,
+ xfs_log_item_t *lip)
+{
+ xfs_log_item_t *min_lip;
+
+ /*
+ * If we're forcibly shutting down, we may have
+ * unlocked log items arbitrarily. The last thing
+ * we want to do is to move the tail of the log
+ * over some potentially valid data.
+ */
+ if (!(lip->li_flags & XFS_LI_IN_AIL) ||
+ XFS_FORCED_SHUTDOWN(mp)) {
+ return;
+ }
+
+ /*
+ * This is the one case where we can call into xfs_ail_min()
+ * without holding the AIL lock because we only care about the
+ * case where we are at the tail of the AIL. If the object isn't
+ * at the tail, it doesn't matter what result we get back. This
+ * is slightly racy because since we were just unlocked, we could
+ * go to sleep between the call to xfs_ail_min and the call to
+ * xfs_log_move_tail, have someone else lock us, commit to us disk,
+ * move us out of the tail of the AIL, and then we wake up. However,
+ * the call to xfs_log_move_tail() doesn't do anything if there's
+ * not enough free space to wake people up so we're safe calling it.
+ */
+ min_lip = xfs_ail_min(&mp->m_ail);
+
+ if (min_lip == lip)
+ xfs_log_move_tail(mp, 1);
+} /* xfs_trans_unlocked_item */
+
+
+/*
+ * Update the position of the item in the AIL with the new
+ * lsn. If it is not yet in the AIL, add it. Otherwise, move
+ * it to its new position by removing it and re-adding it.
+ *
+ * Wakeup anyone with an lsn less than the item's lsn. If the item
+ * we move in the AIL is the minimum one, update the tail lsn in the
+ * log manager.
+ *
+ * Increment the AIL's generation count to indicate that the tree
+ * has changed.
+ *
+ * This function must be called with the AIL lock held. The lock
+ * is dropped before returning, so the caller must pass in the
+ * cookie returned by AIL_LOCK.
+ */
+void
+xfs_trans_update_ail(
+ xfs_mount_t *mp,
+ xfs_log_item_t *lip,
+ xfs_lsn_t lsn,
+ unsigned long s)
+{
+ xfs_ail_entry_t *ailp;
+ xfs_log_item_t *dlip=NULL;
+ xfs_log_item_t *mlip; /* ptr to minimum lip */
+
+ ailp = &(mp->m_ail);
+ mlip = xfs_ail_min(ailp);
+
+ if (lip->li_flags & XFS_LI_IN_AIL) {
+ dlip = xfs_ail_delete(ailp, lip);
+ ASSERT(dlip == lip);
+ } else {
+ lip->li_flags |= XFS_LI_IN_AIL;
+ }
+
+ lip->li_lsn = lsn;
+
+ xfs_ail_insert(ailp, lip);
+ mp->m_ail_gen++;
+
+ if (mlip == dlip) {
+ mlip = xfs_ail_min(&(mp->m_ail));
+ AIL_UNLOCK(mp, s);
+ xfs_log_move_tail(mp, mlip->li_lsn);
+ } else {
+ AIL_UNLOCK(mp, s);
+ }
+
+
+} /* xfs_trans_update_ail */
+
+/*
+ * Delete the given item from the AIL. It must already be in
+ * the AIL.
+ *
+ * Wakeup anyone with an lsn less than item's lsn. If the item
+ * we delete in the AIL is the minimum one, update the tail lsn in the
+ * log manager.
+ *
+ * Clear the IN_AIL flag from the item, reset its lsn to 0, and
+ * bump the AIL's generation count to indicate that the tree
+ * has changed.
+ *
+ * This function must be called with the AIL lock held. The lock
+ * is dropped before returning, so the caller must pass in the
+ * cookie returned by AIL_LOCK.
+ */
+void
+xfs_trans_delete_ail(
+ xfs_mount_t *mp,
+ xfs_log_item_t *lip,
+ unsigned long s)
+{
+ xfs_ail_entry_t *ailp;
+ xfs_log_item_t *dlip;
+ xfs_log_item_t *mlip;
+
+ if (lip->li_flags & XFS_LI_IN_AIL) {
+ ailp = &(mp->m_ail);
+ mlip = xfs_ail_min(ailp);
+ dlip = xfs_ail_delete(ailp, lip);
+ ASSERT(dlip == lip);
+
+
+ lip->li_flags &= ~XFS_LI_IN_AIL;
+ lip->li_lsn = 0;
+ mp->m_ail_gen++;
+
+ if (mlip == dlip) {
+ mlip = xfs_ail_min(&(mp->m_ail));
+ AIL_UNLOCK(mp, s);
+ xfs_log_move_tail(mp, (mlip ? mlip->li_lsn : 0));
+ } else {
+ AIL_UNLOCK(mp, s);
+ }
+ }
+ else {
+ /*
+ * If the file system is not being shutdown, we are in
+ * serious trouble if we get to this stage.
+ */
+ if (XFS_FORCED_SHUTDOWN(mp))
+ AIL_UNLOCK(mp, s);
+ else {
+ xfs_cmn_err(XFS_PTAG_AILDELETE, CE_ALERT, mp,
+ "xfs_trans_delete_ail: attempting to delete a log item that is not in the AIL");
+ xfs_force_shutdown(mp, XFS_CORRUPT_INCORE);
+ AIL_UNLOCK(mp, s);
+ }
+ }
+}
+
+
+
+/*
+ * Return the item in the AIL with the smallest lsn.
+ * Return the current tree generation number for use
+ * in calls to xfs_trans_next_ail().
+ */
+xfs_log_item_t *
+xfs_trans_first_ail(
+ xfs_mount_t *mp,
+ int *gen)
+{
+ xfs_log_item_t *lip;
+
+ lip = xfs_ail_min(&(mp->m_ail));
+ *gen = (int)mp->m_ail_gen;
+
+ return (lip);
+}
+
+/*
+ * If the generation count of the tree has not changed since the
+ * caller last took something from the AIL, then return the elmt
+ * in the tree which follows the one given. If the count has changed,
+ * then return the minimum elmt of the AIL and bump the restarts counter
+ * if one is given.
+ */
+xfs_log_item_t *
+xfs_trans_next_ail(
+ xfs_mount_t *mp,
+ xfs_log_item_t *lip,
+ int *gen,
+ int *restarts)
+{
+ xfs_log_item_t *nlip;
+
+ ASSERT(mp && lip && gen);
+ if (mp->m_ail_gen == *gen) {
+ nlip = xfs_ail_next(&(mp->m_ail), lip);
+ } else {
+ nlip = xfs_ail_min(&(mp->m_ail));
+ *gen = (int)mp->m_ail_gen;
+ if (restarts != NULL) {
+ XFS_STATS_INC(xs_push_ail_restarts);
+ (*restarts)++;
+ }
+ }
+
+ return (nlip);
+}
+
+
+/*
+ * The active item list (AIL) is a doubly linked list of log
+ * items sorted by ascending lsn. The base of the list is
+ * a forw/back pointer pair embedded in the xfs mount structure.
+ * The base is initialized with both pointers pointing to the
+ * base. This case always needs to be distinguished, because
+ * the base has no lsn to look at. We almost always insert
+ * at the end of the list, so on inserts we search from the
+ * end of the list to find where the new item belongs.
+ */
+
+/*
+ * Initialize the doubly linked list to point only to itself.
+ */
+void
+xfs_trans_ail_init(
+ xfs_mount_t *mp)
+{
+ mp->m_ail.ail_forw = (xfs_log_item_t*)&(mp->m_ail);
+ mp->m_ail.ail_back = (xfs_log_item_t*)&(mp->m_ail);
+}
+
+/*
+ * Insert the given log item into the AIL.
+ * We almost always insert at the end of the list, so on inserts
+ * we search from the end of the list to find where the
+ * new item belongs.
+ */
+STATIC void
+xfs_ail_insert(
+ xfs_ail_entry_t *base,
+ xfs_log_item_t *lip)
+/* ARGSUSED */
+{
+ xfs_log_item_t *next_lip;
+
+ /*
+ * If the list is empty, just insert the item.
+ */
+ if (base->ail_back == (xfs_log_item_t*)base) {
+ base->ail_forw = lip;
+ base->ail_back = lip;
+ lip->li_ail.ail_forw = (xfs_log_item_t*)base;
+ lip->li_ail.ail_back = (xfs_log_item_t*)base;
+ return;
+ }
+
+ next_lip = base->ail_back;
+ while ((next_lip != (xfs_log_item_t*)base) &&
+ (XFS_LSN_CMP(next_lip->li_lsn, lip->li_lsn) > 0)) {
+ next_lip = next_lip->li_ail.ail_back;
+ }
+ ASSERT((next_lip == (xfs_log_item_t*)base) ||
+ (XFS_LSN_CMP(next_lip->li_lsn, lip->li_lsn) <= 0));
+ lip->li_ail.ail_forw = next_lip->li_ail.ail_forw;
+ lip->li_ail.ail_back = next_lip;
+ next_lip->li_ail.ail_forw = lip;
+ lip->li_ail.ail_forw->li_ail.ail_back = lip;
+
+ xfs_ail_check(base);
+ return;
+}
+
+/*
+ * Delete the given item from the AIL. Return a pointer to the item.
+ */
+/*ARGSUSED*/
+STATIC xfs_log_item_t *
+xfs_ail_delete(
+ xfs_ail_entry_t *base,
+ xfs_log_item_t *lip)
+/* ARGSUSED */
+{
+ lip->li_ail.ail_forw->li_ail.ail_back = lip->li_ail.ail_back;
+ lip->li_ail.ail_back->li_ail.ail_forw = lip->li_ail.ail_forw;
+ lip->li_ail.ail_forw = NULL;
+ lip->li_ail.ail_back = NULL;
+
+ xfs_ail_check(base);
+ return lip;
+}
+
+/*
+ * Return a pointer to the first item in the AIL.
+ * If the AIL is empty, then return NULL.
+ */
+STATIC xfs_log_item_t *
+xfs_ail_min(
+ xfs_ail_entry_t *base)
+/* ARGSUSED */
+{
+ register xfs_log_item_t *forw = base->ail_forw;
+ if (forw == (xfs_log_item_t*)base) {
+ return NULL;
+ }
+ return forw;
+}
+
+/*
+ * Return a pointer to the item which follows
+ * the given item in the AIL. If the given item
+ * is the last item in the list, then return NULL.
+ */
+STATIC xfs_log_item_t *
+xfs_ail_next(
+ xfs_ail_entry_t *base,
+ xfs_log_item_t *lip)
+/* ARGSUSED */
+{
+ if (lip->li_ail.ail_forw == (xfs_log_item_t*)base) {
+ return NULL;
+ }
+ return lip->li_ail.ail_forw;
+
+}
+
+#ifdef DEBUG
+/*
+ * Check that the list is sorted as it should be.
+ */
+STATIC void
+xfs_ail_check(
+ xfs_ail_entry_t *base)
+{
+ xfs_log_item_t *lip;
+ xfs_log_item_t *prev_lip;
+
+ lip = base->ail_forw;
+ if (lip == (xfs_log_item_t*)base) {
+ /*
+ * Make sure the pointers are correct when the list
+ * is empty.
+ */
+ ASSERT(base->ail_back == (xfs_log_item_t*)base);
+ return;
+ }
+
+ /*
+ * Walk the list checking forward and backward pointers,
+ * lsn ordering, and that every entry has the XFS_LI_IN_AIL
+ * flag set.
+ */
+ prev_lip = (xfs_log_item_t*)base;
+ while (lip != (xfs_log_item_t*)base) {
+ if (prev_lip != (xfs_log_item_t*)base) {
+ ASSERT(prev_lip->li_ail.ail_forw == lip);
+ ASSERT(XFS_LSN_CMP(prev_lip->li_lsn, lip->li_lsn) <= 0);
+ }
+ ASSERT(lip->li_ail.ail_back == prev_lip);
+ ASSERT((lip->li_flags & XFS_LI_IN_AIL) != 0);
+ prev_lip = lip;
+ lip = lip->li_ail.ail_forw;
+ }
+ ASSERT(lip == (xfs_log_item_t*)base);
+ ASSERT(base->ail_back == prev_lip);
+}
+#endif /* DEBUG */
diff --git a/fs/xfs/xfs_trans_buf.c b/fs/xfs/xfs_trans_buf.c
new file mode 100644
index 00000000000000..a9682b9510c1dd
--- /dev/null
+++ b/fs/xfs/xfs_trans_buf.c
@@ -0,0 +1,1093 @@
+/*
+ * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_buf_item.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_trans_priv.h"
+#include "xfs_error.h"
+#include "xfs_rw.h"
+
+
+STATIC xfs_buf_t *xfs_trans_buf_item_match(xfs_trans_t *, xfs_buftarg_t *,
+ xfs_daddr_t, int);
+STATIC xfs_buf_t *xfs_trans_buf_item_match_all(xfs_trans_t *, xfs_buftarg_t *,
+ xfs_daddr_t, int);
+
+
+/*
+ * Get and lock the buffer for the caller if it is not already
+ * locked within the given transaction. If it is already locked
+ * within the transaction, just increment its lock recursion count
+ * and return a pointer to it.
+ *
+ * Use the fast path function xfs_trans_buf_item_match() or the buffer
+ * cache routine incore_match() to find the buffer
+ * if it is already owned by this transaction.
+ *
+ * If we don't already own the buffer, use get_buf() to get it.
+ * If it doesn't yet have an associated xfs_buf_log_item structure,
+ * then allocate one and add the item to this transaction.
+ *
+ * If the transaction pointer is NULL, make this just a normal
+ * get_buf() call.
+ */
+xfs_buf_t *
+xfs_trans_get_buf(xfs_trans_t *tp,
+ xfs_buftarg_t *target_dev,
+ xfs_daddr_t blkno,
+ int len,
+ uint flags)
+{
+ xfs_buf_t *bp;
+ xfs_buf_log_item_t *bip;
+
+ if (flags == 0)
+ flags = XFS_BUF_LOCK | XFS_BUF_MAPPED;
+
+ /*
+ * Default to a normal get_buf() call if the tp is NULL.
+ */
+ if (tp == NULL) {
+ bp = xfs_buf_get_flags(target_dev, blkno, len,
+ flags | BUF_BUSY);
+ return(bp);
+ }
+
+ /*
+ * If we find the buffer in the cache with this transaction
+ * pointer in its b_fsprivate2 field, then we know we already
+ * have it locked. In this case we just increment the lock
+ * recursion count and return the buffer to the caller.
+ */
+ if (tp->t_items.lic_next == NULL) {
+ bp = xfs_trans_buf_item_match(tp, target_dev, blkno, len);
+ } else {
+ bp = xfs_trans_buf_item_match_all(tp, target_dev, blkno, len);
+ }
+ if (bp != NULL) {
+ ASSERT(XFS_BUF_VALUSEMA(bp) <= 0);
+ if (XFS_FORCED_SHUTDOWN(tp->t_mountp)) {
+ xfs_buftrace("TRANS GET RECUR SHUT", bp);
+ XFS_BUF_SUPER_STALE(bp);
+ }
+ /*
+ * If the buffer is stale then it was binval'ed
+ * since last read. This doesn't matter since the
+ * caller isn't allowed to use the data anyway.
+ */
+ else if (XFS_BUF_ISSTALE(bp)) {
+ xfs_buftrace("TRANS GET RECUR STALE", bp);
+ ASSERT(!XFS_BUF_ISDELAYWRITE(bp));
+ }
+ ASSERT(XFS_BUF_FSPRIVATE2(bp, xfs_trans_t *) == tp);
+ bip = XFS_BUF_FSPRIVATE(bp, xfs_buf_log_item_t *);
+ ASSERT(bip != NULL);
+ ASSERT(atomic_read(&bip->bli_refcount) > 0);
+ bip->bli_recur++;
+ xfs_buftrace("TRANS GET RECUR", bp);
+ xfs_buf_item_trace("GET RECUR", bip);
+ return (bp);
+ }
+
+ /*
+ * We always specify the BUF_BUSY flag within a transaction so
+ * that get_buf does not try to push out a delayed write buffer
+ * which might cause another transaction to take place (if the
+ * buffer was delayed alloc). Such recursive transactions can
+ * easily deadlock with our current transaction as well as cause
+ * us to run out of stack space.
+ */
+ bp = xfs_buf_get_flags(target_dev, blkno, len, flags | BUF_BUSY);
+ if (bp == NULL) {
+ return NULL;
+ }
+
+ ASSERT(!XFS_BUF_GETERROR(bp));
+
+ /*
+ * The xfs_buf_log_item pointer is stored in b_fsprivate. If
+ * it doesn't have one yet, then allocate one and initialize it.
+ * The checks to see if one is there are in xfs_buf_item_init().
+ */
+ xfs_buf_item_init(bp, tp->t_mountp);
+
+ /*
+ * Set the recursion count for the buffer within this transaction
+ * to 0.
+ */
+ bip = XFS_BUF_FSPRIVATE(bp, xfs_buf_log_item_t*);
+ ASSERT(!(bip->bli_flags & XFS_BLI_STALE));
+ ASSERT(!(bip->bli_format.blf_flags & XFS_BLI_CANCEL));
+ ASSERT(!(bip->bli_flags & XFS_BLI_LOGGED));
+ bip->bli_recur = 0;
+
+ /*
+ * Take a reference for this transaction on the buf item.
+ */
+ atomic_inc(&bip->bli_refcount);
+
+ /*
+ * Get a log_item_desc to point at the new item.
+ */
+ (void) xfs_trans_add_item(tp, (xfs_log_item_t*)bip);
+
+ /*
+ * Initialize b_fsprivate2 so we can find it with incore_match()
+ * above.
+ */
+ XFS_BUF_SET_FSPRIVATE2(bp, tp);
+
+ xfs_buftrace("TRANS GET", bp);
+ xfs_buf_item_trace("GET", bip);
+ return (bp);
+}
+
+/*
+ * Get and lock the superblock buffer of this file system for the
+ * given transaction.
+ *
+ * We don't need to use incore_match() here, because the superblock
+ * buffer is a private buffer which we keep a pointer to in the
+ * mount structure.
+ */
+xfs_buf_t *
+xfs_trans_getsb(xfs_trans_t *tp,
+ struct xfs_mount *mp,
+ int flags)
+{
+ xfs_buf_t *bp;
+ xfs_buf_log_item_t *bip;
+
+ /*
+ * Default to just trying to lock the superblock buffer
+ * if tp is NULL.
+ */
+ if (tp == NULL) {
+ return (xfs_getsb(mp, flags));
+ }
+
+ /*
+ * If the superblock buffer already has this transaction
+ * pointer in its b_fsprivate2 field, then we know we already
+ * have it locked. In this case we just increment the lock
+ * recursion count and return the buffer to the caller.
+ */
+ bp = mp->m_sb_bp;
+ if (XFS_BUF_FSPRIVATE2(bp, xfs_trans_t *) == tp) {
+ bip = XFS_BUF_FSPRIVATE(bp, xfs_buf_log_item_t*);
+ ASSERT(bip != NULL);
+ ASSERT(atomic_read(&bip->bli_refcount) > 0);
+ bip->bli_recur++;
+ xfs_buf_item_trace("GETSB RECUR", bip);
+ return (bp);
+ }
+
+ bp = xfs_getsb(mp, flags);
+ if (bp == NULL) {
+ return NULL;
+ }
+
+ /*
+ * The xfs_buf_log_item pointer is stored in b_fsprivate. If
+ * it doesn't have one yet, then allocate one and initialize it.
+ * The checks to see if one is there are in xfs_buf_item_init().
+ */
+ xfs_buf_item_init(bp, mp);
+
+ /*
+ * Set the recursion count for the buffer within this transaction
+ * to 0.
+ */
+ bip = XFS_BUF_FSPRIVATE(bp, xfs_buf_log_item_t*);
+ ASSERT(!(bip->bli_flags & XFS_BLI_STALE));
+ ASSERT(!(bip->bli_format.blf_flags & XFS_BLI_CANCEL));
+ ASSERT(!(bip->bli_flags & XFS_BLI_LOGGED));
+ bip->bli_recur = 0;
+
+ /*
+ * Take a reference for this transaction on the buf item.
+ */
+ atomic_inc(&bip->bli_refcount);
+
+ /*
+ * Get a log_item_desc to point at the new item.
+ */
+ (void) xfs_trans_add_item(tp, (xfs_log_item_t*)bip);
+
+ /*
+ * Initialize b_fsprivate2 so we can find it with incore_match()
+ * above.
+ */
+ XFS_BUF_SET_FSPRIVATE2(bp, tp);
+
+ xfs_buf_item_trace("GETSB", bip);
+ return (bp);
+}
+
+#ifdef DEBUG
+xfs_buftarg_t *xfs_error_target;
+int xfs_do_error;
+int xfs_req_num;
+int xfs_error_mod = 33;
+#endif
+
+/*
+ * Get and lock the buffer for the caller if it is not already
+ * locked within the given transaction. If it has not yet been
+ * read in, read it from disk. If it is already locked
+ * within the transaction and already read in, just increment its
+ * lock recursion count and return a pointer to it.
+ *
+ * Use the fast path function xfs_trans_buf_item_match() or the buffer
+ * cache routine incore_match() to find the buffer
+ * if it is already owned by this transaction.
+ *
+ * If we don't already own the buffer, use read_buf() to get it.
+ * If it doesn't yet have an associated xfs_buf_log_item structure,
+ * then allocate one and add the item to this transaction.
+ *
+ * If the transaction pointer is NULL, make this just a normal
+ * read_buf() call.
+ */
+int
+xfs_trans_read_buf(
+ xfs_mount_t *mp,
+ xfs_trans_t *tp,
+ xfs_buftarg_t *target,
+ xfs_daddr_t blkno,
+ int len,
+ uint flags,
+ xfs_buf_t **bpp)
+{
+ xfs_buf_t *bp;
+ xfs_buf_log_item_t *bip;
+ int error;
+
+ if (flags == 0)
+ flags = XFS_BUF_LOCK | XFS_BUF_MAPPED;
+
+ /*
+ * Default to a normal get_buf() call if the tp is NULL.
+ */
+ if (tp == NULL) {
+ bp = xfs_buf_read_flags(target, blkno, len, flags | BUF_BUSY);
+ if (!bp)
+ return XFS_ERROR(ENOMEM);
+
+ if ((bp != NULL) && (XFS_BUF_GETERROR(bp) != 0)) {
+ xfs_ioerror_alert("xfs_trans_read_buf", mp,
+ bp, blkno);
+ error = XFS_BUF_GETERROR(bp);
+ xfs_buf_relse(bp);
+ return error;
+ }
+#ifdef DEBUG
+ if (xfs_do_error && (bp != NULL)) {
+ if (xfs_error_target == target) {
+ if (((xfs_req_num++) % xfs_error_mod) == 0) {
+ xfs_buf_relse(bp);
+ printk("Returning error!\n");
+ return XFS_ERROR(EIO);
+ }
+ }
+ }
+#endif
+ if (XFS_FORCED_SHUTDOWN(mp))
+ goto shutdown_abort;
+ *bpp = bp;
+ return 0;
+ }
+
+ /*
+ * If we find the buffer in the cache with this transaction
+ * pointer in its b_fsprivate2 field, then we know we already
+ * have it locked. If it is already read in we just increment
+ * the lock recursion count and return the buffer to the caller.
+ * If the buffer is not yet read in, then we read it in, increment
+ * the lock recursion count, and return it to the caller.
+ */
+ if (tp->t_items.lic_next == NULL) {
+ bp = xfs_trans_buf_item_match(tp, target, blkno, len);
+ } else {
+ bp = xfs_trans_buf_item_match_all(tp, target, blkno, len);
+ }
+ if (bp != NULL) {
+ ASSERT(XFS_BUF_VALUSEMA(bp) <= 0);
+ ASSERT(XFS_BUF_FSPRIVATE2(bp, xfs_trans_t *) == tp);
+ ASSERT(XFS_BUF_FSPRIVATE(bp, void *) != NULL);
+ ASSERT((XFS_BUF_ISERROR(bp)) == 0);
+ if (!(XFS_BUF_ISDONE(bp))) {
+ xfs_buftrace("READ_BUF_INCORE !DONE", bp);
+ ASSERT(!XFS_BUF_ISASYNC(bp));
+ XFS_BUF_READ(bp);
+ xfsbdstrat(tp->t_mountp, bp);
+ xfs_iowait(bp);
+ if (XFS_BUF_GETERROR(bp) != 0) {
+ xfs_ioerror_alert("xfs_trans_read_buf", mp,
+ bp, blkno);
+ error = XFS_BUF_GETERROR(bp);
+ xfs_buf_relse(bp);
+ /*
+ * We can gracefully recover from most
+ * read errors. Ones we can't are those
+ * that happen after the transaction's
+ * already dirty.
+ */
+ if (tp->t_flags & XFS_TRANS_DIRTY)
+ xfs_force_shutdown(tp->t_mountp,
+ XFS_METADATA_IO_ERROR);
+ return error;
+ }
+ }
+ /*
+ * We never locked this buf ourselves, so we shouldn't
+ * brelse it either. Just get out.
+ */
+ if (XFS_FORCED_SHUTDOWN(mp)) {
+ xfs_buftrace("READ_BUF_INCORE XFSSHUTDN", bp);
+ *bpp = NULL;
+ return XFS_ERROR(EIO);
+ }
+
+
+ bip = XFS_BUF_FSPRIVATE(bp, xfs_buf_log_item_t*);
+ bip->bli_recur++;
+
+ ASSERT(atomic_read(&bip->bli_refcount) > 0);
+ xfs_buf_item_trace("READ RECUR", bip);
+ *bpp = bp;
+ return 0;
+ }
+
+ /*
+ * We always specify the BUF_BUSY flag within a transaction so
+ * that get_buf does not try to push out a delayed write buffer
+ * which might cause another transaction to take place (if the
+ * buffer was delayed alloc). Such recursive transactions can
+ * easily deadlock with our current transaction as well as cause
+ * us to run out of stack space.
+ */
+ bp = xfs_buf_read_flags(target, blkno, len, flags | BUF_BUSY);
+ if (bp == NULL) {
+ *bpp = NULL;
+ return 0;
+ }
+ if (XFS_BUF_GETERROR(bp) != 0) {
+ XFS_BUF_SUPER_STALE(bp);
+ xfs_buftrace("READ ERROR", bp);
+ error = XFS_BUF_GETERROR(bp);
+
+ xfs_ioerror_alert("xfs_trans_read_buf", mp,
+ bp, blkno);
+ if (tp->t_flags & XFS_TRANS_DIRTY)
+ xfs_force_shutdown(tp->t_mountp, XFS_METADATA_IO_ERROR);
+ xfs_buf_relse(bp);
+ return error;
+ }
+#ifdef DEBUG
+ if (xfs_do_error && !(tp->t_flags & XFS_TRANS_DIRTY)) {
+ if (xfs_error_target == target) {
+ if (((xfs_req_num++) % xfs_error_mod) == 0) {
+ xfs_force_shutdown(tp->t_mountp,
+ XFS_METADATA_IO_ERROR);
+ xfs_buf_relse(bp);
+ printk("Returning error in trans!\n");
+ return XFS_ERROR(EIO);
+ }
+ }
+ }
+#endif
+ if (XFS_FORCED_SHUTDOWN(mp))
+ goto shutdown_abort;
+
+ /*
+ * The xfs_buf_log_item pointer is stored in b_fsprivate. If
+ * it doesn't have one yet, then allocate one and initialize it.
+ * The checks to see if one is there are in xfs_buf_item_init().
+ */
+ xfs_buf_item_init(bp, tp->t_mountp);
+
+ /*
+ * Set the recursion count for the buffer within this transaction
+ * to 0.
+ */
+ bip = XFS_BUF_FSPRIVATE(bp, xfs_buf_log_item_t*);
+ ASSERT(!(bip->bli_flags & XFS_BLI_STALE));
+ ASSERT(!(bip->bli_format.blf_flags & XFS_BLI_CANCEL));
+ ASSERT(!(bip->bli_flags & XFS_BLI_LOGGED));
+ bip->bli_recur = 0;
+
+ /*
+ * Take a reference for this transaction on the buf item.
+ */
+ atomic_inc(&bip->bli_refcount);
+
+ /*
+ * Get a log_item_desc to point at the new item.
+ */
+ (void) xfs_trans_add_item(tp, (xfs_log_item_t*)bip);
+
+ /*
+ * Initialize b_fsprivate2 so we can find it with incore_match()
+ * above.
+ */
+ XFS_BUF_SET_FSPRIVATE2(bp, tp);
+
+ xfs_buftrace("TRANS READ", bp);
+ xfs_buf_item_trace("READ", bip);
+ *bpp = bp;
+ return 0;
+
+shutdown_abort:
+ /*
+ * the theory here is that buffer is good but we're
+ * bailing out because the filesystem is being forcibly
+ * shut down. So we should leave the b_flags alone since
+ * the buffer's not staled and just get out.
+ */
+#if defined(DEBUG)
+ if (XFS_BUF_ISSTALE(bp) && XFS_BUF_ISDELAYWRITE(bp))
+ cmn_err(CE_NOTE, "about to pop assert, bp == 0x%p", bp);
+#endif
+ ASSERT((XFS_BUF_BFLAGS(bp) & (XFS_B_STALE|XFS_B_DELWRI)) !=
+ (XFS_B_STALE|XFS_B_DELWRI));
+
+ xfs_buftrace("READ_BUF XFSSHUTDN", bp);
+ xfs_buf_relse(bp);
+ *bpp = NULL;
+ return XFS_ERROR(EIO);
+}
+
+
+/*
+ * Release the buffer bp which was previously acquired with one of the
+ * xfs_trans_... buffer allocation routines if the buffer has not
+ * been modified within this transaction. If the buffer is modified
+ * within this transaction, do decrement the recursion count but do
+ * not release the buffer even if the count goes to 0. If the buffer is not
+ * modified within the transaction, decrement the recursion count and
+ * release the buffer if the recursion count goes to 0.
+ *
+ * If the buffer is to be released and it was not modified before
+ * this transaction began, then free the buf_log_item associated with it.
+ *
+ * If the transaction pointer is NULL, make this just a normal
+ * brelse() call.
+ */
+void
+xfs_trans_brelse(xfs_trans_t *tp,
+ xfs_buf_t *bp)
+{
+ xfs_buf_log_item_t *bip;
+ xfs_log_item_t *lip;
+ xfs_log_item_desc_t *lidp;
+
+ /*
+ * Default to a normal brelse() call if the tp is NULL.
+ */
+ if (tp == NULL) {
+ ASSERT(XFS_BUF_FSPRIVATE2(bp, void *) == NULL);
+ /*
+ * If there's a buf log item attached to the buffer,
+ * then let the AIL know that the buffer is being
+ * unlocked.
+ */
+ if (XFS_BUF_FSPRIVATE(bp, void *) != NULL) {
+ lip = XFS_BUF_FSPRIVATE(bp, xfs_log_item_t *);
+ if (lip->li_type == XFS_LI_BUF) {
+ bip = XFS_BUF_FSPRIVATE(bp,xfs_buf_log_item_t*);
+ xfs_trans_unlocked_item(
+ bip->bli_item.li_mountp,
+ lip);
+ }
+ }
+ xfs_buf_relse(bp);
+ return;
+ }
+
+ ASSERT(XFS_BUF_FSPRIVATE2(bp, xfs_trans_t *) == tp);
+ bip = XFS_BUF_FSPRIVATE(bp, xfs_buf_log_item_t *);
+ ASSERT(bip->bli_item.li_type == XFS_LI_BUF);
+ ASSERT(!(bip->bli_flags & XFS_BLI_STALE));
+ ASSERT(!(bip->bli_format.blf_flags & XFS_BLI_CANCEL));
+ ASSERT(atomic_read(&bip->bli_refcount) > 0);
+
+ /*
+ * Find the item descriptor pointing to this buffer's
+ * log item. It must be there.
+ */
+ lidp = xfs_trans_find_item(tp, (xfs_log_item_t*)bip);
+ ASSERT(lidp != NULL);
+
+ /*
+ * If the release is just for a recursive lock,
+ * then decrement the count and return.
+ */
+ if (bip->bli_recur > 0) {
+ bip->bli_recur--;
+ xfs_buf_item_trace("RELSE RECUR", bip);
+ return;
+ }
+
+ /*
+ * If the buffer is dirty within this transaction, we can't
+ * release it until we commit.
+ */
+ if (lidp->lid_flags & XFS_LID_DIRTY) {
+ xfs_buf_item_trace("RELSE DIRTY", bip);
+ return;
+ }
+
+ /*
+ * If the buffer has been invalidated, then we can't release
+ * it until the transaction commits to disk unless it is re-dirtied
+ * as part of this transaction. This prevents us from pulling
+ * the item from the AIL before we should.
+ */
+ if (bip->bli_flags & XFS_BLI_STALE) {
+ xfs_buf_item_trace("RELSE STALE", bip);
+ return;
+ }
+
+ ASSERT(!(bip->bli_flags & XFS_BLI_LOGGED));
+ xfs_buf_item_trace("RELSE", bip);
+
+ /*
+ * Free up the log item descriptor tracking the released item.
+ */
+ xfs_trans_free_item(tp, lidp);
+
+ /*
+ * Clear the hold flag in the buf log item if it is set.
+ * We wouldn't want the next user of the buffer to
+ * get confused.
+ */
+ if (bip->bli_flags & XFS_BLI_HOLD) {
+ bip->bli_flags &= ~XFS_BLI_HOLD;
+ }
+
+ /*
+ * Drop our reference to the buf log item.
+ */
+ atomic_dec(&bip->bli_refcount);
+
+ /*
+ * If the buf item is not tracking data in the log, then
+ * we must free it before releasing the buffer back to the
+ * free pool. Before releasing the buffer to the free pool,
+ * clear the transaction pointer in b_fsprivate2 to dissolve
+ * its relation to this transaction.
+ */
+ if (!xfs_buf_item_dirty(bip)) {
+/***
+ ASSERT(bp->b_pincount == 0);
+***/
+ ASSERT(atomic_read(&bip->bli_refcount) == 0);
+ ASSERT(!(bip->bli_item.li_flags & XFS_LI_IN_AIL));
+ ASSERT(!(bip->bli_flags & XFS_BLI_INODE_ALLOC_BUF));
+ xfs_buf_item_relse(bp);
+ bip = NULL;
+ }
+ XFS_BUF_SET_FSPRIVATE2(bp, NULL);
+
+ /*
+ * If we've still got a buf log item on the buffer, then
+ * tell the AIL that the buffer is being unlocked.
+ */
+ if (bip != NULL) {
+ xfs_trans_unlocked_item(bip->bli_item.li_mountp,
+ (xfs_log_item_t*)bip);
+ }
+
+ xfs_buf_relse(bp);
+ return;
+}
+
+/*
+ * Add the locked buffer to the transaction.
+ * The buffer must be locked, and it cannot be associated with any
+ * transaction.
+ *
+ * If the buffer does not yet have a buf log item associated with it,
+ * then allocate one for it. Then add the buf item to the transaction.
+ */
+void
+xfs_trans_bjoin(xfs_trans_t *tp,
+ xfs_buf_t *bp)
+{
+ xfs_buf_log_item_t *bip;
+
+ ASSERT(XFS_BUF_ISBUSY(bp));
+ ASSERT(XFS_BUF_FSPRIVATE2(bp, void *) == NULL);
+
+ /*
+ * The xfs_buf_log_item pointer is stored in b_fsprivate. If
+ * it doesn't have one yet, then allocate one and initialize it.
+ * The checks to see if one is there are in xfs_buf_item_init().
+ */
+ xfs_buf_item_init(bp, tp->t_mountp);
+ bip = XFS_BUF_FSPRIVATE(bp, xfs_buf_log_item_t *);
+ ASSERT(!(bip->bli_flags & XFS_BLI_STALE));
+ ASSERT(!(bip->bli_format.blf_flags & XFS_BLI_CANCEL));
+ ASSERT(!(bip->bli_flags & XFS_BLI_LOGGED));
+
+ /*
+ * Take a reference for this transaction on the buf item.
+ */
+ atomic_inc(&bip->bli_refcount);
+
+ /*
+ * Get a log_item_desc to point at the new item.
+ */
+ (void) xfs_trans_add_item(tp, (xfs_log_item_t *)bip);
+
+ /*
+ * Initialize b_fsprivate2 so we can find it with incore_match()
+ * in xfs_trans_get_buf() and friends above.
+ */
+ XFS_BUF_SET_FSPRIVATE2(bp, tp);
+
+ xfs_buf_item_trace("BJOIN", bip);
+}
+
+/*
+ * Mark the buffer as not needing to be unlocked when the buf item's
+ * IOP_UNLOCK() routine is called. The buffer must already be locked
+ * and associated with the given transaction.
+ */
+/* ARGSUSED */
+void
+xfs_trans_bhold(xfs_trans_t *tp,
+ xfs_buf_t *bp)
+{
+ xfs_buf_log_item_t *bip;
+
+ ASSERT(XFS_BUF_ISBUSY(bp));
+ ASSERT(XFS_BUF_FSPRIVATE2(bp, xfs_trans_t *) == tp);
+ ASSERT(XFS_BUF_FSPRIVATE(bp, void *) != NULL);
+
+ bip = XFS_BUF_FSPRIVATE(bp, xfs_buf_log_item_t *);
+ ASSERT(!(bip->bli_flags & XFS_BLI_STALE));
+ ASSERT(!(bip->bli_format.blf_flags & XFS_BLI_CANCEL));
+ ASSERT(atomic_read(&bip->bli_refcount) > 0);
+ bip->bli_flags |= XFS_BLI_HOLD;
+ xfs_buf_item_trace("BHOLD", bip);
+}
+
+/*
+ * This is called to mark bytes first through last inclusive of the given
+ * buffer as needing to be logged when the transaction is committed.
+ * The buffer must already be associated with the given transaction.
+ *
+ * First and last are numbers relative to the beginning of this buffer,
+ * so the first byte in the buffer is numbered 0 regardless of the
+ * value of b_blkno.
+ */
+void
+xfs_trans_log_buf(xfs_trans_t *tp,
+ xfs_buf_t *bp,
+ uint first,
+ uint last)
+{
+ xfs_buf_log_item_t *bip;
+ xfs_log_item_desc_t *lidp;
+
+ ASSERT(XFS_BUF_ISBUSY(bp));
+ ASSERT(XFS_BUF_FSPRIVATE2(bp, xfs_trans_t *) == tp);
+ ASSERT(XFS_BUF_FSPRIVATE(bp, void *) != NULL);
+ ASSERT((first <= last) && (last < XFS_BUF_COUNT(bp)));
+ ASSERT((XFS_BUF_IODONE_FUNC(bp) == NULL) ||
+ (XFS_BUF_IODONE_FUNC(bp) == xfs_buf_iodone_callbacks));
+
+ /*
+ * Mark the buffer as needing to be written out eventually,
+ * and set its iodone function to remove the buffer's buf log
+ * item from the AIL and free it when the buffer is flushed
+ * to disk. See xfs_buf_attach_iodone() for more details
+ * on li_cb and xfs_buf_iodone_callbacks().
+ * If we end up aborting this transaction, we trap this buffer
+ * inside the b_bdstrat callback so that this won't get written to
+ * disk.
+ */
+ XFS_BUF_DELAYWRITE(bp);
+ XFS_BUF_DONE(bp);
+
+ bip = XFS_BUF_FSPRIVATE(bp, xfs_buf_log_item_t *);
+ ASSERT(atomic_read(&bip->bli_refcount) > 0);
+ XFS_BUF_SET_IODONE_FUNC(bp, xfs_buf_iodone_callbacks);
+ bip->bli_item.li_cb = (void(*)(xfs_buf_t*,xfs_log_item_t*))xfs_buf_iodone;
+
+ /*
+ * If we invalidated the buffer within this transaction, then
+ * cancel the invalidation now that we're dirtying the buffer
+ * again. There are no races with the code in xfs_buf_item_unpin(),
+ * because we have a reference to the buffer this entire time.
+ */
+ if (bip->bli_flags & XFS_BLI_STALE) {
+ xfs_buf_item_trace("BLOG UNSTALE", bip);
+ bip->bli_flags &= ~XFS_BLI_STALE;
+ ASSERT(XFS_BUF_ISSTALE(bp));
+ XFS_BUF_UNSTALE(bp);
+ bip->bli_format.blf_flags &= ~XFS_BLI_CANCEL;
+ }
+
+ lidp = xfs_trans_find_item(tp, (xfs_log_item_t*)bip);
+ ASSERT(lidp != NULL);
+
+ tp->t_flags |= XFS_TRANS_DIRTY;
+ lidp->lid_flags |= XFS_LID_DIRTY;
+ lidp->lid_flags &= ~XFS_LID_BUF_STALE;
+ bip->bli_flags |= XFS_BLI_LOGGED;
+ xfs_buf_item_log(bip, first, last);
+ xfs_buf_item_trace("BLOG", bip);
+}
+
+
+/*
+ * This called to invalidate a buffer that is being used within
+ * a transaction. Typically this is because the blocks in the
+ * buffer are being freed, so we need to prevent it from being
+ * written out when we're done. Allowing it to be written again
+ * might overwrite data in the free blocks if they are reallocated
+ * to a file.
+ *
+ * We prevent the buffer from being written out by clearing the
+ * B_DELWRI flag. We can't always
+ * get rid of the buf log item at this point, though, because
+ * the buffer may still be pinned by another transaction. If that
+ * is the case, then we'll wait until the buffer is committed to
+ * disk for the last time (we can tell by the ref count) and
+ * free it in xfs_buf_item_unpin(). Until it is cleaned up we
+ * will keep the buffer locked so that the buffer and buf log item
+ * are not reused.
+ */
+void
+xfs_trans_binval(
+ xfs_trans_t *tp,
+ xfs_buf_t *bp)
+{
+ xfs_log_item_desc_t *lidp;
+ xfs_buf_log_item_t *bip;
+
+ ASSERT(XFS_BUF_ISBUSY(bp));
+ ASSERT(XFS_BUF_FSPRIVATE2(bp, xfs_trans_t *) == tp);
+ ASSERT(XFS_BUF_FSPRIVATE(bp, void *) != NULL);
+
+ bip = XFS_BUF_FSPRIVATE(bp, xfs_buf_log_item_t *);
+ lidp = xfs_trans_find_item(tp, (xfs_log_item_t*)bip);
+ ASSERT(lidp != NULL);
+ ASSERT(atomic_read(&bip->bli_refcount) > 0);
+
+ if (bip->bli_flags & XFS_BLI_STALE) {
+ /*
+ * If the buffer is already invalidated, then
+ * just return.
+ */
+ ASSERT(!(XFS_BUF_ISDELAYWRITE(bp)));
+ ASSERT(XFS_BUF_ISSTALE(bp));
+ ASSERT(!(bip->bli_flags & (XFS_BLI_LOGGED | XFS_BLI_DIRTY)));
+ ASSERT(!(bip->bli_format.blf_flags & XFS_BLI_INODE_BUF));
+ ASSERT(bip->bli_format.blf_flags & XFS_BLI_CANCEL);
+ ASSERT(lidp->lid_flags & XFS_LID_DIRTY);
+ ASSERT(tp->t_flags & XFS_TRANS_DIRTY);
+ xfs_buftrace("XFS_BINVAL RECUR", bp);
+ xfs_buf_item_trace("BINVAL RECUR", bip);
+ return;
+ }
+
+ /*
+ * Clear the dirty bit in the buffer and set the STALE flag
+ * in the buf log item. The STALE flag will be used in
+ * xfs_buf_item_unpin() to determine if it should clean up
+ * when the last reference to the buf item is given up.
+ * We set the XFS_BLI_CANCEL flag in the buf log format structure
+ * and log the buf item. This will be used at recovery time
+ * to determine that copies of the buffer in the log before
+ * this should not be replayed.
+ * We mark the item descriptor and the transaction dirty so
+ * that we'll hold the buffer until after the commit.
+ *
+ * Since we're invalidating the buffer, we also clear the state
+ * about which parts of the buffer have been logged. We also
+ * clear the flag indicating that this is an inode buffer since
+ * the data in the buffer will no longer be valid.
+ *
+ * We set the stale bit in the buffer as well since we're getting
+ * rid of it.
+ */
+ XFS_BUF_UNDELAYWRITE(bp);
+ XFS_BUF_STALE(bp);
+ bip->bli_flags |= XFS_BLI_STALE;
+ bip->bli_flags &= ~(XFS_BLI_LOGGED | XFS_BLI_DIRTY);
+ bip->bli_format.blf_flags &= ~XFS_BLI_INODE_BUF;
+ bip->bli_format.blf_flags |= XFS_BLI_CANCEL;
+ memset((char *)(bip->bli_format.blf_data_map), 0,
+ (bip->bli_format.blf_map_size * sizeof(uint)));
+ lidp->lid_flags |= XFS_LID_DIRTY|XFS_LID_BUF_STALE;
+ tp->t_flags |= XFS_TRANS_DIRTY;
+ xfs_buftrace("XFS_BINVAL", bp);
+ xfs_buf_item_trace("BINVAL", bip);
+}
+
+/*
+ * This call is used to indicate that the buffer contains on-disk
+ * inodes which must be handled specially during recovery. They
+ * require special handling because only the di_next_unlinked from
+ * the inodes in the buffer should be recovered. The rest of the
+ * data in the buffer is logged via the inodes themselves.
+ *
+ * All we do is set the XFS_BLI_INODE_BUF flag in the buffer's log
+ * format structure so that we'll know what to do at recovery time.
+ */
+/* ARGSUSED */
+void
+xfs_trans_inode_buf(
+ xfs_trans_t *tp,
+ xfs_buf_t *bp)
+{
+ xfs_buf_log_item_t *bip;
+
+ ASSERT(XFS_BUF_ISBUSY(bp));
+ ASSERT(XFS_BUF_FSPRIVATE2(bp, xfs_trans_t *) == tp);
+ ASSERT(XFS_BUF_FSPRIVATE(bp, void *) != NULL);
+
+ bip = XFS_BUF_FSPRIVATE(bp, xfs_buf_log_item_t *);
+ ASSERT(atomic_read(&bip->bli_refcount) > 0);
+
+ bip->bli_format.blf_flags |= XFS_BLI_INODE_BUF;
+}
+
+/*
+ * This call is used to indicate that the buffer is going to
+ * be staled and was an inode buffer. This means it gets
+ * special processing during unpin - where any inodes
+ * associated with the buffer should be removed from ail.
+ * There is also special processing during recovery,
+ * any replay of the inodes in the buffer needs to be
+ * prevented as the buffer may have been reused.
+ */
+void
+xfs_trans_stale_inode_buf(
+ xfs_trans_t *tp,
+ xfs_buf_t *bp)
+{
+ xfs_buf_log_item_t *bip;
+
+ ASSERT(XFS_BUF_ISBUSY(bp));
+ ASSERT(XFS_BUF_FSPRIVATE2(bp, xfs_trans_t *) == tp);
+ ASSERT(XFS_BUF_FSPRIVATE(bp, void *) != NULL);
+
+ bip = XFS_BUF_FSPRIVATE(bp, xfs_buf_log_item_t *);
+ ASSERT(atomic_read(&bip->bli_refcount) > 0);
+
+ bip->bli_flags |= XFS_BLI_STALE_INODE;
+ bip->bli_item.li_cb = (void(*)(xfs_buf_t*,xfs_log_item_t*))
+ xfs_buf_iodone;
+}
+
+
+
+/*
+ * Mark the buffer as being one which contains newly allocated
+ * inodes. We need to make sure that even if this buffer is
+ * relogged as an 'inode buf' we still recover all of the inode
+ * images in the face of a crash. This works in coordination with
+ * xfs_buf_item_committed() to ensure that the buffer remains in the
+ * AIL at its original location even after it has been relogged.
+ */
+/* ARGSUSED */
+void
+xfs_trans_inode_alloc_buf(
+ xfs_trans_t *tp,
+ xfs_buf_t *bp)
+{
+ xfs_buf_log_item_t *bip;
+
+ ASSERT(XFS_BUF_ISBUSY(bp));
+ ASSERT(XFS_BUF_FSPRIVATE2(bp, xfs_trans_t *) == tp);
+ ASSERT(XFS_BUF_FSPRIVATE(bp, void *) != NULL);
+
+ bip = XFS_BUF_FSPRIVATE(bp, xfs_buf_log_item_t *);
+ ASSERT(atomic_read(&bip->bli_refcount) > 0);
+
+ bip->bli_flags |= XFS_BLI_INODE_ALLOC_BUF;
+}
+
+
+/*
+ * Similar to xfs_trans_inode_buf(), this marks the buffer as a cluster of
+ * dquots. However, unlike in inode buffer recovery, dquot buffers get
+ * recovered in their entirety. (Hence, no XFS_BLI_DQUOT_ALLOC_BUF flag).
+ * The only thing that makes dquot buffers different from regular
+ * buffers is that we must not replay dquot bufs when recovering
+ * if a _corresponding_ quotaoff has happened. We also have to distinguish
+ * between usr dquot bufs and grp dquot bufs, because usr and grp quotas
+ * can be turned off independently.
+ */
+/* ARGSUSED */
+void
+xfs_trans_dquot_buf(
+ xfs_trans_t *tp,
+ xfs_buf_t *bp,
+ uint type)
+{
+ xfs_buf_log_item_t *bip;
+
+ ASSERT(XFS_BUF_ISBUSY(bp));
+ ASSERT(XFS_BUF_FSPRIVATE2(bp, xfs_trans_t *) == tp);
+ ASSERT(XFS_BUF_FSPRIVATE(bp, void *) != NULL);
+ ASSERT(type == XFS_BLI_UDQUOT_BUF ||
+ type == XFS_BLI_GDQUOT_BUF);
+
+ bip = XFS_BUF_FSPRIVATE(bp, xfs_buf_log_item_t *);
+ ASSERT(atomic_read(&bip->bli_refcount) > 0);
+
+ bip->bli_format.blf_flags |= type;
+}
+
+/*
+ * Check to see if a buffer matching the given parameters is already
+ * a part of the given transaction. Only check the first, embedded
+ * chunk, since we don't want to spend all day scanning large transactions.
+ */
+STATIC xfs_buf_t *
+xfs_trans_buf_item_match(
+ xfs_trans_t *tp,
+ xfs_buftarg_t *target,
+ xfs_daddr_t blkno,
+ int len)
+{
+ xfs_log_item_chunk_t *licp;
+ xfs_log_item_desc_t *lidp;
+ xfs_buf_log_item_t *blip;
+ xfs_buf_t *bp;
+ int i;
+
+ bp = NULL;
+ len = BBTOB(len);
+ licp = &tp->t_items;
+ if (!XFS_LIC_ARE_ALL_FREE(licp)) {
+ for (i = 0; i < licp->lic_unused; i++) {
+ /*
+ * Skip unoccupied slots.
+ */
+ if (XFS_LIC_ISFREE(licp, i)) {
+ continue;
+ }
+
+ lidp = XFS_LIC_SLOT(licp, i);
+ blip = (xfs_buf_log_item_t *)lidp->lid_item;
+ if (blip->bli_item.li_type != XFS_LI_BUF) {
+ continue;
+ }
+
+ bp = blip->bli_buf;
+ if ((XFS_BUF_TARGET(bp) == target) &&
+ (XFS_BUF_ADDR(bp) == blkno) &&
+ (XFS_BUF_COUNT(bp) == len)) {
+ /*
+ * We found it. Break out and
+ * return the pointer to the buffer.
+ */
+ break;
+ } else {
+ bp = NULL;
+ }
+ }
+ }
+ return bp;
+}
+
+/*
+ * Check to see if a buffer matching the given parameters is already
+ * a part of the given transaction. Check all the chunks, we
+ * want to be thorough.
+ */
+STATIC xfs_buf_t *
+xfs_trans_buf_item_match_all(
+ xfs_trans_t *tp,
+ xfs_buftarg_t *target,
+ xfs_daddr_t blkno,
+ int len)
+{
+ xfs_log_item_chunk_t *licp;
+ xfs_log_item_desc_t *lidp;
+ xfs_buf_log_item_t *blip;
+ xfs_buf_t *bp;
+ int i;
+
+ bp = NULL;
+ len = BBTOB(len);
+ for (licp = &tp->t_items; licp != NULL; licp = licp->lic_next) {
+ if (XFS_LIC_ARE_ALL_FREE(licp)) {
+ ASSERT(licp == &tp->t_items);
+ ASSERT(licp->lic_next == NULL);
+ return NULL;
+ }
+ for (i = 0; i < licp->lic_unused; i++) {
+ /*
+ * Skip unoccupied slots.
+ */
+ if (XFS_LIC_ISFREE(licp, i)) {
+ continue;
+ }
+
+ lidp = XFS_LIC_SLOT(licp, i);
+ blip = (xfs_buf_log_item_t *)lidp->lid_item;
+ if (blip->bli_item.li_type != XFS_LI_BUF) {
+ continue;
+ }
+
+ bp = blip->bli_buf;
+ if ((XFS_BUF_TARGET(bp) == target) &&
+ (XFS_BUF_ADDR(bp) == blkno) &&
+ (XFS_BUF_COUNT(bp) == len)) {
+ /*
+ * We found it. Break out and
+ * return the pointer to the buffer.
+ */
+ return bp;
+ }
+ }
+ }
+ return NULL;
+}
diff --git a/fs/xfs/xfs_trans_extfree.c b/fs/xfs/xfs_trans_extfree.c
new file mode 100644
index 00000000000000..93259a15f983d7
--- /dev/null
+++ b/fs/xfs/xfs_trans_extfree.c
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_dir.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_trans_priv.h"
+#include "xfs_extfree_item.h"
+
+/*
+ * This routine is called to allocate an "extent free intention"
+ * log item that will hold nextents worth of extents. The
+ * caller must use all nextents extents, because we are not
+ * flexible about this at all.
+ */
+xfs_efi_log_item_t *
+xfs_trans_get_efi(xfs_trans_t *tp,
+ uint nextents)
+{
+ xfs_efi_log_item_t *efip;
+
+ ASSERT(tp != NULL);
+ ASSERT(nextents > 0);
+
+ efip = xfs_efi_init(tp->t_mountp, nextents);
+ ASSERT(efip != NULL);
+
+ /*
+ * Get a log_item_desc to point at the new item.
+ */
+ (void) xfs_trans_add_item(tp, (xfs_log_item_t*)efip);
+
+ return (efip);
+}
+
+/*
+ * This routine is called to indicate that the described
+ * extent is to be logged as needing to be freed. It should
+ * be called once for each extent to be freed.
+ */
+void
+xfs_trans_log_efi_extent(xfs_trans_t *tp,
+ xfs_efi_log_item_t *efip,
+ xfs_fsblock_t start_block,
+ xfs_extlen_t ext_len)
+{
+ xfs_log_item_desc_t *lidp;
+ uint next_extent;
+ xfs_extent_t *extp;
+
+ lidp = xfs_trans_find_item(tp, (xfs_log_item_t*)efip);
+ ASSERT(lidp != NULL);
+
+ tp->t_flags |= XFS_TRANS_DIRTY;
+ lidp->lid_flags |= XFS_LID_DIRTY;
+
+ next_extent = efip->efi_next_extent;
+ ASSERT(next_extent < efip->efi_format.efi_nextents);
+ extp = &(efip->efi_format.efi_extents[next_extent]);
+ extp->ext_start = start_block;
+ extp->ext_len = ext_len;
+ efip->efi_next_extent++;
+}
+
+
+/*
+ * This routine is called to allocate an "extent free done"
+ * log item that will hold nextents worth of extents. The
+ * caller must use all nextents extents, because we are not
+ * flexible about this at all.
+ */
+xfs_efd_log_item_t *
+xfs_trans_get_efd(xfs_trans_t *tp,
+ xfs_efi_log_item_t *efip,
+ uint nextents)
+{
+ xfs_efd_log_item_t *efdp;
+
+ ASSERT(tp != NULL);
+ ASSERT(nextents > 0);
+
+ efdp = xfs_efd_init(tp->t_mountp, efip, nextents);
+ ASSERT(efdp != NULL);
+
+ /*
+ * Get a log_item_desc to point at the new item.
+ */
+ (void) xfs_trans_add_item(tp, (xfs_log_item_t*)efdp);
+
+ return (efdp);
+}
+
+/*
+ * This routine is called to indicate that the described
+ * extent is to be logged as having been freed. It should
+ * be called once for each extent freed.
+ */
+void
+xfs_trans_log_efd_extent(xfs_trans_t *tp,
+ xfs_efd_log_item_t *efdp,
+ xfs_fsblock_t start_block,
+ xfs_extlen_t ext_len)
+{
+ xfs_log_item_desc_t *lidp;
+ uint next_extent;
+ xfs_extent_t *extp;
+
+ lidp = xfs_trans_find_item(tp, (xfs_log_item_t*)efdp);
+ ASSERT(lidp != NULL);
+
+ tp->t_flags |= XFS_TRANS_DIRTY;
+ lidp->lid_flags |= XFS_LID_DIRTY;
+
+ next_extent = efdp->efd_next_extent;
+ ASSERT(next_extent < efdp->efd_format.efd_nextents);
+ extp = &(efdp->efd_format.efd_extents[next_extent]);
+ extp->ext_start = start_block;
+ extp->ext_len = ext_len;
+ efdp->efd_next_extent++;
+}
diff --git a/fs/xfs/xfs_trans_inode.c b/fs/xfs/xfs_trans_inode.c
new file mode 100644
index 00000000000000..e2c3706f453de7
--- /dev/null
+++ b/fs/xfs/xfs_trans_inode.c
@@ -0,0 +1,342 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_trans_priv.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode_item.h"
+#include "xfs_inode.h"
+
+#ifdef XFS_TRANS_DEBUG
+STATIC void
+xfs_trans_inode_broot_debug(
+ xfs_inode_t *ip);
+#else
+#define xfs_trans_inode_broot_debug(ip)
+#endif
+
+
+/*
+ * Get and lock the inode for the caller if it is not already
+ * locked within the given transaction. If it is already locked
+ * within the transaction, just increment its lock recursion count
+ * and return a pointer to it.
+ *
+ * For an inode to be locked in a transaction, the inode lock, as
+ * opposed to the io lock, must be taken exclusively. This ensures
+ * that the inode can be involved in only 1 transaction at a time.
+ * Lock recursion is handled on the io lock, but only for lock modes
+ * of equal or lesser strength. That is, you can recur on the io lock
+ * held EXCL with a SHARED request but not vice versa. Also, if
+ * the inode is already a part of the transaction then you cannot
+ * go from not holding the io lock to having it EXCL or SHARED.
+ *
+ * Use the inode cache routine xfs_inode_incore() to find the inode
+ * if it is already owned by this transaction.
+ *
+ * If we don't already own the inode, use xfs_iget() to get it.
+ * Since the inode log item structure is embedded in the incore
+ * inode structure and is initialized when the inode is brought
+ * into memory, there is nothing to do with it here.
+ *
+ * If the given transaction pointer is NULL, just call xfs_iget().
+ * This simplifies code which must handle both cases.
+ */
+int
+xfs_trans_iget(
+ xfs_mount_t *mp,
+ xfs_trans_t *tp,
+ xfs_ino_t ino,
+ uint flags,
+ uint lock_flags,
+ xfs_inode_t **ipp)
+{
+ int error;
+ xfs_inode_t *ip;
+ xfs_inode_log_item_t *iip;
+
+ /*
+ * If the transaction pointer is NULL, just call the normal
+ * xfs_iget().
+ */
+ if (tp == NULL)
+ return xfs_iget(mp, NULL, ino, flags, lock_flags, ipp, 0);
+
+ /*
+ * If we find the inode in core with this transaction
+ * pointer in its i_transp field, then we know we already
+ * have it locked. In this case we just increment the lock
+ * recursion count and return the inode to the caller.
+ * Assert that the inode is already locked in the mode requested
+ * by the caller. We cannot do lock promotions yet, so
+ * die if someone gets this wrong.
+ */
+ if ((ip = xfs_inode_incore(tp->t_mountp, ino, tp)) != NULL) {
+ /*
+ * Make sure that the inode lock is held EXCL and
+ * that the io lock is never upgraded when the inode
+ * is already a part of the transaction.
+ */
+ ASSERT(ip->i_itemp != NULL);
+ ASSERT(lock_flags & XFS_ILOCK_EXCL);
+ ASSERT(ismrlocked(&ip->i_lock, MR_UPDATE));
+ ASSERT((!(lock_flags & XFS_IOLOCK_EXCL)) ||
+ ismrlocked(&ip->i_iolock, MR_UPDATE));
+ ASSERT((!(lock_flags & XFS_IOLOCK_EXCL)) ||
+ (ip->i_itemp->ili_flags & XFS_ILI_IOLOCKED_EXCL));
+ ASSERT((!(lock_flags & XFS_IOLOCK_SHARED)) ||
+ ismrlocked(&ip->i_iolock, (MR_UPDATE | MR_ACCESS)));
+ ASSERT((!(lock_flags & XFS_IOLOCK_SHARED)) ||
+ (ip->i_itemp->ili_flags & XFS_ILI_IOLOCKED_ANY));
+
+ if (lock_flags & (XFS_IOLOCK_SHARED | XFS_IOLOCK_EXCL)) {
+ ip->i_itemp->ili_iolock_recur++;
+ }
+ if (lock_flags & XFS_ILOCK_EXCL) {
+ ip->i_itemp->ili_ilock_recur++;
+ }
+ *ipp = ip;
+ return 0;
+ }
+
+ ASSERT(lock_flags & XFS_ILOCK_EXCL);
+ error = xfs_iget(tp->t_mountp, tp, ino, flags, lock_flags, &ip, 0);
+ if (error) {
+ return error;
+ }
+ ASSERT(ip != NULL);
+
+ /*
+ * Get a log_item_desc to point at the new item.
+ */
+ if (ip->i_itemp == NULL)
+ xfs_inode_item_init(ip, mp);
+ iip = ip->i_itemp;
+ (void) xfs_trans_add_item(tp, (xfs_log_item_t *)(iip));
+
+ xfs_trans_inode_broot_debug(ip);
+
+ /*
+ * If the IO lock has been acquired, mark that in
+ * the inode log item so we'll know to unlock it
+ * when the transaction commits.
+ */
+ ASSERT(iip->ili_flags == 0);
+ if (lock_flags & XFS_IOLOCK_EXCL) {
+ iip->ili_flags |= XFS_ILI_IOLOCKED_EXCL;
+ } else if (lock_flags & XFS_IOLOCK_SHARED) {
+ iip->ili_flags |= XFS_ILI_IOLOCKED_SHARED;
+ }
+
+ /*
+ * Initialize i_transp so we can find it with xfs_inode_incore()
+ * above.
+ */
+ ip->i_transp = tp;
+
+ *ipp = ip;
+ return 0;
+}
+
+/*
+ * Add the locked inode to the transaction.
+ * The inode must be locked, and it cannot be associated with any
+ * transaction. The caller must specify the locks already held
+ * on the inode.
+ */
+void
+xfs_trans_ijoin(
+ xfs_trans_t *tp,
+ xfs_inode_t *ip,
+ uint lock_flags)
+{
+ xfs_inode_log_item_t *iip;
+
+ ASSERT(ip->i_transp == NULL);
+ ASSERT(ismrlocked(&ip->i_lock, MR_UPDATE));
+ ASSERT(lock_flags & XFS_ILOCK_EXCL);
+ if (ip->i_itemp == NULL)
+ xfs_inode_item_init(ip, ip->i_mount);
+ iip = ip->i_itemp;
+ ASSERT(iip->ili_flags == 0);
+ ASSERT(iip->ili_ilock_recur == 0);
+ ASSERT(iip->ili_iolock_recur == 0);
+
+ /*
+ * Get a log_item_desc to point at the new item.
+ */
+ (void) xfs_trans_add_item(tp, (xfs_log_item_t*)(iip));
+
+ xfs_trans_inode_broot_debug(ip);
+
+ /*
+ * If the IO lock is already held, mark that in the inode log item.
+ */
+ if (lock_flags & XFS_IOLOCK_EXCL) {
+ iip->ili_flags |= XFS_ILI_IOLOCKED_EXCL;
+ } else if (lock_flags & XFS_IOLOCK_SHARED) {
+ iip->ili_flags |= XFS_ILI_IOLOCKED_SHARED;
+ }
+
+ /*
+ * Initialize i_transp so we can find it with xfs_inode_incore()
+ * in xfs_trans_iget() above.
+ */
+ ip->i_transp = tp;
+}
+
+
+
+/*
+ * Mark the inode as not needing to be unlocked when the inode item's
+ * IOP_UNLOCK() routine is called. The inode must already be locked
+ * and associated with the given transaction.
+ */
+/*ARGSUSED*/
+void
+xfs_trans_ihold(
+ xfs_trans_t *tp,
+ xfs_inode_t *ip)
+{
+ ASSERT(ip->i_transp == tp);
+ ASSERT(ip->i_itemp != NULL);
+ ASSERT(ismrlocked(&ip->i_lock, MR_UPDATE));
+
+ ip->i_itemp->ili_flags |= XFS_ILI_HOLD;
+}
+
+/*
+ * Cancel the previous inode hold request made on this inode
+ * for this transaction.
+ */
+/*ARGSUSED*/
+void
+xfs_trans_ihold_release(
+ xfs_trans_t *tp,
+ xfs_inode_t *ip)
+{
+ ASSERT(ip->i_transp == tp);
+ ASSERT(ip->i_itemp != NULL);
+ ASSERT(ismrlocked(&ip->i_lock, MR_UPDATE));
+ ASSERT(ip->i_itemp->ili_flags & XFS_ILI_HOLD);
+
+ ip->i_itemp->ili_flags &= ~XFS_ILI_HOLD;
+}
+
+
+/*
+ * This is called to mark the fields indicated in fieldmask as needing
+ * to be logged when the transaction is committed. The inode must
+ * already be associated with the given transaction.
+ *
+ * The values for fieldmask are defined in xfs_inode_item.h. We always
+ * log all of the core inode if any of it has changed, and we always log
+ * all of the inline data/extents/b-tree root if any of them has changed.
+ */
+void
+xfs_trans_log_inode(
+ xfs_trans_t *tp,
+ xfs_inode_t *ip,
+ uint flags)
+{
+ xfs_log_item_desc_t *lidp;
+
+ ASSERT(ip->i_transp == tp);
+ ASSERT(ip->i_itemp != NULL);
+ ASSERT(ismrlocked(&ip->i_lock, MR_UPDATE));
+
+ lidp = xfs_trans_find_item(tp, (xfs_log_item_t*)(ip->i_itemp));
+ ASSERT(lidp != NULL);
+
+ tp->t_flags |= XFS_TRANS_DIRTY;
+ lidp->lid_flags |= XFS_LID_DIRTY;
+
+ /*
+ * Always OR in the bits from the ili_last_fields field.
+ * This is to coordinate with the xfs_iflush() and xfs_iflush_done()
+ * routines in the eventual clearing of the ilf_fields bits.
+ * See the big comment in xfs_iflush() for an explanation of
+ * this coorination mechanism.
+ */
+ flags |= ip->i_itemp->ili_last_fields;
+ ip->i_itemp->ili_format.ilf_fields |= flags;
+}
+
+#ifdef XFS_TRANS_DEBUG
+/*
+ * Keep track of the state of the inode btree root to make sure we
+ * log it properly.
+ */
+STATIC void
+xfs_trans_inode_broot_debug(
+ xfs_inode_t *ip)
+{
+ xfs_inode_log_item_t *iip;
+
+ ASSERT(ip->i_itemp != NULL);
+ iip = ip->i_itemp;
+ if (iip->ili_root_size != 0) {
+ ASSERT(iip->ili_orig_root != NULL);
+ kmem_free(iip->ili_orig_root, iip->ili_root_size);
+ iip->ili_root_size = 0;
+ iip->ili_orig_root = NULL;
+ }
+ if (ip->i_d.di_format == XFS_DINODE_FMT_BTREE) {
+ ASSERT((ip->i_df.if_broot != NULL) &&
+ (ip->i_df.if_broot_bytes > 0));
+ iip->ili_root_size = ip->i_df.if_broot_bytes;
+ iip->ili_orig_root =
+ (char*)kmem_alloc(iip->ili_root_size, KM_SLEEP);
+ memcpy(iip->ili_orig_root, (char*)(ip->i_df.if_broot),
+ iip->ili_root_size);
+ }
+}
+#endif
diff --git a/fs/xfs/xfs_trans_item.c b/fs/xfs/xfs_trans_item.c
new file mode 100644
index 00000000000000..1b8a756d80ed00
--- /dev/null
+++ b/fs/xfs/xfs_trans_item.c
@@ -0,0 +1,553 @@
+/*
+ * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+
+STATIC int xfs_trans_unlock_chunk(xfs_log_item_chunk_t *,
+ int, int, xfs_lsn_t);
+
+/*
+ * This is called to add the given log item to the transaction's
+ * list of log items. It must find a free log item descriptor
+ * or allocate a new one and add the item to that descriptor.
+ * The function returns a pointer to item descriptor used to point
+ * to the new item. The log item will now point to its new descriptor
+ * with its li_desc field.
+ */
+xfs_log_item_desc_t *
+xfs_trans_add_item(xfs_trans_t *tp, xfs_log_item_t *lip)
+{
+ xfs_log_item_desc_t *lidp;
+ xfs_log_item_chunk_t *licp;
+ int i=0;
+
+ /*
+ * If there are no free descriptors, allocate a new chunk
+ * of them and put it at the front of the chunk list.
+ */
+ if (tp->t_items_free == 0) {
+ licp = (xfs_log_item_chunk_t*)
+ kmem_alloc(sizeof(xfs_log_item_chunk_t), KM_SLEEP);
+ ASSERT(licp != NULL);
+ /*
+ * Initialize the chunk, and then
+ * claim the first slot in the newly allocated chunk.
+ */
+ XFS_LIC_INIT(licp);
+ XFS_LIC_CLAIM(licp, 0);
+ licp->lic_unused = 1;
+ XFS_LIC_INIT_SLOT(licp, 0);
+ lidp = XFS_LIC_SLOT(licp, 0);
+
+ /*
+ * Link in the new chunk and update the free count.
+ */
+ licp->lic_next = tp->t_items.lic_next;
+ tp->t_items.lic_next = licp;
+ tp->t_items_free = XFS_LIC_NUM_SLOTS - 1;
+
+ /*
+ * Initialize the descriptor and the generic portion
+ * of the log item.
+ *
+ * Point the new slot at this item and return it.
+ * Also point the log item at its currently active
+ * descriptor and set the item's mount pointer.
+ */
+ lidp->lid_item = lip;
+ lidp->lid_flags = 0;
+ lidp->lid_size = 0;
+ lip->li_desc = lidp;
+ lip->li_mountp = tp->t_mountp;
+ return (lidp);
+ }
+
+ /*
+ * Find the free descriptor. It is somewhere in the chunklist
+ * of descriptors.
+ */
+ licp = &tp->t_items;
+ while (licp != NULL) {
+ if (XFS_LIC_VACANCY(licp)) {
+ if (licp->lic_unused <= XFS_LIC_MAX_SLOT) {
+ i = licp->lic_unused;
+ ASSERT(XFS_LIC_ISFREE(licp, i));
+ break;
+ }
+ for (i = 0; i <= XFS_LIC_MAX_SLOT; i++) {
+ if (XFS_LIC_ISFREE(licp, i))
+ break;
+ }
+ ASSERT(i <= XFS_LIC_MAX_SLOT);
+ break;
+ }
+ licp = licp->lic_next;
+ }
+ ASSERT(licp != NULL);
+ /*
+ * If we find a free descriptor, claim it,
+ * initialize it, and return it.
+ */
+ XFS_LIC_CLAIM(licp, i);
+ if (licp->lic_unused <= i) {
+ licp->lic_unused = i + 1;
+ XFS_LIC_INIT_SLOT(licp, i);
+ }
+ lidp = XFS_LIC_SLOT(licp, i);
+ tp->t_items_free--;
+ lidp->lid_item = lip;
+ lidp->lid_flags = 0;
+ lidp->lid_size = 0;
+ lip->li_desc = lidp;
+ lip->li_mountp = tp->t_mountp;
+ return (lidp);
+}
+
+/*
+ * Free the given descriptor.
+ *
+ * This requires setting the bit in the chunk's free mask corresponding
+ * to the given slot.
+ */
+void
+xfs_trans_free_item(xfs_trans_t *tp, xfs_log_item_desc_t *lidp)
+{
+ uint slot;
+ xfs_log_item_chunk_t *licp;
+ xfs_log_item_chunk_t **licpp;
+
+ slot = XFS_LIC_DESC_TO_SLOT(lidp);
+ licp = XFS_LIC_DESC_TO_CHUNK(lidp);
+ XFS_LIC_RELSE(licp, slot);
+ lidp->lid_item->li_desc = NULL;
+ tp->t_items_free++;
+
+ /*
+ * If there are no more used items in the chunk and this is not
+ * the chunk embedded in the transaction structure, then free
+ * the chunk. First pull it from the chunk list and then
+ * free it back to the heap. We didn't bother with a doubly
+ * linked list here because the lists should be very short
+ * and this is not a performance path. It's better to save
+ * the memory of the extra pointer.
+ *
+ * Also decrement the transaction structure's count of free items
+ * by the number in a chunk since we are freeing an empty chunk.
+ */
+ if (XFS_LIC_ARE_ALL_FREE(licp) && (licp != &(tp->t_items))) {
+ licpp = &(tp->t_items.lic_next);
+ while (*licpp != licp) {
+ ASSERT(*licpp != NULL);
+ licpp = &((*licpp)->lic_next);
+ }
+ *licpp = licp->lic_next;
+ kmem_free(licp, sizeof(xfs_log_item_chunk_t));
+ tp->t_items_free -= XFS_LIC_NUM_SLOTS;
+ }
+}
+
+/*
+ * This is called to find the descriptor corresponding to the given
+ * log item. It returns a pointer to the descriptor.
+ * The log item MUST have a corresponding descriptor in the given
+ * transaction. This routine does not return NULL, it panics.
+ *
+ * The descriptor pointer is kept in the log item's li_desc field.
+ * Just return it.
+ */
+/*ARGSUSED*/
+xfs_log_item_desc_t *
+xfs_trans_find_item(xfs_trans_t *tp, xfs_log_item_t *lip)
+{
+ ASSERT(lip->li_desc != NULL);
+
+ return (lip->li_desc);
+}
+
+
+/*
+ * Return a pointer to the first descriptor in the chunk list.
+ * This does not return NULL if there are none, it panics.
+ *
+ * The first descriptor must be in either the first or second chunk.
+ * This is because the only chunk allowed to be empty is the first.
+ * All others are freed when they become empty.
+ *
+ * At some point this and xfs_trans_next_item() should be optimized
+ * to quickly look at the mask to determine if there is anything to
+ * look at.
+ */
+xfs_log_item_desc_t *
+xfs_trans_first_item(xfs_trans_t *tp)
+{
+ xfs_log_item_chunk_t *licp;
+ int i;
+
+ licp = &tp->t_items;
+ /*
+ * If it's not in the first chunk, skip to the second.
+ */
+ if (XFS_LIC_ARE_ALL_FREE(licp)) {
+ licp = licp->lic_next;
+ }
+
+ /*
+ * Return the first non-free descriptor in the chunk.
+ */
+ ASSERT(!XFS_LIC_ARE_ALL_FREE(licp));
+ for (i = 0; i < licp->lic_unused; i++) {
+ if (XFS_LIC_ISFREE(licp, i)) {
+ continue;
+ }
+
+ return (XFS_LIC_SLOT(licp, i));
+ }
+ cmn_err(CE_WARN, "xfs_trans_first_item() -- no first item");
+ return(NULL);
+}
+
+
+/*
+ * Given a descriptor, return the next descriptor in the chunk list.
+ * This returns NULL if there are no more used descriptors in the list.
+ *
+ * We do this by first locating the chunk in which the descriptor resides,
+ * and then scanning forward in the chunk and the list for the next
+ * used descriptor.
+ */
+/*ARGSUSED*/
+xfs_log_item_desc_t *
+xfs_trans_next_item(xfs_trans_t *tp, xfs_log_item_desc_t *lidp)
+{
+ xfs_log_item_chunk_t *licp;
+ int i;
+
+ licp = XFS_LIC_DESC_TO_CHUNK(lidp);
+
+ /*
+ * First search the rest of the chunk. The for loop keeps us
+ * from referencing things beyond the end of the chunk.
+ */
+ for (i = (int)XFS_LIC_DESC_TO_SLOT(lidp) + 1; i < licp->lic_unused; i++) {
+ if (XFS_LIC_ISFREE(licp, i)) {
+ continue;
+ }
+
+ return (XFS_LIC_SLOT(licp, i));
+ }
+
+ /*
+ * Now search the next chunk. It must be there, because the
+ * next chunk would have been freed if it were empty.
+ * If there is no next chunk, return NULL.
+ */
+ if (licp->lic_next == NULL) {
+ return (NULL);
+ }
+
+ licp = licp->lic_next;
+ ASSERT(!XFS_LIC_ARE_ALL_FREE(licp));
+ for (i = 0; i < licp->lic_unused; i++) {
+ if (XFS_LIC_ISFREE(licp, i)) {
+ continue;
+ }
+
+ return (XFS_LIC_SLOT(licp, i));
+ }
+ ASSERT(0);
+ /* NOTREACHED */
+ return NULL; /* keep gcc quite */
+}
+
+/*
+ * This is called to unlock all of the items of a transaction and to free
+ * all the descriptors of that transaction.
+ *
+ * It walks the list of descriptors and unlocks each item. It frees
+ * each chunk except that embedded in the transaction as it goes along.
+ */
+void
+xfs_trans_free_items(
+ xfs_trans_t *tp,
+ int flags)
+{
+ xfs_log_item_chunk_t *licp;
+ xfs_log_item_chunk_t *next_licp;
+ int abort;
+
+ abort = flags & XFS_TRANS_ABORT;
+ licp = &tp->t_items;
+ /*
+ * Special case the embedded chunk so we don't free it below.
+ */
+ if (!XFS_LIC_ARE_ALL_FREE(licp)) {
+ (void) xfs_trans_unlock_chunk(licp, 1, abort, NULLCOMMITLSN);
+ XFS_LIC_ALL_FREE(licp);
+ licp->lic_unused = 0;
+ }
+ licp = licp->lic_next;
+
+ /*
+ * Unlock each item in each chunk and free the chunks.
+ */
+ while (licp != NULL) {
+ ASSERT(!XFS_LIC_ARE_ALL_FREE(licp));
+ (void) xfs_trans_unlock_chunk(licp, 1, abort, NULLCOMMITLSN);
+ next_licp = licp->lic_next;
+ kmem_free(licp, sizeof(xfs_log_item_chunk_t));
+ licp = next_licp;
+ }
+
+ /*
+ * Reset the transaction structure's free item count.
+ */
+ tp->t_items_free = XFS_LIC_NUM_SLOTS;
+ tp->t_items.lic_next = NULL;
+}
+
+
+
+/*
+ * This is called to unlock the items associated with a transaction.
+ * Items which were not logged should be freed.
+ * Those which were logged must still be tracked so they can be unpinned
+ * when the transaction commits.
+ */
+void
+xfs_trans_unlock_items(xfs_trans_t *tp, xfs_lsn_t commit_lsn)
+{
+ xfs_log_item_chunk_t *licp;
+ xfs_log_item_chunk_t *next_licp;
+ xfs_log_item_chunk_t **licpp;
+ int freed;
+
+ freed = 0;
+ licp = &tp->t_items;
+
+ /*
+ * Special case the embedded chunk so we don't free.
+ */
+ if (!XFS_LIC_ARE_ALL_FREE(licp)) {
+ freed = xfs_trans_unlock_chunk(licp, 0, 0, commit_lsn);
+ }
+ licpp = &(tp->t_items.lic_next);
+ licp = licp->lic_next;
+
+ /*
+ * Unlock each item in each chunk, free non-dirty descriptors,
+ * and free empty chunks.
+ */
+ while (licp != NULL) {
+ ASSERT(!XFS_LIC_ARE_ALL_FREE(licp));
+ freed += xfs_trans_unlock_chunk(licp, 0, 0, commit_lsn);
+ next_licp = licp->lic_next;
+ if (XFS_LIC_ARE_ALL_FREE(licp)) {
+ *licpp = next_licp;
+ kmem_free(licp, sizeof(xfs_log_item_chunk_t));
+ freed -= XFS_LIC_NUM_SLOTS;
+ } else {
+ licpp = &(licp->lic_next);
+ }
+ ASSERT(*licpp == next_licp);
+ licp = next_licp;
+ }
+
+ /*
+ * Fix the free descriptor count in the transaction.
+ */
+ tp->t_items_free += freed;
+}
+
+/*
+ * Unlock each item pointed to by a descriptor in the given chunk.
+ * Stamp the commit lsn into each item if necessary.
+ * Free descriptors pointing to items which are not dirty if freeing_chunk
+ * is zero. If freeing_chunk is non-zero, then we need to unlock all
+ * items in the chunk.
+ *
+ * Return the number of descriptors freed.
+ */
+STATIC int
+xfs_trans_unlock_chunk(
+ xfs_log_item_chunk_t *licp,
+ int freeing_chunk,
+ int abort,
+ xfs_lsn_t commit_lsn)
+{
+ xfs_log_item_desc_t *lidp;
+ xfs_log_item_t *lip;
+ int i;
+ int freed;
+
+ freed = 0;
+ lidp = licp->lic_descs;
+ for (i = 0; i < licp->lic_unused; i++, lidp++) {
+ if (XFS_LIC_ISFREE(licp, i)) {
+ continue;
+ }
+ lip = lidp->lid_item;
+ lip->li_desc = NULL;
+
+ if (commit_lsn != NULLCOMMITLSN)
+ IOP_COMMITTING(lip, commit_lsn);
+ if (abort)
+ lip->li_flags |= XFS_LI_ABORTED;
+ IOP_UNLOCK(lip);
+
+ /*
+ * Free the descriptor if the item is not dirty
+ * within this transaction and the caller is not
+ * going to just free the entire thing regardless.
+ */
+ if (!(freeing_chunk) &&
+ (!(lidp->lid_flags & XFS_LID_DIRTY) || abort)) {
+ XFS_LIC_RELSE(licp, i);
+ freed++;
+ }
+ }
+
+ return (freed);
+}
+
+
+/*
+ * This is called to add the given busy item to the transaction's
+ * list of busy items. It must find a free busy item descriptor
+ * or allocate a new one and add the item to that descriptor.
+ * The function returns a pointer to busy descriptor used to point
+ * to the new busy entry. The log busy entry will now point to its new
+ * descriptor with its ???? field.
+ */
+xfs_log_busy_slot_t *
+xfs_trans_add_busy(xfs_trans_t *tp, xfs_agnumber_t ag, xfs_extlen_t idx)
+{
+ xfs_log_busy_chunk_t *lbcp;
+ xfs_log_busy_slot_t *lbsp;
+ int i=0;
+
+ /*
+ * If there are no free descriptors, allocate a new chunk
+ * of them and put it at the front of the chunk list.
+ */
+ if (tp->t_busy_free == 0) {
+ lbcp = (xfs_log_busy_chunk_t*)
+ kmem_alloc(sizeof(xfs_log_busy_chunk_t), KM_SLEEP);
+ ASSERT(lbcp != NULL);
+ /*
+ * Initialize the chunk, and then
+ * claim the first slot in the newly allocated chunk.
+ */
+ XFS_LBC_INIT(lbcp);
+ XFS_LBC_CLAIM(lbcp, 0);
+ lbcp->lbc_unused = 1;
+ lbsp = XFS_LBC_SLOT(lbcp, 0);
+
+ /*
+ * Link in the new chunk and update the free count.
+ */
+ lbcp->lbc_next = tp->t_busy.lbc_next;
+ tp->t_busy.lbc_next = lbcp;
+ tp->t_busy_free = XFS_LIC_NUM_SLOTS - 1;
+
+ /*
+ * Initialize the descriptor and the generic portion
+ * of the log item.
+ *
+ * Point the new slot at this item and return it.
+ * Also point the log item at its currently active
+ * descriptor and set the item's mount pointer.
+ */
+ lbsp->lbc_ag = ag;
+ lbsp->lbc_idx = idx;
+ return (lbsp);
+ }
+
+ /*
+ * Find the free descriptor. It is somewhere in the chunklist
+ * of descriptors.
+ */
+ lbcp = &tp->t_busy;
+ while (lbcp != NULL) {
+ if (XFS_LBC_VACANCY(lbcp)) {
+ if (lbcp->lbc_unused <= XFS_LBC_MAX_SLOT) {
+ i = lbcp->lbc_unused;
+ break;
+ } else {
+ /* out-of-order vacancy */
+ printk("OOO vacancy lbcp 0x%p\n", lbcp);
+ ASSERT(0);
+ }
+ }
+ lbcp = lbcp->lbc_next;
+ }
+ ASSERT(lbcp != NULL);
+ /*
+ * If we find a free descriptor, claim it,
+ * initialize it, and return it.
+ */
+ XFS_LBC_CLAIM(lbcp, i);
+ if (lbcp->lbc_unused <= i) {
+ lbcp->lbc_unused = i + 1;
+ }
+ lbsp = XFS_LBC_SLOT(lbcp, i);
+ tp->t_busy_free--;
+ lbsp->lbc_ag = ag;
+ lbsp->lbc_idx = idx;
+ return (lbsp);
+}
+
+
+/*
+ * xfs_trans_free_busy
+ * Free all of the busy lists from a transaction
+ */
+void
+xfs_trans_free_busy(xfs_trans_t *tp)
+{
+ xfs_log_busy_chunk_t *lbcp;
+ xfs_log_busy_chunk_t *lbcq;
+
+ lbcp = tp->t_busy.lbc_next;
+ while (lbcp != NULL) {
+ lbcq = lbcp->lbc_next;
+ kmem_free(lbcp, sizeof(xfs_log_busy_chunk_t));
+ lbcp = lbcq;
+ }
+
+ XFS_LBC_INIT(&tp->t_busy);
+ tp->t_busy.lbc_unused = 0;
+}
diff --git a/fs/xfs/xfs_trans_priv.h b/fs/xfs/xfs_trans_priv.h
new file mode 100644
index 00000000000000..d4dae7d06afcdb
--- /dev/null
+++ b/fs/xfs/xfs_trans_priv.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2000, 2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_TRANS_PRIV_H__
+#define __XFS_TRANS_PRIV_H__
+
+struct xfs_log_item;
+struct xfs_log_item_desc;
+struct xfs_mount;
+struct xfs_trans;
+
+/*
+ * From xfs_trans_item.c
+ */
+struct xfs_log_item_desc *xfs_trans_add_item(struct xfs_trans *,
+ struct xfs_log_item *);
+void xfs_trans_free_item(struct xfs_trans *,
+ struct xfs_log_item_desc *);
+struct xfs_log_item_desc *xfs_trans_find_item(struct xfs_trans *,
+ struct xfs_log_item *);
+struct xfs_log_item_desc *xfs_trans_first_item(struct xfs_trans *);
+struct xfs_log_item_desc *xfs_trans_next_item(struct xfs_trans *,
+ struct xfs_log_item_desc *);
+void xfs_trans_free_items(struct xfs_trans *, int);
+void xfs_trans_unlock_items(struct xfs_trans *,
+ xfs_lsn_t);
+void xfs_trans_free_busy(xfs_trans_t *tp);
+xfs_log_busy_slot_t *xfs_trans_add_busy(xfs_trans_t *tp,
+ xfs_agnumber_t ag,
+ xfs_extlen_t idx);
+
+/*
+ * From xfs_trans_ail.c
+ */
+void xfs_trans_update_ail(struct xfs_mount *,
+ struct xfs_log_item *, xfs_lsn_t,
+ unsigned long);
+void xfs_trans_delete_ail(struct xfs_mount *,
+ struct xfs_log_item *, unsigned long);
+struct xfs_log_item *xfs_trans_first_ail(struct xfs_mount *, int *);
+struct xfs_log_item *xfs_trans_next_ail(struct xfs_mount *,
+ struct xfs_log_item *, int *, int *);
+
+
+#endif /* __XFS_TRANS_PRIV_H__ */
diff --git a/fs/xfs/xfs_trans_space.h b/fs/xfs/xfs_trans_space.h
new file mode 100644
index 00000000000000..e91d173f4ed325
--- /dev/null
+++ b/fs/xfs/xfs_trans_space.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_TRANS_SPACE_H__
+#define __XFS_TRANS_SPACE_H__
+
+/*
+ * Components of space reservations.
+ */
+#define XFS_MAX_CONTIG_EXTENTS_PER_BLOCK(mp) \
+ (((mp)->m_alloc_mxr[0]) - ((mp)->m_alloc_mnr[0]))
+#define XFS_EXTENTADD_SPACE_RES(mp,w) (XFS_BM_MAXLEVELS(mp,w) - 1)
+#define XFS_NEXTENTADD_SPACE_RES(mp,b,w)\
+ (((b + XFS_MAX_CONTIG_EXTENTS_PER_BLOCK(mp) - 1) / \
+ XFS_MAX_CONTIG_EXTENTS_PER_BLOCK(mp)) * \
+ XFS_EXTENTADD_SPACE_RES(mp,w))
+#define XFS_DAENTER_1B(mp,w) ((w) == XFS_DATA_FORK ? (mp)->m_dirblkfsbs : 1)
+#define XFS_DAENTER_DBS(mp,w) \
+ (XFS_DA_NODE_MAXDEPTH + \
+ ((XFS_DIR_IS_V2(mp) && (w) == XFS_DATA_FORK) ? 2 : 0))
+#define XFS_DAENTER_BLOCKS(mp,w) \
+ (XFS_DAENTER_1B(mp,w) * XFS_DAENTER_DBS(mp,w))
+#define XFS_DAENTER_BMAP1B(mp,w) \
+ XFS_NEXTENTADD_SPACE_RES(mp, XFS_DAENTER_1B(mp, w), w)
+#define XFS_DAENTER_BMAPS(mp,w) \
+ (XFS_DAENTER_DBS(mp,w) * XFS_DAENTER_BMAP1B(mp,w))
+#define XFS_DAENTER_SPACE_RES(mp,w) \
+ (XFS_DAENTER_BLOCKS(mp,w) + XFS_DAENTER_BMAPS(mp,w))
+#define XFS_DAREMOVE_SPACE_RES(mp,w) XFS_DAENTER_BMAPS(mp,w)
+#define XFS_DIRENTER_MAX_SPLIT(mp,nl) \
+ (((mp)->m_sb.sb_blocksize == 512 && \
+ XFS_DIR_IS_V1(mp) && \
+ (nl) >= XFS_DIR_LEAF_CAN_DOUBLE_SPLIT_LEN) ? 2 : 1)
+#define XFS_DIRENTER_SPACE_RES(mp,nl) \
+ (XFS_DAENTER_SPACE_RES(mp, XFS_DATA_FORK) * \
+ XFS_DIRENTER_MAX_SPLIT(mp,nl))
+#define XFS_DIRREMOVE_SPACE_RES(mp) \
+ XFS_DAREMOVE_SPACE_RES(mp, XFS_DATA_FORK)
+#define XFS_IALLOC_SPACE_RES(mp) \
+ (XFS_IALLOC_BLOCKS(mp) + XFS_IN_MAXLEVELS(mp)-1)
+
+/*
+ * Space reservation values for various transactions.
+ */
+#define XFS_ADDAFORK_SPACE_RES(mp) \
+ ((mp)->m_dirblkfsbs + \
+ (XFS_DIR_IS_V1(mp) ? 0 : XFS_DAENTER_BMAP1B(mp, XFS_DATA_FORK)))
+#define XFS_ATTRRM_SPACE_RES(mp) \
+ XFS_DAREMOVE_SPACE_RES(mp, XFS_ATTR_FORK)
+/* This macro is not used - see inline code in xfs_attr_set */
+#define XFS_ATTRSET_SPACE_RES(mp, v) \
+ (XFS_DAENTER_SPACE_RES(mp, XFS_ATTR_FORK) + XFS_B_TO_FSB(mp, v))
+#define XFS_CREATE_SPACE_RES(mp,nl) \
+ (XFS_IALLOC_SPACE_RES(mp) + XFS_DIRENTER_SPACE_RES(mp,nl))
+#define XFS_DIOSTRAT_SPACE_RES(mp, v) \
+ (XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK) + (v))
+#define XFS_GROWFS_SPACE_RES(mp) \
+ (2 * XFS_AG_MAXLEVELS(mp))
+#define XFS_GROWFSRT_SPACE_RES(mp,b) \
+ ((b) + XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK))
+#define XFS_LINK_SPACE_RES(mp,nl) \
+ XFS_DIRENTER_SPACE_RES(mp,nl)
+#define XFS_MKDIR_SPACE_RES(mp,nl) \
+ (XFS_IALLOC_SPACE_RES(mp) + XFS_DIRENTER_SPACE_RES(mp,nl))
+#define XFS_QM_DQALLOC_SPACE_RES(mp) \
+ (XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK) + \
+ XFS_DQUOT_CLUSTER_SIZE_FSB)
+#define XFS_QM_QINOCREATE_SPACE_RES(mp) \
+ XFS_IALLOC_SPACE_RES(mp)
+#define XFS_REMOVE_SPACE_RES(mp) \
+ XFS_DIRREMOVE_SPACE_RES(mp)
+#define XFS_RENAME_SPACE_RES(mp,nl) \
+ (XFS_DIRREMOVE_SPACE_RES(mp) + XFS_DIRENTER_SPACE_RES(mp,nl))
+#define XFS_SYMLINK_SPACE_RES(mp,nl,b) \
+ (XFS_IALLOC_SPACE_RES(mp) + XFS_DIRENTER_SPACE_RES(mp,nl) + (b))
+
+#endif /* __XFS_TRANS_SPACE_H__ */
diff --git a/fs/xfs/xfs_types.h b/fs/xfs/xfs_types.h
new file mode 100644
index 00000000000000..04609d27ea519d
--- /dev/null
+++ b/fs/xfs/xfs_types.h
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_TYPES_H__
+#define __XFS_TYPES_H__
+
+#ifdef __KERNEL__
+
+/*
+ * POSIX Extensions
+ */
+typedef unsigned char uchar_t;
+typedef unsigned short ushort_t;
+typedef unsigned int uint_t;
+typedef unsigned long ulong_t;
+
+/*
+ * Additional type declarations for XFS
+ */
+typedef signed char __int8_t;
+typedef unsigned char __uint8_t;
+typedef signed short int __int16_t;
+typedef unsigned short int __uint16_t;
+typedef signed int __int32_t;
+typedef unsigned int __uint32_t;
+typedef signed long long int __int64_t;
+typedef unsigned long long int __uint64_t;
+
+typedef enum { B_FALSE,B_TRUE } boolean_t;
+typedef __int64_t prid_t; /* project ID */
+typedef __uint32_t inst_t; /* an instruction */
+
+typedef __s64 xfs_off_t; /* <file offset> type */
+typedef __u64 xfs_ino_t; /* <inode> type */
+typedef __s64 xfs_daddr_t; /* <disk address> type */
+typedef char * xfs_caddr_t; /* <core address> type */
+typedef __u32 xfs_dev_t;
+
+/* __psint_t is the same size as a pointer */
+#if (BITS_PER_LONG == 32)
+typedef __int32_t __psint_t;
+typedef __uint32_t __psunsigned_t;
+#elif (BITS_PER_LONG == 64)
+typedef __int64_t __psint_t;
+typedef __uint64_t __psunsigned_t;
+#else
+#error BITS_PER_LONG must be 32 or 64
+#endif
+
+#endif /* __KERNEL__ */
+
+typedef __uint32_t xfs_agblock_t; /* blockno in alloc. group */
+typedef __uint32_t xfs_extlen_t; /* extent length in blocks */
+typedef __uint32_t xfs_agnumber_t; /* allocation group number */
+typedef __int32_t xfs_extnum_t; /* # of extents in a file */
+typedef __int16_t xfs_aextnum_t; /* # extents in an attribute fork */
+typedef __int64_t xfs_fsize_t; /* bytes in a file */
+typedef __uint64_t xfs_ufsize_t; /* unsigned bytes in a file */
+
+typedef __int32_t xfs_suminfo_t; /* type of bitmap summary info */
+typedef __int32_t xfs_rtword_t; /* word type for bitmap manipulations */
+
+typedef __int64_t xfs_lsn_t; /* log sequence number */
+typedef __int32_t xfs_tid_t; /* transaction identifier */
+
+typedef __uint32_t xfs_dablk_t; /* dir/attr block number (in file) */
+typedef __uint32_t xfs_dahash_t; /* dir/attr hash value */
+
+typedef __uint16_t xfs_prid_t; /* prid_t truncated to 16bits in XFS */
+
+/*
+ * These types are 64 bits on disk but are either 32 or 64 bits in memory.
+ * Disk based types:
+ */
+typedef __uint64_t xfs_dfsbno_t; /* blockno in filesystem (agno|agbno) */
+typedef __uint64_t xfs_drfsbno_t; /* blockno in filesystem (raw) */
+typedef __uint64_t xfs_drtbno_t; /* extent (block) in realtime area */
+typedef __uint64_t xfs_dfiloff_t; /* block number in a file */
+typedef __uint64_t xfs_dfilblks_t; /* number of blocks in a file */
+
+/*
+ * Memory based types are conditional.
+ */
+#if XFS_BIG_BLKNOS
+typedef __uint64_t xfs_fsblock_t; /* blockno in filesystem (agno|agbno) */
+typedef __uint64_t xfs_rfsblock_t; /* blockno in filesystem (raw) */
+typedef __uint64_t xfs_rtblock_t; /* extent (block) in realtime area */
+typedef __int64_t xfs_srtblock_t; /* signed version of xfs_rtblock_t */
+#else
+typedef __uint32_t xfs_fsblock_t; /* blockno in filesystem (agno|agbno) */
+typedef __uint32_t xfs_rfsblock_t; /* blockno in filesystem (raw) */
+typedef __uint32_t xfs_rtblock_t; /* extent (block) in realtime area */
+typedef __int32_t xfs_srtblock_t; /* signed version of xfs_rtblock_t */
+#endif
+typedef __uint64_t xfs_fileoff_t; /* block number in a file */
+typedef __int64_t xfs_sfiloff_t; /* signed block number in a file */
+typedef __uint64_t xfs_filblks_t; /* number of blocks in a file */
+
+typedef __uint8_t xfs_arch_t; /* architecture of an xfs fs */
+
+/*
+ * Null values for the types.
+ */
+#define NULLDFSBNO ((xfs_dfsbno_t)-1)
+#define NULLDRFSBNO ((xfs_drfsbno_t)-1)
+#define NULLDRTBNO ((xfs_drtbno_t)-1)
+#define NULLDFILOFF ((xfs_dfiloff_t)-1)
+
+#define NULLFSBLOCK ((xfs_fsblock_t)-1)
+#define NULLRFSBLOCK ((xfs_rfsblock_t)-1)
+#define NULLRTBLOCK ((xfs_rtblock_t)-1)
+#define NULLFILEOFF ((xfs_fileoff_t)-1)
+
+#define NULLAGBLOCK ((xfs_agblock_t)-1)
+#define NULLAGNUMBER ((xfs_agnumber_t)-1)
+#define NULLEXTNUM ((xfs_extnum_t)-1)
+
+#define NULLCOMMITLSN ((xfs_lsn_t)-1)
+
+/*
+ * Max values for extlen, extnum, aextnum.
+ */
+#define MAXEXTLEN ((xfs_extlen_t)0x001fffff) /* 21 bits */
+#define MAXEXTNUM ((xfs_extnum_t)0x7fffffff) /* signed int */
+#define MAXAEXTNUM ((xfs_aextnum_t)0x7fff) /* signed short */
+
+/*
+ * MAXNAMELEN is the length (including the terminating null) of
+ * the longest permissible file (component) name.
+ */
+#define MAXNAMELEN 256
+
+typedef struct xfs_dirent { /* data from readdir() */
+ xfs_ino_t d_ino; /* inode number of entry */
+ xfs_off_t d_off; /* offset of disk directory entry */
+ unsigned short d_reclen; /* length of this record */
+ char d_name[1]; /* name of file */
+} xfs_dirent_t;
+
+#define DIRENTBASESIZE (((xfs_dirent_t *)0)->d_name - (char *)0)
+#define DIRENTSIZE(namelen) \
+ ((DIRENTBASESIZE + (namelen) + \
+ sizeof(xfs_off_t)) & ~(sizeof(xfs_off_t) - 1))
+
+typedef enum {
+ XFS_LOOKUP_EQi, XFS_LOOKUP_LEi, XFS_LOOKUP_GEi
+} xfs_lookup_t;
+
+typedef enum {
+ XFS_BTNUM_BNOi, XFS_BTNUM_CNTi, XFS_BTNUM_BMAPi, XFS_BTNUM_INOi,
+ XFS_BTNUM_MAX
+} xfs_btnum_t;
+
+#endif /* __XFS_TYPES_H__ */
diff --git a/fs/xfs/xfs_utils.c b/fs/xfs/xfs_utils.c
new file mode 100644
index 00000000000000..816b945fa0ea88
--- /dev/null
+++ b/fs/xfs/xfs_utils.c
@@ -0,0 +1,488 @@
+/*
+ * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode_item.h"
+#include "xfs_inode.h"
+#include "xfs_bmap.h"
+#include "xfs_error.h"
+#include "xfs_quota.h"
+#include "xfs_rw.h"
+#include "xfs_itable.h"
+#include "xfs_utils.h"
+
+/*
+ * xfs_get_dir_entry is used to get a reference to an inode given
+ * its parent directory inode and the name of the file. It does
+ * not lock the child inode, and it unlocks the directory before
+ * returning. The directory's generation number is returned for
+ * use by a later call to xfs_lock_dir_and_entry.
+ */
+int
+xfs_get_dir_entry(
+ vname_t *dentry,
+ xfs_inode_t **ipp)
+{
+ vnode_t *vp;
+ bhv_desc_t *bdp;
+
+ vp = VNAME_TO_VNODE(dentry);
+ bdp = vn_bhv_lookup_unlocked(VN_BHV_HEAD(vp), &xfs_vnodeops);
+ if (!bdp) {
+ *ipp = NULL;
+ return XFS_ERROR(ENOENT);
+ }
+ VN_HOLD(vp);
+ *ipp = XFS_BHVTOI(bdp);
+ return 0;
+}
+
+int
+xfs_dir_lookup_int(
+ bhv_desc_t *dir_bdp,
+ uint lock_mode,
+ vname_t *dentry,
+ xfs_ino_t *inum,
+ xfs_inode_t **ipp)
+{
+ vnode_t *dir_vp;
+ xfs_inode_t *dp;
+ int error;
+
+ dir_vp = BHV_TO_VNODE(dir_bdp);
+ vn_trace_entry(dir_vp, __FUNCTION__, (inst_t *)__return_address);
+
+ dp = XFS_BHVTOI(dir_bdp);
+
+ error = XFS_DIR_LOOKUP(dp->i_mount, NULL, dp,
+ VNAME(dentry), VNAMELEN(dentry), inum);
+ if (!error) {
+ /*
+ * Unlock the directory. We do this because we can't
+ * hold the directory lock while doing the vn_get()
+ * in xfs_iget(). Doing so could cause us to hold
+ * a lock while waiting for the inode to finish
+ * being inactive while it's waiting for a log
+ * reservation in the inactive routine.
+ */
+ xfs_iunlock(dp, lock_mode);
+ error = xfs_iget(dp->i_mount, NULL, *inum, 0, 0, ipp, 0);
+ xfs_ilock(dp, lock_mode);
+
+ if (error) {
+ *ipp = NULL;
+ } else if ((*ipp)->i_d.di_mode == 0) {
+ /*
+ * The inode has been freed. Something is
+ * wrong so just get out of here.
+ */
+ xfs_iunlock(dp, lock_mode);
+ xfs_iput_new(*ipp, 0);
+ *ipp = NULL;
+ xfs_ilock(dp, lock_mode);
+ error = XFS_ERROR(ENOENT);
+ }
+ }
+ return error;
+}
+
+/*
+ * Allocates a new inode from disk and return a pointer to the
+ * incore copy. This routine will internally commit the current
+ * transaction and allocate a new one if the Space Manager needed
+ * to do an allocation to replenish the inode free-list.
+ *
+ * This routine is designed to be called from xfs_create and
+ * xfs_create_dir.
+ *
+ */
+int
+xfs_dir_ialloc(
+ xfs_trans_t **tpp, /* input: current transaction;
+ output: may be a new transaction. */
+ xfs_inode_t *dp, /* directory within whose allocate
+ the inode. */
+ mode_t mode,
+ nlink_t nlink,
+ xfs_dev_t rdev,
+ cred_t *credp,
+ prid_t prid, /* project id */
+ int okalloc, /* ok to allocate new space */
+ xfs_inode_t **ipp, /* pointer to inode; it will be
+ locked. */
+ int *committed)
+
+{
+ xfs_trans_t *tp;
+ xfs_trans_t *ntp;
+ xfs_inode_t *ip;
+ xfs_buf_t *ialloc_context = NULL;
+ boolean_t call_again = B_FALSE;
+ int code;
+ uint log_res;
+ uint log_count;
+ void *dqinfo;
+ uint tflags;
+
+ tp = *tpp;
+ ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES);
+
+ /*
+ * xfs_ialloc will return a pointer to an incore inode if
+ * the Space Manager has an available inode on the free
+ * list. Otherwise, it will do an allocation and replenish
+ * the freelist. Since we can only do one allocation per
+ * transaction without deadlocks, we will need to commit the
+ * current transaction and start a new one. We will then
+ * need to call xfs_ialloc again to get the inode.
+ *
+ * If xfs_ialloc did an allocation to replenish the freelist,
+ * it returns the bp containing the head of the freelist as
+ * ialloc_context. We will hold a lock on it across the
+ * transaction commit so that no other process can steal
+ * the inode(s) that we've just allocated.
+ */
+ code = xfs_ialloc(tp, dp, mode, nlink, rdev, credp, prid, okalloc,
+ &ialloc_context, &call_again, &ip);
+
+ /*
+ * Return an error if we were unable to allocate a new inode.
+ * This should only happen if we run out of space on disk or
+ * encounter a disk error.
+ */
+ if (code) {
+ *ipp = NULL;
+ return code;
+ }
+ if (!call_again && (ip == NULL)) {
+ *ipp = NULL;
+ return XFS_ERROR(ENOSPC);
+ }
+
+ /*
+ * If call_again is set, then we were unable to get an
+ * inode in one operation. We need to commit the current
+ * transaction and call xfs_ialloc() again. It is guaranteed
+ * to succeed the second time.
+ */
+ if (call_again) {
+
+ /*
+ * Normally, xfs_trans_commit releases all the locks.
+ * We call bhold to hang on to the ialloc_context across
+ * the commit. Holding this buffer prevents any other
+ * processes from doing any allocations in this
+ * allocation group.
+ */
+ xfs_trans_bhold(tp, ialloc_context);
+ /*
+ * Save the log reservation so we can use
+ * them in the next transaction.
+ */
+ log_res = xfs_trans_get_log_res(tp);
+ log_count = xfs_trans_get_log_count(tp);
+
+ /*
+ * We want the quota changes to be associated with the next
+ * transaction, NOT this one. So, detach the dqinfo from this
+ * and attach it to the next transaction.
+ */
+ dqinfo = NULL;
+ tflags = 0;
+ if (tp->t_dqinfo) {
+ dqinfo = (void *)tp->t_dqinfo;
+ tp->t_dqinfo = NULL;
+ tflags = tp->t_flags & XFS_TRANS_DQ_DIRTY;
+ tp->t_flags &= ~(XFS_TRANS_DQ_DIRTY);
+ }
+
+ ntp = xfs_trans_dup(tp);
+ code = xfs_trans_commit(tp, 0, NULL);
+ tp = ntp;
+ if (committed != NULL) {
+ *committed = 1;
+ }
+ /*
+ * If we get an error during the commit processing,
+ * release the buffer that is still held and return
+ * to the caller.
+ */
+ if (code) {
+ xfs_buf_relse(ialloc_context);
+ if (dqinfo) {
+ tp->t_dqinfo = dqinfo;
+ XFS_TRANS_FREE_DQINFO(tp->t_mountp, tp);
+ }
+ *tpp = ntp;
+ *ipp = NULL;
+ return code;
+ }
+ code = xfs_trans_reserve(tp, 0, log_res, 0,
+ XFS_TRANS_PERM_LOG_RES, log_count);
+ /*
+ * Re-attach the quota info that we detached from prev trx.
+ */
+ if (dqinfo) {
+ tp->t_dqinfo = dqinfo;
+ tp->t_flags |= tflags;
+ }
+
+ if (code) {
+ xfs_buf_relse(ialloc_context);
+ *tpp = ntp;
+ *ipp = NULL;
+ return code;
+ }
+ xfs_trans_bjoin(tp, ialloc_context);
+
+ /*
+ * Call ialloc again. Since we've locked out all
+ * other allocations in this allocation group,
+ * this call should always succeed.
+ */
+ code = xfs_ialloc(tp, dp, mode, nlink, rdev, credp, prid,
+ okalloc, &ialloc_context, &call_again, &ip);
+
+ /*
+ * If we get an error at this point, return to the caller
+ * so that the current transaction can be aborted.
+ */
+ if (code) {
+ *tpp = tp;
+ *ipp = NULL;
+ return code;
+ }
+ ASSERT ((!call_again) && (ip != NULL));
+
+ } else {
+ if (committed != NULL) {
+ *committed = 0;
+ }
+ }
+
+ *ipp = ip;
+ *tpp = tp;
+
+ return 0;
+}
+
+/*
+ * Decrement the link count on an inode & log the change.
+ * If this causes the link count to go to zero, initiate the
+ * logging activity required to truncate a file.
+ */
+int /* error */
+xfs_droplink(
+ xfs_trans_t *tp,
+ xfs_inode_t *ip)
+{
+ int error;
+
+ xfs_ichgtime(ip, XFS_ICHGTIME_CHG);
+
+ ASSERT (ip->i_d.di_nlink > 0);
+ ip->i_d.di_nlink--;
+ xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+
+ error = 0;
+ if (ip->i_d.di_nlink == 0) {
+ /*
+ * We're dropping the last link to this file.
+ * Move the on-disk inode to the AGI unlinked list.
+ * From xfs_inactive() we will pull the inode from
+ * the list and free it.
+ */
+ error = xfs_iunlink(tp, ip);
+ }
+ return error;
+}
+
+/*
+ * This gets called when the inode's version needs to be changed from 1 to 2.
+ * Currently this happens when the nlink field overflows the old 16-bit value
+ * or when chproj is called to change the project for the first time.
+ * As a side effect the superblock version will also get rev'd
+ * to contain the NLINK bit.
+ */
+void
+xfs_bump_ino_vers2(
+ xfs_trans_t *tp,
+ xfs_inode_t *ip)
+{
+ xfs_mount_t *mp;
+ unsigned long s;
+
+ ASSERT(ismrlocked (&ip->i_lock, MR_UPDATE));
+ ASSERT(ip->i_d.di_version == XFS_DINODE_VERSION_1);
+
+ ip->i_d.di_version = XFS_DINODE_VERSION_2;
+ ip->i_d.di_onlink = 0;
+ memset(&(ip->i_d.di_pad[0]), 0, sizeof(ip->i_d.di_pad));
+ mp = tp->t_mountp;
+ if (!XFS_SB_VERSION_HASNLINK(&mp->m_sb)) {
+ s = XFS_SB_LOCK(mp);
+ if (!XFS_SB_VERSION_HASNLINK(&mp->m_sb)) {
+ XFS_SB_VERSION_ADDNLINK(&mp->m_sb);
+ XFS_SB_UNLOCK(mp, s);
+ xfs_mod_sb(tp, XFS_SB_VERSIONNUM);
+ } else {
+ XFS_SB_UNLOCK(mp, s);
+ }
+ }
+ /* Caller must log the inode */
+}
+
+/*
+ * Increment the link count on an inode & log the change.
+ */
+int
+xfs_bumplink(
+ xfs_trans_t *tp,
+ xfs_inode_t *ip)
+{
+ if (ip->i_d.di_nlink >= XFS_MAXLINK)
+ return XFS_ERROR(EMLINK);
+ xfs_ichgtime(ip, XFS_ICHGTIME_CHG);
+
+ ASSERT(ip->i_d.di_nlink > 0);
+ ip->i_d.di_nlink++;
+ if ((ip->i_d.di_version == XFS_DINODE_VERSION_1) &&
+ (ip->i_d.di_nlink > XFS_MAXLINK_1)) {
+ /*
+ * The inode has increased its number of links beyond
+ * what can fit in an old format inode. It now needs
+ * to be converted to a version 2 inode with a 32 bit
+ * link count. If this is the first inode in the file
+ * system to do this, then we need to bump the superblock
+ * version number as well.
+ */
+ xfs_bump_ino_vers2(tp, ip);
+ }
+
+ xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+ return 0;
+}
+
+/*
+ * Try to truncate the given file to 0 length. Currently called
+ * only out of xfs_remove when it has to truncate a file to free
+ * up space for the remove to proceed.
+ */
+int
+xfs_truncate_file(
+ xfs_mount_t *mp,
+ xfs_inode_t *ip)
+{
+ xfs_trans_t *tp;
+ int error;
+
+#ifdef QUOTADEBUG
+ /*
+ * This is called to truncate the quotainodes too.
+ */
+ if (XFS_IS_UQUOTA_ON(mp)) {
+ if (ip->i_ino != mp->m_sb.sb_uquotino)
+ ASSERT(ip->i_udquot);
+ }
+ if (XFS_IS_GQUOTA_ON(mp)) {
+ if (ip->i_ino != mp->m_sb.sb_gquotino)
+ ASSERT(ip->i_gdquot);
+ }
+#endif
+ /*
+ * Make the call to xfs_itruncate_start before starting the
+ * transaction, because we cannot make the call while we're
+ * in a transaction.
+ */
+ xfs_ilock(ip, XFS_IOLOCK_EXCL);
+ xfs_itruncate_start(ip, XFS_ITRUNC_DEFINITE, (xfs_fsize_t)0);
+
+ tp = xfs_trans_alloc(mp, XFS_TRANS_TRUNCATE_FILE);
+ if ((error = xfs_trans_reserve(tp, 0, XFS_ITRUNCATE_LOG_RES(mp), 0,
+ XFS_TRANS_PERM_LOG_RES,
+ XFS_ITRUNCATE_LOG_COUNT))) {
+ xfs_trans_cancel(tp, 0);
+ xfs_iunlock(ip, XFS_IOLOCK_EXCL);
+ return error;
+ }
+
+ /*
+ * Follow the normal truncate locking protocol. Since we
+ * hold the inode in the transaction, we know that it's number
+ * of references will stay constant.
+ */
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
+ xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL);
+ xfs_trans_ihold(tp, ip);
+ /*
+ * Signal a sync xaction. The only case where that isn't
+ * the case is if we're truncating an already unlinked file
+ * on a wsync fs. In that case, we know the blocks can't
+ * reappear in the file because the links to file are
+ * permanently toast. Currently, we're always going to
+ * want a sync transaction because this code is being
+ * called from places where nlink is guaranteed to be 1
+ * but I'm leaving the tests in to protect against future
+ * changes -- rcc.
+ */
+ error = xfs_itruncate_finish(&tp, ip, (xfs_fsize_t)0,
+ XFS_DATA_FORK,
+ ((ip->i_d.di_nlink != 0 ||
+ !(mp->m_flags & XFS_MOUNT_WSYNC))
+ ? 1 : 0));
+ if (error) {
+ xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES |
+ XFS_TRANS_ABORT);
+ } else {
+ xfs_ichgtime(ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
+ error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES,
+ NULL);
+ }
+ xfs_iunlock(ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL);
+
+ return error;
+}
diff --git a/fs/xfs/xfs_utils.h b/fs/xfs/xfs_utils.h
new file mode 100644
index 00000000000000..e1ed6a5880007f
--- /dev/null
+++ b/fs/xfs/xfs_utils.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __XFS_UTILS_H__
+#define __XFS_UTILS_H__
+
+#define IRELE(ip) VN_RELE(XFS_ITOV(ip))
+#define IHOLD(ip) VN_HOLD(XFS_ITOV(ip))
+#define ITRACE(ip) vn_trace_ref(XFS_ITOV(ip), __FILE__, __LINE__, \
+ (inst_t *)__return_address)
+
+extern int xfs_rename (bhv_desc_t *, vname_t *, vnode_t *, vname_t *, cred_t *);
+extern int xfs_get_dir_entry (vname_t *, xfs_inode_t **);
+extern int xfs_dir_lookup_int (bhv_desc_t *, uint, vname_t *, xfs_ino_t *,
+ xfs_inode_t **);
+extern int xfs_truncate_file (xfs_mount_t *, xfs_inode_t *);
+extern int xfs_dir_ialloc (xfs_trans_t **, xfs_inode_t *, mode_t, nlink_t,
+ xfs_dev_t, cred_t *, prid_t, int,
+ xfs_inode_t **, int *);
+extern int xfs_droplink (xfs_trans_t *, xfs_inode_t *);
+extern int xfs_bumplink (xfs_trans_t *, xfs_inode_t *);
+extern void xfs_bump_ino_vers2 (xfs_trans_t *, xfs_inode_t *);
+
+#endif /* __XFS_UTILS_H__ */
diff --git a/fs/xfs/xfs_vfsops.c b/fs/xfs/xfs_vfsops.c
new file mode 100644
index 00000000000000..f176ce1b92740f
--- /dev/null
+++ b/fs/xfs/xfs_vfsops.c
@@ -0,0 +1,1928 @@
+/*
+ * XFS filesystem operations.
+ *
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_btree.h"
+#include "xfs_alloc.h"
+#include "xfs_ialloc.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode_item.h"
+#include "xfs_inode.h"
+#include "xfs_ag.h"
+#include "xfs_error.h"
+#include "xfs_bmap.h"
+#include "xfs_da_btree.h"
+#include "xfs_rw.h"
+#include "xfs_refcache.h"
+#include "xfs_buf_item.h"
+#include "xfs_extfree_item.h"
+#include "xfs_quota.h"
+#include "xfs_dir2_trace.h"
+#include "xfs_acl.h"
+#include "xfs_attr.h"
+#include "xfs_clnt.h"
+#include "xfs_log_priv.h"
+
+STATIC int xfs_sync(bhv_desc_t *, int, cred_t *);
+
+int
+xfs_init(void)
+{
+ extern kmem_zone_t *xfs_bmap_free_item_zone;
+ extern kmem_zone_t *xfs_btree_cur_zone;
+ extern kmem_zone_t *xfs_trans_zone;
+ extern kmem_zone_t *xfs_buf_item_zone;
+ extern kmem_zone_t *xfs_dabuf_zone;
+#ifdef XFS_DABUF_DEBUG
+ extern lock_t xfs_dabuf_global_lock;
+ spinlock_init(&xfs_dabuf_global_lock, "xfsda");
+#endif
+
+ /*
+ * Initialize all of the zone allocators we use.
+ */
+ xfs_bmap_free_item_zone = kmem_zone_init(sizeof(xfs_bmap_free_item_t),
+ "xfs_bmap_free_item");
+ xfs_btree_cur_zone = kmem_zone_init(sizeof(xfs_btree_cur_t),
+ "xfs_btree_cur");
+ xfs_inode_zone = kmem_zone_init(sizeof(xfs_inode_t), "xfs_inode");
+ xfs_trans_zone = kmem_zone_init(sizeof(xfs_trans_t), "xfs_trans");
+ xfs_da_state_zone =
+ kmem_zone_init(sizeof(xfs_da_state_t), "xfs_da_state");
+ xfs_dabuf_zone = kmem_zone_init(sizeof(xfs_dabuf_t), "xfs_dabuf");
+
+ /*
+ * The size of the zone allocated buf log item is the maximum
+ * size possible under XFS. This wastes a little bit of memory,
+ * but it is much faster.
+ */
+ xfs_buf_item_zone =
+ kmem_zone_init((sizeof(xfs_buf_log_item_t) +
+ (((XFS_MAX_BLOCKSIZE / XFS_BLI_CHUNK) /
+ NBWORD) * sizeof(int))),
+ "xfs_buf_item");
+ xfs_efd_zone = kmem_zone_init((sizeof(xfs_efd_log_item_t) +
+ ((XFS_EFD_MAX_FAST_EXTENTS - 1) * sizeof(xfs_extent_t))),
+ "xfs_efd_item");
+ xfs_efi_zone = kmem_zone_init((sizeof(xfs_efi_log_item_t) +
+ ((XFS_EFI_MAX_FAST_EXTENTS - 1) * sizeof(xfs_extent_t))),
+ "xfs_efi_item");
+ xfs_ifork_zone = kmem_zone_init(sizeof(xfs_ifork_t), "xfs_ifork");
+ xfs_ili_zone = kmem_zone_init(sizeof(xfs_inode_log_item_t), "xfs_ili");
+ xfs_chashlist_zone = kmem_zone_init(sizeof(xfs_chashlist_t),
+ "xfs_chashlist");
+ xfs_acl_zone_init(xfs_acl_zone, "xfs_acl");
+
+ /*
+ * Allocate global trace buffers.
+ */
+#ifdef XFS_ALLOC_TRACE
+ xfs_alloc_trace_buf = ktrace_alloc(XFS_ALLOC_TRACE_SIZE, KM_SLEEP);
+#endif
+#ifdef XFS_BMAP_TRACE
+ xfs_bmap_trace_buf = ktrace_alloc(XFS_BMAP_TRACE_SIZE, KM_SLEEP);
+#endif
+#ifdef XFS_BMBT_TRACE
+ xfs_bmbt_trace_buf = ktrace_alloc(XFS_BMBT_TRACE_SIZE, KM_SLEEP);
+#endif
+#ifdef XFS_DIR_TRACE
+ xfs_dir_trace_buf = ktrace_alloc(XFS_DIR_TRACE_SIZE, KM_SLEEP);
+#endif
+#ifdef XFS_ATTR_TRACE
+ xfs_attr_trace_buf = ktrace_alloc(XFS_ATTR_TRACE_SIZE, KM_SLEEP);
+#endif
+#ifdef XFS_DIR2_TRACE
+ xfs_dir2_trace_buf = ktrace_alloc(XFS_DIR2_GTRACE_SIZE, KM_SLEEP);
+#endif
+
+ xfs_dir_startup();
+
+#if (defined(DEBUG) || defined(INDUCE_IO_ERROR))
+ xfs_error_test_init();
+#endif /* DEBUG || INDUCE_IO_ERROR */
+
+ xfs_init_procfs();
+ xfs_sysctl_register();
+ return 0;
+}
+
+void
+xfs_cleanup(void)
+{
+ extern kmem_zone_t *xfs_bmap_free_item_zone;
+ extern kmem_zone_t *xfs_btree_cur_zone;
+ extern kmem_zone_t *xfs_inode_zone;
+ extern kmem_zone_t *xfs_trans_zone;
+ extern kmem_zone_t *xfs_da_state_zone;
+ extern kmem_zone_t *xfs_dabuf_zone;
+ extern kmem_zone_t *xfs_efd_zone;
+ extern kmem_zone_t *xfs_efi_zone;
+ extern kmem_zone_t *xfs_buf_item_zone;
+ extern kmem_zone_t *xfs_chashlist_zone;
+
+ xfs_cleanup_procfs();
+ xfs_sysctl_unregister();
+ xfs_refcache_destroy();
+ xfs_acl_zone_destroy(xfs_acl_zone);
+
+#ifdef XFS_DIR2_TRACE
+ ktrace_free(xfs_dir2_trace_buf);
+#endif
+#ifdef XFS_ATTR_TRACE
+ ktrace_free(xfs_attr_trace_buf);
+#endif
+#ifdef XFS_DIR_TRACE
+ ktrace_free(xfs_dir_trace_buf);
+#endif
+#ifdef XFS_BMBT_TRACE
+ ktrace_free(xfs_bmbt_trace_buf);
+#endif
+#ifdef XFS_BMAP_TRACE
+ ktrace_free(xfs_bmap_trace_buf);
+#endif
+#ifdef XFS_ALLOC_TRACE
+ ktrace_free(xfs_alloc_trace_buf);
+#endif
+
+ kmem_cache_destroy(xfs_bmap_free_item_zone);
+ kmem_cache_destroy(xfs_btree_cur_zone);
+ kmem_cache_destroy(xfs_inode_zone);
+ kmem_cache_destroy(xfs_trans_zone);
+ kmem_cache_destroy(xfs_da_state_zone);
+ kmem_cache_destroy(xfs_dabuf_zone);
+ kmem_cache_destroy(xfs_buf_item_zone);
+ kmem_cache_destroy(xfs_efd_zone);
+ kmem_cache_destroy(xfs_efi_zone);
+ kmem_cache_destroy(xfs_ifork_zone);
+ kmem_cache_destroy(xfs_ili_zone);
+ kmem_cache_destroy(xfs_chashlist_zone);
+}
+
+/*
+ * xfs_start_flags
+ *
+ * This function fills in xfs_mount_t fields based on mount args.
+ * Note: the superblock has _not_ yet been read in.
+ */
+STATIC int
+xfs_start_flags(
+ struct vfs *vfs,
+ struct xfs_mount_args *ap,
+ struct xfs_mount *mp)
+{
+ /* Values are in BBs */
+ if ((ap->flags & XFSMNT_NOALIGN) != XFSMNT_NOALIGN) {
+ /*
+ * At this point the superblock has not been read
+ * in, therefore we do not know the block size.
+ * Before the mount call ends we will convert
+ * these to FSBs.
+ */
+ mp->m_dalign = ap->sunit;
+ mp->m_swidth = ap->swidth;
+ }
+
+ if (ap->logbufs != -1 &&
+#if defined(DEBUG) || defined(XLOG_NOLOG)
+ ap->logbufs != 0 &&
+#endif
+ (ap->logbufs < XLOG_MIN_ICLOGS ||
+ ap->logbufs > XLOG_MAX_ICLOGS)) {
+ cmn_err(CE_WARN,
+ "XFS: invalid logbufs value: %d [not %d-%d]",
+ ap->logbufs, XLOG_MIN_ICLOGS, XLOG_MAX_ICLOGS);
+ return XFS_ERROR(EINVAL);
+ }
+ mp->m_logbufs = ap->logbufs;
+ if (ap->logbufsize != -1 &&
+ ap->logbufsize != 16 * 1024 &&
+ ap->logbufsize != 32 * 1024 &&
+ ap->logbufsize != 64 * 1024 &&
+ ap->logbufsize != 128 * 1024 &&
+ ap->logbufsize != 256 * 1024) {
+ cmn_err(CE_WARN,
+ "XFS: invalid logbufsize: %d [not 16k,32k,64k,128k or 256k]",
+ ap->logbufsize);
+ return XFS_ERROR(EINVAL);
+ }
+ mp->m_logbsize = ap->logbufsize;
+ mp->m_fsname_len = strlen(ap->fsname) + 1;
+ mp->m_fsname = kmem_alloc(mp->m_fsname_len, KM_SLEEP);
+ strcpy(mp->m_fsname, ap->fsname);
+
+ /*
+ * Pull in the 'wsync' and 'ino64' mount options before we do the real
+ * work of mounting and recovery. The arg pointer will
+ * be NULL when we are being called from the root mount code.
+ */
+ if (ap->flags & XFSMNT_WSYNC)
+ mp->m_flags |= XFS_MOUNT_WSYNC;
+#if XFS_BIG_INUMS
+ if (ap->flags & XFSMNT_INO64) {
+ mp->m_flags |= XFS_MOUNT_INO64;
+ mp->m_inoadd = XFS_INO64_OFFSET;
+ }
+#endif
+ if (ap->flags & XFSMNT_NOATIME)
+ mp->m_flags |= XFS_MOUNT_NOATIME;
+
+ if (ap->flags & XFSMNT_RETERR)
+ mp->m_flags |= XFS_MOUNT_RETERR;
+
+ if (ap->flags & XFSMNT_NOALIGN)
+ mp->m_flags |= XFS_MOUNT_NOALIGN;
+
+ if (ap->flags & XFSMNT_SWALLOC)
+ mp->m_flags |= XFS_MOUNT_SWALLOC;
+
+ if (ap->flags & XFSMNT_OSYNCISOSYNC)
+ mp->m_flags |= XFS_MOUNT_OSYNCISOSYNC;
+
+ if (ap->flags & XFSMNT_32BITINODES)
+ mp->m_flags |= (XFS_MOUNT_32BITINODES | XFS_MOUNT_32BITINOOPT);
+
+ if (ap->flags & XFSMNT_IOSIZE) {
+ if (ap->iosizelog > XFS_MAX_IO_LOG ||
+ ap->iosizelog < XFS_MIN_IO_LOG) {
+ cmn_err(CE_WARN,
+ "XFS: invalid log iosize: %d [not %d-%d]",
+ ap->iosizelog, XFS_MIN_IO_LOG,
+ XFS_MAX_IO_LOG);
+ return XFS_ERROR(EINVAL);
+ }
+
+ mp->m_flags |= XFS_MOUNT_DFLT_IOSIZE;
+ mp->m_readio_log = mp->m_writeio_log = ap->iosizelog;
+ }
+ if (ap->flags & XFSMNT_IDELETE)
+ mp->m_flags |= XFS_MOUNT_IDELETE;
+
+ /*
+ * no recovery flag requires a read-only mount
+ */
+ if (ap->flags & XFSMNT_NORECOVERY) {
+ if (!(vfs->vfs_flag & VFS_RDONLY)) {
+ cmn_err(CE_WARN,
+ "XFS: tried to mount a FS read-write without recovery!");
+ return XFS_ERROR(EINVAL);
+ }
+ mp->m_flags |= XFS_MOUNT_NORECOVERY;
+ }
+
+ if (ap->flags & XFSMNT_NOUUID)
+ mp->m_flags |= XFS_MOUNT_NOUUID;
+ if (ap->flags & XFSMNT_NOLOGFLUSH)
+ mp->m_flags |= XFS_MOUNT_NOLOGFLUSH;
+
+ return 0;
+}
+
+/*
+ * This function fills in xfs_mount_t fields based on mount args.
+ * Note: the superblock _has_ now been read in.
+ */
+STATIC int
+xfs_finish_flags(
+ struct vfs *vfs,
+ struct xfs_mount_args *ap,
+ struct xfs_mount *mp)
+{
+ int ronly = (vfs->vfs_flag & VFS_RDONLY);
+
+ /* Fail a mount where the logbuf is smaller then the log stripe */
+ if (XFS_SB_VERSION_HASLOGV2(&mp->m_sb)) {
+ if ((ap->logbufsize == -1) &&
+ (mp->m_sb.sb_logsunit > XLOG_BIG_RECORD_BSIZE)) {
+ mp->m_logbsize = mp->m_sb.sb_logsunit;
+ } else if (ap->logbufsize < mp->m_sb.sb_logsunit) {
+ cmn_err(CE_WARN,
+ "XFS: logbuf size must be greater than or equal to log stripe size");
+ return XFS_ERROR(EINVAL);
+ }
+ } else {
+ /* Fail a mount if the logbuf is larger than 32K */
+ if (ap->logbufsize > XLOG_BIG_RECORD_BSIZE) {
+ cmn_err(CE_WARN,
+ "XFS: logbuf size for version 1 logs must be 16K or 32K");
+ return XFS_ERROR(EINVAL);
+ }
+ }
+
+ /*
+ * prohibit r/w mounts of read-only filesystems
+ */
+ if ((mp->m_sb.sb_flags & XFS_SBF_READONLY) && !ronly) {
+ cmn_err(CE_WARN,
+ "XFS: cannot mount a read-only filesystem as read-write");
+ return XFS_ERROR(EROFS);
+ }
+
+ /*
+ * disallow mount attempts with (IRIX) project quota enabled
+ */
+ if (XFS_SB_VERSION_HASQUOTA(&mp->m_sb) &&
+ (mp->m_sb.sb_qflags & XFS_PQUOTA_ACCT)) {
+ cmn_err(CE_WARN,
+ "XFS: cannot mount a filesystem with IRIX project quota enabled");
+ return XFS_ERROR(ENOSYS);
+ }
+
+ /*
+ * check for shared mount.
+ */
+ if (ap->flags & XFSMNT_SHARED) {
+ if (!XFS_SB_VERSION_HASSHARED(&mp->m_sb))
+ return XFS_ERROR(EINVAL);
+
+ /*
+ * For IRIX 6.5, shared mounts must have the shared
+ * version bit set, have the persistent readonly
+ * field set, must be version 0 and can only be mounted
+ * read-only.
+ */
+ if (!ronly || !(mp->m_sb.sb_flags & XFS_SBF_READONLY) ||
+ (mp->m_sb.sb_shared_vn != 0))
+ return XFS_ERROR(EINVAL);
+
+ mp->m_flags |= XFS_MOUNT_SHARED;
+
+ /*
+ * Shared XFS V0 can't deal with DMI. Return EINVAL.
+ */
+ if (mp->m_sb.sb_shared_vn == 0 && (ap->flags & XFSMNT_DMAPI))
+ return XFS_ERROR(EINVAL);
+ }
+
+ return 0;
+}
+
+/*
+ * xfs_mount
+ *
+ * The file system configurations are:
+ * (1) device (partition) with data and internal log
+ * (2) logical volume with data and log subvolumes.
+ * (3) logical volume with data, log, and realtime subvolumes.
+ *
+ * We only have to handle opening the log and realtime volumes here if
+ * they are present. The data subvolume has already been opened by
+ * get_sb_bdev() and is stored in vfsp->vfs_super->s_bdev.
+ */
+STATIC int
+xfs_mount(
+ struct bhv_desc *bhvp,
+ struct xfs_mount_args *args,
+ cred_t *credp)
+{
+ struct vfs *vfsp = bhvtovfs(bhvp);
+ struct bhv_desc *p;
+ struct xfs_mount *mp = XFS_BHVTOM(bhvp);
+ struct block_device *ddev, *logdev, *rtdev;
+ int flags = 0, error;
+
+ ddev = vfsp->vfs_super->s_bdev;
+ logdev = rtdev = NULL;
+
+ /*
+ * Setup xfs_mount function vectors from available behaviors
+ */
+ p = vfs_bhv_lookup(vfsp, VFS_POSITION_DM);
+ mp->m_dm_ops = p ? *(xfs_dmops_t *) vfs_bhv_custom(p) : xfs_dmcore_stub;
+ p = vfs_bhv_lookup(vfsp, VFS_POSITION_QM);
+ mp->m_qm_ops = p ? *(xfs_qmops_t *) vfs_bhv_custom(p) : xfs_qmcore_stub;
+ p = vfs_bhv_lookup(vfsp, VFS_POSITION_IO);
+ mp->m_io_ops = p ? *(xfs_ioops_t *) vfs_bhv_custom(p) : xfs_iocore_xfs;
+
+ /*
+ * Open real time and log devices - order is important.
+ */
+ if (args->logname[0]) {
+ error = xfs_blkdev_get(mp, args->logname, &logdev);
+ if (error)
+ return error;
+ }
+ if (args->rtname[0]) {
+ error = xfs_blkdev_get(mp, args->rtname, &rtdev);
+ if (error) {
+ xfs_blkdev_put(logdev);
+ return error;
+ }
+
+ if (rtdev == ddev || rtdev == logdev) {
+ cmn_err(CE_WARN,
+ "XFS: Cannot mount filesystem with identical rtdev and ddev/logdev.");
+ xfs_blkdev_put(logdev);
+ xfs_blkdev_put(rtdev);
+ return EINVAL;
+ }
+ }
+
+ /*
+ * Setup xfs_mount buffer target pointers
+ */
+ error = ENOMEM;
+ mp->m_ddev_targp = xfs_alloc_buftarg(ddev);
+ if (!mp->m_ddev_targp) {
+ xfs_blkdev_put(logdev);
+ xfs_blkdev_put(rtdev);
+ return error;
+ }
+ if (rtdev) {
+ mp->m_rtdev_targp = xfs_alloc_buftarg(rtdev);
+ if (!mp->m_rtdev_targp)
+ goto error0;
+ }
+ mp->m_logdev_targp = (logdev && logdev != ddev) ?
+ xfs_alloc_buftarg(logdev) : mp->m_ddev_targp;
+ if (!mp->m_logdev_targp)
+ goto error0;
+
+ /*
+ * Setup flags based on mount(2) options and then the superblock
+ */
+ error = xfs_start_flags(vfsp, args, mp);
+ if (error)
+ goto error1;
+ error = xfs_readsb(mp);
+ if (error)
+ goto error1;
+ error = xfs_finish_flags(vfsp, args, mp);
+ if (error)
+ goto error2;
+
+ /*
+ * Setup xfs_mount buffer target pointers based on superblock
+ */
+ error = xfs_setsize_buftarg(mp->m_ddev_targp, mp->m_sb.sb_blocksize,
+ mp->m_sb.sb_sectsize);
+ if (!error && logdev && logdev != ddev) {
+ unsigned int log_sector_size = BBSIZE;
+
+ if (XFS_SB_VERSION_HASSECTOR(&mp->m_sb))
+ log_sector_size = mp->m_sb.sb_logsectsize;
+ error = xfs_setsize_buftarg(mp->m_logdev_targp,
+ mp->m_sb.sb_blocksize,
+ log_sector_size);
+ }
+ if (!error && rtdev)
+ error = xfs_setsize_buftarg(mp->m_rtdev_targp,
+ mp->m_sb.sb_blocksize,
+ mp->m_sb.sb_sectsize);
+ if (error)
+ goto error2;
+
+ error = XFS_IOINIT(vfsp, args, flags);
+ if (!error)
+ return 0;
+error2:
+ if (mp->m_sb_bp)
+ xfs_freesb(mp);
+error1:
+ xfs_binval(mp->m_ddev_targp);
+ if (logdev && logdev != ddev)
+ xfs_binval(mp->m_logdev_targp);
+ if (rtdev)
+ xfs_binval(mp->m_rtdev_targp);
+error0:
+ xfs_unmountfs_close(mp, credp);
+ return error;
+}
+
+STATIC int
+xfs_unmount(
+ bhv_desc_t *bdp,
+ int flags,
+ cred_t *credp)
+{
+ struct vfs *vfsp = bhvtovfs(bdp);
+ xfs_mount_t *mp = XFS_BHVTOM(bdp);
+ xfs_inode_t *rip;
+ vnode_t *rvp;
+ int unmount_event_wanted = 0;
+ int unmount_event_flags = 0;
+ int xfs_unmountfs_needed = 0;
+ int error;
+
+ rip = mp->m_rootip;
+ rvp = XFS_ITOV(rip);
+
+ if (vfsp->vfs_flag & VFS_DMI) {
+ error = XFS_SEND_PREUNMOUNT(mp, vfsp,
+ rvp, DM_RIGHT_NULL, rvp, DM_RIGHT_NULL,
+ NULL, NULL, 0, 0,
+ (mp->m_dmevmask & (1<<DM_EVENT_PREUNMOUNT))?
+ 0:DM_FLAGS_UNWANTED);
+ if (error)
+ return XFS_ERROR(error);
+ unmount_event_wanted = 1;
+ unmount_event_flags = (mp->m_dmevmask & (1<<DM_EVENT_UNMOUNT))?
+ 0 : DM_FLAGS_UNWANTED;
+ }
+
+ /*
+ * First blow any referenced inode from this file system
+ * out of the reference cache, and delete the timer.
+ */
+ xfs_refcache_purge_mp(mp);
+
+ XFS_bflush(mp->m_ddev_targp);
+ error = xfs_unmount_flush(mp, 0);
+ if (error)
+ goto out;
+
+ ASSERT(vn_count(rvp) == 1);
+
+ /*
+ * Drop the reference count
+ */
+ VN_RELE(rvp);
+
+ /*
+ * If we're forcing a shutdown, typically because of a media error,
+ * we want to make sure we invalidate dirty pages that belong to
+ * referenced vnodes as well.
+ */
+ if (XFS_FORCED_SHUTDOWN(mp)) {
+ error = xfs_sync(&mp->m_bhv,
+ (SYNC_WAIT | SYNC_CLOSE), credp);
+ ASSERT(error != EFSCORRUPTED);
+ }
+ xfs_unmountfs_needed = 1;
+
+out:
+ /* Send DMAPI event, if required.
+ * Then do xfs_unmountfs() if needed.
+ * Then return error (or zero).
+ */
+ if (unmount_event_wanted) {
+ /* Note: mp structure must still exist for
+ * XFS_SEND_UNMOUNT() call.
+ */
+ XFS_SEND_UNMOUNT(mp, vfsp, error == 0 ? rvp : NULL,
+ DM_RIGHT_NULL, 0, error, unmount_event_flags);
+ }
+ if (xfs_unmountfs_needed) {
+ /*
+ * Call common unmount function to flush to disk
+ * and free the super block buffer & mount structures.
+ */
+ xfs_unmountfs(mp, credp);
+ }
+
+ return XFS_ERROR(error);
+}
+
+#define REMOUNT_READONLY_FLAGS (SYNC_REMOUNT|SYNC_ATTR|SYNC_WAIT)
+
+STATIC int
+xfs_mntupdate(
+ bhv_desc_t *bdp,
+ int *flags,
+ struct xfs_mount_args *args)
+{
+ struct vfs *vfsp = bhvtovfs(bdp);
+ xfs_mount_t *mp = XFS_BHVTOM(bdp);
+ int pincount, error;
+ int count = 0;
+
+ if (args->flags & XFSMNT_NOATIME)
+ mp->m_flags |= XFS_MOUNT_NOATIME;
+ else
+ mp->m_flags &= ~XFS_MOUNT_NOATIME;
+
+ if (!(vfsp->vfs_flag & VFS_RDONLY)) {
+ VFS_SYNC(vfsp, SYNC_FSDATA|SYNC_BDFLUSH|SYNC_ATTR, NULL, error);
+ }
+
+ if (*flags & MS_RDONLY) {
+ xfs_refcache_purge_mp(mp);
+ xfs_flush_buftarg(mp->m_ddev_targp, 0);
+ xfs_finish_reclaim_all(mp, 0);
+
+ /* This loop must run at least twice.
+ * The first instance of the loop will flush
+ * most meta data but that will generate more
+ * meta data (typically directory updates).
+ * Which then must be flushed and logged before
+ * we can write the unmount record.
+ */
+ do {
+ VFS_SYNC(vfsp, REMOUNT_READONLY_FLAGS, NULL, error);
+ pincount = xfs_flush_buftarg(mp->m_ddev_targp, 1);
+ if (!pincount) {
+ delay(50);
+ count++;
+ }
+ } while (count < 2);
+
+ /* Ok now write out an unmount record */
+ xfs_log_unmount_write(mp);
+ xfs_unmountfs_writesb(mp);
+ vfsp->vfs_flag |= VFS_RDONLY;
+ } else {
+ vfsp->vfs_flag &= ~VFS_RDONLY;
+ }
+
+ return 0;
+}
+
+/*
+ * xfs_unmount_flush implements a set of flush operation on special
+ * inodes, which are needed as a separate set of operations so that
+ * they can be called as part of relocation process.
+ */
+int
+xfs_unmount_flush(
+ xfs_mount_t *mp, /* Mount structure we are getting
+ rid of. */
+ int relocation) /* Called from vfs relocation. */
+{
+ xfs_inode_t *rip = mp->m_rootip;
+ xfs_inode_t *rbmip;
+ xfs_inode_t *rsumip = NULL;
+ vnode_t *rvp = XFS_ITOV(rip);
+ int error;
+
+ xfs_ilock(rip, XFS_ILOCK_EXCL);
+ xfs_iflock(rip);
+
+ /*
+ * Flush out the real time inodes.
+ */
+ if ((rbmip = mp->m_rbmip) != NULL) {
+ xfs_ilock(rbmip, XFS_ILOCK_EXCL);
+ xfs_iflock(rbmip);
+ error = xfs_iflush(rbmip, XFS_IFLUSH_SYNC);
+ xfs_iunlock(rbmip, XFS_ILOCK_EXCL);
+
+ if (error == EFSCORRUPTED)
+ goto fscorrupt_out;
+
+ ASSERT(vn_count(XFS_ITOV(rbmip)) == 1);
+
+ rsumip = mp->m_rsumip;
+ xfs_ilock(rsumip, XFS_ILOCK_EXCL);
+ xfs_iflock(rsumip);
+ error = xfs_iflush(rsumip, XFS_IFLUSH_SYNC);
+ xfs_iunlock(rsumip, XFS_ILOCK_EXCL);
+
+ if (error == EFSCORRUPTED)
+ goto fscorrupt_out;
+
+ ASSERT(vn_count(XFS_ITOV(rsumip)) == 1);
+ }
+
+ /*
+ * Synchronously flush root inode to disk
+ */
+ error = xfs_iflush(rip, XFS_IFLUSH_SYNC);
+ if (error == EFSCORRUPTED)
+ goto fscorrupt_out2;
+
+ if (vn_count(rvp) != 1 && !relocation) {
+ xfs_iunlock(rip, XFS_ILOCK_EXCL);
+ return XFS_ERROR(EBUSY);
+ }
+
+ /*
+ * Release dquot that rootinode, rbmino and rsumino might be holding,
+ * flush and purge the quota inodes.
+ */
+ error = XFS_QM_UNMOUNT(mp);
+ if (error == EFSCORRUPTED)
+ goto fscorrupt_out2;
+
+ if (rbmip) {
+ VN_RELE(XFS_ITOV(rbmip));
+ VN_RELE(XFS_ITOV(rsumip));
+ }
+
+ xfs_iunlock(rip, XFS_ILOCK_EXCL);
+ return 0;
+
+fscorrupt_out:
+ xfs_ifunlock(rip);
+
+fscorrupt_out2:
+ xfs_iunlock(rip, XFS_ILOCK_EXCL);
+
+ return XFS_ERROR(EFSCORRUPTED);
+}
+
+/*
+ * xfs_root extracts the root vnode from a vfs.
+ *
+ * vfsp -- the vfs struct for the desired file system
+ * vpp -- address of the caller's vnode pointer which should be
+ * set to the desired fs root vnode
+ */
+STATIC int
+xfs_root(
+ bhv_desc_t *bdp,
+ vnode_t **vpp)
+{
+ vnode_t *vp;
+
+ vp = XFS_ITOV((XFS_BHVTOM(bdp))->m_rootip);
+ VN_HOLD(vp);
+ *vpp = vp;
+ return 0;
+}
+
+/*
+ * xfs_statvfs
+ *
+ * Fill in the statvfs structure for the given file system. We use
+ * the superblock lock in the mount structure to ensure a consistent
+ * snapshot of the counters returned.
+ */
+STATIC int
+xfs_statvfs(
+ bhv_desc_t *bdp,
+ xfs_statfs_t *statp,
+ vnode_t *vp)
+{
+ __uint64_t fakeinos;
+ xfs_extlen_t lsize;
+ xfs_mount_t *mp;
+ xfs_sb_t *sbp;
+ unsigned long s;
+
+ mp = XFS_BHVTOM(bdp);
+ sbp = &(mp->m_sb);
+
+ statp->f_type = XFS_SB_MAGIC;
+
+ s = XFS_SB_LOCK(mp);
+ statp->f_bsize = sbp->sb_blocksize;
+ lsize = sbp->sb_logstart ? sbp->sb_logblocks : 0;
+ statp->f_blocks = sbp->sb_dblocks - lsize;
+ statp->f_bfree = statp->f_bavail = sbp->sb_fdblocks;
+ fakeinos = statp->f_bfree << sbp->sb_inopblog;
+#if XFS_BIG_INUMS
+ fakeinos += mp->m_inoadd;
+#endif
+ statp->f_files =
+ MIN(sbp->sb_icount + fakeinos, (__uint64_t)XFS_MAXINUMBER);
+ if (mp->m_maxicount)
+#if XFS_BIG_INUMS
+ if (!mp->m_inoadd)
+#endif
+ statp->f_files = min_t(typeof(statp->f_files),
+ statp->f_files,
+ mp->m_maxicount);
+ statp->f_ffree = statp->f_files - (sbp->sb_icount - sbp->sb_ifree);
+ XFS_SB_UNLOCK(mp, s);
+
+ statp->f_fsid.val[0] = mp->m_dev;
+ statp->f_fsid.val[1] = 0;
+ statp->f_namelen = MAXNAMELEN - 1;
+
+ return 0;
+}
+
+
+/*
+ * xfs_sync flushes any pending I/O to file system vfsp.
+ *
+ * This routine is called by vfs_sync() to make sure that things make it
+ * out to disk eventually, on sync() system calls to flush out everything,
+ * and when the file system is unmounted. For the vfs_sync() case, all
+ * we really need to do is sync out the log to make all of our meta-data
+ * updates permanent (except for timestamps). For calls from pflushd(),
+ * dirty pages are kept moving by calling pdflush() on the inodes
+ * containing them. We also flush the inodes that we can lock without
+ * sleeping and the superblock if we can lock it without sleeping from
+ * vfs_sync() so that items at the tail of the log are always moving out.
+ *
+ * Flags:
+ * SYNC_BDFLUSH - We're being called from vfs_sync() so we don't want
+ * to sleep if we can help it. All we really need
+ * to do is ensure that the log is synced at least
+ * periodically. We also push the inodes and
+ * superblock if we can lock them without sleeping
+ * and they are not pinned.
+ * SYNC_ATTR - We need to flush the inodes. If SYNC_BDFLUSH is not
+ * set, then we really want to lock each inode and flush
+ * it.
+ * SYNC_WAIT - All the flushes that take place in this call should
+ * be synchronous.
+ * SYNC_DELWRI - This tells us to push dirty pages associated with
+ * inodes. SYNC_WAIT and SYNC_BDFLUSH are used to
+ * determine if they should be flushed sync, async, or
+ * delwri.
+ * SYNC_CLOSE - This flag is passed when the system is being
+ * unmounted. We should sync and invalidate everthing.
+ * SYNC_FSDATA - This indicates that the caller would like to make
+ * sure the superblock is safe on disk. We can ensure
+ * this by simply makeing sure the log gets flushed
+ * if SYNC_BDFLUSH is set, and by actually writing it
+ * out otherwise.
+ *
+ */
+/*ARGSUSED*/
+STATIC int
+xfs_sync(
+ bhv_desc_t *bdp,
+ int flags,
+ cred_t *credp)
+{
+ xfs_mount_t *mp;
+
+ mp = XFS_BHVTOM(bdp);
+ return (xfs_syncsub(mp, flags, 0, NULL));
+}
+
+/*
+ * xfs sync routine for internal use
+ *
+ * This routine supports all of the flags defined for the generic VFS_SYNC
+ * interface as explained above under xfs_sync. In the interests of not
+ * changing interfaces within the 6.5 family, additional internallly-
+ * required functions are specified within a separate xflags parameter,
+ * only available by calling this routine.
+ *
+ */
+STATIC int
+xfs_sync_inodes(
+ xfs_mount_t *mp,
+ int flags,
+ int xflags,
+ int *bypassed)
+{
+ xfs_inode_t *ip = NULL;
+ xfs_inode_t *ip_next;
+ xfs_buf_t *bp;
+ vnode_t *vp = NULL;
+ vmap_t vmap;
+ int error;
+ int last_error;
+ uint64_t fflag;
+ uint lock_flags;
+ uint base_lock_flags;
+ boolean_t mount_locked;
+ boolean_t vnode_refed;
+ int preempt;
+ xfs_dinode_t *dip;
+ xfs_iptr_t *ipointer;
+#ifdef DEBUG
+ boolean_t ipointer_in = B_FALSE;
+
+#define IPOINTER_SET ipointer_in = B_TRUE
+#define IPOINTER_CLR ipointer_in = B_FALSE
+#else
+#define IPOINTER_SET
+#define IPOINTER_CLR
+#endif
+
+
+/* Insert a marker record into the inode list after inode ip. The list
+ * must be locked when this is called. After the call the list will no
+ * longer be locked.
+ */
+#define IPOINTER_INSERT(ip, mp) { \
+ ASSERT(ipointer_in == B_FALSE); \
+ ipointer->ip_mnext = ip->i_mnext; \
+ ipointer->ip_mprev = ip; \
+ ip->i_mnext = (xfs_inode_t *)ipointer; \
+ ipointer->ip_mnext->i_mprev = (xfs_inode_t *)ipointer; \
+ preempt = 0; \
+ XFS_MOUNT_IUNLOCK(mp); \
+ mount_locked = B_FALSE; \
+ IPOINTER_SET; \
+ }
+
+/* Remove the marker from the inode list. If the marker was the only item
+ * in the list then there are no remaining inodes and we should zero out
+ * the whole list. If we are the current head of the list then move the head
+ * past us.
+ */
+#define IPOINTER_REMOVE(ip, mp) { \
+ ASSERT(ipointer_in == B_TRUE); \
+ if (ipointer->ip_mnext != (xfs_inode_t *)ipointer) { \
+ ip = ipointer->ip_mnext; \
+ ip->i_mprev = ipointer->ip_mprev; \
+ ipointer->ip_mprev->i_mnext = ip; \
+ if (mp->m_inodes == (xfs_inode_t *)ipointer) { \
+ mp->m_inodes = ip; \
+ } \
+ } else { \
+ ASSERT(mp->m_inodes == (xfs_inode_t *)ipointer); \
+ mp->m_inodes = NULL; \
+ ip = NULL; \
+ } \
+ IPOINTER_CLR; \
+ }
+
+#define XFS_PREEMPT_MASK 0x7f
+
+ if (bypassed)
+ *bypassed = 0;
+ if (XFS_MTOVFS(mp)->vfs_flag & VFS_RDONLY)
+ return 0;
+ error = 0;
+ last_error = 0;
+ preempt = 0;
+
+ /* Allocate a reference marker */
+ ipointer = (xfs_iptr_t *)kmem_zalloc(sizeof(xfs_iptr_t), KM_SLEEP);
+
+ fflag = XFS_B_ASYNC; /* default is don't wait */
+ if (flags & SYNC_BDFLUSH)
+ fflag = XFS_B_DELWRI;
+ if (flags & SYNC_WAIT)
+ fflag = 0; /* synchronous overrides all */
+
+ base_lock_flags = XFS_ILOCK_SHARED;
+ if (flags & (SYNC_DELWRI | SYNC_CLOSE)) {
+ /*
+ * We need the I/O lock if we're going to call any of
+ * the flush/inval routines.
+ */
+ base_lock_flags |= XFS_IOLOCK_SHARED;
+ }
+
+ XFS_MOUNT_ILOCK(mp);
+
+ ip = mp->m_inodes;
+
+ mount_locked = B_TRUE;
+ vnode_refed = B_FALSE;
+
+ IPOINTER_CLR;
+
+ do {
+ ASSERT(ipointer_in == B_FALSE);
+ ASSERT(vnode_refed == B_FALSE);
+
+ lock_flags = base_lock_flags;
+
+ /*
+ * There were no inodes in the list, just break out
+ * of the loop.
+ */
+ if (ip == NULL) {
+ break;
+ }
+
+ /*
+ * We found another sync thread marker - skip it
+ */
+ if (ip->i_mount == NULL) {
+ ip = ip->i_mnext;
+ continue;
+ }
+
+ vp = XFS_ITOV_NULL(ip);
+
+ /*
+ * If the vnode is gone then this is being torn down,
+ * call reclaim if it is flushed, else let regular flush
+ * code deal with it later in the loop.
+ */
+
+ if (vp == NULL) {
+ /* Skip ones already in reclaim */
+ if (ip->i_flags & XFS_IRECLAIM) {
+ ip = ip->i_mnext;
+ continue;
+ }
+ if (xfs_ilock_nowait(ip, XFS_ILOCK_EXCL) == 0) {
+ ip = ip->i_mnext;
+ } else if ((xfs_ipincount(ip) == 0) &&
+ xfs_iflock_nowait(ip)) {
+ IPOINTER_INSERT(ip, mp);
+
+ xfs_finish_reclaim(ip, 1,
+ XFS_IFLUSH_DELWRI_ELSE_ASYNC);
+
+ XFS_MOUNT_ILOCK(mp);
+ mount_locked = B_TRUE;
+ IPOINTER_REMOVE(ip, mp);
+ } else {
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ ip = ip->i_mnext;
+ }
+ continue;
+ }
+
+ if (VN_BAD(vp)) {
+ ip = ip->i_mnext;
+ continue;
+ }
+
+ if (XFS_FORCED_SHUTDOWN(mp) && !(flags & SYNC_CLOSE)) {
+ XFS_MOUNT_IUNLOCK(mp);
+ kmem_free(ipointer, sizeof(xfs_iptr_t));
+ return 0;
+ }
+
+ /*
+ * If this is just vfs_sync() or pflushd() calling
+ * then we can skip inodes for which it looks like
+ * there is nothing to do. Since we don't have the
+ * inode locked this is racey, but these are periodic
+ * calls so it doesn't matter. For the others we want
+ * to know for sure, so we at least try to lock them.
+ */
+ if (flags & SYNC_BDFLUSH) {
+ if (((ip->i_itemp == NULL) ||
+ !(ip->i_itemp->ili_format.ilf_fields &
+ XFS_ILOG_ALL)) &&
+ (ip->i_update_core == 0)) {
+ ip = ip->i_mnext;
+ continue;
+ }
+ }
+
+ /*
+ * Try to lock without sleeping. We're out of order with
+ * the inode list lock here, so if we fail we need to drop
+ * the mount lock and try again. If we're called from
+ * bdflush() here, then don't bother.
+ *
+ * The inode lock here actually coordinates with the
+ * almost spurious inode lock in xfs_ireclaim() to prevent
+ * the vnode we handle here without a reference from
+ * being freed while we reference it. If we lock the inode
+ * while it's on the mount list here, then the spurious inode
+ * lock in xfs_ireclaim() after the inode is pulled from
+ * the mount list will sleep until we release it here.
+ * This keeps the vnode from being freed while we reference
+ * it. It is also cheaper and simpler than actually doing
+ * a vn_get() for every inode we touch here.
+ */
+ if (xfs_ilock_nowait(ip, lock_flags) == 0) {
+
+ if ((flags & SYNC_BDFLUSH) || (vp == NULL)) {
+ ip = ip->i_mnext;
+ continue;
+ }
+
+ /*
+ * We need to unlock the inode list lock in order
+ * to lock the inode. Insert a marker record into
+ * the inode list to remember our position, dropping
+ * the lock is now done inside the IPOINTER_INSERT
+ * macro.
+ *
+ * We also use the inode list lock to protect us
+ * in taking a snapshot of the vnode version number
+ * for use in calling vn_get().
+ */
+ VMAP(vp, vmap);
+ IPOINTER_INSERT(ip, mp);
+
+ vp = vn_get(vp, &vmap);
+ if (vp == NULL) {
+ /*
+ * The vnode was reclaimed once we let go
+ * of the inode list lock. Skip to the
+ * next list entry. Remove the marker.
+ */
+
+ XFS_MOUNT_ILOCK(mp);
+
+ mount_locked = B_TRUE;
+ vnode_refed = B_FALSE;
+
+ IPOINTER_REMOVE(ip, mp);
+
+ continue;
+ }
+
+ xfs_ilock(ip, lock_flags);
+
+ ASSERT(vp == XFS_ITOV(ip));
+ ASSERT(ip->i_mount == mp);
+
+ vnode_refed = B_TRUE;
+ }
+
+ /* From here on in the loop we may have a marker record
+ * in the inode list.
+ */
+
+ if ((flags & SYNC_CLOSE) && (vp != NULL)) {
+ /*
+ * This is the shutdown case. We just need to
+ * flush and invalidate all the pages associated
+ * with the inode. Drop the inode lock since
+ * we can't hold it across calls to the buffer
+ * cache.
+ *
+ * We don't set the VREMAPPING bit in the vnode
+ * here, because we don't hold the vnode lock
+ * exclusively. It doesn't really matter, though,
+ * because we only come here when we're shutting
+ * down anyway.
+ */
+ xfs_iunlock(ip, XFS_ILOCK_SHARED);
+
+ if (XFS_FORCED_SHUTDOWN(mp)) {
+ VOP_TOSS_PAGES(vp, 0, -1, FI_REMAPF);
+ } else {
+ VOP_FLUSHINVAL_PAGES(vp, 0, -1, FI_REMAPF);
+ }
+
+ xfs_ilock(ip, XFS_ILOCK_SHARED);
+
+ } else if ((flags & SYNC_DELWRI) && (vp != NULL)) {
+ if (VN_DIRTY(vp)) {
+ /* We need to have dropped the lock here,
+ * so insert a marker if we have not already
+ * done so.
+ */
+ if (mount_locked) {
+ IPOINTER_INSERT(ip, mp);
+ }
+
+ /*
+ * Drop the inode lock since we can't hold it
+ * across calls to the buffer cache.
+ */
+ xfs_iunlock(ip, XFS_ILOCK_SHARED);
+ VOP_FLUSH_PAGES(vp, (xfs_off_t)0, -1,
+ fflag, FI_NONE, error);
+ xfs_ilock(ip, XFS_ILOCK_SHARED);
+ }
+
+ }
+
+ if (flags & SYNC_BDFLUSH) {
+ if ((flags & SYNC_ATTR) &&
+ ((ip->i_update_core) ||
+ ((ip->i_itemp != NULL) &&
+ (ip->i_itemp->ili_format.ilf_fields != 0)))) {
+
+ /* Insert marker and drop lock if not already
+ * done.
+ */
+ if (mount_locked) {
+ IPOINTER_INSERT(ip, mp);
+ }
+
+ /*
+ * We don't want the periodic flushing of the
+ * inodes by vfs_sync() to interfere with
+ * I/O to the file, especially read I/O
+ * where it is only the access time stamp
+ * that is being flushed out. To prevent
+ * long periods where we have both inode
+ * locks held shared here while reading the
+ * inode's buffer in from disk, we drop the
+ * inode lock while reading in the inode
+ * buffer. We have to release the buffer
+ * and reacquire the inode lock so that they
+ * are acquired in the proper order (inode
+ * locks first). The buffer will go at the
+ * end of the lru chain, though, so we can
+ * expect it to still be there when we go
+ * for it again in xfs_iflush().
+ */
+ if ((xfs_ipincount(ip) == 0) &&
+ xfs_iflock_nowait(ip)) {
+
+ xfs_ifunlock(ip);
+ xfs_iunlock(ip, XFS_ILOCK_SHARED);
+
+ error = xfs_itobp(mp, NULL, ip,
+ &dip, &bp, 0);
+ if (!error) {
+ xfs_buf_relse(bp);
+ } else {
+ /* Bailing out, remove the
+ * marker and free it.
+ */
+ XFS_MOUNT_ILOCK(mp);
+
+ IPOINTER_REMOVE(ip, mp);
+
+ XFS_MOUNT_IUNLOCK(mp);
+
+ ASSERT(!(lock_flags &
+ XFS_IOLOCK_SHARED));
+
+ kmem_free(ipointer,
+ sizeof(xfs_iptr_t));
+ return (0);
+ }
+
+ /*
+ * Since we dropped the inode lock,
+ * the inode may have been reclaimed.
+ * Therefore, we reacquire the mount
+ * lock and check to see if we were the
+ * inode reclaimed. If this happened
+ * then the ipointer marker will no
+ * longer point back at us. In this
+ * case, move ip along to the inode
+ * after the marker, remove the marker
+ * and continue.
+ */
+ XFS_MOUNT_ILOCK(mp);
+ mount_locked = B_TRUE;
+
+ if (ip != ipointer->ip_mprev) {
+ IPOINTER_REMOVE(ip, mp);
+
+ ASSERT(!vnode_refed);
+ ASSERT(!(lock_flags &
+ XFS_IOLOCK_SHARED));
+ continue;
+ }
+
+ ASSERT(ip->i_mount == mp);
+
+ if (xfs_ilock_nowait(ip,
+ XFS_ILOCK_SHARED) == 0) {
+ ASSERT(ip->i_mount == mp);
+ /*
+ * We failed to reacquire
+ * the inode lock without
+ * sleeping, so just skip
+ * the inode for now. We
+ * clear the ILOCK bit from
+ * the lock_flags so that we
+ * won't try to drop a lock
+ * we don't hold below.
+ */
+ lock_flags &= ~XFS_ILOCK_SHARED;
+ IPOINTER_REMOVE(ip_next, mp);
+ } else if ((xfs_ipincount(ip) == 0) &&
+ xfs_iflock_nowait(ip)) {
+ ASSERT(ip->i_mount == mp);
+ /*
+ * Since this is vfs_sync()
+ * calling we only flush the
+ * inode out if we can lock
+ * it without sleeping and
+ * it is not pinned. Drop
+ * the mount lock here so
+ * that we don't hold it for
+ * too long. We already have
+ * a marker in the list here.
+ */
+ XFS_MOUNT_IUNLOCK(mp);
+ mount_locked = B_FALSE;
+ error = xfs_iflush(ip,
+ XFS_IFLUSH_DELWRI);
+ } else {
+ ASSERT(ip->i_mount == mp);
+ IPOINTER_REMOVE(ip_next, mp);
+ }
+ }
+
+ }
+
+ } else {
+ if ((flags & SYNC_ATTR) &&
+ ((ip->i_update_core) ||
+ ((ip->i_itemp != NULL) &&
+ (ip->i_itemp->ili_format.ilf_fields != 0)))) {
+ if (mount_locked) {
+ IPOINTER_INSERT(ip, mp);
+ }
+
+ if (flags & SYNC_WAIT) {
+ xfs_iflock(ip);
+ error = xfs_iflush(ip,
+ XFS_IFLUSH_SYNC);
+ } else {
+ /*
+ * If we can't acquire the flush
+ * lock, then the inode is already
+ * being flushed so don't bother
+ * waiting. If we can lock it then
+ * do a delwri flush so we can
+ * combine multiple inode flushes
+ * in each disk write.
+ */
+ if (xfs_iflock_nowait(ip)) {
+ error = xfs_iflush(ip,
+ XFS_IFLUSH_DELWRI);
+ }
+ else if (bypassed)
+ (*bypassed)++;
+ }
+ }
+ }
+
+ if (lock_flags != 0) {
+ xfs_iunlock(ip, lock_flags);
+ }
+
+ if (vnode_refed) {
+ /*
+ * If we had to take a reference on the vnode
+ * above, then wait until after we've unlocked
+ * the inode to release the reference. This is
+ * because we can be already holding the inode
+ * lock when VN_RELE() calls xfs_inactive().
+ *
+ * Make sure to drop the mount lock before calling
+ * VN_RELE() so that we don't trip over ourselves if
+ * we have to go for the mount lock again in the
+ * inactive code.
+ */
+ if (mount_locked) {
+ IPOINTER_INSERT(ip, mp);
+ }
+
+ VN_RELE(vp);
+
+ vnode_refed = B_FALSE;
+ }
+
+ if (error) {
+ last_error = error;
+ }
+
+ /*
+ * bail out if the filesystem is corrupted.
+ */
+ if (error == EFSCORRUPTED) {
+ if (!mount_locked) {
+ XFS_MOUNT_ILOCK(mp);
+ IPOINTER_REMOVE(ip, mp);
+ }
+ XFS_MOUNT_IUNLOCK(mp);
+ ASSERT(ipointer_in == B_FALSE);
+ kmem_free(ipointer, sizeof(xfs_iptr_t));
+ return XFS_ERROR(error);
+ }
+
+ /* Let other threads have a chance at the mount lock
+ * if we have looped many times without dropping the
+ * lock.
+ */
+ if ((++preempt & XFS_PREEMPT_MASK) == 0) {
+ if (mount_locked) {
+ IPOINTER_INSERT(ip, mp);
+ }
+ }
+
+ if (mount_locked == B_FALSE) {
+ XFS_MOUNT_ILOCK(mp);
+ mount_locked = B_TRUE;
+ IPOINTER_REMOVE(ip, mp);
+ continue;
+ }
+
+ ASSERT(ipointer_in == B_FALSE);
+ ip = ip->i_mnext;
+
+ } while (ip != mp->m_inodes);
+
+ XFS_MOUNT_IUNLOCK(mp);
+
+ ASSERT(ipointer_in == B_FALSE);
+
+ kmem_free(ipointer, sizeof(xfs_iptr_t));
+ return XFS_ERROR(last_error);
+}
+
+/*
+ * xfs sync routine for internal use
+ *
+ * This routine supports all of the flags defined for the generic VFS_SYNC
+ * interface as explained above under xfs_sync. In the interests of not
+ * changing interfaces within the 6.5 family, additional internallly-
+ * required functions are specified within a separate xflags parameter,
+ * only available by calling this routine.
+ *
+ */
+int
+xfs_syncsub(
+ xfs_mount_t *mp,
+ int flags,
+ int xflags,
+ int *bypassed)
+{
+ int error = 0;
+ int last_error = 0;
+ uint log_flags = XFS_LOG_FORCE;
+ xfs_buf_t *bp;
+ xfs_buf_log_item_t *bip;
+
+ /*
+ * Sync out the log. This ensures that the log is periodically
+ * flushed even if there is not enough activity to fill it up.
+ */
+ if (flags & SYNC_WAIT)
+ log_flags |= XFS_LOG_SYNC;
+
+ xfs_log_force(mp, (xfs_lsn_t)0, log_flags);
+
+ if (flags & (SYNC_ATTR|SYNC_DELWRI)) {
+ if (flags & SYNC_BDFLUSH)
+ xfs_finish_reclaim_all(mp, 1);
+ else
+ error = xfs_sync_inodes(mp, flags, xflags, bypassed);
+ }
+
+ /*
+ * Flushing out dirty data above probably generated more
+ * log activity, so if this isn't vfs_sync() then flush
+ * the log again.
+ */
+ if (flags & SYNC_DELWRI) {
+ xfs_log_force(mp, (xfs_lsn_t)0, log_flags);
+ }
+
+ if (flags & SYNC_FSDATA) {
+ /*
+ * If this is vfs_sync() then only sync the superblock
+ * if we can lock it without sleeping and it is not pinned.
+ */
+ if (flags & SYNC_BDFLUSH) {
+ bp = xfs_getsb(mp, XFS_BUF_TRYLOCK);
+ if (bp != NULL) {
+ bip = XFS_BUF_FSPRIVATE(bp,xfs_buf_log_item_t*);
+ if ((bip != NULL) &&
+ xfs_buf_item_dirty(bip)) {
+ if (!(XFS_BUF_ISPINNED(bp))) {
+ XFS_BUF_ASYNC(bp);
+ error = xfs_bwrite(mp, bp);
+ } else {
+ xfs_buf_relse(bp);
+ }
+ } else {
+ xfs_buf_relse(bp);
+ }
+ }
+ } else {
+ bp = xfs_getsb(mp, 0);
+ /*
+ * If the buffer is pinned then push on the log so
+ * we won't get stuck waiting in the write for
+ * someone, maybe ourselves, to flush the log.
+ * Even though we just pushed the log above, we
+ * did not have the superblock buffer locked at
+ * that point so it can become pinned in between
+ * there and here.
+ */
+ if (XFS_BUF_ISPINNED(bp))
+ xfs_log_force(mp, (xfs_lsn_t)0, XFS_LOG_FORCE);
+ if (flags & SYNC_WAIT)
+ XFS_BUF_UNASYNC(bp);
+ else
+ XFS_BUF_ASYNC(bp);
+ error = xfs_bwrite(mp, bp);
+ }
+ if (error) {
+ last_error = error;
+ }
+ }
+
+ /*
+ * If this is the periodic sync, then kick some entries out of
+ * the reference cache. This ensures that idle entries are
+ * eventually kicked out of the cache.
+ */
+ if (flags & SYNC_REFCACHE) {
+ if (flags & SYNC_WAIT)
+ xfs_refcache_purge_mp(mp);
+ else
+ xfs_refcache_purge_some(mp);
+ }
+
+ /*
+ * Now check to see if the log needs a "dummy" transaction.
+ */
+
+ if (!(flags & SYNC_REMOUNT) && xfs_log_need_covered(mp)) {
+ xfs_trans_t *tp;
+ xfs_inode_t *ip;
+
+ /*
+ * Put a dummy transaction in the log to tell
+ * recovery that all others are OK.
+ */
+ tp = xfs_trans_alloc(mp, XFS_TRANS_DUMMY1);
+ if ((error = xfs_trans_reserve(tp, 0,
+ XFS_ICHANGE_LOG_RES(mp),
+ 0, 0, 0))) {
+ xfs_trans_cancel(tp, 0);
+ return error;
+ }
+
+ ip = mp->m_rootip;
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
+
+ xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
+ xfs_trans_ihold(tp, ip);
+ xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+ error = xfs_trans_commit(tp, 0, NULL);
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ xfs_log_force(mp, (xfs_lsn_t)0, log_flags);
+ }
+
+ /*
+ * When shutting down, we need to insure that the AIL is pushed
+ * to disk or the filesystem can appear corrupt from the PROM.
+ */
+ if ((flags & (SYNC_CLOSE|SYNC_WAIT)) == (SYNC_CLOSE|SYNC_WAIT)) {
+ XFS_bflush(mp->m_ddev_targp);
+ if (mp->m_rtdev_targp) {
+ XFS_bflush(mp->m_rtdev_targp);
+ }
+ }
+
+ return XFS_ERROR(last_error);
+}
+
+/*
+ * xfs_vget - called by DMAPI and NFSD to get vnode from file handle
+ */
+STATIC int
+xfs_vget(
+ bhv_desc_t *bdp,
+ vnode_t **vpp,
+ fid_t *fidp)
+{
+ xfs_mount_t *mp = XFS_BHVTOM(bdp);
+ xfs_fid_t *xfid = (struct xfs_fid *)fidp;
+ xfs_inode_t *ip;
+ int error;
+ xfs_ino_t ino;
+ unsigned int igen;
+
+ /*
+ * Invalid. Since handles can be created in user space and passed in
+ * via gethandle(), this is not cause for a panic.
+ */
+ if (xfid->xfs_fid_len != sizeof(*xfid) - sizeof(xfid->xfs_fid_len))
+ return XFS_ERROR(EINVAL);
+
+ ino = xfid->xfs_fid_ino;
+ igen = xfid->xfs_fid_gen;
+
+ /*
+ * NFS can sometimes send requests for ino 0. Fail them gracefully.
+ */
+ if (ino == 0)
+ return XFS_ERROR(ESTALE);
+
+ error = xfs_iget(mp, NULL, ino, 0, XFS_ILOCK_SHARED, &ip, 0);
+ if (error) {
+ *vpp = NULL;
+ return error;
+ }
+
+ if (ip == NULL) {
+ *vpp = NULL;
+ return XFS_ERROR(EIO);
+ }
+
+ if (ip->i_d.di_mode == 0 || ip->i_d.di_gen != igen) {
+ xfs_iput_new(ip, XFS_ILOCK_SHARED);
+ *vpp = NULL;
+ return XFS_ERROR(ENOENT);
+ }
+
+ *vpp = XFS_ITOV(ip);
+ xfs_iunlock(ip, XFS_ILOCK_SHARED);
+ return 0;
+}
+
+
+#define MNTOPT_LOGBUFS "logbufs" /* number of XFS log buffers */
+#define MNTOPT_LOGBSIZE "logbsize" /* size of XFS log buffers */
+#define MNTOPT_LOGDEV "logdev" /* log device */
+#define MNTOPT_RTDEV "rtdev" /* realtime I/O device */
+#define MNTOPT_BIOSIZE "biosize" /* log2 of preferred buffered io size */
+#define MNTOPT_WSYNC "wsync" /* safe-mode nfs compatible mount */
+#define MNTOPT_INO64 "ino64" /* force inodes into 64-bit range */
+#define MNTOPT_NOALIGN "noalign" /* turn off stripe alignment */
+#define MNTOPT_SWALLOC "swalloc" /* turn on stripe width allocation */
+#define MNTOPT_SUNIT "sunit" /* data volume stripe unit */
+#define MNTOPT_SWIDTH "swidth" /* data volume stripe width */
+#define MNTOPT_NOUUID "nouuid" /* ignore filesystem UUID */
+#define MNTOPT_MTPT "mtpt" /* filesystem mount point */
+#define MNTOPT_NORECOVERY "norecovery" /* don't run XFS recovery */
+#define MNTOPT_NOLOGFLUSH "nologflush" /* don't hard flush on log writes */
+#define MNTOPT_OSYNCISOSYNC "osyncisosync" /* o_sync is REALLY o_sync */
+#define MNTOPT_64BITINODE "inode64" /* inodes can be allocated anywhere */
+#define MNTOPT_IKEEP "ikeep" /* do not free empty inode clusters */
+#define MNTOPT_NOIKEEP "noikeep" /* free empty inode clusters */
+
+
+int
+xfs_parseargs(
+ struct bhv_desc *bhv,
+ char *options,
+ struct xfs_mount_args *args,
+ int update)
+{
+ struct vfs *vfsp = bhvtovfs(bhv);
+ char *this_char, *value, *eov;
+ int dsunit, dswidth, vol_dsunit, vol_dswidth;
+ int iosize;
+
+#if 0 /* XXX: off by default, until some remaining issues ironed out */
+ args->flags |= XFSMNT_IDELETE; /* default to on */
+#endif
+
+ if (!options)
+ return 0;
+
+ iosize = dsunit = dswidth = vol_dsunit = vol_dswidth = 0;
+
+ while ((this_char = strsep(&options, ",")) != NULL) {
+ if (!*this_char)
+ continue;
+ if ((value = strchr(this_char, '=')) != NULL)
+ *value++ = 0;
+
+ if (!strcmp(this_char, MNTOPT_LOGBUFS)) {
+ if (!value || !*value) {
+ printk("XFS: %s option requires an argument\n",
+ MNTOPT_LOGBUFS);
+ return EINVAL;
+ }
+ args->logbufs = simple_strtoul(value, &eov, 10);
+ } else if (!strcmp(this_char, MNTOPT_LOGBSIZE)) {
+ int last, in_kilobytes = 0;
+
+ if (!value || !*value) {
+ printk("XFS: %s option requires an argument\n",
+ MNTOPT_LOGBSIZE);
+ return EINVAL;
+ }
+ last = strlen(value) - 1;
+ if (value[last] == 'K' || value[last] == 'k') {
+ in_kilobytes = 1;
+ value[last] = '\0';
+ }
+ args->logbufsize = simple_strtoul(value, &eov, 10);
+ if (in_kilobytes)
+ args->logbufsize <<= 10;
+ } else if (!strcmp(this_char, MNTOPT_LOGDEV)) {
+ if (!value || !*value) {
+ printk("XFS: %s option requires an argument\n",
+ MNTOPT_LOGDEV);
+ return EINVAL;
+ }
+ strncpy(args->logname, value, MAXNAMELEN);
+ } else if (!strcmp(this_char, MNTOPT_MTPT)) {
+ if (!value || !*value) {
+ printk("XFS: %s option requires an argument\n",
+ MNTOPT_MTPT);
+ return EINVAL;
+ }
+ strncpy(args->mtpt, value, MAXNAMELEN);
+ } else if (!strcmp(this_char, MNTOPT_RTDEV)) {
+ if (!value || !*value) {
+ printk("XFS: %s option requires an argument\n",
+ MNTOPT_RTDEV);
+ return EINVAL;
+ }
+ strncpy(args->rtname, value, MAXNAMELEN);
+ } else if (!strcmp(this_char, MNTOPT_BIOSIZE)) {
+ if (!value || !*value) {
+ printk("XFS: %s option requires an argument\n",
+ MNTOPT_BIOSIZE);
+ return EINVAL;
+ }
+ iosize = simple_strtoul(value, &eov, 10);
+ args->flags |= XFSMNT_IOSIZE;
+ args->iosizelog = (uint8_t) iosize;
+ } else if (!strcmp(this_char, MNTOPT_WSYNC)) {
+ args->flags |= XFSMNT_WSYNC;
+ } else if (!strcmp(this_char, MNTOPT_OSYNCISOSYNC)) {
+ args->flags |= XFSMNT_OSYNCISOSYNC;
+ } else if (!strcmp(this_char, MNTOPT_NORECOVERY)) {
+ args->flags |= XFSMNT_NORECOVERY;
+ } else if (!strcmp(this_char, MNTOPT_INO64)) {
+ args->flags |= XFSMNT_INO64;
+#if !XFS_BIG_INUMS
+ printk("XFS: %s option not allowed on this system\n",
+ MNTOPT_INO64);
+ return EINVAL;
+#endif
+ } else if (!strcmp(this_char, MNTOPT_NOALIGN)) {
+ args->flags |= XFSMNT_NOALIGN;
+ } else if (!strcmp(this_char, MNTOPT_SWALLOC)) {
+ args->flags |= XFSMNT_SWALLOC;
+ } else if (!strcmp(this_char, MNTOPT_SUNIT)) {
+ if (!value || !*value) {
+ printk("XFS: %s option requires an argument\n",
+ MNTOPT_SUNIT);
+ return EINVAL;
+ }
+ dsunit = simple_strtoul(value, &eov, 10);
+ } else if (!strcmp(this_char, MNTOPT_SWIDTH)) {
+ if (!value || !*value) {
+ printk("XFS: %s option requires an argument\n",
+ MNTOPT_SWIDTH);
+ return EINVAL;
+ }
+ dswidth = simple_strtoul(value, &eov, 10);
+ } else if (!strcmp(this_char, MNTOPT_64BITINODE)) {
+ args->flags &= ~XFSMNT_32BITINODES;
+#if !XFS_BIG_INUMS
+ printk("XFS: %s option not allowed on this system\n",
+ MNTOPT_64BITINODE);
+ return EINVAL;
+#endif
+ } else if (!strcmp(this_char, MNTOPT_NOUUID)) {
+ args->flags |= XFSMNT_NOUUID;
+ } else if (!strcmp(this_char, MNTOPT_NOLOGFLUSH)) {
+ args->flags |= XFSMNT_NOLOGFLUSH;
+ } else if (!strcmp(this_char, MNTOPT_IKEEP)) {
+ args->flags &= ~XFSMNT_IDELETE;
+ } else if (!strcmp(this_char, MNTOPT_NOIKEEP)) {
+ args->flags |= XFSMNT_IDELETE;
+ } else if (!strcmp(this_char, "osyncisdsync")) {
+ /* no-op, this is now the default */
+printk("XFS: osyncisdsync is now the default, option is deprecated.\n");
+ } else if (!strcmp(this_char, "irixsgid")) {
+printk("XFS: irixsgid is now a sysctl(2) variable, option is deprecated.\n");
+ } else {
+ printk("XFS: unknown mount option [%s].\n", this_char);
+ return EINVAL;
+ }
+ }
+
+ if (args->flags & XFSMNT_NORECOVERY) {
+ if ((vfsp->vfs_flag & VFS_RDONLY) == 0) {
+ printk("XFS: no-recovery mounts must be read-only.\n");
+ return EINVAL;
+ }
+ }
+
+ if ((args->flags & XFSMNT_NOALIGN) && (dsunit || dswidth)) {
+ printk(
+ "XFS: sunit and swidth options incompatible with the noalign option\n");
+ return EINVAL;
+ }
+
+ if ((dsunit && !dswidth) || (!dsunit && dswidth)) {
+ printk("XFS: sunit and swidth must be specified together\n");
+ return EINVAL;
+ }
+
+ if (dsunit && (dswidth % dsunit != 0)) {
+ printk(
+ "XFS: stripe width (%d) must be a multiple of the stripe unit (%d)\n",
+ dswidth, dsunit);
+ return EINVAL;
+ }
+
+ if ((args->flags & XFSMNT_NOALIGN) != XFSMNT_NOALIGN) {
+ if (dsunit) {
+ args->sunit = dsunit;
+ args->flags |= XFSMNT_RETERR;
+ } else {
+ args->sunit = vol_dsunit;
+ }
+ dswidth ? (args->swidth = dswidth) :
+ (args->swidth = vol_dswidth);
+ } else {
+ args->sunit = args->swidth = 0;
+ }
+
+ return 0;
+}
+
+int
+xfs_showargs(
+ struct bhv_desc *bhv,
+ struct seq_file *m)
+{
+ static struct proc_xfs_info {
+ int flag;
+ char *str;
+ } xfs_info[] = {
+ /* the few simple ones we can get from the mount struct */
+ { XFS_MOUNT_WSYNC, "," MNTOPT_WSYNC },
+ { XFS_MOUNT_INO64, "," MNTOPT_INO64 },
+ { XFS_MOUNT_NOALIGN, "," MNTOPT_NOALIGN },
+ { XFS_MOUNT_SWALLOC, "," MNTOPT_SWALLOC },
+ { XFS_MOUNT_NOUUID, "," MNTOPT_NOUUID },
+ { XFS_MOUNT_NORECOVERY, "," MNTOPT_NORECOVERY },
+ { XFS_MOUNT_OSYNCISOSYNC, "," MNTOPT_OSYNCISOSYNC },
+ { XFS_MOUNT_NOLOGFLUSH, "," MNTOPT_NOLOGFLUSH },
+ { XFS_MOUNT_IDELETE, "," MNTOPT_NOIKEEP },
+ { 0, NULL }
+ };
+ struct proc_xfs_info *xfs_infop;
+ struct xfs_mount *mp = XFS_BHVTOM(bhv);
+
+ for (xfs_infop = xfs_info; xfs_infop->flag; xfs_infop++) {
+ if (mp->m_flags & xfs_infop->flag)
+ seq_puts(m, xfs_infop->str);
+ }
+
+ if (mp->m_flags & XFS_MOUNT_DFLT_IOSIZE)
+ seq_printf(m, "," MNTOPT_BIOSIZE "=%d", mp->m_writeio_log);
+
+ if (mp->m_logbufs > 0)
+ seq_printf(m, "," MNTOPT_LOGBUFS "=%d", mp->m_logbufs);
+
+ if (mp->m_logbsize > 0)
+ seq_printf(m, "," MNTOPT_LOGBSIZE "=%d", mp->m_logbsize);
+
+ if (mp->m_ddev_targp != mp->m_logdev_targp)
+ seq_printf(m, "," MNTOPT_LOGDEV "=%s",
+ XFS_BUFTARG_NAME(mp->m_logdev_targp));
+
+ if (mp->m_rtdev_targp && mp->m_ddev_targp != mp->m_rtdev_targp)
+ seq_printf(m, "," MNTOPT_RTDEV "=%s",
+ XFS_BUFTARG_NAME(mp->m_rtdev_targp));
+
+ if (mp->m_dalign > 0)
+ seq_printf(m, "," MNTOPT_SUNIT "=%d",
+ (int)XFS_FSB_TO_BB(mp, mp->m_dalign));
+
+ if (mp->m_swidth > 0)
+ seq_printf(m, "," MNTOPT_SWIDTH "=%d",
+ (int)XFS_FSB_TO_BB(mp, mp->m_swidth));
+
+ if (!(mp->m_flags & XFS_MOUNT_32BITINOOPT))
+ seq_printf(m, "," MNTOPT_64BITINODE);
+
+ return 0;
+}
+
+STATIC void
+xfs_freeze(
+ bhv_desc_t *bdp)
+{
+ xfs_mount_t *mp = XFS_BHVTOM(bdp);
+
+ while (atomic_read(&mp->m_active_trans) > 0)
+ delay(100);
+
+ /* Push the superblock and write an unmount record */
+ xfs_log_unmount_write(mp);
+ xfs_unmountfs_writesb(mp);
+}
+
+
+vfsops_t xfs_vfsops = {
+ BHV_IDENTITY_INIT(VFS_BHV_XFS,VFS_POSITION_XFS),
+ .vfs_parseargs = xfs_parseargs,
+ .vfs_showargs = xfs_showargs,
+ .vfs_mount = xfs_mount,
+ .vfs_unmount = xfs_unmount,
+ .vfs_mntupdate = xfs_mntupdate,
+ .vfs_root = xfs_root,
+ .vfs_statvfs = xfs_statvfs,
+ .vfs_sync = xfs_sync,
+ .vfs_vget = xfs_vget,
+ .vfs_dmapiops = (vfs_dmapiops_t)fs_nosys,
+ .vfs_quotactl = (vfs_quotactl_t)fs_nosys,
+ .vfs_get_inode = xfs_get_inode,
+ .vfs_init_vnode = xfs_initialize_vnode,
+ .vfs_force_shutdown = xfs_do_force_shutdown,
+ .vfs_freeze = xfs_freeze,
+};
diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c
new file mode 100644
index 00000000000000..627505db58b16a
--- /dev/null
+++ b/fs/xfs/xfs_vnodeops.c
@@ -0,0 +1,4712 @@
+/*
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "xfs.h"
+#include "xfs_macros.h"
+#include "xfs_types.h"
+#include "xfs_inum.h"
+#include "xfs_log.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir.h"
+#include "xfs_dir2.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_itable.h"
+#include "xfs_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_alloc.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dir_sf.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode_item.h"
+#include "xfs_inode.h"
+#include "xfs_bmap.h"
+#include "xfs_da_btree.h"
+#include "xfs_attr.h"
+#include "xfs_rw.h"
+#include "xfs_refcache.h"
+#include "xfs_error.h"
+#include "xfs_bit.h"
+#include "xfs_rtalloc.h"
+#include "xfs_quota.h"
+#include "xfs_utils.h"
+#include "xfs_trans_space.h"
+#include "xfs_dir_leaf.h"
+#include "xfs_mac.h"
+#include "xfs_log_priv.h"
+
+
+/*
+ * The maximum pathlen is 1024 bytes. Since the minimum file system
+ * blocksize is 512 bytes, we can get a max of 2 extents back from
+ * bmapi.
+ */
+#define SYMLINK_MAPS 2
+
+/*
+ * For xfs, we check that the file isn't too big to be opened by this kernel.
+ * No other open action is required for regular files. Devices are handled
+ * through the specfs file system, pipes through fifofs. Device and
+ * fifo vnodes are "wrapped" by specfs and fifofs vnodes, respectively,
+ * when a new vnode is first looked up or created.
+ */
+STATIC int
+xfs_open(
+ bhv_desc_t *bdp,
+ cred_t *credp)
+{
+ int mode;
+ vnode_t *vp;
+ xfs_inode_t *ip;
+
+ vp = BHV_TO_VNODE(bdp);
+ ip = XFS_BHVTOI(bdp);
+
+ if (XFS_FORCED_SHUTDOWN(ip->i_mount))
+ return XFS_ERROR(EIO);
+
+ /*
+ * If it's a directory with any blocks, read-ahead block 0
+ * as we're almost certain to have the next operation be a read there.
+ */
+ if (vp->v_type == VDIR && ip->i_d.di_nextents > 0) {
+ mode = xfs_ilock_map_shared(ip);
+ if (ip->i_d.di_nextents > 0)
+ (void)xfs_da_reada_buf(NULL, ip, 0, XFS_DATA_FORK);
+ xfs_iunlock(ip, mode);
+ }
+ return 0;
+}
+
+
+/*
+ * xfs_getattr
+ */
+STATIC int
+xfs_getattr(
+ bhv_desc_t *bdp,
+ vattr_t *vap,
+ int flags,
+ cred_t *credp)
+{
+ xfs_inode_t *ip;
+ xfs_mount_t *mp;
+ vnode_t *vp;
+
+ vp = BHV_TO_VNODE(bdp);
+ vn_trace_entry(vp, __FUNCTION__, (inst_t *)__return_address);
+
+ ip = XFS_BHVTOI(bdp);
+ mp = ip->i_mount;
+
+ if (XFS_FORCED_SHUTDOWN(mp))
+ return XFS_ERROR(EIO);
+
+ if (!(flags & ATTR_LAZY))
+ xfs_ilock(ip, XFS_ILOCK_SHARED);
+
+ vap->va_size = ip->i_d.di_size;
+ if (vap->va_mask == XFS_AT_SIZE)
+ goto all_done;
+
+ vap->va_nblocks =
+ XFS_FSB_TO_BB(mp, ip->i_d.di_nblocks + ip->i_delayed_blks);
+ vap->va_nodeid = ip->i_ino;
+#if XFS_BIG_INUMS
+ vap->va_nodeid += mp->m_inoadd;
+#endif
+ vap->va_nlink = ip->i_d.di_nlink;
+
+ /*
+ * Quick exit for non-stat callers
+ */
+ if ((vap->va_mask &
+ ~(XFS_AT_SIZE|XFS_AT_FSID|XFS_AT_NODEID|
+ XFS_AT_NLINK|XFS_AT_BLKSIZE)) == 0)
+ goto all_done;
+
+ /*
+ * Copy from in-core inode.
+ */
+ vap->va_type = vp->v_type;
+ vap->va_mode = ip->i_d.di_mode & MODEMASK;
+ vap->va_uid = ip->i_d.di_uid;
+ vap->va_gid = ip->i_d.di_gid;
+ vap->va_projid = ip->i_d.di_projid;
+
+ /*
+ * Check vnode type block/char vs. everything else.
+ * Do it with bitmask because that's faster than looking
+ * for multiple values individually.
+ */
+ if (((1 << vp->v_type) & ((1<<VBLK) | (1<<VCHR))) == 0) {
+ vap->va_rdev = 0;
+
+ if (!(ip->i_d.di_flags & XFS_DIFLAG_REALTIME)) {
+
+#if 0
+ /* Large block sizes confuse various
+ * user space programs, so letting the
+ * stripe size through is not a good
+ * idea for now.
+ */
+ vap->va_blocksize = mp->m_swidth ?
+ /*
+ * If the underlying volume is a stripe, then
+ * return the stripe width in bytes as the
+ * recommended I/O size.
+ */
+ (mp->m_swidth << mp->m_sb.sb_blocklog) :
+ /*
+ * Return the largest of the preferred buffer
+ * sizes since doing small I/Os into larger
+ * buffers causes buffers to be decommissioned.
+ * The value returned is in bytes.
+ */
+ (1 << (int)MAX(mp->m_readio_log,
+ mp->m_writeio_log));
+
+#else
+ vap->va_blocksize =
+ /*
+ * Return the largest of the preferred buffer
+ * sizes since doing small I/Os into larger
+ * buffers causes buffers to be decommissioned.
+ * The value returned is in bytes.
+ */
+ 1 << (int)MAX(mp->m_readio_log,
+ mp->m_writeio_log);
+#endif
+ } else {
+
+ /*
+ * If the file blocks are being allocated from a
+ * realtime partition, then return the inode's
+ * realtime extent size or the realtime volume's
+ * extent size.
+ */
+ vap->va_blocksize = ip->i_d.di_extsize ?
+ (ip->i_d.di_extsize << mp->m_sb.sb_blocklog) :
+ (mp->m_sb.sb_rextsize << mp->m_sb.sb_blocklog);
+ }
+ } else {
+ vap->va_rdev = ip->i_df.if_u2.if_rdev;
+ vap->va_blocksize = BLKDEV_IOSIZE;
+ }
+
+ vap->va_atime.tv_sec = ip->i_d.di_atime.t_sec;
+ vap->va_atime.tv_nsec = ip->i_d.di_atime.t_nsec;
+ vap->va_mtime.tv_sec = ip->i_d.di_mtime.t_sec;
+ vap->va_mtime.tv_nsec = ip->i_d.di_mtime.t_nsec;
+ vap->va_ctime.tv_sec = ip->i_d.di_ctime.t_sec;
+ vap->va_ctime.tv_nsec = ip->i_d.di_ctime.t_nsec;
+
+ /*
+ * Exit for stat callers. See if any of the rest of the fields
+ * to be filled in are needed.
+ */
+ if ((vap->va_mask &
+ (XFS_AT_XFLAGS|XFS_AT_EXTSIZE|XFS_AT_NEXTENTS|XFS_AT_ANEXTENTS|
+ XFS_AT_GENCOUNT|XFS_AT_VCODE)) == 0)
+ goto all_done;
+
+ /*
+ * Convert di_flags to xflags.
+ */
+ vap->va_xflags = xfs_dic2xflags(&ip->i_d, ARCH_NOCONVERT);
+
+ /*
+ * Exit for inode revalidate. See if any of the rest of
+ * the fields to be filled in are needed.
+ */
+ if ((vap->va_mask &
+ (XFS_AT_EXTSIZE|XFS_AT_NEXTENTS|XFS_AT_ANEXTENTS|
+ XFS_AT_GENCOUNT|XFS_AT_VCODE)) == 0)
+ goto all_done;
+
+ vap->va_extsize = ip->i_d.di_extsize << mp->m_sb.sb_blocklog;
+ vap->va_nextents =
+ (ip->i_df.if_flags & XFS_IFEXTENTS) ?
+ ip->i_df.if_bytes / sizeof(xfs_bmbt_rec_t) :
+ ip->i_d.di_nextents;
+ if (ip->i_afp)
+ vap->va_anextents =
+ (ip->i_afp->if_flags & XFS_IFEXTENTS) ?
+ ip->i_afp->if_bytes / sizeof(xfs_bmbt_rec_t) :
+ ip->i_d.di_anextents;
+ else
+ vap->va_anextents = 0;
+ vap->va_gen = ip->i_d.di_gen;
+
+ all_done:
+ if (!(flags & ATTR_LAZY))
+ xfs_iunlock(ip, XFS_ILOCK_SHARED);
+ return 0;
+}
+
+
+/*
+ * xfs_setattr
+ */
+int
+xfs_setattr(
+ bhv_desc_t *bdp,
+ vattr_t *vap,
+ int flags,
+ cred_t *credp)
+{
+ xfs_inode_t *ip;
+ xfs_trans_t *tp;
+ xfs_mount_t *mp;
+ int mask;
+ int code;
+ uint lock_flags;
+ uint commit_flags=0;
+ uid_t uid=0, iuid=0;
+ gid_t gid=0, igid=0;
+ int timeflags = 0;
+ vnode_t *vp;
+ xfs_prid_t projid=0, iprojid=0;
+ int mandlock_before, mandlock_after;
+ struct xfs_dquot *udqp, *gdqp, *olddquot1, *olddquot2;
+ int file_owner;
+ int need_iolock = (flags & ATTR_DMI) == 0;
+
+ vp = BHV_TO_VNODE(bdp);
+ vn_trace_entry(vp, __FUNCTION__, (inst_t *)__return_address);
+
+ if (vp->v_vfsp->vfs_flag & VFS_RDONLY)
+ return XFS_ERROR(EROFS);
+
+ /*
+ * Cannot set certain attributes.
+ */
+ mask = vap->va_mask;
+ if (mask & XFS_AT_NOSET) {
+ return XFS_ERROR(EINVAL);
+ }
+
+ ip = XFS_BHVTOI(bdp);
+ mp = ip->i_mount;
+
+ if (XFS_FORCED_SHUTDOWN(mp))
+ return XFS_ERROR(EIO);
+
+ /*
+ * Timestamps do not need to be logged and hence do not
+ * need to be done within a transaction.
+ */
+ if (mask & XFS_AT_UPDTIMES) {
+ ASSERT((mask & ~XFS_AT_UPDTIMES) == 0);
+ timeflags = ((mask & XFS_AT_UPDATIME) ? XFS_ICHGTIME_ACC : 0) |
+ ((mask & XFS_AT_UPDCTIME) ? XFS_ICHGTIME_CHG : 0) |
+ ((mask & XFS_AT_UPDMTIME) ? XFS_ICHGTIME_MOD : 0);
+ xfs_ichgtime(ip, timeflags);
+ return 0;
+ }
+
+ olddquot1 = olddquot2 = NULL;
+ udqp = gdqp = NULL;
+
+ /*
+ * If disk quotas is on, we make sure that the dquots do exist on disk,
+ * before we start any other transactions. Trying to do this later
+ * is messy. We don't care to take a readlock to look at the ids
+ * in inode here, because we can't hold it across the trans_reserve.
+ * If the IDs do change before we take the ilock, we're covered
+ * because the i_*dquot fields will get updated anyway.
+ */
+ if (XFS_IS_QUOTA_ON(mp) && (mask & (XFS_AT_UID|XFS_AT_GID))) {
+ uint qflags = 0;
+
+ if (mask & XFS_AT_UID) {
+ uid = vap->va_uid;
+ qflags |= XFS_QMOPT_UQUOTA;
+ } else {
+ uid = ip->i_d.di_uid;
+ }
+ if (mask & XFS_AT_GID) {
+ gid = vap->va_gid;
+ qflags |= XFS_QMOPT_GQUOTA;
+ } else {
+ gid = ip->i_d.di_gid;
+ }
+ /*
+ * We take a reference when we initialize udqp and gdqp,
+ * so it is important that we never blindly double trip on
+ * the same variable. See xfs_create() for an example.
+ */
+ ASSERT(udqp == NULL);
+ ASSERT(gdqp == NULL);
+ code = XFS_QM_DQVOPALLOC(mp, ip, uid,gid, qflags, &udqp, &gdqp);
+ if (code)
+ return (code);
+ }
+
+ /*
+ * For the other attributes, we acquire the inode lock and
+ * first do an error checking pass.
+ */
+ tp = NULL;
+ lock_flags = XFS_ILOCK_EXCL;
+ if (!(mask & XFS_AT_SIZE)) {
+ if ((mask != (XFS_AT_CTIME|XFS_AT_ATIME|XFS_AT_MTIME)) ||
+ (mp->m_flags & XFS_MOUNT_WSYNC)) {
+ tp = xfs_trans_alloc(mp, XFS_TRANS_SETATTR_NOT_SIZE);
+ commit_flags = 0;
+ if ((code = xfs_trans_reserve(tp, 0,
+ XFS_ICHANGE_LOG_RES(mp), 0,
+ 0, 0))) {
+ lock_flags = 0;
+ goto error_return;
+ }
+ }
+ } else {
+ if (DM_EVENT_ENABLED (vp->v_vfsp, ip, DM_EVENT_TRUNCATE) &&
+ !(flags & ATTR_DMI)) {
+ int dmflags = AT_DELAY_FLAG(flags) | DM_SEM_FLAG_WR;
+ code = XFS_SEND_DATA(mp, DM_EVENT_TRUNCATE, vp,
+ vap->va_size, 0, dmflags, NULL);
+ if (code) {
+ lock_flags = 0;
+ goto error_return;
+ }
+ }
+ if (need_iolock)
+ lock_flags |= XFS_IOLOCK_EXCL;
+ }
+
+ xfs_ilock(ip, lock_flags);
+
+ /* boolean: are we the file owner? */
+ file_owner = (current_fsuid(credp) == ip->i_d.di_uid);
+
+ /*
+ * Change various properties of a file.
+ * Only the owner or users with CAP_FOWNER
+ * capability may do these things.
+ */
+ if (mask &
+ (XFS_AT_MODE|XFS_AT_XFLAGS|XFS_AT_EXTSIZE|XFS_AT_UID|
+ XFS_AT_GID|XFS_AT_PROJID)) {
+ /*
+ * CAP_FOWNER overrides the following restrictions:
+ *
+ * The user ID of the calling process must be equal
+ * to the file owner ID, except in cases where the
+ * CAP_FSETID capability is applicable.
+ */
+ if (!file_owner && !capable(CAP_FOWNER)) {
+ code = XFS_ERROR(EPERM);
+ goto error_return;
+ }
+
+ /*
+ * CAP_FSETID overrides the following restrictions:
+ *
+ * The effective user ID of the calling process shall match
+ * the file owner when setting the set-user-ID and
+ * set-group-ID bits on that file.
+ *
+ * The effective group ID or one of the supplementary group
+ * IDs of the calling process shall match the group owner of
+ * the file when setting the set-group-ID bit on that file
+ */
+ if (mask & XFS_AT_MODE) {
+ mode_t m = 0;
+
+ if ((vap->va_mode & S_ISUID) && !file_owner)
+ m |= S_ISUID;
+ if ((vap->va_mode & S_ISGID) &&
+ !in_group_p((gid_t)ip->i_d.di_gid))
+ m |= S_ISGID;
+#if 0
+ /* Linux allows this, Irix doesn't. */
+ if ((vap->va_mode & S_ISVTX) && vp->v_type != VDIR)
+ m |= S_ISVTX;
+#endif
+ if (m && !capable(CAP_FSETID))
+ vap->va_mode &= ~m;
+ }
+ }
+
+ /*
+ * Change file ownership. Must be the owner or privileged.
+ * If the system was configured with the "restricted_chown"
+ * option, the owner is not permitted to give away the file,
+ * and can change the group id only to a group of which he
+ * or she is a member.
+ */
+ if (mask & (XFS_AT_UID|XFS_AT_GID|XFS_AT_PROJID)) {
+ /*
+ * These IDs could have changed since we last looked at them.
+ * But, we're assured that if the ownership did change
+ * while we didn't have the inode locked, inode's dquot(s)
+ * would have changed also.
+ */
+ iuid = ip->i_d.di_uid;
+ iprojid = ip->i_d.di_projid;
+ igid = ip->i_d.di_gid;
+ gid = (mask & XFS_AT_GID) ? vap->va_gid : igid;
+ uid = (mask & XFS_AT_UID) ? vap->va_uid : iuid;
+ projid = (mask & XFS_AT_PROJID) ? (xfs_prid_t)vap->va_projid :
+ iprojid;
+
+ /*
+ * CAP_CHOWN overrides the following restrictions:
+ *
+ * If _POSIX_CHOWN_RESTRICTED is defined, this capability
+ * shall override the restriction that a process cannot
+ * change the user ID of a file it owns and the restriction
+ * that the group ID supplied to the chown() function
+ * shall be equal to either the group ID or one of the
+ * supplementary group IDs of the calling process.
+ *
+ * XXX: How does restricted_chown affect projid?
+ */
+ if (restricted_chown &&
+ (iuid != uid || (igid != gid &&
+ !in_group_p((gid_t)gid))) &&
+ !capable(CAP_CHOWN)) {
+ code = XFS_ERROR(EPERM);
+ goto error_return;
+ }
+ /*
+ * Do a quota reservation only if uid or gid is actually
+ * going to change.
+ */
+ if ((XFS_IS_UQUOTA_ON(mp) && iuid != uid) ||
+ (XFS_IS_GQUOTA_ON(mp) && igid != gid)) {
+ ASSERT(tp);
+ code = XFS_QM_DQVOPCHOWNRESV(mp, tp, ip, udqp, gdqp,
+ capable(CAP_FOWNER) ?
+ XFS_QMOPT_FORCE_RES : 0);
+ if (code) /* out of quota */
+ goto error_return;
+ }
+ }
+
+ /*
+ * Truncate file. Must have write permission and not be a directory.
+ */
+ if (mask & XFS_AT_SIZE) {
+ /* Short circuit the truncate case for zero length files */
+ if ((vap->va_size == 0) &&
+ (ip->i_d.di_size == 0) && (ip->i_d.di_nextents == 0)) {
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ lock_flags &= ~XFS_ILOCK_EXCL;
+ if (mask & XFS_AT_CTIME)
+ xfs_ichgtime(ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
+ code = 0;
+ goto error_return;
+ }
+
+ if (vp->v_type == VDIR) {
+ code = XFS_ERROR(EISDIR);
+ goto error_return;
+ } else if (vp->v_type != VREG) {
+ code = XFS_ERROR(EINVAL);
+ goto error_return;
+ }
+ /*
+ * Make sure that the dquots are attached to the inode.
+ */
+ if ((code = XFS_QM_DQATTACH(mp, ip, XFS_QMOPT_ILOCKED)))
+ goto error_return;
+ }
+
+ /*
+ * Change file access or modified times.
+ */
+ if (mask & (XFS_AT_ATIME|XFS_AT_MTIME)) {
+ if (!file_owner) {
+ if ((flags & ATTR_UTIME) &&
+ !capable(CAP_FOWNER)) {
+ code = XFS_ERROR(EPERM);
+ goto error_return;
+ }
+ }
+ }
+
+ /*
+ * Change extent size or realtime flag.
+ */
+ if (mask & (XFS_AT_EXTSIZE|XFS_AT_XFLAGS)) {
+ /*
+ * Can't change extent size if any extents are allocated.
+ */
+ if ((ip->i_d.di_nextents || ip->i_delayed_blks) &&
+ (mask & XFS_AT_EXTSIZE) &&
+ ((ip->i_d.di_extsize << mp->m_sb.sb_blocklog) !=
+ vap->va_extsize) ) {
+ code = XFS_ERROR(EINVAL); /* EFBIG? */
+ goto error_return;
+ }
+
+ /*
+ * Can't set extent size unless the file is marked, or
+ * about to be marked as a realtime file.
+ *
+ * This check will be removed when fixed size extents
+ * with buffered data writes is implemented.
+ *
+ */
+ if ((mask & XFS_AT_EXTSIZE) &&
+ ((ip->i_d.di_extsize << mp->m_sb.sb_blocklog) !=
+ vap->va_extsize) &&
+ (!((ip->i_d.di_flags & XFS_DIFLAG_REALTIME) ||
+ ((mask & XFS_AT_XFLAGS) &&
+ (vap->va_xflags & XFS_XFLAG_REALTIME))))) {
+ code = XFS_ERROR(EINVAL);
+ goto error_return;
+ }
+
+ /*
+ * Can't change realtime flag if any extents are allocated.
+ */
+ if (ip->i_d.di_nextents && (mask & XFS_AT_XFLAGS) &&
+ (ip->i_d.di_flags & XFS_DIFLAG_REALTIME) !=
+ (vap->va_xflags & XFS_XFLAG_REALTIME)) {
+ code = XFS_ERROR(EINVAL); /* EFBIG? */
+ goto error_return;
+ }
+ /*
+ * Extent size must be a multiple of the appropriate block
+ * size, if set at all.
+ */
+ if ((mask & XFS_AT_EXTSIZE) && vap->va_extsize != 0) {
+ xfs_extlen_t size;
+
+ if ((ip->i_d.di_flags & XFS_DIFLAG_REALTIME) ||
+ ((mask & XFS_AT_XFLAGS) &&
+ (vap->va_xflags & XFS_XFLAG_REALTIME))) {
+ size = mp->m_sb.sb_rextsize <<
+ mp->m_sb.sb_blocklog;
+ } else {
+ size = mp->m_sb.sb_blocksize;
+ }
+ if (vap->va_extsize % size) {
+ code = XFS_ERROR(EINVAL);
+ goto error_return;
+ }
+ }
+ /*
+ * If realtime flag is set then must have realtime data.
+ */
+ if ((mask & XFS_AT_XFLAGS) &&
+ (vap->va_xflags & XFS_XFLAG_REALTIME)) {
+ if ((mp->m_sb.sb_rblocks == 0) ||
+ (mp->m_sb.sb_rextsize == 0) ||
+ (ip->i_d.di_extsize % mp->m_sb.sb_rextsize)) {
+ code = XFS_ERROR(EINVAL);
+ goto error_return;
+ }
+ }
+
+ /*
+ * Can't modify an immutable/append-only file unless
+ * we have appropriate permission.
+ */
+ if ((mask & XFS_AT_XFLAGS) &&
+ (ip->i_d.di_flags &
+ (XFS_DIFLAG_IMMUTABLE|XFS_DIFLAG_APPEND) ||
+ (vap->va_xflags &
+ (XFS_XFLAG_IMMUTABLE | XFS_XFLAG_APPEND))) &&
+ !capable(CAP_LINUX_IMMUTABLE)) {
+ code = XFS_ERROR(EPERM);
+ goto error_return;
+ }
+ }
+
+ /*
+ * Now we can make the changes. Before we join the inode
+ * to the transaction, if XFS_AT_SIZE is set then take care of
+ * the part of the truncation that must be done without the
+ * inode lock. This needs to be done before joining the inode
+ * to the transaction, because the inode cannot be unlocked
+ * once it is a part of the transaction.
+ */
+ if (mask & XFS_AT_SIZE) {
+ code = 0;
+ if (vap->va_size > ip->i_d.di_size)
+ code = xfs_igrow_start(ip, vap->va_size, credp);
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ if (!code)
+ code = xfs_itruncate_data(ip, vap->va_size);
+ if (code) {
+ ASSERT(tp == NULL);
+ lock_flags &= ~XFS_ILOCK_EXCL;
+ ASSERT(lock_flags == XFS_IOLOCK_EXCL);
+ goto error_return;
+ }
+ tp = xfs_trans_alloc(mp, XFS_TRANS_SETATTR_SIZE);
+ if ((code = xfs_trans_reserve(tp, 0,
+ XFS_ITRUNCATE_LOG_RES(mp), 0,
+ XFS_TRANS_PERM_LOG_RES,
+ XFS_ITRUNCATE_LOG_COUNT))) {
+ xfs_trans_cancel(tp, 0);
+ if (need_iolock)
+ xfs_iunlock(ip, XFS_IOLOCK_EXCL);
+ return code;
+ }
+ commit_flags = XFS_TRANS_RELEASE_LOG_RES;
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
+ }
+
+ if (tp) {
+ xfs_trans_ijoin(tp, ip, lock_flags);
+ xfs_trans_ihold(tp, ip);
+ }
+
+ /* determine whether mandatory locking mode changes */
+ mandlock_before = MANDLOCK(vp, ip->i_d.di_mode);
+
+ /*
+ * Truncate file. Must have write permission and not be a directory.
+ */
+ if (mask & XFS_AT_SIZE) {
+ if (vap->va_size > ip->i_d.di_size) {
+ xfs_igrow_finish(tp, ip, vap->va_size,
+ !(flags & ATTR_DMI));
+ } else if ((vap->va_size <= ip->i_d.di_size) ||
+ ((vap->va_size == 0) && ip->i_d.di_nextents)) {
+ /*
+ * signal a sync transaction unless
+ * we're truncating an already unlinked
+ * file on a wsync filesystem
+ */
+ code = xfs_itruncate_finish(&tp, ip,
+ (xfs_fsize_t)vap->va_size,
+ XFS_DATA_FORK,
+ ((ip->i_d.di_nlink != 0 ||
+ !(mp->m_flags & XFS_MOUNT_WSYNC))
+ ? 1 : 0));
+ if (code) {
+ goto abort_return;
+ }
+ }
+ /*
+ * Have to do this even if the file's size doesn't change.
+ */
+ timeflags |= XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG;
+ }
+
+ /*
+ * Change file access modes.
+ */
+ if (mask & XFS_AT_MODE) {
+ ip->i_d.di_mode &= S_IFMT;
+ ip->i_d.di_mode |= vap->va_mode & ~S_IFMT;
+
+ xfs_trans_log_inode (tp, ip, XFS_ILOG_CORE);
+ timeflags |= XFS_ICHGTIME_CHG;
+ }
+
+ /*
+ * Change file ownership. Must be the owner or privileged.
+ * If the system was configured with the "restricted_chown"
+ * option, the owner is not permitted to give away the file,
+ * and can change the group id only to a group of which he
+ * or she is a member.
+ */
+ if (mask & (XFS_AT_UID|XFS_AT_GID|XFS_AT_PROJID)) {
+ /*
+ * CAP_FSETID overrides the following restrictions:
+ *
+ * The set-user-ID and set-group-ID bits of a file will be
+ * cleared upon successful return from chown()
+ */
+ if ((ip->i_d.di_mode & (S_ISUID|S_ISGID)) &&
+ !capable(CAP_FSETID)) {
+ ip->i_d.di_mode &= ~(S_ISUID|S_ISGID);
+ }
+
+ /*
+ * Change the ownerships and register quota modifications
+ * in the transaction.
+ */
+ if (iuid != uid) {
+ if (XFS_IS_UQUOTA_ON(mp)) {
+ ASSERT(mask & XFS_AT_UID);
+ ASSERT(udqp);
+ olddquot1 = XFS_QM_DQVOPCHOWN(mp, tp, ip,
+ &ip->i_udquot, udqp);
+ }
+ ip->i_d.di_uid = uid;
+ }
+ if (igid != gid) {
+ if (XFS_IS_GQUOTA_ON(mp)) {
+ ASSERT(mask & XFS_AT_GID);
+ ASSERT(gdqp);
+ olddquot2 = XFS_QM_DQVOPCHOWN(mp, tp, ip,
+ &ip->i_gdquot, gdqp);
+ }
+ ip->i_d.di_gid = gid;
+ }
+ if (iprojid != projid) {
+ ip->i_d.di_projid = projid;
+ /*
+ * We may have to rev the inode as well as
+ * the superblock version number since projids didn't
+ * exist before DINODE_VERSION_2 and SB_VERSION_NLINK.
+ */
+ if (ip->i_d.di_version == XFS_DINODE_VERSION_1)
+ xfs_bump_ino_vers2(tp, ip);
+ }
+
+ xfs_trans_log_inode (tp, ip, XFS_ILOG_CORE);
+ timeflags |= XFS_ICHGTIME_CHG;
+ }
+
+
+ /*
+ * Change file access or modified times.
+ */
+ if (mask & (XFS_AT_ATIME|XFS_AT_MTIME)) {
+ if (mask & XFS_AT_ATIME) {
+ ip->i_d.di_atime.t_sec = vap->va_atime.tv_sec;
+ ip->i_d.di_atime.t_nsec = vap->va_atime.tv_nsec;
+ ip->i_update_core = 1;
+ timeflags &= ~XFS_ICHGTIME_ACC;
+ }
+ if (mask & XFS_AT_MTIME) {
+ ip->i_d.di_mtime.t_sec = vap->va_mtime.tv_sec;
+ ip->i_d.di_mtime.t_nsec = vap->va_mtime.tv_nsec;
+ timeflags &= ~XFS_ICHGTIME_MOD;
+ timeflags |= XFS_ICHGTIME_CHG;
+ }
+ if (tp && (flags & ATTR_UTIME))
+ xfs_trans_log_inode (tp, ip, XFS_ILOG_CORE);
+ }
+
+ /*
+ * Change XFS-added attributes.
+ */
+ if (mask & (XFS_AT_EXTSIZE|XFS_AT_XFLAGS)) {
+ if (mask & XFS_AT_EXTSIZE) {
+ /*
+ * Converting bytes to fs blocks.
+ */
+ ip->i_d.di_extsize = vap->va_extsize >>
+ mp->m_sb.sb_blocklog;
+ }
+ if (mask & XFS_AT_XFLAGS) {
+ uint di_flags;
+
+ /* can't set PREALLOC this way, just preserve it */
+ di_flags = (ip->i_d.di_flags & XFS_DIFLAG_PREALLOC);
+ if (vap->va_xflags & XFS_XFLAG_IMMUTABLE)
+ di_flags |= XFS_DIFLAG_IMMUTABLE;
+ if (vap->va_xflags & XFS_XFLAG_APPEND)
+ di_flags |= XFS_DIFLAG_APPEND;
+ if (vap->va_xflags & XFS_XFLAG_SYNC)
+ di_flags |= XFS_DIFLAG_SYNC;
+ if (vap->va_xflags & XFS_XFLAG_NOATIME)
+ di_flags |= XFS_DIFLAG_NOATIME;
+ if (vap->va_xflags & XFS_XFLAG_NODUMP)
+ di_flags |= XFS_DIFLAG_NODUMP;
+ if ((ip->i_d.di_mode & S_IFMT) == S_IFDIR) {
+ if (vap->va_xflags & XFS_XFLAG_RTINHERIT)
+ di_flags |= XFS_DIFLAG_RTINHERIT;
+ if (vap->va_xflags & XFS_XFLAG_NOSYMLINKS)
+ di_flags |= XFS_DIFLAG_NOSYMLINKS;
+ } else {
+ if (vap->va_xflags & XFS_XFLAG_REALTIME) {
+ di_flags |= XFS_DIFLAG_REALTIME;
+ ip->i_iocore.io_flags |= XFS_IOCORE_RT;
+ } else {
+ ip->i_iocore.io_flags &= ~XFS_IOCORE_RT;
+ }
+ }
+ ip->i_d.di_flags = di_flags;
+ }
+ xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+ timeflags |= XFS_ICHGTIME_CHG;
+ }
+
+ /*
+ * Change file inode change time only if XFS_AT_CTIME set
+ * AND we have been called by a DMI function.
+ */
+
+ if ( (flags & ATTR_DMI) && (mask & XFS_AT_CTIME) ) {
+ ip->i_d.di_ctime.t_sec = vap->va_ctime.tv_sec;
+ ip->i_d.di_ctime.t_nsec = vap->va_ctime.tv_nsec;
+ ip->i_update_core = 1;
+ timeflags &= ~XFS_ICHGTIME_CHG;
+ }
+
+ /*
+ * Send out timestamp changes that need to be set to the
+ * current time. Not done when called by a DMI function.
+ */
+ if (timeflags && !(flags & ATTR_DMI))
+ xfs_ichgtime(ip, timeflags);
+
+ XFS_STATS_INC(xs_ig_attrchg);
+
+ /*
+ * If this is a synchronous mount, make sure that the
+ * transaction goes to disk before returning to the user.
+ * This is slightly sub-optimal in that truncates require
+ * two sync transactions instead of one for wsync filesytems.
+ * One for the truncate and one for the timestamps since we
+ * don't want to change the timestamps unless we're sure the
+ * truncate worked. Truncates are less than 1% of the laddis
+ * mix so this probably isn't worth the trouble to optimize.
+ */
+ code = 0;
+ if (tp) {
+ if (mp->m_flags & XFS_MOUNT_WSYNC)
+ xfs_trans_set_sync(tp);
+
+ code = xfs_trans_commit(tp, commit_flags, NULL);
+ }
+
+ /*
+ * If the (regular) file's mandatory locking mode changed, then
+ * notify the vnode. We do this under the inode lock to prevent
+ * racing calls to vop_vnode_change.
+ */
+ mandlock_after = MANDLOCK(vp, ip->i_d.di_mode);
+ if (mandlock_before != mandlock_after) {
+ VOP_VNODE_CHANGE(vp, VCHANGE_FLAGS_ENF_LOCKING,
+ mandlock_after);
+ }
+
+ xfs_iunlock(ip, lock_flags);
+
+ /*
+ * Release any dquot(s) the inode had kept before chown.
+ */
+ XFS_QM_DQRELE(mp, olddquot1);
+ XFS_QM_DQRELE(mp, olddquot2);
+ XFS_QM_DQRELE(mp, udqp);
+ XFS_QM_DQRELE(mp, gdqp);
+
+ if (code) {
+ return code;
+ }
+
+ if (DM_EVENT_ENABLED(vp->v_vfsp, ip, DM_EVENT_ATTRIBUTE) &&
+ !(flags & ATTR_DMI)) {
+ (void) XFS_SEND_NAMESP(mp, DM_EVENT_ATTRIBUTE, vp, DM_RIGHT_NULL,
+ NULL, DM_RIGHT_NULL, NULL, NULL,
+ 0, 0, AT_DELAY_FLAG(flags));
+ }
+ return 0;
+
+ abort_return:
+ commit_flags |= XFS_TRANS_ABORT;
+ /* FALLTHROUGH */
+ error_return:
+ XFS_QM_DQRELE(mp, udqp);
+ XFS_QM_DQRELE(mp, gdqp);
+ if (tp) {
+ xfs_trans_cancel(tp, commit_flags);
+ }
+ if (lock_flags != 0) {
+ xfs_iunlock(ip, lock_flags);
+ }
+ return code;
+}
+
+
+/*
+ * xfs_access
+ * Null conversion from vnode mode bits to inode mode bits, as in efs.
+ */
+STATIC int
+xfs_access(
+ bhv_desc_t *bdp,
+ int mode,
+ cred_t *credp)
+{
+ xfs_inode_t *ip;
+ int error;
+
+ vn_trace_entry(BHV_TO_VNODE(bdp), __FUNCTION__,
+ (inst_t *)__return_address);
+
+ ip = XFS_BHVTOI(bdp);
+ xfs_ilock(ip, XFS_ILOCK_SHARED);
+ error = xfs_iaccess(ip, mode, credp);
+ xfs_iunlock(ip, XFS_ILOCK_SHARED);
+ return error;
+}
+
+
+/*
+ * xfs_readlink
+ *
+ */
+STATIC int
+xfs_readlink(
+ bhv_desc_t *bdp,
+ uio_t *uiop,
+ int ioflags,
+ cred_t *credp)
+{
+ xfs_inode_t *ip;
+ int count;
+ xfs_off_t offset;
+ int pathlen;
+ vnode_t *vp;
+ int error = 0;
+ xfs_mount_t *mp;
+ int nmaps;
+ xfs_bmbt_irec_t mval[SYMLINK_MAPS];
+ xfs_daddr_t d;
+ int byte_cnt;
+ int n;
+ xfs_buf_t *bp;
+
+ vp = BHV_TO_VNODE(bdp);
+ vn_trace_entry(vp, __FUNCTION__, (inst_t *)__return_address);
+
+ ip = XFS_BHVTOI(bdp);
+ mp = ip->i_mount;
+
+ if (XFS_FORCED_SHUTDOWN(mp))
+ return XFS_ERROR(EIO);
+
+ xfs_ilock(ip, XFS_ILOCK_SHARED);
+
+ ASSERT((ip->i_d.di_mode & S_IFMT) == S_IFLNK);
+
+ offset = uiop->uio_offset;
+ count = uiop->uio_resid;
+
+ if (offset < 0) {
+ error = XFS_ERROR(EINVAL);
+ goto error_return;
+ }
+ if (count <= 0) {
+ error = 0;
+ goto error_return;
+ }
+
+ if (!(ioflags & IO_INVIS)) {
+ xfs_ichgtime(ip, XFS_ICHGTIME_ACC);
+ }
+
+ /*
+ * See if the symlink is stored inline.
+ */
+ pathlen = (int)ip->i_d.di_size;
+
+ if (ip->i_df.if_flags & XFS_IFINLINE) {
+ error = uio_read(ip->i_df.if_u1.if_data, pathlen, uiop);
+ }
+ else {
+ /*
+ * Symlink not inline. Call bmap to get it in.
+ */
+ nmaps = SYMLINK_MAPS;
+
+ error = xfs_bmapi(NULL, ip, 0, XFS_B_TO_FSB(mp, pathlen),
+ 0, NULL, 0, mval, &nmaps, NULL);
+
+ if (error) {
+ goto error_return;
+ }
+
+ for (n = 0; n < nmaps; n++) {
+ d = XFS_FSB_TO_DADDR(mp, mval[n].br_startblock);
+ byte_cnt = XFS_FSB_TO_B(mp, mval[n].br_blockcount);
+ bp = xfs_buf_read(mp->m_ddev_targp, d,
+ BTOBB(byte_cnt), 0);
+ error = XFS_BUF_GETERROR(bp);
+ if (error) {
+ xfs_ioerror_alert("xfs_readlink",
+ ip->i_mount, bp, XFS_BUF_ADDR(bp));
+ xfs_buf_relse(bp);
+ goto error_return;
+ }
+ if (pathlen < byte_cnt)
+ byte_cnt = pathlen;
+ pathlen -= byte_cnt;
+
+ error = uio_read(XFS_BUF_PTR(bp), byte_cnt, uiop);
+ xfs_buf_relse (bp);
+ }
+
+ }
+
+
+error_return:
+
+ xfs_iunlock(ip, XFS_ILOCK_SHARED);
+
+ return error;
+}
+
+
+/*
+ * xfs_fsync
+ *
+ * This is called to sync the inode and its data out to disk.
+ * We need to hold the I/O lock while flushing the data, and
+ * the inode lock while flushing the inode. The inode lock CANNOT
+ * be held while flushing the data, so acquire after we're done
+ * with that.
+ */
+STATIC int
+xfs_fsync(
+ bhv_desc_t *bdp,
+ int flag,
+ cred_t *credp,
+ xfs_off_t start,
+ xfs_off_t stop)
+{
+ xfs_inode_t *ip;
+ xfs_trans_t *tp;
+ int error;
+
+ vn_trace_entry(BHV_TO_VNODE(bdp),
+ __FUNCTION__, (inst_t *)__return_address);
+
+ ip = XFS_BHVTOI(bdp);
+
+ ASSERT(start >= 0 && stop >= -1);
+
+ if (XFS_FORCED_SHUTDOWN(ip->i_mount))
+ return XFS_ERROR(EIO);
+
+ /*
+ * We always need to make sure that the required inode state
+ * is safe on disk. The vnode might be clean but because
+ * of committed transactions that haven't hit the disk yet.
+ * Likewise, there could be unflushed non-transactional
+ * changes to the inode core that have to go to disk.
+ *
+ * The following code depends on one assumption: that
+ * any transaction that changes an inode logs the core
+ * because it has to change some field in the inode core
+ * (typically nextents or nblocks). That assumption
+ * implies that any transactions against an inode will
+ * catch any non-transactional updates. If inode-altering
+ * transactions exist that violate this assumption, the
+ * code breaks. Right now, it figures that if the involved
+ * update_* field is clear and the inode is unpinned, the
+ * inode is clean. Either it's been flushed or it's been
+ * committed and the commit has hit the disk unpinning the inode.
+ * (Note that xfs_inode_item_format() called at commit clears
+ * the update_* fields.)
+ */
+ xfs_ilock(ip, XFS_ILOCK_SHARED);
+
+ /* If we are flushing data then we care about update_size
+ * being set, otherwise we care about update_core
+ */
+ if ((flag & FSYNC_DATA) ?
+ (ip->i_update_size == 0) :
+ (ip->i_update_core == 0)) {
+ /*
+ * Timestamps/size haven't changed since last inode
+ * flush or inode transaction commit. That means
+ * either nothing got written or a transaction
+ * committed which caught the updates. If the
+ * latter happened and the transaction hasn't
+ * hit the disk yet, the inode will be still
+ * be pinned. If it is, force the log.
+ */
+
+ xfs_iunlock(ip, XFS_ILOCK_SHARED);
+
+ if (xfs_ipincount(ip)) {
+ xfs_log_force(ip->i_mount, (xfs_lsn_t)0,
+ XFS_LOG_FORCE |
+ ((flag & FSYNC_WAIT)
+ ? XFS_LOG_SYNC : 0));
+ }
+ error = 0;
+ } else {
+ /*
+ * Kick off a transaction to log the inode
+ * core to get the updates. Make it
+ * sync if FSYNC_WAIT is passed in (which
+ * is done by everybody but specfs). The
+ * sync transaction will also force the log.
+ */
+ xfs_iunlock(ip, XFS_ILOCK_SHARED);
+ tp = xfs_trans_alloc(ip->i_mount, XFS_TRANS_FSYNC_TS);
+ if ((error = xfs_trans_reserve(tp, 0,
+ XFS_FSYNC_TS_LOG_RES(ip->i_mount),
+ 0, 0, 0))) {
+ xfs_trans_cancel(tp, 0);
+ return error;
+ }
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
+
+ /*
+ * Note - it's possible that we might have pushed
+ * ourselves out of the way during trans_reserve
+ * which would flush the inode. But there's no
+ * guarantee that the inode buffer has actually
+ * gone out yet (it's delwri). Plus the buffer
+ * could be pinned anyway if it's part of an
+ * inode in another recent transaction. So we
+ * play it safe and fire off the transaction anyway.
+ */
+ xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
+ xfs_trans_ihold(tp, ip);
+ xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+ if (flag & FSYNC_WAIT)
+ xfs_trans_set_sync(tp);
+ error = xfs_trans_commit(tp, 0, NULL);
+
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ }
+ return error;
+}
+
+/*
+ * This is called by xfs_inactive to free any blocks beyond eof,
+ * when the link count isn't zero.
+ */
+STATIC int
+xfs_inactive_free_eofblocks(
+ xfs_mount_t *mp,
+ xfs_inode_t *ip)
+{
+ xfs_trans_t *tp;
+ int error;
+ xfs_fileoff_t end_fsb;
+ xfs_fileoff_t last_fsb;
+ xfs_filblks_t map_len;
+ int nimaps;
+ xfs_bmbt_irec_t imap;
+
+ /*
+ * Figure out if there are any blocks beyond the end
+ * of the file. If not, then there is nothing to do.
+ */
+ end_fsb = XFS_B_TO_FSB(mp, ((xfs_ufsize_t)ip->i_d.di_size));
+ last_fsb = XFS_B_TO_FSB(mp, (xfs_ufsize_t)XFS_MAXIOFFSET(mp));
+ map_len = last_fsb - end_fsb;
+ if (map_len <= 0)
+ return (0);
+
+ nimaps = 1;
+ xfs_ilock(ip, XFS_ILOCK_SHARED);
+ error = xfs_bmapi(NULL, ip, end_fsb, map_len, 0,
+ NULL, 0, &imap, &nimaps, NULL);
+ xfs_iunlock(ip, XFS_ILOCK_SHARED);
+
+ if (!error && (nimaps != 0) &&
+ (imap.br_startblock != HOLESTARTBLOCK)) {
+ /*
+ * Attach the dquots to the inode up front.
+ */
+ if ((error = XFS_QM_DQATTACH(mp, ip, 0)))
+ return (error);
+
+ /*
+ * There are blocks after the end of file.
+ * Free them up now by truncating the file to
+ * its current size.
+ */
+ tp = xfs_trans_alloc(mp, XFS_TRANS_INACTIVE);
+
+ /*
+ * Do the xfs_itruncate_start() call before
+ * reserving any log space because
+ * itruncate_start will call into the buffer
+ * cache and we can't
+ * do that within a transaction.
+ */
+ xfs_ilock(ip, XFS_IOLOCK_EXCL);
+ xfs_itruncate_start(ip, XFS_ITRUNC_DEFINITE,
+ ip->i_d.di_size);
+
+ error = xfs_trans_reserve(tp, 0,
+ XFS_ITRUNCATE_LOG_RES(mp),
+ 0, XFS_TRANS_PERM_LOG_RES,
+ XFS_ITRUNCATE_LOG_COUNT);
+ if (error) {
+ ASSERT(XFS_FORCED_SHUTDOWN(mp));
+ xfs_trans_cancel(tp, 0);
+ xfs_iunlock(ip, XFS_IOLOCK_EXCL);
+ return (error);
+ }
+
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
+ xfs_trans_ijoin(tp, ip,
+ XFS_IOLOCK_EXCL |
+ XFS_ILOCK_EXCL);
+ xfs_trans_ihold(tp, ip);
+
+ error = xfs_itruncate_finish(&tp, ip,
+ ip->i_d.di_size,
+ XFS_DATA_FORK,
+ 0);
+ /*
+ * If we get an error at this point we
+ * simply don't bother truncating the file.
+ */
+ if (error) {
+ xfs_trans_cancel(tp,
+ (XFS_TRANS_RELEASE_LOG_RES |
+ XFS_TRANS_ABORT));
+ } else {
+ error = xfs_trans_commit(tp,
+ XFS_TRANS_RELEASE_LOG_RES,
+ NULL);
+ }
+ xfs_iunlock(ip, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL);
+ }
+ return (error);
+}
+
+/*
+ * Free a symlink that has blocks associated with it.
+ */
+STATIC int
+xfs_inactive_symlink_rmt(
+ xfs_inode_t *ip,
+ xfs_trans_t **tpp)
+{
+ xfs_buf_t *bp;
+ int committed;
+ int done;
+ int error;
+ xfs_fsblock_t first_block;
+ xfs_bmap_free_t free_list;
+ int i;
+ xfs_mount_t *mp;
+ xfs_bmbt_irec_t mval[SYMLINK_MAPS];
+ int nmaps;
+ xfs_trans_t *ntp;
+ int size;
+ xfs_trans_t *tp;
+
+ tp = *tpp;
+ mp = ip->i_mount;
+ ASSERT(ip->i_d.di_size > XFS_IFORK_DSIZE(ip));
+ /*
+ * We're freeing a symlink that has some
+ * blocks allocated to it. Free the
+ * blocks here. We know that we've got
+ * either 1 or 2 extents and that we can
+ * free them all in one bunmapi call.
+ */
+ ASSERT(ip->i_d.di_nextents > 0 && ip->i_d.di_nextents <= 2);
+ if ((error = xfs_trans_reserve(tp, 0, XFS_ITRUNCATE_LOG_RES(mp), 0,
+ XFS_TRANS_PERM_LOG_RES, XFS_ITRUNCATE_LOG_COUNT))) {
+ ASSERT(XFS_FORCED_SHUTDOWN(mp));
+ xfs_trans_cancel(tp, 0);
+ *tpp = NULL;
+ return error;
+ }
+ /*
+ * Lock the inode, fix the size, and join it to the transaction.
+ * Hold it so in the normal path, we still have it locked for
+ * the second transaction. In the error paths we need it
+ * held so the cancel won't rele it, see below.
+ */
+ xfs_ilock(ip, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL);
+ size = (int)ip->i_d.di_size;
+ ip->i_d.di_size = 0;
+ xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL);
+ xfs_trans_ihold(tp, ip);
+ xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+ /*
+ * Find the block(s) so we can inval and unmap them.
+ */
+ done = 0;
+ XFS_BMAP_INIT(&free_list, &first_block);
+ nmaps = sizeof(mval) / sizeof(mval[0]);
+ if ((error = xfs_bmapi(tp, ip, 0, XFS_B_TO_FSB(mp, size),
+ XFS_BMAPI_METADATA, &first_block, 0, mval, &nmaps,
+ &free_list)))
+ goto error0;
+ /*
+ * Invalidate the block(s).
+ */
+ for (i = 0; i < nmaps; i++) {
+ bp = xfs_trans_get_buf(tp, mp->m_ddev_targp,
+ XFS_FSB_TO_DADDR(mp, mval[i].br_startblock),
+ XFS_FSB_TO_BB(mp, mval[i].br_blockcount), 0);
+ xfs_trans_binval(tp, bp);
+ }
+ /*
+ * Unmap the dead block(s) to the free_list.
+ */
+ if ((error = xfs_bunmapi(tp, ip, 0, size, XFS_BMAPI_METADATA, nmaps,
+ &first_block, &free_list, &done)))
+ goto error1;
+ ASSERT(done);
+ /*
+ * Commit the first transaction. This logs the EFI and the inode.
+ */
+ if ((error = xfs_bmap_finish(&tp, &free_list, first_block, &committed)))
+ goto error1;
+ /*
+ * The transaction must have been committed, since there were
+ * actually extents freed by xfs_bunmapi. See xfs_bmap_finish.
+ * The new tp has the extent freeing and EFDs.
+ */
+ ASSERT(committed);
+ /*
+ * The first xact was committed, so add the inode to the new one.
+ * Mark it dirty so it will be logged and moved forward in the log as
+ * part of every commit.
+ */
+ xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL);
+ xfs_trans_ihold(tp, ip);
+ xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+ /*
+ * Get a new, empty transaction to return to our caller.
+ */
+ ntp = xfs_trans_dup(tp);
+ /*
+ * Commit the transaction containing extent freeing and EFD's.
+ * If we get an error on the commit here or on the reserve below,
+ * we need to unlock the inode since the new transaction doesn't
+ * have the inode attached.
+ */
+ error = xfs_trans_commit(tp, 0, NULL);
+ tp = ntp;
+ if (error) {
+ ASSERT(XFS_FORCED_SHUTDOWN(mp));
+ goto error0;
+ }
+ /*
+ * Remove the memory for extent descriptions (just bookkeeping).
+ */
+ if (ip->i_df.if_bytes)
+ xfs_idata_realloc(ip, -ip->i_df.if_bytes, XFS_DATA_FORK);
+ ASSERT(ip->i_df.if_bytes == 0);
+ /*
+ * Put an itruncate log reservation in the new transaction
+ * for our caller.
+ */
+ if ((error = xfs_trans_reserve(tp, 0, XFS_ITRUNCATE_LOG_RES(mp), 0,
+ XFS_TRANS_PERM_LOG_RES, XFS_ITRUNCATE_LOG_COUNT))) {
+ ASSERT(XFS_FORCED_SHUTDOWN(mp));
+ goto error0;
+ }
+ /*
+ * Return with the inode locked but not joined to the transaction.
+ */
+ *tpp = tp;
+ return 0;
+
+ error1:
+ xfs_bmap_cancel(&free_list);
+ error0:
+ /*
+ * Have to come here with the inode locked and either
+ * (held and in the transaction) or (not in the transaction).
+ * If the inode isn't held then cancel would iput it, but
+ * that's wrong since this is inactive and the vnode ref
+ * count is 0 already.
+ * Cancel won't do anything to the inode if held, but it still
+ * needs to be locked until the cancel is done, if it was
+ * joined to the transaction.
+ */
+ xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT);
+ xfs_iunlock(ip, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL);
+ *tpp = NULL;
+ return error;
+
+}
+
+STATIC int
+xfs_inactive_symlink_local(
+ xfs_inode_t *ip,
+ xfs_trans_t **tpp)
+{
+ int error;
+
+ ASSERT(ip->i_d.di_size <= XFS_IFORK_DSIZE(ip));
+ /*
+ * We're freeing a symlink which fit into
+ * the inode. Just free the memory used
+ * to hold the old symlink.
+ */
+ error = xfs_trans_reserve(*tpp, 0,
+ XFS_ITRUNCATE_LOG_RES(ip->i_mount),
+ 0, XFS_TRANS_PERM_LOG_RES,
+ XFS_ITRUNCATE_LOG_COUNT);
+
+ if (error) {
+ xfs_trans_cancel(*tpp, 0);
+ *tpp = NULL;
+ return (error);
+ }
+ xfs_ilock(ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL);
+
+ /*
+ * Zero length symlinks _can_ exist.
+ */
+ if (ip->i_df.if_bytes > 0) {
+ xfs_idata_realloc(ip,
+ -(ip->i_df.if_bytes),
+ XFS_DATA_FORK);
+ ASSERT(ip->i_df.if_bytes == 0);
+ }
+ return (0);
+}
+
+/*
+ *
+ */
+STATIC int
+xfs_inactive_attrs(
+ xfs_inode_t *ip,
+ xfs_trans_t **tpp)
+{
+ xfs_trans_t *tp;
+ int error;
+ xfs_mount_t *mp;
+
+ ASSERT(ismrlocked(&ip->i_iolock, MR_UPDATE));
+ tp = *tpp;
+ mp = ip->i_mount;
+ ASSERT(ip->i_d.di_forkoff != 0);
+ xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES, NULL);
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+
+ error = xfs_attr_inactive(ip);
+ if (error) {
+ *tpp = NULL;
+ xfs_iunlock(ip, XFS_IOLOCK_EXCL);
+ return (error); /* goto out*/
+ }
+
+ tp = xfs_trans_alloc(mp, XFS_TRANS_INACTIVE);
+ error = xfs_trans_reserve(tp, 0,
+ XFS_IFREE_LOG_RES(mp),
+ 0, XFS_TRANS_PERM_LOG_RES,
+ XFS_INACTIVE_LOG_COUNT);
+ if (error) {
+ ASSERT(XFS_FORCED_SHUTDOWN(mp));
+ xfs_trans_cancel(tp, 0);
+ *tpp = NULL;
+ xfs_iunlock(ip, XFS_IOLOCK_EXCL);
+ return (error);
+ }
+
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
+ xfs_trans_ijoin(tp, ip, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL);
+ xfs_trans_ihold(tp, ip);
+ xfs_idestroy_fork(ip, XFS_ATTR_FORK);
+
+ ASSERT(ip->i_d.di_anextents == 0);
+
+ *tpp = tp;
+ return (0);
+}
+
+STATIC int
+xfs_release(
+ bhv_desc_t *bdp)
+{
+ xfs_inode_t *ip;
+ vnode_t *vp;
+ xfs_mount_t *mp;
+ int error;
+
+ vp = BHV_TO_VNODE(bdp);
+ ip = XFS_BHVTOI(bdp);
+
+ if ((vp->v_type != VREG) || (ip->i_d.di_mode == 0)) {
+ return 0;
+ }
+
+ /* If this is a read-only mount, don't do this (would generate I/O) */
+ if (vp->v_vfsp->vfs_flag & VFS_RDONLY)
+ return 0;
+
+#ifdef HAVE_REFCACHE
+ /* If we are in the NFS reference cache then don't do this now */
+ if (ip->i_refcache)
+ return 0;
+#endif
+
+ mp = ip->i_mount;
+
+ if (ip->i_d.di_nlink != 0) {
+ if ((((ip->i_d.di_mode & S_IFMT) == S_IFREG) &&
+ ((ip->i_d.di_size > 0) || (VN_CACHED(vp) > 0)) &&
+ (ip->i_df.if_flags & XFS_IFEXTENTS)) &&
+ (!(ip->i_d.di_flags & (XFS_DIFLAG_PREALLOC|XFS_DIFLAG_APPEND)))) {
+ if ((error = xfs_inactive_free_eofblocks(mp, ip)))
+ return (error);
+ /* Update linux inode block count after free above */
+ LINVFS_GET_IP(vp)->i_blocks = XFS_FSB_TO_BB(mp,
+ ip->i_d.di_nblocks + ip->i_delayed_blks);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * xfs_inactive
+ *
+ * This is called when the vnode reference count for the vnode
+ * goes to zero. If the file has been unlinked, then it must
+ * now be truncated. Also, we clear all of the read-ahead state
+ * kept for the inode here since the file is now closed.
+ */
+STATIC int
+xfs_inactive(
+ bhv_desc_t *bdp,
+ cred_t *credp)
+{
+ xfs_inode_t *ip;
+ vnode_t *vp;
+ xfs_bmap_free_t free_list;
+ xfs_fsblock_t first_block;
+ int committed;
+ xfs_trans_t *tp;
+ xfs_mount_t *mp;
+ int error;
+ int truncate;
+
+ vp = BHV_TO_VNODE(bdp);
+ vn_trace_entry(vp, __FUNCTION__, (inst_t *)__return_address);
+
+ ip = XFS_BHVTOI(bdp);
+
+ /*
+ * If the inode is already free, then there can be nothing
+ * to clean up here.
+ */
+ if (ip->i_d.di_mode == 0 || VN_BAD(vp)) {
+ ASSERT(ip->i_df.if_real_bytes == 0);
+ ASSERT(ip->i_df.if_broot_bytes == 0);
+ return VN_INACTIVE_CACHE;
+ }
+
+ /*
+ * Only do a truncate if it's a regular file with
+ * some actual space in it. It's OK to look at the
+ * inode's fields without the lock because we're the
+ * only one with a reference to the inode.
+ */
+ truncate = ((ip->i_d.di_nlink == 0) &&
+ ((ip->i_d.di_size != 0) || (ip->i_d.di_nextents > 0)) &&
+ ((ip->i_d.di_mode & S_IFMT) == S_IFREG));
+
+ mp = ip->i_mount;
+
+ if (ip->i_d.di_nlink == 0 &&
+ DM_EVENT_ENABLED(vp->v_vfsp, ip, DM_EVENT_DESTROY)) {
+ (void) XFS_SEND_DESTROY(mp, vp, DM_RIGHT_NULL);
+ }
+
+ error = 0;
+
+ /* If this is a read-only mount, don't do this (would generate I/O) */
+ if (vp->v_vfsp->vfs_flag & VFS_RDONLY)
+ goto out;
+
+ if (ip->i_d.di_nlink != 0) {
+ if ((((ip->i_d.di_mode & S_IFMT) == S_IFREG) &&
+ ((ip->i_d.di_size > 0) || (VN_CACHED(vp) > 0)) &&
+ (ip->i_df.if_flags & XFS_IFEXTENTS)) &&
+ (!(ip->i_d.di_flags & (XFS_DIFLAG_PREALLOC|XFS_DIFLAG_APPEND)) ||
+ (ip->i_delayed_blks != 0))) {
+ if ((error = xfs_inactive_free_eofblocks(mp, ip)))
+ return (VN_INACTIVE_CACHE);
+ /* Update linux inode block count after free above */
+ LINVFS_GET_IP(vp)->i_blocks = XFS_FSB_TO_BB(mp,
+ ip->i_d.di_nblocks + ip->i_delayed_blks);
+ }
+ goto out;
+ }
+
+ ASSERT(ip->i_d.di_nlink == 0);
+
+ if ((error = XFS_QM_DQATTACH(mp, ip, 0)))
+ return (VN_INACTIVE_CACHE);
+
+ tp = xfs_trans_alloc(mp, XFS_TRANS_INACTIVE);
+ if (truncate) {
+ /*
+ * Do the xfs_itruncate_start() call before
+ * reserving any log space because itruncate_start
+ * will call into the buffer cache and we can't
+ * do that within a transaction.
+ */
+ xfs_ilock(ip, XFS_IOLOCK_EXCL);
+
+ xfs_itruncate_start(ip, XFS_ITRUNC_DEFINITE, 0);
+
+ error = xfs_trans_reserve(tp, 0,
+ XFS_ITRUNCATE_LOG_RES(mp),
+ 0, XFS_TRANS_PERM_LOG_RES,
+ XFS_ITRUNCATE_LOG_COUNT);
+ if (error) {
+ /* Don't call itruncate_cleanup */
+ ASSERT(XFS_FORCED_SHUTDOWN(mp));
+ xfs_trans_cancel(tp, 0);
+ xfs_iunlock(ip, XFS_IOLOCK_EXCL);
+ return (VN_INACTIVE_CACHE);
+ }
+
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
+ xfs_trans_ijoin(tp, ip, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL);
+ xfs_trans_ihold(tp, ip);
+
+ /*
+ * normally, we have to run xfs_itruncate_finish sync.
+ * But if filesystem is wsync and we're in the inactive
+ * path, then we know that nlink == 0, and that the
+ * xaction that made nlink == 0 is permanently committed
+ * since xfs_remove runs as a synchronous transaction.
+ */
+ error = xfs_itruncate_finish(&tp, ip, 0, XFS_DATA_FORK,
+ (!(mp->m_flags & XFS_MOUNT_WSYNC) ? 1 : 0));
+
+ if (error) {
+ xfs_trans_cancel(tp,
+ XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT);
+ xfs_iunlock(ip, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL);
+ return (VN_INACTIVE_CACHE);
+ }
+ } else if ((ip->i_d.di_mode & S_IFMT) == S_IFLNK) {
+
+ /*
+ * If we get an error while cleaning up a
+ * symlink we bail out.
+ */
+ error = (ip->i_d.di_size > XFS_IFORK_DSIZE(ip)) ?
+ xfs_inactive_symlink_rmt(ip, &tp) :
+ xfs_inactive_symlink_local(ip, &tp);
+
+ if (error) {
+ ASSERT(tp == NULL);
+ return (VN_INACTIVE_CACHE);
+ }
+
+ xfs_trans_ijoin(tp, ip, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL);
+ xfs_trans_ihold(tp, ip);
+ } else {
+ error = xfs_trans_reserve(tp, 0,
+ XFS_IFREE_LOG_RES(mp),
+ 0, XFS_TRANS_PERM_LOG_RES,
+ XFS_INACTIVE_LOG_COUNT);
+ if (error) {
+ ASSERT(XFS_FORCED_SHUTDOWN(mp));
+ xfs_trans_cancel(tp, 0);
+ return (VN_INACTIVE_CACHE);
+ }
+
+ xfs_ilock(ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL);
+ xfs_trans_ijoin(tp, ip, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL);
+ xfs_trans_ihold(tp, ip);
+ }
+
+ /*
+ * If there are attributes associated with the file
+ * then blow them away now. The code calls a routine
+ * that recursively deconstructs the attribute fork.
+ * We need to just commit the current transaction
+ * because we can't use it for xfs_attr_inactive().
+ */
+ if (ip->i_d.di_anextents > 0) {
+ error = xfs_inactive_attrs(ip, &tp);
+ /*
+ * If we got an error, the transaction is already
+ * cancelled, and the inode is unlocked. Just get out.
+ */
+ if (error)
+ return (VN_INACTIVE_CACHE);
+ } else if (ip->i_afp) {
+ xfs_idestroy_fork(ip, XFS_ATTR_FORK);
+ }
+
+ /*
+ * Free the inode.
+ */
+ XFS_BMAP_INIT(&free_list, &first_block);
+ error = xfs_ifree(tp, ip, &free_list);
+ if (error) {
+ /*
+ * If we fail to free the inode, shut down. The cancel
+ * might do that, we need to make sure. Otherwise the
+ * inode might be lost for a long time or forever.
+ */
+ if (!XFS_FORCED_SHUTDOWN(mp)) {
+ cmn_err(CE_NOTE,
+ "xfs_inactive: xfs_ifree() returned an error = %d on %s",
+ error, mp->m_fsname);
+ xfs_force_shutdown(mp, XFS_METADATA_IO_ERROR);
+ }
+ xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_ABORT);
+ } else {
+ /*
+ * Credit the quota account(s). The inode is gone.
+ */
+ XFS_TRANS_MOD_DQUOT_BYINO(mp, tp, ip, XFS_TRANS_DQ_ICOUNT, -1);
+
+ /*
+ * Just ignore errors at this point. There is
+ * nothing we can do except to try to keep going.
+ */
+ (void) xfs_bmap_finish(&tp, &free_list, first_block,
+ &committed);
+ (void) xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES, NULL);
+ }
+ /*
+ * Release the dquots held by inode, if any.
+ */
+ XFS_QM_DQDETACH(mp, ip);
+
+ xfs_iunlock(ip, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL);
+
+ out:
+ return VN_INACTIVE_CACHE;
+}
+
+
+/*
+ * xfs_lookup
+ */
+STATIC int
+xfs_lookup(
+ bhv_desc_t *dir_bdp,
+ vname_t *dentry,
+ vnode_t **vpp,
+ int flags,
+ vnode_t *rdir,
+ cred_t *credp)
+{
+ xfs_inode_t *dp, *ip;
+ xfs_ino_t e_inum;
+ int error;
+ uint lock_mode;
+ vnode_t *dir_vp;
+
+ dir_vp = BHV_TO_VNODE(dir_bdp);
+ vn_trace_entry(dir_vp, __FUNCTION__, (inst_t *)__return_address);
+
+ dp = XFS_BHVTOI(dir_bdp);
+
+ if (XFS_FORCED_SHUTDOWN(dp->i_mount))
+ return XFS_ERROR(EIO);
+
+ lock_mode = xfs_ilock_map_shared(dp);
+ error = xfs_dir_lookup_int(dir_bdp, lock_mode, dentry, &e_inum, &ip);
+ if (!error) {
+ *vpp = XFS_ITOV(ip);
+ ITRACE(ip);
+ }
+ xfs_iunlock_map_shared(dp, lock_mode);
+ return error;
+}
+
+
+/*
+ * xfs_create (create a new file).
+ */
+STATIC int
+xfs_create(
+ bhv_desc_t *dir_bdp,
+ vname_t *dentry,
+ vattr_t *vap,
+ vnode_t **vpp,
+ cred_t *credp)
+{
+ char *name = VNAME(dentry);
+ vnode_t *dir_vp;
+ xfs_inode_t *dp, *ip;
+ vnode_t *vp=NULL;
+ xfs_trans_t *tp;
+ xfs_mount_t *mp;
+ xfs_dev_t rdev;
+ int error;
+ xfs_bmap_free_t free_list;
+ xfs_fsblock_t first_block;
+ boolean_t dp_joined_to_trans;
+ int dm_event_sent = 0;
+ uint cancel_flags;
+ int committed;
+ xfs_prid_t prid;
+ struct xfs_dquot *udqp, *gdqp;
+ uint resblks;
+ int dm_di_mode;
+ int namelen;
+
+ ASSERT(!*vpp);
+ dir_vp = BHV_TO_VNODE(dir_bdp);
+ vn_trace_entry(dir_vp, __FUNCTION__, (inst_t *)__return_address);
+
+ dp = XFS_BHVTOI(dir_bdp);
+ mp = dp->i_mount;
+
+ dm_di_mode = vap->va_mode|VTTOIF(vap->va_type);
+ namelen = VNAMELEN(dentry);
+
+ if (DM_EVENT_ENABLED(dir_vp->v_vfsp, dp, DM_EVENT_CREATE)) {
+ error = XFS_SEND_NAMESP(mp, DM_EVENT_CREATE,
+ dir_vp, DM_RIGHT_NULL, NULL,
+ DM_RIGHT_NULL, name, NULL,
+ dm_di_mode, 0, 0);
+
+ if (error)
+ return error;
+ dm_event_sent = 1;
+ }
+
+ if (XFS_FORCED_SHUTDOWN(mp))
+ return XFS_ERROR(EIO);
+
+ /* Return through std_return after this point. */
+
+ udqp = gdqp = NULL;
+ if (vap->va_mask & XFS_AT_PROJID)
+ prid = (xfs_prid_t)vap->va_projid;
+ else
+ prid = (xfs_prid_t)dfltprid;
+
+ /*
+ * Make sure that we have allocated dquot(s) on disk.
+ */
+ error = XFS_QM_DQVOPALLOC(mp, dp,
+ current_fsuid(credp), current_fsgid(credp),
+ XFS_QMOPT_QUOTALL|XFS_QMOPT_INHERIT, &udqp, &gdqp);
+ if (error)
+ goto std_return;
+
+ ip = NULL;
+ dp_joined_to_trans = B_FALSE;
+
+ tp = xfs_trans_alloc(mp, XFS_TRANS_CREATE);
+ cancel_flags = XFS_TRANS_RELEASE_LOG_RES;
+ resblks = XFS_CREATE_SPACE_RES(mp, namelen);
+ /*
+ * Initially assume that the file does not exist and
+ * reserve the resources for that case. If that is not
+ * the case we'll drop the one we have and get a more
+ * appropriate transaction later.
+ */
+ error = xfs_trans_reserve(tp, resblks, XFS_CREATE_LOG_RES(mp), 0,
+ XFS_TRANS_PERM_LOG_RES, XFS_CREATE_LOG_COUNT);
+ if (error == ENOSPC) {
+ resblks = 0;
+ error = xfs_trans_reserve(tp, 0, XFS_CREATE_LOG_RES(mp), 0,
+ XFS_TRANS_PERM_LOG_RES, XFS_CREATE_LOG_COUNT);
+ }
+ if (error) {
+ cancel_flags = 0;
+ dp = NULL;
+ goto error_return;
+ }
+
+ xfs_ilock(dp, XFS_ILOCK_EXCL);
+
+ XFS_BMAP_INIT(&free_list, &first_block);
+
+ ASSERT(ip == NULL);
+
+ /*
+ * Reserve disk quota and the inode.
+ */
+ error = XFS_TRANS_RESERVE_QUOTA(mp, tp, udqp, gdqp, resblks, 1, 0);
+ if (error)
+ goto error_return;
+
+ if (resblks == 0 &&
+ (error = XFS_DIR_CANENTER(mp, tp, dp, name, namelen)))
+ goto error_return;
+ rdev = (vap->va_mask & XFS_AT_RDEV) ? vap->va_rdev : 0;
+ error = xfs_dir_ialloc(&tp, dp,
+ MAKEIMODE(vap->va_type,vap->va_mode), 1,
+ rdev, credp, prid, resblks > 0,
+ &ip, &committed);
+ if (error) {
+ if (error == ENOSPC)
+ goto error_return;
+ goto abort_return;
+ }
+ ITRACE(ip);
+
+ /*
+ * At this point, we've gotten a newly allocated inode.
+ * It is locked (and joined to the transaction).
+ */
+
+ ASSERT(ismrlocked (&ip->i_lock, MR_UPDATE));
+
+ /*
+ * Now we join the directory inode to the transaction.
+ * We do not do it earlier because xfs_dir_ialloc
+ * might commit the previous transaction (and release
+ * all the locks).
+ */
+
+ VN_HOLD(dir_vp);
+ xfs_trans_ijoin(tp, dp, XFS_ILOCK_EXCL);
+ dp_joined_to_trans = B_TRUE;
+
+ error = XFS_DIR_CREATENAME(mp, tp, dp, name, namelen, ip->i_ino,
+ &first_block, &free_list,
+ resblks ? resblks - XFS_IALLOC_SPACE_RES(mp) : 0);
+ if (error) {
+ ASSERT(error != ENOSPC);
+ goto abort_return;
+ }
+ xfs_ichgtime(dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
+ xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE);
+
+ /*
+ * If this is a synchronous mount, make sure that the
+ * create transaction goes to disk before returning to
+ * the user.
+ */
+ if (mp->m_flags & XFS_MOUNT_WSYNC) {
+ xfs_trans_set_sync(tp);
+ }
+
+ dp->i_gen++;
+
+ /*
+ * Attach the dquot(s) to the inodes and modify them incore.
+ * These ids of the inode couldn't have changed since the new
+ * inode has been locked ever since it was created.
+ */
+ XFS_QM_DQVOPCREATE(mp, tp, ip, udqp, gdqp);
+
+ /*
+ * xfs_trans_commit normally decrements the vnode ref count
+ * when it unlocks the inode. Since we want to return the
+ * vnode to the caller, we bump the vnode ref count now.
+ */
+ IHOLD(ip);
+ vp = XFS_ITOV(ip);
+
+ error = xfs_bmap_finish(&tp, &free_list, first_block, &committed);
+ if (error) {
+ xfs_bmap_cancel(&free_list);
+ goto abort_rele;
+ }
+
+ error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES, NULL);
+ if (error) {
+ IRELE(ip);
+ tp = NULL;
+ goto error_return;
+ }
+
+ XFS_QM_DQRELE(mp, udqp);
+ XFS_QM_DQRELE(mp, gdqp);
+
+ /*
+ * Propogate the fact that the vnode changed after the
+ * xfs_inode locks have been released.
+ */
+ VOP_VNODE_CHANGE(vp, VCHANGE_FLAGS_TRUNCATED, 3);
+
+ *vpp = vp;
+
+ /* Fallthrough to std_return with error = 0 */
+
+std_return:
+ if ( (*vpp || (error != 0 && dm_event_sent != 0)) &&
+ DM_EVENT_ENABLED(dir_vp->v_vfsp, XFS_BHVTOI(dir_bdp),
+ DM_EVENT_POSTCREATE)) {
+ (void) XFS_SEND_NAMESP(mp, DM_EVENT_POSTCREATE,
+ dir_vp, DM_RIGHT_NULL,
+ *vpp ? vp:NULL,
+ DM_RIGHT_NULL, name, NULL,
+ dm_di_mode, error, 0);
+ }
+ return error;
+
+ abort_return:
+ cancel_flags |= XFS_TRANS_ABORT;
+ /* FALLTHROUGH */
+ error_return:
+
+ if (tp != NULL)
+ xfs_trans_cancel(tp, cancel_flags);
+
+ if (!dp_joined_to_trans && (dp != NULL))
+ xfs_iunlock(dp, XFS_ILOCK_EXCL);
+ XFS_QM_DQRELE(mp, udqp);
+ XFS_QM_DQRELE(mp, gdqp);
+
+ goto std_return;
+
+ abort_rele:
+ /*
+ * Wait until after the current transaction is aborted to
+ * release the inode. This prevents recursive transactions
+ * and deadlocks from xfs_inactive.
+ */
+ cancel_flags |= XFS_TRANS_ABORT;
+ xfs_trans_cancel(tp, cancel_flags);
+ IRELE(ip);
+
+ XFS_QM_DQRELE(mp, udqp);
+ XFS_QM_DQRELE(mp, gdqp);
+
+ goto std_return;
+}
+
+#ifdef DEBUG
+/*
+ * Some counters to see if (and how often) we are hitting some deadlock
+ * prevention code paths.
+ */
+
+int xfs_rm_locks;
+int xfs_rm_lock_delays;
+int xfs_rm_attempts;
+#endif
+
+/*
+ * The following routine will lock the inodes associated with the
+ * directory and the named entry in the directory. The locks are
+ * acquired in increasing inode number.
+ *
+ * If the entry is "..", then only the directory is locked. The
+ * vnode ref count will still include that from the .. entry in
+ * this case.
+ *
+ * There is a deadlock we need to worry about. If the locked directory is
+ * in the AIL, it might be blocking up the log. The next inode we lock
+ * could be already locked by another thread waiting for log space (e.g
+ * a permanent log reservation with a long running transaction (see
+ * xfs_itruncate_finish)). To solve this, we must check if the directory
+ * is in the ail and use lock_nowait. If we can't lock, we need to
+ * drop the inode lock on the directory and try again. xfs_iunlock will
+ * potentially push the tail if we were holding up the log.
+ */
+STATIC int
+xfs_lock_dir_and_entry(
+ xfs_inode_t *dp,
+ vname_t *dentry,
+ xfs_inode_t *ip) /* inode of entry 'name' */
+{
+ int attempts;
+ xfs_ino_t e_inum;
+ xfs_inode_t *ips[2];
+ xfs_log_item_t *lp;
+
+#ifdef DEBUG
+ xfs_rm_locks++;
+#endif
+ attempts = 0;
+
+again:
+ xfs_ilock(dp, XFS_ILOCK_EXCL);
+
+ e_inum = ip->i_ino;
+
+ ITRACE(ip);
+
+ /*
+ * We want to lock in increasing inum. Since we've already
+ * acquired the lock on the directory, we may need to release
+ * if if the inum of the entry turns out to be less.
+ */
+ if (e_inum > dp->i_ino) {
+ /*
+ * We are already in the right order, so just
+ * lock on the inode of the entry.
+ * We need to use nowait if dp is in the AIL.
+ */
+
+ lp = (xfs_log_item_t *)dp->i_itemp;
+ if (lp && (lp->li_flags & XFS_LI_IN_AIL)) {
+ if (!xfs_ilock_nowait(ip, XFS_ILOCK_EXCL)) {
+ attempts++;
+#ifdef DEBUG
+ xfs_rm_attempts++;
+#endif
+
+ /*
+ * Unlock dp and try again.
+ * xfs_iunlock will try to push the tail
+ * if the inode is in the AIL.
+ */
+
+ xfs_iunlock(dp, XFS_ILOCK_EXCL);
+
+ if ((attempts % 5) == 0) {
+ delay(1); /* Don't just spin the CPU */
+#ifdef DEBUG
+ xfs_rm_lock_delays++;
+#endif
+ }
+ goto again;
+ }
+ } else {
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
+ }
+ } else if (e_inum < dp->i_ino) {
+ xfs_iunlock(dp, XFS_ILOCK_EXCL);
+
+ ips[0] = ip;
+ ips[1] = dp;
+ xfs_lock_inodes(ips, 2, 0, XFS_ILOCK_EXCL);
+ }
+ /* else e_inum == dp->i_ino */
+ /* This can happen if we're asked to lock /x/..
+ * the entry is "..", which is also the parent directory.
+ */
+
+ return 0;
+}
+
+#ifdef DEBUG
+int xfs_locked_n;
+int xfs_small_retries;
+int xfs_middle_retries;
+int xfs_lots_retries;
+int xfs_lock_delays;
+#endif
+
+/*
+ * The following routine will lock n inodes in exclusive mode.
+ * We assume the caller calls us with the inodes in i_ino order.
+ *
+ * We need to detect deadlock where an inode that we lock
+ * is in the AIL and we start waiting for another inode that is locked
+ * by a thread in a long running transaction (such as truncate). This can
+ * result in deadlock since the long running trans might need to wait
+ * for the inode we just locked in order to push the tail and free space
+ * in the log.
+ */
+void
+xfs_lock_inodes(
+ xfs_inode_t **ips,
+ int inodes,
+ int first_locked,
+ uint lock_mode)
+{
+ int attempts = 0, i, j, try_lock;
+ xfs_log_item_t *lp;
+
+ ASSERT(ips && (inodes >= 2)); /* we need at least two */
+
+ if (first_locked) {
+ try_lock = 1;
+ i = 1;
+ } else {
+ try_lock = 0;
+ i = 0;
+ }
+
+again:
+ for (; i < inodes; i++) {
+ ASSERT(ips[i]);
+
+ if (i && (ips[i] == ips[i-1])) /* Already locked */
+ continue;
+
+ /*
+ * If try_lock is not set yet, make sure all locked inodes
+ * are not in the AIL.
+ * If any are, set try_lock to be used later.
+ */
+
+ if (!try_lock) {
+ for (j = (i - 1); j >= 0 && !try_lock; j--) {
+ lp = (xfs_log_item_t *)ips[j]->i_itemp;
+ if (lp && (lp->li_flags & XFS_LI_IN_AIL)) {
+ try_lock++;
+ }
+ }
+ }
+
+ /*
+ * If any of the previous locks we have locked is in the AIL,
+ * we must TRY to get the second and subsequent locks. If
+ * we can't get any, we must release all we have
+ * and try again.
+ */
+
+ if (try_lock) {
+ /* try_lock must be 0 if i is 0. */
+ /*
+ * try_lock means we have an inode locked
+ * that is in the AIL.
+ */
+ ASSERT(i != 0);
+ if (!xfs_ilock_nowait(ips[i], lock_mode)) {
+ attempts++;
+
+ /*
+ * Unlock all previous guys and try again.
+ * xfs_iunlock will try to push the tail
+ * if the inode is in the AIL.
+ */
+
+ for(j = i - 1; j >= 0; j--) {
+
+ /*
+ * Check to see if we've already
+ * unlocked this one.
+ * Not the first one going back,
+ * and the inode ptr is the same.
+ */
+ if ((j != (i - 1)) && ips[j] ==
+ ips[j+1])
+ continue;
+
+ xfs_iunlock(ips[j], lock_mode);
+ }
+
+ if ((attempts % 5) == 0) {
+ delay(1); /* Don't just spin the CPU */
+#ifdef DEBUG
+ xfs_lock_delays++;
+#endif
+ }
+ i = 0;
+ try_lock = 0;
+ goto again;
+ }
+ } else {
+ xfs_ilock(ips[i], lock_mode);
+ }
+ }
+
+#ifdef DEBUG
+ if (attempts) {
+ if (attempts < 5) xfs_small_retries++;
+ else if (attempts < 100) xfs_middle_retries++;
+ else xfs_lots_retries++;
+ } else {
+ xfs_locked_n++;
+ }
+#endif
+}
+
+#ifdef DEBUG
+#define REMOVE_DEBUG_TRACE(x) {remove_which_error_return = (x);}
+int remove_which_error_return = 0;
+#else /* ! DEBUG */
+#define REMOVE_DEBUG_TRACE(x)
+#endif /* ! DEBUG */
+
+
+/*
+ * xfs_remove
+ *
+ */
+STATIC int
+xfs_remove(
+ bhv_desc_t *dir_bdp,
+ vname_t *dentry,
+ cred_t *credp)
+{
+ vnode_t *dir_vp;
+ char *name = VNAME(dentry);
+ xfs_inode_t *dp, *ip;
+ xfs_trans_t *tp = NULL;
+ xfs_mount_t *mp;
+ int error = 0;
+ xfs_bmap_free_t free_list;
+ xfs_fsblock_t first_block;
+ int cancel_flags;
+ int committed;
+ int dm_di_mode = 0;
+ int link_zero;
+ uint resblks;
+ int namelen;
+
+ dir_vp = BHV_TO_VNODE(dir_bdp);
+ vn_trace_entry(dir_vp, __FUNCTION__, (inst_t *)__return_address);
+
+ dp = XFS_BHVTOI(dir_bdp);
+ mp = dp->i_mount;
+
+ if (XFS_FORCED_SHUTDOWN(mp))
+ return XFS_ERROR(EIO);
+
+ namelen = VNAMELEN(dentry);
+
+ if (DM_EVENT_ENABLED(dir_vp->v_vfsp, dp, DM_EVENT_REMOVE)) {
+ error = XFS_SEND_NAMESP(mp, DM_EVENT_REMOVE, dir_vp,
+ DM_RIGHT_NULL, NULL, DM_RIGHT_NULL,
+ name, NULL, 0, 0, 0);
+ if (error)
+ return error;
+ }
+
+ /* From this point on, return through std_return */
+ ip = NULL;
+
+ /*
+ * We need to get a reference to ip before we get our log
+ * reservation. The reason for this is that we cannot call
+ * xfs_iget for an inode for which we do not have a reference
+ * once we've acquired a log reservation. This is because the
+ * inode we are trying to get might be in xfs_inactive going
+ * for a log reservation. Since we'll have to wait for the
+ * inactive code to complete before returning from xfs_iget,
+ * we need to make sure that we don't have log space reserved
+ * when we call xfs_iget. Instead we get an unlocked referece
+ * to the inode before getting our log reservation.
+ */
+ error = xfs_get_dir_entry(dentry, &ip);
+ if (error) {
+ REMOVE_DEBUG_TRACE(__LINE__);
+ goto std_return;
+ }
+
+ dm_di_mode = ip->i_d.di_mode;
+
+ vn_trace_entry(XFS_ITOV(ip), __FUNCTION__, (inst_t *)__return_address);
+
+ ITRACE(ip);
+
+ error = XFS_QM_DQATTACH(mp, dp, 0);
+ if (!error && dp != ip)
+ error = XFS_QM_DQATTACH(mp, ip, 0);
+ if (error) {
+ REMOVE_DEBUG_TRACE(__LINE__);
+ IRELE(ip);
+ goto std_return;
+ }
+
+ tp = xfs_trans_alloc(mp, XFS_TRANS_REMOVE);
+ cancel_flags = XFS_TRANS_RELEASE_LOG_RES;
+ /*
+ * We try to get the real space reservation first,
+ * allowing for directory btree deletion(s) implying
+ * possible bmap insert(s). If we can't get the space
+ * reservation then we use 0 instead, and avoid the bmap
+ * btree insert(s) in the directory code by, if the bmap
+ * insert tries to happen, instead trimming the LAST
+ * block from the directory.
+ */
+ resblks = XFS_REMOVE_SPACE_RES(mp);
+ error = xfs_trans_reserve(tp, resblks, XFS_REMOVE_LOG_RES(mp), 0,
+ XFS_TRANS_PERM_LOG_RES, XFS_REMOVE_LOG_COUNT);
+ if (error == ENOSPC) {
+ resblks = 0;
+ error = xfs_trans_reserve(tp, 0, XFS_REMOVE_LOG_RES(mp), 0,
+ XFS_TRANS_PERM_LOG_RES, XFS_REMOVE_LOG_COUNT);
+ }
+ if (error) {
+ ASSERT(error != ENOSPC);
+ REMOVE_DEBUG_TRACE(__LINE__);
+ xfs_trans_cancel(tp, 0);
+ IRELE(ip);
+ return error;
+ }
+
+ error = xfs_lock_dir_and_entry(dp, dentry, ip);
+ if (error) {
+ REMOVE_DEBUG_TRACE(__LINE__);
+ xfs_trans_cancel(tp, cancel_flags);
+ IRELE(ip);
+ goto std_return;
+ }
+
+ /*
+ * At this point, we've gotten both the directory and the entry
+ * inodes locked.
+ */
+ xfs_trans_ijoin(tp, dp, XFS_ILOCK_EXCL);
+ if (dp != ip) {
+ /*
+ * Increment vnode ref count only in this case since
+ * there's an extra vnode reference in the case where
+ * dp == ip.
+ */
+ IHOLD(dp);
+ xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
+ }
+
+ /*
+ * Entry must exist since we did a lookup in xfs_lock_dir_and_entry.
+ */
+ XFS_BMAP_INIT(&free_list, &first_block);
+ error = XFS_DIR_REMOVENAME(mp, tp, dp, name, namelen, ip->i_ino,
+ &first_block, &free_list, 0);
+ if (error) {
+ ASSERT(error != ENOENT);
+ REMOVE_DEBUG_TRACE(__LINE__);
+ goto error1;
+ }
+ xfs_ichgtime(dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
+
+ dp->i_gen++;
+ xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE);
+
+ error = xfs_droplink(tp, ip);
+ if (error) {
+ REMOVE_DEBUG_TRACE(__LINE__);
+ goto error1;
+ }
+
+ /* Determine if this is the last link while
+ * we are in the transaction.
+ */
+ link_zero = (ip)->i_d.di_nlink==0;
+
+ /*
+ * Take an extra ref on the inode so that it doesn't
+ * go to xfs_inactive() from within the commit.
+ */
+ IHOLD(ip);
+
+ /*
+ * If this is a synchronous mount, make sure that the
+ * remove transaction goes to disk before returning to
+ * the user.
+ */
+ if (mp->m_flags & XFS_MOUNT_WSYNC) {
+ xfs_trans_set_sync(tp);
+ }
+
+ error = xfs_bmap_finish(&tp, &free_list, first_block, &committed);
+ if (error) {
+ REMOVE_DEBUG_TRACE(__LINE__);
+ goto error_rele;
+ }
+
+ error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES, NULL);
+ if (error) {
+ IRELE(ip);
+ goto std_return;
+ }
+
+ /*
+ * Before we drop our extra reference to the inode, purge it
+ * from the refcache if it is there. By waiting until afterwards
+ * to do the IRELE, we ensure that we won't go inactive in the
+ * xfs_refcache_purge_ip routine (although that would be OK).
+ */
+ xfs_refcache_purge_ip(ip);
+
+ vn_trace_exit(XFS_ITOV(ip), __FUNCTION__, (inst_t *)__return_address);
+
+ /*
+ * Let interposed file systems know about removed links.
+ */
+ VOP_LINK_REMOVED(XFS_ITOV(ip), dir_vp, link_zero);
+
+ IRELE(ip);
+
+/* Fall through to std_return with error = 0 */
+ std_return:
+ if (DM_EVENT_ENABLED(dir_vp->v_vfsp, dp,
+ DM_EVENT_POSTREMOVE)) {
+ (void) XFS_SEND_NAMESP(mp, DM_EVENT_POSTREMOVE,
+ dir_vp, DM_RIGHT_NULL,
+ NULL, DM_RIGHT_NULL,
+ name, NULL, dm_di_mode, error, 0);
+ }
+ return error;
+
+ error1:
+ xfs_bmap_cancel(&free_list);
+ cancel_flags |= XFS_TRANS_ABORT;
+ xfs_trans_cancel(tp, cancel_flags);
+ goto std_return;
+
+ error_rele:
+ /*
+ * In this case make sure to not release the inode until after
+ * the current transaction is aborted. Releasing it beforehand
+ * can cause us to go to xfs_inactive and start a recursive
+ * transaction which can easily deadlock with the current one.
+ */
+ xfs_bmap_cancel(&free_list);
+ cancel_flags |= XFS_TRANS_ABORT;
+ xfs_trans_cancel(tp, cancel_flags);
+
+ /*
+ * Before we drop our extra reference to the inode, purge it
+ * from the refcache if it is there. By waiting until afterwards
+ * to do the IRELE, we ensure that we won't go inactive in the
+ * xfs_refcache_purge_ip routine (although that would be OK).
+ */
+ xfs_refcache_purge_ip(ip);
+
+ IRELE(ip);
+
+ goto std_return;
+}
+
+
+/*
+ * xfs_link
+ *
+ */
+STATIC int
+xfs_link(
+ bhv_desc_t *target_dir_bdp,
+ vnode_t *src_vp,
+ vname_t *dentry,
+ cred_t *credp)
+{
+ xfs_inode_t *tdp, *sip;
+ xfs_trans_t *tp;
+ xfs_mount_t *mp;
+ xfs_inode_t *ips[2];
+ int error;
+ xfs_bmap_free_t free_list;
+ xfs_fsblock_t first_block;
+ int cancel_flags;
+ int committed;
+ vnode_t *target_dir_vp;
+ bhv_desc_t *src_bdp;
+ int resblks;
+ char *target_name = VNAME(dentry);
+ int target_namelen;
+
+ target_dir_vp = BHV_TO_VNODE(target_dir_bdp);
+ vn_trace_entry(target_dir_vp, __FUNCTION__, (inst_t *)__return_address);
+ vn_trace_entry(src_vp, __FUNCTION__, (inst_t *)__return_address);
+
+ target_namelen = VNAMELEN(dentry);
+ if (src_vp->v_type == VDIR)
+ return XFS_ERROR(EPERM);
+
+ /*
+ * For now, manually find the XFS behavior descriptor for
+ * the source vnode. If it doesn't exist then something
+ * is wrong and we should just return an error.
+ * Eventually we need to figure out how link is going to
+ * work in the face of stacked vnodes.
+ */
+ src_bdp = vn_bhv_lookup_unlocked(VN_BHV_HEAD(src_vp), &xfs_vnodeops);
+ if (src_bdp == NULL) {
+ return XFS_ERROR(EXDEV);
+ }
+ sip = XFS_BHVTOI(src_bdp);
+ tdp = XFS_BHVTOI(target_dir_bdp);
+ mp = tdp->i_mount;
+ if (XFS_FORCED_SHUTDOWN(mp))
+ return XFS_ERROR(EIO);
+
+ if (DM_EVENT_ENABLED(src_vp->v_vfsp, tdp, DM_EVENT_LINK)) {
+ error = XFS_SEND_NAMESP(mp, DM_EVENT_LINK,
+ target_dir_vp, DM_RIGHT_NULL,
+ src_vp, DM_RIGHT_NULL,
+ target_name, NULL, 0, 0, 0);
+ if (error)
+ return error;
+ }
+
+ /* Return through std_return after this point. */
+
+ error = XFS_QM_DQATTACH(mp, sip, 0);
+ if (!error && sip != tdp)
+ error = XFS_QM_DQATTACH(mp, tdp, 0);
+ if (error)
+ goto std_return;
+
+ tp = xfs_trans_alloc(mp, XFS_TRANS_LINK);
+ cancel_flags = XFS_TRANS_RELEASE_LOG_RES;
+ resblks = XFS_LINK_SPACE_RES(mp, target_namelen);
+ error = xfs_trans_reserve(tp, resblks, XFS_LINK_LOG_RES(mp), 0,
+ XFS_TRANS_PERM_LOG_RES, XFS_LINK_LOG_COUNT);
+ if (error == ENOSPC) {
+ resblks = 0;
+ error = xfs_trans_reserve(tp, 0, XFS_LINK_LOG_RES(mp), 0,
+ XFS_TRANS_PERM_LOG_RES, XFS_LINK_LOG_COUNT);
+ }
+ if (error) {
+ cancel_flags = 0;
+ goto error_return;
+ }
+
+ if (sip->i_ino < tdp->i_ino) {
+ ips[0] = sip;
+ ips[1] = tdp;
+ } else {
+ ips[0] = tdp;
+ ips[1] = sip;
+ }
+
+ xfs_lock_inodes(ips, 2, 0, XFS_ILOCK_EXCL);
+
+ /*
+ * Increment vnode ref counts since xfs_trans_commit &
+ * xfs_trans_cancel will both unlock the inodes and
+ * decrement the associated ref counts.
+ */
+ VN_HOLD(src_vp);
+ VN_HOLD(target_dir_vp);
+ xfs_trans_ijoin(tp, sip, XFS_ILOCK_EXCL);
+ xfs_trans_ijoin(tp, tdp, XFS_ILOCK_EXCL);
+
+ /*
+ * If the source has too many links, we can't make any more to it.
+ */
+ if (sip->i_d.di_nlink >= XFS_MAXLINK) {
+ error = XFS_ERROR(EMLINK);
+ goto error_return;
+ }
+
+ if (resblks == 0 &&
+ (error = XFS_DIR_CANENTER(mp, tp, tdp, target_name,
+ target_namelen)))
+ goto error_return;
+
+ XFS_BMAP_INIT(&free_list, &first_block);
+
+ error = XFS_DIR_CREATENAME(mp, tp, tdp, target_name, target_namelen,
+ sip->i_ino, &first_block, &free_list,
+ resblks);
+ if (error)
+ goto abort_return;
+ xfs_ichgtime(tdp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
+ tdp->i_gen++;
+ xfs_trans_log_inode(tp, tdp, XFS_ILOG_CORE);
+
+ error = xfs_bumplink(tp, sip);
+ if (error) {
+ goto abort_return;
+ }
+
+ /*
+ * If this is a synchronous mount, make sure that the
+ * link transaction goes to disk before returning to
+ * the user.
+ */
+ if (mp->m_flags & XFS_MOUNT_WSYNC) {
+ xfs_trans_set_sync(tp);
+ }
+
+ error = xfs_bmap_finish (&tp, &free_list, first_block, &committed);
+ if (error) {
+ xfs_bmap_cancel(&free_list);
+ goto abort_return;
+ }
+
+ error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES, NULL);
+ if (error) {
+ goto std_return;
+ }
+
+ /* Fall through to std_return with error = 0. */
+std_return:
+ if (DM_EVENT_ENABLED(src_vp->v_vfsp, sip,
+ DM_EVENT_POSTLINK)) {
+ (void) XFS_SEND_NAMESP(mp, DM_EVENT_POSTLINK,
+ target_dir_vp, DM_RIGHT_NULL,
+ src_vp, DM_RIGHT_NULL,
+ target_name, NULL, 0, error, 0);
+ }
+ return error;
+
+ abort_return:
+ cancel_flags |= XFS_TRANS_ABORT;
+ /* FALLTHROUGH */
+ error_return:
+ xfs_trans_cancel(tp, cancel_flags);
+
+ goto std_return;
+}
+/*
+ * xfs_mkdir
+ *
+ */
+STATIC int
+xfs_mkdir(
+ bhv_desc_t *dir_bdp,
+ vname_t *dentry,
+ vattr_t *vap,
+ vnode_t **vpp,
+ cred_t *credp)
+{
+ char *dir_name = VNAME(dentry);
+ xfs_inode_t *dp;
+ xfs_inode_t *cdp; /* inode of created dir */
+ vnode_t *cvp; /* vnode of created dir */
+ xfs_trans_t *tp;
+ xfs_mount_t *mp;
+ int cancel_flags;
+ int error;
+ int committed;
+ xfs_bmap_free_t free_list;
+ xfs_fsblock_t first_block;
+ vnode_t *dir_vp;
+ boolean_t dp_joined_to_trans;
+ boolean_t created = B_FALSE;
+ int dm_event_sent = 0;
+ xfs_prid_t prid;
+ struct xfs_dquot *udqp, *gdqp;
+ uint resblks;
+ int dm_di_mode;
+ int dir_namelen;
+
+ dir_vp = BHV_TO_VNODE(dir_bdp);
+ dp = XFS_BHVTOI(dir_bdp);
+ mp = dp->i_mount;
+
+ if (XFS_FORCED_SHUTDOWN(mp))
+ return XFS_ERROR(EIO);
+
+ dir_namelen = VNAMELEN(dentry);
+
+ tp = NULL;
+ dp_joined_to_trans = B_FALSE;
+ dm_di_mode = vap->va_mode|VTTOIF(vap->va_type);
+
+ if (DM_EVENT_ENABLED(dir_vp->v_vfsp, dp, DM_EVENT_CREATE)) {
+ error = XFS_SEND_NAMESP(mp, DM_EVENT_CREATE,
+ dir_vp, DM_RIGHT_NULL, NULL,
+ DM_RIGHT_NULL, dir_name, NULL,
+ dm_di_mode, 0, 0);
+ if (error)
+ return error;
+ dm_event_sent = 1;
+ }
+
+ /* Return through std_return after this point. */
+
+ vn_trace_entry(dir_vp, __FUNCTION__, (inst_t *)__return_address);
+
+ mp = dp->i_mount;
+ udqp = gdqp = NULL;
+ if (vap->va_mask & XFS_AT_PROJID)
+ prid = (xfs_prid_t)vap->va_projid;
+ else
+ prid = (xfs_prid_t)dfltprid;
+
+ /*
+ * Make sure that we have allocated dquot(s) on disk.
+ */
+ error = XFS_QM_DQVOPALLOC(mp, dp,
+ current_fsuid(credp), current_fsgid(credp),
+ XFS_QMOPT_QUOTALL | XFS_QMOPT_INHERIT, &udqp, &gdqp);
+ if (error)
+ goto std_return;
+
+ tp = xfs_trans_alloc(mp, XFS_TRANS_MKDIR);
+ cancel_flags = XFS_TRANS_RELEASE_LOG_RES;
+ resblks = XFS_MKDIR_SPACE_RES(mp, dir_namelen);
+ error = xfs_trans_reserve(tp, resblks, XFS_MKDIR_LOG_RES(mp), 0,
+ XFS_TRANS_PERM_LOG_RES, XFS_MKDIR_LOG_COUNT);
+ if (error == ENOSPC) {
+ resblks = 0;
+ error = xfs_trans_reserve(tp, 0, XFS_MKDIR_LOG_RES(mp), 0,
+ XFS_TRANS_PERM_LOG_RES,
+ XFS_MKDIR_LOG_COUNT);
+ }
+ if (error) {
+ cancel_flags = 0;
+ dp = NULL;
+ goto error_return;
+ }
+
+ xfs_ilock(dp, XFS_ILOCK_EXCL);
+
+ /*
+ * Check for directory link count overflow.
+ */
+ if (dp->i_d.di_nlink >= XFS_MAXLINK) {
+ error = XFS_ERROR(EMLINK);
+ goto error_return;
+ }
+
+ /*
+ * Reserve disk quota and the inode.
+ */
+ error = XFS_TRANS_RESERVE_QUOTA(mp, tp, udqp, gdqp, resblks, 1, 0);
+ if (error)
+ goto error_return;
+
+ if (resblks == 0 &&
+ (error = XFS_DIR_CANENTER(mp, tp, dp, dir_name, dir_namelen)))
+ goto error_return;
+ /*
+ * create the directory inode.
+ */
+ error = xfs_dir_ialloc(&tp, dp,
+ MAKEIMODE(vap->va_type,vap->va_mode), 2,
+ 0, credp, prid, resblks > 0,
+ &cdp, NULL);
+ if (error) {
+ if (error == ENOSPC)
+ goto error_return;
+ goto abort_return;
+ }
+ ITRACE(cdp);
+
+ /*
+ * Now we add the directory inode to the transaction.
+ * We waited until now since xfs_dir_ialloc might start
+ * a new transaction. Had we joined the transaction
+ * earlier, the locks might have gotten released.
+ */
+ VN_HOLD(dir_vp);
+ xfs_trans_ijoin(tp, dp, XFS_ILOCK_EXCL);
+ dp_joined_to_trans = B_TRUE;
+
+ XFS_BMAP_INIT(&free_list, &first_block);
+
+ error = XFS_DIR_CREATENAME(mp, tp, dp, dir_name, dir_namelen,
+ cdp->i_ino, &first_block, &free_list,
+ resblks ? resblks - XFS_IALLOC_SPACE_RES(mp) : 0);
+ if (error) {
+ ASSERT(error != ENOSPC);
+ goto error1;
+ }
+ xfs_ichgtime(dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
+
+ /*
+ * Bump the in memory version number of the parent directory
+ * so that other processes accessing it will recognize that
+ * the directory has changed.
+ */
+ dp->i_gen++;
+
+ error = XFS_DIR_INIT(mp, tp, cdp, dp);
+ if (error) {
+ goto error2;
+ }
+
+ cdp->i_gen = 1;
+ error = xfs_bumplink(tp, dp);
+ if (error) {
+ goto error2;
+ }
+
+ cvp = XFS_ITOV(cdp);
+
+ created = B_TRUE;
+
+ *vpp = cvp;
+ IHOLD(cdp);
+
+ /*
+ * Attach the dquots to the new inode and modify the icount incore.
+ */
+ XFS_QM_DQVOPCREATE(mp, tp, cdp, udqp, gdqp);
+
+ /*
+ * If this is a synchronous mount, make sure that the
+ * mkdir transaction goes to disk before returning to
+ * the user.
+ */
+ if (mp->m_flags & XFS_MOUNT_WSYNC) {
+ xfs_trans_set_sync(tp);
+ }
+
+ error = xfs_bmap_finish(&tp, &free_list, first_block, &committed);
+ if (error) {
+ IRELE(cdp);
+ goto error2;
+ }
+
+ error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES, NULL);
+ XFS_QM_DQRELE(mp, udqp);
+ XFS_QM_DQRELE(mp, gdqp);
+ if (error) {
+ IRELE(cdp);
+ }
+
+ /* Fall through to std_return with error = 0 or errno from
+ * xfs_trans_commit. */
+
+std_return:
+ if ( (created || (error != 0 && dm_event_sent != 0)) &&
+ DM_EVENT_ENABLED(dir_vp->v_vfsp, XFS_BHVTOI(dir_bdp),
+ DM_EVENT_POSTCREATE)) {
+ (void) XFS_SEND_NAMESP(mp, DM_EVENT_POSTCREATE,
+ dir_vp, DM_RIGHT_NULL,
+ created ? XFS_ITOV(cdp):NULL,
+ DM_RIGHT_NULL,
+ dir_name, NULL,
+ dm_di_mode, error, 0);
+ }
+ return error;
+
+ error2:
+ error1:
+ xfs_bmap_cancel(&free_list);
+ abort_return:
+ cancel_flags |= XFS_TRANS_ABORT;
+ error_return:
+ xfs_trans_cancel(tp, cancel_flags);
+ XFS_QM_DQRELE(mp, udqp);
+ XFS_QM_DQRELE(mp, gdqp);
+
+ if (!dp_joined_to_trans && (dp != NULL)) {
+ xfs_iunlock(dp, XFS_ILOCK_EXCL);
+ }
+
+ goto std_return;
+}
+
+
+/*
+ * xfs_rmdir
+ *
+ */
+STATIC int
+xfs_rmdir(
+ bhv_desc_t *dir_bdp,
+ vname_t *dentry,
+ cred_t *credp)
+{
+ char *name = VNAME(dentry);
+ xfs_inode_t *dp;
+ xfs_inode_t *cdp; /* child directory */
+ xfs_trans_t *tp;
+ xfs_mount_t *mp;
+ int error;
+ xfs_bmap_free_t free_list;
+ xfs_fsblock_t first_block;
+ int cancel_flags;
+ int committed;
+ vnode_t *dir_vp;
+ int dm_di_mode = 0;
+ int last_cdp_link;
+ int namelen;
+ uint resblks;
+
+ dir_vp = BHV_TO_VNODE(dir_bdp);
+ dp = XFS_BHVTOI(dir_bdp);
+ mp = dp->i_mount;
+
+ vn_trace_entry(dir_vp, __FUNCTION__, (inst_t *)__return_address);
+
+ if (XFS_FORCED_SHUTDOWN(XFS_BHVTOI(dir_bdp)->i_mount))
+ return XFS_ERROR(EIO);
+ namelen = VNAMELEN(dentry);
+
+ if (DM_EVENT_ENABLED(dir_vp->v_vfsp, dp, DM_EVENT_REMOVE)) {
+ error = XFS_SEND_NAMESP(mp, DM_EVENT_REMOVE,
+ dir_vp, DM_RIGHT_NULL,
+ NULL, DM_RIGHT_NULL,
+ name, NULL, 0, 0, 0);
+ if (error)
+ return XFS_ERROR(error);
+ }
+
+ /* Return through std_return after this point. */
+
+ cdp = NULL;
+
+ /*
+ * We need to get a reference to cdp before we get our log
+ * reservation. The reason for this is that we cannot call
+ * xfs_iget for an inode for which we do not have a reference
+ * once we've acquired a log reservation. This is because the
+ * inode we are trying to get might be in xfs_inactive going
+ * for a log reservation. Since we'll have to wait for the
+ * inactive code to complete before returning from xfs_iget,
+ * we need to make sure that we don't have log space reserved
+ * when we call xfs_iget. Instead we get an unlocked referece
+ * to the inode before getting our log reservation.
+ */
+ error = xfs_get_dir_entry(dentry, &cdp);
+ if (error) {
+ REMOVE_DEBUG_TRACE(__LINE__);
+ goto std_return;
+ }
+ mp = dp->i_mount;
+ dm_di_mode = cdp->i_d.di_mode;
+
+ /*
+ * Get the dquots for the inodes.
+ */
+ error = XFS_QM_DQATTACH(mp, dp, 0);
+ if (!error && dp != cdp)
+ error = XFS_QM_DQATTACH(mp, cdp, 0);
+ if (error) {
+ IRELE(cdp);
+ REMOVE_DEBUG_TRACE(__LINE__);
+ goto std_return;
+ }
+
+ tp = xfs_trans_alloc(mp, XFS_TRANS_RMDIR);
+ cancel_flags = XFS_TRANS_RELEASE_LOG_RES;
+ /*
+ * We try to get the real space reservation first,
+ * allowing for directory btree deletion(s) implying
+ * possible bmap insert(s). If we can't get the space
+ * reservation then we use 0 instead, and avoid the bmap
+ * btree insert(s) in the directory code by, if the bmap
+ * insert tries to happen, instead trimming the LAST
+ * block from the directory.
+ */
+ resblks = XFS_REMOVE_SPACE_RES(mp);
+ error = xfs_trans_reserve(tp, resblks, XFS_REMOVE_LOG_RES(mp), 0,
+ XFS_TRANS_PERM_LOG_RES, XFS_DEFAULT_LOG_COUNT);
+ if (error == ENOSPC) {
+ resblks = 0;
+ error = xfs_trans_reserve(tp, 0, XFS_REMOVE_LOG_RES(mp), 0,
+ XFS_TRANS_PERM_LOG_RES, XFS_DEFAULT_LOG_COUNT);
+ }
+ if (error) {
+ ASSERT(error != ENOSPC);
+ cancel_flags = 0;
+ IRELE(cdp);
+ goto error_return;
+ }
+ XFS_BMAP_INIT(&free_list, &first_block);
+
+ /*
+ * Now lock the child directory inode and the parent directory
+ * inode in the proper order. This will take care of validating
+ * that the directory entry for the child directory inode has
+ * not changed while we were obtaining a log reservation.
+ */
+ error = xfs_lock_dir_and_entry(dp, dentry, cdp);
+ if (error) {
+ xfs_trans_cancel(tp, cancel_flags);
+ IRELE(cdp);
+ goto std_return;
+ }
+
+ xfs_trans_ijoin(tp, dp, XFS_ILOCK_EXCL);
+ if (dp != cdp) {
+ /*
+ * Only increment the parent directory vnode count if
+ * we didn't bump it in looking up cdp. The only time
+ * we don't bump it is when we're looking up ".".
+ */
+ VN_HOLD(dir_vp);
+ }
+
+ ITRACE(cdp);
+ xfs_trans_ijoin(tp, cdp, XFS_ILOCK_EXCL);
+
+ ASSERT(cdp->i_d.di_nlink >= 2);
+ if (cdp->i_d.di_nlink != 2) {
+ error = XFS_ERROR(ENOTEMPTY);
+ goto error_return;
+ }
+ if (!XFS_DIR_ISEMPTY(mp, cdp)) {
+ error = XFS_ERROR(ENOTEMPTY);
+ goto error_return;
+ }
+
+ error = XFS_DIR_REMOVENAME(mp, tp, dp, name, namelen, cdp->i_ino,
+ &first_block, &free_list, resblks);
+ if (error) {
+ goto error1;
+ }
+
+ xfs_ichgtime(dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
+
+ /*
+ * Bump the in memory generation count on the parent
+ * directory so that other can know that it has changed.
+ */
+ dp->i_gen++;
+
+ /*
+ * Drop the link from cdp's "..".
+ */
+ error = xfs_droplink(tp, dp);
+ if (error) {
+ goto error1;
+ }
+
+ /*
+ * Drop the link from dp to cdp.
+ */
+ error = xfs_droplink(tp, cdp);
+ if (error) {
+ goto error1;
+ }
+
+ /*
+ * Drop the "." link from cdp to self.
+ */
+ error = xfs_droplink(tp, cdp);
+ if (error) {
+ goto error1;
+ }
+
+ /* Determine these before committing transaction */
+ last_cdp_link = (cdp)->i_d.di_nlink==0;
+
+ /*
+ * Take an extra ref on the child vnode so that it
+ * does not go to xfs_inactive() from within the commit.
+ */
+ IHOLD(cdp);
+
+ /*
+ * If this is a synchronous mount, make sure that the
+ * rmdir transaction goes to disk before returning to
+ * the user.
+ */
+ if (mp->m_flags & XFS_MOUNT_WSYNC) {
+ xfs_trans_set_sync(tp);
+ }
+
+ error = xfs_bmap_finish (&tp, &free_list, first_block, &committed);
+ if (error) {
+ xfs_bmap_cancel(&free_list);
+ xfs_trans_cancel(tp, (XFS_TRANS_RELEASE_LOG_RES |
+ XFS_TRANS_ABORT));
+ IRELE(cdp);
+ goto std_return;
+ }
+
+ error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES, NULL);
+ if (error) {
+ IRELE(cdp);
+ goto std_return;
+ }
+
+
+ /*
+ * Let interposed file systems know about removed links.
+ */
+ VOP_LINK_REMOVED(XFS_ITOV(cdp), dir_vp, last_cdp_link);
+
+ IRELE(cdp);
+
+ /* Fall through to std_return with error = 0 or the errno
+ * from xfs_trans_commit. */
+std_return:
+ if (DM_EVENT_ENABLED(dir_vp->v_vfsp, dp, DM_EVENT_POSTREMOVE)) {
+ (void) XFS_SEND_NAMESP(mp, DM_EVENT_POSTREMOVE,
+ dir_vp, DM_RIGHT_NULL,
+ NULL, DM_RIGHT_NULL,
+ name, NULL, dm_di_mode,
+ error, 0);
+ }
+ return error;
+
+ error1:
+ xfs_bmap_cancel(&free_list);
+ cancel_flags |= XFS_TRANS_ABORT;
+ error_return:
+ xfs_trans_cancel(tp, cancel_flags);
+ goto std_return;
+}
+
+
+/*
+ * xfs_readdir
+ *
+ * Read dp's entries starting at uiop->uio_offset and translate them into
+ * bufsize bytes worth of struct dirents starting at bufbase.
+ */
+STATIC int
+xfs_readdir(
+ bhv_desc_t *dir_bdp,
+ uio_t *uiop,
+ cred_t *credp,
+ int *eofp)
+{
+ xfs_inode_t *dp;
+ xfs_trans_t *tp = NULL;
+ int error = 0;
+ uint lock_mode;
+ xfs_off_t start_offset;
+
+ vn_trace_entry(BHV_TO_VNODE(dir_bdp), __FUNCTION__,
+ (inst_t *)__return_address);
+ dp = XFS_BHVTOI(dir_bdp);
+
+ if (XFS_FORCED_SHUTDOWN(dp->i_mount)) {
+ return XFS_ERROR(EIO);
+ }
+
+ lock_mode = xfs_ilock_map_shared(dp);
+ start_offset = uiop->uio_offset;
+ error = XFS_DIR_GETDENTS(dp->i_mount, tp, dp, uiop, eofp);
+ if (start_offset != uiop->uio_offset) {
+ xfs_ichgtime(dp, XFS_ICHGTIME_ACC);
+ }
+ xfs_iunlock_map_shared(dp, lock_mode);
+ return error;
+}
+
+
+/*
+ * xfs_symlink
+ *
+ */
+STATIC int
+xfs_symlink(
+ bhv_desc_t *dir_bdp,
+ vname_t *dentry,
+ vattr_t *vap,
+ char *target_path,
+ vnode_t **vpp,
+ cred_t *credp)
+{
+ xfs_trans_t *tp;
+ xfs_mount_t *mp;
+ xfs_inode_t *dp;
+ xfs_inode_t *ip;
+ int error;
+ int pathlen;
+ xfs_bmap_free_t free_list;
+ xfs_fsblock_t first_block;
+ boolean_t dp_joined_to_trans;
+ vnode_t *dir_vp;
+ uint cancel_flags;
+ int committed;
+ xfs_fileoff_t first_fsb;
+ xfs_filblks_t fs_blocks;
+ int nmaps;
+ xfs_bmbt_irec_t mval[SYMLINK_MAPS];
+ xfs_daddr_t d;
+ char *cur_chunk;
+ int byte_cnt;
+ int n;
+ xfs_buf_t *bp;
+ xfs_prid_t prid;
+ struct xfs_dquot *udqp, *gdqp;
+ uint resblks;
+ char *link_name = VNAME(dentry);
+ int link_namelen;
+
+ *vpp = NULL;
+ dir_vp = BHV_TO_VNODE(dir_bdp);
+ dp = XFS_BHVTOI(dir_bdp);
+ dp_joined_to_trans = B_FALSE;
+ error = 0;
+ ip = NULL;
+ tp = NULL;
+
+ vn_trace_entry(dir_vp, __FUNCTION__, (inst_t *)__return_address);
+
+ mp = dp->i_mount;
+
+ if (XFS_FORCED_SHUTDOWN(mp))
+ return XFS_ERROR(EIO);
+
+ link_namelen = VNAMELEN(dentry);
+
+ /*
+ * Check component lengths of the target path name.
+ */
+ pathlen = strlen(target_path);
+ if (pathlen >= MAXPATHLEN) /* total string too long */
+ return XFS_ERROR(ENAMETOOLONG);
+ if (pathlen >= MAXNAMELEN) { /* is any component too long? */
+ int len, total;
+ char *path;
+
+ for(total = 0, path = target_path; total < pathlen;) {
+ /*
+ * Skip any slashes.
+ */
+ while(*path == '/') {
+ total++;
+ path++;
+ }
+
+ /*
+ * Count up to the next slash or end of path.
+ * Error out if the component is bigger than MAXNAMELEN.
+ */
+ for(len = 0; *path != '/' && total < pathlen;total++, path++) {
+ if (++len >= MAXNAMELEN) {
+ error = ENAMETOOLONG;
+ return error;
+ }
+ }
+ }
+ }
+
+ if (DM_EVENT_ENABLED(dir_vp->v_vfsp, dp, DM_EVENT_SYMLINK)) {
+ error = XFS_SEND_NAMESP(mp, DM_EVENT_SYMLINK, dir_vp,
+ DM_RIGHT_NULL, NULL, DM_RIGHT_NULL,
+ link_name, target_path, 0, 0, 0);
+ if (error)
+ return error;
+ }
+
+ /* Return through std_return after this point. */
+
+ udqp = gdqp = NULL;
+ if (vap->va_mask & XFS_AT_PROJID)
+ prid = (xfs_prid_t)vap->va_projid;
+ else
+ prid = (xfs_prid_t)dfltprid;
+
+ /*
+ * Make sure that we have allocated dquot(s) on disk.
+ */
+ error = XFS_QM_DQVOPALLOC(mp, dp,
+ current_fsuid(credp), current_fsgid(credp),
+ XFS_QMOPT_QUOTALL | XFS_QMOPT_INHERIT, &udqp, &gdqp);
+ if (error)
+ goto std_return;
+
+ tp = xfs_trans_alloc(mp, XFS_TRANS_SYMLINK);
+ cancel_flags = XFS_TRANS_RELEASE_LOG_RES;
+ /*
+ * The symlink will fit into the inode data fork?
+ * There can't be any attributes so we get the whole variable part.
+ */
+ if (pathlen <= XFS_LITINO(mp))
+ fs_blocks = 0;
+ else
+ fs_blocks = XFS_B_TO_FSB(mp, pathlen);
+ resblks = XFS_SYMLINK_SPACE_RES(mp, link_namelen, fs_blocks);
+ error = xfs_trans_reserve(tp, resblks, XFS_SYMLINK_LOG_RES(mp), 0,
+ XFS_TRANS_PERM_LOG_RES, XFS_SYMLINK_LOG_COUNT);
+ if (error == ENOSPC && fs_blocks == 0) {
+ resblks = 0;
+ error = xfs_trans_reserve(tp, 0, XFS_SYMLINK_LOG_RES(mp), 0,
+ XFS_TRANS_PERM_LOG_RES, XFS_SYMLINK_LOG_COUNT);
+ }
+ if (error) {
+ cancel_flags = 0;
+ dp = NULL;
+ goto error_return;
+ }
+
+ xfs_ilock(dp, XFS_ILOCK_EXCL);
+
+ /*
+ * Check whether the directory allows new symlinks or not.
+ */
+ if (dp->i_d.di_flags & XFS_DIFLAG_NOSYMLINKS) {
+ error = XFS_ERROR(EPERM);
+ goto error_return;
+ }
+
+ /*
+ * Reserve disk quota : blocks and inode.
+ */
+ error = XFS_TRANS_RESERVE_QUOTA(mp, tp, udqp, gdqp, resblks, 1, 0);
+ if (error)
+ goto error_return;
+
+ /*
+ * Check for ability to enter directory entry, if no space reserved.
+ */
+ if (resblks == 0 &&
+ (error = XFS_DIR_CANENTER(mp, tp, dp, link_name, link_namelen)))
+ goto error_return;
+ /*
+ * Initialize the bmap freelist prior to calling either
+ * bmapi or the directory create code.
+ */
+ XFS_BMAP_INIT(&free_list, &first_block);
+
+ /*
+ * Allocate an inode for the symlink.
+ */
+ error = xfs_dir_ialloc(&tp, dp, S_IFLNK | (vap->va_mode&~S_IFMT),
+ 1, 0, credp, prid, resblks > 0, &ip, NULL);
+ if (error) {
+ if (error == ENOSPC)
+ goto error_return;
+ goto error1;
+ }
+ ITRACE(ip);
+
+ VN_HOLD(dir_vp);
+ xfs_trans_ijoin(tp, dp, XFS_ILOCK_EXCL);
+ dp_joined_to_trans = B_TRUE;
+
+ /*
+ * Also attach the dquot(s) to it, if applicable.
+ */
+ XFS_QM_DQVOPCREATE(mp, tp, ip, udqp, gdqp);
+
+ if (resblks)
+ resblks -= XFS_IALLOC_SPACE_RES(mp);
+ /*
+ * If the symlink will fit into the inode, write it inline.
+ */
+ if (pathlen <= XFS_IFORK_DSIZE(ip)) {
+ xfs_idata_realloc(ip, pathlen, XFS_DATA_FORK);
+ memcpy(ip->i_df.if_u1.if_data, target_path, pathlen);
+ ip->i_d.di_size = pathlen;
+
+ /*
+ * The inode was initially created in extent format.
+ */
+ ip->i_df.if_flags &= ~(XFS_IFEXTENTS | XFS_IFBROOT);
+ ip->i_df.if_flags |= XFS_IFINLINE;
+
+ ip->i_d.di_format = XFS_DINODE_FMT_LOCAL;
+ xfs_trans_log_inode(tp, ip, XFS_ILOG_DDATA | XFS_ILOG_CORE);
+
+ } else {
+ first_fsb = 0;
+ nmaps = SYMLINK_MAPS;
+
+ error = xfs_bmapi(tp, ip, first_fsb, fs_blocks,
+ XFS_BMAPI_WRITE | XFS_BMAPI_METADATA,
+ &first_block, resblks, mval, &nmaps,
+ &free_list);
+ if (error) {
+ goto error1;
+ }
+
+ if (resblks)
+ resblks -= fs_blocks;
+ ip->i_d.di_size = pathlen;
+ xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+
+ cur_chunk = target_path;
+ for (n = 0; n < nmaps; n++) {
+ d = XFS_FSB_TO_DADDR(mp, mval[n].br_startblock);
+ byte_cnt = XFS_FSB_TO_B(mp, mval[n].br_blockcount);
+ bp = xfs_trans_get_buf(tp, mp->m_ddev_targp, d,
+ BTOBB(byte_cnt), 0);
+ ASSERT(bp && !XFS_BUF_GETERROR(bp));
+ if (pathlen < byte_cnt) {
+ byte_cnt = pathlen;
+ }
+ pathlen -= byte_cnt;
+
+ memcpy(XFS_BUF_PTR(bp), cur_chunk, byte_cnt);
+ cur_chunk += byte_cnt;
+
+ xfs_trans_log_buf(tp, bp, 0, byte_cnt - 1);
+ }
+ }
+
+ /*
+ * Create the directory entry for the symlink.
+ */
+ error = XFS_DIR_CREATENAME(mp, tp, dp, link_name, link_namelen,
+ ip->i_ino, &first_block, &free_list, resblks);
+ if (error) {
+ goto error1;
+ }
+ xfs_ichgtime(dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
+ xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE);
+
+ /*
+ * Bump the in memory version number of the parent directory
+ * so that other processes accessing it will recognize that
+ * the directory has changed.
+ */
+ dp->i_gen++;
+
+ /*
+ * If this is a synchronous mount, make sure that the
+ * symlink transaction goes to disk before returning to
+ * the user.
+ */
+ if (mp->m_flags & XFS_MOUNT_WSYNC) {
+ xfs_trans_set_sync(tp);
+ }
+
+ /*
+ * xfs_trans_commit normally decrements the vnode ref count
+ * when it unlocks the inode. Since we want to return the
+ * vnode to the caller, we bump the vnode ref count now.
+ */
+ IHOLD(ip);
+
+ error = xfs_bmap_finish(&tp, &free_list, first_block, &committed);
+ if (error) {
+ goto error2;
+ }
+ error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES, NULL);
+ XFS_QM_DQRELE(mp, udqp);
+ XFS_QM_DQRELE(mp, gdqp);
+
+ /* Fall through to std_return with error = 0 or errno from
+ * xfs_trans_commit */
+std_return:
+ if (DM_EVENT_ENABLED(dir_vp->v_vfsp, XFS_BHVTOI(dir_bdp),
+ DM_EVENT_POSTSYMLINK)) {
+ (void) XFS_SEND_NAMESP(mp, DM_EVENT_POSTSYMLINK,
+ dir_vp, DM_RIGHT_NULL,
+ error ? NULL : XFS_ITOV(ip),
+ DM_RIGHT_NULL, link_name, target_path,
+ 0, error, 0);
+ }
+
+ if (!error) {
+ vnode_t *vp;
+
+ ASSERT(ip);
+ vp = XFS_ITOV(ip);
+ *vpp = vp;
+ }
+ return error;
+
+ error2:
+ IRELE(ip);
+ error1:
+ xfs_bmap_cancel(&free_list);
+ cancel_flags |= XFS_TRANS_ABORT;
+ error_return:
+ xfs_trans_cancel(tp, cancel_flags);
+ XFS_QM_DQRELE(mp, udqp);
+ XFS_QM_DQRELE(mp, gdqp);
+
+ if (!dp_joined_to_trans && (dp != NULL)) {
+ xfs_iunlock(dp, XFS_ILOCK_EXCL);
+ }
+
+ goto std_return;
+}
+
+
+/*
+ * xfs_fid2
+ *
+ * A fid routine that takes a pointer to a previously allocated
+ * fid structure (like xfs_fast_fid) but uses a 64 bit inode number.
+ */
+STATIC int
+xfs_fid2(
+ bhv_desc_t *bdp,
+ fid_t *fidp)
+{
+ xfs_inode_t *ip;
+ xfs_fid2_t *xfid;
+
+ vn_trace_entry(BHV_TO_VNODE(bdp), __FUNCTION__,
+ (inst_t *)__return_address);
+ ASSERT(sizeof(fid_t) >= sizeof(xfs_fid2_t));
+
+ xfid = (xfs_fid2_t *)fidp;
+ ip = XFS_BHVTOI(bdp);
+ xfid->fid_len = sizeof(xfs_fid2_t) - sizeof(xfid->fid_len);
+ xfid->fid_pad = 0;
+ /*
+ * use memcpy because the inode is a long long and there's no
+ * assurance that xfid->fid_ino is properly aligned.
+ */
+ memcpy(&xfid->fid_ino, &ip->i_ino, sizeof(xfid->fid_ino));
+ xfid->fid_gen = ip->i_d.di_gen;
+
+ return 0;
+}
+
+
+/*
+ * xfs_rwlock
+ */
+int
+xfs_rwlock(
+ bhv_desc_t *bdp,
+ vrwlock_t locktype)
+{
+ xfs_inode_t *ip;
+ vnode_t *vp;
+
+ vp = BHV_TO_VNODE(bdp);
+ if (vp->v_type == VDIR)
+ return 1;
+ ip = XFS_BHVTOI(bdp);
+ if (locktype == VRWLOCK_WRITE) {
+ xfs_ilock(ip, XFS_IOLOCK_EXCL);
+ } else if (locktype == VRWLOCK_TRY_READ) {
+ return (xfs_ilock_nowait(ip, XFS_IOLOCK_SHARED));
+ } else if (locktype == VRWLOCK_TRY_WRITE) {
+ return (xfs_ilock_nowait(ip, XFS_IOLOCK_EXCL));
+ } else {
+ ASSERT((locktype == VRWLOCK_READ) ||
+ (locktype == VRWLOCK_WRITE_DIRECT));
+ xfs_ilock(ip, XFS_IOLOCK_SHARED);
+ }
+
+ return 1;
+}
+
+
+/*
+ * xfs_rwunlock
+ */
+void
+xfs_rwunlock(
+ bhv_desc_t *bdp,
+ vrwlock_t locktype)
+{
+ xfs_inode_t *ip;
+ vnode_t *vp;
+
+ vp = BHV_TO_VNODE(bdp);
+ if (vp->v_type == VDIR)
+ return;
+ ip = XFS_BHVTOI(bdp);
+ if (locktype == VRWLOCK_WRITE) {
+ /*
+ * In the write case, we may have added a new entry to
+ * the reference cache. This might store a pointer to
+ * an inode to be released in this inode. If it is there,
+ * clear the pointer and release the inode after unlocking
+ * this one.
+ */
+ xfs_refcache_iunlock(ip, XFS_IOLOCK_EXCL);
+ } else {
+ ASSERT((locktype == VRWLOCK_READ) ||
+ (locktype == VRWLOCK_WRITE_DIRECT));
+ xfs_iunlock(ip, XFS_IOLOCK_SHARED);
+ }
+ return;
+}
+
+STATIC int
+xfs_inode_flush(
+ bhv_desc_t *bdp,
+ int flags)
+{
+ xfs_inode_t *ip;
+ xfs_mount_t *mp;
+ int error = 0;
+
+ ip = XFS_BHVTOI(bdp);
+ mp = ip->i_mount;
+
+ if (XFS_FORCED_SHUTDOWN(mp))
+ return XFS_ERROR(EIO);
+
+ /* Bypass inodes which have already been cleaned by
+ * the inode flush clustering code inside xfs_iflush
+ */
+ if ((ip->i_update_core == 0) &&
+ ((ip->i_itemp == NULL) ||
+ !(ip->i_itemp->ili_format.ilf_fields & XFS_ILOG_ALL)))
+ return 0;
+
+ if (flags & FLUSH_LOG) {
+ xfs_inode_log_item_t *iip = ip->i_itemp;
+
+ if (iip && iip->ili_last_lsn) {
+ xlog_t *log = mp->m_log;
+ xfs_lsn_t sync_lsn;
+ int s, log_flags = XFS_LOG_FORCE;
+
+ s = GRANT_LOCK(log);
+ sync_lsn = log->l_last_sync_lsn;
+ GRANT_UNLOCK(log, s);
+
+ if ((XFS_LSN_CMP(iip->ili_last_lsn, sync_lsn) <= 0))
+ return 0;
+
+ if (flags & FLUSH_SYNC)
+ log_flags |= XFS_LOG_SYNC;
+ return xfs_log_force(mp, iip->ili_last_lsn,
+ log_flags);
+ }
+ }
+
+ /* We make this non-blocking if the inode is contended,
+ * return EAGAIN to indicate to the caller that they
+ * did not succeed. This prevents the flush path from
+ * blocking on inodes inside another operation right
+ * now, they get caught later by xfs_sync.
+ */
+ if (flags & FLUSH_INODE) {
+ int flush_flags;
+
+ if (xfs_ipincount(ip))
+ return EAGAIN;
+
+ if (flags & FLUSH_SYNC) {
+ xfs_ilock(ip, XFS_ILOCK_SHARED);
+ xfs_iflock(ip);
+ } else if (xfs_ilock_nowait(ip, XFS_ILOCK_SHARED)) {
+ if (xfs_ipincount(ip) || !xfs_iflock_nowait(ip)) {
+ xfs_iunlock(ip, XFS_ILOCK_SHARED);
+ return EAGAIN;
+ }
+ } else {
+ return EAGAIN;
+ }
+
+ if (flags & FLUSH_SYNC)
+ flush_flags = XFS_IFLUSH_SYNC;
+ else
+ flush_flags = XFS_IFLUSH_ASYNC;
+
+ error = xfs_iflush(ip, flush_flags);
+ xfs_iunlock(ip, XFS_ILOCK_SHARED);
+ }
+
+ return error;
+}
+
+
+int
+xfs_set_dmattrs (
+ bhv_desc_t *bdp,
+ u_int evmask,
+ u_int16_t state,
+ cred_t *credp)
+{
+ xfs_inode_t *ip;
+ xfs_trans_t *tp;
+ xfs_mount_t *mp;
+ int error;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return XFS_ERROR(EPERM);
+
+ ip = XFS_BHVTOI(bdp);
+ mp = ip->i_mount;
+
+ if (XFS_FORCED_SHUTDOWN(mp))
+ return XFS_ERROR(EIO);
+
+ tp = xfs_trans_alloc(mp, XFS_TRANS_SET_DMATTRS);
+ error = xfs_trans_reserve(tp, 0, XFS_ICHANGE_LOG_RES (mp), 0, 0, 0);
+ if (error) {
+ xfs_trans_cancel(tp, 0);
+ return error;
+ }
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
+ xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
+
+ ip->i_iocore.io_dmevmask = ip->i_d.di_dmevmask = evmask;
+ ip->i_iocore.io_dmstate = ip->i_d.di_dmstate = state;
+
+ xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+ IHOLD(ip);
+ error = xfs_trans_commit(tp, 0, NULL);
+
+ return error;
+}
+
+
+/*
+ * xfs_reclaim
+ */
+STATIC int
+xfs_reclaim(
+ bhv_desc_t *bdp)
+{
+ xfs_inode_t *ip;
+ vnode_t *vp;
+
+ vp = BHV_TO_VNODE(bdp);
+ ip = XFS_BHVTOI(bdp);
+
+ vn_trace_entry(vp, __FUNCTION__, (inst_t *)__return_address);
+
+ ASSERT(!VN_MAPPED(vp));
+
+ /* bad inode, get out here ASAP */
+ if (VN_BAD(vp)) {
+ xfs_ireclaim(ip);
+ return 0;
+ }
+
+ if ((ip->i_d.di_mode & S_IFMT) == S_IFREG) {
+ if (ip->i_d.di_size > 0) {
+ /*
+ * Flush and invalidate any data left around that is
+ * a part of this file.
+ *
+ * Get the inode's i/o lock so that buffers are pushed
+ * out while holding the proper lock. We can't hold
+ * the inode lock here since flushing out buffers may
+ * cause us to try to get the lock in xfs_strategy().
+ *
+ * We don't have to call remapf() here, because there
+ * cannot be any mapped file references to this vnode
+ * since it is being reclaimed.
+ */
+ xfs_ilock(ip, XFS_IOLOCK_EXCL);
+
+ /*
+ * If we hit an IO error, we need to make sure that the
+ * buffer and page caches of file data for
+ * the file are tossed away. We don't want to use
+ * VOP_FLUSHINVAL_PAGES here because we don't want dirty
+ * pages to stay attached to the vnode, but be
+ * marked P_BAD. pdflush/vnode_pagebad
+ * hates that.
+ */
+ if (!XFS_FORCED_SHUTDOWN(ip->i_mount)) {
+ VOP_FLUSHINVAL_PAGES(vp, 0, -1, FI_NONE);
+ } else {
+ VOP_TOSS_PAGES(vp, 0, -1, FI_NONE);
+ }
+
+ ASSERT(VN_CACHED(vp) == 0);
+ ASSERT(XFS_FORCED_SHUTDOWN(ip->i_mount) ||
+ ip->i_delayed_blks == 0);
+ xfs_iunlock(ip, XFS_IOLOCK_EXCL);
+ } else if (XFS_FORCED_SHUTDOWN(ip->i_mount)) {
+ /*
+ * di_size field may not be quite accurate if we're
+ * shutting down.
+ */
+ VOP_TOSS_PAGES(vp, 0, -1, FI_NONE);
+ ASSERT(VN_CACHED(vp) == 0);
+ }
+ }
+
+ /* If we have nothing to flush with this inode then complete the
+ * teardown now, otherwise break the link between the xfs inode
+ * and the linux inode and clean up the xfs inode later. This
+ * avoids flushing the inode to disk during the delete operation
+ * itself.
+ */
+ if (!ip->i_update_core && (ip->i_itemp == NULL)) {
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
+ xfs_iflock(ip);
+ return xfs_finish_reclaim(ip, 1, XFS_IFLUSH_DELWRI_ELSE_SYNC);
+ } else {
+ xfs_mount_t *mp = ip->i_mount;
+
+ /* Protect sync from us */
+ XFS_MOUNT_ILOCK(mp);
+ vn_bhv_remove(VN_BHV_HEAD(vp), XFS_ITOBHV(ip));
+ list_add_tail(&ip->i_reclaim, &mp->m_del_inodes);
+ ip->i_flags |= XFS_IRECLAIMABLE;
+ XFS_MOUNT_IUNLOCK(mp);
+ }
+ return 0;
+}
+
+int
+xfs_finish_reclaim(
+ xfs_inode_t *ip,
+ int locked,
+ int sync_mode)
+{
+ xfs_ihash_t *ih = ip->i_hash;
+ vnode_t *vp = XFS_ITOV_NULL(ip);
+ int error;
+
+ if (vp && VN_BAD(vp))
+ goto reclaim;
+
+ /* The hash lock here protects a thread in xfs_iget_core from
+ * racing with us on linking the inode back with a vnode.
+ * Once we have the XFS_IRECLAIM flag set it will not touch
+ * us.
+ */
+ write_lock(&ih->ih_lock);
+ if ((ip->i_flags & XFS_IRECLAIM) ||
+ (!(ip->i_flags & XFS_IRECLAIMABLE) && vp == NULL)) {
+ write_unlock(&ih->ih_lock);
+ if (locked) {
+ xfs_ifunlock(ip);
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ }
+ return(1);
+ }
+ ip->i_flags |= XFS_IRECLAIM;
+ write_unlock(&ih->ih_lock);
+
+ /*
+ * If the inode is still dirty, then flush it out. If the inode
+ * is not in the AIL, then it will be OK to flush it delwri as
+ * long as xfs_iflush() does not keep any references to the inode.
+ * We leave that decision up to xfs_iflush() since it has the
+ * knowledge of whether it's OK to simply do a delwri flush of
+ * the inode or whether we need to wait until the inode is
+ * pulled from the AIL.
+ * We get the flush lock regardless, though, just to make sure
+ * we don't free it while it is being flushed.
+ */
+ if (!XFS_FORCED_SHUTDOWN(ip->i_mount)) {
+ if (!locked) {
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
+ xfs_iflock(ip);
+ }
+
+ if (ip->i_update_core ||
+ ((ip->i_itemp != NULL) &&
+ (ip->i_itemp->ili_format.ilf_fields != 0))) {
+ error = xfs_iflush(ip, sync_mode);
+ /*
+ * If we hit an error, typically because of filesystem
+ * shutdown, we don't need to let vn_reclaim to know
+ * because we're gonna reclaim the inode anyway.
+ */
+ if (error) {
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ goto reclaim;
+ }
+ xfs_iflock(ip); /* synchronize with xfs_iflush_done */
+ }
+
+ ASSERT(ip->i_update_core == 0);
+ ASSERT(ip->i_itemp == NULL ||
+ ip->i_itemp->ili_format.ilf_fields == 0);
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ } else if (locked) {
+ /*
+ * We are not interested in doing an iflush if we're
+ * in the process of shutting down the filesystem forcibly.
+ * So, just reclaim the inode.
+ */
+ xfs_ifunlock(ip);
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ }
+
+ reclaim:
+ xfs_ireclaim(ip);
+ return 0;
+}
+
+int
+xfs_finish_reclaim_all(xfs_mount_t *mp, int noblock)
+{
+ int purged;
+ xfs_inode_t *ip, *n;
+ int done = 0;
+
+ while (!done) {
+ purged = 0;
+ XFS_MOUNT_ILOCK(mp);
+ list_for_each_entry_safe(ip, n, &mp->m_del_inodes, i_reclaim) {
+ if (noblock) {
+ if (xfs_ilock_nowait(ip, XFS_ILOCK_EXCL) == 0)
+ continue;
+ if (xfs_ipincount(ip) ||
+ !xfs_iflock_nowait(ip)) {
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ continue;
+ }
+ }
+ XFS_MOUNT_IUNLOCK(mp);
+ xfs_finish_reclaim(ip, noblock,
+ XFS_IFLUSH_DELWRI_ELSE_ASYNC);
+ purged = 1;
+ break;
+ }
+
+ done = !purged;
+ }
+
+ XFS_MOUNT_IUNLOCK(mp);
+ return 0;
+}
+
+/*
+ * xfs_alloc_file_space()
+ * This routine allocates disk space for the given file.
+ *
+ * If alloc_type == 0, this request is for an ALLOCSP type
+ * request which will change the file size. In this case, no
+ * DMAPI event will be generated by the call. A TRUNCATE event
+ * will be generated later by xfs_setattr.
+ *
+ * If alloc_type != 0, this request is for a RESVSP type
+ * request, and a DMAPI DM_EVENT_WRITE will be generated if the
+ * lower block boundary byte address is less than the file's
+ * length.
+ *
+ * RETURNS:
+ * 0 on success
+ * errno on error
+ *
+ */
+int
+xfs_alloc_file_space(
+ xfs_inode_t *ip,
+ xfs_off_t offset,
+ xfs_off_t len,
+ int alloc_type,
+ int attr_flags)
+{
+ xfs_filblks_t allocated_fsb;
+ xfs_filblks_t allocatesize_fsb;
+ int committed;
+ xfs_off_t count;
+ xfs_filblks_t datablocks;
+ int error;
+ xfs_fsblock_t firstfsb;
+ xfs_bmap_free_t free_list;
+ xfs_bmbt_irec_t *imapp;
+ xfs_bmbt_irec_t imaps[1];
+ xfs_mount_t *mp;
+ int numrtextents;
+ int reccount;
+ uint resblks;
+ int rt;
+ int rtextsize;
+ xfs_fileoff_t startoffset_fsb;
+ xfs_trans_t *tp;
+ int xfs_bmapi_flags;
+
+ vn_trace_entry(XFS_ITOV(ip), __FUNCTION__, (inst_t *)__return_address);
+ mp = ip->i_mount;
+
+ if (XFS_FORCED_SHUTDOWN(mp))
+ return XFS_ERROR(EIO);
+
+ /*
+ * determine if this is a realtime file
+ */
+ if ((rt = XFS_IS_REALTIME_INODE(ip)) != 0) {
+ if (ip->i_d.di_extsize)
+ rtextsize = ip->i_d.di_extsize;
+ else
+ rtextsize = mp->m_sb.sb_rextsize;
+ } else
+ rtextsize = 0;
+
+ if ((error = XFS_QM_DQATTACH(mp, ip, 0)))
+ return error;
+
+ if (len <= 0)
+ return XFS_ERROR(EINVAL);
+
+ count = len;
+ error = 0;
+ imapp = &imaps[0];
+ reccount = 1;
+ xfs_bmapi_flags = XFS_BMAPI_WRITE | (alloc_type ? XFS_BMAPI_PREALLOC : 0);
+ startoffset_fsb = XFS_B_TO_FSBT(mp, offset);
+ allocatesize_fsb = XFS_B_TO_FSB(mp, count);
+
+ /* Generate a DMAPI event if needed. */
+ if (alloc_type != 0 && offset < ip->i_d.di_size &&
+ (attr_flags&ATTR_DMI) == 0 &&
+ DM_EVENT_ENABLED(XFS_MTOVFS(mp), ip, DM_EVENT_WRITE)) {
+ xfs_off_t end_dmi_offset;
+
+ end_dmi_offset = offset+len;
+ if (end_dmi_offset > ip->i_d.di_size)
+ end_dmi_offset = ip->i_d.di_size;
+ error = XFS_SEND_DATA(mp, DM_EVENT_WRITE, XFS_ITOV(ip),
+ offset, end_dmi_offset - offset,
+ 0, NULL);
+ if (error)
+ return(error);
+ }
+
+ /*
+ * allocate file space until done or until there is an error
+ */
+retry:
+ while (allocatesize_fsb && !error) {
+ /*
+ * determine if reserving space on
+ * the data or realtime partition.
+ */
+ if (rt) {
+ xfs_fileoff_t s, e;
+
+ s = startoffset_fsb;
+ do_div(s, rtextsize);
+ s *= rtextsize;
+ e = roundup_64(startoffset_fsb + allocatesize_fsb,
+ rtextsize);
+ numrtextents = (int)(e - s) / mp->m_sb.sb_rextsize;
+ datablocks = 0;
+ } else {
+ datablocks = allocatesize_fsb;
+ numrtextents = 0;
+ }
+
+ /*
+ * allocate and setup the transaction
+ */
+ tp = xfs_trans_alloc(mp, XFS_TRANS_DIOSTRAT);
+ resblks = XFS_DIOSTRAT_SPACE_RES(mp, datablocks);
+ error = xfs_trans_reserve(tp,
+ resblks,
+ XFS_WRITE_LOG_RES(mp),
+ numrtextents,
+ XFS_TRANS_PERM_LOG_RES,
+ XFS_WRITE_LOG_COUNT);
+
+ /*
+ * check for running out of space
+ */
+ if (error) {
+ /*
+ * Free the transaction structure.
+ */
+ ASSERT(error == ENOSPC || XFS_FORCED_SHUTDOWN(mp));
+ xfs_trans_cancel(tp, 0);
+ break;
+ }
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
+ error = XFS_TRANS_RESERVE_QUOTA_BYDQUOTS(mp, tp,
+ ip->i_udquot, ip->i_gdquot, resblks, 0, rt ?
+ XFS_QMOPT_RES_RTBLKS : XFS_QMOPT_RES_REGBLKS);
+ if (error)
+ goto error1;
+
+ xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
+ xfs_trans_ihold(tp, ip);
+
+ /*
+ * issue the bmapi() call to allocate the blocks
+ */
+ XFS_BMAP_INIT(&free_list, &firstfsb);
+ error = xfs_bmapi(tp, ip, startoffset_fsb,
+ allocatesize_fsb, xfs_bmapi_flags,
+ &firstfsb, 0, imapp, &reccount,
+ &free_list);
+ if (error) {
+ goto error0;
+ }
+
+ /*
+ * complete the transaction
+ */
+ error = xfs_bmap_finish(&tp, &free_list, firstfsb, &committed);
+ if (error) {
+ goto error0;
+ }
+
+ error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES, NULL);
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ if (error) {
+ break;
+ }
+
+ allocated_fsb = imapp->br_blockcount;
+
+ if (reccount == 0) {
+ error = XFS_ERROR(ENOSPC);
+ break;
+ }
+
+ startoffset_fsb += allocated_fsb;
+ allocatesize_fsb -= allocated_fsb;
+ }
+dmapi_enospc_check:
+ if (error == ENOSPC && (attr_flags&ATTR_DMI) == 0 &&
+ DM_EVENT_ENABLED(XFS_MTOVFS(mp), ip, DM_EVENT_NOSPACE)) {
+
+ error = XFS_SEND_NAMESP(mp, DM_EVENT_NOSPACE,
+ XFS_ITOV(ip), DM_RIGHT_NULL,
+ XFS_ITOV(ip), DM_RIGHT_NULL,
+ NULL, NULL, 0, 0, 0); /* Delay flag intentionally unused */
+ if (error == 0)
+ goto retry; /* Maybe DMAPI app. has made space */
+ /* else fall through with error from XFS_SEND_DATA */
+ }
+
+ return error;
+
+ error0:
+ xfs_bmap_cancel(&free_list);
+ error1:
+ xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT);
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ goto dmapi_enospc_check;
+}
+
+/*
+ * Zero file bytes between startoff and endoff inclusive.
+ * The iolock is held exclusive and no blocks are buffered.
+ */
+STATIC int
+xfs_zero_remaining_bytes(
+ xfs_inode_t *ip,
+ xfs_off_t startoff,
+ xfs_off_t endoff)
+{
+ xfs_bmbt_irec_t imap;
+ xfs_fileoff_t offset_fsb;
+ xfs_off_t lastoffset;
+ xfs_off_t offset;
+ xfs_buf_t *bp;
+ xfs_mount_t *mp = ip->i_mount;
+ int nimap;
+ int error = 0;
+
+ bp = xfs_buf_get_noaddr(mp->m_sb.sb_blocksize,
+ ip->i_d.di_flags & XFS_DIFLAG_REALTIME ?
+ mp->m_rtdev_targp : mp->m_ddev_targp);
+
+ for (offset = startoff; offset <= endoff; offset = lastoffset + 1) {
+ offset_fsb = XFS_B_TO_FSBT(mp, offset);
+ nimap = 1;
+ error = xfs_bmapi(NULL, ip, offset_fsb, 1, 0, NULL, 0, &imap,
+ &nimap, NULL);
+ if (error || nimap < 1)
+ break;
+ ASSERT(imap.br_blockcount >= 1);
+ ASSERT(imap.br_startoff == offset_fsb);
+ lastoffset = XFS_FSB_TO_B(mp, imap.br_startoff + 1) - 1;
+ if (lastoffset > endoff)
+ lastoffset = endoff;
+ if (imap.br_startblock == HOLESTARTBLOCK)
+ continue;
+ ASSERT(imap.br_startblock != DELAYSTARTBLOCK);
+ if (imap.br_state == XFS_EXT_UNWRITTEN)
+ continue;
+ XFS_BUF_UNDONE(bp);
+ XFS_BUF_UNWRITE(bp);
+ XFS_BUF_READ(bp);
+ XFS_BUF_SET_ADDR(bp, XFS_FSB_TO_DB(ip, imap.br_startblock));
+ xfsbdstrat(mp, bp);
+ if ((error = xfs_iowait(bp))) {
+ xfs_ioerror_alert("xfs_zero_remaining_bytes(read)",
+ mp, bp, XFS_BUF_ADDR(bp));
+ break;
+ }
+ memset(XFS_BUF_PTR(bp) +
+ (offset - XFS_FSB_TO_B(mp, imap.br_startoff)),
+ 0, lastoffset - offset + 1);
+ XFS_BUF_UNDONE(bp);
+ XFS_BUF_UNREAD(bp);
+ XFS_BUF_WRITE(bp);
+ xfsbdstrat(mp, bp);
+ if ((error = xfs_iowait(bp))) {
+ xfs_ioerror_alert("xfs_zero_remaining_bytes(write)",
+ mp, bp, XFS_BUF_ADDR(bp));
+ break;
+ }
+ }
+ xfs_buf_free(bp);
+ return error;
+}
+
+/*
+ * xfs_free_file_space()
+ * This routine frees disk space for the given file.
+ *
+ * This routine is only called by xfs_change_file_space
+ * for an UNRESVSP type call.
+ *
+ * RETURNS:
+ * 0 on success
+ * errno on error
+ *
+ */
+STATIC int
+xfs_free_file_space(
+ xfs_inode_t *ip,
+ xfs_off_t offset,
+ xfs_off_t len,
+ int attr_flags)
+{
+ int committed;
+ int done;
+ xfs_off_t end_dmi_offset;
+ xfs_fileoff_t endoffset_fsb;
+ int error;
+ xfs_fsblock_t firstfsb;
+ xfs_bmap_free_t free_list;
+ xfs_off_t ilen;
+ xfs_bmbt_irec_t imap;
+ xfs_off_t ioffset;
+ xfs_extlen_t mod=0;
+ xfs_mount_t *mp;
+ int nimap;
+ uint resblks;
+ int rounding;
+ int rt;
+ xfs_fileoff_t startoffset_fsb;
+ xfs_trans_t *tp;
+ int need_iolock = (attr_flags & ATTR_DMI) == 0;
+
+ vn_trace_entry(XFS_ITOV(ip), __FUNCTION__, (inst_t *)__return_address);
+ mp = ip->i_mount;
+
+ if ((error = XFS_QM_DQATTACH(mp, ip, 0)))
+ return error;
+
+ error = 0;
+ if (len <= 0) /* if nothing being freed */
+ return error;
+ rt = (ip->i_d.di_flags & XFS_DIFLAG_REALTIME);
+ startoffset_fsb = XFS_B_TO_FSB(mp, offset);
+ end_dmi_offset = offset + len;
+ endoffset_fsb = XFS_B_TO_FSBT(mp, end_dmi_offset);
+
+ if (offset < ip->i_d.di_size &&
+ (attr_flags & ATTR_DMI) == 0 &&
+ DM_EVENT_ENABLED(XFS_MTOVFS(mp), ip, DM_EVENT_WRITE)) {
+ if (end_dmi_offset > ip->i_d.di_size)
+ end_dmi_offset = ip->i_d.di_size;
+ error = XFS_SEND_DATA(mp, DM_EVENT_WRITE, XFS_ITOV(ip),
+ offset, end_dmi_offset - offset,
+ AT_DELAY_FLAG(attr_flags), NULL);
+ if (error)
+ return(error);
+ }
+
+ if (need_iolock)
+ xfs_ilock(ip, XFS_IOLOCK_EXCL);
+ rounding = MAX((__uint8_t)(1 << mp->m_sb.sb_blocklog),
+ (__uint8_t)NBPP);
+ ilen = len + (offset & (rounding - 1));
+ ioffset = offset & ~(rounding - 1);
+ if (ilen & (rounding - 1))
+ ilen = (ilen + rounding) & ~(rounding - 1);
+ xfs_inval_cached_pages(XFS_ITOV(ip), &(ip->i_iocore), ioffset, 0, 0);
+ /*
+ * Need to zero the stuff we're not freeing, on disk.
+ * If its a realtime file & can't use unwritten extents then we
+ * actually need to zero the extent edges. Otherwise xfs_bunmapi
+ * will take care of it for us.
+ */
+ if (rt && !XFS_SB_VERSION_HASEXTFLGBIT(&mp->m_sb)) {
+ nimap = 1;
+ error = xfs_bmapi(NULL, ip, startoffset_fsb, 1, 0, NULL, 0,
+ &imap, &nimap, NULL);
+ if (error)
+ goto out_unlock_iolock;
+ ASSERT(nimap == 0 || nimap == 1);
+ if (nimap && imap.br_startblock != HOLESTARTBLOCK) {
+ xfs_daddr_t block;
+
+ ASSERT(imap.br_startblock != DELAYSTARTBLOCK);
+ block = imap.br_startblock;
+ mod = do_div(block, mp->m_sb.sb_rextsize);
+ if (mod)
+ startoffset_fsb += mp->m_sb.sb_rextsize - mod;
+ }
+ nimap = 1;
+ error = xfs_bmapi(NULL, ip, endoffset_fsb - 1, 1, 0, NULL, 0,
+ &imap, &nimap, NULL);
+ if (error)
+ goto out_unlock_iolock;
+ ASSERT(nimap == 0 || nimap == 1);
+ if (nimap && imap.br_startblock != HOLESTARTBLOCK) {
+ ASSERT(imap.br_startblock != DELAYSTARTBLOCK);
+ mod++;
+ if (mod && (mod != mp->m_sb.sb_rextsize))
+ endoffset_fsb -= mod;
+ }
+ }
+ if ((done = (endoffset_fsb <= startoffset_fsb)))
+ /*
+ * One contiguous piece to clear
+ */
+ error = xfs_zero_remaining_bytes(ip, offset, offset + len - 1);
+ else {
+ /*
+ * Some full blocks, possibly two pieces to clear
+ */
+ if (offset < XFS_FSB_TO_B(mp, startoffset_fsb))
+ error = xfs_zero_remaining_bytes(ip, offset,
+ XFS_FSB_TO_B(mp, startoffset_fsb) - 1);
+ if (!error &&
+ XFS_FSB_TO_B(mp, endoffset_fsb) < offset + len)
+ error = xfs_zero_remaining_bytes(ip,
+ XFS_FSB_TO_B(mp, endoffset_fsb),
+ offset + len - 1);
+ }
+
+ /*
+ * free file space until done or until there is an error
+ */
+ resblks = XFS_DIOSTRAT_SPACE_RES(mp, 0);
+ while (!error && !done) {
+
+ /*
+ * allocate and setup the transaction
+ */
+ tp = xfs_trans_alloc(mp, XFS_TRANS_DIOSTRAT);
+ error = xfs_trans_reserve(tp,
+ resblks,
+ XFS_WRITE_LOG_RES(mp),
+ 0,
+ XFS_TRANS_PERM_LOG_RES,
+ XFS_WRITE_LOG_COUNT);
+
+ /*
+ * check for running out of space
+ */
+ if (error) {
+ /*
+ * Free the transaction structure.
+ */
+ ASSERT(error == ENOSPC || XFS_FORCED_SHUTDOWN(mp));
+ xfs_trans_cancel(tp, 0);
+ break;
+ }
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
+ error = XFS_TRANS_RESERVE_QUOTA(mp, tp,
+ ip->i_udquot, ip->i_gdquot, resblks, 0, rt ?
+ XFS_QMOPT_RES_RTBLKS : XFS_QMOPT_RES_REGBLKS);
+ if (error)
+ goto error1;
+
+ xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
+ xfs_trans_ihold(tp, ip);
+
+ /*
+ * issue the bunmapi() call to free the blocks
+ */
+ XFS_BMAP_INIT(&free_list, &firstfsb);
+ error = xfs_bunmapi(tp, ip, startoffset_fsb,
+ endoffset_fsb - startoffset_fsb,
+ 0, 2, &firstfsb, &free_list, &done);
+ if (error) {
+ goto error0;
+ }
+
+ /*
+ * complete the transaction
+ */
+ error = xfs_bmap_finish(&tp, &free_list, firstfsb, &committed);
+ if (error) {
+ goto error0;
+ }
+
+ error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES, NULL);
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ }
+
+ out_unlock_iolock:
+ if (need_iolock)
+ xfs_iunlock(ip, XFS_IOLOCK_EXCL);
+ return error;
+
+ error0:
+ xfs_bmap_cancel(&free_list);
+ error1:
+ xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT);
+ xfs_iunlock(ip, need_iolock ? (XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL) :
+ XFS_ILOCK_EXCL);
+ return error;
+}
+
+/*
+ * xfs_change_file_space()
+ * This routine allocates or frees disk space for the given file.
+ * The user specified parameters are checked for alignment and size
+ * limitations.
+ *
+ * RETURNS:
+ * 0 on success
+ * errno on error
+ *
+ */
+int
+xfs_change_file_space(
+ bhv_desc_t *bdp,
+ int cmd,
+ xfs_flock64_t *bf,
+ xfs_off_t offset,
+ cred_t *credp,
+ int attr_flags)
+{
+ int clrprealloc;
+ int error;
+ xfs_fsize_t fsize;
+ xfs_inode_t *ip;
+ xfs_mount_t *mp;
+ int setprealloc;
+ xfs_off_t startoffset;
+ xfs_off_t llen;
+ xfs_trans_t *tp;
+ vattr_t va;
+ vnode_t *vp;
+
+ vp = BHV_TO_VNODE(bdp);
+ vn_trace_entry(vp, __FUNCTION__, (inst_t *)__return_address);
+
+ ip = XFS_BHVTOI(bdp);
+ mp = ip->i_mount;
+
+ /*
+ * must be a regular file and have write permission
+ */
+ if (vp->v_type != VREG)
+ return XFS_ERROR(EINVAL);
+
+ xfs_ilock(ip, XFS_ILOCK_SHARED);
+
+ if ((error = xfs_iaccess(ip, S_IWUSR, credp))) {
+ xfs_iunlock(ip, XFS_ILOCK_SHARED);
+ return error;
+ }
+
+ xfs_iunlock(ip, XFS_ILOCK_SHARED);
+
+ switch (bf->l_whence) {
+ case 0: /*SEEK_SET*/
+ break;
+ case 1: /*SEEK_CUR*/
+ bf->l_start += offset;
+ break;
+ case 2: /*SEEK_END*/
+ bf->l_start += ip->i_d.di_size;
+ break;
+ default:
+ return XFS_ERROR(EINVAL);
+ }
+
+ llen = bf->l_len > 0 ? bf->l_len - 1 : bf->l_len;
+
+ if ( (bf->l_start < 0)
+ || (bf->l_start > XFS_MAXIOFFSET(mp))
+ || (bf->l_start + llen < 0)
+ || (bf->l_start + llen > XFS_MAXIOFFSET(mp)))
+ return XFS_ERROR(EINVAL);
+
+ bf->l_whence = 0;
+
+ startoffset = bf->l_start;
+ fsize = ip->i_d.di_size;
+
+ /*
+ * XFS_IOC_RESVSP and XFS_IOC_UNRESVSP will reserve or unreserve
+ * file space.
+ * These calls do NOT zero the data space allocated to the file,
+ * nor do they change the file size.
+ *
+ * XFS_IOC_ALLOCSP and XFS_IOC_FREESP will allocate and free file
+ * space.
+ * These calls cause the new file data to be zeroed and the file
+ * size to be changed.
+ */
+ setprealloc = clrprealloc = 0;
+
+ switch (cmd) {
+ case XFS_IOC_RESVSP:
+ case XFS_IOC_RESVSP64:
+ error = xfs_alloc_file_space(ip, startoffset, bf->l_len,
+ 1, attr_flags);
+ if (error)
+ return error;
+ setprealloc = 1;
+ break;
+
+ case XFS_IOC_UNRESVSP:
+ case XFS_IOC_UNRESVSP64:
+ if ((error = xfs_free_file_space(ip, startoffset, bf->l_len,
+ attr_flags)))
+ return error;
+ break;
+
+ case XFS_IOC_ALLOCSP:
+ case XFS_IOC_ALLOCSP64:
+ case XFS_IOC_FREESP:
+ case XFS_IOC_FREESP64:
+ if (startoffset > fsize) {
+ error = xfs_alloc_file_space(ip, fsize,
+ startoffset - fsize, 0, attr_flags);
+ if (error)
+ break;
+ }
+
+ va.va_mask = XFS_AT_SIZE;
+ va.va_size = startoffset;
+
+ error = xfs_setattr(bdp, &va, attr_flags, credp);
+
+ if (error)
+ return error;
+
+ clrprealloc = 1;
+ break;
+
+ default:
+ ASSERT(0);
+ return XFS_ERROR(EINVAL);
+ }
+
+ /*
+ * update the inode timestamp, mode, and prealloc flag bits
+ */
+ tp = xfs_trans_alloc(mp, XFS_TRANS_WRITEID);
+
+ if ((error = xfs_trans_reserve(tp, 0, XFS_WRITEID_LOG_RES(mp),
+ 0, 0, 0))) {
+ /* ASSERT(0); */
+ xfs_trans_cancel(tp, 0);
+ return error;
+ }
+
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
+
+ xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
+ xfs_trans_ihold(tp, ip);
+
+ if ((attr_flags & ATTR_DMI) == 0) {
+ ip->i_d.di_mode &= ~S_ISUID;
+
+ /*
+ * Note that we don't have to worry about mandatory
+ * file locking being disabled here because we only
+ * clear the S_ISGID bit if the Group execute bit is
+ * on, but if it was on then mandatory locking wouldn't
+ * have been enabled.
+ */
+ if (ip->i_d.di_mode & S_IXGRP)
+ ip->i_d.di_mode &= ~S_ISGID;
+
+ xfs_ichgtime(ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
+ }
+ if (setprealloc)
+ ip->i_d.di_flags |= XFS_DIFLAG_PREALLOC;
+ else if (clrprealloc)
+ ip->i_d.di_flags &= ~XFS_DIFLAG_PREALLOC;
+
+ xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+ xfs_trans_set_sync(tp);
+
+ error = xfs_trans_commit(tp, 0, NULL);
+
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+
+ return error;
+}
+
+vnodeops_t xfs_vnodeops = {
+ BHV_IDENTITY_INIT(VN_BHV_XFS,VNODE_POSITION_XFS),
+ .vop_open = xfs_open,
+ .vop_read = xfs_read,
+#ifdef HAVE_SENDFILE
+ .vop_sendfile = xfs_sendfile,
+#endif
+ .vop_write = xfs_write,
+ .vop_ioctl = xfs_ioctl,
+ .vop_getattr = xfs_getattr,
+ .vop_setattr = xfs_setattr,
+ .vop_access = xfs_access,
+ .vop_lookup = xfs_lookup,
+ .vop_create = xfs_create,
+ .vop_remove = xfs_remove,
+ .vop_link = xfs_link,
+ .vop_rename = xfs_rename,
+ .vop_mkdir = xfs_mkdir,
+ .vop_rmdir = xfs_rmdir,
+ .vop_readdir = xfs_readdir,
+ .vop_symlink = xfs_symlink,
+ .vop_readlink = xfs_readlink,
+ .vop_fsync = xfs_fsync,
+ .vop_inactive = xfs_inactive,
+ .vop_fid2 = xfs_fid2,
+ .vop_rwlock = xfs_rwlock,
+ .vop_rwunlock = xfs_rwunlock,
+ .vop_bmap = xfs_bmap,
+ .vop_reclaim = xfs_reclaim,
+ .vop_attr_get = xfs_attr_get,
+ .vop_attr_set = xfs_attr_set,
+ .vop_attr_remove = xfs_attr_remove,
+ .vop_attr_list = xfs_attr_list,
+ .vop_link_removed = (vop_link_removed_t)fs_noval,
+ .vop_vnode_change = (vop_vnode_change_t)fs_noval,
+ .vop_tosspages = fs_tosspages,
+ .vop_flushinval_pages = fs_flushinval_pages,
+ .vop_flush_pages = fs_flush_pages,
+ .vop_release = xfs_release,
+ .vop_iflush = xfs_inode_flush,
+};